[
  {
    "path": ".codeclimate.yml",
    "content": "exclude_patterns:\n  - \"src/backend/distributed/utils/citus_outfuncs.c\"\n  - \"src/backend/distributed/deparser/ruleutils_*.c\"\n  - \"src/include/distributed/citus_nodes.h\"\n  - \"src/backend/distributed/safeclib\"\n  - \"src/backend/columnar/safeclib\"\n  - \"**/vendor/\"\n"
  },
  {
    "path": ".codecov.yml",
    "content": "codecov:\n  notify:\n    require_ci_to_pass: yes\n\ncoverage:\n  precision: 2\n  round: down\n  range: \"70...100\"\n\n  ignore:\n    - \"src/backend/distributed/utils/citus_outfuncs.c\"\n    - \"src/backend/distributed/deparser/ruleutils_*.c\"\n    - \"src/include/distributed/citus_nodes.h\"\n    - \"src/backend/distributed/safeclib\"\n    - \"vendor\"\n\n  status:\n    project:\n      default:\n        target: 87.5\n        threshold: 0.5\n\n    patch:\n      default:\n        target: 75\n\n    changes: no\n\nparsers:\n  gcov:\n    branch_detection:\n      conditional: yes\n      loop: yes\n      method: no\n      macro: no\n\ncomment:\n  layout: \"header, diff\"\n  behavior: default\n  require_changes: no\n"
  },
  {
    "path": ".devcontainer/.gdbinit",
    "content": "# gdbpg.py contains scripts to nicely print the postgres datastructures\n# while in a gdb session. Since the vscode debugger is based on gdb this\n# actually also works when debugging with vscode. Providing nice tools\n# to understand the internal datastructures we are working with.\nsource /root/gdbpg.py\n\n# when debugging postgres it is convenient to _always_ have a breakpoint\n# trigger when an error is logged. Because .gdbinit is sourced before gdb\n# is fully attached and has the sources loaded. To make sure the breakpoint\n# is added when the library is loaded we temporary set the breakpoint pending\n# to on. After we have added out breakpoint we revert back to the default\n# configuration for breakpoint pending.\n# The breakpoint is hard to read, but at entry of the function we don't have\n# the level loaded in elevel. Instead we hardcode the location where the\n# level of the current error is stored. Also gdb doesn't understand the\n# ERROR symbol so we hardcode this to the value of ERROR. It is very unlikely\n# this value will ever change in postgres, but if it does we might need to\n# find a way to conditionally load the correct breakpoint.\nset breakpoint pending on\nbreak elog.c:errfinish if errordata[errordata_stack_depth].elevel == 21\nset breakpoint pending auto\n\necho \\n\necho ----------------------------------------------------------------------------------\\n\necho when attaching to a postgres backend a breakpoint will be set on elog.c:errfinish \\n\necho it will only break on errors being raised in postgres \\n\necho \\n\necho to disable this breakpoint from vscode run `-exec disable 1` in the debug console \\n\necho this assumes it's the first breakpoint loaded as it is loaded from .gdbinit \\n\necho this can be verified with `-exec info break`, enabling can be done with \\n\necho `-exec enable 1` \\n\necho ----------------------------------------------------------------------------------\\n\necho \\n\n"
  },
  {
    "path": ".devcontainer/.gitignore",
    "content": "postgresql-*.tar.bz2\n"
  },
  {
    "path": ".devcontainer/.psqlrc",
    "content": "\\timing on\n\\pset linestyle unicode\n\\pset border 2\n\\setenv PAGER 'pspg --no-mouse -bX --no-commandbar --no-topbar'\n\\set HISTSIZE 100000\n\\set PROMPT1 '\\n%[%033[1m%]%M %n@%/:%> (PID: %p)%R%[%033[0m%]%# '\n\\set PROMPT2 '  '\n"
  },
  {
    "path": ".devcontainer/.vscode/Pipfile",
    "content": "[[source]]\nurl = \"https://pypi.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n[packages]\ndocopt = \"*\"\n\n[dev-packages]\n\n[requires]\npython_version = \"3.12\"\n"
  },
  {
    "path": ".devcontainer/.vscode/generate_c_cpp_properties-json.py",
    "content": "#! /usr/bin/env pipenv-shebang\n\"\"\"Generate C/C++ properties file for VSCode.\n\nUses pgenv to iterate postgres versions and generate\na C/C++ properties file for VSCode containing the\ninclude paths for the postgres headers.\n\nUsage:\n  generate_c_cpp_properties-json.py <target_path>\n  generate_c_cpp_properties-json.py (-h | --help)\n  generate_c_cpp_properties-json.py --version\n\nOptions:\n  -h --help     Show this screen.\n  --version     Show version.\n\n\"\"\"\nimport json\nimport subprocess\n\nfrom docopt import docopt\n\n\ndef main(args):\n    target_path = args['<target_path>']\n\n    output = subprocess.check_output(['pgenv', 'versions'])\n    # typical output is:\n    #      14.8      pgsql-14.8\n    #  *   15.3      pgsql-15.3\n    #      16beta2    pgsql-16beta2\n    # where the line marked with a * is the currently active version\n    #\n    # we are only interested in the first word of each line, which is the version number\n    # thus we strip the whitespace and the * from the line and split it into words\n    # and take the first word\n    versions = [line.strip('* ').split()[0] for line in output.decode('utf-8').splitlines()]\n\n    # create the list of configurations per version\n    configurations = []\n    for version in versions:\n        configurations.append(generate_configuration(version))\n\n    # create the json file\n    c_cpp_properties = {\n        \"configurations\": configurations,\n        \"version\": 4\n    }\n\n    # write the c_cpp_properties.json file\n    with open(target_path, 'w') as f:\n        json.dump(c_cpp_properties, f, indent=4)\n\n\ndef generate_configuration(version):\n    \"\"\"Returns a configuration for the given postgres version.\n\n    >>> generate_configuration('14.8')\n    {\n        \"name\": \"Citus Development Configuration - Postgres 14.8\",\n        \"includePath\": [\n            \"/usr/local/include\",\n            \"/home/citus/.pgenv/src/postgresql-14.8/src/**\",\n            \"${workspaceFolder}/**\",\n            \"${workspaceFolder}/src/include/\",\n        ],\n        \"configurationProvider\": \"ms-vscode.makefile-tools\"\n    }\n    \"\"\"\n    return {\n        \"name\": f\"Citus Development Configuration - Postgres {version}\",\n        \"includePath\": [\n            \"/usr/local/include\",\n            f\"/home/citus/.pgenv/src/postgresql-{version}/src/**\",\n            \"${workspaceFolder}/**\",\n            \"${workspaceFolder}/src/include/\",\n        ],\n        \"configurationProvider\": \"ms-vscode.makefile-tools\"\n    }\n\n\nif __name__ == '__main__':\n    arguments = docopt(__doc__, version='0.1.0')\n    main(arguments)\n"
  },
  {
    "path": ".devcontainer/.vscode/launch.json",
    "content": "{\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \"Attach Citus (devcontainer)\",\n            \"type\": \"cppdbg\",\n            \"request\": \"attach\",\n            \"processId\": \"${command:pickProcess}\",\n            \"program\": \"/home/citus/.pgenv/pgsql/bin/postgres\",\n            \"additionalSOLibSearchPath\": \"/home/citus/.pgenv/pgsql/lib\",\n            \"setupCommands\": [\n                {\n                    \"text\": \"handle SIGUSR1 noprint nostop pass\",\n                    \"description\": \"let gdb not stop when SIGUSR1 is sent to process\",\n                    \"ignoreFailures\": true\n                }\n            ],\n        },\n        {\n            \"name\": \"Open core file\",\n            \"type\": \"cppdbg\",\n            \"request\": \"launch\",\n            \"program\": \"/home/citus/.pgenv/pgsql/bin/postgres\",\n            \"coreDumpPath\": \"${input:corefile}\",\n            \"cwd\": \"${workspaceFolder}\",\n            \"MIMode\": \"gdb\",\n        }\n    ],\n    \"inputs\": [\n        {\n            \"id\": \"corefile\",\n            \"type\": \"command\",\n            \"command\": \"extension.commandvariable.file.pickFile\",\n            \"args\": {\n                \"dialogTitle\": \"Select core file\",\n                \"include\": \"**/core*\",\n            },\n        },\n    ],\n}\n"
  },
  {
    "path": ".devcontainer/Dockerfile",
    "content": "FROM ubuntu:22.04 AS base\n\n# environment is to make python pass an interactive shell, probably not the best timezone given a wide variety of colleagues\nENV TZ=UTC\nRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone\n\n# install build tools\nRUN apt update && apt install -y \\\n    bison \\\n    bzip2 \\\n    cpanminus \\\n    curl \\\n    docbook-xml \\\n    docbook-xsl \\\n    flex \\\n    gcc \\\n    git \\\n    libcurl4-gnutls-dev \\\n    libicu-dev \\\n    libkrb5-dev \\\n    liblz4-dev \\\n    libpam0g-dev \\\n    libreadline-dev \\\n    libselinux1-dev \\\n    libssl-dev \\\n    libxml2-utils \\\n    libxslt-dev \\\n    libzstd-dev \\\n    locales \\\n    make \\\n    perl \\\n    pkg-config \\\n    python3 \\\n    python3-pip \\\n    software-properties-common \\\n    sudo \\\n    uuid-dev \\\n    valgrind \\\n    xsltproc \\\n    zlib1g-dev \\\n && add-apt-repository ppa:deadsnakes/ppa -y \\\n && apt install -y \\\n    python3.12 python3.12-venv python3-distutils \\\n # software properties pulls in pkexec, which makes the debugger unusable in vscode\n && apt purge -y \\\n    software-properties-common \\\n && apt autoremove -y \\\n && apt clean\n\nRUN sudo pip3 install pipenv pipenv-shebang\n\nRUN cpanm install IPC::Run\n\nRUN locale-gen en_US.UTF-8\n\n# add the citus user to sudoers and allow all sudoers to login without a password prompt\nRUN useradd -ms /bin/bash citus \\\n && usermod -aG sudo citus \\\n && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers\n\nWORKDIR /home/citus\nUSER citus\n\n# run all make commands with the number of cores available\nRUN echo \"export MAKEFLAGS=\\\"-j \\$(nproc)\\\"\" >> \"/home/citus/.bashrc\"\n\nRUN git clone --branch v1.3.2 --depth 1 https://github.com/theory/pgenv.git .pgenv\nCOPY --chown=citus:citus pgenv/config/ .pgenv/config/\nENV PATH=\"/home/citus/.pgenv/bin:${PATH}\"\nENV PATH=\"/home/citus/.pgenv/pgsql/bin:${PATH}\"\n\nUSER citus\n\n# build postgres versions separately for effective parrallelism and caching of already built versions when changing only certain versions\nFROM base AS pg16\nRUN MAKEFLAGS=\"-j $(nproc)\" pgenv build 16.11\nRUN rm .pgenv/src/*.tar*\nRUN make -C .pgenv/src/postgresql-*/ clean\nRUN make -C .pgenv/src/postgresql-*/src/include install\n\n# create a staging directory with all files we want to copy from our pgenv build\n# we will copy the contents of the staged folder into the final image at once\nRUN mkdir .pgenv-staging/\nRUN cp -r .pgenv/src .pgenv/pgsql-* .pgenv/config .pgenv-staging/\nRUN rm .pgenv-staging/config/default.conf\n\nFROM base AS pg17\nRUN MAKEFLAGS=\"-j $(nproc)\" pgenv build 17.7\nRUN rm .pgenv/src/*.tar*\nRUN make -C .pgenv/src/postgresql-*/ clean\nRUN make -C .pgenv/src/postgresql-*/src/include install\n\n# create a staging directory with all files we want to copy from our pgenv build\n# we will copy the contents of the staged folder into the final image at once\nRUN mkdir .pgenv-staging/\nRUN cp -r .pgenv/src .pgenv/pgsql-* .pgenv/config .pgenv-staging/\nRUN rm .pgenv-staging/config/default.conf\n\nFROM base AS pg18\nRUN MAKEFLAGS=\"-j $(nproc)\" pgenv build 18.3\nRUN rm .pgenv/src/*.tar*\nRUN make -C .pgenv/src/postgresql-*/ clean\nRUN make -C .pgenv/src/postgresql-*/src/include install\n\n# Stage the pgenv artifacts for PG18\nRUN mkdir .pgenv-staging/\nRUN cp -r .pgenv/src .pgenv/pgsql-* .pgenv/config .pgenv-staging/\nRUN rm .pgenv-staging/config/default.conf\n\n\nFROM base AS uncrustify-builder\n\nRUN sudo apt update && sudo apt install -y cmake tree\n\nWORKDIR /uncrustify\nRUN curl -L https://github.com/uncrustify/uncrustify/archive/uncrustify-0.82.0.tar.gz | tar xz\nWORKDIR /uncrustify/uncrustify-uncrustify-0.82.0/\nRUN mkdir build\nWORKDIR /uncrustify/uncrustify-uncrustify-0.82.0/build/\nRUN cmake ..\nRUN MAKEFLAGS=\"-j $(nproc)\" make -s\n\nRUN make install DESTDIR=/uncrustify\n\n# builder for all pipenv's to get them contained in a single layer\nFROM base AS pipenv\n\nWORKDIR /workspaces/citus/\n\n# tools to sync pgenv with vscode\nCOPY --chown=citus:citus .vscode/Pipfile .vscode/Pipfile.lock .devcontainer/.vscode/\nRUN ( cd .devcontainer/.vscode && pipenv install )\n\n# environment to run our failure tests\nCOPY --chown=citus:citus src/ src/\nRUN ( cd src/test/regress && pipenv install )\n\n# assemble the final container by copying over the artifacts from separately build containers\nFROM base AS devcontainer\n\nLABEL org.opencontainers.image.source=https://github.com/citusdata/citus\nLABEL org.opencontainers.image.description=\"Development container for the Citus project\"\nLABEL org.opencontainers.image.licenses=AGPL-3.0-only\n\nRUN yes | sudo unminimize\n\n# install developer productivity tools\nRUN sudo apt update \\\n && sudo apt install -y \\\n    autoconf2.69 \\\n    bash-completion \\\n    fswatch \\\n    gdb \\\n    htop \\\n    libdbd-pg-perl \\\n    libdbi-perl \\\n    lsof \\\n    man \\\n    net-tools \\\n    psmisc \\\n    pspg \\\n    tree \\\n    vim \\\n && sudo apt clean\n\n# Since gdb will run in the context of the root user when debugging citus we will need to both\n# download the gdbpg.py script as the root user, into their home directory, as well as add .gdbinit\n# as a file owned by root\n# This will make that as soon as the debugger attaches to a postgres backend (or frankly any other process)\n# the gdbpg.py script will be sourced and the developer can direcly use it.\nRUN sudo curl -o /root/gdbpg.py https://raw.githubusercontent.com/tvesely/gdbpg/6065eee7872457785f830925eac665aa535caf62/gdbpg.py\nCOPY --chown=root:root .gdbinit /root/\n\n# install developer dependencies in the global environment\nRUN --mount=type=bind,source=requirements.txt,target=requirements.txt pip install -r requirements.txt\n\n# for persistent bash history across devcontainers we need to have\n# a) a directory to store the history in\n# b) a prompt command to append the history to the file\n# c) specify the history file to store the history in\n# b and c are done in the .bashrc to make it persistent across shells only\nRUN sudo install -d -o citus -g citus /commandhistory \\\n && echo \"export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history\" >> \"/home/citus/.bashrc\"\n\n# install citus-dev\nRUN git clone --branch develop https://github.com/citusdata/tools.git citus-tools \\\n && ( cd citus-tools/citus_dev && pipenv install ) \\\n && mkdir -p ~/.local/bin \\\n && ln -s /home/citus/citus-tools/citus_dev/citus_dev-pipenv .local/bin/citus_dev \\\n && sudo make -C citus-tools/uncrustify install bindir=/usr/local/bin pkgsysconfdir=/usr/local/etc/ \\\n && mkdir -p ~/.local/share/bash-completion/completions/ \\\n && ln -s ~/citus-tools/citus_dev/bash_completion ~/.local/share/bash-completion/completions/citus_dev\n\n# TODO some LC_ALL errors, possibly solved by locale-gen\nRUN git clone https://github.com/so-fancy/diff-so-fancy.git \\\n && mkdir -p ~/.local/bin \\\n && ln -s /home/citus/diff-so-fancy/diff-so-fancy .local/bin/\n\nCOPY --link --from=uncrustify-builder /uncrustify/usr/ /usr/\n\nCOPY --link --from=pg16 /home/citus/.pgenv-staging/ /home/citus/.pgenv/\nCOPY --link --from=pg17 /home/citus/.pgenv-staging/ /home/citus/.pgenv/\nCOPY --link --from=pg18 /home/citus/.pgenv-staging/ /home/citus/.pgenv/\n\nCOPY --link --from=pipenv /home/citus/.local/share/virtualenvs/ /home/citus/.local/share/virtualenvs/\n\n# place to run your cluster with citus_dev\nVOLUME /data\nRUN sudo mkdir /data \\\n && sudo chown citus:citus /data\n\nCOPY --chown=citus:citus .psqlrc .\n\n# with the copy linking of layers github actions seem to misbehave with the ownership of the\n# directories leading upto the link, hence a small patch layer to have to right ownerships set\nRUN sudo chown --from=root:root citus:citus -R ~\n\n# sets default pg version\nRUN pgenv switch 18.3\n\n# make connecting to the coordinator easy\nENV PGPORT=9700\n"
  },
  {
    "path": ".devcontainer/Makefile",
    "content": "\ninit: ../.vscode/c_cpp_properties.json ../.vscode/launch.json\n\n../.vscode:\n\tmkdir -p ../.vscode\n\n../.vscode/launch.json: ../.vscode .vscode/launch.json\n\tcp .vscode/launch.json ../.vscode/launch.json\n\n../.vscode/c_cpp_properties.json: ../.vscode\n\t./.vscode/generate_c_cpp_properties-json.py ../.vscode/c_cpp_properties.json\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n    \"image\": \"ghcr.io/citusdata/citus-devcontainer:main\",\n    \"runArgs\": [\n        \"--cap-add=SYS_PTRACE\",\n        \"--cap-add=SYS_NICE\",                 // allow NUMA page inquiry\n        \"--security-opt=seccomp=unconfined\",  // unblocks move_pages() in the container\n        \"--ulimit=core=-1\",\n    ],\n    \"forwardPorts\": [\n        9700\n    ],\n    \"customizations\": {\n        \"vscode\": {\n            \"extensions\": [\n                \"eamodio.gitlens\",\n                \"GitHub.copilot-chat\",\n                \"GitHub.copilot\",\n                \"github.vscode-github-actions\",\n                \"github.vscode-pull-request-github\",\n                \"ms-vscode.cpptools-extension-pack\",\n                \"ms-vsliveshare.vsliveshare\",\n                \"rioj7.command-variable\",\n            ],\n            \"settings\": {\n                \"files.exclude\": {\n                    \"**/*.o\": true,\n                    \"**/.deps/\": true,\n                }\n            },\n        }\n    },\n    \"mounts\": [\n        \"type=volume,target=/data\",\n        \"source=citus-bashhistory,target=/commandhistory,type=volume\",\n    ],\n    \"updateContentCommand\": \"./configure\",\n    \"postCreateCommand\": \"make -C .devcontainer/\",\n}\n\n"
  },
  {
    "path": ".devcontainer/pgenv/config/default.conf",
    "content": "PGENV_MAKE_OPTIONS=(-s)\n\nPGENV_CONFIGURE_OPTIONS=(\n    --enable-debug\n    --enable-depend\n    --enable-cassert\n    --enable-tap-tests\n    'CFLAGS=-ggdb -Og -g3 -fno-omit-frame-pointer -DUSE_VALGRIND'\n    --with-openssl\n    --with-libxml\n    --with-libxslt\n    --with-uuid=e2fs\n    --with-icu\n    --with-lz4\n)\n"
  },
  {
    "path": ".devcontainer/requirements.txt",
    "content": "black==24.3.0\nclick==8.1.7\nisort==5.12.0\nmypy-extensions==1.0.0\npackaging==23.2\npathspec==0.11.2\nplatformdirs==4.0.0\ntomli==2.0.1\ntyping_extensions==4.8.0\n"
  },
  {
    "path": ".devcontainer/src/test/regress/Pipfile",
    "content": "[[source]]\nname = \"pypi\"\nurl = \"https://pypi.python.org/simple\"\nverify_ssl = true\n\n[packages]\nmitmproxy = {git = \"https://github.com/citusdata/mitmproxy.git\", ref = \"main\"}\n\"aioquic\" = \">=1.2.0,<1.3.0\"\n\"mitmproxy-rs\" = \">=0.12.6,<0.13.0\"\nargon2-cffi = \">=23.1.0\"\nbcrypt = \">=4.1.2\"\nbrotli = \"<=1.2.0\"\nh11 = \"==0.16.0\"\nh2 = \"==4.3.0\"\ntornado = \">=6.5.1,<6.6.0\"\nzstandard = \">=0.25.0\"\nconstruct = \"*\"\ndocopt = \"==0.6.2\"\ncryptography = \"==46.0.5\"\npytest = \"*\"\npsycopg = \"*\"\nfilelock = \"*\"\npytest-asyncio = \"*\"\npytest-timeout = \"*\"\npytest-xdist = \"*\"\npytest-repeat = \"*\"\npyyaml = \"*\"\nwerkzeug = \"==3.1.5\"\n\"typing-extensions\" = \">=4.13.2,<5\"\npyperclip = \"==1.9.0\"\n\n[dev-packages]\nblack = \"==24.10.0\"\nisort = \"*\"\nflake8 = \"*\"\nflake8-bugbear = \"*\"\n\n[requires]\npython_version = \"3.12\"\n"
  },
  {
    "path": ".editorconfig",
    "content": "# top-most EditorConfig file\nroot = true\n\n# rules for all files\n# we use tabs with indent size 4\n[*]\nindent_style = tab\nindent_size = 4\ntab_width = 4\nend_of_line = lf\ninsert_final_newline = true\ncharset = utf-8\ntrim_trailing_whitespace = true\n\n# Don't change test output files, pngs or test data files\n[*.{out,png,data}]\ninsert_final_newline = unset\ntrim_trailing_whitespace = unset\n\n[*.{sql,sh,py,toml}]\nindent_style = space\nindent_size = 4\ntab_width = 4\n\n[*.yml]\nindent_style = space\nindent_size = 2\ntab_width = 2\n"
  },
  {
    "path": ".flake8",
    "content": "[flake8]\n# E203 is ignored for black\nextend-ignore = E203\n# black will truncate to 88 characters usually, but long string literals it\n# might keep. That's fine in most cases unless it gets really excessive.\nmax-line-length = 150\nexclude = .git,__pycache__,vendor,tmp_*\n"
  },
  {
    "path": ".gitattributes",
    "content": "*\t\twhitespace=space-before-tab,trailing-space\n*.[chly]\twhitespace=space-before-tab,trailing-space,indent-with-non-tab,tabwidth=4\n*.dsl\t\twhitespace=space-before-tab,trailing-space,tab-in-indent\n*.patch\t\t-whitespace\n*.pl\t\twhitespace=space-before-tab,trailing-space,tabwidth=4\n*.po\t\twhitespace=space-before-tab,trailing-space,tab-in-indent,-blank-at-eof\n*.sgml\t\twhitespace=space-before-tab,trailing-space,tab-in-indent,-blank-at-eol\n*.x[ms]l\twhitespace=space-before-tab,trailing-space,tab-in-indent\n\n# Avoid confusing ASCII underlines with leftover merge conflict markers\nREADME\t\tconflict-marker-size=32\nREADME.*\tconflict-marker-size=32\n\n# Certain data files that contain special whitespace, and other special cases\n*.data\t\t\t\t\t\t-whitespace\n\n# Test output files that contain extra whitespace\n*.out\t\t\t\t\t-whitespace\n\n# These files are maintained or generated elsewhere.  We take them as is.\nconfigure\t\t\t\t-whitespace\n\n# all C files (implementation and header) use our style...\n*.[ch] citus-style\n\n# except these exceptions...\nsrc/backend/distributed/utils/citus_outfuncs.c -citus-style\nsrc/backend/distributed/deparser/ruleutils_16.c -citus-style\nsrc/backend/distributed/deparser/ruleutils_17.c -citus-style\nsrc/backend/distributed/deparser/ruleutils_18.c -citus-style\nsrc/backend/distributed/commands/index_pg_source.c -citus-style\n\nsrc/include/distributed/citus_nodes.h -citus-style\n/vendor/** -citus-style\n\n# Hide diff on github by default for copied udfs\nsrc/backend/distributed/sql/udfs/*/[123456789]*.sql linguist-generated=true\n"
  },
  {
    "path": ".github/actions/parallelization/action.yml",
    "content": "name: 'Parallelization matrix'\ninputs:\n  count:\n    required: false\n    default: 32\noutputs:\n  json:\n    value: ${{ steps.generate_matrix.outputs.json }}\nruns:\n  using: \"composite\"\n  steps:\n    - name: Generate parallelization matrix\n      id: generate_matrix\n      shell: bash\n      run: |-\n        json_array=\"{\\\"include\\\": [\"\n        for ((i = 1; i <= ${{ inputs.count }}; i++)); do\n            json_array+=\"{\\\"id\\\":\\\"$i\\\"},\"\n        done\n        json_array=${json_array%,}\n        json_array+=\" ]}\"\n        echo \"json=$json_array\" >> \"$GITHUB_OUTPUT\"\n        echo \"json=$json_array\"\n"
  },
  {
    "path": ".github/actions/save_logs_and_results/action.yml",
    "content": "name: save_logs_and_results\ninputs:\n  folder:\n    required: false\n    default: \"log\"\nruns:\n  using: composite\n  steps:\n  - uses: actions/upload-artifact@v4.6.0\n    name: Upload logs\n    with:\n      name: ${{ inputs.folder }}\n      if-no-files-found: ignore\n      path: |\n        src/test/**/proxy.output\n        src/test/**/results/\n        src/test/**/tmp_check/master/log\n        src/test/**/tmp_check/worker.57638/log\n        src/test/**/tmp_check/worker.57637/log\n        src/test/**/*.diffs\n        src/test/**/out/ddls.sql\n        src/test/**/out/queries.sql\n        src/test/**/logfile_*\n        /tmp/pg_upgrade_newData_logs\n  - name: Publish regression.diffs\n    run: |-\n      diffs=\"$(find src/test/regress -name \"*.diffs\" -exec cat {} \\;)\"\n      if ! [ -z \"$diffs\" ]; then\n        echo '```diff' >> $GITHUB_STEP_SUMMARY\n        echo -E \"$diffs\" >> $GITHUB_STEP_SUMMARY\n        echo '```' >> $GITHUB_STEP_SUMMARY\n        echo -E $diffs\n      fi\n    shell: bash\n  - name: Print stack traces\n    run: \"./ci/print_stack_trace.sh\"\n    if: failure()\n    shell: bash\n"
  },
  {
    "path": ".github/actions/setup_extension/action.yml",
    "content": "name: setup_extension\ninputs:\n  pg_major:\n    required: false\n  skip_installation:\n    required: false\n    default: false\n    type: boolean\nruns:\n  using: composite\n  steps:\n  - name: Expose $PG_MAJOR to Github Env\n    run: |-\n        if [ -z \"${{ inputs.pg_major }}\" ]; then\n          echo \"PG_MAJOR=${PG_MAJOR}\" >> $GITHUB_ENV\n        else\n          echo \"PG_MAJOR=${{ inputs.pg_major }}\" >> $GITHUB_ENV\n        fi\n    shell: bash\n  - uses: actions/download-artifact@v4.1.8\n    with:\n      name: build-${{ env.PG_MAJOR }}\n  - name: Install Extension\n    if: ${{ inputs.skip_installation == 'false' }}\n    run: tar xfv \"install-$PG_MAJOR.tar\" --directory /\n    shell: bash\n  - name: Configure\n    run: |-\n      chown -R circleci .\n      git config --global --add safe.directory ${GITHUB_WORKSPACE}\n      gosu circleci ./configure --without-pg-version-check\n    shell: bash\n  - name: Enable core dumps\n    run: ulimit -c unlimited\n    shell: bash\n"
  },
  {
    "path": ".github/actions/upload_coverage/action.yml",
    "content": "name: coverage\ninputs:\n  flags:\n    required: false\n  codecov_token:\n    required: true\nruns:\n  using: composite\n  steps:\n  - uses: codecov/codecov-action@v3\n    with:\n      flags: ${{ inputs.flags }}\n      token: ${{ inputs.codecov_token }}\n      verbose: true\n      gcov: true\n"
  },
  {
    "path": ".github/packaging/packaging_ignore.yml",
    "content": "base:\n  - \".* warning: ignoring old recipe for target [`']check'\"\n  - \".* warning: overriding recipe for target [`']check'\"\n"
  },
  {
    "path": ".github/packaging/validate_build_output.sh",
    "content": "#!/bin/bash\n\nset -ex\n\n# Function to get the OS version\nget_rpm_os_version() {\n    if [[ -f /etc/centos-release ]]; then\n        cat /etc/centos-release | awk '{print $4}'\n    elif [[ -f /etc/oracle-release ]]; then\n        cat /etc/oracle-release | awk '{print $5}'\n    else\n        echo \"Unknown\"\n    fi\n}\n\npackage_type=${1}\n\n# Since $HOME is set in GH_Actions as /github/home, pyenv fails to create virtualenvs.\n# For this script, we set $HOME to /root and then set it back to /github/home.\nGITHUB_HOME=\"${HOME}\"\nexport HOME=\"/root\"\n\neval \"$(pyenv init -)\"\npyenv versions\npyenv virtualenv ${PACKAGING_PYTHON_VERSION} packaging_env\npyenv activate packaging_env\n\ngit clone -b v0.8.27 --depth=1  https://github.com/citusdata/tools.git tools\npython3 -m pip install -r tools/packaging_automation/requirements.txt\n\n\necho \"Package type: ${package_type}\"\necho \"OS version: $(get_rpm_os_version)\"\n\n # For RHEL 7, we need to install urllib3<2 due to below execution error\n # ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the 'ssl'\n # module is compiled with 'OpenSSL 1.0.2k-fips  26 Jan 2017'.\n # See: https://github.com/urllib3/urllib3/issues/2168\nif [[ ${package_type} == \"rpm\" && $(get_rpm_os_version) == 7* ]]; then\n    python3 -m pip uninstall -y urllib3\n    python3 -m pip install 'urllib3<2'\nfi\n\npython3 -m tools.packaging_automation.validate_build_output --output_file output.log \\\n                                                            --ignore_file .github/packaging/packaging_ignore.yml \\\n                                                            --package_type ${package_type}\npyenv deactivate\n# Set $HOME back to /github/home\nexport HOME=${GITHUB_HOME}\n\n# Print the output to the console\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "DESCRIPTION: PR description that will go into the change log, up to 78 characters\n"
  },
  {
    "path": ".github/workflows/build_and_test.yml",
    "content": "name: Build & Test\nrun-name: Build & Test - ${{ github.event.pull_request.title || github.ref_name }}\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}\n  cancel-in-progress: true\non:\n  workflow_dispatch:\n    inputs:\n      skip_test_flakyness:\n        required: false\n        default: false\n        type: boolean\n  push:\n    branches:\n      - \"main\"\n      - \"release-*\"\n  pull_request:\n    types: [opened, reopened,synchronize]\n  merge_group:\njobs:\n  # Since GHA does not interpolate env varibles in matrix context, we need to\n  # define them in a separate job and use them in other jobs.\n  params:\n    runs-on: ubuntu-latest\n    name: Initialize parameters\n    outputs:\n      build_image_name: \"ghcr.io/citusdata/extbuilder\"\n      test_image_name: \"ghcr.io/citusdata/exttester\"\n      citusupgrade_image_name: \"ghcr.io/citusdata/citusupgradetester\"\n      fail_test_image_name: \"ghcr.io/citusdata/failtester\"\n      pgupgrade_image_name: \"ghcr.io/citusdata/pgupgradetester\"\n      style_checker_image_name: \"ghcr.io/citusdata/stylechecker\"\n      style_checker_tools_version: \"0.8.33\"\n      sql_snapshot_pg_version: \"18.3\"\n      image_suffix: \"-vac4338a\"\n      pg16_version: '{ \"major\": \"16\", \"full\": \"16.13\" }'\n      pg17_version: '{ \"major\": \"17\", \"full\": \"17.9\" }'\n      pg18_version: '{ \"major\": \"18\", \"full\": \"18.3\" }'\n      upgrade_pg_versions: \"16.13-17.9-18.3\"\n    steps:\n      # Since GHA jobs need at least one step we use a noop step here.\n      - name: Set up parameters\n        run: echo 'noop'\n  check-sql-snapshots:\n    needs: params\n    runs-on: ubuntu-latest\n    container:\n      image: ${{ needs.params.outputs.build_image_name }}:${{ needs.params.outputs.sql_snapshot_pg_version }}${{ needs.params.outputs.image_suffix }}\n      options: --user root\n    steps:\n    - uses: actions/checkout@v4\n    - name: Check Snapshots\n      run: |\n        git config --global --add safe.directory ${GITHUB_WORKSPACE}\n        ci/check_sql_snapshots.sh\n  check-style:\n    needs: params\n    runs-on: ubuntu-latest\n    container:\n      image: ${{ needs.params.outputs.style_checker_image_name }}:${{ needs.params.outputs.style_checker_tools_version }}${{ needs.params.outputs.image_suffix }}\n    steps:\n    - name: Check Snapshots\n      run: |\n        git config --global --add safe.directory ${GITHUB_WORKSPACE}\n    - uses: actions/checkout@v4\n      with:\n        fetch-depth: 0\n    - name: Check C Style\n      run: citus_indent --check\n    - name: Check Python style\n      run: black --check .\n    - name: Check Python import order\n      run: isort --check .\n    - name: Check Python lints\n      run: flake8 .\n    - name: Fix whitespace\n      run: ci/editorconfig.sh && git diff --exit-code\n    - name: Remove useless declarations\n      run: ci/remove_useless_declarations.sh && git diff --cached --exit-code\n    - name: Sort and group includes\n      run: ci/sort_and_group_includes.sh && git diff --exit-code\n    - name: Normalize test output\n      run: ci/normalize_expected.sh && git diff --exit-code\n    - name: Check for C-style comments in migration files\n      run: ci/disallow_c_comments_in_migrations.sh && git diff --exit-code\n    - name: 'Check for comment--cached ns that start with # character in spec files'\n      run: ci/disallow_hash_comments_in_spec_files.sh && git diff --exit-code\n    - name: Check for gitignore entries .for source files\n      run: ci/fix_gitignore.sh && git diff --exit-code\n    - name: Check for lengths of changelog entries\n      run: ci/disallow_long_changelog_entries.sh\n    - name: Check for banned C API usage\n      run: ci/banned.h.sh\n    - name: Check for tests missing in schedules\n      run: ci/check_all_tests_are_run.sh\n    - name: Check if all CI scripts are actually run\n      run: ci/check_all_ci_scripts_are_run.sh\n    - name: Check if all GUCs are sorted alphabetically\n      run: ci/check_gucs_are_alphabetically_sorted.sh\n    - name: Check for missing downgrade scripts\n      run: ci/check_migration_files.sh\n  build:\n    needs: params\n    name: Build for PG${{ fromJson(matrix.pg_version).major }}\n    strategy:\n      fail-fast: false\n      matrix:\n        image_name:\n          - ${{ needs.params.outputs.build_image_name }}\n        image_suffix:\n          - ${{ needs.params.outputs.image_suffix}}\n        pg_version:\n          - ${{ needs.params.outputs.pg16_version }}\n          - ${{ needs.params.outputs.pg17_version }}\n          - ${{ needs.params.outputs.pg18_version }}\n    runs-on: ubuntu-latest\n    container:\n      image: \"${{ matrix.image_name }}:${{ fromJson(matrix.pg_version).full }}${{ matrix.image_suffix }}\"\n      options: --user root\n    steps:\n    - uses: actions/checkout@v4\n    - name: Expose $PG_MAJOR to Github Env\n      run: echo \"PG_MAJOR=${PG_MAJOR}\" >> $GITHUB_ENV\n      shell: bash\n    - name: Build\n      run: \"./ci/build-citus.sh\"\n      shell: bash\n    - uses: actions/upload-artifact@v4.6.0\n      with:\n        name: build-${{ env.PG_MAJOR }}\n        path: |-\n          ./build-${{ env.PG_MAJOR }}/*\n          ./install-${{ env.PG_MAJOR }}.tar\n  test-citus:\n    name: Test Citus\n    uses: ./.github/workflows/run_tests.yml\n    needs:\n      - params\n      - build\n    with:\n      pg_versions: >\n        [\n          ${{ needs.params.outputs.pg16_version }},\n          ${{ needs.params.outputs.pg17_version }},\n          ${{ needs.params.outputs.pg18_version }}\n        ]\n      make_targets: '[\"check-split\", \"check-multi\", \"check-multi-1\", \"check-multi-1-create-citus\", \"check-multi-mx\", \"check-vanilla\", \"check-isolation\", \"check-operations\", \"check-follower-cluster\", \"check-add-backup-node\", \"check-columnar\", \"check-columnar-isolation\", \"check-enterprise\", \"check-enterprise-isolation\", \"check-enterprise-isolation-logicalrep-1\", \"check-enterprise-isolation-logicalrep-2\", \"check-enterprise-isolation-logicalrep-3\", \"check-tap\"]'\n      image_suffix: ${{ needs.params.outputs.image_suffix }}\n      image_name: ${{ needs.params.outputs.test_image_name }}\n    secrets:\n      codecov_token: ${{ secrets.CODECOV_TOKEN }}\n  test-citus-failure:\n    name: Test Citus Failure\n    uses: ./.github/workflows/run_tests.yml\n    needs:\n      - params\n      - build\n    with:\n      pg_versions: >\n        [\n          ${{ needs.params.outputs.pg16_version }},\n          ${{ needs.params.outputs.pg17_version }},\n          ${{ needs.params.outputs.pg18_version }}\n        ]\n      make_targets: '[\"check-failure\", \"check-enterprise-failure\", \"check-pytest\", \"check-query-generator\"]'\n      image_suffix: ${{ needs.params.outputs.image_suffix }}\n      image_name: ${{ needs.params.outputs.fail_test_image_name }}\n    secrets:\n      codecov_token: ${{ secrets.CODECOV_TOKEN }}\n  test-citus-cdc:\n    name: Test Citus CDC\n    uses: ./.github/workflows/run_tests.yml\n    needs:\n      - params\n      - build\n    with:\n      pg_versions: >\n        [\n          ${{ needs.params.outputs.pg16_version }},\n          ${{ needs.params.outputs.pg17_version }},\n          ${{ needs.params.outputs.pg18_version }}\n        ]\n      make_targets: '[\"installcheck\"]'\n      image_suffix: ${{ needs.params.outputs.image_suffix }}\n      image_name: ${{ needs.params.outputs.test_image_name }}\n      suite: cdc\n    secrets:\n      codecov_token: ${{ secrets.CODECOV_TOKEN }}\n  test-arbitrary-configs:\n    name: PG${{ fromJson(matrix.pg_version).major }} - check-arbitrary-configs-${{ matrix.parallel }}\n    runs-on: [\"self-hosted\", \"1ES.Pool=1es-gha-citusdata-pool\"]\n    container:\n      image: \"${{ matrix.image_name }}:${{ fromJson(matrix.pg_version).full }}${{ needs.params.outputs.image_suffix }}\"\n      options: --user root\n    needs:\n      - params\n      - build\n    strategy:\n      fail-fast: false\n      matrix:\n        image_name:\n          - ${{ needs.params.outputs.fail_test_image_name }}\n        pg_version:\n          - ${{ needs.params.outputs.pg16_version }}\n          - ${{ needs.params.outputs.pg17_version }}\n          - ${{ needs.params.outputs.pg18_version }}\n        parallel: [0,1,2,3,4,5] # workaround for running 6 parallel jobs\n    steps:\n    - uses: actions/checkout@v4\n    - uses: \"./.github/actions/setup_extension\"\n    - name: Test arbitrary configs\n      run: |-\n        # we use parallel jobs to split the tests into 6 parts and run them in parallel\n        # the script below extracts the tests for the current job\n        N=6  # Total number of jobs (see matrix.parallel)\n        X=${{ matrix.parallel }}  # Current job number\n        TESTS=$(src/test/regress/citus_tests/print_test_names.py |\n          tr '\\n' ',' | awk -v N=\"$N\" -v X=\"$X\" -F, '{\n            split(\"\", parts)\n            for (i = 1; i <= NF; i++) {\n                parts[i % N] = parts[i % N] $i \",\"\n            }\n            print substr(parts[X], 1, length(parts[X])-1)\n        }')\n        echo $TESTS\n        gosu circleci \\\n          make -C src/test/regress \\\n            check-arbitrary-configs parallel=4 CONFIGS=$TESTS\n    - uses: \"./.github/actions/save_logs_and_results\"\n      if: always()\n      with:\n        folder: ${{ env.PG_MAJOR }}_arbitrary_configs_${{ matrix.parallel }}\n    - uses: \"./.github/actions/upload_coverage\"\n      if: always()\n      with:\n        flags: ${{ env.PG_MAJOR }}_arbitrary_configs_${{ matrix.parallel }}\n        codecov_token: ${{ secrets.CODECOV_TOKEN }}\n  test-pg-upgrade:\n    name: PG${{ matrix.old_pg_major }}-PG${{ matrix.new_pg_major }} - check-pg-upgrade\n    runs-on: ubuntu-latest\n    container:\n      image: \"${{ needs.params.outputs.pgupgrade_image_name }}:${{ needs.params.outputs.upgrade_pg_versions }}${{ needs.params.outputs.image_suffix }}\"\n      options: --user root\n    needs:\n    - params\n    - build\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - old_pg_major: 16\n            new_pg_major: 17\n          - old_pg_major: 17\n            new_pg_major: 18\n          - old_pg_major: 16\n            new_pg_major: 18\n    env:\n      old_pg_major: ${{ matrix.old_pg_major }}\n      new_pg_major: ${{ matrix.new_pg_major }}\n    steps:\n    - uses: actions/checkout@v4\n    - uses: \"./.github/actions/setup_extension\"\n      with:\n        pg_major: \"${{ env.old_pg_major }}\"\n    - uses: \"./.github/actions/setup_extension\"\n      with:\n        pg_major: \"${{ env.new_pg_major }}\"\n    - name: Install and test postgres upgrade\n      run: |-\n        gosu circleci \\\n          make -C src/test/regress \\\n            check-pg-upgrade \\\n            old-bindir=/usr/lib/postgresql/${{ env.old_pg_major }}/bin \\\n            new-bindir=/usr/lib/postgresql/${{ env.new_pg_major }}/bin \\\n            test-with-columnar=false\n\n        gosu circleci \\\n          make -C src/test/regress \\\n            check-pg-upgrade \\\n            old-bindir=/usr/lib/postgresql/${{ env.old_pg_major }}/bin \\\n            new-bindir=/usr/lib/postgresql/${{ env.new_pg_major }}/bin \\\n            test-with-columnar=true\n    - name: Copy pg_upgrade logs for newData dir\n      run: |-\n        mkdir -p /tmp/pg_upgrade_newData_logs\n        if ls src/test/regress/tmp_upgrade/newData/*.log 1> /dev/null 2>&1; then\n            cp src/test/regress/tmp_upgrade/newData/*.log /tmp/pg_upgrade_newData_logs\n        fi\n      if: failure()\n    - uses: \"./.github/actions/save_logs_and_results\"\n      if: always()\n      with:\n        folder: ${{ env.old_pg_major }}_${{ env.new_pg_major }}_upgrade\n    - uses: \"./.github/actions/upload_coverage\"\n      if: always()\n      with:\n        flags: ${{ env.old_pg_major }}_${{ env.new_pg_major }}_upgrade\n        codecov_token: ${{ secrets.CODECOV_TOKEN }}\n  test-citus-upgrade:\n    name: PG${{ fromJson(matrix.pg_version).major }} - check-citus-upgrade\n    runs-on: ubuntu-latest\n    container:\n      image: \"${{ needs.params.outputs.citusupgrade_image_name }}:${{ fromJson(matrix.pg_version).full }}${{ needs.params.outputs.image_suffix }}\"\n      options: --user root\n    needs:\n    - params\n    - build\n    strategy:\n      fail-fast: false\n      matrix:\n        pg_version:\n          - ${{ needs.params.outputs.pg16_version }}\n          - ${{ needs.params.outputs.pg17_version }}\n    steps:\n    - uses: actions/checkout@v4\n    - uses: \"./.github/actions/setup_extension\"\n      with:\n        skip_installation: true\n    - name: Install and test citus upgrade\n      run: |-\n        # run make check-citus-upgrade for all citus versions\n        # the image has ${CITUS_VERSIONS} set with all versions it contains the binaries of\n        for citus_version in ${CITUS_VERSIONS}; do \\\n          gosu circleci \\\n            make -C src/test/regress \\\n              check-citus-upgrade \\\n              bindir=/usr/lib/postgresql/${PG_MAJOR}/bin \\\n              citus-old-version=${citus_version} \\\n              citus-pre-tar=/install-pg${PG_MAJOR}-citus${citus_version}.tar \\\n              citus-post-tar=${GITHUB_WORKSPACE}/install-$PG_MAJOR.tar; \\\n        done;\n        # run make check-citus-upgrade-mixed for all citus versions\n        # the image has ${CITUS_VERSIONS} set with all versions it contains the binaries of\n        for citus_version in ${CITUS_VERSIONS}; do \\\n          gosu circleci \\\n            make -C src/test/regress \\\n              check-citus-upgrade-mixed \\\n              citus-old-version=${citus_version} \\\n              bindir=/usr/lib/postgresql/${PG_MAJOR}/bin \\\n              citus-pre-tar=/install-pg${PG_MAJOR}-citus${citus_version}.tar \\\n              citus-post-tar=${GITHUB_WORKSPACE}/install-$PG_MAJOR.tar; \\\n        done;\n    - uses: \"./.github/actions/save_logs_and_results\"\n      if: always()\n      with:\n        folder: ${{ env.PG_MAJOR }}_citus_upgrade\n    - uses: \"./.github/actions/upload_coverage\"\n      if: always()\n      with:\n        flags: ${{ env.PG_MAJOR }}_citus_upgrade\n        codecov_token: ${{ secrets.CODECOV_TOKEN }}\n  ch_benchmark:\n    name: CH Benchmark\n    if: startsWith(github.ref, 'refs/heads/ch_benchmark/')\n    runs-on: ubuntu-latest\n    needs:\n    - build\n    steps:\n    - uses: actions/checkout@v4\n    - uses: azure/login@v1\n      with:\n        creds: ${{ secrets.AZURE_CREDENTIALS }}\n    - name: install dependencies and run ch_benchmark tests\n      uses: azure/CLI@v1\n      with:\n        inlineScript: |\n          cd ./src/test/hammerdb\n          chmod +x run_hammerdb.sh\n          run_hammerdb.sh citusbot_ch_benchmark_rg\n  tpcc_benchmark:\n    name: TPCC Benchmark\n    if: startsWith(github.ref, 'refs/heads/tpcc_benchmark/')\n    runs-on: ubuntu-latest\n    needs:\n    - build\n    steps:\n    - uses: actions/checkout@v4\n    - uses: azure/login@v1\n      with:\n        creds: ${{ secrets.AZURE_CREDENTIALS }}\n    - name: install dependencies and run tpcc_benchmark tests\n      uses: azure/CLI@v1\n      with:\n        inlineScript: |\n          cd ./src/test/hammerdb\n          chmod +x run_hammerdb.sh\n          run_hammerdb.sh citusbot_tpcc_benchmark_rg\n  prepare_parallelization_matrix_32:\n    name: Prepare parallelization matrix\n    if: ${{ needs.test-flakyness-pre.outputs.tests != ''}}\n    needs: test-flakyness-pre\n    runs-on: ubuntu-latest\n    outputs:\n      json: ${{ steps.parallelization.outputs.json }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: \"./.github/actions/parallelization\"\n        id: parallelization\n        with:\n          count: 32\n  test-flakyness-pre:\n    name: Detect regression tests need to be ran\n    if: ${{ !inputs.skip_test_flakyness }}}\n    runs-on: ubuntu-latest\n    needs: build\n    outputs:\n      tests: ${{ steps.detect-regression-tests.outputs.tests }}\n    steps:\n    - uses: actions/checkout@v4\n      with:\n        fetch-depth: 0\n    - name: Detect regression tests need to be ran\n      id: detect-regression-tests\n      run: |-\n        detected_changes=$(git diff origin/main... --name-only --diff-filter=AM | (grep 'src/test/regress/sql/.*\\.sql\\|src/test/regress/spec/.*\\.spec\\|src/test/regress/citus_tests/test/test_.*\\.py' || true))\n        tests=${detected_changes}\n\n        # split the tests to be skipped --today we only skip upgrade tests\n        # and snapshot based node addition tests.\n        # snapshot based node addition tests are not flaky, as they promote\n        # the streaming replica (clone) to a PostgreSQL primary node that is one way\n        # operation\n        skipped_tests=\"\"\n        not_skipped_tests=\"\"\n        for test in $tests; do\n            if [[ $test =~ ^src/test/regress/sql/upgrade_ ]] || [[ $test =~ ^src/test/regress/sql/multi_add_node_from_backup ]]; then\n                skipped_tests=\"$skipped_tests $test\"\n            else\n                not_skipped_tests=\"$not_skipped_tests $test\"\n            fi\n        done\n\n        if [ ! -z \"$skipped_tests\" ]; then\n            echo \"Skipped tests \" $skipped_tests\n        fi\n\n        if [ -z \"$not_skipped_tests\" ]; then\n            echo \"Not detected any tests that flaky test detection should run\"\n        else\n            echo \"Detected tests \" $not_skipped_tests\n        fi\n\n        echo 'tests<<EOF' >> $GITHUB_OUTPUT\n        echo \"$not_skipped_tests\" >> \"$GITHUB_OUTPUT\"\n        echo 'EOF' >> $GITHUB_OUTPUT\n  test-flakyness:\n    if: ${{ needs.test-flakyness-pre.outputs.tests != ''}}\n    name: Test flakyness\n    runs-on: ubuntu-latest\n    container:\n      image: ${{ needs.params.outputs.fail_test_image_name }}:${{ fromJson(needs.params.outputs.pg18_version).full }}${{ needs.params.outputs.image_suffix }}\n      options: --user root\n    env:\n      runs: 8\n    needs:\n    - params\n    - build\n    - test-flakyness-pre\n    - prepare_parallelization_matrix_32\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.prepare_parallelization_matrix_32.outputs.json) }}\n    steps:\n    - uses: actions/checkout@v4\n    - uses: actions/download-artifact@v4.1.8\n    - uses: \"./.github/actions/setup_extension\"\n    - name: Run minimal tests\n      run: |-\n        tests=\"${{ needs.test-flakyness-pre.outputs.tests }}\"\n        tests_array=($tests)\n        for test in \"${tests_array[@]}\"\n        do\n            test_name=$(echo \"$test\" | sed -r \"s/.+\\/(.+)\\..+/\\1/\")\n            gosu circleci src/test/regress/citus_tests/run_test.py $test_name --repeat ${{ env.runs }} --use-whole-schedule-line\n        done\n      shell: bash\n    - uses: \"./.github/actions/save_logs_and_results\"\n      if: always()\n      with:\n        folder: test_flakyness_parallel_${{ matrix.id }}\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\n\non:\n  schedule:\n    - cron: '59 23 * * 6'\n  workflow_dispatch:\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-22.04\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'cpp', 'python']\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v4\n\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@v4\n      with:\n        languages: ${{ matrix.language }}\n\n    - name: Install package dependencies\n      run: |\n        # Create the file repository configuration:\n        sudo sh -c 'echo \"deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main 15\" > /etc/apt/sources.list.d/pgdg.list'\n        # Import the repository signing key:\n        wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -\n        sudo apt-get update\n        sudo apt-get install -y --no-install-recommends \\\n          autotools-dev \\\n          build-essential \\\n          ca-certificates \\\n          curl \\\n          debhelper \\\n          devscripts \\\n          fakeroot \\\n          flex \\\n          libcurl4-openssl-dev \\\n          libdistro-info-perl \\\n          libedit-dev \\\n          libfile-fcntllock-perl \\\n          libicu-dev \\\n          libkrb5-dev \\\n          liblz4-1 \\\n          liblz4-dev \\\n          libpam0g-dev \\\n          libreadline-dev \\\n          libselinux1-dev \\\n          libssl-dev \\\n          libxslt-dev \\\n          libzstd-dev \\\n          libzstd1 \\\n          lintian \\\n          postgresql-server-dev-17 \\\n          python3-pip \\\n          python3-setuptools \\\n          wget \\\n          zlib1g-dev\n\n\n    - name: Configure, Build and Install Citus\n      if: matrix.language == 'cpp'\n      run: |\n        ./configure\n        make -sj8\n        sudo make install-all\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@v4\n"
  },
  {
    "path": ".github/workflows/devcontainer.yml",
    "content": "name: \"Build devcontainer\"\n\n# Since building of containers can be quite time consuming, and take up some storage,\n# there is no need to finish a build for a tag if new changes are concurrently being made.\n# This cancels any previous builds for the same tag, and only the latest one will be kept.\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\non:\n  push:\n    paths:\n      - \".devcontainer/**\"\n  workflow_dispatch:\n\njobs:\n  docker:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n      attestations: write\n      id-token: write\n    steps:\n      -\n        name: Docker meta\n        id: meta\n        uses: docker/metadata-action@v5\n        with:\n          images: |\n            ghcr.io/citusdata/citus-devcontainer\n          tags: |\n            type=ref,event=branch\n            type=sha\n      -\n        name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v2\n      -\n        name: 'Login to GitHub Container Registry'\n        uses: docker/login-action@v3\n        with:\n          registry: ghcr.io\n          username: ${{github.actor}}\n          password: ${{secrets.GITHUB_TOKEN}}\n      -\n        name: Build and push\n        uses: docker/build-push-action@v5\n        with:\n          context: \"{{defaultContext}}:.devcontainer\"\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n"
  },
  {
    "path": ".github/workflows/flaky_test_debugging.yml",
    "content": "name: Flaky test debugging\nrun-name: Flaky test debugging - ${{ inputs.flaky_test }} (${{ inputs.flaky_test_runs_per_job }}x${{ inputs.flaky_test_parallel_jobs }})\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}\n  cancel-in-progress: true\non:\n  workflow_dispatch:\n    inputs:\n      flaky_test:\n        required: true\n        type: string\n        description: Test to run\n      flaky_test_runs_per_job:\n        required: false\n        default: 8\n        type: number\n        description: Number of times to run the test\n      flaky_test_parallel_jobs:\n        required: false\n        default: 32\n        type: number\n        description: Number of parallel jobs to run\njobs:\n  build:\n    name: Build Citus\n    runs-on: ubuntu-latest\n    container:\n      image: ${{ vars.build_image_name }}:${{ vars.pg16_version  }}${{ vars.image_suffix }}\n      options: --user root\n    steps:\n    - uses: actions/checkout@v4\n    - name: Configure, Build, and Install\n      run: |\n        echo \"PG_MAJOR=${PG_MAJOR}\" >> $GITHUB_ENV\n        ./ci/build-citus.sh\n      shell: bash\n    - uses: actions/upload-artifact@v4.6.0\n      with:\n        name: build-${{ env.PG_MAJOR }}\n        path: |-\n          ./build-${{ env.PG_MAJOR }}/*\n          ./install-${{ env.PG_MAJOR }}.tar\n  prepare_parallelization_matrix:\n    name: Prepare parallelization matrix\n    runs-on: ubuntu-latest\n    outputs:\n      json: ${{ steps.parallelization.outputs.json }}\n    steps:\n      - uses: actions/checkout@v4\n      - uses: \"./.github/actions/parallelization\"\n        id: parallelization\n        with:\n          count: ${{ inputs.flaky_test_parallel_jobs }}\n  test_flakyness:\n    name: Test flakyness\n    runs-on: ubuntu-latest\n    container:\n      image: ${{ vars.fail_test_image_name }}:${{ vars.pg16_version  }}${{ vars.image_suffix }}\n      options: --user root\n    needs:\n      [build, prepare_parallelization_matrix]\n    env:\n      test: \"${{ inputs.flaky_test }}\"\n      runs: \"${{ inputs.flaky_test_runs_per_job }}\"\n      skip: false\n    strategy:\n      fail-fast: false\n      matrix: ${{ fromJson(needs.prepare_parallelization_matrix.outputs.json) }}\n    steps:\n    - uses: actions/checkout@v4\n    - uses: \"./.github/actions/setup_extension\"\n    - name: Run minimal tests\n      run: |-\n          gosu circleci src/test/regress/citus_tests/run_test.py ${{ env.test }} --repeat ${{ env.runs }} --use-whole-schedule-line\n      shell: bash\n    - uses: \"./.github/actions/save_logs_and_results\"\n      if: always()\n      with:\n          folder: check_flakyness_parallel_${{ matrix.id }}\n"
  },
  {
    "path": ".github/workflows/packaging-test-pipelines.yml",
    "content": "name: Build tests in packaging images\n\non:\n  pull_request:\n    types: [opened, reopened,synchronize]\n  merge_group:\n\n  workflow_dispatch:\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n\n  get_postgres_versions_from_file:\n    runs-on: ubuntu-latest\n    outputs:\n      pg_versions: ${{ steps.get-postgres-versions.outputs.pg_versions }}\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 2\n      - name: Get Postgres Versions\n        id: get-postgres-versions\n        run: |\n          set -euxo pipefail\n          # Postgres versions are stored in .github/workflows/build_and_test.yml\n          # file in json strings with major and full keys.\n          # Below command extracts the versions and get the unique values.\n          pg_versions=$(cat .github/workflows/build_and_test.yml | grep -oE '\"major\": \"[0-9]+\", \"full\": \"[^\"]+\"' | sed -E 's/.*\"major\": \"([0-9]+)\".*/\\1/' | sort -n | uniq | tr '\\n' ',')\n          pg_versions_array=\"[ ${pg_versions} ]\"\n          echo \"Supported PG Versions: ${pg_versions_array}\"\n          # Below line is needed to set the output variable to be used in the next job\n          echo \"pg_versions=${pg_versions_array}\" >> $GITHUB_OUTPUT\n        shell: bash\n  rpm_build_tests:\n    name: rpm_build_tests\n    needs: get_postgres_versions_from_file\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        # While we use separate images for different Postgres versions in rpm\n        # based distros\n        # For this reason, we need to use a \"matrix\" to generate names of\n        # rpm images, e.g. citus/packaging:centos-7-pg12\n        packaging_docker_image:\n          - oraclelinux-8\n          - almalinux-8\n          - almalinux-9\n        POSTGRES_VERSION: ${{ fromJson(needs.get_postgres_versions_from_file.outputs.pg_versions) }}\n\n    container:\n      image: citus/packaging:${{ matrix.packaging_docker_image }}-pg${{ matrix.POSTGRES_VERSION }}\n      options: --user root\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n\n      - name: Set Postgres and python parameters for rpm based distros\n        run: |\n          echo \"/usr/pgsql-${{ matrix.POSTGRES_VERSION }}/bin\" >> $GITHUB_PATH\n          echo \"/root/.pyenv/bin:$PATH\" >> $GITHUB_PATH\n          echo \"PACKAGING_PYTHON_VERSION=3.8.16\" >> $GITHUB_ENV\n\n      - name: Configure\n        run: |\n          echo \"Current Shell:$0\"\n          echo \"GCC Version: $(gcc --version)\"\n          ./configure 2>&1 | tee output.log\n\n      - name: Make clean\n        run: |\n          make clean\n\n      - name: Make\n        run: |\n          git config --global --add safe.directory ${GITHUB_WORKSPACE}\n          make CFLAGS=\"-Wno-missing-braces\" -sj$(cat /proc/cpuinfo | grep \"core id\" | wc -l) 2>&1 | tee -a output.log\n\n          # Check the exit code of the make command\n          make_exit_code=${PIPESTATUS[0]}\n\n          # If the make command returned a non-zero exit code, exit with the same code\n          if [[ $make_exit_code -ne 0 ]]; then\n              echo \"make command failed with exit code $make_exit_code\"\n              exit $make_exit_code\n          fi\n\n      - name: Make install\n        run: |\n          make CFLAGS=\"-Wno-missing-braces\" install 2>&1 | tee -a output.log\n\n      - name: Validate output\n        env:\n          POSTGRES_VERSION: ${{ matrix.POSTGRES_VERSION }}\n          PACKAGING_DOCKER_IMAGE: ${{ matrix.packaging_docker_image }}\n        run: |\n          echo \"Postgres version: ${POSTGRES_VERSION}\"\n          ./.github/packaging/validate_build_output.sh \"rpm\"\n\n  deb_build_tests:\n    name: deb_build_tests\n    needs: get_postgres_versions_from_file\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        # On deb based distros, we use the same docker image for\n        # builds based on different Postgres versions because deb\n        # based images include all postgres installations.\n        # For this reason, we have multiple runs --which is 3 today--\n        # for each deb based image and we use POSTGRES_VERSION to set\n        # PG_CONFIG variable in each of those runs.\n        packaging_docker_image:\n          - debian-bookworm-all\n          - debian-bullseye-all\n          - ubuntu-focal-all\n          - ubuntu-jammy-all\n\n        POSTGRES_VERSION: ${{ fromJson(needs.get_postgres_versions_from_file.outputs.pg_versions) }}\n        exclude:\n          # PG18 is not supported on Ubuntu focal\n          - packaging_docker_image: ubuntu-focal-all\n            POSTGRES_VERSION: 18\n\n    container:\n      image: citus/packaging:${{ matrix.packaging_docker_image }}\n      options: --user root\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n\n      - name: Set pg_config path and python parameters for deb based distros\n        run: |\n          echo \"PG_CONFIG=/usr/lib/postgresql/${{ matrix.POSTGRES_VERSION }}/bin/pg_config\" >> $GITHUB_ENV\n          echo \"/root/.pyenv/bin:$PATH\" >> $GITHUB_PATH\n          echo \"PACKAGING_PYTHON_VERSION=3.8.16\" >> $GITHUB_ENV\n\n      - name: Configure\n        run: |\n          echo \"Current Shell:$0\"\n          echo \"GCC Version: $(gcc --version)\"\n          ./configure 2>&1 | tee output.log\n\n      - name: Make clean\n        run: |\n          make clean\n\n      - name: Make\n        shell: bash\n        run: |\n          set -e\n          git config --global --add safe.directory ${GITHUB_WORKSPACE}\n          make -sj$(cat /proc/cpuinfo | grep \"core id\" | wc -l) 2>&1 | tee -a output.log\n\n          # Check the exit code of the make command\n          make_exit_code=${PIPESTATUS[0]}\n\n          # If the make command returned a non-zero exit code, exit with the same code\n          if [[ $make_exit_code -ne 0 ]]; then\n              echo \"make command failed with exit code $make_exit_code\"\n              exit $make_exit_code\n          fi\n\n\n      - name: Make install\n        run: |\n          make install 2>&1 | tee -a output.log\n\n      - name: Validate output\n        env:\n          POSTGRES_VERSION: ${{ matrix.POSTGRES_VERSION }}\n          PACKAGING_DOCKER_IMAGE: ${{ matrix.packaging_docker_image }}\n        run: |\n          echo \"Postgres version: ${POSTGRES_VERSION}\"\n          ./.github/packaging/validate_build_output.sh \"deb\"\n"
  },
  {
    "path": ".github/workflows/run_tests.yml",
    "content": "name: Run Tests\non:\n  workflow_call:\n    inputs:\n      pg_versions:\n        required: true\n        type: string\n      make_targets:\n        required: true\n        type: string\n      image_suffix:\n        required: true\n        type: string\n      image_name:\n        required: true\n        type: string\n      suite:\n        required: false\n        type: string\n        default: \"regress\"\n      citus_version:\n        required: false\n        type: string\n        default: \"\"\n      citus_libdir:\n        required: false\n        type: string\n        default: \"\"\n      citus_libdir_label:\n        required: false\n        type: string\n        default: \"\"\n      n_1_mode:\n        required: false\n        type: string\n        default: \"\"\n    secrets:\n      codecov_token:\n        required: false\n\njobs:\n  test:\n    name: PG${{ matrix.pg_version.major }} - ${{ matrix.make }}${{ inputs.citus_version && format(' - {0}', inputs.citus_version) || '' }}${{ inputs.citus_libdir_label && format(' - lib-{0}', inputs.citus_libdir_label) || '' }}\n    strategy:\n      fail-fast: false\n      matrix:\n        pg_version: ${{ fromJson(inputs.pg_versions) }}\n        make: ${{ fromJson(inputs.make_targets) }}\n    runs-on: ubuntu-latest\n    container:\n      image: \"${{ inputs.image_name }}:${{ matrix.pg_version.full }}${{ inputs.image_suffix }}\"\n      options: >-\n        --user root\n        --dns=8.8.8.8\n        --cap-add=SYS_NICE\n        --security-opt seccomp=unconfined\n    steps:\n      - uses: actions/checkout@v4\n      - uses: \"./.github/actions/setup_extension\"\n      - name: Fix PostgreSQL library permissions for symlink setup\n        if: ${{ inputs.citus_libdir != '' }}\n        run: chmod -R a+w /usr/lib/postgresql/${{ matrix.pg_version.major }}/lib/\n      - name: Run Test\n        run: CITUSVERSION=${{ inputs.citus_version }} CITUSLIBDIR=${{ inputs.citus_libdir }} N_1_MODE=${{ inputs.n_1_mode }} gosu circleci make -C src/test/${{ inputs.suite }} ${{ matrix.make }}\n        timeout-minutes: 20\n      - uses: \"./.github/actions/save_logs_and_results\"\n        if: always()\n        with:\n          folder: ${{ matrix.pg_version.major }}_${{ matrix.make }}${{ inputs.citus_version && format('_{0}', inputs.citus_version) || '' }}${{ inputs.citus_libdir_label && format('_{0}', inputs.citus_libdir_label) || '' }}${{ inputs.n_1_mode && format('_{0}', inputs.n_1_mode) || '' }}\n      - uses: \"./.github/actions/upload_coverage\"\n        if: always()\n        with:\n          flags: ${{ env.PG_MAJOR }}_${{ inputs.suite }}_${{ matrix.make }}${{ inputs.citus_version && format('_{0}', inputs.citus_version) || '' }}${{ inputs.citus_libdir_label && format('_{0}', inputs.citus_libdir_label) || '' }}${{ inputs.n_1_mode && format('_{0}', inputs.n_1_mode) || '' }}\n          codecov_token: ${{ secrets.codecov_token }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Global excludes across all subdirectories\n*.o\n*.so\n*.so.[0-9]\n*.so.[0-9].[0-9]\n*.sl\n*.sl.[0-9]\n*.sl.[0-9].[0-9]\n*.dylib\n*.dll\n*.a\n*.mo\n*.pot\nobjfiles.txt\n.deps/\n*.gcno\n*.gcda\n*.gcov\n*.gcov.out\nlcov.info\ncoverage/\n*.vcproj\n*.vcxproj\nwin32ver.rc\n*.exe\nlib*dll.def\nlib*.pc\n*.bc\n\n# Local excludes in root directory\n/config.log\n/config.status\n/pgsql.sln\n/pgsql.sln.cache\n/Debug/\n/Release/\n/autom4te.cache\n/Makefile.global\n/src/Makefile.custom\n/compile_commands.json\n/src/backend/distributed/cdc/build-cdc-*/*\n/src/test/cdc/tmp_check/*\n/src/test/tap/tmp_check/*\n/src/test/tap/log/*\n\n\n# temporary files vim creates\n*.swp\n\n# vscode\n.vscode/*\n\n# output from diff normalization that shouldn't be commited\n*.unmodified\n*.modified\n\n# style related temporary outputs\n*.uncrustify\n.venv\n\n# added output when modifying check_gucs_are_alphabetically_sorted.sh\nguc.out\n"
  },
  {
    "path": ".ignore",
    "content": "/vendor\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "### citus v14.0.0 (February 9, 2026) ###\n\n* Drops PG15 support (#8372)\n\n* Adds support for PostgreSQL 18 (#8065)\n\n* Adds support for virtual generated columns on distributed tables\n  for PostgreSQL 18 (#8346)\n\n* Propagates publish_generated_columns publication option to worker\n  nodes for distributed tables on PostgreSQL 18 (#8360)\n\n* Respects VACUUM/ANALYZE ONLY semantics for Citus tables by\n  skipping shard propagation when ONLY is specified on\n  PostgreSQL 18 (#8365)\n\n* Allows configuring sslkeylogfile connection parameter using\n  citus.node_conn_info on PostgreSQL 18 (#8437)\n\n* Fixes INSERT .. SELECT planning error with GROUP BY on\n  PostgreSQL 18 (#8256)\n\n* Fixes deparser error with named joins and whole-row references\n  on PostgreSQL 18 (#8300)\n\n* Fixes columnar temp table access failure on PostgreSQL 18 (#8309)\n\n* Fixes multi-shard MIN/MAX on composite types by blessing record\n  aggregates (#8429)\n\n* Fixes distributed MIN/MAX for array types (#8421)\n\n* Adds propagation of ENFORCED / NOT ENFORCED on CHECK\n  constraints (#8349)\n\n* Stops on-demand statistics collection for clusters and deprecates\n  citus.enable_statistics_collection GUC (#8460)\n\n* Disallows creating a distributed table or altering it to be colocated\n  with another table if distribution key collations don't\n  match (#8257)\n\n* Makes citus_create_restore_point MX-safe by blocking 2PC commit\n  decisions (#8352)\n\n* Supports binary transfer from worker to coordinator for custom\n  aggregates (#8446)\n\n* Ensures query remains safe for deparse when a modify scan needs to\n  evaluate expressions before worker query execution (#8443)\n\n* Avoids local plan cache reuse for multi-shard queries (#8371)\n\n* Tightens distributed plan check to cover distributed\n  subplans (#8388)\n\n* Improves performance by avoiding unnecessary shallow copy of target\n  list when no array or json subscripts are present (#8155)\n\n* Fixes a bug that ignores reference or schema sharded tables in worker\n  subquery task construction when no distributed tables are\n  involved (#8440)\n\n* Fixes a bug in redundant WHERE clause detection (#8162)\n\n* Fixes a bug that causes allowing UPDATE / MERGE queries that may\n  change the distribution column value (#8214)\n\n* Fixes a bug that causes an unexpected error when executing\n  repartitioned MERGE (#8201)\n\n* Fixes an assertion failure in Citus maintenance daemon that can\n  happen in very slow systems (#8158)\n\n* Removes an assertion from Postgres ruleutils that was rendered\n  meaningless by a previous Citus commit (#8136)\n\n* Fixes incorrect rejection of colocated joins when Row Level\n  Security policies use volatile functions (#8357)\n\n* Fixes metadata sync failure when distributed tables use domain\n  types defined in non-public schemas (#8363)\n\n* Fixes a crash on CREATE STATISTICS with non-table\n  expressions (#8213, #8227)\n\n* Fixes invalid input syntax for type bigint in citus_stats with\n  large tables (#8166)\n\n* Fixes an undefined behavior that could happen when computing\n  tenant score for citus_stat_tenants (#7954)\n\n### citus v13.1.1 (Oct 1st, 2025) ###\n\n* Adds support for latest PG minors: 14.19, 15.14, 16.10 (#8142)\n\n* Fixes an assertion failure when an expression in the query references\n  a CTE (#8106)\n\n* Fixes a bug that causes an unexpected error when executing\n  repartitioned MERGE (#8201)\n\n* Fixes a bug that causes allowing UPDATE / MERGE queries that may\n  change the distribution column value (#8214)\n\n* Updates dynamic_library_path automatically when CDC is enabled (#8025)\n\n### citus v13.0.5 (Oct 1st, 2025) ###\n\n* Adds support for latest PG minors: 14.19, 15.14, 16.10 (#7986, #8142)\n\n* Fixes a bug that causes an unexpected error when executing\n  repartitioned MERGE (#8201)\n\n* Fixes a bug that causes allowing UPDATE / MERGE queries that may\n  change the distribution column value (#8214)\n\n* Fixes a bug in redundant WHERE clause detection (#8162)\n\n* Updates dynamic_library_path automatically when CDC is enabled (#8025)\n\n### citus v12.1.10 (Oct 1, 2025) ###\n\n* Adds support for latest PG minors: 14.19, 15.14, 16.10 (#7986, #8142)\n\n* Fixes a bug that causes allowing UPDATE / MERGE queries that may\n  change the distribution column value (#8214)\n\n* Fixes an assertion failure that happens when querying a view that is\n  defined on distributed tables (#8136)\n\n### citus v12.1.9 (Sep 3, 2025) ###\n\n* Adds a GUC for queries with outer joins and pseudoconstant quals (#8163)\n\n* Updates dynamic_library_path automatically when CDC is enabled (#7715)\n\n### citus v13.2.0 (August 18, 2025) ###\n\n* Adds `citus_add_clone_node()`, `citus_add_clone_node_with_nodeid()`,\n  `citus_remove_clone_node()` and `citus_remove_clone_node_with_nodeid()`\n  UDFs to support snapshot-based node splits. This feature allows promoting\n  a streaming replica (clone) to a primary node and rebalancing shards\n  between the original and newly promoted node without requiring a full data\n  copy. This greatly reduces rebalance times for scale-out operations when\n  the new node already has the data via streaming replication (#8122)\n\n* Improves performance of shard rebalancer by parallelizing moves and removing\n  bottlenecks that blocked concurrent logical-replication transfers. This\n  reduces rebalance windows especially for clusters with large reference\n  tables and allows multiple shard transfers to run in parallel (#7983)\n\n* Adds citus.enable_recurring_outer_join_pushdown GUC (enabled by default)\n  to allow pushing down LEFT/RIGHT outer joins having a reference table in\n  the outer side and a distributed table on the inner side (e.g.,\n  \\<reference table\\> LEFT JOIN \\<distributed table\\>) (#7973)\n\n* Adds citus.enable_local_fast_path_query_optimization (enabled by default)\n  GUC to avoid unnecessary query deparsing to improve performance of\n  fast-path queries targeting local shards (#8035)\n\n* Adds `citus_stats()` UDF that can be used to retrieve distributed `pg_stats`\n  for the provided Citus table. (#8026)\n\n* Avoids automatically creating citus_columnar when there are no relations\n  using it (#8081)\n\n* Makes sure to check if the distribution key is in the target list before\n  pushing down a query with a union and an outer join (#8092)\n\n* Fixes a bug in EXPLAIN ANALYZE to prevent unintended (duplicate) execution\n  of the (sub)plans during the explain phase (#8017)\n\n* Fixes potential memory corruptions that could happen when accessing\n  various catalog tables after a Citus downgrade is followed by a Citus\n  upgrade (#7950, #8120, #8124, #8121, #8114, #8146)\n\n* Fixes UPDATE statements with indirection and array/jsonb subscripting with\n  more than one field (#7675)\n\n* Fixes an assertion failure that happens when an expression in the query\n  references a CTE (#8106)\n\n* Fixes an assertion failure that happens when querying a view that is\n  defined on distributed tables (#8136)\n\n### citus v13.1.0 (May 30th, 2025) ###\n\n* Adds `citus_stat_counters` view that can be used to query\n  stat counters that Citus collects while the feature is enabled, which is\n  controlled by citus.enable_stat_counters. `citus_stat_counters()` can be\n  used to query the stat counters for the provided database oid and\n  `citus_stat_counters_reset()` can be used to reset them for the provided\n  database oid or for the current database if nothing or 0 is provided (#7917)\n\n* Adds `citus_nodes` view that displays the node name, port role, and \"active\"\n  for nodes in the cluster (#7968)\n\n* Adds `citus_is_primary_node()` UDF to determine if the current node is a\n  primary node in the cluster (#7720)\n\n* Adds support for propagating `GRANT/REVOKE` rights on table columns (#7918)\n\n* Adds support for propagating `REASSIGN OWNED BY` commands (#7319)\n\n* Adds support for propagating `CREATE`/`DROP` database from all nodes (#7240,\n  #7253, #7359)\n\n* Propagates `SECURITY LABEL ON ROLE` statement from any node (#7508)\n\n* Adds support for issuing role management commands from worker nodes (#7278)\n\n* Adds support for propagating `ALTER USER RENAME` commands (#7204)\n\n* Adds support for propagating `ALTER DATABASE <db_name> SET ..` commands\n  (#7181)\n\n* Adds support for propagating `SECURITY LABEL` on tables and columns (#7956)\n\n* Adds support for propagating `COMMENT ON <database>/<role>` commands (#7388)\n\n* Moves some of the internal citus functions from `pg_catalog` to\n  `citus_internal` schema (#7473, #7470, #7466, 7456, 7450)\n\n* Adjusts `max_prepared_transactions` only when it's set to default on PG >= 16\n  (#7712)\n\n* Adds skip_qualify_public param to shard_name() UDF to allow qualifying for\n  \"public\" schema when needed (#8014)\n\n* Allows `citus_*_size` on indexes on a distributed tables (#7271)\n\n* Allows `GRANT ADMIN` to now also be `INHERIT` or `SET` in support of PG16\n\n* Makes sure `worker_copy_table_to_node` errors out with Citus tables (#7662)\n\n* Adds information to explain output when using\n  `citus.explain_distributed_queries=false` (#7412)\n\n* Logs username in the failed connection message (#7432)\n\n* Makes sure to avoid incorrectly pushing-down the outer joins between\n  distributed tables and recurring relations (like reference tables, local\n  tables and `VALUES(..)` etc.) prior to PG 17 (#7937)\n\n* Prevents incorrectly pushing `nextval()` call down to workers to avoid using\n  incorrect sequence value for some types of `INSERT .. SELECT`s (#7976)\n\n* Makes sure to prevent `INSERT INTO ... SELECT` queries involving subfield or\n  sublink, to avoid crashes (#7912)\n\n* Makes sure to take improvement_threshold into the account\n  in `citus_add_rebalance_strategy()` (#7247)\n\n* Makes sure to disallow creating a replicated distributed\n  table concurrently (#7219)\n\n* Fixes a bug that causes omitting `CASCADE` clause for the commands sent to\n  workers for `REVOKE` commands on tables (#7958)\n\n* Fixes an issue detected using address sanitizer (#7948, #7949)\n\n* Fixes a bug in deparsing of shard query in case of \"output-table column\" name\n  conflict (#7932)\n\n* Fixes a crash in columnar custom scan that happens when a columnar table is\n  used in a join (#7703)\n\n* Fixes `MERGE` command when insert value does not have source distributed\n  column (#7627)\n\n* Fixes performance issue when using `\\d tablename` on a server with many\n  tables (#7577)\n\n* Fixes performance issue in `GetForeignKeyOids` on systems with many\n  constraints (#7580)\n\n* Fixes performance issue when distributing a table that depends on an\n  extension (#7574)\n\n* Fixes performance issue when creating distributed tables if many already\n  exist (#7575)\n\n* Fixes a crash caused by some form of `ALTER TABLE ADD COLUMN` statements. When\n  adding multiple columns, if one of the `ADD COLUMN` statements contains a\n  `FOREIGN` constraint ommitting the referenced\n  columns in the statement, a `SEGFAULT` occurs (#7522)\n\n* Fixes assertion failure in maintenance daemon during Citus upgrades  (#7537)\n\n* Fixes segmentation fault when using `CASE WHEN` in `DO` block functions\n  (#7554)\n\n* Fixes undefined behavior in `master_disable_node` due to argument mismatch\n  (#7492)\n\n* Fixes incorrect propagating of `GRANTED BY` and `CASCADE/RESTRICT` clauses\n  for `REVOKE` statements (#7451)\n\n* Fixes the incorrect column count after `ALTER TABLE` (#7379)\n\n* Fixes timeout when underlying socket is changed for an inter-node connection\n  (#7377)\n\n* Fixes memory leaks (#7441, #7440)\n\n* Fixes leaking of memory and memory contexts when tracking foreign keys between\n  Citus tables (#7236)\n\n* Fixes a potential segfault for background rebalancer (#7694)\n\n* Fixes potential `NULL` dereference in casual clocks (#7704)\n\n### citus v13.0.4 (May 29th, 2025) ###\n\n* Fixes an issue detected using address sanitizer (#7966)\n\n* Error out for queries with outer joins and pseudoconstant quals in versions\n  prior to PG 17 (#7937)\n\n### citus v12.1.8 (May 29, 2025) ###\n\n* Fixes a crash in left outer joins that can happen when there is an an\n  aggregate on a column from the inner side of the join (#7904)\n\n* Fixes an issue detected using address sanitizer (#7965)\n\n* Fixes a crash when executing a prepared CALL, which is not pure SQL but\navailable with some drivers like npgsql and jpgdbc (#7288)\n\n### citus v13.0.3 (March 20th, 2025) ###\n\n* Fixes a version bump issue in 13.0.2\n\n### citus v13.0.2 (March 12th, 2025) ###\n\n* Fixes a crash in columnar custom scan that happens when a columnar table is\n  used in a join. (#7647)\n\n* Fixes a bug that breaks `UPDATE SET (...) = (SELECT some_func(),... )`\n  type of queries on Citus tables (#7914)\n\n* Fixes a planning error caused by a redundant WHERE clause (#7907)\n\n* Fixes a crash in left outer joins that can happen when there is an aggregate\n  on a column from the inner side of the join. (#7901)\n\n* Fixes deadlock with transaction recovery that is possible during Citus\n  upgrades. (#7910)\n\n* Fixes a bug that prevents inserting into Citus tables that uses\n  a GENERATED ALWAYS AS IDENTITY column. (#7920)\n\n* Ensures that a MERGE command on a distributed table with a WHEN NOT MATCHED BY\n  SOURCE clause runs against all shards of the distributed table. (#7900)\n\n* Fixes a bug that breaks router updates on distributed tables\n  when a reference table is used in the subquery (#7897)\n\n### citus v12.1.7 (Feb 6, 2025) ###\n\n* Fixes a crash that happens because of unsafe catalog access when re-assigning\n  the global pid after `application_name` changes (#7791)\n\n* Prevents crashes when another extension skips executing the\n  `ClientAuthentication_hook` of Citus. (#7836)\n\n### citus v13.0.1 (February 4th, 2025) ###\n\n* Drops support for PostgreSQL 14 (#7753)\n\n### citus v13.0.0 (January 22, 2025) ###\n\n* Adds support for PostgreSQL 17 (#7699, #7661)\n\n* Adds `JSON_TABLE()` support in distributed queries (#7816)\n\n* Propagates `MERGE ... WHEN NOT MATCHED BY SOURCE` (#7807)\n\n* Propagates `MEMORY` and `SERIALIZE` options of `EXPLAIN` (#7802)\n\n* Adds support for identity columns in distributed partitioned tables (#7785)\n\n* Allows specifying an access method for distributed partitioned tables (#7818)\n\n* Allows exclusion constraints on distributed partitioned tables (#7733)\n\n* Allows configuring sslnegotiation using `citus.node_conn_info` (#7821)\n\n* Avoids wal receiver timeouts during large shard splits (#7229)\n\n* Fixes a bug causing incorrect writing of data to target `MERGE` repartition\n  command (#7659)\n\n* Fixes a crash that happens because of unsafe catalog access when re-assigning\n  the global pid after `application_name` changes (#7791)\n\n* Fixes incorrect `VALID UNTIL` setting assumption made for roles when syncing\n  them to new nodes (#7534)\n\n* Fixes segfault when calling distributed procedure with a parameterized\n  distribution argument (#7242)\n\n* Fixes server crash when trying to execute `activate_node_snapshot()` on a\n  single-node cluster (#7552)\n\n* Improves `citus_move_shard_placement()` to fail early if there is a new node\n  without reference tables yet (#7467)\n\n### citus v12.1.6 (Nov 14, 2024) ###\n\n* Propagates `SECURITY LABEL .. ON ROLE` statements (#7304)\n\n* Fixes crash caused by running queries with window partition (#7718)\n\n### citus v12.1.5 (July 17, 2024) ###\n\n* Adds support for MERGE commands with single shard distributed target tables\n  (#7643)\n\n* Fixes an error with MERGE commands when insert value does not have source\n  distribution column (#7627)\n\n### citus v12.1.4 (May 28, 2024) ###\n\n* Adds null check for node in HasRangeTableRef (#7604)\n\n### citus v12.1.3 (April 18, 2024) ###\n\n* Allows overwriting host name for all inter-node connections by\n  supporting \"host\" parameter in citus.node_conninfo (#7541)\n\n* Avoids distributed deadlocks by changing the order in which the locks are\n  acquired for the target and reference tables (#7542)\n\n* Fixes a performance issue when distributing a table that depends on an\n  extension (#7574)\n\n* Fixes a performance issue when using \"\\d tablename\" on a server with\n  many tables (#7577)\n\n* Fixes a crash caused by some form of ALTER TABLE ADD COLUMN\n  statements. When adding multiple columns, if one of the ADD COLUMN\n  statements contains a FOREIGN constraint omitting the referenced\n  columns in the statement, a SEGFAULT was occurring. (#7522)\n\n* Fixes a performance issue when creating distributed tables if many\n  already exist (#7575, #7579)\n\n* Fixes a bug when hostname in pg_dist_node resolves to multiple IPs\n  (#7377)\n\n* Fixes performance issue when tracking foreign key constraints on\n  systems with many constraints (#7578)\n\n* Fixes segmentation fault when using CASE WHEN in DO block within\n  functions. (#7554)\n\n* Fixes undefined behavior in master_disable_node due to argument\n  mismatch (#7492)\n\n* Fixes some potential bugs by correctly marking some variables as\n  volatile (#7570)\n\n* Logs username in the failed connection message (#7432)\n\n### citus v11.0.10 (February 15, 2024) ###\n\n* Removes pg_send_cancellation and all references (#7135)\n\n### citus v12.1.2 (February 12, 2024) ###\n\n* Fixes the incorrect column count after ALTER TABLE (#7379)\n\n### citus v12.0.1 (July 11, 2023) ###\n\n* Fixes incorrect default value assumption for VACUUM(PROCESS_TOAST) #7122)\n\n* Fixes a bug that causes an unexpected error when adding a column\n  with a NULL constraint (#7093)\n\n* Fixes a bug that could cause COPY logic to skip data in case of OOM (#7152)\n\n* Fixes a bug with deleting colocation groups (#6929)\n\n* Fixes memory and memory contexts leaks in Foreign Constraint Graphs (#7236)\n\n* Fixes shard size bug with too many shards (#7018)\n\n* Fixes the incorrect column count after ALTER TABLE (#7379)\n\n* Improves citus_tables view performance (#7050)\n\n* Makes sure to disallow creating a replicated distributed table\n  concurrently (#7219)\n\n* Removes pg_send_cancellation and all references (#7135)\n\n### citus v11.3.1 (February 12, 2024) ###\n\n* Disallows MERGE when the query prunes down to zero shards (#6946)\n\n* Fixes a bug related to non-existent objects in DDL commands (#6984)\n\n* Fixes a bug that could cause COPY logic to skip data in case of OOM (#7152)\n\n* Fixes a bug with deleting colocation groups (#6929)\n\n* Fixes incorrect results on fetching scrollable with hold cursors (#7014)\n\n* Fixes memory and memory context leaks in Foreign Constraint Graphs (#7236)\n\n* Fixes replicate reference tables task fail when user is superuser (#6930)\n\n* Fixes the incorrect column count after ALTER TABLE (#7379)\n\n* Improves citus_shard_sizes performance (#7050)\n\n* Makes sure to disallow creating a replicated distributed table\n  concurrently (#7219)\n\n* Removes pg_send_cancellation and all references (#7135)\n\n### citus v11.2.2 (February 12, 2024) ###\n\n* Fixes a bug in background shard rebalancer where the replicate\n  reference tables task fails if the current user is not a superuser (#6930)\n\n* Fixes a bug related to non-existent objects in DDL commands (#6984)\n\n* Fixes a bug that could cause COPY logic to skip data in case of OOM (#7152)\n\n* Fixes a bug with deleting colocation groups (#6929)\n\n* Fixes incorrect results on fetching scrollable with hold cursors (#7014)\n\n* Fixes memory and memory context leaks in Foreign Constraint Graphs (#7236)\n\n* Fixes the incorrect column count after ALTER TABLE (#7379)\n\n* Improves failure handling of distributed execution (#7090)\n\n* Makes sure to disallow creating a replicated distributed table\n  concurrently (#7219)\n\n* Removes pg_send_cancellation (#7135)\n\n### citus v11.1.7 (February 12, 2024) ###\n\n* Fixes memory and memory context leaks in Foreign Constraint Graphs (#7236)\n\n* Fixes a bug related to non-existent objects in DDL commands (#6984)\n\n* Fixes a bug that could cause COPY logic to skip data in case of OOM (#7152)\n\n* Fixes a bug with deleting colocation groups (#6929)\n\n* Fixes incorrect results on fetching scrollable with hold cursors (#7014)\n\n* Fixes the incorrect column count after ALTER TABLE (#7379)\n\n* Improves failure handling of distributed execution (#7090)\n\n* Makes sure to disallow creating a replicated distributed table\n  concurrently (#7219)\n\n* Removes pg_send_cancellation and all references (#7135)\n\n### citus v11.0.9 (February 12, 2024) ###\n\n* Fixes a bug that could cause COPY logic to skip data in case of OOM (#7152)\n\n* Fixes a bug with deleting colocation groups (#6929)\n\n* Fixes memory and memory context leaks in Foreign Constraint Graphs (#7236)\n\n* Fixes the incorrect column count after ALTER TABLE (#7462)\n\n* Improve failure handling of distributed execution (#7090)\n\n### citus v12.1.1 (November 9, 2023) ###\n\n* Fixes leaking of memory and memory contexts in Citus foreign key cache\n  (#7236)\n\n* Makes sure to disallow creating a replicated distributed table concurrently\n  (#7219)\n\n### citus v12.1.0 (September 12, 2023) ###\n\n* Adds support for PostgreSQL 16.0 (#7173)\n\n* Add `citus_schema_move()` function which moves tables within a\n  distributed schema to another node (#7180)\n\n* Adds `citus_pause_node_within_txn()` UDF that allows pausing the node with\n  given id (#7089)\n\n* Makes sure to enforce shard level colocation with the GUC\n  `citus.enable_non_colocated_router_query_pushdown` (#7076)\n\n* Allows creating reference / distributed-schema tables from local tables added\n  to metadata and that use identity columns (#7131)\n\n* Propagates `BUFFER_USAGE_LIMIT` option in `VACUUM` and `ANALYZE` (#7114)\n\n* Propagates `PROCESS_MAIN`, `SKIP_DATABASE_STATS`, `ONLY_DATABASE_STATS`\n  options in `VACUUM` (#7114)\n\n* Propagates `GENERIC_PLAN` option in `EXPLAIN` (#7141)\n\n* Propagates \"rules\" option in `CREATE COLLATION` (#7185)\n\n* Propagates `GRANT`/ `REVOKE` for database privileges (#7109)\n\n* Adds TRUNCATE trigger support on Citus foreign tables (#7170)\n\n* Removes `pg_send_cancellation` (#7135)\n\n* Prevents unnecessarily pulling the data into coordinator for some\n  `INSERT .. SELECT` queries that target a single-shard group (#7077)\n\n* Makes sure that rebalancer throws an error if replication factor is greater\n  than the shard allowed node count. Also makes sure to avoid moving a shard\n  to a node that it already exists on. (#7074)\n\n* Fixes a bug that may appear during 2PC recovery when there are multiple\n  databases (#7174)\n\n* Fixes a bug that could cause `COPY` logic to skip data in case of\n  out-of-memory (#7152)\n\n* Fixes a bug that causes an unexpected error when adding a column with\n  a `NULL` constraint (#7093)\n\n* Fixes `PROCESS_TOAST` default value to `true` (#7122)\n\n* Improves the error thrown when there is datatype mismatch in `MERGE ON`\n  (#7081)\n\n### citus v12.0.0 (July 11, 2023) ###\n\n* Adds support for schema-based sharding.\n  While `citus.enable_schema_based_sharding` GUC allows sharding the database\n  based on newly created schemas, `citus_schema_distribute()` allows doing so\n  for the existing schemas. Distributed schemas used for sharding the database\n  can be listed by using the view `citus_schemas`, monitored by using the view\n  `citus_stat_schemas`, and undistributed by using the udf\n  `citus_schema_undistribute()`\n  (#6866, #6979, #6933, #6936 and many others)\n\n* Supports MERGE command across non-colocated distributed tables/subqueries,\n  reference tables and joins on non-distribution columns (#6927)\n\n* Drops PG13 Support (#7002, #7007)\n\n* Changes default rebalance strategy to by_disk_size (#7033)\n\n* Changes by_disk_size rebalance strategy to have a base size (#7035)\n\n* Improves citus_tables view performance (#7018)\n\n* Improves tenant monitoring performance (#6868)\n\n* Introduces the GUC `citus.stat_tenants_untracked_sample_rate` for sampling in\n  tenant monitoring (#7026)\n\n* Adds CPU usage to citus_stat_tenants (#6844)\n\n* Propagates `ALTER SCHEMA .. OWNER TO ..` commands to worker (#6987)\n\n* Allows `ADD COLUMN` in command string with other commands (#7032)\n\n* Allows `DROP CONSTRAINT` in command string with other commands (#7012)\n\n* Makes sure to properly handle index storage options for `ADD CONSTRAINT\n  `/ COLUMN commands (#7032)\n\n* Makes sure to properly handle `IF NOT EXISTS` for `ADD COLUMN` commands\n  (#7032)\n\n* Allows using generated identity column based on int/smallint when creating\n  a distributed table with the limitation of not being able perform DMLs on\n  identity columns from worker nodes (#7008)\n\n* Supports custom cast from / to timestamptz in time partition management UDFs\n  (#6923)\n\n* Optimizes pushdown planner on memory and cpu (#6945)\n\n* Changes citus_shard_sizes view's table_name column to shard_id (#7003)\n\n* The GUC search_path is now reported when it is updated (#6983)\n\n* Disables citus.enable_non_colocated_router_query_pushdown GUC by default to\n  ensure generating a consistent distributed plan for the queries that\n  reference non-colocated distributed tables (#6909)\n\n* Disallows MERGE with filters that prune down to zero shards (#6946)\n\n* Makes sure to take `shouldhaveshards` setting into account for a node when\n  planning rebalance steps (#6887)\n\n* Improves the compatibility with other extension by forwarding to existing\n  emit_log_hook in our log hook (#6877)\n\n* Fixes wrong result when using `NOT MATCHED` with MERGE command (#6943)\n\n* Fixes querying the view `citus_shard_sizes` when there are too many shards\n  (#7018)\n\n* Fixes a bug related to type casts from other types to text/varchar (#6391)\n\n* Fixes propagating `CREATE SCHEMA AUTHORIZATION ..` with no schema name\n  (#7015)\n\n* Fixes an error when creating a FOREIGN KEY without a name referencing a schema\n  qualified table (#6986)\n\n* Fixes a rare bug which mostly happens with queries that contain both outer\n  join and where clauses  (#6857)\n\n* Fixes a bug related to propagation of schemas when pg_dist_node is empty\n  (#6900)\n\n* Fixes a crash when a query is locally executed with explain analyze (#6892)\n\n### citus v11.3.0 (May 2, 2023) ###\n\n* Introduces CDC implementation for Citus using logical replication\n  (#6623, #6810, #6827)\n\n* Adds support for `MERGE` command on co-located distributed tables joined on\n  distribution column (#6696, #6733)\n\n* Adds the view `citus_stat_tenants` that monitor statistics on tenant usages\n  (#6725)\n\n* Adds the GUC `citus.max_background_task_executors_per_node` to control number\n  of background task executors involving a node (#6771)\n\n* Allows parallel shard moves in background rebalancer (#6756)\n\n* Introduces the GUC `citus.metadata_sync_mode` that introduces nontransactional\n  mode for metadata sync (#6728, #6889)\n\n* Propagates CREATE/ALTER/DROP PUBLICATION statements for distributed tables\n  (#6776)\n\n* Adds the GUC `citus.enable_non_colocated_router_query_pushdown` to ensure\n  generating a consistent distributed plan for the queries that reference\n  non-colocated distributed tables when set to \"false\" (#6793)\n\n* Checks if all moves are able to be done via logical replication for rebalancer\n  (#6754)\n\n* Correctly reports shard size in `citus_shards` view (#6748)\n\n* Fixes a bug in shard copy operations (#6721)\n\n* Fixes a bug that prevents enforcing identity column restrictions on worker\n  nodes (#6738)\n\n* Fixes a bug with `INSERT .. SELECT` queries with identity columns (#6802)\n\n* Fixes an issue that caused some queries with custom aggregates to fail (#6805)\n\n* Fixes an issue when `citus_set_coordinator_host` is called more than once\n  (#6837)\n\n* Fixes an uninitialized memory access in shard split API (#6845)\n\n* Fixes memory leak and max allocation block errors during metadata syncing\n  (#6728)\n\n* Fixes memory leak in `undistribute_table` (#6693)\n\n* Fixes memory leak in `alter_distributed_table` (#6726)\n\n* Fixes memory leak in `create_distributed_table` (#6722)\n\n* Fixes memory leak issue with query results that returns single row (#6724)\n\n* Improves rebalancer when shard groups have placement count less than worker\n  count (#6739)\n\n* Makes sure to stop maintenance daemon when dropping a database even without\n  Citus extension (#6688)\n\n* Prevents using `alter_distributed_table` and `undistribute_table` UDFs when a\n  table has identity columns (#6738)\n\n* Prevents using identity columns on data types other than `bigint` on\n  distributed tables (#6738)\n\n### citus v11.2.1 (April 20, 2023) ###\n\n* Correctly reports shard size in `citus_shards` view (#6748)\n\n* Fixes a bug in shard copy operations (#6721)\n\n* Fixes a bug with `INSERT .. SELECT` queries with identity columns (#6802)\n\n* Fixes an uninitialized memory access in shard split API (#6845)\n\n* Fixes compilation for PG13.10 and PG14.7 (#6711)\n\n* Fixes memory leak in `alter_distributed_table` (#6726)\n\n* Fixes memory leak issue with query results that returns single row (#6724)\n\n* Prevents using `alter_distributed_table` and `undistribute_table` UDFs when a\n  table has identity columns (#6738)\n\n* Prevents using identity columns on data types other than `bigint` on\n  distributed tables (#6738)\n\n### citus v11.1.6 (April 20, 2023) ###\n\n* Correctly reports shard size in `citus_shards` view (#6748)\n\n* Fixes a bug in shard copy operations (#6721)\n\n* Fixes a bug that breaks pg upgrades if the user has a columnar table (#6624)\n\n* Fixes a bug that causes background rebalancer to fail when a reference table\n  doesn't have a primary key (#6682)\n\n* Fixes a regression in allowed foreign keys on distributed tables (#6550)\n\n* Fixes a use-after-free bug in connection management (#6685)\n\n* Fixes an unexpected foreign table error by disallowing to drop the\n  `table_name` option (#6669)\n\n* Fixes an uninitialized memory access in shard split API (#6845)\n\n* Fixes compilation for PG13.10 and PG14.7 (#6711)\n\n* Fixes crash that happens when trying to replicate a reference table that is\n  actually dropped (#6595)\n\n* Fixes memory leak issue with query results that returns single row (#6724)\n\n* Fixes the modifiers for subscription and role creation (#6603)\n\n* Makes sure to quote all identifiers used for logical replication to prevent\n  potential issues (#6604)\n\n* Makes sure to skip foreign key validations at the end of shard moves (#6640)\n\n### citus v11.0.8 (April 20, 2023) ###\n\n* Correctly reports shard size in `citus_shards` view (#6748)\n\n* Fixes a bug that breaks pg upgrades if the user has a columnar table (#6624)\n\n* Fixes an unexpected foreign table error by disallowing to drop the\n  `table_name` option (#6669)\n\n* Fixes compilation warning on PG13 + OpenSSL 3.0 (#6038, #6502)\n\n* Fixes crash that happens when trying to replicate a reference table that is\n  actually dropped (#6595)\n\n* Fixes memory leak issue with query results that returns single row (#6724)\n\n* Fixes the modifiers for subscription and role creation (#6603)\n\n* Fixes two potential dangling pointer issues (#6504, #6507)\n\n* Makes sure to quote all identifiers used for logical replication to prevent\n  potential issues (#6604)\n\n### citus v10.2.9 (April 20, 2023) ###\n\n* Correctly reports shard size in `citus_shards` view (#6748)\n\n* Fixes a bug in `ALTER EXTENSION citus UPDATE` (#6383)\n\n* Fixes a bug that breaks pg upgrades if the user has a columnar table (#6624)\n\n* Fixes a bug that prevents retaining columnar table options after a\n  table-rewrite (#6337)\n\n* Fixes memory leak issue with query results that returns single row (#6724)\n\n* Raises memory limits in columnar from 256MB to 1GB for reads and writes\n  (#6419)\n\n### citus v10.1.6 (April 20, 2023) ###\n\n* Fixes a crash that occurs when the aggregate that cannot be pushed-down\n  returns empty result from a worker (#5679)\n\n* Fixes columnar freezing/wraparound bug (#5962)\n\n* Fixes memory leak issue with query results that returns single row (#6724)\n\n* Prevents alter table functions from dropping extensions (#5974)\n\n### citus v10.0.8 (April 20, 2023) ###\n\n* Fixes a bug that could break `DROP SCHEMA/EXTENSON` commands when there is a\n  columnar table (#5458)\n\n* Fixes a crash that occurs when the aggregate that cannot be pushed-down\n  returns empty result from a worker (#5679)\n\n* Fixes columnar freezing/wraparound bug (#5962)\n\n* Fixes memory leak issue with query results that returns single row (#6724)\n\n* Prevents alter table functions from dropping extensions (#5974)\n\n### citus v9.5.12 (April 20, 2023) ###\n\n* Fixes a crash that occurs when the aggregate that cannot be pushed-down\n  returns empty result from a worker (#5679)\n\n* Fixes memory leak issue with query results that returns single row (#6724)\n\n* Prevents alter table functions from dropping extensions (#5974)\n\n### citus v11.2.0 (January 30, 2023) ###\n\n* Adds support for outer joins with reference tables / complex subquery-CTEs\n  in the outer side of the join (e.g., \\<reference table\\> LEFT JOIN\n  \\<distributed table\\>)\n\n* Adds support for creating `PRIMARY KEY`s and `UNIQUE`/`EXCLUSION`/`CHECK`/\n  `FOREIGN KEY` constraints via `ALTER TABLE` command without providing a\n  constraint name\n\n* Adds support for using identity columns on Citus tables\n\n* Adds support for `MERGE` command on local tables\n\n* Adds `citus_job_list()`, `citus_job_status()` and `citus_rebalance_status()`\n  UDFs that allow monitoring rebalancer progress\n\n* Adds `citus_task_wait()` UDF to wait on desired task status\n\n* Adds `source_lsn`, `target_lsn` and `status` fields into\n  `get_rebalance_progress()`\n\n* Introduces `citus_copy_shard_placement()` UDF with node id\n\n* Introduces `citus_move_shard_placement()` UDF with node id\n\n* Propagates `BEGIN` properties to worker nodes\n\n* Propagates `DROP OWNED BY` to worker nodes\n\n* Deprecates `citus.replicate_reference_tables_on_activate` and makes it\n  always `off`\n\n* Drops GUC `citus.defer_drop_after_shard_move`\n\n* Drops GUC `citus.defer_drop_after_shard_split`\n\n* Drops `SHARD_STATE_TO_DELETE` state and uses the cleanup records instead\n\n* Allows `citus_update_node()` to work with nodes from different clusters\n\n* Adds signal handlers for queue monitor to gracefully shutdown, cancel and to\n  see config changes\n\n* Defers cleanup after a failure in shard move or split\n\n* Extends cleanup process for replication artifacts\n\n* Improves a query that terminates compelling backends from\n  `citus_update_node()`\n\n* Includes Citus global pid in all internal `application_name`s\n\n* Avoids leaking `search_path` to workers when executing DDL commands\n\n* Fixes `alter_table_set_access_method error()` for views\n\n* Fixes `citus_drain_node()` to allow draining the specified worker only\n\n* Fixes a bug in global pid assignment for connections opened by rebalancer\n  internally\n\n* Fixes a bug that causes background rebalancer to fail when a reference table\n  doesn't have a primary key\n\n* Fixes a bug that might cause failing to query the views based on tables that\n  have renamed columns\n\n* Fixes a bug that might cause incorrectly planning the sublinks in query tree\n\n* Fixes a floating point exception during\n  `create_distributed_table_concurrently()`\n\n* Fixes a rebalancer failure due to integer overflow in subscription and role\n  creation\n\n* Fixes a regression in allowed foreign keys on distributed tables\n\n* Fixes a use-after-free bug in connection management\n\n* Fixes an unexpected foreign table error by disallowing to drop the\n  table_name option\n\n* Fixes an uninitialized memory access in `create_distributed_function()`\n\n* Fixes crash that happens when trying to replicate a reference table that is\n  actually dropped\n\n* Make sure to cleanup the shard on the target node in case of a\n  failed/aborted shard move\n\n* Makes sure to create replication artifacts with unique names\n\n* Makes sure to disallow triggers that depend on extensions\n\n* Makes sure to quote all identifiers used for logical replication to prevent\n  potential issues\n\n* Makes sure to skip foreign key validations at the end of shard moves\n\n* Prevents crashes on `UPDATE` with certain `RETURNING` clauses\n\n* Propagates column aliases in the shard-level commands\n\n### citus v11.1.5 (December 12, 2022) ###\n\n* Fixes two potential dangling pointer issues\n\n* Fixes compilation warning on PG13 + OpenSSL 3.0\n\n### citus v11.0.7 (November 8, 2022) ###\n\n* Adds the GUC `citus.allow_unsafe_constraints` to allow unique/exclusion/\n  primary key constraints without distribution column\n\n* Allows `citus_internal` `application_name` with additional suffix\n\n* Disallows having `ON DELETE/UPDATE SET DEFAULT` actions on columns that\n  default to sequences\n\n* Fixes a bug in `ALTER EXTENSION citus UPDATE`\n\n* Fixes a bug that causes a crash with empty/null password\n\n* Fixes a bug that causes not retaining trigger enable/disable settings when\n  re-creating them on shards\n\n* Fixes a bug that might cause inserting incorrect `DEFAULT` values when\n  applying foreign key actions\n\n* Fixes a bug that prevents retaining columnar table options after a\n  table-rewrite\n\n* Fixes a bug that prevents setting colocation group of a partitioned\n  distributed table to `none`\n\n* Fixes an issue that can cause logical reference table replication to fail\n\n* Raises memory limits in columnar from 256MB to 1GB for reads and writes\n\n### citus v11.1.4 (October 24, 2022) ###\n\n* Fixes an upgrade problem for `worker_fetch_foreign_file` when upgrade path\n  starts from 8.3 up to 11.1\n\n* Fixes an upgrade problem for `worker_repartition_cleanup` when upgrade path\n  starts from 9.1 up to 11.1\n\n### citus v11.1.3 (October 14, 2022) ###\n\n* Adds support for PostgreSQL 15.0\n\n* Fixes a bug in `ALTER EXTENSION citus UPDATE`\n\n* Fixes a bug that causes a crash with empty/null password\n\n* Fixes a bug that causes not retaining trigger enable/disable settings when\n  re-creating them on shards\n\n* Fixes a bug that prevents retaining columnar table options after a\n  table-rewrite\n\n* Raises memory limits in columnar from 256MB to 1GB for reads and writes\n\n### citus v11.1.2 (September 30, 2022) ###\n\n* Adds support for PostgreSQL 15rc1\n\n* Disallows having `ON DELETE/UPDATE SET DEFAULT` actions on columns that\n  default to sequences\n\n* Fixes a bug that might cause inserting incorrect `DEFAULT` values when\n  applying foreign key actions\n\n* Fixes a performance issue related to shard-moves by creating replica\n  identities before copying shards\n\n* Improves logging during shard-splits and resource cleanup\n\n* Makes sure to reuse connections for shard-splits and logical replication\n\n* Makes sure to try dropping replication slots a few more times after a failure\n  at the end of the shard-split\n\n### citus v11.1.1 (September 16, 2022) ###\n\n* Fixes a bug that prevents `create_distributed_table_concurrently()` working\n  on an empty node\n\n### citus v11.1.0 (September 15, 2022) ###\n\n* Adds support for PostgreSQL 15beta4\n\n* Adds ability to run shard rebalancer in the background\n\n* Adds `create_distributed_table_concurrently()` UDF to distribute tables\n  without interrupting the application\n\n* Adds `citus_split_shard_by_split_points()` UDF that allows\n  splitting a shard to specified set of nodes without blocking writes\n  and based on given split points\n\n* Adds support for non-blocking tenant isolation\n\n* Adds support for isolation tenants that use partitioned tables\n  or columnar tables\n\n* Separates columnar table access method into a separate logical extension\n\n* Adds support for online replication in `replicate_reference_tables()`\n\n* Improves performance of blocking shard moves\n\n* Improves non-blocking shard moves with a faster custom copy logic\n\n* Creates all foreign keys quickly at the end of a shard move\n\n* Limits `get_rebalance_progress()` to show shards in moving state\n\n* Makes `citus_move_shard_placement()` idempotent if shard already exists\n  on target node\n\n* Shows `citus_copy_shard_placement()` progress in `get_rebalance_progres()`\n\n* Supports changing CPU priorities for backends and shard moves\n\n* Adds the GUC `citus.allow_unsafe_constraints` to allow unique/exclusion/\n  primary key constraints without distribution column\n\n* Introduces GUC `citus.skip_constraint_validation`\n\n* Introduces `citus_locks` view\n\n* Improves `citus_tables` view by showing local tables added to metadata\n\n* Improves columnar table access method by moving old catalog tables into\n  an internal schema and introduces more secure & informative views based\n  on them\n\n* Adds support for `GRANT/REVOKE` on aggregates\n\n* Adds support for `NULLS NOT DISTINCT` clauses for indexes for PG15+\n\n* Adds support for setting relation options for columnar tables using\n  `ALTER TABLE`\n\n* Adds support for unlogged distributed sequences\n\n* Removes `do_repair` option from `citus_copy_shard_placement()`\n\n* Removes deprecated re-partitioning functions like\n  `worker_hash_partition_table()`\n\n* Drops support for isolation tenants that use replicated tables\n\n* Checks existence of the shards before insert, delete, and update\n\n* Hides tables owned by extensions from `citus_tables` and `citus_shards`\n\n* Propagates `VACUUM` and `ANALYZE` to worker nodes\n\n* Makes non-partitioned table size calculation quicker\n\n* Improves `create_distributed_table()` by creating new colocation entries when\n  using `colocate_with => 'none'`\n\n* Ensures that `SELECT .. FOR UPDATE` opens a transaction block when used in\n  a function call\n\n* Prevents a segfault by disallowing usage of SQL functions referencing to a\n  distributed table\n\n* Prevents creating a new colocation entry when replicating reference tables\n\n* Fixes a bug in query escaping in `undistribute_table()` and\n  `alter_distributed_table()`\n\n* Fixes a bug preventing the usage of `isolate_tenant_to_new_shard()` with text\n  column\n\n* Fixes a bug that may cause `GRANT` to propagate within `CREATE EXTENSION`\n\n* Fixes a bug that causes incorrectly marking `metadatasynced` flag for\n  coordinator\n\n* Fixes a bug that may prevent Citus from creating function in transaction\n  block properly\n\n* Fixes a bug that prevents promoting read-replicas as primaries\n\n* Fixes a bug that prevents setting colocation group of a partitioned\n  distributed table to `none`\n\n* Fixes a bug that prevents using `AUTO` option for `VACUUM (INDEX_CLEANUP)`\n  operation\n\n* Fixes a segfault in `citus_copy_shard_placement()`\n\n* Fixes an issue that can cause logical reference table replication to fail\n\n* Fixes schema name qualification for `RENAME SEQUENCE` statement\n\n* Fixes several small memory leaks\n\n* Fixes the transaction timestamp column of the `get_current_transaction_id()`\n  on coordinator\n\n* Maps any unused parameters to a generic type in prepared statements\n\n### citus v10.2.8 (August 19, 2022) ###\n\n* Fixes compilation warning caused by latest upgrade script changes\n\n* Fixes compilation warning on PG13 + OpenSSL 3.0\n\n### citus v11.0.6 (August 19, 2022) ###\n\n* Fixes a bug that could cause failures in `CREATE ROLE` statement\n\n* Fixes a bug that could cause failures in `create_distributed_table`\n\n* Fixes a bug that prevents distributing tables that depend on sequences\n\n* Fixes reference table lock contention\n\n* Fixes upgrade paths for 11.0\n\n### citus v10.2.7 (August 19, 2022) ###\n\n* Fixes a bug that could cause failures in `INSERT INTO .. SELECT`\n\n* Fixes a bug that could cause leaking files when materialized views are\n  refreshed\n\n* Fixes an unexpected error for foreign tables when upgrading Postgres\n\n* Fixes columnar freezing/wraparound bug\n\n* Fixes reference table lock contention\n\n* Prevents alter table functions from dropping extensions\n\n### citus v11.0.5 (August 1, 2022) ###\n\n* Avoids possible information leakage about existing users\n\n* Allows using `WITH HOLD` cursors with parameters\n\n* Fixes a bug that could cause failures in `INSERT INTO .. SELECT`\n\n* Fixes a bug that prevents pushing down `IN` expressions when using columnar\n  custom scan\n\n* Fixes a concurrency bug between creating a co-located distributed table and\n  shard moves\n\n* Fixes a crash that can happen due to catalog read in `shmem_exit`\n\n* Fixes an unexpected error caused by constraints when moving shards\n\n* Fixes an unexpected error for foreign tables when upgrading Postgres\n\n* Prevents adding local table into metadata if there is a view with circular\n  dependencies on it\n\n* Reduces memory consumption of index name fix for partitioned tables\n\n### citus v11.0.4 (July 13, 2022) ###\n\n* Fixes a bug that prevents promoting read-replicas as primaries\n\n### citus v11.0.3 (July 5, 2022) ###\n\n* Fixes a bug that prevents adding local tables with materialized views to\n  Citus metadata\n\n* Fixes a bug that prevents using `COMPRESSION` and `CONSTRAINT` on a column\n\n* Fixes upgrades to Citus 11 when there are no nodes in the metadata\n\n### citus v11.0.2 (June 15, 2022) ###\n\n* Drops support for PostgreSQL 12\n\n* Open sources enterprise features, see the rest of changelog items\n\n* Turns metadata syncing on by default\n\n* Introduces `citus_finish_citus_upgrade()` procedure which is necessary to\n  upgrade from earlier versions\n\n* Open sources non-blocking shard moves/shard rebalancer\n  (`citus.logical_replication_timeout`)\n\n* Open sources propagation of `CREATE/DROP/ALTER ROLE` statements\n\n* Open sources propagation of `GRANT` statements\n\n* Open sources propagation of `CLUSTER` statements\n\n* Open sources propagation of `ALTER DATABASE ... OWNER TO ...`\n\n* Open sources optimization for `COPY` when loading `JSON` to avoid double\n  parsing of the `JSON` object (`citus.skip_jsonb_validation_in_copy`)\n\n* Open sources support for row level security\n\n* Open sources support for `pg_dist_authinfo`, which allows storing different\n  authentication options for different users, e.g. you can store\n  passwords or certificates here.\n\n* Open sources support for `pg_dist_poolinfo`, which allows using connection\n  poolers in between coordinator and workers\n\n* Open sources tracking distributed query execution times using\n  citus_stat_statements (`citus.stat_statements_max`,\n  `citus.stat_statements_purge_interval`,\n  `citus.stat_statements_track`). This is disabled by default.\n\n* Open sources tenant_isolation\n\n* Open sources support for `sslkey` and `sslcert` in `citus.node_conninfo`\n\n* Adds `citus.max_client_connections` GUC to limit non-Citus connections\n\n* Allows locally creating objects having a dependency that cannot be distributed\n\n* Distributes aggregates with `CREATE AGGREGATE` command\n\n* Distributes functions with `CREATE FUNCTION` command\n\n* Adds `citus.create_object_propagation` GUC to control DDL creation behaviour\n  in transactions\n\n* Hides shards based on `application_name` prefix\n\n* Prevents specifying `application_name` via `citus.node_conninfo`\n\n* Starts identifying rebalancer backends by `application_name=citus_rebalancer`\n\n* Starts identifying internal backends by `application_name=citus_internal`\n\n* Adds `citus.enable_unsafe_triggers` flag to enable unsafe triggers on\n  distributed tables\n\n* Adds `fix_partition_shard_index_names` UDF to fix currently broken names\n\n* Adds propagation for foreign server commands\n\n* Adds propagation of `DOMAIN` objects\n\n* Adds propagation of `TEXT SEARCH CONFIGURATION` objects\n\n* Adds propagation of `TEXT SEARCH DICTIONARY` objects\n\n* Adds support for `ALTER FUNCTION ... SUPPORT ...` commands\n\n* Adds support for `CREATE SCHEMA AUTHORIZATION` statements without schema name\n\n* Adds support for `CREATE/DROP/ALTER VIEW` commands\n\n* Adds support for `TRUNCATE` for foreign tables\n\n* Adds support for adding local tables to metadata using\n  `citus_add_local_table_to_metadata()` UDF\n\n* Adds support for adding partitioned local tables to Citus metadata\n\n* Adds support for automatic binary encoding in re-partition joins when possible\n\n* Adds support for foreign tables in MX\n\n* Adds support for operator class parameters in indexes\n\n* Adds support for re-partition joins in transaction blocks\n\n* Adds support for re-partition joins on followers\n\n* Adds support for shard replication > 1 hash distributed tables on Citus MX\n\n* Adds support for `LOCK` commands on distributed tables from worker nodes\n\n* Adds support for `TABLESAMPLE`\n\n* Adds support for propagating views when syncing Citus table metadata\n\n* Improves handling of `IN`, `OUT` and `INOUT` parameters for functions\n\n* Introduces `citus_backend_gpid()` UDF to get global pid of the current backend\n\n* Introduces `citus_check_cluster_node_health` UDF to check cluster connectivity\n\n* Introduces `citus_check_connection_to_node` UDF to check node connectivity\n\n* Introduces `citus_coordinator_nodeid` UDF to find the node id of the\n  coordinator node\n\n* Introduces `citus_stat_activity` view and drops `citus_worker_stat_activity`\n  UDF\n\n* Introduces `citus.use_citus_managed_tables` GUC to add local tables to Citus\n  metadata automatically\n\n* Introduces a new flag `force_delegation` in `create_distributed_function()`\n\n* Introduces `run_command_on_coordinator` UDF\n\n* Introduces `synchronous` option to `citus_disable_node()` UDF\n\n* Introduces `citus_is_coordinator` UDF to check whether a node is the\n  coordinator\n\n* Allows adding a unique constraint with an index\n\n* Allows `create_distributed_function()` on a function owned by an extension\n\n* Allows creating distributed tables in sequential mode\n\n* Allows disabling nodes when multiple failures happen\n\n* Allows `lock_table_if_exits` to be called outside of a transaction blocks\n\n* Adds support for pushing procedures with `OUT` arguments down to the worker\n  nodes\n\n* Overrides `pg_cancel_backend()` and `pg_terminate_backend()` to run with\n  global pid\n\n* Delegates function calls of the form `SELECT .. FROM func()`\n\n* Adds propagation of `CREATE SCHEMA .. GRANT ON SCHEMA ..` commands\n\n* Propagates `pg_dist_object` to worker nodes\n\n* Adds propagation of `SCHEMA` operations\n\n* Adds missing version-mismatch checks for columnar tables\n\n* Adds missing version-mismatch checks for internal functions\n\n* `citus_shard_indexes_on_worker` shows all local shard indexes regardless of\n  `search_path`\n\n* `citus_shards_on_worker` shows all local shards regardless of `search_path`\n\n* Enables distributed execution from `run_command_on_*` functions\n\n* Deprecates inactive shard state, never marks any placement inactive\n\n* Disables distributed & reference foreign tables\n\n* Prevents propagating objects having a circular dependency\n\n* Prevents propagating objects having a dependency to an object with unsupported\n  type\n\n* Deprecates `master_get_table_metadata` UDF\n\n* Disallows remote execution from queries on shards\n\n* Drops `citus.enable_cte_inlining` GUC\n\n* Drops `citus.single_shard_commit_protocol` GUC, defaults to 2PC\n\n* Drops support for `citus.multi_shard_commit_protocol`, always use 2PC\n\n* Avoids unnecessary errors for `ALTER STATISTICS IF EXISTS` when the statistics\n  does not exist\n\n* Fixes a bug that prevents dropping/altering indexes\n\n* Fixes a bug that prevents non-client backends from accessing shards\n\n* Fixes columnar freezing/wraparound bug\n\n* Fixes `invalid read of size 1` memory error with `citus_add_node`\n\n* Fixes schema name qualification for `ALTER/DROP SEQUENCE`\n\n* Fixes schema name qualification for `ALTER/DROP STATISTICS`\n\n* Fixes schema name qualification for `CREATE STATISTICS`\n\n* Fixes a bug that causes columnar storage pages to have zero LSN\n\n* Fixes a bug that causes issues while create dependencies from multiple\n  sessions\n\n* Fixes a bug that causes reading columnar metapage as all-zeros when\n  writing to a columnar table\n\n* Fixes a bug that could break `DROP SCHEMA/EXTENSON` commands when there is a\n  columnar table\n\n* Fixes a bug that could break pg upgrades due to missing `pg_depend` records\n  for columnar table access method\n\n* Fixes a bug that could cause `CREATE INDEX` to fail for expressions when using\n  custom `search_path`\n\n* Fixes a bug that could cause `worker_save_query_explain_analyze` to fail on\n  custom types\n\n* Fixes a bug that could cause failed re-partition joins to leak result tables\n\n* Fixes a bug that could cause prerequisite columnar table access method\n  objects being not created during pg upgrades\n\n* Fixes a bug that could cause re-partition joins involving local shards to fail\n\n* Fixes a bug that could cause false positive distributed deadlocks due to local\n  execution\n\n* Fixes a bug that could cause leaking files when materialized views are\n  refreshed\n\n* Fixes a bug that could cause unqualified `DROP DOMAIN IF EXISTS` to fail\n\n* Fixes a bug that could cause wrong schema and ownership after\n  `alter_distributed_table`\n\n* Fixes a bug that could cause `EXPLAIN ANALYZE` to fail for prepared statements\n  with custom type\n\n* Fixes a bug that could cause Citus not to create function in transaction block\n  properly\n\n* Fixes a bug that could cause returning invalid JSON when running\n  `EXPLAIN ANALYZE` with subplans\n\n* Fixes a bug that limits usage of sequences in non-int columns\n\n* Fixes a bug that prevents `DROP SCHEMA CASCADE`\n\n* Fixes a build error that happens when `lz4` is not installed\n\n* Fixes a clog lookup failure that could occur when writing to a columnar table\n\n* Fixes a crash that occurs when the aggregate that cannot be pushed-down\n  returns empty result from a worker\n\n* Fixes a missing `FROM` clause entry error\n\n* Fixes a possible segfault that could happen when reporting distributed\n  deadlock\n\n* Fixes an issue that could cause unexpected errors when there is an in-progress\n  write to a columnar table\n\n* Fixes an unexpected error that occurs due to aborted writes to a columnar\n  table with an index\n\n* Fixes an unexpected error that occurs when writing to a columnar table created\n  in older version\n\n* Fixes issue when compiling Citus from source with some compilers\n\n* Fixes issues on `ATTACH PARTITION` logic\n\n* Fixes naming issues of newly created partitioned indexes\n\n* Honors `enable_metadata_sync` in node operations\n\n* Improves nested execution checks and adds GUC to control\n  (`citus.allow_nested_distributed_execution`)\n\n* Improves self-deadlock prevention for `CREATE INDEX / REINDEX CONCURRENTLY`\n  commands for builds using PG14 or higher\n\n* Moves `pg_dist_object` to `pg_catalog` schema\n\n* Parallelizes metadata syncing on node activation\n\n* Partitions shards to be co-located with the parent shards\n\n* Prevents Citus table functions from being called on shards\n\n* Prevents creating distributed functions when there are out of sync nodes\n\n* Prevents alter table functions from dropping extensions\n\n* Refrains reading the metadata cache for all tables during upgrade\n\n* Provides notice message for idempotent `create_distributed_function` calls\n\n* Reinstates optimisation for uniform shard interval ranges\n\n* Relaxes table ownership check to privileges check while acquiring lock\n\n* Drops support for `citus.shard_placement_policy` GUC\n\n* Drops `master_append_table_to_shard` UDF\n\n* Drops `master_apply_delete_command` UDF\n\n* Removes copy into new shard logic for append-distributed tables\n\n* Drops support for distributed `cstore_fdw` tables in favor of Citus\n  columnar table access method\n\n* Removes support for dropping distributed and local indexes in the same\n  statement\n\n* Replaces `citus.enable_object_propagation` GUC with\n  `citus.enable_metadata_sync`\n\n* Requires superuser for `citus_add_node()` and `citus_activate_node()` UDFs\n\n* Revokes read access to `columnar.chunk` from unprivileged user\n\n* Disallows unsupported lateral subqueries on distributed tables\n\n* Stops updating shard range in `citus_update_shard_statistics` for append\n  tables\n\n### citus v10.2.5 (March 15, 2022) ###\n\n* Fixes a bug that could cause `worker_save_query_explain_analyze` to fail on\n  custom types\n\n* Fixes a bug that limits usage of sequences in non-integer columns\n\n* Fixes a crash that occurs when the aggregate that cannot be pushed-down\n  returns empty result from a worker\n\n* Improves concurrent metadata syncing and metadata changing DDL operations\n\n### citus v10.2.4 (February 1, 2022) ###\n\n* Adds support for operator class parameters in indexes\n\n* Fixes a bug with distributed functions that have `OUT` parameters or\n  return `TABLE`\n\n* Fixes a build error that happens when `lz4` is not installed\n\n* Improves self-deadlock prevention for `CREATE INDEX` &\n  `REINDEX CONCURRENTLY` commands for builds using PG14 or higher\n\n* Fixes a bug that causes commands to fail when `application_name` is set\n\n### citus v10.1.4 (February 1, 2022) ###\n\n* Adds missing version checks for columnar tables\n\n* Fixes a bug that could break `DROP SCHEMA/EXTENSION` commands when there is\n  a columnar table\n\n* Fixes a build error that happens when `lz4` is not installed\n\n* Fixes a missing `FROM` clause entry error\n\n* Reinstates optimisation for uniform shard interval ranges\n\n* Fixes a bug that causes commands to fail when `application_name` is set\n\n### citus v10.2.3 (November 29, 2021) ###\n\n* Adds `fix_partition_shard_index_names` udf to fix currently broken\n  partition index names\n\n* Fixes a bug that could break `DROP SCHEMA/EXTENSION` commands when there is\n  a columnar table\n\n* Fixes a bug that could break pg upgrades due to missing `pg_depend` records\n  for columnar table access method\n\n* Fixes a missing `FROM` clause entry error\n\n* Fixes an unexpected error that occurs when writing to a columnar table\n  created in older versions\n\n* Fixes issue when compiling Citus from source with some compilers\n\n* Reinstates optimisation for uniform shard interval ranges\n\n* Relaxes table ownership check to privileges check while acquiring lock\n\n### citus v10.0.6 (November 12, 2021) ###\n\n* Adds missing version checks for columnar tables\n\n* Fixes a bug that caused `worker_append_table_to_shard` to write as superuser\n\n* Fixes a bug with local cached plans on tables with dropped columns\n\n* Fixes a missing `FROM` clause entry error\n\n* Fixes a use after free issue that could happen when altering a distributed\n  table\n\n* Reinstates optimisation for uniform shard interval ranges\n\n### citus v9.5.10 (November 8, 2021) ###\n\n* Fixes a release problem in 9.5.9\n\n### citus v9.5.9 (November 8, 2021) ###\n\n* Fixes a bug preventing `INSERT SELECT .. ON CONFLICT` with a constraint name\n  on local shards\n\n* Fixes a bug with local cached plans on tables with dropped columns\n\n* Fixes a crash in queries with a modifying `CTE` and a `SELECT`\n  without `FROM`\n\n* Fixes a missing `FROM` clause entry error\n\n* Fixes a missing intermediate result when coordinator is in metadata\n\n* Reinstates optimisation for uniform shard interval ranges\n\n### citus v9.2.8 (November 4, 2021) ###\n\n* Adds a configure flag to enforce security\n\n### citus v9.2.7 (November 3, 2021) ###\n\n* Fixes `ALTER TABLE IF EXISTS SET SCHEMA` with non-existing table bug\n\n* Fixes `CREATE INDEX CONCURRENTLY` with no index name on a postgres table bug\n\n* Fixes a bug that could cause crashes with certain compile flags\n\n* Fixes a crash because of overflow in partition id with certain compile flags\n\n* Fixes a memory leak in subtransaction memory handling\n\n* Fixes deparsing for queries with anonymous column references\n\n### citus v10.2.2 (October 14, 2021) ###\n\n* Fixes a bug that causes reading columnar metapage as all-zeros when\n  writing to a columnar table\n\n* Fixes a bug that could cause prerequisite columnar table access method\n  objects being not created during pg upgrades\n\n* Fixes a bug that could cause `CREATE INDEX` to fail for expressions when\n  using custom `search_path`\n\n* Fixes an unexpected error that occurs due to aborted writes to a columnar\n  table with an index\n\n### citus v10.2.1 (September 24, 2021) ###\n\n* Adds missing version-mismatch checks for columnar tables\n\n* Adds missing version-mismatch checks for internal functions\n\n* Fixes a bug that could cause partition shards being not co-located with\n  parent shards\n\n* Fixes a bug that prevents pushing down boolean expressions when using\n  columnar custom scan\n\n* Fixes a clog lookup failure that could occur when writing to a columnar table\n\n* Fixes an issue that could cause unexpected errors when there is an\n  in-progress write to a columnar table\n\n* Revokes read access to `columnar.chunk` from unprivileged user\n\n### citus v10.1.3 (September 17, 2021) ###\n\n* Fixes a bug that caused `worker_append_table_to_shard` to write as superuser\n\n* Fixes a crash in shard rebalancer when no distributed tables exist\n\n* Fixes a use after free issue that could happen when altering a distributed\n  table\n\n### citus v9.5.8 (September 15, 2021) ###\n\n* Fixes a bug that caused `worker_append_table_to_shard` to write as superuser\n\n* Fixes a use after free issue that could happen when altering a distributed\n  table\n\n### citus v10.2.0 (September 14, 2021) ###\n\n* Adds PostgreSQL 14 support\n\n* Adds hash & btree index support for columnar tables\n\n* Adds helper UDFs for easy time partition management:\n  `get_missing_time_partition_ranges`, `create_time_partitions`, and\n  `drop_old_time_partitions`\n\n* Adds propagation of ALTER SEQUENCE\n\n* Adds support for ALTER INDEX ATTACH PARTITION\n\n* Adds support for CREATE INDEX ON ONLY\n\n* Allows more graceful failovers when replication factor > 1\n\n* Enables chunk group filtering to work with Params for columnar tables\n\n* Enables qual push down for joins including columnar tables\n\n* Enables transferring of data using binary encoding by default on PG14\n\n* Improves `master_update_table_statistics` and provides distributed deadlock\n  detection\n\n* Includes `data_type` and `cache` in sequence definition on worker\n\n* Makes start/stop_metadata_sync_to_node() transactional\n\n* Makes sure that table exists before updating table statistics\n\n* Prevents errors with concurrent `citus_update_table_statistics` and DROP table\n\n* Reduces memory usage of columnar table scans by freeing the memory used for\n  last stripe read\n\n* Shows projected columns for columnar tables in EXPLAIN output\n\n* Speeds up dropping partitioned tables\n\n* Synchronizes hasmetadata flag on mx workers\n\n* Uses current user while syncing metadata\n\n* Adds a parameter to cleanup metadata when metadata syncing is stopped\n\n* Fixes a bug about int and smallint sequences on MX\n\n* Fixes a bug that cause partitions to have wrong distribution key after\n  DROP COLUMN\n\n* Fixes a bug that caused `worker_append_table_to_shard` to write as superuser\n\n* Fixes a bug that caused `worker_create_or_alter_role` to crash with NULL input\n\n* Fixes a bug that causes pruning incorrect shard of a range distributed table\n\n* Fixes a bug that may cause crash while aborting transaction\n\n* Fixes a bug that prevents attaching partitions when colocated foreign key\n  exists\n\n* Fixes a bug with `nextval('seq_name'::text)`\n\n* Fixes a crash in shard rebalancer when no distributed tables exist\n\n* Fixes a segfault caused by use after free in when using a cached connection\n\n* Fixes a UNION pushdown issue\n\n* Fixes a use after free issue that could happen when altering a distributed\n  table\n\n* Fixes showing target shard size in the rebalance progress monitor\n\n### citus v10.1.2 (August 16, 2021) ###\n\n* Allows more graceful failovers when replication factor > 1\n\n* Fixes a bug that causes partitions to have wrong distribution key after\n  `DROP COLUMN`\n\n### citus v10.0.5 (August 16, 2021) ###\n\n* Allows more graceful failovers when replication factor > 1\n\n* Fixes a bug that causes partitions to have wrong distribution key after\n  `DROP COLUMN`\n\n* Improves citus_update_table_statistics and provides distributed deadlock\n  detection\n\n### citus v9.5.7 (August 16, 2021) ###\n\n* Allows more graceful failovers when replication factor > 1\n\n* Fixes a bug that causes partitions to have wrong distribution key after\n  `DROP COLUMN`\n\n* Improves master_update_table_statistics and provides distributed deadlock\n  detection\n\n### citus v9.4.6 (August 8, 2021) ###\n\n* Allows more graceful failovers when replication factor > 1\n\n* Improves master_update_table_statistics and provides distributed deadlock\n  detection\n\n### citus v10.1.1 (August 5, 2021) ###\n\n* Improves citus_update_table_statistics and provides distributed deadlock\n  detection\n\n* Fixes showing target shard size in the rebalance progress monitor\n\n### citus v10.1.0 (July 14, 2021) ###\n\n* Drops support for PostgreSQL 11\n\n* Adds `shard_count` parameter to `create_distributed_table` function\n\n* Adds support for `ALTER DATABASE OWNER`\n\n* Adds support for temporary columnar tables\n\n* Adds support for using sequences as column default values when syncing\n  metadata\n\n* `alter_columnar_table_set` enforces columnar table option constraints\n\n* Continues to remove shards after failure in `DropMarkedShards`\n\n* Deprecates the `citus.replication_model` GUC\n\n* Enables `citus.defer_drop_after_shard_move` by default\n\n* Ensures free disk space before moving a shard\n\n* Fetches shard size on the fly for the rebalance monitor\n\n* Ignores old placements when disabling or removing a node\n\n* Implements `improvement_threshold` at shard rebalancer moves\n\n* Improves orphaned shard cleanup logic\n\n* Improves performance of `citus_shards`\n\n* Introduces `citus.local_hostname` GUC for connections to the current node\n\n* Makes sure connection is closed after each shard move\n\n* Makes sure that target node in shard moves is eligible for shard move\n\n* Optimizes partitioned disk size calculation for shard rebalancer\n\n* Prevents connection errors by properly terminating connections\n\n* Prevents inheriting a distributed table\n\n* Prevents users from dropping & truncating known shards\n\n* Pushes down `VALUES` clause as long as not in outer part of a `JOIN`\n\n* Reduces memory usage for multi-row inserts\n\n* Reduces memory usage while rebalancing shards\n\n* Removes length limits around partition names\n\n* Removes dependencies on the existence of public schema\n\n* Executor avoids opening extra connections\n\n* Excludes orphaned shards while finding shard placements\n\n* Preserves access method of materialized views when undistributing\n  or altering distributed tables\n\n* Fixes a bug that allowed moving of shards belonging to a reference table\n\n* Fixes a bug that can cause a crash when DEBUG4 logging is enabled\n\n* Fixes a bug that causes pruning incorrect shard of a range distributed table\n\n* Fixes a bug that causes worker_create_or_alter_role to crash with NULL input\n\n* Fixes a bug where foreign key to reference table was disallowed\n\n* Fixes a bug with local cached plans on tables with dropped columns\n\n* Fixes data race in `get_rebalance_progress`\n\n* Fixes `FROM ONLY` queries on partitioned tables\n\n* Fixes an issue that could cause citus_finish_pg_upgrade to fail\n\n* Fixes error message for local table joins\n\n* Fixes issues caused by omitting public schema in queries\n\n* Fixes nested `SELECT` query with `UNION` bug\n\n* Fixes null relationName bug at parallel execution\n\n* Fixes possible segfaults when using Citus in the middle of an upgrade\n\n* Fixes problems with concurrent calls of `DropMarkedShards`\n\n* Fixes shared dependencies that are not resident in a database\n\n* Fixes stale hostnames bug in prepared statements after `master_update_node`\n\n* Fixes the relation size bug during rebalancing\n\n* Fixes two race conditions in the get_rebalance_progress\n\n* Fixes using 2PC when it might be necessary\n\n### citus v10.0.4 (July 14, 2021) ###\n\n* Introduces `citus.local_hostname` GUC for connections to the current node\n\n* Removes dependencies on the existence of public schema\n\n* Removes limits around long partition names\n\n* Fixes a bug that can cause a crash when DEBUG4 logging is enabled\n\n* Fixes a bug that causes pruning incorrect shard of a range distributed table\n\n* Fixes an issue that could cause citus_finish_pg_upgrade to fail\n\n* Fixes FROM ONLY queries on partitioned tables\n\n* Fixes issues caused by public schema being omitted in queries\n\n* Fixes problems with concurrent calls of DropMarkedShards\n\n* Fixes relname null bug when using parallel execution\n\n* Fixes two race conditions in the get_rebalance_progress\n\n### citus v9.5.6 (July 8, 2021) ###\n\n* Fixes minor bug in `citus_prepare_pg_upgrade` that caused it to lose its\n  idempotency\n\n### citus v9.5.5 (July 7, 2021) ###\n\n* Adds a configure flag to enforce security\n\n* Fixes a bug that causes pruning incorrect shard of a range distributed table\n\n* Fixes an issue that could cause citus_finish_pg_upgrade to fail\n\n### citus v9.4.5 (July 7, 2021) ###\n\n* Adds a configure flag to enforce security\n\n* Avoids re-using connections for intermediate results\n\n* Fixes a bug that causes pruning incorrect shard of a range distributed table\n\n* Fixes a bug that might cause self-deadlocks when COPY used in TX block\n\n* Fixes an issue that could cause citus_finish_pg_upgrade to fail\n\n### citus v8.3.3 (March 23, 2021) ###\n\n* Fixes a bug that leads to various issues when a connection is lost\n\n* Fixes a bug where one could create a foreign key between non-colocated tables\n\n* Fixes a deadlock during transaction recovery\n\n* Fixes a memory leak in adaptive executor when query returns many columns\n\n### citus v10.0.3 (March 16, 2021) ###\n\n* Prevents infinite recursion for queries that involve `UNION ALL`\n  below `JOIN`\n\n* Fixes a crash in queries with a modifying `CTE` and a `SELECT`\n  without `FROM`\n\n* Fixes upgrade and downgrade paths for `citus_update_table_statistics`\n\n* Fixes a bug that causes `SELECT` queries to use 2PC unnecessarily\n\n* Fixes a bug that might cause self-deadlocks with\n  `CREATE INDEX` / `REINDEX CONCURRENTLY` commands\n\n* Adds `citus.max_cached_connection_lifetime` GUC to set maximum connection\n  lifetime\n\n* Adds `citus.remote_copy_flush_threshold` GUC that controls\n  per-shard memory usages by `COPY`\n\n* Adds `citus_get_active_worker_nodes` UDF to deprecate\n  `master_get_active_worker_nodes`\n\n* Skips 2PC for readonly connections in a transaction\n\n* Makes sure that local execution starts coordinated transaction\n\n* Removes open temporary file warning when cancelling a query with\n  an open tuple store\n\n* Relaxes the locks when adding an existing node\n\n### citus v10.0.2 (March 3, 2021) ###\n\n* Adds a configure flag to enforce security\n\n* Fixes a bug due to cross join without target list\n\n* Fixes a bug with `UNION ALL` on PG 13\n\n* Fixes a compatibility issue with pg_audit in utility calls\n\n* Fixes insert query with CTEs/sublinks/subqueries etc\n\n* Grants `SELECT` permission on `citus_tables` view to `public`\n\n* Grants `SELECT` permission on columnar metadata tables to `public`\n\n* Improves `citus_update_table_statistics` and provides distributed deadlock\n  detection\n\n* Preserves colocation with procedures in `alter_distributed_table`\n\n* Prevents using `alter_columnar_table_set` and `alter_columnar_table_reset`\n  on a columnar table not owned by the user\n\n* Removes limits around long table names\n\n### citus v9.5.4 (February 19, 2021) ###\n\n* Fixes a compatibility issue with pg_audit in utility calls\n\n### citus v10.0.1 (February 19, 2021) ###\n\n* Fixes an issue in creation of `pg_catalog.time_partitions` view\n\n### citus v9.5.3 (February 16, 2021) ###\n\n* Avoids re-using connections for intermediate results\n\n* Fixes a bug that might cause self-deadlocks when `COPY` used in xact block\n\n* Fixes a crash that occurs when distributing table after dropping foreign key\n\n### citus v10.0.0 (February 16, 2021) ###\n\n* Adds support for per-table option for columnar storage\n\n* Adds `rebalance_table_shards` to rebalance the shards across the nodes\n\n* Adds `citus_drain_node` to move all shards away from any node\n\n* Enables single-node Citus for production workloads\n\n* Adds support for local table and distributed table/subquery joins\n\n* Enables foreign keys between reference tables and local tables\n  (`citus.enable_local_reference_table_foreign_keys`)\n\n* Adds support for co-located/recurring correlated subqueries\n\n* Adds support for co-located/recurring sublinks in the target list\n\n* Adds `alter_distributed_table` and `alter_table_set_access_method` UDFs\n\n* Adds `alter_old_partitions_set_access_method` UDF to compress old partitions\n\n* Adds cascade option to `undistribute_table` UDF for foreign key connected\n  relations\n\n* Allows distributed table creation immediately after CREATE EXTENSION citus\n\n* Automatically adds coordinator to the metadata before the first distributed\n  table created\n\n* Introduces adaptive connection management for local nodes\n\n* Adds support for local execution in `INSERT..SELECT` commands that perform\n  re-partitioning\n\n* Adds `public.citus_tables` view\n\n* Adds `pg_catalog.citus_shards`, `pg_catalog.citus_shards_on_worker` and\n  `pg_catalog.citus_shard_indexes_on_worker` views\n\n* Adds `pg_catalog.time_partitions` view for simple (time) partitions and their\n  access methods\n\n* Adds `remove_local_tables_from_metadata` UDF for local tables automatically\n  added to metadata when defining foreign keys with reference tables\n\n* Adds support for `CREATE INDEX` commands without index name on citus tables\n\n* Adds support for `CREATE TABLE ... USING table_access_method` statements\n\n* Propagates `ALTER TABLE .. SET LOGGED/UNLOGGED` statements\n\n* Propagates `ALTER INDEX .. SET STATISTICS` statements\n\n* Propagates `ALTER SCHEMA RENAME` statements\n\n* Propagates `CREATE STATISTICS` statements across workers.\n\n* Propagates `DROP STATISTICS` statements across the workers\n\n* Propagates all types of `ALTER STATISTICS` statements.\n\n* Avoids establishing new connections until the previous ones are finalized\n\n* Avoids re-using connections for intermediate results\n\n* Improves performance in some subquery pushdown cases\n\n* Removes unused `citus.binary_master_copy_format` GUC\n\n* Removes unused `citus.expire_cached_shards` GUC\n\n* Removes unused `citus.large_table_shard_count` GUC\n\n* Removes unused `citus.max_assign_task_batch_size` GUC\n\n* Removes unused `citus.max_running_tasks_per_node` GUC\n\n* Removes unused `citus.max_task_string_size` GUC\n\n* Removes unused `citus.max_tracked_tasks_per_node` GUC\n\n* Removes unused `citus.sslmode` GUC\n\n* Removes unused `citus.task_tracker_delay` GUC\n\n* Removes the word 'master' from Citus UDFs\n\n* Removes unused UDF `mark_tables_colocated`\n\n* Removes `upgrade_to_reference_table` UDF\n\n* Renames 'master' to 'distributed' for columns of `citus_dist_stat_activity`\n\n* Renames 'master' to 'distributed' for columns of `citus_worker_stat_activity`\n\n* Chooses the default co-location group deterministically\n\n* Deletes distributed transaction entries when removing a node\n\n* Drops support for `create_citus_local_table` in favor of\n  `citus_add_local_table_to_metadata`\n\n* Fixes `EXPLAIN ANALYZE` error when query returns no columns\n\n* Fixes a bug preventing `INSERT SELECT .. ON CONFLICT` with a constraint name\n  on local shards\n\n* Fixes a bug related to correctness of multiple joins\n\n* Fixes a bug that might cause self-deadlocks when `COPY` used in xact block\n\n* Fixes a bug with partitioned tables that block new partition creation due to\n  wrong constraint names on workers\n\n* Fixes a crash that occurs when distributing table after dropping foreign key\n\n* Fixes a crash when adding/dropping foreign keys from reference to local\n  tables added to metadata\n\n* Fixes an unexpected error when executing `CREATE TABLE IF NOT EXISTS\n  PARTITION OF` commands\n\n* Fixes deadlock issue for `CREATE INDEX` in single node\n\n* Fixes distributed deadlock detection being blocked by metadata sync\n\n* Fixes handling indexes when adding local table to metadata\n\n* Fixes `undistribute_table` when table has column with `GENERATED ALWAYS AS\n  STORED` expressions\n\n* Improves concurrent index creation failure message\n\n* Prevents empty placement creation in the coordinator when executing\n  `master_create_empty_shard()`\n\n### citus v9.5.2 (January 26, 2021) ###\n\n* Fixes distributed deadlock detection being blocked by metadata sync\n\n* Prevents segfaults when SAVEPOINT handling cannot recover from connection\n  failures\n\n* Fixes possible issues that might occur with single shard distributed tables\n\n### citus v9.4.4 (December 28, 2020) ###\n\n* Fixes a bug that could cause router queries with local tables to be pushed\n  down\n\n* Fixes a segfault in connection management due to invalid connection hash\n  entries\n\n* Fixes possible issues that might occur with single shard distributed tables\n\n### citus v9.5.1 (December 1, 2020) ###\n\n* Enables PostgreSQL's parallel queries on EXPLAIN ANALYZE\n\n* Fixes a bug that could cause excessive memory consumption when a partition is\n  created\n\n* Fixes a bug that triggers subplan executions unnecessarily with cursors\n\n* Fixes a segfault in connection management due to invalid connection hash\n  entries\n\n### citus v9.4.3 (November 24, 2020) ###\n\n* Enables PostgreSQL's parallel queries on EXPLAIN ANALYZE\n\n* Fixes a bug that triggers subplan executions unnecessarily with cursors\n\n### citus v9.5.0 (November 10, 2020) ###\n\n* Adds support for PostgreSQL 13\n\n* Removes the task-tracker executor\n\n* Introduces citus local tables\n\n* Introduces `undistribute_table` UDF to convert tables back to postgres tables\n\n* Adds support for `EXPLAIN (ANALYZE) EXECUTE` and `EXPLAIN EXECUTE`\n\n* Adds support for `EXPLAIN (ANALYZE, WAL)` for PG13\n\n* Sorts the output of `EXPLAIN (ANALYZE)` by execution duration.\n\n* Adds support for CREATE TABLE ... USING table_access_method\n\n* Adds support for `WITH TIES` option in SELECT and INSERT SELECT queries\n\n* Avoids taking multi-shard locks on workers\n\n* Enforces `citus.max_shared_pool_size` config in COPY queries\n\n* Enables custom aggregates with multiple parameters to be executed on workers\n\n* Enforces `citus.max_intermediate_result_size` in local execution\n\n* Improves cost estimation of INSERT SELECT plans\n\n* Introduces delegation of procedures that read from reference tables\n\n* Prevents pull-push execution for simple pushdownable subqueries\n\n* Improves error message when creating a foreign key to a local table\n\n* Makes `citus_prepare_pg_upgrade` idempotent by dropping transition tables\n\n* Disallows `ON TRUE` outer joins with reference & distributed tables when\n  reference table is outer relation to avoid incorrect results\n\n* Disallows field indirection in INSERT/UPDATE queries to avoid incorrect\n  results\n\n* Disallows volatile functions in UPDATE subqueries to avoid incorrect results\n\n* Fixes CREATE INDEX CONCURRENTLY crash with local execution\n\n* Fixes `citus_finish_pg_upgrade` to drop all backup tables\n\n* Fixes a bug that cause failures when `RECURSIVE VIEW` joined reference table\n\n* Fixes DROP SEQUENCE failures when metadata syncing is enabled\n\n* Fixes a bug that caused CREATE TABLE with CHECK constraint to fail\n\n* Fixes a bug that could cause VACUUM to deadlock\n\n* Fixes master_update_node failure when no background worker slots are available\n\n* Fixes a bug that caused replica identity to not be propagated on shard repair\n\n* Fixes a bug that could cause crashes after connection timeouts\n\n* Fixes a bug that could cause crashes with certain compile flags\n\n* Fixes a bug that could cause deadlocks on CREATE INDEX\n\n* Fixes a bug with genetic query optimization in outer joins\n\n* Fixes a crash when aggregating empty tables\n\n* Fixes a crash with inserting domain constrained composite types\n\n* Fixes a crash with multi-row & router INSERT's in local execution\n\n* Fixes a possibility of doing temporary file cleanup more than once\n\n* Fixes incorrect setting of join related fields\n\n* Fixes memory issues around deparsing index commands\n\n* Fixes reference table access tracking for sequential execution\n\n* Fixes removal of a single node with only reference tables\n\n* Fixes sending commands to coordinator when it is added as a worker\n\n* Fixes write queries with const expressions and COLLATE in various places\n\n* Fixes wrong cancellation message about distributed deadlock\n\n### citus v9.4.2 (October 21, 2020) ###\n\n* Fixes a bug that could lead to multiple maintenance daemons\n\n* Fixes an issue preventing views in reference table modifications\n\n### citus v9.4.1 (September 30, 2020) ###\n\n* Fixes EXPLAIN ANALYZE output truncation\n\n* Fixes a deadlock during transaction recovery\n\n### citus v9.4.0 (July 28, 2020) ###\n\n* Improves COPY by honoring max_adaptive_executor_pool_size config\n\n* Adds support for insert into local table select from distributed table\n\n* Adds support to partially push down tdigest aggregates\n\n* Adds support for receiving binary encoded results from workers using\n  citus.enable_binary_protocol\n\n* Enables joins between local tables and CTEs\n\n* Adds showing query text in EXPLAIN output when explain verbose is true\n\n* Adds support for showing CTE statistics in EXPLAIN ANALYZE\n\n* Adds support for showing amount of data received in EXPLAIN ANALYZE\n\n* Introduces downgrade paths in migration scripts\n\n* Avoids returning incorrect results when changing roles in a transaction\n\n* Fixes `ALTER TABLE IF EXISTS SET SCHEMA` with non-existing table bug\n\n* Fixes `CREATE INDEX CONCURRENTLY` with no index name on a postgres table bug\n\n* Fixes a bug that could cause crashes with certain compile flags\n\n* Fixes a bug with lists of configuration values in ALTER ROLE SET statements\n\n* Fixes a bug that occurs when coordinator is added as a worker node\n\n* Fixes a crash because of overflow in partition id with certain compile flags\n\n* Fixes a crash that may happen if no worker nodes are added\n\n* Fixes a crash that occurs when inserting implicitly coerced constants\n\n* Fixes a crash when aggregating empty tables\n\n* Fixes a memory leak in subtransaction memory handling\n\n* Fixes crash when using rollback to savepoint after cancellation of DML\n\n* Fixes deparsing for queries with anonymous column references\n\n* Fixes distribution of composite types failing to include typemods\n\n* Fixes explain analyze on adaptive executor repartitions\n\n* Fixes possible error throwing in abort handle\n\n* Fixes segfault when evaluating func calls with default params on coordinator\n\n* Fixes several EXPLAIN ANALYZE issues\n\n* Fixes write queries with const expressions and COLLATE in various places\n\n* Fixes wrong cancellation message about distributed deadlocks\n\n* Reports correct INSERT/SELECT method in EXPLAIN\n\n* Disallows triggers on citus tables\n\n### citus v9.3.5 (July 24, 2020) ###\n\n* Fixes `ALTER TABLE IF EXISTS SET SCHEMA` with non-existing table bug\n\n* Fixes `CREATE INDEX CONCURRENTLY` with no index name on a postgres table bug\n\n* Fixes a crash because of overflow in partition id with certain compile flags\n\n### citus v9.3.4 (July 21, 2020) ###\n\n* Fixes a bug that could cause crashes with certain compile flags\n\n* Fixes a bug with lists of configuration values in ALTER ROLE SET statements\n\n* Fixes deparsing for queries with anonymous column references\n\n### citus v9.3.3 (July 10, 2020) ###\n\n* Fixes a memory leak in subtransaction memory handling\n\n### citus v9.3.2 (Jun 22, 2020) ###\n\n* Fixes a version bump issue in 9.3.1\n\n### citus v9.2.6 (Jun 22, 2020) ###\n\n* Fixes a version bump issue in 9.2.5\n\n### citus v9.3.1 (Jun 17, 2020) ###\n\n* Adds support to partially push down tdigest aggregates\n\n* Fixes a crash that occurs when inserting implicitly coerced constants\n\n### citus v9.2.5 (Jun 17, 2020) ###\n\n* Adds support to partially push down tdigest aggregates\n\n* Fixes an issue with distributing tables having generated cols not at the end\n\n### citus v9.3.0 (May 6, 2020) ###\n\n* Adds `max_shared_pool_size` to control number of connections across sessions\n\n* Adds support for window functions on coordinator\n\n* Improves shard pruning logic to understand OR-conditions\n\n* Prevents using an extra connection for intermediate result multi-casts\n\n* Adds propagation of `ALTER ROLE .. SET` statements\n\n* Adds `update_distributed_table_colocation` UDF to update colocation of tables\n\n* Introduces a UDF to truncate local data after distributing a table\n\n* Adds support for creating temp schemas in parallel\n\n* Adds support for evaluation of `nextval` in the target list on coordinator\n\n* Adds support for local execution of `COPY/TRUNCATE/DROP/DDL` commands\n\n* Adds support for local execution of shard creation\n\n* Uses local execution in a transaction block\n\n* Adds support for querying distributed table sizes concurrently\n\n* Allows `master_copy_shard_placement` to replicate placements to new nodes\n\n* Allows table type to be used in target list\n\n* Avoids having multiple maintenance daemons active for a single database\n\n* Defers reference table replication to shard creation time\n\n* Enables joins between local tables and reference tables in transaction blocks\n\n* Ignores pruned target list entries in coordinator plan\n\n* Improves `SIGTERM` handling of maintenance daemon\n\n* Increases the default of `citus.node_connection_timeout` to 30 seconds\n\n* Fixes a bug that occurs when creating remote tasks in local execution\n\n* Fixes a bug that causes some DML queries containing aggregates to fail\n\n* Fixes a bug that could cause failures in queries with subqueries or CTEs\n\n* Fixes a bug that may cause some connection failures to throw errors\n\n* Fixes a bug which caused queries with SRFs and function evalution to fail\n\n* Fixes a bug with generated columns when executing `COPY dist_table TO file`\n\n* Fixes a crash when using non-constant limit clauses\n\n* Fixes a failure when composite types used in prepared statements\n\n* Fixes a possible segfault when dropping dist. table in a transaction block\n\n* Fixes a possible segfault when non-pushdownable aggs are solely used in HAVING\n\n* Fixes a segfault when executing queries using `GROUPING`\n\n* Fixes an error when using `LEFT JOIN with GROUP BY` on primary key\n\n* Fixes an issue with distributing tables having generated cols not at the end\n\n* Fixes automatic SSL permission issue when using \"initdb --allow-group-access\"\n\n* Fixes errors which could occur when subqueries are parameters to aggregates\n\n* Fixes possible issues by invalidating the plan cache in `master_update_node`\n\n* Fixes timing issues which could be caused by changing system clock\n\n### citus v9.2.4 (March 30, 2020) ###\n\n* Fixes a release problem in 9.2.3\n\n### citus v9.2.3 (March 25, 2020) ###\n\n* Do not use C functions that have been banned by Microsoft\n\n* Fixes a bug that causes wrong results with complex outer joins\n\n* Fixes issues found using static analysis\n\n* Fixes left join shard pruning in pushdown planner\n\n* Fixes possibility for segmentation fault in internal aggregate functions\n\n* Fixes possible segfault when non pushdownable aggregates are used in `HAVING`\n\n* Improves correctness of planning subqueries in `HAVING`\n\n* Prevents using old connections for security if `citus.node_conninfo` changed\n\n* Uses Microsoft approved cipher string for default TLS setup\n\n### citus v9.0.2 (March 6, 2020) ###\n\n* Fixes build errors on EL/OL 6 based distros\n\n* Fixes a bug that caused maintenance daemon to fail on standby nodes\n\n* Disallows distributed function creation when replication_model is `statement`\n\n### citus v9.2.2 (March 5, 2020) ###\n\n* Fixes a bug that caused some prepared stmts with function calls to fail\n\n* Fixes a bug that caused some prepared stmts with composite types to fail\n\n* Fixes a bug that caused missing subplan results in workers\n\n* Improves performance of re-partition joins\n\n### citus v9.2.1 (February 14, 2020) ###\n\n* Fixes a bug that could cause crashes if distribution key is NULL\n\n### citus v9.2.0 (February 10, 2020) ###\n\n* Adds support for `INSERT...SELECT` queries with re-partitioning\n\n* Adds `citus.coordinator_aggregation_strategy` to support more aggregates\n\n* Adds caching of local plans on shards for Citus MX\n\n* Adds compatibility support for dist. object infrastructure from old versions\n\n* Adds defering shard-pruning for fast-path router queries to execution\n\n* Adds propagation of `GRANT ... ON SCHEMA` queries\n\n* Adds support for CTE pushdown via CTE inlining in distributed planning\n\n* Adds support for `ALTER TABLE ... SET SCHEMA` propagation.\n\n* Adds support for `DROP ROUTINE` & `ALTER ROUTINE` commands\n\n* Adds support for any inner join on a reference table\n\n* Changes `citus.log_remote_commands` level to `NOTICE`\n\n* Disallows marking ref. table shards unhealthy in the presence of savepoints\n\n* Disallows placing new shards with shards in TO_DELETE state\n\n* Enables local execution of queries that do not need any data access\n\n* Fixes Makefile trying to cleanup PG directory during install\n\n* Fixes a bug causing errors when planning a query with multiple subqueries\n\n* Fixes a possible deadlock that could happen during shard moves\n\n* Fixes a problem when adding a new node due to tables referenced in func body\n\n* Fixes an issue that could cause joins with reference tables to be slow\n\n* Fixes cached metadata for shard is inconsistent issue\n\n* Fixes inserting multiple composite types as partition key in VALUES\n\n* Fixes unnecessary repartition on joins with more than 4 tables\n\n* Prevents wrong results for replicated partitioned tables after failure\n\n* Restricts LIMIT approximation for non-commutative aggregates\n\n### citus v9.1.2 (December 30, 2019) ###\n\n* Fixes a bug that prevents installation from source\n\n### citus v9.1.1 (December 18, 2019) ###\n\n* Fixes a bug causing SQL-executing UDFs to crash when passing in DDL\n\n* Fixes a bug that caused column_to_column_name to crash for invalid input\n\n* Fixes a bug that caused inserts into local tables w/ dist. subqueries to crash\n\n* Fixes a bug that caused some noop DML statements to fail\n\n* Fixes a bug that prevents dropping reference table columns\n\n* Fixes a crash in IN (.., NULL) queries\n\n* Fixes a crash when calling a distributed function from PL/pgSQL\n\n* Fixes an issue that caused CTEs to sometimes leak connections\n\n* Fixes strange errors in DML with unreachable sublinks\n\n* Prevents statements in SQL functions to run outside of a transaction\n\n### citus v9.1.0 (November 21, 2019) ###\n\n* Adds extensions to distributed object propagation infrastructure\n\n* Adds support for ALTER ROLE propagation\n\n* Adds support for aggregates in create_distributed_function\n\n* Adds support for expressions in reference joins\n\n* Adds support for returning RECORD in multi-shard queries\n\n* Adds support for simple IN subqueries on unique cols in repartition joins\n\n* Adds support for subqueries in HAVING clauses\n\n* Automatically distributes unary aggs w/ combinefunc and non-internal stype\n\n* Disallows distributed func creation when replication_model is 'statement'\n\n* Drops support for deprecated real-time and router executors\n\n* Fixes a bug in local execution that could cause missing rows in RETURNING\n\n* Fixes a bug that caused maintenance daemon to fail on standby nodes\n\n* Fixes a bug that caused other CREATE EXTENSION commands to take longer\n\n* Fixes a bug that prevented REFRESH MATERIALIZED VIEW\n\n* Fixes a bug when view is used in modify statements\n\n* Fixes a memory leak in adaptive executor when query returns many columns\n\n* Fixes underflow init of default values in worker extended op node creation\n\n* Fixes potential segfault in standard_planner inlining functions\n\n* Fixes an issue that caused failures in RHEL 6 builds\n\n* Fixes queries with repartition joins and group by unique column\n\n* Improves CTE/Subquery performance by pruning intermediate rslt broadcasting\n\n* Removes `citus.worker_list_file` GUC\n\n* Revokes usage from the citus schema from public\n\n### citus v9.0.1 (October 25, 2019) ###\n\n* Fixes a memory leak in the executor\n\n* Revokes usage from the citus schema from public\n\n### citus v9.0.0 (October 7, 2019) ###\n\n* Adds support for PostgreSQL 12\n\n* Adds UDFs to help with PostgreSQL upgrades\n\n* Distributes types to worker nodes\n\n* Introduces `create_distributed_function` UDF\n\n* Introduces local query execution for Citus MX\n\n* Implements infrastructure for routing `CALL` to MX workers\n\n* Implements infrastructure for routing `SELECT function()` to MX workers\n\n* Adds support for foreign key constraints between reference tables\n\n* Adds a feature flag to turn off `CREATE TYPE` propagation\n\n* Adds option `citus.single_shard_commit_protocol`\n\n* Adds support for `EXPLAIN SUMMARY`\n\n* Adds support for `GENERATE ALWAYS AS STORED`\n\n* Adds support for `serial` and `smallserial` in MX mode\n\n* Adds support for anon composite types on the target list in router queries\n\n* Avoids race condition between `create_reference_table` & `master_add_node`\n\n* Fixes a bug in schemas of distributed sequence definitions\n\n* Fixes a bug that caused `run_command_on_colocated_placements` to fail\n\n* Fixes a bug that leads to various issues when a connection is lost\n\n* Fixes a schema leak on `CREATE INDEX` statement\n\n* Fixes assert failure in bare `SELECT FROM reference table FOR UPDATE` in MX\n\n* Makes `master_update_node` MX compatible\n\n* Prevents `pg_dist_colocation` from multiple records for reference tables\n\n* Prevents segfault in `worker_partition_protocol` edgecase\n\n* Propagates `ALTER FUNCTION` statements for distributed functions\n\n* Propagates `CREATE OR REPLACE FUNCTION` for distributed functions\n\n* Propagates `REINDEX` on tables & indexes\n\n* Provides a GUC to turn of the new dependency propagation functionality\n\n* Uses 2PC in adaptive executor when dealing with replication factors above 1\n\n### citus v8.3.2 (August 09, 2019) ###\n\n* Fixes performance issues by skipping unnecessary relation access recordings\n\n### citus v8.3.1 (July 29, 2019) ###\n\n* Improves Adaptive Executor performance\n\n### citus v8.3.0 (July 10, 2019) ###\n\n* Adds a new distributed executor: Adaptive Executor\n\n* citus.enable_statistics_collection defaults to off (opt-in)\n\n* Adds support for CTEs in router planner for modification queries\n\n* Adds support for propagating SET LOCAL at xact start\n\n* Adds option to force master_update_node during failover\n\n* Deprecates master_modify_multiple_shards\n\n* Improves round robin logic on router queries\n\n* Creates all distributed schemas as superuser on a separate connection\n\n* Makes COPY adapt to connection use behaviour of previous commands\n\n* Replaces SESSION_LIFESPAN with configurable number of connections at xact end\n\n* Propagates ALTER FOREIGN TABLE commands to workers\n\n* Don't schedule tasks on inactive nodes\n\n* Makes DROP/VALIDATE CONSTRAINT tolerant of ambiguous shard extension\n\n* Fixes an issue with subquery map merge jobs as non-root\n\n* Fixes null pointers caused by partial initialization of ConnParamsHashEntry\n\n* Fixes errors caused by joins with shadowed aliases\n\n* Fixes a regression in outer joining subqueries introduced in 8.2.0\n\n* Fixes a crash that can occur under high memory load\n\n* Fixes a bug that selects wrong worker when using round-robin assignment\n\n* Fixes savepoint rollback after multi-shard modify/copy failure\n\n* Fixes bad foreign constraint name search\n\n* Fixes a bug that prevents stack size to be adjusted\n\n### citus v8.2.2 (June 11, 2019) ###\n\n* Fixes a bug in outer joins wrapped in subqueries\n\n### citus v8.2.1 (April 03, 2019) ###\n\n* Fixes a bug that prevents stack size to be adjusted\n\n### citus v8.1.2 (April 03, 2019) ###\n\n* Don't do redundant ALTER TABLE consistency checks at coordinator\n\n* Fixes a bug that prevents stack size to be adjusted\n\n* Fix an issue with some DECLARE .. CURSOR WITH HOLD commands\n\n### citus v8.2.0 (March 28, 2019) ###\n\n* Removes support and code for PostgreSQL 9.6\n\n* Enable more outer joins with reference tables\n\n* Execute CREATE INDEX CONCURRENTLY in parallel\n\n* Treat functions as transaction blocks\n\n* Add support for column aliases on join clauses\n\n* Skip standard_planner() for trivial queries\n\n* Added support for function calls in joins\n\n* Round-robin task assignment policy relies on local transaction id\n\n* Relax subquery union pushdown restrictions for reference tables\n\n* Speed-up run_command_on_shards()\n\n* Address some memory issues in connection config\n\n* Restrict visibility of get_*_active_transactions functions to pg_monitor\n\n* Don't do redundant ALTER TABLE consistency checks at coordinator\n\n* Queries with only intermediate results do not rely on task assignment policy\n\n* Finish connection establishment in parallel for multiple connections\n\n* Fixes a bug related to pruning shards using a coerced value\n\n* Fix an issue with some DECLARE .. CURSOR WITH HOLD commands\n\n* Fixes a bug that could lead to infinite recursion during recursive planning\n\n* Fixes a bug that could prevent planning full outer joins with using clause\n\n* Fixes a bug that could lead to memory leak on `citus_relation_size`\n\n* Fixes a problem that could cause segmentation fault with recursive planning\n\n* Switch CI solution to CircleCI\n\n### citus v8.0.3 (January 9, 2019) ###\n\n* Fixes maintenance daemon panic due to unreleased spinlock\n\n* Fixes an issue with having clause when used with complex joins\n\n### citus v8.1.1 (January 7, 2019) ###\n\n* Fixes maintenance daemon panic due to unreleased spinlock\n\n* Fixes an issue with having clause when used with complex joins\n\n### citus v8.1.0 (December 17, 2018) ###\n\n* Turns on ssl by default for new installations of citus\n\n* Restricts SSL Ciphers to TLS1.2 and above\n\n* Adds support for INSERT INTO SELECT..ON CONFLICT/RETURNING via coordinator\n\n* Adds support for round-robin task assignment for queries to reference tables\n\n* Adds support for SQL tasks using worker_execute_sql_task UDF with task-tracker\n\n* Adds support for VALIDATE CONSTRAINT queries\n\n* Adds support for disabling hash aggregate with HLL\n\n* Adds user ID suffix to intermediate files generated by task-tracker\n\n* Only allow transmit from pgsql_job_cache directory\n\n* Disallows GROUPING SET clauses in subqueries\n\n* Removes restriction on user-defined group ID in node addition functions\n\n* Relaxes multi-shard modify locks when enable_deadlock_prevention is disabled\n\n* Improves security in task-tracker protocol\n\n* Improves permission checks in internal DROP TABLE functions\n\n* Improves permission checks in cluster management functions\n\n* Cleans up UDFs and fixes permission checks\n\n* Fixes crashes caused by stack size increase under high memory load\n\n* Fixes a bug that could cause maintenance daemon panic\n\n### citus v8.0.2 (December 13, 2018) ###\n\n* Fixes a bug that could cause maintenance daemon panic\n\n* Fixes crashes caused by stack size increase under high memory load\n\n### citus v7.5.4 (December 11, 2018) ###\n\n* Fixes a bug that could cause maintenance daemon panic\n\n### citus v8.0.1 (November 27, 2018) ###\n\n* Execute SQL tasks using worker_execute_sql_task UDF when using task-tracker\n\n### citus v7.5.3 (November 27, 2018) ###\n\n* Execute SQL tasks using worker_execute_sql_task UDF when using task-tracker\n\n### citus v7.5.2 (November 14, 2018) ###\n\n* Fixes inconsistent metadata error when shard metadata caching get interrupted\n\n* Fixes a bug that could cause memory leak\n\n* Fixes a bug that prevents recovering wrong transactions in MX\n\n* Fixes a bug to prevent wrong memory accesses on Citus MX under very high load\n\n* Fixes crashes caused by stack size increase under high memory load\n\n### citus v8.0.0 (October 31, 2018) ###\n\n* Adds support for PostgreSQL 11\n\n* Adds support for applying DML operations on reference tables from MX nodes\n\n* Adds distributed locking to truncated MX tables\n\n* Adds support for running TRUNCATE command from MX worker nodes\n\n* Adds views to provide insight about the distributed transactions\n\n* Adds support for TABLESAMPLE in router queries\n\n* Adds support for INCLUDE option in index creation\n\n* Adds option to allow simple DML commands from hot standby\n\n* Adds support for partitioned tables with replication factor > 1\n\n* Prevents a deadlock on concurrent DROP TABLE and SELECT on Citus MX\n\n* Fixes a bug that prevents recovering wrong transactions in MX\n\n* Fixes a bug to prevent wrong memory accesses on Citus MX under very high load\n\n* Fixes a bug in MX mode, calling DROP SCHEMA with existing partitioned table\n\n* Fixes a bug that could cause modifying CTEs to select wrong execution mode\n\n* Fixes a bug preventing rollback in CREATE PROCEDURE\n\n* Fixes a bug on not being able to drop index on a partitioned table\n\n* Fixes a bug on TRUNCATE when there is a foreign key to a reference table\n\n* Fixes a performance issue in prepared INSERT..SELECT\n\n* Fixes a bug which causes errors on DROP DATABASE IF EXISTS\n\n* Fixes a bug to remove intermediate result directory in pull-push execution\n\n* Improves query pushdown planning performance\n\n* Evaluate functions anywhere in query\n\n### citus v7.5.1 (August 28, 2018) ###\n\n* Improves query pushdown planning performance\n\n* Fixes a bug that could cause modifying CTEs to select wrong execution mode\n\n### citus v7.4.2 (July 27, 2018) ###\n\n* Fixes a segfault in real-time executor during online shard move\n\n### citus v7.5.0 (July 25, 2018) ###\n\n* Adds foreign key support from hash distributed to reference tables\n\n* Adds SELECT ... FOR UPDATE support for router plannable queries\n\n* Adds support for non-partition columns in count distinct\n\n* Fixes a segfault in real-time executor during online shard move\n\n* Fixes ALTER TABLE ADD COLUMN constraint check\n\n* Fixes a bug where INSERT ... SELECT was allowed to update distribution column\n\n* Allows DDL commands to be sequentialized via `citus.multi_shard_modify_mode`\n\n* Adds support for topn_union_agg and topn_add_agg across shards\n\n* Adds support for hll_union_agg and hll_add_agg across shards\n\n* Fixes a bug that might cause shards to have a wrong owner\n\n* GUC select_opens_transaction_block defers opening transaction block on workers\n\n* Utils to implement DDLs for policies in future, warn about being unsupported\n\n* Intermediate results use separate connections to avoid interfering with tasks\n\n* Adds a node_conninfo GUC to set outgoing connection settings\n\n### citus v6.2.6 (July 06, 2018) ###\n\n* Adds support for respecting enable_hashagg in the master planner\n\n### citus v7.4.1 (June 20, 2018) ###\n\n* Fixes a bug that could cause transactions to incorrectly proceed after failure\n\n* Fixes a bug on INSERT ... SELECT queries in prepared statements\n\n### citus v7.4.0 (May 15, 2018) ###\n\n* Adds support for non-pushdownable subqueries and CTEs in UPDATE/DELETE queries\n\n* Adds support for pushdownable subqueries and joins in UPDATE/DELETE queries\n\n* Adds faster shard pruning for subqueries\n\n* Adds partitioning support to MX table\n\n* Adds support for (VACUUM | ANALYZE) VERBOSE\n\n* Adds support for multiple ANDs in `HAVING` for pushdown planner\n\n* Adds support for quotation needy schema names\n\n* Improves operator check time in physical planner for custom data types\n\n* Removes broadcast join logic\n\n* Deprecates `large_table_shard_count` and `master_expire_table_cache()`\n\n* Modifies master_update_node to lock write on shards hosted by node over update\n\n* `DROP TABLE` now drops shards as the currrent user instead of the superuser\n\n* Adds specialised error codes for connection failures\n\n* Improves error messages on connection failure\n\n* Fixes issue which prevented multiple `citus_table_size` calls per query\n\n* Tests are updated to use `create_distributed_table`\n\n### citus v7.2.2 (May 4, 2018) ###\n\n* Fixes a bug that could cause SELECTs to crash during a rebalance\n\n### citus v7.3.0 (March 15, 2018) ###\n\n* Adds support for non-colocated joins between subqueries\n\n* Adds support for window functions that can be pushed down to worker\n\n* Adds support for modifying CTEs\n\n* Adds recursive plan for subqueries in WHERE clause with recurring FROM clause\n\n* Adds support for bool_ and bit_ aggregates\n\n* Adds support for Postgres `jsonb` and `json` aggregation functions\n\n* Adds support for respecting enable_hashagg in the master plan\n\n* Adds support for renaming a distributed table\n\n* Adds support for ALTER INDEX (SET|RESET|RENAME TO) commands\n\n* Adds support for setting storage parameters on distributed tables\n\n* Performance improvements to reduce distributed planning time\n\n* Fixes a bug on planner when aggregate is used in ORDER BY\n\n* Fixes a bug on planner when DISTINCT (ON) clause is used with GROUP BY\n\n* Fixes a bug of creating coordinator planner with distinct and aggregate clause\n\n* Fixes a bug that could open a new connection on every table size function call\n\n* Fixes a bug canceling backends that are not involved in distributed deadlocks\n\n* Fixes count distinct bug on column expressions when used with subqueries\n\n* Improves error handling on worker node failures\n\n* Improves error messages for INSERT queries that have subqueries\n\n### citus v7.2.1 (February 6, 2018) ###\n\n* Fixes count distinct bug on column expressions when used with subqueries\n\n* Adds support for respecting enable_hashagg in the master plan\n\n* Fixes a bug canceling backends that are not involved in distributed deadlocks\n\n### citus v7.2.0 (January 16, 2018) ###\n\n* Adds support for CTEs\n\n* Adds support for subqueries that require merge step\n\n* Adds support for set operations (UNION, INTERSECT, ...)\n\n* Adds support for 2PC auto-recovery\n\n* Adds support for querying local tables in CTEs and subqueries\n\n* Adds support for more SQL coverage in subqueries for reference tables\n\n* Adds support for count(distinct) in queries with a subquery\n\n* Adds support for non-equijoins when there is already an equijoin for queries\n\n* Adds support for non-equijoins when there is already an equijoin for subquery\n\n* Adds support for real-time executor to run in transaction blocks\n\n* Adds infrastructure for storing intermediate distributed query results\n\n* Adds a new GUC named `enable_repartition_joins` for auto executor switch\n\n* Adds support for limiting the intermediate result size\n\n* Improves support for queries with unions containing filters\n\n* Improves support for queries with unions containing joins\n\n* Improves support for subqueries in the `WHERE` clause\n\n* Increases `COPY` throughput\n\n* Enables pushing down queries containing only recurring tuples and `GROUP BY`\n\n* Load-balance queries that read from 0 shards\n\n* Improves support for using functions in subqueries\n\n* Fixes a bug that could cause real-time executor to crash during cancellation\n\n* Fixes a bug that could cause real-time executor to get stuck on cancellation\n\n* Fixes a bug that could block modification queries unnecessarily\n\n* Fixes a bug that could cause assigning wrong IDs to transactions\n\n* Fixes a bug that could cause an assert failure with `ANALYZE` statements\n\n* Fixes a bug that could allow pushing down wrong set operations in subqueries\n\n* Fixes a bug that could cause a deadlock in create_distributed_table\n\n* Fixes a bug that could confuse user about `ANALYZE` usage\n\n* Fixes a bug that could lead to false positive distributed deadlock detections\n\n* Relaxes the locking for DDL commands on partitioned tables\n\n* Relaxes the locking on `COPY` with replication\n\n* Logs more remote commands when citus.log_remote_commands is set\n\n### citus v6.2.5 (January 11, 2018) ###\n\n* Fixes a bug that could crash the coordinator while reporting a remote error\n\n### citus v7.1.2 (January 4, 2018) ###\n\n* Fixes a bug that could cause assigning wrong IDs to transactions\n\n* Increases `COPY` throughput\n\n### citus v7.1.1 (December 1, 2017) ###\n\n* Fixes a bug that could prevent pushing down subqueries with reference tables\n\n* Fixes a bug that could create false positive distributed deadlocks\n\n* Fixes a bug that could prevent running concurrent COPY and multi-shard DDL\n\n* Fixes a bug that could mislead users about `ANALYZE` queries\n\n### citus v7.1.0 (November 14, 2017) ###\n\n* Adds support for native queries with multi shard `UPDATE`/`DELETE` queries\n\n* Expands reference table support in subquery pushdown\n\n* Adds window function support for subqueries and `INSERT ... SELECT` queries\n\n* Adds support for `COUNT(DISTINCT) [ON]` queries on non-partition columns\n\n* Adds support for `DISTINCT [ON]` queries on non-partition columns\n\n* Introduces basic usage statistic collector\n\n* Adds support for setting replica identity while creating distributed tables\n\n* Adds support for `ALTER TABLE ... REPLICA IDENTITY` queries\n\n* Adds pushdown support for `LIMIT` and `HAVING` grouped by partition key\n\n* Adds support for `INSERT ... SELECT` queries via worker nodes on MX clusters\n\n* Adds support for adding primary key using already defined index\n\n* Adds parameter to shard copy functions to support distinct replication models\n\n* Changes `shard_name` UDF to omit public schema name\n\n* Adds `master_move_node` UDF to make changes on nodename/nodeport more easy\n\n* Fixes a bug that could cause casting error with `INSERT ... SELECT` queries\n\n* Fixes a bug that could prevent upgrading servers from Citus 6.1\n\n* Fixes a bug that could prevent attaching partitions to a table in schema\n\n* Fixes a bug that could prevent adding a node to cluster with reference table\n\n* Fixes a bug that could cause a crash with `INSERT ... SELECT` queries\n\n* Fixes a bug that could prevent creating a partitoned table on Cloud\n\n* Implements various performance improvements\n\n* Adds internal infrastructures and tests to improve development process\n\n* Addresses various race conditions and deadlocks\n\n* Improves and standardizes error messages\n\n### citus v7.0.3 (October 16, 2017) ###\n\n* Fixes several bugs that could cause crash\n\n* Fixes a bug that could cause deadlock while creating reference tables\n\n* Fixes a bug that could cause false-positives in deadlock detection\n\n* Fixes a bug that could cause  2PC recovery not to work from MX workers\n\n* Fixes a bug that could cause cache incohorency\n\n* Fixes a bug that could cause maintenance daemon to skip cache invalidations\n\n* Improves performance of transaction recovery by using correct index\n\n### citus v7.0.2 (September 28, 2017) ###\n\n* Updates task-tracker to limit file access\n\n### citus v6.2.4 (September 28, 2017) ###\n\n* Updates task-tracker to limit file access\n\n### citus v6.1.3 (September 28, 2017) ###\n\n* Updates task-tracker to limit file access\n\n### citus v7.0.1 (September 12, 2017) ###\n\n* Fixes a bug that could cause memory leaks in `INSERT ... SELECT` queries\n\n* Fixes a bug that could cause incorrect execution of prepared statements\n\n* Fixes a bug that could cause excessive memory usage during COPY\n\n* Incorporates latest changes from core PostgreSQL code\n\n### citus v7.0.0 (August 28, 2017) ###\n\n* Adds support for PostgreSQL 10\n\n* Drops support for PostgreSQL 9.5\n\n* Adds support for multi-row `INSERT`\n\n* Adds support for router `UPDATE` and `DELETE` queries with subqueries\n\n* Adds infrastructure for distributed deadlock detection\n\n* Deprecates `enable_deadlock_prevention` flag\n\n* Adds support for partitioned tables\n\n* Adds support for creating `UNLOGGED` tables\n\n* Adds support for `SAVEPOINT`\n\n* Adds UDF `citus_create_restore_point` for taking distributed snapshots\n\n* Adds support for evaluating non-pushable `INSERT ... SELECT` queries\n\n* Adds support for subquery pushdown on reference tables\n\n* Adds shard pruning support for `IN` and `ANY`\n\n* Adds support for `UPDATE` and `DELETE` commands that prune down to 0 shard\n\n* Enhances transaction support by relaxing some transaction restrictions\n\n* Fixes a bug causing crash if distributed table has no shards\n\n* Fixes a bug causing crash when removing inactive node\n\n* Fixes a bug causing failure during `COPY` on tables with dropped columns\n\n* Fixes a bug causing failure during `DROP EXTENSION`\n\n* Fixes a bug preventing executing `VACUUM` and `INSERT` concurrently\n\n* Fixes a bug in prepared `INSERT` statements containing an implicit cast\n\n* Fixes several issues related to statement cancellations and connections\n\n* Fixes several 2PC related issues\n\n* Removes an unnecessary dependency causing warning messages in pg_dump\n\n* Adds internal infrastructure for follower clusters\n\n* Adds internal infrastructure for progress tracking\n\n* Implements various performance improvements\n\n* Adds internal infrastructures and tests to improve development process\n\n* Addresses various race conditions and deadlocks\n\n* Improves and standardizes error messages\n\n### citus v6.2.3 (July 13, 2017) ###\n\n* Fixes a crash during execution of local CREATE INDEX CONCURRENTLY\n\n* Fixes a bug preventing usage of quoted column names in COPY\n\n* Fixes a bug in prepared INSERTs with implicit cast in partition column\n\n* Relaxes locks in VACUUM to ensure concurrent execution with INSERT\n\n### citus v6.2.2 (May 31, 2017) ###\n\n* Fixes a common cause of deadlocks when repairing tables with foreign keys\n\n### citus v6.2.1 (May 24, 2017) ###\n\n* Relaxes version-check logic to avoid breaking non-distributed commands\n\n### citus v6.2.0 (May 16, 2017) ###\n\n* Increases SQL subquery coverage by pushing down more kinds of queries\n\n* Adds CustomScan API support to allow read-only transactions\n\n* Adds support for `CREATE/DROP INDEX CONCURRENTLY`\n\n* Adds support for `ALTER TABLE ... ADD CONSTRAINT`\n\n* Adds support for `ALTER TABLE ... RENAME COLUMN`\n\n* Adds support for `DISABLE/ENABLE TRIGGER ALL`\n\n* Adds support for expressions in the partition column in INSERTs\n\n* Adds support for query parameters in combination with function evaluation\n\n* Adds support for creating distributed tables from non-empty local tables\n\n* Adds UDFs to get size of distributed tables\n\n* Adds UDFs to add a new node without replicating reference tables\n\n* Adds checks to prevent running Citus binaries with wrong metadata tables\n\n* Improves shard pruning performance for range queries\n\n* Improves planner performance for joins involving co-located tables\n\n* Improves shard copy performance by creating indexes after copy\n\n* Improves task-tracker performance by batching several status checks\n\n* Enables router planner for queries on range partitioned table\n\n* Changes `TRUNCATE` to drop local data only if `enable_ddl_propagation` is off\n\n* Starts to execute DDL on coordinator before workers\n\n* Fixes a bug causing incorrectly reading invalidated cache\n\n* Fixes a bug related to creation of schemas of in workers with incorrect owner\n\n* Fixes a bug related to concurrent run of shard drop functions\n\n* Fixes a bug related to `EXPLAIN ANALYZE` with DML queries\n\n* Fixes a bug related to SQL functions in FROM clause\n\n* Adds a GUC variable to report cross shard queries\n\n* Fixes a bug related to partition columns without native hash function\n\n* Adds internal infrastructures and tests to improve development process\n\n* Addresses various race conditions and deadlocks\n\n* Improves and standardizes error messages\n\n### citus v6.1.2 (May 31, 2017) ###\n\n* Fixes a common cause of deadlocks when repairing tables with foreign keys\n\n### citus v6.1.1 (May 5, 2017) ###\n\n* Fixes a crash caused by router executor use after connection timeouts\n\n* Fixes a crash caused by relation cache invalidation during COPY\n\n* Fixes bug related to DDL use within PL/pgSQL functions\n\n* Fixes a COPY bug related to types lacking binary output functions\n\n* Fixes a bug related to modifications with parameterized partition values\n\n* Fixes improper value interpolation in worker sequence generation\n\n* Guards shard pruning logic against zero-shard tables\n\n* Fixes possible NULL pointer dereference and buffer underflow (via PVS-Studio)\n\n* Fixes a INSERT ... SELECT bug that could push down non-partition column JOINs\n\n### citus v6.1.0 (February 9, 2017) ###\n\n* Implements _reference tables_, transactionally replicated to all nodes\n\n* Adds `upgrade_to_reference_table` UDF to upgrade pre-6.1 reference tables\n\n* Expands prepared statement support to nearly all statements\n\n* Adds support for creating `VIEW`s which reference distributed tables\n\n* Adds targeted `VACUUM`/`ANALYZE` support\n\n* Adds support for the `FILTER` clause in aggregate expressions\n\n* Adds support for function evaluation within `INSERT INTO ... SELECT`\n\n* Adds support for creating foreign key constraints with `ALTER TABLE`\n\n* Adds logic to choose router planner for all queries it supports\n\n* Enhances `create_distributed_table` with parameter for explicit colocation\n\n* Adds generally useful utility UDFs previously available as \"Citus Tools\"\n\n* Adds user-facing UDFs for locking shard resources and metadata\n\n* Refactors connection and transaction management; giving consistent experience\n\n* Enhances `COPY` with fully transactional semantics\n\n* Improves support for cancellation for a number of queries and commands\n\n* Adds `column_to_column_name` UDF to help users understand `partkey` values\n\n* Adds `master_disable_node` UDF for temporarily disabling nodes\n\n* Adds proper MX (\"masterless\") metadata propagation logic\n\n* Adds `start_metadata_sync_to_node` UDF to propagate metadata changes to nodes\n\n* Enhances `SERIAL` compatibility with MX tables\n\n* Adds `node_connection_timeout` parameter to control node connection timeouts\n\n* Adds `enable_deadlock_prevention` setting to permit multi-node transactions\n\n* Adds a `replication_model` setting to specify replication of new tables\n\n* Changes the `shard_replication_factor` setting's default value to one\n\n* Adds code to automatically set `max_prepared_transactions` if not configured\n\n* Accelerates lookup of colocated shard placements\n\n* Fixes a bug affecting `INSERT INTO ... SELECT` queries using constant values\n\n* Fixes a bug by ensuring `COPY` does not mark placements inactive\n\n* Fixes a bug affecting reads from `pg_dist_shard_placement` table\n\n* Fixes a crash triggered by creating a foreign key without a column\n\n* Fixes a crash related to accessing catalog tables after aborted transactions\n\n* Fixes a bug affecting JOIN queries requiring repartitions\n\n* Fixes a bug affecting node insertions to `pg_dist_node` table\n\n* Fixes a crash triggered by queries with modifying common table expressions\n\n* Fixes a bug affecting workloads with concurrent shard appends and deletions\n\n* Addresses various race conditions and deadlocks\n\n* Improves and standardizes error messages\n\n### citus v6.0.1 (November 29, 2016) ###\n\n* Fixes a bug causing failures during pg_upgrade\n\n* Fixes a bug preventing DML queries during colocated table creation\n\n* Fixes a bug that caused NULL parameters to be incorrectly passed as text\n\n### citus v6.0.0 (November 7, 2016) ###\n\n* Adds compatibility with PostgreSQL 9.6, now the recommended version\n\n* Removes the `pg_worker_list.conf` file in favor of a `pg_dist_node` table\n\n* Adds `master_add_node` and `master_add_node` UDFs to manage membership\n\n* Removes the `\\stage` command and corresponding csql binary in favor of `COPY`\n\n* Removes `copy_to_distributed_table` in favor of first-class `COPY` support\n\n* Adds support for multiple DDL statements within a transaction\n\n* Adds support for certain foreign key constraints\n\n* Adds support for parallel `INSERT INTO ... SELECT` against colocated tables\n\n* Adds support for the `TRUNCATE` command\n\n* Adds support for `HAVING` clauses in `SELECT` queries\n\n* Adds support for `EXCLUDE` constraints which include the partition column\n\n* Adds support for system columns in queries (`tableoid`, `ctid`, etc.)\n\n* Adds support for relation name extension within `INDEX` definitions\n\n* Adds support for no-op `UPDATE`s of the partition column\n\n* Adds several general-purpose utility UDFs to aid in Citus maintenance\n\n* Adds `master_expire_table_cache` UDF to forcibly expire cached shards\n\n* Parallelizes the processing of DDL commands which affect distributed tables\n\n* Adds support for repartition jobs using composite or custom types\n\n* Enhances object name extension to handle long names and large shard counts\n\n* Parallelizes the `master_modify_multiple_shards` UDF\n\n* Changes distributed table creation to error if the target table is not empty\n\n* Changes the `pg_dist_shard.logicalrelid` column from an `oid` to `regclass`\n\n* Adds a `placementid` column to `pg_dist_shard_placement`, replacing Oid use\n\n* Removes the `pg_dist_shard.shardalias` distribution metadata column\n\n* Adds `pg_dist_partition.repmodel` to track tables using streaming replication\n\n* Adds internal infrastructure to take snapshots of distribution metadata\n\n* Addresses the need to invalidate prepared statements on metadata changes\n\n* Adds a `mark_tables_colocated` UDF for denoting pre-6.0 manual colocation\n\n* Fixes a bug affecting prepared statement execution within PL/pgSQL\n\n* Fixes a bug affecting `COPY` commands using composite types\n\n* Fixes a bug that could cause crashes during `EXPLAIN EXECUTE`\n\n* Separates worker and master job temporary folders\n\n* Eliminates race condition between distributed modification and repair\n\n* Relaxes the requirement that shard repairs also repair colocated shards\n\n* Implements internal functions to track which tables' shards are colocated\n\n* Adds `pg_dist_partition.colocationid` to track colocation group membership\n\n* Extends shard copy and move operations to respect colocation settings\n\n* Adds `pg_dist_local_group` to prepare for future MX-related changes\n\n* Adds `create_distributed_table` to easily create shards and infer colocation\n\n### citus v5.2.2 (November 7, 2016) ###\n\n* Adds support for `IF NOT EXISTS` clause of `CREATE INDEX` command\n\n* Adds support for `RETURN QUERY` and `FOR ... IN` PL/pgSQL features\n\n* Extends the router planner to handle more queries\n\n* Changes `COUNT` of zero-row sets to return `0` rather than an empty result\n\n* Reduces the minimum permitted `task_tracker_delay` to a single millisecond\n\n* Fixes a bug that caused crashes during joins with a `WHERE false` clause\n\n* Fixes a bug triggered by unique violation errors raised in long transactions\n\n* Fixes a bug resulting in multiple registration of transaction callbacks\n\n* Fixes a bug which could result in stale reads of distribution metadata\n\n* Fixes a bug preventing distributed modifications in some PL/pgSQL functions\n\n* Fixes some code paths that could hypothetically read uninitialized memory\n\n* Lowers log level of _waiting for activity_ messages\n\n### citus v5.2.1 (September 6, 2016) ###\n\n* Fixes subquery pushdown to properly extract outer join qualifiers\n\n* Addresses possible memory leak during multi-shard transactions\n\n### citus v5.2.0 (August 15, 2016) ###\n\n* Drops support for PostgreSQL 9.4; PostgreSQL 9.5 is required\n\n* Adds schema support for tables, other named objects (types, operators, etc.)\n\n* Evaluates non-immutable functions on master in all modification commands\n\n* Adds support for SERIAL types in non-partition columns\n\n* Adds support for RETURNING clause in INSERT, UPDATE, and DELETE commands\n\n* Adds support for multi-statement transactions involving a fixed set of nodes\n\n* Full SQL support for SELECT queries which can be executed on a single worker\n\n* Adds option to perform DDL changes using prepared transactions (2PC)\n\n* Adds an `enable_ddl_propagation` parameter to control DDL propagation\n\n* Accelerates shard pruning during merges\n\n* Adds `master_modify_multiple_shards` UDF to modify many shards at once\n\n* Adds COPY support for arrays of user-defined types\n\n* Now supports parameterized prepared statements for certain use cases\n\n* Extends LIMIT/OFFSET support to all executor types\n\n* Constraint violations now fail fast rather than hitting all placements\n\n* Makes `master_create_empty_shard` aware of shard placement policy\n\n* Reduces unnecessary sleep during queries processed by real-time executor\n\n* Improves task tracker executor's task cleanup logic\n\n* Relaxes restrictions on cancellation of DDL commands\n\n* Removes ONLY keyword from worker SELECT queries\n\n* Error message improvements and standardization\n\n* Moves `master_update_shard_statistics` function to `pg_catalog` schema\n\n* Fixes a bug where hash-partitioned anti-joins could return incorrect results\n\n* Now sets storage type correctly for foreign table-backed shards\n\n* Fixes `master_update_shard_statistics` issue with hash-partitioned tables\n\n* Fixes an issue related to extending table names that require escaping\n\n* Reduces risk of row counter overflows during modifications\n\n* Fixes a crash related to FILTER clause use in COUNT DISTINCT subqueries\n\n* Fixes crashes related to partition columns with high attribute numbers\n\n* Fixes certain subquery and join crashes\n\n* Detects flex for build even if PostgreSQL was built without it\n\n* Fixes assert-enabled crash when `all_modifications_commutative` is true\n\n### citus v5.2.0-rc.1 (August 1, 2016) ###\n\n* Initial 5.2.0 candidate\n\n### citus v5.1.1 (June 17, 2016) ###\n\n* Adds complex count distinct expression support in repartitioned subqueries\n\n* Improves task tracker job cleanup logic, addressing a memory leak\n\n* Fixes bug that generated incorrect results for LEFT JOIN queries\n\n* Improves compatibility with Debian's reproducible builds project\n\n* Fixes build issues on FreeBSD platforms\n\n### citus v5.1.0 (May 17, 2016) ###\n\n* Adds distributed COPY to rapidly populate distributed tables\n\n* Adds support for using EXPLAIN on distributed queries\n\n* Recognizes and fast-paths single-shard SELECT statements automatically\n\n* Increases INSERT throughput via shard pruning optimizations\n\n* Improves planner performance for joins involving tables with many shards\n\n* Adds ability to pass columns as arguments to function calls in UPDATEs\n\n* Introduces transaction manager for use by multi-shard commands\n\n* Adds COUNT(DISTINCT ...) pushdown optimization for hash-partitioned tables\n\n* Adds support for certain UNIQUE indexes on hash- or range-partitioned tables\n\n* Deprecates \\stage in favor of using COPY for append-partition tables\n\n* Deprecates `copy_to_distributed_table` in favor of first-class COPY support\n\n* Fixes build problems when using non-packaged PostgreSQL installs\n\n* Fixes bug that sometimes skipped pruning when partitioned by a VARCHAR column\n\n* Fixes bug impeding use of user-defined functions in repartitioned subqueries\n\n* Fixes bug involving queries with equality comparisons of boolean types\n\n* Fixes crash that prevented use alongside `pg_stat_statements`\n\n* Fixes crash arising from SELECT queries that lack a target list\n\n* Improves warning and error messages\n\n### citus v5.1.0-rc.2 (May 10, 2016) ###\n\n* Fixes test failures\n\n* Fixes EXPLAIN output when FORMAT JSON in use\n\n### citus v5.1.0-rc.1 (May 4, 2016) ###\n\n* Initial 5.1.0 candidate\n\n### citus v5.0.1 (April 15, 2016) ###\n\n* Fixes issues on 32-bit systems\n\n### citus v5.0.0 (March 24, 2016) ###\n\n* Public release under AGPLv3\n\n* PostgreSQL extension compatible with PostgreSQL 9.5 and 9.4\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Microsoft Open Source Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\n\nResources:\n\n- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)\n- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)\n- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Citus\n\nWe're happy you want to contribute! You can help us in different ways:\n\n* Open an [issue](https://github.com/citusdata/citus/issues) with\n  suggestions for improvements\n* Fork this repository and submit a pull request\n\nBefore accepting any code contributions we ask that contributors\nsign a Contributor License Agreement (CLA). For an explanation of\nwhy we ask this as well as instructions for how to proceed, see the\n[Microsoft CLA](https://cla.opensource.microsoft.com/).\n\n### Devcontainer / Github Codespaces\n\nThe easiest way to start contributing is via our devcontainer. This container works both locally in visual studio code with docker-desktop/docker-for-mac as well as [Github Codespaces](https://github.com/features/codespaces). To open the project in vscode you will need the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers). For codespaces you will need to [create a new codespace](https://codespace.new/citusdata/citus).\n\nWith the extension installed you can run the following from the command pallet to get started\n\n```\n> Dev Containers: Clone Repository in Container Volume...\n```\n\nIn the subsequent popup paste the url to the repo and hit enter.\n\n```\nhttps://github.com/citusdata/citus\n```\n\nThis will create an isolated Workspace in vscode, complete with all tools required to build, test and run the Citus extension. We keep this container up to date with the supported postgres versions as well as the exact versions of tooling we use.\n\nTo quickly start we suggest splitting your terminal once to have two shells. The left one in the `/workspaces/citus`, the second one changed to `/data`. The left terminal will be used to interact with the project, the right one with a testing cluster.\n\nTo get citus installed from source we run `make install -s` in the first terminal. Once installed you can start a Citus cluster in the second terminal via `citus_dev make citus`. The cluster will run in the background, and can be interacted with via `citus_dev`. To get an overview of the available commands.\n\nWith the Citus cluster running you can connect to the coordinator in the first terminal via `psql -p9700`. Because the coordinator is the most common entrypoint the `PGPORT` environment is set accordingly, so a simple `psql` will connect directly to the coordinator.\n\n### Debugging in the VS code\n\n1. Start Debugging: Press F5 in VS Code to start debugging. When prompted, you'll need to attach the debugger to the appropriate PostgreSQL process.\n\n2. Identify the Process: If you're running a psql command, take note of the PID that appears in your psql prompt. For example:\n```\n[local] citus@citus:9700 (PID: 5436)=#\n```\nThis PID (5436 in this case) indicates the process that you should attach the debugger to.\nIf you are uncertain about which process to attach, you can list all running PostgreSQL processes using the following command:\n```\nps aux | grep postgres\n```\n\nLook for the process associated with the PID you noted. For example:\n```\ncitus      5436  0.0  0.0  0  0 ?        S    14:00   0:00 postgres: citus citus\n```\n4. Attach the Debugger: Once you've identified the correct PID, select that process when prompted in VS Code to attach the debugger. You should now be able to debug the PostgreSQL session tied to the psql command.\n\n5. Set Breakpoints and Debug: With the debugger attached, you can set breakpoints within the code. This allows you to step through the code execution, inspect variables, and fully debug the PostgreSQL instance running in your container.\n\n### Getting and building\n\n[PostgreSQL documentation](https://www.postgresql.org/support/versioning/) has a\nsection on upgrade policy.\n\n\tWe always recommend that all users run the latest available minor release [for PostgreSQL] for whatever major version is in use.\n\nWe expect Citus users to honor this recommendation and use latest available\nPostgreSQL minor release. Failure to do so may result in failures in our test\nsuite. There are some known improvements in PG test architecture such as\n[this commit](https://github.com/postgres/postgres/commit/3f323956128ff8589ce4d3a14e8b950837831803)\nthat are missing in earlier minor versions.\n\n#### Mac\n\n1. Install Xcode\n2. Install packages with Homebrew\n\n  ```bash\n  brew update\n  brew install git postgresql python\n  ```\n\n3. Get, build, and test the code\n\n  ```bash\n  git clone https://github.com/citusdata/citus.git\n\n  cd citus\n  ./configure\n  # If you have already installed the project, you need to clean it first\n  make clean\n  make\n  make install\n  # Optionally, you might instead want to use `make install-all`\n  # since `multi_extension` regression test would fail due to missing downgrade scripts.\n  cd src/test/regress\n\n  pip install pipenv\n  pipenv --rm\n  pipenv install\n  pipenv shell\n\n  make check\n  ```\n\n#### Debian-based Linux (Ubuntu, Debian)\n\n1. Install build dependencies\n\n  ```bash\n  echo \"deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main\" | \\\n       sudo tee /etc/apt/sources.list.d/pgdg.list\n  wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | \\\n       sudo apt-key add -\n  sudo apt-get update\n\n  sudo apt-get install -y postgresql-server-dev-14 postgresql-14 \\\n                          autoconf flex git libcurl4-gnutls-dev libicu-dev \\\n                          libkrb5-dev liblz4-dev libpam0g-dev libreadline-dev \\\n                          libselinux1-dev libssl-dev libxslt1-dev libzstd-dev \\\n                          make uuid-dev\n  ```\n\n2. Get, build, and test the code\n\n  ```bash\n  git clone https://github.com/citusdata/citus.git\n  cd citus\n  ./configure\n  # If you have already installed the project previously, you need to clean it first\n  make clean\n  make\n  sudo make install\n  # Optionally, you might instead want to use `sudo make install-all`\n  # since `multi_extension` regression test would fail due to missing downgrade scripts.\n  cd src/test/regress\n\n  pip install pipenv\n  pipenv --rm\n  pipenv install\n  pipenv shell\n\n  make check\n  ```\n\n#### Red Hat-based Linux (RHEL, CentOS, Fedora)\n\n1. Find the RPM URL for your repo at [yum.postgresql.org](http://yum.postgresql.org/repopackages.php)\n2. Register its contents with Yum:\n\n  ```bash\n  sudo yum install -y <url>\n  ```\n\n3. Register EPEL and SCL repositories for your distro.\n\n  On CentOS:\n\n  ```bash\n  yum install -y centos-release-scl-rh epel-release\n  ```\n\n  On RHEL, see [this RedHat blog post](https://developers.redhat.com/blog/2018/07/07/yum-install-gcc7-clang/) to install set-up SCL first. Then run:\n\n  ```bash\n  yum install -y epel-release\n  ```\n\n4. Install build dependencies\n\n  ```bash\n  sudo yum update -y\n  sudo yum groupinstall -y 'Development Tools'\n  sudo yum install -y postgresql14-devel postgresql14-server     \\\n                      git libcurl-devel libxml2-devel libxslt-devel \\\n                      libzstd-devel llvm-toolset-7-clang llvm5.0 lz4-devel \\\n                      openssl-devel pam-devel readline-devel\n\n  git clone https://github.com/citusdata/citus.git\n  cd citus\n  PG_CONFIG=/usr/pgsql-14/bin/pg_config ./configure\n  # If you have already installed the project previously, you need to clean it first\n  make clean\n  make\n  sudo make install\n  # Optionally, you might instead want to use `sudo make install-all`\n  # since `multi_extension` regression test would fail due to missing downgrade scripts.\n  cd src/test/regress\n\n  pip install pipenv\n  pipenv --rm\n  pipenv install\n  pipenv shell\n\n  make check\n  ```\n\n### Following our coding conventions\n\nOur coding conventions are documented in [STYLEGUIDE.md](STYLEGUIDE.md).\n\n### Making SQL changes\n\nSometimes you need to make change to the SQL that the citus extension runs upon\ncreations. The way this is done is by changing the last file in\n`src/backend/distributed/sql`, or creating it if the last file is from a\npublished release. If you needed to create a new file, also change the\n`default_version` field in `src/backend/distributed/citus.control` to match your\nnew version. All the files in this directory are run in order based on\ntheir name. See [this page in the Postgres\ndocs](https://www.postgresql.org/docs/current/extend-extensions.html) for more\ninformation on how Postgres runs these files.\n\n#### Changing or creating functions\n\nIf you need to change any functions defined by Citus. You should check inside\n`src/backend/distributed/sql/udfs` to see if there is already a directory for\nthis function, if not create one. Then change or create the file called\n`latest.sql` in that directory to match how it should create the function. This\nshould be including any DROP (IF EXISTS), COMMENT and REVOKE statements for this\nfunction.\n\nThen copy the `latest.sql` file to `{version}.sql`, where `{version}` is the\nversion for which this sql change is, e.g. `{9.0-1.sql}`. Now that you've\ncreated this stable snapshot of the function definition for your version you\nshould use it in your actual sql file, e.g.\n`src/backend/distributed/sql/citus--8.3-1--9.0-1.sql`. You do this by using C\nstyle `#include` statements like this:\n```\n#include \"udfs/myudf/9.0-1.sql\"\n```\n\n#### Other SQL\n\nAny other SQL you can put directly in the main sql file, e.g.\n`src/backend/distributed/sql/citus--8.3-1--9.0-1.sql`.\n\n### Backporting a commit to a release branch\n\n1. Check out the release branch that you want to backport to `git checkout release-11.3`\n2. Make sure you have the latest changes `git pull`\n3. Create a new release branch with a unique name `git checkout -b release-11.3-<yourname>`\n4. Cherry-pick the commit that you want to backport `git cherry-pick -x <sha>` (the `-x` is important)\n5. Push the branch `git push`\n6. Wait for tests to pass\n7. If the cherry-pick required non-trivial merge conflicts, create a PR and ask\n   for a review.\n8. After the tests pass on CI, fast-forward the release branch `git push origin release-11.3-<yourname>:release-11.3`\n\n### Running tests\n\nSee [`src/test/regress/README.md`](https://github.com/citusdata/citus/blob/master/src/test/regress/README.md)\n\n### Documentation\n\nUser-facing documentation is published on [docs.citusdata.com](https://docs.citusdata.com/). When adding a new feature, function, or setting, you can open a pull request or issue against the [Citus docs repo](https://github.com/citusdata/citus_docs/).\n\nDetailed descriptions of the implementation for Citus developers are provided in the [Citus Technical Documentation](src/backend/distributed/README.md). It is currently a single file for ease of searching. Please update the documentation if you make any changes that affect the design or add major new features.\n\n# Making a pull request ready for reviews\n\nAsking for help and asking for reviews are two different things. When you're asking for help, you're asking for someone to help you with something that you're not expected to know.\n\nBut when you're asking for a review, you're asking for someone to review your work and provide feedback. So, when you're asking for a review, you're expected to make sure that:\n\n* Your changes don't perform **unnecessary line addition / deletions / style changes on unrelated files / lines**.\n\n* All CI jobs are **passing**, including **style checks** and **flaky test detection jobs**. Note that if you're an external contributor, you don't have to wait CI jobs to run (and finish) because they don't get automatically triggered for external contributors.\n\n* Your PR has necessary amount of **tests** and that they're passing.\n\n* You separated as much as possible work into **separate PRs**, e.g., a prerequisite bugfix, a refactoring etc..\n\n* Your PR doesn't introduce a typo or something that you can easily fix yourself.\n\n* After all CI jobs pass, code-coverage measurement job (CodeCov as of today) then kicks in. That's why it's important to make the **tests passing** first. At that point, you're expected to check **CodeCov annotations** that can be seen in the **Files Changed** tab and expected to make sure that it doesn't complain about any lines that are not covered. For example, it's ok if CodeCov complains about an `ereport()` call that you put for an \"unexpected-but-better-than-crashing\" case, but it's not ok if it complains about an uncovered `if` branch that you added.\n\n* And finally, perform a **self-review** to make sure that:\n  * Code and code-comments reflects the idea **without requiring an extra explanation** via a chat message / email / PR comment.\n    This is important because we don't expect developers to reach out to author / read about the whole discussion in the PR to understand the idea behind a commit merged into `main` branch.\n  * PR description is clear enough.\n  * If-and-only-if you're **introducing a user facing change / bugfix**, your PR has a line that starts with `DESCRIPTION: <Present simple tense word that starts with a capital letter, e.g., Adds support for / Fixes / Disallows>`.\n  * **Commit messages** are clear enough if the commits are doing logically different things.\n"
  },
  {
    "path": "DEVCONTAINER.md",
    "content": "# Devcontainer\n\n## Coredumps\nWhen postgres/citus crashes, there is the option to create a coredump. This is useful for debugging the issue. Coredumps are enabled in the devcontainer by default. However, not all environments are configured correctly out of the box. The most important configuration that is not standardized is the `core_pattern`. The configuration can be verified from the container, however, you cannot change this setting from inside the container as the filesystem containing this setting is in read only mode while inside the container.\n\nTo verify if corefiles are written run the following command in a terminal. This shows the filename pattern with which the corefile will be written.\n```bash\ncat /proc/sys/kernel/core_pattern\n```\n\nThis should be configured with a relative path or simply a simple filename, such as `core`. When your environment shows an absolute path you will need to change this setting. How to change this setting depends highly on the underlying system as the setting needs to be changed on the kernel of the host running the container.\n\nYou can put any pattern in `/proc/sys/kernel/core_pattern` as you see fit. eg. You can add the PID to the core pattern in one of two ways;\n- You either include `%p` in the core_pattern. This gets substituted with the PID of the crashing process.\n- Alternatively you could set `/proc/sys/kernel/core_uses_pid` to `1` in the same way as you set `core_pattern`. This will append the PID to the corefile if `%p` is not explicitly contained in the core_pattern.\n\nWhen a coredump is written you can use the debug/launch configuration `Open core file` which is preconfigured in the devcontainer. This will open a fileprompt that lists all coredumps that are found in your workspace. When you want to debug coredumps from `citus_dev` that are run in your `/data` directory, you can add the data directory to your workspace. In the command pallet of vscode you can run `>Workspace: Add Folder to Workspace...` and select the `/data` directory. This will allow you to open the coredumps from the `/data` directory in the `Open core file` debug configuration.\n\n### Windows (docker desktop)\nWhen running in docker desktop on windows you will most likely need to change this setting. The linux guest in WSL2 that runs your container is the `docker-desktop` environment. The easiest way to get onto the host, where you can change this setting, is to open a powershell window and verify you have the docker-desktop environment listed.\n\n```powershell\nwsl --list\n```\n\nAmong others this should list both `docker-desktop` and `docker-desktop-data`. You can then open a shell in the `docker-desktop` environment.\n\n```powershell\nwsl -d docker-desktop\n```\n\nInside this shell you can verify that you have the right environment by running\n\n```bash\ncat /proc/sys/kernel/core_pattern\n```\n\nThis should show the same configuration as the one you see inside the devcontainer. You can then change the setting by running the following command.\nThis will change the setting for the current session. If you want to make the change permanent you will need to add this to a startup script.\n\n```bash\necho \"core\" > /proc/sys/kernel/core_pattern\n```\n"
  },
  {
    "path": "EXTENSION_COMPATIBILITY.md",
    "content": "Below table is created with Citus 12.1.7 on PG16\n| Extension Name               | Works as Expected   | Notes   |\n|:-----------------------------|:--------------------|:--------|\n| address_standardizer         | Yes                 |         |\n| address_standardizer_data_us | Yes                 |         |\n| age                          | Partially           | Works fine side by side, but graph data cannot be distributed. |\n| amcheck                      | Yes                 |         |\n| anon                         | Partially           | Cannot anonymize distributed tables. It is possible to anonymize local tables. |\n| auto_explain                 | No                  | [Issue #6448](https://github.com/citusdata/citus/issues/6448) |\n| azure                        | Yes                 |         |\n| azure_ai                     | Yes                 |         |\n| azure_storage                | Yes                 |         |\n| bloom                        | Yes                 |         |\n| Btree_gin                    | Yes                 |         |\n| btree_gist                   | Yes                 |         |\n| citext                       | Yes                 |         |\n| Citus_columnar               | Yes                 |         |\n| cube                         | Yes                 |         |\n| dblink                       | Yes                 |         |\n| dict_int                     | Yes                 |         |\n| dict_xsyn                    | Yes                 |         |\n| earthdistance                | Yes                 |         |\n| fuzzystrmatch                | Yes                 |         |\n| hll                          | Yes                 |         |\n| hstore                       | Yes                 |         |\n| hypopg                       | Partially           | Hypopg can work on local tables and individual shards, however, when we create a hypothetical index on a distributed table, citus does not propagate the index creation command to worker nodes, and thus, hypothetical index is not used in explain statements.         |\n| intagg                       | Yes                 |         |\n| intarray                     | Yes                 |         |\n| isn                          | Yes                 |         |\n| lo                           | Partially           | Extension relies on triggers, but Citus does not support triggers over distributed tables |\n| login_hook                   | Yes                 |         |\n| ltree                        | Yes                 |         |\n| oracle_fdw                   | Yes                 |         |\n| orafce                       | Yes                 |         |\n| pageinspect                  | Yes                 |         |\n| pg_buffercache               | Yes                 |         |\n| pg_cron                      | Yes                 |         |\n| pg_diskann                   | Yes                 |         |\n| pg_failover_slots            | To be tested        |         |\n| pg_freespacemap              | Partially           | Users can set citus.override_table_visibility='off'; to get accurate calculation of free space map. |\n| pg_hint_plan                 | Partially           | Works fine side by side, but hints are ignored for distributed queries |\n| pg_partman                   | Yes                 |         |\n| pg_prewarm                   | Partially           | In order to prewarm distributed tables, set \" citus.override_table_visibility\" to off, and run prewarm for each shard. This needs to be done at each node. |\n| pg_repack                    | Partially           | Extension relies on triggers, but Citus does not support triggers over distributed tables. It works fine on local tables. |\n| pg_squeeze                   | Partially           | It can work on local tables, but it is not aware of distributed tables. Users can set citus.override_table_visibility='off'; and then run pg_squeeze for each shard. This needs to be done at each node. |\n| pg_stat_statements           | Yes                 |         |\n| pg_trgm                      | Yes                 |         |\n| pg_visibility                | Partially           | In order to get visibility map of a distributed table, customers can run the functions for shard tables. |\n| pgaadauth                    | Yes                 |         |\n| pgaudit                      | Yes                 |         |\n| pgcrypto                     | Yes                 |         |\n| pglogical                    | No                  |         |\n| pgrowlocks                   | Partially           | It works only with individual shards, not with distributed table names. |\n| pgstattuple                  | Yes                 |         |\n| plpgsql                      | Yes                 |         |\n| plv8                         | Yes                 |         |\n| postgis                      | Yes                 |         |\n| postgis_raster               | Yes                 |         |\n| postgis_sfcgal               | Yes                 |         |\n| postgis_tiger_geocoder       | No                  |         |\n| postgis_topology             | No                  |         |\n| postgres_fdw                 | Yes                 |         |\n| postgres_protobuf            | Yes                 |         |\n| semver                       | Yes                 |         |\n| session_variable             | No                  |         |\n| sslinfo                      | Yes                 |         |\n| tablefunc                    | Yes                 |         |\n| tdigest                      | Yes                 |         |\n| tds_fdw                      | Yes                 |         |\n| timescaledb                  | No                  | [Known to be incompatible with Citus](https://www.citusdata.com/blog/2021/10/22/how-to-scale-postgres-for-time-series-data-with-citus/#:~:text=Postgres%E2%80%99%20built-in%20partitioning) |\n| topn                         | Yes                 |         |\n| tsm_system_rows              | Yes                 |         |\n| tsm_system_time              | Yes                 |         |\n| unaccent                     | Yes                 |         |\n| uuid-ossp                    | Yes                 |         |\n| vector (aka pg_vector)       | Yes                 |         |\n| wal2json                     | To be tested        |         |\n| xml2                         | To be tested        |         |\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<http://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "Makefile",
    "content": "# Citus toplevel Makefile\n\ncitus_subdir = .\ncitus_top_builddir = .\nextension_dir = $(shell $(PG_CONFIG) --sharedir)/extension\n\n# Hint that configure should be run first\nifeq (,$(wildcard Makefile.global))\n  $(error ./configure needs to be run before compiling Citus)\nendif\n\ninclude Makefile.global\n\nall: extension\n\n\n# build columnar only\ncolumnar:\n\t$(MAKE) -C src/backend/columnar all\n# build extension\nextension: $(citus_top_builddir)/src/include/citus_version.h columnar\n\t$(MAKE) -C src/backend/distributed/ all\ninstall-columnar: columnar\n\t$(MAKE) -C src/backend/columnar install\ninstall-extension: extension install-columnar\n\t$(MAKE) -C src/backend/distributed/ install\ninstall-headers: extension\n\t$(MKDIR_P) '$(DESTDIR)$(includedir_server)/distributed/'\n# generated headers are located in the build directory\n\t$(INSTALL_DATA) $(citus_top_builddir)/src/include/citus_version.h '$(DESTDIR)$(includedir_server)/'\n# the rest in the source tree\n\t$(INSTALL_DATA) $(citus_abs_srcdir)/src/include/distributed/*.h '$(DESTDIR)$(includedir_server)/distributed/'\n\nclean-extension:\n\t$(MAKE) -C src/backend/distributed/ clean\n\t$(MAKE) -C src/backend/columnar/ clean\nclean-full:\n\t$(MAKE) -C src/backend/distributed/ clean-full\n.PHONY: extension install-extension clean-extension clean-full\n\ninstall-downgrades:\n\t$(MAKE) -C src/backend/distributed/ install-downgrades\ninstall-all: install-headers\n\t$(MAKE) -C src/backend/columnar/ install-all\n\t$(MAKE) -C src/backend/distributed/ install-all\n\n\n# Add to generic targets\ninstall: install-extension install-headers\nclean: clean-extension\n\n# apply or check style\nreindent:\n\t${citus_abs_top_srcdir}/ci/fix_style.sh\ncheck-style:\n\tblack . --check --quiet\n\tisort . --check --quiet\n\tflake8\n\tcd ${citus_abs_top_srcdir} && citus_indent --quiet --check\n.PHONY: reindent check-style\n\n# depend on install-all so that downgrade scripts are installed as well\ncheck: all install-all\n\t# explicetely does not use $(MAKE) to avoid parallelism\n\tmake -C src/test/regress check\n\n.PHONY: all check clean install install-downgrades install-all\n"
  },
  {
    "path": "Makefile.global.in",
    "content": "# -*-makefile-*-\n# @configure_input@\n# Makefile.global.in - Makefile to be included by all submakes\n#\n# This file is converted by configure into an actual Makefile,\n# replacing the @varname@ placeholders by actual values.\n#\n# This files is intended to contain infrastructure needed by several\n# makefiles, particulary central handling of compilation flags and\n# rules.\n\ncitus_abs_srcdir:=@abs_top_srcdir@/${citus_subdir}\ncitus_abs_top_srcdir:=@abs_top_srcdir@\npostgres_abs_srcdir:=@POSTGRES_SRCDIR@\npostgres_abs_builddir:=@POSTGRES_BUILDDIR@\n\nPG_CONFIG:=@PG_CONFIG@\nPGXS:=$(shell $(PG_CONFIG) --pgxs)\n\n# if both, git is installed and there is a .git directory in the working dir we set the\n# GIT_VERSION to a human readable gitref that resembles the version from which citus is\n# built. During releases it will show the tagname which by convention is the verion of the\n# release\nifneq (@GIT_BIN@,)\nifneq (@HAS_DOTGIT@,)\n\t# try to find a tag that exactly matches the current branch, swallow the error if cannot find such a tag\n\tGIT_VERSION := \"$(shell @GIT_BIN@ describe --exact-match --dirty --always --tags 2>/dev/null)\"\n\n\t# if there is not a tag that exactly matches the branch, then GIT_VERSION would still be empty\n\t# in that case, set GIT_VERSION with current branch's name and the short sha of the HEAD\nifeq ($(GIT_VERSION),\"\")\n\tGIT_VERSION := \"$(shell @GIT_BIN@ rev-parse --abbrev-ref HEAD)(sha: $(shell @GIT_BIN@ rev-parse --short HEAD))\"\nendif\nendif\nendif\n\n# Support for VPATH builds (i.e. builds from outside the source tree)\nvpath_build=@vpath_build@\nifeq ($(vpath_build),yes)\n\toverride VPATH:=$(citus_abs_srcdir)\n\tUSE_VPATH:=$(VPATH)\n\tcitus_top_srcdir:=$(citus_abs_top_srcdir)\n\toverride srcdir=$(VPATH)\nelse\n\tcitus_top_srcdir:=$(citus_top_builddir)\nendif\n\n# Citus is built using PostgreSQL's pgxs\nUSE_PGXS=1\ninclude $(PGXS)\n\n# Remake Makefile.global from Makefile.global.in if the latter\n# changed. In order to trigger this rule, the including file must\n# write `include $(citus_top_builddir)/Makefile.global', not some\n# shortcut thereof.  This makes it less likely to accidentally run\n# with some outdated Makefile.global.\n# Make internally restarts whenever included Makefiles are\n# regenerated.\n$(citus_top_builddir)/Makefile.global: $(citus_abs_top_srcdir)/configure $(citus_top_builddir)/Makefile.global.in $(citus_top_builddir)/config.status\n\tcd @abs_top_builddir@ && ./config.status Makefile.global\n\n# Ensure configuration is generated by the most recent configure,\n# useful for longer existing build directories.\n$(citus_top_builddir)/config.status: $(citus_abs_top_srcdir)/configure $(citus_abs_top_srcdir)/src/backend/distributed/citus.control\n\tcd @abs_top_builddir@ && ./config.status --recheck && ./config.status\n\n# Regenerate configure if configure.ac changed\n$(citus_abs_top_srcdir)/configure: $(citus_abs_top_srcdir)/configure.ac\n\tcd ${citus_abs_top_srcdir} && ./autogen.sh\n\n# If specified via configure, replace the default compiler. Normally\n# we'll build with the one postgres was built with. But it's useful to\n# be able to use a different one, especially when building against\n# distribution packages.\nifneq (@CC@,)\n    override CC=@CC@\nendif\n\n# If detected by our configure script, override the FLEX postgres\n# detected.  That allows to compile citus against a postgres which was\n# built without flex available (possible because generated files are\n# included)\nifneq (@FLEX@,)\n    override FLEX=@FLEX@\nendif\n\n# Add options passed to configure or computed therein, to CFLAGS/CPPFLAGS/...\noverride CFLAGS += @CFLAGS@ @CITUS_CFLAGS@\noverride BITCODE_CFLAGS := $(BITCODE_CFLAGS) @CITUS_BITCODE_CFLAGS@\nifneq ($(GIT_VERSION),)\n    override CFLAGS += -DGIT_VERSION=\\\"$(GIT_VERSION)\\\"\nendif\noverride CPPFLAGS := @CPPFLAGS@ @CITUS_CPPFLAGS@ -I '${citus_abs_top_srcdir}/src/include' -I'${citus_top_builddir}/src/include' $(CPPFLAGS)\noverride LDFLAGS += @LDFLAGS@ @CITUS_LDFLAGS@\n\n# optional file with user defined, additional, rules\n-include ${citus_abs_srcdir}/src/Makefile.custom\n"
  },
  {
    "path": "NOTICE",
    "content": "NOTICES AND INFORMATION\nDo Not Translate or Localize\n\nThis software incorporates material from third parties.\nMicrosoft makes certain open source code available at https://3rdpartysource.microsoft.com,\nor you may send a check or money order for US $5.00, including the product name,\nthe open source component name, platform, and version number, to:\n\nSource Code Compliance Team\nMicrosoft Corporation\nOne Microsoft Way\nRedmond, WA 98052\nUSA\n\nNotwithstanding any other terms, you may reverse engineer this software to the extent\nrequired to debug changes to any libraries licensed under the GNU Lesser General Public License.\n\n---------------------------------------------------------\n\n---------------------------------------------------------\n\nintel/safestringlib 245c4b8cff1d2e7338b7f3a82828fc8e72b29549 - MIT\n\nCopyright (c) 2014-2018 Intel Corporation\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\n================================================================================\n\nCopyright (C) 2012, 2013 Cisco Systems\nAll rights reserved.\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or\nsell copies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n---------------------------------------------------------\n\n\npostgres/postgres 29be9983a64c011eac0b9ee29895cce71e15ea77\n\n\nPostgreSQL Database Management System\n(formerly known as Postgres, then as Postgres95)\n\nPortions Copyright (c) 1996-2020, PostgreSQL Global Development Group\n\nPortions Copyright (c) 1994, The Regents of the University of California\n\nPermission to use, copy, modify, and distribute this software and its\ndocumentation for any purpose, without fee, and without a written agreement\nis hereby granted, provided that the above copyright notice and this\nparagraph and the following two paragraphs appear in all copies.\n\nIN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR\nDIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING\nLOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS\nDOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n\nTHE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,\nINCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS\nON AN \"AS IS\" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO\nPROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.\n\n---------------------------------------------------------\n"
  },
  {
    "path": "README.md",
    "content": "| **<br/>The Citus database is 100% open source.<br/><img width=1000/><br/>Learn what's new in the [Citus 13.0 release blog](https://www.citusdata.com/blog/2025/02/06/distribute-postgresql-17-with-citus-13/) and the [Citus Updates page](https://www.citusdata.com/updates/).<br/><br/>**|\n|---|\n<br/>\n\n\n\n![Citus Banner](images/citus-readme-banner.png)\n\n[![Latest Docs](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://docs.citusdata.com/)\n[![Stack Overflow](https://img.shields.io/badge/Stack%20Overflow-%20-545353?logo=Stack%20Overflow)](https://stackoverflow.com/questions/tagged/citus)\n[![Slack](https://cituscdn.azureedge.net/images/social/slack-badge.svg)](https://slack.citusdata.com/)\n[![Code Coverage](https://codecov.io/gh/citusdata/citus/branch/master/graph/badge.svg)](https://app.codecov.io/gh/citusdata/citus)\n[![Twitter](https://img.shields.io/twitter/follow/citusdata.svg?label=Follow%20@citusdata)](https://twitter.com/intent/follow?screen_name=citusdata)\n\n[![Citus Deb Packages](https://img.shields.io/badge/deb-packagecloud.io-844fec.svg)](https://packagecloud.io/app/citusdata/community/search?q=&filter=debs)\n[![Citus Rpm Packages](https://img.shields.io/badge/rpm-packagecloud.io-844fec.svg)](https://packagecloud.io/app/citusdata/community/search?q=&filter=rpms)\n\n## What is Citus?\n\nCitus is a [PostgreSQL extension](https://www.citusdata.com/blog/2017/10/25/what-it-means-to-be-a-postgresql-extension/) that transforms Postgres into a distributed database—so you can achieve high performance at any scale.\n\nWith Citus, you extend your PostgreSQL database with new superpowers:\n\n- **Distributed tables** are sharded across a cluster of PostgreSQL nodes to combine their CPU, memory, storage and I/O capacity.\n- **References tables** are replicated to all nodes for joins and foreign keys from distributed tables and maximum read performance.\n- **Distributed query engine** routes and parallelizes SELECT, DML, and other operations on distributed tables across the cluster.\n- **Columnar storage** compresses data, speeds up scans, and supports fast projections, both on regular and distributed tables.\n- **Query from any node** enables you to utilize the full capacity of your cluster for distributed queries\n\nYou can use these Citus superpowers to make your Postgres database scale-out ready on a single Citus node. Or you can build a large cluster capable of handling **high transaction throughputs**, especially in **multi-tenant apps**, run **fast analytical queries**, and process large amounts of **time series** or **IoT data** for **real-time analytics**. When your data size and volume grow, you can easily add more worker nodes to the cluster and rebalance the shards.\n\nOur [SIGMOD '21](https://2021.sigmod.org/) paper [Citus: Distributed PostgreSQL for Data-Intensive Applications](https://doi.org/10.1145/3448016.3457551) gives a more detailed look into what Citus is, how it works, and why it works that way.\n\n![Citus scales out from a single node](images/citus-scale-out.png)\n\nSince Citus is an extension to Postgres, you can use Citus with the latest Postgres versions. And Citus works seamlessly with the PostgreSQL tools and extensions you are already familiar with.\n\n- [Why Citus?](#why-citus)\n- [Getting Started](#getting-started)\n- [Using Citus](#using-citus)\n- [Schema-based sharding](#schema-based-sharding)\n- [Setting up with High Availability](#setting-up-with-high-availability)\n- [Documentation](#documentation)\n- [Architecture](#architecture)\n- [When to Use Citus](#when-to-use-citus)\n- [Need Help?](#need-help)\n- [Contributing](#contributing)\n- [Stay Connected](#stay-connected)\n\n## Why Citus?\n\nDevelopers choose Citus for two reasons:\n\n1. Your application is outgrowing a single PostgreSQL node\n\n\tIf the size and volume of your data increases over time, you may start seeing any number of performance and scalability problems on a single PostgreSQL node. For example: High CPU utilization and I/O wait times slow down your queries, SQL queries return out of memory errors, autovacuum cannot keep up and increases table bloat, etc.\n\n\tWith Citus you can distribute and optionally compress your tables to always have enough memory, CPU, and I/O capacity to achieve high performance at scale. The distributed query engine can efficiently route transactions across the cluster, while parallelizing analytical queries and batch operations across all cores. Moreover, you can still use the PostgreSQL features and tools you know and love.\n\n2. PostgreSQL can do things other systems can’t\n\n\tThere are many data processing systems that are built to scale out, but few have as many powerful capabilities as PostgreSQL, including: Advanced joins and subqueries, user-defined functions, update/delete/upsert, constraints and foreign keys, powerful extensions (e.g. PostGIS, HyperLogLog), many types of indexes, time-partitioning, and sophisticated JSON support.\n\n\tCitus makes PostgreSQL’s most powerful capabilities work at any scale, allowing you to handle complex data-intensive workloads on a single database system.\n\n## Getting Started\n\nThe quickest way to get started with Citus is to use the [Azure Cosmos DB for PostgreSQL](https://learn.microsoft.com/azure/cosmos-db/postgresql/quickstart-create-portal) managed service in the cloud—or [set up Citus locally](https://docs.citusdata.com/en/stable/installation/single_node.html).\n\n### Citus Managed Service on Azure\n\nYou can get a fully-managed Citus cluster in minutes through the [Azure Cosmos DB for PostgreSQL portal](https://azure.microsoft.com/products/cosmos-db/). Azure will manage your backups, high availability through auto-failover, software updates, monitoring, and more for all of your servers. To get started Citus on Azure, use the [Azure Cosmos DB for PostgreSQL Quickstart](https://learn.microsoft.com/azure/cosmos-db/postgresql/quickstart-create-portal).\n\n### Running Citus using Docker\n\nThe smallest possible Citus cluster is a single PostgreSQL node with the Citus extension, which means you can try out Citus by running a single Docker container.\n\n```bash\n# run PostgreSQL with Citus on port 5500\ndocker run -d --name citus -p 5500:5432 -e POSTGRES_PASSWORD=mypassword citusdata/citus\n\n# connect using psql within the Docker container\ndocker exec -it citus psql -U postgres\n\n# or, connect using local psql\npsql -U postgres -d postgres -h localhost -p 5500\n```\n\n### Install Citus locally\n\nIf you already have a local PostgreSQL installation, the easiest way to install Citus is to use our packaging repo\n\nInstall packages on Ubuntu / Debian:\n\n```bash\ncurl https://install.citusdata.com/community/deb.sh > add-citus-repo.sh\nsudo bash add-citus-repo.sh\nsudo apt-get -y install postgresql-17-citus-13.0\n```\n\nInstall packages on Red Hat:\n```bash\ncurl https://install.citusdata.com/community/rpm.sh > add-citus-repo.sh\nsudo bash add-citus-repo.sh\nsudo yum install -y citus130_17\n```\n\nTo add Citus to your local PostgreSQL database, add the following to `postgresql.conf`:\n\n```\nshared_preload_libraries = 'citus'\n```\n\nAfter restarting PostgreSQL, connect using `psql` and run:\n\n```sql\nCREATE EXTENSION citus;\n````\nYou’re now ready to get started and use Citus tables on a single node.\n\n### Install Citus on multiple nodes\n\nIf you want to set up a multi-node cluster, you can also set up additional PostgreSQL nodes with the Citus extensions and add them to form a Citus cluster:\n\n```sql\n-- before adding the first worker node, tell future worker nodes how to reach the coordinator\nSELECT citus_set_coordinator_host('10.0.0.1', 5432);\n\n-- add worker nodes\nSELECT citus_add_node('10.0.0.2', 5432);\nSELECT citus_add_node('10.0.0.3', 5432);\n\n-- rebalance the shards over the new worker nodes\nSELECT rebalance_table_shards();\n```\n\nFor more details, see our [documentation on how to set up a multi-node Citus cluster](https://docs.citusdata.com/en/stable/installation/multi_node.html) on various operating systems.\n\n## Using Citus\n\nOnce you have your Citus cluster, you can start creating distributed tables, reference tables and use columnar storage.\n\n### Creating Distributed Tables\n\nThe `create_distributed_table` UDF will transparently shard your table locally or across the worker nodes:\n\n```sql\nCREATE TABLE events (\n  device_id bigint,\n  event_id bigserial,\n  event_time timestamptz default now(),\n  data jsonb not null,\n  PRIMARY KEY (device_id, event_id)\n);\n\n-- distribute the events table across shards placed locally or on the worker nodes\nSELECT create_distributed_table('events', 'device_id');\n```\n\nAfter this operation, queries for a specific device ID will be efficiently routed to a single worker node, while queries across device IDs will be parallelized across the cluster.\n\n```sql\n-- insert some events\nINSERT INTO events (device_id, data)\nSELECT s % 100, ('{\"measurement\":'||random()||'}')::jsonb FROM generate_series(1,1000000) s;\n\n-- get the last 3 events for device 1, routed to a single node\nSELECT * FROM events WHERE device_id = 1 ORDER BY event_time DESC, event_id DESC LIMIT 3;\n┌───────────┬──────────┬───────────────────────────────┬───────────────────────────────────────┐\n│ device_id │ event_id │          event_time           │                 data                  │\n├───────────┼──────────┼───────────────────────────────┼───────────────────────────────────────┤\n│         1 │  1999901 │ 2021-03-04 16:00:31.189963+00 │ {\"measurement\": 0.88722643925054}     │\n│         1 │  1999801 │ 2021-03-04 16:00:31.189963+00 │ {\"measurement\": 0.6512231304621992}   │\n│         1 │  1999701 │ 2021-03-04 16:00:31.189963+00 │ {\"measurement\": 0.019368766051897524} │\n└───────────┴──────────┴───────────────────────────────┴───────────────────────────────────────┘\n(3 rows)\n\nTime: 4.588 ms\n\n-- explain plan for a query that is parallelized across shards, which shows the plan for\n-- a query one of the shards and how the aggregation across shards is done\nEXPLAIN (VERBOSE ON) SELECT count(*) FROM events;\n┌────────────────────────────────────────────────────────────────────────────────────┐\n│                                     QUERY PLAN                                     │\n├────────────────────────────────────────────────────────────────────────────────────┤\n│ Aggregate                                                                          │\n│   Output: COALESCE((pg_catalog.sum(remote_scan.count))::bigint, '0'::bigint)       │\n│   ->  Custom Scan (Citus Adaptive)                                                 │\n│         ...                                                                        │\n│         ->  Task                                                                   │\n│               Query: SELECT count(*) AS count FROM events_102008 events WHERE true │\n│               Node: host=localhost port=5432 dbname=postgres                       │\n│               ->  Aggregate                                                        │\n│                     ->  Seq Scan on public.events_102008 events                    │\n└────────────────────────────────────────────────────────────────────────────────────┘\n```\n\n### Creating Distributed Tables with Co-location\n\nDistributed tables that have the same distribution column can be co-located to enable high performance distributed joins and foreign keys between distributed tables.\nBy default, distributed tables will be co-located based on the type of the distribution column, but you define co-location explicitly with the `colocate_with` argument in `create_distributed_table`.\n\n```sql\nCREATE TABLE devices (\n  device_id bigint primary key,\n  device_name text,\n  device_type_id int\n);\nCREATE INDEX ON devices (device_type_id);\n\n-- co-locate the devices table with the events table\nSELECT create_distributed_table('devices', 'device_id', colocate_with := 'events');\n\n-- insert device metadata\nINSERT INTO devices (device_id, device_name, device_type_id)\nSELECT s, 'device-'||s, 55 FROM generate_series(0, 99) s;\n\n-- optionally: make sure the application can only insert events for a known device\nALTER TABLE events ADD CONSTRAINT device_id_fk\nFOREIGN KEY (device_id) REFERENCES devices (device_id);\n\n-- get the average measurement across all devices of type 55, parallelized across shards\nSELECT avg((data->>'measurement')::double precision)\nFROM events JOIN devices USING (device_id)\nWHERE device_type_id = 55;\n\n┌────────────────────┐\n│        avg         │\n├────────────────────┤\n│ 0.5000191877513974 │\n└────────────────────┘\n(1 row)\n\nTime: 209.961 ms\n```\n\nCo-location also helps you scale [INSERT..SELECT](https://docs.citusdata.com/en/stable/articles/aggregation.html), [stored procedures](https://www.citusdata.com/blog/2020/11/21/making-postgres-stored-procedures-9x-faster-in-citus/), and [distributed transactions](https://www.citusdata.com/blog/2017/06/02/scaling-complex-sql-transactions/).\n\n### Distributing Tables without interrupting the application\n\n\nSome of you already start with Postgres, and decide to distribute tables later on while your application using the tables. In that case, you want to avoid downtime for both reads and writes. `create_distributed_table` command block writes (e.g., DML commands) on the table until the command is finished. Instead, with `create_distributed_table_concurrently` command, your application can continue to read and write the data even during the command.\n\n\n```sql\nCREATE TABLE device_logs (\n  device_id bigint primary key,\n  log text\n);\n\n-- insert device logs\nINSERT INTO device_logs (device_id, log)\nSELECT s, 'device log:'||s FROM generate_series(0, 99) s;\n\n-- convert device_logs into a distributed table without interrupting the application\nSELECT create_distributed_table_concurrently('device_logs', 'device_id', colocate_with := 'devices');\n\n\n-- get the count of the logs, parallelized across shards\nSELECT count(*) FROM device_logs;\n\n┌───────┐\n│ count │\n├───────┤\n│   100 │\n└───────┘\n(1 row)\n\nTime: 48.734 ms\n```\n\n\n\n### Creating Reference Tables\n\nWhen you need fast joins or foreign keys that do not include the distribution column, you can use `create_reference_table` to replicate a table across all nodes in the cluster.\n\n```sql\nCREATE TABLE device_types (\n  device_type_id int primary key,\n  device_type_name text not null unique\n);\n\n-- replicate the table across all nodes to enable foreign keys and joins on any column\nSELECT create_reference_table('device_types');\n\n-- insert a device type\nINSERT INTO device_types (device_type_id, device_type_name) VALUES (55, 'laptop');\n\n-- optionally: make sure the application can only insert devices with known types\nALTER TABLE devices ADD CONSTRAINT device_type_fk\nFOREIGN KEY (device_type_id) REFERENCES device_types (device_type_id);\n\n-- get the last 3 events for devices whose type name starts with laptop, parallelized across shards\nSELECT device_id, event_time, data->>'measurement' AS value, device_name, device_type_name\nFROM events JOIN devices USING (device_id) JOIN device_types USING (device_type_id)\nWHERE device_type_name LIKE 'laptop%' ORDER BY event_time DESC LIMIT 3;\n\n┌───────────┬───────────────────────────────┬─────────────────────┬─────────────┬──────────────────┐\n│ device_id │          event_time           │        value        │ device_name │ device_type_name │\n├───────────┼───────────────────────────────┼─────────────────────┼─────────────┼──────────────────┤\n│        60 │ 2021-03-04 16:00:31.189963+00 │ 0.28902084163415864 │ device-60   │ laptop           │\n│         8 │ 2021-03-04 16:00:31.189963+00 │ 0.8723803076285073  │ device-8    │ laptop           │\n│        20 │ 2021-03-04 16:00:31.189963+00 │ 0.8177634801548557  │ device-20   │ laptop           │\n└───────────┴───────────────────────────────┴─────────────────────┴─────────────┴──────────────────┘\n(3 rows)\n\nTime: 146.063 ms\n```\n\nReference tables enable you to scale out complex data models and take full advantage of relational database features.\n\n### Creating Tables with Columnar Storage\n\nTo use columnar storage in your PostgreSQL database, all you need to do is add `USING columnar` to your `CREATE TABLE` statements and your data will be automatically compressed using the columnar access method.\n\n```sql\nCREATE TABLE events_columnar (\n  device_id bigint,\n  event_id bigserial,\n  event_time timestamptz default now(),\n  data jsonb not null\n)\nUSING columnar;\n\n-- insert some data\nINSERT INTO events_columnar (device_id, data)\nSELECT d, '{\"hello\":\"columnar\"}' FROM generate_series(1,10000000) d;\n\n-- create a row-based table to compare\nCREATE TABLE events_row AS SELECT * FROM events_columnar;\n\n-- see the huge size difference!\n\\d+\n                                          List of relations\n┌────────┬──────────────────────────────┬──────────┬───────┬─────────────┬────────────┬─────────────┐\n│ Schema │             Name             │   Type   │ Owner │ Persistence │    Size    │ Description │\n├────────┼──────────────────────────────┼──────────┼───────┼─────────────┼────────────┼─────────────┤\n│ public │ events_columnar              │ table    │ marco │ permanent   │ 25 MB      │             │\n│ public │ events_row                   │ table    │ marco │ permanent   │ 651 MB     │             │\n└────────┴──────────────────────────────┴──────────┴───────┴─────────────┴────────────┴─────────────┘\n(2 rows)\n```\n\nYou can use columnar storage by itself, or in a distributed table to combine the benefits of compression and the distributed query engine.\n\nWhen using columnar storage, you should only load data in batch using `COPY` or `INSERT..SELECT` to achieve good  compression. Update, delete, and foreign keys are currently unsupported on columnar tables. However, you can use partitioned tables in which newer partitions use row-based storage, and older partitions are compressed using columnar storage.\n\nTo learn more about columnar storage, check out the [columnar storage README](https://github.com/citusdata/citus/blob/master/src/backend/columnar/README.md).\n\n## Schema-based sharding\n\nAvailable since Citus 12.0, [schema-based sharding](https://docs.citusdata.com/en/stable/get_started/concepts.html#schema-based-sharding) is the shared database, separate schema model, the schema becomes the logical shard within the database. Multi-tenant apps can a use a schema per tenant to easily shard along the tenant dimension. Query changes are not required and the application usually only needs a small modification to set the proper search_path when switching tenants. Schema-based sharding is an ideal solution for microservices, and for ISVs deploying applications that cannot undergo the changes required to onboard row-based sharding.\n\n### Creating distributed schemas\n\nYou can turn an existing schema into a distributed schema by calling `citus_schema_distribute`:\n\n```sql\nSELECT citus_schema_distribute('user_service');\n```\n\nAlternatively, you can set `citus.enable_schema_based_sharding` to have all newly created schemas be automatically converted into distributed schemas:\n\n```sql\nSET citus.enable_schema_based_sharding TO ON;\n\nCREATE SCHEMA AUTHORIZATION user_service;\nCREATE SCHEMA AUTHORIZATION time_service;\nCREATE SCHEMA AUTHORIZATION ping_service;\n```\n\n### Running queries\n\nQueries will be properly routed to schemas based on `search_path` or by explicitly using the schema name in the query.\n\nFor [microservices](https://docs.citusdata.com/en/stable/get_started/tutorial_microservices.html) you would create a USER per service matching the schema name, hence the default `search_path` would contain the schema name. When connected the user queries would be automatically routed and no changes to the microservice would be required.\n\n```sql\nCREATE USER user_service;\nCREATE SCHEMA AUTHORIZATION user_service;\n```\n\nFor typical multi-tenant applications, you would set the search path to the tenant schema name in your application:\n\n```sql\nSET search_path = tenant_name, public;\n```\n\n## Setting up with High Availability\n\nOne of the most popular high availability solutions for PostgreSQL, [Patroni 3.0](https://github.com/zalando/patroni), has [first class support for Citus 10.0 and above](https://patroni.readthedocs.io/en/latest/citus.html#citus), additionally since Citus 11.2 ships with improvements for smoother node switchover in Patroni.\n\nAn example of patronictl list output for the Citus cluster:\n\n```bash\npostgres@coord1:~$ patronictl list demo\n```\n\n```text\n+ Citus cluster: demo ----------+--------------+---------+----+-----------+\n| Group | Member  | Host        | Role         | State   | TL | Lag in MB |\n+-------+---------+-------------+--------------+---------+----+-----------+\n|     0 | coord1  | 172.27.0.10 | Replica      | running |  1 |         0 |\n|     0 | coord2  | 172.27.0.6  | Sync Standby | running |  1 |         0 |\n|     0 | coord3  | 172.27.0.4  | Leader       | running |  1 |           |\n|     1 | work1-1 | 172.27.0.8  | Sync Standby | running |  1 |         0 |\n|     1 | work1-2 | 172.27.0.2  | Leader       | running |  1 |           |\n|     2 | work2-1 | 172.27.0.5  | Sync Standby | running |  1 |         0 |\n|     2 | work2-2 | 172.27.0.7  | Leader       | running |  1 |           |\n+-------+---------+-------------+--------------+---------+----+-----------+\n```\n\n## Documentation\n\nIf you’re ready to get started with Citus or want to know more, we recommend reading the [Citus open source documentation](https://docs.citusdata.com/en/stable/). Or, if you are using Citus on Azure, then the [Azure Cosmos DB for PostgreSQL](https://learn.microsoft.com/azure/cosmos-db/postgresql/introduction) is the place to start.\n\nOur Citus docs contain comprehensive use case guides on how to build a [multi-tenant SaaS application](https://docs.citusdata.com/en/stable/use_cases/multi_tenant.html), [real-time analytics dashboard]( https://docs.citusdata.com/en/stable/use_cases/realtime_analytics.html), or work with [time series data](https://docs.citusdata.com/en/stable/use_cases/timeseries.html).\n\n## Architecture\n\nA Citus database cluster grows from a single PostgreSQL node into a cluster by adding worker nodes. In a Citus cluster, the original node to which the application connects is referred to as the coordinator node. The Citus coordinator contains both the metadata of distributed tables and reference tables, as well as regular (local) tables, sequences, and other database objects (e.g. foreign tables).\n\nData in distributed tables is stored in “shards”, which are actually just regular PostgreSQL tables on the worker nodes. When querying a distributed table on the coordinator node, Citus will send regular SQL queries to the worker nodes. That way, all the usual PostgreSQL optimizations and extensions can automatically be used with Citus.\n\n![Citus architecture](images/citus-architecture.png)\n\nWhen you send a query in which all (co-located) distributed tables have the same filter on the distribution column, Citus will automatically detect that and send the whole query to the worker node that stores the data. That way, arbitrarily complex queries are supported with minimal routing overhead, which is especially useful for scaling transactional workloads. If queries do not have a specific filter, each shard is queried in parallel, which is especially useful in analytical workloads. The Citus distributed executor is adaptive and is designed to handle both query types at the same time on the same system under high concurrency, which enables large-scale mixed workloads.\n\nThe schema and metadata of distributed tables and reference tables are automatically synchronized to all the nodes in the cluster. That way, you can connect to any node to run distributed queries. Schema changes and cluster administration still need to go through the coordinator.\n\nDetailed descriptions of the implementation for Citus developers are provided in the [Citus Technical Documentation](src/backend/distributed/README.md).\n\n## When to use Citus\n\nCitus is uniquely capable of scaling both analytical and transactional workloads with up to petabytes of data. Use cases in which Citus is commonly used:\n\n- **[Customer-facing analytics dashboards](http://docs.citusdata.com/en/stable/use_cases/realtime_analytics.html)**:\n  Citus enables you to build analytics dashboards that simultaneously ingest and process large amounts of data in the database and give sub-second response times even with a large number of concurrent users.\n\n  The advanced parallel, distributed query engine in Citus combined with PostgreSQL features such as [array types](https://www.postgresql.org/docs/current/arrays.html), [JSONB](https://www.postgresql.org/docs/current/datatype-json.html), [lateral joins](https://heap.io/blog/engineering/postgresqls-powerful-new-join-type-lateral), and extensions like [HyperLogLog](https://github.com/citusdata/postgresql-hll) and [TopN](https://github.com/citusdata/postgresql-topn) allow you to build responsive analytics dashboards no matter how many customers or how much data you have.\n\n  Example real-time analytics users: [Algolia](https://www.citusdata.com/customers/algolia)\n\n- **[Time series data](http://docs.citusdata.com/en/stable/use_cases/timeseries.html)**:\n  Citus enables you to process and analyze very large amounts of time series data. The biggest Citus clusters store well over a petabyte of time series data and ingest terabytes per day.\n\n  Citus integrates seamlessly with [Postgres table partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html) and has [built-in functions for partitioning by time](https://www.citusdata.com/blog/2021/10/22/how-to-scale-postgres-for-time-series-data-with-citus/), which can speed up queries and writes on time series tables. You can take advantage of Citus’s parallel, distributed query engine for fast analytical queries, and use the built-in *columnar storage* to compress old partitions.\n\n  Example users: [MixRank](https://www.citusdata.com/customers/mixrank)\n\n- **[Software-as-a-service (SaaS) applications](http://docs.citusdata.com/en/stable/use_cases/multi_tenant.html)**:\n  SaaS and other multi-tenant applications need to be able to scale their database as the number of tenants/customers grows. Citus enables you to transparently shard a complex data model by the tenant dimension, so your database can grow along with your business.\n\n  By distributing tables along a tenant ID column and co-locating data for the same tenant, Citus can horizontally scale complex (tenant-scoped) queries, transactions, and foreign key graphs. Reference tables and distributed DDL commands make database management a breeze compared to manual sharding. On top of that, you have a built-in distributed query engine for doing cross-tenant analytics inside the database.\n\n  Example multi-tenant SaaS users: [Salesloft](https://fivetran.com/case-studies/replicating-sharded-databases-a-case-study-of-salesloft-citus-data-and-fivetran), [ConvertFlow](https://www.citusdata.com/customers/convertflow)\n\n- **[Microservices](https://docs.citusdata.com/en/stable/get_started/tutorial_microservices.html)**: Citus supports schema based sharding, which allows distributing regular database schemas across many machines. This sharding methodology fits nicely with typical Microservices architecture, where storage is fully owned by the service hence can’t share the same schema definition with other tenants. Citus allows distributing horizontally scalable state across services, solving one of the [main problems](https://stackoverflow.blog/2020/11/23/the-macro-problem-with-microservices/) of microservices.\n\n- **Geospatial**:\n  Because of the powerful [PostGIS](https://postgis.net/) extension to Postgres that adds support for geographic objects into Postgres, many people run spatial/GIS applications on top of Postgres. And since spatial location information has become part of our daily life, well, there are more geospatial applications than ever. When your Postgres database needs to scale out to handle an increased workload, Citus is a good fit.\n\n  Example geospatial users: [Helsinki Regional Transportation Authority (HSL)](https://customers.microsoft.com/story/845146-transit-authority-improves-traffic-monitoring-with-azure-database-for-postgresql-hyperscale), [MobilityDB](https://www.citusdata.com/blog/2020/11/09/analyzing-gps-trajectories-at-scale-with-postgres-mobilitydb/).\n\n## Need Help?\n\n- **Slack**: Ask questions in our Citus community [Slack channel](https://slack.citusdata.com).\n- **GitHub issues**: Please submit issues via [GitHub issues](https://github.com/citusdata/citus/issues).\n- **Documentation**: Our [Citus docs](https://docs.citusdata.com ) have a wealth of resources, including sections on [query performance tuning](https://docs.citusdata.com/en/stable/performance/performance_tuning.html), [useful diagnostic queries](https://docs.citusdata.com/en/stable/admin_guide/diagnostic_queries.html), and [common error messages](https://docs.citusdata.com/en/stable/reference/common_errors.html).\n- **Docs issues**: You can also submit documentation issues via [GitHub issues for our Citus docs](https://github.com/citusdata/citus_docs/issues).\n- **Updates & Release Notes**: Learn about what's new in each Citus version on the [Citus Updates page](https://www.citusdata.com/updates/).\n\n## Contributing\n\nCitus is built on and of open source, and we welcome your contributions. The [CONTRIBUTING.md](CONTRIBUTING.md) file explains how to get started developing the Citus extension itself and our code quality guidelines.\n\n## Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n\n## Stay Connected\n\n- **Twitter**: Follow us [@citusdata](https://twitter.com/citusdata) to track the latest posts & updates on what’s happening.\n- **Citus Blog**: Read our popular [Citus Open Source Blog](https://www.citusdata.com/blog/) for posts about PostgreSQL and Citus.\n- **Citus Newsletter**: Subscribe to our monthly technical [Citus Newsletter](https://www.citusdata.com/join-newsletter) to get a curated collection of our favorite posts, videos, docs, talks, & other Postgres goodies.\n- **Slack**: Our [Citus Public slack](https://slack.citusdata.com/) is a good way to stay connected, not just with us but with other Citus users.\n- **Sister Blog**: Read the PostgreSQL posts on the [Azure Cosmos DB for PostgreSQL blog](https://devblogs.microsoft.com/cosmosdb/category/postgresql/) about our managed service on Azure.\n- **Videos**: Check out this [YouTube playlist](https://www.youtube.com/playlist?list=PLixnExCn6lRq261O0iwo4ClYxHpM9qfVy) of some of our favorite Citus videos and demos. If you want to deep dive into how Citus extends PostgreSQL, you might want to check out Marco Slot’s talk at Carnegie Mellon titled [Citus: Distributed PostgreSQL as an Extension](https://youtu.be/X-aAgXJZRqM) that was part of Andy Pavlo’s Vaccination Database Talks series at CMUDB.\n- **Our other Postgres projects**: Our team also works on other awesome PostgreSQL open source extensions & projects, including: [pg_cron](https://github.com/citusdata/pg_cron), [HyperLogLog](https://github.com/citusdata/postgresql-hll), [TopN](https://github.com/citusdata/postgresql-topn), [pg_auto_failover](https://github.com/citusdata/pg_auto_failover), [activerecord-multi-tenant](https://github.com/citusdata/activerecord-multi-tenant), and [django-multitenant](https://github.com/citusdata/django-multitenant).\n\n___\n\nCopyright © Citus Data, Inc.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.8 BLOCK -->\n\n## Security\n\nMicrosoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).\n\nIf you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.\n\n## Reporting Security Issues\n\n**Please do not report security vulnerabilities through public GitHub issues.**\n\nInstead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).\n\nIf you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).\n\nYou should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).\n\nPlease include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:\n\n  * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)\n  * Full paths of source file(s) related to the manifestation of the issue\n  * The location of the affected source code (tag/branch/commit or direct URL)\n  * Any special configuration required to reproduce the issue\n  * Step-by-step instructions to reproduce the issue\n  * Proof-of-concept or exploit code (if possible)\n  * Impact of the issue, including how an attacker might exploit the issue\n\nThis information will help us triage your report more quickly.\n\nIf you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.\n\n## Preferred Languages\n\nWe prefer all communications to be in English.\n\n## Policy\n\nMicrosoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).\n\n<!-- END MICROSOFT SECURITY.MD BLOCK -->\n"
  },
  {
    "path": "STYLEGUIDE.md",
    "content": "# Coding style\n\nThe existing code-style in our code-base is not super consistent. There are multiple reasons for that. One big reason is because our code-base is relatively old and our standards have changed over time. The second big reason is that our style-guide is different from style-guide of Postgres and some code is copied from Postgres source code and is slightly modified. The below rules are for new code. If you're changing existing code that uses a different style, use your best judgement to decide if you use the rules here or if you match the existing style.\n\n## Using citus_indent\n\nCI pipeline will automatically reject any PRs which do not follow our coding\nconventions. The easiest way to ensure your PR adheres to those conventions is\nto use the [citus_indent](https://github.com/citusdata/tools/tree/develop/uncrustify)\ntool. This tool uses `uncrustify` under the hood.\n\n```bash\n# Uncrustify changes the way it formats code every release a bit. To make sure\n# everyone formats consistently we use version 0.82.0:\ncurl -L https://github.com/uncrustify/uncrustify/archive/uncrustify-0.82.0.tar.gz | tar xz\ncd uncrustify-uncrustify-0.82.0/\nmkdir build\ncd build\ncmake ..\nmake -j5\nsudo make install\ncd ../..\n\ngit clone https://github.com/citusdata/tools.git\ncd tools\nmake uncrustify/.install\n```\n\nOnce you've done that, you can run the `make reindent` command from the top\ndirectory to recursively check and correct the style of any source files in the\ncurrent directory. Under the hood, `make reindent` will run `citus_indent` and\nsome other style corrections for you.\n\nYou can also run the following in the directory of this repository to\nautomatically format all the files that you have changed before committing:\n\n```bash\ncat > .git/hooks/pre-commit << __EOF__\n#!/bin/bash\ncitus_indent --check --diff || { citus_indent --diff; exit 1; }\n__EOF__\nchmod +x .git/hooks/pre-commit\n```\n\n## Other rules we follow that citus_indent does not enforce\n\n* We almost always use **CamelCase**, when naming functions, variables etc., **not snake_case**.\n\n* We also have the habits of using a **lowerCamelCase** for some variables named from their type or from their function name, as shown in the examples:\n\n  ```c\n  bool IsCitusExtensionLoaded = false;\n\n\n  bool\n  IsAlterTableRenameStmt(RenameStmt *renameStmt)\n  {\n    AlterTableCmd *alterTableCommand = NULL;\n    ..\n    ..\n\n    bool isAlterTableRenameStmt = false;\n    ..\n  }\n  ```\n\n* We **start functions with a comment**:\n\n  ```c\n  /*\n   * MyNiceFunction <something in present simple tense, e.g., processes / returns / checks / takes X as input / does Y> ..\n   * <some more nice words> ..\n   * <some more nice words> ..\n   */\n  <static?> <return type>\n  MyNiceFunction(..)\n  {\n    ..\n    ..\n  }\n  ```\n\n* `#includes` needs to be sorted based on below ordering and then alphabetically and we should not include what we don't need in a file:\n\n  * System includes (eg. #include<...>)\n  * Postgres.h (eg. #include \"postgres.h\")\n  * Toplevel imports from postgres, not contained in a directory (eg. #include \"miscadmin.h\")\n  * General postgres includes (eg . #include \"nodes/...\")\n  * Toplevel citus includes, not contained in a directory (eg. #include \"citus_verion.h\")\n  * Columnar includes (eg. #include \"columnar/...\")\n  * Distributed includes (eg. #include \"distributed/...\")\n\n* Comments:\n  ```c\n  /* single line comments start with a lower-case */\n\n  /*\n   * We start multi-line comments with a capital letter\n   * and keep adding a star to the beginning of each line\n   * until we close the comment with a star and a slash.\n   */\n  ```\n\n* Order of function implementations and their declarations in a file:\n\n  We define static functions after the functions that call them. For example:\n\n  ```c\n  #include<..>\n  #include<..>\n  ..\n  ..\n  typedef struct\n  {\n    ..\n    ..\n  } MyNiceStruct;\n  ..\n  ..\n  PG_FUNCTION_INFO_V1(my_nice_udf1);\n  PG_FUNCTION_INFO_V1(my_nice_udf2);\n  ..\n  ..\n  // ..  somewhere on top of the file …\n  static void MyNiceStaticlyDeclaredFunction1(…);\n  static void MyNiceStaticlyDeclaredFunction2(…);\n  ..\n  ..\n\n\n  void\n  MyNiceFunctionExternedViaHeaderFile(..)\n  {\n    ..\n    ..\n    MyNiceStaticlyDeclaredFunction1(..);\n    ..\n    ..\n    MyNiceStaticlyDeclaredFunction2(..);\n    ..\n  }\n\n  ..\n  ..\n\n  // we define this first because it's called by MyNiceFunctionExternedViaHeaderFile()\n  // before MyNiceStaticlyDeclaredFunction2()\n  static void\n  MyNiceStaticlyDeclaredFunction1(…)\n  {\n  }\n  ..\n  ..\n\n  // then we define this\n  static void\n  MyNiceStaticlyDeclaredFunction2(…)\n  {\n  }\n  ```\n"
  },
  {
    "path": "aclocal.m4",
    "content": "dnl aclocal.m4\nm4_include([config/general.m4])\n"
  },
  {
    "path": "autogen.sh",
    "content": "#!/bin/bash\n#\n# autogen.sh converts configure.ac to configure and creates\n# citus_config.h.in. The resuting resulting files are checked into\n# the SCM, to avoid everyone needing autoconf installed.\n\nautoreconf -f\n"
  },
  {
    "path": "cgmanifest.json",
    "content": "{\n    \"Registrations\": [\n        {\n            \"Component\": {\n                \"Type\": \"git\",\n                \"git\": {\n                    \"RepositoryUrl\": \"https://github.com/intel/safestringlib\",\n                    \"CommitHash\": \"245c4b8cff1d2e7338b7f3a82828fc8e72b29549\"\n                }\n            },\n            \"DevelopmentDependency\": false\n        },\n        {\n            \"Component\": {\n                \"Type\": \"git\",\n                \"git\": {\n                    \"RepositoryUrl\": \"https://github.com/postgres/postgres\",\n                    \"CommitHash\": \"29be9983a64c011eac0b9ee29895cce71e15ea77\"\n                }\n            },\n            \"license\": \"PostgreSQL\",\n            \"licenseDetail\": [\n\t\t\t\t\"Portions Copyright (c) 1996-2010, The PostgreSQL Global Development Group\",\n\t\t\t\t\"\",\n\t\t\t\t\"Portions Copyright (c) 1994, The Regents of the University of California\",\n                \"\",\n                \"Permission to use, copy, modify, and distribute this software and its documentation for \",\n                \"any purpose, without fee, and without a written agreement is hereby granted, provided \",\n                \"that the above copyright notice and this paragraph and the following two paragraphs appear \",\n                \"in all copies.\",\n                \"\",\n                \"IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, \",\n                \"INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS \",\n                \"SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE \",\n                \"POSSIBILITY OF SUCH DAMAGE.\",\n                \"\",\n                \"THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, \",\n                \"THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED \",\n                \"HEREUNDER IS ON AN \\\"AS IS\\\" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE \",\n                \"MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.\"\n            ],\n            \"version\": \"0.0.1\",\n            \"DevelopmentDependency\": false\n        }\n\n    ]\n}\n"
  },
  {
    "path": "ci/README.md",
    "content": "# CI scripts\n\nWe have a few scripts that we run in CI to confirm that code confirms to our\nstandards. Be sure you have followed the setup in the [Following our coding\nconventions](https://github.com/citusdata/citus/blob/master/CONTRIBUTING.md#following-our-coding-conventions)\nsection of `CONTRIBUTING.md`. Once you've done that, most of them should be\nfixed automatically, when running:\n```\nmake reindent\n```\n\nSee the sections below for details on what a specific failing script means.\n\n## `citus_indent`\n\nWe format all our code using the coding conventions in the\n[citus_indent](https://github.com/citusdata/tools/tree/develop/uncrustify)\ntool. This tool uses `uncrustify` under the hood. See [Following our coding\nconventions](https://github.com/citusdata/citus/blob/master/CONTRIBUTING.md#following-our-coding-conventions) on how to install this.\n\n## `editorconfig.sh`\n\nYou should install the Editorconfig plugin for your editor/IDE\nhttps://editorconfig.org/\n\n## `banned.h.sh`\n\nYou're using a C library function that is banned by Microsoft, mostly because of\nrisk for buffer overflows. This page lists the Microsoft suggested replacements:\nhttps://liquid.microsoft.com/Web/Object/Read/ms.security/Requirements/Microsoft.Security.SystemsADM.10082#guide\nThese replacements are only available on Windows normally. Since we build for\nLinux we make most of them available with this header file:\n```c\n#include \"distributed/citus_safe_lib.h\"\n```\nThis uses https://github.com/intel/safestringlib to provide them.\n\nHowever, still not all of them are available. For those cases we provide\nsome extra functions in `citus_safe_lib.h`, with similar functionality.\n\nIf none of those replacements match your requirements you have to do one of the\nfollowing:\n1. Add a replacement to `citus_safe_lib.{c,h}` that handles the same error cases\n   that the `{func_name}_s` function that Microsoft suggests.\n2. Add a `/* IGNORE-BANNED */` comment to the line that complains. Doing this\n   requires also adding a comment before explaining why this specific use of the\n   function is safe.\n\n## `build-citus.sh`\n\nThis is the script used during the build phase of the extension. Historically this script\nwas embedded in the docker images. This made maintenance a hassle. Now it lives in tree\nwith the rest of the source code.\n\nWhen this script fails you most likely have a build error on the postgres version it was\nbuilding at the time of the failure. Fix the compile error and push a new version of your\ncode to fix.\n\n## `check_enterprise_merge.sh`\n\nThis check exists to make sure that we can always merge the `master` branch of\n`community` into the `enterprise-master` branch of the `enterprise` repo.\nThere are two conditions in which this check passes:\n\n1. There are no merge conflicts between your PR branch and `enterprise-master` and after this merge the code compiles.\n2. There are merge conflicts, but there is a branch with the same name in the\n   enterprise repo that:\n   1. Contains the last commit of the community branch with the same name.\n   2. Merges cleanly into `enterprise-master`\n3. After merging, the code can be compiled.\n\nIf the job already passes, you are done, nothing further required! Otherwise\nfollow the below steps.\n\n### Prerequisites\n\nBefore continuing with the real steps make sure you have done the following\n(this only needs to be done once):\n1. You have enabled `git rerere` in globally or in your enterprise repo\n   ([docs](https://git-scm.com/docs/git-rerere), [very useful blog](https://medium.com/@porteneuve/fix-conflicts-only-once-with-git-rerere-7d116b2cec67#.3vui844dt)):\n   ```bash\n   # Enables it globally for all repos\n   git config --global rerere.enabled true\n   # Enables it only for the enterprise repo\n   cd <enterprise-repo>\n   git config rerere.enabled true\n   ```\n2. You have set up the `community` remote on your enterprise as\n   [described in CONTRIBUTING.md](https://github.com/citusdata/citus-enterprise/blob/enterprise-master/CONTRIBUTING.md#merging-community-changes-onto-enterprise).\n\n\n#### Important notes on `git rerere`\n\nThis is very useful as it will make sure git will automatically redo merges that\nyou have done before. However, this has a downside too. It will also redo merges\nthat you did, but that were incorrect. Two work around this you can use these\ncommands.\n1. Make `git rerere` forget a merge:\n   ```bash\n   git rerere forget <badly_merged_file>\n   ```\n2. During conflict resolution where `git rerere` already applied the bad merge,\n   simply forgetting it is not enough. Since it is already applied. In that case\n   you also have to undo the apply using:\n   ```bash\n   git checkout --conflict=merge <badly_merged_file>\n   ```\n\n### Actual steps\n\nAfter the prerequisites are met we continue on to the real steps. Say your\nbranch name is `$PR_BRANCH`, we will refer to `$PR_BRANCH` on community as\n`community/$PR_BRANCH` and on enterprise as `enterprise/$PR_BRANCH`. First make\nsure these two things are the case:\n\n1. Get approval from your reviewer for `community/$PR_BRANCH`. Only follow the\n   next steps after you are about to merge the branch to community master.\n2. Make sure your commits are in a nice state, since you should not do\n   \"squash and merge\" on Github later. Otherwise you will certainly get\n   duplicate commits and possibly get merge conflicts with enterprise again.\n\nOnce that's done, you need to create a merged version of your PR branch on the\nenterprise repo. For example if `community` is added as a remote in\nyour enterprise repo, you can do the following:\n\n```bash\nexport PR_BRANCH=<YOUR BRANCHNAME OF THE PR HERE>\ngit checkout enterprise-master\ngit pull # Make sure your local enterprise-master is up to date\ngit fetch community # Fetch your up to date branch name\ngit checkout -b \"$PR_BRANCH\" enterprise-master\n```\nNow you have X in your enterprise repo, which we refer to as\n`enterprise/$PR_BRANCH` (even though in git commands you would reference it as\n`origin/$PR_BRANCH`). This branch is currently the same as `enterprise-master`.\nFirst to make review easier, you should merge community master into it. This\nshould apply without any merge conflicts:\n\n```bash\ngit merge community/master\n```\nNow you need to merge `community/$PR_BRANCH` to `enterprise/$PR_BRANCH`. Solve\nany conflicts and make sure to remove any parts that should not be in enterprise\neven though it doesn't have a conflict, on enterprise repository:\n\n```bash\ngit merge \"community/$PR_BRANCH\"\n```\n\n1. You should push this branch to the enterprise repo. This is so that the job\n   on community will see this branch.\n2. Wait until tests on `enterprise/$PR_BRANCH` pass.\n3. Create a PR on the enterprise repo for your `enterprise/$PR_BRANCH` branch.\n4. You should get approval for the merge conflict changes on\n   `enterprise/$PR_BRANCH`, preferably from the same reviewer as they are\n   familiar with the change.\n5. You should rerun the `check-merge-to-enterprise` check on\n   `community/$PR_BRANCH`. You can use re-run from failed option in circle CI.\n6. You can now merge the PR on community. Be sure to NOT use \"squash and merge\",\n   but instead use the regular \"merge commit\" mode.\n7. You can now merge the PR on enterprise. Be sure to NOT use \"squash and merge\",\n   but instead use the regular \"merge commit\" mode.\n\nThe subsequent PRs on community will be able to pass the\n`check-merge-to-enterprise` check as long as they don't have a conflict with\n`enterprise-master`.\n\n### What to do when your branch got outdated?\n\nSo there's one issue that can occur. Your branch will become outdated with\nmaster and you have to make it up to date. There are two ways to do this using\n`git merge` or `git rebase`. As usual, `git merge` is a bit easier than `git\nrebase`, but clutters git history. This section will explain both. If you don't\nknow which one makes the most sense, start with `git rebase`. It's possible that\nfor whatever reason this doesn't work or becomes very complex, for instance when\nnew merge conflicts appear. Feel free to fall back to `git merge` in that case,\nby using `git rebase --abort`.\n\n#### Updating both branches with `git rebase`\n\nIn the community repo, first update the outdated branch using `rebase`:\n\n```bash\ngit checkout $PR_BRANCH\n# Keep a backup in case you want to fallback to the merge approach\ngit checkout -b ${PR_BRANCH}-backup\ngit checkout $PR_BRANCH\n# Actually update the branch\ngit fetch origin\ngit rebase origin/master\ngit push origin $PR_BRANCH --force-with-lease\n```\n\nIn the enterprise repo, rebase onto the new community branch with\n`--preserve-merges`:\n\n```bash\ngit checkout $PR_BRANCH\ngit fetch community\ngit rebase community/$PR_BRANCH --preserve-merges\n```\n\nAutomatic merge might have failed with the above command. However, because of\n`git rerere` it should have re-applied your original merge resolution. If this\nis indeed the case it should show something like this in the output of the\nprevious command (note the `Resolved ...` line):\n```\nCONFLICT (content): Merge conflict in <file_path>\nResolved '<file_path>' using previous resolution.\nAutomatic merge failed; fix conflicts and then commit the result.\nError redoing merge <merge_sha>\n```\n\nConfirm that the merge conflict is indeed resolved correctly. In that case you\ncan do the following:\n```bash\n# Add files that were conflicting\ngit add \"$(git diff --name-only --diff-filter=U)\"\ngit rebase --continue\n```\n\nBefore pushing you should do a final check that the commit hash of your final\nnon merge commit matches the commit hash that's on the community repo. If that's\nnot the case, you should fallback to the `git merge` approach.\n```bash\ngit reset origin/$PR_BRANCH --hard\n```\n\nIf the commit hashes were as expected, push the branch:\n```bash\ngit push origin $PR_BRANCH --force-with-lease\n```\n\n#### Updating both branches with `git merge`\n\nIf you are falling back to the `git merge` approach after trying the\n`git rebase` approach, you should first restore the original branch on the\ncommunity repo.\n```bash\ngit checkout $PR_BRANCH\ngit reset ${PR_BRANCH}-backup --hard\ngit push origin $PR_BRANCH --force-with-lease\n```\n\nIn the community repo, first update the outdated branch using `merge`:\n\n```bash\ngit checkout $PR_BRANCH\ngit fetch origin\ngit merge origin/master\ngit push origin $PR_BRANCH\n```\n\nIn the enterprise repo, merge with the updated `community/$PR_BRANCH`:\n\n```bash\ngit checkout $PR_BRANCH\ngit fetch community\ngit merge community/$PR_BRANCH\ngit push origin $PR_BRANCH\n```\n\n## `check_sql_snapshots.sh`\n\nTo allow for better diffs during review we have snapshots of SQL UDFs. This\nmeans that `latest.sql` is not up to date with the SQL file of the highest\nversion number in the directory. The output of the script shows you what is\ndifferent.\n\n## `check_all_tests_are_run.sh`\n\nA test should always be included in a schedule file, otherwise it will not be\nrun in CI. This is most commonly forgotten for newly added tests. In that case\nthe dev ran it locally without running a full schedule with something like:\n```bash\nmake -C src/test/regress/ check-minimal EXTRA_TESTS='multi_create_table_new_features'\n```\n\n## `check_all_ci_scripts_are_run.sh`\n\nThis is the meta CI script. This checks that all existing CI scripts are\nactually run in CI. This is most commonly forgotten for newly added CI tests\nthat the developer only ran locally. It also checks that all CI scripts have a\nsection in this `README.md` file and that they include `ci/ci_helpers.sh`.\n\n## `check_migration_files.sh`\n\nA branch that touches a set of upgrade scripts is also expected to touch\ncorresponding downgrade scripts as well. If this script fails, read the output\nand make sure you update the downgrade scripts in the printed list. If you\nreally don't need a downgrade to run any SQL. You can write a comment in the\nfile explaining why a downgrade step is not necessary.\n\n## `disallow_c_comments_in_migrations.sh`\n\nWe do not use C-style comments in migration files as the stripped\nzero-length migration files cause warning during packaging.\nInstead use SQL type comments, i.e:\n```\n-- this is a comment\n```\nSee [#3115](https://github.com/citusdata/citus/pull/3115) for more info.\n\n## `disallow_hash_comments_in_spec_files.sh`\n\nWe do not use comments starting with # in spec files because it creates errors\nfrom C preprocessor that expects directives after this character.\nInstead use C type comments, i.e:\n```\n// this is a single line comment\n\n/*\n * this is a multi line comment\n */\n```\n\n## `disallow_long_changelog_entries.sh`\n\nHaving changelog items with entries that are longer than 80 characters are\nforbidden. It's allowed to split up the entry over multiple lines, as long as\neach line of the entry is 80 characters or less.\n\n## `normalize_expected.sh`\n\nAll files in `src/test/expected` should be committed in normalized form.\nThis error mostly happens if someone added a new normalization rule and you have\nnot rerun tests that you have added.\n\nWe normalize the test output files using a `sed` script called\n[`normalize.sed`](https://github.com/citusdata/citus/blob/master/src/test/regress/bin/normalize.sed).\nThe reason for this is that some output changes randomly in ways we don't care\nabout. An example of this is when an error happens on a different port number,\nor a different worker shard, or a different placement, etc. Either randomly or\nbecause we are running the tests in a slightly different configuration.\n\n## `remove_useless_declarations.sh`\n\nThis script tries to make sure that we don't add useless declarations to our\ncode. What it effectively does is replace this:\n```c\nint a = 0;\nint b = 2;\nAssert(b == 2);\na = b + b;\n```\nWith this equivalent, but shorter version:\n```c\nint b = 2;\nAssert(b == 2);\nint a = b + b;\n```\n\nIt relies on the fact that `citus_indent` formats our code in certain ways. So\nbefore running this script, make sure that you've done that.\nThis replacement is all done using a [regex replace](xkcd.com/1171), so it's\ndefinitely possible there's a bug in there. So far no bad ones have been found.\n\nA known issue is that it does not replace code in a block after an `#ifdef` like\nthis.\n```c\nint foo = 0;\n#ifdef SOMETHING\nfoo = 1\n#else\nfoo = 2\n#endif\n```\nThis was deemed to be error prone and not worth the effort.\n\n## `fix_gitignore.sh`\n\nThis script checks and fixes issues with `.gitignore` rules:\n\n\n1. Makes sure we do not commit any generated files that should be ignored. If there is an\n   ignored file in the git tree, the user is expected to review the files that are removed\n   from the git tree and commit them.\n\n## `check_gucs_are_alphabetically_sorted.sh`\n\nThis script checks the order of the GUCs defined in `shared_library_init.c`.\nTo solve this failure, please check `shared_library_init.c` and make sure that the GUC\ndefinitions are in alphabetical order.\n\n## `print_stack_trace.sh`\n\nThis script prints stack traces for failed tests, if they left core files.\n\n## `sort_and_group_includes.sh`\n\nThis script checks and fixes issues with include grouping and sorting in C files.\n\nIncludes are grouped in the following groups:\n - System includes (eg. `#include <math>`)\n - Postgres.h include (eg. `#include \"postgres.h\"`)\n - Toplevel postgres includes (includes not in a directory eg. `#include \"miscadmin.h`)\n - Postgres includes in a directory (eg. `#include \"catalog/pg_type.h\"`)\n - Toplevel citus includes (includes not in a directory eg. `#include \"pg_version_constants.h\"`)\n - Columnar includes (eg. `#include \"columnar/columnar.h\"`)\n - Distributed includes (eg. `#include \"distributed/maintenanced.h\"`)\n\nWithin every group the include lines are sorted alphabetically.\n"
  },
  {
    "path": "ci/banned.h.sh",
    "content": "#!/bin/bash\n\n# Checks for the APIs that are banned by microsoft. Since we compile for Linux\n# we use the replacements from https://github.com/intel/safestringlib\n# Not all replacement functions are available in safestringlib. If it doesn't\n# exist and you cannot rewrite the code to not use the banned API, then you can\n# add a comment containing \"IGNORE-BANNED\" to the line where the error is and\n# this check will ignore that match.\n#\n# The replacement function that you should use are listed here:\n# https://liquid.microsoft.com/Web/Object/Read/ms.security/Requirements/Microsoft.Security.SystemsADM.10082#guide\n\nset -eu\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\nfiles=$(find src -iname '*.[ch]' | git check-attr --stdin citus-style | grep -v ': unset$' | sed 's/: citus-style: set$//')\n\n# grep is allowed to fail, that means no banned matches are found\nset +e\n# Required banned from banned.h. These functions are not allowed to be used at\n# all.\n# shellcheck disable=SC2086\ngrep -E '\\b(strcpy|strcpyA|strcpyW|wcscpy|_tcscpy|_mbscpy|StrCpy|StrCpyA|StrCpyW|lstrcpy|lstrcpyA|lstrcpyW|_tccpy|_mbccpy|_ftcscpy|strcat|strcatA|strcatW|wcscat|_tcscat|_mbscat|StrCat|StrCatA|StrCatW|lstrcat|lstrcatA|lstrcatW|StrCatBuff|StrCatBuffA|StrCatBuffW|StrCatChainW|_tccat|_mbccat|_ftcscat|sprintfW|sprintfA|wsprintf|wsprintfW|wsprintfA|sprintf|swprintf|_stprintf|wvsprintf|wvsprintfA|wvsprintfW|vsprintf|_vstprintf|vswprintf|strncpy|wcsncpy|_tcsncpy|_mbsncpy|_mbsnbcpy|StrCpyN|StrCpyNA|StrCpyNW|StrNCpy|strcpynA|StrNCpyA|StrNCpyW|lstrcpyn|lstrcpynA|lstrcpynW|strncat|wcsncat|_tcsncat|_mbsncat|_mbsnbcat|StrCatN|StrCatNA|StrCatNW|StrNCat|StrNCatA|StrNCatW|lstrncat|lstrcatnA|lstrcatnW|lstrcatn|gets|_getts|_gettws|IsBadWritePtr|IsBadHugeWritePtr|IsBadReadPtr|IsBadHugeReadPtr|IsBadCodePtr|IsBadStringPtr|memcpy|RtlCopyMemory|CopyMemory|wmemcpy|lstrlen)\\(' $files \\\n    | grep -v \"IGNORE-BANNED\" \\\n    && echo \"ERROR: Required banned API usage detected\" && exit 1\n\n# Required banned from table on liquid. These functions are not allowed to be\n# used at all.\n# shellcheck disable=SC2086\ngrep -E  '\\b(strcat|strcpy|strerror|strncat|strncpy|strtok|wcscat|wcscpy|wcsncat|wcsncpy|wcstok|fprintf|fwprintf|printf|snprintf|sprintf|swprintf|vfprintf|vprintf|vsnprintf|vsprintf|vswprintf|vwprintf|wprintf|fscanf|fwscanf|gets|scanf|sscanf|swscanf|vfscanf|vfwscanf|vscanf|vsscanf|vswscanf|vwscanf|wscanf|asctime|atof|atoi|atol|atoll|bsearch|ctime|fopen|freopen|getenv|gmtime|localtime|mbsrtowcs|mbstowcs|memcpy|memmove|qsort|rewind|setbuf|wmemcpy|wmemmove)\\(' $files \\\n    | grep -v \"IGNORE-BANNED\" \\\n    && echo \"ERROR: Required banned API usage from table detected\" && exit 1\n\n# Recommended banned from banned.h. If you can change the code not to use these\n# that would be great. You can use IGNORE-BANNED if you need to use it anyway.\n# You can also remove it from the regex, if you want to mark the API as allowed\n# throughout the codebase (to not have to add IGNORED-BANNED everywhere). In\n# that case note it in this comment that you did so.\n# shellcheck disable=SC2086\ngrep -E '\\b(wnsprintf|wnsprintfA|wnsprintfW|_snwprintf|_snprintf|_sntprintf|_vsnprintf|vsnprintf|_vsnwprintf|_vsntprintf|wvnsprintf|wvnsprintfA|wvnsprintfW|strtok|_tcstok|wcstok|_mbstok|makepath|_tmakepath| _makepath|_wmakepath|_splitpath|_tsplitpath|_wsplitpath|scanf|wscanf|_tscanf|sscanf|swscanf|_stscanf|snscanf|snwscanf|_sntscanf|_itoa|_itow|_i64toa|_i64tow|_ui64toa|_ui64tot|_ui64tow|_ultoa|_ultot|_ultow|CharToOem|CharToOemA|CharToOemW|OemToChar|OemToCharA|OemToCharW|CharToOemBuffA|CharToOemBuffW|alloca|_alloca|ChangeWindowMessageFilter)\\(' $files  \\\n    | grep -v \"IGNORE-BANNED\" \\\n    && echo \"ERROR: Recomended banned API usage detected\" && exit 1\n\n# Recommended banned from table on liquid. If you can change the code not to use these\n# that would be great. You can use IGNORE-BANNED if you need to use it anyway.\n# You can also remove it from the regex, if you want to mark the API as allowed\n# throughout the codebase (to not have to add IGNORED-BANNED everywhere). In\n# that case note it in this comment that you did so.\n# Banned APIs ignored throughout the codebase:\n# - strlen\n# shellcheck disable=SC2086\ngrep -E '\\b(alloca|getwd|mktemp|tmpnam|wcrtomb|wcrtombs|wcslen|wcsrtombs|wcstombs|wctomb|class_addMethod|class_replaceMethod)\\(' $files  \\\n    | grep -v \"IGNORE-BANNED\" \\\n    && echo \"ERROR: Recomended banned API usage detected\" && exit 1\nexit 0\n"
  },
  {
    "path": "ci/build-citus.sh",
    "content": "#!/bin/bash\n\n# make bash behave\nset -euo pipefail\nIFS=$'\\n\\t'\n\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\n# read pg major version, error if not provided\nPG_MAJOR=${PG_MAJOR:?please provide the postgres major version}\n\n# get codename from release file\n. /etc/os-release\ncodename=${VERSION#*(}\ncodename=${codename%)*}\n\n# we'll do everything with absolute paths\nbasedir=\"$(pwd)\"\n\n# get the project and clear out the git repo (reduce workspace size\nrm -rf \"${basedir}/.git\"\n\nbuild_ext() {\n  pg_major=\"$1\"\n\n  builddir=\"${basedir}/build-${pg_major}\"\n  echo \"Beginning build for PostgreSQL ${pg_major}...\" >&2\n\n  # do everything in a subdirectory to avoid clutter in current directory\n  mkdir -p \"${builddir}\" && cd \"${builddir}\"\n\n  CFLAGS=-Werror \"${basedir}/configure\" PG_CONFIG=\"/usr/lib/postgresql/${pg_major}/bin/pg_config\" --enable-coverage --with-security-flags\n\n  installdir=\"${builddir}/install\"\n  make -j$(nproc) && mkdir -p \"${installdir}\" && { make DESTDIR=\"${installdir}\" install-all || make DESTDIR=\"${installdir}\" install ; }\n\n  cd \"${installdir}\" && find . -type f -print > \"${builddir}/files.lst\"\n  tar cvf \"${basedir}/install-${pg_major}.tar\" `cat ${builddir}/files.lst`\n\n  cd \"${builddir}\" && rm -rf install files.lst && make clean\n}\n\nbuild_ext \"${PG_MAJOR}\"\n"
  },
  {
    "path": "ci/check_all_ci_scripts_are_run.sh",
    "content": "#!/bin/bash\nset -euo pipefail\n\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\n\n# 1. Find all *.sh files in the ci directory\n# 2. Strip the directory\n# 3. Exclude some scripts that we should not run in CI directly\nci_scripts=$(\n    find ci/ -iname \"*.sh\" |\n    sed -E 's#^ci/##g' |\n    grep -v -E '^(ci_helpers.sh|fix_style.sh)$'\n)\nfor script in $ci_scripts; do\n    if ! grep \"\\\\bci/$script\\\\b\" -r .github > /dev/null; then\n        echo \"ERROR: CI script with name \\\"$script\\\" is not actually used in .github folder\"\n        exit 1\n    fi\n    if ! grep \"^## \\`$script\\`\\$\" ci/README.md > /dev/null; then\n        echo \"ERROR: CI script with name \\\"$script\\\" does not have a section in ci/README.md\"\n        exit 1\n    fi\n    if ! grep \"source ci/ci_helpers.sh\" \"ci/$script\" > /dev/null; then\n        echo \"ERROR: CI script with name \\\"$script\\\" does not include ci/ci_helpers.sh\"\n        exit 1\n    fi\ndone\n"
  },
  {
    "path": "ci/check_all_tests_are_run.sh",
    "content": "#!/bin/bash\nset -euo pipefail\n\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\n\ncd src/test/regress\n\n# 1. Find all *.sql and *.spec files in the sql, and spec directories\n# 2. Strip the extension and the directory\n# 3. Ignore names that end with .include, those files are meant to be in an C\n#    preprocessor #include statement. They should not be in schedules.\ntest_names=$(\n    find sql spec -iname \"*.sql\" -o -iname \"*.spec\" |\n    sed -E 's#^\\w+/([^/]+)\\.[^.]+$#\\1#g' |\n    grep -v '.include$'\n)\nfor name in $test_names; do\n    if ! grep \"\\\\b$name\\\\b\" ./*_schedule > /dev/null; then\n        echo \"ERROR: Test with name \\\"$name\\\" is not used in any of the schedule files\"\n        exit 1\n    fi\ndone\n"
  },
  {
    "path": "ci/check_gucs_are_alphabetically_sorted.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\n# Find the line that exactly matches \"RegisterCitusConfigVariables(void)\" in\n# shared_library_init.c. grep command returns something like\n# \"934:RegisterCitusConfigVariables(void)\" and we extract the line number\n# with cut.\nRegisterCitusConfigVariables_begin_linenumber=$(grep -n \"^RegisterCitusConfigVariables(void)$\" src/backend/distributed/shared_library_init.c | cut -d: -f1)\n\n# Consider the lines starting from $RegisterCitusConfigVariables_begin_linenumber,\n# grep the first line that starts with \"}\" and extract the line number with cut\n# as in the previous step.\nRegisterCitusConfigVariables_length=$(tail -n +$RegisterCitusConfigVariables_begin_linenumber src/backend/distributed/shared_library_init.c | grep -n -m 1 \"^}$\" | cut -d: -f1)\n\n# extract the function definition of RegisterCitusConfigVariables into a temp file\ntail -n +$RegisterCitusConfigVariables_begin_linenumber src/backend/distributed/shared_library_init.c | head -n $(($RegisterCitusConfigVariables_length)) > RegisterCitusConfigVariables_func_def.out\n\n# extract citus gucs in the form of <tab><tab>\"citus.X\"\ngrep -P \"^[\\t][\\t]\\\"citus\\.[a-zA-Z_0-9]+\\\"\" RegisterCitusConfigVariables_func_def.out > gucs.out\nLC_COLLATE=C sort -c gucs.out\nrm gucs.out\nrm RegisterCitusConfigVariables_func_def.out\n"
  },
  {
    "path": "ci/check_migration_files.sh",
    "content": "#! /bin/bash\n\nset -euo pipefail\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\n# This file checks for the existence of downgrade scripts for every upgrade script that is changed in the branch.\n\n# create list of migration files for upgrades\nupgrade_files=$(git diff --name-only origin/main | { grep \"src/backend/distributed/sql/citus--.*sql\" || exit 0 ; })\ndowngrade_files=$(git diff --name-only origin/main | { grep \"src/backend/distributed/sql/downgrades/citus--.*sql\" || exit 0 ; })\nret_value=0\n\nfor file in $upgrade_files\ndo\n    # There should always be 2 matches, and no need to avoid splitting here\n    # shellcheck disable=SC2207\n    versions=($(grep --only-matching --extended-regexp \"[0-9]+\\.[0-9]+[-.][0-9]+\" <<< \"$file\"))\n\n    from_version=${versions[0]};\n    to_version=${versions[1]};\n\n    downgrade_migration_file=\"src/backend/distributed/sql/downgrades/citus--$to_version--$from_version.sql\"\n\n    # check for the existence of migration scripts\n    if [[ $(grep --line-regexp --count \"$downgrade_migration_file\" <<< \"$downgrade_files\") == 0 ]]\n    then\n        echo \"$file is updated, but $downgrade_migration_file is not updated in branch\"\n        ret_value=1\n    fi\ndone\n\nexit $ret_value;\n"
  },
  {
    "path": "ci/check_sql_snapshots.sh",
    "content": "#!/bin/bash\nset -euo pipefail\n\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\nfor udf_dir in src/backend/distributed/sql/udfs/* src/backend/columnar/sql/udfs/*; do\n    # We want to find the last snapshotted sql file, to make sure it's the same\n    # as \"latest.sql\". This is done by:\n    # 1. Getting the filenames in the UDF directory (using find instead of ls, to keep shellcheck happy)\n    # 2. Filter out latest.sql\n    # 3. Sort using \"version sort\"\n    # 4. Get the last one using tail\n    latest_snapshot=$(\\\n        find \"$udf_dir\" -iname \"*.sql\" -exec basename {} \\; \\\n        | { grep --invert-match latest.sql || true; } \\\n        | sort --version-sort \\\n        | tail --lines 1);\n    diff --unified --color=auto \"$udf_dir/latest.sql\" \"$udf_dir/$latest_snapshot\"; \\\ndone\n"
  },
  {
    "path": "ci/ci_helpers.sh",
    "content": "#!/bin/bash\n\n# For echo commands \"set -x\" would show the message effectively twice. Once as\n# part of the echo command shown by \"set -x\" and once because of the output of\n# the echo command. We do not want \"set -x\" to show the echo command. We only\n# want to see the actual message in the output of echo itself. This function is\n# a trick to do so. Read the StackOverflow post below to understand why this\n# works and what this works around.\n# Source: https://superuser.com/a/1141026/242593\nshopt -s expand_aliases\nalias echo='{ save_flags=\"$-\"; set +x;} 2> /dev/null && echo_and_restore'\necho_and_restore() {\n        builtin echo \"$*\"\n        #shellcheck disable=SC2154\n        case \"$save_flags\" in\n         (*x*)  set -x\n        esac\n}\n\n# Make sure that on a failing exit we show a useful message\nhint_on_fail() {\n    exit_code=$?\n    # Get filename of the currently running script\n    # Source: https://stackoverflow.com/a/192337/2570866\n    filename=$(basename \"$0\")\n    if [ $exit_code != 0 ]; then\n        echo \"HINT: To solve this failure look here: https://github.com/citusdata/citus/blob/master/ci/README.md#$filename\"\n    fi\n    exit $exit_code\n}\ntrap hint_on_fail EXIT\n\n"
  },
  {
    "path": "ci/disallow_c_comments_in_migrations.sh",
    "content": "#! /bin/bash\n\nset -euo pipefail\n\n# make ** match all directories and subdirectories\nshopt -s globstar\n\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\n# We do not use c-style comments in migration files as the stripped\n# zero-length migration files cause warning during packaging\n# See #3115 for more info\n\n# In this file, we aim to keep the indentation intact by capturing whitespaces,\n# and reusing them if needed. GNU sed unfortunately does not support lookaround assertions.\n\n# /* -> --\nfind src/backend/{distributed,columnar}/sql/**/*.sql -print0 | xargs -0 sed -i 's#/\\*#--#g'\n\n# */ -> `` (empty string)\n# remove all whitespaces immediately before the match\nfind src/backend/{distributed,columnar}/sql/**/*.sql -print0 | xargs -0 sed -i 's#\\s*\\*/\\s*##g'\n\n# * -> --\n# keep the indentation\n# allow only whitespaces before the match\nfind src/backend/{distributed,columnar}/sql/**/*.sql -print0 | xargs -0 sed -i 's#^\\(\\s*\\) \\*#\\1--#g'\n\n# // -> --\n# do not touch http:// or similar by allowing only whitespaces before //\nfind src/backend/{distributed,columnar}/sql/**/*.sql -print0 | xargs -0 sed -i 's#^\\(\\s*\\)//#\\1--#g'\n"
  },
  {
    "path": "ci/disallow_hash_comments_in_spec_files.sh",
    "content": "#! /bin/bash\n\nset -euo pipefail\n\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\n# We do not use comments starting with # in spec files because it creates warnings from\n# preprocessor that expects directives after this character.\n\n# `# ` -> `-- `\nfind src/test/regress/spec/*.spec -print0 | xargs -0 sed -i 's!# !// !g'\n"
  },
  {
    "path": "ci/disallow_long_changelog_entries.sh",
    "content": "#! /bin/bash\n\nset -eu\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\n# Having changelog items with entries that are longer than 80 characters are forbidden.\n# Find all lines with disallowed length, and for all such lines store\n#  - line number\n#  - length of the line\n#  - the line content\ntoo_long_lines=$(awk 'length() > 80 {print NR,\"(\",length(),\"characters ) :\",$0}' CHANGELOG.md)\n\nif [[ -n $too_long_lines ]]\nthen\n    echo \"We allow at most 80 characters in CHANGELOG.md.\"\n    echo \"${too_long_lines}\"\n    exit 1\nfi\n"
  },
  {
    "path": "ci/editorconfig.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\nfor f in $(git ls-tree -r HEAD --name-only); do\n    if [ \"$f\" = \"${f%.out}\" ]  &&\n        [ \"$f\" = \"${f%.data}\" ] &&\n        [ \"$f\" = \"${f%.png}\" ] &&\n        [ -f \"$f\" ] &&\n        [ \"$(echo \"$f\" | cut -d / -f1)\" != \"vendor\" ] &&\n        [ \"$(dirname \"$f\")\" != \"src/test/regress/output\" ]\n    then\n        # Trim trailing whitespace\n        sed -e 's/[[:space:]]*$//' -i \"./$f\"\n        # Add final newline if not there\n        if [ -n \"$(tail -c1 \"$f\")\" ]; then\n            echo >> \"$f\"\n        fi\n    fi\ndone\n"
  },
  {
    "path": "ci/fix_gitignore.sh",
    "content": "#! /bin/bash\nset -euo pipefail\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\n# Remove all the ignored files from git tree, and error out\n# find all ignored files in git tree, and use quotation marks to prevent word splitting on filenames with spaces in them\n# NOTE: Option --cached is needed to avoid a bug in git ls-files command.\nignored_lines_in_git_tree=$(git ls-files --ignored --cached --exclude-standard | sed 's/.*/\"&\"/')\n\nif [[ -n $ignored_lines_in_git_tree ]]\nthen\n    echo \"Ignored files should not be in git tree!\"\n    echo \"${ignored_lines_in_git_tree}\"\n\n    echo \"Removing these files from git tree, please review and commit\"\n    echo \"$ignored_lines_in_git_tree\" | xargs git rm -r --cached\n    exit 1\nfi\n"
  },
  {
    "path": "ci/fix_style.sh",
    "content": "#!/bin/sh\n\n# fail if trying to reference a variable that is not set.\nset -u / set -o nounset\n# exit immediately if a command fails\nset -e\n\ncidir=\"${0%/*}\"\ncd ${cidir}/..\n\ncitus_indent . --quiet\nblack . --quiet\nisort . --quiet\nci/editorconfig.sh\nci/remove_useless_declarations.sh\nci/disallow_c_comments_in_migrations.sh\nci/disallow_hash_comments_in_spec_files.sh\nci/disallow_long_changelog_entries.sh\nci/normalize_expected.sh\nci/fix_gitignore.sh\nci/print_stack_trace.sh\nci/sort_and_group_includes.sh\n"
  },
  {
    "path": "ci/include_grouping.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\neasy command line to run against all citus-style checked files:\n\n$ git ls-files \\\n  | git check-attr --stdin citus-style \\\n  | grep 'citus-style: set' \\\n  | awk '{print $1}' \\\n  | cut -d':' -f1 \\\n  | xargs -n1 ./ci/include_grouping.py\n\"\"\"\n\nimport collections\nimport os\nimport sys\n\n\ndef main(args):\n    if len(args) < 2:\n        print(\"Usage: include_grouping.py <file>\")\n        return\n\n    file = args[1]\n    if not os.path.isfile(file):\n        sys.exit(f\"File '{file}' does not exist\")\n\n    with open(file, \"r\") as in_file:\n        with open(file + \".tmp\", \"w\") as out_file:\n            includes = []\n            skipped_lines = []\n\n            # This calls print_sorted_includes on a set of consecutive #include lines.\n            # This implicitly keeps separation of any #include lines that are contained in\n            # an #ifdef, because it will order the #include lines inside and after the\n            # #ifdef completely separately.\n            for line in in_file:\n                # if a line starts with #include we don't want to print it yet, instead we\n                # want to collect all consecutive #include lines\n                if line.startswith(\"#include\"):\n                    includes.append(line)\n                    skipped_lines = []\n                    continue\n\n                # if we have collected any #include lines, we want to print them sorted\n                # before printing the current line. However, if the current line is empty\n                # we want to perform a lookahead to see if the next line is an #include.\n                # To maintain any separation between #include lines and their subsequent\n                # lines we keep track of all lines we have skipped inbetween.\n                if len(includes) > 0:\n                    if len(line.strip()) == 0:\n                        skipped_lines.append(line)\n                        continue\n\n                    # we have includes that need to be grouped before printing the current\n                    # line.\n                    print_sorted_includes(includes, file=out_file)\n                    includes = []\n\n                    # print any skipped lines\n                    print(\"\".join(skipped_lines), end=\"\", file=out_file)\n                    skipped_lines = []\n\n                print(line, end=\"\", file=out_file)\n\n    # move out_file to file\n    os.rename(file + \".tmp\", file)\n\n\ndef print_sorted_includes(includes, file=sys.stdout):\n    default_group_key = 1\n    groups = collections.defaultdict(set)\n\n    # define the groups that we separate correctly. The matchers are tested in the order\n    # of their priority field. The first matcher that matches the include is used to\n    # assign the include to a group.\n    # The groups are printed in the order of their group_key.\n    matchers = [\n        {\n            \"name\": \"system includes\",\n            \"matcher\": lambda x: x.startswith(\"<\"),\n            \"group_key\": -2,\n            \"priority\": 0,\n        },\n        {\n            \"name\": \"toplevel postgres includes\",\n            \"matcher\": lambda x: \"/\" not in x,\n            \"group_key\": 0,\n            \"priority\": 9,\n        },\n        {\n            \"name\": \"postgres.h\",\n            \"matcher\": lambda x: x.strip() in ['\"postgres.h\"'],\n            \"group_key\": -1,\n            \"priority\": -1,\n        },\n        {\n            \"name\": \"toplevel citus inlcudes\",\n            \"matcher\": lambda x: x.strip()\n            in [\n                '\"citus_version.h\"',\n                '\"pg_version_compat.h\"',\n                '\"pg_version_constants.h\"',\n            ],\n            \"group_key\": 3,\n            \"priority\": 0,\n        },\n        {\n            \"name\": \"columnar includes\",\n            \"matcher\": lambda x: x.startswith('\"columnar/'),\n            \"group_key\": 4,\n            \"priority\": 1,\n        },\n        {\n            \"name\": \"distributed includes\",\n            \"matcher\": lambda x: x.startswith('\"distributed/'),\n            \"group_key\": 5,\n            \"priority\": 1,\n        },\n    ]\n    matchers.sort(key=lambda x: x[\"priority\"])\n\n    # throughout our codebase we have some includes where either postgres or citus\n    # includes are wrongfully included with the syntax for system includes. Before we\n    # try to match those we will change the <> to \"\" to make them match our system. This\n    # will also rewrite the include to the correct syntax.\n    common_system_include_error_prefixes = [\"<nodes/\", \"<distributed/\"]\n\n    # assign every include to a group\n    for include in includes:\n        # extract the group key from the include\n        include_content = include.split(\" \")[1]\n\n        # fix common system includes which are secretly postgres or citus includes\n        for common_prefix in common_system_include_error_prefixes:\n            if include_content.startswith(common_prefix):\n                include_content = '\"' + include_content.strip()[1:-1] + '\"'\n                include = include.split(\" \")[0] + \" \" + include_content + \"\\n\"\n                break\n\n        group_key = default_group_key\n        for matcher in matchers:\n            if matcher[\"matcher\"](include_content):\n                group_key = matcher[\"group_key\"]\n                break\n\n        groups[group_key].add(include)\n\n    # iterate over all groups in the natural order of its keys\n    for i, group in enumerate(sorted(groups.items())):\n        if i > 0:\n            print(file=file)\n        includes = group[1]\n        print(\"\".join(sorted(includes)), end=\"\", file=file)\n\n\nif __name__ == \"__main__\":\n    main(sys.argv)\n"
  },
  {
    "path": "ci/normalize_expected.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\nfor f in $(git ls-tree -r HEAD --name-only src/test/regress/expected/*.out); do\n\tsed -Ef src/test/regress/bin/normalize.sed < \"$f\" > \"$f.modified\"\n\tmv \"$f.modified\" \"$f\"\ndone\n"
  },
  {
    "path": "ci/print_stack_trace.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\n# find all core files\ncore_files=( $(find . -type f -regex .*core.*\\d*.*postgres) )\nif [ ${#core_files[@]} -gt 0 ]; then\n    # print stack traces for the core files\n    for core_file in \"${core_files[@]}\"\n    do\n        # set print frame-arguments all: show all scalars + structures in the frame\n        # set print pretty on:           show structures in indented mode\n        # set print addr off:            do not show pointer address\n        # thread apply all bt full:      show stack traces for all threads\n        gdb --batch \\\n            -ex \"set print frame-arguments all\" \\\n            -ex \"set print pretty on\" \\\n            -ex \"set print addr off\" \\\n            -ex \"thread apply all bt full\" \\\n            postgres \"${core_file}\"\n    done\nfi\n"
  },
  {
    "path": "ci/remove_useless_declarations.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\nfiles=$(find src -iname '*.c' -type f | git check-attr --stdin citus-style | grep -v ': unset$' | sed 's/: citus-style: set$//')\nwhile true; do\n    # A visual version of this regex can be seen here (it is MUCH clearer):\n    # https://www.debuggex.com/r/XodMNE9auT9e-bTx\n    # This visual version only contains the search bit, the replacement bit is\n    # quite simple. It looks like when extracted from the command below:\n    # \\n$+{code_between}\\t$+{type}$+{variable} =\n    # shellcheck disable=SC2086\n    perl -i -p0e 's/\\n\\t(?!return )(?P<type>(\\w+ )+\\**)(?>(?P<variable>\\w+)( = *[\\w>\\s\\n-]*?)?;\\n(?P<code_between>(?>(?P<comment_or_string_or_not_preprocessor>\\/\\*.*?\\*\\/|\"(?>\\\\\"|.)*?\"|[^#]))*?)(\\t)?(?=\\b(?P=variable)\\b))(?<=\\n\\t)(?P=variable) =(?![^;]*?[^>_]\\b(?P=variable)\\b[^_])/\\n$+{code_between}\\t$+{type}$+{variable} =/sg' $files\n    # The following are simply the same regex, but repeated for different\n    # indentation levels, i.e. finding declarations indented using 2, 3, 4, 5\n    # and 6 tabs. More than 6 don't really occur in the wild.\n    # (this is needed because variable sized backtracking is not supported in perl)\n    # shellcheck disable=SC2086\n    perl -i -p0e 's/\\n\\t\\t(?!return )(?P<type>(\\w+ )+\\**)(?>(?P<variable>\\w+)( = *[\\w>\\s\\n-]*?)?;\\n(?P<code_between>(?>(?P<comment_or_string_or_not_preprocessor>\\/\\*.*?\\*\\/|\"(?>\\\\\"|.)*?\"|[^#]))*?)(\\t\\t)?(?=\\b(?P=variable)\\b))(?<=\\n\\t\\t)(?P=variable) =(?![^;]*?[^>_]\\b(?P=variable)\\b[^_])/\\n$+{code_between}\\t\\t$+{type}$+{variable} =/sg' $files\n    # shellcheck disable=SC2086\n    perl -i -p0e 's/\\n\\t\\t\\t(?!return )(?P<type>(\\w+ )+\\**)(?>(?P<variable>\\w+)( = *[\\w>\\s\\n-]*?)?;\\n(?P<code_between>(?>(?P<comment_or_string_or_not_preprocessor>\\/\\*.*?\\*\\/|\"(?>\\\\\"|.)*?\"|[^#]))*?)(\\t\\t\\t)?(?=\\b(?P=variable)\\b))(?<=\\n\\t\\t\\t)(?P=variable) =(?![^;]*?[^>_]\\b(?P=variable)\\b[^_])/\\n$+{code_between}\\t\\t\\t$+{type}$+{variable} =/sg' $files\n    # shellcheck disable=SC2086\n    perl -i -p0e 's/\\n\\t\\t\\t\\t(?!return )(?P<type>(\\w+ )+\\**)(?>(?P<variable>\\w+)( = *[\\w>\\s\\n-]*?)?;\\n(?P<code_between>(?>(?P<comment_or_string_or_not_preprocessor>\\/\\*.*?\\*\\/|\"(?>\\\\\"|.)*?\"|[^#]))*?)(\\t\\t\\t\\t)?(?=\\b(?P=variable)\\b))(?<=\\n\\t\\t\\t\\t)(?P=variable) =(?![^;]*?[^>_]\\b(?P=variable)\\b[^_])/\\n$+{code_between}\\t\\t\\t\\t$+{type}$+{variable} =/sg' $files\n    # shellcheck disable=SC2086\n    perl -i -p0e 's/\\n\\t\\t\\t\\t\\t(?!return )(?P<type>(\\w+ )+\\**)(?>(?P<variable>\\w+)( = *[\\w>\\s\\n-]*?)?;\\n(?P<code_between>(?>(?P<comment_or_string_or_not_preprocessor>\\/\\*.*?\\*\\/|\"(?>\\\\\"|.)*?\"|[^#]))*?)(\\t\\t\\t\\t\\t)?(?=\\b(?P=variable)\\b))(?<=\\n\\t\\t\\t\\t\\t)(?P=variable) =(?![^;]*?[^>_]\\b(?P=variable)\\b[^_])/\\n$+{code_between}\\t\\t\\t\\t\\t$+{type}$+{variable} =/sg' $files\n    # shellcheck disable=SC2086\n    perl -i -p0e 's/\\n\\t\\t\\t\\t\\t\\t(?!return )(?P<type>(\\w+ )+\\**)(?>(?P<variable>\\w+)( = *[\\w>\\s\\n-]*?)?;\\n(?P<code_between>(?>(?P<comment_or_string_or_not_preprocessor>\\/\\*.*?\\*\\/|\"(?>\\\\\"|.)*?\"|[^#]))*?)(\\t\\t\\t\\t\\t\\t)?(?=\\b(?P=variable)\\b))(?<=\\n\\t\\t\\t\\t\\t\\t)(?P=variable) =(?![^;]*?[^>_]\\b(?P=variable)\\b[^_])/\\n$+{code_between}\\t\\t\\t\\t\\t\\t$+{type}$+{variable} =/sg' $files\n    # shellcheck disable=SC2086\n    git diff --quiet $files && break;\n    # shellcheck disable=SC2086\n    git add $files;\ndone\n"
  },
  {
    "path": "ci/sort_and_group_includes.sh",
    "content": "#!/bin/bash\n\nset -euo pipefail\n# shellcheck disable=SC1091\nsource ci/ci_helpers.sh\n\ngit ls-files \\\n  | git check-attr --stdin citus-style \\\n  | grep 'citus-style: set' \\\n  | awk '{print $1}' \\\n  | cut -d':' -f1 \\\n  | xargs -n1 ./ci/include_grouping.py\n"
  },
  {
    "path": "config/config.guess",
    "content": "#! /bin/sh\n# Attempt to guess a canonical system name.\n#   Copyright 1992-2017 Free Software Foundation, Inc.\n\ntimestamp='2017-09-26'\n\n# This file is free software; you can redistribute it and/or modify it\n# under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful, but\n# WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n# General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, see <https://www.gnu.org/licenses/>.\n#\n# As a special exception to the GNU General Public License, if you\n# distribute this file as part of a program that contains a\n# configuration script generated by Autoconf, you may include it under\n# the same distribution terms that you use for the rest of that\n# program.  This Exception is an additional permission under section 7\n# of the GNU General Public License, version 3 (\"GPLv3\").\n#\n# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.\n#\n# You can get the latest version of this script from:\n# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess\n#\n# Please send patches to <config-patches@gnu.org>.\n\n\nme=`echo \"$0\" | sed -e 's,.*/,,'`\n\nusage=\"\\\nUsage: $0 [OPTION]\n\nOutput the configuration name of the system \\`$me' is run on.\n\nOperation modes:\n  -h, --help         print this help, then exit\n  -t, --time-stamp   print date of last modification, then exit\n  -v, --version      print version number, then exit\n\nReport bugs and patches to <config-patches@gnu.org>.\"\n\nversion=\"\\\nGNU config.guess ($timestamp)\n\nOriginally written by Per Bothner.\nCopyright 1992-2017 Free Software Foundation, Inc.\n\nThis is free software; see the source for copying conditions.  There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\"\n\nhelp=\"\nTry \\`$me --help' for more information.\"\n\n# Parse command line\nwhile test $# -gt 0 ; do\n  case $1 in\n    --time-stamp | --time* | -t )\n       echo \"$timestamp\" ; exit ;;\n    --version | -v )\n       echo \"$version\" ; exit ;;\n    --help | --h* | -h )\n       echo \"$usage\"; exit ;;\n    -- )     # Stop option processing\n       shift; break ;;\n    - )\t# Use stdin as input.\n       break ;;\n    -* )\n       echo \"$me: invalid option $1$help\" >&2\n       exit 1 ;;\n    * )\n       break ;;\n  esac\ndone\n\nif test $# != 0; then\n  echo \"$me: too many arguments$help\" >&2\n  exit 1\nfi\n\ntrap 'exit 1' 1 2 15\n\n# CC_FOR_BUILD -- compiler used by this script. Note that the use of a\n# compiler to aid in system detection is discouraged as it requires\n# temporary files to be created and, as you can see below, it is a\n# headache to deal with in a portable fashion.\n\n# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still\n# use `HOST_CC' if defined, but it is deprecated.\n\n# Portable tmp directory creation inspired by the Autoconf team.\n\nset_cc_for_build='\ntrap \"exitcode=\\$?; (rm -f \\$tmpfiles 2>/dev/null; rmdir \\$tmp 2>/dev/null) && exit \\$exitcode\" 0 ;\ntrap \"rm -f \\$tmpfiles 2>/dev/null; rmdir \\$tmp 2>/dev/null; exit 1\" 1 2 13 15 ;\n: ${TMPDIR=/tmp} ;\n { tmp=`(umask 077 && mktemp -d \"$TMPDIR/cgXXXXXX\") 2>/dev/null` && test -n \"$tmp\" && test -d \"$tmp\" ; } ||\n { test -n \"$RANDOM\" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||\n { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo \"Warning: creating insecure temp directory\" >&2 ; } ||\n { echo \"$me: cannot create a temporary directory in $TMPDIR\" >&2 ; exit 1 ; } ;\ndummy=$tmp/dummy ;\ntmpfiles=\"$dummy.c $dummy.o $dummy.rel $dummy\" ;\ncase $CC_FOR_BUILD,$HOST_CC,$CC in\n ,,)    echo \"int x;\" > $dummy.c ;\n\tfor c in cc gcc c89 c99 ; do\n\t  if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then\n\t     CC_FOR_BUILD=\"$c\"; break ;\n\t  fi ;\n\tdone ;\n\tif test x\"$CC_FOR_BUILD\" = x ; then\n\t  CC_FOR_BUILD=no_compiler_found ;\n\tfi\n\t;;\n ,,*)   CC_FOR_BUILD=$CC ;;\n ,*,*)  CC_FOR_BUILD=$HOST_CC ;;\nesac ; set_cc_for_build= ;'\n\n# This is needed to find uname on a Pyramid OSx when run in the BSD universe.\n# (ghazi@noc.rutgers.edu 1994-08-24)\nif (test -f /.attbin/uname) >/dev/null 2>&1 ; then\n\tPATH=$PATH:/.attbin ; export PATH\nfi\n\nUNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown\nUNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown\nUNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown\nUNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown\n\ncase \"${UNAME_SYSTEM}\" in\nLinux|GNU|GNU/*)\n\t# If the system lacks a compiler, then just pick glibc.\n\t# We could probably try harder.\n\tLIBC=gnu\n\n\teval $set_cc_for_build\n\tcat <<-EOF > $dummy.c\n\t#include <features.h>\n\t#if defined(__UCLIBC__)\n\tLIBC=uclibc\n\t#elif defined(__dietlibc__)\n\tLIBC=dietlibc\n\t#else\n\tLIBC=gnu\n\t#endif\n\tEOF\n\teval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`\n\t;;\nesac\n\n# Note: order is significant - the case branches are not exclusive.\n\ncase \"${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}\" in\n    *:NetBSD:*:*)\n\t# NetBSD (nbsd) targets should (where applicable) match one or\n\t# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,\n\t# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently\n\t# switched to ELF, *-*-netbsd* would select the old\n\t# object file format.  This provides both forward\n\t# compatibility and a consistent mechanism for selecting the\n\t# object file format.\n\t#\n\t# Note: NetBSD doesn't particularly care about the vendor\n\t# portion of the name.  We always set it to \"unknown\".\n\tsysctl=\"sysctl -n hw.machine_arch\"\n\tUNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \\\n\t    /sbin/$sysctl 2>/dev/null || \\\n\t    /usr/sbin/$sysctl 2>/dev/null || \\\n\t    echo unknown)`\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    armeb) machine=armeb-unknown ;;\n\t    arm*) machine=arm-unknown ;;\n\t    sh3el) machine=shl-unknown ;;\n\t    sh3eb) machine=sh-unknown ;;\n\t    sh5el) machine=sh5le-unknown ;;\n\t    earmv*)\n\t\tarch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\\(armv[0-9]\\).*$,\\1,'`\n\t\tendian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\\(eb\\)$,\\1,p'`\n\t\tmachine=${arch}${endian}-unknown\n\t\t;;\n\t    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;\n\tesac\n\t# The Operating System including object format, if it has switched\n\t# to ELF recently (or will in the future) and ABI.\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    earm*)\n\t\tos=netbsdelf\n\t\t;;\n\t    arm*|i386|m68k|ns32k|sh3*|sparc|vax)\n\t\teval $set_cc_for_build\n\t\tif echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \\\n\t\t\t| grep -q __ELF__\n\t\tthen\n\t\t    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).\n\t\t    # Return netbsd for either.  FIX?\n\t\t    os=netbsd\n\t\telse\n\t\t    os=netbsdelf\n\t\tfi\n\t\t;;\n\t    *)\n\t\tos=netbsd\n\t\t;;\n\tesac\n\t# Determine ABI tags.\n\tcase \"${UNAME_MACHINE_ARCH}\" in\n\t    earm*)\n\t\texpr='s/^earmv[0-9]/-eabi/;s/eb$//'\n\t\tabi=`echo ${UNAME_MACHINE_ARCH} | sed -e \"$expr\"`\n\t\t;;\n\tesac\n\t# The OS release\n\t# Debian GNU/NetBSD machines have a different userland, and\n\t# thus, need a distinct triplet. However, they do not need\n\t# kernel version information, so it can be replaced with a\n\t# suitable tag, in the style of linux-gnu.\n\tcase \"${UNAME_VERSION}\" in\n\t    Debian*)\n\t\trelease='-gnu'\n\t\t;;\n\t    *)\n\t\trelease=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2`\n\t\t;;\n\tesac\n\t# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:\n\t# contains redundant information, the shorter form:\n\t# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.\n\techo \"${machine}-${os}${release}${abi}\"\n\texit ;;\n    *:Bitrig:*:*)\n\tUNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`\n\techo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}\n\texit ;;\n    *:OpenBSD:*:*)\n\tUNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`\n\techo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}\n\texit ;;\n    *:LibertyBSD:*:*)\n\tUNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\\.//'`\n\techo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE}\n\texit ;;\n    *:ekkoBSD:*:*)\n\techo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}\n\texit ;;\n    *:SolidBSD:*:*)\n\techo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}\n\texit ;;\n    macppc:MirBSD:*:*)\n\techo powerpc-unknown-mirbsd${UNAME_RELEASE}\n\texit ;;\n    *:MirBSD:*:*)\n\techo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}\n\texit ;;\n    *:Sortix:*:*)\n\techo ${UNAME_MACHINE}-unknown-sortix\n\texit ;;\n    *:Redox:*:*)\n\techo ${UNAME_MACHINE}-unknown-redox\n\texit ;;\n    alpha:OSF1:*:*)\n\tcase $UNAME_RELEASE in\n\t*4.0)\n\t\tUNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`\n\t\t;;\n\t*5.*)\n\t\tUNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`\n\t\t;;\n\tesac\n\t# According to Compaq, /usr/sbin/psrinfo has been available on\n\t# OSF/1 and Tru64 systems produced since 1995.  I hope that\n\t# covers most systems running today.  This code pipes the CPU\n\t# types through head -n 1, so we only detect the type of CPU 0.\n\tALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \\(.*\\) processor.*$/\\1/p' | head -n 1`\n\tcase \"$ALPHA_CPU_TYPE\" in\n\t    \"EV4 (21064)\")\n\t\tUNAME_MACHINE=alpha ;;\n\t    \"EV4.5 (21064)\")\n\t\tUNAME_MACHINE=alpha ;;\n\t    \"LCA4 (21066/21068)\")\n\t\tUNAME_MACHINE=alpha ;;\n\t    \"EV5 (21164)\")\n\t\tUNAME_MACHINE=alphaev5 ;;\n\t    \"EV5.6 (21164A)\")\n\t\tUNAME_MACHINE=alphaev56 ;;\n\t    \"EV5.6 (21164PC)\")\n\t\tUNAME_MACHINE=alphapca56 ;;\n\t    \"EV5.7 (21164PC)\")\n\t\tUNAME_MACHINE=alphapca57 ;;\n\t    \"EV6 (21264)\")\n\t\tUNAME_MACHINE=alphaev6 ;;\n\t    \"EV6.7 (21264A)\")\n\t\tUNAME_MACHINE=alphaev67 ;;\n\t    \"EV6.8CB (21264C)\")\n\t\tUNAME_MACHINE=alphaev68 ;;\n\t    \"EV6.8AL (21264B)\")\n\t\tUNAME_MACHINE=alphaev68 ;;\n\t    \"EV6.8CX (21264D)\")\n\t\tUNAME_MACHINE=alphaev68 ;;\n\t    \"EV6.9A (21264/EV69A)\")\n\t\tUNAME_MACHINE=alphaev69 ;;\n\t    \"EV7 (21364)\")\n\t\tUNAME_MACHINE=alphaev7 ;;\n\t    \"EV7.9 (21364A)\")\n\t\tUNAME_MACHINE=alphaev79 ;;\n\tesac\n\t# A Pn.n version is a patched version.\n\t# A Vn.n version is a released version.\n\t# A Tn.n version is a released field test version.\n\t# A Xn.n version is an unreleased experimental baselevel.\n\t# 1.2 uses \"1.2\" for uname -r.\n\techo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`\n\t# Reset EXIT trap before exiting to avoid spurious non-zero exit code.\n\texitcode=$?\n\ttrap '' 0\n\texit $exitcode ;;\n    Amiga*:UNIX_System_V:4.0:*)\n\techo m68k-unknown-sysv4\n\texit ;;\n    *:[Aa]miga[Oo][Ss]:*:*)\n\techo ${UNAME_MACHINE}-unknown-amigaos\n\texit ;;\n    *:[Mm]orph[Oo][Ss]:*:*)\n\techo ${UNAME_MACHINE}-unknown-morphos\n\texit ;;\n    *:OS/390:*:*)\n\techo i370-ibm-openedition\n\texit ;;\n    *:z/VM:*:*)\n\techo s390-ibm-zvmoe\n\texit ;;\n    *:OS400:*:*)\n\techo powerpc-ibm-os400\n\texit ;;\n    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)\n\techo arm-acorn-riscix${UNAME_RELEASE}\n\texit ;;\n    arm*:riscos:*:*|arm*:RISCOS:*:*)\n\techo arm-unknown-riscos\n\texit ;;\n    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)\n\techo hppa1.1-hitachi-hiuxmpp\n\texit ;;\n    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)\n\t# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.\n\tif test \"`(/bin/universe) 2>/dev/null`\" = att ; then\n\t\techo pyramid-pyramid-sysv3\n\telse\n\t\techo pyramid-pyramid-bsd\n\tfi\n\texit ;;\n    NILE*:*:*:dcosx)\n\techo pyramid-pyramid-svr4\n\texit ;;\n    DRS?6000:unix:4.0:6*)\n\techo sparc-icl-nx6\n\texit ;;\n    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)\n\tcase `/usr/bin/uname -p` in\n\t    sparc) echo sparc-icl-nx7; exit ;;\n\tesac ;;\n    s390x:SunOS:*:*)\n\techo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4H:SunOS:5.*:*)\n\techo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)\n\techo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)\n\techo i386-pc-auroraux${UNAME_RELEASE}\n\texit ;;\n    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)\n\teval $set_cc_for_build\n\tSUN_ARCH=i386\n\t# If there is a compiler, see if it is configured for 64-bit objects.\n\t# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.\n\t# This test works for both compilers.\n\tif [ \"$CC_FOR_BUILD\" != no_compiler_found ]; then\n\t    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \\\n\t\t(CCOPTS=\"\" $CC_FOR_BUILD -E - 2>/dev/null) | \\\n\t\tgrep IS_64BIT_ARCH >/dev/null\n\t    then\n\t\tSUN_ARCH=x86_64\n\t    fi\n\tfi\n\techo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4*:SunOS:6*:*)\n\t# According to config.sub, this is the proper way to canonicalize\n\t# SunOS6.  Hard to guess exactly what SunOS6 will be like, but\n\t# it's likely to be more like Solaris than SunOS4.\n\techo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    sun4*:SunOS:*:*)\n\tcase \"`/usr/bin/arch -k`\" in\n\t    Series*|S4*)\n\t\tUNAME_RELEASE=`uname -v`\n\t\t;;\n\tesac\n\t# Japanese Language versions have a version number like `4.1.3-JL'.\n\techo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`\n\texit ;;\n    sun3*:SunOS:*:*)\n\techo m68k-sun-sunos${UNAME_RELEASE}\n\texit ;;\n    sun*:*:4.2BSD:*)\n\tUNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`\n\ttest \"x${UNAME_RELEASE}\" = x && UNAME_RELEASE=3\n\tcase \"`/bin/arch`\" in\n\t    sun3)\n\t\techo m68k-sun-sunos${UNAME_RELEASE}\n\t\t;;\n\t    sun4)\n\t\techo sparc-sun-sunos${UNAME_RELEASE}\n\t\t;;\n\tesac\n\texit ;;\n    aushp:SunOS:*:*)\n\techo sparc-auspex-sunos${UNAME_RELEASE}\n\texit ;;\n    # The situation for MiNT is a little confusing.  The machine name\n    # can be virtually everything (everything which is not\n    # \"atarist\" or \"atariste\" at least should have a processor\n    # > m68000).  The system name ranges from \"MiNT\" over \"FreeMiNT\"\n    # to the lowercase version \"mint\" (or \"freemint\").  Finally\n    # the system name \"TOS\" denotes a system which is actually not\n    # MiNT.  But MiNT is downward compatible to TOS, so this should\n    # be no problem.\n    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)\n\techo m68k-atari-mint${UNAME_RELEASE}\n\texit ;;\n    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)\n\techo m68k-atari-mint${UNAME_RELEASE}\n\texit ;;\n    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)\n\techo m68k-atari-mint${UNAME_RELEASE}\n\texit ;;\n    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)\n\techo m68k-milan-mint${UNAME_RELEASE}\n\texit ;;\n    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)\n\techo m68k-hades-mint${UNAME_RELEASE}\n\texit ;;\n    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)\n\techo m68k-unknown-mint${UNAME_RELEASE}\n\texit ;;\n    m68k:machten:*:*)\n\techo m68k-apple-machten${UNAME_RELEASE}\n\texit ;;\n    powerpc:machten:*:*)\n\techo powerpc-apple-machten${UNAME_RELEASE}\n\texit ;;\n    RISC*:Mach:*:*)\n\techo mips-dec-mach_bsd4.3\n\texit ;;\n    RISC*:ULTRIX:*:*)\n\techo mips-dec-ultrix${UNAME_RELEASE}\n\texit ;;\n    VAX*:ULTRIX*:*:*)\n\techo vax-dec-ultrix${UNAME_RELEASE}\n\texit ;;\n    2020:CLIX:*:* | 2430:CLIX:*:*)\n\techo clipper-intergraph-clix${UNAME_RELEASE}\n\texit ;;\n    mips:*:*:UMIPS | mips:*:*:RISCos)\n\teval $set_cc_for_build\n\tsed 's/^\t//' << EOF >$dummy.c\n#ifdef __cplusplus\n#include <stdio.h>  /* for printf() prototype */\n\tint main (int argc, char *argv[]) {\n#else\n\tint main (argc, argv) int argc; char *argv[]; {\n#endif\n\t#if defined (host_mips) && defined (MIPSEB)\n\t#if defined (SYSTYPE_SYSV)\n\t  printf (\"mips-mips-riscos%ssysv\\n\", argv[1]); exit (0);\n\t#endif\n\t#if defined (SYSTYPE_SVR4)\n\t  printf (\"mips-mips-riscos%ssvr4\\n\", argv[1]); exit (0);\n\t#endif\n\t#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)\n\t  printf (\"mips-mips-riscos%sbsd\\n\", argv[1]); exit (0);\n\t#endif\n\t#endif\n\t  exit (-1);\n\t}\nEOF\n\t$CC_FOR_BUILD -o $dummy $dummy.c &&\n\t  dummyarg=`echo \"${UNAME_RELEASE}\" | sed -n 's/\\([0-9]*\\).*/\\1/p'` &&\n\t  SYSTEM_NAME=`$dummy $dummyarg` &&\n\t    { echo \"$SYSTEM_NAME\"; exit; }\n\techo mips-mips-riscos${UNAME_RELEASE}\n\texit ;;\n    Motorola:PowerMAX_OS:*:*)\n\techo powerpc-motorola-powermax\n\texit ;;\n    Motorola:*:4.3:PL8-*)\n\techo powerpc-harris-powermax\n\texit ;;\n    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)\n\techo powerpc-harris-powermax\n\texit ;;\n    Night_Hawk:Power_UNIX:*:*)\n\techo powerpc-harris-powerunix\n\texit ;;\n    m88k:CX/UX:7*:*)\n\techo m88k-harris-cxux7\n\texit ;;\n    m88k:*:4*:R4*)\n\techo m88k-motorola-sysv4\n\texit ;;\n    m88k:*:3*:R3*)\n\techo m88k-motorola-sysv3\n\texit ;;\n    AViiON:dgux:*:*)\n\t# DG/UX returns AViiON for all architectures\n\tUNAME_PROCESSOR=`/usr/bin/uname -p`\n\tif [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]\n\tthen\n\t    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \\\n\t       [ ${TARGET_BINARY_INTERFACE}x = x ]\n\t    then\n\t\techo m88k-dg-dgux${UNAME_RELEASE}\n\t    else\n\t\techo m88k-dg-dguxbcs${UNAME_RELEASE}\n\t    fi\n\telse\n\t    echo i586-dg-dgux${UNAME_RELEASE}\n\tfi\n\texit ;;\n    M88*:DolphinOS:*:*)\t# DolphinOS (SVR3)\n\techo m88k-dolphin-sysv3\n\texit ;;\n    M88*:*:R3*:*)\n\t# Delta 88k system running SVR3\n\techo m88k-motorola-sysv3\n\texit ;;\n    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)\n\techo m88k-tektronix-sysv3\n\texit ;;\n    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)\n\techo m68k-tektronix-bsd\n\texit ;;\n    *:IRIX*:*:*)\n\techo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`\n\texit ;;\n    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.\n\techo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id\n\texit ;;               # Note that: echo \"'`uname -s`'\" gives 'AIX '\n    i*86:AIX:*:*)\n\techo i386-ibm-aix\n\texit ;;\n    ia64:AIX:*:*)\n\tif [ -x /usr/bin/oslevel ] ; then\n\t\tIBM_REV=`/usr/bin/oslevel`\n\telse\n\t\tIBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}\n\tfi\n\techo ${UNAME_MACHINE}-ibm-aix${IBM_REV}\n\texit ;;\n    *:AIX:2:3)\n\tif grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then\n\t\teval $set_cc_for_build\n\t\tsed 's/^\t\t//' << EOF >$dummy.c\n\t\t#include <sys/systemcfg.h>\n\n\t\tmain()\n\t\t\t{\n\t\t\tif (!__power_pc())\n\t\t\t\texit(1);\n\t\t\tputs(\"powerpc-ibm-aix3.2.5\");\n\t\t\texit(0);\n\t\t\t}\nEOF\n\t\tif $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`\n\t\tthen\n\t\t\techo \"$SYSTEM_NAME\"\n\t\telse\n\t\t\techo rs6000-ibm-aix3.2.5\n\t\tfi\n\telif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then\n\t\techo rs6000-ibm-aix3.2.4\n\telse\n\t\techo rs6000-ibm-aix3.2\n\tfi\n\texit ;;\n    *:AIX:*:[4567])\n\tIBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`\n\tif /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then\n\t\tIBM_ARCH=rs6000\n\telse\n\t\tIBM_ARCH=powerpc\n\tfi\n\tif [ -x /usr/bin/lslpp ] ; then\n\t\tIBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |\n\t\t\t   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`\n\telse\n\t\tIBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}\n\tfi\n\techo ${IBM_ARCH}-ibm-aix${IBM_REV}\n\texit ;;\n    *:AIX:*:*)\n\techo rs6000-ibm-aix\n\texit ;;\n    ibmrt:4.4BSD:*|romp-ibm:BSD:*)\n\techo romp-ibm-bsd4.4\n\texit ;;\n    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and\n\techo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to\n\texit ;;                             # report: romp-ibm BSD 4.3\n    *:BOSX:*:*)\n\techo rs6000-bull-bosx\n\texit ;;\n    DPX/2?00:B.O.S.:*:*)\n\techo m68k-bull-sysv3\n\texit ;;\n    9000/[34]??:4.3bsd:1.*:*)\n\techo m68k-hp-bsd\n\texit ;;\n    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)\n\techo m68k-hp-bsd4.4\n\texit ;;\n    9000/[34678]??:HP-UX:*:*)\n\tHPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`\n\tcase \"${UNAME_MACHINE}\" in\n\t    9000/31? )            HP_ARCH=m68000 ;;\n\t    9000/[34]?? )         HP_ARCH=m68k ;;\n\t    9000/[678][0-9][0-9])\n\t\tif [ -x /usr/bin/getconf ]; then\n\t\t    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`\n\t\t    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`\n\t\t    case \"${sc_cpu_version}\" in\n\t\t      523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0\n\t\t      528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1\n\t\t      532)                      # CPU_PA_RISC2_0\n\t\t\tcase \"${sc_kernel_bits}\" in\n\t\t\t  32) HP_ARCH=hppa2.0n ;;\n\t\t\t  64) HP_ARCH=hppa2.0w ;;\n\t\t\t  '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20\n\t\t\tesac ;;\n\t\t    esac\n\t\tfi\n\t\tif [ \"${HP_ARCH}\" = \"\" ]; then\n\t\t    eval $set_cc_for_build\n\t\t    sed 's/^\t\t//' << EOF >$dummy.c\n\n\t\t#define _HPUX_SOURCE\n\t\t#include <stdlib.h>\n\t\t#include <unistd.h>\n\n\t\tint main ()\n\t\t{\n\t\t#if defined(_SC_KERNEL_BITS)\n\t\t    long bits = sysconf(_SC_KERNEL_BITS);\n\t\t#endif\n\t\t    long cpu  = sysconf (_SC_CPU_VERSION);\n\n\t\t    switch (cpu)\n\t\t\t{\n\t\t\tcase CPU_PA_RISC1_0: puts (\"hppa1.0\"); break;\n\t\t\tcase CPU_PA_RISC1_1: puts (\"hppa1.1\"); break;\n\t\t\tcase CPU_PA_RISC2_0:\n\t\t#if defined(_SC_KERNEL_BITS)\n\t\t\t    switch (bits)\n\t\t\t\t{\n\t\t\t\tcase 64: puts (\"hppa2.0w\"); break;\n\t\t\t\tcase 32: puts (\"hppa2.0n\"); break;\n\t\t\t\tdefault: puts (\"hppa2.0\"); break;\n\t\t\t\t} break;\n\t\t#else  /* !defined(_SC_KERNEL_BITS) */\n\t\t\t    puts (\"hppa2.0\"); break;\n\t\t#endif\n\t\t\tdefault: puts (\"hppa1.0\"); break;\n\t\t\t}\n\t\t    exit (0);\n\t\t}\nEOF\n\t\t    (CCOPTS=\"\" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`\n\t\t    test -z \"$HP_ARCH\" && HP_ARCH=hppa\n\t\tfi ;;\n\tesac\n\tif [ ${HP_ARCH} = hppa2.0w ]\n\tthen\n\t    eval $set_cc_for_build\n\n\t    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating\n\t    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler\n\t    # generating 64-bit code.  GNU and HP use different nomenclature:\n\t    #\n\t    # $ CC_FOR_BUILD=cc ./config.guess\n\t    # => hppa2.0w-hp-hpux11.23\n\t    # $ CC_FOR_BUILD=\"cc +DA2.0w\" ./config.guess\n\t    # => hppa64-hp-hpux11.23\n\n\t    if echo __LP64__ | (CCOPTS=\"\" $CC_FOR_BUILD -E - 2>/dev/null) |\n\t\tgrep -q __LP64__\n\t    then\n\t\tHP_ARCH=hppa2.0w\n\t    else\n\t\tHP_ARCH=hppa64\n\t    fi\n\tfi\n\techo ${HP_ARCH}-hp-hpux${HPUX_REV}\n\texit ;;\n    ia64:HP-UX:*:*)\n\tHPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`\n\techo ia64-hp-hpux${HPUX_REV}\n\texit ;;\n    3050*:HI-UX:*:*)\n\teval $set_cc_for_build\n\tsed 's/^\t//' << EOF >$dummy.c\n\t#include <unistd.h>\n\tint\n\tmain ()\n\t{\n\t  long cpu = sysconf (_SC_CPU_VERSION);\n\t  /* The order matters, because CPU_IS_HP_MC68K erroneously returns\n\t     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct\n\t     results, however.  */\n\t  if (CPU_IS_PA_RISC (cpu))\n\t    {\n\t      switch (cpu)\n\t\t{\n\t\t  case CPU_PA_RISC1_0: puts (\"hppa1.0-hitachi-hiuxwe2\"); break;\n\t\t  case CPU_PA_RISC1_1: puts (\"hppa1.1-hitachi-hiuxwe2\"); break;\n\t\t  case CPU_PA_RISC2_0: puts (\"hppa2.0-hitachi-hiuxwe2\"); break;\n\t\t  default: puts (\"hppa-hitachi-hiuxwe2\"); break;\n\t\t}\n\t    }\n\t  else if (CPU_IS_HP_MC68K (cpu))\n\t    puts (\"m68k-hitachi-hiuxwe2\");\n\t  else puts (\"unknown-hitachi-hiuxwe2\");\n\t  exit (0);\n\t}\nEOF\n\t$CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&\n\t\t{ echo \"$SYSTEM_NAME\"; exit; }\n\techo unknown-hitachi-hiuxwe2\n\texit ;;\n    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )\n\techo hppa1.1-hp-bsd\n\texit ;;\n    9000/8??:4.3bsd:*:*)\n\techo hppa1.0-hp-bsd\n\texit ;;\n    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)\n\techo hppa1.0-hp-mpeix\n\texit ;;\n    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )\n\techo hppa1.1-hp-osf\n\texit ;;\n    hp8??:OSF1:*:*)\n\techo hppa1.0-hp-osf\n\texit ;;\n    i*86:OSF1:*:*)\n\tif [ -x /usr/sbin/sysversion ] ; then\n\t    echo ${UNAME_MACHINE}-unknown-osf1mk\n\telse\n\t    echo ${UNAME_MACHINE}-unknown-osf1\n\tfi\n\texit ;;\n    parisc*:Lites*:*:*)\n\techo hppa1.1-hp-lites\n\texit ;;\n    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)\n\techo c1-convex-bsd\n\texit ;;\n    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)\n\tif getsysinfo -f scalar_acc\n\tthen echo c32-convex-bsd\n\telse echo c2-convex-bsd\n\tfi\n\texit ;;\n    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)\n\techo c34-convex-bsd\n\texit ;;\n    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)\n\techo c38-convex-bsd\n\texit ;;\n    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)\n\techo c4-convex-bsd\n\texit ;;\n    CRAY*Y-MP:*:*:*)\n\techo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*[A-Z]90:*:*:*)\n\techo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \\\n\t| sed -e 's/CRAY.*\\([A-Z]90\\)/\\1/' \\\n\t      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \\\n\t      -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*TS:*:*:*)\n\techo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*T3E:*:*:*)\n\techo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    CRAY*SV1:*:*:*)\n\techo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    *:UNICOS/mp:*:*)\n\techo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\\.[^.]*$/.X/'\n\texit ;;\n    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)\n\tFUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`\n\tFUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\\///'`\n\tFUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`\n\techo \"${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}\"\n\texit ;;\n    5000:UNIX_System_V:4.*:*)\n\tFUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\\///'`\n\tFUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`\n\techo \"sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}\"\n\texit ;;\n    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\\ Embedded/OS:*:*)\n\techo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}\n\texit ;;\n    sparc*:BSD/OS:*:*)\n\techo sparc-unknown-bsdi${UNAME_RELEASE}\n\texit ;;\n    *:BSD/OS:*:*)\n\techo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}\n\texit ;;\n    *:FreeBSD:*:*)\n\tUNAME_PROCESSOR=`/usr/bin/uname -p`\n\tcase ${UNAME_PROCESSOR} in\n\t    amd64)\n\t\tUNAME_PROCESSOR=x86_64 ;;\n\t    i386)\n\t\tUNAME_PROCESSOR=i586 ;;\n\tesac\n\techo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`\n\texit ;;\n    i*:CYGWIN*:*)\n\techo ${UNAME_MACHINE}-pc-cygwin\n\texit ;;\n    *:MINGW64*:*)\n\techo ${UNAME_MACHINE}-pc-mingw64\n\texit ;;\n    *:MINGW*:*)\n\techo ${UNAME_MACHINE}-pc-mingw32\n\texit ;;\n    *:MSYS*:*)\n\techo ${UNAME_MACHINE}-pc-msys\n\texit ;;\n    i*:PW*:*)\n\techo ${UNAME_MACHINE}-pc-pw32\n\texit ;;\n    *:Interix*:*)\n\tcase ${UNAME_MACHINE} in\n\t    x86)\n\t\techo i586-pc-interix${UNAME_RELEASE}\n\t\texit ;;\n\t    authenticamd | genuineintel | EM64T)\n\t\techo x86_64-unknown-interix${UNAME_RELEASE}\n\t\texit ;;\n\t    IA64)\n\t\techo ia64-unknown-interix${UNAME_RELEASE}\n\t\texit ;;\n\tesac ;;\n    i*:UWIN*:*)\n\techo ${UNAME_MACHINE}-pc-uwin\n\texit ;;\n    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)\n\techo x86_64-unknown-cygwin\n\texit ;;\n    prep*:SunOS:5.*:*)\n\techo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`\n\texit ;;\n    *:GNU:*:*)\n\t# the GNU system\n\techo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`\n\texit ;;\n    *:GNU/*:*:*)\n\t# other systems with GNU libc and userland\n\techo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr \"[:upper:]\" \"[:lower:]\"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}\n\texit ;;\n    i*86:Minix:*:*)\n\techo ${UNAME_MACHINE}-pc-minix\n\texit ;;\n    aarch64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    aarch64_be:Linux:*:*)\n\tUNAME_MACHINE=aarch64_be\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    alpha:Linux:*:*)\n\tcase `sed -n '/^cpu model/s/^.*: \\(.*\\)/\\1/p' < /proc/cpuinfo` in\n\t  EV5)   UNAME_MACHINE=alphaev5 ;;\n\t  EV56)  UNAME_MACHINE=alphaev56 ;;\n\t  PCA56) UNAME_MACHINE=alphapca56 ;;\n\t  PCA57) UNAME_MACHINE=alphapca56 ;;\n\t  EV6)   UNAME_MACHINE=alphaev6 ;;\n\t  EV67)  UNAME_MACHINE=alphaev67 ;;\n\t  EV68*) UNAME_MACHINE=alphaev68 ;;\n\tesac\n\tobjdump --private-headers /bin/sh | grep -q ld.so.1\n\tif test \"$?\" = 0 ; then LIBC=gnulibc1 ; fi\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    arc:Linux:*:* | arceb:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    arm*:Linux:*:*)\n\teval $set_cc_for_build\n\tif echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \\\n\t    | grep -q __ARM_EABI__\n\tthen\n\t    echo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\telse\n\t    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \\\n\t\t| grep -q __ARM_PCS_VFP\n\t    then\n\t\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi\n\t    else\n\t\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf\n\t    fi\n\tfi\n\texit ;;\n    avr32*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    cris:Linux:*:*)\n\techo ${UNAME_MACHINE}-axis-linux-${LIBC}\n\texit ;;\n    crisv32:Linux:*:*)\n\techo ${UNAME_MACHINE}-axis-linux-${LIBC}\n\texit ;;\n    e2k:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    frv:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    hexagon:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    i*86:Linux:*:*)\n\techo ${UNAME_MACHINE}-pc-linux-${LIBC}\n\texit ;;\n    ia64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    k1om:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    m32r*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    m68*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    mips:Linux:*:* | mips64:Linux:*:*)\n\teval $set_cc_for_build\n\tsed 's/^\t//' << EOF >$dummy.c\n\t#undef CPU\n\t#undef ${UNAME_MACHINE}\n\t#undef ${UNAME_MACHINE}el\n\t#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)\n\tCPU=${UNAME_MACHINE}el\n\t#else\n\t#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)\n\tCPU=${UNAME_MACHINE}\n\t#else\n\tCPU=\n\t#endif\n\t#endif\nEOF\n\teval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`\n\ttest x\"${CPU}\" != x && { echo \"${CPU}-unknown-linux-${LIBC}\"; exit; }\n\t;;\n    mips64el:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    openrisc*:Linux:*:*)\n\techo or1k-unknown-linux-${LIBC}\n\texit ;;\n    or32:Linux:*:* | or1k*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    padre:Linux:*:*)\n\techo sparc-unknown-linux-${LIBC}\n\texit ;;\n    parisc64:Linux:*:* | hppa64:Linux:*:*)\n\techo hppa64-unknown-linux-${LIBC}\n\texit ;;\n    parisc:Linux:*:* | hppa:Linux:*:*)\n\t# Look for CPU level\n\tcase `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in\n\t  PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;\n\t  PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;\n\t  *)    echo hppa-unknown-linux-${LIBC} ;;\n\tesac\n\texit ;;\n    ppc64:Linux:*:*)\n\techo powerpc64-unknown-linux-${LIBC}\n\texit ;;\n    ppc:Linux:*:*)\n\techo powerpc-unknown-linux-${LIBC}\n\texit ;;\n    ppc64le:Linux:*:*)\n\techo powerpc64le-unknown-linux-${LIBC}\n\texit ;;\n    ppcle:Linux:*:*)\n\techo powerpcle-unknown-linux-${LIBC}\n\texit ;;\n    riscv32:Linux:*:* | riscv64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    s390:Linux:*:* | s390x:Linux:*:*)\n\techo ${UNAME_MACHINE}-ibm-linux-${LIBC}\n\texit ;;\n    sh64*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    sh*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    sparc:Linux:*:* | sparc64:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    tile*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    vax:Linux:*:*)\n\techo ${UNAME_MACHINE}-dec-linux-${LIBC}\n\texit ;;\n    x86_64:Linux:*:*)\n\techo ${UNAME_MACHINE}-pc-linux-${LIBC}\n\texit ;;\n    xtensa*:Linux:*:*)\n\techo ${UNAME_MACHINE}-unknown-linux-${LIBC}\n\texit ;;\n    i*86:DYNIX/ptx:4*:*)\n\t# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.\n\t# earlier versions are messed up and put the nodename in both\n\t# sysname and nodename.\n\techo i386-sequent-sysv4\n\texit ;;\n    i*86:UNIX_SV:4.2MP:2.*)\n\t# Unixware is an offshoot of SVR4, but it has its own version\n\t# number series starting with 2...\n\t# I am not positive that other SVR4 systems won't match this,\n\t# I just have to hope.  -- rms.\n\t# Use sysv4.2uw... so that sysv4* matches it.\n\techo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}\n\texit ;;\n    i*86:OS/2:*:*)\n\t# If we were able to find `uname', then EMX Unix compatibility\n\t# is probably installed.\n\techo ${UNAME_MACHINE}-pc-os2-emx\n\texit ;;\n    i*86:XTS-300:*:STOP)\n\techo ${UNAME_MACHINE}-unknown-stop\n\texit ;;\n    i*86:atheos:*:*)\n\techo ${UNAME_MACHINE}-unknown-atheos\n\texit ;;\n    i*86:syllable:*:*)\n\techo ${UNAME_MACHINE}-pc-syllable\n\texit ;;\n    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)\n\techo i386-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    i*86:*DOS:*:*)\n\techo ${UNAME_MACHINE}-pc-msdosdjgpp\n\texit ;;\n    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)\n\tUNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\\/MP$//'`\n\tif grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then\n\t\techo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}\n\telse\n\t\techo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}\n\tfi\n\texit ;;\n    i*86:*:5:[678]*)\n\t# UnixWare 7.x, OpenUNIX and OpenServer 6.\n\tcase `/bin/uname -X | grep \"^Machine\"` in\n\t    *486*)\t     UNAME_MACHINE=i486 ;;\n\t    *Pentium)\t     UNAME_MACHINE=i586 ;;\n\t    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;\n\tesac\n\techo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}\n\texit ;;\n    i*86:*:3.2:*)\n\tif test -f /usr/options/cb.name; then\n\t\tUNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`\n\t\techo ${UNAME_MACHINE}-pc-isc$UNAME_REL\n\telif /bin/uname -X 2>/dev/null >/dev/null ; then\n\t\tUNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`\n\t\t(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486\n\t\t(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \\\n\t\t\t&& UNAME_MACHINE=i586\n\t\t(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \\\n\t\t\t&& UNAME_MACHINE=i686\n\t\t(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \\\n\t\t\t&& UNAME_MACHINE=i686\n\t\techo ${UNAME_MACHINE}-pc-sco$UNAME_REL\n\telse\n\t\techo ${UNAME_MACHINE}-pc-sysv32\n\tfi\n\texit ;;\n    pc:*:*:*)\n\t# Left here for compatibility:\n\t# uname -m prints for DJGPP always 'pc', but it prints nothing about\n\t# the processor, so we play safe by assuming i586.\n\t# Note: whatever this is, it MUST be the same as what config.sub\n\t# prints for the \"djgpp\" host, or else GDB configure will decide that\n\t# this is a cross-build.\n\techo i586-pc-msdosdjgpp\n\texit ;;\n    Intel:Mach:3*:*)\n\techo i386-pc-mach3\n\texit ;;\n    paragon:*:*:*)\n\techo i860-intel-osf1\n\texit ;;\n    i860:*:4.*:*) # i860-SVR4\n\tif grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then\n\t  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4\n\telse # Add other i860-SVR4 vendors below as they are discovered.\n\t  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4\n\tfi\n\texit ;;\n    mini*:CTIX:SYS*5:*)\n\t# \"miniframe\"\n\techo m68010-convergent-sysv\n\texit ;;\n    mc68k:UNIX:SYSTEM5:3.51m)\n\techo m68k-convergent-sysv\n\texit ;;\n    M680?0:D-NIX:5.3:*)\n\techo m68k-diab-dnix\n\texit ;;\n    M68*:*:R3V[5678]*:*)\n\ttest -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;\n    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)\n\tOS_REL=''\n\ttest -r /etc/.relid \\\n\t&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \\([0-9][0-9]\\).*/\\1/p' < /etc/.relid`\n\t/bin/uname -p 2>/dev/null | grep 86 >/dev/null \\\n\t  && { echo i486-ncr-sysv4.3${OS_REL}; exit; }\n\t/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \\\n\t  && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;\n    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)\n\t/bin/uname -p 2>/dev/null | grep 86 >/dev/null \\\n\t  && { echo i486-ncr-sysv4; exit; } ;;\n    NCR*:*:4.2:* | MPRAS*:*:4.2:*)\n\tOS_REL='.3'\n\ttest -r /etc/.relid \\\n\t    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \\([0-9][0-9]\\).*/\\1/p' < /etc/.relid`\n\t/bin/uname -p 2>/dev/null | grep 86 >/dev/null \\\n\t    && { echo i486-ncr-sysv4.3${OS_REL}; exit; }\n\t/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \\\n\t    && { echo i586-ncr-sysv4.3${OS_REL}; exit; }\n\t/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \\\n\t    && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;\n    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)\n\techo m68k-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    mc68030:UNIX_System_V:4.*:*)\n\techo m68k-atari-sysv4\n\texit ;;\n    TSUNAMI:LynxOS:2.*:*)\n\techo sparc-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    rs6000:LynxOS:2.*:*)\n\techo rs6000-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)\n\techo powerpc-unknown-lynxos${UNAME_RELEASE}\n\texit ;;\n    SM[BE]S:UNIX_SV:*:*)\n\techo mips-dde-sysv${UNAME_RELEASE}\n\texit ;;\n    RM*:ReliantUNIX-*:*:*)\n\techo mips-sni-sysv4\n\texit ;;\n    RM*:SINIX-*:*:*)\n\techo mips-sni-sysv4\n\texit ;;\n    *:SINIX-*:*:*)\n\tif uname -p 2>/dev/null >/dev/null ; then\n\t\tUNAME_MACHINE=`(uname -p) 2>/dev/null`\n\t\techo ${UNAME_MACHINE}-sni-sysv4\n\telse\n\t\techo ns32k-sni-sysv\n\tfi\n\texit ;;\n    PENTIUM:*:4.0*:*)\t# Unisys `ClearPath HMP IX 4000' SVR4/MP effort\n\t\t\t# says <Richard.M.Bartel@ccMail.Census.GOV>\n\techo i586-unisys-sysv4\n\texit ;;\n    *:UNIX_System_V:4*:FTX*)\n\t# From Gerald Hewes <hewes@openmarket.com>.\n\t# How about differentiating between stratus architectures? -djm\n\techo hppa1.1-stratus-sysv4\n\texit ;;\n    *:*:*:FTX*)\n\t# From seanf@swdc.stratus.com.\n\techo i860-stratus-sysv4\n\texit ;;\n    i*86:VOS:*:*)\n\t# From Paul.Green@stratus.com.\n\techo ${UNAME_MACHINE}-stratus-vos\n\texit ;;\n    *:VOS:*:*)\n\t# From Paul.Green@stratus.com.\n\techo hppa1.1-stratus-vos\n\texit ;;\n    mc68*:A/UX:*:*)\n\techo m68k-apple-aux${UNAME_RELEASE}\n\texit ;;\n    news*:NEWS-OS:6*:*)\n\techo mips-sony-newsos6\n\texit ;;\n    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)\n\tif [ -d /usr/nec ]; then\n\t\techo mips-nec-sysv${UNAME_RELEASE}\n\telse\n\t\techo mips-unknown-sysv${UNAME_RELEASE}\n\tfi\n\texit ;;\n    BeBox:BeOS:*:*)\t# BeOS running on hardware made by Be, PPC only.\n\techo powerpc-be-beos\n\texit ;;\n    BeMac:BeOS:*:*)\t# BeOS running on Mac or Mac clone, PPC only.\n\techo powerpc-apple-beos\n\texit ;;\n    BePC:BeOS:*:*)\t# BeOS running on Intel PC compatible.\n\techo i586-pc-beos\n\texit ;;\n    BePC:Haiku:*:*)\t# Haiku running on Intel PC compatible.\n\techo i586-pc-haiku\n\texit ;;\n    x86_64:Haiku:*:*)\n\techo x86_64-unknown-haiku\n\texit ;;\n    SX-4:SUPER-UX:*:*)\n\techo sx4-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-5:SUPER-UX:*:*)\n\techo sx5-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-6:SUPER-UX:*:*)\n\techo sx6-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-7:SUPER-UX:*:*)\n\techo sx7-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-8:SUPER-UX:*:*)\n\techo sx8-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-8R:SUPER-UX:*:*)\n\techo sx8r-nec-superux${UNAME_RELEASE}\n\texit ;;\n    SX-ACE:SUPER-UX:*:*)\n\techo sxace-nec-superux${UNAME_RELEASE}\n\texit ;;\n    Power*:Rhapsody:*:*)\n\techo powerpc-apple-rhapsody${UNAME_RELEASE}\n\texit ;;\n    *:Rhapsody:*:*)\n\techo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}\n\texit ;;\n    *:Darwin:*:*)\n\tUNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown\n\teval $set_cc_for_build\n\tif test \"$UNAME_PROCESSOR\" = unknown ; then\n\t    UNAME_PROCESSOR=powerpc\n\tfi\n\tif test `echo \"$UNAME_RELEASE\" | sed -e 's/\\..*//'` -le 10 ; then\n\t    if [ \"$CC_FOR_BUILD\" != no_compiler_found ]; then\n\t\tif (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \\\n\t\t       (CCOPTS=\"\" $CC_FOR_BUILD -E - 2>/dev/null) | \\\n\t\t       grep IS_64BIT_ARCH >/dev/null\n\t\tthen\n\t\t    case $UNAME_PROCESSOR in\n\t\t\ti386) UNAME_PROCESSOR=x86_64 ;;\n\t\t\tpowerpc) UNAME_PROCESSOR=powerpc64 ;;\n\t\t    esac\n\t\tfi\n\t\t# On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc\n\t\tif (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \\\n\t\t       (CCOPTS=\"\" $CC_FOR_BUILD -E - 2>/dev/null) | \\\n\t\t       grep IS_PPC >/dev/null\n\t\tthen\n\t\t    UNAME_PROCESSOR=powerpc\n\t\tfi\n\t    fi\n\telif test \"$UNAME_PROCESSOR\" = i386 ; then\n\t    # Avoid executing cc on OS X 10.9, as it ships with a stub\n\t    # that puts up a graphical alert prompting to install\n\t    # developer tools.  Any system running Mac OS X 10.7 or\n\t    # later (Darwin 11 and later) is required to have a 64-bit\n\t    # processor. This is not true of the ARM version of Darwin\n\t    # that Apple uses in portable devices.\n\t    UNAME_PROCESSOR=x86_64\n\tfi\n\techo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}\n\texit ;;\n    *:procnto*:*:* | *:QNX:[0123456789]*:*)\n\tUNAME_PROCESSOR=`uname -p`\n\tif test \"$UNAME_PROCESSOR\" = x86; then\n\t\tUNAME_PROCESSOR=i386\n\t\tUNAME_MACHINE=pc\n\tfi\n\techo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}\n\texit ;;\n    *:QNX:*:4*)\n\techo i386-pc-qnx\n\texit ;;\n    NEO-*:NONSTOP_KERNEL:*:*)\n\techo neo-tandem-nsk${UNAME_RELEASE}\n\texit ;;\n    NSE-*:NONSTOP_KERNEL:*:*)\n\techo nse-tandem-nsk${UNAME_RELEASE}\n\texit ;;\n    NSR-*:NONSTOP_KERNEL:*:*)\n\techo nsr-tandem-nsk${UNAME_RELEASE}\n\texit ;;\n    NSX-*:NONSTOP_KERNEL:*:*)\n\techo nsx-tandem-nsk${UNAME_RELEASE}\n\texit ;;\n    *:NonStop-UX:*:*)\n\techo mips-compaq-nonstopux\n\texit ;;\n    BS2000:POSIX*:*:*)\n\techo bs2000-siemens-sysv\n\texit ;;\n    DS/*:UNIX_System_V:*:*)\n\techo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}\n\texit ;;\n    *:Plan9:*:*)\n\t# \"uname -m\" is not consistent, so use $cputype instead. 386\n\t# is converted to i386 for consistency with other x86\n\t# operating systems.\n\tif test \"$cputype\" = 386; then\n\t    UNAME_MACHINE=i386\n\telse\n\t    UNAME_MACHINE=\"$cputype\"\n\tfi\n\techo ${UNAME_MACHINE}-unknown-plan9\n\texit ;;\n    *:TOPS-10:*:*)\n\techo pdp10-unknown-tops10\n\texit ;;\n    *:TENEX:*:*)\n\techo pdp10-unknown-tenex\n\texit ;;\n    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)\n\techo pdp10-dec-tops20\n\texit ;;\n    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)\n\techo pdp10-xkl-tops20\n\texit ;;\n    *:TOPS-20:*:*)\n\techo pdp10-unknown-tops20\n\texit ;;\n    *:ITS:*:*)\n\techo pdp10-unknown-its\n\texit ;;\n    SEI:*:*:SEIUX)\n\techo mips-sei-seiux${UNAME_RELEASE}\n\texit ;;\n    *:DragonFly:*:*)\n\techo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`\n\texit ;;\n    *:*VMS:*:*)\n\tUNAME_MACHINE=`(uname -p) 2>/dev/null`\n\tcase \"${UNAME_MACHINE}\" in\n\t    A*) echo alpha-dec-vms ; exit ;;\n\t    I*) echo ia64-dec-vms ; exit ;;\n\t    V*) echo vax-dec-vms ; exit ;;\n\tesac ;;\n    *:XENIX:*:SysV)\n\techo i386-pc-xenix\n\texit ;;\n    i*86:skyos:*:*)\n\techo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'`\n\texit ;;\n    i*86:rdos:*:*)\n\techo ${UNAME_MACHINE}-pc-rdos\n\texit ;;\n    i*86:AROS:*:*)\n\techo ${UNAME_MACHINE}-pc-aros\n\texit ;;\n    x86_64:VMkernel:*:*)\n\techo ${UNAME_MACHINE}-unknown-esx\n\texit ;;\n    amd64:Isilon\\ OneFS:*:*)\n\techo x86_64-unknown-onefs\n\texit ;;\nesac\n\necho \"$0: unable to guess system type\" >&2\n\ncase \"${UNAME_MACHINE}:${UNAME_SYSTEM}\" in\n    mips:Linux | mips64:Linux)\n\t# If we got here on MIPS GNU/Linux, output extra information.\n\tcat >&2 <<EOF\n\nNOTE: MIPS GNU/Linux systems require a C compiler to fully recognize\nthe system type. Please install a C compiler and try again.\nEOF\n\t;;\nesac\n\ncat >&2 <<EOF\n\nThis script (version $timestamp), has failed to recognize the\noperating system you are using. If your script is old, overwrite *all*\ncopies of config.guess and config.sub with the latest versions from:\n\n  https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess\nand\n  https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub\n\nIf $0 has already been updated, send the following data and any\ninformation you think might be pertinent to config-patches@gnu.org to\nprovide the necessary information to handle your system.\n\nconfig.guess timestamp = $timestamp\n\nuname -m = `(uname -m) 2>/dev/null || echo unknown`\nuname -r = `(uname -r) 2>/dev/null || echo unknown`\nuname -s = `(uname -s) 2>/dev/null || echo unknown`\nuname -v = `(uname -v) 2>/dev/null || echo unknown`\n\n/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`\n/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`\n\nhostinfo               = `(hostinfo) 2>/dev/null`\n/bin/universe          = `(/bin/universe) 2>/dev/null`\n/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`\n/bin/arch              = `(/bin/arch) 2>/dev/null`\n/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`\n/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`\n\nUNAME_MACHINE = ${UNAME_MACHINE}\nUNAME_RELEASE = ${UNAME_RELEASE}\nUNAME_SYSTEM  = ${UNAME_SYSTEM}\nUNAME_VERSION = ${UNAME_VERSION}\nEOF\n\nexit 1\n\n# Local variables:\n# eval: (add-hook 'write-file-functions 'time-stamp)\n# time-stamp-start: \"timestamp='\"\n# time-stamp-format: \"%:y-%02m-%02d\"\n# time-stamp-end: \"'\"\n# End:\n"
  },
  {
    "path": "config/general.m4",
    "content": "# config/general.m4\n# Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group\n# Portions Copyright (c) 1994, The Regents of the University of California\n\n# This file defines new macros to process configure command line\n# arguments, to replace the brain-dead AC_ARG_WITH and AC_ARG_ENABLE.\n# The flaw in these is particularly that they only differentiate\n# between \"given\" and \"not given\" and do not provide enough help to\n# process arguments that only accept \"yes/no\", that require an\n# argument (other than \"yes/no\"), etc.\n#\n# The point of this implementation is to reduce code size and\n# redundancy in configure.ac and to improve robustness and consistency\n# in the option evaluation code.\n\n\n# Convert type and name to shell variable name (e.g., \"enable_long_strings\")\nm4_define([pgac_arg_to_variable],\n          [$1[]_[]patsubst($2, -, _)])\n\n\n# PGAC_ARG(TYPE, NAME, HELP-STRING-LHS-EXTRA, HELP-STRING-RHS,\n#          [ACTION-IF-YES], [ACTION-IF-NO], [ACTION-IF-ARG],\n#          [ACTION-IF-OMITTED])\n# ------------------------------------------------------------\n# This is the base layer. TYPE is either \"with\" or \"enable\", depending\n# on what you like.  NAME is the rest of the option name.\n# HELP-STRING-LHS-EXTRA is a string to append to the option name on\n# the left-hand side of the help output, e.g., an argument name.  If\n# set to \"-\", append nothing, but let the option appear in the\n# negative form (disable/without).  HELP-STRING-RHS is the option\n# description, for the right-hand side of the help output.\n# ACTION-IF-YES is executed if the option is given without an argument\n# (or \"yes\", which is the same); similar for ACTION-IF-NO.\n\nAC_DEFUN([PGAC_ARG],\n[\nm4_case([$1],\n\nenable, [\nAC_ARG_ENABLE([$2], [AS_HELP_STRING([--]m4_if($3, -, disable, enable)[-$2]m4_if($3, -, , $3), [$4])], [\n  case [$]enableval in\n    yes)\n      m4_default([$5], :)\n      ;;\n    no)\n      m4_default([$6], :)\n      ;;\n    *)\n      $7\n      ;;\n  esac\n],\n[$8])[]dnl AC_ARG_ENABLE\n],\n\nwith, [\nAC_ARG_WITH([$2], [AS_HELP_STRING([--]m4_if($3, -, without, with)[-$2]m4_if($3, -, , $3), [$4])], [\n  case [$]withval in\n    yes)\n      m4_default([$5], :)\n      ;;\n    no)\n      m4_default([$6], :)\n      ;;\n    *)\n      $7\n      ;;\n  esac\n],\n[$8])[]dnl AC_ARG_WITH\n],\n\n[m4_fatal([first argument of $0 must be 'enable' or 'with', not '$1'])]\n)\n])# PGAC_ARG\n\n\n# PGAC_ARG_BOOL(TYPE, NAME, DEFAULT, HELP-STRING-RHS,\n#               [ACTION-IF-YES], [ACTION-IF-NO])\n# ---------------------------------------------------\n# Accept a boolean option, that is, one that only takes yes or no.\n# (\"no\" is equivalent to \"disable\" or \"without\"). DEFAULT is what\n# should be done if the option is omitted; it should be \"yes\" or \"no\".\n# (Consequently, one of ACTION-IF-YES and ACTION-IF-NO will always\n# execute.)\n\nAC_DEFUN([PGAC_ARG_BOOL],\n[dnl The following hack is necessary because in a few instances this\ndnl macro is called twice for the same option with different default\ndnl values.  But we only want it to appear once in the help.  We achieve\ndnl that by making the help string look the same, which is why we need to\ndnl save the default that was passed in previously.\nm4_define([_pgac_helpdefault], m4_ifdef([pgac_defined_$1_$2_bool], [m4_defn([pgac_defined_$1_$2_bool])], [$3]))dnl\nPGAC_ARG([$1], [$2], [m4_if(_pgac_helpdefault, yes, -)], [$4], [$5], [$6],\n          [AC_MSG_ERROR([no argument expected for --$1-$2 option])],\n          [m4_case([$3],\n                   yes, [pgac_arg_to_variable([$1], [$2])=yes\n$5],\n                   no,  [pgac_arg_to_variable([$1], [$2])=no\n$6],\n                   [m4_fatal([third argument of $0 must be 'yes' or 'no', not '$3'])])])[]dnl\nm4_define([pgac_defined_$1_$2_bool], [$3])dnl\n])# PGAC_ARG_BOOL\n\n\n# PGAC_ARG_REQ(TYPE, NAME, HELP-ARGNAME, HELP-STRING-RHS,\n#              [ACTION-IF-GIVEN], [ACTION-IF-NOT-GIVEN])\n# -------------------------------------------------------\n# This option will require an argument; \"yes\" or \"no\" will not be\n# accepted.  HELP-ARGNAME is a name for the argument for the help output.\n\nAC_DEFUN([PGAC_ARG_REQ],\n[PGAC_ARG([$1], [$2], [=$3], [$4],\n          [AC_MSG_ERROR([argument required for --$1-$2 option])],\n          [AC_MSG_ERROR([argument required for --$1-$2 option])],\n          [$5],\n          [$6])])# PGAC_ARG_REQ\n\n\n# PGAC_ARG_OPTARG(TYPE, NAME, HELP-ARGNAME, HELP-STRING-RHS,\n#                 [DEFAULT-ACTION], [ARG-ACTION],\n#                 [ACTION-ENABLED], [ACTION-DISABLED])\n# ----------------------------------------------------------\n# This will create an option that behaves as follows: If omitted, or\n# called with \"no\", then set the enable_variable to \"no\" and do\n# nothing else. If called with \"yes\", then execute DEFAULT-ACTION. If\n# called with argument, set enable_variable to \"yes\" and execute\n# ARG-ACTION. Additionally, execute ACTION-ENABLED if we ended up with\n# \"yes\" either way, else ACTION-DISABLED.\n#\n# The intent is to allow enabling a feature, and optionally pass an\n# additional piece of information.\n\nAC_DEFUN([PGAC_ARG_OPTARG],\n[PGAC_ARG([$1], [$2], [@<:@=$3@:>@], [$4], [$5], [],\n          [pgac_arg_to_variable([$1], [$2])=yes\n$6],\n          [pgac_arg_to_variable([$1], [$2])=no])\ndnl Add this code only if there's a ACTION-ENABLED or ACTION-DISABLED.\nm4_ifval([$7[]$8],\n[\nif test \"[$]pgac_arg_to_variable([$1], [$2])\" = yes; then\n  m4_default([$7], :)\nm4_ifval([$8],\n[else\n  $8\n])[]dnl\nfi\n])[]dnl\n])# PGAC_ARG_OPTARG\n"
  },
  {
    "path": "configure",
    "content": "#! /bin/sh\n# Guess values for system-dependent variables and create Makefiles.\n# Generated by GNU Autoconf 2.69 for Citus 15.0devel.\n#\n#\n# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.\n#\n#\n# This configure script is free software; the Free Software Foundation\n# gives unlimited permission to copy, distribute and modify it.\n#\n# Copyright (c) Citus Data, Inc.\n## -------------------- ##\n## M4sh Initialization. ##\n## -------------------- ##\n\n# Be more Bourne compatible\nDUALCASE=1; export DUALCASE # for MKS sh\nif test -n \"${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :\n  emulate sh\n  NULLCMD=:\n  # Pre-4.2 versions of Zsh do word splitting on ${1+\"$@\"}, which\n  # is contrary to our usage.  Disable this feature.\n  alias -g '${1+\"$@\"}'='\"$@\"'\n  setopt NO_GLOB_SUBST\nelse\n  case `(set -o) 2>/dev/null` in #(\n  *posix*) :\n    set -o posix ;; #(\n  *) :\n     ;;\nesac\nfi\n\n\nas_nl='\n'\nexport as_nl\n# Printing a long string crashes Solaris 7 /usr/bin/printf.\nas_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo\n# Prefer a ksh shell builtin over an external printf program on Solaris,\n# but without wasting forks for bash or zsh.\nif test -z \"$BASH_VERSION$ZSH_VERSION\" \\\n    && (test \"X`print -r -- $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='print -r --'\n  as_echo_n='print -rn --'\nelif (test \"X`printf %s $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='printf %s\\n'\n  as_echo_n='printf %s'\nelse\n  if test \"X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`\" = \"X-n $as_echo\"; then\n    as_echo_body='eval /usr/ucb/echo -n \"$1$as_nl\"'\n    as_echo_n='/usr/ucb/echo -n'\n  else\n    as_echo_body='eval expr \"X$1\" : \"X\\\\(.*\\\\)\"'\n    as_echo_n_body='eval\n      arg=$1;\n      case $arg in #(\n      *\"$as_nl\"*)\n\texpr \"X$arg\" : \"X\\\\(.*\\\\)$as_nl\";\n\targ=`expr \"X$arg\" : \".*$as_nl\\\\(.*\\\\)\"`;;\n      esac;\n      expr \"X$arg\" : \"X\\\\(.*\\\\)\" | tr -d \"$as_nl\"\n    '\n    export as_echo_n_body\n    as_echo_n='sh -c $as_echo_n_body as_echo'\n  fi\n  export as_echo_body\n  as_echo='sh -c $as_echo_body as_echo'\nfi\n\n# The user is always right.\nif test \"${PATH_SEPARATOR+set}\" != set; then\n  PATH_SEPARATOR=:\n  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {\n    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||\n      PATH_SEPARATOR=';'\n  }\nfi\n\n\n# IFS\n# We need space, tab and new line, in precisely that order.  Quoting is\n# there to prevent editors from complaining about space-tab.\n# (If _AS_PATH_WALK were called with IFS unset, it would disable word\n# splitting by setting IFS to empty value.)\nIFS=\" \"\"\t$as_nl\"\n\n# Find who we are.  Look in the path if we contain no directory separator.\nas_myself=\ncase $0 in #((\n  *[\\\\/]* ) as_myself=$0 ;;\n  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    test -r \"$as_dir/$0\" && as_myself=$as_dir/$0 && break\n  done\nIFS=$as_save_IFS\n\n     ;;\nesac\n# We did not find ourselves, most probably we were run as `sh COMMAND'\n# in which case we are not to be found in the path.\nif test \"x$as_myself\" = x; then\n  as_myself=$0\nfi\nif test ! -f \"$as_myself\"; then\n  $as_echo \"$as_myself: error: cannot find myself; rerun with an absolute file name\" >&2\n  exit 1\nfi\n\n# Unset variables that we do not need and which cause bugs (e.g. in\n# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the \"|| exit 1\"\n# suppresses any \"Segmentation fault\" message there.  '((' could\n# trigger a bug in pdksh 5.2.14.\nfor as_var in BASH_ENV ENV MAIL MAILPATH\ndo eval test x\\${$as_var+set} = xset \\\n  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :\ndone\nPS1='$ '\nPS2='> '\nPS4='+ '\n\n# NLS nuisances.\nLC_ALL=C\nexport LC_ALL\nLANGUAGE=C\nexport LANGUAGE\n\n# CDPATH.\n(unset CDPATH) >/dev/null 2>&1 && unset CDPATH\n\n# Use a proper internal environment variable to ensure we don't fall\n  # into an infinite loop, continuously re-executing ourselves.\n  if test x\"${_as_can_reexec}\" != xno && test \"x$CONFIG_SHELL\" != x; then\n    _as_can_reexec=no; export _as_can_reexec;\n    # We cannot yet assume a decent shell, so we have to provide a\n# neutralization value for shells without unset; and this also\n# works around shells that cannot unset nonexistent variables.\n# Preserve -v and -x to the replacement shell.\nBASH_ENV=/dev/null\nENV=/dev/null\n(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV\ncase $- in # ((((\n  *v*x* | *x*v* ) as_opts=-vx ;;\n  *v* ) as_opts=-v ;;\n  *x* ) as_opts=-x ;;\n  * ) as_opts= ;;\nesac\nexec $CONFIG_SHELL $as_opts \"$as_myself\" ${1+\"$@\"}\n# Admittedly, this is quite paranoid, since all the known shells bail\n# out after a failed `exec'.\n$as_echo \"$0: could not re-execute with $CONFIG_SHELL\" >&2\nas_fn_exit 255\n  fi\n  # We don't want this to propagate to other subprocesses.\n          { _as_can_reexec=; unset _as_can_reexec;}\nif test \"x$CONFIG_SHELL\" = x; then\n  as_bourne_compatible=\"if test -n \\\"\\${ZSH_VERSION+set}\\\" && (emulate sh) >/dev/null 2>&1; then :\n  emulate sh\n  NULLCMD=:\n  # Pre-4.2 versions of Zsh do word splitting on \\${1+\\\"\\$@\\\"}, which\n  # is contrary to our usage.  Disable this feature.\n  alias -g '\\${1+\\\"\\$@\\\"}'='\\\"\\$@\\\"'\n  setopt NO_GLOB_SUBST\nelse\n  case \\`(set -o) 2>/dev/null\\` in #(\n  *posix*) :\n    set -o posix ;; #(\n  *) :\n     ;;\nesac\nfi\n\"\n  as_required=\"as_fn_return () { (exit \\$1); }\nas_fn_success () { as_fn_return 0; }\nas_fn_failure () { as_fn_return 1; }\nas_fn_ret_success () { return 0; }\nas_fn_ret_failure () { return 1; }\n\nexitcode=0\nas_fn_success || { exitcode=1; echo as_fn_success failed.; }\nas_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }\nas_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }\nas_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }\nif ( set x; as_fn_ret_success y && test x = \\\"\\$1\\\" ); then :\n\nelse\n  exitcode=1; echo positional parameters were not saved.\nfi\ntest x\\$exitcode = x0 || exit 1\ntest -x / || exit 1\"\n  as_suggested=\"  as_lineno_1=\";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested\" as_lineno_1a=\\$LINENO\n  as_lineno_2=\";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested\" as_lineno_2a=\\$LINENO\n  eval 'test \\\"x\\$as_lineno_1'\\$as_run'\\\" != \\\"x\\$as_lineno_2'\\$as_run'\\\" &&\n  test \\\"x\\`expr \\$as_lineno_1'\\$as_run' + 1\\`\\\" = \\\"x\\$as_lineno_2'\\$as_run'\\\"' || exit 1\ntest \\$(( 1 + 1 )) = 2 || exit 1\"\n  if (eval \"$as_required\") 2>/dev/null; then :\n  as_have_required=yes\nelse\n  as_have_required=no\nfi\n  if test x$as_have_required = xyes && (eval \"$as_suggested\") 2>/dev/null; then :\n\nelse\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nas_found=false\nfor as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n  as_found=:\n  case $as_dir in #(\n\t /*)\n\t   for as_base in sh bash ksh sh5; do\n\t     # Try only shells that exist, to save several forks.\n\t     as_shell=$as_dir/$as_base\n\t     if { test -f \"$as_shell\" || test -f \"$as_shell.exe\"; } &&\n\t\t    { $as_echo \"$as_bourne_compatible\"\"$as_required\" | as_run=a \"$as_shell\"; } 2>/dev/null; then :\n  CONFIG_SHELL=$as_shell as_have_required=yes\n\t\t   if { $as_echo \"$as_bourne_compatible\"\"$as_suggested\" | as_run=a \"$as_shell\"; } 2>/dev/null; then :\n  break 2\nfi\nfi\n\t   done;;\n       esac\n  as_found=false\ndone\n$as_found || { if { test -f \"$SHELL\" || test -f \"$SHELL.exe\"; } &&\n\t      { $as_echo \"$as_bourne_compatible\"\"$as_required\" | as_run=a \"$SHELL\"; } 2>/dev/null; then :\n  CONFIG_SHELL=$SHELL as_have_required=yes\nfi; }\nIFS=$as_save_IFS\n\n\n      if test \"x$CONFIG_SHELL\" != x; then :\n  export CONFIG_SHELL\n             # We cannot yet assume a decent shell, so we have to provide a\n# neutralization value for shells without unset; and this also\n# works around shells that cannot unset nonexistent variables.\n# Preserve -v and -x to the replacement shell.\nBASH_ENV=/dev/null\nENV=/dev/null\n(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV\ncase $- in # ((((\n  *v*x* | *x*v* ) as_opts=-vx ;;\n  *v* ) as_opts=-v ;;\n  *x* ) as_opts=-x ;;\n  * ) as_opts= ;;\nesac\nexec $CONFIG_SHELL $as_opts \"$as_myself\" ${1+\"$@\"}\n# Admittedly, this is quite paranoid, since all the known shells bail\n# out after a failed `exec'.\n$as_echo \"$0: could not re-execute with $CONFIG_SHELL\" >&2\nexit 255\nfi\n\n    if test x$as_have_required = xno; then :\n  $as_echo \"$0: This script requires a shell more modern than all\"\n  $as_echo \"$0: the shells that I found on your system.\"\n  if test x${ZSH_VERSION+set} = xset ; then\n    $as_echo \"$0: In particular, zsh $ZSH_VERSION has bugs and should\"\n    $as_echo \"$0: be upgraded to zsh 4.3.4 or later.\"\n  else\n    $as_echo \"$0: Please tell bug-autoconf@gnu.org about your system,\n$0: including any error possibly output before this\n$0: message. Then install a modern shell, or manually run\n$0: the script under such a shell if you do have one.\"\n  fi\n  exit 1\nfi\nfi\nfi\nSHELL=${CONFIG_SHELL-/bin/sh}\nexport SHELL\n# Unset more variables known to interfere with behavior of common tools.\nCLICOLOR_FORCE= GREP_OPTIONS=\nunset CLICOLOR_FORCE GREP_OPTIONS\n\n## --------------------- ##\n## M4sh Shell Functions. ##\n## --------------------- ##\n# as_fn_unset VAR\n# ---------------\n# Portably unset VAR.\nas_fn_unset ()\n{\n  { eval $1=; unset $1;}\n}\nas_unset=as_fn_unset\n\n# as_fn_set_status STATUS\n# -----------------------\n# Set $? to STATUS, without forking.\nas_fn_set_status ()\n{\n  return $1\n} # as_fn_set_status\n\n# as_fn_exit STATUS\n# -----------------\n# Exit the shell with STATUS, even in a \"trap 0\" or \"set -e\" context.\nas_fn_exit ()\n{\n  set +e\n  as_fn_set_status $1\n  exit $1\n} # as_fn_exit\n\n# as_fn_mkdir_p\n# -------------\n# Create \"$as_dir\" as a directory, including parents if necessary.\nas_fn_mkdir_p ()\n{\n\n  case $as_dir in #(\n  -*) as_dir=./$as_dir;;\n  esac\n  test -d \"$as_dir\" || eval $as_mkdir_p || {\n    as_dirs=\n    while :; do\n      case $as_dir in #(\n      *\\'*) as_qdir=`$as_echo \"$as_dir\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"`;; #'(\n      *) as_qdir=$as_dir;;\n      esac\n      as_dirs=\"'$as_qdir' $as_dirs\"\n      as_dir=`$as_dirname -- \"$as_dir\" ||\n$as_expr X\"$as_dir\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)$' \\| \\\n\t X\"$as_dir\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$as_dir\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n      test -d \"$as_dir\" && break\n    done\n    test -z \"$as_dirs\" || eval \"mkdir $as_dirs\"\n  } || test -d \"$as_dir\" || as_fn_error $? \"cannot create directory $as_dir\"\n\n\n} # as_fn_mkdir_p\n\n# as_fn_executable_p FILE\n# -----------------------\n# Test if FILE is an executable regular file.\nas_fn_executable_p ()\n{\n  test -f \"$1\" && test -x \"$1\"\n} # as_fn_executable_p\n# as_fn_append VAR VALUE\n# ----------------------\n# Append the text in VALUE to the end of the definition contained in VAR. Take\n# advantage of any shell optimizations that allow amortized linear growth over\n# repeated appends, instead of the typical quadratic growth present in naive\n# implementations.\nif (eval \"as_var=1; as_var+=2; test x\\$as_var = x12\") 2>/dev/null; then :\n  eval 'as_fn_append ()\n  {\n    eval $1+=\\$2\n  }'\nelse\n  as_fn_append ()\n  {\n    eval $1=\\$$1\\$2\n  }\nfi # as_fn_append\n\n# as_fn_arith ARG...\n# ------------------\n# Perform arithmetic evaluation on the ARGs, and store the result in the\n# global $as_val. Take advantage of shells that can avoid forks. The arguments\n# must be portable across $(()) and expr.\nif (eval \"test \\$(( 1 + 1 )) = 2\") 2>/dev/null; then :\n  eval 'as_fn_arith ()\n  {\n    as_val=$(( $* ))\n  }'\nelse\n  as_fn_arith ()\n  {\n    as_val=`expr \"$@\" || test $? -eq 1`\n  }\nfi # as_fn_arith\n\n\n# as_fn_error STATUS ERROR [LINENO LOG_FD]\n# ----------------------------------------\n# Output \"`basename $0`: error: ERROR\" to stderr. If LINENO and LOG_FD are\n# provided, also output the error to LOG_FD, referencing LINENO. Then exit the\n# script with STATUS, using 1 if that was 0.\nas_fn_error ()\n{\n  as_status=$1; test $as_status -eq 0 && as_status=1\n  if test \"$4\"; then\n    as_lineno=${as_lineno-\"$3\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n    $as_echo \"$as_me:${as_lineno-$LINENO}: error: $2\" >&$4\n  fi\n  $as_echo \"$as_me: error: $2\" >&2\n  as_fn_exit $as_status\n} # as_fn_error\n\nif expr a : '\\(a\\)' >/dev/null 2>&1 &&\n   test \"X`expr 00001 : '.*\\(...\\)'`\" = X001; then\n  as_expr=expr\nelse\n  as_expr=false\nfi\n\nif (basename -- /) >/dev/null 2>&1 && test \"X`basename -- / 2>&1`\" = \"X/\"; then\n  as_basename=basename\nelse\n  as_basename=false\nfi\n\nif (as_dir=`dirname -- /` && test \"X$as_dir\" = X/) >/dev/null 2>&1; then\n  as_dirname=dirname\nelse\n  as_dirname=false\nfi\n\nas_me=`$as_basename -- \"$0\" ||\n$as_expr X/\"$0\" : '.*/\\([^/][^/]*\\)/*$' \\| \\\n\t X\"$0\" : 'X\\(//\\)$' \\| \\\n\t X\"$0\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X/\"$0\" |\n    sed '/^.*\\/\\([^/][^/]*\\)\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n\n# Avoid depending upon Character Ranges.\nas_cr_letters='abcdefghijklmnopqrstuvwxyz'\nas_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'\nas_cr_Letters=$as_cr_letters$as_cr_LETTERS\nas_cr_digits='0123456789'\nas_cr_alnum=$as_cr_Letters$as_cr_digits\n\n\n  as_lineno_1=$LINENO as_lineno_1a=$LINENO\n  as_lineno_2=$LINENO as_lineno_2a=$LINENO\n  eval 'test \"x$as_lineno_1'$as_run'\" != \"x$as_lineno_2'$as_run'\" &&\n  test \"x`expr $as_lineno_1'$as_run' + 1`\" = \"x$as_lineno_2'$as_run'\"' || {\n  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)\n  sed -n '\n    p\n    /[$]LINENO/=\n  ' <$as_myself |\n    sed '\n      s/[$]LINENO.*/&-/\n      t lineno\n      b\n      :lineno\n      N\n      :loop\n      s/[$]LINENO\\([^'$as_cr_alnum'_].*\\n\\)\\(.*\\)/\\2\\1\\2/\n      t loop\n      s/-\\n.*//\n    ' >$as_me.lineno &&\n  chmod +x \"$as_me.lineno\" ||\n    { $as_echo \"$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell\" >&2; as_fn_exit 1; }\n\n  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have\n  # already done that, so ensure we don't try to do so again and fall\n  # in an infinite loop.  This has already happened in practice.\n  _as_can_reexec=no; export _as_can_reexec\n  # Don't try to exec as it changes $[0], causing all sort of problems\n  # (the dirname of $[0] is not the place where we might find the\n  # original and so on.  Autoconf is especially sensitive to this).\n  . \"./$as_me.lineno\"\n  # Exit status is that of the last command.\n  exit\n}\n\nECHO_C= ECHO_N= ECHO_T=\ncase `echo -n x` in #(((((\n-n*)\n  case `echo 'xy\\c'` in\n  *c*) ECHO_T='\t';;\t# ECHO_T is single tab character.\n  xy)  ECHO_C='\\c';;\n  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null\n       ECHO_T='\t';;\n  esac;;\n*)\n  ECHO_N='-n';;\nesac\n\nrm -f conf$$ conf$$.exe conf$$.file\nif test -d conf$$.dir; then\n  rm -f conf$$.dir/conf$$.file\nelse\n  rm -f conf$$.dir\n  mkdir conf$$.dir 2>/dev/null\nfi\nif (echo >conf$$.file) 2>/dev/null; then\n  if ln -s conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s='ln -s'\n    # ... but there are two gotchas:\n    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.\n    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.\n    # In both cases, we have to default to `cp -pR'.\n    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||\n      as_ln_s='cp -pR'\n  elif ln conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s=ln\n  else\n    as_ln_s='cp -pR'\n  fi\nelse\n  as_ln_s='cp -pR'\nfi\nrm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file\nrmdir conf$$.dir 2>/dev/null\n\nif mkdir -p . 2>/dev/null; then\n  as_mkdir_p='mkdir -p \"$as_dir\"'\nelse\n  test -d ./-p && rmdir ./-p\n  as_mkdir_p=false\nfi\n\nas_test_x='test -x'\nas_executable_p=as_fn_executable_p\n\n# Sed expression to map a string onto a valid CPP name.\nas_tr_cpp=\"eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'\"\n\n# Sed expression to map a string onto a valid variable name.\nas_tr_sh=\"eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'\"\n\n\ntest -n \"$DJDIR\" || exec 7<&0 </dev/null\nexec 6>&1\n\n# Name of the host.\n# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,\n# so uname gets run too.\nac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`\n\n#\n# Initializations.\n#\nac_default_prefix=/usr/local\nac_clean_files=\nac_config_libobj_dir=.\nLIBOBJS=\ncross_compiling=no\nsubdirs=\nMFLAGS=\nMAKEFLAGS=\n\n# Identity of this package.\nPACKAGE_NAME='Citus'\nPACKAGE_TARNAME='citus'\nPACKAGE_VERSION='15.0devel'\nPACKAGE_STRING='Citus 15.0devel'\nPACKAGE_BUGREPORT=''\nPACKAGE_URL=''\n\n# Factoring default headers for most tests.\nac_includes_default=\"\\\n#include <stdio.h>\n#ifdef HAVE_SYS_TYPES_H\n# include <sys/types.h>\n#endif\n#ifdef HAVE_SYS_STAT_H\n# include <sys/stat.h>\n#endif\n#ifdef STDC_HEADERS\n# include <stdlib.h>\n# include <stddef.h>\n#else\n# ifdef HAVE_STDLIB_H\n#  include <stdlib.h>\n# endif\n#endif\n#ifdef HAVE_STRING_H\n# if !defined STDC_HEADERS && defined HAVE_MEMORY_H\n#  include <memory.h>\n# endif\n# include <string.h>\n#endif\n#ifdef HAVE_STRINGS_H\n# include <strings.h>\n#endif\n#ifdef HAVE_INTTYPES_H\n# include <inttypes.h>\n#endif\n#ifdef HAVE_STDINT_H\n# include <stdint.h>\n#endif\n#ifdef HAVE_UNISTD_H\n# include <unistd.h>\n#endif\"\n\nac_subst_vars='LTLIBOBJS\nLIBOBJS\nHAS_DOTGIT\nPOSTGRES_BUILDDIR\nPOSTGRES_SRCDIR\nCITUS_LDFLAGS\nCITUS_CPPFLAGS\nCITUS_BITCODE_CFLAGS\nCITUS_CFLAGS\nGIT_BIN\nwith_security_flags\nwith_zstd\nwith_lz4\nEGREP\nGREP\nCPP\nOBJEXT\nEXEEXT\nac_ct_CC\nCPPFLAGS\nLDFLAGS\nCFLAGS\nCC\nvpath_build\nwith_pg_version_check\nPATH\nPG_CONFIG\nFLEX\nAWK\nSED\ntarget_alias\nhost_alias\nbuild_alias\nLIBS\nECHO_T\nECHO_N\nECHO_C\nDEFS\nmandir\nlocaledir\nlibdir\npsdir\npdfdir\ndvidir\nhtmldir\ninfodir\ndocdir\noldincludedir\nincludedir\nlocalstatedir\nsharedstatedir\nsysconfdir\ndatadir\ndatarootdir\nlibexecdir\nsbindir\nbindir\nprogram_transform_name\nprefix\nexec_prefix\nPACKAGE_URL\nPACKAGE_BUGREPORT\nPACKAGE_STRING\nPACKAGE_VERSION\nPACKAGE_TARNAME\nPACKAGE_NAME\nPATH_SEPARATOR\nSHELL'\nac_subst_files=''\nac_user_opts='\nenable_option_checking\nwith_extra_version\nwith_pg_version_check\nenable_coverage\nwith_lz4\nwith_zstd\nwith_security_flags\n'\n      ac_precious_vars='build_alias\nhost_alias\ntarget_alias\nPG_CONFIG\nPATH\nCC\nCFLAGS\nLDFLAGS\nLIBS\nCPPFLAGS\nCPP'\n\n\n# Initialize some variables set by options.\nac_init_help=\nac_init_version=false\nac_unrecognized_opts=\nac_unrecognized_sep=\n# The variables have the same names as the options, with\n# dashes changed to underlines.\ncache_file=/dev/null\nexec_prefix=NONE\nno_create=\nno_recursion=\nprefix=NONE\nprogram_prefix=NONE\nprogram_suffix=NONE\nprogram_transform_name=s,x,x,\nsilent=\nsite=\nsrcdir=\nverbose=\nx_includes=NONE\nx_libraries=NONE\n\n# Installation directory options.\n# These are left unexpanded so users can \"make install exec_prefix=/foo\"\n# and all the variables that are supposed to be based on exec_prefix\n# by default will actually change.\n# Use braces instead of parens because sh, perl, etc. also accept them.\n# (The list follows the same order as the GNU Coding Standards.)\nbindir='${exec_prefix}/bin'\nsbindir='${exec_prefix}/sbin'\nlibexecdir='${exec_prefix}/libexec'\ndatarootdir='${prefix}/share'\ndatadir='${datarootdir}'\nsysconfdir='${prefix}/etc'\nsharedstatedir='${prefix}/com'\nlocalstatedir='${prefix}/var'\nincludedir='${prefix}/include'\noldincludedir='/usr/include'\ndocdir='${datarootdir}/doc/${PACKAGE_TARNAME}'\ninfodir='${datarootdir}/info'\nhtmldir='${docdir}'\ndvidir='${docdir}'\npdfdir='${docdir}'\npsdir='${docdir}'\nlibdir='${exec_prefix}/lib'\nlocaledir='${datarootdir}/locale'\nmandir='${datarootdir}/man'\n\nac_prev=\nac_dashdash=\nfor ac_option\ndo\n  # If the previous option needs an argument, assign it.\n  if test -n \"$ac_prev\"; then\n    eval $ac_prev=\\$ac_option\n    ac_prev=\n    continue\n  fi\n\n  case $ac_option in\n  *=?*) ac_optarg=`expr \"X$ac_option\" : '[^=]*=\\(.*\\)'` ;;\n  *=)   ac_optarg= ;;\n  *)    ac_optarg=yes ;;\n  esac\n\n  # Accept the important Cygnus configure options, so we can diagnose typos.\n\n  case $ac_dashdash$ac_option in\n  --)\n    ac_dashdash=yes ;;\n\n  -bindir | --bindir | --bindi | --bind | --bin | --bi)\n    ac_prev=bindir ;;\n  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)\n    bindir=$ac_optarg ;;\n\n  -build | --build | --buil | --bui | --bu)\n    ac_prev=build_alias ;;\n  -build=* | --build=* | --buil=* | --bui=* | --bu=*)\n    build_alias=$ac_optarg ;;\n\n  -cache-file | --cache-file | --cache-fil | --cache-fi \\\n  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)\n    ac_prev=cache_file ;;\n  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \\\n  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)\n    cache_file=$ac_optarg ;;\n\n  --config-cache | -C)\n    cache_file=config.cache ;;\n\n  -datadir | --datadir | --datadi | --datad)\n    ac_prev=datadir ;;\n  -datadir=* | --datadir=* | --datadi=* | --datad=*)\n    datadir=$ac_optarg ;;\n\n  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \\\n  | --dataroo | --dataro | --datar)\n    ac_prev=datarootdir ;;\n  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \\\n  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)\n    datarootdir=$ac_optarg ;;\n\n  -disable-* | --disable-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*disable-\\(.*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid feature name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"enable_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval enable_$ac_useropt=no ;;\n\n  -docdir | --docdir | --docdi | --doc | --do)\n    ac_prev=docdir ;;\n  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)\n    docdir=$ac_optarg ;;\n\n  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)\n    ac_prev=dvidir ;;\n  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)\n    dvidir=$ac_optarg ;;\n\n  -enable-* | --enable-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*enable-\\([^=]*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid feature name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"enable_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval enable_$ac_useropt=\\$ac_optarg ;;\n\n  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \\\n  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \\\n  | --exec | --exe | --ex)\n    ac_prev=exec_prefix ;;\n  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \\\n  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \\\n  | --exec=* | --exe=* | --ex=*)\n    exec_prefix=$ac_optarg ;;\n\n  -gas | --gas | --ga | --g)\n    # Obsolete; use --with-gas.\n    with_gas=yes ;;\n\n  -help | --help | --hel | --he | -h)\n    ac_init_help=long ;;\n  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)\n    ac_init_help=recursive ;;\n  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)\n    ac_init_help=short ;;\n\n  -host | --host | --hos | --ho)\n    ac_prev=host_alias ;;\n  -host=* | --host=* | --hos=* | --ho=*)\n    host_alias=$ac_optarg ;;\n\n  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)\n    ac_prev=htmldir ;;\n  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \\\n  | --ht=*)\n    htmldir=$ac_optarg ;;\n\n  -includedir | --includedir | --includedi | --included | --include \\\n  | --includ | --inclu | --incl | --inc)\n    ac_prev=includedir ;;\n  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \\\n  | --includ=* | --inclu=* | --incl=* | --inc=*)\n    includedir=$ac_optarg ;;\n\n  -infodir | --infodir | --infodi | --infod | --info | --inf)\n    ac_prev=infodir ;;\n  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)\n    infodir=$ac_optarg ;;\n\n  -libdir | --libdir | --libdi | --libd)\n    ac_prev=libdir ;;\n  -libdir=* | --libdir=* | --libdi=* | --libd=*)\n    libdir=$ac_optarg ;;\n\n  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \\\n  | --libexe | --libex | --libe)\n    ac_prev=libexecdir ;;\n  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \\\n  | --libexe=* | --libex=* | --libe=*)\n    libexecdir=$ac_optarg ;;\n\n  -localedir | --localedir | --localedi | --localed | --locale)\n    ac_prev=localedir ;;\n  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)\n    localedir=$ac_optarg ;;\n\n  -localstatedir | --localstatedir | --localstatedi | --localstated \\\n  | --localstate | --localstat | --localsta | --localst | --locals)\n    ac_prev=localstatedir ;;\n  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \\\n  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)\n    localstatedir=$ac_optarg ;;\n\n  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)\n    ac_prev=mandir ;;\n  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)\n    mandir=$ac_optarg ;;\n\n  -nfp | --nfp | --nf)\n    # Obsolete; use --without-fp.\n    with_fp=no ;;\n\n  -no-create | --no-create | --no-creat | --no-crea | --no-cre \\\n  | --no-cr | --no-c | -n)\n    no_create=yes ;;\n\n  -no-recursion | --no-recursion | --no-recursio | --no-recursi \\\n  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)\n    no_recursion=yes ;;\n\n  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \\\n  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \\\n  | --oldin | --oldi | --old | --ol | --o)\n    ac_prev=oldincludedir ;;\n  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \\\n  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \\\n  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)\n    oldincludedir=$ac_optarg ;;\n\n  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)\n    ac_prev=prefix ;;\n  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)\n    prefix=$ac_optarg ;;\n\n  -program-prefix | --program-prefix | --program-prefi | --program-pref \\\n  | --program-pre | --program-pr | --program-p)\n    ac_prev=program_prefix ;;\n  -program-prefix=* | --program-prefix=* | --program-prefi=* \\\n  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)\n    program_prefix=$ac_optarg ;;\n\n  -program-suffix | --program-suffix | --program-suffi | --program-suff \\\n  | --program-suf | --program-su | --program-s)\n    ac_prev=program_suffix ;;\n  -program-suffix=* | --program-suffix=* | --program-suffi=* \\\n  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)\n    program_suffix=$ac_optarg ;;\n\n  -program-transform-name | --program-transform-name \\\n  | --program-transform-nam | --program-transform-na \\\n  | --program-transform-n | --program-transform- \\\n  | --program-transform | --program-transfor \\\n  | --program-transfo | --program-transf \\\n  | --program-trans | --program-tran \\\n  | --progr-tra | --program-tr | --program-t)\n    ac_prev=program_transform_name ;;\n  -program-transform-name=* | --program-transform-name=* \\\n  | --program-transform-nam=* | --program-transform-na=* \\\n  | --program-transform-n=* | --program-transform-=* \\\n  | --program-transform=* | --program-transfor=* \\\n  | --program-transfo=* | --program-transf=* \\\n  | --program-trans=* | --program-tran=* \\\n  | --progr-tra=* | --program-tr=* | --program-t=*)\n    program_transform_name=$ac_optarg ;;\n\n  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)\n    ac_prev=pdfdir ;;\n  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)\n    pdfdir=$ac_optarg ;;\n\n  -psdir | --psdir | --psdi | --psd | --ps)\n    ac_prev=psdir ;;\n  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)\n    psdir=$ac_optarg ;;\n\n  -q | -quiet | --quiet | --quie | --qui | --qu | --q \\\n  | -silent | --silent | --silen | --sile | --sil)\n    silent=yes ;;\n\n  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)\n    ac_prev=sbindir ;;\n  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \\\n  | --sbi=* | --sb=*)\n    sbindir=$ac_optarg ;;\n\n  -sharedstatedir | --sharedstatedir | --sharedstatedi \\\n  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \\\n  | --sharedst | --shareds | --shared | --share | --shar \\\n  | --sha | --sh)\n    ac_prev=sharedstatedir ;;\n  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \\\n  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \\\n  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \\\n  | --sha=* | --sh=*)\n    sharedstatedir=$ac_optarg ;;\n\n  -site | --site | --sit)\n    ac_prev=site ;;\n  -site=* | --site=* | --sit=*)\n    site=$ac_optarg ;;\n\n  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)\n    ac_prev=srcdir ;;\n  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)\n    srcdir=$ac_optarg ;;\n\n  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \\\n  | --syscon | --sysco | --sysc | --sys | --sy)\n    ac_prev=sysconfdir ;;\n  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \\\n  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)\n    sysconfdir=$ac_optarg ;;\n\n  -target | --target | --targe | --targ | --tar | --ta | --t)\n    ac_prev=target_alias ;;\n  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)\n    target_alias=$ac_optarg ;;\n\n  -v | -verbose | --verbose | --verbos | --verbo | --verb)\n    verbose=yes ;;\n\n  -version | --version | --versio | --versi | --vers | -V)\n    ac_init_version=: ;;\n\n  -with-* | --with-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*with-\\([^=]*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid package name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"with_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval with_$ac_useropt=\\$ac_optarg ;;\n\n  -without-* | --without-*)\n    ac_useropt=`expr \"x$ac_option\" : 'x-*without-\\(.*\\)'`\n    # Reject names that are not valid shell variable names.\n    expr \"x$ac_useropt\" : \".*[^-+._$as_cr_alnum]\" >/dev/null &&\n      as_fn_error $? \"invalid package name: $ac_useropt\"\n    ac_useropt_orig=$ac_useropt\n    ac_useropt=`$as_echo \"$ac_useropt\" | sed 's/[-+.]/_/g'`\n    case $ac_user_opts in\n      *\"\n\"with_$ac_useropt\"\n\"*) ;;\n      *) ac_unrecognized_opts=\"$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig\"\n\t ac_unrecognized_sep=', ';;\n    esac\n    eval with_$ac_useropt=no ;;\n\n  --x)\n    # Obsolete; use --with-x.\n    with_x=yes ;;\n\n  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \\\n  | --x-incl | --x-inc | --x-in | --x-i)\n    ac_prev=x_includes ;;\n  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \\\n  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)\n    x_includes=$ac_optarg ;;\n\n  -x-libraries | --x-libraries | --x-librarie | --x-librari \\\n  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)\n    ac_prev=x_libraries ;;\n  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \\\n  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)\n    x_libraries=$ac_optarg ;;\n\n  -*) as_fn_error $? \"unrecognized option: \\`$ac_option'\nTry \\`$0 --help' for more information\"\n    ;;\n\n  *=*)\n    ac_envvar=`expr \"x$ac_option\" : 'x\\([^=]*\\)='`\n    # Reject names that are not valid shell variable names.\n    case $ac_envvar in #(\n      '' | [0-9]* | *[!_$as_cr_alnum]* )\n      as_fn_error $? \"invalid variable name: \\`$ac_envvar'\" ;;\n    esac\n    eval $ac_envvar=\\$ac_optarg\n    export $ac_envvar ;;\n\n  *)\n    # FIXME: should be removed in autoconf 3.0.\n    $as_echo \"$as_me: WARNING: you should use --build, --host, --target\" >&2\n    expr \"x$ac_option\" : \".*[^-._$as_cr_alnum]\" >/dev/null &&\n      $as_echo \"$as_me: WARNING: invalid host type: $ac_option\" >&2\n    : \"${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}\"\n    ;;\n\n  esac\ndone\n\nif test -n \"$ac_prev\"; then\n  ac_option=--`echo $ac_prev | sed 's/_/-/g'`\n  as_fn_error $? \"missing argument to $ac_option\"\nfi\n\nif test -n \"$ac_unrecognized_opts\"; then\n  case $enable_option_checking in\n    no) ;;\n    fatal) as_fn_error $? \"unrecognized options: $ac_unrecognized_opts\" ;;\n    *)     $as_echo \"$as_me: WARNING: unrecognized options: $ac_unrecognized_opts\" >&2 ;;\n  esac\nfi\n\n# Check all directory arguments for consistency.\nfor ac_var in\texec_prefix prefix bindir sbindir libexecdir datarootdir \\\n\t\tdatadir sysconfdir sharedstatedir localstatedir includedir \\\n\t\toldincludedir docdir infodir htmldir dvidir pdfdir psdir \\\n\t\tlibdir localedir mandir\ndo\n  eval ac_val=\\$$ac_var\n  # Remove trailing slashes.\n  case $ac_val in\n    */ )\n      ac_val=`expr \"X$ac_val\" : 'X\\(.*[^/]\\)' \\| \"X$ac_val\" : 'X\\(.*\\)'`\n      eval $ac_var=\\$ac_val;;\n  esac\n  # Be sure to have absolute directory names.\n  case $ac_val in\n    [\\\\/$]* | ?:[\\\\/]* )  continue;;\n    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;\n  esac\n  as_fn_error $? \"expected an absolute directory name for --$ac_var: $ac_val\"\ndone\n\n# There might be people who depend on the old broken behavior: `$host'\n# used to hold the argument of --host etc.\n# FIXME: To remove some day.\nbuild=$build_alias\nhost=$host_alias\ntarget=$target_alias\n\n# FIXME: To remove some day.\nif test \"x$host_alias\" != x; then\n  if test \"x$build_alias\" = x; then\n    cross_compiling=maybe\n  elif test \"x$build_alias\" != \"x$host_alias\"; then\n    cross_compiling=yes\n  fi\nfi\n\nac_tool_prefix=\ntest -n \"$host_alias\" && ac_tool_prefix=$host_alias-\n\ntest \"$silent\" = yes && exec 6>/dev/null\n\n\nac_pwd=`pwd` && test -n \"$ac_pwd\" &&\nac_ls_di=`ls -di .` &&\nac_pwd_ls_di=`cd \"$ac_pwd\" && ls -di .` ||\n  as_fn_error $? \"working directory cannot be determined\"\ntest \"X$ac_ls_di\" = \"X$ac_pwd_ls_di\" ||\n  as_fn_error $? \"pwd does not report name of working directory\"\n\n\n# Find the source files, if location was not specified.\nif test -z \"$srcdir\"; then\n  ac_srcdir_defaulted=yes\n  # Try the directory containing this script, then the parent directory.\n  ac_confdir=`$as_dirname -- \"$as_myself\" ||\n$as_expr X\"$as_myself\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$as_myself\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$as_myself\" : 'X\\(//\\)$' \\| \\\n\t X\"$as_myself\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$as_myself\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n  srcdir=$ac_confdir\n  if test ! -r \"$srcdir/$ac_unique_file\"; then\n    srcdir=..\n  fi\nelse\n  ac_srcdir_defaulted=no\nfi\nif test ! -r \"$srcdir/$ac_unique_file\"; then\n  test \"$ac_srcdir_defaulted\" = yes && srcdir=\"$ac_confdir or ..\"\n  as_fn_error $? \"cannot find sources ($ac_unique_file) in $srcdir\"\nfi\nac_msg=\"sources are in $srcdir, but \\`cd $srcdir' does not work\"\nac_abs_confdir=`(\n\tcd \"$srcdir\" && test -r \"./$ac_unique_file\" || as_fn_error $? \"$ac_msg\"\n\tpwd)`\n# When building in place, set srcdir=.\nif test \"$ac_abs_confdir\" = \"$ac_pwd\"; then\n  srcdir=.\nfi\n# Remove unnecessary trailing slashes from srcdir.\n# Double slashes in file names in object file debugging info\n# mess up M-x gdb in Emacs.\ncase $srcdir in\n*/) srcdir=`expr \"X$srcdir\" : 'X\\(.*[^/]\\)' \\| \"X$srcdir\" : 'X\\(.*\\)'`;;\nesac\nfor ac_var in $ac_precious_vars; do\n  eval ac_env_${ac_var}_set=\\${${ac_var}+set}\n  eval ac_env_${ac_var}_value=\\$${ac_var}\n  eval ac_cv_env_${ac_var}_set=\\${${ac_var}+set}\n  eval ac_cv_env_${ac_var}_value=\\$${ac_var}\ndone\n\n#\n# Report the --help message.\n#\nif test \"$ac_init_help\" = \"long\"; then\n  # Omit some internal or obsolete options to make the list less imposing.\n  # This message is too long to be a string in the A/UX 3.1 sh.\n  cat <<_ACEOF\n\\`configure' configures Citus 15.0devel to adapt to many kinds of systems.\n\nUsage: $0 [OPTION]... [VAR=VALUE]...\n\nTo assign environment variables (e.g., CC, CFLAGS...), specify them as\nVAR=VALUE.  See below for descriptions of some of the useful variables.\n\nDefaults for the options are specified in brackets.\n\nConfiguration:\n  -h, --help              display this help and exit\n      --help=short        display options specific to this package\n      --help=recursive    display the short help of all the included packages\n  -V, --version           display version information and exit\n  -q, --quiet, --silent   do not print \\`checking ...' messages\n      --cache-file=FILE   cache test results in FILE [disabled]\n  -C, --config-cache      alias for \\`--cache-file=config.cache'\n  -n, --no-create         do not create output files\n      --srcdir=DIR        find the sources in DIR [configure dir or \\`..']\n\nInstallation directories:\n  --prefix=PREFIX         install architecture-independent files in PREFIX\n                          [$ac_default_prefix]\n  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX\n                          [PREFIX]\n\nBy default, \\`make install' will install all the files in\n\\`$ac_default_prefix/bin', \\`$ac_default_prefix/lib' etc.  You can specify\nan installation prefix other than \\`$ac_default_prefix' using \\`--prefix',\nfor instance \\`--prefix=\\$HOME'.\n\nFor better control, use the options below.\n\nFine tuning of the installation directories:\n  --bindir=DIR            user executables [EPREFIX/bin]\n  --sbindir=DIR           system admin executables [EPREFIX/sbin]\n  --libexecdir=DIR        program executables [EPREFIX/libexec]\n  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]\n  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]\n  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]\n  --libdir=DIR            object code libraries [EPREFIX/lib]\n  --includedir=DIR        C header files [PREFIX/include]\n  --oldincludedir=DIR     C header files for non-gcc [/usr/include]\n  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]\n  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]\n  --infodir=DIR           info documentation [DATAROOTDIR/info]\n  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]\n  --mandir=DIR            man documentation [DATAROOTDIR/man]\n  --docdir=DIR            documentation root [DATAROOTDIR/doc/citus]\n  --htmldir=DIR           html documentation [DOCDIR]\n  --dvidir=DIR            dvi documentation [DOCDIR]\n  --pdfdir=DIR            pdf documentation [DOCDIR]\n  --psdir=DIR             ps documentation [DOCDIR]\n_ACEOF\n\n  cat <<\\_ACEOF\n_ACEOF\nfi\n\nif test -n \"$ac_init_help\"; then\n  case $ac_init_help in\n     short | recursive ) echo \"Configuration of Citus 15.0devel:\";;\n   esac\n  cat <<\\_ACEOF\n\nOptional Features:\n  --disable-option-checking  ignore unrecognized --enable/--with options\n  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)\n  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]\n  --enable-coverage       build with coverage testing instrumentation\n\nOptional Packages:\n  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]\n  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)\n  --with-extra-version=STRING\n                          append STRING to version\n  --without-pg-version-check\n                          do not check postgres version during configure\n  --without-lz4           do not use lz4\n  --without-zstd          do not use zstd\n  --with-security-flags   use security flags\n\nSome influential environment variables:\n  PG_CONFIG   Location to find pg_config for target PostgreSQL instalation\n              (default PATH)\n  PATH        PATH for target PostgreSQL install pg_config\n  CC          C compiler command\n  CFLAGS      C compiler flags\n  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a\n              nonstandard directory <lib dir>\n  LIBS        libraries to pass to the linker, e.g. -l<library>\n  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if\n              you have headers in a nonstandard directory <include dir>\n  CPP         C preprocessor\n\nUse these variables to override the choices made by `configure' or to help\nit to find libraries and programs with nonstandard names/locations.\n\nReport bugs to the package provider.\n_ACEOF\nac_status=$?\nfi\n\nif test \"$ac_init_help\" = \"recursive\"; then\n  # If there are subdirs, report their specific --help.\n  for ac_dir in : $ac_subdirs_all; do test \"x$ac_dir\" = x: && continue\n    test -d \"$ac_dir\" ||\n      { cd \"$srcdir\" && ac_pwd=`pwd` && srcdir=. && test -d \"$ac_dir\"; } ||\n      continue\n    ac_builddir=.\n\ncase \"$ac_dir\" in\n.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;\n*)\n  ac_dir_suffix=/`$as_echo \"$ac_dir\" | sed 's|^\\.[\\\\/]||'`\n  # A \"..\" for each directory in $ac_dir_suffix.\n  ac_top_builddir_sub=`$as_echo \"$ac_dir_suffix\" | sed 's|/[^\\\\/]*|/..|g;s|/||'`\n  case $ac_top_builddir_sub in\n  \"\") ac_top_builddir_sub=. ac_top_build_prefix= ;;\n  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;\n  esac ;;\nesac\nac_abs_top_builddir=$ac_pwd\nac_abs_builddir=$ac_pwd$ac_dir_suffix\n# for backward compatibility:\nac_top_builddir=$ac_top_build_prefix\n\ncase $srcdir in\n  .)  # We are building in place.\n    ac_srcdir=.\n    ac_top_srcdir=$ac_top_builddir_sub\n    ac_abs_top_srcdir=$ac_pwd ;;\n  [\\\\/]* | ?:[\\\\/]* )  # Absolute name.\n    ac_srcdir=$srcdir$ac_dir_suffix;\n    ac_top_srcdir=$srcdir\n    ac_abs_top_srcdir=$srcdir ;;\n  *) # Relative name.\n    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix\n    ac_top_srcdir=$ac_top_build_prefix$srcdir\n    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;\nesac\nac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix\n\n    cd \"$ac_dir\" || { ac_status=$?; continue; }\n    # Check for guested configure.\n    if test -f \"$ac_srcdir/configure.gnu\"; then\n      echo &&\n      $SHELL \"$ac_srcdir/configure.gnu\" --help=recursive\n    elif test -f \"$ac_srcdir/configure\"; then\n      echo &&\n      $SHELL \"$ac_srcdir/configure\" --help=recursive\n    else\n      $as_echo \"$as_me: WARNING: no configuration information is in $ac_dir\" >&2\n    fi || ac_status=$?\n    cd \"$ac_pwd\" || { ac_status=$?; break; }\n  done\nfi\n\ntest -n \"$ac_init_help\" && exit $ac_status\nif $ac_init_version; then\n  cat <<\\_ACEOF\nCitus configure 15.0devel\ngenerated by GNU Autoconf 2.69\n\nCopyright (C) 2012 Free Software Foundation, Inc.\nThis configure script is free software; the Free Software Foundation\ngives unlimited permission to copy, distribute and modify it.\n\nCopyright (c) Citus Data, Inc.\n_ACEOF\n  exit\nfi\n\n## ------------------------ ##\n## Autoconf initialization. ##\n## ------------------------ ##\n\n# ac_fn_c_try_compile LINENO\n# --------------------------\n# Try to compile conftest.$ac_ext, and return whether this succeeded.\nac_fn_c_try_compile ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  rm -f conftest.$ac_objext\n  if { { ac_try=\"$ac_compile\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compile\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && {\n\t test -z \"$ac_c_werror_flag\" ||\n\t test ! -s conftest.err\n       } && test -s conftest.$ac_objext; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n\tac_retval=1\nfi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_compile\n\n# ac_fn_c_try_run LINENO\n# ----------------------\n# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes\n# that executables *can* be run.\nac_fn_c_try_run ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'\n  { { case \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_try\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; }; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: program exited with status $ac_status\" >&5\n       $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n       ac_retval=$ac_status\nfi\n  rm -rf conftest.dSYM conftest_ipa8_conftest.oo\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_run\n\n# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES\n# --------------------------------------------\n# Tries to find the compile-time value of EXPR in a program that includes\n# INCLUDES, setting VAR accordingly. Returns whether the value could be\n# computed\nac_fn_c_compute_int ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if test \"$cross_compiling\" = yes; then\n    # Depending upon the size, compute the lo and hi bounds.\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nstatic int test_array [1 - 2 * !(($2) >= 0)];\ntest_array [0] = 0;\nreturn test_array [0];\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_lo=0 ac_mid=0\n  while :; do\n    cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nstatic int test_array [1 - 2 * !(($2) <= $ac_mid)];\ntest_array [0] = 0;\nreturn test_array [0];\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_hi=$ac_mid; break\nelse\n  as_fn_arith $ac_mid + 1 && ac_lo=$as_val\n\t\t\tif test $ac_lo -le $ac_mid; then\n\t\t\t  ac_lo= ac_hi=\n\t\t\t  break\n\t\t\tfi\n\t\t\tas_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n  done\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nstatic int test_array [1 - 2 * !(($2) < 0)];\ntest_array [0] = 0;\nreturn test_array [0];\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_hi=-1 ac_mid=-1\n  while :; do\n    cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nstatic int test_array [1 - 2 * !(($2) >= $ac_mid)];\ntest_array [0] = 0;\nreturn test_array [0];\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_lo=$ac_mid; break\nelse\n  as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val\n\t\t\tif test $ac_mid -le $ac_hi; then\n\t\t\t  ac_lo= ac_hi=\n\t\t\t  break\n\t\t\tfi\n\t\t\tas_fn_arith 2 '*' $ac_mid && ac_mid=$as_val\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n  done\nelse\n  ac_lo= ac_hi=\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n# Binary search between lo and hi bounds.\nwhile test \"x$ac_lo\" != \"x$ac_hi\"; do\n  as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nint\nmain ()\n{\nstatic int test_array [1 - 2 * !(($2) <= $ac_mid)];\ntest_array [0] = 0;\nreturn test_array [0];\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_hi=$ac_mid\nelse\n  as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\ndone\ncase $ac_lo in #((\n?*) eval \"$3=\\$ac_lo\"; ac_retval=0 ;;\n'') ac_retval=1 ;;\nesac\n  else\n    cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\nstatic long int longval () { return $2; }\nstatic unsigned long int ulongval () { return $2; }\n#include <stdio.h>\n#include <stdlib.h>\nint\nmain ()\n{\n\n  FILE *f = fopen (\"conftest.val\", \"w\");\n  if (! f)\n    return 1;\n  if (($2) < 0)\n    {\n      long int i = longval ();\n      if (i != ($2))\n\treturn 1;\n      fprintf (f, \"%ld\", i);\n    }\n  else\n    {\n      unsigned long int i = ulongval ();\n      if (i != ($2))\n\treturn 1;\n      fprintf (f, \"%lu\", i);\n    }\n  /* Do not output a trailing newline, as this causes \\r\\n confusion\n     on some platforms.  */\n  return ferror (f) || fclose (f) != 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_run \"$LINENO\"; then :\n  echo >>conftest.val; read $3 <conftest.val; ac_retval=0\nelse\n  ac_retval=1\nfi\nrm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \\\n  conftest.$ac_objext conftest.beam conftest.$ac_ext\nrm -f conftest.val\n\n  fi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_compute_int\n\n# ac_fn_c_try_cpp LINENO\n# ----------------------\n# Try to preprocess conftest.$ac_ext, and return whether this succeeded.\nac_fn_c_try_cpp ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if { { ac_try=\"$ac_cpp conftest.$ac_ext\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_cpp conftest.$ac_ext\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } > conftest.i && {\n\t test -z \"$ac_c_preproc_warn_flag$ac_c_werror_flag\" ||\n\t test ! -s conftest.err\n       }; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n    ac_retval=1\nfi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_cpp\n\n# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES\n# -------------------------------------------------------\n# Tests whether HEADER exists and can be compiled using the include files in\n# INCLUDES, setting the cache variable VAR accordingly.\nac_fn_c_check_header_compile ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\n#include <$2>\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  eval \"$3=yes\"\nelse\n  eval \"$3=no\"\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n\n} # ac_fn_c_check_header_compile\n\n# ac_fn_c_try_link LINENO\n# -----------------------\n# Try to link conftest.$ac_ext, and return whether this succeeded.\nac_fn_c_try_link ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  rm -f conftest.$ac_objext conftest$ac_exeext\n  if { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    grep -v '^ *+' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n    mv -f conftest.er1 conftest.err\n  fi\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; } && {\n\t test -z \"$ac_c_werror_flag\" ||\n\t test ! -s conftest.err\n       } && test -s conftest$ac_exeext && {\n\t test \"$cross_compiling\" = yes ||\n\t test -x conftest$ac_exeext\n       }; then :\n  ac_retval=0\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n\tac_retval=1\nfi\n  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information\n  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would\n  # interfere with the next link command; also delete a directory that is\n  # left behind by Apple's compiler.  We do this before executing the actions.\n  rm -rf conftest.dSYM conftest_ipa8_conftest.oo\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n  as_fn_set_status $ac_retval\n\n} # ac_fn_c_try_link\n\n# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES\n# -------------------------------------------------------\n# Tests whether HEADER exists, giving a warning if it cannot be compiled using\n# the include files in INCLUDES and setting the cache variable VAR\n# accordingly.\nac_fn_c_check_header_mongrel ()\n{\n  as_lineno=${as_lineno-\"$1\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n  if eval \\${$3+:} false; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\nelse\n  # Is the header compilable?\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking $2 usability\" >&5\n$as_echo_n \"checking $2 usability... \" >&6; }\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n$4\n#include <$2>\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_header_compiler=yes\nelse\n  ac_header_compiler=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler\" >&5\n$as_echo \"$ac_header_compiler\" >&6; }\n\n# Is the header present?\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking $2 presence\" >&5\n$as_echo_n \"checking $2 presence... \" >&6; }\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <$2>\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n  ac_header_preproc=yes\nelse\n  ac_header_preproc=no\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc\" >&5\n$as_echo \"$ac_header_preproc\" >&6; }\n\n# So?  What about this header?\ncase $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((\n  yes:no: )\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!\" >&5\n$as_echo \"$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result\" >&5\n$as_echo \"$as_me: WARNING: $2: proceeding with the compiler's result\" >&2;}\n    ;;\n  no:yes:* )\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled\" >&5\n$as_echo \"$as_me: WARNING: $2: present but cannot be compiled\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?\" >&5\n$as_echo \"$as_me: WARNING: $2:     check for missing prerequisite headers?\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation\" >&5\n$as_echo \"$as_me: WARNING: $2: see the Autoconf documentation\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \\\"Present But Cannot Be Compiled\\\"\" >&5\n$as_echo \"$as_me: WARNING: $2:     section \\\"Present But Cannot Be Compiled\\\"\" >&2;}\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result\" >&5\n$as_echo \"$as_me: WARNING: $2: proceeding with the compiler's result\" >&2;}\n    ;;\nesac\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $2\" >&5\n$as_echo_n \"checking for $2... \" >&6; }\nif eval \\${$3+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  eval \"$3=\\$ac_header_compiler\"\nfi\neval ac_res=\\$$3\n\t       { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_res\" >&5\n$as_echo \"$ac_res\" >&6; }\nfi\n  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno\n\n} # ac_fn_c_check_header_mongrel\ncat >config.log <<_ACEOF\nThis file contains any messages produced by compilers while\nrunning configure, to aid debugging if configure makes a mistake.\n\nIt was created by Citus $as_me 15.0devel, which was\ngenerated by GNU Autoconf 2.69.  Invocation command line was\n\n  $ $0 $@\n\n_ACEOF\nexec 5>>config.log\n{\ncat <<_ASUNAME\n## --------- ##\n## Platform. ##\n## --------- ##\n\nhostname = `(hostname || uname -n) 2>/dev/null | sed 1q`\nuname -m = `(uname -m) 2>/dev/null || echo unknown`\nuname -r = `(uname -r) 2>/dev/null || echo unknown`\nuname -s = `(uname -s) 2>/dev/null || echo unknown`\nuname -v = `(uname -v) 2>/dev/null || echo unknown`\n\n/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`\n/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`\n\n/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`\n/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`\n/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`\n/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`\n/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`\n/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`\n/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`\n\n_ASUNAME\n\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    $as_echo \"PATH: $as_dir\"\n  done\nIFS=$as_save_IFS\n\n} >&5\n\ncat >&5 <<_ACEOF\n\n\n## ----------- ##\n## Core tests. ##\n## ----------- ##\n\n_ACEOF\n\n\n# Keep a trace of the command line.\n# Strip out --no-create and --no-recursion so they do not pile up.\n# Strip out --silent because we don't want to record it for future runs.\n# Also quote any args containing shell meta-characters.\n# Make two passes to allow for proper duplicate-argument suppression.\nac_configure_args=\nac_configure_args0=\nac_configure_args1=\nac_must_keep_next=false\nfor ac_pass in 1 2\ndo\n  for ac_arg\n  do\n    case $ac_arg in\n    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;\n    -q | -quiet | --quiet | --quie | --qui | --qu | --q \\\n    | -silent | --silent | --silen | --sile | --sil)\n      continue ;;\n    *\\'*)\n      ac_arg=`$as_echo \"$ac_arg\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    esac\n    case $ac_pass in\n    1) as_fn_append ac_configure_args0 \" '$ac_arg'\" ;;\n    2)\n      as_fn_append ac_configure_args1 \" '$ac_arg'\"\n      if test $ac_must_keep_next = true; then\n\tac_must_keep_next=false # Got value, back to normal.\n      else\n\tcase $ac_arg in\n\t  *=* | --config-cache | -C | -disable-* | --disable-* \\\n\t  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \\\n\t  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \\\n\t  | -with-* | --with-* | -without-* | --without-* | --x)\n\t    case \"$ac_configure_args0 \" in\n\t      \"$ac_configure_args1\"*\" '$ac_arg' \"* ) continue ;;\n\t    esac\n\t    ;;\n\t  -* ) ac_must_keep_next=true ;;\n\tesac\n      fi\n      as_fn_append ac_configure_args \" '$ac_arg'\"\n      ;;\n    esac\n  done\ndone\n{ ac_configure_args0=; unset ac_configure_args0;}\n{ ac_configure_args1=; unset ac_configure_args1;}\n\n# When interrupted or exit'd, cleanup temporary files, and complete\n# config.log.  We remove comments because anyway the quotes in there\n# would cause problems or look ugly.\n# WARNING: Use '\\'' to represent an apostrophe within the trap.\n# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.\ntrap 'exit_status=$?\n  # Save into config.log some information that might help in debugging.\n  {\n    echo\n\n    $as_echo \"## ---------------- ##\n## Cache variables. ##\n## ---------------- ##\"\n    echo\n    # The following way of writing the cache mishandles newlines in values,\n(\n  for ac_var in `(set) 2>&1 | sed -n '\\''s/^\\([a-zA-Z_][a-zA-Z0-9_]*\\)=.*/\\1/p'\\''`; do\n    eval ac_val=\\$$ac_var\n    case $ac_val in #(\n    *${as_nl}*)\n      case $ac_var in #(\n      *_cv_*) { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline\" >&5\n$as_echo \"$as_me: WARNING: cache variable $ac_var contains a newline\" >&2;} ;;\n      esac\n      case $ac_var in #(\n      _ | IFS | as_nl) ;; #(\n      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(\n      *) { eval $ac_var=; unset $ac_var;} ;;\n      esac ;;\n    esac\n  done\n  (set) 2>&1 |\n    case $as_nl`(ac_space='\\'' '\\''; set) 2>&1` in #(\n    *${as_nl}ac_space=\\ *)\n      sed -n \\\n\t\"s/'\\''/'\\''\\\\\\\\'\\'''\\''/g;\n\t  s/^\\\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\\\)=\\\\(.*\\\\)/\\\\1='\\''\\\\2'\\''/p\"\n      ;; #(\n    *)\n      sed -n \"/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p\"\n      ;;\n    esac |\n    sort\n)\n    echo\n\n    $as_echo \"## ----------------- ##\n## Output variables. ##\n## ----------------- ##\"\n    echo\n    for ac_var in $ac_subst_vars\n    do\n      eval ac_val=\\$$ac_var\n      case $ac_val in\n      *\\'\\''*) ac_val=`$as_echo \"$ac_val\" | sed \"s/'\\''/'\\''\\\\\\\\\\\\\\\\'\\'''\\''/g\"`;;\n      esac\n      $as_echo \"$ac_var='\\''$ac_val'\\''\"\n    done | sort\n    echo\n\n    if test -n \"$ac_subst_files\"; then\n      $as_echo \"## ------------------- ##\n## File substitutions. ##\n## ------------------- ##\"\n      echo\n      for ac_var in $ac_subst_files\n      do\n\teval ac_val=\\$$ac_var\n\tcase $ac_val in\n\t*\\'\\''*) ac_val=`$as_echo \"$ac_val\" | sed \"s/'\\''/'\\''\\\\\\\\\\\\\\\\'\\'''\\''/g\"`;;\n\tesac\n\t$as_echo \"$ac_var='\\''$ac_val'\\''\"\n      done | sort\n      echo\n    fi\n\n    if test -s confdefs.h; then\n      $as_echo \"## ----------- ##\n## confdefs.h. ##\n## ----------- ##\"\n      echo\n      cat confdefs.h\n      echo\n    fi\n    test \"$ac_signal\" != 0 &&\n      $as_echo \"$as_me: caught signal $ac_signal\"\n    $as_echo \"$as_me: exit $exit_status\"\n  } >&5\n  rm -f core *.core core.conftest.* &&\n    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&\n    exit $exit_status\n' 0\nfor ac_signal in 1 2 13 15; do\n  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal\ndone\nac_signal=0\n\n# confdefs.h avoids OS command line length limits that DEFS can exceed.\nrm -f -r conftest* confdefs.h\n\n$as_echo \"/* confdefs.h */\" > confdefs.h\n\n# Predefined preprocessor variables.\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_NAME \"$PACKAGE_NAME\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_VERSION \"$PACKAGE_VERSION\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_STRING \"$PACKAGE_STRING\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"\n_ACEOF\n\ncat >>confdefs.h <<_ACEOF\n#define PACKAGE_URL \"$PACKAGE_URL\"\n_ACEOF\n\n\n# Let the site file select an alternate cache file if it wants to.\n# Prefer an explicitly selected file to automatically selected ones.\nac_site_file1=NONE\nac_site_file2=NONE\nif test -n \"$CONFIG_SITE\"; then\n  # We do not want a PATH search for config.site.\n  case $CONFIG_SITE in #((\n    -*)  ac_site_file1=./$CONFIG_SITE;;\n    */*) ac_site_file1=$CONFIG_SITE;;\n    *)   ac_site_file1=./$CONFIG_SITE;;\n  esac\nelif test \"x$prefix\" != xNONE; then\n  ac_site_file1=$prefix/share/config.site\n  ac_site_file2=$prefix/etc/config.site\nelse\n  ac_site_file1=$ac_default_prefix/share/config.site\n  ac_site_file2=$ac_default_prefix/etc/config.site\nfi\nfor ac_site_file in \"$ac_site_file1\" \"$ac_site_file2\"\ndo\n  test \"x$ac_site_file\" = xNONE && continue\n  if test /dev/null != \"$ac_site_file\" && test -r \"$ac_site_file\"; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file\" >&5\n$as_echo \"$as_me: loading site script $ac_site_file\" >&6;}\n    sed 's/^/| /' \"$ac_site_file\" >&5\n    . \"$ac_site_file\" \\\n      || { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"failed to load site script $ac_site_file\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n  fi\ndone\n\nif test -r \"$cache_file\"; then\n  # Some versions of bash will fail to source /dev/null (special files\n  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.\n  if test /dev/null != \"$cache_file\" && test -f \"$cache_file\"; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: loading cache $cache_file\" >&5\n$as_echo \"$as_me: loading cache $cache_file\" >&6;}\n    case $cache_file in\n      [\\\\/]* | ?:[\\\\/]* ) . \"$cache_file\";;\n      *)                      . \"./$cache_file\";;\n    esac\n  fi\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: creating cache $cache_file\" >&5\n$as_echo \"$as_me: creating cache $cache_file\" >&6;}\n  >$cache_file\nfi\n\n# Check that the precious variables saved in the cache have kept the same\n# value.\nac_cache_corrupted=false\nfor ac_var in $ac_precious_vars; do\n  eval ac_old_set=\\$ac_cv_env_${ac_var}_set\n  eval ac_new_set=\\$ac_env_${ac_var}_set\n  eval ac_old_val=\\$ac_cv_env_${ac_var}_value\n  eval ac_new_val=\\$ac_env_${ac_var}_value\n  case $ac_old_set,$ac_new_set in\n    set,)\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: error: \\`$ac_var' was set to \\`$ac_old_val' in the previous run\" >&5\n$as_echo \"$as_me: error: \\`$ac_var' was set to \\`$ac_old_val' in the previous run\" >&2;}\n      ac_cache_corrupted=: ;;\n    ,set)\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: error: \\`$ac_var' was not set in the previous run\" >&5\n$as_echo \"$as_me: error: \\`$ac_var' was not set in the previous run\" >&2;}\n      ac_cache_corrupted=: ;;\n    ,);;\n    *)\n      if test \"x$ac_old_val\" != \"x$ac_new_val\"; then\n\t# differences in whitespace do not lead to failure.\n\tac_old_val_w=`echo x $ac_old_val`\n\tac_new_val_w=`echo x $ac_new_val`\n\tif test \"$ac_old_val_w\" != \"$ac_new_val_w\"; then\n\t  { $as_echo \"$as_me:${as_lineno-$LINENO}: error: \\`$ac_var' has changed since the previous run:\" >&5\n$as_echo \"$as_me: error: \\`$ac_var' has changed since the previous run:\" >&2;}\n\t  ac_cache_corrupted=:\n\telse\n\t  { $as_echo \"$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \\`$ac_var' since the previous run:\" >&5\n$as_echo \"$as_me: warning: ignoring whitespace changes in \\`$ac_var' since the previous run:\" >&2;}\n\t  eval $ac_var=\\$ac_old_val\n\tfi\n\t{ $as_echo \"$as_me:${as_lineno-$LINENO}:   former value:  \\`$ac_old_val'\" >&5\n$as_echo \"$as_me:   former value:  \\`$ac_old_val'\" >&2;}\n\t{ $as_echo \"$as_me:${as_lineno-$LINENO}:   current value: \\`$ac_new_val'\" >&5\n$as_echo \"$as_me:   current value: \\`$ac_new_val'\" >&2;}\n      fi;;\n  esac\n  # Pass precious variables to config.status.\n  if test \"$ac_new_set\" = set; then\n    case $ac_new_val in\n    *\\'*) ac_arg=$ac_var=`$as_echo \"$ac_new_val\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    *) ac_arg=$ac_var=$ac_new_val ;;\n    esac\n    case \" $ac_configure_args \" in\n      *\" '$ac_arg' \"*) ;; # Avoid dups.  Use of quotes ensures accuracy.\n      *) as_fn_append ac_configure_args \" '$ac_arg'\" ;;\n    esac\n  fi\ndone\nif $ac_cache_corrupted; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build\" >&5\n$as_echo \"$as_me: error: changes in the environment can compromise the build\" >&2;}\n  as_fn_error $? \"run \\`make distclean' and/or \\`rm $cache_file' and start over\" \"$LINENO\" 5\nfi\n## -------------------- ##\n## Main body of script. ##\n## -------------------- ##\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\n\n\n\n# we'll need sed and awk for some of the version commands\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output\" >&5\n$as_echo_n \"checking for a sed that does not truncate output... \" >&6; }\nif ${ac_cv_path_SED+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n            ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/\n     for ac_i in 1 2 3 4 5 6 7; do\n       ac_script=\"$ac_script$as_nl$ac_script\"\n     done\n     echo \"$ac_script\" 2>/dev/null | sed 99q >conftest.sed\n     { ac_script=; unset ac_script;}\n     if test -z \"$SED\"; then\n  ac_path_SED_found=false\n  # Loop through the user's path and test for each of PROGNAME-LIST\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_prog in sed gsed; do\n    for ac_exec_ext in '' $ac_executable_extensions; do\n      ac_path_SED=\"$as_dir/$ac_prog$ac_exec_ext\"\n      as_fn_executable_p \"$ac_path_SED\" || continue\n# Check for GNU ac_path_SED and select it if it is found.\n  # Check for GNU $ac_path_SED\ncase `\"$ac_path_SED\" --version 2>&1` in\n*GNU*)\n  ac_cv_path_SED=\"$ac_path_SED\" ac_path_SED_found=:;;\n*)\n  ac_count=0\n  $as_echo_n 0123456789 >\"conftest.in\"\n  while :\n  do\n    cat \"conftest.in\" \"conftest.in\" >\"conftest.tmp\"\n    mv \"conftest.tmp\" \"conftest.in\"\n    cp \"conftest.in\" \"conftest.nl\"\n    $as_echo '' >> \"conftest.nl\"\n    \"$ac_path_SED\" -f conftest.sed < \"conftest.nl\" >\"conftest.out\" 2>/dev/null || break\n    diff \"conftest.out\" \"conftest.nl\" >/dev/null 2>&1 || break\n    as_fn_arith $ac_count + 1 && ac_count=$as_val\n    if test $ac_count -gt ${ac_path_SED_max-0}; then\n      # Best one so far, save it but keep looking for a better one\n      ac_cv_path_SED=\"$ac_path_SED\"\n      ac_path_SED_max=$ac_count\n    fi\n    # 10*(2^10) chars as input seems more than enough\n    test $ac_count -gt 10 && break\n  done\n  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;\nesac\n\n      $ac_path_SED_found && break 3\n    done\n  done\n  done\nIFS=$as_save_IFS\n  if test -z \"$ac_cv_path_SED\"; then\n    as_fn_error $? \"no acceptable sed could be found in \\$PATH\" \"$LINENO\" 5\n  fi\nelse\n  ac_cv_path_SED=$SED\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED\" >&5\n$as_echo \"$ac_cv_path_SED\" >&6; }\n SED=\"$ac_cv_path_SED\"\n  rm -f conftest.sed\n\nfor ac_prog in gawk mawk nawk awk\ndo\n  # Extract the first word of \"$ac_prog\", so it can be a program name with args.\nset dummy $ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_AWK+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$AWK\"; then\n  ac_cv_prog_AWK=\"$AWK\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_AWK=\"$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nAWK=$ac_cv_prog_AWK\nif test -n \"$AWK\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $AWK\" >&5\n$as_echo \"$AWK\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  test -n \"$AWK\" && break\ndone\n\n\n# CITUS_NAME definition\n\ncat >>confdefs.h <<_ACEOF\n#define CITUS_NAME \"$PACKAGE_NAME\"\n_ACEOF\n\n\ncase $PACKAGE_NAME in\n  'Citus Enterprise') citus_edition=enterprise ;;\n               Citus) citus_edition=community ;;\n                   *) as_fn_error $? \"Unrecognized package name.\" \"$LINENO\" 5 ;;\nesac\n\n# CITUS_EDITION definition\n\ncat >>confdefs.h <<_ACEOF\n#define CITUS_EDITION \"$citus_edition\"\n_ACEOF\n\n\n# CITUS_MAJORVERSION definition\nCITUS_MAJORVERSION=`expr \"$PACKAGE_VERSION\" : '\\([0-9][0-9]*\\.[0-9][0-9]*\\)'`\n\ncat >>confdefs.h <<_ACEOF\n#define CITUS_MAJORVERSION \"$CITUS_MAJORVERSION\"\n_ACEOF\n\n\n# CITUS_VERSION definition\n\n\n\n# Check whether --with-extra-version was given.\nif test \"${with_extra_version+set}\" = set; then :\n  withval=$with_extra_version;\n  case $withval in\n    yes)\n      as_fn_error $? \"argument required for --with-extra-version option\" \"$LINENO\" 5\n      ;;\n    no)\n      as_fn_error $? \"argument required for --with-extra-version option\" \"$LINENO\" 5\n      ;;\n    *)\n      CITUS_VERSION=\"$PACKAGE_VERSION$withval\"\n      ;;\n  esac\n\nelse\n  CITUS_VERSION=\"$PACKAGE_VERSION\"\nfi\n\n\n\ncat >>confdefs.h <<_ACEOF\n#define CITUS_VERSION \"$CITUS_VERSION\"\n_ACEOF\n\n\n# CITUS_VERSION_NUM definition\n# awk -F is a regex on some platforms, and not on others, so make \".\" a tab\nCITUS_VERSION_NUM=\"`echo \"$PACKAGE_VERSION\" | sed 's/[A-Za-z].*$//' |\ntr '.' '\t' |\n$AWK '{printf \"%d%02d%02d\", $1, $2, (NF >= 3) ? $3 : 0}'`\"\n\ncat >>confdefs.h <<_ACEOF\n#define CITUS_VERSION_NUM $CITUS_VERSION_NUM\n_ACEOF\n\n\n# CITUS_EXTENSIONVERSION definition\nCITUS_EXTENSIONVERSION=\"`grep '^default_version' $srcdir/src/backend/distributed/citus.control | cut -d\\' -f2`\"\n\ncat >>confdefs.h <<_ACEOF\n#define CITUS_EXTENSIONVERSION \"$CITUS_EXTENSIONVERSION\"\n_ACEOF\n\n\n# Re-check for flex. That allows to compile citus against a postgres\n# which was built without flex available (possible because generated\n# files are included)\n# Extract the first word of \"flex\", so it can be a program name with args.\nset dummy flex; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_FLEX+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $FLEX in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_FLEX=\"$FLEX\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_FLEX=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nFLEX=$ac_cv_path_FLEX\nif test -n \"$FLEX\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $FLEX\" >&5\n$as_echo \"$FLEX\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n\n# Locate pg_config binary\n\n\n\nif test -z \"$PG_CONFIG\"; then\n  # Extract the first word of \"pg_config\", so it can be a program name with args.\nset dummy pg_config; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_PG_CONFIG+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $PG_CONFIG in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_PG_CONFIG=\"$PG_CONFIG\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_PG_CONFIG=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nPG_CONFIG=$ac_cv_path_PG_CONFIG\nif test -n \"$PG_CONFIG\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $PG_CONFIG\" >&5\n$as_echo \"$PG_CONFIG\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\nfi\n\nif test -z \"$PG_CONFIG\"; then\n   as_fn_error $? \"Could not find pg_config. Set PG_CONFIG or PATH.\" \"$LINENO\" 5\nfi\n\n# check we're building against a supported version of PostgreSQL\ncitusac_pg_config_version=$($PG_CONFIG --version 2>/dev/null)\nversion_num=$(echo \"$citusac_pg_config_version\"|\n              $SED -e 's/^PostgreSQL \\([0-9]*\\)\\(\\.[0-9]*\\)\\{0,1\\}\\(.*\\)$/\\1\\2/')\n\n# if PostgreSQL version starts with two digits, the major version is those digits\nversion_num=$(echo \"$version_num\"| $SED -e 's/^\\([0-9]\\{2\\}\\)\\(.*\\)$/\\1/')\n\nif test -z \"$version_num\"; then\n  as_fn_error $? \"Could not detect PostgreSQL version from pg_config.\" \"$LINENO\" 5\nfi\n\n\n\n\n# Check whether --with-pg-version-check was given.\nif test \"${with_pg_version_check+set}\" = set; then :\n  withval=$with_pg_version_check;\n  case $withval in\n    yes)\n      :\n      ;;\n    no)\n      :\n      ;;\n    *)\n      as_fn_error $? \"no argument expected for --with-pg-version-check option\" \"$LINENO\" 5\n      ;;\n  esac\n\nelse\n  with_pg_version_check=yes\n\nfi\n\n\n\n\nif test \"$with_pg_version_check\" = no; then\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: building against PostgreSQL $version_num (skipped compatibility check)\" >&5\n$as_echo \"$as_me: building against PostgreSQL $version_num (skipped compatibility check)\" >&6;}\nelif test \"$version_num\" != '16' -a  \"$version_num\" != '17' -a  \"$version_num\" != '18'; then\n   as_fn_error $? \"Citus is not compatible with the detected PostgreSQL version ${version_num}.\" \"$LINENO\" 5\nelse\n   { $as_echo \"$as_me:${as_lineno-$LINENO}: building against PostgreSQL $version_num\" >&5\n$as_echo \"$as_me: building against PostgreSQL $version_num\" >&6;}\nfi;\n\n# Check whether we're building inside the source tree, if not, prepare\n# the build directory.\nif test \"$srcdir\" -ef '.' ; then\n  vpath_build=no\nelse\n  vpath_build=yes\n  $as_echo_n \"preparing build tree... \" >&6\n  citusac_abs_top_srcdir=`cd \"$srcdir\" && pwd`\n  $SHELL \"$citusac_abs_top_srcdir/prep_buildtree\" \"$citusac_abs_top_srcdir\" \".\" \\\n      || as_fn_error $? \"failed\" \"$LINENO\" 5\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: done\" >&5\n$as_echo \"done\" >&6; }\nfi\n\n\n# Allow to overwrite the C compiler, default to the one postgres was\n# compiled with. We don't want autoconf's default CFLAGS though, so save\n# those.\nSAVE_CFLAGS=\"$CFLAGS\"\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\nif test -n \"$ac_tool_prefix\"; then\n  for ac_prog in $($PG_CONFIG --cc)\n  do\n    # Extract the first word of \"$ac_tool_prefix$ac_prog\", so it can be a program name with args.\nset dummy $ac_tool_prefix$ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$CC\"; then\n  ac_cv_prog_CC=\"$CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_CC=\"$ac_tool_prefix$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nCC=$ac_cv_prog_CC\nif test -n \"$CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CC\" >&5\n$as_echo \"$CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n    test -n \"$CC\" && break\n  done\nfi\nif test -z \"$CC\"; then\n  ac_ct_CC=$CC\n  for ac_prog in $($PG_CONFIG --cc)\ndo\n  # Extract the first word of \"$ac_prog\", so it can be a program name with args.\nset dummy $ac_prog; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_prog_ac_ct_CC+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -n \"$ac_ct_CC\"; then\n  ac_cv_prog_ac_ct_CC=\"$ac_ct_CC\" # Let the user override the test.\nelse\nas_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_prog_ac_ct_CC=\"$ac_prog\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\nfi\nfi\nac_ct_CC=$ac_cv_prog_ac_ct_CC\nif test -n \"$ac_ct_CC\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC\" >&5\n$as_echo \"$ac_ct_CC\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n  test -n \"$ac_ct_CC\" && break\ndone\n\n  if test \"x$ac_ct_CC\" = x; then\n    CC=\"\"\n  else\n    case $cross_compiling:$ac_tool_warned in\nyes:)\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet\" >&5\n$as_echo \"$as_me: WARNING: using cross tools not prefixed with host triplet\" >&2;}\nac_tool_warned=yes ;;\nesac\n    CC=$ac_ct_CC\n  fi\nfi\n\n\ntest -z \"$CC\" && { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"no acceptable C compiler found in \\$PATH\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n\n# Provide some information about the compiler.\n$as_echo \"$as_me:${as_lineno-$LINENO}: checking for C compiler version\" >&5\nset X $ac_compile\nac_compiler=$2\nfor ac_option in --version -v -V -qversion; do\n  { { ac_try=\"$ac_compiler $ac_option >&5\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compiler $ac_option >&5\") 2>conftest.err\n  ac_status=$?\n  if test -s conftest.err; then\n    sed '10a\\\n... rest of stderr output deleted ...\n         10q' conftest.err >conftest.er1\n    cat conftest.er1 >&5\n  fi\n  rm -f conftest.er1 conftest.err\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\ndone\n\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nac_clean_files_save=$ac_clean_files\nac_clean_files=\"$ac_clean_files a.out a.out.dSYM a.exe b.out\"\n# Try to create an executable without -o first, disregard a.out.\n# It will help us diagnose broken compilers, and finding out an intuition\n# of exeext.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether the C compiler works\" >&5\n$as_echo_n \"checking whether the C compiler works... \" >&6; }\nac_link_default=`$as_echo \"$ac_link\" | sed 's/ -o *conftest[^ ]*//'`\n\n# The possible output files:\nac_files=\"a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*\"\n\nac_rmfiles=\nfor ac_file in $ac_files\ndo\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;\n    * ) ac_rmfiles=\"$ac_rmfiles $ac_file\";;\n  esac\ndone\nrm -f $ac_rmfiles\n\nif { { ac_try=\"$ac_link_default\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link_default\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then :\n  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.\n# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'\n# in a Makefile.  We should not override ac_cv_exeext if it was cached,\n# so that the user can short-circuit this test for compilers unknown to\n# Autoconf.\nfor ac_file in $ac_files ''\ndo\n  test -f \"$ac_file\" || continue\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )\n\t;;\n    [ab].out )\n\t# We found the default executable, but exeext='' is most\n\t# certainly right.\n\tbreak;;\n    *.* )\n\tif test \"${ac_cv_exeext+set}\" = set && test \"$ac_cv_exeext\" != no;\n\tthen :; else\n\t   ac_cv_exeext=`expr \"$ac_file\" : '[^.]*\\(\\..*\\)'`\n\tfi\n\t# We set ac_cv_exeext here because the later test for it is not\n\t# safe: cross compilers may not add the suffix if given an `-o'\n\t# argument, so we may need to know it at that point already.\n\t# Even if this section looks crufty: it has the advantage of\n\t# actually working.\n\tbreak;;\n    * )\n\tbreak;;\n  esac\ndone\ntest \"$ac_cv_exeext\" = no && ac_cv_exeext=\n\nelse\n  ac_file=''\nfi\nif test -z \"$ac_file\"; then :\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\n$as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n{ { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error 77 \"C compiler cannot create executables\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: yes\" >&5\n$as_echo \"yes\" >&6; }\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name\" >&5\n$as_echo_n \"checking for C compiler default output file name... \" >&6; }\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_file\" >&5\n$as_echo \"$ac_file\" >&6; }\nac_exeext=$ac_cv_exeext\n\nrm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out\nac_clean_files=$ac_clean_files_save\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for suffix of executables\" >&5\n$as_echo_n \"checking for suffix of executables... \" >&6; }\nif { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then :\n  # If both `conftest.exe' and `conftest' are `present' (well, observable)\n# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will\n# work properly (i.e., refer to `conftest.exe'), while it won't with\n# `rm'.\nfor ac_file in conftest.exe conftest conftest.*; do\n  test -f \"$ac_file\" || continue\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;\n    *.* ) ac_cv_exeext=`expr \"$ac_file\" : '[^.]*\\(\\..*\\)'`\n\t  break;;\n    * ) break;;\n  esac\ndone\nelse\n  { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"cannot compute suffix of executables: cannot compile and link\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nfi\nrm -f conftest conftest$ac_cv_exeext\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext\" >&5\n$as_echo \"$ac_cv_exeext\" >&6; }\n\nrm -f conftest.$ac_ext\nEXEEXT=$ac_cv_exeext\nac_exeext=$EXEEXT\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdio.h>\nint\nmain ()\n{\nFILE *f = fopen (\"conftest.out\", \"w\");\n return ferror (f) || fclose (f) != 0;\n\n  ;\n  return 0;\n}\n_ACEOF\nac_clean_files=\"$ac_clean_files conftest.out\"\n# Check that the compiler produces executables we can run.  If not, either\n# the compiler is broken, or we cross compile.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling\" >&5\n$as_echo_n \"checking whether we are cross compiling... \" >&6; }\nif test \"$cross_compiling\" != yes; then\n  { { ac_try=\"$ac_link\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_link\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }\n  if { ac_try='./conftest$ac_cv_exeext'\n  { { case \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_try\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; }; then\n    cross_compiling=no\n  else\n    if test \"$cross_compiling\" = maybe; then\n\tcross_compiling=yes\n    else\n\t{ { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"cannot run C compiled programs.\nIf you meant to cross compile, use \\`--host'.\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n    fi\n  fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $cross_compiling\" >&5\n$as_echo \"$cross_compiling\" >&6; }\n\nrm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out\nac_clean_files=$ac_clean_files_save\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for suffix of object files\" >&5\n$as_echo_n \"checking for suffix of object files... \" >&6; }\nif ${ac_cv_objext+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nrm -f conftest.o conftest.obj\nif { { ac_try=\"$ac_compile\"\ncase \"(($ac_try\" in\n  *\\\"* | *\\`* | *\\\\*) ac_try_echo=\\$ac_try;;\n  *) ac_try_echo=$ac_try;;\nesac\neval ac_try_echo=\"\\\"\\$as_me:${as_lineno-$LINENO}: $ac_try_echo\\\"\"\n$as_echo \"$ac_try_echo\"; } >&5\n  (eval \"$ac_compile\") 2>&5\n  ac_status=$?\n  $as_echo \"$as_me:${as_lineno-$LINENO}: \\$? = $ac_status\" >&5\n  test $ac_status = 0; }; then :\n  for ac_file in conftest.o conftest.obj conftest.*; do\n  test -f \"$ac_file\" || continue;\n  case $ac_file in\n    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;\n    *) ac_cv_objext=`expr \"$ac_file\" : '.*\\.\\(.*\\)'`\n       break;;\n  esac\ndone\nelse\n  $as_echo \"$as_me: failed program was:\" >&5\nsed 's/^/| /' conftest.$ac_ext >&5\n\n{ { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"cannot compute suffix of object files: cannot compile\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nfi\nrm -f conftest.$ac_cv_objext conftest.$ac_ext\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext\" >&5\n$as_echo \"$ac_cv_objext\" >&6; }\nOBJEXT=$ac_cv_objext\nac_objext=$OBJEXT\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler\" >&5\n$as_echo_n \"checking whether we are using the GNU C compiler... \" >&6; }\nif ${ac_cv_c_compiler_gnu+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n#ifndef __GNUC__\n       choke me\n#endif\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_compiler_gnu=yes\nelse\n  ac_compiler_gnu=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_cv_c_compiler_gnu=$ac_compiler_gnu\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu\" >&5\n$as_echo \"$ac_cv_c_compiler_gnu\" >&6; }\nif test $ac_compiler_gnu = yes; then\n  GCC=yes\nelse\n  GCC=\nfi\nac_test_CFLAGS=${CFLAGS+set}\nac_save_CFLAGS=$CFLAGS\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g\" >&5\n$as_echo_n \"checking whether $CC accepts -g... \" >&6; }\nif ${ac_cv_prog_cc_g+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_save_c_werror_flag=$ac_c_werror_flag\n   ac_c_werror_flag=yes\n   ac_cv_prog_cc_g=no\n   CFLAGS=\"-g\"\n   cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_g=yes\nelse\n  CFLAGS=\"\"\n      cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n\nelse\n  ac_c_werror_flag=$ac_save_c_werror_flag\n\t CFLAGS=\"-g\"\n\t cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_g=yes\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n   ac_c_werror_flag=$ac_save_c_werror_flag\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g\" >&5\n$as_echo \"$ac_cv_prog_cc_g\" >&6; }\nif test \"$ac_test_CFLAGS\" = set; then\n  CFLAGS=$ac_save_CFLAGS\nelif test $ac_cv_prog_cc_g = yes; then\n  if test \"$GCC\" = yes; then\n    CFLAGS=\"-g -O2\"\n  else\n    CFLAGS=\"-g\"\n  fi\nelse\n  if test \"$GCC\" = yes; then\n    CFLAGS=\"-O2\"\n  else\n    CFLAGS=\n  fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89\" >&5\n$as_echo_n \"checking for $CC option to accept ISO C89... \" >&6; }\nif ${ac_cv_prog_cc_c89+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_cv_prog_cc_c89=no\nac_save_CC=$CC\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdarg.h>\n#include <stdio.h>\nstruct stat;\n/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */\nstruct buf { int x; };\nFILE * (*rcsopen) (struct buf *, struct stat *, int);\nstatic char *e (p, i)\n     char **p;\n     int i;\n{\n  return p[i];\n}\nstatic char *f (char * (*g) (char **, int), char **p, ...)\n{\n  char *s;\n  va_list v;\n  va_start (v,p);\n  s = g (p, va_arg (v,int));\n  va_end (v);\n  return s;\n}\n\n/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has\n   function prototypes and stuff, but not '\\xHH' hex character constants.\n   These don't provoke an error unfortunately, instead are silently treated\n   as 'x'.  The following induces an error, until -std is added to get\n   proper ANSI mode.  Curiously '\\x00'!='x' always comes out true, for an\n   array size at least.  It's necessary to write '\\x00'==0 to get something\n   that's true only with -std.  */\nint osf4_cc_array ['\\x00' == 0 ? 1 : -1];\n\n/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters\n   inside strings and character constants.  */\n#define FOO(x) 'x'\nint xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];\n\nint test (int i, double x);\nstruct s1 {int (*f) (int a);};\nstruct s2 {int (*f) (double a);};\nint pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);\nint argc;\nchar **argv;\nint\nmain ()\n{\nreturn f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];\n  ;\n  return 0;\n}\n_ACEOF\nfor ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \\\n\t-Ae \"-Aa -D_HPUX_SOURCE\" \"-Xc -D__EXTENSIONS__\"\ndo\n  CC=\"$ac_save_CC $ac_arg\"\n  if ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_prog_cc_c89=$ac_arg\nfi\nrm -f core conftest.err conftest.$ac_objext\n  test \"x$ac_cv_prog_cc_c89\" != \"xno\" && break\ndone\nrm -f conftest.$ac_ext\nCC=$ac_save_CC\n\nfi\n# AC_CACHE_VAL\ncase \"x$ac_cv_prog_cc_c89\" in\n  x)\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: none needed\" >&5\n$as_echo \"none needed\" >&6; } ;;\n  xno)\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: unsupported\" >&5\n$as_echo \"unsupported\" >&6; } ;;\n  *)\n    CC=\"$CC $ac_cv_prog_cc_c89\"\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89\" >&5\n$as_echo \"$ac_cv_prog_cc_c89\" >&6; } ;;\nesac\nif test \"x$ac_cv_prog_cc_c89\" != xno; then :\n\nfi\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\nCFLAGS=\"$SAVE_CFLAGS\"\n\nhost_guess=`${SHELL} $srcdir/config/config.guess`\n\n# Create compiler version string\nif test x\"$GCC\" = x\"yes\" ; then\n  cc_string=`${CC} --version | sed q`\n  case $cc_string in [A-Za-z]*) ;; *) cc_string=\"GCC $cc_string\";; esac\nelif test x\"$SUN_STUDIO_CC\" = x\"yes\" ; then\n  cc_string=`${CC} -V 2>&1 | sed q`\nelse\n  cc_string=$CC\nfi\n\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor\" >&5\n$as_echo_n \"checking how to run the C preprocessor... \" >&6; }\n# On Suns, sometimes $CPP names a directory.\nif test -n \"$CPP\" && test -d \"$CPP\"; then\n  CPP=\nfi\nif test -z \"$CPP\"; then\n  if ${ac_cv_prog_CPP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n      # Double quotes because CPP needs to be expanded\n    for CPP in \"$CC -E\" \"$CC -E -traditional-cpp\" \"/lib/cpp\"\n    do\n      ac_preproc_ok=false\nfor ac_c_preproc_warn_flag in '' yes\ndo\n  # Use a header file that comes with gcc, so configuring glibc\n  # with a fresh cross-compiler works.\n  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since\n  # <limits.h> exists even on freestanding compilers.\n  # On the NeXT, cc -E runs the code through the compiler's parser,\n  # not just through cpp. \"Syntax error\" is here to catch this case.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#ifdef __STDC__\n# include <limits.h>\n#else\n# include <assert.h>\n#endif\n\t\t     Syntax error\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n\nelse\n  # Broken: fails on valid input.\ncontinue\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\n  # OK, works on sane cases.  Now check whether nonexistent headers\n  # can be detected and how.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ac_nonexistent.h>\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n  # Broken: success on invalid input.\ncontinue\nelse\n  # Passes both tests.\nac_preproc_ok=:\nbreak\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\ndone\n# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.\nrm -f conftest.i conftest.err conftest.$ac_ext\nif $ac_preproc_ok; then :\n  break\nfi\n\n    done\n    ac_cv_prog_CPP=$CPP\n\nfi\n  CPP=$ac_cv_prog_CPP\nelse\n  ac_cv_prog_CPP=$CPP\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $CPP\" >&5\n$as_echo \"$CPP\" >&6; }\nac_preproc_ok=false\nfor ac_c_preproc_warn_flag in '' yes\ndo\n  # Use a header file that comes with gcc, so configuring glibc\n  # with a fresh cross-compiler works.\n  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since\n  # <limits.h> exists even on freestanding compilers.\n  # On the NeXT, cc -E runs the code through the compiler's parser,\n  # not just through cpp. \"Syntax error\" is here to catch this case.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#ifdef __STDC__\n# include <limits.h>\n#else\n# include <assert.h>\n#endif\n\t\t     Syntax error\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n\nelse\n  # Broken: fails on valid input.\ncontinue\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\n  # OK, works on sane cases.  Now check whether nonexistent headers\n  # can be detected and how.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ac_nonexistent.h>\n_ACEOF\nif ac_fn_c_try_cpp \"$LINENO\"; then :\n  # Broken: success on invalid input.\ncontinue\nelse\n  # Passes both tests.\nac_preproc_ok=:\nbreak\nfi\nrm -f conftest.err conftest.i conftest.$ac_ext\n\ndone\n# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.\nrm -f conftest.i conftest.err conftest.$ac_ext\nif $ac_preproc_ok; then :\n\nelse\n  { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error $? \"C preprocessor \\\"$CPP\\\" fails sanity check\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\nfi\n\nac_ext=c\nac_cpp='$CPP $CPPFLAGS'\nac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'\nac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'\nac_compiler_gnu=$ac_cv_c_compiler_gnu\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e\" >&5\n$as_echo_n \"checking for grep that handles long lines and -e... \" >&6; }\nif ${ac_cv_path_GREP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if test -z \"$GREP\"; then\n  ac_path_GREP_found=false\n  # Loop through the user's path and test for each of PROGNAME-LIST\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_prog in grep ggrep; do\n    for ac_exec_ext in '' $ac_executable_extensions; do\n      ac_path_GREP=\"$as_dir/$ac_prog$ac_exec_ext\"\n      as_fn_executable_p \"$ac_path_GREP\" || continue\n# Check for GNU ac_path_GREP and select it if it is found.\n  # Check for GNU $ac_path_GREP\ncase `\"$ac_path_GREP\" --version 2>&1` in\n*GNU*)\n  ac_cv_path_GREP=\"$ac_path_GREP\" ac_path_GREP_found=:;;\n*)\n  ac_count=0\n  $as_echo_n 0123456789 >\"conftest.in\"\n  while :\n  do\n    cat \"conftest.in\" \"conftest.in\" >\"conftest.tmp\"\n    mv \"conftest.tmp\" \"conftest.in\"\n    cp \"conftest.in\" \"conftest.nl\"\n    $as_echo 'GREP' >> \"conftest.nl\"\n    \"$ac_path_GREP\" -e 'GREP$' -e '-(cannot match)-' < \"conftest.nl\" >\"conftest.out\" 2>/dev/null || break\n    diff \"conftest.out\" \"conftest.nl\" >/dev/null 2>&1 || break\n    as_fn_arith $ac_count + 1 && ac_count=$as_val\n    if test $ac_count -gt ${ac_path_GREP_max-0}; then\n      # Best one so far, save it but keep looking for a better one\n      ac_cv_path_GREP=\"$ac_path_GREP\"\n      ac_path_GREP_max=$ac_count\n    fi\n    # 10*(2^10) chars as input seems more than enough\n    test $ac_count -gt 10 && break\n  done\n  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;\nesac\n\n      $ac_path_GREP_found && break 3\n    done\n  done\n  done\nIFS=$as_save_IFS\n  if test -z \"$ac_cv_path_GREP\"; then\n    as_fn_error $? \"no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin\" \"$LINENO\" 5\n  fi\nelse\n  ac_cv_path_GREP=$GREP\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP\" >&5\n$as_echo \"$ac_cv_path_GREP\" >&6; }\n GREP=\"$ac_cv_path_GREP\"\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for egrep\" >&5\n$as_echo_n \"checking for egrep... \" >&6; }\nif ${ac_cv_path_EGREP+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1\n   then ac_cv_path_EGREP=\"$GREP -E\"\n   else\n     if test -z \"$EGREP\"; then\n  ac_path_EGREP_found=false\n  # Loop through the user's path and test for each of PROGNAME-LIST\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_prog in egrep; do\n    for ac_exec_ext in '' $ac_executable_extensions; do\n      ac_path_EGREP=\"$as_dir/$ac_prog$ac_exec_ext\"\n      as_fn_executable_p \"$ac_path_EGREP\" || continue\n# Check for GNU ac_path_EGREP and select it if it is found.\n  # Check for GNU $ac_path_EGREP\ncase `\"$ac_path_EGREP\" --version 2>&1` in\n*GNU*)\n  ac_cv_path_EGREP=\"$ac_path_EGREP\" ac_path_EGREP_found=:;;\n*)\n  ac_count=0\n  $as_echo_n 0123456789 >\"conftest.in\"\n  while :\n  do\n    cat \"conftest.in\" \"conftest.in\" >\"conftest.tmp\"\n    mv \"conftest.tmp\" \"conftest.in\"\n    cp \"conftest.in\" \"conftest.nl\"\n    $as_echo 'EGREP' >> \"conftest.nl\"\n    \"$ac_path_EGREP\" 'EGREP$' < \"conftest.nl\" >\"conftest.out\" 2>/dev/null || break\n    diff \"conftest.out\" \"conftest.nl\" >/dev/null 2>&1 || break\n    as_fn_arith $ac_count + 1 && ac_count=$as_val\n    if test $ac_count -gt ${ac_path_EGREP_max-0}; then\n      # Best one so far, save it but keep looking for a better one\n      ac_cv_path_EGREP=\"$ac_path_EGREP\"\n      ac_path_EGREP_max=$ac_count\n    fi\n    # 10*(2^10) chars as input seems more than enough\n    test $ac_count -gt 10 && break\n  done\n  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;\nesac\n\n      $ac_path_EGREP_found && break 3\n    done\n  done\n  done\nIFS=$as_save_IFS\n  if test -z \"$ac_cv_path_EGREP\"; then\n    as_fn_error $? \"no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin\" \"$LINENO\" 5\n  fi\nelse\n  ac_cv_path_EGREP=$EGREP\nfi\n\n   fi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP\" >&5\n$as_echo \"$ac_cv_path_EGREP\" >&6; }\n EGREP=\"$ac_cv_path_EGREP\"\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for ANSI C header files\" >&5\n$as_echo_n \"checking for ANSI C header files... \" >&6; }\nif ${ac_cv_header_stdc+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdlib.h>\n#include <stdarg.h>\n#include <string.h>\n#include <float.h>\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  ac_cv_header_stdc=yes\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\n\nif test $ac_cv_header_stdc = yes; then\n  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <string.h>\n\n_ACEOF\nif (eval \"$ac_cpp conftest.$ac_ext\") 2>&5 |\n  $EGREP \"memchr\" >/dev/null 2>&1; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f conftest*\n\nfi\n\nif test $ac_cv_header_stdc = yes; then\n  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <stdlib.h>\n\n_ACEOF\nif (eval \"$ac_cpp conftest.$ac_ext\") 2>&5 |\n  $EGREP \"free\" >/dev/null 2>&1; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f conftest*\n\nfi\n\nif test $ac_cv_header_stdc = yes; then\n  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.\n  if test \"$cross_compiling\" = yes; then :\n  :\nelse\n  cat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n#include <ctype.h>\n#include <stdlib.h>\n#if ((' ' & 0x0FF) == 0x020)\n# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')\n# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))\n#else\n# define ISLOWER(c) \\\n\t\t   (('a' <= (c) && (c) <= 'i') \\\n\t\t     || ('j' <= (c) && (c) <= 'r') \\\n\t\t     || ('s' <= (c) && (c) <= 'z'))\n# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))\n#endif\n\n#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))\nint\nmain ()\n{\n  int i;\n  for (i = 0; i < 256; i++)\n    if (XOR (islower (i), ISLOWER (i))\n\t|| toupper (i) != TOUPPER (i))\n      return 2;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_run \"$LINENO\"; then :\n\nelse\n  ac_cv_header_stdc=no\nfi\nrm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \\\n  conftest.$ac_objext conftest.beam conftest.$ac_ext\nfi\n\nfi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc\" >&5\n$as_echo \"$ac_cv_header_stdc\" >&6; }\nif test $ac_cv_header_stdc = yes; then\n\n$as_echo \"#define STDC_HEADERS 1\" >>confdefs.h\n\nfi\n\n# On IRIX 5.3, sys/types and inttypes.h are conflicting.\nfor ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \\\n\t\t  inttypes.h stdint.h unistd.h\ndo :\n  as_ac_Header=`$as_echo \"ac_cv_header_$ac_header\" | $as_tr_sh`\nac_fn_c_check_header_compile \"$LINENO\" \"$ac_header\" \"$as_ac_Header\" \"$ac_includes_default\n\"\nif eval test \\\"x\\$\"$as_ac_Header\"\\\" = x\"yes\"; then :\n  cat >>confdefs.h <<_ACEOF\n#define `$as_echo \"HAVE_$ac_header\" | $as_tr_cpp` 1\n_ACEOF\n\nfi\n\ndone\n\n\n# The cast to long int works around a bug in the HP C Compiler\n# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects\n# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.\n# This bug is HP SR number 8606223364.\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking size of void *\" >&5\n$as_echo_n \"checking size of void *... \" >&6; }\nif ${ac_cv_sizeof_void_p+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  if ac_fn_c_compute_int \"$LINENO\" \"(long int) (sizeof (void *))\" \"ac_cv_sizeof_void_p\"        \"$ac_includes_default\"; then :\n\nelse\n  if test \"$ac_cv_type_void_p\" = yes; then\n     { { $as_echo \"$as_me:${as_lineno-$LINENO}: error: in \\`$ac_pwd':\" >&5\n$as_echo \"$as_me: error: in \\`$ac_pwd':\" >&2;}\nas_fn_error 77 \"cannot compute sizeof (void *)\nSee \\`config.log' for more details\" \"$LINENO\" 5; }\n   else\n     ac_cv_sizeof_void_p=0\n   fi\nfi\n\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p\" >&5\n$as_echo \"$ac_cv_sizeof_void_p\" >&6; }\n\n\n\ncat >>confdefs.h <<_ACEOF\n#define SIZEOF_VOID_P $ac_cv_sizeof_void_p\n_ACEOF\n\n\n\n\ncat >>confdefs.h <<_ACEOF\n#define CITUS_VERSION_STR \"$PACKAGE_NAME $CITUS_VERSION on $host_guess, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \\* 8`-bit\"\n_ACEOF\n\n\n# Locate source and build directory of the postgres we're building\n# against. Can't rely on either still being present, but e.g. optional\n# test infrastructure can rely on it.\nPOSTGRES_SRCDIR=$(grep ^abs_top_srcdir $(dirname $($PG_CONFIG --pgxs))/../Makefile.global|cut -d ' ' -f3-)\nPOSTGRES_BUILDDIR=$(grep ^abs_top_builddir $(dirname $($PG_CONFIG --pgxs))/../Makefile.global|cut -d ' ' -f3-)\n\n\n# check for a number of CFLAGS that make development easier\n\n# CITUSAC_PROG_CC_CFLAGS_OPT\n# -----------------------\n# Given a string, check if the compiler supports the string as a\n# command-line option. If it does, add the string to CFLAGS.\n# CITUSAC_PROG_CC_CFLAGS_OPT\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -std=gnu99\" >&5\n$as_echo_n \"checking whether $CC supports -std=gnu99... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__std_gnu99+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-std=gnu99\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__std_gnu99=yes\nelse\n  citusac_cv_prog_cc_cflags__std_gnu99=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__std_gnu99\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__std_gnu99\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__std_gnu99\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -std=gnu99\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wall\" >&5\n$as_echo_n \"checking whether $CC supports -Wall... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wall+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wall\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wall=yes\nelse\n  citusac_cv_prog_cc_cflags__Wall=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wall\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wall\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wall\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wall\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wextra\" >&5\n$as_echo_n \"checking whether $CC supports -Wextra... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wextra+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wextra\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wextra=yes\nelse\n  citusac_cv_prog_cc_cflags__Wextra=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wextra\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wextra\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wextra\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wextra\"\nfi\n\n# disarm options included in the above, which are too noisy for now\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wno-unused-parameter\" >&5\n$as_echo_n \"checking whether $CC supports -Wno-unused-parameter... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wno_unused_parameter+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wno-unused-parameter\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wno_unused_parameter=yes\nelse\n  citusac_cv_prog_cc_cflags__Wno_unused_parameter=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wno_unused_parameter\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wno_unused_parameter\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wno_unused_parameter\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wno-unused-parameter\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wno-sign-compare\" >&5\n$as_echo_n \"checking whether $CC supports -Wno-sign-compare... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wno_sign_compare+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wno-sign-compare\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wno_sign_compare=yes\nelse\n  citusac_cv_prog_cc_cflags__Wno_sign_compare=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wno_sign_compare\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wno_sign_compare\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wno_sign_compare\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wno-sign-compare\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wno-missing-field-initializers\" >&5\n$as_echo_n \"checking whether $CC supports -Wno-missing-field-initializers... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wno_missing_field_initializers+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wno-missing-field-initializers\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wno_missing_field_initializers=yes\nelse\n  citusac_cv_prog_cc_cflags__Wno_missing_field_initializers=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wno_missing_field_initializers\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wno_missing_field_initializers\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wno_missing_field_initializers\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wno-missing-field-initializers\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wno-clobbered\" >&5\n$as_echo_n \"checking whether $CC supports -Wno-clobbered... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wno_clobbered+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wno-clobbered\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wno_clobbered=yes\nelse\n  citusac_cv_prog_cc_cflags__Wno_clobbered=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wno_clobbered\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wno_clobbered\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wno_clobbered\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wno-clobbered\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wno-gnu-variable-sized-type-not-at-end\" >&5\n$as_echo_n \"checking whether $CC supports -Wno-gnu-variable-sized-type-not-at-end... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wno_gnu_variable_sized_type_not_at_end+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wno-gnu-variable-sized-type-not-at-end\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wno_gnu_variable_sized_type_not_at_end=yes\nelse\n  citusac_cv_prog_cc_cflags__Wno_gnu_variable_sized_type_not_at_end=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wno_gnu_variable_sized_type_not_at_end\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wno_gnu_variable_sized_type_not_at_end\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wno_gnu_variable_sized_type_not_at_end\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wno-gnu-variable-sized-type-not-at-end\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wno-declaration-after-statement\" >&5\n$as_echo_n \"checking whether $CC supports -Wno-declaration-after-statement... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wno_declaration_after_statement+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wno-declaration-after-statement\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wno_declaration_after_statement=yes\nelse\n  citusac_cv_prog_cc_cflags__Wno_declaration_after_statement=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wno_declaration_after_statement\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wno_declaration_after_statement\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wno_declaration_after_statement\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wno-declaration-after-statement\"\nfi\n\n# And add a few extra warnings\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wendif-labels\" >&5\n$as_echo_n \"checking whether $CC supports -Wendif-labels... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wendif_labels+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wendif-labels\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wendif_labels=yes\nelse\n  citusac_cv_prog_cc_cflags__Wendif_labels=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wendif_labels\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wendif_labels\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wendif_labels\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wendif-labels\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wmissing-format-attribute\" >&5\n$as_echo_n \"checking whether $CC supports -Wmissing-format-attribute... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wmissing_format_attribute+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wmissing-format-attribute\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wmissing_format_attribute=yes\nelse\n  citusac_cv_prog_cc_cflags__Wmissing_format_attribute=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wmissing_format_attribute\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wmissing_format_attribute\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wmissing_format_attribute\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wmissing-format-attribute\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wmissing-declarations\" >&5\n$as_echo_n \"checking whether $CC supports -Wmissing-declarations... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wmissing_declarations+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wmissing-declarations\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wmissing_declarations=yes\nelse\n  citusac_cv_prog_cc_cflags__Wmissing_declarations=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wmissing_declarations\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wmissing_declarations\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wmissing_declarations\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wmissing-declarations\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wmissing-prototypes\" >&5\n$as_echo_n \"checking whether $CC supports -Wmissing-prototypes... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wmissing_prototypes+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wmissing-prototypes\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wmissing_prototypes=yes\nelse\n  citusac_cv_prog_cc_cflags__Wmissing_prototypes=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wmissing_prototypes\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wmissing_prototypes\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wmissing_prototypes\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wmissing-prototypes\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wshadow\" >&5\n$as_echo_n \"checking whether $CC supports -Wshadow... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Wshadow+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Wshadow\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Wshadow=yes\nelse\n  citusac_cv_prog_cc_cflags__Wshadow=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Wshadow\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Wshadow\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Wshadow\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Wshadow\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=vla\" >&5\n$as_echo_n \"checking whether $CC supports -Werror=vla... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Werror_vla+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Werror=vla\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Werror_vla=yes\nelse\n  citusac_cv_prog_cc_cflags__Werror_vla=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Werror_vla\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Werror_vla\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Werror_vla\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Werror=vla\"\nfi\n  # visual studio does not support these\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=implicit-int\" >&5\n$as_echo_n \"checking whether $CC supports -Werror=implicit-int... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Werror_implicit_int+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Werror=implicit-int\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Werror_implicit_int=yes\nelse\n  citusac_cv_prog_cc_cflags__Werror_implicit_int=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Werror_implicit_int\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Werror_implicit_int\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Werror_implicit_int\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Werror=implicit-int\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=implicit-function-declaration\" >&5\n$as_echo_n \"checking whether $CC supports -Werror=implicit-function-declaration... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Werror_implicit_function_declaration+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Werror=implicit-function-declaration\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Werror_implicit_function_declaration=yes\nelse\n  citusac_cv_prog_cc_cflags__Werror_implicit_function_declaration=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Werror_implicit_function_declaration\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Werror_implicit_function_declaration\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Werror_implicit_function_declaration\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Werror=implicit-function-declaration\"\nfi\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror=return-type\" >&5\n$as_echo_n \"checking whether $CC supports -Werror=return-type... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__Werror_return_type+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-Werror=return-type\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__Werror_return_type=yes\nelse\n  citusac_cv_prog_cc_cflags__Werror_return_type=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__Werror_return_type\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__Werror_return_type\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__Werror_return_type\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -Werror=return-type\"\nfi\n\n# Security flags\n# Flags taken from: https://liquid.microsoft.com/Web/Object/Read/ms.security/Requirements/Microsoft.Security.SystemsADM.10203#guide\n# We do not enforce the following flag because it is only available on GCC>=8\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking whether $CC supports -fstack-clash-protection\" >&5\n$as_echo_n \"checking whether $CC supports -fstack-clash-protection... \" >&6; }\nif ${citusac_cv_prog_cc_cflags__fstack_clash_protection+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  citusac_save_CFLAGS=$CFLAGS\nflag=-fstack-clash-protection\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\nint\nmain ()\n{\n\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_compile \"$LINENO\"; then :\n  citusac_cv_prog_cc_cflags__fstack_clash_protection=yes\nelse\n  citusac_cv_prog_cc_cflags__fstack_clash_protection=no\nfi\nrm -f core conftest.err conftest.$ac_objext conftest.$ac_ext\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $citusac_cv_prog_cc_cflags__fstack_clash_protection\" >&5\n$as_echo \"$citusac_cv_prog_cc_cflags__fstack_clash_protection\" >&6; }\nif test x\"$citusac_cv_prog_cc_cflags__fstack_clash_protection\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS -fstack-clash-protection\"\nfi\n\n\n#\n# --enable-coverage enables generation of code coverage metrics with gcov\n#\n# Check whether --enable-coverage was given.\nif test \"${enable_coverage+set}\" = set; then :\n  enableval=$enable_coverage;\nfi\n\nif test \"$enable_coverage\" = yes; then\n    CITUS_CFLAGS=\"$CITUS_CFLAGS -O0 -g --coverage\"\n    CITUS_CPPFLAGS=\"$CITUS_CPPFLAGS -DNDEBUG\"\n    CITUS_LDFLAGS=\"$CITUS_LDFLAGS --coverage\"\nfi\n\n#\n# LZ4\n#\n\n\n\n# Check whether --with-lz4 was given.\nif test \"${with_lz4+set}\" = set; then :\n  withval=$with_lz4;\n  case $withval in\n    yes)\n\n$as_echo \"#define HAVE_CITUS_LIBLZ4 1\" >>confdefs.h\n\n      ;;\n    no)\n      :\n      ;;\n    *)\n      as_fn_error $? \"no argument expected for --with-lz4 option\" \"$LINENO\" 5\n      ;;\n  esac\n\nelse\n  with_lz4=yes\n\n$as_echo \"#define HAVE_CITUS_LIBLZ4 1\" >>confdefs.h\n\nfi\n\n\n\n\nif test \"$with_lz4\" = yes; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for LZ4_compress_default in -llz4\" >&5\n$as_echo_n \"checking for LZ4_compress_default in -llz4... \" >&6; }\nif ${ac_cv_lib_lz4_LZ4_compress_default+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-llz4  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar LZ4_compress_default ();\nint\nmain ()\n{\nreturn LZ4_compress_default ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_lz4_LZ4_compress_default=yes\nelse\n  ac_cv_lib_lz4_LZ4_compress_default=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lz4_LZ4_compress_default\" >&5\n$as_echo \"$ac_cv_lib_lz4_LZ4_compress_default\" >&6; }\nif test \"x$ac_cv_lib_lz4_LZ4_compress_default\" = xyes; then :\n  cat >>confdefs.h <<_ACEOF\n#define HAVE_LIBLZ4 1\n_ACEOF\n\n  LIBS=\"-llz4 $LIBS\"\n\nelse\n  as_fn_error $? \"lz4 library not found\nIf you have lz4 installed, see config.log for details on the\nfailure.  It is possible the compiler isn't looking in the proper directory.\nUse --without-lz4 to disable lz4 support.\" \"$LINENO\" 5\nfi\n\n  ac_fn_c_check_header_mongrel \"$LINENO\" \"lz4.h\" \"ac_cv_header_lz4_h\" \"$ac_includes_default\"\nif test \"x$ac_cv_header_lz4_h\" = xyes; then :\n\nelse\n  as_fn_error $? \"lz4 header not found\nIf you have lz4 already installed, see config.log for details on the\nfailure.  It is possible the compiler isn't looking in the proper directory.\nUse --without-lz4 to disable lz4 support.\" \"$LINENO\" 5\nfi\n\n\nfi\n\n#\n# ZSTD\n#\n\n\n\n# Check whether --with-zstd was given.\nif test \"${with_zstd+set}\" = set; then :\n  withval=$with_zstd;\n  case $withval in\n    yes)\n      :\n      ;;\n    no)\n      :\n      ;;\n    *)\n      as_fn_error $? \"no argument expected for --with-zstd option\" \"$LINENO\" 5\n      ;;\n  esac\n\nelse\n  with_zstd=yes\n\nfi\n\n\n\n\nif test \"$with_zstd\" = yes; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: checking for ZSTD_decompress in -lzstd\" >&5\n$as_echo_n \"checking for ZSTD_decompress in -lzstd... \" >&6; }\nif ${ac_cv_lib_zstd_ZSTD_decompress+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  ac_check_lib_save_LIBS=$LIBS\nLIBS=\"-lzstd  $LIBS\"\ncat confdefs.h - <<_ACEOF >conftest.$ac_ext\n/* end confdefs.h.  */\n\n/* Override any GCC internal prototype to avoid an error.\n   Use char because int might match the return type of a GCC\n   builtin and then its argument prototype would still apply.  */\n#ifdef __cplusplus\nextern \"C\"\n#endif\nchar ZSTD_decompress ();\nint\nmain ()\n{\nreturn ZSTD_decompress ();\n  ;\n  return 0;\n}\n_ACEOF\nif ac_fn_c_try_link \"$LINENO\"; then :\n  ac_cv_lib_zstd_ZSTD_decompress=yes\nelse\n  ac_cv_lib_zstd_ZSTD_decompress=no\nfi\nrm -f core conftest.err conftest.$ac_objext \\\n    conftest$ac_exeext conftest.$ac_ext\nLIBS=$ac_check_lib_save_LIBS\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_zstd_ZSTD_decompress\" >&5\n$as_echo \"$ac_cv_lib_zstd_ZSTD_decompress\" >&6; }\nif test \"x$ac_cv_lib_zstd_ZSTD_decompress\" = xyes; then :\n  cat >>confdefs.h <<_ACEOF\n#define HAVE_LIBZSTD 1\n_ACEOF\n\n  LIBS=\"-lzstd $LIBS\"\n\nelse\n  as_fn_error $? \"zstd library not found\nIf you have zstd installed, see config.log for details on the\nfailure.  It is possible the compiler isn't looking in the proper directory.\nUse --without-zstd to disable zstd support.\" \"$LINENO\" 5\nfi\n\n  ac_fn_c_check_header_mongrel \"$LINENO\" \"zstd.h\" \"ac_cv_header_zstd_h\" \"$ac_includes_default\"\nif test \"x$ac_cv_header_zstd_h\" = xyes; then :\n\nelse\n  as_fn_error $? \"zstd header not found\nIf you have zstd already installed, see config.log for details on the\nfailure.  It is possible the compiler isn't looking in the proper directory.\nUse --without-zstd to disable zstd support.\" \"$LINENO\" 5\nfi\n\n\nfi\n\n\n\n\n\n# Check whether --with-security-flags was given.\nif test \"${with_security_flags+set}\" = set; then :\n  withval=$with_security_flags;\n  case $withval in\n    yes)\n      :\n      ;;\n    no)\n      :\n      ;;\n    *)\n      as_fn_error $? \"no argument expected for --with-security-flags option\" \"$LINENO\" 5\n      ;;\n  esac\n\nelse\n  with_security_flags=no\n\nfi\n\n\n\n\nif test \"$with_security_flags\" = yes; then\n# Flags taken from: https://liquid.microsoft.com/Web/Object/Read/ms.security/Requirements/Microsoft.Security.SystemsADM.10203#guide\n\n# We always want to have some compiler flags for security concerns.\nSECURITY_CFLAGS=\"-fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 -z noexecstack -fpic -shared -Wl,-z,relro -Wl,-z,now -Wformat -Wformat-security -Werror=format-security\"\nCITUS_CFLAGS=\"$CITUS_CFLAGS $SECURITY_CFLAGS\"\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: Blindly added security flags for linker: $SECURITY_CFLAGS\" >&5\n$as_echo \"$as_me: Blindly added security flags for linker: $SECURITY_CFLAGS\" >&6;}\n\n# We always want to have some clang flags for security concerns.\n# This doesn't include \"-Wl,-z,relro -Wl,-z,now\" on purpuse, because bitcode is not linked.\n# This doesn't include -fsanitize=cfi because it breaks builds on many distros including\n# Debian/Buster, Debian/Stretch, Ubuntu/Bionic, Ubuntu/Xenial and EL7.\nSECURITY_BITCODE_CFLAGS=\"-fsanitize=safe-stack -fstack-protector-strong -flto -fPIC -Wformat -Wformat-security -Werror=format-security\"\nCITUS_BITCODE_CFLAGS=\"$CITUS_BITCODE_CFLAGS $SECURITY_BITCODE_CFLAGS\"\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: Blindly added security flags for llvm: $SECURITY_BITCODE_CFLAGS\" >&5\n$as_echo \"$as_me: Blindly added security flags for llvm: $SECURITY_BITCODE_CFLAGS\" >&6;}\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: If you run into issues during linking or bitcode compilation, you can use --without-security-flags.\" >&5\n$as_echo \"$as_me: WARNING: If you run into issues during linking or bitcode compilation, you can use --without-security-flags.\" >&2;}\nfi\n\n# Check if git is installed, when installed the gitref of the checkout will be baked in the application\n# Extract the first word of \"git\", so it can be a program name with args.\nset dummy git; ac_word=$2\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for $ac_word\" >&5\n$as_echo_n \"checking for $ac_word... \" >&6; }\nif ${ac_cv_path_GIT_BIN+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  case $GIT_BIN in\n  [\\\\/]* | ?:[\\\\/]*)\n  ac_cv_path_GIT_BIN=\"$GIT_BIN\" # Let the user override the test with a path.\n  ;;\n  *)\n  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    for ac_exec_ext in '' $ac_executable_extensions; do\n  if as_fn_executable_p \"$as_dir/$ac_word$ac_exec_ext\"; then\n    ac_cv_path_GIT_BIN=\"$as_dir/$ac_word$ac_exec_ext\"\n    $as_echo \"$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext\" >&5\n    break 2\n  fi\ndone\n  done\nIFS=$as_save_IFS\n\n  ;;\nesac\nfi\nGIT_BIN=$ac_cv_path_GIT_BIN\nif test -n \"$GIT_BIN\"; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: $GIT_BIN\" >&5\n$as_echo \"$GIT_BIN\" >&6; }\nelse\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: result: no\" >&5\n$as_echo \"no\" >&6; }\nfi\n\n\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: checking for .git\" >&5\n$as_echo_n \"checking for .git... \" >&6; }\nif ${ac_cv_file__git+:} false; then :\n  $as_echo_n \"(cached) \" >&6\nelse\n  test \"$cross_compiling\" = yes &&\n  as_fn_error $? \"cannot check for file existence when cross compiling\" \"$LINENO\" 5\nif test -r \".git\"; then\n  ac_cv_file__git=yes\nelse\n  ac_cv_file__git=no\nfi\nfi\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: result: $ac_cv_file__git\" >&5\n$as_echo \"$ac_cv_file__git\" >&6; }\nif test \"x$ac_cv_file__git\" = xyes; then :\n  HAS_DOTGIT=yes\nelse\n  HAS_DOTGIT=\nfi\n\n\nCITUS_CFLAGS=\"$CITUS_CFLAGS\"\n\nCITUS_BITCODE_CFLAGS=\"$CITUS_BITCODE_CFLAGS\"\n\nCITUS_CPPFLAGS=\"$CITUS_CPPFLAGS\"\n\nCITUS_LDFLAGS=\"$LIBS $CITUS_LDFLAGS\"\n\nPOSTGRES_SRCDIR=\"$POSTGRES_SRCDIR\"\n\nPOSTGRES_BUILDDIR=\"$POSTGRES_BUILDDIR\"\n\nHAS_DOTGIT=\"$HAS_DOTGIT\"\n\n\nac_config_files=\"$ac_config_files Makefile.global\"\n\nac_config_headers=\"$ac_config_headers src/include/citus_config.h src/include/citus_version.h\"\n\n\ncat >confcache <<\\_ACEOF\n# This file is a shell script that caches the results of configure\n# tests run on this system so they can be shared between configure\n# scripts and configure runs, see configure's option --config-cache.\n# It is not useful on other systems.  If it contains results you don't\n# want to keep, you may remove or edit it.\n#\n# config.status only pays attention to the cache file if you give it\n# the --recheck option to rerun configure.\n#\n# `ac_cv_env_foo' variables (set or unset) will be overridden when\n# loading this file, other *unset* `ac_cv_foo' will be assigned the\n# following values.\n\n_ACEOF\n\n# The following way of writing the cache mishandles newlines in values,\n# but we know of no workaround that is simple, portable, and efficient.\n# So, we kill variables containing newlines.\n# Ultrix sh set writes to stderr and can't be redirected directly,\n# and sets the high bit in the cache file unless we assign to the vars.\n(\n  for ac_var in `(set) 2>&1 | sed -n 's/^\\([a-zA-Z_][a-zA-Z0-9_]*\\)=.*/\\1/p'`; do\n    eval ac_val=\\$$ac_var\n    case $ac_val in #(\n    *${as_nl}*)\n      case $ac_var in #(\n      *_cv_*) { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline\" >&5\n$as_echo \"$as_me: WARNING: cache variable $ac_var contains a newline\" >&2;} ;;\n      esac\n      case $ac_var in #(\n      _ | IFS | as_nl) ;; #(\n      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(\n      *) { eval $ac_var=; unset $ac_var;} ;;\n      esac ;;\n    esac\n  done\n\n  (set) 2>&1 |\n    case $as_nl`(ac_space=' '; set) 2>&1` in #(\n    *${as_nl}ac_space=\\ *)\n      # `set' does not quote correctly, so add quotes: double-quote\n      # substitution turns \\\\\\\\ into \\\\, and sed turns \\\\ into \\.\n      sed -n \\\n\t\"s/'/'\\\\\\\\''/g;\n\t  s/^\\\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\\\)=\\\\(.*\\\\)/\\\\1='\\\\2'/p\"\n      ;; #(\n    *)\n      # `set' quotes correctly as required by POSIX, so do not add quotes.\n      sed -n \"/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p\"\n      ;;\n    esac |\n    sort\n) |\n  sed '\n     /^ac_cv_env_/b end\n     t clear\n     :clear\n     s/^\\([^=]*\\)=\\(.*[{}].*\\)$/test \"${\\1+set}\" = set || &/\n     t end\n     s/^\\([^=]*\\)=\\(.*\\)$/\\1=${\\1=\\2}/\n     :end' >>confcache\nif diff \"$cache_file\" confcache >/dev/null 2>&1; then :; else\n  if test -w \"$cache_file\"; then\n    if test \"x$cache_file\" != \"x/dev/null\"; then\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: updating cache $cache_file\" >&5\n$as_echo \"$as_me: updating cache $cache_file\" >&6;}\n      if test ! -f \"$cache_file\" || test -h \"$cache_file\"; then\n\tcat confcache >\"$cache_file\"\n      else\n        case $cache_file in #(\n        */* | ?:*)\n\t  mv -f confcache \"$cache_file\"$$ &&\n\t  mv -f \"$cache_file\"$$ \"$cache_file\" ;; #(\n        *)\n\t  mv -f confcache \"$cache_file\" ;;\n\tesac\n      fi\n    fi\n  else\n    { $as_echo \"$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file\" >&5\n$as_echo \"$as_me: not updating unwritable cache $cache_file\" >&6;}\n  fi\nfi\nrm -f confcache\n\ntest \"x$prefix\" = xNONE && prefix=$ac_default_prefix\n# Let make expand exec_prefix.\ntest \"x$exec_prefix\" = xNONE && exec_prefix='${prefix}'\n\nDEFS=-DHAVE_CONFIG_H\n\nac_libobjs=\nac_ltlibobjs=\nU=\nfor ac_i in : $LIBOBJS; do test \"x$ac_i\" = x: && continue\n  # 1. Remove the extension, and $U if already installed.\n  ac_script='s/\\$U\\././;s/\\.o$//;s/\\.obj$//'\n  ac_i=`$as_echo \"$ac_i\" | sed \"$ac_script\"`\n  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR\n  #    will be set to the directory where LIBOBJS objects are built.\n  as_fn_append ac_libobjs \" \\${LIBOBJDIR}$ac_i\\$U.$ac_objext\"\n  as_fn_append ac_ltlibobjs \" \\${LIBOBJDIR}$ac_i\"'$U.lo'\ndone\nLIBOBJS=$ac_libobjs\n\nLTLIBOBJS=$ac_ltlibobjs\n\n\n\n: \"${CONFIG_STATUS=./config.status}\"\nac_write_fail=0\nac_clean_files_save=$ac_clean_files\nac_clean_files=\"$ac_clean_files $CONFIG_STATUS\"\n{ $as_echo \"$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS\" >&5\n$as_echo \"$as_me: creating $CONFIG_STATUS\" >&6;}\nas_write_fail=0\ncat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1\n#! $SHELL\n# Generated by $as_me.\n# Run this file to recreate the current configuration.\n# Compiler output produced by configure, useful for debugging\n# configure, is in config.log if it exists.\n\ndebug=false\nac_cs_recheck=false\nac_cs_silent=false\n\nSHELL=\\${CONFIG_SHELL-$SHELL}\nexport SHELL\n_ASEOF\ncat >>$CONFIG_STATUS <<\\_ASEOF || as_write_fail=1\n## -------------------- ##\n## M4sh Initialization. ##\n## -------------------- ##\n\n# Be more Bourne compatible\nDUALCASE=1; export DUALCASE # for MKS sh\nif test -n \"${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :\n  emulate sh\n  NULLCMD=:\n  # Pre-4.2 versions of Zsh do word splitting on ${1+\"$@\"}, which\n  # is contrary to our usage.  Disable this feature.\n  alias -g '${1+\"$@\"}'='\"$@\"'\n  setopt NO_GLOB_SUBST\nelse\n  case `(set -o) 2>/dev/null` in #(\n  *posix*) :\n    set -o posix ;; #(\n  *) :\n     ;;\nesac\nfi\n\n\nas_nl='\n'\nexport as_nl\n# Printing a long string crashes Solaris 7 /usr/bin/printf.\nas_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo\nas_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo\n# Prefer a ksh shell builtin over an external printf program on Solaris,\n# but without wasting forks for bash or zsh.\nif test -z \"$BASH_VERSION$ZSH_VERSION\" \\\n    && (test \"X`print -r -- $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='print -r --'\n  as_echo_n='print -rn --'\nelif (test \"X`printf %s $as_echo`\" = \"X$as_echo\") 2>/dev/null; then\n  as_echo='printf %s\\n'\n  as_echo_n='printf %s'\nelse\n  if test \"X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`\" = \"X-n $as_echo\"; then\n    as_echo_body='eval /usr/ucb/echo -n \"$1$as_nl\"'\n    as_echo_n='/usr/ucb/echo -n'\n  else\n    as_echo_body='eval expr \"X$1\" : \"X\\\\(.*\\\\)\"'\n    as_echo_n_body='eval\n      arg=$1;\n      case $arg in #(\n      *\"$as_nl\"*)\n\texpr \"X$arg\" : \"X\\\\(.*\\\\)$as_nl\";\n\targ=`expr \"X$arg\" : \".*$as_nl\\\\(.*\\\\)\"`;;\n      esac;\n      expr \"X$arg\" : \"X\\\\(.*\\\\)\" | tr -d \"$as_nl\"\n    '\n    export as_echo_n_body\n    as_echo_n='sh -c $as_echo_n_body as_echo'\n  fi\n  export as_echo_body\n  as_echo='sh -c $as_echo_body as_echo'\nfi\n\n# The user is always right.\nif test \"${PATH_SEPARATOR+set}\" != set; then\n  PATH_SEPARATOR=:\n  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {\n    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||\n      PATH_SEPARATOR=';'\n  }\nfi\n\n\n# IFS\n# We need space, tab and new line, in precisely that order.  Quoting is\n# there to prevent editors from complaining about space-tab.\n# (If _AS_PATH_WALK were called with IFS unset, it would disable word\n# splitting by setting IFS to empty value.)\nIFS=\" \"\"\t$as_nl\"\n\n# Find who we are.  Look in the path if we contain no directory separator.\nas_myself=\ncase $0 in #((\n  *[\\\\/]* ) as_myself=$0 ;;\n  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR\nfor as_dir in $PATH\ndo\n  IFS=$as_save_IFS\n  test -z \"$as_dir\" && as_dir=.\n    test -r \"$as_dir/$0\" && as_myself=$as_dir/$0 && break\n  done\nIFS=$as_save_IFS\n\n     ;;\nesac\n# We did not find ourselves, most probably we were run as `sh COMMAND'\n# in which case we are not to be found in the path.\nif test \"x$as_myself\" = x; then\n  as_myself=$0\nfi\nif test ! -f \"$as_myself\"; then\n  $as_echo \"$as_myself: error: cannot find myself; rerun with an absolute file name\" >&2\n  exit 1\nfi\n\n# Unset variables that we do not need and which cause bugs (e.g. in\n# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the \"|| exit 1\"\n# suppresses any \"Segmentation fault\" message there.  '((' could\n# trigger a bug in pdksh 5.2.14.\nfor as_var in BASH_ENV ENV MAIL MAILPATH\ndo eval test x\\${$as_var+set} = xset \\\n  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :\ndone\nPS1='$ '\nPS2='> '\nPS4='+ '\n\n# NLS nuisances.\nLC_ALL=C\nexport LC_ALL\nLANGUAGE=C\nexport LANGUAGE\n\n# CDPATH.\n(unset CDPATH) >/dev/null 2>&1 && unset CDPATH\n\n\n# as_fn_error STATUS ERROR [LINENO LOG_FD]\n# ----------------------------------------\n# Output \"`basename $0`: error: ERROR\" to stderr. If LINENO and LOG_FD are\n# provided, also output the error to LOG_FD, referencing LINENO. Then exit the\n# script with STATUS, using 1 if that was 0.\nas_fn_error ()\n{\n  as_status=$1; test $as_status -eq 0 && as_status=1\n  if test \"$4\"; then\n    as_lineno=${as_lineno-\"$3\"} as_lineno_stack=as_lineno_stack=$as_lineno_stack\n    $as_echo \"$as_me:${as_lineno-$LINENO}: error: $2\" >&$4\n  fi\n  $as_echo \"$as_me: error: $2\" >&2\n  as_fn_exit $as_status\n} # as_fn_error\n\n\n# as_fn_set_status STATUS\n# -----------------------\n# Set $? to STATUS, without forking.\nas_fn_set_status ()\n{\n  return $1\n} # as_fn_set_status\n\n# as_fn_exit STATUS\n# -----------------\n# Exit the shell with STATUS, even in a \"trap 0\" or \"set -e\" context.\nas_fn_exit ()\n{\n  set +e\n  as_fn_set_status $1\n  exit $1\n} # as_fn_exit\n\n# as_fn_unset VAR\n# ---------------\n# Portably unset VAR.\nas_fn_unset ()\n{\n  { eval $1=; unset $1;}\n}\nas_unset=as_fn_unset\n# as_fn_append VAR VALUE\n# ----------------------\n# Append the text in VALUE to the end of the definition contained in VAR. Take\n# advantage of any shell optimizations that allow amortized linear growth over\n# repeated appends, instead of the typical quadratic growth present in naive\n# implementations.\nif (eval \"as_var=1; as_var+=2; test x\\$as_var = x12\") 2>/dev/null; then :\n  eval 'as_fn_append ()\n  {\n    eval $1+=\\$2\n  }'\nelse\n  as_fn_append ()\n  {\n    eval $1=\\$$1\\$2\n  }\nfi # as_fn_append\n\n# as_fn_arith ARG...\n# ------------------\n# Perform arithmetic evaluation on the ARGs, and store the result in the\n# global $as_val. Take advantage of shells that can avoid forks. The arguments\n# must be portable across $(()) and expr.\nif (eval \"test \\$(( 1 + 1 )) = 2\") 2>/dev/null; then :\n  eval 'as_fn_arith ()\n  {\n    as_val=$(( $* ))\n  }'\nelse\n  as_fn_arith ()\n  {\n    as_val=`expr \"$@\" || test $? -eq 1`\n  }\nfi # as_fn_arith\n\n\nif expr a : '\\(a\\)' >/dev/null 2>&1 &&\n   test \"X`expr 00001 : '.*\\(...\\)'`\" = X001; then\n  as_expr=expr\nelse\n  as_expr=false\nfi\n\nif (basename -- /) >/dev/null 2>&1 && test \"X`basename -- / 2>&1`\" = \"X/\"; then\n  as_basename=basename\nelse\n  as_basename=false\nfi\n\nif (as_dir=`dirname -- /` && test \"X$as_dir\" = X/) >/dev/null 2>&1; then\n  as_dirname=dirname\nelse\n  as_dirname=false\nfi\n\nas_me=`$as_basename -- \"$0\" ||\n$as_expr X/\"$0\" : '.*/\\([^/][^/]*\\)/*$' \\| \\\n\t X\"$0\" : 'X\\(//\\)$' \\| \\\n\t X\"$0\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X/\"$0\" |\n    sed '/^.*\\/\\([^/][^/]*\\)\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\/\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n\n# Avoid depending upon Character Ranges.\nas_cr_letters='abcdefghijklmnopqrstuvwxyz'\nas_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'\nas_cr_Letters=$as_cr_letters$as_cr_LETTERS\nas_cr_digits='0123456789'\nas_cr_alnum=$as_cr_Letters$as_cr_digits\n\nECHO_C= ECHO_N= ECHO_T=\ncase `echo -n x` in #(((((\n-n*)\n  case `echo 'xy\\c'` in\n  *c*) ECHO_T='\t';;\t# ECHO_T is single tab character.\n  xy)  ECHO_C='\\c';;\n  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null\n       ECHO_T='\t';;\n  esac;;\n*)\n  ECHO_N='-n';;\nesac\n\nrm -f conf$$ conf$$.exe conf$$.file\nif test -d conf$$.dir; then\n  rm -f conf$$.dir/conf$$.file\nelse\n  rm -f conf$$.dir\n  mkdir conf$$.dir 2>/dev/null\nfi\nif (echo >conf$$.file) 2>/dev/null; then\n  if ln -s conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s='ln -s'\n    # ... but there are two gotchas:\n    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.\n    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.\n    # In both cases, we have to default to `cp -pR'.\n    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||\n      as_ln_s='cp -pR'\n  elif ln conf$$.file conf$$ 2>/dev/null; then\n    as_ln_s=ln\n  else\n    as_ln_s='cp -pR'\n  fi\nelse\n  as_ln_s='cp -pR'\nfi\nrm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file\nrmdir conf$$.dir 2>/dev/null\n\n\n# as_fn_mkdir_p\n# -------------\n# Create \"$as_dir\" as a directory, including parents if necessary.\nas_fn_mkdir_p ()\n{\n\n  case $as_dir in #(\n  -*) as_dir=./$as_dir;;\n  esac\n  test -d \"$as_dir\" || eval $as_mkdir_p || {\n    as_dirs=\n    while :; do\n      case $as_dir in #(\n      *\\'*) as_qdir=`$as_echo \"$as_dir\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"`;; #'(\n      *) as_qdir=$as_dir;;\n      esac\n      as_dirs=\"'$as_qdir' $as_dirs\"\n      as_dir=`$as_dirname -- \"$as_dir\" ||\n$as_expr X\"$as_dir\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$as_dir\" : 'X\\(//\\)$' \\| \\\n\t X\"$as_dir\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$as_dir\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n      test -d \"$as_dir\" && break\n    done\n    test -z \"$as_dirs\" || eval \"mkdir $as_dirs\"\n  } || test -d \"$as_dir\" || as_fn_error $? \"cannot create directory $as_dir\"\n\n\n} # as_fn_mkdir_p\nif mkdir -p . 2>/dev/null; then\n  as_mkdir_p='mkdir -p \"$as_dir\"'\nelse\n  test -d ./-p && rmdir ./-p\n  as_mkdir_p=false\nfi\n\n\n# as_fn_executable_p FILE\n# -----------------------\n# Test if FILE is an executable regular file.\nas_fn_executable_p ()\n{\n  test -f \"$1\" && test -x \"$1\"\n} # as_fn_executable_p\nas_test_x='test -x'\nas_executable_p=as_fn_executable_p\n\n# Sed expression to map a string onto a valid CPP name.\nas_tr_cpp=\"eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'\"\n\n# Sed expression to map a string onto a valid variable name.\nas_tr_sh=\"eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'\"\n\n\nexec 6>&1\n## ----------------------------------- ##\n## Main body of $CONFIG_STATUS script. ##\n## ----------------------------------- ##\n_ASEOF\ntest $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n# Save the log message, to keep $0 and so on meaningful, and to\n# report actual input values of CONFIG_FILES etc. instead of their\n# values after options handling.\nac_log=\"\nThis file was extended by Citus $as_me 15.0devel, which was\ngenerated by GNU Autoconf 2.69.  Invocation command line was\n\n  CONFIG_FILES    = $CONFIG_FILES\n  CONFIG_HEADERS  = $CONFIG_HEADERS\n  CONFIG_LINKS    = $CONFIG_LINKS\n  CONFIG_COMMANDS = $CONFIG_COMMANDS\n  $ $0 $@\n\non `(hostname || uname -n) 2>/dev/null | sed 1q`\n\"\n\n_ACEOF\n\ncase $ac_config_files in *\"\n\"*) set x $ac_config_files; shift; ac_config_files=$*;;\nesac\n\ncase $ac_config_headers in *\"\n\"*) set x $ac_config_headers; shift; ac_config_headers=$*;;\nesac\n\n\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n# Files that config.status was made for.\nconfig_files=\"$ac_config_files\"\nconfig_headers=\"$ac_config_headers\"\n\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nac_cs_usage=\"\\\n\\`$as_me' instantiates files and other configuration actions\nfrom templates according to the current configuration.  Unless the files\nand actions are specified as TAGs, all are instantiated by default.\n\nUsage: $0 [OPTION]... [TAG]...\n\n  -h, --help       print this help, then exit\n  -V, --version    print version number and configuration settings, then exit\n      --config     print configuration, then exit\n  -q, --quiet, --silent\n                   do not print progress messages\n  -d, --debug      don't remove temporary files\n      --recheck    update $as_me by reconfiguring in the same conditions\n      --file=FILE[:TEMPLATE]\n                   instantiate the configuration file FILE\n      --header=FILE[:TEMPLATE]\n                   instantiate the configuration header FILE\n\nConfiguration files:\n$config_files\n\nConfiguration headers:\n$config_headers\n\nReport bugs to the package provider.\"\n\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\nac_cs_config=\"`$as_echo \"$ac_configure_args\" | sed 's/^ //; s/[\\\\\"\"\\`\\$]/\\\\\\\\&/g'`\"\nac_cs_version=\"\\\\\nCitus config.status 15.0devel\nconfigured by $0, generated by GNU Autoconf 2.69,\n  with options \\\\\"\\$ac_cs_config\\\\\"\n\nCopyright (C) 2012 Free Software Foundation, Inc.\nThis config.status script is free software; the Free Software Foundation\ngives unlimited permission to copy, distribute and modify it.\"\n\nac_pwd='$ac_pwd'\nsrcdir='$srcdir'\nAWK='$AWK'\ntest -n \"\\$AWK\" || AWK=awk\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n# The default lists apply if the user does not specify any file.\nac_need_defaults=:\nwhile test $# != 0\ndo\n  case $1 in\n  --*=?*)\n    ac_option=`expr \"X$1\" : 'X\\([^=]*\\)='`\n    ac_optarg=`expr \"X$1\" : 'X[^=]*=\\(.*\\)'`\n    ac_shift=:\n    ;;\n  --*=)\n    ac_option=`expr \"X$1\" : 'X\\([^=]*\\)='`\n    ac_optarg=\n    ac_shift=:\n    ;;\n  *)\n    ac_option=$1\n    ac_optarg=$2\n    ac_shift=shift\n    ;;\n  esac\n\n  case $ac_option in\n  # Handling of the options.\n  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)\n    ac_cs_recheck=: ;;\n  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )\n    $as_echo \"$ac_cs_version\"; exit ;;\n  --config | --confi | --conf | --con | --co | --c )\n    $as_echo \"$ac_cs_config\"; exit ;;\n  --debug | --debu | --deb | --de | --d | -d )\n    debug=: ;;\n  --file | --fil | --fi | --f )\n    $ac_shift\n    case $ac_optarg in\n    *\\'*) ac_optarg=`$as_echo \"$ac_optarg\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    '') as_fn_error $? \"missing file argument\" ;;\n    esac\n    as_fn_append CONFIG_FILES \" '$ac_optarg'\"\n    ac_need_defaults=false;;\n  --header | --heade | --head | --hea )\n    $ac_shift\n    case $ac_optarg in\n    *\\'*) ac_optarg=`$as_echo \"$ac_optarg\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"` ;;\n    esac\n    as_fn_append CONFIG_HEADERS \" '$ac_optarg'\"\n    ac_need_defaults=false;;\n  --he | --h)\n    # Conflict between --help and --header\n    as_fn_error $? \"ambiguous option: \\`$1'\nTry \\`$0 --help' for more information.\";;\n  --help | --hel | -h )\n    $as_echo \"$ac_cs_usage\"; exit ;;\n  -q | -quiet | --quiet | --quie | --qui | --qu | --q \\\n  | -silent | --silent | --silen | --sile | --sil | --si | --s)\n    ac_cs_silent=: ;;\n\n  # This is an error.\n  -*) as_fn_error $? \"unrecognized option: \\`$1'\nTry \\`$0 --help' for more information.\" ;;\n\n  *) as_fn_append ac_config_targets \" $1\"\n     ac_need_defaults=false ;;\n\n  esac\n  shift\ndone\n\nac_configure_extra_args=\n\nif $ac_cs_silent; then\n  exec 6>/dev/null\n  ac_configure_extra_args=\"$ac_configure_extra_args --silent\"\nfi\n\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\nif \\$ac_cs_recheck; then\n  set X $SHELL '$0' $ac_configure_args \\$ac_configure_extra_args --no-create --no-recursion\n  shift\n  \\$as_echo \"running CONFIG_SHELL=$SHELL \\$*\" >&6\n  CONFIG_SHELL='$SHELL'\n  export CONFIG_SHELL\n  exec \"\\$@\"\nfi\n\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nexec 5>>config.log\n{\n  echo\n  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX\n## Running $as_me. ##\n_ASBOX\n  $as_echo \"$ac_log\"\n} >&5\n\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n\n# Handling of arguments.\nfor ac_config_target in $ac_config_targets\ndo\n  case $ac_config_target in\n    \"Makefile.global\") CONFIG_FILES=\"$CONFIG_FILES Makefile.global\" ;;\n    \"src/include/citus_config.h\") CONFIG_HEADERS=\"$CONFIG_HEADERS src/include/citus_config.h\" ;;\n    \"src/include/citus_version.h\") CONFIG_HEADERS=\"$CONFIG_HEADERS src/include/citus_version.h\" ;;\n\n  *) as_fn_error $? \"invalid argument: \\`$ac_config_target'\" \"$LINENO\" 5;;\n  esac\ndone\n\n\n# If the user did not use the arguments to specify the items to instantiate,\n# then the envvar interface is used.  Set only those that are not.\n# We use the long form for the default assignment because of an extremely\n# bizarre bug on SunOS 4.1.3.\nif $ac_need_defaults; then\n  test \"${CONFIG_FILES+set}\" = set || CONFIG_FILES=$config_files\n  test \"${CONFIG_HEADERS+set}\" = set || CONFIG_HEADERS=$config_headers\nfi\n\n# Have a temporary directory for convenience.  Make it in the build tree\n# simply because there is no reason against having it here, and in addition,\n# creating and moving files from /tmp can sometimes cause problems.\n# Hook for its removal unless debugging.\n# Note that there is a small window in which the directory will not be cleaned:\n# after its creation but before its name has been assigned to `$tmp'.\n$debug ||\n{\n  tmp= ac_tmp=\n  trap 'exit_status=$?\n  : \"${ac_tmp:=$tmp}\"\n  { test ! -d \"$ac_tmp\" || rm -fr \"$ac_tmp\"; } && exit $exit_status\n' 0\n  trap 'as_fn_exit 1' 1 2 13 15\n}\n# Create a (secure) tmp directory for tmp files.\n\n{\n  tmp=`(umask 077 && mktemp -d \"./confXXXXXX\") 2>/dev/null` &&\n  test -d \"$tmp\"\n}  ||\n{\n  tmp=./conf$$-$RANDOM\n  (umask 077 && mkdir \"$tmp\")\n} || as_fn_error $? \"cannot create a temporary directory in .\" \"$LINENO\" 5\nac_tmp=$tmp\n\n# Set up the scripts for CONFIG_FILES section.\n# No need to generate them if there are no CONFIG_FILES.\n# This happens for instance with `./config.status config.h'.\nif test -n \"$CONFIG_FILES\"; then\n\n\nac_cr=`echo X | tr X '\\015'`\n# On cygwin, bash can eat \\r inside `` if the user requested igncr.\n# But we know of no other shell where ac_cr would be empty at this\n# point, so we can use a bashism as a fallback.\nif test \"x$ac_cr\" = x; then\n  eval ac_cr=\\$\\'\\\\r\\'\nfi\nac_cs_awk_cr=`$AWK 'BEGIN { print \"a\\rb\" }' </dev/null 2>/dev/null`\nif test \"$ac_cs_awk_cr\" = \"a${ac_cr}b\"; then\n  ac_cs_awk_cr='\\\\r'\nelse\n  ac_cs_awk_cr=$ac_cr\nfi\n\necho 'BEGIN {' >\"$ac_tmp/subs1.awk\" &&\n_ACEOF\n\n\n{\n  echo \"cat >conf$$subs.awk <<_ACEOF\" &&\n  echo \"$ac_subst_vars\" | sed 's/.*/&!$&$ac_delim/' &&\n  echo \"_ACEOF\"\n} >conf$$subs.sh ||\n  as_fn_error $? \"could not make $CONFIG_STATUS\" \"$LINENO\" 5\nac_delim_num=`echo \"$ac_subst_vars\" | grep -c '^'`\nac_delim='%!_!# '\nfor ac_last_try in false false false false false :; do\n  . ./conf$$subs.sh ||\n    as_fn_error $? \"could not make $CONFIG_STATUS\" \"$LINENO\" 5\n\n  ac_delim_n=`sed -n \"s/.*$ac_delim\\$/X/p\" conf$$subs.awk | grep -c X`\n  if test $ac_delim_n = $ac_delim_num; then\n    break\n  elif $ac_last_try; then\n    as_fn_error $? \"could not make $CONFIG_STATUS\" \"$LINENO\" 5\n  else\n    ac_delim=\"$ac_delim!$ac_delim _$ac_delim!! \"\n  fi\ndone\nrm -f conf$$subs.sh\n\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\ncat >>\"\\$ac_tmp/subs1.awk\" <<\\\\_ACAWK &&\n_ACEOF\nsed -n '\nh\ns/^/S[\"/; s/!.*/\"]=/\np\ng\ns/^[^!]*!//\n:repl\nt repl\ns/'\"$ac_delim\"'$//\nt delim\n:nl\nh\ns/\\(.\\{148\\}\\)..*/\\1/\nt more1\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\\\\n\"\\\\/\np\nn\nb repl\n:more1\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"\\\\/\np\ng\ns/.\\{148\\}//\nt nl\n:delim\nh\ns/\\(.\\{148\\}\\)..*/\\1/\nt more2\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"/\np\nb\n:more2\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"\\\\/\np\ng\ns/.\\{148\\}//\nt delim\n' <conf$$subs.awk | sed '\n/^[^\"\"]/{\n  N\n  s/\\n//\n}\n' >>$CONFIG_STATUS || ac_write_fail=1\nrm -f conf$$subs.awk\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n_ACAWK\ncat >>\"\\$ac_tmp/subs1.awk\" <<_ACAWK &&\n  for (key in S) S_is_set[key] = 1\n  FS = \"\u0007\"\n\n}\n{\n  line = $ 0\n  nfields = split(line, field, \"@\")\n  substed = 0\n  len = length(field[1])\n  for (i = 2; i < nfields; i++) {\n    key = field[i]\n    keylen = length(key)\n    if (S_is_set[key]) {\n      value = S[key]\n      line = substr(line, 1, len) \"\" value \"\" substr(line, len + keylen + 3)\n      len += length(value) + length(field[++i])\n      substed = 1\n    } else\n      len += 1 + keylen\n  }\n\n  print line\n}\n\n_ACAWK\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nif sed \"s/$ac_cr//\" < /dev/null > /dev/null 2>&1; then\n  sed \"s/$ac_cr\\$//; s/$ac_cr/$ac_cs_awk_cr/g\"\nelse\n  cat\nfi < \"$ac_tmp/subs1.awk\" > \"$ac_tmp/subs.awk\" \\\n  || as_fn_error $? \"could not setup config files machinery\" \"$LINENO\" 5\n_ACEOF\n\n# VPATH may cause trouble with some makes, so we remove sole $(srcdir),\n# ${srcdir} and @srcdir@ entries from VPATH if srcdir is \".\", strip leading and\n# trailing colons and then remove the whole line if VPATH becomes empty\n# (actually we leave an empty line to preserve line numbers).\nif test \"x$srcdir\" = x.; then\n  ac_vpsub='/^[\t ]*VPATH[\t ]*=[\t ]*/{\nh\ns///\ns/^/:/\ns/[\t ]*$/:/\ns/:\\$(srcdir):/:/g\ns/:\\${srcdir}:/:/g\ns/:@srcdir@:/:/g\ns/^:*//\ns/:*$//\nx\ns/\\(=[\t ]*\\).*/\\1/\nG\ns/\\n//\ns/^[^=]*=[\t ]*$//\n}'\nfi\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\nfi # test -n \"$CONFIG_FILES\"\n\n# Set up the scripts for CONFIG_HEADERS section.\n# No need to generate them if there are no CONFIG_HEADERS.\n# This happens for instance with `./config.status Makefile'.\nif test -n \"$CONFIG_HEADERS\"; then\ncat >\"$ac_tmp/defines.awk\" <<\\_ACAWK ||\nBEGIN {\n_ACEOF\n\n# Transform confdefs.h into an awk script `defines.awk', embedded as\n# here-document in config.status, that substitutes the proper values into\n# config.h.in to produce config.h.\n\n# Create a delimiter string that does not exist in confdefs.h, to ease\n# handling of long lines.\nac_delim='%!_!# '\nfor ac_last_try in false false :; do\n  ac_tt=`sed -n \"/$ac_delim/p\" confdefs.h`\n  if test -z \"$ac_tt\"; then\n    break\n  elif $ac_last_try; then\n    as_fn_error $? \"could not make $CONFIG_HEADERS\" \"$LINENO\" 5\n  else\n    ac_delim=\"$ac_delim!$ac_delim _$ac_delim!! \"\n  fi\ndone\n\n# For the awk script, D is an array of macro values keyed by name,\n# likewise P contains macro parameters if any.  Preserve backslash\n# newline sequences.\n\nac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*\nsed -n '\ns/.\\{148\\}/&'\"$ac_delim\"'/g\nt rset\n:rset\ns/^[\t ]*#[\t ]*define[\t ][\t ]*/ /\nt def\nd\n:def\ns/\\\\$//\nt bsnl\ns/[\"\\\\]/\\\\&/g\ns/^ \\('\"$ac_word_re\"'\\)\\(([^()]*)\\)[\t ]*\\(.*\\)/P[\"\\1\"]=\"\\2\"\\\nD[\"\\1\"]=\" \\3\"/p\ns/^ \\('\"$ac_word_re\"'\\)[\t ]*\\(.*\\)/D[\"\\1\"]=\" \\2\"/p\nd\n:bsnl\ns/[\"\\\\]/\\\\&/g\ns/^ \\('\"$ac_word_re\"'\\)\\(([^()]*)\\)[\t ]*\\(.*\\)/P[\"\\1\"]=\"\\2\"\\\nD[\"\\1\"]=\" \\3\\\\\\\\\\\\n\"\\\\/p\nt cont\ns/^ \\('\"$ac_word_re\"'\\)[\t ]*\\(.*\\)/D[\"\\1\"]=\" \\2\\\\\\\\\\\\n\"\\\\/p\nt cont\nd\n:cont\nn\ns/.\\{148\\}/&'\"$ac_delim\"'/g\nt clear\n:clear\ns/\\\\$//\nt bsnlc\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\"/p\nd\n:bsnlc\ns/[\"\\\\]/\\\\&/g; s/^/\"/; s/$/\\\\\\\\\\\\n\"\\\\/p\nb cont\n' <confdefs.h | sed '\ns/'\"$ac_delim\"'/\"\\\\\\\n\"/g' >>$CONFIG_STATUS || ac_write_fail=1\n\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n  for (key in D) D_is_set[key] = 1\n  FS = \"\u0007\"\n}\n/^[\\t ]*#[\\t ]*(define|undef)[\\t ]+$ac_word_re([\\t (]|\\$)/ {\n  line = \\$ 0\n  split(line, arg, \" \")\n  if (arg[1] == \"#\") {\n    defundef = arg[2]\n    mac1 = arg[3]\n  } else {\n    defundef = substr(arg[1], 2)\n    mac1 = arg[2]\n  }\n  split(mac1, mac2, \"(\") #)\n  macro = mac2[1]\n  prefix = substr(line, 1, index(line, defundef) - 1)\n  if (D_is_set[macro]) {\n    # Preserve the white space surrounding the \"#\".\n    print prefix \"define\", macro P[macro] D[macro]\n    next\n  } else {\n    # Replace #undef with comments.  This is necessary, for example,\n    # in the case of _POSIX_SOURCE, which is predefined and required\n    # on some systems where configure will not decide to define it.\n    if (defundef == \"undef\") {\n      print \"/*\", prefix defundef, macro, \"*/\"\n      next\n    }\n  }\n}\n{ print }\n_ACAWK\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n  as_fn_error $? \"could not setup config headers machinery\" \"$LINENO\" 5\nfi # test -n \"$CONFIG_HEADERS\"\n\n\neval set X \"  :F $CONFIG_FILES  :H $CONFIG_HEADERS    \"\nshift\nfor ac_tag\ndo\n  case $ac_tag in\n  :[FHLC]) ac_mode=$ac_tag; continue;;\n  esac\n  case $ac_mode$ac_tag in\n  :[FHL]*:*);;\n  :L* | :C*:*) as_fn_error $? \"invalid tag \\`$ac_tag'\" \"$LINENO\" 5;;\n  :[FH]-) ac_tag=-:-;;\n  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;\n  esac\n  ac_save_IFS=$IFS\n  IFS=:\n  set x $ac_tag\n  IFS=$ac_save_IFS\n  shift\n  ac_file=$1\n  shift\n\n  case $ac_mode in\n  :L) ac_source=$1;;\n  :[FH])\n    ac_file_inputs=\n    for ac_f\n    do\n      case $ac_f in\n      -) ac_f=\"$ac_tmp/stdin\";;\n      *) # Look for the file first in the build tree, then in the source tree\n\t # (if the path is not absolute).  The absolute path cannot be DOS-style,\n\t # because $ac_f cannot contain `:'.\n\t test -f \"$ac_f\" ||\n\t   case $ac_f in\n\t   [\\\\/$]*) false;;\n\t   *) test -f \"$srcdir/$ac_f\" && ac_f=\"$srcdir/$ac_f\";;\n\t   esac ||\n\t   as_fn_error 1 \"cannot find input file: \\`$ac_f'\" \"$LINENO\" 5;;\n      esac\n      case $ac_f in *\\'*) ac_f=`$as_echo \"$ac_f\" | sed \"s/'/'\\\\\\\\\\\\\\\\''/g\"`;; esac\n      as_fn_append ac_file_inputs \" '$ac_f'\"\n    done\n\n    # Let's still pretend it is `configure' which instantiates (i.e., don't\n    # use $as_me), people would be surprised to read:\n    #    /* config.h.  Generated by config.status.  */\n    configure_input='Generated from '`\n\t  $as_echo \"$*\" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'\n\t`' by configure.'\n    if test x\"$ac_file\" != x-; then\n      configure_input=\"$ac_file.  $configure_input\"\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: creating $ac_file\" >&5\n$as_echo \"$as_me: creating $ac_file\" >&6;}\n    fi\n    # Neutralize special characters interpreted by sed in replacement strings.\n    case $configure_input in #(\n    *\\&* | *\\|* | *\\\\* )\n       ac_sed_conf_input=`$as_echo \"$configure_input\" |\n       sed 's/[\\\\\\\\&|]/\\\\\\\\&/g'`;; #(\n    *) ac_sed_conf_input=$configure_input;;\n    esac\n\n    case $ac_tag in\n    *:-:* | *:-) cat >\"$ac_tmp/stdin\" \\\n      || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5 ;;\n    esac\n    ;;\n  esac\n\n  ac_dir=`$as_dirname -- \"$ac_file\" ||\n$as_expr X\"$ac_file\" : 'X\\(.*[^/]\\)//*[^/][^/]*/*$' \\| \\\n\t X\"$ac_file\" : 'X\\(//\\)[^/]' \\| \\\n\t X\"$ac_file\" : 'X\\(//\\)$' \\| \\\n\t X\"$ac_file\" : 'X\\(/\\)' \\| . 2>/dev/null ||\n$as_echo X\"$ac_file\" |\n    sed '/^X\\(.*[^/]\\)\\/\\/*[^/][^/]*\\/*$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)[^/].*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\/\\)$/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  /^X\\(\\/\\).*/{\n\t    s//\\1/\n\t    q\n\t  }\n\t  s/.*/./; q'`\n  as_dir=\"$ac_dir\"; as_fn_mkdir_p\n  ac_builddir=.\n\ncase \"$ac_dir\" in\n.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;\n*)\n  ac_dir_suffix=/`$as_echo \"$ac_dir\" | sed 's|^\\.[\\\\/]||'`\n  # A \"..\" for each directory in $ac_dir_suffix.\n  ac_top_builddir_sub=`$as_echo \"$ac_dir_suffix\" | sed 's|/[^\\\\/]*|/..|g;s|/||'`\n  case $ac_top_builddir_sub in\n  \"\") ac_top_builddir_sub=. ac_top_build_prefix= ;;\n  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;\n  esac ;;\nesac\nac_abs_top_builddir=$ac_pwd\nac_abs_builddir=$ac_pwd$ac_dir_suffix\n# for backward compatibility:\nac_top_builddir=$ac_top_build_prefix\n\ncase $srcdir in\n  .)  # We are building in place.\n    ac_srcdir=.\n    ac_top_srcdir=$ac_top_builddir_sub\n    ac_abs_top_srcdir=$ac_pwd ;;\n  [\\\\/]* | ?:[\\\\/]* )  # Absolute name.\n    ac_srcdir=$srcdir$ac_dir_suffix;\n    ac_top_srcdir=$srcdir\n    ac_abs_top_srcdir=$srcdir ;;\n  *) # Relative name.\n    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix\n    ac_top_srcdir=$ac_top_build_prefix$srcdir\n    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;\nesac\nac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix\n\n\n  case $ac_mode in\n  :F)\n  #\n  # CONFIG_FILE\n  #\n\n_ACEOF\n\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n# If the template does not know about datarootdir, expand it.\n# FIXME: This hack should be removed a few years after 2.60.\nac_datarootdir_hack=; ac_datarootdir_seen=\nac_sed_dataroot='\n/datarootdir/ {\n  p\n  q\n}\n/@datadir@/p\n/@docdir@/p\n/@infodir@/p\n/@localedir@/p\n/@mandir@/p'\ncase `eval \"sed -n \\\"\\$ac_sed_dataroot\\\" $ac_file_inputs\"` in\n*datarootdir*) ac_datarootdir_seen=yes;;\n*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting\" >&5\n$as_echo \"$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting\" >&2;}\n_ACEOF\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\n  ac_datarootdir_hack='\n  s&@datadir@&$datadir&g\n  s&@docdir@&$docdir&g\n  s&@infodir@&$infodir&g\n  s&@localedir@&$localedir&g\n  s&@mandir@&$mandir&g\n  s&\\\\\\${datarootdir}&$datarootdir&g' ;;\nesac\n_ACEOF\n\n# Neutralize VPATH when `$srcdir' = `.'.\n# Shell code in configure.ac might set extrasub.\n# FIXME: do we really want to maintain this feature?\ncat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1\nac_sed_extra=\"$ac_vpsub\n$extrasub\n_ACEOF\ncat >>$CONFIG_STATUS <<\\_ACEOF || ac_write_fail=1\n:t\n/@[a-zA-Z_][a-zA-Z_0-9]*@/!b\ns|@configure_input@|$ac_sed_conf_input|;t t\ns&@top_builddir@&$ac_top_builddir_sub&;t t\ns&@top_build_prefix@&$ac_top_build_prefix&;t t\ns&@srcdir@&$ac_srcdir&;t t\ns&@abs_srcdir@&$ac_abs_srcdir&;t t\ns&@top_srcdir@&$ac_top_srcdir&;t t\ns&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t\ns&@builddir@&$ac_builddir&;t t\ns&@abs_builddir@&$ac_abs_builddir&;t t\ns&@abs_top_builddir@&$ac_abs_top_builddir&;t t\n$ac_datarootdir_hack\n\"\neval sed \\\"\\$ac_sed_extra\\\" \"$ac_file_inputs\" | $AWK -f \"$ac_tmp/subs.awk\" \\\n  >$ac_tmp/out || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n\ntest -z \"$ac_datarootdir_hack$ac_datarootdir_seen\" &&\n  { ac_out=`sed -n '/\\${datarootdir}/p' \"$ac_tmp/out\"`; test -n \"$ac_out\"; } &&\n  { ac_out=`sed -n '/^[\t ]*datarootdir[\t ]*:*=/p' \\\n      \"$ac_tmp/out\"`; test -z \"$ac_out\"; } &&\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \\`datarootdir'\nwhich seems to be undefined.  Please make sure it is defined\" >&5\n$as_echo \"$as_me: WARNING: $ac_file contains a reference to the variable \\`datarootdir'\nwhich seems to be undefined.  Please make sure it is defined\" >&2;}\n\n  rm -f \"$ac_tmp/stdin\"\n  case $ac_file in\n  -) cat \"$ac_tmp/out\" && rm -f \"$ac_tmp/out\";;\n  *) rm -f \"$ac_file\" && mv \"$ac_tmp/out\" \"$ac_file\";;\n  esac \\\n  || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n ;;\n  :H)\n  #\n  # CONFIG_HEADER\n  #\n  if test x\"$ac_file\" != x-; then\n    {\n      $as_echo \"/* $configure_input  */\" \\\n      && eval '$AWK -f \"$ac_tmp/defines.awk\"' \"$ac_file_inputs\"\n    } >\"$ac_tmp/config.h\" \\\n      || as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n    if diff \"$ac_file\" \"$ac_tmp/config.h\" >/dev/null 2>&1; then\n      { $as_echo \"$as_me:${as_lineno-$LINENO}: $ac_file is unchanged\" >&5\n$as_echo \"$as_me: $ac_file is unchanged\" >&6;}\n    else\n      rm -f \"$ac_file\"\n      mv \"$ac_tmp/config.h\" \"$ac_file\" \\\n\t|| as_fn_error $? \"could not create $ac_file\" \"$LINENO\" 5\n    fi\n  else\n    $as_echo \"/* $configure_input  */\" \\\n      && eval '$AWK -f \"$ac_tmp/defines.awk\"' \"$ac_file_inputs\" \\\n      || as_fn_error $? \"could not create -\" \"$LINENO\" 5\n  fi\n ;;\n\n\n  esac\n\ndone # for ac_tag\n\n\nas_fn_exit 0\n_ACEOF\nac_clean_files=$ac_clean_files_save\n\ntest $ac_write_fail = 0 ||\n  as_fn_error $? \"write failure creating $CONFIG_STATUS\" \"$LINENO\" 5\n\n\n# configure is writing to config.log, and then calls config.status.\n# config.status does its own redirection, appending to config.log.\n# Unfortunately, on DOS this fails, as config.log is still kept open\n# by configure, so config.status won't be able to write to it; its\n# output is simply discarded.  So we exec the FD to /dev/null,\n# effectively closing config.log, so it can be properly (re)opened and\n# appended to by config.status.  When coming back to configure, we\n# need to make the FD available again.\nif test \"$no_create\" != yes; then\n  ac_cs_success=:\n  ac_config_status_args=\n  test \"$silent\" = yes &&\n    ac_config_status_args=\"$ac_config_status_args --quiet\"\n  exec 5>/dev/null\n  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false\n  exec 5>>config.log\n  # Use ||, not &&, to avoid exiting from the if with $? = 1, which\n  # would make configure fail if this is the last instruction.\n  $ac_cs_success || as_fn_exit 1\nfi\nif test -n \"$ac_unrecognized_opts\" && test \"$enable_option_checking\" != no; then\n  { $as_echo \"$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts\" >&5\n$as_echo \"$as_me: WARNING: unrecognized options: $ac_unrecognized_opts\" >&2;}\nfi\n\n"
  },
  {
    "path": "configure.ac",
    "content": "# Citus autoconf input script.\n#\n# Converted into an actual configure script by autogen.sh. This\n# conversion only has to be done when configure.in changes. To avoid\n# everyone needing autoconf installed, the resulting files are checked\n# into the SCM.\n\nAC_INIT([Citus], [15.0devel])\nAC_COPYRIGHT([Copyright (c) Citus Data, Inc.])\n\n# we'll need sed and awk for some of the version commands\nAC_PROG_SED\nAC_PROG_AWK\n\n# CITUS_NAME definition\nAC_DEFINE_UNQUOTED(CITUS_NAME, \"$PACKAGE_NAME\", [Citus full name as a string])\n\ncase $PACKAGE_NAME in\n  'Citus Enterprise') citus_edition=enterprise ;;\n               Citus) citus_edition=community ;;\n                   *) AC_MSG_ERROR([Unrecognized package name.]) ;;\nesac\n\n# CITUS_EDITION definition\nAC_DEFINE_UNQUOTED(CITUS_EDITION, \"$citus_edition\", [Citus edition as a string])\n\n# CITUS_MAJORVERSION definition\n[CITUS_MAJORVERSION=`expr \"$PACKAGE_VERSION\" : '\\([0-9][0-9]*\\.[0-9][0-9]*\\)'`]\nAC_DEFINE_UNQUOTED(CITUS_MAJORVERSION, \"$CITUS_MAJORVERSION\", [Citus major version as a string])\n\n# CITUS_VERSION definition\nPGAC_ARG_REQ(with, extra-version, [STRING], [append STRING to version],\n             [CITUS_VERSION=\"$PACKAGE_VERSION$withval\"],\n             [CITUS_VERSION=\"$PACKAGE_VERSION\"])\nAC_DEFINE_UNQUOTED(CITUS_VERSION, \"$CITUS_VERSION\", [Citus version as a string])\n\n# CITUS_VERSION_NUM definition\n# awk -F is a regex on some platforms, and not on others, so make \".\" a tab\n[CITUS_VERSION_NUM=\"`echo \"$PACKAGE_VERSION\" | sed 's/[A-Za-z].*$//' |\ntr '.' '\t' |\n$AWK '{printf \"%d%02d%02d\", $1, $2, (NF >= 3) ? $3 : 0}'`\"]\nAC_DEFINE_UNQUOTED(CITUS_VERSION_NUM, $CITUS_VERSION_NUM, [Citus version as a number])\n\n# CITUS_EXTENSIONVERSION definition\n[CITUS_EXTENSIONVERSION=\"`grep '^default_version' $srcdir/src/backend/distributed/citus.control | cut -d\\' -f2`\"]\nAC_DEFINE_UNQUOTED([CITUS_EXTENSIONVERSION], \"$CITUS_EXTENSIONVERSION\", [Extension version expected by this Citus build])\n\n# Re-check for flex. That allows to compile citus against a postgres\n# which was built without flex available (possible because generated\n# files are included)\nAC_PATH_PROG([FLEX], [flex])\n\n# Locate pg_config binary\nAC_ARG_VAR([PG_CONFIG], [Location to find pg_config for target PostgreSQL instalation (default PATH)])\nAC_ARG_VAR([PATH], [PATH for target PostgreSQL install pg_config])\n\nif test -z \"$PG_CONFIG\"; then\n  AC_PATH_PROG(PG_CONFIG, pg_config)\nfi\n\nif test -z \"$PG_CONFIG\"; then\n   AC_MSG_ERROR([Could not find pg_config. Set PG_CONFIG or PATH.])\nfi\n\n# check we're building against a supported version of PostgreSQL\ncitusac_pg_config_version=$($PG_CONFIG --version 2>/dev/null)\nversion_num=$(echo \"$citusac_pg_config_version\"|\n              $SED -e 's/^PostgreSQL \\([[0-9]]*\\)\\(\\.[[0-9]]*\\)\\{0,1\\}\\(.*\\)$/\\1\\2/')\n\n# if PostgreSQL version starts with two digits, the major version is those digits\nversion_num=$(echo \"$version_num\"| $SED -e 's/^\\([[0-9]]\\{2\\}\\)\\(.*\\)$/\\1/')\n\nif test -z \"$version_num\"; then\n  AC_MSG_ERROR([Could not detect PostgreSQL version from pg_config.])\nfi\n\nPGAC_ARG_BOOL(with, pg-version-check, yes,\n              [do not check postgres version during configure])\nAC_SUBST(with_pg_version_check)\n\nif test \"$with_pg_version_check\" = no; then\n    AC_MSG_NOTICE([building against PostgreSQL $version_num (skipped compatibility check)])\nelif test \"$version_num\" != '16' -a  \"$version_num\" != '17' -a  \"$version_num\" != '18'; then\n   AC_MSG_ERROR([Citus is not compatible with the detected PostgreSQL version ${version_num}.])\nelse\n   AC_MSG_NOTICE([building against PostgreSQL $version_num])\nfi;\n\n# Check whether we're building inside the source tree, if not, prepare\n# the build directory.\nif test \"$srcdir\" -ef '.' ; then\n  vpath_build=no\nelse\n  vpath_build=yes\n  _AS_ECHO_N([preparing build tree... ])\n  citusac_abs_top_srcdir=`cd \"$srcdir\" && pwd`\n  $SHELL \"$citusac_abs_top_srcdir/prep_buildtree\" \"$citusac_abs_top_srcdir\" \".\" \\\n      || AC_MSG_ERROR(failed)\n  AC_MSG_RESULT(done)\nfi\nAC_SUBST(vpath_build)\n\n# Allow to overwrite the C compiler, default to the one postgres was\n# compiled with. We don't want autoconf's default CFLAGS though, so save\n# those.\nSAVE_CFLAGS=\"$CFLAGS\"\nAC_PROG_CC([$($PG_CONFIG --cc)])\nCFLAGS=\"$SAVE_CFLAGS\"\n\nhost_guess=`${SHELL} $srcdir/config/config.guess`\n\n# Create compiler version string\nif test x\"$GCC\" = x\"yes\" ; then\n  cc_string=`${CC} --version | sed q`\n  case $cc_string in [[A-Za-z]]*) ;; *) cc_string=\"GCC $cc_string\";; esac\nelif test x\"$SUN_STUDIO_CC\" = x\"yes\" ; then\n  cc_string=`${CC} -V 2>&1 | sed q`\nelse\n  cc_string=$CC\nfi\n\nAC_CHECK_SIZEOF([void *])\n\nAC_DEFINE_UNQUOTED(CITUS_VERSION_STR,\n                   [\"$PACKAGE_NAME $CITUS_VERSION on $host_guess, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \\* 8`-bit\"],\n                   [A string containing the version number, platform, and C compiler])\n\n# Locate source and build directory of the postgres we're building\n# against. Can't rely on either still being present, but e.g. optional\n# test infrastructure can rely on it.\nPOSTGRES_SRCDIR=$(grep ^abs_top_srcdir $(dirname $($PG_CONFIG --pgxs))/../Makefile.global|cut -d ' ' -f3-)\nPOSTGRES_BUILDDIR=$(grep ^abs_top_builddir $(dirname $($PG_CONFIG --pgxs))/../Makefile.global|cut -d ' ' -f3-)\n\n\n# check for a number of CFLAGS that make development easier\n\n# CITUSAC_PROG_CC_CFLAGS_OPT\n# -----------------------\n# Given a string, check if the compiler supports the string as a\n# command-line option. If it does, add the string to CFLAGS.\nAC_DEFUN([CITUSAC_PROG_CC_CFLAGS_OPT],\n[define([Ac_cachevar], [AS_TR_SH([citusac_cv_prog_cc_cflags_$1])])dnl\nAC_CACHE_CHECK([whether $CC supports $1], [Ac_cachevar],\n[citusac_save_CFLAGS=$CFLAGS\nflag=$1\ncase $flag in -Wno*)\n\t flag=-W$(echo $flag | cut -c 6-)\nesac\nCFLAGS=\"$citusac_save_CFLAGS $flag\"\nac_save_c_werror_flag=$ac_c_werror_flag\nac_c_werror_flag=yes\n_AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],\n                   [Ac_cachevar=yes],\n                   [Ac_cachevar=no])\nac_c_werror_flag=$ac_save_c_werror_flag\nCFLAGS=\"$citusac_save_CFLAGS\"])\nif test x\"$Ac_cachevar\" = x\"yes\"; then\n  CITUS_CFLAGS=\"$CITUS_CFLAGS $1\"\nfi\nundefine([Ac_cachevar])dnl\n])# CITUSAC_PROG_CC_CFLAGS_OPT\n\nCITUSAC_PROG_CC_CFLAGS_OPT([-std=gnu99])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wall])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wextra])\n# disarm options included in the above, which are too noisy for now\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wno-unused-parameter])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wno-sign-compare])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wno-missing-field-initializers])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wno-clobbered])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wno-gnu-variable-sized-type-not-at-end])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wno-declaration-after-statement])\n# And add a few extra warnings\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wendif-labels])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wmissing-format-attribute])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wmissing-declarations])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wmissing-prototypes])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Wshadow])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Werror=vla])  # visual studio does not support these\nCITUSAC_PROG_CC_CFLAGS_OPT([-Werror=implicit-int])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Werror=implicit-function-declaration])\nCITUSAC_PROG_CC_CFLAGS_OPT([-Werror=return-type])\n# Security flags\n# Flags taken from: https://liquid.microsoft.com/Web/Object/Read/ms.security/Requirements/Microsoft.Security.SystemsADM.10203#guide\n# We do not enforce the following flag because it is only available on GCC>=8\nCITUSAC_PROG_CC_CFLAGS_OPT([-fstack-clash-protection])\n\n#\n# --enable-coverage enables generation of code coverage metrics with gcov\n#\nAC_ARG_ENABLE([coverage], AS_HELP_STRING([--enable-coverage], [build with coverage testing instrumentation]))\nif test \"$enable_coverage\" = yes; then\n    CITUS_CFLAGS=\"$CITUS_CFLAGS -O0 -g --coverage\"\n    CITUS_CPPFLAGS=\"$CITUS_CPPFLAGS -DNDEBUG\"\n    CITUS_LDFLAGS=\"$CITUS_LDFLAGS --coverage\"\nfi\n\n#\n# LZ4\n#\nPGAC_ARG_BOOL(with, lz4, yes,\n              [do not use lz4],\n              [AC_DEFINE([HAVE_CITUS_LIBLZ4], 1, [Define to 1 to build with lz4 support. (--with-lz4)])])\nAC_SUBST(with_lz4)\n\nif test \"$with_lz4\" = yes; then\n  AC_CHECK_LIB(lz4, LZ4_compress_default, [],\n              [AC_MSG_ERROR([lz4 library not found\nIf you have lz4 installed, see config.log for details on the\nfailure.  It is possible the compiler isn't looking in the proper directory.\nUse --without-lz4 to disable lz4 support.])])\n  AC_CHECK_HEADER(lz4.h, [], [AC_MSG_ERROR([lz4 header not found\nIf you have lz4 already installed, see config.log for details on the\nfailure.  It is possible the compiler isn't looking in the proper directory.\nUse --without-lz4 to disable lz4 support.])])\nfi\n\n#\n# ZSTD\n#\nPGAC_ARG_BOOL(with, zstd, yes,\n              [do not use zstd])\nAC_SUBST(with_zstd)\n\nif test \"$with_zstd\" = yes; then\n  AC_CHECK_LIB(zstd, ZSTD_decompress, [],\n              [AC_MSG_ERROR([zstd library not found\nIf you have zstd installed, see config.log for details on the\nfailure.  It is possible the compiler isn't looking in the proper directory.\nUse --without-zstd to disable zstd support.])])\n  AC_CHECK_HEADER(zstd.h, [], [AC_MSG_ERROR([zstd header not found\nIf you have zstd already installed, see config.log for details on the\nfailure.  It is possible the compiler isn't looking in the proper directory.\nUse --without-zstd to disable zstd support.])])\nfi\n\n\nPGAC_ARG_BOOL(with, security-flags, no,\n              [use security flags])\nAC_SUBST(with_security_flags)\n\nif test \"$with_security_flags\" = yes; then\n# Flags taken from: https://liquid.microsoft.com/Web/Object/Read/ms.security/Requirements/Microsoft.Security.SystemsADM.10203#guide\n\n# We always want to have some compiler flags for security concerns.\nSECURITY_CFLAGS=\"-fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 -z noexecstack -fpic -shared -Wl,-z,relro -Wl,-z,now -Wformat -Wformat-security -Werror=format-security\"\nCITUS_CFLAGS=\"$CITUS_CFLAGS $SECURITY_CFLAGS\"\nAC_MSG_NOTICE([Blindly added security flags for linker: $SECURITY_CFLAGS])\n\n# We always want to have some clang flags for security concerns.\n# This doesn't include \"-Wl,-z,relro -Wl,-z,now\" on purpuse, because bitcode is not linked.\n# This doesn't include -fsanitize=cfi because it breaks builds on many distros including\n# Debian/Buster, Debian/Stretch, Ubuntu/Bionic, Ubuntu/Xenial and EL7.\nSECURITY_BITCODE_CFLAGS=\"-fsanitize=safe-stack -fstack-protector-strong -flto -fPIC -Wformat -Wformat-security -Werror=format-security\"\nCITUS_BITCODE_CFLAGS=\"$CITUS_BITCODE_CFLAGS $SECURITY_BITCODE_CFLAGS\"\nAC_MSG_NOTICE([Blindly added security flags for llvm: $SECURITY_BITCODE_CFLAGS])\n\nAC_MSG_WARN([If you run into issues during linking or bitcode compilation, you can use --without-security-flags.])\nfi\n\n# Check if git is installed, when installed the gitref of the checkout will be baked in the application\nAC_PATH_PROG(GIT_BIN, git)\nAC_CHECK_FILE(.git,[HAS_DOTGIT=yes], [HAS_DOTGIT=])\n\nAC_SUBST(CITUS_CFLAGS, \"$CITUS_CFLAGS\")\nAC_SUBST(CITUS_BITCODE_CFLAGS, \"$CITUS_BITCODE_CFLAGS\")\nAC_SUBST(CITUS_CPPFLAGS, \"$CITUS_CPPFLAGS\")\nAC_SUBST(CITUS_LDFLAGS, \"$LIBS $CITUS_LDFLAGS\")\nAC_SUBST(POSTGRES_SRCDIR, \"$POSTGRES_SRCDIR\")\nAC_SUBST(POSTGRES_BUILDDIR, \"$POSTGRES_BUILDDIR\")\nAC_SUBST(HAS_DOTGIT, \"$HAS_DOTGIT\")\n\nAC_CONFIG_FILES([Makefile.global])\nAC_CONFIG_HEADERS([src/include/citus_config.h] [src/include/citus_version.h])\nAH_TOP([\n/*\n * citus_config.h.in is generated by autoconf/autoheader and\n * converted into citus_config.h by configure.  Include when code needs to\n * depend on determinations made by configure.\n *\n * Do not manually edit!\n */\n])\nAC_OUTPUT\n"
  },
  {
    "path": "prep_buildtree",
    "content": "#! /bin/sh\n#\n# Citus copy of PostgreSQL's config/prep_buildtree\n#\n# This script prepares a Citus build tree for an out-of-tree/VPATH\n# build.  It is intended to be run by the configure script.\n\nme=`basename $0`\n\nhelp=\"\\\nUsage: $me sourcetree [buildtree]\"\n\nif test -z \"$1\"; then\n    echo \"$help\" 1>&2\n    exit 1\nelif test x\"$1\" = x\"--help\"; then\n    echo \"$help\"\n    exit 0\nfi\n\nunset CDPATH\n\nsourcetree=`cd $1 && pwd`\n\nbuildtree=`cd ${2:-'.'} && pwd`\n\n# We must not auto-create the subdirectories holding built documentation.\n# If we did, it would interfere with installation of prebuilt docs from\n# the source tree, if a VPATH build is done from a distribution tarball.\n# See bug #5595.\nfor item in `find \"$sourcetree\" -type d \\( \\( -name CVS -prune \\) -o \\( -name .git -prune \\) -o -print \\) | grep -v \"$sourcetree/doc/src/sgml/\\+\"`; do\n    subdir=`expr \"$item\" : \"$sourcetree\\(.*\\)\"`\n    if test ! -d \"$buildtree/$subdir\"; then\n        mkdir -p \"$buildtree/$subdir\" || exit 1\n    fi\ndone\n\nfor item in `find \"$sourcetree\" -not -path '*/.git/hg/*' \\( -name Makefile -print -o -name GNUmakefile -print \\)`; do\n    filename=`expr \"$item\" : \"$sourcetree\\(.*\\)\"`\n    if test ! -f \"${item}.in\"; then\n        if cmp \"$item\" \"$buildtree/$filename\" >/dev/null 2>&1; then : ; else\n            ln -fs \"$item\" \"$buildtree/$filename\" || exit 1\n        fi\n    fi\ndone\n\nexit 0\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[tool.isort]\nprofile = 'black'\n\n[tool.black]\ninclude = '(src/test/regress/bin/diff-filter|\\.pyi?|\\.ipynb)$'\n\n[tool.pytest.ini_options]\naddopts = [\n    \"--import-mode=importlib\",\n    \"--showlocals\",\n    \"--tb=short\",\n]\npythonpath = 'src/test/regress/citus_tests'\nasyncio_mode = 'auto'\n\n# Make test discovery quicker from the root dir of the repo\ntestpaths = ['src/test/regress/citus_tests/test']\n\n# Make test discovery quicker from other directories than root directory\nnorecursedirs = [\n    '*.egg',\n    '.*',\n    'build',\n    'venv',\n    'ci',\n    'vendor',\n    'backend',\n    'bin',\n    'include',\n    'tmp_*',\n    'results',\n    'expected',\n    'sql',\n    'spec',\n    'data',\n    '__pycache__',\n]\n\n# Don't find files with test at the end such as run_test.py\npython_files = ['test_*.py']\n"
  },
  {
    "path": "src/backend/.gitignore",
    "content": ""
  },
  {
    "path": "src/backend/columnar/.gitattributes",
    "content": "*\t\twhitespace=space-before-tab,trailing-space\n*.[chly]\twhitespace=space-before-tab,trailing-space,indent-with-non-tab,tabwidth=4\n*.dsl\t\twhitespace=space-before-tab,trailing-space,tab-in-indent\n*.patch\t\t-whitespace\n*.pl\t\twhitespace=space-before-tab,trailing-space,tabwidth=4\n*.po\t\twhitespace=space-before-tab,trailing-space,tab-in-indent,-blank-at-eof\n*.sgml\t\twhitespace=space-before-tab,trailing-space,tab-in-indent,-blank-at-eol\n*.x[ms]l\twhitespace=space-before-tab,trailing-space,tab-in-indent\n\n# Avoid confusing ASCII underlines with leftover merge conflict markers\nREADME\t\tconflict-marker-size=32\nREADME.*\tconflict-marker-size=32\n\n# Certain data files that contain special whitespace, and other special cases\n*.data\t\t\t\t\t\t-whitespace\n\n# Test output files that contain extra whitespace\n*.out\t\t\t\t\t-whitespace\n\n# These files are maintained or generated elsewhere.  We take them as is.\nconfigure\t\t\t\t-whitespace\n\n# all C files (implementation and header) use our style...\n*.[ch] citus-style\n\n"
  },
  {
    "path": "src/backend/columnar/.gitignore",
    "content": "# The directory used to store columnar sql files after pre-processing them\n# with 'cpp' in build-time, see src/backend/columnar/Makefile.\n/build/\n"
  },
  {
    "path": "src/backend/columnar/Makefile",
    "content": "citus_subdir = src/backend/columnar\ncitus_top_builddir = ../../..\nsafestringlib_srcdir = $(citus_abs_top_srcdir)/vendor/safestringlib\nSUBDIRS = . safeclib\nSUBDIRS +=\nENSURE_SUBDIRS_EXIST := $(shell mkdir -p $(SUBDIRS))\nOBJS += \\\n\t$(patsubst $(citus_abs_srcdir)/%.c,%.o,$(foreach dir,$(SUBDIRS), $(sort $(wildcard $(citus_abs_srcdir)/$(dir)/*.c))))\n\nMODULE_big = citus_columnar\nEXTENSION = citus_columnar\n\ntemplate_sql_files = $(patsubst $(citus_abs_srcdir)/%,%,$(wildcard $(citus_abs_srcdir)/sql/*.sql))\ntemplate_downgrade_sql_files = $(patsubst $(citus_abs_srcdir)/sql/downgrades/%,%,$(wildcard $(citus_abs_srcdir)/sql/downgrades/*.sql))\ngenerated_sql_files = $(patsubst %,$(citus_abs_srcdir)/build/%,$(template_sql_files))\ngenerated_downgrade_sql_files += $(patsubst %,$(citus_abs_srcdir)/build/sql/%,$(template_downgrade_sql_files))\n\nDATA_built = $(generated_sql_files)\n\nPG_CPPFLAGS += -I$(libpq_srcdir) -I$(safestringlib_srcdir)/include\n\ninclude $(citus_top_builddir)/Makefile.global\n\nSQL_DEPDIR=.deps/sql\nSQL_BUILDDIR=build/sql\n\n$(generated_sql_files): $(citus_abs_srcdir)/build/%: %\n\t@mkdir -p $(citus_abs_srcdir)/$(SQL_DEPDIR) $(citus_abs_srcdir)/$(SQL_BUILDDIR)\n\t@# -MF is used to store dependency files(.Po) in another directory for separation\n\t@# -MT is used to change the target of the rule emitted by dependency generation.\n\t@# -P is used to inhibit generation of linemarkers in the output from the preprocessor.\n\t@# -undef is used to not predefine any system-specific or GCC-specific macros.\n\t@# `man cpp` for further information\n\tcd $(citus_abs_srcdir) && cpp -undef -w -P -MMD -MP -MF$(SQL_DEPDIR)/$(*F).Po -MT$@ $< > $@\n\n$(generated_downgrade_sql_files): $(citus_abs_srcdir)/build/sql/%: sql/downgrades/%\n\t@mkdir -p $(citus_abs_srcdir)/$(SQL_DEPDIR) $(citus_abs_srcdir)/$(SQL_BUILDDIR)\n\t@# -MF is used to store dependency files(.Po) in another directory for separation\n\t@# -MT is used to change the target of the rule emitted by dependency generation.\n\t@# -P is used to inhibit generation of linemarkers in the output from the preprocessor.\n\t@# -undef is used to not predefine any system-specific or GCC-specific macros.\n\t@# `man cpp` for further information\n\tcd $(citus_abs_srcdir) && cpp -undef -w -P -MMD -MP -MF$(SQL_DEPDIR)/$(*F).Po -MT$@ $< > $@\n\n.PHONY: install install-downgrades install-all\n\ncleanup-before-install:\n\trm -f $(DESTDIR)$(datadir)/$(datamoduledir)/citus_columnar.control\n\trm -f $(DESTDIR)$(datadir)/$(datamoduledir)/columnar--*\n\trm -f $(DESTDIR)$(datadir)/$(datamoduledir)/citus_columnar--*\n\ninstall: cleanup-before-install\n\n# install and install-downgrades should be run sequentially\ninstall-all: install\n\t$(MAKE) install-downgrades\n\ninstall-downgrades: $(generated_downgrade_sql_files)\n\t$(INSTALL_DATA) $(generated_downgrade_sql_files) '$(DESTDIR)$(datadir)/$(datamoduledir)/'\n\n"
  },
  {
    "path": "src/backend/columnar/README.md",
    "content": "# Introduction\n\nCitus Columnar offers a per-table option for columnar storage to\nreduce IO requirements though compression and projection pushdown.\n\n# Design Trade-Offs\n\nExisting PostgreSQL row tables work well for OLTP:\n\n* Support `UPDATE`/`DELETE` efficiently\n* Efficient single-tuple lookups\n\nThe Citus Columnar tables work best for analytic or DW workloads:\n\n* Compression\n* Doesn't read unnecessary columns\n* Efficient `VACUUM`\n\n# Next generation of cstore_fdw\n\nCitus Columnar is the next generation of\n[cstore_fdw](https://github.com/citusdata/cstore_fdw/).\n\nBenefits of Citus Columnar over cstore_fdw:\n\n* Citus Columnar is based on the [Table Access Method\n  API](https://www.postgresql.org/docs/current/tableam.html), which\n  allows it to behave exactly like an ordinary heap (row) table for\n  most operations.\n* Supports Write-Ahead Log (WAL).\n* Supports ``ROLLBACK``.\n* Supports physical replication.\n* Supports recovery, including Point-In-Time Restore (PITR).\n* Supports ``pg_dump`` and ``pg_upgrade`` without the need for special\n  options or extra steps.\n* Better user experience; simple ``USING``clause.\n* Supports more features that work on ordinary heap (row) tables.\n\n# Limitations\n\n* Append-only (no ``UPDATE``/``DELETE`` support)\n* No space reclamation (e.g. rolled-back transactions may still\n  consume disk space)\n* No bitmap index scans\n* No tidscans\n* No sample scans\n* No TOAST support (large values supported inline)\n* No support for [``ON\n  CONFLICT``](https://www.postgresql.org/docs/12/sql-insert.html#SQL-ON-CONFLICT)\n  statements (except ``DO NOTHING`` actions with no target specified).\n* No support for tuple locks (``SELECT ... FOR SHARE``, ``SELECT\n  ... FOR UPDATE``)\n* No support for serializable isolation level\n* Support for PostgreSQL server versions 12+ only\n* No support for foreign keys\n* No support for logical decoding\n* No support for intra-node parallel scans\n* No support for ``AFTER ... FOR EACH ROW`` triggers\n* No `UNLOGGED` columnar tables\n\nFuture iterations will incrementally lift the limitations listed above.\n\n# User Experience\n\nCreate a Columnar table by specifying ``USING columnar`` when creating\nthe table.\n\n```sql\nCREATE TABLE my_columnar_table\n(\n    id INT,\n    i1 INT,\n    i2 INT8,\n    n NUMERIC,\n    t TEXT\n) USING columnar;\n```\n\nInsert data into the table and read from it like normal (subject to\nthe limitations listed above).\n\nTo see internal statistics about the table, use ``VACUUM\nVERBOSE``. Note that ``VACUUM`` (without ``FULL``) is much faster on a\ncolumnar table, because it scans only the metadata, and not the actual\ndata.\n\n## Options\n\nSet options using:\n\n```sql\nALTER TABLE my_columnar_table SET\n  (columnar.compression = none, columnar.stripe_row_limit = 10000);\n```\n\nThe following options are available:\n\n* **columnar.compression**: `[none|pglz|zstd|lz4|lz4hc]` - set the compression type\n  for _newly-inserted_ data. Existing data will not be\n  recompressed/decompressed. The default value is `zstd` (if support\n  has been compiled in).\n* **columnar.compression_level**: ``<integer>`` - Sets compression level. Valid\n  settings are from 1 through 19. If the compression method does not\n  support the level chosen, the closest level will be selected\n  instead.\n* **columnar.stripe_row_limit**: ``<integer>`` - the maximum number of rows per\n  stripe for _newly-inserted_ data. Existing stripes of data will not\n  be changed and may have more rows than this maximum value. The\n  default value is `150000`.\n* **columnar.chunk_group_row_limit**: ``<integer>`` - the maximum number of rows per\n  chunk for _newly-inserted_ data. Existing chunks of data will not be\n  changed and may have more rows than this maximum value. The default\n  value is `10000`.\n\nView options for all tables with:\n\n```sql\nSELECT * FROM columnar.options;\n```\n\nYou can also adjust options with a `SET` command of one of the\nfollowing GUCs:\n\n* `columnar.compression`\n* `columnar.compression_level`\n* `columnar.stripe_row_limit`\n* `columnar.chunk_group_row_limit`\n\nGUCs only affect newly-created *tables*, not any newly-created\n*stripes* on an existing table.\n\n## Partitioning\n\nColumnar tables can be used as partitions; and a partitioned table may\nbe made up of any combination of row and columnar partitions.\n\n```sql\nCREATE TABLE parent(ts timestamptz, i int, n numeric, s text)\n  PARTITION BY RANGE (ts);\n\n-- columnar partition\nCREATE TABLE p0 PARTITION OF parent\n  FOR VALUES FROM ('2020-01-01') TO ('2020-02-01')\n  USING COLUMNAR;\n-- columnar partition\nCREATE TABLE p1 PARTITION OF parent\n  FOR VALUES FROM ('2020-02-01') TO ('2020-03-01')\n  USING COLUMNAR;\n-- row partition\nCREATE TABLE p2 PARTITION OF parent\n  FOR VALUES FROM ('2020-03-01') TO ('2020-04-01');\n\nINSERT INTO parent VALUES ('2020-01-15', 10, 100, 'one thousand'); -- columnar\nINSERT INTO parent VALUES ('2020-02-15', 20, 200, 'two thousand'); -- columnar\nINSERT INTO parent VALUES ('2020-03-15', 30, 300, 'three thousand'); -- row\n```\n\nWhen performing operations on a partitioned table with a mix of row\nand columnar partitions, take note of the following behaviors for\noperations that are supported on row tables but not columnar\n(e.g. ``UPDATE``, ``DELETE``, tuple locks, etc.):\n\n* If the operation is targeted at a specific row partition\n  (e.g. ``UPDATE p2 SET i = i + 1``), it will succeed; if targeted at\n  a specified columnar partition (e.g. ``UPDATE p1 SET i = i + 1``),\n  it will fail.\n* If the operation is targeted at the partitioned table and has a\n  ``WHERE`` clause that excludes all columnar partitions\n  (e.g. ``UPDATE parent SET i = i + 1 WHERE ts = '2020-03-15'``), it\n  will succeed.\n* If the operation is targeted at the partitioned table, but does not\n  exclude all columnar partitions, it will fail; even if the actual\n  data to be updated only affects row tables (e.g. ``UPDATE parent SET\n  i = i + 1 WHERE n = 300``).\n\nNote that Citus Columnar supports `btree` and `hash `indexes (and\nthe constraints requiring them) but does not support `gist`, `gin`,\n`spgist` and `brin` indexes.\nFor this reason, if some partitions are columnar and if the index is\nnot supported by Citus Columnar, then it's impossible to create indexes\non the partitioned (parent) table directly. In that case, you need to\ncreate the index on the individual row partitions. Similarly for the\nconstraints that require indexes, e.g.:\n\n```sql\nCREATE INDEX p2_ts_idx ON p2 (ts);\nCREATE UNIQUE INDEX p2_i_unique ON p2 (i);\nALTER TABLE p2 ADD UNIQUE (n);\n```\n\n## Converting Between Row and Columnar\n\nNote: ensure that you understand any advanced features that may be\nused with the table before converting it (e.g. row-level security,\nstorage options, constraints, inheritance, etc.), and ensure that they\nare reproduced in the new table or partition appropriately. ``LIKE``,\nused below, is a shorthand that works only in simple cases.\n\n```sql\nCREATE TABLE my_table(i INT8 DEFAULT '7');\nINSERT INTO my_table VALUES(1);\n-- convert to columnar\nSELECT alter_table_set_access_method('my_table', 'columnar');\n-- back to row\nSELECT alter_table_set_access_method('my_table', 'heap');\n```\n\n# Performance Microbenchmark\n\n*Important*: This microbenchmark is not intended to represent any real\n workload. Compression ratios, and therefore performance, will depend\n heavily on the specific workload. This is only for the purpose of\n illustrating a \"columnar friendly\" contrived workload that showcases\n the benefits of columnar.\n\n## Schema\n\n```sql\nCREATE TABLE perf_row(\n    id INT8,\n    ts TIMESTAMPTZ,\n    customer_id INT8,\n    vendor_id INT8,\n    name TEXT,\n    description TEXT,\n    value NUMERIC,\n    quantity INT4\n);\n\nCREATE TABLE perf_columnar(LIKE perf_row) USING COLUMNAR;\n```\n\n## Data\n\n```sql\nCREATE OR REPLACE FUNCTION random_words(n INT4) RETURNS TEXT LANGUAGE sql AS $$\n  WITH words(w) AS (\n    SELECT ARRAY['zero','one','two','three','four','five','six','seven','eight','nine','ten']\n  ),\n  random (word) AS (\n    SELECT w[(random()*array_length(w, 1))::int] FROM generate_series(1, $1) AS i, words\n  )\n  SELECT string_agg(word, ' ') FROM random;\n$$;\n```\n\n```sql\nINSERT INTO perf_row\n   SELECT\n    g, -- id\n    '2020-01-01'::timestamptz + ('1 minute'::interval * g), -- ts\n    (random() * 1000000)::INT4, -- customer_id\n    (random() * 100)::INT4, -- vendor_id\n    random_words(7), -- name\n    random_words(100), -- description\n    (random() * 100000)::INT4/100.0, -- value\n    (random() * 100)::INT4 -- quantity\n   FROM generate_series(1,75000000) g;\n\nINSERT INTO perf_columnar SELECT * FROM perf_row;\n```\n\n## Compression Ratio\n\n```\n=> SELECT pg_total_relation_size('perf_row')::numeric/pg_total_relation_size('perf_columnar') AS compression_ratio;\n compression_ratio\n--------------------\n 5.3958044063457513\n(1 row)\n```\n\nThe overall compression ratio of columnar table, versus the same data\nstored with row storage, is **5.4X**.\n\n```\n=> VACUUM VERBOSE perf_columnar;\nINFO:  statistics for \"perf_columnar\":\nstorage id: 10000000000\ntotal file size: 8761368576, total data size: 8734266196\ncompression rate: 5.01x\ntotal row count: 75000000, stripe count: 500, average rows per stripe: 150000\nchunk count: 60000, containing data for dropped columns: 0, zstd compressed: 60000\n```\n\n``VACUUM VERBOSE`` reports a smaller compression ratio, because it\nonly averages the compression ratio of the individual chunks, and does\nnot account for the metadata savings of the columnar format.\n\n## System\n\n* Azure VM: Standard D2s v3 (2 vcpus, 8 GiB memory)\n* Linux (ubuntu 18.04)\n* Data Drive: Standard HDD (512GB, 500 IOPS Max, 60 MB/s Max)\n* PostgreSQL 13 (``--with-llvm``, ``--with-python``)\n* ``shared_buffers = 128MB``\n* ``max_parallel_workers_per_gather = 0``\n* ``jit = on``\n\nNote: because this was run on a system with enough physical memory to\nhold a substantial fraction of the table, the IO benefits of columnar\nwon't be entirely realized by the query runtime unless the data size\nis substantially increased.\n\n## Query\n\n```sql\n-- OFFSET 1000 so that no rows are returned, and we collect only timings\n\nSELECT vendor_id, SUM(quantity) FROM perf_row GROUP BY vendor_id OFFSET 1000;\nSELECT vendor_id, SUM(quantity) FROM perf_row GROUP BY vendor_id OFFSET 1000;\nSELECT vendor_id, SUM(quantity) FROM perf_row GROUP BY vendor_id OFFSET 1000;\nSELECT vendor_id, SUM(quantity) FROM perf_columnar GROUP BY vendor_id OFFSET 1000;\nSELECT vendor_id, SUM(quantity) FROM perf_columnar GROUP BY vendor_id OFFSET 1000;\nSELECT vendor_id, SUM(quantity) FROM perf_columnar GROUP BY vendor_id OFFSET 1000;\n```\n\nTiming (median of three runs):\n * row: 436s\n * columnar: 16s\n * speedup: **27X**\n"
  },
  {
    "path": "src/backend/columnar/citus_columnar.control",
    "content": "# Columnar extension\ncomment = 'Citus Columnar extension'\ndefault_version = '15.0-1'\nmodule_pathname = '$libdir/citus_columnar'\nrelocatable = false\nschema = pg_catalog\n"
  },
  {
    "path": "src/backend/columnar/columnar.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar.c\n *\n * This file contains...\n *\n * Copyright (c) 2016, Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"utils/guc.h\"\n#include \"utils/rel.h\"\n\n#include \"citus_version.h\"\n\n#include \"columnar/columnar.h\"\n#include \"columnar/columnar_tableam.h\"\n\n/* Default values for option parameters */\n#define DEFAULT_STRIPE_ROW_COUNT 150000\n#define DEFAULT_CHUNK_ROW_COUNT 10000\n\n#if HAVE_LIBZSTD\n#define DEFAULT_COMPRESSION_TYPE COMPRESSION_ZSTD\n#elif HAVE_CITUS_LIBLZ4\n#define DEFAULT_COMPRESSION_TYPE COMPRESSION_LZ4\n#else\n#define DEFAULT_COMPRESSION_TYPE COMPRESSION_PG_LZ\n#endif\n\nint columnar_compression = DEFAULT_COMPRESSION_TYPE;\nint columnar_stripe_row_limit = DEFAULT_STRIPE_ROW_COUNT;\nint columnar_chunk_group_row_limit = DEFAULT_CHUNK_ROW_COUNT;\nint columnar_compression_level = 3;\n\nstatic const struct config_enum_entry columnar_compression_options[] =\n{\n\t{ \"none\", COMPRESSION_NONE, false },\n\t{ \"pglz\", COMPRESSION_PG_LZ, false },\n#if HAVE_CITUS_LIBLZ4\n\t{ \"lz4\", COMPRESSION_LZ4, false },\n#endif\n#if HAVE_LIBZSTD\n\t{ \"zstd\", COMPRESSION_ZSTD, false },\n#endif\n\t{ NULL, 0, false }\n};\n\nvoid\ncolumnar_init(void)\n{\n\tcolumnar_init_gucs();\n\tcolumnar_tableam_init();\n}\n\n\nvoid\ncolumnar_init_gucs()\n{\n\tDefineCustomEnumVariable(\"columnar.compression\",\n\t\t\t\t\t\t\t \"Compression type for columnar.\",\n\t\t\t\t\t\t\t NULL,\n\t\t\t\t\t\t\t &columnar_compression,\n\t\t\t\t\t\t\t DEFAULT_COMPRESSION_TYPE,\n\t\t\t\t\t\t\t columnar_compression_options,\n\t\t\t\t\t\t\t PGC_USERSET,\n\t\t\t\t\t\t\t 0,\n\t\t\t\t\t\t\t NULL,\n\t\t\t\t\t\t\t NULL,\n\t\t\t\t\t\t\t NULL);\n\n\tDefineCustomIntVariable(\"columnar.compression_level\",\n\t\t\t\t\t\t\t\"Compression level to be used with zstd.\",\n\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\t&columnar_compression_level,\n\t\t\t\t\t\t\t3,\n\t\t\t\t\t\t\tCOMPRESSION_LEVEL_MIN,\n\t\t\t\t\t\t\tCOMPRESSION_LEVEL_MAX,\n\t\t\t\t\t\t\tPGC_USERSET,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\tNULL);\n\n\tDefineCustomIntVariable(\"columnar.stripe_row_limit\",\n\t\t\t\t\t\t\t\"Maximum number of tuples per stripe.\",\n\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\t&columnar_stripe_row_limit,\n\t\t\t\t\t\t\tDEFAULT_STRIPE_ROW_COUNT,\n\t\t\t\t\t\t\tSTRIPE_ROW_COUNT_MINIMUM,\n\t\t\t\t\t\t\tSTRIPE_ROW_COUNT_MAXIMUM,\n\t\t\t\t\t\t\tPGC_USERSET,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\tNULL);\n\n\tDefineCustomIntVariable(\"columnar.chunk_group_row_limit\",\n\t\t\t\t\t\t\t\"Maximum number of rows per chunk.\",\n\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\t&columnar_chunk_group_row_limit,\n\t\t\t\t\t\t\tDEFAULT_CHUNK_ROW_COUNT,\n\t\t\t\t\t\t\tCHUNK_ROW_COUNT_MINIMUM,\n\t\t\t\t\t\t\tCHUNK_ROW_COUNT_MAXIMUM,\n\t\t\t\t\t\t\tPGC_USERSET,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\tNULL);\n}\n\n\n/*\n * ParseCompressionType converts a string to a compression type.\n * For compression algorithms that are invalid or not compiled, it\n * returns COMPRESSION_TYPE_INVALID.\n */\nCompressionType\nParseCompressionType(const char *compressionTypeString)\n{\n\tAssert(compressionTypeString != NULL);\n\n\tfor (int compressionIndex = 0;\n\t\t columnar_compression_options[compressionIndex].name != NULL;\n\t\t compressionIndex++)\n\t{\n\t\tconst char *compressionName = columnar_compression_options[compressionIndex].name;\n\t\tif (strncmp(compressionTypeString, compressionName, NAMEDATALEN) == 0)\n\t\t{\n\t\t\treturn columnar_compression_options[compressionIndex].val;\n\t\t}\n\t}\n\n\treturn COMPRESSION_TYPE_INVALID;\n}\n\n\n/*\n * CompressionTypeStr returns string representation of a compression type.\n * For compression algorithms that are invalid or not compiled, it\n * returns NULL.\n */\nconst char *\nCompressionTypeStr(CompressionType requestedType)\n{\n\tfor (int compressionIndex = 0;\n\t\t columnar_compression_options[compressionIndex].name != NULL;\n\t\t compressionIndex++)\n\t{\n\t\tCompressionType compressionType =\n\t\t\tcolumnar_compression_options[compressionIndex].val;\n\t\tif (compressionType == requestedType)\n\t\t{\n\t\t\treturn columnar_compression_options[compressionIndex].name;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/backend/columnar/columnar_compression.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_compression.c\n *\n * This file contains compression/decompression functions definitions\n * used for columnar.\n *\n * Copyright (c) 2016, Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"common/pg_lzcompress.h\"\n#include \"lib/stringinfo.h\"\n\n#include \"citus_version.h\"\n#include \"pg_version_constants.h\"\n\n#include \"columnar/columnar_compression.h\"\n\n#if HAVE_CITUS_LIBLZ4\n#include <lz4.h>\n#endif\n\n#include \"varatt.h\"\n\n#if HAVE_LIBZSTD\n#include <zstd.h>\n#endif\n\n/*\n *\tThe information at the start of the compressed data. This decription is taken\n *\tfrom pg_lzcompress in pre-9.5 version of PostgreSQL.\n */\ntypedef struct ColumnarCompressHeader\n{\n\tint32 vl_len_;              /* varlena header (do not touch directly!) */\n\tint32 rawsize;\n} ColumnarCompressHeader;\n\n/*\n * Utilities for manipulation of header information for compressed data\n */\n\n#define COLUMNAR_COMPRESS_HDRSZ ((int32) sizeof(ColumnarCompressHeader))\n#define COLUMNAR_COMPRESS_RAWSIZE(ptr) (((ColumnarCompressHeader *) (ptr))->rawsize)\n#define COLUMNAR_COMPRESS_RAWDATA(ptr) (((char *) (ptr)) + COLUMNAR_COMPRESS_HDRSZ)\n#define COLUMNAR_COMPRESS_SET_RAWSIZE(ptr, \\\n\t\t\t\t\t\t\t\t\t  len) (((ColumnarCompressHeader *) (ptr))->rawsize = \\\n\t\t\t\t\t\t\t\t\t\t\t\t(len))\n\n\n/*\n * CompressBuffer compresses the given buffer with the given compression type\n * outputBuffer enlarged to contain compressed data. The function returns true\n * if compression is done, returns false if compression is not done.\n * outputBuffer is valid only if the function returns true.\n */\nbool\nCompressBuffer(StringInfo inputBuffer,\n\t\t\t   StringInfo outputBuffer,\n\t\t\t   CompressionType compressionType,\n\t\t\t   int compressionLevel)\n{\n\tswitch (compressionType)\n\t{\n#if HAVE_CITUS_LIBLZ4\n\t\tcase COMPRESSION_LZ4:\n\t\t{\n\t\t\tint maximumLength = LZ4_compressBound(inputBuffer->len);\n\n\t\t\tresetStringInfo(outputBuffer);\n\t\t\tenlargeStringInfo(outputBuffer, maximumLength);\n\n\t\t\tint compressedSize = LZ4_compress_default(inputBuffer->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  outputBuffer->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  inputBuffer->len, maximumLength);\n\t\t\tif (compressedSize <= 0)\n\t\t\t{\n\t\t\t\telog(DEBUG1,\n\t\t\t\t\t \"failure in LZ4_compress_default, input size=%d, output size=%d\",\n\t\t\t\t\t inputBuffer->len, maximumLength);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\telog(DEBUG1, \"compressed %d bytes to %d bytes\", inputBuffer->len,\n\t\t\t\t compressedSize);\n\n\t\t\toutputBuffer->len = compressedSize;\n\t\t\treturn true;\n\t\t}\n#endif\n\n#if HAVE_LIBZSTD\n\t\tcase COMPRESSION_ZSTD:\n\t\t{\n\t\t\tint maximumLength = ZSTD_compressBound(inputBuffer->len);\n\n\t\t\tresetStringInfo(outputBuffer);\n\t\t\tenlargeStringInfo(outputBuffer, maximumLength);\n\n\t\t\tsize_t compressedSize = ZSTD_compress(outputBuffer->data,\n\t\t\t\t\t\t\t\t\t\t\t\t  outputBuffer->maxlen,\n\t\t\t\t\t\t\t\t\t\t\t\t  inputBuffer->data,\n\t\t\t\t\t\t\t\t\t\t\t\t  inputBuffer->len,\n\t\t\t\t\t\t\t\t\t\t\t\t  compressionLevel);\n\n\t\t\tif (ZSTD_isError(compressedSize))\n\t\t\t{\n\t\t\t\tereport(WARNING, (errmsg(\"zstd compression failed\"),\n\t\t\t\t\t\t\t\t  (errdetail(\"%s\", ZSTD_getErrorName(compressedSize)))));\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\toutputBuffer->len = compressedSize;\n\t\t\treturn true;\n\t\t}\n#endif\n\n\t\tcase COMPRESSION_PG_LZ:\n\t\t{\n\t\t\tuint64 maximumLength = PGLZ_MAX_OUTPUT(inputBuffer->len) +\n\t\t\t\t\t\t\t\t   COLUMNAR_COMPRESS_HDRSZ;\n\t\t\tbool compressionResult = false;\n\n\t\t\tresetStringInfo(outputBuffer);\n\t\t\tenlargeStringInfo(outputBuffer, maximumLength);\n\n\t\t\tint32 compressedByteCount = pglz_compress((const char *) inputBuffer->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  inputBuffer->len,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  COLUMNAR_COMPRESS_RAWDATA(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  outputBuffer->data),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  PGLZ_strategy_always);\n\t\t\tif (compressedByteCount >= 0)\n\t\t\t{\n\t\t\t\tCOLUMNAR_COMPRESS_SET_RAWSIZE(outputBuffer->data, inputBuffer->len);\n\t\t\t\tSET_VARSIZE_COMPRESSED(outputBuffer->data,\n\t\t\t\t\t\t\t\t\t   compressedByteCount + COLUMNAR_COMPRESS_HDRSZ);\n\t\t\t\tcompressionResult = true;\n\t\t\t}\n\n\t\t\tif (compressionResult)\n\t\t\t{\n\t\t\t\toutputBuffer->len = VARSIZE(outputBuffer->data);\n\t\t\t}\n\n\t\t\treturn compressionResult;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\n\n/*\n * DecompressBuffer decompresses the given buffer with the given compression\n * type. This function returns the buffer as-is when no compression is applied.\n */\nStringInfo\nDecompressBuffer(StringInfo buffer,\n\t\t\t\t CompressionType compressionType,\n\t\t\t\t uint64 decompressedSize)\n{\n\tswitch (compressionType)\n\t{\n\t\tcase COMPRESSION_NONE:\n\t\t{\n\t\t\treturn buffer;\n\t\t}\n\n#if HAVE_CITUS_LIBLZ4\n\t\tcase COMPRESSION_LZ4:\n\t\t{\n\t\t\tStringInfo decompressedBuffer = makeStringInfo();\n\t\t\tenlargeStringInfo(decompressedBuffer, decompressedSize);\n\n\t\t\tint lz4DecompressSize = LZ4_decompress_safe(buffer->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdecompressedBuffer->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbuffer->len,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdecompressedSize);\n\n\t\t\tif (lz4DecompressSize != decompressedSize)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"cannot decompress the buffer\"),\n\t\t\t\t\t\t\t\terrdetail(\"Expected %lu bytes, but received %d bytes\",\n\t\t\t\t\t\t\t\t\t\t  decompressedSize, lz4DecompressSize)));\n\t\t\t}\n\n\t\t\tdecompressedBuffer->len = decompressedSize;\n\n\t\t\treturn decompressedBuffer;\n\t\t}\n#endif\n\n#if HAVE_LIBZSTD\n\t\tcase COMPRESSION_ZSTD:\n\t\t{\n\t\t\tStringInfo decompressedBuffer = makeStringInfo();\n\t\t\tenlargeStringInfo(decompressedBuffer, decompressedSize);\n\n\t\t\tsize_t zstdDecompressSize = ZSTD_decompress(decompressedBuffer->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdecompressedSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbuffer->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbuffer->len);\n\t\t\tif (ZSTD_isError(zstdDecompressSize))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"zstd decompression failed\"),\n\t\t\t\t\t\t\t\t(errdetail(\"%s\", ZSTD_getErrorName(\n\t\t\t\t\t\t\t\t\t\t\t   zstdDecompressSize)))));\n\t\t\t}\n\n\t\t\tif (zstdDecompressSize != decompressedSize)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"unexpected decompressed size\"),\n\t\t\t\t\t\t\t\terrdetail(\"Expected %ld, received %ld\", decompressedSize,\n\t\t\t\t\t\t\t\t\t\t  zstdDecompressSize)));\n\t\t\t}\n\n\t\t\tdecompressedBuffer->len = decompressedSize;\n\n\t\t\treturn decompressedBuffer;\n\t\t}\n#endif\n\n\t\tcase COMPRESSION_PG_LZ:\n\t\t{\n\t\t\tuint32 compressedDataSize = VARSIZE(buffer->data) - COLUMNAR_COMPRESS_HDRSZ;\n\t\t\tuint32 decompressedDataSize = COLUMNAR_COMPRESS_RAWSIZE(buffer->data);\n\n\t\t\tif (compressedDataSize + COLUMNAR_COMPRESS_HDRSZ != buffer->len)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"cannot decompress the buffer\"),\n\t\t\t\t\t\t\t\terrdetail(\"Expected %u bytes, but received %u bytes\",\n\t\t\t\t\t\t\t\t\t\t  compressedDataSize, buffer->len)));\n\t\t\t}\n\n\t\t\tchar *decompressedData = palloc0(decompressedDataSize);\n\n\t\t\tint32 decompressedByteCount = pglz_decompress(COLUMNAR_COMPRESS_RAWDATA(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  buffer->data),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  compressedDataSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  decompressedData,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  decompressedDataSize, true);\n\n\t\t\tif (decompressedByteCount < 0)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"cannot decompress the buffer\"),\n\t\t\t\t\t\t\t\terrdetail(\"compressed data is corrupted\")));\n\t\t\t}\n\n\t\t\tStringInfo decompressedBuffer = palloc0(sizeof(StringInfoData));\n\t\t\tdecompressedBuffer->data = decompressedData;\n\t\t\tdecompressedBuffer->len = decompressedDataSize;\n\t\t\tdecompressedBuffer->maxlen = decompressedDataSize;\n\n\t\t\treturn decompressedBuffer;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unexpected compression type: %d\", compressionType)));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/columnar/columnar_customscan.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_customscan.c\n *\n * This file contains the implementation of a postgres custom scan that\n * we use to push down the projections into the table access methods.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <math.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/amapi.h\"\n#include \"access/skey.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_statistic.h\"\n#include \"commands/defrem.h\"\n\n#include \"columnar/columnar_version_compat.h\"\n#if PG_VERSION_NUM >= PG_VERSION_18\n#include \"commands/explain_format.h\"\n#endif\n#include \"executor/executor.h\"   /* for ExecInitExprWithParams(), ExecEvalExpr() */\n#include \"nodes/execnodes.h\"     /* for ExprState, ExprContext, etc. */\n#include \"nodes/extensible.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/plannodes.h\"\n#include \"optimizer/cost.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/pathnode.h\"\n#include \"optimizer/paths.h\"\n#include \"optimizer/plancat.h\"\n#include \"optimizer/restrictinfo.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parsetree.h\"\n#include \"utils/builtins.h\"\n#include \"utils/guc.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/relcache.h\"\n#include \"utils/ruleutils.h\"\n#include \"utils/selfuncs.h\"\n#include \"utils/spccache.h\"\n\n#include \"citus_version.h\"\n\n#include \"columnar/columnar.h\"\n#include \"columnar/columnar_customscan.h\"\n#include \"columnar/columnar_metadata.h\"\n#include \"columnar/columnar_tableam.h\"\n\n#include \"distributed/listutils.h\"\n\n/*\n * ColumnarScanState represents the state for a columnar scan. It's a\n * CustomScanState with additional fields specific to columnar scans.\n */\ntypedef struct ColumnarScanState\n{\n\tCustomScanState custom_scanstate; /* must be first field */\n\n\tExprContext *css_RuntimeContext;\n\tList *qual;\n} ColumnarScanState;\n\n\ntypedef bool (*PathPredicate)(Path *path);\n\n\n/* functions to cost paths in-place */\nstatic void CostColumnarPaths(PlannerInfo *root, RelOptInfo *rel, Oid relationId);\nstatic void CostColumnarIndexPath(PlannerInfo *root, RelOptInfo *rel, Oid relationId,\n\t\t\t\t\t\t\t\t  IndexPath *indexPath);\nstatic void CostColumnarSeqPath(RelOptInfo *rel, Oid relationId, Path *path);\nstatic void CostColumnarScan(PlannerInfo *root, RelOptInfo *rel, Oid relationId,\n\t\t\t\t\t\t\t CustomPath *cpath, int numberOfColumnsRead,\n\t\t\t\t\t\t\t int nClauses);\n\n/* functions to add new paths */\nstatic void AddColumnarScanPaths(PlannerInfo *root, RelOptInfo *rel,\n\t\t\t\t\t\t\t\t RangeTblEntry *rte);\nstatic void AddColumnarScanPath(PlannerInfo *root, RelOptInfo *rel,\n\t\t\t\t\t\t\t\tRangeTblEntry *rte, Relids required_relids);\n\n/* helper functions to be used when costing paths or altering them */\nstatic void RemovePathsByPredicate(RelOptInfo *rel, PathPredicate removePathPredicate);\nstatic bool IsNotIndexPath(Path *path);\nstatic Cost ColumnarIndexScanAdditionalCost(PlannerInfo *root, RelOptInfo *rel,\n\t\t\t\t\t\t\t\t\t\t\tOid relationId, IndexPath *indexPath);\nstatic int RelationIdGetNumberOfAttributes(Oid relationId);\nstatic Cost ColumnarPerStripeScanCost(RelOptInfo *rel, Oid relationId,\n\t\t\t\t\t\t\t\t\t  int numberOfColumnsRead);\nstatic uint64 ColumnarTableStripeCount(Oid relationId);\nstatic Path * CreateColumnarSeqScanPath(PlannerInfo *root, RelOptInfo *rel,\n\t\t\t\t\t\t\t\t\t\tOid relationId);\nstatic void AddColumnarScanPathsRec(PlannerInfo *root, RelOptInfo *rel,\n\t\t\t\t\t\t\t\t\tRangeTblEntry *rte, Relids paramRelids,\n\t\t\t\t\t\t\t\t\tRelids candidateRelids,\n\t\t\t\t\t\t\t\t\tint depthLimit);\n\n/* hooks and callbacks */\nstatic void ColumnarSetRelPathlistHook(PlannerInfo *root, RelOptInfo *rel, Index rti,\n\t\t\t\t\t\t\t\t\t   RangeTblEntry *rte);\nstatic void ColumnarGetRelationInfoHook(PlannerInfo *root, Oid relationObjectId,\n\t\t\t\t\t\t\t\t\t\tbool inhparent, RelOptInfo *rel);\nstatic Plan * ColumnarScanPath_PlanCustomPath(PlannerInfo *root,\n\t\t\t\t\t\t\t\t\t\t\t  RelOptInfo *rel,\n\t\t\t\t\t\t\t\t\t\t\t  struct CustomPath *best_path,\n\t\t\t\t\t\t\t\t\t\t\t  List *tlist,\n\t\t\t\t\t\t\t\t\t\t\t  List *clauses,\n\t\t\t\t\t\t\t\t\t\t\t  List *custom_plans);\nstatic List * ColumnarScanPath_ReparameterizeCustomPathByChild(PlannerInfo *root,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   List *custom_private,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   RelOptInfo *child_rel);\nstatic Node * ColumnarScan_CreateCustomScanState(CustomScan *cscan);\n\nstatic void ColumnarScan_BeginCustomScan(CustomScanState *node, EState *estate,\n\t\t\t\t\t\t\t\t\t\t int eflags);\nstatic TupleTableSlot * ColumnarScan_ExecCustomScan(CustomScanState *node);\nstatic void ColumnarScan_EndCustomScan(CustomScanState *node);\nstatic void ColumnarScan_ReScanCustomScan(CustomScanState *node);\nstatic void ColumnarScan_ExplainCustomScan(CustomScanState *node, List *ancestors,\n\t\t\t\t\t\t\t\t\t\t   ExplainState *es);\n\n/* helper functions to build strings for EXPLAIN */\nstatic const char * ColumnarPushdownClausesStr(List *context, List *clauses);\nstatic const char * ColumnarProjectedColumnsStr(List *context,\n\t\t\t\t\t\t\t\t\t\t\t\tList *projectedColumns);\nstatic List * set_deparse_context_planstate(List *dpcontext, Node *node,\n\t\t\t\t\t\t\t\t\t\t\tList *ancestors);\n\n/* other helpers */\nstatic List * ColumnarVarNeeded(ColumnarScanState *columnarScanState);\nstatic Bitmapset * ColumnarAttrNeeded(ScanState *ss);\nstatic Bitmapset * fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns);\n\n/* saved hook value in case of unload */\nstatic set_rel_pathlist_hook_type PreviousSetRelPathlistHook = NULL;\nstatic get_relation_info_hook_type PreviousGetRelationInfoHook = NULL;\n\nstatic bool EnableColumnarCustomScan = true;\nstatic bool EnableColumnarQualPushdown = true;\nstatic double ColumnarQualPushdownCorrelationThreshold = 0.9;\nstatic int ColumnarMaxCustomScanPaths = 64;\nstatic int ColumnarPlannerDebugLevel = DEBUG3;\n\n\nconst struct CustomPathMethods ColumnarScanPathMethods = {\n\t.CustomName = \"ColumnarScan\",\n\t.PlanCustomPath = ColumnarScanPath_PlanCustomPath,\n\t.ReparameterizeCustomPathByChild = ColumnarScanPath_ReparameterizeCustomPathByChild,\n};\n\nconst struct CustomScanMethods ColumnarScanScanMethods = {\n\t.CustomName = \"ColumnarScan\",\n\t.CreateCustomScanState = ColumnarScan_CreateCustomScanState,\n};\n\nconst struct CustomExecMethods ColumnarScanExecuteMethods = {\n\t.CustomName = \"ColumnarScan\",\n\n\t.BeginCustomScan = ColumnarScan_BeginCustomScan,\n\t.ExecCustomScan = ColumnarScan_ExecCustomScan,\n\t.EndCustomScan = ColumnarScan_EndCustomScan,\n\t.ReScanCustomScan = ColumnarScan_ReScanCustomScan,\n\n\t.ExplainCustomScan = ColumnarScan_ExplainCustomScan,\n};\n\nstatic const struct config_enum_entry debug_level_options[] = {\n\t{ \"debug5\", DEBUG5, false },\n\t{ \"debug4\", DEBUG4, false },\n\t{ \"debug3\", DEBUG3, false },\n\t{ \"debug2\", DEBUG2, false },\n\t{ \"debug1\", DEBUG1, false },\n\t{ \"debug\", DEBUG2, true },\n\t{ \"info\", INFO, false },\n\t{ \"notice\", NOTICE, false },\n\t{ \"warning\", WARNING, false },\n\t{ \"log\", LOG, false },\n\t{ NULL, 0, false }\n};\n\n\n/*\n * columnar_customscan_init installs the hook required to intercept the postgres planner and\n * provide extra paths for columnar tables\n */\nvoid\ncolumnar_customscan_init()\n{\n\tPreviousSetRelPathlistHook = set_rel_pathlist_hook;\n\tset_rel_pathlist_hook = ColumnarSetRelPathlistHook;\n\n\tPreviousGetRelationInfoHook = get_relation_info_hook;\n\tget_relation_info_hook = ColumnarGetRelationInfoHook;\n\n\t/* register customscan specific GUC's */\n\tDefineCustomBoolVariable(\n\t\t\"columnar.enable_custom_scan\",\n\t\tgettext_noop(\"Enables the use of a custom scan to push projections and quals \"\n\t\t\t\t\t \"into the storage layer.\"),\n\t\tNULL,\n\t\t&EnableColumnarCustomScan,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\tDefineCustomBoolVariable(\n\t\t\"columnar.enable_qual_pushdown\",\n\t\tgettext_noop(\"Enables qual pushdown into columnar. This has no effect unless \"\n\t\t\t\t\t \"columnar.enable_custom_scan is true.\"),\n\t\tNULL,\n\t\t&EnableColumnarQualPushdown,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\tDefineCustomRealVariable(\n\t\t\"columnar.qual_pushdown_correlation_threshold\",\n\t\tgettext_noop(\"Correlation threshold to attempt to push a qual \"\n\t\t\t\t\t \"referencing the given column. A value of 0 means \"\n\t\t\t\t\t \"attempt to push down all quals, even if the column \"\n\t\t\t\t\t \"is uncorrelated.\"),\n\t\tNULL,\n\t\t&ColumnarQualPushdownCorrelationThreshold,\n\t\t0.9,\n\t\t0.0,\n\t\t1.0,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\tDefineCustomIntVariable(\n\t\t\"columnar.max_custom_scan_paths\",\n\t\tgettext_noop(\"Maximum number of custom scan paths to generate \"\n\t\t\t\t\t \"for a columnar table when planning.\"),\n\t\tNULL,\n\t\t&ColumnarMaxCustomScanPaths,\n\t\t64,\n\t\t1,\n\t\t1024,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\tDefineCustomEnumVariable(\n\t\t\"columnar.planner_debug_level\",\n\t\t\"Message level for columnar planning information.\",\n\t\tNULL,\n\t\t&ColumnarPlannerDebugLevel,\n\t\tDEBUG3,\n\t\tdebug_level_options,\n\t\tPGC_USERSET,\n\t\t0,\n\t\tNULL,\n\t\tNULL,\n\t\tNULL);\n\n\tRegisterCustomScanMethods(&ColumnarScanScanMethods);\n}\n\n\nstatic void\nColumnarSetRelPathlistHook(PlannerInfo *root, RelOptInfo *rel, Index rti,\n\t\t\t\t\t\t   RangeTblEntry *rte)\n{\n\t/* call into previous hook if assigned */\n\tif (PreviousSetRelPathlistHook)\n\t{\n\t\tPreviousSetRelPathlistHook(root, rel, rti, rte);\n\t}\n\n\tif (!OidIsValid(rte->relid) || rte->rtekind != RTE_RELATION || rte->inh)\n\t{\n\t\t/* some calls to the pathlist hook don't have a valid relation set. Do nothing */\n\t\treturn;\n\t}\n\n\t/*\n\t * Here we want to inspect if this relation pathlist hook is accessing a columnar table.\n\t * If that is the case we want to insert an extra path that pushes down the projection\n\t * into the scan of the table to minimize the data read.\n\t */\n\tRelation relation = RelationIdGetRelation(rte->relid);\n\tif (!RelationIsValid(relation))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not open relation with OID %u\", rte->relid)));\n\t}\n\n\tif (relation->rd_tableam == GetColumnarTableAmRoutine())\n\t{\n\t\tif (rte->tablesample != NULL)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"sample scans not supported on columnar tables\")));\n\t\t}\n\n\t\tif (list_length(rel->partial_pathlist) != 0)\n\t\t{\n\t\t\t/*\n\t\t\t * Parallel scans on columnar tables are already discardad by\n\t\t\t * ColumnarGetRelationInfoHook but be on the safe side.\n\t\t\t */\n\t\t\telog(ERROR, \"parallel scans on columnar are not supported\");\n\t\t}\n\n\t\t/*\n\t\t * There are cases where IndexPath is normally more preferrable over\n\t\t * SeqPath for heapAM but not for columnarAM. In such cases, an\n\t\t * IndexPath could wrongly dominate a SeqPath based on the costs\n\t\t * estimated by postgres earlier. For this reason, here we manually\n\t\t * create a SeqPath, estimate the cost based on columnarAM and append\n\t\t * to pathlist.\n\t\t *\n\t\t * Before doing that, we first re-cost all the existing paths so that\n\t\t * add_path makes correct cost comparisons when appending our SeqPath.\n\t\t */\n\t\tCostColumnarPaths(root, rel, rte->relid);\n\n\t\tPath *seqPath = CreateColumnarSeqScanPath(root, rel, rte->relid);\n\t\tadd_path(rel, seqPath);\n\n\t\tif (EnableColumnarCustomScan)\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"pathlist hook for columnar table am\")));\n\n\t\t\t/*\n\t\t\t * When columnar custom scan is enabled (columnar.enable_custom_scan),\n\t\t\t * we only consider ColumnarScanPath's & IndexPath's. For this reason,\n\t\t\t * we remove other paths and re-estimate IndexPath costs to make accurate\n\t\t\t * comparisons between them.\n\t\t\t *\n\t\t\t * Even more, we might calculate an equal cost for a\n\t\t\t * ColumnarCustomScan and a SeqPath if we are reading all columns\n\t\t\t * of given table since we don't consider chunk group filtering\n\t\t\t * when costing ColumnarCustomScan.\n\t\t\t * In that case, if we don't remove SeqPath's, we might wrongly choose\n\t\t\t * SeqPath thinking that its cost would be equal to ColumnarCustomScan.\n\t\t\t */\n\t\t\tRemovePathsByPredicate(rel, IsNotIndexPath);\n\t\t\tAddColumnarScanPaths(root, rel, rte);\n\t\t}\n\t}\n\tRelationClose(relation);\n}\n\n\nstatic void\nColumnarGetRelationInfoHook(PlannerInfo *root, Oid relationObjectId,\n\t\t\t\t\t\t\tbool inhparent, RelOptInfo *rel)\n{\n\tif (PreviousGetRelationInfoHook)\n\t{\n\t\tPreviousGetRelationInfoHook(root, relationObjectId, inhparent, rel);\n\t}\n\n\tif (IsColumnarTableAmTable(relationObjectId))\n\t{\n\t\t/* disable parallel query */\n\t\trel->rel_parallel_workers = 0;\n\n\t\t/* disable index-only scan */\n\t\tIndexOptInfo *indexOptInfo = NULL;\n\t\tforeach_declared_ptr(indexOptInfo, rel->indexlist)\n\t\t{\n\t\t\tmemset(indexOptInfo->canreturn, false, indexOptInfo->ncolumns * sizeof(bool));\n\t\t}\n\t}\n}\n\n\n/*\n * RemovePathsByPredicate removes the paths that removePathPredicate\n * evaluates to true from pathlist of given rel.\n */\nstatic void\nRemovePathsByPredicate(RelOptInfo *rel, PathPredicate removePathPredicate)\n{\n\tList *filteredPathList = NIL;\n\n\tPath *path = NULL;\n\tforeach_declared_ptr(path, rel->pathlist)\n\t{\n\t\tif (!removePathPredicate(path))\n\t\t{\n\t\t\tfilteredPathList = lappend(filteredPathList, path);\n\t\t}\n\t}\n\n\trel->pathlist = filteredPathList;\n}\n\n\n/*\n * IsNotIndexPath returns true if given path is not an IndexPath.\n */\nstatic bool\nIsNotIndexPath(Path *path)\n{\n\treturn !IsA(path, IndexPath);\n}\n\n\n/*\n * CreateColumnarSeqScanPath returns Path for sequential scan on columnar\n * table with relationId.\n */\nstatic Path *\nCreateColumnarSeqScanPath(PlannerInfo *root, RelOptInfo *rel, Oid relationId)\n{\n\t/* columnar doesn't support parallel scan */\n\tint parallelWorkers = 0;\n\n\tRelids requiredOuter = rel->lateral_relids;\n\tPath *path = create_seqscan_path(root, rel, requiredOuter, parallelWorkers);\n\tCostColumnarSeqPath(rel, relationId, path);\n\treturn path;\n}\n\n\n/*\n * CostColumnarPaths re-costs paths of given RelOptInfo for\n * columnar table with relationId.\n */\nstatic void\nCostColumnarPaths(PlannerInfo *root, RelOptInfo *rel, Oid relationId)\n{\n\tPath *path = NULL;\n\tforeach_declared_ptr(path, rel->pathlist)\n\t{\n\t\tif (IsA(path, IndexPath))\n\t\t{\n\t\t\t/*\n\t\t\t * Since we don't provide implementations for scan_bitmap_next_block\n\t\t\t * & scan_bitmap_next_tuple, postgres doesn't generate bitmap index\n\t\t\t * scan paths for columnar tables already (see related comments in\n\t\t\t * TableAmRoutine). For this reason, we only consider IndexPath's\n\t\t\t * here.\n\t\t\t */\n\t\t\tCostColumnarIndexPath(root, rel, relationId, (IndexPath *) path);\n\t\t}\n\t\telse if (path->pathtype == T_SeqScan)\n\t\t{\n\t\t\tCostColumnarSeqPath(rel, relationId, path);\n\t\t}\n\t}\n}\n\n\n/*\n * CostColumnarIndexPath re-costs given index path for columnar table with\n * relationId.\n */\nstatic void\nCostColumnarIndexPath(PlannerInfo *root, RelOptInfo *rel, Oid relationId,\n\t\t\t\t\t  IndexPath *indexPath)\n{\n\tif (!enable_indexscan)\n\t{\n\t\t/* costs are already set to disable_cost, don't adjust them */\n\t\treturn;\n\t}\n\n\tereport(DEBUG4, (errmsg(\"columnar table index scan costs estimated by \"\n\t\t\t\t\t\t\t\"indexAM: startup cost = %.10f, total cost = \"\n\t\t\t\t\t\t\t\"%.10f\", indexPath->path.startup_cost,\n\t\t\t\t\t\t\tindexPath->path.total_cost)));\n\n\t/*\n\t * We estimate the cost for columnar table read during index scan. Also,\n\t * instead of overwriting total cost, we \"add\" ours to the cost estimated\n\t * by indexAM since we should consider index traversal related costs too.\n\t */\n\tCost columnarIndexScanCost = ColumnarIndexScanAdditionalCost(root, rel, relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t indexPath);\n\tindexPath->path.total_cost += columnarIndexScanCost;\n\n\tereport(DEBUG4, (errmsg(\"columnar table index scan costs re-estimated \"\n\t\t\t\t\t\t\t\"by columnarAM (including indexAM costs): \"\n\t\t\t\t\t\t\t\"startup cost = %.10f, total cost = %.10f\",\n\t\t\t\t\t\t\tindexPath->path.startup_cost,\n\t\t\t\t\t\t\tindexPath->path.total_cost)));\n}\n\n\n/*\n * ColumnarIndexScanAdditionalCost returns additional cost estimated for\n * index scan described by IndexPath for columnar table with relationId.\n */\nstatic Cost\nColumnarIndexScanAdditionalCost(PlannerInfo *root, RelOptInfo *rel,\n\t\t\t\t\t\t\t\tOid relationId, IndexPath *indexPath)\n{\n\tint numberOfColumnsRead = RelationIdGetNumberOfAttributes(relationId);\n\tCost perStripeCost = ColumnarPerStripeScanCost(rel, relationId, numberOfColumnsRead);\n\n\t/*\n\t * We don't need to pass correct loop count to amcostestimate since we\n\t * will only use index correlation & index selectivity, and loop count\n\t * doesn't have any effect on those two.\n\t */\n\tdouble fakeLoopCount = 1;\n\tCost fakeIndexStartupCost;\n\tCost fakeIndexTotalCost;\n\tdouble fakeIndexPages;\n\tSelectivity indexSelectivity;\n\tdouble indexCorrelation;\n\tamcostestimate_function amcostestimate = indexPath->indexinfo->amcostestimate;\n\tamcostestimate(root, indexPath, fakeLoopCount, &fakeIndexStartupCost,\n\t\t\t\t   &fakeIndexTotalCost, &indexSelectivity,\n\t\t\t\t   &indexCorrelation, &fakeIndexPages);\n\n\tRelation relation = RelationIdGetRelation(relationId);\n\tif (!RelationIsValid(relation))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not open relation with OID %u\", relationId)));\n\t}\n\n\tuint64 rowCount = ColumnarTableRowCount(relation);\n\tRelationClose(relation);\n\tdouble estimatedRows = rowCount * indexSelectivity;\n\n\t/*\n\t * In the worst case (i.e no correlation between the column & the index),\n\t * we need to read a different stripe for each row.\n\t */\n\tdouble maxStripeReadCount = estimatedRows;\n\n\t/*\n\t * In the best case (i.e the column is fully correlated with the index),\n\t * we wouldn't read the same stripe again and again thanks\n\t * to locality.\n\t */\n\tdouble avgStripeRowCount =\n\t\trowCount / (double) ColumnarTableStripeCount(relationId);\n\tdouble minStripeReadCount = estimatedRows / avgStripeRowCount;\n\n\t/*\n\t * While being close to 0 means low correlation, being close to -1 or +1\n\t * means high correlation. For index scans on columnar tables, it doesn't\n\t * matter if the column and the index are \"correlated\" (+1) or\n\t * \"anti-correlated\" (-1) since both help us avoiding from reading the\n\t * same stripe again and again.\n\t */\n\tdouble absIndexCorrelation = fabs(indexCorrelation);\n\n\t/*\n\t * To estimate the number of stripes that we need to read, we do linear\n\t * interpolation between minStripeReadCount & maxStripeReadCount. To do\n\t * that, we use complement to 1 of absolute correlation, where being\n\t * close to 0 means high correlation and being close to 1 means low\n\t * correlation.\n\t * In practice, we only want to do an index scan when absIndexCorrelation\n\t * is 1 (or extremely close to it), or when the absolute number of tuples\n\t * returned is very small. Other cases will have a prohibitive cost.\n\t */\n\tdouble complementIndexCorrelation = 1 - absIndexCorrelation;\n\tdouble estimatedStripeReadCount =\n\t\tminStripeReadCount + complementIndexCorrelation * (maxStripeReadCount -\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   minStripeReadCount);\n\n\t/* even in the best case, we will read a single stripe */\n\testimatedStripeReadCount = Max(estimatedStripeReadCount, 1.0);\n\n\tCost scanCost = perStripeCost * estimatedStripeReadCount;\n\n\tereport(DEBUG4, (errmsg(\"re-costing index scan for columnar table: \"\n\t\t\t\t\t\t\t\"selectivity = %.10f, complement abs \"\n\t\t\t\t\t\t\t\"correlation = %.10f, per stripe cost = %.10f, \"\n\t\t\t\t\t\t\t\"estimated stripe read count = %.10f, \"\n\t\t\t\t\t\t\t\"total additional cost = %.10f\",\n\t\t\t\t\t\t\tindexSelectivity, complementIndexCorrelation,\n\t\t\t\t\t\t\tperStripeCost, estimatedStripeReadCount,\n\t\t\t\t\t\t\tscanCost)));\n\n\treturn scanCost;\n}\n\n\n/*\n * CostColumnarSeqPath sets costs given seq path for columnar table with\n * relationId.\n */\nstatic void\nCostColumnarSeqPath(RelOptInfo *rel, Oid relationId, Path *path)\n{\n\tif (!enable_seqscan)\n\t{\n\t\t/* costs are already set to disable_cost, don't adjust them */\n\t\treturn;\n\t}\n\n\t/*\n\t * Seq scan doesn't support projection or qual pushdown, so we will read\n\t * all the stripes and all the columns.\n\t */\n\tdouble stripesToRead = ColumnarTableStripeCount(relationId);\n\tint numberOfColumnsRead = RelationIdGetNumberOfAttributes(relationId);\n\n\tpath->startup_cost = 0;\n\tpath->total_cost = stripesToRead *\n\t\t\t\t\t   ColumnarPerStripeScanCost(rel, relationId, numberOfColumnsRead);\n}\n\n\n/*\n * RelationIdGetNumberOfAttributes returns number of attributes that relation\n * with relationId has.\n */\nstatic int\nRelationIdGetNumberOfAttributes(Oid relationId)\n{\n\tRelation relation = RelationIdGetRelation(relationId);\n\tif (!RelationIsValid(relation))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not open relation with OID %u\", relationId)));\n\t}\n\n\tint nattrs = relation->rd_att->natts;\n\tRelationClose(relation);\n\treturn nattrs;\n}\n\n\n/*\n * CheckVarStats() checks whether a qual involving this Var is likely to be\n * useful based on the correlation stats. If so, or if stats are unavailable,\n * return true; otherwise return false and sets absVarCorrelation in case\n * caller wants to use for logging purposes.\n */\nstatic bool\nCheckVarStats(PlannerInfo *root, Var *var, Oid sortop, float4 *absVarCorrelation)\n{\n\t/*\n\t * Collect isunique, ndistinct, and varCorrelation.\n\t */\n\tVariableStatData varStatData;\n\texamine_variable(root, (Node *) var, var->varno, &varStatData);\n\tif (varStatData.rel == NULL ||\n\t\t!HeapTupleIsValid(varStatData.statsTuple))\n\t{\n\t\treturn true;\n\t}\n\n\tAttStatsSlot sslot;\n\tif (!get_attstatsslot(&sslot, varStatData.statsTuple,\n\t\t\t\t\t\t  STATISTIC_KIND_CORRELATION, sortop,\n\t\t\t\t\t\t  ATTSTATSSLOT_NUMBERS))\n\t{\n\t\tReleaseVariableStats(varStatData);\n\t\treturn true;\n\t}\n\n\tAssert(sslot.nnumbers == 1);\n\n\tfloat4 varCorrelation = sslot.numbers[0];\n\n\tReleaseVariableStats(varStatData);\n\n\t/*\n\t * If the Var is not highly correlated, then the chunk's min/max bounds\n\t * will be nearly useless.\n\t */\n\tif (fabs(varCorrelation) < ColumnarQualPushdownCorrelationThreshold)\n\t{\n\t\tif (absVarCorrelation)\n\t\t{\n\t\t\t/*\n\t\t\t * Report absVarCorrelation if caller wants to know why given\n\t\t\t * var is rejected.\n\t\t\t */\n\t\t\t*absVarCorrelation = fabs(varCorrelation);\n\t\t}\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ExprReferencesRelid returns true if any of the Expr's Vars refer to the\n * given relid; false otherwise.\n */\nstatic bool\nExprReferencesRelid(Expr *expr, Index relid)\n{\n\tList *exprVars = pull_var_clause(\n\t\t(Node *) expr, PVC_RECURSE_AGGREGATES |\n\t\tPVC_RECURSE_WINDOWFUNCS | PVC_RECURSE_PLACEHOLDERS);\n\tListCell *lc;\n\tforeach(lc, exprVars)\n\t{\n\t\tVar *var = (Var *) lfirst(lc);\n\t\tif (var->varno == relid)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ExtractPushdownClause extracts an Expr node from given clause for pushing down\n * into the given rel (including join clauses). This test may not be exact in\n * all cases; it's used to reduce the search space for parameterization.\n *\n * Note that we don't try to handle cases like \"Var + ExtParam = 3\". That\n * would require going through eval_const_expression after parameter binding,\n * and that doesn't seem worth the effort. Here we just look for \"Var op Expr\"\n * or \"Expr op Var\", where Var references rel and Expr references other rels\n * (or no rels at all).\n *\n * Moreover, this function also looks into BoolExpr's to recursively extract\n * pushdownable OpExpr's of them:\n * i)   AND_EXPR:\n *      Take pushdownable args of AND expressions by ignoring the other args.\n * ii)  OR_EXPR:\n *      Ignore the whole OR expression if we cannot exract a pushdownable Expr\n *      from one of its args.\n * iii) NOT_EXPR:\n *      Simply ignore NOT expressions since we don't expect to see them before\n *      an expression that we can pushdown, see the comment in function.\n *\n * The reasoning for those three rules could also be summarized as such;\n * for any expression that we cannot push-down, we must assume that it\n * evaluates to true.\n *\n * For example, given following WHERE clause:\n * (\n *     (a > random() OR a < 30)\n *     AND\n *     a < 200\n * ) OR\n * (\n *     a = 300\n *     OR\n *     a > 400\n * );\n * Even if we can pushdown (a < 30), we cannot pushdown (a > random() OR a < 30)\n * due to (a > random()). However, we can pushdown (a < 200), so we extract\n * (a < 200) from the lhs of the top level OR expression.\n *\n * For the rhs of the top level OR expression, since we can pushdown both (a = 300)\n * and (a > 400), we take this part as is.\n *\n * Finally, since both sides of the top level OR expression yielded pushdownable\n * expressions, we will pushdown the following:\n *  (a < 200) OR ((a = 300) OR (a > 400))\n */\nstatic Expr *\nExtractPushdownClause(PlannerInfo *root, RelOptInfo *rel, Node *node)\n{\n\tCHECK_FOR_INTERRUPTS();\n\tcheck_stack_depth();\n\n\tif (node == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (IsA(node, BoolExpr))\n\t{\n\t\tBoolExpr *boolExpr = castNode(BoolExpr, node);\n\t\tif (boolExpr->boolop == NOT_EXPR)\n\t\t{\n\t\t\t/*\n\t\t\t * Standard planner should have already applied de-morgan rule to\n\t\t\t * simple NOT expressions. If we encounter with such an expression\n\t\t\t * here, then it can't be a pushdownable one, such as:\n\t\t\t *   WHERE id NOT IN (SELECT id FROM something).\n\t\t\t */\n\t\t\tereport(ColumnarPlannerDebugLevel,\n\t\t\t\t\t(errmsg(\"columnar planner: cannot push down clause: \"\n\t\t\t\t\t\t\t\"must not contain a subplan\")));\n\t\t\treturn NULL;\n\t\t}\n\n\t\tList *pushdownableArgs = NIL;\n\n\t\tNode *boolExprArg = NULL;\n\t\tforeach_declared_ptr(boolExprArg, boolExpr->args)\n\t\t{\n\t\t\tExpr *pushdownableArg = ExtractPushdownClause(root, rel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  (Node *) boolExprArg);\n\t\t\tif (pushdownableArg)\n\t\t\t{\n\t\t\t\tpushdownableArgs = lappend(pushdownableArgs, pushdownableArg);\n\t\t\t}\n\t\t\telse if (boolExpr->boolop == OR_EXPR)\n\t\t\t{\n\t\t\t\tereport(ColumnarPlannerDebugLevel,\n\t\t\t\t\t\t(errmsg(\"columnar planner: cannot push down clause: \"\n\t\t\t\t\t\t\t\t\"all arguments of an OR expression must be \"\n\t\t\t\t\t\t\t\t\"pushdownable but one of them was not, due \"\n\t\t\t\t\t\t\t\t\"to the reason given above\")));\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\t/* simply skip AND args that we cannot pushdown */\n\t\t}\n\n\t\tint npushdownableArgs = list_length(pushdownableArgs);\n\t\tif (npushdownableArgs == 0)\n\t\t{\n\t\t\tereport(ColumnarPlannerDebugLevel,\n\t\t\t\t\t(errmsg(\"columnar planner: cannot push down clause: \"\n\t\t\t\t\t\t\t\"none of the arguments were pushdownable, \"\n\t\t\t\t\t\t\t\"due to the reason(s) given above \")));\n\t\t\treturn NULL;\n\t\t}\n\t\telse if (npushdownableArgs == 1)\n\t\t{\n\t\t\treturn (Expr *) linitial(pushdownableArgs);\n\t\t}\n\n\t\tif (boolExpr->boolop == AND_EXPR)\n\t\t{\n\t\t\treturn make_andclause(pushdownableArgs);\n\t\t}\n\t\telse if (boolExpr->boolop == OR_EXPR)\n\t\t{\n\t\t\treturn make_orclause(pushdownableArgs);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* already discarded NOT expr, so should not be reachable */\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tif (IsA(node, ScalarArrayOpExpr))\n\t{\n\t\tif (!contain_volatile_functions(node))\n\t\t{\n\t\t\treturn (Expr *) node;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tif (!IsA(node, OpExpr) || list_length(((OpExpr *) node)->args) != 2)\n\t{\n\t\tereport(ColumnarPlannerDebugLevel,\n\t\t\t\t(errmsg(\"columnar planner: cannot push down clause: \"\n\t\t\t\t\t\t\"must be binary operator expression\")));\n\t\treturn NULL;\n\t}\n\n\tOpExpr *opExpr = castNode(OpExpr, node);\n\tExpr *lhs = list_nth(opExpr->args, 0);\n\tExpr *rhs = list_nth(opExpr->args, 1);\n\n\tVar *varSide;\n\tExpr *exprSide;\n\n\tif (IsA(lhs, Var) && ((Var *) lhs)->varno == rel->relid &&\n\t\t!ExprReferencesRelid((Expr *) rhs, rel->relid))\n\t{\n\t\tvarSide = castNode(Var, lhs);\n\t\texprSide = rhs;\n\t}\n\telse if (IsA(rhs, Var) && ((Var *) rhs)->varno == rel->relid &&\n\t\t\t !ExprReferencesRelid((Expr *) lhs, rel->relid))\n\t{\n\t\tvarSide = castNode(Var, rhs);\n\t\texprSide = lhs;\n\t}\n\telse\n\t{\n\t\tereport(ColumnarPlannerDebugLevel,\n\t\t\t\t(errmsg(\"columnar planner: cannot push down clause: \"\n\t\t\t\t\t\t\"must match 'Var <op> Expr' or 'Expr <op> Var'\"),\n\t\t\t\t errhint(\"Var must only reference this rel, \"\n\t\t\t\t\t\t \"and Expr must not reference this rel\")));\n\t\treturn NULL;\n\t}\n\n\tif (varSide->varattno <= 0)\n\t{\n\t\tereport(ColumnarPlannerDebugLevel,\n\t\t\t\t(errmsg(\"columnar planner: cannot push down clause: \"\n\t\t\t\t\t\t\"var is whole-row reference or system column\")));\n\t\treturn NULL;\n\t}\n\n\tif (contain_volatile_functions((Node *) exprSide))\n\t{\n\t\tereport(ColumnarPlannerDebugLevel,\n\t\t\t\t(errmsg(\"columnar planner: cannot push down clause: \"\n\t\t\t\t\t\t\"expr contains volatile functions\")));\n\t\treturn NULL;\n\t}\n\n\t/* only the default opclass is used for qual pushdown. */\n\tOid varOpClass = GetDefaultOpClass(varSide->vartype, BTREE_AM_OID);\n\tOid varOpFamily;\n\tOid varOpcInType;\n\n\tif (!OidIsValid(varOpClass) ||\n\t\t!get_opclass_opfamily_and_input_type(varOpClass, &varOpFamily,\n\t\t\t\t\t\t\t\t\t\t\t &varOpcInType))\n\t{\n\t\tereport(ColumnarPlannerDebugLevel,\n\t\t\t\t(errmsg(\"columnar planner: cannot push down clause: \"\n\t\t\t\t\t\t\"cannot find default btree opclass and opfamily for type: %s\",\n\t\t\t\t\t\tformat_type_be(varSide->vartype))));\n\t\treturn NULL;\n\t}\n\n\tif (!op_in_opfamily(opExpr->opno, varOpFamily))\n\t{\n\t\tereport(ColumnarPlannerDebugLevel,\n\t\t\t\t(errmsg(\"columnar planner: cannot push down clause: \"\n\t\t\t\t\t\t\"operator %d not a member of opfamily %d\",\n\t\t\t\t\t\topExpr->opno, varOpFamily)));\n\t\treturn NULL;\n\t}\n\n\tOid sortop = get_opfamily_member(varOpFamily, varOpcInType,\n\t\t\t\t\t\t\t\t\t varOpcInType, BTLessStrategyNumber);\n\tAssert(OidIsValid(sortop));\n\n\t/*\n\t * Check that statistics on the Var support the utility of this\n\t * clause.\n\t */\n\tfloat4 absVarCorrelation = 0;\n\tif (!CheckVarStats(root, varSide, sortop, &absVarCorrelation))\n\t{\n\t\tereport(ColumnarPlannerDebugLevel,\n\t\t\t\t(errmsg(\"columnar planner: cannot push down clause: \"\n\t\t\t\t\t\t\"absolute correlation (%.3f) of var attribute %d is \"\n\t\t\t\t\t\t\"smaller than the value configured in \"\n\t\t\t\t\t\t\"\\\"columnar.qual_pushdown_correlation_threshold\\\" \"\n\t\t\t\t\t\t\"(%.3f)\", absVarCorrelation, varSide->varattno,\n\t\t\t\t\t\tColumnarQualPushdownCorrelationThreshold)));\n\t\treturn NULL;\n\t}\n\n\treturn (Expr *) node;\n}\n\n\n/*\n * FilterPushdownClauses filters for clauses that are candidates for pushing\n * down into rel.\n */\nstatic List *\nFilterPushdownClauses(PlannerInfo *root, RelOptInfo *rel, List *inputClauses)\n{\n\tList *filteredClauses = NIL;\n\tListCell *lc;\n\tforeach(lc, inputClauses)\n\t{\n\t\tRestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);\n\n\t\t/*\n\t\t * Ignore clauses that don't refer to this rel, and pseudoconstants.\n\t\t *\n\t\t * XXX: A pseudoconstant may be of use, but it doesn't make sense to\n\t\t * push it down because it doesn't contain any Vars. Look into if\n\t\t * there's something we should do with pseudoconstants here.\n\t\t */\n\t\tif (rinfo->pseudoconstant ||\n\t\t\t!bms_is_member(rel->relid, rinfo->required_relids))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tExpr *pushdownableExpr = ExtractPushdownClause(root, rel, (Node *) rinfo->clause);\n\t\tif (!pushdownableExpr)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\trinfo = copyObject(rinfo);\n\t\trinfo->clause = pushdownableExpr;\n\t\tfilteredClauses = lappend(filteredClauses, rinfo);\n\t}\n\n\treturn filteredClauses;\n}\n\n\n/*\n * PushdownJoinClauseMatches is a callback that returns true, indicating that\n * we want all of the clauses from generate_implied_equalities_for_column().\n */\nstatic bool\nPushdownJoinClauseMatches(PlannerInfo *root, RelOptInfo *rel,\n\t\t\t\t\t\t  EquivalenceClass *ec, EquivalenceMember *em,\n\t\t\t\t\t\t  void *arg)\n{\n\treturn true;\n}\n\n\n/*\n * FindPushdownJoinClauses finds join clauses, including those implied by ECs,\n * that may be pushed down.\n */\nstatic List *\nFindPushdownJoinClauses(PlannerInfo *root, RelOptInfo *rel)\n{\n\tList *joinClauses = copyObject(rel->joininfo);\n\n\t/*\n\t * Here we are generating the clauses just so we can later extract the\n\t * interesting relids. This is somewhat wasteful, but it allows us to\n\t * filter out joinclauses, reducing the number of relids we need to\n\t * consider.\n\t *\n\t * XXX: also find additional clauses for joininfo that are implied by ECs?\n\t */\n\tList *ecClauses = generate_implied_equalities_for_column(\n\t\troot, rel, PushdownJoinClauseMatches, NULL,\n\t\trel->lateral_referencers);\n\tList *allClauses = list_concat(joinClauses, ecClauses);\n\n\treturn FilterPushdownClauses(root, rel, allClauses);\n}\n\n\n/*\n * FindCandidateRelids identifies candidate rels for parameterization from the\n * list of join clauses.\n *\n * Some rels cannot be considered for parameterization, such as a partitioned\n * parent of the given rel. Other rels are just not useful because they don't\n * appear in a join clause that could be pushed down.\n */\nstatic Relids\nFindCandidateRelids(PlannerInfo *root, RelOptInfo *rel, List *joinClauses)\n{\n\tRelids candidateRelids = NULL;\n\tListCell *lc;\n\tforeach(lc, joinClauses)\n\t{\n\t\tRestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);\n\n\t\tcandidateRelids = bms_add_members(candidateRelids,\n\t\t\t\t\t\t\t\t\t\t  rinfo->required_relids);\n\t}\n\n\tcandidateRelids = bms_del_members(candidateRelids, rel->relids);\n\tcandidateRelids = bms_del_members(candidateRelids, rel->lateral_relids);\n\n\t/*\n\t * For the relevant PG16 commit requiring this addition:\n\t * postgres/postgres@2489d76\n\t */\n\tcandidateRelids = bms_del_members(candidateRelids, root->outer_join_rels);\n\n\treturn candidateRelids;\n}\n\n\n/*\n * Combinations() calculates the number of combinations of n things taken k at\n * a time. When the correct result is large, the calculation may produce a\n * non-integer result, or overflow to inf, which caller should handle\n * appropriately.\n *\n * Use the following two formulae from Knuth TAoCP, 1.2.6:\n *    (2) Combinations(n, k) = (n*(n-1)..(n-k+1)) / (k*(k-1)..1)\n *    (5) Combinations(n, k) = Combinations(n, n-k)\n */\nstatic double\nCombinations(int n, int k)\n{\n\tdouble v = 1;\n\n\t/*\n\t * If k is close to n, then both the numerator and the denominator are\n\t * close to n!, and we may overflow even if the input is reasonable\n\t * (e.g. Combinations(500, 500)). Use formula (5) to choose the smaller,\n\t * but equivalent, k.\n\t */\n\tk = Min(k, n - k);\n\n\t/* calculate numerator of formula (2) first */\n\tfor (int i = n; i >= n - k + 1; i--)\n\t{\n\t\tv *= i;\n\t}\n\n\t/*\n\t * Divide by each factor in the denominator of formula (2), skipping\n\t * division by 1.\n\t */\n\tfor (int i = k; i >= 2; i--)\n\t{\n\t\tv /= i;\n\t}\n\n\treturn v;\n}\n\n\n/*\n * ChooseDepthLimit() calculates the depth limit for the parameterization\n * search, given the number of candidate relations.\n *\n * The maximum number of paths generated for a given depthLimit is:\n *\n *    Combinations(nCandidates, 0) + Combinations(nCandidates, 1) + ... +\n *    Combinations(nCandidates, depthLimit)\n *\n * There's no closed formula for a partial sum of combinations, so just keep\n * increasing the depth until the number of combinations exceeds the limit.\n */\nstatic int\nChooseDepthLimit(int nCandidates)\n{\n\tif (!EnableColumnarQualPushdown)\n\t{\n\t\treturn 0;\n\t}\n\n\tint depth = 0;\n\tdouble numPaths = 1;\n\n\twhile (depth < nCandidates)\n\t{\n\t\tnumPaths += Combinations(nCandidates, depth + 1);\n\n\t\tif (numPaths > (double) ColumnarMaxCustomScanPaths)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tdepth++;\n\t}\n\n\treturn depth;\n}\n\n\n/*\n * AddColumnarScanPaths is the entry point for recursively generating\n * parameterized paths. See AddColumnarScanPathsRec() for discussion.\n */\nstatic void\nAddColumnarScanPaths(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)\n{\n\tList *joinClauses = FindPushdownJoinClauses(root, rel);\n\tRelids candidateRelids = FindCandidateRelids(root, rel, joinClauses);\n\n\tint depthLimit = ChooseDepthLimit(bms_num_members(candidateRelids));\n\n\t/* must always parameterize by lateral refs */\n\tRelids paramRelids = bms_copy(rel->lateral_relids);\n\n\tAddColumnarScanPathsRec(root, rel, rte, paramRelids, candidateRelids,\n\t\t\t\t\t\t\tdepthLimit);\n}\n\n\n/*\n * AddColumnarScanPathsRec is a recursive function to search the\n * parameterization space and add CustomPaths for columnar scans.\n *\n * The set paramRelids is the parameterization at the current level, and\n * candidateRelids is the set from which we draw to generate paths with\n * greater parameterization.\n *\n * Columnar tables resemble indexes because of the ability to push down\n * quals. Ordinary quals, such as x = 7, can be pushed down easily. But join\n * quals of the form \"x = y\" (where \"y\" comes from another rel) require the\n * proper parameterization.\n *\n * Paths that require more outer rels can push down more join clauses that\n * depend on those outer rels. But requiring more outer rels gives the planner\n * fewer options for the shape of the plan. That means there is a trade-off,\n * and we should generate plans of various parameterizations, then let the\n * planner choose. We always need to generate one minimally-parameterized path\n * (parameterized only by lateral refs, if present) to make sure that at least\n * one path can be chosen. Then, we generate as many parameterized paths as we\n * reasonably can.\n *\n * The set of all possible parameterizations is the power set of\n * candidateRelids. The power set has cardinality 2^N, where N is the\n * cardinality of candidateRelids. To avoid creating a huge number of paths,\n * limit the depth of the search; the depthLimit is equivalent to the maximum\n * number of required outer rels (beyond the minimal parameterization) for the\n * path. A depthLimit of zero means that only the minimally-parameterized path\n * will be generated.\n */\nstatic void\nAddColumnarScanPathsRec(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte,\n\t\t\t\t\t\tRelids paramRelids, Relids candidateRelids,\n\t\t\t\t\t\tint depthLimit)\n{\n\tCHECK_FOR_INTERRUPTS();\n\tcheck_stack_depth();\n\n\tAssert(!bms_overlap(paramRelids, candidateRelids));\n\tAddColumnarScanPath(root, rel, rte, paramRelids);\n\n\t/* recurse for all candidateRelids, unless we hit the depth limit */\n\tAssert(depthLimit >= 0);\n\tif (depthLimit-- == 0)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * Iterate through parameter combinations depth-first. Deeper levels\n\t * generate paths of greater parameterization (and hopefully lower\n\t * cost).\n\t */\n\tRelids tmpCandidateRelids = bms_copy(candidateRelids);\n\tint relid = -1;\n\twhile ((relid = bms_next_member(candidateRelids, relid)) >= 0)\n\t{\n\t\tRelids tmpParamRelids = bms_add_member(\n\t\t\tbms_copy(paramRelids), relid);\n\n\t\t/*\n\t\t * Because we are generating combinations (not permutations), remove\n\t\t * the relid from the set of candidates at this level as we descend to\n\t\t * the next.\n\t\t */\n\t\ttmpCandidateRelids = bms_del_member(tmpCandidateRelids, relid);\n\n\t\tAddColumnarScanPathsRec(root, rel, rte, tmpParamRelids,\n\t\t\t\t\t\t\t\ttmpCandidateRelids, depthLimit);\n\t}\n\n\tbms_free(tmpCandidateRelids);\n}\n\n\n/*\n * ParameterizationAsString returns the string representation of the set of\n * rels given in paramRelids.\n *\n * Takes a StringInfo so that it doesn't return palloc'd memory. This makes it\n * easy to call this function as an argument to ereport(), such that it won't\n * be evaluated unless the message is going to be output somewhere.\n */\nstatic char *\nParameterizationAsString(PlannerInfo *root, Relids paramRelids, StringInfo buf)\n{\n\tbool firstTime = true;\n\tint relid = -1;\n\n\tif (bms_num_members(paramRelids) == 0)\n\t{\n\t\treturn \"unparameterized\";\n\t}\n\n\tappendStringInfoString(buf, \"parameterized by rels {\");\n\twhile ((relid = bms_next_member(paramRelids, relid)) >= 0)\n\t{\n\t\tRangeTblEntry *rte = root->simple_rte_array[relid];\n\t\tconst char *relname = quote_identifier(rte->eref->aliasname);\n\n\t\tappendStringInfo(buf, \"%s%s\", firstTime ? \"\" : \", \", relname);\n\n\t\tif (relname != rte->eref->aliasname)\n\t\t{\n\t\t\tpfree((void *) relname);\n\t\t}\n\n\t\tfirstTime = false;\n\t}\n\tappendStringInfoString(buf, \"}\");\n\treturn buf->data;\n}\n\n\n/*\n * ContainsExecParams tests whether the node contains any exec params. The\n * signature accepts an extra argument for use with expression_tree_walker.\n */\nstatic bool\nContainsExecParams(Node *node, void *notUsed)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\telse if (IsA(node, Param))\n\t{\n\t\tParam *param = castNode(Param, node);\n\t\tif (param->paramkind == PARAM_EXEC)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn expression_tree_walker(node, ContainsExecParams, NULL);\n}\n\n\n/*\n * Create and add a path with the given parameterization paramRelids.\n *\n * XXX: Consider refactoring to be more like postgresGetForeignPaths(). The\n * only differences are param_info and custom_private.\n */\nstatic void\nAddColumnarScanPath(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte,\n\t\t\t\t\tRelids paramRelids)\n{\n\t/*\n\t * Must return a CustomPath, not a larger structure containing a\n\t * CustomPath as the first field. Otherwise, nodeToString() will fail to\n\t * output the additional fields.\n\t */\n\tCustomPath *cpath = makeNode(CustomPath);\n\n\tcpath->methods = &ColumnarScanPathMethods;\n\n\t/* necessary to avoid extra Result node in PG15 */\n\tcpath->flags = CUSTOMPATH_SUPPORT_PROJECTION;\n\n\t/*\n\t * populate generic path information\n\t */\n\tPath *path = &cpath->path;\n\tpath->pathtype = T_CustomScan;\n\tpath->parent = rel;\n\tpath->pathtarget = rel->reltarget;\n\n\t/* columnar scans are not parallel-aware, but they are parallel-safe */\n\tpath->parallel_safe = rel->consider_parallel;\n\n\tpath->param_info = get_baserel_parampathinfo(root, rel, paramRelids);\n\n\t/*\n\t * Usable clauses for this parameterization exist in baserestrictinfo and\n\t * ppi_clauses.\n\t */\n\tList *allClauses = copyObject(rel->baserestrictinfo);\n\tif (path->param_info != NULL)\n\t{\n\t\tallClauses = list_concat(allClauses, path->param_info->ppi_clauses);\n\t}\n\n\tallClauses = FilterPushdownClauses(root, rel, allClauses);\n\n\t/*\n\t * Plain clauses may contain extern params, but not exec params, and can\n\t * be evaluated at init time or rescan time. Track them in another list\n\t * that is a subset of allClauses.\n\t *\n\t * Note: although typically baserestrictinfo contains plain clauses,\n\t * that's not always true. It can also contain a qual referencing a Var at\n\t * a higher query level, which can be turned into an exec param, and\n\t * therefore it won't be a plain clause.\n\t */\n\tList *plainClauses = NIL;\n\tListCell *lc;\n\tforeach(lc, allClauses)\n\t{\n\t\tRestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);\n\t\tif (bms_is_subset(rinfo->required_relids, rel->relids) &&\n\t\t\t!ContainsExecParams((Node *) rinfo->clause, NULL))\n\t\t{\n\t\t\tplainClauses = lappend(plainClauses, rinfo);\n\t\t}\n\t}\n\n\t/*\n\t * We can't make our own CustomPath structure, so we need to put\n\t * everything in the custom_private list. To keep the two lists separate,\n\t * we make them sublists in a 2-element list.\n\t */\n\tif (EnableColumnarQualPushdown)\n\t{\n\t\tcpath->custom_private = list_make2(copyObject(plainClauses),\n\t\t\t\t\t\t\t\t\t\t   copyObject(allClauses));\n\t}\n\telse\n\t{\n\t\tcpath->custom_private = list_make2(NIL, NIL);\n\t}\n\n\tint numberOfColumnsRead = 0;\n\tif (rte->perminfoindex > 0)\n\t{\n\t\t/*\n\t\t * If perminfoindex > 0, that means that this relation's permission info\n\t\t * is directly found in the list of rteperminfos of the Query(root->parse)\n\t\t * So, all we have to do here is retrieve that info.\n\t\t */\n\t\tRTEPermissionInfo *perminfo = getRTEPermissionInfo(root->parse->rteperminfos,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   rte);\n\t\tnumberOfColumnsRead = bms_num_members(perminfo->selectedCols);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * If perminfoindex = 0, that means we are skipping the check for permission info\n\t\t * for this relation, which means that it's either a partition or an inheritance child.\n\t\t * In these cases, we need to access the permission info of the top parent of this relation.\n\t\t * After thorough checking, we found that the index of the top parent pointing to the correct\n\t\t * range table entry in Query's range tables (root->parse->rtable) is found under\n\t\t * RelOptInfo rel->top_parent->relid.\n\t\t * For reference, check expand_partitioned_rtentry and expand_inherited_rtentry PG functions\n\t\t */\n\t\tAssert(rel->top_parent);\n\t\tRangeTblEntry *parent_rte = rt_fetch(rel->top_parent->relid, root->parse->rtable);\n\t\tRTEPermissionInfo *perminfo = getRTEPermissionInfo(root->parse->rteperminfos,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   parent_rte);\n\t\tnumberOfColumnsRead = bms_num_members(fixup_inherited_columns(perminfo->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  perminfo->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  selectedCols));\n\t}\n\n\tint numberOfClausesPushed = list_length(allClauses);\n\n\tCostColumnarScan(root, rel, rte->relid, cpath, numberOfColumnsRead,\n\t\t\t\t\t numberOfClausesPushed);\n\n\n\tStringInfoData buf;\n\tinitStringInfo(&buf);\n\tereport(ColumnarPlannerDebugLevel,\n\t\t\t(errmsg(\"columnar planner: adding CustomScan path for %s\",\n\t\t\t\t\trte->eref->aliasname),\n\t\t\t errdetail(\"%s; %d clauses pushed down\",\n\t\t\t\t\t   ParameterizationAsString(root, paramRelids, &buf),\n\t\t\t\t\t   numberOfClausesPushed)));\n\n\tadd_path(rel, path);\n}\n\n\n/*\n * fixup_inherited_columns\n *\n * Exact function Copied from PG16 as it's static.\n *\n * When user is querying on a table with children, it implicitly accesses\n * child tables also. So, we also need to check security label of child\n * tables and columns, but there is no guarantee attribute numbers are\n * same between the parent and children.\n * It returns a bitmapset which contains attribute number of the child\n * table based on the given bitmapset of the parent.\n */\nstatic Bitmapset *\nfixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)\n{\n\tBitmapset *result = NULL;\n\n\t/*\n\t * obviously, no need to do anything here\n\t */\n\tif (parentId == childId)\n\t{\n\t\treturn columns;\n\t}\n\n\tint index = -1;\n\twhile ((index = bms_next_member(columns, index)) >= 0)\n\t{\n\t\t/* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */\n\t\tAttrNumber attno = index + FirstLowInvalidHeapAttributeNumber;\n\n\t\t/*\n\t\t * whole-row-reference shall be fixed-up later\n\t\t */\n\t\tif (attno == InvalidAttrNumber)\n\t\t{\n\t\t\tresult = bms_add_member(result, index);\n\t\t\tcontinue;\n\t\t}\n\n\t\tchar *attname = get_attname(parentId, attno, false);\n\t\tattno = get_attnum(childId, attname);\n\t\tif (attno == InvalidAttrNumber)\n\t\t{\n\t\t\telog(ERROR, \"cache lookup failed for attribute %s of relation %u\",\n\t\t\t\t attname, childId);\n\t\t}\n\n\t\tresult = bms_add_member(result,\n\t\t\t\t\t\t\t\tattno - FirstLowInvalidHeapAttributeNumber);\n\n\t\tpfree(attname);\n\t}\n\n\treturn result;\n}\n\n\n/*\n * CostColumnarScan calculates the cost of scanning the columnar table. The\n * cost is estimated by using all stripe metadata to estimate based on the\n * columns to read how many pages need to be read.\n */\nstatic void\nCostColumnarScan(PlannerInfo *root, RelOptInfo *rel, Oid relationId,\n\t\t\t\t CustomPath *cpath, int numberOfColumnsRead, int nClauses)\n{\n\tPath *path = &cpath->path;\n\n\tList *allClauses = lsecond(cpath->custom_private);\n\tSelectivity clauseSel = clauselist_selectivity(\n\t\troot, allClauses, rel->relid, JOIN_INNER, NULL);\n\n\t/*\n\t * We already filtered out clauses where the overall selectivity would be\n\t * misleading, such as inequalities involving an uncorrelated column. So\n\t * we can apply the selectivity directly to the number of stripes.\n\t */\n\tdouble stripesToRead = clauseSel * ColumnarTableStripeCount(relationId);\n\tstripesToRead = Max(stripesToRead, 1.0);\n\n\tpath->rows = rel->rows;\n\tpath->startup_cost = 0;\n\tpath->total_cost = stripesToRead *\n\t\t\t\t\t   ColumnarPerStripeScanCost(rel, relationId, numberOfColumnsRead);\n}\n\n\n/*\n * ColumnarPerStripeScanCost calculates the cost to scan a single stripe\n * of given columnar table based on number of columns that needs to be\n * read during scan operation.\n */\nstatic Cost\nColumnarPerStripeScanCost(RelOptInfo *rel, Oid relationId, int numberOfColumnsRead)\n{\n\tRelation relation = RelationIdGetRelation(relationId);\n\tif (!RelationIsValid(relation))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not open relation with OID %u\", relationId)));\n\t}\n\n\tList *stripeList = StripesForRelfilelocator(relation);\n\tRelationClose(relation);\n\n\tuint32 maxColumnCount = 0;\n\tuint64 totalStripeSize = 0;\n\tStripeMetadata *stripeMetadata = NULL;\n\tforeach_declared_ptr(stripeMetadata, stripeList)\n\t{\n\t\ttotalStripeSize += stripeMetadata->dataLength;\n\t\tmaxColumnCount = Max(maxColumnCount, stripeMetadata->columnCount);\n\t}\n\n\t/*\n\t * When no stripes are in the table we don't have a count in maxColumnCount. To\n\t * prevent a division by zero turning into a NaN we keep the ratio on zero.\n\t * This will result in a cost of 0 for scanning the table which is a reasonable\n\t * cost on an empty table.\n\t */\n\tif (maxColumnCount == 0)\n\t{\n\t\treturn 0;\n\t}\n\n\tdouble columnSelectionRatio = numberOfColumnsRead / (double) maxColumnCount;\n\tCost tableScanCost = (double) totalStripeSize / BLCKSZ * columnSelectionRatio;\n\tCost perStripeScanCost = tableScanCost / list_length(stripeList);\n\n\t/*\n\t * Finally, multiply the cost of reading a single stripe by seq page read\n\t * cost to make our estimation scale compatible with postgres.\n\t * Since we are calculating the cost for a single stripe here, we use seq\n\t * page cost instead of random page cost. This is because, random page\n\t * access only happens when switching between columns, which is pretty\n\t * much neglactable.\n\t */\n\tdouble relSpaceSeqPageCost;\n\tget_tablespace_page_costs(rel->reltablespace,\n\t\t\t\t\t\t\t  NULL, &relSpaceSeqPageCost);\n\tperStripeScanCost = perStripeScanCost * relSpaceSeqPageCost;\n\n\treturn perStripeScanCost;\n}\n\n\n/*\n * ColumnarTableStripeCount returns the number of stripes that columnar\n * table with relationId has by using stripe metadata.\n */\nstatic uint64\nColumnarTableStripeCount(Oid relationId)\n{\n\tRelation relation = RelationIdGetRelation(relationId);\n\tif (!RelationIsValid(relation))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not open relation with OID %u\", relationId)));\n\t}\n\n\tList *stripeList = StripesForRelfilelocator(relation);\n\tint stripeCount = list_length(stripeList);\n\tRelationClose(relation);\n\n\treturn stripeCount;\n}\n\n\nstatic Plan *\nColumnarScanPath_PlanCustomPath(PlannerInfo *root,\n\t\t\t\t\t\t\t\tRelOptInfo *rel,\n\t\t\t\t\t\t\t\tstruct CustomPath *best_path,\n\t\t\t\t\t\t\t\tList *tlist,\n\t\t\t\t\t\t\t\tList *clauses,\n\t\t\t\t\t\t\t\tList *custom_plans)\n{\n\t/*\n\t * Must return a CustomScan, not a larger structure containing a\n\t * CustomScan as the first field. Otherwise, copyObject() will fail to\n\t * copy the additional fields.\n\t */\n\tCustomScan *cscan = makeNode(CustomScan);\n\n\tcscan->methods = &ColumnarScanScanMethods;\n\n\t/* XXX: also need to store projected column list for EXPLAIN */\n\n\tif (EnableColumnarQualPushdown)\n\t{\n\t\t/*\n\t\t * Lists of pushed-down clauses. The Vars in custom_exprs referencing\n\t\t * other relations will be changed into exec Params by\n\t\t * create_customscan_plan().\n\t\t *\n\t\t * Like CustomPath->custom_private, keep a list of plain clauses\n\t\t * separate from the list of all clauses by making them sublists of a\n\t\t * 2-element list.\n\t\t *\n\t\t * XXX: custom_exprs are the quals that will be pushed into the\n\t\t * columnar reader code; some of these may not be usable. We should\n\t\t * fix this by processing the quals more completely and using\n\t\t * ScanKeys.\n\t\t */\n\t\tList *plainClauses = extract_actual_clauses(\n\t\t\tlinitial(best_path->custom_private), false /* no pseudoconstants */);\n\t\tList *allClauses = extract_actual_clauses(\n\t\t\tlsecond(best_path->custom_private), false /* no pseudoconstants */);\n\t\tcscan->custom_exprs = copyObject(list_make2(plainClauses, allClauses));\n\t}\n\telse\n\t{\n\t\tcscan->custom_exprs = list_make2(NIL, NIL);\n\t}\n\n\tcscan->scan.plan.qual = extract_actual_clauses(\n\t\tclauses, false /* no pseudoconstants */);\n\tcscan->scan.plan.targetlist = list_copy(tlist);\n\tcscan->scan.scanrelid = best_path->path.parent->relid;\n\n#if (PG_VERSION_NUM >= 150000)\n\n\t/* necessary to avoid extra Result node in PG15 */\n\tcscan->flags = CUSTOMPATH_SUPPORT_PROJECTION;\n#endif\n\n\treturn (Plan *) cscan;\n}\n\n\n/*\n * ReparameterizeMutator changes all varnos referencing the topmost parent of\n * child_rel to instead reference child_rel directly.\n */\nstatic Node *\nReparameterizeMutator(Node *node, RelOptInfo *child_rel)\n{\n\tif (node == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\tif (IsA(node, Var))\n\t{\n\t\tVar *var = castNode(Var, node);\n\t\tif (bms_is_member(var->varno, child_rel->top_parent_relids))\n\t\t{\n\t\t\tvar = copyObject(var);\n\t\t\tvar->varno = child_rel->relid;\n\t\t}\n\t\treturn (Node *) var;\n\t}\n\n\tif (IsA(node, RestrictInfo))\n\t{\n\t\tRestrictInfo *rinfo = castNode(RestrictInfo, node);\n\t\trinfo = copyObject(rinfo);\n\t\trinfo->clause = (Expr *) expression_tree_mutator(\n\t\t\t(Node *) rinfo->clause, ReparameterizeMutator, (void *) child_rel);\n\t\treturn (Node *) rinfo;\n\t}\n\treturn expression_tree_mutator(node, ReparameterizeMutator,\n\t\t\t\t\t\t\t\t   (void *) child_rel);\n}\n\n\n/*\n * ColumnarScanPath_ReparameterizeCustomPathByChild is a method called when a\n * path is reparameterized directly to a child relation, rather than the\n * top-level parent.\n *\n * For instance, let there be a join of two partitioned columnar relations PX\n * and PY. A path for a ColumnarScan of PY3 might be parameterized by PX so\n * that the join qual \"PY3.a = PX.a\" (referencing the parent PX) can be pushed\n * down. But if the planner decides on a partition-wise join, then the path\n * will be reparameterized on the child table PX3 directly.\n *\n * When that happens, we need to update all Vars in the pushed-down quals to\n * reference PX3, not PX, to match the new parameterization. This method\n * notifies us that it needs to be done, and allows us to update the\n * information in custom_private.\n */\nstatic List *\nColumnarScanPath_ReparameterizeCustomPathByChild(PlannerInfo *root,\n\t\t\t\t\t\t\t\t\t\t\t\t List *custom_private,\n\t\t\t\t\t\t\t\t\t\t\t\t RelOptInfo *child_rel)\n{\n\treturn (List *) ReparameterizeMutator((Node *) custom_private, child_rel);\n}\n\n\nstatic Node *\nColumnarScan_CreateCustomScanState(CustomScan *cscan)\n{\n\tColumnarScanState *columnarScanState = (ColumnarScanState *) newNode(\n\t\tsizeof(ColumnarScanState), T_CustomScanState);\n\n\tCustomScanState *cscanstate = &columnarScanState->custom_scanstate;\n\tcscanstate->methods = &ColumnarScanExecuteMethods;\n\n\treturn (Node *) cscanstate;\n}\n\n\n/*\n * EvalParamsMutator evaluates Params in the expression and replaces them with\n * Consts.\n */\nstatic Node *\nEvalParamsMutator(Node *node, ExprContext *econtext)\n{\n\tif (node == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (IsA(node, Param))\n\t{\n\t\tParam *param = (Param *) node;\n\t\tint16 typLen;\n\t\tbool typByVal;\n\t\tbool isnull;\n\n\t\tget_typlenbyval(param->paramtype, &typLen, &typByVal);\n\n\t\t/* XXX: should save ExprState for efficiency */\n\t\tExprState *exprState = ExecInitExprWithParams((Expr *) node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  econtext->ecxt_param_list_info);\n\t\tDatum pval = ExecEvalExpr(exprState, econtext, &isnull);\n\n\t\treturn (Node *) makeConst(param->paramtype,\n\t\t\t\t\t\t\t\t  param->paramtypmod,\n\t\t\t\t\t\t\t\t  param->paramcollid,\n\t\t\t\t\t\t\t\t  (int) typLen,\n\t\t\t\t\t\t\t\t  pval,\n\t\t\t\t\t\t\t\t  isnull,\n\t\t\t\t\t\t\t\t  typByVal);\n\t}\n\n\treturn expression_tree_mutator(node, EvalParamsMutator, (void *) econtext);\n}\n\n\nstatic void\nColumnarScan_BeginCustomScan(CustomScanState *cscanstate, EState *estate, int eflags)\n{\n\tCustomScan *cscan = (CustomScan *) cscanstate->ss.ps.plan;\n\tColumnarScanState *columnarScanState = (ColumnarScanState *) cscanstate;\n\tExprContext *stdecontext = cscanstate->ss.ps.ps_ExprContext;\n\n\t/*\n\t * Make a new ExprContext just like the existing one, except that we don't\n\t * reset it every tuple.\n\t */\n\tExecAssignExprContext(estate, &cscanstate->ss.ps);\n\tcolumnarScanState->css_RuntimeContext = cscanstate->ss.ps.ps_ExprContext;\n\tcscanstate->ss.ps.ps_ExprContext = stdecontext;\n\n\tResetExprContext(columnarScanState->css_RuntimeContext);\n\tList *plainClauses = linitial(cscan->custom_exprs);\n\tcolumnarScanState->qual = (List *) EvalParamsMutator(\n\t\t(Node *) plainClauses, columnarScanState->css_RuntimeContext);\n\n\t/* scan slot is already initialized */\n}\n\n\n/*\n * ColumnarAttrNeeded returns a list of AttrNumber's for the ones that are\n * needed during columnar custom scan.\n * Throws an error if finds a Var referencing to an attribute not supported\n * by ColumnarScan.\n */\nstatic Bitmapset *\nColumnarAttrNeeded(ScanState *ss)\n{\n\tTupleTableSlot *slot = ss->ss_ScanTupleSlot;\n\tint natts = slot->tts_tupleDescriptor->natts;\n\tBitmapset *attr_needed = NULL;\n\tPlan *plan = ss->ps.plan;\n\tint flags = PVC_RECURSE_AGGREGATES |\n\t\t\t\tPVC_RECURSE_WINDOWFUNCS | PVC_RECURSE_PLACEHOLDERS;\n\tList *vars = list_concat(pull_var_clause((Node *) plan->targetlist, flags),\n\t\t\t\t\t\t\t pull_var_clause((Node *) plan->qual, flags));\n\tListCell *lc;\n\n\tforeach(lc, vars)\n\t{\n\t\tVar *var = lfirst(lc);\n\n\t\tif (var->varattno < 0)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\"UPDATE and CTID scans not supported for ColumnarScan\")));\n\t\t}\n\n\t\tif (var->varattno == 0)\n\t\t{\n\t\t\telog(DEBUG1, \"Need attribute: all\");\n\n\t\t\t/* all attributes are required, we don't need to add more so break*/\n\t\t\tattr_needed = bms_add_range(attr_needed, 0, natts - 1);\n\t\t\tbreak;\n\t\t}\n\n\t\telog(DEBUG1, \"Need attribute: %d\", var->varattno);\n\t\tattr_needed = bms_add_member(attr_needed, var->varattno - 1);\n\t}\n\n\treturn attr_needed;\n}\n\n\nstatic TupleTableSlot *\nColumnarScanNext(ColumnarScanState *columnarScanState)\n{\n\tCustomScanState *node = (CustomScanState *) columnarScanState;\n\n\t/*\n\t * get information from the estate and scan state\n\t */\n\tTableScanDesc scandesc = node->ss.ss_currentScanDesc;\n\tEState *estate = node->ss.ps.state;\n\tScanDirection direction = estate->es_direction;\n\tTupleTableSlot *slot = node->ss.ss_ScanTupleSlot;\n\n\tif (scandesc == NULL)\n\t{\n\t\t/* the columnar access method does not use the flags, they are specific to heap */\n\t\tuint32 flags = 0;\n\t\tBitmapset *attr_needed = ColumnarAttrNeeded(&node->ss);\n\n\t\t/*\n\t\t * We reach here if the scan is not parallel, or if we're serially\n\t\t * executing a scan that was planned to be parallel.\n\t\t */\n\t\tscandesc = columnar_beginscan_extended(node->ss.ss_currentRelation,\n\t\t\t\t\t\t\t\t\t\t\t   estate->es_snapshot,\n\t\t\t\t\t\t\t\t\t\t\t   0, NULL, NULL, flags, attr_needed,\n\t\t\t\t\t\t\t\t\t\t\t   columnarScanState->qual);\n\t\tbms_free(attr_needed);\n\n\t\tnode->ss.ss_currentScanDesc = scandesc;\n\t}\n\n\t/*\n\t * get the next tuple from the table\n\t */\n\tif (table_scan_getnextslot(scandesc, direction, slot))\n\t{\n\t\treturn slot;\n\t}\n\treturn NULL;\n}\n\n\n/*\n * SeqRecheck -- access method routine to recheck a tuple in EvalPlanQual\n */\nstatic bool\nColumnarScanRecheck(ColumnarScanState *node, TupleTableSlot *slot)\n{\n\treturn true;\n}\n\n\nstatic TupleTableSlot *\nColumnarScan_ExecCustomScan(CustomScanState *node)\n{\n\treturn ExecScan(&node->ss,\n\t\t\t\t\t(ExecScanAccessMtd) ColumnarScanNext,\n\t\t\t\t\t(ExecScanRecheckMtd) ColumnarScanRecheck);\n}\n\n\nstatic void\nColumnarScan_EndCustomScan(CustomScanState *node)\n{\n\t/*\n\t * get information from node\n\t */\n\tTableScanDesc scanDesc = node->ss.ss_currentScanDesc;\n\n\t/*\n\t * clean out the tuple table\n\t */\n\tif (node->ss.ps.ps_ResultTupleSlot)\n\t{\n\t\tExecClearTuple(node->ss.ps.ps_ResultTupleSlot);\n\t}\n\tExecClearTuple(node->ss.ss_ScanTupleSlot);\n\n\t/*\n\t * close heap scan\n\t */\n\tif (scanDesc != NULL)\n\t{\n\t\ttable_endscan(scanDesc);\n\t}\n}\n\n\nstatic void\nColumnarScan_ReScanCustomScan(CustomScanState *node)\n{\n\tCustomScan *cscan = (CustomScan *) node->ss.ps.plan;\n\tColumnarScanState *columnarScanState = (ColumnarScanState *) node;\n\n\tResetExprContext(columnarScanState->css_RuntimeContext);\n\tList *allClauses = lsecond(cscan->custom_exprs);\n\tcolumnarScanState->qual = (List *) EvalParamsMutator(\n\t\t(Node *) allClauses, columnarScanState->css_RuntimeContext);\n\n\tTableScanDesc scanDesc = node->ss.ss_currentScanDesc;\n\n\tif (scanDesc != NULL)\n\t{\n\t\t/* XXX: hack to pass quals as scan keys */\n\t\tScanKey scanKeys = (ScanKey) columnarScanState->qual;\n\t\ttable_rescan(node->ss.ss_currentScanDesc,\n\t\t\t\t\t scanKeys);\n\t}\n}\n\n\nstatic void\nColumnarScan_ExplainCustomScan(CustomScanState *node, List *ancestors,\n\t\t\t\t\t\t\t   ExplainState *es)\n{\n\tColumnarScanState *columnarScanState = (ColumnarScanState *) node;\n\n\tList *context = set_deparse_context_planstate(\n\t\tes->deparse_cxt, (Node *) &node->ss.ps, ancestors);\n\n\tList *projectedColumns = ColumnarVarNeeded(columnarScanState);\n\tconst char *projectedColumnsStr = ColumnarProjectedColumnsStr(\n\t\tcontext, projectedColumns);\n\tExplainPropertyText(\"Columnar Projected Columns\",\n\t\t\t\t\t\tprojectedColumnsStr, es);\n\n\tCustomScan *cscan = castNode(CustomScan, node->ss.ps.plan);\n\tList *chunkGroupFilter = lsecond(cscan->custom_exprs);\n\tif (chunkGroupFilter != NULL)\n\t{\n\t\tconst char *pushdownClausesStr = ColumnarPushdownClausesStr(\n\t\t\tcontext, chunkGroupFilter);\n\t\tExplainPropertyText(\"Columnar Chunk Group Filters\",\n\t\t\t\t\t\t\tpushdownClausesStr, es);\n\n\t\tColumnarScanDesc columnarScanDesc =\n\t\t\t(ColumnarScanDesc) node->ss.ss_currentScanDesc;\n\t\tif (columnarScanDesc != NULL)\n\t\t{\n\t\t\tExplainPropertyInteger(\n\t\t\t\t\"Columnar Chunk Groups Removed by Filter\",\n\t\t\t\tNULL, ColumnarScanChunkGroupsFiltered(columnarScanDesc), es);\n\t\t}\n\t}\n}\n\n\n/*\n * ColumnarPushdownClausesStr represents the clauses to push down as a string.\n */\nstatic const char *\nColumnarPushdownClausesStr(List *context, List *clauses)\n{\n\tExpr *conjunction;\n\n\tAssert(list_length(clauses) > 0);\n\n\tif (list_length(clauses) == 1)\n\t{\n\t\tconjunction = (Expr *) linitial(clauses);\n\t}\n\telse\n\t{\n\t\tconjunction = make_andclause(clauses);\n\t}\n\n\tbool useTableNamePrefix = false;\n\tbool showImplicitCast = false;\n\treturn deparse_expression((Node *) conjunction, context,\n\t\t\t\t\t\t\t  useTableNamePrefix, showImplicitCast);\n}\n\n\n/*\n * ColumnarProjectedColumnsStr generates projected column string for\n * explain output.\n */\nstatic const char *\nColumnarProjectedColumnsStr(List *context, List *projectedColumns)\n{\n\tif (list_length(projectedColumns) == 0)\n\t{\n\t\treturn \"<columnar optimized out all columns>\";\n\t}\n\n\tbool useTableNamePrefix = false;\n\tbool showImplicitCast = false;\n\treturn deparse_expression((Node *) projectedColumns, context,\n\t\t\t\t\t\t\t  useTableNamePrefix, showImplicitCast);\n}\n\n\n/*\n * ColumnarVarNeeded returns a list of Var objects for the ones that are\n * needed during columnar custom scan.\n * Throws an error if finds a Var referencing to an attribute not supported\n * by ColumnarScan.\n */\nstatic List *\nColumnarVarNeeded(ColumnarScanState *columnarScanState)\n{\n\tScanState *scanState = &columnarScanState->custom_scanstate.ss;\n\n\tList *varList = NIL;\n\n\tBitmapset *neededAttrSet = ColumnarAttrNeeded(scanState);\n\tint bmsMember = -1;\n\twhile ((bmsMember = bms_next_member(neededAttrSet, bmsMember)) >= 0)\n\t{\n\t\tRelation columnarRelation = scanState->ss_currentRelation;\n\n\t\t/* neededAttrSet already represents 0-indexed attribute numbers */\n\t\tForm_pg_attribute columnForm =\n\t\t\tTupleDescAttr(RelationGetDescr(columnarRelation), bmsMember);\n\t\tif (columnForm->attisdropped)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN),\n\t\t\t\t\t\t\terrmsg(\"cannot explain column with attrNum=%d \"\n\t\t\t\t\t\t\t\t   \"of columnar table %s since it is dropped\",\n\t\t\t\t\t\t\t\t   bmsMember + 1,\n\t\t\t\t\t\t\t\t   RelationGetRelationName(columnarRelation))));\n\t\t}\n\t\telse if (columnForm->attnum <= 0)\n\t\t{\n\t\t\t/*\n\t\t\t * ColumnarAttrNeeded should have already thrown an error for\n\t\t\t * system columns. Similarly, it should have already expanded\n\t\t\t * whole-row references to individual attributes.\n\t\t\t */\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot explain column with attrNum=%d \"\n\t\t\t\t\t\t\t\t   \"of columnar table %s since it is either \"\n\t\t\t\t\t\t\t\t   \"a system column or a whole-row \"\n\t\t\t\t\t\t\t\t   \"reference\", columnForm->attnum,\n\t\t\t\t\t\t\t\t   RelationGetRelationName(columnarRelation))));\n\t\t}\n\n\n\t\t/*\n\t\t * varlevelsup is used to figure out the (query) level of the Var\n\t\t * that we are investigating. Since we are dealing with a particular\n\t\t * relation, it is useless here.\n\t\t */\n\t\tIndex varlevelsup = 0;\n\n\t\tCustomScanState *customScanState = (CustomScanState *) columnarScanState;\n\t\tCustomScan *customScan = (CustomScan *) customScanState->ss.ps.plan;\n\t\tIndex scanrelid = customScan->scan.scanrelid;\n\t\tVar *var = makeVar(scanrelid, columnForm->attnum, columnForm->atttypid,\n\t\t\t\t\t\t   columnForm->atttypmod, columnForm->attcollation,\n\t\t\t\t\t\t   varlevelsup);\n\t\tvarList = lappend(varList, var);\n\t}\n\n\treturn varList;\n}\n\n\n/*\n * set_deparse_context_planstate is a compatibility wrapper for versions 13+.\n */\nstatic List *\nset_deparse_context_planstate(List *dpcontext, Node *node, List *ancestors)\n{\n\tPlanState *ps = (PlanState *) node;\n\treturn set_deparse_context_plan(dpcontext, ps->plan, ancestors);\n}\n"
  },
  {
    "path": "src/backend/columnar/columnar_debug.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_debug.c\n *\n * Helper functions to debug column store.\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"access/nbtree.h\"\n#include \"access/table.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_type.h\"\n#include \"storage/fd.h\"\n#include \"storage/smgr.h\"\n#include \"utils/guc.h\"\n#include \"utils/memutils.h\"\n#include \"utils/rel.h\"\n#include \"utils/tuplestore.h\"\n\n#include \"pg_version_compat.h\"\n#include \"pg_version_constants.h\"\n\n#include \"columnar/columnar.h\"\n#include \"columnar/columnar_storage.h\"\n#include \"columnar/columnar_version_compat.h\"\n\nstatic void MemoryContextTotals(MemoryContext context, MemoryContextCounters *counters);\n\nPG_FUNCTION_INFO_V1(columnar_store_memory_stats);\nPG_FUNCTION_INFO_V1(columnar_storage_info);\n\n\n/*\n * columnar_store_memory_stats returns a record of 3 values: size of\n * TopMemoryContext, TopTransactionContext, and Write State context.\n */\nDatum\ncolumnar_store_memory_stats(PG_FUNCTION_ARGS)\n{\n\tconst int resultColumnCount = 3;\n\n\tTupleDesc tupleDescriptor = CreateTemplateTupleDesc(resultColumnCount);\n\n\tTupleDescInitEntry(tupleDescriptor, (AttrNumber) 1, \"TopMemoryContext\",\n\t\t\t\t\t   INT8OID, -1, 0);\n\tTupleDescInitEntry(tupleDescriptor, (AttrNumber) 2, \"TopTransactionContext\",\n\t\t\t\t\t   INT8OID, -1, 0);\n\tTupleDescInitEntry(tupleDescriptor, (AttrNumber) 3, \"WriteStateContext\",\n\t\t\t\t\t   INT8OID, -1, 0);\n\n\ttupleDescriptor = BlessTupleDesc(tupleDescriptor);\n\n\tMemoryContextCounters transactionCounters = { 0 };\n\tMemoryContextCounters topCounters = { 0 };\n\tMemoryContextCounters writeStateCounters = { 0 };\n\tMemoryContextTotals(TopTransactionContext, &transactionCounters);\n\tMemoryContextTotals(TopMemoryContext, &topCounters);\n\tMemoryContextTotals(GetWriteContextForDebug(), &writeStateCounters);\n\n\tbool nulls[3] = { false };\n\tDatum values[3] = {\n\t\tInt64GetDatum(topCounters.totalspace),\n\t\tInt64GetDatum(transactionCounters.totalspace),\n\t\tInt64GetDatum(writeStateCounters.totalspace)\n\t};\n\n\tHeapTuple tuple = heap_form_tuple(tupleDescriptor, values, nulls);\n\n\tPG_RETURN_DATUM(HeapTupleGetDatum(tuple));\n}\n\n\n/*\n * columnar_storage_info - UDF to return internal storage info for a columnar relation.\n *\n * DDL:\n *  CREATE OR REPLACE FUNCTION columnar_storage_info(\n *      rel regclass,\n *      version_major OUT int4,\n *      version_minor OUT int4,\n *      storage_id OUT int8,\n *      reserved_stripe_id OUT int8,\n *      reserved_row_number OUT int8,\n *      reserved_offset OUT int8)\n *    STRICT\n *    LANGUAGE c AS 'MODULE_PATHNAME', 'columnar_storage_info';\n */\nDatum\ncolumnar_storage_info(PG_FUNCTION_ARGS)\n{\n#define STORAGE_INFO_NATTS 6\n\tOid relid = PG_GETARG_OID(0);\n\tTupleDesc tupdesc;\n\n\t/* Build a tuple descriptor for our result type */\n\tif (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)\n\t{\n\t\telog(ERROR, \"return type must be a row type\");\n\t}\n\n\tif (tupdesc->natts != STORAGE_INFO_NATTS)\n\t{\n\t\telog(ERROR, \"return type must have %d columns\", STORAGE_INFO_NATTS);\n\t}\n\n\tRelation rel = table_open(relid, AccessShareLock);\n\tif (!IsColumnarTableAmTable(relid))\n\t{\n\t\tereport(ERROR, (errmsg(\"table \\\"%s\\\" is not a columnar table\",\n\t\t\t\t\t\t\t   RelationGetRelationName(rel))));\n\t}\n\n\tDatum values[STORAGE_INFO_NATTS] = { 0 };\n\tbool nulls[STORAGE_INFO_NATTS] = { 0 };\n\n\t/*\n\t * Pass force = true so that we can inspect metapages that are not the\n\t * current version.\n\t *\n\t * NB: ensure the order and number of attributes correspond to DDL\n\t * declaration.\n\t */\n\tvalues[0] = Int32GetDatum(ColumnarStorageGetVersionMajor(rel, true));\n\tvalues[1] = Int32GetDatum(ColumnarStorageGetVersionMinor(rel, true));\n\tvalues[2] = Int64GetDatum(ColumnarStorageGetStorageId(rel, true));\n\tvalues[3] = Int64GetDatum(ColumnarStorageGetReservedStripeId(rel, true));\n\tvalues[4] = Int64GetDatum(ColumnarStorageGetReservedRowNumber(rel, true));\n\tvalues[5] = Int64GetDatum(ColumnarStorageGetReservedOffset(rel, true));\n\n\t/* release lock */\n\ttable_close(rel, AccessShareLock);\n\n\tHeapTuple tuple = heap_form_tuple(tupdesc, values, nulls);\n\n\tPG_RETURN_DATUM(HeapTupleGetDatum(tuple));\n}\n\n\n/*\n * MemoryContextTotals adds stats of the given memory context and its\n * subtree to the given counters.\n */\nstatic void\nMemoryContextTotals(MemoryContext context, MemoryContextCounters *counters)\n{\n\tif (context == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tMemoryContext child;\n\tfor (child = context->firstchild; child != NULL; child = child->nextchild)\n\t{\n\t\tMemoryContextTotals(child, counters);\n\t}\n\n\tcontext->methods->stats(context, NULL, NULL, counters, true);\n}\n"
  },
  {
    "path": "src/backend/columnar/columnar_metadata.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_metadata.c\n *\n * Copyright (c) Citus Data, Inc.\n *\n * Manages metadata for columnar relations in separate, shared metadata tables\n * in the \"columnar\" schema.\n *\n *   * holds basic stripe information including data size and row counts\n *   * holds basic chunk and chunk group information like data offsets and\n *     min/max values (used for Chunk Group Filtering)\n *   * useful for fast VACUUM operations (e.g. reporting with VACUUM VERBOSE)\n *   * useful for stats/costing\n *   * maps logical row numbers to stripe IDs\n *   * TODO: visibility information\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include <sys/stat.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n#include \"port.h\"\n#include \"safe_lib.h\"\n\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/nbtree.h\"\n#include \"access/xact.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/defrem.h\"\n#include \"commands/sequence.h\"\n#include \"commands/trigger.h\"\n#include \"executor/executor.h\"\n#include \"executor/spi.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/execnodes.h\"\n#include \"parser/parse_relation.h\"\n#include \"storage/fd.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/procarray.h\"\n#include \"storage/relfilelocator.h\"\n#include \"storage/smgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/rel.h\"\n#include \"utils/relfilenumbermap.h\"\n\n#include \"citus_version.h\"\n#include \"pg_version_constants.h\"\n\n#include \"columnar/columnar.h\"\n#include \"columnar/columnar_storage.h\"\n#include \"columnar/columnar_version_compat.h\"\n\n#include \"distributed/listutils.h\"\n\n#define COLUMNAR_RELOPTION_NAMESPACE \"columnar\"\n#define SLOW_METADATA_ACCESS_WARNING \\\n\t\t\"Metadata index %s is not available, this might mean slower read/writes \" \\\n\t\t\"on columnar tables. This is expected during Postgres upgrades and not \" \\\n\t\t\"expected otherwise.\"\n\ntypedef struct\n{\n\tRelation rel;\n\tEState *estate;\n\tResultRelInfo *resultRelInfo;\n} ModifyState;\n\n/* RowNumberLookupMode to be used in StripeMetadataLookupRowNumber */\ntypedef enum RowNumberLookupMode\n{\n\t/*\n\t * Find the stripe whose firstRowNumber is less than or equal to given\n\t * input rowNumber.\n\t */\n\tFIND_LESS_OR_EQUAL,\n\n\t/*\n\t * Find the stripe whose firstRowNumber is greater than input rowNumber.\n\t */\n\tFIND_GREATER\n} RowNumberLookupMode;\n\nstatic void ParseColumnarRelOptions(List *reloptions, ColumnarOptions *options);\nstatic void InsertEmptyStripeMetadataRow(uint64 storageId, uint64 stripeId,\n\t\t\t\t\t\t\t\t\t\t uint32 columnCount, uint32 chunkGroupRowCount,\n\t\t\t\t\t\t\t\t\t\t uint64 firstRowNumber);\nstatic void GetHighestUsedAddressAndId(uint64 storageId,\n\t\t\t\t\t\t\t\t\t   uint64 *highestUsedAddress,\n\t\t\t\t\t\t\t\t\t   uint64 *highestUsedId);\nstatic StripeMetadata * UpdateStripeMetadataRow(uint64 storageId, uint64 stripeId,\n\t\t\t\t\t\t\t\t\t\t\t\tuint64 fileOffset, uint64 dataLength,\n\t\t\t\t\t\t\t\t\t\t\t\tuint64 rowCount, uint64 chunkCount);\n\nstatic List * ReadDataFileStripeList(uint64 storageId, Snapshot snapshot);\nstatic StripeMetadata * BuildStripeMetadata(Relation columnarStripes,\n\t\t\t\t\t\t\t\t\t\t\tHeapTuple heapTuple);\nstatic uint32 * ReadChunkGroupRowCounts(uint64 storageId, uint64 stripe, uint32\n\t\t\t\t\t\t\t\t\t\tchunkGroupCount, Snapshot snapshot);\nstatic Oid ColumnarStorageIdSequenceRelationId(void);\nstatic Oid ColumnarStripeRelationId(void);\nstatic Oid ColumnarStripePKeyIndexRelationId(void);\nstatic Oid ColumnarStripeFirstRowNumberIndexRelationId(void);\nstatic Oid ColumnarOptionsRelationId(void);\nstatic Oid ColumnarOptionsIndexRegclass(void);\nstatic Oid ColumnarChunkRelationId(void);\nstatic Oid ColumnarChunkGroupRelationId(void);\nstatic Oid ColumnarChunkIndexRelationId(void);\nstatic Oid ColumnarChunkGroupIndexRelationId(void);\nstatic Oid ColumnarNamespaceId(void);\nstatic uint64 LookupStorageId(Oid relationId, RelFileLocator relfilelocator);\nstatic uint64 GetHighestUsedRowNumber(uint64 storageId);\nstatic void DeleteStorageFromColumnarMetadataTable(Oid metadataTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t   AttrNumber storageIdAtrrNumber,\n\t\t\t\t\t\t\t\t\t\t\t\t   Oid storageIdIndexId,\n\t\t\t\t\t\t\t\t\t\t\t\t   uint64 storageId);\nstatic ModifyState * StartModifyRelation(Relation rel);\nstatic void InsertTupleAndEnforceConstraints(ModifyState *state, Datum *values,\n\t\t\t\t\t\t\t\t\t\t\t bool *nulls);\nstatic void DeleteTupleAndEnforceConstraints(ModifyState *state, HeapTuple heapTuple);\nstatic void FinishModifyRelation(ModifyState *state);\nstatic EState * create_estate_for_relation(Relation rel);\nstatic bytea * DatumToBytea(Datum value, Form_pg_attribute attrForm);\nstatic Datum ByteaToDatum(bytea *bytes, Form_pg_attribute attrForm);\nstatic bool WriteColumnarOptions(Oid regclass, ColumnarOptions *options, bool overwrite);\nstatic StripeMetadata * StripeMetadataLookupRowNumber(Relation relation, uint64 rowNumber,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  Snapshot snapshot,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  RowNumberLookupMode lookupMode);\nstatic void CheckStripeMetadataConsistency(StripeMetadata *stripeMetadata);\n\nPG_FUNCTION_INFO_V1(columnar_relation_storageid);\n\n/* constants for columnar.options */\n#define Natts_columnar_options 5\n#define Anum_columnar_options_regclass 1\n#define Anum_columnar_options_chunk_group_row_limit 2\n#define Anum_columnar_options_stripe_row_limit 3\n#define Anum_columnar_options_compression_level 4\n#define Anum_columnar_options_compression 5\n\n/* ----------------\n *\t\tcolumnar.options definition.\n * ----------------\n */\ntypedef struct FormData_columnar_options\n{\n\tOid regclass;\n\tint32 chunk_group_row_limit;\n\tint32 stripe_row_limit;\n\tint32 compressionLevel;\n\tNameData compression;\n\n#ifdef CATALOG_VARLEN           /* variable-length fields start here */\n#endif\n} FormData_columnar_options;\ntypedef FormData_columnar_options *Form_columnar_options;\n\n\n/* constants for columnar.stripe */\n#define Natts_columnar_stripe 9\n#define Anum_columnar_stripe_storageid 1\n#define Anum_columnar_stripe_stripe 2\n#define Anum_columnar_stripe_file_offset 3\n#define Anum_columnar_stripe_data_length 4\n#define Anum_columnar_stripe_column_count 5\n#define Anum_columnar_stripe_chunk_row_count 6\n#define Anum_columnar_stripe_row_count 7\n#define Anum_columnar_stripe_chunk_count 8\n#define Anum_columnar_stripe_first_row_number 9\n\nstatic int GetFirstRowNumberAttrIndexInColumnarStripe(TupleDesc tupleDesc);\n\n/* constants for columnar.chunk_group */\n#define Natts_columnar_chunkgroup 4\n#define Anum_columnar_chunkgroup_storageid 1\n#define Anum_columnar_chunkgroup_stripe 2\n#define Anum_columnar_chunkgroup_chunk 3\n#define Anum_columnar_chunkgroup_row_count 4\n\n/* constants for columnar.chunk */\n#define Natts_columnar_chunk 14\n#define Anum_columnar_chunk_storageid 1\n#define Anum_columnar_chunk_stripe 2\n#define Anum_columnar_chunk_attr 3\n#define Anum_columnar_chunk_chunk 4\n#define Anum_columnar_chunk_minimum_value 5\n#define Anum_columnar_chunk_maximum_value 6\n#define Anum_columnar_chunk_value_stream_offset 7\n#define Anum_columnar_chunk_value_stream_length 8\n#define Anum_columnar_chunk_exists_stream_offset 9\n#define Anum_columnar_chunk_exists_stream_length 10\n#define Anum_columnar_chunk_value_compression_type 11\n#define Anum_columnar_chunk_value_compression_level 12\n#define Anum_columnar_chunk_value_decompressed_size 13\n#define Anum_columnar_chunk_value_count 14\n\n\n/*\n * InitColumnarOptions initialized the columnar table options. Meaning it writes the\n * default options to the options table if not already existing.\n */\nvoid\nInitColumnarOptions(Oid regclass)\n{\n\t/*\n\t * When upgrading we retain options for all columnar tables by upgrading\n\t * \"columnar.options\" catalog table, so we shouldn't do anything here.\n\t */\n\tif (IsBinaryUpgrade)\n\t{\n\t\treturn;\n\t}\n\n\tColumnarOptions defaultOptions = {\n\t\t.chunkRowCount = columnar_chunk_group_row_limit,\n\t\t.stripeRowCount = columnar_stripe_row_limit,\n\t\t.compressionType = columnar_compression,\n\t\t.compressionLevel = columnar_compression_level\n\t};\n\n\tWriteColumnarOptions(regclass, &defaultOptions, false);\n}\n\n\n/*\n * ParseColumnarRelOptions - update the given 'options' using the given list\n * of DefElem.\n */\nstatic void\nParseColumnarRelOptions(List *reloptions, ColumnarOptions *options)\n{\n\tListCell *lc = NULL;\n\n\tforeach(lc, reloptions)\n\t{\n\t\tDefElem *elem = castNode(DefElem, lfirst(lc));\n\n\t\tif (elem->defnamespace == NULL ||\n\t\t\tstrcmp(elem->defnamespace, COLUMNAR_RELOPTION_NAMESPACE) != 0)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"columnar options must have the prefix \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t   COLUMNAR_RELOPTION_NAMESPACE)));\n\t\t}\n\n\t\tif (strcmp(elem->defname, \"chunk_group_row_limit\") == 0)\n\t\t{\n\t\t\toptions->chunkRowCount = (elem->arg == NULL) ?\n\t\t\t\t\t\t\t\t\t columnar_chunk_group_row_limit : defGetInt64(elem);\n\t\t\tif (options->chunkRowCount < CHUNK_ROW_COUNT_MINIMUM ||\n\t\t\t\toptions->chunkRowCount > CHUNK_ROW_COUNT_MAXIMUM)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"chunk group row count limit out of range\"),\n\t\t\t\t\t\t\t\terrhint(\"chunk group row count limit must be between \"\n\t\t\t\t\t\t\t\t\t\tUINT64_FORMAT \" and \" UINT64_FORMAT,\n\t\t\t\t\t\t\t\t\t\t(uint64) CHUNK_ROW_COUNT_MINIMUM,\n\t\t\t\t\t\t\t\t\t\t(uint64) CHUNK_ROW_COUNT_MAXIMUM)));\n\t\t\t}\n\t\t}\n\t\telse if (strcmp(elem->defname, \"stripe_row_limit\") == 0)\n\t\t{\n\t\t\toptions->stripeRowCount = (elem->arg == NULL) ?\n\t\t\t\t\t\t\t\t\t  columnar_stripe_row_limit : defGetInt64(elem);\n\n\t\t\tif (options->stripeRowCount < STRIPE_ROW_COUNT_MINIMUM ||\n\t\t\t\toptions->stripeRowCount > STRIPE_ROW_COUNT_MAXIMUM)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"stripe row count limit out of range\"),\n\t\t\t\t\t\t\t\terrhint(\"stripe row count limit must be between \"\n\t\t\t\t\t\t\t\t\t\tUINT64_FORMAT \" and \" UINT64_FORMAT,\n\t\t\t\t\t\t\t\t\t\t(uint64) STRIPE_ROW_COUNT_MINIMUM,\n\t\t\t\t\t\t\t\t\t\t(uint64) STRIPE_ROW_COUNT_MAXIMUM)));\n\t\t\t}\n\t\t}\n\t\telse if (strcmp(elem->defname, \"compression\") == 0)\n\t\t{\n\t\t\toptions->compressionType = (elem->arg == NULL) ?\n\t\t\t\t\t\t\t\t\t   columnar_compression : ParseCompressionType(\n\t\t\t\tdefGetString(elem));\n\n\t\t\tif (options->compressionType == COMPRESSION_TYPE_INVALID)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"unknown compression type for columnar table: %s\",\n\t\t\t\t\t\t\t\t\t   quote_identifier(defGetString(elem)))));\n\t\t\t}\n\t\t}\n\t\telse if (strcmp(elem->defname, \"compression_level\") == 0)\n\t\t{\n\t\t\toptions->compressionLevel = (elem->arg == NULL) ?\n\t\t\t\t\t\t\t\t\t\tcolumnar_compression_level : defGetInt64(elem);\n\n\t\t\tif (options->compressionLevel < COMPRESSION_LEVEL_MIN ||\n\t\t\t\toptions->compressionLevel > COMPRESSION_LEVEL_MAX)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"compression level out of range\"),\n\t\t\t\t\t\t\t\terrhint(\"compression level must be between %d and %d\",\n\t\t\t\t\t\t\t\t\t\tCOMPRESSION_LEVEL_MIN,\n\t\t\t\t\t\t\t\t\t\tCOMPRESSION_LEVEL_MAX)));\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unrecognized columnar storage parameter \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t   elem->defname)));\n\t\t}\n\t}\n}\n\n\n/*\n * ExtractColumnarOptions - extract columnar options from inOptions, appending\n * to inoutColumnarOptions. Return the remaining (non-columnar) options.\n */\nList *\nExtractColumnarRelOptions(List *inOptions, List **inoutColumnarOptions)\n{\n\tList *otherOptions = NIL;\n\n\tListCell *lc = NULL;\n\tforeach(lc, inOptions)\n\t{\n\t\tDefElem *elem = castNode(DefElem, lfirst(lc));\n\n\t\tif (elem->defnamespace != NULL &&\n\t\t\tstrcmp(elem->defnamespace, COLUMNAR_RELOPTION_NAMESPACE) == 0)\n\t\t{\n\t\t\t*inoutColumnarOptions = lappend(*inoutColumnarOptions, elem);\n\t\t}\n\t\telse\n\t\t{\n\t\t\totherOptions = lappend(otherOptions, elem);\n\t\t}\n\t}\n\n\t/* validate options */\n\tColumnarOptions dummy = { 0 };\n\tParseColumnarRelOptions(*inoutColumnarOptions, &dummy);\n\n\treturn otherOptions;\n}\n\n\n/*\n * SetColumnarRelOptions - apply the list of DefElem options to the\n * relation. If there are duplicates, the last one in the list takes effect.\n */\nvoid\nSetColumnarRelOptions(RangeVar *rv, List *reloptions)\n{\n\tColumnarOptions options = { 0 };\n\n\tif (reloptions == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tRelation rel = relation_openrv(rv, AccessShareLock);\n\tOid relid = RelationGetRelid(rel);\n\trelation_close(rel, NoLock);\n\n\t/* get existing or default options */\n\tif (!ReadColumnarOptions(relid, &options))\n\t{\n\t\t/* if extension doesn't exist, just return */\n\t\treturn;\n\t}\n\n\tParseColumnarRelOptions(reloptions, &options);\n\n\tSetColumnarOptions(relid, &options);\n}\n\n\n/*\n * SetColumnarOptions writes the passed table options as the authoritive options to the\n * table irregardless of the optiones already existing or not. This can be used to put a\n * table in a certain state.\n */\nvoid\nSetColumnarOptions(Oid regclass, ColumnarOptions *options)\n{\n\tWriteColumnarOptions(regclass, options, true);\n}\n\n\n/*\n * WriteColumnarOptions writes the options to the catalog table for a given regclass.\n *  - If overwrite is false it will only write the values if there is not already a record\n *    found.\n *  - If overwrite is true it will always write the settings\n *\n * The return value indicates if the record has been written.\n */\nstatic bool\nWriteColumnarOptions(Oid regclass, ColumnarOptions *options, bool overwrite)\n{\n\t/*\n\t * When upgrading we should retain the options from the previous\n\t * cluster and don't write new options.\n\t */\n\tAssert(!IsBinaryUpgrade);\n\n\tbool written = false;\n\n\tbool nulls[Natts_columnar_options] = { 0 };\n\tDatum values[Natts_columnar_options] = {\n\t\tObjectIdGetDatum(regclass),\n\t\tInt32GetDatum(options->chunkRowCount),\n\t\tInt32GetDatum(options->stripeRowCount),\n\t\tInt32GetDatum(options->compressionLevel),\n\t\t0, /* to be filled below */\n\t};\n\n\tNameData compressionName = { 0 };\n\tnamestrcpy(&compressionName, CompressionTypeStr(options->compressionType));\n\tvalues[Anum_columnar_options_compression - 1] = NameGetDatum(&compressionName);\n\n\t/* create heap tuple and insert into catalog table */\n\tRelation columnarOptions = relation_open(ColumnarOptionsRelationId(),\n\t\t\t\t\t\t\t\t\t\t\t RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(columnarOptions);\n\n\t/* find existing item to perform update if exist */\n\tScanKeyData scanKey[1] = { 0 };\n\tScanKeyInit(&scanKey[0], Anum_columnar_options_regclass, BTEqualStrategyNumber,\n\t\t\t\tF_OIDEQ,\n\t\t\t\tObjectIdGetDatum(regclass));\n\n\tRelation index = index_open(ColumnarOptionsIndexRegclass(), AccessShareLock);\n\tSysScanDesc scanDescriptor = systable_beginscan_ordered(columnarOptions, index, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t1, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext_ordered(scanDescriptor, ForwardScanDirection);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tif (overwrite)\n\t\t{\n\t\t\t/* TODO check if the options are actually different, skip if not changed */\n\t\t\t/* update existing record */\n\t\t\tbool update[Natts_columnar_options] = { 0 };\n\t\t\tupdate[Anum_columnar_options_chunk_group_row_limit - 1] = true;\n\t\t\tupdate[Anum_columnar_options_stripe_row_limit - 1] = true;\n\t\t\tupdate[Anum_columnar_options_compression_level - 1] = true;\n\t\t\tupdate[Anum_columnar_options_compression - 1] = true;\n\n\t\t\tHeapTuple tuple = heap_modify_tuple(heapTuple, tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\tvalues, nulls, update);\n\t\t\tCatalogTupleUpdate(columnarOptions, &tuple->t_self, tuple);\n\t\t\twritten = true;\n\t\t}\n\t}\n\telse\n\t{\n\t\t/* inserting new record */\n\t\tHeapTuple newTuple = heap_form_tuple(tupleDescriptor, values, nulls);\n\t\tCatalogTupleInsert(columnarOptions, newTuple);\n\t\twritten = true;\n\t}\n\n\tif (written)\n\t{\n\t\tCommandCounterIncrement();\n\t}\n\n\tsystable_endscan_ordered(scanDescriptor);\n\tindex_close(index, AccessShareLock);\n\trelation_close(columnarOptions, RowExclusiveLock);\n\n\treturn written;\n}\n\n\n/*\n * DeleteColumnarTableOptions removes the columnar table options for a regclass. When\n * missingOk is false it will throw an error when no table options can be found.\n *\n * Returns whether a record has been removed.\n */\nbool\nDeleteColumnarTableOptions(Oid regclass, bool missingOk)\n{\n\tbool result = false;\n\n\t/*\n\t * When upgrading we shouldn't delete or modify table options and\n\t * retain options from the previous cluster.\n\t */\n\tAssert(!IsBinaryUpgrade);\n\n\tRelation columnarOptions = try_relation_open(ColumnarOptionsRelationId(),\n\t\t\t\t\t\t\t\t\t\t\t\t RowExclusiveLock);\n\tif (columnarOptions == NULL)\n\t{\n\t\t/* extension has been dropped */\n\t\treturn false;\n\t}\n\n\t/* find existing item to remove */\n\tScanKeyData scanKey[1] = { 0 };\n\tScanKeyInit(&scanKey[0], Anum_columnar_options_regclass, BTEqualStrategyNumber,\n\t\t\t\tF_OIDEQ,\n\t\t\t\tObjectIdGetDatum(regclass));\n\n\tRelation index = index_open(ColumnarOptionsIndexRegclass(), AccessShareLock);\n\tSysScanDesc scanDescriptor = systable_beginscan_ordered(columnarOptions, index, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t1, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext_ordered(scanDescriptor, ForwardScanDirection);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tCatalogTupleDelete(columnarOptions, &heapTuple->t_self);\n\t\tCommandCounterIncrement();\n\n\t\tresult = true;\n\t}\n\telse if (!missingOk)\n\t{\n\t\tereport(ERROR, (errmsg(\"missing options for regclass: %d\", regclass)));\n\t}\n\n\tsystable_endscan_ordered(scanDescriptor);\n\tindex_close(index, AccessShareLock);\n\trelation_close(columnarOptions, RowExclusiveLock);\n\n\treturn result;\n}\n\n\nbool\nReadColumnarOptions(Oid regclass, ColumnarOptions *options)\n{\n\tScanKeyData scanKey[1];\n\n\tScanKeyInit(&scanKey[0], Anum_columnar_options_regclass, BTEqualStrategyNumber,\n\t\t\t\tF_OIDEQ,\n\t\t\t\tObjectIdGetDatum(regclass));\n\n\tOid columnarOptionsOid = ColumnarOptionsRelationId();\n\tRelation columnarOptions = try_relation_open(columnarOptionsOid, AccessShareLock);\n\tif (columnarOptions == NULL)\n\t{\n\t\t/*\n\t\t * Extension has been dropped. This can be called while\n\t\t * dropping extension or database via ObjectAccess().\n\t\t */\n\t\treturn false;\n\t}\n\n\tRelation index = try_relation_open(ColumnarOptionsIndexRegclass(), AccessShareLock);\n\tif (index == NULL)\n\t{\n\t\ttable_close(columnarOptions, AccessShareLock);\n\n\t\t/* extension has been dropped */\n\t\treturn false;\n\t}\n\n\tSysScanDesc scanDescriptor = systable_beginscan_ordered(columnarOptions, index, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t1, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext_ordered(scanDescriptor, ForwardScanDirection);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tForm_columnar_options tupOptions = (Form_columnar_options) GETSTRUCT(heapTuple);\n\n\t\toptions->chunkRowCount = tupOptions->chunk_group_row_limit;\n\t\toptions->stripeRowCount = tupOptions->stripe_row_limit;\n\t\toptions->compressionLevel = tupOptions->compressionLevel;\n\t\toptions->compressionType = ParseCompressionType(NameStr(tupOptions->compression));\n\t}\n\telse\n\t{\n\t\t/* populate options with system defaults */\n\t\toptions->compressionType = columnar_compression;\n\t\toptions->stripeRowCount = columnar_stripe_row_limit;\n\t\toptions->chunkRowCount = columnar_chunk_group_row_limit;\n\t\toptions->compressionLevel = columnar_compression_level;\n\t}\n\n\tsystable_endscan_ordered(scanDescriptor);\n\tindex_close(index, AccessShareLock);\n\trelation_close(columnarOptions, AccessShareLock);\n\n\treturn true;\n}\n\n\n/*\n * SaveStripeSkipList saves chunkList for a given stripe as rows\n * of columnar.chunk.\n */\nvoid\nSaveStripeSkipList(Oid relid, RelFileLocator relfilelocator, uint64 stripe,\n\t\t\t\t   StripeSkipList *chunkList,\n\t\t\t\t   TupleDesc tupleDescriptor)\n{\n\tuint32 columnIndex = 0;\n\tuint32 chunkIndex = 0;\n\tuint32 columnCount = chunkList->columnCount;\n\n\tuint64 storageId = LookupStorageId(relid, relfilelocator);\n\tOid columnarChunkOid = ColumnarChunkRelationId();\n\tRelation columnarChunk = table_open(columnarChunkOid, RowExclusiveLock);\n\tModifyState *modifyState = StartModifyRelation(columnarChunk);\n\tbool pushed_snapshot = false;\n\n\tif (!ActiveSnapshotSet())\n\t{\n\t\tPushActiveSnapshot(GetTransactionSnapshot());\n\t\tpushed_snapshot = true;\n\t}\n\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tfor (chunkIndex = 0; chunkIndex < chunkList->chunkCount; chunkIndex++)\n\t\t{\n\t\t\tColumnChunkSkipNode *chunk =\n\t\t\t\t&chunkList->chunkSkipNodeArray[columnIndex][chunkIndex];\n\n\t\t\tDatum values[Natts_columnar_chunk] = {\n\t\t\t\tUInt64GetDatum(storageId),\n\t\t\t\tInt64GetDatum(stripe),\n\t\t\t\tInt32GetDatum(columnIndex + 1),\n\t\t\t\tInt32GetDatum(chunkIndex),\n\t\t\t\t0, /* to be filled below */\n\t\t\t\t0, /* to be filled below */\n\t\t\t\tInt64GetDatum(chunk->valueChunkOffset),\n\t\t\t\tInt64GetDatum(chunk->valueLength),\n\t\t\t\tInt64GetDatum(chunk->existsChunkOffset),\n\t\t\t\tInt64GetDatum(chunk->existsLength),\n\t\t\t\tInt32GetDatum(chunk->valueCompressionType),\n\t\t\t\tInt32GetDatum(chunk->valueCompressionLevel),\n\t\t\t\tInt64GetDatum(chunk->decompressedValueSize),\n\t\t\t\tInt64GetDatum(chunk->rowCount)\n\t\t\t};\n\n\t\t\tbool nulls[Natts_columnar_chunk] = { false };\n\n\t\t\tif (chunk->hasMinMax)\n\t\t\t{\n\t\t\t\tvalues[Anum_columnar_chunk_minimum_value - 1] =\n\t\t\t\t\tPointerGetDatum(DatumToBytea(chunk->minimumValue,\n\t\t\t\t\t\t\t\t\t\t\t\t TupleDescAttr(tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   columnIndex)));\n\t\t\t\tvalues[Anum_columnar_chunk_maximum_value - 1] =\n\t\t\t\t\tPointerGetDatum(DatumToBytea(chunk->maximumValue,\n\t\t\t\t\t\t\t\t\t\t\t\t TupleDescAttr(tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   columnIndex)));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnulls[Anum_columnar_chunk_minimum_value - 1] = true;\n\t\t\t\tnulls[Anum_columnar_chunk_maximum_value - 1] = true;\n\t\t\t}\n\t\t\tInsertTupleAndEnforceConstraints(modifyState, values, nulls);\n\t\t}\n\t}\n\tif (pushed_snapshot)\n\t{\n\t\tPopActiveSnapshot();\n\t}\n\n\tFinishModifyRelation(modifyState);\n\ttable_close(columnarChunk, RowExclusiveLock);\n}\n\n\n/*\n * SaveChunkGroups saves the metadata for given chunk groups in columnar.chunk_group.\n */\nvoid\nSaveChunkGroups(Oid relid, RelFileLocator relfilelocator, uint64 stripe,\n\t\t\t\tList *chunkGroupRowCounts)\n{\n\tuint64 storageId = LookupStorageId(relid, relfilelocator);\n\tOid columnarChunkGroupOid = ColumnarChunkGroupRelationId();\n\tRelation columnarChunkGroup = table_open(columnarChunkGroupOid, RowExclusiveLock);\n\tModifyState *modifyState = StartModifyRelation(columnarChunkGroup);\n\n\tListCell *lc = NULL;\n\tint chunkId = 0;\n\n\tforeach(lc, chunkGroupRowCounts)\n\t{\n\t\tint64 rowCount = lfirst_int(lc);\n\t\tDatum values[Natts_columnar_chunkgroup] = {\n\t\t\tUInt64GetDatum(storageId),\n\t\t\tInt64GetDatum(stripe),\n\t\t\tInt32GetDatum(chunkId),\n\t\t\tInt64GetDatum(rowCount)\n\t\t};\n\n\t\tbool nulls[Natts_columnar_chunkgroup] = { false };\n\n\t\tInsertTupleAndEnforceConstraints(modifyState, values, nulls);\n\t\tchunkId++;\n\t}\n\n\tFinishModifyRelation(modifyState);\n\ttable_close(columnarChunkGroup, NoLock);\n}\n\n\n/*\n * ReadStripeSkipList fetches chunk metadata for a given stripe.\n */\nStripeSkipList *\nReadStripeSkipList(Relation rel, uint64 stripe,\n\t\t\t\t   TupleDesc tupleDescriptor,\n\t\t\t\t   uint32 chunkCount, Snapshot snapshot)\n{\n\tint32 columnIndex = 0;\n\tHeapTuple heapTuple = NULL;\n\tuint32 columnCount = tupleDescriptor->natts;\n\tScanKeyData scanKey[2];\n\n\tuint64 storageId = LookupStorageId(RelationPrecomputeOid(rel),\n\t\t\t\t\t\t\t\t\t   rel->rd_locator);\n\n\tOid columnarChunkOid = ColumnarChunkRelationId();\n\tRelation columnarChunk = table_open(columnarChunkOid, AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_columnar_chunk_storageid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));\n\tScanKeyInit(&scanKey[1], Anum_columnar_chunk_stripe,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(stripe));\n\n\tOid indexId = ColumnarChunkIndexRelationId();\n\tbool indexOk = OidIsValid(indexId);\n\tSysScanDesc scanDescriptor = systable_beginscan(columnarChunk, indexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOk, snapshot, 2, scanKey);\n\n\tstatic bool loggedSlowMetadataAccessWarning = false;\n\tif (!indexOk && !loggedSlowMetadataAccessWarning)\n\t{\n\t\tereport(WARNING, (errmsg(SLOW_METADATA_ACCESS_WARNING, \"chunk_pkey\")));\n\t\tloggedSlowMetadataAccessWarning = true;\n\t}\n\n\tStripeSkipList *chunkList = palloc0(sizeof(StripeSkipList));\n\tchunkList->chunkCount = chunkCount;\n\tchunkList->columnCount = columnCount;\n\tchunkList->chunkSkipNodeArray = palloc0(columnCount * sizeof(ColumnChunkSkipNode *));\n\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tchunkList->chunkSkipNodeArray[columnIndex] =\n\t\t\tpalloc0(chunkCount * sizeof(ColumnChunkSkipNode));\n\t}\n\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tDatum datumArray[Natts_columnar_chunk];\n\t\tbool isNullArray[Natts_columnar_chunk];\n\n\t\theap_deform_tuple(heapTuple, RelationGetDescr(columnarChunk), datumArray,\n\t\t\t\t\t\t  isNullArray);\n\n\t\tint32 attr = DatumGetInt32(datumArray[Anum_columnar_chunk_attr - 1]);\n\t\tint32 chunkIndex = DatumGetInt32(datumArray[Anum_columnar_chunk_chunk - 1]);\n\n\t\tif (attr <= 0 || attr > columnCount)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"invalid columnar chunk entry\"),\n\t\t\t\t\t\t\terrdetail(\"Attribute number out of range: %d\", attr)));\n\t\t}\n\n\t\tif (chunkIndex < 0 || chunkIndex >= chunkCount)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"invalid columnar chunk entry\"),\n\t\t\t\t\t\t\terrdetail(\"Chunk number out of range: %d\", chunkIndex)));\n\t\t}\n\n\t\tcolumnIndex = attr - 1;\n\n\t\tColumnChunkSkipNode *chunk =\n\t\t\t&chunkList->chunkSkipNodeArray[columnIndex][chunkIndex];\n\t\tchunk->rowCount = DatumGetInt64(datumArray[Anum_columnar_chunk_value_count -\n\t\t\t\t\t\t\t\t\t\t\t\t   1]);\n\t\tchunk->valueChunkOffset =\n\t\t\tDatumGetInt64(datumArray[Anum_columnar_chunk_value_stream_offset - 1]);\n\t\tchunk->valueLength =\n\t\t\tDatumGetInt64(datumArray[Anum_columnar_chunk_value_stream_length - 1]);\n\t\tchunk->existsChunkOffset =\n\t\t\tDatumGetInt64(datumArray[Anum_columnar_chunk_exists_stream_offset - 1]);\n\t\tchunk->existsLength =\n\t\t\tDatumGetInt64(datumArray[Anum_columnar_chunk_exists_stream_length - 1]);\n\t\tchunk->valueCompressionType =\n\t\t\tDatumGetInt32(datumArray[Anum_columnar_chunk_value_compression_type - 1]);\n\t\tchunk->valueCompressionLevel =\n\t\t\tDatumGetInt32(datumArray[Anum_columnar_chunk_value_compression_level - 1]);\n\t\tchunk->decompressedValueSize =\n\t\t\tDatumGetInt64(datumArray[Anum_columnar_chunk_value_decompressed_size - 1]);\n\n\t\tif (isNullArray[Anum_columnar_chunk_minimum_value - 1] ||\n\t\t\tisNullArray[Anum_columnar_chunk_maximum_value - 1])\n\t\t{\n\t\t\tchunk->hasMinMax = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbytea *minValue = DatumGetByteaP(\n\t\t\t\tdatumArray[Anum_columnar_chunk_minimum_value - 1]);\n\t\t\tbytea *maxValue = DatumGetByteaP(\n\t\t\t\tdatumArray[Anum_columnar_chunk_maximum_value - 1]);\n\n\t\t\tchunk->minimumValue =\n\t\t\t\tByteaToDatum(minValue, TupleDescAttr(tupleDescriptor, columnIndex));\n\t\t\tchunk->maximumValue =\n\t\t\t\tByteaToDatum(maxValue, TupleDescAttr(tupleDescriptor, columnIndex));\n\n\t\t\tchunk->hasMinMax = true;\n\t\t}\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(columnarChunk, AccessShareLock);\n\n\tchunkList->chunkGroupRowCounts =\n\t\tReadChunkGroupRowCounts(storageId, stripe, chunkCount, snapshot);\n\n\treturn chunkList;\n}\n\n\n/*\n * FindStripeByRowNumber returns StripeMetadata for the stripe that has the\n * smallest firstRowNumber among the stripes whose firstRowNumber is grater\n * than given rowNumber. If no such stripe exists, then returns NULL.\n */\nStripeMetadata *\nFindNextStripeByRowNumber(Relation relation, uint64 rowNumber, Snapshot snapshot)\n{\n\treturn StripeMetadataLookupRowNumber(relation, rowNumber, snapshot, FIND_GREATER);\n}\n\n\n/*\n * FindStripeByRowNumber returns StripeMetadata for the stripe that contains\n * the row with rowNumber. If no such stripe exists, then returns NULL.\n */\nStripeMetadata *\nFindStripeByRowNumber(Relation relation, uint64 rowNumber, Snapshot snapshot)\n{\n\tStripeMetadata *stripeMetadata =\n\t\tFindStripeWithMatchingFirstRowNumber(relation, rowNumber, snapshot);\n\tif (!stripeMetadata)\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (rowNumber > StripeGetHighestRowNumber(stripeMetadata))\n\t{\n\t\treturn NULL;\n\t}\n\n\treturn stripeMetadata;\n}\n\n\n/*\n * FindStripeWithMatchingFirstRowNumber returns a StripeMetadata object for\n * the stripe that has the greatest firstRowNumber among the stripes whose\n * firstRowNumber is smaller than or equal to given rowNumber. If no such\n * stripe exists, then returns NULL.\n *\n * Note that this doesn't mean that found stripe certainly contains the tuple\n * with given rowNumber. This is because, it also needs to be verified if\n * highest row number that found stripe contains is greater than or equal to\n * given rowNumber. For this reason, unless that additional check is done,\n * this function is mostly useful for checking against \"possible\" constraint\n * violations due to concurrent writes that are not flushed by other backends\n * yet.\n */\nStripeMetadata *\nFindStripeWithMatchingFirstRowNumber(Relation relation, uint64 rowNumber,\n\t\t\t\t\t\t\t\t\t Snapshot snapshot)\n{\n\treturn StripeMetadataLookupRowNumber(relation, rowNumber, snapshot,\n\t\t\t\t\t\t\t\t\t\t FIND_LESS_OR_EQUAL);\n}\n\n\n/*\n * StripeWriteState returns write state of given stripe.\n */\nStripeWriteStateEnum\nStripeWriteState(StripeMetadata *stripeMetadata)\n{\n\tif (stripeMetadata->aborted)\n\t{\n\t\treturn STRIPE_WRITE_ABORTED;\n\t}\n\telse if (stripeMetadata->rowCount > 0)\n\t{\n\t\treturn STRIPE_WRITE_FLUSHED;\n\t}\n\telse\n\t{\n\t\treturn STRIPE_WRITE_IN_PROGRESS;\n\t}\n}\n\n\n/*\n * StripeGetHighestRowNumber returns rowNumber of the row with highest\n * rowNumber in given stripe.\n */\nuint64\nStripeGetHighestRowNumber(StripeMetadata *stripeMetadata)\n{\n\treturn stripeMetadata->firstRowNumber + stripeMetadata->rowCount - 1;\n}\n\n\n/*\n * StripeMetadataLookupRowNumber returns StripeMetadata for the stripe whose\n * firstRowNumber is less than or equal to (FIND_LESS_OR_EQUAL), or is\n * greater than (FIND_GREATER) given rowNumber.\n * If no such stripe exists, then returns NULL.\n */\nstatic StripeMetadata *\nStripeMetadataLookupRowNumber(Relation relation, uint64 rowNumber, Snapshot snapshot,\n\t\t\t\t\t\t\t  RowNumberLookupMode lookupMode)\n{\n\tAssert(lookupMode == FIND_LESS_OR_EQUAL || lookupMode == FIND_GREATER);\n\n\tStripeMetadata *foundStripeMetadata = NULL;\n\n\tuint64 storageId = ColumnarStorageGetStorageId(relation, false);\n\tScanKeyData scanKey[2];\n\tScanKeyInit(&scanKey[0], Anum_columnar_stripe_storageid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));\n\n\tStrategyNumber strategyNumber = InvalidStrategy;\n\tRegProcedure procedure = InvalidOid;\n\tif (lookupMode == FIND_LESS_OR_EQUAL)\n\t{\n\t\tstrategyNumber = BTLessEqualStrategyNumber;\n\t\tprocedure = F_INT8LE;\n\t}\n\telse if (lookupMode == FIND_GREATER)\n\t{\n\t\tstrategyNumber = BTGreaterStrategyNumber;\n\t\tprocedure = F_INT8GT;\n\t}\n\n\tRelation columnarStripes = table_open(ColumnarStripeRelationId(), AccessShareLock);\n\tTupleDesc tupleDesc = RelationGetDescr(columnarStripes);\n\n\tScanKeyInit(&scanKey[1], GetFirstRowNumberAttrIndexInColumnarStripe(tupleDesc) + 1,\n\t\t\t\tstrategyNumber, procedure, Int64GetDatum(rowNumber));\n\n\tOid indexId = ColumnarStripeFirstRowNumberIndexRelationId();\n\tbool indexOk = OidIsValid(indexId);\n\tSysScanDesc scanDescriptor = systable_beginscan(columnarStripes, indexId, indexOk,\n\t\t\t\t\t\t\t\t\t\t\t\t\tsnapshot, 2, scanKey);\n\n\tstatic bool loggedSlowMetadataAccessWarning = false;\n\tif (!indexOk && !loggedSlowMetadataAccessWarning)\n\t{\n\t\tereport(WARNING, (errmsg(SLOW_METADATA_ACCESS_WARNING,\n\t\t\t\t\t\t\t\t \"stripe_first_row_number_idx\")));\n\t\tloggedSlowMetadataAccessWarning = true;\n\t}\n\n\tif (indexOk)\n\t{\n\t\tScanDirection scanDirection = NoMovementScanDirection;\n\t\tif (lookupMode == FIND_LESS_OR_EQUAL)\n\t\t{\n\t\t\tscanDirection = BackwardScanDirection;\n\t\t}\n\t\telse if (lookupMode == FIND_GREATER)\n\t\t{\n\t\t\tscanDirection = ForwardScanDirection;\n\t\t}\n\t\tHeapTuple heapTuple = systable_getnext_ordered(scanDescriptor, scanDirection);\n\t\tif (HeapTupleIsValid(heapTuple))\n\t\t{\n\t\t\tfoundStripeMetadata = BuildStripeMetadata(columnarStripes, heapTuple);\n\t\t}\n\t}\n\telse\n\t{\n\t\tHeapTuple heapTuple = NULL;\n\t\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t\t{\n\t\t\tStripeMetadata *stripe = BuildStripeMetadata(columnarStripes, heapTuple);\n\t\t\tif (!foundStripeMetadata)\n\t\t\t{\n\t\t\t\t/* first match */\n\t\t\t\tfoundStripeMetadata = stripe;\n\t\t\t}\n\t\t\telse if (lookupMode == FIND_LESS_OR_EQUAL &&\n\t\t\t\t\t stripe->firstRowNumber > foundStripeMetadata->firstRowNumber)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Among the stripes with firstRowNumber less-than-or-equal-to given,\n\t\t\t\t * we're looking for the one with the greatest firstRowNumber.\n\t\t\t\t */\n\t\t\t\tfoundStripeMetadata = stripe;\n\t\t\t}\n\t\t\telse if (lookupMode == FIND_GREATER &&\n\t\t\t\t\t stripe->firstRowNumber < foundStripeMetadata->firstRowNumber)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Among the stripes with firstRowNumber greater-than given,\n\t\t\t\t * we're looking for the one with the smallest firstRowNumber.\n\t\t\t\t */\n\t\t\t\tfoundStripeMetadata = stripe;\n\t\t\t}\n\t\t}\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(columnarStripes, AccessShareLock);\n\n\treturn foundStripeMetadata;\n}\n\n\n/*\n * CheckStripeMetadataConsistency first decides if stripe write operation for\n * given stripe is \"flushed\", \"aborted\" or \"in-progress\", then errors out if\n * its metadata entry contradicts with this fact.\n *\n * Checks performed here are just to catch bugs, so it is encouraged to call\n * this function whenever a StripeMetadata object is built from an heap tuple\n * of columnar.stripe. Currently, BuildStripeMetadata is the only function\n * that does this.\n */\nstatic void\nCheckStripeMetadataConsistency(StripeMetadata *stripeMetadata)\n{\n\tbool stripeLooksInProgress =\n\t\tstripeMetadata->rowCount == 0 && stripeMetadata->chunkCount == 0 &&\n\t\tstripeMetadata->fileOffset == ColumnarInvalidLogicalOffset &&\n\t\tstripeMetadata->dataLength == 0;\n\n\t/*\n\t * Even if stripe is flushed, fileOffset and dataLength might be equal\n\t * to 0 for zero column tables, but those two should still be consistent\n\t * with respect to each other.\n\t */\n\tbool stripeLooksFlushed =\n\t\tstripeMetadata->rowCount > 0 && stripeMetadata->chunkCount > 0 &&\n\t\t((stripeMetadata->fileOffset != ColumnarInvalidLogicalOffset &&\n\t\t  stripeMetadata->dataLength > 0) ||\n\t\t (stripeMetadata->fileOffset == ColumnarInvalidLogicalOffset &&\n\t\t  stripeMetadata->dataLength == 0));\n\n\tStripeWriteStateEnum stripeWriteState = StripeWriteState(stripeMetadata);\n\tif (stripeWriteState == STRIPE_WRITE_FLUSHED && stripeLooksFlushed)\n\t{\n\t\t/*\n\t\t * If stripe was flushed to disk, then we expect stripe to store\n\t\t * at least one tuple.\n\t\t */\n\t\treturn;\n\t}\n\telse if (stripeWriteState == STRIPE_WRITE_IN_PROGRESS && stripeLooksInProgress)\n\t{\n\t\t/*\n\t\t * If stripe was not flushed to disk, then values of given four\n\t\t * fields should match the columns inserted by\n\t\t * InsertEmptyStripeMetadataRow.\n\t\t */\n\t\treturn;\n\t}\n\telse if (stripeWriteState == STRIPE_WRITE_ABORTED && (stripeLooksInProgress ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  stripeLooksFlushed))\n\t{\n\t\t/*\n\t\t * Stripe metadata entry for an aborted write can be complete or\n\t\t * incomplete. We might have aborted the transaction before or after\n\t\t * inserting into stripe metadata.\n\t\t */\n\t\treturn;\n\t}\n\n\tereport(ERROR, (errmsg(\"unexpected stripe state, stripe metadata \"\n\t\t\t\t\t\t   \"entry for stripe with id=\" UINT64_FORMAT\n\t\t\t\t\t\t   \" is not consistent\", stripeMetadata->id)));\n}\n\n\n/*\n * FindStripeWithHighestRowNumber returns StripeMetadata for the stripe that\n * has the row with highest rowNumber. If given relation is empty, then returns\n * NULL.\n */\nStripeMetadata *\nFindStripeWithHighestRowNumber(Relation relation, Snapshot snapshot)\n{\n\tStripeMetadata *stripeWithHighestRowNumber = NULL;\n\n\tuint64 storageId = ColumnarStorageGetStorageId(relation, false);\n\tScanKeyData scanKey[1];\n\tScanKeyInit(&scanKey[0], Anum_columnar_stripe_storageid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));\n\n\tRelation columnarStripes = table_open(ColumnarStripeRelationId(), AccessShareLock);\n\n\tOid indexId = ColumnarStripeFirstRowNumberIndexRelationId();\n\tbool indexOk = OidIsValid(indexId);\n\tSysScanDesc scanDescriptor = systable_beginscan(columnarStripes, indexId, indexOk,\n\t\t\t\t\t\t\t\t\t\t\t\t\tsnapshot, 1, scanKey);\n\n\tstatic bool loggedSlowMetadataAccessWarning = false;\n\tif (!indexOk && !loggedSlowMetadataAccessWarning)\n\t{\n\t\tereport(WARNING, (errmsg(SLOW_METADATA_ACCESS_WARNING,\n\t\t\t\t\t\t\t\t \"stripe_first_row_number_idx\")));\n\t\tloggedSlowMetadataAccessWarning = true;\n\t}\n\n\tif (indexOk)\n\t{\n\t\t/* do one-time fetch using the index */\n\t\tHeapTuple heapTuple = systable_getnext_ordered(scanDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   BackwardScanDirection);\n\t\tif (HeapTupleIsValid(heapTuple))\n\t\t{\n\t\t\tstripeWithHighestRowNumber = BuildStripeMetadata(columnarStripes, heapTuple);\n\t\t}\n\t}\n\telse\n\t{\n\t\tHeapTuple heapTuple = NULL;\n\t\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t\t{\n\t\t\tStripeMetadata *stripe = BuildStripeMetadata(columnarStripes, heapTuple);\n\t\t\tif (!stripeWithHighestRowNumber ||\n\t\t\t\tstripe->firstRowNumber > stripeWithHighestRowNumber->firstRowNumber)\n\t\t\t{\n\t\t\t\t/* first or a greater match */\n\t\t\t\tstripeWithHighestRowNumber = stripe;\n\t\t\t}\n\t\t}\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(columnarStripes, AccessShareLock);\n\n\treturn stripeWithHighestRowNumber;\n}\n\n\n/*\n * ReadChunkGroupRowCounts returns an array of row counts of chunk groups for the\n * given stripe.\n */\nstatic uint32 *\nReadChunkGroupRowCounts(uint64 storageId, uint64 stripe, uint32 chunkGroupCount,\n\t\t\t\t\t\tSnapshot snapshot)\n{\n\tOid columnarChunkGroupOid = ColumnarChunkGroupRelationId();\n\tRelation columnarChunkGroup = table_open(columnarChunkGroupOid, AccessShareLock);\n\n\tScanKeyData scanKey[2];\n\tScanKeyInit(&scanKey[0], Anum_columnar_chunkgroup_storageid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));\n\tScanKeyInit(&scanKey[1], Anum_columnar_chunkgroup_stripe,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(stripe));\n\n\tOid indexId = ColumnarChunkGroupIndexRelationId();\n\tbool indexOk = OidIsValid(indexId);\n\tSysScanDesc scanDescriptor =\n\t\tsystable_beginscan(columnarChunkGroup, indexId, indexOk, snapshot, 2, scanKey);\n\n\tstatic bool loggedSlowMetadataAccessWarning = false;\n\tif (!indexOk && !loggedSlowMetadataAccessWarning)\n\t{\n\t\tereport(WARNING, (errmsg(SLOW_METADATA_ACCESS_WARNING, \"chunk_group_pkey\")));\n\t\tloggedSlowMetadataAccessWarning = true;\n\t}\n\n\tHeapTuple heapTuple = NULL;\n\tuint32 *chunkGroupRowCounts = palloc0(chunkGroupCount * sizeof(uint32));\n\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tDatum datumArray[Natts_columnar_chunkgroup];\n\t\tbool isNullArray[Natts_columnar_chunkgroup];\n\n\t\theap_deform_tuple(heapTuple,\n\t\t\t\t\t\t  RelationGetDescr(columnarChunkGroup),\n\t\t\t\t\t\t  datumArray, isNullArray);\n\n\t\tuint32 tupleChunkGroupIndex =\n\t\t\tDatumGetUInt32(datumArray[Anum_columnar_chunkgroup_chunk - 1]);\n\t\tif (tupleChunkGroupIndex >= chunkGroupCount)\n\t\t{\n\t\t\telog(ERROR, \"unexpected chunk group\");\n\t\t}\n\n\t\tchunkGroupRowCounts[tupleChunkGroupIndex] =\n\t\t\t(uint32) DatumGetUInt64(datumArray[Anum_columnar_chunkgroup_row_count - 1]);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(columnarChunkGroup, AccessShareLock);\n\n\treturn chunkGroupRowCounts;\n}\n\n\n/*\n * InsertEmptyStripeMetadataRow adds a row to columnar.stripe for the empty\n * stripe reservation made for stripeId.\n */\nstatic void\nInsertEmptyStripeMetadataRow(uint64 storageId, uint64 stripeId, uint32 columnCount,\n\t\t\t\t\t\t\t uint32 chunkGroupRowCount, uint64 firstRowNumber)\n{\n\tOid columnarStripesOid = ColumnarStripeRelationId();\n\tRelation columnarStripes = table_open(columnarStripesOid, RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(columnarStripes);\n\n\tDatum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *nulls = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tvalues[Anum_columnar_stripe_storageid - 1] =\n\t\tUInt64GetDatum(storageId);\n\tvalues[Anum_columnar_stripe_stripe - 1] =\n\t\tUInt64GetDatum(stripeId);\n\tvalues[Anum_columnar_stripe_column_count - 1] =\n\t\tUInt32GetDatum(columnCount);\n\tvalues[Anum_columnar_stripe_chunk_row_count - 1] =\n\t\tUInt32GetDatum(chunkGroupRowCount);\n\tvalues[GetFirstRowNumberAttrIndexInColumnarStripe(tupleDescriptor)] =\n\t\tUInt64GetDatum(firstRowNumber);\n\n\t/* stripe has no rows yet, so initialize rest of the columns accordingly */\n\tvalues[Anum_columnar_stripe_row_count - 1] =\n\t\tUInt64GetDatum(0);\n\tvalues[Anum_columnar_stripe_file_offset - 1] =\n\t\tUInt64GetDatum(ColumnarInvalidLogicalOffset);\n\tvalues[Anum_columnar_stripe_data_length - 1] =\n\t\tUInt64GetDatum(0);\n\tvalues[Anum_columnar_stripe_chunk_count - 1] =\n\t\tUInt32GetDatum(0);\n\n\tModifyState *modifyState = StartModifyRelation(columnarStripes);\n\n\tInsertTupleAndEnforceConstraints(modifyState, values, nulls);\n\n\tFinishModifyRelation(modifyState);\n\n\ttable_close(columnarStripes, RowExclusiveLock);\n\n\tpfree(values);\n\tpfree(nulls);\n}\n\n\n/*\n * StripesForRelfilelocator returns a list of StripeMetadata for stripes\n * of the given relfilenode.\n */\nList *\nStripesForRelfilelocator(Relation rel)\n{\n\tuint64 storageId = LookupStorageId(RelationPrecomputeOid(rel),\n\t\t\t\t\t\t\t\t\t   rel->rd_locator);\n\n\t/*\n\t * PG18 requires snapshot to be active or registered before it's used\n\t * Without this, we hit\n\t * Assert(snapshot->regd_count > 0 || snapshot->active_count > 0);\n\t * when reading columnar stripes.\n\t * Relevant PG18 commit:\n\t * 8076c00592e40e8dbd1fce7a98b20d4bf075e4ba\n\t */\n\tSnapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());\n\n\tList *readDataFileStripeList = ReadDataFileStripeList(storageId, snapshot);\n\n\tUnregisterSnapshot(snapshot);\n\n\treturn readDataFileStripeList;\n}\n\n\n/*\n * GetHighestUsedAddress returns the highest used address for the given\n * relfilenode across all active and inactive transactions.\n *\n * This is used by truncate stage of VACUUM, and VACUUM can be called\n * for empty tables. So this doesn't throw errors for empty tables and\n * returns 0.\n */\nuint64\nGetHighestUsedAddress(Relation rel)\n{\n\tuint64 storageId = LookupStorageId(RelationPrecomputeOid(rel),\n\t\t\t\t\t\t\t\t\t   rel->rd_locator);\n\n\tuint64 highestUsedAddress = 0;\n\tuint64 highestUsedId = 0;\n\tGetHighestUsedAddressAndId(storageId, &highestUsedAddress, &highestUsedId);\n\n\treturn highestUsedAddress;\n}\n\n\n/*\n * In case if relid hasn't been defined yet, we should use RelidByRelfilenumber\n * to get correct relid value.\n *\n * Now it is basically used for temp rels, because since PG18(it was backpatched\n * through PG13) RelidByRelfilenumber skip temp relations and we should use\n * alternative ways to get relid value in case of temp objects.\n */\nOid\nColumnarRelationId(Oid relid, RelFileLocator relfilelocator)\n{\n\treturn OidIsValid(relid) ? relid : RelidByRelfilenumber(relfilelocator.spcOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trelfilelocator.relNumber);\n}\n\n\n/*\n * GetHighestUsedAddressAndId returns the highest used address and id for\n * the given relfilenode across all active and inactive transactions.\n */\nstatic void\nGetHighestUsedAddressAndId(uint64 storageId,\n\t\t\t\t\t\t   uint64 *highestUsedAddress,\n\t\t\t\t\t\t   uint64 *highestUsedId)\n{\n\tListCell *stripeMetadataCell = NULL;\n\n\tSnapshotData SnapshotDirty;\n\tInitDirtySnapshot(SnapshotDirty);\n\n\tList *stripeMetadataList = ReadDataFileStripeList(storageId, &SnapshotDirty);\n\n\t*highestUsedId = 0;\n\n\t/* file starts with metapage */\n\t*highestUsedAddress = COLUMNAR_BYTES_PER_PAGE;\n\n\tforeach(stripeMetadataCell, stripeMetadataList)\n\t{\n\t\tStripeMetadata *stripe = lfirst(stripeMetadataCell);\n\t\tuint64 lastByte = stripe->fileOffset + stripe->dataLength - 1;\n\t\t*highestUsedAddress = Max(*highestUsedAddress, lastByte);\n\t\t*highestUsedId = Max(*highestUsedId, stripe->id);\n\t}\n}\n\n\n/*\n * ReserveEmptyStripe reserves an empty stripe for given relation\n * and inserts it into columnar.stripe. It is guaranteed that concurrent\n * writes won't overwrite the returned stripe.\n */\nEmptyStripeReservation *\nReserveEmptyStripe(Relation rel, uint64 columnCount, uint64 chunkGroupRowCount,\n\t\t\t\t   uint64 stripeRowCount)\n{\n\tEmptyStripeReservation *stripeReservation = palloc0(sizeof(EmptyStripeReservation));\n\n\tuint64 storageId = ColumnarStorageGetStorageId(rel, false);\n\n\tstripeReservation->stripeId = ColumnarStorageReserveStripeId(rel);\n\tstripeReservation->stripeFirstRowNumber =\n\t\tColumnarStorageReserveRowNumber(rel, stripeRowCount);\n\n\t/*\n\t * XXX: Instead of inserting a dummy entry to columnar.stripe and\n\t * updating it when flushing the stripe, we could have a hash table\n\t * in shared memory for the bookkeeping of ongoing writes.\n\t */\n\tInsertEmptyStripeMetadataRow(storageId, stripeReservation->stripeId,\n\t\t\t\t\t\t\t\t columnCount, chunkGroupRowCount,\n\t\t\t\t\t\t\t\t stripeReservation->stripeFirstRowNumber);\n\n\treturn stripeReservation;\n}\n\n\n/*\n * CompleteStripeReservation completes reservation of the stripe with\n * stripeId for given size and in-place updates related stripe metadata tuple\n * to complete reservation.\n */\nStripeMetadata *\nCompleteStripeReservation(Relation rel, uint64 stripeId, uint64 sizeBytes,\n\t\t\t\t\t\t  uint64 rowCount, uint64 chunkCount)\n{\n\tuint64 resLogicalStart = ColumnarStorageReserveData(rel, sizeBytes);\n\tuint64 storageId = ColumnarStorageGetStorageId(rel, false);\n\n\treturn UpdateStripeMetadataRow(storageId, stripeId, resLogicalStart,\n\t\t\t\t\t\t\t\t   sizeBytes, rowCount, chunkCount);\n}\n\n\n/*\n * UpdateStripeMetadataRow updates stripe metadata tuple for the stripe with\n * stripeId according to given newValues and update arrays.\n * Note that this function shouldn't be used for the cases where any indexes\n * of stripe metadata should be updated according to modifications done.\n */\nstatic StripeMetadata *\nUpdateStripeMetadataRow(uint64 storageId, uint64 stripeId, uint64 fileOffset,\n\t\t\t\t\t\tuint64 dataLength, uint64 rowCount, uint64 chunkCount)\n{\n\tScanKeyData scanKey[2];\n\tScanKeyInit(&scanKey[0], Anum_columnar_stripe_storageid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));\n\tScanKeyInit(&scanKey[1], Anum_columnar_stripe_stripe,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(stripeId));\n\n\tOid columnarStripesOid = ColumnarStripeRelationId();\n\n\tRelation columnarStripes = table_open(columnarStripesOid, AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(columnarStripes);\n\n\tOid indexId = ColumnarStripePKeyIndexRelationId();\n\tbool indexOk = OidIsValid(indexId);\n\n\tvoid *state;\n\tHeapTuple tuple;\n\tsystable_inplace_update_begin(columnarStripes, indexId, indexOk, NULL,\n\t\t\t\t\t\t\t\t  2, scanKey, &tuple, &state);\n\n\tstatic bool loggedSlowMetadataAccessWarning = false;\n\tif (!indexOk && !loggedSlowMetadataAccessWarning)\n\t{\n\t\tereport(WARNING, (errmsg(SLOW_METADATA_ACCESS_WARNING, \"stripe_pkey\")));\n\t\tloggedSlowMetadataAccessWarning = true;\n\t}\n\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"attempted to modify an unexpected stripe, \"\n\t\t\t\t\t\t\t   \"columnar storage with id=\" UINT64_FORMAT\n\t\t\t\t\t\t\t   \" does not have stripe with id=\" UINT64_FORMAT,\n\t\t\t\t\t\t\t   storageId, stripeId)));\n\t}\n\n\t/*\n\t * systable_inplace_update_finish already doesn't allow changing size of the original\n\t * tuple, so we don't allow setting any Datum's to NULL values.\n\t */\n\n\tDatum *newValues = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *newNulls = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\tbool *update = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tupdate[Anum_columnar_stripe_file_offset - 1] = true;\n\tupdate[Anum_columnar_stripe_data_length - 1] = true;\n\tupdate[Anum_columnar_stripe_row_count - 1] = true;\n\tupdate[Anum_columnar_stripe_chunk_count - 1] = true;\n\n\tnewValues[Anum_columnar_stripe_file_offset - 1] = Int64GetDatum(fileOffset);\n\tnewValues[Anum_columnar_stripe_data_length - 1] = Int64GetDatum(dataLength);\n\tnewValues[Anum_columnar_stripe_row_count - 1] = UInt64GetDatum(rowCount);\n\tnewValues[Anum_columnar_stripe_chunk_count - 1] = Int32GetDatum(chunkCount);\n\n\ttuple = heap_modify_tuple(tuple,\n\t\t\t\t\t\t\t  tupleDescriptor,\n\t\t\t\t\t\t\t  newValues,\n\t\t\t\t\t\t\t  newNulls,\n\t\t\t\t\t\t\t  update);\n\n\tsystable_inplace_update_finish(state, tuple);\n\n\tStripeMetadata *modifiedStripeMetadata = BuildStripeMetadata(columnarStripes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t tuple);\n\n\tCommandCounterIncrement();\n\n\theap_freetuple(tuple);\n\ttable_close(columnarStripes, AccessShareLock);\n\n\tpfree(newValues);\n\tpfree(newNulls);\n\tpfree(update);\n\n\t/* return StripeMetadata object built from modified tuple */\n\treturn modifiedStripeMetadata;\n}\n\n\n/*\n * ReadDataFileStripeList reads the stripe list for a given storageId\n * in the given snapshot.\n *\n * Doesn't sort the stripes by their ids before returning if\n * stripe_first_row_number_idx is not available --normally can only happen\n * during pg upgrades.\n */\nstatic List *\nReadDataFileStripeList(uint64 storageId, Snapshot snapshot)\n{\n\tList *stripeMetadataList = NIL;\n\tScanKeyData scanKey[1];\n\tHeapTuple heapTuple;\n\n\tScanKeyInit(&scanKey[0], Anum_columnar_stripe_storageid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(storageId));\n\n\tOid columnarStripesOid = ColumnarStripeRelationId();\n\n\tRelation columnarStripes = table_open(columnarStripesOid, AccessShareLock);\n\n\tOid indexId = ColumnarStripeFirstRowNumberIndexRelationId();\n\tbool indexOk = OidIsValid(indexId);\n\tSysScanDesc scanDescriptor = systable_beginscan(columnarStripes, indexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOk, snapshot, 1, scanKey);\n\n\tstatic bool loggedSlowMetadataAccessWarning = false;\n\tif (!indexOk && !loggedSlowMetadataAccessWarning)\n\t{\n\t\tereport(WARNING, (errmsg(SLOW_METADATA_ACCESS_WARNING,\n\t\t\t\t\t\t\t\t \"stripe_first_row_number_idx\")));\n\t\tloggedSlowMetadataAccessWarning = true;\n\t}\n\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tStripeMetadata *stripeMetadata = BuildStripeMetadata(columnarStripes, heapTuple);\n\t\tstripeMetadataList = lappend(stripeMetadataList, stripeMetadata);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(columnarStripes, AccessShareLock);\n\n\treturn stripeMetadataList;\n}\n\n\n/*\n * BuildStripeMetadata builds a StripeMetadata object from given heap tuple.\n *\n * NB: heapTuple must be a proper heap tuple with MVCC fields.\n */\nstatic StripeMetadata *\nBuildStripeMetadata(Relation columnarStripes, HeapTuple heapTuple)\n{\n\tAssert(RelationGetRelid(columnarStripes) == ColumnarStripeRelationId());\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(columnarStripes);\n\n\tDatum *datumArray = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNullArray = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\n\theap_deform_tuple(heapTuple, tupleDescriptor, datumArray, isNullArray);\n\n\tStripeMetadata *stripeMetadata = palloc0(sizeof(StripeMetadata));\n\tstripeMetadata->id = DatumGetInt64(datumArray[Anum_columnar_stripe_stripe - 1]);\n\tstripeMetadata->fileOffset = DatumGetInt64(\n\t\tdatumArray[Anum_columnar_stripe_file_offset - 1]);\n\tstripeMetadata->dataLength = DatumGetInt64(\n\t\tdatumArray[Anum_columnar_stripe_data_length - 1]);\n\tstripeMetadata->columnCount = DatumGetInt32(\n\t\tdatumArray[Anum_columnar_stripe_column_count - 1]);\n\tstripeMetadata->chunkCount = DatumGetInt32(\n\t\tdatumArray[Anum_columnar_stripe_chunk_count - 1]);\n\tstripeMetadata->chunkGroupRowCount = DatumGetInt32(\n\t\tdatumArray[Anum_columnar_stripe_chunk_row_count - 1]);\n\tstripeMetadata->rowCount = DatumGetInt64(\n\t\tdatumArray[Anum_columnar_stripe_row_count - 1]);\n\tstripeMetadata->firstRowNumber = DatumGetUInt64(\n\t\tdatumArray[GetFirstRowNumberAttrIndexInColumnarStripe(tupleDescriptor)]);\n\n\tpfree(datumArray);\n\tpfree(isNullArray);\n\n\t/*\n\t * If there is unflushed data in a parent transaction, then we would\n\t * have already thrown an error before starting to scan the table.. If\n\t * the data is from an earlier subxact that committed, then it would\n\t * have been flushed already. For this reason, we don't care about\n\t * subtransaction id here.\n\t */\n\tTransactionId entryXmin = HeapTupleHeaderGetXmin(heapTuple->t_data);\n\tstripeMetadata->aborted = !TransactionIdIsInProgress(entryXmin) &&\n\t\t\t\t\t\t\t  TransactionIdDidAbort(entryXmin);\n\tstripeMetadata->insertedByCurrentXact =\n\t\tTransactionIdIsCurrentTransactionId(entryXmin);\n\n\tCheckStripeMetadataConsistency(stripeMetadata);\n\n\treturn stripeMetadata;\n}\n\n\n/*\n * DeleteMetadataRows removes the rows with given relfilenode from columnar\n * metadata tables.\n */\nvoid\nDeleteMetadataRows(Relation rel)\n{\n\t/*\n\t * During a restore for binary upgrade, metadata tables and indexes may or\n\t * may not exist.\n\t */\n\tif (IsBinaryUpgrade)\n\t{\n\t\treturn;\n\t}\n\n\tuint64 storageId = LookupStorageId(RelationPrecomputeOid(rel),\n\t\t\t\t\t\t\t\t\t   rel->rd_locator);\n\n\tDeleteStorageFromColumnarMetadataTable(ColumnarStripeRelationId(),\n\t\t\t\t\t\t\t\t\t\t   Anum_columnar_stripe_storageid,\n\t\t\t\t\t\t\t\t\t\t   ColumnarStripePKeyIndexRelationId(),\n\t\t\t\t\t\t\t\t\t\t   storageId);\n\tDeleteStorageFromColumnarMetadataTable(ColumnarChunkGroupRelationId(),\n\t\t\t\t\t\t\t\t\t\t   Anum_columnar_chunkgroup_storageid,\n\t\t\t\t\t\t\t\t\t\t   ColumnarChunkGroupIndexRelationId(),\n\t\t\t\t\t\t\t\t\t\t   storageId);\n\tDeleteStorageFromColumnarMetadataTable(ColumnarChunkRelationId(),\n\t\t\t\t\t\t\t\t\t\t   Anum_columnar_chunk_storageid,\n\t\t\t\t\t\t\t\t\t\t   ColumnarChunkIndexRelationId(),\n\t\t\t\t\t\t\t\t\t\t   storageId);\n}\n\n\n/*\n * DeleteStorageFromColumnarMetadataTable removes the rows with given\n * storageId from given columnar metadata table.\n */\nstatic void\nDeleteStorageFromColumnarMetadataTable(Oid metadataTableId,\n\t\t\t\t\t\t\t\t\t   AttrNumber storageIdAtrrNumber,\n\t\t\t\t\t\t\t\t\t   Oid storageIdIndexId, uint64 storageId)\n{\n\tScanKeyData scanKey[1];\n\tScanKeyInit(&scanKey[0], storageIdAtrrNumber, BTEqualStrategyNumber,\n\t\t\t\tF_INT8EQ, Int64GetDatum(storageId));\n\n\tRelation metadataTable = try_relation_open(metadataTableId, AccessShareLock);\n\tif (metadataTable == NULL)\n\t{\n\t\t/* extension has been dropped */\n\t\treturn;\n\t}\n\n\tbool indexOk = OidIsValid(storageIdIndexId);\n\tSysScanDesc scanDescriptor = systable_beginscan(metadataTable, storageIdIndexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOk, NULL, 1, scanKey);\n\n\tstatic bool loggedSlowMetadataAccessWarning = false;\n\tif (!indexOk && !loggedSlowMetadataAccessWarning)\n\t{\n\t\tereport(WARNING, (errmsg(SLOW_METADATA_ACCESS_WARNING,\n\t\t\t\t\t\t\t\t \"on a columnar metadata table\")));\n\t\tloggedSlowMetadataAccessWarning = true;\n\t}\n\n\tModifyState *modifyState = StartModifyRelation(metadataTable);\n\n\tHeapTuple heapTuple;\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tDeleteTupleAndEnforceConstraints(modifyState, heapTuple);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\n\tFinishModifyRelation(modifyState);\n\n\ttable_close(metadataTable, AccessShareLock);\n}\n\n\n/*\n * StartModifyRelation allocates resources for modifications.\n */\nstatic ModifyState *\nStartModifyRelation(Relation rel)\n{\n\tEState *estate = create_estate_for_relation(rel);\n\n\tResultRelInfo *resultRelInfo = makeNode(ResultRelInfo);\n\tInitResultRelInfo(resultRelInfo, rel, 1, NULL, 0);\n\n\t/* ExecSimpleRelationInsert, ... require caller to open indexes */\n\tExecOpenIndices(resultRelInfo, false);\n\n\tModifyState *modifyState = palloc(sizeof(ModifyState));\n\tmodifyState->rel = rel;\n\tmodifyState->estate = estate;\n\tmodifyState->resultRelInfo = resultRelInfo;\n\n\treturn modifyState;\n}\n\n\n/*\n * InsertTupleAndEnforceConstraints inserts a tuple into a relation and makes\n * sure constraints are enforced and indexes are updated.\n */\nstatic void\nInsertTupleAndEnforceConstraints(ModifyState *state, Datum *values, bool *nulls)\n{\n\tTupleDesc tupleDescriptor = RelationGetDescr(state->rel);\n\tHeapTuple tuple = heap_form_tuple(tupleDescriptor, values, nulls);\n\n\tTupleTableSlot *slot = ExecInitExtraTupleSlot(state->estate, tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t  &TTSOpsHeapTuple);\n\n\tExecStoreHeapTuple(tuple, slot, false);\n\n\t/* use ExecSimpleRelationInsert to enforce constraints */\n\tExecSimpleRelationInsert(state->resultRelInfo, state->estate, slot);\n}\n\n\n/*\n * DeleteTupleAndEnforceConstraints deletes a tuple from a relation and\n * makes sure constraints (e.g. FK constraints) are enforced.\n */\nstatic void\nDeleteTupleAndEnforceConstraints(ModifyState *state, HeapTuple heapTuple)\n{\n\tEState *estate = state->estate;\n\tResultRelInfo *resultRelInfo = state->resultRelInfo;\n\n\tItemPointer tid = &(heapTuple->t_self);\n\tsimple_heap_delete(state->rel, tid);\n\n\t/* execute AFTER ROW DELETE Triggers to enforce constraints */\n\tExecARDeleteTriggers(estate, resultRelInfo, tid, NULL, NULL, false);\n}\n\n\n/*\n * FinishModifyRelation cleans up resources after modifications are done.\n */\nstatic void\nFinishModifyRelation(ModifyState *state)\n{\n\tExecCloseIndices(state->resultRelInfo);\n\n\tAfterTriggerEndQuery(state->estate);\n\tExecCloseResultRelations(state->estate);\n\tExecCloseRangeTableRelations(state->estate);\n\tExecResetTupleTable(state->estate->es_tupleTable, false);\n\tFreeExecutorState(state->estate);\n\n\tCommandCounterIncrement();\n}\n\n\n/*\n * Based on a similar function from\n * postgres/src/backend/replication/logical/worker.c.\n *\n * Executor state preparation for evaluation of constraint expressions,\n * indexes and triggers.\n *\n * This is based on similar code in copy.c\n */\nstatic EState *\ncreate_estate_for_relation(Relation rel)\n{\n\tEState *estate = CreateExecutorState();\n\n\tRangeTblEntry *rte = makeNode(RangeTblEntry);\n\trte->rtekind = RTE_RELATION;\n\trte->relid = RelationGetRelid(rel);\n\trte->relkind = rel->rd_rel->relkind;\n\trte->rellockmode = AccessShareLock;\n\n/* Prepare permission info on PG 16+ */\n\tList *perminfos = NIL;\n\taddRTEPermissionInfo(&perminfos, rte);\n\n/* Initialize the range table, with the right signature for each PG version */\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t/* PG 18+ needs four arguments (unpruned_relids) */\n\tExecInitRangeTable(\n\t\testate,\n\t\tlist_make1(rte),\n\t\tperminfos,\n\t\tNULL  /* unpruned_relids: not used by columnar */\n\t\t);\n#else\n\n\t/* PG 16–17: three-arg signature (permInfos) */\n\tExecInitRangeTable(\n\t\testate,\n\t\tlist_make1(rte),\n\t\tperminfos\n\t\t);\n#endif\n\n\testate->es_output_cid = GetCurrentCommandId(true);\n\n\t/* Prepare to catch AFTER triggers. */\n\tAfterTriggerBeginQuery();\n\n\treturn estate;\n}\n\n\n/*\n * DatumToBytea serializes a datum into a bytea value.\n *\n * Since we don't want to limit datum size to RSIZE_MAX unnecessarily,\n * we use memcpy instead of memcpy_s several places in this function.\n */\nstatic bytea *\nDatumToBytea(Datum value, Form_pg_attribute attrForm)\n{\n\tint datumLength = att_addlength_datum(0, attrForm->attlen, value);\n\tbytea *result = palloc0(datumLength + VARHDRSZ);\n\n\tSET_VARSIZE(result, datumLength + VARHDRSZ);\n\n\tif (attrForm->attlen > 0)\n\t{\n\t\tif (attrForm->attbyval)\n\t\t{\n\t\t\tDatum tmp;\n\t\t\tstore_att_byval(&tmp, value, attrForm->attlen);\n\n\t\t\tmemcpy(VARDATA(result), &tmp, attrForm->attlen); /* IGNORE-BANNED */\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmemcpy(VARDATA(result), DatumGetPointer(value), attrForm->attlen); /* IGNORE-BANNED */\n\t\t}\n\t}\n\telse\n\t{\n\t\tmemcpy(VARDATA(result), DatumGetPointer(value), datumLength); /* IGNORE-BANNED */\n\t}\n\n\treturn result;\n}\n\n\n/*\n * ByteaToDatum deserializes a value which was previously serialized using\n * DatumToBytea.\n */\nstatic Datum\nByteaToDatum(bytea *bytes, Form_pg_attribute attrForm)\n{\n\t/*\n\t * We copy the data so the result of this function lives even\n\t * after the byteaDatum is freed.\n\t */\n\tchar *binaryDataCopy = palloc0(VARSIZE_ANY_EXHDR(bytes));\n\n\t/*\n\t * We use IGNORE-BANNED here since we don't want to limit datum size to\n\t * RSIZE_MAX unnecessarily.\n\t */\n\tmemcpy(binaryDataCopy, VARDATA_ANY(bytes), VARSIZE_ANY_EXHDR(bytes)); /* IGNORE-BANNED */\n\n\treturn fetch_att(binaryDataCopy, attrForm->attbyval, attrForm->attlen);\n}\n\n\n/*\n * ColumnarStorageIdSequenceRelationId returns relation id of columnar.stripe.\n * TODO: should we cache this similar to citus?\n */\nstatic Oid\nColumnarStorageIdSequenceRelationId(void)\n{\n\treturn get_relname_relid(\"storageid_seq\", ColumnarNamespaceId());\n}\n\n\n/*\n * ColumnarStripeRelationId returns relation id of columnar.stripe.\n * TODO: should we cache this similar to citus?\n */\nstatic Oid\nColumnarStripeRelationId(void)\n{\n\treturn get_relname_relid(\"stripe\", ColumnarNamespaceId());\n}\n\n\n/*\n * ColumnarStripePKeyIndexRelationId returns relation id of columnar.stripe_pkey.\n * TODO: should we cache this similar to citus?\n */\nstatic Oid\nColumnarStripePKeyIndexRelationId(void)\n{\n\treturn get_relname_relid(\"stripe_pkey\", ColumnarNamespaceId());\n}\n\n\n/*\n * ColumnarStripeFirstRowNumberIndexRelationId returns relation id of\n * columnar.stripe_first_row_number_idx.\n * TODO: should we cache this similar to citus?\n */\nstatic Oid\nColumnarStripeFirstRowNumberIndexRelationId(void)\n{\n\treturn get_relname_relid(\"stripe_first_row_number_idx\", ColumnarNamespaceId());\n}\n\n\n/*\n * ColumnarOptionsRelationId returns relation id of columnar.options.\n */\nstatic Oid\nColumnarOptionsRelationId(void)\n{\n\treturn get_relname_relid(\"options\", ColumnarNamespaceId());\n}\n\n\n/*\n * ColumnarOptionsIndexRegclass returns relation id of columnar.options_pkey.\n */\nstatic Oid\nColumnarOptionsIndexRegclass(void)\n{\n\treturn get_relname_relid(\"options_pkey\", ColumnarNamespaceId());\n}\n\n\n/*\n * ColumnarChunkRelationId returns relation id of columnar.chunk.\n * TODO: should we cache this similar to citus?\n */\nstatic Oid\nColumnarChunkRelationId(void)\n{\n\treturn get_relname_relid(\"chunk\", ColumnarNamespaceId());\n}\n\n\n/*\n * ColumnarChunkGroupRelationId returns relation id of columnar.chunk_group.\n * TODO: should we cache this similar to citus?\n */\nstatic Oid\nColumnarChunkGroupRelationId(void)\n{\n\treturn get_relname_relid(\"chunk_group\", ColumnarNamespaceId());\n}\n\n\n/*\n * ColumnarChunkIndexRelationId returns relation id of columnar.chunk_pkey.\n * TODO: should we cache this similar to citus?\n */\nstatic Oid\nColumnarChunkIndexRelationId(void)\n{\n\treturn get_relname_relid(\"chunk_pkey\", ColumnarNamespaceId());\n}\n\n\n/*\n * ColumnarChunkGroupIndexRelationId returns relation id of columnar.chunk_group_pkey.\n * TODO: should we cache this similar to citus?\n */\nstatic Oid\nColumnarChunkGroupIndexRelationId(void)\n{\n\treturn get_relname_relid(\"chunk_group_pkey\", ColumnarNamespaceId());\n}\n\n\n/*\n * ColumnarNamespaceId returns namespace id of the schema we store columnar\n * related tables.\n */\nstatic Oid\nColumnarNamespaceId(void)\n{\n\tOid namespace = get_namespace_oid(\"columnar_internal\", true);\n\n\t/* if schema is earlier than 11.1-1 */\n\tif (!OidIsValid(namespace))\n\t{\n\t\tnamespace = get_namespace_oid(\"columnar\", false);\n\t}\n\n\treturn namespace;\n}\n\n\n/*\n * LookupStorageId reads storage metapage to find the storage ID for the given relfilenode. It returns\n * false if the relation doesn't have a meta page yet.\n */\nstatic uint64\nLookupStorageId(Oid relid, RelFileLocator relfilelocator)\n{\n\trelid = ColumnarRelationId(relid, relfilelocator);\n\n\tRelation relation = relation_open(relid, AccessShareLock);\n\tuint64 storageId = ColumnarStorageGetStorageId(relation, false);\n\ttable_close(relation, AccessShareLock);\n\n\treturn storageId;\n}\n\n\n/*\n * ColumnarMetadataNewStorageId - create a new, unique storage id and return\n * it.\n */\nuint64\nColumnarMetadataNewStorageId()\n{\n\treturn nextval_internal(ColumnarStorageIdSequenceRelationId(), false);\n}\n\n\n/*\n * columnar_relation_storageid returns storage id associated with the\n * given relation id, or -1 if there is no associated storage id yet.\n */\nDatum\ncolumnar_relation_storageid(PG_FUNCTION_ARGS)\n{\n\tOid relationId = PG_GETARG_OID(0);\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\n\tif (!object_ownercheck(RelationRelationId, relationId, GetUserId()))\n\t{\n\t\taclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TABLE,\n\t\t\t\t\t   get_rel_name(relationId));\n\t}\n\n\tif (!IsColumnarTableAmTable(relationId))\n\t{\n\t\telog(ERROR, \"relation \\\"%s\\\" is not a columnar table\",\n\t\t\t RelationGetRelationName(relation));\n\t}\n\n\tuint64 storageId = ColumnarStorageGetStorageId(relation, false);\n\n\trelation_close(relation, AccessShareLock);\n\n\tPG_RETURN_INT64(storageId);\n}\n\n\n/*\n * ColumnarStorageUpdateIfNeeded - upgrade columnar storage to the current version by\n * using information from the metadata tables.\n */\nvoid\nColumnarStorageUpdateIfNeeded(Relation rel, bool isUpgrade)\n{\n\tif (ColumnarStorageIsCurrent(rel))\n\t{\n\t\treturn;\n\t}\n\n\tBlockNumber nblocks = smgrnblocks(RelationGetSmgr(rel), MAIN_FORKNUM);\n\tif (nblocks < 2)\n\t{\n\t\tColumnarStorageInit(RelationGetSmgr(rel), ColumnarMetadataNewStorageId());\n\t\treturn;\n\t}\n\n\tuint64 storageId = ColumnarStorageGetStorageId(rel, true);\n\n\tuint64 highestId;\n\tuint64 highestOffset;\n\tGetHighestUsedAddressAndId(storageId, &highestOffset, &highestId);\n\n\tuint64 reservedStripeId = highestId + 1;\n\tuint64 reservedOffset = highestOffset + 1;\n\tuint64 reservedRowNumber = GetHighestUsedRowNumber(storageId) + 1;\n\tColumnarStorageUpdateCurrent(rel, isUpgrade, reservedStripeId,\n\t\t\t\t\t\t\t\t reservedRowNumber, reservedOffset);\n}\n\n\n/*\n * GetHighestUsedRowNumber returns the highest used rowNumber for given\n * storageId. Returns COLUMNAR_INVALID_ROW_NUMBER if storage with\n * storageId has no stripes.\n * Note that normally we would use ColumnarStorageGetReservedRowNumber\n * to decide that. However, this function is designed to be used when\n * building the metapage itself during upgrades.\n */\nstatic uint64\nGetHighestUsedRowNumber(uint64 storageId)\n{\n\tuint64 highestRowNumber = COLUMNAR_INVALID_ROW_NUMBER;\n\n\tList *stripeMetadataList = ReadDataFileStripeList(storageId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  GetTransactionSnapshot());\n\tStripeMetadata *stripeMetadata = NULL;\n\tforeach_declared_ptr(stripeMetadata, stripeMetadataList)\n\t{\n\t\thighestRowNumber = Max(highestRowNumber,\n\t\t\t\t\t\t\t   StripeGetHighestRowNumber(stripeMetadata));\n\t}\n\n\treturn highestRowNumber;\n}\n\n\n/*\n * GetFirstRowNumberAttrIndexInColumnarStripe returns attrnum for first_row_number attr.\n *\n * first_row_number attr was added to table columnar.stripe using alter operation after\n * the version where Citus started supporting downgrades, and it's only column that we've\n * introduced to columnar.stripe since then.\n *\n * And in case of a downgrade + upgrade, tupleDesc->natts becomes greater than\n * Natts_columnar_stripe and when this happens, then we know that attrnum first_row_number is\n * not Anum_columnar_stripe_first_row_number anymore but tupleDesc->natts - 1.\n */\nstatic int\nGetFirstRowNumberAttrIndexInColumnarStripe(TupleDesc tupleDesc)\n{\n\treturn tupleDesc->natts == Natts_columnar_stripe\n\t\t   ? (Anum_columnar_stripe_first_row_number - 1)\n\t\t   : tupleDesc->natts - 1;\n}\n"
  },
  {
    "path": "src/backend/columnar/columnar_reader.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_reader.c\n *\n * This file contains function definitions for reading columnar tables. This\n * includes the logic for reading file level metadata, reading row stripes,\n * and skipping unrelated row chunks and columns.\n *\n * Copyright (c) 2016, Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include \"postgres.h\"\n\n#include \"safe_lib.h\"\n\n#include \"access/nbtree.h\"\n#include \"access/xact.h\"\n#include \"catalog/pg_am.h\"\n#include \"commands/defrem.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/restrictinfo.h\"\n#include \"storage/fd.h\"\n#include \"utils/guc.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/rel.h\"\n\n#include \"columnar/columnar.h\"\n#include \"columnar/columnar_storage.h\"\n#include \"columnar/columnar_tableam.h\"\n#include \"columnar/columnar_version_compat.h\"\n\n#include \"distributed/listutils.h\"\n\n#define UNEXPECTED_STRIPE_READ_ERR_MSG \\\n\t\t\"attempted to read an unexpected stripe while reading columnar \" \\\n\t\t\"table %s, stripe with id=\" UINT64_FORMAT \" is not flushed\"\n\ntypedef struct ChunkGroupReadState\n{\n\tint64 currentRow;\n\tint64 rowCount;\n\tint columnCount;\n\tList *projectedColumnList;  /* borrowed reference */\n\tChunkData *chunkGroupData;\n} ChunkGroupReadState;\n\ntypedef struct StripeReadState\n{\n\tint columnCount;\n\tint64 rowCount;\n\tint64 currentRow;\n\tTupleDesc tupleDescriptor;\n\tRelation relation;\n\tint chunkGroupIndex;\n\tint64 chunkGroupsFiltered;\n\tMemoryContext stripeReadContext;\n\tStripeBuffers *stripeBuffers;   /* allocated in stripeReadContext */\n\tList *projectedColumnList;      /* borrowed reference */\n\tChunkGroupReadState *chunkGroupReadState; /* owned */\n} StripeReadState;\n\nstruct ColumnarReadState\n{\n\tTupleDesc tupleDescriptor;\n\tRelation relation;\n\n\tStripeMetadata *currentStripeMetadata;\n\tStripeReadState *stripeReadState;\n\n\t/*\n\t * Integer list of attribute numbers (1-indexed) for columns needed by the\n\t * query.\n\t */\n\tList *projectedColumnList;\n\n\tList *whereClauseList;\n\tList *whereClauseVars;\n\n\tMemoryContext stripeReadContext;\n\tint64 chunkGroupsFiltered;\n\n\t/*\n\t * Memory context guaranteed to be not freed during scan so we can\n\t * safely use for any memory allocations regarding ColumnarReadState\n\t * itself.\n\t */\n\tMemoryContext scanContext;\n\n\tSnapshot snapshot;\n\tbool snapshotRegisteredByUs;\n};\n\n/* static function declarations */\nstatic MemoryContext CreateStripeReadMemoryContext(void);\nstatic bool ColumnarReadIsCurrentStripe(ColumnarReadState *readState,\n\t\t\t\t\t\t\t\t\t\tuint64 rowNumber);\nstatic StripeMetadata * ColumnarReadGetCurrentStripe(ColumnarReadState *readState);\nstatic void ReadStripeRowByRowNumber(ColumnarReadState *readState,\n\t\t\t\t\t\t\t\t\t uint64 rowNumber, Datum *columnValues,\n\t\t\t\t\t\t\t\t\t bool *columnNulls);\nstatic bool StripeReadIsCurrentChunkGroup(StripeReadState *stripeReadState,\n\t\t\t\t\t\t\t\t\t\t  int chunkGroupIndex);\nstatic void ReadChunkGroupRowByRowOffset(ChunkGroupReadState *chunkGroupReadState,\n\t\t\t\t\t\t\t\t\t\t StripeMetadata *stripeMetadata,\n\t\t\t\t\t\t\t\t\t\t uint64 stripeRowOffset, Datum *columnValues,\n\t\t\t\t\t\t\t\t\t\t bool *columnNulls);\nstatic bool StripeReadInProgress(ColumnarReadState *readState);\nstatic bool HasUnreadStripe(ColumnarReadState *readState);\nstatic StripeReadState * BeginStripeRead(StripeMetadata *stripeMetadata, Relation rel,\n\t\t\t\t\t\t\t\t\t\t TupleDesc tupleDesc, List *projectedColumnList,\n\t\t\t\t\t\t\t\t\t\t List *whereClauseList, List *whereClauseVars,\n\t\t\t\t\t\t\t\t\t\t MemoryContext stripeReadContext,\n\t\t\t\t\t\t\t\t\t\t Snapshot snapshot);\nstatic void AdvanceStripeRead(ColumnarReadState *readState);\nstatic bool SnapshotMightSeeUnflushedStripes(Snapshot snapshot);\nstatic bool ReadStripeNextRow(StripeReadState *stripeReadState, Datum *columnValues,\n\t\t\t\t\t\t\t  bool *columnNulls);\nstatic ChunkGroupReadState * BeginChunkGroupRead(StripeBuffers *stripeBuffers, int\n\t\t\t\t\t\t\t\t\t\t\t\t chunkIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t TupleDesc tupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t List *projectedColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t MemoryContext cxt);\nstatic void EndChunkGroupRead(ChunkGroupReadState *chunkGroupReadState);\nstatic bool ReadChunkGroupNextRow(ChunkGroupReadState *chunkGroupReadState,\n\t\t\t\t\t\t\t\t  Datum *columnValues,\n\t\t\t\t\t\t\t\t  bool *columnNulls);\nstatic StripeBuffers * LoadFilteredStripeBuffers(Relation relation,\n\t\t\t\t\t\t\t\t\t\t\t\t StripeMetadata *stripeMetadata,\n\t\t\t\t\t\t\t\t\t\t\t\t TupleDesc tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t List *projectedColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t List *whereClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t List *whereClauseVars,\n\t\t\t\t\t\t\t\t\t\t\t\t int64 *chunkGroupsFiltered,\n\t\t\t\t\t\t\t\t\t\t\t\t Snapshot snapshot);\nstatic ColumnBuffers * LoadColumnBuffers(Relation relation,\n\t\t\t\t\t\t\t\t\t\t ColumnChunkSkipNode *chunkSkipNodeArray,\n\t\t\t\t\t\t\t\t\t\t uint32 chunkCount, uint64 stripeOffset,\n\t\t\t\t\t\t\t\t\t\t Form_pg_attribute attributeForm);\nstatic bool * SelectedChunkMask(StripeSkipList *stripeSkipList,\n\t\t\t\t\t\t\t\tList *whereClauseList, List *whereClauseVars,\n\t\t\t\t\t\t\t\tint64 *chunkGroupsFiltered);\nstatic Node * BuildBaseConstraint(Var *variable);\nstatic List * GetClauseVars(List *clauses, int natts);\nstatic OpExpr * MakeOpExpression(Var *variable, int16 strategyNumber);\nstatic Oid GetOperatorByType(Oid typeId, Oid accessMethodId, int16 strategyNumber);\nstatic void UpdateConstraint(Node *baseConstraint, Datum minValue, Datum maxValue);\nstatic StripeSkipList * SelectedChunkSkipList(StripeSkipList *stripeSkipList,\n\t\t\t\t\t\t\t\t\t\t\t  bool *projectedColumnMask,\n\t\t\t\t\t\t\t\t\t\t\t  bool *selectedChunkMask);\nstatic uint32 StripeSkipListRowCount(StripeSkipList *stripeSkipList);\nstatic bool * ProjectedColumnMask(uint32 columnCount, List *projectedColumnList);\nstatic void DeserializeBoolArray(StringInfo boolArrayBuffer, bool *boolArray,\n\t\t\t\t\t\t\t\t uint32 boolArrayLength);\nstatic void DeserializeDatumArray(StringInfo datumBuffer, bool *existsArray,\n\t\t\t\t\t\t\t\t  uint32 datumCount, bool datumTypeByValue,\n\t\t\t\t\t\t\t\t  int datumTypeLength, char datumTypeAlign,\n\t\t\t\t\t\t\t\t  Datum *datumArray);\nstatic ChunkData * DeserializeChunkData(StripeBuffers *stripeBuffers, uint64 chunkIndex,\n\t\t\t\t\t\t\t\t\t\tuint32 rowCount, TupleDesc tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\tList *projectedColumnList);\nstatic Datum ColumnDefaultValue(TupleConstr *tupleConstraints,\n\t\t\t\t\t\t\t\tForm_pg_attribute attributeForm);\n\n/*\n * ColumnarBeginRead initializes a columnar read operation. This function returns a\n * read handle that's used during reading rows and finishing the read operation.\n *\n * projectedColumnList is an integer list of attribute numbers (1-indexed).\n */\nColumnarReadState *\nColumnarBeginRead(Relation relation, TupleDesc tupleDescriptor,\n\t\t\t\t  List *projectedColumnList, List *whereClauseList,\n\t\t\t\t  MemoryContext scanContext, Snapshot snapshot,\n\t\t\t\t  bool randomAccess)\n{\n\t/*\n\t * We allocate all stripe specific data in the stripeReadContext, and reset\n\t * this memory context before loading a new stripe. This is to avoid memory\n\t * leaks.\n\t */\n\tMemoryContext stripeReadContext = CreateStripeReadMemoryContext();\n\n\tColumnarReadState *readState = palloc0(sizeof(ColumnarReadState));\n\treadState->relation = relation;\n\treadState->projectedColumnList = projectedColumnList;\n\treadState->whereClauseList = whereClauseList;\n\treadState->whereClauseVars = GetClauseVars(whereClauseList, tupleDescriptor->natts);\n\treadState->chunkGroupsFiltered = 0;\n\treadState->tupleDescriptor = tupleDescriptor;\n\treadState->stripeReadContext = stripeReadContext;\n\treadState->stripeReadState = NULL;\n\treadState->scanContext = scanContext;\n\n\t/*\n\t * Note that ColumnarReadFlushPendingWrites might update those two by\n\t * registering a new snapshot.\n\t */\n\treadState->snapshot = snapshot;\n\treadState->snapshotRegisteredByUs = false;\n\n\tif (!randomAccess)\n\t{\n\t\t/*\n\t\t * When doing random access (i.e.: index scan), we don't need to flush\n\t\t * pending writes until we need to read them.\n\t\t * columnar_index_fetch_tuple would do so when needed.\n\t\t */\n\t\tColumnarReadFlushPendingWrites(readState);\n\n\t\t/*\n\t\t * AdvanceStripeRead sets currentStripeMetadata for the first stripe\n\t\t * to read if not doing random access. Otherwise, reader (i.e.:\n\t\t * ColumnarReadRowByRowNumber) would already decide the stripe to read\n\t\t * on-the-fly.\n\t\t *\n\t\t * Moreover, Since we don't flush pending writes for random access,\n\t\t * AdvanceStripeRead might encounter with stripe metadata entries due\n\t\t * to current transaction's pending writes even when using an MVCC\n\t\t * snapshot, but AdvanceStripeRead would throw an error for that.\n\t\t * Note that this is not the case with for plain table scan methods\n\t\t * (i.e.: SeqScan and Columnar CustomScan).\n\t\t *\n\t\t * For those reasons, we don't call AdvanceStripeRead if we will do\n\t\t * random access.\n\t\t */\n\t\tAdvanceStripeRead(readState);\n\t}\n\n\treturn readState;\n}\n\n\n/*\n * ColumnarReadFlushPendingWrites flushes pending writes for read operation\n * and sets a new (registered) snapshot if necessary.\n *\n * If it sets a new snapshot, then sets snapshotRegisteredByUs to true to\n * indicate that caller should unregister the snapshot after finishing read\n * operation.\n *\n * Note that this function assumes that readState's relation and snapshot\n * fields are already set.\n */\nvoid\nColumnarReadFlushPendingWrites(ColumnarReadState *readState)\n{\n\tAssert(!readState->snapshotRegisteredByUs);\n\n\tRelFileNumber relfilenumber = readState->relation->rd_locator.relNumber;\n\tFlushWriteStateForRelfilenumber(relfilenumber, GetCurrentSubTransactionId());\n\n\tif (readState->snapshot == InvalidSnapshot || !IsMVCCSnapshot(readState->snapshot))\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * If we flushed any pending writes, then we should guarantee that\n\t * those writes are visible to us too. For this reason, if given\n\t * snapshot is an MVCC snapshot, then we set its curcid to current\n\t * command id.\n\t *\n\t * For simplicity, we do that even if we didn't flush any writes\n\t * since we don't see any problem with that.\n\t *\n\t * XXX: We should either not update cid if we are executing a FETCH\n\t * (from cursor) command, or we should have a better way to deal with\n\t * pending writes, see the discussion in\n\t * https://github.com/citusdata/citus/issues/5231.\n\t */\n\tPushCopiedSnapshot(readState->snapshot);\n\n\t/* now our snapshot is the active one */\n\tUpdateActiveSnapshotCommandId();\n\tSnapshot newSnapshot = GetActiveSnapshot();\n\tRegisterSnapshot(newSnapshot);\n\n\t/*\n\t * To be able to use UpdateActiveSnapshotCommandId, we pushed the\n\t * copied snapshot to the stack. However, we don't need to keep it\n\t * there since we will anyway rely on ColumnarReadState->snapshot\n\t * during read operation.\n\t *\n\t * Note that since we registered the snapshot already, we guarantee\n\t * that PopActiveSnapshot won't free it.\n\t */\n\tPopActiveSnapshot();\n\n\treadState->snapshot = newSnapshot;\n\n\t/* not forget to unregister it when finishing read operation */\n\treadState->snapshotRegisteredByUs = true;\n}\n\n\n/*\n * CreateStripeReadMemoryContext creates a memory context to be used when\n * reading a stripe.\n */\nstatic MemoryContext\nCreateStripeReadMemoryContext()\n{\n\treturn AllocSetContextCreate(CurrentMemoryContext, \"Stripe Read Memory Context\",\n\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_SIZES);\n}\n\n\n/*\n * ColumnarReadNextRow tries to read a row from the columnar table. On success, it sets\n * column values, column nulls and rowNumber (if passed to be non-NULL), and returns true.\n * If there are no more rows to read, the function returns false.\n */\nbool\nColumnarReadNextRow(ColumnarReadState *readState, Datum *columnValues, bool *columnNulls,\n\t\t\t\t\tuint64 *rowNumber)\n{\n\twhile (true)\n\t{\n\t\tif (!StripeReadInProgress(readState))\n\t\t{\n\t\t\tif (!HasUnreadStripe(readState))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treadState->stripeReadState = BeginStripeRead(readState->currentStripeMetadata,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t readState->relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t readState->tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t readState->projectedColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t readState->whereClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t readState->whereClauseVars,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t readState->stripeReadContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t readState->snapshot);\n\t\t}\n\n\t\tif (!ReadStripeNextRow(readState->stripeReadState, columnValues, columnNulls))\n\t\t{\n\t\t\tAdvanceStripeRead(readState);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (rowNumber)\n\t\t{\n\t\t\t*rowNumber = readState->currentStripeMetadata->firstRowNumber +\n\t\t\t\t\t\t readState->stripeReadState->currentRow - 1;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ColumnarReadRowByRowNumberOrError is a wrapper around\n * ColumnarReadRowByRowNumber that throws an error if tuple\n * with rowNumber does not exist.\n */\nvoid\nColumnarReadRowByRowNumberOrError(ColumnarReadState *readState,\n\t\t\t\t\t\t\t\t  uint64 rowNumber, Datum *columnValues,\n\t\t\t\t\t\t\t\t  bool *columnNulls)\n{\n\tif (!ColumnarReadRowByRowNumber(readState, rowNumber,\n\t\t\t\t\t\t\t\t\tcolumnValues, columnNulls))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot read from columnar table %s, tuple with \"\n\t\t\t\t\t\t\t   \"row number \" UINT64_FORMAT \" does not exist\",\n\t\t\t\t\t\t\t   RelationGetRelationName(readState->relation),\n\t\t\t\t\t\t\t   rowNumber)));\n\t}\n}\n\n\n/*\n * ColumnarReadRowByRowNumber reads row with rowNumber from given relation\n * into columnValues and columnNulls, and returns true. If no such row\n * exists, then returns false.\n */\nbool\nColumnarReadRowByRowNumber(ColumnarReadState *readState,\n\t\t\t\t\t\t   uint64 rowNumber, Datum *columnValues,\n\t\t\t\t\t\t   bool *columnNulls)\n{\n\tif (!ColumnarReadIsCurrentStripe(readState, rowNumber))\n\t{\n\t\tRelation columnarRelation = readState->relation;\n\t\tSnapshot snapshot = readState->snapshot;\n\t\tStripeMetadata *stripeMetadata = FindStripeByRowNumber(columnarRelation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   rowNumber, snapshot);\n\t\tif (stripeMetadata == NULL)\n\t\t{\n\t\t\t/* no such row exists */\n\t\t\treturn false;\n\t\t}\n\n\t\tif (StripeWriteState(stripeMetadata) != STRIPE_WRITE_FLUSHED)\n\t\t{\n\t\t\t/*\n\t\t\t * Callers are expected to skip stripes that are not flushed to\n\t\t\t * disk yet or should wait for the writer xact to commit or abort,\n\t\t\t * but let's be on the safe side.\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(UNEXPECTED_STRIPE_READ_ERR_MSG,\n\t\t\t\t\t\t\t\t   RelationGetRelationName(columnarRelation),\n\t\t\t\t\t\t\t\t   stripeMetadata->id)));\n\t\t}\n\n\t\t/* do the cleanup before reading a new stripe */\n\t\tColumnarResetRead(readState);\n\n\t\tTupleDesc relationTupleDesc = RelationGetDescr(columnarRelation);\n\t\tList *whereClauseList = NIL;\n\t\tList *whereClauseVars = NIL;\n\t\tMemoryContext stripeReadContext = readState->stripeReadContext;\n\t\treadState->stripeReadState = BeginStripeRead(stripeMetadata,\n\t\t\t\t\t\t\t\t\t\t\t\t\t columnarRelation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t relationTupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t readState->projectedColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t whereClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t whereClauseVars,\n\t\t\t\t\t\t\t\t\t\t\t\t\t stripeReadContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t snapshot);\n\n\t\treadState->currentStripeMetadata = stripeMetadata;\n\t}\n\n\tReadStripeRowByRowNumber(readState, rowNumber, columnValues, columnNulls);\n\n\treturn true;\n}\n\n\n/*\n * ColumnarReadIsCurrentStripe returns true if stripe being read contains\n * row with given rowNumber.\n */\nstatic bool\nColumnarReadIsCurrentStripe(ColumnarReadState *readState, uint64 rowNumber)\n{\n\tif (!StripeReadInProgress(readState))\n\t{\n\t\treturn false;\n\t}\n\n\tStripeMetadata *currentStripeMetadata = readState->currentStripeMetadata;\n\tif (rowNumber >= currentStripeMetadata->firstRowNumber &&\n\t\trowNumber <= StripeGetHighestRowNumber(currentStripeMetadata))\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ColumnarReadGetCurrentStripe returns StripeMetadata for the stripe that is\n * being read.\n */\nstatic StripeMetadata *\nColumnarReadGetCurrentStripe(ColumnarReadState *readState)\n{\n\treturn readState->currentStripeMetadata;\n}\n\n\n/*\n * ReadStripeRowByRowNumber reads row with rowNumber from given\n * stripeReadState into columnValues and columnNulls.\n * Errors out if no such row exists in the stripe being read.\n */\nstatic void\nReadStripeRowByRowNumber(ColumnarReadState *readState,\n\t\t\t\t\t\t uint64 rowNumber, Datum *columnValues,\n\t\t\t\t\t\t bool *columnNulls)\n{\n\tStripeMetadata *stripeMetadata = ColumnarReadGetCurrentStripe(readState);\n\tStripeReadState *stripeReadState = readState->stripeReadState;\n\n\tif (rowNumber < stripeMetadata->firstRowNumber)\n\t{\n\t\t/* not expected but be on the safe side */\n\t\tereport(ERROR, (errmsg(\"row offset cannot be negative\")));\n\t}\n\n\t/* find the exact chunk group to be read */\n\tuint64 stripeRowOffset = rowNumber - stripeMetadata->firstRowNumber;\n\tint chunkGroupIndex = stripeRowOffset / stripeMetadata->chunkGroupRowCount;\n\tif (!StripeReadIsCurrentChunkGroup(stripeReadState, chunkGroupIndex))\n\t{\n\t\tif (stripeReadState->chunkGroupReadState)\n\t\t{\n\t\t\tEndChunkGroupRead(stripeReadState->chunkGroupReadState);\n\t\t}\n\n\t\tstripeReadState->chunkGroupIndex = chunkGroupIndex;\n\t\tstripeReadState->chunkGroupReadState = BeginChunkGroupRead(\n\t\t\tstripeReadState->stripeBuffers,\n\t\t\tstripeReadState->chunkGroupIndex,\n\t\t\tstripeReadState->tupleDescriptor,\n\t\t\tstripeReadState->projectedColumnList,\n\t\t\tstripeReadState->stripeReadContext);\n\t}\n\n\tReadChunkGroupRowByRowOffset(stripeReadState->chunkGroupReadState,\n\t\t\t\t\t\t\t\t stripeMetadata, stripeRowOffset,\n\t\t\t\t\t\t\t\t columnValues, columnNulls);\n}\n\n\n/*\n * StripeReadIsCurrentChunkGroup returns true if chunk group being read is\n * the has given chunkGroupIndex in its stripe.\n */\nstatic bool\nStripeReadIsCurrentChunkGroup(StripeReadState *stripeReadState, int chunkGroupIndex)\n{\n\tif (!stripeReadState->chunkGroupReadState)\n\t{\n\t\treturn false;\n\t}\n\n\treturn (stripeReadState->chunkGroupIndex == chunkGroupIndex);\n}\n\n\n/*\n * ReadChunkGroupRowByRowOffset reads row with stripeRowOffset from given\n * chunkGroupReadState into columnValues and columnNulls.\n * Errors out if no such row exists in the chunk group being read.\n */\nstatic void\nReadChunkGroupRowByRowOffset(ChunkGroupReadState *chunkGroupReadState,\n\t\t\t\t\t\t\t StripeMetadata *stripeMetadata,\n\t\t\t\t\t\t\t uint64 stripeRowOffset, Datum *columnValues,\n\t\t\t\t\t\t\t bool *columnNulls)\n{\n\t/* set the exact row number to be read from given chunk roup */\n\tchunkGroupReadState->currentRow = stripeRowOffset %\n\t\t\t\t\t\t\t\t\t  stripeMetadata->chunkGroupRowCount;\n\tif (!ReadChunkGroupNextRow(chunkGroupReadState, columnValues, columnNulls))\n\t{\n\t\t/* not expected but be on the safe side */\n\t\tereport(ERROR, (errmsg(\"could not find the row in stripe\")));\n\t}\n}\n\n\n/*\n * StripeReadInProgress returns true if we already started reading a stripe.\n */\nstatic bool\nStripeReadInProgress(ColumnarReadState *readState)\n{\n\treturn readState->stripeReadState != NULL;\n}\n\n\n/*\n * HasUnreadStripe returns true if we still have stripes to read during current\n * read operation.\n */\nstatic bool\nHasUnreadStripe(ColumnarReadState *readState)\n{\n\treturn readState->currentStripeMetadata != NULL;\n}\n\n\n/*\n * ColumnarRescan clears the position where we were scanning so that the next read starts at\n * the beginning again\n */\nvoid\nColumnarRescan(ColumnarReadState *readState, List *scanQual)\n{\n\tMemoryContext oldContext = MemoryContextSwitchTo(readState->scanContext);\n\n\tColumnarResetRead(readState);\n\n\t/* set currentStripeMetadata for the first stripe to read */\n\tAdvanceStripeRead(readState);\n\n\treadState->chunkGroupsFiltered = 0;\n\n\treadState->whereClauseList = copyObject(scanQual);\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * Finishes a columnar read operation.\n */\nvoid\nColumnarEndRead(ColumnarReadState *readState)\n{\n\tif (readState->snapshotRegisteredByUs)\n\t{\n\t\t/*\n\t\t * init_columnar_read_state created a new snapshot and registered it,\n\t\t * so now forget it.\n\t\t */\n\t\tUnregisterSnapshot(readState->snapshot);\n\t}\n\n\tMemoryContextDelete(readState->stripeReadContext);\n\tif (readState->currentStripeMetadata)\n\t{\n\t\tpfree(readState->currentStripeMetadata);\n\t}\n\n\tpfree(readState);\n}\n\n\n/*\n * ColumnarResetRead resets the stripe and the chunk group that is\n * being read currently (if any).\n */\nvoid\nColumnarResetRead(ColumnarReadState *readState)\n{\n\tif (StripeReadInProgress(readState))\n\t{\n\t\tpfree(readState->currentStripeMetadata);\n\t\treadState->currentStripeMetadata = NULL;\n\n\t\treadState->stripeReadState = NULL;\n\t\tMemoryContextReset(readState->stripeReadContext);\n\t}\n}\n\n\n/*\n * BeginStripeRead allocates state for reading a stripe.\n */\nstatic StripeReadState *\nBeginStripeRead(StripeMetadata *stripeMetadata, Relation rel, TupleDesc tupleDesc,\n\t\t\t\tList *projectedColumnList, List *whereClauseList, List *whereClauseVars,\n\t\t\t\tMemoryContext stripeReadContext, Snapshot snapshot)\n{\n\tMemoryContext oldContext = MemoryContextSwitchTo(stripeReadContext);\n\n\tStripeReadState *stripeReadState = palloc0(sizeof(StripeReadState));\n\n\tstripeReadState->relation = rel;\n\tstripeReadState->tupleDescriptor = tupleDesc;\n\tstripeReadState->columnCount = tupleDesc->natts;\n\tstripeReadState->chunkGroupReadState = NULL;\n\tstripeReadState->projectedColumnList = projectedColumnList;\n\tstripeReadState->stripeReadContext = stripeReadContext;\n\n\tstripeReadState->stripeBuffers = LoadFilteredStripeBuffers(rel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   stripeMetadata,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   tupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   projectedColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   whereClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   whereClauseVars,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &stripeReadState->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   chunkGroupsFiltered,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   snapshot);\n\n\tstripeReadState->rowCount = stripeReadState->stripeBuffers->rowCount;\n\n\tMemoryContextSwitchTo(oldContext);\n\n\n\treturn stripeReadState;\n}\n\n\n/*\n * AdvanceStripeRead updates chunkGroupsFiltered and sets\n * currentStripeMetadata for next stripe read.\n */\nstatic void\nAdvanceStripeRead(ColumnarReadState *readState)\n{\n\tMemoryContext oldContext = MemoryContextSwitchTo(readState->scanContext);\n\n\t/* if not read any stripes yet, start from the first one .. */\n\tuint64 lastReadRowNumber = COLUMNAR_INVALID_ROW_NUMBER;\n\tif (StripeReadInProgress(readState))\n\t{\n\t\t/* .. otherwise, continue with the next stripe */\n\t\tlastReadRowNumber = StripeGetHighestRowNumber(readState->currentStripeMetadata);\n\n\t\treadState->chunkGroupsFiltered +=\n\t\t\treadState->stripeReadState->chunkGroupsFiltered;\n\t}\n\n\treadState->currentStripeMetadata = FindNextStripeByRowNumber(readState->relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t lastReadRowNumber,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t readState->snapshot);\n\n\tif (readState->currentStripeMetadata &&\n\t\tStripeWriteState(readState->currentStripeMetadata) != STRIPE_WRITE_FLUSHED &&\n\t\t!SnapshotMightSeeUnflushedStripes(readState->snapshot))\n\t{\n\t\t/*\n\t\t * To be on the safe side, error out if we don't expect to encounter\n\t\t * with an un-flushed stripe. Otherwise, we will skip such stripes\n\t\t * until finding a flushed one.\n\t\t */\n\t\tereport(ERROR, (errmsg(UNEXPECTED_STRIPE_READ_ERR_MSG,\n\t\t\t\t\t\t\t   RelationGetRelationName(readState->relation),\n\t\t\t\t\t\t\t   readState->currentStripeMetadata->id)));\n\t}\n\n\twhile (readState->currentStripeMetadata &&\n\t\t   StripeWriteState(readState->currentStripeMetadata) != STRIPE_WRITE_FLUSHED)\n\t{\n\t\treadState->currentStripeMetadata =\n\t\t\tFindNextStripeByRowNumber(readState->relation,\n\t\t\t\t\t\t\t\t\t  readState->currentStripeMetadata->firstRowNumber,\n\t\t\t\t\t\t\t\t\t  readState->snapshot);\n\t}\n\n\treadState->stripeReadState = NULL;\n\tMemoryContextReset(readState->stripeReadContext);\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * SnapshotMightSeeUnflushedStripes returns true if given snapshot is\n * expected to see un-flushed stripes either because of other backends'\n * pending writes or aborted transactions.\n */\nstatic bool\nSnapshotMightSeeUnflushedStripes(Snapshot snapshot)\n{\n\tif (snapshot == InvalidSnapshot)\n\t{\n\t\treturn false;\n\t}\n\n\tswitch (snapshot->snapshot_type)\n\t{\n\t\tcase SNAPSHOT_ANY:\n\t\tcase SNAPSHOT_DIRTY:\n\t\tcase SNAPSHOT_NON_VACUUMABLE:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\n\n/*\n * ReadStripeNextRow: If more rows can be read from the current stripe, fill\n * in non-NULL columnValues and return true. Otherwise, return false.\n *\n * On entry, all entries in columnNulls should be true; this function only\n * sets non-NULL entries.\n *\n */\nstatic bool\nReadStripeNextRow(StripeReadState *stripeReadState, Datum *columnValues,\n\t\t\t\t  bool *columnNulls)\n{\n\tif (stripeReadState->currentRow >= stripeReadState->rowCount)\n\t{\n\t\tAssert(stripeReadState->currentRow == stripeReadState->rowCount);\n\t\treturn false;\n\t}\n\n\twhile (true)\n\t{\n\t\tif (stripeReadState->chunkGroupReadState == NULL)\n\t\t{\n\t\t\tstripeReadState->chunkGroupReadState = BeginChunkGroupRead(\n\t\t\t\tstripeReadState->stripeBuffers,\n\t\t\t\tstripeReadState->\n\t\t\t\tchunkGroupIndex,\n\t\t\t\tstripeReadState->\n\t\t\t\ttupleDescriptor,\n\t\t\t\tstripeReadState->\n\t\t\t\tprojectedColumnList,\n\t\t\t\tstripeReadState->\n\t\t\t\tstripeReadContext);\n\t\t}\n\n\t\tif (!ReadChunkGroupNextRow(stripeReadState->chunkGroupReadState, columnValues,\n\t\t\t\t\t\t\t\t   columnNulls))\n\t\t{\n\t\t\t/* if this chunk group is exhausted, fetch the next one and loop */\n\t\t\tEndChunkGroupRead(stripeReadState->chunkGroupReadState);\n\t\t\tstripeReadState->chunkGroupReadState = NULL;\n\t\t\tstripeReadState->chunkGroupIndex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tstripeReadState->currentRow++;\n\t\treturn true;\n\t}\n\n\tAssert(stripeReadState->currentRow == stripeReadState->rowCount);\n\treturn false;\n}\n\n\n/*\n * BeginChunkGroupRead allocates state for reading a chunk.\n */\nstatic ChunkGroupReadState *\nBeginChunkGroupRead(StripeBuffers *stripeBuffers, int chunkIndex, TupleDesc tupleDesc,\n\t\t\t\t\tList *projectedColumnList, MemoryContext cxt)\n{\n\tuint32 chunkGroupRowCount =\n\t\tstripeBuffers->selectedChunkGroupRowCounts[chunkIndex];\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(cxt);\n\n\tChunkGroupReadState *chunkGroupReadState = palloc0(sizeof(ChunkGroupReadState));\n\n\tchunkGroupReadState->currentRow = 0;\n\tchunkGroupReadState->rowCount = chunkGroupRowCount;\n\tchunkGroupReadState->columnCount = tupleDesc->natts;\n\tchunkGroupReadState->projectedColumnList = projectedColumnList;\n\n\tchunkGroupReadState->chunkGroupData = DeserializeChunkData(stripeBuffers, chunkIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   chunkGroupRowCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   tupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   projectedColumnList);\n\tMemoryContextSwitchTo(oldContext);\n\n\treturn chunkGroupReadState;\n}\n\n\n/*\n * EndChunkRead finishes a chunk read.\n */\nstatic void\nEndChunkGroupRead(ChunkGroupReadState *chunkGroupReadState)\n{\n\tFreeChunkData(chunkGroupReadState->chunkGroupData);\n\tpfree(chunkGroupReadState);\n}\n\n\n/*\n * ReadChunkGroupNextRow: if more rows can be read from the current chunk\n * group, fill in non-NULL columnValues and return true. Otherwise, return\n * false.\n *\n * On entry, all entries in columnNulls should be true; this function only\n * sets non-NULL entries.\n */\nstatic bool\nReadChunkGroupNextRow(ChunkGroupReadState *chunkGroupReadState, Datum *columnValues,\n\t\t\t\t\t  bool *columnNulls)\n{\n\tif (chunkGroupReadState->currentRow >= chunkGroupReadState->rowCount)\n\t{\n\t\tAssert(chunkGroupReadState->currentRow == chunkGroupReadState->rowCount);\n\t\treturn false;\n\t}\n\n\t/*\n\t * Initialize to all-NULL. Only non-NULL projected attributes will be set.\n\t */\n\tmemset(columnNulls, true, sizeof(bool) * chunkGroupReadState->columnCount);\n\n\tint attno;\n\tforeach_declared_int(attno, chunkGroupReadState->projectedColumnList)\n\t{\n\t\tconst ChunkData *chunkGroupData = chunkGroupReadState->chunkGroupData;\n\t\tconst int rowIndex = chunkGroupReadState->currentRow;\n\n\t\t/* attno is 1-indexed; existsArray is 0-indexed */\n\t\tconst uint32 columnIndex = attno - 1;\n\n\t\tif (chunkGroupData->existsArray[columnIndex][rowIndex])\n\t\t{\n\t\t\tcolumnValues[columnIndex] = chunkGroupData->valueArray[columnIndex][rowIndex];\n\t\t\tcolumnNulls[columnIndex] = false;\n\t\t}\n\t}\n\n\tchunkGroupReadState->currentRow++;\n\treturn true;\n}\n\n\n/*\n * ColumnarReadChunkGroupsFiltered\n *\n * Return the number of chunk groups filtered during this read operation.\n */\nint64\nColumnarReadChunkGroupsFiltered(ColumnarReadState *state)\n{\n\treturn state->chunkGroupsFiltered;\n}\n\n\n/*\n * CreateEmptyChunkDataArray creates data buffers to keep deserialized exist and\n * value arrays for requested columns in columnMask.\n */\nChunkData *\nCreateEmptyChunkData(uint32 columnCount, bool *columnMask, uint32 chunkGroupRowCount)\n{\n\tuint32 columnIndex = 0;\n\n\tChunkData *chunkData = palloc0(sizeof(ChunkData));\n\tchunkData->existsArray = palloc0(columnCount * sizeof(bool *));\n\tchunkData->valueArray = palloc0(columnCount * sizeof(Datum *));\n\tchunkData->valueBufferArray = palloc0(columnCount * sizeof(StringInfo));\n\tchunkData->columnCount = columnCount;\n\tchunkData->rowCount = chunkGroupRowCount;\n\n\t/* allocate chunk memory for deserialized data */\n\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tif (columnMask[columnIndex])\n\t\t{\n\t\t\tchunkData->existsArray[columnIndex] = palloc0(chunkGroupRowCount *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  sizeof(bool));\n\t\t\tchunkData->valueArray[columnIndex] = palloc0(chunkGroupRowCount *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t sizeof(Datum));\n\t\t\tchunkData->valueBufferArray[columnIndex] = NULL;\n\t\t}\n\t}\n\n\treturn chunkData;\n}\n\n\n/*\n * FreeChunkData deallocates data buffers to keep deserialized exist and\n * value arrays for requested columns in columnMask.\n * ColumnChunkData->serializedValueBuffer lives in memory read/write context\n * so it is deallocated automatically when the context is deleted.\n */\nvoid\nFreeChunkData(ChunkData *chunkData)\n{\n\tuint32 columnIndex = 0;\n\n\tif (chunkData == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tfor (columnIndex = 0; columnIndex < chunkData->columnCount; columnIndex++)\n\t{\n\t\tif (chunkData->existsArray[columnIndex] != NULL)\n\t\t{\n\t\t\tpfree(chunkData->existsArray[columnIndex]);\n\t\t}\n\n\t\tif (chunkData->valueArray[columnIndex] != NULL)\n\t\t{\n\t\t\tpfree(chunkData->valueArray[columnIndex]);\n\t\t}\n\t}\n\n\tpfree(chunkData->existsArray);\n\tpfree(chunkData->valueArray);\n\tpfree(chunkData);\n}\n\n\n/* ColumnarTableRowCount returns the exact row count of a table using skiplists */\nuint64\nColumnarTableRowCount(Relation relation)\n{\n\tListCell *stripeMetadataCell = NULL;\n\tuint64 totalRowCount = 0;\n\tList *stripeList = StripesForRelfilelocator(relation);\n\n\tforeach(stripeMetadataCell, stripeList)\n\t{\n\t\tStripeMetadata *stripeMetadata = (StripeMetadata *) lfirst(stripeMetadataCell);\n\t\ttotalRowCount += stripeMetadata->rowCount;\n\t}\n\n\treturn totalRowCount;\n}\n\n\n/*\n * LoadFilteredStripeBuffers reads serialized stripe data from the given file.\n * The function skips over chunks whose rows are refuted by restriction qualifiers,\n * and only loads columns that are projected in the query.\n */\nstatic StripeBuffers *\nLoadFilteredStripeBuffers(Relation relation, StripeMetadata *stripeMetadata,\n\t\t\t\t\t\t  TupleDesc tupleDescriptor, List *projectedColumnList,\n\t\t\t\t\t\t  List *whereClauseList, List *whereClauseVars,\n\t\t\t\t\t\t  int64 *chunkGroupsFiltered, Snapshot snapshot)\n{\n\tuint32 columnIndex = 0;\n\tuint32 columnCount = tupleDescriptor->natts;\n\n\tbool *projectedColumnMask = ProjectedColumnMask(columnCount, projectedColumnList);\n\n\tStripeSkipList *stripeSkipList = ReadStripeSkipList(relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstripeMetadata->id,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstripeMetadata->chunkCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsnapshot);\n\n\tbool *selectedChunkMask = SelectedChunkMask(stripeSkipList, whereClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\twhereClauseVars, chunkGroupsFiltered);\n\n\tStripeSkipList *selectedChunkSkipList =\n\t\tSelectedChunkSkipList(stripeSkipList, projectedColumnMask,\n\t\t\t\t\t\t\t  selectedChunkMask);\n\n\t/* load column data for projected columns */\n\tColumnBuffers **columnBuffersArray = palloc0(columnCount * sizeof(ColumnBuffers *));\n\n\tfor (columnIndex = 0; columnIndex < stripeMetadata->columnCount; columnIndex++)\n\t{\n\t\tif (projectedColumnMask[columnIndex])\n\t\t{\n\t\t\tColumnChunkSkipNode *chunkSkipNode =\n\t\t\t\tselectedChunkSkipList->chunkSkipNodeArray[columnIndex];\n\t\t\tForm_pg_attribute attributeForm = TupleDescAttr(tupleDescriptor, columnIndex);\n\t\t\tuint32 chunkCount = selectedChunkSkipList->chunkCount;\n\n\t\t\tColumnBuffers *columnBuffers = LoadColumnBuffers(relation, chunkSkipNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t chunkCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t stripeMetadata->fileOffset,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t attributeForm);\n\n\t\t\tcolumnBuffersArray[columnIndex] = columnBuffers;\n\t\t}\n\t}\n\n\tStripeBuffers *stripeBuffers = palloc0(sizeof(StripeBuffers));\n\tstripeBuffers->columnCount = columnCount;\n\tstripeBuffers->rowCount = StripeSkipListRowCount(selectedChunkSkipList);\n\tstripeBuffers->columnBuffersArray = columnBuffersArray;\n\tstripeBuffers->selectedChunkGroupRowCounts =\n\t\tselectedChunkSkipList->chunkGroupRowCounts;\n\n\treturn stripeBuffers;\n}\n\n\n/*\n * LoadColumnBuffers reads serialized column data from the given file. These\n * column data are laid out as sequential chunks in the file; and chunk positions\n * and lengths are retrieved from the column chunk skip node array.\n */\nstatic ColumnBuffers *\nLoadColumnBuffers(Relation relation, ColumnChunkSkipNode *chunkSkipNodeArray,\n\t\t\t\t  uint32 chunkCount, uint64 stripeOffset,\n\t\t\t\t  Form_pg_attribute attributeForm)\n{\n\tuint32 chunkIndex = 0;\n\tColumnChunkBuffers **chunkBuffersArray =\n\t\tpalloc0(chunkCount * sizeof(ColumnChunkBuffers *));\n\n\tfor (chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++)\n\t{\n\t\tchunkBuffersArray[chunkIndex] = palloc0(sizeof(ColumnChunkBuffers));\n\t}\n\n\t/*\n\t * We first read the \"exists\" chunks. We don't read \"values\" array here,\n\t * because \"exists\" chunks are stored sequentially on disk, and we want to\n\t * minimize disk seeks.\n\t */\n\tfor (chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++)\n\t{\n\t\tColumnChunkSkipNode *chunkSkipNode = &chunkSkipNodeArray[chunkIndex];\n\t\tuint64 existsOffset = stripeOffset + chunkSkipNode->existsChunkOffset;\n\t\tStringInfo rawExistsBuffer = makeStringInfo();\n\n\t\tenlargeStringInfo(rawExistsBuffer, chunkSkipNode->existsLength);\n\t\trawExistsBuffer->len = chunkSkipNode->existsLength;\n\t\tColumnarStorageRead(relation, existsOffset, rawExistsBuffer->data,\n\t\t\t\t\t\t\tchunkSkipNode->existsLength);\n\n\t\tchunkBuffersArray[chunkIndex]->existsBuffer = rawExistsBuffer;\n\t}\n\n\t/* then read \"values\" chunks, which are also stored sequentially on disk */\n\tfor (chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++)\n\t{\n\t\tColumnChunkSkipNode *chunkSkipNode = &chunkSkipNodeArray[chunkIndex];\n\t\tCompressionType compressionType = chunkSkipNode->valueCompressionType;\n\t\tuint64 valueOffset = stripeOffset + chunkSkipNode->valueChunkOffset;\n\t\tStringInfo rawValueBuffer = makeStringInfo();\n\n\t\tenlargeStringInfo(rawValueBuffer, chunkSkipNode->valueLength);\n\t\trawValueBuffer->len = chunkSkipNode->valueLength;\n\t\tColumnarStorageRead(relation, valueOffset, rawValueBuffer->data,\n\t\t\t\t\t\t\tchunkSkipNode->valueLength);\n\n\t\tchunkBuffersArray[chunkIndex]->valueBuffer = rawValueBuffer;\n\t\tchunkBuffersArray[chunkIndex]->valueCompressionType = compressionType;\n\t\tchunkBuffersArray[chunkIndex]->decompressedValueSize =\n\t\t\tchunkSkipNode->decompressedValueSize;\n\t}\n\n\tColumnBuffers *columnBuffers = palloc0(sizeof(ColumnBuffers));\n\tcolumnBuffers->chunkBuffersArray = chunkBuffersArray;\n\n\treturn columnBuffers;\n}\n\n\n/*\n * SelectedChunkMask walks over each column's chunks and checks if a chunk can\n * be filtered without reading its data. The filtering happens when all rows in\n * the chunk can be refuted by the given qualifier conditions.\n */\nstatic bool *\nSelectedChunkMask(StripeSkipList *stripeSkipList, List *whereClauseList,\n\t\t\t\t  List *whereClauseVars, int64 *chunkGroupsFiltered)\n{\n\tListCell *columnCell = NULL;\n\tuint32 chunkIndex = 0;\n\n\tbool *selectedChunkMask = palloc0(stripeSkipList->chunkCount * sizeof(bool));\n\tmemset(selectedChunkMask, true, stripeSkipList->chunkCount * sizeof(bool));\n\n\tforeach(columnCell, whereClauseVars)\n\t{\n\t\tVar *column = lfirst(columnCell);\n\t\tuint32 columnIndex = column->varattno - 1;\n\n\t\t/* if this column's data type doesn't have a comparator, skip it */\n\t\tFmgrInfo *comparisonFunction = GetFunctionInfoOrNull(column->vartype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t BTREE_AM_OID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t BTORDER_PROC);\n\t\tif (comparisonFunction == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tNode *baseConstraint = BuildBaseConstraint(column);\n\t\tfor (chunkIndex = 0; chunkIndex < stripeSkipList->chunkCount; chunkIndex++)\n\t\t{\n\t\t\tColumnChunkSkipNode *chunkSkipNodeArray =\n\t\t\t\tstripeSkipList->chunkSkipNodeArray[columnIndex];\n\t\t\tColumnChunkSkipNode *chunkSkipNode = &chunkSkipNodeArray[chunkIndex];\n\n\t\t\t/*\n\t\t\t * A column chunk with comparable data type can miss min/max values\n\t\t\t * if all values in the chunk are NULL.\n\t\t\t */\n\t\t\tif (!chunkSkipNode->hasMinMax)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tUpdateConstraint(baseConstraint, chunkSkipNode->minimumValue,\n\t\t\t\t\t\t\t chunkSkipNode->maximumValue);\n\n\t\t\tList *constraintList = list_make1(baseConstraint);\n\t\t\tbool predicateRefuted =\n\t\t\t\tpredicate_refuted_by(constraintList, whereClauseList, false);\n\t\t\tif (predicateRefuted && selectedChunkMask[chunkIndex])\n\t\t\t{\n\t\t\t\tselectedChunkMask[chunkIndex] = false;\n\t\t\t\t*chunkGroupsFiltered += 1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn selectedChunkMask;\n}\n\n\n/*\n * GetFunctionInfoOrNull first resolves the operator for the given data type,\n * access method, and support procedure. The function then uses the resolved\n * operator's identifier to fill in a function manager object, and returns\n * this object. This function is based on a similar function from CitusDB's code.\n */\nFmgrInfo *\nGetFunctionInfoOrNull(Oid typeId, Oid accessMethodId, int16 procedureId)\n{\n\tFmgrInfo *functionInfo = NULL;\n\n\t/* get default operator class from pg_opclass for datum type */\n\tOid operatorClassId = GetDefaultOpClass(typeId, accessMethodId);\n\tif (operatorClassId == InvalidOid)\n\t{\n\t\treturn NULL;\n\t}\n\n\tOid operatorFamilyId = get_opclass_family(operatorClassId);\n\tif (operatorFamilyId == InvalidOid)\n\t{\n\t\treturn NULL;\n\t}\n\n\tOid operatorId = get_opfamily_proc(operatorFamilyId, typeId, typeId, procedureId);\n\tif (operatorId != InvalidOid)\n\t{\n\t\tfunctionInfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo));\n\n\t\t/* fill in the FmgrInfo struct using the operatorId */\n\t\tfmgr_info(operatorId, functionInfo);\n\t}\n\n\treturn functionInfo;\n}\n\n\n/*\n * BuildBaseConstraint builds and returns a base constraint. This constraint\n * implements an expression in the form of (var <= max && var >= min), where\n * min and max values represent a chunk's min and max values. These chunk\n * values are filled in after the constraint is built. This function is based\n * on a similar function from CitusDB's shard pruning logic.\n */\nstatic Node *\nBuildBaseConstraint(Var *variable)\n{\n\tOpExpr *lessThanExpr = MakeOpExpression(variable, BTLessEqualStrategyNumber);\n\tOpExpr *greaterThanExpr = MakeOpExpression(variable, BTGreaterEqualStrategyNumber);\n\n\tNode *baseConstraint = make_and_qual((Node *) lessThanExpr, (Node *) greaterThanExpr);\n\n\treturn baseConstraint;\n}\n\n\n/*\n * GetClauseVars extracts the Vars from the given clauses for the purpose of\n * building constraints that can be refuted by predicate_refuted_by(). It also\n * deduplicates and sorts them.\n */\nstatic List *\nGetClauseVars(List *whereClauseList, int natts)\n{\n\t/*\n\t * We don't recurse into or include aggregates, window functions, or\n\t * PHVs. We don't expect any PHVs during execution; and Vars found inside\n\t * an aggregate or window function aren't going to be useful in forming\n\t * constraints that can be refuted.\n\t */\n\tint flags = 0;\n\tList *vars = pull_var_clause((Node *) whereClauseList, flags);\n\tVar **deduplicate = palloc0(sizeof(Var *) * natts);\n\n\tListCell *lc;\n\tforeach(lc, vars)\n\t{\n\t\tNode *node = lfirst(lc);\n\t\tAssert(IsA(node, Var));\n\n\t\tVar *var = (Var *) node;\n\t\tint idx = var->varattno - 1;\n\n\t\tif (deduplicate[idx] != NULL)\n\t\t{\n\t\t\t/* if they have the same varattno, the rest should be identical */\n\t\t\tAssert(equal(var, deduplicate[idx]));\n\t\t}\n\n\t\tdeduplicate[idx] = var;\n\t}\n\n\tList *whereClauseVars = NIL;\n\tfor (int i = 0; i < natts; i++)\n\t{\n\t\tVar *var = deduplicate[i];\n\t\tif (var != NULL)\n\t\t{\n\t\t\twhereClauseVars = lappend(whereClauseVars, var);\n\t\t}\n\t}\n\n\tpfree(deduplicate);\n\n\treturn whereClauseVars;\n}\n\n\n/*\n * MakeOpExpression builds an operator expression node. This operator expression\n * implements the operator clause as defined by the variable and the strategy\n * number. The function is copied from CitusDB's shard pruning logic.\n */\nstatic OpExpr *\nMakeOpExpression(Var *variable, int16 strategyNumber)\n{\n\tOid typeId = variable->vartype;\n\tOid typeModId = variable->vartypmod;\n\tOid collationId = variable->varcollid;\n\n\tOid accessMethodId = BTREE_AM_OID;\n\n\t/* Load the operator from system catalogs */\n\tOid operatorId = GetOperatorByType(typeId, accessMethodId, strategyNumber);\n\n\tConst *constantValue = makeNullConst(typeId, typeModId, collationId);\n\n\t/* Now make the expression with the given variable and a null constant */\n\tOpExpr *expression = (OpExpr *) make_opclause(operatorId,\n\t\t\t\t\t\t\t\t\t\t\t\t  InvalidOid, /* no result type yet */\n\t\t\t\t\t\t\t\t\t\t\t\t  false, /* no return set */\n\t\t\t\t\t\t\t\t\t\t\t\t  (Expr *) variable,\n\t\t\t\t\t\t\t\t\t\t\t\t  (Expr *) constantValue,\n\t\t\t\t\t\t\t\t\t\t\t\t  InvalidOid, collationId);\n\n\t/* Set implementing function id and result type */\n\texpression->opfuncid = get_opcode(operatorId);\n\texpression->opresulttype = get_func_rettype(expression->opfuncid);\n\n\treturn expression;\n}\n\n\n/*\n * GetOperatorByType returns operator Oid for the given type, access method,\n * and strategy number. Note that this function incorrectly errors out when\n * the given type doesn't have its own operator but can use another compatible\n * type's default operator. The function is copied from CitusDB's shard pruning\n * logic.\n */\nstatic Oid\nGetOperatorByType(Oid typeId, Oid accessMethodId, int16 strategyNumber)\n{\n\t/* Get default operator class from pg_opclass */\n\tOid operatorClassId = GetDefaultOpClass(typeId, accessMethodId);\n\n\tOid operatorFamily = get_opclass_family(operatorClassId);\n\n\tOid operatorId = get_opfamily_member(operatorFamily, typeId, typeId, strategyNumber);\n\n\treturn operatorId;\n}\n\n\n/*\n * UpdateConstraint updates the base constraint with the given min/max values.\n * The function is copied from CitusDB's shard pruning logic.\n */\nstatic void\nUpdateConstraint(Node *baseConstraint, Datum minValue, Datum maxValue)\n{\n\tBoolExpr *andExpr = (BoolExpr *) baseConstraint;\n\tNode *lessThanExpr = (Node *) linitial(andExpr->args);\n\tNode *greaterThanExpr = (Node *) lsecond(andExpr->args);\n\n\tNode *minNode = get_rightop((Expr *) greaterThanExpr);\n\tNode *maxNode = get_rightop((Expr *) lessThanExpr);\n\n\tAssert(IsA(minNode, Const));\n\tAssert(IsA(maxNode, Const));\n\n\tConst *minConstant = (Const *) minNode;\n\tConst *maxConstant = (Const *) maxNode;\n\n\tminConstant->constvalue = minValue;\n\tmaxConstant->constvalue = maxValue;\n\n\tminConstant->constisnull = false;\n\tmaxConstant->constisnull = false;\n\n\tminConstant->constbyval = true;\n\tmaxConstant->constbyval = true;\n}\n\n\n/*\n * SelectedChunkSkipList constructs a new StripeSkipList in which the\n * non-selected chunks are removed from the given stripeSkipList.\n */\nstatic StripeSkipList *\nSelectedChunkSkipList(StripeSkipList *stripeSkipList, bool *projectedColumnMask,\n\t\t\t\t\t  bool *selectedChunkMask)\n{\n\tuint32 selectedChunkCount = 0;\n\tuint32 chunkIndex = 0;\n\tuint32 columnIndex = 0;\n\tuint32 columnCount = stripeSkipList->columnCount;\n\tuint32 selectedChunkIndex = 0;\n\n\tfor (chunkIndex = 0; chunkIndex < stripeSkipList->chunkCount; chunkIndex++)\n\t{\n\t\tif (selectedChunkMask[chunkIndex])\n\t\t{\n\t\t\tselectedChunkCount++;\n\t\t}\n\t}\n\n\tColumnChunkSkipNode **selectedChunkSkipNodeArray =\n\t\tpalloc0(columnCount * sizeof(ColumnChunkSkipNode *));\n\n\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tbool firstColumn = columnIndex == 0;\n\t\tselectedChunkIndex = 0;\n\n\t\t/* first column's chunk skip node is always read */\n\t\tif (!projectedColumnMask[columnIndex] && !firstColumn)\n\t\t{\n\t\t\tselectedChunkSkipNodeArray[columnIndex] = NULL;\n\t\t\tcontinue;\n\t\t}\n\n\t\tAssert(stripeSkipList->chunkSkipNodeArray[columnIndex] != NULL);\n\n\t\tselectedChunkSkipNodeArray[columnIndex] = palloc0(selectedChunkCount *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  sizeof(ColumnChunkSkipNode));\n\n\t\tfor (chunkIndex = 0; chunkIndex < stripeSkipList->chunkCount; chunkIndex++)\n\t\t{\n\t\t\tif (selectedChunkMask[chunkIndex])\n\t\t\t{\n\t\t\t\tselectedChunkSkipNodeArray[columnIndex][selectedChunkIndex] =\n\t\t\t\t\tstripeSkipList->chunkSkipNodeArray[columnIndex][chunkIndex];\n\t\t\t\tselectedChunkIndex++;\n\t\t\t}\n\t\t}\n\t}\n\n\tselectedChunkIndex = 0;\n\tuint32 *chunkGroupRowCounts = palloc0(selectedChunkCount * sizeof(uint32));\n\tfor (chunkIndex = 0; chunkIndex < stripeSkipList->chunkCount; chunkIndex++)\n\t{\n\t\tif (selectedChunkMask[chunkIndex])\n\t\t{\n\t\t\tchunkGroupRowCounts[selectedChunkIndex++] =\n\t\t\t\tstripeSkipList->chunkGroupRowCounts[chunkIndex];\n\t\t}\n\t}\n\n\tStripeSkipList *selectedChunkSkipList = palloc0(sizeof(StripeSkipList));\n\tselectedChunkSkipList->chunkSkipNodeArray = selectedChunkSkipNodeArray;\n\tselectedChunkSkipList->chunkCount = selectedChunkCount;\n\tselectedChunkSkipList->columnCount = stripeSkipList->columnCount;\n\tselectedChunkSkipList->chunkGroupRowCounts = chunkGroupRowCounts;\n\n\treturn selectedChunkSkipList;\n}\n\n\n/*\n * StripeSkipListRowCount counts the number of rows in the given stripeSkipList.\n * To do this, the function finds the first column, and sums up row counts across\n * all chunks for that column.\n */\nstatic uint32\nStripeSkipListRowCount(StripeSkipList *stripeSkipList)\n{\n\tuint32 stripeSkipListRowCount = 0;\n\tuint32 chunkIndex = 0;\n\tuint32 *chunkGroupRowCounts = stripeSkipList->chunkGroupRowCounts;\n\n\tfor (chunkIndex = 0; chunkIndex < stripeSkipList->chunkCount; chunkIndex++)\n\t{\n\t\tuint32 chunkGroupRowCount = chunkGroupRowCounts[chunkIndex];\n\t\tstripeSkipListRowCount += chunkGroupRowCount;\n\t}\n\n\treturn stripeSkipListRowCount;\n}\n\n\n/*\n * ProjectedColumnMask returns a boolean array in which the projected columns\n * from the projected column list are marked as true.\n */\nstatic bool *\nProjectedColumnMask(uint32 columnCount, List *projectedColumnList)\n{\n\tbool *projectedColumnMask = palloc0(columnCount * sizeof(bool));\n\tint attno;\n\n\tforeach_declared_int(attno, projectedColumnList)\n\t{\n\t\t/* attno is 1-indexed; projectedColumnMask is 0-indexed */\n\t\tint columnIndex = attno - 1;\n\t\tprojectedColumnMask[columnIndex] = true;\n\t}\n\n\treturn projectedColumnMask;\n}\n\n\n/*\n * DeserializeBoolArray reads an array of bits from the given buffer and stores\n * it in provided bool array.\n */\nstatic void\nDeserializeBoolArray(StringInfo boolArrayBuffer, bool *boolArray,\n\t\t\t\t\t uint32 boolArrayLength)\n{\n\tuint32 boolArrayIndex = 0;\n\n\tuint32 maximumBoolCount = boolArrayBuffer->len * 8;\n\tif (boolArrayLength > maximumBoolCount)\n\t{\n\t\tereport(ERROR, (errmsg(\"insufficient data for reading boolean array\")));\n\t}\n\n\tfor (boolArrayIndex = 0; boolArrayIndex < boolArrayLength; boolArrayIndex++)\n\t{\n\t\tuint32 byteIndex = boolArrayIndex / 8;\n\t\tuint32 bitIndex = boolArrayIndex % 8;\n\t\tuint8 bitmask = (1 << bitIndex);\n\n\t\tuint8 shiftedBit = (boolArrayBuffer->data[byteIndex] & bitmask);\n\t\tif (shiftedBit == 0)\n\t\t{\n\t\t\tboolArray[boolArrayIndex] = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tboolArray[boolArrayIndex] = true;\n\t\t}\n\t}\n}\n\n\n/*\n * DeserializeDatumArray reads an array of datums from the given buffer and stores\n * them in provided datumArray. If a value is marked as false in the exists array,\n * the function assumes that the datum isn't in the buffer, and simply skips it.\n */\nstatic void\nDeserializeDatumArray(StringInfo datumBuffer, bool *existsArray, uint32 datumCount,\n\t\t\t\t\t  bool datumTypeByValue, int datumTypeLength,\n\t\t\t\t\t  char datumTypeAlign, Datum *datumArray)\n{\n\tuint32 datumIndex = 0;\n\tuint32 currentDatumDataOffset = 0;\n\n\tfor (datumIndex = 0; datumIndex < datumCount; datumIndex++)\n\t{\n\t\tif (!existsArray[datumIndex])\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tchar *currentDatumDataPointer = datumBuffer->data + currentDatumDataOffset;\n\n\t\tdatumArray[datumIndex] = fetch_att(currentDatumDataPointer, datumTypeByValue,\n\t\t\t\t\t\t\t\t\t\t   datumTypeLength);\n\t\tcurrentDatumDataOffset = att_addlength_datum(currentDatumDataOffset,\n\t\t\t\t\t\t\t\t\t\t\t\t\t datumTypeLength,\n\t\t\t\t\t\t\t\t\t\t\t\t\t datumArray[datumIndex]);\n\t\tcurrentDatumDataOffset = att_align_nominal(currentDatumDataOffset,\n\t\t\t\t\t\t\t\t\t\t\t\t   datumTypeAlign);\n\n\t\tif (currentDatumDataOffset > datumBuffer->len)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"insufficient data left in datum buffer\")));\n\t\t}\n\t}\n}\n\n\n/*\n * DeserializeChunkGroupData deserializes requested data chunk for all columns and\n * stores in chunkDataArray. It uncompresses serialized data if necessary. The\n * function also deallocates data buffers used for previous chunk, and compressed\n * data buffers for the current chunk which will not be needed again. If a column\n * data is not present serialized buffer, then default value (or null) is used\n * to fill value array.\n */\nstatic ChunkData *\nDeserializeChunkData(StripeBuffers *stripeBuffers, uint64 chunkIndex,\n\t\t\t\t\t uint32 rowCount, TupleDesc tupleDescriptor,\n\t\t\t\t\t List *projectedColumnList)\n{\n\tint columnIndex = 0;\n\tbool *columnMask = ProjectedColumnMask(tupleDescriptor->natts, projectedColumnList);\n\tChunkData *chunkData = CreateEmptyChunkData(tupleDescriptor->natts, columnMask,\n\t\t\t\t\t\t\t\t\t\t\t\trowCount);\n\n\tfor (columnIndex = 0; columnIndex < stripeBuffers->columnCount; columnIndex++)\n\t{\n\t\tForm_pg_attribute attributeForm = TupleDescAttr(tupleDescriptor, columnIndex);\n\t\tColumnBuffers *columnBuffers = stripeBuffers->columnBuffersArray[columnIndex];\n\t\tbool columnAdded = false;\n\n\t\tif (columnBuffers == NULL && columnMask[columnIndex])\n\t\t{\n\t\t\tcolumnAdded = true;\n\t\t}\n\n\t\tif (columnBuffers != NULL)\n\t\t{\n\t\t\tColumnChunkBuffers *chunkBuffers =\n\t\t\t\tcolumnBuffers->chunkBuffersArray[chunkIndex];\n\n\t\t\t/* decompress and deserialize current chunk's data */\n\t\t\tStringInfo valueBuffer =\n\t\t\t\tDecompressBuffer(chunkBuffers->valueBuffer,\n\t\t\t\t\t\t\t\t chunkBuffers->valueCompressionType,\n\t\t\t\t\t\t\t\t chunkBuffers->decompressedValueSize);\n\n\t\t\tDeserializeBoolArray(chunkBuffers->existsBuffer,\n\t\t\t\t\t\t\t\t chunkData->existsArray[columnIndex],\n\t\t\t\t\t\t\t\t rowCount);\n\t\t\tDeserializeDatumArray(valueBuffer, chunkData->existsArray[columnIndex],\n\t\t\t\t\t\t\t\t  rowCount, attributeForm->attbyval,\n\t\t\t\t\t\t\t\t  attributeForm->attlen, attributeForm->attalign,\n\t\t\t\t\t\t\t\t  chunkData->valueArray[columnIndex]);\n\n\t\t\t/* store current chunk's data buffer to be freed at next chunk read */\n\t\t\tchunkData->valueBufferArray[columnIndex] = valueBuffer;\n\t\t}\n\t\telse if (columnAdded)\n\t\t{\n\t\t\t/*\n\t\t\t * This is a column that was added after creation of this stripe.\n\t\t\t * So we use either the default value or NULL.\n\t\t\t */\n\t\t\tif (attributeForm->atthasdef)\n\t\t\t{\n\t\t\t\tint rowIndex = 0;\n\n\t\t\t\tDatum defaultValue = ColumnDefaultValue(tupleDescriptor->constr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tattributeForm);\n\n\t\t\t\tfor (rowIndex = 0; rowIndex < rowCount; rowIndex++)\n\t\t\t\t{\n\t\t\t\t\tchunkData->existsArray[columnIndex][rowIndex] = true;\n\t\t\t\t\tchunkData->valueArray[columnIndex][rowIndex] = defaultValue;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tmemset(chunkData->existsArray[columnIndex], false,\n\t\t\t\t\t   rowCount * sizeof(bool));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn chunkData;\n}\n\n\n/*\n * ColumnDefaultValue returns default value for given column. Only const values\n * are supported. The function errors on any other default value expressions.\n */\nstatic Datum\nColumnDefaultValue(TupleConstr *tupleConstraints, Form_pg_attribute attributeForm)\n{\n\tNode *defaultValueNode = NULL;\n\tint defValIndex = 0;\n\n\tfor (defValIndex = 0; defValIndex < tupleConstraints->num_defval; defValIndex++)\n\t{\n\t\tAttrDefault attrDefault = tupleConstraints->defval[defValIndex];\n\t\tif (attrDefault.adnum == attributeForm->attnum)\n\t\t{\n\t\t\tdefaultValueNode = stringToNode(attrDefault.adbin);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tAssert(defaultValueNode != NULL);\n\n\t/* try reducing the default value node to a const node */\n\tdefaultValueNode = eval_const_expressions(NULL, defaultValueNode);\n\tif (IsA(defaultValueNode, Const))\n\t{\n\t\tConst *constNode = (Const *) defaultValueNode;\n\t\treturn constNode->constvalue;\n\t}\n\telse\n\t{\n\t\tconst char *columnName = NameStr(attributeForm->attname);\n\t\tereport(ERROR, (errmsg(\"unsupported default value for column \\\"%s\\\"\", columnName),\n\t\t\t\t\t\terrhint(\"Expression is either mutable or \"\n\t\t\t\t\t\t\t\t\"does not evaluate to constant value\")));\n\t}\n}\n"
  },
  {
    "path": "src/backend/columnar/columnar_storage.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_storage.c\n *\n * Copyright (c) Citus Data, Inc.\n *\n * Low-level storage layer for columnar.\n *   - Translates columnar read/write operations on logical offsets into operations on pages/blocks.\n *   - Emits WAL.\n *   - Reads/writes the columnar metapage.\n *   - Reserves data offsets, stripe numbers, and row offsets.\n *   - Truncation.\n *\n * Higher-level columnar operations deal with logical offsets and large\n * contiguous buffers of data that need to be stored. But the buffer manager\n * and WAL depend on formatted pages with headers, so these large buffers need\n * to be written across many pages. This module translates the contiguous\n * buffers into individual block reads/writes, and performs WAL when\n * necessary.\n *\n * Storage layout: a metapage in block 0, followed by an empty page in block\n * 1, followed by logical data starting at the first byte after the page\n * header in block 2 (having logical offset ColumnarFirstLogicalOffset). (XXX:\n * Block 1 is left empty for no particular reason. Reconsider?). A columnar\n * table should always have at least 2 blocks.\n *\n * Reservation is done with a relation extension lock, and designed for\n * concurrency, so the callers only need an ordinary lock on the\n * relation. Initializing the metapage or truncating the relation require that\n * the caller holds an AccessExclusiveLock. (XXX: New reservations of data are\n * aligned onto a new page for no particular reason. Reconsider?).\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n#include \"safe_lib.h\"\n\n#include \"access/generic_xlog.h\"\n#include \"catalog/storage.h\"\n#include \"storage/bufmgr.h\"\n#include \"storage/lmgr.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"columnar/columnar.h\"\n#include \"columnar/columnar_storage.h\"\n\n\n/*\n * Content of the first page in main fork, which stores metadata at file\n * level.\n */\ntypedef struct ColumnarMetapage\n{\n\t/*\n\t * Store version of file format used, so we can detect files from\n\t * previous versions if we change file format.\n\t */\n\tuint32 versionMajor;\n\tuint32 versionMinor;\n\n\t/*\n\t * Each of the metadata table rows are identified by a storageId.\n\t * We store it also in the main fork so we can link metadata rows\n\t * with data files.\n\t */\n\tuint64 storageId;\n\n\tuint64 reservedStripeId; /* first unused stripe id */\n\tuint64 reservedRowNumber; /* first unused row number */\n\tuint64 reservedOffset; /* first unused byte offset */\n\n\t/*\n\t * Flag set to true in the init fork. After an unlogged table reset (due\n\t * to a crash), the init fork will be copied over the main fork. When\n\t * trying to read an unlogged table, if this flag is set to true, we must\n\t * clear the metadata for the table (because the actual data is gone,\n\t * too), and clear the flag. We can cross-check that the table is\n\t * UNLOGGED, and that the main fork is at the minimum size (no actual\n\t * data).\n\t *\n\t * XXX: Not used yet; reserved field for later support for UNLOGGED.\n\t */\n\tbool unloggedReset;\n} ColumnarMetapage;\n\n\n/* represents a \"physical\" block+offset address */\ntypedef struct PhysicalAddr\n{\n\tBlockNumber blockno;\n\tuint32 offset;\n} PhysicalAddr;\n\n\n#define COLUMNAR_METAPAGE_BLOCKNO 0\n#define COLUMNAR_EMPTY_BLOCKNO 1\n#define COLUMNAR_INVALID_STRIPE_ID 0\n#define COLUMNAR_FIRST_STRIPE_ID 1\n\n\n#define OLD_METAPAGE_VERSION_HINT \"Use \\\"VACUUM\\\" to upgrade the columnar table format \" \\\n\t\t\t\t\t\t\t\t  \"version or run \\\"ALTER EXTENSION citus UPDATE\\\".\"\n\n\n/* only for testing purposes */\nPG_FUNCTION_INFO_V1(test_columnar_storage_write_new_page);\n\n\n/*\n * Map logical offsets to a physical page and offset where the data is kept.\n */\nstatic inline PhysicalAddr\nLogicalToPhysical(uint64 logicalOffset)\n{\n\tPhysicalAddr addr;\n\n\taddr.blockno = logicalOffset / COLUMNAR_BYTES_PER_PAGE;\n\taddr.offset = SizeOfPageHeaderData + (logicalOffset % COLUMNAR_BYTES_PER_PAGE);\n\n\treturn addr;\n}\n\n\n/*\n * Map a physical page and offset address to a logical address.\n */\nstatic inline uint64\nPhysicalToLogical(PhysicalAddr addr)\n{\n\treturn COLUMNAR_BYTES_PER_PAGE * addr.blockno + addr.offset - SizeOfPageHeaderData;\n}\n\n\nstatic void ColumnarOverwriteMetapage(Relation relation,\n\t\t\t\t\t\t\t\t\t  ColumnarMetapage columnarMetapage);\nstatic ColumnarMetapage ColumnarMetapageRead(Relation rel, bool force);\nstatic void ReadFromBlock(Relation rel, BlockNumber blockno, uint32 offset,\n\t\t\t\t\t\t  char *buf, uint32 len, bool force);\nstatic void WriteToBlock(Relation rel, BlockNumber blockno, uint32 offset,\n\t\t\t\t\t\t char *buf, uint32 len, bool clear);\nstatic uint64 AlignReservation(uint64 prevReservation);\nstatic bool ColumnarMetapageIsCurrent(ColumnarMetapage *metapage);\nstatic bool ColumnarMetapageIsOlder(ColumnarMetapage *metapage);\nstatic bool ColumnarMetapageIsNewer(ColumnarMetapage *metapage);\nstatic void ColumnarMetapageCheckVersion(Relation rel, ColumnarMetapage *metapage);\n\n\n/*\n * ColumnarStorageInit - initialize a new metapage in an empty relation\n * with the given storageId.\n *\n * Caller must hold AccessExclusiveLock on the relation.\n */\nvoid\nColumnarStorageInit(SMgrRelation srel, uint64 storageId)\n{\n\tBlockNumber nblocks = smgrnblocks(srel, MAIN_FORKNUM);\n\n\tif (nblocks > 0)\n\t{\n\t\telog(ERROR,\n\t\t\t \"attempted to initialize metapage, but %d pages already exist\",\n\t\t\t nblocks);\n\t}\n\n\t/* create two pages */\n\tPGIOAlignedBlock block;\n\tPage page = block.data;\n\n\t/* write metapage */\n\tPageInit(page, BLCKSZ, 0);\n\tPageHeader phdr = (PageHeader) page;\n\n\tColumnarMetapage metapage = { 0 };\n\tmetapage.storageId = storageId;\n\tmetapage.versionMajor = COLUMNAR_VERSION_MAJOR;\n\tmetapage.versionMinor = COLUMNAR_VERSION_MINOR;\n\tmetapage.reservedStripeId = COLUMNAR_FIRST_STRIPE_ID;\n\tmetapage.reservedRowNumber = COLUMNAR_FIRST_ROW_NUMBER;\n\tmetapage.reservedOffset = ColumnarFirstLogicalOffset;\n\tmetapage.unloggedReset = false;\n\tmemcpy_s(page + phdr->pd_lower, phdr->pd_upper - phdr->pd_lower,\n\t\t\t (char *) &metapage, sizeof(ColumnarMetapage));\n\tphdr->pd_lower += sizeof(ColumnarMetapage);\n\n\tlog_newpage(&srel->smgr_rlocator.locator, MAIN_FORKNUM,\n\t\t\t\tCOLUMNAR_METAPAGE_BLOCKNO, page, true);\n\tPageSetChecksumInplace(page, COLUMNAR_METAPAGE_BLOCKNO);\n\tsmgrextend(srel, MAIN_FORKNUM, COLUMNAR_METAPAGE_BLOCKNO, page, true);\n\n\t/* write empty page */\n\tPageInit(page, BLCKSZ, 0);\n\n\tlog_newpage(&srel->smgr_rlocator.locator, MAIN_FORKNUM,\n\t\t\t\tCOLUMNAR_EMPTY_BLOCKNO, page, true);\n\tPageSetChecksumInplace(page, COLUMNAR_EMPTY_BLOCKNO);\n\tsmgrextend(srel, MAIN_FORKNUM, COLUMNAR_EMPTY_BLOCKNO, page, true);\n\n\t/*\n\t * An immediate sync is required even if we xlog'd the page, because the\n\t * write did not go through shared_buffers and therefore a concurrent\n\t * checkpoint may have moved the redo pointer past our xlog record.\n\t */\n\tsmgrimmedsync(srel, MAIN_FORKNUM);\n}\n\n\n/*\n * ColumnarStorageUpdateCurrent - update the metapage to the current\n * version. No effect if the version already matches. If 'upgrade' is true,\n * throw an error if metapage version is newer; if 'upgrade' is false, it's a\n * downgrade, so throw an error if the metapage version is older.\n *\n * NB: caller must ensure that metapage already exists, which might not be the\n * case on 10.0.\n */\nvoid\nColumnarStorageUpdateCurrent(Relation rel, bool upgrade, uint64 reservedStripeId,\n\t\t\t\t\t\t\t uint64 reservedRowNumber, uint64 reservedOffset)\n{\n\tLockRelationForExtension(rel, ExclusiveLock);\n\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, true);\n\n\tif (ColumnarMetapageIsCurrent(&metapage))\n\t{\n\t\t/* nothing to do */\n\t\treturn;\n\t}\n\n\tif (upgrade && ColumnarMetapageIsNewer(&metapage))\n\t{\n\t\telog(ERROR, \"found newer columnar metapage while upgrading\");\n\t}\n\n\tif (!upgrade && ColumnarMetapageIsOlder(&metapage))\n\t{\n\t\telog(ERROR, \"found older columnar metapage while downgrading\");\n\t}\n\n\tmetapage.versionMajor = COLUMNAR_VERSION_MAJOR;\n\tmetapage.versionMinor = COLUMNAR_VERSION_MINOR;\n\n\t/* storageId remains the same */\n\tmetapage.reservedStripeId = reservedStripeId;\n\tmetapage.reservedRowNumber = reservedRowNumber;\n\tmetapage.reservedOffset = reservedOffset;\n\n\tColumnarOverwriteMetapage(rel, metapage);\n\n\tUnlockRelationForExtension(rel, ExclusiveLock);\n}\n\n\n/*\n * ColumnarStorageGetVersionMajor - return major version from the metapage.\n *\n * Throw an error if the metapage is not the current version, unless\n * 'force' is true.\n */\nuint64\nColumnarStorageGetVersionMajor(Relation rel, bool force)\n{\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, force);\n\n\treturn metapage.versionMajor;\n}\n\n\n/*\n * ColumnarStorageGetVersionMinor - return minor version from the metapage.\n *\n * Throw an error if the metapage is not the current version, unless\n * 'force' is true.\n */\nuint64\nColumnarStorageGetVersionMinor(Relation rel, bool force)\n{\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, force);\n\n\treturn metapage.versionMinor;\n}\n\n\n/*\n * ColumnarStorageGetStorageId - return storage ID from the metapage.\n *\n * Throw an error if the metapage is not the current version, unless\n * 'force' is true.\n */\nuint64\nColumnarStorageGetStorageId(Relation rel, bool force)\n{\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, force);\n\n\treturn metapage.storageId;\n}\n\n\n/*\n * ColumnarStorageGetReservedStripeId - return reserved stripe ID from the\n * metapage.\n *\n * Throw an error if the metapage is not the current version, unless\n * 'force' is true.\n */\nuint64\nColumnarStorageGetReservedStripeId(Relation rel, bool force)\n{\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, force);\n\n\treturn metapage.reservedStripeId;\n}\n\n\n/*\n * ColumnarStorageGetReservedRowNumber - return reserved row number from the\n * metapage.\n *\n * Throw an error if the metapage is not the current version, unless\n * 'force' is true.\n */\nuint64\nColumnarStorageGetReservedRowNumber(Relation rel, bool force)\n{\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, force);\n\n\treturn metapage.reservedRowNumber;\n}\n\n\n/*\n * ColumnarStorageGetReservedOffset - return reserved offset from the metapage.\n *\n * Throw an error if the metapage is not the current version, unless\n * 'force' is true.\n */\nuint64\nColumnarStorageGetReservedOffset(Relation rel, bool force)\n{\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, force);\n\n\treturn metapage.reservedOffset;\n}\n\n\n/*\n * ColumnarStorageIsCurrent - return true if metapage exists and is not\n * the current version.\n */\nbool\nColumnarStorageIsCurrent(Relation rel)\n{\n\tBlockNumber nblocks = smgrnblocks(RelationGetSmgr(rel), MAIN_FORKNUM);\n\n\tif (nblocks < 2)\n\t{\n\t\treturn false;\n\t}\n\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, true);\n\treturn ColumnarMetapageIsCurrent(&metapage);\n}\n\n\n/*\n * ColumnarStorageReserveRowNumber returns reservedRowNumber and advances\n * it for next row number reservation.\n */\nuint64\nColumnarStorageReserveRowNumber(Relation rel, uint64 nrows)\n{\n\tLockRelationForExtension(rel, ExclusiveLock);\n\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, false);\n\n\tuint64 firstRowNumber = metapage.reservedRowNumber;\n\tmetapage.reservedRowNumber += nrows;\n\n\tColumnarOverwriteMetapage(rel, metapage);\n\n\tUnlockRelationForExtension(rel, ExclusiveLock);\n\n\treturn firstRowNumber;\n}\n\n\n/*\n * ColumnarStorageReserveStripeId returns stripeId and advances it for next\n * stripeId reservation.\n * Note that this function doesn't handle row number reservation.\n * See ColumnarStorageReserveRowNumber function.\n */\nuint64\nColumnarStorageReserveStripeId(Relation rel)\n{\n\tLockRelationForExtension(rel, ExclusiveLock);\n\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, false);\n\n\tuint64 stripeId = metapage.reservedStripeId;\n\tmetapage.reservedStripeId++;\n\n\tColumnarOverwriteMetapage(rel, metapage);\n\n\tUnlockRelationForExtension(rel, ExclusiveLock);\n\n\treturn stripeId;\n}\n\n\n/*\n * ColumnarStorageReserveData - reserve logical data offsets for writing.\n */\nuint64\nColumnarStorageReserveData(Relation rel, uint64 amount)\n{\n\tif (amount == 0)\n\t{\n\t\treturn ColumnarInvalidLogicalOffset;\n\t}\n\n\tLockRelationForExtension(rel, ExclusiveLock);\n\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, false);\n\n\tuint64 alignedReservation = AlignReservation(metapage.reservedOffset);\n\tuint64 nextReservation = alignedReservation + amount;\n\tmetapage.reservedOffset = nextReservation;\n\n\t/* write new reservation */\n\tColumnarOverwriteMetapage(rel, metapage);\n\n\t/* last used PhysicalAddr of new reservation */\n\tPhysicalAddr final = LogicalToPhysical(nextReservation - 1);\n\n\t/* extend with new pages */\n\tBlockNumber nblocks = smgrnblocks(RelationGetSmgr(rel), MAIN_FORKNUM);\n\n\twhile (nblocks <= final.blockno)\n\t{\n\t\tBuffer newBuffer = ReadBuffer(rel, P_NEW);\n\t\tAssert(BufferGetBlockNumber(newBuffer) == nblocks);\n\t\tReleaseBuffer(newBuffer);\n\t\tnblocks++;\n\t}\n\n\tUnlockRelationForExtension(rel, ExclusiveLock);\n\n\treturn alignedReservation;\n}\n\n\n/*\n * ColumnarStorageRead - map the logical offset to a block and offset, then\n * read the buffer from multiple blocks if necessary.\n */\nvoid\nColumnarStorageRead(Relation rel, uint64 logicalOffset, char *data, uint32 amount)\n{\n\t/* if there's no work to do, succeed even with invalid offset */\n\tif (amount == 0)\n\t{\n\t\treturn;\n\t}\n\n\tif (!ColumnarLogicalOffsetIsValid(logicalOffset))\n\t{\n\t\telog(ERROR,\n\t\t\t \"attempted columnar read on relation %d from invalid logical offset: \"\n\t\t\t UINT64_FORMAT,\n\t\t\t rel->rd_id, logicalOffset);\n\t}\n\n\tuint64 read = 0;\n\n\twhile (read < amount)\n\t{\n\t\tPhysicalAddr addr = LogicalToPhysical(logicalOffset + read);\n\n\t\tuint32 to_read = Min(amount - read, BLCKSZ - addr.offset);\n\t\tReadFromBlock(rel, addr.blockno, addr.offset, data + read, to_read,\n\t\t\t\t\t  false);\n\n\t\tread += to_read;\n\t}\n}\n\n\n/*\n * ColumnarStorageWrite - map the logical offset to a block and offset, then\n * write the buffer across multiple blocks if necessary.\n */\nvoid\nColumnarStorageWrite(Relation rel, uint64 logicalOffset, char *data, uint32 amount)\n{\n\t/* if there's no work to do, succeed even with invalid offset */\n\tif (amount == 0)\n\t{\n\t\treturn;\n\t}\n\n\tif (!ColumnarLogicalOffsetIsValid(logicalOffset))\n\t{\n\t\telog(ERROR,\n\t\t\t \"attempted columnar write on relation %d to invalid logical offset: \"\n\t\t\t UINT64_FORMAT,\n\t\t\t rel->rd_id, logicalOffset);\n\t}\n\n\tuint64 written = 0;\n\n\twhile (written < amount)\n\t{\n\t\tPhysicalAddr addr = LogicalToPhysical(logicalOffset + written);\n\n\t\tuint64 to_write = Min(amount - written, BLCKSZ - addr.offset);\n\t\tWriteToBlock(rel, addr.blockno, addr.offset, data + written, to_write,\n\t\t\t\t\t false);\n\n\t\twritten += to_write;\n\t}\n}\n\n\n/*\n * ColumnarStorageTruncate - truncate the columnar storage such that\n * newDataReservation will be the first unused logical offset available. Free\n * pages at the end of the relation.\n *\n * Caller must hold AccessExclusiveLock on the relation.\n *\n * Returns true if pages were truncated; false otherwise.\n */\nbool\nColumnarStorageTruncate(Relation rel, uint64 newDataReservation)\n{\n\tif (!ColumnarLogicalOffsetIsValid(newDataReservation))\n\t{\n\t\telog(ERROR,\n\t\t\t \"attempted to truncate relation %d to \"\n\t\t\t \"invalid logical offset: \" UINT64_FORMAT,\n\t\t\t rel->rd_id, newDataReservation);\n\t}\n\n\tBlockNumber old_rel_pages = smgrnblocks(RelationGetSmgr(rel), MAIN_FORKNUM);\n\tif (old_rel_pages == 0)\n\t{\n\t\t/* nothing to do */\n\t\treturn false;\n\t}\n\n\tLockRelationForExtension(rel, ExclusiveLock);\n\n\tColumnarMetapage metapage = ColumnarMetapageRead(rel, false);\n\n\tif (metapage.reservedOffset < newDataReservation)\n\t{\n\t\telog(ERROR,\n\t\t\t \"attempted to truncate relation %d to offset \" UINT64_FORMAT \\\n\t\t\t \" which is higher than existing offset \" UINT64_FORMAT,\n\t\t\t rel->rd_id, newDataReservation, metapage.reservedOffset);\n\t}\n\n\tif (metapage.reservedOffset == newDataReservation)\n\t{\n\t\t/* nothing to do */\n\t\tUnlockRelationForExtension(rel, ExclusiveLock);\n\t\treturn false;\n\t}\n\n\tmetapage.reservedOffset = newDataReservation;\n\n\t/* write new reservation */\n\tColumnarOverwriteMetapage(rel, metapage);\n\n\tUnlockRelationForExtension(rel, ExclusiveLock);\n\n\tPhysicalAddr final = LogicalToPhysical(newDataReservation - 1);\n\tBlockNumber new_rel_pages = final.blockno + 1;\n\tAssert(new_rel_pages <= old_rel_pages);\n\n\t/*\n\t * Truncate the storage. Note that RelationTruncate() takes care of\n\t * Write Ahead Logging.\n\t */\n\tif (new_rel_pages < old_rel_pages)\n\t{\n\t\tRelationTruncate(rel, new_rel_pages);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ColumnarOverwriteMetapage writes given columnarMetapage back to metapage\n * for given relation.\n */\nstatic void\nColumnarOverwriteMetapage(Relation relation, ColumnarMetapage columnarMetapage)\n{\n\t/* clear metapage because we are overwriting */\n\tbool clear = true;\n\tWriteToBlock(relation, COLUMNAR_METAPAGE_BLOCKNO, SizeOfPageHeaderData,\n\t\t\t\t (char *) &columnarMetapage, sizeof(ColumnarMetapage), clear);\n}\n\n\n/*\n * ColumnarMetapageRead - read the current contents of the metapage. Error if\n * it does not exist. Throw an error if the metapage is not the current\n * version, unless 'force' is true.\n *\n * NB: it's safe to read a different version of a metapage because we\n * guarantee that fields will only be added and existing fields will never be\n * changed. However, it's important that we don't depend on new fields being\n * set properly when we read an old metapage; an old metapage should only be\n * read for the purposes of upgrading or error checking.\n */\nstatic ColumnarMetapage\nColumnarMetapageRead(Relation rel, bool force)\n{\n\tBlockNumber nblocks = smgrnblocks(RelationGetSmgr(rel), MAIN_FORKNUM);\n\tif (nblocks == 0)\n\t{\n\t\t/*\n\t\t * We only expect this to happen when upgrading citus.so. This is because,\n\t\t * in current version of columnar, we immediately create the metapage\n\t\t * for columnar tables, i.e right after creating the table.\n\t\t * However in older versions, we were creating metapages lazily, i.e\n\t\t * when ingesting data to columnar table.\n\t\t */\n\t\tereport(ERROR, (errmsg(\"columnar metapage for relation \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t   RelationGetRelationName(rel)),\n\t\t\t\t\t\terrhint(OLD_METAPAGE_VERSION_HINT)));\n\t}\n\n\t/*\n\t * Regardless of \"force\" parameter, always force read metapage block.\n\t * We will check metapage version in ColumnarMetapageCheckVersion\n\t * depending on \"force\".\n\t */\n\tbool forceReadBlock = true;\n\tColumnarMetapage metapage;\n\tReadFromBlock(rel, COLUMNAR_METAPAGE_BLOCKNO, SizeOfPageHeaderData,\n\t\t\t\t  (char *) &metapage, sizeof(ColumnarMetapage), forceReadBlock);\n\n\tif (!force)\n\t{\n\t\tColumnarMetapageCheckVersion(rel, &metapage);\n\t}\n\n\treturn metapage;\n}\n\n\n/*\n * ReadFromBlock - read bytes from a page at the given offset. If 'force' is\n * true, don't check pd_lower; useful when reading a metapage of unknown\n * version.\n */\nstatic void\nReadFromBlock(Relation rel, BlockNumber blockno, uint32 offset, char *buf,\n\t\t\t  uint32 len, bool force)\n{\n\tBuffer buffer = ReadBuffer(rel, blockno);\n\tLockBuffer(buffer, BUFFER_LOCK_SHARE);\n\tPage page = BufferGetPage(buffer);\n\tPageHeader phdr = (PageHeader) page;\n\n\tif (BLCKSZ < offset + len || (!force && (phdr->pd_lower < offset + len)))\n\t{\n\t\telog(ERROR,\n\t\t\t \"attempt to read columnar data of length %d from offset %d of block %d of relation %d\",\n\t\t\t len, offset, blockno, rel->rd_id);\n\t}\n\n\tmemcpy_s(buf, len, page + offset, len);\n\tUnlockReleaseBuffer(buffer);\n}\n\n\n/*\n * WriteToBlock - append data to a block, initializing if necessary, and emit\n * WAL. If 'clear' is true, always clear the data on the page and reinitialize\n * it first, and offset must be SizeOfPageHeaderData. Otherwise, offset must\n * be equal to pd_lower and pd_lower will be set to the end of the written\n * data.\n */\nstatic void\nWriteToBlock(Relation rel, BlockNumber blockno, uint32 offset, char *buf,\n\t\t\t uint32 len, bool clear)\n{\n\tBuffer buffer = ReadBuffer(rel, blockno);\n\tGenericXLogState *state = GenericXLogStart(rel);\n\n\tLockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);\n\n\tPage page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);\n\n\tPageHeader phdr = (PageHeader) page;\n\tif (PageIsNew(page) || clear)\n\t{\n\t\tPageInit(page, BLCKSZ, 0);\n\t}\n\n\tif (phdr->pd_lower < offset || phdr->pd_upper - offset < len)\n\t{\n\t\telog(ERROR,\n\t\t\t \"attempt to write columnar data of length %d to offset %d of block %d of relation %d\",\n\t\t\t len, offset, blockno, rel->rd_id);\n\t}\n\n\t/*\n\t * After a transaction has been rolled-back, we might be\n\t * over-writing the rolledback write, so phdr->pd_lower can be\n\t * different from addr.offset.\n\t *\n\t * We reset pd_lower to reset the rolledback write.\n\t *\n\t * Given that we always align page reservation to the next page as of\n\t * 10.2, having such a disk page is only possible if write operaion\n\t * failed in an older version of columnar, but now user attempts writing\n\t * to that table in version >= 10.2.\n\t */\n\tif (phdr->pd_lower > offset)\n\t{\n\t\tereport(DEBUG4, (errmsg(\"overwriting page %u\", blockno),\n\t\t\t\t\t\t errdetail(\"This can happen after a roll-back.\")));\n\t\tphdr->pd_lower = offset;\n\t}\n\n\tmemcpy_s(page + phdr->pd_lower, phdr->pd_upper - phdr->pd_lower, buf, len);\n\tphdr->pd_lower += len;\n\n\tGenericXLogFinish(state);\n\n\tUnlockReleaseBuffer(buffer);\n}\n\n\n/*\n * AlignReservation - given an unused logical byte offset, align it so that it\n * falls at the start of a page.\n *\n * XXX: Reconsider whether we want/need to do this at all.\n */\nstatic uint64\nAlignReservation(uint64 prevReservation)\n{\n\tPhysicalAddr prevAddr = LogicalToPhysical(prevReservation);\n\tuint64 alignedReservation = prevReservation;\n\n\tif (prevAddr.offset != SizeOfPageHeaderData)\n\t{\n\t\t/* not aligned; align on beginning of next page */\n\t\tPhysicalAddr initial = { 0 };\n\t\tinitial.blockno = prevAddr.blockno + 1;\n\t\tinitial.offset = SizeOfPageHeaderData;\n\t\talignedReservation = PhysicalToLogical(initial);\n\t}\n\n\tAssert(alignedReservation >= prevReservation);\n\treturn alignedReservation;\n}\n\n\n/*\n * ColumnarMetapageIsCurrent - is the metapage at the latest version?\n */\nstatic bool\nColumnarMetapageIsCurrent(ColumnarMetapage *metapage)\n{\n\treturn (metapage->versionMajor == COLUMNAR_VERSION_MAJOR &&\n\t\t\tmetapage->versionMinor == COLUMNAR_VERSION_MINOR);\n}\n\n\n/*\n * ColumnarMetapageIsOlder - is the metapage older than the current version?\n */\nstatic bool\nColumnarMetapageIsOlder(ColumnarMetapage *metapage)\n{\n\treturn (metapage->versionMajor < COLUMNAR_VERSION_MAJOR ||\n\t\t\t(metapage->versionMajor == COLUMNAR_VERSION_MAJOR &&\n\t\t\t (int) metapage->versionMinor < (int) COLUMNAR_VERSION_MINOR));\n}\n\n\n/*\n * ColumnarMetapageIsNewer - is the metapage newer than the current version?\n */\nstatic bool\nColumnarMetapageIsNewer(ColumnarMetapage *metapage)\n{\n\treturn (metapage->versionMajor > COLUMNAR_VERSION_MAJOR ||\n\t\t\t(metapage->versionMajor == COLUMNAR_VERSION_MAJOR &&\n\t\t\t metapage->versionMinor > COLUMNAR_VERSION_MINOR));\n}\n\n\n/*\n * ColumnarMetapageCheckVersion - throw an error if accessing old\n * version of metapage.\n */\nstatic void\nColumnarMetapageCheckVersion(Relation rel, ColumnarMetapage *metapage)\n{\n\tif (!ColumnarMetapageIsCurrent(metapage))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"attempted to access relation \\\"%s\\\", which uses an older columnar format\",\n\t\t\t\t\t\t\tRelationGetRelationName(rel)),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"Columnar format version %d.%d is required, \\\"%s\\\" has version %d.%d.\",\n\t\t\t\t\t\t\tCOLUMNAR_VERSION_MAJOR, COLUMNAR_VERSION_MINOR,\n\t\t\t\t\t\t\tRelationGetRelationName(rel),\n\t\t\t\t\t\t\tmetapage->versionMajor, metapage->versionMinor),\n\t\t\t\t\t\terrhint(OLD_METAPAGE_VERSION_HINT)));\n\t}\n}\n\n\n/*\n * test_columnar_storage_write_new_page is a UDF only used for testing\n * purposes. It could make more sense to define this in columnar_debug.c,\n * but the storage layer doesn't expose ColumnarMetapage to any other files,\n * so we define it here.\n */\nDatum\ntest_columnar_storage_write_new_page(PG_FUNCTION_ARGS)\n{\n\tOid relationId = PG_GETARG_OID(0);\n\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\n\t/*\n\t * Allocate a new page, write some data to there, and set reserved offset\n\t * to the start of that page. That way, for a subsequent write operation,\n\t * storage layer would try to overwrite the page that we allocated here.\n\t */\n\tuint64 newPageOffset = ColumnarStorageGetReservedOffset(relation, false);\n\n\tColumnarStorageReserveData(relation, 100);\n\tColumnarStorageWrite(relation, newPageOffset, \"foo_bar\", 8);\n\n\tColumnarMetapage metapage = ColumnarMetapageRead(relation, false);\n\tmetapage.reservedOffset = newPageOffset;\n\tColumnarOverwriteMetapage(relation, metapage);\n\n\trelation_close(relation, AccessShareLock);\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/columnar/columnar_tableam.c",
    "content": "#include <math.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n#include \"safe_lib.h\"\n\n#include \"access/detoast.h\"\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/multixact.h\"\n#include \"access/rewriteheap.h\"\n#include \"access/tableam.h\"\n#include \"access/tsmapi.h\"\n#include \"access/xact.h\"\n#include \"catalog/catalog.h\"\n#include \"catalog/index.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/objectaccess.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_extension.h\"\n#include \"catalog/pg_publication.h\"\n#include \"catalog/pg_trigger.h\"\n#include \"catalog/storage.h\"\n#include \"catalog/storage_xlog.h\"\n#include \"commands/defrem.h\"\n#include \"commands/extension.h\"\n#include \"commands/progress.h\"\n#include \"commands/vacuum.h\"\n#include \"executor/executor.h\"\n#include \"nodes/makefuncs.h\"\n#include \"optimizer/plancat.h\"\n#include \"storage/bufmgr.h\"\n#include \"storage/bufpage.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/predicate.h\"\n#include \"storage/procarray.h\"\n#include \"storage/smgr.h\"\n#include \"tcop/utility.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/pg_rusage.h\"\n#include \"utils/rel.h\"\n#include \"utils/relcache.h\"\n#include \"utils/syscache.h\"\n\n#include \"citus_version.h\"\n#include \"pg_version_compat.h\"\n\n#include \"columnar/columnar.h\"\n#include \"columnar/columnar_customscan.h\"\n#include \"columnar/columnar_storage.h\"\n#include \"columnar/columnar_tableam.h\"\n#include \"columnar/columnar_version_compat.h\"\n\n#include \"distributed/listutils.h\"\n\n/*\n * Timing parameters for truncate locking heuristics.\n *\n * These are the same values from src/backend/access/heap/vacuumlazy.c\n */\n#define VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL 50       /* ms */\n#define VACUUM_TRUNCATE_LOCK_TIMEOUT 4500               /* ms */\n\n/*\n * ColumnarScanDescData is the scan state passed between beginscan(),\n * getnextslot(), rescan(), and endscan() calls.\n */\ntypedef struct ColumnarScanDescData\n{\n\tTableScanDescData cs_base;\n\tColumnarReadState *cs_readState;\n\n\t/*\n\t * We initialize cs_readState lazily in the first getnextslot() call. We\n\t * need the following for initialization. We save them in beginscan().\n\t */\n\tMemoryContext scanContext;\n\tBitmapset *attr_needed;\n\tList *scanQual;\n} ColumnarScanDescData;\n\n\n/*\n * IndexFetchColumnarData is the scan state passed between index_fetch_begin,\n * index_fetch_reset, index_fetch_end, index_fetch_tuple calls.\n */\ntypedef struct IndexFetchColumnarData\n{\n\tIndexFetchTableData cs_base;\n\tColumnarReadState *cs_readState;\n\n\t/*\n\t * We initialize cs_readState lazily in the first columnar_index_fetch_tuple\n\t * call. However, we want to do memory allocations in a sub MemoryContext of\n\t * columnar_index_fetch_begin. For this reason, we store scanContext in\n\t * columnar_index_fetch_begin.\n\t */\n\tMemoryContext scanContext;\n} IndexFetchColumnarData;\n\nstatic object_access_hook_type PrevObjectAccessHook = NULL;\nstatic ProcessUtility_hook_type PrevProcessUtilityHook = NULL;\n\n/* forward declaration for static functions */\nstatic MemoryContext CreateColumnarScanMemoryContext(void);\nstatic void ColumnarTableDropHook(Oid tgid);\nstatic void ColumnarTriggerCreateHook(Oid tgid);\nstatic void ColumnarTableAMObjectAccessHook(ObjectAccessType access, Oid classId,\n\t\t\t\t\t\t\t\t\t\t\tOid objectId, int subId,\n\t\t\t\t\t\t\t\t\t\t\tvoid *arg);\nstatic RangeVar * ColumnarProcessAlterTable(AlterTableStmt *alterTableStmt,\n\t\t\t\t\t\t\t\t\t\t\tList **columnarOptions);\nstatic void ColumnarProcessUtility(PlannedStmt *pstmt,\n\t\t\t\t\t\t\t\t   const char *queryString,\n\t\t\t\t\t\t\t\t   bool readOnlyTree,\n\t\t\t\t\t\t\t\t   ProcessUtilityContext context,\n\t\t\t\t\t\t\t\t   ParamListInfo params,\n\t\t\t\t\t\t\t\t   struct QueryEnvironment *queryEnv,\n\t\t\t\t\t\t\t\t   DestReceiver *dest,\n\t\t\t\t\t\t\t\t   QueryCompletion *completionTag);\nstatic bool ConditionalLockRelationWithTimeout(Relation rel, LOCKMODE lockMode,\n\t\t\t\t\t\t\t\t\t\t\t   int timeout, int retryInterval);\nstatic List * NeededColumnsList(TupleDesc tupdesc, Bitmapset *attr_needed);\nstatic void LogRelationStats(Relation rel, int elevel);\nstatic void TruncateColumnar(Relation rel, int elevel);\nstatic HeapTuple ColumnarSlotCopyHeapTuple(TupleTableSlot *slot);\nstatic void ColumnarCheckLogicalReplication(Relation rel);\nstatic Datum * detoast_values(TupleDesc tupleDesc, Datum *orig_values, bool *isnull);\nstatic ItemPointerData row_number_to_tid(uint64 rowNumber);\nstatic uint64 tid_to_row_number(ItemPointerData tid);\nstatic void ErrorIfInvalidRowNumber(uint64 rowNumber);\nstatic void ColumnarReportTotalVirtualBlocks(Relation relation, Snapshot snapshot,\n\t\t\t\t\t\t\t\t\t\t\t int progressArrIndex);\nstatic BlockNumber ColumnarGetNumberOfVirtualBlocks(Relation relation, Snapshot snapshot);\nstatic ItemPointerData ColumnarGetHighestItemPointer(Relation relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t Snapshot snapshot);\nstatic double ColumnarReadRowsIntoIndex(TableScanDesc scan,\n\t\t\t\t\t\t\t\t\t\tRelation indexRelation,\n\t\t\t\t\t\t\t\t\t\tIndexInfo *indexInfo,\n\t\t\t\t\t\t\t\t\t\tbool progress,\n\t\t\t\t\t\t\t\t\t\tIndexBuildCallback indexCallback,\n\t\t\t\t\t\t\t\t\t\tvoid *indexCallbackState,\n\t\t\t\t\t\t\t\t\t\tEState *estate, ExprState *predicate);\nstatic void ColumnarReadMissingRowsIntoIndex(TableScanDesc scan, Relation indexRelation,\n\t\t\t\t\t\t\t\t\t\t\t IndexInfo *indexInfo, EState *estate,\n\t\t\t\t\t\t\t\t\t\t\t ExprState *predicate,\n\t\t\t\t\t\t\t\t\t\t\t ValidateIndexState *state);\nstatic ItemPointerData TupleSortSkipSmallerItemPointers(Tuplesortstate *tupleSort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tItemPointer targetItemPointer);\n\n/* functions for CheckCitusColumnarVersion */\nstatic bool CheckAvailableVersionColumnar(int elevel);\nstatic bool CheckInstalledVersionColumnar(int elevel);\nstatic char * AvailableExtensionVersionColumnar(void);\nstatic char * InstalledExtensionVersionColumnar(void);\nstatic bool CitusColumnarHasBeenLoadedInternal(void);\nstatic bool CitusColumnarHasBeenLoaded(void);\nstatic bool CheckCitusColumnarVersion(int elevel);\nstatic bool MajorVersionsCompatibleColumnar(char *leftVersion, char *rightVersion);\nstatic bool MinorVersionsCompatibleRelaxedColumnar(char *leftVersion, char *rightVersion);\nstatic int ParseVersionComponent(const char *version, char **endPtr);\n\n/* global variables for CheckCitusColumnarVersion */\nstatic bool extensionLoadedColumnar = false;\nstatic bool EnableVersionChecksColumnar = true;\nstatic bool citusVersionKnownCompatibleColumnar = false;\n\n/* Custom tuple slot ops used for columnar. Initialized in columnar_tableam_init(). */\nstatic TupleTableSlotOps TTSOpsColumnar;\n\nstatic const TupleTableSlotOps *\ncolumnar_slot_callbacks(Relation relation)\n{\n\treturn &TTSOpsColumnar;\n}\n\n\nstatic TableScanDesc\ncolumnar_beginscan(Relation relation, Snapshot snapshot,\n\t\t\t\t   int nkeys, ScanKey key,\n\t\t\t\t   ParallelTableScanDesc parallel_scan,\n\t\t\t\t   uint32 flags)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\tint natts = relation->rd_att->natts;\n\n\t/* attr_needed represents 0-indexed attribute numbers */\n\tBitmapset *attr_needed = bms_add_range(NULL, 0, natts - 1);\n\n\tTableScanDesc scandesc = columnar_beginscan_extended(relation, snapshot, nkeys, key,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t parallel_scan,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t flags, attr_needed, NULL);\n\n\tbms_free(attr_needed);\n\n\treturn scandesc;\n}\n\n\nTableScanDesc\ncolumnar_beginscan_extended(Relation relation, Snapshot snapshot,\n\t\t\t\t\t\t\tint nkeys, ScanKey key,\n\t\t\t\t\t\t\tParallelTableScanDesc parallel_scan,\n\t\t\t\t\t\t\tuint32 flags, Bitmapset *attr_needed, List *scanQual)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\tRelFileNumber relfilenumber = relation->rd_locator.relNumber;\n\n\t/*\n\t * A memory context to use for scan-wide data, including the lazily\n\t * initialized read state. We assume that beginscan is called in a\n\t * context that will last until end of scan.\n\t */\n\tMemoryContext scanContext = CreateColumnarScanMemoryContext();\n\tMemoryContext oldContext = MemoryContextSwitchTo(scanContext);\n\n\tColumnarScanDesc scan = palloc0(sizeof(ColumnarScanDescData));\n\tscan->cs_base.rs_rd = relation;\n\tscan->cs_base.rs_snapshot = snapshot;\n\tscan->cs_base.rs_nkeys = nkeys;\n\tscan->cs_base.rs_key = key;\n\tscan->cs_base.rs_flags = flags;\n\tscan->cs_base.rs_parallel = parallel_scan;\n\n\t/*\n\t * We will initialize this lazily in first tuple, where we have the actual\n\t * tuple descriptor to use for reading. In some cases like ALTER TABLE ...\n\t * ALTER COLUMN ... TYPE, the tuple descriptor of relation doesn't match\n\t * the storage which we are reading, so we need to use the tuple descriptor\n\t * of \"slot\" in first read.\n\t */\n\tscan->cs_readState = NULL;\n\tscan->attr_needed = bms_copy(attr_needed);\n\tscan->scanQual = copyObject(scanQual);\n\tscan->scanContext = scanContext;\n\n\tif (PendingWritesInUpperTransactions(relfilenumber, GetCurrentSubTransactionId()))\n\t{\n\t\telog(ERROR,\n\t\t\t \"cannot read from table when there is unflushed data in upper transactions\");\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\n\treturn ((TableScanDesc) scan);\n}\n\n\n/*\n * CreateColumnarScanMemoryContext creates a memory context to store\n * ColumnarReadStare in it.\n */\nstatic MemoryContext\nCreateColumnarScanMemoryContext(void)\n{\n\treturn AllocSetContextCreate(CurrentMemoryContext, \"Columnar Scan Context\",\n\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_SIZES);\n}\n\n\n/*\n * init_columnar_read_state initializes a column store table read and returns the\n * state.\n */\nstatic ColumnarReadState *\ninit_columnar_read_state(Relation relation, TupleDesc tupdesc, Bitmapset *attr_needed,\n\t\t\t\t\t\t List *scanQual, MemoryContext scanContext, Snapshot snapshot,\n\t\t\t\t\t\t bool randomAccess)\n{\n\tMemoryContext oldContext = MemoryContextSwitchTo(scanContext);\n\n\tList *neededColumnList = NeededColumnsList(tupdesc, attr_needed);\n\tColumnarReadState *readState = ColumnarBeginRead(relation, tupdesc, neededColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t scanQual, scanContext, snapshot,\n\t\t\t\t\t\t\t\t\t\t\t\t\t randomAccess);\n\n\tMemoryContextSwitchTo(oldContext);\n\n\treturn readState;\n}\n\n\nstatic void\ncolumnar_endscan(TableScanDesc sscan)\n{\n\tColumnarScanDesc scan = (ColumnarScanDesc) sscan;\n\tif (scan->cs_readState != NULL)\n\t{\n\t\tColumnarEndRead(scan->cs_readState);\n\t\tscan->cs_readState = NULL;\n\t}\n\n\tif (scan->cs_base.rs_flags & SO_TEMP_SNAPSHOT)\n\t{\n\t\tUnregisterSnapshot(scan->cs_base.rs_snapshot);\n\t}\n}\n\n\nstatic void\ncolumnar_rescan(TableScanDesc sscan, ScanKey key, bool set_params,\n\t\t\t\tbool allow_strat, bool allow_sync, bool allow_pagemode)\n{\n\tColumnarScanDesc scan = (ColumnarScanDesc) sscan;\n\n\t/* XXX: hack to pass in new quals that aren't actually scan keys */\n\tList *scanQual = (List *) key;\n\n\tif (scan->cs_readState != NULL)\n\t{\n\t\tColumnarRescan(scan->cs_readState, scanQual);\n\t}\n}\n\n\nstatic bool\ncolumnar_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)\n{\n\tColumnarScanDesc scan = (ColumnarScanDesc) sscan;\n\n\t/*\n\t * if this is the first row, initialize read state.\n\t */\n\tif (scan->cs_readState == NULL)\n\t{\n\t\tbool randomAccess = false;\n\t\tscan->cs_readState =\n\t\t\tinit_columnar_read_state(scan->cs_base.rs_rd, slot->tts_tupleDescriptor,\n\t\t\t\t\t\t\t\t\t scan->attr_needed, scan->scanQual,\n\t\t\t\t\t\t\t\t\t scan->scanContext, scan->cs_base.rs_snapshot,\n\t\t\t\t\t\t\t\t\t randomAccess);\n\t}\n\n\tExecClearTuple(slot);\n\n\tuint64 rowNumber;\n\tbool nextRowFound = ColumnarReadNextRow(scan->cs_readState, slot->tts_values,\n\t\t\t\t\t\t\t\t\t\t\tslot->tts_isnull, &rowNumber);\n\n\tif (!nextRowFound)\n\t{\n\t\treturn false;\n\t}\n\n\tExecStoreVirtualTuple(slot);\n\n\tslot->tts_tid = row_number_to_tid(rowNumber);\n\n\treturn true;\n}\n\n\n/*\n * row_number_to_tid maps given rowNumber to ItemPointerData.\n */\nstatic ItemPointerData\nrow_number_to_tid(uint64 rowNumber)\n{\n\tErrorIfInvalidRowNumber(rowNumber);\n\n\tItemPointerData tid = { 0 };\n\tItemPointerSetBlockNumber(&tid, rowNumber / VALID_ITEMPOINTER_OFFSETS);\n\tItemPointerSetOffsetNumber(&tid, rowNumber % VALID_ITEMPOINTER_OFFSETS +\n\t\t\t\t\t\t\t   FirstOffsetNumber);\n\treturn tid;\n}\n\n\n/*\n * tid_to_row_number maps given ItemPointerData to rowNumber.\n */\nstatic uint64\ntid_to_row_number(ItemPointerData tid)\n{\n\tuint64 rowNumber = ItemPointerGetBlockNumber(&tid) * VALID_ITEMPOINTER_OFFSETS +\n\t\t\t\t\t   ItemPointerGetOffsetNumber(&tid) - FirstOffsetNumber;\n\n\tErrorIfInvalidRowNumber(rowNumber);\n\n\treturn rowNumber;\n}\n\n\n/*\n * ErrorIfInvalidRowNumber errors out if given rowNumber is invalid.\n */\nstatic void\nErrorIfInvalidRowNumber(uint64 rowNumber)\n{\n\tif (rowNumber == COLUMNAR_INVALID_ROW_NUMBER)\n\t{\n\t\t/* not expected but be on the safe side */\n\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t\t\terrmsg(\"unexpected row number for columnar table\")));\n\t}\n\telse if (rowNumber > COLUMNAR_MAX_ROW_NUMBER)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"columnar tables can't have row numbers \"\n\t\t\t\t\t\t\t   \"greater than \" UINT64_FORMAT,\n\t\t\t\t\t\t\t   (uint64) COLUMNAR_MAX_ROW_NUMBER),\n\t\t\t\t\t\terrhint(\"Consider using VACUUM FULL for your table\")));\n\t}\n}\n\n\nstatic Size\ncolumnar_parallelscan_estimate(Relation rel)\n{\n\telog(ERROR, \"columnar_parallelscan_estimate not implemented\");\n}\n\n\nstatic Size\ncolumnar_parallelscan_initialize(Relation rel, ParallelTableScanDesc pscan)\n{\n\telog(ERROR, \"columnar_parallelscan_initialize not implemented\");\n}\n\n\nstatic void\ncolumnar_parallelscan_reinitialize(Relation rel, ParallelTableScanDesc pscan)\n{\n\telog(ERROR, \"columnar_parallelscan_reinitialize not implemented\");\n}\n\n\nstatic IndexFetchTableData *\ncolumnar_index_fetch_begin(Relation rel)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\tRelFileNumber relfilenumber = rel->rd_locator.relNumber;\n\tif (PendingWritesInUpperTransactions(relfilenumber, GetCurrentSubTransactionId()))\n\t{\n\t\t/* XXX: maybe we can just flush the data and continue */\n\t\telog(ERROR, \"cannot read from index when there is unflushed data in \"\n\t\t\t\t\t\"upper transactions\");\n\t}\n\n\tMemoryContext scanContext = CreateColumnarScanMemoryContext();\n\tMemoryContext oldContext = MemoryContextSwitchTo(scanContext);\n\n\tIndexFetchColumnarData *scan = palloc0(sizeof(IndexFetchColumnarData));\n\tscan->cs_base.rel = rel;\n\tscan->cs_readState = NULL;\n\tscan->scanContext = scanContext;\n\n\tMemoryContextSwitchTo(oldContext);\n\n\treturn &scan->cs_base;\n}\n\n\nstatic void\ncolumnar_index_fetch_reset(IndexFetchTableData *sscan)\n{\n\t/* no-op */\n}\n\n\nstatic void\ncolumnar_index_fetch_end(IndexFetchTableData *sscan)\n{\n\tcolumnar_index_fetch_reset(sscan);\n\n\tIndexFetchColumnarData *scan = (IndexFetchColumnarData *) sscan;\n\tif (scan->cs_readState)\n\t{\n\t\tColumnarEndRead(scan->cs_readState);\n\t\tscan->cs_readState = NULL;\n\t}\n}\n\n\nstatic bool\ncolumnar_index_fetch_tuple(struct IndexFetchTableData *sscan,\n\t\t\t\t\t\t   ItemPointer tid,\n\t\t\t\t\t\t   Snapshot snapshot,\n\t\t\t\t\t\t   TupleTableSlot *slot,\n\t\t\t\t\t\t   bool *call_again, bool *all_dead)\n{\n\t/* no HOT chains are possible in columnar, directly set it to false */\n\t*call_again = false;\n\n\t/*\n\t * Initialize all_dead to false if passed to be non-NULL.\n\t *\n\t * XXX: For aborted writes, we should set all_dead to true but this would\n\t * require implementing columnar_index_delete_tuples for simple deletion\n\t * of dead tuples (TM_IndexDeleteOp.bottomup = false).\n\t */\n\tif (all_dead)\n\t{\n\t\t*all_dead = false;\n\t}\n\n\tExecClearTuple(slot);\n\n\tIndexFetchColumnarData *scan = (IndexFetchColumnarData *) sscan;\n\tRelation columnarRelation = scan->cs_base.rel;\n\n\t/* initialize read state for the first row */\n\tif (scan->cs_readState == NULL)\n\t{\n\t\t/* we need all columns */\n\t\tint natts = columnarRelation->rd_att->natts;\n\t\tBitmapset *attr_needed = bms_add_range(NULL, 0, natts - 1);\n\n\t\t/* no quals for index scan */\n\t\tList *scanQual = NIL;\n\n\t\tbool randomAccess = true;\n\t\tscan->cs_readState = init_columnar_read_state(columnarRelation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  slot->tts_tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  attr_needed, scanQual,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  scan->scanContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  snapshot, randomAccess);\n\t}\n\n\tuint64 rowNumber = tid_to_row_number(*tid);\n\tStripeMetadata *stripeMetadata =\n\t\tFindStripeWithMatchingFirstRowNumber(columnarRelation, rowNumber, snapshot);\n\tif (!stripeMetadata)\n\t{\n\t\t/* it is certain that tuple with rowNumber doesn't exist */\n\t\treturn false;\n\t}\n\n\tStripeWriteStateEnum stripeWriteState = StripeWriteState(stripeMetadata);\n\tif (stripeWriteState == STRIPE_WRITE_FLUSHED &&\n\t\t!ColumnarReadRowByRowNumber(scan->cs_readState, rowNumber,\n\t\t\t\t\t\t\t\t\tslot->tts_values, slot->tts_isnull))\n\t{\n\t\t/*\n\t\t * FindStripeWithMatchingFirstRowNumber doesn't verify upper row\n\t\t * number boundary of found stripe. For this reason, we didn't\n\t\t * certainly know if given row number belongs to one of the stripes.\n\t\t */\n\t\treturn false;\n\t}\n\telse if (stripeWriteState == STRIPE_WRITE_ABORTED)\n\t{\n\t\t/*\n\t\t * We only expect to see un-flushed stripes when checking against\n\t\t * constraint violation. In that case, indexAM provides dirty\n\t\t * snapshot to index_fetch_tuple callback.\n\t\t */\n\t\tAssert(snapshot->snapshot_type == SNAPSHOT_DIRTY);\n\t\treturn false;\n\t}\n\telse if (stripeWriteState == STRIPE_WRITE_IN_PROGRESS)\n\t{\n\t\tif (stripeMetadata->insertedByCurrentXact)\n\t\t{\n\t\t\t/*\n\t\t\t * Stripe write is in progress and its entry is inserted by current\n\t\t\t * transaction, so obviously it must be written by me. Since caller\n\t\t\t * might want to use tupleslot datums for some reason, do another\n\t\t\t * look-up, but this time by first flushing our writes.\n\t\t\t *\n\t\t\t * XXX: For index scan, this is the only case that we flush pending\n\t\t\t * writes of the current backend. If we have taught reader how to\n\t\t\t * read from WriteStateMap. then we could guarantee that\n\t\t\t * index_fetch_tuple would never flush pending writes, but this seem\n\t\t\t * to be too much work for now, but should be doable.\n\t\t\t */\n\t\t\tColumnarReadFlushPendingWrites(scan->cs_readState);\n\n\t\t\t/*\n\t\t\t * Fill the tupleslot and fall through to return true, it\n\t\t\t * certainly exists.\n\t\t\t */\n\t\t\tColumnarReadRowByRowNumberOrError(scan->cs_readState, rowNumber,\n\t\t\t\t\t\t\t\t\t\t\t  slot->tts_values, slot->tts_isnull);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* similar to aborted writes, it should be dirty snapshot */\n\t\t\tAssert(snapshot->snapshot_type == SNAPSHOT_DIRTY);\n\n\t\t\t/*\n\t\t\t * Stripe that \"might\" contain the tuple with rowNumber is not\n\t\t\t * flushed yet. Here we set all attributes of given tupleslot to NULL\n\t\t\t * before returning true and expect the indexAM callback that called\n\t\t\t * us --possibly to check against constraint violation-- blocks until\n\t\t\t * writer transaction commits or aborts, without requiring us to fill\n\t\t\t * the tupleslot properly.\n\t\t\t *\n\t\t\t * XXX: Note that the assumption we made above for the tupleslot\n\t\t\t * holds for \"unique\" constraints defined on \"btree\" indexes.\n\t\t\t *\n\t\t\t * For the other constraints that we support, namely:\n\t\t\t * * exclusion on btree,\n\t\t\t * * exclusion on hash,\n\t\t\t * * unique on btree;\n\t\t\t * we still need to fill tts_values.\n\t\t\t *\n\t\t\t * However, for the same reason, we should have already flushed\n\t\t\t * single tuple stripes when inserting into table for those three\n\t\t\t * classes of constraints.\n\t\t\t *\n\t\t\t * This is annoying, but this also explains why this hack works for\n\t\t\t * unique constraints on btree indexes, and also explains how we\n\t\t\t * would never end up with \"else\" condition otherwise.\n\t\t\t */\n\t\t\tmemset(slot->tts_isnull, true, slot->tts_nvalid * sizeof(bool));\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * At this point, we certainly know that stripe is flushed and\n\t\t * ColumnarReadRowByRowNumber successfully filled the tupleslot.\n\t\t */\n\t\tAssert(stripeWriteState == STRIPE_WRITE_FLUSHED);\n\t}\n\n\tslot->tts_tableOid = RelationGetRelid(columnarRelation);\n\tslot->tts_tid = *tid;\n\tExecStoreVirtualTuple(slot);\n\n\treturn true;\n}\n\n\nstatic bool\ncolumnar_fetch_row_version(Relation relation,\n\t\t\t\t\t\t   ItemPointer tid,\n\t\t\t\t\t\t   Snapshot snapshot,\n\t\t\t\t\t\t   TupleTableSlot *slot)\n{\n\telog(ERROR, \"columnar_fetch_row_version not implemented\");\n}\n\n\nstatic void\ncolumnar_get_latest_tid(TableScanDesc sscan,\n\t\t\t\t\t\tItemPointer tid)\n{\n\telog(ERROR, \"columnar_get_latest_tid not implemented\");\n}\n\n\nstatic bool\ncolumnar_tuple_tid_valid(TableScanDesc scan, ItemPointer tid)\n{\n\telog(ERROR, \"columnar_tuple_tid_valid not implemented\");\n}\n\n\nstatic bool\ncolumnar_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot,\n\t\t\t\t\t\t\t\t  Snapshot snapshot)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\tuint64 rowNumber = tid_to_row_number(slot->tts_tid);\n\tStripeMetadata *stripeMetadata = FindStripeByRowNumber(rel, rowNumber, snapshot);\n\treturn stripeMetadata != NULL;\n}\n\n\nstatic TransactionId\ncolumnar_index_delete_tuples(Relation rel,\n\t\t\t\t\t\t\t TM_IndexDeleteOp *delstate)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\t/*\n\t * XXX: We didn't bother implementing index_delete_tuple for neither of\n\t * simple deletion and bottom-up deletion cases. There is no particular\n\t * reason for that, just to keep things simple.\n\t *\n\t * See the rest of this function to see how we deal with\n\t * index_delete_tuples requests made to columnarAM.\n\t */\n\n\tif (delstate->bottomup)\n\t{\n\t\t/*\n\t\t * Ignore any bottom-up deletion requests.\n\t\t *\n\t\t * Currently only caller in postgres that does bottom-up deletion is\n\t\t * _bt_bottomupdel_pass, which in turn calls _bt_delitems_delete_check.\n\t\t * And this function is okay with ndeltids being set to 0 by tableAM\n\t\t * for bottom-up deletion.\n\t\t */\n\t\tdelstate->ndeltids = 0;\n\t\treturn InvalidTransactionId;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * TableAM is not expected to set ndeltids to 0 for simple deletion\n\t\t * case, so here we cannot do the same trick that we do for\n\t\t * bottom-up deletion.\n\t\t * See the assertion around table_index_delete_tuples call in pg\n\t\t * function index_compute_xid_horizon_for_tuples.\n\t\t *\n\t\t * For this reason, to avoid receiving simple deletion requests for\n\t\t * columnar tables (bottomup = false), columnar_index_fetch_tuple\n\t\t * doesn't ever set all_dead to true in order to prevent triggering\n\t\t * simple deletion of index tuples. But let's throw an error to be on\n\t\t * the safe side.\n\t\t */\n\t\telog(ERROR, \"columnar_index_delete_tuples not implemented for simple deletion\");\n\t}\n}\n\n\nstatic void\ncolumnar_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid,\n\t\t\t\t\t  int options, BulkInsertState bistate)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\t/*\n\t * columnar_init_write_state allocates the write state in a longer\n\t * lasting context, so no need to worry about it.\n\t */\n\tColumnarWriteState *writeState = columnar_init_write_state(relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   RelationGetDescr(relation),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   slot->tts_tableOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   GetCurrentSubTransactionId());\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(ColumnarWritePerTupleContext(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t writeState));\n\n\tColumnarCheckLogicalReplication(relation);\n\n\tslot_getallattrs(slot);\n\n\tDatum *values = detoast_values(slot->tts_tupleDescriptor,\n\t\t\t\t\t\t\t\t   slot->tts_values, slot->tts_isnull);\n\n\tuint64 writtenRowNumber = ColumnarWriteRow(writeState, values, slot->tts_isnull);\n\tslot->tts_tid = row_number_to_tid(writtenRowNumber);\n\n\tMemoryContextSwitchTo(oldContext);\n\tMemoryContextReset(ColumnarWritePerTupleContext(writeState));\n}\n\n\nstatic void\ncolumnar_tuple_insert_speculative(Relation relation, TupleTableSlot *slot,\n\t\t\t\t\t\t\t\t  CommandId cid, int options,\n\t\t\t\t\t\t\t\t  BulkInsertState bistate, uint32 specToken)\n{\n\telog(ERROR, \"columnar_tuple_insert_speculative not implemented\");\n}\n\n\nstatic void\ncolumnar_tuple_complete_speculative(Relation relation, TupleTableSlot *slot,\n\t\t\t\t\t\t\t\t\tuint32 specToken, bool succeeded)\n{\n\telog(ERROR, \"columnar_tuple_complete_speculative not implemented\");\n}\n\n\nstatic void\ncolumnar_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,\n\t\t\t\t\t  CommandId cid, int options, BulkInsertState bistate)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\t/*\n\t * The callback to .multi_insert is table_multi_insert() and this is only used for the COPY\n\t * command, so slot[i]->tts_tableoid will always be equal to relation->id. Thus, we can send\n\t * RelationGetRelid(relation) as the tupSlotTableOid\n\t */\n\tColumnarWriteState *writeState = columnar_init_write_state(relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   RelationGetDescr(relation),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   RelationGetRelid(relation),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   GetCurrentSubTransactionId());\n\n\tColumnarCheckLogicalReplication(relation);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(ColumnarWritePerTupleContext(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t writeState));\n\n\tfor (int i = 0; i < ntuples; i++)\n\t{\n\t\tTupleTableSlot *tupleSlot = slots[i];\n\n\t\tslot_getallattrs(tupleSlot);\n\n\t\tDatum *values = detoast_values(tupleSlot->tts_tupleDescriptor,\n\t\t\t\t\t\t\t\t\t   tupleSlot->tts_values, tupleSlot->tts_isnull);\n\n\t\tuint64 writtenRowNumber = ColumnarWriteRow(writeState, values,\n\t\t\t\t\t\t\t\t\t\t\t\t   tupleSlot->tts_isnull);\n\t\ttupleSlot->tts_tid = row_number_to_tid(writtenRowNumber);\n\n\t\tMemoryContextReset(ColumnarWritePerTupleContext(writeState));\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\nstatic TM_Result\ncolumnar_tuple_delete(Relation relation, ItemPointer tid, CommandId cid,\n\t\t\t\t\t  Snapshot snapshot, Snapshot crosscheck, bool wait,\n\t\t\t\t\t  TM_FailureData *tmfd, bool changingPart)\n{\n\telog(ERROR, \"columnar_tuple_delete not implemented\");\n}\n\n\nstatic TM_Result\ncolumnar_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot,\n\t\t\t\t\t  CommandId cid, Snapshot snapshot, Snapshot crosscheck,\n\t\t\t\t\t  bool wait, TM_FailureData *tmfd,\n\t\t\t\t\t  LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)\n{\n\telog(ERROR, \"columnar_tuple_update not implemented\");\n}\n\n\nstatic TM_Result\ncolumnar_tuple_lock(Relation relation, ItemPointer tid, Snapshot snapshot,\n\t\t\t\t\tTupleTableSlot *slot, CommandId cid, LockTupleMode mode,\n\t\t\t\t\tLockWaitPolicy wait_policy, uint8 flags,\n\t\t\t\t\tTM_FailureData *tmfd)\n{\n\telog(ERROR, \"columnar_tuple_lock not implemented\");\n}\n\n\nstatic void\ncolumnar_finish_bulk_insert(Relation relation, int options)\n{\n\t/*\n\t * Nothing to do here. We keep write states live until transaction end.\n\t */\n}\n\n\nstatic void\ncolumnar_relation_set_new_filelocator(Relation rel,\n\t\t\t\t\t\t\t\t\t  const RelFileLocator *newrlocator,\n\t\t\t\t\t\t\t\t\t  char persistence,\n\t\t\t\t\t\t\t\t\t  TransactionId *freezeXid,\n\t\t\t\t\t\t\t\t\t  MultiXactId *minmulti)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\tif (persistence == RELPERSISTENCE_UNLOGGED)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"unlogged columnar tables are not supported\")));\n\t}\n\n\t/*\n\t * If existing and new relfilenode are different, that means the existing\n\t * storage was dropped and we also need to clean up the metadata and\n\t * state. If they are equal, this is a new relation object and we don't\n\t * need to clean anything.\n\t */\n\tif (rel->rd_locator.relNumber != newrlocator->relNumber)\n\t{\n\t\tMarkRelfilenumberDropped(rel->rd_locator.relNumber,\n\t\t\t\t\t\t\t\t GetCurrentSubTransactionId());\n\n\t\tDeleteMetadataRows(rel);\n\t}\n\n\t*freezeXid = RecentXmin;\n\t*minmulti = GetOldestMultiXactId();\n\tSMgrRelation srel = RelationCreateStorage(*newrlocator, persistence, true);\n\n\tColumnarStorageInit(srel, ColumnarMetadataNewStorageId());\n\tInitColumnarOptions(rel->rd_id);\n\n\tsmgrclose(srel);\n\n\t/* we will lazily initialize metadata in first stripe reservation */\n}\n\n\nstatic void\ncolumnar_relation_nontransactional_truncate(Relation rel)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\tRelFileLocator relfilelocator = rel->rd_locator;\n\n\tNonTransactionDropWriteState(relfilelocator.relNumber);\n\n\t/* Delete old relfilenode metadata */\n\tDeleteMetadataRows(rel);\n\n\t/*\n\t * No need to set new relfilenode, since the table was created in this\n\t * transaction and no other transaction can see this relation yet. We\n\t * can just truncate the relation.\n\t *\n\t * This is similar to what is done in heapam_relation_nontransactional_truncate.\n\t */\n\tRelationTruncate(rel, 0);\n\n\tuint64 storageId = ColumnarMetadataNewStorageId();\n\tColumnarStorageInit(RelationGetSmgr(rel), storageId);\n}\n\n\nstatic void\ncolumnar_relation_copy_data(Relation rel, const RelFileLocator *newrnode)\n{\n\telog(ERROR, \"columnar_relation_copy_data not implemented\");\n}\n\n\n/*\n * columnar_relation_copy_for_cluster is called on VACUUM FULL, at which\n * we should copy data from OldHeap to NewHeap.\n *\n * In general TableAM case this can also be called for the CLUSTER command\n * which is not applicable for columnar since it doesn't support indexes.\n */\nstatic void\ncolumnar_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,\n\t\t\t\t\t\t\t\t   Relation OldIndex, bool use_sort,\n\t\t\t\t\t\t\t\t   TransactionId OldestXmin,\n\t\t\t\t\t\t\t\t   TransactionId *xid_cutoff,\n\t\t\t\t\t\t\t\t   MultiXactId *multi_cutoff,\n\t\t\t\t\t\t\t\t   double *num_tuples,\n\t\t\t\t\t\t\t\t   double *tups_vacuumed,\n\t\t\t\t\t\t\t\t   double *tups_recently_dead)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\tTupleDesc sourceDesc = RelationGetDescr(OldHeap);\n\tTupleDesc targetDesc = RelationGetDescr(NewHeap);\n\n\tif (OldIndex != NULL || use_sort)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"clustering columnar tables using indexes is \"\n\t\t\t\t\t\t\t   \"not supported\")));\n\t}\n\n\t/*\n\t * copy_table_data in cluster.c assumes tuple descriptors are exactly\n\t * the same. Even dropped columns exist and are marked as attisdropped\n\t * in the target relation.\n\t */\n\tAssert(sourceDesc->natts == targetDesc->natts);\n\n\t/* read settings from old heap, relfilenode will be swapped at the end */\n\tColumnarOptions columnarOptions = { 0 };\n\tReadColumnarOptions(OldHeap->rd_id, &columnarOptions);\n\n\tColumnarWriteState *writeState = ColumnarBeginWrite(NewHeap,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolumnarOptions,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetDesc);\n\n\t/* we need all columns */\n\tint natts = OldHeap->rd_att->natts;\n\tBitmapset *attr_needed = bms_add_range(NULL, 0, natts - 1);\n\n\t/* no quals for table rewrite */\n\tList *scanQual = NIL;\n\n\t/* use SnapshotAny when re-writing table as heapAM does */\n\tSnapshot snapshot = SnapshotAny;\n\n\tMemoryContext scanContext = CreateColumnarScanMemoryContext();\n\tbool randomAccess = false;\n\tColumnarReadState *readState = init_columnar_read_state(OldHeap, sourceDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tattr_needed, scanQual,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tscanContext, snapshot,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trandomAccess);\n\n\tDatum *values = palloc0(sourceDesc->natts * sizeof(Datum));\n\tbool *nulls = palloc0(sourceDesc->natts * sizeof(bool));\n\n\t*num_tuples = 0;\n\n\t/* we don't need to know rowNumber here */\n\twhile (ColumnarReadNextRow(readState, values, nulls, NULL))\n\t{\n\t\tColumnarWriteRow(writeState, values, nulls);\n\t\t(*num_tuples)++;\n\t}\n\n\t*tups_vacuumed = 0;\n\n\tColumnarEndWrite(writeState);\n\tColumnarEndRead(readState);\n}\n\n\n/*\n * NeededColumnsList returns a list of AttrNumber's for the columns that\n * are not dropped and specified by attr_needed.\n */\nstatic List *\nNeededColumnsList(TupleDesc tupdesc, Bitmapset *attr_needed)\n{\n\tList *columnList = NIL;\n\n\tfor (int i = 0; i < tupdesc->natts; i++)\n\t{\n\t\tif (TupleDescAttr(tupdesc, i)->attisdropped)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* attr_needed is 0-indexed but columnList is 1-indexed */\n\t\tif (bms_is_member(i, attr_needed))\n\t\t{\n\t\t\tAttrNumber varattno = i + 1;\n\t\t\tcolumnList = lappend_int(columnList, varattno);\n\t\t}\n\t}\n\n\treturn columnList;\n}\n\n\n/*\n * ColumnarTableTupleCount returns the number of tuples that columnar\n * table with relationId has by using stripe metadata.\n */\nstatic uint64\nColumnarTableTupleCount(Relation relation)\n{\n\tList *stripeList = StripesForRelfilelocator(relation);\n\tuint64 tupleCount = 0;\n\n\tListCell *lc = NULL;\n\tforeach(lc, stripeList)\n\t{\n\t\tStripeMetadata *stripe = lfirst(lc);\n\t\ttupleCount += stripe->rowCount;\n\t}\n\n\treturn tupleCount;\n}\n\n\n/*\n * columnar_vacuum_rel implements VACUUM without FULL option.\n */\nstatic void\ncolumnar_vacuum_rel(Relation rel, VacuumParams *params,\n\t\t\t\t\tBufferAccessStrategy bstrategy)\n{\n\tif (!CheckCitusColumnarVersion(WARNING))\n\t{\n\t\t/*\n\t\t * Skip if the extension catalogs are not up-to-date, but avoid\n\t\t * erroring during auto-vacuum.\n\t\t */\n\t\treturn;\n\t}\n\n\tpgstat_progress_start_command(PROGRESS_COMMAND_VACUUM,\n\t\t\t\t\t\t\t\t  RelationGetRelid(rel));\n\n\t/*\n\t * If metapage version of relation is older, then we hint users to VACUUM\n\t * the relation in ColumnarMetapageCheckVersion. So if needed, upgrade\n\t * the metapage before doing anything.\n\t */\n\tbool isUpgrade = true;\n\tColumnarStorageUpdateIfNeeded(rel, isUpgrade);\n\n\tint elevel = (params->options & VACOPT_VERBOSE) ? INFO : DEBUG2;\n\n\t/* this should have been resolved by vacuum.c until now */\n\tAssert(params->truncate != VACOPTVALUE_UNSPECIFIED);\n\n\tLogRelationStats(rel, elevel);\n\n\t/*\n\t * We don't have updates, deletes, or concurrent updates, so all we\n\t * care for now is truncating the unused space at the end of storage.\n\t */\n\tif (params->truncate == VACOPTVALUE_ENABLED)\n\t{\n\t\tTruncateColumnar(rel, elevel);\n\t}\n\n\tBlockNumber new_rel_pages = smgrnblocks(RelationGetSmgr(rel), MAIN_FORKNUM);\n\n\t/* get the number of indexes */\n\tList *indexList = RelationGetIndexList(rel);\n\tint nindexes = list_length(indexList);\n\n\tstruct VacuumCutoffs cutoffs;\n\tvacuum_get_cutoffs(rel, params, &cutoffs);\n\n\tAssert(MultiXactIdPrecedesOrEquals(cutoffs.MultiXactCutoff, cutoffs.OldestMxact));\n\tAssert(TransactionIdPrecedesOrEquals(cutoffs.FreezeLimit, cutoffs.OldestXmin));\n\n\t/*\n\t * Columnar storage doesn't hold any transaction IDs, so we can always\n\t * just advance to the most aggressive value.\n\t */\n\tTransactionId newRelFrozenXid = cutoffs.OldestXmin;\n\tMultiXactId newRelminMxid = cutoffs.OldestMxact;\n\tdouble new_live_tuples = ColumnarTableTupleCount(rel);\n\n\t/* all visible pages are always 0 */\n\tBlockNumber new_rel_allvisible = 0;\n\n\tbool frozenxid_updated;\n\tbool minmulti_updated;\n\n/* for PG 18+, vac_update_relstats gained a new “all_frozen” param */\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t/* all frozen pages are always 0, because columnar stripes never store XIDs */\n\tBlockNumber new_rel_allfrozen = 0;\n\n\tvac_update_relstats(rel, new_rel_pages, new_live_tuples,\n\t\t\t\t\t\tnew_rel_allvisible,  /* allvisible */\n\t\t\t\t\t\tnew_rel_allfrozen,   /* all_frozen */\n\t\t\t\t\t\tnindexes > 0,\n\t\t\t\t\t\tnewRelFrozenXid, newRelminMxid,\n\t\t\t\t\t\t&frozenxid_updated, &minmulti_updated,\n\t\t\t\t\t\tfalse);\n#else\n\tvac_update_relstats(rel, new_rel_pages, new_live_tuples,\n\t\t\t\t\t\tnew_rel_allvisible, nindexes > 0,\n\t\t\t\t\t\tnewRelFrozenXid, newRelminMxid,\n\t\t\t\t\t\t&frozenxid_updated, &minmulti_updated,\n\t\t\t\t\t\tfalse);\n#endif\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\tpgstat_report_vacuum(RelationGetRelid(rel),\n\t\t\t\t\t\t rel->rd_rel->relisshared,\n\t\t\t\t\t\t Max(new_live_tuples, 0), /* live tuples */\n\t\t\t\t\t\t 0,                       /* dead tuples */\n\t\t\t\t\t\t GetCurrentTimestamp());  /* start time */\n#else\n\tpgstat_report_vacuum(RelationGetRelid(rel),\n\t\t\t\t\t\t rel->rd_rel->relisshared,\n\t\t\t\t\t\t Max(new_live_tuples, 0),\n\t\t\t\t\t\t 0);\n#endif\n\n\tpgstat_progress_end_command();\n}\n\n\n/*\n * LogRelationStats logs statistics as the output of the VACUUM VERBOSE.\n */\nstatic void\nLogRelationStats(Relation rel, int elevel)\n{\n\tListCell *stripeMetadataCell = NULL;\n\tStringInfo infoBuf = makeStringInfo();\n\n\tint compressionStats[COMPRESSION_COUNT] = { 0 };\n\tuint64 totalStripeLength = 0;\n\tuint64 tupleCount = 0;\n\tuint64 chunkCount = 0;\n\tTupleDesc tupdesc = RelationGetDescr(rel);\n\tuint64 droppedChunksWithData = 0;\n\tuint64 totalDecompressedLength = 0;\n\n\tList *stripeList = StripesForRelfilelocator(rel);\n\tint stripeCount = list_length(stripeList);\n\n\tforeach(stripeMetadataCell, stripeList)\n\t{\n\t\tStripeMetadata *stripe = lfirst(stripeMetadataCell);\n\n\t\tSnapshot snapshot = RegisterSnapshot(GetTransactionSnapshot());\n\t\tStripeSkipList *skiplist = ReadStripeSkipList(rel, stripe->id,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  RelationGetDescr(rel),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  stripe->chunkCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  snapshot);\n\t\tUnregisterSnapshot(snapshot);\n\n\t\tfor (uint32 column = 0; column < skiplist->columnCount; column++)\n\t\t{\n\t\t\tbool attrDropped = TupleDescAttr(tupdesc, column)->attisdropped;\n\t\t\tfor (uint32 chunk = 0; chunk < skiplist->chunkCount; chunk++)\n\t\t\t{\n\t\t\t\tColumnChunkSkipNode *skipnode =\n\t\t\t\t\t&skiplist->chunkSkipNodeArray[column][chunk];\n\n\t\t\t\t/* ignore zero length chunks for dropped attributes */\n\t\t\t\tif (skipnode->valueLength > 0)\n\t\t\t\t{\n\t\t\t\t\tcompressionStats[skipnode->valueCompressionType]++;\n\t\t\t\t\tchunkCount++;\n\n\t\t\t\t\tif (attrDropped)\n\t\t\t\t\t{\n\t\t\t\t\t\tdroppedChunksWithData++;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * We don't compress exists buffer, so its compressed & decompressed\n\t\t\t\t * lengths are the same.\n\t\t\t\t */\n\t\t\t\ttotalDecompressedLength += skipnode->existsLength;\n\t\t\t\ttotalDecompressedLength += skipnode->decompressedValueSize;\n\t\t\t}\n\t\t}\n\n\t\ttupleCount += stripe->rowCount;\n\t\ttotalStripeLength += stripe->dataLength;\n\t}\n\n\tuint64 relPages = smgrnblocks(RelationGetSmgr(rel), MAIN_FORKNUM);\n\tRelationCloseSmgr(rel);\n\n\tDatum storageId = DirectFunctionCall1(columnar_relation_storageid,\n\t\t\t\t\t\t\t\t\t\t  ObjectIdGetDatum(RelationGetRelid(rel)));\n\n\tdouble compressionRate = totalStripeLength ?\n\t\t\t\t\t\t\t (double) totalDecompressedLength / totalStripeLength :\n\t\t\t\t\t\t\t 1.0;\n\n\tappendStringInfo(infoBuf, \"storage id: %ld\\n\", DatumGetInt64(storageId));\n\tappendStringInfo(infoBuf, \"total file size: %ld, total data size: %ld\\n\",\n\t\t\t\t\t relPages * BLCKSZ, totalStripeLength);\n\tappendStringInfo(infoBuf, \"compression rate: %.2fx\\n\", compressionRate);\n\tappendStringInfo(infoBuf,\n\t\t\t\t\t \"total row count: %ld, stripe count: %d, \"\n\t\t\t\t\t \"average rows per stripe: %ld\\n\",\n\t\t\t\t\t tupleCount, stripeCount,\n\t\t\t\t\t stripeCount ? tupleCount / stripeCount : 0);\n\tappendStringInfo(infoBuf,\n\t\t\t\t\t \"chunk count: %ld\"\n\t\t\t\t\t \", containing data for dropped columns: %ld\",\n\t\t\t\t\t chunkCount, droppedChunksWithData);\n\tfor (int compressionType = 0; compressionType < COMPRESSION_COUNT; compressionType++)\n\t{\n\t\tconst char *compressionName = CompressionTypeStr(compressionType);\n\n\t\t/* skip if this compression algorithm has not been compiled */\n\t\tif (compressionName == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* skip if no chunks use this compression type */\n\t\tif (compressionStats[compressionType] == 0)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tappendStringInfo(infoBuf,\n\t\t\t\t\t\t \", %s compressed: %d\",\n\t\t\t\t\t\t compressionName,\n\t\t\t\t\t\t compressionStats[compressionType]);\n\t}\n\tappendStringInfoString(infoBuf, \"\\n\");\n\n\tereport(elevel, (errmsg(\"statistics for \\\"%s\\\":\\n%s\", RelationGetRelationName(rel),\n\t\t\t\t\t\t\tinfoBuf->data)));\n}\n\n\n/*\n * TruncateColumnar truncates the unused space at the end of main fork for\n * a columnar table. This unused space can be created by aborted transactions.\n *\n * This implementation is based on heap_vacuum_rel in vacuumlazy.c with some\n * changes so it suits columnar store relations.\n */\nstatic void\nTruncateColumnar(Relation rel, int elevel)\n{\n\tPGRUsage ru0;\n\n\tpg_rusage_init(&ru0);\n\n\t/* Report that we are now truncating */\n\tpgstat_progress_update_param(PROGRESS_VACUUM_PHASE,\n\t\t\t\t\t\t\t\t PROGRESS_VACUUM_PHASE_TRUNCATE);\n\n\n\t/*\n\t * We need access exclusive lock on the relation in order to do\n\t * truncation. If we can't get it, give up rather than waiting --- we\n\t * don't want to block other backends, and we don't want to deadlock\n\t * (which is quite possible considering we already hold a lower-grade\n\t * lock).\n\t *\n\t * The decisions for AccessExclusiveLock and conditional lock with\n\t * a timeout is based on lazy_truncate_heap in vacuumlazy.c.\n\t */\n\tif (!ConditionalLockRelationWithTimeout(rel, AccessExclusiveLock,\n\t\t\t\t\t\t\t\t\t\t\tVACUUM_TRUNCATE_LOCK_TIMEOUT,\n\t\t\t\t\t\t\t\t\t\t\tVACUUM_TRUNCATE_LOCK_WAIT_INTERVAL))\n\t{\n\t\t/*\n\t\t * We failed to establish the lock in the specified number of\n\t\t * retries. This means we give up truncating.\n\t\t */\n\t\tereport(elevel,\n\t\t\t\t(errmsg(\"\\\"%s\\\": stopping truncate due to conflicting lock request\",\n\t\t\t\t\t\tRelationGetRelationName(rel))));\n\t\treturn;\n\t}\n\n\t/*\n\t * Due to the AccessExclusive lock there's no danger that\n\t * new stripes be added beyond highestPhysicalAddress while\n\t * we're truncating.\n\t */\n\tuint64 newDataReservation = Max(GetHighestUsedAddress(rel) + 1,\n\t\t\t\t\t\t\t\t\tColumnarFirstLogicalOffset);\n\n\tBlockNumber old_rel_pages = smgrnblocks(RelationGetSmgr(rel), MAIN_FORKNUM);\n\n\tif (!ColumnarStorageTruncate(rel, newDataReservation))\n\t{\n\t\tUnlockRelation(rel, AccessExclusiveLock);\n\t\treturn;\n\t}\n\n\tBlockNumber new_rel_pages = smgrnblocks(RelationGetSmgr(rel), MAIN_FORKNUM);\n\n\t/*\n\t * We can release the exclusive lock as soon as we have truncated.\n\t * Other backends can't safely access the relation until they have\n\t * processed the smgr invalidation that smgrtruncate sent out ... but\n\t * that should happen as part of standard invalidation processing once\n\t * they acquire lock on the relation.\n\t */\n\tUnlockRelation(rel, AccessExclusiveLock);\n\n\tereport(elevel,\n\t\t\t(errmsg(\"\\\"%s\\\": truncated %u to %u pages\",\n\t\t\t\t\tRelationGetRelationName(rel),\n\t\t\t\t\told_rel_pages, new_rel_pages),\n\t\t\t errdetail_internal(\"%s\", pg_rusage_show(&ru0))));\n}\n\n\n/*\n * ConditionalLockRelationWithTimeout tries to acquire a relation lock until\n * it either succeeds or timesout. It doesn't enter wait queue and instead it\n * sleeps between lock tries.\n *\n * This is based on the lock loop in lazy_truncate_heap().\n */\nstatic bool\nConditionalLockRelationWithTimeout(Relation rel, LOCKMODE lockMode, int timeout,\n\t\t\t\t\t\t\t\t   int retryInterval)\n{\n\tint lock_retry = 0;\n\n\twhile (true)\n\t{\n\t\tif (ConditionalLockRelation(rel, lockMode))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t/*\n\t\t * Check for interrupts while trying to (re-)acquire the lock\n\t\t */\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tif (++lock_retry > (timeout / retryInterval))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tpg_usleep(retryInterval * 1000L);\n\t}\n\n\treturn true;\n}\n\n\nstatic bool\ncolumnar_scan_analyze_next_block(TableScanDesc scan,\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\t\t\t\t\t\t\t ReadStream *stream)\n#else\n\t\t\t\t\t\t\t\t BlockNumber blockno,\n\t\t\t\t\t\t\t\t BufferAccessStrategy bstrategy)\n#endif\n{\n\t/*\n\t * Our access method is not pages based, i.e. tuples are not confined\n\t * to pages boundaries. So not much to do here. We return true anyway\n\t * so acquire_sample_rows() in analyze.c would call our\n\t * columnar_scan_analyze_next_tuple() callback.\n\t * In PG17, we return false in case there is no buffer left, since\n\t * the outer loop changed in acquire_sample_rows(), and it is\n\t * expected for the scan_analyze_next_block function to check whether\n\t * there are any blocks left in the block sampler.\n\t */\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tBuffer buf = read_stream_next_buffer(stream, NULL);\n\tif (!BufferIsValid(buf))\n\t{\n\t\treturn false;\n\t}\n\tReleaseBuffer(buf);\n#endif\n\treturn true;\n}\n\n\nstatic bool\ncolumnar_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,\n\t\t\t\t\t\t\t\t double *liverows, double *deadrows,\n\t\t\t\t\t\t\t\t TupleTableSlot *slot)\n{\n\t/*\n\t * Currently we don't do anything smart to reduce number of rows returned\n\t * for ANALYZE. The TableAM API's ANALYZE functions are designed for page\n\t * based access methods where it chooses random pages, and then reads\n\t * tuples from those pages.\n\t *\n\t * We could do something like that here by choosing sample stripes or chunks,\n\t * but getting that correct might need quite some work. Since columnar_fdw's\n\t * ANALYZE scanned all rows, as a starter we do the same here and scan all\n\t * rows.\n\t */\n\tif (columnar_getnextslot(scan, ForwardScanDirection, slot))\n\t{\n\t\t(*liverows)++;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nstatic double\ncolumnar_index_build_range_scan(Relation columnarRelation,\n\t\t\t\t\t\t\t\tRelation indexRelation,\n\t\t\t\t\t\t\t\tIndexInfo *indexInfo,\n\t\t\t\t\t\t\t\tbool allow_sync,\n\t\t\t\t\t\t\t\tbool anyvisible,\n\t\t\t\t\t\t\t\tbool progress,\n\t\t\t\t\t\t\t\tBlockNumber start_blockno,\n\t\t\t\t\t\t\t\tBlockNumber numblocks,\n\t\t\t\t\t\t\t\tIndexBuildCallback callback,\n\t\t\t\t\t\t\t\tvoid *callback_state,\n\t\t\t\t\t\t\t\tTableScanDesc scan)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\tif (start_blockno != 0 || numblocks != InvalidBlockNumber)\n\t{\n\t\t/*\n\t\t * Columnar utility hook already errors out for BRIN indexes on columnar\n\t\t * tables, but be on the safe side.\n\t\t */\n\t\tereport(ERROR, (errmsg(\"BRIN indexes on columnar tables are not supported\")));\n\t}\n\n\tif (scan)\n\t{\n\t\t/*\n\t\t * Parallel scans on columnar tables are already discardad by\n\t\t * ColumnarGetRelationInfoHook but be on the safe side.\n\t\t */\n\t\telog(ERROR, \"parallel scans on columnar are not supported\");\n\t}\n\n\t/*\n\t * In a normal index build, we use SnapshotAny to retrieve all tuples. In\n\t * a concurrent build or during bootstrap, we take a regular MVCC snapshot\n\t * and index whatever's live according to that.\n\t */\n\tTransactionId OldestXmin = InvalidTransactionId;\n\tif (!IsBootstrapProcessingMode() && !indexInfo->ii_Concurrent)\n\t{\n\t\t/* ignore lazy VACUUM's */\n\t\tOldestXmin = GetOldestNonRemovableTransactionId(columnarRelation);\n\t}\n\n\tSnapshot snapshot = { 0 };\n\tbool snapshotRegisteredByUs = false;\n\n\t/*\n\t * For serial index build, we begin our own scan. We may also need to\n\t * register a snapshot whose lifetime is under our direct control.\n\t */\n\tif (!TransactionIdIsValid(OldestXmin))\n\t{\n\t\tsnapshot = RegisterSnapshot(GetTransactionSnapshot());\n\t\tsnapshotRegisteredByUs = true;\n\t}\n\telse\n\t{\n\t\tsnapshot = SnapshotAny;\n\t}\n\n\tint nkeys = 0;\n\tScanKeyData *scanKey = NULL;\n\tbool allowAccessStrategy = true;\n\tscan = table_beginscan_strat(columnarRelation, snapshot, nkeys, scanKey,\n\t\t\t\t\t\t\t\t allowAccessStrategy, allow_sync);\n\n\tif (progress)\n\t{\n\t\tColumnarReportTotalVirtualBlocks(columnarRelation, snapshot,\n\t\t\t\t\t\t\t\t\t\t PROGRESS_SCAN_BLOCKS_TOTAL);\n\t}\n\n\t/*\n\t * Set up execution state for predicate, if any.\n\t * Note that this is only useful for partial indexes.\n\t */\n\tEState *estate = CreateExecutorState();\n\tExprContext *econtext = GetPerTupleExprContext(estate);\n\tecontext->ecxt_scantuple = table_slot_create(columnarRelation, NULL);\n\tExprState *predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);\n\n\tdouble reltuples = ColumnarReadRowsIntoIndex(scan, indexRelation, indexInfo,\n\t\t\t\t\t\t\t\t\t\t\t\t progress, callback, callback_state,\n\t\t\t\t\t\t\t\t\t\t\t\t estate, predicate);\n\ttable_endscan(scan);\n\n\tif (progress)\n\t{\n\t\t/* report the last \"virtual\" block as \"done\" */\n\t\tColumnarReportTotalVirtualBlocks(columnarRelation, snapshot,\n\t\t\t\t\t\t\t\t\t\t PROGRESS_SCAN_BLOCKS_DONE);\n\t}\n\n\tif (snapshotRegisteredByUs)\n\t{\n\t\tUnregisterSnapshot(snapshot);\n\t}\n\n\tExecDropSingleTupleTableSlot(econtext->ecxt_scantuple);\n\tFreeExecutorState(estate);\n\tindexInfo->ii_ExpressionsState = NIL;\n\tindexInfo->ii_PredicateState = NULL;\n\n\treturn reltuples;\n}\n\n\n/*\n * ColumnarReportTotalVirtualBlocks reports progress for index build based on\n * number of \"virtual\" blocks that given relation has.\n * \"progressArrIndex\" argument determines which entry in st_progress_param\n * array should be updated. In this case, we only expect PROGRESS_SCAN_BLOCKS_TOTAL\n * or PROGRESS_SCAN_BLOCKS_DONE to specify whether we want to report calculated\n * number of blocks as \"done\" or as \"total\" number of \"virtual\" blocks to scan.\n */\nstatic void\nColumnarReportTotalVirtualBlocks(Relation relation, Snapshot snapshot,\n\t\t\t\t\t\t\t\t int progressArrIndex)\n{\n\t/*\n\t * Indeed, columnar tables might have gaps between row numbers, e.g\n\t * due to aborted transactions etc. Also, ItemPointer BlockNumber's\n\t * for columnar tables don't actually correspond to actual disk blocks\n\t * as in heapAM. For this reason, we call them as \"virtual\" blocks. At\n\t * the moment, we believe it is better to report our progress based on\n\t * this \"virtual\" block concept instead of doing nothing.\n\t */\n\tAssert(progressArrIndex == PROGRESS_SCAN_BLOCKS_TOTAL ||\n\t\t   progressArrIndex == PROGRESS_SCAN_BLOCKS_DONE);\n\tBlockNumber nvirtualBlocks =\n\t\tColumnarGetNumberOfVirtualBlocks(relation, snapshot);\n\tpgstat_progress_update_param(progressArrIndex, nvirtualBlocks);\n}\n\n\n/*\n * ColumnarGetNumberOfVirtualBlocks returns total number of \"virtual\" blocks\n * that given columnar table has based on based on ItemPointer BlockNumber's.\n */\nstatic BlockNumber\nColumnarGetNumberOfVirtualBlocks(Relation relation, Snapshot snapshot)\n{\n\tItemPointerData highestItemPointer =\n\t\tColumnarGetHighestItemPointer(relation, snapshot);\n\tif (!ItemPointerIsValid(&highestItemPointer))\n\t{\n\t\t/* table is empty according to our snapshot */\n\t\treturn 0;\n\t}\n\n\t/*\n\t * Since BlockNumber is 0-based, increment it by 1 to find the total\n\t * number of \"virtual\" blocks.\n\t */\n\treturn ItemPointerGetBlockNumber(&highestItemPointer) + 1;\n}\n\n\n/*\n * ColumnarGetHighestItemPointer returns ItemPointerData for the tuple with\n * highest tid for given relation.\n * If given relation is empty, then returns invalid item pointer.\n */\nstatic ItemPointerData\nColumnarGetHighestItemPointer(Relation relation, Snapshot snapshot)\n{\n\tStripeMetadata *stripeWithHighestRowNumber =\n\t\tFindStripeWithHighestRowNumber(relation, snapshot);\n\tif (stripeWithHighestRowNumber == NULL ||\n\t\tStripeGetHighestRowNumber(stripeWithHighestRowNumber) == 0)\n\t{\n\t\t/* table is empty according to our snapshot */\n\t\tItemPointerData invalidItemPtr;\n\t\tItemPointerSetInvalid(&invalidItemPtr);\n\t\treturn invalidItemPtr;\n\t}\n\n\tuint64 highestRowNumber = StripeGetHighestRowNumber(stripeWithHighestRowNumber);\n\treturn row_number_to_tid(highestRowNumber);\n}\n\n\n/*\n * ColumnarReadRowsIntoIndex builds indexRelation tuples by reading the\n * actual relation based on given \"scan\" and returns number of tuples\n * scanned to build the indexRelation.\n */\nstatic double\nColumnarReadRowsIntoIndex(TableScanDesc scan, Relation indexRelation,\n\t\t\t\t\t\t  IndexInfo *indexInfo, bool progress,\n\t\t\t\t\t\t  IndexBuildCallback indexCallback,\n\t\t\t\t\t\t  void *indexCallbackState, EState *estate,\n\t\t\t\t\t\t  ExprState *predicate)\n{\n\tdouble reltuples = 0;\n\n\tBlockNumber lastReportedBlockNumber = InvalidBlockNumber;\n\n\tExprContext *econtext = GetPerTupleExprContext(estate);\n\tTupleTableSlot *slot = econtext->ecxt_scantuple;\n\twhile (columnar_getnextslot(scan, ForwardScanDirection, slot))\n\t{\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tBlockNumber currentBlockNumber = ItemPointerGetBlockNumber(&slot->tts_tid);\n\t\tif (progress && lastReportedBlockNumber != currentBlockNumber)\n\t\t{\n\t\t\t/*\n\t\t\t * columnar_getnextslot guarantees that returned tuple will\n\t\t\t * always have a greater ItemPointer than the ones we fetched\n\t\t\t * before, so we directly use BlockNumber to report our progress.\n\t\t\t */\n\t\t\tAssert(lastReportedBlockNumber == InvalidBlockNumber ||\n\t\t\t\t   currentBlockNumber >= lastReportedBlockNumber);\n\t\t\tpgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE,\n\t\t\t\t\t\t\t\t\t\t currentBlockNumber);\n\t\t\tlastReportedBlockNumber = currentBlockNumber;\n\t\t}\n\n\t\tMemoryContextReset(econtext->ecxt_per_tuple_memory);\n\n\t\tif (predicate != NULL && !ExecQual(predicate, econtext))\n\t\t{\n\t\t\t/* for partial indexes, discard tuples that don't satisfy the predicate */\n\t\t\tcontinue;\n\t\t}\n\n\t\tDatum indexValues[INDEX_MAX_KEYS];\n\t\tbool indexNulls[INDEX_MAX_KEYS];\n\t\tFormIndexDatum(indexInfo, slot, estate, indexValues, indexNulls);\n\n\t\tItemPointerData itemPointerData = slot->tts_tid;\n\n\t\t/* currently, columnar tables can't have dead tuples */\n\t\tbool tupleIsAlive = true;\n\t\tindexCallback(indexRelation, &itemPointerData, indexValues, indexNulls,\n\t\t\t\t\t  tupleIsAlive, indexCallbackState);\n\n\t\treltuples++;\n\t}\n\n\treturn reltuples;\n}\n\n\nstatic void\ncolumnar_index_validate_scan(Relation columnarRelation,\n\t\t\t\t\t\t\t Relation indexRelation,\n\t\t\t\t\t\t\t IndexInfo *indexInfo,\n\t\t\t\t\t\t\t Snapshot snapshot,\n\t\t\t\t\t\t\t ValidateIndexState *\n\t\t\t\t\t\t\t validateIndexState)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\tColumnarReportTotalVirtualBlocks(columnarRelation, snapshot,\n\t\t\t\t\t\t\t\t\t PROGRESS_SCAN_BLOCKS_TOTAL);\n\n\t/*\n\t * Set up execution state for predicate, if any.\n\t * Note that this is only useful for partial indexes.\n\t */\n\tEState *estate = CreateExecutorState();\n\tExprContext *econtext = GetPerTupleExprContext(estate);\n\tecontext->ecxt_scantuple = table_slot_create(columnarRelation, NULL);\n\tExprState *predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);\n\n\tint nkeys = 0;\n\tScanKeyData *scanKey = NULL;\n\tbool allowAccessStrategy = true;\n\tbool allowSyncScan = false;\n\tTableScanDesc scan = table_beginscan_strat(columnarRelation, snapshot, nkeys, scanKey,\n\t\t\t\t\t\t\t\t\t\t\t   allowAccessStrategy, allowSyncScan);\n\n\tColumnarReadMissingRowsIntoIndex(scan, indexRelation, indexInfo, estate,\n\t\t\t\t\t\t\t\t\t predicate, validateIndexState);\n\n\ttable_endscan(scan);\n\n\t/* report the last \"virtual\" block as \"done\" */\n\tColumnarReportTotalVirtualBlocks(columnarRelation, snapshot,\n\t\t\t\t\t\t\t\t\t PROGRESS_SCAN_BLOCKS_DONE);\n\n\tExecDropSingleTupleTableSlot(econtext->ecxt_scantuple);\n\tFreeExecutorState(estate);\n\tindexInfo->ii_ExpressionsState = NIL;\n\tindexInfo->ii_PredicateState = NULL;\n}\n\n\n/*\n * ColumnarReadMissingRowsIntoIndex inserts the tuples that are not in\n * the index yet by reading the actual relation based on given \"scan\".\n */\nstatic void\nColumnarReadMissingRowsIntoIndex(TableScanDesc scan, Relation indexRelation,\n\t\t\t\t\t\t\t\t IndexInfo *indexInfo, EState *estate,\n\t\t\t\t\t\t\t\t ExprState *predicate,\n\t\t\t\t\t\t\t\t ValidateIndexState *validateIndexState)\n{\n\tBlockNumber lastReportedBlockNumber = InvalidBlockNumber;\n\n\tbool indexTupleSortEmpty = false;\n\tItemPointerData indexedItemPointerData;\n\tItemPointerSetInvalid(&indexedItemPointerData);\n\n\tExprContext *econtext = GetPerTupleExprContext(estate);\n\tTupleTableSlot *slot = econtext->ecxt_scantuple;\n\twhile (columnar_getnextslot(scan, ForwardScanDirection, slot))\n\t{\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tItemPointer columnarItemPointer = &slot->tts_tid;\n\t\tBlockNumber currentBlockNumber = ItemPointerGetBlockNumber(columnarItemPointer);\n\t\tif (lastReportedBlockNumber != currentBlockNumber)\n\t\t{\n\t\t\t/*\n\t\t\t * columnar_getnextslot guarantees that returned tuple will\n\t\t\t * always have a greater ItemPointer than the ones we fetched\n\t\t\t * before, so we directly use BlockNumber to report our progress.\n\t\t\t */\n\t\t\tAssert(lastReportedBlockNumber == InvalidBlockNumber ||\n\t\t\t\t   currentBlockNumber >= lastReportedBlockNumber);\n\t\t\tpgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE,\n\t\t\t\t\t\t\t\t\t\t currentBlockNumber);\n\t\t\tlastReportedBlockNumber = currentBlockNumber;\n\t\t}\n\n\t\tvalidateIndexState->htups += 1;\n\n\t\tif (!indexTupleSortEmpty &&\n\t\t\t(!ItemPointerIsValid(&indexedItemPointerData) ||\n\t\t\t ItemPointerCompare(&indexedItemPointerData, columnarItemPointer) < 0))\n\t\t{\n\t\t\t/*\n\t\t\t * Skip indexed item pointers until we find or pass the current\n\t\t\t * columnar relation item pointer.\n\t\t\t */\n\t\t\tindexedItemPointerData =\n\t\t\t\tTupleSortSkipSmallerItemPointers(validateIndexState->tuplesort,\n\t\t\t\t\t\t\t\t\t\t\t\t columnarItemPointer);\n\t\t\tindexTupleSortEmpty = !ItemPointerIsValid(&indexedItemPointerData);\n\t\t}\n\n\t\tif (!indexTupleSortEmpty &&\n\t\t\tItemPointerCompare(&indexedItemPointerData, columnarItemPointer) == 0)\n\t\t{\n\t\t\t/* tuple is already covered by the index, skip */\n\t\t\tcontinue;\n\t\t}\n\t\tAssert(indexTupleSortEmpty ||\n\t\t\t   ItemPointerCompare(&indexedItemPointerData, columnarItemPointer) > 0);\n\n\t\tMemoryContextReset(econtext->ecxt_per_tuple_memory);\n\n\t\tif (predicate != NULL && !ExecQual(predicate, econtext))\n\t\t{\n\t\t\t/* for partial indexes, discard tuples that don't satisfy the predicate */\n\t\t\tcontinue;\n\t\t}\n\n\t\tDatum indexValues[INDEX_MAX_KEYS];\n\t\tbool indexNulls[INDEX_MAX_KEYS];\n\t\tFormIndexDatum(indexInfo, slot, estate, indexValues, indexNulls);\n\n\t\tRelation columnarRelation = scan->rs_rd;\n\t\tIndexUniqueCheck indexUniqueCheck =\n\t\t\tindexInfo->ii_Unique ? UNIQUE_CHECK_YES : UNIQUE_CHECK_NO;\n\t\tindex_insert(indexRelation, indexValues, indexNulls, columnarItemPointer,\n\t\t\t\t\t columnarRelation, indexUniqueCheck, false, indexInfo);\n\n\t\tvalidateIndexState->tups_inserted += 1;\n\t}\n}\n\n\n/*\n * TupleSortSkipSmallerItemPointers iterates given tupleSort until finding an\n * ItemPointer that is greater than or equal to given targetItemPointer and\n * returns that ItemPointer.\n * If such an ItemPointer does not exist, then returns invalid ItemPointer.\n *\n * Note that this function assumes given tupleSort doesn't have any NULL\n * Datum's.\n */\nstatic ItemPointerData\nTupleSortSkipSmallerItemPointers(Tuplesortstate *tupleSort, ItemPointer targetItemPointer)\n{\n\tItemPointerData tsItemPointerData;\n\tItemPointerSetInvalid(&tsItemPointerData);\n\n\twhile (!ItemPointerIsValid(&tsItemPointerData) ||\n\t\t   ItemPointerCompare(&tsItemPointerData, targetItemPointer) < 0)\n\t{\n\t\tbool forwardDirection = true;\n\t\tDatum *abbrev = NULL;\n\t\tDatum tsDatum;\n\t\tbool tsDatumIsNull;\n\t\tif (!tuplesort_getdatum(tupleSort, forwardDirection, false,\n\t\t\t\t\t\t\t\t&tsDatum, &tsDatumIsNull, abbrev))\n\t\t{\n\t\t\tItemPointerSetInvalid(&tsItemPointerData);\n\t\t\tbreak;\n\t\t}\n\n\t\tAssert(!tsDatumIsNull);\n\t\titemptr_decode(&tsItemPointerData, DatumGetInt64(tsDatum));\n\n#ifndef USE_FLOAT8_BYVAL\n\n\t\t/*\n\t\t * If int8 is pass-by-ref, we need to free Datum memory.\n\t\t * See tuplesort_getdatum function's comment.\n\t\t */\n\t\tpfree(DatumGetPointer(tsDatum));\n#endif\n\t}\n\n\treturn tsItemPointerData;\n}\n\n\nstatic uint64\ncolumnar_relation_size(Relation rel, ForkNumber forkNumber)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\tuint64 nblocks = 0;\n\n\t/* InvalidForkNumber indicates returning the size for all forks */\n\tif (forkNumber == InvalidForkNumber)\n\t{\n\t\tfor (int i = 0; i < MAX_FORKNUM; i++)\n\t\t{\n\t\t\tnblocks += smgrnblocks(RelationGetSmgr(rel), i);\n\t\t}\n\t}\n\telse\n\t{\n\t\tnblocks = smgrnblocks(RelationGetSmgr(rel), forkNumber);\n\t}\n\n\treturn nblocks * BLCKSZ;\n}\n\n\nstatic bool\ncolumnar_relation_needs_toast_table(Relation rel)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\n\treturn false;\n}\n\n\nstatic void\ncolumnar_estimate_rel_size(Relation rel, int32 *attr_widths,\n\t\t\t\t\t\t   BlockNumber *pages, double *tuples,\n\t\t\t\t\t\t   double *allvisfrac)\n{\n\tCheckCitusColumnarVersion(ERROR);\n\t*pages = smgrnblocks(RelationGetSmgr(rel), MAIN_FORKNUM);\n\t*tuples = ColumnarTableRowCount(rel);\n\n\t/*\n\t * Append-only, so everything is visible except in-progress or rolled-back\n\t * transactions.\n\t */\n\t*allvisfrac = 1.0;\n\n\tget_rel_data_width(rel, attr_widths);\n}\n\n\nstatic bool\ncolumnar_scan_sample_next_block(TableScanDesc scan, SampleScanState *scanstate)\n{\n\telog(ERROR, \"columnar_scan_sample_next_block not implemented\");\n}\n\n\nstatic bool\ncolumnar_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,\n\t\t\t\t\t\t\t\tTupleTableSlot *slot)\n{\n\telog(ERROR, \"columnar_scan_sample_next_tuple not implemented\");\n}\n\n\nstatic void\nColumnarXactCallback(XactEvent event, void *arg)\n{\n\tswitch (event)\n\t{\n\t\tcase XACT_EVENT_COMMIT:\n\t\tcase XACT_EVENT_PARALLEL_COMMIT:\n\t\tcase XACT_EVENT_PREPARE:\n\t\t{\n\t\t\t/* nothing to do */\n\t\t\tbreak;\n\t\t}\n\n\t\tcase XACT_EVENT_ABORT:\n\t\tcase XACT_EVENT_PARALLEL_ABORT:\n\t\t{\n\t\t\tDiscardWriteStateForAllRels(GetCurrentSubTransactionId(), 0);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase XACT_EVENT_PRE_COMMIT:\n\t\tcase XACT_EVENT_PARALLEL_PRE_COMMIT:\n\t\tcase XACT_EVENT_PRE_PREPARE:\n\t\t{\n\t\t\tFlushWriteStateForAllRels(GetCurrentSubTransactionId(), 0);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\nstatic void\nColumnarSubXactCallback(SubXactEvent event, SubTransactionId mySubid,\n\t\t\t\t\t\tSubTransactionId parentSubid, void *arg)\n{\n\tswitch (event)\n\t{\n\t\tcase SUBXACT_EVENT_START_SUB:\n\t\tcase SUBXACT_EVENT_COMMIT_SUB:\n\t\t{\n\t\t\t/* nothing to do */\n\t\t\tbreak;\n\t\t}\n\n\t\tcase SUBXACT_EVENT_ABORT_SUB:\n\t\t{\n\t\t\tDiscardWriteStateForAllRels(mySubid, parentSubid);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase SUBXACT_EVENT_PRE_COMMIT_SUB:\n\t\t{\n\t\t\tFlushWriteStateForAllRels(mySubid, parentSubid);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\nvoid\ncolumnar_tableam_init()\n{\n\tRegisterXactCallback(ColumnarXactCallback, NULL);\n\tRegisterSubXactCallback(ColumnarSubXactCallback, NULL);\n\n\tPrevObjectAccessHook = object_access_hook;\n\tobject_access_hook = ColumnarTableAMObjectAccessHook;\n\n\tPrevProcessUtilityHook = ProcessUtility_hook ?\n\t\t\t\t\t\t\t ProcessUtility_hook :\n\t\t\t\t\t\t\t standard_ProcessUtility;\n\tProcessUtility_hook = ColumnarProcessUtility;\n\n\tcolumnar_customscan_init();\n\n\tTTSOpsColumnar = TTSOpsVirtual;\n\tTTSOpsColumnar.copy_heap_tuple = ColumnarSlotCopyHeapTuple;\n\tDefineCustomBoolVariable(\n\t\t\"columnar.enable_version_checks\",\n\t\tgettext_noop(\"Enables Version Check for Columnar\"),\n\t\tNULL,\n\t\t&EnableVersionChecksColumnar,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n}\n\n\n/*\n * Get the number of chunks filtered out during the given scan.\n */\nint64\nColumnarScanChunkGroupsFiltered(ColumnarScanDesc columnarScanDesc)\n{\n\tColumnarReadState *readState = columnarScanDesc->cs_readState;\n\n\t/* readState is initialized lazily */\n\tif (readState != NULL)\n\t{\n\t\treturn ColumnarReadChunkGroupsFiltered(readState);\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * Implementation of TupleTableSlotOps.copy_heap_tuple for TTSOpsColumnar.\n */\nstatic HeapTuple\nColumnarSlotCopyHeapTuple(TupleTableSlot *slot)\n{\n\tAssert(!TTS_EMPTY(slot));\n\n\tHeapTuple tuple = heap_form_tuple(slot->tts_tupleDescriptor,\n\t\t\t\t\t\t\t\t\t  slot->tts_values,\n\t\t\t\t\t\t\t\t\t  slot->tts_isnull);\n\n\t/* slot->tts_tid is filled in columnar_getnextslot */\n\ttuple->t_self = slot->tts_tid;\n\n\treturn tuple;\n}\n\n\n/*\n * ColumnarTableDropHook\n *\n * Clean-up resources for columnar tables.\n */\nstatic void\nColumnarTableDropHook(Oid relid)\n{\n\t/*\n\t * Lock relation to prevent it from being dropped and to avoid\n\t * race conditions in the next if block.\n\t */\n\tLockRelationOid(relid, AccessShareLock);\n\n\tif (IsColumnarTableAmTable(relid))\n\t{\n\t\tCheckCitusColumnarVersion(ERROR);\n\n\t\t/*\n\t\t * Drop metadata. No need to drop storage here since for\n\t\t * tableam tables storage is managed by postgres.\n\t\t */\n\t\tRelation rel = table_open(relid, AccessExclusiveLock);\n\t\tRelFileLocator relfilelocator = rel->rd_locator;\n\n\t\tDeleteMetadataRows(rel);\n\t\tDeleteColumnarTableOptions(rel->rd_id, true);\n\n\t\tMarkRelfilenumberDropped(relfilelocator.relNumber,\n\t\t\t\t\t\t\t\t GetCurrentSubTransactionId());\n\n\t\t/* keep the lock since we did physical changes to the relation */\n\t\ttable_close(rel, NoLock);\n\t}\n}\n\n\n/*\n * Reject AFTER ... FOR EACH ROW triggers on columnar tables.\n */\nstatic void\nColumnarTriggerCreateHook(Oid tgid)\n{\n\t/*\n\t * Fetch the pg_trigger tuple by the Oid of the trigger\n\t */\n\tScanKeyData skey[1];\n\tRelation tgrel = table_open(TriggerRelationId, AccessShareLock);\n\n\tScanKeyInit(&skey[0],\n\t\t\t\tAnum_pg_trigger_oid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(tgid));\n\n\tSysScanDesc tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,\n\t\t\t\t\t\t\t\t\t\t\tSnapshotSelf, 1, skey);\n\n\tHeapTuple tgtup = systable_getnext(tgscan);\n\n\tif (!HeapTupleIsValid(tgtup))\n\t{\n\t\ttable_close(tgrel, AccessShareLock);\n\t\treturn;\n\t}\n\n\tForm_pg_trigger tgrec = (Form_pg_trigger) GETSTRUCT(tgtup);\n\n\tOid tgrelid = tgrec->tgrelid;\n\tint16 tgtype = tgrec->tgtype;\n\n\tsystable_endscan(tgscan);\n\ttable_close(tgrel, AccessShareLock);\n\n\tif (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_AFTER(tgtype) &&\n\t\tIsColumnarTableAmTable(tgrelid))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Foreign keys and AFTER ROW triggers are not supported for columnar tables\"),\n\t\t\t\t\t\terrhint(\"Consider an AFTER STATEMENT trigger instead.\")));\n\t}\n}\n\n\n/*\n * Capture create/drop events and dispatch to the proper action.\n */\nstatic void\nColumnarTableAMObjectAccessHook(ObjectAccessType access, Oid classId, Oid objectId,\n\t\t\t\t\t\t\t\tint subId, void *arg)\n{\n\tif (PrevObjectAccessHook)\n\t{\n\t\tPrevObjectAccessHook(access, classId, objectId, subId, arg);\n\t}\n\n\t/* dispatch to the proper action */\n\tif (access == OAT_DROP && classId == RelationRelationId && !OidIsValid(subId))\n\t{\n\t\tColumnarTableDropHook(objectId);\n\t}\n\telse if (access == OAT_POST_CREATE && classId == TriggerRelationId)\n\t{\n\t\tColumnarTriggerCreateHook(objectId);\n\t}\n}\n\n\n/*\n * ColumnarProcessAlterTable - if modifying a columnar table, extract columnar\n * options and return the table's RangeVar.\n */\nstatic RangeVar *\nColumnarProcessAlterTable(AlterTableStmt *alterTableStmt, List **columnarOptions)\n{\n\tRangeVar *columnarRangeVar = NULL;\n\tRelation rel = relation_openrv_extended(alterTableStmt->relation, AccessShareLock,\n\t\t\t\t\t\t\t\t\t\t\talterTableStmt->missing_ok);\n\n\tif (rel == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\t/* track separately in case of ALTER TABLE ... SET ACCESS METHOD */\n\tbool srcIsColumnar = rel->rd_tableam == GetColumnarTableAmRoutine();\n\tbool destIsColumnar = srcIsColumnar;\n\n\tListCell *lc = NULL;\n\tforeach(lc, alterTableStmt->cmds)\n\t{\n\t\tAlterTableCmd *alterTableCmd = castNode(AlterTableCmd, lfirst(lc));\n\n\t\tif (alterTableCmd->subtype == AT_SetRelOptions ||\n\t\t\talterTableCmd->subtype == AT_ResetRelOptions)\n\t\t{\n\t\t\tList *options = castNode(List, alterTableCmd->def);\n\n\t\t\talterTableCmd->def = (Node *) ExtractColumnarRelOptions(\n\t\t\t\toptions, columnarOptions);\n\n\t\t\tif (destIsColumnar)\n\t\t\t{\n\t\t\t\tcolumnarRangeVar = alterTableStmt->relation;\n\t\t\t}\n\t\t}\n\t\telse if (alterTableCmd->subtype == AT_SetAccessMethod)\n\t\t{\n\t\t\tif (columnarRangeVar || *columnarOptions)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\t\"ALTER TABLE cannot alter the access method after altering storage parameters\"),\n\t\t\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\t\t\"Specify SET ACCESS METHOD before storage parameters, or use separate ALTER TABLE commands.\")));\n\t\t\t}\n\n\t\t\tdestIsColumnar = (strcmp(alterTableCmd->name ? alterTableCmd->name :\n\t\t\t\t\t\t\t\t\t default_table_access_method,\n\t\t\t\t\t\t\t\t\t COLUMNAR_AM_NAME) == 0);\n\n\t\t\tif (srcIsColumnar && !destIsColumnar)\n\t\t\t{\n\t\t\t\tDeleteColumnarTableOptions(RelationGetRelid(rel), true);\n\t\t\t}\n\t\t}\n\t}\n\n\trelation_close(rel, NoLock);\n\n\treturn columnarRangeVar;\n}\n\n\n/*\n * Utility hook for columnar tables.\n */\nstatic void\nColumnarProcessUtility(PlannedStmt *pstmt,\n\t\t\t\t\t   const char *queryString,\n\t\t\t\t\t   bool readOnlyTree,\n\t\t\t\t\t   ProcessUtilityContext context,\n\t\t\t\t\t   ParamListInfo params,\n\t\t\t\t\t   struct QueryEnvironment *queryEnv,\n\t\t\t\t\t   DestReceiver *dest,\n\t\t\t\t\t   QueryCompletion *completionTag)\n{\n\tif (readOnlyTree)\n\t{\n\t\tpstmt = copyObject(pstmt);\n\t}\n\n\tNode *parsetree = pstmt->utilityStmt;\n\n\tRangeVar *columnarRangeVar = NULL;\n\tList *columnarOptions = NIL;\n\n\tswitch (nodeTag(parsetree))\n\t{\n\t\tcase T_IndexStmt:\n\t\t{\n\t\t\tIndexStmt *indexStmt = (IndexStmt *) parsetree;\n\n\t\t\tRelation rel = relation_openrv(indexStmt->relation,\n\t\t\t\t\t\t\t\t\t\t   indexStmt->concurrent ?\n\t\t\t\t\t\t\t\t\t\t   ShareUpdateExclusiveLock :\n\t\t\t\t\t\t\t\t\t\t   ShareLock);\n\n\t\t\tif (rel->rd_tableam == GetColumnarTableAmRoutine())\n\t\t\t{\n\t\t\t\tCheckCitusColumnarVersion(ERROR);\n\t\t\t\tif (!ColumnarSupportsIndexAM(indexStmt->accessMethod))\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\terrmsg(\"unsupported access method for the \"\n\t\t\t\t\t\t\t\t\t\t   \"index on columnar table %s\",\n\t\t\t\t\t\t\t\t\t\t   RelationGetRelationName(rel))));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tRelationClose(rel);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_CreateStmt:\n\t\t{\n\t\t\tCreateStmt *createStmt = castNode(CreateStmt, parsetree);\n\t\t\tbool no_op = false;\n\n\t\t\tif (createStmt->if_not_exists)\n\t\t\t{\n\t\t\t\tOid existing_relid;\n\n\t\t\t\t/* use same check as transformCreateStmt */\n\t\t\t\t(void) RangeVarGetAndCheckCreationNamespace(\n\t\t\t\t\tcreateStmt->relation, AccessShareLock, &existing_relid);\n\n\t\t\t\tno_op = OidIsValid(existing_relid);\n\t\t\t}\n\n\t\t\tif (!no_op && createStmt->accessMethod != NULL &&\n\t\t\t\t!strcmp(createStmt->accessMethod, COLUMNAR_AM_NAME))\n\t\t\t{\n\t\t\t\tcolumnarRangeVar = createStmt->relation;\n\t\t\t\tcreateStmt->options = ExtractColumnarRelOptions(createStmt->options,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t&columnarOptions);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_CreateTableAsStmt:\n\t\t{\n\t\t\tCreateTableAsStmt *createTableAsStmt = castNode(CreateTableAsStmt, parsetree);\n\t\t\tIntoClause *into = createTableAsStmt->into;\n\t\t\tbool no_op = false;\n\n\t\t\tif (createTableAsStmt->if_not_exists)\n\t\t\t{\n\t\t\t\tOid existing_relid;\n\n\t\t\t\t/* use same check as transformCreateStmt */\n\t\t\t\t(void) RangeVarGetAndCheckCreationNamespace(\n\t\t\t\t\tinto->rel, AccessShareLock, &existing_relid);\n\n\t\t\t\tno_op = OidIsValid(existing_relid);\n\t\t\t}\n\n\t\t\tif (!no_op && into->accessMethod != NULL &&\n\t\t\t\t!strcmp(into->accessMethod, COLUMNAR_AM_NAME))\n\t\t\t{\n\t\t\t\tcolumnarRangeVar = into->rel;\n\t\t\t\tinto->options = ExtractColumnarRelOptions(into->options,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &columnarOptions);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_AlterTableStmt:\n\t\t{\n\t\t\tAlterTableStmt *alterTableStmt = castNode(AlterTableStmt, parsetree);\n\t\t\tcolumnarRangeVar = ColumnarProcessAlterTable(alterTableStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t &columnarOptions);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\t/* FALL THROUGH */\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (columnarOptions != NIL && columnarRangeVar == NULL)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"columnar storage parameters specified on non-columnar table\")));\n\t}\n\n\tif (IsA(parsetree, CreateExtensionStmt))\n\t{\n\t\tCheckCitusColumnarCreateExtensionStmt(parsetree);\n\t}\n\n\tif (IsA(parsetree, AlterExtensionStmt))\n\t{\n\t\tCheckCitusColumnarAlterExtensionStmt(parsetree);\n\t}\n\n\tPrevProcessUtilityHook(pstmt, queryString, false, context,\n\t\t\t\t\t\t   params, queryEnv, dest, completionTag);\n\n\tif (columnarOptions != NIL)\n\t{\n\t\tSetColumnarRelOptions(columnarRangeVar, columnarOptions);\n\t}\n}\n\n\n/*\n * ColumnarSupportsIndexAM returns true if indexAM with given name is\n * supported by columnar tables.\n */\nbool\nColumnarSupportsIndexAM(char *indexAMName)\n{\n\treturn strncmp(indexAMName, \"btree\", NAMEDATALEN) == 0 ||\n\t\t   strncmp(indexAMName, \"hash\", NAMEDATALEN) == 0;\n}\n\n\n/*\n * IsColumnarTableAmTable returns true if relation has columnar_tableam\n * access method. This can be called before extension creation.\n */\nbool\nIsColumnarTableAmTable(Oid relationId)\n{\n\tif (!OidIsValid(relationId))\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * Lock relation to prevent it from being dropped &\n\t * avoid race conditions.\n\t */\n\tRelation rel = relation_open(relationId, AccessShareLock);\n\tbool result = rel->rd_tableam == GetColumnarTableAmRoutine();\n\trelation_close(rel, NoLock);\n\n\treturn result;\n}\n\n\n/*\n * CheckCitusColumnarCreateExtensionStmt determines whether can install\n * citus_columnar per given CREATE extension statment\n */\nvoid\nCheckCitusColumnarCreateExtensionStmt(Node *parseTree)\n{\n\tCreateExtensionStmt *createExtensionStmt = castNode(CreateExtensionStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tparseTree);\n\tif (get_extension_oid(\"citus_columnar\", true) == InvalidOid)\n\t{\n\t\tif (strcmp(createExtensionStmt->extname, \"citus_columnar\") == 0)\n\t\t{\n\t\t\tDefElem *newVersionValue = GetExtensionOption(\n\t\t\t\tcreateExtensionStmt->options,\n\t\t\t\t\"new_version\");\n\n\t\t\t/*we are not allowed to install citus_columnar as version 11.1-0 by cx*/\n\t\t\tif (newVersionValue)\n\t\t\t{\n\t\t\t\tconst char *newVersion = defGetString(newVersionValue);\n\t\t\t\tif (strcmp(newVersion, CITUS_COLUMNAR_INTERNAL_VERSION) == 0)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\t\"unsupported citus_columnar version 11.1-0\")));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * CheckCitusColumnarAlterExtensionStmt determines whether can alter\n * citus_columnar per given ALTER extension statment\n */\nvoid\nCheckCitusColumnarAlterExtensionStmt(Node *parseTree)\n{\n\tAlterExtensionStmt *alterExtensionStmt = castNode(AlterExtensionStmt, parseTree);\n\tif (strcmp(alterExtensionStmt->extname, \"citus_columnar\") == 0)\n\t{\n\t\tDefElem *newVersionValue = GetExtensionOption(alterExtensionStmt->options,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"new_version\");\n\n\t\t/*we are not allowed cx to downgrade citus_columnar to 11.1-0*/\n\t\tif (newVersionValue)\n\t\t{\n\t\t\tconst char *newVersion = defGetString(newVersionValue);\n\t\t\tif (strcmp(newVersion, CITUS_COLUMNAR_INTERNAL_VERSION) == 0)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\terrmsg(\"unsupported citus_columnar version 11.1-0\")));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic const TableAmRoutine columnar_am_methods = {\n\t.type = T_TableAmRoutine,\n\n\t.slot_callbacks = columnar_slot_callbacks,\n\n\t.scan_begin = columnar_beginscan,\n\t.scan_end = columnar_endscan,\n\t.scan_rescan = columnar_rescan,\n\t.scan_getnextslot = columnar_getnextslot,\n\n\t.parallelscan_estimate = columnar_parallelscan_estimate,\n\t.parallelscan_initialize = columnar_parallelscan_initialize,\n\t.parallelscan_reinitialize = columnar_parallelscan_reinitialize,\n\n\t.index_fetch_begin = columnar_index_fetch_begin,\n\t.index_fetch_reset = columnar_index_fetch_reset,\n\t.index_fetch_end = columnar_index_fetch_end,\n\t.index_fetch_tuple = columnar_index_fetch_tuple,\n\n\t.tuple_fetch_row_version = columnar_fetch_row_version,\n\t.tuple_get_latest_tid = columnar_get_latest_tid,\n\t.tuple_tid_valid = columnar_tuple_tid_valid,\n\t.tuple_satisfies_snapshot = columnar_tuple_satisfies_snapshot,\n\t.index_delete_tuples = columnar_index_delete_tuples,\n\n\t.tuple_insert = columnar_tuple_insert,\n\t.tuple_insert_speculative = columnar_tuple_insert_speculative,\n\t.tuple_complete_speculative = columnar_tuple_complete_speculative,\n\t.multi_insert = columnar_multi_insert,\n\t.tuple_delete = columnar_tuple_delete,\n\t.tuple_update = columnar_tuple_update,\n\t.tuple_lock = columnar_tuple_lock,\n\t.finish_bulk_insert = columnar_finish_bulk_insert,\n\n\t.relation_set_new_filelocator = columnar_relation_set_new_filelocator,\n\t.relation_nontransactional_truncate = columnar_relation_nontransactional_truncate,\n\t.relation_copy_data = columnar_relation_copy_data,\n\t.relation_copy_for_cluster = columnar_relation_copy_for_cluster,\n\t.relation_vacuum = columnar_vacuum_rel,\n\t.scan_analyze_next_block = columnar_scan_analyze_next_block,\n\t.scan_analyze_next_tuple = columnar_scan_analyze_next_tuple,\n\t.index_build_range_scan = columnar_index_build_range_scan,\n\t.index_validate_scan = columnar_index_validate_scan,\n\n\t.relation_size = columnar_relation_size,\n\t.relation_needs_toast_table = columnar_relation_needs_toast_table,\n\n\t.relation_estimate_size = columnar_estimate_rel_size,\n\n#if PG_VERSION_NUM < PG_VERSION_18\n\n\t/* these two fields were removed in PG 18 */\n\t.scan_bitmap_next_block = NULL,\n\t.scan_bitmap_next_tuple = NULL,\n#endif\n\n\t.scan_sample_next_block = columnar_scan_sample_next_block,\n\t.scan_sample_next_tuple = columnar_scan_sample_next_tuple\n};\n\n\nconst TableAmRoutine *\nGetColumnarTableAmRoutine(void)\n{\n\treturn &columnar_am_methods;\n}\n\n\nPG_FUNCTION_INFO_V1(columnar_handler);\nDatum\ncolumnar_handler(PG_FUNCTION_ARGS)\n{\n\tPG_RETURN_POINTER(&columnar_am_methods);\n}\n\n\n/*\n * detoast_values\n *\n * Detoast and decompress all values. If there's no work to do, return\n * original pointer; otherwise return a newly-allocated values array. Should\n * be called in per-tuple context.\n */\nstatic Datum *\ndetoast_values(TupleDesc tupleDesc, Datum *orig_values, bool *isnull)\n{\n\tint natts = tupleDesc->natts;\n\n\t/* copy on write to optimize for case where nothing is toasted */\n\tDatum *values = orig_values;\n\n\tfor (int i = 0; i < tupleDesc->natts; i++)\n\t{\n\t\tif (!isnull[i] && TupleDescAttr(tupleDesc, i)->attlen == -1 &&\n\t\t\tVARATT_IS_EXTENDED(values[i]))\n\t\t{\n\t\t\t/* make a copy */\n\t\t\tif (values == orig_values)\n\t\t\t{\n\t\t\t\tvalues = palloc(sizeof(Datum) * natts);\n\n\t\t\t\t/*\n\t\t\t\t * We use IGNORE-BANNED here since we don't want to limit\n\t\t\t\t * size of the buffer that holds the datum array to RSIZE_MAX\n\t\t\t\t * unnecessarily.\n\t\t\t\t */\n\t\t\t\tmemcpy(values, orig_values, sizeof(Datum) * natts); /* IGNORE-BANNED */\n\t\t\t}\n\n\t\t\t/* will be freed when per-tuple context is reset */\n\t\t\tstruct varlena *new_value = (struct varlena *) DatumGetPointer(values[i]);\n\t\t\tnew_value = detoast_attr(new_value);\n\t\t\tvalues[i] = PointerGetDatum(new_value);\n\t\t}\n\t}\n\n\treturn values;\n}\n\n\n/*\n * ColumnarCheckLogicalReplication throws an error if the relation is\n * part of any publication. This should be called before any write to\n * a columnar table, because columnar changes are not replicated with\n * logical replication (similar to a row table without a replica\n * identity).\n */\nstatic void\nColumnarCheckLogicalReplication(Relation rel)\n{\n\tbool pubActionInsert = false;\n\n\tif (!is_publishable_relation(rel))\n\t{\n\t\treturn;\n\t}\n\n\t{\n\t\tPublicationDesc pubdesc;\n\n\t\tRelationBuildPublicationDesc(rel, &pubdesc);\n\t\tpubActionInsert = pubdesc.pubactions.pubinsert;\n\t}\n\n\tif (pubActionInsert)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"cannot insert into columnar table that is a part of a publication\")));\n\t}\n}\n\n\n/*\n * alter_columnar_table_set()\n *\n * Deprecated in 11.1-1: should issue ALTER TABLE ... SET instead. Function\n * still available, but implemented in PL/pgSQL instead of C.\n *\n * C code is removed -- the symbol may still be required in some\n * upgrade/downgrade paths, but it should not be called.\n */\nPG_FUNCTION_INFO_V1(alter_columnar_table_set);\nDatum\nalter_columnar_table_set(PG_FUNCTION_ARGS)\n{\n\telog(ERROR, \"alter_columnar_table_set is deprecated\");\n}\n\n\n/*\n * alter_columnar_table_reset()\n *\n * Deprecated in 11.1-1: should issue ALTER TABLE ... RESET instead. Function\n * still available, but implemented in PL/pgSQL instead of C.\n *\n * C code is removed -- the symbol may still be required in some\n * upgrade/downgrade paths, but it should not be called.\n */\nPG_FUNCTION_INFO_V1(alter_columnar_table_reset);\nDatum\nalter_columnar_table_reset(PG_FUNCTION_ARGS)\n{\n\telog(ERROR, \"alter_columnar_table_reset is deprecated\");\n}\n\n\n/*\n * upgrade_columnar_storage - upgrade columnar storage to the current\n * version.\n *\n * DDL:\n *   CREATE OR REPLACE FUNCTION upgrade_columnar_storage(rel regclass)\n *     RETURNS VOID\n *     STRICT\n *     LANGUAGE c AS 'MODULE_PATHNAME', 'upgrade_columnar_storage';\n */\nPG_FUNCTION_INFO_V1(upgrade_columnar_storage);\nDatum\nupgrade_columnar_storage(PG_FUNCTION_ARGS)\n{\n\tOid relid = PG_GETARG_OID(0);\n\n\t/*\n\t * ACCESS EXCLUSIVE LOCK is not required by the low-level routines, so we\n\t * can take only an ACCESS SHARE LOCK. But all access to non-current\n\t * columnar tables will fail anyway, so it's better to take ACCESS\n\t * EXCLUSIVE LOCK now.\n\t */\n\tRelation rel = table_open(relid, AccessExclusiveLock);\n\tif (!IsColumnarTableAmTable(relid))\n\t{\n\t\tereport(ERROR, (errmsg(\"table %s is not a columnar table\",\n\t\t\t\t\t\t\t   quote_identifier(RelationGetRelationName(rel)))));\n\t}\n\n\tColumnarStorageUpdateIfNeeded(rel, true);\n\n\ttable_close(rel, AccessExclusiveLock);\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * downgrade_columnar_storage - downgrade columnar storage to the\n * current version.\n *\n * DDL:\n *   CREATE OR REPLACE FUNCTION downgrade_columnar_storage(rel regclass)\n *     RETURNS VOID\n *     STRICT\n *     LANGUAGE c AS 'MODULE_PATHNAME', 'downgrade_columnar_storage';\n */\nPG_FUNCTION_INFO_V1(downgrade_columnar_storage);\nDatum\ndowngrade_columnar_storage(PG_FUNCTION_ARGS)\n{\n\tOid relid = PG_GETARG_OID(0);\n\n\t/*\n\t * ACCESS EXCLUSIVE LOCK is not required by the low-level routines, so we\n\t * can take only an ACCESS SHARE LOCK. But all access to non-current\n\t * columnar tables will fail anyway, so it's better to take ACCESS\n\t * EXCLUSIVE LOCK now.\n\t */\n\tRelation rel = table_open(relid, AccessExclusiveLock);\n\tif (!IsColumnarTableAmTable(relid))\n\t{\n\t\tereport(ERROR, (errmsg(\"table %s is not a columnar table\",\n\t\t\t\t\t\t\t   quote_identifier(RelationGetRelationName(rel)))));\n\t}\n\n\tColumnarStorageUpdateIfNeeded(rel, false);\n\n\ttable_close(rel, AccessExclusiveLock);\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * Code to check the Citus Version, helps remove dependency from Citus\n */\n\n/*\n * CitusColumnarHasBeenLoaded returns true if the citus extension has been created\n * in the current database and the extension script has been executed. Otherwise,\n * it returns false. The result is cached as this is called very frequently.\n */\nbool\nCitusColumnarHasBeenLoaded(void)\n{\n\tif (!extensionLoadedColumnar || creating_extension)\n\t{\n\t\t/*\n\t\t * Refresh if we have not determined whether the extension has been\n\t\t * loaded yet, or in case of ALTER EXTENSION since we want to treat\n\t\t * Citus as \"not loaded\" during ALTER EXTENSION citus.\n\t\t */\n\t\tbool extensionLoaded = CitusColumnarHasBeenLoadedInternal();\n\t\textensionLoadedColumnar = extensionLoaded;\n\t}\n\n\treturn extensionLoadedColumnar;\n}\n\n\n/*\n * CitusColumnarHasBeenLoadedInternal returns true if the citus extension has been created\n * in the current database and the extension script has been executed. Otherwise,\n * it returns false.\n */\nstatic bool\nCitusColumnarHasBeenLoadedInternal(void)\n{\n\tif (IsBinaryUpgrade)\n\t{\n\t\t/* never use Citus logic during pg_upgrade */\n\t\treturn false;\n\t}\n\n\tOid citusExtensionOid = get_extension_oid(\"citus\", true);\n\tif (citusExtensionOid == InvalidOid)\n\t{\n\t\t/* Citus extension does not exist yet */\n\t\treturn false;\n\t}\n\n\tif (creating_extension && CurrentExtensionObject == citusExtensionOid)\n\t{\n\t\t/*\n\t\t * We do not use Citus hooks during CREATE/ALTER EXTENSION citus\n\t\t * since the objects used by the C code might be not be there yet.\n\t\t */\n\t\treturn false;\n\t}\n\n\t/* citus extension exists and has been created */\n\treturn true;\n}\n\n\n/*\n * CheckCitusColumnarVersion checks whether there is a version mismatch between the\n * available version and the loaded version or between the installed version\n * and the loaded version. Returns true if compatible, false otherwise.\n *\n * As a side effect, this function also sets citusVersionKnownCompatible_Columnar global\n * variable to true which reduces version check cost of next calls.\n */\nbool\nCheckCitusColumnarVersion(int elevel)\n{\n\tif (citusVersionKnownCompatibleColumnar ||\n\t\t!CitusColumnarHasBeenLoaded() ||\n\t\t!EnableVersionChecksColumnar)\n\t{\n\t\treturn true;\n\t}\n\n\tif (CheckAvailableVersionColumnar(elevel) && CheckInstalledVersionColumnar(elevel))\n\t{\n\t\tcitusVersionKnownCompatibleColumnar = true;\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\treturn false;\n\t}\n}\n\n\n/*\n * CheckAvailableVersion compares CITUS_EXTENSIONVERSION and the currently\n * available version from the citus.control file. If they are not compatible,\n * this function logs an error with the specified elevel and returns false,\n * otherwise it returns true.\n */\nbool\nCheckAvailableVersionColumnar(int elevel)\n{\n\tif (!EnableVersionChecksColumnar)\n\t{\n\t\treturn true;\n\t}\n\n\tchar *availableVersion = AvailableExtensionVersionColumnar();\n\n\tif (!MajorVersionsCompatibleColumnar(availableVersion, CITUS_EXTENSIONVERSION))\n\t{\n\t\tereport(elevel, (errmsg(\"loaded Citus library version differs from latest \"\n\t\t\t\t\t\t\t\t\"available extension version\"),\n\t\t\t\t\t\t errdetail(\"Loaded library requires %s, but the latest control \"\n\t\t\t\t\t\t\t\t   \"file specifies %s.\", CITUS_MAJORVERSION,\n\t\t\t\t\t\t\t\t   availableVersion),\n\t\t\t\t\t\t errhint(\"Restart the database to load the latest Citus \"\n\t\t\t\t\t\t\t\t \"library.\")));\n\t\tpfree(availableVersion);\n\t\treturn false;\n\t}\n\tpfree(availableVersion);\n\treturn true;\n}\n\n\n/*\n * CheckInstalledVersion compares CITUS_EXTENSIONVERSION and the\n * extension's current version from the pg_extension catalog table. If they\n * are not compatible, this function logs an error with the specified elevel,\n * otherwise it returns true.\n */\nstatic bool\nCheckInstalledVersionColumnar(int elevel)\n{\n\tAssert(CitusColumnarHasBeenLoaded());\n\tAssert(EnableVersionChecksColumnar);\n\n\tchar *installedVersion = InstalledExtensionVersionColumnar();\n\n\tif (!MinorVersionsCompatibleRelaxedColumnar(installedVersion, CITUS_EXTENSIONVERSION))\n\t{\n\t\tereport(elevel, (errmsg(\"loaded Citus library version differs from installed \"\n\t\t\t\t\t\t\t\t\"extension version\"),\n\t\t\t\t\t\t errdetail(\"Loaded library requires %s, but the installed \"\n\t\t\t\t\t\t\t\t   \"extension version is %s.\", CITUS_MAJORVERSION,\n\t\t\t\t\t\t\t\t   installedVersion),\n\t\t\t\t\t\t errhint(\"Run ALTER EXTENSION citus UPDATE and try again.\")));\n\t\tpfree(installedVersion);\n\t\treturn false;\n\t}\n\tpfree(installedVersion);\n\treturn true;\n}\n\n\n/*\n * MajorVersionsCompatible checks whether both versions are compatible. They\n * are if major and minor version numbers match, the schema version is\n * ignored.  Returns true if compatible, false otherwise.\n */\nbool\nMajorVersionsCompatibleColumnar(char *leftVersion, char *rightVersion)\n{\n\tconst char schemaVersionSeparator = '-';\n\n\tchar *leftSeperatorPosition = strchr(leftVersion, schemaVersionSeparator);\n\tchar *rightSeperatorPosition = strchr(rightVersion, schemaVersionSeparator);\n\tint leftComparisionLimit = 0;\n\tint rightComparisionLimit = 0;\n\n\tif (leftSeperatorPosition != NULL)\n\t{\n\t\tleftComparisionLimit = leftSeperatorPosition - leftVersion;\n\t}\n\telse\n\t{\n\t\tleftComparisionLimit = strlen(leftVersion);\n\t}\n\n\tif (rightSeperatorPosition != NULL)\n\t{\n\t\trightComparisionLimit = rightSeperatorPosition - rightVersion;\n\t}\n\telse\n\t{\n\t\trightComparisionLimit = strlen(rightVersion);\n\t}\n\n\t/* we can error out early if hypens are not in the same position */\n\tif (leftComparisionLimit != rightComparisionLimit)\n\t{\n\t\treturn false;\n\t}\n\n\treturn strncmp(leftVersion, rightVersion, leftComparisionLimit) == 0;\n}\n\n\n/*\n * ParseVersionComponent parses the integer at the current position and\n * advances endPtr past the parsed digits to the next character.\n */\nstatic int\nParseVersionComponent(const char *version, char **endPtr)\n{\n\terrno = 0;\n\tlong int val = strtol(version, endPtr, 10);\n\n\tif (errno == ERANGE || val > INT_MAX || val < INT_MIN)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),\n\t\t\t\t\t\terrmsg(\"Invalid integer in version string\")));\n\t}\n\treturn (int) val;\n}\n\n\n/*\n * MinorVersionsCompatibleRelaxedColumnar checks if two versions have the same major\n * version and their minor versions differ by at most 1. The schema version\n * (after '-') is ignored. Returns true if compatible, false otherwise.\n *\n * Version format expected: \"major.minor-schema\" (e.g., \"13.1-2\")\n */\nbool\nMinorVersionsCompatibleRelaxedColumnar(char *leftVersion, char *rightVersion)\n{\n\tchar *leftSep;\n\tchar *rightSep;\n\n\tint leftMajor = ParseVersionComponent(leftVersion, &leftSep);\n\tint rightMajor = ParseVersionComponent(rightVersion, &rightSep);\n\n\tif (leftMajor != rightMajor)\n\t{\n\t\treturn false;\n\t}\n\n\tint leftMinor = (*leftSep == '.') ? ParseVersionComponent(leftSep + 1, &leftSep) : 0;\n\tint rightMinor = (*rightSep == '.') ? ParseVersionComponent(rightSep + 1, &rightSep) :\n\t\t\t\t\t 0;\n\n\tint diff = leftMinor - rightMinor;\n\treturn diff >= -1 && diff <= 1;\n}\n\n\n/*\n * AvailableExtensionVersion returns the Citus version from citus.control file. It also\n * saves the result, thus consecutive calls to CitusExtensionAvailableVersion will\n * not read the citus.control file again.\n */\nstatic char *\nAvailableExtensionVersionColumnar(void)\n{\n\tLOCAL_FCINFO(fcinfo, 0);\n\tFmgrInfo flinfo;\n\n\tbool goForward = true;\n\tbool doCopy = false;\n\tchar *availableExtensionVersion;\n\n\tEState *estate = CreateExecutorState();\n\tReturnSetInfo *extensionsResultSet = makeNode(ReturnSetInfo);\n\textensionsResultSet->econtext = GetPerTupleExprContext(estate);\n\textensionsResultSet->allowedModes = SFRM_Materialize;\n\n\tfmgr_info(F_PG_AVAILABLE_EXTENSIONS, &flinfo);\n\tInitFunctionCallInfoData(*fcinfo, &flinfo, 0, InvalidOid, NULL,\n\t\t\t\t\t\t\t (Node *) extensionsResultSet);\n\n\t/* pg_available_extensions returns result set containing all available extensions */\n\t(*pg_available_extensions)(fcinfo);\n\n\tTupleTableSlot *tupleTableSlot = MakeSingleTupleTableSlot(\n\t\textensionsResultSet->setDesc,\n\t\t&TTSOpsMinimalTuple);\n\tbool hasTuple = tuplestore_gettupleslot(extensionsResultSet->setResult, goForward,\n\t\t\t\t\t\t\t\t\t\t\tdoCopy,\n\t\t\t\t\t\t\t\t\t\t\ttupleTableSlot);\n\twhile (hasTuple)\n\t{\n\t\tbool isNull = false;\n\n\t\tDatum extensionNameDatum = slot_getattr(tupleTableSlot, 1, &isNull);\n\t\tchar *extensionName = NameStr(*DatumGetName(extensionNameDatum));\n\t\tif (strcmp(extensionName, \"citus\") == 0)\n\t\t{\n\t\t\tDatum availableVersion = slot_getattr(tupleTableSlot, 2, &isNull);\n\n\n\t\t\tavailableExtensionVersion = text_to_cstring(DatumGetTextPP(availableVersion));\n\n\n\t\t\tExecClearTuple(tupleTableSlot);\n\t\t\tExecDropSingleTupleTableSlot(tupleTableSlot);\n\n\t\t\treturn availableExtensionVersion;\n\t\t}\n\n\t\tExecClearTuple(tupleTableSlot);\n\t\thasTuple = tuplestore_gettupleslot(extensionsResultSet->setResult, goForward,\n\t\t\t\t\t\t\t\t\t\t   doCopy, tupleTableSlot);\n\t}\n\n\tExecDropSingleTupleTableSlot(tupleTableSlot);\n\n\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\terrmsg(\"citus extension is not found\")));\n\n\treturn NULL; /* keep compiler happy */\n}\n\n\n/*\n * InstalledExtensionVersion returns the Citus version in PostgreSQL pg_extension table.\n */\nstatic char *\nInstalledExtensionVersionColumnar(void)\n{\n\tScanKeyData entry[1];\n\tchar *installedExtensionVersion = NULL;\n\n\tRelation relation = table_open(ExtensionRelationId, AccessShareLock);\n\n\tScanKeyInit(&entry[0], Anum_pg_extension_extname, BTEqualStrategyNumber, F_NAMEEQ,\n\t\t\t\tCStringGetDatum(\"citus\"));\n\n\tSysScanDesc scandesc = systable_beginscan(relation, ExtensionNameIndexId, true,\n\t\t\t\t\t\t\t\t\t\t\t  NULL, 1, entry);\n\n\tHeapTuple extensionTuple = systable_getnext(scandesc);\n\n\t/* We assume that there can be at most one matching tuple */\n\tif (HeapTupleIsValid(extensionTuple))\n\t{\n\t\tint extensionIndex = Anum_pg_extension_extversion;\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\t\tbool isNull = false;\n\n\t\tDatum installedVersion = heap_getattr(extensionTuple, extensionIndex,\n\t\t\t\t\t\t\t\t\t\t\t  tupleDescriptor, &isNull);\n\n\t\tif (isNull)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"citus extension version is null\")));\n\t\t}\n\n\n\t\tinstalledExtensionVersion = text_to_cstring(DatumGetTextPP(installedVersion));\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"citus extension is not loaded\")));\n\t}\n\n\tsystable_endscan(scandesc);\n\n\ttable_close(relation, AccessShareLock);\n\n\treturn installedExtensionVersion;\n}\n\n\n/*\n * GetExtensionOption returns DefElem * node with \"defname\" from \"options\" list\n */\nDefElem *\nGetExtensionOption(List *extensionOptions, const char *defname)\n{\n\tDefElem *defElement = NULL;\n\tforeach_declared_ptr(defElement, extensionOptions)\n\t{\n\t\tif (IsA(defElement, DefElem) &&\n\t\t\tstrncmp(defElement->defname, defname, NAMEDATALEN) == 0)\n\t\t{\n\t\t\treturn defElement;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/backend/columnar/columnar_writer.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_writer.c\n *\n * This file contains function definitions for writing columnar tables. This\n * includes the logic for writing file level metadata, writing row stripes,\n * and calculating chunk skip nodes.\n *\n * Copyright (c) 2016, Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n#include \"safe_lib.h\"\n\n#include \"access/heapam.h\"\n#include \"access/nbtree.h\"\n#include \"catalog/pg_am.h\"\n#include \"storage/fd.h\"\n#include \"storage/relfilelocator.h\"\n#include \"storage/smgr.h\"\n#include \"utils/guc.h\"\n#include \"utils/memutils.h\"\n#include \"utils/rel.h\"\n#include \"utils/relfilenumbermap.h\"\n\n#include \"pg_version_compat.h\"\n#include \"pg_version_constants.h\"\n\n#include \"columnar/columnar.h\"\n#include \"columnar/columnar_storage.h\"\n#include \"columnar/columnar_version_compat.h\"\n\nstruct ColumnarWriteState\n{\n\tTupleDesc tupleDescriptor;\n\tFmgrInfo **comparisonFunctionArray;\n\tRelFileLocator relfilelocator;\n\n\t/*\n\t * We can't rely on RelidByRelfilenumber for temp tables since\n\t * PG18(it was backpatched through PG13).\n\t */\n\tOid temp_relid;\n\n\tMemoryContext stripeWriteContext;\n\tMemoryContext perTupleContext;\n\tStripeBuffers *stripeBuffers;\n\tStripeSkipList *stripeSkipList;\n\tEmptyStripeReservation *emptyStripeReservation;\n\tColumnarOptions options;\n\tChunkData *chunkData;\n\n\tList *chunkGroupRowCounts;\n\n\t/*\n\t * compressionBuffer buffer is used as temporary storage during\n\t * data value compression operation. It is kept here to minimize\n\t * memory allocations. It lives in stripeWriteContext and gets\n\t * deallocated when memory context is reset.\n\t */\n\tStringInfo compressionBuffer;\n};\n\nstatic StripeBuffers * CreateEmptyStripeBuffers(uint32 stripeMaxRowCount,\n\t\t\t\t\t\t\t\t\t\t\t\tuint32 chunkRowCount,\n\t\t\t\t\t\t\t\t\t\t\t\tuint32 columnCount);\nstatic StripeSkipList * CreateEmptyStripeSkipList(uint32 stripeMaxRowCount,\n\t\t\t\t\t\t\t\t\t\t\t\t  uint32 chunkRowCount,\n\t\t\t\t\t\t\t\t\t\t\t\t  uint32 columnCount);\nstatic void FlushStripe(ColumnarWriteState *writeState);\nstatic StringInfo SerializeBoolArray(bool *boolArray, uint32 boolArrayLength);\nstatic void SerializeSingleDatum(StringInfo datumBuffer, Datum datum,\n\t\t\t\t\t\t\t\t bool datumTypeByValue, int datumTypeLength,\n\t\t\t\t\t\t\t\t char datumTypeAlign);\nstatic void SerializeChunkData(ColumnarWriteState *writeState, uint32 chunkIndex,\n\t\t\t\t\t\t\t   uint32 rowCount);\nstatic void UpdateChunkSkipNodeMinMax(ColumnChunkSkipNode *chunkSkipNode,\n\t\t\t\t\t\t\t\t\t  Datum columnValue, bool columnTypeByValue,\n\t\t\t\t\t\t\t\t\t  int columnTypeLength, Oid columnCollation,\n\t\t\t\t\t\t\t\t\t  FmgrInfo *comparisonFunction);\nstatic Datum DatumCopy(Datum datum, bool datumTypeByValue, int datumTypeLength);\nstatic StringInfo CopyStringInfo(StringInfo sourceString);\n\n/*\n * ColumnarBeginWrite initializes a columnar data load operation and returns a table\n * handle. This handle should be used for adding the row values and finishing the\n * data load operation.\n */\nColumnarWriteState *\nColumnarBeginWrite(Relation rel,\n\t\t\t\t   ColumnarOptions options,\n\t\t\t\t   TupleDesc tupleDescriptor)\n{\n\tRelFileLocator relfilelocator = rel->rd_locator;\n\n\t/* get comparison function pointers for each of the columns */\n\tuint32 columnCount = tupleDescriptor->natts;\n\tFmgrInfo **comparisonFunctionArray = palloc0(columnCount * sizeof(FmgrInfo *));\n\tfor (uint32 columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tFmgrInfo *comparisonFunction = NULL;\n\t\tFormData_pg_attribute *attributeForm = TupleDescAttr(tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t columnIndex);\n\n\t\tif (!attributeForm->attisdropped)\n\t\t{\n\t\t\tOid typeId = attributeForm->atttypid;\n\n\t\t\tcomparisonFunction = GetFunctionInfoOrNull(typeId, BTREE_AM_OID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   BTORDER_PROC);\n\t\t}\n\n\t\tcomparisonFunctionArray[columnIndex] = comparisonFunction;\n\t}\n\n\t/*\n\t * We allocate all stripe specific data in the stripeWriteContext, and\n\t * reset this memory context once we have flushed the stripe to the file.\n\t * This is to avoid memory leaks.\n\t */\n\tMemoryContext stripeWriteContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Stripe Write Memory Context\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_SIZES);\n\n\tbool *columnMaskArray = palloc(columnCount * sizeof(bool));\n\tmemset(columnMaskArray, true, columnCount * sizeof(bool));\n\n\tChunkData *chunkData = CreateEmptyChunkData(columnCount, columnMaskArray,\n\t\t\t\t\t\t\t\t\t\t\t\toptions.chunkRowCount);\n\n\tColumnarWriteState *writeState = palloc0(sizeof(ColumnarWriteState));\n\twriteState->relfilelocator = relfilelocator;\n\twriteState->temp_relid = RelationPrecomputeOid(rel);\n\twriteState->options = options;\n\twriteState->tupleDescriptor = CreateTupleDescCopy(tupleDescriptor);\n\twriteState->comparisonFunctionArray = comparisonFunctionArray;\n\twriteState->stripeBuffers = NULL;\n\twriteState->stripeSkipList = NULL;\n\twriteState->emptyStripeReservation = NULL;\n\twriteState->stripeWriteContext = stripeWriteContext;\n\twriteState->chunkData = chunkData;\n\twriteState->compressionBuffer = NULL;\n\twriteState->perTupleContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Columnar per tuple context\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tALLOCSET_DEFAULT_SIZES);\n\n\treturn writeState;\n}\n\n\n/*\n * ColumnarWriteRow adds a row to the columnar table. If the stripe is not initialized,\n * we create structures to hold stripe data and skip list. Then, we serialize and\n * append data to serialized value buffer for each of the columns and update\n * corresponding skip nodes. Then, whole chunk data is compressed at every\n * rowChunkCount insertion. Then, if row count exceeds stripeMaxRowCount, we flush\n * the stripe, and add its metadata to the table footer.\n *\n * Returns the \"row number\" assigned to written row.\n */\nuint64\nColumnarWriteRow(ColumnarWriteState *writeState, Datum *columnValues, bool *columnNulls)\n{\n\tuint32 columnIndex = 0;\n\tStripeBuffers *stripeBuffers = writeState->stripeBuffers;\n\tStripeSkipList *stripeSkipList = writeState->stripeSkipList;\n\tuint32 columnCount = writeState->tupleDescriptor->natts;\n\tColumnarOptions *options = &writeState->options;\n\tconst uint32 chunkRowCount = options->chunkRowCount;\n\tChunkData *chunkData = writeState->chunkData;\n\tMemoryContext oldContext = MemoryContextSwitchTo(writeState->stripeWriteContext);\n\n\tif (stripeBuffers == NULL)\n\t{\n\t\tstripeBuffers = CreateEmptyStripeBuffers(options->stripeRowCount,\n\t\t\t\t\t\t\t\t\t\t\t\t chunkRowCount, columnCount);\n\t\tstripeSkipList = CreateEmptyStripeSkipList(options->stripeRowCount,\n\t\t\t\t\t\t\t\t\t\t\t\t   chunkRowCount, columnCount);\n\t\twriteState->stripeBuffers = stripeBuffers;\n\t\twriteState->stripeSkipList = stripeSkipList;\n\t\twriteState->compressionBuffer = makeStringInfo();\n\n\t\tOid relationId = ColumnarRelationId(writeState->temp_relid,\n\t\t\t\t\t\t\t\t\t\t\twriteState->relfilelocator);\n\n\t\tRelation relation = relation_open(relationId, NoLock);\n\t\twriteState->emptyStripeReservation =\n\t\t\tReserveEmptyStripe(relation, columnCount, chunkRowCount,\n\t\t\t\t\t\t\t   options->stripeRowCount);\n\t\trelation_close(relation, NoLock);\n\n\t\t/*\n\t\t * serializedValueBuffer lives in stripe write memory context so it needs to be\n\t\t * initialized when the stripe is created.\n\t\t */\n\t\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t\t{\n\t\t\tchunkData->valueBufferArray[columnIndex] = makeStringInfo();\n\t\t}\n\t}\n\n\tuint32 chunkIndex = stripeBuffers->rowCount / chunkRowCount;\n\tuint32 chunkRowIndex = stripeBuffers->rowCount % chunkRowCount;\n\n\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tColumnChunkSkipNode **chunkSkipNodeArray = stripeSkipList->chunkSkipNodeArray;\n\t\tColumnChunkSkipNode *chunkSkipNode =\n\t\t\t&chunkSkipNodeArray[columnIndex][chunkIndex];\n\n\t\tif (columnNulls[columnIndex])\n\t\t{\n\t\t\tchunkData->existsArray[columnIndex][chunkRowIndex] = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tFmgrInfo *comparisonFunction =\n\t\t\t\twriteState->comparisonFunctionArray[columnIndex];\n\t\t\tForm_pg_attribute attributeForm =\n\t\t\t\tTupleDescAttr(writeState->tupleDescriptor, columnIndex);\n\t\t\tbool columnTypeByValue = attributeForm->attbyval;\n\t\t\tint columnTypeLength = attributeForm->attlen;\n\t\t\tOid columnCollation = attributeForm->attcollation;\n\t\t\tchar columnTypeAlign = attributeForm->attalign;\n\n\t\t\tchunkData->existsArray[columnIndex][chunkRowIndex] = true;\n\n\t\t\tSerializeSingleDatum(chunkData->valueBufferArray[columnIndex],\n\t\t\t\t\t\t\t\t columnValues[columnIndex], columnTypeByValue,\n\t\t\t\t\t\t\t\t columnTypeLength, columnTypeAlign);\n\n\t\t\tUpdateChunkSkipNodeMinMax(chunkSkipNode, columnValues[columnIndex],\n\t\t\t\t\t\t\t\t\t  columnTypeByValue, columnTypeLength,\n\t\t\t\t\t\t\t\t\t  columnCollation, comparisonFunction);\n\t\t}\n\n\t\tchunkSkipNode->rowCount++;\n\t}\n\n\tstripeSkipList->chunkCount = chunkIndex + 1;\n\n\t/* last row of the chunk is inserted serialize the chunk */\n\tif (chunkRowIndex == chunkRowCount - 1)\n\t{\n\t\tSerializeChunkData(writeState, chunkIndex, chunkRowCount);\n\t}\n\n\tuint64 writtenRowNumber = writeState->emptyStripeReservation->stripeFirstRowNumber +\n\t\t\t\t\t\t\t  stripeBuffers->rowCount;\n\tstripeBuffers->rowCount++;\n\tif (stripeBuffers->rowCount >= options->stripeRowCount)\n\t{\n\t\tColumnarFlushPendingWrites(writeState);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\n\treturn writtenRowNumber;\n}\n\n\n/*\n * ColumnarEndWrite finishes a columnar data load operation. If we have an unflushed\n * stripe, we flush it.\n */\nvoid\nColumnarEndWrite(ColumnarWriteState *writeState)\n{\n\tColumnarFlushPendingWrites(writeState);\n\n\tMemoryContextDelete(writeState->stripeWriteContext);\n\tpfree(writeState->comparisonFunctionArray);\n\tFreeChunkData(writeState->chunkData);\n\tpfree(writeState);\n}\n\n\nvoid\nColumnarFlushPendingWrites(ColumnarWriteState *writeState)\n{\n\tStripeBuffers *stripeBuffers = writeState->stripeBuffers;\n\tif (stripeBuffers != NULL)\n\t{\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(writeState->stripeWriteContext);\n\n\t\tFlushStripe(writeState);\n\t\tMemoryContextReset(writeState->stripeWriteContext);\n\n\t\t/* set stripe data and skip list to NULL so they are recreated next time */\n\t\twriteState->stripeBuffers = NULL;\n\t\twriteState->stripeSkipList = NULL;\n\n\t\tMemoryContextSwitchTo(oldContext);\n\t}\n}\n\n\n/*\n * ColumnarWritePerTupleContext\n *\n * Return per-tuple context for columnar write operation.\n */\nMemoryContext\nColumnarWritePerTupleContext(ColumnarWriteState *state)\n{\n\treturn state->perTupleContext;\n}\n\n\n/*\n * CreateEmptyStripeBuffers allocates an empty StripeBuffers structure with the given\n * column count.\n */\nstatic StripeBuffers *\nCreateEmptyStripeBuffers(uint32 stripeMaxRowCount, uint32 chunkRowCount,\n\t\t\t\t\t\t uint32 columnCount)\n{\n\tuint32 columnIndex = 0;\n\tuint32 maxChunkCount = (stripeMaxRowCount / chunkRowCount) + 1;\n\tColumnBuffers **columnBuffersArray = palloc0(columnCount * sizeof(ColumnBuffers *));\n\n\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tuint32 chunkIndex = 0;\n\t\tColumnChunkBuffers **chunkBuffersArray =\n\t\t\tpalloc0(maxChunkCount * sizeof(ColumnChunkBuffers *));\n\n\t\tfor (chunkIndex = 0; chunkIndex < maxChunkCount; chunkIndex++)\n\t\t{\n\t\t\tchunkBuffersArray[chunkIndex] = palloc0(sizeof(ColumnChunkBuffers));\n\t\t\tchunkBuffersArray[chunkIndex]->existsBuffer = NULL;\n\t\t\tchunkBuffersArray[chunkIndex]->valueBuffer = NULL;\n\t\t\tchunkBuffersArray[chunkIndex]->valueCompressionType = COMPRESSION_NONE;\n\t\t}\n\n\t\tcolumnBuffersArray[columnIndex] = palloc0(sizeof(ColumnBuffers));\n\t\tcolumnBuffersArray[columnIndex]->chunkBuffersArray = chunkBuffersArray;\n\t}\n\n\tStripeBuffers *stripeBuffers = palloc0(sizeof(StripeBuffers));\n\tstripeBuffers->columnBuffersArray = columnBuffersArray;\n\tstripeBuffers->columnCount = columnCount;\n\tstripeBuffers->rowCount = 0;\n\n\treturn stripeBuffers;\n}\n\n\n/*\n * CreateEmptyStripeSkipList allocates an empty StripeSkipList structure with\n * the given column count. This structure has enough chunks to hold statistics\n * for stripeMaxRowCount rows.\n */\nstatic StripeSkipList *\nCreateEmptyStripeSkipList(uint32 stripeMaxRowCount, uint32 chunkRowCount,\n\t\t\t\t\t\t  uint32 columnCount)\n{\n\tuint32 columnIndex = 0;\n\tuint32 maxChunkCount = (stripeMaxRowCount / chunkRowCount) + 1;\n\n\tColumnChunkSkipNode **chunkSkipNodeArray =\n\t\tpalloc0(columnCount * sizeof(ColumnChunkSkipNode *));\n\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tchunkSkipNodeArray[columnIndex] =\n\t\t\tpalloc0(maxChunkCount * sizeof(ColumnChunkSkipNode));\n\t}\n\n\tStripeSkipList *stripeSkipList = palloc0(sizeof(StripeSkipList));\n\tstripeSkipList->columnCount = columnCount;\n\tstripeSkipList->chunkCount = 0;\n\tstripeSkipList->chunkSkipNodeArray = chunkSkipNodeArray;\n\n\treturn stripeSkipList;\n}\n\n\n/*\n * FlushStripe flushes current stripe data into the file. The function first ensures\n * the last data chunk for each column is properly serialized and compressed. Then,\n * the function creates the skip list and footer buffers. Finally, the function\n * flushes the skip list, data, and footer buffers to the file.\n */\nstatic void\nFlushStripe(ColumnarWriteState *writeState)\n{\n\tuint32 columnIndex = 0;\n\tuint32 chunkIndex = 0;\n\tStripeBuffers *stripeBuffers = writeState->stripeBuffers;\n\tStripeSkipList *stripeSkipList = writeState->stripeSkipList;\n\tColumnChunkSkipNode **columnSkipNodeArray = stripeSkipList->chunkSkipNodeArray;\n\tTupleDesc tupleDescriptor = writeState->tupleDescriptor;\n\tuint32 columnCount = tupleDescriptor->natts;\n\tuint32 chunkCount = stripeSkipList->chunkCount;\n\tuint32 chunkRowCount = writeState->options.chunkRowCount;\n\tuint32 lastChunkIndex = stripeBuffers->rowCount / chunkRowCount;\n\tuint32 lastChunkRowCount = stripeBuffers->rowCount % chunkRowCount;\n\tuint64 stripeSize = 0;\n\tuint64 stripeRowCount = stripeBuffers->rowCount;\n\n\telog(DEBUG1, \"Flushing Stripe of size %d\", stripeBuffers->rowCount);\n\n\tOid relationId = ColumnarRelationId(writeState->temp_relid,\n\t\t\t\t\t\t\t\t\t\twriteState->relfilelocator);\n\n\tRelation relation = relation_open(relationId, NoLock);\n\n\t/*\n\t * check if the last chunk needs serialization , the last chunk was not serialized\n\t * if it was not full yet, e.g.  (rowCount > 0)\n\t */\n\tif (lastChunkRowCount > 0)\n\t{\n\t\tSerializeChunkData(writeState, lastChunkIndex, lastChunkRowCount);\n\t}\n\n\t/* update buffer sizes in stripe skip list */\n\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tColumnChunkSkipNode *chunkSkipNodeArray = columnSkipNodeArray[columnIndex];\n\t\tColumnBuffers *columnBuffers = stripeBuffers->columnBuffersArray[columnIndex];\n\n\t\tfor (chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++)\n\t\t{\n\t\t\tColumnChunkBuffers *chunkBuffers =\n\t\t\t\tcolumnBuffers->chunkBuffersArray[chunkIndex];\n\t\t\tuint64 existsBufferSize = chunkBuffers->existsBuffer->len;\n\t\t\tColumnChunkSkipNode *chunkSkipNode = &chunkSkipNodeArray[chunkIndex];\n\n\t\t\tchunkSkipNode->existsChunkOffset = stripeSize;\n\t\t\tchunkSkipNode->existsLength = existsBufferSize;\n\t\t\tstripeSize += existsBufferSize;\n\t\t}\n\n\t\tfor (chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++)\n\t\t{\n\t\t\tColumnChunkBuffers *chunkBuffers =\n\t\t\t\tcolumnBuffers->chunkBuffersArray[chunkIndex];\n\t\t\tuint64 valueBufferSize = chunkBuffers->valueBuffer->len;\n\t\t\tCompressionType valueCompressionType = chunkBuffers->valueCompressionType;\n\t\t\tColumnChunkSkipNode *chunkSkipNode = &chunkSkipNodeArray[chunkIndex];\n\n\t\t\tchunkSkipNode->valueChunkOffset = stripeSize;\n\t\t\tchunkSkipNode->valueLength = valueBufferSize;\n\t\t\tchunkSkipNode->valueCompressionType = valueCompressionType;\n\t\t\tchunkSkipNode->valueCompressionLevel = writeState->options.compressionLevel;\n\t\t\tchunkSkipNode->decompressedValueSize = chunkBuffers->decompressedValueSize;\n\n\t\t\tstripeSize += valueBufferSize;\n\t\t}\n\t}\n\n\tStripeMetadata *stripeMetadata =\n\t\tCompleteStripeReservation(relation, writeState->emptyStripeReservation->stripeId,\n\t\t\t\t\t\t\t\t  stripeSize, stripeRowCount, chunkCount);\n\n\tuint64 currentFileOffset = stripeMetadata->fileOffset;\n\n\t/*\n\t * Each stripe has only one section:\n\t * Data section, in which we store data for each column continuously.\n\t * We store data for each for each column in chunks. For each chunk, we\n\t * store two buffers: \"exists\" buffer, and \"value\" buffer. \"exists\" buffer\n\t * tells which values are not NULL. \"value\" buffer contains values for\n\t * present values. For each column, we first store all \"exists\" buffers,\n\t * and then all \"value\" buffers.\n\t */\n\n\t/* flush the data buffers */\n\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tColumnBuffers *columnBuffers = stripeBuffers->columnBuffersArray[columnIndex];\n\n\t\tfor (chunkIndex = 0; chunkIndex < stripeSkipList->chunkCount; chunkIndex++)\n\t\t{\n\t\t\tColumnChunkBuffers *chunkBuffers =\n\t\t\t\tcolumnBuffers->chunkBuffersArray[chunkIndex];\n\t\t\tStringInfo existsBuffer = chunkBuffers->existsBuffer;\n\n\t\t\tColumnarStorageWrite(relation, currentFileOffset,\n\t\t\t\t\t\t\t\t existsBuffer->data, existsBuffer->len);\n\t\t\tcurrentFileOffset += existsBuffer->len;\n\t\t}\n\n\t\tfor (chunkIndex = 0; chunkIndex < stripeSkipList->chunkCount; chunkIndex++)\n\t\t{\n\t\t\tColumnChunkBuffers *chunkBuffers =\n\t\t\t\tcolumnBuffers->chunkBuffersArray[chunkIndex];\n\t\t\tStringInfo valueBuffer = chunkBuffers->valueBuffer;\n\n\t\t\tColumnarStorageWrite(relation, currentFileOffset,\n\t\t\t\t\t\t\t\t valueBuffer->data, valueBuffer->len);\n\t\t\tcurrentFileOffset += valueBuffer->len;\n\t\t}\n\t}\n\n\tSaveChunkGroups(writeState->temp_relid,\n\t\t\t\t\twriteState->relfilelocator,\n\t\t\t\t\tstripeMetadata->id,\n\t\t\t\t\twriteState->chunkGroupRowCounts);\n\tSaveStripeSkipList(writeState->temp_relid,\n\t\t\t\t\t   writeState->relfilelocator,\n\t\t\t\t\t   stripeMetadata->id,\n\t\t\t\t\t   stripeSkipList, tupleDescriptor);\n\n\twriteState->chunkGroupRowCounts = NIL;\n\n\trelation_close(relation, NoLock);\n}\n\n\n/*\n * SerializeBoolArray serializes the given boolean array and returns the result\n * as a StringInfo. This function packs every 8 boolean values into one byte.\n */\nstatic StringInfo\nSerializeBoolArray(bool *boolArray, uint32 boolArrayLength)\n{\n\tuint32 boolArrayIndex = 0;\n\tuint32 byteCount = ((boolArrayLength * sizeof(bool)) + (8 - sizeof(bool))) / 8;\n\n\tStringInfo boolArrayBuffer = makeStringInfo();\n\tenlargeStringInfo(boolArrayBuffer, byteCount);\n\tboolArrayBuffer->len = byteCount;\n\tmemset(boolArrayBuffer->data, 0, byteCount);\n\n\tfor (boolArrayIndex = 0; boolArrayIndex < boolArrayLength; boolArrayIndex++)\n\t{\n\t\tif (boolArray[boolArrayIndex])\n\t\t{\n\t\t\tuint32 byteIndex = boolArrayIndex / 8;\n\t\t\tuint32 bitIndex = boolArrayIndex % 8;\n\t\t\tboolArrayBuffer->data[byteIndex] |= (1 << bitIndex);\n\t\t}\n\t}\n\n\treturn boolArrayBuffer;\n}\n\n\n/*\n * SerializeSingleDatum serializes the given datum value and appends it to the\n * provided string info buffer.\n *\n * Since we don't want to limit datum buffer size to RSIZE_MAX unnecessarily,\n * we use memcpy instead of memcpy_s several places in this function.\n */\nstatic void\nSerializeSingleDatum(StringInfo datumBuffer, Datum datum, bool datumTypeByValue,\n\t\t\t\t\t int datumTypeLength, char datumTypeAlign)\n{\n\tuint32 datumLength = att_addlength_datum(0, datumTypeLength, datum);\n\tuint32 datumLengthAligned = att_align_nominal(datumLength, datumTypeAlign);\n\n\tenlargeStringInfo(datumBuffer, datumLengthAligned);\n\n\tchar *currentDatumDataPointer = datumBuffer->data + datumBuffer->len;\n\tmemset(currentDatumDataPointer, 0, datumLengthAligned);\n\n\tif (datumTypeLength > 0)\n\t{\n\t\tif (datumTypeByValue)\n\t\t{\n\t\t\tstore_att_byval(currentDatumDataPointer, datum, datumTypeLength);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmemcpy(currentDatumDataPointer, DatumGetPointer(datum), datumTypeLength); /* IGNORE-BANNED */\n\t\t}\n\t}\n\telse\n\t{\n\t\tAssert(!datumTypeByValue);\n\t\tmemcpy(currentDatumDataPointer, DatumGetPointer(datum), datumLength); /* IGNORE-BANNED */\n\t}\n\n\tdatumBuffer->len += datumLengthAligned;\n}\n\n\n/*\n * SerializeChunkData serializes and compresses chunk data at given chunk index with given\n * compression type for every column.\n */\nstatic void\nSerializeChunkData(ColumnarWriteState *writeState, uint32 chunkIndex, uint32 rowCount)\n{\n\tuint32 columnIndex = 0;\n\tStripeBuffers *stripeBuffers = writeState->stripeBuffers;\n\tChunkData *chunkData = writeState->chunkData;\n\tCompressionType requestedCompressionType = writeState->options.compressionType;\n\tint compressionLevel = writeState->options.compressionLevel;\n\tconst uint32 columnCount = stripeBuffers->columnCount;\n\tStringInfo compressionBuffer = writeState->compressionBuffer;\n\n\twriteState->chunkGroupRowCounts =\n\t\tlappend_int(writeState->chunkGroupRowCounts, rowCount);\n\n\t/* serialize exist values, data values are already serialized */\n\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tColumnBuffers *columnBuffers = stripeBuffers->columnBuffersArray[columnIndex];\n\t\tColumnChunkBuffers *chunkBuffers = columnBuffers->chunkBuffersArray[chunkIndex];\n\n\t\tchunkBuffers->existsBuffer =\n\t\t\tSerializeBoolArray(chunkData->existsArray[columnIndex], rowCount);\n\t}\n\n\t/*\n\t * check and compress value buffers, if a value buffer is not compressable\n\t * then keep it as uncompressed, store compression information.\n\t */\n\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tColumnBuffers *columnBuffers = stripeBuffers->columnBuffersArray[columnIndex];\n\t\tColumnChunkBuffers *chunkBuffers = columnBuffers->chunkBuffersArray[chunkIndex];\n\t\tCompressionType actualCompressionType = COMPRESSION_NONE;\n\n\t\tStringInfo serializedValueBuffer = chunkData->valueBufferArray[columnIndex];\n\n\t\tAssert(requestedCompressionType >= 0 &&\n\t\t\t   requestedCompressionType < COMPRESSION_COUNT);\n\n\t\tchunkBuffers->decompressedValueSize =\n\t\t\tchunkData->valueBufferArray[columnIndex]->len;\n\n\t\t/*\n\t\t * if serializedValueBuffer is be compressed, update serializedValueBuffer\n\t\t * with compressed data and store compression type.\n\t\t */\n\t\tbool compressed = CompressBuffer(serializedValueBuffer, compressionBuffer,\n\t\t\t\t\t\t\t\t\t\t requestedCompressionType,\n\t\t\t\t\t\t\t\t\t\t compressionLevel);\n\t\tif (compressed)\n\t\t{\n\t\t\tserializedValueBuffer = compressionBuffer;\n\t\t\tactualCompressionType = requestedCompressionType;\n\t\t}\n\n\t\t/* store (compressed) value buffer */\n\t\tchunkBuffers->valueCompressionType = actualCompressionType;\n\t\tchunkBuffers->valueBuffer = CopyStringInfo(serializedValueBuffer);\n\n\t\t/* valueBuffer needs to be reset for next chunk's data */\n\t\tresetStringInfo(chunkData->valueBufferArray[columnIndex]);\n\t}\n}\n\n\n/*\n * UpdateChunkSkipNodeMinMax takes the given column value, and checks if this\n * value falls outside the range of minimum/maximum values of the given column\n * chunk skip node. If it does, the function updates the column chunk skip node\n * accordingly.\n */\nstatic void\nUpdateChunkSkipNodeMinMax(ColumnChunkSkipNode *chunkSkipNode, Datum columnValue,\n\t\t\t\t\t\t  bool columnTypeByValue, int columnTypeLength,\n\t\t\t\t\t\t  Oid columnCollation, FmgrInfo *comparisonFunction)\n{\n\tbool hasMinMax = chunkSkipNode->hasMinMax;\n\tDatum previousMinimum = chunkSkipNode->minimumValue;\n\tDatum previousMaximum = chunkSkipNode->maximumValue;\n\tDatum currentMinimum = 0;\n\tDatum currentMaximum = 0;\n\n\t/* if type doesn't have a comparison function, skip min/max values */\n\tif (comparisonFunction == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tif (!hasMinMax)\n\t{\n\t\tcurrentMinimum = DatumCopy(columnValue, columnTypeByValue, columnTypeLength);\n\t\tcurrentMaximum = DatumCopy(columnValue, columnTypeByValue, columnTypeLength);\n\t}\n\telse\n\t{\n\t\tDatum minimumComparisonDatum = FunctionCall2Coll(comparisonFunction,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t columnCollation, columnValue,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t previousMinimum);\n\t\tDatum maximumComparisonDatum = FunctionCall2Coll(comparisonFunction,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t columnCollation, columnValue,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t previousMaximum);\n\t\tint minimumComparison = DatumGetInt32(minimumComparisonDatum);\n\t\tint maximumComparison = DatumGetInt32(maximumComparisonDatum);\n\n\t\tif (minimumComparison < 0)\n\t\t{\n\t\t\tcurrentMinimum = DatumCopy(columnValue, columnTypeByValue, columnTypeLength);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcurrentMinimum = previousMinimum;\n\t\t}\n\n\t\tif (maximumComparison > 0)\n\t\t{\n\t\t\tcurrentMaximum = DatumCopy(columnValue, columnTypeByValue, columnTypeLength);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcurrentMaximum = previousMaximum;\n\t\t}\n\t}\n\n\tchunkSkipNode->hasMinMax = true;\n\tchunkSkipNode->minimumValue = currentMinimum;\n\tchunkSkipNode->maximumValue = currentMaximum;\n}\n\n\n/* Creates a copy of the given datum. */\nstatic Datum\nDatumCopy(Datum datum, bool datumTypeByValue, int datumTypeLength)\n{\n\tDatum datumCopy = 0;\n\n\tif (datumTypeByValue)\n\t{\n\t\tdatumCopy = datum;\n\t}\n\telse\n\t{\n\t\tuint32 datumLength = att_addlength_datum(0, datumTypeLength, datum);\n\t\tchar *datumData = palloc0(datumLength);\n\n\t\t/*\n\t\t * We use IGNORE-BANNED here since we don't want to limit datum size to\n\t\t * RSIZE_MAX unnecessarily.\n\t\t */\n\t\tmemcpy(datumData, DatumGetPointer(datum), datumLength); /* IGNORE-BANNED */\n\n\t\tdatumCopy = PointerGetDatum(datumData);\n\t}\n\n\treturn datumCopy;\n}\n\n\n/*\n * CopyStringInfo creates a deep copy of given source string allocating only needed\n * amount of memory.\n */\nstatic StringInfo\nCopyStringInfo(StringInfo sourceString)\n{\n\tStringInfo targetString = palloc0(sizeof(StringInfoData));\n\n\tif (sourceString->len > 0)\n\t{\n\t\ttargetString->data = palloc0(sourceString->len);\n\t\ttargetString->len = sourceString->len;\n\t\ttargetString->maxlen = sourceString->len;\n\n\t\t/*\n\t\t * We use IGNORE-BANNED here since we don't want to limit string\n\t\t * buffer size to RSIZE_MAX unnecessarily.\n\t\t */\n\t\tmemcpy(targetString->data, sourceString->data, sourceString->len); /* IGNORE-BANNED */\n\t}\n\n\treturn targetString;\n}\n\n\nbool\nContainsPendingWrites(ColumnarWriteState *state)\n{\n\treturn state->stripeBuffers != NULL && state->stripeBuffers->rowCount != 0;\n}\n"
  },
  {
    "path": "src/backend/columnar/mod.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * mod.c\n *\n * This file contains module-level definitions.\n *\n * Copyright (c) 2016, Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"citus_version.h\"\n\n#include \"columnar/columnar.h\"\n#include \"columnar/columnar_tableam.h\"\n\n\n#if PG_VERSION_NUM >= PG_VERSION_18\nPG_MODULE_MAGIC_EXT(.name = \"citus_columnar\", .version = \"15.0devel\");\n#else\nPG_MODULE_MAGIC;\n#endif\n\nvoid _PG_init(void);\n\nvoid\n_PG_init(void)\n{\n\tcolumnar_init();\n}\n"
  },
  {
    "path": "src/backend/columnar/sql/citus_columnar--11.1-0--11.1-1.sql",
    "content": "-- add columnar objects back\nALTER EXTENSION citus_columnar ADD SCHEMA columnar;\nALTER EXTENSION citus_columnar ADD SCHEMA columnar_internal;\nALTER EXTENSION citus_columnar ADD SEQUENCE columnar_internal.storageid_seq;\nALTER EXTENSION citus_columnar ADD TABLE columnar_internal.options;\nALTER EXTENSION citus_columnar ADD TABLE columnar_internal.stripe;\nALTER EXTENSION citus_columnar ADD TABLE columnar_internal.chunk_group;\nALTER EXTENSION citus_columnar ADD TABLE columnar_internal.chunk;\n\nALTER EXTENSION citus_columnar ADD FUNCTION columnar_internal.columnar_handler;\nALTER EXTENSION citus_columnar ADD ACCESS METHOD columnar;\nALTER EXTENSION citus_columnar ADD FUNCTION pg_catalog.alter_columnar_table_set;\nALTER EXTENSION citus_columnar ADD FUNCTION pg_catalog.alter_columnar_table_reset;\n\nALTER EXTENSION citus_columnar ADD FUNCTION citus_internal.upgrade_columnar_storage;\nALTER EXTENSION citus_columnar ADD FUNCTION citus_internal.downgrade_columnar_storage;\nALTER EXTENSION citus_columnar ADD FUNCTION citus_internal.columnar_ensure_am_depends_catalog;\n\nALTER EXTENSION citus_columnar ADD FUNCTION columnar.get_storage_id;\nALTER EXTENSION citus_columnar ADD VIEW columnar.storage;\nALTER EXTENSION citus_columnar ADD VIEW columnar.options;\nALTER EXTENSION citus_columnar ADD VIEW columnar.stripe;\nALTER EXTENSION citus_columnar ADD VIEW columnar.chunk_group;\nALTER EXTENSION citus_columnar ADD VIEW columnar.chunk;\n\n-- move citus_internal functions to columnar_internal\n\nALTER FUNCTION citus_internal.upgrade_columnar_storage(regclass) SET SCHEMA columnar_internal;\nALTER FUNCTION citus_internal.downgrade_columnar_storage(regclass) SET SCHEMA columnar_internal;\nALTER FUNCTION citus_internal.columnar_ensure_am_depends_catalog() SET SCHEMA columnar_internal;\n\n\n"
  },
  {
    "path": "src/backend/columnar/sql/citus_columnar--11.1-0.sql",
    "content": "-- fake sql file 'Y'\n"
  },
  {
    "path": "src/backend/columnar/sql/citus_columnar--11.1-1--11.2-1.sql",
    "content": "-- citus_columnar--11.1-1--11.2-1\n\n#include \"udfs/columnar_ensure_am_depends_catalog/11.2-1.sql\"\n\nDELETE FROM pg_depend\nWHERE classid = 'pg_am'::regclass::oid\n    AND objid IN (select oid from pg_am where amname = 'columnar')\n    AND objsubid = 0\n    AND refclassid = 'pg_class'::regclass::oid\n    AND refobjid IN (\n        'columnar_internal.stripe_first_row_number_idx'::regclass::oid,\n        'columnar_internal.chunk_group_pkey'::regclass::oid,\n        'columnar_internal.chunk_pkey'::regclass::oid,\n        'columnar_internal.options_pkey'::regclass::oid,\n        'columnar_internal.stripe_first_row_number_idx'::regclass::oid,\n        'columnar_internal.stripe_pkey'::regclass::oid\n    )\n    AND refobjsubid = 0\n    AND deptype = 'n';\n"
  },
  {
    "path": "src/backend/columnar/sql/citus_columnar--11.1-1.sql",
    "content": "-- complain if script is sourced in psql, rather than via CREATE EXTENSION\n\\echo Use \"CREATE EXTENSION citus_columnar\" to load this file. \\quit\n\n-- columnar--9.5-1--10.0-1.sql\n\nCREATE SCHEMA IF NOT EXISTS columnar;\nSET search_path TO columnar;\n\n\nCREATE SEQUENCE IF NOT EXISTS storageid_seq MINVALUE 10000000000 NO CYCLE;\n\nCREATE TABLE IF NOT EXISTS options (\n    regclass regclass NOT NULL PRIMARY KEY,\n    chunk_group_row_limit int NOT NULL,\n    stripe_row_limit int NOT NULL,\n    compression_level int NOT NULL,\n    compression name NOT NULL\n) WITH (user_catalog_table = true);\n\nCOMMENT ON TABLE options IS 'columnar table specific options, maintained by alter_columnar_table_set';\n\nCREATE TABLE IF NOT EXISTS stripe (\n    storage_id bigint NOT NULL,\n    stripe_num bigint NOT NULL,\n    file_offset bigint NOT NULL,\n    data_length bigint NOT NULL,\n    column_count int NOT NULL,\n    chunk_row_count int NOT NULL,\n    row_count bigint NOT NULL,\n    chunk_group_count int NOT NULL,\n    first_row_number bigint NOT NULL,\n    PRIMARY KEY (storage_id, stripe_num),\n    CONSTRAINT stripe_first_row_number_idx UNIQUE (storage_id, first_row_number)\n) WITH (user_catalog_table = true);\n\nCOMMENT ON TABLE stripe IS 'Columnar per stripe metadata';\n\nCREATE TABLE IF NOT EXISTS chunk_group (\n    storage_id bigint NOT NULL,\n    stripe_num bigint NOT NULL,\n    chunk_group_num int NOT NULL,\n    row_count bigint NOT NULL,\n    PRIMARY KEY (storage_id, stripe_num, chunk_group_num)\n);\n\nCOMMENT ON TABLE chunk_group IS 'Columnar chunk group metadata';\n\nCREATE TABLE IF NOT EXISTS chunk (\n    storage_id bigint NOT NULL,\n    stripe_num bigint NOT NULL,\n    attr_num int NOT NULL,\n    chunk_group_num int NOT NULL,\n    minimum_value bytea,\n    maximum_value bytea,\n    value_stream_offset bigint NOT NULL,\n    value_stream_length bigint NOT NULL,\n    exists_stream_offset bigint NOT NULL,\n    exists_stream_length bigint NOT NULL,\n    value_compression_type int NOT NULL,\n    value_compression_level int NOT NULL,\n    value_decompressed_length bigint NOT NULL,\n    value_count bigint NOT NULL,\n    PRIMARY KEY (storage_id, stripe_num, attr_num, chunk_group_num)\n) WITH (user_catalog_table = true);\n\nCOMMENT ON TABLE chunk IS 'Columnar per chunk metadata';\n\nDO $proc$\nBEGIN\n\n-- from version 12 and up we have support for tableam's if installed on pg11 we can't\n-- create the objects here. Instead we rely on citus_finish_pg_upgrade to be called by the\n-- user instead to add the missing objects\nIF substring(current_Setting('server_version'), '\\d+')::int >= 12 THEN\n  EXECUTE $$\n--#include \"udfs/columnar_handler/10.0-1.sql\"\nCREATE OR REPLACE FUNCTION columnar.columnar_handler(internal)\n    RETURNS table_am_handler\n    LANGUAGE C\nAS 'MODULE_PATHNAME', 'columnar_handler';\nCOMMENT ON FUNCTION columnar.columnar_handler(internal)\n    IS 'internal function returning the handler for columnar tables';\n\n-- postgres 11.8 does not support the syntax for table am, also it is seemingly trying\n-- to parse the upgrade file and erroring on unknown syntax.\n-- normally this section would not execute on postgres 11 anyway. To trick it to pass on\n-- 11.8 we wrap the statement in a plpgsql block together with an EXECUTE. This is valid\n-- syntax on 11.8 and will execute correctly in 12\nDO $create_table_am$\nBEGIN\n  EXECUTE 'CREATE ACCESS METHOD columnar TYPE TABLE HANDLER columnar.columnar_handler';\nEND $create_table_am$;\n\n--#include \"udfs/alter_columnar_table_set/10.0-1.sql\"\nCREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int DEFAULT NULL,\n    stripe_row_limit int DEFAULT NULL,\n    compression name DEFAULT null,\n    compression_level int DEFAULT NULL)\n    RETURNS void\n    LANGUAGE C\nAS 'MODULE_PATHNAME', 'alter_columnar_table_set';\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int,\n    stripe_row_limit int,\n    compression name,\n    compression_level int)\nIS 'set one or more options on a columnar table, when set to NULL no change is made';\n\n\n--#include \"udfs/alter_columnar_table_reset/10.0-1.sql\"\nCREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool DEFAULT false,\n    stripe_row_limit bool DEFAULT false,\n    compression bool DEFAULT false,\n    compression_level bool DEFAULT false)\n    RETURNS void\n    LANGUAGE C\nAS 'MODULE_PATHNAME', 'alter_columnar_table_reset';\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool,\n    stripe_row_limit bool,\n    compression bool,\n    compression_level bool)\nIS 'reset on or more options on a columnar table to the system defaults';\n\n  $$;\nEND IF;\nEND$proc$;\n\n-- (this function being dropped in 10.0.3)->#include \"udfs/columnar_ensure_objects_exist/10.0-1.sql\"\n\nRESET search_path;\n\n-- columnar--10.0.-1 --10.0.2\nGRANT USAGE ON SCHEMA columnar TO PUBLIC;\nGRANT SELECT ON ALL tables IN SCHEMA columnar TO PUBLIC ;\n\n-- columnar--10.0-3--10.1-1.sql\n\n-- Drop foreign keys between columnar metadata tables.\n\n\n-- columnar--10.1-1--10.2-1.sql\n\n-- For a proper mapping between tid & (stripe, row_num), add a new column to\n-- columnar.stripe and define a BTREE index on this column.\n-- Also include storage_id column for per-relation scans.\n\n\n-- Populate first_row_number column of columnar.stripe table.\n--\n-- For simplicity, we calculate MAX(row_count) value across all the stripes\n-- of all the columanar tables and then use it to populate first_row_number\n-- column. This would introduce some gaps however we are okay with that since\n-- it's already the case with regular INSERT/COPY's.\nDO $$\nDECLARE\n  max_row_count bigint;\n  -- this should be equal to columnar_storage.h/COLUMNAR_FIRST_ROW_NUMBER\n  COLUMNAR_FIRST_ROW_NUMBER constant bigint := 1;\nBEGIN\n  SELECT MAX(row_count) INTO max_row_count FROM columnar.stripe;\n  UPDATE columnar.stripe SET first_row_number = COLUMNAR_FIRST_ROW_NUMBER +\n                                                (stripe_num - 1) * max_row_count;\nEND;\n$$;\n\n-- columnar--10.2-1--10.2-2.sql\n\n-- revoke read access for columnar.chunk from unprivileged\n-- user as it contains chunk min/max values\nREVOKE SELECT ON columnar.chunk FROM PUBLIC;\n\n\n-- columnar--10.2-2--10.2-3.sql\n\n-- Since stripe_first_row_number_idx is required to scan a columnar table, we\n-- need to make sure that it is created before doing anything with columnar\n-- tables during pg upgrades.\n--\n-- However, a plain btree index is not a dependency of a table, so pg_upgrade\n-- cannot guarantee that stripe_first_row_number_idx gets created when\n-- creating columnar.stripe, unless we make it a unique \"constraint\".\n--\n-- To do that, drop stripe_first_row_number_idx and create a unique\n-- constraint with the same name to keep the code change at minimum.\n\n-- columnar--10.2-3--10.2-4.sql\n\n\n-- columnar--11.0-2--11.1-1.sql\n\nCREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int DEFAULT NULL,\n    stripe_row_limit int DEFAULT NULL,\n    compression name DEFAULT null,\n    compression_level int DEFAULT NULL)\n    RETURNS void\n    LANGUAGE plpgsql AS\n$alter_columnar_table_set$\ndeclare\n  noop BOOLEAN := true;\n  cmd  TEXT    := 'ALTER TABLE ' || table_name::text || ' SET (';\nbegin\n  if (chunk_group_row_limit is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd  || 'columnar.chunk_group_row_limit=' || chunk_group_row_limit;\n    noop := false;\n  end if;\n  if (stripe_row_limit is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.stripe_row_limit=' || stripe_row_limit;\n    noop := false;\n  end if;\n  if (compression is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression=' || compression;\n    noop := false;\n  end if;\n  if (compression_level is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression_level=' || compression_level;\n    noop := false;\n  end if;\n  cmd := cmd || ')';\n  if (not noop) then\n    execute cmd;\n  end if;\n  return;\nend;\n$alter_columnar_table_set$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int,\n    stripe_row_limit int,\n    compression name,\n    compression_level int)\nIS 'set one or more options on a columnar table, when set to NULL no change is made';\n\nCREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool DEFAULT false,\n    stripe_row_limit bool DEFAULT false,\n    compression bool DEFAULT false,\n    compression_level bool DEFAULT false)\n    RETURNS void\n    LANGUAGE plpgsql AS\n$alter_columnar_table_reset$\ndeclare\n  noop BOOLEAN := true;\n  cmd  TEXT    := 'ALTER TABLE ' || table_name::text || ' RESET (';\nbegin\n  if (chunk_group_row_limit) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd   || 'columnar.chunk_group_row_limit';\n    noop := false;\n  end if;\n  if (stripe_row_limit) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.stripe_row_limit';\n    noop := false;\n  end if;\n  if (compression) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression';\n    noop := false;\n  end if;\n  if (compression_level) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression_level';\n    noop := false;\n  end if;\n  cmd := cmd || ')';\n  if (not noop) then\n    execute cmd;\n  end if;\n  return;\nend;\n$alter_columnar_table_reset$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool,\n    stripe_row_limit bool,\n    compression bool,\n    compression_level bool)\nIS 'reset on or more options on a columnar table to the system defaults';\n\n-- rename columnar schema to columnar_internal and tighten security\n\nREVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA columnar FROM PUBLIC;\nALTER SCHEMA columnar RENAME TO columnar_internal;\nREVOKE ALL PRIVILEGES ON SCHEMA columnar_internal FROM PUBLIC;\n\n-- create columnar schema with public usage privileges\n\nCREATE SCHEMA columnar;\nGRANT USAGE ON SCHEMA columnar TO PUBLIC;\n\n--#include \"udfs/upgrade_columnar_storage/10.2-1.sql\"\nCREATE OR REPLACE FUNCTION columnar_internal.upgrade_columnar_storage(rel regclass)\n  RETURNS VOID\n  STRICT\n  LANGUAGE c AS 'MODULE_PATHNAME', $$upgrade_columnar_storage$$;\n\nCOMMENT ON FUNCTION columnar_internal.upgrade_columnar_storage(regclass)\n  IS 'function to upgrade the columnar storage, if necessary';\n\n\n--#include \"udfs/downgrade_columnar_storage/10.2-1.sql\"\n\nCREATE OR REPLACE FUNCTION columnar_internal.downgrade_columnar_storage(rel regclass)\n  RETURNS VOID\n  STRICT\n  LANGUAGE c AS 'MODULE_PATHNAME', $$downgrade_columnar_storage$$;\n\nCOMMENT ON FUNCTION columnar_internal.downgrade_columnar_storage(regclass)\n  IS 'function to downgrade the columnar storage, if necessary';\n\n-- update UDF to account for columnar_internal schema\nCREATE OR REPLACE FUNCTION columnar_internal.columnar_ensure_am_depends_catalog()\n  RETURNS void\n  LANGUAGE plpgsql\n  SET search_path = pg_catalog\nAS $func$\nBEGIN\n  INSERT INTO pg_depend\n  WITH columnar_schema_members(relid) AS (\n    SELECT pg_class.oid AS relid FROM pg_class\n      WHERE relnamespace =\n            COALESCE(\n\t       (SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = 'columnar_internal'),\n\t       (SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = 'columnar')\n\t    )\n        AND relname IN ('chunk',\n                        'chunk_group',\n                        'chunk_group_pkey',\n                        'chunk_pkey',\n                        'options',\n                        'options_pkey',\n                        'storageid_seq',\n                        'stripe',\n                        'stripe_first_row_number_idx',\n                        'stripe_pkey')\n  )\n  SELECT -- Define a dependency edge from \"columnar table access method\" ..\n         'pg_am'::regclass::oid as classid,\n         (select oid from pg_am where amname = 'columnar') as objid,\n         0 as objsubid,\n         -- ... to each object that is registered to pg_class and that lives\n         -- in \"columnar\" schema. That contains catalog tables, indexes\n         -- created on them and the sequences created in \"columnar\" schema.\n         --\n         -- Given the possibility of user might have created their own objects\n         -- in columnar schema, we explicitly specify list of objects that we\n         -- are interested in.\n         'pg_class'::regclass::oid as refclassid,\n         columnar_schema_members.relid as refobjid,\n         0 as refobjsubid,\n         'n' as deptype\n  FROM columnar_schema_members\n  -- Avoid inserting duplicate entries into pg_depend.\n  EXCEPT TABLE pg_depend;\nEND;\n$func$;\nCOMMENT ON FUNCTION columnar_internal.columnar_ensure_am_depends_catalog()\n  IS 'internal function responsible for creating dependencies from columnar '\n     'table access method to the rel objects in columnar schema';\n\nSELECT columnar_internal.columnar_ensure_am_depends_catalog();\n\n-- add utility function\n\nCREATE FUNCTION columnar.get_storage_id(regclass) RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'citus_columnar', $$columnar_relation_storageid$$;\n\n-- create views for columnar table information\n\nCREATE VIEW columnar.storage WITH (security_barrier) AS\n  SELECT c.oid::regclass AS relation,\n         columnar.get_storage_id(c.oid) AS storage_id\n    FROM pg_class c, pg_am am\n    WHERE c.relam = am.oid AND am.amname = 'columnar'\n      AND pg_has_role(c.relowner, 'USAGE');\nCOMMENT ON VIEW columnar.storage IS 'Columnar relation ID to storage ID mapping.';\nGRANT SELECT ON columnar.storage TO PUBLIC;\n\nCREATE VIEW columnar.options WITH (security_barrier) AS\n  SELECT regclass AS relation, chunk_group_row_limit,\n         stripe_row_limit, compression, compression_level\n    FROM columnar_internal.options o, pg_class c\n    WHERE o.regclass = c.oid\n      AND pg_has_role(c.relowner, 'USAGE');\nCOMMENT ON VIEW columnar.options\n  IS 'Columnar options for tables on which the current user has ownership privileges.';\nGRANT SELECT ON columnar.options TO PUBLIC;\n\nCREATE VIEW columnar.stripe WITH (security_barrier) AS\n  SELECT relation, storage.storage_id, stripe_num, file_offset, data_length,\n         column_count, chunk_row_count, row_count, chunk_group_count, first_row_number\n    FROM columnar_internal.stripe stripe, columnar.storage storage\n    WHERE stripe.storage_id = storage.storage_id;\nCOMMENT ON VIEW columnar.stripe\n  IS 'Columnar stripe information for tables on which the current user has ownership privileges.';\nGRANT SELECT ON columnar.stripe TO PUBLIC;\n\nCREATE VIEW columnar.chunk_group WITH (security_barrier) AS\n  SELECT relation, storage.storage_id, stripe_num, chunk_group_num, row_count\n    FROM columnar_internal.chunk_group cg, columnar.storage storage\n    WHERE cg.storage_id = storage.storage_id;\nCOMMENT ON VIEW columnar.chunk_group\n  IS 'Columnar chunk group information for tables on which the current user has ownership privileges.';\nGRANT SELECT ON columnar.chunk_group TO PUBLIC;\n\nCREATE VIEW columnar.chunk WITH (security_barrier) AS\n  SELECT relation, storage.storage_id, stripe_num, attr_num, chunk_group_num,\n         minimum_value, maximum_value, value_stream_offset, value_stream_length,\n         exists_stream_offset, exists_stream_length, value_compression_type,\n         value_compression_level, value_decompressed_length, value_count\n    FROM columnar_internal.chunk chunk, columnar.storage storage\n    WHERE chunk.storage_id = storage.storage_id;\nCOMMENT ON VIEW columnar.chunk\n  IS 'Columnar chunk information for tables on which the current user has ownership privileges.';\nGRANT SELECT ON columnar.chunk TO PUBLIC;\n\n"
  },
  {
    "path": "src/backend/columnar/sql/citus_columnar--11.2-1--11.3-1.sql",
    "content": "-- citus_columnar--11.2-1--11.3-1\n"
  },
  {
    "path": "src/backend/columnar/sql/citus_columnar--11.3-1--12.2-1.sql",
    "content": "-- citus_columnar--11.3-1--12.2-1\n"
  },
  {
    "path": "src/backend/columnar/sql/citus_columnar--12.2-1--13.2-1.sql",
    "content": "-- citus_columnar--12.2-1--13.2-1.sql\n\n#include \"udfs/columnar_finish_pg_upgrade/13.2-1.sql\"\n"
  },
  {
    "path": "src/backend/columnar/sql/citus_columnar--13.2-1--14.0-1.sql",
    "content": "-- citus_columnar--13.2-1--14.0-1\n-- bump version to 14.0-1\n"
  },
  {
    "path": "src/backend/columnar/sql/citus_columnar--14.0-1--15.0-1.sql",
    "content": "-- citus_columnar--14.0-1--15.0-1\n-- bump version to 15.0-1\n"
  },
  {
    "path": "src/backend/columnar/sql/columnar--10.0-1--10.0-2.sql",
    "content": "-- columnar--10.0-1--10.0-2.sql\n\n-- grant read access for columnar metadata tables to unprivileged user\nGRANT USAGE ON SCHEMA columnar TO PUBLIC;\nGRANT SELECT ON ALL tables IN SCHEMA columnar TO PUBLIC ;\n"
  },
  {
    "path": "src/backend/columnar/sql/columnar--10.0-3--10.1-1.sql",
    "content": "-- columnar--10.0-3--10.1-1.sql\n\n-- Drop foreign keys between columnar metadata tables.\n-- Postgres assigns different names to those foreign keys in PG11, so act accordingly.\nDO $proc$\nBEGIN\nIF substring(current_Setting('server_version'), '\\d+')::int >= 12 THEN\n  EXECUTE $$\nALTER TABLE columnar.chunk DROP CONSTRAINT chunk_storage_id_stripe_num_chunk_group_num_fkey;\nALTER TABLE columnar.chunk_group DROP CONSTRAINT chunk_group_storage_id_stripe_num_fkey;\n  $$;\nELSE\n  EXECUTE $$\nALTER TABLE columnar.chunk DROP CONSTRAINT chunk_storage_id_fkey;\nALTER TABLE columnar.chunk_group DROP CONSTRAINT chunk_group_storage_id_fkey;\n  $$;\nEND IF;\nEND$proc$;\n\n-- since we dropped pg11 support, we don't need to worry about missing\n-- columnar objects when upgrading postgres\nDROP FUNCTION citus_internal.columnar_ensure_objects_exist();\n"
  },
  {
    "path": "src/backend/columnar/sql/columnar--10.1-1--10.2-1.sql",
    "content": "-- columnar--10.1-1--10.2-1.sql\n\n-- For a proper mapping between tid & (stripe, row_num), add a new column to\n-- columnar.stripe and define a BTREE index on this column.\n-- Also include storage_id column for per-relation scans.\nALTER TABLE columnar.stripe ADD COLUMN first_row_number bigint;\nCREATE INDEX stripe_first_row_number_idx ON columnar.stripe USING BTREE(storage_id, first_row_number);\n\n-- Populate first_row_number column of columnar.stripe table.\n--\n-- For simplicity, we calculate MAX(row_count) value across all the stripes\n-- of all the columanar tables and then use it to populate first_row_number\n-- column. This would introduce some gaps however we are okay with that since\n-- it's already the case with regular INSERT/COPY's.\nDO $$\nDECLARE\n  max_row_count bigint;\n  -- this should be equal to columnar_storage.h/COLUMNAR_FIRST_ROW_NUMBER\n  COLUMNAR_FIRST_ROW_NUMBER constant bigint := 1;\nBEGIN\n  SELECT MAX(row_count) INTO max_row_count FROM columnar.stripe;\n  UPDATE columnar.stripe SET first_row_number = COLUMNAR_FIRST_ROW_NUMBER +\n                                                (stripe_num - 1) * max_row_count;\nEND;\n$$;\n\n#include \"udfs/upgrade_columnar_storage/10.2-1.sql\"\n#include \"udfs/downgrade_columnar_storage/10.2-1.sql\"\n\n-- upgrade storage for all columnar relations\nPERFORM citus_internal.upgrade_columnar_storage(c.oid) FROM pg_class c, pg_am a\n  WHERE c.relam = a.oid AND amname = 'columnar';\n"
  },
  {
    "path": "src/backend/columnar/sql/columnar--10.2-1--10.2-2.sql",
    "content": "-- columnar--10.2-1--10.2-2.sql\n\n-- revoke read access for columnar.chunk from unprivileged\n-- user as it contains chunk min/max values\nREVOKE SELECT ON columnar.chunk FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/columnar/sql/columnar--10.2-2--10.2-3.sql",
    "content": "-- columnar--10.2-2--10.2-3.sql\n\n-- Since stripe_first_row_number_idx is required to scan a columnar table, we\n-- need to make sure that it is created before doing anything with columnar\n-- tables during pg upgrades.\n--\n-- However, a plain btree index is not a dependency of a table, so pg_upgrade\n-- cannot guarantee that stripe_first_row_number_idx gets created when\n-- creating columnar.stripe, unless we make it a unique \"constraint\".\n--\n-- To do that, drop stripe_first_row_number_idx and create a unique\n-- constraint with the same name to keep the code change at minimum.\n--\n-- If we have a pg_depend entry for this index, we can not drop it as\n-- the extension depends on it. Remove the pg_depend entry if it exists.\nDELETE FROM pg_depend\nWHERE classid = 'pg_am'::regclass::oid\n    AND objid IN (select oid from pg_am where amname = 'columnar')\n    AND objsubid = 0\n    AND refclassid = 'pg_class'::regclass::oid\n    AND refobjid = 'columnar.stripe_first_row_number_idx'::regclass::oid\n    AND refobjsubid = 0\n    AND deptype = 'n';\nDROP INDEX columnar.stripe_first_row_number_idx;\nALTER TABLE columnar.stripe ADD CONSTRAINT stripe_first_row_number_idx\nUNIQUE (storage_id, first_row_number);\n"
  },
  {
    "path": "src/backend/columnar/sql/columnar--10.2-3--10.2-4.sql",
    "content": "-- columnar--10.2-3--10.2-4.sql\n\n#include \"udfs/columnar_ensure_am_depends_catalog/10.2-4.sql\"\n\nPERFORM citus_internal.columnar_ensure_am_depends_catalog();\n"
  },
  {
    "path": "src/backend/columnar/sql/columnar--11.0-2--11.0-3.sql",
    "content": "-- no changes needed\n"
  },
  {
    "path": "src/backend/columnar/sql/columnar--11.0-3--11.1-1.sql",
    "content": "#include \"udfs/alter_columnar_table_set/11.1-1.sql\"\n#include \"udfs/alter_columnar_table_reset/11.1-1.sql\"\n\n-- rename columnar schema to columnar_internal and tighten security\n\nREVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA columnar FROM PUBLIC;\nALTER SCHEMA columnar RENAME TO columnar_internal;\nREVOKE ALL PRIVILEGES ON SCHEMA columnar_internal FROM PUBLIC;\n\n-- create columnar schema with public usage privileges\n\nCREATE SCHEMA columnar;\nGRANT USAGE ON SCHEMA columnar TO PUBLIC;\n\n-- update UDF to account for columnar_internal schema\n#include \"udfs/columnar_ensure_am_depends_catalog/11.1-1.sql\"\n\n-- add utility function\n\nCREATE FUNCTION columnar.get_storage_id(regclass) RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'citus_columnar', $$columnar_relation_storageid$$;\n\n-- create views for columnar table information\n\nCREATE VIEW columnar.storage WITH (security_barrier) AS\n  SELECT c.oid::regclass AS relation,\n         columnar.get_storage_id(c.oid) AS storage_id\n    FROM pg_class c, pg_am am\n    WHERE c.relam = am.oid AND am.amname = 'columnar'\n      AND pg_has_role(c.relowner, 'USAGE');\nCOMMENT ON VIEW columnar.storage IS 'Columnar relation ID to storage ID mapping.';\nGRANT SELECT ON columnar.storage TO PUBLIC;\n\nCREATE VIEW columnar.options WITH (security_barrier) AS\n  SELECT regclass AS relation, chunk_group_row_limit,\n         stripe_row_limit, compression, compression_level\n    FROM columnar_internal.options o, pg_class c\n    WHERE o.regclass = c.oid\n      AND pg_has_role(c.relowner, 'USAGE');\nCOMMENT ON VIEW columnar.options\n  IS 'Columnar options for tables on which the current user has ownership privileges.';\nGRANT SELECT ON columnar.options TO PUBLIC;\n\nCREATE VIEW columnar.stripe WITH (security_barrier) AS\n  SELECT relation, storage.storage_id, stripe_num, file_offset, data_length,\n         column_count, chunk_row_count, row_count, chunk_group_count, first_row_number\n    FROM columnar_internal.stripe stripe, columnar.storage storage\n    WHERE stripe.storage_id = storage.storage_id;\nCOMMENT ON VIEW columnar.stripe\n  IS 'Columnar stripe information for tables on which the current user has ownership privileges.';\nGRANT SELECT ON columnar.stripe TO PUBLIC;\n\nCREATE VIEW columnar.chunk_group WITH (security_barrier) AS\n  SELECT relation, storage.storage_id, stripe_num, chunk_group_num, row_count\n    FROM columnar_internal.chunk_group cg, columnar.storage storage\n    WHERE cg.storage_id = storage.storage_id;\nCOMMENT ON VIEW columnar.chunk_group\n  IS 'Columnar chunk group information for tables on which the current user has ownership privileges.';\nGRANT SELECT ON columnar.chunk_group TO PUBLIC;\n\nCREATE VIEW columnar.chunk WITH (security_barrier) AS\n  SELECT relation, storage.storage_id, stripe_num, attr_num, chunk_group_num,\n         minimum_value, maximum_value, value_stream_offset, value_stream_length,\n         exists_stream_offset, exists_stream_length, value_compression_type,\n         value_compression_level, value_decompressed_length, value_count\n    FROM columnar_internal.chunk chunk, columnar.storage storage\n    WHERE chunk.storage_id = storage.storage_id;\nCOMMENT ON VIEW columnar.chunk\n  IS 'Columnar chunk information for tables on which the current user has ownership privileges.';\nGRANT SELECT ON columnar.chunk TO PUBLIC;\n"
  },
  {
    "path": "src/backend/columnar/sql/columnar--9.5-1--10.0-1.sql",
    "content": "-- columnar--9.5-1--10.0-1.sql\n\nCREATE SCHEMA columnar;\nSET search_path TO columnar;\n\nCREATE SEQUENCE storageid_seq MINVALUE 10000000000 NO CYCLE;\n\nCREATE TABLE options (\n    regclass regclass NOT NULL PRIMARY KEY,\n    chunk_group_row_limit int NOT NULL,\n    stripe_row_limit int NOT NULL,\n    compression_level int NOT NULL,\n    compression name NOT NULL\n) WITH (user_catalog_table = true);\n\nCOMMENT ON TABLE options IS 'columnar table specific options, maintained by alter_columnar_table_set';\n\nCREATE TABLE stripe (\n    storage_id bigint NOT NULL,\n    stripe_num bigint NOT NULL,\n    file_offset bigint NOT NULL,\n    data_length bigint NOT NULL,\n    column_count int NOT NULL,\n    chunk_row_count int NOT NULL,\n    row_count bigint NOT NULL,\n    chunk_group_count int NOT NULL,\n    PRIMARY KEY (storage_id, stripe_num)\n) WITH (user_catalog_table = true);\n\nCOMMENT ON TABLE stripe IS 'Columnar per stripe metadata';\n\nCREATE TABLE chunk_group (\n    storage_id bigint NOT NULL,\n    stripe_num bigint NOT NULL,\n    chunk_group_num int NOT NULL,\n    row_count bigint NOT NULL,\n    PRIMARY KEY (storage_id, stripe_num, chunk_group_num),\n    FOREIGN KEY (storage_id, stripe_num) REFERENCES stripe(storage_id, stripe_num) ON DELETE CASCADE\n);\n\nCOMMENT ON TABLE chunk_group IS 'Columnar chunk group metadata';\n\nCREATE TABLE chunk (\n    storage_id bigint NOT NULL,\n    stripe_num bigint NOT NULL,\n    attr_num int NOT NULL,\n    chunk_group_num int NOT NULL,\n    minimum_value bytea,\n    maximum_value bytea,\n    value_stream_offset bigint NOT NULL,\n    value_stream_length bigint NOT NULL,\n    exists_stream_offset bigint NOT NULL,\n    exists_stream_length bigint NOT NULL,\n    value_compression_type int NOT NULL,\n    value_compression_level int NOT NULL,\n    value_decompressed_length bigint NOT NULL,\n    value_count bigint NOT NULL,\n    PRIMARY KEY (storage_id, stripe_num, attr_num, chunk_group_num),\n    FOREIGN KEY (storage_id, stripe_num, chunk_group_num) REFERENCES chunk_group(storage_id, stripe_num, chunk_group_num) ON DELETE CASCADE\n) WITH (user_catalog_table = true);\n\nCOMMENT ON TABLE chunk IS 'Columnar per chunk metadata';\n\nDO $proc$\nBEGIN\n\n-- from version 12 and up we have support for tableam's if installed on pg11 we can't\n-- create the objects here. Instead we rely on citus_finish_pg_upgrade to be called by the\n-- user instead to add the missing objects\nIF substring(current_Setting('server_version'), '\\d+')::int >= 12 THEN\n  EXECUTE $$\n#include \"udfs/columnar_handler/10.0-1.sql\"\n#include \"udfs/alter_columnar_table_set/10.0-1.sql\"\n#include \"udfs/alter_columnar_table_reset/10.0-1.sql\"\n  $$;\nEND IF;\nEND$proc$;\n\n#include \"udfs/columnar_ensure_objects_exist/10.0-1.sql\"\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/citus--11.0-3--11.0-2.sql",
    "content": "-- no changes needed\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/citus_columnar--11.1-1--11.1-0.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int DEFAULT NULL,\n    stripe_row_limit int DEFAULT NULL,\n    compression name DEFAULT null,\n    compression_level int DEFAULT NULL)\n    RETURNS void\n    LANGUAGE C\nAS 'MODULE_PATHNAME', 'alter_columnar_table_set';\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int,\n    stripe_row_limit int,\n    compression name,\n    compression_level int)\nIS 'set one or more options on a columnar table, when set to NULL no change is made';\nCREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool DEFAULT false,\n    stripe_row_limit bool DEFAULT false,\n    compression bool DEFAULT false,\n    compression_level bool DEFAULT false)\n    RETURNS void\n    LANGUAGE C\nAS 'MODULE_PATHNAME', 'alter_columnar_table_reset';\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool,\n    stripe_row_limit bool,\n    compression bool,\n    compression_level bool)\nIS 'reset on or more options on a columnar table to the system defaults';\n\nCREATE OR REPLACE FUNCTION columnar_internal.columnar_ensure_am_depends_catalog()\n  RETURNS void\n  LANGUAGE plpgsql\n  SET search_path = pg_catalog\nAS $func$\nBEGIN\n  INSERT INTO pg_depend\n  SELECT -- Define a dependency edge from \"columnar table access method\" ..\n         'pg_am'::regclass::oid as classid,\n         (select oid from pg_am where amname = 'columnar') as objid,\n         0 as objsubid,\n         -- ... to each object that is registered to pg_class and that lives\n         -- in \"columnar\" schema. That contains catalog tables, indexes\n         -- created on them and the sequences created in \"columnar\" schema.\n         --\n         -- Given the possibility of user might have created their own objects\n         -- in columnar schema, we explicitly specify list of objects that we\n         -- are interested in.\n         'pg_class'::regclass::oid as refclassid,\n         columnar_schema_members.relname::regclass::oid as refobjid,\n         0 as refobjsubid,\n         'n' as deptype\n  FROM (VALUES ('columnar.chunk'),\n               ('columnar.chunk_group'),\n               ('columnar.chunk_group_pkey'),\n               ('columnar.chunk_pkey'),\n               ('columnar.options'),\n               ('columnar.options_pkey'),\n               ('columnar.storageid_seq'),\n               ('columnar.stripe'),\n               ('columnar.stripe_first_row_number_idx'),\n               ('columnar.stripe_pkey')\n       ) columnar_schema_members(relname)\n  -- Avoid inserting duplicate entries into pg_depend.\n  EXCEPT TABLE pg_depend;\nEND;\n$func$;\nCOMMENT ON FUNCTION columnar_internal.columnar_ensure_am_depends_catalog()\n  IS 'internal function responsible for creating dependencies from columnar '\n     'table access method to the rel objects in columnar schema';\n\nDROP VIEW columnar.options;\nDROP VIEW columnar.stripe;\nDROP VIEW columnar.chunk_group;\nDROP VIEW columnar.chunk;\nDROP VIEW columnar.storage;\nDROP FUNCTION columnar.get_storage_id(regclass);\n\nDROP SCHEMA columnar;\n\n-- move columnar_internal functions back to citus_internal\n\nALTER FUNCTION columnar_internal.upgrade_columnar_storage(regclass) SET SCHEMA citus_internal;\nALTER FUNCTION columnar_internal.downgrade_columnar_storage(regclass) SET SCHEMA citus_internal;\nALTER FUNCTION columnar_internal.columnar_ensure_am_depends_catalog() SET SCHEMA citus_internal;\n\nALTER SCHEMA columnar_internal RENAME TO columnar;\nGRANT USAGE ON SCHEMA columnar TO PUBLIC;\nGRANT SELECT ON columnar.options TO PUBLIC;\nGRANT SELECT ON columnar.stripe TO PUBLIC;\nGRANT SELECT ON columnar.chunk_group TO PUBLIC;\n\n-- detach relations from citus_columnar\n\nALTER EXTENSION citus_columnar DROP SCHEMA columnar;\nALTER EXTENSION citus_columnar DROP SEQUENCE columnar.storageid_seq;\n-- columnar tables\nALTER EXTENSION citus_columnar DROP TABLE columnar.options;\nALTER EXTENSION citus_columnar DROP TABLE columnar.stripe;\nALTER EXTENSION citus_columnar DROP TABLE columnar.chunk_group;\nALTER EXTENSION citus_columnar DROP TABLE columnar.chunk;\n\nALTER EXTENSION citus_columnar DROP FUNCTION columnar.columnar_handler;\nALTER EXTENSION citus_columnar DROP ACCESS METHOD columnar;\nALTER EXTENSION citus_columnar DROP FUNCTION pg_catalog.alter_columnar_table_set;\nALTER EXTENSION citus_columnar DROP FUNCTION pg_catalog.alter_columnar_table_reset;\n\n-- functions under citus_internal for columnar\nALTER EXTENSION citus_columnar DROP FUNCTION citus_internal.upgrade_columnar_storage;\nALTER EXTENSION citus_columnar DROP FUNCTION citus_internal.downgrade_columnar_storage;\nALTER EXTENSION citus_columnar DROP FUNCTION citus_internal.columnar_ensure_am_depends_catalog;\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/citus_columnar--11.2-1--11.1-1.sql",
    "content": "-- citus_columnar--11.2-1--11.1-1\n\n-- Note that we intentionally do not re-insert the pg_depend records that we\n-- deleted via citus_columnar--11.1-1--11.2-1.sql.\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/citus_columnar--11.3-1--11.2-1.sql",
    "content": "-- citus_columnar--11.3-1--11.2-1\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/citus_columnar--12.2-1--11.3-1.sql",
    "content": "-- citus_columnar--12.2-1--11.3-1\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/citus_columnar--13.2-1--12.2-1.sql",
    "content": "-- citus_columnar--13.2-1--12.2-1.sql\n\nDROP FUNCTION IF EXISTS pg_catalog.columnar_finish_pg_upgrade();\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/citus_columnar--14.0-1--13.2-1.sql",
    "content": "-- citus_columnar--14.0-1--13.2-1\n-- downgrade version to 13.2-1\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/citus_columnar--15.0-1--14.0-1.sql",
    "content": "-- citus_columnar--15.0-1--14.0-1\n-- downgrade version to 14.0-1\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/columnar--10.0-1--9.5-1.sql",
    "content": "-- columnar--10.0-1--9.5-1.sql\n\nSET search_path TO columnar;\n\nDO $proc$\nBEGIN\n\nIF substring(current_Setting('server_version'), '\\d+')::int >= 12 THEN\n  EXECUTE $$\n    DROP FUNCTION pg_catalog.alter_columnar_table_reset(\n        table_name regclass,\n        chunk_group_row_limit bool,\n        stripe_row_limit bool,\n        compression bool,\n        compression_level bool);\n\n    DROP FUNCTION pg_catalog.alter_columnar_table_set(\n        table_name regclass,\n        chunk_group_row_limit int,\n        stripe_row_limit int,\n        compression name,\n        compression_level int);\n\n    DROP ACCESS METHOD columnar;\n\n    DROP FUNCTION columnar_handler(internal);\n\n  $$;\nEND IF;\nEND$proc$;\n\nDROP TABLE chunk;\nDROP TABLE chunk_group;\nDROP TABLE stripe;\nDROP TABLE options;\nDROP SEQUENCE storageid_seq;\n\nDROP FUNCTION citus_internal.columnar_ensure_objects_exist();\n\nRESET search_path;\nDROP SCHEMA columnar;\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/columnar--10.0-2--10.0-1.sql",
    "content": "-- columnar--10.0-2--10.0-1.sql\n\n-- revoke read access for columnar metadata tables from unprivileged user\nREVOKE USAGE ON SCHEMA columnar FROM PUBLIC;\nREVOKE SELECT ON ALL tables IN SCHEMA columnar FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/columnar--10.1-1--10.0-3.sql",
    "content": "-- columnar--10.1-1--10.0-3.sql\n\n-- define foreign keys between columnar metadata tables\nALTER TABLE columnar.chunk\nADD FOREIGN KEY (storage_id, stripe_num, chunk_group_num)\nREFERENCES columnar.chunk_group(storage_id, stripe_num, chunk_group_num) ON DELETE CASCADE;\n\nALTER TABLE columnar.chunk_group\nADD FOREIGN KEY (storage_id, stripe_num)\nREFERENCES columnar.stripe(storage_id, stripe_num) ON DELETE CASCADE;\n\n-- define columnar_ensure_objects_exist again\n#include \"../udfs/columnar_ensure_objects_exist/10.0-1.sql\"\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/columnar--10.2-1--10.1-1.sql",
    "content": "-- columnar--10.2-1--10.1-1.sql\n\n-- downgrade storage for all columnar relations\nSELECT citus_internal.downgrade_columnar_storage(c.oid) FROM pg_class c, pg_am a\n  WHERE c.relam = a.oid AND amname = 'columnar';\n\nDROP FUNCTION citus_internal.upgrade_columnar_storage(regclass);\nDROP FUNCTION citus_internal.downgrade_columnar_storage(regclass);\n\n-- drop \"first_row_number\" column and the index defined on it\n--\n-- If we have a pg_depend entry for this index, we can not drop it as\n-- the extension depends on it. Remove the pg_depend entry if it exists.\nDELETE FROM pg_depend\nWHERE classid = 'pg_am'::regclass::oid\n    AND objid IN (select oid from pg_am where amname = 'columnar')\n    AND objsubid = 0\n    AND refclassid = 'pg_class'::regclass::oid\n    AND refobjid = 'columnar.stripe_first_row_number_idx'::regclass::oid\n    AND refobjsubid = 0\n    AND deptype = 'n';\nDROP INDEX columnar.stripe_first_row_number_idx;\nALTER TABLE columnar.stripe DROP COLUMN first_row_number;\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/columnar--10.2-2--10.2-1.sql",
    "content": "-- columnar--10.2-2--10.2-1.sql\n\n-- grant read access for columnar.chunk to unprivileged user\nGRANT SELECT ON columnar.chunk TO PUBLIC;\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/columnar--10.2-3--10.2-2.sql",
    "content": "-- columnar--10.2-3--10.2-2.sql\n--\n-- If we have a pg_depend entry for this index, we can not drop it as\n-- the extension depends on it. Remove the pg_depend entry if it exists.\nDELETE FROM pg_depend\nWHERE classid = 'pg_am'::regclass::oid\n    AND objid IN (select oid from pg_am where amname = 'columnar')\n    AND objsubid = 0\n    AND refclassid = 'pg_class'::regclass::oid\n    AND refobjid = 'columnar.stripe_first_row_number_idx'::regclass::oid\n    AND refobjsubid = 0\n    AND deptype = 'n';\nALTER TABLE columnar.stripe DROP CONSTRAINT stripe_first_row_number_idx;\nCREATE INDEX stripe_first_row_number_idx ON columnar.stripe USING BTREE(storage_id, first_row_number);\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/columnar--10.2-4--10.2-3.sql",
    "content": "-- columnar--10.2-4--10.2-3.sql\n\nDROP FUNCTION citus_internal.columnar_ensure_am_depends_catalog();\n\n-- Note that we intentionally do not delete pg_depend records that we inserted\n-- via columnar--10.2-3--10.2-4.sql (by using columnar_ensure_am_depends_catalog).\n"
  },
  {
    "path": "src/backend/columnar/sql/downgrades/columnar--11.1-1--11.0-3.sql",
    "content": "#include \"../udfs/alter_columnar_table_set/10.0-1.sql\"\n#include \"../udfs/alter_columnar_table_reset/10.0-1.sql\"\n\n#include \"../udfs/columnar_ensure_am_depends_catalog/10.2-4.sql\"\n\nDROP VIEW columnar.options;\nDROP VIEW columnar.stripe;\nDROP VIEW columnar.chunk_group;\nDROP VIEW columnar.chunk;\nDROP VIEW columnar.storage;\nDROP FUNCTION columnar.get_storage_id(regclass);\n\nDROP SCHEMA columnar;\n\nALTER SCHEMA columnar_internal RENAME TO columnar;\nGRANT USAGE ON SCHEMA columnar TO PUBLIC;\nGRANT SELECT ON columnar.options TO PUBLIC;\nGRANT SELECT ON columnar.stripe TO PUBLIC;\nGRANT SELECT ON columnar.chunk_group TO PUBLIC;\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/alter_columnar_table_reset/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool DEFAULT false,\n    stripe_row_limit bool DEFAULT false,\n    compression bool DEFAULT false,\n    compression_level bool DEFAULT false)\n    RETURNS void\n    LANGUAGE C\nAS 'MODULE_PATHNAME', 'alter_columnar_table_reset';\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool,\n    stripe_row_limit bool,\n    compression bool,\n    compression_level bool)\nIS 'reset on or more options on a columnar table to the system defaults';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/alter_columnar_table_reset/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool DEFAULT false,\n    stripe_row_limit bool DEFAULT false,\n    compression bool DEFAULT false,\n    compression_level bool DEFAULT false)\n    RETURNS void\n    LANGUAGE plpgsql AS\n$alter_columnar_table_reset$\ndeclare\n  noop BOOLEAN := true;\n  cmd  TEXT    := 'ALTER TABLE ' || table_name::text || ' RESET (';\nbegin\n  if (chunk_group_row_limit) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd   || 'columnar.chunk_group_row_limit';\n    noop := false;\n  end if;\n  if (stripe_row_limit) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.stripe_row_limit';\n    noop := false;\n  end if;\n  if (compression) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression';\n    noop := false;\n  end if;\n  if (compression_level) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression_level';\n    noop := false;\n  end if;\n  cmd := cmd || ')';\n  if (not noop) then\n    execute cmd;\n  end if;\n  return;\nend;\n$alter_columnar_table_reset$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool,\n    stripe_row_limit bool,\n    compression bool,\n    compression_level bool)\nIS 'reset on or more options on a columnar table to the system defaults';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/alter_columnar_table_reset/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool DEFAULT false,\n    stripe_row_limit bool DEFAULT false,\n    compression bool DEFAULT false,\n    compression_level bool DEFAULT false)\n    RETURNS void\n    LANGUAGE plpgsql AS\n$alter_columnar_table_reset$\ndeclare\n  noop BOOLEAN := true;\n  cmd  TEXT    := 'ALTER TABLE ' || table_name::text || ' RESET (';\nbegin\n  if (chunk_group_row_limit) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd   || 'columnar.chunk_group_row_limit';\n    noop := false;\n  end if;\n  if (stripe_row_limit) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.stripe_row_limit';\n    noop := false;\n  end if;\n  if (compression) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression';\n    noop := false;\n  end if;\n  if (compression_level) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression_level';\n    noop := false;\n  end if;\n  cmd := cmd || ')';\n  if (not noop) then\n    execute cmd;\n  end if;\n  return;\nend;\n$alter_columnar_table_reset$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_reset(\n    table_name regclass,\n    chunk_group_row_limit bool,\n    stripe_row_limit bool,\n    compression bool,\n    compression_level bool)\nIS 'reset on or more options on a columnar table to the system defaults';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/alter_columnar_table_set/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int DEFAULT NULL,\n    stripe_row_limit int DEFAULT NULL,\n    compression name DEFAULT null,\n    compression_level int DEFAULT NULL)\n    RETURNS void\n    LANGUAGE C\nAS 'MODULE_PATHNAME', 'alter_columnar_table_set';\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int,\n    stripe_row_limit int,\n    compression name,\n    compression_level int)\nIS 'set one or more options on a columnar table, when set to NULL no change is made';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/alter_columnar_table_set/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int DEFAULT NULL,\n    stripe_row_limit int DEFAULT NULL,\n    compression name DEFAULT null,\n    compression_level int DEFAULT NULL)\n    RETURNS void\n    LANGUAGE plpgsql AS\n$alter_columnar_table_set$\ndeclare\n  noop BOOLEAN := true;\n  cmd  TEXT    := 'ALTER TABLE ' || table_name::text || ' SET (';\nbegin\n  if (chunk_group_row_limit is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd  || 'columnar.chunk_group_row_limit=' || chunk_group_row_limit;\n    noop := false;\n  end if;\n  if (stripe_row_limit is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.stripe_row_limit=' || stripe_row_limit;\n    noop := false;\n  end if;\n  if (compression is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression=' || compression;\n    noop := false;\n  end if;\n  if (compression_level is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression_level=' || compression_level;\n    noop := false;\n  end if;\n  cmd := cmd || ')';\n  if (not noop) then\n    execute cmd;\n  end if;\n  return;\nend;\n$alter_columnar_table_set$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int,\n    stripe_row_limit int,\n    compression name,\n    compression_level int)\nIS 'set one or more options on a columnar table, when set to NULL no change is made';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/alter_columnar_table_set/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int DEFAULT NULL,\n    stripe_row_limit int DEFAULT NULL,\n    compression name DEFAULT null,\n    compression_level int DEFAULT NULL)\n    RETURNS void\n    LANGUAGE plpgsql AS\n$alter_columnar_table_set$\ndeclare\n  noop BOOLEAN := true;\n  cmd  TEXT    := 'ALTER TABLE ' || table_name::text || ' SET (';\nbegin\n  if (chunk_group_row_limit is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd  || 'columnar.chunk_group_row_limit=' || chunk_group_row_limit;\n    noop := false;\n  end if;\n  if (stripe_row_limit is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.stripe_row_limit=' || stripe_row_limit;\n    noop := false;\n  end if;\n  if (compression is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression=' || compression;\n    noop := false;\n  end if;\n  if (compression_level is not null) then\n    if (not noop) then cmd := cmd || ', '; end if;\n    cmd := cmd || 'columnar.compression_level=' || compression_level;\n    noop := false;\n  end if;\n  cmd := cmd || ')';\n  if (not noop) then\n    execute cmd;\n  end if;\n  return;\nend;\n$alter_columnar_table_set$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_columnar_table_set(\n    table_name regclass,\n    chunk_group_row_limit int,\n    stripe_row_limit int,\n    compression name,\n    compression_level int)\nIS 'set one or more options on a columnar table, when set to NULL no change is made';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/columnar_ensure_am_depends_catalog/10.2-4.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.columnar_ensure_am_depends_catalog()\n  RETURNS void\n  LANGUAGE plpgsql\n  SET search_path = pg_catalog\nAS $func$\nBEGIN\n  INSERT INTO pg_depend\n  SELECT -- Define a dependency edge from \"columnar table access method\" ..\n         'pg_am'::regclass::oid as classid,\n         (select oid from pg_am where amname = 'columnar') as objid,\n         0 as objsubid,\n         -- ... to each object that is registered to pg_class and that lives\n         -- in \"columnar\" schema. That contains catalog tables, indexes\n         -- created on them and the sequences created in \"columnar\" schema.\n         --\n         -- Given the possibility of user might have created their own objects\n         -- in columnar schema, we explicitly specify list of objects that we\n         -- are interested in.\n         'pg_class'::regclass::oid as refclassid,\n         columnar_schema_members.relname::regclass::oid as refobjid,\n         0 as refobjsubid,\n         'n' as deptype\n  FROM (VALUES ('columnar.chunk'),\n               ('columnar.chunk_group'),\n               ('columnar.chunk_group_pkey'),\n               ('columnar.chunk_pkey'),\n               ('columnar.options'),\n               ('columnar.options_pkey'),\n               ('columnar.storageid_seq'),\n               ('columnar.stripe'),\n               ('columnar.stripe_first_row_number_idx'),\n               ('columnar.stripe_pkey')\n       ) columnar_schema_members(relname)\n  -- Avoid inserting duplicate entries into pg_depend.\n  EXCEPT TABLE pg_depend;\nEND;\n$func$;\nCOMMENT ON FUNCTION citus_internal.columnar_ensure_am_depends_catalog()\n  IS 'internal function responsible for creating dependencies from columnar '\n     'table access method to the rel objects in columnar schema';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/columnar_ensure_am_depends_catalog/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.columnar_ensure_am_depends_catalog()\n  RETURNS void\n  LANGUAGE plpgsql\n  SET search_path = pg_catalog\nAS $func$\nBEGIN\n  INSERT INTO pg_depend\n  WITH columnar_schema_members(relid) AS (\n    SELECT pg_class.oid AS relid FROM pg_class\n      WHERE relnamespace =\n            COALESCE(\n\t       (SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = 'columnar_internal'),\n\t       (SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = 'columnar')\n\t    )\n        AND relname IN ('chunk',\n                        'chunk_group',\n                        'chunk_group_pkey',\n                        'chunk_pkey',\n                        'options',\n                        'options_pkey',\n                        'storageid_seq',\n                        'stripe',\n                        'stripe_first_row_number_idx',\n                        'stripe_pkey')\n  )\n  SELECT -- Define a dependency edge from \"columnar table access method\" ..\n         'pg_am'::regclass::oid as classid,\n         (select oid from pg_am where amname = 'columnar') as objid,\n         0 as objsubid,\n         -- ... to each object that is registered to pg_class and that lives\n         -- in \"columnar\" schema. That contains catalog tables, indexes\n         -- created on them and the sequences created in \"columnar\" schema.\n         --\n         -- Given the possibility of user might have created their own objects\n         -- in columnar schema, we explicitly specify list of objects that we\n         -- are interested in.\n         'pg_class'::regclass::oid as refclassid,\n         columnar_schema_members.relid as refobjid,\n         0 as refobjsubid,\n         'n' as deptype\n  FROM columnar_schema_members\n  -- Avoid inserting duplicate entries into pg_depend.\n  EXCEPT TABLE pg_depend;\nEND;\n$func$;\nCOMMENT ON FUNCTION citus_internal.columnar_ensure_am_depends_catalog()\n  IS 'internal function responsible for creating dependencies from columnar '\n     'table access method to the rel objects in columnar schema';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/columnar_ensure_am_depends_catalog/11.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION columnar_internal.columnar_ensure_am_depends_catalog()\n  RETURNS void\n  LANGUAGE plpgsql\n  SET search_path = pg_catalog\nAS $func$\nBEGIN\n  INSERT INTO pg_depend\n  WITH columnar_schema_members(relid) AS (\n    SELECT pg_class.oid AS relid FROM pg_class\n      WHERE relnamespace =\n            COALESCE(\n\t       (SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = 'columnar_internal'),\n\t       (SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = 'columnar')\n\t    )\n        AND relname IN ('chunk',\n                        'chunk_group',\n                        'options',\n                        'storageid_seq',\n                        'stripe')\n  )\n  SELECT -- Define a dependency edge from \"columnar table access method\" ..\n         'pg_am'::regclass::oid as classid,\n         (select oid from pg_am where amname = 'columnar') as objid,\n         0 as objsubid,\n         -- ... to some objects registered as regclass and that lives in\n         -- \"columnar\" schema. That contains catalog tables and the sequences\n         -- created in \"columnar\" schema.\n         --\n         -- Given the possibility of user might have created their own objects\n         -- in columnar schema, we explicitly specify list of objects that we\n         -- are interested in.\n         'pg_class'::regclass::oid as refclassid,\n         columnar_schema_members.relid as refobjid,\n         0 as refobjsubid,\n         'n' as deptype\n  FROM columnar_schema_members\n  -- Avoid inserting duplicate entries into pg_depend.\n  EXCEPT TABLE pg_depend;\nEND;\n$func$;\nCOMMENT ON FUNCTION columnar_internal.columnar_ensure_am_depends_catalog()\n  IS 'internal function responsible for creating dependencies from columnar '\n     'table access method to the rel objects in columnar schema';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/columnar_ensure_am_depends_catalog/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION columnar_internal.columnar_ensure_am_depends_catalog()\n  RETURNS void\n  LANGUAGE plpgsql\n  SET search_path = pg_catalog\nAS $func$\nBEGIN\n  INSERT INTO pg_depend\n  WITH columnar_schema_members(relid) AS (\n    SELECT pg_class.oid AS relid FROM pg_class\n      WHERE relnamespace =\n            COALESCE(\n\t       (SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = 'columnar_internal'),\n\t       (SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = 'columnar')\n\t    )\n        AND relname IN ('chunk',\n                        'chunk_group',\n                        'options',\n                        'storageid_seq',\n                        'stripe')\n  )\n  SELECT -- Define a dependency edge from \"columnar table access method\" ..\n         'pg_am'::regclass::oid as classid,\n         (select oid from pg_am where amname = 'columnar') as objid,\n         0 as objsubid,\n         -- ... to some objects registered as regclass and that lives in\n         -- \"columnar\" schema. That contains catalog tables and the sequences\n         -- created in \"columnar\" schema.\n         --\n         -- Given the possibility of user might have created their own objects\n         -- in columnar schema, we explicitly specify list of objects that we\n         -- are interested in.\n         'pg_class'::regclass::oid as refclassid,\n         columnar_schema_members.relid as refobjid,\n         0 as refobjsubid,\n         'n' as deptype\n  FROM columnar_schema_members\n  -- Avoid inserting duplicate entries into pg_depend.\n  EXCEPT TABLE pg_depend;\nEND;\n$func$;\nCOMMENT ON FUNCTION columnar_internal.columnar_ensure_am_depends_catalog()\n  IS 'internal function responsible for creating dependencies from columnar '\n     'table access method to the rel objects in columnar schema';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/columnar_ensure_objects_exist/10.0-1.sql",
    "content": "-- citus_internal.columnar_ensure_objects_exist is an internal helper function to create\n-- missing objects, anything related to the table access methods.\n-- Since the API for table access methods is only available in PG12 we can't create these\n-- objects when Citus is installed in PG11. Once citus is installed on PG11 the user can\n-- upgrade their database to PG12. Now they require the table access method objects that\n-- we couldn't create before.\n-- This internal function is called from `citus_finish_pg_upgrade` which the user is\n-- required to call after a PG major upgrade.\nCREATE OR REPLACE FUNCTION citus_internal.columnar_ensure_objects_exist()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\nAS $ceoe$\nBEGIN\n\n-- when postgres is version 12 or above we need to create the tableam. If the tableam\n-- exist we assume all objects have been created.\nIF substring(current_Setting('server_version'), '\\d+')::int >= 12 THEN\nIF NOT EXISTS (SELECT 1 FROM pg_am WHERE amname = 'columnar') THEN\n\n#include \"../columnar_handler/10.0-1.sql\"\n\n#include \"../alter_columnar_table_set/10.0-1.sql\"\n\n#include \"../alter_columnar_table_reset/10.0-1.sql\"\n\n    -- add the missing objects to the extension\n    ALTER EXTENSION citus ADD FUNCTION columnar.columnar_handler(internal);\n    ALTER EXTENSION citus ADD ACCESS METHOD columnar;\n    ALTER EXTENSION citus ADD FUNCTION pg_catalog.alter_columnar_table_set(\n        table_name regclass,\n        chunk_group_row_limit int,\n        stripe_row_limit int,\n        compression name,\n        compression_level int);\n    ALTER EXTENSION citus ADD FUNCTION pg_catalog.alter_columnar_table_reset(\n        table_name regclass,\n        chunk_group_row_limit bool,\n        stripe_row_limit bool,\n        compression bool,\n        compression_level bool);\n\nEND IF;\nEND IF;\nEND;\n$ceoe$;\n\nCOMMENT ON FUNCTION citus_internal.columnar_ensure_objects_exist()\n    IS 'internal function to be called by pg_catalog.citus_finish_pg_upgrade responsible for creating the columnar objects';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/columnar_ensure_objects_exist/latest.sql",
    "content": "-- citus_internal.columnar_ensure_objects_exist is an internal helper function to create\n-- missing objects, anything related to the table access methods.\n-- Since the API for table access methods is only available in PG12 we can't create these\n-- objects when Citus is installed in PG11. Once citus is installed on PG11 the user can\n-- upgrade their database to PG12. Now they require the table access method objects that\n-- we couldn't create before.\n-- This internal function is called from `citus_finish_pg_upgrade` which the user is\n-- required to call after a PG major upgrade.\nCREATE OR REPLACE FUNCTION citus_internal.columnar_ensure_objects_exist()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\nAS $ceoe$\nBEGIN\n\n-- when postgres is version 12 or above we need to create the tableam. If the tableam\n-- exist we assume all objects have been created.\nIF substring(current_Setting('server_version'), '\\d+')::int >= 12 THEN\nIF NOT EXISTS (SELECT 1 FROM pg_am WHERE amname = 'columnar') THEN\n\n#include \"../columnar_handler/10.0-1.sql\"\n\n#include \"../alter_columnar_table_set/10.0-1.sql\"\n\n#include \"../alter_columnar_table_reset/10.0-1.sql\"\n\n    -- add the missing objects to the extension\n    ALTER EXTENSION citus ADD FUNCTION columnar.columnar_handler(internal);\n    ALTER EXTENSION citus ADD ACCESS METHOD columnar;\n    ALTER EXTENSION citus ADD FUNCTION pg_catalog.alter_columnar_table_set(\n        table_name regclass,\n        chunk_group_row_limit int,\n        stripe_row_limit int,\n        compression name,\n        compression_level int);\n    ALTER EXTENSION citus ADD FUNCTION pg_catalog.alter_columnar_table_reset(\n        table_name regclass,\n        chunk_group_row_limit bool,\n        stripe_row_limit bool,\n        compression bool,\n        compression_level bool);\n\nEND IF;\nEND IF;\nEND;\n$ceoe$;\n\nCOMMENT ON FUNCTION citus_internal.columnar_ensure_objects_exist()\n    IS 'internal function to be called by pg_catalog.citus_finish_pg_upgrade responsible for creating the columnar objects';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/columnar_finish_pg_upgrade/13.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.columnar_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n    -- set dependencies for columnar table access method\n    PERFORM columnar_internal.columnar_ensure_am_depends_catalog();\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.columnar_finish_pg_upgrade()\n    IS 'perform tasks to properly complete a Postgres upgrade for columnar extension';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/columnar_finish_pg_upgrade/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.columnar_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n    -- set dependencies for columnar table access method\n    PERFORM columnar_internal.columnar_ensure_am_depends_catalog();\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.columnar_finish_pg_upgrade()\n    IS 'perform tasks to properly complete a Postgres upgrade for columnar extension';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/columnar_handler/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION columnar.columnar_handler(internal)\n    RETURNS table_am_handler\n    LANGUAGE C\nAS 'MODULE_PATHNAME', 'columnar_handler';\n\nCOMMENT ON FUNCTION columnar.columnar_handler(internal)\n    IS 'internal function returning the handler for columnar tables';\n\n-- postgres 11.8 does not support the syntax for table am, also it is seemingly trying\n-- to parse the upgrade file and erroring on unknown syntax.\n-- normally this section would not execute on postgres 11 anyway. To trick it to pass on\n-- 11.8 we wrap the statement in a plpgsql block together with an EXECUTE. This is valid\n-- syntax on 11.8 and will execute correctly in 12\nDO $create_table_am$\nBEGIN\nEXECUTE 'CREATE ACCESS METHOD columnar TYPE TABLE HANDLER columnar.columnar_handler';\nEND $create_table_am$;\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/columnar_handler/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION columnar.columnar_handler(internal)\n    RETURNS table_am_handler\n    LANGUAGE C\nAS 'MODULE_PATHNAME', 'columnar_handler';\n\nCOMMENT ON FUNCTION columnar.columnar_handler(internal)\n    IS 'internal function returning the handler for columnar tables';\n\n-- postgres 11.8 does not support the syntax for table am, also it is seemingly trying\n-- to parse the upgrade file and erroring on unknown syntax.\n-- normally this section would not execute on postgres 11 anyway. To trick it to pass on\n-- 11.8 we wrap the statement in a plpgsql block together with an EXECUTE. This is valid\n-- syntax on 11.8 and will execute correctly in 12\nDO $create_table_am$\nBEGIN\nEXECUTE 'CREATE ACCESS METHOD columnar TYPE TABLE HANDLER columnar.columnar_handler';\nEND $create_table_am$;\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/downgrade_columnar_storage/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.downgrade_columnar_storage(rel regclass)\n  RETURNS VOID\n  STRICT\n  LANGUAGE c AS 'MODULE_PATHNAME', $$downgrade_columnar_storage$$;\n\nCOMMENT ON FUNCTION citus_internal.downgrade_columnar_storage(regclass)\n  IS 'function to downgrade the columnar storage, if necessary';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/downgrade_columnar_storage/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.downgrade_columnar_storage(rel regclass)\n  RETURNS VOID\n  STRICT\n  LANGUAGE c AS 'MODULE_PATHNAME', $$downgrade_columnar_storage$$;\n\nCOMMENT ON FUNCTION citus_internal.downgrade_columnar_storage(regclass)\n  IS 'function to downgrade the columnar storage, if necessary';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/upgrade_columnar_storage/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.upgrade_columnar_storage(rel regclass)\n  RETURNS VOID\n  STRICT\n  LANGUAGE c AS 'MODULE_PATHNAME', $$upgrade_columnar_storage$$;\n\nCOMMENT ON FUNCTION citus_internal.upgrade_columnar_storage(regclass)\n  IS 'function to upgrade the columnar storage, if necessary';\n"
  },
  {
    "path": "src/backend/columnar/sql/udfs/upgrade_columnar_storage/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.upgrade_columnar_storage(rel regclass)\n  RETURNS VOID\n  STRICT\n  LANGUAGE c AS 'MODULE_PATHNAME', $$upgrade_columnar_storage$$;\n\nCOMMENT ON FUNCTION citus_internal.upgrade_columnar_storage(regclass)\n  IS 'function to upgrade the columnar storage, if necessary';\n"
  },
  {
    "path": "src/backend/columnar/write_state_management.c",
    "content": "\n#include <math.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/heaptoast.h\"\n#include \"access/multixact.h\"\n#include \"access/rewriteheap.h\"\n#include \"access/tsmapi.h\"\n#include \"access/xact.h\"\n#include \"catalog/catalog.h\"\n#include \"catalog/index.h\"\n#include \"catalog/objectaccess.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_trigger.h\"\n#include \"catalog/storage.h\"\n#include \"catalog/storage_xlog.h\"\n#include \"commands/progress.h\"\n#include \"commands/vacuum.h\"\n#include \"common/hashfn.h\"\n#include \"executor/executor.h\"\n#include \"nodes/makefuncs.h\"\n#include \"optimizer/plancat.h\"\n#include \"storage/bufmgr.h\"\n#include \"storage/bufpage.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/predicate.h\"\n#include \"storage/procarray.h\"\n#include \"storage/smgr.h\"\n#include \"tcop/utility.h\"\n#include \"utils/builtins.h\"\n#include \"utils/pg_rusage.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n\n#include \"citus_version.h\"\n#include \"pg_version_compat.h\"\n\n#include \"columnar/columnar.h\"\n#include \"columnar/columnar_customscan.h\"\n#include \"columnar/columnar_tableam.h\"\n#include \"columnar/columnar_version_compat.h\"\n\n\n/*\n * Mapping from relfilenode to WriteStateMapEntry. This keeps write state for\n * each relation.\n */\nstatic HTAB *WriteStateMap = NULL;\n\n/* memory context for allocating WriteStateMap & all write states */\nstatic MemoryContext WriteStateContext = NULL;\n\n/*\n * Each member of the writeStateStack in WriteStateMapEntry. This means that\n * we did some inserts in the subtransaction subXid, and the state of those\n * inserts is stored at writeState. Those writes can be flushed or unflushed.\n */\ntypedef struct SubXidWriteState\n{\n\tSubTransactionId subXid;\n\tColumnarWriteState *writeState;\n\n\tstruct SubXidWriteState *next;\n} SubXidWriteState;\n\n\n/*\n * An entry in WriteStateMap.\n */\ntypedef struct WriteStateMapEntry\n{\n\t/* key of the entry */\n\tRelFileNumber relfilenumber;\n\n\t/*\n\t * If a table is dropped, we set dropped to true and set dropSubXid to the\n\t * id of the subtransaction in which the drop happened.\n\t */\n\tbool dropped;\n\tSubTransactionId dropSubXid;\n\n\t/*\n\t * Stack of SubXidWriteState where first element is top of the stack. When\n\t * inserts happen, we look at top of the stack. If top of stack belongs to\n\t * current subtransaction, we forward writes to its writeState. Otherwise,\n\t * we create a new stack entry for current subtransaction and push it to\n\t * the stack, and forward writes to that.\n\t */\n\tSubXidWriteState *writeStateStack;\n} WriteStateMapEntry;\n\n\n/*\n * Memory context reset callback so we reset WriteStateMap to NULL at the end\n * of transaction. WriteStateMap is allocated in & WriteStateMap, so its\n * leaked reference can cause memory issues.\n */\nstatic MemoryContextCallback cleanupCallback;\nstatic void\nCleanupWriteStateMap(void *arg)\n{\n\tWriteStateMap = NULL;\n\tWriteStateContext = NULL;\n}\n\n\nColumnarWriteState *\ncolumnar_init_write_state(Relation relation, TupleDesc tupdesc,\n\t\t\t\t\t\t  Oid tupSlotRelationId,\n\t\t\t\t\t\t  SubTransactionId currentSubXid)\n{\n\tbool found;\n\n\t/*\n\t * If this is the first call in current transaction, allocate the hash\n\t * table.\n\t */\n\tif (WriteStateMap == NULL)\n\t{\n\t\tWriteStateContext =\n\t\t\tAllocSetContextCreate(\n\t\t\t\tTopTransactionContext,\n\t\t\t\t\"Column Store Write State Management Context\",\n\t\t\t\tALLOCSET_DEFAULT_SIZES);\n\t\tHASHCTL info;\n\t\tuint32 hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);\n\t\tmemset(&info, 0, sizeof(info));\n\t\tinfo.keysize = sizeof(RelFileNumber);\n\t\tinfo.hash = oid_hash;\n\t\tinfo.entrysize = sizeof(WriteStateMapEntry);\n\t\tinfo.hcxt = WriteStateContext;\n\n\t\tWriteStateMap = hash_create(\"column store write state map\",\n\t\t\t\t\t\t\t\t\t64, &info, hashFlags);\n\n\t\tcleanupCallback.arg = NULL;\n\t\tcleanupCallback.func = &CleanupWriteStateMap;\n\t\tcleanupCallback.next = NULL;\n\t\tMemoryContextRegisterResetCallback(WriteStateContext, &cleanupCallback);\n\t}\n\n\tWriteStateMapEntry *hashEntry = hash_search(WriteStateMap,\n\t\t\t\t\t\t\t\t\t\t\t\t&(relation->rd_locator.relNumber),\n\t\t\t\t\t\t\t\t\t\t\t\tHASH_ENTER, &found);\n\tif (!found)\n\t{\n\t\thashEntry->writeStateStack = NULL;\n\t\thashEntry->dropped = false;\n\t}\n\n\tAssert(!hashEntry->dropped);\n\n\t/*\n\t * If top of stack belongs to the current subtransaction, return its\n\t * writeState, ...\n\t */\n\tif (hashEntry->writeStateStack != NULL)\n\t{\n\t\tSubXidWriteState *stackHead = hashEntry->writeStateStack;\n\n\t\tif (stackHead->subXid == currentSubXid)\n\t\t{\n\t\t\treturn stackHead->writeState;\n\t\t}\n\t}\n\n\t/*\n\t * ... otherwise we need to create a new stack entry for the current\n\t * subtransaction.\n\t */\n\tMemoryContext oldContext = MemoryContextSwitchTo(WriteStateContext);\n\n\tColumnarOptions columnarOptions = { 0 };\n\n\t/*\n\t * In case of a table rewrite, we need to fetch table options based on the\n\t * relation id of the source tuple slot.\n\t *\n\t * For this reason, we always pass tupSlotRelationId here; which should be\n\t * same as the target table if the write operation is not related to a table\n\t * rewrite etc.\n\t */\n\tReadColumnarOptions(tupSlotRelationId, &columnarOptions);\n\n\tSubXidWriteState *stackEntry = palloc0(sizeof(SubXidWriteState));\n\tstackEntry->writeState = ColumnarBeginWrite(relation,\n\t\t\t\t\t\t\t\t\t\t\t\tcolumnarOptions,\n\t\t\t\t\t\t\t\t\t\t\t\ttupdesc);\n\tstackEntry->subXid = currentSubXid;\n\tstackEntry->next = hashEntry->writeStateStack;\n\thashEntry->writeStateStack = stackEntry;\n\n\tMemoryContextSwitchTo(oldContext);\n\n\treturn stackEntry->writeState;\n}\n\n\n/*\n * Flushes pending writes for given relfilenode in the given subtransaction.\n */\nvoid\nFlushWriteStateForRelfilenumber(RelFileNumber relfilenumber,\n\t\t\t\t\t\t\t\tSubTransactionId currentSubXid)\n{\n\tif (WriteStateMap == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tWriteStateMapEntry *entry = hash_search(WriteStateMap, &relfilenumber, HASH_FIND,\n\t\t\t\t\t\t\t\t\t\t\tNULL);\n\n\tAssert(!entry || !entry->dropped);\n\n\tif (entry && entry->writeStateStack != NULL)\n\t{\n\t\tSubXidWriteState *stackEntry = entry->writeStateStack;\n\t\tif (stackEntry->subXid == currentSubXid)\n\t\t{\n\t\t\tColumnarFlushPendingWrites(stackEntry->writeState);\n\t\t}\n\t}\n}\n\n\n/*\n * Helper function for FlushWriteStateForAllRels and DiscardWriteStateForAllRels.\n * Pops all of write states for current subtransaction, and depending on \"commit\"\n * either flushes them or discards them. This also takes into account dropped\n * tables, and either propagates the dropped flag to parent subtransaction or\n * rolls back abort.\n */\nstatic void\nPopWriteStateForAllRels(SubTransactionId currentSubXid, SubTransactionId parentSubXid,\n\t\t\t\t\t\tbool commit)\n{\n\tHASH_SEQ_STATUS status;\n\tWriteStateMapEntry *entry;\n\n\tif (WriteStateMap == NULL)\n\t{\n\t\treturn;\n\t}\n\n\thash_seq_init(&status, WriteStateMap);\n\twhile ((entry = hash_seq_search(&status)) != 0)\n\t{\n\t\tif (entry->writeStateStack == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * If the table has been dropped in current subtransaction, either\n\t\t * commit the drop or roll it back.\n\t\t */\n\t\tif (entry->dropped)\n\t\t{\n\t\t\tif (entry->dropSubXid == currentSubXid)\n\t\t\t{\n\t\t\t\tif (commit)\n\t\t\t\t{\n\t\t\t\t\t/* elevate drop to the upper subtransaction */\n\t\t\t\t\tentry->dropSubXid = parentSubXid;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* abort the drop */\n\t\t\t\t\tentry->dropped = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Otherwise, commit or discard pending writes.\n\t\t */\n\t\telse\n\t\t{\n\t\t\tSubXidWriteState *stackHead = entry->writeStateStack;\n\t\t\tif (stackHead->subXid == currentSubXid)\n\t\t\t{\n\t\t\t\tif (commit)\n\t\t\t\t{\n\t\t\t\t\tColumnarEndWrite(stackHead->writeState);\n\t\t\t\t}\n\n\t\t\t\tentry->writeStateStack = stackHead->next;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * Called when current subtransaction is committed.\n */\nvoid\nFlushWriteStateForAllRels(SubTransactionId currentSubXid, SubTransactionId parentSubXid)\n{\n\tPopWriteStateForAllRels(currentSubXid, parentSubXid, true);\n}\n\n\n/*\n * Called when current subtransaction is aborted.\n */\nvoid\nDiscardWriteStateForAllRels(SubTransactionId currentSubXid, SubTransactionId parentSubXid)\n{\n\tPopWriteStateForAllRels(currentSubXid, parentSubXid, false);\n}\n\n\n/*\n * Called when the given relfilenode is dropped.\n */\nvoid\nMarkRelfilenumberDropped(RelFileNumber relfilenumber, SubTransactionId currentSubXid)\n{\n\tif (WriteStateMap == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tWriteStateMapEntry *entry = hash_search(WriteStateMap, &relfilenumber, HASH_FIND,\n\t\t\t\t\t\t\t\t\t\t\tNULL);\n\tif (!entry || entry->dropped)\n\t{\n\t\treturn;\n\t}\n\n\tentry->dropped = true;\n\tentry->dropSubXid = currentSubXid;\n}\n\n\n/*\n * Called when the given relfilenode is dropped in non-transactional TRUNCATE.\n */\nvoid\nNonTransactionDropWriteState(RelFileNumber relfilenumber)\n{\n\tif (WriteStateMap)\n\t{\n\t\thash_search(WriteStateMap, &relfilenumber, HASH_REMOVE, false);\n\t}\n}\n\n\n/*\n * Returns true if there are any pending writes in upper transactions.\n */\nbool\nPendingWritesInUpperTransactions(RelFileNumber relfilenumber,\n\t\t\t\t\t\t\t\t SubTransactionId currentSubXid)\n{\n\tif (WriteStateMap == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tWriteStateMapEntry *entry = hash_search(WriteStateMap, &relfilenumber, HASH_FIND,\n\t\t\t\t\t\t\t\t\t\t\tNULL);\n\n\tif (entry && entry->writeStateStack != NULL)\n\t{\n\t\tSubXidWriteState *stackEntry = entry->writeStateStack;\n\n\t\twhile (stackEntry != NULL)\n\t\t{\n\t\t\tif (stackEntry->subXid != currentSubXid &&\n\t\t\t\tContainsPendingWrites(stackEntry->writeState))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tstackEntry = stackEntry->next;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * GetWriteContextForDebug exposes WriteStateContext for debugging\n * purposes.\n */\nextern MemoryContext\nGetWriteContextForDebug(void)\n{\n\treturn WriteStateContext;\n}\n"
  },
  {
    "path": "src/backend/distributed/.gitignore",
    "content": "# ====================\n# = Project-Specific =\n# ====================\n\n# regression test detritus\n/log/\n/regression.diffs\n/regression.out\n/results/\n/tmp_check*\n/build/\n"
  },
  {
    "path": "src/backend/distributed/Makefile",
    "content": "# Makefile for the Citus extension\n\ncitus_subdir = src/backend/distributed\ncitus_top_builddir = ../../..\nsafestringlib_srcdir = $(citus_abs_top_srcdir)/vendor/safestringlib\nsafestringlib_builddir = $(citus_top_builddir)/vendor/safestringlib/build\nsafestringlib_a = $(safestringlib_builddir)/libsafestring_static.a\nsafestringlib_sources = $(wildcard $(safestringlib_srcdir)/safeclib/*)\n\nMODULE_big = citus\nEXTENSION = citus\n\ntemplate_sql_files = $(patsubst $(citus_abs_srcdir)/%,%,$(wildcard $(citus_abs_srcdir)/sql/*.sql))\ntemplate_downgrade_sql_files = $(patsubst $(citus_abs_srcdir)/sql/downgrades/%,%,$(wildcard $(citus_abs_srcdir)/sql/downgrades/*.sql))\ngenerated_sql_files = $(patsubst %,$(citus_abs_srcdir)/build/%,$(template_sql_files))\ngenerated_downgrade_sql_files += $(patsubst %,$(citus_abs_srcdir)/build/sql/%,$(template_downgrade_sql_files))\n# All citus--*.sql files that are used to upgrade between versions\nDATA_built = $(generated_sql_files)\n\n# directories with source files\nSUBDIRS = . commands connection ddl deparser executor metadata operations planner progress relay safeclib shardsplit stats test transaction utils worker clock\n# enterprise modules\nSUBDIRS += replication\n\n# Symlinks are not copied over to the build directory if a separete build\n# directory is used during configure (such as on CI)\nENSURE_SUBDIRS_EXIST := $(shell mkdir -p $(SUBDIRS))\n\n# That patsubst rule searches all directories listed in SUBDIRS for .c\n# files, and adds the corresponding .o files to OBJS\nOBJS += \\\n\t$(patsubst $(citus_abs_srcdir)/%.c,%.o,$(foreach dir,$(SUBDIRS), $(sort $(wildcard $(citus_abs_srcdir)/$(dir)/*.c))))\n\n# be explicit about the default target\n.PHONY: cdc\n\nall: cdc\n\ncdc:\n\t$(MAKE) -C cdc all\n\nNO_PGXS = 1\n\nSHLIB_LINK = $(libpq)\n\ninclude $(citus_top_builddir)/Makefile.global\n\n# make sure citus_version.o is recompiled whenever any change is made to the binary or any\n# other artifact being installed to reflect the correct gitref for every build\nCITUS_VERSION_INVALIDATE := $(filter-out utils/citus_version.o,$(OBJS))\nCITUS_VERSION_INVALIDATE += $(generated_sql_files)\nifneq ($(wildcard $(citus_top_builddir)/.git/.*),)\n        CITUS_VERSION_INVALIDATE += $(citus_top_builddir)/.git/index\nendif\nutils/citus_version.o: $(CITUS_VERSION_INVALIDATE)\n\nSHLIB_LINK += $(filter -lssl -lcrypto -lssleay32 -leay32, $(LIBS))\n\noverride CPPFLAGS += -I$(libpq_srcdir) -I$(safestringlib_srcdir)/include\n\nSQL_DEPDIR=.deps/sql\nSQL_BUILDDIR=build/sql\n\n$(generated_sql_files): $(citus_abs_srcdir)/build/%: %\n\t@mkdir -p $(citus_abs_srcdir)/$(SQL_DEPDIR) $(citus_abs_srcdir)/$(SQL_BUILDDIR)\n\t@# -MF is used to store dependency files(.Po) in another directory for separation\n\t@# -MT is used to change the target of the rule emitted by dependency generation.\n\t@# -P is used to inhibit generation of linemarkers in the output from the preprocessor.\n\t@# -undef is used to not predefine any system-specific or GCC-specific macros.\n\t@# `man cpp` for further information\n\tcd $(citus_abs_srcdir) && cpp -undef -w -P -MMD -MP -MF$(SQL_DEPDIR)/$(*F).Po -MT$@ $< > $@\n\n$(generated_downgrade_sql_files): $(citus_abs_srcdir)/build/sql/%: sql/downgrades/%\n\t@mkdir -p $(citus_abs_srcdir)/$(SQL_DEPDIR) $(citus_abs_srcdir)/$(SQL_BUILDDIR)\n\t@# -MF is used to store dependency files(.Po) in another directory for separation\n\t@# -MT is used to change the target of the rule emitted by dependency generation.\n\t@# -P is used to inhibit generation of linemarkers in the output from the preprocessor.\n\t@# -undef is used to not predefine any system-specific or GCC-specific macros.\n\t@# `man cpp` for further information\n\tcd $(citus_abs_srcdir) && cpp -undef -w -P -MMD -MP -MF$(SQL_DEPDIR)/$(*F).Po -MT$@ $< > $@\n\nSQL_Po_files := $(wildcard $(SQL_DEPDIR)/*.Po)\nifneq (,$(SQL_Po_files))\ninclude $(SQL_Po_files)\nendif\n\n\n.PHONY: clean-full install install-downgrades install-all install-cdc clean-cdc\n\nclean: clean-cdc\n\nclean-cdc:\n\t$(MAKE) -C cdc clean\n\ncleanup-before-install:\n\trm -f $(DESTDIR)$(datadir)/$(datamoduledir)/citus.control\n\trm -f $(DESTDIR)$(datadir)/$(datamoduledir)/citus--*\n\ninstall: cleanup-before-install install-cdc\n\ninstall-cdc:\n\t$(MAKE) -C cdc install\n\n# install and install-downgrades should be run sequentially\ninstall-all: install\n\t$(MAKE) install-downgrades\n\ninstall-downgrades: $(generated_downgrade_sql_files)\n\t$(INSTALL_DATA) $(generated_downgrade_sql_files) '$(DESTDIR)$(datadir)/$(datamoduledir)/'\n\nclean-full:\n\t$(MAKE) clean\n\t$(MAKE) -C cdc clean-full\n\trm -rf $(safestringlib_builddir)\n"
  },
  {
    "path": "src/backend/distributed/README.md",
    "content": " <!--- we use Github's href convention in Table of Content --->\n# Citus Technical Documentation\n\nThe purpose of this document is to provide comprehensive technical documentation for Citus, in particular the distributed database implementation.\n\n# Table of Contents\n- [Citus Concepts](#citus-concepts)\n  - [Principles](#principles)\n- [Use of hooks](#use-of-hooks)\n- [Query planner](#query-planner)\n  - [High-level design/flow:](#high-level-designflow)\n  - [Distributed Query Planning with Examples in Citus (as of Citus 12.1)](#distributed-query-planning-with-examples-in-citus-as-of-citus-121)\n  - [Logical Planner & Optimizer](#logical-planner--optimizer)\n  - [Combine query planner](#combine-query-planner)\n  - [Restriction Equivalence](#restriction-equivalence)\n  - [Recurring Tuples](#recurring-tuples)\n- [Executor](#executor)\n  - [Custom scan](#custom-scan)\n  - [Function evaluation](#function-evaluation)\n  - [Prepared statements](#prepared-statements)\n  - [Adaptive executor](#adaptive-executor)\n  - [Local execution](#local-execution)\n  - [Subplans](#subplans)\n  - [Re-partitioning](#re-partitioning)\n  - [COPY .. FROM command](#copy--from-command)\n  - [COPY .. TO command](#copy--to-command)\n  - [INSERT..SELECT](#insertselect)\n  - [Merge command](#merge-command)\n- [DDL](#ddl)\n  - [Object & dependency propagation](#object--dependency-propagation)\n  - [Foreign keys](#foreign-keys)\n  - [DROP Table](#drop-table)\n- [Connection management](#connection-management)\n  - [Connection management](#connection-management-1)\n  - [Placement connection tracking](#placement-connection-tracking)\n  - [citus.max_cached_connections_per_worker](#citusmax_cached_connections_per_worker)\n  - [citus.max_shared_pool_size](#citusmax_shared_pool_size)\n- [Transactions (2PC)](#transactions-2pc)\n  - [Single-node transactions](#single-node-transactions)\n  - [Multi-node transactions](#multi-node-transactions)\n  - [No distributed snapshot isolation](#no-distributed-snapshot-isolation)\n  - [Distributed Deadlocks](#distributed-deadlocks)\n- [Locking](#locking)\n  - [Lock Levels](#lock-levels)\n  - [Lock Monitoring](#lock-monitoring)\n  - [Lock Types](#lock-types)\n- [Rebalancing](#rebalancing)\n  - [Rebalancing algorithm](#rebalancing-algorithm)\n  - [Shard moves](#shard-moves)\n  - [Shard splits](#shard-splits)\n  - [Background task runner](#background-task-runner)\n  - [Resource cleanup](#resource-cleanup)\n- [Logical decoding / CDC](#logical-decoding--cdc)\n  - [CDC ordering](#cdc-ordering)\n- [Global PID](#global-pid)\n- [Function call delegation](#function-call-delegation)\n- [Query from any node](#query-from-any-node)\n  - [Why didn’t we have dedicated Query Nodes and Data Nodes?](#why-didnt-we-have-dedicated-query-nodes-and-data-nodes)\n  - [Shard visibility](#shard-visibility)\n- [Statistic tracking](#statistic-tracking)\n  - [Citus stat counters](#citus-stat-counters)\n\n# Citus Concepts\n\n**Citus table**: A table managed by Citus through PostgreSQL hooks. Whenever the table is involved in a command, the command is handled by Citus.\n\n**Shell table**: The “original” (inaccessible) PostgreSQL table which has been converted to a Citus table. Every node has shell tables and the corresponding Citus metadata.\n\n**Metadata**: The Citus catalog tables, shell tables, and dependent objects that are replicated to all nodes to enable distributed queries from those nodes.\n\n**Metadata syncing**: The process of replicating metadata from the coordinator to other nodes, which happens when adding a node or any change to the metadata.\n\nThere are several types of Citus tables:\n\n**Distributed tables** are created using `SELECT create_distributed_table('table_name', 'distribution_column')`. They have a distribution column and for each row the value in the distribution column determines to which shard the row is assigned. There are 3 different partitioning schemes for distributed tables, though only the first is supported:\n\n- Hash-distributed tables have a range of hash values in shardminvalue/shardmaxvalue in pg_dist_shard\n- Range-distributed tables (deprecated) have shards with a distinct range of values in pg_dist_shard\n- Append-distributed tables (deprecated) have shards with a range of values in pg_dist_shard, though the ranges can overlap.\n\nHash-distributed tables can be **co-located** with each other, such that the shards with the same hash value range are always on the same node. From the planner point-of-view, range-distributed tables can also be colocated, but shards are not managed by Citus.\n\nShards can be replicated (deprecated), in which case they have multiple shard placements. Writes to the placements are performed using 2PC and use aggressive locking to avoid diverging.\n\n**Reference tables** are created using SELECT create_reference_table(..). They have a single shard, which is replicated to all nodes. It is possible to have a node without reference table replicas, but in that case the reference tables are replicated before the next rebalance. Reference tables are always co-located with each other and have a common co-location ID.\n\nWrites to a reference table are performed using 2PC and use aggressive locking to avoid diverging.\n\n**Single shard tables** are a special type of distributed table without a distribution column and with a single shard. When using schema-based sharding, tables created in a distributed schema automatically become single shard tables. Single shard tables can be co-located with each other, but not replicated. Single shard tables can be explicitly created using `SELECT create_distributed_table('table_name', NULL);`, though are meant to be auto-generated by schema-based sharding.\n\n**Citus local tables**: A single shard table that can only be placed on the coordinator and are primarily used as a drop-in replacement for regular PostgreSQL tables when creating foreign keys to/from reference tables. All Citus local tables are implicitly co-located with each other, but do not have a co-location ID. Citus local tables can be explicitly created using `SELECT citus_add_local_table_to_metadata('table_name');`, though are meant to be auto-generated by foreign keys.\n\nAll Citus table types have the notion of a “shard”, though in many cases there is only a single shard.\n\n**Shard**: The table that contains the actual data in a Citus table. Shards reside in the same schema as the Citus table and are named after the Citus table with the shard ID as a suffix. Shards are hidden from catalog queries unless you SET citus.override_table_visibility TO off. Hash-distributed tables have multiple shards, each of which has distinct shardminvalue/shardmaxvalue.\n\n**Colocation group**: A set of distributed tables that have the same co-location ID, such that their shards are always co-located.\n\n**Shard group**: A set of shards with the same shardminvalue/shardmaxvalue that are part of the same co-location group. Citus guarantees that shards from the same shard group are always placed on the same node group.\n\n**Shard placement**: The assignment of a shard to a node group. There can be multiple placements of the same shard if the table is replicated (e.g. reference tables).\n\n**Shard group placement**: The assignment of a shard to a node group must be the same for all shards in a shard group, since those are always co-located. We’ll refer to the group of placements of a shard group as a shard group placement.\n\n**Node**: A single PostgreSQL/Citus server listed in pg_dist_node and added via SELECT citus_add_node(..).\n\n**Node group**: Each primary node can have 0 or more physical replicas in read replica clusters. Together the nodes form a node group identified by the groupid in pg_dist_node. Per convention, the coordinator(s) have group ID 0. Each node can know its own node groupid by reading it from pg_dist_local_group.\n\n**Coordinator**: The node with groupid 0, which can perform reads, writes, and administrative operations such as adding a node, rebalancing, and schema changes.\n\n**Worker nodes**: Nodes with groupid > 0, which can perform reads and writes, but not administrative operations.\n\n**Cluster**: The combination of worker nodes and coordinator is a cluster. When the cluster has a read replica, the nodes in the read replica are listed in pg_dist_node with a different nodecluster value, and the servers have a corresponding citus.cluster_name in their postgresql.conf. That way, nodes know which other nodes in pg_dist_node belong to their cluster, and they will ignore others.\n\n**Read replica cluster**: In a read replica cluster, every node is a physical replica of a node in a primary Citus cluster. The read replica has a distinct citus.cluster_name value and the nodes in the read replica cluster should be added to pg_dist_node on the primary coordinator with the corresponding cluster name.\n\n**Client connections**: Connections made by the client to any of the nodes in the cluster. Each client connection is backed by a postgres process/backend, which we sometimes refer to as a client session.\n\n**Internal connections**: Connections to other nodes made by a command running on a client session. Each internal connection is backed by a process, which we sometimes refer to as an internal session. In the code, you can use IsCitusInternalBackend()\n\n**Maintenance daemon**: A background worker that is started in each database that has the Citus extension. It performs distributed deadlock detection, 2PC recovery, synchronizing node metadata after citus_update_node, resource cleanup, and other tasks.\n\nIn the query planner, we use the following terminology:\n\n**Distributed query**: A query sent by the client that involves a Citus table and is therefore handled by the distributed query planner.\n\n**Shard query**: An internal query on shards (at most 1 shard group).\n\n**Intermediate result**: A temporary file that contains the result of a subquery or CTE, created as a result of a broadcast or repartition operation. Intermediate results are automatically cleaned up on transaction end or restart.\n\n## Principles\n\nUse cases:\n\n- Multi-tenant apps are the primary use case for Citus, which we can scale through distributing and co-locating by tenant ID, or through schema-based sharding. Citus is reasonably complete for this use case, but there are still SQL and operational improvements that can be made.\n- Real-time analytics is another popular use case due the combination of parallel distributed queries with indexes & in-database materialization (ETL). Improvement areas include automated time partitioning, better columnar storage (perf and update/delete), and incremental materialized views.\n- Citus works well for CRUD use cases, but would be far easier to use if we introduced a load balancer, DDL from any node (no explicit coordinator), and by better use of connection pooling for better performance (e.g. outbound pgbouncers).\n- Marketplace use cases could work well if we made it easier to distribute tables twice by different dimensions or made it easier to keep paired tables in sync.\n\nSchema management:\n\n - Our goal is for all DDL commands on Citus tables to work transparently, and for global DDL commands (e.g. CREATE TYPE) to be propagated to all nodes. Not all DDL is implemented yet and may either error or not propagate.\n - Since we cannot define custom DDL commands for sharding operations, we use functions that are called from a SELECT query.\n\nQuery layer:\n\n- No incompatibilities with PostgreSQL – any query on a Citus table is supported on an equivalent PostgreSQL table.\n- We optimize for maximum pushdown (& performance) over complete compatibility, but our long-term goal is for all queries to be supported in all cases.\n- For single-shard queries, it is useful to avoid detailed query analysis through the fast path  planner (simple, single table) and router planner (co-located joins) layers. However, multi-shard queries can go through disparate code paths that were added out of expediency and should eventually be unified.\n\nTransactional semantics:\n\n- Transactions scoped to a single node follow the same semantics as PostgreSQL.\n- Transactions across nodes are atomic, durable, and consistent, but do not have full snapshot isolation: A multi-shard query may see a concurrently committing transaction as committed on one node, but not yet committed on another node.\n- Read-your-writes consistency should be preserved.\n- Monotonic read consistency should be preserved for tables without replication, may not always be the case for replicated/reference tables.\n\nReplication model:\n\n- High availability is achieved through hot standby nodes managed by a control plane or PostgreSQL HA solution like Patroni or pg_auto_failover.\n- Read replicas are Citus clusters in which each node is a physical replica of a node in another Citus cluster.\n- Hot standby nodes are, at the time of writing, not in the metadata. Instead, the hostname/IP is replaced or rerouted at failover time.\n- The deprecated “statement based” replication is (as of Citus 11.0+) only useful for providing read scalability, not for HA as all modifications are done via 2PC. Reference tables do use statement-based replication.\n\n# Use of hooks\n\nA PostgreSQL extension consists of two parts: a set of SQL objects (e.g. metadata tables, functions, types) and a shared library that is loaded into PostgreSQL and can alter the behavior of PostgreSQL by setting certain hooks. You can find a high-level description of these concepts in [this talk](https://learn.microsoft.com/en-us/events/azure-cosmos-db-liftoff/foundations-of-distributed-postgresql-with-citus).\n\nCitus uses the following hooks:\n\n**User-defined functions (UDFs)** are callable from SQL queries as part of a transaction, but have an implementation in C, and are primarily used to manipulate the Citus metadata and implement remote procedure calls between servers.\n\n**Planner and executor hooks** are global function pointers that allow an extension to provide an alternative query plan and execution method. After PostgreSQL parses a query, Citus checks if the query involves a Citus table. If so, Citus generates a plan tree that contains a CustomScan operator, which encapsulates distributed query plan.\n\n**CustomScan** is an operator in a PostgreSQL query plan that returns tuples via custom function pointers. The Citus CustomScan calls the distributed query executor, which sends queries to other servers and collects the results before returning them to the PostgreSQL executor.\n\n**Transaction callbacks** are called at critical points in the lifecycle of a transaction (e.g. pre-commit, post-commit, abort). Citus uses these to implement distributed transactions.\n\n**Utility hook** is called after parsing any command that does not go through the regular query planner. Citus uses this hook primarily to apply DDL and COPY commands that affect Citus tables.\n\n**Background workers** run user-supplied code in separate processes. Citus uses this API to run a maintenance daemon. This daemon performs distributed deadlock detection, 2PC prepared transaction recovery, and cleanup.\n\nThrough these hooks, Citus can intercept any interaction between the client and the PostgreSQL engine that involves Citus tables. Citus can then replace or augment PostgreSQL's behavior.\n\n# Query planner\n\nCitus has a layered planner architecture that accommodates different workloads. There are several useful presentations/papers that are relevant to Citus’ distributed planner, below we try to categorize them:\n\n## High-level design/flow:\n\n- Distributing Queries the Citus Way: Marco’s PG Con presentation provides a good introduction: https://postgresconf.org/system/events/document/000/000/233/Distributing_Queries_the_Citus_Way.pdf\n\n- Another useful content on this topic is the Planner README.md: https://github.com/citusdata/citus/blob/main/src/backend/distributed/planner/README.md\n\n- Onder’s talk at CitusCon: https://www.youtube.com/watch?v=raw3Pwv0jb8\n- Citus paper: https://dl.acm.org/doi/pdf/10.1145/3448016.3457551\n\n- Logical planner design - 1: https://speakerdeck.com/marcocitus/scaling-out-postgre-sql\n\n- Logical Planner design - 2: https://www.youtube.com/watch?v=xJghcPs0ibQ\n\n- Logical Planner based on the paper: Correctness of query execution strategies in distributed databases: https://dl.acm.org/doi/pdf/10.1145/319996.320009\n\n##  Distributed Query Planning with Examples in Citus (as of Citus 12.1)\n\nThis part of the documentation aims to provide a comprehensive understanding of how Citus handles distributed query planning with examples. We will use a set of realistic tables to demonstrate various queries. Through these examples, we hope to offer a step-by-step guide on how Citus chooses to plan queries.\n\nCitus hooks into the PostgreSQL planner using the top-level planner_hook function pointer, which sees the query tree after parsing and analysis. If the query tree contains a Citus table, we go through several planner stages: fast path planner, router planner, recursive planning, logical planner & optimizer. Each stage can handle more complex queries than the previous, but also comes with more overhead. That way, we can handle a mixture of high throughput transactional workloads (without adding significant planning overhead), as well as more complex analytical queries (with more sophisticated distributed query execution). For specific types of queries (e.g. insert..select), we have separate planner code paths.\n\nFor a more comprehensive high-level overview of the planner, go to https://postgresconf.org/system/events/document/000/000/233/Distributing_Queries_the_Citus_Way.pdf\n\n### Table definitions used in this section\n\n```sql\n-- Distributed Table: Users Table\nCREATE TABLE users_table (\n    user_id bigserial PRIMARY KEY,\n    username VARCHAR(50) NOT NULL,\n    email VARCHAR(50),\n    date_of_birth DATE,\n    country_code VARCHAR(3)\n);\nSELECT create_distributed_table('users_table', 'user_id');\n\n-- Distributed Table: Orders Table\nCREATE TABLE orders_table (\n    order_id bigserial,\n    user_id BIGINT REFERENCES users_table(user_id),\n    product_id BIGINT,\n    order_date TIMESTAMPTZ,\n    status VARCHAR(20)\n);\nSELECT create_distributed_table('orders_table', 'user_id');\n\n\n-- Distributed Table: Products Table\nCREATE TABLE products_table (\n    product_id bigserial PRIMARY KEY,\n    product_name VARCHAR(100),\n    category_id INT,\n    price NUMERIC(10, 2)\n);\nSELECT create_distributed_table('products_table', 'product_id');\n\n-- Reference Table: Country Codes\nCREATE TABLE country_codes (\n    country_code VARCHAR(3) PRIMARY KEY,\n    country_name VARCHAR(50)\n);\nSELECT create_reference_table('country_codes');\n\n-- Reference Table: Order Status\nCREATE TABLE order_status (\n    status VARCHAR(20) PRIMARY KEY,\n    description TEXT\n);\nSELECT create_reference_table('order_status');\n\n-- Reference Table: Product Categories\nCREATE TABLE product_categories (\n    category_id INT PRIMARY KEY,\n    category_name VARCHAR(50)\n);\nSELECT create_reference_table('product_categories');\n```\n\n## Fast Path Router Planner\n\nThe Fast Path Router Planner is specialized in optimizing queries that are both simple in structure and certain to touch a single shard. Importantly, it targets queries on a single shard distributed, citus local or reference tables. This does not mean the planner is restricted to trivial queries; it can handle complex SQL constructs like `GROUP BY`, `HAVING`, `DISTINCT`, etc., as long as these operate on a single table and involve an equality condition on the distribution key (`distribution_key = X`). The main SQL limitation for fast path distributed query planning is the subquery/CTE support. Those are left to the next planner: Router planner.\n\nThe aim of this planner is to avoid relying on PostgreSQL's standard_planner() for planning, which performs unnecessary computations like cost estimation, irrelevant for distributed planning. Skipping the standard_planner has significant performance gains for OLTP workloads. By focusing on \"shard-reachable\" queries, the Fast Path Router Planner is able to bypass the need for more computationally expensive planning processes, thereby accelerating query execution.\n\n### Main C Functions Involved:\n\n- `FastPathPlanner()`: The primary function for creating the fast-path query plan.\n- `FastPathRouterQuery()`: Validates if a query is eligible for fast-path routing by checking its structure and the WHERE clause.\n\nWith  set client_min_messages to debug4; you should see the following in the DEBUG messages: \"DEBUG:  Distributed planning for a fast-path router query\"\n\n```sql\n-- Fetches the count of users born in the same year, but only\n-- for a single country, with a filter on the distribution column\n-- Normally we have a single user with id = 15 because it's a PRIMARY KEY\n-- this is just to demonstrate that fast-path can handle complex queries\n-- with EXTRACT(), COUNT(), GROUP BY, HAVING, etc.\nSELECT EXTRACT(YEAR FROM date_of_birth) as birth_year, COUNT(*)\nFROM users_table\nWHERE country_code = 'USA' AND user_id = 15\nGROUP BY birth_year\nHAVING COUNT(*) > 10;\n```\n\n```sql\n-- all INSERT commands are by definition fast path\n-- router queries in the sense that they do not\n-- need any information from Postgres' standard_planner()\nINSERT INTO orders_table (user_id, product_id, order_date, status)\nVALUES (42, 555, now(), 'NEW');\n```\n\n```sql\n-- UPDATE/DELETEs can also be qualified as fast path router\n-- queries\nUPDATE products_table SET price = price * 1.1 WHERE product_id = 555;\n```\n\nFast path queries have another important characteristic named \"deferredPruning.\"\n\nFor regular queries, Citus does the shard pruning during the planning phase, meaning that the shards that the query touches are calculated during the planning phase. However, in an ideal world, the shard pruning should happen during the execution and, for a certain class of queries, we support that. In the code, that is denoted by \"Job->deferredPruning\" field.\n\nGiven that fast path queries are performance critical, they can be planned with prepared statements. When this is done, \"Job->deferredPruning\" becomes \"true\". And, the meaning of that is Citus can support PREPARED statements as expected. The first 6 executions of the plan do distributed planning, the rest is cached similar to Postgres' plan caching, and the shard pruning is done during the execution phase. And, if you attach a debugger, you'd see that on the first 6 executions, the debugger will stop at distributed_planner() function, but on the rest, it will not. The shard pruning for the cached command will happen in CitusBeginScan() function.\n\nTo see that in action, checkout the DEBUG messages:\n\n```sql\nset client_min_messages to debug4;\nPREPARE p1 (bigint) AS SELECT * FROM users_table WHERE user_id = $1;\n\n-- 1st execute\nexecute p1(1);\nDEBUG:  Deferred pruning for a fast-path router query\nDEBUG:  Creating router plan\n....\n(0 rows)\n\n-- 2nd execute\nexecute p1(1);\nDEBUG:  Deferred pruning for a fast-path router query\nDEBUG:  Creating router plan\n....\n(0 rows)\n...\nexecute p1(1);\nexecute p1(1);\nexecute p1(1);\n...\n\n-- 6th execute\nexecute p1(1);\nDEBUG:  Deferred pruning for a fast-path router query\nDEBUG:  Creating router plan\n....\n(0 rows)\n\n-- now, on the 7th execute, you would **NOT** see the fast-path\n-- planning anymore, because the query comes from Postgres'\n-- query cache\nexecute p1(1);\nDEBUG:  constraint value: '1'::bigint\nDEBUG:  shard count after pruning for users_table: 1\nDEBUG:  opening 1 new connections to localhost:9702\nDEBUG:  established connection to localhost:9702 for session 8 in 9499 microseconds\nDEBUG:  task execution (0) for placement (46) on anchor shard (102049) finished in 1281 microseconds on worker node localhost:9702\nDEBUG:  Total number of commands sent over the session 8: 1 to node localhost:9702\n(0 rows)\n```\n\n### Delaying the Fast Path Plan\n\nAs of Citus 13.2, if it can be determined at plan-time that a fast path query is against a local shard then a shortcut can be taken so that deparse and parse/plan of the shard query is avoided. Citus must be in MX mode and the shard must be local to the Citus node processing the query. If so, the OID of the distributed table is replaced by the OID of the shard in the parse tree. The parse tree is then given to the Postgres planner which returns a plan that is stored in the distributed plan's task. That plan can be repeatedly used by the local executor (described in the next section), avoiding the need to deparse and plan the shard query on each execution.\n\nWe call this delayed fast path planning because if a query is eligible for fast path planning then `FastPathPlanner()` is delayed if the following properties hold:\n- The query is a SELECT or UPDATE on a distributed table (schema or column sharded) or Citus managed local table\n- The query has no volatile functions\n\nIf so, then `FastPathRouterQuery()` sets a flag indicating that making the fast path plan should be delayed until after the worker job has been created. At that point the router planner uses `CheckAndBuildDelayedFastPathPlan()` to see if the task's shard placement is local (and not a dummy placement) and the metadata of the shard table and distributed table are consistent (no DDL in progress on the distributed table). If so the parse tree with OID of the distributed table replaced by the OID of the shard table is fed to `standard_planner()` and the resultant plan is saved in the task. Otherwise, if the worker job has been marked for deferred pruning or the shard is not local or the shard is local but it's not safe to swap OIDs, then `CheckAndBuildDelayedFastPathPlan()` calls `FastPathPlanner()` to ensure a complete plan context. Reference tables are not currently supported, but this may be relaxed for SELECT statements in the future. Delayed fast path planning can be disabled by turning off `citus.enable_local_fast_path_query_optimization` (it is on by default).\n\n## Router Planner in Citus\n\n### Overview\n\nThe Router Planner plays a key role in Citus' query optimization landscape. While sharing some common traits with the Fast Path Router Planner, it offers unique capabilities as well. Router (and fast path router) planners are the bedrock for the multi-tenant use cases.\n\n#### Similarities with Fast Path Router Planner\n\n- **Single Node Routing**: Both planners send queries to a single node. Unlike the Fast Path Planner, the Router Planner can work with multiple colocated tables, provided they have filters on their distribution columns.\n\n- **Query Routing Mechanics**: Router Planner takes the query, verifies if it can be routed, and if so, it replaces original table names with their corresponding shard names, directing the query to the appropriate nodes.\n\n#### Differences\n\n- **Subqueries and CTEs**: The Router Planner can manage subqueries and Common Table Expressions (CTEs), routing the entire query to a single node as long as all involved tables have filters on their distribution columns.\n\n- **Standard Planner Reliance**: Router Planner relies on PostgreSQL's `standard_planner()` to learn the necessary filter restrictions on the tables.\n\n#### Main C Functions Involved\n\n- `PlanRouterQuery()`: Responsible for creating the router plan.\n- `TargetShardIntervalsForRestrictInfo()`: Retrieves the shard intervals based on restrictions provided by PostgreSQL's `standard_planner()`.\n\n### Example Router Planner Queries\n\n```sql\n-- Fetch user data and their respective orders for a given user_id\nSELECT u.username, o.order_id\nFROM users_table u, orders_table o\nWHERE u.user_id = o.user_id AND u.user_id = 42;\n\n-- With Subqueries:\n-- Fetch the username and their total order amount\n-- for a specific user\nSELECT u.username,\n       (SELECT COUNT(*) FROM orders_table o\n        WHERE o.user_id = 42 AND\n        o.user_id = u.user_id)\nFROM users_table u\nWHERE u.user_id = 42;\n\n-- Router planner works with CTEs (and UPDATE/DELETE Query):\n-- Update the status of the most recent order for a specific user\nWITH RecentOrder AS (\n  SELECT MAX(order_id) as last_order_id\n  FROM orders_table\n  WHERE user_id = 42\n)\nUPDATE orders_table\nSET status = 'COMPLETED'\nFROM RecentOrder\nWHERE orders_table.user_id = 42 AND\n\t\torders_table.order_id = RecentOrder.last_order_id;\n```\n\n\n## Query Pushdown Planning in Citus\n\n### Overview\n\nWhile Router and Fast-Path Router Planners are proficient at dealing with single-shard commands—making them ideal for multi-tenant and OLTP applications—Citus also excels in analytical use-cases. In these scenarios, a single query is broken down into multiple parallel sub-queries, which are run on various shards across multiple machines, thereby speeding up query execution times significantly.\n\n#### What is Query Pushdown Planning?\n\nQuery Pushdown Planning is an extension of the Router Planning paradigm. Unlike the latter, which deals with single-shard, single-node queries, Query Pushdown can route a query to multiple shards across multiple nodes. Instead of verifying that all tables have the same filters, as in Router Planning, Query Pushdown ascertains that all tables are joined on their distribution keys.\n\n#### Core Functions\n\nThe core C function responsible for this check is `RestrictionEquivalenceForPartitionKeys()`, which ensures that tables in the query are joined based on their distribution keys. Initially intended for subqueries, Query Pushdown has been extended to include other cases as well. The decision to utilize Query Pushdown is determined by the `ShouldUseSubqueryPushDown()` function.\n\n#### Understanding Query Pushdown\n\nUnderstanding Query Pushdown Planning and how it extends the simpler Router Planning can help you fully utilize Citus for your analytical workloads.\n\n#### Key Characteristics of Query Pushdown\n\n- **High Parallelism**: The query is broken down into multiple sub-queries, leading to parallel execution on multiple shards and nodes.\n- **Worker Subquery**: You will typically notice the alias `worker_subquery` in the SQL queries sent to the shards, indicating a pushdown operation.\n\n### Examples of query pushdown\n\n#### Basic Example\n\n```sql\n-- Count of distinct product_ids where user_ids from two different tables match\nSELECT count(DISTINCT product_id)\nFROM (\n  SELECT DISTINCT user_id as distinct_user_id\n  FROM users_table\n) foo, orders_table\nWHERE orders_table.user_id = distinct_user_id;\n```\n\n#### Subquery in Target List\n\n```sql\n-- retrieves the most recent order date for each user\nSELECT   (SELECT MAX(order_date) FROM orders_table o WHERE o.user_id = u.user_id) FROM users_table u;\n```\n\n#### Subquery in WHERE Clause\n\n```sql\n-- Number of distinct users who have placed an order\nSELECT COUNT(DISTINCT u.user_id)\nFROM users_table u\nWHERE u.user_id IN (\n  SELECT o.user_id\n  FROM orders_table o\n);\n```\n\n#### More Examples\n\n```sql\n-- Count of distinct products per user, with maximum order date from orders\n--  as a subquery in the target list\n SELECT\n  (SELECT MAX(o.order_date) FROM orders_table o WHERE o.user_id = u.user_id),\n  COUNT(DISTINCT o.product_id)\nFROM orders_table o, users_table u\nWHERE o.user_id = u.user_id\nGROUP BY u.user_id;\n```\n\n#### UPDATE and DELETE with Query Pushdown\n\n```sql\n-- Update status in orders_table for users whose email ends with '@example.com'\nUPDATE orders_table o\nSET status = 'DISCOUNTED'\nFROM users_table u\nWHERE o.user_id = u.user_id AND u.email LIKE '%@example.com';\n```\n\n```sql\n-- Delete orders for users who were born before '2000-01-01'\nDELETE FROM orders_table o\nUSING users_table u\nWHERE o.user_id = u.user_id AND u.date_of_birth < '2000-01-01';\n```\n\n\n## Recursive Planning\n\nCentral to understanding Citus' approach to distributed query planning are two closely interrelated concepts: \"Query Pushdown Planning\" and \"Recursive Planning.\" These dual strategies lay the foundation for Citus' capacity to manage complex query structures across multiple shards and nodes effectively.\n\nWhile Query Pushdown Planning optimizes queries by breaking them into smaller components that can run in parallel across multiple shards, Recursive Planning takes a more nuanced approach. It works its way through the query tree from the deepest level upwards, scrutinizing each subquery to determine its suitability for pushdown.\n\nThe essence of recursive planning lies in treating each recursively planned query in isolation. This means correlated subqueries can't take advantage of recursive planning. However, (sub)queries on local tables can be done via recursive planning.\n\nThis process is primarily executed in the `RecursivelyPlanSubqueryWalker()` C function. In this function, the engine goes to the innermost subquery and assesses whether it can safely be pushed down as a stand-alone query. If it can, the query engine simply moves on. However, if the subquery isn't suitable for pushdown, Citus generates a separate \"sub-plan\" for that subquery, substituting it with a `read_intermediate_result()` function call. These sub-plans are later executed as independent queries, a task overseen by the `ExecuteSubPlans()` function.\n\nThe engine continues this way, moving upward through each level of subqueries, evaluating and, if needed, creating sub-plans until it reaches the top-level query.\n\n### Intermediate Results as Reference Tables\n\nOne of the key aspects of Recursive Planning is the use of \"intermediate results.\" These are essentially the outcomes of subqueries that have been recursively planned and executed on worker nodes. Once these intermediate results are obtained, they are treated much like reference tables in the subsequent stages of query planning and execution. The key advantage here is that, like reference tables, these intermediate results can be joined with distributed tables on any column, not just the distribution key.\n\n### Full SQL Coverage via Recursive Planning\n\nThe practice of recursively creating sub-plans and generating intermediate results offers a workaround for achieving full SQL coverage in Citus. If each subquery in a complex SQL query can be replaced with an intermediate result, then the entire query essentially becomes a query on a reference table. This feature is a crucial aspect for many users who require comprehensive SQL support in their distributed systems.\n\n### Trade-offs of using recursive planning\n\nWhile Recursive Planning brings a lot to the table, it's not without its drawbacks. First, the method inherently adds more network round-trips, as each recursively planned query is executed separately, and its results are pushed back to all worker nodes. Secondly, when functions like `read_intermediate_results` are used to fetch data from these intermediate results, it can confound the Postgres planner, particularly in the context of complex joins. As a result, query estimations may be inaccurate, leading to suboptimal execution plans.\n\nUnderstanding these facets of Recursive Planning can provide you with a comprehensive view of how Citus approaches distributed query planning, allowing you to better optimize your database operations.\n\nThis may seem complex at first glance, but it's a bit like a step-by-step puzzle-solving process that the Citus query engine performs to optimize your database queries effectively. To help clarify these intricate mechanics, we'll present a series of examples.\n\n#### Recursive Plan Example 1:\n\nIn the simplest example, we'll have a single subquery which is NOT pushdown-safe due to LIMIT 1, hence creating a subplan\n\n```sql\nSET client_min_messages TO DEBUG1;\nSELECT count(*) FROM (SELECT * FROM users_table LIMIT 1) as foo;\nSET\nTime: 0.765 ms\nDEBUG:  push down of limit count: 1\nDEBUG:  generating subplan 7_1 for subquery SELECT user_id, username, email, date_of_birth, country_code FROM public.users_table LIMIT 1\nDEBUG:  Plan 7 query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.user_id, intermediate_result.username, intermediate_result.email, intermediate_result.date_of_birth, intermediate_result.country_code FROM read_intermediate_result('7_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id bigint, username character varying(50), email character varying(50), date_of_birth date, country_code character varying(3))) foo\n```\n\n#### Recursive Plan Example 2:\n\nNow, we have multiple subqueries in the same level which are NOT pushdown-safe due to LIMIT 1 and GROUP BY non distribution keys, hence creating a subplan\n\n```sql\nSELECT count(*) FROM\n\t(SELECT * FROM users_table LIMIT 1) as foo,\n\t(SELECT count(*) FROM users_table GROUP BY country_code) as bar;\nDEBUG:  push down of limit count: 1\nDEBUG:  generating subplan 9_1 for subquery SELECT user_id, username, email, date_of_birth, country_code FROM public.users_table LIMIT 1\nDEBUG:  generating subplan 9_2 for subquery SELECT count(*) AS count FROM public.users_table GROUP BY country_code\nDEBUG:  Plan 9 query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.user_id, intermediate_result.username, intermediate_result.email, intermediate_result.date_of_birth, intermediate_result.country_code FROM read_intermediate_result('9_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id bigint, username character varying(50), email character varying(50), date_of_birth date, country_code character varying(3))) foo, (SELECT intermediate_result.count FROM read_intermediate_result('9_2'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) bar\n```\n\n#### Recursive Plan Example 3:\n\nWe have a subquery foo that is NOT safe-to-pushdown but once that subquery is replaced with an intermediate result, the rest of the query becomes safe-to-pushdown\n\n```sql\nSELECT count(*) FROM\n  (SELECT 1 FROM (SELECT user_id FROM users_table LIMIT 1) as foo,\n                           (SELECT * FROM orders_table) as o1,\n                           (SELECT * FROM users_table) as u2\n                           WHERE\n                                  foo.user_id = o1.user_id AND\n                                  o1.user_id = u2.user_id) as top_level_subquery;\nDEBUG:  push down of limit count: 1\nDEBUG:  generating subplan 1_1 for subquery SELECT user_id FROM public.users_table LIMIT 1\nDEBUG:  Plan 1 query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT 1 AS \"?column?\" FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('1_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id bigint)) foo, (SELECT orders_table.order_id, orders_table.user_id, orders_table.product_id, orders_table.order_date, orders_table.status FROM public.orders_table) o1, (SELECT users_table.user_id, users_table.username, users_table.email, users_table.date_of_birth, users_table.country_code FROM public.users_table) u2 WHERE ((foo.user_id OPERATOR(pg_catalog.=) o1.user_id) AND (o1.user_id OPERATOR(pg_catalog.=) u2.user_id))) top_level_subquery\n```\n\n\n### More advanced recursive planning constructs\nIn the previous parts of the recursive planning examples, we only dealt with a subquery at a time. However, recursive planning is capable of considering multiple subqueries in the same query level or converting tables to subqueries in the same level. In this part of the document, let's discuss these advanced query planning capabilities.\n\n\n### Set operations via recursive planning (and query pushdown)\n\nSet operations like UNION, UNION ALL, and EXCEPT are essentially two subqueries in the same query level.\n\n> **Note:** The rules for set operation planning on Citus can be confusing and should be taken carefully.\n\nCitus is capable of \"pushing down\" certain set operations: UNION and UNION ALL. To allow this, two rules must be met, which are defined in the `SafeToPushdownUnionSubquery()` C code.\n\n1. The set operation cannot be on the top level; it should be wrapped into a subquery. This is purely an implementation limitation that can and should be eased.\n2. For all subqueries, each leaf query should have a \"distribution key\" on the target list, and the ordinal positions of these \"distribution keys\" should match across all set operations. This second limitation is required to preserve correctness.\n\n#### Set operation query examples:\n\n```sql\n-- safe to pushdown\nSELECT * FROM (SELECT * FROM users_table UNION SELECT * FROM users_table) as foo;\n```\n\n```sql\n-- not safe to pushdown because the set operation is NOT wrapped into a subquery.\n-- Each leaf query is recursively planned.\nSELECT * FROM users_table UNION SELECT * FROM users_table;\n```\n\n```sql\n-- not safe to pushdown because the distribution columns do NOT match (e.g., not existing)\nSELECT * FROM (SELECT username FROM users_table UNION SELECT username FROM users_table) as foo;\n```\n\n```sql\n-- not safe to pushdown because the distribution columns do NOT match.\nSELECT * FROM (SELECT user_id + 1 FROM users_table UNION SELECT user_id - 1 FROM users_table) as foo;\n```\n\n```sql\n-- EXCEPT is never safe to pushdown\nSELECT * FROM (SELECT * FROM users_table EXCEPT SELECT * FROM users_table) as foo;\n```\n\n\n\n### Set operations and joins\n\n\nAlthough not very common, some users might have joins along with set operations. Example queries might look like:\n\n- `(SELECT .. t1 JOIN t2) UNION (SELECT t2 JOIN t3)`\n- `(SELECT .. t1 UNION SELECT t2) JOIN t3 ..`\n- `((SELECT .. t1 JOIN t2) UNION (SELECT t2 JOIN t3)) JOIN t4`\n\nFor all these cases, similar rules apply:\n\n- JOINs should be made on the distribution keys.\n- SET operations should satisfy the `SafeToPushdownUnionSubquery()` conditions.\n\nWhen combined, all conditions should match.\n\n#### Safe to Pushdown Examples:\n\n```sql\n-- All joins are on the distribution key and all the unions have the distribution key in the same ordinal position.\nSELECT * FROM (\n\t(SELECT user_id FROM users_table u1 JOIN users_table u2 USING (user_id))\n\tUNION\n\t(SELECT user_id FROM users_table u1 JOIN users_table u2 USING (user_id))\n) as foo;\n```\n\n```sql\n-- All joins are on the distribution key and all the unions have the distribution key in the same ordinal position.\nSELECT * FROM\n\t(SELECT user_id FROM users_table u1 UNION\n\t SELECT user_id FROM users_table u2) as foo\n\t\tJOIN\n\tusers_table u2\n\tUSING (user_id);\n```\n\n### HAVING subqueries via recursive planning\n\n\nPostgres allows the HAVING clause to contain subqueries. If the subqueries in the HAVING clause don't reference the outer query (i.e., not correlated), then it's possible to recursively plan the subquery in the HAVING clause. This involves using the `RecursivelyPlanAllSubqueries()` function specifically for the HAVING clause.\n\n#### Example:\n\n```sql\n-- Find user_ids who have placed more orders than the average number of orders per user.\nSELECT\n    u.user_id,\n    COUNT(o.order_id) AS total_orders\nFROM\n    users_table u\nJOIN\n    orders_table o ON u.user_id = o.user_id\nGROUP BY\n    u.user_id\nHAVING\n    COUNT(o.order_id) > (SELECT AVG(order_count) FROM (\n                            SELECT\n                                user_id,\n                                COUNT(order_id) AS order_count\n                            FROM\n                                orders_table\n                            GROUP BY\n                                user_id) AS subquery);\n```\n\n\n### Non-colocated subqueries via recursive planning\n\nAssume that there are two subqueries; each subquery is individually joined on their distribution keys. However, when the two subqueries are joined on arbitrary keys, the non-colocated subquery join logic kicks in, as described in `RecursivelyPlanNonColocatedSubqueries()`.\n\n#### Non-colocated subquery Example 1:\n\n```sql\n-- Find users who do not have orders with status 'shipped' and 'pending'\n-- Sub1 and Sub2 are individually safe to pushdown.\n-- The join condition between them is: sub1.user_id != sub2.user_id, which does not preserve distribution key equality.\n-- Citus qualifies sub1 as the anchor subquery and checks whether all other subqueries are joined on the distribution key.\n-- In this case, sub2 is not joined on the distribution key, so Citus decides to recursively plan the whole sub2.\nSELECT sub1.user_id, sub2.user_id\nFROM (\n    SELECT u.user_id\n    FROM users_table u\n    JOIN orders_table o ON u.user_id = o.user_id\n    WHERE o.status = 'shipped'\n    GROUP BY u.user_id\n) AS sub1\nJOIN (\n    SELECT u.user_id\n    FROM users_table u\n    JOIN orders_table o ON u.user_id = o.user_id\n    WHERE o.status = 'pending'\n    GROUP BY u.user_id\n) AS sub2 ON sub1.user_id != sub2.user_id;\n```\n\n#### Non-colocated subquery Example 2:\n\n```sql\n-- Similar logic also applies for subqueries in the WHERE clause.\n-- Both the query in the FROM clause and the subquery in the WHERE clause are individually safe to pushdown.\n-- However, as a whole, the query is not safe to pushdown.\n-- Therefore, Citus decides to recursively plan the subquery in the WHERE clause.\nSELECT o1.order_id, o1.order_date\nFROM orders_table o1, users_table u1\nWHERE o1.user_id = u1.user_id\nAND o1.order_date IN (\n    SELECT o2.order_date\n    FROM orders_table o2, users_table u2\n    WHERE o2.user_id = u2.user_id AND o2.status = 'shipped'\n);\n```\n\n\n### Local table - distributed table JOINs via recursive planning\n\nIn Citus, joins between a local table and a distributed table require special handling. The local table data resides on the Citus coordinator node, while the distributed table data is across multiple worker nodes. The `RecursivelyPlanLocalTableJoins()` C function handles this.\n\n#### Performance Characteristics\n\nLocal and distributed table joins have specific performance traits. They push down filters and projections, meaning only relevant data is pulled to the coordinator. See the `RequiredAttrNumbersForRelation()` and `ReplaceRTERelationWithRteSubquery()` functions for more details.\n\n#### How It Works\n\n1. Citus scans the query tree to find joins between local and distributed tables.\n2. Upon finding such a join, Citus forms a sub-plan for the local table.\n3. This sub-plan retrieves relevant data from the local table into an intermediate result and distributes it across worker nodes.\n4. The original query is then rewritten, replacing the local table with these intermediate results.\n5. Finally, this new query, now only involving distributed tables, is executed using Citus's standard query execution engine.\n\n#### Example 1\n\nFor example, consider a local table `local_users` and a distributed table `orders_table`. A query like this:\n\n```sql\nSELECT *\nFROM local_users l, orders_table o\nWHERE l.user_id = o.user_id;\n```\n\nWould be internally transformed by Citus as follows:\n\n```sql\n-- Create a temporary reference table and populate it with local table data\nCREATE TEMP TABLE temp_local_users AS SELECT * FROM local_users;\nSELECT create_reference_table('temp_local_users');\n\n-- Replace the local table with the temporary distributed table in the original query\nSELECT *\nFROM temp_local_users t, orders_table o\nWHERE t.user_id = o.user_id;\n```\n\n\n#### Configuration Option\n\nBy tweaking `citus.local_table_join_policy`, you can control how Citus behaves for queries involving local and distributed tables. The default behavior is to pull local table data to the coordinator, with exceptions for distributed tables filtered on primary key or unique index.\n\n#### Example 2\n\nFor instance, when the distributed table is guaranteed to return at most one row, Citus chooses to recursively plan the distributed table:\n\n```sql\nSELECT *\nFROM local_users l, orders_table o\nWHERE l.user_id = o.user_id AND o.primary_key = 55;\n```\n\n\n\n### Outer joins between reference and distributed tables\n\nIn general, when the outer side of an outer join is a recurring tuple (e.g., reference table, intermediate results, or set returning functions), it is not safe to push down the join.\n```sql\n\"... ref_table LEFT JOIN distributed_table ...\"\n\"... distributed_table RIGHT JOIN ref_table ...\"\n```\nIn these situations, Citus recursively plans the \"distributed\" part of the join. Even though it may seem excessive to recursively plan a distributed table, remember that Citus pushes down the filters and projections. Functions involved here include `RequiredAttrNumbersForRelation()` and `ReplaceRTERelationWithRteSubquery()`.\n\nThe core function handling this logic is `RecursivelyPlanRecurringTupleOuterJoinWalker()`. There are likely numerous optimizations possible (e.g., first pushing down an inner JOIN then an outer join), but these have not been implemented due to their complexity.\n\n#### Example Query\n\nHere's an example that counts the number of orders for each status, including only statuses that also appear in the reference table:\n\n```sql\nSELECT os.status, COUNT(o.order_id)\nFROM order_status os\nLEFT JOIN orders_table o ON os.status = o.status\nGROUP BY os.status;\n```\n\n#### Debug Messages\n```\nDEBUG:  recursively planning right side of the left join since the outer side is a recurring rel\nDEBUG:  recursively planning distributed relation \"orders_table\" \"o\" since it is part of a distributed join node that is outer joined with a recurring rel\nDEBUG:  Wrapping relation \"orders_table\" \"o\" to a subquery\nDEBUG:  generating subplan 45_1 for subquery SELECT order_id, status FROM public.orders_table o WHERE true\n```\n\nAs of Citus 13.2, under certain conditions, Citus can push down these types of LEFT and RIGHT outer joins by injecting constraints—derived from the shard intervals of distributed tables—into shard queries for the reference table. The eligibility rules for pushdown are defined in `CanPushdownRecurringOuterJoin()`, while the logic for computing and injecting the constraints is implemented in `UpdateWhereClauseToPushdownRecurringOuterJoin()`.\n\n#### Example Query\n\nIn the example below, Citus pushes down the query by injecting interval constraints on the reference table. The injected constraints are visible in the EXPLAIN output.\n\n```sql\nSELECT pc.category_name, count(pt.product_id)\nFROM product_categories pc\nLEFT JOIN products_table pt ON pc.category_id = pt.product_id\nGROUP BY pc.category_name;\n```\n\n#### Debug Messages\n```\nDEBUG:  Router planner cannot handle multi-shard select queries\nDEBUG:  a push down safe left join with recurring left side\n```\n\n#### Explain Output\n```\nHashAggregate\n  Group Key: remote_scan.category_name\n  ->  Custom Scan (Citus Adaptive)\n        Task Count: 32\n        Tasks Shown: One of 32\n        ->  Task\n              Node: host=localhost port=9701 dbname=ebru\n              ->  HashAggregate\n                    Group Key: pc.category_name\n                    ->  Hash Right Join\n                          Hash Cond: (pt.product_id = pc.category_id)\n                          ->  Seq Scan on products_table_102072 pt\n                          ->  Hash\n                                ->  Seq Scan on product_categories_102106 pc\n                                      Filter: ((category_id IS NULL) OR ((btint4cmp('-2147483648'::integer, hashint8((category_id)::bigint)) < 0) AND (btint4cmp(hashint8((category_id::bigint), '-2013265921'::integer) <= 0)))\n```\n\n\n### Recursive Planning When FROM Clause has Reference Table (or Recurring Tuples)\n\nThis section discusses a specific scenario in Citus's recursive query planning: handling queries where the main query's `FROM` clause is recurring, but there are subqueries in the `SELECT` or `WHERE` clauses involving distributed tables.\n\n#### Definitions\n\n- **Recurring**: Here, \"recurring\" implies that the `FROM` clause doesn't contain any distributed tables. Instead, it may have reference tables, local tables, or set-returning functions.\n\n- **Subqueries in SELECT and WHERE**: In case the main query's `FROM` clause is recurring, then no distributed tables should be present in the `SELECT` and `WHERE` subqueries.\n\n#### Citus's Approach\n\nCitus solves this by recursively planning these problematic subqueries, effectively replacing them with calls to `read_intermediate_result()`.\n\n#### Handling the WHERE Clause\n\nFor the `WHERE` clause, the function `RecursivelyPlanAllSubqueries` is called, transforming all subqueries within it.\n\n```sql\n-- Main query FROM clause is recurring, but\n-- WHERE clause contains a pushdownable subquery from\n-- orders_table (distributed table)\nSELECT country_name\nFROM country_codes\nWHERE country_code IN\n\t(SELECT country_code FROM users_table WHERE user_id IN (SELECT user_id FROM orders_table));\n```\n\n#### Handling the SELECT Clause\n\nSimilarly, `RecursivelyPlanAllSubqueries` is called for the `SELECT` clause to replace any existing subqueries.\n\n```sql\n-- Main query FROM clause is recurring, but SELECT clause contains a subquery from orders_table (distributed table)\nSELECT\n\t(SELECT COUNT(*) FROM orders_table WHERE status = 'shipped') AS shipped_orders, country_name\nFROM country_codes;\n```\n\nIn both examples, since the main query's `FROM` clause is recurring and involves subqueries on distributed tables in `WHERE` or `SELECT`, Citus uses `RecursivelyPlanAllSubqueries` to manage these subqueries.\n\n### Logical Planner & Optimizer\n\nAt the high level, all multi-task queries go through the logical planner. However, when it comes to query pushdown or the recursive planner, the logical planner does very little. Most of its complexity deals with multi-shard queries that don't fall into these categories. Below, we are going to discuss those details.\n\n#### Simple Example\n\nThe simplest example of a query processed by the logical planner would be:\n\n```sql\nSELECT * FROM users_table;\n```\n\n#### Academic Background\n\nThe logical planner implements the concepts from the paper: \"Correctness of query execution strategies in distributed databases.\" The paper is available [here](https://dl.acm.org/doi/pdf/10.1145/319996.320009).\n\nIf you find the paper hard to read, Marco provides a good introduction to the same concepts in the following presentation:\n\n- [YouTube Video](https://www.youtube.com/watch?v=xJghcPs0ibQ)\n- [Speaker Deck](https://speakerdeck.com/marcocitus/scaling-out-postgre-sql)\n\n#### Core Functions\n\nWe assume you have either watched the video or read the paper. The core C functions involved are `MultiLogicalPlanCreate()`, `MultiNodeTree()`, and `MultiLogicalPlanOptimize()`.\n\nCitus has a rules-based optimizer. The core function `MultiLogicalPlanCreate()` maps the SQL query to a C structure (e.g., `MultiNode`). Then `MultiLogicalPlanOptimize()` applies available optimizations to the `MultiNode`.\n\nFor instance, one simple optimization pushes the \"filter\" operation below the \"MultiCollect.\" Such rules are defined in the function `Commutative()` in `multi_logical_optimizer.c`.\n\nThe most interesting part of the optimizer is usually in the final stage, when handling the more complex operators (GROUP BY, DISTINCT window functions, ORDER BY, aggregates). These operators are conjoined in a `MultiExtendedOpNode`. In many cases, they can only partially be pushed down into the worker nodes, which results in one `MultiExtendedOpNode` above the `MultiCollect` (which will run on the coordinator and aggregates across worker nodes), and another `MultiExtendedOpNode` below the `MultiCollect` (which will be pushed down to worker nodes). The bulk of the logic for generating the two nodes lives in `MasterExtendedOpNode()` and `WorkerExtendedOpNode()`, respectively.\n\n##### Aggregate functions\n\n[Aggregate functions](https://www.postgresql.org/docs/current/sql-createaggregate.html) can appear in the SELECT (target list) or HAVING clause of a query, often in the context of a `GROUP BY`. The aggregate primarily specify a state function (`sfunc`), which is called for every row in the group, and an `stype` which defines the data format in which intermediate state is held as a type, which maybe be `internal`. Many aggregates also have a `finalfunc`, which converts the last `stype` value to the final result of the aggregate function.\n\nCitus support distributing aggregate functions in several ways described below, each with an example.\n\n**Aggregate functions in queries that group by distribution column can be fully pushed down, since no cross-shard aggregation is needed**. This is mostly handled by the rules in `CanPushDownExpression`.\n\nExample:\n\n```sql\nselect x, avg(y) from test group by x;\nDEBUG:  combine query: SELECT x, avg FROM pg_catalog.citus_extradata_container(10, NULL::cstring(0), NULL::cstring(0), '(i 1)'::cstring(0)) remote_scan(x integer, avg numeric)\nNOTICE:  issuing SELECT x, avg(y) AS avg FROM public.test_102041 test WHERE true GROUP BY x\nNOTICE:  issuing SELECT x, avg(y) AS avg FROM public.test_102042 test WHERE true GROUP BY x\n...\n```\n\n**Built-in, or well-known aggregate functions (based on their name) are distributed using custom rules**. An almost-complete list of aggregates that are handled in this way can be found in the  `AggregateNames` variable. Examples are `avg`, `sum`, `count`, `min`, `max`. To distribute an aggregate function like `avg`, the optimizer implements rules such as injecting a `sum` and `count` aggregate in the worker target list, and doing a `sum(sum)/sum(count)` on the master target list. The logic is agnostic to types, so it will for work any custom type that implements aggregate functions with the same name.\n\nExample:\n\n```sql\nselect y, avg(x) from test group by y;\nDEBUG:  combine query: SELECT y, (pg_catalog.sum(avg) OPERATOR(pg_catalog./) pg_catalog.sum(avg_1)) AS avg FROM pg_catalog.citus_extradata_container(10, NULL::cstring(0), NULL::cs\ntring(0), '(i 1)'::cstring(0)) remote_scan(y integer, avg bigint, avg_1 bigint) GROUP BY y\nNOTICE:  issuing SELECT y, sum(x) AS avg, count(x) AS avg FROM public.test_102041 test WHERE true GROUP BY y\nNOTICE:  issuing SELECT y, sum(x) AS avg, count(x) AS avg FROM public.test_102042 test WHERE true GROUP BY y\n```\n\n**Aggregates that specify a `combinefunc` and have an non-internal `stype` are distributed using generic aggregate functions**. The `worker_partial_agg` aggregate function is pushed down to the worker runs the `sfunc` of the custom aggregate across the tuples of a shard without running the `finalfunc` (which should come after `combinefunc`). The `coord_combine_agg` aggregate function runs the `combinefunc` across the `stype` values returned by `worker_partial_agg` and runs the `finalfunc` to obtain the final result of the aggregate function. This approach currently does not support aggregates whose `stype` is `internal`. A reason we for not handling  `internal` is that it is not clear that they can always be safely transferred to a different server, though that may be overly pedantic.\n\nExample:\n```sql\nselect st_memunion(geo) from test;\nDEBUG:  combine query: SELECT coord_combine_agg('351463'::oid, st_memunion, NULL::postgis_public.geometry) AS st_memunion FROM pg_catalog.citus_extradata_container(10, NULL::cstring(0), NULL::cstring(0), '(i 1)'::cstring(0)) remote_scan(st_memunion cstring)\nNOTICE:  issuing SELECT worker_partial_agg('postgis_public.st_memunion(postgis_public.geometry)'::regprocedure, geo) AS st_memunion FROM public.test_102041 test WHERE true\nNOTICE:  issuing SELECT worker_partial_agg('postgis_public.st_memunion(postgis_public.geometry)'::regprocedure, geo) AS st_memunion FROM public.test_102042 test WHERE true\n```\n\n**Other aggregates will be fully above the `MultiCollect` node, meaning the source data is pulled to the coordinator.** If this is undesirable due to the performance/load risk, it can be disabled using `citus.coordinator_aggregation_strategy = 'disabled'`, in which case the aggregate function calls would result in an error.\n\nExample:\n```sql\nselect st_union(geo) from test;\nDEBUG:  combine query: SELECT postgis_public.st_union(st_union) AS st_union FROM pg_catalog.citus_extradata_container(10, NULL::cstring(0), NULL::cstring(0), '(i 1)'::cstring(0)) remote_scan(st_union postgis_public.geometry)\nNOTICE:  issuing SELECT geo AS st_union FROM public.test_102041 test WHERE true\nNOTICE:  issuing SELECT geo AS st_union FROM public.test_102042 test WHERE true\n```\n\n### Multi Join Order\n\n**Context and Use Case**:\nThis query planning mechanism is primarily geared towards data warehouse type of query planning. It's worth noting that the Citus team has not actively pursued optimizations in this direction, resulting in some non-optimized code paths.\n\n**Join Order Optimization**:\nIn Citus' logical planner, the `JoinOrderList()` function serves to choose the most efficient join order possible. However, its primary focus has been on joins that require repartitioning, as well as some specific non-repartition joins. For example, joins on distribution keys that are not eligible for pushdown planning may pass through this code path, although no optimizations are made in those cases.\n\n**Algorithm Simplicity**:\nThe current algorithm, encapsulated in the `BestJoinOrder()` function, is relatively naive. While it aims to minimize the number of repartition joins, it does not provide a performance evaluation for each of them. This function provides room for performance optimizations, especially when dealing with complex joins that necessitate repartitioning.\n\n**Control via GUCs**:\nTwo GUCs control the behavior of repartitioning in Citus: `citus.enable_single_hash_repartition_joins` and `citus.repartition_join_bucket_count_per_node`.\n\n- **citus.enable_single_hash_repartition_joins**:\n  The default value is \"off\". When \"off\", both tables involved in the join are repartitioned. When \"on\", if one table is already joined on its distribution key, only the other table is repartitioned.\n\n- **citus.repartition_join_bucket_count_per_node**:\n  This setting defines the level of parallelism during repartitioning. The reason for the \"off\" default is tied to this GUC. Opting for a fixed bucket count, rather than dynamically adjusting based on shard count, provides more stability and safety. If you ever consider changing these defaults, be cautious of the potential performance implications.\n\n\n### Combine Query\n\n- **Overview**:\n  The multi-task SELECT queries pull results to the coordinator, and the tuples returned always go through the \"combine query\".\n\n- **Structure and Source**:\n  The `combineQuery` can be traced back through the `DistributedPlan->combineQuery` struct. This query is essentially constructed in the `CreatePhysicalDistributedPlan` function. However, the actual source comes from `MasterExtendedOpNode()` within the logical optimizer. For deeper insights into this logic, you can refer to the paper and video links shared under the \"Logical Planner & Optimizer\" section.\n\n- **Example**:\n  The simplest example is the following where Citus sends `count(*)` to the shards, and needs to do a `sum()` on top of the results collected from the workers.\n  ```sql\n  SET client_min_messages TO DEBUG4;\n  DEBUG:  generated sql query for task 1\n  DETAIL:  query string: \"SELECT count(*) AS count FROM public.users_table_102008 users_table WHERE true\"\n  ....\n  DEBUG:  combine query: SELECT COALESCE((pg_catalog.sum(count))::bigint, '0'::bigint) AS count FROM pg_catalog.citus_extradata_container(10, NULL::cstring(0), NULL::cstring(0), '(i 1)'::cstring(0)) remote_scan(count bigint)\n  D\n  ```\n\n\n### CTE Processing\n\n- **In Postgres 13 and Later Versions**:\nIn Postgres 13 and later versions, CTEs (Common Table Expressions) are almost like subqueries. Usually, these CTEs are transformed into subqueries during `standard_planner()`. Citus follows the same approach via `RecursivelyInlineCtesInQueryTree()`.\n\n- **Additional Consideration in Citus**:\nFor Citus, there's an additional consideration. CTEs that aren't inlined get materialized. In the Citus context, materialization converts these CTEs into intermediate results. Some users leverage this for achieving full-SQL coverage.\n\n- **Extra CTE Check in Citus**:\n Citus includes an extra check before inlining CTEs, conducted by the function `TryCreateDistributedPlannedStmt`. Here, the planner first tries to inline all CTEs and then checks whether Citus can still plan the query. If not, the CTEs remain as is, leading to their materialization. If all CTEs are materialized (e.g., read_intermediate_result), then the query becomes equivalent of a query on reference table, hence full SQL.\n\n **Examples for Better Understanding**:\n I understand the logic might seem complex at first. Simple examples will be provided for better understanding.\n\n```sql\n-- a CTE that is inlined as subquery, and does a query-pushdown\nWITH cte_1 AS (SELECT DISTINCT user_id FROM orders_table)\nSELECT * FROM cte_1;\n\n```\n\nSo, from Citus' query planning perspective\n the above CTE is equivalent to the following subquery\n\n ```sql\nSELECT * FROM\n\t(SELECT DISTINCT user_id FROM orders_table) cte_1;\n```\n\nOnce a CTE is inlined, then the rest of the query\n planning logic kicks in\nfor example, below, the cte is inlined and then\nbecause the subquery is NOT safe to pushdown\nit is recursively planned\n```sql\nWITH cte_1 AS (SELECT DISTINCT product_id FROM orders_table)\nSELECT * FROM cte_1;\n..\nDEBUG:  CTE cte_1 is going to be inlined via distributed planning\nDEBUG: generating subplan 81_1 for subquery SELECT DISTINCT product_id FROM public.orders_table\nDEBUG:  Plan 81 query after replacing subqueries and CTEs: SELECT product_id FROM (SELECT intermediate_result.product_id FROM read_intermediate_result('81_1'::text, 'binary'::citus_copy_format) intermediate_result(product_id bigint)) cte_1;\n```\n\n-  **Which CTEs Are Materialized**:\n Citus follows the same rules as Postgres. See [Postgres documentation](https://www.postgresql.org/docs/current/queries-with.html#id-1.5.6.12.7).\n\n```sql\n-- the same query as the first query\n-- but due to MATERIALIZED keyword\n-- Citus converts the CTE to intermediate result\nWITH cte_1 AS MATERIALIZED (SELECT DISTINCT user_id FROM orders_table)\nSELECT * FROM cte_1;\n\n-- the same query as the first query\n-- but as the same cte used twice\n-- Citus converts the CTE to intermediate result\nWITH cte_1 AS (SELECT DISTINCT user_id FROM orders_table)\nSELECT * FROM cte_1 as c1\n\t\t JOIN cte_1 as c2 USING (user_id);\n```\n\n- **Citus Specific Materialization**:\n Citus first tries to inline the CTEs, but if it decides that after inlining the query cannot be supported due Citus' SQL limitations, it lets the CTE to be materialized.\n\nAs of writing this document, Citus does NOT support\n GROUPING SETs on distributed tables/subqueries. So,\n when we inline the CTE, then Citus would try to plan\n a query with GROUPING SETs on a distributed table, which\n would fail. Then, citus would materialize the cte\n and the final query would be GROUPING SET on an\n intermediate result, hence can be supported\n\n```sql\nWITH users_that_have_orders AS (SELECT users_table.* FROM users_table JOIN orders_table USING (user_id))\nSELECT max(date_of_birth)\nFROM users_that_have_orders\nGROUP BY GROUPING SETS (user_id, email);\n...\nDEBUG:  CTE users_that_have_orders is going to be inlined via distributed planning\n...\nDEBUG:  Planning after CTEs inlined failed with\nmessage: could not run distributed query with GROUPING SETS, CUBE, or ROLLUP\nhint: Consider using an equality filter on the distributed table''s partition column.\n...\nDEBUG:  generating subplan 98_1 for CTE users_that_have_orders: SELECT users_table.user_id, users_table.username, users_table.email, users_table.date_of_birth, users_table.country_code FROM (public.users_table JOIN public.orders_table USING (user_id))\n```\n\n\n### INSERT Query Planning\n\n **At a High-Level Overview**:\n- There are approximately 4 different ways that an INSERT command can be planned in Citus. The first one is the INSERT ... SELECT command, which will be discussed separately.\n\n **INSERT with Sublink (Not Supported)**:\n```sql\nINSERT INTO users_table (user_id) VALUES ((SELECT count(8) FROM orders_table));\nERROR:  subqueries are not supported within INSERT queries\nHINT:  Try rewriting your queries with 'INSERT INTO ... SELECT' syntax.\n\nINSERT INTO users_table (user_id) VALUES (1) RETURNING (SELECT count(*) FROM users_table);\nERROR:  subqueries are not supported within INSERT queries\nHINT:  Try rewriting your queries with 'INSERT INTO ... SELECT' syntax.\n```\n\n **Simple Inserts with a Single VALUES Clause**:\n-- As noted in the \"fast-path router planner\", these INSERT commands are planned with fast-path planning. This does not require calling into `standard_planner()`, and the distribution key should be extracted from the query itself.\n```sql\nINSERT INTO users_table VALUES (1, 'onder', 'onderkalaci@gmail.com', now() - '5 years'::interval, 'TR');\n```\n\n **Main Functions**:\n  The main functions involved in this path are `RegenerateTaskListForInsert()`, `FastPathRouterQuery()`, and `RouterInsertTaskList`. For single-row INSERT tasks, `Job->deferredPruning=true`, meaning we can always do the shard pruning during execution.\n\n **Multi-row INSERTs**:\nFor multi-row INSERTs, `RouterInsertTaskList()` becomes slightly more interesting. Citus groups rows by target shard.\n```sql\nINSERT INTO orders_table (order_id, user_id) VALUES\n\t(1, 1), (2, 2), (3, 1), (4, 3), (5, 2);\n```\n\n **Debug Info**:\n Debug information shows how the query is rebuilt for different user_ids. Here, the shard_count is 4.\n```sql\n-- for user_id: 1\nDEBUG:  query after rebuilding:  INSERT INTO public.orders_table_102041 AS citus_table_alias (order_id, user_id) VALUES ('1'::bigint,'1'::bigint), ('3'::bigint,'1'::bigint)\n\n-- for user_id: 3\nDEBUG:  query after rebuilding:  INSERT INTO public.orders_table_102055 AS citus_table_alias (order_id, user_id) VALUES ('4'::bigint,'3'::bigint)\n\n-- for user_id: 2\nDEBUG:  query after rebuilding:  INSERT INTO public.orders_table_102064 AS citus_table_alias (order_id, user_id) VALUES ('2'::bigint,'2'::bigint), ('5'::bigint,'2'::bigint)\n```\n\n\n\n### INSERT.. SELECT and MERGE Command Query Planning\n\n **Overview**:\n-- This section discusses `INSERT .. SELECT` and `MERGE` commands, which share almost identical planning logic.\n\n **Planning Methods**:\n Broadly, there are three methods to plan these commands:\n1. Pushdown\n2. Pull-to-coordinator\n3. Repartition\n\n **Performance Considerations**:\n When it comes to performance and resource utilization, pushdown is generally the most efficient. For handling large data sizes, the repartition method scales better than the pull-to-coordinator method.\n\n **Further Reading**:\n For more detailed information on pushdown and repartition methods, refer to this [blog post](https://www.citusdata.com/blog/2023/07/27/how-citus-12-supports-postgres-merge/). The post focuses on the `MERGE` command but is also applicable to `INSERT .. SELECT`.\n\n **Examples**:\n The following section will delve into examples, starting with simple ones and moving to more complex scenarios.\n\n### INSERT.. SELECT Query Planning\n\n **Overview**:\n The `INSERT .. SELECT` pushdown logic builds upon the pushdown planning for `SELECT` commands. The key requirements include colocated tables and matching distribution columns. Relevant C functions are `CreateDistributedInsertSelectPlan`, `DistributedInsertSelectSupported()`, and `AllDistributionKeysInQueryAreEqual`.\n\n **Additional Conditions for INSERT .. SELECT pushdown**:\n- The destination table's distribution keys should match the source query's distribution column.\n\n **Simplest INSERT .. SELECT Pushdown Example**:\n```sql\nINSERT INTO users_table SELECT * FROM users_table;\n```\n\n **INSERT .. SELECT with Subqueries/Joins**:\n Provided subqueries can be pushed down, additional checks such as matching distribution columns are performed.\n```sql\nINSERT INTO users_table\n  SELECT users_table.* FROM users_table,\n         (SELECT user_id FROM users_table JOIN orders_table USING (user_id)) as foo\n  WHERE foo.user_id = users_table.user_id;\n```\n\n **Non-pushdownable Scenarios**:\n\n **Due to Distribution Key Mismatch**:\n Citus opts for repartitioning since no \"merge step\" is needed for the `SELECT` query. The deciding function is `IsRedistributablePlan()`.\n  ```sql\n  INSERT INTO users_table (user_id) SELECT user_id + 1 FROM users_table;\n  ```\n\n **Due to LIMIT**:\n The `SELECT` query requires a \"merge step\" for the `LIMIT` clause. Citus uses the pull-to-coordinator strategy.\n  ```sql\n  INSERT INTO users_table SELECT * FROM users_table LIMIT 5;\n  ```\n\n **Pull-to-Coordinator Details**:\n Citus typically pulls `SELECT` results and initiates a `COPY` command to the destination table. See `NonPushableInsertSelectExecScan()`.\n\n**Special Cases**:\n **ON CONFLICT or RETURNING**:\n In these cases, a simple `COPY` is insufficient. Citus pushes results as \"colocated intermediate files\" on the workers, which are colocated with the target table's shards. Then, Citus performs an `INSERT .. SELECT` on these colocated intermediate results. See `ExecutePlanIntoColocatedIntermediateResults()` and `GenerateTaskListWithColocatedIntermediateResults()`.\n\n **Example: Pull-to-coordinator with COPY back to shards**:\n```sql\nINSERT INTO users_table SELECT * FROM users_table LIMIT 5;\n```\n\n **Example: Pull-to-coordinator with push as colocated intermediate results**:\n```sql\nINSERT INTO users_table SELECT * FROM users_table LIMIT 5 ON CONFLICT(user_id) DO NOTHING;\n```\n\n\n### MERGE Command Query Planning\n\n **Overview**:\n The `MERGE` command planning is similar to `INSERT .. SELECT`. The key difference is in the pull-to-coordinator strategy. `MERGE` always uses \"colocated intermediate result\" files, as the final executed command must be a `MERGE` command, not a `COPY`. The entry function in the code is `CreateMergePlan()`.\n\n**Further Reading**:\n For more insights, check out this [blog post](https://www.citusdata.com/blog/2023/07/27/how-citus-12-supports-postgres-merge/).\n\n **Pushdown MERGE Example**:\n The join is based on the distribution key.\n```sql\nMERGE INTO users_table u\nUSING orders_table o\nON (u.user_id = o.user_id)\nWHEN MATCHED AND o.status = 'DONE' THEN DELETE;\n```\n\n**Pull-to-Coordinator MERGE Example**:\n The source query requires a \"merge step\" on the coordinator.\n```sql\nMERGE INTO users_table u\nUSING (SELECT * FROM orders_table ORDER BY order_date LIMIT 50) o\nON (u.user_id = o.user_id)\nWHEN MATCHED AND o.status = 'DONE' THEN DELETE;\n```\n\n **Repartition MERGE Example**:\n The join is NOT on the distribution key, and the source query doesn't require a \"merge step\" on the coordinator. Note that this example is mostly hypothetical to illustrate the case.\n```sql\nMERGE INTO users_table u\nUSING (SELECT * FROM orders_table ORDER BY order_date) o\nON (u.user_id = o.product_id)\nWHEN MATCHED AND o.status = 'DONE' THEN DELETE;\n```\n\n### UPDATE / DELETE Planning\n\n **Overview**:\n The planning logic for UPDATE/DELETE queries is quite similar to what we've discussed for INSERT and MERGE commands. There are essentially four primary methods of planning:\n\n **1) Fast-Path Router Planning**:\n Targets a single shard and filters on the distribution key in the WHERE clause.\n```sql\nUPDATE users_table SET email = 'new@email.com' WHERE user_id = 5;\n```\n\n\n **2) Router Planning**:\n Targets a single shard, but all the shards are on a single node and are colocated.\n```sql\nUPDATE users_table u\n\tSET email = ''\n\tFROM orders_table o\n\tWHERE o.user_id = u.user_id AND\n\t\t\tu.user_id = 5 AND\n\t\t\to.status = 'done';\n```\n\n\n **3) Pushdown Planning**:\n The query can be pushed down to worker nodes, targeting multiple shards. Joins are also possible if they are on distribution keys.\n```sql\nUPDATE users_table SET email = 'new@email.com'\nWHERE user_id IN (SELECT user_id FROM orders_table WHERE status = 'in progress');\n```\n\n **Additional Example for Pushdown with Materialized CTE**:\n```sql\nWITH high_value_users AS (\n  SELECT user_id FROM orders_table WHERE status = 'done' ORDER BY order_date LIMIT 50\n)\nUPDATE users_table SET username = 'High Value'\nWHERE user_id IN (SELECT user_id FROM high_value_users);\n```\n\n\n **4) Recursive Planning**:\nUsed for more complex queries, like those with subqueries or joins that can't be pushed down. The queries are planned recursively.\n```sql\nDELETE FROM users_table WHERE user_id\nIN (SELECT user_id FROM orders_table WHERE order_date < '2023-01-01' ORDER BY order_date LIMIT 5);\n```\n\n### Correlated/Lateral Subqueries in Planning\n\n**Overview**:\nCorrelated or LATERAL subqueries have special behavior in Citus. They can often be pushed down, especially when the join is on the distribution key. There are limitations for joins not on the distribution key.\n\n\n **Key Code Details**:\n For more information on the code, check the following functions:\n `DeferErrorIfCannotPushdownSubquery()` ->\n `ContainsReferencesToOuterQuery()`, `DeferErrorIfSubqueryRequiresMerge()`, `DeferredErrorIfUnsupportedLateralSubquery()`. LATERAL queries are different/unique: even if the subquery requires a merge step such as a `LIMIT`, if the correlation is on the distribution column, we can push it down. See [#4385](https://github.com/citusdata/citus/pull/4385).\n\n\n\n **Example 1**: Using LATERAL, where the join is on the distribution key.\n```sql\nSELECT u.*, o_sum.total\nFROM users_table u,\nLATERAL (SELECT count(DISTINCT status) as total FROM orders_table o WHERE o.user_id = u.user_id) o_sum;\n```\n\n\n **Example 2**: Complex LATERAL with GROUP BY on a non-distribution key.  It's pushdownable because the join is on the distribution key.\n```sql\nSELECT u.*, o_info.product, o_info.total\nFROM users_table u,\nLATERAL (\n  SELECT o.product_id as product, count(DISTINCT o.status) as total\n  FROM orders_table o WHERE o.user_id = u.user_id\n  GROUP BY o.product_id\n) o_info;\n```\n\n\n\n **Debug and Error Messages**:\nWhen it's not possible to push down correlated subqueries, recursive planning also can't be used.\n```sql\nSELECT u.*\nFROM users_table u,\nLATERAL (\n  SELECT o.product_id as product\n  FROM orders_table o WHERE o.user_id != u.user_id\n) o_info;\n\nDEBUG:  skipping recursive planning for the subquery since it contains references to outer queries\nERROR:  complex joins are only supported when all distributed tables are joined on their distribution columns with equal operator\n```\n\n\n\n\n\n\n\n\n\n### Planning Methodologies in Citus: Compatibility and Incompatibility\n\n\n\n#### Compatibilities\n\n\n\n1. **Interleaving Recursive and Pushdown Planning**:\n\n    - Recursive planning and pushdown planning can often be interleaved within a query. This allows for greater flexibility and optimized performance.\n\n\n\n2. **Router Queries in Recursive Planning**:\n\n    - Subqueries in recursive planning can often be router queries. This includes both fast-path router and regular router queries.\n\n\n\n3. **Command Types**:\n\n    - Command types like `UPDATE`, `DELETE`, `MERGE`, and `INSERT .. SELECT` can work well with both pushdown and recursive planning.\n\n\n\n#### Incompatibilities\n\n1. **Repartition Joins**:\n\n    - Repartition joins are generally incompatible with both recursive and pushdown planning. If a query uses recursive planning, it can't also use repartition joins. However, re-partition joins can be in a CTE that is recursively planned.\n\n\n\n#### Examples of Compatibility and Incompatibility\n\n##### Recursive and Pushdown Planning\n\n```sql\n-- Example 1: Recursive and Pushdown Planning Interleaved\n-- subquery is recursively planned multi-shard command\nWITH recent_orders AS (\n  SELECT * FROM orders_table ORDER BY order_date LIMIT 10\n)\nSELECT * FROM users_table WHERE user_id IN (SELECT user_id FROM recent_orders);\n```\n\n##### Router Queries in Recursive Planning\n\n```sql\n-- Example 2: Subquery as Fast-Path Router Query is recursively planned\n-- the rest is pushdown\nWITH user_info AS (\n  SELECT * FROM users_table WHERE user_id = 5 ORDER BY date_of_birth LIMIT 1\n)\nSELECT * FROM orders_table WHERE user_id IN (SELECT user_id FROM user_info);\n```\n\n##### UPDATE Pushdown and Recursive Planning\n\n```sql\n-- Example 3: UPDATE command with Pushdown, Router and Recursive Planning\n-- recursively planned router query and the rest is pushdown\nWITH high_value_users AS (\n  SELECT user_id FROM orders_table WHERE user_id = 15 AND status = 'done' ORDER BY order_date LIMIT 50\n)\nUPDATE users_table SET username = 'High Value' WHERE user_id IN (SELECT user_id FROM high_value_users);\n```\n\n\n\n#### Incompatibility with Repartition Joins\n\n```sql\n-- Example 4: Incompatible Query involving Recursive Planning and Repartition Joins\n-- This query will fail because it tries to use recursive planning for recent_orders\n-- and trying to repartition joins between o2 and recent_orders\nWITH recent_orders AS (\n  SELECT * FROM orders_table WHERE order_date > '2023-01-01' LIMIT 10\n)\nSELECT u.*\nFROM users_table u\nJOIN recent_orders o ON u.user_id = o.product_id\nJOIN orders_table o2 ON o2.product_id = o.product_id;\nERROR:  complex joins are only supported when all distributed tables are co-located and joined on their distribution columns\n```\n\n## Combine query planner\n\nThe combine planner is the final stage of planning for multi-shard queries. The logical & physical planner path creates a combine query, which will run on the coordinator. The combine query contains a special function call (called the extra data container), which can be observed using debug messages emitted during planning:\n\n```\nSET client_min_messages TO debug4;\nSELECT count(*) FROM test;\n…\nDEBUG:  combine query: SELECT COALESCE((pg_catalog.sum(count))::bigint, '0'::bigint) AS count FROM pg_catalog.citus_extradata_container(10, NULL::cstring(0), NULL::cstring(0), '(i 1)'::cstring(0)) remote_scan(count bigint)\n```\n\nThe reason we use a special function call is simply that it lets us put custom information in query trees. We use the same approach to pass sharding information into the deparser.\n\nIn the combine query planner, we run the combine query through standard_planner and use the set_rel_pathlist_hook to inject a CustomPath plan for the function call. The CustomPath translates into the Citus Custom Scan that runs a Job.\n\n## Restriction Equivalence\n\nIn the PostgreSQL source code, an `EquivalenceClass` is a data structure used in query optimization. It is a way to represent a set of expressions in a query that are all equal. The PostgreSQL query planner uses this information to choose the most efficient execution plan for a query.\n\nFor example, let's say you have a query like this:\n\n```sql\nSELECT * FROM table1, table2 WHERE table1.a = table2.b AND table1.a = 5;\n```\n\nHere, `table1.a`, `table2.b`, and `5` can all be considered to belong to the same equivalence class because they are equal. Knowing this, the query optimizer might choose to use an index on `table1.a` or `table2.b` to speed up the query, among other optimizations.\n\nOne level beyond that, Postgres can also apply transitivity rules for joins:\n\n```sql\nSELECT * FROM table1, table2,table3 WHERE table1.a = table2.a AND table1.a = table3.a;\n```\n\nHere, `table1.a`, `table2.a`, and `table3.a` can all be considered to belong to the same equivalence class because they are (transitively) equal.\n\n\n\nCitus finds this information important. But Citus and Postgres have different structures. In Postgres, each (sub)query inside a big query is planned by itself. Citus tries to plan the whole big query at one time for performance reasons (see Query Pushdown Planning). This makes how they use Equivalence Classes different.\n\nIn Postgres, each subquery has its own Equivalence Classes. But Citus needs Equivalence Classes for the whole big query. For example:\n\n```sql\nSELECT count(*) FROM (SELECT a FROM t1 JOIN t2 USING(a)) as foo,\n(SELECT a FROM t3 JOIN t4 USING (a)) as bar USING (a);\n```\n\nFor Postgres, it's enough to make Equivalence Classes for the subqueries `foo` and `bar`. Then make another one for the top-level query where `foo` and `bar` join. Postgres can plan joins this way.\n\nIn Citus, we need to check if all tables in the big query (t1,t2,t3,t4) join on distribution columns. Citus makes Equivalence Classes for the whole big query. The logic is in the `AttributeEquivalenceClass` C struct. The function `GenerateAllAttributeEquivalences()` makes this structure in Citus. The idea is to simply merge all the Equivalence classes of different query levels into a one Equivalence Class (e.g., AttributeEquivalenceClass)\n\nCitus also introduces a new idea: RTEIdentity. Each table in the query gets a unique ID called RTEIdentity (see `AssignRTEIdentity()` C function). This ID helps make a new type of Equivalence Class that works for many levels of small queries. Without RTEIdentity, we can't tell tables apart in different levels of the query. We rely on a hack while assigning the RTEIdentities. We basically use a field in `RangeTblEntry` struct that we are sure it is not used for tables. In practice, this might break at some point.\n\n\n\n## Recurring Tuples\n\n\n\nThe Recurring Tuples concept in Citus helps manage expressions that give the same set of results across multiple shards. This is mainly useful for JOIN operations. The idea is to understand and handle how some tables or functions behave the same way across different shards of a distributed table. This concept helps to provide accurate error messages if such recurring tuples are used in a way that might give wrong results.\n\nThe `RecurringTuplesType` enum in the code helps categorize these recurring tuples into different types. The types include:\n\n- Reference Table\n- Function\n- Empty Join Tree\n- Result Function\n- Values\n\nThe main point is that recurring tuples \"recur\" for each shard in a multi-shard query.\n\nFor example, consider a JOIN between a distributed table and a reference table. The query on each shard would look something like this:\n\n```sql\nSELECT ... FROM dist_table_shard_1 JOIN ref_table_shard_1;\nSELECT ... FROM dist_table_shard_2 JOIN ref_table_shard_1;\n...\nSELECT ... FROM dist_table_shard_n JOIN ref_table_shard_1;\n```\n\nHere, `ref_table_shard_1` is a recurring tuple because it appears in each shard query of the distributed table (`dist_table_shard_X`). It \"recurs\" for each shard, making it a recurring tuple.\n\nIn summary, the Recurring Tuples concept in Citus helps in managing and identifying expressions that behave the same way across different shards, mainly to ensure accurate query results and error handling.\n\n# Executor\n\nCitus primarily hooks into the PostgreSQL executor by producing a query plan with a CustomScan. The overall hierarchy of where Citus hooks into the executor looks like this:\n\n- PostgreSQL executor\n  - ExecutorRun_hook\n    - Subplans are executed before regular execution\n    - CustomScan functions are invoked as part of overall scan tree\n      - BeginCustomScan (which steps are included depends on the query)\n        - Function calls & distribution column parameters are evaluated\n        - Deferred shard pruning\n        - Lock shards to prevent concurrent move  (write only)\n        - Find placements for shards\n      - ExecCustomScan\n        - Adaptive Executor executes a list of tasks and concatenates the results into a tuple store\n          - Re-partition jobs are executed\n          - Remote tasks are executed\n          - Local tasks are executed\n\nWe describe each part in more detail below.\n\n## Custom scan\n\nThe Custom scan is the main entry point for the executor into Citus. The whole query plan might be a single Custom Scan node (e.g. single shard queries), or it can be a leaf node in a query plan that aggregates results across shards.\n\nThe BeginCustomScan function evaluates function calls, parameters, and performs deferred pruning, and local plan caching, which are described in the next few sections. The ExecCustomScan function runs the adaptive executor which executes a list of tasks across the worker nodes.\n\nWe also use top-level executor hooks, but primarily to capture some execution time information. The one important thing we do in the top-level ExecutorRun hook is execute subplans. That is because we allow subqueries to appear in certain parts of the combine query, and in case of a subquery on a Citus table that subquery needs to be executed before the overall plan.\n\nWe use a separate custom scans for insert..select and merge commands due to the specialized nature of these commands (multiple phases).\n\n![Diagram of CustomScan APIs](https://wiki.postgresql.org/images/0/05/CustomScan_Fig01.png)\n\n## Function evaluation\n\nIt is often necessary to evaluate function calls on the coordinator, rather than pushing them down to the worker node. One example is evaluating the `nextval('my_sequence')` in an insert, or stable functions like `now()` that should return the same value for the duration of the query. This is especially true for writes to replicated (reference) tables, since we cannot afford to push down function calls that might return different values on different nodes. We perform function evaluation on the “job query” of the distributed plan in `ExecuteCoordinatorEvaluableExpressions`, before deparsing the query.\n\nWhether a function call should be evaluated once on the coordinator, or many times (e.g. for every row) depends on the context in which the function call appears. For instance, a function call in a WHERE or SELECT clause might be evaluated many times, while a function call in a VALUES clause will only be evaluated once. On the other hand, stable & immutable functions are expected to return the same result for the same input for the whole query execution, so they should be evaluated once, unless their input can change (e.g. parameter is a column).\n\nSo far, the function evaluation logic does not distinguish between different contexts within queries. Instead, we follow a simple policy:\n\n- For inserts, evaluate all function calls, including calls to volatile functions, but disallow stable/volatile functions in RETURNING\n- For update/delete, evaluate all function calls, but disallow volatile functions\n- For select, do not evaluate function calls on coordinator (not entirely correct)\n\nWhen DML commands appear in a CTE, the restriction only applies to the CTE. In many cases, the CTE will in that case be planned and executed separately through recursive planning.\n\nA function call that takes a column (Var) as a parameter will not be evaluated on the coordinator, since it depends on data on the worker nodes and will need to be evaluated many times. However, if we did this on a replicated table then stable/volatile functions may return different results on different nodes, in the context of an update/delete it would cause replicas diverge. That is one of the reasons why we disallow stable/volatile functions in update/delete statements, but we could permit them for regular tables with a single replica.\n\nThe reason we also disallow volatile functions in regular update/delete is purely implementation related: Our current function evaluation logic does not know how to distinguish between stable & volatile functions. If we were to run it on a query that contains WHERE x > random(), it would evaluate the random() once, even though it’s supposed to be pushed down and re-evaluated for every row.\n\n## Prepared statements\n\nPrepared statements is a feature that lets clients send a query once and then execute it multiple times. Plans may be cached across execution. Prepared statements can be created explicitly via PREPARE/EXECUTE commands, via protocol messages (what most clients do), via PL/pgSQL, and via SPI.\n\nCitus has limited prepared statement support in the sense that they functionally work, but there are only a few cases in which plans are meaningfully cached across executions. Despite the lack of meaningful optimization, prepared statements involve a lot of complexity and counterintuitive logic. Which parts are necessary and which parts are technical debt is left as an exercise to the reader.\n\nThe plan of a prepared statement is only cached when the same prepared statement is executed 5 times by PostgreSQL (hard-coded value). The 5th time, the planner is called without supplying parameter values to obtain a “generic plan” and that plan is cached unless it is much costlier than using custom plan. Hence, the planner might be called twice on the 5th execution and if a generic plan is created then the planner may not be called again.\n\nThere are a few important cases to distinguish in case of Citus:\n\n- Multi-shard queries vs. single shard (Fast path & router)\n- Custom plan vs. Generic plan.\n- Parameter in a filter on the distribution column vs. only on other columns\n- Local vs. remote execution\n- Combinations of parameters & function evaluation.\n\nLet’s start with the simplest case: Multi-shard queries. These queries have complex planning logic, and it would be even more complex if the planner did not know the values of parameters. Therefore, we dissuade PostgreSQL from using a generic plan by returning a mock PlannedStmt with an extremely high cost when asked for a generic plan (see `DissuadePlannerFromUsingPlan()`). That will cause PostgreSQL to keep using a custom plan with known parameter values. In addition, we replace any Params that appear in the query tree with their Const values in ResolveExternalParams before distributed planning, so the remaining planner logic does not need to concern itself with query parameters.\n\nFor single shard queries, the story is a lot more complex. An important question is whether there is a parameter in the distribution column, and whether a query is single shard in the planner or not. A query like `SELECT * FROM table1 WHERE distcol = $1` will clearly go to a single shard, but for a query like `SELECT * FROM table1 WHERE distcol = $1 UNION ALL SELECT * FROM table2 WHERE distcol = $2` it may or may not be.\n\nWe do not precisely distinguish all possible cases, but rather have a simple distinction:\n\n- Fast path queries are simple queries on a single table with a \"distribution column\" = \"Param or Const\" filter (or single row inserts). We know that they prune to at most 1 shard regardless of the parameter value. The case of “distcol = NULL” is false/null by definition (unlike “distcol IS NULL”) and therefore prunes to 0 shards.\n- Router queries are arbitrarily complex queries that prune down to a single shard at planning time based on the RestrictInfo data structures obtained from postgres planner.\n\nWe can only decide whether a query is a router query in the planner, because if it is not a router query, we need to fall back to the multi-shard query planning code path. Hence, we can only support generic router plans when all distribution column filters are constant, or there are only single shard/reference tables in the query. The router planner cannot prune based on unbound parameters and will therefore return a soft error. When the planner sees a soft error, we return a mock plan with a high cost, similar to multi-shard queries.\n\nFast path queries prune to a single shard regardless of the parameter values. If the distribution column value is a parameter, we defer additional planning decisions, in particular “shard pruning” to the executor (deferredPruning flag in the Job). Currently, we resolve the parameters in `ExecuteCoordinatorEvaluableExpressions()` which replaces the Param nodes in the query tree, and then `TargetShardIntervalForFastPathQuery()` finds \"distribution column\" = \"Const\" filters in the WHERE clause. This could perhaps be optimized but keeps the logic consistent between parameters and non-parameterized queries.\n\nFor both fast path queries and router queries, the job query tree for single shard queries still has all the parameters when we get to the executor. We resolve the parameters in the query tree before deparsing when:\n\n- pruning is deferred (has WHERE distcol = $1 …)\n- the query is a DML that contains function calls that need to resolved\n\nThe latter happens primarily because function evaluation also resolves parameters. Otherwise, it would not be able to resolve expressions like `stable_fn($1)`. If the parameters are not resolved in the executor, they are passed on to the worker node using the libpq functions that take parameters.\n\nBoth fast path and router query plans can be stored in the PostgreSQL cache (plancache.c) if they are run at least five times. The way these plans are handled depends on whether or not the query includes a parameter on the distribution key. In the first case below, there is no parameter; in the second case, there is a parameter:\n\n- the query pruned to a single shard in the planner, the task is therefore static (always goes to the same shard group, with same query string)\n- the query uses deferred pruning, the shard group is therefore decided in the executor (not cached, query string rebuilt)\n\nBoth scenarios reduce compute cycles in terms of planning the distributed query, but the plan for the shard query is never cached, except in the local execution case, which is described in the next section.\n\nThe current structure is “less than ideal”, but by now it is battle hardened and has extensive regression tests that cover all the cases. It should be improved, but with caution. Caching the wrong plan could easily lead to invalid results, and there are many subtle edge cases.\n\n### Local plan caching\n\nWe currently only take advantage of plan caching for shard queries that access a single local shard group and use deferred pruning (described in the previous section). This avoids reparsing or replanning the query on the local shard. That works well in combination with smart clients that immediately connect to the right node, function call delegation, triggers, and Citus local tables.\n\nWe can only know whether we are dealing with a local shard group after evaluating parameters and functions. Immediately after that, we plan the query on the local shard group and store the resulting (regular PG) plan in the distributed plan data structure (Job). The reason we store it in the distributed plan is that it is already cached by PostgreSQL, so anything we add to the plan will be cached along with it, with the correct lifecycle. We store a list of local plans, one for each shard plan.\n\nLocal plan caching quite significantly improves performance for certain workloads, but it comes with a subtle caveat. For queries with deferred pruning, we only know whether the shard query is on a local shard query after evaluating parameters and function calls, which we do by replacing them in the query tree. However, to obtain a cacheable generic plan, we need to use the original query tree which still has the original function calls and parameters. That means re-execute those function calls when executing the shard query, which is unusual since we usually only execute them in the BeginCustomScan hook. Since we only do this for local execution, the function calls will still run in the same process and will therefore have the same effect, but it means we sometimes evaluate function calls twice. That is acceptable for stable functions, but not for volatile functions. We therefore skip caching when there are calls to volatile functions.\n\n## Adaptive executor\n\nOnce function and parameter evaluation are completed and the final task list is ready, we call into the adaptive executor. The goal of the adaptive executor is to efficiently execute a list of tasks. A task is typically a shard query that is to be executed on 1 placement (read) or all placements (write). It can also be an arbitrary command unrelated to shards. Implementation-wise, its primary function is to concurrently execute multiple queries on multiple remote nodes using libpq in non-blocking mode with appropriate failure handling and adaptive connection pools.\n\nThe adaptive executor tries to minimize network round trips for single shard statements (transactional workloads) by using a single, cached connection per node, and parallelize queries using multiple connections per node for multi-shard statements (analytical workloads, ETL, DDL).\n\nHistorically, Citus executed single shard queries via a single connection per worker node (router executor), while it executed multi-shard queries via a connection per shard to parallelize across nodes and cores (real-time executor), but this approach had several limitations.\n\n**The executor must consider preceding writes and locks on shards in the transaction**. In the past, if the router executor performed 2 inserts on different shards over the same connection, then the real-time executor could no longer run. It is not valid to query those shards over two separate connections, since only one of them would see the inserts. The executor must ensure that after a write or lock on a shard group, all subsequent queries on the shard group use the same connection until transaction end.\n\n**The executor should consider fast vs. slow multi-shard commands**. We observed many cases in which multi-shard commands only took a few milliseconds (e.g. index lookups on a non-distribution column) and opening a connection per shard was excessive, since it could add tens or hundreds of milliseconds to a query that could otherwise finish in 10-20ms. _Whether parallelization is beneficial depends on the runtime of individual tasks._ Some tasks can also take much longer than others.\n\n**The executor should gracefully handle failures**. One of the more challenging parts of doing remote, concurrent query execution is handling a variety of failures, including timeouts, failed connections, and query errors. The handling can be different for reads and writes, since reads on replicated tables can fail over to a different placement.\n\n**The executor should consider replicated shards**. Writes to reference tables (or replicated shards) need to be sent to all nodes, while reads can fail over to other replicas. Update/delete can be performed in parallel due to the exclusive lock on the shard, while inserts need to run in a consistent order to avoid deadlocks in case of constraint violations. The executor also needs to consider that replicas may be on the local node and use local execution.\n\nTo fulfill the first two requirements, the adaptive executor uses a (process-local) **pool of connections per node**, which typically starts at 1 connection, but can grow based on the runtime of the query. Queries on shard groups that were already modified are assigned to the connection that performed the modification(s), while remaining queries are assigned to the pool (to be parallelized at will).\n\n<img alt=\"Adaptive executor pools\" src=\"../../../images/executor-connections.png\" width=\"500\">\n\n**Both the pool and the session have a “pending queue” and a “ready queue”**. The pending queue is mainly used in case of replication (e.g. reference tables). In the case of reads, whether a (pending) task on placement 2 needs to run depends on whether the (ready) task on placement 1 succeeds. In case of inserts, we write to the placements in order, so the task on placement 2 runs only once placement 1 is done.\n\n**The main loop of the adaptive executor waits for IO on the overall list of connections using a WaitEventSet**. When a connection has IO events, it triggers the connection state machine logic (ConnectionStateMachine). When the connection is ready, it enters the transaction state machine logic (TransactionStateMachine) which is responsible for sending queries and processing their results. The executor is designed with state machines, and the code has an extensive comment describing the state machines, please refer there for the details\n\nWhen a connection is ready, we first send BEGIN if needed, and then take tasks from the session-level ready queue, and then tasks from the pool-level ready queue. We currently process one task at a time per connection. There are opportunities for optimization like pipelining/batching, though we need to be careful not to break parallelism.\n\n**Late binding of tasks to connections via the pool-level queue has nice emergent properties**. If there is a task list with one particularly slow task, then one connection will spend most of its time on that task, while other connections complete the shorter tasks. We can also easily increase the number of connections at runtime, which we do via a process called slow start (described below). Finally, we’re not dependent on a connection being successfully established. We can finish the query when some connections fail, and we finish the query if BEGIN never terminates on some connection, which might happen if we were connecting via outbound pgbouncers.\n\n**The pool expands via “slow start”, which grows the pool every ~10ms as long as tasks remain in the pool-level queue**. The name slow start is derived from the process in TCP which expands the window size (the amount of data TCP sends at once). As in the case of TCP, the name slow is a misnomer. While it starts very conservatively, namely with 1 connection, the _rate_ at which new connections open increases by 1 every 10ms, starting at 1. That means after 50ms, the executor is allowed to open 6 additional connections. In a very typical scenario of 16 shards per node, the executor would reach maximum parallelism after ~60ms. It will open at most as many additional connections as there are tasks in the ready queue.\n\n<img alt=\"Adaptive executor slow start example\" src=\"../../../images/executor-slow-start.png\" width=\"700\">\n\nThe 10ms was chosen to be higher than a typical connection-establishment time, but low enough to quickly expand the pool when the runtime of the tasks is long enough to benefit from parallelism. The 10ms has mostly proven effective, but we have seen cases in which slow connection establishment due to Azure network latencies would justify a higher value. In addition, we found that workloads with many queries in the 20-60ms range would see a relatively high number of redundant connection attempts. To reduce that, we introduced “cost-based connection establishment”, which factors in the average task execution time compared to the average connection establishment time and thereby significantly reduced the number of redundant connections.\n\n**The citus.max_adaptive_executor_pool_size setting can be used to limit the per-process pool sizes**. The default behaviour of the adaptive executor is optimized for parallel query performance. In practice, we find that there is another factor than runtime that users care about: memory. The memory usage of a query that uses 16 connections can be 16 times higher than the memory usage of a query that uses 1 connection. For that reason, users often prefer to limit the pool size to a lower number (e.g. 4) using citus.max_adaptive_executor_pool_size.\n\n**The citus.max_shared_pool_size setting can be used to limit the pool sizes globally**. It’s important to reiterate that the adaptive executor operates in the context of a single process. Each coordinating process has its own pools of connections to other nodes. This would lead to issues if e.g. the client makes 200 connections which each make 4 connections per node (800 total) concurrently while max_connections is 500. Therefore, there is a global limit on the number of connections configured by max_shared_pool_size. The citus.max_shared_pool_size is implemented in the connection management layer rather than the executor. Refer to the connection management section for details.\n\n**The comment on top of [adaptive_executor.c](executor/adaptive_executor.c) has a detailed description of the underlying data structures.** While these data structures are complex and this might look like an area technical debt, the current data structures and algorithm have proven to be a relatively elegant and robust way to meet all the different requirements. It is worth noting that a significant part of the complexity comes from dealing with replication, and shard replication is mostly a deprecated feature, but keep in mind that reference tables are also replicated tables and most of the same logic applies.\n\n## Local execution\n\nWhen the adaptive executor completes all of its remote tasks, the final step is to perform local execution. We formally see this as part of the adaptive executor, though the code is largely separate (in local_executor.c). Local execution is essentially just executing the shard queries on local shards directly by invoking the planner & executor. In other words, there is no additional backends or connections are established for local execution.\n\nSome queries strictly require local execution. In particular, queries that depend on objects (types, tables) created by the current transaction, or joins between regular tables and Citus local or reference tables.\n\nIn case of a multi-shard query, a downside of local execution is that there is no parallelism across shards. Therefore, the executor tries to avoid local execution for simple multi-shard queries outside of a transaction block. Instead, it will open multiple connections to localhost to run queries in parallel. In a multi-statement transaction, the executor always prefers local execution even for multi-shard queries, since the tranasaction might also perform operations that require local execution.\n\nSome queries cannot use local execution. For instance, we cannot use CREATE INDEX CONCURRENTLY as part of a bigger transaction, and we have not implemented a local version of EXPLAIN ANALYZE. We also cannot perform replication commands like creating a subscription via local execution. For the most part, these commands are typically executed outside of a transaction block or as internal commands, so it does not significantly affect the user experience.\n\nThe executor always does the local execution after remote execution. That way, if there are any problems with the remote execution, Citus can still switch back (e.g., failover) to local execution.\n\n## Subplans\n\nExecution of subplans (CTEs, subqueries that cannot be pushed down) is relatively straight-forward. The distributed plan has a list of subplans, which can be regular or distributed, and they are passed to the PostgreSQL executor sequentially.\n\nThe result of each subplan is broadcast to all participating nodes via the COPY .. WITH (format ‘result’) command, which writes to an intermediate result. The intermediate results are read in subsequent shard queries via the read_intermediate_result function.\n\nA current downside of the read_intermediate_result function is that it first copies all the tuples into a tuple store, which may be flushed to disk. This could be fixed through a CustomScan, or in PostgreSQL itself.\n\n## Re-partitioning\n\nRe-partitioning happens when joining distributed tables on columns other than the distribution column, or when the tables are not co-located. In the distributed plan, a re-partitioning operation is generally expressed through a Job which has dependent Jobs. The dependent Jobs are a special type of subplan whose results are re-partitioned.\n\nTwo stages are executed to resolve the dependent jobs:\n\n- Run a query on all shards using the worker_partition_query_result function, which writes the result of the query to a set of intermediate results based on a partition column and set of hash ranges\n- Fetch the intermediate results to the target node using fetch_intermediate_result, for each source shard group & target hash range pair.\n\nThese stages are run in parallel for all dependent jobs (read: all tables in a join) by building a combined task list and passing it to the adaptive executor. This logic primarily lives in ExecuteTasksInDependencyOrder.\n\nOnce all dependent jobs are finished, the main Job is executed via the regular adaptive executor code path. The main job will include calls to read_intermediate_result that concatenate all the intermediate results for a particular hash range.\n\n<img alt=\"Single hash re-partition join example\" src=\"../../../images/single-repartition-join.png\" width=\"700\">\n\nDependent jobs have similarities with subplans. A Job can only be a distributed query without a merge step, which is what allows the results to be repartitioned, while a subplan can be any type of plan, and is always broadcast. One could imagine a subplan also being repartitioned if it is subsequently used in a join with a distributed table. The difference between subplans and jobs in the distributed query plans is one of the most significant technical debts.\n\n## COPY .. FROM command\n\nThe COPY .. FROM command is used to load a CSV (or TSV, or binary copy format) file or stream from the client into a table. The \\copy command is a psql command that can load files from the client, and internally does COPY .. FROM STDIN and sends the file contents over the socket.\n\n**Citus supports COPY into distributed tables via the ProcessUtility_hook by internally doing a COPY to each shard.** We go through the regular COPY parsing logic in PostgreSQL (BeginCopyFrom & NextCopyFrom), which reads from the socket, parses the CSV, and returns a tuple. The tuple is passed through the CitusCopyDestReceiver API. Most of the relevant logic lives in CitusSendTupleToPlacements.\n\n**The CitusCopyDestReceiver inspects the value in the distribution column and finds the corresponding shard.** It opens a connection to the node(s) on which the shard is placed, starts a COPY into the shard, and forwards the tuple. For performance reasons, we use the binary copy format over the internal connections, when possible (e.g. all types have send/receive), even if the client used CSV.\n\n**The COPY protocol does not require immediate confirmation when sending a tuple**, which means we can continue parsing the next tuple without waiting for the previous tuple to be fully processed. This creates nice pipelining behaviour where tuples are effectively ingested in parallel and can improve performance over regular PostgreSQL, even though parsing runs at the same speed. This effect will be more pronounced when insertions are relatively heavy-weight due to triggers or heavy indexes.\n\n**COPY does not always use a connection per shard.** If there were already writes to multiple shards on a specific connection earlier in the transaction (e.g. consecutive inserts), then that connection must be used for the writes done by the COPY (e.g. to be able to check unique constraints). However, we can only COPY into one table at a time. In this case, the COPY logic maps multiple shards to the same connection and switches back-and-forth between shards through multiple COPY commands (which has overhead). If we get a tuple for a shard that is currently active, we forward immediately over the connection. Otherwise, we add the tuple to a per-shard buffer, or switch the connection if we already sent `citus.copy_switchover_threshold` bytes to the current shard.\n\nThere is a caveat in the current COPY logic. Citus always uses non-blocking I/O, which means libpq keeps outgoing bytes in a buffer when the socket is busy. We only run the relevant libpq functions to flush the per-connection libpq buffer (or flush the per-shard buffer) when there is a tuple for a particular connection, or when reaching the end of the stream. Ideally, we keep getting tuples for all the different shards, such that all connections are flushed. In some cases, when many consecutive tuples are for the same shard, a large amount of data can remain buffered on the coordinator in libpq or the per-shard buffer when we come to the end of the stream (CitusCopyDestReceiverShutdown). The connections and per-shard buffers are then flushed one by one. There is room for optimization where the COPY loop and final flush behave more like the adaptive executor and uses a WaitEventSet to check for I/O events on all the sockets, and flush the libpq buffer.\n\nAnother (smaller) caveat is that the libpq buffer can fill up if the outgoing connection to the worker cannot keep up with the rate at which the coordinator is receiving and parsing tuples. To bound the size of the buffer and thereby avoid running out of memory, we force a flush on a connection after every `citus.remote_copy_flush_threshold` bytes that are written to a connection. We do this regardless of whether the libpq buffer is becoming large, because we do not have direct insight into its current size. Fortunately, it will only cause a very short pause if the buffer is not large or empty.\n\nFor local shards, COPY can also use local execution. We use local execution by default in transaction blocks, but try to use connections to the local node for a single statement COPY because we get more parallelism benefits.\n\n## COPY .. TO command\n\nThe COPY .. TO command is used to dump the data from a table, or to get the output of a query in CSV format. The COPY (SELECT ..) TO syntax does not use any special logic. PostgreSQL’s implementation will plan and execute the query in the usual way, and Citus will intercept queries on distributed tables. That means these commands do not use COPY internally to query the shards. Instead, the results of the query are buffered in a tuple store and then converted to CSV.\n\nThe COPY distributed_table TO .. syntax will typically return a lot of data and buffering it all in a tuplestore would cause issues. Therefore, Citus uses the process utility hook to propagate the COPY distributed_table TO .. command to each shard one by one. The output is forwarded directly to the client. If the user asked for a header, it is only requested from the first shard to avoid repeating it for each shard.\n\n## INSERT..SELECT\n\nThe INSERT.. SELECT command inserts the result of a SELECT query into a target table. In real-time analytics use cases, INSERT..SELECT enables transformation of an incoming stream of data inside the database. A typical example is maintaining a rollup table or converting raw data into a more structured form and adding indexes.\n\n<img alt=\"INSERT..SELECT modes\" src=\"../../../images/insert-select-modes.png\" width=\"700\">\n\nCitus has three different methods of handling INSERT..SELECT commands that insert into a distributed table as shown in the figure above. We identify these methods as: (1) co-located, where shards for the source and destination tables are co-located; (2) repartitioning, where the source and destination tables are not co-located and the operation requires a distributed reshuffle; and (3) pull to coordinator, where neither of the previous two methods can be applied. These three approaches can process around 100M, 10M, and 1M rows per second, respectively, in a single command.\n\nCo-located INSERT..SELECT is executed in a similar fashion to multi-shard update/delete commands. There is a single list of tasks with one task for each shard group, which runs via the adaptive executor.\n\nINSERT..SELECT with re-partitioning is architecturally similar to re-partition joins, but it goes via separate code path and uses more optimizations. Empty files are skipped and files traveling between the same pair of nodes are batched in a single call to fetch_intermediate_results, which saves round trips. The final step in INSERT..SELECT with re-partitioning runs queries like INSERT INTO dist_table SELECT .. FROM read_intermediate_result(…) with optional ON CONFLICT and RETURNING clauses. In principle, we could do an additional GROUP BY in the final step when grouping by the target distribution column, but that is not currently implemented and instead falls back to pull to coordinator.\n\nINSERT..SELECT via the coordinator logic uses the COPY code path to write results of an arbitrary SELECT  into multiple shards at the same time. In case of ON CONFLICT or RETURNING, they are first written to intermediate results that are co-located with the destination shards. Then a co-located INSERT..SELECT between the intermediate results and the target shards is performed, similar to the final step of re-partitioning.\n\n## Merge command\n\nMerge command the same principles as INSERT .. SELECT processing. However, due to the nature of distributed systems, there are few more additional limitations on top of the INSERT .. SELECT processing. The [MERGE blog post](https://www.citusdata.com/blog/2023/07/27/how-citus-12-supports-postgres-merge/) dives deep on this topic.\n\n# DDL\n\nDDL commands are primarily handled via the citus_ProcessUtility hook, which gets the parse tree of the DDL command. For supported DDL commands, we always follow the same sequence of steps:\n\n1. Qualify the table names in the parse tree (simplifies deparsing, avoids sensitivity to search_path changes)\n2. Pre-process logic\n3. Call original previous ProcessUtility to execute the command on the local shell table\n4. Post-process logic\n5. Execute command on all other nodes\n6. Execute command on shards (in case of table DDL)\n\nEither the pre-process or post-process step generates a \"Distributed DDL Job\", which contains a task list to run in steps 4 & 5 (via adaptive executor).\n\nIn general pre-process should:\n\n- Acquire any locks that are needed beyond the ones PostgreSQL will acquire in step 3\n- Perform upfront error checks (e.g. is this unique constrained allowed on a distributed table?)\n\nPost-process should:\n\n- Ensure dependencies of the current object exist on worker nodes (e.g. types used in parameters when creating a function)\n- Deparse the DDL parse tree to a string\n- Generate a task list using the deparsed DDL command\n\nThe reason for handling dependencies and deparsing in post-process step is that in case of a CREATE/ALTER, the object exist in its intended form at that point. In case of a DROP, the opposite is true and the pre-process should be used. Most commands have either a pre-process or post-process function. We have not been particularly careful about defining what should be done in pre-process vs. post-process, so the steps are not always the same across different commands.\n\nNot all table DDL is currently deparsed. In that case, the original command sent by the client is used. That is a shortcoming in our DDL logic that causes user-facing issues and should be addressed. We do not directly construct a separate DDL command for each shard. Instead, we call the `worker_apply_shard_ddl_command(shardid bigint, ddl_command text)` function which parses the DDL command, replaces the table names with shard names in the parse tree according to the shard ID, and then executes the command. That also has some shortcomings, because we cannot support more complex DDL commands in this manner (e.g. adding multiple foreign keys). Ideally, all DDL would be deparsed, and for table DDL the deparsed query string would have shard names, similar to regular queries.\n\n## Defining a new DDL command\n\nAll commands that are propagated by Citus should be defined in DistributeObjectOps struct. Below is a sample DistributeObjectOps for ALTER DATABASE command that is defined in [distribute_object_ops.c](commands/distribute_object_ops.c) file.\n\n```c\nstatic DistributeObjectOps Database_Alter = {\n\t.deparse = DeparseAlterDatabaseStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDatabaseStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_DATABASE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\n```\n\nEach field in the struct is documented in the comments within the `DistributeObjectOps`. When defining a new DDL command, follow these guidelines:\n\n- **Returning tasks for `preprocess` and `postprocess`**: Ensure that either `preprocess` or `postprocess` returns a list of \"DDLJob\"s. If both functions return non-empty lists, then you would get an assertion failure.\n\n- **Generic `preprocess` and `postprocess` methods**: The generic methods, `PreprocessAlterDistributedObjectStmt` and `PostprocessAlterDistributedObjectStmt`, serve as generic pre and post methods utilized for various statements. Both of these methods find application in distributed object operations.\n\n  - The `PreprocessAlterDistributedObjectStmt` method carries out the following operations:\n    - Performs a qualification operation.\n    - Deparses the statement and generates a task list.\n\n  - As for the `PostprocessAlterDistributedObjectStmt` method, it:\n    - Invokes the `EnsureAllObjectDependenciesExistOnAllNodes` function to propagate missing dependencies, both on the coordinator and the worker.\n\n  - Before defining new `preprocess` or `postprocess` methods, it is advisable to assess whether the generic methods can be employed in your specific case.\n\n\n- **`deparse`**: When propagating the command to worker nodes, make sure to define `deparse`. This is necessary because it generates a query string for each worker node.\n\n- **`markDistributed`**: Set this flag to true if you want to add a record to the `pg_dist_object` table. This is particularly important for `CREATE` statements when introducing a new object to the system.\n\n- **`address`**: If `markDistributed` is set to true, you must define the `address`. Failure to do so will result in a runtime error. The `address` is required to identify the fields that will be stored in the `pg_dist_object` table.\n\n- **`markDistributed` usage in `DROP` Statements**: Please note that `markDistributed` does not apply to `DROP` statements. For `DROP` statements, instead you need to call `UnmarkObjectDistributed()` for the object either in `preprocess` or `postprocess`. Otherwise, state records in ``pg_dist_object`` table will cause errors in UDF calls such as ``citus_add_node()``, which will try to copy the non-existent db object.\n\n- **`qualify`**: The `qualify` function is used to qualify the objects based on their schemas in the parse tree. It is employed to prevent sensitivity to changes in the `search_path` on worker nodes. Note that it is not mandatory to define this function for all DDL commands. It is only required for commands that involve objects that are bound to schemas, such as; tables, types, functions and so on.\n\nAfter defining the `DistributeObjectOps` structure, this structure should be implemented in the `GetDistributeObjectOps()` function as shown below:\n\n```c\n// Example implementation in C code\nconst DistributeObjectOps *\nGetDistributeObjectOps(Node *node)\n{\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_AlterDatabaseStmt:\n\t\t{\n\t\t\treturn &Database_Alter;\n\t\t}\n...\n```\n\nFinally, when adding support for propagation of a new DDL command, you also need to make sure that:\n* Use `quote_identifier()` or `quote_literal_cstr()` for the fields that might need escaping some characters or bare quotes when deparsing a DDL command.\n* The code is tolerant to nullable fields within given `Stmt *` object, i.e., the ones that Postgres allows not specifying at all.\n* You register the object into `pg_dist_object` if it's a CREATE command and you delete the object from `pg_dist_object` if it's a DROP command.\n* Node activation (e.g., `citus_add_node()`) properly propagates the object and its dependencies to new nodes.\n* Add tests cases for all the scenarios noted above.\n* Add test cases for different options that can be specified for the settings. For example, `CREATE DATABASE .. IS_TEMPLATE = TRUE` and `CREATE DATABASE .. IS_TEMPLATE = FALSE` should be tested separately.\n\n## Object & dependency propagation\n\nThese two topics are closely related, so we'll discuss them together. You can start the topic by reading [Nils' blog](https://www.citusdata.com/blog/2020/06/25/using-custom-types-with-citus-and-postgres/) on the topic.\n\n### The concept of \"Dependency\" for Postgres/Citus\n\nStarting with the basics, Postgres already understands object dependencies. For instance, it won't allow you to execute `DROP SCHEMA` without the `CASCADE` option if tables exist within the schema. In this case, the table is a `dependent object`, and the schema serves as the `referenced object`.\n\n```sql\nCREATE SCHEMA sc1;\nCREATE TABLE sc1.test(a int);\nDROP SCHEMA sc1;\nERROR:  cannot drop schema sc1 because other objects depend on it\nDETAIL:  table sc1.test depends on schema sc1\nHINT:  Use DROP ... CASCADE to drop the dependent objects too.\n```\n\nThe information about these dependencies is stored in a specific Postgres catalog table, known as [`pg_depend`](https://www.postgresql.org/docs/current/catalog-pg-depend.html). You can inspect the aforementioned dependency within this catalog using the following query:\n\n```sql\nSELECT\n    pg_identify_object_as_address(classid, objid, objsubid) as dependent_object,\n    pg_identify_object_as_address(refclassid, refobjid, refobjsubid) as referenced_object\nFROM\n    pg_depend\nWHERE\n    (classid, objid, objsubid)\n        IN\n    (SELECT classid, objid, objsubid FROM pg_get_object_address('table', '{sc1,test}', '{}'));\n\n┌─────────────────────────┬───────────────────┐\n│    dependent_object     │ referenced_object │\n├─────────────────────────┼───────────────────┤\n│ (table,\"{sc1,test}\",{}) │ (schema,{sc1},{}) │\n└─────────────────────────┴───────────────────┘\n(1 row)\n```\n\n### Citus' Approach to Object Creation and Dependency Tracking: `pg_dist_object`\n\nCitus employs its own catalog table called `pg_dist_object`. This table keeps records of all objects that need to be created on every node in the cluster. These objects are commonly referred to as `Distributed Objects`.\n\nWhen adding a new node to the cluster using `citus_add_node()`, Citus must ensure the creation of all dependent objects even before moving data to the new node. For instance, if a table relies on a custom type or an extension, these objects need to be created before any table is set up. In short, Citus is responsible for setting up all the dependent objects related to the tables.\n\nSimilarly, when creating a new Citus table, Citus must confirm that all dependent objects, such as custom types, already exist before the shell table or shards are set up on the worker nodes. Note that this applies not just to tables; all distributed objects follow the same pattern.\n\nHere is a brief overview of `pg_dist_object`, which has a similar structure to `pg_depend` in terms of overlapping columns like `classid, objid, objsubid`:\n\n```sql\nCREATE SCHEMA sc1;\nCREATE TABLE sc1.test(a int);\nSELECT create_distributed_table('sc1.test', 'a');\n\nSELECT\n    pg_identify_object_as_address(classid, objid, objsubid) as distributed_object\nFROM\n    pg_dist_object;\n┌─────────────────────────────┐\n│     distributed_object      │\n├─────────────────────────────┤\n│ (role,{onderkalaci},{})     │\n│ (database,{onderkalaci},{}) │\n│ (schema,{sc1},{})           │\n│ (table,\"{sc1,test}\",{})     │\n└─────────────────────────────┘\n(4 rows)\n```\n\n\n### When Is `pg_dist_object` Populated?\n\nGenerally, the process is straightforward: When a new object is created, Citus adds a record to `pg_dist_object`. The C functions responsible for this are `MarkObjectDistributed()` and `MarkObjectDistributedViaSuperuser()`. We'll discuss the difference between them in the next section.\n\nCitus employs a universal strategy for dealing with objects. Every object creation, alteration, or deletion event (like custom types, tables, or extensions) is represented by the C struct `DistributeObjectOps`. You can find a list of all supported object types in [`distribute_object_ops.c`](https://github.com/citusdata/citus/blob/2c190d068918d1c457894adf97f550e5b3739184/src/backend/distributed/commands/distribute_object_ops.c#L4). As of Citus 12.1, most Postgres objects are supported, although there are a few exceptions.\n\nWhenever `DistributeObjectOps->markDistributed` is set to true—usually during `CREATE` operations—Citus calls `MarkObjectDistributed()`. Citus also labels the same objects as distributed across all nodes via the `citus_internal.add_object_metadata()` UDF.\n\nHere's a simple example:\n\n```sql\n-- Citus automatically creates the object on all nodes\nCREATE TYPE type_test AS (a int, b int);\n...\nNOTICE:  issuing SELECT worker_create_or_replace_object('CREATE TYPE public.type_test AS (a integer, b integer);');\n....\n WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation)  AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal.add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data;\n ...\n\n-- Then, check pg_dist_object. This should be consistent across all nodes.\nSELECT\n    pg_identify_object_as_address(classid, objid, objsubid) as distributed_object\nFROM\n    pg_dist_object\nWHERE\n    (classid, objid, objsubid)\n        IN\n    (SELECT classid, objid, objsubid FROM pg_get_object_address('type', '{type_test}', '{}'));\n┌──────────────────────────────┐\n│      distributed_object      │\n├──────────────────────────────┤\n│ (type,{public.type_test},{}) │\n└──────────────────────────────┘\n(1 row)\n```\n\nIn rare cases, `pg_dist_object` is updated during Citus version upgrades. If you upgrade Citus from `version X` to `version Y`, and a certain object becomes a supported distributed object in `version Y` but wasn't in `version X`, Citus marks it as such during the `ALTER EXTENSION citus` command. The details can be found in the C function `PostprocessAlterExtensionCitusUpdateStmt()`.\n\n### How Are `pg_dist_object` and `pg_depend` Related?\n\nIn the prior sections, we focused on standalone objects, but in reality, most objects have dependencies. That's where `pg_depend` and also `pg_shdepend` become crucial. Any mention of `pg_depend` in this section also applies to `pg_shdepend`.\n\nWhen Citus creates an object, it scans the `pg_depend` table to identify all dependencies and then automatically generates these dependencies as distributed objects.\n\nThe core C function in this process is `EnsureDependenciesExistOnAllNodes()`. This function takes the object as an argument and conducts a depth-first search (DFS) on `pg_depend` and `pg_shdepend` tables. The DFS sequence is crucial because dependencies must be established in a specific order. For instance, if a table depends on a type `t1`, and `t1` relies on another type `t2`, then `t2` must be created before `t1`. The DFS ensures that this order is maintained.\n\nIf Citus encounters a dependency it can't support, it will throw an error instead of silently allowing it. The rationale behind this approach is to avoid subtle issues later, especially when adding new nodes. For instance, Citus currently throws an error for circular dependencies. The main function performing these checks is `EnsureDependenciesCanBeDistributed()`.\n\nDuring the DFS, Citus might need to extend its search, especially for complex dependencies like array types that don't have a straightforward dependency on their element types. Citus expands the traversal to account for such cases. The main function responsible for this is `ExpandCitusSupportedTypes()`, which has extensive comments explaining the specific rules.\n\n### Current User vs `superuser` for Object Propagation:\n\nThe difference between `MarkObjectDistributed()` and `MarkObjectDistributedViaSuperuser()` is important here. Generally, Citus tries to minimize the use of `superuser` operations for security reasons. However, there are cases where it's necessary. We employ `superuser` permissions primarily when marking the dependencies of an object we are working on. This is because creating dependencies might require higher-level privileges that the current user might not have. For example, if a schema depends on a role, and the current user doesn't have the privilege to create roles, an error will be thrown. To avoid this, we use `superuser` for creating dependencies.\n\nHowever, there's an exception. If the dependency is created within the same transaction, we use the current user. This prevents visibility issues and is mainly relevant for `serial` columns. More details can be found in [Citus GitHub PR 7028](https://github.com/citusdata/citus/pull/7028).\n\n### When Are the Objects in `pg_dist_object` Used?\n\nThere are three main scenarios:\n\n+ When adding a new node—or more precisely, activating it—Citus reads all objects listed in `pg_dist_object`, sorts them by dependency, and then creates those objects on the new node. The core C function for this is `SendDependencyCreationCommands()`, and the sorting is done by `OrderObjectAddressListInDependencyOrder()`.\n\n+ When Citus creates a new object and processes its dependencies, any dependencies already marked as distributed are skipped. This is handled in `FollowNewSupportedDependencies()`, where dependencies are bypassed if `IsAnyObjectDistributed()` returns true.\n\n+ When user modifies an object, Citus acts only when the object is distributed. For non-distributed object, Citus gives the control back to Postgres.\n\n## Foreign keys\n\nCitus relies fully on Postgres to enforce foreign keys. To provide that, Citus requires the relevant shards to be colocated. That’s also why the foreign keys between distributed tables should always include the distribution key. When reference tables / citus local tables involved, Citus can relax some of the restrictions. [Onder’s talk Demystifying Postgres Foreign Key Constraints on Citus](https://www.youtube.com/watch?v=xReWGcSg7sc) at CitusCon discusses all the supported foreign key combinations within Citus.\n\nThere is one tricky behavior regarding transactions when there is a foreign key from a distributed table to a reference table. If a statement in a transaction modifies the reference table, then Postgres acquires row locks on the referencing tables (e.g., shards of the distributed table) within the internal connection that modified the reference table. After that point, Citus cannot access the shards of the distributed table in parallel anymore. Otherwise, the multiple internal connections that would be opened via parallel command might compete to acquire the same locks, leading to a (self) distributed deadlock. To prevent these scenarios, Citus switches to sequential execution. The relevant function is `RecordParallelRelationAccessForTaskList()`, which documents the possible scenarios. The regression test file [foreign_key_restriction_enforcement](https://github.com/citusdata/citus/blob/2c190d068918d1c457894adf97f550e5b3739184/src/test/regress/sql/foreign_key_restriction_enforcement.sql) has lots of nice examples of this behavior.\n\n\n## DROP TABLE\n\nCitus' handling of `DROP TABLE` is slightly different than other DDL operations. In this section, we aim to highlight the key differences and their reasoning.\n\nCitus implements an event trigger, [`citus_drop_trigger()`](https://github.com/citusdata/citus/blob/main/src/backend/distributed/sql/udfs/citus_drop_trigger/latest.sql). The trigger is defined as:\n```sql\n select * from pg_event_trigger ;\n┌───────┬────────────────────────────┬──────────┬──────────┬─────────┬────────────┬─────────┐\n│  oid  │          evtname           │ evtevent │ evtowner │ evtfoid │ evtenabled │ evttags │\n├───────┼────────────────────────────┼──────────┼──────────┼─────────┼────────────┼─────────┤\n│ 16676 │ citus_cascade_to_partition │ sql_drop │       10 │   16675 │ O          │         │\n└───────┴────────────────────────────┴──────────┴──────────┴─────────┴────────────┴─────────┘\n(1 row)\n```\n\nThe drop trigger proves useful for capturing all tables affected by the `CASCADE` operation. For instance, if you delete a parent table, Postgres will automatically execute a `DROP TABLE` command for its partitions. Citus can then seamlessly apply the same operation to all cascaded tables, eliminating the need for manual identification of tables that would be affected by the cascade.\n\nAnother reason for utilizing a trigger for `DROP` processing is that after executing `standardProcess_Utility`, the `oid` of the table being dropped is eliminated from Postgres' catalog tables. This makes it more challenging to manage a dropped table in `PostProcessUtility`, as is customary for many `DDL` commands. Instead, we depend on the event trigger to supply the `oid` of the table that has been dropped. This allows us to delete all related metadata, such as entries in `pg_dist_partition` or `pg_dist_shard` from Citus' catalog tables. Additionally, we eliminate all relevant metadata from every node in the cluster. Ultimately, this enables us to remove the shard placements linked to the dropped Citus table.\n\nAlso, if we were to rely on `standardProcess_Utility`, we'd need to handle all sorts of `DROP` commands that could cascade into `DROP TABLE`. With drop trigger, Postgres handles that and calls Citus' drop trigger.\n\nGenerally speaking, there isn't a compelling reason to avoid using `PostProcessUtility` for managing `DROP TABLE` commands. Theoretically, one could implement all the same logic within `PostProcessUtility`. However, the drop trigger offers a convenient approach.\n\n Additional functionalities are present in [`PreprocessDropTableStmt()`](https://github.com/citusdata/citus/blob/c323f49e8378b5e8ce95457c845659b5fc14ccb1/src/backend/distributed/commands/table.c#L146), particularly concerning the handling of partitioned tables and colocation locking. These aspects are well-documented in the code, so for further details, please refer to the documentation there.\n\n# Connection management\n\nEach client session makes “internal” connections to other nodes in the cluster. Connection management is an important part of our overall execution logic. The design largely comes from the need to achieve a few different goals:\n\n- Cache and reuse connections for low latency.\n- Parallelize commands over multiple connections per node, to use multiple cores.\n- Multi-statement transactions have locks and uncommitted state that is only visible over a particular connection. We therefore need to make sure that:\n  - After a write to a shard, any access to that shard group should use the same connection as the write. We need to cover the whole shard group because writes and locks can cascade to other shards in the shard group via foreign keys, and they might be used together in a join.\n  - After a write to a reference tables, any subsequent read of a reference table, including joins between distributed table shards and reference tables, should use the same connection as the write.\n- Metadata and global object changes should always use the same connection.\n- We should not overload worker nodes with parallel connections.\n\nIn some cases, these goals conflict. For instance, if a multi-statement transaction performs a parallel delete on a distributed table, and then inserts into a reference table, and then attempts to join the distributed table with the reference table, then there is no way to complete that transaction correctly, since there is no single connection that can see both the reference table update and all the updates to distributed table shards. The command that reaches the conflict will error out:\n\n```sql\n-- may fail if delete is parallelized\nbegin;\ndelete from dist_table;\ninsert into reference_table values (1,2);\nselect * from dist_table join reference_table on (x = a);\nERROR:  cannot perform query with placements that were modified over multiple connections\nabort;\n\n```\n\nThe workaround is to `set citus.multi_shard_modify_mode TO 'sequential';` before or at the start of the transaction, which forces the delete (multi-shard modification) command to use a single connection, such that the insert and select can use the same connection.\n\nThe primary goal of the connection management layer is not to solve all these problems, but to detect them and prevent any form of incorrectness, such as not seeing preceding changes in the transaction and self-deadlocks. A lot of important error-checking logic lives in FindPlacementListConnection, which attempts to find a suitable connection given a list of shard placements out of the connections that are already open, and also checks if the intent of the caller would lead to a conflict.\n\nThe connection management logic is divided into two parts:\n\n- **connection_management.c** tracks all connections for various purposes and concerns itself with connection establishment, caching, and error handling.\n- **placement_connections.c** concerns itself with finding the right connection for a given shard placement access based on preceding commands in the transaction.\n\n## Connection management\n\nConnection management tracks all the connections made by the current backend to other worker nodes. The connections can exist for the lifetime of the transaction, or longer when they are cached. The connections are kept in a hash that is keyed by hostname, port, user, database, and replication (yes/no). Each hash entry has a list of connections, since there can be multiple when the executor decides to parallelize a multi-shard query.\n\nCitus operations that need a connection call `StartNodeUserDatabaseConnection` (or a wrapper), which either returns an existing connection or a new one. the caller should wait for the connection to be fully established.\n\nWhen a Citus operation needs a connection to a worker node (hostname, port, user, database, replication), it can ask for it in a few different ways via flags:\n\n- Pick any connection (default), open a new one if none exists\n- Force a new connection (FORCE_NEW_CONNECTION), even if connections already exist\n- Pick a connection outside of a transaction (OUTSIDE_TRANSACTION), or open a new one if none exists\n- Pick the connection used for metadata syncing (REQUIRE_METADATA_CONNECTION), or open a new one if none exists and mark it for metadata syncing\n\nIn addition, the caller can claim a connection exclusively, in which case it will not be returned until it is unclaimed (or transaction end). For instance, the adaptive executor claims connections it uses exclusively. When it calls `StartNodeUserDatabaseConnection` again, it will always get a new connection that it can use to parallelize the query.\n\nIt is important that global commands like creating a type, or a function, or changing Citus metadata, all use the same connection. Otherwise, we might end up creating a type over one connection, and a function that depends on it over another. The use of the REQUIRE_METADATA_CONNECTION flag prevents this.\n\nThe FORCE_NEW_CONNECTION and OUTSIDE_TRANSACTION flags can BOTH be used to execute (and commit) commands outside of the current transaction. Many usages of the older FORCE_NEW_CONNECTION flag could perhaps be replaced by OUTSIDE_TRANSACTION. A benefit of FORCE_NEW_CONNECTION is that it can provide a more intuitive way to parallelize commands than claiming connections exclusively. For instance, the `run_command_on_shards` uses FORCE_NEW_CONNECTION for this purpose.\n\nIt is worth noting that Citus currently always opens a new connection when switching to a different user (e.g. via SET ROLE), rather than propagating the SET ROLE command. That can lead to some inconsistent behaviour (e.g. cannot see uncommitted writes after SET ROLE).\n\n## Placement connection tracking\n\nThe placement connection tracking logic stores which shard group placements were accessed over which connections during the current transactions, and whether they performed a SELECT, DML, or DDL. It considers whether to use same connection for accesses to the same shard group placement in the following cases:\n\n- SELECT after SELECT - can use different connection\n- DML after SELECT – can use different connection\n- All other cases – must use same connection\n\nThe key function that deals with this logic is `FindPlacementListConnection` in [placement_connection.c](/src/backend/distributed/connection/placement_connection.c), which is called via `GetConnectionIfPlacementAccessedInXact` by the adaptive executor.\n\nWe sometimes allow the same shard group placement to be accessed from different connections (first two cases). Consider a transaction that does a query on a reference table followed by a join between a distributed table and a reference table. Currently Citus would parallelize the second query, but that implicitly causes the reference table to be accessed from multiple connections. After that, we can still perform writes on the reference table (second case), because they do not conflict with the reads. However, we cannot perform most DDL commands involving the reference table because the locks would conflict with the reads, such that it would self-deadlock (blocked waiting for itself). We throw an error to prevent the self-deadlock and suggest set citus.multi_shard_modify_mode is ‘sequential’. Probably some DDL commands that take weaker locks would still be permissible, but we currently treat them all the same way.\n\nA downside of the current placement connection tracking logic is that it does not consider foreign keys to reference tables, and the fact that writes and locks can cascade from a write to a reference table. We have a separate subsystem for error checking those scenarios (relation_access_tracking.c), but it would be nice if they can be unified.\n\n## citus.max_cached_connections_per_worker\n\nAn important part of the connection management is caching at least 1 outgoing connection per worker node in the session. Establishing a new connection for every query is quite expensive due to SSL establishment, forking a process on the worker node, and rebuilding caches. Transactional workloads that have a high rate of short-running queries benefit a lot from caching connections. For analytical queries that take hundreds of milliseconds or more, the relative benefit is smaller, but often still noticeable.\n\nAt the end of a transaction, the connection management logic decides which connections to keep. It keeps at most `citus.max_cached_connections_per_worker` regular connections that are in a healthy state, unless they are open for more than `citus.max_cached_connection_lifetime` (10 minutes by default). For workloads with a high rate of multi-shard queries, it can be beneficial to increase `citus.max_cached_connections_per_worker`.\n\n## citus.max_shared_pool_size\n\n**The citus.max_shared_pool_size setting can be used to limit the number of outgoing connections across processes **. Each session has its own set of connections to other nodes. We often make multiple connections to the same worker node from the same session to parallelize analytical queries, but if all session are doing that we might overload the worker nodes with connections. That is prevented by setting citus.max_shared_pool_size, which should be at least `citus.max_client_connections` on coordinator node, and at most `max_connections - citus.max_client_connections` on worker node.\n\nThe principle behind `citus.max_shared_pool_size` is that under high concurrency (all client connections used) it converges to each process having 1 connection per node. To do so, we distinguish between “optional” and “required” connections. When the executor asks the connection management layer for a connection, the first connection to a node is always required, and other connections are optional. If all connection slots are in use, the connection manager blocks until one is available when asking for a required connection, or returns NULL when asking for an optional connection. That signals to the executor that it cannot currently expand its pool. It may try again later. Most Citus code paths are tweaked to be able to complete their operation with 1 connection per node, and use local execution for local shards.\n\nNote that `citus.max_shared_pool_size` can only control the number of outgoing connections on a single node. When there are many nodes, the number of possible inbound internal connections is the sum of the `citus.max_shared_pool_size` on all other nodes. To ensure this does not exceed max_connections, we recommend that `sum(citus.max_client_connections) < max_connections`.\n\n# Transactions (2PC)\n\nCitus uses the transaction callbacks in PostgreSQL for pre-commit, post-commit, and abort to implement distributed transactions. In general, distributed transactions comprise a transaction on the coordinator and one or more transactions on worker nodes. For transactions that only involve a single worker node, Citus delegates responsibility to the worker node. For transactions that involve multiple nodes, Citus uses two-phase commit for atomicity and implements distributed deadlock detection.\n\n## Single-node transactions\n\nMost multi-tenant and high-performance CRUD workloads only involve transactions that access a single worker node (or rather, a single shard group). For example, multi-tenant applications typically distribute and co-locate tables by tenant and transactions typically only involve a single tenant. When all statements in a transaction are routed to the same worker node, the coordinator simply sends commit/abort commands to that worker node from the commit/abort callbacks. In this case, the transaction is effectively delegated to that worker node. The worker node, by definition, provides the same guarantees as a single PostgreSQL server.\n\n## Multi-node transactions\n\nFor transactions that write to multiple nodes, Citus uses the built-in two-phase commit (2PC) machinery in PostgreSQL. In the pre-commit callback, a “prepare transaction” command is sent over all connections to worker nodes with open transaction blocks, then a commit record is stored on the coordinator. In the post-commit callback, “commit prepared” commands are sent to commit on the worker nodes. The maintenance daemon takes care of recovering failed 2PC transactions by comparing the commit records on the coordinator to the list of pending prepared transactions on the worker. The presence of a record implies the transaction was committed, while the absence implies it was aborted. Pending prepared transactions are moved forward accordingly.\n\n<img alt=\"2PC recovery\" src=\"../../../images/2pc-recovery.png\" width=\"700\">\n\nNice animation at: [How Citus Executes Distributed Transactions on Postgres](https://www.citusdata.com/blog/2017/11/22/how-citus-executes-distributed-transactions/)\n\n## No distributed snapshot isolation\n\nMulti-node transactions provide atomicity, consistency, and durability guarantees. Since the prepared transactions commit at different times, they do not provide distributed snapshot isolation guarantees.\n\nAn example anomaly that can occur is two distributed transactions:\n\nTwo inserts in a transaction block into two different shards\n\n```sql\nBEGIN;\nINSERT INTO test (key, value) VALUES (1,2);\nINSERT INTO test (key, value) VALUES (2,2);\nEND;\n```\n\nAn update across shards\n\n```sql\nUPDATE test SET value = 3 WHERE value = 2;\n```\n\nAn update across shards\n```sql\nUPDATE test SET value = 3 WHERE value = 2;\n```\nIf Citus provided serializability, there could only be 2 outcomes (a happens first or b happens first). However, it can have at least 4 outcomes, because the update depends on the inserts, and it might see only one of the insert as committed.\n\nThis can happen because the inserts commit using a 2PC if the shards are on different nodes, and therefore they might not become visible at exactly the same time. Since the commits happen in parallel, there are no guarantees w.r.t. which insert becomes visible first. The update could see either insert as committed, or none, or both, depending on exact timings. Hence, there is no well-defined order between a and b, theye are intertwined.\n\nIf the inserts depend on the update, there may be even more possible outcomes. For instance, if there is a unique constraint on (key, value), and we do upserts concurrently with the multi-shard update:\n\n```sql\nBEGIN;\nINSERT INTO test (key, value) VALUES (1,2) ON CONFLICT DO NOTHING;\nINSERT INTO test (key, value) VALUES (2,2) ON CONFLICT DO NOTHING;\nEND;\n```\n\nNow, whether the insert proceeds or does nothing depends on whether the update is already committed or not. Hence, this scenario has 6 possible outcomes.\n\nIt is hard for users to understand these semantics and their implications. Therefore, many database researchers and engineers have a strong preference for serializability. Having fewer possible outcomes means less potential for bugs and unintended situations. On the other hand, the performance impacts of snapshot isolation are generally significant, and we have not seen a lot of problems due to the lack of snapshot isolation in practice. The types of transactional workloads that scale well and therefore benefit from Citus are the types of workloads that scope their transactions to a single node and therefore get all the usual PostgreSQL guarantees.\n\nOur long-term goal is to provide snapshot isolation as an optional feature, with at least read committed guarantees (default in PostgreSQL).\n\n\n\n## Distributed Deadlocks\n\nDeadlocks are inevitable in a database system which supports transactions, and Citus is no exception. Marco wrote a useful blog post on what are locks & deadlocks & distributed deadlocks, please read the blog post first: https://www.citusdata.com/blog/2017/08/31/databases-and-distributed-deadlocks-a-faq/ Another good introduction for distributed deadlocks can be found here: https://www.citusdata.com/blog/2017/11/22/how-citus-executes-distributed-transactions/\n\n\n\nAt a high-level, the applications should try to avoid (distributed) deadlocks. The application should avoid patterns that could cause deadlocks. If those patterns are unavailable, then the database can still resolve the deadlocks.\n\n\n\n### Distributed Deadlock Detection Implementation\n\nCitus heavily relies on PostgreSQL’s internal locking infrastructure for detecting distributed deadlocks. The entry function for the distributed deadlock detection is ` CheckForDistributedDeadlocks ()`. Distributed deadlock detection runs in the background as part of maintenance daemon.\n\nAt a high level, Citus assigns “distributed transaction ids” for all backends running distributed transactions that might be part of a distributed deadlock (e.g., BEGIN; command;    or any multi-shard command). See `assign_distributed_transaction()` function:\n```sql\n\nBEGIN\n\nDELETE FROM test WHERE a = 1;\n\nNOTICE:  issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_distributed_transaction_id(0, 5, '2023-09-14 17:39:21.263272+03');\n\n```\n\nThen, Citus periodically (default 2 seconds), pulls all the “lock graphs” from all the nodes and combines them. If it finds a cycle in the combined graph, Citus concludes that there is a deadlock and kills one of the (distributed) processes involved in the graph. To give an example, assume we have the following case:\n\n+ Transaction 1 waits Transaction 2 on Node-1\n+ Transaction 2 waits Transaction 1 on Node-2\n\nIn this case, none of the transactions can continue. This is a distributed deadlock as neither `Node-1` nor `Node-2` can detect the deadlock as there is no local deadlocks. To find distributed deadlocks, we pull the `lock graph`s from each node and combine.\n\nLet’s dive a little deeper. A node-lock lock graph is created in C function ` BuildLocalWaitGraph ()`. The lock graph includes only distributed transactions that are assigned via `assign_distributed_transaction` UDF. As noted, Citus heavily relies on Postgres on which backends are waiting for others, for details see ` AddEdgesForLockWaits ()` and  ` AddEdgesForWaitQueue ()` C functions.\n\nOnce each local lock graph is created, then the results are combined in on the coordinator. Then, for each node in the graph, we do a DFS (depth-first search) to check if there is a cycle involving that node. If there is a cycle, we conculde that there is a distributed deadlock.\n\nWhile doing the DFS, we also keep track of the other backends that are involved in the cycle. Citus picks the `youngest` transaction as the candidate to cancel (e.g., sends SIGINT). The idea is that let’s allow longer running transactions to continue, such as a long running DDL.\n\nIf there is a cycle in the local graph, typically Postgres’ deadlock detection kicks in before Citus’ deadlock detection, hence breaks the cycle. There is a safe race condition between Citus’ deadlock detection and Postgres’ deadlock detection. Even if the race happens, the worst-case scenario is that the multiple backends from the same cycle is cancelled. In practice, we do not see much, because Citus deadlock detection runs `2x` slower (e.g., `citus.distributed_deadlock_detection_factor`) than Postgres deadlock detection.\n\n<img alt=\"Deadlock detection\" src=\"../../../images/deadlock-detection.png\" width=\"700\">\n\nFor debugging purposes, you can enable logging with distributed deadlock detection: `citus.log_distributed_deadlock_detection`\n\nWith query from any node, we run the deadlock detection from all nodes. However, each node would only try to find deadlocks on the backends that are initiated on them. This helps to scale deadlock detection workload across all nodes.\n\nWhen there are too many active backends(>1000), creating lots of `waiting activity` (e.g., blocked on the same locks not necessarily deadlocks involved), then the deadlock detection process might become a bottleneck. There are probably some opportunities to optimize the code for these kinds of workloads. As a workaround, we suggest increase `citus.distributed_deadlock_detection_factor`.\n\nThe distributed transactionId and backend/PID mapping is done via BackendData structure. For every Postgres backend, Citus keeps a `BackendData` structure. Each backends state is preserved in `MyBackendData` C structure. Assigning (and removing) distributed transaction id to a backend means to update this structure.\n\nIf we were to implement distributed deadlock detection today, we would probably try to build it on top of `Global PID` concept instead of `distributed transaction id`. But, before changing that, we should make sure the `Global PID` is robust enough. `Global PID` today mostly used for observation of the cluster. We should slowly put more emphasis on the `Global PID` and once we feel confortable with it, we can consider using it for distributed deadlock detection as well.\n\n# Locking\nLocks in a database like Postgres (and Citus) make sure that only one user can change a piece of data at a time. This helps to keep the data correct and safe. If two users try to change the same data at the same time, it could create problems or errors.\n\nCitus, a distributed database, needs extra locks because it stores data across multiple servers. These extra locks help make sure that even when many servers are involved, the data stays correct and safe during changes made by multiple users.\n\nIn PostgreSQL and Citus, there are several types of locks that serve different purposes. In this section, we’d like to go over these different types of locks and explain when/how they are useful.\n\n## Lock Levels\n\nIn database management, locking mechanisms are crucial for maintaining data integrity during concurrent operations. However, not all locks are created equal. As a building block, PostgreSQL allows different levels/modes of locks. So, this is probably different than what you have learned in your college, where if a lock is held, others have to wait. No, in PostgreSQL, some locks do not conflict with each other, whereas some do. This flexibility allows Postgres (and Citus) to implement sophisticated concurrency scenarios.\n\nFor details of lock levels, please refer to PostgreSQL docs: https://www.postgresql.org/docs/current/explicit-locking.html\n\nUnderstanding these lock types and their levels of restrictiveness can help you better manage concurrent operations and ensure data integrity.\n\n## Lock Monitoring\n\nBoth PostgreSQL and Citus provide comprehensive views for monitoring the locks held (or waiting on) for each backend. `pg_locks`: https://www.postgresql.org/docs/current/view-pg-locks.html.\n\nIn Citus, we have the same view, but they are collected from all nodes in the cluster: `citus_locks`\n\nYou can find lots of examples of how `pg_locks` (and `citus_locks`) can be used in debugging systems. One of the good one is from PostgreSQL’s wiki, ` Сombination of blocked and blocking activity`: https://wiki.postgresql.org/wiki/Lock_Monitoring The same query is also implemented within Citus for the distributed cluster, with the name: citus_lock_waits: https://github.com/citusdata/citus/blob/4e46708789478d6deccd3d121f2b4da7f631ebe3/src/backend/distributed/sql/udfs/citus_lock_waits/11.0-1.sql#L4\n\nThese simple monitoring tools are essential to understanding the concurrency in PostgreSQL and Citus.\n\n## Lock Types\n\n1. **Table-level Locks**:\n\nTable-level locks in PostgreSQL range from less restrictive to more restrictive, allowing various levels of concurrent access. They can lock an entire table to ensure data integrity during operations like adding columns. You can also acquire any of these locks explicitly with the command LOCK. Two transactions cannot hold locks of conflicting modes on the same table at the same time. (However, a transaction never conflicts with itself. For example, it might acquire `ACCESS EXCLUSIVE` lock and later acquire `ACCESS SHARE` lock on the same table.)\n\nMarco’s blog post on locks could provide a nice read on this topic: https://www.citusdata.com/blog/2018/02/15/when-postgresql-blocks/\n\nAs a rule, in most cases, Citus relies on PostgreSQL to acquire the table-level locks. For example,\n\n+ When a DDL is executed on a Citus table, Citus first executes Postgres’ `standardProcess_Utility()` function. One of the key reasons behind that is Postgres acquires the table-level lock, and Citus provides similar concurrency behavior with Postgres on Citus tables. If an `ALTER TABLE .. ADD COLUMN` is running on a distributed table, no `SELECT` command could run concurrently due to the table-level locks.\n\n+ When regular commands like `INSERT`/`UPDATE`/`DELETE`/`SELECT` is executed, Citus again relies on Postgres to acquire the table-level locks. PostgreSQL acquires the table-level locks during the parsing of the statements, which makes life simple for Citus, as parsing happens even before any Citus logic kicks in. If the command doesn’t require parsing, such as prepared statements, then Postgres still acquires the same locks before using the cached plan. So, from Citus’ perspective, there mostly is nothing to do for acquiring the table-level locks.\n + + There is only a one expection to this rule, Citus' _local plan caching_. When Citus caches the queries by itself, Citus acquires the relevant table-level locks. See `ExecuteLocalTaskListExtended()` as the relevant C function.\n\n\nCitus additionally use table-level locks for certain table management operations on tables. With all these operations, Citus aims to fit into the same concurrency behaviors as Postgres. For example, when `create_distributed_table()` is executed, Citus acquires an `ExclusiveLock` on the table. We do that because we want to block `write`s on the tables – which acquire RowExclusiveLock  -- but let `read-only` queries to continue – which acquire AccessShareLock. An additional benefit of this approach is that no two concurrent `create_distributed_table` on the same table can run.\n\n\nOne another use case for table-level locks on Citus is the table-level locks acquired on the Citus metadata tables. Citus uses table-level locks on the metadata tables to define the concurrency behavior of certain operations. For example, while creating a new table or moving shards, it is common to acquire `ShareLock` on `pg_dist_node` table, and `citus_add_node` function to acquire `ExclusiveLock` on the same metadata table. The latter signals the rest of the backends that the node metadata is about to change, so it is not allowed to rely on the current state of `pg_dist_node` (or vice-versa `citus_add_node` should wait until rebalance finishes).\n\nThe main C function involved in table-level locking is ` LockRelationOid ()`. So, you can put a break-point to this function and see when/how Citus and Postgres acquires table-level locks.\n\n\n2. **Row-level Locks**:\n\nRow-level locks are more granular and lock only specific rows within a table. This allows multiple users to read and update different rows simultaneously, which can improve performance for multi-user scenarios.\n\n Citus does NOT involve in the row-level locks, fully relies on Postgres to acquire the locks on the shards. Marco’s blog-post gives a nice overview of row-level locks: https://www.citusdata.com/blog/2018/02/15/when-postgresql-blocks/\n\n\n\n3. **Advisory Locks:**\n\nAdvisory locks are a special type of lock in PostgreSQL that give you more control over database operations. Unlike standard table or row-level locks that automatically lock database objects, advisory locks serve as flexible markers or flags. Developers can implement these custom locks to define their own rules for managing concurrency, making them particularly useful for extensions and custom workflows.\n\n#### Importance of Advisory locks for Citus\n\nIn a distributed system like Citus, advisory locks take on an even more critical role. Because Citus spreads data across multiple nodes, managing concurrent operations becomes a complex task. Citus heavily relies on advisory locks for a variety of essential operations. Whether it's handling queries from any node, moving/splitting shards, preventing deadlocks, or managing colocation of related data, advisory locks serve as a powerful tool for ensuring smooth operation and data integrity.\n\nBy employing advisory locks, Citus effectively deals with the complexities that come with distributed databases. They allow the system to implement sophisticated concurrency scenarios, ensuring that data remains consistent and operations are efficient across all nodes in the cluster.\n\nBelow, we list some of the crucial advisory locks that Citus relies on:\n\n#### Citus Advisory Locks 1: Distributed Execution locks\n\nThe C code is for a function called `AcquireExecutorShardLocksForExecution()`. The function has an extensive comment for the specific rules. The function's main goal is to get advisory locks on shard IDs to make sure data stays safe and consistent across different operations. These locks are sometimes referred as `ShardResourceLock`s in the code.\n\nIn the context of distributed databases like Citus, \"safe\" generally means avoiding situations where different nodes in the cluster are trying to modify the same data at the same time in a way that could lead to errors, inconsistencies, or even deadlocks. This is critical when data is replicated across nodes (e.g., multiple copies of the same shard like reference tables) or when a single operation affects multiple shards (e.g., multi-shard update).\n\nThe \"consistency\" here primarily refers to two things:\n\n1. **Order of Operations on Replicated Tables**: In a replicated table setup, the same data exists on multiple nodes. The function aims to make sure that any updates, deletes, or inserts happen in the same order on all copies of the data (replicas). This way, all the replicas stay in sync.\n\n2. **Preventing Distributed Deadlocks**: When you're running multi-shard operations that update the data, you can run into distributed deadlocks if operations on different nodes lock shards in a different order. This function ensures that the locks are acquired in a specific, consistent order, thus minimizing the risk of deadlocks.\n\nThere are also options (`GUCs`) to relax these locking mechanisms based on the user's needs, but they come with the trade-off of potentially reduced consistency or increased risk of deadlocks.\n\nSo, in summary, this function is about acquiring the right kind of lock based on what the operation is doing and what kind of table it's affecting, all to ensure that the data stays consistent and that operations are executed safely.\n\n\n#### Citus Advisory Locks 2: Metadata locks\n\nThe second class of advisory locks referred as `metadata locks` in the code. See `AcquireMetadataLocks()` and `LockShardDistributionMetadata()` functions.\n\nThe main goal is to prevent concurrent placement changes and query executions. Essentially ensure that the query execution always works on the accurate placement metadata (e.g., shard placements).\n\nCitus always acquire `Metadata Lock`s for shard moves and shard splits, irrespetive of blocking vs non-blocking operations. For blocking operations, the locks are held from the start of the operation whereas for non-blocking operations the locks are held briefly at the end right before metadata is updated.\n\nCitus always acquires `Metadata Lock`s for modification queries, at the `CitusBeginModifyScan` such that it serializes query modification with placement metadata changes. A modification query would always see up-to date metadata for the placements involved. Otherwise, the modification might get lost.\n\nCitus does not acquire Metadata Locks for SELECT queries. The main reason is that SELECTs are often long-running and would hold up the move. Instead, we allow SELECT commands to operate on the old placements in case of a concurrent shard move. The SELECT commands would already see the snapshot of the shard(s) when the SELECT started. So, there is no difference in terms of query correctness. We then later drop the old placements via \"deferred drop\" (see Resource cleanup).\n\n#### Citus Advisory Locks 3: Query from any node\n\nWhen users are allowed to run queries from any node, then in certain cases, we need to form a synchronization that involes multiple nodes. Advisory locks is a convinent tool for achieving these types of goals.\n\n Citus exposes few UDFs like `lock_shard_resources()` and `lock_shard_metadata()` which are simple wrappers around the metadata and executor locks we discussed above.\n\n When there are nodes with metadata, then Citus acquires some of the advisory locks on all nodes, like:\n\n ```sql\n  select citus_move_shard_placement(102008, 'localhost', 9701, 'localhost', 9702, shard_transfer_mode:='force_logical');\n  ....\n\n  DETAIL:  on server onderkalaci@localhost:9702 connectionId: 2\nNOTICE:  issuing SELECT lock_shard_metadata(7, ARRAY[102008])\nDETAIL:  on server onderkalaci@localhost:9701 connectionId: 1\nNOTICE:  issuing SELECT lock_shard_metadata(7, ARRAY[102008])\nDETAIL:  on server onderkalaci@localhost:9702 connectionId: 2\n...\n```\n\nAnother useful application of advisory locks in query from any node is modifications to reference tables. Reference tables (or in general replicated tables) have multiple placements for a given shard. So, modifications to the placements of the same shard should be serialized. We cannot allow multiple modification commands to modify the placements in different orders. It could cause diverging the contents of the data.\n\nThe coordinator already serializes the modifications to reference tables (or in general all replicated tables) via `LockShardResource()` C function. When there are other nodes in the cluster, Citus sends the similar command, to the first worker node in the cluster. In general, Citus aims to serialize operations on the reference tables via acquiring advisory locks on the first worker node, see `SerializeNonCommutativeWrites()` and `LockShardListResourcesOnFirstWorker()` C functions. We use the `first worker node` as an optimization. Instead of acquiring the locks in all the nodes, each node sorts the worker nodes deterministically, and acquires the lock on the first node. Whichever distributed transaction acquires the lock, it has the autority to continue to the transaction. If there are any other transactions, they are blocked until the first distributed transaction finishes, as it would be in coordinator-only configuration. We used the first worker node as opposed to the coordinator for two reasons. First, the coordinator might already be getting lots of client queries, and we don't want to create additional load to the coordinator. Second, in some Citus deployments, the coordinator may **not** be in the metadata. Hence, the other nodes might not know about the coordinator.\n\nGetting back to the basic flow, the outcome of modifying the reference tables (or replicated tables) is the following where `lock_shard_resources` is acquired on the first node:\n\n```sql\n BEGIN;\n-- 9701 is the first worker node in the metadata\ninsert into reference_table VALUES (1);\nNOTICE:  issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_distributed_transaction_id(0, 8, '2023-09-18 16:30:18.715259+03');\nDETAIL:  on server onderkalaci@localhost:9701 connectionId: 1\nNOTICE:  issuing SELECT lock_shard_resources(3, ARRAY[102040])\nDETAIL:  on server onderkalaci@localhost:9701 connectionId: 1\nNOTICE:  issuing INSERT INTO public.reference_table_102040 (a) VALUES (1)\n\n```\n\n\n#### Citus Advisory Locks 4: Colocation locks\n\nAlthough not ideal, there are currently multiple advisory locks that deal with colocation: `AcquireRebalanceColocationLock()` and `AcquirePlacementColocationLock()`.\n\nThe former has a simple logic. It is to prevent concurrent shard moves of the same shards with the same table colocation. So, basically, prevent user running `citus_move_shard_placement()` for the same colocated shards.\n\nThe latter is a bit more interesting. It aims to ensure the placements of the same colocation group never diverges regarding where the placements reside. This might sound a given feature of Citus.  However, with concurrent `create_distributed_table` and `shard move/split`s there are might be some race conditions. The former operation might use the view of the placements before the `shard move/split`, whereas the latter changes this. As a result, the new table might have a placement that is not colocated with the other placements anymore.\n\nTo prevent that, Citus acquires `AcquirePlacementColocationLock()` while the metadata of placements changed/read. This lock introduced to fix a user reported bug: https://github.com/citusdata/citus/issues/6050\n\n\n4. **Low-level Locks (SpinLocks and LWLocks)**:\n\nSpinLocks and LWLocks are low-level locking mechanisms often used internally by the database system.\nCitus uses `LwLocks` and `SpinLocks` as described in Postgres' source code: https://github.com/postgres/postgres/tree/master/src/backend/storage/lmgr\n\n+ **Spinlocks:** Spinlocks are used for very short-term locking and are meant to be held only for a few instructions. They don't have features like deadlock detection or automatic release, and waiting processes keep checking (\"busy-loop\") until they can acquire the lock.\n\nThe lack of \"automatic release\" could be very critical. For other lock types, when the transaction finishes, the locks are released by Postgres automatically. It means that, say a `palloc` fails due to lack of enough memory, we rely on Postgres to release all the locks. However, this is **NOT** true for spin locks. So, do not even allocate any memory while holding spinlock, only do very simple assignments etc. Otherwise, the lock might be held until a restart of the server.\n\nIn the past we had some bugs where we had a `palloc` failure while holding `SpinLock` and which prevented the lock to be released. So, be extra careful when using `SpinLock`. See the bugfix as a reference: https://github.com/citusdata/citus/pull/2568/files\n\n+ **Lightweight Locks (LWLocks):** These locks are mainly used for controlling access to shared memory data structures and support both exclusive and shared lock modes. While they also lack deadlock detection, they do automatically release when errors are raised, and waiting processes block on a SysV semaphore, conserving CPU time.\n\n\n# Rebalancing\n\nA high-level overview of the shard rebalancer is given in [this rebalancer blog post][rebalancer-post]. It is a bit outdated though, specifically that it uses `rebalance_table_shards()` instead of the newer `citus_rebalance_start()`.\n\nThe shard rebalancer consists of 4 main parts:\n\n1. The rebalancing algorithm: Decides what moves/splits it should do to make\n   the cluster balanced.\n2. The background task runner: Runs a full rebalance according to a plan\n   created by the planner.\n3. A shard group moves/split: These are the smallest units of work that the\n   rebalancer does, if this fails midway through the move is aborted and the\n   shard group remains unchanged.\n4. Deferred cleanup: The source shards stay present for a while after a move to\n   let long-running read queries continue, eventually they need to be cleaned\n   up.\n\nThese parts interact, but they are pretty self-contained. Usually it's only\nnecessary to change one of them to add a feature/fix a bug.\n\n[rebalancer-post]: https://www.citusdata.com/blog/2021/03/13/scaling-out-postgres-with-citus-open-source-shard-rebalancer/\n\n## Rebalancing algorithm\n\nThe rebalancing algorithm tries to find an optimal placement of shard groups\nacross nodes. This is not an easy job, because this is a [co-NP-complete\nproblem](https://en.wikipedia.org/wiki/Knapsack_problem). So instead of going for\nthe fully optimal solution it uses a greedy approach to reach a local\noptimum, which so far has proved effective in getting to a pretty optimal\nsolution.\n\nEven though it won't result in the perfect balance, the  greedy approach has two\nimportant practical benefits over a perfect solution:\n1. It's relatively easy to understand why the algorithm decided on a certain move.\n2. Every move makes the balance better. So if the rebalance is cancelled midway\n   through, the cluster will always be in a better situation than before.\n\nAs described in the [this rebalancer blog post][rebalance-post] the algorithm\ntakes three inputs from the function in the `pg_dist_rebalance_strategy` table:\n\n1. Is a shard group allowed on a certain node?\n2. What is the \"cost\" of a shard group, relative to the other shard groups?\n3. What is the \"capacity\" of a node, relative to the other nodes?\n\nCost and capacity are vague on purpose, this way users can choose their own\nway to determine cost of a shard group, but **in practice \"cost\" is usually\ndisk size** (because `by_disk_size` is the default rebalance strategy).\nCapacity is almost always set to 1, because almost all Citus clusters are\nhomogeneous (they contain the same nodes, except for maybe the coordinator). The\nmain usage for \"Is a shard group allowed on a certain node?\" is to be able to pin a\nspecific shard group to a specific node.\n\nThere is one last definition that you should know to understand the algorithm\nand that is \"utilization\". Utilization is the total cost of all shard groups\ndivided by capacity. In practice this means that utilization is almost always\nthe same as cost because as explained above capacity is almost always 1. So if\nyou see \"utilization\" in the algorithm, for all intents and purposes you can\nread it as \"cost\".\n\nThe way the general algorithm works is fairly straightforward. It starts by\ncreating an in-memory representation of the cluster, and then it tries to\nimprove that in-memory representation by making theoretical moves. So to be\nclear the algorithm doesn't actually do any shard group moves, it only does\nthose moves to its in-memory representation. The way it determines what\ntheoretical moves to make is as follows (updating utilization of in-memory\nnodes after every move):\n\n1. Find all shard groups that are on a node where they are not allowed (due to\n   \"Is a shard group allowed on a certain node?\")\n2. Order those nodes by cost\n3. Move them one-by one to nodes with the lowest utilization where they are\n   allowed.\n4. If the cluster is balanced we are done.\n5. Take the most utilized node (A) and take the least utilized node (B).\n6. Try moving the shard group with the highest cost from A to B.\n7. If the balance is \"better\" commit this move and continue from step 4. (See subsection below for\n   what is \"better\")\n8. If the balance is worse/equal try again from step 6 with the shard group\n   with the next highest cost on node A. If this was the lowest cost shard on\n   node A, then try with the highest cost shard again but on the next least\n   utilized node after node B. If no moves helped with the balance, try with\n   the next most utilized node after node A. If we tried all moves for all\n   nodes like this, we are done (we cannot get a better balance).\n\n\nOf course, the devil is in the details though.\n\n### When is the balance better?\n\nThe main way to determine if the balance is better is by comparing the\nutilization of node A and B, before and after the move and seeing if they are\nnet closer to the average utilization of the nodes in the cluster. The easiest\nway to explain this is with a simple example:\n\nWe have two nodes A and B. A has a utilization of 100GB and B has a utilization\nof 70GB. So we will move a shard from A to B. A move of 15GB is obviously best,\nit results in perfect balance (A=85GB, B=85GB). A move of a 10GB is still\ngreat, both improved in balance (A=90GB, B=80GB). A move of 20GB is also good,\nthe result is the same as a move of 10GB only with the nodes swapped (A=80GB,\nB=90GB).\n\nThe 10GB vs 20GB move shows a limitation of the current algorithm. The\nalgorithm mostly makes choices based on the end state, not on the cost of\nmoving a shard. This is usually not a huge problem in practice though.\n\n### Thresholds\n\nThe algorithm is full of thresholds, the main reason these exist is because\nmoving shards around isn't free.\n\n- `threshold`: Used to determine if the cluster is in a good enough state. For\n  the `by_disk_size` rebalance strategy this is 10%, so if all nodes are at\n  most 10% above or 10% below the average utilization then no moves are\n  necessary anymore (i.e. the nodes are balanced enough). The main reason for\n  this threshold is that these small differences in utilization are not\n  necessarily problematic and might very well resolve automatically over time. For example, consider a scenario in which\n  one shard gets mostly written in during the weekend, while another one during\n  the week. Moving shards on Monday and that you then have to move back on\n  Friday is not very helpful given the overhead of moving data around.\n- `improvement_threshold`: This is used in cases where a shard group move from\n  node A to B swaps which node now has the highest utilization (so afterwards B\n  will have higher utilization than A). As described above this can still\n  result in better balance. This threshold is meant to work around a\n  particularly bad situation where we move a lot of data for very little\n  benefit. Imagine this situation: A=200GB and B=99, thus moving a 100GB shard\n  from A to B would bring their utilization closer to the average (A=100GB,\n  B=199GB). But obviously that's a tiny gain for a move of 100GB, which\n  probably takes lots of resources and time. The `improvement_threshold` is set\n  to 50% for the `by_disk_size` rebalance strategy. This means that this move\n  is only chosen if the utilization improvement is larger than 50% of the\n  utilization that the shard group causes on its current node.\n\n### How do multiple colocation groups impact the rebalancer algorithm?\n\nThe previous section glossed over colocation groups a bit. The main reason for\nthat is that the algorithm doesn't handle multiple colocation groups very well.\nIf there are multiple colocation groups each colocation group gets balanced\ncompletely separately. For the utilization calculations only the costs are used\nfor the shard groups in the colocation group that is currently being rebalanced.\nThe reasoning for this is that if you have two colocation groups, you probably\nwant to spread the shard groups from both colocation groups across multiple\nnodes. And not have shard groups from colocation group 1 only be on node A and\nshard groups from colocation group 2 only be on node B.\n\nThere is an important caveat here though for colocation groups that have fewer\nshard groups than the number of nodes in the cluster (in practice these are\nusually colocation groups used by schema based sharding, i.e. with a single\nshard group): The rebalancer algorithm balances the shard groups from these\ncolocation groups as if they are all all part of a single colocation group.\nThe main reason for this is to make sure that schemas for schema based sharding\nare spread evenly across the nodes.\n\n\n## Shard moves\n\nShard moves move a shard group placement to a different node (group). It would be more correct if these were called \"shard **group** moves\", but in many places we don't due to historical reasons. Moves are orchestrated by the `citus_move_shard_placement` UDF, which is also the function that the rebalancer runs to move a shard.\n\nWe implement blocking and non-blocking shard splits. Non-blocking shard moves use logical replication, which has an important limitation. If the (distributed) table does not have a replica identity (usually the primary key), then update/delete commands will error out once we create a publication. That means using a non-blocking move without a replica identity does incur some downtime. Since a blocking move is generally faster (in part because it forces out regular work), it may be less invasive. We therefore force the user to choose when trying to move a shard group that includes a table without a replica identity by supplying `shard_transfer_mode := 'force_logical'` or `shard_transfer_mode := 'block_writes'`.\n\nThe blocking-move is mostly a simplified variant of the non-blocking move, where the write locks are taken upfront so that no catch-up using logical replication is needed. A non-blocking move involves the following steps:\n\n- **Create the new shard group placement on the target node**. We also create constraints that do not involve an index and set up ownership and access control.\n- **Create publication(s) on the source node**. We create publications containing the shards in the source shard group placement. We create one publications per table owner, mainly because we need one subscription per table owner to prevent privilege escalation issues on older versions of PostgreSQL (15 and below).\n- **Create replication slot(s) and export snapshot(s)**. We create a slot per table owner because we use a separate subscription per table owner, similar to publications. Subscriptions can create the replication slot, but we (nowadays) copy the data outside of the subscription because we apply several optimizations.\n- **Create subscription(s) in disabled state**. We create subscriptions upfront in case there are any errors (e.g. hitting resource limits, connectivity issues). We create one subscription per table owner, and we set the subscription owner to the table owner. The logical replication will happen with the permissions of the subscription owner.\n- **Copy the data from the source to target** by calling `worker_shard_copy` function for each source shard placement via the executor. The `worker_shard_copy` function makes a single pass over the source shard and pushes it into the target shard via `COPY`. We found this to be faster than using the `copy_data` option in the subscription because we can benefit from binary copy, optimizations in the adaptive executor, uses fewer replication slots, and it simplifies the flow. Some of these optimizations might be obsolete as of PostgreSQL 16.\n- **Enable subscription(s)**, which starts the replication of writes that happened on the source shard group placement during the data copy.\n- **Wait for subscription(s) to catch up to the current source LSN**. This can take a while since many writes could have happened during the data copy.\n- **Create indexes, unique/exclusion constraints, statistics, etc.**. For efficiency, we create these objects after copying the data and catching up to concurrent writes.\n- **Wait for subscription(s) to catch up to the current source LSN**. This can take a while since many writes could have happened during the index creation.\n- **Block writes to the split parent by acquiring metadata locks globally**. At this point, we wait for any ongoing INSERT/UPDATE/DELETE/COPY/MERGE to finish and block new ones. Once we acquire the locks we try to quickly finalize the split.\n- **Wait for subscription(s) to catch up to the current source LSN**. Some writes could still have happened before acquiring locks, we wait for those writes to be replicated.\n- **Update the metadata**. We globally update the `pg_dist_placement` record to point to the new node. Writes tactically acquire metadata locks just before reading from `pg_dist_placement`, so they will see the new placement as soon as we commit.\n- **Create foreign keys on the target shard group placement**. Creating foreign keys is deferred until the replication is fully done, because we used multiple subscriptions for tables with different owners and this is the first time that the data is guaranteed to be consistent between shards. We avoid rechecking foreign keys by using the `citus.skip_constraint_validation` setting on the session.\n- **Final cleanup of connections, resources**. We primarily lean on \"Resource cleanup\" to drop publications, replication slots, subscriptions, which ensures they are removed both in case of success and failure. The source shard group placement is dropped once all ongoing (read-only) queries are done, by repeatedly dropping with a short lock timeout until it succeeds.\n\nIt is worth noting that the final commit happens in a 2PC, with all the characteristics of a 2PC. If the commit phase fails on one of the nodes, writes on the shell table remain blocked on that node until the prepared transaction is recovered, after which they will see the updated placement. The data movement generally happens outside of the 2PC, so the 2PC failing on the target node does not necessarily prevent access to the shard.\n\nA similar operation to shard moves is `citus_copy_shard_placement`, which can be used to add a replica to a shard group. We also use this function to replicate reference tables without blocking. The main difference is that dropping the old shard group placement is skipped.\n\nA workaround for the replica identity problem is to always assign REPLICA IDENTITY FULL to distributed tables / shards if they have no other replica identity. However, prior to PostgreSQL 16, replication of updates and delete to a table with replica identity full could be extremely slow (it does a sequential scan per tuple). As of PostgreSQL 16, the logical replication worker can use a regular btree index to find a matching tuple (if one exists). Even for distributed tables without any indexes, and without a replica identity, we could tactically set REPLICA IDENTITY FULL on the shards, and create a suitable index on the target shard group placement for the duration of the move. Once we implement this, we could avoid erroring for distributed tables without a replica identity.\n\n## Shard splits\n\nShard splits convert one shard group (\"split parent\") into two or more shard groups (\"split children\") by splitting the hash range. Just like with shard moves it would be more correct to call these \"shard **group** splits\", but again we often don't. The new shard groups can be placed on the node itself, or on other nodes. We implement blocking and non-blocking shard splits. The blocking variant is mostly a simplified version of non-blocking, so we only cover non-blocking here. Shard splits have many similarities to shard moves, and have the same `shard_transfer_mode` choice.\n\nThe shard split is a lengthy process performed by the `NonBlockingShardSplit` function, supported by a custom output plugin to handle writes that happen during the split. There are a few different entry-points in this logic, namely: `citus_split_shard_by_split_points`, `create_distributed_table_concurrently`, and `isolate_tenant_to_node`.\n\nWe currently do not build a separate .so file for the output plug-in, so it is part of citus.so and therefore the name of the output plug-in is \"citus\". The purpose of the output plug-in is to translate changes to the original shard group to changes to the split children, and emit them in pgoutput format (by calling the original pgoutput plug-in). In some cases, the schema of the split parent can be subtly different from the split children. In particular, some columns may have been dropped on the parent. Dropped columns remain in the metadata and remaining columns are not renumbered, but when we create the split children we only create it with current columns. When this scenario occurs, we convert the tuple in `GetTupleForTargetSchema`.\n\nA split involves the following steps:\n\n- **Create the new shard groups (\"split children\") on the target node(s)**. We also create constraints that do not involve an index and set up ownership and access control.\n- **Create \"dummy\" shard groups on the source node**, unless the split child is also on the source node. The reason for creating the dummy shards is primarily to make the pgoutput output plug-in happy. Our output plug-in maps changes to the split parent into changes to split children before calling pgouput, and those tables need to exist for pgoutput to be able to interpret and emit the change, even when that table is not actually used on the source node.\n- **Create replica identities on dummy shards**. This is also needed to keep pgoutput happy, because for updates and deletes it emits the values in the replica identity columns, so it needs to know what the replica identity is.\n- **Create publication(s) on the source node**, which include both the parent and children. We add the split parent for our own output plug-in to recognize which shard group it should split, and we add the split children for pgoutput to recognize that it should emit them. We might make multiple publications per shard group because we use a separate publication and subscription per table owner, to prevent privilege escalation issues on older versions of PostgreSQL (15 and below).\n- **Set up the shard split output plug-in**. We configure our output plug-in on the source node via `worker_split_shard_replication_setup`, which sets up a dynamic shared memory (DSM) segment that the output plug-in will read from. We currently only have one DSM segment, which would need to changed to support concurrent splits from the same node.\n- **Create replication slot(s) and export snapshot(s)**. We cannot perform any write to the database before this step, because this step waits for all transactions that perform writes to finish. We create multiple slots because we use a separate slot per table owner, similar to publications.\n- **Create subscription(s) in disabled state**. We create subscriptions upfront in case there are any errors (e.g. hitting resource limits, connectivity issues). We create a slot per table owner because we use a separate subscription per table owner, similar to publications. The logical replication will happen with the permissions of the subscription owner.\n- **Split the data in the split parent into the split children** using `worker_split_copy` with the exported snapshot. The `worker_split_copy` function makes a single pass over the parent shard and pushes it into the split children via `COPY`, either via a connection to another node or by invoking the COPY logic locally when the split children are on the same node. Internally, it uses the DestReceiver APIS and effectively it layers the DestReceiver used in re-partition operations on top of the DestReceiver used by `worker_shard_copy` in shard moves. We run a separate `worker_split_copy` task for every shard in the shard group and execute them via the adaptive executor, which may elect to parallelize them.\n- **Enable subscription(s)**, which starts the replication of writes that happened on the split parent during the data copy into the split children.\n- **Wait for subscription(s) to catch up to the current source LSN**. This can take a while since many writes could have happened during the data copy.\n- **Create indexes, unique/exclusion constraints, statistics, etc.**. For efficiency, we create these objects after copying the data and catching up to concurrent writes.\n- **Wait for subscription(s) to catch up to the current source LSN**. This can take a while since many writes could have happened during the index creation.\n- **Block writes to the split parent by acquiring metadata locks globally**. At this point, we wait for any ongoing INSERT/UPDATE/DELETE/COPY/MERGE to finish and block new ones. Once we acquire the locks we try to quickly finalize the split.\n- **Wait for subscription(s) to catch up to the current source LSN**. Some writes could still have happened before acquiring locks, we wait for those writes to be replicated.\n- **Update the metadata**. We globally delete the metadata of the split parent, and insert the metadata of the split children. In case of `create_distributed_table_concurrently` we also update `pg_dist_partition` and `pg_dist_colocation`.\n- **Create partitioning hierarchy and foreign keys on the split children**. Creating these relationships is deferred until the replication is fully done, because we used multiple subscriptions for tables with different owners and this is the first time that the data is guaranteed to be consistent between shards. We avoid rechecking foreign keys by using the `citus.skip_constraint_validation` setting on the session.\n- **Final cleanup of DSM, connections, resources**. We clean up all the resources we created such as publications, subscriptions, replication slots, dummy shards via \"Resource lceanup\", as well as the split parent (deferred, in case of success) or split children (in case of failure). We currently do not clean up the DSM in case of failure, but we always idempotently reset it when doing another split.\n\nA difference between splits and moves is that the old shard ID disappears. In case of a move, only the placement changes and for writes we always look up placement in the executor after acquiring locks that conflict with moves (wait until move is done). In case of a split, the query changes in more fundamental ways, and a single-shard query might actually become a multi-shard queryif it were replanned. When a writes get to the executor, after acquiring locks that conflict with the shard split (wait until split is done), we check whether the shard still exists _in the metadata_ and in case of fast path queries (which are strictly single shard), we try to reroute in `TryToRerouteFastPathModifyQuery`. Otherwise, we error in `EnsureAnchorShardsInJobExist`. In case of reads, we lean on the deferred drop logic to let the read proceed on the old shard placement.\n\n## Background tasks\n\nIn the past the only way to trigger a rebalance was to call\n`rebalance_table_shards()`, this function run the rebalance using the current\nsession. This has the huge downside that the connection needs to be kept open\nuntil the rebalance completes. So eventually we [introduced\n`citus_rebalance_start()`](https://www.citusdata.com/blog/2022/09/19/citus-11-1-shards-postgres-tables-without-interruption/#rebalance-background),\nwhich uses a background worker to do the rebalancing, so users can disconnect\ntheir client and the rebalance continues. It even automatically retries moves\nif they failed for some reason.\n\nThe way this works is using a general background job infrastructure that Citus\nhas in the tables `pg_dist_backround_job` and `pg_dist_background_task`.\nA job (often) contains multiple tasks. In case of the rebalancer, the job is\nthe full rebalance, and each of its tasks are separate shard group moves.\n\n### Parallel background task execution\n\nA big benefit of the background task infrastructure is that it can execute tasks\nand jobs in parallel. This can make rebalancing go much faster especially in\nclusters with many nodes. To ensure that we're not doing too many tasks in\nparallel though we have a few ways to limit concurrency:\n\n1. Tasks can depend on each other. This makes sure that one task doesn't start\n   before all the ones that it depends on have finished.\n2. The maximum number of parallel tasks being executed at the same time can be\n   limited using `citus.max_background_task_executors`. The default for\n   this is 4.\n3. Tasks can specify which nodes are involved in the task, that way we can\n   control that a single node is not involved into too many tasks. The\n   rebalancer specifies both the source and target node as being involved in\n   the task. That together with the default of 1 for\n   `citus.max_background_task_executors_per_node` makes sure that a node\n   doesn't handle more than a single shard move at once, while still allowing\n   moves involving different nodes to happen in parallel. For larger machines\n   it can be beneficial to increase the default a bit.\n\n## Resource cleanup\n\nDuring a shard move/split, some PostgreSQL objects can be created that live outside of the scope of any transaction or are committed early. We need to make sure those objects are dropped once the shard move ends, either through failure or success. For instance, subscriptions and publications used for logical replication need to be dropped in case of failure, but also the target shard (in case of failure) and source shard (in case of success).\n\nTo achieve that, we write records to pg_dist_cleanup before creating an object to remember that we need to clean it. We distinguish between a few scenarios:\n\n**Cleanup-always**: For most resources that require cleanup records, cleanup should happen regardless of whether the operation succeeds or fails. For instance, subscriptions and publications should always be dropped. We achieve cleanup always by writing pg_dist_cleanup records in a subtransaction, and at the end of the operation we try to clean up object immediately and if it succeeds delete the record. If cleanup fails, we do not fail the whole operation, but instead leave the pg_dist_cleanup record in place for the maintenance daemon.\n\n**Cleanup-on-failure**: Cleanup should only happen if the operation fails. The main example is the target shard of a move/split. We achieve cleanup-on-failure by writing pg_dist_cleanup records in a subtransaction (transaction on a localhost connection that commits immediately) and deleting them in the outer transaction that performs the move/split. That way, they remain in pg_dist_cleanup in case of failure, but disappear in case of success.\n\n**Cleanup-deferred-on-success**: Cleanup should only happen after the operation (move/split) succeeds. We use this to clean the source shards of a shard move. We previously dropped shards immediately as part of the transaction, but this frequently led to deadlocks at the end of a shard move. We achieve cleanup-on-success by writing pg_dist_cleanup records as part of the outer transaction that performs the move/split.\n\nResource cleaner (currently shard_cleaner.c) is part of the maintenance daemon and periodically checks pg_dist_cleanup for cleanup tasks. It’s important to prevent cleanup of operations that are still running. Therefore, each operation has a unique operation ID (from a sequence) and takes an advisory lock on the operation ID. The resource cleaner learns the operation ID from pg_dist_cleanup and attempts to acquire this lock. If it cannot acquire the lock, the operation is not done and cleanup is skipped. If it can, the operation is done, and the resource cleaner rechecks whether the record still exists, since it could have been deleted by the operation.\n\nCleanup records always need to be committed before creating the actual object. It’s also important for the cleanup operation to be idempotent, since the server might crash immediately after committing a cleanup record, but before actually creating the object. Hence, the object might not exist when trying to clean it up. In that case, the cleanup is seen as successful, and the cleanup record removed.\n\n# Logical decoding / CDC\n\nPostgreSQL supports change data capture (CDC) via the logical decoding interface. The basic idea behind logical decoding is that you make a replication connection (a special type of postgres connection), start replication, and then the backend process reads through the WAL and decodes the WAL records and emits it over the wire in a format defined by the output plugin. If we were to use regular logical decoding on the nodes of a Citus cluster, we would see the name of the shard in each write, and internal data transfers such as shard moves would result in inserts being emitted. We use several techniques to avoid this.\n\nAll writes in PostgreSQL are marked with a replication origin (0 by default) and the decoder can make decisions on whether to emit the change based on the replication origin. We use this to filter out internal data transfers. If `citus.enable_change_data_capture` is enabled, all internal data transfers are marked with the special DoNotReplicateId replication origin by calling the `citus_internal.start_replication_origin_tracking()` UDF before writing the data. This replication origin ID is special in the sense that it does not need to be created (which prevents locking issues, especially when dropping replication origins). It is still up to output plugin to decide what to do with changes marked as DoNotReplicateId.\n\nWe have very minimal control over replication commands like `CREATE_REPLICATION_SLOT`, since there are no direct hooks, and decoder names (e.g. “pgoutput”) are typically hard-coded in the client. The only method we found of overriding logical decoding behaviour is to overload the output plugin name in the dynamic library path.\n\nFor specific output plugins, we supply a wrapper .so that has the same name as the existing output plugin, but in a separate directory in the PostgreSQL installation folder, and we automatically prefix the `dynamic_library_path` with this folder such that PostgreSQL will load our wrapper. The wrapper internally loads the original output plugin, and calls the original output plugin functions after applying two transformations:\n\n- Shard OIDS are mapped to distributed table OIDS\n- Changes marked with DoNotReplicateId are skipped\n\nMapping the shard OIDs to distributed table OIDs not only makes the output understandable for users, but also simplifies our implementation of the CREATE PUBLICATION command, which is used to configure the pgoutput plugin (used by logical replication). We create the same publication on all nodes using the same distributed table names. Since the original pgoutput plugin only sees changes to distributed tables, it can relate those to the set of distributed tables in the publication.\n\nWe have to build a .so for each wrapper separately. We currently build wrappers for [pgoutput and wal2json](https://github.com/citusdata/citus/blob/main/src/backend/distributed/cdc/Makefile#L8).\n\nThis approach fulfills our main requirements, though we currently have a small correctness issue. Logical decoding always deals with a situation in the past, and to do so they build a historical snapshot of the PostgreSQL catalogs. Tables may have been renamed or dropped since the change happened, but the historical snapshot shows the schema as it was at the time of the change. However, we cannot build a historical snapshot of the Citus catalogs, and we therefore rely on the present values. The main issue that can arise is that the shard may have been dropped, in which case the change might be emitted using its original shard name, since it’s not recognized as a shard name. In many cases, this issue is avoided by caching the Citus catalogs.\n\nAn open issue with CDC is that there is no good way to get a consistent snapshot followed by a change stream that starts from the snapshot. One way to resolve this is to allow reading only from local shards using an exported snapshot. That way, clients can create a replication slot and export a snapshot from each node, pull a subset of the data from each node using the snapshots, and then start replication on each node from the snapshot LSN.\n\n## CDC ordering\n\nWhen you have a multi-node cluster, clients should connect to each node and combine the changes. It is important to note that there are no guarantees with regard to when and in what order changes will be emitted between nodes. It is especially important to understand that changes cannot be reordered (e.g. based on timestamp or transaction ID), because only the node-level order is correct. The lack of distributed snapshot isolation in Citus means that changes can be interleaved (a happens before b on node 1, b happens before a on node 2). The node-level decoder output will reflect that as it happened.\n\n_Do not reorder changes based on timestamp or distributed transaction ID or anything that is not guaranteed to preserve node-level order. It is never correct._\n\n# Global PID\n\nThe global PID (gpid) is used to give each client connection to the cluster a unique process identifier, and to understand which internal connections belong to a specific client connection. A gpid consists of the combination of the node ID and the PID of the coordinating process (i.e. the process serving a client connection). It can be seen in various monitoring views:\n\n```sql\nSELECT citus_backend_gpid();\nSELECT * FROM citus_stat_activity;\nSELECT * FROM citus_lock_waits;\nSELECT * FROM citus_locks;\n```\n\nThe gpid is passed over internal connections via the application_name setting. This is one of the few settings that pgbouncer automatically propagates when reusing a server connection for a different client connection. Hence, gpids are robust to having pgbouncers in between nodes, but it means that internal sessions might switch between gpids.\n\nAdditional details: Monitor distributed Postgres activity with citus_stat_activity & citus_lock_waits (citusdata.com)\n\n# Function call delegation\n\nOne of the downsides of multi-statement transactions in a distributed database is the extra network round trips involved in each individual statement, even when each statement goes to the same worker node. In Citus this can be solved by marking a function or stored procedure as distributed. A distributed function has a distribution argument and can be co-located with distributed tables. It can be created using:\n\n```sql\nSELECT create_distributed_function('delivery(int,int)', '$1');\n```\n\nWhen a distributed function is called, the argument is treated as a distribution column filter on a co-located distributed table and delegated to the worker node that stores the corresponding shards. Ideally, every statement in the function uses the distribution argument as a distribution column filter and only accesses co-located tables, such that the transaction remains local to the worker node. Otherwise, the worker assumes the role of coordinator and performs a distributed transaction. Function call delegation is especially useful in multi-tenant applications that involve complex transactions, as those transactions can be handled in a single network round-trip and with almost no overhead on the coordinator.\n\n\n<img alt=\"Citus coordinator delegates stored procedure call to worker nodes\" src=\"../../../images/coordinator_delegates_stored_procedure.png\" width=\"700\">\n\nWe’ve implemented function call delegation through early-stage checks in the planner hook and the utility hook (for CALL, in case of procedures). If the query matches the simple form shown in the figure above, and the function is marked as distributed, then the function call will be propagated to the right node based on the sharding metadata of a co-located table.\n\nOn the target node, the function is executed as usual using the distributed tables (shell tables + metadata) on that node. The target node will hopefully find that most of the queries are on local shards and only use local execution and take advantage of _local plan caching_.\n\n\n\n# Query from any node\n\nSome Citus users have remarkably high query throughputs (>500k/s). A single-coordinator architecture could sometimes become a bottleneck for scaling such applications. To avoid that, Citus supports querying the database from any node in the cluster.\n\nIn the past, this feature was referred to as Citus MX. We currently refer that as Query From Any Node. The idea is simple: Synchronize all the metadata (including the shell tables and pg_dist_XXX tables) on all the nodes. We do this for all DDL commands as well as when a new node is added to the cluster. In essence, all the nodes in the cluster always have the same metadata through 2PC.\n\nOnce the metadata is synced, then each node can act as the coordinator, capable of doing the distributed query processing. We also provide some monitoring tools such that from the user’s perspective, it should not matter which node the client is connected to. The user should be able to monitor / cancel all the activity in the cluster, using the infrastructure described here: https://www.citusdata.com/blog/2022/07/21/citus-stat-activity-views-for-distributed-postgres/\n\nOne of the challenges with query from any node is the total number of connections in the cluster. In a single coordinator world, only the coordinator establishes connections per node. Now, each node connects to each other. Hence, the user should adjust the connection related settings, see here for details: https://www.citusdata.com/updates/v11-0/#connection-management\n\nBy default, Citus hides all the shards from the applications because it had confused many users: https://www.citusdata.com/updates/v11-0/#shard-visibility  We do it in a slightly hacky way. Read ` HideShardsFromSomeApplications ()` C function for the details.\n\n\n\nAnother important piece of query from any node is that the managed service should provide a single connection string and do the load balancing for the user. It is impractical to have multiple connection strings to the database from any application’s perspective. As of writing this document (Sept. 2023), the managed service did not provide this infrastructure. Another future improvement area for query from any node is the “smart client”. A smart “pgbouncer” type of client might be able to route the client queries to the worker node with the relevant data. This could eliminate the need for additional query routing in case the query does not hit the worker node with the relevant data.\n\nAnother future improvement is to allow running DDLs from any node. Currently, DDLs (including ALTER TABLE, create_distributed_table etc) all should go through the coordinator.\n\n## Why didn’t we have dedicated Query Nodes and Data Nodes?\n\nSome distributed databases distinguish the Query Nodes and Data Nodes. As the names imply, Query Nodes would only do the query processing, whereas Data Nodes would only hold the data. In Citus, we decided not to follow that route, mostly because our initial benchmarks showed that combined nodes performed better in terms of price/performance. Still, some people argued that it might be better to have different classes of nodes such that they can be tuned /scaled-up-out differently based on the load for a given application.\n\n<img alt=\"Dedicated query nodes benchmarks\" src=\"../../../images/mx-dedicated-query-nodes.png\" width=\"500\">\n\nIf this discussion comes up again, we suggest running some more benchmarks and ensuring the performance characteristics do not change dramatically. We do not foresee any architectural problems with that. It mostly comes down to price, performance, and product discussions. Note that you can quickly test this by disallowing certain nodes to have shards on them. You should also consider whether reference tables should still be present on query nodes, and whether there are any behavioural differences between query nodes and the coordinator.\n\n## Shard visibility\n\nShards live in the same schema as the distributed table they belong to, so you might expect to see them when connecting to a worker node and running `\\d`. While this was previously the case, it caused confusion among users and also breaks tools like `pg_dump`. Therefore, we now aggressively hide the shards by default from any query on `pg_class`. We do this by injecting a `relation_is_a_known_shard(oid)` filter in the query tree via the planner hook when we encounter a RangeTblEntry for `pg_class`. The fact that shards are hidden from `pg_class` does not affect queries on the shards, since PostgreSQL internals will not go through the query planner when accessing `pg_class`.\n\nShards can be revealed via two settings:\n\n- `citus.override_shard_visibility = off` disables shard hiding entirely\n- `citus.show_shards_for_app_name_prefixes`= 'pgAdmin,psql'` disables shard hiding only for specific application_name values, by prefix\n\n## Statistic tracking\n\nStatistic views defined by Postgres already work well for one Citus node, like `pg_stat_database`, `pg_stat_activity`, `pg_stat_statements`, etc. And for some of them, we even provide wrapper views in Citus to have a global (i.e., cluster-wide) view of the statistics, like `citus_stat_activity`.\n\nAnd beside these, Citus itself also provides some additional statistic views to track the Citus-specific activities. Note that the way we collect statastics for each is quite different.\n\n- `citus_stat_tenants` (needs documentation)\n- `citus_stat_statements` (needs documentation)\n- `citus_stat_counters`\n\n### Citus stat counters\nCitus keeps track of several stat counters and exposes them via the `citus_stat_counters` view. The counters are tracked once `citus.enable_stat_counters` is set to true. Also, `citus_stat_counters_reset()` can be used to reset the counters for the given database if a database id different than 0 (default, InvalidOid) is provided, otherwise, it resets the counters for the current database.\n\nDetails about the implementation and its caveats can be found in the header comment of [stat_counters.c](/src/backend/distributed/stats/stat_counters.c). However, at the high level;\n1. We allocate a shared memory array of length `MaxBackends` so that each backend has its own counter slot to reduce the contention while incrementing the counters at the runtime.\n2. We also allocate a shared hash, whose entries correspond to individual databases. Then, when a backend exits, it first aggregates its counters to the relevant entry in the shared hash, and then it resets its own counters because the same counter slot might be reused by another backend later.\n\n   Note that today we use the regular shared hash table API (`ShmemInitHash()`) to do this, but we should consider using `dshash_table()` once using many databases with Citus becomes \"practically\" possible because the performance of the regular shared hash table API is supposed to degrade when the number of entries in the hash table becomes much larger than the `max_size` parameter provided when creating the shared hash table.\n3. So, when `citus_stat_counters` is queried, we first aggregate the counters from the shared memory array and then we add this with the counters aggregated so far in the relevant shared hash entry for the database.\n   This means that if we weren't aggregating the counters in the shared hash when exiting, counters seen in `citus_stat_counters` could drift backwards in time.\n\n   Note that `citus_stat_counters` might observe the counters for a backend twice or perhaps unsee it if the backend was concurrently exiting. However, the next call to `citus_stat_counters` will see the correct values for the counters if the same situation doesn't happen due to another backend that is exiting concurrently, so we can live with that for now. However, if one day we think that this is very undesirable, we can enforce blocking behavior between the whole period of `citus_stat_counters` queries and saving the counters in the shared hash entry. However, that will also mean that exiting backends will have to wait for any active `citus_stat_counters` queries to finish, so this needs to be carefully considered.\n4. Finally, when `citus_stat_counters_reset()` is called, we reset the shared hash entry for the relevant database and also reset the relevant slots in the shared memory array for the provided database.\n\n   Note that there is chance that `citus_stat_counters_reset()` might partially fail to reset the counters for of a backend slot under some rare circumstances, but this should be very rare and we choose to ignore that for the sake of lock-free counter increments.\n5. Also, today neither of `citus_stat_counters` nor `citus_stat_counters_reset()` explicitly exclude the backend slots that belong to exited backends during their operations. Instead, they consider any \"not unused\" backend slots where the relevant `PGPROC` points to a valid database oid, which doesn't guarantee that the backend slot is actively used. However, in practice, this is not a problem for neither of these operations due to the reasons mentioned in the relevant functions. However, if we ever decide that the way that they operate slow down these operations, we can consider explicitly excluding the exited backend slots by checking (Citus) `BackendData`'s `activeBackend` field for the backend slots.\n6. As of today, we don't persist stat counters on server shutdown. Although it seems quite straightforward to do so, we skipped doing that at v1. Once we decide to persist the counters, one can check the relevant functions that we have for `citus_stat_statements`, namely, `CitusQueryStatsShmemShutdown()` and `CitusQueryStatsShmemStartup()`. And since it has been quite a long time since we wrote these two functions, we should also make sure to check `pgstat_write_statsfile()` and `pgstat_read_statsfile()` in Postgres to double check if we're missing anything -it seems we have a few-.\n7. Note that today we don't evict the entries of the said hash table that point to dropped databases because the wrapper view anyway filters them out (thanks to LEFT JOIN) and we don't expect a performance hit when reading from / writing to the hash table because of that unless users have a lot of databases that are dropped and recreated frequently. If one day we think that this is a problem, then we can let Citus maintenance daemon to do that for us periodically.\n\nThe reason why we don't just use a shared hash table for the counters is that it could be more expensive to do hash lookups for each increment. Even more importantly, using a single counter slot for all the backends that are connected to the same database could lead to contention because that definitely requires a lock to be taken or the contended usage of atomic integers etc.. However, incrementing a counter in today's implementation doesn't require acquiring any sort of locks.\n\nAlso, as of writing section, it seems quite likely that Postgres will expose their Cumulative Statistics infra starting with Postgres 18, see https://github.com/postgres/postgres/commit/7949d9594582ab49dee221e1db1aa5401ace49d4.\nSo, once this happens, we can also consider using the same infra to track Citus stat counters. However, we can only do that once we drop support for Postgres versions older than 18.\n\n### A side note on query stat counters\n\nInitially, we were thinking of tracking query stat counters at the very end of the planner, namely, via `FinalizePlan()` function. However, that would mean not tracking the execution of the prepared statements because a prepared statement is not planned again once its plan is cached. To give a bit more details, query plan for a prepared statement is typically cached when the same prepared statement is executed five times by Postgres (hard-coded value). Even further, a plan may even be cached after the first time it's executed if it's straightforward enough (e.g. when it doesn't have any parameters).\n\nFor this reason, we track the query stat counters at appropriate places within the CustomScan implementations provided by Citus for adaptive executor and non-pushable insert-select / merge executors.\n"
  },
  {
    "path": "src/backend/distributed/cdc/Makefile",
    "content": "citus_top_builddir = ../../../..\ninclude $(citus_top_builddir)/Makefile.global\n\ncitus_subdir = src/backend/distributed/cdc\nSRC_DIR = $(citus_abs_top_srcdir)/$(citus_subdir)\n\n#List of supported based decoders. Add new decoders here.\ncdc_base_decoders :=pgoutput wal2json\n\nall: build-cdc-decoders\n\ncopy-decoder-files-to-build-dir:\n\t$(eval DECODER_BUILD_DIR=build-cdc-$(DECODER))\n\tmkdir -p $(DECODER_BUILD_DIR)\n\t@for file in $(SRC_DIR)/*.c $(SRC_DIR)/*.h; do \\\n\t\tif [ -f $$file ]; then \\\n\t\t\tif [ -f $(DECODER_BUILD_DIR)/$$(basename $$file) ]; then \\\n\t\t\t\tif ! diff -q $$file $(DECODER_BUILD_DIR)/$$(basename $$file); then \\\n\t\t\t\t\tcp $$file $(DECODER_BUILD_DIR)/$$(basename $$file); \\\n\t\t\t\tfi \\\n\t\t\telse \\\n\t\t\t\tcp $$file $(DECODER_BUILD_DIR)/$$(basename $$file); \\\n\t\t\tfi \\\n\t\tfi \\\n\tdone\n\tcp $(SRC_DIR)/Makefile.decoder $(DECODER_BUILD_DIR)/Makefile\n\nbuild-cdc-decoders:\n\t$(foreach base_decoder,$(cdc_base_decoders),$(MAKE) DECODER=$(base_decoder) build-cdc-decoder;)\n\ninstall-cdc-decoders:\n\t$(foreach base_decoder,$(cdc_base_decoders),$(MAKE) DECODER=$(base_decoder) -C build-cdc-$(base_decoder) install;)\n\nclean-cdc-decoders:\n\t$(foreach base_decoder,$(cdc_base_decoders),rm -rf build-cdc-$(base_decoder);)\n\n\nbuild-cdc-decoder:\n\t$(MAKE) DECODER=$(DECODER) copy-decoder-files-to-build-dir\n\t$(MAKE) DECODER=$(DECODER) -C build-cdc-$(DECODER)\n\ninstall: install-cdc-decoders\n\nclean: clean-cdc-decoders\n\n"
  },
  {
    "path": "src/backend/distributed/cdc/Makefile.decoder",
    "content": "MODULE_big = citus_$(DECODER)\n\ncitus_decoders_dir = $(DESTDIR)$(pkglibdir)/citus_decoders\n\ncitus_top_builddir = ../../../../..\ncitus_subdir = src/backend/distributed/cdc/cdc_$(DECODER)\n\nOBJS += cdc_decoder.o cdc_decoder_utils.o\n\ninclude $(citus_top_builddir)/Makefile.global\n\noverride CFLAGS += -DDECODER=\\\"$(DECODER)\\\" -I$(citus_abs_top_srcdir)/include\noverride CPPFLAGS += -DDECODER=\\\"$(DECODER)\\\" -I$(citus_abs_top_srcdir)/include\n\ninstall: install-cdc\n\ninstall-cdc:\n\tmkdir -p '$(citus_decoders_dir)'\n\t$(INSTALL_SHLIB) citus_$(DECODER)$(DLSUFFIX) '$(citus_decoders_dir)/$(DECODER)$(DLSUFFIX)'\n"
  },
  {
    "path": "src/backend/distributed/cdc/cdc_decoder.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * cdc_decoder.c\n *\t\tCDC Decoder plugin for Citus\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"cdc_decoder_utils.h\"\n#include \"fmgr.h\"\n\n#include \"access/genam.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_publication.h\"\n#include \"commands/extension.h\"\n#include \"common/hashfn.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/typcache.h\"\n\n#include \"pg_version_constants.h\"\n\nPG_MODULE_MAGIC;\n\nextern void _PG_output_plugin_init(OutputPluginCallbacks *cb);\nstatic LogicalDecodeChangeCB ouputPluginChangeCB;\n\nstatic void InitShardToDistributedTableMap(void);\n\nstatic void PublishDistributedTableChanges(LogicalDecodingContext *ctx,\n\t\t\t\t\t\t\t\t\t\t   ReorderBufferTXN *txn,\n\t\t\t\t\t\t\t\t\t\t   Relation relation,\n\t\t\t\t\t\t\t\t\t\t   ReorderBufferChange *change);\n\n\nstatic bool replication_origin_filter_cb(LogicalDecodingContext *ctx, RepOriginId\n\t\t\t\t\t\t\t\t\t\t origin_id);\n\nstatic void TranslateChangesIfSchemaChanged(Relation relation, Relation targetRelation,\n\t\t\t\t\t\t\t\t\t\t\tReorderBufferChange *change);\n\nstatic void TranslateAndPublishRelationForCDC(LogicalDecodingContext *ctx,\n\t\t\t\t\t\t\t\t\t\t\t  ReorderBufferTXN *txn,\n\t\t\t\t\t\t\t\t\t\t\t  Relation relation,\n\t\t\t\t\t\t\t\t\t\t\t  ReorderBufferChange *change, Oid shardId,\n\t\t\t\t\t\t\t\t\t\t\t  Oid targetRelationid);\n\ntypedef struct\n{\n\tuint64 shardId;\n\tOid distributedTableId;\n\tbool isReferenceTable;\n\tbool isNull;\n} ShardIdHashEntry;\n\nstatic HTAB *shardToDistributedTableMap = NULL;\n\nstatic void cdc_change_cb(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,\n\t\t\t\t\t\t  Relation relation, ReorderBufferChange *change);\n\n\n/* build time macro for base decoder plugin name for CDC and Shard Split. */\n#ifndef DECODER\n#define DECODER \"pgoutput\"\n#endif\n\n#define DECODER_INIT_FUNCTION_NAME \"_PG_output_plugin_init\"\n\n#define CITUS_SHARD_TRANSFER_SLOT_PREFIX \"citus_shard_\"\n#define CITUS_SHARD_TRANSFER_SLOT_PREFIX_SIZE (sizeof(CITUS_SHARD_TRANSFER_SLOT_PREFIX) - \\\n\t\t\t\t\t\t\t\t\t\t\t   1)\n\n/*\n * Postgres uses 'pgoutput' as default plugin for logical replication.\n * We want to reuse Postgres pgoutput's functionality as much as possible.\n * Hence we load all the functions of this plugin and override as required.\n */\nvoid\n_PG_output_plugin_init(OutputPluginCallbacks *cb)\n{\n\telog(LOG, \"Initializing CDC decoder\");\n\n\t/*\n\t * We build custom .so files whose name matches common decoders (pgoutput, wal2json)\n\t * and place them in $libdir/citus_decoders/ such that administrators can configure\n\t * dynamic_library_path to include this directory, and users can then use the\n\t * regular decoder names when creating replications slots.\n\t *\n\t * To load the original decoder, we need to remove citus_decoders/ from the\n\t * dynamic_library_path.\n\t */\n\tchar *originalDLP = Dynamic_library_path;\n\tDynamic_library_path = RemoveCitusDecodersFromPaths(Dynamic_library_path);\n\n\tLogicalOutputPluginInit plugin_init =\n\t\t(LogicalOutputPluginInit) (void *)\n\t\tload_external_function(DECODER,\n\t\t\t\t\t\t\t   DECODER_INIT_FUNCTION_NAME,\n\t\t\t\t\t\t\t   false, NULL);\n\n\tif (plugin_init == NULL)\n\t{\n\t\telog(ERROR, \"output plugins have to declare the _PG_output_plugin_init symbol\");\n\t}\n\n\t/* in case this session is used for different replication slots */\n\tDynamic_library_path = originalDLP;\n\n\t/* ask the output plugin to fill the callback struct */\n\tplugin_init(cb);\n\n\t/* Initialize the Shard Id to Distributed Table id mapping hash table.*/\n\tInitShardToDistributedTableMap();\n\n\t/* actual pgoutput callback function will be called  */\n\touputPluginChangeCB = cb->change_cb;\n\tcb->change_cb = cdc_change_cb;\n\tcb->filter_by_origin_cb = replication_origin_filter_cb;\n}\n\n\n/*\n *  Check if the replication slot is for Shard transfer by checking for prefix.\n */\ninline static\nbool\nIsShardTransferSlot(char *replicationSlotName)\n{\n\treturn strncmp(replicationSlotName, CITUS_SHARD_TRANSFER_SLOT_PREFIX,\n\t\t\t\t   CITUS_SHARD_TRANSFER_SLOT_PREFIX_SIZE) == 0;\n}\n\n\n/*\n * shard_split_and_cdc_change_cb function emits the incoming tuple change\n * to the appropriate destination shard.\n */\nstatic void\ncdc_change_cb(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,\n\t\t\t  Relation relation, ReorderBufferChange *change)\n{\n\t/*\n\t * If Citus has not been loaded yet, pass the changes\n\t * through to the undrelying decoder plugin.\n\t */\n\tif (!CdcCitusHasBeenLoaded())\n\t{\n\t\touputPluginChangeCB(ctx, txn, relation, change);\n\t\treturn;\n\t}\n\n\t/* check if the relation is publishable.*/\n\tif (!is_publishable_relation(relation))\n\t{\n\t\treturn;\n\t}\n\n\tchar *replicationSlotName = ctx->slot->data.name.data;\n\tif (replicationSlotName == NULL)\n\t{\n\t\telog(ERROR, \"Replication slot name is NULL!\");\n\t\treturn;\n\t}\n\n\t/* If the slot is for internal shard operations, call the base plugin's call back. */\n\tif (IsShardTransferSlot(replicationSlotName))\n\t{\n\t\touputPluginChangeCB(ctx, txn, relation, change);\n\t\treturn;\n\t}\n\n\t/* Transalate the changes from shard to distributes table and publish. */\n\tPublishDistributedTableChanges(ctx, txn, relation, change);\n}\n\n\n/*\n * InitShardToDistributedTableMap initializes the hash table that is used to\n * translate the changes in the shard table to the changes in the distributed table.\n */\nstatic void\nInitShardToDistributedTableMap()\n{\n\tHASHCTL info;\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(uint64);\n\tinfo.entrysize = sizeof(ShardIdHashEntry);\n\tinfo.hash = tag_hash;\n\tinfo.hcxt = CurrentMemoryContext;\n\n\tint hashFlags = (HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION);\n\tshardToDistributedTableMap = hash_create(\"CDC Decoder translation hash table\", 1024,\n\t\t\t\t\t\t\t\t\t\t\t &info, hashFlags);\n}\n\n\n/*\n * AddShardIdToHashTable adds the shardId to the hash table.\n */\nstatic Oid\nAddShardIdToHashTable(uint64 shardId, ShardIdHashEntry *entry)\n{\n\tentry->shardId = shardId;\n\tentry->distributedTableId = CdcLookupShardRelationFromCatalog(shardId, true);\n\tentry->isReferenceTable = CdcIsReferenceTableViaCatalog(entry->distributedTableId);\n\treturn entry->distributedTableId;\n}\n\n\nstatic Oid\nLookupDistributedTableIdForShardId(uint64 shardId, bool *isReferenceTable)\n{\n\tbool found;\n\tOid distributedTableId = InvalidOid;\n\tShardIdHashEntry *entry = (ShardIdHashEntry *) hash_search(shardToDistributedTableMap,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &found);\n\tif (found)\n\t{\n\t\tdistributedTableId = entry->distributedTableId;\n\t}\n\telse\n\t{\n\t\tdistributedTableId = AddShardIdToHashTable(shardId, entry);\n\t}\n\t*isReferenceTable = entry->isReferenceTable;\n\treturn distributedTableId;\n}\n\n\n/*\n * replication_origin_filter_cb call back function filters out publication of changes\n * originated from any other node other than the current node. This is\n * identified by the \"origin_id\" of the changes. The origin_id is set to\n * a non-zero value in the origin node as part of WAL replication for internal\n * operations like shard split/moves/create_distributed_table etc.\n */\nstatic bool\nreplication_origin_filter_cb(LogicalDecodingContext *ctx, RepOriginId origin_id)\n{\n\treturn  (origin_id != InvalidRepOriginId);\n}\n\n\n/*\n * This function is responsible for translating the changes in the shard table to\n * the changes in the shell table and publishing the changes as a change to the\n * distributed table so that CDD clients are not aware of the shard tables. It also\n * handles schema changes to the distributed table.\n */\nstatic void\nTranslateAndPublishRelationForCDC(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,\n\t\t\t\t\t\t\t\t  Relation relation, ReorderBufferChange *change, Oid\n\t\t\t\t\t\t\t\t  shardId, Oid targetRelationid)\n{\n\t/* Get the distributed table's relation for this shard.*/\n\tRelation targetRelation = RelationIdGetRelation(targetRelationid);\n\n\t/*\n\t * Check if there has been a schema change (such as a dropped column), by comparing\n\t * the number of attributes in the shard table and the shell table.\n\t */\n\tTranslateChangesIfSchemaChanged(relation, targetRelation, change);\n\n\t/*\n\t * Publish the change to the shard table as the change in the distributed table,\n\t * so that the CDC client can see the change in the distributed table,\n\t * instead of the shard table, by calling the pgoutput's callback function.\n\t */\n\touputPluginChangeCB(ctx, txn, targetRelation, change);\n\tRelationClose(targetRelation);\n}\n\n\n/*\n * PublishChangesIfCdcSlot checks if the current slot is a CDC slot. If so, it publishes\n * the changes as the change for the distributed table instead of shard.\n * If not, it returns false. It also skips the Citus metadata tables.\n */\nstatic void\nPublishDistributedTableChanges(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,\n\t\t\t\t\t\t\t   Relation relation, ReorderBufferChange *change)\n{\n\tchar *shardRelationName = RelationGetRelationName(relation);\n\n\t/* Skip publishing CDC changes for any system relations in pg_catalog*/\n\tif (relation->rd_rel->relnamespace == PG_CATALOG_NAMESPACE)\n\t{\n\t\treturn;\n\t}\n\n\t/* Check if the relation is a distributed table by checking for shard name.\t*/\n\tuint64 shardId = CdcExtractShardIdFromTableName(shardRelationName, true);\n\n\t/* If this relation is not distributed, call the pgoutput's callback and return. */\n\tif (shardId == INVALID_SHARD_ID)\n\t{\n\t\touputPluginChangeCB(ctx, txn, relation, change);\n\t\treturn;\n\t}\n\n\tbool isReferenceTable = false;\n\tOid distRelationId = LookupDistributedTableIdForShardId(shardId, &isReferenceTable);\n\tif (distRelationId == InvalidOid)\n\t{\n\t\touputPluginChangeCB(ctx, txn, relation, change);\n\t\treturn;\n\t}\n\n\t/* Publish changes for reference table only from the coordinator node. */\n\tif (isReferenceTable && !CdcIsCoordinator())\n\t{\n\t\treturn;\n\t}\n\n\t/* translate and publish from shard relation to distributed table relation for CDC. */\n\tTranslateAndPublishRelationForCDC(ctx, txn, relation, change, shardId,\n\t\t\t\t\t\t\t\t\t  distRelationId);\n}\n\n\n/*\n * GetTupleForTargetSchemaForCdc returns a heap tuple with the data from sourceRelationTuple\n * to match the schema in targetRelDesc. Either or both source and target relations may have\n * dropped columns. This function handles it by adding NULL values for dropped columns in\n * target relation and skipping dropped columns in source relation. It returns a heap tuple\n * adjusted to the current schema of the target relation.\n */\nstatic HeapTuple\nGetTupleForTargetSchemaForCdc(HeapTuple sourceRelationTuple,\n\t\t\t\t\t\t\t  TupleDesc sourceRelDesc,\n\t\t\t\t\t\t\t  TupleDesc targetRelDesc)\n{\n\t/* Allocate memory for sourceValues and sourceNulls arrays. */\n\tDatum *sourceValues = (Datum *) palloc0(sourceRelDesc->natts * sizeof(Datum));\n\tbool *sourceNulls = (bool *) palloc0(sourceRelDesc->natts * sizeof(bool));\n\n\t/* Deform the source tuple to sourceValues and sourceNulls arrays. */\n\theap_deform_tuple(sourceRelationTuple, sourceRelDesc, sourceValues,\n\t\t\t\t\t  sourceNulls);\n\n\t/* This is the next field to Read in the source relation */\n\tuint32 sourceIndex = 0;\n\tuint32 targetIndex = 0;\n\n\t/* Allocate memory for sourceValues and sourceNulls arrays. */\n\tDatum *targetValues = (Datum *) palloc0(targetRelDesc->natts * sizeof(Datum));\n\tbool *targetNulls = (bool *) palloc0(targetRelDesc->natts * sizeof(bool));\n\n\t/* Loop through all source and target attributes one by one and handle any dropped attributes.*/\n\twhile (targetIndex < targetRelDesc->natts)\n\t{\n\t\t/* If this target attribute has been dropped, add a NULL attribute in targetValues and continue.*/\n\t\tif (TupleDescAttr(targetRelDesc, targetIndex)->attisdropped)\n\t\t{\n\t\t\tDatum nullDatum = (Datum) 0;\n\t\t\ttargetValues[targetIndex] = nullDatum;\n\t\t\ttargetNulls[targetIndex] = true;\n\t\t\ttargetIndex++;\n\t\t}\n\n\t\t/* If this source attribute has been dropped, just skip this source attribute.*/\n\t\telse if (TupleDescAttr(sourceRelDesc, sourceIndex)->attisdropped)\n\t\t{\n\t\t\tsourceIndex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* If both source and target attributes are not dropped, add the attribute field to targetValues. */\n\t\telse if (sourceIndex < sourceRelDesc->natts)\n\t\t{\n\t\t\ttargetValues[targetIndex] = sourceValues[sourceIndex];\n\t\t\ttargetNulls[targetIndex] = sourceNulls[sourceIndex];\n\t\t\tsourceIndex++;\n\t\t\ttargetIndex++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* If there are no more source fields, add a NULL field in targetValues. */\n\t\t\tDatum nullDatum = (Datum) 0;\n\t\t\ttargetValues[targetIndex] = nullDatum;\n\t\t\ttargetNulls[targetIndex] = true;\n\t\t\ttargetIndex++;\n\t\t}\n\t}\n\n\t/* Form a new tuple from the target values created by the above loop. */\n\tHeapTuple targetRelationTuple = heap_form_tuple(targetRelDesc, targetValues,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttargetNulls);\n\treturn targetRelationTuple;\n}\n\n\n/* HasSchemaChanged function returns if there any schema changes between source and target relations.*/\nstatic bool\nHasSchemaChanged(TupleDesc sourceRelationDesc, TupleDesc targetRelationDesc)\n{\n\tbool hasSchemaChanged = (sourceRelationDesc->natts != targetRelationDesc->natts);\n\tif (hasSchemaChanged)\n\t{\n\t\treturn true;\n\t}\n\n\tfor (uint32 i = 0; i < sourceRelationDesc->natts; i++)\n\t{\n\t\tif (TupleDescAttr(sourceRelationDesc, i)->attisdropped ||\n\t\t\tTupleDescAttr(targetRelationDesc, i)->attisdropped)\n\t\t{\n\t\t\thasSchemaChanged = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn hasSchemaChanged;\n}\n\n\n/*\n * TranslateChangesIfSchemaChanged translates the tuples ReorderBufferChange\n * if there is a schema change between source and target relations.\n */\nstatic void\nTranslateChangesIfSchemaChanged(Relation sourceRelation, Relation targetRelation,\n\t\t\t\t\t\t\t\tReorderBufferChange *change)\n{\n\tTupleDesc sourceRelationDesc = RelationGetDescr(sourceRelation);\n\tTupleDesc targetRelationDesc = RelationGetDescr(targetRelation);\n\n\t/* if there are no changes between source and target relations, return. */\n\tif (!HasSchemaChanged(sourceRelationDesc, targetRelationDesc))\n\t{\n\t\treturn;\n\t}\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\n\t/* Check the ReorderBufferChange's action type and handle them accordingly.*/\n\tswitch (change->action)\n\t{\n\t\tcase REORDER_BUFFER_CHANGE_INSERT:\n\t\t{\n\t\t\t/* For insert action, only new tuple should always be translated*/\n\t\t\tHeapTuple sourceRelationNewTuple = change->data.tp.newtuple;\n\t\t\tHeapTuple targetRelationNewTuple = GetTupleForTargetSchemaForCdc(\n\t\t\t\tsourceRelationNewTuple, sourceRelationDesc, targetRelationDesc);\n\t\t\tchange->data.tp.newtuple = targetRelationNewTuple;\n\t\t\tbreak;\n\t\t}\n\n\t\t/*\n\t\t * For update changes both old and new tuples need to be translated for target relation\n\t\t * if the REPLICA IDENTITY is set to FULL. Otherwise, only the new tuple needs to be\n\t\t * translated for target relation.\n\t\t */\n\t\tcase REORDER_BUFFER_CHANGE_UPDATE:\n\t\t{\n\t\t\t/* For update action, new tuple should always be translated*/\n\t\t\t/* Get the new tuple from the ReorderBufferChange, and translate it to target relation. */\n\t\t\tHeapTuple sourceRelationNewTuple = change->data.tp.newtuple;\n\t\t\tHeapTuple targetRelationNewTuple = GetTupleForTargetSchemaForCdc(\n\t\t\t\tsourceRelationNewTuple, sourceRelationDesc, targetRelationDesc);\n\t\t\tchange->data.tp.newtuple = targetRelationNewTuple;\n\n\t\t\t/*\n\t\t\t * Format oldtuple according to the target relation. If the column values of replica\n\t\t\t * identiy change, then the old tuple is non-null and needs to be formatted according\n\t\t\t * to the target relation schema.\n\t\t\t */\n\t\t\tif (change->data.tp.oldtuple != NULL)\n\t\t\t{\n\t\t\t\tHeapTuple sourceRelationOldTuple = change->data.tp.oldtuple;\n\t\t\t\tHeapTuple targetRelationOldTuple = GetTupleForTargetSchemaForCdc(\n\t\t\t\t\tsourceRelationOldTuple,\n\t\t\t\t\tsourceRelationDesc,\n\t\t\t\t\ttargetRelationDesc);\n\n\t\t\t\tchange->data.tp.oldtuple = targetRelationOldTuple;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase REORDER_BUFFER_CHANGE_DELETE:\n\t\t{\n\t\t\t/* For delete action, only old tuple should be translated*/\n\t\t\tHeapTuple sourceRelationOldTuple = change->data.tp.oldtuple;\n\t\t\tHeapTuple targetRelationOldTuple = GetTupleForTargetSchemaForCdc(\n\t\t\t\tsourceRelationOldTuple,\n\t\t\t\tsourceRelationDesc,\n\t\t\t\ttargetRelationDesc);\n\n\t\t\tchange->data.tp.oldtuple = targetRelationOldTuple;\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\t/* Do nothing for other action types. */\n\t\t\tbreak;\n\t\t}\n\t}\n#else\n\n\t/* Check the ReorderBufferChange's action type and handle them accordingly.*/\n\tswitch (change->action)\n\t{\n\t\tcase REORDER_BUFFER_CHANGE_INSERT:\n\t\t{\n\t\t\t/* For insert action, only new tuple should always be translated*/\n\t\t\tHeapTuple sourceRelationNewTuple = &(change->data.tp.newtuple->tuple);\n\t\t\tHeapTuple targetRelationNewTuple = GetTupleForTargetSchemaForCdc(\n\t\t\t\tsourceRelationNewTuple, sourceRelationDesc, targetRelationDesc);\n\t\t\tchange->data.tp.newtuple->tuple = *targetRelationNewTuple;\n\t\t\tbreak;\n\t\t}\n\n\t\t/*\n\t\t * For update changes both old and new tuples need to be translated for target relation\n\t\t * if the REPLICA IDENTITY is set to FULL. Otherwise, only the new tuple needs to be\n\t\t * translated for target relation.\n\t\t */\n\t\tcase REORDER_BUFFER_CHANGE_UPDATE:\n\t\t{\n\t\t\t/* For update action, new tuple should always be translated*/\n\t\t\t/* Get the new tuple from the ReorderBufferChange, and translate it to target relation. */\n\t\t\tHeapTuple sourceRelationNewTuple = &(change->data.tp.newtuple->tuple);\n\t\t\tHeapTuple targetRelationNewTuple = GetTupleForTargetSchemaForCdc(\n\t\t\t\tsourceRelationNewTuple, sourceRelationDesc, targetRelationDesc);\n\t\t\tchange->data.tp.newtuple->tuple = *targetRelationNewTuple;\n\n\t\t\t/*\n\t\t\t * Format oldtuple according to the target relation. If the column values of replica\n\t\t\t * identiy change, then the old tuple is non-null and needs to be formatted according\n\t\t\t * to the target relation schema.\n\t\t\t */\n\t\t\tif (change->data.tp.oldtuple != NULL)\n\t\t\t{\n\t\t\t\tHeapTuple sourceRelationOldTuple = &(change->data.tp.oldtuple->tuple);\n\t\t\t\tHeapTuple targetRelationOldTuple = GetTupleForTargetSchemaForCdc(\n\t\t\t\t\tsourceRelationOldTuple,\n\t\t\t\t\tsourceRelationDesc,\n\t\t\t\t\ttargetRelationDesc);\n\n\t\t\t\tchange->data.tp.oldtuple->tuple = *targetRelationOldTuple;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase REORDER_BUFFER_CHANGE_DELETE:\n\t\t{\n\t\t\t/* For delete action, only old tuple should be translated*/\n\t\t\tHeapTuple sourceRelationOldTuple = &(change->data.tp.oldtuple->tuple);\n\t\t\tHeapTuple targetRelationOldTuple = GetTupleForTargetSchemaForCdc(\n\t\t\t\tsourceRelationOldTuple,\n\t\t\t\tsourceRelationDesc,\n\t\t\t\ttargetRelationDesc);\n\n\t\t\tchange->data.tp.oldtuple->tuple = *targetRelationOldTuple;\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\t/* Do nothing for other action types. */\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n}\n"
  },
  {
    "path": "src/backend/distributed/cdc/cdc_decoder_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * cdc_decoder_utils.c\n *\t\tCDC Decoder plugin utility functions for Citus\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"cdc_decoder_utils.h\"\n#include \"fmgr.h\"\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"commands/extension.h\"\n#include \"common/hashfn.h\"\n#include \"common/string.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/typcache.h\"\n\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/relay_utility.h\"\n\nstatic int32 LocalGroupId = -1;\nstatic Oid PgDistLocalGroupRelationId = InvalidOid;\nstatic Oid PgDistShardRelationId = InvalidOid;\nstatic Oid PgDistShardShardidIndexId = InvalidOid;\nstatic Oid PgDistPartitionRelationId = InvalidOid;\nstatic Oid PgDistPartitionLogicalrelidIndexId = InvalidOid;\nstatic bool IsCitusExtensionLoaded = false;\n\n#define COORDINATOR_GROUP_ID 0\n#define InvalidRepOriginId 0\n#define Anum_pg_dist_local_groupid 1\n#define GROUP_ID_UPGRADING -2\n\n\nstatic Oid DistLocalGroupIdRelationId(void);\nstatic int32 CdcGetLocalGroupId(void);\nstatic HeapTuple CdcPgDistPartitionTupleViaCatalog(Oid relationId);\n\n/*\n * DistLocalGroupIdRelationId returns the relation id of the pg_dist_local_group\n */\nstatic Oid\nDistLocalGroupIdRelationId(void)\n{\n\tif (PgDistLocalGroupRelationId == InvalidOid)\n\t{\n\t\tPgDistLocalGroupRelationId = get_relname_relid(\"pg_dist_local_group\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   PG_CATALOG_NAMESPACE);\n\t}\n\treturn PgDistLocalGroupRelationId;\n}\n\n\n/*\n * DistShardRelationId returns the relation id of the pg_dist_shard\n */\nstatic Oid\nDistShardRelationId(void)\n{\n\tif (PgDistShardRelationId == InvalidOid)\n\t{\n\t\tPgDistShardRelationId = get_relname_relid(\"pg_dist_shard\", PG_CATALOG_NAMESPACE);\n\t}\n\treturn PgDistShardRelationId;\n}\n\n\n/*\n * DistShardShardidIndexId returns the relation id of the pg_dist_shard_shardid_index\n */\nstatic Oid\nDistShardShardidIndexId(void)\n{\n\tif (PgDistShardShardidIndexId == InvalidOid)\n\t{\n\t\tPgDistShardShardidIndexId = get_relname_relid(\"pg_dist_shard_shardid_index\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  PG_CATALOG_NAMESPACE);\n\t}\n\treturn PgDistShardShardidIndexId;\n}\n\n\n/*\n * DistPartitionRelationId returns the relation id of the pg_dist_partition\n */\nstatic Oid\nDistPartitionRelationId(void)\n{\n\tif (PgDistPartitionRelationId == InvalidOid)\n\t{\n\t\tPgDistPartitionRelationId = get_relname_relid(\"pg_dist_partition\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  PG_CATALOG_NAMESPACE);\n\t}\n\treturn PgDistPartitionRelationId;\n}\n\n\nstatic Oid\nDistPartitionLogicalRelidIndexId(void)\n{\n\tif (PgDistPartitionLogicalrelidIndexId == InvalidOid)\n\t{\n\t\tPgDistPartitionLogicalrelidIndexId = get_relname_relid(\n\t\t\t\"pg_dist_partition_logicalrelid_index\", PG_CATALOG_NAMESPACE);\n\t}\n\treturn PgDistPartitionLogicalrelidIndexId;\n}\n\n\n/*\n * CdcIsCoordinator function returns true if this node is identified as the\n * schema/coordinator/master node of the cluster.\n */\nbool\nCdcIsCoordinator(void)\n{\n\treturn (CdcGetLocalGroupId() == COORDINATOR_GROUP_ID);\n}\n\n\n/*\n * CdcCitusHasBeenLoaded function returns true if the citus extension has been loaded.\n */\nbool\nCdcCitusHasBeenLoaded()\n{\n\tif (!IsCitusExtensionLoaded)\n\t{\n\t\tIsCitusExtensionLoaded = (get_extension_oid(\"citus\", true) != InvalidOid);\n\t}\n\n\treturn IsCitusExtensionLoaded;\n}\n\n\n/*\n * ExtractShardIdFromTableName tries to extract shard id from the given table name,\n * and returns the shard id if table name is formatted as shard name.\n * Else, the function returns INVALID_SHARD_ID.\n */\nuint64\nCdcExtractShardIdFromTableName(const char *tableName, bool missingOk)\n{\n\tchar *shardIdStringEnd = NULL;\n\n\t/* find the last underscore and increment for shardId string */\n\tchar *shardIdString = strrchr(tableName, SHARD_NAME_SEPARATOR);\n\tif (shardIdString == NULL && !missingOk)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not extract shardId from table name \\\"%s\\\"\",\n\t\t\t\t\t\t\t   tableName)));\n\t}\n\telse if (shardIdString == NULL && missingOk)\n\t{\n\t\treturn INVALID_SHARD_ID;\n\t}\n\n\tshardIdString++;\n\n\terrno = 0;\n\tuint64 shardId = strtoull(shardIdString, &shardIdStringEnd, 0);\n\n\tif (errno != 0 || (*shardIdStringEnd != '\\0'))\n\t{\n\t\tif (!missingOk)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"could not extract shardId from table name \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t   tableName)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn INVALID_SHARD_ID;\n\t\t}\n\t}\n\n\treturn shardId;\n}\n\n\n/*\n * CdcGetLocalGroupId returns the group identifier of the local node. The\n * function assumes that pg_dist_local_group has exactly one row and has at\n * least one column. Otherwise, the function errors out.\n */\nstatic int32\nCdcGetLocalGroupId(void)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 0;\n\tint32 groupId = 0;\n\n\t/*\n\t * Already set the group id, no need to read the heap again.\n\t */\n\tif (LocalGroupId != -1)\n\t{\n\t\treturn LocalGroupId;\n\t}\n\n\tOid localGroupTableOid = DistLocalGroupIdRelationId();\n\tif (localGroupTableOid == InvalidOid)\n\t{\n\t\treturn 0;\n\t}\n\n\tRelation pgDistLocalGroupId = table_open(localGroupTableOid, AccessShareLock);\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistLocalGroupId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistLocalGroupId);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tbool isNull = false;\n\t\tDatum groupIdDatum = heap_getattr(heapTuple,\n\t\t\t\t\t\t\t\t\t\t  Anum_pg_dist_local_groupid,\n\t\t\t\t\t\t\t\t\t\t  tupleDescriptor, &isNull);\n\n\t\tgroupId = DatumGetInt32(groupIdDatum);\n\n\t\t/* set the local cache variable */\n\t\tLocalGroupId = groupId;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * Upgrade is happening. When upgrading postgres, pg_dist_local_group is\n\t\t * temporarily empty before citus_finish_pg_upgrade() finishes execution.\n\t\t */\n\t\tgroupId = GROUP_ID_UPGRADING;\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistLocalGroupId, AccessShareLock);\n\n\treturn groupId;\n}\n\n\n/*\n * CdcLookupShardRelationFromCatalog returns the logical relation oid a shard belongs to.\n *\n * Errors out if the shardId does not exist and missingOk is false.\n * Returns InvalidOid if the shardId does not exist and missingOk is true.\n */\nOid\nCdcLookupShardRelationFromCatalog(int64 shardId, bool missingOk)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tForm_pg_dist_shard shardForm = NULL;\n\tRelation pgDistShard = table_open(DistShardRelationId(), AccessShareLock);\n\tOid relationId = InvalidOid;\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_shard_shardid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(shardId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistShard,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistShardShardidIndexId(), true,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple) && !missingOk)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for shard \"\n\t\t\t\t\t\t\t   UINT64_FORMAT, shardId)));\n\t}\n\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\trelationId = InvalidOid;\n\t}\n\telse\n\t{\n\t\tshardForm = (Form_pg_dist_shard) GETSTRUCT(heapTuple);\n\t\trelationId = shardForm->logicalrelid;\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistShard, NoLock);\n\n\treturn relationId;\n}\n\n\n/*\n * CdcPgDistPartitionTupleViaCatalog is a helper function that searches\n * pg_dist_partition for the given relationId. The caller is responsible\n * for ensuring that the returned heap tuple is valid before accessing\n * its fields.\n */\nstatic HeapTuple\nCdcPgDistPartitionTupleViaCatalog(Oid relationId)\n{\n\tconst int scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\tbool indexOK = true;\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_partition_logicalrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPartitionLogicalRelidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK, NULL, scanKeyCount, scanKey);\n\n\tHeapTuple partitionTuple = systable_getnext(scanDescriptor);\n\n\tif (HeapTupleIsValid(partitionTuple))\n\t{\n\t\t/* callers should have the tuple in their memory contexts */\n\t\tpartitionTuple = heap_copytuple(partitionTuple);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistPartition, AccessShareLock);\n\n\treturn partitionTuple;\n}\n\n\n/*\n * CdcIsReferenceTableViaCatalog gets a relationId and returns true if the relation\n * is a reference table and false otherwise.\n */\nchar\nCdcIsReferenceTableViaCatalog(Oid relationId)\n{\n\tHeapTuple partitionTuple = CdcPgDistPartitionTupleViaCatalog(relationId);\n\tif (!HeapTupleIsValid(partitionTuple))\n\t{\n\t\treturn false;\n\t}\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), AccessShareLock);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\tDatum *datumArray = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNullArray = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\n\theap_deform_tuple(partitionTuple, tupleDescriptor, datumArray, isNullArray);\n\n\tif (isNullArray[Anum_pg_dist_partition_partmethod - 1] ||\n\t\tisNullArray[Anum_pg_dist_partition_repmodel - 1])\n\t{\n\t\t/*\n\t\t * partition method and replication model cannot be NULL,\n\t\t * still let's make sure\n\t\t */\n\t\theap_freetuple(partitionTuple);\n\t\ttable_close(pgDistPartition, NoLock);\n\t\tpfree(datumArray);\n\t\tpfree(isNullArray);\n\t\treturn false;\n\t}\n\n\tDatum partitionMethodDatum = datumArray[Anum_pg_dist_partition_partmethod - 1];\n\tchar partitionMethodChar = DatumGetChar(partitionMethodDatum);\n\n\tDatum replicationModelDatum = datumArray[Anum_pg_dist_partition_repmodel - 1];\n\tchar replicationModelChar = DatumGetChar(replicationModelDatum);\n\n\theap_freetuple(partitionTuple);\n\ttable_close(pgDistPartition, NoLock);\n\tpfree(datumArray);\n\tpfree(isNullArray);\n\n\t/*\n\t * A table is a reference table when its partition method is 'none'\n\t * and replication model is 'two phase commit'\n\t */\n\treturn partitionMethodChar == DISTRIBUTE_BY_NONE &&\n\t\t   replicationModelChar == REPLICATION_MODEL_2PC;\n}\n\n\n/*\n * RemoveCitusDecodersFromPaths removes a path ending in citus_decoders\n * from the given input paths.\n */\nchar *\nRemoveCitusDecodersFromPaths(char *paths)\n{\n\tif (strlen(paths) == 0)\n\t{\n\t\t/* dynamic_library_path is empty */\n\t\treturn paths;\n\t}\n\n\tStringInfo newPaths = makeStringInfo();\n\n\tchar *remainingPaths = paths;\n\n\tfor (;;)\n\t{\n\t\tint pathLength = 0;\n\n\t\tchar *pathStart = first_path_var_separator(remainingPaths);\n\t\tif (pathStart == remainingPaths)\n\t\t{\n\t\t\t/*\n\t\t\t * This will error out in find_in_dynamic_libpath, return\n\t\t\t * original value here.\n\t\t\t */\n\t\t\treturn paths;\n\t\t}\n\t\telse if (pathStart == NULL)\n\t\t{\n\t\t\t/* final path */\n\t\t\tpathLength = strlen(remainingPaths);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* more paths remaining */\n\t\t\tpathLength = pathStart - remainingPaths;\n\t\t}\n\n\t\tchar *currentPath = palloc(pathLength + 1);\n\t\tstrlcpy(currentPath, remainingPaths, pathLength + 1);\n\t\tcanonicalize_path(currentPath);\n\n\t\tif (!pg_str_endswith(currentPath, \"/citus_decoders\"))\n\t\t{\n\t\t\tappendStringInfo(newPaths, \"%s%s\", newPaths->len > 0 ? \":\" : \"\", currentPath);\n\t\t}\n\n\t\tif (remainingPaths[pathLength] == '\\0')\n\t\t{\n\t\t\t/* end of string */\n\t\t\tbreak;\n\t\t}\n\n\t\tremainingPaths += pathLength + 1;\n\t}\n\n\treturn newPaths->data;\n}\n"
  },
  {
    "path": "src/backend/distributed/cdc/cdc_decoder_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * cdc_decoder_utils.h\n *\t  Utility functions and declerations for cdc decoder.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_CDC_DECODER_H\n#define CITUS_CDC_DECODER_H\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n\n#include \"replication/logical.h\"\n\n#define InvalidRepOriginId 0\n#define INVALID_SHARD_ID 0\n\nbool CdcIsCoordinator(void);\n\nuint64 CdcExtractShardIdFromTableName(const char *tableName, bool missingOk);\n\nOid CdcLookupShardRelationFromCatalog(int64 shardId, bool missingOk);\n\nchar CdcIsReferenceTableViaCatalog(Oid relationId);\n\nbool CdcCitusHasBeenLoaded(void);\n\nchar * RemoveCitusDecodersFromPaths(char *paths);\n\n#endif   /* CITUS_CDC_DECODER_UTILS_H */\n"
  },
  {
    "path": "src/backend/distributed/citus--11.1-1.control",
    "content": "requires = 'citus_columnar'\n"
  },
  {
    "path": "src/backend/distributed/citus.control",
    "content": "# Citus extension\ncomment = 'Citus distributed database'\ndefault_version = '15.0-1'\nmodule_pathname = '$libdir/citus'\nrelocatable = false\nschema = pg_catalog\n"
  },
  {
    "path": "src/backend/distributed/clock/README.md",
    "content": "# Cluster Clock\n### Requirement:\nMany distributed applications need to track the changes in the same order as they are applied on the database. The changes can be to databases or objects within them, either within a single node or across the sharded cluster.\n### Definitions\n**Total ordering** - Every pair of change events can be placed in some order.\n**Causal ordering** - Only events that are causally related (an event A caused an event B) can be ordered i.e., it's only a partial order - sometimes events happen independently with no possible causal relationship, such events are treated to concurrent.\n**Sequential consistency** - All writes must be seen in the same order by all processes.\n**Causal consistency** - Causally related writes must be seen in the same order.\n\nTransactions on a single node system naturally provide a total and sequential ordering guarantees for client read and write operations as all operations are routed to the same node, but there are challenges for a multi node distributed system, such as, Citus.\n\nOne possible way to totally order all the changes in the system is to timestamp all the events with a global physical clock or a centralized logical clock. Thus, observing the events in the increasing order of the timestamp will give the total ordering of events. For both the performance and cost reasons such solutions are impractical. In the absence of total ordering, a little weaker ordering is the **causal order**.\n\nCausal order is defined as a model that preserves a partial order of events in a distributed system. If an event\n\n1.  A causes another event B, every other process in the system observes the event A before observing event B.\n2.  Causal order is transitive: if A causes B, and B causes C, then A causes C.\n3.  Non causally ordered events are treated as concurrent.\n\nCausal consistency is a weak form of consistency that preserves the order of causally related operations. The causal consistency model can be refined into four session guarantees.\n\n1.  Read Your Writes: If a process performs a write, the same process later observes the result of its write.\n6.  Monotonic Reads: The set of writes observed (read) by a process is guaranteed to be monotonically increasing.\n7.  Writes Follow Reads: If some process performs a read followed by a write, and another process observes the result of the write, then it can also observe the read.\n8.  Monotonic Writes: If some process performs a write, followed sometime later by another write, other processes will observe them in the same order.\n\n### Hybrid Logical Clock (HLC)\nHLC provides a way to get the causality relationship like logical clocks. It can also be used for backup/recovery too as the logical clock value is maintained close to the wall clock time. HLC consists of\n LC - Logical clock\n C - Counter\n\nClock components - Unsigned 64 bit <LC, C>\nEpoch Milliseconds ( LC ) | Logical counter ( C )|\n|--|--|\n| 42 bits | 22 bits |\n\n2^42 milliseconds - 4398046511104 milliseconds, which is ~139 years.\n\n2^22 ticks - maximum of four million operations per millisecond.\n\n### UDFs\nA new UDF `citus_get_cluster_clock`() that returns a monotonically increasing logical clock. Clock guarantees to never go back in value after restarts and makes best attempt to keep the value close to UNIX epoch time in milliseconds.\n\nA new UDF `citus_get_transaction_clock`(), when called by the user, returns the logical causal clock timestamp current transaction,\nInternally, this is the maximum clock among all transaction nodes, and\nall nodes move to the new clock.\n\n### GUC\nA new GUC parameter, \"**citus.enable_cluster_clock**\", If clocks go bad for any reason, this serves as a safety valve to avoid the need to change the application and (re)deploy it.\n\n### Sequence\nIn Unix, though rare, there is a possibility of clock drifting backwards (or\nforward) after a restart. In such rare scenarios, we might end up with a logical clock value less than the previously used value, this violates the fundamental requirement of monotonically increasing clock. To avoid such disasters, every logical clock tick is persisted using sequences (non-transactional). After a restart, the persisted sequence value is read and clock starts from that value, which will ensure that system starts the clock from where we left off.\n\n### Psuedo code\n    WC - Current Wall Clock in milliseconds\n    HLC - Current Hybrid Logical Clock in shared\n          memory\n    MAX_COUNTER - Four million\n\n    /* Tick the clock by 1 */\n    IncrementClusterClock()\n    {\n       /* It's the counter that always ticks, once it reaches\n          the maximum, reset the counter to 1 and increment\n          the logical clock. */\n\n        if (HLC.C == MAX_COUNTER)\n        {\n                HLC.LC++;\n                HLC.C = 0;\n                return;\n        }\n        HLC.C++;\n    }\n\n    /* Tick for each event, must increase monotonically */\n    GetNextNodeClockValue()\n    {\n\t    IncrementClusterClock(HLC);\n\n\t    /* From the incremented clock and current wall clock,\n\t       pick which ever is highest */\n\t    NextClock = MAX(HLC, WC);\n\n\t    /* Save the NextClock value in both the shared memory\n\t       and sequence */\n\t    HLC = NextClock;\n\t    SETVAL(pg_dist_clock_logical_seq, HLC);\n\t}\n\n\t/* Returns true if the clock1 is after clock2 */\n    IsEventAfter(HLC1, HLC2)\n    {\n\t    IF (HLC1.LC != HLC2.LC)\n\t\t    return (HLC1.LC > HLC2.LC);\n\t\tELSE\n\t\t\treturn (HLC1.C > HLC2.C);\n    }\n\n    /* Simply returns the highest node clock value among all\n       nodes */\n    GetHighestClockInTransaction()\n    {\n\t    For each node\n\t    {\n\t\t    NodeClock[N] = GetNextNodeClockValue();\n\t\t}\n\n\t\t/* Return the highest clock value of all the nodes */\n\t\treturn MAX(NodeClock[N]);\n    }\n\n    /* Adjust the local shared memory clock to the received\n       value (RHLC) from the remote node */\n\tAdjustClock(RHLC)\n\t{\n\t\t/* local clock is ahead or equal, do nothing */\n        IF (HLC >= RHLC)\n        {\n          return;\n        }\n        /* Save the remote clockvalue in both the shared\n           memory and sequence */\n\t    HLC = RHLC;\n\t    SETVAL(pg_dist_clock_logical_seq, HLC);\n\t}\n\n    /* All the nodes will adjust their clocks to the highest\n       of the newly negotiated clock */\n    AdjustClocksToTransactionHighest(HLC)\n    {\n\t    For each node\n\t    {\n\t\t    SendCommand (\"AdjustClock(HLC)\");\n\t\t}\n    }\n\n    /* When citus_get_transaction_clock() UDF is invoked */\n    PrepareAndSetTransactionClock()\n    {\n\t    /* Pick the highest logical clock value among all\n\t       transaction-nodes */\n        txnCLock = GetHighestClockInTransaction()\n\n        /* Adjust all the nodes with the new clock value */\n        AdjustClocksToTransactionHighest(txnCLock )\n\n        return txnClock;\n    }\n\n    /* Initialize the clock value to the highest clock\n       persisted in sequence */\n    InitClockAtBoot()\n    {\n\t    /* Start with the current wall clock */\n\t    HLC = WC;\n\n\t    IF (SEQUENCE == 1)\n\t\t/* clock never ticked on this node, start with the\n\t\t   wall clock. */\n\t\t\t\treturn;\n\t\t/* get the most recent clock ever used from disk */\n\t\tpersistedClock =\n\t\t    NEXT_VAL(pg_dist_clock_logical_seq...)\n\t\t    /* Start the clock with persisted value */\n\t\t    AdjustLocalClock(persistedClock);\n\t    }\n    }\n\n #### Usage\n**Step 1**\nIn the application, track every change of a transaction along with the unique transaction ID by calling UDF\n`get_current_transaction_id`()\n\n    INSERT INTO track_table\n    SET TransactionId =\n    get_current_transaction_id(),\n    operation = <insert/update/delete>,\n    row_key = <>,\n    ....;\n\n**Step 2**\nAs the transaction is about to end, and before the COMMIT, capture the causal clock timestamp along with the transaction ID in a table\n\n    INSERT INTO transaction_commit_clock\n    (TransactionId, CommitClock, timestamp)\n    SELECT\n            citus_get_transaction_clock(),\n            get_current_transaction_id(),\n            now()\n\n**Step 3**\nHow to get all the events in the causal order?\n\n    SELECT tt.row_key, tt.operation\n    FROM track_table tt,\n         transaction_commit_clock cc\n    WHERE tt.TransactionId = cc.TransactionId\n    ORDER BY cc.CommitClock\n\nEvents for an object\n\n    SELECT tt.row_key, tt.operation\n    FROM track_table tt,\n         transaction_commit_clock cc\n    WHERE tt.TransactionId = cc.TransactionId\n    and row_key = $1 ORDER BY cc.CommitClock\n\nEvents in the last one hour\n\n    SELECT tt.row_key, tt.operation\n    FROM track_table tt,\n         transaction_commit_clockcc\n    WHERE cc.timestamp >= now() - interval '1 hour'\n    and tt.TransactionId = cc.TransactionId\n\n**Note**: In Citus we use 2PC, if any node goes down after the PREPARE and before the COMMIT, we might have changes partially committed. Citus tracks such transactions in **pg_dist_transaction** and eventually will be committed when the node becomes healthy, but when we track change-data from committed transactions of **transaction_commit_clock** we will miss the changes from a bad node.\n\nTo address this issue, proposal is to have a new UDF #TBD, that freezes\nthe clock and ensures that all the 2PCs are fully complete\n(i.e., **pg_dist_transaction** should be empty) and return the highest\nclock used. All transactions in `transaction_commit_clock` with\ntimestamp below this returned clock are visible to the application. The\nexact nuances, such as frequency of calling such UDF, are still TBD.\nCaveat is, if the node and the 2PC takes long to fully recover, the\nvisibility of the committed transactions might stall.\n\n### Catalog pruning\nThe data in **transaction_commit_clock** should be ephemeral data i.e., eventually rows have to automatically be deleted. Users can install a pg_cron job to prune the catalog regularly.\n\n    delete from transaction_commit_clock\n        where timestamp < now() - interval '7 days'\n\n### Limitations of Citus\nUsing this transaction commit clock ordering to build a secondary, that's a mirror copy of the original, may not be feasible at this time for the following reasons.\n\nGiven that there is no well-defined order between concurrent distributed transactions in Citus, we cannot retroactively apply a transaction-order that leads to an exact replica of the primary unless we preserve the original object-level ordering as it happened on individual nodes.\n\nFor instance, if a multi-shard insert (transaction A) happens concurrently with a multi-shard update (transaction B) and the WHERE clause of the update matches inserted rows in multiple shards, we could have a scenario in which only a subset of the inserted rows gets updated. Effectively, transaction A might happen before transaction B on node 1, while transaction B happens before transaction A on node 2. While unfortunate, we cannot simply claim changes made by transaction A happened first based on commit timestamps, because that would lead us reorder changes to the same object ID on node 2, which might lead to a different outcome when replayed.\n\nIn such scenario, even if we use causal commit clock to order changes. It is essential that the order of modifications to an object matches the original order. Otherwise, you could have above scenarios where an insert happens before an update in the primary cluster, but the update happens before the insert. Replaying the changes would then lead to a different database.\n\nIn absence of a coherent transaction-ordering semantics in distributed cluster, best we can do is ensure that changes to the same object are in the correct order and ensure exactly once delivery (correct pagination).\n"
  },
  {
    "path": "src/backend/distributed/clock/causal_clock.c",
    "content": "/*-------------------------------------------------------------------------\n * causal_clock.c\n *\n * Core function defintions to implement hybrid logical clock.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <sys/time.h>\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"catalog/namespace.h\"\n#include \"commands/extension.h\"\n#include \"commands/sequence.h\"\n#include \"executor/spi.h\"\n#include \"nodes/pg_list.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/ipc.h\"\n#include \"storage/lwlock.h\"\n#include \"storage/s_lock.h\"\n#include \"storage/shmem.h\"\n#include \"storage/spin.h\"\n#include \"utils/builtins.h\"\n#include \"utils/datum.h\"\n#include \"utils/numeric.h\"\n#include \"utils/typcache.h\"\n\n#include \"distributed/causal_clock.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/remote_commands.h\"\n\n#define SAVE_AND_PERSIST(c) \\\n\t\tdo { \\\n\t\t\tOid savedUserId = InvalidOid; \\\n\t\t\tint savedSecurityContext = 0; \\\n\t\t\tLogicalClockShmem->clusterClockValue = *(c); \\\n\t\t\tGetUserIdAndSecContext(&savedUserId, &savedSecurityContext); \\\n\t\t\tSetUserIdAndSecContext(CitusExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE); \\\n\t\t\tDirectFunctionCall2(setval_oid, \\\n\t\t\t\t\t\t\t\tObjectIdGetDatum(DistClockLogicalSequenceId()), \\\n\t\t\t\t\t\t\t\tInt64GetDatum((c)->logical)); \\\n\t\t\tSetUserIdAndSecContext(savedUserId, savedSecurityContext); \\\n\t\t} while (0)\n\nPG_FUNCTION_INFO_V1(citus_get_node_clock);\nPG_FUNCTION_INFO_V1(citus_internal_adjust_local_clock_to_remote);\nPG_FUNCTION_INFO_V1(citus_is_clock_after);\nPG_FUNCTION_INFO_V1(citus_get_transaction_clock);\n\n/*\n * Current state of the logical clock\n */\ntypedef enum ClockState\n{\n\tCLOCKSTATE_INITIALIZED,\n\tCLOCKSTATE_UNINITIALIZED\n} ClockState;\n\n/*\n * Holds the cluster clock variables in shared memory.\n */\ntypedef struct LogicalClockShmemData\n{\n\tNamedLWLockTranche namedLockTranche;\n\tLWLock clockLock;\n\n\t/* Current logical clock value of this node */\n\tClusterClock clusterClockValue;\n\n\t/* Tracks initialization at boot */\n\tClockState clockInitialized;\n} LogicalClockShmemData;\n\n\nstatic shmem_startup_hook_type prev_shmem_startup_hook = NULL;\nstatic LogicalClockShmemData *LogicalClockShmem = NULL;\nstatic void AdjustLocalClock(ClusterClock *remoteClock);\nstatic void GetNextNodeClockValue(ClusterClock *nextClusterClockValue);\nstatic ClusterClock * GetHighestClockInTransaction(List *nodeConnectionList);\nstatic void AdjustClocksToTransactionHighest(List *nodeConnectionList,\n\t\t\t\t\t\t\t\t\t\t\t ClusterClock *transactionClockValue);\nstatic void InitClockAtFirstUse(void);\nstatic void IncrementClusterClock(ClusterClock *clusterClock);\nstatic ClusterClock * LargerClock(ClusterClock *clock1, ClusterClock *clock2);\nstatic ClusterClock * PrepareAndSetTransactionClock(void);\nbool EnableClusterClock = true;\n\n\n/*\n * GetEpochTimeAsClock returns the epoch value milliseconds used as logical\n * value in ClusterClock.\n */\nClusterClock *\nGetEpochTimeAsClock(void)\n{\n\tstruct timeval tp = { 0 };\n\n\tgettimeofday(&tp, NULL);\n\n\tuint64 result = (uint64) (tp.tv_sec) * 1000;\n\tresult = result + (uint64) (tp.tv_usec) / 1000;\n\n\tClusterClock *epochClock = (ClusterClock *) palloc(sizeof(ClusterClock));\n\tepochClock->logical = result;\n\tepochClock->counter = 0;\n\n\treturn epochClock;\n}\n\n\n/*\n * LogicalClockShmemSize returns the size that should be allocated\n * in the shared memory for logical clock management.\n */\nsize_t\nLogicalClockShmemSize(void)\n{\n\tSize size = 0;\n\n\tsize = add_size(size, sizeof(LogicalClockShmemData));\n\n\treturn size;\n}\n\n\n/*\n * InitializeClusterClockMem reserves shared-memory space needed to\n * store LogicalClockShmemData, and sets the hook for initialization\n * of the same.\n */\nvoid\nInitializeClusterClockMem(void)\n{\n\tprev_shmem_startup_hook = shmem_startup_hook;\n\tshmem_startup_hook = LogicalClockShmemInit;\n}\n\n\n/*\n * LogicalClockShmemInit Allocates and initializes shared memory for\n * cluster clock related variables.\n */\nvoid\nLogicalClockShmemInit(void)\n{\n\tbool alreadyInitialized = false;\n\n\tLWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);\n\n\tLogicalClockShmem = (LogicalClockShmemData *)\n\t\t\t\t\t\tShmemInitStruct(\"Logical Clock Shmem\",\n\t\t\t\t\t\t\t\t\t\tLogicalClockShmemSize(),\n\t\t\t\t\t\t\t\t\t\t&alreadyInitialized);\n\n\tif (!alreadyInitialized)\n\t{\n\t\t/* A zero value indicates that the clock is not adjusted yet */\n\t\tmemset(&LogicalClockShmem->clusterClockValue, 0, sizeof(ClusterClock));\n\n\t\tLogicalClockShmem->namedLockTranche.trancheName = \"Cluster Clock Setup Tranche\";\n\t\tLogicalClockShmem->namedLockTranche.trancheId = LWLockNewTrancheId();\n\t\tLWLockRegisterTranche(LogicalClockShmem->namedLockTranche.trancheId,\n\t\t\t\t\t\t\t  LogicalClockShmem->namedLockTranche.trancheName);\n\t\tLWLockInitialize(&LogicalClockShmem->clockLock,\n\t\t\t\t\t\t LogicalClockShmem->namedLockTranche.trancheId);\n\n\t\tLogicalClockShmem->clockInitialized = CLOCKSTATE_UNINITIALIZED;\n\t}\n\n\tLWLockRelease(AddinShmemInitLock);\n\n\tif (prev_shmem_startup_hook != NULL)\n\t{\n\t\tprev_shmem_startup_hook();\n\t}\n}\n\n\n/*\n * IncrementClusterClock increments the ClusterClock by 1.\n */\nstatic void\nIncrementClusterClock(ClusterClock *clusterClock)\n{\n\t/*\n\t * It's the counter that always ticks, once it reaches the maximum, reset\n\t * the counter to 1 and increment the logical clock.\n\t */\n\tif (clusterClock->counter == MAX_COUNTER)\n\t{\n\t\tclusterClock->logical++;\n\t\tclusterClock->counter = 0;\n\t\treturn;\n\t}\n\n\tclusterClock->counter++;\n}\n\n\n/*\n * LargerClock compares two ClusterClock(s) and returns pointer to the larger one.\n * Note: If equal or one of the clock is NULL, non NULL clock is copied.\n */\nstatic ClusterClock *\nLargerClock(ClusterClock *clock1, ClusterClock *clock2)\n{\n\t/* Check if one of the paramater is NULL */\n\tif (!clock1 || !clock2)\n\t{\n\t\tAssert(clock1 || clock2);\n\t\treturn (!clock1 ? clock2 : clock1);\n\t}\n\n\tif (cluster_clock_cmp_internal(clock1, clock2) > 0)\n\t{\n\t\treturn clock1;\n\t}\n\telse\n\t{\n\t\treturn clock2;\n\t}\n}\n\n\n/*\n * GetNextNodeClock implements the internal guts of the UDF citus_get_node_clock()\n */\nstatic void\nGetNextNodeClockValue(ClusterClock *nextClusterClockValue)\n{\n\tstatic bool isClockInitChecked = false; /* serves as a local cache */\n\tClusterClock *epochValue = GetEpochTimeAsClock();\n\n\t/* If this backend already checked for initialization, skip it */\n\tif (!isClockInitChecked)\n\t{\n\t\tInitClockAtFirstUse();\n\n\t\t/* We reach here only if CLOCKSTATE_INITIALIZED, all other cases error out. */\n\t\tisClockInitChecked = true;\n\t}\n\n\tLWLockAcquire(&LogicalClockShmem->clockLock, LW_EXCLUSIVE);\n\n\tAssert(LogicalClockShmem->clockInitialized == CLOCKSTATE_INITIALIZED);\n\n\t/* Tick the clock */\n\tIncrementClusterClock(&LogicalClockShmem->clusterClockValue);\n\n\t/* Pick the larger of the two, wallclock and logical clock. */\n\tClusterClock *clockValue = LargerClock(&LogicalClockShmem->clusterClockValue,\n\t\t\t\t\t\t\t\t\t\t   epochValue);\n\n\t/*\n\t * Save the returned value in both the shared memory and sequences.\n\t */\n\tSAVE_AND_PERSIST(clockValue);\n\n\tLWLockRelease(&LogicalClockShmem->clockLock);\n\n\t/* Return the clock */\n\t*nextClusterClockValue = *clockValue;\n}\n\n\n/*\n * AdjustLocalClock Adjusts the local shared memory clock to the\n * received value from the remote node.\n */\nvoid\nAdjustLocalClock(ClusterClock *remoteClock)\n{\n\tLWLockAcquire(&LogicalClockShmem->clockLock, LW_EXCLUSIVE);\n\n\tClusterClock *localClock = &LogicalClockShmem->clusterClockValue;\n\n\t/* local clock is ahead or equal, do nothing */\n\tif (cluster_clock_cmp_internal(localClock, remoteClock) >= 0)\n\t{\n\t\tLWLockRelease(&LogicalClockShmem->clockLock);\n\t\treturn;\n\t}\n\n\tSAVE_AND_PERSIST(remoteClock);\n\n\tLWLockRelease(&LogicalClockShmem->clockLock);\n\n\tereport(DEBUG1, (errmsg(\"adjusted to remote clock: \"\n\t\t\t\t\t\t\t\"<logical(%lu) counter(%u)>\",\n\t\t\t\t\t\t\tremoteClock->logical,\n\t\t\t\t\t\t\tremoteClock->counter)));\n}\n\n\n/*\n * GetHighestClockInTransaction takes the connection list of participating nodes in the\n * current transaction and polls the logical clock value of all the nodes. Returns the\n * highest logical clock value of all the nodes in the current distributed transaction,\n * which may be used as commit order for individual objects in the transaction.\n */\nstatic ClusterClock *\nGetHighestClockInTransaction(List *nodeConnectionList)\n{\n\tMultiConnection *connection = NULL;\n\n\tforeach_declared_ptr(connection, nodeConnectionList)\n\t{\n\t\tint querySent =\n\t\t\tSendRemoteCommand(connection, \"SELECT citus_get_node_clock();\");\n\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tReportConnectionError(connection, ERROR);\n\t\t}\n\t}\n\n\t/* Check for the current node */\n\tClusterClock *globalClockValue = (ClusterClock *) palloc(sizeof(ClusterClock));\n\n\tGetNextNodeClockValue(globalClockValue);\n\n\tereport(DEBUG1, (errmsg(\"node(%u) transaction clock %lu:%u\",\n\t\t\t\t\t\t\tPostPortNumber, globalClockValue->logical,\n\t\t\t\t\t\t\tglobalClockValue->counter)));\n\n\t/* fetch the results and pick the highest clock value of all the nodes */\n\tforeach_declared_ptr(connection, nodeConnectionList)\n\t{\n\t\tbool raiseInterrupts = true;\n\n\t\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"connection to %s:%d failed when \"\n\t\t\t\t\t\t\t\t   \"fetching logical clock value\",\n\t\t\t\t\t\t\t\t   connection->hostname, connection->port)));\n\t\t}\n\n\t\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\t\tif (!IsResponseOK(result))\n\t\t{\n\t\t\tReportResultError(connection, result, ERROR);\n\t\t}\n\n\t\tClusterClock *nodeClockValue = ParseClusterClockPGresult(result, 0, 0);\n\n\t\tereport(DEBUG1, (errmsg(\"node(%u) transaction clock %lu:%u\",\n\t\t\t\t\t\t\t\tconnection->port, nodeClockValue->logical,\n\t\t\t\t\t\t\t\tnodeClockValue->counter)));\n\n\t\tglobalClockValue = LargerClock(globalClockValue, nodeClockValue);\n\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\t}\n\n\tereport(DEBUG1, (errmsg(\"final global transaction clock %lu:%u\",\n\t\t\t\t\t\t\tglobalClockValue->logical,\n\t\t\t\t\t\t\tglobalClockValue->counter)));\n\treturn globalClockValue;\n}\n\n\n/*\n * AdjustClocksToTransactionHighest Sets the clock value of all the nodes, participated\n * in the PREPARE of the transaction, to the highest clock value of all the nodes.\n */\nstatic void\nAdjustClocksToTransactionHighest(List *nodeConnectionList,\n\t\t\t\t\t\t\t\t ClusterClock *transactionClockValue)\n{\n\tStringInfo queryToSend = makeStringInfo();\n\n\t/* Set the clock value on participating worker nodes */\n\tappendStringInfo(queryToSend,\n\t\t\t\t\t \"SELECT citus_internal.adjust_local_clock_to_remote\"\n\t\t\t\t\t \"('(%lu, %u)'::pg_catalog.cluster_clock);\",\n\t\t\t\t\t transactionClockValue->logical, transactionClockValue->counter);\n\n\tExecuteRemoteCommandInConnectionList(nodeConnectionList, queryToSend->data);\n\tAdjustLocalClock(transactionClockValue);\n}\n\n\n/*\n * PrepareAndSetTransactionClock polls all the transaction-nodes for their respective clocks,\n * picks the highest clock and returns it via UDF citus_get_transaction_clock. All the nodes\n * will now move to this newly negotiated clock.\n */\nstatic ClusterClock *\nPrepareAndSetTransactionClock(void)\n{\n\tif (!EnableClusterClock)\n\t{\n\t\t/* citus.enable_cluster_clock is false */\n\t\tereport(WARNING, (errmsg(\"GUC enable_cluster_clock is off\")));\n\t\treturn NULL;\n\t}\n\n\tdlist_iter iter;\n\tList *transactionNodeList = NIL;\n\tList *nodeList = NIL;\n\n\t/* Prepare the connection list */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tWorkerNode *workerNode = FindWorkerNode(connection->hostname, connection->port);\n\t\tif (!workerNode)\n\t\t{\n\t\t\tereport(WARNING, errmsg(\"Worker node is missing\"));\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* Skip the node if we already in the list */\n\t\tif (list_member_int(nodeList, workerNode->groupId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t\tAssert(transaction->transactionState != REMOTE_TRANS_NOT_STARTED);\n\n\t\t/* Skip a transaction that failed */\n\t\tif (transaction->transactionFailed)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tnodeList = lappend_int(nodeList, workerNode->groupId);\n\t\ttransactionNodeList = lappend(transactionNodeList, connection);\n\t}\n\n\t/* Pick the highest logical clock value among all transaction-nodes */\n\tClusterClock *transactionClockValue =\n\t\tGetHighestClockInTransaction(transactionNodeList);\n\n\t/* Adjust all the nodes with the new clock value */\n\tAdjustClocksToTransactionHighest(transactionNodeList, transactionClockValue);\n\n\treturn transactionClockValue;\n}\n\n\n/*\n * InitClockAtFirstUse Initializes the shared memory clock value to the highest clock\n * persisted. This will protect from any clock drifts.\n */\nstatic void\nInitClockAtFirstUse(void)\n{\n\tLWLockAcquire(&LogicalClockShmem->clockLock, LW_EXCLUSIVE);\n\n\t/* Avoid repeated and parallel initialization */\n\tif (LogicalClockShmem->clockInitialized == CLOCKSTATE_INITIALIZED)\n\t{\n\t\tLWLockRelease(&LogicalClockShmem->clockLock);\n\t\treturn;\n\t}\n\n\tif (DistClockLogicalSequenceId() == InvalidOid)\n\t{\n\t\tereport(ERROR, (errmsg(\"Clock related sequence is missing\")));\n\t}\n\n\t/* Start with the wall clock value */\n\tClusterClock *epochValue = GetEpochTimeAsClock();\n\tLogicalClockShmem->clusterClockValue = *epochValue;\n\n\t/*  Retrieve the highest clock value persisted in the sequence */\n\tClusterClock persistedMaxClock = { 0 };\n\n\t/*\n\t * We will get one more than the persisted value, but that's harmless and\n\t * also very _crucial_ in below scenarios\n\t *\n\t * 1) As sequences are not transactional, this will protect us from crashes\n\t *    after the logical increment and before the counter increment.\n\t *\n\t * 2) If a clock drifts backwards, we should always start one clock above\n\t *    the previous value, though we are not persisting the counter as the\n\t *    logical value supersedes the counter, a simple increment of it will\n\t *    protect us.\n\t *\n\t * Note: The first (and every 32nd) call to nextval() consumes 32 values in the\n\t * WAL. This is an optimization that postgres does to only have to write a WAL\n\t * entry every 32 invocations. Normally this is harmless, however, if the database\n\t * gets in a crashloop it could outrun the wall clock, if the database crashes at\n\t * a higher rate than once every 32 seconds.\n\t *\n\t */\n\tOid saveUserId = InvalidOid;\n\tint savedSecurityCtx = 0;\n\n\tGetUserIdAndSecContext(&saveUserId, &savedSecurityCtx);\n\tSetUserIdAndSecContext(CitusExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE);\n\n\tpersistedMaxClock.logical =\n\t\tDirectFunctionCall1(nextval_oid, ObjectIdGetDatum(DistClockLogicalSequenceId()));\n\n\tSetUserIdAndSecContext(saveUserId, savedSecurityCtx);\n\n\t/*\n\t * Sequence 1 indicates no prior clock timestamps on this server, retain\n\t * the wall clock i.e. no adjustment needed.\n\t */\n\tif (persistedMaxClock.logical != 1)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"adjusting the clock with persisted value: \"\n\t\t\t\t\t\t\t\t\"<logical(%lu) and counter(%u)>\",\n\t\t\t\t\t\t\t\tpersistedMaxClock.logical,\n\t\t\t\t\t\t\t\tpersistedMaxClock.counter)));\n\n\t\t/*\n\t\t * Adjust the local clock according to the most recent\n\t\t * clock stamp value persisted in the catalog.\n\t\t */\n\t\tif (cluster_clock_cmp_internal(&persistedMaxClock, epochValue) > 0)\n\t\t{\n\t\t\tSAVE_AND_PERSIST(&persistedMaxClock);\n\t\t\tereport(NOTICE, (errmsg(\"clock drifted backwards, adjusted to: \"\n\t\t\t\t\t\t\t\t\t\"<logical(%lu) counter(%u)>\",\n\t\t\t\t\t\t\t\t\tpersistedMaxClock.logical,\n\t\t\t\t\t\t\t\t\tpersistedMaxClock.counter)));\n\t\t}\n\t}\n\n\tLogicalClockShmem->clockInitialized = CLOCKSTATE_INITIALIZED;\n\n\tLWLockRelease(&LogicalClockShmem->clockLock);\n}\n\n\n/*\n * citus_get_node_clock() is an UDF that returns a monotonically increasing\n * logical clock. Clock guarantees to never go back in value after restarts, and\n * makes best attempt to keep the value close to unix epoch time in milliseconds.\n */\nDatum\ncitus_get_node_clock(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tClusterClock *nodeClockValue = (ClusterClock *) palloc(sizeof(ClusterClock));\n\n\tGetNextNodeClockValue(nodeClockValue);\n\n\tPG_RETURN_POINTER(nodeClockValue);\n}\n\n\n/*\n * citus_internal_adjust_local_clock_to_remote is an internal UDF to adjust\n * the local clock to the highest in the cluster.\n */\nDatum\ncitus_internal_adjust_local_clock_to_remote(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tClusterClock *remoteClock = (ClusterClock *) PG_GETARG_POINTER(0);\n\tAdjustLocalClock(remoteClock);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_is_clock_after is an UDF that accepts logical clock timestamps of\n * two causally related events and returns true if the argument1 happened\n * before argument2.\n */\nDatum\ncitus_is_clock_after(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\t/* Fetch both the arguments */\n\tClusterClock *clock1 = (ClusterClock *) PG_GETARG_POINTER(0);\n\tClusterClock *clock2 = (ClusterClock *) PG_GETARG_POINTER(1);\n\n\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t \"clock1 @ LC:%lu, C:%u, \"\n\t\t\t\t\t\t \"clock2 @ LC:%lu, C:%u\",\n\t\t\t\t\t\t clock1->logical, clock1->counter,\n\t\t\t\t\t\t clock2->logical, clock2->counter)));\n\n\tbool result = (cluster_clock_cmp_internal(clock1, clock2) > 0);\n\n\tPG_RETURN_BOOL(result);\n}\n\n\n/*\n * citus_get_transaction_clock() is an UDF that returns a transaction timestamp\n * logical clock. Clock returned is the maximum of all transaction-nodes and the\n * all the nodes adjust to the this new clock value.\n */\nDatum\ncitus_get_transaction_clock(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tClusterClock *clusterClockValue = PrepareAndSetTransactionClock();\n\n\tPG_RETURN_POINTER(clusterClockValue);\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/README.md",
    "content": "# Commands\n\nThe commands module is modeled after `backend/commands` from the postgres repository and\ncontains the logic for Citus on how to run these commands on distributed objects. Even\nthough the structure of the directory has some resemblence to its postgres relative, files\nhere are somewhat more fine-grained. This is due to the nature of citus commands that are\nheavily focused on distributed tables. Instead of having all commands in `tablecmds.c`\nthey are often moved to files that are named after the command.\n\n| File                         | Description |\n|------------------------------|-------------|\n| `create_distributed_table.c` | Implementation of UDF's for creating distributed tables |\n| `drop_distributed_table.c`   | Implementation for dropping metadata for partitions of distributed tables |\n| `extension.c`                | Implementation of `CREATE EXTENSION` commands for citus specific checks |\n| `foreign_constraint.c`       | Implementation of and helper functions for foreign key constraints |\n| `grant.c`                    | Implementation of `GRANT` commands for roles/users on relations |\n| `index.c`                    | Implementation of commands specific to indices on distributed tables |\n| `multi_copy.c`               | Implementation of `COPY` command. There are multiple different copy modes which are described in detail below |\n| `policy.c`                   | Implementation of `CREATE\\ALTER POLICY` commands. |\n| `rename.c`                   | Implementation of `ALTER ... RENAME ...` commands. It implements the renaming of applicable objects, otherwise provides the user with a warning |\n| `schema.c`                   | |\n| `sequence.c`                 | Implementation of `CREATE/ALTER SEQUENCE` commands. Primarily checks correctness of sequence statements as they are not propagated to the worker nodes |\n| `table.c`                    | |\n| `transmit.c`                 | Implementation of `COPY` commands with `format transmit` set in the options. This format is used to transfer files from one node to another node |\n| `truncate.c`                 | Implementation of `TRUNCATE` commands on distributed tables |\n| `utility_hook.c`             | This is the entry point from postgres into the commands module of citus. It contains the implementation that gets registered in postgres' `ProcessUtility_hook` callback to extends the functionality of the original ProcessUtility. This code is used to route the incoming commands to their respective implementation in Citus |\n| `vacuum.c`                   | Implementation of `VACUUM` commands on distributed tables |\n\n# COPY\n\nThe copy command is overloaded for a couple of use-cases specific to citus. The syntax of\nthe command stays the same, however the implementation might slightly differ from the\nstock implementation. The overloading is mostly done via extra options that Citus uses to\nindicate how to operate the copy. The options used are described below.\n\n## FORMAT transmit\n\nImplemented in `transmit.c`\n\nTODO: to be written by someone with enough knowledge to write how, when and why it is used.\n\n## FORMAT result\n\nImplemented in `multi_copy.c`\n\nTODO: to be written by someone with enough knowledge to write how, when and why it is used.\n"
  },
  {
    "path": "src/backend/distributed/commands/alter_table.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * alter_table.c\n *\t  Routines related to the altering of tables.\n *\n *\t\tThere are three UDFs defined in this file:\n *\t\tundistribute_table:\n *\t\t\tTurns a distributed table to a local table\n *\t\talter_distributed_table:\n *\t\t\tAlters distribution_column, shard_count or colocate_with\n *\t\t\tproperties of a distributed table\n *\t\talter_table_set_access_method:\n *\t\t\tChanges the access method of a table\n *\n *\t\tAll three methods work in similar steps:\n *\t\t\t- Create a new table the required way (with a different\n *\t\t\t  shard count, distribution column, colocate with value,\n *\t\t\t  access method or local)\n *\t\t\t- Move everything to the new table\n *\t\t\t- Drop the old one\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"access/hash.h\"\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_rewrite_d.h\"\n#include \"commands/defrem.h\"\n#include \"executor/spi.h\"\n#include \"nodes/pg_list.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"columnar/columnar.h\"\n#include \"columnar/columnar_tableam.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/distribution_column.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/replication_origin_session_utils.h\"\n#include \"distributed/shard_utils.h\"\n#include \"distributed/shared_library_init.h\"\n#include \"distributed/tenant_schema_metadata.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_transaction.h\"\n\n\n/* Table Conversion Types */\n#define UNDISTRIBUTE_TABLE 'u'\n#define ALTER_DISTRIBUTED_TABLE 'a'\n#define ALTER_TABLE_SET_ACCESS_METHOD 'm'\n\n#define UNDISTRIBUTE_TABLE_CASCADE_HINT \\\n\t\t\"Use cascade option to undistribute all the relations involved in \" \\\n\t\t\"a foreign key relationship with %s by executing SELECT \" \\\n\t\t\"undistribute_table($$%s$$, cascade_via_foreign_keys=>true)\"\n\n\ntypedef TableConversionReturn *(*TableConversionFunction)(struct\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  TableConversionParameters *);\n\n\n/*\n * TableConversionState objects are used for table conversion functions:\n * UndistributeTable, AlterDistributedTable, AlterTableSetAccessMethod.\n *\n * They can be created using TableConversionParameters objects with\n * CreateTableConversion function.\n *\n * TableConversionState objects include everything TableConversionParameters\n * objects do and some extra to be used in the conversion process.\n */\ntypedef struct TableConversionState\n{\n\t/*\n\t * Determines type of conversion: UNDISTRIBUTE_TABLE,\n\t * ALTER_DISTRIBUTED_TABLE, ALTER_TABLE_SET_ACCESS_METHOD.\n\t */\n\tchar conversionType;\n\n\t/* Oid of the table to do conversion on */\n\tOid relationId;\n\n\t/*\n\t * Options to do conversions on the table\n\t * distributionColumn is the name of the new distribution column,\n\t * shardCountIsNull is if the shardCount variable is not given\n\t * shardCount is the new shard count,\n\t * colocateWith is the name of the table to colocate with, 'none', or\n\t * 'default'\n\t * accessMethod is the name of the new accessMethod for the table\n\t */\n\tchar *distributionColumn;\n\tbool shardCountIsNull;\n\tint shardCount;\n\tchar *colocateWith;\n\tchar *accessMethod;\n\n\t/*\n\t * cascadeToColocated determines whether the shardCount and\n\t * colocateWith will be cascaded to the currently colocated tables\n\t */\n\tCascadeToColocatedOption cascadeToColocated;\n\n\t/*\n\t * cascadeViaForeignKeys determines if the conversion operation\n\t * will be cascaded to the graph connected with foreign keys\n\t * to the table\n\t */\n\tbool cascadeViaForeignKeys;\n\n\n\t/* schema of the table */\n\tchar *schemaName;\n\tOid schemaId;\n\n\t/* name of the table */\n\tchar *relationName;\n\n\t/* new relation oid after the conversion */\n\tOid newRelationId;\n\n\t/* temporary name for intermediate table */\n\tchar *tempName;\n\n\t/*hash that is appended to the name to create tempName */\n\tuint32 hashOfName;\n\n\t/* shard count of the table before conversion */\n\tint originalShardCount;\n\n\t/* list of the table oids of tables colocated with the table before conversion */\n\tList *colocatedTableList;\n\n\t/* new distribution key, if distributionColumn variable is given */\n\tVar *distributionKey;\n\n\t/* distribution key of the table before conversion */\n\tVar *originalDistributionKey;\n\n\t/* access method name of the table before conversion */\n\tchar *originalAccessMethod;\n\n\t/*\n\t * The function that will be used for the conversion\n\t * Must comply with conversionType\n\t * UNDISTRIBUTE_TABLE -> UndistributeTable\n\t * ALTER_DISTRIBUTED_TABLE -> AlterDistributedTable\n\t * ALTER_TABLE_SET_ACCESS_METHOD -> AlterTableSetAccessMethod\n\t */\n\tTableConversionFunction function;\n\n\t/*\n\t * suppressNoticeMessages determines if we want to suppress NOTICE\n\t * messages that we explicitly issue\n\t */\n\tbool suppressNoticeMessages;\n} TableConversionState;\n\n\nstatic TableConversionReturn * AlterDistributedTable(TableConversionParameters *params);\nstatic TableConversionReturn * AlterTableSetAccessMethod(TableConversionParameters *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t params);\nstatic TableConversionReturn * ConvertTable(TableConversionState *con);\nstatic TableConversionReturn * ConvertTableInternal(TableConversionState *con);\nstatic bool SwitchToSequentialAndLocalExecutionIfShardNameTooLong(char *relationName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  char *longestShardName);\nstatic void DropIndexesNotSupportedByColumnar(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t  bool suppressNoticeMessages);\nstatic char * GetIndexAccessMethodName(Oid indexId);\nstatic void DropConstraintRestrict(Oid relationId, Oid constraintId);\nstatic void DropIndexRestrict(Oid indexId);\nstatic void EnsureTableNotReferencing(Oid relationId, char conversionType);\nstatic void EnsureTableNotReferenced(Oid relationId, char conversionType);\nstatic void EnsureTableNotForeign(Oid relationId);\nstatic void EnsureTableNotPartition(Oid relationId);\nstatic void ErrorIfColocateWithTenantTable(char *colocateWith);\nstatic TableConversionState * CreateTableConversion(TableConversionParameters *params);\nstatic void CreateDistributedTableLike(TableConversionState *con);\nstatic void CreateCitusTableLike(TableConversionState *con);\nstatic void ReplaceTable(Oid sourceId, Oid targetId, List *justBeforeDropCommands,\n\t\t\t\t\t\t bool suppressNoticeMessages);\nstatic bool HasAnyGeneratedStoredColumns(Oid relationId);\nstatic List * GetNonGeneratedStoredColumnNameList(Oid relationId);\nstatic void CheckAlterDistributedTableConversionParameters(TableConversionState *con);\nstatic char * CreateWorkerChangeSequenceDependencyCommand(char *qualifiedSequeceName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  char *qualifiedSourceName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  char *qualifiedTargetName);\nstatic void ErrorIfMatViewSizeExceedsTheLimit(Oid matViewOid);\nstatic char * CreateMaterializedViewDDLCommand(Oid matViewOid);\nstatic char * GetAccessMethodForMatViewIfExists(Oid viewOid);\nstatic bool WillRecreateFKeyToReferenceTable(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t CascadeToColocatedOption cascadeOption);\nstatic void WarningsForDroppingForeignKeysWithDistributedTables(Oid relationId);\nstatic void ErrorIfUnsupportedCascadeObjects(Oid relationId);\nstatic List * WrapTableDDLCommands(List *commandStrings);\nstatic bool DoesCascadeDropUnsupportedObject(Oid classId, Oid id, HTAB *nodeMap);\nstatic TableConversionReturn * CopyTableConversionReturnIntoCurrentContext(\n\tTableConversionReturn *tableConversionReturn);\n\nPG_FUNCTION_INFO_V1(undistribute_table);\nPG_FUNCTION_INFO_V1(alter_distributed_table);\nPG_FUNCTION_INFO_V1(alter_table_set_access_method);\nPG_FUNCTION_INFO_V1(worker_change_sequence_dependency);\n\n/* global variable keeping track of whether we are in a table type conversion function */\nbool InTableTypeConversionFunctionCall = false;\n\n/* controlled by GUC, in MB */\nint MaxMatViewSizeToAutoRecreate = 1024;\n\n/*\n * undistribute_table gets a distributed table name and\n * udistributes it.\n */\nDatum\nundistribute_table(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\tbool cascadeViaForeignKeys = PG_GETARG_BOOL(1);\n\n\tTableConversionParameters params = {\n\t\t.relationId = relationId,\n\t\t.cascadeViaForeignKeys = cascadeViaForeignKeys,\n\t\t.bypassTenantCheck = false\n\t};\n\n\tUndistributeTable(&params);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * alter_distributed_table gets a distributed table and some other\n * parameters and alters some properties of the table according to\n * the parameters.\n */\nDatum\nalter_distributed_table(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\n\tchar *distributionColumn = NULL;\n\tif (!PG_ARGISNULL(1))\n\t{\n\t\ttext *distributionColumnText = PG_GETARG_TEXT_P(1);\n\t\tdistributionColumn = text_to_cstring(distributionColumnText);\n\t}\n\n\tint shardCount = 0;\n\tbool shardCountIsNull = true;\n\tif (!PG_ARGISNULL(2))\n\t{\n\t\tshardCount = PG_GETARG_INT32(2);\n\t\tshardCountIsNull = false;\n\t}\n\n\tchar *colocateWith = NULL;\n\tif (!PG_ARGISNULL(3))\n\t{\n\t\ttext *colocateWithText = PG_GETARG_TEXT_P(3);\n\t\tcolocateWith = text_to_cstring(colocateWithText);\n\t}\n\n\tCascadeToColocatedOption cascadeToColocated = CASCADE_TO_COLOCATED_UNSPECIFIED;\n\tif (!PG_ARGISNULL(4))\n\t{\n\t\tif (PG_GETARG_BOOL(4) == true)\n\t\t{\n\t\t\tcascadeToColocated = CASCADE_TO_COLOCATED_YES;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcascadeToColocated = CASCADE_TO_COLOCATED_NO;\n\t\t}\n\t}\n\n\tTableConversionParameters params = {\n\t\t.relationId = relationId,\n\t\t.distributionColumn = distributionColumn,\n\t\t.shardCountIsNull = shardCountIsNull,\n\t\t.shardCount = shardCount,\n\t\t.colocateWith = colocateWith,\n\t\t.cascadeToColocated = cascadeToColocated\n\t};\n\n\tAlterDistributedTable(&params);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * alter_table_set_access_method gets a distributed table and an access\n * method and changes table's access method into that.\n */\nDatum\nalter_table_set_access_method(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\n\ttext *accessMethodText = PG_GETARG_TEXT_P(1);\n\tchar *accessMethod = text_to_cstring(accessMethodText);\n\n\tTableConversionParameters params = {\n\t\t.relationId = relationId,\n\t\t.accessMethod = accessMethod\n\t};\n\n\tAlterTableSetAccessMethod(&params);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * worker_change_sequence_dependency is a wrapper UDF for\n * changeDependencyFor function\n */\nDatum\nworker_change_sequence_dependency(PG_FUNCTION_ARGS)\n{\n\tOid sequenceOid = PG_GETARG_OID(0);\n\tOid sourceRelationOid = PG_GETARG_OID(1);\n\tOid targetRelationOid = PG_GETARG_OID(2);\n\n\tchangeDependencyFor(RelationRelationId, sequenceOid,\n\t\t\t\t\t\tRelationRelationId, sourceRelationOid, targetRelationOid);\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * DropFKeysAndUndistributeTable drops all foreign keys that relation with\n * relationId is involved then undistributes it.\n * Note that as UndistributeTable changes relationId of relation, this\n * function also returns new relationId of relation.\n * Also note that callers are responsible for storing & recreating foreign\n * keys to be dropped if needed.\n */\nOid\nDropFKeysAndUndistributeTable(Oid relationId)\n{\n\tDropFKeysRelationInvolvedWithTableType(relationId, INCLUDE_ALL_TABLE_TYPES);\n\n\t/* store them before calling UndistributeTable as it changes relationId */\n\tchar *relationName = get_rel_name(relationId);\n\tOid schemaId = get_rel_namespace(relationId);\n\n\t/* suppress notices messages not to be too verbose */\n\tTableConversionParameters params = {\n\t\t.relationId = relationId,\n\t\t.cascadeViaForeignKeys = false,\n\t\t.suppressNoticeMessages = true\n\t};\n\tUndistributeTable(&params);\n\n\tOid newRelationId = get_relname_relid(relationName, schemaId);\n\n\t/*\n\t * We don't expect this to happen but to be on the safe side let's error\n\t * out here.\n\t */\n\tEnsureRelationExists(newRelationId);\n\n\treturn newRelationId;\n}\n\n\n/*\n * UndistributeTables undistributes given relations. It first collects all foreign keys\n * to recreate them after the undistribution. Then, drops the foreign keys and\n * undistributes the relations. Finally, it recreates foreign keys.\n */\nvoid\nUndistributeTables(List *relationIdList)\n{\n\t/*\n\t * Collect foreign keys for recreation and then drop fkeys and undistribute\n\t * tables.\n\t */\n\tList *originalForeignKeyRecreationCommands = NIL;\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tList *fkeyCommandsForRelation =\n\t\t\tGetFKeyCreationCommandsRelationInvolvedWithTableType(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t INCLUDE_ALL_TABLE_TYPES);\n\t\toriginalForeignKeyRecreationCommands = list_concat(\n\t\t\toriginalForeignKeyRecreationCommands, fkeyCommandsForRelation);\n\t\tDropFKeysAndUndistributeTable(relationId);\n\t}\n\n\t/* We can skip foreign key validations as we are sure about them at start */\n\tbool skip_validation = true;\n\tExecuteForeignKeyCreateCommandList(originalForeignKeyRecreationCommands,\n\t\t\t\t\t\t\t\t\t   skip_validation);\n}\n\n\n/*\n * EnsureUndistributeTenantTableSafe ensures that it is safe to undistribute a tenant table.\n */\nvoid\nEnsureUndistributeTenantTableSafe(Oid relationId, const char *operationName)\n{\n\tOid schemaId = get_rel_namespace(relationId);\n\tAssert(IsTenantSchema(schemaId));\n\n\t/* We only allow undistribute while altering schema */\n\tif (strcmp(operationName, TenantOperationNames[TENANT_SET_SCHEMA]) != 0)\n\t{\n\t\tErrorIfTenantTable(relationId, operationName);\n\t}\n\n\tchar *tableName = get_rel_name(relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\n\t/*\n\t * Partition table cannot be undistributed. Otherwise, its parent table would still\n\t * be a tenant table whereas partition table would be a local table.\n\t */\n\tif (PartitionTable(relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"%s is not allowed for partition table %s in distributed \"\n\t\t\t\t\t\t\t   \"schema %s\", operationName, tableName, schemaName),\n\t\t\t\t\t\terrdetail(\"partition table should be under the same distributed \"\n\t\t\t\t\t\t\t\t  \"schema as its parent and be a \"\n\t\t\t\t\t\t\t\t  \"distributed schema table.\")));\n\t}\n\n\t/*\n\t * When table is referenced by or referencing to a table in the same tenant\n\t * schema, we should disallow undistributing the table since we do not allow\n\t * foreign keys from/to Citus local or Postgres local table to/from distributed\n\t * schema.\n\t */\n\tList *fkeyCommandsWithSingleShardTables =\n\t\tGetFKeyCreationCommandsRelationInvolvedWithTableType(\n\t\t\trelationId, INCLUDE_SINGLE_SHARD_TABLES);\n\tif (fkeyCommandsWithSingleShardTables != NIL)\n\t{\n\t\tereport(ERROR, (errmsg(\"%s is not allowed for table %s in distributed schema %s\",\n\t\t\t\t\t\t\t   operationName, tableName, schemaName),\n\t\t\t\t\t\terrdetail(\"distributed schemas cannot have foreign keys from/to \"\n\t\t\t\t\t\t\t\t  \"local tables or different schema\")));\n\t}\n}\n\n\n/*\n * UndistributeTable undistributes the given table. It uses ConvertTable function to\n * create a new local table and move everything to that table.\n *\n * The local tables, tables with references, partition tables and foreign tables are\n * not supported. The function gives errors in these cases.\n */\nTableConversionReturn *\nUndistributeTable(TableConversionParameters *params)\n{\n\tEnsureCoordinator();\n\tEnsureRelationExists(params->relationId);\n\tEnsureTableOwner(params->relationId);\n\n\tif (!IsCitusTable(params->relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot undistribute table \"\n\t\t\t\t\t\t\t   \"because the table is not distributed\")));\n\t}\n\n\tOid schemaId = get_rel_namespace(params->relationId);\n\tif (!params->bypassTenantCheck && IsTenantSchema(schemaId) &&\n\t\tIsCitusTableType(params->relationId, SINGLE_SHARD_DISTRIBUTED))\n\t{\n\t\tEnsureUndistributeTenantTableSafe(\n\t\t\tparams->relationId,\n\t\t\tTenantOperationNames[TENANT_UNDISTRIBUTE_TABLE]);\n\t}\n\n\tif (!params->cascadeViaForeignKeys)\n\t{\n\t\tEnsureTableNotReferencing(params->relationId, UNDISTRIBUTE_TABLE);\n\t\tEnsureTableNotReferenced(params->relationId, UNDISTRIBUTE_TABLE);\n\t}\n\n\tEnsureTableNotPartition(params->relationId);\n\n\tif (PartitionedTable(params->relationId))\n\t{\n\t\tList *partitionList = PartitionList(params->relationId);\n\n\t\t/*\n\t\t * This is a less common pattern where foreing key is directly from/to\n\t\t * the partition relation as we already handled inherited foreign keys\n\t\t * on partitions either by erroring out or cascading via foreign keys.\n\t\t * It seems an acceptable limitation for now to ask users to drop such\n\t\t * foreign keys manually.\n\t\t */\n\t\tErrorIfAnyPartitionRelationInvolvedInNonInheritedFKey(partitionList);\n\t}\n\n\tErrorIfUnsupportedCascadeObjects(params->relationId);\n\n\tparams->conversionType = UNDISTRIBUTE_TABLE;\n\tparams->shardCountIsNull = true;\n\tTableConversionState *con = CreateTableConversion(params);\n\n\tSetupReplicationOriginLocalSession();\n\tTableConversionReturn *conv = ConvertTable(con);\n\tResetReplicationOriginLocalSession();\n\treturn conv;\n}\n\n\n/*\n * AlterDistributedTable changes some properties of the given table. It uses\n * ConvertTable function to create a new local table and move everything to that table.\n *\n * The local and reference tables, tables with references, partition tables and foreign\n * tables are not supported. The function gives errors in these cases.\n */\nTableConversionReturn *\nAlterDistributedTable(TableConversionParameters *params)\n{\n\tEnsureCoordinator();\n\tEnsureRelationExists(params->relationId);\n\tEnsureTableOwner(params->relationId);\n\n\tif (!IsCitusTableType(params->relationId, DISTRIBUTED_TABLE))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot alter table because the table \"\n\t\t\t\t\t\t\t   \"is not distributed\")));\n\t}\n\n\tErrorIfTenantTable(params->relationId, TenantOperationNames[TENANT_ALTER_TABLE]);\n\tErrorIfColocateWithTenantTable(params->colocateWith);\n\n\tEnsureTableNotForeign(params->relationId);\n\tEnsureTableNotPartition(params->relationId);\n\tEnsureHashDistributedTable(params->relationId);\n\n\tErrorIfUnsupportedCascadeObjects(params->relationId);\n\n\tparams->conversionType = ALTER_DISTRIBUTED_TABLE;\n\tTableConversionState *con = CreateTableConversion(params);\n\tCheckAlterDistributedTableConversionParameters(con);\n\n\tif (WillRecreateFKeyToReferenceTable(con->relationId, con->cascadeToColocated))\n\t{\n\t\tereport(DEBUG1, (errmsg(\"setting multi shard modify mode to sequential\")));\n\t\tSetLocalMultiShardModifyModeToSequential();\n\t}\n\n\treturn ConvertTable(con);\n}\n\n\n/*\n * AlterTableSetAccessMethod changes the access method of the given table. It uses\n * ConvertTable function to create a new table with the access method and move everything\n * to that table.\n *\n * The local and references tables, tables with references, partition tables and foreign\n * tables are not supported. The function gives errors in these cases.\n */\nTableConversionReturn *\nAlterTableSetAccessMethod(TableConversionParameters *params)\n{\n\tEnsureRelationExists(params->relationId);\n\tEnsureTableOwner(params->relationId);\n\n\tif (IsCitusTable(params->relationId))\n\t{\n\t\tEnsureCoordinator();\n\t}\n\n\tEnsureTableNotReferencing(params->relationId, ALTER_TABLE_SET_ACCESS_METHOD);\n\tEnsureTableNotReferenced(params->relationId, ALTER_TABLE_SET_ACCESS_METHOD);\n\tEnsureTableNotForeign(params->relationId);\n\n\tif (!IsCitusTableType(params->relationId, SINGLE_SHARD_DISTRIBUTED) &&\n\t\tIsCitusTableType(params->relationId, DISTRIBUTED_TABLE))\n\t{\n\t\t/* we do not support non-hash distributed tables, except single shard tables */\n\t\tEnsureHashDistributedTable(params->relationId);\n\t}\n\n\tif (PartitionedTable(params->relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"you cannot alter access method of a partitioned table\")));\n\t}\n\n\tif (get_rel_relkind(params->relationId) == RELKIND_VIEW)\n\t{\n\t\tereport(ERROR, (errmsg(\"you cannot alter access method of a view\")));\n\t}\n\n\tif (PartitionTable(params->relationId) &&\n\t\tIsCitusTableType(params->relationId, DISTRIBUTED_TABLE))\n\t{\n\t\tOid parentRelationId = PartitionParentOid(params->relationId);\n\t\tif (HasForeignKeyToReferenceTable(parentRelationId))\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"setting multi shard modify mode to sequential\")));\n\t\t\tSetLocalMultiShardModifyModeToSequential();\n\t\t}\n\t}\n\n\tErrorIfUnsupportedCascadeObjects(params->relationId);\n\n\tparams->conversionType = ALTER_TABLE_SET_ACCESS_METHOD;\n\tparams->shardCountIsNull = true;\n\tTableConversionState *con = CreateTableConversion(params);\n\n\tif (strcmp(con->originalAccessMethod, con->accessMethod) == 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"the access method of %s is already %s\",\n\t\t\t\t\t\t\t   generate_qualified_relation_name(con->relationId),\n\t\t\t\t\t\t\t   con->accessMethod)));\n\t}\n\n\treturn ConvertTable(con);\n}\n\n\n/*\n * ConvertTableInternal is used for converting a table into a new table with different\n * properties. The conversion is done by creating a new table, moving everything to the\n * new table and dropping the old one. So the oid of the table is not preserved.\n *\n * The new table will have the same name, columns and rows. It will also have partitions,\n * views, sequences of the old table. Finally it will have everything created by\n * GetPostLoadTableCreationCommands function, which include indexes. These will be\n * re-created during conversion, so their oids are not preserved either (except for\n * sequences). However, their names are preserved.\n *\n * The dropping of old table is done with CASCADE. Anything not mentioned here will\n * be dropped.\n *\n * The function returns a TableConversionReturn object that can stores variables that\n * can be used at the caller operations.\n *\n * To be able to provide more meaningful messages while converting a table type,\n * Citus keeps InTableTypeConversionFunctionCall flag. Don't forget to set it properly\n * in case you add a new way to return from this function.\n */\nTableConversionReturn *\nConvertTableInternal(TableConversionState *con)\n{\n\tInTableTypeConversionFunctionCall = true;\n\n\t/*\n\t * We undistribute citus local tables that are not chained with any reference\n\t * tables via foreign keys at the end of the utility hook.\n\t * Here we temporarily set the related GUC to off to disable the logic for\n\t * internally executed DDL's that might invoke this mechanism unnecessarily.\n\t */\n\tbool oldEnableLocalReferenceForeignKeys = EnableLocalReferenceForeignKeys;\n\tSetLocalEnableLocalReferenceForeignKeys(false);\n\n\t/* switch to sequential execution if shard names will be too long */\n\tSwitchToSequentialAndLocalExecutionIfRelationNameTooLong(con->relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t con->relationName);\n\n\tif (con->conversionType == UNDISTRIBUTE_TABLE && con->cascadeViaForeignKeys &&\n\t\t(TableReferencing(con->relationId) || TableReferenced(con->relationId)))\n\t{\n\t\t/*\n\t\t * Acquire ExclusiveLock as UndistributeTable does in order to\n\t\t * make sure that no modifications happen on the relations.\n\t\t */\n\t\tCascadeOperationForFkeyConnectedRelations(con->relationId, ExclusiveLock,\n\t\t\t\t\t\t\t\t\t\t\t\t  CASCADE_FKEY_UNDISTRIBUTE_TABLE);\n\n\t\t/*\n\t\t * Undistributed every foreign key connected relation in our foreign key\n\t\t * subgraph including itself, so return here.\n\t\t */\n\t\tSetLocalEnableLocalReferenceForeignKeys(oldEnableLocalReferenceForeignKeys);\n\t\tInTableTypeConversionFunctionCall = false;\n\t\treturn NULL;\n\t}\n\tchar *newAccessMethod = con->accessMethod ? con->accessMethod :\n\t\t\t\t\t\t\tcon->originalAccessMethod;\n\tIncludeSequenceDefaults includeSequenceDefaults = NEXTVAL_SEQUENCE_DEFAULTS;\n\tIncludeIdentities includeIdentity = INCLUDE_IDENTITY;\n\n\tList *preLoadCommands = GetPreLoadTableCreationCommands(con->relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tincludeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tincludeIdentity,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewAccessMethod);\n\n\tif (con->accessMethod && strcmp(con->accessMethod, \"columnar\") == 0)\n\t{\n\t\tDropIndexesNotSupportedByColumnar(con->relationId,\n\t\t\t\t\t\t\t\t\t\t  con->suppressNoticeMessages);\n\t}\n\n\t/*\n\t * Since we already dropped unsupported indexes, we can safely pass\n\t * includeIndexes to be true.\n\t */\n\tbool includeIndexes = true;\n\tbool includeReplicaIdentity = true;\n\tList *postLoadCommands = GetPostLoadTableCreationCommands(con->relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  includeIndexes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  includeReplicaIdentity);\n\tList *justBeforeDropCommands = NIL;\n\tList *attachPartitionCommands = NIL;\n\n\tList *createViewCommands = GetViewCreationCommandsOfTable(con->relationId);\n\n\tpostLoadCommands = list_concat(postLoadCommands,\n\t\t\t\t\t\t\t\t   WrapTableDDLCommands(createViewCommands));\n\n\t/* need to add back to publications after dropping the original table */\n\tbool isAdd = true;\n\tList *alterPublicationCommands =\n\t\tGetAlterPublicationDDLCommandsForTable(con->relationId, isAdd);\n\n\tpostLoadCommands = list_concat(postLoadCommands,\n\t\t\t\t\t\t\t\t   WrapTableDDLCommands(alterPublicationCommands));\n\n\tList *foreignKeyCommands = NIL;\n\tif (con->conversionType == ALTER_DISTRIBUTED_TABLE)\n\t{\n\t\tforeignKeyCommands = GetForeignConstraintToReferenceTablesCommands(\n\t\t\tcon->relationId);\n\t\tif (con->cascadeToColocated == CASCADE_TO_COLOCATED_YES ||\n\t\t\tcon->cascadeToColocated == CASCADE_TO_COLOCATED_NO_ALREADY_CASCADED)\n\t\t{\n\t\t\tList *foreignKeyToDistributedTableCommands =\n\t\t\t\tGetForeignConstraintToDistributedTablesCommands(con->relationId);\n\t\t\tforeignKeyCommands = list_concat(foreignKeyCommands,\n\t\t\t\t\t\t\t\t\t\t\t foreignKeyToDistributedTableCommands);\n\n\t\t\tList *foreignKeyFromDistributedTableCommands =\n\t\t\t\tGetForeignConstraintFromDistributedTablesCommands(con->relationId);\n\t\t\tforeignKeyCommands = list_concat(foreignKeyCommands,\n\t\t\t\t\t\t\t\t\t\t\t foreignKeyFromDistributedTableCommands);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tWarningsForDroppingForeignKeysWithDistributedTables(con->relationId);\n\t\t}\n\t}\n\n\tbool isPartitionTable = false;\n\tchar *attachToParentCommand = NULL;\n\tif (PartitionTable(con->relationId))\n\t{\n\t\tisPartitionTable = true;\n\t\tchar *detachFromParentCommand = GenerateDetachPartitionCommand(con->relationId);\n\t\tattachToParentCommand = GenerateAlterTableAttachPartitionCommand(con->relationId);\n\n\t\tjustBeforeDropCommands = lappend(justBeforeDropCommands, detachFromParentCommand);\n\t}\n\n\tchar *qualifiedRelationName = quote_qualified_identifier(con->schemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t con->relationName);\n\n\tif (PartitionedTable(con->relationId))\n\t{\n\t\tif (!con->suppressNoticeMessages)\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\"converting the partitions of %s\",\n\t\t\t\t\t\t\t\t\tqualifiedRelationName)));\n\t\t}\n\n\t\tList *partitionList = PartitionList(con->relationId);\n\n\t\tOid partitionRelationId = InvalidOid;\n\t\tforeach_declared_oid(partitionRelationId, partitionList)\n\t\t{\n\t\t\tchar *tableQualifiedName = generate_qualified_relation_name(\n\t\t\t\tpartitionRelationId);\n\t\t\tchar *detachPartitionCommand = GenerateDetachPartitionCommand(\n\t\t\t\tpartitionRelationId);\n\t\t\tchar *attachPartitionCommand = GenerateAlterTableAttachPartitionCommand(\n\t\t\t\tpartitionRelationId);\n\n\t\t\t/*\n\t\t\t * We first detach the partitions to be able to convert them separately.\n\t\t\t * After this they are no longer partitions, so they will not be caught by\n\t\t\t * the checks.\n\t\t\t */\n\t\t\tExecuteQueryViaSPI(detachPartitionCommand, SPI_OK_UTILITY);\n\t\t\tattachPartitionCommands = lappend(attachPartitionCommands,\n\t\t\t\t\t\t\t\t\t\t\t  attachPartitionCommand);\n\n\t\t\tCascadeToColocatedOption cascadeOption = CASCADE_TO_COLOCATED_NO;\n\t\t\tif (con->cascadeToColocated == CASCADE_TO_COLOCATED_YES ||\n\t\t\t\tcon->cascadeToColocated == CASCADE_TO_COLOCATED_NO_ALREADY_CASCADED)\n\t\t\t{\n\t\t\t\tcascadeOption = CASCADE_TO_COLOCATED_NO_ALREADY_CASCADED;\n\t\t\t}\n\n\t\t\tTableConversionParameters partitionParam = {\n\t\t\t\t.relationId = partitionRelationId,\n\t\t\t\t.distributionColumn = con->distributionColumn,\n\t\t\t\t.shardCountIsNull = con->shardCountIsNull,\n\t\t\t\t.shardCount = con->shardCount,\n\t\t\t\t.cascadeToColocated = cascadeOption,\n\t\t\t\t.colocateWith = con->colocateWith,\n\t\t\t\t.suppressNoticeMessages = con->suppressNoticeMessages,\n\n\t\t\t\t/*\n\t\t\t\t * Even if we called UndistributeTable with cascade option, we\n\t\t\t\t * shouldn't cascade via foreign keys on partitions. Otherwise,\n\t\t\t\t * we might try to undistribute partitions of other tables in\n\t\t\t\t * our foreign key subgraph more than once.\n\t\t\t\t */\n\t\t\t\t.cascadeViaForeignKeys = false\n\t\t\t};\n\n\t\t\tTableConversionReturn *partitionReturn = con->function(&partitionParam);\n\t\t\tif (cascadeOption == CASCADE_TO_COLOCATED_NO_ALREADY_CASCADED)\n\t\t\t{\n\t\t\t\tforeignKeyCommands = list_concat(foreignKeyCommands,\n\t\t\t\t\t\t\t\t\t\t\t\t partitionReturn->foreignKeyCommands);\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t * If we are altering a partitioned distributed table by\n\t\t\t * colocateWith:none, we override con->colocationWith parameter\n\t\t\t * with the first newly created partition table to share the\n\t\t\t * same colocation group for rest of partitions and partitioned\n\t\t\t * table.\n\t\t\t */\n\t\t\tif (con->colocateWith != NULL && IsColocateWithNone(con->colocateWith))\n\t\t\t{\n\t\t\t\tcon->colocateWith = tableQualifiedName;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!con->suppressNoticeMessages)\n\t{\n\t\tereport(NOTICE, (errmsg(\"creating a new table for %s\", qualifiedRelationName)));\n\t}\n\n\tTableDDLCommand *tableCreationCommand = NULL;\n\tforeach_declared_ptr(tableCreationCommand, preLoadCommands)\n\t{\n\t\tAssert(CitusIsA(tableCreationCommand, TableDDLCommand));\n\n\t\tchar *tableCreationSql = GetTableDDLCommand(tableCreationCommand);\n\t\tNode *parseTree = ParseTreeNode(tableCreationSql);\n\n\t\tRelayEventExtendNames(parseTree, con->schemaName, con->hashOfName);\n\t\tProcessUtilityParseTree(parseTree, tableCreationSql, PROCESS_UTILITY_QUERY,\n\t\t\t\t\t\t\t\tNULL, None_Receiver, NULL);\n\t}\n\n\t/* set columnar options */\n\tif (con->accessMethod == NULL && con->originalAccessMethod &&\n\t\tstrcmp(con->originalAccessMethod, \"columnar\") == 0)\n\t{\n\t\tColumnarOptions options = { 0 };\n\t\textern_ReadColumnarOptions(con->relationId, &options);\n\n\t\tColumnarTableDDLContext *context = (ColumnarTableDDLContext *) palloc0(\n\t\t\tsizeof(ColumnarTableDDLContext));\n\n\t\t/* build the context */\n\t\tcontext->schemaName = con->schemaName;\n\t\tcontext->relationName = con->relationName;\n\t\tcontext->options = options;\n\n\t\tchar *columnarOptionsSql = GetShardedTableDDLCommandColumnar(con->hashOfName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context);\n\n\t\tExecuteQueryViaSPI(columnarOptionsSql, SPI_OK_UTILITY);\n\t}\n\n\tcon->newRelationId = get_relname_relid(con->tempName, con->schemaId);\n\n\tif (con->conversionType == ALTER_DISTRIBUTED_TABLE)\n\t{\n\t\tCreateDistributedTableLike(con);\n\t}\n\telse if (con->conversionType == ALTER_TABLE_SET_ACCESS_METHOD)\n\t{\n\t\tCreateCitusTableLike(con);\n\t}\n\n\t/* preserve colocation with procedures/functions */\n\tif (con->conversionType == ALTER_DISTRIBUTED_TABLE)\n\t{\n\t\t/*\n\t\t * Updating the colocationId of functions is always desirable for\n\t\t * the following scenario:\n\t\t *    we have shardCount or colocateWith change\n\t\t *    AND  entire co-location group is altered\n\t\t * The reason for the second condition is because we currently don't\n\t\t * remember the original table specified in the colocateWith when\n\t\t * distributing the function. We only remember the colocationId in\n\t\t * pg_dist_object table.\n\t\t */\n\t\tif ((!con->shardCountIsNull || con->colocateWith != NULL) &&\n\t\t\t(con->cascadeToColocated == CASCADE_TO_COLOCATED_YES || list_length(\n\t\t\t\t con->colocatedTableList) == 1) && con->distributionColumn == NULL)\n\t\t{\n\t\t\t/*\n\t\t\t * Update the colocationId from the one of the old relation to the one\n\t\t\t * of the new relation for all tuples in citus.pg_dist_object\n\t\t\t */\n\t\t\tUpdateDistributedObjectColocationId(TableColocationId(con->relationId),\n\t\t\t\t\t\t\t\t\t\t\t\tTableColocationId(con->newRelationId));\n\t\t}\n\t}\n\n\tReplaceTable(con->relationId, con->newRelationId, justBeforeDropCommands,\n\t\t\t\t con->suppressNoticeMessages);\n\n\tTableDDLCommand *tableConstructionCommand = NULL;\n\tforeach_declared_ptr(tableConstructionCommand, postLoadCommands)\n\t{\n\t\tAssert(CitusIsA(tableConstructionCommand, TableDDLCommand));\n\t\tchar *tableConstructionSQL = GetTableDDLCommand(tableConstructionCommand);\n\t\tExecuteQueryViaSPI(tableConstructionSQL, SPI_OK_UTILITY);\n\t}\n\n\t/*\n\t * when there are many partitions, each call to ProcessUtilityParseTree\n\t * accumulates used memory. Free context after each call.\n\t */\n\tMemoryContext citusPerPartitionContext =\n\t\tAllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t  \"citus_per_partition_context\",\n\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(citusPerPartitionContext);\n\n\tchar *attachPartitionCommand = NULL;\n\tforeach_declared_ptr(attachPartitionCommand, attachPartitionCommands)\n\t{\n\t\tMemoryContextReset(citusPerPartitionContext);\n\n\t\tNode *parseTree = ParseTreeNode(attachPartitionCommand);\n\n\t\tProcessUtilityParseTree(parseTree, attachPartitionCommand,\n\t\t\t\t\t\t\t\tPROCESS_UTILITY_QUERY,\n\t\t\t\t\t\t\t\tNULL, None_Receiver, NULL);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\tMemoryContextDelete(citusPerPartitionContext);\n\n\tif (isPartitionTable)\n\t{\n\t\tExecuteQueryViaSPI(attachToParentCommand, SPI_OK_UTILITY);\n\t}\n\n\tif (con->cascadeToColocated == CASCADE_TO_COLOCATED_YES)\n\t{\n\t\tOid colocatedTableId = InvalidOid;\n\n\t\t/* For now we only support cascade to colocation for alter_distributed_table UDF */\n\t\tAssert(con->conversionType == ALTER_DISTRIBUTED_TABLE);\n\t\tforeach_declared_oid(colocatedTableId, con->colocatedTableList)\n\t\t{\n\t\t\tif (colocatedTableId == con->relationId)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tTableConversionParameters cascadeParam = {\n\t\t\t\t.relationId = colocatedTableId,\n\t\t\t\t.shardCountIsNull = con->shardCountIsNull,\n\t\t\t\t.shardCount = con->shardCount,\n\t\t\t\t.colocateWith = qualifiedRelationName,\n\t\t\t\t.cascadeToColocated = CASCADE_TO_COLOCATED_NO_ALREADY_CASCADED,\n\t\t\t\t.suppressNoticeMessages = con->suppressNoticeMessages\n\t\t\t};\n\t\t\tTableConversionReturn *colocatedReturn = con->function(&cascadeParam);\n\t\t\tforeignKeyCommands = list_concat(foreignKeyCommands,\n\t\t\t\t\t\t\t\t\t\t\t colocatedReturn->foreignKeyCommands);\n\t\t}\n\t}\n\n\t/* recreate foreign keys */\n\tTableConversionReturn *ret = NULL;\n\tif (con->conversionType == ALTER_DISTRIBUTED_TABLE)\n\t{\n\t\tif (con->cascadeToColocated != CASCADE_TO_COLOCATED_NO_ALREADY_CASCADED)\n\t\t{\n\t\t\tchar *foreignKeyCommand = NULL;\n\t\t\tforeach_declared_ptr(foreignKeyCommand, foreignKeyCommands)\n\t\t\t{\n\t\t\t\tExecuteQueryViaSPI(foreignKeyCommand, SPI_OK_UTILITY);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tret = palloc0(sizeof(TableConversionReturn));\n\t\t\tret->foreignKeyCommands = foreignKeyCommands;\n\t\t}\n\t}\n\n\t/* increment command counter so that next command can see the new table */\n\tCommandCounterIncrement();\n\n\tSetLocalEnableLocalReferenceForeignKeys(oldEnableLocalReferenceForeignKeys);\n\n\tInTableTypeConversionFunctionCall = false;\n\n\treturn ret;\n}\n\n\n/*\n * CopyTableConversionReturnIntoCurrentContext copies given tableConversionReturn\n * into CurrentMemoryContext.\n */\nstatic TableConversionReturn *\nCopyTableConversionReturnIntoCurrentContext(TableConversionReturn *tableConversionReturn)\n{\n\tTableConversionReturn *tableConversionReturnCopy = NULL;\n\tif (tableConversionReturn)\n\t{\n\t\ttableConversionReturnCopy = palloc0(sizeof(TableConversionReturn));\n\t\tList *copyForeignKeyCommands = NIL;\n\t\tchar *foreignKeyCommand = NULL;\n\t\tforeach_declared_ptr(foreignKeyCommand, tableConversionReturn->foreignKeyCommands)\n\t\t{\n\t\t\tchar *copyForeignKeyCommand = MemoryContextStrdup(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  foreignKeyCommand);\n\t\t\tcopyForeignKeyCommands = lappend(copyForeignKeyCommands,\n\t\t\t\t\t\t\t\t\t\t\t copyForeignKeyCommand);\n\t\t}\n\t\ttableConversionReturnCopy->foreignKeyCommands = copyForeignKeyCommands;\n\t}\n\n\treturn tableConversionReturnCopy;\n}\n\n\n/*\n * ConvertTable is a wrapper for ConvertTableInternal to persist only\n * TableConversionReturn and delete all other allocations.\n */\nstatic TableConversionReturn *\nConvertTable(TableConversionState *con)\n{\n\t/*\n\t * We do not allow alter_distributed_table and undistribute_table operations\n\t * for tables with identity columns. This is because we do not have a proper way\n\t * of keeping sequence states consistent across the cluster.\n\t */\n\tErrorIfTableHasIdentityColumn(con->relationId);\n\n\t/*\n\t * when there are many partitions or colocated tables, memory usage is\n\t * accumulated. Free context for each call to ConvertTable.\n\t */\n\tMemoryContext convertTableContext =\n\t\tAllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t  \"citus_convert_table_context\",\n\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(convertTableContext);\n\n\tTableConversionReturn *tableConversionReturn = ConvertTableInternal(con);\n\n\tMemoryContextSwitchTo(oldContext);\n\n\t/* persist TableConversionReturn in oldContext */\n\tTableConversionReturn *tableConversionReturnCopy =\n\t\tCopyTableConversionReturnIntoCurrentContext(tableConversionReturn);\n\n\t/* delete convertTableContext */\n\tMemoryContextDelete(convertTableContext);\n\n\treturn tableConversionReturnCopy;\n}\n\n\n/*\n * DropIndexesNotSupportedByColumnar is a helper function used during accces\n * method conversion to drop the indexes that are not supported by columnarAM.\n */\nstatic void\nDropIndexesNotSupportedByColumnar(Oid relationId, bool suppressNoticeMessages)\n{\n\tRelation columnarRelation = RelationIdGetRelation(relationId);\n\tif (!RelationIsValid(columnarRelation))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not open relation with OID %u\", relationId)));\n\t}\n\n\tList *indexIdList = RelationGetIndexList(columnarRelation);\n\n\t/*\n\t * Immediately close the relation since we might execute ALTER TABLE\n\t * for that relation.\n\t */\n\tRelationClose(columnarRelation);\n\n\tOid indexId = InvalidOid;\n\tforeach_declared_oid(indexId, indexIdList)\n\t{\n\t\tchar *indexAmName = GetIndexAccessMethodName(indexId);\n\t\tif (extern_ColumnarSupportsIndexAM(indexAmName))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!suppressNoticeMessages)\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\"unsupported access method for index %s \"\n\t\t\t\t\t\t\t\t\t\"on columnar table %s, given index and \"\n\t\t\t\t\t\t\t\t\t\"the constraint depending on the index \"\n\t\t\t\t\t\t\t\t\t\"(if any) will be dropped\",\n\t\t\t\t\t\t\t\t\tget_rel_name(indexId),\n\t\t\t\t\t\t\t\t\tgenerate_qualified_relation_name(relationId))));\n\t\t}\n\n\t\tOid constraintId = get_index_constraint(indexId);\n\t\tif (OidIsValid(constraintId))\n\t\t{\n\t\t\t/* index is implied by a constraint, so drop the constraint itself */\n\t\t\tDropConstraintRestrict(relationId, constraintId);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tDropIndexRestrict(indexId);\n\t\t}\n\t}\n}\n\n\n/*\n * GetIndexAccessMethodName returns access method name of index with indexId.\n * If there is no such index, then errors out.\n */\nstatic char *\nGetIndexAccessMethodName(Oid indexId)\n{\n\t/* fetch pg_class tuple of the index relation */\n\tHeapTuple indexTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(indexId));\n\tif (!HeapTupleIsValid(indexTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"index with oid %u does not exist\", indexId)));\n\t}\n\n\tForm_pg_class indexForm = (Form_pg_class) GETSTRUCT(indexTuple);\n\tOid indexAMId = indexForm->relam;\n\tReleaseSysCache(indexTuple);\n\n\tchar *indexAmName = get_am_name(indexAMId);\n\tif (!indexAmName)\n\t{\n\t\tereport(ERROR, (errmsg(\"access method with oid %u does not exist\", indexAMId)));\n\t}\n\n\treturn indexAmName;\n}\n\n\n/*\n * DropConstraintRestrict drops the constraint with constraintId by using spi.\n */\nstatic void\nDropConstraintRestrict(Oid relationId, Oid constraintId)\n{\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\tchar *constraintName = get_constraint_name(constraintId);\n\tconst char *quotedConstraintName = quote_identifier(constraintName);\n\tStringInfo dropConstraintCommand = makeStringInfo();\n\tappendStringInfo(dropConstraintCommand, \"ALTER TABLE %s DROP CONSTRAINT %s RESTRICT;\",\n\t\t\t\t\t qualifiedRelationName, quotedConstraintName);\n\tExecuteQueryViaSPI(dropConstraintCommand->data, SPI_OK_UTILITY);\n}\n\n\n/*\n * DropIndexRestrict drops the index with indexId by using spi.\n */\nstatic void\nDropIndexRestrict(Oid indexId)\n{\n\tchar *qualifiedIndexName = generate_qualified_relation_name(indexId);\n\tStringInfo dropIndexCommand = makeStringInfo();\n\tappendStringInfo(dropIndexCommand, \"DROP INDEX %s RESTRICT;\", qualifiedIndexName);\n\tExecuteQueryViaSPI(dropIndexCommand->data, SPI_OK_UTILITY);\n}\n\n\n/*\n * EnsureTableNotReferencing checks if the table has a reference to another\n * table and errors if it is.\n */\nvoid\nEnsureTableNotReferencing(Oid relationId, char conversionType)\n{\n\tif (TableReferencing(relationId))\n\t{\n\t\tif (conversionType == UNDISTRIBUTE_TABLE)\n\t\t{\n\t\t\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\t\t\tereport(ERROR, (errmsg(\"cannot complete operation \"\n\t\t\t\t\t\t\t\t   \"because table %s has a foreign key\",\n\t\t\t\t\t\t\t\t   get_rel_name(relationId)),\n\t\t\t\t\t\t\terrhint(UNDISTRIBUTE_TABLE_CASCADE_HINT,\n\t\t\t\t\t\t\t\t\tqualifiedRelationName,\n\t\t\t\t\t\t\t\t\tqualifiedRelationName)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot complete operation \"\n\t\t\t\t\t\t\t\t   \"because table %s has a foreign key\",\n\t\t\t\t\t\t\t\t   get_rel_name(relationId))));\n\t\t}\n\t}\n}\n\n\n/*\n * EnsureTableNotReferenced checks if the table is referenced by another\n * table and errors if it is.\n */\nvoid\nEnsureTableNotReferenced(Oid relationId, char conversionType)\n{\n\tif (TableReferenced(relationId))\n\t{\n\t\tif (conversionType == UNDISTRIBUTE_TABLE)\n\t\t{\n\t\t\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\t\t\tereport(ERROR, (errmsg(\"cannot complete operation \"\n\t\t\t\t\t\t\t\t   \"because table %s is referenced by a foreign key\",\n\t\t\t\t\t\t\t\t   get_rel_name(relationId)),\n\t\t\t\t\t\t\terrhint(UNDISTRIBUTE_TABLE_CASCADE_HINT,\n\t\t\t\t\t\t\t\t\tqualifiedRelationName,\n\t\t\t\t\t\t\t\t\tqualifiedRelationName)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot complete operation \"\n\t\t\t\t\t\t\t\t   \"because table %s is referenced by a foreign key\",\n\t\t\t\t\t\t\t\t   get_rel_name(relationId))));\n\t\t}\n\t}\n}\n\n\n/*\n * EnsureTableNotForeign checks if the table is a foreign table and errors\n * if it is.\n */\nvoid\nEnsureTableNotForeign(Oid relationId)\n{\n\tif (IsForeignTable(relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot complete operation \"\n\t\t\t\t\t\t\t   \"because it is a foreign table\")));\n\t}\n}\n\n\n/*\n * EnsureTableNotPartition checks if the table is a partition of another\n * table and errors if it is.\n */\nvoid\nEnsureTableNotPartition(Oid relationId)\n{\n\tif (PartitionTable(relationId))\n\t{\n\t\tOid parentRelationId = PartitionParentOid(relationId);\n\t\tchar *parentRelationName = get_rel_name(parentRelationId);\n\t\tereport(ERROR, (errmsg(\"cannot complete operation \"\n\t\t\t\t\t\t\t   \"because table is a partition\"),\n\t\t\t\t\t\terrhint(\"the parent table is \\\"%s\\\"\",\n\t\t\t\t\t\t\t\tparentRelationName)));\n\t}\n}\n\n\n/*\n * ErrorIfColocateWithTenantTable errors out if given colocateWith text refers to\n * a tenant table.\n */\nvoid\nErrorIfColocateWithTenantTable(char *colocateWith)\n{\n\tif (colocateWith != NULL &&\n\t\t!IsColocateWithDefault(colocateWith) &&\n\t\t!IsColocateWithNone(colocateWith))\n\t{\n\t\ttext *colocateWithTableNameText = cstring_to_text(colocateWith);\n\t\tOid colocateWithTableId = ResolveRelationId(colocateWithTableNameText, false);\n\t\tErrorIfTenantTable(colocateWithTableId,\n\t\t\t\t\t\t   TenantOperationNames[TENANT_COLOCATE_WITH]);\n\t}\n}\n\n\nTableConversionState *\nCreateTableConversion(TableConversionParameters *params)\n{\n\tTableConversionState *con = palloc0(sizeof(TableConversionState));\n\n\tcon->conversionType = params->conversionType;\n\tcon->relationId = params->relationId;\n\tcon->distributionColumn = params->distributionColumn;\n\tcon->shardCountIsNull = params->shardCountIsNull;\n\tcon->shardCount = params->shardCount;\n\tcon->colocateWith = params->colocateWith;\n\tcon->accessMethod = params->accessMethod;\n\tcon->cascadeToColocated = params->cascadeToColocated;\n\tcon->cascadeViaForeignKeys = params->cascadeViaForeignKeys;\n\tcon->suppressNoticeMessages = params->suppressNoticeMessages;\n\n\tRelation relation = try_relation_open(con->relationId, ExclusiveLock);\n\tif (relation == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot complete operation \"\n\t\t\t\t\t\t\t   \"because no such table exists\")));\n\t}\n\n\n\tOid relam = relation->rd_rel->relam;\n\trelation_close(relation, NoLock);\n\tcon->distributionKey =\n\t\tBuildDistributionKeyFromColumnName(con->relationId, con->distributionColumn,\n\t\t\t\t\t\t\t\t\t\t   NoLock);\n\n\tcon->originalAccessMethod = NULL;\n\tif (!PartitionedTable(con->relationId) && !IsForeignTable(con->relationId))\n\t{\n\t\tHeapTuple amTuple = SearchSysCache1(AMOID, ObjectIdGetDatum(\n\t\t\t\t\t\t\t\t\t\t\t\trelam));\n\t\tif (!HeapTupleIsValid(amTuple))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cache lookup failed for access method %d\",\n\t\t\t\t\t\t\t\t   relam)));\n\t\t}\n\t\tForm_pg_am amForm = (Form_pg_am) GETSTRUCT(amTuple);\n\t\tcon->originalAccessMethod = NameStr(amForm->amname);\n\t\tReleaseSysCache(amTuple);\n\t}\n\n\n\tcon->colocatedTableList = NIL;\n\tif (IsCitusTableType(con->relationId, DISTRIBUTED_TABLE))\n\t{\n\t\tcon->originalDistributionKey = DistPartitionKey(con->relationId);\n\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(con->relationId);\n\t\tcon->originalShardCount = cacheEntry->shardIntervalArrayLength;\n\n\t\tList *colocatedTableList = ColocatedTableList(con->relationId);\n\n\t\t/*\n\t\t * we will not add partition tables to the colocatedTableList\n\t\t * since they will be handled separately.\n\t\t */\n\t\tOid colocatedTableId = InvalidOid;\n\t\tforeach_declared_oid(colocatedTableId, colocatedTableList)\n\t\t{\n\t\t\tif (PartitionTable(colocatedTableId))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcon->colocatedTableList = lappend_oid(con->colocatedTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t  colocatedTableId);\n\t\t}\n\n\t\t/* sort the oids to avoid deadlock */\n\t\tcon->colocatedTableList = SortList(con->colocatedTableList, CompareOids);\n\t}\n\n\t/* find relation and schema names */\n\tcon->relationName = get_rel_name(con->relationId);\n\tcon->schemaId = get_rel_namespace(con->relationId);\n\tcon->schemaName = get_namespace_name(con->schemaId);\n\n\t/* calculate a temp name for the new table */\n\tcon->tempName = pstrdup(con->relationName);\n\tcon->hashOfName = hash_any((unsigned char *) con->tempName, strlen(con->tempName));\n\tAppendShardIdToName(&con->tempName, con->hashOfName);\n\n\tif (con->conversionType == UNDISTRIBUTE_TABLE)\n\t{\n\t\tcon->function = &UndistributeTable;\n\t}\n\telse if (con->conversionType == ALTER_DISTRIBUTED_TABLE)\n\t{\n\t\tcon->function = &AlterDistributedTable;\n\t}\n\telse if (con->conversionType == ALTER_TABLE_SET_ACCESS_METHOD)\n\t{\n\t\tcon->function = &AlterTableSetAccessMethod;\n\t}\n\n\treturn con;\n}\n\n\n/*\n * CreateDistributedTableLike distributes the new table in con parameter\n * like the old one. It checks the distribution column, colocation and\n * shard count and if they are not changed sets them to the old table's values.\n */\nvoid\nCreateDistributedTableLike(TableConversionState *con)\n{\n\tVar *newDistributionKey =\n\t\tcon->distributionColumn ? con->distributionKey : con->originalDistributionKey;\n\n\tchar *newColocateWith = con->colocateWith;\n\tif (con->colocateWith == NULL)\n\t{\n\t\t/*\n\t\t * If the new distribution column and the old one have the same data type\n\t\t * and the shard_count parameter is null (which means shard count will not\n\t\t * change) we can create the new table in the same colocation as the old one.\n\t\t * In this case we set the new table's colocate_with value as the old table\n\t\t * so we don't even change the colocation id of the table during conversion.\n\t\t */\n\t\tif (con->originalDistributionKey->vartype == newDistributionKey->vartype &&\n\t\t\tcon->shardCountIsNull)\n\t\t{\n\t\t\tnewColocateWith =\n\t\t\t\tquote_qualified_identifier(con->schemaName, con->relationName);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnewColocateWith = \"default\";\n\t\t}\n\t}\n\tint newShardCount = 0;\n\tif (con->shardCountIsNull)\n\t{\n\t\tnewShardCount = con->originalShardCount;\n\t}\n\telse\n\t{\n\t\tnewShardCount = con->shardCount;\n\t}\n\n\t/*\n\t * To get the correct column name, we use the original relation id, not the\n\t * new relation id. The reason is that the cached attributes of the original\n\t * and newly created tables are not the same if the original table has\n\t * dropped columns (dropped columns are still present in the attribute cache)\n\t * Detailed example in https://github.com/citusdata/citus/pull/6387\n\t */\n\tchar *distributionColumnName =\n\t\tColumnToColumnName(con->relationId, (Node *) newDistributionKey);\n\n\tOid originalRelationId = con->relationId;\n\tif (con->originalDistributionKey != NULL && PartitionTable(originalRelationId))\n\t{\n\t\t/*\n\t\t * Due to dropped columns, the partition tables might have different\n\t\t * distribution keys than their parents, see issue #5123 for details.\n\t\t *\n\t\t * At this point, we get the partitioning information from the\n\t\t * originalRelationId, but we get the distribution key for newRelationId.\n\t\t *\n\t\t * We have to do this, because the newRelationId is just a placeholder\n\t\t * at this moment, but that's going to be the table in pg_dist_partition.\n\t\t */\n\t\tOid parentRelationId = PartitionParentOid(originalRelationId);\n\t\tVar *parentDistKey = DistPartitionKeyOrError(parentRelationId);\n\t\tdistributionColumnName =\n\t\t\tColumnToColumnName(parentRelationId, (Node *) parentDistKey);\n\t}\n\n\tchar partitionMethod = PartitionMethod(con->relationId);\n\n\tCreateDistributedTable(con->newRelationId, distributionColumnName, partitionMethod,\n\t\t\t\t\t\t   newShardCount, true, newColocateWith);\n}\n\n\n/*\n * CreateCitusTableLike converts the new table to the Citus table type\n * of the old table.\n */\nvoid\nCreateCitusTableLike(TableConversionState *con)\n{\n\tif (IsCitusTableType(con->relationId, DISTRIBUTED_TABLE))\n\t{\n\t\tif (IsCitusTableType(con->relationId, SINGLE_SHARD_DISTRIBUTED))\n\t\t{\n\t\t\tColocationParam colocationParam = {\n\t\t\t\t.colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT,\n\t\t\t\t.colocateWithTableName = quote_qualified_identifier(con->schemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcon->relationName)\n\t\t\t};\n\t\t\tCreateSingleShardTable(con->newRelationId, colocationParam);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tCreateDistributedTableLike(con);\n\t\t}\n\t}\n\telse if (IsCitusTableType(con->relationId, REFERENCE_TABLE))\n\t{\n\t\tCreateReferenceTable(con->newRelationId);\n\t}\n\telse if (IsCitusTableType(con->relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\tCitusTableCacheEntry *entry = GetCitusTableCacheEntry(con->relationId);\n\t\tbool autoConverted = entry->autoConverted;\n\t\tbool cascade = false;\n\t\tCreateCitusLocalTable(con->newRelationId, cascade, autoConverted);\n\n\t\t/*\n\t\t * creating Citus local table adds a shell table on top\n\t\t * so we need its oid now\n\t\t */\n\t\tcon->newRelationId = get_relname_relid(con->tempName, con->schemaId);\n\t}\n}\n\n\n/*\n * ErrorIfUnsupportedCascadeObjects gets oid of a relation, finds the objects\n * that dropping this relation cascades into and errors if there are any extensions\n * that would be dropped.\n */\nstatic void\nErrorIfUnsupportedCascadeObjects(Oid relationId)\n{\n\tHTAB *nodeMap = CreateSimpleHashSetWithName(Oid, \"object dependency map (oid)\");\n\n\tbool unsupportedObjectInDepGraph =\n\t\tDoesCascadeDropUnsupportedObject(RelationRelationId, relationId, nodeMap);\n\n\tif (unsupportedObjectInDepGraph)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot alter table because an extension depends on it\")));\n\t}\n}\n\n\n/*\n * DoesCascadeDropUnsupportedObject walks through the objects that depend on the\n * object with object id and returns true if it finds any unsupported objects.\n *\n * This function only checks extensions as unsupported objects.\n *\n * Extension dependency is different than the rest. If an object depends on an extension\n * dropping the object would drop the extension too.\n * So we check with IsAnyObjectAddressOwnedByExtension function.\n */\nstatic bool\nDoesCascadeDropUnsupportedObject(Oid classId, Oid objectId, HTAB *nodeMap)\n{\n\tbool found = false;\n\thash_search(nodeMap, &objectId, HASH_ENTER, &found);\n\n\tif (found)\n\t{\n\t\treturn false;\n\t}\n\n\tObjectAddress *objectAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*objectAddress, classId, objectId);\n\tif (IsAnyObjectAddressOwnedByExtension(list_make1(objectAddress), NULL))\n\t{\n\t\treturn true;\n\t}\n\n\tOid targetObjectClassId = classId;\n\tOid targetObjectId = objectId;\n\tList *dependencyTupleList = GetPgDependTuplesForDependingObjects(targetObjectClassId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t targetObjectId);\n\n\tHeapTuple depTup = NULL;\n\tforeach_declared_ptr(depTup, dependencyTupleList)\n\t{\n\t\tForm_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);\n\n\t\tOid dependingOid = InvalidOid;\n\t\tOid dependingClassId = InvalidOid;\n\n\t\tif (pg_depend->classid == RewriteRelationId)\n\t\t{\n\t\t\tdependingOid = GetDependingView(pg_depend);\n\t\t\tdependingClassId = RelationRelationId;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdependingOid = pg_depend->objid;\n\t\t\tdependingClassId = pg_depend->classid;\n\t\t}\n\n\t\tif (DoesCascadeDropUnsupportedObject(dependingClassId, dependingOid, nodeMap))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n\n/*\n * GetViewCreationCommandsOfTable takes a table oid generates the CREATE VIEW\n * commands for views that depend to the given table. This includes the views\n * that recursively depend on the table too.\n */\nList *\nGetViewCreationCommandsOfTable(Oid relationId)\n{\n\tList *views = GetDependingViews(relationId);\n\n\tList *commands = NIL;\n\n\tOid viewOid = InvalidOid;\n\tforeach_declared_oid(viewOid, views)\n\t{\n\t\tStringInfo query = makeStringInfo();\n\n\t\t/* See comments on CreateMaterializedViewDDLCommand for its limitations */\n\t\tif (get_rel_relkind(viewOid) == RELKIND_MATVIEW)\n\t\t{\n\t\t\tErrorIfMatViewSizeExceedsTheLimit(viewOid);\n\n\t\t\tchar *matViewCreateCommands = CreateMaterializedViewDDLCommand(viewOid);\n\t\t\tappendStringInfoString(query, matViewCreateCommands);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar *viewCreateCommand = CreateViewDDLCommand(viewOid);\n\t\t\tappendStringInfoString(query, viewCreateCommand);\n\t\t}\n\n\t\tchar *alterViewCommmand = AlterViewOwnerCommand(viewOid);\n\t\tappendStringInfoString(query, alterViewCommmand);\n\n\t\tcommands = lappend(commands, query->data);\n\t}\n\n\treturn commands;\n}\n\n\n/*\n * WrapTableDDLCommands takes a list of command strings and wraps them\n * in TableDDLCommand structs.\n */\nstatic List *\nWrapTableDDLCommands(List *commandStrings)\n{\n\tList *tableDDLCommands = NIL;\n\n\tchar *command = NULL;\n\tforeach_declared_ptr(command, commandStrings)\n\t{\n\t\ttableDDLCommands = lappend(tableDDLCommands, makeTableDDLCommandString(command));\n\t}\n\n\treturn tableDDLCommands;\n}\n\n\n/*\n * ErrorIfMatViewSizeExceedsTheLimit takes the oid of a materialized view and errors\n * out if the size of the matview exceeds the limit set by the GUC\n * citus.max_matview_size_to_auto_recreate.\n */\nstatic void\nErrorIfMatViewSizeExceedsTheLimit(Oid matViewOid)\n{\n\tif (MaxMatViewSizeToAutoRecreate >= 0)\n\t{\n\t\t/* if it's below 0, it means the user has removed the limit */\n\t\tDatum relSizeDatum = DirectFunctionCall1(pg_total_relation_size,\n\t\t\t\t\t\t\t\t\t\t\t\t ObjectIdGetDatum(matViewOid));\n\t\tuint64 matViewSize = DatumGetInt64(relSizeDatum);\n\n\t\t/* convert from MB to bytes */\n\t\tuint64 limitSizeInBytes = MaxMatViewSizeToAutoRecreate * 1024L * 1024L;\n\n\t\tif (matViewSize > limitSizeInBytes)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"size of the materialized view %s exceeds \"\n\t\t\t\t\t\t\t\t   \"citus.max_matview_size_to_auto_recreate \"\n\t\t\t\t\t\t\t\t   \"(currently %d MB)\", get_rel_name(matViewOid),\n\t\t\t\t\t\t\t\t   MaxMatViewSizeToAutoRecreate),\n\t\t\t\t\t\t\terrdetail(\"Citus restricts automatically recreating \"\n\t\t\t\t\t\t\t\t\t  \"materialized views that are larger than the \"\n\t\t\t\t\t\t\t\t\t  \"limit, because it could take too long.\"),\n\t\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\t\"Consider increasing the size limit by setting \"\n\t\t\t\t\t\t\t\t\"citus.max_matview_size_to_auto_recreate; \"\n\t\t\t\t\t\t\t\t\"or you can remove the limit by setting it to -1\")));\n\t\t}\n\t}\n}\n\n\n/*\n * CreateMaterializedViewDDLCommand creates the command to create materialized view.\n * Note that this function doesn't support\n * - Aliases\n * - Storage parameters\n * - Tablespace\n * - WITH [NO] DATA\n * options for the given materialized view. Parser functions for materialized views\n * should be added to handle them.\n *\n * Related issue: https://github.com/citusdata/citus/issues/5968\n */\nstatic char *\nCreateMaterializedViewDDLCommand(Oid matViewOid)\n{\n\tStringInfo query = makeStringInfo();\n\n\tchar *qualifiedViewName = generate_qualified_relation_name(matViewOid);\n\n\t/* here we need to get the access method of the view to recreate it */\n\tchar *accessMethodName = GetAccessMethodForMatViewIfExists(matViewOid);\n\n\tappendStringInfo(query, \"CREATE MATERIALIZED VIEW %s \", qualifiedViewName);\n\n\tif (accessMethodName)\n\t{\n\t\tappendStringInfo(query, \"USING %s \", accessMethodName);\n\t}\n\n\t/*\n\t * Set search_path to NIL so that all objects outside of pg_catalog will be\n\t * schema-prefixed.\n\t */\n\tint saveNestLevel = PushEmptySearchPath();\n\n\t/*\n\t * Push the transaction snapshot to be able to get vief definition with pg_get_viewdef\n\t */\n\tPushActiveSnapshot(GetTransactionSnapshot());\n\n\tDatum viewDefinitionDatum = DirectFunctionCall1(pg_get_viewdef,\n\t\t\t\t\t\t\t\t\t\t\t\t\tObjectIdGetDatum(matViewOid));\n\tchar *viewDefinition = TextDatumGetCString(viewDefinitionDatum);\n\n\tPopActiveSnapshot();\n\tPopEmptySearchPath(saveNestLevel);\n\n\tappendStringInfo(query, \"AS %s\", viewDefinition);\n\n\treturn query->data;\n}\n\n\n/*\n * ReplaceTable replaces the source table with the target table.\n * It moves all the rows of the source table to target table with INSERT SELECT.\n * Changes the dependencies of the sequences owned by source table to target table.\n * Then drops the source table and renames the target table to source tables name.\n *\n * Source and target tables need to be in the same schema and have the same columns.\n */\nvoid\nReplaceTable(Oid sourceId, Oid targetId, List *justBeforeDropCommands,\n\t\t\t bool suppressNoticeMessages)\n{\n\tchar *sourceName = get_rel_name(sourceId);\n\tchar *qualifiedSourceName = generate_qualified_relation_name(sourceId);\n\tchar *qualifiedTargetName = generate_qualified_relation_name(targetId);\n\n\tStringInfo query = makeStringInfo();\n\n\tif (!PartitionedTable(sourceId) && !IsForeignTable(sourceId))\n\t{\n\t\tif (!suppressNoticeMessages)\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\"moving the data of %s\", qualifiedSourceName)));\n\t\t}\n\n\t\tif (!HasAnyGeneratedStoredColumns(sourceId))\n\t\t{\n\t\t\t/*\n\t\t\t * Relation has no GENERATED STORED columns, copy the table via plain\n\t\t\t * \"INSERT INTO .. SELECT *\"\".\n\t\t\t */\n\t\t\tappendStringInfo(query, \"INSERT INTO %s SELECT * FROM %s\",\n\t\t\t\t\t\t\t qualifiedTargetName, qualifiedSourceName);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Skip columns having GENERATED ALWAYS AS (...) STORED expressions\n\t\t\t * since Postgres doesn't allow inserting into such columns.\n\t\t\t * This is not bad since Postgres would already generate such columns.\n\t\t\t * Note that here we intentionally don't skip columns having DEFAULT\n\t\t\t * expressions since user might have inserted non-default values.\n\t\t\t */\n\t\t\tList *nonStoredColumnNameList = GetNonGeneratedStoredColumnNameList(sourceId);\n\t\t\tchar *insertColumnString = StringJoin(nonStoredColumnNameList, ',');\n\t\t\tappendStringInfo(query,\n\t\t\t\t\t\t\t \"INSERT INTO %s (%s) OVERRIDING SYSTEM VALUE SELECT %s FROM %s\",\n\t\t\t\t\t\t\t qualifiedTargetName, insertColumnString,\n\t\t\t\t\t\t\t insertColumnString, qualifiedSourceName);\n\t\t}\n\n\t\tExecuteQueryViaSPI(query->data, SPI_OK_INSERT);\n\t}\n\n\t/*\n\t * Modify regular sequence dependencies (sequences marked as DEPENDENCY_AUTO)\n\t */\n\tList *ownedSequences = getOwnedSequences_internal(sourceId, 0, DEPENDENCY_AUTO);\n\tOid sequenceOid = InvalidOid;\n\tforeach_declared_oid(sequenceOid, ownedSequences)\n\t{\n\t\tchangeDependencyFor(RelationRelationId, sequenceOid,\n\t\t\t\t\t\t\tRelationRelationId, sourceId, targetId);\n\n\t\t/*\n\t\t * Skip if we cannot sync metadata for target table.\n\t\t * Checking only for the target table is sufficient since we will\n\t\t * anyway drop the source table even if it was a Citus table that\n\t\t * has metadata on MX workers.\n\t\t */\n\t\tif (ShouldSyncTableMetadata(targetId))\n\t\t{\n\t\t\tchar *qualifiedSequenceName = generate_qualified_relation_name(sequenceOid);\n\t\t\tchar *workerChangeSequenceDependencyCommand =\n\t\t\t\tCreateWorkerChangeSequenceDependencyCommand(qualifiedSequenceName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tqualifiedSourceName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tqualifiedTargetName);\n\t\t\tSendCommandToWorkersWithMetadata(workerChangeSequenceDependencyCommand);\n\t\t}\n\t\telse if (ShouldSyncTableMetadata(sourceId))\n\t\t{\n\t\t\t/*\n\t\t\t * We are converting a citus local table to a distributed/reference table,\n\t\t\t * so we should prevent dropping the sequence on the table. Otherwise, we'd\n\t\t\t * lose track of the previous changes in the sequence.\n\t\t\t */\n\t\t\tchar *command = WorkerDropSequenceDependencyCommand(sourceId);\n\t\t\tSendCommandToWorkersWithMetadata(command);\n\t\t}\n\t}\n\n\tchar *justBeforeDropCommand = NULL;\n\tforeach_declared_ptr(justBeforeDropCommand, justBeforeDropCommands)\n\t{\n\t\tExecuteQueryViaSPI(justBeforeDropCommand, SPI_OK_UTILITY);\n\t}\n\n\tif (!suppressNoticeMessages)\n\t{\n\t\tereport(NOTICE, (errmsg(\"dropping the old %s\", qualifiedSourceName)));\n\t}\n\n\tresetStringInfo(query);\n\tappendStringInfo(query, \"DROP %sTABLE %s CASCADE\",\n\t\t\t\t\t IsForeignTable(sourceId) ? \"FOREIGN \" : \"\",\n\t\t\t\t\t qualifiedSourceName);\n\tExecuteQueryViaSPI(query->data, SPI_OK_UTILITY);\n\n\tif (!suppressNoticeMessages)\n\t{\n\t\tereport(NOTICE, (errmsg(\"renaming the new table to %s\", qualifiedSourceName)));\n\t}\n\n\tresetStringInfo(query);\n\tappendStringInfo(query, \"ALTER TABLE %s RENAME TO %s\",\n\t\t\t\t\t qualifiedTargetName,\n\t\t\t\t\t quote_identifier(sourceName));\n\tExecuteQueryViaSPI(query->data, SPI_OK_UTILITY);\n}\n\n\n/*\n * HasAnyGeneratedStoredColumns decides if relation has any columns that we\n * might need to copy the data of when replacing table.\n */\nstatic bool\nHasAnyGeneratedStoredColumns(Oid relationId)\n{\n\treturn list_length(GetNonGeneratedStoredColumnNameList(relationId)) > 0;\n}\n\n\n/*\n * GetNonGeneratedStoredColumnNameList returns a list of column names for\n * columns not having GENERATED ALWAYS AS (...) STORED expressions.\n */\nstatic List *\nGetNonGeneratedStoredColumnNameList(Oid relationId)\n{\n\tList *nonStoredColumnNameList = NIL;\n\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\tfor (int columnIndex = 0; columnIndex < tupleDescriptor->natts; columnIndex++)\n\t{\n\t\tForm_pg_attribute currentColumn = TupleDescAttr(tupleDescriptor, columnIndex);\n\n\t\tif (IsDroppedOrGenerated(currentColumn))\n\t\t{\n\t\t\t/* skip dropped or generated columns */\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst char *quotedColumnName = quote_identifier(NameStr(currentColumn->attname));\n\t\tnonStoredColumnNameList = lappend(nonStoredColumnNameList,\n\t\t\t\t\t\t\t\t\t\t  pstrdup(quotedColumnName));\n\t}\n\n\trelation_close(relation, NoLock);\n\n\treturn nonStoredColumnNameList;\n}\n\n\n/*\n * CheckAlterDistributedTableConversionParameters errors for the cases where\n * alter_distributed_table UDF wouldn't work.\n */\nvoid\nCheckAlterDistributedTableConversionParameters(TableConversionState *con)\n{\n\t/* Changing nothing is not allowed */\n\tif (con->distributionColumn == NULL && con->shardCountIsNull &&\n\t\tcon->colocateWith == NULL && con->cascadeToColocated != CASCADE_TO_COLOCATED_YES)\n\t{\n\t\tereport(ERROR, (errmsg(\"you have to specify at least one of the \"\n\t\t\t\t\t\t\t   \"distribution_column, shard_count or \"\n\t\t\t\t\t\t\t   \"colocate_with parameters\")));\n\t}\n\n\t/* check if the parameters in this conversion are given and same with table's properties */\n\tbool sameDistColumn = false;\n\tif (con->distributionColumn != NULL &&\n\t\tequal(con->distributionKey, con->originalDistributionKey))\n\t{\n\t\tsameDistColumn = true;\n\t}\n\n\tbool sameShardCount = false;\n\tif (!con->shardCountIsNull && con->originalShardCount == con->shardCount)\n\t{\n\t\tsameShardCount = true;\n\t}\n\n\tbool sameColocateWith = false;\n\tif (con->colocateWith != NULL && strcmp(con->colocateWith, \"default\") != 0 &&\n\t\tstrcmp(con->colocateWith, \"none\") != 0)\n\t{\n\t\t/* check if already colocated with colocate_with */\n\t\tOid colocatedTableOid = InvalidOid;\n\t\ttext *colocateWithText = cstring_to_text(con->colocateWith);\n\t\tOid colocateWithTableOid = ResolveRelationId(colocateWithText, false);\n\t\tforeach_declared_oid(colocatedTableOid, con->colocatedTableList)\n\t\t{\n\t\t\tif (colocateWithTableOid == colocatedTableOid)\n\t\t\t{\n\t\t\t\tsameColocateWith = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * already found colocateWithTableOid so let's check if\n\t\t * colocate_with table is a distributed table\n\t\t */\n\t\tif (!IsCitusTableType(colocateWithTableOid, DISTRIBUTED_TABLE))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot colocate with %s because \"\n\t\t\t\t\t\t\t\t   \"it is not a distributed table\",\n\t\t\t\t\t\t\t\t   con->colocateWith)));\n\t\t}\n\t\telse if (IsCitusTableType(colocateWithTableOid, SINGLE_SHARD_DISTRIBUTED))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot colocate with %s because \"\n\t\t\t\t\t\t\t\t   \"it is a single shard distributed table\",\n\t\t\t\t\t\t\t\t   con->colocateWith)));\n\t\t}\n\t}\n\n\t/* shard_count:=0 is not allowed */\n\tif (!con->shardCountIsNull && con->shardCount == 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"shard_count cannot be 0\"),\n\t\t\t\t\t\terrhint(\"if you no longer want this to be a \"\n\t\t\t\t\t\t\t\t\"distributed table you can try \"\n\t\t\t\t\t\t\t\t\"undistribute_table() function\")));\n\t}\n\n\tif (con->cascadeToColocated == CASCADE_TO_COLOCATED_YES &&\n\t\tcon->distributionColumn != NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"distribution_column cannot be \"\n\t\t\t\t\t\t\t   \"cascaded to colocated tables\")));\n\t}\n\tif (con->cascadeToColocated == CASCADE_TO_COLOCATED_YES && con->shardCountIsNull &&\n\t\tcon->colocateWith == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"shard_count or colocate_with is necessary \"\n\t\t\t\t\t\t\t   \"for cascading to colocated tables\")));\n\t}\n\n\t/*\n\t * if every parameter is either not given or already the\n\t * same then give error\n\t */\n\tif ((con->distributionColumn == NULL || sameDistColumn) &&\n\t\t(con->shardCountIsNull || sameShardCount) &&\n\t\t(con->colocateWith == NULL || sameColocateWith))\n\t{\n\t\tereport(ERROR, (errmsg(\"this call doesn't change any properties of the table\"),\n\t\t\t\t\t\terrhint(\"check citus_tables view to see current \"\n\t\t\t\t\t\t\t\t\"properties of the table\")));\n\t}\n\tif (con->cascadeToColocated == CASCADE_TO_COLOCATED_YES &&\n\t\tcon->colocateWith != NULL &&\n\t\tstrcmp(con->colocateWith, \"none\") == 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"colocate_with := 'none' cannot be \"\n\t\t\t\t\t\t\t   \"cascaded to colocated tables\")));\n\t}\n\n\tint colocatedTableCount = list_length(con->colocatedTableList) - 1;\n\tif (colocatedTableCount > 0 && !con->shardCountIsNull && !sameShardCount &&\n\t\tcon->cascadeToColocated == CASCADE_TO_COLOCATED_UNSPECIFIED)\n\t{\n\t\tereport(ERROR, (errmsg(\"cascade_to_colocated parameter is necessary\"),\n\t\t\t\t\t\terrdetail(\"this table is colocated with some other tables\"),\n\t\t\t\t\t\terrhint(\"cascade_to_colocated := false will break the \"\n\t\t\t\t\t\t\t\t\"current colocation, cascade_to_colocated := true \"\n\t\t\t\t\t\t\t\t\"will change the shard count of colocated tables \"\n\t\t\t\t\t\t\t\t\"too.\")));\n\t}\n\n\tif (con->colocateWith != NULL && strcmp(con->colocateWith, \"default\") != 0 &&\n\t\tstrcmp(con->colocateWith, \"none\") != 0)\n\t{\n\t\ttext *colocateWithText = cstring_to_text(con->colocateWith);\n\t\tOid colocateWithTableOid = ResolveRelationId(colocateWithText, false);\n\t\tCitusTableCacheEntry *colocateWithTableCacheEntry =\n\t\t\tGetCitusTableCacheEntry(colocateWithTableOid);\n\t\tint colocateWithTableShardCount =\n\t\t\tcolocateWithTableCacheEntry->shardIntervalArrayLength;\n\n\t\tif (!con->shardCountIsNull && con->shardCount != colocateWithTableShardCount)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"shard_count cannot be different than the shard \"\n\t\t\t\t\t\t\t\t   \"count of the table in colocate_with\"),\n\t\t\t\t\t\t\terrhint(\"if no shard_count is specified shard count \"\n\t\t\t\t\t\t\t\t\t\"will be same with colocate_with table's\")));\n\t\t}\n\n\t\tif (colocateWithTableShardCount != con->originalShardCount)\n\t\t{\n\t\t\t/*\n\t\t\t * shardCount is either 0 or already same with colocateWith table's\n\t\t\t * It's ok to set shardCountIsNull to false because we assume giving a table\n\t\t\t * to colocate with and no shard count is the same with giving colocate_with\n\t\t\t * table's shard count if it is different than the original.\n\t\t\t * So it is almost like the shard_count parameter was given by the user.\n\t\t\t */\n\t\t\tcon->shardCount = colocateWithTableShardCount;\n\t\t\tcon->shardCountIsNull = false;\n\t\t}\n\n\t\tVar *colocateWithPartKey = DistPartitionKey(colocateWithTableOid);\n\n\t\tif (colocateWithPartKey == NULL)\n\t\t{\n\t\t\t/* this should never happen */\n\t\t\tereport(ERROR, (errmsg(\"cannot colocate %s with %s because %s doesn't have a \"\n\t\t\t\t\t\t\t\t   \"distribution column\",\n\t\t\t\t\t\t\t\t   con->relationName, con->colocateWith,\n\t\t\t\t\t\t\t\t   con->colocateWith)));\n\t\t}\n\t\telse if (con->distributionColumn &&\n\t\t\t\t colocateWithPartKey->vartype != con->distributionKey->vartype)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot colocate with %s and change distribution \"\n\t\t\t\t\t\t\t\t   \"column to %s because data type of column %s is \"\n\t\t\t\t\t\t\t\t   \"different than the distribution column of the %s\",\n\t\t\t\t\t\t\t\t   con->colocateWith, con->distributionColumn,\n\t\t\t\t\t\t\t\t   con->distributionColumn, con->colocateWith)));\n\t\t}\n\t\telse if (!con->distributionColumn &&\n\t\t\t\t colocateWithPartKey->vartype != con->originalDistributionKey->vartype)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot colocate with %s because data type of its \"\n\t\t\t\t\t\t\t\t   \"distribution column is different than %s\",\n\t\t\t\t\t\t\t\t   con->colocateWith, con->relationName)));\n\t\t}\n\t\telse if (con->distributionColumn &&\n\t\t\t\t colocateWithPartKey->varcollid != con->distributionKey->varcollid)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot colocate with %s and change distribution \"\n\t\t\t\t\t\t\t\t   \"column to %s because collation of column %s is \"\n\t\t\t\t\t\t\t\t   \"different than the distribution column of the %s\",\n\t\t\t\t\t\t\t\t   con->colocateWith, con->distributionColumn,\n\t\t\t\t\t\t\t\t   con->distributionColumn, con->colocateWith)));\n\t\t}\n\t\telse if (!con->distributionColumn &&\n\t\t\t\t colocateWithPartKey->varcollid != con->originalDistributionKey->varcollid\n\t\t\t\t )\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot colocate with %s because collation of its \"\n\t\t\t\t\t\t\t\t   \"distribution column is different than %s\",\n\t\t\t\t\t\t\t\t   con->colocateWith, con->relationName)));\n\t\t}\n\t}\n\n\tif (!con->suppressNoticeMessages)\n\t{\n\t\t/* Notices for no operation UDF calls */\n\t\tif (sameDistColumn)\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\"table is already distributed by %s\",\n\t\t\t\t\t\t\t\t\tcon->distributionColumn)));\n\t\t}\n\n\t\tif (sameShardCount)\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\"shard count of the table is already %d\",\n\t\t\t\t\t\t\t\t\tcon->shardCount)));\n\t\t}\n\n\t\tif (sameColocateWith)\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\"table is already colocated with %s\",\n\t\t\t\t\t\t\t\t\tcon->colocateWith)));\n\t\t}\n\t}\n}\n\n\n/*\n * CreateWorkerChangeSequenceDependencyCommand creates and returns a\n * worker_change_sequence_dependency query with the parameters.\n */\nstatic char *\nCreateWorkerChangeSequenceDependencyCommand(char *qualifiedSequeceName,\n\t\t\t\t\t\t\t\t\t\t\tchar *qualifiedSourceName,\n\t\t\t\t\t\t\t\t\t\t\tchar *qualifiedTargetName)\n{\n\tStringInfo query = makeStringInfo();\n\tappendStringInfo(query, \"SELECT worker_change_sequence_dependency(%s, %s, %s)\",\n\t\t\t\t\t quote_literal_cstr(qualifiedSequeceName),\n\t\t\t\t\t quote_literal_cstr(qualifiedSourceName),\n\t\t\t\t\t quote_literal_cstr(qualifiedTargetName));\n\n\treturn query->data;\n}\n\n\n/*\n * GetAccessMethodForMatViewIfExists returns if there's an access method\n * set to the view with the given oid. Returns NULL otherwise.\n */\nstatic char *\nGetAccessMethodForMatViewIfExists(Oid viewOid)\n{\n\tchar *accessMethodName = NULL;\n\tRelation relation = try_relation_open(viewOid, AccessShareLock);\n\tif (relation == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot complete operation \"\n\t\t\t\t\t\t\t   \"because no such view exists\")));\n\t}\n\n\tOid accessMethodOid = relation->rd_rel->relam;\n\tif (OidIsValid(accessMethodOid))\n\t{\n\t\taccessMethodName = get_am_name(accessMethodOid);\n\t}\n\trelation_close(relation, NoLock);\n\n\treturn accessMethodName;\n}\n\n\n/*\n * WillRecreateFKeyToReferenceTable checks if the table of relationId has any foreign\n * key to a reference table, if conversion will be cascaded to colocated table this function\n * also checks if any of the colocated tables have a foreign key to a reference table too\n */\nbool\nWillRecreateFKeyToReferenceTable(Oid relationId,\n\t\t\t\t\t\t\t\t CascadeToColocatedOption cascadeOption)\n{\n\tif (cascadeOption == CASCADE_TO_COLOCATED_NO ||\n\t\tcascadeOption == CASCADE_TO_COLOCATED_UNSPECIFIED)\n\t{\n\t\treturn HasForeignKeyToReferenceTable(relationId);\n\t}\n\telse if (cascadeOption == CASCADE_TO_COLOCATED_YES)\n\t{\n\t\tList *colocatedTableList = ColocatedTableList(relationId);\n\t\tOid colocatedTableOid = InvalidOid;\n\t\tforeach_declared_oid(colocatedTableOid, colocatedTableList)\n\t\t{\n\t\t\tif (HasForeignKeyToReferenceTable(colocatedTableOid))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n\n/*\n * WarningsForDroppingForeignKeysWithDistributedTables gives warnings for the\n * foreign keys that will be dropped because formerly colocated distributed tables\n * are not colocated.\n */\nvoid\nWarningsForDroppingForeignKeysWithDistributedTables(Oid relationId)\n{\n\tint flags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_DISTRIBUTED_TABLES;\n\tList *referencingForeingKeys = GetForeignKeyOids(relationId, flags);\n\tflags = INCLUDE_REFERENCED_CONSTRAINTS | INCLUDE_DISTRIBUTED_TABLES;\n\tList *referencedForeignKeys = GetForeignKeyOids(relationId, flags);\n\n\tList *foreignKeys = list_concat(referencingForeingKeys, referencedForeignKeys);\n\n\tOid foreignKeyOid = InvalidOid;\n\tforeach_declared_oid(foreignKeyOid, foreignKeys)\n\t{\n\t\tereport(WARNING, (errmsg(\"foreign key %s will be dropped\",\n\t\t\t\t\t\t\t\t get_constraint_name(foreignKeyOid))));\n\t}\n}\n\n\n/*\n * ExecuteQueryViaSPI connects to SPI, executes the query and checks if it\n * returned the OK value and finishes the SPI connection\n */\nvoid\nExecuteQueryViaSPI(char *query, int SPIOK)\n{\n\tint spiResult = SPI_connect();\n\tif (spiResult != SPI_OK_CONNECT)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not connect to SPI manager\")));\n\t}\n\n\tspiResult = SPI_execute(query, false, 0);\n\tif (spiResult != SPIOK)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not run SPI query\")));\n\t}\n\n\tspiResult = SPI_finish();\n\tif (spiResult != SPI_OK_FINISH)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not finish SPI connection\")));\n\t}\n}\n\n\n/*\n * ExecuteAndLogQueryViaSPI is a wrapper around ExecuteQueryViaSPI, that logs\n * the query to be executed, with the given log level.\n */\nvoid\nExecuteAndLogQueryViaSPI(char *query, int SPIOK, int logLevel)\n{\n\tereport(logLevel, (errmsg(\"executing \\\"%s\\\"\", query)));\n\n\tExecuteQueryViaSPI(query, SPIOK);\n}\n\n\n/*\n * SwitchToSequentialAndLocalExecutionIfRelationNameTooLong generates the longest shard name\n * on the shards of a distributed table, and if exceeds the limit switches to sequential and\n * local execution to prevent self-deadlocks.\n *\n * In case of a RENAME, the relation name parameter should store the new table name, so\n * that the function can generate shard names of the renamed relations\n */\nvoid\nSwitchToSequentialAndLocalExecutionIfRelationNameTooLong(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t char *finalRelationName)\n{\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn;\n\t}\n\n\tif (ShardIntervalCount(relationId) == 0)\n\t{\n\t\t/*\n\t\t * Relation has no shards, so we cannot run into \"long shard relation\n\t\t * name\" issue.\n\t\t */\n\t\treturn;\n\t}\n\n\tchar *longestShardName = GetLongestShardName(relationId, finalRelationName);\n\tbool switchedToSequentialAndLocalExecution =\n\t\tSwitchToSequentialAndLocalExecutionIfShardNameTooLong(finalRelationName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  longestShardName);\n\n\tif (switchedToSequentialAndLocalExecution)\n\t{\n\t\treturn;\n\t}\n\n\tif (PartitionedTable(relationId))\n\t{\n\t\tOid longestNamePartitionId = PartitionWithLongestNameRelationId(relationId);\n\t\tif (!OidIsValid(longestNamePartitionId))\n\t\t{\n\t\t\t/* no partitions have been created yet */\n\t\t\treturn;\n\t\t}\n\n\t\tchar *longestPartitionName = get_rel_name(longestNamePartitionId);\n\t\tchar *longestPartitionShardName = NULL;\n\n\t\t/*\n\t\t * Use the shardId values of the partition if it is distributed, otherwise use\n\t\t * hypothetical values\n\t\t */\n\t\tif (IsCitusTable(longestNamePartitionId) &&\n\t\t\tShardIntervalCount(longestNamePartitionId) > 0)\n\t\t{\n\t\t\tlongestPartitionShardName =\n\t\t\t\tGetLongestShardName(longestNamePartitionId, longestPartitionName);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tlongestPartitionShardName =\n\t\t\t\tGetLongestShardNameForLocalPartition(relationId, longestPartitionName);\n\t\t}\n\n\t\tSwitchToSequentialAndLocalExecutionIfShardNameTooLong(longestPartitionName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  longestPartitionShardName);\n\t}\n}\n\n\n/*\n * SwitchToSequentialAndLocalExecutionIfShardNameTooLong switches to sequential and local\n * execution if the shard name is too long.\n *\n * returns true if switched to sequential and local execution.\n */\nstatic bool\nSwitchToSequentialAndLocalExecutionIfShardNameTooLong(char *relationName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  char *longestShardName)\n{\n\tif (strlen(longestShardName) >= NAMEDATALEN - 1)\n\t{\n\t\tif (ParallelQueryExecutedInTransaction())\n\t\t{\n\t\t\t/*\n\t\t\t * If there has already been a parallel query executed, the sequential mode\n\t\t\t * would still use the already opened parallel connections to the workers,\n\t\t\t * thus contradicting our purpose of using sequential mode.\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\"Shard name (%s) for table (%s) is too long and could \"\n\t\t\t\t\t\t\t\t\"lead to deadlocks when executed in a transaction \"\n\t\t\t\t\t\t\t\t\"block after a parallel query\", longestShardName,\n\t\t\t\t\t\t\t\trelationName),\n\t\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t\t}\n\t\telse\n\t\t{\n\t\t\telog(DEBUG1, \"the name of the shard (%s) for relation (%s) is too long, \"\n\t\t\t\t\t\t \"switching to sequential and local execution mode to prevent \"\n\t\t\t\t\t\t \"self deadlocks\",\n\t\t\t\t longestShardName, relationName);\n\n\t\t\tSetLocalMultiShardModifyModeToSequential();\n\t\t\tSetLocalExecutionStatus(LOCAL_EXECUTION_REQUIRED);\n\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * SwitchToSequentialAndLocalExecutionIfPartitionNameTooLong is a wrapper for new\n * partitions that will be distributed after attaching to a distributed partitioned table\n */\nvoid\nSwitchToSequentialAndLocalExecutionIfPartitionNameTooLong(Oid parentRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Oid partitionRelationId)\n{\n\tSwitchToSequentialAndLocalExecutionIfRelationNameTooLong(\n\t\tparentRelationId, get_rel_name(partitionRelationId));\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/begin.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * begin.c\n *    Processing of the BEGIN command.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n\n#include \"nodes/parsenodes.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/transaction_management.h\"\n\n\n/*\n * SaveBeginCommandProperties stores the transaction properties passed\n * via BEGIN.\n */\nvoid\nSaveBeginCommandProperties(TransactionStmt *transactionStmt)\n{\n\tDefElem *item = NULL;\n\n\t/*\n\t * This loop is similar to the one in standard_ProcessUtility.\n\t *\n\t * While BEGIN can be quite frequent it will rarely have options set.\n\t */\n\tforeach_declared_ptr(item, transactionStmt->options)\n\t{\n\t\tA_Const *constant = (A_Const *) item->arg;\n\n\t\tif (strcmp(item->defname, \"transaction_read_only\") == 0)\n\t\t{\n\t\t\tif (intVal(&constant->val) == 1)\n\t\t\t{\n\t\t\t\tBeginXactReadOnly = BeginXactReadOnly_Enabled;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tBeginXactReadOnly = BeginXactReadOnly_Disabled;\n\t\t\t}\n\t\t}\n\t\telse if (strcmp(item->defname, \"transaction_deferrable\") == 0)\n\t\t{\n\t\t\tif (intVal(&constant->val) == 1)\n\t\t\t{\n\t\t\t\tBeginXactDeferrable = BeginXactDeferrable_Enabled;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tBeginXactDeferrable = BeginXactDeferrable_Disabled;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/call.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * call.c\n *    Commands for distributing CALL for distributed procedures.\n *\n *    Procedures can be distributed with create_distributed_function.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"catalog/pg_proc.h\"\n#include \"commands/defrem.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/primnodes.h\"\n#include \"optimizer/clauses.h\"\n#include \"tcop/dest.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/backend_data.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/function_call_delegation.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/tuple_destination.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_log_messages.h\"\n#include \"distributed/worker_manager.h\"\n\n\n/* global variable tracking whether we are in a delegated procedure call */\nbool InDelegatedProcedureCall = false;\n\n\n/*\n * CallDistributedProcedureRemotely calls a stored procedure on the worker if possible.\n */\nbool\nCallDistributedProcedureRemotely(CallStmt *callStmt, DestReceiver *dest)\n{\n\tFuncExpr *funcExpr = callStmt->funcexpr;\n\tOid functionId = funcExpr->funcid;\n\n\tDistObjectCacheEntry *procedure = LookupDistObjectCacheEntry(ProcedureRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t functionId, 0);\n\tif (procedure == NULL || !procedure->isDistributed)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsCitusInternalBackend())\n\t{\n\t\t/*\n\t\t * We are in a citus-initiated backend handling a CALL to a distributed\n\t\t * procedure. That means that this is the delegated call.\n\t\t */\n\t\tInDelegatedProcedureCall = true;\n\t\treturn false;\n\t}\n\n\tif (IsMultiStatementTransaction())\n\t{\n\t\tereport(DEBUG1, (errmsg(\"cannot push down CALL in multi-statement transaction\")));\n\t\treturn false;\n\t}\n\n\tOid colocatedRelationId = ColocatedTableId(procedure->colocationId);\n\tif (colocatedRelationId == InvalidOid)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"stored procedure does not have co-located tables\")));\n\t\treturn false;\n\t}\n\n\tif (contain_volatile_functions((Node *) funcExpr->args))\n\t{\n\t\tereport(DEBUG1, (errmsg(\"arguments in a distributed stored procedure must \"\n\t\t\t\t\t\t\t\t\"be constant expressions\")));\n\t\treturn false;\n\t}\n\n\tCitusTableCacheEntry *distTable = GetCitusTableCacheEntry(colocatedRelationId);\n\tVar *partitionColumn = distTable->partitionColumn;\n\tbool colocatedWithReferenceTable = false;\n\tif (IsCitusTableTypeCacheEntry(distTable, REFERENCE_TABLE))\n\t{\n\t\t/* This can happen if colocated with a reference table. Punt for now. */\n\t\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t\t \"will push down CALL for reference tables\")));\n\t\tcolocatedWithReferenceTable = true;\n\t}\n\n\tShardPlacement *placement = NULL;\n\tif (colocatedWithReferenceTable)\n\t{\n\t\tplacement = ShardPlacementForFunctionColocatedWithReferenceTable(distTable);\n\t}\n\telse\n\t{\n\t\tList *argumentList = NIL;\n\t\tList *namedArgList;\n\t\tint numberOfArgs;\n\t\tOid *argumentTypes;\n\n\t\tif (!get_merged_argument_list(callStmt, &namedArgList, &argumentTypes,\n\t\t\t\t\t\t\t\t\t  &argumentList, &numberOfArgs))\n\t\t{\n\t\t\targumentList = funcExpr->args;\n\t\t}\n\n\t\tplacement =\n\t\t\tShardPlacementForFunctionColocatedWithDistTable(procedure, argumentList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionColumn, distTable,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL);\n\t}\n\n\t/* return if we could not find a placement */\n\tif (placement == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tWorkerNode *workerNode = FindWorkerNode(placement->nodeName, placement->nodePort);\n\tif (workerNode == NULL || !workerNode->hasMetadata || !workerNode->metadataSynced)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"there is no worker node with metadata\")));\n\t\treturn false;\n\t}\n\telse if (workerNode->groupId == GetLocalGroupId())\n\t{\n\t\t/*\n\t\t * Two reasons for this:\n\t\t *  (a) It would lead to infinite recursion as the node would\n\t\t *      keep pushing down the procedure as it gets\n\t\t *  (b) It doesn't have any value to pushdown as we are already\n\t\t *      on the node itself\n\t\t */\n\t\tereport(DEBUG1, (errmsg(\"not pushing down procedure to the same node\")));\n\t\treturn false;\n\t}\n\n\tereport(DEBUG1, (errmsg(\"pushing down the procedure\")));\n\n\t/* build remote command with fully qualified names */\n\tStringInfo callCommand = makeStringInfo();\n\n\tappendStringInfo(callCommand, \"CALL %s\", pg_get_rule_expr((Node *) callStmt));\n\t{\n\t\tTuplestorestate *tupleStore = tuplestore_begin_heap(true, false, work_mem);\n\t\tTupleDesc tupleDesc = CallStmtResultDesc(callStmt);\n\t\tTupleTableSlot *slot = MakeSingleTupleTableSlot(tupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t&TTSOpsMinimalTuple);\n\t\tbool expectResults = true;\n\t\tTask *task = CitusMakeNode(Task);\n\n\t\ttask->jobId = INVALID_JOB_ID;\n\t\ttask->taskId = INVALID_TASK_ID;\n\t\ttask->taskType = DDL_TASK;\n\t\tSetTaskQueryString(task, callCommand->data);\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\ttask->dependentTaskList = NIL;\n\t\ttask->anchorShardId = placement->shardId;\n\t\ttask->relationShardList = NIL;\n\t\ttask->taskPlacementList = list_make1(placement);\n\n\t\t/*\n\t\t * We are delegating the distributed transaction to the worker, so we\n\t\t * should not run the CALL in a transaction block.\n\t\t */\n\t\tTransactionProperties xactProperties = {\n\t\t\t.errorOnAnyFailure = true,\n\t\t\t.useRemoteTransactionBlocks = TRANSACTION_BLOCKS_DISALLOWED,\n\t\t\t.requires2PC = false\n\t\t};\n\n\t\tEnableWorkerMessagePropagation();\n\n\t\tbool localExecutionSupported = true;\n\t\tExecutionParams *executionParams = CreateBasicExecutionParams(\n\t\t\tROW_MODIFY_NONE, list_make1(task), MaxAdaptiveExecutorPoolSize,\n\t\t\tlocalExecutionSupported\n\t\t\t);\n\t\texecutionParams->tupleDestination = CreateTupleStoreTupleDest(tupleStore,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  tupleDesc);\n\t\texecutionParams->expectResults = expectResults;\n\t\texecutionParams->xactProperties = xactProperties;\n\t\texecutionParams->isUtilityCommand = true;\n\t\tExecuteTaskListExtended(executionParams);\n\n\t\tDisableWorkerMessagePropagation();\n\n\t\twhile (tuplestore_gettupleslot(tupleStore, true, false, slot))\n\t\t{\n\t\t\tif (!dest->receiveSlot(slot, dest))\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/* Don't call tuplestore_end(tupleStore). It'll be freed soon enough in a top level CALL,\n\t\t * & dest->receiveSlot could conceivably rely on slots being long lived.\n\t\t */\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/cascade_table_operation_for_connected_relations.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * cascade_table_operation_for_connected_relations.c\n *   Routines to execute citus table functions (e.g undistribute_table,\n *   create_citus_local_table) by cascading to foreign key connected\n *   relations.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/xact.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"executor/spi.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/foreign_key_relationship.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/worker_protocol.h\"\n\n\nstatic void EnsureSequentialModeForCitusTableCascadeFunction(List *relationIdList);\nstatic List * GetPartitionRelationIds(List *relationIdList);\nstatic void LockRelationsWithLockMode(List *relationIdList, LOCKMODE lockMode);\nstatic void ErrorIfConvertingMultiLevelPartitionedTable(List *relationIdList);\nstatic void DropRelationIdListForeignKeys(List *relationIdList, int fKeyFlags);\nstatic List * GetRelationDropFkeyCommands(Oid relationId, int fKeyFlags);\nstatic char * GetDropFkeyCascadeCommand(Oid foreignKeyId);\nstatic void ExecuteCascadeOperationForRelationIdList(List *relationIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t CascadeOperationType\n\t\t\t\t\t\t\t\t\t\t\t\t\t cascadeOperationType);\nstatic void ExecuteForeignKeyCreateCommand(const char *commandString,\n\t\t\t\t\t\t\t\t\t\t   bool skip_validation);\n\n/*\n * CascadeOperationForFkeyConnectedRelations is a wrapper function which calls\n * CascadeOperationForRelationIdList for the foreign key connected relations, for\n * the given relationId.\n */\nvoid\nCascadeOperationForFkeyConnectedRelations(Oid relationId, LOCKMODE lockMode,\n\t\t\t\t\t\t\t\t\t\t  CascadeOperationType\n\t\t\t\t\t\t\t\t\t\t  cascadeOperationType)\n{\n\t/*\n\t * As we will operate on foreign key connected relations, here we\n\t * invalidate foreign key graph to be on the safe side.\n\t */\n\tInvalidateForeignKeyGraph();\n\n\tList *fKeyConnectedRelationIdList = GetForeignKeyConnectedRelationIdList(relationId);\n\n\t/* early exit if there are no connected relations */\n\tif (fKeyConnectedRelationIdList == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tCascadeOperationForRelationIdList(fKeyConnectedRelationIdList, lockMode,\n\t\t\t\t\t\t\t\t\t  cascadeOperationType);\n}\n\n\n/*\n * CascadeOperationForRelationIdList executes citus table function specified\n * by CascadeOperationType argument on each relation in the relationIdList;\n * Also see CascadeOperationType enum definition for supported\n * citus table functions.\n */\nvoid\nCascadeOperationForRelationIdList(List *relationIdList, LOCKMODE lockMode,\n\t\t\t\t\t\t\t\t  CascadeOperationType\n\t\t\t\t\t\t\t\t  cascadeOperationType)\n{\n\tLockRelationsWithLockMode(relationIdList, lockMode);\n\n\tif (cascadeOperationType == CASCADE_USER_ADD_LOCAL_TABLE_TO_METADATA ||\n\t\tcascadeOperationType == CASCADE_AUTO_ADD_LOCAL_TABLE_TO_METADATA)\n\t{\n\t\t/*\n\t\t * In CreateCitusLocalTable function, this check would never error out,\n\t\t * since CreateCitusLocalTable gets called with partition relations, *after*\n\t\t * they are detached.\n\t\t * Instead, here, it would error out if the user tries to convert a multi-level\n\t\t * partitioned table, since partitioned table conversions always go through here.\n\t\t * Also, there can be a multi-level partitioned table, to be cascaded via foreign\n\t\t * keys, and they are hard to detect in CreateCitusLocalTable.\n\t\t * Therefore, we put this check here.\n\t\t */\n\t\tErrorIfConvertingMultiLevelPartitionedTable(relationIdList);\n\t}\n\n\t/*\n\t * Before removing any partition relations, we should error out here if any\n\t * of connected relations is a partition table involved in a foreign key that\n\t * is not inherited from its parent table.\n\t * We should handle this case here as we remove partition relations in this\n\t * function\tbefore ExecuteCascadeOperationForRelationIdList.\n\t */\n\tErrorIfAnyPartitionRelationInvolvedInNonInheritedFKey(relationIdList);\n\n\tList *partitonRelationList = GetPartitionRelationIds(relationIdList);\n\n\t/*\n\t * Here we generate detach/attach commands, if there are any partition tables\n\t * in our \"relations-to-cascade\" list.\n\t */\n\tList *detachPartitionCommands =\n\t\tGenerateDetachPartitionCommandRelationIdList(partitonRelationList);\n\tList *attachPartitionCommands =\n\t\tGenerateAttachPartitionCommandRelationIdList(partitonRelationList);\n\n\t/*\n\t * Our foreign key subgraph can have distributed tables which might already\n\t * be modified in current transaction. So switch to sequential execution\n\t * before executing any ddl's to prevent erroring out later in this function.\n\t */\n\tEnsureSequentialModeForCitusTableCascadeFunction(relationIdList);\n\n\t/* store foreign key creation commands before dropping them */\n\tList *fKeyCreationCommands =\n\t\tGetFKeyCreationCommandsForRelationIdList(relationIdList);\n\n\t/*\n\t * Note that here we only drop referencing foreign keys for each relation.\n\t * This is because referenced foreign keys are already captured as other\n\t * relations' referencing foreign keys.\n\t */\n\tint fKeyFlags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES;\n\tDropRelationIdListForeignKeys(relationIdList, fKeyFlags);\n\n\tExecuteAndLogUtilityCommandList(detachPartitionCommands);\n\n\tExecuteCascadeOperationForRelationIdList(relationIdList,\n\t\t\t\t\t\t\t\t\t\t\t cascadeOperationType);\n\n\tExecuteAndLogUtilityCommandList(attachPartitionCommands);\n\n\t/* now recreate foreign keys on tables */\n\tbool skip_validation = true;\n\tExecuteForeignKeyCreateCommandList(fKeyCreationCommands, skip_validation);\n}\n\n\n/*\n * GetPartitionRelationIds returns a list of relation id's by picking\n * partition relation id's from given relationIdList.\n */\nstatic List *\nGetPartitionRelationIds(List *relationIdList)\n{\n\tList *partitionRelationIdList = NIL;\n\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tif (PartitionTable(relationId))\n\t\t{\n\t\t\tpartitionRelationIdList = lappend_oid(partitionRelationIdList, relationId);\n\t\t}\n\t}\n\n\treturn partitionRelationIdList;\n}\n\n\n/*\n * LockRelationsWithLockMode sorts given relationIdList and then acquires\n * specified lockMode on those relations.\n */\nstatic void\nLockRelationsWithLockMode(List *relationIdList, LOCKMODE lockMode)\n{\n\tOid relationId;\n\trelationIdList = SortList(relationIdList, CompareOids);\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tLockRelationOid(relationId, lockMode);\n\t}\n}\n\n\n/*\n * ErrorIfConvertingMultiLevelPartitionedTable iterates given relationIdList and checks\n * if there's a multi-level partitioned table involved or not. As we currently don't\n * support converting multi-level partitioned tables into Citus Local Tables,\n * this function errors out for such a case. We detect the multi-level partitioned\n * table if one of the relations is both partition and partitioned table.\n */\nstatic void\nErrorIfConvertingMultiLevelPartitionedTable(List *relationIdList)\n{\n\tOid relationId;\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tif (PartitionedTable(relationId) && PartitionTable(relationId))\n\t\t{\n\t\t\tOid parentRelId = PartitionParentOid(relationId);\n\t\t\tchar *parentRelationName = get_rel_name(parentRelId);\n\t\t\tchar *relationName = get_rel_name(relationId);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"Citus does not support multi-level \"\n\t\t\t\t\t\t\t\t   \"partitioned tables\"),\n\t\t\t\t\t\t\terrdetail(\"Relation \\\"%s\\\" is partitioned table itself so \"\n\t\t\t\t\t\t\t\t\t  \"cannot be partition of relation \\\"%s\\\".\",\n\t\t\t\t\t\t\t\t\t  relationName, parentRelationName)));\n\t\t}\n\t}\n}\n\n\n/*\n * ErrorIfAnyPartitionRelationInvolvedInNonInheritedFKey searches given\n * relationIdList for a partition relation involved in a foreign key relationship\n * that is not inherited from its parent and errors out if such a partition\n * relation exists.\n */\nvoid\nErrorIfAnyPartitionRelationInvolvedInNonInheritedFKey(List *relationIdList)\n{\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tif (!PartitionTable(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!RelationInvolvedInAnyNonInheritedForeignKeys(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tchar *partitionRelationQualifiedName =\n\t\t\tgenerate_qualified_relation_name(relationId);\n\t\tereport(ERROR, (errmsg(\"cannot cascade operation via foreign keys as \"\n\t\t\t\t\t\t\t   \"partition table %s involved in a foreign key \"\n\t\t\t\t\t\t\t   \"relationship that is not inherited from its \"\n\t\t\t\t\t\t\t   \"parent table\", partitionRelationQualifiedName),\n\t\t\t\t\t\terrhint(\"Remove non-inherited foreign keys from %s and \"\n\t\t\t\t\t\t\t\t\"try operation again\", partitionRelationQualifiedName)));\n\t}\n}\n\n\n/*\n * EnsureSequentialModeForCitusTableCascadeFunction switches to sequential\n * execution mode if needed. If it's not possible, then errors out.\n */\nstatic void\nEnsureSequentialModeForCitusTableCascadeFunction(List *relationIdList)\n{\n\tif (!RelationIdListHasReferenceTable(relationIdList))\n\t{\n\t\t/*\n\t\t * We don't need to switch to sequential execution if there is no\n\t\t * reference table in our foreign key subgraph.\n\t\t */\n\t\treturn;\n\t}\n\n\tif (ParallelQueryExecutedInTransaction())\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot execute command because there was a parallel \"\n\t\t\t\t\t\t\t   \"operation on a distributed table in transaction\"),\n\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t}\n\n\tereport(DEBUG1, (errmsg(\"switching to sequential query execution mode because the \"\n\t\t\t\t\t\t\t\"operation cascades into distributed tables with foreign \"\n\t\t\t\t\t\t\t\"keys to reference tables\")));\n\tSetLocalMultiShardModifyModeToSequential();\n}\n\n\n/*\n * RelationIdListHasReferenceTable returns true if relationIdList has a relation\n * id that belongs to a reference table.\n */\nbool\nRelationIdListHasReferenceTable(List *relationIdList)\n{\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tif (IsCitusTableType(relationId, REFERENCE_TABLE))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * GetFKeyCreationCommandsForRelationIdList returns a list of DDL commands to\n * create foreign keys for each relation in relationIdList.\n */\nList *\nGetFKeyCreationCommandsForRelationIdList(List *relationIdList)\n{\n\tList *fKeyCreationCommands = NIL;\n\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tList *relationFKeyCreationCommands =\n\t\t\tGetReferencingForeignConstaintCommands(relationId);\n\t\tfKeyCreationCommands = list_concat(fKeyCreationCommands,\n\t\t\t\t\t\t\t\t\t\t   relationFKeyCreationCommands);\n\t}\n\n\treturn fKeyCreationCommands;\n}\n\n\n/*\n * DropRelationIdListForeignKeys drops foreign keys for each relation in given\n * relation id list.\n */\nstatic void\nDropRelationIdListForeignKeys(List *relationIdList, int fKeyFlags)\n{\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tDropRelationForeignKeys(relationId, fKeyFlags);\n\t}\n}\n\n\n/*\n * DropRelationForeignKeys drops foreign keys where the relation with\n * relationId is the referencing relation.\n */\nvoid\nDropRelationForeignKeys(Oid relationId, int fKeyFlags)\n{\n\t/*\n\t * We undistribute citus local tables that are not chained with any reference\n\t * tables via foreign keys at the end of the utility hook.\n\t * Here we temporarily set the related GUC to off to disable the logic for\n\t * internally executed DDL's that might invoke this mechanism unnecessarily.\n\t */\n\tbool oldEnableLocalReferenceForeignKeys = EnableLocalReferenceForeignKeys;\n\tSetLocalEnableLocalReferenceForeignKeys(false);\n\n\tList *dropFkeyCascadeCommandList = GetRelationDropFkeyCommands(relationId, fKeyFlags);\n\tExecuteAndLogUtilityCommandList(dropFkeyCascadeCommandList);\n\n\tSetLocalEnableLocalReferenceForeignKeys(oldEnableLocalReferenceForeignKeys);\n}\n\n\n/*\n * SetLocalEnableLocalReferenceForeignKeys is simply a C interface for setting\n * the following:\n *      SET LOCAL citus.enable_local_reference_table_foreign_keys = 'on'|'off';\n */\nvoid\nSetLocalEnableLocalReferenceForeignKeys(bool state)\n{\n\tchar *stateStr = state ? \"on\" : \"off\";\n\tset_config_option(\"citus.enable_local_reference_table_foreign_keys\", stateStr,\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n}\n\n\n/*\n * GetRelationDropFkeyCommands returns a list of DDL commands to drop foreign\n * keys where the relation with relationId is the referencing relation.\n */\nstatic List *\nGetRelationDropFkeyCommands(Oid relationId, int fKeyFlags)\n{\n\tList *dropFkeyCascadeCommandList = NIL;\n\n\tList *relationFKeyIdList = GetForeignKeyOids(relationId, fKeyFlags);\n\n\tOid foreignKeyId;\n\tforeach_declared_oid(foreignKeyId, relationFKeyIdList)\n\t{\n\t\tchar *dropFkeyCascadeCommand = GetDropFkeyCascadeCommand(foreignKeyId);\n\t\tdropFkeyCascadeCommandList = lappend(dropFkeyCascadeCommandList,\n\t\t\t\t\t\t\t\t\t\t\t dropFkeyCascadeCommand);\n\t}\n\n\treturn dropFkeyCascadeCommandList;\n}\n\n\n/*\n * GetDropFkeyCascadeCommand returns DDL command to drop foreign key with\n * foreignKeyId.\n */\nstatic char *\nGetDropFkeyCascadeCommand(Oid foreignKeyId)\n{\n\t/*\n\t * As we need to execute ALTER TABLE DROP CONSTRAINT command on\n\t * referencing relation, resolve it here.\n\t */\n\tHeapTuple heapTuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(foreignKeyId));\n\tForm_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);\n\tOid relationId = constraintForm->conrelid;\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\n\tReleaseSysCache(heapTuple);\n\n\tchar *constraintName = get_constraint_name(foreignKeyId);\n\tconst char *quotedConstraintName = quote_identifier(constraintName);\n\n\tStringInfo dropFkeyCascadeCommand = makeStringInfo();\n\tappendStringInfo(dropFkeyCascadeCommand, \"ALTER TABLE %s DROP CONSTRAINT %s CASCADE;\",\n\t\t\t\t\t qualifiedRelationName, quotedConstraintName);\n\n\treturn dropFkeyCascadeCommand->data;\n}\n\n\n/*\n * ExecuteCascadeOperationForRelationIdList executes citus table function\n * specified by CascadeOperationType argument for given relation id\n * list.\n */\nstatic void\nExecuteCascadeOperationForRelationIdList(List *relationIdList,\n\t\t\t\t\t\t\t\t\t\t CascadeOperationType\n\t\t\t\t\t\t\t\t\t\t cascadeOperationType)\n{\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\t/*\n\t\t * The reason behind skipping certain table types in below loop is\n\t\t * that we support some sort of foreign keys between postgres tables\n\t\t * and citus tables when enable_local_reference_table_foreign_keys is\n\t\t * false or when coordinator is not added to metadata.\n\t\t *\n\t\t * Also, as caller already passed the relations that we should operate\n\t\t * on, we don't cascade via foreign keys here.\n\t\t */\n\t\tbool cascadeViaForeignKeys = false;\n\t\tswitch (cascadeOperationType)\n\t\t{\n\t\t\tcase CASCADE_FKEY_UNDISTRIBUTE_TABLE:\n\t\t\t{\n\t\t\t\tif (IsCitusTable(relationId))\n\t\t\t\t{\n\t\t\t\t\tTableConversionParameters params = {\n\t\t\t\t\t\t.relationId = relationId,\n\t\t\t\t\t\t.cascadeViaForeignKeys = cascadeViaForeignKeys,\n\t\t\t\t\t\t.bypassTenantCheck = false\n\t\t\t\t\t};\n\t\t\t\t\tUndistributeTable(&params);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase CASCADE_USER_ADD_LOCAL_TABLE_TO_METADATA:\n\t\t\t{\n\t\t\t\tif (!IsCitusTable(relationId))\n\t\t\t\t{\n\t\t\t\t\tbool autoConverted = false;\n\t\t\t\t\tCreateCitusLocalTable(relationId, cascadeViaForeignKeys,\n\t\t\t\t\t\t\t\t\t\t  autoConverted);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase CASCADE_AUTO_ADD_LOCAL_TABLE_TO_METADATA:\n\t\t\t{\n\t\t\t\tif (!IsCitusTable(relationId))\n\t\t\t\t{\n\t\t\t\t\tbool autoConverted = true;\n\t\t\t\t\tCreateCitusLocalTable(relationId, cascadeViaForeignKeys,\n\t\t\t\t\t\t\t\t\t\t  autoConverted);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * This is not expected as other create table functions don't have\n\t\t\t\t * cascade option yet. To be on the safe side, error out here.\n\t\t\t\t */\n\t\t\t\tereport(ERROR, (errmsg(\"citus table function could not be found\")));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * ExecuteAndLogUtilityCommandListInTableTypeConversionViaSPI is a wrapper function\n * around ExecuteAndLogQueryViaSPI, that executes view creation commands\n * with the flag InTableTypeConversionFunctionCall set to true.\n */\nvoid\nExecuteAndLogUtilityCommandListInTableTypeConversionViaSPI(List *utilityCmdList)\n{\n\tbool oldValue = InTableTypeConversionFunctionCall;\n\tInTableTypeConversionFunctionCall = true;\n\n\tMemoryContext savedMemoryContext = CurrentMemoryContext;\n\tPG_TRY();\n\t{\n\t\tchar *utilityCommand = NULL;\n\t\tforeach_declared_ptr(utilityCommand, utilityCmdList)\n\t\t{\n\t\t\t/*\n\t\t\t * CREATE MATERIALIZED VIEW commands need to be parsed/transformed,\n\t\t\t * which SPI does for us.\n\t\t\t */\n\t\t\tExecuteAndLogQueryViaSPI(utilityCommand, SPI_OK_UTILITY, DEBUG1);\n\t\t}\n\t}\n\tPG_CATCH();\n\t{\n\t\tInTableTypeConversionFunctionCall = oldValue;\n\t\tMemoryContextSwitchTo(savedMemoryContext);\n\n\t\tErrorData *errorData = CopyErrorData();\n\t\tFlushErrorState();\n\n\t\tif (errorData->elevel != ERROR)\n\t\t{\n\t\t\tPG_RE_THROW();\n\t\t}\n\n\t\tThrowErrorData(errorData);\n\t}\n\tPG_END_TRY();\n\n\tInTableTypeConversionFunctionCall = oldValue;\n}\n\n\n/*\n * ExecuteAndLogUtilityCommandList takes a list of utility commands and calls\n * ExecuteAndLogUtilityCommand function for each of them.\n */\nvoid\nExecuteAndLogUtilityCommandList(List *utilityCmdList)\n{\n\tchar *utilityCommand = NULL;\n\tforeach_declared_ptr(utilityCommand, utilityCmdList)\n\t{\n\t\tExecuteAndLogUtilityCommand(utilityCommand);\n\t}\n}\n\n\n/*\n * ExecuteAndLogUtilityCommand takes a utility command and logs it in DEBUG4 log level.\n * Then, parses and executes it via CitusProcessUtility.\n */\nvoid\nExecuteAndLogUtilityCommand(const char *commandString)\n{\n\tereport(DEBUG4, (errmsg(\"executing \\\"%s\\\"\", commandString)));\n\n\tExecuteUtilityCommand(commandString);\n}\n\n\n/*\n * ExecuteForeignKeyCreateCommandList takes a list of foreign key creation ddl commands\n * and calls ExecuteAndLogForeignKeyCreateCommand function for each of them.\n */\nvoid\nExecuteForeignKeyCreateCommandList(List *ddlCommandList, bool skip_validation)\n{\n\tchar *ddlCommand = NULL;\n\tforeach_declared_ptr(ddlCommand, ddlCommandList)\n\t{\n\t\tExecuteForeignKeyCreateCommand(ddlCommand, skip_validation);\n\t}\n}\n\n\n/*\n * ExecuteForeignKeyCreateCommand takes a foreign key creation command\n * and logs it in DEBUG4 log level.\n *\n * Then, parses, sets skip_validation flag to considering the input and\n * executes the command via CitusProcessUtility.\n */\nstatic void\nExecuteForeignKeyCreateCommand(const char *commandString, bool skip_validation)\n{\n\tereport(DEBUG4, (errmsg(\"executing foreign key create command \\\"%s\\\"\",\n\t\t\t\t\t\t\tcommandString)));\n\n\tNode *parseTree = ParseTreeNode(commandString);\n\n\t/*\n\t * We might have thrown an error if IsA(parseTree, AlterTableStmt),\n\t * but that doesn't seem to provide any benefits, so assertion is\n\t * fine for this case.\n\t */\n\tAssert(IsA(parseTree, AlterTableStmt));\n\n\tif (skip_validation && IsA(parseTree, AlterTableStmt))\n\t{\n\t\tSkipForeignKeyValidationIfConstraintIsFkey((AlterTableStmt *) parseTree, true);\n\t\tereport(DEBUG4, (errmsg(\"skipping validation for foreign key create \"\n\t\t\t\t\t\t\t\t\"command \\\"%s\\\"\", commandString)));\n\t}\n\n\tProcessUtilityParseTree(parseTree, commandString, PROCESS_UTILITY_QUERY,\n\t\t\t\t\t\t\tNULL, None_Receiver, NULL);\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/citus_add_local_table_to_metadata.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_add_local_table_to_metadata.c\n *\n * This file contains functions to add local table to citus metadata.\n *\n * A local table added to metadata is composed of a shell relation to wrap the\n * the regular postgres relation as its coordinator local shard.\n *\n * As we want to hide \"citus local table\" concept from users, we renamed\n * udf and re-branded that concept as \"local tables added to metadata\".\n * Note that we might still call this local table concept as \"citus local\" in\n * many places of the code base.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_statistic_ext.h\"\n#include \"catalog/pg_trigger.h\"\n#include \"foreign/foreign.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/ruleutils.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/sequence.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/foreign_key_relationship.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_shard_visibility.h\"\n\n\n/*\n * Global variable for the GUC citus.use_citus_managed_tables.\n * This is used after every CREATE TABLE statement in utility_hook.c\n * If this variable is set to true, we add all created tables to metadata.\n */\nbool AddAllLocalTablesToMetadata = false;\n\nstatic void citus_add_local_table_to_metadata_internal(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   bool cascadeViaForeignKeys);\nstatic void ErrorIfAddingPartitionTableToMetadata(Oid relationId);\nstatic void ErrorIfUnsupportedCreateCitusLocalTable(Relation relation);\nstatic void ErrorIfUnsupportedCitusLocalTableKind(Oid relationId);\nstatic void EnsureIfPostgresFdwHasTableName(Oid relationId);\nstatic void ErrorIfOptionListHasNoTableName(List *optionList);\nstatic void NoticeIfAutoConvertingLocalTables(bool autoConverted, Oid relationId);\nstatic CascadeOperationType GetCascadeTypeForCitusLocalTables(bool autoConverted);\nstatic List * GetShellTableDDLEventsForCitusLocalTable(Oid relationId);\nstatic uint64 ConvertLocalTableToShard(Oid relationId);\nstatic void RenameRelationToShardRelation(Oid shellRelationId, uint64 shardId);\nstatic void RenameShardRelationConstraints(Oid shardRelationId, uint64 shardId);\nstatic List * GetConstraintNameList(Oid relationId);\nstatic char * GetRenameShardConstraintCommand(Oid relationId, char *constraintName,\n\t\t\t\t\t\t\t\t\t\t\t  uint64 shardId);\nstatic void RenameShardRelationIndexes(Oid shardRelationId, uint64 shardId);\nstatic void RenameShardRelationStatistics(Oid shardRelationId, uint64 shardId);\nstatic char * GetDropTriggerCommand(Oid relationId, char *triggerName);\nstatic char * GetRenameShardIndexCommand(Oid indexOid, uint64 shardId);\nstatic char * GetRenameShardStatsCommand(char *statSchema, char *statsName,\n\t\t\t\t\t\t\t\t\t\t char *statsNameWithShardId);\nstatic void RenameShardRelationNonTruncateTriggers(Oid shardRelationId, uint64 shardId);\nstatic char * GetRenameShardTriggerCommand(Oid shardRelationId, char *triggerName,\n\t\t\t\t\t\t\t\t\t\t   uint64 shardId);\nstatic void DropRelationTruncateTriggers(Oid relationId);\nstatic char * GetDropTriggerCommand(Oid relationId, char *triggerName);\nstatic void DropViewsOnTable(Oid relationId);\nstatic void DropIdentitiesOnTable(Oid relationId);\nstatic void DropTableFromPublications(Oid relationId);\nstatic List * GetRenameStatsCommandList(List *statsOidList, uint64 shardId);\nstatic List * ReversedOidList(List *oidList);\nstatic void AppendExplicitIndexIdsToList(Form_pg_index indexForm,\n\t\t\t\t\t\t\t\t\t\t List **explicitIndexIdList,\n\t\t\t\t\t\t\t\t\t\t int flags);\nstatic void DropNextValExprsAndMoveOwnedSeqOwnerships(Oid sourceRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  Oid targetRelationId);\nstatic void DropDefaultColumnDefinition(Oid relationId, char *columnName);\nstatic void TransferSequenceOwnership(Oid ownedSequenceId, Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t  char *columnName);\nstatic void InsertMetadataForCitusLocalTable(Oid citusLocalTableId, uint64 shardId,\n\t\t\t\t\t\t\t\t\t\t\t bool autoConverted);\nstatic void FinalizeCitusLocalTableCreation(Oid relationId);\n\n\nPG_FUNCTION_INFO_V1(citus_add_local_table_to_metadata);\nPG_FUNCTION_INFO_V1(create_citus_local_table);\nPG_FUNCTION_INFO_V1(remove_local_tables_from_metadata);\n\n\n/*\n * citus_add_local_table_to_metadata creates a citus local table from the table with\n * relationId by executing the internal method CreateCitusLocalTable.\n * (See CreateCitusLocalTable function's comment.)\n */\nDatum\ncitus_add_local_table_to_metadata(PG_FUNCTION_ARGS)\n{\n\tOid relationId = PG_GETARG_OID(0);\n\tbool cascadeViaForeignKeys = PG_GETARG_BOOL(1);\n\n\tcitus_add_local_table_to_metadata_internal(relationId, cascadeViaForeignKeys);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_add_local_table_to_metadata_internal is the internal method for\n * citus_add_local_table_to_metadata udf.\n */\nstatic void\ncitus_add_local_table_to_metadata_internal(Oid relationId, bool cascadeViaForeignKeys)\n{\n\tCheckCitusVersion(ERROR);\n\n\t/* enable citus_add_local_table_to_metadata on an empty node */\n\tInsertCoordinatorIfClusterEmpty();\n\n\tbool autoConverted = false;\n\tCreateCitusLocalTable(relationId, cascadeViaForeignKeys, autoConverted);\n}\n\n\n/*\n * create_citus_local_table is a wrapper function for old name of\n * of citus_add_local_table_to_metadata.\n *\n * The only reason for having this udf in citus binary is to make\n * multi_extension test happy as it uses this udf when testing the\n * downgrade scenario from 9.5 to 9.4.\n */\nDatum\ncreate_citus_local_table(PG_FUNCTION_ARGS)\n{\n\tereport(NOTICE, (errmsg(\"create_citus_local_table is deprecated in favour of \"\n\t\t\t\t\t\t\t\"citus_add_local_table_to_metadata\")));\n\n\tOid relationId = PG_GETARG_OID(0);\n\n\t/*\n\t * create_citus_local_table doesn't have cascadeViaForeignKeys option,\n\t * so we can't directly call citus_add_local_table_to_metadata udf itself\n\t * since create_citus_local_table doesn't specify cascadeViaForeignKeys.\n\t */\n\tbool cascadeViaForeignKeys = false;\n\tcitus_add_local_table_to_metadata_internal(relationId, cascadeViaForeignKeys);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * remove_local_tables_from_metadata undistributes citus local\n * tables that are not chained with any reference tables via foreign keys.\n */\nDatum\nremove_local_tables_from_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tUndistributeDisconnectedCitusLocalTables();\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * CreateCitusLocalTable is the internal method that creates a citus table\n * from the table with relationId. The created table would have the following\n * properties:\n *  - it will have only one shard,\n *  - its distribution method will be DISTRIBUTE_BY_NONE,\n *  - its replication model will be REPLICATION_MODEL_STREAMING,\n *  - its replication factor will be set to 1.\n * Similar to reference tables, it has only 1 placement. In addition to that, that\n * single placement is only allowed to be on the coordinator.\n */\nvoid\nCreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys, bool autoConverted)\n{\n\t/*\n\t * These checks should be done before acquiring any locks on relation.\n\t * This is because we don't allow creating citus local tables in worker\n\t * nodes and we don't want to acquire any locks on a table if we are not\n\t * the owner of it.\n\t */\n\tEnsureCoordinator();\n\tEnsureTableOwner(relationId);\n\n\t/* enable citus_add_local_table_to_metadata on an empty node */\n\tInsertCoordinatorIfClusterEmpty();\n\n\t/*\n\t * Creating Citus local tables relies on functions that accesses\n\t * shards locally (e.g., ExecuteAndLogUtilityCommand()). As long as\n\t * we don't teach those functions to access shards remotely, we\n\t * cannot relax this check.\n\t */\n\tSetLocalExecutionStatus(LOCAL_EXECUTION_REQUIRED);\n\n\tif (!autoConverted && IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\t/*\n\t\t * We allow users to mark local tables already added to metadata\n\t\t * as \"autoConverted = false\".\n\t\t * If the user called citus_add_local_table_to_metadata for a table that is\n\t\t * already added to metadata, we should mark this one and connected relations\n\t\t * as auto-converted = false.\n\t\t */\n\t\tUpdateAutoConvertedForConnectedRelations(list_make1_oid(relationId),\n\t\t\t\t\t\t\t\t\t\t\t\t autoConverted);\n\n\t\treturn;\n\t}\n\n\t/*\n\t * Lock target relation with an AccessExclusiveLock as we don't want\n\t * multiple backends manipulating this relation. We could actually simply\n\t * lock the relation without opening it. However, we also want to check\n\t * if the relation does not exist or dropped by another backend. Also,\n\t * we open the relation with try_relation_open instead of relation_open\n\t * to give a nice error in case the table is dropped by another backend.\n\t */\n\tLOCKMODE lockMode = AccessExclusiveLock;\n\tRelation relation = try_relation_open(relationId, lockMode);\n\n\tErrorIfUnsupportedCreateCitusLocalTable(relation);\n\n\tErrorIfAddingPartitionTableToMetadata(relationId);\n\n\t/*\n\t * We immediately close relation with NoLock right after opening it. This is\n\t * because, in this function, we may execute ALTER TABLE commands modifying\n\t * relation's column definitions and postgres does not allow us to do so when\n\t * the table is still open. (See the postgres function CheckTableNotInUse for\n\t * more information)\n\t */\n\trelation_close(relation, NoLock);\n\n\tNoticeIfAutoConvertingLocalTables(autoConverted, relationId);\n\n\tif (TableHasExternalForeignKeys(relationId))\n\t{\n\t\tif (!cascadeViaForeignKeys)\n\t\t{\n\t\t\t/*\n\t\t\t * We do not allow creating citus local table if the table is involved in a\n\t\t\t * foreign key relationship with \"any other table\", unless the option\n\t\t\t * cascadeViaForeignKeys is given true.\n\t\t\t * Note that we allow self references.\n\t\t\t */\n\t\t\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"relation %s is involved in a foreign key \"\n\t\t\t\t\t\t\t\t   \"relationship with another table\",\n\t\t\t\t\t\t\t\t   qualifiedRelationName),\n\t\t\t\t\t\t\terrhint(\"Use cascade_via_foreign_keys option to add \"\n\t\t\t\t\t\t\t\t\t\"all the relations involved in a foreign key \"\n\t\t\t\t\t\t\t\t\t\"relationship with %s to citus metadata by \"\n\t\t\t\t\t\t\t\t\t\"executing SELECT citus_add_local_table_to_metadata($$%s$$, \"\n\t\t\t\t\t\t\t\t\t\"cascade_via_foreign_keys=>true)\",\n\t\t\t\t\t\t\t\t\tqualifiedRelationName, qualifiedRelationName)));\n\t\t}\n\n\t\tCascadeOperationType cascadeType =\n\t\t\tGetCascadeTypeForCitusLocalTables(autoConverted);\n\n\t\t/*\n\t\t * By acquiring AccessExclusiveLock, make sure that no modifications happen\n\t\t * on the relations.\n\t\t */\n\t\tCascadeOperationForFkeyConnectedRelations(relationId, lockMode, cascadeType);\n\n\t\t/*\n\t\t * We converted every foreign key connected table in our subgraph\n\t\t * including itself to a citus local table, so return here.\n\t\t */\n\t\treturn;\n\t}\n\n\tif (PartitionedTable(relationId))\n\t{\n\t\tList *relationList = PartitionList(relationId);\n\t\tif (list_length(relationList) > 0)\n\t\t{\n\t\t\trelationList = lappend_oid(relationList, relationId);\n\n\t\t\tCascadeOperationType cascadeType =\n\t\t\t\tGetCascadeTypeForCitusLocalTables(autoConverted);\n\n\t\t\tCascadeOperationForRelationIdList(relationList, AccessExclusiveLock,\n\t\t\t\t\t\t\t\t\t\t\t  cascadeType);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tObjectAddress *tableAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*tableAddress, RelationRelationId, relationId);\n\n\t/*\n\t * Ensure that the sequences used in column defaults of the table\n\t * have proper types\n\t */\n\tEnsureRelationHasCompatibleSequenceTypes(relationId);\n\n\t/*\n\t * Ensure dependencies exist as we will create shell table on the other nodes\n\t * in the MX case.\n\t */\n\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(tableAddress));\n\n\t/*\n\t * Make sure that existing reference tables have been replicated to all\n\t * the nodes such that we can create foreign keys and joins work\n\t * immediately after creation.\n\t */\n\tEnsureReferenceTablesExistOnAllNodes();\n\n\tList *shellTableDDLEvents = GetShellTableDDLEventsForCitusLocalTable(relationId);\n\tList *tableViewCreationCommands = GetViewCreationCommandsOfTable(relationId);\n\n\tbool isAdd = true;\n\tList *alterPublicationCommands =\n\t\tGetAlterPublicationDDLCommandsForTable(relationId, isAdd);\n\n\tchar *relationName = get_rel_name(relationId);\n\tOid relationSchemaId = get_rel_namespace(relationId);\n\n\t/*\n\t * Drop identities before local shard conversion since the shell table owns\n\t * identities\n\t */\n\tDropIdentitiesOnTable(relationId);\n\n\t/*\n\t * We do not want the shard to be in the publication (subscribers are\n\t * unlikely to recognize it).\n\t */\n\tDropTableFromPublications(relationId);\n\n\t/* below we convert relation with relationId to the shard relation */\n\tuint64 shardId = ConvertLocalTableToShard(relationId);\n\n\t/*\n\t * As we retrieved the DDL commands necessary to create the shell table\n\t * from scratch, below we simply recreate the shell table executing them\n\t * via process utility.\n\t */\n\tExecuteAndLogUtilityCommandList(shellTableDDLEvents);\n\n\t/*\n\t * Execute the view creation commands with the shell table.\n\t * Views will be distributed via FinalizeCitusLocalTableCreation below.\n\t */\n\tExecuteAndLogUtilityCommandListInTableTypeConversionViaSPI(tableViewCreationCommands);\n\n\t/*\n\t * Execute the publication creation commands with the shell table.\n\t */\n\tExecuteAndLogUtilityCommandListInTableTypeConversionViaSPI(alterPublicationCommands);\n\n\t/*\n\t * Set shellRelationId as the relation with relationId now points\n\t * to the shard relation.\n\t */\n\tOid shardRelationId = relationId;\n\tOid shellRelationId = get_relname_relid(relationName, relationSchemaId);\n\n\t/* assert that we created the shell table properly in the same schema */\n\tAssert(OidIsValid(shellRelationId));\n\n\t/*\n\t * Move sequence ownerships from shard table to shell table and also drop\n\t * DEFAULT expressions based on sequences from shard relation as we should\n\t * evaluate such columns in shell table when needed.\n\t */\n\tDropNextValExprsAndMoveOwnedSeqOwnerships(shardRelationId,\n\t\t\t\t\t\t\t\t\t\t\t  shellRelationId);\n\n\tInsertMetadataForCitusLocalTable(shellRelationId, shardId, autoConverted);\n\n\tFinalizeCitusLocalTableCreation(shellRelationId);\n}\n\n\n/*\n * CreateCitusLocalTablePartitionOf generates and executes the necessary commands\n * to create a table as partition of a partitioned Citus Local Table.\n * The conversion is done by CreateCitusLocalTable.\n */\nvoid\nCreateCitusLocalTablePartitionOf(CreateStmt *createStatement, Oid relationId,\n\t\t\t\t\t\t\t\t Oid parentRelationId)\n{\n\tif (createStatement->partspec)\n\t{\n\t\t/*\n\t\t * Since partspec represents \"PARTITION BY\" clause, being different than\n\t\t * NULL means that given CreateStmt attempts to create a parent table\n\t\t * at the same time. That means multi-level partitioning within this\n\t\t * function's context. We don't support this currently.\n\t\t */\n\t\tchar *parentRelationName = get_rel_name(parentRelationId);\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"distributing multi-level partitioned tables \"\n\t\t\t\t\t\t\t   \"is not supported\"),\n\t\t\t\t\t\terrdetail(\"Relation \\\"%s\\\" is partitioned table itself \"\n\t\t\t\t\t\t\t\t  \"and it is also partition of relation \\\"%s\\\".\",\n\t\t\t\t\t\t\t\t  relationName, parentRelationName)));\n\t}\n\n\t/*\n\t * Since the shell table for the partition is not created yet on MX workers,\n\t * we should disable DDL propagation before the DETACH command, to avoid\n\t * getting an error on the worker.\n\t */\n\tList *detachCommands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t\t  GenerateDetachPartitionCommand(relationId),\n\t\t\t\t\t\t\t\t\t  ENABLE_DDL_PROPAGATION);\n\tchar *attachCommand = GenerateAlterTableAttachPartitionCommand(relationId);\n\tExecuteAndLogUtilityCommandList(detachCommands);\n\tint fKeyFlags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES;\n\n\t/*\n\t * When cascadeViaForeignKeys is false, CreateCitusLocalTable doesn't expect\n\t * any foreign keys on given relation. Note that we don't want to pass\n\t * cascadeViaForeignKeys to be true here since we don't already allow non-inherited\n\t * foreign keys on child relations, and for the inherited ones, we should have already\n\t * cascaded to the other relations when creating a citus local table from parent.\n\t *\n\t * For this reason, we drop inherited foreign keys here, they'll anyway get created\n\t * again with the attach command\n\t */\n\tDropRelationForeignKeys(relationId, fKeyFlags);\n\n\t/* get the autoconverted field from the parent */\n\tCitusTableCacheEntry *entry = GetCitusTableCacheEntry(parentRelationId);\n\n\tbool cascade = false;\n\tbool autoConverted = entry->autoConverted;\n\tCreateCitusLocalTable(relationId, cascade, autoConverted);\n\tExecuteAndLogUtilityCommand(attachCommand);\n}\n\n\n/*\n * ErrorIfAddingPartitionTableToMetadata errors out if we try to create the\n * citus local table from a partition table.\n */\nstatic void\nErrorIfAddingPartitionTableToMetadata(Oid relationId)\n{\n\tif (PartitionTable(relationId))\n\t{\n\t\t/*\n\t\t * We do not allow converting only partitions into Citus Local Tables.\n\t\t * Users should call the UDF citus_add_local_table_to_metadata with the\n\t\t * parent table; then the whole partitioned table will be converted.\n\t\t */\n\t\tchar *relationName = get_rel_name(relationId);\n\t\tOid parentRelationId = PartitionParentOid(relationId);\n\t\tchar *parentRelationName = get_rel_name(parentRelationId);\n\t\tereport(ERROR, (errmsg(\"cannot add local table %s to metadata since \"\n\t\t\t\t\t\t\t   \"it is a partition of %s. Instead, add the parent \"\n\t\t\t\t\t\t\t   \"table %s to metadata.\",\n\t\t\t\t\t\t\t   relationName, parentRelationName,\n\t\t\t\t\t\t\t   parentRelationName)));\n\t}\n}\n\n\n/*\n * ErrorIfUnsupportedCreateCitusLocalTable errors out if we cannot create the\n * citus local table from the relation.\n */\nstatic void\nErrorIfUnsupportedCreateCitusLocalTable(Relation relation)\n{\n\tif (!RelationIsValid(relation))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot add local table to metadata, relation does \"\n\t\t\t\t\t\t\t   \"not exist\")));\n\t}\n\n\tErrorIfTableIsACatalogTable(relation);\n\n\tOid relationId = relation->rd_id;\n\n\tErrorIfCoordinatorNotAddedAsWorkerNode();\n\tErrorIfUnsupportedCitusLocalTableKind(relationId);\n\tEnsureTableNotDistributed(relationId);\n\tErrorIfRelationHasUnsupportedTrigger(relationId);\n\n\t/*\n\t * Error out with a hint if the foreign table is using postgres_fdw and\n\t * the option table_name is not provided.\n\t * Citus relays all the Citus local foreign table logic to the placement of the\n\t * Citus local table. If table_name is NOT provided, Citus would try to talk to\n\t * the foreign postgres table over the shard's table name, which would not exist\n\t * on the remote server.\n\t */\n\tEnsureIfPostgresFdwHasTableName(relationId);\n\n\t/*\n\t * When creating other citus table types, we don't need to check that case as\n\t * EnsureTableNotDistributed already errors out if the given relation implies\n\t * a citus table. However, as we don't mark the relation as citus table, i.e we\n\t * do not use the relation with relationId as the shell relation, parallel\n\t * citus_add_local_table_to_metadata executions would not error out for that relation.\n\t * Hence we need to error out for shard relations too.\n\t */\n\tErrorIfRelationIsAKnownShard(relationId);\n\n\t/* we do not support policies in citus community */\n\tErrorIfUnsupportedPolicy(relation);\n}\n\n\n/*\n * ServerUsesPostgresFdw gets a foreign server Oid and returns true if the FDW that\n * the server depends on is postgres_fdw. Returns false otherwise.\n */\nbool\nServerUsesPostgresFdw(Oid serverId)\n{\n\tForeignServer *server = GetForeignServer(serverId);\n\tForeignDataWrapper *fdw = GetForeignDataWrapper(server->fdwid);\n\n\tif (strcmp(fdw->fdwname, \"postgres_fdw\") == 0)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * EnsureIfPostgresFdwHasTableName errors out with a hint if the foreign table is using postgres_fdw and\n * the option table_name is not provided.\n */\nstatic void\nEnsureIfPostgresFdwHasTableName(Oid relationId)\n{\n\tchar relationKind = get_rel_relkind(relationId);\n\tif (relationKind == RELKIND_FOREIGN_TABLE)\n\t{\n\t\tForeignTable *foreignTable = GetForeignTable(relationId);\n\t\tif (ServerUsesPostgresFdw(foreignTable->serverid))\n\t\t{\n\t\t\tErrorIfOptionListHasNoTableName(foreignTable->options);\n\t\t}\n\t}\n}\n\n\n/*\n * ErrorIfOptionListHasNoTableName gets an option list (DefElem) and errors out\n * if the list does not contain a table_name element.\n */\nstatic void\nErrorIfOptionListHasNoTableName(List *optionList)\n{\n\tchar *table_nameString = \"table_name\";\n\tDefElem *option = NULL;\n\tforeach_declared_ptr(option, optionList)\n\t{\n\t\tchar *optionName = option->defname;\n\t\tif (strcmp(optionName, table_nameString) == 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t}\n\n\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\"table_name option must be provided when using postgres_fdw with Citus\"),\n\t\t\t\t\terrhint(\"Provide the option \\\"table_name\\\" with value target table's\"\n\t\t\t\t\t\t\t\" name\")));\n}\n\n\n/*\n * ForeignTableDropsTableNameOption returns true if given option list contains\n * (DROP table_name).\n */\nbool\nForeignTableDropsTableNameOption(List *optionList)\n{\n\tchar *table_nameString = \"table_name\";\n\tDefElem *option = NULL;\n\tforeach_declared_ptr(option, optionList)\n\t{\n\t\tchar *optionName = option->defname;\n\t\tDefElemAction optionAction = option->defaction;\n\t\tif (strcmp(optionName, table_nameString) == 0 &&\n\t\t\toptionAction == DEFELEM_DROP)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ErrorIfUnsupportedCitusLocalTableKind errors out if the relation kind of\n * relation with relationId is not supported for citus local table creation.\n */\nstatic void\nErrorIfUnsupportedCitusLocalTableKind(Oid relationId)\n{\n\tconst char *relationName = get_rel_name(relationId);\n\n\tif (IsChildTable(relationId) || IsParentTable(relationId))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot add local table \\\"%s\\\" to metadata, local tables \"\n\t\t\t\t\t\t\t   \"added to metadata cannot be involved in inheritance \"\n\t\t\t\t\t\t\t   \"relationships\", relationName)));\n\t}\n\n\tchar relationKind = get_rel_relkind(relationId);\n\tif (!(relationKind == RELKIND_RELATION || relationKind == RELKIND_FOREIGN_TABLE ||\n\t\t  relationKind == RELKIND_PARTITIONED_TABLE))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot add local table \\\"%s\\\" to metadata, only regular \"\n\t\t\t\t\t\t\t   \"tables, partitioned tables and foreign tables\"\n\t\t\t\t\t\t\t   \" can be added to citus metadata \",\n\t\t\t\t\t\t\t   relationName)));\n\t}\n\n\tif (get_rel_persistence(relationId) == RELPERSISTENCE_TEMP)\n\t{\n\t\t/*\n\t\t * Currently, we use citus local tables only to support foreign keys\n\t\t * between local tables and reference tables. Citus already doesn't\n\t\t * support creating reference tables from temp tables.\n\t\t * So now we are creating a citus local table from a temp table that\n\t\t * has a foreign key from/to a reference table with persistent storage.\n\t\t * In that case, we want to give the same error as postgres would do.\n\t\t */\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"constraints on temporary tables may reference only \"\n\t\t\t\t\t\t\t   \"temporary tables\")));\n\t}\n}\n\n\n/*\n * NoticeIfAutoConvertingLocalTables logs a NOTICE message to inform the user that we are\n * automatically adding local tables to metadata. The user should know that this table\n * will be undistributed automatically, if it gets disconnected from reference table(s).\n */\nstatic void\nNoticeIfAutoConvertingLocalTables(bool autoConverted, Oid relationId)\n{\n\tif (autoConverted && ShouldEnableLocalReferenceForeignKeys())\n\t{\n\t\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\n\t\t/*\n\t\t * When foreign keys between reference tables and postgres tables are\n\t\t * enabled, we automatically undistribute citus local tables that are\n\t\t * not chained with any reference tables back to postgres tables.\n\t\t * So give a warning to user for that.\n\t\t */\n\t\tereport(NOTICE, (errmsg(\"local tables that are added to metadata automatically \"\n\t\t\t\t\t\t\t\t\"by citus, but not chained with reference tables via \"\n\t\t\t\t\t\t\t\t\"foreign keys might be automatically converted back to \"\n\t\t\t\t\t\t\t\t\"postgres tables\"),\n\t\t\t\t\t\t errhint(\"Executing citus_add_local_table_to_metadata($$%s$$) \"\n\t\t\t\t\t\t\t\t \"prevents this for the given relation, and all of the \"\n\t\t\t\t\t\t\t\t \"connected relations\", qualifiedRelationName)));\n\t}\n}\n\n\n/*\n * GetCascadeTypeForCitusLocalTables returns CASCADE_AUTO_ADD_LOCAL_TABLE_TO_METADATA\n * if autoConverted is true. Returns CASCADE_USER_ADD_LOCAL_TABLE_TO_METADATA otherwise.\n */\nstatic CascadeOperationType\nGetCascadeTypeForCitusLocalTables(bool autoConverted)\n{\n\tif (autoConverted)\n\t{\n\t\treturn CASCADE_AUTO_ADD_LOCAL_TABLE_TO_METADATA;\n\t}\n\n\treturn CASCADE_USER_ADD_LOCAL_TABLE_TO_METADATA;\n}\n\n\n/*\n * UpdateAutoConvertedForConnectedRelations updates the autoConverted field on\n * pg_dist_partition for the foreign key connected relations of the given relations.\n * Sets it to given autoConverted value for all of the connected relations.\n * We don't need to update partition relations separately, since the foreign key\n * graph already includes them, as they have the same (inherited) fkeys as their parents.\n */\nvoid\nUpdateAutoConvertedForConnectedRelations(List *relationIds, bool autoConverted)\n{\n\tInvalidateForeignKeyGraph();\n\n\tList *relationIdList = NIL;\n\tOid relid = InvalidOid;\n\tforeach_declared_oid(relid, relationIds)\n\t{\n\t\tList *connectedRelations = GetForeignKeyConnectedRelationIdList(relid);\n\t\trelationIdList = list_concat_unique_oid(relationIdList, connectedRelations);\n\t}\n\n\trelationIdList = SortList(relationIdList, CompareOids);\n\n\tforeach_declared_oid(relid, relationIdList)\n\t{\n\t\tUpdatePgDistPartitionAutoConverted(relid, autoConverted);\n\t}\n}\n\n\n/*\n * GetShellTableDDLEventsForCitusLocalTable returns a list of DDL commands\n * to create the shell table from scratch.\n */\nstatic List *\nGetShellTableDDLEventsForCitusLocalTable(Oid relationId)\n{\n\t/*\n\t * As we don't allow foreign keys with other tables initially, below we\n\t * only pick self-referencing foreign keys.\n\t */\n\tList *foreignConstraintCommands =\n\t\tGetReferencingForeignConstaintCommands(relationId);\n\n\t/*\n\t * Include DEFAULT clauses for columns getting their default values from\n\t * a sequence.\n\t */\n\tIncludeSequenceDefaults includeSequenceDefaults = NEXTVAL_SEQUENCE_DEFAULTS;\n\tIncludeIdentities includeIdentityDefaults = INCLUDE_IDENTITY;\n\n\tbool creatingShellTableOnRemoteNode = false;\n\tList *tableDDLCommands = GetFullTableCreationCommands(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  includeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  includeIdentityDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  creatingShellTableOnRemoteNode);\n\n\tList *shellTableDDLEvents = NIL;\n\tTableDDLCommand *tableDDLCommand = NULL;\n\tforeach_declared_ptr(tableDDLCommand, tableDDLCommands)\n\t{\n\t\tAssert(CitusIsA(tableDDLCommand, TableDDLCommand));\n\t\tshellTableDDLEvents = lappend(shellTableDDLEvents,\n\t\t\t\t\t\t\t\t\t  GetTableDDLCommand(tableDDLCommand));\n\t}\n\tshellTableDDLEvents = list_concat(shellTableDDLEvents, foreignConstraintCommands);\n\n\treturn shellTableDDLEvents;\n}\n\n\n/*\n * ConvertLocalTableToShard first acquires a shardId and then converts the\n * given relation with relationId to the shard relation with shardId. That\n * means, this function suffixes shardId to:\n *  - relation name,\n *  - all the objects \"defined on\" the relation.\n * After converting the given relation, returns the acquired shardId.\n */\nstatic uint64\nConvertLocalTableToShard(Oid relationId)\n{\n\tuint64 shardId = GetNextShardId();\n\n\tRenameRelationToShardRelation(relationId, shardId);\n\tRenameShardRelationConstraints(relationId, shardId);\n\tRenameShardRelationIndexes(relationId, shardId);\n\tRenameShardRelationStatistics(relationId, shardId);\n\n\t/*\n\t * We do not create truncate triggers on shard relation. This is\n\t * because truncate triggers are fired by utility hook and we would\n\t * need to disable them to prevent executing them twice if we don't\n\t * drop the trigger on shard relation.\n\t */\n\tDropRelationTruncateTriggers(relationId);\n\n\t/* drop views that depend on the shard table */\n\tDropViewsOnTable(relationId);\n\n\t/*\n\t * We create INSERT|DELETE|UPDATE triggers on shard relation too.\n\t * This is because citus prevents postgres executor to fire those\n\t * triggers. So, here we suffix such triggers on shard relation\n\t * with shardId.\n\t */\n\tRenameShardRelationNonTruncateTriggers(relationId, shardId);\n\n\treturn shardId;\n}\n\n\n/*\n * RenameRelationToShardRelation appends given shardId to the end of the name\n * of relation with shellRelationId.\n */\nstatic void\nRenameRelationToShardRelation(Oid shellRelationId, uint64 shardId)\n{\n\tchar *qualifiedShellRelationName = generate_qualified_relation_name(shellRelationId);\n\n\tchar *shellRelationName = get_rel_name(shellRelationId);\n\tchar *shardRelationName = pstrdup(shellRelationName);\n\tAppendShardIdToName(&shardRelationName, shardId);\n\tconst char *quotedShardRelationName = quote_identifier(shardRelationName);\n\n\tStringInfo renameCommand = makeStringInfo();\n\tappendStringInfo(renameCommand, \"ALTER TABLE %s RENAME TO %s;\",\n\t\t\t\t\t qualifiedShellRelationName, quotedShardRelationName);\n\n\tExecuteAndLogUtilityCommand(renameCommand->data);\n}\n\n\n/*\n * RenameShardRelationConstraints appends given shardId to the end of the name\n * of constraints \"defined on\" the relation with shardRelationId. This function\n * utilizes GetConstraintNameList to pick the constraints to be renamed,\n * see more details in function's comment.\n */\nstatic void\nRenameShardRelationConstraints(Oid shardRelationId, uint64 shardId)\n{\n\tList *constraintNameList = GetConstraintNameList(shardRelationId);\n\n\tchar *constraintName = NULL;\n\tforeach_declared_ptr(constraintName, constraintNameList)\n\t{\n\t\tconst char *commandString =\n\t\t\tGetRenameShardConstraintCommand(shardRelationId, constraintName, shardId);\n\t\tExecuteAndLogUtilityCommand(commandString);\n\t}\n}\n\n\n/*\n * GetConstraintNameList returns a list of constraint names \"defined on\" the\n * relation with relationId. Those constraints can be:\n *  - \"check\" constraints or,\n *  - \"primary key\" constraints or,\n *  - \"unique\" constraints or,\n *  - \"trigger\" constraints or,\n *  - \"exclusion\" constraints or,\n *  - \"foreign key\" constraints in which the relation is the \"referencing\"\n *     relation (including the self-referencing foreign keys).\n */\nstatic List *\nGetConstraintNameList(Oid relationId)\n{\n\tList *constraintNameList = NIL;\n\n\tint scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\n\tRelation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));\n\n\tbool useIndex = true;\n\tSysScanDesc scanDescriptor = systable_beginscan(pgConstraint,\n\t\t\t\t\t\t\t\t\t\t\t\t\tConstraintRelidTypidNameIndexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tuseIndex, NULL, scanKeyCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\tscanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tForm_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);\n\n\t\tchar *constraintName = NameStr(constraintForm->conname);\n\t\tconstraintNameList = lappend(constraintNameList, pstrdup(constraintName));\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgConstraint, NoLock);\n\n\treturn constraintNameList;\n}\n\n\n/*\n * GetRenameShardConstraintCommand returns DDL command to append given\n * shardId to the constrant with constraintName on the relation with\n * relationId.\n */\nstatic char *\nGetRenameShardConstraintCommand(Oid relationId, char *constraintName, uint64 shardId)\n{\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\n\tchar *shardConstraintName = pstrdup(constraintName);\n\tAppendShardIdToName(&shardConstraintName, shardId);\n\tconst char *quotedShardConstraintName = quote_identifier(shardConstraintName);\n\n\tconst char *quotedConstraintName = quote_identifier(constraintName);\n\n\tStringInfo renameCommand = makeStringInfo();\n\tappendStringInfo(renameCommand, \"ALTER TABLE %s RENAME CONSTRAINT %s TO %s;\",\n\t\t\t\t\t qualifiedRelationName, quotedConstraintName,\n\t\t\t\t\t quotedShardConstraintName);\n\n\treturn renameCommand->data;\n}\n\n\n/*\n * RenameShardRelationIndexes appends given shardId to the end of the names\n * of shard relation indexes except the ones that are already renamed via\n * RenameShardRelationConstraints. This function utilizes\n * GetExplicitIndexOidList to pick the indexes to be renamed, see more\n * details in function's comment.\n */\nstatic void\nRenameShardRelationIndexes(Oid shardRelationId, uint64 shardId)\n{\n\tList *indexOidList = GetExplicitIndexOidList(shardRelationId);\n\n\tOid indexOid = InvalidOid;\n\tforeach_declared_oid(indexOid, indexOidList)\n\t{\n\t\tconst char *commandString = GetRenameShardIndexCommand(indexOid, shardId);\n\t\tExecuteAndLogUtilityCommand(commandString);\n\t}\n}\n\n\n/*\n * GetRenameShardIndexCommand returns DDL command to append given shardId to\n * the index with indexName.\n */\nstatic char *\nGetRenameShardIndexCommand(Oid indexOid, uint64 shardId)\n{\n\tchar *indexName = get_rel_name(indexOid);\n\tchar *shardIndexName = pstrdup(indexName);\n\tAppendShardIdToName(&shardIndexName, shardId);\n\tconst char *quotedShardIndexName = quote_identifier(shardIndexName);\n\n\tconst char *quotedIndexName = generate_qualified_relation_name(indexOid);\n\n\tStringInfo renameCommand = makeStringInfo();\n\tappendStringInfo(renameCommand, \"ALTER INDEX %s RENAME TO %s;\",\n\t\t\t\t\t quotedIndexName, quotedShardIndexName);\n\n\treturn renameCommand->data;\n}\n\n\n/*\n * RenameShardRelationStatistics appends given shardId to the end of the names\n * of shard relation statistics. This function utilizes GetExplicitStatsNameList\n * to pick the statistics to be renamed, see more details in function's comment.\n */\nstatic void\nRenameShardRelationStatistics(Oid shardRelationId, uint64 shardId)\n{\n\tRelation shardRelation = RelationIdGetRelation(shardRelationId);\n\tif (!RelationIsValid(shardRelation))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not open relation with OID %u\", shardRelationId)));\n\t}\n\n\tList *statsOidList = RelationGetStatExtList(shardRelation);\n\tRelationClose(shardRelation);\n\n\tList *statsCommandList = GetRenameStatsCommandList(statsOidList, shardId);\n\n\tchar *command = NULL;\n\tforeach_declared_ptr(command, statsCommandList)\n\t{\n\t\tExecuteAndLogUtilityCommand(command);\n\t}\n}\n\n\n/*\n * GetRenameShardStatsCommand returns DDL command to append given shardId to\n * the statistics with statName.\n */\nstatic char *\nGetRenameShardStatsCommand(char *statSchema, char *statsName, char *statsNameWithShardId)\n{\n\tconst char *quotedStatsNameWithShardId = quote_identifier(statsNameWithShardId);\n\tchar *qualifiedStatsName = quote_qualified_identifier(statSchema, statsName);\n\n\tStringInfo renameCommand = makeStringInfo();\n\tappendStringInfo(renameCommand, \"ALTER STATISTICS %s RENAME TO %s;\",\n\t\t\t\t\t qualifiedStatsName, quotedStatsNameWithShardId);\n\n\treturn renameCommand->data;\n}\n\n\n/*\n * RenameShardRelationNonTruncateTriggers appends given shardId to the end of\n * the names of shard relation INSERT/DELETE/UPDATE triggers that are explicitly\n * created.\n */\nstatic void\nRenameShardRelationNonTruncateTriggers(Oid shardRelationId, uint64 shardId)\n{\n\tList *triggerIdList = GetExplicitTriggerIdList(shardRelationId);\n\n\tOid triggerId = InvalidOid;\n\tforeach_declared_oid(triggerId, triggerIdList)\n\t{\n\t\tbool missingOk = false;\n\t\tHeapTuple triggerTuple = GetTriggerTupleById(triggerId, missingOk);\n\t\tForm_pg_trigger triggerForm = (Form_pg_trigger) GETSTRUCT(triggerTuple);\n\n\t\tif (!TRIGGER_FOR_TRUNCATE(triggerForm->tgtype))\n\t\t{\n\t\t\tchar *triggerName = NameStr(triggerForm->tgname);\n\t\t\tchar *commandString =\n\t\t\t\tGetRenameShardTriggerCommand(shardRelationId, triggerName, shardId);\n\t\t\tExecuteAndLogUtilityCommand(commandString);\n\t\t}\n\n\t\theap_freetuple(triggerTuple);\n\t}\n}\n\n\n/*\n * GetRenameShardTriggerCommand returns DDL command to append given shardId to\n * the trigger with triggerName.\n */\nstatic char *\nGetRenameShardTriggerCommand(Oid shardRelationId, char *triggerName, uint64 shardId)\n{\n\tchar *qualifiedShardRelationName = generate_qualified_relation_name(shardRelationId);\n\n\tchar *shardTriggerName = pstrdup(triggerName);\n\tAppendShardIdToName(&shardTriggerName, shardId);\n\tconst char *quotedShardTriggerName = quote_identifier(shardTriggerName);\n\n\tconst char *quotedTriggerName = quote_identifier(triggerName);\n\n\tStringInfo renameCommand = makeStringInfo();\n\tappendStringInfo(renameCommand, \"ALTER TRIGGER %s ON %s RENAME TO %s;\",\n\t\t\t\t\t quotedTriggerName, qualifiedShardRelationName,\n\t\t\t\t\t quotedShardTriggerName);\n\n\treturn renameCommand->data;\n}\n\n\n/*\n * DropRelationTruncateTriggers drops TRUNCATE triggers that are explicitly\n * created on relation with relationId.\n */\nstatic void\nDropRelationTruncateTriggers(Oid relationId)\n{\n\tList *triggerIdList = GetExplicitTriggerIdList(relationId);\n\n\tOid triggerId = InvalidOid;\n\tforeach_declared_oid(triggerId, triggerIdList)\n\t{\n\t\tbool missingOk = false;\n\t\tHeapTuple triggerTuple = GetTriggerTupleById(triggerId, missingOk);\n\t\tForm_pg_trigger triggerForm = (Form_pg_trigger) GETSTRUCT(triggerTuple);\n\n\t\tif (TRIGGER_FOR_TRUNCATE(triggerForm->tgtype))\n\t\t{\n\t\t\tchar *triggerName = NameStr(triggerForm->tgname);\n\t\t\tchar *commandString = GetDropTriggerCommand(relationId, triggerName);\n\t\t\tExecuteAndLogUtilityCommand(commandString);\n\t\t}\n\n\t\theap_freetuple(triggerTuple);\n\t}\n}\n\n\n/*\n * GetDropTriggerCommand returns DDL command to drop the trigger with triggerName\n * on relationId.\n */\nstatic char *\nGetDropTriggerCommand(Oid relationId, char *triggerName)\n{\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\tconst char *quotedTriggerName = quote_identifier(triggerName);\n\n\t/*\n\t * In postgres, the only possible object type that may depend on a trigger\n\t * is the \"constraint\" object implied by the trigger itself if it is a\n\t * constraint trigger, and it would be an internal dependency so it could\n\t * be dropped without using CASCADE. Other than this, it is also possible\n\t * to define dependencies on trigger via recordDependencyOn api by other\n\t * extensions. We don't handle those kind of dependencies, we just drop\n\t * them with CASCADE.\n\t */\n\tStringInfo dropCommand = makeStringInfo();\n\tappendStringInfo(dropCommand, \"DROP TRIGGER %s ON %s CASCADE;\",\n\t\t\t\t\t quotedTriggerName, qualifiedRelationName);\n\n\treturn dropCommand->data;\n}\n\n\n/*\n * DropIdentitiesOnTable drops the identities that depend on the given relation.\n */\nstatic void\nDropIdentitiesOnTable(Oid relationId)\n{\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\tList *dropCommandList = NIL;\n\n\tfor (int attributeIndex = 0; attributeIndex < tupleDescriptor->natts;\n\t\t attributeIndex++)\n\t{\n\t\tForm_pg_attribute attributeForm = TupleDescAttr(tupleDescriptor, attributeIndex);\n\t\tchar *columnName = NameStr(attributeForm->attname);\n\n\t\tif (attributeForm->attidentity)\n\t\t{\n\t\t\tchar *qualifiedTableName = generate_qualified_relation_name(relationId);\n\n\t\t\tStringInfo dropCommand = makeStringInfo();\n\n\t\t\tappendStringInfo(dropCommand, \"ALTER TABLE %s ALTER %s DROP IDENTITY\",\n\t\t\t\t\t\t\t qualifiedTableName,\n\t\t\t\t\t\t\t columnName);\n\n\t\t\tdropCommandList = lappend(dropCommandList, dropCommand->data);\n\t\t}\n\t}\n\n\trelation_close(relation, NoLock);\n\n\tchar *dropCommand = NULL;\n\tforeach_declared_ptr(dropCommand, dropCommandList)\n\t{\n\t\t/*\n\t\t * We need to disable/enable ddl propagation for this command, to prevent\n\t\t * sending unnecessary ALTER COLUMN commands for partitions, to MX workers.\n\t\t */\n\t\tExecuteAndLogUtilityCommandList(list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t\t\t\t\t   dropCommand,\n\t\t\t\t\t\t\t\t\t\t\t\t   ENABLE_DDL_PROPAGATION));\n\t}\n}\n\n\n/*\n * DropTableFromPublications drops the table from all of its publications.\n */\nstatic void\nDropTableFromPublications(Oid relationId)\n{\n\tbool isAdd = false;\n\n\tList *alterPublicationCommands =\n\t\tGetAlterPublicationDDLCommandsForTable(relationId, isAdd);\n\n\tExecuteAndLogUtilityCommandList(alterPublicationCommands);\n}\n\n\n/*\n * DropViewsOnTable drops the views that depend on the given relation.\n */\nstatic void\nDropViewsOnTable(Oid relationId)\n{\n\tList *views = GetDependingViews(relationId);\n\n\t/*\n\t * GetDependingViews returns views in the dependency order. We should drop views\n\t * in the reversed order since dropping views can cascade to other views below.\n\t */\n\tList *reverseOrderedViews = ReversedOidList(views);\n\n\tOid viewId = InvalidOid;\n\tforeach_declared_oid(viewId, reverseOrderedViews)\n\t{\n\t\tchar *qualifiedViewName = generate_qualified_relation_name(viewId);\n\n\t\tStringInfo dropCommand = makeStringInfo();\n\t\tappendStringInfo(dropCommand, \"DROP %sVIEW IF EXISTS %s\",\n\t\t\t\t\t\t get_rel_relkind(viewId) == RELKIND_MATVIEW ? \"MATERIALIZED \" :\n\t\t\t\t\t\t \"\",\n\t\t\t\t\t\t qualifiedViewName);\n\n\t\tExecuteAndLogUtilityCommand(dropCommand->data);\n\t}\n}\n\n\n/*\n * ReversedOidList takes a list of oids and returns the reverse ordered version of it.\n */\nstatic List *\nReversedOidList(List *oidList)\n{\n\tList *reversed = NIL;\n\tOid oid = InvalidOid;\n\tforeach_declared_oid(oid, oidList)\n\t{\n\t\treversed = lcons_oid(oid, reversed);\n\t}\n\n\treturn reversed;\n}\n\n\n/*\n * GetExplicitIndexOidList returns a list of index oids defined \"explicitly\"\n * on the relation with relationId by the \"CREATE INDEX\" commands. That means,\n * all the constraints defined on the relation except:\n *  - primary indexes,\n *  - unique indexes and\n *  - exclusion indexes\n * that are actually applied by the related constraints.\n */\nList *\nGetExplicitIndexOidList(Oid relationId)\n{\n\t/* flags are not applicable for AppendExplicitIndexIdsToList */\n\tint flags = 0;\n\treturn ExecuteFunctionOnEachTableIndex(relationId, AppendExplicitIndexIdsToList,\n\t\t\t\t\t\t\t\t\t\t   flags);\n}\n\n\n/*\n * AppendExplicitIndexIdsToList adds the given index oid if it is\n * explicitly created on its relation.\n */\nstatic void\nAppendExplicitIndexIdsToList(Form_pg_index indexForm, List **explicitIndexIdList,\n\t\t\t\t\t\t\t int flags)\n{\n\tif (!IndexImpliedByAConstraint(indexForm))\n\t{\n\t\t*explicitIndexIdList = lappend_oid(*explicitIndexIdList, indexForm->indexrelid);\n\t}\n}\n\n\n/*\n * GetRenameStatsCommandList returns a list of \"ALTER STATISTICS ...\n * RENAME TO ..._shardId\" commands for given statistics oid list.\n */\nstatic List *\nGetRenameStatsCommandList(List *statsOidList, uint64 shardId)\n{\n\tList *statsCommandList = NIL;\n\tOid statsOid;\n\tforeach_declared_oid(statsOid, statsOidList)\n\t{\n\t\tHeapTuple tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));\n\n\t\tif (!HeapTupleIsValid(tup))\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"No stats object found with id: %u\", statsOid)));\n\t\t\tcontinue;\n\t\t}\n\n\t\tForm_pg_statistic_ext statisticsForm = (Form_pg_statistic_ext) GETSTRUCT(tup);\n\n\t\tchar *statsName = statisticsForm->stxname.data;\n\t\tOid statsSchemaOid = statisticsForm->stxnamespace;\n\t\tchar *statsSchema = get_namespace_name(statsSchemaOid);\n\n\t\tchar *statsNameWithShardId = pstrdup(statsName);\n\t\tAppendShardIdToName(&statsNameWithShardId, shardId);\n\n\t\tchar *renameShardStatsCommand = GetRenameShardStatsCommand(statsSchema, statsName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   statsNameWithShardId);\n\t\tstatsCommandList = lappend(statsCommandList, renameShardStatsCommand);\n\t\tReleaseSysCache(tup);\n\t}\n\n\treturn statsCommandList;\n}\n\n\n/*\n * DropNextValExprsAndMoveOwnedSeqOwnerships drops default column definitions\n * that are based on sequences for relation with sourceRelationId.\n *\n * Also, for each such column that owns a sequence, it grants ownership to the\n * same named column of the relation with targetRelationId.\n */\nstatic void\nDropNextValExprsAndMoveOwnedSeqOwnerships(Oid sourceRelationId,\n\t\t\t\t\t\t\t\t\t\t  Oid targetRelationId)\n{\n\tList *columnNameList = NIL;\n\tList *ownedSequenceIdList = NIL;\n\tExtractDefaultColumnsAndOwnedSequences(sourceRelationId, &columnNameList,\n\t\t\t\t\t\t\t\t\t\t   &ownedSequenceIdList);\n\n\tchar *columnName = NULL;\n\tOid ownedSequenceId = InvalidOid;\n\tforboth_ptr_oid(columnName, columnNameList, ownedSequenceId, ownedSequenceIdList)\n\t{\n\t\t/*\n\t\t * We drop nextval() expressions because Citus currently evaluates\n\t\t * nextval() on the shell table, not on the shards. Hence, there is\n\t\t * no reason for keeping nextval(). Also, distributed/reference table\n\t\t * shards do not have - so be consistent with those.\n\t\t *\n\t\t * Note that we keep other kind of DEFAULT expressions on shards\n\t\t * because we still want to be able to evaluate DEFAULT expressions\n\t\t * that are not based on sequences on shards, e.g., for foreign key\n\t\t * - SET DEFAULT actions.\n\t\t */\n\t\tAttrNumber columnAttrNumber = get_attnum(sourceRelationId, columnName);\n\t\tif (ColumnDefaultsToNextVal(sourceRelationId, columnAttrNumber))\n\t\t{\n\t\t\tDropDefaultColumnDefinition(sourceRelationId, columnName);\n\t\t}\n\n\t\t/*\n\t\t * Column might own a sequence without having a nextval() expr on it\n\t\t * --e.g., due to ALTER SEQUENCE OWNED BY .. --, so check if that is\n\t\t * the case even if the column doesn't have a DEFAULT.\n\t\t */\n\t\tif (OidIsValid(ownedSequenceId))\n\t\t{\n\t\t\tTransferSequenceOwnership(ownedSequenceId, targetRelationId, columnName);\n\t\t}\n\t}\n}\n\n\n/*\n * DropDefaultColumnDefinition drops the DEFAULT definiton of the column with\n * columnName of the relation with relationId via process utility.\n */\nstatic void\nDropDefaultColumnDefinition(Oid relationId, char *columnName)\n{\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\tconst char *quotedColumnName = quote_identifier(columnName);\n\n\tStringInfo sequenceDropCommand = makeStringInfo();\n\tappendStringInfo(sequenceDropCommand,\n\t\t\t\t\t \"ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT\",\n\t\t\t\t\t qualifiedRelationName, quotedColumnName);\n\n\t/*\n\t * We need to disable/enable ddl propagation for this command, to prevent\n\t * sending unnecessary ALTER COLUMN commands for partitions, to MX workers.\n\t */\n\tExecuteAndLogUtilityCommandList(list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t\t\t\t   sequenceDropCommand->data,\n\t\t\t\t\t\t\t\t\t\t\t   ENABLE_DDL_PROPAGATION));\n}\n\n\n/*\n * TransferSequenceOwnership grants ownership of the sequence with sequenceId\n * to the column with targetColumnName of relation with targetRelationId via\n * process utility. Note that this function assumes that the target relation\n * has a column with targetColumnName which can default to the given sequence.\n */\nstatic void\nTransferSequenceOwnership(Oid sequenceId, Oid targetRelationId, char *targetColumnName)\n{\n\tchar *qualifiedSequenceName = generate_qualified_relation_name(sequenceId);\n\tchar *qualifiedTargetRelationName =\n\t\tgenerate_qualified_relation_name(targetRelationId);\n\tconst char *quotedTargetColumnName = quote_identifier(targetColumnName);\n\n\tStringInfo sequenceOwnershipCommand = makeStringInfo();\n\tappendStringInfo(sequenceOwnershipCommand, \"ALTER SEQUENCE %s OWNED BY %s.%s\",\n\t\t\t\t\t qualifiedSequenceName, qualifiedTargetRelationName,\n\t\t\t\t\t quotedTargetColumnName);\n\n\t/*\n\t * We need to disable/enable ddl propagation for this command, to prevent\n\t * sending unnecessary ALTER SEQUENCE commands for partitions, to MX workers.\n\t * Especially for partitioned tables, where the same sequence is used for\n\t * all partitions, this might cause errors.\n\t */\n\tExecuteAndLogUtilityCommandList(list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t\t\t\t   sequenceOwnershipCommand->data,\n\t\t\t\t\t\t\t\t\t\t\t   ENABLE_DDL_PROPAGATION));\n}\n\n\n/*\n * InsertMetadataForCitusLocalTable inserts necessary metadata for the citus\n * local table to the following metadata tables:\n * pg_dist_partition, pg_dist_shard & pg_dist_placement.\n */\nstatic void\nInsertMetadataForCitusLocalTable(Oid citusLocalTableId, uint64 shardId,\n\t\t\t\t\t\t\t\t bool autoConverted)\n{\n\tAssert(OidIsValid(citusLocalTableId));\n\tAssert(shardId != INVALID_SHARD_ID);\n\n\tchar distributionMethod = DISTRIBUTE_BY_NONE;\n\tchar replicationModel = REPLICATION_MODEL_STREAMING;\n\n\tuint32 colocationId = INVALID_COLOCATION_ID;\n\tVar *distributionColumn = NULL;\n\tInsertIntoPgDistPartition(citusLocalTableId, distributionMethod,\n\t\t\t\t\t\t\t  distributionColumn, colocationId,\n\t\t\t\t\t\t\t  replicationModel, autoConverted);\n\n\t/* set shard storage type according to relation type */\n\tchar shardStorageType = ShardStorageType(citusLocalTableId);\n\n\ttext *shardMinValue = NULL;\n\ttext *shardMaxValue = NULL;\n\tInsertShardRow(citusLocalTableId, shardId, shardStorageType,\n\t\t\t\t   shardMinValue, shardMaxValue);\n\n\tList *nodeList = list_make1(CoordinatorNodeIfAddedAsWorkerOrError());\n\n\tint replicationFactor = 1;\n\tint workerStartIndex = 0;\n\tInsertShardPlacementRows(citusLocalTableId, shardId, nodeList,\n\t\t\t\t\t\t\t workerStartIndex, replicationFactor);\n}\n\n\n/*\n * FinalizeCitusLocalTableCreation completes creation of the citus local table\n * with relationId by performing operations that should be done after creating\n * the shard and inserting the metadata.\n * If the cluster has metadata workers, we ensure proper propagation of the\n * sequences dependent with the table.\n */\nstatic void\nFinalizeCitusLocalTableCreation(Oid relationId)\n{\n\t/*\n\t * PG16+ supports truncate triggers on foreign tables\n\t */\n\tif (RegularTable(relationId) || IsForeignTable(relationId))\n\t{\n\t\tCreateTruncateTrigger(relationId);\n\t}\n\n\tif (ShouldSyncTableMetadata(relationId))\n\t{\n\t\tSyncCitusTableMetadata(relationId);\n\t}\n\n\t/*\n\t * We've a custom way of foreign key graph invalidation,\n\t * see InvalidateForeignKeyGraph().\n\t */\n\tif (TableReferenced(relationId) || TableReferencing(relationId))\n\t{\n\t\tInvalidateForeignKeyGraph();\n\t}\n}\n\n\n/*\n * ShouldAddNewTableToMetadata takes a relationId and returns true if we need to add a\n * newly created table to metadata, false otherwise.\n * For partitions and temporary tables, ShouldAddNewTableToMetadata returns false.\n * For other tables created, returns true, if we are on a coordinator that is added\n * as worker, and ofcourse, if the GUC use_citus_managed_tables is set to on.\n */\nbool\nShouldAddNewTableToMetadata(Oid relationId)\n{\n\tif (get_rel_persistence(relationId) == RELPERSISTENCE_TEMP ||\n\t\tPartitionTableNoLock(relationId))\n\t{\n\t\t/*\n\t\t * Shouldn't add table to metadata if it's a temp table, or a partition.\n\t\t * Creating partitions of a table that is added to metadata is already handled.\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (AddAllLocalTablesToMetadata && !IsBinaryUpgrade &&\n\t\tIsCoordinator() && CoordinatorAddedAsWorkerNode())\n\t{\n\t\t/*\n\t\t * We have verified that the GUC is set to true, and we are not upgrading,\n\t\t * and we are on the coordinator that is added as worker node.\n\t\t * So return true here, to add this newly created table to metadata.\n\t\t */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/citus_global_signal.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_global_signal.c\n *    Commands for Citus' overriden versions of pg_cancel_backend\n *    and pg_terminate_backend statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"signal.h\"\n\n#include \"lib/stringinfo.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/worker_manager.h\"\n\nstatic bool CitusSignalBackend(uint64 globalPID, uint64 timeout, int sig);\n\nPG_FUNCTION_INFO_V1(citus_cancel_backend);\nPG_FUNCTION_INFO_V1(citus_terminate_backend);\n\n/*\n * pg_cancel_backend overrides the Postgres' pg_cancel_backend to cancel\n * a query with a global pid so a query can be cancelled from another node.\n *\n * To cancel a query that is on another node, a pg_cancel_backend command is sent\n * to that node. This new command is sent with pid instead of global pid, so original\n * pg_cancel_backend function is used.\n */\nDatum\ncitus_cancel_backend(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tuint64 pid = PG_GETARG_INT64(0);\n\n\tint sig = SIGINT;\n\tuint64 timeout = 0;\n\tbool success = CitusSignalBackend(pid, timeout, sig);\n\n\tPG_RETURN_BOOL(success);\n}\n\n\n/*\n * pg_terminate_backend overrides the Postgres' pg_terminate_backend to terminate\n * a query with a global pid so a query can be terminated from another node.\n *\n * To terminate a query that is on another node, a pg_terminate_backend command is sent\n * to that node. This new command is sent with pid instead of global pid, so original\n * pg_terminate_backend function is used.\n */\nDatum\ncitus_terminate_backend(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tuint64 pid = PG_GETARG_INT64(0);\n\tuint64 timeout = PG_GETARG_INT64(1);\n\n\tint sig = SIGTERM;\n\tbool success = CitusSignalBackend(pid, timeout, sig);\n\n\tPG_RETURN_BOOL(success);\n}\n\n\n/*\n * CitusSignalBackend gets a global pid and and ends the original query with the global pid\n * that might have started in another node by connecting to that node and running either\n * pg_cancel_backend or pg_terminate_backend based on the withTerminate argument.\n */\nstatic bool\nCitusSignalBackend(uint64 globalPID, uint64 timeout, int sig)\n{\n\tAssert((sig == SIGINT) || (sig == SIGTERM));\n\n\tbool missingOk = false;\n\tint nodeId = ExtractNodeIdFromGlobalPID(globalPID, missingOk);\n\tint processId = ExtractProcessIdFromGlobalPID(globalPID);\n\n\tWorkerNode *workerNode = FindNodeWithNodeId(nodeId, missingOk);\n\n\tStringInfo cancelQuery = makeStringInfo();\n\n\tif (sig == SIGINT)\n\t{\n\t\tappendStringInfo(cancelQuery, \"SELECT pg_cancel_backend(%d::integer)\", processId);\n\t}\n\telse\n\t{\n\t\tappendStringInfo(cancelQuery,\n\t\t\t\t\t\t \"SELECT pg_terminate_backend(%d::integer, %lu::bigint)\",\n\t\t\t\t\t\t processId, timeout);\n\t}\n\n\tint connectionFlags = 0;\n\tMultiConnection *connection = GetNodeConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\tworkerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tworkerNode->workerPort);\n\n\tif (!SendRemoteCommand(connection, cancelQuery->data))\n\t{\n\t\t/* if we cannot connect, we warn and report false */\n\t\tReportConnectionError(connection, WARNING);\n\t\treturn false;\n\t}\n\n\tbool raiseInterrupts = true;\n\tPGresult *queryResult = GetRemoteCommandResult(connection, raiseInterrupts);\n\n\t/* if remote node throws an error, we also throw an error */\n\tif (!IsResponseOK(queryResult))\n\t{\n\t\tReportResultError(connection, queryResult, ERROR);\n\t}\n\n\tStringInfo queryResultString = makeStringInfo();\n\tbool success = EvaluateSingleQueryResult(connection, queryResult, queryResultString);\n\tif (success && strcmp(queryResultString->data, \"f\") == 0)\n\t{\n\t\t/* worker node returned \"f\" */\n\t\tsuccess = false;\n\t}\n\n\tPQclear(queryResult);\n\n\tbool raiseErrors = false;\n\tClearResults(connection, raiseErrors);\n\n\treturn success;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/cluster.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * cluster.c\n *    Commands for CLUSTER statement\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"commands/defrem.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n\n\nstatic bool IsClusterStmtVerbose_compat(ClusterStmt *clusterStmt);\n\n/*\n * PreprocessClusterStmt first determines whether a given cluster statement involves\n * a distributed table. If so (and if it is supported, i.e. no verbose), it\n * creates a DDLJob to encapsulate information needed during the worker node\n * portion of DDL execution before returning that DDLJob in a List. If no\n * distributed table is involved, this function returns NIL.\n */\nList *\nPreprocessClusterStmt(Node *node, const char *clusterCommand,\n\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tClusterStmt *clusterStmt = castNode(ClusterStmt, node);\n\tbool missingOK = false;\n\n\tif (clusterStmt->relation == NULL)\n\t{\n\t\tif (EnableUnsupportedFeatureMessages)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"not propagating CLUSTER command to worker nodes\"),\n\t\t\t\t\t\t\t  errhint(\"Provide a specific table in order to CLUSTER \"\n\t\t\t\t\t\t\t\t\t  \"distributed tables.\")));\n\t\t}\n\n\t\treturn NIL;\n\t}\n\n\t/* PostgreSQL uses access exclusive lock for CLUSTER command */\n\tOid relationId = RangeVarGetRelid(clusterStmt->relation, AccessExclusiveLock,\n\t\t\t\t\t\t\t\t\t  missingOK);\n\n\t/*\n\t * If the table does not exist, don't do anything here to allow PostgreSQL\n\t * to throw the appropriate error or notice message later.\n\t */\n\tif (!OidIsValid(relationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* we have no planning to do unless the table is distributed */\n\tbool isCitusRelation = IsCitusTable(relationId);\n\tif (!isCitusRelation)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * We do not support CLUSTER command on partitioned tables as it can not be run inside\n\t * transaction blocks. PostgreSQL currently does not support CLUSTER command on\n\t * partitioned tables in a transaction block. Although Citus can execute commands\n\t * outside of transaction block -- such as VACUUM -- we cannot do that here because\n\t * CLUSTER command is also not allowed from a function call as well. By default, Citus\n\t * uses `worker_apply_shard_ddl_command()`, where we should avoid it for this case.\n\t */\n\tif (PartitionedTable(relationId))\n\t{\n\t\tif (EnableUnsupportedFeatureMessages)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"not propagating CLUSTER command for partitioned \"\n\t\t\t\t\t\t\t\t\t \"table to worker nodes\"),\n\t\t\t\t\t\t\t  errhint(\"Provide a child partition table names in order to \"\n\t\t\t\t\t\t\t\t\t  \"CLUSTER distributed partitioned tables.\")));\n\t\t}\n\n\t\treturn NIL;\n\t}\n\n\tif (IsClusterStmtVerbose_compat(clusterStmt))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot run CLUSTER command\"),\n\t\t\t\t\t\terrdetail(\"VERBOSE option is currently unsupported \"\n\t\t\t\t\t\t\t\t  \"for distributed tables.\")));\n\t}\n\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\tddlJob->metadataSyncCommand = clusterCommand;\n\tddlJob->taskList = DDLTaskList(relationId, clusterCommand);\n\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * IsClusterStmtVerbose_compat returns true if the given statement\n * is a cluster statement with verbose option.\n */\nstatic bool\nIsClusterStmtVerbose_compat(ClusterStmt *clusterStmt)\n{\n\tDefElem *opt = NULL;\n\tforeach_declared_ptr(opt, clusterStmt->params)\n\t{\n\t\tif (strcmp(opt->defname, \"verbose\") == 0)\n\t\t{\n\t\t\treturn defGetBoolean(opt);\n\t\t}\n\t}\n\treturn false;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/collation.c",
    "content": "/*-------------------------------------------------------------------------\n * collation.c\n *\n * This file contains functions to create, alter and drop policies on\n * distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/htup_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/pg_collation.h\"\n#include \"parser/parse_type.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_compat.h\"\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/worker_create_or_replace.h\"\n#include \"distributed/worker_manager.h\"\n\n\nstatic char * CreateCollationDDLInternal(Oid collationId, Oid *collowner,\n\t\t\t\t\t\t\t\t\t\t char **quotedCollationName);\n\n/*\n * GetCreateCollationDDLInternal returns a CREATE COLLATE sql string for the\n * given collationId.\n *\n * It includes 2 out parameters to assist creation of ALTER COLLATION OWNER.\n * quotedCollationName must not be NULL.\n */\nstatic char *\nCreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollationName)\n{\n\tStringInfoData collationNameDef;\n\n\tHeapTuple heapTuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(collationId));\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\telog(ERROR, \"citus cache lookup failed for collation %u\", collationId);\n\t}\n\n\tForm_pg_collation collationForm = (Form_pg_collation) GETSTRUCT(heapTuple);\n\tchar collprovider = collationForm->collprovider;\n\tOid collnamespace = collationForm->collnamespace;\n\tconst char *collname = NameStr(collationForm->collname);\n\tbool collisdeterministic = collationForm->collisdeterministic;\n\n\tchar *collcollate;\n\tchar *collctype;\n\n\t/*\n\t * In PG15, there is an added option to use ICU as global locale provider.\n\t * pg_collation has three locale-related fields: collcollate and collctype,\n\t * which are libc-related fields, and a new one colliculocale, which is the\n\t * ICU-related field. Only the libc-related fields or the ICU-related field\n\t * is set, never both.\n\t */\n\tchar *colllocale;\n\tbool isnull;\n\n\tDatum datum = SysCacheGetAttr(COLLOID, heapTuple, Anum_pg_collation_collcollate,\n\t\t\t\t\t\t\t\t  &isnull);\n\tif (!isnull)\n\t{\n\t\tcollcollate = TextDatumGetCString(datum);\n\t}\n\telse\n\t{\n\t\tcollcollate = NULL;\n\t}\n\n\tdatum = SysCacheGetAttr(COLLOID, heapTuple, Anum_pg_collation_collctype, &isnull);\n\tif (!isnull)\n\t{\n\t\tcollctype = TextDatumGetCString(datum);\n\t}\n\telse\n\t{\n\t\tcollctype = NULL;\n\t}\n\n\tdatum = SysCacheGetAttr(COLLOID, heapTuple, Anum_pg_collation_colllocale, &isnull);\n\tif (!isnull)\n\t{\n\t\tcolllocale = TextDatumGetCString(datum);\n\t}\n\telse\n\t{\n\t\tcolllocale = NULL;\n\t}\n\n\tAssert((collcollate && collctype) || colllocale);\n\n\tif (collowner != NULL)\n\t{\n\t\t*collowner = collationForm->collowner;\n\t}\n\n\tReleaseSysCache(heapTuple);\n\tchar *schemaName = get_namespace_name(collnamespace);\n\t*quotedCollationName = quote_qualified_identifier(schemaName, collname);\n\tconst char *providerString =\n\t\tcollprovider == COLLPROVIDER_BUILTIN ? \"builtin\" :\n\t\tcollprovider == COLLPROVIDER_DEFAULT ? \"default\" :\n\t\tcollprovider == COLLPROVIDER_ICU ? \"icu\" :\n\t\tcollprovider == COLLPROVIDER_LIBC ? \"libc\" : NULL;\n\n\tif (providerString == NULL)\n\t{\n\t\telog(ERROR, \"unknown collation provider: %c\", collprovider);\n\t}\n\n\tinitStringInfo(&collationNameDef);\n\tappendStringInfo(&collationNameDef,\n\t\t\t\t\t \"CREATE COLLATION %s (provider = '%s'\",\n\t\t\t\t\t *quotedCollationName, providerString);\n\n\tif (colllocale)\n\t{\n\t\tappendStringInfo(&collationNameDef,\n\t\t\t\t\t\t \", locale = %s\",\n\t\t\t\t\t\t quote_literal_cstr(colllocale));\n\t\tpfree(colllocale);\n\t}\n\telse\n\t{\n\t\tif (strcmp(collcollate, collctype) == 0)\n\t\t{\n\t\t\tappendStringInfo(&collationNameDef,\n\t\t\t\t\t\t\t \", locale = %s\",\n\t\t\t\t\t\t\t quote_literal_cstr(collcollate));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(&collationNameDef,\n\t\t\t\t\t\t\t \", lc_collate = %s, lc_ctype = %s\",\n\t\t\t\t\t\t\t quote_literal_cstr(collcollate),\n\t\t\t\t\t\t\t quote_literal_cstr(collctype));\n\t\t}\n\t\tpfree(collcollate);\n\t\tpfree(collctype);\n\t}\n\n\tchar *collicurules = NULL;\n\tdatum = SysCacheGetAttr(COLLOID, heapTuple, Anum_pg_collation_collicurules, &isnull);\n\tif (!isnull)\n\t{\n\t\tcollicurules = TextDatumGetCString(datum);\n\t\tappendStringInfo(&collationNameDef, \", rules = %s\",\n\t\t\t\t\t\t quote_literal_cstr(collicurules));\n\t}\n\tif (!collisdeterministic)\n\t{\n\t\tappendStringInfoString(&collationNameDef, \", deterministic = false\");\n\t}\n\n\n\tappendStringInfoChar(&collationNameDef, ')');\n\treturn collationNameDef.data;\n}\n\n\n/*\n * CreateCollationDDL wrap CreateCollationDDLInternal to hide the out parameters.\n */\nchar *\nCreateCollationDDL(Oid collationId)\n{\n\tchar *quotedCollationName = NULL;\n\treturn CreateCollationDDLInternal(collationId, NULL, &quotedCollationName);\n}\n\n\n/*\n * CreateCollationDDLsIdempotent returns a List of cstrings for creating the collation\n * using create_or_replace_object & includes ALTER COLLATION ROLE.\n */\nList *\nCreateCollationDDLsIdempotent(Oid collationId)\n{\n\tStringInfoData collationAlterOwnerCommand;\n\tOid collowner = InvalidOid;\n\tchar *quotedCollationName = NULL;\n\tchar *createCollationCommand = CreateCollationDDLInternal(collationId, &collowner,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &quotedCollationName);\n\n\tinitStringInfo(&collationAlterOwnerCommand);\n\tappendStringInfo(&collationAlterOwnerCommand,\n\t\t\t\t\t \"ALTER COLLATION %s OWNER TO %s\",\n\t\t\t\t\t quotedCollationName,\n\t\t\t\t\t quote_identifier(GetUserNameFromId(collowner, false)));\n\n\treturn list_make2(WrapCreateOrReplace(createCollationCommand),\n\t\t\t\t\t  collationAlterOwnerCommand.data);\n}\n\n\nList *\nAlterCollationOwnerObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tRelation relation;\n\n\tAssert(stmt->objectType == OBJECT_COLLATION);\n\n\tObjectAddress objectAddress = get_object_address(stmt->objectType, stmt->object,\n\t\t\t\t\t\t\t\t\t\t\t\t\t &relation, AccessExclusiveLock,\n\t\t\t\t\t\t\t\t\t\t\t\t\t missing_ok);\n\n\tObjectAddress *objectAddressCopy = palloc0(sizeof(ObjectAddress));\n\t*objectAddressCopy = objectAddress;\n\treturn list_make1(objectAddressCopy);\n}\n\n\n/*\n * RenameCollationStmtObjectAddress returns the ObjectAddress of the type that is the object\n * of the RenameStmt. Errors if missing_ok is false.\n */\nList *\nRenameCollationStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_COLLATION);\n\n\tOid collationOid = get_collation_oid((List *) stmt->object, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, CollationRelationId, collationOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * AlterCollationSchemaStmtObjectAddress returns the ObjectAddress of the type that is the\n * subject of the AlterObjectSchemaStmt. Errors if missing_ok is false.\n *\n * This could be called both before or after it has been applied locally. It will look in\n * the old schema first, if the type cannot be found in that schema it will look in the\n * new schema. Errors if missing_ok is false and the type cannot be found in either of the\n * schemas.\n */\nList *\nAlterCollationSchemaStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_COLLATION);\n\n\tList *name = (List *) stmt->object;\n\tOid collationOid = get_collation_oid(name, true);\n\n\tif (collationOid == InvalidOid)\n\t{\n\t\tList *newName = list_make2(makeString(stmt->newschema), lfirst(list_tail(name)));\n\n\t\tcollationOid = get_collation_oid(newName, true);\n\n\t\tif (!missing_ok && collationOid == InvalidOid)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t\t\terrmsg(\"type \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t\t   NameListToString(name))));\n\t\t}\n\t}\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, CollationRelationId, collationOid);\n\treturn list_make1(address);\n}\n\n\n/*\n * GenerateBackupNameForCollationCollision generates a new collation name for an existing collation.\n * The name is generated in such a way that the new name doesn't overlap with an existing collation\n * by adding a suffix with incrementing number after the new name.\n */\nchar *\nGenerateBackupNameForCollationCollision(const ObjectAddress *address)\n{\n\tchar *newName = palloc0(NAMEDATALEN);\n\tchar suffix[NAMEDATALEN] = { 0 };\n\tint count = 0;\n\tchar *baseName = get_collation_name(address->objectId);\n\tint baseLength = strlen(baseName);\n\tHeapTuple collationTuple = SearchSysCache1(COLLOID, address->objectId);\n\n\tif (!HeapTupleIsValid(collationTuple))\n\t{\n\t\telog(ERROR, \"citus cache lookup failed\");\n\t\treturn NULL;\n\t}\n\tForm_pg_collation collationForm = (Form_pg_collation) GETSTRUCT(collationTuple);\n\tString *namespace = makeString(get_namespace_name(collationForm->collnamespace));\n\tReleaseSysCache(collationTuple);\n\n\twhile (true)\n\t{\n\t\tint suffixLength = SafeSnprintf(suffix, NAMEDATALEN - 1, \"(citus_backup_%d)\",\n\t\t\t\t\t\t\t\t\t\tcount);\n\n\t\t/* trim the base name at the end to leave space for the suffix and trailing \\0 */\n\t\tbaseLength = Min(baseLength, NAMEDATALEN - suffixLength - 1);\n\n\t\t/* clear newName before copying the potentially trimmed baseName and suffix */\n\t\tmemset(newName, 0, NAMEDATALEN);\n\t\tstrncpy_s(newName, NAMEDATALEN, baseName, baseLength);\n\t\tstrncpy_s(newName + baseLength, NAMEDATALEN - baseLength, suffix,\n\t\t\t\t  suffixLength);\n\n\t\tList *newCollationName = list_make2(namespace, makeString(newName));\n\n\t\t/* don't need to rename if the input arguments don't match */\n\t\tOid existingCollationId = get_collation_oid(newCollationName, true);\n\n\t\tif (existingCollationId == InvalidOid)\n\t\t{\n\t\t\treturn newName;\n\t\t}\n\n\t\tcount++;\n\t}\n}\n\n\nList *\nDefineCollationStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tDefineStmt *stmt = castNode(DefineStmt, node);\n\tAssert(stmt->kind == OBJECT_COLLATION);\n\n\tOid collOid = get_collation_oid(stmt->defnames, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, CollationRelationId, collOid);\n\n\treturn list_make1(address);\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/comment.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * comment.c\n *    Commands to interact with the comments for all database\n *    object types.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"access/table.h\"\n#include \"catalog/pg_shdescription.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/rel.h\"\n\n#include \"distributed/comment.h\"\n\nstatic char * GetCommentForObject(Oid classOid, Oid objectOid);\n\n\nList *\nGetCommentPropagationCommands(Oid classOid, Oid objOoid, char *objectName, ObjectType\n\t\t\t\t\t\t\t  objectType)\n{\n\tList *commands = NIL;\n\n\tStringInfo commentStmt = makeStringInfo();\n\n\t/* Get the comment for the database */\n\tchar *comment = GetCommentForObject(classOid, objOoid);\n\tchar const *commentObjectType = ObjectTypeNames[objectType];\n\n\t/* Create the SQL command to propagate the comment to other nodes */\n\tif (comment != NULL)\n\t{\n\t\tappendStringInfo(commentStmt, \"COMMENT ON %s %s IS %s;\", commentObjectType,\n\t\t\t\t\t\t quote_identifier(objectName),\n\t\t\t\t\t\t quote_literal_cstr(comment));\n\t}\n\n\n\t/* Add the command to the list */\n\tif (commentStmt->len > 0)\n\t{\n\t\tcommands = list_make1(commentStmt->data);\n\t}\n\n\treturn commands;\n}\n\n\nstatic char *\nGetCommentForObject(Oid classOid, Oid objectOid)\n{\n\tHeapTuple tuple;\n\tchar *comment = NULL;\n\n\t/* Open pg_shdescription catalog */\n\tRelation shdescRelation = table_open(SharedDescriptionRelationId, AccessShareLock);\n\n\t/* Scan the table */\n\tScanKeyData scanKey[2];\n\n\tScanKeyInit(&scanKey[0],\n\t\t\t\tAnum_pg_shdescription_objoid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(objectOid));\n\tScanKeyInit(&scanKey[1],\n\t\t\t\tAnum_pg_shdescription_classoid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(classOid));\n\tbool indexOk = true;\n\tint scanKeyCount = 2;\n\tSysScanDesc scan = systable_beginscan(shdescRelation, SharedDescriptionObjIndexId,\n\t\t\t\t\t\t\t\t\t\t  indexOk, NULL, scanKeyCount,\n\t\t\t\t\t\t\t\t\t\t  scanKey);\n\tif ((tuple = systable_getnext(scan)) != NULL)\n\t{\n\t\tbool isNull = false;\n\n\t\tTupleDesc tupdesc = RelationGetDescr(shdescRelation);\n\n\t\tDatum descDatum = heap_getattr(tuple, Anum_pg_shdescription_description, tupdesc,\n\t\t\t\t\t\t\t\t\t   &isNull);\n\n\n\t\t/* Add the command to the list */\n\t\tif (!isNull)\n\t\t{\n\t\t\tcomment = TextDatumGetCString(descDatum);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcomment = NULL;\n\t\t}\n\t}\n\n\t/* End the scan and close the catalog */\n\tsystable_endscan(scan);\n\ttable_close(shdescRelation, AccessShareLock);\n\n\treturn comment;\n}\n\n\n/*\n * CommentObjectAddress resolves the ObjectAddress for the object\n * on which the comment is placed. Optionally errors if the object does not\n * exist based on the missing_ok flag passed in by the caller.\n */\nList *\nCommentObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tCommentStmt *stmt = castNode(CommentStmt, node);\n\tRelation relation;\n\n\tObjectAddress objectAddress = get_object_address(stmt->objtype, stmt->object,\n\t\t\t\t\t\t\t\t\t\t\t\t\t &relation, AccessExclusiveLock,\n\t\t\t\t\t\t\t\t\t\t\t\t\t missing_ok);\n\n\tObjectAddress *objectAddressCopy = palloc0(sizeof(ObjectAddress));\n\t*objectAddressCopy = objectAddress;\n\treturn list_make1(objectAddressCopy);\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/common.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * common.c\n *\n *    Most of the object propagation code consists of mostly the same\n *    operations, varying slightly in parameters passed around. This\n *    file contains most of the reusable logic in object propagation.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/objectaddress.h\"\n#include \"catalog/pg_ts_config.h\"\n#include \"catalog/pg_ts_dict.h\"\n#include \"nodes/parsenodes.h\"\n#include \"tcop/utility.h\"\n\n#include \"distributed/citus_depended_object.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/worker_transaction.h\"\n\n\n/*\n * PostprocessCreateDistributedObjectFromCatalogStmt is a common function that can be used\n * for most objects during their creation phase. After the creation has happened locally\n * this function creates idempotent statements to recreate the object addressed by the\n * ObjectAddress of resolved from the creation statement.\n *\n * Since object already need to be able to create idempotent creation sql to support\n * scaleout operations we can reuse this logic during the initial creation of the objects\n * to reduce the complexity of implementation of new DDL commands.\n */\nList *\nPostprocessCreateDistributedObjectFromCatalogStmt(Node *stmt, const char *queryString)\n{\n\tconst DistributeObjectOps *ops = GetDistributeObjectOps(stmt);\n\tAssert(ops != NULL);\n\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* check creation against multi-statement transaction policy */\n\tif (!ShouldPropagateCreateInCoordinatedTransction())\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (ops->featureFlag && *ops->featureFlag == false)\n\t{\n\t\t/* not propagating when a configured feature flag is turned off by the user */\n\t\treturn NIL;\n\t}\n\n\tif (ops->qualify &&\n\t\tDistOpsValidityState(stmt, ops) == ShouldQualifyAfterLocalCreation)\n\t{\n\t\t/* qualify the statement after local creation */\n\t\tops->qualify(stmt);\n\t}\n\n\tList *addresses = GetObjectAddressListFromParseTree(stmt, false, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\tEnsureCoordinator();\n\tEnsureSequentialMode(ops->objectType);\n\n\t/* If the object has any unsupported dependency warn, and only create locally */\n\tDeferredErrorMessage *depError = DeferErrorIfAnyObjectHasUnsupportedDependency(\n\t\taddresses);\n\tif (depError != NULL)\n\t{\n\t\tif (EnableUnsupportedFeatureMessages)\n\t\t{\n\t\t\tRaiseDeferredError(depError, WARNING);\n\t\t}\n\n\t\treturn NIL;\n\t}\n\n\tEnsureAllObjectDependenciesExistOnAllNodes(addresses);\n\n\tList *commands = GetAllDependencyCreateDDLCommands(addresses);\n\n\tcommands = lcons(DISABLE_DDL_PROPAGATION, commands);\n\tcommands = lappend(commands, ENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * PreprocessAlterDistributedObjectStmt handles any updates to distributed objects by\n * creating the fully qualified sql to apply to all workers after checking all\n * predconditions that apply to propagating changes.\n *\n * Preconditions are (in order):\n *  - not in a CREATE/ALTER EXTENSION code block\n *  - citus.enable_metadata_sync is turned on\n *  - object being altered is distributed\n *  - any object specific feature flag is turned on when a feature flag is available\n *\n * Once we conclude to propagate the changes to the workers we make sure that the command\n * has been executed on the coordinator and force any ongoing transaction to run in\n * sequential mode. If any of these steps fail we raise an error to inform the user.\n *\n * Lastly we recreate a fully qualified version of the original sql and prepare the tasks\n * to send these sql commands to the workers. These tasks include instructions to prevent\n * recursion of propagation with Citus' MX functionality.\n */\nList *\nPreprocessAlterDistributedObjectStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tconst DistributeObjectOps *ops = GetDistributeObjectOps(stmt);\n\tAssert(ops != NULL);\n\n\tList *addresses = GetObjectAddressListFromParseTree(stmt, false, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(addresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (ops->featureFlag && *ops->featureFlag == false)\n\t{\n\t\t/* not propagating when a configured feature flag is turned off by the user */\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tEnsureSequentialMode(ops->objectType);\n\n\tQualifyTreeNode(stmt);\n\tconst char *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * PostprocessAlterDistributedObjectStmt is the counter part of\n * PreprocessAlterDistributedObjectStmt that should be executed after the object has been\n * changed locally.\n *\n * We perform the same precondition checks as before to skip this operation if any of the\n * failed during preprocessing. Since we already raised an error on other checks we don't\n * have to repeat them here, as they will never fail during postprocessing.\n *\n * When objects get altered they can start depending on undistributed objects. Now that\n * the objects has been changed locally we can find these new dependencies and make sure\n * they get created on the workers before we send the command list to the workers.\n */\nList *\nPostprocessAlterDistributedObjectStmt(Node *stmt, const char *queryString)\n{\n\tconst DistributeObjectOps *ops = GetDistributeObjectOps(stmt);\n\tAssert(ops != NULL);\n\n\tList *addresses = GetObjectAddressListFromParseTree(stmt, false, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(addresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (ops->featureFlag && *ops->featureFlag == false)\n\t{\n\t\t/* not propagating when a configured feature flag is turned off by the user */\n\t\treturn NIL;\n\t}\n\n\tEnsureAllObjectDependenciesExistOnAllNodes(addresses);\n\n\treturn NIL;\n}\n\n\n/*\n * PreprocessDropDistributedObjectStmt is a general purpose hook that can propagate any\n * DROP statement.\n *\n * DROP statements are one of the few DDL statements that can work on many different\n * objects at once. Instead of resolving just one ObjectAddress and check it is\n * distributed we will need to lookup many different object addresses. Only if an object\n * was _not_ distributed we will need to remove it from the list of objects before we\n * recreate the sql statement.\n *\n * Given that we actually _do_ need to drop them locally we can't simply remove them from\n * the object list. Instead we create a new list where we only add distributed objects to.\n * Before we recreate the sql statement we put this list on the drop statement, so that\n * the SQL created will only contain the objects that are actually distributed in the\n * cluster. After we have the SQL we restore the old list so that all objects get deleted\n * locally.\n *\n * The reason we need to go through all this effort is taht we can't resolve the object\n * addresses anymore after the objects have been removed locally. Meaning during the\n * postprocessing we cannot understand which objects were distributed to begin with.\n */\nList *\nPreprocessDropDistributedObjectStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\n\t/*\n\t * We swap the list of objects to remove during deparse so we need a reference back to\n\t * the old list to put back\n\t */\n\tList *originalObjects = stmt->objects;\n\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tQualifyTreeNode(node);\n\n\tList *distributedObjects = NIL;\n\tList *distributedObjectAddresses = NIL;\n\tNode *object = NULL;\n\tforeach_declared_ptr(object, stmt->objects)\n\t{\n\t\t/* TODO understand if the lock should be sth else */\n\t\tRelation rel = NULL; /* not used, but required to pass to get_object_address */\n\t\tObjectAddress address = get_object_address(stmt->removeType, object, &rel,\n\t\t\t\t\t\t\t\t\t\t\t\t   AccessShareLock, stmt->missing_ok);\n\t\tObjectAddress *addressPtr = palloc0(sizeof(ObjectAddress));\n\t\t*addressPtr = address;\n\t\tif (IsAnyObjectDistributed(list_make1(addressPtr)))\n\t\t{\n\t\t\tdistributedObjects = lappend(distributedObjects, object);\n\t\t\tdistributedObjectAddresses = lappend(distributedObjectAddresses, addressPtr);\n\t\t}\n\t}\n\n\tif (list_length(distributedObjects) <= 0)\n\t{\n\t\t/* no distributed objects to drop */\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * managing objects can only be done on the coordinator if ddl propagation is on. when\n\t * it is off we will never get here. MX workers don't have a notion of distributed\n\t * types, so we block the call.\n\t */\n\tEnsureCoordinator();\n\n\t/*\n\t * remove the entries for the distributed objects on dropping\n\t */\n\tObjectAddress *address = NULL;\n\tforeach_declared_ptr(address, distributedObjectAddresses)\n\t{\n\t\tUnmarkObjectDistributed(address);\n\t}\n\n\t/*\n\t * temporary swap the lists of objects to delete with the distributed objects and\n\t * deparse to an executable sql statement for the workers\n\t */\n\tstmt->objects = distributedObjects;\n\tchar *dropStmtSql = DeparseTreeNode((Node *) stmt);\n\tstmt->objects = originalObjects;\n\n\tEnsureSequentialMode(stmt->removeType);\n\n\t/* to prevent recursion with mx we disable ddl propagation */\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\tdropStmtSql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * DropTextSearchDictObjectAddress returns list of object addresses in\n * the drop tsdict statement.\n */\nList *\nDropTextSearchDictObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\n\tList *objectAddresses = NIL;\n\n\tList *objNameList = NIL;\n\tforeach_declared_ptr(objNameList, stmt->objects)\n\t{\n\t\tOid tsdictOid = get_ts_dict_oid(objNameList, missing_ok);\n\n\t\tObjectAddress *objectAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*objectAddress, TSDictionaryRelationId, tsdictOid);\n\t\tobjectAddresses = lappend(objectAddresses, objectAddress);\n\t}\n\n\treturn objectAddresses;\n}\n\n\n/*\n * DropTextSearchConfigObjectAddress returns list of object addresses in\n * the drop tsconfig statement.\n */\nList *\nDropTextSearchConfigObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\n\tList *objectAddresses = NIL;\n\n\tList *objNameList = NIL;\n\tforeach_declared_ptr(objNameList, stmt->objects)\n\t{\n\t\tOid tsconfigOid = get_ts_config_oid(objNameList, missing_ok);\n\n\t\tObjectAddress *objectAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*objectAddress, TSConfigRelationId, tsconfigOid);\n\t\tobjectAddresses = lappend(objectAddresses, objectAddress);\n\t}\n\n\treturn objectAddresses;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/create_distributed_table.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * create_distributed_table.c\n *\t  Routines relation to the creation of distributed relations.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/hash.h\"\n#include \"access/heapam.h\"\n#include \"access/htup.h\"\n#include \"access/htup_details.h\"\n#include \"access/nbtree.h\"\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/index.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_attrdef.h\"\n#include \"catalog/pg_attribute.h\"\n#include \"catalog/pg_enum.h\"\n#include \"catalog/pg_extension.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_opclass.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_trigger.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/defrem.h\"\n#include \"commands/extension.h\"\n#include \"commands/sequence.h\"\n#include \"commands/tablecmds.h\"\n#include \"commands/trigger.h\"\n#include \"executor/executor.h\"\n#include \"executor/spi.h\"\n#include \"nodes/execnodes.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/pg_list.h\"\n#include \"parser/parse_expr.h\"\n#include \"parser/parse_node.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parser.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/lmgr.h\"\n#include \"tcop/pquery.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/inval.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/rel.h\"\n#include \"utils/snapmgr.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/distributed_execution_locks.h\"\n#include \"distributed/distribution_column.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/pg_dist_colocation.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/replicate_none_dist_table_shard.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/shard_rebalancer.h\"\n#include \"distributed/shard_split.h\"\n#include \"distributed/shard_transfer.h\"\n#include \"distributed/shared_library_init.h\"\n#include \"distributed/utils/distribution_column_map.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_shard_visibility.h\"\n#include \"distributed/worker_transaction.h\"\n\n\n/* common params that apply to all Citus table types */\ntypedef struct\n{\n\tchar distributionMethod;\n\tchar replicationModel;\n} CitusTableParams;\n\n\n/*\n * Params that only apply to distributed tables, i.e., the ones that are\n * known as DISTRIBUTED_TABLE by Citus metadata.\n */\ntypedef struct\n{\n\tint shardCount;\n\tbool shardCountIsStrict;\n\tchar *distributionColumnName;\n\tColocationParam colocationParam;\n} DistributedTableParams;\n\n\n/*\n * once every LOG_PER_TUPLE_AMOUNT, the copy will be logged.\n */\n#define LOG_PER_TUPLE_AMOUNT 1000000\n\n/* local function forward declarations */\nstatic void CreateDistributedTableConcurrently(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t   char *distributionColumnName,\n\t\t\t\t\t\t\t\t\t\t\t   char distributionMethod,\n\t\t\t\t\t\t\t\t\t\t\t   char *colocateWithTableName,\n\t\t\t\t\t\t\t\t\t\t\t   int shardCount,\n\t\t\t\t\t\t\t\t\t\t\t   bool shardCountIsStrict);\nstatic char DecideDistTableReplicationModel(char distributionMethod,\n\t\t\t\t\t\t\t\t\t\t\tchar *colocateWithTableName);\nstatic List * HashSplitPointsForShardList(List *shardList);\nstatic List * HashSplitPointsForShardCount(int shardCount);\nstatic List * WorkerNodesForShardList(List *shardList);\nstatic List * RoundRobinWorkerNodeList(List *workerNodeList, int listLength);\nstatic CitusTableParams DecideCitusTableParams(CitusTableType tableType,\n\t\t\t\t\t\t\t\t\t\t\t   DistributedTableParams *\n\t\t\t\t\t\t\t\t\t\t\t   distributedTableParams);\nstatic void CreateCitusTable(Oid relationId, CitusTableType tableType,\n\t\t\t\t\t\t\t DistributedTableParams *distributedTableParams);\nstatic void ConvertCitusLocalTableToTableType(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t  CitusTableType tableType,\n\t\t\t\t\t\t\t\t\t\t\t  DistributedTableParams *\n\t\t\t\t\t\t\t\t\t\t\t  distributedTableParams);\nstatic void CreateHashDistributedTableShards(Oid relationId, int shardCount,\n\t\t\t\t\t\t\t\t\t\t\t Oid colocatedTableId, bool localTableEmpty);\nstatic void CreateSingleShardTableShard(Oid relationId, Oid colocatedTableId,\n\t\t\t\t\t\t\t\t\t\tuint32 colocationId);\nstatic uint32 ColocationIdForNewTable(Oid relationId, CitusTableType tableType,\n\t\t\t\t\t\t\t\t\t  DistributedTableParams *distributedTableParams,\n\t\t\t\t\t\t\t\t\t  Var *distributionColumn);\nstatic void EnsureRelationCanBeDistributed(Oid relationId, Var *distributionColumn,\n\t\t\t\t\t\t\t\t\t\t   char distributionMethod, uint32 colocationId,\n\t\t\t\t\t\t\t\t\t\t   char replicationModel);\nstatic void EnsureLocalTableEmpty(Oid relationId);\nstatic void EnsureRelationHasNoTriggers(Oid relationId);\nstatic Oid SupportFunctionForColumn(Var *partitionColumn, Oid accessMethodId,\n\t\t\t\t\t\t\t\t\tint16 supportFunctionNumber);\nstatic void EnsureLocalTableEmptyIfNecessary(Oid relationId, char distributionMethod);\nstatic bool ShouldLocalTableBeEmpty(Oid relationId, char distributionMethod);\nstatic void EnsureCitusTableCanBeCreated(Oid relationOid);\nstatic void PropagatePrerequisiteObjectsForDistributedTable(Oid relationId);\nstatic void EnsureDistributedSequencesHaveOneType(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t  List *seqInfoList);\nstatic void CopyLocalDataIntoShards(Oid distributedTableId);\nstatic List * TupleDescColumnNameList(TupleDesc tupleDescriptor);\n\nstatic bool DistributionColumnUsesNumericColumnNegativeScale(TupleDesc relationDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Var *distributionColumn);\nstatic int numeric_typmod_scale(int32 typmod);\nstatic bool is_valid_numeric_typmod(int32 typmod);\n\nstatic void DistributionColumnIsGeneratedCheck(TupleDesc relationDesc,\n\t\t\t\t\t\t\t\t\t\t\t   Var *distributionColumn,\n\t\t\t\t\t\t\t\t\t\t\t   const char *relationName);\nstatic bool CanUseExclusiveConnections(Oid relationId, bool localTableEmpty);\nstatic uint64 DoCopyFromLocalTableIntoShards(Relation distributedRelation,\n\t\t\t\t\t\t\t\t\t\t\t DestReceiver *copyDest,\n\t\t\t\t\t\t\t\t\t\t\t TupleTableSlot *slot,\n\t\t\t\t\t\t\t\t\t\t\t EState *estate);\nstatic void ErrorIfTemporaryTable(Oid relationId);\nstatic void ErrorIfForeignTable(Oid relationOid);\nstatic void SendAddLocalTableToMetadataCommandOutsideTransaction(Oid relationId);\nstatic void EnsureDistributableTable(Oid relationId);\nstatic void EnsureForeignKeysForDistributedTableConcurrently(Oid relationId);\nstatic void EnsureColocateWithTableIsValid(Oid relationId, char distributionMethod,\n\t\t\t\t\t\t\t\t\t\t   char *distributionColumnName,\n\t\t\t\t\t\t\t\t\t\t   char *colocateWithTableName);\nstatic void WarnIfTableHaveNoReplicaIdentity(Oid relationId);\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(master_create_distributed_table);\nPG_FUNCTION_INFO_V1(create_distributed_table_concurrently);\nPG_FUNCTION_INFO_V1(create_distributed_table);\nPG_FUNCTION_INFO_V1(create_reference_table);\n\n\n/*\n * master_create_distributed_table is a deprecated predecessor to\n * create_distributed_table\n */\nDatum\nmaster_create_distributed_table(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"master_create_distributed_table has been deprecated\")));\n}\n\n\n/*\n * create_distributed_table gets a table name, distribution column,\n * distribution method and colocate_with option, then it creates a\n * distributed table.\n */\nDatum\ncreate_distributed_table(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tif (PG_ARGISNULL(0) || PG_ARGISNULL(3))\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\tOid relationId = PG_GETARG_OID(0);\n\ttext *distributionColumnText = PG_ARGISNULL(1) ? NULL : PG_GETARG_TEXT_P(1);\n\tOid distributionMethodOid = PG_GETARG_OID(2);\n\ttext *colocateWithTableNameText = PG_GETARG_TEXT_P(3);\n\tchar *colocateWithTableName = text_to_cstring(colocateWithTableNameText);\n\n\tbool shardCountIsStrict = false;\n\tif (distributionColumnText)\n\t{\n\t\tif (PG_ARGISNULL(2))\n\t\t{\n\t\t\tPG_RETURN_VOID();\n\t\t}\n\n\t\tint shardCount = ShardCount;\n\t\tif (!PG_ARGISNULL(4))\n\t\t{\n\t\t\tif (!IsColocateWithDefault(colocateWithTableName) &&\n\t\t\t\t!IsColocateWithNone(colocateWithTableName))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"Cannot use colocate_with with a table \"\n\t\t\t\t\t\t\t\t\t   \"and shard_count at the same time\")));\n\t\t\t}\n\n\t\t\tshardCount = PG_GETARG_INT32(4);\n\n\t\t\t/*\n\t\t\t * If shard_count parameter is given, then we have to\n\t\t\t * make sure table has that many shards.\n\t\t\t */\n\t\t\tshardCountIsStrict = true;\n\t\t}\n\n\t\tchar *distributionColumnName = text_to_cstring(distributionColumnText);\n\t\tAssert(distributionColumnName != NULL);\n\n\t\tchar distributionMethod = LookupDistributionMethod(distributionMethodOid);\n\n\t\tif (shardCount < 1 || shardCount > MAX_SHARD_COUNT)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"%d is outside the valid range for \"\n\t\t\t\t\t\t\t\t   \"parameter \\\"shard_count\\\" (1 .. %d)\",\n\t\t\t\t\t\t\t\t   shardCount, MAX_SHARD_COUNT)));\n\t\t}\n\n\t\tCreateDistributedTable(relationId, distributionColumnName, distributionMethod,\n\t\t\t\t\t\t\t   shardCount, shardCountIsStrict, colocateWithTableName);\n\t}\n\telse\n\t{\n\t\tif (!PG_ARGISNULL(4))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"shard_count can't be specified when the \"\n\t\t\t\t\t\t\t\t   \"distribution column is null because in \"\n\t\t\t\t\t\t\t\t   \"that case it's automatically set to 1\")));\n\t\t}\n\n\t\tif (!PG_ARGISNULL(2) &&\n\t\t\tLookupDistributionMethod(PG_GETARG_OID(2)) != DISTRIBUTE_BY_HASH)\n\t\t{\n\t\t\t/*\n\t\t\t * As we do for shard_count parameter, we could throw an error if\n\t\t\t * distribution_type is not NULL when creating a single-shard table.\n\t\t\t * However, this requires changing the default value of distribution_type\n\t\t\t * parameter to NULL and this would mean a breaking change for most\n\t\t\t * users because they're mostly using this API to create sharded\n\t\t\t * tables. For this reason, here we instead do nothing if the distribution\n\t\t\t * method is DISTRIBUTE_BY_HASH.\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\"distribution_type can't be specified \"\n\t\t\t\t\t\t\t\t   \"when the distribution column is null \")));\n\t\t}\n\n\t\tColocationParam colocationParam = {\n\t\t\t.colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT,\n\t\t\t.colocateWithTableName = colocateWithTableName,\n\t\t};\n\t\tCreateSingleShardTable(relationId, colocationParam);\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * create_distributed_concurrently gets a table name, distribution column,\n * distribution method and colocate_with option, then it creates a\n * distributed table.\n */\nDatum\ncreate_distributed_table_concurrently(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tif (PG_ARGISNULL(0) || PG_ARGISNULL(2) || PG_ARGISNULL(3))\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\tif (PG_ARGISNULL(1))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot use create_distributed_table_concurrently \"\n\t\t\t\t\t\t\t   \"to create a distributed table with a null shard \"\n\t\t\t\t\t\t\t   \"key, consider using create_distributed_table()\")));\n\t}\n\n\tOid relationId = PG_GETARG_OID(0);\n\ttext *distributionColumnText = PG_GETARG_TEXT_P(1);\n\tchar *distributionColumnName = text_to_cstring(distributionColumnText);\n\tOid distributionMethodOid = PG_GETARG_OID(2);\n\tchar distributionMethod = LookupDistributionMethod(distributionMethodOid);\n\ttext *colocateWithTableNameText = PG_GETARG_TEXT_P(3);\n\tchar *colocateWithTableName = text_to_cstring(colocateWithTableNameText);\n\n\tbool shardCountIsStrict = false;\n\tint shardCount = ShardCount;\n\tif (!PG_ARGISNULL(4))\n\t{\n\t\tif (pg_strncasecmp(colocateWithTableName, \"default\", NAMEDATALEN) != 0 &&\n\t\t\tpg_strncasecmp(colocateWithTableName, \"none\", NAMEDATALEN) != 0)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"Cannot use colocate_with with a table \"\n\t\t\t\t\t\t\t\t   \"and shard_count at the same time\")));\n\t\t}\n\n\t\tshardCount = PG_GETARG_INT32(4);\n\n\t\t/*\n\t\t * if shard_count parameter is given than we have to\n\t\t * make sure table has that many shards\n\t\t */\n\t\tshardCountIsStrict = true;\n\t}\n\n\tCreateDistributedTableConcurrently(relationId, distributionColumnName,\n\t\t\t\t\t\t\t\t\t   distributionMethod,\n\t\t\t\t\t\t\t\t\t   colocateWithTableName,\n\t\t\t\t\t\t\t\t\t   shardCount,\n\t\t\t\t\t\t\t\t\t   shardCountIsStrict);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * CreateDistributedTableConcurrently distributes a table by first converting\n * it to a Citus local table and then splitting the shard of the Citus local\n * table.\n *\n * If anything goes wrong during the second phase, the table is left as a\n * Citus local table.\n */\nstatic void\nCreateDistributedTableConcurrently(Oid relationId, char *distributionColumnName,\n\t\t\t\t\t\t\t\t   char distributionMethod,\n\t\t\t\t\t\t\t\t   char *colocateWithTableName,\n\t\t\t\t\t\t\t\t   int shardCount,\n\t\t\t\t\t\t\t\t   bool shardCountIsStrict)\n{\n\t/*\n\t * We disallow create_distributed_table_concurrently in transaction blocks\n\t * because we cannot handle preceding writes, and we block writes at the\n\t * very end of the operation so the transaction should end immediately after.\n\t */\n\tPreventInTransactionBlock(true, \"create_distributed_table_concurrently\");\n\n\t/*\n\t * do not allow multiple create_distributed_table_concurrently in the same\n\t * transaction. We should do that check just here because concurrent local table\n\t * conversion can cause issues.\n\t */\n\tErrorIfMultipleNonblockingMoveSplitInTheSameTransaction();\n\n\t/* do not allow concurrent CreateDistributedTableConcurrently operations */\n\tAcquireCreateDistributedTableConcurrentlyLock(relationId);\n\n\tif (distributionMethod != DISTRIBUTE_BY_HASH)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"only hash-distributed tables can be distributed \"\n\t\t\t\t\t\t\t   \"without blocking writes\")));\n\t}\n\n\tif (ShardReplicationFactor > 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot distribute a table concurrently when \"\n\t\t\t\t\t\t\t   \"citus.shard_replication_factor > 1\")));\n\t}\n\n\tDropOrphanedResourcesInSeparateTransaction();\n\n\tEnsureCitusTableCanBeCreated(relationId);\n\n\tEnsureValidDistributionColumn(relationId, distributionColumnName);\n\n\t/*\n\t * Ensure table type is valid to be distributed. It should be either regular or citus local table.\n\t */\n\tEnsureDistributableTable(relationId);\n\n\t/*\n\t * we rely on citus_add_local_table_to_metadata, so it can generate irrelevant messages.\n\t * we want to error with a user friendly message if foreign keys are not supported.\n\t * We can miss foreign key violations because we are not holding locks, so relation\n\t * can be modified until we acquire the lock for the relation, but we do as much as we can\n\t * to be user friendly on foreign key violation messages.\n\t */\n\n\tEnsureForeignKeysForDistributedTableConcurrently(relationId);\n\n\tchar replicationModel = DecideDistTableReplicationModel(distributionMethod,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolocateWithTableName);\n\n\t/*\n\t * we fail transaction before local table conversion if the table could not be colocated with\n\t * given table. We should make those checks after local table conversion by acquiring locks to\n\t * the relation because the distribution column can be modified in that period.\n\t */\n\tif (!IsColocateWithDefault(colocateWithTableName) && !IsColocateWithNone(\n\t\t\tcolocateWithTableName))\n\t{\n\t\tif (replicationModel != REPLICATION_MODEL_STREAMING)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot create distributed table \"\n\t\t\t\t\t\t\t\t   \"concurrently because Citus allows \"\n\t\t\t\t\t\t\t\t   \"concurrent table distribution only when \"\n\t\t\t\t\t\t\t\t   \"citus.shard_replication_factor = 1\"),\n\t\t\t\t\t\t\terrhint(\"table %s is requested to be colocated \"\n\t\t\t\t\t\t\t\t\t\"with %s which has \"\n\t\t\t\t\t\t\t\t\t\"citus.shard_replication_factor > 1\",\n\t\t\t\t\t\t\t\t\tget_rel_name(relationId),\n\t\t\t\t\t\t\t\t\tcolocateWithTableName)));\n\t\t}\n\n\t\tEnsureColocateWithTableIsValid(relationId, distributionMethod,\n\t\t\t\t\t\t\t\t\t   distributionColumnName,\n\t\t\t\t\t\t\t\t\t   colocateWithTableName);\n\t}\n\n\t/*\n\t * Get name of the table before possibly replacing it in\n\t * citus_add_local_table_to_metadata.\n\t */\n\tchar *tableName = get_rel_name(relationId);\n\tOid schemaId = get_rel_namespace(relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\tRangeVar *rangeVar = makeRangeVar(schemaName, tableName, -1);\n\n\t/* If table is a regular table, then we need to add it into metadata. */\n\tif (!IsCitusTable(relationId))\n\t{\n\t\t/*\n\t\t * Before taking locks, convert the table into a Citus local table and commit\n\t\t * to allow shard split to see the shard.\n\t\t */\n\t\tSendAddLocalTableToMetadataCommandOutsideTransaction(relationId);\n\t}\n\n\t/*\n\t * Lock target relation with a shard update exclusive lock to\n\t * block DDL, but not writes.\n\t *\n\t * If there was a concurrent drop/rename, error out by setting missingOK = false.\n\t */\n\tbool missingOK = false;\n\trelationId = RangeVarGetRelid(rangeVar, ShareUpdateExclusiveLock, missingOK);\n\n\tif (PartitionedTableNoLock(relationId))\n\t{\n\t\t/* also lock partitions */\n\t\tLockPartitionRelations(relationId, ShareUpdateExclusiveLock);\n\t}\n\n\tWarnIfTableHaveNoReplicaIdentity(relationId);\n\n\tList *shardList = LoadShardIntervalList(relationId);\n\n\t/*\n\t * It's technically possible for the table to have been concurrently\n\t * distributed just after citus_add_local_table_to_metadata and just\n\t * before acquiring the lock, so double check.\n\t */\n\tif (list_length(shardList) != 1 ||\n\t\t!IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"table was concurrently modified\")));\n\t}\n\n\t/*\n\t * The table currently has one shard, we will split that shard to match the\n\t * target distribution.\n\t */\n\tShardInterval *shardToSplit = (ShardInterval *) linitial(shardList);\n\n\tPropagatePrerequisiteObjectsForDistributedTable(relationId);\n\n\t/*\n\t * we should re-evaluate distribution column values. It may have changed,\n\t * because we did not lock the relation at the previous check before local\n\t * table conversion.\n\t */\n\tVar *distributionColumn = BuildDistributionKeyFromColumnName(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t distributionColumnName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t NoLock);\n\n\t/* get an advisory lock to serialize concurrent default group creations */\n\tif (IsColocateWithDefault(colocateWithTableName))\n\t{\n\t\tAcquireColocationDefaultLock();\n\t}\n\n\t/*\n\t * At this stage, we only want to check for an existing co-location group.\n\t * We cannot create a new co-location group until after replication slot\n\t * creation in NonBlockingShardSplit.\n\t */\n\tuint32 colocationId = FindColocateWithColocationId(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   replicationModel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   distributionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   shardCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   shardCountIsStrict,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   colocateWithTableName);\n\n\tif (IsColocateWithDefault(colocateWithTableName) && (colocationId !=\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t INVALID_COLOCATION_ID))\n\t{\n\t\t/*\n\t\t * we can release advisory lock if there is already a default entry for given params;\n\t\t * else, we should keep it to prevent different default coloc entry creation by\n\t\t * concurrent operations.\n\t\t */\n\t\tReleaseColocationDefaultLock();\n\t}\n\n\tEnsureRelationCanBeDistributed(relationId, distributionColumn, distributionMethod,\n\t\t\t\t\t\t\t\t   colocationId, replicationModel);\n\n\tOid colocatedTableId = InvalidOid;\n\tif (colocationId != INVALID_COLOCATION_ID)\n\t{\n\t\tcolocatedTableId = ColocatedTableId(colocationId);\n\t}\n\n\tList *workerNodeList = DistributedTablePlacementNodeList(NoLock);\n\tif (workerNodeList == NIL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"no worker nodes are available for placing shards\"),\n\t\t\t\t\t\terrhint(\"Add more worker nodes.\")));\n\t}\n\n\tList *workersForPlacementList;\n\tList *shardSplitPointsList;\n\n\tif (colocatedTableId != InvalidOid)\n\t{\n\t\tList *colocatedShardList = LoadShardIntervalList(colocatedTableId);\n\n\t\t/*\n\t\t * Match the shard ranges of an existing table.\n\t\t */\n\t\tshardSplitPointsList = HashSplitPointsForShardList(colocatedShardList);\n\n\t\t/*\n\t\t * Find the node IDs of the shard placements.\n\t\t */\n\t\tworkersForPlacementList = WorkerNodesForShardList(colocatedShardList);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * Generate a new set of #shardCount shards.\n\t\t */\n\t\tshardSplitPointsList = HashSplitPointsForShardCount(shardCount);\n\n\t\t/*\n\t\t * Place shards in a round-robin fashion across all data nodes.\n\t\t */\n\t\tworkersForPlacementList = RoundRobinWorkerNodeList(workerNodeList, shardCount);\n\t}\n\n\t/*\n\t * Make sure that existing reference tables have been replicated to all the nodes\n\t * such that we can create foreign keys and joins work immediately after creation.\n\t * We do this after applying all essential checks to error out early in case of\n\t * user error.\n\t *\n\t * Use force_logical since this function is meant to not block writes.\n\t */\n\tEnsureReferenceTablesExistOnAllNodesExtended(TRANSFER_MODE_FORCE_LOGICAL);\n\n\t/*\n\t * At this point, the table is a Citus local table, which means it does\n\t * not have a partition column in the metadata. However, we cannot update\n\t * the metadata here because that would prevent us from creating a replication\n\t * slot to copy ongoing changes. Instead, we pass a hash that maps relation\n\t * IDs to partition column vars.\n\t */\n\tDistributionColumnMap *distributionColumnOverrides = CreateDistributionColumnMap();\n\tAddDistributionColumnForRelation(distributionColumnOverrides, relationId,\n\t\t\t\t\t\t\t\t\t distributionColumnName);\n\n\t/*\n\t * there is no colocation entries yet for local table, so we should\n\t * check if table has any partition and add them to same colocation\n\t * group\n\t */\n\tList *sourceColocatedShardIntervalList = ListShardsUnderParentRelation(relationId);\n\n\tSplitMode splitMode = NON_BLOCKING_SPLIT;\n\tSplitOperation splitOperation = CREATE_DISTRIBUTED_TABLE;\n\tSplitShard(\n\t\tsplitMode,\n\t\tsplitOperation,\n\t\tshardToSplit->shardId,\n\t\tshardSplitPointsList,\n\t\tworkersForPlacementList,\n\t\tdistributionColumnOverrides,\n\t\tsourceColocatedShardIntervalList,\n\t\tcolocationId\n\t\t);\n}\n\n\n/*\n * EnsureForeignKeysForDistributedTableConcurrently ensures that referenced and referencing foreign\n * keys for the given table are supported.\n *\n * We allow distributed -> reference\n *          distributed -> citus local\n *\n * We disallow reference   -> distributed\n *             citus local -> distributed\n *             regular     -> distributed\n *\n * Normally regular\t\t-> distributed is allowed but it is not allowed when we create the\n * distributed table concurrently because we rely on conversion of regular table to citus local table,\n * which errors with an unfriendly message.\n */\nstatic void\nEnsureForeignKeysForDistributedTableConcurrently(Oid relationId)\n{\n\t/*\n\t * disallow citus local -> distributed fkeys.\n\t * disallow reference   -> distributed fkeys.\n\t * disallow regular     -> distributed fkeys.\n\t */\n\tEnsureNoFKeyFromTableType(relationId, INCLUDE_CITUS_LOCAL_TABLES |\n\t\t\t\t\t\t\t  INCLUDE_REFERENCE_TABLES | INCLUDE_LOCAL_TABLES);\n\n\t/*\n\t * disallow distributed -> regular fkeys.\n\t */\n\tEnsureNoFKeyToTableType(relationId, INCLUDE_LOCAL_TABLES);\n}\n\n\n/*\n * EnsureColocateWithTableIsValid ensures given relation can be colocated with the table of given name.\n */\nstatic void\nEnsureColocateWithTableIsValid(Oid relationId, char distributionMethod,\n\t\t\t\t\t\t\t   char *distributionColumnName, char *colocateWithTableName)\n{\n\tchar replicationModel = DecideDistTableReplicationModel(distributionMethod,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolocateWithTableName);\n\n\ttext *colocateWithTableNameText = cstring_to_text(colocateWithTableName);\n\tOid colocateWithTableId = ResolveRelationId(colocateWithTableNameText, false);\n\n\tVar *distributionColumn = BuildDistributionKeyFromColumnName(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t distributionColumnName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t NoLock);\n\tEnsureTableCanBeColocatedWith(relationId, replicationModel,\n\t\t\t\t\t\t\t\t  distributionColumn, colocateWithTableId);\n}\n\n\n/*\n * AcquireCreateDistributedTableConcurrentlyLock does not allow concurrent create_distributed_table_concurrently\n * operations.\n */\nvoid\nAcquireCreateDistributedTableConcurrentlyLock(Oid relationId)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = true;\n\n\tSET_LOCKTAG_CITUS_OPERATION(tag, CITUS_CREATE_DISTRIBUTED_TABLE_CONCURRENTLY);\n\n\tLockAcquireResult lockAcquired = LockAcquire(&tag, ExclusiveLock, sessionLock,\n\t\t\t\t\t\t\t\t\t\t\t\t dontWait);\n\tif (!lockAcquired)\n\t{\n\t\tereport(ERROR, (errmsg(\"another create_distributed_table_concurrently \"\n\t\t\t\t\t\t\t   \"operation is in progress\"),\n\t\t\t\t\t\terrhint(\"Make sure that the concurrent operation has \"\n\t\t\t\t\t\t\t\t\"finished and re-run the command\")));\n\t}\n}\n\n\n/*\n * SendAddLocalTableToMetadataCommandOutsideTransaction executes metadata add local\n * table command locally to avoid deadlock.\n */\nstatic void\nSendAddLocalTableToMetadataCommandOutsideTransaction(Oid relationId)\n{\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\n\t/*\n\t * we need to allow nested distributed execution, because we start a new distributed\n\t * execution inside the pushed-down UDF citus_add_local_table_to_metadata. Normally\n\t * citus does not allow that because it cannot guarantee correctness.\n\t */\n\tStringInfo allowNestedDistributionCommand = makeStringInfo();\n\tappendStringInfo(allowNestedDistributionCommand,\n\t\t\t\t\t \"SET LOCAL citus.allow_nested_distributed_execution to ON\");\n\n\tStringInfo addLocalTableToMetadataCommand = makeStringInfo();\n\tappendStringInfo(addLocalTableToMetadataCommand,\n\t\t\t\t\t \"SELECT pg_catalog.citus_add_local_table_to_metadata(%s)\",\n\t\t\t\t\t quote_literal_cstr(qualifiedRelationName));\n\n\tList *commands = list_make2(allowNestedDistributionCommand->data,\n\t\t\t\t\t\t\t\taddLocalTableToMetadataCommand->data);\n\tchar *username = NULL;\n\tSendCommandListToWorkerOutsideTransaction(LocalHostName, PostPortNumber, username,\n\t\t\t\t\t\t\t\t\t\t\t  commands);\n}\n\n\n/*\n * WarnIfTableHaveNoReplicaIdentity notices user if the given table or its partitions (if any)\n * do not have a replica identity which is required for logical replication to replicate\n * UPDATE and DELETE commands during create_distributed_table_concurrently.\n */\nvoid\nWarnIfTableHaveNoReplicaIdentity(Oid relationId)\n{\n\tbool foundRelationWithNoReplicaIdentity = false;\n\n\t/*\n\t * Check for source relation's partitions if any. We do not need to check for the source relation\n\t * because we can replicate partitioned table even if it does not have replica identity.\n\t * Source table will have no data if it has partitions.\n\t */\n\tif (PartitionedTable(relationId))\n\t{\n\t\tList *partitionList = PartitionList(relationId);\n\t\tListCell *partitionCell = NULL;\n\n\t\tforeach(partitionCell, partitionList)\n\t\t{\n\t\t\tOid partitionTableId = lfirst_oid(partitionCell);\n\n\t\t\tif (!RelationCanPublishAllModifications(partitionTableId))\n\t\t\t{\n\t\t\t\tfoundRelationWithNoReplicaIdentity = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t/* check for source relation if it is not partitioned */\n\telse\n\t{\n\t\tif (!RelationCanPublishAllModifications(relationId))\n\t\t{\n\t\t\tfoundRelationWithNoReplicaIdentity = true;\n\t\t}\n\t}\n\n\tif (foundRelationWithNoReplicaIdentity)\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tereport(NOTICE, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t errmsg(\"relation %s does not have a REPLICA \"\n\t\t\t\t\t\t\t\t\"IDENTITY or PRIMARY KEY\", relationName),\n\t\t\t\t\t\t errdetail(\"UPDATE and DELETE commands on the relation will \"\n\t\t\t\t\t\t\t\t   \"error out during create_distributed_table_concurrently unless \"\n\t\t\t\t\t\t\t\t   \"there is a REPLICA IDENTITY or PRIMARY KEY. \"\n\t\t\t\t\t\t\t\t   \"INSERT commands will still work.\")));\n\t}\n}\n\n\n/*\n * HashSplitPointsForShardList returns a list of split points which match\n * the shard ranges of the given list of shards;\n */\nstatic List *\nHashSplitPointsForShardList(List *shardList)\n{\n\tList *splitPointList = NIL;\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardList)\n\t{\n\t\tint32 shardMaxValue = DatumGetInt32(shardInterval->maxValue);\n\n\t\tsplitPointList = lappend_int(splitPointList, shardMaxValue);\n\t}\n\n\t/*\n\t * Split point lists only include the upper boundaries.\n\t */\n\tsplitPointList = list_delete_last(splitPointList);\n\n\treturn splitPointList;\n}\n\n\n/*\n * HashSplitPointsForShardCount returns a list of split points for a given\n * shard count with roughly equal hash ranges.\n */\nstatic List *\nHashSplitPointsForShardCount(int shardCount)\n{\n\tList *splitPointList = NIL;\n\n\t/* calculate the split of the hash space */\n\tuint64 hashTokenIncrement = HASH_TOKEN_COUNT / shardCount;\n\n\t/*\n\t * Split points lists only include the upper boundaries, so we only\n\t * go up to shardCount - 1 and do not have to apply the correction\n\t * for the last shardmaxvalue.\n\t */\n\tfor (int64 shardIndex = 0; shardIndex < shardCount - 1; shardIndex++)\n\t{\n\t\t/* initialize the hash token space for this shard */\n\t\tint32 shardMinValue = PG_INT32_MIN + (shardIndex * hashTokenIncrement);\n\t\tint32 shardMaxValue = shardMinValue + (hashTokenIncrement - 1);\n\n\t\tsplitPointList = lappend_int(splitPointList, shardMaxValue);\n\t}\n\n\treturn splitPointList;\n}\n\n\n/*\n * WorkerNodesForShardList returns a list of node ids reflecting the locations of\n * the given list of shards.\n */\nstatic List *\nWorkerNodesForShardList(List *shardList)\n{\n\tList *nodeIdList = NIL;\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardList)\n\t{\n\t\tWorkerNode *workerNode = ActiveShardPlacementWorkerNode(shardInterval->shardId);\n\t\tnodeIdList = lappend_int(nodeIdList, workerNode->nodeId);\n\t}\n\n\treturn nodeIdList;\n}\n\n\n/*\n * RoundRobinWorkerNodeList round robins over the workers in the worker node list\n * and adds node ids to a list of length listLength.\n */\nstatic List *\nRoundRobinWorkerNodeList(List *workerNodeList, int listLength)\n{\n\tAssert(workerNodeList != NIL);\n\n\tList *nodeIdList = NIL;\n\n\tfor (int idx = 0; idx < listLength; idx++)\n\t{\n\t\tint nodeIdx = idx % list_length(workerNodeList);\n\t\tWorkerNode *workerNode = (WorkerNode *) list_nth(workerNodeList, nodeIdx);\n\t\tnodeIdList = lappend_int(nodeIdList, workerNode->nodeId);\n\t}\n\n\treturn nodeIdList;\n}\n\n\n/*\n * create_reference_table creates a distributed table with the given relationId. The\n * created table has one shard and replication factor is set to the active worker\n * count. In fact, the above is the definition of a reference table in Citus.\n */\nDatum\ncreate_reference_table(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tOid relationId = PG_GETARG_OID(0);\n\n\tCreateReferenceTable(relationId);\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * EnsureCitusTableCanBeCreated checks if\n * - we are on the coordinator\n * - the current user is the owner of the table\n * - relation kind is supported\n * - relation is not a shard\n */\nstatic void\nEnsureCitusTableCanBeCreated(Oid relationOid)\n{\n\tEnsureCoordinator();\n\tEnsureRelationExists(relationOid);\n\tEnsureTableOwner(relationOid);\n\tErrorIfTemporaryTable(relationOid);\n\tErrorIfForeignTable(relationOid);\n\n\t/*\n\t * We should do this check here since the codes in the following lines rely\n\t * on this relation to have a supported relation kind. More extensive checks\n\t * will be performed in CreateDistributedTable.\n\t */\n\tEnsureRelationKindSupported(relationOid);\n\n\t/*\n\t * When coordinator is added to the metadata, or on the workers,\n\t * some of the relations of the coordinator node may/will be shards.\n\t * We disallow creating distributed tables from shard relations, by\n\t * erroring out here.\n\t */\n\tErrorIfRelationIsAKnownShard(relationOid);\n}\n\n\n/*\n * EnsureRelationExists does a basic check on whether the OID belongs to\n * an existing relation.\n */\nvoid\nEnsureRelationExists(Oid relationId)\n{\n\tif (!RelationExists(relationId))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"relation with OID %d does not exist\",\n\t\t\t\t\t\t\t   relationId)));\n\t}\n}\n\n\n/*\n * CreateReferenceTable is a wrapper around CreateCitusTable that creates a\n * distributed table.\n */\nvoid\nCreateDistributedTable(Oid relationId, char *distributionColumnName,\n\t\t\t\t\t   char distributionMethod,\n\t\t\t\t\t   int shardCount, bool shardCountIsStrict,\n\t\t\t\t\t   char *colocateWithTableName)\n{\n\tCitusTableType tableType;\n\tswitch (distributionMethod)\n\t{\n\t\tcase DISTRIBUTE_BY_HASH:\n\t\t{\n\t\t\ttableType = HASH_DISTRIBUTED;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase DISTRIBUTE_BY_APPEND:\n\t\t{\n\t\t\ttableType = APPEND_DISTRIBUTED;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase DISTRIBUTE_BY_RANGE:\n\t\t{\n\t\t\ttableType = RANGE_DISTRIBUTED;\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unexpected distribution method when \"\n\t\t\t\t\t\t\t\t   \"deciding Citus table type\")));\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tDistributedTableParams distributedTableParams = {\n\t\t.colocationParam = {\n\t\t\t.colocateWithTableName = colocateWithTableName,\n\t\t\t.colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT\n\t\t},\n\t\t.shardCount = shardCount,\n\t\t.shardCountIsStrict = shardCountIsStrict,\n\t\t.distributionColumnName = distributionColumnName\n\t};\n\tCreateCitusTable(relationId, tableType, &distributedTableParams);\n}\n\n\n/*\n * CreateReferenceTable creates a reference table.\n */\nvoid\nCreateReferenceTable(Oid relationId)\n{\n\tif (IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\t/*\n\t\t * Create the shard of given Citus local table on workers to convert\n\t\t * it into a reference table.\n\t\t */\n\t\tConvertCitusLocalTableToTableType(relationId, REFERENCE_TABLE, NULL);\n\t}\n\telse\n\t{\n\t\tCreateCitusTable(relationId, REFERENCE_TABLE, NULL);\n\t}\n}\n\n\n/*\n * CreateSingleShardTable creates a single shard distributed table that\n * doesn't have a shard key.\n */\nvoid\nCreateSingleShardTable(Oid relationId, ColocationParam colocationParam)\n{\n\tDistributedTableParams distributedTableParams = {\n\t\t.colocationParam = colocationParam,\n\t\t.shardCount = 1,\n\t\t.shardCountIsStrict = true,\n\t\t.distributionColumnName = NULL\n\t};\n\n\tif (IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\t/*\n\t\t * Create the shard of given Citus local table on appropriate node\n\t\t * and drop the local one to convert it into a single-shard distributed\n\t\t * table.\n\t\t */\n\t\tConvertCitusLocalTableToTableType(relationId, SINGLE_SHARD_DISTRIBUTED,\n\t\t\t\t\t\t\t\t\t\t  &distributedTableParams);\n\t}\n\telse\n\t{\n\t\tCreateCitusTable(relationId, SINGLE_SHARD_DISTRIBUTED, &distributedTableParams);\n\t}\n}\n\n\n/*\n * CreateCitusTable is the internal method that creates a Citus table in\n * given configuration.\n *\n * DistributedTableParams should be non-null only if we're creating a distributed\n * table.\n *\n * This functions contains all necessary logic to create distributed tables. It\n * performs necessary checks to ensure distributing the table is safe. If it is\n * safe to distribute the table, this function creates distributed table metadata,\n * creates shards and copies local data to shards. This function also handles\n * partitioned tables by distributing its partitions as well.\n */\nstatic void\nCreateCitusTable(Oid relationId, CitusTableType tableType,\n\t\t\t\t DistributedTableParams *distributedTableParams)\n{\n\tif ((tableType == HASH_DISTRIBUTED || tableType == APPEND_DISTRIBUTED ||\n\t\t tableType == SINGLE_SHARD_DISTRIBUTED ||\n\t\t tableType == RANGE_DISTRIBUTED) != (distributedTableParams != NULL))\n\t{\n\t\tereport(ERROR, (errmsg(\"distributed table params must be provided \"\n\t\t\t\t\t\t\t   \"when creating a distributed table and must \"\n\t\t\t\t\t\t\t   \"not be otherwise\")));\n\t}\n\n\tEnsureCitusTableCanBeCreated(relationId);\n\n\t/* allow creating a Citus table on an empty cluster */\n\tInsertCoordinatorIfClusterEmpty();\n\n\tRelation relation = try_relation_open(relationId, ExclusiveLock);\n\tif (relation == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not create Citus table: \"\n\t\t\t\t\t\t\t   \"relation does not exist\")));\n\t}\n\n\trelation_close(relation, NoLock);\n\n\tif (tableType == SINGLE_SHARD_DISTRIBUTED && ShardReplicationFactor > 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not create single shard table: \"\n\t\t\t\t\t\t\t   \"citus.shard_replication_factor is greater than 1\"),\n\t\t\t\t\t\terrhint(\"Consider setting citus.shard_replication_factor to 1 \"\n\t\t\t\t\t\t\t\t\"and try again\")));\n\t}\n\n\t/*\n\t * EnsureTableNotDistributed errors out when relation is a citus table but\n\t * we don't want to ask user to first undistribute their citus local tables\n\t * when creating distributed tables from them.\n\t * For this reason, here we undistribute citus local tables beforehand.\n\t * But since UndistributeTable does not support undistributing relations\n\t * involved in foreign key relationships, we first drop foreign keys that\n\t * given relation is involved, then we undistribute the relation and finally\n\t * we re-create dropped foreign keys at the end of this function.\n\t */\n\tList *originalForeignKeyRecreationCommands = NIL;\n\tif (IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\t/*\n\t\t * We use ConvertCitusLocalTableToTableType instead of CreateCitusTable\n\t\t * to create a reference table or a single-shard table from a Citus\n\t\t * local table.\n\t\t */\n\t\tAssert(tableType != REFERENCE_TABLE && tableType != SINGLE_SHARD_DISTRIBUTED);\n\n\t\t/* store foreign key creation commands that relation is involved */\n\t\toriginalForeignKeyRecreationCommands =\n\t\t\tGetFKeyCreationCommandsRelationInvolvedWithTableType(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t INCLUDE_ALL_TABLE_TYPES);\n\t\trelationId = DropFKeysAndUndistributeTable(relationId);\n\t}\n\t/*\n\t * To support foreign keys between reference tables and local tables,\n\t * we drop & re-define foreign keys at the end of this function so\n\t * that ALTER TABLE hook does the necessary job, which means converting\n\t * local tables to citus local tables to properly support such foreign\n\t * keys.\n\t */\n\telse if (tableType == REFERENCE_TABLE &&\n\t\t\t ShouldEnableLocalReferenceForeignKeys() &&\n\t\t\t HasForeignKeyWithLocalTable(relationId))\n\t{\n\t\t/*\n\t\t * Store foreign key creation commands for foreign key relationships\n\t\t * that relation has with postgres tables.\n\t\t */\n\t\toriginalForeignKeyRecreationCommands =\n\t\t\tGetFKeyCreationCommandsRelationInvolvedWithTableType(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t INCLUDE_LOCAL_TABLES);\n\n\t\t/*\n\t\t * Soon we will convert local tables to citus local tables. As\n\t\t * CreateCitusLocalTable needs to use local execution, now we\n\t\t * switch to local execution beforehand so that reference table\n\t\t * creation doesn't use remote execution and we don't error out\n\t\t * in CreateCitusLocalTable.\n\t\t */\n\t\tSetLocalExecutionStatus(LOCAL_EXECUTION_REQUIRED);\n\n\t\tDropFKeysRelationInvolvedWithTableType(relationId, INCLUDE_LOCAL_TABLES);\n\t}\n\n\tLockRelationOid(relationId, ExclusiveLock);\n\n\tEnsureTableNotDistributed(relationId);\n\n\tPropagatePrerequisiteObjectsForDistributedTable(relationId);\n\n\tVar *distributionColumn = NULL;\n\tif (distributedTableParams && distributedTableParams->distributionColumnName)\n\t{\n\t\tdistributionColumn = BuildDistributionKeyFromColumnName(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdistributedTableParams->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdistributionColumnName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNoLock);\n\t}\n\n\tCitusTableParams citusTableParams = DecideCitusTableParams(tableType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   distributedTableParams);\n\n\t/*\n\t * ColocationIdForNewTable assumes caller acquires lock on relationId. In our case,\n\t * our caller already acquired lock on relationId.\n\t */\n\tuint32 colocationId = INVALID_COLOCATION_ID;\n\tif (distributedTableParams &&\n\t\tdistributedTableParams->colocationParam.colocationParamType ==\n\t\tCOLOCATE_WITH_COLOCATION_ID)\n\t{\n\t\tcolocationId = distributedTableParams->colocationParam.colocationId;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * ColocationIdForNewTable assumes caller acquires lock on relationId. In our case,\n\t\t * our caller already acquired lock on relationId.\n\t\t */\n\t\tcolocationId = ColocationIdForNewTable(relationId, tableType,\n\t\t\t\t\t\t\t\t\t\t\t   distributedTableParams,\n\t\t\t\t\t\t\t\t\t\t\t   distributionColumn);\n\t}\n\n\tEnsureRelationCanBeDistributed(relationId, distributionColumn,\n\t\t\t\t\t\t\t\t   citusTableParams.distributionMethod,\n\t\t\t\t\t\t\t\t   colocationId, citusTableParams.replicationModel);\n\n\t/*\n\t * Make sure that existing reference tables have been replicated to all the nodes\n\t * such that we can create foreign keys and joins work immediately after creation.\n\t *\n\t * This will take a lock on the nodes to make sure no nodes are added after we have\n\t * verified and ensured the reference tables are copied everywhere.\n\t * Although copying reference tables here for anything but creating a new colocation\n\t * group, it requires significant refactoring which we don't want to perform now.\n\t */\n\tEnsureReferenceTablesExistOnAllNodes();\n\n\t/*\n\t * While adding tables to a colocation group we need to make sure no concurrent\n\t * mutations happen on the colocation group with regards to its placements. It is\n\t * important that we have already copied any reference tables before acquiring this\n\t * lock as these are competing operations.\n\t */\n\tLockColocationId(colocationId, ShareLock);\n\n\t/* we need to calculate these variables before creating distributed metadata */\n\tbool localTableEmpty = TableEmpty(relationId);\n\tOid colocatedTableId = ColocatedTableId(colocationId);\n\n\t/* setting to false since this flag is only valid for citus local tables */\n\tbool autoConverted = false;\n\n\t/* create an entry for distributed table in pg_dist_partition */\n\tInsertIntoPgDistPartition(relationId, citusTableParams.distributionMethod,\n\t\t\t\t\t\t\t  distributionColumn,\n\t\t\t\t\t\t\t  colocationId, citusTableParams.replicationModel,\n\t\t\t\t\t\t\t  autoConverted);\n\n\t/*\n\t * PG16+ supports truncate triggers on foreign tables\n\t */\n\tif (RegularTable(relationId) || IsForeignTable(relationId))\n\t{\n\t\tCreateTruncateTrigger(relationId);\n\t}\n\n\tif (tableType == HASH_DISTRIBUTED)\n\t{\n\t\t/* create shards for hash distributed table */\n\t\tCreateHashDistributedTableShards(relationId, distributedTableParams->shardCount,\n\t\t\t\t\t\t\t\t\t\t colocatedTableId,\n\t\t\t\t\t\t\t\t\t\t localTableEmpty);\n\t}\n\telse if (tableType == REFERENCE_TABLE)\n\t{\n\t\t/* create shards for reference table */\n\t\tCreateReferenceTableShard(relationId);\n\t}\n\telse if (tableType == SINGLE_SHARD_DISTRIBUTED)\n\t{\n\t\t/* create the shard of given single-shard distributed table */\n\t\tCreateSingleShardTableShard(relationId, colocatedTableId,\n\t\t\t\t\t\t\t\t\tcolocationId);\n\t}\n\n\tif (ShouldSyncTableMetadata(relationId))\n\t{\n\t\tSyncCitusTableMetadata(relationId);\n\t}\n\n\t/*\n\t * We've a custom way of foreign key graph invalidation,\n\t * see InvalidateForeignKeyGraph().\n\t */\n\tif (TableReferenced(relationId) || TableReferencing(relationId))\n\t{\n\t\tInvalidateForeignKeyGraph();\n\t}\n\n\t/* if this table is partitioned table, distribute its partitions too */\n\tif (PartitionedTable(relationId))\n\t{\n\t\tList *partitionList = PartitionList(relationId);\n\t\tOid partitionRelationId = InvalidOid;\n\t\tchar *parentRelationName = generate_qualified_relation_name(relationId);\n\n\t\t/*\n\t\t * when there are many partitions, each call to CreateDistributedTable\n\t\t * accumulates used memory. Create and free context for each call.\n\t\t */\n\t\tMemoryContext citusPartitionContext =\n\t\t\tAllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t  \"citus_per_partition_context\",\n\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_SIZES);\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(citusPartitionContext);\n\n\t\tforeach_declared_oid(partitionRelationId, partitionList)\n\t\t{\n\t\t\tMemoryContextReset(citusPartitionContext);\n\n\t\t\tDistributedTableParams childDistributedTableParams = {\n\t\t\t\t.colocationParam = {\n\t\t\t\t\t.colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT,\n\t\t\t\t\t.colocateWithTableName = parentRelationName,\n\t\t\t\t},\n\t\t\t\t.shardCount = distributedTableParams->shardCount,\n\t\t\t\t.shardCountIsStrict = false,\n\t\t\t\t.distributionColumnName = distributedTableParams->distributionColumnName,\n\t\t\t};\n\t\t\tCreateCitusTable(partitionRelationId, tableType,\n\t\t\t\t\t\t\t &childDistributedTableParams);\n\t\t}\n\n\t\tMemoryContextSwitchTo(oldContext);\n\t\tMemoryContextDelete(citusPartitionContext);\n\t}\n\n\t/* copy over data for hash distributed and reference tables */\n\tif (tableType == HASH_DISTRIBUTED || tableType == SINGLE_SHARD_DISTRIBUTED ||\n\t\ttableType == REFERENCE_TABLE)\n\t{\n\t\tif (RegularTable(relationId))\n\t\t{\n\t\t\tCopyLocalDataIntoShards(relationId);\n\t\t}\n\t}\n\n\t/*\n\t * Now recreate foreign keys that we dropped beforehand. As modifications are not\n\t * allowed on the relations that are involved in the foreign key relationship,\n\t * we can skip the validation of the foreign keys.\n\t */\n\tbool skip_validation = true;\n\tExecuteForeignKeyCreateCommandList(originalForeignKeyRecreationCommands,\n\t\t\t\t\t\t\t\t\t   skip_validation);\n}\n\n\n/*\n * ConvertCitusLocalTableToTableType converts given Citus local table to\n * given table type.\n *\n * This only supports converting Citus local tables to reference tables\n * (by replicating the shard to workers) and single-shard distributed\n * tables (by replicating the shard to the appropriate worker and dropping\n * the local one).\n */\nstatic void\nConvertCitusLocalTableToTableType(Oid relationId, CitusTableType tableType,\n\t\t\t\t\t\t\t\t  DistributedTableParams *distributedTableParams)\n{\n\tif (!IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\tereport(ERROR, (errmsg(\"table is not a local table added to metadata\")));\n\t}\n\n\tif (tableType != REFERENCE_TABLE && tableType != SINGLE_SHARD_DISTRIBUTED)\n\t{\n\t\tereport(ERROR, (errmsg(\"table type is not supported for conversion\")));\n\t}\n\n\tif ((tableType == SINGLE_SHARD_DISTRIBUTED) != (distributedTableParams != NULL))\n\t{\n\t\tereport(ERROR, (errmsg(\"distributed table params must be provided \"\n\t\t\t\t\t\t\t   \"when creating a distributed table and must \"\n\t\t\t\t\t\t\t   \"not be otherwise\")));\n\t}\n\n\tEnsureCitusTableCanBeCreated(relationId);\n\n\tRelation relation = try_relation_open(relationId, ExclusiveLock);\n\tif (relation == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not create Citus table: \"\n\t\t\t\t\t\t\t   \"relation does not exist\")));\n\t}\n\n\trelation_close(relation, NoLock);\n\n\tif (tableType == SINGLE_SHARD_DISTRIBUTED && ShardReplicationFactor > 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not create single shard table: \"\n\t\t\t\t\t\t\t   \"citus.shard_replication_factor is greater than 1\"),\n\t\t\t\t\t\terrhint(\"Consider setting citus.shard_replication_factor to 1 \"\n\t\t\t\t\t\t\t\t\"and try again\")));\n\t}\n\n\tLockRelationOid(relationId, ExclusiveLock);\n\n\tVar *distributionColumn = NULL;\n\tCitusTableParams citusTableParams = DecideCitusTableParams(tableType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   distributedTableParams);\n\n\tuint32 colocationId = INVALID_COLOCATION_ID;\n\tif (distributedTableParams &&\n\t\tdistributedTableParams->colocationParam.colocationParamType ==\n\t\tCOLOCATE_WITH_COLOCATION_ID)\n\t{\n\t\tcolocationId = distributedTableParams->colocationParam.colocationId;\n\t}\n\telse\n\t{\n\t\tcolocationId = ColocationIdForNewTable(relationId, tableType,\n\t\t\t\t\t\t\t\t\t\t\t   distributedTableParams,\n\t\t\t\t\t\t\t\t\t\t\t   distributionColumn);\n\t}\n\n\t/* check constraints etc. on table based on new distribution params */\n\tEnsureRelationCanBeDistributed(relationId, distributionColumn,\n\t\t\t\t\t\t\t\t   citusTableParams.distributionMethod,\n\t\t\t\t\t\t\t\t   colocationId, citusTableParams.replicationModel);\n\n\t/*\n\t * Regarding the foreign key relationships that given relation is involved,\n\t * EnsureRelationCanBeDistributed() only checks the ones where the relation\n\t * is the referencing table. And given that the table at hand is a Citus\n\t * local table, right now it may only be referenced by a reference table\n\t * or a Citus local table. However, given that neither of those two cases\n\t * are not applicable for a distributed table, here we throw an error if\n\t * that's the case.\n\t *\n\t * Note that we don't need to check the same if we're creating a reference\n\t * table from a Citus local table because all the foreign keys referencing\n\t * Citus local tables are supported by reference tables.\n\t */\n\tif (tableType == SINGLE_SHARD_DISTRIBUTED)\n\t{\n\t\tEnsureNoFKeyFromTableType(relationId, INCLUDE_CITUS_LOCAL_TABLES |\n\t\t\t\t\t\t\t\t  INCLUDE_REFERENCE_TABLES);\n\t}\n\n\tEnsureReferenceTablesExistOnAllNodes();\n\n\tLockColocationId(colocationId, ShareLock);\n\n\t/*\n\t * When converting to a single shard table, we want to drop the placement\n\t * on the coordinator, but only if transferring to a different node. In that\n\t * case, shouldDropLocalPlacement is true. When converting to a reference\n\t * table, we always keep the placement on the coordinator, so for reference\n\t * tables shouldDropLocalPlacement is always false.\n\t */\n\tbool shouldDropLocalPlacement = false;\n\n\tList *targetNodeList = NIL;\n\tif (tableType == SINGLE_SHARD_DISTRIBUTED)\n\t{\n\t\tuint32 targetNodeId = SingleShardTableColocationNodeId(colocationId);\n\t\tif (targetNodeId != CoordinatorNodeIfAddedAsWorkerOrError()->nodeId)\n\t\t{\n\t\t\tbool missingOk = false;\n\t\t\tWorkerNode *targetNode = FindNodeWithNodeId(targetNodeId, missingOk);\n\t\t\ttargetNodeList = list_make1(targetNode);\n\n\t\t\tshouldDropLocalPlacement = true;\n\t\t}\n\t}\n\telse if (tableType == REFERENCE_TABLE)\n\t{\n\t\ttargetNodeList = ActivePrimaryNonCoordinatorNodeList(ShareLock);\n\t\ttargetNodeList = SortList(targetNodeList, CompareWorkerNodes);\n\t}\n\n\tbool autoConverted = false;\n\tUpdateNoneDistTableMetadataGlobally(\n\t\trelationId, citusTableParams.replicationModel,\n\t\tcolocationId, autoConverted);\n\n\t/* create the shard placement on workers and insert into pg_dist_placement globally */\n\tif (list_length(targetNodeList) > 0)\n\t{\n\t\tNoneDistTableReplicateCoordinatorPlacement(relationId, targetNodeList);\n\t}\n\n\tif (shouldDropLocalPlacement)\n\t{\n\t\t/*\n\t\t * We don't yet drop the local placement before handling partitions.\n\t\t * Otherewise, local shard placements of the partitions will be gone\n\t\t * before we create them on workers.\n\t\t *\n\t\t * However, we need to delete the related entry from pg_dist_placement\n\t\t * before distributing partitions (if any) because we need a sane metadata\n\t\t * state before doing so.\n\t\t */\n\t\tNoneDistTableDeleteCoordinatorPlacement(relationId);\n\t}\n\n\t/* if this table is partitioned table, distribute its partitions too */\n\tif (PartitionedTable(relationId))\n\t{\n\t\t/* right now we don't allow partitioned reference tables */\n\t\tAssert(tableType == SINGLE_SHARD_DISTRIBUTED);\n\n\t\tList *partitionList = PartitionList(relationId);\n\n\t\tchar *parentRelationName = generate_qualified_relation_name(relationId);\n\n\t\t/*\n\t\t * When there are many partitions, each call to\n\t\t * ConvertCitusLocalTableToTableType accumulates used memory.\n\t\t * Create and free citus_per_partition_context for each call.\n\t\t */\n\t\tMemoryContext citusPartitionContext =\n\t\t\tAllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t  \"citus_per_partition_context\",\n\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_SIZES);\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(citusPartitionContext);\n\n\t\tOid partitionRelationId = InvalidOid;\n\t\tforeach_declared_oid(partitionRelationId, partitionList)\n\t\t{\n\t\t\tMemoryContextReset(citusPartitionContext);\n\n\t\t\tDistributedTableParams childDistributedTableParams = {\n\t\t\t\t.colocationParam = {\n\t\t\t\t\t.colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT,\n\t\t\t\t\t.colocateWithTableName = parentRelationName,\n\t\t\t\t},\n\t\t\t\t.shardCount = distributedTableParams->shardCount,\n\t\t\t\t.shardCountIsStrict = false,\n\t\t\t\t.distributionColumnName = distributedTableParams->distributionColumnName,\n\t\t\t};\n\t\t\tConvertCitusLocalTableToTableType(partitionRelationId, tableType,\n\t\t\t\t\t\t\t\t\t\t\t  &childDistributedTableParams);\n\t\t}\n\n\t\tMemoryContextSwitchTo(oldContext);\n\t\tMemoryContextDelete(citusPartitionContext);\n\t}\n\n\tif (shouldDropLocalPlacement)\n\t{\n\t\tNoneDistTableDropCoordinatorPlacementTable(relationId);\n\t}\n}\n\n\n/*\n * DecideCitusTableParams decides CitusTableParams based on given CitusTableType\n * and DistributedTableParams if it's a distributed table.\n *\n * DistributedTableParams should be non-null only if CitusTableType corresponds\n * to a distributed table.\n */\nstatic\nCitusTableParams\nDecideCitusTableParams(CitusTableType tableType,\n\t\t\t\t\t   DistributedTableParams *distributedTableParams)\n{\n\tCitusTableParams citusTableParams = { 0 };\n\tswitch (tableType)\n\t{\n\t\tcase HASH_DISTRIBUTED:\n\t\t{\n\t\t\tAssert(distributedTableParams->colocationParam.colocationParamType ==\n\t\t\t\t   COLOCATE_WITH_TABLE_LIKE_OPT);\n\n\t\t\tcitusTableParams.distributionMethod = DISTRIBUTE_BY_HASH;\n\t\t\tcitusTableParams.replicationModel =\n\t\t\t\tDecideDistTableReplicationModel(DISTRIBUTE_BY_HASH,\n\t\t\t\t\t\t\t\t\t\t\t\tdistributedTableParams->colocationParam.\n\t\t\t\t\t\t\t\t\t\t\t\tcolocateWithTableName);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase APPEND_DISTRIBUTED:\n\t\t{\n\t\t\tAssert(distributedTableParams->colocationParam.colocationParamType ==\n\t\t\t\t   COLOCATE_WITH_TABLE_LIKE_OPT);\n\n\t\t\tcitusTableParams.distributionMethod = DISTRIBUTE_BY_APPEND;\n\t\t\tcitusTableParams.replicationModel =\n\t\t\t\tDecideDistTableReplicationModel(APPEND_DISTRIBUTED,\n\t\t\t\t\t\t\t\t\t\t\t\tdistributedTableParams->colocationParam.\n\t\t\t\t\t\t\t\t\t\t\t\tcolocateWithTableName);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase RANGE_DISTRIBUTED:\n\t\t{\n\t\t\tAssert(distributedTableParams->colocationParam.colocationParamType ==\n\t\t\t\t   COLOCATE_WITH_TABLE_LIKE_OPT);\n\n\t\t\tcitusTableParams.distributionMethod = DISTRIBUTE_BY_RANGE;\n\t\t\tcitusTableParams.replicationModel =\n\t\t\t\tDecideDistTableReplicationModel(RANGE_DISTRIBUTED,\n\t\t\t\t\t\t\t\t\t\t\t\tdistributedTableParams->colocationParam.\n\t\t\t\t\t\t\t\t\t\t\t\tcolocateWithTableName);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase SINGLE_SHARD_DISTRIBUTED:\n\t\t{\n\t\t\tcitusTableParams.distributionMethod = DISTRIBUTE_BY_NONE;\n\t\t\tcitusTableParams.replicationModel = REPLICATION_MODEL_STREAMING;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase REFERENCE_TABLE:\n\t\t{\n\t\t\tcitusTableParams.distributionMethod = DISTRIBUTE_BY_NONE;\n\t\t\tcitusTableParams.replicationModel = REPLICATION_MODEL_2PC;\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unexpected table type when deciding Citus \"\n\t\t\t\t\t\t\t\t   \"table params\")));\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn citusTableParams;\n}\n\n\n/*\n * PropagatePrerequisiteObjectsForDistributedTable ensures we can create shards\n * on all nodes by ensuring all dependent objects exist on all node.\n */\nstatic void\nPropagatePrerequisiteObjectsForDistributedTable(Oid relationId)\n{\n\t/*\n\t * Ensure that the sequences used in column defaults of the table\n\t * have proper types\n\t */\n\tEnsureRelationHasCompatibleSequenceTypes(relationId);\n\n\t/*\n\t * distributed tables might have dependencies on different objects, since we create\n\t * shards for a distributed table via multiple sessions these objects will be created\n\t * via their own connection and committed immediately so they become visible to all\n\t * sessions creating shards.\n\t */\n\tObjectAddress *tableAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*tableAddress, RelationRelationId, relationId);\n\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(tableAddress));\n\tTrackPropagatedTableAndSequences(relationId);\n}\n\n\n/*\n * EnsureSequenceTypeSupported ensures that the type of the column that uses\n * a sequence on its DEFAULT is consistent with previous uses (if any) of the\n * sequence in distributed tables.\n * If any other distributed table uses the input sequence, it checks whether\n * the types of the columns using the sequence match. If they don't, it errors out.\n * Otherwise, the condition is ensured.\n * Since the owner of the sequence may not distributed yet, it should be added\n * explicitly.\n */\nvoid\nEnsureSequenceTypeSupported(Oid seqOid, Oid attributeTypeId, Oid ownerRelationId)\n{\n\tOid attrDefOid;\n\tList *attrDefOids = GetAttrDefsFromSequence(seqOid);\n\n\tforeach_declared_oid(attrDefOid, attrDefOids)\n\t{\n\t\tObjectAddress columnAddress = GetAttrDefaultColumnAddress(attrDefOid);\n\n\t\t/*\n\t\t * If another distributed table is using the same sequence\n\t\t * in one of its column defaults, make sure the types of the\n\t\t * columns match.\n\t\t *\n\t\t * We skip non-distributed tables, but we need to check the current\n\t\t * table as it might reference the same sequence multiple times.\n\t\t */\n\t\tif (columnAddress.objectId != ownerRelationId &&\n\t\t\t!IsCitusTable(columnAddress.objectId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tOid currentAttributeTypId = GetAttributeTypeOid(columnAddress.objectId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolumnAddress.objectSubId);\n\t\tif (attributeTypeId != currentAttributeTypId)\n\t\t{\n\t\t\tchar *sequenceName = generate_qualified_relation_name(\n\t\t\t\tseqOid);\n\t\t\tchar *citusTableName =\n\t\t\t\tgenerate_qualified_relation_name(columnAddress.objectId);\n\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\"The sequence %s is already used for a different\"\n\t\t\t\t\t\t\t\t\" type in column %d of the table %s\",\n\t\t\t\t\t\t\t\tsequenceName, columnAddress.objectSubId,\n\t\t\t\t\t\t\t\tcitusTableName)));\n\t\t}\n\t}\n}\n\n\n/*\n * AlterSequenceType alters the given sequence's type to the given type.\n */\nvoid\nAlterSequenceType(Oid seqOid, Oid typeOid)\n{\n\tForm_pg_sequence sequenceData = pg_get_sequencedef(seqOid);\n\tOid currentSequenceTypeOid = sequenceData->seqtypid;\n\tif (currentSequenceTypeOid != typeOid)\n\t{\n\t\tAlterSeqStmt *alterSequenceStatement = makeNode(AlterSeqStmt);\n\t\tchar *seqNamespace = get_namespace_name(get_rel_namespace(seqOid));\n\t\tchar *seqName = get_rel_name(seqOid);\n\t\talterSequenceStatement->sequence = makeRangeVar(seqNamespace, seqName, -1);\n\t\tNode *asTypeNode = (Node *) makeTypeNameFromOid(typeOid, -1);\n\t\tSetDefElemArg(alterSequenceStatement, \"as\", asTypeNode);\n\t\tParseState *pstate = make_parsestate(NULL);\n\t\tAlterSequence(pstate, alterSequenceStatement);\n\t\tCommandCounterIncrement();\n\t}\n}\n\n\n/*\n * EnsureRelationHasCompatibleSequenceTypes ensures that sequences used for columns\n * of the table have compatible types both with the column type on that table and\n * all other distributed tables' columns they have used for\n */\nvoid\nEnsureRelationHasCompatibleSequenceTypes(Oid relationId)\n{\n\tList *seqInfoList = NIL;\n\n\tGetDependentSequencesWithRelation(relationId, &seqInfoList, 0, DEPENDENCY_AUTO);\n\tEnsureDistributedSequencesHaveOneType(relationId, seqInfoList);\n}\n\n\n/*\n * EnsureDistributedSequencesHaveOneType first ensures that the type of the column\n * in which the sequence is used as default is supported for each sequence in input\n * dependentSequenceList, and then alters the sequence type if not the same with the column type.\n */\nstatic void\nEnsureDistributedSequencesHaveOneType(Oid relationId, List *seqInfoList)\n{\n\tSequenceInfo *seqInfo = NULL;\n\tforeach_declared_ptr(seqInfo, seqInfoList)\n\t{\n\t\tif (!seqInfo->isNextValDefault)\n\t\t{\n\t\t\t/*\n\t\t\t * If a sequence is not on the nextval, we don't need any check.\n\t\t\t * This is a dependent sequence via ALTER SEQUENCE .. OWNED BY col\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * We should make sure that the type of the column that uses\n\t\t * that sequence is supported\n\t\t */\n\t\tOid sequenceOid = seqInfo->sequenceOid;\n\t\tAttrNumber attnum = seqInfo->attributeNumber;\n\t\tOid attributeTypeId = GetAttributeTypeOid(relationId, attnum);\n\t\tEnsureSequenceTypeSupported(sequenceOid, attributeTypeId, relationId);\n\n\t\t/*\n\t\t * Alter the sequence's data type in the coordinator if needed.\n\t\t *\n\t\t * First, we should only change the sequence type if the column\n\t\t * is a supported sequence type. For example, if a sequence is used\n\t\t * in an expression which then becomes a text, we should not try to\n\t\t * alter the sequence type to text. Postgres only supports int2, int4\n\t\t * and int8 as the sequence type.\n\t\t *\n\t\t * A sequence's type is bigint by default and it doesn't change even if\n\t\t * it's used in an int column. We should change the type if needed,\n\t\t * and not allow future ALTER SEQUENCE ... TYPE ... commands for\n\t\t * sequences used as defaults in distributed tables.\n\t\t */\n\t\tif (attributeTypeId == INT2OID ||\n\t\t\tattributeTypeId == INT4OID ||\n\t\t\tattributeTypeId == INT8OID)\n\t\t{\n\t\t\tAlterSequenceType(sequenceOid, attributeTypeId);\n\t\t}\n\t}\n}\n\n\n/*\n * DecideDistTableReplicationModel function decides which replication model should be\n * used for a distributed table depending on given distribution configuration.\n */\nstatic char\nDecideDistTableReplicationModel(char distributionMethod, char *colocateWithTableName)\n{\n\tAssert(distributionMethod != DISTRIBUTE_BY_NONE);\n\n\tif (!IsColocateWithDefault(colocateWithTableName) &&\n\t\t!IsColocateWithNone(colocateWithTableName))\n\t{\n\t\ttext *colocateWithTableNameText = cstring_to_text(colocateWithTableName);\n\t\tOid colocatedRelationId = ResolveRelationId(colocateWithTableNameText, false);\n\t\tCitusTableCacheEntry *targetTableEntry = GetCitusTableCacheEntry(\n\t\t\tcolocatedRelationId);\n\t\tchar replicationModel = targetTableEntry->replicationModel;\n\n\t\treturn replicationModel;\n\t}\n\telse if (distributionMethod == DISTRIBUTE_BY_HASH &&\n\t\t\t !DistributedTableReplicationIsEnabled())\n\t{\n\t\treturn REPLICATION_MODEL_STREAMING;\n\t}\n\telse\n\t{\n\t\treturn REPLICATION_MODEL_COORDINATOR;\n\t}\n\n\t/* we should not reach to this point */\n\treturn REPLICATION_MODEL_INVALID;\n}\n\n\n/*\n * CreateHashDistributedTableShards creates shards of given hash distributed table.\n */\nstatic void\nCreateHashDistributedTableShards(Oid relationId, int shardCount,\n\t\t\t\t\t\t\t\t Oid colocatedTableId, bool localTableEmpty)\n{\n\tbool useExclusiveConnection = false;\n\n\t/*\n\t * Decide whether to use exclusive connections per placement or not. Note that\n\t * if the local table is not empty, we cannot use sequential mode since the COPY\n\t * operation that'd load the data into shards currently requires exclusive\n\t * connections.\n\t */\n\tif (RegularTable(relationId))\n\t{\n\t\tuseExclusiveConnection = CanUseExclusiveConnections(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlocalTableEmpty);\n\t}\n\n\tif (colocatedTableId != InvalidOid)\n\t{\n\t\t/*\n\t\t * We currently allow concurrent distribution of colocated tables (which\n\t\t * we probably should not be allowing because of foreign keys /\n\t\t * partitioning etc).\n\t\t *\n\t\t * We also prevent concurrent shard moves / copy / splits) while creating\n\t\t * a colocated table.\n\t\t */\n\t\tAcquirePlacementColocationLock(colocatedTableId, ShareLock,\n\t\t\t\t\t\t\t\t\t   \"colocate distributed table\");\n\n\t\tCreateColocatedShards(relationId, colocatedTableId, useExclusiveConnection);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * This path is only reached by create_distributed_table for the distributed\n\t\t * tables which will not be part of an existing colocation group. Therefore,\n\t\t * we can directly use ShardReplicationFactor global variable here.\n\t\t */\n\t\tCreateShardsWithRoundRobinPolicy(relationId, shardCount, ShardReplicationFactor,\n\t\t\t\t\t\t\t\t\t\t useExclusiveConnection);\n\t}\n}\n\n\n/*\n * CreateSingleShardTableShard creates the shard of given single-shard\n * distributed table.\n */\nstatic void\nCreateSingleShardTableShard(Oid relationId, Oid colocatedTableId,\n\t\t\t\t\t\t\tuint32 colocationId)\n{\n\tif (colocatedTableId != InvalidOid)\n\t{\n\t\t/*\n\t\t * We currently allow concurrent distribution of colocated tables (which\n\t\t * we probably should not be allowing because of foreign keys /\n\t\t * partitioning etc).\n\t\t *\n\t\t * We also prevent concurrent shard moves / copy / splits) while creating\n\t\t * a colocated table.\n\t\t */\n\t\tAcquirePlacementColocationLock(colocatedTableId, ShareLock,\n\t\t\t\t\t\t\t\t\t   \"colocate distributed table\");\n\n\t\t/*\n\t\t * We don't need to force using exclusive connections because we're anyway\n\t\t * creating a single shard.\n\t\t */\n\t\tbool useExclusiveConnection = false;\n\t\tCreateColocatedShards(relationId, colocatedTableId, useExclusiveConnection);\n\t}\n\telse\n\t{\n\t\tCreateSingleShardTableShardWithRoundRobinPolicy(relationId, colocationId);\n\t}\n}\n\n\n/*\n * ColocationIdForNewTable returns a colocation id for given table\n * according to given configuration. If there is no such configuration, it\n * creates one and returns colocation id of newly the created colocation group.\n * Note that DistributedTableParams and the distribution column Var should be\n * non-null only if CitusTableType corresponds to a distributed table.\n *\n * For append and range distributed tables, this function errors out if\n * colocateWithTableName parameter is not NULL, otherwise directly returns\n * INVALID_COLOCATION_ID.\n *\n * For reference tables, returns the common reference table colocation id.\n *\n * This function assumes its caller take necessary lock on relationId to\n * prevent possible changes on it.\n */\nstatic uint32\nColocationIdForNewTable(Oid relationId, CitusTableType tableType,\n\t\t\t\t\t\tDistributedTableParams *distributedTableParams,\n\t\t\t\t\t\tVar *distributionColumn)\n{\n\tCitusTableParams citusTableParams = DecideCitusTableParams(tableType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   distributedTableParams);\n\n\tuint32 colocationId = INVALID_COLOCATION_ID;\n\n\tif (tableType == APPEND_DISTRIBUTED || tableType == RANGE_DISTRIBUTED)\n\t{\n\t\tAssert(distributedTableParams->colocationParam.colocationParamType ==\n\t\t\t   COLOCATE_WITH_TABLE_LIKE_OPT);\n\t\tchar *colocateWithTableName =\n\t\t\tdistributedTableParams->colocationParam.colocateWithTableName;\n\t\tif (!IsColocateWithDefault(colocateWithTableName))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot distribute relation\"),\n\t\t\t\t\t\t\terrdetail(\"Currently, colocate_with option is not supported \"\n\t\t\t\t\t\t\t\t\t  \"for append / range distributed tables.\")));\n\t\t}\n\n\t\treturn colocationId;\n\t}\n\telse if (tableType == REFERENCE_TABLE)\n\t{\n\t\treturn CreateReferenceTableColocationId();\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * Get an exclusive lock on the colocation system catalog. Therefore, we\n\t\t * can be sure that there will no modifications on the colocation table\n\t\t * until this transaction is committed.\n\t\t */\n\n\t\t/* distributionColumn can only be null for single-shard tables */\n\t\tOid distributionColumnType =\n\t\t\tdistributionColumn ? distributionColumn->vartype : InvalidOid;\n\t\tOid distributionColumnCollation =\n\t\t\tdistributionColumn ? distributionColumn->varcollid : InvalidOid;\n\n\t\tAssert(distributedTableParams->colocationParam.colocationParamType ==\n\t\t\t   COLOCATE_WITH_TABLE_LIKE_OPT);\n\t\tchar *colocateWithTableName =\n\t\t\tdistributedTableParams->colocationParam.colocateWithTableName;\n\n\t\t/* get an advisory lock to serialize concurrent default group creations */\n\t\tif (IsColocateWithDefault(colocateWithTableName))\n\t\t{\n\t\t\tAcquireColocationDefaultLock();\n\t\t}\n\n\t\tcolocationId = FindColocateWithColocationId(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcitusTableParams.replicationModel,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdistributionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdistributedTableParams->shardCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdistributedTableParams->\n\t\t\t\t\t\t\t\t\t\t\t\t\tshardCountIsStrict,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolocateWithTableName);\n\n\t\tif (IsColocateWithDefault(colocateWithTableName) &&\n\t\t\t(colocationId != INVALID_COLOCATION_ID))\n\t\t{\n\t\t\t/*\n\t\t\t * we can release advisory lock if there is already a default entry for given params;\n\t\t\t * else, we should keep it to prevent different default coloc entry creation by\n\t\t\t * concurrent operations.\n\t\t\t */\n\t\t\tReleaseColocationDefaultLock();\n\t\t}\n\n\t\tif (colocationId == INVALID_COLOCATION_ID)\n\t\t{\n\t\t\tif (IsColocateWithDefault(colocateWithTableName))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Generate a new colocation ID and insert a pg_dist_colocation\n\t\t\t\t * record.\n\t\t\t\t */\n\t\t\t\tcolocationId = CreateColocationGroup(distributedTableParams->shardCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t ShardReplicationFactor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t distributionColumnType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t distributionColumnCollation);\n\t\t\t}\n\t\t\telse if (IsColocateWithNone(colocateWithTableName))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Generate a new colocation ID and insert a pg_dist_colocation\n\t\t\t\t * record.\n\t\t\t\t */\n\t\t\t\tcolocationId = CreateColocationGroup(distributedTableParams->shardCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t ShardReplicationFactor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t distributionColumnType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t distributionColumnCollation);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn colocationId;\n}\n\n\n/*\n * EnsureRelationCanBeDistributed checks whether Citus can safely distribute given\n * relation with the given configuration. We perform almost all safety checks for\n * distributing table here. If there is an unsatisfied requirement, we error out\n * and do not distribute the table.\n *\n * This function assumes, callers have already acquired necessary locks to ensure\n * there will not be any change in the given relation.\n */\nstatic void\nEnsureRelationCanBeDistributed(Oid relationId, Var *distributionColumn,\n\t\t\t\t\t\t\t   char distributionMethod, uint32 colocationId,\n\t\t\t\t\t\t\t   char replicationModel)\n{\n\tOid parentRelationId = InvalidOid;\n\n\tEnsureLocalTableEmptyIfNecessary(relationId, distributionMethod);\n\n\t/* user really wants triggers? */\n\tif (EnableUnsafeTriggers)\n\t{\n\t\tErrorIfRelationHasUnsupportedTrigger(relationId);\n\t}\n\telse\n\t{\n\t\tEnsureRelationHasNoTriggers(relationId);\n\t}\n\n\t/* we assume callers took necessary locks */\n\tRelation relation = relation_open(relationId, NoLock);\n\tTupleDesc relationDesc = RelationGetDescr(relation);\n\tchar *relationName = RelationGetRelationName(relation);\n\n\tErrorIfTableIsACatalogTable(relation);\n\n\n\t/* verify target relation is not distributed by a generated stored column\n\t */\n\tif (distributionMethod != DISTRIBUTE_BY_NONE)\n\t{\n\t\tDistributionColumnIsGeneratedCheck(relationDesc, distributionColumn,\n\t\t\t\t\t\t\t\t\t\t   relationName);\n\t}\n\n\t/* verify target relation is not distributed by a column of type numeric with negative scale */\n\tif (distributionMethod != DISTRIBUTE_BY_NONE &&\n\t\tDistributionColumnUsesNumericColumnNegativeScale(relationDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t distributionColumn))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot distribute relation: %s\", relationName),\n\t\t\t\t\t\terrdetail(\"Distribution column must not use numeric type \"\n\t\t\t\t\t\t\t\t  \"with negative scale\")));\n\t}\n\n\t/* check for support function needed by specified partition method */\n\tif (distributionMethod == DISTRIBUTE_BY_HASH)\n\t{\n\t\tOid hashSupportFunction = SupportFunctionForColumn(distributionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   HASH_AM_OID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   HASHSTANDARD_PROC);\n\t\tif (hashSupportFunction == InvalidOid)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION),\n\t\t\t\t\t\t\terrmsg(\"could not identify a hash function for type %s\",\n\t\t\t\t\t\t\t\t   format_type_be(distributionColumn->vartype)),\n\t\t\t\t\t\t\terrdatatype(distributionColumn->vartype),\n\t\t\t\t\t\t\terrdetail(\"Partition column types must have a hash function \"\n\t\t\t\t\t\t\t\t\t  \"defined to use hash partitioning.\")));\n\t\t}\n\n\t\tif (distributionColumn->varcollid != InvalidOid &&\n\t\t\t!get_collation_isdeterministic(distributionColumn->varcollid))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"Hash distributed partition columns may not use \"\n\t\t\t\t\t\t\t\t   \"a non deterministic collation\")));\n\t\t}\n\t}\n\telse if (distributionMethod == DISTRIBUTE_BY_RANGE)\n\t{\n\t\tOid btreeSupportFunction = SupportFunctionForColumn(distributionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tBTREE_AM_OID, BTORDER_PROC);\n\t\tif (btreeSupportFunction == InvalidOid)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_UNDEFINED_FUNCTION),\n\t\t\t\t\t errmsg(\"could not identify a comparison function for type %s\",\n\t\t\t\t\t\t\tformat_type_be(distributionColumn->vartype)),\n\t\t\t\t\t errdatatype(distributionColumn->vartype),\n\t\t\t\t\t errdetail(\"Partition column types must have a comparison function \"\n\t\t\t\t\t\t\t   \"defined to use range partitioning.\")));\n\t\t}\n\t}\n\n\tif (PartitionTableNoLock(relationId))\n\t{\n\t\tparentRelationId = PartitionParentOid(relationId);\n\t}\n\n\t/* partitions cannot be distributed if their parent is not distributed */\n\tif (PartitionTableNoLock(relationId) && !IsCitusTable(parentRelationId))\n\t{\n\t\tchar *parentRelationName = get_rel_name(parentRelationId);\n\n\t\tereport(ERROR, (errmsg(\"cannot distribute relation \\\"%s\\\" which is partition of \"\n\t\t\t\t\t\t\t   \"\\\"%s\\\"\", relationName, parentRelationName),\n\t\t\t\t\t\terrdetail(\"Citus does not support distributing partitions \"\n\t\t\t\t\t\t\t\t  \"if their parent is not distributed table.\"),\n\t\t\t\t\t\terrhint(\"Distribute the partitioned table \\\"%s\\\" instead.\",\n\t\t\t\t\t\t\t\tparentRelationName)));\n\t}\n\n\t/*\n\t * These checks are mostly for partitioned tables not partitions because we prevent\n\t * distributing partitions directly in the above check. However, partitions can still\n\t * reach this point because, we call CreateDistributedTable for partitions if their\n\t * parent table is distributed.\n\t */\n\tif (PartitionedTableNoLock(relationId))\n\t{\n\t\t/*\n\t\t * Distributing partitioned tables is only supported for hash-distribution\n\t\t * or single-shard tables.\n\t\t */\n\t\tbool isSingleShardTable =\n\t\t\tdistributionMethod == DISTRIBUTE_BY_NONE &&\n\t\t\treplicationModel == REPLICATION_MODEL_STREAMING &&\n\t\t\tcolocationId != INVALID_COLOCATION_ID;\n\t\tif (distributionMethod != DISTRIBUTE_BY_HASH && !isSingleShardTable)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"distributing partitioned tables in only supported \"\n\t\t\t\t\t\t\t\t   \"for hash-distributed tables\")));\n\t\t}\n\n\t\t/* we don't support distributing tables with multi-level partitioning */\n\t\tif (PartitionTableNoLock(relationId))\n\t\t{\n\t\t\tchar *parentRelationName = get_rel_name(parentRelationId);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"distributing multi-level partitioned tables \"\n\t\t\t\t\t\t\t\t   \"is not supported\"),\n\t\t\t\t\t\t\terrdetail(\"Relation \\\"%s\\\" is partitioned table itself and \"\n\t\t\t\t\t\t\t\t\t  \"it is also partition of relation \\\"%s\\\".\",\n\t\t\t\t\t\t\t\t\t  relationName, parentRelationName)));\n\t\t}\n\t}\n\n\tErrorIfUnsupportedConstraint(relation, distributionMethod, replicationModel,\n\t\t\t\t\t\t\t\t distributionColumn, colocationId);\n\n\n\tErrorIfUnsupportedPolicy(relation);\n\trelation_close(relation, NoLock);\n}\n\n\n/*\n * ErrorIfTemporaryTable errors out if the given table is a temporary table.\n */\nstatic void\nErrorIfTemporaryTable(Oid relationId)\n{\n\tif (get_rel_persistence(relationId) == RELPERSISTENCE_TEMP)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot distribute a temporary table\")));\n\t}\n}\n\n\n/*\n * ErrorIfTableIsACatalogTable is a helper function to error out for citus\n * table creation from a catalog table.\n */\nvoid\nErrorIfTableIsACatalogTable(Relation relation)\n{\n\tif (relation->rd_rel->relnamespace != PG_CATALOG_NAMESPACE)\n\t{\n\t\treturn;\n\t}\n\n\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\terrmsg(\"cannot create a citus table from a catalog table\")));\n}\n\n\n/*\n * EnsureLocalTableEmptyIfNecessary errors out if the function should be empty\n * according to ShouldLocalTableBeEmpty but it is not.\n */\nstatic void\nEnsureLocalTableEmptyIfNecessary(Oid relationId, char distributionMethod)\n{\n\tif (ShouldLocalTableBeEmpty(relationId, distributionMethod))\n\t{\n\t\tEnsureLocalTableEmpty(relationId);\n\t}\n}\n\n\n/*\n * ShouldLocalTableBeEmpty returns true if the local table should be empty\n * before creating a citus table.\n * In some cases, it is possible and safe to send local data to shards while\n * distributing the table. In those cases, we can distribute non-empty local\n * tables. This function checks the distributionMethod and relation kind to\n * see whether we need to be ensure emptiness of local table.\n */\nstatic bool\nShouldLocalTableBeEmpty(Oid relationId, char distributionMethod)\n{\n\tbool shouldLocalTableBeEmpty = false;\n\tif (distributionMethod != DISTRIBUTE_BY_HASH &&\n\t\tdistributionMethod != DISTRIBUTE_BY_NONE)\n\t{\n\t\t/*\n\t\t * We only support hash distributed tables and reference tables\n\t\t * for initial data loading\n\t\t */\n\t\tshouldLocalTableBeEmpty = true;\n\t}\n\telse if (!RegularTable(relationId))\n\t{\n\t\t/*\n\t\t * We only support tables and partitioned tables for initial\n\t\t * data loading\n\t\t */\n\t\tshouldLocalTableBeEmpty = true;\n\t}\n\n\treturn shouldLocalTableBeEmpty;\n}\n\n\n/*\n * EnsureLocalTableEmpty errors out if the local table is not empty.\n */\nstatic void\nEnsureLocalTableEmpty(Oid relationId)\n{\n\tchar *relationName = get_rel_name(relationId);\n\tbool localTableEmpty = TableEmpty(relationId);\n\n\tif (!localTableEmpty)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION),\n\t\t\t\t\t\terrmsg(\"cannot distribute relation \\\"%s\\\"\", relationName),\n\t\t\t\t\t\terrdetail(\"Relation \\\"%s\\\" contains data.\", relationName),\n\t\t\t\t\t\terrhint(\"Empty your table before distributing it.\")));\n\t}\n}\n\n\n/*\n * EnsureDistributableTable ensures the given table type is appropriate to\n * be distributed. Table type should be regular or citus local table.\n */\nstatic void\nEnsureDistributableTable(Oid relationId)\n{\n\tbool isLocalTable = IsCitusTableType(relationId, CITUS_LOCAL_TABLE);\n\tbool isRegularTable = !IsCitusTableType(relationId, ANY_CITUS_TABLE_TYPE);\n\n\tif (!isLocalTable && !isRegularTable)\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION),\n\t\t\t\t\t\terrmsg(\"table \\\"%s\\\" is already distributed\",\n\t\t\t\t\t\t\t   relationName)));\n\t}\n}\n\n\n/*\n * EnsureTableNotDistributed errors out if the table is distributed.\n */\nvoid\nEnsureTableNotDistributed(Oid relationId)\n{\n\tchar *relationName = get_rel_name(relationId);\n\n\tbool isCitusTable = IsCitusTable(relationId);\n\n\tif (isCitusTable)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION),\n\t\t\t\t\t\terrmsg(\"table \\\"%s\\\" is already distributed\",\n\t\t\t\t\t\t\t   relationName)));\n\t}\n}\n\n\n/*\n * EnsureRelationHasNoTriggers errors out if the given table has triggers on\n * it. See also GetExplicitTriggerIdList function's comment for the triggers this\n * function errors out.\n */\nstatic void\nEnsureRelationHasNoTriggers(Oid relationId)\n{\n\tList *explicitTriggerIds = GetExplicitTriggerIdList(relationId);\n\n\tif (list_length(explicitTriggerIds) > 0)\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tAssert(relationName != NULL);\n\t\tereport(ERROR, (errmsg(\"cannot distribute relation \\\"%s\\\" because it \"\n\t\t\t\t\t\t\t   \"has triggers\", relationName),\n\t\t\t\t\t\terrhint(\"Consider dropping all the triggers on \\\"%s\\\" \"\n\t\t\t\t\t\t\t\t\"and retry.\", relationName)));\n\t}\n}\n\n\n/*\n * LookupDistributionMethod maps the oids of citus.distribution_type enum\n * values to pg_dist_partition.partmethod values.\n *\n * The passed in oid has to belong to a value of citus.distribution_type.\n */\nchar\nLookupDistributionMethod(Oid distributionMethodOid)\n{\n\tchar distributionMethod = 0;\n\n\tHeapTuple enumTuple = SearchSysCache1(ENUMOID, ObjectIdGetDatum(\n\t\t\t\t\t\t\t\t\t\t\t  distributionMethodOid));\n\tif (!HeapTupleIsValid(enumTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"invalid internal value for enum: %u\",\n\t\t\t\t\t\t\t   distributionMethodOid)));\n\t}\n\n\tForm_pg_enum enumForm = (Form_pg_enum) GETSTRUCT(enumTuple);\n\tconst char *enumLabel = NameStr(enumForm->enumlabel);\n\n\tif (strncmp(enumLabel, \"append\", NAMEDATALEN) == 0)\n\t{\n\t\tdistributionMethod = DISTRIBUTE_BY_APPEND;\n\t}\n\telse if (strncmp(enumLabel, \"hash\", NAMEDATALEN) == 0)\n\t{\n\t\tdistributionMethod = DISTRIBUTE_BY_HASH;\n\t}\n\telse if (strncmp(enumLabel, \"range\", NAMEDATALEN) == 0)\n\t{\n\t\tdistributionMethod = DISTRIBUTE_BY_RANGE;\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"invalid label for enum: %s\", enumLabel)));\n\t}\n\n\tReleaseSysCache(enumTuple);\n\n\treturn distributionMethod;\n}\n\n\n/*\n *\tSupportFunctionForColumn locates a support function given a column, an access method,\n *\tand and id of a support function. This function returns InvalidOid if there is no\n *\tsupport function for the operator class family of the column, but if the data type\n *\tof the column has no default operator class whatsoever, this function errors out.\n */\nstatic Oid\nSupportFunctionForColumn(Var *partitionColumn, Oid accessMethodId,\n\t\t\t\t\t\t int16 supportFunctionNumber)\n{\n\tOid columnOid = partitionColumn->vartype;\n\tOid operatorClassId = GetDefaultOpClass(columnOid, accessMethodId);\n\n\t/* currently only support using the default operator class */\n\tif (operatorClassId == InvalidOid)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t\terrmsg(\"data type %s has no default operator class for specified\"\n\t\t\t\t\t\t\t   \" partition method\", format_type_be(columnOid)),\n\t\t\t\t\t\terrdatatype(columnOid),\n\t\t\t\t\t\terrdetail(\"Partition column types must have a default operator\"\n\t\t\t\t\t\t\t\t  \" class defined.\")));\n\t}\n\n\tOid operatorFamilyId = get_opclass_family(operatorClassId);\n\tOid operatorClassInputType = get_opclass_input_type(operatorClassId);\n\tOid supportFunctionOid = get_opfamily_proc(operatorFamilyId, operatorClassInputType,\n\t\t\t\t\t\t\t\t\t\t\t   operatorClassInputType,\n\t\t\t\t\t\t\t\t\t\t\t   supportFunctionNumber);\n\n\treturn supportFunctionOid;\n}\n\n\n/*\n * TableEmpty function checks whether given table contains any row and\n * returns false if there is any data.\n */\nbool\nTableEmpty(Oid tableId)\n{\n\tOid schemaId = get_rel_namespace(tableId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\tchar *tableName = get_rel_name(tableId);\n\tchar *tableQualifiedName = quote_qualified_identifier(schemaName, tableName);\n\n\tStringInfo selectTrueQueryString = makeStringInfo();\n\n\tbool readOnly = true;\n\n\tint spiConnectionResult = SPI_connect();\n\tif (spiConnectionResult != SPI_OK_CONNECT)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not connect to SPI manager\")));\n\t}\n\n\tappendStringInfo(selectTrueQueryString, SELECT_TRUE_QUERY, tableQualifiedName);\n\n\tint spiQueryResult = SPI_execute(selectTrueQueryString->data, readOnly, 0);\n\tif (spiQueryResult != SPI_OK_SELECT)\n\t{\n\t\tereport(ERROR, (errmsg(\"execution was not successful \\\"%s\\\"\",\n\t\t\t\t\t\t\t   selectTrueQueryString->data)));\n\t}\n\n\t/* we expect that SELECT TRUE query will return single value in a single row OR empty set */\n\tAssert(SPI_processed == 1 || SPI_processed == 0);\n\n\tbool localTableEmpty = !SPI_processed;\n\n\tSPI_finish();\n\n\treturn localTableEmpty;\n}\n\n\n/*\n * CanUseExclusiveConnections checks if we can open parallel connections\n * while creating shards. We simply error out if we need to execute\n * sequentially but there is data in the table, since we cannot copy the\n * data to shards sequentially.\n */\nstatic bool\nCanUseExclusiveConnections(Oid relationId, bool localTableEmpty)\n{\n\tbool hasForeignKeyToReferenceTable = HasForeignKeyToReferenceTable(relationId);\n\tbool shouldRunSequential = MultiShardConnectionType == SEQUENTIAL_CONNECTION ||\n\t\t\t\t\t\t\t   hasForeignKeyToReferenceTable;\n\n\tif (shouldRunSequential && ParallelQueryExecutedInTransaction())\n\t{\n\t\t/*\n\t\t * We decided to use sequential execution. It's either because relation\n\t\t * has a pre-existing foreign key to a reference table or because we\n\t\t * decided to use sequential execution due to a query executed in the\n\t\t * current xact beforehand.\n\t\t * We have specific error messages for either cases.\n\t\t */\n\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tif (hasForeignKeyToReferenceTable)\n\t\t{\n\t\t\t/*\n\t\t\t * If there has already been a parallel query executed, the sequential mode\n\t\t\t * would still use the already opened parallel connections to the workers,\n\t\t\t * thus contradicting our purpose of using sequential mode.\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\"cannot distribute relation \\\"%s\\\" in this \"\n\t\t\t\t\t\t\t\t   \"transaction because it has a foreign key to \"\n\t\t\t\t\t\t\t\t   \"a reference table\", relationName),\n\t\t\t\t\t\t\terrdetail(\"If a hash distributed table has a foreign key \"\n\t\t\t\t\t\t\t\t\t  \"to a reference table, it has to be created \"\n\t\t\t\t\t\t\t\t\t  \"in sequential mode before any parallel commands \"\n\t\t\t\t\t\t\t\t\t  \"have been executed in the same transaction\"),\n\t\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t\t}\n\t\telse if (MultiShardConnectionType == SEQUENTIAL_CONNECTION)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot distribute \\\"%s\\\" in sequential mode because \"\n\t\t\t\t\t\t\t\t   \"a parallel query was executed in this transaction\",\n\t\t\t\t\t\t\t\t   relationName),\n\t\t\t\t\t\t\terrhint(\"If you have manually set \"\n\t\t\t\t\t\t\t\t\t\"citus.multi_shard_modify_mode to 'sequential', \"\n\t\t\t\t\t\t\t\t\t\"try with 'parallel' option. \")));\n\t\t}\n\t}\n\telse if (shouldRunSequential)\n\t{\n\t\treturn false;\n\t}\n\telse if (!localTableEmpty || IsMultiStatementTransaction())\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CreateTruncateTrigger creates a truncate trigger on table identified by relationId\n * and assigns citus_truncate_trigger() as handler.\n */\nvoid\nCreateTruncateTrigger(Oid relationId)\n{\n\tStringInfo triggerName = makeStringInfo();\n\tbool internal = true;\n\n\tappendStringInfo(triggerName, \"truncate_trigger\");\n\n\tCreateTrigStmt *trigger = makeNode(CreateTrigStmt);\n\ttrigger->trigname = triggerName->data;\n\ttrigger->relation = NULL;\n\ttrigger->funcname = SystemFuncName(CITUS_TRUNCATE_TRIGGER_NAME);\n\ttrigger->args = NIL;\n\ttrigger->row = false;\n\ttrigger->timing = TRIGGER_TYPE_AFTER;\n\ttrigger->events = TRIGGER_TYPE_TRUNCATE;\n\ttrigger->columns = NIL;\n\ttrigger->whenClause = NULL;\n\ttrigger->isconstraint = false;\n\n\tCreateTrigger(trigger, NULL, relationId, InvalidOid, InvalidOid, InvalidOid,\n\t\t\t\t  InvalidOid, InvalidOid, NULL,\n\t\t\t\t  internal, false);\n}\n\n\n/*\n * RegularTable function returns true if given table's relation kind is RELKIND_RELATION\n * or RELKIND_PARTITIONED_TABLE otherwise it returns false.\n */\nbool\nRegularTable(Oid relationId)\n{\n\tchar relationKind = get_rel_relkind(relationId);\n\n\tif (relationKind == RELKIND_RELATION || relationKind == RELKIND_PARTITIONED_TABLE)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CopyLocalDataIntoShards is a wrapper around CopyFromLocalTableIntoDistTable\n * to copy data from the local table, which is hidden after converting it to a\n * distributed table, into the shards of the distributed table.\n *\n * After copying local data into the distributed table, the local data remains\n * in place and should be truncated at a later time.\n */\nstatic void\nCopyLocalDataIntoShards(Oid distributedTableId)\n{\n\tuint64 rowsCopied = CopyFromLocalTableIntoDistTable(distributedTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdistributedTableId);\n\tif (rowsCopied > 0)\n\t{\n\t\tchar *qualifiedRelationName =\n\t\t\tgenerate_qualified_relation_name(distributedTableId);\n\t\tereport(NOTICE, (errmsg(\"copying the data has completed\"),\n\t\t\t\t\t\t errdetail(\"The local data in the table is no longer visible, \"\n\t\t\t\t\t\t\t\t   \"but is still on disk.\"),\n\t\t\t\t\t\t errhint(\"To remove the local data, run: SELECT \"\n\t\t\t\t\t\t\t\t \"truncate_local_data_after_distributing_table($$%s$$)\",\n\t\t\t\t\t\t\t\t qualifiedRelationName)));\n\t}\n}\n\n\n/*\n * CopyFromLocalTableIntoDistTable copies data from given local table into\n * the shards of given distributed table.\n *\n * For partitioned tables, this functions returns without copying the data\n * because we call this function for both partitioned tables and its partitions.\n * Returning early saves us from copying data to workers twice.\n *\n * This function uses CitusCopyDestReceiver to invoke the distributed COPY logic.\n * We cannot use a regular COPY here since that cannot read from a table. Instead\n * we read from the table and pass each tuple to the CitusCopyDestReceiver which\n * opens a connection and starts a COPY for each shard placement that will have\n * data.\n *\n * We assume that the local table might indeed be a distributed table and the\n * caller would want to read the local data from the shell table in that case.\n * For this reason, to keep it simple, we perform a heap scan directly on the\n * table instead of using SELECT.\n *\n * We read from the table and pass each tuple to the CitusCopyDestReceiver which\n * opens a connection and starts a COPY for each shard placement that will have\n * data.\n */\nuint64\nCopyFromLocalTableIntoDistTable(Oid localTableId, Oid distributedTableId)\n{\n\t/* take an ExclusiveLock to block all operations except SELECT */\n\tRelation localRelation = table_open(localTableId, ExclusiveLock);\n\n\t/*\n\t * Skip copying from partitioned tables, we will copy the data from\n\t * partition to partition's shards.\n\t */\n\tif (PartitionedTable(distributedTableId))\n\t{\n\t\ttable_close(localRelation, NoLock);\n\n\t\treturn 0;\n\t}\n\n\t/*\n\t * All writes have finished, make sure that we can see them by using the\n\t * latest snapshot. We use GetLatestSnapshot instead of\n\t * GetTransactionSnapshot since the latter would not reveal all writes\n\t * in serializable or repeatable read mode. Note that subsequent reads\n\t * from the distributed table would reveal those writes, temporarily\n\t * violating the isolation level. However, this seems preferable over\n\t * dropping the writes entirely.\n\t */\n\tPushActiveSnapshot(GetLatestSnapshot());\n\n\tRelation distributedRelation = RelationIdGetRelation(distributedTableId);\n\n\t/* get the table columns for distributed table */\n\tTupleDesc destTupleDescriptor = RelationGetDescr(distributedRelation);\n\tList *columnNameList = TupleDescColumnNameList(destTupleDescriptor);\n\n\tRelationClose(distributedRelation);\n\n\tint partitionColumnIndex = INVALID_PARTITION_COLUMN_INDEX;\n\n\t/* determine the partition column in the tuple descriptor */\n\tVar *partitionColumn = PartitionColumn(distributedTableId, 0);\n\tif (partitionColumn != NULL)\n\t{\n\t\tpartitionColumnIndex = partitionColumn->varattno - 1;\n\t}\n\n\t/* create tuple slot for local relation */\n\tTupleDesc sourceTupleDescriptor = RelationGetDescr(localRelation);\n\tTupleTableSlot *slot = table_slot_create(localRelation, NULL);\n\n\t/* initialise per-tuple memory context */\n\tEState *estate = CreateExecutorState();\n\tExprContext *econtext = GetPerTupleExprContext(estate);\n\tecontext->ecxt_scantuple = slot;\n\tconst bool nonPublishableData = false;\n\n\t/* we don't track query counters when distributing a table */\n\tconst bool trackQueryCounters = false;\n\tDestReceiver *copyDest =\n\t\t(DestReceiver *) CreateCitusCopyDestReceiver(distributedTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t columnNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t estate, NULL, nonPublishableData,\n\t\t\t\t\t\t\t\t\t\t\t\t\t trackQueryCounters);\n\n\t/* initialise state for writing to shards, we'll open connections on demand */\n\tcopyDest->rStartup(copyDest, 0, sourceTupleDescriptor);\n\n\tuint64 rowsCopied = DoCopyFromLocalTableIntoShards(localRelation, copyDest, slot,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   estate);\n\n\t/* finish writing into the shards */\n\tcopyDest->rShutdown(copyDest);\n\tcopyDest->rDestroy(copyDest);\n\n\t/* free memory and close the relation */\n\tExecDropSingleTupleTableSlot(slot);\n\tFreeExecutorState(estate);\n\ttable_close(localRelation, NoLock);\n\n\tPopActiveSnapshot();\n\n\treturn rowsCopied;\n}\n\n\n/*\n * DoCopyFromLocalTableIntoShards performs a copy operation\n * from local tables into shards.\n *\n * Returns the number of rows copied.\n */\nstatic uint64\nDoCopyFromLocalTableIntoShards(Relation localRelation,\n\t\t\t\t\t\t\t   DestReceiver *copyDest,\n\t\t\t\t\t\t\t   TupleTableSlot *slot,\n\t\t\t\t\t\t\t   EState *estate)\n{\n\t/* begin reading from local table */\n\tTableScanDesc scan = table_beginscan(localRelation, GetActiveSnapshot(), 0,\n\t\t\t\t\t\t\t\t\t\t NULL);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));\n\n\tuint64 rowsCopied = 0;\n\twhile (table_scan_getnextslot(scan, ForwardScanDirection, slot))\n\t{\n\t\t/* send tuple it to a shard */\n\t\tcopyDest->receiveSlot(slot, copyDest);\n\n\t\t/* clear tuple memory */\n\t\tResetPerTupleExprContext(estate);\n\n\t\t/* make sure we roll back on cancellation */\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tif (rowsCopied == 0)\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\"Copying data from local table...\")));\n\t\t}\n\n\t\trowsCopied++;\n\n\t\tif (rowsCopied % LOG_PER_TUPLE_AMOUNT == 0)\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"Copied \" UINT64_FORMAT \" rows\", rowsCopied)));\n\t\t}\n\t}\n\n\tif (rowsCopied % LOG_PER_TUPLE_AMOUNT != 0)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"Copied \" UINT64_FORMAT \" rows\", rowsCopied)));\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\n\t/* finish reading from the local table */\n\ttable_endscan(scan);\n\n\treturn rowsCopied;\n}\n\n\n/*\n * TupleDescColumnNameList returns a list of column names for the given tuple\n * descriptor as plain strings.\n */\nstatic List *\nTupleDescColumnNameList(TupleDesc tupleDescriptor)\n{\n\tList *columnNameList = NIL;\n\n\tfor (int columnIndex = 0; columnIndex < tupleDescriptor->natts; columnIndex++)\n\t{\n\t\tForm_pg_attribute currentColumn = TupleDescAttr(tupleDescriptor, columnIndex);\n\t\tchar *columnName = NameStr(currentColumn->attname);\n\n\t\tif (IsDroppedOrGenerated(currentColumn))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tcolumnNameList = lappend(columnNameList, columnName);\n\t}\n\n\treturn columnNameList;\n}\n\n\n/*\n * is_valid_numeric_typmod checks if the typmod value is valid\n *\n * Because of the offset, valid numeric typmods are at least VARHDRSZ\n *\n * Copied from PG. See numeric.c for understanding how this works.\n */\nstatic bool\nis_valid_numeric_typmod(int32 typmod)\n{\n\treturn typmod >= (int32) VARHDRSZ;\n}\n\n\n/*\n * numeric_typmod_scale extracts the scale from a numeric typmod.\n *\n * Copied from PG. See numeric.c for understanding how this works.\n *\n */\nstatic int\nnumeric_typmod_scale(int32 typmod)\n{\n\treturn (((typmod - VARHDRSZ) & 0x7ff) ^ 1024) - 1024;\n}\n\n\n/*\n * DistributionColumnUsesNumericColumnNegativeScale returns whether a given relation uses\n * numeric data type with negative scale on distribution column\n */\nstatic bool\nDistributionColumnUsesNumericColumnNegativeScale(TupleDesc relationDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t Var *distributionColumn)\n{\n\tForm_pg_attribute attributeForm = TupleDescAttr(relationDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdistributionColumn->varattno - 1);\n\n\tif (attributeForm->atttypid == NUMERICOID &&\n\t\tis_valid_numeric_typmod(attributeForm->atttypmod) &&\n\t\tnumeric_typmod_scale(attributeForm->atttypmod) < 0)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * DistributionColumnIsGeneratedCheck throws an error if a given relation uses\n * GENERATED ALWAYS AS (...) STORED | VIRTUAL on distribution column\n */\nstatic void\nDistributionColumnIsGeneratedCheck(TupleDesc relationDesc,\n\t\t\t\t\t\t\t\t   Var *distributionColumn,\n\t\t\t\t\t\t\t\t   const char *relationName)\n{\n\tForm_pg_attribute attributeForm = TupleDescAttr(relationDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdistributionColumn->varattno - 1);\n\tswitch (attributeForm->attgenerated)\n\t{\n\t\tcase ATTRIBUTE_GENERATED_STORED:\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot distribute relation: %s\", relationName),\n\t\t\t\t\t\t\terrdetail(\"Distribution column must not use GENERATED ALWAYS \"\n\t\t\t\t\t\t\t\t\t  \"AS (...) STORED.\")));\n\t\t\tbreak;\n\t\t}\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\t\tcase ATTRIBUTE_GENERATED_VIRTUAL:\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot distribute relation: %s\", relationName),\n\t\t\t\t\t\t\terrdetail(\"Distribution column must not use GENERATED ALWAYS \"\n\t\t\t\t\t\t\t\t\t  \"AS (...) VIRTUAL.\")));\n\t\t\tbreak;\n\t\t}\n\n#endif\n\t\tdefault:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n/*\n * ErrorIfForeignTable errors out if the relation with given relationOid\n * is a foreign table.\n */\nstatic void\nErrorIfForeignTable(Oid relationOid)\n{\n\tif (IsForeignTable(relationOid))\n\t{\n\t\tchar *relname = get_rel_name(relationOid);\n\t\tchar *qualifiedRelname = generate_qualified_relation_name(relationOid);\n\t\tereport(ERROR, (errmsg(\"foreign tables cannot be distributed\"),\n\t\t\t\t\t\t(errhint(\"Can add foreign table \\\"%s\\\" to metadata by running: \"\n\t\t\t\t\t\t\t\t \"SELECT citus_add_local_table_to_metadata($$%s$$);\",\n\t\t\t\t\t\t\t\t relname, qualifiedRelname))));\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/database.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * database.c\n *    Commands to interact with the database object in a distributed\n *    environment.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/table.h\"\n#include \"access/xact.h\"\n#include \"catalog/objectaddress.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_database.h\"\n#include \"catalog/pg_database_d.h\"\n#include \"catalog/pg_tablespace.h\"\n#include \"commands/dbcommands.h\"\n#include \"commands/defrem.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/relcache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/serialize_distributed_ddls.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/comment.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_transaction.h\"\n\n\n/*\n * Used to save original name of the database before it is replaced with a\n * temporary name for failure handling purposes in PreprocessCreateDatabaseStmt().\n */\nstatic char *CreateDatabaseCommandOriginalDbName = NULL;\n\n\n/*\n * The format string used when creating a temporary databases for failure\n * handling purposes.\n *\n * The fields are as follows to ensure using a unique name for each temporary\n * database:\n * - operationId: The operation id returned by RegisterOperationNeedingCleanup().\n * - groupId:     The group id of the worker node where CREATE DATABASE command\n *                is issued from.\n */\n#define TEMP_DATABASE_NAME_FMT \"citus_temp_database_%lu_%d\"\n\n\n/*\n * DatabaseCollationInfo is used to store collation related information of a database.\n */\ntypedef struct DatabaseCollationInfo\n{\n\tchar *datcollate;\n\tchar *datctype;\n\tchar *daticulocale;\n\tchar *datcollversion;\n\tchar *daticurules;\n} DatabaseCollationInfo;\n\nstatic char * GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdatabaseForm);\nstatic DatabaseCollationInfo GetDatabaseCollation(Oid dbOid);\nstatic AlterOwnerStmt * RecreateAlterDatabaseOwnerStmt(Oid databaseOid);\nstatic char * GetLocaleProviderString(char datlocprovider);\nstatic char * GetTablespaceName(Oid tablespaceOid);\nstatic ObjectAddress * GetDatabaseAddressFromDatabaseName(char *databaseName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  bool missingOk);\n\nstatic List * FilterDistributedDatabases(List *databases);\nstatic Oid get_database_owner(Oid dbId);\n\n\n/* controlled via GUC */\nbool EnableCreateDatabasePropagation = false;\nbool EnableAlterDatabaseOwner = true;\n\n/*\n * AlterDatabaseOwnerObjectAddress returns the ObjectAddress of the database that is the\n * object of the AlterOwnerStmt. Errors if missing_ok is false.\n */\nList *\nAlterDatabaseOwnerObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_DATABASE);\n\n\tOid databaseOid = get_database_oid(strVal((String *) stmt->object), missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, DatabaseRelationId, databaseOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * DatabaseOwnerDDLCommands returns a list of sql statements to idempotently apply a\n * change of the database owner on the workers so that the database is owned by the same\n * user on all nodes in the cluster.\n */\nList *\nDatabaseOwnerDDLCommands(const ObjectAddress *address)\n{\n\tNode *stmt = (Node *) RecreateAlterDatabaseOwnerStmt(address->objectId);\n\treturn list_make1(DeparseTreeNode(stmt));\n}\n\n\n/*\n * RecreateAlterDatabaseOwnerStmt creates an AlterOwnerStmt that represents the operation\n * of changing the owner of the database to its current owner.\n */\nstatic AlterOwnerStmt *\nRecreateAlterDatabaseOwnerStmt(Oid databaseOid)\n{\n\tAlterOwnerStmt *stmt = makeNode(AlterOwnerStmt);\n\n\tstmt->objectType = OBJECT_DATABASE;\n\tstmt->object = (Node *) makeString(get_database_name(databaseOid));\n\n\tOid ownerOid = get_database_owner(databaseOid);\n\tstmt->newowner = makeNode(RoleSpec);\n\tstmt->newowner->roletype = ROLESPEC_CSTRING;\n\tstmt->newowner->rolename = GetUserNameFromId(ownerOid, false);\n\n\treturn stmt;\n}\n\n\n/*\n * get_database_owner returns the Oid of the role owning the database\n */\nstatic Oid\nget_database_owner(Oid dbId)\n{\n\tHeapTuple tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(dbId));\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE),\n\t\t\t\t\t\terrmsg(\"database with OID %u does not exist\", dbId)));\n\t}\n\n\tOid dba = ((Form_pg_database) GETSTRUCT(tuple))->datdba;\n\n\tReleaseSysCache(tuple);\n\n\treturn dba;\n}\n\n\n/*\n * PreprocessGrantOnDatabaseStmt is executed before the statement is applied to the local\n * postgres instance.\n *\n * In this stage we can prepare the commands that need to be run on all workers to grant\n * on databases.\n */\nList *\nPreprocessGrantOnDatabaseStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_DATABASE);\n\n\tList *distributedDatabases = FilterDistributedDatabases(stmt->objects);\n\n\tif (list_length(distributedDatabases) == 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tList *originalObjects = stmt->objects;\n\n\tstmt->objects = distributedDatabases;\n\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\n\tstmt->objects = originalObjects;\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * FilterDistributedDatabases filters the database list and returns the distributed ones,\n * as a list.\n */\nstatic List *\nFilterDistributedDatabases(List *databases)\n{\n\tList *distributedDatabases = NIL;\n\tString *databaseName = NULL;\n\tforeach_declared_ptr(databaseName, databases)\n\t{\n\t\tbool missingOk = true;\n\t\tObjectAddress *dbAddress =\n\t\t\tGetDatabaseAddressFromDatabaseName(strVal(databaseName), missingOk);\n\t\tif (IsAnyObjectDistributed(list_make1(dbAddress)))\n\t\t{\n\t\t\tdistributedDatabases = lappend(distributedDatabases, databaseName);\n\t\t}\n\t}\n\n\treturn distributedDatabases;\n}\n\n\n/*\n * IsSetTablespaceStatement returns true if the statement is a SET TABLESPACE statement,\n * false otherwise.\n */\nstatic bool\nIsSetTablespaceStatement(AlterDatabaseStmt *stmt)\n{\n\tDefElem *def = NULL;\n\tforeach_declared_ptr(def, stmt->options)\n\t{\n\t\tif (strcmp(def->defname, \"tablespace\") == 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n\n/*\n * PreprocessAlterDatabaseStmt is executed before the statement is applied to the local\n * postgres instance.\n *\n * In this stage we can prepare the commands that need to be run on all workers to grant\n * on databases.\n *\n * We also serialize database commands globally by acquiring a Citus specific advisory\n * lock based on OCLASS_DATABASE on the first primary worker node.\n */\nList *\nPreprocessAlterDatabaseStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tbool missingOk = false;\n\tAlterDatabaseStmt *stmt = castNode(AlterDatabaseStmt, node);\n\tObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  missingOk);\n\n\tif (!ShouldPropagate() || !IsAnyObjectDistributed(list_make1(dbAddress)))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tSerializeDistributedDDLsOnObjectClassObject(OCLASS_DATABASE, stmt->dbname);\n\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\tif (IsSetTablespaceStatement(stmt))\n\t{\n\t\t/*\n\t\t * Set tablespace does not work inside a transaction.Therefore, we need to use\n\t\t * NontransactionalNodeDDLTask to run the command on the workers outside\n\t\t * the transaction block.\n\t\t */\n\t\tbool warnForPartialFailure = true;\n\t\treturn NontransactionalNodeDDLTaskList(NON_COORDINATOR_NODES, commands,\n\t\t\t\t\t\t\t\t\t\t\t   warnForPartialFailure);\n\t}\n\telse\n\t{\n\t\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n\t}\n}\n\n\n/*\n * PreprocessAlterDatabaseRefreshCollStmt is executed before the statement is applied to\n * the local postgres instance.\n *\n * In this stage we can prepare the commands that need to be run on all workers to grant\n * on databases.\n *\n * We also serialize database commands globally by acquiring a Citus specific advisory\n * lock based on OCLASS_DATABASE on the first primary worker node.\n */\nList *\nPreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tbool missingOk = true;\n\tAlterDatabaseRefreshCollStmt *stmt = castNode(AlterDatabaseRefreshCollStmt, node);\n\tObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  missingOk);\n\n\tif (!ShouldPropagate() || !IsAnyObjectDistributed(list_make1(dbAddress)))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tSerializeDistributedDDLsOnObjectClassObject(OCLASS_DATABASE, stmt->dbname);\n\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * PreprocessAlterDatabaseRenameStmt is executed before the statement is applied to\n * the local postgres instance.\n *\n * We also serialize database commands globally by acquiring a Citus specific advisory\n * lock based on OCLASS_DATABASE on the first primary worker node.\n *\n * We acquire this lock here instead of PostprocessAlterDatabaseRenameStmt because the\n * command renames the database and SerializeDistributedDDLsOnObjectClass resolves the\n * object on workers based on database name. For this reason, we need to acquire the lock\n * before the command is applied to the local postgres instance.\n */\nList *\nPreprocessAlterDatabaseRenameStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tbool missingOk = true;\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->subname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  missingOk);\n\n\tif (!ShouldPropagate() || !IsAnyObjectDistributed(list_make1(dbAddress)))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\t/*\n\t * Different than other ALTER DATABASE commands, we first acquire a lock\n\t * by providing InvalidOid because we want ALTER TABLE .. RENAME TO ..\n\t * commands to block not only with ALTER DATABASE operations but also\n\t * with CREATE DATABASE operations because they might cause name conflicts\n\t * and that could also cause deadlocks too.\n\t */\n\tSerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE);\n\tSerializeDistributedDDLsOnObjectClassObject(OCLASS_DATABASE, stmt->subname);\n\n\treturn NIL;\n}\n\n\n/*\n * PostprocessAlterDatabaseRenameStmt is executed after the statement is applied to the local\n * postgres instance. In this stage we prepare ALTER DATABASE RENAME statement to be run on\n * all workers.\n */\nList *\nPostprocessAlterDatabaseRenameStmt(Node *node, const char *queryString)\n{\n\tbool missingOk = false;\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->newname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  missingOk);\n\n\tif (!ShouldPropagate() || !IsAnyObjectDistributed(list_make1(dbAddress)))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * PreprocessAlterDatabaseSetStmt is executed before the statement is applied to the local\n * postgres instance.\n *\n * In this stage we can prepare the commands that need to be run on all workers to grant\n * on databases.\n *\n * We also serialize database commands globally by acquiring a Citus specific advisory\n * lock based on OCLASS_DATABASE on the first primary worker node.\n */\nList *\nPreprocessAlterDatabaseSetStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tAlterDatabaseSetStmt *stmt = castNode(AlterDatabaseSetStmt, node);\n\n\tbool missingOk = true;\n\tObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  missingOk);\n\tif (!ShouldPropagate() || !IsAnyObjectDistributed(list_make1(dbAddress)))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tSerializeDistributedDDLsOnObjectClassObject(OCLASS_DATABASE, stmt->dbname);\n\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * PreprocessCreateDatabaseStmt is executed before the statement is applied to the local\n * Postgres instance.\n *\n * In this stage, we perform validations that we want to ensure before delegating to\n * previous utility hooks because it might not be convenient to throw an error in an\n * implicit transaction that creates a database. Also in this stage, we save the original\n * database name and replace dbname field with a temporary name for failure handling\n * purposes. We let Postgres create the database with the temporary name, insert a cleanup\n * record for the temporary database name on all nodes and let PostprocessCreateDatabaseStmt()\n * to return the distributed DDL job that both creates the database with the temporary name\n * and then renames it back to its original name.\n *\n * We also serialize database commands globally by acquiring a Citus specific advisory\n * lock based on OCLASS_DATABASE on the first primary worker node.\n */\nList *\nPreprocessCreateDatabaseStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tif (!EnableCreateDatabasePropagation || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinatorIsInMetadata();\n\n\tCreatedbStmt *stmt = castNode(CreatedbStmt, node);\n\tEnsureSupportedCreateDatabaseCommand(stmt);\n\n\tSerializeDistributedDDLsOnObjectClass(OCLASS_DATABASE);\n\n\tOperationId operationId = RegisterOperationNeedingCleanup();\n\n\tchar *tempDatabaseName = psprintf(TEMP_DATABASE_NAME_FMT,\n\t\t\t\t\t\t\t\t\t  operationId, GetLocalGroupId());\n\n\tList *remoteNodes = TargetWorkerSetNodeList(ALL_SHARD_NODES, RowShareLock);\n\tWorkerNode *remoteNode = NULL;\n\tforeach_declared_ptr(remoteNode, remoteNodes)\n\t{\n\t\tInsertCleanupRecordOutsideTransaction(\n\t\t\tCLEANUP_OBJECT_DATABASE,\n\t\t\tpstrdup(quote_identifier(tempDatabaseName)),\n\t\t\tremoteNode->groupId,\n\t\t\tCLEANUP_ON_FAILURE\n\t\t\t);\n\t}\n\n\tCreateDatabaseCommandOriginalDbName = stmt->dbname;\n\tstmt->dbname = tempDatabaseName;\n\n\t/*\n\t * Delete cleanup records in the same transaction so that if the current\n\t * transactions fails for some reason, then the cleanup records won't be\n\t * deleted. In the happy path, we will delete the cleanup records without\n\t * deferring them to the background worker.\n\t */\n\tFinalizeOperationNeedingCleanupOnSuccess(\"create database\");\n\n\treturn NIL;\n}\n\n\n/*\n * PostprocessCreateDatabaseStmt is executed after the statement is applied to the local\n * postgres instance.\n *\n * In this stage, we first rename the temporary database back to its original name for\n * local node and then return a list of distributed DDL jobs to create the database with\n * the temporary name and then to rename it back to its original name. That way, if CREATE\n * DATABASE fails on any of the nodes, the temporary database will be cleaned up by the\n * cleanup records that we inserted in PreprocessCreateDatabaseStmt() and in case of a\n * failure, we won't leak any databases called as the name that user intended to use for\n * the database.\n */\nList *\nPostprocessCreateDatabaseStmt(Node *node, const char *queryString)\n{\n\tif (!EnableCreateDatabasePropagation || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsurePropagationToCoordinator();\n\n\t/*\n\t * Given that CREATE DATABASE doesn't support \"IF NOT EXISTS\" and we're\n\t * in the post-process, database must exist, hence missingOk = false.\n\t */\n\tbool missingOk = false;\n\tbool isPostProcess = true;\n\tList *addresses = GetObjectAddressListFromParseTree(node, missingOk,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tisPostProcess);\n\tEnsureAllObjectDependenciesExistOnAllNodes(addresses);\n\n\tchar *createDatabaseCommand = DeparseTreeNode(node);\n\n\tList *createDatabaseCommands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t\t\t\t  (void *) createDatabaseCommand,\n\t\t\t\t\t\t\t\t\t\t\t  ENABLE_DDL_PROPAGATION);\n\n\t/*\n\t * Since the CREATE DATABASE statements cannot be executed in a transaction\n\t * block, we need to use NontransactionalNodeDDLTaskList() to send the CREATE\n\t * DATABASE statement to the workers.\n\t */\n\tbool warnForPartialFailure = false;\n\tList *createDatabaseDDLJobList =\n\t\tNontransactionalNodeDDLTaskList(REMOTE_NODES, createDatabaseCommands,\n\t\t\t\t\t\t\t\t\t\twarnForPartialFailure);\n\n\tCreatedbStmt *stmt = castNode(CreatedbStmt, node);\n\n\tchar *renameDatabaseCommand =\n\t\tpsprintf(\"ALTER DATABASE %s RENAME TO %s\",\n\t\t\t\t quote_identifier(stmt->dbname),\n\t\t\t\t quote_identifier(CreateDatabaseCommandOriginalDbName));\n\n\tList *renameDatabaseCommands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t\t\t\t  renameDatabaseCommand,\n\t\t\t\t\t\t\t\t\t\t\t  ENABLE_DDL_PROPAGATION);\n\n\t/*\n\t * We use NodeDDLTaskList() to send the RENAME DATABASE statement to the\n\t * workers because we want to execute it in a coordinated transaction.\n\t */\n\tList *renameDatabaseDDLJobList =\n\t\tNodeDDLTaskList(REMOTE_NODES, renameDatabaseCommands);\n\n\t/*\n\t * Temporarily disable citus.enable_ddl_propagation before issuing\n\t * rename command locally because we don't want to execute it on remote\n\t * nodes yet. We will execute it on remote nodes by returning it as a\n\t * distributed DDL job.\n\t *\n\t * The reason why we don't want to execute it on remote nodes yet is that\n\t * the database is not created on remote nodes yet.\n\t */\n\tint saveNestLevel = NewGUCNestLevel();\n\tset_config_option(\"citus.enable_ddl_propagation\", \"off\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n\n\tExecuteUtilityCommand(renameDatabaseCommand);\n\n\tAtEOXact_GUC(true, saveNestLevel);\n\n\t/*\n\t * Restore the original database name because MarkObjectDistributed()\n\t * resolves oid of the object based on the database name and is called\n\t * after executing the distributed DDL job that renames temporary database.\n\t */\n\tstmt->dbname = CreateDatabaseCommandOriginalDbName;\n\n\treturn list_concat(createDatabaseDDLJobList, renameDatabaseDDLJobList);\n}\n\n\n/*\n * PreprocessDropDatabaseStmt is executed before the statement is applied to the local\n * postgres instance. In this stage we can prepare the commands that need to be run on\n * all workers to drop the database.\n *\n * We also serialize database commands globally by acquiring a Citus specific advisory\n * lock based on OCLASS_DATABASE on the first primary worker node.\n */\nList *\nPreprocessDropDatabaseStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tif (!EnableCreateDatabasePropagation || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsurePropagationToCoordinator();\n\n\tDropdbStmt *stmt = (DropdbStmt *) node;\n\n\tbool isPostProcess = false;\n\tList *addresses = GetObjectAddressListFromParseTree(node, stmt->missing_ok,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tisPostProcess);\n\n\tif (list_length(addresses) != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"unexpected number of objects found when \"\n\t\t\t\t\t\t\t   \"executing DROP DATABASE command\")));\n\t}\n\n\tObjectAddress *address = (ObjectAddress *) linitial(addresses);\n\tif (address->objectId == InvalidOid || !IsAnyObjectDistributed(list_make1(address)))\n\t{\n\t\treturn NIL;\n\t}\n\n\tSerializeDistributedDDLsOnObjectClassObject(OCLASS_DATABASE, stmt->dbname);\n\n\tchar *dropDatabaseCommand = DeparseTreeNode(node);\n\n\tList *dropDatabaseCommands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t\t\t\t(void *) dropDatabaseCommand,\n\t\t\t\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\t/*\n\t * Due to same reason stated in PostprocessCreateDatabaseStmt(), we need to\n\t * use NontransactionalNodeDDLTaskList() to send the DROP DATABASE statement\n\t * to the workers.\n\t */\n\tbool warnForPartialFailure = true;\n\tList *dropDatabaseDDLJobList =\n\t\tNontransactionalNodeDDLTaskList(REMOTE_NODES, dropDatabaseCommands,\n\t\t\t\t\t\t\t\t\t\twarnForPartialFailure);\n\treturn dropDatabaseDDLJobList;\n}\n\n\n/*\n * DropDatabaseStmtObjectAddress gets the ObjectAddress of the database that is the\n * object of the DropdbStmt.\n */\nList *\nDropDatabaseStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess)\n{\n\tDropdbStmt *stmt = castNode(DropdbStmt, node);\n\tObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  missingOk);\n\treturn list_make1(dbAddress);\n}\n\n\n/*\n * CreateDatabaseStmtObjectAddress gets the ObjectAddress of the database that is the\n * object of the CreatedbStmt.\n */\nList *\nCreateDatabaseStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess)\n{\n\tCreatedbStmt *stmt = castNode(CreatedbStmt, node);\n\tObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  missingOk);\n\treturn list_make1(dbAddress);\n}\n\n\n/*\n * EnsureSupportedCreateDatabaseCommand validates the options provided for the CREATE\n * DATABASE command.\n *\n * Parameters:\n * stmt: A CreatedbStmt struct representing a CREATE DATABASE command.\n *       The options field is a list of DefElem structs, each representing an option.\n *\n * Currently, this function checks for the following:\n * - The \"oid\" option is not supported.\n * - The \"template\" option is only supported with the value \"template1\".\n * - The \"strategy\" option is only supported with the value \"wal_log\".\n */\nvoid\nEnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt)\n{\n\tDefElem *option = NULL;\n\tforeach_declared_ptr(option, stmt->options)\n\t{\n\t\tif (strcmp(option->defname, \"oid\") == 0)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\terrmsg(\"CREATE DATABASE option \\\"%s\\\" is not supported\",\n\t\t\t\t\t\t   option->defname));\n\t\t}\n\n\t\tchar *optionValue = defGetString(option);\n\n\t\tif (strcmp(option->defname, \"template\") == 0 &&\n\t\t\tstrcmp(optionValue, \"template1\") != 0)\n\t\t{\n\t\t\tereport(ERROR, errmsg(\"Only template1 is supported as template \"\n\t\t\t\t\t\t\t\t  \"parameter for CREATE DATABASE\"));\n\t\t}\n\n\t\tif (strcmp(option->defname, \"strategy\") == 0 &&\n\t\t\tstrcmp(optionValue, \"wal_log\") != 0)\n\t\t{\n\t\t\tereport(ERROR, errmsg(\"Only wal_log is supported as strategy \"\n\t\t\t\t\t\t\t\t  \"parameter for CREATE DATABASE\"));\n\t\t}\n\t}\n}\n\n\n/*\n * GetDatabaseAddressFromDatabaseName gets the database name and returns the ObjectAddress\n * of the database.\n */\nstatic ObjectAddress *\nGetDatabaseAddressFromDatabaseName(char *databaseName, bool missingOk)\n{\n\tOid databaseOid = get_database_oid(databaseName, missingOk);\n\tObjectAddress *dbObjectAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*dbObjectAddress, DatabaseRelationId, databaseOid);\n\treturn dbObjectAddress;\n}\n\n\n/*\n * GetTablespaceName gets the tablespace oid and returns the tablespace name.\n */\nstatic char *\nGetTablespaceName(Oid tablespaceOid)\n{\n\tHeapTuple tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tablespaceOid));\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\treturn NULL;\n\t}\n\n\tForm_pg_tablespace tablespaceForm = (Form_pg_tablespace) GETSTRUCT(tuple);\n\tchar *tablespaceName = pstrdup(NameStr(tablespaceForm->spcname));\n\n\tReleaseSysCache(tuple);\n\n\treturn tablespaceName;\n}\n\n\n/*\n * GetDatabaseMetadataSyncCommands returns a list of sql statements\n * for the given database id. The list contains the database ddl command,\n * grant commands and comment propagation commands.\n */\nList *\nGetDatabaseMetadataSyncCommands(Oid dbOid)\n{\n\tchar *databaseName = get_database_name(dbOid);\n\tchar *databaseDDLCommand = CreateDatabaseDDLCommand(dbOid);\n\n\tList *ddlCommands = list_make1(databaseDDLCommand);\n\n\tList *grantDDLCommands = GrantOnDatabaseDDLCommands(dbOid);\n\tList *commentDDLCommands = GetCommentPropagationCommands(DatabaseRelationId, dbOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t databaseName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t OBJECT_DATABASE);\n\n\tddlCommands = list_concat(ddlCommands, grantDDLCommands);\n\tddlCommands = list_concat(ddlCommands, commentDDLCommands);\n\n\treturn ddlCommands;\n}\n\n\n/*\n * GetDatabaseCollation gets oid of a database and returns all the collation related information\n * We need this method since collation related info in Form_pg_database is not accessible.\n */\nstatic DatabaseCollationInfo\nGetDatabaseCollation(Oid dbOid)\n{\n\tDatabaseCollationInfo info;\n\tmemset(&info, 0, sizeof(DatabaseCollationInfo));\n\n\tRelation rel = table_open(DatabaseRelationId, AccessShareLock);\n\tHeapTuple tup = get_catalog_object_by_oid(rel, Anum_pg_database_oid, dbOid);\n\tif (!HeapTupleIsValid(tup))\n\t{\n\t\telog(ERROR, \"cache lookup failed for database %u\", dbOid);\n\t}\n\n\tbool isNull = false;\n\n\tTupleDesc tupdesc = RelationGetDescr(rel);\n\n\tDatum collationDatum = heap_getattr(tup, Anum_pg_database_datcollate, tupdesc,\n\t\t\t\t\t\t\t\t\t\t&isNull);\n\tinfo.datcollate = TextDatumGetCString(collationDatum);\n\n\tDatum ctypeDatum = heap_getattr(tup, Anum_pg_database_datctype, tupdesc, &isNull);\n\tinfo.datctype = TextDatumGetCString(ctypeDatum);\n\n\tDatum icuLocaleDatum = heap_getattr(tup, Anum_pg_database_datlocale, tupdesc,\n\t\t\t\t\t\t\t\t\t\t&isNull);\n\tif (!isNull)\n\t{\n\t\tinfo.daticulocale = TextDatumGetCString(icuLocaleDatum);\n\t}\n\n\tDatum collverDatum = heap_getattr(tup, Anum_pg_database_datcollversion, tupdesc,\n\t\t\t\t\t\t\t\t\t  &isNull);\n\tif (!isNull)\n\t{\n\t\tinfo.datcollversion = TextDatumGetCString(collverDatum);\n\t}\n\n\tDatum icurulesDatum = heap_getattr(tup, Anum_pg_database_daticurules, tupdesc,\n\t\t\t\t\t\t\t\t\t   &isNull);\n\tif (!isNull)\n\t{\n\t\tinfo.daticurules = TextDatumGetCString(icurulesDatum);\n\t}\n\n\ttable_close(rel, AccessShareLock);\n\theap_freetuple(tup);\n\n\treturn info;\n}\n\n\n/*\n * GetLocaleProviderString gets the datlocprovider stored in pg_database\n * and returns the string representation of the datlocprovider\n */\nstatic char *\nGetLocaleProviderString(char datlocprovider)\n{\n\tswitch (datlocprovider)\n\t{\n\t\tcase 'c':\n\t\t{\n\t\t\treturn \"libc\";\n\t\t}\n\n\t\tcase 'i':\n\t\t{\n\t\t\treturn \"icu\";\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unexpected datlocprovider value: %c\",\n\t\t\t\t\t\t\t\t   datlocprovider)));\n\t\t}\n\t}\n}\n\n\n/*\n * GenerateCreateDatabaseStatementFromPgDatabase gets the pg_database tuple and returns the\n * CREATE DATABASE statement that can be used to create given database.\n *\n * Note that this doesn't deparse OID of the database and this is not a\n * problem as we anyway don't allow specifying custom OIDs for databases\n * when creating them.\n */\nstatic char *\nGenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm)\n{\n\tDatabaseCollationInfo collInfo = GetDatabaseCollation(databaseForm->oid);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tappendStringInfo(&str, \"CREATE DATABASE %s\",\n\t\t\t\t\t quote_identifier(NameStr(databaseForm->datname)));\n\n\tappendStringInfo(&str, \" CONNECTION LIMIT %d\", databaseForm->datconnlimit);\n\n\tappendStringInfo(&str, \" ALLOW_CONNECTIONS = %s\",\n\t\t\t\t\t quote_literal_cstr(databaseForm->datallowconn ? \"true\" : \"false\"));\n\n\tappendStringInfo(&str, \" IS_TEMPLATE = %s\",\n\t\t\t\t\t quote_literal_cstr(databaseForm->datistemplate ? \"true\" : \"false\"));\n\n\tappendStringInfo(&str, \" LC_COLLATE = %s\",\n\t\t\t\t\t quote_literal_cstr(collInfo.datcollate));\n\n\tappendStringInfo(&str, \" LC_CTYPE = %s\", quote_literal_cstr(collInfo.datctype));\n\n\tappendStringInfo(&str, \" OWNER = %s\",\n\t\t\t\t\t quote_identifier(GetUserNameFromId(databaseForm->datdba, false)));\n\n\tappendStringInfo(&str, \" TABLESPACE = %s\",\n\t\t\t\t\t quote_identifier(GetTablespaceName(databaseForm->dattablespace)));\n\n\tappendStringInfo(&str, \" ENCODING = %s\",\n\t\t\t\t\t quote_literal_cstr(pg_encoding_to_char(databaseForm->encoding)));\n\n\tif (collInfo.datcollversion != NULL)\n\t{\n\t\tappendStringInfo(&str, \" COLLATION_VERSION = %s\",\n\t\t\t\t\t\t quote_identifier(collInfo.datcollversion));\n\t}\n\n\tif (collInfo.daticulocale != NULL)\n\t{\n\t\tappendStringInfo(&str, \" ICU_LOCALE = %s\", quote_identifier(\n\t\t\t\t\t\t\t collInfo.daticulocale));\n\t}\n\n\tappendStringInfo(&str, \" LOCALE_PROVIDER = %s\",\n\t\t\t\t\t quote_identifier(GetLocaleProviderString(\n\t\t\t\t\t\t\t\t\t\t  databaseForm->datlocprovider)));\n\n\tif (collInfo.daticurules != NULL)\n\t{\n\t\tappendStringInfo(&str, \" ICU_RULES = %s\", quote_identifier(\n\t\t\t\t\t\t\t collInfo.daticurules));\n\t}\n\n\treturn str.data;\n}\n\n\n/*\n * CreateDatabaseDDLCommand returns a CREATE DATABASE command to create given\n * database\n *\n * Command is wrapped by citus_internal_database_command() UDF\n * to avoid from transaction block restrictions that apply to database commands.\n */\nchar *\nCreateDatabaseDDLCommand(Oid dbId)\n{\n\tHeapTuple tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(dbId));\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE),\n\t\t\t\t\t\terrmsg(\"database with OID %u does not exist\", dbId)));\n\t}\n\n\tForm_pg_database databaseForm = (Form_pg_database) GETSTRUCT(tuple);\n\n\tchar *createStmt = GenerateCreateDatabaseStatementFromPgDatabase(databaseForm);\n\n\tStringInfo outerDbStmt = makeStringInfo();\n\n\t/* Generate the CREATE DATABASE statement */\n\tappendStringInfo(outerDbStmt,\n\t\t\t\t\t \"SELECT citus_internal.database_command(%s)\",\n\t\t\t\t\t quote_literal_cstr(createStmt));\n\n\tReleaseSysCache(tuple);\n\n\treturn outerDbStmt->data;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/dependencies.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * dependencies.c\n *    Commands to create dependencies of an object on all workers.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/dependency.h\"\n#include \"catalog/objectaddress.h\"\n#include \"commands/extension.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_transaction.h\"\n\ntypedef enum RequiredObjectSet\n{\n\tREQUIRE_ONLY_DEPENDENCIES = 1,\n\tREQUIRE_OBJECT_AND_DEPENDENCIES = 2,\n} RequiredObjectSet;\n\n\nstatic void EnsureDependenciesCanBeDistributed(const ObjectAddress *relationAddress);\nstatic void ErrorIfCircularDependencyExists(const ObjectAddress *objectAddress);\nstatic int ObjectAddressComparator(const void *a, const void *b);\nstatic void EnsureDependenciesExistOnAllNodes(const ObjectAddress *target);\nstatic void EnsureRequiredObjectSetExistOnAllNodes(const ObjectAddress *target,\n\t\t\t\t\t\t\t\t\t\t\t\t   RequiredObjectSet requiredObjectSet);\nstatic List * GetDependencyCreateDDLCommands(const ObjectAddress *dependency);\nstatic bool ShouldPropagateObject(const ObjectAddress *address);\nstatic char * DropTableIfExistsCommand(Oid relationId);\n\n/*\n * EnsureObjectAndDependenciesExistOnAllNodes is a wrapper around\n * EnsureRequiredObjectSetExistOnAllNodes to ensure the \"object itself\" (together\n * with its dependencies) is available on all nodes.\n *\n * Different than EnsureDependenciesExistOnAllNodes, we return early if the\n * target object is distributed already.\n *\n * The reason why we don't do the same in EnsureDependenciesExistOnAllNodes\n * is that it's is used when altering an object too and hence the target object\n * may instantly have a dependency that needs to be propagated now. For example,\n * when \"⁠GRANT non_dist_role TO dist_role\" is executed, we need to propagate\n * \"non_dist_role\" to all nodes before propagating the \"GRANT\" command itself.\n * For this reason, we call EnsureDependenciesExistOnAllNodes for \"dist_role\"\n * and it would automatically discover that \"non_dist_role\" is a dependency of\n * \"dist_role\" and propagate it beforehand.\n *\n * However, when we're requested to create the target object itself (and\n * implicitly its dependencies), we're sure that we're not altering the target\n * object itself, hence we can return early if the target object is already\n * distributed. This is the case, for example, when\n * \"REASSIGN OWNED BY dist_role TO non_dist_role\" is executed. In that case,\n * \"non_dist_role\" is not a dependency of \"dist_role\" but we want to distribute\n * \"non_dist_role\" beforehand and we call this function for \"non_dist_role\",\n * not for \"dist_role\".\n *\n * See EnsureRequiredObjectExistOnAllNodes to learn more about how this\n * function deals with an object created within the same transaction.\n */\nvoid\nEnsureObjectAndDependenciesExistOnAllNodes(const ObjectAddress *target)\n{\n\tif (IsAnyObjectDistributed(list_make1((ObjectAddress *) target)))\n\t{\n\t\treturn;\n\t}\n\tEnsureRequiredObjectSetExistOnAllNodes(target, REQUIRE_OBJECT_AND_DEPENDENCIES);\n}\n\n\n/*\n * EnsureDependenciesExistOnAllNodes is a wrapper around\n * EnsureRequiredObjectSetExistOnAllNodes to ensure \"all dependencies\" of given\n * object --but not the object itself-- are available on all nodes.\n *\n * See EnsureRequiredObjectSetExistOnAllNodes to learn more about how this\n * function deals with an object created within the same transaction.\n */\nstatic void\nEnsureDependenciesExistOnAllNodes(const ObjectAddress *target)\n{\n\tEnsureRequiredObjectSetExistOnAllNodes(target, REQUIRE_ONLY_DEPENDENCIES);\n}\n\n\n/*\n * EnsureRequiredObjectSetExistOnAllNodes finds all the dependencies that we support and makes\n * sure these are available on all nodes if required object set is REQUIRE_ONLY_DEPENDENCIES.\n * Otherwise, i.e., if required object set is REQUIRE_OBJECT_AND_DEPENDENCIES, then this\n * function creates the object itself on all nodes too. This function ensures that each\n * of the dependencies are supported by Citus but doesn't check the same for the target\n * object itself (when REQUIRE_OBJECT_AND_DEPENDENCIES) is provided because we assume that\n * callers don't call this function for an unsupported function at all.\n *\n * If not available, they will be created on the nodes via a separate session that will be\n * committed directly so that the objects are visible to potentially multiple sessions creating\n * the shards.\n *\n * Note; only the actual objects are created via a separate session, the records to\n * pg_dist_object are created in this session. As a side effect the objects could be\n * created on the nodes without a catalog entry. Updates to the objects on local node\n * are not propagated to the remote nodes until the record is visible on local node.\n *\n * This is solved by creating the dependencies in an idempotent manner, either via\n * postgres native CREATE IF NOT EXISTS, or citus helper functions.\n */\nstatic void\nEnsureRequiredObjectSetExistOnAllNodes(const ObjectAddress *target,\n\t\t\t\t\t\t\t\t\t   RequiredObjectSet requiredObjectSet)\n{\n\tAssert(requiredObjectSet == REQUIRE_ONLY_DEPENDENCIES ||\n\t\t   requiredObjectSet == REQUIRE_OBJECT_AND_DEPENDENCIES);\n\n\n\tList *objectsWithCommands = NIL;\n\tList *ddlCommands = NULL;\n\n\t/*\n\t * If there is any unsupported dependency or circular dependency exists, Citus can\n\t * not ensure dependencies will exist on all nodes.\n\t *\n\t * Note that we don't check whether \"target\" is distributable (in case\n\t * REQUIRE_OBJECT_AND_DEPENDENCIES is provided) because we expect callers\n\t * to not even call this function if Citus doesn't know how to propagate\n\t * \"target\" object itself.\n\t */\n\tEnsureDependenciesCanBeDistributed(target);\n\n\t/* collect all dependencies in creation order and get their ddl commands */\n\tList *objectsToBeCreated = GetDependenciesForObject(target);\n\n\t/*\n\t * Append the target object to make sure that it's created after its\n\t * dependencies are created, if requested.\n\t */\n\tif (requiredObjectSet == REQUIRE_OBJECT_AND_DEPENDENCIES)\n\t{\n\t\tObjectAddress *targetCopy = palloc(sizeof(ObjectAddress));\n\t\t*targetCopy = *target;\n\n\t\tobjectsToBeCreated = lappend(objectsToBeCreated, targetCopy);\n\t}\n\n\tObjectAddress *object = NULL;\n\tforeach_declared_ptr(object, objectsToBeCreated)\n\t{\n\t\tList *dependencyCommands = GetDependencyCreateDDLCommands(object);\n\t\tddlCommands = list_concat(ddlCommands, dependencyCommands);\n\n\t\t/* create a new list with objects that actually created commands */\n\t\tif (list_length(dependencyCommands) > 0)\n\t\t{\n\t\t\tobjectsWithCommands = lappend(objectsWithCommands, object);\n\t\t}\n\t}\n\tif (list_length(ddlCommands) <= 0)\n\t{\n\t\t/* no ddl commands to be executed */\n\t\treturn;\n\t}\n\n\t/* since we are executing ddl commands lets disable propagation, primarily for mx */\n\tddlCommands = list_concat(list_make1(DISABLE_DDL_PROPAGATION), ddlCommands);\n\n\t/*\n\t * Make sure that no new nodes are added after this point until the end of the\n\t * transaction by taking a RowShareLock on pg_dist_node, which conflicts with the\n\t * ExclusiveLock taken by citus_add_node.\n\t * This guarantees that all active nodes will have the object, because they will\n\t * either get it now, or get it in citus_add_node after this transaction finishes and\n\t * the pg_dist_object record becomes visible.\n\t */\n\tList *remoteNodeList = ActivePrimaryRemoteNodeList(RowShareLock);\n\n\t/*\n\t * Lock objects to be created explicitly to make sure same DDL command won't be sent\n\t * multiple times from parallel sessions.\n\t *\n\t * Sort the objects that will be created on workers to not to have any deadlock\n\t * issue if different sessions are creating different objects.\n\t */\n\tList *addressSortedDependencies = SortList(objectsWithCommands,\n\t\t\t\t\t\t\t\t\t\t\t   ObjectAddressComparator);\n\tforeach_declared_ptr(object, addressSortedDependencies)\n\t{\n\t\tLockDatabaseObject(object->classId, object->objectId,\n\t\t\t\t\t\t   object->objectSubId, ExclusiveLock);\n\t}\n\n\n\t/*\n\t * We need to propagate objects via the current user's metadata connection if\n\t * any of the objects that we're interested in are created in the current transaction.\n\t * Our assumption is that if we rely on an object created in the current transaction,\n\t * then the current user, most probably, has permissions to create the target object\n\t * as well.\n\t *\n\t * Note that, user still may not be able to create the target due to no permissions\n\t * for any of its dependencies. But this is ok since it should be rare.\n\t *\n\t * If we opted to use a separate superuser connection for the target, then we would\n\t * have visibility issues since propagated dependencies would be invisible to\n\t * the separate connection until we locally commit.\n\t */\n\tList *createdObjectList = GetAllSupportedDependenciesForObject(target);\n\n\t/* consider target as well if we're requested to create it too */\n\tif (requiredObjectSet == REQUIRE_OBJECT_AND_DEPENDENCIES)\n\t{\n\t\tObjectAddress *targetCopy = palloc(sizeof(ObjectAddress));\n\t\t*targetCopy = *target;\n\n\t\tcreatedObjectList = lappend(createdObjectList, targetCopy);\n\t}\n\n\tif (HasAnyObjectInPropagatedObjects(createdObjectList))\n\t{\n\t\tSendCommandListToRemoteNodesWithMetadata(ddlCommands);\n\t}\n\telse\n\t{\n\t\tWorkerNode *workerNode = NULL;\n\t\tforeach_declared_ptr(workerNode, remoteNodeList)\n\t\t{\n\t\t\tconst char *nodeName = workerNode->workerName;\n\t\t\tuint32 nodePort = workerNode->workerPort;\n\n\t\t\tSendCommandListToWorkerOutsideTransaction(nodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  CitusExtensionOwnerName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ddlCommands);\n\t\t}\n\t}\n\n\t/*\n\t * We do this after creating the objects on remote nodes, we make sure\n\t * that objects have been created on remote nodes before marking them\n\t * distributed, so MarkObjectDistributed wouldn't fail.\n\t */\n\tforeach_declared_ptr(object, objectsWithCommands)\n\t{\n\t\t/*\n\t\t * pg_dist_object entries must be propagated with the super user, since\n\t\t * the owner of the target object may not own dependencies but we must\n\t\t * propagate as we send objects itself with the superuser.\n\t\t *\n\t\t * Only dependent object's metadata should be propagated with super user.\n\t\t * Metadata of the table itself must be propagated with the current user.\n\t\t */\n\t\tMarkObjectDistributedViaSuperUser(object);\n\t}\n}\n\n\n/*\n * EnsureAllObjectDependenciesExistOnAllNodes iteratively calls EnsureDependenciesExistOnAllNodes\n * for given targets.\n */\nvoid\nEnsureAllObjectDependenciesExistOnAllNodes(const List *targets)\n{\n\tObjectAddress *target = NULL;\n\tforeach_declared_ptr(target, targets)\n\t{\n\t\tEnsureDependenciesExistOnAllNodes(target);\n\t}\n}\n\n\n/*\n * EnsureDependenciesCanBeDistributed ensures all dependencies of the given object\n * can be distributed.\n */\nstatic void\nEnsureDependenciesCanBeDistributed(const ObjectAddress *objectAddress)\n{\n\t/* If the object circularcly depends to itself, Citus can not handle it */\n\tErrorIfCircularDependencyExists(objectAddress);\n\n\t/* If the object has any unsupported dependency, error out */\n\tDeferredErrorMessage *depError = DeferErrorIfAnyObjectHasUnsupportedDependency(\n\t\tlist_make1((ObjectAddress *) objectAddress));\n\n\tif (depError != NULL)\n\t{\n\t\t/* override error detail as it is not applicable here*/\n\t\tdepError->detail = NULL;\n\t\tRaiseDeferredError(depError, ERROR);\n\t}\n}\n\n\n/*\n * ErrorIfCircularDependencyExists is a wrapper around\n * DeferErrorIfCircularDependencyExists(), and throws error\n * if circular dependency exists.\n */\nstatic void\nErrorIfCircularDependencyExists(const ObjectAddress *objectAddress)\n{\n\tDeferredErrorMessage *depError =\n\t\tDeferErrorIfCircularDependencyExists(objectAddress);\n\tif (depError != NULL)\n\t{\n\t\tRaiseDeferredError(depError, ERROR);\n\t}\n}\n\n\n/*\n * DeferErrorIfCircularDependencyExists checks whether given object has\n * circular dependency with itself. If so, returns a deferred error.\n */\nDeferredErrorMessage *\nDeferErrorIfCircularDependencyExists(const ObjectAddress *objectAddress)\n{\n\tList *dependencies = GetAllDependenciesForObject(objectAddress);\n\n\tObjectAddress *dependency = NULL;\n\tforeach_declared_ptr(dependency, dependencies)\n\t{\n\t\tif (dependency->classId == objectAddress->classId &&\n\t\t\tdependency->objectId == objectAddress->objectId &&\n\t\t\tdependency->objectSubId == objectAddress->objectSubId)\n\t\t{\n\t\t\tchar *objectDescription = getObjectDescription(objectAddress, false);\n\n\t\t\tStringInfo detailInfo = makeStringInfo();\n\t\t\tappendStringInfo(detailInfo, \"\\\"%s\\\" circularly depends itself, resolve \"\n\t\t\t\t\t\t\t\t\t\t \"circular dependency first\", objectDescription);\n\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"Citus can not handle circular dependencies \"\n\t\t\t\t\t\t\t\t \"between distributed objects\", detailInfo->data,\n\t\t\t\t\t\t\t\t NULL);\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * Copied from PG object_address_comparator function to compare ObjectAddresses.\n */\nstatic int\nObjectAddressComparator(const void *a, const void *b)\n{\n\tconst ObjectAddress *obja = (const ObjectAddress *) a;\n\tconst ObjectAddress *objb = (const ObjectAddress *) b;\n\n\t/*\n\t * Primary sort key is OID descending.\n\t */\n\tif (obja->objectId > objb->objectId)\n\t{\n\t\treturn -1;\n\t}\n\tif (obja->objectId < objb->objectId)\n\t{\n\t\treturn 1;\n\t}\n\n\t/*\n\t * Next sort on catalog ID, in case identical OIDs appear in different\n\t * catalogs. Sort direction is pretty arbitrary here.\n\t */\n\tif (obja->classId < objb->classId)\n\t{\n\t\treturn -1;\n\t}\n\tif (obja->classId > objb->classId)\n\t{\n\t\treturn 1;\n\t}\n\n\t/*\n\t * Last, sort on object subId.\n\t */\n\tif ((unsigned int) obja->objectSubId < (unsigned int) objb->objectSubId)\n\t{\n\t\treturn -1;\n\t}\n\tif ((unsigned int) obja->objectSubId > (unsigned int) objb->objectSubId)\n\t{\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\n\n/*\n * GetDistributableDependenciesForObject finds all the dependencies that Citus\n * can distribute and returns those dependencies regardless of their existency\n * on nodes.\n */\nList *\nGetDistributableDependenciesForObject(const ObjectAddress *target)\n{\n\t/* local variables to work with dependencies */\n\tList *distributableDependencies = NIL;\n\n\t/* collect all dependencies in creation order */\n\tList *dependencies = GetDependenciesForObject(target);\n\n\t/* filter the ones that can be distributed */\n\tObjectAddress *dependency = NULL;\n\tforeach_declared_ptr(dependency, dependencies)\n\t{\n\t\t/*\n\t\t * TODO: maybe we can optimize the logic applied in below line. Actually we\n\t\t * do not need to create ddl commands as we are not ensuring their existence\n\t\t * in nodes, but we utilize logic it follows to choose the objects that could\n\t\t * be distributed\n\t\t */\n\t\tList *dependencyCommands = GetDependencyCreateDDLCommands(dependency);\n\n\t\t/* create a new list with dependencies that actually created commands */\n\t\tif (list_length(dependencyCommands) > 0)\n\t\t{\n\t\t\tdistributableDependencies = lappend(distributableDependencies, dependency);\n\t\t}\n\t}\n\n\treturn distributableDependencies;\n}\n\n\n/*\n * DropTableIfExistsCommand returns command to drop given table if exists.\n */\nstatic char *\nDropTableIfExistsCommand(Oid relationId)\n{\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\tStringInfo dropTableCommand = makeStringInfo();\n\tappendStringInfo(dropTableCommand, \"DROP TABLE IF EXISTS %s CASCADE\",\n\t\t\t\t\t qualifiedRelationName);\n\n\treturn dropTableCommand->data;\n}\n\n\n/*\n * GetDependencyCreateDDLCommands returns a list (potentially empty or NIL) of ddl\n * commands to execute on a worker to create the object.\n */\nstatic List *\nGetDependencyCreateDDLCommands(const ObjectAddress *dependency)\n{\n\tswitch (getObjectClass(dependency))\n\t{\n\t\tcase OCLASS_CLASS:\n\t\t{\n\t\t\tchar relKind = get_rel_relkind(dependency->objectId);\n\n\t\t\t/*\n\t\t\t * types have an intermediate dependency on a relation (aka class), so we do\n\t\t\t * support classes when the relkind is composite\n\t\t\t */\n\t\t\tif (relKind == RELKIND_COMPOSITE_TYPE)\n\t\t\t{\n\t\t\t\treturn NIL;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Indices are created separately, however, they do show up in the dependency\n\t\t\t * list for a table since they will have potentially their own dependencies.\n\t\t\t * The commands will be added to both shards and metadata tables via the table\n\t\t\t * creation commands.\n\t\t\t */\n\t\t\tif (relKind == RELKIND_INDEX ||\n\t\t\t\trelKind == RELKIND_PARTITIONED_INDEX)\n\t\t\t{\n\t\t\t\treturn NIL;\n\t\t\t}\n\n\t\t\tif (relKind == RELKIND_RELATION || relKind == RELKIND_PARTITIONED_TABLE ||\n\t\t\t\trelKind == RELKIND_FOREIGN_TABLE)\n\t\t\t{\n\t\t\t\tOid relationId = dependency->objectId;\n\t\t\t\tList *commandList = NIL;\n\n\t\t\t\tif (IsCitusTable(relationId))\n\t\t\t\t{\n\t\t\t\t\tbool creatingShellTableOnRemoteNode = true;\n\t\t\t\t\tList *tableDDLCommands = GetFullTableCreationCommands(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  WORKER_NEXTVAL_SEQUENCE_DEFAULTS,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  INCLUDE_IDENTITY,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  creatingShellTableOnRemoteNode);\n\t\t\t\t\tTableDDLCommand *tableDDLCommand = NULL;\n\t\t\t\t\tforeach_declared_ptr(tableDDLCommand, tableDDLCommands)\n\t\t\t\t\t{\n\t\t\t\t\t\tAssert(CitusIsA(tableDDLCommand, TableDDLCommand));\n\t\t\t\t\t\tcommandList = lappend(commandList, GetTableDDLCommand(\n\t\t\t\t\t\t\t\t\t\t\t\t  tableDDLCommand));\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t * We need to drop table, if exists, first to make table creation\n\t\t\t\t\t * idempotent. Before dropping the table, we should also break\n\t\t\t\t\t * dependencies with sequences since `drop cascade table` would also\n\t\t\t\t\t * drop depended sequences. This is safe as we still record dependency\n\t\t\t\t\t * with the sequence during table creation.\n\t\t\t\t\t */\n\t\t\t\t\tcommandList = lcons(DropTableIfExistsCommand(relationId),\n\t\t\t\t\t\t\t\t\t\tcommandList);\n\t\t\t\t\tcommandList = lcons(WorkerDropSequenceDependencyCommand(relationId),\n\t\t\t\t\t\t\t\t\t\tcommandList);\n\t\t\t\t}\n\n\t\t\t\treturn commandList;\n\t\t\t}\n\n\t\t\tif (relKind == RELKIND_SEQUENCE)\n\t\t\t{\n\t\t\t\tchar *sequenceOwnerName = TableOwner(dependency->objectId);\n\t\t\t\treturn DDLCommandsForSequence(dependency->objectId, sequenceOwnerName);\n\t\t\t}\n\n\t\t\tif (relKind == RELKIND_VIEW)\n\t\t\t{\n\t\t\t\tchar *createViewCommand = CreateViewDDLCommand(dependency->objectId);\n\t\t\t\tchar *alterViewOwnerCommand = AlterViewOwnerCommand(dependency->objectId);\n\n\t\t\t\treturn list_make2(createViewCommand, alterViewOwnerCommand);\n\t\t\t}\n\n\t\t\t/* if this relation is not supported, break to the error at the end */\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OCLASS_COLLATION:\n\t\t{\n\t\t\treturn CreateCollationDDLsIdempotent(dependency->objectId);\n\t\t}\n\n\t\tcase OCLASS_CONSTRAINT:\n\t\t{\n\t\t\t/*\n\t\t\t * Constraints can only be reached by domains, they resolve functions.\n\t\t\t * Constraints themself are recreated by the domain recreation.\n\t\t\t */\n\t\t\treturn NIL;\n\t\t}\n\n\t\tcase OCLASS_DATABASE:\n\t\t{\n\t\t\t/*\n\t\t\t * For the database where Citus is installed, only propagate the ownership of the\n\t\t\t * database, only when the feature is on.\n\t\t\t *\n\t\t\t * This is because this database must exist on all nodes already so we shouldn't\n\t\t\t * need to \"CREATE\" it on other nodes. However, we still need to correctly reflect\n\t\t\t * its owner on other nodes too.\n\t\t\t */\n\t\t\tif (dependency->objectId == MyDatabaseId && EnableAlterDatabaseOwner)\n\t\t\t{\n\t\t\t\treturn DatabaseOwnerDDLCommands(dependency);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * For the other databases, create the database on all nodes, only when the feature\n\t\t\t * is on.\n\t\t\t */\n\t\t\tif (dependency->objectId != MyDatabaseId && EnableCreateDatabasePropagation)\n\t\t\t{\n\t\t\t\treturn GetDatabaseMetadataSyncCommands(dependency->objectId);\n\t\t\t}\n\n\t\t\treturn NIL;\n\t\t}\n\n\t\tcase OCLASS_PROC:\n\t\t{\n\t\t\tList *DDLCommands = CreateFunctionDDLCommandsIdempotent(dependency);\n\t\t\tList *grantDDLCommands = GrantOnFunctionDDLCommands(dependency->objectId);\n\t\t\tDDLCommands = list_concat(DDLCommands, grantDDLCommands);\n\t\t\treturn DDLCommands;\n\t\t}\n\n\t\tcase OCLASS_PUBLICATION:\n\t\t{\n\t\t\treturn CreatePublicationDDLCommandsIdempotent(dependency);\n\t\t}\n\n\t\tcase OCLASS_ROLE:\n\t\t{\n\t\t\treturn GenerateCreateOrAlterRoleCommand(dependency->objectId);\n\t\t}\n\n\t\tcase OCLASS_SCHEMA:\n\t\t{\n\t\t\tchar *schemaDDLCommand = CreateSchemaDDLCommand(dependency->objectId);\n\n\t\t\tList *DDLCommands = list_make1(schemaDDLCommand);\n\n\t\t\tList *grantDDLCommands = GrantOnSchemaDDLCommands(dependency->objectId);\n\n\t\t\tDDLCommands = list_concat(DDLCommands, grantDDLCommands);\n\n\t\t\treturn DDLCommands;\n\t\t}\n\n\t\tcase OCLASS_TSCONFIG:\n\t\t{\n\t\t\treturn CreateTextSearchConfigDDLCommandsIdempotent(dependency);\n\t\t}\n\n\t\tcase OCLASS_TSDICT:\n\t\t{\n\t\t\treturn CreateTextSearchDictDDLCommandsIdempotent(dependency);\n\t\t}\n\n\t\tcase OCLASS_TYPE:\n\t\t{\n\t\t\treturn CreateTypeDDLCommandsIdempotent(dependency);\n\t\t}\n\n\t\tcase OCLASS_EXTENSION:\n\t\t{\n\t\t\treturn CreateExtensionDDLCommand(dependency);\n\t\t}\n\n\t\tcase OCLASS_FOREIGN_SERVER:\n\t\t{\n\t\t\tOid serverId = dependency->objectId;\n\n\t\t\tList *DDLCommands = GetForeignServerCreateDDLCommand(serverId);\n\t\t\tList *grantDDLCommands = GrantOnForeignServerDDLCommands(serverId);\n\t\t\tDDLCommands = list_concat(DDLCommands, grantDDLCommands);\n\n\t\t\treturn DDLCommands;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/*\n\t * make sure it fails hard when in debug mode, leave a hint for the user if this ever\n\t * happens in production\n\t */\n\tAssert(false);\n\tereport(ERROR, (errmsg(\"unsupported object %s for distribution by citus\",\n\t\t\t\t\t\t   getObjectTypeDescription(dependency,\n\n\t                                                /* missingOk: */ false)),\n\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\"citus tries to recreate an unsupported object on its workers\"),\n\t\t\t\t\terrhint(\"please report a bug as this should not be happening\")));\n}\n\n\n/*\n * GetAllDependencyCreateDDLCommands iteratively calls GetDependencyCreateDDLCommands\n * for given dependencies.\n */\nList *\nGetAllDependencyCreateDDLCommands(const List *dependencies)\n{\n\tList *commands = NIL;\n\n\tObjectAddress *dependency = NULL;\n\tforeach_declared_ptr(dependency, dependencies)\n\t{\n\t\tcommands = list_concat(commands, GetDependencyCreateDDLCommands(dependency));\n\t}\n\n\treturn commands;\n}\n\n\n/*\n * ShouldPropagate determines if we should be propagating anything\n */\nbool\nShouldPropagate(void)\n{\n\tif (creating_extension)\n\t{\n\t\t/*\n\t\t * extensions should be created separately on the workers, types cascading from an\n\t\t * extension should therefore not be propagated.\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (!EnableMetadataSync)\n\t{\n\t\t/*\n\t\t * we are configured to disable object propagation, should not propagate anything\n\t\t */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ShouldPropagateCreateInCoordinatedTransction returns based the current state of the\n * session and policies if Citus needs to propagate the creation of new objects.\n *\n * Creation of objects on other nodes could be postponed till the object is actually used\n * in a sharded object (eg. distributed table or index on a distributed table). In certain\n * use cases the opportunity for parallelism in a transaction block is preferred. When\n * configured like that the creation of an object might be postponed and backfilled till\n * the object is actually used.\n */\nbool\nShouldPropagateCreateInCoordinatedTransction()\n{\n\tif (!IsMultiStatementTransaction())\n\t{\n\t\t/*\n\t\t * If we are in a single statement transaction we will always propagate the\n\t\t * creation of objects. There are no downsides in regard to performance or\n\t\t * transactional limitations. These only arise with transaction blocks consisting\n\t\t * of multiple statements.\n\t\t */\n\t\treturn true;\n\t}\n\n\tif (MultiShardConnectionType == SEQUENTIAL_CONNECTION)\n\t{\n\t\t/*\n\t\t * If we are in a transaction that is already switched to sequential, either by\n\t\t * the user, or automatically by an other command, we will always propagate the\n\t\t * creation of new objects to the workers.\n\t\t *\n\t\t * This guarantees no strange anomalies when the transaction aborts or on\n\t\t * visibility of the newly created object.\n\t\t */\n\t\treturn true;\n\t}\n\n\tswitch (CreateObjectPropagationMode)\n\t{\n\t\tcase CREATE_OBJECT_PROPAGATION_DEFERRED:\n\t\t{\n\t\t\t/*\n\t\t\t * We prefer parallelism at this point. Since we did not already return while\n\t\t\t * checking for sequential mode we are still in parallel mode. We don't want\n\t\t\t * to switch that now, thus not propagating the creation.\n\t\t\t */\n\t\t\treturn false;\n\t\t}\n\n\t\tcase CREATE_OBJECT_PROPAGATION_AUTOMATIC:\n\t\t{\n\t\t\t/*\n\t\t\t * When we run in optimistic mode we want to switch to sequential mode, only\n\t\t\t * if this would _not_ give an error to the user. Meaning, we either are\n\t\t\t * already in sequential mode (checked earlier), or there has been no parallel\n\t\t\t * execution in the current transaction block.\n\t\t\t *\n\t\t\t * If switching to sequential would throw an error we would stay in parallel\n\t\t\t * mode while creating new objects. We will rely on Citus' mechanism to ensure\n\t\t\t * the existence if the object would be used in the same transaction.\n\t\t\t */\n\t\t\tif (ParallelQueryExecutedInTransaction())\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tcase CREATE_OBJECT_PROPAGATION_IMMEDIATE:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"unsupported ddl propagation mode\");\n\t\t}\n\t}\n}\n\n\n/*\n * ShouldPropagateObject determines if we should be propagating DDLs based\n * on their object address.\n */\nstatic bool\nShouldPropagateObject(const ObjectAddress *address)\n{\n\tif (!ShouldPropagate())\n\t{\n\t\treturn false;\n\t}\n\n\tif (!IsAnyObjectDistributed(list_make1((ObjectAddress *) address)))\n\t{\n\t\t/* do not propagate for non-distributed types */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ShouldPropagateAnyObject determines if we should be propagating DDLs based\n * on their object addresses.\n */\nbool\nShouldPropagateAnyObject(List *addresses)\n{\n\tObjectAddress *address = NULL;\n\tforeach_declared_ptr(address, addresses)\n\t{\n\t\tif (ShouldPropagateObject(address))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * FilterObjectAddressListByPredicate takes a list of ObjectAddress *'s and returns a list\n * only containing the ObjectAddress *'s for which the predicate returned true.\n */\nList *\nFilterObjectAddressListByPredicate(List *objectAddressList, AddressPredicate predicate)\n{\n\tList *result = NIL;\n\n\tObjectAddress *address = NULL;\n\tforeach_declared_ptr(address, objectAddressList)\n\t{\n\t\tif (predicate(address))\n\t\t{\n\t\t\tresult = lappend(result, address);\n\t\t}\n\t}\n\n\treturn result;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/distribute_object_ops.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * distribute_object_ops.c\n *\n *    Contains declarations for DistributeObjectOps, along with their\n *    lookup function, GetDistributeObjectOps.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/comment.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/version_compat.h\"\n\nstatic DistributeObjectOps NoDistributeOps = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_NONE,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Aggregate_AlterObjectSchema = {\n\t.deparse = DeparseAlterFunctionSchemaStmt,\n\t.qualify = QualifyAlterFunctionSchemaStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Aggregate_AlterOwner = {\n\t.deparse = DeparseAlterFunctionOwnerStmt,\n\t.qualify = QualifyAlterFunctionOwnerStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionOwnerObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Aggregate_Define = {\n\t.deparse = NULL,\n\t.qualify = QualifyDefineAggregateStmt,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreateDistributedObjectFromCatalogStmt,\n\t.objectType = OBJECT_AGGREGATE,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = DefineAggregateStmtObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps Aggregate_Drop = {\n\t.deparse = DeparseDropFunctionStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Aggregate_Rename = {\n\t.deparse = DeparseRenameFunctionStmt,\n\t.qualify = QualifyRenameFunctionStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameFunctionStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Aggregate_Grant = {\n\t.deparse = DeparseGrantOnFunctionStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessGrantOnFunctionStmt,\n\t.postprocess = PostprocessGrantOnFunctionStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_AlterEnum = {\n\t.deparse = DeparseAlterEnumStmt,\n\t.qualify = QualifyAlterEnumStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_TYPE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterEnumStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_AlterExtension = {\n\t.deparse = DeparseAlterExtensionStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterExtensionUpdateStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterExtensionUpdateStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_AlterExtensionContents = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterExtensionContentsStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_AlterForeignServer = {\n\t.deparse = DeparseAlterForeignServerStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_FOREIGN_SERVER,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterForeignServerStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_AlterFunction = {\n\t.deparse = DeparseAlterFunctionStmt,\n\t.qualify = QualifyAlterFunctionStmt,\n\t.preprocess = PreprocessAlterFunctionStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_AlterPolicy = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterPolicyStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_AlterRole = {\n\t.deparse = DeparseAlterRoleStmt,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessAlterRoleStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterRoleStmtObjectAddress,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Any_AlterRoleRename = {\n\t.deparse = DeparseRenameRoleStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterRoleRenameStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameRoleStmtObjectAddress,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Any_AlterRoleSet = {\n\t.deparse = DeparseAlterRoleSetStmt,\n\t.qualify = QualifyAlterRoleSetStmt,\n\t.preprocess = PreprocessAlterRoleSetStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterRoleSetStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_AlterTableMoveAll = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterTableMoveAllStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_Cluster = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessClusterStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_NONE,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_CompositeType = {\n\t.deparse = DeparseCompositeTypeStmt,\n\t.qualify = QualifyCompositeTypeStmt,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreateDistributedObjectFromCatalogStmt,\n\t.objectType = OBJECT_TYPE,\n\t.operationType = DIST_OPS_CREATE,\n\t.featureFlag = &EnableCreateTypePropagation,\n\t.address = CompositeTypeStmtObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps Any_CreateDomain = {\n\t.deparse = DeparseCreateDomainStmt,\n\t.qualify = QualifyCreateDomainStmt,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreateDistributedObjectFromCatalogStmt,\n\t.objectType = OBJECT_DOMAIN,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreateDomainStmtObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps Any_CreateEnum = {\n\t.deparse = DeparseCreateEnumStmt,\n\t.qualify = QualifyCreateEnumStmt,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreateDistributedObjectFromCatalogStmt,\n\t.objectType = OBJECT_TYPE,\n\t.operationType = DIST_OPS_CREATE,\n\t.featureFlag = &EnableCreateTypePropagation,\n\t.address = CreateEnumStmtObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps Any_CreateExtension = {\n\t.deparse = DeparseCreateExtensionStmt,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreateExtensionStmt,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreateExtensionStmtObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps Any_CreateFunction = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessCreateFunctionStmt,\n\t.postprocess = PostprocessCreateFunctionStmt,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreateFunctionStmtObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps Any_View = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessViewStmt,\n\t.postprocess = PostprocessViewStmt,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = ViewStmtObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps Any_CreatePolicy = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreatePolicyStmt,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_CreatePublication = {\n\t.deparse = DeparseCreatePublicationStmt,\n\t.qualify = QualifyCreatePublicationStmt,\n\t.preprocess = NULL,\n\t.postprocess = PostProcessCreatePublicationStmt,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreatePublicationStmtObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps Any_CreateRole = {\n\t.deparse = DeparseCreateRoleStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessCreateRoleStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreateRoleStmtObjectAddress,\n\t.markDistributed = true,\n};\n\nstatic DistributeObjectOps Any_ReassignOwned = {\n\t.deparse = DeparseReassignOwnedStmt,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessReassignOwnedStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Any_DropOwned = {\n\t.deparse = DeparseDropOwnedStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropOwnedStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_DropRole = {\n\t.deparse = DeparseDropRoleStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropRoleStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Role_Comment = {\n\t.deparse = DeparseCommentStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_DATABASE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = CommentObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_CreateForeignServer = {\n\t.deparse = DeparseCreateForeignServerStmt,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreateDistributedObjectFromCatalogStmt,\n\t.objectType = OBJECT_FOREIGN_SERVER,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreateForeignServerStmtObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps Any_CreateSchema = {\n\t.deparse = DeparseCreateSchemaStmt,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreateSchemaStmt,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreateSchemaStmtObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps Any_CreateStatistics = {\n\t.deparse = DeparseCreateStatisticsStmt,\n\t.qualify = QualifyCreateStatisticsStmt,\n\t.preprocess = PreprocessCreateStatisticsStmt,\n\t.postprocess = PostprocessCreateStatisticsStmt,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreateStatisticsStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_CreateTrigger = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreateTriggerStmt,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreateTriggerStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_Grant = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessGrantStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_GrantRole = {\n\t.deparse = DeparseGrantRoleStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessGrantRoleStmt,\n\t.postprocess = PostprocessGrantRoleStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_Index = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessIndexStmt,\n\t.postprocess = PostprocessIndexStmt,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_Reindex = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessReindexStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_NONE,\n\t.address = ReindexStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_Rename = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessRenameStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Any_SecLabel = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessAnySecLabelStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = SecLabelStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Role_SecLabel = {\n\t.deparse = DeparseRoleSecLabelStmt,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessRoleSecLabelStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = SecLabelStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Table_SecLabel = {\n\t.deparse = DeparseTableSecLabelStmt,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessTableOrColumnSecLabelStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = SecLabelStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Column_SecLabel = {\n\t.deparse = DeparseColumnSecLabelStmt,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessTableOrColumnSecLabelStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = SecLabelStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Attribute_Rename = {\n\t.deparse = DeparseRenameAttributeStmt,\n\t.qualify = QualifyRenameAttributeStmt,\n\t.preprocess = PreprocessRenameAttributeStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameAttributeStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Collation_AlterObjectSchema = {\n\t.deparse = DeparseAlterCollationSchemaStmt,\n\t.qualify = QualifyAlterCollationSchemaStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_COLLATION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterCollationSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Collation_AlterOwner = {\n\t.deparse = DeparseAlterCollationOwnerStmt,\n\t.qualify = QualifyAlterCollationOwnerStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_COLLATION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterCollationOwnerObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Collation_Define = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreateDistributedObjectFromCatalogStmt,\n\t.objectType = OBJECT_COLLATION,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = DefineCollationStmtObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps Collation_Drop = {\n\t.deparse = DeparseDropCollationStmt,\n\t.qualify = QualifyDropCollationStmt,\n\t.preprocess = PreprocessDropDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Collation_Rename = {\n\t.deparse = DeparseRenameCollationStmt,\n\t.qualify = QualifyRenameCollationStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_COLLATION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameCollationStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Database_AlterOwner = {\n\t.deparse = DeparseAlterDatabaseOwnerStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_DATABASE,\n\t.operationType = DIST_OPS_ALTER,\n\t.featureFlag = &EnableAlterDatabaseOwner,\n\t.address = AlterDatabaseOwnerObjectAddress,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Database_Grant = {\n\t.deparse = DeparseGrantOnDatabaseStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessGrantOnDatabaseStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_DATABASE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Database_Alter = {\n\t.deparse = DeparseAlterDatabaseStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDatabaseStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_DATABASE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Database_Create = {\n\t.deparse = DeparseCreateDatabaseStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessCreateDatabaseStmt,\n\t.postprocess = PostprocessCreateDatabaseStmt,\n\t.objectType = OBJECT_DATABASE,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreateDatabaseStmtObjectAddress,\n\t.markDistributed = true,\n};\n\nstatic DistributeObjectOps Database_Drop = {\n\t.deparse = DeparseDropDatabaseStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropDatabaseStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_DATABASE,\n\t.operationType = DIST_OPS_DROP,\n\t.address = DropDatabaseStmtObjectAddress,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Database_RefreshColl = {\n\t.deparse = DeparseAlterDatabaseRefreshCollStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDatabaseRefreshCollStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_DATABASE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Database_Set = {\n\t.deparse = DeparseAlterDatabaseSetStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDatabaseSetStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_DATABASE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Database_Comment = {\n\t.deparse = DeparseCommentStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_DATABASE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = CommentObjectAddress,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Database_Rename = {\n\t.deparse = DeparseAlterDatabaseRenameStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDatabaseRenameStmt,\n\t.postprocess = PostprocessAlterDatabaseRenameStmt,\n\t.objectType = OBJECT_DATABASE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Domain_Alter = {\n\t.deparse = DeparseAlterDomainStmt,\n\t.qualify = QualifyAlterDomainStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_DOMAIN,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterDomainStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Domain_AlterObjectSchema = {\n\t.deparse = DeparseAlterDomainSchemaStmt,\n\t.qualify = QualifyAlterDomainSchemaStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_DOMAIN,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterTypeSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Domain_AlterOwner = {\n\t.deparse = DeparseAlterDomainOwnerStmt,\n\t.qualify = QualifyAlterDomainOwnerStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_DOMAIN,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterDomainOwnerStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Domain_Drop = {\n\t.deparse = DeparseDropDomainStmt,\n\t.qualify = QualifyDropDomainStmt,\n\t.preprocess = PreprocessDropDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Domain_Rename = {\n\t.deparse = DeparseRenameDomainStmt,\n\t.qualify = QualifyRenameDomainStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_DOMAIN,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameDomainStmtObjectAddress,\n\t.markDistributed = false,\n};\n\nstatic DistributeObjectOps Domain_RenameConstraint = {\n\t.deparse = DeparseDomainRenameConstraintStmt,\n\t.qualify = QualifyDomainRenameConstraintStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_DOMAIN,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = DomainRenameConstraintStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Extension_AlterObjectSchema = {\n\t.deparse = DeparseAlterExtensionSchemaStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterExtensionSchemaStmt,\n\t.postprocess = PostprocessAlterExtensionSchemaStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterExtensionSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Extension_Drop = {\n\t.deparse = DeparseDropExtensionStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropExtensionStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps FDW_Grant = {\n\t.deparse = DeparseGrantOnFDWStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessGrantOnFDWStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps ForeignServer_Drop = {\n\t.deparse = DeparseDropForeignServerStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps ForeignServer_Grant = {\n\t.deparse = DeparseGrantOnForeignServerStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessGrantOnForeignServerStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps ForeignServer_Rename = {\n\t.deparse = DeparseAlterForeignServerRenameStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_FOREIGN_SERVER,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameForeignServerStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps ForeignServer_AlterOwner = {\n\t.deparse = DeparseAlterForeignServerOwnerStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_FOREIGN_SERVER,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterForeignServerOwnerStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps ForeignTable_AlterTable = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterTableStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Function_AlterObjectDepends = {\n\t.deparse = DeparseAlterFunctionDependsStmt,\n\t.qualify = QualifyAlterFunctionDependsStmt,\n\t.preprocess = PreprocessAlterFunctionDependsStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionDependsStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Function_AlterObjectSchema = {\n\t.deparse = DeparseAlterFunctionSchemaStmt,\n\t.qualify = QualifyAlterFunctionSchemaStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Function_AlterOwner = {\n\t.deparse = DeparseAlterFunctionOwnerStmt,\n\t.qualify = QualifyAlterFunctionOwnerStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionOwnerObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Function_Drop = {\n\t.deparse = DeparseDropFunctionStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Function_Grant = {\n\t.deparse = DeparseGrantOnFunctionStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessGrantOnFunctionStmt,\n\t.postprocess = PostprocessGrantOnFunctionStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps View_Drop = {\n\t.deparse = DeparseDropViewStmt,\n\t.qualify = QualifyDropViewStmt,\n\t.preprocess = PreprocessDropViewStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = DropViewStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Function_Rename = {\n\t.deparse = DeparseRenameFunctionStmt,\n\t.qualify = QualifyRenameFunctionStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameFunctionStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Index_AlterTable = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterTableStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Index_Drop = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropIndexStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Policy_Drop = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropPolicyStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Procedure_AlterObjectDepends = {\n\t.deparse = DeparseAlterFunctionDependsStmt,\n\t.qualify = QualifyAlterFunctionDependsStmt,\n\t.preprocess = PreprocessAlterFunctionDependsStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionDependsStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Procedure_AlterObjectSchema = {\n\t.deparse = DeparseAlterFunctionSchemaStmt,\n\t.qualify = QualifyAlterFunctionSchemaStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Procedure_AlterOwner = {\n\t.deparse = DeparseAlterFunctionOwnerStmt,\n\t.qualify = QualifyAlterFunctionOwnerStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionOwnerObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Procedure_Drop = {\n\t.deparse = DeparseDropFunctionStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Procedure_Grant = {\n\t.deparse = DeparseGrantOnFunctionStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessGrantOnFunctionStmt,\n\t.postprocess = PostprocessGrantOnFunctionStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Procedure_Rename = {\n\t.deparse = DeparseRenameFunctionStmt,\n\t.qualify = QualifyRenameFunctionStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameFunctionStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Publication_Alter = {\n\t.deparse = DeparseAlterPublicationStmt,\n\t.qualify = QualifyAlterPublicationStmt,\n\t.preprocess = PreprocessAlterPublicationStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_PUBLICATION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterPublicationStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Publication_AlterOwner = {\n\t.deparse = DeparseAlterPublicationOwnerStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_PUBLICATION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterPublicationOwnerStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Publication_Drop = {\n\t.deparse = DeparseDropPublicationStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Publication_Rename = {\n\t.deparse = DeparseRenamePublicationStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_PUBLICATION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenamePublicationStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Routine_AlterObjectDepends = {\n\t.deparse = DeparseAlterFunctionDependsStmt,\n\t.qualify = QualifyAlterFunctionDependsStmt,\n\t.preprocess = PreprocessAlterFunctionDependsStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionDependsStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Sequence_Alter = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterSequenceStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterSequenceStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Sequence_AlterObjectSchema = {\n\t.deparse = DeparseAlterSequenceSchemaStmt,\n\t.qualify = QualifyAlterSequenceSchemaStmt,\n\t.preprocess = PreprocessAlterSequenceSchemaStmt,\n\t.postprocess = PostprocessAlterSequenceSchemaStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterSequenceSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Sequence_AlterOwner = {\n\t.deparse = DeparseAlterSequenceOwnerStmt,\n\t.qualify = QualifyAlterSequenceOwnerStmt,\n\t.preprocess = PreprocessAlterSequenceOwnerStmt,\n\t.postprocess = PostprocessAlterSequenceOwnerStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterSequenceOwnerStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Sequence_AlterPersistence = {\n\t.deparse = DeparseAlterSequencePersistenceStmt,\n\t.qualify = QualifyAlterSequencePersistenceStmt,\n\t.preprocess = PreprocessAlterSequencePersistenceStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterSequencePersistenceStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Sequence_Drop = {\n\t.deparse = DeparseDropSequenceStmt,\n\t.qualify = QualifyDropSequenceStmt,\n\t.preprocess = PreprocessDropSequenceStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = SequenceDropStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Sequence_Grant = {\n\t.deparse = DeparseGrantOnSequenceStmt,\n\t.qualify = QualifyGrantOnSequenceStmt,\n\t.preprocess = PreprocessGrantOnSequenceStmt,\n\t.postprocess = PostprocessGrantOnSequenceStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Sequence_Rename = {\n\t.deparse = DeparseRenameSequenceStmt,\n\t.qualify = QualifyRenameSequenceStmt,\n\t.preprocess = PreprocessRenameSequenceStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameSequenceStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchConfig_Alter = {\n\t.deparse = DeparseAlterTextSearchConfigurationStmt,\n\t.qualify = QualifyAlterTextSearchConfigurationStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_TSCONFIGURATION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterTextSearchConfigurationStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchConfig_AlterObjectSchema = {\n\t.deparse = DeparseAlterTextSearchConfigurationSchemaStmt,\n\t.qualify = QualifyAlterTextSearchConfigurationSchemaStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_TSCONFIGURATION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterTextSearchConfigurationSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchConfig_AlterOwner = {\n\t.deparse = DeparseAlterTextSearchConfigurationOwnerStmt,\n\t.qualify = QualifyAlterTextSearchConfigurationOwnerStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_TSCONFIGURATION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterTextSearchConfigurationOwnerObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchConfig_Comment = {\n\t.deparse = DeparseCommentStmt,\n\n\t/* TODO: When adding new comment types we should create an abstracted\n\t * qualify function, just like we have an abstract deparse\n\t * and adress function\n\t */\n\t.qualify = QualifyTextSearchConfigurationCommentStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_TSCONFIGURATION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = CommentObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchConfig_Define = {\n\t.deparse = DeparseCreateTextSearchConfigurationStmt,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreateDistributedObjectFromCatalogStmt,\n\t.objectType = OBJECT_TSCONFIGURATION,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreateTextSearchConfigurationObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps TextSearchConfig_Drop = {\n\t.deparse = DeparseDropTextSearchConfigurationStmt,\n\t.qualify = QualifyDropTextSearchConfigurationStmt,\n\t.preprocess = PreprocessDropDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = DropTextSearchConfigObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchConfig_Rename = {\n\t.deparse = DeparseRenameTextSearchConfigurationStmt,\n\t.qualify = QualifyRenameTextSearchConfigurationStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_TSCONFIGURATION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameTextSearchConfigurationStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchDict_Alter = {\n\t.deparse = DeparseAlterTextSearchDictionaryStmt,\n\t.qualify = QualifyAlterTextSearchDictionaryStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_TSDICTIONARY,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterTextSearchDictionaryStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchDict_AlterObjectSchema = {\n\t.deparse = DeparseAlterTextSearchDictionarySchemaStmt,\n\t.qualify = QualifyAlterTextSearchDictionarySchemaStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_TSDICTIONARY,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterTextSearchDictionarySchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchDict_AlterOwner = {\n\t.deparse = DeparseAlterTextSearchDictionaryOwnerStmt,\n\t.qualify = QualifyAlterTextSearchDictionaryOwnerStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_TSDICTIONARY,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterTextSearchDictOwnerObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchDict_Comment = {\n\t.deparse = DeparseCommentStmt,\n\t.qualify = QualifyTextSearchDictionaryCommentStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_TSDICTIONARY,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = CommentObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchDict_Define = {\n\t.deparse = DeparseCreateTextSearchDictionaryStmt,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessCreateDistributedObjectFromCatalogStmt,\n\t.objectType = OBJECT_TSDICTIONARY,\n\t.operationType = DIST_OPS_CREATE,\n\t.address = CreateTextSearchDictObjectAddress,\n\t.markDistributed = true,\n};\nstatic DistributeObjectOps TextSearchDict_Drop = {\n\t.deparse = DeparseDropTextSearchDictionaryStmt,\n\t.qualify = QualifyDropTextSearchDictionaryStmt,\n\t.preprocess = PreprocessDropDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = DropTextSearchDictObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps TextSearchDict_Rename = {\n\t.deparse = DeparseRenameTextSearchDictionaryStmt,\n\t.qualify = QualifyRenameTextSearchDictionaryStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_TSDICTIONARY,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameTextSearchDictionaryStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Trigger_AlterObjectDepends = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterTriggerDependsStmt,\n\t.postprocess = PostprocessAlterTriggerDependsStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Routine_AlterObjectSchema = {\n\t.deparse = DeparseAlterFunctionSchemaStmt,\n\t.qualify = QualifyAlterFunctionSchemaStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Routine_AlterOwner = {\n\t.deparse = DeparseAlterFunctionOwnerStmt,\n\t.qualify = QualifyAlterFunctionOwnerStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterFunctionOwnerObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Routine_Drop = {\n\t.deparse = DeparseDropFunctionStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Routine_Grant = {\n\t.deparse = DeparseGrantOnFunctionStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessGrantOnFunctionStmt,\n\t.postprocess = PostprocessGrantOnFunctionStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Routine_Rename = {\n\t.deparse = DeparseRenameFunctionStmt,\n\t.qualify = QualifyRenameFunctionStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_FUNCTION,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameFunctionStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Schema_AlterOwner = {\n\t.deparse = DeparseAlterSchemaOwnerStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.postprocess = NULL,\n\t.address = AlterSchemaOwnerStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Schema_Drop = {\n\t.deparse = DeparseDropSchemaStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropSchemaStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Schema_Grant = {\n\t.deparse = DeparseGrantOnSchemaStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessGrantOnSchemaStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Schema_Rename = {\n\t.deparse = DeparseAlterSchemaRenameStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_SCHEMA,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterSchemaRenameStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Statistics_Alter = {\n\t.deparse = DeparseAlterStatisticsStmt,\n\t.qualify = QualifyAlterStatisticsStmt,\n\t.preprocess = PreprocessAlterStatisticsStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Statistics_AlterObjectSchema = {\n\t.deparse = DeparseAlterStatisticsSchemaStmt,\n\t.qualify = QualifyAlterStatisticsSchemaStmt,\n\t.preprocess = PreprocessAlterStatisticsSchemaStmt,\n\t.postprocess = PostprocessAlterStatisticsSchemaStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterStatisticsSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Statistics_AlterOwner = {\n\t.deparse = DeparseAlterStatisticsOwnerStmt,\n\t.qualify = QualifyAlterStatisticsOwnerStmt,\n\t.preprocess = PreprocessAlterStatisticsOwnerStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.postprocess = PostprocessAlterStatisticsOwnerStmt,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Statistics_Drop = {\n\t.deparse = NULL,\n\t.qualify = QualifyDropStatisticsStmt,\n\t.preprocess = PreprocessDropStatisticsStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = DropStatisticsObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Statistics_Rename = {\n\t.deparse = DeparseAlterStatisticsRenameStmt,\n\t.qualify = QualifyAlterStatisticsRenameStmt,\n\t.preprocess = PreprocessAlterStatisticsRenameStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Table_AlterTable = {\n\t.deparse = DeparseAlterTableStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessAlterTableStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Table_AlterObjectSchema = {\n\t.deparse = DeparseAlterTableSchemaStmt,\n\t.qualify = QualifyAlterTableSchemaStmt,\n\t.preprocess = PreprocessAlterTableSchemaStmt,\n\t.postprocess = PostprocessAlterTableSchemaStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterTableSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Table_Drop = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropTableStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Type_AlterObjectSchema = {\n\t.deparse = DeparseAlterTypeSchemaStmt,\n\t.qualify = QualifyAlterTypeSchemaStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_TYPE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterTypeSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\n\n/*\n * PreprocessAlterViewSchemaStmt and PostprocessAlterViewSchemaStmt functions can be called\n * internally by ALTER TABLE view_name SET SCHEMA ... if the ALTER TABLE command targets a\n * view. In other words ALTER VIEW view_name SET SCHEMA will use the View_AlterObjectSchema\n * but ALTER TABLE view_name SET SCHEMA will use Table_AlterObjectSchema but call process\n * functions of View_AlterObjectSchema internally.\n */\nstatic DistributeObjectOps View_AlterObjectSchema = {\n\t.deparse = DeparseAlterViewSchemaStmt,\n\t.qualify = QualifyAlterViewSchemaStmt,\n\t.preprocess = PreprocessAlterViewSchemaStmt,\n\t.postprocess = PostprocessAlterViewSchemaStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterViewSchemaStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Type_AlterOwner = {\n\t.deparse = DeparseAlterTypeOwnerStmt,\n\t.qualify = QualifyAlterTypeOwnerStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = PostprocessAlterDistributedObjectStmt,\n\t.objectType = OBJECT_TYPE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterTypeOwnerObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Type_AlterTable = {\n\t.deparse = DeparseAlterTypeStmt,\n\t.qualify = QualifyAlterTypeStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_TYPE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterTypeStmtObjectAddress,\n\t.markDistributed = false,\n};\n\n/*\n * PreprocessAlterViewStmt and PostprocessAlterViewStmt functions can be called internally\n * by ALTER TABLE view_name SET/RESET ... if the ALTER TABLE command targets a view. In\n * other words ALTER VIEW view_name SET/RESET will use the View_AlterView\n * but ALTER TABLE view_name SET/RESET will use Table_AlterTable but call process\n * functions of View_AlterView internally.\n */\nstatic DistributeObjectOps View_AlterView = {\n\t.deparse = DeparseAlterViewStmt,\n\t.qualify = QualifyAlterViewStmt,\n\t.preprocess = PreprocessAlterViewStmt,\n\t.postprocess = PostprocessAlterViewStmt,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = AlterViewStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Type_Drop = {\n\t.deparse = DeparseDropTypeStmt,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Trigger_Drop = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = PreprocessDropTriggerStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_DROP,\n\t.address = NULL,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Type_Rename = {\n\t.deparse = DeparseRenameTypeStmt,\n\t.qualify = QualifyRenameTypeStmt,\n\t.preprocess = PreprocessAlterDistributedObjectStmt,\n\t.postprocess = NULL,\n\t.objectType = OBJECT_TYPE,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameTypeStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Vacuum_Analyze = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.postprocess = PostprocessVacuumStmt,\n\t.operationType = DIST_OPS_NONE,\n\t.address = NULL,\n\t.markDistributed = false,\n};\n\n/*\n * PreprocessRenameViewStmt function can be called internally by ALTER TABLE view_name\n * RENAME ... if the ALTER TABLE command targets a view or a view's column. In other words\n * ALTER VIEW view_name RENAME will use the View_Rename but ALTER TABLE view_name RENAME\n * will use Any_Rename but call process functions of View_Rename internally.\n */\nstatic DistributeObjectOps View_Rename = {\n\t.deparse = DeparseRenameViewStmt,\n\t.qualify = QualifyRenameViewStmt,\n\t.preprocess = PreprocessRenameViewStmt,\n\t.postprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.address = RenameViewStmtObjectAddress,\n\t.markDistributed = false,\n};\nstatic DistributeObjectOps Trigger_Rename = {\n\t.deparse = NULL,\n\t.qualify = NULL,\n\t.preprocess = NULL,\n\t.operationType = DIST_OPS_ALTER,\n\t.postprocess = PostprocessAlterTriggerRenameStmt,\n\t.address = NULL,\n\t.markDistributed = false,\n};\n\n/*\n * GetDistributeObjectOps looks up the DistributeObjectOps which handles the node.\n *\n * Never returns NULL.\n */\nconst DistributeObjectOps *\nGetDistributeObjectOps(Node *node)\n{\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_AlterDatabaseStmt:\n\t\t{\n\t\t\treturn &Database_Alter;\n\t\t}\n\n\t\tcase T_CreatedbStmt:\n\t\t{\n\t\t\treturn &Database_Create;\n\t\t}\n\n\t\tcase T_DropdbStmt:\n\t\t{\n\t\t\treturn &Database_Drop;\n\t\t}\n\n\t\tcase T_AlterDatabaseRefreshCollStmt:\n\t\t{\n\t\t\treturn &Database_RefreshColl;\n\t\t}\n\n\t\tcase T_AlterDatabaseSetStmt:\n\t\t{\n\t\t\treturn &Database_Set;\n\t\t}\n\n\n\t\tcase T_AlterDomainStmt:\n\t\t{\n\t\t\treturn &Domain_Alter;\n\t\t}\n\n\t\tcase T_AlterEnumStmt:\n\t\t{\n\t\t\treturn &Any_AlterEnum;\n\t\t}\n\n\t\tcase T_AlterExtensionStmt:\n\t\t{\n\t\t\treturn &Any_AlterExtension;\n\t\t}\n\n\t\tcase T_AlterExtensionContentsStmt:\n\t\t{\n\t\t\treturn &Any_AlterExtensionContents;\n\t\t}\n\n\t\tcase T_AlterFunctionStmt:\n\t\t{\n\t\t\treturn &Any_AlterFunction;\n\t\t}\n\n\t\tcase T_AlterForeignServerStmt:\n\t\t{\n\t\t\treturn &Any_AlterForeignServer;\n\t\t}\n\n\t\tcase T_AlterObjectDependsStmt:\n\t\t{\n\t\t\tAlterObjectDependsStmt *stmt = castNode(AlterObjectDependsStmt, node);\n\t\t\tswitch (stmt->objectType)\n\t\t\t{\n\t\t\t\tcase OBJECT_FUNCTION:\n\t\t\t\t{\n\t\t\t\t\treturn &Function_AlterObjectDepends;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_PROCEDURE:\n\t\t\t\t{\n\t\t\t\t\treturn &Procedure_AlterObjectDepends;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_ROUTINE:\n\t\t\t\t{\n\t\t\t\t\treturn &Routine_AlterObjectDepends;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TRIGGER:\n\t\t\t\t{\n\t\t\t\t\treturn &Trigger_AlterObjectDepends;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\treturn &NoDistributeOps;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcase T_AlterObjectSchemaStmt:\n\t\t{\n\t\t\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\t\t\tswitch (stmt->objectType)\n\t\t\t{\n\t\t\t\tcase OBJECT_AGGREGATE:\n\t\t\t\t{\n\t\t\t\t\treturn &Aggregate_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_COLLATION:\n\t\t\t\t{\n\t\t\t\t\treturn &Collation_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_DOMAIN:\n\t\t\t\t{\n\t\t\t\t\treturn &Domain_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_EXTENSION:\n\t\t\t\t{\n\t\t\t\t\treturn &Extension_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FUNCTION:\n\t\t\t\t{\n\t\t\t\t\treturn &Function_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_PROCEDURE:\n\t\t\t\t{\n\t\t\t\t\treturn &Procedure_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_ROUTINE:\n\t\t\t\t{\n\t\t\t\t\treturn &Routine_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_SEQUENCE:\n\t\t\t\t{\n\t\t\t\t\treturn &Sequence_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_STATISTIC_EXT:\n\t\t\t\t{\n\t\t\t\t\treturn &Statistics_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FOREIGN_TABLE:\n\t\t\t\tcase OBJECT_TABLE:\n\t\t\t\t{\n\t\t\t\t\treturn &Table_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TSCONFIGURATION:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchConfig_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TSDICTIONARY:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchDict_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TYPE:\n\t\t\t\t{\n\t\t\t\t\treturn &Type_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_VIEW:\n\t\t\t\t{\n\t\t\t\t\treturn &View_AlterObjectSchema;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\treturn &NoDistributeOps;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcase T_AlterOwnerStmt:\n\t\t{\n\t\t\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\t\t\tswitch (stmt->objectType)\n\t\t\t{\n\t\t\t\tcase OBJECT_AGGREGATE:\n\t\t\t\t{\n\t\t\t\t\treturn &Aggregate_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_COLLATION:\n\t\t\t\t{\n\t\t\t\t\treturn &Collation_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_DATABASE:\n\t\t\t\t{\n\t\t\t\t\treturn &Database_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_DOMAIN:\n\t\t\t\t{\n\t\t\t\t\treturn &Domain_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FOREIGN_SERVER:\n\t\t\t\t{\n\t\t\t\t\treturn &ForeignServer_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FUNCTION:\n\t\t\t\t{\n\t\t\t\t\treturn &Function_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_PROCEDURE:\n\t\t\t\t{\n\t\t\t\t\treturn &Procedure_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_PUBLICATION:\n\t\t\t\t{\n\t\t\t\t\treturn &Publication_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_ROUTINE:\n\t\t\t\t{\n\t\t\t\t\treturn &Routine_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_SCHEMA:\n\t\t\t\t{\n\t\t\t\t\treturn &Schema_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_STATISTIC_EXT:\n\t\t\t\t{\n\t\t\t\t\treturn &Statistics_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TSCONFIGURATION:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchConfig_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TSDICTIONARY:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchDict_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TYPE:\n\t\t\t\t{\n\t\t\t\t\treturn &Type_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\treturn &NoDistributeOps;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcase T_AlterPolicyStmt:\n\t\t{\n\t\t\treturn &Any_AlterPolicy;\n\t\t}\n\n\t\tcase T_AlterPublicationStmt:\n\t\t{\n\t\t\treturn &Publication_Alter;\n\t\t}\n\n\t\tcase T_AlterRoleStmt:\n\t\t{\n\t\t\treturn &Any_AlterRole;\n\t\t}\n\n\t\tcase T_AlterRoleSetStmt:\n\t\t{\n\t\t\treturn &Any_AlterRoleSet;\n\t\t}\n\n\t\tcase T_AlterSeqStmt:\n\t\t{\n\t\t\treturn &Sequence_Alter;\n\t\t}\n\n\t\tcase T_AlterStatsStmt:\n\t\t{\n\t\t\treturn &Statistics_Alter;\n\t\t}\n\n\t\tcase T_AlterTableStmt:\n\t\t{\n\t\t\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\t\t\tswitch (stmt->objtype)\n\t\t\t{\n\t\t\t\tcase OBJECT_TYPE:\n\t\t\t\t{\n\t\t\t\t\treturn &Type_AlterTable;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TABLE:\n\t\t\t\t{\n\t\t\t\t\treturn &Table_AlterTable;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FOREIGN_TABLE:\n\t\t\t\t{\n\t\t\t\t\treturn &ForeignTable_AlterTable;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_INDEX:\n\t\t\t\t{\n\t\t\t\t\treturn &Index_AlterTable;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_SEQUENCE:\n\t\t\t\t{\n\t\t\t\t\tListCell *cmdCell = NULL;\n\t\t\t\t\tforeach(cmdCell, stmt->cmds)\n\t\t\t\t\t{\n\t\t\t\t\t\tAlterTableCmd *cmd = castNode(AlterTableCmd, lfirst(cmdCell));\n\t\t\t\t\t\tswitch (cmd->subtype)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase AT_ChangeOwner:\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn &Sequence_AlterOwner;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcase AT_SetLogged:\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn &Sequence_AlterPersistence;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tcase AT_SetUnLogged:\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn &Sequence_AlterPersistence;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\treturn &NoDistributeOps;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Prior to PG15, the only Alter Table statement\n\t\t\t\t\t * with Sequence as its object was an\n\t\t\t\t\t * Alter Owner statement\n\t\t\t\t\t */\n\t\t\t\t\treturn &Sequence_AlterOwner;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_VIEW:\n\t\t\t\t{\n\t\t\t\t\treturn &View_AlterView;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\treturn &NoDistributeOps;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcase T_AlterTableMoveAllStmt:\n\t\t{\n\t\t\treturn &Any_AlterTableMoveAll;\n\t\t}\n\n\t\tcase T_AlterTSConfigurationStmt:\n\t\t{\n\t\t\treturn &TextSearchConfig_Alter;\n\t\t}\n\n\t\tcase T_AlterTSDictionaryStmt:\n\t\t{\n\t\t\treturn &TextSearchDict_Alter;\n\t\t}\n\n\t\tcase T_ClusterStmt:\n\t\t{\n\t\t\treturn &Any_Cluster;\n\t\t}\n\n\t\tcase T_CommentStmt:\n\t\t{\n\t\t\tCommentStmt *stmt = castNode(CommentStmt, node);\n\t\t\tswitch (stmt->objtype)\n\t\t\t{\n\t\t\t\tcase OBJECT_TSCONFIGURATION:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchConfig_Comment;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TSDICTIONARY:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchDict_Comment;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_DATABASE:\n\t\t\t\t{\n\t\t\t\t\treturn &Database_Comment;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_ROLE:\n\t\t\t\t{\n\t\t\t\t\treturn &Role_Comment;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\treturn &NoDistributeOps;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcase T_CompositeTypeStmt:\n\t\t{\n\t\t\treturn &Any_CompositeType;\n\t\t}\n\n\t\tcase T_CreateDomainStmt:\n\t\t{\n\t\t\treturn &Any_CreateDomain;\n\t\t}\n\n\t\tcase T_CreateEnumStmt:\n\t\t{\n\t\t\treturn &Any_CreateEnum;\n\t\t}\n\n\t\tcase T_CreateExtensionStmt:\n\t\t{\n\t\t\treturn &Any_CreateExtension;\n\t\t}\n\n\t\tcase T_CreateFunctionStmt:\n\t\t{\n\t\t\treturn &Any_CreateFunction;\n\t\t}\n\n\t\tcase T_CreateForeignServerStmt:\n\t\t{\n\t\t\treturn &Any_CreateForeignServer;\n\t\t}\n\n\t\tcase T_CreatePolicyStmt:\n\t\t{\n\t\t\treturn &Any_CreatePolicy;\n\t\t}\n\n\t\tcase T_CreatePublicationStmt:\n\t\t{\n\t\t\treturn &Any_CreatePublication;\n\t\t}\n\n\t\tcase T_CreateRoleStmt:\n\t\t{\n\t\t\treturn &Any_CreateRole;\n\t\t}\n\n\t\tcase T_CreateSchemaStmt:\n\t\t{\n\t\t\treturn &Any_CreateSchema;\n\t\t}\n\n\t\tcase T_CreateStatsStmt:\n\t\t{\n\t\t\treturn &Any_CreateStatistics;\n\t\t}\n\n\t\tcase T_CreateTrigStmt:\n\t\t{\n\t\t\treturn &Any_CreateTrigger;\n\t\t}\n\n\t\tcase T_DefineStmt:\n\t\t{\n\t\t\tDefineStmt *stmt = castNode(DefineStmt, node);\n\t\t\tswitch (stmt->kind)\n\t\t\t{\n\t\t\t\tcase OBJECT_AGGREGATE:\n\t\t\t\t{\n\t\t\t\t\treturn &Aggregate_Define;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_COLLATION:\n\t\t\t\t{\n\t\t\t\t\treturn &Collation_Define;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TSCONFIGURATION:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchConfig_Define;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TSDICTIONARY:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchDict_Define;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\treturn &NoDistributeOps;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcase T_DropRoleStmt:\n\t\t{\n\t\t\treturn &Any_DropRole;\n\t\t}\n\n\t\tcase T_DropOwnedStmt:\n\t\t{\n\t\t\treturn &Any_DropOwned;\n\t\t}\n\n\t\tcase T_ReassignOwnedStmt:\n\t\t{\n\t\t\treturn &Any_ReassignOwned;\n\t\t}\n\n\t\tcase T_DropStmt:\n\t\t{\n\t\t\tDropStmt *stmt = castNode(DropStmt, node);\n\t\t\tswitch (stmt->removeType)\n\t\t\t{\n\t\t\t\tcase OBJECT_AGGREGATE:\n\t\t\t\t{\n\t\t\t\t\treturn &Aggregate_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_COLLATION:\n\t\t\t\t{\n\t\t\t\t\treturn &Collation_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_DOMAIN:\n\t\t\t\t{\n\t\t\t\t\treturn &Domain_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_EXTENSION:\n\t\t\t\t{\n\t\t\t\t\treturn &Extension_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FUNCTION:\n\t\t\t\t{\n\t\t\t\t\treturn &Function_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FOREIGN_SERVER:\n\t\t\t\t{\n\t\t\t\t\treturn &ForeignServer_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_INDEX:\n\t\t\t\t{\n\t\t\t\t\treturn &Index_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_POLICY:\n\t\t\t\t{\n\t\t\t\t\treturn &Policy_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_PROCEDURE:\n\t\t\t\t{\n\t\t\t\t\treturn &Procedure_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_PUBLICATION:\n\t\t\t\t{\n\t\t\t\t\treturn &Publication_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_ROUTINE:\n\t\t\t\t{\n\t\t\t\t\treturn &Routine_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_SCHEMA:\n\t\t\t\t{\n\t\t\t\t\treturn &Schema_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_SEQUENCE:\n\t\t\t\t{\n\t\t\t\t\treturn &Sequence_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_STATISTIC_EXT:\n\t\t\t\t{\n\t\t\t\t\treturn &Statistics_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TABLE:\n\t\t\t\t{\n\t\t\t\t\treturn &Table_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TSCONFIGURATION:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchConfig_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TSDICTIONARY:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchDict_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TYPE:\n\t\t\t\t{\n\t\t\t\t\treturn &Type_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TRIGGER:\n\t\t\t\t{\n\t\t\t\t\treturn &Trigger_Drop;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_VIEW:\n\t\t\t\t{\n\t\t\t\t\treturn &View_Drop;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\treturn &NoDistributeOps;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcase T_GrantRoleStmt:\n\t\t{\n\t\t\treturn &Any_GrantRole;\n\t\t}\n\n\t\tcase T_GrantStmt:\n\t\t{\n\t\t\tGrantStmt *stmt = castNode(GrantStmt, node);\n\t\t\tswitch (stmt->objtype)\n\t\t\t{\n\t\t\t\tcase OBJECT_SCHEMA:\n\t\t\t\t{\n\t\t\t\t\treturn &Schema_Grant;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_SEQUENCE:\n\t\t\t\t{\n\t\t\t\t\treturn &Sequence_Grant;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FDW:\n\t\t\t\t{\n\t\t\t\t\treturn &FDW_Grant;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FOREIGN_SERVER:\n\t\t\t\t{\n\t\t\t\t\treturn &ForeignServer_Grant;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FUNCTION:\n\t\t\t\t{\n\t\t\t\t\treturn &Function_Grant;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_AGGREGATE:\n\t\t\t\t{\n\t\t\t\t\treturn &Aggregate_Grant;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_PROCEDURE:\n\t\t\t\t{\n\t\t\t\t\treturn &Procedure_Grant;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_ROUTINE:\n\t\t\t\t{\n\t\t\t\t\treturn &Routine_Grant;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_DATABASE:\n\t\t\t\t{\n\t\t\t\t\treturn &Database_Grant;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\treturn &Any_Grant;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcase T_IndexStmt:\n\t\t{\n\t\t\treturn &Any_Index;\n\t\t}\n\n\t\tcase T_ViewStmt:\n\t\t{\n\t\t\treturn &Any_View;\n\t\t}\n\n\t\tcase T_ReindexStmt:\n\t\t{\n\t\t\treturn &Any_Reindex;\n\t\t}\n\n\t\tcase T_VacuumStmt:\n\t\t{\n\t\t\treturn &Vacuum_Analyze;\n\t\t}\n\n\t\tcase T_SecLabelStmt:\n\t\t{\n\t\t\tSecLabelStmt *stmt = castNode(SecLabelStmt, node);\n\t\t\tswitch (stmt->objtype)\n\t\t\t{\n\t\t\t\tcase OBJECT_ROLE:\n\t\t\t\t{\n\t\t\t\t\treturn &Role_SecLabel;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TABLE:\n\t\t\t\t{\n\t\t\t\t\treturn &Table_SecLabel;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_COLUMN:\n\t\t\t\t{\n\t\t\t\t\treturn &Column_SecLabel;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\treturn &Any_SecLabel;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcase T_RenameStmt:\n\t\t{\n\t\t\tRenameStmt *stmt = castNode(RenameStmt, node);\n\t\t\tswitch (stmt->renameType)\n\t\t\t{\n\t\t\t\tcase OBJECT_AGGREGATE:\n\t\t\t\t{\n\t\t\t\t\treturn &Aggregate_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_ATTRIBUTE:\n\t\t\t\t{\n\t\t\t\t\treturn &Attribute_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_COLLATION:\n\t\t\t\t{\n\t\t\t\t\treturn &Collation_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_DATABASE:\n\t\t\t\t{\n\t\t\t\t\treturn &Database_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_DOMAIN:\n\t\t\t\t{\n\t\t\t\t\treturn &Domain_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_DOMCONSTRAINT:\n\t\t\t\t{\n\t\t\t\t\treturn &Domain_RenameConstraint;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FOREIGN_SERVER:\n\t\t\t\t{\n\t\t\t\t\treturn &ForeignServer_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_FUNCTION:\n\t\t\t\t{\n\t\t\t\t\treturn &Function_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_PROCEDURE:\n\t\t\t\t{\n\t\t\t\t\treturn &Procedure_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_PUBLICATION:\n\t\t\t\t{\n\t\t\t\t\treturn &Publication_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_ROLE:\n\t\t\t\t{\n\t\t\t\t\treturn &Any_AlterRoleRename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_ROUTINE:\n\t\t\t\t{\n\t\t\t\t\treturn &Routine_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_SCHEMA:\n\t\t\t\t{\n\t\t\t\t\treturn &Schema_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_SEQUENCE:\n\t\t\t\t{\n\t\t\t\t\treturn &Sequence_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_STATISTIC_EXT:\n\t\t\t\t{\n\t\t\t\t\treturn &Statistics_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TSCONFIGURATION:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchConfig_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TSDICTIONARY:\n\t\t\t\t{\n\t\t\t\t\treturn &TextSearchDict_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TYPE:\n\t\t\t\t{\n\t\t\t\t\treturn &Type_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_TRIGGER:\n\t\t\t\t{\n\t\t\t\t\treturn &Trigger_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_VIEW:\n\t\t\t\t{\n\t\t\t\t\treturn &View_Rename;\n\t\t\t\t}\n\n\t\t\t\tcase OBJECT_COLUMN:\n\t\t\t\t{\n\t\t\t\t\tswitch (stmt->relationType)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase OBJECT_VIEW:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn &View_Rename;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treturn &Any_Rename;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\treturn &Any_Rename;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn &NoDistributeOps;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/domain.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * domain.c\n *    Hooks to handle the creation, altering and removal of domains.\n *    These hooks are responsible for duplicating the changes to the\n *    workers nodes.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/genam.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"parser/parse_type.h\"\n#include \"tcop/utility.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/regproc.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/worker_create_or_replace.h\"\n#include \"distributed/worker_transaction.h\"\n\n\nstatic CollateClause * MakeCollateClauseFromOid(Oid collationOid);\nstatic List * GetDomainAddressByName(TypeName *domainName, bool missing_ok);\n\n/*\n * GetDomainAddressByName returns the ObjectAddress of the domain identified by\n * domainName. When missing_ok is true the object id part of the ObjectAddress can be\n * InvalidOid. When missing_ok is false this function will raise an error instead when the\n * domain can't be found.\n */\nstatic List *\nGetDomainAddressByName(TypeName *domainName, bool missing_ok)\n{\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tOid domainOid = LookupTypeNameOid(NULL, domainName, missing_ok);\n\tObjectAddressSet(*address, TypeRelationId, domainOid);\n\treturn list_make1(address);\n}\n\n\n/*\n * RecreateDomainStmt returns a CreateDomainStmt pointer where the statement represents\n * the creation of the domain to recreate the domain on a different postgres node based on\n * the current representation in the local catalog.\n */\nCreateDomainStmt *\nRecreateDomainStmt(Oid domainOid)\n{\n\tCreateDomainStmt *stmt = makeNode(CreateDomainStmt);\n\tstmt->domainname = stringToQualifiedNameList(format_type_be_qualified(domainOid),\n\t\t\t\t\t\t\t\t\t\t\t\t NULL);\n\n\tHeapTuple tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(domainOid));\n\tif (!HeapTupleIsValid(tup))\n\t{\n\t\telog(ERROR, \"cache lookup failed for type %u\", domainOid);\n\t}\n\n\tForm_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);\n\tif (typTup->typtype != TYPTYPE_DOMAIN)\n\t{\n\t\telog(ERROR, \"type is not a domain type\");\n\t}\n\n\tstmt->typeName = makeTypeNameFromOid(typTup->typbasetype, typTup->typtypmod);\n\n\tif (OidIsValid(typTup->typcollation))\n\t{\n\t\tstmt->collClause = MakeCollateClauseFromOid(typTup->typcollation);\n\t}\n\n\t/*\n\t * typdefault and typdefaultbin are potentially null, so don't try to\n\t * access 'em as struct fields. Must do it the hard way with\n\t * SysCacheGetAttr.\n\t */\n\tbool isNull = false;\n\tDatum typeDefaultDatum = SysCacheGetAttr(TYPEOID,\n\t\t\t\t\t\t\t\t\t\t\t tup,\n\t\t\t\t\t\t\t\t\t\t\t Anum_pg_type_typdefaultbin,\n\t\t\t\t\t\t\t\t\t\t\t &isNull);\n\tif (!isNull)\n\t{\n\t\t/* when not null there is default value which we should add as a constraint */\n\t\tConstraint *constraint = makeNode(Constraint);\n\t\tconstraint->contype = CONSTR_DEFAULT;\n\t\tconstraint->cooked_expr = TextDatumGetCString(typeDefaultDatum);\n\n\t\tstmt->constraints = lappend(stmt->constraints, constraint);\n\t}\n\n\t/* NOT NULL constraints are non-named on the actual type */\n\tif (typTup->typnotnull)\n\t{\n\t\tConstraint *constraint = makeNode(Constraint);\n\t\tconstraint->contype = CONSTR_NOTNULL;\n\n\t\tstmt->constraints = lappend(stmt->constraints, constraint);\n\t}\n\n\t/* lookup and look all constraints to add them to the CreateDomainStmt */\n\tRelation conRel = table_open(ConstraintRelationId, AccessShareLock);\n\n\t/* Look for CHECK Constraints on this domain */\n\tScanKeyData key[1];\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_constraint_contypid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(domainOid));\n\n\tSysScanDesc scan = systable_beginscan(conRel, ConstraintTypidIndexId, true, NULL, 1,\n\t\t\t\t\t\t\t\t\t\t  key);\n\n\tHeapTuple conTup = NULL;\n\twhile (HeapTupleIsValid(conTup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);\n\n\t\tif (c->contype != CONSTRAINT_CHECK)\n\t\t{\n\t\t\t/* Ignore non-CHECK constraints, shouldn't be any */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * We create a constraint, completely ignoring c->convalidated because we can't\n\t\t * create a domain with an invalidated constraint. Once a constraint is added to\n\t\t * a domain -even non valid-, all new data is validated. Meaning, creating a\n\t\t * domain with a non-valid constraint doesn't make any sense.\n\t\t *\n\t\t * Given it will be too hard to defer the creation of a constraint till we\n\t\t * validate the constraint on the coordinator we will simply create the\n\t\t * non-validated constraint to ad hear to validating all new data.\n\t\t *\n\t\t * An edgecase here would be when moving existing data, that hasn't been validated\n\t\t * before to an other node. This behaviour is consistent with sending it to an\n\t\t * already existing node (that has the constraint created but not validated) and a\n\t\t * new node.\n\t\t */\n\n\t\tConstraint *constraint = makeNode(Constraint);\n\n\t\tconstraint->conname = pstrdup(NameStr(c->conname));\n\t\tconstraint->contype = CONSTR_CHECK; /* we only come here with check constraints */\n\n\t\t/* Not expecting conbin to be NULL, but we'll test for it anyway */\n\t\tDatum conbin = heap_getattr(conTup, Anum_pg_constraint_conbin, conRel->rd_att,\n\t\t\t\t\t\t\t\t\t&isNull);\n\t\tif (isNull)\n\t\t{\n\t\t\telog(ERROR, \"domain \\\"%s\\\" constraint \\\"%s\\\" has NULL conbin\",\n\t\t\t\t NameStr(typTup->typname), NameStr(c->conname));\n\t\t}\n\n\t\t/*\n\t\t * The conbin containes the cooked expression from when the constraint was\n\t\t * inserted into the catalog. We store it here for the deparser to distinguish\n\t\t * between cooked expressions and raw expressions.\n\t\t *\n\t\t * There is no supported way to go from a cooked expression to a raw expression.\n\t\t */\n\t\tconstraint->cooked_expr = TextDatumGetCString(conbin);\n\n\t\tstmt->constraints = lappend(stmt->constraints, constraint);\n\t}\n\n\tsystable_endscan(scan);\n\ttable_close(conRel, NoLock);\n\n\tReleaseSysCache(tup);\n\n\tQualifyTreeNode((Node *) stmt);\n\n\treturn stmt;\n}\n\n\n/*\n * MakeCollateClauseFromOid returns a CollateClause describing the COLLATE segment of a\n * CREATE DOMAIN statement based on the Oid of the collation used for the domain.\n */\nstatic CollateClause *\nMakeCollateClauseFromOid(Oid collationOid)\n{\n\tCollateClause *collateClause = makeNode(CollateClause);\n\n\tObjectAddress collateAddress = { 0 };\n\tObjectAddressSet(collateAddress, CollationRelationId, collationOid);\n\n\tList *objName = NIL;\n\tList *objArgs = NIL;\n\n\tgetObjectIdentityParts(&collateAddress, &objName, &objArgs, false);\n\n\tchar *name = NULL;\n\tforeach_declared_ptr(name, objName)\n\t{\n\t\tcollateClause->collname = lappend(collateClause->collname, makeString(name));\n\t}\n\n\tcollateClause->location = -1;\n\n\treturn collateClause;\n}\n\n\n/*\n * CreateDomainStmtObjectAddress returns the ObjectAddress of the domain that would be\n * created by the statement. When missing_ok is false the function will raise an error if\n * the domain cannot be found in the local catalog.\n */\nList *\nCreateDomainStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tCreateDomainStmt *stmt = castNode(CreateDomainStmt, node);\n\n\tTypeName *typeName = makeTypeNameFromNameList(stmt->domainname);\n\tOid typeOid = LookupTypeNameOid(NULL, typeName, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TypeRelationId, typeOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * AlterDomainStmtObjectAddress returns the ObjectAddress of the domain being altered.\n * When missing_ok is false this function will raise an error when the domain is not\n * found.\n */\nList *\nAlterDomainStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterDomainStmt *stmt = castNode(AlterDomainStmt, node);\n\n\tTypeName *domainName = makeTypeNameFromNameList(stmt->typeName);\n\tList *domainObjectAddresses = GetDomainAddressByName(domainName, missing_ok);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(domainObjectAddresses) == 1);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *address = linitial(domainObjectAddresses);\n\n\tOid domainOid = address->objectId;\n\tbool isDropConstraintStmt = (stmt->subtype == 'X');\n\tif (!isPostprocess && isDropConstraintStmt && OidIsValid(domainOid))\n\t{\n\t\t/*\n\t\t * we validate constraint if we are not in postprocess yet. It should have\n\t\t * been already dropped at postprocess, so we do not validate in postprocess.\n\t\t */\n\t\tchar *constraintName = stmt->name;\n\t\tOid constraintOid = get_domain_constraint_oid(domainOid, constraintName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  missing_ok);\n\t\tif (!OidIsValid(constraintOid))\n\t\t{\n\t\t\t/*\n\t\t\t * Although the domain is valid, the constraint is not. Eventually, PG will\n\t\t\t * throw an error. To prevent diverging outputs between Citus and PG, we treat\n\t\t\t * the domain as invalid.\n\t\t\t */\n\t\t\taddress->objectId = InvalidOid;\n\t\t}\n\t}\n\n\treturn list_make1(address);\n}\n\n\n/*\n * DomainRenameConstraintStmtObjectAddress returns the ObjectAddress of the domain for\n * which the constraint is being renamed. When missing_ok this function will raise an\n * error if the domain cannot be found.\n */\nList *\nDomainRenameConstraintStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\n\tTypeName *domainName = makeTypeNameFromNameList(castNode(List, stmt->object));\n\treturn GetDomainAddressByName(domainName, missing_ok);\n}\n\n\n/*\n * AlterDomainOwnerStmtObjectAddress returns the ObjectAddress for which the owner is\n * being changed. When missing_ok is false this function will raise an error if the domain\n * cannot be found.\n */\nList *\nAlterDomainOwnerStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_DOMAIN);\n\n\tTypeName *domainName = makeTypeNameFromNameList(castNode(List, stmt->object));\n\treturn GetDomainAddressByName(domainName, missing_ok);\n}\n\n\n/*\n * RenameDomainStmtObjectAddress returns the ObjectAddress of the domain being renamed.\n * When missing_ok is false this function will raise an error when the domain cannot be\n * found.\n */\nList *\nRenameDomainStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_DOMAIN);\n\n\tTypeName *domainName = makeTypeNameFromNameList(castNode(List, stmt->object));\n\treturn GetDomainAddressByName(domainName, missing_ok);\n}\n\n\n/*\n * get_constraint_typid returns the contypid of a constraint. This field is only set for\n * constraints on domain types. Returns InvalidOid if conoid is an invalid constraint, as\n * well as for constraints that are not on domain types.\n */\nOid\nget_constraint_typid(Oid conoid)\n{\n\tHeapTuple tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(conoid));\n\tif (HeapTupleIsValid(tp))\n\t{\n\t\tForm_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);\n\t\tOid result = contup->contypid;\n\t\tReleaseSysCache(tp);\n\t\treturn result;\n\t}\n\telse\n\t{\n\t\treturn InvalidOid;\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/drop_distributed_table.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * drop_distributed_table.c\n *\t  Routines related to dropping distributed relations from a trigger.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/tenant_schema_metadata.h\"\n#include \"distributed/worker_transaction.h\"\n\n\n/* local function forward declarations */\nstatic void MasterRemoveDistributedTableMetadataFromWorkers(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar *schemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar *tableName);\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(master_drop_distributed_table_metadata);\nPG_FUNCTION_INFO_V1(master_remove_partition_metadata);\nPG_FUNCTION_INFO_V1(master_remove_distributed_table_metadata_from_workers);\nPG_FUNCTION_INFO_V1(notify_constraint_dropped);\n\n\n/*\n * master_drop_distributed_table_metadata UDF is a stub UDF to install Citus flawlessly.\n * Otherwise we need to delete them from our sql files, which is confusing and not a\n * common operation in the code-base.\n *\n * This function is basically replaced with\n * master_remove_distributed_table_metadata_from_workers() followed by\n * master_remove_partition_metadata().\n */\nDatum\nmaster_drop_distributed_table_metadata(PG_FUNCTION_ARGS)\n{\n\tereport(INFO, (errmsg(\"this function is deprecated and no longer is used\")));\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * master_remove_partition_metadata removes the entry of the specified distributed\n * table from pg_dist_partition.\n */\nDatum\nmaster_remove_partition_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\ttext *schemaNameText = PG_GETARG_TEXT_P(1);\n\ttext *tableNameText = PG_GETARG_TEXT_P(2);\n\n\tchar *schemaName = text_to_cstring(schemaNameText);\n\tchar *tableName = text_to_cstring(tableNameText);\n\n\tuint32 colocationId = ColocationIdViaCatalog(relationId);\n\n\t/*\n\t * The SQL_DROP trigger calls this function even for tables that are\n\t * not distributed. In that case, silently ignore. This is not very\n\t * user-friendly, but this function is really only meant to be called\n\t * from the trigger.\n\t */\n\tif (!IsCitusTableViaCatalog(relationId) || !EnableDDLPropagation)\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\tEnsureCoordinator();\n\n\tCheckTableSchemaNameForDrop(relationId, &schemaName, &tableName);\n\n\tDeletePartitionRow(relationId);\n\n\t/*\n\t * We want to keep using the same colocation group for the tenant even if\n\t * all the tables that belong to it are dropped and new tables are created\n\t * for the tenant etc. For this reason, if a colocation group belongs to a\n\t * tenant schema, we don't delete the colocation group even if there are no\n\t * tables that belong to it.\n\t *\n\t * We do the same if system catalog cannot find the schema of the table\n\t * because this means that the whole schema is dropped.\n\t *\n\t * In that case, we want to delete the colocation group regardless of\n\t * whether the schema is a tenant schema or not. Even more, calling\n\t * IsTenantSchema() with InvalidOid would cause an error, hence we check\n\t * whether the schema is valid beforehand.\n\t */\n\tbool missingOk = true;\n\tOid schemaId = get_namespace_oid(schemaName, missingOk);\n\tif (!OidIsValid(schemaId) || !IsTenantSchema(schemaId))\n\t{\n\t\tDeleteColocationGroupIfNoTablesBelong(colocationId);\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * master_remove_distributed_table_metadata_from_workers removes the entry of the\n * specified distributed table from pg_dist_partition and drops the table from\n * the workers if needed.\n */\nDatum\nmaster_remove_distributed_table_metadata_from_workers(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\ttext *schemaNameText = PG_GETARG_TEXT_P(1);\n\ttext *tableNameText = PG_GETARG_TEXT_P(2);\n\n\tchar *schemaName = text_to_cstring(schemaNameText);\n\tchar *tableName = text_to_cstring(tableNameText);\n\n\tCheckTableSchemaNameForDrop(relationId, &schemaName, &tableName);\n\n\tMasterRemoveDistributedTableMetadataFromWorkers(relationId, schemaName, tableName);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * MasterRemoveDistributedTableMetadataFromWorkers drops the table and removes\n * all the metadata belonging the distributed table in the worker nodes\n * with metadata. The function doesn't drop the tables that are\n * the shards on the workers.\n *\n * The function is a no-op for non-distributed tables and clusters that don't\n * have any workers with metadata. Also, the function errors out if called\n * from a worker node.\n *\n * This function assumed that it is called via a trigger. But we cannot do the\n * typical CALLED_AS_TRIGGER check because this is called via another trigger,\n * which CALLED_AS_TRIGGER does not cover.\n */\nstatic void\nMasterRemoveDistributedTableMetadataFromWorkers(Oid relationId, char *schemaName,\n\t\t\t\t\t\t\t\t\t\t\t\tchar *tableName)\n{\n\t/*\n\t * The SQL_DROP trigger calls this function even for tables that are\n\t * not distributed. In that case, silently ignore. This is not very\n\t * user-friendly, but this function is really only meant to be called\n\t * from the trigger.\n\t */\n\tif (!IsCitusTableViaCatalog(relationId) || !EnableDDLPropagation)\n\t{\n\t\treturn;\n\t}\n\n\tEnsureCoordinator();\n\n\tif (!ShouldSyncTableMetadataViaCatalog(relationId))\n\t{\n\t\treturn;\n\t}\n\n\tif (PartitionTable(relationId))\n\t{\n\t\t/*\n\t\t * MasterRemoveDistributedTableMetadataFromWorkers is only called from drop trigger.\n\t\t * When parent is dropped in a drop trigger, we remove all the corresponding\n\t\t * partitions via the parent, mostly for performance reasons.\n\t\t */\n\t\treturn;\n\t}\n\n\t/* drop the distributed table metadata on the workers */\n\tchar *deleteDistributionCommand = DistributionDeleteCommand(schemaName, tableName);\n\tSendCommandToWorkersWithMetadata(deleteDistributionCommand);\n}\n\n\n/*\n * notify_constraint_dropped simply calls NotifyUtilityHookConstraintDropped\n * to set ConstraintDropped to true.\n * This udf is designed to be called from citus_drop_trigger to tell us we\n * dropped a table constraint.\n */\nDatum\nnotify_constraint_dropped(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\t/*\n\t * We reset this only in utility hook, so we should not set this flag\n\t * otherwise if we are not in utility hook.\n\t * In some cases -where dropping foreign key not issued via utility\n\t * hook-, we would not be able to undistribute such citus local tables\n\t * but we are ok with that.\n\t */\n\tif (UtilityHookLevel >= 1)\n\t{\n\t\tNotifyUtilityHookConstraintDropped();\n\t}\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/extension.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * extension.c\n *    Commands for creating and altering extensions.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/genam.h\"\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_extension_d.h\"\n#include \"catalog/pg_foreign_data_wrapper.h\"\n#include \"commands/defrem.h\"\n#include \"commands/extension.h\"\n#include \"foreign/foreign.h\"\n#include \"nodes/makefuncs.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#if PG_VERSION_NUM < PG_VERSION_17\n#include \"catalog/pg_am_d.h\"\n#endif\n\n#include \"citus_version.h\"\n\n#include \"columnar/columnar.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/transaction_management.h\"\n\n\n/* Local functions forward declarations for helper functions */\nstatic char * ExtractNewExtensionVersion(Node *parseTree);\nstatic void AddSchemaFieldIfMissing(CreateExtensionStmt *stmt);\nstatic List * FilterDistributedExtensions(List *extensionObjectList);\nstatic List * ExtensionNameListToObjectAddressList(List *extensionObjectList);\nstatic void MarkExistingObjectDependenciesDistributedIfSupported(void);\nstatic List * GetAllViews(void);\nstatic bool ShouldPropagateExtensionCommand(Node *parseTree);\nstatic bool IsAlterExtensionSetSchemaCitus(Node *parseTree);\nstatic bool HasAnyRelationsUsingOldColumnar(void);\nstatic Oid GetOldColumnarAMIdIfExists(void);\nstatic bool AccessMethodDependsOnAnyExtensions(Oid accessMethodId);\nstatic bool HasAnyRelationsUsingAccessMethod(Oid accessMethodId);\nstatic Node * RecreateExtensionStmt(Oid extensionOid);\nstatic List * GenerateGrantCommandsOnExtensionDependentFDWs(Oid extensionId);\n\n\n/*\n * ErrorIfUnstableCreateOrAlterExtensionStmt compares CITUS_EXTENSIONVERSION\n * and version given CREATE/ALTER EXTENSION statement will create/update to. If\n * they are not same in major or minor version numbers, this function errors\n * out. It ignores the schema version.\n */\nvoid\nErrorIfUnstableCreateOrAlterExtensionStmt(Node *parseTree)\n{\n\tchar *newExtensionVersion = ExtractNewExtensionVersion(parseTree);\n\n\tif (newExtensionVersion != NULL)\n\t{\n\t\t/*  explicit version provided in CREATE or ALTER EXTENSION UPDATE; verify */\n\t\tif (!MinorVersionsCompatibleRelaxed(newExtensionVersion, CITUS_EXTENSIONVERSION))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"specified version incompatible with loaded \"\n\t\t\t\t\t\t\t\t   \"Citus library\"),\n\t\t\t\t\t\t\terrdetail(\"Loaded library requires %s, but %s was specified.\",\n\t\t\t\t\t\t\t\t\t  CITUS_MAJORVERSION, newExtensionVersion),\n\t\t\t\t\t\t\terrhint(\"If a newer library is present, restart the database \"\n\t\t\t\t\t\t\t\t\t\"and try the command again.\")));\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * No version was specified, so PostgreSQL will use the default_version\n\t\t * from the citus.control file.\n\t\t */\n\t\tCheckAvailableVersion(ERROR);\n\t}\n}\n\n\n/*\n * ExtractNewExtensionVersion returns the palloc'd new extension version specified\n * by a CREATE or ALTER EXTENSION statement. Other inputs are not permitted.\n * This function returns NULL for statements with no explicit version specified.\n */\nstatic char *\nExtractNewExtensionVersion(Node *parseTree)\n{\n\tList *optionsList = NIL;\n\n\tif (IsA(parseTree, CreateExtensionStmt))\n\t{\n\t\toptionsList = ((CreateExtensionStmt *) parseTree)->options;\n\t}\n\telse if (IsA(parseTree, AlterExtensionStmt))\n\t{\n\t\toptionsList = ((AlterExtensionStmt *) parseTree)->options;\n\t}\n\telse\n\t{\n\t\t/* input must be one of the two above types */\n\t\tAssert(false);\n\t}\n\n\tDefElem *newVersionValue = GetExtensionOption(optionsList, \"new_version\");\n\n\t/* return target string safely */\n\tif (newVersionValue)\n\t{\n\t\tconst char *newVersion = defGetString(newVersionValue);\n\t\treturn pstrdup(newVersion);\n\t}\n\telse\n\t{\n\t\treturn NULL;\n\t}\n}\n\n\n/*\n * PostprocessCreateExtensionStmt is called after the creation of an extension.\n * We decide if the extension needs to be replicated to the worker, and\n * if that is the case return a list of DDLJob's that describe how and\n * where the extension needs to be created.\n *\n * As we now have access to ObjectAddress of the extension that is just\n * created, we can mark it as distributed to make sure that its\n * dependencies exist on all nodes.\n */\nList *\nPostprocessCreateExtensionStmt(Node *node, const char *queryString)\n{\n\tCreateExtensionStmt *stmt = castNode(CreateExtensionStmt, node);\n\n\tif (!ShouldPropagateExtensionCommand(node))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* check creation against multi-statement transaction policy */\n\tif (!ShouldPropagateCreateInCoordinatedTransction())\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* extension management can only be done via coordinator node */\n\tEnsureCoordinator();\n\n\t/*\n\t * Make sure that the current transaction is already in sequential mode,\n\t * or can still safely be put in sequential mode\n\t */\n\tEnsureSequentialMode(OBJECT_EXTENSION);\n\n\t/*\n\t * Here we append \"schema\" field to the \"options\" list (if not specified)\n\t * to satisfy the schema consistency between worker nodes and the coordinator.\n\t */\n\tAddSchemaFieldIfMissing(stmt);\n\n\t/* always send commands with IF NOT EXISTS */\n\tstmt->if_not_exists = true;\n\n\tconst char *createExtensionStmtSql = DeparseTreeNode(node);\n\n\t/*\n\t * To prevent recursive propagation in mx architecture, we disable ddl\n\t * propagation before sending the command to workers.\n\t */\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) createExtensionStmtSql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\tList *extensionAddresses = GetObjectAddressListFromParseTree(node, false, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(extensionAddresses) == 1);\n\n\t/*\n\t * If the extension is already distributed, don't do anything to avoid\n\t * ownership checks on workers. This is important when a non-owner user runs\n\t * CREATE EXTENSION IF NOT EXISTS for an existing extension - PostgreSQL's\n\t * standard_ProcessUtility succeeds (extension exists, no-op), but metadata\n\t * propagation would fail the ownership check.\n\t */\n\tif (IsAnyObjectDistributed(extensionAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureAllObjectDependenciesExistOnAllNodes(extensionAddresses);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * AddSchemaFieldIfMissing adds DefElem item for \"schema\" (if not specified\n * in statement) to \"options\" list before deparsing the statement to satisfy\n * the schema consistency between worker nodes and the coordinator.\n */\nstatic void\nAddSchemaFieldIfMissing(CreateExtensionStmt *createExtensionStmt)\n{\n\tList *optionsList = createExtensionStmt->options;\n\n\tDefElem *schemaNameValue = GetExtensionOption(optionsList, \"schema\");\n\n\tif (!schemaNameValue)\n\t{\n\t\t/*\n\t\t * As we already created the extension by standard_ProcessUtility,\n\t\t * we actually know the schema it belongs to\n\t\t */\n\t\tconst bool missingOk = false;\n\t\tOid extensionOid = get_extension_oid(createExtensionStmt->extname, missingOk);\n\t\tOid extensionSchemaOid = get_extension_schema(extensionOid);\n\t\tchar *extensionSchemaName = get_namespace_name(extensionSchemaOid);\n\n\t\tNode *schemaNameArg = (Node *) makeString(extensionSchemaName);\n\n\t\t/* set location to -1 as it is unknown */\n\t\tint location = -1;\n\n\t\tDefElem *newDefElement = makeDefElem(\"schema\", schemaNameArg, location);\n\n\t\tcreateExtensionStmt->options = lappend(createExtensionStmt->options,\n\t\t\t\t\t\t\t\t\t\t\t   newDefElement);\n\t}\n}\n\n\n/*\n * PreprocessDropExtensionStmt is called to drop extension(s) in coordinator and\n * in worker nodes if distributed before.\n * We first ensure that we keep only the distributed ones before propagating\n * the statement to worker nodes.\n * If no extensions in the drop list are distributed, then no calls will\n * be made to the workers.\n */\nList *\nPreprocessDropExtensionStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\n\tif (!ShouldPropagateExtensionCommand(node))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* get distributed extensions to be dropped in worker nodes as well */\n\tList *allDroppedExtensions = stmt->objects;\n\tList *distributedExtensions = FilterDistributedExtensions(allDroppedExtensions);\n\n\tif (list_length(distributedExtensions) <= 0)\n\t{\n\t\t/* no distributed extensions to drop */\n\t\treturn NIL;\n\t}\n\n\t/* extension management can only be done via coordinator node */\n\tEnsureCoordinator();\n\n\t/*\n\t * Make sure that the current transaction is already in sequential mode,\n\t * or can still safely be put in sequential mode\n\t */\n\tEnsureSequentialMode(OBJECT_EXTENSION);\n\n\tList *distributedExtensionAddresses = ExtensionNameListToObjectAddressList(\n\t\tdistributedExtensions);\n\n\t/* unmark each distributed extension */\n\tObjectAddress *address = NULL;\n\tforeach_declared_ptr(address, distributedExtensionAddresses)\n\t{\n\t\tUnmarkObjectDistributed(address);\n\t}\n\n\t/*\n\t * Temporary swap the lists of objects to delete with the distributed\n\t * objects and deparse to an sql statement for the workers.\n\t * Then switch back to allDroppedExtensions to drop all specified\n\t * extensions in coordinator after PreprocessDropExtensionStmt completes\n\t * its execution.\n\t */\n\tstmt->objects = distributedExtensions;\n\tconst char *deparsedStmt = DeparseTreeNode((Node *) stmt);\n\tstmt->objects = allDroppedExtensions;\n\n\t/*\n\t * To prevent recursive propagation in mx architecture, we disable ddl\n\t * propagation before sending the command to workers.\n\t */\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) deparsedStmt,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * FilterDistributedExtensions returns the distributed objects in an \"objects\"\n * list of a DropStmt, a list having the format of a \"DropStmt.objects\" list.\n * That is, in turn, a list of string \"Value\"s.\n */\nstatic List *\nFilterDistributedExtensions(List *extensionObjectList)\n{\n\tList *extensionNameList = NIL;\n\n\tString *objectName = NULL;\n\tforeach_declared_ptr(objectName, extensionObjectList)\n\t{\n\t\tconst char *extensionName = strVal(objectName);\n\t\tconst bool missingOk = true;\n\n\t\tOid extensionOid = get_extension_oid(extensionName, missingOk);\n\n\t\tif (!OidIsValid(extensionOid))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*address, ExtensionRelationId, extensionOid);\n\t\tif (!IsAnyObjectDistributed(list_make1(address)))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\textensionNameList = lappend(extensionNameList, objectName);\n\t}\n\n\treturn extensionNameList;\n}\n\n\n/*\n * ExtensionNameListToObjectAddressList returns the object addresses in\n * an ObjectAddress list for an \"objects\" list of a DropStmt.\n * Callers of this function should ensure that all the objects in the list\n * are valid and distributed.\n */\nstatic List *\nExtensionNameListToObjectAddressList(List *extensionObjectList)\n{\n\tList *extensionObjectAddressList = NIL;\n\n\tString *objectName;\n\tforeach_declared_ptr(objectName, extensionObjectList)\n\t{\n\t\t/*\n\t\t * We set missingOk to false as we assume all the objects in\n\t\t * extensionObjectList list are valid and distributed.\n\t\t */\n\t\tconst char *extensionName = strVal(objectName);\n\t\tconst bool missingOk = false;\n\n\t\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\n\t\tOid extensionOid = get_extension_oid(extensionName, missingOk);\n\n\t\tObjectAddressSet(*address, ExtensionRelationId, extensionOid);\n\n\t\textensionObjectAddressList = lappend(extensionObjectAddressList, address);\n\t}\n\n\treturn extensionObjectAddressList;\n}\n\n\n/*\n * PreprocessAlterExtensionSchemaStmt is invoked for alter extension set schema statements.\n */\nList *\nPreprocessAlterExtensionSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tif (!ShouldPropagateExtensionCommand(node))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* extension management can only be done via coordinator node */\n\tEnsureCoordinator();\n\n\t/*\n\t * Make sure that the current transaction is already in sequential mode,\n\t * or can still safely be put in sequential mode\n\t */\n\tEnsureSequentialMode(OBJECT_EXTENSION);\n\n\tconst char *alterExtensionStmtSql = DeparseTreeNode(node);\n\n\t/*\n\t * To prevent recursive propagation in mx architecture, we disable ddl\n\t * propagation before sending the command to workers.\n\t */\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) alterExtensionStmtSql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * PostprocessAlterExtensionSchemaStmt is executed after the change has been applied\n * locally, we can now use the new dependencies (schema) of the extension to ensure\n * all its dependencies exist on the workers before we apply the commands remotely.\n */\nList *\nPostprocessAlterExtensionSchemaStmt(Node *node, const char *queryString)\n{\n\tList *extensionAddresses = GetObjectAddressListFromParseTree(node, false, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(extensionAddresses) == 1);\n\n\tif (!ShouldPropagateExtensionCommand(node))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* dependencies (schema) have changed let's ensure they exist */\n\tEnsureAllObjectDependenciesExistOnAllNodes(extensionAddresses);\n\n\treturn NIL;\n}\n\n\n/*\n * PreprocessAlterExtensionUpdateStmt is invoked for alter extension update statements.\n */\nList *\nPreprocessAlterExtensionUpdateStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tAlterExtensionStmt *alterExtensionStmt = castNode(AlterExtensionStmt, node);\n\n\tif (!ShouldPropagateExtensionCommand((Node *) alterExtensionStmt))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* extension management can only be done via coordinator node */\n\tEnsureCoordinator();\n\n\t/*\n\t * Make sure that the current transaction is already in sequential mode,\n\t * or can still safely be put in sequential mode\n\t */\n\tEnsureSequentialMode(OBJECT_EXTENSION);\n\n\tconst char *alterExtensionStmtSql = DeparseTreeNode((Node *) alterExtensionStmt);\n\n\t/*\n\t * To prevent recursive propagation in mx architecture, we disable ddl\n\t * propagation before sending the command to workers.\n\t */\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) alterExtensionStmtSql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * PostprocessAlterExtensionCitusUpdateStmt is called after ALTER EXTENSION\n * citus UPDATE command is executed by standard utility hook.\n *\n * Actually, we do not need such a post process function for ALTER EXTENSION\n * UPDATE commands unless the extension is Citus itself. This is because we\n * need to mark existing objects that are not included in distributed object\n * infrastructure in older versions of Citus (but now should be) as distributed\n * if we really updated Citus to the available version successfully via standard\n * utility hook.\n */\nvoid\nPostprocessAlterExtensionCitusUpdateStmt(Node *node)\n{\n\tbool citusIsUpdatedToLatestVersion = InstalledAndAvailableVersionsSame();\n\n\t/*\n\t * Knowing that Citus version was different than the available version before\n\t * standard process utility runs ALTER EXTENSION command, we perform post\n\t * process operations if Citus is updated to that available version\n\t */\n\tif (!citusIsUpdatedToLatestVersion)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * Finally, mark existing objects that are not included in distributed object\n\t * infrastructure in older versions of Citus (but now should be) as distributed\n\t */\n\tMarkExistingObjectDependenciesDistributedIfSupported();\n}\n\n\n/*\n * MarkExistingObjectDependenciesDistributedIfSupported marks all objects that could\n * be distributed by resolving dependencies of \"existing distributed tables\" and\n * \"already distributed objects\" to introduce the objects created in older versions\n * of Citus to distributed object infrastructure as well.\n *\n * Note that this function is not responsible for ensuring if dependencies exist on\n * nodes and satisfying these dependendencies if not exists, which is already done by\n * EnsureAllObjectDependenciesExistOnAllNodes on demand. Hence, this function is just designed\n * to be used when \"ALTER EXTENSION citus UPDATE\" is executed.\n * This is because we want to add existing objects that would have already been in\n * pg_dist_object if we had created them in new version of Citus to pg_dist_object.\n */\nstatic void\nMarkExistingObjectDependenciesDistributedIfSupported()\n{\n\t/* resulting object addresses to be marked as distributed */\n\tList *resultingObjectAddresses = NIL;\n\n\t/* resolve dependencies of citus tables */\n\tList *citusTableIdList = CitusTableTypeIdList(ANY_CITUS_TABLE_TYPE);\n\n\tOid citusTableId = InvalidOid;\n\tforeach_declared_oid(citusTableId, citusTableIdList)\n\t{\n\t\tif (!ShouldMarkRelationDistributed(citusTableId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* refrain reading the metadata cache for all tables */\n\t\tif (ShouldSyncTableMetadataViaCatalog(citusTableId))\n\t\t{\n\t\t\tObjectAddress tableAddress = { 0 };\n\t\t\tObjectAddressSet(tableAddress, RelationRelationId, citusTableId);\n\n\t\t\t/*\n\t\t\t * We mark tables distributed immediately because we also need to mark\n\t\t\t * views as distributed. We check whether the views that depend on\n\t\t\t * the table has any auto-distirbutable dependencies below. Citus\n\t\t\t * currently cannot \"auto\" distribute tables as dependencies, so we\n\t\t\t * mark them distributed immediately.\n\t\t\t */\n\t\t\tMarkObjectDistributedLocally(&tableAddress);\n\n\t\t\t/*\n\t\t\t * All the distributable dependencies of a table should be marked as\n\t\t\t * distributed.\n\t\t\t */\n\t\t\tList *distributableDependencyObjectAddresses =\n\t\t\t\tGetDistributableDependenciesForObject(&tableAddress);\n\n\t\t\tresultingObjectAddresses =\n\t\t\t\tlist_concat(resultingObjectAddresses,\n\t\t\t\t\t\t\tdistributableDependencyObjectAddresses);\n\t\t}\n\t}\n\n\t/*\n\t * As of Citus 11, views on Citus tables that do not have any unsupported\n\t * dependency should also be distributed.\n\t *\n\t * In general, we mark views distributed as long as it does not have\n\t * any unsupported dependencies.\n\t */\n\tList *viewList = GetAllViews();\n\tOid viewOid = InvalidOid;\n\tforeach_declared_oid(viewOid, viewList)\n\t{\n\t\tif (!ShouldMarkRelationDistributed(viewOid))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tObjectAddress viewAddress = { 0 };\n\t\tObjectAddressSet(viewAddress, RelationRelationId, viewOid);\n\n\t\t/*\n\t\t * If a view depends on multiple views, that view will be marked\n\t\t * as distributed while it is processed for the last view\n\t\t * table.\n\t\t */\n\t\tMarkObjectDistributedLocally(&viewAddress);\n\n\t\t/* we need to pass pointer allocated in the heap */\n\t\tObjectAddress *addressPointer = palloc0(sizeof(ObjectAddress));\n\t\t*addressPointer = viewAddress;\n\n\t\tList *distributableDependencyObjectAddresses =\n\t\t\tGetDistributableDependenciesForObject(&viewAddress);\n\n\t\tresultingObjectAddresses =\n\t\t\tlist_concat(resultingObjectAddresses,\n\t\t\t\t\t\tdistributableDependencyObjectAddresses);\n\t}\n\n\n\t/* resolve dependencies of the objects in pg_dist_object*/\n\tList *distributedObjectAddressList = GetDistributedObjectAddressList();\n\n\tObjectAddress *distributedObjectAddress = NULL;\n\tforeach_declared_ptr(distributedObjectAddress, distributedObjectAddressList)\n\t{\n\t\tList *distributableDependencyObjectAddresses =\n\t\t\tGetDistributableDependenciesForObject(distributedObjectAddress);\n\n\t\tresultingObjectAddresses = list_concat(resultingObjectAddresses,\n\t\t\t\t\t\t\t\t\t\t\t   distributableDependencyObjectAddresses);\n\t}\n\n\t/* remove duplicates from object addresses list for efficiency */\n\tList *uniqueObjectAddresses = GetUniqueDependenciesList(resultingObjectAddresses);\n\n\t/*\n\t * We should sync the new dependencies during ALTER EXTENSION because\n\t * we cannot know whether the nodes has already been upgraded or not. If\n\t * the nodes are not upgraded at this point, we cannot sync the object. Also,\n\t * when the workers upgraded, they'd get the same objects anyway.\n\t */\n\tbool prevMetadataSyncValue = EnableMetadataSync;\n\tSetLocalEnableMetadataSync(false);\n\n\tObjectAddress *objectAddress = NULL;\n\tforeach_declared_ptr(objectAddress, uniqueObjectAddresses)\n\t{\n\t\tMarkObjectDistributed(objectAddress);\n\t}\n\n\tSetLocalEnableMetadataSync(prevMetadataSyncValue);\n}\n\n\n/*\n * GetAllViews returns list of view oids that exists on this server.\n */\nstatic List *\nGetAllViews(void)\n{\n\tList *viewOidList = NIL;\n\n\tRelation pgClass = table_open(RelationRelationId, AccessShareLock);\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgClass, InvalidOid, false, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t0, NULL);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tForm_pg_class relationForm = (Form_pg_class) GETSTRUCT(heapTuple);\n\n\t\t/* we're only interested in views */\n\t\tif (relationForm->relkind == RELKIND_VIEW)\n\t\t{\n\t\t\tviewOidList = lappend_oid(viewOidList, relationForm->oid);\n\t\t}\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgClass, NoLock);\n\n\treturn viewOidList;\n}\n\n\n/*\n * PreprocessAlterExtensionContentsStmt issues a notice. It does not propagate.\n */\nList *\nPreprocessAlterExtensionContentsStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t \"Citus does not propagate adding/dropping member objects\"),\n\t\t\t\t\t errhint(\n\t\t\t\t\t\t \"You can add/drop the member objects on the workers as well.\")));\n\n\treturn NIL;\n}\n\n\n/*\n * ShouldPropagateExtensionCommand determines whether to propagate an extension\n * command to the worker nodes.\n */\nstatic bool\nShouldPropagateExtensionCommand(Node *parseTree)\n{\n\t/* if we disabled object propagation, then we should not propagate anything. */\n\tif (!EnableMetadataSync)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * If extension command is run for/on citus, leave the rest to standard utility hook\n\t * by returning false.\n\t */\n\tif (IsCreateAlterExtensionUpdateCitusStmt(parseTree))\n\t{\n\t\treturn false;\n\t}\n\telse if (IsDropCitusExtensionStmt(parseTree))\n\t{\n\t\treturn false;\n\t}\n\telse if (IsAlterExtensionSetSchemaCitus(parseTree))\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * IsCreateAlterExtensionUpdateCitusStmt returns whether a given utility is a\n * CREATE or ALTER EXTENSION UPDATE statement which references the citus extension.\n * This function returns false for all other inputs.\n */\nbool\nIsCreateAlterExtensionUpdateCitusStmt(Node *parseTree)\n{\n\tconst char *extensionName = NULL;\n\n\tif (IsA(parseTree, CreateExtensionStmt))\n\t{\n\t\textensionName = ((CreateExtensionStmt *) parseTree)->extname;\n\t}\n\telse if (IsA(parseTree, AlterExtensionStmt))\n\t{\n\t\textensionName = ((AlterExtensionStmt *) parseTree)->extname;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * If it is not a Create Extension or a Alter Extension stmt,\n\t\t * it does not matter if the it is about citus\n\t\t */\n\t\treturn false;\n\t}\n\n\t/*\n\t * Now that we have CreateExtensionStmt or AlterExtensionStmt,\n\t * check if it is run for/on citus\n\t */\n\treturn (strncasecmp(extensionName, \"citus\", NAMEDATALEN) == 0);\n}\n\n\n/*\n * PreProcessCreateExtensionCitusStmtForColumnar determines whether need to\n * install citus_columnar first or citus_columnar is supported on current\n * citus version, when a given utility is a CREATE statement\n */\nvoid\nPreprocessCreateExtensionStmtForCitusColumnar(Node *parsetree)\n{\n\t/*CREATE EXTENSION CITUS (version Z) */\n\tCreateExtensionStmt *createExtensionStmt = castNode(CreateExtensionStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tparsetree);\n\n\tif (strcmp(createExtensionStmt->extname, \"citus\") == 0)\n\t{\n\t\tint versionNumber = (int) (100 * strtod(CITUS_MAJORVERSION, NULL));\n\t\tDefElem *newVersionValue = GetExtensionOption(createExtensionStmt->options,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"new_version\");\n\n\t\t/*create extension citus version xxx*/\n\t\tif (newVersionValue)\n\t\t{\n\t\t\tchar *newVersion = pstrdup(defGetString(newVersionValue));\n\t\t\tversionNumber = GetExtensionVersionNumber(newVersion);\n\t\t}\n\n\t\t/*citus version >= 11.1 requires install citus_columnar first*/\n\t\tif (versionNumber >= 1110 && !CitusHasBeenLoaded())\n\t\t{\n\t\t\tif (get_extension_oid(\"citus_columnar\", true) == InvalidOid &&\n\t\t\t\t(versionNumber < 1320 || HasAnyRelationsUsingOldColumnar()))\n\t\t\t{\n\t\t\t\tCreateExtensionWithVersion(\"citus_columnar\", NULL);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*Edge case check: citus_columnar are supported on citus version >= 11.1*/\n\tif (strcmp(createExtensionStmt->extname, \"citus_columnar\") == 0)\n\t{\n\t\tOid citusOid = get_extension_oid(\"citus\", true);\n\t\tif (citusOid != InvalidOid)\n\t\t{\n\t\t\tchar *curCitusVersion = pstrdup(get_extension_version(citusOid));\n\t\t\tint curCitusVersionNum = GetExtensionVersionNumber(curCitusVersion);\n\t\t\tif (curCitusVersionNum < 1110)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\"must upgrade citus to version 11.1-1 first before install citus_columnar\")));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * IsDropCitusExtensionStmt iterates the objects to be dropped in a drop statement\n * and try to find citus extension there.\n */\nbool\nIsDropCitusExtensionStmt(Node *parseTree)\n{\n\t/* if it is not a DropStmt, it is needless to search for citus */\n\tif (!IsA(parseTree, DropStmt))\n\t{\n\t\treturn false;\n\t}\n\tDropStmt *dropStmt = (DropStmt *) parseTree;\n\n\t/* check if the drop command is a DROP EXTENSION command */\n\tif (dropStmt->removeType != OBJECT_EXTENSION)\n\t{\n\t\treturn false;\n\t}\n\n\t/* now that we have a DropStmt, check if citus extension is among the objects to dropped */\n\tString *objectName;\n\tforeach_declared_ptr(objectName, dropStmt->objects)\n\t{\n\t\tconst char *extensionName = strVal(objectName);\n\n\t\tif (strncasecmp(extensionName, \"citus\", NAMEDATALEN) == 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * IsAlterExtensionSetSchemaCitus returns whether a given utility is an\n * ALTER EXTENSION SET SCHEMA statement which references the citus extension.\n * This function returns false for all other inputs.\n */\nstatic bool\nIsAlterExtensionSetSchemaCitus(Node *parseTree)\n{\n\tconst char *extensionName = NULL;\n\n\tif (IsA(parseTree, AlterObjectSchemaStmt))\n\t{\n\t\tAlterObjectSchemaStmt *alterExtensionSetSchemaStmt =\n\t\t\t(AlterObjectSchemaStmt *) parseTree;\n\n\t\tif (alterExtensionSetSchemaStmt->objectType == OBJECT_EXTENSION)\n\t\t{\n\t\t\textensionName = strVal(((AlterObjectSchemaStmt *) parseTree)->object);\n\n\t\t\t/*\n\t\t\t * Now that we have AlterObjectSchemaStmt for an extension,\n\t\t\t * check if it is run for/on citus\n\t\t\t */\n\t\t\treturn (strncasecmp(extensionName, \"citus\", NAMEDATALEN) == 0);\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * PreprocessAlterExtensionCitusStmtForCitusColumnar pre-process the case when upgrade citus\n * to version that support citus_columnar, or downgrade citus to lower version that\n * include columnar inside citus extension\n */\nvoid\nPreprocessAlterExtensionCitusStmtForCitusColumnar(Node *parseTree)\n{\n\t/*upgrade citus: alter extension citus update to 'xxx' */\n\tDefElem *newVersionValue = GetExtensionOption(\n\t\t((AlterExtensionStmt *) parseTree)->options, \"new_version\");\n\tOid citusColumnarOid = get_extension_oid(\"citus_columnar\", true);\n\tif (newVersionValue)\n\t{\n\t\tchar *newVersion = defGetString(newVersionValue);\n\t\tdouble newVersionNumber = GetExtensionVersionNumber(pstrdup(newVersion));\n\n\t\t/*alter extension citus update to version >= 11.1-1, and no citus_columnar installed */\n\t\tif (newVersionNumber >= 1110 && citusColumnarOid == InvalidOid &&\n\t\t\t(newVersionNumber < 1320 || HasAnyRelationsUsingOldColumnar()))\n\t\t{\n\t\t\t/*it's upgrade citus to 11.1-1 or further version and there are relations using old columnar */\n\t\t\tCreateExtensionWithVersion(\"citus_columnar\", CITUS_COLUMNAR_INTERNAL_VERSION);\n\t\t}\n\t\telse if (newVersionNumber < 1110 && citusColumnarOid != InvalidOid)\n\t\t{\n\t\t\t/*downgrade citus, need downgrade citus_columnar to Y */\n\t\t\tAlterExtensionUpdateStmt(\"citus_columnar\", CITUS_COLUMNAR_INTERNAL_VERSION);\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*alter extension citus update without specifying the version*/\n\t\tint versionNumber = (int) (100 * strtod(CITUS_MAJORVERSION, NULL));\n\t\tif (versionNumber >= 1110)\n\t\t{\n\t\t\tif (citusColumnarOid == InvalidOid &&\n\t\t\t\t(versionNumber < 1320 || HasAnyRelationsUsingOldColumnar()))\n\t\t\t{\n\t\t\t\tCreateExtensionWithVersion(\"citus_columnar\",\n\t\t\t\t\t\t\t\t\t\t   CITUS_COLUMNAR_INTERNAL_VERSION);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * HasAnyRelationsUsingOldColumnar returns true if there are any relations\n * using the old columnar access method.\n */\nstatic bool\nHasAnyRelationsUsingOldColumnar(void)\n{\n\tOid oldColumnarAMId = GetOldColumnarAMIdIfExists();\n\treturn OidIsValid(oldColumnarAMId) &&\n\t\t   HasAnyRelationsUsingAccessMethod(oldColumnarAMId);\n}\n\n\n/*\n * GetOldColumnarAMIdIfExists returns the oid of the old columnar access\n * method, i.e., the columnar access method that we had as part of \"citus\"\n * extension before we split it into \"citus_columnar\" at version 11.1, if\n * it exists. Otherwise, it returns InvalidOid.\n *\n * We know that it's \"old columnar\" only if the access method doesn't depend\n * on any extensions. This is because, in citus--11.0-4--11.1-1.sql, we\n * detach the columnar objects (including the access method) from citus\n * in preparation for splitting of the columnar into a separate extension.\n */\nstatic Oid\nGetOldColumnarAMIdIfExists(void)\n{\n\tOid columnarAMId = get_am_oid(\"columnar\", true);\n\tif (OidIsValid(columnarAMId) && !AccessMethodDependsOnAnyExtensions(columnarAMId))\n\t{\n\t\treturn columnarAMId;\n\t}\n\n\treturn InvalidOid;\n}\n\n\n/*\n * AccessMethodDependsOnAnyExtensions returns true if the access method\n * with the given accessMethodId depends on any extensions.\n */\nstatic bool\nAccessMethodDependsOnAnyExtensions(Oid accessMethodId)\n{\n\tScanKeyData key[3];\n\n\tRelation pgDepend = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_classid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(AccessMethodRelationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_objid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(accessMethodId));\n\n\tScanKeyInit(&key[2],\n\t\t\t\tAnum_pg_depend_objsubid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ,\n\t\t\t\tInt32GetDatum(0));\n\n\tSysScanDesc scan = systable_beginscan(pgDepend, DependDependerIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, 3, key);\n\n\tbool result = false;\n\n\tHeapTuple heapTuple = NULL;\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend dependForm = (Form_pg_depend) GETSTRUCT(heapTuple);\n\n\t\tif (dependForm->refclassid == ExtensionRelationId)\n\t\t{\n\t\t\tresult = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\ttable_close(pgDepend, AccessShareLock);\n\n\treturn result;\n}\n\n\n/*\n * HasAnyRelationsUsingAccessMethod returns true if there are any relations\n * using the access method with the given accessMethodId.\n */\nstatic bool\nHasAnyRelationsUsingAccessMethod(Oid accessMethodId)\n{\n\tScanKeyData key[1];\n\tRelation pgClass = table_open(RelationRelationId, AccessShareLock);\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_class_relam,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(accessMethodId));\n\n\tSysScanDesc scan = systable_beginscan(pgClass, InvalidOid, false, NULL, 1, key);\n\n\tbool result = HeapTupleIsValid(systable_getnext(scan));\n\n\tsystable_endscan(scan);\n\ttable_close(pgClass, AccessShareLock);\n\n\treturn result;\n}\n\n\n/*\n * PostprocessAlterExtensionCitusStmtForCitusColumnar process the case when upgrade citus\n * to version that support citus_columnar, or downgrade citus to lower version that\n * include columnar inside citus extension\n */\nvoid\nPostprocessAlterExtensionCitusStmtForCitusColumnar(Node *parseTree)\n{\n\tDefElem *newVersionValue = GetExtensionOption(\n\t\t((AlterExtensionStmt *) parseTree)->options, \"new_version\");\n\tOid citusColumnarOid = get_extension_oid(\"citus_columnar\", true);\n\tif (newVersionValue)\n\t{\n\t\tchar *newVersion = defGetString(newVersionValue);\n\t\tdouble newVersionNumber = GetExtensionVersionNumber(pstrdup(newVersion));\n\t\tif (newVersionNumber >= 1110 && citusColumnarOid != InvalidOid)\n\t\t{\n\t\t\t/*upgrade citus, after \"ALTER EXTENSION citus update to xxx\" updates citus_columnar Y to version Z. */\n\t\t\tchar *curColumnarVersion = get_extension_version(citusColumnarOid);\n\t\t\tif (strcmp(curColumnarVersion, CITUS_COLUMNAR_INTERNAL_VERSION) == 0)\n\t\t\t{\n\t\t\t\tAlterExtensionUpdateStmt(\"citus_columnar\", \"11.1-1\");\n\t\t\t}\n\t\t}\n\t\telse if (newVersionNumber < 1110 && citusColumnarOid != InvalidOid)\n\t\t{\n\t\t\t/*downgrade citus, after \"ALTER EXTENSION citus update to xxx\" drops citus_columnar extension */\n\t\t\tchar *curColumnarVersion = get_extension_version(citusColumnarOid);\n\t\t\tif (strcmp(curColumnarVersion, CITUS_COLUMNAR_INTERNAL_VERSION) == 0)\n\t\t\t{\n\t\t\t\tRemoveExtensionById(citusColumnarOid);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*alter extension citus update, need upgrade citus_columnar from Y to Z*/\n\t\tint versionNumber = (int) (100 * strtod(CITUS_MAJORVERSION, NULL));\n\t\tif (versionNumber >= 1110 && citusColumnarOid != InvalidOid)\n\t\t{\n\t\t\tchar *curColumnarVersion = get_extension_version(citusColumnarOid);\n\t\t\tif (strcmp(curColumnarVersion, CITUS_COLUMNAR_INTERNAL_VERSION) == 0)\n\t\t\t{\n\t\t\t\tAlterExtensionUpdateStmt(\"citus_columnar\", \"11.1-1\");\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * CreateExtensionDDLCommand returns a list of DDL statements (const char *) to be\n * executed on a node to recreate the extension addressed by the extensionAddress.\n */\nList *\nCreateExtensionDDLCommand(const ObjectAddress *extensionAddress)\n{\n\t/* generate a statement for creation of the extension in \"if not exists\" construct */\n\tNode *stmt = RecreateExtensionStmt(extensionAddress->objectId);\n\n\t/* capture ddl command for the create statement */\n\tconst char *ddlCommand = DeparseTreeNode(stmt);\n\n\tList *ddlCommands = list_make1((void *) ddlCommand);\n\n\t/* any privilege granted on FDWs that belong to the extension should be included */\n\tList *FDWGrants =\n\t\tGenerateGrantCommandsOnExtensionDependentFDWs(extensionAddress->objectId);\n\n\tddlCommands = list_concat(ddlCommands, FDWGrants);\n\n\treturn ddlCommands;\n}\n\n\n/*\n * RecreateExtensionStmt returns a parsetree for a CREATE EXTENSION statement that would\n * recreate the given extension on a new node.\n */\nstatic Node *\nRecreateExtensionStmt(Oid extensionOid)\n{\n\tCreateExtensionStmt *createExtensionStmt = makeNode(CreateExtensionStmt);\n\n\tchar *extensionName = get_extension_name(extensionOid);\n\n\tif (!extensionName)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t\terrmsg(\"extension with oid %u does not exist\",\n\t\t\t\t\t\t\t   extensionOid)));\n\t}\n\n\t/* schema DefElement related variables */\n\n\t/* set location to -1 as it is unknown */\n\tint location = -1;\n\n\t/* set extension name and if_not_exists fields */\n\tcreateExtensionStmt->extname = extensionName;\n\tcreateExtensionStmt->if_not_exists = true;\n\n\t/* get schema name that extension was created on */\n\tOid extensionSchemaOid = get_extension_schema(extensionOid);\n\tchar *extensionSchemaName = get_namespace_name(extensionSchemaOid);\n\n\t/* make DefEleme for extensionSchemaName */\n\tNode *schemaNameArg = (Node *) makeString(extensionSchemaName);\n\tDefElem *schemaDefElement = makeDefElem(\"schema\", schemaNameArg, location);\n\n\t/* append the schema name DefElem finally */\n\tcreateExtensionStmt->options = lappend(createExtensionStmt->options,\n\t\t\t\t\t\t\t\t\t\t   schemaDefElement);\n\n\tchar *extensionVersion = get_extension_version(extensionOid);\n\tif (extensionVersion != NULL)\n\t{\n\t\tNode *extensionVersionArg = (Node *) makeString(extensionVersion);\n\t\tDefElem *extensionVersionElement =\n\t\t\tmakeDefElem(\"new_version\", extensionVersionArg, location);\n\n\t\tcreateExtensionStmt->options = lappend(createExtensionStmt->options,\n\t\t\t\t\t\t\t\t\t\t\t   extensionVersionElement);\n\t}\n\n\treturn (Node *) createExtensionStmt;\n}\n\n\n/*\n * GenerateGrantCommandsOnExtensionDependentFDWs returns a list of commands that GRANTs\n * the privileges on FDWs that are depending on the given extension.\n */\nstatic List *\nGenerateGrantCommandsOnExtensionDependentFDWs(Oid extensionId)\n{\n\tList *commands = NIL;\n\tList *FDWOids = GetDependentFDWsToExtension(extensionId);\n\n\tOid FDWOid = InvalidOid;\n\tforeach_declared_oid(FDWOid, FDWOids)\n\t{\n\t\tAcl *aclEntry = GetPrivilegesForFDW(FDWOid);\n\n\t\tif (aclEntry == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tAclItem *privileges = ACL_DAT(aclEntry);\n\t\tint numberOfPrivsGranted = ACL_NUM(aclEntry);\n\n\t\tfor (int i = 0; i < numberOfPrivsGranted; i++)\n\t\t{\n\t\t\tcommands = list_concat(commands,\n\t\t\t\t\t\t\t\t   GenerateGrantOnFDWQueriesFromAclItem(FDWOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t&privileges[i]));\n\t\t}\n\t}\n\n\treturn commands;\n}\n\n\n/*\n * GetDependentFDWsToExtension gets an extension oid and returns the list of oids of FDWs\n * that are depending on the given extension.\n */\nList *\nGetDependentFDWsToExtension(Oid extensionId)\n{\n\tList *extensionFDWs = NIL;\n\tScanKeyData key[1];\n\tHeapTuple tup;\n\n\tRelation pgDepend = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_classid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(ForeignDataWrapperRelationId));\n\n\tSysScanDesc scan = systable_beginscan(pgDepend, DependDependerIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, lengthof(key), key);\n\n\twhile (HeapTupleIsValid(tup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend pgDependEntry = (Form_pg_depend) GETSTRUCT(tup);\n\n\t\tif (pgDependEntry->deptype == DEPENDENCY_EXTENSION &&\n\t\t\tpgDependEntry->refclassid == ExtensionRelationId &&\n\t\t\tpgDependEntry->refobjid == extensionId)\n\t\t{\n\t\t\textensionFDWs = lappend_oid(extensionFDWs, pgDependEntry->objid);\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\ttable_close(pgDepend, AccessShareLock);\n\n\treturn extensionFDWs;\n}\n\n\n/*\n * AlterExtensionSchemaStmtObjectAddress returns the ObjectAddress of the extension that is\n * the subject of the AlterObjectSchemaStmt. Errors if missing_ok is false.\n */\nList *\nAlterExtensionSchemaStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_EXTENSION);\n\n\tconst char *extensionName = strVal(stmt->object);\n\n\tOid extensionOid = get_extension_oid(extensionName, missing_ok);\n\n\tif (extensionOid == InvalidOid)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t\terrmsg(\"extension \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t   extensionName)));\n\t}\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, ExtensionRelationId, extensionOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * AlterExtensionUpdateStmtObjectAddress returns the ObjectAddress of the extension that is\n * the subject of the AlterExtensionStmt. Errors if missing_ok is false.\n */\nList *\nAlterExtensionUpdateStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterExtensionStmt *stmt = castNode(AlterExtensionStmt, node);\n\tconst char *extensionName = stmt->extname;\n\n\tOid extensionOid = get_extension_oid(extensionName, missing_ok);\n\n\tif (extensionOid == InvalidOid)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t\terrmsg(\"extension \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t   extensionName)));\n\t}\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, ExtensionRelationId, extensionOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * CreateExtensionWithVersion builds and execute create extension statements\n * per given extension name and extension verision\n */\nvoid\nCreateExtensionWithVersion(char *extname, char *extVersion)\n{\n\tCreateExtensionStmt *createExtensionStmt = makeNode(CreateExtensionStmt);\n\n\t/* set location to -1 as it is unknown */\n\tint location = -1;\n\n\t/* set extension name and if_not_exists fields */\n\tcreateExtensionStmt->extname = extname;\n\tcreateExtensionStmt->if_not_exists = true;\n\n\tif (extVersion == NULL)\n\t{\n\t\tcreateExtensionStmt->options = NIL;\n\t}\n\telse\n\t{\n\t\tNode *extensionVersionArg = (Node *) makeString(extVersion);\n\t\tDefElem *extensionVersionElement = makeDefElem(\"new_version\", extensionVersionArg,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   location);\n\t\tcreateExtensionStmt->options = lappend(createExtensionStmt->options,\n\t\t\t\t\t\t\t\t\t\t\t   extensionVersionElement);\n\t}\n\n\tCreateExtension(NULL, createExtensionStmt);\n\tCommandCounterIncrement();\n}\n\n\n/*\n * GetExtensionVersionNumber convert extension version to real value\n */\nint\nGetExtensionVersionNumber(char *extVersion)\n{\n\tchar *strtokPosition = NULL;\n\tchar *versionVal = strtok_r(extVersion, \"-\", &strtokPosition);\n\tdouble versionNumber = strtod(versionVal, NULL);\n\treturn (int) (versionNumber * 100);\n}\n\n\n/*\n * AlterExtensionUpdateStmt builds and execute Alter extension statements\n * per given extension name and updates extension verision\n */\nvoid\nAlterExtensionUpdateStmt(char *extname, char *extVersion)\n{\n\tAlterExtensionStmt *alterExtensionStmt = makeNode(AlterExtensionStmt);\n\n\t/* set location to -1 as it is unknown */\n\tint location = -1;\n\talterExtensionStmt->extname = extname;\n\n\tif (extVersion == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t\terrmsg(\"alter extension \\\"%s\\\" should not be empty\",\n\t\t\t\t\t\t\t   extVersion)));\n\t}\n\n\tNode *extensionVersionArg = (Node *) makeString(extVersion);\n\tDefElem *extensionVersionElement = makeDefElem(\"new_version\", extensionVersionArg,\n\t\t\t\t\t\t\t\t\t\t\t\t   location);\n\talterExtensionStmt->options = lappend(alterExtensionStmt->options,\n\t\t\t\t\t\t\t\t\t\t  extensionVersionElement);\n\tExecAlterExtensionStmt(NULL, alterExtensionStmt);\n\tCommandCounterIncrement();\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/foreign_constraint.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * foreign_constraint.c\n *\n * This file contains functions to create, alter and drop foreign\n * constraints on distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"access/sysattr.h\"\n#include \"access/xact.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_type.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/inval.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/relcache.h\"\n#include \"utils/ruleutils.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/sequence.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/version_compat.h\"\n\n\n#define BehaviorIsRestrictOrNoAction(x) \\\n\t\t((x) == FKCONSTR_ACTION_NOACTION || (x) == FKCONSTR_ACTION_RESTRICT)\n\n\n#define USE_CREATE_REFERENCE_TABLE_HINT \\\n\t\t\"You could use SELECT create_reference_table('%s') \" \\\n\t\t\"to replicate the referenced table to all nodes or \" \\\n\t\t\"consider dropping the foreign key\"\n\n\ntypedef bool (*CheckRelationFunc)(Oid);\n\n\n/* Local functions forward declarations */\nstatic void EnsureReferencingTableNotReplicated(Oid referencingTableId);\nstatic void EnsureSupportedFKeyOnDistKey(Form_pg_constraint constraintForm);\nstatic bool ForeignKeySetsNextValColumnToDefault(HeapTuple pgConstraintTuple);\nstatic List * ForeignKeyGetDefaultingAttrs(HeapTuple pgConstraintTuple);\nstatic void EnsureSupportedFKeyBetweenCitusLocalAndRefTable(Form_pg_constraint\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconstraintForm,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treferencingReplicationModel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treferencedReplicationModel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOid referencedTableId);\nstatic bool HeapTupleOfForeignConstraintIncludesColumn(HeapTuple heapTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   int pgConstraintKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   char *columnName);\nstatic Oid FindForeignKeyOidWithName(List *foreignKeyOids, const\n\t\t\t\t\t\t\t\t\t char *inputConstraintName);\nstatic void ForeignConstraintFindDistKeys(HeapTuple pgConstraintTuple,\n\t\t\t\t\t\t\t\t\t\t  Var *referencingDistColumn,\n\t\t\t\t\t\t\t\t\t\t  Var *referencedDistColumn,\n\t\t\t\t\t\t\t\t\t\t  int *referencingAttrIndex,\n\t\t\t\t\t\t\t\t\t\t  int *referencedAttrIndex);\nstatic List * GetForeignKeyIdsForColumn(char *columnName, Oid relationId,\n\t\t\t\t\t\t\t\t\t\tint searchForeignKeyColumnFlags);\nstatic List * GetForeignKeysWithLocalTables(Oid relationId);\nstatic bool IsTableTypeIncluded(Oid relationId, int flags);\n\n\n/*\n * ConstraintIsAForeignKeyToReferenceTable checks if the given constraint is a\n * foreign key constraint from the given relation to any reference table.\n */\nbool\nConstraintIsAForeignKeyToReferenceTable(char *inputConstaintName, Oid relationId)\n{\n\tint flags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_REFERENCE_TABLES;\n\tList *foreignKeyOids = GetForeignKeyOids(relationId, flags);\n\n\tOid foreignKeyOid = FindForeignKeyOidWithName(foreignKeyOids, inputConstaintName);\n\n\treturn OidIsValid(foreignKeyOid);\n}\n\n\n/*\n * EnsureNoFKeyFromTableType ensures that given relation is not referenced by any table specified\n * by table type flag.\n */\nvoid\nEnsureNoFKeyFromTableType(Oid relationId, int tableTypeFlag)\n{\n\tint flags = INCLUDE_REFERENCED_CONSTRAINTS | EXCLUDE_SELF_REFERENCES |\n\t\t\t\ttableTypeFlag;\n\tList *referencedFKeyOids = GetForeignKeyOids(relationId, flags);\n\n\tif (list_length(referencedFKeyOids) > 0)\n\t{\n\t\tOid referencingFKeyOid = linitial_oid(referencedFKeyOids);\n\t\tOid referencingTableId = GetReferencingTableId(referencingFKeyOid);\n\n\t\tchar *referencingRelName = get_rel_name(referencingTableId);\n\t\tchar *referencedRelName = get_rel_name(relationId);\n\t\tchar *referencingTableTypeName = GetTableTypeName(referencingTableId);\n\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"relation %s is referenced by a foreign key from %s\",\n\t\t\t\t\t\t\t   referencedRelName, referencingRelName),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"foreign keys from a %s to a distributed table are not supported.\",\n\t\t\t\t\t\t\treferencingTableTypeName)));\n\t}\n}\n\n\n/*\n * EnsureNoFKeyToTableType ensures that given relation is not referencing any table specified\n * by table type flag.\n */\nvoid\nEnsureNoFKeyToTableType(Oid relationId, int tableTypeFlag)\n{\n\tint flags = INCLUDE_REFERENCING_CONSTRAINTS | EXCLUDE_SELF_REFERENCES |\n\t\t\t\ttableTypeFlag;\n\tList *referencingFKeyOids = GetForeignKeyOids(relationId, flags);\n\n\tif (list_length(referencingFKeyOids) > 0)\n\t{\n\t\tOid referencedFKeyOid = linitial_oid(referencingFKeyOids);\n\t\tOid referencedTableId = GetReferencedTableId(referencedFKeyOid);\n\n\t\tchar *referencedRelName = get_rel_name(referencedTableId);\n\t\tchar *referencingRelName = get_rel_name(relationId);\n\t\tchar *referencedTableTypeName = GetTableTypeName(referencedTableId);\n\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"relation %s is referenced by a foreign key from %s\",\n\t\t\t\t\t\t\t   referencedRelName, referencingRelName),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"foreign keys from a distributed table to a %s are not supported.\",\n\t\t\t\t\t\t\treferencedTableTypeName)));\n\t}\n}\n\n\n/*\n * ErrorIfUnsupportedForeignConstraintExists runs checks related to foreign\n * constraints and errors out if it is not possible to create one of the\n * foreign constraint in distributed environment.\n *\n * To support foreign constraints, we require that;\n * - If referencing and referenced tables are hash-distributed\n *\t\t- Referencing and referenced tables are co-located.\n *      - Foreign constraint is defined over distribution column.\n *\t\t- ON DELETE/UPDATE SET NULL, ON DELETE/UPDATE SET DEFAULT and\n *        ON UPDATE CASCADE options\n *        are not used.\n *      - Replication factors of referencing and referenced table are 1.\n * - If referenced table is a reference table\n *      - ON DELETE/UPDATE SET NULL, ON DELETE/UPDATE SET DEFAULT and\n *        ON UPDATE CASCADE options are not used on the distribution key\n *        of the referencing column.\n * - If referencing table is a reference table, error out if the referenced\n *   table is a distributed table.\n * - If referencing table is a reference table and referenced table is a\n *   citus local table:\n *      - ON DELETE/UPDATE SET NULL, ON DELETE/UPDATE SET DEFAULT and\n *        ON CASCADE options are not used.\n * - If referencing or referenced table is distributed table, then the\n *   other table is not a citus local table.\n */\nvoid\nErrorIfUnsupportedForeignConstraintExists(Relation relation, char referencingDistMethod,\n\t\t\t\t\t\t\t\t\t\t  char referencingReplicationModel,\n\t\t\t\t\t\t\t\t\t\t  Var *referencingDistKey,\n\t\t\t\t\t\t\t\t\t\t  uint32 referencingColocationId)\n{\n\tOid referencingTableId = relation->rd_id;\n\n\tint flags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES;\n\tList *foreignKeyOids = GetForeignKeyOids(referencingTableId, flags);\n\n\tOid foreignKeyOid = InvalidOid;\n\tforeach_declared_oid(foreignKeyOid, foreignKeyOids)\n\t{\n\t\tHeapTuple heapTuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(foreignKeyOid));\n\n\t\tAssert(HeapTupleIsValid(heapTuple));\n\n\t\tForm_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);\n\n\t\tint referencingAttrIndex = -1;\n\n\t\tVar *referencedDistKey = NULL;\n\t\tint referencedAttrIndex = -1;\n\t\tuint32 referencedColocationId = INVALID_COLOCATION_ID;\n\n\t\tOid referencedTableId = constraintForm->confrelid;\n\t\tbool referencedIsCitus = IsCitusTable(referencedTableId);\n\n\t\tbool selfReferencingTable = (referencingTableId == referencedTableId);\n\n\t\tif (!referencedIsCitus && !selfReferencingTable)\n\t\t{\n\t\t\tif (IsCitusLocalTableByDistParams(referencingDistMethod,\n\t\t\t\t\t\t\t\t\t\t\t  referencingReplicationModel,\n\t\t\t\t\t\t\t\t\t\t\t  referencingColocationId))\n\t\t\t{\n\t\t\t\tErrorOutForFKeyBetweenPostgresAndCitusLocalTable(referencedTableId);\n\t\t\t}\n\n\t\t\tchar *referencedTableName = get_rel_name(referencedTableId);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION),\n\t\t\t\t\t\t\terrmsg(\"referenced table \\\"%s\\\" must be a distributed table\"\n\t\t\t\t\t\t\t\t   \" or a reference table\",\n\t\t\t\t\t\t\t\t   referencedTableName),\n\t\t\t\t\t\t\terrdetail(\"To enforce foreign keys, the referencing and \"\n\t\t\t\t\t\t\t\t\t  \"referenced rows need to be stored on the same \"\n\t\t\t\t\t\t\t\t\t  \"node.\"),\n\t\t\t\t\t\t\terrhint(USE_CREATE_REFERENCE_TABLE_HINT,\n\t\t\t\t\t\t\t\t\treferencedTableName)));\n\t\t}\n\n\t\t/* set referenced table related variables here if table is referencing itself */\n\t\tchar referencedDistMethod = 0;\n\t\tchar referencedReplicationModel = REPLICATION_MODEL_INVALID;\n\t\tif (!selfReferencingTable)\n\t\t{\n\t\t\treferencedDistMethod = PartitionMethod(referencedTableId);\n\t\t\treferencedDistKey = !HasDistributionKey(referencedTableId) ?\n\t\t\t\t\t\t\t\tNULL :\n\t\t\t\t\t\t\t\tDistPartitionKey(referencedTableId);\n\t\t\treferencedColocationId = TableColocationId(referencedTableId);\n\t\t\treferencedReplicationModel = TableReplicationModel(referencedTableId);\n\t\t}\n\t\telse\n\t\t{\n\t\t\treferencedDistMethod = referencingDistMethod;\n\t\t\treferencedDistKey = referencingDistKey;\n\t\t\treferencedColocationId = referencingColocationId;\n\t\t\treferencedReplicationModel = referencingReplicationModel;\n\t\t}\n\n\t\t/*\n\t\t * Given that we drop DEFAULT nextval('sequence') expressions from\n\t\t * shard relation columns, allowing ON DELETE/UPDATE SET DEFAULT\n\t\t * on such columns causes inserting NULL values to referencing relation\n\t\t * as a result of a delete/update operation on referenced relation.\n\t\t *\n\t\t * For this reason, we disallow ON DELETE/UPDATE SET DEFAULT actions\n\t\t * on columns that default to sequences.\n\t\t */\n\t\tif (ForeignKeySetsNextValColumnToDefault(heapTuple))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot create foreign key constraint \"\n\t\t\t\t\t\t\t\t   \"since Citus does not support ON DELETE \"\n\t\t\t\t\t\t\t\t   \"/ UPDATE SET DEFAULT actions on the \"\n\t\t\t\t\t\t\t\t   \"columns that default to sequences\")));\n\t\t}\n\n\t\tbool referencingIsCitusLocalOrRefTable =\n\t\t\tIsCitusLocalTableByDistParams(referencingDistMethod,\n\t\t\t\t\t\t\t\t\t\t  referencingReplicationModel,\n\t\t\t\t\t\t\t\t\t\t  referencingColocationId) ||\n\t\t\tIsReferenceTableByDistParams(referencingDistMethod,\n\t\t\t\t\t\t\t\t\t\t referencingReplicationModel);\n\t\tbool referencedIsCitusLocalOrRefTable =\n\t\t\tIsCitusLocalTableByDistParams(referencedDistMethod,\n\t\t\t\t\t\t\t\t\t\t  referencedReplicationModel,\n\t\t\t\t\t\t\t\t\t\t  referencedColocationId) ||\n\t\t\tIsReferenceTableByDistParams(referencedDistMethod,\n\t\t\t\t\t\t\t\t\t\t referencedReplicationModel);\n\t\tif (referencingIsCitusLocalOrRefTable && referencedIsCitusLocalOrRefTable)\n\t\t{\n\t\t\tEnsureSupportedFKeyBetweenCitusLocalAndRefTable(constraintForm,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treferencingReplicationModel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treferencedReplicationModel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treferencedTableId);\n\n\t\t\tReleaseSysCache(heapTuple);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Foreign keys from citus local tables or reference tables to distributed\n\t\t * tables are not supported.\n\t\t *\n\t\t * We could support foreign keys from references tables to single-shard\n\t\t * tables but this doesn't seem useful a lot. However, if we decide supporting\n\t\t * this, then we need to expand relation access tracking check for the single-shard\n\t\t * tables too.\n\t\t */\n\t\tif (referencingIsCitusLocalOrRefTable && !referencedIsCitusLocalOrRefTable)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot create foreign key constraint \"\n\t\t\t\t\t\t\t\t   \"since foreign keys from reference tables \"\n\t\t\t\t\t\t\t\t   \"and local tables to distributed tables \"\n\t\t\t\t\t\t\t\t   \"are not supported\"),\n\t\t\t\t\t\t\terrdetail(\"Reference tables and local tables \"\n\t\t\t\t\t\t\t\t\t  \"can only have foreign keys to reference \"\n\t\t\t\t\t\t\t\t\t  \"tables and local tables\")));\n\t\t}\n\n\t\t/*\n\t\t * To enforce foreign constraints, tables must be co-located unless a\n\t\t * reference table is referenced.\n\t\t */\n\t\tbool referencedIsReferenceTable =\n\t\t\tIsReferenceTableByDistParams(referencedDistMethod,\n\t\t\t\t\t\t\t\t\t\t referencedReplicationModel);\n\t\tif (!referencedIsReferenceTable && (\n\t\t\t\treferencingColocationId == INVALID_COLOCATION_ID ||\n\t\t\t\treferencingColocationId != referencedColocationId))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot create foreign key constraint since \"\n\t\t\t\t\t\t\t\t   \"relations are not colocated or not referencing \"\n\t\t\t\t\t\t\t\t   \"a reference table\"),\n\t\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\t\"A distributed table can only have foreign keys \"\n\t\t\t\t\t\t\t\t\"if it is referencing another colocated hash \"\n\t\t\t\t\t\t\t\t\"distributed table or a reference table\")));\n\t\t}\n\n\t\tForeignConstraintFindDistKeys(heapTuple,\n\t\t\t\t\t\t\t\t\t  referencingDistKey,\n\t\t\t\t\t\t\t\t\t  referencedDistKey,\n\t\t\t\t\t\t\t\t\t  &referencingAttrIndex,\n\t\t\t\t\t\t\t\t\t  &referencedAttrIndex);\n\t\tbool referencingColumnsIncludeDistKey = (referencingAttrIndex != -1);\n\t\tbool foreignConstraintOnDistKey =\n\t\t\t(referencingColumnsIncludeDistKey && referencingAttrIndex ==\n\t\t\t referencedAttrIndex);\n\n\t\t/*\n\t\t * If columns in the foreign key includes the distribution key from the\n\t\t * referencing side, we do not allow update/delete operations through\n\t\t * foreign key constraints (e.g. ... ON UPDATE SET NULL)\n\t\t */\n\t\tif (referencingColumnsIncludeDistKey)\n\t\t{\n\t\t\tEnsureSupportedFKeyOnDistKey(constraintForm);\n\t\t}\n\n\t\t/*\n\t\t * if tables are hash-distributed and colocated, we need to make sure that\n\t\t * the distribution key is included in foreign constraint.\n\t\t */\n\t\tbool referencedIsSingleShardTable =\n\t\t\tIsSingleShardTableByDistParams(referencedDistMethod,\n\t\t\t\t\t\t\t\t\t\t   referencedReplicationModel,\n\t\t\t\t\t\t\t\t\t\t   referencedColocationId);\n\t\tif (!referencedIsCitusLocalOrRefTable && !referencedIsSingleShardTable &&\n\t\t\t!foreignConstraintOnDistKey)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot create foreign key constraint\"),\n\t\t\t\t\t\t\terrdetail(\"Foreign keys are supported in two cases, \"\n\t\t\t\t\t\t\t\t\t  \"either in between two colocated tables including \"\n\t\t\t\t\t\t\t\t\t  \"partition column in the same ordinal in the both \"\n\t\t\t\t\t\t\t\t\t  \"tables or from distributed to reference tables\")));\n\t\t}\n\n\t\t/*\n\t\t * We do not allow to create foreign constraints if shard replication factor is\n\t\t * greater than 1. Because in our current design, multiple replicas may cause\n\t\t * locking problems and inconsistent shard contents.\n\t\t *\n\t\t * Note that we allow referenced table to be a reference table (e.g., not a\n\t\t * single replicated table). This is allowed since (a) we are sure that\n\t\t * placements always be in the same state (b) executors are aware of reference\n\t\t * tables and handle concurrency related issues accordingly.\n\t\t */\n\t\tEnsureReferencingTableNotReplicated(referencingTableId);\n\n\t\tReleaseSysCache(heapTuple);\n\t}\n}\n\n\n/*\n * ForeignKeySetsNextValColumnToDefault returns true if at least one of the\n * columns specified in ON DELETE / UPDATE SET DEFAULT clauses default to\n * nextval().\n */\nstatic bool\nForeignKeySetsNextValColumnToDefault(HeapTuple pgConstraintTuple)\n{\n\tForm_pg_constraint pgConstraintForm =\n\t\t(Form_pg_constraint) GETSTRUCT(pgConstraintTuple);\n\n\tList *setDefaultAttrs = ForeignKeyGetDefaultingAttrs(pgConstraintTuple);\n\tAttrNumber setDefaultAttr = InvalidAttrNumber;\n\tforeach_declared_int(setDefaultAttr, setDefaultAttrs)\n\t{\n\t\tif (ColumnDefaultsToNextVal(pgConstraintForm->conrelid, setDefaultAttr))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ForeignKeyGetDefaultingAttrs returns a list of AttrNumbers\n * might be set to default ON DELETE or ON UPDATE.\n *\n * For example; if the foreign key has SET DEFAULT clause for\n * both actions, then returns a superset of the attributes that\n * might be set to DEFAULT on either of those actions.\n */\nstatic List *\nForeignKeyGetDefaultingAttrs(HeapTuple pgConstraintTuple)\n{\n\tbool isNull = false;\n\tDatum referencingColumnsDatum = SysCacheGetAttr(CONSTROID, pgConstraintTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\tAnum_pg_constraint_conkey, &isNull);\n\tif (isNull)\n\t{\n\t\tereport(ERROR, (errmsg(\"got NULL conkey from catalog\")));\n\t}\n\n\tList *referencingColumns =\n\t\tIntegerArrayTypeToList(DatumGetArrayTypeP(referencingColumnsDatum));\n\n\tForm_pg_constraint pgConstraintForm =\n\t\t(Form_pg_constraint) GETSTRUCT(pgConstraintTuple);\n\tif (pgConstraintForm->confupdtype == FKCONSTR_ACTION_SETDEFAULT)\n\t{\n\t\t/*\n\t\t * Postgres doesn't allow specifying SET DEFAULT for a subset of\n\t\t * the referencing columns for ON UPDATE action, so in that case\n\t\t * we return all referencing columns regardless of what ON DELETE\n\t\t * action says.\n\t\t */\n\t\treturn referencingColumns;\n\t}\n\n\tif (pgConstraintForm->confdeltype != FKCONSTR_ACTION_SETDEFAULT)\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *onDeleteSetDefColumnList = NIL;\n\tDatum onDeleteSetDefColumnsDatum = SysCacheGetAttr(CONSTROID, pgConstraintTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   Anum_pg_constraint_confdelsetcols,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   &isNull);\n\n\t/*\n\t * confdelsetcols being NULL means that \"ON DELETE SET DEFAULT\" doesn't\n\t * specify which subset of columns should be set to DEFAULT, so fetching\n\t * NULL from the catalog is also possible.\n\t */\n\tif (!isNull)\n\t{\n\t\tonDeleteSetDefColumnList =\n\t\t\tIntegerArrayTypeToList(DatumGetArrayTypeP(onDeleteSetDefColumnsDatum));\n\t}\n\n\tif (list_length(onDeleteSetDefColumnList) == 0)\n\t{\n\t\t/*\n\t\t * That means that all referencing columns need to be set to\n\t\t * DEFAULT.\n\t\t */\n\t\treturn referencingColumns;\n\t}\n\telse\n\t{\n\t\treturn onDeleteSetDefColumnList;\n\t}\n}\n\n\n/*\n * EnsureSupportedFKeyBetweenCitusLocalAndRefTable is a helper function that\n * takes a foreign key constraint form for a foreign key between two citus\n * tables that are either citus local table or reference table and errors\n * out if it it an unsupported foreign key from a reference table to a citus\n * local table according to given replication model parameters.\n */\nstatic void\nEnsureSupportedFKeyBetweenCitusLocalAndRefTable(Form_pg_constraint fKeyConstraintForm,\n\t\t\t\t\t\t\t\t\t\t\t\tchar referencingReplicationModel,\n\t\t\t\t\t\t\t\t\t\t\t\tchar referencedReplicationModel,\n\t\t\t\t\t\t\t\t\t\t\t\tOid referencedTableId)\n{\n\tbool referencingIsReferenceTable =\n\t\t(referencingReplicationModel == REPLICATION_MODEL_2PC);\n\tbool referencedIsCitusLocalTable =\n\t\t(referencedReplicationModel != REPLICATION_MODEL_2PC);\n\tif (referencingIsReferenceTable && referencedIsCitusLocalTable)\n\t{\n\t\t/*\n\t\t * We only support RESTRICT and NO ACTION behaviors for the\n\t\t * foreign keys from reference tables to citus local tables.\n\t\t * This is because, we can't cascade dml operations from citus\n\t\t * local tables's coordinator placement to the remote placements\n\t\t * of the reference table.\n\t\t * Note that for the foreign keys from citus local tables to\n\t\t * reference tables, we support all foreign key behaviors.\n\t\t */\n\t\tif (!(BehaviorIsRestrictOrNoAction(fKeyConstraintForm->confdeltype) &&\n\t\t\t  BehaviorIsRestrictOrNoAction(fKeyConstraintForm->confupdtype)))\n\t\t{\n\t\t\tchar *referencedTableName = get_rel_name(referencedTableId);\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot define foreign key constraint, \"\n\t\t\t\t\t\t\t\t   \"foreign keys from reference tables to \"\n\t\t\t\t\t\t\t\t   \"local tables can only be defined \"\n\t\t\t\t\t\t\t\t   \"with NO ACTION or RESTRICT behaviors\"),\n\t\t\t\t\t\t\terrhint(USE_CREATE_REFERENCE_TABLE_HINT,\n\t\t\t\t\t\t\t\t\treferencedTableName)));\n\t\t}\n\t}\n}\n\n\n/*\n * EnsureSupportedFKeyOnDistKey errors out if given foreign key constraint form\n * implies an unsupported ON DELETE/UPDATE behavior assuming the referencing column\n * is the distribution column of the referencing distributed table.\n */\nstatic void\nEnsureSupportedFKeyOnDistKey(Form_pg_constraint fKeyConstraintForm)\n{\n\t/*\n\t * ON DELETE SET NULL and ON DELETE SET DEFAULT is not supported. Because we do\n\t * not want to set partition column to NULL or default value.\n\t */\n\tif (fKeyConstraintForm->confdeltype == FKCONSTR_ACTION_SETNULL ||\n\t\tfKeyConstraintForm->confdeltype == FKCONSTR_ACTION_SETDEFAULT)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot create foreign key constraint\"),\n\t\t\t\t\t\terrdetail(\"SET NULL or SET DEFAULT is not supported \"\n\t\t\t\t\t\t\t\t  \"in ON DELETE operation when distribution \"\n\t\t\t\t\t\t\t\t  \"key is included in the foreign key constraint\")));\n\t}\n\n\t/*\n\t * ON UPDATE SET NULL, ON UPDATE SET DEFAULT and UPDATE CASCADE is not supported.\n\t * Because we do not want to set partition column to NULL or default value. Also\n\t * cascading update operation would require re-partitioning. Updating partition\n\t * column value is not allowed anyway even outside of foreign key concept.\n\t */\n\tif (fKeyConstraintForm->confupdtype == FKCONSTR_ACTION_SETNULL ||\n\t\tfKeyConstraintForm->confupdtype == FKCONSTR_ACTION_SETDEFAULT ||\n\t\tfKeyConstraintForm->confupdtype == FKCONSTR_ACTION_CASCADE)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot create foreign key constraint\"),\n\t\t\t\t\t\terrdetail(\"SET NULL, SET DEFAULT or CASCADE is not \"\n\t\t\t\t\t\t\t\t  \"supported in ON UPDATE operation when \"\n\t\t\t\t\t\t\t\t  \"distribution key included in the foreign \"\n\t\t\t\t\t\t\t\t  \"constraint.\")));\n\t}\n}\n\n\n/*\n * EnsureReferencingTableNotReplicated takes referencingTableId for the\n * referencing table of the foreign key and errors out if it's not a single\n * replicated table.\n */\nstatic void\nEnsureReferencingTableNotReplicated(Oid referencingTableId)\n{\n\tbool referencingNotReplicated = true;\n\tbool referencingIsCitus = IsCitusTable(referencingTableId);\n\n\tif (referencingIsCitus)\n\t{\n\t\t/* ALTER TABLE command is applied over single replicated table */\n\t\treferencingNotReplicated = SingleReplicatedTable(referencingTableId);\n\t}\n\telse\n\t{\n\t\t/* Creating single replicated table with foreign constraint */\n\t\treferencingNotReplicated = !DistributedTableReplicationIsEnabled();\n\t}\n\n\tif (!referencingNotReplicated)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot create foreign key constraint\"),\n\t\t\t\t\t\terrdetail(\"Citus currently supports foreign key constraints \"\n\t\t\t\t\t\t\t\t  \"only for \\\"citus.shard_replication_factor = 1\\\".\"),\n\t\t\t\t\t\terrhint(\"Please change \\\"citus.shard_replication_factor to \"\n\t\t\t\t\t\t\t\t\"1\\\". To learn more about using foreign keys with \"\n\t\t\t\t\t\t\t\t\"other replication factors, please contact us at \"\n\t\t\t\t\t\t\t\t\"https://citusdata.com/about/contact_us.\")));\n\t}\n}\n\n\n/*\n * ErrorOutForFKeyBetweenPostgresAndCitusLocalTable is a helper function to\n * error out for foreign keys between postgres local tables and citus local\n * tables.\n */\nvoid\nErrorOutForFKeyBetweenPostgresAndCitusLocalTable(Oid localTableId)\n{\n\tchar *localTableName = get_rel_name(localTableId);\n\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\terrmsg(\"cannot create foreign key constraint as \\\"%s\\\" is \"\n\t\t\t\t\t\t   \"a postgres local table\", localTableName),\n\t\t\t\t\terrhint(\"first add local table to citus metadata \"\n\t\t\t\t\t\t\t\"by using SELECT citus_add_local_table_to_metadata('%s') \"\n\t\t\t\t\t\t\t\"and execute the ALTER TABLE command to create the \"\n\t\t\t\t\t\t\t\"foreign key to local table\", localTableName)));\n}\n\n\n/*\n * ForeignConstraintFindDistKeys finds the index of the given distribution columns\n * in the given foreign key constraint and returns them in referencingAttrIndex\n * and referencedAttrIndex. If one of them is not found, it returns -1 instead.\n */\nstatic void\nForeignConstraintFindDistKeys(HeapTuple pgConstraintTuple,\n\t\t\t\t\t\t\t  Var *referencingDistColumn,\n\t\t\t\t\t\t\t  Var *referencedDistColumn,\n\t\t\t\t\t\t\t  int *referencingAttrIndex,\n\t\t\t\t\t\t\t  int *referencedAttrIndex)\n{\n\tDatum *referencingColumnArray = NULL;\n\tint referencingColumnCount = 0;\n\tDatum *referencedColumnArray = NULL;\n\tint referencedColumnCount = 0;\n\tbool isNull = false;\n\n\t*referencedAttrIndex = -1;\n\t*referencedAttrIndex = -1;\n\n\t/*\n\t * Column attributes are not available in Form_pg_constraint, therefore we need\n\t * to find them in the system catalog. After finding them, we iterate over column\n\t * attributes together because partition column must be at the same place in both\n\t * referencing and referenced side of the foreign key constraint.\n\t */\n\tDatum referencingColumnsDatum = SysCacheGetAttr(CONSTROID, pgConstraintTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\tAnum_pg_constraint_conkey, &isNull);\n\tDatum referencedColumnsDatum = SysCacheGetAttr(CONSTROID, pgConstraintTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t   Anum_pg_constraint_confkey, &isNull);\n\n\tdeconstruct_array(DatumGetArrayTypeP(referencingColumnsDatum), INT2OID, 2, true,\n\t\t\t\t\t  's', &referencingColumnArray, NULL, &referencingColumnCount);\n\tdeconstruct_array(DatumGetArrayTypeP(referencedColumnsDatum), INT2OID, 2, true,\n\t\t\t\t\t  's', &referencedColumnArray, NULL, &referencedColumnCount);\n\n\tAssert(referencingColumnCount == referencedColumnCount);\n\n\tfor (int attrIdx = 0; attrIdx < referencingColumnCount; ++attrIdx)\n\t{\n\t\tAttrNumber referencingAttrNo = DatumGetInt16(referencingColumnArray[attrIdx]);\n\t\tAttrNumber referencedAttrNo = DatumGetInt16(referencedColumnArray[attrIdx]);\n\n\t\tif (referencedDistColumn != NULL &&\n\t\t\treferencedDistColumn->varattno == referencedAttrNo)\n\t\t{\n\t\t\t*referencedAttrIndex = attrIdx;\n\t\t}\n\n\t\tif (referencingDistColumn != NULL &&\n\t\t\treferencingDistColumn->varattno == referencingAttrNo)\n\t\t{\n\t\t\t*referencingAttrIndex = attrIdx;\n\t\t}\n\t}\n}\n\n\n/*\n * ColumnAppearsInForeignKey returns true if there is a foreign key constraint\n * from/to given column. False otherwise.\n */\nbool\nColumnAppearsInForeignKey(char *columnName, Oid relationId)\n{\n\tint searchForeignKeyColumnFlags = SEARCH_REFERENCING_RELATION |\n\t\t\t\t\t\t\t\t\t  SEARCH_REFERENCED_RELATION;\n\tList *foreignKeysColumnAppeared =\n\t\tGetForeignKeyIdsForColumn(columnName, relationId, searchForeignKeyColumnFlags);\n\treturn list_length(foreignKeysColumnAppeared) > 0;\n}\n\n\n/*\n * ColumnAppearsInForeignKeyToReferenceTable checks if there is a foreign key\n * constraint from/to any reference table on the given column.\n */\nbool\nColumnAppearsInForeignKeyToReferenceTable(char *columnName, Oid relationId)\n{\n\tint searchForeignKeyColumnFlags = SEARCH_REFERENCING_RELATION |\n\t\t\t\t\t\t\t\t\t  SEARCH_REFERENCED_RELATION;\n\tList *foreignKeyIdsColumnAppeared =\n\t\tGetForeignKeyIdsForColumn(columnName, relationId, searchForeignKeyColumnFlags);\n\n\tOid foreignKeyId = InvalidOid;\n\tforeach_declared_oid(foreignKeyId, foreignKeyIdsColumnAppeared)\n\t{\n\t\tOid referencedTableId = GetReferencedTableId(foreignKeyId);\n\t\tif (IsCitusTableType(referencedTableId, REFERENCE_TABLE))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * GetForeignKeyIdsForColumn takes columnName and relationId for the owning\n * relation, and returns a list of OIDs for foreign constraints that the column\n * with columnName is involved according to \"searchForeignKeyColumnFlags\" argument.\n * See SearchForeignKeyColumnFlags enum definition for usage.\n */\nstatic List *\nGetForeignKeyIdsForColumn(char *columnName, Oid relationId,\n\t\t\t\t\t\t  int searchForeignKeyColumnFlags)\n{\n\tbool searchReferencing = searchForeignKeyColumnFlags & SEARCH_REFERENCING_RELATION;\n\tbool searchReferenced = searchForeignKeyColumnFlags & SEARCH_REFERENCED_RELATION;\n\n\t/* at least one of them should be true */\n\tAssert(searchReferencing || searchReferenced);\n\n\tList *foreignKeyIdsColumnAppeared = NIL;\n\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\n\tRelation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_constraint_contype, BTEqualStrategyNumber,\n\t\t\t\tF_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgConstraint, InvalidOid, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tint pgConstraintKey = 0;\n\t\tForm_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);\n\n\t\tOid referencedTableId = constraintForm->confrelid;\n\t\tOid referencingTableId = constraintForm->conrelid;\n\n\t\tif (referencedTableId == relationId && searchReferenced)\n\t\t{\n\t\t\tpgConstraintKey = Anum_pg_constraint_confkey;\n\t\t}\n\t\telse if (referencingTableId == relationId && searchReferencing)\n\t\t{\n\t\t\tpgConstraintKey = Anum_pg_constraint_conkey;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * If the constraint is not from/to the given relation, we should simply\n\t\t\t * skip.\n\t\t\t */\n\t\t\theapTuple = systable_getnext(scanDescriptor);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (HeapTupleOfForeignConstraintIncludesColumn(heapTuple, relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   pgConstraintKey, columnName))\n\t\t{\n\t\t\tforeignKeyIdsColumnAppeared = lappend_oid(foreignKeyIdsColumnAppeared,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  constraintForm->oid);\n\t\t}\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\t/* clean up scan and close system catalog */\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgConstraint, NoLock);\n\n\treturn foreignKeyIdsColumnAppeared;\n}\n\n\n/*\n * GetReferencingForeignConstaintCommands takes in a relationId, and\n * returns the list of foreign constraint commands needed to reconstruct\n * foreign key constraints that the table is involved in as the \"referencing\"\n * one.\n */\nList *\nGetReferencingForeignConstaintCommands(Oid relationId)\n{\n\tint flags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES;\n\treturn GetForeignConstraintCommandsInternal(relationId, flags);\n}\n\n\n/*\n * GetForeignConstraintToReferenceTablesCommands takes in a relationId, and\n * returns the list of foreign constraint commands needed to reconstruct\n * foreign key constraints that the table is involved in as the \"referencing\"\n * one and the \"referenced\" table is a reference table.\n */\nList *\nGetForeignConstraintToReferenceTablesCommands(Oid relationId)\n{\n\tint flags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_REFERENCE_TABLES;\n\treturn GetForeignConstraintCommandsInternal(relationId, flags);\n}\n\n\n/*\n * GetForeignConstraintToReferenceTablesCommands takes in a relationId, and\n * returns the list of foreign constraint commands needed to reconstruct\n * foreign key constraints that the table is involved in as the \"referenced\"\n * one and the \"referencing\" table is a reference table.\n */\nList *\nGetForeignConstraintFromOtherReferenceTablesCommands(Oid relationId)\n{\n\tint flags = INCLUDE_REFERENCED_CONSTRAINTS |\n\t\t\t\tEXCLUDE_SELF_REFERENCES |\n\t\t\t\tINCLUDE_REFERENCE_TABLES;\n\treturn GetForeignConstraintCommandsInternal(relationId, flags);\n}\n\n\n/*\n * GetForeignConstraintToDistributedTablesCommands takes in a relationId, and\n * returns the list of foreign constraint commands needed to reconstruct\n * foreign key constraints that the table is involved in as the \"referencing\"\n * one and the \"referenced\" table is a distributed table.\n */\nList *\nGetForeignConstraintToDistributedTablesCommands(Oid relationId)\n{\n\tint flags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_DISTRIBUTED_TABLES;\n\treturn GetForeignConstraintCommandsInternal(relationId, flags);\n}\n\n\n/*\n * GetForeignConstraintFromDistributedTablesCommands takes in a relationId, and\n * returns the list of foreign constraint commands needed to reconstruct\n * foreign key constraints that the table is involved in as the \"referenced\"\n * one and the \"referencing\" table is a distributed table.\n */\nList *\nGetForeignConstraintFromDistributedTablesCommands(Oid relationId)\n{\n\tint flags = INCLUDE_REFERENCED_CONSTRAINTS | INCLUDE_DISTRIBUTED_TABLES;\n\treturn GetForeignConstraintCommandsInternal(relationId, flags);\n}\n\n\n/*\n * GetForeignConstraintCommandsInternal is a wrapper function to get the\n * DDL commands to recreate the foreign key constraints returned by\n * GetForeignKeyOids. See more details at the underlying function.\n */\nList *\nGetForeignConstraintCommandsInternal(Oid relationId, int flags)\n{\n\tList *foreignKeyOids = GetForeignKeyOids(relationId, flags);\n\n\tList *foreignKeyCommands = NIL;\n\n\tint saveNestLevel = PushEmptySearchPath();\n\n\tOid foreignKeyOid = InvalidOid;\n\tforeach_declared_oid(foreignKeyOid, foreignKeyOids)\n\t{\n\t\tchar *statementDef = pg_get_constraintdef_command(foreignKeyOid);\n\n\t\tforeignKeyCommands = lappend(foreignKeyCommands, statementDef);\n\t}\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n\n\treturn foreignKeyCommands;\n}\n\n\n/*\n * GetFKeyCreationCommandsRelationInvolvedWithTableType returns a list of DDL\n * commands to recreate the foreign keys that relation with relationId is involved\n * with given table type.\n */\nList *\nGetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag)\n{\n\tint referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS |\n\t\t\t\t\t\t\t   tableTypeFlag;\n\tList *referencingFKeyCreationCommands =\n\t\tGetForeignConstraintCommandsInternal(relationId, referencingFKeysFlag);\n\n\t/* already captured self referencing foreign keys, so use EXCLUDE_SELF_REFERENCES */\n\tint referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS |\n\t\t\t\t\t\t\t  EXCLUDE_SELF_REFERENCES |\n\t\t\t\t\t\t\t  tableTypeFlag;\n\tList *referencedFKeyCreationCommands =\n\t\tGetForeignConstraintCommandsInternal(relationId, referencedFKeysFlag);\n\treturn list_concat(referencingFKeyCreationCommands, referencedFKeyCreationCommands);\n}\n\n\n/*\n * DropFKeysRelationInvolvedWithTableType drops foreign keys that relation\n * with relationId is involved with given table type.\n */\nvoid\nDropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag)\n{\n\tint referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS |\n\t\t\t\t\t\t\t   tableTypeFlag;\n\tDropRelationForeignKeys(relationId, referencingFKeysFlag);\n\n\t/* already captured self referencing foreign keys, so use EXCLUDE_SELF_REFERENCES */\n\tint referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS |\n\t\t\t\t\t\t\t  EXCLUDE_SELF_REFERENCES |\n\t\t\t\t\t\t\t  tableTypeFlag;\n\tDropRelationForeignKeys(relationId, referencedFKeysFlag);\n}\n\n\n/*\n * HasForeignKeyWithLocalTable returns true if relation has foreign key\n * relationship with a local table.\n */\nbool\nHasForeignKeyWithLocalTable(Oid relationId)\n{\n\tList *foreignKeysWithLocalTables = GetForeignKeysWithLocalTables(relationId);\n\treturn list_length(foreignKeysWithLocalTables) > 0;\n}\n\n\n/*\n * GetForeignKeysWithLocalTables returns a list of foreign keys for foreign key\n * relationships that relation has with local tables.\n */\nstatic List *\nGetForeignKeysWithLocalTables(Oid relationId)\n{\n\tint referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS |\n\t\t\t\t\t\t\t   INCLUDE_LOCAL_TABLES;\n\tList *referencingFKeyList = GetForeignKeyOids(relationId, referencingFKeysFlag);\n\n\t/* already captured self referencing foreign keys, so use EXCLUDE_SELF_REFERENCES */\n\tint referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS |\n\t\t\t\t\t\t\t  EXCLUDE_SELF_REFERENCES |\n\t\t\t\t\t\t\t  INCLUDE_LOCAL_TABLES;\n\tList *referencedFKeyList = GetForeignKeyOids(relationId, referencedFKeysFlag);\n\treturn list_concat(referencingFKeyList, referencedFKeyList);\n}\n\n\n/*\n * GetForeignKeysFromLocalTables returns a list of foreign keys where the referencing\n * relation is a local table.\n */\nList *\nGetForeignKeysFromLocalTables(Oid relationId)\n{\n\tint referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS |\n\t\t\t\t\t\t\t  INCLUDE_LOCAL_TABLES;\n\tList *referencingFKeyList = GetForeignKeyOids(relationId, referencedFKeysFlag);\n\n\treturn referencingFKeyList;\n}\n\n\n/*\n * HasForeignKeyToReferenceTable returns true if any of the foreign key\n * constraints on the relation with relationId references to a reference\n * table.\n */\nbool\nHasForeignKeyToReferenceTable(Oid relationId)\n{\n\tint flags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_REFERENCE_TABLES;\n\tList *foreignKeyOids = GetForeignKeyOids(relationId, flags);\n\n\treturn list_length(foreignKeyOids) > 0;\n}\n\n\n/*\n * TableReferenced function checks whether given table is referenced by another table\n * via foreign constraints. If it is referenced, this function returns true.\n */\nbool\nTableReferenced(Oid relationId)\n{\n\tint flags = INCLUDE_REFERENCED_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES;\n\tList *foreignKeyOids = GetForeignKeyOids(relationId, flags);\n\n\treturn list_length(foreignKeyOids) > 0;\n}\n\n\n/*\n * HeapTupleOfForeignConstraintIncludesColumn fetches the columns from the foreign\n * constraint and checks if the given column name matches one of them.\n */\nstatic bool\nHeapTupleOfForeignConstraintIncludesColumn(HeapTuple heapTuple, Oid relationId,\n\t\t\t\t\t\t\t\t\t\t   int pgConstraintKey, char *columnName)\n{\n\tDatum *columnArray = NULL;\n\tint columnCount = 0;\n\tbool isNull = false;\n\n\tDatum columnsDatum = SysCacheGetAttr(CONSTROID, heapTuple, pgConstraintKey, &isNull);\n\tdeconstruct_array(DatumGetArrayTypeP(columnsDatum), INT2OID, 2, true,\n\t\t\t\t\t  's', &columnArray, NULL, &columnCount);\n\n\tfor (int attrIdx = 0; attrIdx < columnCount; ++attrIdx)\n\t{\n\t\tAttrNumber attrNo = DatumGetInt16(columnArray[attrIdx]);\n\n\t\tchar *colName = get_attname(relationId, attrNo, false);\n\t\tif (strncmp(colName, columnName, NAMEDATALEN) == 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * TableReferencing function checks whether given table is referencing to another\n * table via foreign key constraints. If it is referencing, this function returns\n * true.\n */\nbool\nTableReferencing(Oid relationId)\n{\n\tint flags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES;\n\tList *foreignKeyOids = GetForeignKeyOids(relationId, flags);\n\n\treturn list_length(foreignKeyOids) > 0;\n}\n\n\n/*\n * ConstraintIsAUniquenessConstraint is a wrapper around ConstraintWithNameIsOfType\n * that returns true if given constraint name identifies a uniqueness constraint, i.e:\n *   - primary key constraint, or\n *   - unique constraint\n */\nbool\nConstraintIsAUniquenessConstraint(char *inputConstaintName, Oid relationId)\n{\n\tbool isUniqueConstraint = ConstraintWithNameIsOfType(inputConstaintName, relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t CONSTRAINT_UNIQUE);\n\tbool isPrimaryConstraint = ConstraintWithNameIsOfType(inputConstaintName, relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  CONSTRAINT_PRIMARY);\n\treturn isUniqueConstraint || isPrimaryConstraint;\n}\n\n\n/*\n * ConstraintIsAForeignKey is a wrapper around ConstraintWithNameIsOfType that returns true\n * if given constraint name identifies a foreign key constraint.\n */\nbool\nConstraintIsAForeignKey(char *inputConstaintName, Oid relationId)\n{\n\treturn ConstraintWithNameIsOfType(inputConstaintName, relationId, CONSTRAINT_FOREIGN);\n}\n\n\n/*\n * ConstraintWithNameIsOfType is a wrapper around get_relation_constraint_oid that\n * returns true if given constraint name identifies a valid constraint defined\n * on relation with relationId and its type matches the input constraint type.\n */\nbool\nConstraintWithNameIsOfType(char *inputConstaintName, Oid relationId,\n\t\t\t\t\t\t   char targetConstraintType)\n{\n\tbool missingOk = true;\n\tOid constraintId =\n\t\tget_relation_constraint_oid(relationId, inputConstaintName, missingOk);\n\treturn ConstraintWithIdIsOfType(constraintId, targetConstraintType);\n}\n\n\n/*\n * ConstraintWithIdIsOfType returns true if constraint with constraintId exists\n * and is of type targetConstraintType.\n */\nbool\nConstraintWithIdIsOfType(Oid constraintId, char targetConstraintType)\n{\n\tHeapTuple heapTuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintId));\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\t/* no such constraint */\n\t\treturn false;\n\t}\n\n\tForm_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);\n\tchar constraintType = constraintForm->contype;\n\tbool constraintTypeMatches = (constraintType == targetConstraintType);\n\n\tReleaseSysCache(heapTuple);\n\n\treturn constraintTypeMatches;\n}\n\n\n/*\n * FindForeignKeyOidWithName searches the foreign key constraint with\n * inputConstraintName in the given list of foreign key constraint OIDs.\n * Returns the OID of the matching constraint. If there no matching constraint\n * in the given list, then returns InvalidOid.\n */\nstatic Oid\nFindForeignKeyOidWithName(List *foreignKeyOids, const char *inputConstraintName)\n{\n\tOid foreignKeyOid = InvalidOid;\n\tforeach_declared_oid(foreignKeyOid, foreignKeyOids)\n\t{\n\t\tchar *constraintName = get_constraint_name(foreignKeyOid);\n\n\t\tAssert(constraintName != NULL);\n\n\t\tif (strncmp(constraintName, inputConstraintName, NAMEDATALEN) == 0)\n\t\t{\n\t\t\treturn foreignKeyOid;\n\t\t}\n\t}\n\n\treturn InvalidOid;\n}\n\n\n/*\n * TableHasExternalForeignKeys returns true if the relation with relationId is\n * involved in a foreign key relationship other than the self-referencing ones.\n */\nbool\nTableHasExternalForeignKeys(Oid relationId)\n{\n\tint flags = (INCLUDE_REFERENCING_CONSTRAINTS | EXCLUDE_SELF_REFERENCES |\n\t\t\t\t INCLUDE_ALL_TABLE_TYPES);\n\tList *foreignKeyIdsTableReferencing = GetForeignKeyOids(relationId, flags);\n\n\tflags = (INCLUDE_REFERENCED_CONSTRAINTS | EXCLUDE_SELF_REFERENCES |\n\t\t\t INCLUDE_ALL_TABLE_TYPES);\n\tList *foreignKeyIdsTableReferenced = GetForeignKeyOids(relationId, flags);\n\n\tList *foreignKeysWithOtherTables = list_concat(foreignKeyIdsTableReferencing,\n\t\t\t\t\t\t\t\t\t\t\t\t   foreignKeyIdsTableReferenced);\n\n\tif (list_length(foreignKeysWithOtherTables) == 0)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ForeignConstraintMatchesFlags is a function with logic that's very specific\n * to GetForeignKeyOids. There's no reason to use it in any other context.\n */\nstatic bool\nForeignConstraintMatchesFlags(Form_pg_constraint constraintForm,\n\t\t\t\t\t\t\t  int flags)\n{\n\tif (constraintForm->contype != CONSTRAINT_FOREIGN)\n\t{\n\t\treturn false;\n\t}\n\n\tbool inheritedConstraint = OidIsValid(constraintForm->conparentid);\n\tif (inheritedConstraint)\n\t{\n\t\t/*\n\t\t * We only consider the constraints that are explicitly created on\n\t\t * the table as we already process the constraints from parent tables\n\t\t * implicitly when a command is issued\n\t\t */\n\t\treturn false;\n\t}\n\n\tbool excludeSelfReference = (flags & EXCLUDE_SELF_REFERENCES);\n\tbool isSelfReference = (constraintForm->conrelid == constraintForm->confrelid);\n\tif (excludeSelfReference && isSelfReference)\n\t{\n\t\treturn false;\n\t}\n\n\tOid otherTableId = InvalidOid;\n\tif (flags & INCLUDE_REFERENCING_CONSTRAINTS)\n\t{\n\t\totherTableId = constraintForm->confrelid;\n\t}\n\telse\n\t{\n\t\totherTableId = constraintForm->conrelid;\n\t}\n\n\treturn IsTableTypeIncluded(otherTableId, flags);\n}\n\n\n/*\n * GetForeignKeyOidsForReferencedTable returns a list of foreign key OIDs that\n * reference the relationId and match the given flags.\n *\n * This is separated from GetForeignKeyOids because we need to scan pg_depend\n * instead of pg_constraint directly. The reason for this is that there is no\n * index on the confrelid of pg_constraint, so searching by that column\n * requires a seqscan.\n */\nstatic List *\nGetForeignKeyOidsForReferencedTable(Oid relationId, int flags)\n{\n\tHTAB *foreignKeyOidsSet = CreateSimpleHashSetWithName(\n\t\tOid, \"ReferencingForeignKeyOidsSet\");\n\tList *foreignKeyOidsList = NIL;\n\tScanKeyData key[2];\n\tHeapTuple dependTup;\n\tRelation depRel = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_refclassid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(RelationRelationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_refobjid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(relationId));\n\tSysScanDesc scan = systable_beginscan(depRel, DependReferenceIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, lengthof(key), key);\n\twhile (HeapTupleIsValid(dependTup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend deprec = (Form_pg_depend) GETSTRUCT(dependTup);\n\n\t\tif (deprec->classid != ConstraintRelationId ||\n\t\t\tdeprec->deptype != DEPENDENCY_NORMAL ||\n\t\t\thash_search(foreignKeyOidsSet, &deprec->objid, HASH_FIND, NULL))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\n\t\tHeapTuple constraintTup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(\n\t\t\t\t\t\t\t\t\t\t\t\t\t  deprec->objid));\n\t\tif (!HeapTupleIsValid(constraintTup)) /* can happen during DROP TABLE */\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tForm_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(constraintTup);\n\t\tif (constraint->confrelid == relationId &&\n\t\t\tForeignConstraintMatchesFlags(constraint, flags))\n\t\t{\n\t\t\tforeignKeyOidsList = lappend_oid(foreignKeyOidsList, constraint->oid);\n\t\t\thash_search(foreignKeyOidsSet, &constraint->oid, HASH_ENTER, NULL);\n\t\t}\n\t\tReleaseSysCache(constraintTup);\n\t}\n\tsystable_endscan(scan);\n\ttable_close(depRel, AccessShareLock);\n\treturn foreignKeyOidsList;\n}\n\n\n/*\n * GetForeignKeyOids takes in a relationId, and returns a list of OIDs for\n * foreign constraints that the relation with relationId is involved according\n * to \"flags\" argument. See ExtractForeignKeyConstraintsMode enum definition\n * for usage of the flags.\n */\nList *\nGetForeignKeyOids(Oid relationId, int flags)\n{\n\tbool extractReferencing PG_USED_FOR_ASSERTS_ONLY = (flags &\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tINCLUDE_REFERENCING_CONSTRAINTS);\n\tbool extractReferenced = (flags & INCLUDE_REFERENCED_CONSTRAINTS);\n\n\t/*\n\t * Only one of them should be passed at a time since the way we scan\n\t * pg_constraint differs for those columns. Anum_pg_constraint_conrelid\n\t * supports index scan while Anum_pg_constraint_confrelid does not.\n\t */\n\tAssert(!(extractReferencing && extractReferenced));\n\tAssert(extractReferencing || extractReferenced);\n\n\tif (extractReferenced)\n\t{\n\t\treturn GetForeignKeyOidsForReferencedTable(relationId, flags);\n\t}\n\n\tList *foreignKeyOids = NIL;\n\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\n\tRelation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);\n\tScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgConstraint,\n\t\t\t\t\t\t\t\t\t\t\t\t\tConstraintRelidTypidNameIndexId, true,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple;\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tForm_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);\n\n\t\tif (ForeignConstraintMatchesFlags(constraintForm, flags))\n\t\t{\n\t\t\tforeignKeyOids = lappend_oid(foreignKeyOids, constraintForm->oid);\n\t\t}\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\n\t/*\n\t * Do not release AccessShareLock yet to prevent modifications to be done\n\t * on pg_constraint to make sure that caller will process valid foreign key\n\t * constraints through the transaction.\n\t */\n\ttable_close(pgConstraint, NoLock);\n\n\treturn foreignKeyOids;\n}\n\n\n/*\n * GetReferencedTableId returns OID of the referenced relation for the foreign\n * key with foreignKeyId. If there is no such foreign key, then this function\n * returns InvalidOid.\n */\nOid\nGetReferencedTableId(Oid foreignKeyId)\n{\n\tHeapTuple heapTuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(foreignKeyId));\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\t/* no such foreign key */\n\t\treturn InvalidOid;\n\t}\n\n\tForm_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);\n\tOid referencedTableId = constraintForm->confrelid;\n\n\tReleaseSysCache(heapTuple);\n\n\treturn referencedTableId;\n}\n\n\n/*\n * GetReferencingTableId returns OID of the referencing relation for the foreign\n * key with foreignKeyId. If there is no such foreign key, then this function\n * returns InvalidOid.\n */\nOid\nGetReferencingTableId(Oid foreignKeyId)\n{\n\tHeapTuple heapTuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(foreignKeyId));\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\t/* no such foreign key */\n\t\treturn InvalidOid;\n\t}\n\n\tForm_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);\n\tOid referencingTableId = constraintForm->conrelid;\n\n\tReleaseSysCache(heapTuple);\n\n\treturn referencingTableId;\n}\n\n\n/*\n * IsTableTypeIncluded returns true if type of the table with relationId (distributed,\n * reference, Citus local or Postgres local) is included in the flags, false if not\n */\nstatic bool\nIsTableTypeIncluded(Oid relationId, int flags)\n{\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn (flags & INCLUDE_LOCAL_TABLES) != 0;\n\t}\n\telse if (IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED))\n\t{\n\t\treturn (flags & INCLUDE_SINGLE_SHARD_TABLES) != 0;\n\t}\n\telse if (IsCitusTableType(relationId, DISTRIBUTED_TABLE))\n\t{\n\t\treturn (flags & INCLUDE_DISTRIBUTED_TABLES) != 0;\n\t}\n\telse if (IsCitusTableType(relationId, REFERENCE_TABLE))\n\t{\n\t\treturn (flags & INCLUDE_REFERENCE_TABLES) != 0;\n\t}\n\telse if (IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\treturn (flags & INCLUDE_CITUS_LOCAL_TABLES) != 0;\n\t}\n\treturn false;\n}\n\n\n/*\n * RelationInvolvedInAnyNonInheritedForeignKeys returns true if relation involved\n * in a foreign key that is not inherited from its parent relation.\n */\nbool\nRelationInvolvedInAnyNonInheritedForeignKeys(Oid relationId)\n{\n\tList *referencingForeignKeys = GetForeignKeyOids(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t INCLUDE_REFERENCING_CONSTRAINTS |\n\t\t\t\t\t\t\t\t\t\t\t\t\t INCLUDE_ALL_TABLE_TYPES);\n\n\t/*\n\t * We already capture self-referencing foreign keys above, so use\n\t * EXCLUDE_SELF_REFERENCES here\n\t */\n\tList *referencedForeignKeys = GetForeignKeyOids(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tINCLUDE_REFERENCED_CONSTRAINTS |\n\t\t\t\t\t\t\t\t\t\t\t\t\tEXCLUDE_SELF_REFERENCES |\n\t\t\t\t\t\t\t\t\t\t\t\t\tINCLUDE_ALL_TABLE_TYPES);\n\tList *foreignKeysRelationInvolved = list_concat(referencingForeignKeys,\n\t\t\t\t\t\t\t\t\t\t\t\t\treferencedForeignKeys);\n\tOid foreignKeyId = InvalidOid;\n\tforeach_declared_oid(foreignKeyId, foreignKeysRelationInvolved)\n\t{\n\t\tHeapTuple heapTuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(foreignKeyId));\n\t\tif (!HeapTupleIsValid(heapTuple))\n\t\t{\n\t\t\t/* not possible but be on the safe side */\n\t\t\tcontinue;\n\t\t}\n\n\t\tForm_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);\n\t\tOid parentConstraintId = constraintForm->conparentid;\n\t\tif (!OidIsValid(parentConstraintId))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/foreign_data_wrapper.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * foreign_data_wrapper.c\n *    Commands for FOREIGN DATA WRAPPER statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/pg_foreign_data_wrapper.h\"\n#include \"foreign/foreign.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n\nstatic bool NameListHasFDWOwnedByDistributedExtension(List *FDWNames);\nstatic ObjectAddress GetObjectAddressByFDWName(char *FDWName, bool missing_ok);\n\n\n/*\n * PreprocessGrantOnFDWStmt is executed before the statement is applied to the\n * local postgres instance.\n *\n * In this stage we can prepare the commands that need to be run on all workers to grant\n * on foreign data wrappers.\n */\nList *\nPreprocessGrantOnFDWStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_FDW);\n\n\tif (!NameListHasFDWOwnedByDistributedExtension(stmt->objects))\n\t{\n\t\t/*\n\t\t * We propagate granted privileges on a FDW only if it belongs to a distributed\n\t\t * extension. For now, we skip for custom FDWs, as most of the users prefer\n\t\t * extension FDWs.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tif (list_length(stmt->objects) > 1)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot grant on FDW with other FDWs\"),\n\t\t\t\t\t\terrhint(\"Try granting on each object in separate commands\")));\n\t}\n\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(stmt->objects) == 1);\n\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * NameListHasFDWOwnedByDistributedExtension takes a namelist of FDWs and returns true\n * if at least one of them depends on a distributed extension. Returns false otherwise.\n */\nstatic bool\nNameListHasFDWOwnedByDistributedExtension(List *FDWNames)\n{\n\tString *FDWValue = NULL;\n\tforeach_declared_ptr(FDWValue, FDWNames)\n\t{\n\t\t/* captures the extension address during lookup */\n\t\tObjectAddress *extensionAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddress FDWAddress = GetObjectAddressByFDWName(strVal(FDWValue), false);\n\n\t\tObjectAddress *copyFDWAddress = palloc0(sizeof(ObjectAddress));\n\t\t*copyFDWAddress = FDWAddress;\n\t\tif (IsAnyObjectAddressOwnedByExtension(list_make1(copyFDWAddress),\n\t\t\t\t\t\t\t\t\t\t\t   extensionAddress))\n\t\t{\n\t\t\tif (IsAnyObjectDistributed(list_make1(extensionAddress)))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * GetObjectAddressByFDWName takes a FDW name and returns the object address.\n */\nstatic ObjectAddress\nGetObjectAddressByFDWName(char *FDWName, bool missing_ok)\n{\n\tForeignDataWrapper *FDW = GetForeignDataWrapperByName(FDWName, missing_ok);\n\tOid FDWId = FDW->fdwid;\n\tObjectAddress address = { 0 };\n\tObjectAddressSet(address, ForeignDataWrapperRelationId, FDWId);\n\n\treturn address;\n}\n\n\n/*\n * GetPrivilegesForFDW takes a FDW object id and returns the privileges granted\n * on that FDW as a Acl object. Returns NULL if there is no privilege granted.\n */\nAcl *\nGetPrivilegesForFDW(Oid FDWOid)\n{\n\tHeapTuple fdwtup = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(FDWOid));\n\n\tbool isNull = true;\n\tDatum aclDatum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, fdwtup,\n\t\t\t\t\t\t\t\t\t Anum_pg_foreign_data_wrapper_fdwacl, &isNull);\n\tif (isNull)\n\t{\n\t\tReleaseSysCache(fdwtup);\n\t\treturn NULL;\n\t}\n\n\tAcl *aclEntry = DatumGetAclPCopy(aclDatum);\n\n\tReleaseSysCache(fdwtup);\n\n\treturn aclEntry;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/foreign_server.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * foreign_server.c\n *    Commands for FOREIGN SERVER statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/pg_foreign_server.h\"\n#include \"foreign/foreign.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/primnodes.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/worker_transaction.h\"\n\nstatic char * GetForeignServerAlterOwnerCommand(Oid serverId);\nstatic Node * RecreateForeignServerStmt(Oid serverId);\nstatic bool NameListHasDistributedServer(List *serverNames);\nstatic List * GetObjectAddressByServerName(char *serverName, bool missing_ok);\n\n\n/*\n * CreateForeignServerStmtObjectAddress finds the ObjectAddress for the server\n * that is created by given CreateForeignServerStmt. If missingOk is false and if\n * the server does not exist, then it errors out.\n *\n * Never returns NULL, but the objid in the address can be invalid if missingOk\n * was set to true.\n */\nList *\nCreateForeignServerStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tCreateForeignServerStmt *stmt = castNode(CreateForeignServerStmt, node);\n\n\treturn GetObjectAddressByServerName(stmt->servername, missing_ok);\n}\n\n\n/*\n * AlterForeignServerStmtObjectAddress finds the ObjectAddress for the server that is\n * changed by given AlterForeignServerStmt. If missingOk is false and if\n * the server does not exist, then it errors out.\n *\n * Never returns NULL, but the objid in the address can be invalid if missingOk\n * was set to true.\n */\nList *\nAlterForeignServerStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterForeignServerStmt *stmt = castNode(AlterForeignServerStmt, node);\n\n\treturn GetObjectAddressByServerName(stmt->servername, missing_ok);\n}\n\n\n/*\n * PreprocessGrantOnForeignServerStmt is executed before the statement is applied to the\n * local postgres instance.\n *\n * In this stage we can prepare the commands that need to be run on all workers to grant\n * on servers.\n */\nList *\nPreprocessGrantOnForeignServerStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_FOREIGN_SERVER);\n\n\tbool includesDistributedServer = NameListHasDistributedServer(stmt->objects);\n\n\tif (!includesDistributedServer)\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (list_length(stmt->objects) > 1)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot grant on distributed server with other servers\"),\n\t\t\t\t\t\terrhint(\"Try granting on each object in separate commands\")));\n\t}\n\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(stmt->objects) == 1);\n\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * RenameForeignServerStmtObjectAddress finds the ObjectAddress for the server that is\n * renamed by given RenmaeStmt. If missingOk is false and if the server does not exist,\n * then it errors out.\n *\n * Never returns NULL, but the objid in the address can be invalid if missingOk\n * was set to true.\n */\nList *\nRenameForeignServerStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_FOREIGN_SERVER);\n\n\treturn GetObjectAddressByServerName(strVal(stmt->object), missing_ok);\n}\n\n\n/*\n * AlterForeignServerOwnerStmtObjectAddress finds the ObjectAddress for the server\n * given in AlterOwnerStmt. If missingOk is false and if\n * the server does not exist, then it errors out.\n *\n * Never returns NULL, but the objid in the address can be invalid if missingOk\n * was set to true.\n */\nList *\nAlterForeignServerOwnerStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tchar *serverName = strVal(stmt->object);\n\n\treturn GetObjectAddressByServerName(serverName, missing_ok);\n}\n\n\n/*\n * GetForeignServerCreateDDLCommand returns a list that includes the CREATE SERVER\n * command that would recreate the given server on a new node.\n */\nList *\nGetForeignServerCreateDDLCommand(Oid serverId)\n{\n\t/* generate a statement for creation of the server in \"if not exists\" construct */\n\tNode *stmt = RecreateForeignServerStmt(serverId);\n\n\t/* capture ddl command for the create statement */\n\tconst char *createCommand = DeparseTreeNode(stmt);\n\tconst char *alterOwnerCommand = GetForeignServerAlterOwnerCommand(serverId);\n\n\tList *ddlCommands = list_make2((void *) createCommand,\n\t\t\t\t\t\t\t\t   (void *) alterOwnerCommand);\n\n\treturn ddlCommands;\n}\n\n\n/*\n * GetForeignServerAlterOwnerCommand returns \"ALTER SERVER .. OWNER TO ..\" statement\n * for the specified foreign server.\n */\nstatic char *\nGetForeignServerAlterOwnerCommand(Oid serverId)\n{\n\tForeignServer *server = GetForeignServer(serverId);\n\tOid ownerId = server->owner;\n\tchar *ownerName = GetUserNameFromId(ownerId, false);\n\n\tStringInfo alterCommand = makeStringInfo();\n\n\tappendStringInfo(alterCommand, \"ALTER SERVER %s OWNER TO %s;\",\n\t\t\t\t\t quote_identifier(server->servername),\n\t\t\t\t\t quote_identifier(ownerName));\n\n\treturn alterCommand->data;\n}\n\n\n/*\n * RecreateForeignServerStmt returns a parsetree for a CREATE SERVER statement\n * that would recreate the given server on a new node.\n */\nstatic Node *\nRecreateForeignServerStmt(Oid serverId)\n{\n\tForeignServer *server = GetForeignServer(serverId);\n\n\tCreateForeignServerStmt *createStmt = makeNode(CreateForeignServerStmt);\n\n\t/* set server name and if_not_exists fields */\n\tcreateStmt->servername = pstrdup(server->servername);\n\tcreateStmt->if_not_exists = true;\n\n\t/* set foreign data wrapper */\n\tForeignDataWrapper *fdw = GetForeignDataWrapper(server->fdwid);\n\tcreateStmt->fdwname = pstrdup(fdw->fdwname);\n\n\t/* set all fields using the existing server */\n\tif (server->servertype != NULL)\n\t{\n\t\tcreateStmt->servertype = pstrdup(server->servertype);\n\t}\n\n\tif (server->serverversion != NULL)\n\t{\n\t\tcreateStmt->version = pstrdup(server->serverversion);\n\t}\n\n\tcreateStmt->options = NIL;\n\n\tint location = -1;\n\tDefElem *option = NULL;\n\tforeach_declared_ptr(option, server->options)\n\t{\n\t\tDefElem *copyOption = makeDefElem(option->defname, option->arg, location);\n\t\tcreateStmt->options = lappend(createStmt->options, copyOption);\n\t}\n\n\treturn (Node *) createStmt;\n}\n\n\n/*\n * NameListHasDistributedServer takes a namelist of servers and returns true if at least\n * one of them is distributed. Returns false otherwise.\n */\nstatic bool\nNameListHasDistributedServer(List *serverNames)\n{\n\tString *serverValue = NULL;\n\tforeach_declared_ptr(serverValue, serverNames)\n\t{\n\t\tList *addresses = GetObjectAddressByServerName(strVal(serverValue), false);\n\n\t\t/*  the code-path only supports a single object */\n\t\tAssert(list_length(addresses) == 1);\n\n\t\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\t\tObjectAddress *address = linitial(addresses);\n\n\t\tif (IsAnyObjectDistributed(list_make1(address)))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\nstatic List *\nGetObjectAddressByServerName(char *serverName, bool missing_ok)\n{\n\tForeignServer *server = GetForeignServerByName(serverName, missing_ok);\n\tOid serverOid = (server) ? server->serverid : InvalidOid;\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, ForeignServerRelationId, serverOid);\n\n\treturn list_make1(address);\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/function.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * function.c\n *    Commands for FUNCTION statements.\n *\n *    We currently support replicating function definitions on the\n *    coordinator in all the worker nodes in the form of\n *\n *    CREATE OR REPLACE FUNCTION ... queries and\n *    GRANT ... ON FUNCTION queries\n *\n *\n *    ALTER or DROP operations are not yet propagated.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_aggregate.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/extension.h\"\n#include \"nodes/makefuncs.h\"\n#include \"parser/parse_coerce.h\"\n#include \"parser/parse_type.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/fmgrprotos.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/regproc.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_depended_object.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/maintenanced.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata/pg_dist_object.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"distributed/pg_dist_node.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_create_or_replace.h\"\n#include \"distributed/worker_transaction.h\"\n\n#define DISABLE_LOCAL_CHECK_FUNCTION_BODIES \"SET LOCAL check_function_bodies TO off;\"\n#define RESET_CHECK_FUNCTION_BODIES \"RESET check_function_bodies;\"\n#define argumentStartsWith(arg, prefix) \\\n\t\t(strncmp(arg, prefix, strlen(prefix)) == 0)\n\n/* forward declaration for helper functions*/\nstatic bool RecreateSameNonColocatedFunction(ObjectAddress functionAddress,\n\t\t\t\t\t\t\t\t\t\t\t char *distributionArgumentName,\n\t\t\t\t\t\t\t\t\t\t\t bool colocateWithTableNameDefault,\n\t\t\t\t\t\t\t\t\t\t\t bool *forceDelegationAddress);\nstatic void ErrorIfAnyNodeDoesNotHaveMetadata(void);\nstatic char * GetAggregateDDLCommand(const RegProcedure funcOid, bool useCreateOrReplace);\nstatic char * GetFunctionAlterOwnerCommand(const RegProcedure funcOid);\nstatic int GetDistributionArgIndex(Oid functionOid, char *distributionArgumentName,\n\t\t\t\t\t\t\t\t   Oid *distributionArgumentOid);\nstatic int GetFunctionColocationId(Oid functionOid, char *colocateWithName, Oid\n\t\t\t\t\t\t\t\t   distributionArgumentOid);\nstatic void EnsureFunctionCanBeColocatedWithTable(Oid functionOid, Oid\n\t\t\t\t\t\t\t\t\t\t\t\t  distributionColumnType, Oid\n\t\t\t\t\t\t\t\t\t\t\t\t  sourceRelationId);\nstatic bool ShouldPropagateCreateFunction(CreateFunctionStmt *stmt);\nstatic bool ShouldPropagateAlterFunction(const ObjectAddress *address);\nstatic bool ShouldAddFunctionSignature(FunctionParameterMode mode);\nstatic List * FunctionToObjectAddress(ObjectType objectType,\n\t\t\t\t\t\t\t\t\t  ObjectWithArgs *objectWithArgs,\n\t\t\t\t\t\t\t\t\t  bool missing_ok);\nstatic void ErrorIfUnsupportedAlterFunctionStmt(AlterFunctionStmt *stmt);\nstatic char * quote_qualified_func_name(Oid funcOid);\nstatic void DistributeFunctionWithDistributionArgument(RegProcedure funcOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   char *distributionArgumentName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   Oid distributionArgumentOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   char *colocateWithTableName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   bool *forceDelegationAddress,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   const ObjectAddress *\n\t\t\t\t\t\t\t\t\t\t\t\t\t   functionAddress);\nstatic void DistributeFunctionColocatedWithDistributedTable(RegProcedure funcOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar *colocateWithTableName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst ObjectAddress *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfunctionAddress);\nstatic void DistributeFunctionColocatedWithSingleShardTable(const ObjectAddress *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfunctionAddress, text *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolocateWithText);\nstatic void DistributeFunctionColocatedWithReferenceTable(const\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ObjectAddress *functionAddress);\nstatic List * FilterDistributedFunctions(GrantStmt *grantStmt);\n\nstatic void EnsureExtensionFunctionCanBeDistributed(const ObjectAddress functionAddress,\n\t\t\t\t\t\t\t\t\t\t\t\t\tconst ObjectAddress extensionAddress,\n\t\t\t\t\t\t\t\t\t\t\t\t\tchar *distributionArgumentName);\n\nPG_FUNCTION_INFO_V1(create_distributed_function);\n\n\n/*\n * create_distributed_function gets a function or procedure name with their list of\n * argument types in parantheses, then it creates a new distributed function.\n */\nDatum\ncreate_distributed_function(PG_FUNCTION_ARGS)\n{\n\tRegProcedure funcOid = PG_GETARG_OID(0);\n\n\ttext *distributionArgumentNameText = NULL; /* optional */\n\ttext *colocateWithText = NULL; /* optional */\n\n\tStringInfoData ddlCommand = { 0 };\n\tObjectAddress *functionAddress = palloc0(sizeof(ObjectAddress));\n\n\tOid distributionArgumentOid = InvalidOid;\n\tbool colocatedWithReferenceTable = false;\n\tbool colocatedWithSingleShardTable = false;\n\n\tchar *distributionArgumentName = NULL;\n\tchar *colocateWithTableName = NULL;\n\tbool colocateWithTableNameDefault = false;\n\tbool *forceDelegationAddress = NULL;\n\tbool forceDelegation = false;\n\tObjectAddress extensionAddress = { 0 };\n\n\t/* if called on NULL input, error out */\n\tif (funcOid == InvalidOid)\n\t{\n\t\tereport(ERROR, (errmsg(\"the first parameter for create_distributed_function() \"\n\t\t\t\t\t\t\t   \"should be a single a valid function or procedure name \"\n\t\t\t\t\t\t\t   \"followed by a list of parameters in parantheses\"),\n\t\t\t\t\t\terrhint(\"skip the parameters with OUT argtype as they are not \"\n\t\t\t\t\t\t\t\t\"part of the signature in PostgreSQL\")));\n\t}\n\n\tif (PG_ARGISNULL(1))\n\t{\n\t\t/*\n\t\t * Using the default value, so distribute the function but do not set\n\t\t * the distribution argument.\n\t\t */\n\t\tdistributionArgumentName = NULL;\n\t}\n\telse\n\t{\n\t\tdistributionArgumentNameText = PG_GETARG_TEXT_P(1);\n\t\tdistributionArgumentName = text_to_cstring(distributionArgumentNameText);\n\t}\n\n\tif (PG_ARGISNULL(2))\n\t{\n\t\tereport(ERROR, (errmsg(\"colocate_with parameter should not be NULL\"),\n\t\t\t\t\t\terrhint(\"To use the default value, set colocate_with option \"\n\t\t\t\t\t\t\t\t\"to \\\"default\\\"\")));\n\t}\n\telse\n\t{\n\t\tcolocateWithText = PG_GETARG_TEXT_P(2);\n\t\tcolocateWithTableName = text_to_cstring(colocateWithText);\n\n\t\tif (pg_strncasecmp(colocateWithTableName, \"default\", NAMEDATALEN) == 0)\n\t\t{\n\t\t\tcolocateWithTableNameDefault = true;\n\t\t}\n\n\t\t/* check if the colocation belongs to a reference table */\n\t\tif (!colocateWithTableNameDefault)\n\t\t{\n\t\t\tOid colocationRelationId = ResolveRelationId(colocateWithText, false);\n\t\t\tcolocatedWithReferenceTable = IsCitusTableType(colocationRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   REFERENCE_TABLE);\n\t\t\tcolocatedWithSingleShardTable = IsCitusTableType(colocationRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t SINGLE_SHARD_DISTRIBUTED);\n\t\t}\n\t}\n\n\t/* check if the force_delegation flag is explicitly set (default is NULL) */\n\tif (PG_ARGISNULL(3))\n\t{\n\t\tforceDelegationAddress = NULL;\n\t}\n\telse\n\t{\n\t\tforceDelegation = PG_GETARG_BOOL(3);\n\t\tforceDelegationAddress = &forceDelegation;\n\t}\n\n\tEnsureCoordinator();\n\tEnsureFunctionOwner(funcOid);\n\n\tObjectAddressSet(*functionAddress, ProcedureRelationId, funcOid);\n\n\tif (RecreateSameNonColocatedFunction(*functionAddress,\n\t\t\t\t\t\t\t\t\t\t distributionArgumentName,\n\t\t\t\t\t\t\t\t\t\t colocateWithTableNameDefault,\n\t\t\t\t\t\t\t\t\t\t forceDelegationAddress))\n\t{\n\t\tchar *schemaName = get_namespace_name(get_func_namespace(funcOid));\n\t\tchar *functionName = get_func_name(funcOid);\n\t\tchar *qualifiedName = quote_qualified_identifier(schemaName, functionName);\n\t\tereport(NOTICE, (errmsg(\"procedure %s is already distributed\", qualifiedName),\n\t\t\t\t\t\t errdetail(\"Citus distributes procedures with CREATE \"\n\t\t\t\t\t\t\t\t   \"[PROCEDURE|FUNCTION|AGGREGATE] commands\")));\n\t\tPG_RETURN_VOID();\n\t}\n\n\t/*\n\t * If the function is owned by an extension, only update the\n\t * pg_dist_object, and not propagate the CREATE FUNCTION. Function\n\t * will be created by the virtue of the extension creation.\n\t */\n\tif (IsAnyObjectAddressOwnedByExtension(list_make1(functionAddress),\n\t\t\t\t\t\t\t\t\t\t   &extensionAddress))\n\t{\n\t\tEnsureExtensionFunctionCanBeDistributed(*functionAddress, extensionAddress,\n\t\t\t\t\t\t\t\t\t\t\t\tdistributionArgumentName);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * when we allow propagation within a transaction block we should make sure\n\t\t * to only allow this in sequential mode.\n\t\t */\n\t\tEnsureSequentialMode(OBJECT_FUNCTION);\n\n\t\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(functionAddress));\n\n\t\tconst char *createFunctionSQL = GetFunctionDDLCommand(funcOid, true);\n\t\tconst char *alterFunctionOwnerSQL = GetFunctionAlterOwnerCommand(funcOid);\n\t\tinitStringInfo(&ddlCommand);\n\t\tappendStringInfo(&ddlCommand, \"%s;%s;%s\", DISABLE_METADATA_SYNC,\n\t\t\t\t\t\t createFunctionSQL, alterFunctionOwnerSQL);\n\t\tList *grantDDLCommands = GrantOnFunctionDDLCommands(funcOid);\n\t\tchar *grantOnFunctionSQL = NULL;\n\t\tforeach_declared_ptr(grantOnFunctionSQL, grantDDLCommands)\n\t\t{\n\t\t\tappendStringInfo(&ddlCommand, \";%s\", grantOnFunctionSQL);\n\t\t}\n\n\t\tappendStringInfo(&ddlCommand, \";%s\", ENABLE_METADATA_SYNC);\n\n\t\tSendCommandToWorkersAsUser(NON_COORDINATOR_NODES, CurrentUserName(),\n\t\t\t\t\t\t\t\t   ddlCommand.data);\n\t}\n\n\tMarkObjectDistributed(functionAddress);\n\n\tif (distributionArgumentName != NULL)\n\t{\n\t\t/*\n\t\t * Prior to Citus 11, this code was triggering metadata\n\t\t * syncing. However, with Citus 11+, we expect the metadata\n\t\t * has already been synced.\n\t\t */\n\t\tErrorIfAnyNodeDoesNotHaveMetadata();\n\n\t\tDistributeFunctionWithDistributionArgument(funcOid, distributionArgumentName,\n\t\t\t\t\t\t\t\t\t\t\t\t   distributionArgumentOid,\n\t\t\t\t\t\t\t\t\t\t\t\t   colocateWithTableName,\n\t\t\t\t\t\t\t\t\t\t\t\t   forceDelegationAddress,\n\t\t\t\t\t\t\t\t\t\t\t\t   functionAddress);\n\t}\n\telse if (!colocatedWithReferenceTable && !colocatedWithSingleShardTable)\n\t{\n\t\tDistributeFunctionColocatedWithDistributedTable(funcOid, colocateWithTableName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfunctionAddress);\n\t}\n\telse if (colocatedWithSingleShardTable)\n\t{\n\t\tDistributeFunctionColocatedWithSingleShardTable(functionAddress,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolocateWithText);\n\t}\n\telse if (colocatedWithReferenceTable)\n\t{\n\t\t/*\n\t\t * Prior to Citus 11, this code was triggering metadata\n\t\t * syncing. However, with Citus 11+, we expect the metadata\n\t\t * has already been synced.\n\t\t */\n\t\tErrorIfAnyNodeDoesNotHaveMetadata();\n\n\t\tDistributeFunctionColocatedWithReferenceTable(functionAddress);\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * RecreateSameNonColocatedFunction returns true if the given parameters of\n * create_distributed_function will not change anything on the given function.\n * Returns false otherwise.\n */\nstatic bool\nRecreateSameNonColocatedFunction(ObjectAddress functionAddress,\n\t\t\t\t\t\t\t\t char *distributionArgumentName,\n\t\t\t\t\t\t\t\t bool colocateWithTableNameDefault,\n\t\t\t\t\t\t\t\t bool *forceDelegationAddress)\n{\n\tDistObjectCacheEntry *cacheEntry =\n\t\tLookupDistObjectCacheEntry(ProcedureRelationId,\n\t\t\t\t\t\t\t\t   functionAddress.objectId,\n\t\t\t\t\t\t\t\t   InvalidOid);\n\n\tif (cacheEntry == NULL || !cacheEntry->isValid || !cacheEntry->isDistributed)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * If the colocationId, forceDelegation and distributionArgIndex fields of a\n\t * pg_dist_object entry of a distributed function are all set to zero, it means\n\t * that function is either automatically distributed by ddl propagation, without\n\t * calling create_distributed_function. Or, it could be distributed via\n\t * create_distributed_function, but with no parameters.\n\t *\n\t * For these cases, calling create_distributed_function for that function,\n\t * without parameters would be idempotent. Hence we can simply early return here,\n\t * by providing a notice message to the user.\n\t */\n\n\t/* are pg_dist_object fields set to zero? */\n\tbool functionDistributedWithoutParams =\n\t\tcacheEntry->colocationId == 0 &&\n\t\tcacheEntry->forceDelegation == 0 &&\n\t\tcacheEntry->distributionArgIndex == 0;\n\n\t/* called create_distributed_function without parameters? */\n\tbool distributingAgainWithNoParams =\n\t\tdistributionArgumentName == NULL &&\n\t\tcolocateWithTableNameDefault &&\n\t\tforceDelegationAddress == NULL;\n\n\treturn functionDistributedWithoutParams && distributingAgainWithNoParams;\n}\n\n\n/*\n * ErrorIfAnyNodeDoesNotHaveMetadata throws error if any\n * of the worker nodes does not have the metadata.\n */\nstatic void\nErrorIfAnyNodeDoesNotHaveMetadata(void)\n{\n\tList *workerNodeList =\n\t\tActivePrimaryNonCoordinatorNodeList(ShareLock);\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tif (!workerNode->hasMetadata)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t\terrmsg(\"cannot process the distributed function \"\n\t\t\t\t\t\t\t\t   \"since the node %s:%d does not have metadata \"\n\t\t\t\t\t\t\t\t   \"synced and this command requires all the nodes \"\n\t\t\t\t\t\t\t\t   \"have the metadata sycned\", workerNode->workerName,\n\t\t\t\t\t\t\t\t   workerNode->workerPort),\n\t\t\t\t\t\t\terrhint(\"To sync the metadata execute: \"\n\t\t\t\t\t\t\t\t\t\"SELECT enable_citus_mx_for_pre_citus11();\")));\n\t\t}\n\t}\n}\n\n\n/*\n * DistributeFunctionWithDistributionArgument updates pg_dist_object records for\n * a function/procedure that has a distribution argument, and triggers metadata\n * sync so that the functions can be delegated on workers.\n */\nstatic void\nDistributeFunctionWithDistributionArgument(RegProcedure funcOid,\n\t\t\t\t\t\t\t\t\t\t   char *distributionArgumentName,\n\t\t\t\t\t\t\t\t\t\t   Oid distributionArgumentOid,\n\t\t\t\t\t\t\t\t\t\t   char *colocateWithTableName,\n\t\t\t\t\t\t\t\t\t\t   bool *forceDelegationAddress,\n\t\t\t\t\t\t\t\t\t\t   const ObjectAddress *functionAddress)\n{\n\t/* get the argument index, or error out if we cannot find a valid index */\n\tint distributionArgumentIndex =\n\t\tGetDistributionArgIndex(funcOid, distributionArgumentName,\n\t\t\t\t\t\t\t\t&distributionArgumentOid);\n\n\t/* get the colocation id, or error out if we cannot find an appropriate one */\n\tint colocationId =\n\t\tGetFunctionColocationId(funcOid, colocateWithTableName,\n\t\t\t\t\t\t\t\tdistributionArgumentOid);\n\n\t/* record the distribution argument and colocationId */\n\tUpdateFunctionDistributionInfo(functionAddress, &distributionArgumentIndex,\n\t\t\t\t\t\t\t\t   &colocationId,\n\t\t\t\t\t\t\t\t   forceDelegationAddress);\n}\n\n\n/*\n * DistributeFunctionColocatedWithDistributedTable updates pg_dist_object records for\n * a function/procedure that is colocated with a distributed table.\n */\nstatic void\nDistributeFunctionColocatedWithDistributedTable(RegProcedure funcOid,\n\t\t\t\t\t\t\t\t\t\t\t\tchar *colocateWithTableName,\n\t\t\t\t\t\t\t\t\t\t\t\tconst ObjectAddress *functionAddress)\n{\n\t/*\n\t * cannot provide colocate_with without distribution_arg_name when the function\n\t * is not colocated with a reference table\n\t */\n\tif (pg_strncasecmp(colocateWithTableName, \"default\", NAMEDATALEN) != 0)\n\t{\n\t\tchar *functionName = get_func_name(funcOid);\n\n\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"cannot distribute the function \\\"%s\\\" since the \"\n\t\t\t\t\t\t\t   \"distribution argument is not valid \", functionName),\n\t\t\t\t\t\terrhint(\"To provide \\\"colocate_with\\\" option with a\"\n\t\t\t\t\t\t\t\t\" distributed table, the distribution argument\"\n\t\t\t\t\t\t\t\t\" parameter should also be provided\")));\n\t}\n\n\t/* set distribution argument and colocationId to NULL */\n\tUpdateFunctionDistributionInfo(functionAddress, NULL, NULL, NULL);\n}\n\n\n/*\n * DistributeFunctionColocatedWithSingleShardTable updates pg_dist_object records for\n * a function/procedure that is colocated with a single shard table.\n */\nstatic void\nDistributeFunctionColocatedWithSingleShardTable(const ObjectAddress *functionAddress,\n\t\t\t\t\t\t\t\t\t\t\t\ttext *colocateWithText)\n{\n\t/* get the single shard table's colocation id */\n\tint colocationId = TableColocationId(ResolveRelationId(colocateWithText, false));\n\n\t/* set distribution argument to NULL */\n\tint *distributionArgumentIndex = NULL;\n\tUpdateFunctionDistributionInfo(functionAddress, distributionArgumentIndex,\n\t\t\t\t\t\t\t\t   &colocationId,\n\t\t\t\t\t\t\t\t   NULL);\n}\n\n\n/*\n * DistributeFunctionColocatedWithReferenceTable updates pg_dist_object records for\n * a function/procedure that is colocated with a reference table.\n */\nstatic void\nDistributeFunctionColocatedWithReferenceTable(const ObjectAddress *functionAddress)\n{\n\t/* get the reference table colocation id */\n\tint colocationId = CreateReferenceTableColocationId();\n\n\t/* set distribution argument to NULL and colocationId to the reference table colocation id */\n\tint *distributionArgumentIndex = NULL;\n\tUpdateFunctionDistributionInfo(functionAddress, distributionArgumentIndex,\n\t\t\t\t\t\t\t\t   &colocationId,\n\t\t\t\t\t\t\t\t   NULL);\n}\n\n\n/*\n * CreateFunctionDDLCommandsIdempotent returns a list of DDL statements (const char *) to be\n * executed on a node to recreate the function addressed by the functionAddress.\n */\nList *\nCreateFunctionDDLCommandsIdempotent(const ObjectAddress *functionAddress)\n{\n\tAssert(functionAddress->classId == ProcedureRelationId);\n\n\tchar *ddlCommand = GetFunctionDDLCommand(functionAddress->objectId, true);\n\tchar *alterFunctionOwnerSQL = GetFunctionAlterOwnerCommand(functionAddress->objectId);\n\n\treturn list_make4(\n\t\tDISABLE_LOCAL_CHECK_FUNCTION_BODIES,\n\t\tddlCommand,\n\t\talterFunctionOwnerSQL,\n\t\tRESET_CHECK_FUNCTION_BODIES);\n}\n\n\n/*\n * GetDistributionArgIndex calculates the distribution argument with the given\n * parameters. The function errors out if no valid argument is found.\n */\nstatic int\nGetDistributionArgIndex(Oid functionOid, char *distributionArgumentName,\n\t\t\t\t\t\tOid *distributionArgumentOid)\n{\n\tint distributionArgumentIndex = -1;\n\n\tOid *argTypes = NULL;\n\tchar **argNames = NULL;\n\tchar *argModes = NULL;\n\n\n\t*distributionArgumentOid = InvalidOid;\n\n\tHeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));\n\tif (!HeapTupleIsValid(proctup))\n\t{\n\t\telog(ERROR, \"cache lookup failed for function %u\", functionOid);\n\t}\n\n\tint numberOfArgs = get_func_arg_info(proctup, &argTypes, &argNames, &argModes);\n\n\tif (argumentStartsWith(distributionArgumentName, \"$\"))\n\t{\n\t\t/* skip the first character, we're safe because text_to_cstring pallocs */\n\t\tdistributionArgumentName++;\n\n\t\t/* throws error if the input is not an integer */\n\t\tdistributionArgumentIndex = pg_strtoint32(distributionArgumentName);\n\n\t\tif (distributionArgumentIndex < 1 || distributionArgumentIndex > numberOfArgs)\n\t\t{\n\t\t\tchar *functionName = get_func_name(functionOid);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t\terrmsg(\"cannot distribute the function \\\"%s\\\" since \"\n\t\t\t\t\t\t\t\t   \"the distribution argument is not valid\",\n\t\t\t\t\t\t\t\t   functionName),\n\t\t\t\t\t\t\terrhint(\"Either provide a valid function argument name \"\n\t\t\t\t\t\t\t\t\t\"or a valid \\\"$paramIndex\\\" to \"\n\t\t\t\t\t\t\t\t\t\"create_distributed_function()\")));\n\t\t}\n\n\t\t/*\n\t\t * Internal representation for the distributionArgumentIndex\n\t\t * starts from 0 whereas user facing API starts from 1.\n\t\t */\n\t\tdistributionArgumentIndex -= 1;\n\t\t*distributionArgumentOid = argTypes[distributionArgumentIndex];\n\n\t\tReleaseSysCache(proctup);\n\n\t\tAssert(*distributionArgumentOid != InvalidOid);\n\n\t\treturn distributionArgumentIndex;\n\t}\n\n\t/*\n\t * The user didn't provid \"$paramIndex\" but potentially the name of the parameter.\n\t * So, loop over the arguments and try to find the argument name that matches\n\t * the parameter that user provided.\n\t */\n\tfor (int argIndex = 0; argIndex < numberOfArgs; ++argIndex)\n\t{\n\t\tchar *argNameOnIndex = argNames != NULL ? argNames[argIndex] : NULL;\n\n\t\tif (argNameOnIndex != NULL &&\n\t\t\tpg_strncasecmp(argNameOnIndex, distributionArgumentName, NAMEDATALEN) == 0)\n\t\t{\n\t\t\tdistributionArgumentIndex = argIndex;\n\n\t\t\t*distributionArgumentOid = argTypes[argIndex];\n\n\t\t\t/* we found, no need to continue */\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* we still couldn't find the argument, so error out */\n\tif (distributionArgumentIndex == -1)\n\t{\n\t\tchar *functionName = get_func_name(functionOid);\n\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"cannot distribute the function \\\"%s\\\" since the \"\n\t\t\t\t\t\t\t   \"distribution argument is not valid \", functionName),\n\t\t\t\t\t\terrhint(\"Either provide a valid function argument name \"\n\t\t\t\t\t\t\t\t\"or a valid \\\"$paramIndex\\\" to \"\n\t\t\t\t\t\t\t\t\"create_distributed_function()\")));\n\t}\n\n\tReleaseSysCache(proctup);\n\n\tAssert(*distributionArgumentOid != InvalidOid);\n\n\treturn distributionArgumentIndex;\n}\n\n\n/*\n * GetFunctionColocationId gets the parameters for deciding the colocationId\n * of the function that is being distributed. The function errors out if it is\n * not possible to assign a colocationId to the input function.\n */\nstatic int\nGetFunctionColocationId(Oid functionOid, char *colocateWithTableName,\n\t\t\t\t\t\tOid distributionArgumentOid)\n{\n\tint colocationId = INVALID_COLOCATION_ID;\n\tRelation pgDistColocation = table_open(DistColocationRelationId(), ShareLock);\n\n\tif (pg_strncasecmp(colocateWithTableName, \"default\", NAMEDATALEN) == 0)\n\t{\n\t\t/* check for default colocation group */\n\t\tcolocationId = ColocationId(ShardCount, ShardReplicationFactor,\n\t\t\t\t\t\t\t\t\tdistributionArgumentOid, get_typcollation(\n\t\t\t\t\t\t\t\t\t\tdistributionArgumentOid));\n\n\t\tif (colocationId == INVALID_COLOCATION_ID)\n\t\t{\n\t\t\tchar *functionName = get_func_name(functionOid);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t\terrmsg(\"cannot distribute the function \\\"%s\\\" since there \"\n\t\t\t\t\t\t\t\t   \"is no table to colocate with\", functionName),\n\t\t\t\t\t\t\terrhint(\"Provide a distributed table via \\\"colocate_with\\\" \"\n\t\t\t\t\t\t\t\t\t\"option to create_distributed_function()\")));\n\t\t}\n\n\t\tOid colocatedTableId = ColocatedTableId(colocationId);\n\t\tif (colocatedTableId != InvalidOid)\n\t\t{\n\t\t\tEnsureFunctionCanBeColocatedWithTable(functionOid, distributionArgumentOid,\n\t\t\t\t\t\t\t\t\t\t\t\t  colocatedTableId);\n\t\t}\n\t}\n\telse\n\t{\n\t\tOid sourceRelationId =\n\t\t\tResolveRelationId(cstring_to_text(colocateWithTableName), false);\n\n\t\tEnsureFunctionCanBeColocatedWithTable(functionOid, distributionArgumentOid,\n\t\t\t\t\t\t\t\t\t\t\t  sourceRelationId);\n\n\t\tcolocationId = TableColocationId(sourceRelationId);\n\t}\n\n\t/* keep the lock */\n\ttable_close(pgDistColocation, NoLock);\n\n\treturn colocationId;\n}\n\n\n/*\n * EnsureFunctionCanBeColocatedWithTable checks whether the given arguments are\n * suitable to distribute the function to be colocated with given source table.\n */\nstatic void\nEnsureFunctionCanBeColocatedWithTable(Oid functionOid, Oid distributionColumnType,\n\t\t\t\t\t\t\t\t\t  Oid sourceRelationId)\n{\n\tCitusTableCacheEntry *sourceTableEntry = GetCitusTableCacheEntry(sourceRelationId);\n\tchar sourceReplicationModel = sourceTableEntry->replicationModel;\n\n\tif (IsCitusTableTypeCacheEntry(sourceTableEntry, SINGLE_SHARD_DISTRIBUTED) &&\n\t\tdistributionColumnType != InvalidOid)\n\t{\n\t\tchar *functionName = get_func_name(functionOid);\n\t\tchar *sourceRelationName = get_rel_name(sourceRelationId);\n\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot colocate function \\\"%s\\\" and table \\\"%s\\\" because \"\n\t\t\t\t\t\t\t   \"distribution arguments are not supported when \"\n\t\t\t\t\t\t\t   \"colocating with single shard distributed tables.\",\n\t\t\t\t\t\t\t   functionName, sourceRelationName)));\n\t}\n\n\tif (!IsCitusTableTypeCacheEntry(sourceTableEntry, HASH_DISTRIBUTED) &&\n\t\t!IsCitusTableTypeCacheEntry(sourceTableEntry, REFERENCE_TABLE))\n\t{\n\t\tchar *functionName = get_func_name(functionOid);\n\t\tchar *sourceRelationName = get_rel_name(sourceRelationId);\n\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot colocate function \\\"%s\\\" and table \\\"%s\\\" because \"\n\t\t\t\t\t\t\t   \"colocate_with option is only supported for hash \"\n\t\t\t\t\t\t\t   \"distributed tables and reference tables.\",\n\t\t\t\t\t\t\t   functionName, sourceRelationName)));\n\t}\n\n\tif (IsCitusTableTypeCacheEntry(sourceTableEntry, REFERENCE_TABLE) &&\n\t\tdistributionColumnType != InvalidOid)\n\t{\n\t\tchar *functionName = get_func_name(functionOid);\n\t\tchar *sourceRelationName = get_rel_name(sourceRelationId);\n\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot colocate function \\\"%s\\\" and table \\\"%s\\\" because \"\n\t\t\t\t\t\t\t   \"distribution arguments are not supported when \"\n\t\t\t\t\t\t\t   \"colocating with reference tables.\",\n\t\t\t\t\t\t\t   functionName, sourceRelationName)));\n\t}\n\n\tif (sourceReplicationModel != REPLICATION_MODEL_STREAMING)\n\t{\n\t\tchar *functionName = get_func_name(functionOid);\n\t\tchar *sourceRelationName = get_rel_name(sourceRelationId);\n\n\t\tereport(ERROR, (errmsg(\"cannot colocate function \\\"%s\\\" and table \\\"%s\\\"\",\n\t\t\t\t\t\t\t   functionName, sourceRelationName),\n\t\t\t\t\t\terrdetail(\"Citus currently only supports colocating function \"\n\t\t\t\t\t\t\t\t  \"with distributed tables that are created using \"\n\t\t\t\t\t\t\t\t  \"streaming replication model.\"),\n\t\t\t\t\t\terrhint(\"When distributing tables make sure that \"\n\t\t\t\t\t\t\t\t\"citus.shard_replication_factor = 1\")));\n\t}\n\n\t/*\n\t * If the types are the same, we're good. If not, we still check if there\n\t * is any coercion path between the types.\n\t */\n\tVar *sourceDistributionColumn = DistPartitionKeyOrError(sourceRelationId);\n\tOid sourceDistributionColumnType = sourceDistributionColumn->vartype;\n\tif (sourceDistributionColumnType != distributionColumnType)\n\t{\n\t\tOid coercionFuncId = InvalidOid;\n\n\t\tCoercionPathType coercionType =\n\t\t\tfind_coercion_pathway(distributionColumnType, sourceDistributionColumnType,\n\t\t\t\t\t\t\t\t  COERCION_EXPLICIT, &coercionFuncId);\n\n\t\t/* if there is no path for coercion, error out*/\n\t\tif (coercionType == COERCION_PATH_NONE)\n\t\t{\n\t\t\tchar *functionName = get_func_name(functionOid);\n\t\t\tchar *sourceRelationName = get_rel_name(sourceRelationId);\n\n\t\t\tereport(ERROR, (errmsg(\"cannot colocate function \\\"%s\\\" and table \\\"%s\\\" \"\n\t\t\t\t\t\t\t\t   \"because distribution column types don't match and \"\n\t\t\t\t\t\t\t\t   \"there is no coercion path\", sourceRelationName,\n\t\t\t\t\t\t\t\t   functionName)));\n\t\t}\n\t}\n}\n\n\n/*\n * UpdateFunctionDistributionInfo gets object address of a function and\n * updates its distribution_argument_index and colocationId in pg_dist_object.\n * Then update pg_dist_object on nodes with metadata if object propagation is on.\n */\nvoid\nUpdateFunctionDistributionInfo(const ObjectAddress *distAddress,\n\t\t\t\t\t\t\t   int *distribution_argument_index,\n\t\t\t\t\t\t\t   int *colocationId,\n\t\t\t\t\t\t\t   bool *forceDelegation)\n{\n\tconst bool indexOK = true;\n\n\tScanKeyData scanKey[3];\n\n\tRelation pgDistObjectRel = table_open(DistObjectRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistObjectRel);\n\n\tDatum *values = palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isnull = palloc0(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tint forseDelegationIndex = GetForceDelegationAttrIndexInPgDistObject(tupleDescriptor);\n\n\t/* scan pg_dist_object for classid = $1 AND objid = $2 AND objsubid = $3 via index */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_object_classid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(distAddress->classId));\n\tScanKeyInit(&scanKey[1], Anum_pg_dist_object_objid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(distAddress->objectId));\n\tScanKeyInit(&scanKey[2], Anum_pg_dist_object_objsubid, BTEqualStrategyNumber,\n\t\t\t\tF_INT4EQ, Int32GetDatum(distAddress->objectSubId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistObjectRel,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistObjectPrimaryKeyIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, 3, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for node \\\"%d,%d,%d\\\" \"\n\t\t\t\t\t\t\t   \"in pg_dist_object\", distAddress->classId,\n\t\t\t\t\t\t\t   distAddress->objectId, distAddress->objectSubId)));\n\t}\n\n\treplace[Anum_pg_dist_object_distribution_argument_index - 1] = true;\n\tif (distribution_argument_index != NULL)\n\t{\n\t\tvalues[Anum_pg_dist_object_distribution_argument_index - 1] = Int32GetDatum(\n\t\t\t*distribution_argument_index);\n\t\tisnull[Anum_pg_dist_object_distribution_argument_index - 1] = false;\n\t}\n\telse\n\t{\n\t\tisnull[Anum_pg_dist_object_distribution_argument_index - 1] = true;\n\t}\n\n\treplace[Anum_pg_dist_object_colocationid - 1] = true;\n\tif (colocationId != NULL)\n\t{\n\t\tvalues[Anum_pg_dist_object_colocationid - 1] = Int32GetDatum(*colocationId);\n\t\tisnull[Anum_pg_dist_object_colocationid - 1] = false;\n\t}\n\telse\n\t{\n\t\tisnull[Anum_pg_dist_object_colocationid - 1] = true;\n\t}\n\n\treplace[forseDelegationIndex] = true;\n\tif (forceDelegation != NULL)\n\t{\n\t\tvalues[forseDelegationIndex] = BoolGetDatum(*forceDelegation);\n\t\tisnull[forseDelegationIndex] = false;\n\t}\n\telse\n\t{\n\t\tisnull[forseDelegationIndex] = true;\n\t}\n\n\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull, replace);\n\n\tCatalogTupleUpdate(pgDistObjectRel, &heapTuple->t_self, heapTuple);\n\n\tCitusInvalidateRelcacheByRelid(DistObjectRelationId());\n\n\tCommandCounterIncrement();\n\n\tsystable_endscan(scanDescriptor);\n\n\ttable_close(pgDistObjectRel, NoLock);\n\n\tpfree(values);\n\tpfree(isnull);\n\tpfree(replace);\n\n\tif (EnableMetadataSync)\n\t{\n\t\tList *objectAddressList = list_make1((ObjectAddress *) distAddress);\n\t\tList *distArgumentIndexList = NIL;\n\t\tList *colocationIdList = NIL;\n\t\tList *forceDelegationList = NIL;\n\n\t\tif (distribution_argument_index == NULL)\n\t\t{\n\t\t\tdistArgumentIndexList = list_make1_int(INVALID_DISTRIBUTION_ARGUMENT_INDEX);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdistArgumentIndexList = list_make1_int(*distribution_argument_index);\n\t\t}\n\n\t\tif (colocationId == NULL)\n\t\t{\n\t\t\tcolocationIdList = list_make1_int(INVALID_COLOCATION_ID);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcolocationIdList = list_make1_int(*colocationId);\n\t\t}\n\n\t\tif (forceDelegation == NULL)\n\t\t{\n\t\t\tforceDelegationList = list_make1_int(NO_FORCE_PUSHDOWN);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforceDelegationList = list_make1_int(*forceDelegation);\n\t\t}\n\n\t\tchar *workerPgDistObjectUpdateCommand =\n\t\t\tMarkObjectsDistributedCreateCommand(objectAddressList,\n\t\t\t\t\t\t\t\t\t\t\t\tNIL,\n\t\t\t\t\t\t\t\t\t\t\t\tdistArgumentIndexList,\n\t\t\t\t\t\t\t\t\t\t\t\tcolocationIdList,\n\t\t\t\t\t\t\t\t\t\t\t\tforceDelegationList);\n\t\tSendCommandToWorkersWithMetadata(workerPgDistObjectUpdateCommand);\n\t}\n}\n\n\n/*\n * GetFunctionDDLCommand returns the complete \"CREATE OR REPLACE FUNCTION ...\" statement for\n * the specified function.\n *\n * useCreateOrReplace is ignored for non-aggregate functions.\n */\nchar *\nGetFunctionDDLCommand(const RegProcedure funcOid, bool useCreateOrReplace)\n{\n\tchar *createFunctionSQL = NULL;\n\n\tif (get_func_prokind(funcOid) == PROKIND_AGGREGATE)\n\t{\n\t\tcreateFunctionSQL = GetAggregateDDLCommand(funcOid, useCreateOrReplace);\n\t}\n\telse\n\t{\n\t\tDatum sqlTextDatum = (Datum) 0;\n\t\tint saveNestLevel = PushEmptySearchPath();\n\n\t\tsqlTextDatum = DirectFunctionCall1(pg_get_functiondef,\n\t\t\t\t\t\t\t\t\t\t   ObjectIdGetDatum(funcOid));\n\t\tcreateFunctionSQL = TextDatumGetCString(sqlTextDatum);\n\n\t\t/* revert back to original search_path */\n\t\tPopEmptySearchPath(saveNestLevel);\n\t}\n\n\treturn createFunctionSQL;\n}\n\n\n/*\n * GetFunctionAlterOwnerCommand returns \"ALTER FUNCTION .. SET OWNER ..\" statement for\n * the specified function.\n */\nstatic char *\nGetFunctionAlterOwnerCommand(const RegProcedure funcOid)\n{\n\tHeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));\n\tStringInfo alterCommand = makeStringInfo();\n\tOid procOwner = InvalidOid;\n\n\n\tif (HeapTupleIsValid(proctup))\n\t{\n\t\tForm_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);\n\n\t\tprocOwner = procform->proowner;\n\n\t\tReleaseSysCache(proctup);\n\t}\n\telse if (!OidIsValid(funcOid) || !HeapTupleIsValid(proctup))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot find function with oid: %d\", funcOid)));\n\t}\n\n\t/*\n\t * If the function exists we want to use format_procedure_qualified to\n\t * serialize its canonical arguments\n\t */\n\tchar *functionSignature = format_procedure_qualified(funcOid);\n\tchar *functionOwner = GetUserNameFromId(procOwner, false);\n\n\tappendStringInfo(alterCommand, \"ALTER ROUTINE %s OWNER TO %s;\",\n\t\t\t\t\t functionSignature,\n\t\t\t\t\t quote_identifier(functionOwner));\n\n\treturn alterCommand->data;\n}\n\n\n/*\n * GetAggregateDDLCommand returns a string for creating an aggregate.\n * A second parameter useCreateOrReplace signals whether to\n * to create a plain CREATE AGGREGATE or not.\n */\nstatic char *\nGetAggregateDDLCommand(const RegProcedure funcOid, bool useCreateOrReplace)\n{\n\tStringInfoData buf = { 0 };\n\tint i = 0;\n\tOid *argtypes = NULL;\n\tchar **argnames = NULL;\n\tchar *argmodes = NULL;\n\tint insertorderbyat = -1;\n\tint argsprinted = 0;\n\n\tHeapTuple proctup = SearchSysCache1(PROCOID, funcOid);\n\tif (!HeapTupleIsValid(proctup))\n\t{\n\t\telog(ERROR, \"cache lookup failed for %d\", funcOid);\n\t}\n\n\tForm_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);\n\n\tAssert(proc->prokind == PROKIND_AGGREGATE);\n\n\tinitStringInfo(&buf);\n\n\tconst char *name = NameStr(proc->proname);\n\tconst char *nsp = get_namespace_name(proc->pronamespace);\n\n\tif (useCreateOrReplace)\n\t{\n\t\tappendStringInfo(&buf, \"CREATE OR REPLACE AGGREGATE %s(\",\n\t\t\t\t\t\t quote_qualified_identifier(nsp, name));\n\t}\n\telse\n\t{\n\t\tappendStringInfo(&buf, \"CREATE AGGREGATE %s(\",\n\t\t\t\t\t\t quote_qualified_identifier(nsp, name));\n\t}\n\n\t/* Parameters, borrows heavily from print_function_arguments in postgres */\n\tint numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);\n\n\tHeapTuple aggtup = SearchSysCache1(AGGFNOID, funcOid);\n\tif (!HeapTupleIsValid(aggtup))\n\t{\n\t\telog(ERROR, \"cache lookup failed for %d\", funcOid);\n\t}\n\tForm_pg_aggregate agg = (Form_pg_aggregate) GETSTRUCT(aggtup);\n\n\tif (AGGKIND_IS_ORDERED_SET(agg->aggkind))\n\t{\n\t\tinsertorderbyat = agg->aggnumdirectargs;\n\t}\n\n\t/*\n\t * For zero-argument aggregate, write * in place of the list of arguments\n\t */\n\tif (numargs == 0)\n\t{\n\t\tappendStringInfo(&buf, \"*\");\n\t}\n\n\tfor (i = 0; i < numargs; i++)\n\t{\n\t\tOid argtype = argtypes[i];\n\t\tchar *argname = argnames ? argnames[i] : NULL;\n\t\tchar argmode = argmodes ? argmodes[i] : PROARGMODE_IN;\n\t\tconst char *modename;\n\n\t\tswitch (argmode)\n\t\t{\n\t\t\tcase PROARGMODE_IN:\n\t\t\t{\n\t\t\t\tmodename = \"\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROARGMODE_VARIADIC:\n\t\t\t{\n\t\t\t\tmodename = \"VARIADIC \";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\telog(ERROR, \"unexpected parameter mode '%c'\", argmode);\n\t\t\t\tmodename = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (argsprinted == insertorderbyat)\n\t\t{\n\t\t\tappendStringInfoString(&buf, \" ORDER BY \");\n\t\t}\n\t\telse if (argsprinted)\n\t\t{\n\t\t\tappendStringInfoString(&buf, \", \");\n\t\t}\n\n\t\tappendStringInfoString(&buf, modename);\n\n\t\tif (argname && argname[0])\n\t\t{\n\t\t\tappendStringInfo(&buf, \"%s \", quote_identifier(argname));\n\t\t}\n\n\t\tappendStringInfoString(&buf, format_type_be_qualified(argtype));\n\n\t\targsprinted++;\n\n\t\t/* nasty hack: print the last arg twice for variadic ordered-set agg */\n\t\tif (argsprinted == insertorderbyat && i == numargs - 1)\n\t\t{\n\t\t\ti--;\n\t\t}\n\t}\n\n\tappendStringInfo(&buf, \") (STYPE = %s,SFUNC = %s\",\n\t\t\t\t\t format_type_be_qualified(agg->aggtranstype),\n\t\t\t\t\t quote_qualified_func_name(agg->aggtransfn));\n\n\tif (agg->aggtransspace != 0)\n\t{\n\t\tappendStringInfo(&buf, \", SSPACE = %d\", agg->aggtransspace);\n\t}\n\n\tif (agg->aggfinalfn != InvalidOid)\n\t{\n\t\tconst char *finalmodifystring = NULL;\n\t\tswitch (agg->aggfinalmodify)\n\t\t{\n\t\t\tcase AGGMODIFY_READ_ONLY:\n\t\t\t{\n\t\t\t\tfinalmodifystring = \"READ_ONLY\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AGGMODIFY_SHAREABLE:\n\t\t\t{\n\t\t\t\tfinalmodifystring = \"SHAREABLE\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AGGMODIFY_READ_WRITE:\n\t\t\t{\n\t\t\t\tfinalmodifystring = \"READ_WRITE\";\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tappendStringInfo(&buf, \", FINALFUNC = %s\",\n\t\t\t\t\t\t quote_qualified_func_name(agg->aggfinalfn));\n\n\t\tif (finalmodifystring != NULL)\n\t\t{\n\t\t\tappendStringInfo(&buf, \", FINALFUNC_MODIFY = %s\", finalmodifystring);\n\t\t}\n\n\t\tif (agg->aggfinalextra)\n\t\t{\n\t\t\tappendStringInfoString(&buf, \", FINALFUNC_EXTRA\");\n\t\t}\n\t}\n\n\tif (agg->aggmtransspace != 0)\n\t{\n\t\tappendStringInfo(&buf, \", MSSPACE = %d\", agg->aggmtransspace);\n\t}\n\n\tif (agg->aggmfinalfn)\n\t{\n\t\tconst char *mfinalmodifystring = NULL;\n\t\tswitch (agg->aggfinalmodify)\n\t\t{\n\t\t\tcase AGGMODIFY_READ_ONLY:\n\t\t\t{\n\t\t\t\tmfinalmodifystring = \"READ_ONLY\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AGGMODIFY_SHAREABLE:\n\t\t\t{\n\t\t\t\tmfinalmodifystring = \"SHAREABLE\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AGGMODIFY_READ_WRITE:\n\t\t\t{\n\t\t\t\tmfinalmodifystring = \"READ_WRITE\";\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tappendStringInfo(&buf, \", MFINALFUNC = %s\",\n\t\t\t\t\t\t quote_qualified_func_name(agg->aggmfinalfn));\n\n\t\tif (mfinalmodifystring != NULL)\n\t\t{\n\t\t\tappendStringInfo(&buf, \", MFINALFUNC_MODIFY = %s\", mfinalmodifystring);\n\t\t}\n\n\t\tif (agg->aggmfinalextra)\n\t\t{\n\t\t\tappendStringInfoString(&buf, \", MFINALFUNC_EXTRA\");\n\t\t}\n\t}\n\n\tif (agg->aggmtransfn)\n\t{\n\t\tappendStringInfo(&buf, \", MSFUNC = %s\",\n\t\t\t\t\t\t quote_qualified_func_name(agg->aggmtransfn));\n\n\t\tif (agg->aggmtranstype)\n\t\t{\n\t\t\tappendStringInfo(&buf, \", MSTYPE = %s\",\n\t\t\t\t\t\t\t format_type_be_qualified(agg->aggmtranstype));\n\t\t}\n\t}\n\n\tif (agg->aggtransspace != 0)\n\t{\n\t\tappendStringInfo(&buf, \", SSPACE = %d\", agg->aggtransspace);\n\t}\n\n\tif (agg->aggminvtransfn)\n\t{\n\t\tappendStringInfo(&buf, \", MINVFUNC = %s\",\n\t\t\t\t\t\t quote_qualified_func_name(agg->aggminvtransfn));\n\t}\n\n\tif (agg->aggcombinefn)\n\t{\n\t\tappendStringInfo(&buf, \", COMBINEFUNC = %s\",\n\t\t\t\t\t\t quote_qualified_func_name(agg->aggcombinefn));\n\t}\n\n\tif (agg->aggserialfn)\n\t{\n\t\tappendStringInfo(&buf, \", SERIALFUNC = %s\",\n\t\t\t\t\t\t quote_qualified_func_name(agg->aggserialfn));\n\t}\n\n\tif (agg->aggdeserialfn)\n\t{\n\t\tappendStringInfo(&buf, \", DESERIALFUNC = %s\",\n\t\t\t\t\t\t quote_qualified_func_name(agg->aggdeserialfn));\n\t}\n\n\tif (agg->aggsortop != InvalidOid)\n\t{\n\t\tappendStringInfo(&buf, \", SORTOP = %s\",\n\t\t\t\t\t\t generate_operator_name(agg->aggsortop, argtypes[0],\n\t\t\t\t\t\t\t\t\t\t\t\targtypes[0]));\n\t}\n\n\t{\n\t\tconst char *parallelstring = NULL;\n\t\tswitch (proc->proparallel)\n\t\t{\n\t\t\tcase PROPARALLEL_SAFE:\n\t\t\t{\n\t\t\t\tparallelstring = \"SAFE\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROPARALLEL_RESTRICTED:\n\t\t\t{\n\t\t\t\tparallelstring = \"RESTRICTED\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROPARALLEL_UNSAFE:\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\telog(WARNING, \"Unknown parallel option, ignoring: %c\", proc->proparallel);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (parallelstring != NULL)\n\t\t{\n\t\t\tappendStringInfo(&buf, \", PARALLEL = %s\", parallelstring);\n\t\t}\n\t}\n\n\t{\n\t\tbool isNull = false;\n\t\tDatum textInitVal = SysCacheGetAttr(AGGFNOID, aggtup,\n\t\t\t\t\t\t\t\t\t\t\tAnum_pg_aggregate_agginitval,\n\t\t\t\t\t\t\t\t\t\t\t&isNull);\n\t\tif (!isNull)\n\t\t{\n\t\t\tchar *strInitVal = TextDatumGetCString(textInitVal);\n\t\t\tchar *strInitValQuoted = quote_literal_cstr(strInitVal);\n\n\t\t\tappendStringInfo(&buf, \", INITCOND = %s\", strInitValQuoted);\n\n\t\t\tpfree(strInitValQuoted);\n\t\t\tpfree(strInitVal);\n\t\t}\n\t}\n\n\t{\n\t\tbool isNull = false;\n\t\tDatum textInitVal = SysCacheGetAttr(AGGFNOID, aggtup,\n\t\t\t\t\t\t\t\t\t\t\tAnum_pg_aggregate_aggminitval,\n\t\t\t\t\t\t\t\t\t\t\t&isNull);\n\t\tif (!isNull)\n\t\t{\n\t\t\tchar *strInitVal = TextDatumGetCString(textInitVal);\n\t\t\tchar *strInitValQuoted = quote_literal_cstr(strInitVal);\n\n\t\t\tappendStringInfo(&buf, \", MINITCOND = %s\", strInitValQuoted);\n\n\t\t\tpfree(strInitValQuoted);\n\t\t\tpfree(strInitVal);\n\t\t}\n\t}\n\n\tif (agg->aggkind == AGGKIND_HYPOTHETICAL)\n\t{\n\t\tappendStringInfoString(&buf, \", HYPOTHETICAL\");\n\t}\n\n\tappendStringInfoChar(&buf, ')');\n\n\tReleaseSysCache(aggtup);\n\tReleaseSysCache(proctup);\n\n\treturn buf.data;\n}\n\n\n/*\n * ShouldPropagateCreateFunction tests if we need to propagate a CREATE FUNCTION\n * statement.\n */\nstatic bool\nShouldPropagateCreateFunction(CreateFunctionStmt *stmt)\n{\n\tif (!ShouldPropagate())\n\t{\n\t\treturn false;\n\t}\n\n\tif (!ShouldPropagateCreateInCoordinatedTransction())\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ShouldPropagateAlterFunction returns, based on the address of a function, if alter\n * statements targeting the function should be propagated.\n */\nstatic bool\nShouldPropagateAlterFunction(const ObjectAddress *address)\n{\n\tif (creating_extension)\n\t{\n\t\t/*\n\t\t * extensions should be created separately on the workers, functions cascading\n\t\t * from an extension should therefore not be propagated.\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (!EnableMetadataSync)\n\t{\n\t\t/*\n\t\t * we are configured to disable object propagation, should not propagate anything\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (!IsAnyObjectDistributed(list_make1((ObjectAddress *) address)))\n\t{\n\t\t/* do not propagate alter function for non-distributed functions */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * PreprocessCreateFunctionStmt is called during the planning phase for CREATE [OR REPLACE]\n * FUNCTION before it is created on the local node internally.\n *\n * Since we use pg_get_functiondef to get the ddl command we actually do not do any\n * planning here, instead we defer the plan creation to the postprocessing step.\n *\n * Instead we do our basic housekeeping where we make sure we are on the coordinator and\n * can propagate the function in sequential mode.\n */\nList *\nPreprocessCreateFunctionStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tCreateFunctionStmt *stmt = castNode(CreateFunctionStmt, node);\n\n\tif (!ShouldPropagateCreateFunction(stmt))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tEnsureSequentialMode(OBJECT_FUNCTION);\n\n\t/*\n\t * ddl jobs will be generated during the postprocessing phase as we need the function to\n\t * be updated in the catalog to get its sql representation\n\t */\n\treturn NIL;\n}\n\n\n/*\n * PostprocessCreateFunctionStmt actually creates the plan we need to execute for function\n * propagation. This is the downside of using pg_get_functiondef to get the sql statement.\n *\n * If function depends on any non-distributed relation (except sequence and composite type),\n * Citus can not distribute it. In order to not to prevent users from creating local\n * functions on the coordinator WARNING message will be sent to the customer about the case\n * instead of erroring out.\n *\n * Besides creating the plan we also make sure all (new) dependencies of the function are\n * created on all nodes.\n */\nList *\nPostprocessCreateFunctionStmt(Node *node, const char *queryString)\n{\n\tCreateFunctionStmt *stmt = castNode(CreateFunctionStmt, node);\n\n\tif (!ShouldPropagateCreateFunction(stmt))\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *functionAddresses = GetObjectAddressListFromParseTree((Node *) stmt, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttrue);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(functionAddresses) == 1);\n\n\tif (IsAnyObjectAddressOwnedByExtension(functionAddresses, NULL))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* If the function has any unsupported dependency, create it locally */\n\tDeferredErrorMessage *errMsg = DeferErrorIfAnyObjectHasUnsupportedDependency(\n\t\tfunctionAddresses);\n\n\tif (errMsg != NULL)\n\t{\n\t\tif (EnableUnsupportedFeatureMessages)\n\t\t{\n\t\t\tRaiseDeferredError(errMsg, WARNING);\n\t\t}\n\n\t\treturn NIL;\n\t}\n\n\tEnsureAllObjectDependenciesExistOnAllNodes(functionAddresses);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *functionAddress = linitial(functionAddresses);\n\n\tList *commands = list_make1(DISABLE_DDL_PROPAGATION);\n\tcommands = list_concat(commands, CreateFunctionDDLCommandsIdempotent(\n\t\t\t\t\t\t\t   functionAddress));\n\tcommands = list_concat(commands, list_make1(ENABLE_DDL_PROPAGATION));\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * CreateFunctionStmtObjectAddress returns the ObjectAddress for the subject of the\n * CREATE [OR REPLACE] FUNCTION statement. If missing_ok is false it will error with the\n * normal postgres error for unfound functions.\n */\nList *\nCreateFunctionStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tCreateFunctionStmt *stmt = castNode(CreateFunctionStmt, node);\n\tObjectType objectType = OBJECT_FUNCTION;\n\n\tif (stmt->is_procedure)\n\t{\n\t\tobjectType = OBJECT_PROCEDURE;\n\t}\n\n\tObjectWithArgs *objectWithArgs = makeNode(ObjectWithArgs);\n\tobjectWithArgs->objname = stmt->funcname;\n\n\tFunctionParameter *funcParam = NULL;\n\tforeach_declared_ptr(funcParam, stmt->parameters)\n\t{\n\t\tif (ShouldAddFunctionSignature(funcParam->mode))\n\t\t{\n\t\t\tobjectWithArgs->objargs = lappend(objectWithArgs->objargs,\n\t\t\t\t\t\t\t\t\t\t\t  funcParam->argType);\n\t\t}\n\t}\n\n\tint OldClientMinMessage = client_min_messages;\n\n\t/* suppress NOTICE if running under pg vanilla tests */\n\tSetLocalClientMinMessagesIfRunningPGTests(WARNING);\n\n\tList *funcAddresses = FunctionToObjectAddress(objectType, objectWithArgs, missing_ok);\n\n\t/* set it back */\n\tSetLocalClientMinMessagesIfRunningPGTests(OldClientMinMessage);\n\n\treturn funcAddresses;\n}\n\n\n/*\n * DefineAggregateStmtObjectAddress finds the ObjectAddress for the composite type described\n * by the DefineStmtObjectAddress. If missing_ok is false this function throws an error if the\n * aggregate does not exist.\n *\n * objectId in the address can be invalid if missing_ok was set to true.\n */\nList *\nDefineAggregateStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tDefineStmt *stmt = castNode(DefineStmt, node);\n\n\tAssert(stmt->kind == OBJECT_AGGREGATE);\n\n\tObjectWithArgs *objectWithArgs = makeNode(ObjectWithArgs);\n\tobjectWithArgs->objname = stmt->defnames;\n\n\tif (stmt->args != NIL)\n\t{\n\t\tFunctionParameter *funcParam = NULL;\n\t\tforeach_declared_ptr(funcParam, linitial(stmt->args))\n\t\t{\n\t\t\tobjectWithArgs->objargs = lappend(objectWithArgs->objargs,\n\t\t\t\t\t\t\t\t\t\t\t  funcParam->argType);\n\t\t}\n\t}\n\telse\n\t{\n\t\tDefElem *defItem = NULL;\n\t\tforeach_declared_ptr(defItem, stmt->definition)\n\t\t{\n\t\t\t/*\n\t\t\t * If no explicit args are given, pg includes basetype in the signature.\n\t\t\t * If the basetype given is a type, like int4, we should include it in the\n\t\t\t * signature. In that case, defItem->arg would be a TypeName.\n\t\t\t * If the basetype given is a string, like \"ANY\", we shouldn't include it.\n\t\t\t */\n\t\t\tif (strcmp(defItem->defname, \"basetype\") == 0 && IsA(defItem->arg, TypeName))\n\t\t\t{\n\t\t\t\tobjectWithArgs->objargs = lappend(objectWithArgs->objargs,\n\t\t\t\t\t\t\t\t\t\t\t\t  defItem->arg);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn FunctionToObjectAddress(OBJECT_AGGREGATE, objectWithArgs, missing_ok);\n}\n\n\n/*\n * PreprocessAlterFunctionStmt is invoked for alter function statements with actions. Here we\n * plan the jobs to be executed on the workers for functions that have been distributed in\n * the cluster.\n */\nList *\nPreprocessAlterFunctionStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tAlterFunctionStmt *stmt = castNode(AlterFunctionStmt, node);\n\tAssertObjectTypeIsFunctional(stmt->objtype);\n\n\tList *addresses = GetObjectAddressListFromParseTree((Node *) stmt, false, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *address = linitial(addresses);\n\n\tif (!ShouldPropagateAlterFunction(address))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tErrorIfUnsupportedAlterFunctionStmt(stmt);\n\tEnsureSequentialMode(OBJECT_FUNCTION);\n\tQualifyTreeNode((Node *) stmt);\n\tconst char *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * PreprocessAlterFunctionDependsStmt is called during the planning phase of an\n * ALTER FUNCION ... DEPENDS ON EXTENSION ... statement. Since functions depending on\n * extensions are assumed to be Owned by an extension we assume the extension to keep the\n * function in sync.\n *\n * If we would allow users to create a dependency between a distributed function and an\n * extension our pruning logic for which objects to distribute as dependencies of other\n * objects will change significantly which could cause issues adding new workers. Hence we\n * don't allow this dependency to be created.\n */\nList *\nPreprocessAlterFunctionDependsStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tAlterObjectDependsStmt *stmt = castNode(AlterObjectDependsStmt, node);\n\tAssertObjectTypeIsFunctional(stmt->objectType);\n\n\tif (creating_extension)\n\t{\n\t\t/*\n\t\t * extensions should be created separately on the workers, types cascading from an\n\t\t * extension should therefore not be propagated here.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tif (!EnableMetadataSync)\n\t{\n\t\t/*\n\t\t * we are configured to disable object propagation, should not propagate anything\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tList *addresses = GetObjectAddressListFromParseTree((Node *) stmt, true, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\tif (!IsAnyObjectDistributed(addresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *address = linitial(addresses);\n\n\t/*\n\t * Distributed objects should not start depending on an extension, this will break\n\t * the dependency resolving mechanism we use to replicate distributed objects to new\n\t * workers\n\t */\n\tconst char *functionName =\n\t\tgetObjectIdentity(address, /* missingOk: */ false);\n\tereport(ERROR, (errmsg(\"distrtibuted functions are not allowed to depend on an \"\n\t\t\t\t\t\t   \"extension\"),\n\t\t\t\t\terrdetail(\"Function \\\"%s\\\" is already distributed. Functions from \"\n\t\t\t\t\t\t\t  \"extensions are expected to be created on the workers by \"\n\t\t\t\t\t\t\t  \"the extension they depend on.\", functionName)));\n}\n\n\n/*\n * AlterFunctionDependsStmtObjectAddress resolves the ObjectAddress of the function that\n * is the subject of an ALTER FUNCTION ... DEPENS ON EXTENSION ... statement. If\n * missing_ok is set to false the lookup will raise an error.\n */\nList *\nAlterFunctionDependsStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterObjectDependsStmt *stmt = castNode(AlterObjectDependsStmt, node);\n\tAssertObjectTypeIsFunctional(stmt->objectType);\n\n\treturn FunctionToObjectAddress(stmt->objectType,\n\t\t\t\t\t\t\t\t   castNode(ObjectWithArgs, stmt->object), missing_ok);\n}\n\n\n/*\n * AlterFunctionStmtObjectAddress returns the ObjectAddress of the subject in the\n * AlterFunctionStmt. If missing_ok is set to false an error will be raised if postgres\n * was unable to find the function/procedure that was the target of the statement.\n */\nList *\nAlterFunctionStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterFunctionStmt *stmt = castNode(AlterFunctionStmt, node);\n\treturn FunctionToObjectAddress(stmt->objtype, stmt->func, missing_ok);\n}\n\n\n/*\n * RenameFunctionStmtObjectAddress returns the ObjectAddress of the function that is the\n * subject of the RenameStmt. Errors if missing_ok is false.\n */\nList *\nRenameFunctionStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\treturn FunctionToObjectAddress(stmt->renameType,\n\t\t\t\t\t\t\t\t   castNode(ObjectWithArgs, stmt->object), missing_ok);\n}\n\n\n/*\n * AlterFunctionOwnerObjectAddress returns the ObjectAddress of the function that is the\n * subject of the AlterOwnerStmt. Errors if missing_ok is false.\n */\nList *\nAlterFunctionOwnerObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\treturn FunctionToObjectAddress(stmt->objectType,\n\t\t\t\t\t\t\t\t   castNode(ObjectWithArgs, stmt->object), missing_ok);\n}\n\n\n/*\n * AlterFunctionSchemaStmtObjectAddress returns the ObjectAddress of the function that is\n * the subject of the AlterObjectSchemaStmt. Errors if missing_ok is false.\n *\n * This could be called both before or after it has been applied locally. It will look in\n * the old schema first, if the function cannot be found in that schema it will look in\n * the new schema. Errors if missing_ok is false and the type cannot be found in either of\n * the schemas.\n */\nList *\nAlterFunctionSchemaStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssertObjectTypeIsFunctional(stmt->objectType);\n\n\tObjectWithArgs *objectWithArgs = castNode(ObjectWithArgs, stmt->object);\n\tOid funcOid = LookupFuncWithArgs(stmt->objectType, objectWithArgs, true);\n\tList *names = objectWithArgs->objname;\n\n\tif (funcOid == InvalidOid)\n\t{\n\t\t/*\n\t\t * couldn't find the function, might have already been moved to the new schema, we\n\t\t * construct a new objname that uses the new schema to search in.\n\t\t */\n\n\t\t/* the name of the function is the last in the list of names */\n\t\tString *funcNameStr = lfirst(list_tail(names));\n\t\tList *newNames = list_make2(makeString(stmt->newschema), funcNameStr);\n\n\t\t/*\n\t\t * we don't error here either, as the error would be not a good user facing\n\t\t * error if the type didn't exist in the first place.\n\t\t */\n\t\tobjectWithArgs->objname = newNames;\n\t\tfuncOid = LookupFuncWithArgs(stmt->objectType, objectWithArgs, true);\n\t\tobjectWithArgs->objname = names; /* restore the original names */\n\n\t\t/*\n\t\t * if the function is still invalid we couldn't find the function, cause postgres\n\t\t * to error by preforming a lookup once more. Since we know the\n\t\t */\n\t\tif (!missing_ok && funcOid == InvalidOid)\n\t\t{\n\t\t\t/*\n\t\t\t * this will most probably throw an error, unless for some reason the function\n\t\t\t * has just been created (if possible at all). For safety we assign the\n\t\t\t * funcOid.\n\t\t\t */\n\t\t\tfuncOid = LookupFuncWithArgs(stmt->objectType, objectWithArgs,\n\t\t\t\t\t\t\t\t\t\t missing_ok);\n\t\t}\n\t}\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, ProcedureRelationId, funcOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * GenerateBackupNameForProcCollision generates a new proc name for an existing proc. The\n * name is generated in such a way that the new name doesn't overlap with an existing proc\n * by adding a suffix with incrementing number after the new name.\n */\nchar *\nGenerateBackupNameForProcCollision(const ObjectAddress *address)\n{\n\tchar *newName = palloc0(NAMEDATALEN);\n\tchar suffix[NAMEDATALEN] = { 0 };\n\tint count = 0;\n\tString *namespace = makeString(get_namespace_name(get_func_namespace(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  address->objectId)));\n\tchar *baseName = get_func_name(address->objectId);\n\tint baseLength = strlen(baseName);\n\tOid *argtypes = NULL;\n\tchar **argnames = NULL;\n\tchar *argmodes = NULL;\n\tHeapTuple proctup = SearchSysCache1(PROCOID, address->objectId);\n\n\tif (!HeapTupleIsValid(proctup))\n\t{\n\t\telog(ERROR, \"citus cache lookup failed.\");\n\t}\n\n\tint numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);\n\tReleaseSysCache(proctup);\n\n\twhile (true)\n\t{\n\t\tint suffixLength = SafeSnprintf(suffix, NAMEDATALEN - 1, \"(citus_backup_%d)\",\n\t\t\t\t\t\t\t\t\t\tcount);\n\n\t\t/* trim the base name at the end to leave space for the suffix and trailing \\0 */\n\t\tbaseLength = Min(baseLength, NAMEDATALEN - suffixLength - 1);\n\n\t\t/* clear newName before copying the potentially trimmed baseName and suffix */\n\t\tmemset(newName, 0, NAMEDATALEN);\n\t\tstrncpy_s(newName, NAMEDATALEN, baseName, baseLength);\n\t\tstrncpy_s(newName + baseLength, NAMEDATALEN - baseLength, suffix,\n\t\t\t\t  suffixLength);\n\n\t\tList *newProcName = list_make2(namespace, makeString(newName));\n\n\t\t/* don't need to rename if the input arguments don't match */\n\t\tFuncCandidateList clist = FuncnameGetCandidates(newProcName, numargs, NIL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse, false, false, true);\n\t\tfor (; clist; clist = clist->next)\n\t\t{\n\t\t\tif (memcmp(clist->args, argtypes, sizeof(Oid) * numargs) == 0)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!clist)\n\t\t{\n\t\t\treturn newName;\n\t\t}\n\n\t\tcount++;\n\t}\n}\n\n\n/*\n * ObjectWithArgsFromOid returns the corresponding ObjectWithArgs node for a given pg_proc oid\n */\nObjectWithArgs *\nObjectWithArgsFromOid(Oid funcOid)\n{\n\tObjectWithArgs *objectWithArgs = makeNode(ObjectWithArgs);\n\tList *objargs = NIL;\n\tOid *argTypes = NULL;\n\tchar **argNames = NULL;\n\tchar *argModes = NULL;\n\tHeapTuple proctup = SearchSysCache1(PROCOID, funcOid);\n\n\tif (!HeapTupleIsValid(proctup))\n\t{\n\t\telog(ERROR, \"citus cache lookup failed.\");\n\t}\n\n\tint numargs = get_func_arg_info(proctup, &argTypes, &argNames, &argModes);\n\n\tobjectWithArgs->objname = list_make2(\n\t\tmakeString(get_namespace_name(get_func_namespace(funcOid))),\n\t\tmakeString(get_func_name(funcOid))\n\t\t);\n\n\tfor (int i = 0; i < numargs; i++)\n\t{\n\t\tif (argModes == NULL || ShouldAddFunctionSignature(argModes[i]))\n\t\t{\n\t\t\tobjargs = lappend(objargs, makeTypeNameFromOid(argTypes[i], -1));\n\t\t}\n\t}\n\tobjectWithArgs->objargs = objargs;\n\n\tReleaseSysCache(proctup);\n\n\treturn objectWithArgs;\n}\n\n\n/*\n * ShouldAddFunctionSignature takes a FunctionParameterMode and returns true if it should\n * be included in the function signature. Returns false otherwise.\n */\nstatic bool\nShouldAddFunctionSignature(FunctionParameterMode mode)\n{\n\t/* only input parameters should be added to the generated signature */\n\tswitch (mode)\n\t{\n\t\tcase FUNC_PARAM_IN:\n\t\tcase FUNC_PARAM_INOUT:\n\t\tcase FUNC_PARAM_VARIADIC:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tcase FUNC_PARAM_OUT:\n\t\tcase FUNC_PARAM_TABLE:\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n}\n\n\n/*\n * FunctionToObjectAddress returns the ObjectAddress of a Function, Procedure or\n * Aggregate based on its type and ObjectWithArgs describing the\n * Function/Procedure/Aggregate. If missing_ok is set to false an error will be\n * raised by postgres explaining the Function/Procedure could not be found.\n */\nstatic List *\nFunctionToObjectAddress(ObjectType objectType, ObjectWithArgs *objectWithArgs,\n\t\t\t\t\t\tbool missing_ok)\n{\n\tAssertObjectTypeIsFunctional(objectType);\n\n\tOid funcOid = LookupFuncWithArgs(objectType, objectWithArgs, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, ProcedureRelationId, funcOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * ErrorIfUnsupportedAlterFunctionStmt raises an error if the AlterFunctionStmt contains a\n * construct that is not supported to be altered on a distributed function. It is assumed\n * the statement passed in is already tested to be targeting a distributed function, and\n * will only execute the checks to error on unsupported constructs.\n *\n * Unsupported Constructs:\n *  - ALTER FUNCTION ... SET ... FROM CURRENT\n */\nstatic void\nErrorIfUnsupportedAlterFunctionStmt(AlterFunctionStmt *stmt)\n{\n\tDefElem *action = NULL;\n\tforeach_declared_ptr(action, stmt->actions)\n\t{\n\t\tif (strcmp(action->defname, \"set\") == 0)\n\t\t{\n\t\t\tVariableSetStmt *setStmt = castNode(VariableSetStmt, action->arg);\n\t\t\tif (setStmt->kind == VAR_SET_CURRENT)\n\t\t\t{\n\t\t\t\t/* check if the set action is a SET ... FROM CURRENT */\n\t\t\t\tereport(ERROR, (errmsg(\"unsupported ALTER FUNCTION ... SET ... FROM \"\n\t\t\t\t\t\t\t\t\t   \"CURRENT for a distributed function\"),\n\t\t\t\t\t\t\t\terrhint(\"SET FROM CURRENT is not supported for \"\n\t\t\t\t\t\t\t\t\t\t\"distributed functions, instead use the SET ... \"\n\t\t\t\t\t\t\t\t\t\t\"TO ... syntax with a constant value.\")));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/* returns the quoted qualified name of a given function oid */\nstatic char *\nquote_qualified_func_name(Oid funcOid)\n{\n\treturn quote_qualified_identifier(\n\t\tget_namespace_name(get_func_namespace(funcOid)),\n\t\tget_func_name(funcOid));\n}\n\n\n/*\n * EnsureExtensionFuncionCanBeCreated checks if the dependent objects\n * (including extension) exists on all nodes, if not, creates them. In\n * addition, it also checks if distribution argument is passed.\n */\nstatic void\nEnsureExtensionFunctionCanBeDistributed(const ObjectAddress functionAddress,\n\t\t\t\t\t\t\t\t\t\tconst ObjectAddress extensionAddress,\n\t\t\t\t\t\t\t\t\t\tchar *distributionArgumentName)\n{\n\tif (CitusExtensionObject(&extensionAddress))\n\t{\n\t\t/*\n\t\t * Citus extension is a special case. It's the extension that\n\t\t * provides the 'distributed capabilities' in the first place.\n\t\t * Trying to distribute its own function(s) doesn't make sense.\n\t\t */\n\t\tereport(ERROR, (errmsg(\"Citus extension functions(%s) \"\n\t\t\t\t\t\t\t   \"cannot be distributed.\",\n\t\t\t\t\t\t\t   get_func_name(functionAddress.objectId))));\n\t}\n\n\t/*\n\t * Distributing functions from extensions has the most benefit when\n\t * distribution argument is specified.\n\t */\n\tif (distributionArgumentName == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"Extension functions(%s) \"\n\t\t\t\t\t\t\t   \"without distribution argument \"\n\t\t\t\t\t\t\t   \"are not supported.\",\n\t\t\t\t\t\t\t   get_func_name(functionAddress.objectId))));\n\t}\n\n\t/*\n\t * Ensure corresponding extension is in pg_dist_object.\n\t * Functions owned by an extension are depending internally on that extension,\n\t * hence EnsureAllObjectDependenciesExistOnAllNodes() creates the extension, which in\n\t * turn creates the function, and thus we don't have to create it ourself like\n\t * we do for non-extension functions.\n\t */\n\tereport(DEBUG1, (errmsg(\"Extension(%s) owning the \"\n\t\t\t\t\t\t\t\"function(%s) is not distributed, \"\n\t\t\t\t\t\t\t\"attempting to propogate the extension\",\n\t\t\t\t\t\t\tget_extension_name(extensionAddress.objectId),\n\t\t\t\t\t\t\tget_func_name(functionAddress.objectId))));\n\n\tObjectAddress *copyFunctionAddress = palloc0(sizeof(ObjectAddress));\n\t*copyFunctionAddress = functionAddress;\n\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(copyFunctionAddress));\n}\n\n\n/*\n * PreprocessGrantOnFunctionStmt is executed before the statement is applied to the local\n * postgres instance.\n *\n * In this stage we can prepare the commands that need to be run on all workers to grant\n * on distributed functions, procedures, routines.\n */\nList *\nPreprocessGrantOnFunctionStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(isFunction(stmt->objtype));\n\n\tList *distributedFunctions = FilterDistributedFunctions(stmt);\n\n\tif (list_length(distributedFunctions) == 0 || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tList *grantFunctionList = NIL;\n\tObjectAddress *functionAddress = NULL;\n\tforeach_declared_ptr(functionAddress, distributedFunctions)\n\t{\n\t\tObjectWithArgs *distFunction = ObjectWithArgsFromOid(\n\t\t\tfunctionAddress->objectId);\n\t\tgrantFunctionList = lappend(grantFunctionList, distFunction);\n\t}\n\n\tList *originalObjects = stmt->objects;\n\tGrantTargetType originalTargtype = stmt->targtype;\n\n\tstmt->objects = grantFunctionList;\n\tstmt->targtype = ACL_TARGET_OBJECT;\n\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\n\tstmt->objects = originalObjects;\n\tstmt->targtype = originalTargtype;\n\n\tList *commandList = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t   (void *) sql,\n\t\t\t\t\t\t\t\t   ENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commandList);\n}\n\n\n/*\n * PostprocessGrantOnFunctionStmt makes sure dependencies of each\n * distributed function in the statement exist on all nodes\n */\nList *\nPostprocessGrantOnFunctionStmt(Node *node, const char *queryString)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\n\tList *distributedFunctions = FilterDistributedFunctions(stmt);\n\n\tif (list_length(distributedFunctions) == 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tObjectAddress *functionAddress = NULL;\n\tforeach_declared_ptr(functionAddress, distributedFunctions)\n\t{\n\t\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(functionAddress));\n\t}\n\treturn NIL;\n}\n\n\n/*\n * FilterDistributedFunctions determines and returns a list of distributed functions\n * ObjectAddress-es from given grant statement.\n */\nstatic List *\nFilterDistributedFunctions(GrantStmt *grantStmt)\n{\n\tList *grantFunctionList = NIL;\n\n\tbool grantOnFunctionCommand = (grantStmt->targtype == ACL_TARGET_OBJECT &&\n\t\t\t\t\t\t\t\t   isFunction(grantStmt->objtype));\n\tbool grantAllFunctionsOnSchemaCommand = (grantStmt->targtype ==\n\t\t\t\t\t\t\t\t\t\t\t ACL_TARGET_ALL_IN_SCHEMA &&\n\t\t\t\t\t\t\t\t\t\t\t isFunction(grantStmt->objtype));\n\n\t/* we are only interested in function/procedure/routine level grants */\n\tif (!grantOnFunctionCommand && !grantAllFunctionsOnSchemaCommand)\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (grantAllFunctionsOnSchemaCommand)\n\t{\n\t\tList *distributedFunctionList = DistributedFunctionList();\n\t\tObjectAddress *distributedFunction = NULL;\n\t\tList *namespaceOidList = NIL;\n\n\t\t/* iterate over all namespace names provided to get their oid's */\n\t\tString *namespaceValue = NULL;\n\t\tforeach_declared_ptr(namespaceValue, grantStmt->objects)\n\t\t{\n\t\t\tchar *nspname = strVal(namespaceValue);\n\t\t\tbool missing_ok = false;\n\t\t\tOid namespaceOid = get_namespace_oid(nspname, missing_ok);\n\t\t\tnamespaceOidList = list_append_unique_oid(namespaceOidList, namespaceOid);\n\t\t}\n\n\t\t/*\n\t\t * iterate over all distributed functions to filter the ones\n\t\t * that belong to one of the namespaces from above\n\t\t */\n\t\tforeach_declared_ptr(distributedFunction, distributedFunctionList)\n\t\t{\n\t\t\tOid namespaceOid = get_func_namespace(distributedFunction->objectId);\n\n\t\t\t/*\n\t\t\t * if this distributed function's schema is one of the schemas\n\t\t\t * specified in the GRANT .. ALL FUNCTIONS IN SCHEMA ..\n\t\t\t * add it to the list\n\t\t\t */\n\t\t\tif (list_member_oid(namespaceOidList, namespaceOid))\n\t\t\t{\n\t\t\t\tgrantFunctionList = lappend(grantFunctionList, distributedFunction);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tbool missingOk = false;\n\t\tObjectWithArgs *objectWithArgs = NULL;\n\t\tforeach_declared_ptr(objectWithArgs, grantStmt->objects)\n\t\t{\n\t\t\tObjectAddress *functionAddress = palloc0(sizeof(ObjectAddress));\n\t\t\tfunctionAddress->classId = ProcedureRelationId;\n\t\t\tfunctionAddress->objectId = LookupFuncWithArgs(grantStmt->objtype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   objectWithArgs,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   missingOk);\n\t\t\tfunctionAddress->objectSubId = 0;\n\n\t\t\t/*\n\t\t\t * if this function from GRANT .. ON FUNCTION .. is a distributed\n\t\t\t * function, add it to the list\n\t\t\t */\n\t\t\tif (IsAnyObjectDistributed(list_make1(functionAddress)))\n\t\t\t{\n\t\t\t\tgrantFunctionList = lappend(grantFunctionList, functionAddress);\n\t\t\t}\n\t\t}\n\t}\n\treturn grantFunctionList;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/grant.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * grant.c\n *    Commands for granting access to distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/version_compat.h\"\n\n\n/* Local functions forward declarations for helper functions */\nstatic List * CollectGrantTableIdList(GrantStmt *grantStmt);\n\n\n/*\n * PreprocessGrantStmt determines whether a given GRANT/REVOKE statement involves\n * a distributed table. If so, it creates DDLJobs to encapsulate information\n * needed during the worker node portion of DDL execution before returning the\n * DDLJobs in a List. If no distributed table is involved, this returns NIL.\n *\n */\nList *\nPreprocessGrantStmt(Node *node, const char *queryString,\n\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tGrantStmt *grantStmt = castNode(GrantStmt, node);\n\tStringInfoData privsString;\n\tStringInfoData granteesString;\n\tStringInfoData targetString;\n\tStringInfoData ddlString;\n\tListCell *granteeCell = NULL;\n\tListCell *tableListCell = NULL;\n\tbool isFirst = true;\n\tList *ddlJobs = NIL;\n\n\tinitStringInfo(&privsString);\n\tinitStringInfo(&granteesString);\n\tinitStringInfo(&targetString);\n\tinitStringInfo(&ddlString);\n\n\t/*\n\t * So far only table level grants are supported. Most other types of\n\t * grants aren't interesting anyway.\n\t */\n\tif (grantStmt->objtype != OBJECT_TABLE)\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *tableIdList = CollectGrantTableIdList(grantStmt);\n\n\t/* nothing to do if there is no distributed table in the grant list */\n\tif (tableIdList == NIL)\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\t/* deparse the privileges */\n\tif (grantStmt->privileges == NIL)\n\t{\n\t\t/* this is used for table level only */\n\t\tappendStringInfo(&privsString, \"ALL\");\n\t}\n\telse\n\t{\n\t\tListCell *privilegeCell = NULL;\n\n\t\tisFirst = true;\n\t\tforeach(privilegeCell, grantStmt->privileges)\n\t\t{\n\t\t\tAccessPriv *priv = lfirst(privilegeCell);\n\n\t\t\tif (!isFirst)\n\t\t\t{\n\t\t\t\tappendStringInfoString(&privsString, \", \");\n\t\t\t}\n\n\t\t\tif (priv->priv_name)\n\t\t\t{\n\t\t\t\tappendStringInfo(&privsString, \"%s\", priv->priv_name);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * ALL can only be set alone.\n\t\t\t * And ALL is not added as a keyword in priv_name by parser, but\n\t\t\t * because there are column(s) defined, a grantStmt->privileges is\n\t\t\t * defined. So we need to handle this special case here (see if\n\t\t\t * condition above).\n\t\t\t */\n\t\t\telse if (isFirst)\n\t\t\t{\n\t\t\t\t/* this is used for column level only */\n\t\t\t\tappendStringInfo(&privsString, \"ALL\");\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Instead of relying only on the syntax check done by Postgres and\n\t\t\t * adding an assert here, add a default ERROR if ALL is not first\n\t\t\t * and no priv_name is defined.\n\t\t\t */\n\t\t\telse\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t\t\t\t\terrmsg(\"Cannot parse GRANT/REVOKE privileges\")));\n\t\t\t}\n\n\t\t\tisFirst = false;\n\n\t\t\tif (priv->cols != NIL)\n\t\t\t{\n\t\t\t\tStringInfoData colsString;\n\t\t\t\tinitStringInfo(&colsString);\n\n\t\t\t\tAppendColumnNameList(&colsString, priv->cols);\n\t\t\t\tappendStringInfo(&privsString, \"%s\", colsString.data);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* deparse the grantees */\n\tisFirst = true;\n\tforeach(granteeCell, grantStmt->grantees)\n\t{\n\t\tRoleSpec *spec = lfirst(granteeCell);\n\n\t\tif (!isFirst)\n\t\t{\n\t\t\tappendStringInfoString(&granteesString, \", \");\n\t\t}\n\t\tisFirst = false;\n\n\t\tappendStringInfoString(&granteesString, RoleSpecString(spec, true));\n\t}\n\n\t/*\n\t * Deparse the target objects, and issue the deparsed statements to\n\t * workers, if applicable. That's so we easily can replicate statements\n\t * only to distributed relations.\n\t */\n\tisFirst = true;\n\tforeach(tableListCell, tableIdList)\n\t{\n\t\tOid relationId = lfirst_oid(tableListCell);\n\t\tconst char *grantOption = \"\";\n\n\t\tresetStringInfo(&targetString);\n\t\tappendStringInfo(&targetString, \"%s\", generate_relation_name(relationId, NIL));\n\n\t\tif (grantStmt->is_grant)\n\t\t{\n\t\t\tif (grantStmt->grant_option)\n\t\t\t{\n\t\t\t\tgrantOption = \" WITH GRANT OPTION\";\n\t\t\t}\n\n\t\t\tappendStringInfo(&ddlString, \"GRANT %s ON %s TO %s%s\",\n\t\t\t\t\t\t\t privsString.data, targetString.data, granteesString.data,\n\t\t\t\t\t\t\t grantOption);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (grantStmt->grant_option)\n\t\t\t{\n\t\t\t\tgrantOption = \"GRANT OPTION FOR \";\n\t\t\t}\n\n\t\t\tappendStringInfo(&ddlString, \"REVOKE %s%s ON %s FROM %s\",\n\t\t\t\t\t\t\t grantOption, privsString.data, targetString.data,\n\t\t\t\t\t\t\t granteesString.data);\n\n\t\t\tif (grantStmt->behavior == DROP_CASCADE)\n\t\t\t{\n\t\t\t\tappendStringInfoString(&ddlString, \" CASCADE\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfoString(&ddlString, \" RESTRICT\");\n\t\t\t}\n\t\t}\n\n\t\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\t\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\t\tddlJob->metadataSyncCommand = pstrdup(ddlString.data);\n\t\tddlJob->taskList = NIL;\n\t\tif (IsCitusTable(relationId))\n\t\t{\n\t\t\tddlJob->taskList = DDLTaskList(relationId, ddlString.data);\n\t\t}\n\t\tddlJobs = lappend(ddlJobs, ddlJob);\n\n\t\tresetStringInfo(&ddlString);\n\t}\n\n\treturn ddlJobs;\n}\n\n\n/*\n *  CollectGrantTableIdList determines and returns a list of distributed table\n *  Oids from grant statement.\n *  Grant statement may appear in two forms\n *  1 - grant on table:\n *      each distributed table oid in grant object list is added to returned list.\n *  2 - grant all tables in schema:\n *     Collect namespace oid list from grant statement\n *     Add each distributed table oid in the target namespace list to the returned list.\n */\nstatic List *\nCollectGrantTableIdList(GrantStmt *grantStmt)\n{\n\tList *grantTableList = NIL;\n\n\tbool grantOnTableCommand = (grantStmt->targtype == ACL_TARGET_OBJECT &&\n\t\t\t\t\t\t\t\tgrantStmt->objtype == OBJECT_TABLE);\n\tbool grantAllTablesOnSchemaCommand = (grantStmt->targtype == ACL_TARGET_ALL_IN_SCHEMA\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &&\n\t\t\t\t\t\t\t\t\t\t  grantStmt->objtype == OBJECT_TABLE);\n\n\t/* we are only interested in table level grants */\n\tif (!grantOnTableCommand && !grantAllTablesOnSchemaCommand)\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (grantAllTablesOnSchemaCommand)\n\t{\n\t\tList *citusTableIdList = CitusTableTypeIdList(ANY_CITUS_TABLE_TYPE);\n\t\tListCell *citusTableIdCell = NULL;\n\t\tList *namespaceOidList = NIL;\n\n\t\tListCell *objectCell = NULL;\n\t\tforeach(objectCell, grantStmt->objects)\n\t\t{\n\t\t\tchar *nspname = strVal(lfirst(objectCell));\n\t\t\tbool missing_ok = false;\n\t\t\tOid namespaceOid = get_namespace_oid(nspname, missing_ok);\n\t\t\tAssert(namespaceOid != InvalidOid);\n\t\t\tnamespaceOidList = list_append_unique_oid(namespaceOidList, namespaceOid);\n\t\t}\n\n\t\tforeach(citusTableIdCell, citusTableIdList)\n\t\t{\n\t\t\tOid relationId = lfirst_oid(citusTableIdCell);\n\t\t\tOid namespaceOid = get_rel_namespace(relationId);\n\t\t\tif (list_member_oid(namespaceOidList, namespaceOid))\n\t\t\t{\n\t\t\t\tgrantTableList = lappend_oid(grantTableList, relationId);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tListCell *objectCell = NULL;\n\t\tforeach(objectCell, grantStmt->objects)\n\t\t{\n\t\t\tRangeVar *relvar = (RangeVar *) lfirst(objectCell);\n\t\t\tOid relationId = RangeVarGetRelid(relvar, NoLock, false);\n\t\t\tif (IsCitusTable(relationId))\n\t\t\t{\n\t\t\t\tgrantTableList = lappend_oid(grantTableList, relationId);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* check for distributed sequences included in GRANT ON TABLE statement */\n\t\t\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\t\t\tObjectAddressSet(*sequenceAddress, RelationRelationId, relationId);\n\t\t\tif (IsAnyObjectDistributed(list_make1(sequenceAddress)))\n\t\t\t{\n\t\t\t\tgrantTableList = lappend_oid(grantTableList, relationId);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn grantTableList;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/index.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * index.c\n *    Commands for creating and altering indices on distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/catalog.h\"\n#include \"catalog/index.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"commands/defrem.h\"\n#include \"commands/tablecmds.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"parser/parse_utilcmd.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/inval.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/relation_utils.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n\n\n/* Local functions forward declarations for helper functions */\nstatic void ErrorIfCreateIndexHasTooManyColumns(IndexStmt *createIndexStatement);\nstatic int GetNumberOfIndexParameters(IndexStmt *createIndexStatement);\nstatic bool IndexAlreadyExists(IndexStmt *createIndexStatement);\nstatic Oid CreateIndexStmtGetIndexId(IndexStmt *createIndexStatement);\nstatic Oid CreateIndexStmtGetSchemaId(IndexStmt *createIndexStatement);\nstatic void SwitchToSequentialAndLocalExecutionIfIndexNameTooLong(IndexStmt *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  createIndexStatement);\nstatic char * GenerateLongestShardPartitionIndexName(IndexStmt *createIndexStatement);\nstatic char * GenerateDefaultIndexName(IndexStmt *createIndexStatement);\nstatic List * GenerateIndexParameters(IndexStmt *createIndexStatement);\nstatic DDLJob * GenerateCreateIndexDDLJob(IndexStmt *createIndexStatement,\n\t\t\t\t\t\t\t\t\t\t  const char *createIndexCommand);\nstatic Oid CreateIndexStmtGetRelationId(IndexStmt *createIndexStatement);\nstatic List * CreateIndexTaskList(IndexStmt *indexStmt);\nstatic List * CreateReindexTaskList(Oid relationId, ReindexStmt *reindexStmt);\nstatic void RangeVarCallbackForDropIndex(const RangeVar *rel, Oid relOid, Oid oldRelOid,\n\t\t\t\t\t\t\t\t\t\t void *arg);\nstatic void RangeVarCallbackForReindexIndex(const RangeVar *rel, Oid relOid, Oid\n\t\t\t\t\t\t\t\t\t\t\toldRelOid,\n\t\t\t\t\t\t\t\t\t\t\tvoid *arg);\nstatic void ErrorIfUnsupportedIndexStmt(IndexStmt *createIndexStatement);\nstatic void ErrorIfUnsupportedDropIndexStmt(DropStmt *dropIndexStatement);\nstatic List * DropIndexTaskList(Oid relationId, Oid indexId, DropStmt *dropStmt);\nstatic Oid ReindexStmtFindRelationOid(ReindexStmt *reindexStmt, bool missingOk);\n\n/*\n * This struct defines the state for the callback for drop statements.\n * It is copied as it is from commands/tablecmds.c in Postgres source.\n */\nstruct DropRelationCallbackState\n{\n\tchar relkind;\n\tOid heapOid;\n\tbool concurrent;\n};\n\n/*\n * This struct defines the state for the callback for reindex statements.\n * It is copied as it is from commands/indexcmds.c in Postgres source.\n */\nstruct ReindexIndexCallbackState\n{\n\tbool concurrent;\n\tOid locked_table_oid;\n};\n\n\n/*\n * IsIndexRenameStmt returns whether the passed-in RenameStmt is the following\n * form:\n *\n *   - ALTER INDEX RENAME\n */\nbool\nIsIndexRenameStmt(RenameStmt *renameStmt)\n{\n\tbool isIndexRenameStmt = false;\n\n\tif (renameStmt->renameType == OBJECT_INDEX)\n\t{\n\t\tisIndexRenameStmt = true;\n\t}\n\n\treturn isIndexRenameStmt;\n}\n\n\n/*\n * PreprocessIndexStmt determines whether a given CREATE INDEX statement involves\n * a distributed table. If so (and if the statement does not use unsupported\n * options), it modifies the input statement to ensure proper execution against\n * the coordinator node table and creates a DDLJob to encapsulate information needed\n * during the worker node portion of DDL execution before returning that DDLJob\n * in a List. If no distributed table is involved, this function returns NIL.\n */\nList *\nPreprocessIndexStmt(Node *node, const char *createIndexCommand,\n\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tIndexStmt *createIndexStatement = castNode(IndexStmt, node);\n\n\tRangeVar *relationRangeVar = createIndexStatement->relation;\n\tif (relationRangeVar == NULL)\n\t{\n\t\t/* let's be on the safe side */\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * We first check whether a distributed relation is affected. For that,\n\t * we need to open the relation. To prevent race conditions with later\n\t * lookups, lock the table.\n\t *\n\t * XXX: Consider using RangeVarGetRelidExtended with a permission\n\t * checking callback. Right now we'll acquire the lock before having\n\t * checked permissions, and will only fail when executing the actual\n\t * index statements.\n\t */\n\tLOCKMODE lockMode = GetCreateIndexRelationLockMode(createIndexStatement);\n\tRelation relation = table_openrv(relationRangeVar, lockMode);\n\n\t/*\n\t * Before we do any further processing, fix the schema name to make sure\n\t * that a (distributed) table with the same name does not appear on the\n\t * search_path in front of the current schema. We do this even if the\n\t * table is not distributed, since a distributed table may appear on the\n\t * search_path by the time postgres starts processing this command.\n\t */\n\tif (relationRangeVar->schemaname == NULL)\n\t{\n\t\t/* ensure we copy string into proper context */\n\t\tMemoryContext relationContext = GetMemoryChunkContext(relationRangeVar);\n\t\tchar *namespaceName = RelationGetNamespaceName(relation);\n\t\trelationRangeVar->schemaname = MemoryContextStrdup(relationContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   namespaceName);\n\t}\n\n\tOid relationOid = relation->rd_id;\n\ttable_close(relation, NoLock);\n\n\tOid relationId = CreateIndexStmtGetRelationId(createIndexStatement);\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tif (createIndexStatement->idxname == NULL)\n\t{\n\t\t/*\n\t\t * Postgres does not support indexes with over INDEX_MAX_KEYS columns\n\t\t * and we should not attempt to generate an index name for such cases.\n\t\t */\n\t\tErrorIfCreateIndexHasTooManyColumns(createIndexStatement);\n\n\t\t/*\n\t\t * If there are expressions on the index, we should first transform\n\t\t * the statement as the default index name depends on that. We do\n\t\t * it on a copy not to interfere with standard process utility.\n\t\t */\n\t\tIndexStmt *copyCreateIndexStatement =\n\t\t\ttransformIndexStmt(relationOid, copyObject(createIndexStatement),\n\t\t\t\t\t\t\t   createIndexCommand);\n\n\t\t/* ensure we copy string into proper context */\n\t\tMemoryContext relationContext = GetMemoryChunkContext(relationRangeVar);\n\t\tchar *defaultIndexName = GenerateDefaultIndexName(copyCreateIndexStatement);\n\t\tcreateIndexStatement->idxname = MemoryContextStrdup(relationContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdefaultIndexName);\n\t}\n\n\tif (IndexAlreadyExists(createIndexStatement))\n\t{\n\t\t/*\n\t\t * Let standard_processUtility to error out or skip if command has\n\t\t * IF NOT EXISTS.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tErrorIfUnsupportedIndexStmt(createIndexStatement);\n\n\t/*\n\t * Citus has the logic to truncate the long shard names to prevent\n\t * various issues, including self-deadlocks. However, for partitioned\n\t * tables, when index is created on the parent table, the index names\n\t * on the partitions are auto-generated by Postgres. We use the same\n\t * Postgres function to generate the index names on the shards of the\n\t * partitions. If the length exceeds the limit, we switch to sequential\n\t * execution mode.\n\t *\n\t * The root cause of the problem is that postgres truncates the\n\t * table/index names if they are longer than \"NAMEDATALEN - 1\".\n\t * From Citus' perspective, running commands in parallel on the\n\t * shards could mean these table/index names are truncated to be\n\t * the same, and thus forming a self-deadlock as these tables/\n\t * indexes are inserted into postgres' metadata tables, like pg_class.\n\t */\n\tSwitchToSequentialAndLocalExecutionIfIndexNameTooLong(createIndexStatement);\n\n\tDDLJob *ddlJob = GenerateCreateIndexDDLJob(createIndexStatement, createIndexCommand);\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * ErrorIfCreateIndexHasTooManyColumns errors out if given CREATE INDEX command\n * would use more than INDEX_MAX_KEYS columns.\n */\nstatic void\nErrorIfCreateIndexHasTooManyColumns(IndexStmt *createIndexStatement)\n{\n\tint numberOfIndexParameters = GetNumberOfIndexParameters(createIndexStatement);\n\tif (numberOfIndexParameters <= INDEX_MAX_KEYS)\n\t{\n\t\treturn;\n\t}\n\n\tereport(ERROR, (errcode(ERRCODE_TOO_MANY_COLUMNS),\n\t\t\t\t\terrmsg(\"cannot use more than %d columns in an index\",\n\t\t\t\t\t\t   INDEX_MAX_KEYS)));\n}\n\n\n/*\n * GetNumberOfIndexParameters returns number of parameters to be used when\n * creating the index to be defined by given CREATE INDEX command.\n */\nstatic int\nGetNumberOfIndexParameters(IndexStmt *createIndexStatement)\n{\n\tList *indexParams = createIndexStatement->indexParams;\n\tList *indexIncludingParams = createIndexStatement->indexIncludingParams;\n\treturn list_length(indexParams) + list_length(indexIncludingParams);\n}\n\n\n/*\n * IndexAlreadyExists returns true if index to be created by given CREATE INDEX\n * command already exists.\n */\nstatic bool\nIndexAlreadyExists(IndexStmt *createIndexStatement)\n{\n\tOid indexRelationId = CreateIndexStmtGetIndexId(createIndexStatement);\n\treturn OidIsValid(indexRelationId);\n}\n\n\n/*\n * CreateIndexStmtGetIndexId returns OID of the index that given CREATE INDEX\n * command attempts to create if it's already created before. Otherwise, returns\n * InvalidOid.\n */\nstatic Oid\nCreateIndexStmtGetIndexId(IndexStmt *createIndexStatement)\n{\n\tchar *indexName = createIndexStatement->idxname;\n\tOid namespaceId = CreateIndexStmtGetSchemaId(createIndexStatement);\n\tOid indexRelationId = get_relname_relid(indexName, namespaceId);\n\treturn indexRelationId;\n}\n\n\n/*\n * CreateIndexStmtGetSchemaId returns schemaId of the schema that given\n * CREATE INDEX command operates on.\n */\nstatic Oid\nCreateIndexStmtGetSchemaId(IndexStmt *createIndexStatement)\n{\n\tRangeVar *relationRangeVar = createIndexStatement->relation;\n\tchar *schemaName = relationRangeVar->schemaname;\n\tbool missingOk = false;\n\tOid namespaceId = get_namespace_oid(schemaName, missingOk);\n\treturn namespaceId;\n}\n\n\n/*\n * ExecuteFunctionOnEachTableIndex executes the given pgIndexProcessor function on each\n * index of the given relation.\n * It returns a list that is filled by the pgIndexProcessor.\n */\nList *\nExecuteFunctionOnEachTableIndex(Oid relationId, PGIndexProcessor pgIndexProcessor,\n\t\t\t\t\t\t\t\tint indexFlags)\n{\n\tList *result = NIL;\n\n\tRelation relation = RelationIdGetRelation(relationId);\n\tif (!RelationIsValid(relation))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not open relation with OID %u\", relationId)));\n\t}\n\n\tList *indexIdList = RelationGetIndexList(relation);\n\tOid indexId = InvalidOid;\n\tforeach_declared_oid(indexId, indexIdList)\n\t{\n\t\tHeapTuple indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));\n\t\tif (!HeapTupleIsValid(indexTuple))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cache lookup failed for index with oid %u\",\n\t\t\t\t\t\t\t\t   indexId)));\n\t\t}\n\n\t\tForm_pg_index indexForm = (Form_pg_index) GETSTRUCT(indexTuple);\n\t\tpgIndexProcessor(indexForm, &result, indexFlags);\n\t\tReleaseSysCache(indexTuple);\n\t}\n\n\tRelationClose(relation);\n\treturn result;\n}\n\n\n/*\n * SwitchToSequentialAndLocalExecutionIfIndexNameTooLong generates the longest index name\n * on the shards of the partitions, and if exceeds the limit switches to sequential and\n * local execution to prevent self-deadlocks.\n */\nstatic void\nSwitchToSequentialAndLocalExecutionIfIndexNameTooLong(IndexStmt *createIndexStatement)\n{\n\tOid relationId = CreateIndexStmtGetRelationId(createIndexStatement);\n\tif (!PartitionedTable(relationId))\n\t{\n\t\t/* Citus already handles long names for regular tables */\n\t\treturn;\n\t}\n\n\tif (ShardIntervalCount(relationId) == 0)\n\t{\n\t\t/*\n\t\t * Relation has no shards, so we cannot run into \"long shard index\n\t\t * name\" issue.\n\t\t */\n\t\treturn;\n\t}\n\n\tchar *indexName = GenerateLongestShardPartitionIndexName(createIndexStatement);\n\tif (indexName &&\n\t\tstrnlen(indexName, NAMEDATALEN) >= NAMEDATALEN - 1)\n\t{\n\t\tif (ParallelQueryExecutedInTransaction())\n\t\t{\n\t\t\t/*\n\t\t\t * If there has already been a parallel query executed, the sequential mode\n\t\t\t * would still use the already opened parallel connections to the workers,\n\t\t\t * thus contradicting our purpose of using sequential mode.\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\"The index name (%s) on a shard is too long and could lead \"\n\t\t\t\t\t\t\t\t\"to deadlocks when executed in a transaction \"\n\t\t\t\t\t\t\t\t\"block after a parallel query\", indexName),\n\t\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t\t}\n\t\telse\n\t\t{\n\t\t\telog(DEBUG1, \"the index name on the shards of the partition \"\n\t\t\t\t\t\t \"is too long, switching to sequential and local execution \"\n\t\t\t\t\t\t \"mode to prevent self deadlocks: %s\", indexName);\n\n\t\t\tSetLocalMultiShardModifyModeToSequential();\n\t\t\tSetLocalExecutionStatus(LOCAL_EXECUTION_REQUIRED);\n\t\t}\n\t}\n}\n\n\n/*\n * GenerateLongestShardPartitionIndexName emulates Postgres index name\n * generation for partitions on the shards. It returns the longest\n * possible index name.\n */\nstatic char *\nGenerateLongestShardPartitionIndexName(IndexStmt *createIndexStatement)\n{\n\tOid relationId = CreateIndexStmtGetRelationId(createIndexStatement);\n\tOid longestNamePartitionId = PartitionWithLongestNameRelationId(relationId);\n\tif (!OidIsValid(longestNamePartitionId))\n\t{\n\t\t/* no partitions have been created yet */\n\t\treturn NULL;\n\t}\n\n\tchar *longestPartitionShardName = get_rel_name(longestNamePartitionId);\n\tShardInterval *shardInterval = LoadShardIntervalWithLongestShardName(\n\t\tlongestNamePartitionId);\n\tAppendShardIdToName(&longestPartitionShardName, shardInterval->shardId);\n\n\tIndexStmt *createLongestShardIndexStmt = copyObject(createIndexStatement);\n\tcreateLongestShardIndexStmt->relation->relname = longestPartitionShardName;\n\n\tchar *choosenIndexName = GenerateDefaultIndexName(createLongestShardIndexStmt);\n\treturn choosenIndexName;\n}\n\n\n/*\n * GenerateDefaultIndexName is a wrapper around postgres function ChooseIndexName\n * that generates default index name for the index to be created by given CREATE\n * INDEX statement as postgres would do.\n *\n * (See DefineIndex at postgres/src/backend/commands/indexcmds.c)\n */\nstatic char *\nGenerateDefaultIndexName(IndexStmt *createIndexStatement)\n{\n\tchar *relationName = createIndexStatement->relation->relname;\n\tOid namespaceId = CreateIndexStmtGetSchemaId(createIndexStatement);\n\tList *indexParams = GenerateIndexParameters(createIndexStatement);\n\tList *indexColNames = ChooseIndexColumnNames(indexParams);\n\tchar *indexName = ChooseIndexName(relationName, namespaceId, indexColNames,\n\t\t\t\t\t\t\t\t\t  createIndexStatement->excludeOpNames,\n\t\t\t\t\t\t\t\t\t  createIndexStatement->primary,\n\t\t\t\t\t\t\t\t\t  createIndexStatement->isconstraint);\n\n\treturn indexName;\n}\n\n\n/*\n * GenerateIndexParameters is a helper function that creates a list of parameters\n * required to assign a default index name for the index to be created by given\n * CREATE INDEX command.\n */\nstatic List *\nGenerateIndexParameters(IndexStmt *createIndexStatement)\n{\n\tList *indexParams = createIndexStatement->indexParams;\n\tList *indexIncludingParams = createIndexStatement->indexIncludingParams;\n\tList *allIndexParams = list_concat(list_copy(indexParams),\n\t\t\t\t\t\t\t\t\t   list_copy(indexIncludingParams));\n\treturn allIndexParams;\n}\n\n\n/*\n * GenerateCreateIndexDDLJob returns DDLJob for given CREATE INDEX command.\n */\nstatic DDLJob *\nGenerateCreateIndexDDLJob(IndexStmt *createIndexStatement, const char *createIndexCommand)\n{\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId,\n\t\t\t\t\t CreateIndexStmtGetRelationId(createIndexStatement));\n\tddlJob->startNewTransaction = createIndexStatement->concurrent;\n\tddlJob->metadataSyncCommand = createIndexCommand;\n\tddlJob->taskList = CreateIndexTaskList(createIndexStatement);\n\tddlJob->warnForPartialFailure = true;\n\n\treturn ddlJob;\n}\n\n\n/*\n * CreateIndexStmtGetRelationId returns relationId for relation that given\n * CREATE INDEX command operates on.\n */\nstatic Oid\nCreateIndexStmtGetRelationId(IndexStmt *createIndexStatement)\n{\n\tRangeVar *relationRangeVar = createIndexStatement->relation;\n\tLOCKMODE lockMode = GetCreateIndexRelationLockMode(createIndexStatement);\n\tbool missingOk = false;\n\tOid relationId = RangeVarGetRelid(relationRangeVar, lockMode, missingOk);\n\treturn relationId;\n}\n\n\n/*\n * GetCreateIndexRelationLockMode returns required lock mode to open the\n * relation that given CREATE INDEX command operates on.\n */\nLOCKMODE\nGetCreateIndexRelationLockMode(IndexStmt *createIndexStatement)\n{\n\tif (createIndexStatement->concurrent)\n\t{\n\t\treturn ShareUpdateExclusiveLock;\n\t}\n\telse\n\t{\n\t\treturn ShareLock;\n\t}\n}\n\n\n/*\n * ReindexStmtFindRelationOid returns the oid of the relation on which the index exist\n * if the object is an index in the reindex stmt. It returns the oid of the relation\n * if the object is a table in the reindex stmt. It also acquires the relevant lock\n * for the statement.\n */\nstatic Oid\nReindexStmtFindRelationOid(ReindexStmt *reindexStmt, bool missingOk)\n{\n\tAssert(reindexStmt->relation != NULL);\n\n\tAssert(reindexStmt->kind == REINDEX_OBJECT_INDEX ||\n\t\t   reindexStmt->kind == REINDEX_OBJECT_TABLE);\n\n\tOid relationId = InvalidOid;\n\n\tLOCKMODE lockmode = IsReindexWithParam_compat(reindexStmt, \"concurrently\") ?\n\t\t\t\t\t\tShareUpdateExclusiveLock : AccessExclusiveLock;\n\n\tif (reindexStmt->kind == REINDEX_OBJECT_INDEX)\n\t{\n\t\tstruct ReindexIndexCallbackState state;\n\t\tstate.concurrent = IsReindexWithParam_compat(reindexStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"concurrently\");\n\t\tstate.locked_table_oid = InvalidOid;\n\n\t\tOid indOid = RangeVarGetRelidExtended(reindexStmt->relation, lockmode,\n\t\t\t\t\t\t\t\t\t\t\t  (missingOk) ? RVR_MISSING_OK : 0,\n\t\t\t\t\t\t\t\t\t\t\t  RangeVarCallbackForReindexIndex,\n\t\t\t\t\t\t\t\t\t\t\t  &state);\n\t\trelationId = IndexGetRelation(indOid, missingOk);\n\t}\n\telse\n\t{\n\t\trelationId = RangeVarGetRelidExtended(reindexStmt->relation, lockmode,\n\t\t\t\t\t\t\t\t\t\t\t  (missingOk) ? RVR_MISSING_OK : 0,\n\t\t\t\t\t\t\t\t\t\t\t  RangeVarCallbackOwnsTable, NULL);\n\t}\n\n\treturn relationId;\n}\n\n\n/*\n * PreprocessReindexStmt determines whether a given REINDEX statement involves\n * a distributed table. If so (and if the statement does not use unsupported\n * options), it modifies the input statement to ensure proper execution against\n * the coordinator node table and creates a DDLJob to encapsulate information needed\n * during the worker node portion of DDL execution before returning that DDLJob\n * in a List. If no distributed table is involved, this function returns NIL.\n */\nList *\nPreprocessReindexStmt(Node *node, const char *reindexCommand,\n\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tReindexStmt *reindexStatement = castNode(ReindexStmt, node);\n\tList *ddlJobs = NIL;\n\n\t/*\n\t * We first check whether a distributed relation is affected. For that, we need to\n\t * open the relation. To prevent race conditions with later lookups, lock the table,\n\t * and modify the rangevar to include the schema.\n\t */\n\tif (reindexStatement->relation != NULL)\n\t{\n\t\tOid relationId = ReindexStmtFindRelationOid(reindexStatement, false);\n\t\tMemoryContext relationContext = NULL;\n\t\tRelation relation = NULL;\n\t\tif (reindexStatement->kind == REINDEX_OBJECT_INDEX)\n\t\t{\n\t\t\tOid indOid = RangeVarGetRelid(reindexStatement->relation, NoLock, 0);\n\t\t\trelation = index_open(indOid, NoLock);\n\t\t}\n\t\telse\n\t\t{\n\t\t\trelation = table_openrv(reindexStatement->relation, NoLock);\n\t\t}\n\n\t\tbool isCitusRelation = IsCitusTable(relationId);\n\n\t\tif (reindexStatement->relation->schemaname == NULL)\n\t\t{\n\t\t\t/*\n\t\t\t * Before we do any further processing, fix the schema name to make sure\n\t\t\t * that a (distributed) table with the same name does not appear on the\n\t\t\t * search path in front of the current schema. We do this even if the\n\t\t\t * table is not distributed, since a distributed table may appear on the\n\t\t\t * search path by the time postgres starts processing this statement.\n\t\t\t */\n\t\t\tchar *namespaceName = get_namespace_name(RelationGetNamespace(relation));\n\n\t\t\t/* ensure we copy string into proper context */\n\t\t\trelationContext = GetMemoryChunkContext(reindexStatement->relation);\n\t\t\treindexStatement->relation->schemaname = MemoryContextStrdup(relationContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t namespaceName);\n\t\t}\n\n\t\tif (reindexStatement->kind == REINDEX_OBJECT_INDEX)\n\t\t{\n\t\t\tindex_close(relation, NoLock);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttable_close(relation, NoLock);\n\t\t}\n\n\t\tif (isCitusRelation)\n\t\t{\n\t\t\tif (PartitionedTable(relationId))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\terrmsg(\"REINDEX TABLE queries on distributed partitioned \"\n\t\t\t\t\t\t\t\t\t   \"tables are not supported\")));\n\t\t\t}\n\n\t\t\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\t\t\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\t\t\tddlJob->startNewTransaction = IsReindexWithParam_compat(reindexStatement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"concurrently\");\n\t\t\tddlJob->metadataSyncCommand = reindexCommand;\n\t\t\tddlJob->taskList = CreateReindexTaskList(relationId, reindexStatement);\n\t\t\tddlJob->warnForPartialFailure = true;\n\n\t\t\tddlJobs = list_make1(ddlJob);\n\t\t}\n\t}\n\n\treturn ddlJobs;\n}\n\n\n/*\n * ReindexStmtObjectAddress returns list of object addresses in the reindex\n * statement. We add the address if the object is either index or table;\n * else, we add invalid address.\n */\nList *\nReindexStmtObjectAddress(Node *stmt, bool missing_ok, bool isPostprocess)\n{\n\tReindexStmt *reindexStatement = castNode(ReindexStmt, stmt);\n\n\tOid relationId = InvalidOid;\n\tif (reindexStatement->relation != NULL)\n\t{\n\t\t/* we currently only support reindex commands on tables */\n\t\trelationId = ReindexStmtFindRelationOid(reindexStatement, missing_ok);\n\t}\n\n\tObjectAddress *objectAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*objectAddress, RelationRelationId, relationId);\n\n\treturn list_make1(objectAddress);\n}\n\n\n/*\n * PreprocessDropIndexStmt determines whether a given DROP INDEX statement involves\n * a distributed table. If so (and if the statement does not use unsupported\n * options), it modifies the input statement to ensure proper execution against\n * the coordinator node table and creates a DDLJob to encapsulate information needed\n * during the worker node portion of DDL execution before returning that DDLJob\n * in a List. If no distributed table is involved, this function returns NIL.\n */\nList *\nPreprocessDropIndexStmt(Node *node, const char *dropIndexCommand,\n\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tDropStmt *dropIndexStatement = castNode(DropStmt, node);\n\tList *ddlJobs = NIL;\n\tOid distributedIndexId = InvalidOid;\n\tOid distributedRelationId = InvalidOid;\n\n\tAssert(dropIndexStatement->removeType == OBJECT_INDEX);\n\n\t/* check if any of the indexes being dropped belong to a distributed table */\n\tList *objectNameList = NULL;\n\tforeach_declared_ptr(objectNameList, dropIndexStatement->objects)\n\t{\n\t\tstruct DropRelationCallbackState state;\n\t\tuint32 rvrFlags = RVR_MISSING_OK;\n\t\tLOCKMODE lockmode = AccessExclusiveLock;\n\n\t\tRangeVar *rangeVar = makeRangeVarFromNameList(objectNameList);\n\n\t\t/*\n\t\t * We don't support concurrently dropping indexes for distributed\n\t\t * tables, but till this point, we don't know if it is a regular or a\n\t\t * distributed table.\n\t\t */\n\t\tif (dropIndexStatement->concurrent)\n\t\t{\n\t\t\tlockmode = ShareUpdateExclusiveLock;\n\t\t}\n\n\t\t/*\n\t\t * The next few statements are based on RemoveRelations() in\n\t\t * commands/tablecmds.c in Postgres source.\n\t\t */\n\t\tAcceptInvalidationMessages();\n\n\t\tstate.relkind = RELKIND_INDEX;\n\t\tstate.heapOid = InvalidOid;\n\t\tstate.concurrent = dropIndexStatement->concurrent;\n\n\t\tOid indexId = RangeVarGetRelidExtended(rangeVar, lockmode, rvrFlags,\n\t\t\t\t\t\t\t\t\t\t\t   RangeVarCallbackForDropIndex,\n\t\t\t\t\t\t\t\t\t\t\t   (void *) &state);\n\n\t\t/*\n\t\t * If the index does not exist, we don't do anything here, and allow\n\t\t * postgres to throw appropriate error or notice message later.\n\t\t */\n\t\tif (!OidIsValid(indexId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tOid relationId = IndexGetRelation(indexId, false);\n\t\tbool isCitusRelation = IsCitusTable(relationId);\n\t\tif (isCitusRelation)\n\t\t{\n\t\t\tdistributedIndexId = indexId;\n\t\t\tdistributedRelationId = relationId;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (OidIsValid(distributedIndexId))\n\t{\n\t\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\n\t\tErrorIfUnsupportedDropIndexStmt(dropIndexStatement);\n\n\t\tif (AnyForeignKeyDependsOnIndex(distributedIndexId))\n\t\t{\n\t\t\tMarkInvalidateForeignKeyGraph();\n\t\t}\n\n\t\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId,\n\t\t\t\t\t\t distributedRelationId);\n\n\t\t/*\n\t\t * We do not want DROP INDEX CONCURRENTLY to commit locally before\n\t\t * sending commands, because if there is a failure we would like to\n\t\t * to be able to repeat the DROP INDEX later.\n\t\t */\n\t\tddlJob->startNewTransaction = false;\n\t\tddlJob->metadataSyncCommand = dropIndexCommand;\n\t\tddlJob->taskList = DropIndexTaskList(distributedRelationId, distributedIndexId,\n\t\t\t\t\t\t\t\t\t\t\t dropIndexStatement);\n\t\tddlJob->warnForPartialFailure = true;\n\n\t\tddlJobs = list_make1(ddlJob);\n\t}\n\n\treturn ddlJobs;\n}\n\n\n/*\n * PostprocessIndexStmt marks new indexes invalid if they were created using the\n * CONCURRENTLY flag. This (non-transactional) change provides the fallback\n * state if an error is raised, otherwise a sub-sequent change to valid will be\n * committed.\n */\nList *\nPostprocessIndexStmt(Node *node, const char *queryString)\n{\n\tIndexStmt *indexStmt = castNode(IndexStmt, node);\n\n\t/* this logic only applies to the coordinator */\n\tif (!IsCoordinator())\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * We make sure schema name is not null in the PreprocessIndexStmt\n\t */\n\tOid schemaId = get_namespace_oid(indexStmt->relation->schemaname, true);\n\tOid relationId = get_relname_relid(indexStmt->relation->relname, schemaId);\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\tOid indexRelationId = get_relname_relid(indexStmt->idxname, schemaId);\n\n\t/* ensure dependencies of index exist on all nodes */\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, RelationRelationId, indexRelationId);\n\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(address));\n\n\t/* furtheron we are only processing CONCURRENT index statements */\n\tif (!indexStmt->concurrent)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * EnsureAllObjectDependenciesExistOnAllNodes could have distributed objects that are required\n\t * by this index. During the propagation process an active snapshout might be left as\n\t * a side effect of inserting the local tuples via SPI. To not leak a snapshot like\n\t * that we will pop any snapshot if we have any right before we commit.\n\t */\n\tif (ActiveSnapshotSet())\n\t{\n\t\tPopActiveSnapshot();\n\t}\n\n\t/* commit the current transaction and start anew */\n\tCommitTransactionCommand();\n\tStartTransactionCommand();\n\n\t/* get the affected relation and index */\n\tRelation relation = table_openrv(indexStmt->relation, ShareUpdateExclusiveLock);\n\tRelation indexRelation = index_open(indexRelationId, RowExclusiveLock);\n\n\t/* close relations but retain locks */\n\ttable_close(relation, NoLock);\n\tindex_close(indexRelation, NoLock);\n\n\tPushActiveSnapshot(GetTransactionSnapshot());\n\n\t/* mark index as invalid, in-place (cannot be rolled back) */\n\tindex_set_state_flags(indexRelationId, INDEX_DROP_CLEAR_VALID);\n\tPopActiveSnapshot();\n\n\t/* re-open a transaction command from here on out */\n\tCommitTransactionCommand();\n\tStartTransactionCommand();\n\n\treturn NIL;\n}\n\n\n/*\n * ErrorIfUnsupportedAlterIndexStmt checks if the corresponding alter index\n * statement is supported for distributed tables and errors out if it is not.\n * Currently, only the following commands are supported.\n *\n * ALTER INDEX SET ()\n * ALTER INDEX RESET ()\n * ALTER INDEX ALTER COLUMN SET STATISTICS\n */\nvoid\nErrorIfUnsupportedAlterIndexStmt(AlterTableStmt *alterTableStatement)\n{\n\t/* error out if any of the subcommands are unsupported */\n\tList *commandList = alterTableStatement->cmds;\n\tAlterTableCmd *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tAlterTableType alterTableType = command->subtype;\n\n\t\tswitch (alterTableType)\n\t\t{\n\t\t\tcase AT_SetRelOptions:  /* SET (...) */\n\t\t\tcase AT_ResetRelOptions:    /* RESET (...) */\n\t\t\tcase AT_ReplaceRelOptions:  /* replace entire option list */\n\t\t\tcase AT_SetStatistics:  /* SET STATISTICS */\n\t\t\tcase AT_AttachPartition: /* ATTACH PARTITION */\n\t\t\t{\n\t\t\t\t/* this command is supported by Citus */\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* unsupported create index statements */\n\t\t\tcase AT_SetTableSpace:\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t errmsg(\"alter index ... set tablespace ... \"\n\t\t\t\t\t\t\t\t\"is currently unsupported\"),\n\t\t\t\t\t\t errdetail(\"Only RENAME TO, SET (), RESET (), ATTACH PARTITION \"\n\t\t\t\t\t\t\t\t   \"and SET STATISTICS are supported.\")));\n\t\t\t\treturn; /* keep compiler happy */\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * CreateIndexTaskList builds a list of tasks to execute a CREATE INDEX command.\n */\nstatic List *\nCreateIndexTaskList(IndexStmt *indexStmt)\n{\n\tList *taskList = NIL;\n\tOid relationId = CreateIndexStmtGetRelationId(indexStmt);\n\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\tStringInfoData ddlString;\n\tuint64 jobId = INVALID_JOB_ID;\n\tint taskId = 1;\n\n\tinitStringInfo(&ddlString);\n\n\t/* lock metadata before getting placement lists */\n\tLockShardListMetadata(shardIntervalList, ShareLock);\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\n\t\tdeparse_shard_index_statement(indexStmt, relationId, shardId, &ddlString);\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->jobId = jobId;\n\t\ttask->taskId = taskId++;\n\t\ttask->taskType = DDL_TASK;\n\t\tSetTaskQueryString(task, pstrdup(ddlString.data));\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\ttask->dependentTaskList = NULL;\n\t\ttask->anchorShardId = shardId;\n\t\ttask->taskPlacementList = ActiveShardPlacementList(shardId);\n\t\ttask->cannotBeExecutedInTransaction = indexStmt->concurrent;\n\n\t\ttaskList = lappend(taskList, task);\n\n\t\tresetStringInfo(&ddlString);\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * CreateReindexTaskList builds a list of tasks to execute a REINDEX command\n * against a specified distributed table.\n */\nstatic List *\nCreateReindexTaskList(Oid relationId, ReindexStmt *reindexStmt)\n{\n\tList *taskList = NIL;\n\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\tStringInfoData ddlString;\n\tuint64 jobId = INVALID_JOB_ID;\n\tint taskId = 1;\n\n\tinitStringInfo(&ddlString);\n\n\t/* lock metadata before getting placement lists */\n\tLockShardListMetadata(shardIntervalList, ShareLock);\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\n\t\tdeparse_shard_reindex_statement(reindexStmt, relationId, shardId, &ddlString);\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->jobId = jobId;\n\t\ttask->taskId = taskId++;\n\t\ttask->taskType = DDL_TASK;\n\t\tSetTaskQueryString(task, pstrdup(ddlString.data));\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\ttask->dependentTaskList = NULL;\n\t\ttask->anchorShardId = shardId;\n\t\ttask->taskPlacementList = ActiveShardPlacementList(shardId);\n\t\ttask->cannotBeExecutedInTransaction =\n\t\t\tIsReindexWithParam_compat(reindexStmt, \"concurrently\");\n\n\t\ttaskList = lappend(taskList, task);\n\n\t\tresetStringInfo(&ddlString);\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * Before acquiring a table lock, check whether we have sufficient rights.\n * In the case of DROP INDEX, also try to lock the table before the index.\n *\n * This code is heavily borrowed from RangeVarCallbackForDropRelation() in\n * commands/tablecmds.c in Postgres source. We need this to ensure the right\n * order of locking while dealing with DROP INDEX statements. Because we are\n * exclusively using this callback for INDEX processing, the PARTITION-related\n * logic from PostgreSQL's similar callback has been omitted as unneeded.\n */\nstatic void\nRangeVarCallbackForDropIndex(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)\n{\n\t/* *INDENT-OFF* */\n\tHeapTuple\ttuple;\n\tchar\t\trelkind;\n\tchar\t\texpected_relkind;\n\tLOCKMODE\theap_lockmode;\n\n\tstruct DropRelationCallbackState *state = (struct DropRelationCallbackState *) arg;\n\trelkind = state->relkind;\n\theap_lockmode = state->concurrent ?\n\t                ShareUpdateExclusiveLock : AccessExclusiveLock;\n\n\tAssert(relkind == RELKIND_INDEX);\n\n\t/*\n\t * If we previously locked some other index's heap, and the name we're\n\t * looking up no longer refers to that relation, release the now-useless\n\t * lock.\n\t */\n\tif (relOid != oldRelOid && OidIsValid(state->heapOid))\n\t{\n\t\tUnlockRelationOid(state->heapOid, heap_lockmode);\n\t\tstate->heapOid = InvalidOid;\n\t}\n\n\t/* Didn't find a relation, so no need for locking or permission checks. */\n\tif (!OidIsValid(relOid))\n\t\treturn;\n\n\ttuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));\n\tif (!HeapTupleIsValid(tuple))\n\t\treturn;\t\t\t\t\t/* concurrently dropped, so nothing to do */\n\tForm_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);\n\n\t/*\n\t * PG 11 sends relkind as partitioned index for an index\n\t * on partitioned table. It is handled the same\n\t * as regular index as far as we are concerned here.\n\t *\n\t * See tablecmds.c:RangeVarCallbackForDropRelation()\n\t */\n\texpected_relkind = classform->relkind;\n\n\tif (expected_relkind == RELKIND_PARTITIONED_INDEX)\n\t\texpected_relkind = RELKIND_INDEX;\n\n\tif (expected_relkind != relkind)\n\t\tereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),\n\t\t\t\terrmsg(\"\\\"%s\\\" is not an index\", rel->relname)));\n\n\t/* Allow DROP to either table owner or schema owner */\n\tif (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&\n\t\t!object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))\n\t{\n\t\taclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, rel->relname);\n\t}\n\n\tif (!allowSystemTableMods && IsSystemClass(relOid, classform))\n\t\tereport(ERROR,\n\t\t        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),\n\t\t\t\t        errmsg(\"permission denied: \\\"%s\\\" is a system catalog\",\n\t\t\t\t               rel->relname)));\n\n\tReleaseSysCache(tuple);\n\n\t/*\n\t * In DROP INDEX, attempt to acquire lock on the parent table before\n\t * locking the index.  index_drop() will need this anyway, and since\n\t * regular queries lock tables before their indexes, we risk deadlock if\n\t * we do it the other way around.  No error if we don't find a pg_index\n\t * entry, though --- the relation may have been dropped.\n\t */\n\tif (relkind == RELKIND_INDEX && relOid != oldRelOid)\n\t{\n\t\tstate->heapOid = IndexGetRelation(relOid, true);\n\t\tif (OidIsValid(state->heapOid))\n\t\t\tLockRelationOid(state->heapOid, heap_lockmode);\n\t}\n\t/* *INDENT-ON* */\n}\n\n\n/*\n * Check permissions on table before acquiring relation lock; also lock\n * the heap before the RangeVarGetRelidExtended takes the index lock, to avoid\n * deadlocks.\n *\n * This code is borrowed from RangeVarCallbackForReindexIndex() in\n * commands/indexcmds.c in Postgres source. We need this to ensure the right\n * order of locking while dealing with REINDEX statements.\n */\nstatic void\nRangeVarCallbackForReindexIndex(const RangeVar *relation, Oid relId, Oid oldRelId,\n\t\t\t\t\t\t\t\tvoid *arg)\n{\n\t/* *INDENT-OFF* */\n\tchar\t\trelkind;\n\tstruct ReindexIndexCallbackState *state = arg;\n\tLOCKMODE\ttable_lockmode;\n\tOid\t\t\ttable_oid;\n\n\t/*\n\t * Lock level here should match table lock in reindex_index() for\n\t * non-concurrent case and table locks used by index_concurrently_*() for\n\t * concurrent case.\n\t */\n\ttable_lockmode = state->concurrent ? ShareUpdateExclusiveLock : ShareLock;\n\n\t/*\n\t * If we previously locked some other index's heap, and the name we're\n\t * looking up no longer refers to that relation, release the now-useless\n\t * lock.\n\t */\n\tif (relId != oldRelId && OidIsValid(oldRelId))\n\t{\n\t\tUnlockRelationOid(state->locked_table_oid, table_lockmode);\n\t\tstate->locked_table_oid = InvalidOid;\n\t}\n\n\t/* If the relation does not exist, there's nothing more to do. */\n\tif (!OidIsValid(relId))\n\t\treturn;\n\n\t/*\n\t * If the relation does exist, check whether it's an index.  But note that\n\t * the relation might have been dropped between the time we did the name\n\t * lookup and now.  In that case, there's nothing to do.\n\t */\n\trelkind = get_rel_relkind(relId);\n\tif (!relkind)\n\t\treturn;\n\tif (relkind != RELKIND_INDEX && relkind != RELKIND_PARTITIONED_INDEX)\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_WRONG_OBJECT_TYPE),\n\t\t\t\t errmsg(\"\\\"%s\\\" is not an index\", relation->relname)));\n\n\t/* Check permissions */\n\n\t#if PG_VERSION_NUM >= PG_VERSION_17\n\ttable_oid = IndexGetRelation(relId, true);\n\tif (OidIsValid(table_oid))\n\t{\n\t\tAclResult aclresult = pg_class_aclcheck(table_oid, GetUserId(), ACL_MAINTAIN);\n\t\tif (aclresult != ACLCHECK_OK)\n\t\t\taclcheck_error(aclresult, OBJECT_INDEX, relation->relname);\n\t}\n\t#else\n\tif (!object_ownercheck(RelationRelationId, relId, GetUserId()))\n\t\taclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_INDEX, relation->relname);\n\t#endif\n\n\t/* Lock heap before index to avoid deadlock. */\n\tif (relId != oldRelId)\n\t{\n\t\ttable_oid = IndexGetRelation(relId, true);\n\n\t\t/*\n\t\t * If the OID isn't valid, it means the index was concurrently\n\t\t * dropped, which is not a problem for us; just return normally.\n\t\t */\n\t\tif (OidIsValid(table_oid))\n\t\t{\n\t\t\tLockRelationOid(table_oid, table_lockmode);\n\t\t\tstate->locked_table_oid = table_oid;\n\t\t}\n\t}\n\t/* *INDENT-ON* */\n}\n\n\n/*\n * ErrorIfUnsupportedIndexStmt checks if the corresponding index statement is\n * supported for distributed tables and errors out if it is not.\n */\nstatic void\nErrorIfUnsupportedIndexStmt(IndexStmt *createIndexStatement)\n{\n\tif (createIndexStatement->tableSpace != NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"specifying tablespaces with CREATE INDEX statements is \"\n\t\t\t\t\t\t\t   \"currently unsupported\")));\n\t}\n\n\tif (createIndexStatement->unique)\n\t{\n\t\tRangeVar *relation = createIndexStatement->relation;\n\t\tbool missingOk = false;\n\n\t\t/* caller uses ShareLock for non-concurrent indexes, use the same lock here */\n\t\tLOCKMODE lockMode = ShareLock;\n\t\tOid relationId = RangeVarGetRelid(relation, lockMode, missingOk);\n\t\tbool indexContainsPartitionColumn = false;\n\n\t\t/*\n\t\t * Non-distributed tables do not have partition key, and unique constraints\n\t\t * are allowed for them. Thus, we added a short-circuit for non-distributed tables.\n\t\t */\n\t\tif (!HasDistributionKey(relationId))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tif (IsCitusTableType(relationId, APPEND_DISTRIBUTED))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"creating unique indexes on append-partitioned tables \"\n\t\t\t\t\t\t\t\t   \"is currently unsupported\")));\n\t\t}\n\n\t\tif (AllowUnsafeConstraints)\n\t\t{\n\t\t\t/*\n\t\t\t * The user explicitly wants to allow the constraint without\n\t\t\t * distribution column.\n\t\t\t */\n\t\t\treturn;\n\t\t}\n\n\t\tVar *partitionKey = DistPartitionKeyOrError(relationId);\n\t\tList *indexParameterList = createIndexStatement->indexParams;\n\t\tIndexElem *indexElement = NULL;\n\t\tforeach_declared_ptr(indexElement, indexParameterList)\n\t\t{\n\t\t\tconst char *columnName = indexElement->name;\n\n\t\t\t/* column name is null for index expressions, skip it */\n\t\t\tif (columnName == NULL)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tAttrNumber attributeNumber = get_attnum(relationId, columnName);\n\t\t\tif (attributeNumber == partitionKey->varattno)\n\t\t\t{\n\t\t\t\tindexContainsPartitionColumn = true;\n\t\t\t}\n\t\t}\n\n\t\tif (!indexContainsPartitionColumn)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"creating unique indexes on non-partition \"\n\t\t\t\t\t\t\t\t   \"columns is currently unsupported\")));\n\t\t}\n\t}\n}\n\n\n/*\n * ErrorIfUnsupportedDropIndexStmt checks if the corresponding drop index statement is\n * supported for distributed tables and errors out if it is not.\n */\nstatic void\nErrorIfUnsupportedDropIndexStmt(DropStmt *dropIndexStatement)\n{\n\tAssert(dropIndexStatement->removeType == OBJECT_INDEX);\n\n\tif (list_length(dropIndexStatement->objects) > 1)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot drop multiple distributed objects in a \"\n\t\t\t\t\t\t\t   \"single command\"),\n\t\t\t\t\t\terrhint(\"Try dropping each object in a separate DROP \"\n\t\t\t\t\t\t\t\t\"command.\")));\n\t}\n}\n\n\n/*\n * DropIndexTaskList builds a list of tasks to execute a DROP INDEX command\n * against a specified distributed table.\n */\nstatic List *\nDropIndexTaskList(Oid relationId, Oid indexId, DropStmt *dropStmt)\n{\n\tList *taskList = NIL;\n\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\tchar *indexName = get_rel_name(indexId);\n\tOid schemaId = get_rel_namespace(indexId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\tStringInfoData ddlString;\n\tuint64 jobId = INVALID_JOB_ID;\n\tint taskId = 1;\n\n\tinitStringInfo(&ddlString);\n\n\t/* lock metadata before getting placement lists */\n\tLockShardListMetadata(shardIntervalList, ShareLock);\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\t\tchar *shardIndexName = pstrdup(indexName);\n\n\t\tAppendShardIdToName(&shardIndexName, shardId);\n\n\t\t/* deparse shard-specific DROP INDEX command */\n\t\tappendStringInfo(&ddlString, \"DROP INDEX %s %s %s %s\",\n\t\t\t\t\t\t (dropStmt->concurrent ? \"CONCURRENTLY\" : \"\"),\n\t\t\t\t\t\t (dropStmt->missing_ok ? \"IF EXISTS\" : \"\"),\n\t\t\t\t\t\t quote_qualified_identifier(schemaName, shardIndexName),\n\t\t\t\t\t\t (dropStmt->behavior == DROP_RESTRICT ? \"RESTRICT\" : \"CASCADE\"));\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->jobId = jobId;\n\t\ttask->taskId = taskId++;\n\t\ttask->taskType = DDL_TASK;\n\t\tSetTaskQueryString(task, pstrdup(ddlString.data));\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\ttask->dependentTaskList = NULL;\n\t\ttask->anchorShardId = shardId;\n\t\ttask->taskPlacementList = ActiveShardPlacementList(shardId);\n\t\ttask->cannotBeExecutedInTransaction = dropStmt->concurrent;\n\n\t\ttaskList = lappend(taskList, task);\n\n\t\tresetStringInfo(&ddlString);\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * MarkIndexValid marks an index as valid after a CONCURRENTLY command. We mark\n * indexes invalid in PostProcessIndexStmt and then commit, such that any failure\n * leaves behind an invalid index. We mark it as valid here when the command\n * completes.\n */\nvoid\nMarkIndexValid(IndexStmt *indexStmt)\n{\n\tAssert(indexStmt->concurrent);\n\tAssert(IsCoordinator());\n\n\t/*\n\t * We make sure schema name is not null in the PreprocessIndexStmt\n\t */\n\tbool missingOk = false;\n\tOid schemaId = get_namespace_oid(indexStmt->relation->schemaname, missingOk);\n\n\tOid relationId PG_USED_FOR_ASSERTS_ONLY =\n\t\tget_relname_relid(indexStmt->relation->relname, schemaId);\n\n\tAssert(IsCitusTable(relationId));\n\n\t/* get the affected relation and index */\n\tRelation relation = table_openrv(indexStmt->relation, ShareUpdateExclusiveLock);\n\tOid indexRelationId = get_relname_relid(indexStmt->idxname,\n\t\t\t\t\t\t\t\t\t\t\tschemaId);\n\tRelation indexRelation = index_open(indexRelationId, RowExclusiveLock);\n\n\tPushActiveSnapshot(GetTransactionSnapshot());\n\n\t/* mark index as valid, in-place (cannot be rolled back) */\n\tindex_set_state_flags(indexRelationId, INDEX_CREATE_SET_VALID);\n\tPopActiveSnapshot();\n\n\ttable_close(relation, NoLock);\n\tindex_close(indexRelation, NoLock);\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/index_pg_source.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * index_pg_source.c\n *    Helper functions copy & pasted from PostgreSQL source code.\n *    All the functions in this file is copied from\n *    postgres/src/backend/commands/indexcmds.c\n *\n * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group\n * Portions Copyright (c) 1994, Regents of the University of California\n *\n *\n * IDENTIFICATION\n *\t  src/backend/distributed/commands/index_pg_source.c\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"commands/defrem.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/listutils.h\"\n#include \"mb/pg_wchar.h\"\n\n\n/* *INDENT-OFF* */\n\n/*\n * This function is copy & paste from Postgres source code:\n * \t\tpostgres/src/backend/commands/indexcmds.c\n *\n * Select the name to be used for an index.\n *\n * The argument list is pretty ad-hoc :-(\n */\nchar *\nChooseIndexName(const char *tabname, Oid namespaceId,\n\t\t\t\tList *colnames, List *exclusionOpNames,\n\t\t\t\tbool primary, bool isconstraint)\n{\n\tchar *indexname;\n\n\tif (primary)\n\t{\n\t\t/* the primary key's name does not depend on the specific column(s) */\n\t\tindexname = ChooseRelationName(tabname,\n\t\t\t\t\t\t\t\t\t   NULL,\n\t\t\t\t\t\t\t\t\t   \"pkey\",\n\t\t\t\t\t\t\t\t\t   namespaceId,\n\t\t\t\t\t\t\t\t\t   true);\n\t}\n\telse if (exclusionOpNames != NIL)\n\t{\n\t\tindexname = ChooseRelationName(tabname,\n\t\t\t\t\t\t\t\t\t   ChooseIndexNameAddition(colnames),\n\t\t\t\t\t\t\t\t\t   \"excl\",\n\t\t\t\t\t\t\t\t\t   namespaceId,\n\t\t\t\t\t\t\t\t\t   true);\n\t}\n\telse if (isconstraint)\n\t{\n\t\tindexname = ChooseRelationName(tabname,\n\t\t\t\t\t\t\t\t\t   ChooseIndexNameAddition(colnames),\n\t\t\t\t\t\t\t\t\t   \"key\",\n\t\t\t\t\t\t\t\t\t   namespaceId,\n\t\t\t\t\t\t\t\t\t   true);\n\t}\n\telse\n\t{\n\t\tindexname = ChooseRelationName(tabname,\n\t\t\t\t\t\t\t\t\t   ChooseIndexNameAddition(colnames),\n\t\t\t\t\t\t\t\t\t   \"idx\",\n\t\t\t\t\t\t\t\t\t   namespaceId,\n\t\t\t\t\t\t\t\t\t   false);\n\t}\n\n\treturn indexname;\n}\n\n\n/*\n * This function is copy & paste from Postgres source code:\n * \t\tpostgres/src/backend/commands/indexcmds.c\n *\n * Generate \"name2\" for a new index given the list of column names for it\n * (as produced by ChooseIndexColumnNames).  This will be passed to\n * ChooseRelationName along with the parent table name and a suitable label.\n *\n * We know that less than NAMEDATALEN characters will actually be used,\n * so we can truncate the result once we've generated that many.\n *\n * XXX See also ChooseForeignKeyConstraintNameAddition and\n * ChooseExtendedStatisticNameAddition.\n */\nchar *\nChooseIndexNameAddition(List *colnames)\n{\n\tchar buf[NAMEDATALEN * 2];\n\tint buflen = 0;\n\tListCell *lc;\n\n\tbuf[0] = '\\0';\n\tforeach(lc, colnames)\n\t{\n\t\tconst char *name = (const char *) lfirst(lc);\n\n\t\tif (buflen > 0)\n\t\t{\n\t\t\tbuf[buflen++] = '_';    /* insert _ between names */\n\t\t}\n\n\t\t/*\n\t\t * At this point we have buflen <= NAMEDATALEN.  name should be less\n\t\t * than NAMEDATALEN already, but use strlcpy for paranoia.\n\t\t */\n\t\tstrlcpy(buf + buflen, name, NAMEDATALEN);\n\t\tbuflen += strlen(buf + buflen);\n\t\tif (buflen >= NAMEDATALEN)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn pstrdup(buf);\n}\n\n\n/*\n *  * This function is copy & paste from Postgres source code:\n * \t\tpostgres/src/backend/commands/indexcmds.c\n *\n * Select the actual names to be used for the columns of an index, given the\n * list of IndexElems for the columns.  This is mostly about ensuring the\n * names are unique so we don't get a conflicting-attribute-names error.\n *\n * Returns a List of plain strings (char *, not String nodes).\n */\nList *\nChooseIndexColumnNames(List *indexElems)\n{\n\tList *result = NIL;\n\tListCell *lc;\n\n\tforeach(lc, indexElems)\n\t{\n\t\tIndexElem *ielem = (IndexElem *) lfirst(lc);\n\t\tconst char *origname;\n\t\tconst char *curname;\n\t\tint i;\n\t\tchar buf[NAMEDATALEN];\n\n\t\t/* Get the preliminary name from the IndexElem */\n\t\tif (ielem->indexcolname)\n\t\t{\n\t\t\torigname = ielem->indexcolname; /* caller-specified name */\n\t\t}\n\t\telse if (ielem->name)\n\t\t{\n\t\t\torigname = ielem->name; /* simple column reference */\n\t\t}\n\t\telse\n\t\t{\n\t\t\torigname = \"expr\";  /* default name for expression */\n\t\t}\n\n\t\t/* If it conflicts with any previous column, tweak it */\n\t\tcurname = origname;\n\t\tfor (i = 1;; i++)\n\t\t{\n\t\t\tListCell *lc2;\n\t\t\tchar nbuf[32];\n\t\t\tint nlen;\n\n\t\t\tforeach(lc2, result)\n\t\t\t{\n\t\t\t\tif (strcmp(curname, (char *) lfirst(lc2)) == 0)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (lc2 == NULL)\n\t\t\t{\n\t\t\t\tbreak;          /* found nonconflicting name */\n\t\t\t}\n\t\t\tsprintf(nbuf, \"%d\", i); /* lgtm[cpp/banned-api-usage-required-any] */\n\n\t\t\t/* Ensure generated names are shorter than NAMEDATALEN */\n\t\t\tnlen = pg_mbcliplen(origname, strlen(origname),\n\t\t\t\t\t\t\t\tNAMEDATALEN - 1 - strlen(nbuf));\n\t\t\tmemcpy(buf, origname, nlen); /* lgtm[cpp/banned-api-usage-required-any] */\n\t\t\tstrcpy(buf + nlen, nbuf); /* lgtm[cpp/banned-api-usage-required-any] */\n\t\t\tcurname = buf;\n\t\t}\n\n\t\t/* And attach to the result list */\n\t\tresult = lappend(result, pstrdup(curname));\n\t}\n\treturn result;\n}\n\n/* *INDENT-ON* */\n"
  },
  {
    "path": "src/backend/distributed/commands/local_multi_copy.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * local_multi_copy.c\n *    Commands for running a copy locally\n *\n * For each local placement, we have a buffer. When we receive a slot\n * from a copy, the slot will be put to the corresponding buffer based\n * on the shard id. When the buffer size exceeds the threshold a local\n * copy will be done. Also If we reach to the end of copy, we will send\n * the current buffer for local copy.\n *\n * The existing logic from multi_copy.c and format are used, therefore\n * even if user did not do a copy with binary format, it is possible that\n * we are going to be using binary format internally.\n *\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <netinet/in.h> /* for htons */\n\n#include \"postgres.h\"\n\n#include \"safe_lib.h\"\n\n#include \"catalog/namespace.h\"\n#include \"commands/copy.h\"\n#include \"nodes/makefuncs.h\"\n#include \"parser/parse_relation.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/local_multi_copy.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/replication_origin_session_utils.h\"\n#include \"distributed/shard_utils.h\"\n#include \"distributed/transmit.h\"\n#include \"distributed/version_compat.h\"\n\n/* managed via GUC, default is 512 kB */\nint LocalCopyFlushThresholdByte = 512 * 1024;\n\n\nstatic void AddSlotToBuffer(TupleTableSlot *slot, CitusCopyDestReceiver *copyDest,\n\t\t\t\t\t\t\tCopyOutState localCopyOutState);\nstatic bool ShouldAddBinaryHeaders(StringInfo buffer, bool isBinary);\nstatic bool ShouldSendCopyNow(StringInfo buffer);\nstatic void DoLocalCopy(StringInfo buffer, Oid relationId, int64 shardId,\n\t\t\t\t\t\tCopyStmt *copyStatement, bool isEndOfCopy, bool isPublishable);\nstatic int ReadFromLocalBufferCallback(void *outBuf, int minRead, int maxRead);\n\n\n/*\n * LocalCopyBuffer is used in copy callback to return the copied rows.\n * The reason this is a global variable is that we cannot pass an additional\n * argument to the copy callback.\n */\nstatic StringInfo LocalCopyBuffer;\n\n\n/*\n * WriteTupleToLocalShard adds the given slot and does a local copy if\n * this is the end of copy, or the buffer size exceeds the threshold.\n */\nvoid\nWriteTupleToLocalShard(TupleTableSlot *slot, CitusCopyDestReceiver *copyDest, int64\n\t\t\t\t\t   shardId,\n\t\t\t\t\t   CopyOutState localCopyOutState)\n{\n\t/*\n\t * Since we are doing a local copy, the following statements should\n\t * use local execution to see the changes\n\t */\n\tSetLocalExecutionStatus(LOCAL_EXECUTION_REQUIRED);\n\n\tbool isBinaryCopy = localCopyOutState->binary;\n\tif (ShouldAddBinaryHeaders(localCopyOutState->fe_msgbuf, isBinaryCopy))\n\t{\n\t\tAppendCopyBinaryHeaders(localCopyOutState);\n\t}\n\n\tAddSlotToBuffer(slot, copyDest, localCopyOutState);\n\n\tif (ShouldSendCopyNow(localCopyOutState->fe_msgbuf))\n\t{\n\t\tif (isBinaryCopy)\n\t\t{\n\t\t\t/*\n\t\t\t * We're going to flush the buffer to disk by effectively doing a full\n\t\t\t * COPY command. Hence we also need to add footers to the current buffer.\n\t\t\t */\n\t\t\tAppendCopyBinaryFooters(localCopyOutState);\n\t\t}\n\t\tbool isEndOfCopy = false;\n\t\tDoLocalCopy(localCopyOutState->fe_msgbuf, copyDest->distributedRelationId,\n\t\t\t\t\tshardId,\n\t\t\t\t\tcopyDest->copyStatement, isEndOfCopy, copyDest->isPublishable);\n\t\tresetStringInfo(localCopyOutState->fe_msgbuf);\n\t}\n}\n\n\n/*\n * WriteTupleToLocalFile adds the given slot and does a local copy to the\n * file if the buffer size exceeds the threshold.\n */\nvoid\nWriteTupleToLocalFile(TupleTableSlot *slot, CitusCopyDestReceiver *copyDest,\n\t\t\t\t\t  int64 shardId, CopyOutState localFileCopyOutState,\n\t\t\t\t\t  FileCompat *fileCompat)\n{\n\tAddSlotToBuffer(slot, copyDest, localFileCopyOutState);\n\n\tif (ShouldSendCopyNow(localFileCopyOutState->fe_msgbuf))\n\t{\n\t\tWriteToLocalFile(localFileCopyOutState->fe_msgbuf, fileCompat);\n\t\tresetStringInfo(localFileCopyOutState->fe_msgbuf);\n\t}\n}\n\n\n/*\n * FinishLocalCopyToShard finishes local copy for the given shard with the shard id.\n */\nvoid\nFinishLocalCopyToShard(CitusCopyDestReceiver *copyDest, int64 shardId,\n\t\t\t\t\t   CopyOutState localCopyOutState)\n{\n\tbool isBinaryCopy = localCopyOutState->binary;\n\tif (isBinaryCopy)\n\t{\n\t\tAppendCopyBinaryFooters(localCopyOutState);\n\t}\n\tbool isEndOfCopy = true;\n\tDoLocalCopy(localCopyOutState->fe_msgbuf, copyDest->distributedRelationId, shardId,\n\t\t\t\tcopyDest->copyStatement, isEndOfCopy, copyDest->isPublishable);\n}\n\n\n/*\n * FinishLocalCopyToFile finishes local copy for the given file.\n */\nvoid\nFinishLocalCopyToFile(CopyOutState localFileCopyOutState, FileCompat *fileCompat)\n{\n\tStringInfo data = localFileCopyOutState->fe_msgbuf;\n\n\tbool isBinaryCopy = localFileCopyOutState->binary;\n\tif (isBinaryCopy)\n\t{\n\t\tAppendCopyBinaryFooters(localFileCopyOutState);\n\t}\n\tWriteToLocalFile(data, fileCompat);\n\tresetStringInfo(localFileCopyOutState->fe_msgbuf);\n\n\tFileClose(fileCompat->fd);\n}\n\n\n/*\n * AddSlotToBuffer serializes the given slot and adds it to\n * the buffer in localCopyOutState.\n */\nstatic void\nAddSlotToBuffer(TupleTableSlot *slot, CitusCopyDestReceiver *copyDest, CopyOutState\n\t\t\t\tlocalCopyOutState)\n{\n\tDatum *columnValues = slot->tts_values;\n\tbool *columnNulls = slot->tts_isnull;\n\tFmgrInfo *columnOutputFunctions = copyDest->columnOutputFunctions;\n\tCopyCoercionData *columnCoercionPaths = copyDest->columnCoercionPaths;\n\n\tAppendCopyRowData(columnValues, columnNulls, copyDest->tupleDescriptor,\n\t\t\t\t\t  localCopyOutState, columnOutputFunctions,\n\t\t\t\t\t  columnCoercionPaths);\n}\n\n\n/*\n * ShouldSendCopyNow returns true if the given buffer size exceeds the\n * local copy buffer size threshold.\n */\nstatic bool\nShouldSendCopyNow(StringInfo buffer)\n{\n\t/* LocalCopyFlushThreshold is in bytes */\n\treturn buffer->len > LocalCopyFlushThresholdByte;\n}\n\n\n/*\n * DoLocalCopy finds the shard table from the distributed relation id, and copies the given\n * buffer into the shard.\n * CopyFrom calls ReadFromLocalBufferCallback to read bytes from the buffer\n * as though it was reading from stdin. It then parses the tuples and\n * writes them to the shardOid table.\n */\nstatic void\nDoLocalCopy(StringInfo buffer, Oid relationId, int64 shardId, CopyStmt *copyStatement,\n\t\t\tbool isEndOfCopy, bool isPublishable)\n{\n\t/*\n\t * Set the buffer as a global variable to allow ReadFromLocalBufferCallback\n\t * to read from it. We cannot pass additional arguments to\n\t * ReadFromLocalBufferCallback.\n\t */\n\tLocalCopyBuffer = buffer;\n\tif (!isPublishable)\n\t{\n\t\tSetupReplicationOriginLocalSession();\n\t}\n\n\tOid shardOid = GetTableLocalShardOid(relationId, shardId);\n\tRelation shard = table_open(shardOid, RowExclusiveLock);\n\tParseState *pState = make_parsestate(NULL);\n\t(void) addRangeTableEntryForRelation(pState, shard, AccessShareLock,\n\t\t\t\t\t\t\t\t\t\t NULL, false, false);\n\tCopyFromState cstate = BeginCopyFrom(pState, shard, NULL, NULL, false,\n\t\t\t\t\t\t\t\t\t\t ReadFromLocalBufferCallback,\n\t\t\t\t\t\t\t\t\t\t copyStatement->attlist,\n\t\t\t\t\t\t\t\t\t\t copyStatement->options);\n\tCopyFrom(cstate);\n\tEndCopyFrom(cstate);\n\n\ttable_close(shard, NoLock);\n\tif (!isPublishable)\n\t{\n\t\tResetReplicationOriginLocalSession();\n\t}\n\tfree_parsestate(pState);\n}\n\n\n/*\n * ShouldAddBinaryHeaders returns true if the given buffer\n * is empty and the format is binary.\n */\nstatic bool\nShouldAddBinaryHeaders(StringInfo buffer, bool isBinary)\n{\n\tif (!isBinary)\n\t{\n\t\treturn false;\n\t}\n\treturn buffer->len == 0;\n}\n\n\n/*\n * ReadFromLocalBufferCallback is the copy callback.\n * It always tries to copy maxRead bytes.\n */\nstatic int\nReadFromLocalBufferCallback(void *outBuf, int minRead, int maxRead)\n{\n\tint bytesRead = 0;\n\tint avail = LocalCopyBuffer->len - LocalCopyBuffer->cursor;\n\tint bytesToRead = Min(avail, maxRead);\n\tif (bytesToRead > 0)\n\t{\n\t\tmemcpy_s(outBuf, bytesToRead,\n\t\t\t\t &LocalCopyBuffer->data[LocalCopyBuffer->cursor], bytesToRead);\n\t}\n\tbytesRead += bytesToRead;\n\tLocalCopyBuffer->cursor += bytesToRead;\n\n\treturn bytesRead;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/multi_copy.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_copy.c\n *     This file contains implementation of COPY utility for distributed\n *     tables.\n *\n * The CitusCopyFrom function should be called from the utility hook to process\n * COPY ... FROM commands on distributed tables. CitusCopyFrom parses the input\n * from stdin, a program, or a file, and decides to copy new rows to existing\n * shards or new shards based on the partition method of the distributed table.\n *\n * If this is the first command in the transaction, we open a new connection for\n * every shard placement. Otherwise we open as many connections as we can to\n * not conflict with previous commands in transactions, in which case some shards\n * may share connections. See the comments of CopyConnectionState for how we\n * operate in that case.\n *\n * We use the PQputCopyData function to copy the data. Because PQputCopyData\n * transmits data asynchronously, the workers will ingest data at least partially\n * in parallel.\n *\n * For hash-partitioned tables, if it fails to connect to a worker, the master\n * rollbacks the distributed transaction, similar to the way DML statements\n * are handled. If a failure occurs after connecting, the transaction\n * is rolled back on all the workers. Note that,\n * in the case of append-partitioned tables, if a fail occurs, immediately\n * metadata changes are rolled back on the master node, but shard placements\n * are left on the worker nodes.\n *\n * By default, COPY uses normal transactions on the workers. In the case of\n * hash or range-partitioned tables, this can cause a problem when some of the\n * transactions fail to commit while others have succeeded. To ensure no data\n * is lost, COPY uses two-phase commit.\n *\n * Parsing options are processed and enforced on the node where copy command\n * is run, while constraints are enforced on the worker. In either case,\n * failure causes the whole COPY to roll back.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * With contributions from Postgres Professional.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <arpa/inet.h> /* for htons */\n#include <netinet/in.h> /* for htons */\n#include <string.h>\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"access/htup.h\"\n#include \"access/htup_details.h\"\n#include \"access/sdir.h\"\n#include \"access/sysattr.h\"\n#include \"access/xact.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_attribute.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/copy.h\"\n#include \"commands/defrem.h\"\n#include \"commands/progress.h\"\n#include \"executor/executor.h\"\n#include \"foreign/foreign.h\"\n#include \"libpq/libpq.h\"\n#include \"libpq/pqformat.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"parser/parse_func.h\"\n#include \"parser/parse_type.h\"\n#include \"tcop/cmdtag.h\"\n#include \"tsearch/ts_locale.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/local_multi_copy.h\"\n#include \"distributed/locally_reserved_shared_connections.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/relation_utils.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/remote_transaction.h\"\n#include \"distributed/replication_origin_session_utils.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/shared_connection_stats.h\"\n#include \"distributed/stats/stat_counters.h\"\n#include \"distributed/transmit.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/* constant used in binary protocol */\nstatic const char BinarySignature[11] = \"PGCOPY\\n\\377\\r\\n\\0\";\n\n/* if true, skip validation of JSONB columns during COPY */\nbool SkipJsonbValidationInCopy = true;\n\n/* custom Citus option for appending to a shard */\n#define APPEND_TO_SHARD_OPTION \"append_to_shard\"\n\n/*\n * Data size threshold to switch over the active placement for a connection.\n * If this is too low, overhead of starting COPY commands will hurt the\n * performance. If this is too high, buffered data will use lots of memory.\n * 4MB is a good balance between memory usage and performance. Note that this\n * is irrelevant in the common case where we open one connection per placement.\n */\nint CopySwitchOverThresholdBytes = 4 * 1024 * 1024;\n\n#define FILE_IS_OPEN(x) (x > -1)\n\ntypedef struct CopyShardState CopyShardState;\ntypedef struct CopyPlacementState CopyPlacementState;\n\n/*\n * Multiple shard placements can share one connection. Each connection has one\n * of those placements as the activePlacementState, and others in the\n * bufferedPlacementList. When we want to send a tuple to a CopyPlacementState,\n * we check if it is the active one in its connectionState, and in this case we\n * directly put data on wire. Otherwise, we buffer it so we can put it on wire\n * later, when copy ends or a switch-over happens. See CitusSendTupleToPlacements()\n * for more details.\n *\n * This is done so we are compatible with adaptive_executor. If a previous command\n * in the current transaction has been executed using adaptive_executor.c, then\n * CopyGetPlacementConnection() might return the same connection for multiple\n * placements. We support that case by the buffering mechanism described above.\n *\n * If no previous command in the current transaction has used adaptive_executor.c,\n * then CopyGetPlacementConnection() returns one connection per placement and no\n * buffering happens and we put the copy data directly on connection.\n */\ntypedef struct CopyConnectionState\n{\n\t/* Used as hash key. Equal to PQsocket(connection->pgConn). */\n\tint socket;\n\n\tMultiConnection *connection;\n\n\t/*\n\t * Placement for which we have an active COPY going on over connection.\n\t * Can be NULL.\n\t */\n\tCopyPlacementState *activePlacementState;\n\n\t/*\n\t * Other placements that we are buffering data for. Later when a switch-over\n\t * happens, we remove an item from this list and set it to activePlacementState.\n\t * In this case, old activePlacementState isn't NULL, is added to this list.\n\t */\n\tdlist_head bufferedPlacementList;\n\n\t/* length of bufferedPlacementList, to avoid iterations over the list when needed */\n\tint bufferedPlacementCount;\n} CopyConnectionState;\n\n\nstruct CopyPlacementState\n{\n\t/* Connection state to which the placemement is assigned to. */\n\tCopyConnectionState *connectionState;\n\n\t/* State of shard to which the placement belongs to. */\n\tCopyShardState *shardState;\n\n\t/* node group ID of the placement */\n\tint32 groupId;\n\n\t/*\n\t * Buffered COPY data. When the placement is activePlacementState of\n\t * some connection, this is empty. Because in that case we directly\n\t * send the data over connection.\n\t */\n\tStringInfo data;\n\n\t/* List node for CopyConnectionState->bufferedPlacementList. */\n\tdlist_node bufferedPlacementNode;\n};\n\nstruct CopyShardState\n{\n\t/* Used as hash key. */\n\tuint64 shardId;\n\n\t/* used for doing local copy, either for a shard or a co-located file */\n\tCopyOutState copyOutState;\n\n\t/* used when copy is targeting co-located file */\n\tFileCompat fileDest;\n\n\t/* containsLocalPlacement is true if we have a local placement for the shard id of this state */\n\tbool containsLocalPlacement;\n\n\t/* List of CopyPlacementStates for all active placements of the shard. */\n\tList *placementStateList;\n};\n\n\n/*\n * Represents the state for allowing copy via local\n * execution.\n */\ntypedef enum LocalCopyStatus\n{\n\tLOCAL_COPY_REQUIRED,\n\tLOCAL_COPY_OPTIONAL,\n\tLOCAL_COPY_DISABLED\n} LocalCopyStatus;\n\n\n/* Local functions forward declarations */\nstatic void CopyToExistingShards(CopyStmt *copyStatement,\n\t\t\t\t\t\t\t\t QueryCompletion *completionTag);\nstatic bool IsCopyInBinaryFormat(CopyStmt *copyStatement);\nstatic List * FindJsonbInputColumns(TupleDesc tupleDescriptor,\n\t\t\t\t\t\t\t\t\tList *inputColumnNameList);\nstatic List * RemoveOptionFromList(List *optionList, char *optionName);\nstatic bool BinaryOutputFunctionDefined(Oid typeId);\nstatic bool BinaryInputFunctionDefined(Oid typeId);\nstatic void SendCopyBinaryHeaders(CopyOutState copyOutState, int64 shardId,\n\t\t\t\t\t\t\t\t  List *connectionList);\nstatic void SendCopyBinaryFooters(CopyOutState copyOutState, int64 shardId,\n\t\t\t\t\t\t\t\t  List *connectionList);\nstatic StringInfo ConstructCopyStatement(CopyStmt *copyStatement, int64 shardId);\nstatic void SendCopyDataToAll(StringInfo dataBuffer, int64 shardId, List *connectionList);\nstatic void SendCopyDataToPlacement(StringInfo dataBuffer, int64 shardId,\n\t\t\t\t\t\t\t\t\tMultiConnection *connection);\nstatic uint32 AvailableColumnCount(TupleDesc tupleDescriptor);\n\nstatic Oid TypeForColumnName(Oid relationId, TupleDesc tupleDescriptor, char *columnName);\nstatic Oid * TypeArrayFromTupleDescriptor(TupleDesc tupleDescriptor);\nstatic CopyCoercionData * ColumnCoercionPaths(TupleDesc destTupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t  TupleDesc inputTupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t  Oid destRelId, List *columnNameList,\n\t\t\t\t\t\t\t\t\t\t\t  Oid *finalColumnTypeArray);\nstatic FmgrInfo * TypeOutputFunctions(uint32 columnCount, Oid *typeIdArray,\n\t\t\t\t\t\t\t\t\t  bool binaryFormat);\nstatic bool CopyStatementHasFormat(CopyStmt *copyStatement, char *formatName);\nstatic void CitusCopyFrom(CopyStmt *copyStatement, QueryCompletion *completionTag);\nstatic void EnsureCopyCanRunOnRelation(Oid relationId);\nstatic HTAB * CreateConnectionStateHash(MemoryContext memoryContext);\nstatic HTAB * CreateShardStateHash(MemoryContext memoryContext);\nstatic CopyConnectionState * GetConnectionState(HTAB *connectionStateHash,\n\t\t\t\t\t\t\t\t\t\t\t\tMultiConnection *connection);\nstatic CopyShardState * GetShardState(uint64 shardId, HTAB *shardStateHash,\n\t\t\t\t\t\t\t\t\t  HTAB *connectionStateHash,\n\t\t\t\t\t\t\t\t\t  bool *found, bool shouldUseLocalCopy, CopyOutState\n\t\t\t\t\t\t\t\t\t  copyOutState, bool isColocatedIntermediateResult,\n\t\t\t\t\t\t\t\t\t  bool isPublishable);\nstatic MultiConnection * CopyGetPlacementConnection(HTAB *connectionStateHash,\n\t\t\t\t\t\t\t\t\t\t\t\t\tShardPlacement *placement,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbool colocatedIntermediateResult);\nstatic bool HasReachedAdaptiveExecutorPoolSize(List *connectionStateHash);\nstatic MultiConnection * GetLeastUtilisedCopyConnection(List *connectionStateList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar *nodeName, int nodePort);\nstatic List * ConnectionStateList(HTAB *connectionStateHash);\nstatic List * ConnectionStateListToNode(HTAB *connectionStateHash,\n\t\t\t\t\t\t\t\t\t\tconst char *hostname, int32 port);\nstatic void InitializeCopyShardState(CopyShardState *shardState,\n\t\t\t\t\t\t\t\t\t HTAB *connectionStateHash,\n\t\t\t\t\t\t\t\t\t uint64 shardId,\n\t\t\t\t\t\t\t\t\t bool canUseLocalCopy,\n\t\t\t\t\t\t\t\t\t CopyOutState copyOutState,\n\t\t\t\t\t\t\t\t\t bool colocatedIntermediateResult, bool\n\t\t\t\t\t\t\t\t\t isPublishable);\nstatic void StartPlacementStateCopyCommand(CopyPlacementState *placementState,\n\t\t\t\t\t\t\t\t\t\t   CopyStmt *copyStatement,\n\t\t\t\t\t\t\t\t\t\t   CopyOutState copyOutState);\nstatic void EndPlacementStateCopyCommand(CopyPlacementState *placementState,\n\t\t\t\t\t\t\t\t\t\t CopyOutState copyOutState);\nstatic void UnclaimCopyConnections(List *connectionStateList);\nstatic void ShutdownCopyConnectionState(CopyConnectionState *connectionState,\n\t\t\t\t\t\t\t\t\t\tCitusCopyDestReceiver *copyDest);\nstatic SelectStmt * CitusCopySelect(CopyStmt *copyStatement);\nstatic void CitusCopyTo(CopyStmt *copyStatement, QueryCompletion *completionTag);\nstatic int64 ForwardCopyDataFromConnection(CopyOutState copyOutState,\n\t\t\t\t\t\t\t\t\t\t   MultiConnection *connection);\nstatic void ErrorIfCopyHasOnErrorLogVerbosity(CopyStmt *copyStatement);\n\n/* Private functions copied and adapted from copy.c in PostgreSQL */\nstatic void SendCopyBegin(CopyOutState cstate);\nstatic void SendCopyEnd(CopyOutState cstate);\nstatic void CopySendData(CopyOutState outputState, const void *databuf, int datasize);\nstatic void CopySendString(CopyOutState outputState, const char *str);\nstatic void CopySendChar(CopyOutState outputState, char c);\nstatic void CopySendInt32(CopyOutState outputState, int32 val);\nstatic void CopySendInt16(CopyOutState outputState, int16 val);\nstatic void CopySendEndOfRow(CopyOutState cstate, bool includeEndOfLine);\nstatic void CopyAttributeOutText(CopyOutState outputState, char *string);\nstatic inline void CopyFlushOutput(CopyOutState outputState, char *start, char *pointer);\nstatic bool CitusSendTupleToPlacements(TupleTableSlot *slot,\n\t\t\t\t\t\t\t\t\t   CitusCopyDestReceiver *copyDest);\nstatic void AddPlacementStateToCopyConnectionStateBuffer(CopyConnectionState *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t connectionState,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t CopyPlacementState *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t placementState);\nstatic void RemovePlacementStateFromCopyConnectionStateBuffer(CopyConnectionState *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  connectionState,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  CopyPlacementState *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  placementState);\nstatic uint64 ProcessAppendToShardOption(Oid relationId, CopyStmt *copyStatement);\nstatic uint64 ShardIdForTuple(CitusCopyDestReceiver *copyDest, Datum *columnValues,\n\t\t\t\t\t\t\t  bool *columnNulls);\n\n/* CitusCopyDestReceiver functions */\nstatic void CitusCopyDestReceiverStartup(DestReceiver *copyDest, int operation,\n\t\t\t\t\t\t\t\t\t\t TupleDesc inputTupleDesc);\nstatic bool CitusCopyDestReceiverReceive(TupleTableSlot *slot,\n\t\t\t\t\t\t\t\t\t\t DestReceiver *copyDest);\nstatic void CitusCopyDestReceiverShutdown(DestReceiver *destReceiver);\nstatic void CitusCopyDestReceiverDestroy(DestReceiver *destReceiver);\nstatic bool ContainsLocalPlacement(int64 shardId);\nstatic void CompleteCopyQueryTagCompat(QueryCompletion *completionTag, uint64\n\t\t\t\t\t\t\t\t\t   processedRowCount);\nstatic void FinishLocalCopy(CitusCopyDestReceiver *copyDest);\nstatic void CreateLocalColocatedIntermediateFile(CitusCopyDestReceiver *copyDest,\n\t\t\t\t\t\t\t\t\t\t\t\t CopyShardState *shardState);\nstatic void FinishLocalColocatedIntermediateFiles(CitusCopyDestReceiver *copyDest);\nstatic void CloneCopyOutStateForLocalCopy(CopyOutState from, CopyOutState to);\nstatic LocalCopyStatus GetLocalCopyStatus(void);\nstatic bool ShardIntervalListHasLocalPlacements(List *shardIntervalList);\nstatic void LogLocalCopyToRelationExecution(uint64 shardId);\nstatic void LogLocalCopyToFileExecution(uint64 shardId);\nstatic void ErrorIfMergeInCopy(CopyStmt *copyStatement);\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(citus_text_send_as_jsonb);\n\n\n/*\n * CitusCopyFrom implements the COPY table_name FROM. It dispacthes the copy\n * statement to related subfunctions based on where the copy command is run\n * and the partition method of the distributed table.\n */\nstatic void\nCitusCopyFrom(CopyStmt *copyStatement, QueryCompletion *completionTag)\n{\n\tUseCoordinatedTransaction();\n\n\t/* disallow COPY to/from file or program except for superusers */\n\tif (copyStatement->filename != NULL && !superuser())\n\t{\n\t\tif (copyStatement->is_program)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),\n\t\t\t\t\t errmsg(\"must be superuser to COPY to or from an external program\"),\n\t\t\t\t\t errhint(\"Anyone can COPY to stdout or from stdin. \"\n\t\t\t\t\t\t\t \"psql's \\\\copy command also works for anyone.\")));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),\n\t\t\t\t\t errmsg(\"must be superuser to COPY to or from a file\"),\n\t\t\t\t\t errhint(\"Anyone can COPY to stdout or from stdin. \"\n\t\t\t\t\t\t\t \"psql's \\\\copy command also works for anyone.\")));\n\t\t}\n\t}\n\n\n\tOid relationId = RangeVarGetRelid(copyStatement->relation, NoLock, false);\n\n\tEnsureCopyCanRunOnRelation(relationId);\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\n\t/* disallow modifications to a partition table which have rep. factor > 1 */\n\tEnsurePartitionTableNotReplicated(relationId);\n\n\tif (IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED) ||\n\t\tIsCitusTableTypeCacheEntry(cacheEntry, RANGE_DISTRIBUTED) ||\n\t\tIsCitusTableTypeCacheEntry(cacheEntry, APPEND_DISTRIBUTED) ||\n\t\t!HasDistributionKeyCacheEntry(cacheEntry))\n\t{\n\t\tCopyToExistingShards(copyStatement, completionTag);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"unsupported partition method\")));\n\t}\n\n\tXactModificationLevel = XACT_MODIFICATION_DATA;\n}\n\n\n/*\n * EnsureCopyCanRunOnRelation throws error is the database in read-only mode.\n */\nstatic void\nEnsureCopyCanRunOnRelation(Oid relationId)\n{\n\t/* first, do the regular check and give consistent errors with regular queries */\n\tEnsureModificationsCanRunOnRelation(relationId);\n\n\t/*\n\t * We use 2PC for all COPY commands. It means that we cannot allow any COPY\n\t * on replicas even if the user allows via WritableStandbyCoordinator GUC.\n\t */\n\tif (RecoveryInProgress() && WritableStandbyCoordinator)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),\n\t\t\t\t\t\terrmsg(\"COPY command to Citus tables is not allowed in \"\n\t\t\t\t\t\t\t   \"read-only mode\"),\n\t\t\t\t\t\terrhint(\"All COPY commands to citus tables happen via 2PC, \"\n\t\t\t\t\t\t\t\t\"and 2PC requires the database to be in a writable state.\"),\n\t\t\t\t\t\terrdetail(\"the database is read-only\")));\n\t}\n}\n\n\n/*\n * CopyToExistingShards implements the COPY table_name FROM ... for hash or\n * range-partitioned tables where there are already shards into which to copy\n * rows.\n */\nstatic void\nCopyToExistingShards(CopyStmt *copyStatement, QueryCompletion *completionTag)\n{\n\tOid tableId = RangeVarGetRelid(copyStatement->relation, NoLock, false);\n\n\n\tList *columnNameList = NIL;\n\tint partitionColumnIndex = INVALID_PARTITION_COLUMN_INDEX;\n\n\tbool isInputFormatBinary = IsCopyInBinaryFormat(copyStatement);\n\tuint64 processedRowCount = 0;\n\n\tErrorContextCallback errorCallback;\n\n\t/* allocate column values and nulls arrays */\n\tRelation distributedRelation = table_open(tableId, RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(distributedRelation);\n\tuint32 columnCount = tupleDescriptor->natts;\n\tDatum *columnValues = palloc0(columnCount * sizeof(Datum));\n\tbool *columnNulls = palloc0(columnCount * sizeof(bool));\n\n\t/* set up a virtual tuple table slot */\n\tTupleTableSlot *tupleTableSlot = MakeSingleTupleTableSlot(tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &TTSOpsVirtual);\n\ttupleTableSlot->tts_nvalid = columnCount;\n\ttupleTableSlot->tts_values = columnValues;\n\ttupleTableSlot->tts_isnull = columnNulls;\n\n\t/* determine the partition column index in the tuple descriptor */\n\tVar *partitionColumn = PartitionColumn(tableId, 0);\n\tif (partitionColumn != NULL)\n\t{\n\t\tpartitionColumnIndex = partitionColumn->varattno - 1;\n\t}\n\n\t/* build the list of column names for remote COPY statements */\n\tfor (int columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tForm_pg_attribute currentColumn = TupleDescAttr(tupleDescriptor, columnIndex);\n\t\tchar *columnName = NameStr(currentColumn->attname);\n\n\t\tif (IsDroppedOrGenerated(currentColumn))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tcolumnNameList = lappend(columnNameList, columnName);\n\t}\n\n\tEState *executorState = CreateExecutorState();\n\tMemoryContext executorTupleContext = GetPerTupleMemoryContext(executorState);\n\tExprContext *executorExpressionContext = GetPerTupleExprContext(executorState);\n\n\t/* set up the destination for the COPY */\n\tconst bool publishableData = true;\n\n\t/* we want to track query counters for \"COPY (to) distributed-table ..\" commands */\n\tconst bool trackQueryCounters = true;\n\tCitusCopyDestReceiver *copyDest = CreateCitusCopyDestReceiver(tableId, columnNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  executorState, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  publishableData,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  trackQueryCounters);\n\n\t/* if the user specified an explicit append-to_shard option, write to it */\n\tuint64 appendShardId = ProcessAppendToShardOption(tableId, copyStatement);\n\tif (appendShardId != INVALID_SHARD_ID)\n\t{\n\t\tcopyDest->appendShardId = appendShardId;\n\t}\n\n\tDestReceiver *dest = (DestReceiver *) copyDest;\n\tdest->rStartup(dest, 0, tupleDescriptor);\n\n\t/*\n\t * Below, we change a few fields in the Relation to control the behaviour\n\t * of BeginCopyFrom. However, we obviously should not do this in relcache\n\t * and therefore make a copy of the Relation.\n\t */\n\tRelation copiedDistributedRelation = (Relation) palloc(sizeof(RelationData));\n\tForm_pg_class copiedDistributedRelationTuple =\n\t\t(Form_pg_class) palloc(CLASS_TUPLE_SIZE);\n\n\t/*\n\t * There is no need to deep copy everything. We will just deep copy of the fields\n\t * we will change.\n\t */\n\t*copiedDistributedRelation = *distributedRelation;\n\t*copiedDistributedRelationTuple = *distributedRelation->rd_rel;\n\n\tcopiedDistributedRelation->rd_rel = copiedDistributedRelationTuple;\n\tcopiedDistributedRelation->rd_att = CreateTupleDescCopyConstr(tupleDescriptor);\n\n\t/*\n\t * BeginCopyFrom opens all partitions of given partitioned table with relation_open\n\t * and it expects its caller to close those relations. We do not have direct access\n\t * to opened relations, thus we are changing relkind of partitioned tables so that\n\t * Postgres will treat those tables as regular relations and will not open its\n\t * partitions.\n\t */\n\tif (PartitionedTable(tableId))\n\t{\n\t\tcopiedDistributedRelationTuple->relkind = RELKIND_RELATION;\n\t}\n\n\t/*\n\t * We make an optimisation to skip JSON parsing for JSONB columns, because many\n\t * Citus users have large objects in this column and parsing it on the coordinator\n\t * causes significant CPU overhead. We do this by forcing BeginCopyFrom and\n\t * NextCopyFrom to parse the column as text and then encoding it as JSON again\n\t * by using citus_text_send_as_jsonb as the binary output function.\n\t *\n\t * The main downside of enabling this optimisation is that it defers validation\n\t * until the object is parsed by the worker, which is unable to give an accurate\n\t * line number.\n\t */\n\tif (SkipJsonbValidationInCopy && !isInputFormatBinary)\n\t{\n\t\tCopyOutState copyOutState = copyDest->copyOutState;\n\t\tListCell *jsonbColumnIndexCell = NULL;\n\n\t\t/* get the column indices for all JSONB columns that appear in the input */\n\t\tList *jsonbColumnIndexList = FindJsonbInputColumns(\n\t\t\tcopiedDistributedRelation->rd_att,\n\t\t\tcopyStatement->attlist);\n\n\t\tforeach(jsonbColumnIndexCell, jsonbColumnIndexList)\n\t\t{\n\t\t\tint jsonbColumnIndex = lfirst_int(jsonbColumnIndexCell);\n\t\t\tForm_pg_attribute currentColumn =\n\t\t\t\tTupleDescAttr(copiedDistributedRelation->rd_att, jsonbColumnIndex);\n\n\t\t\tif (jsonbColumnIndex == partitionColumnIndex)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * In the curious case of using a JSONB column as partition column,\n\t\t\t\t * we leave it as is because we want to make sure the hashing works\n\t\t\t\t * correctly.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tereport(DEBUG1, (errmsg(\"parsing JSONB column %s as text\",\n\t\t\t\t\t\t\t\t\tNameStr(currentColumn->attname))));\n\n\t\t\t/* parse the column as text instead of JSONB */\n\t\t\tcurrentColumn->atttypid = TEXTOID;\n\n\t\t\tif (copyOutState->binary)\n\t\t\t{\n\t\t\t\tOid textSendAsJsonbFunctionId = CitusTextSendAsJsonbFunctionId();\n\n\t\t\t\t/*\n\t\t\t\t * If we're using binary encoding between coordinator and workers\n\t\t\t\t * then we should honour the format expected by jsonb_recv, which\n\t\t\t\t * is a version number followed by text. We therefore use an output\n\t\t\t\t * function which sends the text as if it were jsonb, namely by\n\t\t\t\t * prepending a version number.\n\t\t\t\t */\n\t\t\t\tfmgr_info(textSendAsJsonbFunctionId,\n\t\t\t\t\t\t  &copyDest->columnOutputFunctions[jsonbColumnIndex]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tOid textoutFunctionId = TextOutFunctionId();\n\t\t\t\tfmgr_info(textoutFunctionId,\n\t\t\t\t\t\t  &copyDest->columnOutputFunctions[jsonbColumnIndex]);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* initialize copy state to read from COPY data source */\n\tCopyFromState copyState = BeginCopyFrom(NULL,\n\t\t\t\t\t\t\t\t\t\t\tcopiedDistributedRelation,\n\t\t\t\t\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\t\t\t\t\tcopyStatement->filename,\n\t\t\t\t\t\t\t\t\t\t\tcopyStatement->is_program,\n\t\t\t\t\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\t\t\t\t\tcopyStatement->attlist,\n\t\t\t\t\t\t\t\t\t\t\tcopyStatement->options);\n\n\t/* set up callback to identify error line number */\n\terrorCallback.callback = CopyFromErrorCallback;\n\terrorCallback.arg = (void *) copyState;\n\terrorCallback.previous = error_context_stack;\n\terror_context_stack = &errorCallback;\n\n\twhile (true)\n\t{\n\t\tResetPerTupleExprContext(executorState);\n\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(executorTupleContext);\n\n\t\t/* parse a row from the input */\n\t\tbool nextRowFound = NextCopyFrom(copyState, executorExpressionContext,\n\t\t\t\t\t\t\t\t\t\t columnValues, columnNulls);\n\n\t\tif (!nextRowFound)\n\t\t{\n\t\t\tMemoryContextSwitchTo(oldContext);\n\t\t\tbreak;\n\t\t}\n\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tMemoryContextSwitchTo(oldContext);\n\n\t\tdest->receiveSlot(tupleTableSlot, dest);\n\n\t\t++processedRowCount;\n\n\t\tpgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, processedRowCount);\n\t}\n\n\tEndCopyFrom(copyState);\n\n\t/* all lines have been copied, stop showing line number in errors */\n\terror_context_stack = errorCallback.previous;\n\n\t/* finish the COPY commands */\n\tdest->rShutdown(dest);\n\tdest->rDestroy(dest);\n\n\tExecDropSingleTupleTableSlot(tupleTableSlot);\n\tFreeExecutorState(executorState);\n\ttable_close(distributedRelation, NoLock);\n\n\tCHECK_FOR_INTERRUPTS();\n\n\tif (completionTag != NULL)\n\t{\n\t\tCompleteCopyQueryTagCompat(completionTag, processedRowCount);\n\t}\n}\n\n\n/*\n * IsCopyInBinaryFormat determines whether the given COPY statement has the\n * WITH (format binary) option.\n */\nstatic bool\nIsCopyInBinaryFormat(CopyStmt *copyStatement)\n{\n\tListCell *optionCell = NULL;\n\n\tforeach(optionCell, copyStatement->options)\n\t{\n\t\tDefElem *defel = lfirst_node(DefElem, optionCell);\n\t\tif (strcmp(defel->defname, \"format\") == 0 &&\n\t\t\tstrcmp(defGetString(defel), \"binary\") == 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * FindJsonbInputColumns finds columns in the tuple descriptor that have\n * the JSONB type and appear in inputColumnNameList. If the list is empty then\n * all JSONB columns are returned.\n */\nstatic List *\nFindJsonbInputColumns(TupleDesc tupleDescriptor, List *inputColumnNameList)\n{\n\tList *jsonbColumnIndexList = NIL;\n\tint columnCount = tupleDescriptor->natts;\n\n\tfor (int columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tForm_pg_attribute currentColumn = TupleDescAttr(tupleDescriptor, columnIndex);\n\t\tif (currentColumn->attisdropped)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (currentColumn->atttypid != JSONBOID)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (inputColumnNameList != NIL)\n\t\t{\n\t\t\tListCell *inputColumnCell = NULL;\n\t\t\tbool isInputColumn = false;\n\n\t\t\tforeach(inputColumnCell, inputColumnNameList)\n\t\t\t{\n\t\t\t\tchar *inputColumnName = strVal(lfirst(inputColumnCell));\n\n\t\t\t\tif (namestrcmp(&currentColumn->attname, inputColumnName) == 0)\n\t\t\t\t{\n\t\t\t\t\tisInputColumn = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!isInputColumn)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tjsonbColumnIndexList = lappend_int(jsonbColumnIndexList, columnIndex);\n\t}\n\n\treturn jsonbColumnIndexList;\n}\n\n\nstatic void\nCompleteCopyQueryTagCompat(QueryCompletion *completionTag, uint64 processedRowCount)\n{\n\tSetQueryCompletion(completionTag, CMDTAG_COPY, processedRowCount);\n}\n\n\n/*\n * RemoveOptionFromList removes an option from a list of options in a\n * COPY .. WITH (..) statement by name and returns the resulting list.\n */\nstatic List *\nRemoveOptionFromList(List *optionList, char *optionName)\n{\n\tListCell *optionCell = NULL;\n\tforeach(optionCell, optionList)\n\t{\n\t\tDefElem *option = (DefElem *) lfirst(optionCell);\n\n\t\tif (strncmp(option->defname, optionName, NAMEDATALEN) == 0)\n\t\t{\n\t\t\treturn list_delete_cell(optionList, optionCell);\n\t\t}\n\t}\n\n\treturn optionList;\n}\n\n\n/*\n * CanUseBinaryCopyFormat iterates over columns of the relation and looks for a\n * column whose type is array of user-defined type or composite type. If it finds\n * such column, that means we cannot use binary format for COPY, because binary\n * format sends Oid of the types, which are generally not same in master and\n * worker nodes for user-defined types. If the function can not detect a binary\n * output function for any of the column, it returns false.\n */\nbool\nCanUseBinaryCopyFormat(TupleDesc tupleDescription)\n{\n\tbool useBinaryCopyFormat = true;\n\tint totalColumnCount = tupleDescription->natts;\n\n\tfor (int columnIndex = 0; columnIndex < totalColumnCount; columnIndex++)\n\t{\n\t\tForm_pg_attribute currentColumn = TupleDescAttr(tupleDescription, columnIndex);\n\n\t\tif (IsDroppedOrGenerated(currentColumn))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tOid typeId = currentColumn->atttypid;\n\t\tif (!CanUseBinaryCopyFormatForType(typeId))\n\t\t{\n\t\t\tuseBinaryCopyFormat = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn useBinaryCopyFormat;\n}\n\n\n/*\n * CanUseBinaryCopyFormatForTargetList returns true if we can use binary\n * copy format for all columns of the given target list.\n */\nbool\nCanUseBinaryCopyFormatForTargetList(List *targetEntryList)\n{\n\tListCell *targetEntryCell = NULL;\n\tforeach(targetEntryCell, targetEntryList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell);\n\t\tNode *targetExpr = (Node *) targetEntry->expr;\n\n\t\tOid columnType = exprType(targetExpr);\n\t\tif (!CanUseBinaryCopyFormatForType(columnType))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * CanUseBinaryCopyFormatForType determines whether it is safe to use the\n * binary copy format for the given type. See contents of the function for\n * details of when it's safe to use binary copy.\n */\nbool\nCanUseBinaryCopyFormatForType(Oid typeId)\n{\n\tif (!BinaryOutputFunctionDefined(typeId))\n\t{\n\t\treturn false;\n\t}\n\n\tif (!BinaryInputFunctionDefined(typeId))\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * A row type can contain any types, possibly types that don't have\n\t * the binary input and output functions defined.\n\t */\n\tif (type_is_rowtype(typeId))\n\t{\n\t\t/*\n\t\t * TODO: Inspect the types inside the record and check if all of them\n\t\t * can be binary encoded. If so, it's safe to use binary encoding.\n\t\t *\n\t\t * IMPORTANT: When implementing this todo keep the following in mind:\n\t\t *\n\t\t * In PG versions before PG14 the record_recv function would error out\n\t\t * more than necessary.\n\t\t *\n\t\t * It errors out when any of the columns in the row have a type oid\n\t\t * that doesn't match with the oid in the received data. This happens\n\t\t * pretty much always for non built in types, because their oids differ\n\t\t * between postgres intallations. So for those Postgres versions we\n\t\t * would need a check like the following for each column:\n\t\t *\n\t\t * if (columnType >= FirstNormalObjectId) {\n\t\t *     return false\n\t\t * }\n\t\t */\n\t\treturn false;\n\t}\n\n\tHeapTuple typeTup = typeidType(typeId);\n\tForm_pg_type type = (Form_pg_type) GETSTRUCT(typeTup);\n\tOid elementType = type->typelem;\n\tReleaseSysCache(typeTup);\n\n\t/*\n\t * Any type that is a wrapper around an element type (e.g. arrays and\n\t * ranges) require the element type to also has support for binary\n\t * encoding.\n\t */\n\tif (elementType != InvalidOid)\n\t{\n\t\tif (!CanUseBinaryCopyFormatForType(elementType))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/*\n\t * For domains, make sure that the underlying type can be binary copied.\n\t */\n\tOid baseTypeId = getBaseType(typeId);\n\tif (typeId != baseTypeId)\n\t{\n\t\tif (!CanUseBinaryCopyFormatForType(baseTypeId))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * BinaryOutputFunctionDefined checks whether binary output function is defined\n * for the given type.\n */\nstatic bool\nBinaryOutputFunctionDefined(Oid typeId)\n{\n\tOid typeFunctionId = InvalidOid;\n\tOid typeIoParam = InvalidOid;\n\tint16 typeLength = 0;\n\tbool typeByVal = false;\n\tchar typeAlign = 0;\n\tchar typeDelim = 0;\n\n\tget_type_io_data(typeId, IOFunc_send, &typeLength, &typeByVal,\n\t\t\t\t\t &typeAlign, &typeDelim, &typeIoParam, &typeFunctionId);\n\n\treturn OidIsValid(typeFunctionId);\n}\n\n\n/*\n * BinaryInputFunctionDefined checks whether binary output function is defined\n * for the given type.\n */\nstatic bool\nBinaryInputFunctionDefined(Oid typeId)\n{\n\tOid typeFunctionId = InvalidOid;\n\tOid typeIoParam = InvalidOid;\n\tint16 typeLength = 0;\n\tbool typeByVal = false;\n\tchar typeAlign = 0;\n\tchar typeDelim = 0;\n\n\tget_type_io_data(typeId, IOFunc_receive, &typeLength, &typeByVal,\n\t\t\t\t\t &typeAlign, &typeDelim, &typeIoParam, &typeFunctionId);\n\n\treturn OidIsValid(typeFunctionId);\n}\n\n\n/* Send copy binary headers to given connections */\nstatic void\nSendCopyBinaryHeaders(CopyOutState copyOutState, int64 shardId, List *connectionList)\n{\n\tresetStringInfo(copyOutState->fe_msgbuf);\n\tAppendCopyBinaryHeaders(copyOutState);\n\tSendCopyDataToAll(copyOutState->fe_msgbuf, shardId, connectionList);\n}\n\n\n/* Send copy binary footers to given connections */\nstatic void\nSendCopyBinaryFooters(CopyOutState copyOutState, int64 shardId, List *connectionList)\n{\n\tresetStringInfo(copyOutState->fe_msgbuf);\n\tAppendCopyBinaryFooters(copyOutState);\n\tSendCopyDataToAll(copyOutState->fe_msgbuf, shardId, connectionList);\n}\n\n\n/*\n * ConstructCopyStatement constructs the text of a COPY statement for a particular\n * shard.\n */\nstatic StringInfo\nConstructCopyStatement(CopyStmt *copyStatement, int64 shardId)\n{\n\tStringInfo command = makeStringInfo();\n\n\tchar *schemaName = copyStatement->relation->schemaname;\n\tchar *relationName = copyStatement->relation->relname;\n\n\tchar *shardName = pstrdup(relationName);\n\n\tAppendShardIdToName(&shardName, shardId);\n\n\tchar *shardQualifiedName = quote_qualified_identifier(schemaName, shardName);\n\n\tappendStringInfo(command, \"COPY %s \", shardQualifiedName);\n\n\tif (copyStatement->attlist != NIL)\n\t{\n\t\tListCell *columnNameCell = NULL;\n\t\tbool appendedFirstName = false;\n\n\t\tforeach(columnNameCell, copyStatement->attlist)\n\t\t{\n\t\t\tchar *columnName = strVal(lfirst(columnNameCell));\n\t\t\tconst char *quotedColumnName = quote_identifier(columnName);\n\n\t\t\tif (!appendedFirstName)\n\t\t\t{\n\t\t\t\tappendStringInfo(command, \"(%s\", quotedColumnName);\n\t\t\t\tappendedFirstName = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(command, \", %s\", quotedColumnName);\n\t\t\t}\n\t\t}\n\n\t\tappendStringInfoString(command, \") \");\n\t}\n\n\tif (copyStatement->is_from)\n\t{\n\t\tappendStringInfoString(command, \"FROM STDIN\");\n\t}\n\telse\n\t{\n\t\tappendStringInfoString(command, \"TO STDOUT\");\n\t}\n\n\tif (copyStatement->options != NIL)\n\t{\n\t\tListCell *optionCell = NULL;\n\n\t\tappendStringInfoString(command, \" WITH (\");\n\n\t\tforeach(optionCell, copyStatement->options)\n\t\t{\n\t\t\tDefElem *defel = (DefElem *) lfirst(optionCell);\n\n\t\t\tif (optionCell != list_head(copyStatement->options))\n\t\t\t{\n\t\t\t\tappendStringInfoString(command, \", \");\n\t\t\t}\n\n\t\t\tappendStringInfo(command, \"%s\", defel->defname);\n\n\t\t\tif (defel->arg == NULL)\n\t\t\t{\n\t\t\t\t/* option without value */\n\t\t\t}\n\t\t\telse if (IsA(defel->arg, String))\n\t\t\t{\n\t\t\t\tchar *value = defGetString(defel);\n\n\t\t\t\t/* make sure strings are quoted (may contain reserved characters) */\n\t\t\t\tappendStringInfo(command, \" %s\", quote_literal_cstr(value));\n\t\t\t}\n\t\t\telse if (IsA(defel->arg, List))\n\t\t\t{\n\t\t\t\tList *nameList = defGetStringList(defel);\n\n\t\t\t\tappendStringInfo(command, \" (%s)\", NameListToQuotedString(nameList));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar *value = defGetString(defel);\n\n\t\t\t\t/* numeric options or * should not have quotes */\n\t\t\t\tappendStringInfo(command, \" %s\", value);\n\t\t\t}\n\t\t}\n\n\t\tappendStringInfoString(command, \")\");\n\t}\n\n\treturn command;\n}\n\n\n/*\n * SendCopyDataToAll sends copy data to all connections in a list.\n */\nstatic void\nSendCopyDataToAll(StringInfo dataBuffer, int64 shardId, List *connectionList)\n{\n\tListCell *connectionCell = NULL;\n\tforeach(connectionCell, connectionList)\n\t{\n\t\tMultiConnection *connection = (MultiConnection *) lfirst(connectionCell);\n\t\tSendCopyDataToPlacement(dataBuffer, shardId, connection);\n\t}\n}\n\n\n/*\n * SendCopyDataToPlacement sends serialized COPY data to a specific shard placement\n * over the given connection.\n */\nstatic void\nSendCopyDataToPlacement(StringInfo dataBuffer, int64 shardId, MultiConnection *connection)\n{\n\tif (!PutRemoteCopyData(connection, dataBuffer->data, dataBuffer->len))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_IO_ERROR),\n\t\t\t\t\t\terrmsg(\"failed to COPY to shard \" INT64_FORMAT \" on %s:%d\",\n\t\t\t\t\t\t\t   shardId, connection->hostname, connection->port),\n\t\t\t\t\t\terrdetail(\"failed to send %d bytes %s\", dataBuffer->len,\n\t\t\t\t\t\t\t\t  dataBuffer->data)));\n\t}\n}\n\n\n/*\n * EndRemoteCopy ends the COPY input on all connections, and unclaims connections.\n * This reports an error on failure.\n */\nvoid\nEndRemoteCopy(int64 shardId, List *connectionList)\n{\n\tListCell *connectionCell = NULL;\n\n\tforeach(connectionCell, connectionList)\n\t{\n\t\tMultiConnection *connection = (MultiConnection *) lfirst(connectionCell);\n\t\tbool raiseInterrupts = true;\n\n\t\t/* end the COPY input */\n\t\tif (!PutRemoteCopyEnd(connection, NULL))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_IO_ERROR),\n\t\t\t\t\t\t\terrmsg(\"failed to COPY to shard \" INT64_FORMAT \" on %s:%d\",\n\t\t\t\t\t\t\t\t   shardId, connection->hostname, connection->port)));\n\t\t}\n\n\t\t/* check whether there were any COPY errors */\n\t\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\t\tif (PQresultStatus(result) != PGRES_COMMAND_OK)\n\t\t{\n\t\t\tReportCopyError(connection, result);\n\t\t}\n\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\t\tUnclaimConnection(connection);\n\t}\n}\n\n\n/*\n * ReportCopyError tries to report a useful error message for the user from\n * the remote COPY error messages.\n */\nvoid\nReportCopyError(MultiConnection *connection, PGresult *result)\n{\n\tchar *remoteMessage = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);\n\n\tif (remoteMessage != NULL)\n\t{\n\t\t/* probably a constraint violation, show remote message and detail */\n\t\tchar *remoteDetail = PQresultErrorField(result, PG_DIAG_MESSAGE_DETAIL);\n\t\tbool haveDetail = remoteDetail != NULL;\n\n\t\tereport(ERROR, (errmsg(\"%s\", remoteMessage),\n\t\t\t\t\t\thaveDetail ? errdetail(\"%s\", remoteDetail) :\n\t\t\t\t\t\t0));\n\t}\n\telse\n\t{\n\t\t/* trim the trailing characters */\n\t\tremoteMessage = pchomp(PQerrorMessage(connection->pgConn));\n\n\t\tereport(ERROR, (errcode(ERRCODE_IO_ERROR),\n\t\t\t\t\t\terrmsg(\"failed to complete COPY on %s:%d\", connection->hostname,\n\t\t\t\t\t\t\t   connection->port),\n\t\t\t\t\t\terrdetail(\"%s\", remoteMessage)));\n\t}\n}\n\n\n/*\n * ConversionPathForTypes fills *result with all the data necessary for converting\n * Datums of type inputType to Datums of type destType.\n */\nvoid\nConversionPathForTypes(Oid inputType, Oid destType, CopyCoercionData *result)\n{\n\tOid coercionFuncId = InvalidOid;\n\tCoercionPathType coercionType = COERCION_PATH_RELABELTYPE;\n\n\tif (destType == inputType)\n\t{\n\t\tresult->coercionType = COERCION_PATH_RELABELTYPE;\n\t\treturn;\n\t}\n\n\tcoercionType = find_coercion_pathway(destType, inputType,\n\t\t\t\t\t\t\t\t\t\t COERCION_EXPLICIT,\n\t\t\t\t\t\t\t\t\t\t &coercionFuncId);\n\n\tswitch (coercionType)\n\t{\n\t\tcase COERCION_PATH_NONE:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot cast %d to %d\", inputType, destType)));\n\t\t\treturn;\n\t\t}\n\n\t\tcase COERCION_PATH_ARRAYCOERCE:\n\t\t{\n\t\t\tOid inputBaseType = get_base_element_type(inputType);\n\t\t\tOid destBaseType = get_base_element_type(destType);\n\t\t\tCoercionPathType baseCoercionType = COERCION_PATH_NONE;\n\n\t\t\tif (inputBaseType != InvalidOid && destBaseType != InvalidOid)\n\t\t\t{\n\t\t\t\tbaseCoercionType = find_coercion_pathway(inputBaseType, destBaseType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t COERCION_EXPLICIT,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t &coercionFuncId);\n\t\t\t}\n\n\t\t\tif (baseCoercionType != COERCION_PATH_COERCEVIAIO)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"can not run query which uses an implicit coercion\"\n\t\t\t\t\t\t\t\t\t   \" between array types\")));\n\t\t\t}\n\t\t}\n\n\t\t/* fallthrough */\n\n\t\tcase COERCION_PATH_COERCEVIAIO:\n\t\t{\n\t\t\tresult->coercionType = COERCION_PATH_COERCEVIAIO;\n\n\t\t\t{\n\t\t\t\tbool typisvarlena = false; /* ignored */\n\t\t\t\tOid iofunc = InvalidOid;\n\t\t\t\tgetTypeOutputInfo(inputType, &iofunc, &typisvarlena);\n\t\t\t\tfmgr_info(iofunc, &(result->outputFunction));\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tOid iofunc = InvalidOid;\n\t\t\t\tgetTypeInputInfo(destType, &iofunc, &(result->typioparam));\n\t\t\t\tfmgr_info(iofunc, &(result->inputFunction));\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tcase COERCION_PATH_FUNC:\n\t\t{\n\t\t\tresult->coercionType = COERCION_PATH_FUNC;\n\t\t\tfmgr_info(coercionFuncId, &(result->coerceFunction));\n\t\t\treturn;\n\t\t}\n\n\t\tcase COERCION_PATH_RELABELTYPE:\n\t\t{\n\t\t\tresult->coercionType = COERCION_PATH_RELABELTYPE;\n\t\t\treturn; /* the types are binary compatible, no need to call a function */\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tAssert(false); /* there are no other options for this enum */\n\t\t}\n\t}\n}\n\n\n/*\n * Returns the type of the provided column of the provided tuple. Throws an error if the\n * column does not exist or is dropped.\n *\n * tupleDescriptor and relationId must refer to the same table.\n */\nstatic Oid\nTypeForColumnName(Oid relationId, TupleDesc tupleDescriptor, char *columnName)\n{\n\tAttrNumber destAttrNumber = get_attnum(relationId, columnName);\n\n\tif (destAttrNumber == InvalidAttrNumber)\n\t{\n\t\tereport(ERROR, (errmsg(\"invalid attr? %s\", columnName)));\n\t}\n\n\tForm_pg_attribute attr = TupleDescAttr(tupleDescriptor, destAttrNumber - 1);\n\treturn attr->atttypid;\n}\n\n\n/*\n * Walks a TupleDesc and returns an array of the types of each attribute.\n * Returns InvalidOid in the place of dropped or generated attributes.\n */\nstatic Oid *\nTypeArrayFromTupleDescriptor(TupleDesc tupleDescriptor)\n{\n\tint columnCount = tupleDescriptor->natts;\n\tOid *typeArray = palloc0(columnCount * sizeof(Oid));\n\n\tfor (int columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tForm_pg_attribute attr = TupleDescAttr(tupleDescriptor, columnIndex);\n\t\tif (IsDroppedOrGenerated(attr))\n\t\t{\n\t\t\ttypeArray[columnIndex] = InvalidOid;\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttypeArray[columnIndex] = attr->atttypid;\n\t\t}\n\t}\n\n\treturn typeArray;\n}\n\n\n/*\n * ColumnCoercionPaths scans the input and output tuples looking for mismatched types,\n * it then returns an array of coercion functions to use on the input tuples, and an\n * array of types which descript the output tuple\n */\nstatic CopyCoercionData *\nColumnCoercionPaths(TupleDesc destTupleDescriptor, TupleDesc inputTupleDescriptor,\n\t\t\t\t\tOid destRelId, List *columnNameList,\n\t\t\t\t\tOid *finalColumnTypeArray)\n{\n\tint columnCount = inputTupleDescriptor->natts;\n\tCopyCoercionData *coercePaths = palloc0(columnCount * sizeof(CopyCoercionData));\n\tOid *inputTupleTypes = TypeArrayFromTupleDescriptor(inputTupleDescriptor);\n\tListCell *currentColumnName = list_head(columnNameList);\n\n\tfor (int columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tOid inputTupleType = inputTupleTypes[columnIndex];\n\t\tchar *columnName = lfirst(currentColumnName);\n\n\t\tif (inputTupleType == InvalidOid)\n\t\t{\n\t\t\t/* TypeArrayFromTupleDescriptor decided to skip this column */\n\t\t\tcontinue;\n\t\t}\n\n\t\tOid destTupleType = TypeForColumnName(destRelId, destTupleDescriptor, columnName);\n\n\t\tfinalColumnTypeArray[columnIndex] = destTupleType;\n\n\t\tConversionPathForTypes(inputTupleType, destTupleType,\n\t\t\t\t\t\t\t   &coercePaths[columnIndex]);\n\n\t\tcurrentColumnName = lnext(columnNameList, currentColumnName);\n\n\t\tif (currentColumnName == NULL)\n\t\t{\n\t\t\t/* the rest of inputTupleDescriptor are dropped columns, return early! */\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn coercePaths;\n}\n\n\n/*\n * TypeOutputFunctions takes an array of types and returns an array of output functions\n * for those types.\n */\nstatic FmgrInfo *\nTypeOutputFunctions(uint32 columnCount, Oid *typeIdArray, bool binaryFormat)\n{\n\tFmgrInfo *columnOutputFunctions = palloc0(columnCount * sizeof(FmgrInfo));\n\n\tfor (uint32 columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tFmgrInfo *currentOutputFunction = &columnOutputFunctions[columnIndex];\n\t\tOid columnTypeId = typeIdArray[columnIndex];\n\t\tbool typeVariableLength = false;\n\t\tOid outputFunctionId = InvalidOid;\n\n\t\tif (columnTypeId == InvalidOid)\n\t\t{\n\t\t\t/* TypeArrayFromTupleDescriptor decided to skip this column */\n\t\t\tcontinue;\n\t\t}\n\t\telse if (binaryFormat)\n\t\t{\n\t\t\tgetTypeBinaryOutputInfo(columnTypeId, &outputFunctionId, &typeVariableLength);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tgetTypeOutputInfo(columnTypeId, &outputFunctionId, &typeVariableLength);\n\t\t}\n\n\t\tfmgr_info(outputFunctionId, currentOutputFunction);\n\t}\n\n\treturn columnOutputFunctions;\n}\n\n\n/*\n * ColumnOutputFunctions is a wrapper around TypeOutputFunctions, it takes a\n * tupleDescriptor and returns an array of output functions, one for each column in\n * the tuple.\n */\nFmgrInfo *\nColumnOutputFunctions(TupleDesc rowDescriptor, bool binaryFormat)\n{\n\tuint32 columnCount = (uint32) rowDescriptor->natts;\n\tOid *columnTypes = TypeArrayFromTupleDescriptor(rowDescriptor);\n\tFmgrInfo *outputFunctions =\n\t\tTypeOutputFunctions(columnCount, columnTypes, binaryFormat);\n\n\treturn outputFunctions;\n}\n\n\n/*\n * citus_text_send_as_jsonb sends a text as if it was a JSONB. This should only\n * be used if the text is indeed valid JSON.\n */\nDatum\ncitus_text_send_as_jsonb(PG_FUNCTION_ARGS)\n{\n\ttext *inputText = PG_GETARG_TEXT_PP(0);\n\tStringInfoData buf;\n\tint version = 1;\n\n\tpq_begintypsend(&buf);\n\tpq_sendint(&buf, version, 1);\n\tpq_sendtext(&buf, VARDATA_ANY(inputText), VARSIZE_ANY_EXHDR(inputText));\n\n\tPG_RETURN_BYTEA_P(pq_endtypsend(&buf));\n}\n\n\n/*\n * AppendCopyRowData serializes one row using the column output functions,\n * and appends the data to the row output state object's message buffer.\n * This function is modeled after the CopyOneRowTo() function in\n * commands/copy.c, but only implements a subset of that functionality.\n * Note that the caller of this function should reset row memory context\n * to not bloat memory usage.\n */\nvoid\nAppendCopyRowData(Datum *valueArray, bool *isNullArray, TupleDesc rowDescriptor,\n\t\t\t\t  CopyOutState rowOutputState, FmgrInfo *columnOutputFunctions,\n\t\t\t\t  CopyCoercionData *columnCoercionPaths)\n{\n\tuint32 totalColumnCount = (uint32) rowDescriptor->natts;\n\tuint32 availableColumnCount = AvailableColumnCount(rowDescriptor);\n\tuint32 appendedColumnCount = 0;\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(rowOutputState->rowcontext);\n\n\tif (rowOutputState->binary)\n\t{\n\t\tCopySendInt16(rowOutputState, availableColumnCount);\n\t}\n\tfor (uint32 columnIndex = 0; columnIndex < totalColumnCount; columnIndex++)\n\t{\n\t\tForm_pg_attribute currentColumn = TupleDescAttr(rowDescriptor, columnIndex);\n\t\tDatum value = valueArray[columnIndex];\n\t\tbool isNull = isNullArray[columnIndex];\n\t\tbool lastColumn = false;\n\n\t\tif (!isNull && columnCoercionPaths != NULL)\n\t\t{\n\t\t\tvalue = CoerceColumnValue(value, &columnCoercionPaths[columnIndex]);\n\t\t}\n\n\t\tif (IsDroppedOrGenerated(currentColumn))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\telse if (rowOutputState->binary)\n\t\t{\n\t\t\tif (!isNull)\n\t\t\t{\n\t\t\t\tFmgrInfo *outputFunctionPointer = &columnOutputFunctions[columnIndex];\n\t\t\t\tbytea *outputBytes = SendFunctionCall(outputFunctionPointer, value);\n\n\t\t\t\tCopySendInt32(rowOutputState, VARSIZE(outputBytes) - VARHDRSZ);\n\t\t\t\tCopySendData(rowOutputState, VARDATA(outputBytes),\n\t\t\t\t\t\t\t VARSIZE(outputBytes) - VARHDRSZ);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCopySendInt32(rowOutputState, -1);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (!isNull)\n\t\t\t{\n\t\t\t\tFmgrInfo *outputFunctionPointer = &columnOutputFunctions[columnIndex];\n\t\t\t\tchar *columnText = OutputFunctionCall(outputFunctionPointer, value);\n\n\t\t\t\tCopyAttributeOutText(rowOutputState, columnText);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCopySendString(rowOutputState, rowOutputState->null_print_client);\n\t\t\t}\n\n\t\t\tlastColumn = ((appendedColumnCount + 1) == availableColumnCount);\n\t\t\tif (!lastColumn)\n\t\t\t{\n\t\t\t\tCopySendChar(rowOutputState, rowOutputState->delim[0]);\n\t\t\t}\n\t\t}\n\n\t\tappendedColumnCount++;\n\t}\n\n\tif (!rowOutputState->binary)\n\t{\n\t\t/* append default line termination string depending on the platform */\n#ifndef WIN32\n\t\tCopySendChar(rowOutputState, '\\n');\n#else\n\t\tCopySendString(rowOutputState, \"\\r\\n\");\n#endif\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * CoerceColumnValue follows the instructions in *coercionPath and uses them to convert\n * inputValue into a Datum of the correct type.\n */\nDatum\nCoerceColumnValue(Datum inputValue, CopyCoercionData *coercionPath)\n{\n\tswitch (coercionPath->coercionType)\n\t{\n\t\tcase COERCION_PATH_NONE:\n\t\t{\n\t\t\treturn inputValue; /* this was a dropped column */\n\t\t}\n\n\t\tcase COERCION_PATH_RELABELTYPE:\n\t\t{\n\t\t\treturn inputValue; /* no need to do anything */\n\t\t}\n\n\t\tcase COERCION_PATH_FUNC:\n\t\t{\n\t\t\tFmgrInfo *coerceFunction = &(coercionPath->coerceFunction);\n\t\t\tDatum outputValue = FunctionCall1(coerceFunction, inputValue);\n\t\t\treturn outputValue;\n\t\t}\n\n\t\tcase COERCION_PATH_COERCEVIAIO:\n\t\t{\n\t\t\tFmgrInfo *outFunction = &(coercionPath->outputFunction);\n\t\t\tDatum textRepr = FunctionCall1(outFunction, inputValue);\n\n\t\t\tFmgrInfo *inFunction = &(coercionPath->inputFunction);\n\t\t\tOid typioparam = coercionPath->typioparam;\n\t\t\tDatum outputValue = FunctionCall3(inFunction, textRepr, typioparam,\n\t\t\t\t\t\t\t\t\t\t\t  Int32GetDatum(-1));\n\n\t\t\treturn outputValue;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\t/* this should never happen */\n\t\t\tereport(ERROR, (errmsg(\"unsupported coercion type\")));\n\t\t}\n\t}\n}\n\n\n/*\n * AvailableColumnCount returns the number of columns in a tuple descriptor, excluding\n * columns that were dropped.\n */\nstatic uint32\nAvailableColumnCount(TupleDesc tupleDescriptor)\n{\n\tuint32 columnCount = 0;\n\n\tfor (uint32 columnIndex = 0; columnIndex < tupleDescriptor->natts; columnIndex++)\n\t{\n\t\tForm_pg_attribute currentColumn = TupleDescAttr(tupleDescriptor, columnIndex);\n\n\t\tif (!IsDroppedOrGenerated(currentColumn))\n\t\t{\n\t\t\tcolumnCount++;\n\t\t}\n\t}\n\n\treturn columnCount;\n}\n\n\n/*\n * AppendCopyBinaryHeaders appends binary headers to the copy buffer in\n * headerOutputState.\n */\nvoid\nAppendCopyBinaryHeaders(CopyOutState headerOutputState)\n{\n\tconst int32 zero = 0;\n\tMemoryContext oldContext = MemoryContextSwitchTo(headerOutputState->rowcontext);\n\n\t/* Signature */\n\tCopySendData(headerOutputState, BinarySignature, 11);\n\n\t/* Flags field (no OIDs) */\n\tCopySendInt32(headerOutputState, zero);\n\n\t/* No header extension */\n\tCopySendInt32(headerOutputState, zero);\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * AppendCopyBinaryFooters appends binary footers to the copy buffer in\n * footerOutputState.\n */\nvoid\nAppendCopyBinaryFooters(CopyOutState footerOutputState)\n{\n\tint16 negative = -1;\n\tMemoryContext oldContext = MemoryContextSwitchTo(footerOutputState->rowcontext);\n\n\tCopySendInt16(footerOutputState, negative);\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/* *INDENT-OFF* */\n\n\n/*\n * Send copy start/stop messages for frontend copies.  These have changed\n * in past protocol redesigns.\n */\nstatic void\nSendCopyBegin(CopyOutState cstate)\n{\n\tStringInfoData buf;\n\tint\t\t\tnatts = list_length(cstate->attnumlist);\n\tint16\t\tformat = (cstate->binary ? 1 : 0);\n\tint\t\t\ti;\n\n\tpq_beginmessage(&buf, 'H');\n\tpq_sendbyte(&buf, format);\t/* overall format */\n\tpq_sendint16(&buf, natts);\n\tfor (i = 0; i < natts; i++)\n\t\tpq_sendint16(&buf, format); /* per-column formats */\n\tpq_endmessage(&buf);\n\tcstate->copy_dest = COPY_FRONTEND;\n}\n\n\n/* End a copy stream sent to the client */\nstatic void\nSendCopyEnd(CopyOutState cstate)\n{\n\t/* Shouldn't have any unsent data */\n\tAssert(cstate->fe_msgbuf->len == 0);\n\t/* Send Copy Done message */\n\tpq_putemptymessage('c');\n}\n\n\n/* Append data to the copy buffer in outputState */\nstatic void\nCopySendData(CopyOutState outputState, const void *databuf, int datasize)\n{\n\tappendBinaryStringInfo(outputState->fe_msgbuf, databuf, datasize);\n}\n\n\n/* Append a striong to the copy buffer in outputState. */\nstatic void\nCopySendString(CopyOutState outputState, const char *str)\n{\n\tappendBinaryStringInfo(outputState->fe_msgbuf, str, strlen(str));\n}\n\n\n/* Append a char to the copy buffer in outputState. */\nstatic void\nCopySendChar(CopyOutState outputState, char c)\n{\n\tappendStringInfoCharMacro(outputState->fe_msgbuf, c);\n}\n\n\n/* Append an int32 to the copy buffer in outputState. */\nstatic void\nCopySendInt32(CopyOutState outputState, int32 val)\n{\n\tuint32 buf = htonl((uint32) val);\n\tCopySendData(outputState, &buf, sizeof(buf));\n}\n\n\n/* Append an int16 to the copy buffer in outputState. */\nstatic void\nCopySendInt16(CopyOutState outputState, int16 val)\n{\n\tuint16 buf = htons((uint16) val);\n\tCopySendData(outputState, &buf, sizeof(buf));\n}\n\n\n/* Send the row to the appropriate destination */\nstatic void\nCopySendEndOfRow(CopyOutState cstate, bool includeEndOfLine)\n{\n\tStringInfo\tfe_msgbuf = cstate->fe_msgbuf;\n\n\tswitch (cstate->copy_dest)\n\t{\n\t\tcase COPY_FRONTEND:\n\t\t\t/* The FE/BE protocol uses \\n as newline for all platforms */\n\t\t\tif (!cstate->binary && includeEndOfLine)\n\t\t\t\tCopySendChar(cstate, '\\n');\n\n\t\t\t/* Dump the accumulated row as one CopyData message */\n\t\t\t(void) pq_putmessage('d', fe_msgbuf->data, fe_msgbuf->len);\n\t\t\tbreak;\n\t\tcase COPY_FILE:\n\t\tcase COPY_CALLBACK:\n\t\t\tAssert(false);\t\t/* Not yet supported. */\n\t\t\tbreak;\n\t}\n\n\tresetStringInfo(fe_msgbuf);\n}\n\n\n/*\n * Send text representation of one column, with conversion and escaping.\n *\n * NB: This function is based on commands/copy.c and doesn't fully conform to\n * our coding style. The function should be kept in sync with copy.c.\n */\nstatic void\nCopyAttributeOutText(CopyOutState cstate, char *string)\n{\n\tchar *pointer = NULL;\n\tchar c = '\\0';\n\tchar delimc = cstate->delim[0];\n\n\tif (cstate->need_transcoding)\n\t{\n\t\tpointer = pg_server_to_any(string, strlen(string), cstate->file_encoding);\n\t}\n\telse\n\t{\n\t\tpointer = string;\n\t}\n\n\t/*\n\t * We have to grovel through the string searching for control characters\n\t * and instances of the delimiter character.  In most cases, though, these\n\t * are infrequent.  To avoid overhead from calling CopySendData once per\n\t * character, we dump out all characters between escaped characters in a\n\t * single call.  The loop invariant is that the data from \"start\" to \"pointer\"\n\t * can be sent literally, but hasn't yet been.\n\t *\n\t * As all encodings here are safe, i.e. backend supported ones, we can\n\t * skip doing pg_encoding_mblen(), because in valid backend encodings,\n\t * extra bytes of a multibyte character never look like ASCII.\n\t */\n\tchar *start = pointer;\n\twhile ((c = *pointer) != '\\0')\n\t{\n\t\tif ((unsigned char) c < (unsigned char) 0x20)\n\t\t{\n\t\t\t/*\n\t\t\t * \\r and \\n must be escaped, the others are traditional. We\n\t\t\t * prefer to dump these using the C-like notation, rather than\n\t\t\t * a backslash and the literal character, because it makes the\n\t\t\t * dump file a bit more proof against Microsoftish data\n\t\t\t * mangling.\n\t\t\t */\n\t\t\tswitch (c)\n\t\t\t{\n\t\t\t\tcase '\\b':\n\t\t\t\t\tc = 'b';\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\f':\n\t\t\t\t\tc = 'f';\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\n':\n\t\t\t\t\tc = 'n';\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\r':\n\t\t\t\t\tc = 'r';\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\t':\n\t\t\t\t\tc = 't';\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\v':\n\t\t\t\t\tc = 'v';\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t/* If it's the delimiter, must backslash it */\n\t\t\t\t\tif (c == delimc)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t/* All ASCII control chars are length 1 */\n\t\t\t\t\tpointer++;\n\t\t\t\t\tcontinue;\t\t/* fall to end of loop */\n\t\t\t}\n\t\t\t/* if we get here, we need to convert the control char */\n\t\t\tCopyFlushOutput(cstate, start, pointer);\n\t\t\tCopySendChar(cstate, '\\\\');\n\t\t\tCopySendChar(cstate, c);\n\t\t\tstart = ++pointer;\t/* do not include char in next run */\n\t\t}\n\t\telse if (c == '\\\\' || c == delimc)\n\t\t{\n\t\t\tCopyFlushOutput(cstate, start, pointer);\n\t\t\tCopySendChar(cstate, '\\\\');\n\t\t\tstart = pointer++;\t/* we include char in next run */\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpointer++;\n\t\t}\n\t}\n\n\tCopyFlushOutput(cstate, start, pointer);\n}\n\n\n/* *INDENT-ON* */\n/* Helper function to send pending copy output */\nstatic inline void\nCopyFlushOutput(CopyOutState cstate, char *start, char *pointer)\n{\n\tif (pointer > start)\n\t{\n\t\tCopySendData(cstate, start, pointer - start);\n\t}\n}\n\n\n/*\n * CreateCitusCopyDestReceiver creates a DestReceiver that copies into\n * a distributed table.\n *\n * The caller should provide the list of column names to use in the\n * remote COPY statement, and the partition column index in the tuple\n * descriptor (*not* the column name list).\n *\n * If intermediateResultIdPrefix is not NULL, the COPY will go into a set\n * of intermediate results that are co-located with the actual table.\n * The names of the intermediate results with be of the form:\n * intermediateResultIdPrefix_<shardid>\n *\n * If trackQueryCounters is true, the COPY will increment the query stat\n * counters as needed at the end of the COPY.\n */\nCitusCopyDestReceiver *\nCreateCitusCopyDestReceiver(Oid tableId, List *columnNameList, int partitionColumnIndex,\n\t\t\t\t\t\t\tEState *executorState,\n\t\t\t\t\t\t\tchar *intermediateResultIdPrefix, bool isPublishable,\n\t\t\t\t\t\t\tbool trackQueryCounters)\n{\n\tCitusCopyDestReceiver *copyDest = (CitusCopyDestReceiver *) palloc0(\n\t\tsizeof(CitusCopyDestReceiver));\n\n\t/* set up the DestReceiver function pointers */\n\tcopyDest->pub.receiveSlot = CitusCopyDestReceiverReceive;\n\tcopyDest->pub.rStartup = CitusCopyDestReceiverStartup;\n\tcopyDest->pub.rShutdown = CitusCopyDestReceiverShutdown;\n\tcopyDest->pub.rDestroy = CitusCopyDestReceiverDestroy;\n\tcopyDest->pub.mydest = DestCopyOut;\n\n\t/* set up output parameters */\n\tcopyDest->distributedRelationId = tableId;\n\tcopyDest->columnNameList = columnNameList;\n\tcopyDest->partitionColumnIndex = partitionColumnIndex;\n\tcopyDest->executorState = executorState;\n\tcopyDest->colocatedIntermediateResultIdPrefix = intermediateResultIdPrefix;\n\tcopyDest->memoryContext = CurrentMemoryContext;\n\tcopyDest->isPublishable = isPublishable;\n\tcopyDest->trackQueryCounters = trackQueryCounters;\n\n\treturn copyDest;\n}\n\n\n/*\n * GetLocalCopyStatus returns the status for executing copy locally.\n * If LOCAL_COPY_DISABLED or LOCAL_COPY_REQUIRED, the caller has to\n * follow that. Else, the caller may decide to use local or remote\n * execution depending on other information.\n */\nstatic LocalCopyStatus\nGetLocalCopyStatus(void)\n{\n\tif (!EnableLocalExecution ||\n\t\tGetCurrentLocalExecutionStatus() == LOCAL_EXECUTION_DISABLED)\n\t{\n\t\treturn LOCAL_COPY_DISABLED;\n\t}\n\telse if (GetCurrentLocalExecutionStatus() == LOCAL_EXECUTION_REQUIRED)\n\t{\n\t\t/*\n\t\t * For various reasons, including the transaction visibility\n\t\t * rules (e.g., read-your-own-writes), we have to use local\n\t\t * execution again if it has already happened within this\n\t\t * transaction block.\n\t\t *\n\t\t * We might error out later in the execution if it is not suitable\n\t\t * to execute the tasks locally.\n\t\t */\n\t\tAssert(IsMultiStatementTransaction() || InCoordinatedTransaction());\n\n\t\t/*\n\t\t * TODO: A future improvement could be to keep track of which placements\n\t\t * have been locally executed. At this point, only use local execution for\n\t\t * those placements. That'd help to benefit more from parallelism.\n\t\t */\n\n\t\treturn LOCAL_COPY_REQUIRED;\n\t}\n\telse if (IsMultiStatementTransaction())\n\t{\n\t\treturn LOCAL_COPY_REQUIRED;\n\t}\n\n\treturn LOCAL_COPY_OPTIONAL;\n}\n\n\n/*\n * ShardIntervalListHasLocalPlacements returns true if any of the input\n * shard placement has a local placement;\n */\nstatic bool\nShardIntervalListHasLocalPlacements(List *shardIntervalList)\n{\n\tint32 localGroupId = GetLocalGroupId();\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tif (ActiveShardPlacementOnGroup(localGroupId, shardInterval->shardId) != NULL)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CitusCopyDestReceiverStartup implements the rStartup interface of\n * CitusCopyDestReceiver. It opens the relation, acquires necessary\n * locks, and initializes the state required for doing the copy.\n */\nstatic void\nCitusCopyDestReceiverStartup(DestReceiver *dest, int operation,\n\t\t\t\t\t\t\t TupleDesc inputTupleDescriptor)\n{\n\tCitusCopyDestReceiver *copyDest = (CitusCopyDestReceiver *) dest;\n\n\tOid tableId = copyDest->distributedRelationId;\n\n\tchar *relationName = get_rel_name(tableId);\n\tOid schemaOid = get_rel_namespace(tableId);\n\tchar *schemaName = get_namespace_name(schemaOid);\n\n\tList *columnNameList = copyDest->columnNameList;\n\tList *attributeList = NIL;\n\n\tListCell *columnNameCell = NULL;\n\n\tconst char *delimiterCharacter = \"\\t\";\n\tconst char *nullPrintCharacter = \"\\\\N\";\n\n\t/* look up table properties */\n\tRelation distributedRelation = table_open(tableId, RowExclusiveLock);\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(tableId);\n\n\tcopyDest->distributedRelation = distributedRelation;\n\tcopyDest->tupleDescriptor = inputTupleDescriptor;\n\n\t/* load the list of shards and verify that we have shards to copy into */\n\tList *shardIntervalList = LoadShardIntervalList(tableId);\n\tif (shardIntervalList == NIL)\n\t{\n\t\tif (IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"could not find any shards into which to copy\"),\n\t\t\t\t\t\t\terrdetail(\"No shards exist for distributed table \\\"%s\\\".\",\n\t\t\t\t\t\t\t\t\t  relationName),\n\t\t\t\t\t\t\terrhint(\"Run master_create_worker_shards to create shards \"\n\t\t\t\t\t\t\t\t\t\"and try again.\")));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"could not find any shards into which to copy\"),\n\t\t\t\t\t\t\terrdetail(\"No shards exist for distributed table \\\"%s\\\".\",\n\t\t\t\t\t\t\t\t\t  relationName)));\n\t\t}\n\t}\n\n\t/* error if any shard missing min/max values */\n\tif (cacheEntry->hasUninitializedShardInterval)\n\t{\n\t\tif (IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED) ||\n\t\t\tIsCitusTableTypeCacheEntry(cacheEntry, RANGE_DISTRIBUTED))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"could not start copy\"),\n\t\t\t\t\t\t\terrdetail(\"Distributed relation \\\"%s\\\" has shards \"\n\t\t\t\t\t\t\t\t\t  \"with missing shardminvalue/shardmaxvalue.\",\n\t\t\t\t\t\t\t\t\t  relationName)));\n\t\t}\n\t}\n\n\t/* prevent concurrent placement changes and non-commutative DML statements */\n\tLockShardListMetadata(shardIntervalList, ShareLock);\n\n\t/*\n\t * Prevent concurrent UPDATE/DELETE on replication factor >1\n\t * (see AcquireExecutorMultiShardLocks() at multi_router_executor.c)\n\t */\n\tSerializeNonCommutativeWrites(shardIntervalList, RowExclusiveLock);\n\n\tUseCoordinatedTransaction();\n\n\t/* all modifications use 2PC */\n\tUse2PCForCoordinatedTransaction();\n\n\t/* define how tuples will be serialised */\n\tCopyOutState copyOutState = (CopyOutState) palloc0(sizeof(CopyOutStateData));\n\tcopyOutState->delim = (char *) delimiterCharacter;\n\tcopyOutState->null_print = (char *) nullPrintCharacter;\n\tcopyOutState->null_print_client = (char *) nullPrintCharacter;\n\tcopyOutState->binary = CanUseBinaryCopyFormat(inputTupleDescriptor);\n\tcopyOutState->fe_msgbuf = makeStringInfo();\n\tcopyOutState->rowcontext = GetPerTupleMemoryContext(copyDest->executorState);\n\tcopyDest->copyOutState = copyOutState;\n\tcopyDest->multiShardCopy = false;\n\n\t/* prepare functions to call on received tuples */\n\t{\n\t\tTupleDesc destTupleDescriptor = distributedRelation->rd_att;\n\t\tint columnCount = inputTupleDescriptor->natts;\n\t\tOid *finalTypeArray = palloc0(columnCount * sizeof(Oid));\n\n\t\t/*\n\t\t * To ensure the proper co-location and distribution of the target table,\n\t\t * the entire process of repartitioning intermediate files requires the\n\t\t * destReceiver to be created on the target rather than the source.\n\t\t *\n\t\t * Within this specific code path, it is assumed that the employed model\n\t\t * is for insert-select. Consequently, it validates the column types of\n\t\t * destTupleDescriptor(target) during the intermediate result generation\n\t\t * process. However, this approach varies significantly for MERGE operations,\n\t\t * where the source tuple(s) can have arbitrary types and are not required to\n\t\t * align with the target column names.\n\t\t *\n\t\t * Despite this minor setback, a significant portion of the code responsible\n\t\t * for repartitioning intermediate files can be reused for the MERGE\n\t\t * operation. By leveraging the ability to perform actual coercion during\n\t\t * the writing process to the target table, we can bypass this specific route.\n\t\t */\n\t\tif (copyDest->skipCoercions)\n\t\t{\n\t\t\tcopyDest->columnOutputFunctions =\n\t\t\t\tColumnOutputFunctions(inputTupleDescriptor, copyOutState->binary);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcopyDest->columnCoercionPaths =\n\t\t\t\tColumnCoercionPaths(destTupleDescriptor, inputTupleDescriptor,\n\t\t\t\t\t\t\t\t\ttableId, columnNameList, finalTypeArray);\n\t\t\tcopyDest->columnOutputFunctions =\n\t\t\t\tTypeOutputFunctions(columnCount, finalTypeArray, copyOutState->binary);\n\t\t}\n\t}\n\n\t/* wrap the column names as Values */\n\tforeach(columnNameCell, columnNameList)\n\t{\n\t\tchar *columnName = (char *) lfirst(columnNameCell);\n\t\tString *columnNameValue = makeString(columnName);\n\n\t\tattributeList = lappend(attributeList, columnNameValue);\n\t}\n\n\tif (IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE) &&\n\t\t!IsCitusTableTypeCacheEntry(cacheEntry, SINGLE_SHARD_DISTRIBUTED) &&\n\t\tcopyDest->partitionColumnIndex == INVALID_PARTITION_COLUMN_INDEX)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),\n\t\t\t\t\t\terrmsg(\"the partition column of table %s should have a value\",\n\t\t\t\t\t\t\t   quote_qualified_identifier(schemaName, relationName))));\n\t}\n\n\t/* define the template for the COPY statement that is sent to workers */\n\tCopyStmt *copyStatement = makeNode(CopyStmt);\n\n\tbool colocatedIntermediateResults =\n\t\tcopyDest->colocatedIntermediateResultIdPrefix != NULL;\n\tif (colocatedIntermediateResults)\n\t{\n\t\tcopyStatement->relation = makeRangeVar(NULL,\n\t\t\t\t\t\t\t\t\t\t\t   copyDest->\n\t\t\t\t\t\t\t\t\t\t\t   colocatedIntermediateResultIdPrefix,\n\t\t\t\t\t\t\t\t\t\t\t   -1);\n\n\t\tDefElem *formatResultOption = makeDefElem(\"format\", (Node *) makeString(\"result\"),\n\t\t\t\t\t\t\t\t\t\t\t\t  -1);\n\t\tcopyStatement->options = list_make1(formatResultOption);\n\t}\n\telse\n\t{\n\t\tcopyStatement->relation = makeRangeVar(schemaName, relationName, -1);\n\t\tcopyStatement->options = NIL;\n\n\t\tif (copyOutState->binary)\n\t\t{\n\t\t\tDefElem *binaryFormatOption =\n\t\t\t\tmakeDefElem(\"format\", (Node *) makeString(\"binary\"), -1);\n\n\t\t\tcopyStatement->options = lappend(copyStatement->options, binaryFormatOption);\n\t\t}\n\t}\n\n\n\tcopyStatement->query = NULL;\n\tcopyStatement->attlist = attributeList;\n\tcopyStatement->is_from = true;\n\tcopyStatement->is_program = false;\n\tcopyStatement->filename = NULL;\n\tcopyDest->copyStatement = copyStatement;\n\n\tcopyDest->shardStateHash = CreateShardStateHash(TopTransactionContext);\n\tcopyDest->connectionStateHash = CreateConnectionStateHash(TopTransactionContext);\n\n\tRecordRelationAccessIfNonDistTable(tableId, PLACEMENT_ACCESS_DML);\n\n\t/*\n\t * Colocated intermediate results do not honor citus.max_shared_pool_size,\n\t * so we don't need to reserve any connections. Each result file is sent\n\t * over a single connection.\n\t */\n\tif (!colocatedIntermediateResults)\n\t{\n\t\t/*\n\t\t * For all the primary (e.g., writable) remote nodes, reserve a shared\n\t\t * connection. We do this upfront because we cannot know which nodes\n\t\t * are going to be accessed. Since the order of the reservation is\n\t\t * important, we need to do it right here. For the details on why the\n\t\t * order important, see EnsureConnectionPossibilityForNodeList().\n\t\t *\n\t\t * We don't need to care about local node because we either get a\n\t\t * connection or use local connection, so it cannot be part of\n\t\t * the starvation. As an edge case, if it cannot get a connection\n\t\t * and cannot switch to local execution (e.g., disabled by user),\n\t\t * COPY would fail hinting the user to change the relevant settiing.\n\t\t */\n\t\tEnsureConnectionPossibilityForRemotePrimaryNodes();\n\t}\n\n\tLocalCopyStatus localCopyStatus = GetLocalCopyStatus();\n\tif (localCopyStatus == LOCAL_COPY_DISABLED)\n\t{\n\t\tcopyDest->shouldUseLocalCopy = false;\n\t}\n\telse if (localCopyStatus == LOCAL_COPY_REQUIRED)\n\t{\n\t\tcopyDest->shouldUseLocalCopy = true;\n\t}\n\telse if (localCopyStatus == LOCAL_COPY_OPTIONAL)\n\t{\n\t\t/*\n\t\t * At this point, there is no requirements for doing the copy locally.\n\t\t * However, if there are local placements, we can try to reserve\n\t\t * a connection to local node. If we cannot reserve, we can still use\n\t\t * local execution.\n\t\t *\n\t\t * NB: It is not advantageous to use remote execution just with a\n\t\t * single remote connection. In other words, a single remote connection\n\t\t * would not perform better than local execution. However, we prefer to\n\t\t * do this because it is likely that the COPY would get more connections\n\t\t * to parallelize the operation. In the future, we might relax this\n\t\t * requirement and failover to local execution as on connection attempt\n\t\t * failures as the executor does.\n\t\t */\n\t\tif (ShardIntervalListHasLocalPlacements(shardIntervalList))\n\t\t{\n\t\t\tbool reservedConnection = TryConnectionPossibilityForLocalPrimaryNode();\n\t\t\tcopyDest->shouldUseLocalCopy = !reservedConnection;\n\t\t}\n\t}\n}\n\n\n/*\n * CitusCopyDestReceiverReceive implements the receiveSlot function of\n * CitusCopyDestReceiver. It takes a TupleTableSlot and sends the contents to\n * the appropriate shard placement(s).\n */\nstatic bool\nCitusCopyDestReceiverReceive(TupleTableSlot *slot, DestReceiver *dest)\n{\n\tbool result = false;\n\tCitusCopyDestReceiver *copyDest = (CitusCopyDestReceiver *) dest;\n\n\tPG_TRY();\n\t{\n\t\tresult = CitusSendTupleToPlacements(slot, copyDest);\n\t}\n\tPG_CATCH();\n\t{\n\t\t/*\n\t\t * We might be able to recover from errors with ROLLBACK TO SAVEPOINT,\n\t\t * so unclaim the connections before throwing errors.\n\t\t */\n\t\tList *connectionStateList = ConnectionStateList(copyDest->connectionStateHash);\n\t\tUnclaimCopyConnections(connectionStateList);\n\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n\n\treturn result;\n}\n\n\n/*\n * CitusSendTupleToPlacements sends the given TupleTableSlot to the appropriate\n * shard placement(s).\n */\nstatic bool\nCitusSendTupleToPlacements(TupleTableSlot *slot, CitusCopyDestReceiver *copyDest)\n{\n\tTupleDesc tupleDescriptor = copyDest->tupleDescriptor;\n\tCopyStmt *copyStatement = copyDest->copyStatement;\n\n\tCopyOutState copyOutState = copyDest->copyOutState;\n\tFmgrInfo *columnOutputFunctions = copyDest->columnOutputFunctions;\n\tCopyCoercionData *columnCoercionPaths = copyDest->columnCoercionPaths;\n\tListCell *placementStateCell = NULL;\n\tbool cachedShardStateFound = false;\n\tbool firstTupleInShard = false;\n\n\n\tEState *executorState = copyDest->executorState;\n\tMemoryContext executorTupleContext = GetPerTupleMemoryContext(executorState);\n\tMemoryContext oldContext = MemoryContextSwitchTo(executorTupleContext);\n\n\tslot_getallattrs(slot);\n\n\tDatum *columnValues = slot->tts_values;\n\tbool *columnNulls = slot->tts_isnull;\n\n\tint64 shardId = ShardIdForTuple(copyDest, columnValues, columnNulls);\n\n\t/* connections hash is kept in memory context */\n\tMemoryContextSwitchTo(copyDest->memoryContext);\n\tbool isColocatedIntermediateResult =\n\t\tcopyDest->colocatedIntermediateResultIdPrefix != NULL;\n\n\tCopyShardState *shardState = GetShardState(shardId, copyDest->shardStateHash,\n\t\t\t\t\t\t\t\t\t\t\t   copyDest->connectionStateHash,\n\t\t\t\t\t\t\t\t\t\t\t   &cachedShardStateFound,\n\t\t\t\t\t\t\t\t\t\t\t   copyDest->shouldUseLocalCopy,\n\t\t\t\t\t\t\t\t\t\t\t   copyDest->copyOutState,\n\t\t\t\t\t\t\t\t\t\t\t   isColocatedIntermediateResult,\n\t\t\t\t\t\t\t\t\t\t\t   copyDest->isPublishable);\n\n\tif (!cachedShardStateFound)\n\t{\n\t\tfirstTupleInShard = true;\n\t}\n\n\tif (firstTupleInShard && !copyDest->multiShardCopy &&\n\t\thash_get_num_entries(copyDest->shardStateHash) == 2)\n\t{\n\t\tOid relationId = copyDest->distributedRelationId;\n\n\t\t/* mark as multi shard to skip doing the same thing over and over */\n\t\tcopyDest->multiShardCopy = true;\n\n\t\tif (MultiShardConnectionType != SEQUENTIAL_CONNECTION)\n\t\t{\n\t\t\t/* when we see multiple shard connections, we mark COPY as parallel modify */\n\t\t\tRecordParallelModifyAccess(relationId);\n\t\t}\n\t}\n\n\tif (isColocatedIntermediateResult && copyDest->shouldUseLocalCopy &&\n\t\tshardState->containsLocalPlacement)\n\t{\n\t\tif (firstTupleInShard)\n\t\t{\n\t\t\tCreateLocalColocatedIntermediateFile(copyDest, shardState);\n\t\t}\n\n\t\tWriteTupleToLocalFile(slot, copyDest, shardId,\n\t\t\t\t\t\t\t  shardState->copyOutState, &shardState->fileDest);\n\t}\n\telse if (copyDest->shouldUseLocalCopy && shardState->containsLocalPlacement)\n\t{\n\t\tWriteTupleToLocalShard(slot, copyDest, shardId, shardState->copyOutState);\n\t}\n\n\tforeach(placementStateCell, shardState->placementStateList)\n\t{\n\t\tCopyPlacementState *currentPlacementState = lfirst(placementStateCell);\n\t\tCopyConnectionState *connectionState = currentPlacementState->connectionState;\n\t\tCopyPlacementState *activePlacementState = connectionState->activePlacementState;\n\t\tbool switchToCurrentPlacement = false;\n\t\tbool sendTupleOverConnection = false;\n\n\t\tif (activePlacementState == NULL)\n\t\t{\n\t\t\tswitchToCurrentPlacement = true;\n\t\t}\n\t\telse if (currentPlacementState != activePlacementState &&\n\t\t\t\t currentPlacementState->data->len > CopySwitchOverThresholdBytes)\n\t\t{\n\t\t\tswitchToCurrentPlacement = true;\n\n\t\t\t/* before switching, make sure to finish the copy */\n\t\t\tEndPlacementStateCopyCommand(activePlacementState, copyOutState);\n\t\t\tAddPlacementStateToCopyConnectionStateBuffer(connectionState,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t activePlacementState);\n\t\t}\n\n\t\tif (switchToCurrentPlacement)\n\t\t{\n\t\t\tStartPlacementStateCopyCommand(currentPlacementState, copyStatement,\n\t\t\t\t\t\t\t\t\t\t   copyOutState);\n\n\t\t\tRemovePlacementStateFromCopyConnectionStateBuffer(connectionState,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  currentPlacementState);\n\n\t\t\tconnectionState->activePlacementState = currentPlacementState;\n\n\t\t\t/* send previously buffered tuples */\n\t\t\tSendCopyDataToPlacement(currentPlacementState->data, shardId,\n\t\t\t\t\t\t\t\t\tconnectionState->connection);\n\t\t\tresetStringInfo(currentPlacementState->data);\n\n\t\t\t/* additionaly, we need to send the current tuple too */\n\t\t\tsendTupleOverConnection = true;\n\t\t}\n\t\telse if (currentPlacementState != activePlacementState)\n\t\t{\n\t\t\t/* buffer data */\n\t\t\tStringInfo copyBuffer = copyOutState->fe_msgbuf;\n\t\t\tresetStringInfo(copyBuffer);\n\t\t\tAppendCopyRowData(columnValues, columnNulls, tupleDescriptor,\n\t\t\t\t\t\t\t  copyOutState, columnOutputFunctions,\n\t\t\t\t\t\t\t  columnCoercionPaths);\n\t\t\tappendBinaryStringInfo(currentPlacementState->data, copyBuffer->data,\n\t\t\t\t\t\t\t\t   copyBuffer->len);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tAssert(currentPlacementState == activePlacementState);\n\t\t\tsendTupleOverConnection = true;\n\t\t}\n\n\t\tif (sendTupleOverConnection)\n\t\t{\n\t\t\tresetStringInfo(copyOutState->fe_msgbuf);\n\t\t\tAppendCopyRowData(columnValues, columnNulls, tupleDescriptor,\n\t\t\t\t\t\t\t  copyOutState, columnOutputFunctions, columnCoercionPaths);\n\t\t\tSendCopyDataToPlacement(copyOutState->fe_msgbuf, shardId,\n\t\t\t\t\t\t\t\t\tconnectionState->connection);\n\t\t}\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\n\tcopyDest->tuplesSent++;\n\n\t/*\n\t * Release per tuple memory allocated in this function. If we're writing\n\t * the results of an INSERT ... SELECT then the SELECT execution will use\n\t * its own executor state and reset the per tuple expression context\n\t * separately.\n\t */\n\tResetPerTupleExprContext(executorState);\n\n\treturn true;\n}\n\n\n/*\n * AddPlacementStateToCopyConnectionStateBuffer is a helper function to add a placement\n * state to connection state's placement buffer. In addition to that, keep the counter\n * up to date.\n */\nstatic void\nAddPlacementStateToCopyConnectionStateBuffer(CopyConnectionState *connectionState,\n\t\t\t\t\t\t\t\t\t\t\t CopyPlacementState *placementState)\n{\n\tdlist_push_head(&connectionState->bufferedPlacementList,\n\t\t\t\t\t&placementState->bufferedPlacementNode);\n\tconnectionState->bufferedPlacementCount++;\n}\n\n\n/*\n * RemovePlacementStateFromCopyConnectionStateBuffer is a helper function to removes a placement\n * state from connection state's placement buffer. In addition to that, keep the counter\n * up to date.\n */\nstatic void\nRemovePlacementStateFromCopyConnectionStateBuffer(CopyConnectionState *connectionState,\n\t\t\t\t\t\t\t\t\t\t\t\t  CopyPlacementState *placementState)\n{\n\tdlist_delete(&placementState->bufferedPlacementNode);\n\tconnectionState->bufferedPlacementCount--;\n}\n\n\n/*\n * ProcessAppendToShardOption returns the value of append_to_shard if set,\n * and removes the option from the options list.\n */\nstatic uint64\nProcessAppendToShardOption(Oid relationId, CopyStmt *copyStatement)\n{\n\tuint64 appendShardId = INVALID_SHARD_ID;\n\tbool appendToShardSet = false;\n\n\tDefElem *defel = NULL;\n\tforeach_declared_ptr(defel, copyStatement->options)\n\t{\n\t\tif (strncmp(defel->defname, APPEND_TO_SHARD_OPTION, NAMEDATALEN) == 0)\n\t\t{\n\t\t\tappendShardId = defGetInt64(defel);\n\t\t\tappendToShardSet = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (appendToShardSet)\n\t{\n\t\tif (!IsCitusTableType(relationId, APPEND_DISTRIBUTED))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(APPEND_TO_SHARD_OPTION \" is only valid for \"\n\t\t\t\t\t\t\t\t   \"append-distributed tables\")));\n\t\t}\n\n\t\t/* throws an error if shard does not exist */\n\t\tShardInterval *shardInterval = LoadShardInterval(appendShardId);\n\n\t\t/* also check whether shard belongs to table */\n\t\tif (shardInterval->relationId != relationId)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"shard \" UINT64_FORMAT \" does not belong to table %s\",\n\t\t\t\t\t\t\t\t   appendShardId, get_rel_name(relationId))));\n\t\t}\n\n\t\tcopyStatement->options =\n\t\t\tRemoveOptionFromList(copyStatement->options, APPEND_TO_SHARD_OPTION);\n\t}\n\telse if (IsCitusTableType(relationId, APPEND_DISTRIBUTED))\n\t{\n\t\tereport(ERROR, (errmsg(\"COPY into append-distributed table requires using the \"\n\t\t\t\t\t\t\t   APPEND_TO_SHARD_OPTION \" option\")));\n\t}\n\n\treturn appendShardId;\n}\n\n\n/*\n * ContainsLocalPlacement returns true if the current node has\n * a local placement for the given shard id.\n */\nstatic bool\nContainsLocalPlacement(int64 shardId)\n{\n\tListCell *placementCell = NULL;\n\tList *activePlacementList = ActiveShardPlacementList(shardId);\n\tint32 localGroupId = GetLocalGroupId();\n\n\tforeach(placementCell, activePlacementList)\n\t{\n\t\tShardPlacement *placement = (ShardPlacement *) lfirst(placementCell);\n\n\t\tif (placement->groupId == localGroupId)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n\n/*\n * ShardIdForTuple returns id of the shard to which the given tuple belongs to.\n */\nstatic uint64\nShardIdForTuple(CitusCopyDestReceiver *copyDest, Datum *columnValues, bool *columnNulls)\n{\n\tint partitionColumnIndex = copyDest->partitionColumnIndex;\n\tDatum partitionColumnValue = 0;\n\tCopyCoercionData *columnCoercionPaths = copyDest->columnCoercionPaths;\n\tCitusTableCacheEntry *cacheEntry =\n\t\tGetCitusTableCacheEntry(copyDest->distributedRelationId);\n\n\tif (IsCitusTableTypeCacheEntry(cacheEntry, APPEND_DISTRIBUTED))\n\t{\n\t\treturn copyDest->appendShardId;\n\t}\n\n\t/*\n\t * Find the partition column value and corresponding shard interval\n\t * for non-reference tables.\n\t * Get the existing (and only a single) shard interval for the reference\n\t * tables. Note that, reference tables has NULL partition column values so\n\t * skip the check.\n\t */\n\tif (partitionColumnIndex != INVALID_PARTITION_COLUMN_INDEX)\n\t{\n\t\tCopyCoercionData *coercePath = &columnCoercionPaths[partitionColumnIndex];\n\n\t\tif (columnNulls[partitionColumnIndex])\n\t\t{\n\t\t\tchar *qualifiedTableName = generate_qualified_relation_name(\n\t\t\t\tcopyDest->distributedRelationId);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),\n\t\t\t\t\t\t\terrmsg(\"the partition column of table %s cannot be NULL\",\n\t\t\t\t\t\t\t\t   qualifiedTableName)));\n\t\t}\n\n\t\t/* find the partition column value */\n\t\tpartitionColumnValue = columnValues[partitionColumnIndex];\n\t\tif (!copyDest->skipCoercions)\n\t\t{\n\t\t\t/* annoyingly this is evaluated twice, but at least we don't crash! */\n\t\t\tpartitionColumnValue = CoerceColumnValue(partitionColumnValue, coercePath);\n\t\t}\n\t}\n\n\t/*\n\t * Find the shard interval and id for the partition column value for\n\t * non-reference tables.\n\t *\n\t * For reference table, and single shard distributed table this function blindly returns the tables single\n\t * shard.\n\t */\n\tShardInterval *shardInterval = FindShardInterval(partitionColumnValue, cacheEntry);\n\tif (shardInterval == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"could not find shard for partition column \"\n\t\t\t\t\t\t\t   \"value\")));\n\t}\n\n\treturn shardInterval->shardId;\n}\n\n\n/*\n * CitusCopyDestReceiverShutdown implements the rShutdown interface of\n * CitusCopyDestReceiver. It ends the COPY on all the open connections, closes\n * the relation and increments the query stat counters based on the shards\n * copied into if requested.\n */\nstatic void\nCitusCopyDestReceiverShutdown(DestReceiver *destReceiver)\n{\n\tCitusCopyDestReceiver *copyDest = (CitusCopyDestReceiver *) destReceiver;\n\n\tHTAB *connectionStateHash = copyDest->connectionStateHash;\n\tListCell *connectionStateCell = NULL;\n\tRelation distributedRelation = copyDest->distributedRelation;\n\n\t/*\n\t * Increment the query stat counters based on the shards copied into\n\t * if requested.\n\t */\n\tif (copyDest->trackQueryCounters)\n\t{\n\t\tint copiedShardCount =\n\t\t\tcopyDest->shardStateHash ?\n\t\t\thash_get_num_entries(copyDest->shardStateHash) :\n\t\t\t0;\n\t\tif (copiedShardCount <= 1)\n\t\t{\n\t\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_SINGLE_SHARD);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_MULTI_SHARD);\n\t\t}\n\t}\n\n\tList *connectionStateList = ConnectionStateList(connectionStateHash);\n\n\tFinishLocalColocatedIntermediateFiles(copyDest);\n\tFinishLocalCopy(copyDest);\n\n\tPG_TRY();\n\t{\n\t\tforeach(connectionStateCell, connectionStateList)\n\t\t{\n\t\t\tCopyConnectionState *connectionState =\n\t\t\t\t(CopyConnectionState *) lfirst(connectionStateCell);\n\n\t\t\tShutdownCopyConnectionState(connectionState, copyDest);\n\t\t}\n\t}\n\tPG_CATCH();\n\t{\n\t\t/*\n\t\t * We might be able to recover from errors with ROLLBACK TO SAVEPOINT,\n\t\t * so unclaim the connections before throwing errors.\n\t\t */\n\t\tUnclaimCopyConnections(connectionStateList);\n\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n\n\ttable_close(distributedRelation, NoLock);\n}\n\n\n/*\n * FinishLocalCopy sends the remaining copies for local placements.\n */\nstatic void\nFinishLocalCopy(CitusCopyDestReceiver *copyDest)\n{\n\tHTAB *shardStateHash = copyDest->shardStateHash;\n\tHASH_SEQ_STATUS status;\n\tCopyShardState *copyShardState;\n\n\tforeach_htab(copyShardState, &status, shardStateHash)\n\t{\n\t\tif (copyShardState->copyOutState != NULL &&\n\t\t\tcopyShardState->copyOutState->fe_msgbuf->len > 0)\n\t\t{\n\t\t\tFinishLocalCopyToShard(copyDest, copyShardState->shardId,\n\t\t\t\t\t\t\t\t   copyShardState->copyOutState);\n\t\t}\n\t}\n}\n\n\n/*\n * CreateLocalColocatedIntermediateFile creates a co-located file for the given\n * shard, and appends the binary headers if needed. The function also modifies\n * shardState to set the fileDest and copyOutState.\n */\nstatic void\nCreateLocalColocatedIntermediateFile(CitusCopyDestReceiver *copyDest,\n\t\t\t\t\t\t\t\t\t CopyShardState *shardState)\n{\n\t/* make sure the directory exists */\n\tCreateIntermediateResultsDirectory();\n\n\tconst int fileFlags = (O_CREAT | O_RDWR | O_TRUNC);\n\n\tStringInfo filePath = makeStringInfo();\n\tappendStringInfo(filePath, \"%s_%ld\", copyDest->colocatedIntermediateResultIdPrefix,\n\t\t\t\t\t shardState->shardId);\n\n\tconst char *fileName = QueryResultFileName(filePath->data);\n\tshardState->fileDest =\n\t\tFileCompatFromFileStart(FileOpenForTransmit(fileName, fileFlags));\n\n\tCopyOutState localFileCopyOutState = shardState->copyOutState;\n\tbool isBinaryCopy = localFileCopyOutState->binary;\n\tif (isBinaryCopy)\n\t{\n\t\tAppendCopyBinaryHeaders(localFileCopyOutState);\n\t}\n}\n\n\n/*\n * FinishLocalColocatedIntermediateFiles iterates over all the colocated\n * intermediate files and finishes the COPY on all of them.\n */\nstatic void\nFinishLocalColocatedIntermediateFiles(CitusCopyDestReceiver *copyDest)\n{\n\tHTAB *shardStateHash = copyDest->shardStateHash;\n\tHASH_SEQ_STATUS status;\n\tCopyShardState *copyShardState;\n\n\tforeach_htab(copyShardState, &status, shardStateHash)\n\t{\n\t\tif (copyShardState->copyOutState != NULL &&\n\t\t\tFILE_IS_OPEN(copyShardState->fileDest.fd))\n\t\t{\n\t\t\tFinishLocalCopyToFile(copyShardState->copyOutState,\n\t\t\t\t\t\t\t\t  &copyShardState->fileDest);\n\t\t}\n\t}\n}\n\n\n/*\n * ShutdownCopyConnectionState ends the copy command for the current active\n * placement on connection, and then sends the rest of the buffers over the\n * connection.\n */\nstatic void\nShutdownCopyConnectionState(CopyConnectionState *connectionState,\n\t\t\t\t\t\t\tCitusCopyDestReceiver *copyDest)\n{\n\tCopyOutState copyOutState = copyDest->copyOutState;\n\tCopyStmt *copyStatement = copyDest->copyStatement;\n\tdlist_iter iter;\n\n\tCopyPlacementState *activePlacementState = connectionState->activePlacementState;\n\tif (activePlacementState != NULL)\n\t{\n\t\tEndPlacementStateCopyCommand(activePlacementState, copyOutState);\n\t\tif (!copyDest->isPublishable)\n\t\t{\n\t\t\tResetReplicationOriginRemoteSession(\n\t\t\t\tactivePlacementState->connectionState->connection);\n\t\t}\n\t}\n\n\tdlist_foreach(iter, &connectionState->bufferedPlacementList)\n\t{\n\t\tCopyPlacementState *placementState =\n\t\t\tdlist_container(CopyPlacementState, bufferedPlacementNode, iter.cur);\n\t\tuint64 shardId = placementState->shardState->shardId;\n\n\t\tStartPlacementStateCopyCommand(placementState, copyStatement,\n\t\t\t\t\t\t\t\t\t   copyOutState);\n\t\tSendCopyDataToPlacement(placementState->data, shardId,\n\t\t\t\t\t\t\t\tconnectionState->connection);\n\t\tEndPlacementStateCopyCommand(placementState, copyOutState);\n\t\tif (!copyDest->isPublishable)\n\t\t{\n\t\t\tResetReplicationOriginRemoteSession(connectionState->connection);\n\t\t}\n\t}\n}\n\n\n/*\n * CitusCopyDestReceiverDestroy frees the DestReceiver\n */\nstatic void\nCitusCopyDestReceiverDestroy(DestReceiver *destReceiver)\n{\n\tCitusCopyDestReceiver *copyDest = (CitusCopyDestReceiver *) destReceiver;\n\n\tif (copyDest->copyOutState)\n\t{\n\t\tpfree(copyDest->copyOutState);\n\t}\n\n\tif (copyDest->columnOutputFunctions)\n\t{\n\t\tpfree(copyDest->columnOutputFunctions);\n\t}\n\n\tif (copyDest->columnCoercionPaths)\n\t{\n\t\tpfree(copyDest->columnCoercionPaths);\n\t}\n\n\tif (copyDest->shardStateHash)\n\t{\n\t\thash_destroy(copyDest->shardStateHash);\n\t}\n\n\tif (copyDest->connectionStateHash)\n\t{\n\t\thash_destroy(copyDest->connectionStateHash);\n\t}\n\n\tpfree(copyDest);\n}\n\n\n/*\n * IsCopyResultStmt determines whether the given copy statement is a\n * COPY \"resultkey\" FROM STDIN WITH (format result) statement, which is used\n * to copy query results from the coordinator into workers.\n */\nbool\nIsCopyResultStmt(CopyStmt *copyStatement)\n{\n\treturn CopyStatementHasFormat(copyStatement, \"result\");\n}\n\n\n/*\n * CopyStatementHasFormat checks whether the COPY statement has the given\n * format.\n */\nstatic bool\nCopyStatementHasFormat(CopyStmt *copyStatement, char *formatName)\n{\n\tListCell *optionCell = NULL;\n\tbool hasFormat = false;\n\n\t/* extract WITH (...) options from the COPY statement */\n\tforeach(optionCell, copyStatement->options)\n\t{\n\t\tDefElem *defel = (DefElem *) lfirst(optionCell);\n\n\t\tif (strncmp(defel->defname, \"format\", NAMEDATALEN) == 0 &&\n\t\t\tstrncmp(defGetString(defel), formatName, NAMEDATALEN) == 0)\n\t\t{\n\t\t\thasFormat = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn hasFormat;\n}\n\n\n/*\n * ErrorIfCopyHasOnErrorLogVerbosity errors out if the COPY statement\n * has on_error option or log_verbosity option specified\n */\nstatic void\nErrorIfCopyHasOnErrorLogVerbosity(CopyStmt *copyStatement)\n{\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tbool log_verbosity = false;\n\tforeach_ptr(DefElem, option, copyStatement->options)\n\t{\n\t\tif (strcmp(option->defname, \"on_error\") == 0)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"Citus does not support \"\n\t\t\t\t\t\t\t\t   \"COPY FROM with ON_ERROR option.\")));\n\t\t}\n\t\telse if (strcmp(option->defname, \"log_verbosity\") == 0)\n\t\t{\n\t\t\tlog_verbosity = true;\n\t\t}\n\t}\n\n\t/*\n\t * Given that log_verbosity is currently used in COPY FROM\n\t * when ON_ERROR option is set to ignore, it makes more\n\t * sense to error out for ON_ERROR option first. For this reason,\n\t * we don't error out in the previous loop directly.\n\t * Relevant PG17 commit: https://github.com/postgres/postgres/commit/f5a227895\n\t */\n\tif (log_verbosity)\n\t{\n\t\tereport(ERROR, (errmsg(\"Citus does not support \"\n\t\t\t\t\t\t\t   \"COPY FROM with LOG_VERBOSITY option.\")));\n\t}\n#endif\n}\n\n\n/*\n * ErrorIfMergeInCopy Raises an exception if the MERGE is called in the COPY\n * where Citus tables are involved, as we don't support this yet\n * Relevant PG17 commit: c649fa24a\n */\nstatic void\nErrorIfMergeInCopy(CopyStmt *copyStatement)\n{\n#if PG_VERSION_NUM < 170000\n\treturn;\n#else\n\tif (!copyStatement->relation && (IsA(copyStatement->query, MergeStmt)))\n\t{\n\t\t/*\n\t\t * This path is currently not reachable because Merge in COPY can\n\t\t * only work with a RETURNING clause, and a RETURNING check\n\t\t * will error out sooner for Citus\n\t\t */\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"MERGE with Citus tables \"\n\t\t\t\t\t\t\t   \"is not yet supported in COPY\")));\n\t}\n#endif\n}\n\n\n/*\n * ProcessCopyStmt handles Citus specific concerns for COPY like supporting\n * COPYing from distributed tables and preventing unsupported actions. The\n * function returns a modified COPY statement to be executed, or NULL if no\n * further processing is needed.\n */\nNode *\nProcessCopyStmt(CopyStmt *copyStatement, QueryCompletion *completionTag, const\n\t\t\t\tchar *queryString)\n{\n\t/*\n\t * Handle special COPY \"resultid\" FROM STDIN WITH (format result) commands\n\t * for sending intermediate results to workers.\n\t */\n\tif (IsCopyResultStmt(copyStatement))\n\t{\n\t\tconst char *resultId = copyStatement->relation->relname;\n\n\t\tif (copyStatement->is_from)\n\t\t{\n\t\t\tReceiveQueryResultViaCopy(resultId);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSendQueryResultViaCopy(resultId);\n\t\t}\n\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * We check whether a distributed relation is affected. For that, we need to open the\n\t * relation. To prevent race conditions with later lookups, lock the table, and modify\n\t * the rangevar to include the schema.\n\t */\n\tif (copyStatement->relation != NULL)\n\t{\n\t\tErrorIfMergeInCopy(copyStatement);\n\n\t\tbool isFrom = copyStatement->is_from;\n\n\t\t/* consider using RangeVarGetRelidExtended to check perms before locking */\n\t\tRelation copiedRelation = table_openrv(copyStatement->relation,\n\t\t\t\t\t\t\t\t\t\t\t   isFrom ? RowExclusiveLock :\n\t\t\t\t\t\t\t\t\t\t\t   AccessShareLock);\n\n\t\tbool isCitusRelation = IsCitusTable(RelationGetRelid(copiedRelation));\n\n\t\t/* ensure future lookups hit the same relation */\n\t\tchar *schemaName = get_namespace_name(RelationGetNamespace(copiedRelation));\n\n\t\t/* ensure we copy string into proper context */\n\t\tMemoryContext relationContext = GetMemoryChunkContext(\n\t\t\tcopyStatement->relation);\n\t\tschemaName = MemoryContextStrdup(relationContext, schemaName);\n\t\tcopyStatement->relation->schemaname = schemaName;\n\n\t\ttable_close(copiedRelation, NoLock);\n\n\t\tif (isCitusRelation)\n\t\t{\n\t\t\tif (copyStatement->is_from)\n\t\t\t{\n\t\t\t\tif (copyStatement->whereClause)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Update progress reporting for tuples progressed so that the\n\t\t\t\t\t * progress is reflected on pg_stat_progress_copy. Citus currently\n\t\t\t\t\t * does not support COPY .. WHERE clause so TUPLES_EXCLUDED is not\n\t\t\t\t\t * handled. When we remove this check, we should implement progress\n\t\t\t\t\t * reporting as well.\n\t\t\t\t\t */\n\t\t\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\t\t\"Citus does not support COPY FROM with WHERE\")));\n\t\t\t\t}\n\n\t\t\t\tErrorIfCopyHasOnErrorLogVerbosity(copyStatement);\n\n\t\t\t\t/* check permissions, we're bypassing postgres' normal checks */\n\t\t\t\tCheckCopyPermissions(copyStatement);\n\t\t\t\tCitusCopyFrom(copyStatement, completionTag);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\telse if (copyStatement->filename == NULL && !copyStatement->is_program &&\n\t\t\t\t\t !CopyStatementHasFormat(copyStatement, \"binary\"))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * COPY table TO STDOUT is handled by specialized logic to\n\t\t\t\t * avoid buffering the table on the coordinator. This enables\n\t\t\t\t * pg_dump of large tables.\n\t\t\t\t */\n\t\t\t\tCitusCopyTo(copyStatement, completionTag);\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * COPY table TO PROGRAM / file is handled by wrapping the table\n\t\t\t\t * in a SELECT and going through the resulting COPY logic.\n\t\t\t\t */\n\t\t\t\tSelectStmt *selectStmt = CitusCopySelect(copyStatement);\n\n\t\t\t\t/* replace original statement */\n\t\t\t\tcopyStatement = copyObject(copyStatement);\n\t\t\t\tcopyStatement->relation = NULL;\n\t\t\t\tcopyStatement->query = (Node *) selectStmt;\n\t\t\t}\n\t\t}\n\t}\n\treturn (Node *) copyStatement;\n}\n\n\n/*\n * CitusCopySelect generates a SelectStmt such that table may be replaced in\n * \"COPY table FROM\" for an equivalent result.\n */\nstatic SelectStmt *\nCitusCopySelect(CopyStmt *copyStatement)\n{\n\tSelectStmt *selectStmt = makeNode(SelectStmt);\n\tselectStmt->fromClause = list_make1(copyObject(copyStatement->relation));\n\n\tRelation distributedRelation = table_openrv(copyStatement->relation, AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(distributedRelation);\n\tList *targetList = NIL;\n\n\tfor (int i = 0; i < tupleDescriptor->natts; i++)\n\t{\n\t\tForm_pg_attribute attr = TupleDescAttr(tupleDescriptor, i);\n\n\t\tif (attr->attisdropped ||\n\t\t\tattr->attgenerated\n\t\t\t)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tColumnRef *column = makeNode(ColumnRef);\n\t\tcolumn->fields = list_make1(makeString(pstrdup(attr->attname.data)));\n\t\tcolumn->location = -1;\n\n\t\tResTarget *selectTarget = makeNode(ResTarget);\n\t\tselectTarget->name = NULL;\n\t\tselectTarget->indirection = NIL;\n\t\tselectTarget->val = (Node *) column;\n\t\tselectTarget->location = -1;\n\n\t\ttargetList = lappend(targetList, selectTarget);\n\t}\n\n\ttable_close(distributedRelation, NoLock);\n\n\tselectStmt->targetList = targetList;\n\treturn selectStmt;\n}\n\n\n/*\n * CitusCopyTo runs a COPY .. TO STDOUT command on each shard to do a full\n * table dump.\n */\nstatic void\nCitusCopyTo(CopyStmt *copyStatement, QueryCompletion *completionTag)\n{\n\tListCell *shardIntervalCell = NULL;\n\tint64 tuplesSent = 0;\n\n\tRelation distributedRelation = table_openrv(copyStatement->relation, AccessShareLock);\n\tOid relationId = RelationGetRelid(distributedRelation);\n\tTupleDesc tupleDescriptor = RelationGetDescr(distributedRelation);\n\n\tCopyOutState copyOutState = (CopyOutState) palloc0(sizeof(CopyOutStateData));\n\tcopyOutState->fe_msgbuf = makeStringInfo();\n\tcopyOutState->binary = false;\n\tcopyOutState->attnumlist = CopyGetAttnums(tupleDescriptor, distributedRelation,\n\t\t\t\t\t\t\t\t\t\t\t  copyStatement->attlist);\n\n\tSendCopyBegin(copyOutState);\n\n\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\n\tforeach(shardIntervalCell, shardIntervalList)\n\t{\n\t\tShardInterval *shardInterval = lfirst(shardIntervalCell);\n\t\tList *shardPlacementList = ActiveShardPlacementList(shardInterval->shardId);\n\t\tListCell *shardPlacementCell = NULL;\n\t\tint placementIndex = 0;\n\n\t\tStringInfo copyCommand = ConstructCopyStatement(copyStatement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardInterval->shardId);\n\n\t\tforeach(shardPlacementCell, shardPlacementList)\n\t\t{\n\t\t\tShardPlacement *shardPlacement = lfirst(shardPlacementCell);\n\t\t\tint connectionFlags = 0;\n\t\t\tchar *userName = NULL;\n\t\t\tconst bool raiseErrors = true;\n\n\t\t\tMultiConnection *connection = GetPlacementConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t userName);\n\n\t\t\t/*\n\t\t\t * This code-path doesn't support optional connections, so we don't expect\n\t\t\t * NULL connections.\n\t\t\t */\n\t\t\tAssert(connection != NULL);\n\n\t\t\tif (placementIndex == list_length(shardPlacementList) - 1)\n\t\t\t{\n\t\t\t\t/* last chance for this shard */\n\t\t\t\tMarkRemoteTransactionCritical(connection);\n\t\t\t}\n\n\t\t\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t\t\t{\n\t\t\t\tReportConnectionError(connection, ERROR);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tRemoteTransactionBeginIfNecessary(connection);\n\n\t\t\tif (!SendRemoteCommand(connection, copyCommand->data))\n\t\t\t{\n\t\t\t\tReportConnectionError(connection, ERROR);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tPGresult *result = GetRemoteCommandResult(connection, raiseErrors);\n\t\t\tif (PQresultStatus(result) != PGRES_COPY_OUT)\n\t\t\t{\n\t\t\t\tReportResultError(connection, result, ERROR);\n\t\t\t}\n\n\t\t\tPQclear(result);\n\n\t\t\ttuplesSent += ForwardCopyDataFromConnection(copyOutState, connection);\n\n\t\t\tbreak;\n\t\t}\n\n\t\tif (shardIntervalCell == list_head(shardIntervalList))\n\t\t{\n\t\t\t/* remove header after the first shard */\n\t\t\tcopyStatement->options =\n\t\t\t\tRemoveOptionFromList(copyStatement->options, \"header\");\n\t\t}\n\t}\n\n\tSendCopyEnd(copyOutState);\n\n\tif (list_length(shardIntervalList) <= 1)\n\t{\n\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_SINGLE_SHARD);\n\t}\n\telse\n\t{\n\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_MULTI_SHARD);\n\t}\n\n\ttable_close(distributedRelation, AccessShareLock);\n\n\tif (completionTag != NULL)\n\t{\n\t\tCompleteCopyQueryTagCompat(completionTag, tuplesSent);\n\t}\n}\n\n\n/*\n * ForwardCopyDataFromConnection forwards copy data received over the given connection\n * to the client or file descriptor.\n */\nstatic int64\nForwardCopyDataFromConnection(CopyOutState copyOutState, MultiConnection *connection)\n{\n\tchar *receiveBuffer = NULL;\n\tconst int useAsync = 0;\n\tbool raiseErrors = true;\n\tint64 tuplesSent = 0;\n\n\t/* receive copy data message in a synchronous manner */\n\tint receiveLength = PQgetCopyData(connection->pgConn, &receiveBuffer, useAsync);\n\twhile (receiveLength > 0)\n\t{\n\t\tbool includeEndOfLine = false;\n\n\t\tCopySendData(copyOutState, receiveBuffer, receiveLength);\n\t\tCopySendEndOfRow(copyOutState, includeEndOfLine);\n\t\ttuplesSent++;\n\n\t\tPQfreemem(receiveBuffer);\n\n\t\treceiveLength = PQgetCopyData(connection->pgConn, &receiveBuffer, useAsync);\n\t}\n\n\tif (receiveLength != -1)\n\t{\n\t\tReportConnectionError(connection, ERROR);\n\t}\n\n\tPGresult *result = GetRemoteCommandResult(connection, raiseErrors);\n\tif (!IsResponseOK(result))\n\t{\n\t\tReportResultError(connection, result, ERROR);\n\t}\n\n\tPQclear(result);\n\tClearResults(connection, raiseErrors);\n\n\treturn tuplesSent;\n}\n\n\n/*\n * Check whether the current user has the permission to execute a COPY\n * statement, raise ERROR if not. In some cases we have to do this separately\n * from postgres' copy.c, because we have to execute the copy with elevated\n * privileges.\n *\n * Copied from postgres, where it's part of DoCopy().\n */\nvoid\nCheckCopyPermissions(CopyStmt *copyStatement)\n{\n\t/* *INDENT-OFF* */\n\tbool\t\tis_from = copyStatement->is_from;\n\tRelation\trel;\n\tList\t   *range_table = NIL;\n\tTupleDesc\ttupDesc;\n\tAclMode\t\trequired_access = (is_from ? ACL_INSERT : ACL_SELECT);\n\tList\t   *attnums;\n\tListCell   *cur;\n\n\trel = table_openrv(copyStatement->relation,\n\t                  is_from ? RowExclusiveLock : AccessShareLock);\n\n\trange_table = CreateRangeTable(rel);\n\tRangeTblEntry *rte = (RangeTblEntry*) linitial(range_table);\n\ttupDesc = RelationGetDescr(rel);\n\n\t/* create permission info for rte */\n\tRTEPermissionInfo *perminfo = GetFilledPermissionInfo(rel->rd_id, rte->inh, required_access);\n\n\tattnums = CopyGetAttnums(tupDesc, rel, copyStatement->attlist);\n\tforeach(cur, attnums)\n\t{\n\t\tint\t\t\tattno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber;\n\n\t\tif (is_from)\n\t\t{\n\t\t\tperminfo->insertedCols = bms_add_member(perminfo->insertedCols, attno);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tperminfo->selectedCols = bms_add_member(perminfo->selectedCols, attno);\n\t\t}\n\t}\n\n\t/* link rte to its permission info then check permissions */\n\trte->perminfoindex = 1;\n\tExecCheckPermissions(list_make1(rte), list_make1(perminfo), true);\n\n\t/* TODO: Perform RLS checks once supported */\n\n\ttable_close(rel, NoLock);\n\t/* *INDENT-ON* */\n}\n\n\n/*\n * CreateRangeTable creates a range table with the given relation.\n */\nList *\nCreateRangeTable(Relation rel)\n{\n\tRangeTblEntry *rte = makeNode(RangeTblEntry);\n\trte->rtekind = RTE_RELATION;\n\trte->relid = rel->rd_id;\n\trte->relkind = rel->rd_rel->relkind;\n\treturn list_make1(rte);\n}\n\n\n/*\n * CreateConnectionStateHash constructs a hash table which maps from socket\n * number to CopyConnectionState, passing the provided MemoryContext to\n * hash_create for hash allocations.\n */\nstatic HTAB *\nCreateConnectionStateHash(MemoryContext memoryContext)\n{\n\tHASHCTL info;\n\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(int);\n\tinfo.entrysize = sizeof(CopyConnectionState);\n\tinfo.hcxt = memoryContext;\n\tint hashFlags = (HASH_ELEM | HASH_CONTEXT | HASH_BLOBS);\n\n\tHTAB *connectionStateHash = hash_create(\"Copy Connection State Hash\", 128, &info,\n\t\t\t\t\t\t\t\t\t\t\thashFlags);\n\n\treturn connectionStateHash;\n}\n\n\n/*\n * CreateShardStateHash constructs a hash table which maps from shard\n * identifier to CopyShardState, passing the provided MemoryContext to\n * hash_create for hash allocations.\n */\nstatic HTAB *\nCreateShardStateHash(MemoryContext memoryContext)\n{\n\tHASHCTL info;\n\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(uint64);\n\tinfo.entrysize = sizeof(CopyShardState);\n\tinfo.hcxt = memoryContext;\n\tint hashFlags = (HASH_ELEM | HASH_CONTEXT | HASH_BLOBS);\n\n\tHTAB *shardStateHash = hash_create(\"Copy Shard State Hash\", 128, &info, hashFlags);\n\n\treturn shardStateHash;\n}\n\n\n/*\n * GetConnectionState finds existing CopyConnectionState for a connection in the\n * provided hash. If not found, then a default structure is returned.\n */\nstatic CopyConnectionState *\nGetConnectionState(HTAB *connectionStateHash, MultiConnection *connection)\n{\n\tbool found = false;\n\n\tint sock = PQsocket(connection->pgConn);\n\tAssert(sock != -1);\n\n\tCopyConnectionState *connectionState = (CopyConnectionState *) hash_search(\n\t\tconnectionStateHash, &sock,\n\t\tHASH_ENTER,\n\t\t&found);\n\tif (!found)\n\t{\n\t\tconnectionState->socket = sock;\n\t\tconnectionState->connection = connection;\n\t\tconnectionState->activePlacementState = NULL;\n\t\tconnectionState->bufferedPlacementCount = 0;\n\t\tdlist_init(&connectionState->bufferedPlacementList);\n\t}\n\n\treturn connectionState;\n}\n\n\n/*\n * ConnectionStateList returns all CopyConnectionState structures in\n * the given hash.\n */\nstatic List *\nConnectionStateList(HTAB *connectionStateHash)\n{\n\tList *connectionStateList = NIL;\n\tHASH_SEQ_STATUS status;\n\n\thash_seq_init(&status, connectionStateHash);\n\n\tCopyConnectionState *connectionState = (CopyConnectionState *) hash_seq_search(\n\t\t&status);\n\twhile (connectionState != NULL)\n\t{\n\t\tconnectionStateList = lappend(connectionStateList, connectionState);\n\n\t\tconnectionState = (CopyConnectionState *) hash_seq_search(&status);\n\t}\n\n\treturn connectionStateList;\n}\n\n\n/*\n * ConnectionStateListToNode returns all CopyConnectionState structures in\n * the given hash for a given hostname and port values.\n */\nstatic List *\nConnectionStateListToNode(HTAB *connectionStateHash, const char *hostname, int32 port)\n{\n\tList *connectionStateList = NIL;\n\tHASH_SEQ_STATUS status;\n\n\thash_seq_init(&status, connectionStateHash);\n\n\tCopyConnectionState *connectionState =\n\t\t(CopyConnectionState *) hash_seq_search(&status);\n\twhile (connectionState != NULL)\n\t{\n\t\tchar *connectionHostname = connectionState->connection->hostname;\n\t\tif (strncmp(connectionHostname, hostname, MAX_NODE_LENGTH) == 0 &&\n\t\t\tconnectionState->connection->port == port)\n\t\t{\n\t\t\tconnectionStateList = lappend(connectionStateList, connectionState);\n\t\t}\n\n\t\tconnectionState = (CopyConnectionState *) hash_seq_search(&status);\n\t}\n\n\treturn connectionStateList;\n}\n\n\n/*\n * GetShardState finds existing CopyShardState for a shard in the provided\n * hash. If not found, then a new shard state is returned with all related\n * CopyPlacementStates initialized.\n */\nstatic CopyShardState *\nGetShardState(uint64 shardId, HTAB *shardStateHash,\n\t\t\t  HTAB *connectionStateHash, bool *found, bool\n\t\t\t  shouldUseLocalCopy, CopyOutState copyOutState,\n\t\t\t  bool isColocatedIntermediateResult, bool isPublishable)\n{\n\tCopyShardState *shardState = (CopyShardState *) hash_search(shardStateHash, &shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tHASH_ENTER, found);\n\tif (!*found)\n\t{\n\t\tInitializeCopyShardState(shardState, connectionStateHash,\n\t\t\t\t\t\t\t\t shardId, shouldUseLocalCopy,\n\t\t\t\t\t\t\t\t copyOutState, isColocatedIntermediateResult,\n\t\t\t\t\t\t\t\t isPublishable);\n\t}\n\n\treturn shardState;\n}\n\n\n/*\n * InitializeCopyShardState initializes the given shardState. It finds all\n * placements for the given shardId, assignes connections to them, and\n * adds them to shardState->placementStateList.\n */\nstatic void\nInitializeCopyShardState(CopyShardState *shardState,\n\t\t\t\t\t\t HTAB *connectionStateHash, uint64 shardId,\n\t\t\t\t\t\t bool shouldUseLocalCopy,\n\t\t\t\t\t\t CopyOutState copyOutState,\n\t\t\t\t\t\t bool colocatedIntermediateResult,\n\t\t\t\t\t\t bool isPublishable)\n{\n\tListCell *placementCell = NULL;\n\tint failedPlacementCount = 0;\n\tbool hasRemoteCopy = false;\n\n\tMemoryContext localContext =\n\t\tAllocSetContextCreateInternal(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t  \"InitializeCopyShardState\",\n\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MINSIZE,\n\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_INITSIZE,\n\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MAXSIZE);\n\n\n\t/* release active placement list at the end of this function */\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\tList *activePlacementList = ActiveShardPlacementList(shardId);\n\n\tMemoryContextSwitchTo(oldContext);\n\n\tshardState->shardId = shardId;\n\tshardState->placementStateList = NIL;\n\tshardState->copyOutState = NULL;\n\tshardState->containsLocalPlacement = ContainsLocalPlacement(shardId);\n\tshardState->fileDest.fd = -1;\n\n\tforeach(placementCell, activePlacementList)\n\t{\n\t\tShardPlacement *placement = (ShardPlacement *) lfirst(placementCell);\n\n\t\tif (shouldUseLocalCopy && placement->groupId == GetLocalGroupId())\n\t\t{\n\t\t\tshardState->copyOutState = (CopyOutState) palloc0(sizeof(*copyOutState));\n\t\t\tCloneCopyOutStateForLocalCopy(copyOutState, shardState->copyOutState);\n\n\t\t\tif (colocatedIntermediateResult)\n\t\t\t{\n\t\t\t\tLogLocalCopyToFileExecution(shardId);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tLogLocalCopyToRelationExecution(shardId);\n\t\t\t}\n\n\t\t\tcontinue;\n\t\t}\n\n\t\thasRemoteCopy = true;\n\n\t\tMultiConnection *connection =\n\t\t\tCopyGetPlacementConnection(connectionStateHash, placement,\n\t\t\t\t\t\t\t\t\t   colocatedIntermediateResult);\n\t\tif (connection == NULL)\n\t\t{\n\t\t\tfailedPlacementCount++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tCopyConnectionState *connectionState = GetConnectionState(connectionStateHash,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  connection);\n\n\t\t/*\n\t\t * If this is the first time we are using this connection for copying a\n\t\t * shard, send begin if necessary.\n\t\t */\n\t\tif (connectionState->activePlacementState == NULL)\n\t\t{\n\t\t\tRemoteTransactionBeginIfNecessary(connection);\n\t\t}\n\n\t\tif (!isPublishable)\n\t\t{\n\t\t\tSetupReplicationOriginRemoteSession(connection);\n\t\t}\n\n\t\tCopyPlacementState *placementState = palloc0(sizeof(CopyPlacementState));\n\t\tplacementState->shardState = shardState;\n\t\tplacementState->data = makeStringInfo();\n\t\tplacementState->groupId = placement->groupId;\n\t\tplacementState->connectionState = connectionState;\n\n\t\t/*\n\t\t * We don't set connectionState->activePlacementState here even if it\n\t\t * is NULL. Later in CitusSendTupleToPlacements() we set it at the\n\t\t * same time as calling StartPlacementStateCopyCommand() so we actually\n\t\t * know the COPY operation for the placement is ongoing.\n\t\t */\n\t\tAddPlacementStateToCopyConnectionStateBuffer(connectionState, placementState);\n\t\tshardState->placementStateList = lappend(shardState->placementStateList,\n\t\t\t\t\t\t\t\t\t\t\t\t placementState);\n\t}\n\n\t/* if all placements failed, error out */\n\tif (failedPlacementCount == list_length(activePlacementList))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not connect to any active placements\")));\n\t}\n\n\tEnsureTaskExecutionAllowed(hasRemoteCopy);\n\n\t/*\n\t * We just error out and code execution should never reach to this\n\t * point. This is the case for all tables.\n\t */\n\tAssert(failedPlacementCount == 0);\n\n\tMemoryContextReset(localContext);\n}\n\n\n/*\n * CloneCopyOutStateForLocalCopy creates a shallow copy of the CopyOutState with a new\n * fe_msgbuf. We keep a separate CopyOutState for every local shard placement, because\n * in case of local copy we serialize and buffer incoming tuples into fe_msgbuf for each\n * placement and the serialization functions take a CopyOutState as a parameter.\n */\nstatic void\nCloneCopyOutStateForLocalCopy(CopyOutState from, CopyOutState to)\n{\n\tto->attnumlist = from->attnumlist;\n\tto->binary = from->binary;\n\tto->copy_dest = from->copy_dest;\n\tto->delim = from->delim;\n\tto->file_encoding = from->file_encoding;\n\tto->need_transcoding = from->need_transcoding;\n\tto->null_print = from->null_print;\n\tto->null_print_client = from->null_print_client;\n\tto->rowcontext = from->rowcontext;\n\tto->fe_msgbuf = makeStringInfo();\n}\n\n\n/*\n * LogLocalCopyToRelationExecution logs that the copy will be done\n * locally for the given shard.\n */\nstatic void\nLogLocalCopyToRelationExecution(uint64 shardId)\n{\n\tif (!(LogRemoteCommands || LogLocalCommands))\n\t{\n\t\treturn;\n\t}\n\tereport(NOTICE, (errmsg(\"executing the copy locally for shard %lu\", shardId)));\n}\n\n\n/*\n * LogLocalCopyToFileExecution logs that the copy will be done locally for\n * a file colocated to the given shard.\n */\nstatic void\nLogLocalCopyToFileExecution(uint64 shardId)\n{\n\tif (!(LogRemoteCommands || LogLocalCommands))\n\t{\n\t\treturn;\n\t}\n\tereport(NOTICE, (errmsg(\"executing the copy locally for colocated file with \"\n\t\t\t\t\t\t\t\"shard %lu\", shardId)));\n}\n\n\n/*\n * CopyGetPlacementConnection assigns a connection to the given placement. If\n * a connection has already been assigned the placement in the current transaction\n * then it reuses the connection. Otherwise, it requests a connection for placement.\n */\nstatic MultiConnection *\nCopyGetPlacementConnection(HTAB *connectionStateHash, ShardPlacement *placement,\n\t\t\t\t\t\t   bool colocatedIntermediateResult)\n{\n\tif (colocatedIntermediateResult)\n\t{\n\t\t/*\n\t\t * Colocated intermediate results are just files and not required to use\n\t\t * the same connections with their co-located shards. So, we are free to\n\t\t * use any connection we can get.\n\t\t *\n\t\t * Also, the current connection re-use logic does not know how to handle\n\t\t * intermediate results as the intermediate results always truncates the\n\t\t * existing files. That's why we we use one connection per intermediate\n\t\t * result.\n\t\t *\n\t\t * Also note that we are breaking the guarantees of citus.shared_pool_size\n\t\t * as we cannot rely on optional connections.\n\t\t */\n\t\tuint32 connectionFlagsForIntermediateResult = 0;\n\t\tMultiConnection *connection =\n\t\t\tGetNodeConnection(connectionFlagsForIntermediateResult, placement->nodeName,\n\t\t\t\t\t\t\t  placement->nodePort);\n\n\t\t/*\n\t\t * As noted above, we want each intermediate file to go over\n\t\t * a separate connection.\n\t\t */\n\t\tClaimConnectionExclusively(connection);\n\n\t\t/* and, we cannot afford to handle failures when anything goes wrong */\n\t\tMarkRemoteTransactionCritical(connection);\n\n\t\treturn connection;\n\t}\n\n\t/*\n\t * Determine whether the task has to be assigned to a particular connection\n\t * due to a preceding access to the placement in the same transaction.\n\t */\n\tShardPlacementAccess *placementAccess = CreatePlacementAccess(placement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  PLACEMENT_ACCESS_DML);\n\tuint32 connectionFlags = FOR_DML;\n\tMultiConnection *connection =\n\t\tGetConnectionIfPlacementAccessedInXact(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t   list_make1(placementAccess), NULL);\n\tif (connection != NULL)\n\t{\n\t\t/*\n\t\t * Errors are supposed to cause immediate aborts (i.e. we don't\n\t\t * want to/can't invalidate placements), mark the connection as\n\t\t * critical so later errors cause failures.\n\t\t */\n\t\tMarkRemoteTransactionCritical(connection);\n\n\t\treturn connection;\n\t}\n\n\t/*\n\t * If we exceeded citus.max_adaptive_executor_pool_size, we should re-use the\n\t * existing connections to multiplex multiple COPY commands on shards over a\n\t * single connection.\n\t */\n\tchar *nodeName = placement->nodeName;\n\tint nodePort = placement->nodePort;\n\tList *copyConnectionStateList =\n\t\tConnectionStateListToNode(connectionStateHash, nodeName, nodePort);\n\tif (HasReachedAdaptiveExecutorPoolSize(copyConnectionStateList))\n\t{\n\t\t/*\n\t\t * If we've already reached the executor pool size, there should be at\n\t\t * least one connection to any given node.\n\t\t *\n\t\t * Note that we don't need to mark the connection as critical, since the\n\t\t * connection was already returned by this function before.\n\t\t */\n\t\tconnection = GetLeastUtilisedCopyConnection(copyConnectionStateList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tnodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tnodePort);\n\n\t\t/*\n\t\t * Make sure that the connection management remembers that Citus\n\t\t * accesses this placement over the connection.\n\t\t */\n\t\tAssignPlacementListToConnection(list_make1(placementAccess), connection);\n\n\t\treturn connection;\n\t}\n\n\tif (IsReservationPossible())\n\t{\n\t\t/*\n\t\t * Enforce the requirements for adaptive connection management\n\t\t * (a.k.a., throttle connections if citus.max_shared_pool_size\n\t\t * reached).\n\t\t *\n\t\t * Given that we have done reservations per node, we do not ever\n\t\t * need to pass WAIT_FOR_CONNECTION, we are sure that there is a\n\t\t * connection either reserved for this backend or already established\n\t\t * by the previous commands in the same transaction block.\n\t\t */\n\t\tint adaptiveConnectionManagementFlag = OPTIONAL_CONNECTION;\n\t\tconnectionFlags |= adaptiveConnectionManagementFlag;\n\t}\n\n\n\t/*\n\t * For placements that haven't been assigned a connection by a previous command\n\t * in the current transaction, we use a separate connection per placement for\n\t * hash-distributed tables in order to get the maximum performance.\n\t */\n\tif (placement->partitionMethod == DISTRIBUTE_BY_HASH &&\n\t\tMultiShardConnectionType != SEQUENTIAL_CONNECTION)\n\t{\n\t\t/*\n\t\t * Claiming the connection exclusively (done below) would also have the\n\t\t * effect of opening multiple connections, but claiming the connection\n\t\t * exclusively prevents GetConnectionIfPlacementAccessedInXact from returning\n\t\t * the connection if it is needed for a different shard placement.\n\t\t *\n\t\t * By setting the REQUIRE_CLEAN_CONNECTION flag we are guaranteed to get\n\t\t * connection that will not be returned by GetConnectionIfPlacementAccessedInXact\n\t\t * for the remainder of the COPY, hence it safe to claim the connection\n\t\t * exclusively. Claiming a connection exclusively prevents it from being\n\t\t * used in other distributed queries that happen during the COPY (e.g. if\n\t\t * the copy logic calls a function to calculate a default value, and the\n\t\t * function does a distributed query).\n\t\t */\n\t\tconnectionFlags |= REQUIRE_CLEAN_CONNECTION;\n\t}\n\n\tchar *nodeUser = CurrentUserName();\n\tconnection = GetPlacementConnection(connectionFlags, placement, nodeUser);\n\tif (connection == NULL)\n\t{\n\t\tif (list_length(copyConnectionStateList) > 0)\n\t\t{\n\t\t\t/*\n\t\t\t * The connection manager throttled any new connections, so pick an existing\n\t\t\t * connection with least utilization.\n\t\t\t *\n\t\t\t * Note that we don't need to mark the connection as critical, since the\n\t\t\t * connection was already returned by this function before.\n\t\t\t */\n\t\t\tconnection =\n\t\t\t\tGetLeastUtilisedCopyConnection(copyConnectionStateList, nodeName,\n\t\t\t\t\t\t\t\t\t\t\t   nodePort);\n\n\t\t\t/*\n\t\t\t * Make sure that the connection management remembers that Citus\n\t\t\t * accesses this placement over the connection.\n\t\t\t */\n\t\t\tAssignPlacementListToConnection(list_make1(placementAccess), connection);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * For this COPY command, we have not established any connections\n\t\t\t * and adaptive connection management throttled the new connection\n\t\t\t * request. This could only happen if this COPY command is the\n\t\t\t * second (or later) COPY command in a transaction block as the\n\t\t\t * first COPY command always gets a connection per node thanks to\n\t\t\t * the connection reservation.\n\t\t\t *\n\t\t\t * As we know that there has been at least one COPY command happened\n\t\t\t * earlier, we need to find the connection to that node, and use it.\n\t\t\t */\n\t\t\tconnection =\n\t\t\t\tConnectionAvailableToNode(nodeName, nodePort, CurrentUserName(),\n\t\t\t\t\t\t\t\t\t\t  CurrentDatabaseName());\n\n\t\t\t/*\n\t\t\t * We do not expect this to happen, but still instead of an assert,\n\t\t\t * we prefer explicit error message.\n\t\t\t */\n\t\t\tif (connection == NULL)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"could not find an available connection\"),\n\t\t\t\t\t\t\t\terrhint(\"Set citus.max_shared_pool_size TO -1 to let \"\n\t\t\t\t\t\t\t\t\t\t\"COPY command finish\")));\n\t\t\t}\n\t\t}\n\n\t\treturn connection;\n\t}\n\n\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t{\n\t\tReportConnectionError(connection, ERROR);\n\t}\n\n\t/*\n\t * Errors are supposed to cause immediate aborts (i.e. we don't\n\t * want to/can't invalidate placements), mark the connection as\n\t * critical so later errors cause failures.\n\t */\n\tMarkRemoteTransactionCritical(connection);\n\n\tif (MultiShardConnectionType != SEQUENTIAL_CONNECTION)\n\t{\n\t\tClaimConnectionExclusively(connection);\n\t}\n\n\treturn connection;\n}\n\n\n/*\n * HasReachedAdaptiveExecutorPoolSize returns true if the number of entries in input\n * connection list has greater than or equal to citus.max_adaptive_executor_pool_size.\n */\nstatic bool\nHasReachedAdaptiveExecutorPoolSize(List *connectionStateList)\n{\n\tif (list_length(connectionStateList) >= MaxAdaptiveExecutorPoolSize)\n\t{\n\t\t/*\n\t\t * We've not reached MaxAdaptiveExecutorPoolSize number of\n\t\t * connections, so we're allowed to establish a new\n\t\t * connection to the given node.\n\t\t */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * GetLeastUtilisedCopyConnection returns a MultiConnection to the given node\n * with the least number of placements assigned to it.\n *\n * It is assumed that there exists at least one connection to the node.\n */\nstatic MultiConnection *\nGetLeastUtilisedCopyConnection(List *connectionStateList, char *nodeName,\n\t\t\t\t\t\t\t   int nodePort)\n{\n\tMultiConnection *connection = NULL;\n\tint minPlacementCount = PG_INT32_MAX;\n\tListCell *connectionStateCell = NULL;\n\n\t/*\n\t * We only pick the least utilised connection when some connection limits are\n\t * reached such as max_shared_pool_size or max_adaptive_executor_pool_size.\n\t *\n\t * Therefore there should be some connections to choose from.\n\t */\n\tAssert(list_length(connectionStateList) > 0);\n\n\tforeach(connectionStateCell, connectionStateList)\n\t{\n\t\tCopyConnectionState *connectionState = lfirst(connectionStateCell);\n\t\tint currentConnectionPlacementCount = connectionState->bufferedPlacementCount;\n\n\t\tif (connectionState->activePlacementState != NULL)\n\t\t{\n\t\t\tcurrentConnectionPlacementCount++;\n\t\t}\n\n\t\tAssert(currentConnectionPlacementCount > 0);\n\n\t\tif (currentConnectionPlacementCount < minPlacementCount)\n\t\t{\n\t\t\tminPlacementCount = currentConnectionPlacementCount;\n\t\t\tconnection = connectionState->connection;\n\t\t}\n\t}\n\n\treturn connection;\n}\n\n\n/*\n * StartPlacementStateCopyCommand sends the COPY for the given placement. It also\n * sends binary headers if this is a binary COPY.\n */\nstatic void\nStartPlacementStateCopyCommand(CopyPlacementState *placementState,\n\t\t\t\t\t\t\t   CopyStmt *copyStatement, CopyOutState copyOutState)\n{\n\tMultiConnection *connection = placementState->connectionState->connection;\n\tuint64 shardId = placementState->shardState->shardId;\n\tbool raiseInterrupts = true;\n\tbool binaryCopy = copyOutState->binary;\n\n\tStringInfo copyCommand = ConstructCopyStatement(copyStatement, shardId);\n\n\tif (!SendRemoteCommand(connection, copyCommand->data))\n\t{\n\t\tReportConnectionError(connection, ERROR);\n\t}\n\n\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\tif (PQresultStatus(result) != PGRES_COPY_IN)\n\t{\n\t\tReportResultError(connection, result, ERROR);\n\t}\n\n\tPQclear(result);\n\n\tif (binaryCopy)\n\t{\n\t\tSendCopyBinaryHeaders(copyOutState, shardId, list_make1(connection));\n\t}\n}\n\n\n/*\n * EndPlacementStateCopyCommand ends the COPY for the given placement. It also\n * sends binary footers if this is a binary COPY.\n */\nstatic void\nEndPlacementStateCopyCommand(CopyPlacementState *placementState,\n\t\t\t\t\t\t\t CopyOutState copyOutState)\n{\n\tMultiConnection *connection = placementState->connectionState->connection;\n\tuint64 shardId = placementState->shardState->shardId;\n\tbool binaryCopy = copyOutState->binary;\n\n\t/* send footers and end copy command */\n\tif (binaryCopy)\n\t{\n\t\tSendCopyBinaryFooters(copyOutState, shardId, list_make1(connection));\n\t}\n\n\tEndRemoteCopy(shardId, list_make1(connection));\n}\n\n\n/*\n * UnclaimCopyConnections unclaims all the connections used for COPY.\n */\nstatic void\nUnclaimCopyConnections(List *connectionStateList)\n{\n\tListCell *connectionStateCell = NULL;\n\n\tforeach(connectionStateCell, connectionStateList)\n\t{\n\t\tCopyConnectionState *connectionState = lfirst(connectionStateCell);\n\t\tUnclaimConnection(connectionState->connection);\n\t}\n}\n\n\n/*\n * IsDroppedOrGenerated - helper function for determining if an attribute is\n * dropped or generated. Used by COPY and Citus DDL to skip such columns.\n */\ninline bool\nIsDroppedOrGenerated(Form_pg_attribute attr)\n{\n\t/*\n\t * If the \"is dropped\" flag is true or the generated column flag\n\t * is not the default nul character (in which case its value is 's'\n\t * for ATTRIBUTE_GENERATED_STORED or possibly 'v' with PG18+ for\n\t * ATTRIBUTE_GENERATED_VIRTUAL) then return true.\n\t */\n\treturn attr->attisdropped || (attr->attgenerated != '\\0');\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/non_main_db_distribute_object_ops.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * non_main_db_distribute_object_ops.c\n *\n *    Routines to support node-wide object management commands from non-main\n *    databases.\n *\n *    RunPreprocessNonMainDBCommand and RunPostprocessNonMainDBCommand are\n *    the entrypoints for this module. These functions are called from\n *    utility_hook.c to support some of the node-wide object management\n *    commands from non-main databases.\n *\n *    To add support for a new command type, one needs to define a new\n *    NonMainDbDistributeObjectOps object within OperationArray. Also, if\n *    the command requires marking or unmarking some objects as distributed,\n *    the necessary operations can be implemented in\n *    RunPreprocessNonMainDBCommand and RunPostprocessNonMainDBCommand.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/xact.h\"\n#include \"catalog/pg_authid_d.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/remote_transaction.h\"\n\n\n#define EXECUTE_COMMAND_ON_REMOTE_NODES_AS_USER \\\n\t\t\"SELECT citus_internal.execute_command_on_remote_nodes_as_user(%s, %s)\"\n#define START_MANAGEMENT_TRANSACTION \\\n\t\t\"SELECT citus_internal.start_management_transaction('%lu')\"\n#define MARK_OBJECT_DISTRIBUTED \\\n\t\t\"SELECT citus_internal.mark_object_distributed(%d, %s, %d, %s)\"\n#define UNMARK_OBJECT_DISTRIBUTED \\\n\t\t\"SELECT pg_catalog.citus_unmark_object_distributed(%d, %d, %d, %s)\"\n\n\n/*\n * NonMainDbDistributeObjectOps contains the necessary callbacks / flags to\n * support node-wide object management commands from non-main databases.\n *\n *  cannotBeExecutedInTransaction:\n *   Indicates whether the statement cannot be executed in a transaction. If\n *   this is set to true, the statement will be executed directly on the main\n *   database because there are no transactional visibility issues for such\n *   commands.\n *\n *  checkSupportedObjectType:\n *   Callback function that checks whether type of the object referred to by\n *   given statement is supported. Can be NULL if not applicable for the\n *   statement type.\n */\ntypedef struct NonMainDbDistributeObjectOps\n{\n\tbool cannotBeExecutedInTransaction;\n\tbool (*checkSupportedObjectType)(Node *parsetree);\n} NonMainDbDistributeObjectOps;\n\n\n/*\n * checkSupportedObjectType callbacks for OperationArray.\n */\nstatic bool CreateDbStmtCheckSupportedObjectType(Node *node);\nstatic bool DropDbStmtCheckSupportedObjectType(Node *node);\nstatic bool GrantStmtCheckSupportedObjectType(Node *node);\nstatic bool SecLabelStmtCheckSupportedObjectType(Node *node);\n\n/*\n * OperationArray that holds NonMainDbDistributeObjectOps for different command types.\n */\nstatic const NonMainDbDistributeObjectOps *const OperationArray[] = {\n\t[T_CreateRoleStmt] = &(NonMainDbDistributeObjectOps) {\n\t\t.cannotBeExecutedInTransaction = false,\n\t\t.checkSupportedObjectType = NULL\n\t},\n\t[T_DropRoleStmt] = &(NonMainDbDistributeObjectOps) {\n\t\t.cannotBeExecutedInTransaction = false,\n\t\t.checkSupportedObjectType = NULL\n\t},\n\t[T_AlterRoleStmt] = &(NonMainDbDistributeObjectOps) {\n\t\t.cannotBeExecutedInTransaction = false,\n\t\t.checkSupportedObjectType = NULL\n\t},\n\t[T_GrantRoleStmt] = &(NonMainDbDistributeObjectOps) {\n\t\t.cannotBeExecutedInTransaction = false,\n\t\t.checkSupportedObjectType = NULL\n\t},\n\t[T_CreatedbStmt] = &(NonMainDbDistributeObjectOps) {\n\t\t.cannotBeExecutedInTransaction = true,\n\t\t.checkSupportedObjectType = CreateDbStmtCheckSupportedObjectType\n\t},\n\t[T_DropdbStmt] = &(NonMainDbDistributeObjectOps) {\n\t\t.cannotBeExecutedInTransaction = true,\n\t\t.checkSupportedObjectType = DropDbStmtCheckSupportedObjectType\n\t},\n\t[T_GrantStmt] = &(NonMainDbDistributeObjectOps) {\n\t\t.cannotBeExecutedInTransaction = false,\n\t\t.checkSupportedObjectType = GrantStmtCheckSupportedObjectType\n\t},\n\t[T_SecLabelStmt] = &(NonMainDbDistributeObjectOps) {\n\t\t.cannotBeExecutedInTransaction = false,\n\t\t.checkSupportedObjectType = SecLabelStmtCheckSupportedObjectType\n\t},\n};\n\n\n/* other static function declarations */\nconst NonMainDbDistributeObjectOps * GetNonMainDbDistributeObjectOps(Node *parsetree);\nstatic void CreateRoleStmtMarkDistGloballyOnMainDbs(CreateRoleStmt *createRoleStmt);\nstatic void DropRoleStmtUnmarkDistOnLocalMainDb(DropRoleStmt *dropRoleStmt);\nstatic void MarkObjectDistributedGloballyOnMainDbs(Oid catalogRelId, Oid objectId,\n\t\t\t\t\t\t\t\t\t\t\t\t   char *objectName);\nstatic void UnmarkObjectDistributedOnLocalMainDb(uint16 catalogRelId, Oid objectId);\n\n\n/*\n * RunPreprocessNonMainDBCommand runs the necessary commands for a query, in main\n * database before query is run on the local node with PrevProcessUtility.\n *\n * Returns true if previous utility hook needs to be skipped after completing\n * preprocess phase.\n */\nbool\nRunPreprocessNonMainDBCommand(Node *parsetree)\n{\n\tif (IsMainDB)\n\t{\n\t\treturn false;\n\t}\n\n\tconst NonMainDbDistributeObjectOps *ops = GetNonMainDbDistributeObjectOps(parsetree);\n\tif (!ops)\n\t{\n\t\treturn false;\n\t}\n\n\tchar *queryString = DeparseTreeNode(parsetree);\n\n\t/*\n\t * For the commands that cannot be executed in a transaction, there are no\n\t * transactional visibility issues. We directly route them to main database\n\t * so that we only have to consider one code-path for such commands.\n\t */\n\tif (ops->cannotBeExecutedInTransaction)\n\t{\n\t\tIsMainDBCommandInXact = false;\n\t\tRunCitusMainDBQuery((char *) queryString);\n\t\treturn true;\n\t}\n\n\tIsMainDBCommandInXact = true;\n\n\tStringInfo mainDBQuery = makeStringInfo();\n\tappendStringInfo(mainDBQuery,\n\t\t\t\t\t START_MANAGEMENT_TRANSACTION,\n\t\t\t\t\t GetCurrentFullTransactionId().value);\n\tRunCitusMainDBQuery(mainDBQuery->data);\n\n\tmainDBQuery = makeStringInfo();\n\tappendStringInfo(mainDBQuery,\n\t\t\t\t\t EXECUTE_COMMAND_ON_REMOTE_NODES_AS_USER,\n\t\t\t\t\t quote_literal_cstr(queryString),\n\t\t\t\t\t quote_literal_cstr(CurrentUserName()));\n\tRunCitusMainDBQuery(mainDBQuery->data);\n\n\tif (IsA(parsetree, DropRoleStmt))\n\t{\n\t\tDropRoleStmtUnmarkDistOnLocalMainDb((DropRoleStmt *) parsetree);\n\t}\n\n\treturn false;\n}\n\n\n/*\n * RunPostprocessNonMainDBCommand runs the necessary commands for a query, in main\n * database after query is run on the local node with PrevProcessUtility.\n */\nvoid\nRunPostprocessNonMainDBCommand(Node *parsetree)\n{\n\tif (IsMainDB || !GetNonMainDbDistributeObjectOps(parsetree))\n\t{\n\t\treturn;\n\t}\n\n\tif (IsA(parsetree, CreateRoleStmt))\n\t{\n\t\tCreateRoleStmtMarkDistGloballyOnMainDbs((CreateRoleStmt *) parsetree);\n\t}\n}\n\n\n/*\n * GetNonMainDbDistributeObjectOps returns the NonMainDbDistributeObjectOps for given\n * command if it's node-wide object management command that's supported from non-main\n * databases.\n */\nconst NonMainDbDistributeObjectOps *\nGetNonMainDbDistributeObjectOps(Node *parsetree)\n{\n\tNodeTag tag = nodeTag(parsetree);\n\tif (tag >= lengthof(OperationArray))\n\t{\n\t\treturn NULL;\n\t}\n\n\tconst NonMainDbDistributeObjectOps *ops = OperationArray[tag];\n\n\tif (ops == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (!ops->checkSupportedObjectType ||\n\t\tops->checkSupportedObjectType(parsetree))\n\t{\n\t\treturn ops;\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * CreateRoleStmtMarkDistGloballyOnMainDbs marks the role as\n * distributed on all main databases globally.\n */\nstatic void\nCreateRoleStmtMarkDistGloballyOnMainDbs(CreateRoleStmt *createRoleStmt)\n{\n\t/* object must exist as we've just created it */\n\tbool missingOk = false;\n\tOid roleId = get_role_oid(createRoleStmt->role, missingOk);\n\n\tMarkObjectDistributedGloballyOnMainDbs(AuthIdRelationId, roleId,\n\t\t\t\t\t\t\t\t\t\t   createRoleStmt->role);\n}\n\n\n/*\n * DropRoleStmtUnmarkDistOnLocalMainDb unmarks the roles as\n * distributed on the local main database.\n */\nstatic void\nDropRoleStmtUnmarkDistOnLocalMainDb(DropRoleStmt *dropRoleStmt)\n{\n\tRoleSpec *roleSpec = NULL;\n\tforeach_declared_ptr(roleSpec, dropRoleStmt->roles)\n\t{\n\t\tOid roleOid = get_role_oid(roleSpec->rolename,\n\t\t\t\t\t\t\t\t   dropRoleStmt->missing_ok);\n\t\tif (roleOid == InvalidOid)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tUnmarkObjectDistributedOnLocalMainDb(AuthIdRelationId, roleOid);\n\t}\n}\n\n\n/*\n * MarkObjectDistributedGloballyOnMainDbs marks an object as\n * distributed on all main databases globally.\n */\nstatic void\nMarkObjectDistributedGloballyOnMainDbs(Oid catalogRelId, Oid objectId, char *objectName)\n{\n\tStringInfo mainDBQuery = makeStringInfo();\n\tappendStringInfo(mainDBQuery,\n\t\t\t\t\t MARK_OBJECT_DISTRIBUTED,\n\t\t\t\t\t catalogRelId,\n\t\t\t\t\t quote_literal_cstr(objectName),\n\t\t\t\t\t objectId,\n\t\t\t\t\t quote_literal_cstr(CurrentUserName()));\n\tRunCitusMainDBQuery(mainDBQuery->data);\n}\n\n\n/*\n * UnmarkObjectDistributedOnLocalMainDb unmarks an object as\n * distributed on the local main database.\n */\nstatic void\nUnmarkObjectDistributedOnLocalMainDb(uint16 catalogRelId, Oid objectId)\n{\n\tconst int subObjectId = 0;\n\tconst char *checkObjectExistence = \"false\";\n\n\tStringInfo query = makeStringInfo();\n\tappendStringInfo(query,\n\t\t\t\t\t UNMARK_OBJECT_DISTRIBUTED,\n\t\t\t\t\t catalogRelId, objectId,\n\t\t\t\t\t subObjectId, checkObjectExistence);\n\tRunCitusMainDBQuery(query->data);\n}\n\n\n/*\n * checkSupportedObjectTypes callbacks for OperationArray lie below.\n */\nstatic bool\nCreateDbStmtCheckSupportedObjectType(Node *node)\n{\n\t/*\n\t * We don't try to send the query to the main database if the CREATE\n\t * DATABASE command is for the main database itself, this is a very\n\t * rare case but it's exercised by our test suite.\n\t */\n\tCreatedbStmt *stmt = castNode(CreatedbStmt, node);\n\treturn strcmp(stmt->dbname, MainDb) != 0;\n}\n\n\nstatic bool\nDropDbStmtCheckSupportedObjectType(Node *node)\n{\n\t/*\n\t * We don't try to send the query to the main database if the DROP\n\t * DATABASE command is for the main database itself, this is a very\n\t * rare case but it's exercised by our test suite.\n\t */\n\tDropdbStmt *stmt = castNode(DropdbStmt, node);\n\treturn strcmp(stmt->dbname, MainDb) != 0;\n}\n\n\nstatic bool\nGrantStmtCheckSupportedObjectType(Node *node)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\treturn stmt->objtype == OBJECT_DATABASE;\n}\n\n\nstatic bool\nSecLabelStmtCheckSupportedObjectType(Node *node)\n{\n\tSecLabelStmt *stmt = castNode(SecLabelStmt, node);\n\treturn stmt->objtype == OBJECT_ROLE;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/owned.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * owned.c\n *    Commands for DROP OWNED statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/table.h\"\n#include \"access/xact.h\"\n#include \"catalog/catalog.h\"\n#include \"catalog/objectaddress.h\"\n#include \"catalog/pg_auth_members.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_db_role_setting.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/dbcommands.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"parser/scansup.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n#include \"utils/varlena.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_transaction.h\"\n\n\nstatic ObjectAddress * GetNewRoleAddress(ReassignOwnedStmt *stmt);\n\n/*\n * PreprocessDropOwnedStmt finds the distributed role out of the ones\n * being dropped and unmarks them distributed and creates the drop statements\n * for the workers.\n */\nList *\nPreprocessDropOwnedStmt(Node *node, const char *queryString,\n\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tDropOwnedStmt *stmt = castNode(DropOwnedStmt, node);\n\tList *allDropRoles = stmt->roles;\n\n\tList *distributedDropRoles = FilterDistributedRoles(allDropRoles);\n\tif (list_length(distributedDropRoles) <= 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* check creation against multi-statement transaction policy */\n\tif (!ShouldPropagateCreateInCoordinatedTransction())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tstmt->roles = distributedDropRoles;\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\tstmt->roles = allDropRoles;\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * PostprocessReassignOwnedStmt takes a Node pointer representing a REASSIGN\n * OWNED statement and performs any necessary post-processing after the statement\n * has been executed locally.\n *\n * We filter out local roles in OWNED BY clause before deparsing the command,\n * meaning that we skip reassigning what is owned by local roles. However,\n * if the role specified in TO clause is local, we automatically distribute\n * it before deparsing the command.\n */\nList *\nPostprocessReassignOwnedStmt(Node *node, const char *queryString)\n{\n\tReassignOwnedStmt *stmt = castNode(ReassignOwnedStmt, node);\n\tList *allReassignRoles = stmt->roles;\n\n\tList *distributedReassignRoles = FilterDistributedRoles(allReassignRoles);\n\n\tif (list_length(distributedReassignRoles) <= 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tstmt->roles = distributedReassignRoles;\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\tstmt->roles = allReassignRoles;\n\n\tObjectAddress *newRoleAddress = GetNewRoleAddress(stmt);\n\n\t/*\n\t * We temporarily enable create / alter role propagation to properly\n\t * propagate the role specified in TO clause.\n\t */\n\tint saveNestLevel = NewGUCNestLevel();\n\tset_config_option(\"citus.enable_create_role_propagation\", \"on\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n\tset_config_option(\"citus.enable_alter_role_propagation\", \"on\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n\n\tset_config_option(\"citus.enable_alter_role_set_propagation\", \"on\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n\n\tEnsureObjectAndDependenciesExistOnAllNodes(newRoleAddress);\n\n\t/* rollback GUCs to the state before this session */\n\tAtEOXact_GUC(true, saveNestLevel);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * GetNewRoleAddress returns the ObjectAddress of the new role\n */\nstatic ObjectAddress *\nGetNewRoleAddress(ReassignOwnedStmt *stmt)\n{\n\tOid roleOid = get_role_oid(stmt->newrole->rolename, false);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, AuthIdRelationId, roleOid);\n\treturn address;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/policy.c",
    "content": "/*-------------------------------------------------------------------------\n * policy.c\n *\n * This file contains functions to create, alter and drop policies on\n * distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/namespace.h\"\n#include \"commands/policy.h\"\n#include \"nodes/makefuncs.h\"\n#include \"parser/parse_clause.h\"\n#include \"parser/parse_relation.h\"\n#include \"rewrite/rewriteManip.h\"\n#include \"rewrite/rowsecurity.h\"\n#include \"utils/builtins.h\"\n#include \"utils/ruleutils.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n\n\nstatic const char * unparse_policy_command(const char aclchar);\nstatic RowSecurityPolicy * GetPolicyByName(Oid relationId, const char *policyName);\nstatic List * GetPolicyListForRelation(Oid relationId);\nstatic char * CreatePolicyCommandForPolicy(Oid relationId, RowSecurityPolicy *policy);\n\n\n/*\n * CreatePolicyCommands takes in a relationId, and returns the list of create policy\n * commands needed to reconstruct the policies of that table.\n */\nList *\nCreatePolicyCommands(Oid relationId)\n{\n\tList *commands = NIL;\n\n\tList *policyList = GetPolicyListForRelation(relationId);\n\n\tRowSecurityPolicy *policy;\n\tforeach_declared_ptr(policy, policyList)\n\t{\n\t\tchar *createPolicyCommand = CreatePolicyCommandForPolicy(relationId, policy);\n\t\tcommands = lappend(commands, makeTableDDLCommandString(createPolicyCommand));\n\t}\n\n\treturn commands;\n}\n\n\n/*\n * GetPolicyListForRelation returns a list of RowSecurityPolicy objects identifying\n * the policies on the relation with relationId. Note that this function acquires\n * AccessShareLock on relation and does not release it in the end to make sure that\n * caller will process valid policies through the transaction.\n */\nstatic List *\nGetPolicyListForRelation(Oid relationId)\n{\n\tRelation relation = table_open(relationId, AccessShareLock);\n\n\tif (!relation_has_policies(relation))\n\t{\n\t\ttable_close(relation, NoLock);\n\n\t\treturn NIL;\n\t}\n\n\tif (relation->rd_rsdesc == NULL)\n\t{\n\t\t/*\n\t\t * there are policies, but since RLS is not enabled they are not loaded into\n\t\t * cache, we will do so here for us to access\n\t\t */\n\t\tRelationBuildRowSecurity(relation);\n\t}\n\n\tList *policyList = NIL;\n\n\tRowSecurityPolicy *policy;\n\tforeach_declared_ptr(policy, relation->rd_rsdesc->policies)\n\t{\n\t\tpolicyList = lappend(policyList, policy);\n\t}\n\n\ttable_close(relation, NoLock);\n\n\treturn policyList;\n}\n\n\n/*\n * CreatePolicyCommandForPolicy takes a relationId and a policy, returns\n * the CREATE POLICY command needed to reconstruct the policy identified\n * by the \"policy\" object on the relation with relationId.\n */\nstatic char *\nCreatePolicyCommandForPolicy(Oid relationId, RowSecurityPolicy *policy)\n{\n\tchar *relationName = generate_qualified_relation_name(relationId);\n\tList *relationContext = deparse_context_for(relationName, relationId);\n\n\tStringInfo createPolicyCommand = makeStringInfo();\n\n\tappendStringInfo(createPolicyCommand, \"CREATE POLICY %s ON %s FOR %s\",\n\t\t\t\t\t quote_identifier(policy->policy_name),\n\t\t\t\t\t relationName,\n\t\t\t\t\t unparse_policy_command(policy->polcmd));\n\n\n\tappendStringInfoString(createPolicyCommand, \" TO \");\n\n\t/*\n\t * iterate over all roles and append them to the ddl command with commas\n\t * separating the role names\n\t */\n\tOid *roles = (Oid *) ARR_DATA_PTR(policy->roles);\n\tfor (int roleIndex = 0; roleIndex < ARR_DIMS(policy->roles)[0]; roleIndex++)\n\t{\n\t\tconst char *roleName;\n\n\t\tif (roleIndex > 0)\n\t\t{\n\t\t\tappendStringInfoString(createPolicyCommand, \", \");\n\t\t}\n\n\t\tif (roles[roleIndex] == ACL_ID_PUBLIC)\n\t\t{\n\t\t\troleName = \"PUBLIC\";\n\t\t}\n\t\telse\n\t\t{\n\t\t\troleName = quote_identifier(GetUserNameFromId(roles[roleIndex], false));\n\t\t}\n\n\t\tappendStringInfoString(createPolicyCommand, roleName);\n\t}\n\n\tif (policy->qual)\n\t{\n\t\tchar *qualString = deparse_expression((Node *) (policy->qual),\n\t\t\t\t\t\t\t\t\t\t\t  relationContext, false, false);\n\t\tappendStringInfo(createPolicyCommand, \" USING (%s)\", qualString);\n\t}\n\n\tif (policy->with_check_qual)\n\t{\n\t\tchar *withCheckQualString = deparse_expression(\n\t\t\t(Node *) (policy->with_check_qual), relationContext, false, false);\n\t\tappendStringInfo(createPolicyCommand, \" WITH CHECK (%s)\",\n\t\t\t\t\t\t withCheckQualString);\n\t}\n\n\treturn createPolicyCommand->data;\n}\n\n\n/*\n * unparse_policy_command takes the type of a policy command and converts it to its full\n * command string. This function is the exact inverse of parse_policy_command that is in\n * postgres.\n */\nstatic const char *\nunparse_policy_command(const char aclchar)\n{\n\tswitch (aclchar)\n\t{\n\t\tcase '*':\n\t\t{\n\t\t\treturn \"ALL\";\n\t\t}\n\n\t\tcase ACL_SELECT_CHR:\n\t\t{\n\t\t\treturn \"SELECT\";\n\t\t}\n\n\t\tcase ACL_INSERT_CHR:\n\t\t{\n\t\t\treturn \"INSERT\";\n\t\t}\n\n\t\tcase ACL_UPDATE_CHR:\n\t\t{\n\t\t\treturn \"UPDATE\";\n\t\t}\n\n\t\tcase ACL_DELETE_CHR:\n\t\t{\n\t\t\treturn \"DELETE\";\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"unrecognized aclchar: %d\", aclchar);\n\t\t\treturn NULL;\n\t\t}\n\t}\n}\n\n\n/*\n * PostprocessCreatePolicyStmt determines when a CREATE POLICY statement involves\n * a distributed table. If so, it creates DDLJobs to encapsulate information\n * needed during the worker node portion of DDL execution before returning the\n * DDLJobs in a List. If no distributed table is involved, this returns NIL.\n */\nList *\nPostprocessCreatePolicyStmt(Node *node, const char *queryString)\n{\n\tCreatePolicyStmt *stmt = castNode(CreatePolicyStmt, node);\n\n\t/* load relation information */\n\tRangeVar *relvar = stmt->table;\n\tOid relationId = RangeVarGetRelid(relvar, NoLock, false);\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\tRelation relation = table_open(relationId, AccessShareLock);\n\n\n\tParseState *qual_pstate = make_parsestate(NULL);\n\tAddRangeTableEntryToQueryCompat(qual_pstate, relation);\n\tNode *qual = transformWhereClause(qual_pstate,\n\t\t\t\t\t\t\t\t\t  copyObject(stmt->qual),\n\t\t\t\t\t\t\t\t\t  EXPR_KIND_POLICY,\n\t\t\t\t\t\t\t\t\t  \"POLICY\");\n\tif (qual)\n\t{\n\t\tErrorIfUnsupportedPolicyExpr(qual);\n\t}\n\n\tParseState *with_check_pstate = make_parsestate(NULL);\n\tAddRangeTableEntryToQueryCompat(with_check_pstate, relation);\n\tNode *with_check_qual = transformWhereClause(with_check_pstate,\n\t\t\t\t\t\t\t\t\t\t\t\t copyObject(stmt->with_check),\n\t\t\t\t\t\t\t\t\t\t\t\t EXPR_KIND_POLICY,\n\t\t\t\t\t\t\t\t\t\t\t\t \"POLICY\");\n\tif (with_check_qual)\n\t{\n\t\tErrorIfUnsupportedPolicyExpr(with_check_qual);\n\t}\n\n\tRowSecurityPolicy *policy = GetPolicyByName(relationId, stmt->policy_name);\n\n\tif (policy == NULL)\n\t{\n\t\t/*\n\t\t * As this function is executed after standard process utility created the\n\t\t * policy, we should be able to find & deparse the policy with policy_name.\n\t\t * But to be more safe, error out here.\n\t\t */\n\t\tereport(ERROR, (errmsg(\"cannot create policy, policy does not exist.\")));\n\t}\n\n\tEnsureCoordinator();\n\n\tchar *ddlCommand = CreatePolicyCommandForPolicy(relationId, policy);\n\n\t/*\n\t * create the DDLJob that needs to be executed both on the local relation and all its\n\t * placements.\n\t */\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\tddlJob->metadataSyncCommand = pstrdup(ddlCommand);\n\tddlJob->taskList = DDLTaskList(relationId, ddlCommand);\n\n\trelation_close(relation, NoLock);\n\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * AddRangeTableEntryToQueryCompat adds the given relation to query.\n * This method is a compatibility wrapper.\n */\nvoid\nAddRangeTableEntryToQueryCompat(ParseState *parseState, Relation relation)\n{\n\tParseNamespaceItem *rte = addRangeTableEntryForRelation(parseState, relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAccessShareLock, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse, false);\n\taddNSItemToQuery(parseState, rte, false, true, true);\n}\n\n\n/*\n * GetPolicyByName takes a relationId and a policyName, returns RowSecurityPolicy\n * object which identifies the policy with name \"policyName\" on the relation\n * with relationId. If there does not exist such a policy, then this function\n * returns NULL.\n */\nstatic RowSecurityPolicy *\nGetPolicyByName(Oid relationId, const char *policyName)\n{\n\tList *policyList = GetPolicyListForRelation(relationId);\n\n\tRowSecurityPolicy *policy = NULL;\n\tforeach_declared_ptr(policy, policyList)\n\t{\n\t\tif (strncmp(policy->policy_name, policyName, NAMEDATALEN) == 0)\n\t\t{\n\t\t\treturn policy;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * PreprocessAlterPolicyStmt determines whether a given ALTER POLICY statement involves a\n * distributed table. If so, it creates DDLJobs to encapsulate information needed during\n * the worker node portion of DDL execution before returning the DDLJobs in a list. If no\n * distributed table is involved this returns NIL.\n */\nList *\nPreprocessAlterPolicyStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tAlterPolicyStmt *stmt = castNode(AlterPolicyStmt, node);\n\tStringInfoData ddlString;\n\tListCell *roleCell = NULL;\n\n\t/* load relation information */\n\tRangeVar *relvar = stmt->table;\n\tOid relOid = RangeVarGetRelid(relvar, NoLock, false);\n\tif (!IsCitusTable(relOid))\n\t{\n\t\treturn NIL;\n\t}\n\n\tinitStringInfo(&ddlString);\n\n\tRelation relation = relation_open(relOid, AccessShareLock);\n\tchar *relationName = generate_relation_name(relOid, NIL);\n\n\tappendStringInfo(&ddlString, \"ALTER POLICY %s ON %s\",\n\t\t\t\t\t quote_identifier(stmt->policy_name),\n\t\t\t\t\t relationName\n\t\t\t\t\t );\n\n\tif (stmt->roles)\n\t{\n\t\tappendStringInfoString(&ddlString, \" TO \");\n\t\tforeach(roleCell, stmt->roles)\n\t\t{\n\t\t\tRoleSpec *roleSpec = (RoleSpec *) lfirst(roleCell);\n\n\t\t\tappendStringInfoString(&ddlString, RoleSpecString(roleSpec, true));\n\n\t\t\tif (lnext(stmt->roles, roleCell) != NULL)\n\t\t\t{\n\t\t\t\tappendStringInfoString(&ddlString, \", \");\n\t\t\t}\n\t\t}\n\t}\n\n\tList *relationContext = deparse_context_for(relationName, relOid);\n\n\tParseState *qual_pstate = make_parsestate(NULL);\n\tAddRangeTableEntryToQueryCompat(qual_pstate, relation);\n\tNode *qual = transformWhereClause(qual_pstate,\n\t\t\t\t\t\t\t\t\t  copyObject(stmt->qual),\n\t\t\t\t\t\t\t\t\t  EXPR_KIND_POLICY,\n\t\t\t\t\t\t\t\t\t  \"POLICY\");\n\tif (qual)\n\t{\n\t\tErrorIfUnsupportedPolicyExpr(qual);\n\n\t\tchar *qualString = deparse_expression(qual, relationContext, false, false);\n\t\tappendStringInfo(&ddlString, \" USING (%s)\", qualString);\n\t}\n\n\tParseState *with_check_pstate = make_parsestate(NULL);\n\tAddRangeTableEntryToQueryCompat(with_check_pstate, relation);\n\tNode *with_check_qual = transformWhereClause(with_check_pstate,\n\t\t\t\t\t\t\t\t\t\t\t\t copyObject(stmt->with_check),\n\t\t\t\t\t\t\t\t\t\t\t\t EXPR_KIND_POLICY,\n\t\t\t\t\t\t\t\t\t\t\t\t \"POLICY\");\n\tif (with_check_qual)\n\t{\n\t\tErrorIfUnsupportedPolicyExpr(with_check_qual);\n\n\t\tchar *withCheckString = deparse_expression(with_check_qual, relationContext,\n\t\t\t\t\t\t\t\t\t\t\t\t   false,\n\t\t\t\t\t\t\t\t\t\t\t\t   false);\n\t\tappendStringInfo(&ddlString, \" WITH CHECK (%s)\", withCheckString);\n\t}\n\n\t/*\n\t * create the DDLJob that needs to be executed both on the local relation  and all its\n\t * placements.\n\t */\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relOid);\n\tddlJob->metadataSyncCommand = pstrdup(ddlString.data);\n\tddlJob->taskList = DDLTaskList(relOid, ddlString.data);\n\n\trelation_close(relation, NoLock);\n\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * ErrorIfUnsupportedPolicy runs checks related to a Relation their Policies and errors\n * out if it is not possible to create one of the policies in a distributed environment.\n *\n * To support policies we require that:\n * - Policy expressions do not contain subqueries.\n */\nvoid\nErrorIfUnsupportedPolicy(Relation relation)\n{\n\tListCell *policyCell = NULL;\n\n\tif (!relation_has_policies(relation))\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * even if a relation has policies they might not be loaded on the Relation yet. This\n\t * happens if policies are on a Relation without Row Level Security enabled. We need\n\t * to make sure the policies installed are valid for distribution if RLS gets enabled\n\t * after the table has been distributed. Therefore we force a build of the policies on\n\t * the cached Relation\n\t */\n\tif (relation->rd_rsdesc == NULL)\n\t{\n\t\tRelationBuildRowSecurity(relation);\n\t}\n\n\tforeach(policyCell, relation->rd_rsdesc->policies)\n\t{\n\t\tRowSecurityPolicy *policy = (RowSecurityPolicy *) lfirst(policyCell);\n\n\t\tErrorIfUnsupportedPolicyExpr((Node *) policy->qual);\n\t\tErrorIfUnsupportedPolicyExpr((Node *) policy->with_check_qual);\n\t}\n}\n\n\n/*\n * ErrorIfUnsupportedPolicyExpr tests if the provided expression for a policy is\n * supported on a distributed table.\n */\nvoid\nErrorIfUnsupportedPolicyExpr(Node *expr)\n{\n\t/*\n\t * We do not allow any sublink to prevent expressions with subqueries to be used as an\n\t * expression in policies on distributed tables.\n\t */\n\tif (checkExprHasSubLink(expr))\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t errmsg(\"cannot create policy\"),\n\t\t\t\t errdetail(\"Subqueries are not supported in policies on distributed \"\n\t\t\t\t\t\t   \"tables\")));\n\t}\n}\n\n\n/*\n * PreprocessDropPolicyStmt determines whether a given DROP POLICY statement involves a\n * distributed table. If so it creates DDLJobs to encapsulate information needed during\n * the worker node portion of DDL execution before returning the DDLJobs in a List. If no\n * distributed table is involved this returns NIL.\n */\nList *\nPreprocessDropPolicyStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tList *ddlJobs = NIL;\n\tListCell *dropObjectCell = NULL;\n\n\tAssert(stmt->removeType == OBJECT_POLICY);\n\n\tforeach(dropObjectCell, stmt->objects)\n\t{\n\t\tList *names = (List *) lfirst(dropObjectCell);\n\n\t\t/*\n\t\t * the last element in the list of names is the name of the policy. The ones\n\t\t * before are describing the relation. By removing the last item from the list we\n\t\t * can use makeRangeVarFromNameList to get to the relation. As list_truncate\n\t\t * changes the list in place we make a copy before.\n\t\t */\n\t\tnames = list_copy(names);\n\t\tnames = list_truncate(names, list_length(names) - 1);\n\t\tRangeVar *relation = makeRangeVarFromNameList(names);\n\n\t\tOid relOid = RangeVarGetRelid(relation, NoLock, false);\n\t\tif (!IsCitusTable(relOid))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\t\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relOid);\n\t\tddlJob->metadataSyncCommand = queryString;\n\t\tddlJob->taskList = DDLTaskList(relOid, queryString);\n\n\t\tddlJobs = lappend(ddlJobs, ddlJob);\n\t}\n\n\treturn ddlJobs;\n}\n\n\n/*\n * IsPolicyRenameStmt returns wherher the passed-in RenameStmt is one of the following\n * forms:\n *\n *   - ALTER POLICY ... ON ... RENAME TO ...\n */\nbool\nIsPolicyRenameStmt(RenameStmt *stmt)\n{\n\treturn stmt->renameType == OBJECT_POLICY;\n}\n\n\n/*\n * CreatePolicyEventExtendNames extends relation names in the given CreatePolicyStmt tree.\n * This function has side effects on the tree as the names are replaced inplace.\n */\nvoid\nCreatePolicyEventExtendNames(CreatePolicyStmt *stmt, const char *schemaName, uint64\n\t\t\t\t\t\t\t shardId)\n{\n\tRangeVar *relation = stmt->table;\n\tchar **relationName = &(relation->relname);\n\tchar **relationSchemaName = &(relation->schemaname);\n\n\t/* prefix with schema name if it is not added already */\n\tSetSchemaNameIfNotExist(relationSchemaName, schemaName);\n\n\tAppendShardIdToName(relationName, shardId);\n}\n\n\n/*\n * AlterPolicyEventExtendNames extends relation names in the given AlterPolicyStatement\n * tree. This function has side effects on the tree as the names are replaced inplace.\n */\nvoid\nAlterPolicyEventExtendNames(AlterPolicyStmt *stmt, const char *schemaName, uint64 shardId)\n{\n\tRangeVar *relation = stmt->table;\n\tchar **relationName = &(relation->relname);\n\tchar **relationSchemaName = &(relation->schemaname);\n\n\t/* prefix with schema name if it is not added already */\n\tSetSchemaNameIfNotExist(relationSchemaName, schemaName);\n\n\tAppendShardIdToName(relationName, shardId);\n}\n\n\n/*\n * RenamePolicyEventExtendNames extends relation names in the given RenameStmt tree. This\n * function has side effects on the tree as the names are replaced inline.\n */\nvoid\nRenamePolicyEventExtendNames(RenameStmt *stmt, const char *schemaName, uint64 shardId)\n{\n\tchar **relationName = &(stmt->relation->relname);\n\tchar **objectSchemaName = &(stmt->relation->schemaname);\n\n\t/* prefix with schema name if it is not added already */\n\tSetSchemaNameIfNotExist(objectSchemaName, schemaName);\n\n\tAppendShardIdToName(relationName, shardId);\n}\n\n\n/*\n * DropPolicyEventExtendNames extends relation names in the given DropStmt tree specific\n * to policies. This function has side effects on the tree as the names are replaced\n * inplace.\n */\nvoid\nDropPolicyEventExtendNames(DropStmt *dropStmt, const char *schemaName, uint64 shardId)\n{\n\tString *relationSchemaNameValue = NULL;\n\tString *relationNameValue = NULL;\n\n\tuint32 dropCount = list_length(dropStmt->objects);\n\tif (dropCount > 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot extend name for multiple drop objects\")));\n\t}\n\n\tList *relationNameList = (List *) linitial(dropStmt->objects);\n\tint relationNameListLength = list_length(relationNameList);\n\n\tswitch (relationNameListLength)\n\t{\n\t\tcase 2:\n\t\t{\n\t\t\trelationNameValue = linitial(relationNameList);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 3:\n\t\t{\n\t\t\trelationSchemaNameValue = linitial(relationNameList);\n\t\t\trelationNameValue = lsecond(relationNameList);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t errmsg(\"improper policy name: \\\"%s\\\"\",\n\t\t\t\t\t\t\tNameListToString(relationNameList))));\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* prefix with schema name if it is not added already */\n\tif (relationSchemaNameValue == NULL)\n\t{\n\t\tString *schemaNameValue = makeString(pstrdup(schemaName));\n\t\trelationNameList = lcons(schemaNameValue, relationNameList);\n\t}\n\n\tchar **relationName = &(strVal(relationNameValue));\n\tAppendShardIdToName(relationName, shardId);\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/publication.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * publication.c\n *    Commands for creating publications\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/pg_publication.h\"\n#include \"catalog/pg_publication_rel.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/worker_create_or_replace.h\"\n\n\nstatic CreatePublicationStmt * BuildCreatePublicationStmt(Oid publicationId);\nstatic PublicationObjSpec * BuildPublicationRelationObjSpec(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOid publicationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbool tableOnly);\nstatic void AppendPublishOptionList(StringInfo str, List *strings);\nstatic char * AlterPublicationOwnerCommand(Oid publicationId);\nstatic bool ShouldPropagateCreatePublication(CreatePublicationStmt *stmt);\nstatic List * ObjectAddressForPublicationName(char *publicationName, bool missingOk);\n\n\n/*\n * PostProcessCreatePublicationStmt handles CREATE PUBLICATION statements\n * that contain distributed tables.\n */\nList *\nPostProcessCreatePublicationStmt(Node *node, const char *queryString)\n{\n\tCreatePublicationStmt *stmt = castNode(CreatePublicationStmt, node);\n\n\tif (!ShouldPropagateCreatePublication(stmt))\n\t{\n\t\t/* should not propagate right now */\n\t\treturn NIL;\n\t}\n\n\t/* call into CreatePublicationStmtObjectAddress */\n\tList *publicationAddresses = GetObjectAddressListFromParseTree(node, false, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(publicationAddresses) == 1);\n\n\tif (IsAnyObjectAddressOwnedByExtension(publicationAddresses, NULL))\n\t{\n\t\t/* should not propagate publications owned by extensions */\n\t\treturn NIL;\n\t}\n\n\tEnsureAllObjectDependenciesExistOnAllNodes(publicationAddresses);\n\n\tconst ObjectAddress *pubAddress = linitial(publicationAddresses);\n\n\tList *commands = NIL;\n\tcommands = lappend(commands, DISABLE_DDL_PROPAGATION);\n\tcommands = lappend(commands, CreatePublicationDDLCommand(pubAddress->objectId));\n\tcommands = lappend(commands, ENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * CreatePublicationDDLCommandsIdempotent returns a list of DDL statements to be\n * executed on a node to recreate the publication addressed by the publicationAddress.\n */\nList *\nCreatePublicationDDLCommandsIdempotent(const ObjectAddress *publicationAddress)\n{\n\tAssert(publicationAddress->classId == PublicationRelationId);\n\n\tchar *ddlCommand =\n\t\tCreatePublicationDDLCommand(publicationAddress->objectId);\n\n\tchar *alterPublicationOwnerSQL =\n\t\tAlterPublicationOwnerCommand(publicationAddress->objectId);\n\n\treturn list_make2(\n\t\tWrapCreateOrReplace(ddlCommand),\n\t\talterPublicationOwnerSQL);\n}\n\n\n/*\n * CreatePublicationDDLCommand returns the CREATE PUBLICATION string that\n * can be used to recreate a given publication.\n */\nchar *\nCreatePublicationDDLCommand(Oid publicationId)\n{\n\tCreatePublicationStmt *createPubStmt = BuildCreatePublicationStmt(publicationId);\n\n\t/* we took the WHERE clause from the catalog where it is already transformed */\n\tbool whereClauseRequiresTransform = false;\n\n\t/* only propagate Citus tables in publication */\n\tbool includeLocalTables = false;\n\n\treturn DeparseCreatePublicationStmtExtended((Node *) createPubStmt,\n\t\t\t\t\t\t\t\t\t\t\t\twhereClauseRequiresTransform,\n\t\t\t\t\t\t\t\t\t\t\t\tincludeLocalTables);\n}\n\n\n/*\n * BuildCreatePublicationStmt constructs a CreatePublicationStmt struct for the\n * given publication.\n */\nstatic CreatePublicationStmt *\nBuildCreatePublicationStmt(Oid publicationId)\n{\n\tCreatePublicationStmt *createPubStmt = makeNode(CreatePublicationStmt);\n\n\tHeapTuple publicationTuple =\n\t\tSearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(publicationId));\n\n\tif (!HeapTupleIsValid(publicationTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot find publication with oid: %d\", publicationId)));\n\t}\n\n\tForm_pg_publication publicationForm =\n\t\t(Form_pg_publication) GETSTRUCT(publicationTuple);\n\n\t/* CREATE PUBLICATION <name> */\n\tcreatePubStmt->pubname = pstrdup(NameStr(publicationForm->pubname));\n\n\t/* FOR ALL TABLES */\n\tcreatePubStmt->for_all_tables = publicationForm->puballtables;\n\n\tReleaseSysCache(publicationTuple);\n\n\tList *schemaIds = GetPublicationSchemas(publicationId);\n\tOid schemaId = InvalidOid;\n\n\tforeach_declared_oid(schemaId, schemaIds)\n\t{\n\t\tchar *schemaName = get_namespace_name(schemaId);\n\n\t\tPublicationObjSpec *publicationObject = makeNode(PublicationObjSpec);\n\t\tpublicationObject->pubobjtype = PUBLICATIONOBJ_TABLES_IN_SCHEMA;\n\t\tpublicationObject->pubtable = NULL;\n\t\tpublicationObject->name = schemaName;\n\t\tpublicationObject->location = -1;\n\n\t\tcreatePubStmt->pubobjects = lappend(createPubStmt->pubobjects, publicationObject);\n\t}\n\n\tList *relationIds = GetPublicationRelations(publicationId,\n\t\t\t\t\t\t\t\t\t\t\t\tpublicationForm->pubviaroot ?\n\t\t\t\t\t\t\t\t\t\t\t\tPUBLICATION_PART_ROOT :\n\t\t\t\t\t\t\t\t\t\t\t\tPUBLICATION_PART_LEAF);\n\tOid relationId = InvalidOid;\n\n\t/* mainly for consistent ordering in test output */\n\trelationIds = SortList(relationIds, CompareOids);\n\n\tforeach_declared_oid(relationId, relationIds)\n\t{\n\t\tbool tableOnly = false;\n\n\t\t/* since postgres 15, tables can have a column list and filter */\n\t\tPublicationObjSpec *publicationObject =\n\t\t\tBuildPublicationRelationObjSpec(relationId, publicationId, tableOnly);\n\n\t\tcreatePubStmt->pubobjects = lappend(createPubStmt->pubobjects, publicationObject);\n\t}\n\n\t/* WITH (publish_via_partition_root = true) option */\n\tbool publishViaRoot = publicationForm->pubviaroot;\n\tchar *publishViaRootString = publishViaRoot ? \"true\" : \"false\";\n\tDefElem *pubViaRootOption = makeDefElem(\"publish_via_partition_root\",\n\t\t\t\t\t\t\t\t\t\t\t(Node *) makeString(publishViaRootString),\n\t\t\t\t\t\t\t\t\t\t\t-1);\n\tcreatePubStmt->options = lappend(createPubStmt->options, pubViaRootOption);\n\n/* WITH (publish_generated_columns = ...) option (PG18+) */\n#if PG_VERSION_NUM >= PG_VERSION_18\n\tif (publicationForm->pubgencols == 's')    /* stored */\n\t{\n\t\tDefElem *pubGenColsOption =\n\t\t\tmakeDefElem(\"publish_generated_columns\",\n\t\t\t\t\t\t(Node *) makeString(\"stored\"),\n\t\t\t\t\t\t-1);\n\n\t\tcreatePubStmt->options =\n\t\t\tlappend(createPubStmt->options, pubGenColsOption);\n\t}\n\telse if (publicationForm->pubgencols != 'n') /* 'n' = none (default) */\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"unexpected pubgencols value '%c' for publication %u\",\n\t\t\t\t\t\tpublicationForm->pubgencols, publicationId)));\n\t}\n#endif\n\n\n\t/* WITH (publish = 'insert, update, delete, truncate') option */\n\tList *publishList = NIL;\n\n\tif (publicationForm->pubinsert)\n\t{\n\t\tpublishList = lappend(publishList, makeString(\"insert\"));\n\t}\n\n\tif (publicationForm->pubupdate)\n\t{\n\t\tpublishList = lappend(publishList, makeString(\"update\"));\n\t}\n\n\tif (publicationForm->pubdelete)\n\t{\n\t\tpublishList = lappend(publishList, makeString(\"delete\"));\n\t}\n\n\tif (publicationForm->pubtruncate)\n\t{\n\t\tpublishList = lappend(publishList, makeString(\"truncate\"));\n\t}\n\n\tif (list_length(publishList) > 0)\n\t{\n\t\tStringInfo optionValue = makeStringInfo();\n\t\tAppendPublishOptionList(optionValue, publishList);\n\n\t\tDefElem *publishOption = makeDefElem(\"publish\",\n\t\t\t\t\t\t\t\t\t\t\t (Node *) makeString(optionValue->data), -1);\n\t\tcreatePubStmt->options = lappend(createPubStmt->options, publishOption);\n\t}\n\n\n\treturn createPubStmt;\n}\n\n\n/*\n * AppendPublishOptionList appends a list of publication options in\n * comma-separate form.\n */\nstatic void\nAppendPublishOptionList(StringInfo str, List *options)\n{\n\tListCell *stringCell = NULL;\n\tforeach(stringCell, options)\n\t{\n\t\tconst char *string = strVal(lfirst(stringCell));\n\t\tif (stringCell != list_head(options))\n\t\t{\n\t\t\tappendStringInfoString(str, \", \");\n\t\t}\n\n\t\t/* we cannot escape these strings */\n\t\tappendStringInfoString(str, string);\n\t}\n}\n\n\n/*\n * BuildPublicationRelationObjSpec returns a PublicationObjSpec that\n * can be included in a CREATE or ALTER PUBLICATION statement.\n */\nstatic PublicationObjSpec *\nBuildPublicationRelationObjSpec(Oid relationId, Oid publicationId,\n\t\t\t\t\t\t\t\tbool tableOnly)\n{\n\tHeapTuple pubRelationTuple = SearchSysCache2(PUBLICATIONRELMAP,\n\t\t\t\t\t\t\t\t\t\t\t\t ObjectIdGetDatum(relationId),\n\t\t\t\t\t\t\t\t\t\t\t\t ObjectIdGetDatum(publicationId));\n\tif (!HeapTupleIsValid(pubRelationTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot find relation with oid %d in publication \"\n\t\t\t\t\t\t\t   \"with oid %d\", relationId, publicationId)));\n\t}\n\n\tList *columnNameList = NIL;\n\tNode *whereClause = NULL;\n\n\t/* build the column list  */\n\tif (!tableOnly)\n\t{\n\t\tbool isNull = false;\n\t\tDatum attributesDatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubRelationTuple,\n\t\t\t\t\t\t\t\t\t\t\t\tAnum_pg_publication_rel_prattrs,\n\t\t\t\t\t\t\t\t\t\t\t\t&isNull);\n\t\tif (!isNull)\n\t\t{\n\t\t\tArrayType *attributesArray = DatumGetArrayTypeP(attributesDatum);\n\t\t\tint attributeCount = ARR_DIMS(attributesArray)[0];\n\t\t\tint16 *elems = (int16 *) ARR_DATA_PTR(attributesArray);\n\n\t\t\tfor (int attNumIndex = 0; attNumIndex < attributeCount; attNumIndex++)\n\t\t\t{\n\t\t\t\tAttrNumber attributeNumber = elems[attNumIndex];\n\t\t\t\tchar *columnName = get_attname(relationId, attributeNumber, false);\n\n\t\t\t\tcolumnNameList = lappend(columnNameList, makeString(columnName));\n\t\t\t}\n\t\t}\n\n\t\t/* build the WHERE clause */\n\t\tDatum whereClauseDatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubRelationTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t Anum_pg_publication_rel_prqual,\n\t\t\t\t\t\t\t\t\t\t\t\t &isNull);\n\t\tif (!isNull)\n\t\t{\n\t\t\t/*\n\t\t\t * We use the already-transformed parse tree form here, which does\n\t\t\t * not match regular CreatePublicationStmt\n\t\t\t */\n\t\t\twhereClause = stringToNode(TextDatumGetCString(whereClauseDatum));\n\t\t}\n\t}\n\n\tReleaseSysCache(pubRelationTuple);\n\n\tchar *schemaName = get_namespace_name(get_rel_namespace(relationId));\n\tchar *tableName = get_rel_name(relationId);\n\tRangeVar *rangeVar = makeRangeVar(schemaName, tableName, -1);\n\n\t/* build the FOR TABLE */\n\tPublicationTable *publicationTable =\n\t\tmakeNode(PublicationTable);\n\tpublicationTable->relation = rangeVar;\n\tpublicationTable->whereClause = whereClause;\n\tpublicationTable->columns = columnNameList;\n\n\tPublicationObjSpec *publicationObject = makeNode(PublicationObjSpec);\n\tpublicationObject->pubobjtype = PUBLICATIONOBJ_TABLE;\n\tpublicationObject->pubtable = publicationTable;\n\tpublicationObject->name = NULL;\n\tpublicationObject->location = -1;\n\n\treturn publicationObject;\n}\n\n\n/*\n * PreprocessAlterPublicationStmt handles ALTER PUBLICATION statements\n * in a way that is mostly similar to PreprocessAlterDistributedObjectStmt,\n * except we do not ensure sequential mode (publications do not interact with\n * shards) and can handle NULL deparse commands for ALTER PUBLICATION commands\n * that only involve local tables.\n */\nList *\nPreprocessAlterPublicationStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tList *addresses = GetObjectAddressListFromParseTree(stmt, false, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(addresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tQualifyTreeNode(stmt);\n\n\tconst char *sql = DeparseTreeNode((Node *) stmt);\n\tif (sql == NULL)\n\t{\n\t\t/*\n\t\t * Deparsing logic decided that there is nothing to propagate, e.g.\n\t\t * because the command only concerns local tables.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * GetAlterPublicationDDLCommandsForTable gets a list of ALTER PUBLICATION .. ADD/DROP\n * commands for the given table.\n *\n * If isAdd is true, it return ALTER PUBLICATION .. ADD TABLE commands for all\n * publications.\n *\n * Otherwise, it returns ALTER PUBLICATION  .. DROP TABLE commands for all\n * publications.\n */\nList *\nGetAlterPublicationDDLCommandsForTable(Oid relationId, bool isAdd)\n{\n\tList *commands = NIL;\n\n\tList *publicationIds = GetRelationPublications(relationId);\n\tOid publicationId = InvalidOid;\n\n\tforeach_declared_oid(publicationId, publicationIds)\n\t{\n\t\tchar *command = GetAlterPublicationTableDDLCommand(publicationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   relationId, isAdd);\n\n\t\tcommands = lappend(commands, command);\n\t}\n\n\treturn commands;\n}\n\n\n/*\n * GetAlterPublicationTableDDLCommand generates an ALTer PUBLICATION .. ADD/DROP TABLE\n * command for the given publication and relation ID.\n *\n * If isAdd is true, it return an ALTER PUBLICATION .. ADD TABLE command.\n * Otherwise, it returns ALTER PUBLICATION  .. DROP TABLE command.\n */\nchar *\nGetAlterPublicationTableDDLCommand(Oid publicationId, Oid relationId,\n\t\t\t\t\t\t\t\t   bool isAdd)\n{\n\tHeapTuple pubTuple = SearchSysCache1(PUBLICATIONOID,\n\t\t\t\t\t\t\t\t\t\t ObjectIdGetDatum(publicationId));\n\tif (!HeapTupleIsValid(pubTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot find publication with oid: %d\",\n\t\t\t\t\t\t\t   publicationId)));\n\t}\n\n\tForm_pg_publication pubForm = (Form_pg_publication) GETSTRUCT(pubTuple);\n\n\tAlterPublicationStmt *alterPubStmt = makeNode(AlterPublicationStmt);\n\talterPubStmt->pubname = pstrdup(NameStr(pubForm->pubname));\n\n\tReleaseSysCache(pubTuple);\n\n\tbool tableOnly = !isAdd;\n\n\t/* since postgres 15, tables can have a column list and filter */\n\tPublicationObjSpec *publicationObject =\n\t\tBuildPublicationRelationObjSpec(relationId, publicationId, tableOnly);\n\n\talterPubStmt->pubobjects = lappend(alterPubStmt->pubobjects, publicationObject);\n\talterPubStmt->action = isAdd ? AP_AddObjects : AP_DropObjects;\n\n\t/* we take the WHERE clause from the catalog where it is already transformed */\n\tbool whereClauseNeedsTransform = false;\n\n\t/*\n\t * We use these commands to restore publications before/after transforming a\n\t * table, including transformations to/from local tables.\n\t */\n\tbool includeLocalTables = true;\n\n\tchar *command = DeparseAlterPublicationStmtExtended((Node *) alterPubStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twhereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tincludeLocalTables);\n\n\treturn command;\n}\n\n\n/*\n * AlterPublicationOwnerCommand returns \"ALTER PUBLICATION .. OWNER TO ..\"\n * statement for the specified publication.\n */\nstatic char *\nAlterPublicationOwnerCommand(Oid publicationId)\n{\n\tHeapTuple publicationTuple =\n\t\tSearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(publicationId));\n\n\tif (!HeapTupleIsValid(publicationTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot find publication with oid: %d\",\n\t\t\t\t\t\t\t   publicationId)));\n\t}\n\n\tForm_pg_publication publicationForm =\n\t\t(Form_pg_publication) GETSTRUCT(publicationTuple);\n\n\tchar *publicationName = NameStr(publicationForm->pubname);\n\tOid publicationOwnerId = publicationForm->pubowner;\n\n\tchar *publicationOwnerName = GetUserNameFromId(publicationOwnerId, false);\n\n\tStringInfo alterCommand = makeStringInfo();\n\tappendStringInfo(alterCommand, \"ALTER PUBLICATION %s OWNER TO %s\",\n\t\t\t\t\t quote_identifier(publicationName),\n\t\t\t\t\t quote_identifier(publicationOwnerName));\n\n\tReleaseSysCache(publicationTuple);\n\n\treturn alterCommand->data;\n}\n\n\n/*\n * ShouldPropagateCreatePublication tests if we need to propagate a CREATE PUBLICATION\n * statement.\n */\nstatic bool\nShouldPropagateCreatePublication(CreatePublicationStmt *stmt)\n{\n\tif (!ShouldPropagate())\n\t{\n\t\treturn false;\n\t}\n\n\tif (!ShouldPropagateCreateInCoordinatedTransction())\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * AlterPublicationStmtObjectAddress generates the object address for the\n * publication altered by a regular ALTER PUBLICATION .. statement.\n */\nList *\nAlterPublicationStmtObjectAddress(Node *node, bool missingOk, bool isPostProcess)\n{\n\tAlterPublicationStmt *stmt = castNode(AlterPublicationStmt, node);\n\n\treturn ObjectAddressForPublicationName(stmt->pubname, missingOk);\n}\n\n\n/*\n * AlterPublicationOwnerStmtObjectAddress generates the object address for the\n * publication altered by the given ALTER PUBLICATION .. OWNER TO statement.\n */\nList *\nAlterPublicationOwnerStmtObjectAddress(Node *node, bool missingOk, bool isPostProcess)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\n\treturn ObjectAddressForPublicationName(strVal(stmt->object), missingOk);\n}\n\n\n/*\n * CreatePublicationStmtObjectAddress generates the object address for the\n * publication created by the given CREATE PUBLICATION statement.\n */\nList *\nCreatePublicationStmtObjectAddress(Node *node, bool missingOk, bool isPostProcess)\n{\n\tCreatePublicationStmt *stmt = castNode(CreatePublicationStmt, node);\n\n\treturn ObjectAddressForPublicationName(stmt->pubname, missingOk);\n}\n\n\n/*\n * RenamePublicationStmtObjectAddress generates the object address for the\n * publication altered by the given ALter PUBLICATION .. RENAME TO statement.\n */\nList *\nRenamePublicationStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\n\treturn ObjectAddressForPublicationName(strVal(stmt->object), missingOk);\n}\n\n\n/*\n * ObjectAddressForPublicationName returns the object address for a given publication\n * name.\n */\nstatic List *\nObjectAddressForPublicationName(char *publicationName, bool missingOk)\n{\n\tOid publicationId = InvalidOid;\n\n\tHeapTuple publicationTuple =\n\t\tSearchSysCache1(PUBLICATIONNAME, CStringGetDatum(publicationName));\n\tif (HeapTupleIsValid(publicationTuple))\n\t{\n\t\tForm_pg_publication publicationForm =\n\t\t\t(Form_pg_publication) GETSTRUCT(publicationTuple);\n\t\tpublicationId = publicationForm->oid;\n\n\t\tReleaseSysCache(publicationTuple);\n\t}\n\telse if (!missingOk)\n\t{\n\t\t/* it should have just been created */\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t\terrmsg(\"publication \\\"%s\\\" does not exist\", publicationName)));\n\t}\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, PublicationRelationId, publicationId);\n\n\treturn list_make1(address);\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/rename.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * rename.c\n *    Commands for renaming objects related to distributed tables\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/index.h\"\n#include \"catalog/namespace.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/metadata_cache.h\"\n\n\n/*\n * PreprocessRenameStmt first determines whether a given rename statement involves\n * a distributed table. If so (and if it is supported, i.e. renames a column),\n * it creates a DDLJob to encapsulate information needed during the worker node\n * portion of DDL execution before returning that DDLJob in a List. If no dis-\n * tributed table is involved, this function returns NIL.\n */\nList *\nPreprocessRenameStmt(Node *node, const char *renameCommand,\n\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tRenameStmt *renameStmt = castNode(RenameStmt, node);\n\tOid objectRelationId = InvalidOid; /* SQL Object OID */\n\tOid tableRelationId = InvalidOid; /* Relation OID, maybe not the same. */\n\n\t/*\n\t * We only support some of the PostgreSQL supported RENAME statements, and\n\t * our list include only renaming table, index, policy and view (related) objects.\n\t */\n\tif (!IsAlterTableRenameStmt(renameStmt) &&\n\t\t!IsIndexRenameStmt(renameStmt) &&\n\t\t!IsPolicyRenameStmt(renameStmt) &&\n\t\t!IsViewRenameStmt(renameStmt))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * The lock levels here should be same as the ones taken in\n\t * RenameRelation(), renameatt() and RenameConstraint(). All statements\n\t * have identical lock levels except alter index rename.\n\t */\n\tLOCKMODE lockmode = (IsIndexRenameStmt(renameStmt)) ?\n\t\t\t\t\t\tShareUpdateExclusiveLock : AccessExclusiveLock;\n\tobjectRelationId = RangeVarGetRelid(renameStmt->relation, lockmode,\n\t\t\t\t\t\t\t\t\t\trenameStmt->missing_ok);\n\n\t/*\n\t * If the table does not exist, don't do anything here to allow PostgreSQL\n\t * to throw the appropriate error or notice message later.\n\t */\n\tif (!OidIsValid(objectRelationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * Check whether we are dealing with a sequence or view here and route queries\n\t * accordingly to the right processor function. We need to check both objects here\n\t * since PG supports targeting sequences and views with ALTER TABLE commands.\n\t */\n\tchar relKind = get_rel_relkind(objectRelationId);\n\tif (relKind == RELKIND_SEQUENCE)\n\t{\n\t\tRenameStmt *stmtCopy = copyObject(renameStmt);\n\t\tstmtCopy->renameType = OBJECT_SEQUENCE;\n\t\treturn PreprocessRenameSequenceStmt((Node *) stmtCopy, renameCommand,\n\t\t\t\t\t\t\t\t\t\t\tprocessUtilityContext);\n\t}\n\telse if (relKind == RELKIND_VIEW)\n\t{\n\t\tRenameStmt *stmtCopy = copyObject(renameStmt);\n\t\tstmtCopy->relationType = OBJECT_VIEW;\n\t\tif (stmtCopy->renameType == OBJECT_TABLE)\n\t\t{\n\t\t\tstmtCopy->renameType = OBJECT_VIEW;\n\t\t}\n\n\t\treturn PreprocessRenameViewStmt((Node *) stmtCopy, renameCommand,\n\t\t\t\t\t\t\t\t\t\tprocessUtilityContext);\n\t}\n\n\t/* we have no planning to do unless the table is distributed */\n\tswitch (renameStmt->renameType)\n\t{\n\t\tcase OBJECT_TABLE:\n\t\tcase OBJECT_FOREIGN_TABLE:\n\t\tcase OBJECT_COLUMN:\n\t\tcase OBJECT_TABCONSTRAINT:\n\t\tcase OBJECT_POLICY:\n\t\t{\n\t\t\tif (relKind == RELKIND_INDEX ||\n\t\t\t\trelKind == RELKIND_PARTITIONED_INDEX)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Although weird, postgres allows ALTER TABLE .. RENAME command\n\t\t\t\t * on indexes. We don't want to break non-distributed tables,\n\t\t\t\t * so allow.\n\t\t\t\t */\n\t\t\t\ttableRelationId = IndexGetRelation(objectRelationId, false);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* the target object is our tableRelationId. */\n\t\t\ttableRelationId = objectRelationId;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_INDEX:\n\t\t{\n\t\t\tif (relKind == RELKIND_RELATION ||\n\t\t\t\trelKind == RELKIND_PARTITIONED_TABLE)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Although weird, postgres allows ALTER INDEX .. RENAME command\n\t\t\t\t * on tables. We don't want to break non-distributed tables,\n\t\t\t\t * so allow.\n\t\t\t\t * Because of the weird syntax, we locked with wrong level, so relock\n\t\t\t\t * the relation to acquire true level of lock. Same logic\n\t\t\t\t * can be found in the function RenameRelation(RenameStmt) at tablecmds.c\n\t\t\t\t */\n\t\t\t\tUnlockRelationOid(objectRelationId, lockmode);\n\t\t\t\tobjectRelationId = RangeVarGetRelid(renameStmt->relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\tAccessExclusiveLock,\n\t\t\t\t\t\t\t\t\t\t\t\t\trenameStmt->missing_ok);\n\t\t\t\ttableRelationId = objectRelationId;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * here, objRelationId points to the index relation entry, and we\n\t\t\t * are interested into the entry of the table on which the index is\n\t\t\t * defined.\n\t\t\t */\n\t\t\ttableRelationId = IndexGetRelation(objectRelationId, false);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\t/*\n\t\t\t * Nodes that are not supported by Citus: we pass-through to the\n\t\t\t * main PostgreSQL executor. Any Citus-supported RenameStmt\n\t\t\t * renameType must appear above in the switch, explicitly.\n\t\t\t */\n\t\t\treturn NIL;\n\t\t}\n\t}\n\n\tbool isCitusRelation = IsCitusTable(tableRelationId);\n\tif (!isCitusRelation)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * We might ERROR out on some commands, but only for Citus tables.\n\t * That's why this test comes this late in the function.\n\t */\n\tErrorIfUnsupportedRenameStmt(renameStmt);\n\n\tif (renameStmt->renameType == OBJECT_TABLE ||\n\t\trenameStmt->renameType == OBJECT_FOREIGN_TABLE)\n\t{\n\t\tSwitchToSequentialAndLocalExecutionIfRelationNameTooLong(tableRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t renameStmt->newname);\n\t}\n\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, tableRelationId);\n\tddlJob->metadataSyncCommand = renameCommand;\n\tddlJob->taskList = DDLTaskList(tableRelationId, renameCommand);\n\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * ErrorIfUnsupportedRenameStmt errors out if the corresponding rename statement\n * operates on any part of a distributed table other than a column.\n *\n * Note: This function handles RenameStmt applied to relations handed by Citus.\n * At the moment of writing this comment, it could be either tables or indexes.\n */\nvoid\nErrorIfUnsupportedRenameStmt(RenameStmt *renameStmt)\n{\n\tif (IsAlterTableRenameStmt(renameStmt) &&\n\t\trenameStmt->renameType == OBJECT_TABCONSTRAINT)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"renaming constraints belonging to distributed tables is \"\n\t\t\t\t\t\t\t   \"currently unsupported\")));\n\t}\n}\n\n\n/*\n * PreprocessRenameAttributeStmt called for RenameStmt's that are targetting an attribute eg.\n * type attributes. Based on the relation type the attribute gets renamed it dispatches to\n * a specialized implementation if present, otherwise return an empty list for its DDLJobs\n */\nList *\nPreprocessRenameAttributeStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_ATTRIBUTE);\n\n\tswitch (stmt->relationType)\n\t{\n\t\tcase OBJECT_TYPE:\n\t\t{\n\t\t\treturn PreprocessRenameTypeAttributeStmt(node, queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t processUtilityContext);\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\t/* unsupported relation for attribute rename, do nothing */\n\t\t\treturn NIL;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/role.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * role.c\n *    Commands for ALTER ROLE statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/table.h\"\n#include \"access/xact.h\"\n#include \"catalog/catalog.h\"\n#include \"catalog/objectaddress.h\"\n#include \"catalog/pg_auth_members.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_db_role_setting.h\"\n#include \"catalog/pg_shseclabel.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/dbcommands.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"parser/scansup.h\"\n#include \"utils/acl.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/guc.h\"\n#include \"utils/guc_tables.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n#include \"utils/varlena.h\"\n\n#include \"pg_version_compat.h\"\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/comment.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_transaction.h\"\n\nstatic const char * ExtractEncryptedPassword(Oid roleOid);\nstatic const char * CreateAlterRoleIfExistsCommand(AlterRoleStmt *stmt);\nstatic const char * CreateAlterRoleSetIfExistsCommand(AlterRoleSetStmt *stmt);\nstatic char * CreateCreateOrAlterRoleCommand(const char *roleName,\n\t\t\t\t\t\t\t\t\t\t\t CreateRoleStmt *createRoleStmt,\n\t\t\t\t\t\t\t\t\t\t\t AlterRoleStmt *alterRoleStmt);\nstatic DefElem * makeDefElemInt(char *name, int value);\nstatic DefElem * makeDefElemBool(char *name, bool value);\nstatic List * GenerateRoleOptionsList(HeapTuple tuple);\nstatic List * GenerateGrantRoleStmtsFromOptions(RoleSpec *roleSpec, List *options);\nstatic List * GenerateGrantRoleStmtsOfRole(Oid roleid);\nstatic List * GenerateSecLabelOnRoleStmts(Oid roleid, char *rolename);\nstatic void EnsureSequentialModeForRoleDDL(void);\n\nstatic char * GetRoleNameFromDbRoleSetting(HeapTuple tuple,\n\t\t\t\t\t\t\t\t\t\t   TupleDesc DbRoleSettingDescription);\nstatic char * GetDatabaseNameFromDbRoleSetting(HeapTuple tuple,\n\t\t\t\t\t\t\t\t\t\t\t   TupleDesc DbRoleSettingDescription);\n#if PG_VERSION_NUM < PG_VERSION_17\nstatic Node * makeStringConst(char *str, int location);\n#endif\nstatic Node * makeIntConst(int val, int location);\nstatic Node * makeFloatConst(char *str, int location);\nstatic const char * WrapQueryInAlterRoleIfExistsCall(const char *query, RoleSpec *role);\nstatic VariableSetStmt * MakeVariableSetStmt(const char *config);\nstatic int ConfigGenericNameCompare(const void *lhs, const void *rhs);\nstatic List * RoleSpecToObjectAddress(RoleSpec *role, bool missing_ok);\n\n/* controlled via GUC */\nbool EnableCreateRolePropagation = true;\nbool EnableAlterRolePropagation = true;\nbool EnableAlterRoleSetPropagation = true;\n\n\n/*\n * AlterRoleStmtObjectAddress returns the ObjectAddress of the role in the\n * AlterRoleStmt. If missing_ok is set to false an error will be raised if postgres\n * was unable to find the role that was the target of the statement.\n */\nList *\nAlterRoleStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterRoleStmt *stmt = castNode(AlterRoleStmt, node);\n\treturn RoleSpecToObjectAddress(stmt->role, missing_ok);\n}\n\n\n/*\n * AlterRoleSetStmtObjectAddress returns the ObjectAddress of the role in the\n * AlterRoleSetStmt. If missing_ok is set to false an error will be raised if postgres\n * was unable to find the role that was the target of the statement.\n */\nList *\nAlterRoleSetStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterRoleSetStmt *stmt = castNode(AlterRoleSetStmt, node);\n\treturn RoleSpecToObjectAddress(stmt->role, missing_ok);\n}\n\n\n/*\n * RoleSpecToObjectAddress returns the ObjectAddress of a Role associated with a\n * RoleSpec. If missing_ok is set to false an error will be raised by postgres\n * explaining the Role could not be found.\n */\nstatic List *\nRoleSpecToObjectAddress(RoleSpec *role, bool missing_ok)\n{\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\n\tif (role != NULL)\n\t{\n\t\t/* roles can be NULL for statements on ALL roles eg. ALTER ROLE ALL SET ... */\n\t\tOid roleOid = get_rolespec_oid(role, missing_ok);\n\t\tObjectAddressSet(*address, AuthIdRelationId, roleOid);\n\t}\n\n\treturn list_make1(address);\n}\n\n\n/*\n * PostprocessAlterRoleStmt actually creates the plan we need to execute for alter\n * role statement. We need to do it this way because we need to use the encrypted\n * password, which is, in some cases, created at standardProcessUtility.\n */\nList *\nPostprocessAlterRoleStmt(Node *node, const char *queryString)\n{\n\tList *addresses = GetObjectAddressListFromParseTree(node, false, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(addresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (!EnableAlterRolePropagation)\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsurePropagationToCoordinator();\n\n\tAlterRoleStmt *stmt = castNode(AlterRoleStmt, node);\n\n\tDefElem *option = NULL;\n\tforeach_declared_ptr(option, stmt->options)\n\t{\n\t\tif (strcasecmp(option->defname, \"password\") == 0)\n\t\t{\n\t\t\tOid roleOid = get_rolespec_oid(stmt->role, true);\n\t\t\tconst char *encryptedPassword = ExtractEncryptedPassword(roleOid);\n\n\t\t\tif (encryptedPassword != NULL)\n\t\t\t{\n\t\t\t\tString *encryptedPasswordValue = makeString((char *) encryptedPassword);\n\t\t\t\toption->arg = (Node *) encryptedPasswordValue;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\toption->arg = NULL;\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\t}\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) CreateAlterRoleIfExistsCommand(stmt),\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(REMOTE_NODES, commands);\n}\n\n\n/*\n * PreprocessAlterRoleSetStmt actually creates the plan we need to execute for alter\n * role set statement.\n */\nList *\nPreprocessAlterRoleSetStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (!EnableAlterRoleSetPropagation)\n\t{\n\t\treturn NIL;\n\t}\n\n\tAlterRoleSetStmt *stmt = castNode(AlterRoleSetStmt, node);\n\n\t/* don't propagate if the statement is scoped to another database */\n\tif (stmt->database != NULL &&\n\t\tstrcmp(stmt->database, get_database_name(MyDatabaseId)) != 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *addresses = GetObjectAddressListFromParseTree(node, false, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\t/*\n\t * stmt->role could be NULL when the statement is on 'ALL' roles, we do propagate for\n\t * ALL roles. If it is not NULL the role is for a specific role. If that role is not\n\t * distributed we will not propagate the statement\n\t */\n\tif (stmt->role != NULL && !IsAnyObjectDistributed(addresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsurePropagationToCoordinator();\n\n\tQualifyTreeNode((Node *) stmt);\n\tconst char *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commandList = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t   (void *) sql,\n\t\t\t\t\t\t\t\t   ENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(REMOTE_NODES, commandList);\n}\n\n\n/*\n * CreateAlterRoleIfExistsCommand creates ALTER ROLE command, from the alter role node\n *  using the alter_role_if_exists() UDF.\n */\nstatic const char *\nCreateAlterRoleIfExistsCommand(AlterRoleStmt *stmt)\n{\n\tconst char *alterRoleQuery = DeparseTreeNode((Node *) stmt);\n\treturn WrapQueryInAlterRoleIfExistsCall(alterRoleQuery, stmt->role);\n}\n\n\n/*\n * CreateAlterRoleSetIfExistsCommand creates ALTER ROLE .. SET command, from the\n * AlterRoleSetStmt node.\n *\n * If the statement affects a single user, the query is wrapped in a\n * alter_role_if_exists() to make sure that it is run on workers that has a user\n * with the same name. If the query is a ALTER ROLE ALL .. SET query, the query\n * is sent to the workers as is.\n */\nstatic const char *\nCreateAlterRoleSetIfExistsCommand(AlterRoleSetStmt *stmt)\n{\n\tchar *alterRoleSetQuery = DeparseTreeNode((Node *) stmt);\n\n\t/* ALTER ROLE ALL .. SET queries should not be wrapped in a alter_role_if_exists() call */\n\tif (stmt->role == NULL)\n\t{\n\t\treturn alterRoleSetQuery;\n\t}\n\telse\n\t{\n\t\treturn WrapQueryInAlterRoleIfExistsCall(alterRoleSetQuery, stmt->role);\n\t}\n}\n\n\n/*\n * WrapQueryInAlterRoleIfExistsCall wraps a given query in a alter_role_if_exists()\n * UDF.\n */\nstatic const char *\nWrapQueryInAlterRoleIfExistsCall(const char *query, RoleSpec *role)\n{\n\tStringInfoData buffer = { 0 };\n\n\tconst char *roleName = RoleSpecString(role, false);\n\tinitStringInfo(&buffer);\n\tappendStringInfo(&buffer,\n\t\t\t\t\t \"SELECT alter_role_if_exists(%s, %s)\",\n\t\t\t\t\t quote_literal_cstr(roleName),\n\t\t\t\t\t quote_literal_cstr(query));\n\n\treturn buffer.data;\n}\n\n\n/*\n * CreateCreateOrAlterRoleCommand creates ALTER ROLE command, from the alter role node\n *  using the alter_role_if_exists() UDF.\n */\nstatic char *\nCreateCreateOrAlterRoleCommand(const char *roleName,\n\t\t\t\t\t\t\t   CreateRoleStmt *createRoleStmt,\n\t\t\t\t\t\t\t   AlterRoleStmt *alterRoleStmt)\n{\n\tStringInfoData createOrAlterRoleQueryBuffer = { 0 };\n\tconst char *createRoleQuery = \"null\";\n\tconst char *alterRoleQuery = \"null\";\n\n\tif (createRoleStmt != NULL)\n\t{\n\t\tcreateRoleQuery = quote_literal_cstr(DeparseTreeNode((Node *) createRoleStmt));\n\t}\n\n\tif (alterRoleStmt != NULL)\n\t{\n\t\talterRoleQuery = quote_literal_cstr(DeparseTreeNode((Node *) alterRoleStmt));\n\t}\n\n\tinitStringInfo(&createOrAlterRoleQueryBuffer);\n\tappendStringInfo(&createOrAlterRoleQueryBuffer,\n\t\t\t\t\t \"SELECT worker_create_or_alter_role(%s, %s, %s)\",\n\t\t\t\t\t quote_literal_cstr(roleName),\n\t\t\t\t\t createRoleQuery,\n\t\t\t\t\t alterRoleQuery);\n\n\treturn createOrAlterRoleQueryBuffer.data;\n}\n\n\n/*\n * ExtractEncryptedPassword extracts the encrypted password of a role. The function\n * gets the password from the pg_authid table.\n */\nstatic const char *\nExtractEncryptedPassword(Oid roleOid)\n{\n\tRelation pgAuthId = table_open(AuthIdRelationId, AccessShareLock);\n\tTupleDesc pgAuthIdDescription = RelationGetDescr(pgAuthId);\n\tHeapTuple tuple = SearchSysCache1(AUTHOID, roleOid);\n\tbool isNull = true;\n\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\treturn NULL;\n\t}\n\n\tDatum passwordDatum = heap_getattr(tuple, Anum_pg_authid_rolpassword,\n\t\t\t\t\t\t\t\t\t   pgAuthIdDescription, &isNull);\n\n\t/*\n\t * In PG, an empty password is treated the same as NULL.\n\t * So we propagate NULL password to the other nodes, even if\n\t * the user supplied an empty password\n\t */\n\n\tchar *passwordCstring = NULL;\n\tif (!isNull)\n\t{\n\t\tpasswordCstring = pstrdup(TextDatumGetCString(passwordDatum));\n\t}\n\n\ttable_close(pgAuthId, AccessShareLock);\n\tReleaseSysCache(tuple);\n\n\treturn passwordCstring;\n}\n\n\n/*\n * GenerateAlterRoleSetIfExistsCommandList generate a list of ALTER ROLE .. SET commands that\n * copies a role session defaults from the pg_db_role_settings table.\n */\nstatic List *\nGenerateAlterRoleSetIfExistsCommandList(HeapTuple tuple,\n\t\t\t\t\t\t\t\t\t\tTupleDesc DbRoleSettingDescription)\n{\n\tAlterRoleSetStmt *stmt = makeNode(AlterRoleSetStmt);\n\tList *commandList = NIL;\n\tbool isnull = false;\n\n\tchar *databaseName =\n\t\tGetDatabaseNameFromDbRoleSetting(tuple, DbRoleSettingDescription);\n\n\tif (databaseName != NULL)\n\t{\n\t\tstmt->database = databaseName;\n\t}\n\n\tchar *roleName = GetRoleNameFromDbRoleSetting(tuple, DbRoleSettingDescription);\n\n\tif (roleName != NULL)\n\t{\n\t\tstmt->role = makeNode(RoleSpec);\n\t\tstmt->role->location = -1;\n\t\tstmt->role->roletype = ROLESPEC_CSTRING;\n\t\tstmt->role->rolename = roleName;\n\t}\n\n\tDatum setconfig = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig,\n\t\t\t\t\t\t\t\t   DbRoleSettingDescription, &isnull);\n\n\tDatum *configs;\n\tint nconfigs;\n\tint i;\n\n\tdeconstruct_array(DatumGetArrayTypeP(setconfig),\n\t\t\t\t\t  TEXTOID, -1, false, 'i',\n\t\t\t\t\t  &configs, NULL, &nconfigs);\n\n\t/*\n\t * A tuple might contain one or more settings that apply to the user-database combination.\n\t * ALTER ROLE ... SET ... only allows to set one at a time. We will create a statement for every\n\t * configuration contained in the tuple.\n\t */\n\tfor (i = 0; i < nconfigs; i++)\n\t{\n\t\tchar *config = TextDatumGetCString(configs[i]);\n\t\tstmt->setstmt = MakeVariableSetStmt(config);\n\t\tcommandList = lappend(commandList,\n\t\t\t\t\t\t\t  (void *) CreateAlterRoleSetIfExistsCommand(stmt));\n\t}\n\treturn commandList;\n}\n\n\n/*\n * MakeVariableSetStmt takes a \"some-option=some value\" string and creates a\n * VariableSetStmt Node.\n */\nstatic VariableSetStmt *\nMakeVariableSetStmt(const char *config)\n{\n\tchar *name = NULL;\n\tchar *value = NULL;\n\n\tParseLongOption(config, &name, &value);\n\n\tVariableSetStmt *variableSetStmt = makeNode(VariableSetStmt);\n\tvariableSetStmt->kind = VAR_SET_VALUE;\n\tvariableSetStmt->name = name;\n\tvariableSetStmt->args = MakeSetStatementArguments(name, value);\n\n\treturn variableSetStmt;\n}\n\n\n/*\n * GenerateRoleOptionsList returns the list of options set on a user based on the record\n * in pg_authid. It requires the HeapTuple for a user entry to access both its fixed\n * length and variable length fields.\n */\nstatic List *\nGenerateRoleOptionsList(HeapTuple tuple)\n{\n\tForm_pg_authid role = ((Form_pg_authid) GETSTRUCT(tuple));\n\n\tList *options = NIL;\n\toptions = lappend(options, makeDefElemBool(\"superuser\", role->rolsuper));\n\toptions = lappend(options, makeDefElemBool(\"createdb\", role->rolcreatedb));\n\toptions = lappend(options, makeDefElemBool(\"createrole\", role->rolcreaterole));\n\toptions = lappend(options, makeDefElemBool(\"inherit\", role->rolinherit));\n\toptions = lappend(options, makeDefElemBool(\"canlogin\", role->rolcanlogin));\n\toptions = lappend(options, makeDefElemBool(\"isreplication\", role->rolreplication));\n\toptions = lappend(options, makeDefElemBool(\"bypassrls\", role->rolbypassrls));\n\toptions = lappend(options, makeDefElemInt(\"connectionlimit\", role->rolconnlimit));\n\n\t/* load password from heap tuple, use NULL if not set */\n\tbool isNull = true;\n\tDatum rolPasswordDatum = SysCacheGetAttr(AUTHNAME, tuple, Anum_pg_authid_rolpassword,\n\t\t\t\t\t\t\t\t\t\t\t &isNull);\n\tif (!isNull)\n\t{\n\t\tchar *rolPassword = pstrdup(TextDatumGetCString(rolPasswordDatum));\n\t\tNode *passwordStringNode = (Node *) makeString(rolPassword);\n\t\tDefElem *passwordOption = makeDefElem(\"password\", passwordStringNode, -1);\n\t\toptions = lappend(options, passwordOption);\n\t}\n\telse\n\t{\n\t\toptions = lappend(options, makeDefElem(\"password\", NULL, -1));\n\t}\n\n\t/* load valid until data from the heap tuple */\n\tDatum rolValidUntilDatum = SysCacheGetAttr(AUTHNAME, tuple,\n\t\t\t\t\t\t\t\t\t\t\t   Anum_pg_authid_rolvaliduntil, &isNull);\n\tif (!isNull)\n\t{\n\t\tchar *rolValidUntil = pstrdup((char *) timestamptz_to_str(rolValidUntilDatum));\n\n\t\tNode *validUntilStringNode = (Node *) makeString(rolValidUntil);\n\t\tDefElem *validUntilOption = makeDefElem(\"validUntil\", validUntilStringNode, -1);\n\t\toptions = lappend(options, validUntilOption);\n\t}\n\n\treturn options;\n}\n\n\n/*\n * GenerateCreateOrAlterRoleCommand generates ALTER ROLE command that copies a role from\n * the pg_authid table.\n */\nList *\nGenerateCreateOrAlterRoleCommand(Oid roleOid)\n{\n\tHeapTuple roleTuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid));\n\tForm_pg_authid role = ((Form_pg_authid) GETSTRUCT(roleTuple));\n\tchar *rolename = pstrdup(NameStr(role->rolname));\n\n\tCreateRoleStmt *createRoleStmt = NULL;\n\tif (EnableCreateRolePropagation)\n\t{\n\t\tcreateRoleStmt = makeNode(CreateRoleStmt);\n\t\tcreateRoleStmt->stmt_type = ROLESTMT_ROLE;\n\t\tcreateRoleStmt->role = rolename;\n\t\tcreateRoleStmt->options = GenerateRoleOptionsList(roleTuple);\n\t}\n\n\tAlterRoleStmt *alterRoleStmt = NULL;\n\tif (EnableAlterRolePropagation)\n\t{\n\t\talterRoleStmt = makeNode(AlterRoleStmt);\n\t\talterRoleStmt->role = makeNode(RoleSpec);\n\t\talterRoleStmt->role->roletype = ROLESPEC_CSTRING;\n\t\talterRoleStmt->role->location = -1;\n\t\talterRoleStmt->role->rolename = rolename;\n\t\talterRoleStmt->action = 1;\n\t\talterRoleStmt->options = GenerateRoleOptionsList(roleTuple);\n\t}\n\n\tReleaseSysCache(roleTuple);\n\n\tList *completeRoleList = NIL;\n\tif (createRoleStmt != NULL || alterRoleStmt != NULL)\n\t{\n\t\t/* add a worker_create_or_alter_role command if any of them are set */\n\t\tchar *createOrAlterRoleQuery = CreateCreateOrAlterRoleCommand(\n\t\t\trolename,\n\t\t\tcreateRoleStmt,\n\t\t\talterRoleStmt);\n\n\t\tcompleteRoleList = lappend(completeRoleList, createOrAlterRoleQuery);\n\t}\n\n\tif (EnableAlterRoleSetPropagation)\n\t{\n\t\t/* append ALTER ROLE ... SET commands fot this specific user */\n\t\tList *alterRoleSetCommands = GenerateAlterRoleSetCommandForRole(roleOid);\n\t\tcompleteRoleList = list_concat(completeRoleList, alterRoleSetCommands);\n\t}\n\n\tif (EnableCreateRolePropagation)\n\t{\n\t\tList *grantRoleStmts = GenerateGrantRoleStmtsOfRole(roleOid);\n\t\tNode *stmt = NULL;\n\t\tforeach_declared_ptr(stmt, grantRoleStmts)\n\t\t{\n\t\t\tcompleteRoleList = lappend(completeRoleList, DeparseTreeNode(stmt));\n\t\t}\n\n\t\t/*\n\t\t * append SECURITY LABEL ON ROLE commands for this specific user\n\t\t * When we propagate user creation, we also want to make sure that we propagate\n\t\t * all the security labels it has been given. For this, we check pg_shseclabel\n\t\t * for the ROLE entry corresponding to roleOid, and generate the relevant\n\t\t * SecLabel stmts to be run in the new node.\n\t\t */\n\t\tList *secLabelOnRoleStmts = GenerateSecLabelOnRoleStmts(roleOid, rolename);\n\t\tstmt = NULL;\n\t\tforeach_declared_ptr(stmt, secLabelOnRoleStmts)\n\t\t{\n\t\t\tcompleteRoleList = lappend(completeRoleList, DeparseTreeNode(stmt));\n\t\t}\n\n\t\t/*\n\t\t * append COMMENT ON ROLE commands for this specific user\n\t\t * When we propagate user creation, we also want to make sure that we propagate\n\t\t * all the comments it has been given. For this, we check pg_shdescription\n\t\t * for the ROLE entry corresponding to roleOid, and generate the relevant\n\t\t * Comment stmts to be run in the new node.\n\t\t */\n\t\tList *commentStmts = GetCommentPropagationCommands(AuthIdRelationId, roleOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   rolename, OBJECT_ROLE);\n\t\tcompleteRoleList = list_concat(completeRoleList, commentStmts);\n\t}\n\n\treturn completeRoleList;\n}\n\n\n/*\n * GenerateAlterRoleSetCommandForRole returns the list of database wide settings for a\n * specifc role. If the roleid is InvalidOid it returns the commands that apply to all\n * users for the database or postgres wide.\n */\nList *\nGenerateAlterRoleSetCommandForRole(Oid roleid)\n{\n\tRelation DbRoleSetting = table_open(DbRoleSettingRelationId, AccessShareLock);\n\tTupleDesc DbRoleSettingDescription = RelationGetDescr(DbRoleSetting);\n\tHeapTuple tuple = NULL;\n\tList *commands = NIL;\n\n\n\tTableScanDesc scan = table_beginscan_catalog(DbRoleSetting, 0, NULL);\n\n\twhile ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)\n\t{\n\t\tForm_pg_db_role_setting roleSetting = (Form_pg_db_role_setting) GETSTRUCT(tuple);\n\t\tif (roleSetting->setrole != roleid)\n\t\t{\n\t\t\t/* not the user we are looking for */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (OidIsValid(roleSetting->setdatabase) &&\n\t\t\troleSetting->setdatabase != MyDatabaseId)\n\t\t{\n\t\t\t/* setting is database specific for a different database */\n\t\t\tcontinue;\n\t\t}\n\n\t\tList *alterRoleSetQueries = GenerateAlterRoleSetIfExistsCommandList(tuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tDbRoleSettingDescription);\n\t\tcommands = list_concat(commands, alterRoleSetQueries);\n\t}\n\n\theap_endscan(scan);\n\ttable_close(DbRoleSetting, AccessShareLock);\n\n\treturn commands;\n}\n\n\n/*\n * makeDefElemInt creates a DefElem with integer typed value with -1 as location.\n */\nstatic DefElem *\nmakeDefElemInt(char *name, int value)\n{\n\treturn makeDefElem(name, (Node *) makeInteger(value), -1);\n}\n\n\n/*\n * makeDefElemBool creates a DefElem with boolean typed value with -1 as location.\n */\nstatic DefElem *\nmakeDefElemBool(char *name, bool value)\n{\n\treturn makeDefElem(name, (Node *) makeBoolean(value), -1);\n}\n\n\n/*\n * GetDatabaseNameFromDbRoleSetting performs a lookup, and finds the database name\n * associated DbRoleSetting Tuple\n */\nstatic char *\nGetDatabaseNameFromDbRoleSetting(HeapTuple tuple, TupleDesc DbRoleSettingDescription)\n{\n\tbool isnull;\n\n\tDatum setdatabase = heap_getattr(tuple, Anum_pg_db_role_setting_setdatabase,\n\t\t\t\t\t\t\t\t\t DbRoleSettingDescription, &isnull);\n\n\tif (isnull)\n\t{\n\t\treturn NULL;\n\t}\n\n\tOid databaseId = DatumGetObjectId(setdatabase);\n\tchar *databaseName = get_database_name(databaseId);\n\n\treturn databaseName;\n}\n\n\n/*\n * GetRoleNameFromDbRoleSetting performs a lookup, and finds the role name\n * associated DbRoleSetting Tuple\n */\nstatic char *\nGetRoleNameFromDbRoleSetting(HeapTuple tuple, TupleDesc DbRoleSettingDescription)\n{\n\tbool isnull;\n\n\tDatum setrole = heap_getattr(tuple, Anum_pg_db_role_setting_setrole,\n\t\t\t\t\t\t\t\t DbRoleSettingDescription, &isnull);\n\n\tif (isnull)\n\t{\n\t\treturn NULL;\n\t}\n\n\tOid roleId = DatumGetObjectId(setrole);\n\tchar *roleName = GetUserNameFromId(roleId, true);\n\n\treturn roleName;\n}\n\n\n/*\n * MakeSetStatementArgs parses a configuration value and creates an List of A_Const\n * Nodes with appropriate types.\n *\n * The allowed A_Const types are Integer, Float, and String.\n */\nList *\nMakeSetStatementArguments(char *configurationName, char *configurationValue)\n{\n\tList *args = NIL;\n\tchar **key = &configurationName;\n\n\t/* Perform a lookup on GUC variables to find the config type and units.\n\t * All user-defined GUCs have string values, but we need to perform a search\n\t * on all the GUCs to understand if it is a user-defined one or not.\n\t *\n\t * Note: get_guc_variables() is intended for internal use only, but there\n\t * is no other way to determine allowed units, and value types other than\n\t * using this function\n\t */\n\tint gucCount = 0;\n\tstruct config_generic **gucVariables = get_guc_variables(&gucCount);\n\n\tstruct config_generic **matchingConfig =\n\t\t(struct config_generic **) SafeBsearch((void *) &key,\n\t\t\t\t\t\t\t\t\t\t\t   (void *) gucVariables,\n\t\t\t\t\t\t\t\t\t\t\t   gucCount,\n\t\t\t\t\t\t\t\t\t\t\t   sizeof(struct config_generic *),\n\t\t\t\t\t\t\t\t\t\t\t   ConfigGenericNameCompare);\n\n\t/* If the config is not user-defined, lookup the variable type to contruct the arguments */\n\tif (matchingConfig != NULL)\n\t{\n\t\tswitch ((*matchingConfig)->vartype)\n\t\t{\n\t\t\t/* We use postgresql parser so that we will parse the units only if\n\t\t\t * the configuration paramater allows it.\n\t\t\t *\n\t\t\t * e.g. `SET statement_timeout = '1min'` will be parsed as 60000 since\n\t\t\t * the value is stored in units of ms internally.\n\t\t\t */\n\t\t\tcase PGC_INT:\n\t\t\t{\n\t\t\t\tint intValue;\n\t\t\t\tparse_int(configurationValue, &intValue,\n\t\t\t\t\t\t  (*matchingConfig)->flags, NULL);\n\t\t\t\tNode *arg = makeIntConst(intValue, -1);\n\t\t\t\targs = lappend(args, arg);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PGC_REAL:\n\t\t\t{\n\t\t\t\tNode *arg = makeFloatConst(configurationValue, -1);\n\t\t\t\targs = lappend(args, arg);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PGC_BOOL:\n\t\t\tcase PGC_STRING:\n\t\t\tcase PGC_ENUM:\n\t\t\t{\n\t\t\t\tList *configurationList = NIL;\n\n\t\t\t\tif ((*matchingConfig)->flags & GUC_LIST_INPUT)\n\t\t\t\t{\n\t\t\t\t\tchar *configurationValueCopy = pstrdup(configurationValue);\n\t\t\t\t\tSplitIdentifierString(configurationValueCopy, ',',\n\t\t\t\t\t\t\t\t\t\t  &configurationList);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tconfigurationList = list_make1(configurationValue);\n\t\t\t\t}\n\n\t\t\t\tchar *configuration = NULL;\n\t\t\t\tforeach_declared_ptr(configuration, configurationList)\n\t\t\t\t{\n\t\t\t\t\tNode *arg = makeStringConst(configuration, -1);\n\t\t\t\t\targs = lappend(args, arg);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"Unrecognized run-time parameter type for %s\",\n\t\t\t\t\t\t\t\t\t   configurationName)));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tNode *arg = makeStringConst(configurationValue, -1);\n\t\targs = lappend(args, arg);\n\t}\n\treturn args;\n}\n\n\n/*\n * GenerateGrantRoleStmtsFromOptions gets a RoleSpec of a role that is being\n * created and a list of options of CreateRoleStmt to generate GrantRoleStmts\n * for the role's memberships.\n */\nstatic List *\nGenerateGrantRoleStmtsFromOptions(RoleSpec *roleSpec, List *options)\n{\n\tList *stmts = NIL;\n\n\tDefElem *option = NULL;\n\tforeach_declared_ptr(option, options)\n\t{\n\t\tif (strcmp(option->defname, \"adminmembers\") != 0 &&\n\t\t\tstrcmp(option->defname, \"rolemembers\") != 0 &&\n\t\t\tstrcmp(option->defname, \"addroleto\") != 0)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tGrantRoleStmt *grantRoleStmt = makeNode(GrantRoleStmt);\n\t\tgrantRoleStmt->is_grant = true;\n\n\t\tif (strcmp(option->defname, \"adminmembers\") == 0 || strcmp(option->defname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"rolemembers\") == 0)\n\t\t{\n\t\t\tgrantRoleStmt->granted_roles = list_make1(roleSpec);\n\t\t\tgrantRoleStmt->grantee_roles = (List *) option->arg;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tgrantRoleStmt->granted_roles = (List *) option->arg;\n\t\t\tgrantRoleStmt->grantee_roles = list_make1(roleSpec);\n\t\t}\n\n\t\tif (strcmp(option->defname, \"adminmembers\") == 0)\n\t\t{\n\t\t\tDefElem *opt = makeDefElem(\"admin\", (Node *) makeBoolean(true), -1);\n\t\t\tgrantRoleStmt->opt = list_make1(opt);\n\t\t}\n\n\t\tstmts = lappend(stmts, grantRoleStmt);\n\t}\n\treturn stmts;\n}\n\n\n/*\n * GenerateGrantRoleStmtsOfRole generates the GrantRoleStmts for the memberships\n * of the role whose oid is roleid.\n */\nstatic List *\nGenerateGrantRoleStmtsOfRole(Oid roleid)\n{\n\tRelation pgAuthMembers = table_open(AuthMemRelationId, AccessShareLock);\n\tHeapTuple tuple = NULL;\n\tList *stmts = NIL;\n\n\tScanKeyData skey[1];\n\n\tScanKeyInit(&skey[0], Anum_pg_auth_members_member, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(roleid));\n\tSysScanDesc scan = systable_beginscan(pgAuthMembers, AuthMemMemRoleIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, 1, &skey[0]);\n\n\twhile (HeapTupleIsValid(tuple = systable_getnext(scan)))\n\t{\n\t\tForm_pg_auth_members membership = (Form_pg_auth_members) GETSTRUCT(tuple);\n\n\t\tObjectAddress *roleAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*roleAddress, AuthIdRelationId, membership->grantor);\n\t\tif (!IsAnyObjectDistributed(list_make1(roleAddress)))\n\t\t{\n\t\t\t/* we only need to propagate the grant if the grantor is distributed */\n\t\t\tcontinue;\n\t\t}\n\n\t\tGrantRoleStmt *grantRoleStmt = makeNode(GrantRoleStmt);\n\t\tgrantRoleStmt->is_grant = true;\n\n\t\tRoleSpec *grantedRole = makeNode(RoleSpec);\n\t\tgrantedRole->roletype = ROLESPEC_CSTRING;\n\t\tgrantedRole->location = -1;\n\t\tgrantedRole->rolename = GetUserNameFromId(membership->roleid, true);\n\t\tgrantRoleStmt->granted_roles = list_make1(grantedRole);\n\n\t\tRoleSpec *granteeRole = makeNode(RoleSpec);\n\t\tgranteeRole->roletype = ROLESPEC_CSTRING;\n\t\tgranteeRole->location = -1;\n\t\tgranteeRole->rolename = GetUserNameFromId(membership->member, true);\n\t\tgrantRoleStmt->grantee_roles = list_make1(granteeRole);\n\n\t\tRoleSpec *grantorRole = makeNode(RoleSpec);\n\t\tgrantorRole->roletype = ROLESPEC_CSTRING;\n\t\tgrantorRole->location = -1;\n\t\tgrantorRole->rolename = GetUserNameFromId(membership->grantor, false);\n\t\tgrantRoleStmt->grantor = grantorRole;\n\n\t\t/* inherit option is always included */\n\t\tDefElem *inherit_opt;\n\t\tif (membership->inherit_option)\n\t\t{\n\t\t\tinherit_opt = makeDefElem(\"inherit\", (Node *) makeBoolean(true), -1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tinherit_opt = makeDefElem(\"inherit\", (Node *) makeBoolean(false), -1);\n\t\t}\n\t\tgrantRoleStmt->opt = list_make1(inherit_opt);\n\n\t\t/* admin option is false by default, only include true case */\n\t\tif (membership->admin_option)\n\t\t{\n\t\t\tDefElem *admin_opt = makeDefElem(\"admin\", (Node *) makeBoolean(true), -1);\n\t\t\tgrantRoleStmt->opt = lappend(grantRoleStmt->opt, admin_opt);\n\t\t}\n\n\t\t/* set option is true by default, only include false case */\n\t\tif (!membership->set_option)\n\t\t{\n\t\t\tDefElem *set_opt = makeDefElem(\"set\", (Node *) makeBoolean(false), -1);\n\t\t\tgrantRoleStmt->opt = lappend(grantRoleStmt->opt, set_opt);\n\t\t}\n\n\t\tstmts = lappend(stmts, grantRoleStmt);\n\t}\n\n\tsystable_endscan(scan);\n\ttable_close(pgAuthMembers, AccessShareLock);\n\n\treturn stmts;\n}\n\n\n/*\n * GenerateSecLabelOnRoleStmts generates the SecLabelStmts for the role\n * whose oid is roleid.\n */\nstatic List *\nGenerateSecLabelOnRoleStmts(Oid roleid, char *rolename)\n{\n\tList *secLabelStmts = NIL;\n\n\t/*\n\t * Note that roles are shared database objects, therefore their\n\t * security labels are stored in pg_shseclabel instead of pg_seclabel.\n\t */\n\tRelation pg_shseclabel = table_open(SharedSecLabelRelationId, AccessShareLock);\n\tScanKeyData skey[1];\n\tScanKeyInit(&skey[0], Anum_pg_shseclabel_objoid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(roleid));\n\tSysScanDesc scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId,\n\t\t\t\t\t\t\t\t\t\t  true, NULL, 1, &skey[0]);\n\n\tHeapTuple tuple = NULL;\n\twhile (HeapTupleIsValid(tuple = systable_getnext(scan)))\n\t{\n\t\tSecLabelStmt *secLabelStmt = makeNode(SecLabelStmt);\n\t\tsecLabelStmt->objtype = OBJECT_ROLE;\n\t\tsecLabelStmt->object = (Node *) makeString(pstrdup(rolename));\n\n\t\tDatum datumArray[Natts_pg_shseclabel];\n\t\tbool isNullArray[Natts_pg_shseclabel];\n\n\t\theap_deform_tuple(tuple, RelationGetDescr(pg_shseclabel), datumArray,\n\t\t\t\t\t\t  isNullArray);\n\n\t\tsecLabelStmt->provider = TextDatumGetCString(\n\t\t\tdatumArray[Anum_pg_shseclabel_provider - 1]);\n\t\tsecLabelStmt->label = TextDatumGetCString(\n\t\t\tdatumArray[Anum_pg_shseclabel_label - 1]);\n\n\t\tsecLabelStmts = lappend(secLabelStmts, secLabelStmt);\n\t}\n\n\tsystable_endscan(scan);\n\ttable_close(pg_shseclabel, AccessShareLock);\n\n\treturn secLabelStmts;\n}\n\n\n/*\n * PreprocessCreateRoleStmt creates a worker_create_or_alter_role query for the\n * role that is being created. With that query we can create the role in the\n * workers or if they exist we alter them to the way they are being created\n * right now.\n */\nList *\nPreprocessCreateRoleStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tif (!EnableCreateRolePropagation || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsurePropagationToCoordinator();\n\n\tEnsureSequentialModeForRoleDDL();\n\n\tLockRelationOid(DistNodeRelationId(), RowShareLock);\n\n\tCreateRoleStmt *createRoleStmt = castNode(CreateRoleStmt, node);\n\n\tAlterRoleStmt *alterRoleStmt = makeNode(AlterRoleStmt);\n\talterRoleStmt->role = makeNode(RoleSpec);\n\talterRoleStmt->role->roletype = ROLESPEC_CSTRING;\n\talterRoleStmt->role->location = -1;\n\talterRoleStmt->role->rolename = pstrdup(createRoleStmt->role);\n\talterRoleStmt->action = 1;\n\talterRoleStmt->options = createRoleStmt->options;\n\n\tList *grantRoleStmts = GenerateGrantRoleStmtsFromOptions(alterRoleStmt->role,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t createRoleStmt->options);\n\n\tchar *createOrAlterRoleQuery = CreateCreateOrAlterRoleCommand(createRoleStmt->role,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  createRoleStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  alterRoleStmt);\n\n\tList *commands = NIL;\n\tcommands = lappend(commands, DISABLE_DDL_PROPAGATION);\n\tcommands = lappend(commands, createOrAlterRoleQuery);\n\n\t/* deparse all grant statements and add them to the commands list */\n\tNode *stmt = NULL;\n\tforeach_declared_ptr(stmt, grantRoleStmts)\n\t{\n\t\tcommands = lappend(commands, DeparseTreeNode(stmt));\n\t}\n\n\tcommands = lappend(commands, ENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(REMOTE_NODES, commands);\n}\n\n\n#if PG_VERSION_NUM < PG_VERSION_17\n\n/*\n * makeStringConst creates a Const Node that stores a given string\n *\n * copied from backend/parser/gram.c\n */\nstatic Node *\nmakeStringConst(char *str, int location)\n{\n\tA_Const *n = makeNode(A_Const);\n\n\tn->val.sval.type = T_String;\n\tn->val.sval.sval = str;\n\tn->location = location;\n\n\treturn (Node *) n;\n}\n\n\n#endif\n\n\n/*\n * makeIntConst creates a Const Node that stores a given integer\n *\n * copied from backend/parser/gram.c\n */\nstatic Node *\nmakeIntConst(int val, int location)\n{\n\tA_Const *n = makeNode(A_Const);\n\n\tn->val.ival.type = T_Integer;\n\tn->val.ival.ival = val;\n\tn->location = location;\n\n\treturn (Node *) n;\n}\n\n\n/*\n * makeIntConst creates a Const Node that stores a given Float\n *\n * copied from backend/parser/gram.c\n */\nstatic Node *\nmakeFloatConst(char *str, int location)\n{\n\tA_Const *n = makeNode(A_Const);\n\n\tn->val.fval.type = T_Float;\n\tn->val.fval.fval = str;\n\tn->location = location;\n\n\treturn (Node *) n;\n}\n\n\n/*\n * PreprocessDropRoleStmt finds the distributed role out of the ones\n * being dropped and unmarks them distributed and creates the drop statements\n * for the workers.\n */\nList *\nPreprocessDropRoleStmt(Node *node, const char *queryString,\n\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tDropRoleStmt *stmt = castNode(DropRoleStmt, node);\n\tList *allDropRoles = stmt->roles;\n\n\tList *distributedDropRoles = FilterDistributedRoles(allDropRoles);\n\tif (list_length(distributedDropRoles) <= 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (!EnableCreateRolePropagation || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsurePropagationToCoordinator();\n\n\tEnsureSequentialModeForRoleDDL();\n\n\n\tstmt->roles = distributedDropRoles;\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\tstmt->roles = allDropRoles;\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(REMOTE_NODES, commands);\n}\n\n\n/*\n * UnmarkRolesDistributed unmarks the roles in the RoleSpec list distributed.\n */\nvoid\nUnmarkRolesDistributed(List *roles)\n{\n\tNode *roleNode = NULL;\n\tforeach_declared_ptr(roleNode, roles)\n\t{\n\t\tRoleSpec *role = castNode(RoleSpec, roleNode);\n\t\tObjectAddress roleAddress = { 0 };\n\t\tOid roleOid = get_rolespec_oid(role, true);\n\n\t\tif (roleOid == InvalidOid)\n\t\t{\n\t\t\t/*\n\t\t\t * If the role is dropped (concurrently), we might get an inactive oid for the\n\t\t\t * role. If it is invalid oid, skip.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tObjectAddressSet(roleAddress, AuthIdRelationId, roleOid);\n\t\tUnmarkObjectDistributed(&roleAddress);\n\t}\n}\n\n\n/*\n * FilterDistributedRoles filters the list of RoleSpecs and returns the ones\n * that are distributed.\n */\nList *\nFilterDistributedRoles(List *roles)\n{\n\tList *distributedRoles = NIL;\n\tNode *roleNode = NULL;\n\tforeach_declared_ptr(roleNode, roles)\n\t{\n\t\tRoleSpec *role = castNode(RoleSpec, roleNode);\n\t\tOid roleOid = get_rolespec_oid(role, true);\n\t\tif (roleOid == InvalidOid)\n\t\t{\n\t\t\t/*\n\t\t\t * Non-existing roles are ignored silently here. Postgres will\n\t\t\t * handle to give an error or not for these roles.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\t\tObjectAddress *roleAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*roleAddress, AuthIdRelationId, roleOid);\n\t\tif (IsAnyObjectDistributed(list_make1(roleAddress)))\n\t\t{\n\t\t\tdistributedRoles = lappend(distributedRoles, role);\n\t\t}\n\t}\n\treturn distributedRoles;\n}\n\n\n/*\n * PreprocessGrantRoleStmt finds the distributed grantee roles and creates the\n * query to run on the workers.\n */\nList *\nPreprocessGrantRoleStmt(Node *node, const char *queryString,\n\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tif (!EnableCreateRolePropagation || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsurePropagationToCoordinator();\n\n\tGrantRoleStmt *stmt = castNode(GrantRoleStmt, node);\n\tList *allGranteeRoles = stmt->grantee_roles;\n\tRoleSpec *grantor = stmt->grantor;\n\n\tList *distributedGranteeRoles = FilterDistributedRoles(allGranteeRoles);\n\tif (list_length(distributedGranteeRoles) <= 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tstmt->grantee_roles = distributedGranteeRoles;\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\tstmt->grantee_roles = allGranteeRoles;\n\tstmt->grantor = grantor;\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(REMOTE_NODES, commands);\n}\n\n\n/*\n * PostprocessGrantRoleStmt actually creates the plan we need to execute for grant\n * role statement.\n */\nList *\nPostprocessGrantRoleStmt(Node *node, const char *queryString)\n{\n\tif (!EnableCreateRolePropagation || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsurePropagationToCoordinator();\n\n\tGrantRoleStmt *stmt = castNode(GrantRoleStmt, node);\n\n\tRoleSpec *role = NULL;\n\tforeach_declared_ptr(role, stmt->grantee_roles)\n\t{\n\t\tOid roleOid = get_rolespec_oid(role, false);\n\t\tObjectAddress *roleAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*roleAddress, AuthIdRelationId, roleOid);\n\n\t\tif (IsAnyObjectDistributed(list_make1(roleAddress)))\n\t\t{\n\t\t\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(roleAddress));\n\t\t}\n\t}\n\treturn NIL;\n}\n\n\n/*\n * ConfigGenericNameCompare compares two config_generic structs based on their\n * name fields. If the name fields contain the same strings two structs are\n * considered to be equal.\n *\n * copied from guc_var_compare in utils/misc/guc.c\n */\nstatic int\nConfigGenericNameCompare(const void *a, const void *b)\n{\n\tconst struct config_generic *confa = *(struct config_generic *const *) a;\n\tconst struct config_generic *confb = *(struct config_generic *const *) b;\n\n\t/*\n\t * guc_var_compare used a custom comparison function here to allow stable\n\t * ordering, but we do not need it here as we only perform a lookup, and do\n\t * not use this function to order the guc list.\n\t */\n\treturn pg_strcasecmp(confa->name, confb->name);\n}\n\n\n/*\n * CreateRoleStmtObjectAddress finds the ObjectAddress for the role described\n * by the CreateRoleStmt. If missing_ok is false this function throws an error if the\n * role does not exist.\n *\n * Never returns NULL, but the objid in the address could be invalid if missing_ok was set\n * to true.\n */\nList *\nCreateRoleStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tCreateRoleStmt *stmt = castNode(CreateRoleStmt, node);\n\tOid roleOid = get_role_oid(stmt->role, missing_ok);\n\tObjectAddress *roleAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*roleAddress, AuthIdRelationId, roleOid);\n\n\treturn list_make1(roleAddress);\n}\n\n\n/*\n * EnsureSequentialModeForRoleDDL makes sure that the current transaction is already in\n * sequential mode, or can still safely be put in sequential mode, it errors if that is\n * not possible. The error contains information for the user to retry the transaction with\n * sequential mode set from the begining.\n *\n * As roles are node scoped objects there exists only 1 instance of the role used by\n * potentially multiple shards. To make sure all shards in the transaction can interact\n * with the role the role needs to be visible on all connections used by the transaction,\n * meaning we can only use 1 connection per node.\n */\nstatic void\nEnsureSequentialModeForRoleDDL(void)\n{\n\tif (!IsTransactionBlock())\n\t{\n\t\t/* we do not need to switch to sequential mode if we are not in a transaction */\n\t\treturn;\n\t}\n\n\tif (ParallelQueryExecutedInTransaction())\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot create or modify role because there was a \"\n\t\t\t\t\t\t\t   \"parallel operation on a distributed table in the \"\n\t\t\t\t\t\t\t   \"transaction\"),\n\t\t\t\t\t\terrdetail(\"When creating or altering a role, Citus needs to \"\n\t\t\t\t\t\t\t\t  \"perform all operations over a single connection per \"\n\t\t\t\t\t\t\t\t  \"node to ensure consistency.\"),\n\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t}\n\n\tereport(DEBUG1, (errmsg(\"switching to sequential query execution mode\"),\n\t\t\t\t\t errdetail(\"Role is created or altered. To make sure subsequent \"\n\t\t\t\t\t\t\t   \"commands see the role correctly we need to make sure to \"\n\t\t\t\t\t\t\t   \"use only one connection for all future commands\")));\n\tSetLocalMultiShardModifyModeToSequential();\n}\n\n\n/*\n * PreprocessAlterDatabaseSetStmt is executed before the statement is applied to the local\n * postgres instance.\n *\n * In this stage we can prepare the commands that need to be run on all workers to grant\n * on databases.\n */\nList *\nPreprocessAlterRoleRenameStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (!EnableAlterRolePropagation)\n\t{\n\t\treturn NIL;\n\t}\n\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_ROLE);\n\n\n\tEnsurePropagationToCoordinator();\n\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(REMOTE_NODES, commands);\n}\n\n\nList *\nRenameRoleStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_ROLE);\n\n\tOid roleOid = get_role_oid(stmt->subname, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, AuthIdRelationId, roleOid);\n\n\treturn list_make1(address);\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/schema.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * schema.c\n *    Commands for creating and altering schemas for distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include \"postgres.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup.h\"\n#include \"access/htup_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/relcache.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/tenant_schema_metadata.h\"\n#include \"distributed/version_compat.h\"\n\n\nstatic List * GetObjectAddressBySchemaName(char *schemaName, bool missing_ok);\nstatic List * FilterDistributedSchemas(List *schemas);\nstatic bool SchemaHasDistributedTableWithFKey(char *schemaName);\nstatic bool ShouldPropagateCreateSchemaStmt(void);\nstatic List * GetGrantCommandsFromCreateSchemaStmt(Node *node);\nstatic bool CreateSchemaStmtCreatesTable(CreateSchemaStmt *stmt);\n\n\n/*\n * PostprocessCreateSchemaStmt is called during the planning phase for\n * CREATE SCHEMA ..\n */\nList *\nPostprocessCreateSchemaStmt(Node *node, const char *queryString)\n{\n\tCreateSchemaStmt *createSchemaStmt = castNode(CreateSchemaStmt, node);\n\n\tif (!ShouldPropagateCreateSchemaStmt())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tEnsureSequentialMode(OBJECT_SCHEMA);\n\n\tbool missingOk = createSchemaStmt->if_not_exists;\n\tList *schemaAdressList = CreateSchemaStmtObjectAddress(node, missingOk, true);\n\tAssert(list_length(schemaAdressList) == 1);\n\tObjectAddress *schemaAdress = linitial(schemaAdressList);\n\tOid schemaId = schemaAdress->objectId;\n\tif (!OidIsValid(schemaId))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* to prevent recursion with mx we disable ddl propagation */\n\tList *commands = list_make1(DISABLE_DDL_PROPAGATION);\n\n\t/* deparse sql*/\n\tconst char *sql = DeparseTreeNode(node);\n\n\tcommands = lappend(commands, (void *) sql);\n\n\tcommands = list_concat(commands, GetGrantCommandsFromCreateSchemaStmt(node));\n\n\tchar *schemaName = get_namespace_name(schemaId);\n\tif (ShouldUseSchemaBasedSharding(schemaName))\n\t{\n\t\t/* for now, we don't allow creating tenant tables when creating the schema itself */\n\t\tif (CreateSchemaStmtCreatesTable(createSchemaStmt))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot create distributed schema and table in a \"\n\t\t\t\t\t\t\t\t   \"single statement\"),\n\t\t\t\t\t\t\terrhint(\"SET citus.enable_schema_based_sharding TO off, \"\n\t\t\t\t\t\t\t\t\t\"or create the schema and table in separate \"\n\t\t\t\t\t\t\t\t\t\"commands.\")));\n\t\t}\n\n\t\t/*\n\t\t * Skip if the schema is already inserted into pg_dist_schema.\n\t\t * This could occur when trying to create an already existing schema,\n\t\t * with IF NOT EXISTS clause.\n\t\t */\n\t\tif (!IsTenantSchema(schemaId))\n\t\t{\n\t\t\t/*\n\t\t\t * Register the tenant schema on the coordinator and save the command\n\t\t\t * to register it on the workers.\n\t\t\t */\n\t\t\tint shardCount = 1;\n\t\t\tint replicationFactor = 1;\n\t\t\tOid distributionColumnType = InvalidOid;\n\t\t\tOid distributionColumnCollation = InvalidOid;\n\t\t\tuint32 colocationId = CreateColocationGroup(\n\t\t\t\tshardCount, replicationFactor, distributionColumnType,\n\t\t\t\tdistributionColumnCollation);\n\n\t\t\tInsertTenantSchemaLocally(schemaId, colocationId);\n\n\t\t\tcommands = lappend(commands, TenantSchemaInsertCommand(schemaId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   colocationId));\n\t\t}\n\t}\n\n\tcommands = lappend(commands, ENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * PreprocessDropSchemaStmt invalidates the foreign key cache if any table created\n * under dropped schema involved in any foreign key relationship.\n */\nList *\nPreprocessDropSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tDropStmt *dropStatement = castNode(DropStmt, node);\n\tAssert(dropStatement->removeType == OBJECT_SCHEMA);\n\n\tList *distributedSchemas = FilterDistributedSchemas(dropStatement->objects);\n\n\tif (list_length(distributedSchemas) < 1)\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tEnsureSequentialMode(OBJECT_SCHEMA);\n\n\tString *schemaVal = NULL;\n\tforeach_declared_ptr(schemaVal, distributedSchemas)\n\t{\n\t\tif (SchemaHasDistributedTableWithFKey(strVal(schemaVal)))\n\t\t{\n\t\t\tMarkInvalidateForeignKeyGraph();\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/*\n\t * We swap around the schema's in the statement to only contain the distributed\n\t * schemas before deparsing. We need to restore the original list as postgres\n\t * will execute on this statement locally, which requires all original schemas\n\t * from the user to be present.\n\t */\n\tList *originalObjects = dropStatement->objects;\n\n\tdropStatement->objects = distributedSchemas;\n\n\tconst char *sql = DeparseTreeNode(node);\n\n\tdropStatement->objects = originalObjects;\n\n\t/* to prevent recursion with mx we disable ddl propagation */\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * PreprocessGrantOnSchemaStmt is executed before the statement is applied to the local\n * postgres instance.\n *\n * In this stage we can prepare the commands that need to be run on all workers to grant\n * on schemas. Only grant statements for distributed schema are propagated.\n */\nList *\nPreprocessGrantOnSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_SCHEMA);\n\n\tList *distributedSchemas = FilterDistributedSchemas(stmt->objects);\n\n\tif (list_length(distributedSchemas) == 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tList *originalObjects = stmt->objects;\n\n\tstmt->objects = distributedSchemas;\n\n\tchar *sql = DeparseTreeNode((Node *) stmt);\n\n\tstmt->objects = originalObjects;\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * CreateSchemaStmtObjectAddress returns the ObjectAddress of the schema that is\n * the object of the CreateSchemaStmt. Errors if missing_ok is false.\n */\nList *\nCreateSchemaStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tCreateSchemaStmt *stmt = castNode(CreateSchemaStmt, node);\n\n\tStringInfoData schemaName = { 0 };\n\tinitStringInfo(&schemaName);\n\n\tif (stmt->schemaname == NULL)\n\t{\n\t\t/*\n\t\t * If the schema name is not provided, the schema will be created\n\t\t * with the name of the authorizated user.\n\t\t */\n\t\tAssert(stmt->authrole != NULL);\n\t\tappendStringInfoString(&schemaName, RoleSpecString(stmt->authrole, true));\n\t}\n\telse\n\t{\n\t\tappendStringInfoString(&schemaName, stmt->schemaname);\n\t}\n\n\treturn GetObjectAddressBySchemaName(schemaName.data, missing_ok);\n}\n\n\n/*\n * AlterSchemaOwnerStmtObjectAddress returns the ObjectAddress of the schema that is\n * the object of the AlterOwnerStmt. Errors if missing_ok is false.\n */\nList *\nAlterSchemaOwnerStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_SCHEMA);\n\n\treturn GetObjectAddressBySchemaName(strVal(stmt->object), missing_ok);\n}\n\n\n/*\n * AlterSchemaRenameStmtObjectAddress returns the ObjectAddress of the schema that is\n * the object of the RenameStmt. Errors if missing_ok is false.\n */\nList *\nAlterSchemaRenameStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_SCHEMA);\n\n\treturn GetObjectAddressBySchemaName(stmt->subname, missing_ok);\n}\n\n\n/*\n * GetObjectAddressBySchemaName returns the ObjectAddress of the schema with the\n * given name. Errors out if schema is not found and missing_ok is false.\n */\nList *\nGetObjectAddressBySchemaName(char *schemaName, bool missing_ok)\n{\n\tOid schemaOid = get_namespace_oid(schemaName, missing_ok);\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, NamespaceRelationId, schemaOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * FilterDistributedSchemas filters the schema list and returns the distributed ones\n * as a list\n */\nstatic List *\nFilterDistributedSchemas(List *schemas)\n{\n\tList *distributedSchemas = NIL;\n\n\tString *schemaValue = NULL;\n\tforeach_declared_ptr(schemaValue, schemas)\n\t{\n\t\tconst char *schemaName = strVal(schemaValue);\n\t\tOid schemaOid = get_namespace_oid(schemaName, true);\n\n\t\tif (!OidIsValid(schemaOid))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*address, NamespaceRelationId, schemaOid);\n\t\tif (!IsAnyObjectDistributed(list_make1(address)))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tdistributedSchemas = lappend(distributedSchemas, schemaValue);\n\t}\n\n\treturn distributedSchemas;\n}\n\n\n/*\n * SchemaHasDistributedTableWithFKey takes a schema name and scans the relations within\n * that schema. If any one of the relations has a foreign key relationship, it returns\n * true. Returns false otherwise.\n */\nstatic bool\nSchemaHasDistributedTableWithFKey(char *schemaName)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tOid scanIndexId = InvalidOid;\n\tbool useIndex = false;\n\n\tOid namespaceOid = get_namespace_oid(schemaName, true);\n\n\tif (namespaceOid == InvalidOid)\n\t{\n\t\treturn false;\n\t}\n\n\tRelation pgClass = table_open(RelationRelationId, AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_class_relnamespace, BTEqualStrategyNumber,\n\t\t\t\tF_OIDEQ, ObjectIdGetDatum(namespaceOid));\n\tSysScanDesc scanDescriptor = systable_beginscan(pgClass, scanIndexId, useIndex, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\tscanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tForm_pg_class relationForm = (Form_pg_class) GETSTRUCT(heapTuple);\n\t\tchar *relationName = NameStr(relationForm->relname);\n\t\tOid relationId = get_relname_relid(relationName, namespaceOid);\n\n\t\t/* we're not interested in non-valid, non-distributed relations */\n\t\tif (relationId == InvalidOid || !IsCitusTable(relationId))\n\t\t{\n\t\t\theapTuple = systable_getnext(scanDescriptor);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* invalidate foreign key cache if the table involved in any foreign key */\n\t\tif (TableReferenced(relationId) || TableReferencing(relationId))\n\t\t{\n\t\t\tsystable_endscan(scanDescriptor);\n\t\t\ttable_close(pgClass, NoLock);\n\t\t\treturn true;\n\t\t}\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgClass, NoLock);\n\n\treturn false;\n}\n\n\n/*\n * ShouldPropagateCreateSchemaStmt gets called only for CreateSchemaStmt's.\n * This function wraps the ShouldPropagate function which is commonly used\n * for all object types; additionally it checks whether there's a multi-statement\n * transaction ongoing or not. For transaction blocks, we require sequential mode\n * with this function, for CREATE SCHEMA statements. If Citus has not already\n * switched to sequential mode, we don't propagate.\n */\nstatic bool\nShouldPropagateCreateSchemaStmt()\n{\n\tif (!ShouldPropagate())\n\t{\n\t\treturn false;\n\t}\n\n\t/* check creation against multi-statement transaction policy */\n\tif (!ShouldPropagateCreateInCoordinatedTransction())\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * GetGrantCommandsFromCreateSchemaStmt takes a CreateSchemaStmt and returns the\n * list of deparsed queries of the inner GRANT commands of the given statement.\n * Ignores commands other than GRANT statements.\n */\nstatic List *\nGetGrantCommandsFromCreateSchemaStmt(Node *node)\n{\n\tList *commands = NIL;\n\tCreateSchemaStmt *stmt = castNode(CreateSchemaStmt, node);\n\n\tNode *element = NULL;\n\tforeach_declared_ptr(element, stmt->schemaElts)\n\t{\n\t\tif (!IsA(element, GrantStmt))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tGrantStmt *grantStmt = castNode(GrantStmt, element);\n\n\t\tswitch (grantStmt->objtype)\n\t\t{\n\t\t\t/* we only propagate GRANT ON SCHEMA in community */\n\t\t\tcase OBJECT_SCHEMA:\n\t\t\t{\n\t\t\t\tcommands = lappend(commands, DeparseGrantOnSchemaStmt(element));\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn commands;\n}\n\n\n/*\n * CreateSchemaStmtCreatesTable returns true if given CreateSchemaStmt\n * creates a table using \"schema_element\" list.\n */\nstatic bool\nCreateSchemaStmtCreatesTable(CreateSchemaStmt *stmt)\n{\n\tNode *element = NULL;\n\tforeach_declared_ptr(element, stmt->schemaElts)\n\t{\n\t\t/*\n\t\t * CREATE TABLE AS and CREATE FOREIGN TABLE commands cannot be\n\t\t * used as schema_elements anyway, so we don't need to check them.\n\t\t */\n\t\tif (IsA(element, CreateStmt))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/schema_based_sharding.c",
    "content": "/*-------------------------------------------------------------------------\n * schema_based_sharding.c\n *\n *\t  Routines for schema-based sharding.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"catalog/catalog.h\"\n#include \"catalog/pg_namespace_d.h\"\n#include \"commands/extension.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/argutils.h\"\n#include \"distributed/backend_data.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/shard_transfer.h\"\n#include \"distributed/tenant_schema_metadata.h\"\n#include \"distributed/worker_shard_visibility.h\"\n\n\n/* return value of CreateCitusMoveSchemaParams() */\ntypedef struct\n{\n\tuint64 anchorShardId;\n\tuint32 sourceNodeId;\n\tchar *sourceNodeName;\n\tuint32 sourceNodePort;\n} CitusMoveSchemaParams;\n\n\nstatic void UnregisterTenantSchemaGlobally(Oid schemaId, char *schemaName);\nstatic List * SchemaGetNonShardTableIdList(Oid schemaId);\nstatic void EnsureSchemaCanBeDistributed(Oid schemaId, List *schemaTableIdList);\nstatic void EnsureTenantSchemaNameAllowed(Oid schemaId);\nstatic void EnsureTableKindSupportedForTenantSchema(Oid relationId);\nstatic void EnsureFKeysForTenantTable(Oid relationId);\nstatic void EnsureSchemaExist(Oid schemaId);\nstatic CitusMoveSchemaParams * CreateCitusMoveSchemaParams(Oid schemaId);\nstatic uint64 TenantSchemaPickAnchorShardId(Oid schemaId);\n\n\n/* controlled via citus.enable_schema_based_sharding GUC */\nbool EnableSchemaBasedSharding = false;\n\n\nconst char *TenantOperationNames[TOTAL_TENANT_OPERATION] = {\n\t\"undistribute_table\",\n\t\"alter_distributed_table\",\n\t\"colocate_with\",\n\t\"update_distributed_table_colocation\",\n\t\"set schema\",\n};\n\n\nPG_FUNCTION_INFO_V1(citus_internal_unregister_tenant_schema_globally);\nPG_FUNCTION_INFO_V1(citus_schema_distribute);\nPG_FUNCTION_INFO_V1(citus_schema_undistribute);\nPG_FUNCTION_INFO_V1(citus_schema_move);\nPG_FUNCTION_INFO_V1(citus_schema_move_with_nodeid);\n\n/*\n * ShouldUseSchemaBasedSharding returns true if schema given name should be\n * used as a tenant schema.\n */\nbool\nShouldUseSchemaBasedSharding(char *schemaName)\n{\n\tif (!EnableSchemaBasedSharding)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsBinaryUpgrade)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * Citus utility hook skips processing CREATE SCHEMA commands while an\n\t * extension is being created. For this reason, we don't expect to get\n\t * here while an extension is being created.\n\t */\n\tAssert(!creating_extension);\n\n\t/*\n\t * CREATE SCHEMA commands issued by internal backends are not meant to\n\t * create tenant schemas but to sync metadata.\n\t *\n\t * On workers, Citus utility hook skips processing CREATE SCHEMA commands\n\t * because we temporarily disable DDL propagation on workers when sending\n\t * CREATE SCHEMA commands. For this reason, right now this check is a bit\n\t * redundant but we prefer to keep it here to be on the safe side.\n\t */\n\tif (IsCitusInternalBackend() || IsRebalancerInternalBackend())\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * Not do an oid comparison based on PG_PUBLIC_NAMESPACE because\n\t * we want to treat \"public\" schema in the same way even if it's\n\t * recreated.\n\t */\n\tif (strcmp(schemaName, \"public\") == 0)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ShouldCreateTenantSchemaTable returns true if we should create a tenant\n * schema table for given relationId.\n */\nbool\nShouldCreateTenantSchemaTable(Oid relationId)\n{\n\tif (IsBinaryUpgrade)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * CREATE TABLE commands issued by internal backends are not meant to\n\t * create tenant tables but to sync metadata.\n\t */\n\tif (IsCitusInternalBackend() || IsRebalancerInternalBackend())\n\t{\n\t\treturn false;\n\t}\n\n\tOid schemaId = get_rel_namespace(relationId);\n\treturn IsTenantSchema(schemaId);\n}\n\n\n/*\n * EnsureTableKindSupportedForTenantSchema ensures that given table's kind is\n * supported by a tenant schema.\n */\nstatic void\nEnsureTableKindSupportedForTenantSchema(Oid relationId)\n{\n\tif (IsForeignTable(relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot create a foreign table in a distributed \"\n\t\t\t\t\t\t\t   \"schema\")));\n\t}\n\n\tif (PartitionTable(relationId))\n\t{\n\t\tErrorIfIllegalPartitioningInTenantSchema(PartitionParentOid(relationId),\n\t\t\t\t\t\t\t\t\t\t\t\t relationId);\n\t}\n\n\tif (PartitionedTable(relationId))\n\t{\n\t\tList *partitionList = PartitionList(relationId);\n\n\t\tOid partitionRelationId = InvalidOid;\n\t\tforeach_declared_oid(partitionRelationId, partitionList)\n\t\t{\n\t\t\tErrorIfIllegalPartitioningInTenantSchema(relationId, partitionRelationId);\n\t\t}\n\t}\n\n\tif (IsChildTable(relationId) || IsParentTable(relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"tables in a distributed schema cannot inherit or \"\n\t\t\t\t\t\t\t   \"be inherited\")));\n\t}\n}\n\n\n/*\n * EnsureFKeysForTenantTable ensures that all referencing and referenced foreign\n * keys are allowed for given table.\n */\nstatic void\nEnsureFKeysForTenantTable(Oid relationId)\n{\n\tOid tenantSchemaId = get_rel_namespace(relationId);\n\tint fKeyReferencingFlags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES;\n\tList *referencingForeignKeys = GetForeignKeyOids(relationId, fKeyReferencingFlags);\n\tOid foreignKeyId = InvalidOid;\n\tforeach_declared_oid(foreignKeyId, referencingForeignKeys)\n\t{\n\t\tOid referencingTableId = GetReferencingTableId(foreignKeyId);\n\t\tOid referencedTableId = GetReferencedTableId(foreignKeyId);\n\t\tOid referencedTableSchemaId = get_rel_namespace(referencedTableId);\n\n\t\t/* We allow foreign keys to a table in the same schema */\n\t\tif (tenantSchemaId == referencedTableSchemaId)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Allow foreign keys to the other schema only if the referenced table is\n\t\t * a reference table.\n\t\t */\n\t\tif (!IsCitusTable(referencedTableId) ||\n\t\t\t!IsCitusTableType(referencedTableId, REFERENCE_TABLE))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"foreign keys from distributed schemas can only \"\n\t\t\t\t\t\t\t\t   \"point to the same distributed schema or reference \"\n\t\t\t\t\t\t\t\t   \"tables in regular schemas\"),\n\t\t\t\t\t\t\terrdetail(\"\\\"%s\\\" references \\\"%s\\\" via foreign key \"\n\t\t\t\t\t\t\t\t\t  \"constraint \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t\t  generate_qualified_relation_name(\n\t\t\t\t\t\t\t\t\t\t  referencingTableId),\n\t\t\t\t\t\t\t\t\t  generate_qualified_relation_name(referencedTableId),\n\t\t\t\t\t\t\t\t\t  get_constraint_name(foreignKeyId))));\n\t\t}\n\t}\n\n\tint fKeyReferencedFlags = INCLUDE_REFERENCED_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES;\n\tList *referencedForeignKeys = GetForeignKeyOids(relationId, fKeyReferencedFlags);\n\tforeach_declared_oid(foreignKeyId, referencedForeignKeys)\n\t{\n\t\tOid referencingTableId = GetReferencingTableId(foreignKeyId);\n\t\tOid referencedTableId = GetReferencedTableId(foreignKeyId);\n\t\tOid referencingTableSchemaId = get_rel_namespace(referencingTableId);\n\n\t\t/* We allow foreign keys from a table in the same schema */\n\t\tif (tenantSchemaId == referencingTableSchemaId)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* Not allow any foreign keys from the other schema */\n\t\tereport(ERROR, (errmsg(\"cannot create foreign keys to tables in a distributed \"\n\t\t\t\t\t\t\t   \"schema from another schema\"),\n\t\t\t\t\t\terrdetail(\"\\\"%s\\\" references \\\"%s\\\" via foreign key \"\n\t\t\t\t\t\t\t\t  \"constraint \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t  generate_qualified_relation_name(referencingTableId),\n\t\t\t\t\t\t\t\t  generate_qualified_relation_name(referencedTableId),\n\t\t\t\t\t\t\t\t  get_constraint_name(foreignKeyId))));\n\t}\n}\n\n\n/*\n * CreateTenantSchemaTable creates a tenant table with given relationId.\n *\n * This means creating a single shard distributed table without a shard\n * key and colocating it with the other tables in its schema.\n */\nvoid\nCreateTenantSchemaTable(Oid relationId)\n{\n\tif (!IsCoordinator())\n\t{\n\t\t/*\n\t\t * We don't support creating tenant tables from workers. We could\n\t\t * let ShouldCreateTenantSchemaTable() to return false to allow users\n\t\t * to create a local table as usual but that would be confusing because\n\t\t * it might sound like we allow creating tenant tables from workers.\n\t\t * For this reason, we prefer to throw an error instead.\n\t\t *\n\t\t * Indeed, CreateSingleShardTable() would already do so but we\n\t\t * prefer to throw an error with a more meaningful message, rather\n\t\t * than saying \"operation is not allowed on this node\".\n\t\t */\n\t\tereport(ERROR, (errmsg(\"cannot create tables in a distributed schema from \"\n\t\t\t\t\t\t\t   \"a worker node\"),\n\t\t\t\t\t\terrhint(\"Connect to the coordinator node and try again.\")));\n\t}\n\n\tEnsureTableKindSupportedForTenantSchema(relationId);\n\n\t/*\n\t * We don't expect this to happen because ShouldCreateTenantSchemaTable()\n\t * should've already verified that; but better to check.\n\t */\n\tOid schemaId = get_rel_namespace(relationId);\n\tuint32 colocationId = SchemaIdGetTenantColocationId(schemaId);\n\tif (colocationId == INVALID_COLOCATION_ID)\n\t{\n\t\tereport(ERROR, (errmsg(\"schema \\\"%s\\\" is not distributed\",\n\t\t\t\t\t\t\t   get_namespace_name(schemaId))));\n\t}\n\n\tColocationParam colocationParam = {\n\t\t.colocationParamType = COLOCATE_WITH_COLOCATION_ID,\n\t\t.colocationId = colocationId,\n\t};\n\tCreateSingleShardTable(relationId, colocationParam);\n}\n\n\n/*\n * ErrorIfIllegalPartitioningInTenantSchema throws an error if the\n * partitioning relationship between the parent and the child is illegal\n * because they are in different schemas while one of them is a tenant table.\n *\n * This function assumes that either the parent or the child are in a tenant\n * schema.\n */\nvoid\nErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId, Oid partitionRelationId)\n{\n\tif (get_rel_namespace(partitionRelationId) != get_rel_namespace(parentRelationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"partitioning within a distributed schema is not \"\n\t\t\t\t\t\t\t   \"supported when the parent and the child \"\n\t\t\t\t\t\t\t   \"are in different schemas\")));\n\t}\n}\n\n\n/*\n * CreateTenantSchemaColocationId returns new colocation id for a tenant schema.\n */\nuint32\nCreateTenantSchemaColocationId(void)\n{\n\tint shardCount = 1;\n\tint replicationFactor = 1;\n\tOid distributionColumnType = InvalidOid;\n\tOid distributionColumnCollation = InvalidOid;\n\tuint32 schemaColocationId = CreateColocationGroup(\n\t\tshardCount, replicationFactor, distributionColumnType,\n\t\tdistributionColumnCollation);\n\treturn schemaColocationId;\n}\n\n\n/*\n * SchemaGetNonShardTableIdList returns all nonshard relation ids\n * inside given schema.\n */\nstatic List *\nSchemaGetNonShardTableIdList(Oid schemaId)\n{\n\tList *relationIdList = NIL;\n\n\t/* scan all relations in pg_class and return all tables inside given schema */\n\tRelation relationRelation = relation_open(RelationRelationId, AccessShareLock);\n\n\tScanKeyData scanKey[1] = { 0 };\n\tScanKeyInit(&scanKey[0], Anum_pg_class_relnamespace, BTEqualStrategyNumber,\n\t\t\t\tF_OIDEQ, ObjectIdGetDatum(schemaId));\n\tSysScanDesc scanDescriptor = systable_beginscan(relationRelation, ClassNameNspIndexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttrue, NULL, 1, scanKey);\n\n\tHeapTuple heapTuple = NULL;\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tForm_pg_class relationForm = (Form_pg_class) GETSTRUCT(heapTuple);\n\t\tchar *relationName = NameStr(relationForm->relname);\n\t\tOid relationId = get_relname_relid(relationName, schemaId);\n\n\t\tif (!OidIsValid(relationId))\n\t\t{\n\t\t\tereport(ERROR, errmsg(\"table %s is dropped by a concurrent operation\",\n\t\t\t\t\t\t\t\t  relationName));\n\t\t}\n\n\t\t/* skip shards */\n\t\tif (RelationIsAKnownShard(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (RegularTable(relationId) || PartitionTable(relationId) ||\n\t\t\tIsForeignTable(relationId))\n\t\t{\n\t\t\trelationIdList = lappend_oid(relationIdList, relationId);\n\t\t}\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\trelation_close(relationRelation, AccessShareLock);\n\n\treturn relationIdList;\n}\n\n\n/*\n * EnsureSchemaCanBeDistributed ensures the schema can be distributed.\n * Caller should take required the lock on relations and the schema.\n *\n * It checks:\n *  - Schema name is in the allowed-list,\n *  - Schema does not depend on an extension (created by extension),\n *  - No extension depends on the schema (CREATE EXTENSION <ext> SCHEMA <schema>),\n *\t- Some checks for the table for being a valid tenant table.\n */\nstatic void\nEnsureSchemaCanBeDistributed(Oid schemaId, List *schemaTableIdList)\n{\n\t/* Ensure schema name is allowed */\n\tEnsureTenantSchemaNameAllowed(schemaId);\n\n\t/* Any schema owned by extension is not allowed */\n\tchar *schemaName = get_namespace_name(schemaId);\n\tObjectAddress *schemaAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*schemaAddress, NamespaceRelationId, schemaId);\n\tif (IsAnyObjectAddressOwnedByExtension(list_make1(schemaAddress), NULL))\n\t{\n\t\tereport(ERROR, (errmsg(\"schema %s, which is owned by an extension, cannot \"\n\t\t\t\t\t\t\t   \"be distributed\", schemaName)));\n\t}\n\n\t/* Extension schemas are not allowed */\n\tObjectAddress *extensionAddress = FirstExtensionWithSchema(schemaId);\n\tif (extensionAddress)\n\t{\n\t\tchar *extensionName = get_extension_name(extensionAddress->objectId);\n\t\tereport(ERROR, (errmsg(\"schema %s cannot be distributed since it is the schema \"\n\t\t\t\t\t\t\t   \"of extension %s\", schemaName, extensionName)));\n\t}\n\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, schemaTableIdList)\n\t{\n\t\tEnsureTenantTable(relationId, \"citus_schema_distribute\");\n\t}\n}\n\n\n/*\n * EnsureTenantTable ensures the table can be a valid tenant table.\n *  - Current user should be the owner of table,\n *  - Table kind is supported,\n *  - Referencing and referenced foreign keys for the table are supported,\n *\t- Table is not owned by an extension,\n *  - Table should be Citus local or Postgres local table.\n */\nvoid\nEnsureTenantTable(Oid relationId, char *operationName)\n{\n\t/* Ensure table owner */\n\tEnsureTableOwner(relationId);\n\n\t/* Check relation kind */\n\tEnsureTableKindSupportedForTenantSchema(relationId);\n\n\t/* Check foreign keys */\n\tEnsureFKeysForTenantTable(relationId);\n\n\t/* Check table not owned by an extension */\n\tObjectAddress *tableAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*tableAddress, RelationRelationId, relationId);\n\tif (IsAnyObjectAddressOwnedByExtension(list_make1(tableAddress), NULL))\n\t{\n\t\tOid schemaId = get_rel_namespace(relationId);\n\t\tchar *tableName = get_namespace_name(schemaId);\n\t\tereport(ERROR, (errmsg(\"schema cannot be distributed since it has \"\n\t\t\t\t\t\t\t   \"table %s which is owned by an extension\",\n\t\t\t\t\t\t\t   tableName)));\n\t}\n\n\t/* Postgres local tables are allowed */\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn;\n\t}\n\n\t/* Only Citus local tables, amongst Citus table types, are allowed */\n\tif (!IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\tereport(ERROR, (errmsg(\"distributed schema cannot have distributed tables\"),\n\t\t\t\t\t\terrhint(\"Undistribute distributed tables before \"\n\t\t\t\t\t\t\t\t\"'%s'.\", operationName)));\n\t}\n}\n\n\n/*\n * EnsureTenantSchemaNameAllowed ensures if given schema is applicable for registering\n * as a tenant schema.\n */\nstatic void\nEnsureTenantSchemaNameAllowed(Oid schemaId)\n{\n\tchar *schemaName = get_namespace_name(schemaId);\n\n\t/* public schema is not allowed */\n\tif (strcmp(schemaName, \"public\") == 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"public schema cannot be distributed\")));\n\t}\n\n\t/* information_schema schema is not allowed */\n\tif (strcmp(schemaName, \"information_schema\") == 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"information_schema schema cannot be distributed\")));\n\t}\n\n\t/* pg_temp_xx and pg_toast_temp_xx schemas are not allowed */\n\tif (isAnyTempNamespace(schemaId))\n\t{\n\t\tereport(ERROR, (errmsg(\"temporary schema cannot be distributed\")));\n\t}\n\n\t/* pg_catalog schema is not allowed */\n\tif (IsCatalogNamespace(schemaId))\n\t{\n\t\tereport(ERROR, (errmsg(\"pg_catalog schema cannot be distributed\")));\n\t}\n\n\t/* pg_toast schema is not allowed */\n\tif (IsToastNamespace(schemaId))\n\t{\n\t\tereport(ERROR, (errmsg(\"pg_toast schema cannot be distributed\")));\n\t}\n}\n\n\n/*\n * EnsureSchemaExist ensures that schema exists. Caller is responsible to take\n * the required lock on the schema.\n */\nstatic void\nEnsureSchemaExist(Oid schemaId)\n{\n\tif (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaId)))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_SCHEMA),\n\t\t\t\t\t\terrmsg(\"schema with OID %u does not exist\", schemaId)));\n\t}\n}\n\n\n/*\n * UnregisterTenantSchemaGlobally removes given schema from the tenant schema\n * metadata table, deletes the colocation group of the schema and sends the\n * command to do the same on the workers.\n */\nstatic void\nUnregisterTenantSchemaGlobally(Oid schemaId, char *schemaName)\n{\n\tuint32 tenantSchemaColocationId = SchemaIdGetTenantColocationId(schemaId);\n\n\tDeleteTenantSchemaLocally(schemaId);\n\tif (EnableMetadataSync)\n\t{\n\t\tSendCommandToWorkersWithMetadata(TenantSchemaDeleteCommand(schemaName));\n\t}\n\n\tDeleteColocationGroup(tenantSchemaColocationId);\n}\n\n\n/*\n * citus_internal_unregister_tenant_schema_globally, called by Citus drop hook,\n * unregisters the schema when a tenant schema is dropped.\n *\n * NOTE: We need to pass schema_name as an argument. We cannot use schema id\n * to obtain schema name since the schema would have already been dropped when this\n * udf is called by the drop hook.\n */\nDatum\ncitus_internal_unregister_tenant_schema_globally(PG_FUNCTION_ARGS)\n{\n\tPG_ENSURE_ARGNOTNULL(0, \"schema_id\");\n\tOid schemaId = PG_GETARG_OID(0);\n\n\tPG_ENSURE_ARGNOTNULL(1, \"schema_name\");\n\ttext *schemaName = PG_GETARG_TEXT_PP(1);\n\tchar *schemaNameStr = text_to_cstring(schemaName);\n\n\t/*\n\t * Skip on workers because we expect this to be called from the coordinator\n\t * only via drop hook.\n\t */\n\tif (!IsCoordinator())\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\t/* make sure that the schema is dropped already */\n\tHeapTuple namespaceTuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(schemaId));\n\tif (HeapTupleIsValid(namespaceTuple))\n\t{\n\t\tReleaseSysCache(namespaceTuple);\n\t\tereport(ERROR, (errmsg(\"schema is expected to be already dropped \"\n\t\t\t\t\t\t\t   \"because this function is only expected to \"\n\t\t\t\t\t\t\t   \"be called from Citus drop hook\")));\n\t}\n\tUnregisterTenantSchemaGlobally(schemaId, schemaNameStr);\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_schema_distribute gets a regular schema name, then converts it to a tenant\n * schema.\n */\nDatum\ncitus_schema_distribute(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tOid schemaId = PG_GETARG_OID(0);\n\tEnsureSchemaExist(schemaId);\n\tEnsureSchemaOwner(schemaId);\n\n\t/* Prevent concurrent table creation under the schema */\n\tLockDatabaseObject(NamespaceRelationId, schemaId, 0, AccessExclusiveLock);\n\n\t/*\n\t * We should ensure the existence of the schema after taking the lock since\n\t * the schema could have been dropped before we acquired the lock.\n\t */\n\tEnsureSchemaExist(schemaId);\n\tEnsureSchemaOwner(schemaId);\n\n\t/* Return if the schema is already a tenant schema */\n\tchar *schemaName = get_namespace_name(schemaId);\n\tif (IsTenantSchema(schemaId))\n\t{\n\t\tereport(NOTICE, (errmsg(\"schema %s is already distributed\", schemaName)));\n\t\tPG_RETURN_VOID();\n\t}\n\n\t/* Take lock on the relations and filter out partition tables */\n\tList *tableIdListInSchema = SchemaGetNonShardTableIdList(schemaId);\n\tList *tableIdListToConvert = NIL;\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, tableIdListInSchema)\n\t{\n\t\t/* prevent concurrent drop of the relation */\n\t\tLockRelationOid(relationId, AccessShareLock);\n\t\tEnsureRelationExists(relationId);\n\n\t\t/*\n\t\t * Skip partitions as they would be distributed by the parent table.\n\t\t *\n\t\t * We should filter out partitions here before distributing the schema.\n\t\t * Otherwise, converted partitioned table would change oid of partitions and its\n\t\t * partition tables would fail with oid not exist.\n\t\t */\n\t\tif (PartitionTable(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\ttableIdListToConvert = lappend_oid(tableIdListToConvert, relationId);\n\t}\n\n\t/* Makes sure the schema can be distributed. */\n\tEnsureSchemaCanBeDistributed(schemaId, tableIdListInSchema);\n\n\tereport(NOTICE, (errmsg(\"distributing the schema %s\", schemaName)));\n\n\t/* Create colocation id and then single shard tables with the colocation id */\n\tuint32 colocationId = CreateTenantSchemaColocationId();\n\tColocationParam colocationParam = {\n\t\t.colocationParamType = COLOCATE_WITH_COLOCATION_ID,\n\t\t.colocationId = colocationId,\n\t};\n\n\t/*\n\t * Collect foreign keys for recreation and then drop fkeys and create single shard\n\t * tables.\n\t */\n\tList *originalForeignKeyRecreationCommands = NIL;\n\tforeach_declared_oid(relationId, tableIdListToConvert)\n\t{\n\t\tList *fkeyCommandsForRelation =\n\t\t\tGetFKeyCreationCommandsRelationInvolvedWithTableType(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t INCLUDE_ALL_TABLE_TYPES);\n\t\toriginalForeignKeyRecreationCommands = list_concat(\n\t\t\toriginalForeignKeyRecreationCommands, fkeyCommandsForRelation);\n\n\t\tDropFKeysRelationInvolvedWithTableType(relationId, INCLUDE_ALL_TABLE_TYPES);\n\t\tCreateSingleShardTable(relationId, colocationParam);\n\t}\n\n\t/* We can skip foreign key validations as we are sure about them at start */\n\tbool skip_validation = true;\n\tExecuteForeignKeyCreateCommandList(originalForeignKeyRecreationCommands,\n\t\t\t\t\t\t\t\t\t   skip_validation);\n\n\t/* Register the schema locally and sync it to workers */\n\tInsertTenantSchemaLocally(schemaId, colocationId);\n\tchar *registerSchemaCommand = TenantSchemaInsertCommand(schemaId, colocationId);\n\tif (EnableMetadataSync)\n\t{\n\t\tSendCommandToWorkersWithMetadata(registerSchemaCommand);\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_schema_undistribute gets a tenant schema name, then converts it to a regular\n * schema by undistributing all tables under it.\n */\nDatum\ncitus_schema_undistribute(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tOid schemaId = PG_GETARG_OID(0);\n\tEnsureSchemaExist(schemaId);\n\tEnsureSchemaOwner(schemaId);\n\n\t/* Prevent concurrent table creation under the schema */\n\tLockDatabaseObject(NamespaceRelationId, schemaId, 0, AccessExclusiveLock);\n\n\t/*\n\t * We should ensure the existence of the schema after taking the lock since\n\t * the schema could have been dropped before we acquired the lock.\n\t */\n\tEnsureSchemaExist(schemaId);\n\tEnsureSchemaOwner(schemaId);\n\n\t/* The schema should be a tenant schema */\n\tchar *schemaName = get_namespace_name(schemaId);\n\tif (!IsTenantSchema(schemaId))\n\t{\n\t\tereport(ERROR, (errmsg(\"schema %s is not distributed\", schemaName)));\n\t}\n\n\tereport(NOTICE, (errmsg(\"undistributing schema %s\", schemaName)));\n\n\t/* Take lock on the relations and filter out partition tables */\n\tList *tableIdListInSchema = SchemaGetNonShardTableIdList(schemaId);\n\tList *tableIdListToConvert = NIL;\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, tableIdListInSchema)\n\t{\n\t\t/* prevent concurrent drop of the relation */\n\t\tLockRelationOid(relationId, AccessShareLock);\n\t\tEnsureRelationExists(relationId);\n\n\t\t/*\n\t\t * Skip partitions as they would be undistributed by the parent table.\n\t\t *\n\t\t * We should filter out partitions here before undistributing the schema.\n\t\t * Otherwise, converted partitioned table would change oid of partitions and its\n\t\t * partition tables would fail with oid not exist.\n\t\t */\n\t\tif (PartitionTable(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\ttableIdListToConvert = lappend_oid(tableIdListToConvert, relationId);\n\n\t\t/* Only single shard tables are expected during the undistribution of the schema */\n\t\tAssert(IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED));\n\t}\n\n\t/*\n\t * First, we need to delete schema metadata and sync it to workers. Otherwise,\n\t * we would get error from `ErrorIfTenantTable` while undistributing the tables.\n\t */\n\tUnregisterTenantSchemaGlobally(schemaId, schemaName);\n\tUndistributeTables(tableIdListToConvert);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_schema_move moves the shards that belong to given distributed tenant\n * schema from one node to the other node by using citus_move_shard_placement().\n */\nDatum\ncitus_schema_move(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tOid schemaId = PG_GETARG_OID(0);\n\tCitusMoveSchemaParams *params = CreateCitusMoveSchemaParams(schemaId);\n\n\tDirectFunctionCall6(citus_move_shard_placement,\n\t\t\t\t\t\tUInt64GetDatum(params->anchorShardId),\n\t\t\t\t\t\tCStringGetTextDatum(params->sourceNodeName),\n\t\t\t\t\t\tUInt32GetDatum(params->sourceNodePort),\n\t\t\t\t\t\tPG_GETARG_DATUM(1),\n\t\t\t\t\t\tPG_GETARG_DATUM(2),\n\t\t\t\t\t\tPG_GETARG_DATUM(3));\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_schema_move_with_nodeid does the same as citus_schema_move(), but\n * accepts node id as parameter instead of hostname and port, hence uses\n * citus_move_shard_placement_with_nodeid().\n */\nDatum\ncitus_schema_move_with_nodeid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tOid schemaId = PG_GETARG_OID(0);\n\tCitusMoveSchemaParams *params = CreateCitusMoveSchemaParams(schemaId);\n\n\tDirectFunctionCall4(citus_move_shard_placement_with_nodeid,\n\t\t\t\t\t\tUInt64GetDatum(params->anchorShardId),\n\t\t\t\t\t\tUInt32GetDatum(params->sourceNodeId),\n\t\t\t\t\t\tPG_GETARG_DATUM(1),\n\t\t\t\t\t\tPG_GETARG_DATUM(2));\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * CreateCitusMoveSchemaParams is a helper function for\n * citus_schema_move() and citus_schema_move_with_nodeid()\n * that validates input schema and returns the parameters to be used in underlying\n * shard transfer functions.\n */\nstatic CitusMoveSchemaParams *\nCreateCitusMoveSchemaParams(Oid schemaId)\n{\n\tEnsureSchemaExist(schemaId);\n\tEnsureSchemaOwner(schemaId);\n\n\tif (!IsTenantSchema(schemaId))\n\t{\n\t\tereport(ERROR, (errmsg(\"schema %s is not a distributed schema\",\n\t\t\t\t\t\t\t   get_namespace_name(schemaId))));\n\t}\n\n\tuint64 anchorShardId = TenantSchemaPickAnchorShardId(schemaId);\n\tif (anchorShardId == INVALID_SHARD_ID)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot move distributed schema %s because it is empty\",\n\t\t\t\t\t\t\t   get_namespace_name(schemaId))));\n\t}\n\n\tuint32 colocationId = SchemaIdGetTenantColocationId(schemaId);\n\tuint32 sourceNodeId = SingleShardTableColocationNodeId(colocationId);\n\n\tbool missingOk = false;\n\tWorkerNode *sourceNode = FindNodeWithNodeId(sourceNodeId, missingOk);\n\n\tCitusMoveSchemaParams *params = palloc0(sizeof(CitusMoveSchemaParams));\n\tparams->anchorShardId = anchorShardId;\n\tparams->sourceNodeId = sourceNodeId;\n\tparams->sourceNodeName = sourceNode->workerName;\n\tparams->sourceNodePort = sourceNode->workerPort;\n\treturn params;\n}\n\n\n/*\n * TenantSchemaPickAnchorShardId returns the id of one of the shards\n * created in given tenant schema.\n *\n * Returns INVALID_SHARD_ID if the schema was initially empty or if it's not\n * a tenant schema.\n *\n * Throws an error if all the tables in the schema are concurrently dropped.\n */\nstatic uint64\nTenantSchemaPickAnchorShardId(Oid schemaId)\n{\n\tuint32 colocationId = SchemaIdGetTenantColocationId(schemaId);\n\tList *tablesInSchema = ColocationGroupTableList(colocationId, 0);\n\tif (list_length(tablesInSchema) == 0)\n\t{\n\t\treturn INVALID_SHARD_ID;\n\t}\n\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, tablesInSchema)\n\t{\n\t\t/*\n\t\t * Make sure the relation isn't dropped for the remainder of\n\t\t * the transaction.\n\t\t */\n\t\tLockRelationOid(relationId, AccessShareLock);\n\n\t\t/*\n\t\t * The relation might have been dropped just before we locked it.\n\t\t * Let's look it up.\n\t\t */\n\t\tRelation relation = RelationIdGetRelation(relationId);\n\t\tif (RelationIsValid(relation))\n\t\t{\n\t\t\t/* relation still exists, we can use it */\n\t\t\tRelationClose(relation);\n\t\t\treturn GetFirstShardId(relationId);\n\t\t}\n\t}\n\n\tereport(ERROR, (errmsg(\"tables in schema %s are concurrently dropped\",\n\t\t\t\t\t\t   get_namespace_name(schemaId))));\n}\n\n\n/*\n * ErrorIfTenantTable errors out with the given operation name,\n * if the given relation is a tenant table.\n */\nvoid\nErrorIfTenantTable(Oid relationId, const char *operationName)\n{\n\tif (IsTenantSchema(get_rel_namespace(relationId)))\n\t{\n\t\tereport(ERROR, (errmsg(\"%s is not allowed for %s because it belongs to \"\n\t\t\t\t\t\t\t   \"a distributed schema\",\n\t\t\t\t\t\t\t   generate_qualified_relation_name(relationId),\n\t\t\t\t\t\t\t   operationName)));\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/seclabel.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * seclabel.c\n *\n *    This file contains the logic of SECURITY LABEL statement propagation.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n\n/*\n * PostprocessRoleSecLabelStmt prepares the commands that need to be run on all workers to assign\n * security labels on distributed roles. It also ensures that all object dependencies exist on all\n * nodes for the role in the SecLabelStmt.\n */\nList *\nPostprocessRoleSecLabelStmt(Node *node, const char *queryString)\n{\n\tif (!EnableAlterRolePropagation || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tSecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node);\n\n\tList *objectAddresses = GetObjectAddressListFromParseTree(node, false, true);\n\tif (!IsAnyObjectDistributed(objectAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsurePropagationToCoordinator();\n\tEnsureAllObjectDependenciesExistOnAllNodes(objectAddresses);\n\n\tconst char *secLabelCommands = DeparseTreeNode((Node *) secLabelStmt);\n\tList *commandList = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t   (void *) secLabelCommands,\n\t\t\t\t\t\t\t\t   ENABLE_DDL_PROPAGATION);\n\treturn NodeDDLTaskList(REMOTE_NODES, commandList);\n}\n\n\n/*\n * PostprocessTableOrColumnSecLabelStmt prepares the commands that need to be run on all\n * workers to assign security labels on distributed tables or the columns of a distributed\n * table. It also ensures that all object dependencies exist on all nodes for the table in\n * the SecLabelStmt.\n */\nList *\nPostprocessTableOrColumnSecLabelStmt(Node *node, const char *queryString)\n{\n\tif (!EnableAlterRolePropagation || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tSecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node);\n\n\tList *objectAddresses = GetObjectAddressListFromParseTree(node, false, true);\n\tif (!IsAnyParentObjectDistributed(objectAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsurePropagationToCoordinator();\n\tEnsureAllObjectDependenciesExistOnAllNodes(objectAddresses);\n\n\tconst char *secLabelCommands = DeparseTreeNode((Node *) secLabelStmt);\n\tList *commandList = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t   (void *) secLabelCommands,\n\t\t\t\t\t\t\t\t   ENABLE_DDL_PROPAGATION);\n\tList *DDLJobs = NodeDDLTaskList(REMOTE_NODES, commandList);\n\tListCell *lc = NULL;\n\n\t/*\n\t * The label is for a table or a column, so we need to set the targetObjectAddress\n\t * of the DDLJob to the relationId of the table. This is needed to ensure that\n\t * the search path is correctly set for the remote security label command; it\n\t * needs to be able to resolve the table that the label is being defined on.\n\t */\n\tAssert(list_length(objectAddresses) == 1);\n\tObjectAddress *target = linitial(objectAddresses);\n\tOid relationId = target->objectId;\n\tAssert(relationId != InvalidOid);\n\n\tforeach(lc, DDLJobs)\n\t{\n\t\tDDLJob *ddlJob = (DDLJob *) lfirst(lc);\n\t\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\t}\n\n\treturn DDLJobs;\n}\n\n\n/*\n * PostprocessAnySecLabelStmt is used for any other object types\n * that are not supported by Citus. It issues a notice to the client\n * if appropriate. Is effectively a nop.\n */\nList *\nPostprocessAnySecLabelStmt(Node *node, const char *queryString)\n{\n\t/*\n\t * If we are not in the coordinator, we don't want to interrupt the security\n\t * label command with notices, the user expects that from the worker node\n\t * the command will not be propagated\n\t */\n\tif (EnableUnsupportedFeatureMessages && IsCoordinator())\n\t{\n\t\tereport(NOTICE, (errmsg(\"not propagating SECURITY LABEL commands whose \"\n\t\t\t\t\t\t\t\t\"object type is not role or table or column\"),\n\t\t\t\t\t\t errhint(\"Connect to worker nodes directly to manually \"\n\t\t\t\t\t\t\t\t \"run the same SECURITY LABEL command.\")));\n\t}\n\treturn NIL;\n}\n\n\n/*\n * SecLabelStmtObjectAddress returns the object address of the object on\n * which this statement operates (secLabelStmt->object). Note that it has no limitation\n * on the object type being OBJECT_ROLE. This is intentionally implemented like this\n * since it is fairly simple to implement and we might extend SECURITY LABEL propagation\n * in the future to include more object types.\n */\nList *\nSecLabelStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tSecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node);\n\n\tRelation rel = NULL;\n\tObjectAddress address = get_object_address(secLabelStmt->objtype,\n\t\t\t\t\t\t\t\t\t\t\t   secLabelStmt->object, &rel,\n\t\t\t\t\t\t\t\t\t\t\t   AccessShareLock, missing_ok);\n\tif (rel != NULL)\n\t{\n\t\trelation_close(rel, AccessShareLock);\n\t}\n\n\tObjectAddress *addressPtr = palloc0(sizeof(ObjectAddress));\n\t*addressPtr = address;\n\treturn list_make1(addressPtr);\n}\n\n\n/*\n * citus_test_object_relabel is a dummy function for check_object_relabel_type hook.\n * It is meant to be used in tests combined with citus_test_register_label_provider\n */\nvoid\ncitus_test_object_relabel(const ObjectAddress *object, const char *seclabel)\n{\n\tif (seclabel == NULL ||\n\t\tstrcmp(seclabel, \"citus_unclassified\") == 0 ||\n\t\tstrcmp(seclabel, \"citus_classified\") == 0 ||\n\t\tstrcmp(seclabel, \"citus '!unclassified\") == 0)\n\t{\n\t\treturn;\n\t}\n\n\tereport(ERROR,\n\t\t\t(errcode(ERRCODE_INVALID_NAME),\n\t\t\t errmsg(\"'%s' is not a valid security label for Citus tests.\", seclabel)));\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/sequence.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * sequence.c\n *     This file contains implementation of CREATE and ALTER SEQUENCE\n *     statement functions to run in a distributed setting\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_attrdef.h\"\n#include \"commands/defrem.h\"\n#include \"commands/extension.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"rewrite/rewriteHandler.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/sequence.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/worker_create_or_replace.h\"\n\n/* Local functions forward declarations for helper functions */\nstatic bool OptionsSpecifyOwnedBy(List *optionList, Oid *ownedByTableId);\nstatic Oid SequenceUsedInDistributedTable(const ObjectAddress *sequenceAddress, char\n\t\t\t\t\t\t\t\t\t\t  depType);\nstatic List * FilterDistributedSequences(GrantStmt *stmt);\n\n\n/*\n * ErrorIfUnsupportedSeqStmt errors out if the provided create sequence\n * statement specifies a distributed table in its OWNED BY clause.\n */\nvoid\nErrorIfUnsupportedSeqStmt(CreateSeqStmt *createSeqStmt)\n{\n\tOid ownedByTableId = InvalidOid;\n\n\t/* create is easy: just prohibit any distributed OWNED BY */\n\tif (OptionsSpecifyOwnedBy(createSeqStmt->options, &ownedByTableId))\n\t{\n\t\tif (IsCitusTable(ownedByTableId))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot create sequences that specify a distributed \"\n\t\t\t\t\t\t\t\t   \"table in their OWNED BY option\"),\n\t\t\t\t\t\t\terrhint(\"Use a sequence in a distributed table by specifying \"\n\t\t\t\t\t\t\t\t\t\"a serial column type before creating any shards.\")));\n\t\t}\n\t}\n}\n\n\n/*\n * ErrorIfDistributedAlterSeqOwnedBy errors out if the provided alter sequence\n * statement attempts to change the owned by property of a distributed sequence\n * or attempt to change a local sequence to be owned by a distributed table.\n */\nvoid\nErrorIfDistributedAlterSeqOwnedBy(AlterSeqStmt *alterSeqStmt)\n{\n\tOid sequenceId = RangeVarGetRelid(alterSeqStmt->sequence, AccessShareLock,\n\t\t\t\t\t\t\t\t\t  alterSeqStmt->missing_ok);\n\tOid ownedByTableId = InvalidOid;\n\tOid newOwnedByTableId = InvalidOid;\n\tint32 ownedByColumnId = 0;\n\tbool hasDistributedOwner = false;\n\n\t/* alter statement referenced nonexistent sequence; return */\n\tif (sequenceId == InvalidOid)\n\t{\n\t\treturn;\n\t}\n\n\tbool sequenceOwned = sequenceIsOwned(sequenceId, DEPENDENCY_AUTO, &ownedByTableId,\n\t\t\t\t\t\t\t\t\t\t &ownedByColumnId);\n\tif (!sequenceOwned)\n\t{\n\t\tsequenceOwned = sequenceIsOwned(sequenceId, DEPENDENCY_INTERNAL, &ownedByTableId,\n\t\t\t\t\t\t\t\t\t\t&ownedByColumnId);\n\t}\n\n\t/* see whether the sequence is already owned by a distributed table */\n\tif (sequenceOwned)\n\t{\n\t\thasDistributedOwner = IsCitusTable(ownedByTableId);\n\t}\n\n\tif (OptionsSpecifyOwnedBy(alterSeqStmt->options, &newOwnedByTableId))\n\t{\n\t\t/* if a distributed sequence tries to change owner, error */\n\t\tif (hasDistributedOwner && ownedByTableId != newOwnedByTableId)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot alter OWNED BY option of a sequence \"\n\t\t\t\t\t\t\t\t   \"already owned by a distributed table\")));\n\t\t}\n\t}\n}\n\n\n/*\n * OptionsSpecifyOwnedBy processes the options list of either a CREATE or ALTER\n * SEQUENCE command, extracting the first OWNED BY option it encounters. The\n * identifier for the specified table is placed in the Oid out parameter before\n * returning true. Returns false if no such option is found. Still returns true\n * for OWNED BY NONE, but leaves the out paramter set to InvalidOid.\n */\nstatic bool\nOptionsSpecifyOwnedBy(List *optionList, Oid *ownedByTableId)\n{\n\tDefElem *defElem = NULL;\n\tforeach_declared_ptr(defElem, optionList)\n\t{\n\t\tif (strcmp(defElem->defname, \"owned_by\") == 0)\n\t\t{\n\t\t\tList *ownedByNames = defGetQualifiedName(defElem);\n\t\t\tint nameCount = list_length(ownedByNames);\n\n\t\t\t/* if only one name is present, this is OWNED BY NONE */\n\t\t\tif (nameCount == 1)\n\t\t\t{\n\t\t\t\t*ownedByTableId = InvalidOid;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Otherwise, we have a list of schema, table, column, which we\n\t\t\t\t * need to truncate to simply the schema and table to determine\n\t\t\t\t * the relevant relation identifier.\n\t\t\t\t */\n\t\t\t\tList *relNameList = list_truncate(list_copy(ownedByNames), nameCount - 1);\n\t\t\t\tRangeVar *rangeVar = makeRangeVarFromNameList(relNameList);\n\t\t\t\tbool failOK = true;\n\n\t\t\t\t*ownedByTableId = RangeVarGetRelid(rangeVar, NoLock, failOK);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ExtractDefaultColumnsAndOwnedSequences finds each column of relation with\n * relationId that has a DEFAULT expression and each sequence owned by such\n * columns (if any). Then, appends the column name and id of the owned sequence\n * -that the column defaults- to the lists passed as NIL initially.\n */\nvoid\nExtractDefaultColumnsAndOwnedSequences(Oid relationId, List **columnNameList,\n\t\t\t\t\t\t\t\t\t   List **ownedSequenceIdList)\n{\n\tAssert(*columnNameList == NIL && *ownedSequenceIdList == NIL);\n\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\n\tfor (int attributeIndex = 0; attributeIndex < tupleDescriptor->natts;\n\t\t attributeIndex++)\n\t{\n\t\tForm_pg_attribute attributeForm = TupleDescAttr(tupleDescriptor, attributeIndex);\n\n\t\tif (IsDroppedOrGenerated(attributeForm))\n\t\t{\n\t\t\t/* skip dropped columns and columns with GENERATED AS ALWAYS expressions */\n\t\t\tcontinue;\n\t\t}\n\n\t\tchar *columnName = NameStr(attributeForm->attname);\n\t\tList *columnOwnedSequences =\n\t\t\tgetOwnedSequences_internal(relationId, attributeIndex + 1, DEPENDENCY_AUTO);\n\n\t\tif (attributeForm->atthasdef && list_length(columnOwnedSequences) == 0)\n\t\t{\n\t\t\t/*\n\t\t\t * Even if there are no owned sequences, the code path still\n\t\t\t * expects the columnName to be filled such that it can DROP\n\t\t\t * DEFAULT for the existing nextval('seq') columns.\n\t\t\t */\n\t\t\t*ownedSequenceIdList = lappend_oid(*ownedSequenceIdList, InvalidOid);\n\t\t\t*columnNameList = lappend(*columnNameList, columnName);\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tOid ownedSequenceId = InvalidOid;\n\t\tforeach_declared_oid(ownedSequenceId, columnOwnedSequences)\n\t\t{\n\t\t\t/*\n\t\t\t * A column might have multiple sequences one via OWNED BY one another\n\t\t\t * via bigserial/default nextval.\n\t\t\t */\n\t\t\t*ownedSequenceIdList = lappend_oid(*ownedSequenceIdList, ownedSequenceId);\n\t\t\t*columnNameList = lappend(*columnNameList, columnName);\n\t\t}\n\t}\n\n\trelation_close(relation, NoLock);\n}\n\n\n/*\n * ColumnDefaultsToNextVal returns true if the column with attrNumber\n * has a default expression that contains nextval().\n */\nbool\nColumnDefaultsToNextVal(Oid relationId, AttrNumber attrNumber)\n{\n\tAssert(AttributeNumberIsValid(attrNumber));\n\n\tRelation relation = RelationIdGetRelation(relationId);\n\tNode *defExpr = build_column_default(relation, attrNumber);\n\tRelationClose(relation);\n\n\tif (defExpr == NULL)\n\t{\n\t\t/* column doesn't have a DEFAULT expression */\n\t\treturn false;\n\t}\n\n\treturn contain_nextval_expression_walker(defExpr, NULL);\n}\n\n\n/*\n * PreprocessDropSequenceStmt gets called during the planning phase of a DROP SEQUENCE statement\n * and returns a list of DDLJob's that will drop any distributed sequences from the\n * workers.\n *\n * The DropStmt could have multiple objects to drop, the list of objects will be filtered\n * to only keep the distributed sequences for deletion on the workers. Non-distributed\n * sequences will still be dropped locally but not on the workers.\n */\nList *\nPreprocessDropSequenceStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tList *distributedSequencesList = NIL;\n\tList *distributedSequenceAddresses = NIL;\n\n\tAssert(stmt->removeType == OBJECT_SEQUENCE);\n\n\tif (creating_extension)\n\t{\n\t\t/*\n\t\t * extensions should be created separately on the workers, sequences cascading\n\t\t * from an extension should therefore not be propagated here.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tif (!EnableMetadataSync)\n\t{\n\t\t/*\n\t\t * we are configured to disable object propagation, should not propagate anything\n\t\t */\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * Our statements need to be fully qualified so we can drop them from the right schema\n\t * on the workers\n\t */\n\tQualifyTreeNode((Node *) stmt);\n\n\t/*\n\t * iterate over all sequences to be dropped and filter to keep only distributed\n\t * sequences.\n\t */\n\tList *deletingSequencesList = stmt->objects;\n\tList *objectNameList = NULL;\n\tforeach_declared_ptr(objectNameList, deletingSequencesList)\n\t{\n\t\tRangeVar *seq = makeRangeVarFromNameList(objectNameList);\n\n\t\tOid seqOid = RangeVarGetRelid(seq, NoLock, stmt->missing_ok);\n\n\t\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*sequenceAddress, RelationRelationId, seqOid);\n\t\tif (!IsAnyObjectDistributed(list_make1(sequenceAddress)))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* collect information for all distributed sequences */\n\t\tdistributedSequenceAddresses = lappend(distributedSequenceAddresses,\n\t\t\t\t\t\t\t\t\t\t\t   sequenceAddress);\n\t\tdistributedSequencesList = lappend(distributedSequencesList, objectNameList);\n\t}\n\n\tif (list_length(distributedSequencesList) <= 0)\n\t{\n\t\t/* no distributed functions to drop */\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * managing sequences can only be done on the coordinator if ddl propagation is on. when\n\t * it is off we will never get here. MX workers don't have a notion of distributed\n\t * sequences, so we block the call.\n\t */\n\tEnsureCoordinator();\n\n\t/* remove the entries for the distributed objects on dropping */\n\tObjectAddress *address = NULL;\n\tforeach_declared_ptr(address, distributedSequenceAddresses)\n\t{\n\t\tUnmarkObjectDistributed(address);\n\t}\n\n\t/*\n\t * Swap the list of objects before deparsing and restore the old list after. This\n\t * ensures we only have distributed sequences in the deparsed drop statement.\n\t */\n\tDropStmt *stmtCopy = copyObject(stmt);\n\tstmtCopy->objects = distributedSequencesList;\n\tconst char *dropStmtSql = DeparseTreeNode((Node *) stmtCopy);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) dropStmtSql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);\n}\n\n\n/*\n * SequenceDropStmtObjectAddress returns list of object addresses in the drop sequence\n * statement.\n */\nList *\nSequenceDropStmtObjectAddress(Node *stmt, bool missing_ok, bool isPostprocess)\n{\n\tDropStmt *dropSeqStmt = castNode(DropStmt, stmt);\n\n\tList *objectAddresses = NIL;\n\n\tList *droppingSequencesList = dropSeqStmt->objects;\n\tList *objectNameList = NULL;\n\tforeach_declared_ptr(objectNameList, droppingSequencesList)\n\t{\n\t\tRangeVar *seq = makeRangeVarFromNameList(objectNameList);\n\n\t\tOid seqOid = RangeVarGetRelid(seq, AccessShareLock, missing_ok);\n\n\t\tObjectAddress *objectAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*objectAddress, SequenceRelationId, seqOid);\n\t\tobjectAddresses = lappend(objectAddresses, objectAddress);\n\t}\n\n\treturn objectAddresses;\n}\n\n\n/*\n * PreprocessRenameSequenceStmt is called when the user is renaming a sequence. The invocation\n * happens before the statement is applied locally.\n *\n * As the sequence already exists we have access to the ObjectAddress, this is used to\n * check if it is distributed. If so the rename is executed on all the workers to keep the\n * types in sync across the cluster.\n */\nList *\nPreprocessRenameSequenceStmt(Node *node, const char *queryString, ProcessUtilityContext\n\t\t\t\t\t\t\t processUtilityContext)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_SEQUENCE);\n\n\tList *addresses = GetObjectAddressListFromParseTree((Node *) stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstmt->missing_ok, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(addresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tQualifyTreeNode((Node *) stmt);\n\n\tconst char *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION, (void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);\n}\n\n\n/*\n * RenameSequenceStmtObjectAddress returns the ObjectAddress of the sequence that is the\n * subject of the RenameStmt.\n */\nList *\nRenameSequenceStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_SEQUENCE);\n\n\tRangeVar *sequence = stmt->relation;\n\tOid seqOid = RangeVarGetRelid(sequence, NoLock, missing_ok);\n\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*sequenceAddress, RelationRelationId, seqOid);\n\n\treturn list_make1(sequenceAddress);\n}\n\n\n/*\n * PreprocessAlterSequenceStmt gets called during the planning phase of an ALTER SEQUENCE statement\n * of one of the following forms:\n * ALTER SEQUENCE [ IF EXISTS ] name\n *  [ AS data_type ]\n *  [ INCREMENT [ BY ] increment ]\n *  [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ]\n *  [ START [ WITH ] start ]\n *  [ RESTART [ [ WITH ] restart ] ]\n *  [ CACHE cache ] [ [ NO ] CYCLE ]\n *  [ OWNED BY { table_name.column_name | NONE } ]\n *\n * For distributed sequences, this operation will not be allowed for now.\n * The reason is that we change sequence parameters when distributing it, so we don't want to\n * touch those parameters for now.\n */\nList *\nPreprocessAlterSequenceStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tAlterSeqStmt *stmt = castNode(AlterSeqStmt, node);\n\n\tList *addresses = GetObjectAddressListFromParseTree((Node *) stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstmt->missing_ok, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *address = linitial(addresses);\n\n\t/* error out if the sequence is distributed */\n\tif (IsAnyObjectDistributed(addresses) || SequenceUsedInDistributedTable(address,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tDEPENDENCY_INTERNAL))\n\t{\n\t\tereport(ERROR, (errmsg(\"Altering a distributed sequence \"\n\t\t\t\t\t\t\t   \"is currently not supported.\")));\n\t}\n\n\t/*\n\t * error out if the sequence is used in a distributed table\n\t * and this is an ALTER SEQUENCE .. AS .. statement\n\t */\n\tOid citusTableId = SequenceUsedInDistributedTable(address, DEPENDENCY_AUTO);\n\tif (citusTableId != InvalidOid)\n\t{\n\t\tList *options = stmt->options;\n\t\tDefElem *defel = NULL;\n\t\tforeach_declared_ptr(defel, options)\n\t\t{\n\t\t\tif (strcmp(defel->defname, \"as\") == 0)\n\t\t\t{\n\t\t\t\tif (IsCitusTableType(citusTableId, CITUS_LOCAL_TABLE))\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\t\t\"Altering a sequence used in a local table that\"\n\t\t\t\t\t\t\t\t\t\t\" is added to metadata is currently not supported.\")));\n\t\t\t\t}\n\t\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\t\"Altering a sequence used in a distributed\"\n\t\t\t\t\t\t\t\t\t\" table is currently not supported.\")));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn NIL;\n}\n\n\n/*\n * SequenceUsedInDistributedTable returns true if the argument sequence\n * is used as the default value of a column in a distributed table.\n * Returns false otherwise\n * See DependencyType for the possible values of depType.\n * We use DEPENDENCY_INTERNAL for sequences created by identity column.\n * DEPENDENCY_AUTO for regular sequences.\n */\nstatic Oid\nSequenceUsedInDistributedTable(const ObjectAddress *sequenceAddress, char depType)\n{\n\tOid relationId;\n\tList *relations = GetDependentRelationsWithSequence(sequenceAddress->objectId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdepType);\n\tforeach_declared_oid(relationId, relations)\n\t{\n\t\tif (IsCitusTable(relationId))\n\t\t{\n\t\t\treturn relationId;\n\t\t}\n\t}\n\n\treturn InvalidOid;\n}\n\n\n/*\n * AlterSequenceStmtObjectAddress returns the ObjectAddress of the sequence that is the\n * subject of the AlterSeqStmt.\n */\nList *\nAlterSequenceStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterSeqStmt *stmt = castNode(AlterSeqStmt, node);\n\n\tRangeVar *sequence = stmt->sequence;\n\tOid seqOid = RangeVarGetRelid(sequence, NoLock, stmt->missing_ok);\n\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*sequenceAddress, RelationRelationId, seqOid);\n\n\treturn list_make1(sequenceAddress);\n}\n\n\n/*\n * PreprocessAlterSequenceSchemaStmt is executed before the statement is applied to the local\n * postgres instance.\n *\n * In this stage we can prepare the commands that need to be run on all workers.\n */\nList *\nPreprocessAlterSequenceSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_SEQUENCE);\n\n\tList *addresses = GetObjectAddressListFromParseTree((Node *) stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstmt->missing_ok, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(addresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tQualifyTreeNode((Node *) stmt);\n\n\tconst char *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION, (void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);\n}\n\n\n/*\n * AlterSequenceSchemaStmtObjectAddress returns the ObjectAddress of the sequence that is\n * the subject of the AlterObjectSchemaStmt.\n */\nList *\nAlterSequenceSchemaStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_SEQUENCE);\n\n\tRangeVar *sequence = stmt->relation;\n\tOid seqOid = RangeVarGetRelid(sequence, NoLock, true);\n\n\tif (seqOid == InvalidOid)\n\t{\n\t\t/*\n\t\t * couldn't find the sequence, might have already been moved to the new schema, we\n\t\t * construct a new sequence name that uses the new schema to search in.\n\t\t */\n\t\tconst char *newSchemaName = stmt->newschema;\n\t\tOid newSchemaOid = get_namespace_oid(newSchemaName, true);\n\t\tseqOid = get_relname_relid(sequence->relname, newSchemaOid);\n\n\t\tif (!missing_ok && seqOid == InvalidOid)\n\t\t{\n\t\t\t/*\n\t\t\t * if the sequence is still invalid we couldn't find the sequence, error with the same\n\t\t\t * message postgres would error with if missing_ok is false (not ok to miss)\n\t\t\t */\n\t\t\tconst char *quotedSequenceName =\n\t\t\t\tquote_qualified_identifier(sequence->schemaname, sequence->relname);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE),\n\t\t\t\t\t\t\terrmsg(\"relation \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t\t   quotedSequenceName)));\n\t\t}\n\t}\n\n\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*sequenceAddress, RelationRelationId, seqOid);\n\n\treturn list_make1(sequenceAddress);\n}\n\n\n/*\n * PostprocessAlterSequenceSchemaStmt is executed after the change has been applied locally,\n * we can now use the new dependencies of the sequence to ensure all its dependencies\n * exist on the workers before we apply the commands remotely.\n */\nList *\nPostprocessAlterSequenceSchemaStmt(Node *node, const char *queryString)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_SEQUENCE);\n\tList *addresses = GetObjectAddressListFromParseTree((Node *) stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstmt->missing_ok, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(addresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* dependencies have changed (schema) let's ensure they exist */\n\tEnsureAllObjectDependenciesExistOnAllNodes(addresses);\n\n\treturn NIL;\n}\n\n\n/*\n * PreprocessAlterSequenceOwnerStmt is called for change of ownership of sequences before the\n * ownership is changed on the local instance.\n *\n * If the sequence for which the owner is changed is distributed we execute the change on\n * all the workers to keep the type in sync across the cluster.\n */\nList *\nPreprocessAlterSequenceOwnerStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tList *sequenceAddresses = GetObjectAddressListFromParseTree((Node *) stmt, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(sequenceAddresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(sequenceAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tQualifyTreeNode((Node *) stmt);\n\n\tconst char *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION, (void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);\n}\n\n\n/*\n * AlterSequenceOwnerStmtObjectAddress returns the ObjectAddress of the sequence that is the\n * subject of the AlterOwnerStmt.\n */\nList *\nAlterSequenceOwnerStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tRangeVar *sequence = stmt->relation;\n\tOid seqOid = RangeVarGetRelid(sequence, NoLock, missing_ok);\n\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*sequenceAddress, RelationRelationId, seqOid);\n\n\treturn list_make1(sequenceAddress);\n}\n\n\n/*\n * PostprocessAlterSequenceOwnerStmt is executed after the change has been applied locally,\n * we can now use the new dependencies of the sequence to ensure all its dependencies\n * exist on the workers before we apply the commands remotely.\n */\nList *\nPostprocessAlterSequenceOwnerStmt(Node *node, const char *queryString)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tList *sequenceAddresses = GetObjectAddressListFromParseTree((Node *) stmt, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttrue);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(sequenceAddresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(sequenceAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* dependencies have changed (owner) let's ensure they exist */\n\tEnsureAllObjectDependenciesExistOnAllNodes(sequenceAddresses);\n\n\treturn NIL;\n}\n\n\n/*\n * PreprocessAlterSequencePersistenceStmt is called for change of persistence\n * of sequences before the persistence is changed on the local instance.\n *\n * If the sequence for which the persistence is changed is distributed, we execute\n * the change on all the workers to keep the type in sync across the cluster.\n */\nList *\nPreprocessAlterSequencePersistenceStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tList *sequenceAddresses = GetObjectAddressListFromParseTree((Node *) stmt, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(sequenceAddresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(sequenceAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tQualifyTreeNode((Node *) stmt);\n\n\tconst char *sql = DeparseTreeNode((Node *) stmt);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION, (void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);\n}\n\n\n/*\n * AlterSequencePersistenceStmtObjectAddress returns the ObjectAddress of the\n * sequence that is the subject of the AlterPersistenceStmt.\n */\nList *\nAlterSequencePersistenceStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tRangeVar *sequence = stmt->relation;\n\tOid seqOid = RangeVarGetRelid(sequence, NoLock, missing_ok);\n\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*sequenceAddress, RelationRelationId, seqOid);\n\n\treturn list_make1(sequenceAddress);\n}\n\n\n/*\n * PreprocessSequenceAlterTableStmt is called for change of persistence or owner\n * of sequences before the persistence/owner is changed on the local instance.\n *\n * Altering persistence or owner are the only ALTER commands of a sequence\n * that may pass through an AlterTableStmt as well\n */\nList *\nPreprocessSequenceAlterTableStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tListCell *cmdCell = NULL;\n\tforeach(cmdCell, stmt->cmds)\n\t{\n\t\tAlterTableCmd *cmd = castNode(AlterTableCmd, lfirst(cmdCell));\n\t\tswitch (cmd->subtype)\n\t\t{\n\t\t\tcase AT_ChangeOwner:\n\t\t\t{\n\t\t\t\treturn PreprocessAlterSequenceOwnerStmt(node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tqueryString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tprocessUtilityContext);\n\t\t\t}\n\n\t\t\tcase AT_SetLogged:\n\t\t\t{\n\t\t\t\treturn PreprocessAlterSequencePersistenceStmt(node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  processUtilityContext);\n\t\t\t}\n\n\t\t\tcase AT_SetUnLogged:\n\t\t\t{\n\t\t\t\treturn PreprocessAlterSequencePersistenceStmt(node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  processUtilityContext);\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\t/* normally we shouldn't ever reach this */\n\t\t\t\tereport(ERROR, (errmsg(\"unsupported subtype for alter sequence command\"),\n\t\t\t\t\t\t\t\terrdetail(\"sub command type: %d\",\n\t\t\t\t\t\t\t\t\t\t  cmd->subtype)));\n\t\t\t}\n\t\t}\n\t}\n\treturn NIL;\n}\n\n\n/*\n * PreprocessGrantOnSequenceStmt is executed before the statement is applied to the local\n * postgres instance.\n *\n * In this stage we can prepare the commands that need to be run on all workers to grant\n * on distributed sequences.\n */\nList *\nPreprocessGrantOnSequenceStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tif (creating_extension)\n\t{\n\t\t/*\n\t\t * extensions should be created separately on the workers, sequences cascading\n\t\t * from an extension should therefore not be propagated here.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tif (!EnableMetadataSync)\n\t{\n\t\t/*\n\t\t * we are configured to disable object propagation, should not propagate anything\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tList *distributedSequences = FilterDistributedSequences(stmt);\n\n\tif (list_length(distributedSequences) == 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tGrantStmt *stmtCopy = copyObject(stmt);\n\tstmtCopy->objects = distributedSequences;\n\n\t/*\n\t * if the original command was targeting schemas, we have expanded to the distributed\n\t * sequences in these schemas through FilterDistributedSequences.\n\t */\n\tstmtCopy->targtype = ACL_TARGET_OBJECT;\n\n\tQualifyTreeNode((Node *) stmtCopy);\n\n\tchar *sql = DeparseTreeNode((Node *) stmtCopy);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION, (void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_METADATA_NODES, commands);\n}\n\n\n/*\n * PostprocessGrantOnSequenceStmt makes sure dependencies of each\n * distributed sequence in the statement exist on all nodes\n */\nList *\nPostprocessGrantOnSequenceStmt(Node *node, const char *queryString)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tList *distributedSequences = FilterDistributedSequences(stmt);\n\n\tif (list_length(distributedSequences) == 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tRangeVar *sequence = NULL;\n\tforeach_declared_ptr(sequence, distributedSequences)\n\t{\n\t\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\t\tOid sequenceOid = RangeVarGetRelid(sequence, NoLock, false);\n\t\tObjectAddressSet(*sequenceAddress, RelationRelationId, sequenceOid);\n\t\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(sequenceAddress));\n\t}\n\treturn NIL;\n}\n\n\n/*\n * GenerateBackupNameForSequenceCollision generates a new sequence name for an existing\n * sequence. The name is generated in such a way that the new name doesn't overlap with\n * an existing relation by adding a suffix with incrementing number after the new name.\n */\nchar *\nGenerateBackupNameForSequenceCollision(const ObjectAddress *address)\n{\n\tchar *newName = palloc0(NAMEDATALEN);\n\tchar suffix[NAMEDATALEN] = { 0 };\n\tint count = 0;\n\tchar *namespaceName = get_namespace_name(get_rel_namespace(address->objectId));\n\tOid schemaId = get_namespace_oid(namespaceName, false);\n\n\tchar *baseName = get_rel_name(address->objectId);\n\tint baseLength = strlen(baseName);\n\n\twhile (true)\n\t{\n\t\tint suffixLength = SafeSnprintf(suffix, NAMEDATALEN - 1, \"(citus_backup_%d)\",\n\t\t\t\t\t\t\t\t\t\tcount);\n\n\t\t/* trim the base name at the end to leave space for the suffix and trailing \\0 */\n\t\tbaseLength = Min(baseLength, NAMEDATALEN - suffixLength - 1);\n\n\t\t/* clear newName before copying the potentially trimmed baseName and suffix */\n\t\tmemset(newName, 0, NAMEDATALEN);\n\t\tstrncpy_s(newName, NAMEDATALEN, baseName, baseLength);\n\t\tstrncpy_s(newName + baseLength, NAMEDATALEN - baseLength, suffix,\n\t\t\t\t  suffixLength);\n\n\t\tOid newRelationId = get_relname_relid(newName, schemaId);\n\t\tif (newRelationId == InvalidOid)\n\t\t{\n\t\t\treturn newName;\n\t\t}\n\n\t\tcount++;\n\t}\n}\n\n\n/*\n * FilterDistributedSequences determines and returns a list of distributed sequences\n * RangeVar-s from given grant statement.\n * - If the stmt's targtype is ACL_TARGET_OBJECT, i.e. of the form GRANT ON SEQUENCE ...\n *   it returns the distributed sequences in the list of sequences in the statement\n * - If targtype is ACL_TARGET_ALL_IN_SCHEMA, i.e. GRANT ON ALL SEQUENCES IN SCHEMA ...\n *   it expands the ALL IN SCHEMA to the actual sequences, and returns the distributed\n *   sequences from those.\n */\nstatic List *\nFilterDistributedSequences(GrantStmt *stmt)\n{\n\tbool grantOnSequenceCommand = (stmt->targtype == ACL_TARGET_OBJECT &&\n\t\t\t\t\t\t\t\t   stmt->objtype == OBJECT_SEQUENCE);\n\tbool grantOnAllSequencesInSchemaCommand = (stmt->targtype == ACL_TARGET_ALL_IN_SCHEMA\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t&&\n\t\t\t\t\t\t\t\t\t\t\t   stmt->objtype == OBJECT_SEQUENCE);\n\n\t/* we are only interested in sequence level grants */\n\tif (!grantOnSequenceCommand && !grantOnAllSequencesInSchemaCommand)\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *grantSequenceList = NIL;\n\n\tif (grantOnAllSequencesInSchemaCommand)\n\t{\n\t\t/* iterate over all namespace names provided to get their oid's */\n\t\tList *namespaceOidList = NIL;\n\t\tString *namespaceValue = NULL;\n\t\tforeach_declared_ptr(namespaceValue, stmt->objects)\n\t\t{\n\t\t\tchar *nspname = strVal(namespaceValue);\n\t\t\tbool missing_ok = false;\n\t\t\tOid namespaceOid = get_namespace_oid(nspname, missing_ok);\n\t\t\tnamespaceOidList = list_append_unique_oid(namespaceOidList, namespaceOid);\n\t\t}\n\n\t\t/*\n\t\t * iterate over all distributed sequences to filter the ones\n\t\t * that belong to one of the namespaces from above\n\t\t */\n\t\tList *distributedSequenceList = DistributedSequenceList();\n\t\tObjectAddress *sequenceAddress = NULL;\n\t\tforeach_declared_ptr(sequenceAddress, distributedSequenceList)\n\t\t{\n\t\t\tOid namespaceOid = get_rel_namespace(sequenceAddress->objectId);\n\n\t\t\t/*\n\t\t\t * if this distributed sequence's schema is one of the schemas\n\t\t\t * specified in the GRANT .. ALL SEQUENCES IN SCHEMA ..\n\t\t\t * add it to the list\n\t\t\t */\n\t\t\tif (list_member_oid(namespaceOidList, namespaceOid))\n\t\t\t{\n\t\t\t\tRangeVar *distributedSequence = makeRangeVar(\n\t\t\t\t\tget_namespace_name(namespaceOid),\n\t\t\t\t\tget_rel_name(sequenceAddress->objectId),\n\t\t\t\t\t-1);\n\t\t\t\tgrantSequenceList = lappend(grantSequenceList, distributedSequence);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tbool missing_ok = false;\n\t\tRangeVar *sequenceRangeVar = NULL;\n\t\tforeach_declared_ptr(sequenceRangeVar, stmt->objects)\n\t\t{\n\t\t\tOid sequenceOid = RangeVarGetRelid(sequenceRangeVar, NoLock, missing_ok);\n\t\t\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\t\t\tObjectAddressSet(*sequenceAddress, RelationRelationId, sequenceOid);\n\n\t\t\t/*\n\t\t\t * if this sequence from GRANT .. ON SEQUENCE .. is a distributed\n\t\t\t * sequence, add it to the list\n\t\t\t */\n\t\t\tif (IsAnyObjectDistributed(list_make1(sequenceAddress)))\n\t\t\t{\n\t\t\t\tgrantSequenceList = lappend(grantSequenceList, sequenceRangeVar);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn grantSequenceList;\n}\n\n\n/*\n * RenameExistingSequenceWithDifferentTypeIfExists renames the sequence's type if\n * that sequence exists and the desired sequence type is different than its type.\n */\nvoid\nRenameExistingSequenceWithDifferentTypeIfExists(RangeVar *sequence, Oid desiredSeqTypeId)\n{\n\tOid sequenceOid;\n\tRangeVarGetAndCheckCreationNamespace(sequence, NoLock, &sequenceOid);\n\n\tif (OidIsValid(sequenceOid))\n\t{\n\t\tForm_pg_sequence pgSequenceForm = pg_get_sequencedef(sequenceOid);\n\t\tif (pgSequenceForm->seqtypid != desiredSeqTypeId)\n\t\t{\n\t\t\tObjectAddress sequenceAddress = { 0 };\n\t\t\tObjectAddressSet(sequenceAddress, RelationRelationId, sequenceOid);\n\n\t\t\tchar *newName = GenerateBackupNameForCollision(&sequenceAddress);\n\n\t\t\tRenameStmt *renameStmt = CreateRenameStatement(&sequenceAddress, newName);\n\t\t\tconst char *sqlRenameStmt = DeparseTreeNode((Node *) renameStmt);\n\t\t\tProcessUtilityParseTree((Node *) renameStmt, sqlRenameStmt,\n\t\t\t\t\t\t\t\t\tPROCESS_UTILITY_QUERY,\n\t\t\t\t\t\t\t\t\tNULL, None_Receiver, NULL);\n\n\t\t\tCommandCounterIncrement();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/serialize_distributed_ddls.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * serialize_distributed_ddls.c\n *\n * This file contains functions for serializing distributed DDLs.\n *\n * If you're adding support for serializing a new DDL, you should\n * extend the following functions to support the new object class:\n *   AcquireCitusAdvisoryObjectClassLockGetOid()\n *   AcquireCitusAdvisoryObjectClassLockCheckPrivileges()\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/dependency.h\"\n#include \"catalog/pg_database_d.h\"\n#include \"commands/dbcommands.h\"\n#include \"storage/lock.h\"\n#include \"utils/builtins.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/argutils.h\"\n#include \"distributed/commands/serialize_distributed_ddls.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/resource_lock.h\"\n\n\nPG_FUNCTION_INFO_V1(citus_internal_acquire_citus_advisory_object_class_lock);\n\n\nstatic void SerializeDistributedDDLsOnObjectClassInternal(ObjectClass objectClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  char *qualifiedObjectName);\nstatic char * AcquireCitusAdvisoryObjectClassLockCommand(ObjectClass objectClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t char *qualifiedObjectName);\nstatic void AcquireCitusAdvisoryObjectClassLock(ObjectClass objectClass,\n\t\t\t\t\t\t\t\t\t\t\t\tchar *qualifiedObjectName);\nstatic Oid AcquireCitusAdvisoryObjectClassLockGetOid(ObjectClass objectClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t char *qualifiedObjectName);\nstatic void AcquireCitusAdvisoryObjectClassLockCheckPrivileges(ObjectClass objectClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   Oid oid);\n\n\n/*\n * citus_internal_acquire_citus_advisory_object_class_lock is an internal UDF\n * to call AcquireCitusAdvisoryObjectClassLock().\n */\nDatum\ncitus_internal_acquire_citus_advisory_object_class_lock(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tPG_ENSURE_ARGNOTNULL(0, \"object_class\");\n\tObjectClass objectClass = PG_GETARG_INT32(0);\n\n\tchar *qualifiedObjectName = PG_ARGISNULL(1) ? NULL : PG_GETARG_CSTRING(1);\n\n\tAcquireCitusAdvisoryObjectClassLock(objectClass, qualifiedObjectName);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * SerializeDistributedDDLsOnObjectClass is a wrapper around\n * SerializeDistributedDDLsOnObjectClassInternal to acquire the lock on given\n * object class itself, see the comment in header file for more details about\n * the difference between this function and\n * SerializeDistributedDDLsOnObjectClassObject().\n */\nvoid\nSerializeDistributedDDLsOnObjectClass(ObjectClass objectClass)\n{\n\tSerializeDistributedDDLsOnObjectClassInternal(objectClass, NULL);\n}\n\n\n/*\n * SerializeDistributedDDLsOnObjectClassObject is a wrapper around\n * SerializeDistributedDDLsOnObjectClassInternal to acquire the lock on given\n * object that belongs to given object class, see the comment in header file\n * for more details about the difference between this function and\n * SerializeDistributedDDLsOnObjectClass().\n */\nvoid\nSerializeDistributedDDLsOnObjectClassObject(ObjectClass objectClass,\n\t\t\t\t\t\t\t\t\t\t\tchar *qualifiedObjectName)\n{\n\tif (qualifiedObjectName == NULL)\n\t{\n\t\telog(ERROR, \"qualified object name cannot be NULL\");\n\t}\n\n\tSerializeDistributedDDLsOnObjectClassInternal(objectClass, qualifiedObjectName);\n}\n\n\n/*\n * SerializeDistributedDDLsOnObjectClassInternal serializes distributed DDLs\n * that target given object class by acquiring a Citus specific advisory lock\n * on the first primary worker node if there are any workers in the cluster.\n *\n * The lock is acquired via a coordinated transaction. For this reason,\n * it automatically gets released when (maybe implicit) transaction on\n * current server commits or rolls back.\n *\n * If qualifiedObjectName is provided to be non-null, then the oid of the\n * object is first resolved on the first primary worker node and then the\n * lock is acquired on that oid. If qualifiedObjectName is null, then the\n * lock is acquired on the object class itself.\n *\n * Note that those two lock types don't conflict with each other and are\n * acquired for different purposes. The lock on the object class\n * (qualifiedObjectName = NULL) is used to serialize DDLs that target the\n * object class itself, e.g., when creating a new object of that class, and\n * the latter is used to serialize DDLs that target a specific object of\n * that class, e.g., when altering an object.\n *\n * In some cases, we may want to acquire both locks at the same time. For\n * example, when renaming a database, we want to acquire both lock types\n * because while the object class lock is used to ensure that another session\n * doesn't create a new database with the same name, the object lock is used\n * to ensure that another session doesn't alter the same database.\n */\nstatic void\nSerializeDistributedDDLsOnObjectClassInternal(ObjectClass objectClass,\n\t\t\t\t\t\t\t\t\t\t\t  char *qualifiedObjectName)\n{\n\tWorkerNode *firstWorkerNode = GetFirstPrimaryWorkerNode();\n\tif (firstWorkerNode == NULL)\n\t{\n\t\t/*\n\t\t * If there are no worker nodes in the cluster, then we don't need\n\t\t * to acquire the lock at all; and we cannot indeed.\n\t\t */\n\t\treturn;\n\t}\n\n\t/*\n\t * Indeed we would already ensure permission checks in remote node\n\t * --via AcquireCitusAdvisoryObjectClassLock()-- but we first do so on\n\t * the local node to avoid from reporting confusing error messages.\n\t */\n\tOid oid = AcquireCitusAdvisoryObjectClassLockGetOid(objectClass, qualifiedObjectName);\n\tAcquireCitusAdvisoryObjectClassLockCheckPrivileges(objectClass, oid);\n\n\tTask *task = CitusMakeNode(Task);\n\ttask->taskType = DDL_TASK;\n\n\tchar *command = AcquireCitusAdvisoryObjectClassLockCommand(objectClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   qualifiedObjectName);\n\tSetTaskQueryString(task, command);\n\n\tShardPlacement *targetPlacement = CitusMakeNode(ShardPlacement);\n\tSetPlacementNodeMetadata(targetPlacement, firstWorkerNode);\n\ttask->taskPlacementList = list_make1(targetPlacement);\n\n\t/* need to be in a transaction to acquire a lock that's bound to transactions */\n\tUseCoordinatedTransaction();\n\n\tbool localExecutionSupported = true;\n\tExecuteUtilityTaskList(list_make1(task), localExecutionSupported);\n}\n\n\n/*\n * AcquireCitusAdvisoryObjectClassLockCommand returns a command to call\n * citus_internal.acquire_citus_advisory_object_class_lock().\n */\nstatic char *\nAcquireCitusAdvisoryObjectClassLockCommand(ObjectClass objectClass,\n\t\t\t\t\t\t\t\t\t\t   char *qualifiedObjectName)\n{\n\t/* safe to cast to int as it's an enum */\n\tint objectClassInt = (int) objectClass;\n\n\tchar *quotedObjectName =\n\t\t!qualifiedObjectName ? \"NULL\" :\n\t\tquote_literal_cstr(qualifiedObjectName);\n\n\tStringInfo command = makeStringInfo();\n\tappendStringInfo(command,\n\t\t\t\t\t \"SELECT citus_internal.acquire_citus_advisory_object_class_lock(%d, %s)\",\n\t\t\t\t\t objectClassInt, quotedObjectName);\n\n\treturn command->data;\n}\n\n\n/*\n * AcquireCitusAdvisoryObjectClassLock acquires a Citus specific advisory\n * ExclusiveLock based on given object class.\n */\nstatic void\nAcquireCitusAdvisoryObjectClassLock(ObjectClass objectClass, char *qualifiedObjectName)\n{\n\tOid oid = AcquireCitusAdvisoryObjectClassLockGetOid(objectClass, qualifiedObjectName);\n\n\tAcquireCitusAdvisoryObjectClassLockCheckPrivileges(objectClass, oid);\n\n\tLOCKTAG locktag;\n\tSET_LOCKTAG_GLOBAL_DDL_SERIALIZATION(locktag, objectClass, oid);\n\n\tLOCKMODE lockmode = ExclusiveLock;\n\tbool sessionLock = false;\n\tbool dontWait = false;\n\tLockAcquire(&locktag, lockmode, sessionLock, dontWait);\n}\n\n\n/*\n * AcquireCitusAdvisoryObjectClassLockGetOid returns the oid of given object\n * that belongs to given object class. If qualifiedObjectName is NULL, then\n * it returns InvalidOid.\n */\nstatic Oid\nAcquireCitusAdvisoryObjectClassLockGetOid(ObjectClass objectClass,\n\t\t\t\t\t\t\t\t\t\t  char *qualifiedObjectName)\n{\n\tif (qualifiedObjectName == NULL)\n\t{\n\t\treturn InvalidOid;\n\t}\n\n\tbool missingOk = false;\n\n\tswitch (objectClass)\n\t{\n\t\tcase OCLASS_DATABASE:\n\t\t{\n\t\t\treturn get_database_oid(qualifiedObjectName, missingOk);\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"unsupported object class: %d\", objectClass);\n\t\t}\n\t}\n}\n\n\n/*\n * AcquireCitusAdvisoryObjectClassLockCheckPrivileges is used to perform privilege checks\n * before acquiring the Citus specific advisory lock on given object class and oid.\n */\nstatic void\nAcquireCitusAdvisoryObjectClassLockCheckPrivileges(ObjectClass objectClass, Oid oid)\n{\n\tswitch (objectClass)\n\t{\n\t\tcase OCLASS_DATABASE:\n\t\t{\n\t\t\tif (OidIsValid(oid) && !object_ownercheck(DatabaseRelationId, oid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  GetUserId()))\n\t\t\t{\n\t\t\t\taclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,\n\t\t\t\t\t\t\t   get_database_name(oid));\n\t\t\t}\n\t\t\telse if (!OidIsValid(oid) && !have_createdb_privilege())\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),\n\t\t\t\t\t\t errmsg(\"permission denied to create / rename database\")));\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"unsupported object class: %d\", objectClass);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/statistics.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * statistics.c\n *    Commands for STATISTICS statements.\n *\n *    We currently support replicating statistics definitions on the\n *    coordinator in all the worker nodes in the form of\n *\n *    CREATE STATISTICS ... queries.\n *\n *    We also support dropping statistics from all the worker nodes in form of\n *\n *    DROP STATISTICS ... queries.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_statistic_ext.h\"\n#include \"catalog/pg_type.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/fmgrprotos.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/relcache.h\"\n#include \"utils/ruleutils.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/worker_transaction.h\"\n\n#define DEFAULT_STATISTICS_TARGET -1\n#define ALTER_INDEX_COLUMN_SET_STATS_COMMAND \\\n\t\t\"ALTER INDEX %s ALTER COLUMN %d SET STATISTICS %d\"\n\nstatic char * GenerateAlterIndexColumnSetStatsCommand(char *indexNameWithSchema,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  int16 attnum,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  int32 attstattarget);\nstatic Oid GetRelIdByStatsOid(Oid statsOid);\nstatic char * CreateAlterCommandIfOwnerNotDefault(Oid statsOid);\nstatic char * CreateAlterCommandIfTargetNotDefault(Oid statsOid);\n\n/*\n * PreprocessCreateStatisticsStmt is called during the planning phase for\n * CREATE STATISTICS.\n */\nList *\nPreprocessCreateStatisticsStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tCreateStatsStmt *stmt = castNode(CreateStatsStmt, node);\n\n\tNode *relationNode = (Node *) linitial(stmt->relations);\n\n\tif (!IsA(relationNode, RangeVar))\n\t{\n\t\treturn NIL;\n\t}\n\n\tRangeVar *relation = (RangeVar *) relationNode;\n\n\tOid relationId = RangeVarGetRelid(relation, ShareUpdateExclusiveLock, false);\n\n\tif (!IsCitusTable(relationId) || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tif (!(stmt->defnames))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot create statistics without a name on a \"\n\t\t\t\t\t\t\t   \"Citus table\"),\n\t\t\t\t\t\terrhint(\"Consider specifying a name for the statistics\")));\n\t}\n\n\tQualifyTreeNode((Node *) stmt);\n\n\tOid statsOid = get_statistics_object_oid(stmt->defnames, true);\n\tif (statsOid != InvalidOid)\n\t{\n\t\t/* if stats object already exists, don't create DDLJobs */\n\t\treturn NIL;\n\t}\n\n\tchar *ddlCommand = DeparseTreeNode((Node *) stmt);\n\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\tddlJob->startNewTransaction = false;\n\tddlJob->metadataSyncCommand = ddlCommand;\n\tddlJob->taskList = DDLTaskList(relationId, ddlCommand);\n\n\tList *ddlJobs = list_make1(ddlJob);\n\n\treturn ddlJobs;\n}\n\n\n/*\n * PostprocessCreateStatisticsStmt is called after a CREATE STATISTICS command has\n * been executed by standard process utility.\n */\nList *\nPostprocessCreateStatisticsStmt(Node *node, const char *queryString)\n{\n\tCreateStatsStmt *stmt = castNode(CreateStatsStmt, node);\n\tAssert(stmt->type == T_CreateStatsStmt);\n\n\tRangeVar *relation = (RangeVar *) linitial(stmt->relations);\n\tOid relationId = RangeVarGetRelid(relation, ShareUpdateExclusiveLock, false);\n\n\tif (!IsCitusTable(relationId) || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tbool missingOk = false;\n\tList *objectAddresses = GetObjectAddressListFromParseTree((Node *) stmt, missingOk,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(objectAddresses) == 1);\n\n\tEnsureAllObjectDependenciesExistOnAllNodes(objectAddresses);\n\n\treturn NIL;\n}\n\n\n/*\n * CreateStatisticsStmtObjectAddress finds the ObjectAddress for the statistics that\n * is created by given CreateStatsStmt. If missingOk is false and if statistics\n * does not exist, then it errors out.\n *\n * Never returns NULL, but the objid in the address can be invalid if missingOk\n * was set to true.\n */\nList *\nCreateStatisticsStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess)\n{\n\tCreateStatsStmt *stmt = castNode(CreateStatsStmt, node);\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tOid statsOid = get_statistics_object_oid(stmt->defnames, missingOk);\n\tObjectAddressSet(*address, StatisticExtRelationId, statsOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * PreprocessDropStatisticsStmt is called during the planning phase for\n * DROP STATISTICS.\n */\nList *\nPreprocessDropStatisticsStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tDropStmt *dropStatisticsStmt = castNode(DropStmt, node);\n\tAssert(dropStatisticsStmt->removeType == OBJECT_STATISTIC_EXT);\n\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tQualifyTreeNode((Node *) dropStatisticsStmt);\n\n\tList *ddlJobs = NIL;\n\tList *processedStatsOids = NIL;\n\tList *objectNameList = NULL;\n\tforeach_declared_ptr(objectNameList, dropStatisticsStmt->objects)\n\t{\n\t\tOid statsOid = get_statistics_object_oid(objectNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t dropStatisticsStmt->missing_ok);\n\n\t\tif (list_member_oid(processedStatsOids, statsOid))\n\t\t{\n\t\t\t/* skip duplicate entries in DROP STATISTICS */\n\t\t\tcontinue;\n\t\t}\n\n\t\tprocessedStatsOids = lappend_oid(processedStatsOids, statsOid);\n\n\t\tOid relationId = GetRelIdByStatsOid(statsOid);\n\n\t\tif (!OidIsValid(relationId) || !IsCitusTable(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tchar *ddlCommand = DeparseDropStatisticsStmt(objectNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t dropStatisticsStmt->missing_ok);\n\n\t\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\n\t\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\t\tddlJob->startNewTransaction = false;\n\t\tddlJob->metadataSyncCommand = ddlCommand;\n\t\tddlJob->taskList = DDLTaskList(relationId, ddlCommand);\n\n\t\tddlJobs = lappend(ddlJobs, ddlJob);\n\t}\n\n\treturn ddlJobs;\n}\n\n\n/*\n * DropStatisticsObjectAddress returns list of object addresses in the drop statistics\n * statement.\n */\nList *\nDropStatisticsObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tDropStmt *dropStatisticsStmt = castNode(DropStmt, node);\n\tAssert(dropStatisticsStmt->removeType == OBJECT_STATISTIC_EXT);\n\n\tList *objectAddresses = NIL;\n\n\tList *objectNameList = NULL;\n\tforeach_declared_ptr(objectNameList, dropStatisticsStmt->objects)\n\t{\n\t\tOid statsOid = get_statistics_object_oid(objectNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t dropStatisticsStmt->missing_ok);\n\n\t\tObjectAddress *objectAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*objectAddress, StatisticExtRelationId, statsOid);\n\t\tobjectAddresses = lappend(objectAddresses, objectAddress);\n\t}\n\n\treturn objectAddresses;\n}\n\n\n/*\n * PreprocessAlterStatisticsRenameStmt is called during the planning phase for\n * ALTER STATISTICS RENAME.\n */\nList *\nPreprocessAlterStatisticsRenameStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tRenameStmt *renameStmt = castNode(RenameStmt, node);\n\tAssert(renameStmt->renameType == OBJECT_STATISTIC_EXT);\n\n\tOid statsOid = get_statistics_object_oid((List *) renameStmt->object, false);\n\tOid relationId = GetRelIdByStatsOid(statsOid);\n\n\tif (!IsCitusTable(relationId) || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tQualifyTreeNode((Node *) renameStmt);\n\n\tchar *ddlCommand = DeparseTreeNode((Node *) renameStmt);\n\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\tddlJob->startNewTransaction = false;\n\tddlJob->metadataSyncCommand = ddlCommand;\n\tddlJob->taskList = DDLTaskList(relationId, ddlCommand);\n\n\tList *ddlJobs = list_make1(ddlJob);\n\n\treturn ddlJobs;\n}\n\n\n/*\n * PreprocessAlterStatisticsSchemaStmt is called during the planning phase for\n * ALTER STATISTICS SET SCHEMA.\n */\nList *\nPreprocessAlterStatisticsSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_STATISTIC_EXT);\n\n\tOid statsOid = get_statistics_object_oid((List *) stmt->object, false);\n\tOid relationId = GetRelIdByStatsOid(statsOid);\n\n\tif (!IsCitusTable(relationId) || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tQualifyTreeNode((Node *) stmt);\n\n\tchar *ddlCommand = DeparseTreeNode((Node *) stmt);\n\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\tddlJob->startNewTransaction = false;\n\tddlJob->metadataSyncCommand = ddlCommand;\n\tddlJob->taskList = DDLTaskList(relationId, ddlCommand);\n\n\tList *ddlJobs = list_make1(ddlJob);\n\n\treturn ddlJobs;\n}\n\n\n/*\n * PostprocessAlterStatisticsSchemaStmt is called after a ALTER STATISTICS SCHEMA\n * command has been executed by standard process utility.\n */\nList *\nPostprocessAlterStatisticsSchemaStmt(Node *node, const char *queryString)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_STATISTIC_EXT);\n\n\tString *statName = llast((List *) stmt->object);\n\tOid statsOid = get_statistics_object_oid(list_make2(makeString(stmt->newschema),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstatName), false);\n\tOid relationId = GetRelIdByStatsOid(statsOid);\n\n\tif (!IsCitusTable(relationId) || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tbool missingOk = false;\n\tList *objectAddresses = GetObjectAddressListFromParseTree((Node *) stmt, missingOk,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(objectAddresses) == 1);\n\n\tEnsureAllObjectDependenciesExistOnAllNodes(objectAddresses);\n\n\treturn NIL;\n}\n\n\n/*\n * AlterStatisticsSchemaStmtObjectAddress finds the ObjectAddress for the statistics\n * that is altered by given AlterObjectSchemaStmt. If missingOk is false and if\n * the statistics does not exist, then it errors out.\n *\n * Never returns NULL, but the objid in the address can be invalid if missingOk\n * was set to true.\n */\nList *\nAlterStatisticsSchemaStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tOid statsOid = InvalidOid;\n\n\tList *statName = (List *) stmt->object;\n\n\tif (isPostprocess)\n\t{\n\t\t/*\n\t\t * we should search the object in the new schema because the method is\n\t\t * called during postprocess, standard_utility should have already moved\n\t\t * the stat into new schema.\n\t\t */\n\t\tList *newStatName = list_make2(makeString(stmt->newschema), llast(statName));\n\t\tstatsOid = get_statistics_object_oid(newStatName, missingOk);\n\t}\n\telse\n\t{\n\t\tstatsOid = get_statistics_object_oid(statName, missingOk);\n\t}\n\n\tObjectAddressSet(*address, StatisticExtRelationId, statsOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * PreprocessAlterStatisticsStmt is called during the planning phase for\n * ALTER STATISTICS .. SET STATISTICS.\n */\nList *\nPreprocessAlterStatisticsStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tAlterStatsStmt *stmt = castNode(AlterStatsStmt, node);\n\n\tOid statsOid = get_statistics_object_oid(stmt->defnames, stmt->missing_ok);\n\n\tif (!OidIsValid(statsOid))\n\t{\n\t\t/*\n\t\t * If statsOid is invalid, here we can assume that the query includes\n\t\t * IF EXISTS clause, since get_statistics_object_oid would error out otherwise.\n\t\t * So here we can safely return NIL here without checking stmt->missing_ok.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tOid relationId = GetRelIdByStatsOid(statsOid);\n\n\tif (!IsCitusTable(relationId) || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tQualifyTreeNode((Node *) stmt);\n\n\tchar *ddlCommand = DeparseTreeNode((Node *) stmt);\n\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\tddlJob->startNewTransaction = false;\n\tddlJob->metadataSyncCommand = ddlCommand;\n\tddlJob->taskList = DDLTaskList(relationId, ddlCommand);\n\n\tList *ddlJobs = list_make1(ddlJob);\n\n\treturn ddlJobs;\n}\n\n\n/*\n * PreprocessAlterStatisticsOwnerStmt is called during the planning phase for\n * ALTER STATISTICS .. OWNER TO.\n */\nList *\nPreprocessAlterStatisticsOwnerStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_STATISTIC_EXT);\n\n\tOid statsOid = get_statistics_object_oid((List *) stmt->object, false);\n\tOid relationId = GetRelIdByStatsOid(statsOid);\n\n\tif (!IsCitusTable(relationId) || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tQualifyTreeNode((Node *) stmt);\n\n\tchar *ddlCommand = DeparseTreeNode((Node *) stmt);\n\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\tddlJob->startNewTransaction = false;\n\tddlJob->metadataSyncCommand = ddlCommand;\n\tddlJob->taskList = DDLTaskList(relationId, ddlCommand);\n\n\tList *ddlJobs = list_make1(ddlJob);\n\n\treturn ddlJobs;\n}\n\n\n/*\n * PostprocessAlterStatisticsOwnerStmt is invoked after the owner has been changed locally.\n * Since changing the owner could result in new dependencies being found for this object\n * we re-ensure all the dependencies for the statistics do exist.\n *\n * This is solely to propagate the new owner (and all its dependencies) if it was not\n * already distributed in the cluster.\n */\nList *\nPostprocessAlterStatisticsOwnerStmt(Node *node, const char *queryString)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_STATISTIC_EXT);\n\n\tOid statsOid = get_statistics_object_oid((List *) stmt->object, false);\n\tOid relationId = GetRelIdByStatsOid(statsOid);\n\n\tif (!IsCitusTable(relationId) || !ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tObjectAddress *statisticsAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*statisticsAddress, StatisticExtRelationId, statsOid);\n\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(statisticsAddress));\n\n\treturn NIL;\n}\n\n\n/*\n * GetExplicitStatisticsCommandList returns the list of DDL commands to create\n * or alter statistics that are explicitly created for the table with relationId.\n * This function gets called when distributing the table with relationId.\n */\nList *\nGetExplicitStatisticsCommandList(Oid relationId)\n{\n\tList *explicitStatisticsCommandList = NIL;\n\n\tRelation relation = RelationIdGetRelation(relationId);\n\tif (!RelationIsValid(relation))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not open relation with OID %u\", relationId)));\n\t}\n\n\tList *statisticsIdList = RelationGetStatExtList(relation);\n\tRelationClose(relation);\n\n\t/* generate fully-qualified names */\n\tint saveNestLevel = PushEmptySearchPath();\n\n\tOid statisticsId = InvalidOid;\n\tforeach_declared_oid(statisticsId, statisticsIdList)\n\t{\n\t\t/* we need create commands for already created stats before distribution */\n\t\tDatum commandText = DirectFunctionCall1(pg_get_statisticsobjdef,\n\t\t\t\t\t\t\t\t\t\t\t\tObjectIdGetDatum(statisticsId));\n\n\t\t/*\n\t\t * pg_get_statisticsobjdef doesn't throw an error if there is no such\n\t\t * statistics object, be on the safe side.\n\t\t */\n\t\tif (DatumGetPointer(commandText) == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"statistics with oid %u does not exist\",\n\t\t\t\t\t\t\t\t   statisticsId)));\n\t\t}\n\n\t\tchar *createStatisticsCommand = TextDatumGetCString(commandText);\n\n\t\texplicitStatisticsCommandList =\n\t\t\tlappend(explicitStatisticsCommandList,\n\t\t\t\t\tmakeTableDDLCommandString(createStatisticsCommand));\n\n\t\t/* we need to alter stats' target if it's getting distributed after creation */\n\t\tchar *alterStatisticsTargetCommand =\n\t\t\tCreateAlterCommandIfTargetNotDefault(statisticsId);\n\n\t\tif (alterStatisticsTargetCommand != NULL)\n\t\t{\n\t\t\texplicitStatisticsCommandList =\n\t\t\t\tlappend(explicitStatisticsCommandList,\n\t\t\t\t\t\tmakeTableDDLCommandString(alterStatisticsTargetCommand));\n\t\t}\n\n\t\t/* we need to alter stats' owner if it's getting distributed after creation */\n\t\tchar *alterStatisticsOwnerCommand =\n\t\t\tCreateAlterCommandIfOwnerNotDefault(statisticsId);\n\n\t\tif (alterStatisticsOwnerCommand != NULL)\n\t\t{\n\t\t\texplicitStatisticsCommandList =\n\t\t\t\tlappend(explicitStatisticsCommandList,\n\t\t\t\t\t\tmakeTableDDLCommandString(alterStatisticsOwnerCommand));\n\t\t}\n\t}\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n\n\treturn explicitStatisticsCommandList;\n}\n\n\n/*\n * GetExplicitStatisticsSchemaIdList returns the list of schema ids of statistics'\n * which are created on relation with given relation id.\n */\nList *\nGetExplicitStatisticsSchemaIdList(Oid relationId)\n{\n\tList *schemaIdList = NIL;\n\n\tRelation relation = RelationIdGetRelation(relationId);\n\tif (!RelationIsValid(relation))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not open relation with OID %u\", relationId)));\n\t}\n\n\tList *statsIdList = RelationGetStatExtList(relation);\n\tRelationClose(relation);\n\n\tOid statsId = InvalidOid;\n\tforeach_declared_oid(statsId, statsIdList)\n\t{\n\t\tHeapTuple heapTuple = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsId));\n\t\tif (!HeapTupleIsValid(heapTuple))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cache lookup failed for statistics \"\n\t\t\t\t\t\t\t\t   \"object with oid %u\", statsId)));\n\t\t}\n\t\tFormData_pg_statistic_ext *statisticsForm =\n\t\t\t(FormData_pg_statistic_ext *) GETSTRUCT(heapTuple);\n\n\t\tOid schemaId = statisticsForm->stxnamespace;\n\t\tif (!list_member_oid(schemaIdList, schemaId))\n\t\t{\n\t\t\tschemaIdList = lappend_oid(schemaIdList, schemaId);\n\t\t}\n\t\tReleaseSysCache(heapTuple);\n\t}\n\n\n\treturn schemaIdList;\n}\n\n\n/*\n * GetAlterIndexStatisticsCommands returns the list of ALTER INDEX .. ALTER COLUMN ..\n * SET STATISTICS commands, based on non default targets of the index with given id.\n * Note that this function only looks for expression indexes, since this command is\n * valid for only expression indexes.\n */\nList *\nGetAlterIndexStatisticsCommands(Oid indexOid)\n{\n\tList *alterIndexStatisticsCommandList = NIL;\n\tint16 exprCount = 1;\n\twhile (true)\n\t{\n\t\tHeapTuple attTuple = SearchSysCacheAttNum(indexOid, exprCount);\n\n\t\tif (!HeapTupleIsValid(attTuple))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tForm_pg_attribute targetAttr = (Form_pg_attribute) GETSTRUCT(attTuple);\n\t\tint32 targetAttstattarget = getAttstattarget_compat(attTuple);\n\t\tif (targetAttstattarget != DEFAULT_STATISTICS_TARGET)\n\t\t{\n\t\t\tchar *indexNameWithSchema = generate_qualified_relation_name(indexOid);\n\n\t\t\tchar *command =\n\t\t\t\tGenerateAlterIndexColumnSetStatsCommand(indexNameWithSchema,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetAttr->attnum,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetAttstattarget);\n\n\t\t\talterIndexStatisticsCommandList =\n\t\t\t\tlappend(alterIndexStatisticsCommandList,\n\t\t\t\t\t\tmakeTableDDLCommandString(command));\n\t\t}\n\n\t\tReleaseSysCache(attTuple);\n\t\texprCount++;\n\t}\n\n\treturn alterIndexStatisticsCommandList;\n}\n\n\n/*\n * GenerateAlterIndexColumnSetStatsCommand returns a string in form of 'ALTER INDEX ..\n * ALTER COLUMN .. SET STATISTICS ..' which will be used to create a DDL command to\n * send to workers.\n */\nstatic char *\nGenerateAlterIndexColumnSetStatsCommand(char *indexNameWithSchema,\n\t\t\t\t\t\t\t\t\t\tint16 attnum,\n\t\t\t\t\t\t\t\t\t\tint32 attstattarget)\n{\n\tStringInfoData command;\n\tinitStringInfo(&command);\n\tappendStringInfo(&command,\n\t\t\t\t\t ALTER_INDEX_COLUMN_SET_STATS_COMMAND,\n\t\t\t\t\t indexNameWithSchema,\n\t\t\t\t\t attnum,\n\t\t\t\t\t attstattarget);\n\n\treturn command.data;\n}\n\n\n/*\n * GetRelIdByStatsOid returns the relation id for given statistics oid.\n * If statistics doesn't exist, returns InvalidOid.\n */\nstatic Oid\nGetRelIdByStatsOid(Oid statsOid)\n{\n\tHeapTuple tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));\n\n\tif (!HeapTupleIsValid(tup))\n\t{\n\t\treturn InvalidOid;\n\t}\n\n\tForm_pg_statistic_ext statisticsForm = (Form_pg_statistic_ext) GETSTRUCT(tup);\n\tReleaseSysCache(tup);\n\n\treturn statisticsForm->stxrelid;\n}\n\n\n/*\n * CreateAlterCommandIfOwnerNotDefault returns an ALTER STATISTICS .. OWNER TO\n * command if the stats object with given id has an owner different than the default one.\n * Returns NULL otherwise.\n */\nstatic char *\nCreateAlterCommandIfOwnerNotDefault(Oid statsOid)\n{\n\tHeapTuple tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));\n\n\tif (!HeapTupleIsValid(tup))\n\t{\n\t\tereport(WARNING, (errmsg(\"No stats object found with id: %u\", statsOid)));\n\t\treturn NULL;\n\t}\n\n\tForm_pg_statistic_ext statisticsForm = (Form_pg_statistic_ext) GETSTRUCT(tup);\n\tReleaseSysCache(tup);\n\n\tif (statisticsForm->stxowner == GetUserId())\n\t{\n\t\treturn NULL;\n\t}\n\n\tchar *schemaName = get_namespace_name(statisticsForm->stxnamespace);\n\tchar *statName = NameStr(statisticsForm->stxname);\n\tchar *ownerName = GetUserNameFromId(statisticsForm->stxowner, false);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tappendStringInfo(&str, \"ALTER STATISTICS %s OWNER TO %s\",\n\t\t\t\t\t NameListToQuotedString(list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t\t\t\t\t   makeString(statName))),\n\t\t\t\t\t quote_identifier(ownerName));\n\n\treturn str.data;\n}\n\n\n/*\n * CreateAlterCommandIfTargetNotDefault returns an ALTER STATISTICS .. SET STATISTICS\n * command if the stats object with given id has a target different than the default one.\n * Returns NULL otherwise.\n */\nstatic char *\nCreateAlterCommandIfTargetNotDefault(Oid statsOid)\n{\n\tHeapTuple tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));\n\n\tif (!HeapTupleIsValid(tup))\n\t{\n\t\tereport(WARNING, (errmsg(\"No stats object found with id: %u\", statsOid)));\n\t\treturn NULL;\n\t}\n\n\tForm_pg_statistic_ext statisticsForm = (Form_pg_statistic_ext) GETSTRUCT(tup);\n\tint16 currentStxstattarget = getStxstattarget_compat(tup);\n\tReleaseSysCache(tup);\n\n\tif (currentStxstattarget == -1)\n\t{\n\t\treturn NULL;\n\t}\n\n\tAlterStatsStmt *alterStatsStmt = makeNode(AlterStatsStmt);\n\n\tchar *schemaName = get_namespace_name(statisticsForm->stxnamespace);\n\tchar *statName = NameStr(statisticsForm->stxname);\n\n\talterStatsStmt->stxstattarget = getAlterStatsStxstattarget_compat(\n\t\tcurrentStxstattarget);\n\talterStatsStmt->defnames = list_make2(makeString(schemaName), makeString(statName));\n\n\treturn DeparseAlterStatisticsStmt((Node *) alterStatsStmt);\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/subscription.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * subscription.c\n *    Commands for creating subscriptions\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <string.h>\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"safe_lib.h\"\n\n#include \"commands/defrem.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/builtins.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/version_compat.h\"\n\n\nstatic char * GenerateConninfoWithAuth(char *conninfo);\n\n/*\n * ProcessCreateSubscriptionStmt looks for a special citus_use_authinfo option.\n * If it is set to true, then we'll expand the node's authinfo into the create\n * statement (see GenerateConninfoWithAuth).\n */\nNode *\nProcessCreateSubscriptionStmt(CreateSubscriptionStmt *createSubStmt)\n{\n\tListCell *currCell = NULL;\n\tbool useAuthinfo = false;\n\n\tforeach(currCell, createSubStmt->options)\n\t{\n\t\tDefElem *defElem = (DefElem *) lfirst(currCell);\n\n\t\tif (strcmp(defElem->defname, \"citus_use_authinfo\") == 0)\n\t\t{\n\t\t\tuseAuthinfo = defGetBoolean(defElem);\n\n\t\t\tcreateSubStmt->options = list_delete_cell(createSubStmt->options,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  currCell);\n\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (useAuthinfo)\n\t{\n\t\tcreateSubStmt->conninfo = GenerateConninfoWithAuth(createSubStmt->conninfo);\n\t}\n\n\treturn (Node *) createSubStmt;\n}\n\n\n/*\n * GenerateConninfoWithAuth extracts the host and port from the provided libpq\n * conninfo string, using them to find an appropriate authinfo for the target\n * host. If such an authinfo is found, it is added to the (repalloc'd) string,\n * which is then returned.\n */\nstatic char *\nGenerateConninfoWithAuth(char *conninfo)\n{\n\tStringInfo connInfoWithAuth = makeStringInfo();\n\tchar *host = NULL, *user = NULL;\n\tint32 port = -1;\n\tPQconninfoOption *option = NULL, *optionArray = NULL;\n\n\toptionArray = PQconninfoParse(conninfo, NULL);\n\tif (optionArray == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t\terrmsg(\"not a valid libpq connection info string: %s\",\n\t\t\t\t\t\t\t   conninfo)));\n\t}\n\n\tfor (option = optionArray; option->keyword != NULL; option++)\n\t{\n\t\tif (option->val == NULL || option->val[0] == '\\0')\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (strcmp(option->keyword, \"host\") == 0)\n\t\t{\n\t\t\thost = option->val;\n\t\t}\n\t\telse if (strcmp(option->keyword, \"port\") == 0)\n\t\t{\n\t\t\tport = pg_strtoint32(option->val);\n\t\t}\n\t\telse if (strcmp(option->keyword, \"user\") == 0)\n\t\t{\n\t\t\tuser = option->val;\n\t\t}\n\t}\n\n\t/*\n\t * In case of repetition of parameters in connection strings, last value\n\t * wins. So first add the provided connection string, then global\n\t * connection parameters, then node specific ones.\n\t *\n\t * Note that currently lists of parameters in pg_dist_authnode and\n\t * citus.node_conninfo do not overlap.\n\t *\n\t * The only overlapping parameter between these three lists is\n\t * connect_timeout, which is assigned in conninfo (generated\n\t * by CreateShardMoveSubscription) and is also allowed in\n\t * citus.node_conninfo. Prioritizing the value in citus.node_conninfo\n\t * over conninfo gives user the power to control this value.\n\t */\n\tappendStringInfo(connInfoWithAuth, \"%s %s\", conninfo, NodeConninfo);\n\tif (host != NULL && port > 0 && user != NULL)\n\t{\n\t\tchar *nodeAuthInfo = GetAuthinfo(host, port, user);\n\t\tappendStringInfo(connInfoWithAuth, \" %s\", nodeAuthInfo);\n\t}\n\n\tPQconninfoFree(optionArray);\n\n\treturn connInfoWithAuth->data;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/table.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * table.c\n *    Commands for creating and altering distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/index.h\"\n#include \"catalog/pg_attrdef.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/tablecmds.h\"\n#include \"foreign/foreign.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"parser/parse_expr.h\"\n#include \"parser/parse_type.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/distribution_column.h\"\n#include \"distributed/foreign_key_relationship.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/tenant_schema_metadata.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_shard_visibility.h\"\n\n\n/* controlled via GUC, should be accessed via GetEnableLocalReferenceForeignKeys() */\nbool EnableLocalReferenceForeignKeys = true;\n\n/*\n * GUC that controls whether to allow unique/exclude constraints without\n * distribution column.\n */\nbool AllowUnsafeConstraints = false;\n\n/* Local functions forward declarations for unsupported command checks */\nstatic void PostprocessCreateTableStmtForeignKeys(CreateStmt *createStatement);\nstatic void PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement,\n\t\t\t\t\t\t\t\t\t\t\t\t  const char *queryString);\nstatic void PreprocessAttachPartitionToCitusTable(Oid parentRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t  Oid partitionRelationId);\nstatic void PreprocessAttachCitusPartitionToCitusTable(Oid parentCitusRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   Oid partitionRelationId);\nstatic void DistributePartitionUsingParent(Oid parentRelationId,\n\t\t\t\t\t\t\t\t\t\t   Oid partitionRelationId);\nstatic void ErrorIfMultiLevelPartitioning(Oid parentRelationId, Oid partitionRelationId);\nstatic void ErrorIfAttachCitusTableToPgLocalTable(Oid parentRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t  Oid partitionRelationId);\nstatic bool DeparserSupportsAlterTableAddColumn(AlterTableStmt *alterTableStatement,\n\t\t\t\t\t\t\t\t\t\t\t\tAlterTableCmd *addColumnSubCommand);\nstatic bool ATDefinesFKeyBetweenPostgresAndCitusLocalOrRef(AlterTableStmt *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   alterTableStatement);\nstatic bool ShouldMarkConnectedRelationsNotAutoConverted(Oid leftRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t Oid rightRelationId);\nstatic bool RelationIdListContainsCitusTableType(List *relationIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t CitusTableType citusTableType);\nstatic bool RelationIdListContainsPostgresTable(List *relationIdList);\nstatic void ConvertPostgresLocalTablesToCitusLocalTables(AlterTableStmt *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t alterTableStatement);\nstatic bool RangeVarListHasLocalRelationConvertedByUser(List *relationRangeVarList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tAlterTableStmt *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\talterTableStatement);\nstatic int CompareRangeVarsByOid(const void *leftElement, const void *rightElement);\nstatic List * GetAlterTableAddFKeyRightRelationIdList(AlterTableStmt *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  alterTableStatement);\nstatic List * GetAlterTableAddFKeyRightRelationRangeVarList(AlterTableStmt *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\talterTableStatement);\nstatic List * GetAlterTableAddFKeyConstraintList(AlterTableStmt *alterTableStatement);\nstatic List * GetAlterTableCommandFKeyConstraintList(AlterTableCmd *command);\nstatic List * GetRangeVarListFromFKeyConstraintList(List *fKeyConstraintList);\nstatic List * GetRelationIdListFromRangeVarList(List *rangeVarList, LOCKMODE lockmode,\n\t\t\t\t\t\t\t\t\t\t\t\tbool missingOk);\nstatic bool AlterTableCommandTypeIsTrigger(AlterTableType alterTableType);\nstatic bool AlterTableDropsForeignKey(AlterTableStmt *alterTableStatement);\nstatic void ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement);\nstatic bool AlterInvolvesPartitionColumn(AlterTableStmt *alterTableStatement,\n\t\t\t\t\t\t\t\t\t\t AlterTableCmd *command);\nstatic bool AlterColumnInvolvesIdentityColumn(AlterTableStmt *alterTableStatement,\n\t\t\t\t\t\t\t\t\t\t\t  AlterTableCmd *command);\nstatic void ErrorIfUnsupportedAlterAddConstraintStmt(AlterTableStmt *alterTableStatement);\nstatic List * CreateRightShardListForInterShardDDLTask(Oid rightRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   Oid leftRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   List *leftShardList);\nstatic void SetInterShardDDLTaskPlacementList(Task *task,\n\t\t\t\t\t\t\t\t\t\t\t  ShardInterval *leftShardInterval,\n\t\t\t\t\t\t\t\t\t\t\t  ShardInterval *rightShardInterval);\nstatic void SetInterShardDDLTaskRelationShardList(Task *task,\n\t\t\t\t\t\t\t\t\t\t\t\t  ShardInterval *leftShardInterval,\n\t\t\t\t\t\t\t\t\t\t\t\t  ShardInterval *rightShardInterval);\nstatic Oid get_attrdef_oid(Oid relationId, AttrNumber attnum);\n\nstatic char * GetAddColumnWithNextvalDefaultCmd(Oid sequenceOid, Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\tchar *colname, TypeName *typeName,\n\t\t\t\t\t\t\t\t\t\t\t\tbool ifNotExists);\nstatic void ErrorIfAlterTableDropTableNameFromPostgresFdw(List *optionList, Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  relationId);\n\n\n/*\n * We need to run some of the commands sequentially if there is a foreign constraint\n * from/to reference table.\n */\nstatic bool SetupExecutionModeForAlterTable(Oid relationId, AlterTableCmd *command);\n\n/*\n * PreprocessDropTableStmt processes DROP TABLE commands for partitioned tables.\n * If we are trying to DROP partitioned tables, we first need to go to MX nodes\n * and DETACH partitions from their parents. Otherwise, we process DROP command\n * multiple times in MX workers. For shards, we send DROP commands with IF EXISTS\n * parameter which solves problem of processing same command multiple times.\n * However, for distributed table itself, we directly remove related table from\n * Postgres catalogs via performDeletion function, thus we need to be cautious\n * about not processing same DROP command twice.\n */\nList *\nPreprocessDropTableStmt(Node *node, const char *queryString,\n\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tDropStmt *dropTableStatement = castNode(DropStmt, node);\n\n\tAssert(dropTableStatement->removeType == OBJECT_TABLE);\n\n\tList *tableNameList = NULL;\n\tforeach_declared_ptr(tableNameList, dropTableStatement->objects)\n\t{\n\t\tRangeVar *tableRangeVar = makeRangeVarFromNameList(tableNameList);\n\t\tbool missingOK = true;\n\n\t\tOid relationId = RangeVarGetRelid(tableRangeVar, AccessShareLock, missingOK);\n\n\t\tErrorIfIllegallyChangingKnownShard(relationId);\n\n\t\t/* we're not interested in non-valid, non-distributed relations */\n\t\tif (relationId == InvalidOid || !IsCitusTable(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * While changing the tables that are part of a colocation group we need to\n\t\t * prevent concurrent mutations to the placements of the shard groups.\n\t\t */\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\t\tif (cacheEntry->colocationId != INVALID_COLOCATION_ID)\n\t\t{\n\t\t\tLockColocationId(cacheEntry->colocationId, ShareLock);\n\t\t}\n\n\t\t/* invalidate foreign key cache if the table involved in any foreign key */\n\t\tif ((TableReferenced(relationId) || TableReferencing(relationId)))\n\t\t{\n\t\t\tMarkInvalidateForeignKeyGraph();\n\t\t}\n\n\t\t/* we're only interested in partitioned and mx tables */\n\t\tif (!ShouldSyncTableMetadata(relationId) || !PartitionedTable(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tEnsureCoordinator();\n\n\t\tList *partitionList = PartitionList(relationId);\n\t\tif (list_length(partitionList) == 0)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tSendCommandToWorkersWithMetadata(DISABLE_DDL_PROPAGATION);\n\n\t\tOid partitionRelationId = InvalidOid;\n\t\tforeach_declared_oid(partitionRelationId, partitionList)\n\t\t{\n\t\t\tchar *detachPartitionCommand =\n\t\t\t\tGenerateDetachPartitionCommand(partitionRelationId);\n\n\t\t\tSendCommandToWorkersWithMetadata(detachPartitionCommand);\n\t\t}\n\n\t\tSendCommandToWorkersWithMetadata(ENABLE_DDL_PROPAGATION);\n\t}\n\n\treturn NIL;\n}\n\n\n/*\n * PostprocessCreateTableStmt takes CreateStmt object as a parameter and\n * processes foreign keys on relation via PostprocessCreateTableStmtForeignKeys\n * function.\n *\n * This function also processes CREATE TABLE ... PARTITION OF statements via\n * PostprocessCreateTableStmtPartitionOf function.\n *\n * Also CREATE TABLE ... INHERITS ... commands are filtered here. If the inherited\n * table is a distributed table, this function errors out, as we currently don't\n * support local tables inheriting a distributed table.\n */\nvoid\nPostprocessCreateTableStmt(CreateStmt *createStatement, const char *queryString)\n{\n\tPostprocessCreateTableStmtForeignKeys(createStatement);\n\n\tbool missingOk = false;\n\tOid relationId = RangeVarGetRelid(createStatement->relation, NoLock, missingOk);\n\tOid schemaId = get_rel_namespace(relationId);\n\tif (createStatement->ofTypename && IsTenantSchema(schemaId))\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t errmsg(\"cannot create tables in a distributed schema using \"\n\t\t\t\t\t\t\"CREATE TABLE OF syntax\")));\n\t}\n\n\tif (createStatement->inhRelations != NIL)\n\t{\n\t\tif (createStatement->partbound != NULL)\n\t\t{\n\t\t\t/* process CREATE TABLE ... PARTITION OF command */\n\t\t\tPostprocessCreateTableStmtPartitionOf(createStatement, queryString);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* process CREATE TABLE ... INHERITS ... */\n\n\t\t\tif (IsTenantSchema(schemaId))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"tables in a distributed schema cannot inherit \"\n\t\t\t\t\t\t\t\t\t   \"or be inherited\")));\n\t\t\t}\n\n\t\t\tRangeVar *parentRelation = NULL;\n\t\t\tforeach_declared_ptr(parentRelation, createStatement->inhRelations)\n\t\t\t{\n\t\t\t\tOid parentRelationId = RangeVarGetRelid(parentRelation, NoLock,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tmissingOk);\n\t\t\t\tAssert(parentRelationId != InvalidOid);\n\n\t\t\t\t/*\n\t\t\t\t * Throw a better error message if the user tries to inherit a\n\t\t\t\t * tenant table or if the user tries to inherit from a tenant\n\t\t\t\t * table.\n\t\t\t\t */\n\t\t\t\tif (IsTenantSchema(get_rel_namespace(parentRelationId)))\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errmsg(\"tables in a distributed schema cannot \"\n\t\t\t\t\t\t\t\t\t\t   \"inherit or be inherited\")));\n\t\t\t\t}\n\t\t\t\telse if (IsCitusTable(parentRelationId))\n\t\t\t\t{\n\t\t\t\t\t/* here we error out if inheriting a distributed table */\n\t\t\t\t\tereport(ERROR, (errmsg(\"non-distributed tables cannot inherit \"\n\t\t\t\t\t\t\t\t\t\t   \"distributed tables\")));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * PostprocessCreateTableStmtForeignKeys drops ands re-defines foreign keys\n * defined by given CREATE TABLE command if command defined any foreign to\n * reference or citus local tables.\n */\nstatic void\nPostprocessCreateTableStmtForeignKeys(CreateStmt *createStatement)\n{\n\tif (!ShouldEnableLocalReferenceForeignKeys())\n\t{\n\t\t/*\n\t\t * Either the user disabled foreign keys from/to local/reference tables\n\t\t * or the coordinator is not in the metadata */\n\t\treturn;\n\t}\n\n\t/*\n\t * Relation must exist and it is already locked as standard process utility\n\t * is already executed.\n\t */\n\tbool missingOk = false;\n\tOid relationId = RangeVarGetRelid(createStatement->relation, NoLock, missingOk);\n\n\tif (ShouldCreateTenantSchemaTable(relationId))\n\t{\n\t\t/*\n\t\t * Avoid unnecessarily adding the table into metadata if we will\n\t\t * distribute it as a tenant table later.\n\t\t */\n\t\treturn;\n\t}\n\n\t/*\n\t * As we are just creating the table, we cannot have foreign keys that our\n\t * relation is referenced. So we use INCLUDE_REFERENCING_CONSTRAINTS here.\n\t * Reason behind using other two flags is explained below.\n\t */\n\tint nonDistTableFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS |\n\t\t\t\t\t\t\t\tINCLUDE_CITUS_LOCAL_TABLES |\n\t\t\t\t\t\t\t\tINCLUDE_REFERENCE_TABLES;\n\tList *nonDistTableForeignKeyIdList =\n\t\tGetForeignKeyOids(relationId, nonDistTableFKeysFlag);\n\tbool hasForeignKeyToNonDistTable = list_length(nonDistTableForeignKeyIdList) != 0;\n\tif (hasForeignKeyToNonDistTable)\n\t{\n\t\t/*\n\t\t * To support foreign keys from postgres tables to reference or citus\n\t\t * local tables, we drop and re-define foreign keys so that our ALTER\n\t\t * TABLE hook does the necessary job.\n\t\t */\n\t\tList *relationFKeyCreationCommands =\n\t\t\tGetForeignConstraintCommandsInternal(relationId, nonDistTableFKeysFlag);\n\t\tDropRelationForeignKeys(relationId, nonDistTableFKeysFlag);\n\n\t\tbool skip_validation = true;\n\t\tExecuteForeignKeyCreateCommandList(relationFKeyCreationCommands,\n\t\t\t\t\t\t\t\t\t\t   skip_validation);\n\t}\n}\n\n\n/*\n * ShouldEnableLocalReferenceForeignKeys is a wrapper around getting the GUC\n * EnableLocalReferenceForeignKeys. If the coordinator is not added\n * to the metadata, the function returns false. Else, the function returns\n * the value set by the user\n *\n */\nbool\nShouldEnableLocalReferenceForeignKeys(void)\n{\n\tif (!EnableLocalReferenceForeignKeys)\n\t{\n\t\treturn false;\n\t}\n\n\treturn CoordinatorAddedAsWorkerNode();\n}\n\n\n/*\n * PostprocessCreateTableStmtPartitionOf processes CREATE TABLE ... PARTITION OF\n * statements and it checks if user creates the table as a partition of a distributed\n * table. In that case, it distributes partition as well. Since the table itself is a\n * partition, CreateDistributedTable will attach it to its parent table automatically\n * after distributing it.\n */\nstatic void\nPostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement, const\n\t\t\t\t\t\t\t\t\t  char *queryString)\n{\n\tRangeVar *parentRelation = linitial(createStatement->inhRelations);\n\tbool missingOk = false;\n\tOid parentRelationId = RangeVarGetRelid(parentRelation, NoLock, missingOk);\n\n\t/* a partition can only inherit from single parent table */\n\tAssert(list_length(createStatement->inhRelations) == 1);\n\n\tAssert(parentRelationId != InvalidOid);\n\n\tOid relationId = RangeVarGetRelid(createStatement->relation, NoLock, missingOk);\n\n\t/*\n\t * In case of an IF NOT EXISTS statement, Postgres lets it pass through the\n\t * standardProcess_Utility, and gets into this Post-process hook by\n\t * ignoring the statement if the table already exists. Thus, we need to make\n\t * sure Citus behaves like plain PG in case the relation already exists.\n\t */\n\tif (createStatement->if_not_exists)\n\t{\n\t\tif (IsCitusTable(relationId))\n\t\t{\n\t\t\t/*\n\t\t\t * Ignore if the relation is already distributed.\n\t\t\t */\n\t\t\treturn;\n\t\t}\n\t\telse if (!PartitionTable(relationId) ||\n\t\t\t\t PartitionParentOid(relationId) != parentRelationId)\n\t\t{\n\t\t\t/*\n\t\t\t * Ignore if the relation is not a partition, or if that\n\t\t\t * partition's parent is not the current parent from parentRelationId\n\t\t\t */\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (IsTenantSchema(get_rel_namespace(parentRelationId)) ||\n\t\tIsTenantSchema(get_rel_namespace(relationId)))\n\t{\n\t\tErrorIfIllegalPartitioningInTenantSchema(parentRelationId, relationId);\n\t}\n\n\t/*\n\t * If a partition is being created and if its parent is a distributed\n\t * table, we will distribute this table as well.\n\t */\n\tif (IsCitusTable(parentRelationId))\n\t{\n\t\t/*\n\t\t * We can create Citus local tables right away, without switching to\n\t\t * sequential mode, because they are going to have only one shard.\n\t\t */\n\t\tif (IsCitusTableType(parentRelationId, CITUS_LOCAL_TABLE))\n\t\t{\n\t\t\tCreateCitusLocalTablePartitionOf(createStatement, relationId,\n\t\t\t\t\t\t\t\t\t\t\t parentRelationId);\n\t\t\treturn;\n\t\t}\n\n\t\tDistributePartitionUsingParent(parentRelationId, relationId);\n\t}\n}\n\n\n/*\n * PreprocessAlterTableStmtAttachPartition takes AlterTableStmt object as\n * parameter but it only processes into ALTER TABLE ... ATTACH PARTITION\n * commands and distributes the partition if necessary. There are four cases\n * to consider;\n *\n * Parent is not distributed, partition is not distributed: We do not need to\n * do anything in this case.\n *\n * Parent is not distributed, partition is distributed: This can happen if\n * user first distributes a table and tries to attach it to a non-distributed\n * table. Non-distributed tables cannot have distributed partitions, thus we\n * simply error out in this case.\n *\n * Parent is distributed, partition is not distributed: We should distribute\n * the table and attach it to its parent in workers. CreateDistributedTable\n * perform both of these operations. Thus, we will not propagate ALTER TABLE\n * ... ATTACH PARTITION command to workers.\n *\n * Parent is distributed, partition is distributed: Partition is already\n * distributed, we only need to attach it to its parent in workers. Attaching\n * operation will be performed via propagating this ALTER TABLE ... ATTACH\n * PARTITION command to workers.\n *\n * This function does nothing if the provided CreateStmt is not an ALTER TABLE ...\n * ATTACH PARTITION OF command.\n */\nList *\nPreprocessAlterTableStmtAttachPartition(AlterTableStmt *alterTableStatement,\n\t\t\t\t\t\t\t\t\t\tconst char *queryString)\n{\n\tList *commandList = alterTableStatement->cmds;\n\tAlterTableCmd *alterTableCommand = NULL;\n\tforeach_declared_ptr(alterTableCommand, commandList)\n\t{\n\t\tif (alterTableCommand->subtype == AT_AttachPartition)\n\t\t{\n\t\t\t/*\n\t\t\t * We acquire the lock on the parent and child as we are in the pre-process\n\t\t\t * and want to ensure we acquire the locks in the same order with Postgres\n\t\t\t */\n\t\t\tLOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);\n\t\t\tOid parentRelationId = AlterTableLookupRelation(alterTableStatement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlockmode);\n\t\t\tPartitionCmd *partitionCommand = (PartitionCmd *) alterTableCommand->def;\n\t\t\tbool partitionMissingOk = true;\n\t\t\tOid partitionRelationId = RangeVarGetRelid(partitionCommand->name, lockmode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   partitionMissingOk);\n\t\t\tif (!OidIsValid(partitionRelationId))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We can stop propagation here instead of logging error. Pg will complain\n\t\t\t\t * in standard_utility, so we are safe to stop here. We pass missing_ok\n\t\t\t\t * as true to not diverge from pg output.\n\t\t\t\t */\n\t\t\t\treturn NIL;\n\t\t\t}\n\n\t\t\tif (IsTenantSchema(get_rel_namespace(parentRelationId)) ||\n\t\t\t\tIsTenantSchema(get_rel_namespace(partitionRelationId)))\n\t\t\t{\n\t\t\t\tErrorIfIllegalPartitioningInTenantSchema(parentRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t partitionRelationId);\n\t\t\t}\n\n\t\t\tif (!IsCitusTable(parentRelationId))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * If the parent is a regular Postgres table, but the partition is a\n\t\t\t\t * Citus table, we error out.\n\t\t\t\t */\n\t\t\t\tErrorIfAttachCitusTableToPgLocalTable(parentRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionRelationId);\n\n\t\t\t\t/*\n\t\t\t\t * If both the parent and the child table are Postgres tables,\n\t\t\t\t * we can just skip preprocessing this command.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* Citus doesn't support multi-level partitioned tables */\n\t\t\tErrorIfMultiLevelPartitioning(parentRelationId, partitionRelationId);\n\n\t\t\t/* attaching to a Citus table */\n\t\t\tPreprocessAttachPartitionToCitusTable(parentRelationId, partitionRelationId);\n\t\t}\n\t}\n\n\treturn NIL;\n}\n\n\n/*\n * PreprocessAttachPartitionToCitusTable takes a parent relation, which is a Citus table,\n * and a partition to be attached to it.\n * If the partition table is a regular Postgres table:\n * - Converts the partition to Citus Local Table, if the parent is a Citus Local Table.\n * - Distributes the partition, if the parent is a distributed table.\n * If not, calls PreprocessAttachCitusPartitionToCitusTable to attach given partition to\n * the parent relation.\n */\nstatic void\nPreprocessAttachPartitionToCitusTable(Oid parentRelationId, Oid partitionRelationId)\n{\n\tAssert(IsCitusTable(parentRelationId));\n\n\t/* reference tables cannot be partitioned */\n\tAssert(!IsCitusTableType(parentRelationId, REFERENCE_TABLE));\n\n\t/* if parent of this table is distributed, distribute this table too */\n\tif (!IsCitusTable(partitionRelationId))\n\t{\n\t\tif (IsCitusTableType(parentRelationId, CITUS_LOCAL_TABLE))\n\t\t{\n\t\t\t/*\n\t\t\t * We pass the cascade option as false, since Citus Local Table partitions\n\t\t\t * cannot have non-inherited foreign keys.\n\t\t\t */\n\t\t\tbool cascadeViaForeignKeys = false;\n\t\t\tCitusTableCacheEntry *entry = GetCitusTableCacheEntry(parentRelationId);\n\t\t\tbool autoConverted = entry->autoConverted;\n\t\t\tCreateCitusLocalTable(partitionRelationId, cascadeViaForeignKeys,\n\t\t\t\t\t\t\t\t  autoConverted);\n\t\t}\n\t\telse if (IsCitusTableType(parentRelationId, DISTRIBUTED_TABLE))\n\t\t{\n\t\t\tDistributePartitionUsingParent(parentRelationId, partitionRelationId);\n\t\t}\n\t}\n\telse\n\t{\n\t\t/* both the parent and child are Citus tables */\n\t\tPreprocessAttachCitusPartitionToCitusTable(parentRelationId, partitionRelationId);\n\t}\n}\n\n\n/*\n * PreprocessAttachCitusPartitionToCitusTable takes a parent relation, and a partition\n * to be attached to it. Both of them are Citus tables.\n * Errors out if the partition is a reference table.\n * Errors out if the partition is distributed and the parent is a Citus Local Table.\n * Distributes the partition, if it's a Citus Local Table, and the parent is distributed.\n */\nstatic void\nPreprocessAttachCitusPartitionToCitusTable(Oid parentCitusRelationId, Oid\n\t\t\t\t\t\t\t\t\t\t   partitionRelationId)\n{\n\tif (IsCitusTableType(partitionRelationId, REFERENCE_TABLE))\n\t{\n\t\tereport(ERROR, (errmsg(\"partitioned reference tables are not supported\")));\n\t}\n\telse if (IsCitusTableType(partitionRelationId, DISTRIBUTED_TABLE) &&\n\t\t\t IsCitusTableType(parentCitusRelationId, CITUS_LOCAL_TABLE))\n\t{\n\t\tereport(ERROR, (errmsg(\"non-distributed partitioned tables cannot have \"\n\t\t\t\t\t\t\t   \"distributed partitions\")));\n\t}\n\telse if (IsCitusTableType(partitionRelationId, CITUS_LOCAL_TABLE) &&\n\t\t\t IsCitusTableType(parentCitusRelationId, DISTRIBUTED_TABLE))\n\t{\n\t\t/* if the parent is a distributed table, distribute the partition too */\n\t\tDistributePartitionUsingParent(parentCitusRelationId, partitionRelationId);\n\t}\n\telse if (IsCitusTableType(partitionRelationId, CITUS_LOCAL_TABLE) &&\n\t\t\t IsCitusTableType(parentCitusRelationId, CITUS_LOCAL_TABLE))\n\t{\n\t\t/*\n\t\t * We should ensure that the partition relation has no foreign keys,\n\t\t * as Citus Local Table partitions can only have inherited foreign keys.\n\t\t */\n\t\tif (TableHasExternalForeignKeys(partitionRelationId))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"partition local tables added to citus metadata \"\n\t\t\t\t\t\t\t\t   \"cannot have non-inherited foreign keys\")));\n\t\t}\n\t}\n\n\t/*\n\t * We don't need to add other cases here, like distributed - distributed and\n\t * citus_local - citus_local, as PreprocessAlterTableStmt and standard process\n\t * utility would do the work to attach partitions to shell and shard relations.\n\t */\n}\n\n\n/*\n * DistributePartitionUsingParent takes a parent and a partition relation and\n * distributes the partition, using the same distribution column as the parent, if the\n * parent has a distribution column. It creates a *hash* distributed table by default, as\n * partitioned tables can only be distributed by hash, unless it's null key distributed.\n *\n * If the parent has no distribution key, we distribute the partition with null key too.\n */\nstatic void\nDistributePartitionUsingParent(Oid parentCitusRelationId, Oid partitionRelationId)\n{\n\tchar *parentRelationName = generate_qualified_relation_name(parentCitusRelationId);\n\n\t/*\n\t * We can create tenant tables and single shard tables right away, without\n\t * switching to sequential mode, because they are going to have only one shard.\n\t */\n\tif (ShouldCreateTenantSchemaTable(partitionRelationId))\n\t{\n\t\tCreateTenantSchemaTable(partitionRelationId);\n\t\treturn;\n\t}\n\telse if (!HasDistributionKey(parentCitusRelationId))\n\t{\n\t\t/*\n\t\t * If the parent is null key distributed, we should distribute the partition\n\t\t * with null distribution key as well.\n\t\t */\n\t\tColocationParam colocationParam = {\n\t\t\t.colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT,\n\t\t\t.colocateWithTableName = parentRelationName,\n\t\t};\n\t\tCreateSingleShardTable(partitionRelationId, colocationParam);\n\t\treturn;\n\t}\n\n\tVar *distributionColumn = DistPartitionKeyOrError(parentCitusRelationId);\n\tchar *distributionColumnName = ColumnToColumnName(parentCitusRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  (Node *) distributionColumn);\n\n\tchar distributionMethod = DISTRIBUTE_BY_HASH;\n\n\tSwitchToSequentialAndLocalExecutionIfPartitionNameTooLong(\n\t\tparentCitusRelationId, partitionRelationId);\n\n\tCreateDistributedTable(partitionRelationId, distributionColumnName,\n\t\t\t\t\t\t   distributionMethod, ShardCount, false,\n\t\t\t\t\t\t   parentRelationName);\n}\n\n\n/*\n * ErrorIfMultiLevelPartitioning takes a parent, and a partition relation to be attached\n * and errors out if the partition is also a partitioned table, which means we are\n * trying to build a multi-level partitioned table.\n */\nstatic void\nErrorIfMultiLevelPartitioning(Oid parentRelationId, Oid partitionRelationId)\n{\n\tif (PartitionedTable(partitionRelationId))\n\t{\n\t\tchar *relationName = get_rel_name(partitionRelationId);\n\t\tchar *parentRelationName = get_rel_name(parentRelationId);\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"Citus doesn't support multi-level \"\n\t\t\t\t\t\t\t   \"partitioned tables\"),\n\t\t\t\t\t\terrdetail(\"Relation \\\"%s\\\" is partitioned table \"\n\t\t\t\t\t\t\t\t  \"itself and it is also partition of \"\n\t\t\t\t\t\t\t\t  \"relation \\\"%s\\\".\",\n\t\t\t\t\t\t\t\t  relationName, parentRelationName)));\n\t}\n}\n\n\n/*\n * ErrorIfAttachCitusTableToPgLocalTable takes a parent, and a partition relation\n * to be attached. Errors out if the partition is a Citus table, and the parent is a\n * regular Postgres table.\n */\nstatic void\nErrorIfAttachCitusTableToPgLocalTable(Oid parentRelationId, Oid partitionRelationId)\n{\n\tif (!IsCitusTable(parentRelationId) &&\n\t\tIsCitusTable(partitionRelationId))\n\t{\n\t\tchar *parentRelationName = get_rel_name(parentRelationId);\n\n\t\tereport(ERROR, (errmsg(\"non-citus partitioned tables cannot have \"\n\t\t\t\t\t\t\t   \"citus table partitions\"),\n\t\t\t\t\t\terrhint(\"Distribute the partitioned table \\\"%s\\\" \"\n\t\t\t\t\t\t\t\t\"instead, or add it to metadata\", parentRelationName)));\n\t}\n}\n\n\n/*\n * PostprocessAlterTableSchemaStmt is executed after the change has been applied\n * locally, we can now use the new dependencies of the table to ensure all its\n * dependencies exist on the workers before we apply the commands remotely.\n */\nList *\nPostprocessAlterTableSchemaStmt(Node *node, const char *queryString)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TABLE || stmt->objectType == OBJECT_FOREIGN_TABLE);\n\n\t/*\n\t * We will let Postgres deal with missing_ok\n\t */\n\tList *tableAddresses = GetObjectAddressListFromParseTree((Node *) stmt, true, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(tableAddresses) == 1);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *tableAddress = linitial(tableAddresses);\n\n\t/*\n\t * Check whether we are dealing with a sequence or view here and route queries\n\t * accordingly to the right processor function.\n\t */\n\tchar relKind = get_rel_relkind(tableAddress->objectId);\n\tif (relKind == RELKIND_SEQUENCE)\n\t{\n\t\tstmt->objectType = OBJECT_SEQUENCE;\n\t\treturn PostprocessAlterSequenceSchemaStmt((Node *) stmt, queryString);\n\t}\n\telse if (relKind == RELKIND_VIEW)\n\t{\n\t\tstmt->objectType = OBJECT_VIEW;\n\t\treturn PostprocessAlterViewSchemaStmt((Node *) stmt, queryString);\n\t}\n\n\tif (!ShouldPropagate() || !IsCitusTable(tableAddress->objectId))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureAllObjectDependenciesExistOnAllNodes(tableAddresses);\n\n\treturn NIL;\n}\n\n\n/*\n * ChooseForeignKeyConstraintNameAddition returns the string of column names to be used when generating a foreign\n * key constraint name. This function is copied from postgres codebase.\n */\nstatic char *\nChooseForeignKeyConstraintNameAddition(List *columnNames)\n{\n\tchar buf[NAMEDATALEN * 2];\n\tint buflen = 0;\n\n\tbuf[0] = '\\0';\n\n\tString *columnNameString = NULL;\n\n\tforeach_declared_ptr(columnNameString, columnNames)\n\t{\n\t\tconst char *name = strVal(columnNameString);\n\n\t\tif (buflen > 0)\n\t\t{\n\t\t\tbuf[buflen++] = '_';                                                                        /* insert _ between names */\n\t\t}\n\n\t\t/*\n\t\t *  At this point we have buflen <= NAMEDATALEN.  name should be less\n\t\t *  than NAMEDATALEN already, but use strlcpy for paranoia.\n\t\t */\n\t\tstrlcpy(buf + buflen, name, NAMEDATALEN);\n\t\tbuflen += strlen(buf + buflen);\n\t\tif (buflen >= NAMEDATALEN)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn pstrdup(buf);\n}\n\n\n/*\n * GenerateConstraintName creates and returns a default name for the constraints Citus supports\n * for default naming. See ConstTypeCitusCanDefaultName function for the supported constraint types.\n */\nstatic char *\nGenerateConstraintName(const char *tableName, Oid namespaceId, Constraint *constraint)\n{\n\tchar *conname = NULL;\n\n\tswitch (constraint->contype)\n\t{\n\t\tcase CONSTR_PRIMARY:\n\t\t{\n\t\t\tconname = ChooseIndexName(tableName, namespaceId,\n\t\t\t\t\t\t\t\t\t  NULL, NULL, true, true);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase CONSTR_UNIQUE:\n\t\t{\n\t\t\tListCell *lc;\n\t\t\tList *indexParams = NIL;\n\n\t\t\tforeach(lc, constraint->keys)\n\t\t\t{\n\t\t\t\tIndexElem *iparam = makeNode(IndexElem);\n\t\t\t\tiparam->name = pstrdup(strVal(lfirst(lc)));\n\t\t\t\tindexParams = lappend(indexParams, iparam);\n\t\t\t}\n\n\t\t\tconname = ChooseIndexName(tableName, namespaceId,\n\t\t\t\t\t\t\t\t\t  ChooseIndexColumnNames(indexParams),\n\t\t\t\t\t\t\t\t\t  NULL, false, true);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase CONSTR_EXCLUSION:\n\t\t{\n\t\t\tListCell *lc;\n\t\t\tList *indexParams = NIL;\n\t\t\tList *excludeOpNames = NIL;\n\n\t\t\tforeach(lc, constraint->exclusions)\n\t\t\t{\n\t\t\t\tList *pair = (List *) lfirst(lc);\n\n\t\t\t\tAssert(list_length(pair) == 2);\n\t\t\t\tIndexElem *elem = linitial_node(IndexElem, pair);\n\t\t\t\tList *opname = lsecond_node(List, pair);\n\n\t\t\t\tindexParams = lappend(indexParams, elem);\n\t\t\t\texcludeOpNames = lappend(excludeOpNames, opname);\n\t\t\t}\n\n\t\t\tconname = ChooseIndexName(tableName, namespaceId,\n\t\t\t\t\t\t\t\t\t  ChooseIndexColumnNames(indexParams),\n\t\t\t\t\t\t\t\t\t  excludeOpNames,\n\t\t\t\t\t\t\t\t\t  false, true);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase CONSTR_CHECK:\n\t\t{\n\t\t\tconname = ChooseConstraintName(tableName, NULL, \"check\", namespaceId, NIL);\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase CONSTR_FOREIGN:\n\t\t{\n\t\t\tconname = ChooseConstraintName(tableName,\n\t\t\t\t\t\t\t\t\t\t   ChooseForeignKeyConstraintNameAddition(\n\t\t\t\t\t\t\t\t\t\t\t   constraint->fk_attrs),\n\t\t\t\t\t\t\t\t\t\t   \"fkey\",\n\t\t\t\t\t\t\t\t\t\t   namespaceId,\n\t\t\t\t\t\t\t\t\t\t   NIL);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\"unsupported constraint type for generating a constraint name: %d\",\n\t\t\t\t\t\t\t\tconstraint->contype)));\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn conname;\n}\n\n\n/*\n * EnsureSequentialModeForAlterTableOperation makes sure that the current transaction is already in\n * sequential mode, or can still safely be put in sequential mode, it errors if that is\n * not possible. The error contains information for the user to retry the transaction with\n * sequential mode set from the beginning.\n */\nstatic void\nEnsureSequentialModeForAlterTableOperation(void)\n{\n\tconst char *objTypeString = \"ALTER TABLE ... ADD FOREIGN KEY\";\n\n\tif (ParallelQueryExecutedInTransaction())\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot run %s command because there was a \"\n\t\t\t\t\t\t\t   \"parallel operation on a distributed table in the \"\n\t\t\t\t\t\t\t   \"transaction\", objTypeString),\n\t\t\t\t\t\terrdetail(\"When running command on/for a distributed %s, Citus \"\n\t\t\t\t\t\t\t\t  \"needs to perform all operations over a single \"\n\t\t\t\t\t\t\t\t  \"connection per node to ensure consistency.\",\n\t\t\t\t\t\t\t\t  objTypeString),\n\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t}\n\n\tereport(DEBUG1, (errmsg(\"switching to sequential query execution mode\"),\n\t\t\t\t\t errdetail(\n\t\t\t\t\t\t \"A command for a distributed %s is run. To make sure subsequent \"\n\t\t\t\t\t\t \"commands see the %s correctly we need to make sure to \"\n\t\t\t\t\t\t \"use only one connection for all future commands\",\n\t\t\t\t\t\t objTypeString, objTypeString)));\n\n\tSetLocalMultiShardModifyModeToSequential();\n}\n\n\n/*\n * SwitchToSequentialAndLocalExecutionIfConstraintNameTooLong generates the longest index constraint name\n * among the shards of the partitions, and if exceeds the limit switches to sequential and\n * local execution to prevent self-deadlocks.\n */\nstatic void\nSwitchToSequentialAndLocalExecutionIfConstraintNameTooLong(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   Constraint *constraint)\n{\n\tif (!PartitionedTable(relationId))\n\t{\n\t\t/* Citus already handles long names for regular tables */\n\t\treturn;\n\t}\n\n\tif (ShardIntervalCount(relationId) == 0)\n\t{\n\t\t/*\n\t\t * Relation has no shards, so we cannot run into \"long shard index\n\t\t * name\" issue.\n\t\t */\n\t\treturn;\n\t}\n\n\tOid longestNamePartitionId = PartitionWithLongestNameRelationId(relationId);\n\n\tif (!OidIsValid(longestNamePartitionId))\n\t{\n\t\t/* no partitions have been created yet */\n\t\treturn;\n\t}\n\n\tchar *longestPartitionShardName = get_rel_name(longestNamePartitionId);\n\tShardInterval *shardInterval = LoadShardIntervalWithLongestShardName(\n\t\tlongestNamePartitionId);\n\n\tAppendShardIdToName(&longestPartitionShardName, shardInterval->shardId);\n\n\n\tRelation rel = RelationIdGetRelation(longestNamePartitionId);\n\tOid namespaceOid = RelationGetNamespace(rel);\n\tRelationClose(rel);\n\n\tchar *longestConname = GenerateConstraintName(longestPartitionShardName,\n\t\t\t\t\t\t\t\t\t\t\t\t  namespaceOid, constraint);\n\n\tif (longestConname && strnlen(longestConname, NAMEDATALEN) >= NAMEDATALEN - 1)\n\t{\n\t\tif (ParallelQueryExecutedInTransaction())\n\t\t{\n\t\t\t/*\n\t\t\t * If there has already been a parallel query executed, the sequential mode\n\t\t\t * would still use the already opened parallel connections to the workers,\n\t\t\t * thus contradicting our purpose of using sequential mode.\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\"The constraint name (%s) on a shard is too long and could lead \"\n\t\t\t\t\t\t\t\t\"to deadlocks when executed in a transaction \"\n\t\t\t\t\t\t\t\t\"block after a parallel query\", longestConname),\n\t\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t\t}\n\t\telse\n\t\t{\n\t\t\telog(DEBUG1, \"the constraint name on the shards of the partition \"\n\t\t\t\t\t\t \"is too long, switching to sequential and local execution \"\n\t\t\t\t\t\t \"mode to prevent self deadlocks: %s\", longestConname);\n\n\t\t\tSetLocalMultiShardModifyModeToSequential();\n\t\t\tSetLocalExecutionStatus(LOCAL_EXECUTION_REQUIRED);\n\t\t}\n\t}\n}\n\n\n/*\n * PreprocessAlterTableAddConstraint creates a new constraint name for {PRIMARY KEY, UNIQUE, EXCLUDE, CHECK, FOREIGN KEY}\n * and changes the original alterTableCommand run by the standard utility hook to use the new constraint name.\n * Then it converts the ALTER TABLE ... ADD {PRIMARY KEY, UNIQUE, EXCLUDE, CHECK, FOREIGN KEY} ... command\n * into ALTER TABLE ... ADD CONSTRAINT <constraint name> {PRIMARY KEY, UNIQUE, EXCLUDE, CHECK, FOREIGN KEY} format and returns the DDLJob\n * to run this command in the workers.\n */\nstatic List *\nPreprocessAlterTableAddConstraint(AlterTableStmt *alterTableStatement, Oid\n\t\t\t\t\t\t\t\t  relationId,\n\t\t\t\t\t\t\t\t  Constraint *constraint)\n{\n\tPrepareAlterTableStmtForConstraint(alterTableStatement, relationId, constraint);\n\n\tchar *ddlCommand = DeparseTreeNode((Node *) alterTableStatement);\n\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\tddlJob->startNewTransaction = false;\n\tddlJob->metadataSyncCommand = ddlCommand;\n\n\n\tif (constraint->contype == CONSTR_FOREIGN)\n\t{\n\t\tOid rightRelationId = RangeVarGetRelid(constraint->pktable, NoLock,\n\t\t\t\t\t\t\t\t\t\t\t   false);\n\n\t\t/*\n\t\t * If one of the relations involved in the FOREIGN KEY constraint is not a distributed table, citus errors out eventually.\n\t\t * PreprocessAlterTableStmt function returns an empty tasklist in those cases.\n\t\t * leftRelation is checked in PreprocessAlterTableStmt before\n\t\t * calling PreprocessAlterTableAddConstraint. However, we need to handle the rightRelation since PreprocessAlterTableAddConstraint\n\t\t * returns early.\n\t\t */\n\t\tbool referencedIsLocalTable = !IsCitusTable(rightRelationId);\n\t\tif (referencedIsLocalTable)\n\t\t{\n\t\t\tddlJob->taskList = NIL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tddlJob->taskList = InterShardDDLTaskList(relationId, rightRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t ddlCommand);\n\t\t}\n\t}\n\telse\n\t{\n\t\tddlJob->taskList = DDLTaskList(relationId, ddlCommand);\n\t}\n\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * PrepareAlterTableStmtForConstraint assigns a name to the constraint if it\n * does not have one and switches to sequential and local execution if the\n * constraint name is too long.\n */\nvoid\nPrepareAlterTableStmtForConstraint(AlterTableStmt *alterTableStatement,\n\t\t\t\t\t\t\t\t   Oid relationId,\n\t\t\t\t\t\t\t\t   Constraint *constraint)\n{\n\tif (constraint->conname == NULL && constraint->indexname == NULL)\n\t{\n\t\tRelation rel = RelationIdGetRelation(relationId);\n\n\t\t/*\n\t\t * Change the alterTableCommand so that the standard utility\n\t\t * hook runs it with the name we created.\n\t\t */\n\n\t\tconstraint->conname = GenerateConstraintName(RelationGetRelationName(rel),\n\t\t\t\t\t\t\t\t\t\t\t\t\t RelationGetNamespace(rel),\n\t\t\t\t\t\t\t\t\t\t\t\t\t constraint);\n\n\t\tRelationClose(rel);\n\t}\n\n\tSwitchToSequentialAndLocalExecutionIfConstraintNameTooLong(relationId, constraint);\n\n\tif (constraint->contype == CONSTR_FOREIGN)\n\t{\n\t\tOid rightRelationId = RangeVarGetRelid(constraint->pktable, NoLock,\n\t\t\t\t\t\t\t\t\t\t\t   false);\n\n\t\tif (IsCitusTableType(rightRelationId, REFERENCE_TABLE))\n\t\t{\n\t\t\tEnsureSequentialModeForAlterTableOperation();\n\t\t}\n\t}\n}\n\n\n/*\n * PreprocessAlterTableStmt determines whether a given ALTER TABLE statement\n * involves a distributed table. If so (and if the statement does not use\n * unsupported options), it modifies the input statement to ensure proper\n * execution against the master node table and creates a DDLJob to encapsulate\n * information needed during the worker node portion of DDL execution before\n * returning that DDLJob in a List. If no distributed table is involved, this\n * function returns NIL.\n */\nList *\nPreprocessAlterTableStmt(Node *node, const char *alterTableCommand,\n\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tAlterTableStmt *alterTableStatement = castNode(AlterTableStmt, node);\n\n\t/* first check whether a distributed relation is affected */\n\tif (alterTableStatement->relation == NULL)\n\t{\n\t\treturn NIL;\n\t}\n\n\tLOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);\n\tOid leftRelationId = AlterTableLookupRelation(alterTableStatement, lockmode);\n\n\tif (!OidIsValid(leftRelationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * check whether we are dealing with a sequence or view here\n\t */\n\tchar relKind = get_rel_relkind(leftRelationId);\n\tif (relKind == RELKIND_SEQUENCE)\n\t{\n\t\tAlterTableStmt *stmtCopy = copyObject(alterTableStatement);\n\t\tstmtCopy->objtype = OBJECT_SEQUENCE;\n\n\t\t/*\n\t\t * it must be ALTER TABLE .. OWNER TO ..\n\t\t * or ALTER TABLE .. SET LOGGED/UNLOGGED command\n\t\t * since these are the only ALTER commands of a sequence that\n\t\t * pass through an AlterTableStmt\n\t\t */\n\t\treturn PreprocessSequenceAlterTableStmt((Node *) stmtCopy, alterTableCommand,\n\t\t\t\t\t\t\t\t\t\t\t\tprocessUtilityContext);\n\t}\n\telse if (relKind == RELKIND_VIEW)\n\t{\n\t\t/*\n\t\t * it must be ALTER TABLE .. OWNER TO .. command\n\t\t * since this is the only ALTER command of a view that\n\t\t * passes through an AlterTableStmt\n\t\t */\n\t\tAlterTableStmt *stmtCopy = copyObject(alterTableStatement);\n\t\tstmtCopy->objtype = OBJECT_VIEW;\n\t\treturn PreprocessAlterViewStmt((Node *) stmtCopy, alterTableCommand,\n\t\t\t\t\t\t\t\t\t   processUtilityContext);\n\t}\n\n\t/*\n\t * AlterTableStmt applies also to INDEX relations, and we have support for\n\t * SET/SET storage parameters in Citus, so we might have to check for\n\t * another relation here.\n\t *\n\t * ALTER INDEX ATTACH PARTITION also applies to INDEX relation, so we might\n\t * check another relation for that option as well.\n\t */\n\tchar leftRelationKind = get_rel_relkind(leftRelationId);\n\tif (leftRelationKind == RELKIND_INDEX ||\n\t\tleftRelationKind == RELKIND_PARTITIONED_INDEX)\n\t{\n\t\tbool missingOk = false;\n\t\tleftRelationId = IndexGetRelation(leftRelationId, missingOk);\n\t}\n\n\tif (ShouldEnableLocalReferenceForeignKeys() &&\n\t\tprocessUtilityContext != PROCESS_UTILITY_SUBCOMMAND &&\n\t\tATDefinesFKeyBetweenPostgresAndCitusLocalOrRef(alterTableStatement))\n\t{\n\t\t/*\n\t\t * We don't process subcommands generated by postgres.\n\t\t * This is mainly because postgres issues ALTER TABLE commands\n\t\t * for some set of objects that are defined via CREATE TABLE commands.\n\t\t * However, citus already has a separate logic for CREATE TABLE\n\t\t * commands.\n\t\t *\n\t\t * To support foreign keys from/to postgres local tables to/from reference\n\t\t * or citus local tables, we convert given postgres local table -and the\n\t\t * other postgres tables that it is connected via a fkey graph- to a citus\n\t\t * local table.\n\t\t *\n\t\t * Note that we don't convert postgres tables to citus local tables if\n\t\t * coordinator is not added to metadata as CreateCitusLocalTable requires\n\t\t * this. In this case, we assume user is about to create reference or\n\t\t * distributed table from local table and we don't want to break user\n\t\t * experience by asking to add coordinator to metadata.\n\t\t */\n\t\tConvertPostgresLocalTablesToCitusLocalTables(alterTableStatement);\n\n\t\t/*\n\t\t * CreateCitusLocalTable converts relation to a shard relation and creates\n\t\t * shell table from scratch.\n\t\t * For this reason we should re-enter to PreprocessAlterTableStmt to operate\n\t\t * on shell table relation id.\n\t\t */\n\t\treturn PreprocessAlterTableStmt(node, alterTableCommand, processUtilityContext);\n\t}\n\n\tif (AlterTableDropsForeignKey(alterTableStatement))\n\t{\n\t\t/*\n\t\t * The foreign key graph keeps track of the foreign keys including local tables.\n\t\t * So, even if a foreign key on a local table is dropped, we should invalidate\n\t\t * the graph so that the next commands can see the graph up-to-date.\n\t\t * We are aware that utility hook would still invalidate foreign key graph\n\t\t * even when command fails, but currently we are ok with that.\n\t\t */\n\t\tMarkInvalidateForeignKeyGraph();\n\t}\n\n\tbool referencingIsLocalTable = !IsCitusTable(leftRelationId);\n\tif (referencingIsLocalTable)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * The PostgreSQL parser dispatches several commands into the node type\n\t * AlterTableStmt, from ALTER INDEX to ALTER SEQUENCE or ALTER VIEW. Here\n\t * we have a special implementation for ALTER INDEX, and a specific error\n\t * message in case of unsupported sub-command.\n\t */\n\tif (leftRelationKind == RELKIND_INDEX ||\n\t\tleftRelationKind == RELKIND_PARTITIONED_INDEX)\n\t{\n\t\tErrorIfUnsupportedAlterIndexStmt(alterTableStatement);\n\t}\n\telse\n\t{\n\t\t/* this function also accepts more than just RELKIND_RELATION... */\n\t\tErrorIfUnsupportedAlterTableStmt(alterTableStatement);\n\t}\n\n\tEnsureCoordinator();\n\n\t/* these will be set in below loop according to subcommands */\n\tOid rightRelationId = InvalidOid;\n\tbool executeSequentially = false;\n\n\t/*\n\t * We check if there is:\n\t *  - an ADD/DROP FOREIGN CONSTRAINT command in sub commands\n\t *    list. If there is we assign referenced relation id to rightRelationId and\n\t *    we also set skip_validation to true to prevent PostgreSQL to verify validity\n\t *    of the foreign constraint in master. Validity will be checked in workers\n\t *    anyway.\n\t *  - an ADD COLUMN .. that is the only subcommand in the list OR\n\t *  - an ADD COLUMN .. DEFAULT nextval('..') OR\n\t *    an ADD COLUMN .. SERIAL pseudo-type OR\n\t *    an ALTER COLUMN .. SET DEFAULT nextval('..'). If there is we set\n\t *    deparseAT variable to true which means we will deparse the statement\n\t *    before we propagate the command to shards. For shards, all the defaults\n\t *    coming from a user-defined sequence will be replaced by\n\t *    NOT NULL constraint.\n\t */\n\tList *commandList = alterTableStatement->cmds;\n\n\t/*\n\t * if deparsing is needed, we will use a different version of the original\n\t * alterTableStmt\n\t */\n\tbool deparseAT = false;\n\tbool propagateCommandToWorkers = true;\n\n\t/*\n\t * Sometimes we want to run a different DDL Command string in MX workers\n\t * For example, in cases where worker_nextval should be used instead\n\t * of nextval() in column defaults with type int and smallint\n\t */\n\tbool useInitialDDLCommandString = true;\n\n\tAlterTableStmt *newStmt = copyObject(alterTableStatement);\n\n\tAlterTableCmd *newCmd = makeNode(AlterTableCmd);\n\n\tAlterTableCmd *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tAlterTableType alterTableType = command->subtype;\n\n\t\t/*\n\t\t * if deparsing is needed, we will use a different version of the original\n\t\t * AlterTableCmd\n\t\t */\n\t\tnewCmd = copyObject(command);\n\n\t\tif (alterTableType == AT_AddConstraint)\n\t\t{\n\t\t\tConstraint *constraint = (Constraint *) command->def;\n\t\t\tif (constraint->contype == CONSTR_FOREIGN)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We only support ALTER TABLE ADD CONSTRAINT ... FOREIGN KEY, if it is\n\t\t\t\t * only subcommand of ALTER TABLE. It was already checked in\n\t\t\t\t * ErrorIfUnsupportedAlterTableStmt.\n\t\t\t\t */\n\t\t\t\tAssert(list_length(commandList) == 1);\n\n\t\t\t\trightRelationId = RangeVarGetRelid(constraint->pktable, lockmode,\n\t\t\t\t\t\t\t\t\t\t\t\t   alterTableStatement->missing_ok);\n\n\t\t\t\tif (processUtilityContext != PROCESS_UTILITY_SUBCOMMAND &&\n\t\t\t\t\tShouldMarkConnectedRelationsNotAutoConverted(leftRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t rightRelationId))\n\t\t\t\t{\n\t\t\t\t\tList *relationList = list_make2_oid(leftRelationId, rightRelationId);\n\t\t\t\t\tUpdateAutoConvertedForConnectedRelations(relationList, false);\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * Foreign constraint validations will be done in workers. If we do not\n\t\t\t\t * set this flag, PostgreSQL tries to do additional checking when we drop\n\t\t\t\t * to standard_ProcessUtility. standard_ProcessUtility tries to open new\n\t\t\t\t * connections to workers to verify foreign constraints while original\n\t\t\t\t * transaction is in process, which causes deadlock.\n\t\t\t\t */\n\t\t\t\tconstraint->skip_validation = true;\n\n\t\t\t\tif (constraint->conname == NULL)\n\t\t\t\t{\n\t\t\t\t\treturn PreprocessAlterTableAddConstraint(alterTableStatement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t leftRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t constraint);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * When constraint->indexname is not NULL we are handling an\n\t\t\t * ADD {PRIMARY KEY, UNIQUE} USING INDEX command. In this case\n\t\t\t * we do not have to create a name and change the command.\n\t\t\t * The existing index name will be used by the postgres.\n\t\t\t */\n\t\t\telse if (constraint->conname == NULL && constraint->indexname == NULL)\n\t\t\t{\n\t\t\t\tif (ConstrTypeCitusCanDefaultName(constraint->contype))\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Create a constraint name. Convert ALTER TABLE ... ADD PRIMARY ... command into\n\t\t\t\t\t * ALTER TABLE ... ADD CONSTRAINT <conname> PRIMARY KEY ... form and create the ddl jobs\n\t\t\t\t\t * for running this form of the command on the workers.\n\t\t\t\t\t */\n\t\t\t\t\treturn PreprocessAlterTableAddConstraint(alterTableStatement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t leftRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t constraint);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (alterTableType == AT_DropConstraint)\n\t\t{\n\t\t\tchar *constraintName = command->name;\n\t\t\tif (ConstraintIsAForeignKey(constraintName, leftRelationId))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We only support ALTER TABLE DROP CONSTRAINT ... FOREIGN KEY, if it is\n\t\t\t\t * only subcommand of ALTER TABLE. It was already checked in\n\t\t\t\t * ErrorIfUnsupportedAlterTableStmt.\n\t\t\t\t */\n\t\t\t\tAssert(list_length(commandList) == 1);\n\n\t\t\t\tbool missingOk = false;\n\t\t\t\tOid foreignKeyId = get_relation_constraint_oid(leftRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   constraintName, missingOk);\n\t\t\t\trightRelationId = GetReferencedTableId(foreignKeyId);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * We support deparsing for DROP CONSTRAINT, but currently deparsing is only\n\t\t\t * possible if all subcommands are supported.\n\t\t\t */\n\t\t\tif (list_length(commandList) == 1 &&\n\t\t\t\talterTableStatement->objtype == OBJECT_TABLE)\n\t\t\t{\n\t\t\t\tdeparseAT = true;\n\t\t\t}\n\t\t}\n\t\telse if (alterTableType == AT_AddColumn)\n\t\t{\n\t\t\tColumnDef *columnDefinition = (ColumnDef *) command->def;\n\t\t\tList *columnConstraints = columnDefinition->constraints;\n\n\t\t\tConstraint *constraint = NULL;\n\t\t\tforeach_declared_ptr(constraint, columnConstraints)\n\t\t\t{\n\t\t\t\tif (constraint->contype == CONSTR_FOREIGN)\n\t\t\t\t{\n\t\t\t\t\trightRelationId = RangeVarGetRelid(constraint->pktable, lockmode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   alterTableStatement->missing_ok);\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Foreign constraint validations will be done in workers. If we do not\n\t\t\t\t\t * set this flag, PostgreSQL tries to do additional checking when we drop\n\t\t\t\t\t * to standard_ProcessUtility. standard_ProcessUtility tries to open new\n\t\t\t\t\t * connections to workers to verify foreign constraints while original\n\t\t\t\t\t * transaction is in process, which causes deadlock.\n\t\t\t\t\t */\n\t\t\t\t\tconstraint->skip_validation = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (DeparserSupportsAlterTableAddColumn(alterTableStatement, command))\n\t\t\t{\n\t\t\t\tdeparseAT = true;\n\n\t\t\t\tconstraint = NULL;\n\t\t\t\tforeach_declared_ptr(constraint, columnConstraints)\n\t\t\t\t{\n\t\t\t\t\tif (ConstrTypeCitusCanDefaultName(constraint->contype))\n\t\t\t\t\t{\n\t\t\t\t\t\tPrepareAlterTableStmtForConstraint(alterTableStatement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   leftRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   constraint);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * Copy the constraints to the new subcommand because now we\n\t\t\t\t * might have assigned names to some of them.\n\t\t\t\t */\n\t\t\t\tColumnDef *newColumnDef = (ColumnDef *) newCmd->def;\n\t\t\t\tnewColumnDef->constraints = copyObject(columnConstraints);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * We check for ADD COLUMN .. DEFAULT expr\n\t\t\t * if expr contains nextval('user_defined_seq')\n\t\t\t * we should deparse the statement\n\t\t\t */\n\t\t\tconstraint = NULL;\n\t\t\tint constraintIdx = 0;\n\t\t\tforeach_declared_ptr(constraint, columnConstraints)\n\t\t\t{\n\t\t\t\tif (constraint->contype == CONSTR_DEFAULT)\n\t\t\t\t{\n\t\t\t\t\tif (constraint->raw_expr != NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\tParseState *pstate = make_parsestate(NULL);\n\t\t\t\t\t\tNode *expr = transformExpr(pstate, constraint->raw_expr,\n\t\t\t\t\t\t\t\t\t\t\t\t   EXPR_KIND_COLUMN_DEFAULT);\n\n\t\t\t\t\t\tif (contain_nextval_expression_walker(expr, NULL))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tdeparseAT = true;\n\t\t\t\t\t\t\tuseInitialDDLCommandString = false;\n\n\t\t\t\t\t\t\t/* drop the default expression from new subcomand */\n\t\t\t\t\t\t\tColumnDef *newColumnDef = (ColumnDef *) newCmd->def;\n\t\t\t\t\t\t\tnewColumnDef->constraints =\n\t\t\t\t\t\t\t\tlist_delete_nth_cell(newColumnDef->constraints,\n\t\t\t\t\t\t\t\t\t\t\t\t\t constraintIdx);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t/* there can only be one DEFAULT constraint that can be used per column */\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconstraintIdx++;\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t * We check for ADD COLUMN .. SERIAL pseudo-type\n\t\t\t * if that's the case, we should deparse the statement\n\t\t\t * The structure of this check is copied from transformColumnDefinition.\n\t\t\t */\n\t\t\tif (columnDefinition->typeName && list_length(\n\t\t\t\t\tcolumnDefinition->typeName->names) == 1 &&\n\t\t\t\t!columnDefinition->typeName->pct_type)\n\t\t\t{\n\t\t\t\tchar *typeName = strVal(linitial(columnDefinition->typeName->names));\n\n\t\t\t\tif (strcmp(typeName, \"smallserial\") == 0 ||\n\t\t\t\t\tstrcmp(typeName, \"serial2\") == 0 ||\n\t\t\t\t\tstrcmp(typeName, \"serial\") == 0 ||\n\t\t\t\t\tstrcmp(typeName, \"serial4\") == 0 ||\n\t\t\t\t\tstrcmp(typeName, \"bigserial\") == 0 ||\n\t\t\t\t\tstrcmp(typeName, \"serial8\") == 0)\n\t\t\t\t{\n\t\t\t\t\tdeparseAT = true;\n\n\t\t\t\t\tColumnDef *newColDef = copyObject(columnDefinition);\n\t\t\t\t\tnewColDef->is_not_null = false;\n\n\t\t\t\t\tif (strcmp(typeName, \"smallserial\") == 0 ||\n\t\t\t\t\t\tstrcmp(typeName, \"serial2\") == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tnewColDef->typeName->names = NIL;\n\t\t\t\t\t\tnewColDef->typeName->typeOid = INT2OID;\n\t\t\t\t\t}\n\t\t\t\t\telse if (strcmp(typeName, \"serial\") == 0 ||\n\t\t\t\t\t\t\t strcmp(typeName, \"serial4\") == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tnewColDef->typeName->names = NIL;\n\t\t\t\t\t\tnewColDef->typeName->typeOid = INT4OID;\n\t\t\t\t\t}\n\t\t\t\t\telse if (strcmp(typeName, \"bigserial\") == 0 ||\n\t\t\t\t\t\t\t strcmp(typeName, \"serial8\") == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tnewColDef->typeName->names = NIL;\n\t\t\t\t\t\tnewColDef->typeName->typeOid = INT8OID;\n\t\t\t\t\t}\n\t\t\t\t\tnewCmd->def = (Node *) newColDef;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * We check for ALTER COLUMN .. SET/DROP DEFAULT\n\t\t * we should not propagate anything to shards\n\t\t */\n\t\telse if (alterTableType == AT_ColumnDefault)\n\t\t{\n\t\t\tParseState *pstate = make_parsestate(NULL);\n\t\t\tNode *expr = transformExpr(pstate, command->def,\n\t\t\t\t\t\t\t\t\t   EXPR_KIND_COLUMN_DEFAULT);\n\n\t\t\tif (contain_nextval_expression_walker(expr, NULL))\n\t\t\t{\n\t\t\t\tpropagateCommandToWorkers = false;\n\t\t\t\tuseInitialDDLCommandString = false;\n\t\t\t}\n\t\t}\n\t\telse if (alterTableType == AT_AttachPartition)\n\t\t{\n\t\t\tPartitionCmd *partitionCommand = (PartitionCmd *) command->def;\n\t\t\tOid attachedRelationId = RangeVarGetRelid(partitionCommand->name, NoLock,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  false);\n\t\t\tchar attachedRelationKind = get_rel_relkind(attachedRelationId);\n\n\t\t\t/*\n\t\t\t * We support ALTER INDEX ATTACH PARTITION and ALTER TABLE ATTACH PARTITION\n\t\t\t * if it is only subcommand of ALTER TABLE command. Since the attached relation\n\t\t\t * type is index for ALTER INDEX ATTACH PARTITION, we need to use the relation\n\t\t\t * id this index is created for.\n\t\t\t *\n\t\t\t * Both were already checked in ErrorIfUnsupportedAlterIndexStmt and\n\t\t\t * ErrorIfUnsupportedAlterTableStmt.\n\t\t\t */\n\t\t\tif (attachedRelationKind == RELKIND_INDEX)\n\t\t\t{\n\t\t\t\tbool missingOk = false;\n\t\t\t\trightRelationId = IndexGetRelation(attachedRelationId, missingOk);\n\n\t\t\t\t/*\n\t\t\t\t * Since left relation is checked above to make sure it is Citus table,\n\t\t\t\t * partition of that must be Citus table as well.\n\t\t\t\t */\n\t\t\t\tAssert(IsCitusTable(rightRelationId));\n\t\t\t}\n\t\t\telse if (attachedRelationKind == RELKIND_RELATION ||\n\t\t\t\t\t attachedRelationKind == RELKIND_FOREIGN_TABLE)\n\t\t\t{\n\t\t\t\tAssert(list_length(commandList) <= 1);\n\n\t\t\t\t/*\n\t\t\t\t * Do not generate tasks if relation is distributed and the partition\n\t\t\t\t * is not distributed. Because, we'll manually convert the partition into\n\t\t\t\t * distributed table and co-locate with its parent.\n\t\t\t\t */\n\t\t\t\tif (!IsCitusTable(attachedRelationId))\n\t\t\t\t{\n\t\t\t\t\treturn NIL;\n\t\t\t\t}\n\n\t\t\t\trightRelationId = attachedRelationId;\n\t\t\t}\n\t\t}\n\t\telse if (alterTableType == AT_DetachPartition)\n\t\t{\n\t\t\tPartitionCmd *partitionCommand = (PartitionCmd *) command->def;\n\n\t\t\t/*\n\t\t\t * We only support ALTER TABLE DETACH PARTITION, if it is only subcommand of\n\t\t\t * ALTER TABLE. It was already checked in ErrorIfUnsupportedAlterTableStmt.\n\t\t\t */\n\t\t\tAssert(list_length(commandList) <= 1);\n\n\t\t\trightRelationId = RangeVarGetRelid(partitionCommand->name, NoLock, false);\n\t\t}\n\t\telse if (AlterTableCommandTypeIsTrigger(alterTableType))\n\t\t{\n\t\t\tchar *triggerName = command->name;\n\t\t\treturn CitusCreateTriggerCommandDDLJob(leftRelationId, triggerName,\n\t\t\t\t\t\t\t\t\t\t\t\t   alterTableCommand);\n\t\t}\n\n\t\t/*\n\t\t * We check and set the execution mode only if we fall into either of first two\n\t\t * conditional blocks, otherwise we already continue the loop\n\t\t */\n\t\texecuteSequentially |= SetupExecutionModeForAlterTable(leftRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   command);\n\t}\n\n\tif (executeSequentially)\n\t{\n\t\tSetLocalMultiShardModifyModeToSequential();\n\t}\n\n\t/* fill them here as it is possible to use them in some conditional blocks below */\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, leftRelationId);\n\n\tif (deparseAT)\n\t{\n\t\tnewStmt->cmds = list_make1(newCmd);\n\t\talterTableCommand = DeparseTreeNode((Node *) newStmt);\n\t}\n\n\tddlJob->metadataSyncCommand = useInitialDDLCommandString ? alterTableCommand : NULL;\n\n\tif (OidIsValid(rightRelationId))\n\t{\n\t\tbool referencedIsLocalTable = !IsCitusTable(rightRelationId);\n\t\tif (referencedIsLocalTable || !propagateCommandToWorkers)\n\t\t{\n\t\t\tddlJob->taskList = NIL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* if foreign key or attaching partition index related, use specialized task list function ... */\n\t\t\tddlJob->taskList = InterShardDDLTaskList(leftRelationId, rightRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t alterTableCommand);\n\t\t}\n\t}\n\telse\n\t{\n\t\t/* ... otherwise use standard DDL task list function */\n\t\tddlJob->taskList = DDLTaskList(leftRelationId, alterTableCommand);\n\t\tif (!propagateCommandToWorkers)\n\t\t{\n\t\t\tddlJob->taskList = NIL;\n\t\t}\n\t}\n\n\tList *ddlJobs = list_make1(ddlJob);\n\n\treturn ddlJobs;\n}\n\n\n/*\n * DeparserSupportsAlterTableAddColumn returns true if it's safe to deparse\n * the given ALTER TABLE statement that is known to contain given ADD COLUMN\n * subcommand.\n */\nstatic bool\nDeparserSupportsAlterTableAddColumn(AlterTableStmt *alterTableStatement,\n\t\t\t\t\t\t\t\t\tAlterTableCmd *addColumnSubCommand)\n{\n\t/*\n\t * We support deparsing for ADD COLUMN only of it's the only\n\t * subcommand.\n\t */\n\tif (list_length(alterTableStatement->cmds) == 1 &&\n\t\talterTableStatement->objtype == OBJECT_TABLE)\n\t{\n\t\tColumnDef *columnDefinition = (ColumnDef *) addColumnSubCommand->def;\n\t\tConstraint *constraint = NULL;\n\t\tforeach_declared_ptr(constraint, columnDefinition->constraints)\n\t\t{\n\t\t\tif (constraint->contype == CONSTR_CHECK)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Given that we're in the preprocess, any reference to the\n\t\t\t\t * column that we're adding would break the deparser. This\n\t\t\t\t * can only be the case with CHECK constraints. For this\n\t\t\t\t * reason, we skip deparsing the command and fall back to\n\t\t\t\t * legacy behavior that we follow for ADD COLUMN subcommands.\n\t\t\t\t *\n\t\t\t\t * For other constraint types, we prepare the constraint to\n\t\t\t\t * make sure that we can deparse it.\n\t\t\t\t */\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ATDefinesFKeyBetweenPostgresAndCitusLocalOrRef returns true if given\n * alter table command defines foreign key between a postgres table and a\n * reference or citus local table.\n */\nstatic bool\nATDefinesFKeyBetweenPostgresAndCitusLocalOrRef(AlterTableStmt *alterTableStatement)\n{\n\tList *foreignKeyConstraintList =\n\t\tGetAlterTableAddFKeyConstraintList(alterTableStatement);\n\tif (list_length(foreignKeyConstraintList) == 0)\n\t{\n\t\t/* we are not defining any foreign keys */\n\t\treturn false;\n\t}\n\n\tList *rightRelationIdList =\n\t\tGetAlterTableAddFKeyRightRelationIdList(alterTableStatement);\n\n\tLOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);\n\tOid leftRelationId = AlterTableLookupRelation(alterTableStatement, lockmode);\n\tif (!IsCitusTable(leftRelationId))\n\t{\n\t\treturn RelationIdListContainsCitusTableType(rightRelationIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tCITUS_LOCAL_TABLE) ||\n\t\t\t   RelationIdListContainsCitusTableType(rightRelationIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tREFERENCE_TABLE);\n\t}\n\telse if (IsCitusTableType(leftRelationId, CITUS_LOCAL_TABLE) ||\n\t\t\t IsCitusTableType(leftRelationId, REFERENCE_TABLE))\n\t{\n\t\treturn RelationIdListContainsPostgresTable(rightRelationIdList);\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ShouldMarkConnectedRelationsNotAutoConverted takes two relations.\n * If both of them are Citus Local Tables, and one of them is auto-converted while the\n * other one is not; then it returns true. False otherwise.\n */\nstatic bool\nShouldMarkConnectedRelationsNotAutoConverted(Oid leftRelationId, Oid rightRelationId)\n{\n\tif (!IsCitusTableType(leftRelationId, CITUS_LOCAL_TABLE))\n\t{\n\t\treturn false;\n\t}\n\n\tif (!IsCitusTableType(rightRelationId, CITUS_LOCAL_TABLE))\n\t{\n\t\treturn false;\n\t}\n\n\tCitusTableCacheEntry *entryLeft = GetCitusTableCacheEntry(leftRelationId);\n\tCitusTableCacheEntry *entryRight = GetCitusTableCacheEntry(rightRelationId);\n\n\treturn entryLeft->autoConverted != entryRight->autoConverted;\n}\n\n\n/*\n * RelationIdListContainsCitusTableType returns true if given relationIdList\n * contains a citus table with given type.\n */\nstatic bool\nRelationIdListContainsCitusTableType(List *relationIdList, CitusTableType citusTableType)\n{\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tif (IsCitusTableType(relationId, citusTableType))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * RelationIdListContainsPostgresTable returns true if given relationIdList\n * contains a postgres table.\n */\nstatic bool\nRelationIdListContainsPostgresTable(List *relationIdList)\n{\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tif (OidIsValid(relationId) && !IsCitusTable(relationId))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ConvertPostgresLocalTablesToCitusLocalTables converts each postgres table\n * involved in foreign keys to be defined by given alter table command and the\n * other tables connected to them via a foreign key graph to citus local tables.\n */\nstatic void\nConvertPostgresLocalTablesToCitusLocalTables(AlterTableStmt *alterTableStatement)\n{\n\tList *rightRelationRangeVarList =\n\t\tGetAlterTableAddFKeyRightRelationRangeVarList(alterTableStatement);\n\tRangeVar *leftRelationRangeVar = alterTableStatement->relation;\n\tList *relationRangeVarList = lappend(rightRelationRangeVarList, leftRelationRangeVar);\n\n\t/*\n\t * To prevent deadlocks, sort the list before converting each postgres local\n\t * table to a citus local table.\n\t */\n\trelationRangeVarList = SortList(relationRangeVarList, CompareRangeVarsByOid);\n\tbool containsAnyUserConvertedLocalRelation =\n\t\tRangeVarListHasLocalRelationConvertedByUser(relationRangeVarList,\n\t\t\t\t\t\t\t\t\t\t\t\t\talterTableStatement);\n\n\t/*\n\t * Here we should operate on RangeVar objects since relations oid's would\n\t * change in below loop due to CreateCitusLocalTable.\n\t */\n\tRangeVar *relationRangeVar;\n\tforeach_declared_ptr(relationRangeVar, relationRangeVarList)\n\t{\n\t\tList *commandList = alterTableStatement->cmds;\n\t\tLOCKMODE lockMode = AlterTableGetLockLevel(commandList);\n\t\tbool missingOk = alterTableStatement->missing_ok;\n\t\tOid relationId = RangeVarGetRelid(relationRangeVar, lockMode, missingOk);\n\t\tif (!OidIsValid(relationId))\n\t\t{\n\t\t\t/*\n\t\t\t * As we are in preprocess, missingOk might be true and relation\n\t\t\t * might not exist.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\t\telse if (IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t\t{\n\t\t\tCitusTableCacheEntry *entry = GetCitusTableCacheEntry(relationId);\n\t\t\tif (!entry->autoConverted)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * This citus local table is already added to the metadata\n\t\t\t\t * by the user, so no further operation needed.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (!containsAnyUserConvertedLocalRelation)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We are safe to skip this relation because none of the citus local\n\t\t\t\t * tables involved are manually added to the metadata by the user.\n\t\t\t\t * This implies that all the Citus local tables involved are marked\n\t\t\t\t * as autoConverted = true and there is no chance to update\n\t\t\t\t * autoConverted = false.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse if (IsCitusTable(relationId))\n\t\t{\n\t\t\t/* we can directly skip for table types other than citus local tables */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * The only reason behind using a try/catch block here is giving a proper\n\t\t * error message. For example, when creating a citus local table we might\n\t\t * give an error telling that partitioned tables are not supported for\n\t\t * citus local table creation. But as a user it wouldn't make much sense\n\t\t * to see such an error. So here we extend error message to tell that we\n\t\t * actually ended up with this error when trying to define the foreign key.\n\t\t *\n\t\t * Also, as CopyErrorData() requires (CurrentMemoryContext != ErrorContext),\n\t\t * so we store CurrentMemoryContext here.\n\t\t */\n\t\tMemoryContext savedMemoryContext = CurrentMemoryContext;\n\t\tPG_TRY();\n\t\t{\n\t\t\tbool cascade = true;\n\n\t\t\t/*\n\t\t\t * Without this check, we would be erroring out in CreateCitusLocalTable\n\t\t\t * for this case anyway. The purpose of this check&error is to provide\n\t\t\t * a more meaningful message for the user.\n\t\t\t */\n\t\t\tif (PartitionTable(relationId))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"cannot build foreign key between\"\n\t\t\t\t\t\t\t\t\t   \" reference table and a partition\"),\n\t\t\t\t\t\t\t\terrhint(\"Try using parent table: %s\",\n\t\t\t\t\t\t\t\t\t\tget_rel_name(PartitionParentOid(relationId)))));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * There might be two scenarios:\n\t\t\t\t *\n\t\t\t\t *   a) A user created foreign key from a reference table\n\t\t\t\t *      to Postgres local table(s) or Citus local table(s)\n\t\t\t\t *      where all of the citus local tables involved are auto\n\t\t\t\t *      converted. In that case, we mark the new table as auto\n\t\t\t\t *      converted as well.\n\t\t\t\t *\n\t\t\t\t *   b) A user created foreign key from a reference table\n\t\t\t\t *      to Postgres local table(s) or Citus local table(s)\n\t\t\t\t *      where at least one of the citus local tables\n\t\t\t\t *      involved is not auto converted. In that case, we mark\n\t\t\t\t *      this new Citus local table as autoConverted = false\n\t\t\t\t *      as well. Because our logic is to keep all the connected\n\t\t\t\t *      Citus local tables to have the same autoConverted value.\n\t\t\t\t */\n\t\t\t\tbool autoConverted = containsAnyUserConvertedLocalRelation ? false : true;\n\t\t\t\tCreateCitusLocalTable(relationId, cascade, autoConverted);\n\t\t\t}\n\t\t}\n\t\tPG_CATCH();\n\t\t{\n\t\t\tMemoryContextSwitchTo(savedMemoryContext);\n\n\t\t\tErrorData *errorData = CopyErrorData();\n\t\t\tFlushErrorState();\n\n\t\t\tif (errorData->elevel != ERROR)\n\t\t\t{\n\t\t\t\tPG_RE_THROW();\n\t\t\t}\n\n\t\t\t/* override error detail */\n\t\t\terrorData->detail = \"When adding a foreign key from a local table to \"\n\t\t\t\t\t\t\t\t\"a reference table, Citus applies a conversion to \"\n\t\t\t\t\t\t\t\t\"all the local tables in the foreign key graph\";\n\t\t\tThrowErrorData(errorData);\n\t\t}\n\t\tPG_END_TRY();\n\t}\n}\n\n\n/*\n * RangeVarListHasLocalRelationConvertedByUser takes a list of relations and returns true\n * if any of these relations is marked as auto-converted = false. Returns true otherwise.\n * This function also takes the current alterTableStatement command, to obtain the\n * necessary locks.\n */\nstatic bool\nRangeVarListHasLocalRelationConvertedByUser(List *relationRangeVarList,\n\t\t\t\t\t\t\t\t\t\t\tAlterTableStmt *alterTableStatement)\n{\n\tRangeVar *relationRangeVar;\n\tforeach_declared_ptr(relationRangeVar, relationRangeVarList)\n\t{\n\t\t/*\n\t\t * Here we iterate the relation list, and if at least one of the relations\n\t\t * is marked as not-auto-converted, we should mark all of them as\n\t\t * not-auto-converted. In that case, we return true here.\n\t\t */\n\t\tList *commandList = alterTableStatement->cmds;\n\t\tLOCKMODE lockMode = AlterTableGetLockLevel(commandList);\n\t\tbool missingOk = alterTableStatement->missing_ok;\n\t\tOid relationId = RangeVarGetRelid(relationRangeVar, lockMode, missingOk);\n\t\tif (OidIsValid(relationId) && IsCitusTable(relationId) &&\n\t\t\tIsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t\t{\n\t\t\tCitusTableCacheEntry *entry = GetCitusTableCacheEntry(relationId);\n\t\t\tif (!entry->autoConverted)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CompareRangeVarsByOid is a comparison function to sort RangeVar object list.\n */\nstatic int\nCompareRangeVarsByOid(const void *leftElement, const void *rightElement)\n{\n\tRangeVar *leftRangeVar = *((RangeVar **) leftElement);\n\tRangeVar *rightRangeVar = *((RangeVar **) rightElement);\n\n\t/*\n\t * Any way we will check their existence, so it's okay to map non-existing\n\t * relations to InvalidOid when sorting.\n\t */\n\tbool missingOk = true;\n\n\t/*\n\t * As this is an object comparator function, there is no way to understand\n\t * proper lock mode. So assume caller already locked relations.\n\t */\n\tLOCKMODE lockMode = NoLock;\n\n\tOid leftRelationId = RangeVarGetRelid(leftRangeVar, lockMode, missingOk);\n\tOid rightRelationId = RangeVarGetRelid(rightRangeVar, lockMode, missingOk);\n\treturn CompareOids(&leftRelationId, &rightRelationId);\n}\n\n\n/*\n * GetAlterTableAddFKeyRightRelationIdList returns a list of oid's for right\n * relations involved in foreign keys to be defined by given ALTER TABLE command.\n */\nstatic List *\nGetAlterTableAddFKeyRightRelationIdList(AlterTableStmt *alterTableStatement)\n{\n\tList *rightRelationRangeVarList =\n\t\tGetAlterTableAddFKeyRightRelationRangeVarList(alterTableStatement);\n\tList *commandList = alterTableStatement->cmds;\n\tLOCKMODE lockMode = AlterTableGetLockLevel(commandList);\n\tbool missingOk = alterTableStatement->missing_ok;\n\tList *rightRelationIdList =\n\t\tGetRelationIdListFromRangeVarList(rightRelationRangeVarList, lockMode, missingOk);\n\treturn rightRelationIdList;\n}\n\n\n/*\n * GetAlterTableAddFKeyRightRelationRangeVarList returns a list of RangeVar\n * objects for right relations involved in foreign keys to be defined by\n * given ALTER TABLE command.\n */\nstatic List *\nGetAlterTableAddFKeyRightRelationRangeVarList(AlterTableStmt *alterTableStatement)\n{\n\tList *fKeyConstraintList = GetAlterTableAddFKeyConstraintList(alterTableStatement);\n\tList *rightRelationRangeVarList =\n\t\tGetRangeVarListFromFKeyConstraintList(fKeyConstraintList);\n\treturn rightRelationRangeVarList;\n}\n\n\n/*\n * GetAlterTableAddFKeyConstraintList returns a list of Constraint objects for\n * foreign keys that given ALTER TABLE to be defined by given ALTER TABLE command.\n */\nstatic List *\nGetAlterTableAddFKeyConstraintList(AlterTableStmt *alterTableStatement)\n{\n\tList *foreignKeyConstraintList = NIL;\n\n\tList *commandList = alterTableStatement->cmds;\n\tAlterTableCmd *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tList *commandForeignKeyConstraintList =\n\t\t\tGetAlterTableCommandFKeyConstraintList(command);\n\t\tforeignKeyConstraintList = list_concat(foreignKeyConstraintList,\n\t\t\t\t\t\t\t\t\t\t\t   commandForeignKeyConstraintList);\n\t}\n\n\treturn foreignKeyConstraintList;\n}\n\n\n/*\n * GetAlterTableCommandFKeyConstraintList returns a list of Constraint objects\n * for the foreign keys that given ALTER TABLE subcommand defines. Note that\n * this is only possible if it is an:\n *  - ADD CONSTRAINT subcommand (explicitly defines) or,\n *  - ADD COLUMN subcommand (implicitly defines by adding a new column that\n *    references to another table.\n */\nstatic List *\nGetAlterTableCommandFKeyConstraintList(AlterTableCmd *command)\n{\n\tList *fkeyConstraintList = NIL;\n\n\tAlterTableType alterTableType = command->subtype;\n\tif (alterTableType == AT_AddConstraint)\n\t{\n\t\tConstraint *constraint = (Constraint *) command->def;\n\t\tif (constraint->contype == CONSTR_FOREIGN)\n\t\t{\n\t\t\tfkeyConstraintList = lappend(fkeyConstraintList, constraint);\n\t\t}\n\t}\n\telse if (alterTableType == AT_AddColumn)\n\t{\n\t\tColumnDef *columnDefinition = (ColumnDef *) command->def;\n\t\tList *columnConstraints = columnDefinition->constraints;\n\n\t\tConstraint *constraint = NULL;\n\t\tforeach_declared_ptr(constraint, columnConstraints)\n\t\t{\n\t\t\tif (constraint->contype == CONSTR_FOREIGN)\n\t\t\t{\n\t\t\t\tfkeyConstraintList = lappend(fkeyConstraintList, constraint);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn fkeyConstraintList;\n}\n\n\n/*\n * GetRangeVarListFromFKeyConstraintList returns a list of RangeVar objects for\n * right relations in fKeyConstraintList.\n */\nstatic List *\nGetRangeVarListFromFKeyConstraintList(List *fKeyConstraintList)\n{\n\tList *rightRelationRangeVarList = NIL;\n\n\tConstraint *fKeyConstraint = NULL;\n\tforeach_declared_ptr(fKeyConstraint, fKeyConstraintList)\n\t{\n\t\tRangeVar *rightRelationRangeVar = fKeyConstraint->pktable;\n\t\trightRelationRangeVarList = lappend(rightRelationRangeVarList,\n\t\t\t\t\t\t\t\t\t\t\trightRelationRangeVar);\n\t}\n\n\treturn rightRelationRangeVarList;\n}\n\n\n/*\n * GetRelationIdListFromRangeVarList returns relation id list for relations\n * identified by RangeVar objects in given list.\n */\nstatic List *\nGetRelationIdListFromRangeVarList(List *rangeVarList, LOCKMODE lockMode, bool missingOk)\n{\n\tList *relationIdList = NIL;\n\n\tRangeVar *rangeVar = NULL;\n\tforeach_declared_ptr(rangeVar, rangeVarList)\n\t{\n\t\tOid rightRelationId = RangeVarGetRelid(rangeVar, lockMode, missingOk);\n\t\trelationIdList = lappend_oid(relationIdList, rightRelationId);\n\t}\n\n\treturn relationIdList;\n}\n\n\n/*\n * AlterTableCommandTypeIsTrigger returns true if given alter table command type\n * is identifies an ALTER TABLE .. TRIGGER .. command.\n */\nstatic bool\nAlterTableCommandTypeIsTrigger(AlterTableType alterTableType)\n{\n\tswitch (alterTableType)\n\t{\n\t\tcase AT_EnableTrig:\n\t\tcase AT_EnableAlwaysTrig:\n\t\tcase AT_EnableReplicaTrig:\n\t\tcase AT_EnableTrigUser:\n\t\tcase AT_DisableTrig:\n\t\tcase AT_DisableTrigUser:\n\t\tcase AT_EnableTrigAll:\n\t\tcase AT_DisableTrigAll:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\n\n/*\n * ConstrTypeUsesIndex returns true if the given constraint type uses an index\n */\nbool\nConstrTypeUsesIndex(ConstrType constrType)\n{\n\treturn constrType == CONSTR_PRIMARY ||\n\t\t   constrType == CONSTR_UNIQUE ||\n\t\t   constrType == CONSTR_EXCLUSION;\n}\n\n\n/*\n * ConstrTypeSupportsDefaultNaming returns true if we can generate a default name for the given constraint type\n */\nbool\nConstrTypeCitusCanDefaultName(ConstrType constrType)\n{\n\treturn constrType == CONSTR_PRIMARY ||\n\t\t   constrType == CONSTR_UNIQUE ||\n\t\t   constrType == CONSTR_EXCLUSION ||\n\t\t   constrType == CONSTR_CHECK ||\n\t\t   constrType == CONSTR_FOREIGN;\n}\n\n\n/*\n * AlterTableDropsForeignKey returns true if the given AlterTableStmt drops\n * a foreign key. False otherwise.\n */\nstatic bool\nAlterTableDropsForeignKey(AlterTableStmt *alterTableStatement)\n{\n\tLOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);\n\tOid relationId = AlterTableLookupRelation(alterTableStatement, lockmode);\n\n\tAlterTableCmd *command = NULL;\n\tforeach_declared_ptr(command, alterTableStatement->cmds)\n\t{\n\t\tAlterTableType alterTableType = command->subtype;\n\n\t\tif (alterTableType == AT_DropColumn)\n\t\t{\n\t\t\tchar *columnName = command->name;\n\t\t\tif (ColumnAppearsInForeignKey(columnName, relationId))\n\t\t\t{\n\t\t\t\t/* dropping a column in the either side of the fkey will drop the fkey */\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * In order to drop the foreign key, other than DROP COLUMN, the command must be\n\t\t * DROP CONSTRAINT command.\n\t\t */\n\t\tif (alterTableType != AT_DropConstraint)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tchar *constraintName = command->name;\n\t\tif (ConstraintIsAForeignKey(constraintName, relationId))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\telse if (ConstraintIsAUniquenessConstraint(constraintName, relationId))\n\t\t{\n\t\t\t/*\n\t\t\t * If the uniqueness constraint of the column that the foreign key depends on\n\t\t\t * is getting dropped, then the foreign key will also be dropped.\n\t\t\t */\n\t\t\tbool missingOk = false;\n\t\t\tOid uniquenessConstraintId =\n\t\t\t\tget_relation_constraint_oid(relationId, constraintName, missingOk);\n\t\t\tOid indexId = get_constraint_index(uniquenessConstraintId);\n\t\t\tif (AnyForeignKeyDependsOnIndex(indexId))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * AnyForeignKeyDependsOnIndex scans pg_depend and returns true if given index\n * is valid and any foreign key depends on it.\n */\nbool\nAnyForeignKeyDependsOnIndex(Oid indexId)\n{\n\tOid dependentObjectClassId = RelationRelationId;\n\tOid dependentObjectId = indexId;\n\tList *dependencyTupleList =\n\t\tGetPgDependTuplesForDependingObjects(dependentObjectClassId, dependentObjectId);\n\n\tHeapTuple dependencyTuple = NULL;\n\tforeach_declared_ptr(dependencyTuple, dependencyTupleList)\n\t{\n\t\tForm_pg_depend dependencyForm = (Form_pg_depend) GETSTRUCT(dependencyTuple);\n\t\tOid dependingClassId = dependencyForm->classid;\n\t\tif (dependingClassId != ConstraintRelationId)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tOid dependingObjectId = dependencyForm->objid;\n\t\tif (ConstraintWithIdIsOfType(dependingObjectId, CONSTRAINT_FOREIGN))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * PreprocessAlterTableStmt issues a warning.\n * ALTER TABLE ALL IN TABLESPACE statements have their node type as\n * AlterTableMoveAllStmt. At the moment we do not support this functionality in\n * the distributed environment. We warn out here.\n */\nList *\nPreprocessAlterTableMoveAllStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext)\n{\n\tif (EnableUnsupportedFeatureMessages)\n\t{\n\t\tereport(WARNING, (errmsg(\"not propagating ALTER TABLE ALL IN TABLESPACE \"\n\t\t\t\t\t\t\t\t \"commands to worker nodes\"),\n\t\t\t\t\t\t  errhint(\"Connect to worker nodes directly to manually \"\n\t\t\t\t\t\t\t\t  \"move all tables.\")));\n\t}\n\n\treturn NIL;\n}\n\n\n/*\n * PreprocessAlterTableSchemaStmt is executed before the statement is applied\n * to the local postgres instance.\n *\n * In this stage we can prepare the commands that will alter the schemas of the\n * shards.\n */\nList *\nPreprocessAlterTableSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TABLE || stmt->objectType == OBJECT_FOREIGN_TABLE);\n\n\tif (stmt->relation == NULL)\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *addresses = GetObjectAddressListFromParseTree((Node *) stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstmt->missing_ok, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(addresses) == 1);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *address = linitial(addresses);\n\tOid relationId = address->objectId;\n\n\t/*\n\t * Check whether we are dealing with a sequence or view here and route queries\n\t * accordingly to the right processor function. We need to check both objects here\n\t * since PG supports targeting sequences and views with ALTER TABLE commands.\n\t */\n\tchar relKind = get_rel_relkind(relationId);\n\tif (relKind == RELKIND_SEQUENCE)\n\t{\n\t\tAlterObjectSchemaStmt *stmtCopy = copyObject(stmt);\n\t\tstmtCopy->objectType = OBJECT_SEQUENCE;\n\t\treturn PreprocessAlterSequenceSchemaStmt((Node *) stmtCopy, queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t processUtilityContext);\n\t}\n\telse if (relKind == RELKIND_VIEW)\n\t{\n\t\tAlterObjectSchemaStmt *stmtCopy = copyObject(stmt);\n\t\tstmtCopy->objectType = OBJECT_VIEW;\n\t\treturn PreprocessAlterViewSchemaStmt((Node *) stmtCopy, queryString,\n\t\t\t\t\t\t\t\t\t\t\t processUtilityContext);\n\t}\n\n\t/* first check whether a distributed relation is affected */\n\tif (!OidIsValid(relationId) || !IsCitusTable(relationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\tOid oldSchemaId = get_rel_namespace(relationId);\n\tOid newSchemaId = get_namespace_oid(stmt->newschema, stmt->missing_ok);\n\tif (!OidIsValid(oldSchemaId) || !OidIsValid(newSchemaId))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*  Do nothing if new schema is the same as old schema */\n\tif (newSchemaId == oldSchemaId)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* Undistribute table if its old schema is a tenant schema */\n\tif (IsTenantSchema(oldSchemaId) && IsCoordinator())\n\t{\n\t\tEnsureUndistributeTenantTableSafe(relationId,\n\t\t\t\t\t\t\t\t\t\t  TenantOperationNames[TENANT_SET_SCHEMA]);\n\n\t\tchar *oldSchemaName = get_namespace_name(oldSchemaId);\n\t\tchar *tableName = stmt->relation->relname;\n\t\tereport(NOTICE, (errmsg(\"undistributing table %s in distributed schema %s \"\n\t\t\t\t\t\t\t\t\"before altering its schema\", tableName, oldSchemaName)));\n\n\t\t/* Undistribute tenant table by suppressing weird notices */\n\t\tTableConversionParameters params = {\n\t\t\t.relationId = relationId,\n\t\t\t.cascadeViaForeignKeys = false,\n\t\t\t.bypassTenantCheck = true,\n\t\t\t.suppressNoticeMessages = true,\n\t\t};\n\t\tUndistributeTable(&params);\n\n\t\t/* relation id changes after undistribute_table */\n\t\trelationId = get_relname_relid(tableName, oldSchemaId);\n\n\t\t/*\n\t\t * After undistribution, the table could be Citus table or Postgres table.\n\t\t * If it is Postgres table, do not propagate the `ALTER TABLE SET SCHEMA`\n\t\t * command to workers.\n\t\t */\n\t\tif (!IsCitusTable(relationId))\n\t\t{\n\t\t\treturn NIL;\n\t\t}\n\t}\n\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tQualifyTreeNode((Node *) stmt);\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\tddlJob->metadataSyncCommand = DeparseTreeNode((Node *) stmt);\n\tddlJob->taskList = DDLTaskList(relationId, ddlJob->metadataSyncCommand);\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * SkipForeignKeyValidationIfConstraintIsFkey checks and processes the alter table\n * statement to be worked on the distributed table. Currently, it only processes\n * ALTER TABLE ... ADD FOREIGN KEY command to skip the validation step.\n */\nvoid\nSkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStatement,\n\t\t\t\t\t\t\t\t\t\t   bool processLocalRelation)\n{\n\t/* first check whether a distributed relation is affected */\n\tif (alterTableStatement->relation == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tLOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);\n\tOid leftRelationId = AlterTableLookupRelation(alterTableStatement, lockmode);\n\tif (!OidIsValid(leftRelationId))\n\t{\n\t\treturn;\n\t}\n\n\tif (!IsCitusTable(leftRelationId) && !processLocalRelation)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * We check if there is a ADD FOREIGN CONSTRAINT command in sub commands\n\t * list. We set skip_validation to true to prevent PostgreSQL to verify\n\t * validity of the foreign constraint. Validity will be checked on the\n\t * shards anyway.\n\t */\n\tAlterTableCmd *command = NULL;\n\tforeach_declared_ptr(command, alterTableStatement->cmds)\n\t{\n\t\tAlterTableType alterTableType = command->subtype;\n\n\t\tif (alterTableType == AT_AddConstraint)\n\t\t{\n\t\t\t/* skip only if the constraint is a foreign key */\n\t\t\tConstraint *constraint = (Constraint *) command->def;\n\t\t\tif (constraint->contype == CONSTR_FOREIGN)\n\t\t\t{\n\t\t\t\t/* foreign constraint validations will be done in shards. */\n\t\t\t\tconstraint->skip_validation = true;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * IsAlterTableRenameStmt returns whether the passed-in RenameStmt is one of\n * the following forms:\n *\n *   - ALTER TABLE RENAME\n *   - ALTER TABLE RENAME COLUMN\n *   - ALTER TABLE RENAME CONSTRAINT\n */\nbool\nIsAlterTableRenameStmt(RenameStmt *renameStmt)\n{\n\tbool isAlterTableRenameStmt = false;\n\n\tif (renameStmt->renameType == OBJECT_TABLE ||\n\t\trenameStmt->renameType == OBJECT_FOREIGN_TABLE)\n\t{\n\t\tisAlterTableRenameStmt = true;\n\t}\n\telse if (renameStmt->renameType == OBJECT_COLUMN &&\n\t\t\t (renameStmt->relationType == OBJECT_TABLE ||\n\t\t\t  renameStmt->relationType == OBJECT_FOREIGN_TABLE))\n\t{\n\t\tisAlterTableRenameStmt = true;\n\t}\n\telse if (renameStmt->renameType == OBJECT_TABCONSTRAINT)\n\t{\n\t\tisAlterTableRenameStmt = true;\n\t}\n\n\treturn isAlterTableRenameStmt;\n}\n\n\n/*\n * ErrorIfAlterDropsPartitionColumn checks if any subcommands of the given alter table\n * command is a DROP COLUMN command which drops the partition column of a distributed\n * table. If there is such a subcommand, this function errors out.\n */\nvoid\nErrorIfAlterDropsPartitionColumn(AlterTableStmt *alterTableStatement)\n{\n\t/* first check whether a distributed relation is affected */\n\tif (alterTableStatement->relation == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tLOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);\n\tOid leftRelationId = AlterTableLookupRelation(alterTableStatement, lockmode);\n\tif (!OidIsValid(leftRelationId))\n\t{\n\t\treturn;\n\t}\n\n\tbool isCitusRelation = IsCitusTable(leftRelationId);\n\tif (!isCitusRelation)\n\t{\n\t\treturn;\n\t}\n\n\t/* then check if any of subcommands drop partition column.*/\n\tList *commandList = alterTableStatement->cmds;\n\tAlterTableCmd *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tAlterTableType alterTableType = command->subtype;\n\t\tif (alterTableType == AT_DropColumn)\n\t\t{\n\t\t\tif (AlterInvolvesPartitionColumn(alterTableStatement, command))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"cannot execute ALTER TABLE command \"\n\t\t\t\t\t\t\t\t\t   \"dropping partition column\")));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * PostprocessAlterTableStmt runs after the ALTER TABLE command has already run\n * on the master, so we are checking constraints over the table with constraints\n * already defined (to make the constraint check process same for ALTER TABLE and\n * CREATE TABLE). If constraints do not fulfill the rules we defined, they will be\n * removed and the table will return back to the state before the ALTER TABLE command.\n */\nvoid\nPostprocessAlterTableStmt(AlterTableStmt *alterTableStatement)\n{\n\tLOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);\n\tOid relationId = AlterTableLookupRelation(alterTableStatement, lockmode);\n\n\tif (relationId != InvalidOid)\n\t{\n\t\t/*\n\t\t * check whether we are dealing with a sequence here\n\t\t * if yes, it must be ALTER TABLE .. OWNER TO .. command\n\t\t * since this is the only ALTER command of a sequence that\n\t\t * passes through an AlterTableStmt\n\t\t */\n\t\tchar relKind = get_rel_relkind(relationId);\n\t\tif (relKind == RELKIND_SEQUENCE)\n\t\t{\n\t\t\talterTableStatement->objtype = OBJECT_SEQUENCE;\n\t\t\tPostprocessAlterSequenceOwnerStmt((Node *) alterTableStatement, NULL);\n\t\t\treturn;\n\t\t}\n\t\telse if (relKind == RELKIND_VIEW)\n\t\t{\n\t\t\talterTableStatement->objtype = OBJECT_VIEW;\n\t\t\tPostprocessAlterViewStmt((Node *) alterTableStatement, NULL);\n\t\t\treturn;\n\t\t}\n\n\t\t/*\n\t\t * Before ensuring each dependency exist, update dependent sequences\n\t\t * types if necessary.\n\t\t */\n\t\tEnsureRelationHasCompatibleSequenceTypes(relationId);\n\n\t\t/* changing a relation could introduce new dependencies */\n\t\tObjectAddress *tableAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*tableAddress, RelationRelationId, relationId);\n\t\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(tableAddress));\n\t}\n\n\t/* for the new sequences coming with this ALTER TABLE statement */\n\tbool needMetadataSyncForNewSequences = false;\n\n\tchar *alterTableDefaultNextvalCmd = NULL;\n\n\tList *commandList = alterTableStatement->cmds;\n\tAlterTableCmd *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tAlterTableType alterTableType = command->subtype;\n\n\t\tif (alterTableType == AT_AddConstraint)\n\t\t{\n\t\t\tAssert(list_length(commandList) == 1);\n\n\t\t\tErrorIfUnsupportedAlterAddConstraintStmt(alterTableStatement);\n\n\t\t\tif (!OidIsValid(relationId))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tConstraint *constraint = (Constraint *) command->def;\n\t\t\tif (constraint->contype == CONSTR_FOREIGN)\n\t\t\t{\n\t\t\t\tInvalidateForeignKeyGraph();\n\t\t\t}\n\t\t}\n\t\telse if (alterTableType == AT_AddColumn)\n\t\t{\n\t\t\tColumnDef *columnDefinition = (ColumnDef *) command->def;\n\t\t\tList *columnConstraints = columnDefinition->constraints;\n\t\t\tif (columnConstraints)\n\t\t\t{\n\t\t\t\tErrorIfUnsupportedAlterAddConstraintStmt(alterTableStatement);\n\t\t\t}\n\n\t\t\tif (!OidIsValid(relationId))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tConstraint *constraint = NULL;\n\t\t\tforeach_declared_ptr(constraint, columnConstraints)\n\t\t\t{\n\t\t\t\tif (constraint->conname == NULL &&\n\t\t\t\t\t(constraint->contype == CONSTR_PRIMARY ||\n\t\t\t\t\t constraint->contype == CONSTR_UNIQUE ||\n\t\t\t\t\t constraint->contype == CONSTR_FOREIGN ||\n\t\t\t\t\t constraint->contype == CONSTR_CHECK))\n\t\t\t\t{\n\t\t\t\t\tErrorUnsupportedAlterTableAddColumn(relationId, command,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tconstraint);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * We check for ADD COLUMN .. DEFAULT expr\n\t\t\t * if expr contains nextval('user_defined_seq')\n\t\t\t * we should make sure that the type of the column that uses\n\t\t\t * that sequence is supported\n\t\t\t */\n\t\t\tconstraint = NULL;\n\t\t\tforeach_declared_ptr(constraint, columnConstraints)\n\t\t\t{\n\t\t\t\tif (constraint->contype == CONSTR_DEFAULT)\n\t\t\t\t{\n\t\t\t\t\tif (constraint->raw_expr != NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\tParseState *pstate = make_parsestate(NULL);\n\t\t\t\t\t\tNode *expr = transformExpr(pstate, constraint->raw_expr,\n\t\t\t\t\t\t\t\t\t\t\t\t   EXPR_KIND_COLUMN_DEFAULT);\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * We should make sure that the type of the column that uses\n\t\t\t\t\t\t * that sequence is supported\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (contain_nextval_expression_walker(expr, NULL))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tAttrNumber attnum = get_attnum(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   columnDefinition->colname);\n\t\t\t\t\t\t\tOid seqOid = GetSequenceOid(relationId, attnum);\n\t\t\t\t\t\t\tif (seqOid != InvalidOid)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif (ShouldSyncTableMetadata(relationId))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tneedMetadataSyncForNewSequences = true;\n\t\t\t\t\t\t\t\t\talterTableDefaultNextvalCmd =\n\t\t\t\t\t\t\t\t\t\tGetAddColumnWithNextvalDefaultCmd(seqOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  columnDefinition\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ->colname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  columnDefinition\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ->typeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  command->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  missing_ok);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * We check for ALTER COLUMN .. SET DEFAULT nextval('user_defined_seq')\n\t\t * we should make sure that the type of the column that uses\n\t\t * that sequence is supported\n\t\t */\n\t\telse if (alterTableType == AT_ColumnDefault)\n\t\t{\n\t\t\tParseState *pstate = make_parsestate(NULL);\n\t\t\tNode *expr = transformExpr(pstate, command->def,\n\t\t\t\t\t\t\t\t\t   EXPR_KIND_COLUMN_DEFAULT);\n\n\t\t\tif (contain_nextval_expression_walker(expr, NULL))\n\t\t\t{\n\t\t\t\tAttrNumber attnum = get_attnum(relationId, command->name);\n\t\t\t\tOid seqOid = GetSequenceOid(relationId, attnum);\n\t\t\t\tif (seqOid != InvalidOid)\n\t\t\t\t{\n\t\t\t\t\tif (ShouldSyncTableMetadata(relationId))\n\t\t\t\t\t{\n\t\t\t\t\t\tneedMetadataSyncForNewSequences = true;\n\t\t\t\t\t\tbool missingTableOk = false;\n\t\t\t\t\t\talterTableDefaultNextvalCmd = GetAlterColumnWithNextvalDefaultCmd(\n\t\t\t\t\t\t\tseqOid, relationId, command->name, missingTableOk);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (needMetadataSyncForNewSequences)\n\t{\n\t\t/* prevent recursive propagation */\n\t\tSendCommandToWorkersWithMetadata(DISABLE_DDL_PROPAGATION);\n\n\t\t/*\n\t\t * It's easy to retrieve the sequence id to create the proper commands\n\t\t * in postprocess, after the dependency between the sequence and the table\n\t\t * has been created. We already return ddlJobs in PreprocessAlterTableStmt,\n\t\t * hence we can't return ddlJobs in PostprocessAlterTableStmt.\n\t\t * That's why we execute the following here instead of\n\t\t * in ExecuteDistributedDDLJob\n\t\t */\n\t\tSendCommandToWorkersWithMetadata(alterTableDefaultNextvalCmd);\n\n\t\tSendCommandToWorkersWithMetadata(ENABLE_DDL_PROPAGATION);\n\t}\n}\n\n\n/*\n * FixAlterTableStmtIndexNames runs after the ALTER TABLE command\n * has already run on the coordinator, and also after the distributed DDL\n * Jobs have been executed on the workers.\n *\n * We might have wrong index names generated on indexes of shards of partitions,\n * see https://github.com/citusdata/citus/pull/5397 for the details. So we\n * perform the relevant checks and index renaming here.\n */\nvoid\nFixAlterTableStmtIndexNames(AlterTableStmt *alterTableStatement)\n{\n\tLOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);\n\tOid relationId = AlterTableLookupRelation(alterTableStatement, lockmode);\n\tif (!(OidIsValid(relationId) && IsCitusTable(relationId) &&\n\t\t  PartitionedTable(relationId)))\n\t{\n\t\t/* we are only interested in partitioned Citus tables */\n\t\treturn;\n\t}\n\n\tList *commandList = alterTableStatement->cmds;\n\tAlterTableCmd *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tAlterTableType alterTableType = command->subtype;\n\n\t\t/*\n\t\t * If this a partitioned table, and the constraint type uses an index\n\t\t * UNIQUE, PRIMARY KEY, EXCLUDE constraint,\n\t\t * we have wrong index names generated on indexes of shards of\n\t\t * partitions of this table, so we should fix them\n\t\t */\n\t\tConstraint *constraint = (Constraint *) command->def;\n\t\tif (alterTableType == AT_AddConstraint &&\n\t\t\tConstrTypeUsesIndex(constraint->contype))\n\t\t{\n\t\t\tbool missingOk = false;\n\t\t\tconst char *constraintName = constraint->conname;\n\t\t\tOid constraintId =\n\t\t\t\tget_relation_constraint_oid(relationId, constraintName, missingOk);\n\n\t\t\t/* fix only the relevant index */\n\t\t\tOid parentIndexOid = get_constraint_index(constraintId);\n\n\t\t\tFixPartitionShardIndexNames(relationId, parentIndexOid);\n\t\t}\n\n\t\t/*\n\t\t * If this is an ALTER TABLE .. ATTACH PARTITION command\n\t\t * we have wrong index names generated on indexes of shards of\n\t\t * the current partition being attached, so we should fix them\n\t\t */\n\t\telse if (alterTableType == AT_AttachPartition)\n\t\t{\n\t\t\tPartitionCmd *partitionCommand = (PartitionCmd *) command->def;\n\t\t\tbool partitionMissingOk = false;\n\t\t\tOid partitionRelationId =\n\t\t\t\tRangeVarGetRelid(partitionCommand->name, lockmode,\n\t\t\t\t\t\t\t\t partitionMissingOk);\n\t\t\tOid parentIndexOid = InvalidOid;     /* fix all the indexes */\n\n\t\t\tFixPartitionShardIndexNames(partitionRelationId, parentIndexOid);\n\t\t}\n\t}\n}\n\n\n/*\n * GetSequenceOid returns the oid of the sequence used as default value\n * of the attribute with given attnum of the given table relationId\n * If there is no sequence used it returns InvalidOid.\n */\nOid\nGetSequenceOid(Oid relationId, AttrNumber attnum)\n{\n\t/* get attrdefoid from the given relationId and attnum */\n\tOid attrdefOid = get_attrdef_oid(relationId, attnum);\n\n\t/* retrieve the sequence id of the sequence found in nextval('seq') */\n\tList *sequencesFromAttrDef = GetSequencesFromAttrDef(attrdefOid);\n\n\tif (list_length(sequencesFromAttrDef) == 0)\n\t{\n\t\t/*\n\t\t * We need this check because sometimes there are cases where the\n\t\t * dependency between the table and the sequence is not formed\n\t\t * One example is when the default is defined by\n\t\t * DEFAULT nextval('seq_name'::text) (not by DEFAULT nextval('seq_name'))\n\t\t * In these cases, sequencesFromAttrDef with be empty.\n\t\t */\n\t\treturn InvalidOid;\n\t}\n\n\tif (list_length(sequencesFromAttrDef) > 1)\n\t{\n\t\t/* to simplify and eliminate cases like \"DEFAULT nextval('..') - nextval('..')\" */\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"More than one sequence in a column default\"\n\t\t\t\t\t\t\t\" is not supported for distribution \"\n\t\t\t\t\t\t\t\"or for adding local tables to metadata\")));\n\t}\n\n\treturn lfirst_oid(list_head(sequencesFromAttrDef));\n}\n\n\n/*\n * get_attrdef_oid gets the oid of the attrdef that has dependency with\n * the given relationId (refobjid) and attnum (refobjsubid).\n * If there is no such attrdef it returns InvalidOid.\n * NOTE: we are iterating pg_depend here since this function is used together\n * with other functions that iterate pg_depend. Normally, a look at pg_attrdef\n * would make more sense.\n */\nstatic Oid\nget_attrdef_oid(Oid relationId, AttrNumber attnum)\n{\n\tOid resultAttrdefOid = InvalidOid;\n\n\tScanKeyData key[3];\n\n\tRelation depRel = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_refclassid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(RelationRelationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_refobjid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(relationId));\n\tScanKeyInit(&key[2],\n\t\t\t\tAnum_pg_depend_refobjsubid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ,\n\t\t\t\tInt32GetDatum(attnum));\n\n\tSysScanDesc scan = systable_beginscan(depRel, DependReferenceIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, attnum ? 3 : 2, key);\n\n\tHeapTuple tup;\n\twhile (HeapTupleIsValid(tup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);\n\n\t\tif (deprec->classid == AttrDefaultRelationId)\n\t\t{\n\t\t\tresultAttrdefOid = deprec->objid;\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\ttable_close(depRel, AccessShareLock);\n\treturn resultAttrdefOid;\n}\n\n\n/*\n * GetAlterColumnWithNextvalDefaultCmd returns a string representing:\n * ALTER TABLE ALTER COLUMN .. SET DEFAULT nextval()\n * If sequence type is not bigint, we use worker_nextval() instead of nextval().\n */\nchar *\nGetAlterColumnWithNextvalDefaultCmd(Oid sequenceOid, Oid relationId, char *colname, bool\n\t\t\t\t\t\t\t\t\tmissingTableOk)\n{\n\tchar *qualifiedSequenceName = generate_qualified_relation_name(sequenceOid);\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\n\tchar *nextvalFunctionName = \"nextval\";\n\tbool useWorkerNextval = (pg_get_sequencedef(sequenceOid)->seqtypid != INT8OID);\n\tif (useWorkerNextval)\n\t{\n\t\t/*\n\t\t * We use worker_nextval for int and smallint types.\n\t\t * Check issue #5126 and PR #5254 for details.\n\t\t * https://github.com/citusdata/citus/issues/5126\n\t\t */\n\t\tnextvalFunctionName = \"worker_nextval\";\n\t}\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tappendStringInfo(&str, \"ALTER TABLE \");\n\n\tif (missingTableOk)\n\t{\n\t\tappendStringInfo(&str, \"IF EXISTS \");\n\t}\n\n\tappendStringInfo(&str, \"%s ALTER COLUMN %s \"\n\t\t\t\t\t\t   \"SET DEFAULT %s(%s::regclass)\",\n\t\t\t\t\t qualifiedRelationName,\n\t\t\t\t\t colname,\n\t\t\t\t\t quote_qualified_identifier(\"pg_catalog\", nextvalFunctionName),\n\t\t\t\t\t quote_literal_cstr(qualifiedSequenceName));\n\n\treturn str.data;\n}\n\n\n/*\n * GetAddColumnWithNextvalDefaultCmd returns a string representing:\n * ALTER TABLE ADD COLUMN .. DEFAULT nextval()\n * If sequence type is not bigint, we use worker_nextval() instead of nextval().\n */\nstatic char *\nGetAddColumnWithNextvalDefaultCmd(Oid sequenceOid, Oid relationId, char *colname,\n\t\t\t\t\t\t\t\t  TypeName *typeName, bool ifNotExists)\n{\n\tchar *qualifiedSequenceName = generate_qualified_relation_name(sequenceOid);\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\n\tchar *nextvalFunctionName = \"nextval\";\n\tbool useWorkerNextval = (pg_get_sequencedef(sequenceOid)->seqtypid != INT8OID);\n\tif (useWorkerNextval)\n\t{\n\t\t/*\n\t\t * We use worker_nextval for int and smallint types.\n\t\t * Check issue #5126 and PR #5254 for details.\n\t\t * https://github.com/citusdata/citus/issues/5126\n\t\t */\n\t\tnextvalFunctionName = \"worker_nextval\";\n\t}\n\n\tint32 typmod = 0;\n\tOid typeOid = InvalidOid;\n\tbits16 formatFlags = FORMAT_TYPE_TYPEMOD_GIVEN | FORMAT_TYPE_FORCE_QUALIFY;\n\ttypenameTypeIdAndMod(NULL, typeName, &typeOid, &typmod);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\tappendStringInfo(&str,\n\t\t\t\t\t \"ALTER TABLE %s ADD COLUMN %s %s %s \"\n\t\t\t\t\t \"DEFAULT %s(%s::regclass)\", qualifiedRelationName,\n\t\t\t\t\t ifNotExists ? \"IF NOT EXISTS\" : \"\", colname,\n\t\t\t\t\t format_type_extended(typeOid, typmod, formatFlags),\n\t\t\t\t\t quote_qualified_identifier(\"pg_catalog\", nextvalFunctionName),\n\t\t\t\t\t quote_literal_cstr(qualifiedSequenceName));\n\n\treturn str.data;\n}\n\n\nvoid\nErrorUnsupportedAlterTableAddColumn(Oid relationId, AlterTableCmd *command,\n\t\t\t\t\t\t\t\t\tConstraint *constraint)\n{\n\tColumnDef *columnDefinition = (ColumnDef *) command->def;\n\tchar *colName = columnDefinition->colname;\n\tchar *errMsg =\n\t\t\"cannot execute ADD COLUMN command with PRIMARY KEY, UNIQUE, FOREIGN and CHECK constraints\";\n\tStringInfo errHint = makeStringInfo();\n\tappendStringInfo(errHint, \"You can issue each command separately such as \");\n\tappendStringInfo(errHint,\n\t\t\t\t\t \"ALTER TABLE %s ADD COLUMN %s data_type; ALTER TABLE %s ADD CONSTRAINT constraint_name \",\n\t\t\t\t\t get_rel_name(relationId),\n\t\t\t\t\t colName, get_rel_name(relationId));\n\n\tif (constraint->contype == CONSTR_UNIQUE)\n\t{\n\t\tappendStringInfo(errHint, \"UNIQUE (%s)\", colName);\n\t}\n\telse if (constraint->contype == CONSTR_PRIMARY)\n\t{\n\t\tappendStringInfo(errHint, \"PRIMARY KEY (%s)\", colName);\n\t}\n\telse if (constraint->contype == CONSTR_CHECK)\n\t{\n\t\tappendStringInfo(errHint, \"CHECK (check_expression)\");\n\t}\n\telse if (constraint->contype == CONSTR_FOREIGN)\n\t{\n\t\tRangeVar *referencedTable = constraint->pktable;\n\t\tOid referencedRelationId = RangeVarGetRelid(referencedTable, NoLock, false);\n\n\t\tappendStringInfo(errHint, \"FOREIGN KEY (%s) REFERENCES %s\", colName,\n\t\t\t\t\t\t get_rel_name(referencedRelationId));\n\n\t\tif (list_length(constraint->pk_attrs) > 0)\n\t\t{\n\t\t\tAppendColumnNameList(errHint, constraint->pk_attrs);\n\t\t}\n\n\t\tif (constraint->fk_del_action == FKCONSTR_ACTION_SETNULL)\n\t\t{\n\t\t\tappendStringInfo(errHint, \" %s\", \"ON DELETE SET NULL\");\n\t\t}\n\t\telse if (constraint->fk_del_action == FKCONSTR_ACTION_CASCADE)\n\t\t{\n\t\t\tappendStringInfo(errHint, \" %s\", \"ON DELETE CASCADE\");\n\t\t}\n\t\telse if (constraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)\n\t\t{\n\t\t\tappendStringInfo(errHint, \" %s\", \"ON DELETE SET DEFAULT\");\n\t\t}\n\t\telse if (constraint->fk_del_action == FKCONSTR_ACTION_RESTRICT)\n\t\t{\n\t\t\tappendStringInfo(errHint, \" %s\", \"ON DELETE RESTRICT\");\n\t\t}\n\n\t\tif (constraint->fk_upd_action == FKCONSTR_ACTION_SETNULL)\n\t\t{\n\t\t\tappendStringInfo(errHint, \" %s\", \"ON UPDATE SET NULL\");\n\t\t}\n\t\telse if (constraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)\n\t\t{\n\t\t\tappendStringInfo(errHint, \" %s\", \"ON UPDATE CASCADE\");\n\t\t}\n\t\telse if (constraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)\n\t\t{\n\t\t\tappendStringInfo(errHint, \" %s\", \"ON UPDATE SET DEFAULT\");\n\t\t}\n\t\telse if (constraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT)\n\t\t{\n\t\t\tappendStringInfo(errHint, \" %s\", \"ON UPDATE RESTRICT\");\n\t\t}\n\t}\n\n\tappendStringInfo(errHint, \"%s\", \";\");\n\n\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\terrmsg(\"%s\", errMsg),\n\t\t\t\t\terrhint(\"%s\", errHint->data),\n\t\t\t\t\terrdetail(\"Adding a column with a constraint in \"\n\t\t\t\t\t\t\t  \"one command is not supported because \"\n\t\t\t\t\t\t\t  \"all constraints in Citus must have \"\n\t\t\t\t\t\t\t  \"explicit names\")));\n}\n\n\n/*\n * ErrorIfUnsupportedConstraint runs checks related to unique index / exclude\n * constraints.\n *\n * The function skips the uniqeness checks for reference tables (i.e., distribution\n * method is 'none').\n *\n * Forbid UNIQUE, PRIMARY KEY, or EXCLUDE constraints on append partitioned\n * tables, since currently there is no way of enforcing uniqueness for\n * overlapping shards.\n *\n * Similarly, do not allow such constraints if they do not include partition\n * column. This check is important for two reasons:\n * i. First, currently Citus does not enforce uniqueness constraint on multiple\n * shards.\n * ii. Second, INSERT INTO .. ON CONFLICT (i.e., UPSERT) queries can be executed\n * with no further check for constraints.\n */\nvoid\nErrorIfUnsupportedConstraint(Relation relation, char distributionMethod,\n\t\t\t\t\t\t\t char referencingReplicationModel,\n\t\t\t\t\t\t\t Var *distributionColumn, uint32 colocationId)\n{\n\t/*\n\t * We first perform check for foreign constraints. It is important to do this\n\t * check before next check, because other types of constraints are allowed on\n\t * reference tables and we return early for those constraints thanks to next\n\t * check. Therefore, for reference tables, we first check for foreign constraints\n\t * and if they are OK, we do not error out for other types of constraints.\n\t */\n\tErrorIfUnsupportedForeignConstraintExists(relation, distributionMethod,\n\t\t\t\t\t\t\t\t\t\t\t  referencingReplicationModel,\n\t\t\t\t\t\t\t\t\t\t\t  distributionColumn,\n\t\t\t\t\t\t\t\t\t\t\t  colocationId);\n\n\t/*\n\t * Citus supports any kind of uniqueness constraints for reference tables\n\t * given that they only consist of a single shard and we can simply rely on\n\t * Postgres.\n\t */\n\tif (distributionMethod == DISTRIBUTE_BY_NONE)\n\t{\n\t\treturn;\n\t}\n\n\tif (distributionColumn == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t\t\terrmsg(\"distribution column of distributed table is NULL\")));\n\t}\n\n\tchar *relationName = RelationGetRelationName(relation);\n\tList *indexOidList = RelationGetIndexList(relation);\n\n\tOid indexOid = InvalidOid;\n\tforeach_declared_oid(indexOid, indexOidList)\n\t{\n\t\tRelation indexDesc = index_open(indexOid, RowExclusiveLock);\n\t\tbool hasDistributionColumn = false;\n\n\t\t/* extract index key information from the index's pg_index info */\n\t\tIndexInfo *indexInfo = BuildIndexInfo(indexDesc);\n\n\t\t/* only check unique indexes and exclusion constraints. */\n\t\tif (indexInfo->ii_Unique == false && indexInfo->ii_ExclusionOps == NULL)\n\t\t{\n\t\t\tindex_close(indexDesc, NoLock);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Citus cannot enforce uniqueness/exclusion constraints with overlapping shards.\n\t\t * Thus, emit a warning for unique indexes and exclusion constraints on\n\t\t * append partitioned tables.\n\t\t */\n\t\tif (distributionMethod == DISTRIBUTE_BY_APPEND)\n\t\t{\n\t\t\tereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t  errmsg(\"table \\\"%s\\\" has a UNIQUE or EXCLUDE constraint\",\n\t\t\t\t\t\t\t\t\t relationName),\n\t\t\t\t\t\t\t  errdetail(\"UNIQUE constraints, EXCLUDE constraints, \"\n\t\t\t\t\t\t\t\t\t\t\"and PRIMARY KEYs on \"\n\t\t\t\t\t\t\t\t\t\t\"append-partitioned tables cannot be enforced.\"),\n\t\t\t\t\t\t\t  errhint(\"Consider using hash partitioning.\")));\n\t\t}\n\n\t\tif (AllowUnsafeConstraints)\n\t\t{\n\t\t\t/*\n\t\t\t * The user explicitly wants to allow the constraint without\n\t\t\t * distribution column.\n\t\t\t */\n\t\t\tindex_close(indexDesc, NoLock);\n\t\t\tcontinue;\n\t\t}\n\n\t\tint attributeCount = indexInfo->ii_NumIndexAttrs;\n\t\tAttrNumber *attributeNumberArray = indexInfo->ii_IndexAttrNumbers;\n\n\t\tfor (int attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++)\n\t\t{\n\t\t\tAttrNumber attributeNumber = attributeNumberArray[attributeIndex];\n\n\t\t\tif (distributionColumn->varattno != attributeNumber)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tbool uniqueConstraint = indexInfo->ii_Unique;\n\t\t\tbool exclusionConstraintWithEquality = (indexInfo->ii_ExclusionOps != NULL &&\n\t\t\t\t\t\t\t\t\t\t\t\t\tOperatorImplementsEquality(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tindexInfo->ii_ExclusionOps[\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tattributeIndex]));\n\n\t\t\tif (uniqueConstraint || exclusionConstraintWithEquality)\n\t\t\t{\n\t\t\t\thasDistributionColumn = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!hasDistributionColumn)\n\t\t{\n\t\t\tereport(ERROR, (\n\t\t\t\t\t\terrcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot create constraint on \\\"%s\\\"\",\n\t\t\t\t\t\t\t   relationName),\n\t\t\t\t\t\terrdetail(\"Distributed relations cannot have UNIQUE, \"\n\t\t\t\t\t\t\t\t  \"EXCLUDE, or PRIMARY KEY constraints that do not \"\n\t\t\t\t\t\t\t\t  \"include the partition column (with an equality \"\n\t\t\t\t\t\t\t\t  \"operator if EXCLUDE).\")));\n\t\t}\n\n\t\tindex_close(indexDesc, NoLock);\n\t}\n}\n\n\n/*\n * ErrorIfAlterTableDropTableNameFromPostgresFdw errors if given alter foreign table\n * option list drops 'table_name' from a postgresfdw foreign table which is\n * inside metadata.\n */\nstatic void\nErrorIfAlterTableDropTableNameFromPostgresFdw(List *optionList, Oid relationId)\n{\n\tchar relationKind PG_USED_FOR_ASSERTS_ONLY =\n\t\tget_rel_relkind(relationId);\n\tAssert(relationKind == RELKIND_FOREIGN_TABLE);\n\n\tForeignTable *foreignTable = GetForeignTable(relationId);\n\tOid serverId = foreignTable->serverid;\n\tif (!ServerUsesPostgresFdw(serverId))\n\t{\n\t\treturn;\n\t}\n\n\tif (IsCitusTableType(relationId, CITUS_LOCAL_TABLE) &&\n\t\tForeignTableDropsTableNameOption(optionList))\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t errmsg(\n\t\t\t\t\t \"alter foreign table alter options (drop table_name) command \"\n\t\t\t\t\t \"is not allowed for Citus tables\"),\n\t\t\t\t errdetail(\n\t\t\t\t\t \"Table_name option can not be dropped from a foreign table \"\n\t\t\t\t\t \"which is inside metadata.\"),\n\t\t\t\t errhint(\n\t\t\t\t\t \"Try to undistribute foreign table before dropping table_name option.\")));\n\t}\n}\n\n\n/*\n * ErrorIfUnsupportedAlterTableStmt checks if the corresponding alter table\n * statement is supported for distributed tables and errors out if it is not.\n * Currently, only the following commands are supported.\n *\n * ALTER TABLE ADD|DROP COLUMN\n * ALTER TABLE ALTER COLUMN SET DATA TYPE\n * ALTER TABLE SET|DROP NOT NULL\n * ALTER TABLE SET|DROP DEFAULT\n * ALTER TABLE ADD|DROP CONSTRAINT\n * ALTER TABLE REPLICA IDENTITY\n * ALTER TABLE SET ()\n * ALTER TABLE ENABLE|DISABLE|NO FORCE|FORCE ROW LEVEL SECURITY\n * ALTER TABLE RESET ()\n * ALTER TABLE ENABLE/DISABLE TRIGGER (if enable_unsafe_triggers is not set, we only support triggers for citus local tables)\n */\nstatic void\nErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement)\n{\n\tList *commandList = alterTableStatement->cmds;\n\n\tLOCKMODE lockmode = AlterTableGetLockLevel(commandList);\n\tOid relationId = AlterTableLookupRelation(alterTableStatement, lockmode);\n\n\t/* error out if any of the subcommands are unsupported */\n\tAlterTableCmd *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tAlterTableType alterTableType = command->subtype;\n\n\t\tswitch (alterTableType)\n\t\t{\n\t\t\tcase AT_AddColumn:\n\t\t\t{\n\t\t\t\tif (IsA(command->def, ColumnDef))\n\t\t\t\t{\n\t\t\t\t\tColumnDef *column = (ColumnDef *) command->def;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Check for SERIAL pseudo-types. The structure of this\n\t\t\t\t\t * check is copied from transformColumnDefinition.\n\t\t\t\t\t */\n\t\t\t\t\tif (column->typeName && list_length(column->typeName->names) == 1 &&\n\t\t\t\t\t\t!column->typeName->pct_type)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar *typeName = strVal(linitial(column->typeName->names));\n\n\t\t\t\t\t\tif (strcmp(typeName, \"smallserial\") == 0 ||\n\t\t\t\t\t\t\tstrcmp(typeName, \"serial2\") == 0 ||\n\t\t\t\t\t\t\tstrcmp(typeName, \"serial\") == 0 ||\n\t\t\t\t\t\t\tstrcmp(typeName, \"serial4\") == 0 ||\n\t\t\t\t\t\t\tstrcmp(typeName, \"bigserial\") == 0 ||\n\t\t\t\t\t\t\tstrcmp(typeName, \"serial8\") == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * We currently don't support adding a serial column for an MX table\n\t\t\t\t\t\t\t * TODO: record the dependency in the workers\n\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\tif (ShouldSyncTableMetadata(relationId))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"cannot execute ADD COLUMN commands involving serial\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\" pseudotypes when metadata is synchronized to workers\")));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * we only allow adding a serial column if it is the only subcommand\n\t\t\t\t\t\t\t * and it has no constraints\n\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\tif (commandList->length > 1 || column->constraints)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tereport(ERROR, (errcode(\n\t\t\t\t\t\t\t\t\t\t\t\t\tERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"cannot execute ADD COLUMN commands involving \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"serial pseudotypes with other subcommands/constraints\"),\n\t\t\t\t\t\t\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"You can issue each subcommand separately\")));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * Currently we don't support backfilling the new column with default values\n\t\t\t\t\t\t\t * if the table is not empty\n\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\tif (!TableEmpty(relationId))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tereport(ERROR, (errcode(\n\t\t\t\t\t\t\t\t\t\t\t\t\tERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Cannot add a column involving serial pseudotypes \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"because the table is not empty\"),\n\t\t\t\t\t\t\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"You can first call ALTER TABLE .. ADD COLUMN .. smallint/int/bigint\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Then set the default by ALTER TABLE .. ALTER COLUMN .. SET DEFAULT nextval('..')\")));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\n\t\t\t\t\tConstraint *columnConstraint = NULL;\n\t\t\t\t\tforeach_declared_ptr(columnConstraint, column->constraints)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (columnConstraint->contype == CONSTR_IDENTITY)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * We currently don't support adding an identity column for an MX table\n\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\tif (ShouldSyncTableMetadata(relationId))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"cannot execute ADD COLUMN commands involving identity\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\" columns when metadata is synchronized to workers\")));\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * Currently we don't support backfilling the new identity column with default values\n\t\t\t\t\t\t\t * if the table is not empty\n\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\tif (!TableEmpty(relationId))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Cannot add an identity column because the table is not empty\")));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\n\t\t\t\t\tList *columnConstraints = column->constraints;\n\n\t\t\t\t\tConstraint *constraint = NULL;\n\t\t\t\t\tforeach_declared_ptr(constraint, columnConstraints)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (constraint->contype == CONSTR_DEFAULT)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (constraint->raw_expr != NULL)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tParseState *pstate = make_parsestate(NULL);\n\t\t\t\t\t\t\t\tNode *expr = transformExpr(pstate, constraint->raw_expr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   EXPR_KIND_COLUMN_DEFAULT);\n\n\t\t\t\t\t\t\t\tif (contain_nextval_expression_walker(expr, NULL))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\t * we only allow adding a column with non_const default\n\t\t\t\t\t\t\t\t\t * if its the only subcommand and has no other constraints\n\t\t\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\t\t\tif (commandList->length > 1 ||\n\t\t\t\t\t\t\t\t\t\tcolumnConstraints->length > 1)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t\t\t\t\t\t\t(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\t\t\t\t errmsg(\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"cannot execute ADD COLUMN .. DEFAULT nextval('..')\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t \" command with other subcommands/constraints\"),\n\t\t\t\t\t\t\t\t\t\t\t\t errhint(\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"You can issue each subcommand separately\")));\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t\t\t * Currently we don't support backfilling the new column with default values\n\t\t\t\t\t\t\t\t\t * if the table is not empty\n\t\t\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\t\t\tif (!TableEmpty(relationId))\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t\t\t\t\t\t\t(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\t\t\t\t errmsg(\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"cannot add a column involving DEFAULT nextval('..') \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"because the table is not empty\"),\n\t\t\t\t\t\t\t\t\t\t\t\t errhint(\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"You can first call ALTER TABLE .. ADD COLUMN .. smallint/int/bigint\\n\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"Then set the default by ALTER TABLE .. ALTER COLUMN .. SET DEFAULT nextval('..')\")));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_ColumnDefault:\n\t\t\t{\n\t\t\t\tif (AlterInvolvesPartitionColumn(alterTableStatement, command))\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errmsg(\"cannot execute ALTER TABLE command \"\n\t\t\t\t\t\t\t\t\t\t   \"involving partition column\")));\n\t\t\t\t}\n\n\t\t\t\tParseState *pstate = make_parsestate(NULL);\n\t\t\t\tNode *expr = transformExpr(pstate, command->def,\n\t\t\t\t\t\t\t\t\t\t   EXPR_KIND_COLUMN_DEFAULT);\n\n\t\t\t\tif (contain_nextval_expression_walker(expr, NULL))\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * we only allow altering a column's default to non_const expr\n\t\t\t\t\t * if its the only subcommand\n\t\t\t\t\t */\n\t\t\t\t\tif (commandList->length > 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tereport(ERROR, (errcode(\n\t\t\t\t\t\t\t\t\t\t\tERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\t\t\"cannot execute ALTER COLUMN COLUMN .. SET DEFAULT \"\n\t\t\t\t\t\t\t\t\t\t\t\"nextval('..') command with other subcommands\"),\n\t\t\t\t\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\t\t\t\t\"You can issue each subcommand separately\")));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_AlterColumnType:\n\t\t\t{\n\t\t\t\tif (AlterInvolvesPartitionColumn(alterTableStatement, command))\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errmsg(\"cannot execute ALTER TABLE command \"\n\t\t\t\t\t\t\t\t\t\t   \"involving partition column\")));\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * We check for ALTER COLUMN TYPE ...\n\t\t\t\t * if the column is an identity column,\n\t\t\t\t * changing the type of the column\n\t\t\t\t * should not be allowed for now\n\t\t\t\t */\n\t\t\t\tif (AlterColumnInvolvesIdentityColumn(alterTableStatement, command))\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errmsg(\"cannot execute ALTER COLUMN command \"\n\t\t\t\t\t\t\t\t\t\t   \"involving identity column\")));\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * We check for ALTER COLUMN TYPE ...\n\t\t\t\t * if the column has default coming from a user-defined sequence\n\t\t\t\t * changing the type of the column\n\t\t\t\t * should not be allowed for now\n\t\t\t\t */\n\t\t\t\tAttrNumber attnum = get_attnum(relationId, command->name);\n\t\t\t\tList *seqInfoList = NIL;\n\t\t\t\tGetDependentSequencesWithRelation(relationId, &seqInfoList, attnum,\n\t\t\t\t\t\t\t\t\t\t\t\t  DEPENDENCY_AUTO);\n\t\t\t\tif (seqInfoList != NIL)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errmsg(\"cannot execute ALTER COLUMN TYPE .. command \"\n\t\t\t\t\t\t\t\t\t\t   \"because the column involves a default coming \"\n\t\t\t\t\t\t\t\t\t\t   \"from a sequence\")));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_DropColumn:\n\t\t\tcase AT_DropNotNull:\n\t\t\t{\n\t\t\t\tif (AlterInvolvesPartitionColumn(alterTableStatement, command))\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errmsg(\"cannot execute ALTER TABLE command \"\n\t\t\t\t\t\t\t\t\t\t   \"involving partition column\")));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_AddConstraint:\n\t\t\t{\n\t\t\t\t/* we only allow constraints if they are only subcommand */\n\t\t\t\tif (commandList->length > 1)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\terrmsg(\"cannot execute ADD CONSTRAINT command with \"\n\t\t\t\t\t\t\t\t\t\t   \"other subcommands\"),\n\t\t\t\t\t\t\t\t\terrhint(\"You can issue each subcommand separately\")));\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_AttachPartition:\n\t\t\t{\n\t\t\t\tPartitionCmd *partitionCommand = (PartitionCmd *) command->def;\n\t\t\t\tbool missingOK = false;\n\t\t\t\tOid partitionRelationId = RangeVarGetRelid(partitionCommand->name,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   lockmode, missingOK);\n\n\t\t\t\t/* we only allow partitioning commands if they are only subcommand */\n\t\t\t\tif (commandList->length > 1)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\terrmsg(\"cannot execute ATTACH PARTITION command \"\n\t\t\t\t\t\t\t\t\t\t   \"with other subcommands\"),\n\t\t\t\t\t\t\t\t\terrhint(\"You can issue each subcommand \"\n\t\t\t\t\t\t\t\t\t\t\t\"separately.\")));\n\t\t\t\t}\n\n\t\t\t\tif (IsCitusTableType(partitionRelationId, CITUS_LOCAL_TABLE) ||\n\t\t\t\t\tIsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Citus Local Tables cannot be colocated with other tables.\n\t\t\t\t\t * If either of two relations is not a Citus Local Table, then we\n\t\t\t\t\t * don't need to check colocation since CreateCitusLocalTable would\n\t\t\t\t\t * anyway throw an error.\n\t\t\t\t\t */\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (IsCitusTable(partitionRelationId) &&\n\t\t\t\t\t!TablesColocated(relationId, partitionRelationId))\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\terrmsg(\"distributed tables cannot have \"\n\t\t\t\t\t\t\t\t\t\t   \"non-colocated distributed tables as a \"\n\t\t\t\t\t\t\t\t\t\t   \"partition \")));\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_DetachPartitionFinalize:\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"ALTER TABLE .. DETACH PARTITION .. FINALIZE \"\n\t\t\t\t\t\t\t\t\t   \"commands are currently unsupported.\")));\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_DetachPartition:\n\t\t\t{\n\t\t\t\t/* we only allow partitioning commands if they are only subcommand */\n\t\t\t\tif (commandList->length > 1)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\terrmsg(\"cannot execute DETACH PARTITION command \"\n\t\t\t\t\t\t\t\t\t\t   \"with other subcommands\"),\n\t\t\t\t\t\t\t\t\terrhint(\"You can issue each subcommand \"\n\t\t\t\t\t\t\t\t\t\t\t\"separately.\")));\n\t\t\t\t}\n\n\t\t\t\tPartitionCmd *partitionCommand = (PartitionCmd *) command->def;\n\n\t\t\t\tif (partitionCommand->concurrent)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errmsg(\"ALTER TABLE .. DETACH PARTITION .. \"\n\t\t\t\t\t\t\t\t\t\t   \"CONCURRENTLY commands are currently \"\n\t\t\t\t\t\t\t\t\t\t   \"unsupported.\")));\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_EnableTrig:\n\t\t\tcase AT_EnableAlwaysTrig:\n\t\t\tcase AT_EnableReplicaTrig:\n\t\t\tcase AT_EnableTrigUser:\n\t\t\tcase AT_DisableTrig:\n\t\t\tcase AT_DisableTrigUser:\n\t\t\tcase AT_EnableTrigAll:\n\t\t\tcase AT_DisableTrigAll:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Postgres already does not allow executing ALTER TABLE TRIGGER\n\t\t\t\t * commands with other subcommands, but let's be on the safe side.\n\t\t\t\t */\n\t\t\t\tif (commandList->length > 1)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t\t\t\t\t\terrmsg(\"cannot execute ENABLE/DISABLE TRIGGER \"\n\t\t\t\t\t\t\t\t\t\t   \"command with other subcommands\"),\n\t\t\t\t\t\t\t\t\terrhint(\"You can issue each subcommand separately\")));\n\t\t\t\t}\n\n\t\t\t\tErrorOutForTriggerIfNotSupported(relationId);\n\n\t\t\t\tbreak;\n\t\t\t}\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\t\tcase AT_SetExpression:\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\"ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION commands \"\n\t\t\t\t\t\t\t\t\t\"are currently unsupported.\")));\n\t\t\t\tbreak;\n\t\t\t}\n#endif\n\n\t\t\tcase AT_SetAccessMethod:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * If command->name == NULL, that means the user is trying to use\n\t\t\t\t * ALTER TABLE ... SET ACCESS METHOD DEFAULT\n\t\t\t\t * which we don't support currently.\n\t\t\t\t */\n\t\t\t\tif (command->name == NULL)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\t\"DEFAULT option in ALTER TABLE ... SET ACCESS METHOD \"\n\t\t\t\t\t\t\t\t\t\t\"is currently unsupported.\"),\n\t\t\t\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\t\t\t\"You can rerun the command by explicitly writing the access method name.\")));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_SetNotNull:\n\t\t\tcase AT_ReplicaIdentity:\n\t\t\tcase AT_ChangeOwner:\n\t\t\tcase AT_EnableRowSecurity:\n\t\t\tcase AT_DisableRowSecurity:\n\t\t\tcase AT_ForceRowSecurity:\n\t\t\tcase AT_NoForceRowSecurity:\n\t\t\tcase AT_ValidateConstraint:\n\t\t\tcase AT_DropConstraint: /* we do the check for invalidation in AlterTableDropsForeignKey */\n\t\t\tcase AT_SetCompression:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We will not perform any special check for:\n\t\t\t\t * ALTER TABLE .. SET ACCESS METHOD ..\n\t\t\t\t * ALTER TABLE .. ALTER COLUMN .. SET NOT NULL\n\t\t\t\t * ALTER TABLE .. REPLICA IDENTITY ..\n\t\t\t\t * ALTER TABLE .. VALIDATE CONSTRAINT ..\n\t\t\t\t * ALTER TABLE .. ALTER COLUMN .. SET COMPRESSION ..\n\t\t\t\t */\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_SetRelOptions:  /* SET (...) */\n\t\t\tcase AT_ResetRelOptions:    /* RESET (...) */\n\t\t\tcase AT_ReplaceRelOptions:  /* replace entire option list */\n\t\t\tcase AT_SetLogged:\n\t\t\tcase AT_SetUnLogged:\n\t\t\t{\n\t\t\t\t/* this command is supported by Citus */\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_GenericOptions:\n\t\t\t{\n\t\t\t\tif (IsForeignTable(relationId))\n\t\t\t\t{\n\t\t\t\t\tList *optionList = (List *) command->def;\n\t\t\t\t\tErrorIfAlterTableDropTableNameFromPostgresFdw(optionList, relationId);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* fallthrough */\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t errmsg(\"alter table command is currently unsupported\"),\n\t\t\t\t\t\t errdetail(\"Only ADD|DROP COLUMN, SET|DROP NOT NULL, \"\n\t\t\t\t\t\t\t\t   \"SET|DROP DEFAULT, ADD|DROP|VALIDATE CONSTRAINT, \"\n\t\t\t\t\t\t\t\t   \"SET (), RESET (), \"\n\t\t\t\t\t\t\t\t   \"ENABLE|DISABLE|NO FORCE|FORCE ROW LEVEL SECURITY, \"\n\t\t\t\t\t\t\t\t   \"ATTACH|DETACH PARTITION and TYPE subcommands \"\n\t\t\t\t\t\t\t\t   \"are supported.\")));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * SetupExecutionModeForAlterTable is the function that is responsible\n * for two things for practical purpose for not doing the same checks\n * twice:\n *     (a) For any command, decide and return whether we should\n *         run the command in sequential mode\n *     (b) For commands in a transaction block, set the transaction local\n *         multi-shard modify mode to sequential when necessary\n *\n * The commands that operate on the same reference table shard in parallel\n * is in the interest of (a), where the return value indicates the executor\n * to run the command sequentially to prevent self-deadlocks.\n *\n * The commands that both operate on the same reference table shard in parallel\n * and cascades to run any parallel operation is in the interest of (b). By\n * setting the multi-shard mode, we ensure that the cascading parallel commands\n * are executed sequentially to prevent self-deadlocks.\n *\n * One final note on the function is that if the function decides to execute\n * the command in sequential mode, and a parallel command has already been\n * executed in the same transaction, the function errors out. See the comment\n * in the function for the rationale.\n */\nstatic bool\nSetupExecutionModeForAlterTable(Oid relationId, AlterTableCmd *command)\n{\n\tbool executeSequentially = false;\n\tAlterTableType alterTableType = command->subtype;\n\tif (alterTableType == AT_DropConstraint)\n\t{\n\t\tchar *constraintName = command->name;\n\t\tif (ConstraintIsAForeignKeyToReferenceTable(constraintName, relationId))\n\t\t{\n\t\t\texecuteSequentially = true;\n\t\t}\n\t}\n\telse if (alterTableType == AT_AddColumn)\n\t{\n\t\tColumnDef *columnDefinition = (ColumnDef *) command->def;\n\t\tList *columnConstraints = columnDefinition->constraints;\n\n\t\tConstraint *constraint = NULL;\n\t\tforeach_declared_ptr(constraint, columnConstraints)\n\t\t{\n\t\t\tif (constraint->contype == CONSTR_FOREIGN)\n\t\t\t{\n\t\t\t\tOid rightRelationId = RangeVarGetRelid(constraint->pktable, NoLock,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   false);\n\t\t\t\tif (IsCitusTableType(rightRelationId, REFERENCE_TABLE))\n\t\t\t\t{\n\t\t\t\t\texecuteSequentially = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse if (alterTableType == AT_DropColumn || alterTableType == AT_AlterColumnType)\n\t{\n\t\tchar *affectedColumnName = command->name;\n\n\t\tif (ColumnAppearsInForeignKeyToReferenceTable(affectedColumnName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  relationId))\n\t\t{\n\t\t\tif (alterTableType == AT_AlterColumnType)\n\t\t\t{\n\t\t\t\tSetLocalMultiShardModifyModeToSequential();\n\t\t\t}\n\n\t\t\texecuteSequentially = true;\n\t\t}\n\t}\n\telse if (alterTableType == AT_AddConstraint)\n\t{\n\t\t/*\n\t\t * We need to execute the ddls working with reference tables on the\n\t\t * right side sequentially, because parallel ddl operations\n\t\t * relating to one and only shard of a reference table on a worker\n\t\t * may cause self-deadlocks.\n\t\t */\n\t\tConstraint *constraint = (Constraint *) command->def;\n\t\tif (constraint->contype == CONSTR_FOREIGN)\n\t\t{\n\t\t\tOid rightRelationId = RangeVarGetRelid(constraint->pktable, NoLock,\n\t\t\t\t\t\t\t\t\t\t\t\t   false);\n\t\t\tif (IsCitusTableType(rightRelationId, REFERENCE_TABLE))\n\t\t\t{\n\t\t\t\texecuteSequentially = true;\n\t\t\t}\n\t\t}\n\t}\n\telse if (alterTableType == AT_DetachPartition || alterTableType == AT_AttachPartition)\n\t{\n\t\t/* check if there are foreign constraints to reference tables */\n\t\tif (HasForeignKeyToReferenceTable(relationId))\n\t\t{\n\t\t\texecuteSequentially = true;\n\t\t}\n\t}\n\n\t/*\n\t * If there has already been a parallel query executed, the sequential mode\n\t * would still use the already opened parallel connections to the workers for\n\t * the distributed tables, thus contradicting our purpose of using\n\t * sequential mode.\n\t */\n\tif (executeSequentially &&\n\t\tHasDistributionKey(relationId) &&\n\t\tParallelQueryExecutedInTransaction())\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tereport(ERROR, (errmsg(\"cannot modify table \\\"%s\\\" because there \"\n\t\t\t\t\t\t\t   \"was a parallel operation on a distributed table \"\n\t\t\t\t\t\t\t   \"in the transaction\", relationName),\n\t\t\t\t\t\terrdetail(\"When there is a foreign key to a reference \"\n\t\t\t\t\t\t\t\t  \"table, Citus needs to perform all operations \"\n\t\t\t\t\t\t\t\t  \"over a single connection per node to ensure \"\n\t\t\t\t\t\t\t\t  \"consistency.\"),\n\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t}\n\n\treturn executeSequentially;\n}\n\n\n/*\n * InterShardDDLTaskList builds a list of tasks to execute a inter shard DDL command on a\n * shards of given list of distributed table. At the moment this function is used to run\n * foreign key, partitioning and attaching partition index command on worker node.\n *\n * leftRelationId is the relation id of actual distributed table which given command is\n * applied. rightRelationId is the relation id of either index or distributed table which\n * given command refers to.\n */\nList *\nInterShardDDLTaskList(Oid leftRelationId, Oid rightRelationId,\n\t\t\t\t\t  const char *commandString)\n{\n\tList *leftShardList = LoadShardIntervalList(leftRelationId);\n\tList *rightShardList = CreateRightShardListForInterShardDDLTask(rightRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tleftRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tleftShardList);\n\n\t/* lock metadata before getting placement lists */\n\tLockShardListMetadata(leftShardList, ShareLock);\n\n\tuint64 jobId = INVALID_JOB_ID;\n\tint taskId = 1;\n\n\tOid leftSchemaId = get_rel_namespace(leftRelationId);\n\tchar *leftSchemaName = get_namespace_name(leftSchemaId);\n\tchar *escapedLeftSchemaName = quote_literal_cstr(leftSchemaName);\n\n\tOid rightSchemaId = get_rel_namespace(rightRelationId);\n\tchar *rightSchemaName = get_namespace_name(rightSchemaId);\n\tchar *escapedRightSchemaName = quote_literal_cstr(rightSchemaName);\n\n\tchar *escapedCommandString = quote_literal_cstr(commandString);\n\n\tList *taskList = NIL;\n\n\tShardInterval *leftShardInterval = NULL;\n\tShardInterval *rightShardInterval = NULL;\n\tforboth_ptr(leftShardInterval, leftShardList, rightShardInterval, rightShardList)\n\t{\n\t\tuint64 leftShardId = leftShardInterval->shardId;\n\t\tuint64 rightShardId = rightShardInterval->shardId;\n\n\t\tStringInfo applyCommand = makeStringInfo();\n\t\tappendStringInfo(applyCommand, WORKER_APPLY_INTER_SHARD_DDL_COMMAND,\n\t\t\t\t\t\t leftShardId, escapedLeftSchemaName, rightShardId,\n\t\t\t\t\t\t escapedRightSchemaName, escapedCommandString);\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->jobId = jobId;\n\t\ttask->taskId = taskId++;\n\t\ttask->taskType = DDL_TASK;\n\t\tSetTaskQueryString(task, applyCommand->data);\n\t\ttask->dependentTaskList = NULL;\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\ttask->anchorShardId = leftShardId;\n\t\tSetInterShardDDLTaskPlacementList(task, leftShardInterval, rightShardInterval);\n\t\tSetInterShardDDLTaskRelationShardList(task, leftShardInterval,\n\t\t\t\t\t\t\t\t\t\t\t  rightShardInterval);\n\n\t\ttaskList = lappend(taskList, task);\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * CreateRightShardListForInterShardDDLTask is a helper function that creates\n * shard list for the right relation for InterShardDDLTaskList.\n */\nstatic List *\nCreateRightShardListForInterShardDDLTask(Oid rightRelationId, Oid leftRelationId,\n\t\t\t\t\t\t\t\t\t\t List *leftShardList)\n{\n\tList *rightShardList = LoadShardIntervalList(rightRelationId);\n\n\n\tif (!IsCitusTableType(leftRelationId, CITUS_LOCAL_TABLE) &&\n\t\tIsCitusTableType(rightRelationId, REFERENCE_TABLE))\n\t{\n\t\t/*\n\t\t * If the right relation is a reference table and left relation is not\n\t\t * a citus local table, we need to make sure that the tasks are created\n\t\t * in a way that the right shard stays the same since we only have one\n\t\t * placement per worker.\n\t\t * If left relation is a citus local table, then we don't need to populate\n\t\t * reference table shards as we will execute ADD/DROP constraint command\n\t\t * only for coordinator placement of reference table.\n\t\t */\n\t\tShardInterval *rightShard = (ShardInterval *) linitial(rightShardList);\n\t\tint leftShardCount = list_length(leftShardList);\n\t\trightShardList = GenerateListFromElement(rightShard, leftShardCount);\n\t}\n\n\treturn rightShardList;\n}\n\n\n/*\n * SetInterShardDDLTaskPlacementList sets taskPlacementList field of given\n * inter-shard DDL task according to passed shard interval arguments.\n */\nstatic void\nSetInterShardDDLTaskPlacementList(Task *task, ShardInterval *leftShardInterval,\n\t\t\t\t\t\t\t\t  ShardInterval *rightShardInterval)\n{\n\tuint64 leftShardId = leftShardInterval->shardId;\n\tList *leftShardPlacementList = ActiveShardPlacementList(leftShardId);\n\n\tuint64 rightShardId = rightShardInterval->shardId;\n\tList *rightShardPlacementList = ActiveShardPlacementList(rightShardId);\n\n\tList *intersectedPlacementList = NIL;\n\n\tShardPlacement *leftShardPlacement = NULL;\n\tforeach_declared_ptr(leftShardPlacement, leftShardPlacementList)\n\t{\n\t\tShardPlacement *rightShardPlacement = NULL;\n\t\tforeach_declared_ptr(rightShardPlacement, rightShardPlacementList)\n\t\t{\n\t\t\tif (leftShardPlacement->nodeId == rightShardPlacement->nodeId)\n\t\t\t{\n\t\t\t\tintersectedPlacementList = lappend(intersectedPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t   leftShardPlacement);\n\t\t\t}\n\t\t}\n\t}\n\n\ttask->taskPlacementList = intersectedPlacementList;\n}\n\n\n/*\n * SetInterShardDDLTaskRelationShardList sets relationShardList field of given\n * inter-shard DDL task according to passed shard interval arguments.\n */\nstatic void\nSetInterShardDDLTaskRelationShardList(Task *task, ShardInterval *leftShardInterval,\n\t\t\t\t\t\t\t\t\t  ShardInterval *rightShardInterval)\n{\n\tRelationShard *leftRelationShard = CitusMakeNode(RelationShard);\n\tleftRelationShard->relationId = leftShardInterval->relationId;\n\tleftRelationShard->shardId = leftShardInterval->shardId;\n\n\tRelationShard *rightRelationShard = CitusMakeNode(RelationShard);\n\trightRelationShard->relationId = rightShardInterval->relationId;\n\trightRelationShard->shardId = rightShardInterval->shardId;\n\n\ttask->relationShardList = list_make2(leftRelationShard, rightRelationShard);\n}\n\n\n/*\n * AlterColumnInvolvesIdentityColumn checks if the given alter column command\n * involves relation's identity column.\n */\nstatic bool\nAlterColumnInvolvesIdentityColumn(AlterTableStmt *alterTableStatement,\n\t\t\t\t\t\t\t\t  AlterTableCmd *command)\n{\n\tbool involvesIdentityColumn = false;\n\tchar *alterColumnName = command->name;\n\n\tLOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);\n\tOid relationId = AlterTableLookupRelation(alterTableStatement, lockmode);\n\tif (!OidIsValid(relationId))\n\t{\n\t\treturn false;\n\t}\n\n\tHeapTuple tuple = SearchSysCacheAttName(relationId, alterColumnName);\n\tif (HeapTupleIsValid(tuple))\n\t{\n\t\tForm_pg_attribute targetAttr = (Form_pg_attribute) GETSTRUCT(tuple);\n\n\t\tif (targetAttr->attidentity)\n\t\t{\n\t\t\tinvolvesIdentityColumn = true;\n\t\t}\n\n\t\tReleaseSysCache(tuple);\n\t}\n\n\treturn involvesIdentityColumn;\n}\n\n\n/*\n * AlterInvolvesPartitionColumn checks if the given alter table command\n * involves relation's partition column.\n */\nstatic bool\nAlterInvolvesPartitionColumn(AlterTableStmt *alterTableStatement,\n\t\t\t\t\t\t\t AlterTableCmd *command)\n{\n\tbool involvesPartitionColumn = false;\n\tchar *alterColumnName = command->name;\n\n\tLOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);\n\tOid relationId = AlterTableLookupRelation(alterTableStatement, lockmode);\n\tif (!OidIsValid(relationId))\n\t{\n\t\treturn false;\n\t}\n\n\tVar *partitionColumn = DistPartitionKey(relationId);\n\n\tHeapTuple tuple = SearchSysCacheAttName(relationId, alterColumnName);\n\tif (HeapTupleIsValid(tuple))\n\t{\n\t\tForm_pg_attribute targetAttr = (Form_pg_attribute) GETSTRUCT(tuple);\n\n\t\t/* reference tables do not have partition column, so allow them */\n\t\tif (partitionColumn != NULL &&\n\t\t\ttargetAttr->attnum == partitionColumn->varattno)\n\t\t{\n\t\t\tinvolvesPartitionColumn = true;\n\t\t}\n\n\t\tReleaseSysCache(tuple);\n\t}\n\n\treturn involvesPartitionColumn;\n}\n\n\n/*\n * ErrorIfUnsupportedAlterAddConstraintStmt runs the constraint checks on distributed\n * table using the same logic with create_distributed_table.\n */\nstatic void\nErrorIfUnsupportedAlterAddConstraintStmt(AlterTableStmt *alterTableStatement)\n{\n\tLOCKMODE lockmode = AlterTableGetLockLevel(alterTableStatement->cmds);\n\tOid relationId = AlterTableLookupRelation(alterTableStatement, lockmode);\n\tchar distributionMethod = PartitionMethod(relationId);\n\tchar referencingReplicationModel = TableReplicationModel(relationId);\n\tVar *distributionColumn = DistPartitionKey(relationId);\n\tuint32 colocationId = TableColocationId(relationId);\n\tRelation relation = relation_open(relationId, ExclusiveLock);\n\n\tErrorIfUnsupportedConstraint(relation, distributionMethod,\n\t\t\t\t\t\t\t\t referencingReplicationModel,\n\t\t\t\t\t\t\t\t distributionColumn, colocationId);\n\trelation_close(relation, NoLock);\n}\n\n\n/*\n * AlterTableSchemaStmtObjectAddress returns the ObjectAddress of the table that\n * is the object of the AlterObjectSchemaStmt.\n *\n * This could be called both before or after it has been applied locally. It will\n * look in the old schema first, if the table cannot be found in that schema it\n * will look in the new schema. Errors if missing_ok is false and the table cannot\n * be found in either of the schemas.\n */\nList *\nAlterTableSchemaStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TABLE || stmt->objectType == OBJECT_FOREIGN_TABLE);\n\n\tconst char *tableName = stmt->relation->relname;\n\tOid tableOid = InvalidOid;\n\tif (stmt->relation->schemaname)\n\t{\n\t\tconst char *schemaName = stmt->relation->schemaname;\n\t\tOid schemaOid = get_namespace_oid(schemaName, missing_ok);\n\t\ttableOid = get_relname_relid(tableName, schemaOid);\n\t}\n\telse\n\t{\n\t\ttableOid = RelnameGetRelid(stmt->relation->relname);\n\t}\n\n\tif (tableOid == InvalidOid)\n\t{\n\t\tconst char *newSchemaName = stmt->newschema;\n\t\tOid newSchemaOid = get_namespace_oid(newSchemaName, true);\n\t\ttableOid = get_relname_relid(tableName, newSchemaOid);\n\n\t\tif (!missing_ok && tableOid == InvalidOid)\n\t\t{\n\t\t\tconst char *quotedTableName =\n\t\t\t\tquote_qualified_identifier(stmt->relation->schemaname, tableName);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE),\n\t\t\t\t\t\t\terrmsg(\"relation \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t\t   quotedTableName)));\n\t\t}\n\t}\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, RelationRelationId, tableOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * MakeNameListFromRangeVar makes a namelist from a RangeVar. Its behaviour\n * should be the exact opposite of postgres' makeRangeVarFromNameList.\n */\nList *\nMakeNameListFromRangeVar(const RangeVar *rel)\n{\n\tif (rel->catalogname != NULL)\n\t{\n\t\tAssert(rel->schemaname != NULL);\n\t\tAssert(rel->relname != NULL);\n\t\treturn list_make3(makeString(rel->catalogname),\n\t\t\t\t\t\t  makeString(rel->schemaname),\n\t\t\t\t\t\t  makeString(rel->relname));\n\t}\n\telse if (rel->schemaname != NULL)\n\t{\n\t\tAssert(rel->relname != NULL);\n\t\treturn list_make2(makeString(rel->schemaname),\n\t\t\t\t\t\t  makeString(rel->relname));\n\t}\n\telse\n\t{\n\t\tAssert(rel->relname != NULL);\n\t\treturn list_make1(makeString(rel->relname));\n\t}\n}\n\n\n/*\n * ErrorIfTableHasIdentityColumn errors out if the given table has identity column\n */\nvoid\nErrorIfTableHasIdentityColumn(Oid relationId)\n{\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\n\tfor (int attributeIndex = 0; attributeIndex < tupleDescriptor->natts;\n\t\t attributeIndex++)\n\t{\n\t\tForm_pg_attribute attributeForm = TupleDescAttr(tupleDescriptor, attributeIndex);\n\n\t\tif (attributeForm->attidentity)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\"cannot complete operation on a table with identity column\")));\n\t\t}\n\t}\n\n\trelation_close(relation, NoLock);\n}\n\n\n/*\n * ConvertNewTableIfNecessary converts the given table to a tenant schema\n * table or a Citus managed table if necessary.\n *\n * Input node is expected to be a CreateStmt or a CreateTableAsStmt.\n */\nvoid\nConvertNewTableIfNecessary(Node *createStmt)\n{\n\t/*\n\t * Need to increment command counter so that next command\n\t * can see the new table.\n\t */\n\tCommandCounterIncrement();\n\n\tif (IsA(createStmt, CreateTableAsStmt))\n\t{\n\t\tCreateTableAsStmt *createTableAsStmt = (CreateTableAsStmt *) createStmt;\n\n\t\tbool missingOk = false;\n\t\tOid createdRelationId = RangeVarGetRelid(createTableAsStmt->into->rel,\n\t\t\t\t\t\t\t\t\t\t\t\t NoLock, missingOk);\n\n\t\tif (ShouldCreateTenantSchemaTable(createdRelationId))\n\t\t{\n\t\t\t/* not try to convert the table if it already exists and IF NOT EXISTS syntax is used */\n\t\t\tif (createTableAsStmt->if_not_exists && IsCitusTable(createdRelationId))\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * We allow mat views in a distributed schema but do not make them a tenant\n\t\t\t * table. We should skip converting them.\n\t\t\t */\n\t\t\tif (get_rel_relkind(createdRelationId) == RELKIND_MATVIEW)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tCreateTenantSchemaTable(createdRelationId);\n\t\t}\n\n\t\t/*\n\t\t * We simply ignore the tables created by using that syntax when using\n\t\t * Citus managed tables.\n\t\t */\n\t\treturn;\n\t}\n\n\tCreateStmt *baseCreateTableStmt = (CreateStmt *) createStmt;\n\n\tbool missingOk = false;\n\tOid createdRelationId = RangeVarGetRelid(baseCreateTableStmt->relation,\n\t\t\t\t\t\t\t\t\t\t\t NoLock, missingOk);\n\n\t/* not try to convert the table if it already exists and IF NOT EXISTS syntax is used */\n\tif (baseCreateTableStmt->if_not_exists && IsCitusTable(createdRelationId))\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * Check ShouldCreateTenantSchemaTable() before ShouldAddNewTableToMetadata()\n\t * because we don't want to unnecessarily add the table into metadata\n\t * (as a Citus managed table) before distributing it as a tenant table.\n\t */\n\tif (ShouldCreateTenantSchemaTable(createdRelationId))\n\t{\n\t\t/*\n\t\t * We skip creating tenant schema table if the table is a partition\n\t\t * table because in that case PostprocessCreateTableStmt() should've\n\t\t * already created a tenant schema table from the partition table.\n\t\t */\n\t\tif (!PartitionTable(createdRelationId))\n\t\t{\n\t\t\tCreateTenantSchemaTable(createdRelationId);\n\t\t}\n\t}\n\telse if (ShouldAddNewTableToMetadata(createdRelationId))\n\t{\n\t\t/*\n\t\t * Here we set autoConverted to false, since the user explicitly\n\t\t * wants these tables to be added to metadata, by setting the\n\t\t * GUC use_citus_managed_tables to true.\n\t\t */\n\t\tbool autoConverted = false;\n\t\tbool cascade = true;\n\t\tCreateCitusLocalTable(createdRelationId, cascade, autoConverted);\n\t}\n}\n\n\n/*\n * ConvertToTenantTableIfNecessary converts given relation to a tenant table if its\n * schema changed to a distributed schema.\n */\nvoid\nConvertToTenantTableIfNecessary(AlterObjectSchemaStmt *stmt)\n{\n\tAssert(stmt->objectType == OBJECT_TABLE || stmt->objectType == OBJECT_FOREIGN_TABLE);\n\n\tif (!IsCoordinator())\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * We will let Postgres deal with missing_ok\n\t */\n\tList *tableAddresses = GetObjectAddressListFromParseTree((Node *) stmt, true, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(tableAddresses) == 1);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *tableAddress = linitial(tableAddresses);\n\tchar relKind = get_rel_relkind(tableAddress->objectId);\n\tif (relKind == RELKIND_SEQUENCE || relKind == RELKIND_VIEW)\n\t{\n\t\treturn;\n\t}\n\n\tOid relationId = tableAddress->objectId;\n\tOid schemaId = get_namespace_oid(stmt->newschema, stmt->missing_ok);\n\tif (!OidIsValid(schemaId))\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * Make table a tenant table when its schema actually changed. When its schema\n\t * is not changed as in `ALTER TABLE <tbl> SET SCHEMA <same_schema>`, we detect\n\t * that by seeing the table is still a single shard table. (i.e. not undistributed\n\t * at `preprocess` step)\n\t */\n\tif (!IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED) &&\n\t\tIsTenantSchema(schemaId))\n\t{\n\t\tEnsureTenantTable(relationId, \"ALTER TABLE SET SCHEMA\");\n\n\t\tchar *schemaName = get_namespace_name(schemaId);\n\t\tchar *tableName = stmt->relation->relname;\n\t\tereport(NOTICE, (errmsg(\"Moving %s into distributed schema %s\",\n\t\t\t\t\t\t\t\ttableName, schemaName)));\n\n\t\tCreateTenantSchemaTable(relationId);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/text_search.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * text_search.c\n *    Commands for creating and altering TEXT SEARCH objects\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"access/genam.h\"\n#include \"access/xact.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/objectaddress.h\"\n#include \"catalog/pg_ts_config.h\"\n#include \"catalog/pg_ts_config_map.h\"\n#include \"catalog/pg_ts_dict.h\"\n#include \"catalog/pg_ts_parser.h\"\n#include \"catalog/pg_ts_template.h\"\n#include \"commands/comment.h\"\n#include \"commands/defrem.h\"\n#include \"commands/extension.h\"\n#include \"nodes/makefuncs.h\"\n#include \"tsearch/ts_cache.h\"\n#include \"tsearch/ts_public.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/worker_create_or_replace.h\"\n\n\nstatic DefineStmt * GetTextSearchConfigDefineStmt(Oid tsconfigOid);\nstatic DefineStmt * GetTextSearchDictionaryDefineStmt(Oid tsdictOid);\nstatic List * GetTextSearchDictionaryInitOptions(HeapTuple tup, Form_pg_ts_dict dict);\nstatic List * GetTextSearchConfigCommentStmt(Oid tsconfigOid);\nstatic List * GetTextSearchDictionaryCommentStmt(Oid tsconfigOid);\nstatic List * get_ts_parser_namelist(Oid tsparserOid);\nstatic List * GetTextSearchConfigMappingStmt(Oid tsconfigOid);\nstatic List * GetTextSearchConfigOwnerStmts(Oid tsconfigOid);\nstatic List * GetTextSearchDictionaryOwnerStmts(Oid tsdictOid);\n\nstatic List * get_ts_dict_namelist(Oid tsdictOid);\nstatic List * get_ts_template_namelist(Oid tstemplateOid);\nstatic Oid get_ts_config_parser_oid(Oid tsconfigOid);\nstatic char * get_ts_parser_tokentype_name(Oid parserOid, int32 tokentype);\n\nList *\nGetCreateTextSearchConfigStatements(const ObjectAddress *address)\n{\n\tAssert(address->classId == TSConfigRelationId);\n\tList *stmts = NIL;\n\n\t/* CREATE TEXT SEARCH CONFIGURATION ...*/\n\tstmts = lappend(stmts, GetTextSearchConfigDefineStmt(address->objectId));\n\n\t/* ALTER TEXT SEARCH CONFIGURATION ... OWNER TO ...*/\n\tstmts = list_concat(stmts, GetTextSearchConfigOwnerStmts(address->objectId));\n\n\t/* COMMENT ON TEXT SEARCH CONFIGURATION ... */\n\tstmts = list_concat(stmts, GetTextSearchConfigCommentStmt(address->objectId));\n\n\n\t/* ALTER TEXT SEARCH CONFIGURATION ... ADD MAPPING FOR ... WITH ... */\n\tstmts = list_concat(stmts, GetTextSearchConfigMappingStmt(address->objectId));\n\n\treturn stmts;\n}\n\n\nList *\nGetCreateTextSearchDictionaryStatements(const ObjectAddress *address)\n{\n\tAssert(address->classId == TSDictionaryRelationId);\n\tList *stmts = NIL;\n\n\t/* CREATE TEXT SEARCH DICTIONARY ...*/\n\tstmts = lappend(stmts, GetTextSearchDictionaryDefineStmt(address->objectId));\n\n\t/* ALTER TEXT SEARCH DICTIONARY ... OWNER TO ...*/\n\tstmts = list_concat(stmts, GetTextSearchDictionaryOwnerStmts(address->objectId));\n\n\t/* COMMENT ON TEXT SEARCH DICTIONARY ... */\n\tstmts = list_concat(stmts, GetTextSearchDictionaryCommentStmt(address->objectId));\n\n\treturn stmts;\n}\n\n\n/*\n * CreateTextSearchConfigDDLCommandsIdempotent creates a list of ddl commands to recreate\n * a TEXT SERACH CONFIGURATION object in an idempotent manner on workers.\n */\nList *\nCreateTextSearchConfigDDLCommandsIdempotent(const ObjectAddress *address)\n{\n\tList *stmts = GetCreateTextSearchConfigStatements(address);\n\tList *sqls = DeparseTreeNodes(stmts);\n\treturn list_make1(WrapCreateOrReplaceList(sqls));\n}\n\n\n/*\n * CreateTextSearchDictDDLCommandsIdempotent creates a list of ddl commands to recreate\n * a TEXT SEARCH CONFIGURATION object in an idempotent manner on workers.\n */\nList *\nCreateTextSearchDictDDLCommandsIdempotent(const ObjectAddress *address)\n{\n\tList *stmts = GetCreateTextSearchDictionaryStatements(address);\n\tList *sqls = DeparseTreeNodes(stmts);\n\treturn list_make1(WrapCreateOrReplaceList(sqls));\n}\n\n\n/*\n * GetTextSearchConfigDefineStmt returns the DefineStmt for a TEXT SEARCH CONFIGURATION\n * based on the configuration as defined in the catalog identified by tsconfigOid.\n *\n * This statement will only contain the parser, as all other properties for text search\n * configurations are stored as mappings in a different catalog.\n */\nstatic DefineStmt *\nGetTextSearchConfigDefineStmt(Oid tsconfigOid)\n{\n\tHeapTuple tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(tsconfigOid));\n\tif (!HeapTupleIsValid(tup)) /* should not happen */\n\t{\n\t\telog(ERROR, \"cache lookup failed for text search configuration %u\",\n\t\t\t tsconfigOid);\n\t}\n\tForm_pg_ts_config config = (Form_pg_ts_config) GETSTRUCT(tup);\n\n\tDefineStmt *stmt = makeNode(DefineStmt);\n\tstmt->kind = OBJECT_TSCONFIGURATION;\n\n\tstmt->defnames = get_ts_config_namelist(tsconfigOid);\n\n\tList *parserNameList = get_ts_parser_namelist(config->cfgparser);\n\tTypeName *parserTypeName = makeTypeNameFromNameList(parserNameList);\n\tstmt->definition = list_make1(makeDefElem(\"parser\", (Node *) parserTypeName, -1));\n\n\tReleaseSysCache(tup);\n\treturn stmt;\n}\n\n\n/*\n * GetTextSearchDictionaryDefineStmt returns the DefineStmt for a TEXT SEARCH DICTIONARY\n * based on the dictionary as defined in the catalog identified by tsdictOid.\n *\n * This statement will contain the template along with all initilaization options.\n */\nstatic DefineStmt *\nGetTextSearchDictionaryDefineStmt(Oid tsdictOid)\n{\n\tHeapTuple tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(tsdictOid));\n\tif (!HeapTupleIsValid(tup)) /* should not happen */\n\t{\n\t\telog(ERROR, \"cache lookup failed for text search dictionary %u\",\n\t\t\t tsdictOid);\n\t}\n\tForm_pg_ts_dict dict = (Form_pg_ts_dict) GETSTRUCT(tup);\n\n\tDefineStmt *stmt = makeNode(DefineStmt);\n\tstmt->kind = OBJECT_TSDICTIONARY;\n\tstmt->defnames = get_ts_dict_namelist(tsdictOid);\n\tstmt->definition = GetTextSearchDictionaryInitOptions(tup, dict);\n\n\tReleaseSysCache(tup);\n\treturn stmt;\n}\n\n\n/*\n * GetTextSearchDictionaryInitOptions returns the list of DefElem for the initialization\n * options for a TEXT SEARCH DICTIONARY.\n *\n * The initialization options contain both the template name, and template specific key,\n * value pairs that are supplied when the dictionary was first created.\n */\nstatic List *\nGetTextSearchDictionaryInitOptions(HeapTuple tup, Form_pg_ts_dict dict)\n{\n\tList *templateNameList = get_ts_template_namelist(dict->dicttemplate);\n\tTypeName *templateTypeName = makeTypeNameFromNameList(templateNameList);\n\tDefElem *templateDefElem = makeDefElem(\"template\", (Node *) templateTypeName, -1);\n\n\tRelation TSDictionaryRelation = table_open(TSDictionaryRelationId, AccessShareLock);\n\tTupleDesc TSDictDescription = RelationGetDescr(TSDictionaryRelation);\n\tbool isnull = false;\n\tDatum dictinitoption = heap_getattr(tup, Anum_pg_ts_dict_dictinitoption,\n\t\t\t\t\t\t\t\t\t\tTSDictDescription, &isnull);\n\n\tList *initOptionDefElemList = NIL;\n\tif (!isnull)\n\t{\n\t\tinitOptionDefElemList = deserialize_deflist(dictinitoption);\n\t}\n\n\ttable_close(TSDictionaryRelation, AccessShareLock);\n\n\treturn lcons(templateDefElem, initOptionDefElemList);\n}\n\n\n/*\n * GetTextSearchConfigCommentStmt returns a list containing all entries to recreate a\n * comment on the configuration identified by tsconfigOid. The list could be empty if\n * there is no comment on a configuration.\n *\n * The reason for a list is for easy use when building a list of all statements to invoke\n * to recreate the text search configuration. An empty list can easily be concatinated\n * without inspection, contrary to a NULL ptr if we would return the CommentStmt struct.\n */\nstatic List *\nGetTextSearchConfigCommentStmt(Oid tsconfigOid)\n{\n\tchar *comment = GetComment(tsconfigOid, TSConfigRelationId, 0);\n\tif (!comment)\n\t{\n\t\treturn NIL;\n\t}\n\n\tCommentStmt *stmt = makeNode(CommentStmt);\n\tstmt->objtype = OBJECT_TSCONFIGURATION;\n\n\tstmt->object = (Node *) get_ts_config_namelist(tsconfigOid);\n\tstmt->comment = comment;\n\treturn list_make1(stmt);\n}\n\n\n/*\n * GetTextSearchDictionaryCommentStmt returns a list containing all entries to recreate a\n * comment on the dictionary identified by tsconfigOid. The list could be empty if\n * there is no comment on a dictionary.\n *\n * The reason for a list is for easy use when building a list of all statements to invoke\n * to recreate the text search dictionary. An empty list can easily be concatinated\n * without inspection, contrary to a NULL ptr if we would return the CommentStmt struct.\n */\nstatic List *\nGetTextSearchDictionaryCommentStmt(Oid tsdictOid)\n{\n\tchar *comment = GetComment(tsdictOid, TSDictionaryRelationId, 0);\n\tif (!comment)\n\t{\n\t\treturn NIL;\n\t}\n\n\tCommentStmt *stmt = makeNode(CommentStmt);\n\tstmt->objtype = OBJECT_TSDICTIONARY;\n\n\tstmt->object = (Node *) get_ts_dict_namelist(tsdictOid);\n\tstmt->comment = comment;\n\treturn list_make1(stmt);\n}\n\n\n/*\n * GetTextSearchConfigMappingStmt returns a list of all mappings from token_types to\n * dictionaries configured on a text search configuration identified by tsconfigOid.\n *\n * Many mappings can exist on a configuration which all require their own statement to\n * recreate.\n */\nstatic List *\nGetTextSearchConfigMappingStmt(Oid tsconfigOid)\n{\n\tScanKeyData mapskey = { 0 };\n\n\t/* mapcfg = tsconfigOid */\n\tScanKeyInit(&mapskey,\n\t\t\t\tAnum_pg_ts_config_map_mapcfg,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(tsconfigOid));\n\n\tRelation maprel = table_open(TSConfigMapRelationId, AccessShareLock);\n\tRelation mapidx = index_open(TSConfigMapIndexId, AccessShareLock);\n\tSysScanDesc mapscan = systable_beginscan_ordered(maprel, mapidx, NULL, 1, &mapskey);\n\n\tList *stmts = NIL;\n\tAlterTSConfigurationStmt *stmt = NULL;\n\n\t/*\n\t * We iterate the config mappings on the index order filtered by mapcfg. Meaning we\n\t * get equal maptokentype's in 1 run. By comparing the current tokentype to the last\n\t * we know when we can create a new stmt and append the previous constructed one to\n\t * the list.\n\t */\n\tint lastTokType = -1;\n\n\t/*\n\t * We read all mappings filtered by config id, hence we only need to load the name\n\t * once and can reuse for every statement.\n\t */\n\tList *configName = get_ts_config_namelist(tsconfigOid);\n\n\tOid parserOid = get_ts_config_parser_oid(tsconfigOid);\n\n\tHeapTuple maptup = NULL;\n\twhile ((maptup = systable_getnext_ordered(mapscan, ForwardScanDirection)) != NULL)\n\t{\n\t\tForm_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);\n\t\tif (lastTokType != cfgmap->maptokentype)\n\t\t{\n\t\t\t/* creating a new statement, appending the previous one (if existing) */\n\t\t\tif (stmt != NULL)\n\t\t\t{\n\t\t\t\tstmts = lappend(stmts, stmt);\n\t\t\t}\n\n\t\t\tstmt = makeNode(AlterTSConfigurationStmt);\n\t\t\tstmt->cfgname = configName;\n\t\t\tstmt->kind = ALTER_TSCONFIG_ADD_MAPPING;\n\t\t\tstmt->tokentype = list_make1(makeString(\n\t\t\t\t\t\t\t\t\t\t\t get_ts_parser_tokentype_name(parserOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  cfgmap->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  maptokentype)));\n\n\t\t\tlastTokType = cfgmap->maptokentype;\n\t\t}\n\n\t\tstmt->dicts = lappend(stmt->dicts, get_ts_dict_namelist(cfgmap->mapdict));\n\t}\n\n\t/*\n\t * If we have ran atleast 1 iteration above we have the last stmt not added to the\n\t * stmts list.\n\t */\n\tif (stmt != NULL)\n\t{\n\t\tstmts = lappend(stmts, stmt);\n\t\tstmt = NULL;\n\t}\n\n\tsystable_endscan_ordered(mapscan);\n\tindex_close(mapidx, NoLock);\n\ttable_close(maprel, NoLock);\n\n\treturn stmts;\n}\n\n\n/*\n * GetTextSearchConfigOwnerStmts returns a potentially empty list of statements to change\n * the ownership of a TEXT SEARCH CONFIGURATION object.\n *\n * The list is for convenience when building a full list of statements to recreate the\n * configuration.\n */\nstatic List *\nGetTextSearchConfigOwnerStmts(Oid tsconfigOid)\n{\n\tHeapTuple tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(tsconfigOid));\n\tif (!HeapTupleIsValid(tup)) /* should not happen */\n\t{\n\t\telog(ERROR, \"cache lookup failed for text search configuration %u\",\n\t\t\t tsconfigOid);\n\t}\n\tForm_pg_ts_config config = (Form_pg_ts_config) GETSTRUCT(tup);\n\n\tAlterOwnerStmt *stmt = makeNode(AlterOwnerStmt);\n\tstmt->objectType = OBJECT_TSCONFIGURATION;\n\tstmt->object = (Node *) get_ts_config_namelist(tsconfigOid);\n\tstmt->newowner = GetRoleSpecObjectForUser(config->cfgowner);\n\n\tReleaseSysCache(tup);\n\treturn list_make1(stmt);\n}\n\n\n/*\n * GetTextSearchDictionaryOwnerStmts returns a potentially empty list of statements to change\n * the ownership of a TEXT SEARCH DICTIONARY object.\n *\n * The list is for convenience when building a full list of statements to recreate the\n * dictionary.\n */\nstatic List *\nGetTextSearchDictionaryOwnerStmts(Oid tsdictOid)\n{\n\tHeapTuple tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(tsdictOid));\n\tif (!HeapTupleIsValid(tup)) /* should not happen */\n\t{\n\t\telog(ERROR, \"cache lookup failed for text search dictionary %u\",\n\t\t\t tsdictOid);\n\t}\n\tForm_pg_ts_dict dict = (Form_pg_ts_dict) GETSTRUCT(tup);\n\n\tAlterOwnerStmt *stmt = makeNode(AlterOwnerStmt);\n\tstmt->objectType = OBJECT_TSDICTIONARY;\n\tstmt->object = (Node *) get_ts_dict_namelist(tsdictOid);\n\tstmt->newowner = GetRoleSpecObjectForUser(dict->dictowner);\n\n\tReleaseSysCache(tup);\n\treturn list_make1(stmt);\n}\n\n\n/*\n * get_ts_config_namelist based on the tsconfigOid this function creates the namelist that\n * identifies the configuration in a fully qualified manner, irregardless of the schema\n * existing on the search_path.\n */\nList *\nget_ts_config_namelist(Oid tsconfigOid)\n{\n\tHeapTuple tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(tsconfigOid));\n\tif (!HeapTupleIsValid(tup)) /* should not happen */\n\t{\n\t\telog(ERROR, \"cache lookup failed for text search configuration %u\",\n\t\t\t tsconfigOid);\n\t}\n\tForm_pg_ts_config config = (Form_pg_ts_config) GETSTRUCT(tup);\n\n\tchar *schema = get_namespace_name(config->cfgnamespace);\n\tchar *configName = pstrdup(NameStr(config->cfgname));\n\tList *names = list_make2(makeString(schema), makeString(configName));\n\n\tReleaseSysCache(tup);\n\treturn names;\n}\n\n\n/*\n * get_ts_dict_namelist based on the tsdictOid this function creates the namelist that\n * identifies the dictionary in a fully qualified manner, irregardless of the schema\n * existing on the search_path.\n */\nstatic List *\nget_ts_dict_namelist(Oid tsdictOid)\n{\n\tHeapTuple tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(tsdictOid));\n\tif (!HeapTupleIsValid(tup)) /* should not happen */\n\t{\n\t\telog(ERROR, \"cache lookup failed for text search dictionary %u\", tsdictOid);\n\t}\n\tForm_pg_ts_dict dict = (Form_pg_ts_dict) GETSTRUCT(tup);\n\n\tchar *schema = get_namespace_name(dict->dictnamespace);\n\tchar *dictName = pstrdup(NameStr(dict->dictname));\n\tList *names = list_make2(makeString(schema), makeString(dictName));\n\n\tReleaseSysCache(tup);\n\treturn names;\n}\n\n\n/*\n * get_ts_template_namelist based on the tstemplateOid this function creates the namelist\n * that identifies the template in a fully qualified manner, irregardless of the schema\n * existing on the search_path.\n */\nstatic List *\nget_ts_template_namelist(Oid tstemplateOid)\n{\n\tHeapTuple tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tstemplateOid));\n\tif (!HeapTupleIsValid(tup)) /* should not happen */\n\t{\n\t\telog(ERROR, \"cache lookup failed for text search template %u\", tstemplateOid);\n\t}\n\tForm_pg_ts_template template = (Form_pg_ts_template) GETSTRUCT(tup);\n\n\tchar *schema = get_namespace_name(template->tmplnamespace);\n\tchar *templateName = pstrdup(NameStr(template->tmplname));\n\tList *names = list_make2(makeString(schema), makeString(templateName));\n\n\tReleaseSysCache(tup);\n\treturn names;\n}\n\n\n/*\n * get_ts_config_parser_oid based on the tsconfigOid this function returns the Oid of the\n * parser used in the configuration.\n */\nstatic Oid\nget_ts_config_parser_oid(Oid tsconfigOid)\n{\n\tHeapTuple tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(tsconfigOid));\n\tif (!HeapTupleIsValid(tup)) /* should not happen */\n\t{\n\t\telog(ERROR, \"cache lookup failed for text search configuration %u\", tsconfigOid);\n\t}\n\tForm_pg_ts_config config = (Form_pg_ts_config) GETSTRUCT(tup);\n\tOid parserOid = config->cfgparser;\n\n\tReleaseSysCache(tup);\n\treturn parserOid;\n}\n\n\n/*\n * get_ts_parser_tokentype_name returns the name of the token as known to the parser by\n * its tokentype identifier. The parser used to resolve the token name is identified by\n * parserOid and should be the same that emitted the tokentype to begin with.\n */\nstatic char *\nget_ts_parser_tokentype_name(Oid parserOid, int32 tokentype)\n{\n\tTSParserCacheEntry *parserCache = lookup_ts_parser_cache(parserOid);\n\tif (!OidIsValid(parserCache->lextypeOid))\n\t{\n\t\telog(ERROR, \"method lextype isn't defined for text search parser %u\", parserOid);\n\t}\n\n\t/* take lextypes from parser */\n\tLexDescr *tokenlist = (LexDescr *) DatumGetPointer(\n\t\tOidFunctionCall1(parserCache->lextypeOid, Int32GetDatum(0)));\n\n\t/* and find the one with lexid = tokentype */\n\tint tokenIndex = 0;\n\twhile (tokenlist && tokenlist[tokenIndex].lexid)\n\t{\n\t\tif (tokenlist[tokenIndex].lexid == tokentype)\n\t\t{\n\t\t\treturn pstrdup(tokenlist[tokenIndex].alias);\n\t\t}\n\t\ttokenIndex++;\n\t}\n\n\t/* we haven't found the token */\n\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\terrmsg(\"token type \\\"%d\\\" does not exist in parser\", tokentype)));\n}\n\n\n/*\n * get_ts_parser_namelist based on the tsparserOid this function creates the namelist that\n * identifies the parser in a fully qualified manner, irregardless of the schema existing\n * on the search_path.\n */\nstatic List *\nget_ts_parser_namelist(Oid tsparserOid)\n{\n\tHeapTuple tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(tsparserOid));\n\tif (!HeapTupleIsValid(tup)) /* should not happen */\n\t{\n\t\telog(ERROR, \"cache lookup failed for text search parser %u\",\n\t\t\t tsparserOid);\n\t}\n\tForm_pg_ts_parser parser = (Form_pg_ts_parser) GETSTRUCT(tup);\n\n\tchar *schema = get_namespace_name(parser->prsnamespace);\n\tchar *parserName = pstrdup(NameStr(parser->prsname));\n\tList *names = list_make2(makeString(schema), makeString(parserName));\n\n\tReleaseSysCache(tup);\n\treturn names;\n}\n\n\n/*\n * CreateTextSearchConfigurationObjectAddress resolves the ObjectAddress for the object\n * being created. If missing_pk is false the function will error, explaining to the user\n * the text search configuration described in the statement doesn't exist.\n */\nList *\nCreateTextSearchConfigurationObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t   isPostprocess)\n{\n\tDefineStmt *stmt = castNode(DefineStmt, node);\n\tAssert(stmt->kind == OBJECT_TSCONFIGURATION);\n\n\tOid objid = get_ts_config_oid(stmt->defnames, missing_ok);\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TSConfigRelationId, objid);\n\treturn list_make1(address);\n}\n\n\n/*\n * CreateTextSearchDictObjectAddress resolves the ObjectAddress for the object\n * being created. If missing_pk is false the function will error, explaining to the user\n * the text search dictionary described in the statement doesn't exist.\n */\nList *\nCreateTextSearchDictObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tDefineStmt *stmt = castNode(DefineStmt, node);\n\tAssert(stmt->kind == OBJECT_TSDICTIONARY);\n\n\tOid objid = get_ts_dict_oid(stmt->defnames, missing_ok);\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TSDictionaryRelationId, objid);\n\treturn list_make1(address);\n}\n\n\n/*\n * RenameTextSearchConfigurationStmtObjectAddress resolves the ObjectAddress for the TEXT\n * SEARCH CONFIGURATION being renamed. Optionally errors if the configuration does not\n * exist based on the missing_ok flag passed in by the caller.\n */\nList *\nRenameTextSearchConfigurationStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t   isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_TSCONFIGURATION);\n\n\tOid objid = get_ts_config_oid(castNode(List, stmt->object), missing_ok);\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TSConfigRelationId, objid);\n\treturn list_make1(address);\n}\n\n\n/*\n * RenameTextSearchDictionaryStmtObjectAddress resolves the ObjectAddress for the TEXT\n * SEARCH DICTIONARY being renamed. Optionally errors if the dictionary does not\n * exist based on the missing_ok flag passed in by the caller.\n */\nList *\nRenameTextSearchDictionaryStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\tisPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_TSDICTIONARY);\n\n\tOid objid = get_ts_dict_oid(castNode(List, stmt->object), missing_ok);\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TSDictionaryRelationId, objid);\n\treturn list_make1(address);\n}\n\n\n/*\n * AlterTextSearchConfigurationStmtObjectAddress resolves the ObjectAddress for the TEXT\n * SEARCH CONFIGURATION being altered. Optionally errors if the configuration does not\n * exist based on the missing_ok flag passed in by the caller.\n */\nList *\nAlterTextSearchConfigurationStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t  isPostprocess)\n{\n\tAlterTSConfigurationStmt *stmt = castNode(AlterTSConfigurationStmt, node);\n\n\tOid objid = get_ts_config_oid(stmt->cfgname, missing_ok);\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TSConfigRelationId, objid);\n\treturn list_make1(address);\n}\n\n\n/*\n * AlterTextSearchDictionaryStmtObjectAddress resolves the ObjectAddress for the TEXT\n * SEARCH CONFIGURATION being altered. Optionally errors if the configuration does not\n * exist based on the missing_ok flag passed in by the caller.\n */\nList *\nAlterTextSearchDictionaryStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t   isPostprocess)\n{\n\tAlterTSDictionaryStmt *stmt = castNode(AlterTSDictionaryStmt, node);\n\n\tOid objid = get_ts_dict_oid(stmt->dictname, missing_ok);\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TSDictionaryRelationId, objid);\n\treturn list_make1(address);\n}\n\n\n/*\n * AlterTextSearchConfigurationSchemaStmtObjectAddress resolves the ObjectAddress for the\n * TEXT SEARCH CONFIGURATION being moved to a different schema. Optionally errors if the\n * configuration does not exist based on the missing_ok flag passed in by the caller.\n *\n * This can be called, either before or after the move of schema has been executed, hence\n * the triple checking before the error might be thrown. Errors for non-existing schema's\n * in edgecases will be raised by postgres while executing the move.\n */\nList *\nAlterTextSearchConfigurationSchemaStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\tisPostprocess)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TSCONFIGURATION);\n\n\tOid objid = get_ts_config_oid(castNode(List, stmt->object), true);\n\n\tif (!OidIsValid(objid))\n\t{\n\t\t/*\n\t\t * couldn't find the text search configuration, might have already been moved to\n\t\t * the new schema, we construct a new sequence name that uses the new schema to\n\t\t * search in.\n\t\t */\n\t\tchar *schemaname = NULL;\n\t\tchar *config_name = NULL;\n\t\tDeconstructQualifiedName(castNode(List, stmt->object), &schemaname, &config_name);\n\n\t\tchar *newSchemaName = stmt->newschema;\n\t\tList *names = list_make2(makeString(newSchemaName), makeString(config_name));\n\t\tobjid = get_ts_config_oid(names, true);\n\n\t\tif (!missing_ok && !OidIsValid(objid))\n\t\t{\n\t\t\t/*\n\t\t\t * if the text search config id is still invalid we couldn't find it, error\n\t\t\t * with the same message postgres would error with if missing_ok is false\n\t\t\t * (not ok to miss)\n\t\t\t */\n\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t errmsg(\"text search configuration \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\tNameListToString(castNode(List, stmt->object)))));\n\t\t}\n\t}\n\n\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*sequenceAddress, TSConfigRelationId, objid);\n\treturn list_make1(sequenceAddress);\n}\n\n\n/*\n * AlterTextSearchDictionarySchemaStmtObjectAddress resolves the ObjectAddress for the\n * TEXT SEARCH DICTIONARY being moved to a different schema. Optionally errors if the\n * dictionary does not exist based on the missing_ok flag passed in by the caller.\n *\n * This can be called, either before or after the move of schema has been executed, hence\n * the triple checking before the error might be thrown. Errors for non-existing schema's\n * in edgecases will be raised by postgres while executing the move.\n */\nList *\nAlterTextSearchDictionarySchemaStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t isPostprocess)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TSDICTIONARY);\n\n\tOid objid = get_ts_dict_oid(castNode(List, stmt->object), true);\n\n\tif (!OidIsValid(objid))\n\t{\n\t\t/*\n\t\t * couldn't find the text search dictionary, might have already been moved to\n\t\t * the new schema, we construct a new sequence name that uses the new schema to\n\t\t * search in.\n\t\t */\n\t\tchar *schemaname = NULL;\n\t\tchar *dict_name = NULL;\n\t\tDeconstructQualifiedName(castNode(List, stmt->object), &schemaname, &dict_name);\n\n\t\tchar *newSchemaName = stmt->newschema;\n\t\tList *names = list_make2(makeString(newSchemaName), makeString(dict_name));\n\t\tobjid = get_ts_dict_oid(names, true);\n\n\t\tif (!missing_ok && !OidIsValid(objid))\n\t\t{\n\t\t\t/*\n\t\t\t * if the text search dict id is still invalid we couldn't find it, error\n\t\t\t * with the same message postgres would error with if missing_ok is false\n\t\t\t * (not ok to miss)\n\t\t\t */\n\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t errmsg(\"text search dictionary \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\tNameListToString(castNode(List, stmt->object)))));\n\t\t}\n\t}\n\n\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*sequenceAddress, TSDictionaryRelationId, objid);\n\treturn list_make1(sequenceAddress);\n}\n\n\n/*\n * AlterTextSearchConfigurationOwnerObjectAddress resolves the ObjectAddress for the TEXT\n * SEARCH CONFIGURATION for which the owner is changed. Optionally errors if the\n * configuration does not exist based on the missing_ok flag passed in by the caller.\n */\nList *\nAlterTextSearchConfigurationOwnerObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t   isPostprocess)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tRelation relation = NULL;\n\n\tAssert(stmt->objectType == OBJECT_TSCONFIGURATION);\n\n\tObjectAddress objectAddress = get_object_address(stmt->objectType, stmt->object,\n\t\t\t\t\t\t\t\t\t\t\t\t\t &relation, AccessShareLock,\n\t\t\t\t\t\t\t\t\t\t\t\t\t missing_ok);\n\n\tObjectAddress *objectAddressCopy = palloc0(sizeof(ObjectAddress));\n\t*objectAddressCopy = objectAddress;\n\n\treturn list_make1(objectAddressCopy);\n}\n\n\n/*\n * AlterTextSearchDictOwnerObjectAddress resolves the ObjectAddress for the TEXT\n * SEARCH DICTIONARY for which the owner is changed. Optionally errors if the\n * configuration does not exist based on the missing_ok flag passed in by the caller.\n */\nList *\nAlterTextSearchDictOwnerObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tRelation relation = NULL;\n\n\tAssert(stmt->objectType == OBJECT_TSDICTIONARY);\n\tObjectAddress objectAddress = get_object_address(stmt->objectType, stmt->object,\n\t\t\t\t\t\t\t\t\t\t\t\t\t &relation, AccessShareLock,\n\t\t\t\t\t\t\t\t\t\t\t\t\t missing_ok);\n\tObjectAddress *objectAddressCopy = palloc0(sizeof(ObjectAddress));\n\t*objectAddressCopy = objectAddress;\n\n\treturn list_make1(objectAddressCopy);\n}\n\n\n/*\n * GenerateBackupNameForTextSearchConfiguration generates a safe name that is not in use\n * already that can be used to rename an existing TEXT SEARCH CONFIGURATION to allow the\n * configuration with a specific name to be created, even if this would not have been\n * possible due to name collisions.\n */\nchar *\nGenerateBackupNameForTextSearchConfiguration(const ObjectAddress *address)\n{\n\tAssert(address->classId == TSConfigRelationId);\n\tList *names = get_ts_config_namelist(address->objectId);\n\n\tRangeVar *rel = makeRangeVarFromNameList(names);\n\n\tchar *newName = palloc0(NAMEDATALEN);\n\tchar suffix[NAMEDATALEN] = { 0 };\n\tchar *baseName = rel->relname;\n\tint baseLength = strlen(baseName);\n\tint count = 0;\n\n\twhile (true)\n\t{\n\t\tint suffixLength = SafeSnprintf(suffix, NAMEDATALEN - 1, \"(citus_backup_%d)\",\n\t\t\t\t\t\t\t\t\t\tcount);\n\n\t\t/* trim the base name at the end to leave space for the suffix and trailing \\0 */\n\t\tbaseLength = Min(baseLength, NAMEDATALEN - suffixLength - 1);\n\n\t\t/* clear newName before copying the potentially trimmed baseName and suffix */\n\t\tmemset(newName, 0, NAMEDATALEN);\n\t\tstrncpy_s(newName, NAMEDATALEN, baseName, baseLength);\n\t\tstrncpy_s(newName + baseLength, NAMEDATALEN - baseLength, suffix,\n\t\t\t\t  suffixLength);\n\n\n\t\trel->relname = newName;\n\t\tList *newNameList = MakeNameListFromRangeVar(rel);\n\n\t\tOid tsconfigOid = get_ts_config_oid(newNameList, true);\n\t\tif (!OidIsValid(tsconfigOid))\n\t\t{\n\t\t\treturn newName;\n\t\t}\n\n\t\tcount++;\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/trigger.c",
    "content": "/*-------------------------------------------------------------------------\n * trigger.c\n *\n * This file contains functions to create and process trigger objects on\n * citus tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"access/genam.h\"\n#include \"access/table.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_trigger.h\"\n#include \"commands/extension.h\"\n#include \"commands/trigger.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/fmgrprotos.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"distributed/shard_utils.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/* appropriate lock modes for the owner relation according to postgres */\n#define CREATE_TRIGGER_LOCK_MODE ShareRowExclusiveLock\n#define ALTER_TRIGGER_LOCK_MODE AccessExclusiveLock\n#define DROP_TRIGGER_LOCK_MODE AccessExclusiveLock\n\n\n/* local function forward declarations */\nstatic char * GetAlterTriggerStateCommand(Oid triggerId);\nstatic bool IsCreateCitusTruncateTriggerStmt(CreateTrigStmt *createTriggerStmt);\nstatic String * GetAlterTriggerDependsTriggerNameValue(AlterObjectDependsStmt *\n\t\t\t\t\t\t\t\t\t\t\t\t\t   alterTriggerDependsStmt);\nstatic void ErrorIfUnsupportedDropTriggerCommand(DropStmt *dropTriggerStmt);\nstatic RangeVar * GetDropTriggerStmtRelation(DropStmt *dropTriggerStmt);\nstatic void ExtractDropStmtTriggerAndRelationName(DropStmt *dropTriggerStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t  char **triggerName,\n\t\t\t\t\t\t\t\t\t\t\t\t  char **relationName);\nstatic void ErrorIfDropStmtDropsMultipleTriggers(DropStmt *dropTriggerStmt);\nstatic char * GetTriggerNameById(Oid triggerId);\nstatic int16 GetTriggerTypeById(Oid triggerId);\n\n\n/* GUC that overrides trigger checks for distributed tables and reference tables */\nbool EnableUnsafeTriggers = false;\n\n\n/*\n * GetExplicitTriggerCommandList returns the list of DDL commands to create\n * triggers that are explicitly created for the table with relationId. See\n * comment of GetExplicitTriggerIdList function.\n */\nList *\nGetExplicitTriggerCommandList(Oid relationId)\n{\n\tList *createTriggerCommandList = NIL;\n\n\tint saveNestLevel = PushEmptySearchPath();\n\n\tList *triggerIdList = GetExplicitTriggerIdList(relationId);\n\n\tOid triggerId = InvalidOid;\n\tforeach_declared_oid(triggerId, triggerIdList)\n\t{\n\t\tbool prettyOutput = false;\n\t\tDatum commandText = DirectFunctionCall2(pg_get_triggerdef_ext,\n\t\t\t\t\t\t\t\t\t\t\t\tObjectIdGetDatum(triggerId),\n\t\t\t\t\t\t\t\t\t\t\t\tBoolGetDatum(prettyOutput));\n\n\t\t/*\n\t\t * pg_get_triggerdef_ext doesn't throw an error if there is no such\n\t\t * trigger, be on the safe side.\n\t\t */\n\t\tif (DatumGetPointer(commandText) == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"trigger with oid %u does not exist\",\n\t\t\t\t\t\t\t\t   triggerId)));\n\t\t}\n\n\t\tchar *createTriggerCommand = TextDatumGetCString(commandText);\n\n\t\tcreateTriggerCommandList = lappend(\n\t\t\tcreateTriggerCommandList,\n\t\t\tmakeTableDDLCommandString(createTriggerCommand));\n\n\t\t/*\n\t\t * Appends the commands for the trigger settings that are not covered\n\t\t * by CREATE TRIGGER command, such as ALTER TABLE ENABLE/DISABLE <trigger>.\n\t\t */\n\n\t\tchar *alterTriggerStateCommand =\n\t\t\tGetAlterTriggerStateCommand(triggerId);\n\n\t\tcreateTriggerCommandList = lappend(\n\t\t\tcreateTriggerCommandList,\n\t\t\tmakeTableDDLCommandString(alterTriggerStateCommand));\n\t}\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n\n\treturn createTriggerCommandList;\n}\n\n\n/*\n * GetAlterTriggerStateCommand returns the DDL command to set enable/disable\n * state for given trigger. Throws an error if no such trigger exists.\n */\nstatic char *\nGetAlterTriggerStateCommand(Oid triggerId)\n{\n\tStringInfo alterTriggerStateCommand = makeStringInfo();\n\n\tbool missingOk = false;\n\tHeapTuple triggerTuple = GetTriggerTupleById(triggerId, missingOk);\n\n\tForm_pg_trigger triggerForm = (Form_pg_trigger) GETSTRUCT(triggerTuple);\n\n\tchar *qualifiedRelName = generate_qualified_relation_name(triggerForm->tgrelid);\n\tconst char *quotedTrigName = quote_identifier(NameStr(triggerForm->tgname));\n\tchar enableDisableState = triggerForm->tgenabled;\n\n\tconst char *alterTriggerStateStr = NULL;\n\tswitch (enableDisableState)\n\t{\n\t\tcase TRIGGER_FIRES_ON_ORIGIN:\n\t\t{\n\t\t\t/* default mode */\n\t\t\talterTriggerStateStr = \"ENABLE\";\n\t\t\tbreak;\n\t\t}\n\n\t\tcase TRIGGER_FIRES_ALWAYS:\n\t\t{\n\t\t\talterTriggerStateStr = \"ENABLE ALWAYS\";\n\t\t\tbreak;\n\t\t}\n\n\t\tcase TRIGGER_FIRES_ON_REPLICA:\n\t\t{\n\t\t\talterTriggerStateStr = \"ENABLE REPLICA\";\n\t\t\tbreak;\n\t\t}\n\n\t\tcase TRIGGER_DISABLED:\n\t\t{\n\t\t\talterTriggerStateStr = \"DISABLE\";\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"unexpected trigger state\");\n\t\t}\n\t}\n\n\tappendStringInfo(alterTriggerStateCommand, \"ALTER TABLE %s %s TRIGGER %s;\",\n\t\t\t\t\t qualifiedRelName, alterTriggerStateStr, quotedTrigName);\n\n\t/*\n\t * Free triggerTuple at the end since quote_identifier() might not return\n\t * a palloc'd string if given identifier doesn't need to be quoted, and in\n\t * that case quotedTrigName would still be bound to triggerTuple.\n\t */\n\theap_freetuple(triggerTuple);\n\n\treturn alterTriggerStateCommand->data;\n}\n\n\n/*\n * GetTriggerTupleById returns copy of the heap tuple from pg_trigger for\n * the trigger with triggerId. If no such trigger exists, this function returns\n * NULL or errors out depending on missingOk.\n */\nHeapTuple\nGetTriggerTupleById(Oid triggerId, bool missingOk)\n{\n\tRelation pgTrigger = table_open(TriggerRelationId, AccessShareLock);\n\n\tint scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\n\tAttrNumber attrNumber = Anum_pg_trigger_oid;\n\n\tScanKeyInit(&scanKey[0], attrNumber, BTEqualStrategyNumber,\n\t\t\t\tF_OIDEQ, ObjectIdGetDatum(triggerId));\n\n\tbool useIndex = true;\n\tSysScanDesc scanDescriptor = systable_beginscan(pgTrigger, TriggerOidIndexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tuseIndex, NULL, scanKeyCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\tscanKey);\n\n\tHeapTuple targetHeapTuple = NULL;\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\ttargetHeapTuple = heap_copytuple(heapTuple);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgTrigger, NoLock);\n\n\tif (targetHeapTuple == NULL && missingOk == false)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find heap tuple for trigger with \"\n\t\t\t\t\t\t\t   \"OID %d\", triggerId)));\n\t}\n\n\treturn targetHeapTuple;\n}\n\n\n/*\n * GetExplicitTriggerIdList returns a list of OIDs corresponding to the triggers\n * that are explicitly created on the relation with relationId. That means,\n * this function discards internal triggers implicitly created by postgres for\n * foreign key constraint validation and the citus_truncate_trigger.\n */\nList *\nGetExplicitTriggerIdList(Oid relationId)\n{\n\tList *triggerIdList = NIL;\n\n\tRelation pgTrigger = table_open(TriggerRelationId, AccessShareLock);\n\n\tint scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\n\tScanKeyInit(&scanKey[0], Anum_pg_trigger_tgrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));\n\n\tbool useIndex = true;\n\tSysScanDesc scanDescriptor = systable_beginscan(pgTrigger, TriggerRelidNameIndexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tuseIndex, NULL, scanKeyCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\tscanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tForm_pg_trigger triggerForm = (Form_pg_trigger) GETSTRUCT(heapTuple);\n\n\t\t/*\n\t\t * Note that we mark truncate trigger that we create on citus tables as\n\t\t * internal. Hence, below we discard citus_truncate_trigger as well as\n\t\t * the implicit triggers created by postgres for foreign key validation.\n\t\t *\n\t\t * Pre PG15, tgisinternal is true for a \"child\" trigger on a partition\n\t\t * cloned from the trigger on the parent.\n\t\t * In PG15, tgisinternal is false in that case. However, we don't want to\n\t\t * create this trigger on the partition since it will create a conflict\n\t\t * when we try to attach the partition to the parent table:\n\t\t * ERROR: trigger \"...\" for relation \"{partition_name}\" already exists\n\t\t * Hence we add an extra check on whether the parent id is invalid to\n\t\t * make sure this is not a child trigger\n\t\t */\n\t\tif (!triggerForm->tgisinternal && (triggerForm->tgparentid == InvalidOid))\n\t\t{\n\t\t\ttriggerIdList = lappend_oid(triggerIdList, triggerForm->oid);\n\t\t}\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgTrigger, NoLock);\n\n\treturn triggerIdList;\n}\n\n\n/*\n * PostprocessCreateTriggerStmt is called after a CREATE TRIGGER command has\n * been executed by standard process utility. This function errors out for\n * unsupported commands or creates ddl job for supported CREATE TRIGGER commands.\n */\nList *\nPostprocessCreateTriggerStmt(Node *node, const char *queryString)\n{\n\tCreateTrigStmt *createTriggerStmt = castNode(CreateTrigStmt, node);\n\tif (IsCreateCitusTruncateTriggerStmt(createTriggerStmt))\n\t{\n\t\treturn NIL;\n\t}\n\n\tRangeVar *relation = createTriggerStmt->relation;\n\tbool missingOk = false;\n\tOid relationId = RangeVarGetRelid(relation, CREATE_TRIGGER_LOCK_MODE, missingOk);\n\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tErrorOutForTriggerIfNotSupported(relationId);\n\n\tList *objectAddresses = GetObjectAddressListFromParseTree(node, missingOk, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(objectAddresses) == 1);\n\n\tEnsureAllObjectDependenciesExistOnAllNodes(objectAddresses);\n\n\tchar *triggerName = createTriggerStmt->trigname;\n\treturn CitusCreateTriggerCommandDDLJob(relationId, triggerName,\n\t\t\t\t\t\t\t\t\t\t   queryString);\n}\n\n\n/*\n * CreateTriggerStmtObjectAddress finds the ObjectAddress for the trigger that\n * is created by given CreateTriggerStmt. If missingOk is false and if trigger\n * does not exist, then it errors out.\n *\n * Never returns NULL, but the objid in the address can be invalid if missingOk\n * was set to true.\n */\nList *\nCreateTriggerStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess)\n{\n\tCreateTrigStmt *createTriggerStmt = castNode(CreateTrigStmt, node);\n\n\tRangeVar *relation = createTriggerStmt->relation;\n\tOid relationId = RangeVarGetRelid(relation, CREATE_TRIGGER_LOCK_MODE, missingOk);\n\n\tchar *triggerName = createTriggerStmt->trigname;\n\tOid triggerId = get_trigger_oid(relationId, triggerName, missingOk);\n\n\tif (triggerId == InvalidOid && missingOk == false)\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t\terrmsg(\"trigger \\\"%s\\\" on relation \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t   triggerName, relationName)));\n\t}\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TriggerRelationId, triggerId);\n\treturn list_make1(address);\n}\n\n\n/*\n * IsCreateCitusTruncateTriggerStmt returns true if given createTriggerStmt\n * creates citus_truncate_trigger.\n */\nstatic bool\nIsCreateCitusTruncateTriggerStmt(CreateTrigStmt *createTriggerStmt)\n{\n\tList *functionNameList = createTriggerStmt->funcname;\n\tRangeVar *functionRangeVar = makeRangeVarFromNameList(functionNameList);\n\tchar *functionName = functionRangeVar->relname;\n\tif (strncmp(functionName, CITUS_TRUNCATE_TRIGGER_NAME, NAMEDATALEN) == 0)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CreateTriggerEventExtendNames extends relation name and trigger name with\n * shardId, and sets schema name in given CreateTrigStmt.\n */\nvoid\nCreateTriggerEventExtendNames(CreateTrigStmt *createTriggerStmt, char *schemaName,\n\t\t\t\t\t\t\t  uint64 shardId)\n{\n\tRangeVar *relation = createTriggerStmt->relation;\n\n\tchar **relationName = &(relation->relname);\n\tAppendShardIdToName(relationName, shardId);\n\n\tchar **triggerName = &(createTriggerStmt->trigname);\n\tAppendShardIdToName(triggerName, shardId);\n\n\tchar **relationSchemaName = &(relation->schemaname);\n\tSetSchemaNameIfNotExist(relationSchemaName, schemaName);\n}\n\n\n/*\n * PostprocessAlterTriggerRenameStmt is called after a ALTER TRIGGER RENAME\n * command has been executed by standard process utility. This function errors\n * out for unsupported commands or creates ddl job for supported ALTER TRIGGER\n * RENAME commands.\n */\nList *\nPostprocessAlterTriggerRenameStmt(Node *node, const char *queryString)\n{\n\tRenameStmt *renameTriggerStmt = castNode(RenameStmt, node);\n\tAssert(renameTriggerStmt->renameType == OBJECT_TRIGGER);\n\n\tRangeVar *relation = renameTriggerStmt->relation;\n\n\tbool missingOk = false;\n\tOid relationId = RangeVarGetRelid(relation, ALTER_TRIGGER_LOCK_MODE, missingOk);\n\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tErrorOutForTriggerIfNotSupported(relationId);\n\n\t/* use newname as standard process utility already renamed it */\n\tchar *triggerName = renameTriggerStmt->newname;\n\treturn CitusCreateTriggerCommandDDLJob(relationId, triggerName,\n\t\t\t\t\t\t\t\t\t\t   queryString);\n}\n\n\n/*\n * AlterTriggerRenameEventExtendNames extends relation name, old and new trigger\n * name with shardId, and sets schema name in given RenameStmt.\n */\nvoid\nAlterTriggerRenameEventExtendNames(RenameStmt *renameTriggerStmt, char *schemaName,\n\t\t\t\t\t\t\t\t   uint64 shardId)\n{\n\tAssert(renameTriggerStmt->renameType == OBJECT_TRIGGER);\n\n\tRangeVar *relation = renameTriggerStmt->relation;\n\n\tchar **relationName = &(relation->relname);\n\tAppendShardIdToName(relationName, shardId);\n\n\tchar **triggerOldName = &(renameTriggerStmt->subname);\n\tAppendShardIdToName(triggerOldName, shardId);\n\n\tchar **triggerNewName = &(renameTriggerStmt->newname);\n\tAppendShardIdToName(triggerNewName, shardId);\n\n\tchar **relationSchemaName = &(relation->schemaname);\n\tSetSchemaNameIfNotExist(relationSchemaName, schemaName);\n}\n\n\n/*\n * PreprocessAlterTriggerDependsStmt is called during the planning phase of an\n * ALTER TRIGGER ... DEPENDS ON EXTENSION ... statement. Since triggers depending on\n * extensions are assumed to be Owned by an extension we assume the extension to keep\n * the trigger in sync.\n *\n * If we would allow users to create a dependency between a distributed trigger and an\n * extension, our pruning logic for which objects to distribute as dependencies of other\n * objects will change significantly, which could cause issues adding new workers. Hence\n * we don't allow this dependency to be created.\n */\nList *\nPreprocessAlterTriggerDependsStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tAlterObjectDependsStmt *alterTriggerDependsStmt =\n\t\tcastNode(AlterObjectDependsStmt, node);\n\tAssert(alterTriggerDependsStmt->objectType == OBJECT_TRIGGER);\n\n\tif (creating_extension)\n\t{\n\t\t/*\n\t\t * extensions should be created separately on the workers, triggers cascading\n\t\t * from an extension should therefore not be propagated here.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tif (!EnableMetadataSync)\n\t{\n\t\t/*\n\t\t * we are configured to disable object propagation, should not propagate anything\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tRangeVar *relation = alterTriggerDependsStmt->relation;\n\n\tbool missingOk = false;\n\tOid relationId = RangeVarGetRelid(relation, ALTER_TRIGGER_LOCK_MODE, missingOk);\n\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * Distributed objects should not start depending on an extension, this will break\n\t * the dependency resolving mechanism we use to replicate distributed objects to new\n\t * workers\n\t */\n\n\tString *triggerNameValue =\n\t\tGetAlterTriggerDependsTriggerNameValue(alterTriggerDependsStmt);\n\n\tereport(ERROR, (errmsg(\"trigger \\\"%s\\\" depends on an extension and this \"\n\t\t\t\t\t\t   \"is not supported for distributed tables and \"\n\t\t\t\t\t\t   \"local tables added to metadata\",\n\t\t\t\t\t\t   strVal(triggerNameValue)),\n\t\t\t\t\terrdetail(\"Triggers from extensions are expected to be \"\n\t\t\t\t\t\t\t  \"created on the workers by the extension they \"\n\t\t\t\t\t\t\t  \"depend on.\")));\n\n\t/* not reachable, keep compiler happy */\n\treturn NIL;\n}\n\n\n/*\n * PostprocessAlterTriggerDependsStmt is called after a ALTER TRIGGER DEPENDS ON\n * command has been executed by standard process utility. This function errors out\n * for unsupported commands or creates ddl job for supported ALTER TRIGGER DEPENDS\n * ON commands.\n */\nList *\nPostprocessAlterTriggerDependsStmt(Node *node, const char *queryString)\n{\n\tAlterObjectDependsStmt *alterTriggerDependsStmt =\n\t\tcastNode(AlterObjectDependsStmt, node);\n\tAssert(alterTriggerDependsStmt->objectType == OBJECT_TRIGGER);\n\n\tRangeVar *relation = alterTriggerDependsStmt->relation;\n\n\tbool missingOk = false;\n\tOid relationId = RangeVarGetRelid(relation, ALTER_TRIGGER_LOCK_MODE, missingOk);\n\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tErrorOutForTriggerIfNotSupported(relationId);\n\n\tString *triggerNameValue =\n\t\tGetAlterTriggerDependsTriggerNameValue(alterTriggerDependsStmt);\n\treturn CitusCreateTriggerCommandDDLJob(relationId, strVal(triggerNameValue),\n\t\t\t\t\t\t\t\t\t\t   queryString);\n}\n\n\n/*\n * AlterTriggerDependsEventExtendNames extends relation name and trigger name\n * with shardId, and sets schema name in given AlterObjectDependsStmt.\n */\nvoid\nAlterTriggerDependsEventExtendNames(AlterObjectDependsStmt *alterTriggerDependsStmt,\n\t\t\t\t\t\t\t\t\tchar *schemaName, uint64 shardId)\n{\n\tAssert(alterTriggerDependsStmt->objectType == OBJECT_TRIGGER);\n\n\tRangeVar *relation = alterTriggerDependsStmt->relation;\n\n\tchar **relationName = &(relation->relname);\n\tAppendShardIdToName(relationName, shardId);\n\n\tString *triggerNameValue =\n\t\tGetAlterTriggerDependsTriggerNameValue(alterTriggerDependsStmt);\n\tAppendShardIdToName(&strVal(triggerNameValue), shardId);\n\n\tchar **relationSchemaName = &(relation->schemaname);\n\tSetSchemaNameIfNotExist(relationSchemaName, schemaName);\n}\n\n\n/*\n * GetAlterTriggerDependsTriggerName returns Value object for the trigger\n * name that given AlterObjectDependsStmt is executed for.\n */\nstatic String *\nGetAlterTriggerDependsTriggerNameValue(AlterObjectDependsStmt *alterTriggerDependsStmt)\n{\n\tList *triggerObjectNameList = (List *) alterTriggerDependsStmt->object;\n\n\t/*\n\t * Before standard process utility, we only have trigger name in \"object\"\n\t * list. However, standard process utility prepends that list with the\n\t * relationNameList retrieved from AlterObjectDependsStmt->RangeVar and\n\t * we call this method after standard process utility. So, for the further\n\t * usages, it is certain that the last element in \"object\" list will always\n\t * be the name of the trigger in either before or after standard process\n\t * utility.\n\t */\n\tString *triggerNameValue = llast(triggerObjectNameList);\n\treturn triggerNameValue;\n}\n\n\n/*\n * PreprocessDropTriggerStmt is called before a DROP TRIGGER command has been\n * executed by standard process utility. This function errors out for\n * unsupported commands or creates ddl job for supported DROP TRIGGER commands.\n * The reason we process drop trigger commands before standard process utility\n * (unlike the other type of trigger commands) is that we act according to trigger\n * type in CitusCreateTriggerCommandDDLJob but trigger wouldn't exist after\n * standard process utility.\n */\nList *\nPreprocessDropTriggerStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tDropStmt *dropTriggerStmt = castNode(DropStmt, node);\n\tAssert(dropTriggerStmt->removeType == OBJECT_TRIGGER);\n\n\tRangeVar *relation = GetDropTriggerStmtRelation(dropTriggerStmt);\n\n\tbool missingOk = true;\n\tOid relationId = RangeVarGetRelid(relation, DROP_TRIGGER_LOCK_MODE, missingOk);\n\n\tif (!OidIsValid(relationId))\n\t{\n\t\t/* let standard process utility to error out */\n\t\treturn NIL;\n\t}\n\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn NIL;\n\t}\n\n\tErrorIfUnsupportedDropTriggerCommand(dropTriggerStmt);\n\n\tchar *triggerName = NULL;\n\tExtractDropStmtTriggerAndRelationName(dropTriggerStmt, &triggerName, NULL);\n\treturn CitusCreateTriggerCommandDDLJob(relationId, triggerName,\n\t\t\t\t\t\t\t\t\t\t   queryString);\n}\n\n\n/*\n * ErrorIfUnsupportedDropTriggerCommand errors out for unsupported\n * \"DROP TRIGGER triggerName ON relationName\" commands.\n */\nstatic void\nErrorIfUnsupportedDropTriggerCommand(DropStmt *dropTriggerStmt)\n{\n\tRangeVar *relation = GetDropTriggerStmtRelation(dropTriggerStmt);\n\n\tbool missingOk = false;\n\tOid relationId = RangeVarGetRelid(relation, DROP_TRIGGER_LOCK_MODE, missingOk);\n\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn;\n\t}\n\n\tEnsureCoordinator();\n\tErrorOutForTriggerIfNotSupported(relationId);\n}\n\n\n/*\n * ErrorOutForTriggerIfNotSupported is a helper function to error\n * out for unsupported trigger commands depending on the citus table type.\n */\nvoid\nErrorOutForTriggerIfNotSupported(Oid relationId)\n{\n\tif (EnableUnsafeTriggers)\n\t{\n\t\t/* user really wants triggers */\n\t\treturn;\n\t}\n\telse if (IsCitusTableType(relationId, REFERENCE_TABLE))\n\t{\n\t\tereport(ERROR, (errmsg(\"triggers are not supported on reference tables\")));\n\t}\n\telse if (IsCitusTableType(relationId, DISTRIBUTED_TABLE))\n\t{\n\t\tereport(ERROR, (errmsg(\"triggers are not supported on distributed tables\")));\n\t}\n\n\t/* we always support triggers on citus local tables */\n}\n\n\n/*\n * ErrorIfRelationHasUnsupportedTrigger throws an error if given relation has\n * a trigger that is not supported by Citus.\n */\nvoid\nErrorIfRelationHasUnsupportedTrigger(Oid relationId)\n{\n\tList *relationTriggerList = GetExplicitTriggerIdList(relationId);\n\n\tOid triggerId = InvalidOid;\n\tforeach_declared_oid(triggerId, relationTriggerList)\n\t{\n\t\tObjectAddress triggerObjectAddress = InvalidObjectAddress;\n\t\tObjectAddressSet(triggerObjectAddress, TriggerRelationId, triggerId);\n\n\t\t/* triggers that depend on extensions are not supported */\n\t\tif (ObjectAddressDependsOnExtension(&triggerObjectAddress))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"trigger \\\"%s\\\" depends on an extension and this \"\n\t\t\t\t\t\t\t\t   \"is not supported for distributed tables and \"\n\t\t\t\t\t\t\t\t   \"local tables added to metadata\",\n\t\t\t\t\t\t\t\t   GetTriggerNameById(triggerId))));\n\t\t}\n\t}\n}\n\n\n/*\n * GetDropTriggerStmtRelation takes a DropStmt for a trigger object and returns\n * RangeVar for the relation that owns the trigger.\n */\nstatic RangeVar *\nGetDropTriggerStmtRelation(DropStmt *dropTriggerStmt)\n{\n\tAssert(dropTriggerStmt->removeType == OBJECT_TRIGGER);\n\n\tErrorIfDropStmtDropsMultipleTriggers(dropTriggerStmt);\n\n\tList *targetObjectList = dropTriggerStmt->objects;\n\tList *triggerObjectNameList = linitial(targetObjectList);\n\n\t/*\n\t * The name list that identifies the trigger to be dropped looks like:\n\t * [catalogName, schemaName, relationName, triggerName], where, the first\n\t * two elements are optional. We should take all elements except the\n\t * triggerName to create the range var object that defines the owner\n\t * relation.\n\t */\n\tint relationNameListLength = list_length(triggerObjectNameList) - 1;\n\tList *relationNameList = list_truncate(list_copy(triggerObjectNameList),\n\t\t\t\t\t\t\t\t\t\t   relationNameListLength);\n\n\treturn makeRangeVarFromNameList(relationNameList);\n}\n\n\n/*\n * DropTriggerEventExtendNames extends relation name and trigger name with\n * shardId, and sets schema name in given DropStmt by recreating \"objects\"\n * list.\n */\nvoid\nDropTriggerEventExtendNames(DropStmt *dropTriggerStmt, char *schemaName, uint64 shardId)\n{\n\tAssert(dropTriggerStmt->removeType == OBJECT_TRIGGER);\n\n\tchar *triggerName = NULL;\n\tchar *relationName = NULL;\n\tExtractDropStmtTriggerAndRelationName(dropTriggerStmt, &triggerName, &relationName);\n\n\tAppendShardIdToName(&triggerName, shardId);\n\tString *triggerNameValue = makeString(triggerName);\n\n\tAppendShardIdToName(&relationName, shardId);\n\tString *relationNameValue = makeString(relationName);\n\n\tString *schemaNameValue = makeString(pstrdup(schemaName));\n\n\tList *shardTriggerNameList =\n\t\tlist_make3(schemaNameValue, relationNameValue, triggerNameValue);\n\tdropTriggerStmt->objects = list_make1(shardTriggerNameList);\n}\n\n\n/*\n * ExtractDropStmtTriggerAndRelationName extracts triggerName and relationName\n * from given dropTriggerStmt if arguments are passed as non-null pointers.\n */\nstatic void\nExtractDropStmtTriggerAndRelationName(DropStmt *dropTriggerStmt, char **triggerName,\n\t\t\t\t\t\t\t\t\t  char **relationName)\n{\n\tErrorIfDropStmtDropsMultipleTriggers(dropTriggerStmt);\n\n\tList *targetObjectList = dropTriggerStmt->objects;\n\tList *triggerObjectNameList = linitial(targetObjectList);\n\tint objectNameListLength = list_length(triggerObjectNameList);\n\n\tif (triggerName != NULL)\n\t{\n\t\tint triggerNameindex = objectNameListLength - 1;\n\t\t*triggerName = strVal(safe_list_nth(triggerObjectNameList, triggerNameindex));\n\t}\n\n\tif (relationName != NULL)\n\t{\n\t\tint relationNameIndex = objectNameListLength - 2;\n\t\t*relationName = strVal(safe_list_nth(triggerObjectNameList, relationNameIndex));\n\t}\n}\n\n\n/*\n * ErrorIfDropStmtDropsMultipleTriggers errors out if given drop trigger\n * command drops more than one trigger. Actually, this can't be the case\n * as postgres doesn't support dropping multiple triggers, but we should\n * be on the safe side.\n */\nstatic void\nErrorIfDropStmtDropsMultipleTriggers(DropStmt *dropTriggerStmt)\n{\n\tList *targetObjectList = dropTriggerStmt->objects;\n\tif (list_length(targetObjectList) > 1)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t\t\terrmsg(\"cannot execute DROP TRIGGER command for multiple \"\n\t\t\t\t\t\t\t   \"triggers\")));\n\t}\n}\n\n\n/*\n * CitusCreateTriggerCommandDDLJob creates a ddl job to execute given\n * queryString trigger command on shell relation(s) in mx worker(s) and to\n * execute necessary ddl task on citus local table shard (if needed).\n */\nList *\nCitusCreateTriggerCommandDDLJob(Oid relationId, char *triggerName,\n\t\t\t\t\t\t\t\tconst char *queryString)\n{\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId);\n\tddlJob->metadataSyncCommand = queryString;\n\n\tif (!triggerName)\n\t{\n\t\t/*\n\t\t * ENABLE/DISABLE TRIGGER ALL/USER commands do not specify trigger\n\t\t * name.\n\t\t */\n\t\tddlJob->taskList = DDLTaskList(relationId, queryString);\n\t\treturn list_make1(ddlJob);\n\t}\n\n\tbool missingOk = true;\n\tOid triggerId = get_trigger_oid(relationId, triggerName, missingOk);\n\tif (!OidIsValid(triggerId))\n\t{\n\t\t/*\n\t\t * For DROP, ENABLE/DISABLE, ENABLE REPLICA/ALWAYS TRIGGER commands,\n\t\t * we create ddl job in preprocess. So trigger may not exist.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tint16 triggerType = GetTriggerTypeById(triggerId);\n\n\t/* we don't have truncate triggers on shard relations */\n\tif (!TRIGGER_FOR_TRUNCATE(triggerType))\n\t{\n\t\tddlJob->taskList = DDLTaskList(relationId, queryString);\n\t}\n\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * GetTriggerNameById returns name of given trigger if such a trigger exists.\n * Otherwise, errors out.\n */\nstatic char *\nGetTriggerNameById(Oid triggerId)\n{\n\tbool missingOk = false;\n\tHeapTuple triggerTuple = GetTriggerTupleById(triggerId, missingOk);\n\n\tForm_pg_trigger triggerForm = (Form_pg_trigger) GETSTRUCT(triggerTuple);\n\tchar *triggerName = pstrdup(NameStr(triggerForm->tgname));\n\theap_freetuple(triggerTuple);\n\n\treturn triggerName;\n}\n\n\n/*\n * GetTriggerTypeById returns trigger type (tgtype) of the trigger identified\n * by triggerId if it exists. Otherwise, errors out.\n */\nstatic int16\nGetTriggerTypeById(Oid triggerId)\n{\n\tbool missingOk = false;\n\tHeapTuple triggerTuple = GetTriggerTupleById(triggerId, missingOk);\n\n\tForm_pg_trigger triggerForm = (Form_pg_trigger) GETSTRUCT(triggerTuple);\n\tint16 triggerType = triggerForm->tgtype;\n\theap_freetuple(triggerTuple);\n\n\treturn triggerType;\n}\n\n\n/*\n * GetTriggerFunctionId returns OID of the function that the trigger with\n * triggerId executes if the trigger exists. Otherwise, errors out.\n */\nOid\nGetTriggerFunctionId(Oid triggerId)\n{\n\tbool missingOk = false;\n\tHeapTuple triggerTuple = GetTriggerTupleById(triggerId, missingOk);\n\n\tForm_pg_trigger triggerForm = (Form_pg_trigger) GETSTRUCT(triggerTuple);\n\tOid functionId = triggerForm->tgfoid;\n\theap_freetuple(triggerTuple);\n\n\treturn functionId;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/truncate.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * truncate.c\n *    Commands for truncating distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_class.h\"\n#include \"commands/tablecmds.h\"\n#include \"commands/trigger.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/regproc.h\"\n#include \"utils/rel.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/foreign_key_relationship.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/worker_shard_visibility.h\"\n#include \"distributed/worker_transaction.h\"\n\n\n/* Local functions forward declarations for unsupported command checks */\nstatic void ErrorIfUnsupportedTruncateStmt(TruncateStmt *truncateStatement);\nstatic void ExecuteTruncateStmtSequentialIfNecessary(TruncateStmt *command);\nstatic void EnsurePartitionTableNotReplicatedForTruncate(TruncateStmt *truncateStatement);\nstatic List * TruncateTaskList(Oid relationId);\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(citus_truncate_trigger);\nPG_FUNCTION_INFO_V1(truncate_local_data_after_distributing_table);\n\nvoid EnsureLocalTableCanBeTruncated(Oid relationId);\n\n\n/*\n * citus_truncate_trigger is called as a trigger when a distributed\n * table is truncated.\n */\nDatum\ncitus_truncate_trigger(PG_FUNCTION_ARGS)\n{\n\tif (!CALLED_AS_TRIGGER(fcinfo))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),\n\t\t\t\t\t\terrmsg(\"must be called as trigger\")));\n\t}\n\n\tTriggerData *triggerData = (TriggerData *) fcinfo->context;\n\tRelation truncatedRelation = triggerData->tg_relation;\n\tOid relationId = RelationGetRelid(truncatedRelation);\n\n\tif (!EnableDDLPropagation)\n\t{\n\t\tPG_RETURN_DATUM(PointerGetDatum(NULL));\n\t}\n\n\t/* we might be truncating multiple relations */\n\tUseCoordinatedTransaction();\n\n\tif (IsCitusTableType(relationId, APPEND_DISTRIBUTED))\n\t{\n\t\tOid schemaId = get_rel_namespace(relationId);\n\t\tchar *schemaName = get_namespace_name(schemaId);\n\t\tchar *relationName = get_rel_name(relationId);\n\t\tbool dropShardsMetadataOnly = false;\n\n\t\tDirectFunctionCall4(citus_drop_all_shards,\n\t\t\t\t\t\t\tObjectIdGetDatum(relationId),\n\t\t\t\t\t\t\tCStringGetTextDatum(schemaName),\n\t\t\t\t\t\t\tCStringGetTextDatum(relationName),\n\t\t\t\t\t\t\tBoolGetDatum(dropShardsMetadataOnly));\n\t}\n\telse\n\t{\n\t\tList *taskList = TruncateTaskList(relationId);\n\n\t\t/*\n\t\t * If it is a local placement of a distributed table or a reference table,\n\t\t * then execute TRUNCATE command locally.\n\t\t */\n\t\tbool localExecutionSupported = true;\n\t\tExecuteUtilityTaskList(taskList, localExecutionSupported);\n\t}\n\n\tPG_RETURN_DATUM(PointerGetDatum(NULL));\n}\n\n\n/*\n * TruncateTaskList returns a list of tasks to execute a TRUNCATE on a\n * distributed table. This is handled separately from other DDL commands\n * because we handle it via the TRUNCATE trigger, which is called whenever\n * a truncate cascades.\n */\nstatic List *\nTruncateTaskList(Oid relationId)\n{\n\t/* resulting task list */\n\tList *taskList = NIL;\n\n\t/* enumerate the tasks when putting them to the taskList */\n\tint taskId = 1;\n\n\tOid schemaId = get_rel_namespace(relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\tchar *relationName = get_rel_name(relationId);\n\n\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\n\t/* lock metadata before getting placement lists */\n\tLockShardListMetadata(shardIntervalList, ShareLock);\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\t\tchar *shardRelationName = pstrdup(relationName);\n\n\t\t/* build shard relation name */\n\t\tAppendShardIdToName(&shardRelationName, shardId);\n\n\t\tchar *quotedShardName = quote_qualified_identifier(schemaName, shardRelationName);\n\n\t\tStringInfo shardQueryString = makeStringInfo();\n\t\tappendStringInfo(shardQueryString, \"TRUNCATE TABLE %s CASCADE\", quotedShardName);\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->jobId = INVALID_JOB_ID;\n\t\ttask->taskId = taskId++;\n\t\ttask->taskType = DDL_TASK;\n\t\tSetTaskQueryString(task, shardQueryString->data);\n\t\ttask->dependentTaskList = NULL;\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\ttask->anchorShardId = shardId;\n\t\ttask->taskPlacementList = ActiveShardPlacementList(shardId);\n\n\t\ttaskList = lappend(taskList, task);\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * truncate_local_data_after_distributing_table truncates the local records of a distributed table.\n *\n * The main advantage of this function is to truncate all local records after creating a\n * distributed table, and prevent constraints from failing due to outdated local records.\n */\nDatum\ntruncate_local_data_after_distributing_table(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tOid relationId = PG_GETARG_OID(0);\n\n\tEnsureLocalTableCanBeTruncated(relationId);\n\n\tTruncateStmt *truncateStmt = makeNode(TruncateStmt);\n\n\tchar *relationName = generate_qualified_relation_name(relationId);\n\tList *names = stringToQualifiedNameList(relationName, NULL);\n\ttruncateStmt->relations = list_make1(makeRangeVarFromNameList(names));\n\ttruncateStmt->restart_seqs = false;\n\ttruncateStmt->behavior = DROP_CASCADE;\n\n\tset_config_option(\"citus.enable_ddl_propagation\", \"false\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n\tExecuteTruncate(truncateStmt);\n\tset_config_option(\"citus.enable_ddl_propagation\", \"true\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * EnsureLocalTableCanBeTruncated performs the necessary checks to make sure it\n * is safe to truncate the local table of a distributed table\n */\nvoid\nEnsureLocalTableCanBeTruncated(Oid relationId)\n{\n\t/* error out if the relation is not a distributed table */\n\tif (!IsCitusTable(relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"supplied parameter is not a distributed relation\"),\n\t\t\t\t\t\terrdetail(\"This UDF only truncates local records of distributed \"\n\t\t\t\t\t\t\t\t  \"tables.\")));\n\t}\n\n\tList *referencingForeignConstaintsFromLocalTables =\n\t\tGetForeignKeysFromLocalTables(relationId);\n\tif (list_length(referencingForeignConstaintsFromLocalTables) > 0)\n\t{\n\t\tOid foreignKeyId = linitial_oid(referencingForeignConstaintsFromLocalTables);\n\t\tOid referencingRelation = GetReferencingTableId(foreignKeyId);\n\t\tchar *referencedRelationName = get_rel_name(relationId);\n\t\tchar *referencingRelationName = get_rel_name(referencingRelation);\n\n\t\tereport(ERROR, (errmsg(\"cannot truncate a table referenced in a \"\n\t\t\t\t\t\t\t   \"foreign key constraint by a local table\"),\n\t\t\t\t\t\terrdetail(\"Table \\\"%s\\\" references \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t  referencingRelationName,\n\t\t\t\t\t\t\t\t  referencedRelationName)));\n\t}\n}\n\n\n/*\n * PreprocessTruncateStatement handles few things that should be\n * done before standard process utility is called for truncate\n * command.\n */\nvoid\nPreprocessTruncateStatement(TruncateStmt *truncateStatement)\n{\n\tErrorIfUnsupportedTruncateStmt(truncateStatement);\n\tEnsurePartitionTableNotReplicatedForTruncate(truncateStatement);\n\tExecuteTruncateStmtSequentialIfNecessary(truncateStatement);\n\n\tuint32 lockAcquiringMode = truncateStatement->behavior == DROP_CASCADE ?\n\t\t\t\t\t\t\t   DIST_LOCK_REFERENCING_TABLES :\n\t\t\t\t\t\t\t   DIST_LOCK_DEFAULT;\n\n\tAcquireDistributedLockOnRelations(truncateStatement->relations, AccessExclusiveLock,\n\t\t\t\t\t\t\t\t\t  lockAcquiringMode);\n}\n\n\n/*\n * ErrorIfUnsupportedTruncateStmt errors out if the command attempts to\n * truncate a distributed foreign table.\n */\nstatic void\nErrorIfUnsupportedTruncateStmt(TruncateStmt *truncateStatement)\n{\n\tList *relationList = truncateStatement->relations;\n\tRangeVar *rangeVar = NULL;\n\tforeach_declared_ptr(rangeVar, relationList)\n\t{\n\t\tOid relationId = RangeVarGetRelid(rangeVar, NoLock, false);\n\n\t\tErrorIfIllegallyChangingKnownShard(relationId);\n\n\t\t/*\n\t\t * We allow truncating foreign tables that are added to metadata\n\t\t * only on the coordinator, as user mappings are not propagated.\n\t\t */\n\t\tif (IsForeignTable(relationId) &&\n\t\t\tIsCitusTableType(relationId, CITUS_LOCAL_TABLE) &&\n\t\t\t!IsCoordinator())\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"truncating foreign tables that are added to metadata \"\n\t\t\t\t\t\t\t\t   \"can only be executed on the coordinator\")));\n\t\t}\n\t}\n}\n\n\n/*\n * EnsurePartitionTableNotReplicatedForTruncate a simple wrapper around\n * EnsurePartitionTableNotReplicated for TRUNCATE command.\n */\nstatic void\nEnsurePartitionTableNotReplicatedForTruncate(TruncateStmt *truncateStatement)\n{\n\tRangeVar *rangeVar = NULL;\n\tforeach_declared_ptr(rangeVar, truncateStatement->relations)\n\t{\n\t\tOid relationId = RangeVarGetRelid(rangeVar, NoLock, false);\n\n\t\tif (!IsCitusTable(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tEnsurePartitionTableNotReplicated(relationId);\n\t}\n}\n\n\n/*\n * ExecuteTruncateStmtSequentialIfNecessary decides if the TRUNCATE stmt needs\n * to run sequential. If so, it calls SetLocalMultiShardModifyModeToSequential().\n *\n * If a reference table which has a foreign key from a distributed table is truncated\n * we need to execute the command sequentially to avoid self-deadlock.\n */\nstatic void\nExecuteTruncateStmtSequentialIfNecessary(TruncateStmt *command)\n{\n\tList *relationList = command->relations;\n\tbool failOK = false;\n\n\tRangeVar *rangeVar = NULL;\n\tforeach_declared_ptr(rangeVar, relationList)\n\t{\n\t\tOid relationId = RangeVarGetRelid(rangeVar, NoLock, failOK);\n\n\t\tif ((IsCitusTableType(relationId, REFERENCE_TABLE) ||\n\t\t\t IsCitusTableType(relationId, CITUS_LOCAL_TABLE)) &&\n\t\t\tTableReferenced(relationId))\n\t\t{\n\t\t\tchar *relationName = get_rel_name(relationId);\n\n\t\t\tereport(DEBUG1, (errmsg(\"switching to sequential query execution mode\"),\n\t\t\t\t\t\t\t errdetail(\n\t\t\t\t\t\t\t\t \"Table \\\"%s\\\" is modified, which might lead \"\n\t\t\t\t\t\t\t\t \"to data inconsistencies or distributed deadlocks via \"\n\t\t\t\t\t\t\t\t \"parallel accesses to hash distributed tables due to \"\n\t\t\t\t\t\t\t\t \"foreign keys. Any parallel modification to \"\n\t\t\t\t\t\t\t\t \"those hash distributed tables in the same \"\n\t\t\t\t\t\t\t\t \"transaction can only be executed in sequential query \"\n\t\t\t\t\t\t\t\t \"execution mode\", relationName)));\n\n\t\t\tSetLocalMultiShardModifyModeToSequential();\n\n\t\t\t/* nothing to do more */\n\t\t\treturn;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/type.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * type.c\n *    Commands for TYPE statements.\n *    The following types are supported in citus\n *     - Composite Types\n *     - Enum Types\n *     - Array Types\n *\n *    Types that are currently not supporter:\n *     - Range Types\n *     - Base Types\n *\n *    Range types have a dependency on functions. We can only support Range\n *    types after we have function distribution sorted.\n *\n *    Base types are more complex and often involve c code from extensions.\n *    These types should be created by creating the extension on all the\n *    workers as well. Therefore types created during the creation of an\n *    extension are not propagated to the worker nodes.\n *\n *    Types will be created on the workers during the following situations:\n *     - on type creation (except if called in a transaction)\n *       By not distributing types directly when in a transaction allows\n *       the type to be used in a newly created table that will be\n *       distributed in the same transaction. In that case the type will be\n *       created just-in-time to allow citus' parallelism to work.\n *     - just-in-time\n *       When the type is not already distributed but used in an object\n *       that will distribute now. This allows distributed tables to use\n *       types that have not yet been propagated, either due to the\n *       transaction case abvove, or due to a type predating the citus\n *       extension.\n *     - node activation\n *       Together with all objects that are marked as distributed in citus\n *       types will be created during the activation of a new node to allow\n *       reference tables to use this type.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_enum.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/extension.h\"\n#include \"nodes/makefuncs.h\"\n#include \"parser/parse_type.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/regproc.h\"\n#include \"utils/syscache.h\"\n#include \"utils/typcache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_create_or_replace.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_transaction.h\"\n\n#define AlterEnumIsRename(stmt) (stmt->oldVal != NULL)\n#define AlterEnumIsAddValue(stmt) (stmt->oldVal == NULL)\n\n\n#define ALTER_TYPE_OWNER_COMMAND \"ALTER TYPE %s OWNER TO %s;\"\n\n\n/* guc to turn of the automatic type distribution */\nbool EnableCreateTypePropagation = true;\n\n/* forward declaration for helper functions*/\nstatic TypeName * MakeTypeNameFromRangeVar(const RangeVar *relation);\nstatic Oid GetTypeOwner(Oid typeOid);\nstatic Oid LookupNonAssociatedArrayTypeNameOid(ParseState *pstate,\n\t\t\t\t\t\t\t\t\t\t\t   const TypeName *typeName,\n\t\t\t\t\t\t\t\t\t\t\t   bool missing_ok);\n\n/* recreate functions */\nstatic CompositeTypeStmt * RecreateCompositeTypeStmt(Oid typeOid);\nstatic List * CompositeTypeColumnDefList(Oid typeOid);\nstatic CreateEnumStmt * RecreateEnumStmt(Oid typeOid);\nstatic List * EnumValsList(Oid typeOid);\n\n/*\n * PreprocessRenameTypeAttributeStmt is called for changes of attribute names for composite\n * types. Planning is called before the statement is applied locally.\n *\n * For distributed types we apply the attribute renames directly on all the workers to\n * keep the type in sync across the cluster.\n */\nList *\nPreprocessRenameTypeAttributeStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_ATTRIBUTE);\n\tAssert(stmt->relationType == OBJECT_TYPE);\n\n\tList *typeAddresses = GetObjectAddressListFromParseTree((Node *) stmt, false, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(typeAddresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(typeAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tQualifyTreeNode((Node *) stmt);\n\n\tconst char *sql = DeparseTreeNode((Node *) stmt);\n\n\tEnsureSequentialMode(OBJECT_TYPE);\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) sql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * CreateTypeStmtByObjectAddress returns a parsetree for the CREATE TYPE statement to\n * recreate the type by its object address.\n */\nNode *\nCreateTypeStmtByObjectAddress(const ObjectAddress *address)\n{\n\tAssert(address->classId == TypeRelationId);\n\n\tswitch (get_typtype(address->objectId))\n\t{\n\t\tcase TYPTYPE_ENUM:\n\t\t{\n\t\t\treturn (Node *) RecreateEnumStmt(address->objectId);\n\t\t}\n\n\t\tcase TYPTYPE_COMPOSITE:\n\t\t{\n\t\t\treturn (Node *) RecreateCompositeTypeStmt(address->objectId);\n\t\t}\n\n\t\tcase TYPTYPE_DOMAIN:\n\t\t{\n\t\t\treturn (Node *) RecreateDomainStmt(address->objectId);\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unsupported type to generate create statement for\"),\n\t\t\t\t\t\t\terrdetail(\"only enum and composite types can be recreated\")));\n\t\t}\n\t}\n}\n\n\n/*\n * RecreateCompositeTypeStmt is called for composite types to create its parsetree for the\n * CREATE TYPE statement that would recreate the composite type.\n */\nstatic CompositeTypeStmt *\nRecreateCompositeTypeStmt(Oid typeOid)\n{\n\tAssert(get_typtype(typeOid) == TYPTYPE_COMPOSITE);\n\n\tCompositeTypeStmt *stmt = makeNode(CompositeTypeStmt);\n\tList *names = stringToQualifiedNameList(format_type_be_qualified(typeOid), NULL);\n\tstmt->typevar = makeRangeVarFromNameList(names);\n\tstmt->coldeflist = CompositeTypeColumnDefList(typeOid);\n\n\treturn stmt;\n}\n\n\n/*\n * attributeFormToColumnDef returns a ColumnDef * describing the field and its property\n * for a pg_attribute entry.\n */\nstatic ColumnDef *\nattributeFormToColumnDef(Form_pg_attribute attributeForm)\n{\n\treturn makeColumnDef(NameStr(attributeForm->attname),\n\t\t\t\t\t\t attributeForm->atttypid,\n\t\t\t\t\t\t attributeForm->atttypmod,\n\t\t\t\t\t\t attributeForm->attcollation);\n}\n\n\n/*\n * CompositeTypeColumnDefList returns a list of ColumnDef *'s that make up all the fields\n * of the composite type.\n */\nstatic List *\nCompositeTypeColumnDefList(Oid typeOid)\n{\n\tList *columnDefs = NIL;\n\n\tOid relationId = typeidTypeRelid(typeOid);\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\tfor (int attributeIndex = 0; attributeIndex < tupleDescriptor->natts;\n\t\t attributeIndex++)\n\t{\n\t\tForm_pg_attribute attributeForm = TupleDescAttr(tupleDescriptor, attributeIndex);\n\n\t\tif (attributeForm->attisdropped)\n\t\t{\n\t\t\t/* skip logically hidden attributes */\n\t\t\tcontinue;\n\t\t}\n\n\t\tcolumnDefs = lappend(columnDefs, attributeFormToColumnDef(attributeForm));\n\t}\n\n\trelation_close(relation, AccessShareLock);\n\n\treturn columnDefs;\n}\n\n\n/*\n * RecreateEnumStmt returns a parsetree for a CREATE TYPE ... AS ENUM statement that would\n * recreate the given enum type.\n */\nstatic CreateEnumStmt *\nRecreateEnumStmt(Oid typeOid)\n{\n\tAssert(get_typtype(typeOid) == TYPTYPE_ENUM);\n\n\tCreateEnumStmt *stmt = makeNode(CreateEnumStmt);\n\tstmt->typeName = stringToQualifiedNameList(format_type_be_qualified(typeOid), NULL);\n\tstmt->vals = EnumValsList(typeOid);\n\n\treturn stmt;\n}\n\n\n/*\n * EnumValsList returns a list of String values containing the enum values for the given\n * enum type.\n */\nstatic List *\nEnumValsList(Oid typeOid)\n{\n\tHeapTuple enum_tuple = NULL;\n\tScanKeyData skey = { 0 };\n\n\tList *vals = NIL;\n\n\t/* Scan pg_enum for the members of the target enum type. */\n\tScanKeyInit(&skey,\n\t\t\t\tAnum_pg_enum_enumtypid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(typeOid));\n\n\tRelation enum_rel = table_open(EnumRelationId, AccessShareLock);\n\tSysScanDesc enum_scan = systable_beginscan(enum_rel,\n\t\t\t\t\t\t\t\t\t\t\t   EnumTypIdSortOrderIndexId,\n\t\t\t\t\t\t\t\t\t\t\t   true, NULL,\n\t\t\t\t\t\t\t\t\t\t\t   1, &skey);\n\n\t/* collect all value names in CREATE TYPE ... AS ENUM stmt */\n\twhile (HeapTupleIsValid(enum_tuple = systable_getnext(enum_scan)))\n\t{\n\t\tForm_pg_enum en = (Form_pg_enum) GETSTRUCT(enum_tuple);\n\t\tvals = lappend(vals, makeString(pstrdup(NameStr(en->enumlabel))));\n\t}\n\n\tsystable_endscan(enum_scan);\n\ttable_close(enum_rel, AccessShareLock);\n\treturn vals;\n}\n\n\n/*\n * CompositeTypeStmtObjectAddress finds the ObjectAddress for the composite type described\n * by the CompositeTypeStmt. If missing_ok is false this function throws an error if the\n * type does not exist.\n *\n * Never returns NULL, but the objid in the address could be invalid if missing_ok was set\n * to true.\n */\nList *\nCompositeTypeStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tCompositeTypeStmt *stmt = castNode(CompositeTypeStmt, node);\n\tTypeName *typeName = MakeTypeNameFromRangeVar(stmt->typevar);\n\tOid typeOid = LookupNonAssociatedArrayTypeNameOid(NULL, typeName, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TypeRelationId, typeOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * CreateEnumStmtObjectAddress finds the ObjectAddress for the enum type described by the\n * CreateEnumStmt. If missing_ok is false this function throws an error if the type does\n * not exist.\n *\n * Never returns NULL, but the objid in the address could be invalid if missing_ok was set\n * to true.\n */\nList *\nCreateEnumStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tCreateEnumStmt *stmt = castNode(CreateEnumStmt, node);\n\tTypeName *typeName = makeTypeNameFromNameList(stmt->typeName);\n\tOid typeOid = LookupNonAssociatedArrayTypeNameOid(NULL, typeName, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TypeRelationId, typeOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * AlterTypeStmtObjectAddress finds the ObjectAddress for the type described by the ALTER\n * TYPE statement. If missing_ok is false this function throws an error if the type does\n * not exist.\n *\n * Never returns NULL, but the objid in the address could be invalid if missing_ok was set\n * to true.\n */\nList *\nAlterTypeStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tAssert(stmt->objtype == OBJECT_TYPE);\n\n\tTypeName *typeName = MakeTypeNameFromRangeVar(stmt->relation);\n\tOid typeOid = LookupTypeNameOid(NULL, typeName, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TypeRelationId, typeOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * AlterEnumStmtObjectAddress return the ObjectAddress of the enum type that is the\n * object of the AlterEnumStmt. Errors is missing_ok is false.\n */\nList *\nAlterEnumStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterEnumStmt *stmt = castNode(AlterEnumStmt, node);\n\tTypeName *typeName = makeTypeNameFromNameList(stmt->typeName);\n\tOid typeOid = LookupTypeNameOid(NULL, typeName, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TypeRelationId, typeOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * RenameTypeStmtObjectAddress returns the ObjectAddress of the type that is the object\n * of the RenameStmt. Errors if missing_ok is false.\n */\nList *\nRenameTypeStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_TYPE);\n\n\tTypeName *typeName = makeTypeNameFromNameList((List *) stmt->object);\n\tOid typeOid = LookupTypeNameOid(NULL, typeName, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TypeRelationId, typeOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * AlterTypeSchemaStmtObjectAddress returns the ObjectAddress of the type that is the\n * object of the AlterObjectSchemaStmt. Errors if missing_ok is false.\n *\n * This could be called both before or after it has been applied locally. It will look in\n * the old schema first, if the type cannot be found in that schema it will look in the\n * new schema. Errors if missing_ok is false and the type cannot be found in either of the\n * schemas.\n */\nList *\nAlterTypeSchemaStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TYPE || stmt->objectType == OBJECT_DOMAIN);\n\n\tList *names = (List *) stmt->object;\n\n\t/*\n\t * we hardcode missing_ok here during LookupTypeNameOid because if we can't find it it\n\t * might have already been moved in this transaction.\n\t */\n\tTypeName *typeName = makeTypeNameFromNameList(names);\n\tOid typeOid = LookupTypeNameOid(NULL, typeName, true);\n\n\tif (typeOid == InvalidOid)\n\t{\n\t\t/*\n\t\t * couldn't find the type, might have already been moved to the new schema, we\n\t\t * construct a new typename that uses the new schema to search in.\n\t\t */\n\n\t\t/* typename is the last in the list of names */\n\t\tString *typeNameStr = lfirst(list_tail(names));\n\n\t\t/*\n\t\t * we don't error here either, as the error would be not a good user facing\n\t\t * error if the type didn't exist in the first place.\n\t\t */\n\t\tList *newNames = list_make2(makeString(stmt->newschema), typeNameStr);\n\t\tTypeName *newTypeName = makeTypeNameFromNameList(newNames);\n\t\ttypeOid = LookupTypeNameOid(NULL, newTypeName, true);\n\n\t\t/*\n\t\t * if the type is still invalid we couldn't find the type, error with the same\n\t\t * message postgres would error with it missing_ok is false (not ok to miss)\n\t\t */\n\t\tif (!missing_ok && typeOid == InvalidOid)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t\t\terrmsg(\"type \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t\t   TypeNameToString(typeName))));\n\t\t}\n\t}\n\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TypeRelationId, typeOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * RenameTypeAttributeStmtObjectAddress returns the ObjectAddress of the type that is the\n * object of the RenameStmt. Errors if missing_ok is false.\n *\n * The ObjectAddress is that of the type, not that of the attributed for which the name is\n * changed as Attributes are not distributed on their own but as a side effect of the\n * whole type distribution.\n */\nList *\nRenameTypeAttributeStmtObjectAddress(Node *node, bool missing_ok)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_ATTRIBUTE);\n\tAssert(stmt->relationType == OBJECT_TYPE);\n\n\tTypeName *typeName = MakeTypeNameFromRangeVar(stmt->relation);\n\tOid typeOid = LookupTypeNameOid(NULL, typeName, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TypeRelationId, typeOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * AlterTypeOwnerObjectAddress returns the ObjectAddress of the type that is the object\n * of the AlterOwnerStmt. Errors if missing_ok is false.\n */\nList *\nAlterTypeOwnerObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_TYPE);\n\n\tTypeName *typeName = makeTypeNameFromNameList((List *) stmt->object);\n\tOid typeOid = LookupTypeNameOid(NULL, typeName, missing_ok);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*address, TypeRelationId, typeOid);\n\n\treturn list_make1(address);\n}\n\n\n/*\n * CreateTypeDDLCommandsIdempotent returns a list of DDL statements (const char *) to be\n * executed on a node to recreate the type addressed by the typeAddress.\n */\nList *\nCreateTypeDDLCommandsIdempotent(const ObjectAddress *typeAddress)\n{\n\tList *ddlCommands = NIL;\n\tStringInfoData buf = { 0 };\n\n\tAssert(typeAddress->classId == TypeRelationId);\n\n\tif (type_is_array(typeAddress->objectId))\n\t{\n\t\t/*\n\t\t * array types cannot be created on their own, but could be a direct dependency of\n\t\t * a table. In that case they are on the dependency graph and tried to be created.\n\t\t *\n\t\t * By returning an empty list we will not send any commands to create this type.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tHeapTuple tup = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeAddress->objectId));\n\tif (!HeapTupleIsValid(tup))\n\t{\n\t\telog(ERROR, \"cache lookup failed for type %u\", typeAddress->objectId);\n\t}\n\n\t/* Don't send any command if the type is a table's row type */\n\tForm_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);\n\tif (typTup->typtype == TYPTYPE_COMPOSITE &&\n\t\tget_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)\n\t{\n\t\treturn NIL;\n\t}\n\n\tNode *stmt = CreateTypeStmtByObjectAddress(typeAddress);\n\n\t/* capture ddl command for recreation and wrap in create if not exists construct */\n\tconst char *ddlCommand = DeparseTreeNode(stmt);\n\tddlCommand = WrapCreateOrReplace(ddlCommand);\n\tddlCommands = lappend(ddlCommands, (void *) ddlCommand);\n\n\t/* add owner ship change so the creation command can be run as a different user */\n\tconst char *username = GetUserNameFromId(GetTypeOwner(typeAddress->objectId), false);\n\tinitStringInfo(&buf);\n\tappendStringInfo(&buf, ALTER_TYPE_OWNER_COMMAND,\n\t\t\t\t\t getObjectIdentity(typeAddress, false),\n\t\t\t\t\t quote_identifier(username));\n\tddlCommands = lappend(ddlCommands, buf.data);\n\n\treturn ddlCommands;\n}\n\n\n/*\n * GenerateBackupNameForTypeCollision generates a new type name for an existing type. The\n * name is generated in such a way that the new name doesn't overlap with an existing type\n * by adding a suffix with incrementing number after the new name.\n */\nchar *\nGenerateBackupNameForTypeCollision(const ObjectAddress *address)\n{\n\tList *names = stringToQualifiedNameList(format_type_be_qualified(address->objectId),\n\t\t\t\t\t\t\t\t\t\t\tNULL);\n\tRangeVar *rel = makeRangeVarFromNameList(names);\n\n\tchar *newName = palloc0(NAMEDATALEN);\n\tchar suffix[NAMEDATALEN] = { 0 };\n\tchar *baseName = rel->relname;\n\tint baseLength = strlen(baseName);\n\tint count = 0;\n\n\twhile (true)\n\t{\n\t\tint suffixLength = SafeSnprintf(suffix, NAMEDATALEN - 1, \"(citus_backup_%d)\",\n\t\t\t\t\t\t\t\t\t\tcount);\n\n\t\t/* trim the base name at the end to leave space for the suffix and trailing \\0 */\n\t\tbaseLength = Min(baseLength, NAMEDATALEN - suffixLength - 1);\n\n\t\t/* clear newName before copying the potentially trimmed baseName and suffix */\n\t\tmemset(newName, 0, NAMEDATALEN);\n\t\tstrncpy_s(newName, NAMEDATALEN, baseName, baseLength);\n\t\tstrncpy_s(newName + baseLength, NAMEDATALEN - baseLength, suffix,\n\t\t\t\t  suffixLength);\n\n\t\trel->relname = newName;\n\t\tTypeName *newTypeName = makeTypeNameFromNameList(MakeNameListFromRangeVar(rel));\n\n\t\tOid typeOid = LookupTypeNameOid(NULL, newTypeName, true);\n\t\tif (typeOid == InvalidOid)\n\t\t{\n\t\t\treturn newName;\n\t\t}\n\n\t\tcount++;\n\t}\n}\n\n\n/*\n * GetTypeOwner\n *\n *\t\tGiven the type OID, find its owner\n */\nstatic Oid\nGetTypeOwner(Oid typeOid)\n{\n\tOid result = InvalidOid;\n\n\tHeapTuple tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));\n\tif (HeapTupleIsValid(tp))\n\t{\n\t\tForm_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);\n\n\t\tresult = typtup->typowner;\n\t\tReleaseSysCache(tp);\n\t}\n\n\treturn result;\n}\n\n\n/*\n * MakeTypeNameFromRangeVar creates a TypeName based on a RangeVar.\n */\nstatic TypeName *\nMakeTypeNameFromRangeVar(const RangeVar *relation)\n{\n\tList *names = NIL;\n\tif (relation->schemaname)\n\t{\n\t\tnames = lappend(names, makeString(relation->schemaname));\n\t}\n\tnames = lappend(names, makeString(relation->relname));\n\n\treturn makeTypeNameFromNameList(names);\n}\n\n\n/*\n * LookupNonAssociatedArrayTypeNameOid returns the oid of the type with the given type name\n * that is not an array type that is associated to another user defined type.\n */\nstatic Oid\nLookupNonAssociatedArrayTypeNameOid(ParseState *pstate, const TypeName *typeName,\n\t\t\t\t\t\t\t\t\tbool missing_ok)\n{\n\tType tup = LookupTypeName(NULL, typeName, NULL, missing_ok);\n\tOid typeOid = InvalidOid;\n\tif (tup != NULL)\n\t{\n\t\tif (((Form_pg_type) GETSTRUCT(tup))->typelem == 0)\n\t\t{\n\t\t\ttypeOid = ((Form_pg_type) GETSTRUCT(tup))->oid;\n\t\t}\n\t\tReleaseSysCache(tup);\n\t}\n\n\tif (!missing_ok && typeOid == InvalidOid)\n\t{\n\t\telog(ERROR, \"type \\\"%s\\\" that is not an array type associated with \"\n\t\t\t\t\t\"another type does not exist\", TypeNameToString(typeName));\n\t}\n\n\treturn typeOid;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/utility_hook.c",
    "content": "/*-------------------------------------------------------------------------\n * utility_hook.c\n *\t  Citus utility hook and related functionality.\n *\n * The utility hook is called by PostgreSQL when processing any command\n * that is not SELECT, UPDATE, DELETE, INSERT, in place of the regular\n * PostprocessUtility function. We use this primarily to implement (or in\n * some cases prevent) DDL commands and COPY on distributed tables.\n *\n * For DDL commands that affect distributed tables, we check whether\n * they are valid (and implemented) for the distributed table and then\n * propagate the command to all shards and, in case of MX, to distributed\n * tables on other nodes. We still call the original ProcessUtility\n * function to apply catalog changes on the coordinator.\n *\n * For COPY into a distributed table, we provide an alternative\n * implementation in ProcessCopyStmt that sends rows to shards based\n * on their distribution column value instead of writing it to the local\n * table on the coordinator. For COPY from a distributed table, we\n * replace the table with a SELECT * FROM table and pass it back to\n * PostprocessUtility, which will plan the query via the distributed planner\n * hook.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/attnum.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"catalog/catalog.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_database.h\"\n#include \"commands/dbcommands.h\"\n#include \"commands/defrem.h\"\n#include \"commands/extension.h\"\n#include \"commands/tablecmds.h\"\n#include \"foreign/foreign.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"postmaster/postmaster.h\"\n#include \"tcop/utility.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/inval.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"citus_version.h\"\n#include \"pg_version_constants.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/backend_data.h\"\n#include \"distributed/citus_depended_object.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/commands/utility_hook.h\" /* IWYU pragma: keep */\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/executor_util.h\"\n#include \"distributed/foreign_key_relationship.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/maintenanced.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_explain.h\"\n#include \"distributed/multi_logical_replication.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/string_utils.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_shard_visibility.h\"\n#include \"distributed/worker_transaction.h\"\n\nbool EnableDDLPropagation = true; /* ddl propagation is enabled */\nint CreateObjectPropagationMode = CREATE_OBJECT_PROPAGATION_IMMEDIATE;\nPropSetCmdBehavior PropagateSetCommands = PROPSETCMD_NONE; /* SET prop off */\nstatic bool shouldInvalidateForeignKeyGraph = false;\nstatic int activeAlterTables = 0;\nstatic int activeDropSchemaOrDBs = 0;\nstatic bool ConstraintDropped = false;\n\nProcessUtility_hook_type PrevProcessUtility = NULL;\nint UtilityHookLevel = 0;\n\n\n/* Local functions forward declarations for helper functions */\nstatic void citus_ProcessUtilityInternal(PlannedStmt *pstmt,\n\t\t\t\t\t\t\t\t\t\t const char *queryString,\n\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext context,\n\t\t\t\t\t\t\t\t\t\t ParamListInfo params,\n\t\t\t\t\t\t\t\t\t\t struct QueryEnvironment *queryEnv,\n\t\t\t\t\t\t\t\t\t\t DestReceiver *dest,\n\t\t\t\t\t\t\t\t\t\t QueryCompletion *completionTag);\nstatic void set_indexsafe_procflags(void);\nstatic char * CurrentSearchPath(void);\nstatic void IncrementUtilityHookCountersIfNecessary(Node *parsetree);\nstatic void PostStandardProcessUtility(Node *parsetree);\nstatic void DecrementUtilityHookCountersIfNecessary(Node *parsetree);\nstatic bool IsDropSchemaOrDB(Node *parsetree);\nstatic bool ShouldCheckUndistributeCitusLocalTables(void);\n\n\n/*\n * ProcessUtilityParseTree is a convenience method to create a PlannedStmt out of\n * pieces of a utility statement before invoking ProcessUtility.\n */\nvoid\nProcessUtilityParseTree(Node *node, const char *queryString, ProcessUtilityContext\n\t\t\t\t\t\tcontext,\n\t\t\t\t\t\tParamListInfo params, DestReceiver *dest,\n\t\t\t\t\t\tQueryCompletion *completionTag)\n{\n\tPlannedStmt *plannedStmt = makeNode(PlannedStmt);\n\tplannedStmt->commandType = CMD_UTILITY;\n\tplannedStmt->utilityStmt = node;\n\n\tProcessUtility(plannedStmt, queryString, false, context, params, NULL, dest,\n\t\t\t\t   completionTag);\n}\n\n\n/*\n * citus_ProcessUtility is the main entry hook for implementing Citus-specific\n * utility behavior. Its primary responsibilities are intercepting COPY and DDL\n * commands and augmenting the coordinator's command with corresponding tasks\n * to be run on worker nodes, after suitably ensuring said commands' options\n * are fully supported by Citus. Much of the DDL behavior is toggled by Citus'\n * enable_ddl_propagation GUC. In addition to DDL and COPY, utilities such as\n * TRUNCATE and VACUUM are also supported.\n */\nvoid\ncitus_ProcessUtility(PlannedStmt *pstmt,\n\t\t\t\t\t const char *queryString,\n\t\t\t\t\t bool readOnlyTree,\n\t\t\t\t\t ProcessUtilityContext context,\n\t\t\t\t\t ParamListInfo params,\n\t\t\t\t\t struct QueryEnvironment *queryEnv,\n\t\t\t\t\t DestReceiver *dest,\n\t\t\t\t\t QueryCompletion *completionTag)\n{\n\tif (readOnlyTree)\n\t{\n\t\tpstmt = copyObject(pstmt);\n\t}\n\n\tNode *parsetree = pstmt->utilityStmt;\n\n\tif (IsA(parsetree, TransactionStmt))\n\t{\n\t\tTransactionStmt *transactionStmt = (TransactionStmt *) parsetree;\n\n\t\tif (context == PROCESS_UTILITY_TOPLEVEL &&\n\t\t\t(transactionStmt->kind == TRANS_STMT_BEGIN ||\n\t\t\t transactionStmt->kind == TRANS_STMT_START))\n\t\t{\n\t\t\tSaveBeginCommandProperties(transactionStmt);\n\t\t}\n\t}\n\n\tif (IsA(parsetree, TransactionStmt) ||\n\t\tIsA(parsetree, ListenStmt) ||\n\t\tIsA(parsetree, NotifyStmt) ||\n\t\tIsA(parsetree, ExecuteStmt) ||\n\t\tIsA(parsetree, PrepareStmt) ||\n\t\tIsA(parsetree, DiscardStmt) ||\n\t\tIsA(parsetree, DeallocateStmt) ||\n\t\tIsA(parsetree, DeclareCursorStmt) ||\n\t\tIsA(parsetree, FetchStmt))\n\t{\n\t\t/*\n\t\t * Skip additional checks for common commands that do not have any\n\t\t * Citus-specific logic.\n\t\t *\n\t\t * Transaction statements (e.g. ABORT, COMMIT) can be run in aborted\n\t\t * transactions in which case a lot of checks cannot be done safely in\n\t\t * that state. Since we never need to intercept transaction statements,\n\t\t * skip our checks and immediately fall into standard_ProcessUtility.\n\t\t */\n\t\tPrevProcessUtility(pstmt, queryString, false, context,\n\t\t\t\t\t\t   params, queryEnv, dest, completionTag);\n\n\t\treturn;\n\t}\n\n\tbool isCreateAlterExtensionUpdateCitusStmt = IsCreateAlterExtensionUpdateCitusStmt(\n\t\tparsetree);\n\n\tif (EnableVersionChecks && isCreateAlterExtensionUpdateCitusStmt)\n\t{\n\t\tErrorIfUnstableCreateOrAlterExtensionStmt(parsetree);\n\t}\n\n\tif (IsA(parsetree, CreateExtensionStmt))\n\t{\n\t\t/*\n\t\t * Postgres forbids creating/altering other extensions from within an extension script, so we use a utility hook instead\n\t\t * This preprocess check whether citus_columnar should be installed first before citus\n\t\t */\n\t\tPreprocessCreateExtensionStmtForCitusColumnar(parsetree);\n\t}\n\n\tif (isCreateAlterExtensionUpdateCitusStmt || IsDropCitusExtensionStmt(parsetree))\n\t{\n\t\t/*\n\t\t * Citus maintains a higher level cache. We use the cache invalidation mechanism\n\t\t * of Postgres to achieve cache coherency between backends. Any change to citus\n\t\t * extension should be made known to other backends. We do this by invalidating the\n\t\t * relcache and therefore invoking the citus registered callback that invalidates\n\t\t * the citus cache in other backends.\n\t\t */\n\t\tCacheInvalidateRelcacheAll();\n\t}\n\n\t/*\n\t * Make sure that on DROP DATABASE we terminate the background daemon\n\t * associated with it.\n\t */\n\tif (IsA(parsetree, DropdbStmt))\n\t{\n\t\tconst bool missingOK = true;\n\t\tDropdbStmt *dropDbStatement = (DropdbStmt *) parsetree;\n\t\tchar *dbname = dropDbStatement->dbname;\n\t\tOid databaseOid = get_database_oid(dbname, missingOK);\n\n\t\tif (OidIsValid(databaseOid))\n\t\t{\n\t\t\tStopMaintenanceDaemon(databaseOid);\n\t\t}\n\t}\n\n\tif (!CitusHasBeenLoaded())\n\t{\n\t\t/*\n\t\t * Process the command via RunPreprocessNonMainDBCommand and\n\t\t * RunPostprocessNonMainDBCommand hooks if we're in a non-main database\n\t\t * and if the command is a node-wide object management command that we\n\t\t * support from non-main databases.\n\t\t */\n\n\t\tbool shouldSkipPrevUtilityHook = RunPreprocessNonMainDBCommand(parsetree);\n\n\t\tif (!shouldSkipPrevUtilityHook)\n\t\t{\n\t\t\t/*\n\t\t\t * Ensure that utility commands do not behave any differently until CREATE\n\t\t\t * EXTENSION is invoked.\n\t\t\t */\n\t\t\tPrevProcessUtility(pstmt, queryString, false, context,\n\t\t\t\t\t\t\t   params, queryEnv, dest, completionTag);\n\t\t}\n\n\t\tRunPostprocessNonMainDBCommand(parsetree);\n\n\t\treturn;\n\t}\n\telse if (IsA(parsetree, CallStmt))\n\t{\n\t\tCallStmt *callStmt = (CallStmt *) parsetree;\n\n\t\t/*\n\t\t * If the procedure is distributed and we are using MX then we have the\n\t\t * possibility of calling it on the worker. If the data is located on\n\t\t * the worker this can avoid making many network round trips.\n\t\t */\n\t\tif (context == PROCESS_UTILITY_TOPLEVEL &&\n\t\t\tCallDistributedProcedureRemotely(callStmt, dest))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t/*\n\t\t * Stored procedures are a bit strange in the sense that some statements\n\t\t * are not in a transaction block, but can be rolled back. We need to\n\t\t * make sure we send all statements in a transaction block. The\n\t\t * StoredProcedureLevel variable signals this to the router executor\n\t\t * and indicates how deep in the call stack we are in case of nested\n\t\t * stored procedures.\n\t\t */\n\t\tStoredProcedureLevel += 1;\n\n\t\tPG_TRY();\n\t\t{\n\t\t\tPrevProcessUtility(pstmt, queryString, false, context,\n\t\t\t\t\t\t\t   params, queryEnv, dest, completionTag);\n\n\t\t\tStoredProcedureLevel -= 1;\n\n\t\t\tif (InDelegatedProcedureCall && StoredProcedureLevel == 0)\n\t\t\t{\n\t\t\t\tInDelegatedProcedureCall = false;\n\t\t\t}\n\t\t}\n\t\tPG_CATCH();\n\t\t{\n\t\t\tStoredProcedureLevel -= 1;\n\n\t\t\tif (InDelegatedProcedureCall && StoredProcedureLevel == 0)\n\t\t\t{\n\t\t\t\tInDelegatedProcedureCall = false;\n\t\t\t}\n\t\t\tPG_RE_THROW();\n\t\t}\n\t\tPG_END_TRY();\n\n\t\treturn;\n\t}\n\telse if (IsA(parsetree, DoStmt))\n\t{\n\t\t/*\n\t\t * All statements in a DO block are executed in a single transaction,\n\t\t * so we need to keep track of whether we are inside a DO block.\n\t\t */\n\t\tDoBlockLevel += 1;\n\n\t\tPG_TRY();\n\t\t{\n\t\t\tPrevProcessUtility(pstmt, queryString, false, context,\n\t\t\t\t\t\t\t   params, queryEnv, dest, completionTag);\n\n\t\t\tDoBlockLevel -= 1;\n\t\t}\n\t\tPG_CATCH();\n\t\t{\n\t\t\tDoBlockLevel -= 1;\n\t\t\tPG_RE_THROW();\n\t\t}\n\t\tPG_END_TRY();\n\n\t\treturn;\n\t}\n\n\tUtilityHookLevel++;\n\n\tPG_TRY();\n\t{\n\t\tcitus_ProcessUtilityInternal(pstmt, queryString, context, params, queryEnv, dest,\n\t\t\t\t\t\t\t\t\t completionTag);\n\n\t\tif (UtilityHookLevel == 1)\n\t\t{\n\t\t\t/*\n\t\t\t * When Citus local tables are disconnected from the foreign key graph, which\n\t\t\t * can happen due to various kinds of drop commands, we immediately\n\t\t\t * undistribute them at the end of the command.\n\t\t\t */\n\t\t\tif (ShouldCheckUndistributeCitusLocalTables())\n\t\t\t{\n\t\t\t\tUndistributeDisconnectedCitusLocalTables();\n\t\t\t}\n\t\t\tResetConstraintDropped();\n\n\t\t\t/*\n\t\t\t * We're only interested in top-level CREATE TABLE commands\n\t\t\t * to create a tenant schema table or a Citus managed table.\n\t\t\t */\n\t\t\tif (context == PROCESS_UTILITY_TOPLEVEL &&\n\t\t\t\t(IsA(parsetree, CreateStmt) ||\n\t\t\t\t IsA(parsetree, CreateForeignTableStmt) ||\n\t\t\t\t IsA(parsetree, CreateTableAsStmt)))\n\t\t\t{\n\t\t\t\tNode *createStmt = NULL;\n\t\t\t\tif (IsA(parsetree, CreateTableAsStmt))\n\t\t\t\t{\n\t\t\t\t\tcreateStmt = parsetree;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Not directly cast to CreateStmt to guard against the case where\n\t\t\t\t\t * the definition of CreateForeignTableStmt changes in future.\n\t\t\t\t\t */\n\t\t\t\t\tcreateStmt =\n\t\t\t\t\t\tIsA(parsetree, CreateStmt) ? parsetree :\n\t\t\t\t\t\t(Node *) &(((CreateForeignTableStmt *) parsetree)->base);\n\t\t\t\t}\n\n\t\t\t\tConvertNewTableIfNecessary(createStmt);\n\t\t\t}\n\n\t\t\tif (context == PROCESS_UTILITY_TOPLEVEL &&\n\t\t\t\tIsA(parsetree, AlterObjectSchemaStmt))\n\t\t\t{\n\t\t\t\tAlterObjectSchemaStmt *alterSchemaStmt = castNode(AlterObjectSchemaStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  parsetree);\n\t\t\t\tif (alterSchemaStmt->objectType == OBJECT_TABLE ||\n\t\t\t\t\talterSchemaStmt->objectType == OBJECT_FOREIGN_TABLE)\n\t\t\t\t{\n\t\t\t\t\tConvertToTenantTableIfNecessary(alterSchemaStmt);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tUtilityHookLevel--;\n\t}\n\tPG_CATCH();\n\t{\n\t\tif (UtilityHookLevel == 1)\n\t\t{\n\t\t\tResetConstraintDropped();\n\t\t}\n\n\t\tUtilityHookLevel--;\n\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n}\n\n\n/*\n * citus_ProcessUtilityInternal is a helper function for citus_ProcessUtility where majority\n * of the Citus specific utility statements are handled here. The distinction between\n * both functions is that Citus_ProcessUtility does not handle CALL and DO statements.\n * The reason for the distinction is implemented to be able to find the \"top-level\" DDL\n * commands (not internal/cascading ones). UtilityHookLevel variable is used to achieve\n * this goal.\n */\nstatic void\ncitus_ProcessUtilityInternal(PlannedStmt *pstmt,\n\t\t\t\t\t\t\t const char *queryString,\n\t\t\t\t\t\t\t ProcessUtilityContext context,\n\t\t\t\t\t\t\t ParamListInfo params,\n\t\t\t\t\t\t\t struct QueryEnvironment *queryEnv,\n\t\t\t\t\t\t\t DestReceiver *dest,\n\t\t\t\t\t\t\t QueryCompletion *completionTag)\n{\n\tNode *parsetree = pstmt->utilityStmt;\n\tList *ddlJobs = NIL;\n\tDistOpsValidationState distOpsValidationState = HasNoneValidObject;\n\n\tif (IsA(parsetree, ExplainStmt) &&\n\t\tIsA(((ExplainStmt *) parsetree)->query, Query))\n\t{\n\t\tExplainStmt *explainStmt = (ExplainStmt *) parsetree;\n\n\t\tif (IsTransactionBlock())\n\t\t{\n\t\t\tbool analyze = false;\n\n\t\t\tDefElem *option = NULL;\n\t\t\tforeach_declared_ptr(option, explainStmt->options)\n\t\t\t{\n\t\t\t\tif (strcmp(option->defname, \"analyze\") == 0)\n\t\t\t\t{\n\t\t\t\t\tanalyze = defGetBoolean(option);\n\t\t\t\t}\n\n\t\t\t\t/* don't \"break\", as explain.c will use the last value */\n\t\t\t}\n\n\t\t\tif (analyze)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Since we cannot execute EXPLAIN ANALYZE locally, we\n\t\t\t\t * cannot continue.\n\t\t\t\t */\n\t\t\t\tErrorIfTransactionAccessedPlacementsLocally();\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * EXPLAIN ANALYZE is tricky with local execution, and there is not\n\t\t * much difference between the local and distributed execution in terms\n\t\t * of the actual EXPLAIN output.\n\t\t *\n\t\t * TODO: It might be nice to have a way to show that the query is locally\n\t\t * executed. Shall we add a INFO output?\n\t\t */\n\t\tDisableLocalExecution();\n\t}\n\n\tif (IsA(parsetree, CreateSubscriptionStmt))\n\t{\n\t\tCreateSubscriptionStmt *createSubStmt = (CreateSubscriptionStmt *) parsetree;\n\n\t\tparsetree = ProcessCreateSubscriptionStmt(createSubStmt);\n\t}\n\n\t/*\n\t * For security and reliability reasons we disallow altering and dropping\n\t * subscriptions created by citus by non superusers. We could probably\n\t * disallow this for all subscriptions without issues. But out of an\n\t * abundance of caution for breaking subscription logic created by users\n\t * for other purposes, we only disallow it for the subscriptions that we\n\t * create i.e. ones that start with \"citus_\".\n\t */\n\tif (IsA(parsetree, AlterSubscriptionStmt))\n\t{\n\t\tAlterSubscriptionStmt *alterSubStmt = (AlterSubscriptionStmt *) parsetree;\n\t\tif (!superuser() &&\n\t\t\tStringStartsWith(alterSubStmt->subname, \"citus_\"))\n\t\t{\n\t\t\tereport(ERROR, (\n\t\t\t\t\t\terrcode(ERRCODE_INSUFFICIENT_PRIVILEGE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Only superusers can alter subscriptions that are created by citus\")));\n\t\t}\n\t}\n\n\tif (IsA(parsetree, DropSubscriptionStmt))\n\t{\n\t\tDropSubscriptionStmt *dropSubStmt = (DropSubscriptionStmt *) parsetree;\n\t\tif (!superuser() &&\n\t\t\tStringStartsWith(dropSubStmt->subname, \"citus_\"))\n\t\t{\n\t\t\tereport(ERROR, (\n\t\t\t\t\t\terrcode(ERRCODE_INSUFFICIENT_PRIVILEGE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Only superusers can drop subscriptions that are created by citus\")));\n\t\t}\n\t}\n\n\t/*\n\t * Process SET LOCAL and SET TRANSACTION statements in multi-statement\n\t * transactions.\n\t */\n\tif (IsA(parsetree, VariableSetStmt))\n\t{\n\t\tVariableSetStmt *setStmt = (VariableSetStmt *) parsetree;\n\n\t\t/* at present, we only implement the NONE and LOCAL behaviors */\n\t\tAssert(PropagateSetCommands == PROPSETCMD_NONE ||\n\t\t\t   PropagateSetCommands == PROPSETCMD_LOCAL);\n\n\t\tif (IsMultiStatementTransaction() && ShouldPropagateSetCommand(setStmt))\n\t\t{\n\t\t\tPostprocessVariableSetStmt(setStmt, queryString);\n\t\t}\n\t}\n\n\tif (IsA(parsetree, CopyStmt))\n\t{\n\t\tMemoryContext planContext = GetMemoryChunkContext(parsetree);\n\n\t\tparsetree = copyObject(parsetree);\n\t\tparsetree = ProcessCopyStmt((CopyStmt *) parsetree, completionTag, queryString);\n\n\t\tif (parsetree == NULL)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tMemoryContext previousContext = MemoryContextSwitchTo(planContext);\n\t\tparsetree = copyObject(parsetree);\n\t\tMemoryContextSwitchTo(previousContext);\n\n\t\t/*\n\t\t * we need to set the parsetree here already as we copy and replace the original\n\t\t * parsetree during ddl propagation. In reality we need to refactor the code above\n\t\t * to not juggle copy the parsetree and leak it to a potential cache above the\n\t\t * utility hook.\n\t\t */\n\t\tpstmt->utilityStmt = parsetree;\n\t}\n\n\t/* we're mostly in DDL (and VACUUM/TRUNCATE) territory at this point... */\n\n\tif (IsA(parsetree, CreateSeqStmt))\n\t{\n\t\tErrorIfUnsupportedSeqStmt((CreateSeqStmt *) parsetree);\n\t}\n\n\tif (IsA(parsetree, AlterSeqStmt))\n\t{\n\t\tErrorIfDistributedAlterSeqOwnedBy((AlterSeqStmt *) parsetree);\n\t}\n\n\tif (IsA(parsetree, TruncateStmt))\n\t{\n\t\tPreprocessTruncateStatement((TruncateStmt *) parsetree);\n\t}\n\n\tif (IsA(parsetree, LockStmt))\n\t{\n\t\t/*\n\t\t * PreprocessLockStatement might lock the relations locally if the\n\t\t * node executing the command is in pg_dist_node. Even though the process\n\t\t * utility will re-acquire the locks across the same relations if the node\n\t\t * is in the metadata (in the pg_dist_node table) that should not be a problem,\n\t\t * plus it ensures consistent locking order between the nodes.\n\t\t */\n\t\tPreprocessLockStatement((LockStmt *) parsetree, context);\n\t}\n\n\t/*\n\t * We only process ALTER TABLE ... ATTACH PARTITION commands in the function below\n\t * and distribute the partition if necessary.\n\t */\n\tif (IsA(parsetree, AlterTableStmt))\n\t{\n\t\tAlterTableStmt *alterTableStatement = (AlterTableStmt *) parsetree;\n\n\t\tPreprocessAlterTableStmtAttachPartition(alterTableStatement, queryString);\n\t}\n\n\t/* only generate worker DDLJobs if propagation is enabled */\n\tconst DistributeObjectOps *ops = NULL;\n\tif (EnableDDLPropagation)\n\t{\n\t\t/* copy planned statement since we might scribble on it or its utilityStmt */\n\t\tpstmt = copyObject(pstmt);\n\t\tparsetree = pstmt->utilityStmt;\n\t\tops = GetDistributeObjectOps(parsetree);\n\n\t\t/*\n\t\t * Preprocess and qualify steps can cause pg tests to fail because of the\n\t\t * unwanted citus related warnings or early error logs related to invalid address.\n\t\t * Therefore, we first check if any address in the given statement is valid.\n\t\t * Then, we do not execute qualify and preprocess if none of the addresses are valid\n\t\t * or any address violates ownership rules to prevent before-mentioned citus related\n\t\t * messages. PG will complain about the invalid address or ownership violation, so we\n\t\t * are safe to not execute qualify and preprocess. Also note that we should not guard\n\t\t * any step after standardProcess_Utility with the enum state distOpsValidationState\n\t\t * because PG would have already failed the transaction.\n\t\t */\n\t\tdistOpsValidationState = DistOpsValidityState(parsetree, ops);\n\n\t\t/*\n\t\t * For some statements Citus defines a Qualify function. The goal of this function\n\t\t * is to take any ambiguity from the statement that is contextual on either the\n\t\t * search_path or current settings.\n\t\t * Instead of relying on the search_path and settings we replace any deduced bits\n\t\t * and fill them out how postgres would resolve them. This makes subsequent\n\t\t * deserialize calls for the statement portable to other postgres servers, the\n\t\t * workers in our case.\n\t\t * If there are no valid objects or any object violates ownership, let's skip\n\t\t * the qualify and preprocess, and do not diverge from Postgres in terms of\n\t\t * error messages.\n\t\t */\n\t\tif (ops && ops->qualify && DistOpsInValidState(distOpsValidationState))\n\t\t{\n\t\t\tops->qualify(parsetree);\n\t\t}\n\n\t\tif (ops && ops->preprocess && DistOpsInValidState(distOpsValidationState))\n\t\t{\n\t\t\tddlJobs = ops->preprocess(parsetree, queryString, context);\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * citus.enable_ddl_propagation is disabled, which means that PostgreSQL\n\t\t * should handle the DDL command on a distributed table directly, without\n\t\t * Citus intervening. The only exception is partition column drop, in\n\t\t * which case we error out. Advanced Citus users use this to implement their\n\t\t * own DDL propagation. We also use it to avoid re-propagating DDL commands\n\t\t * when changing MX tables on workers. Below, we also make sure that DDL\n\t\t * commands don't run queries that might get intercepted by Citus and error\n\t\t * out during planning, specifically we skip validation in foreign keys.\n\t\t */\n\n\t\tif (IsA(parsetree, AlterTableStmt))\n\t\t{\n\t\t\tAlterTableStmt *alterTableStmt = (AlterTableStmt *) parsetree;\n\t\t\tif (alterTableStmt->objtype == OBJECT_TABLE ||\n\t\t\t\talterTableStmt->objtype == OBJECT_FOREIGN_TABLE)\n\t\t\t{\n\t\t\t\tErrorIfAlterDropsPartitionColumn(alterTableStmt);\n\n\t\t\t\t/*\n\t\t\t\t * When issuing an ALTER TABLE ... ADD FOREIGN KEY command, the\n\t\t\t\t * the validation step should be skipped on the distributed table.\n\t\t\t\t * Therefore, we check whether the given ALTER TABLE statement is a\n\t\t\t\t * FOREIGN KEY constraint and if so disable the validation step.\n\t\t\t\t * Note validation is done on the shard level when DDL propagation\n\t\t\t\t * is enabled. The following eagerly executes some tasks on workers.\n\t\t\t\t */\n\t\t\t\tSkipForeignKeyValidationIfConstraintIsFkey(alterTableStmt, false);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * If we've explicitly set the citus.skip_constraint_validation GUC, then\n\t * we skip validation of any added constraints.\n\t */\n\tif (IsA(parsetree, AlterTableStmt) && SkipConstraintValidation)\n\t{\n\t\tAlterTableStmt *alterTableStmt = (AlterTableStmt *) parsetree;\n\t\tAlterTableCmd *command = NULL;\n\t\tforeach_declared_ptr(command, alterTableStmt->cmds)\n\t\t{\n\t\t\tAlterTableType alterTableType = command->subtype;\n\n\t\t\t/*\n\t\t\t * XXX: In theory we could probably use this GUC to skip validation\n\t\t\t * of VALIDATE CONSTRAINT and ALTER CONSTRAINT too. But currently\n\t\t\t * this is not needed, so we make its behaviour only apply to ADD\n\t\t\t * CONSTRAINT.\n\t\t\t */\n\t\t\tif (alterTableType == AT_AddConstraint)\n\t\t\t{\n\t\t\t\tConstraint *constraint = (Constraint *) command->def;\n\t\t\t\tconstraint->skip_validation = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* inform the user about potential caveats */\n\tif (IsA(parsetree, CreatedbStmt) && !EnableCreateDatabasePropagation)\n\t{\n\t\tif (EnableUnsupportedFeatureMessages)\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\"Citus partially supports CREATE DATABASE for \"\n\t\t\t\t\t\t\t\t\t\"distributed databases\"),\n\t\t\t\t\t\t\t errdetail(\"Citus does not propagate CREATE DATABASE \"\n\t\t\t\t\t\t\t\t\t   \"command to other nodes\"),\n\t\t\t\t\t\t\t errhint(\"You can manually create a database and its \"\n\t\t\t\t\t\t\t\t\t \"extensions on other nodes.\")));\n\t\t}\n\t}\n\telse if (IsA(parsetree, CreateRoleStmt) && !EnableCreateRolePropagation)\n\t{\n\t\tereport(NOTICE, (errmsg(\"not propagating CREATE ROLE/USER commands to other\"\n\t\t\t\t\t\t\t\t\" nodes\"),\n\t\t\t\t\t\t errhint(\"Connect to other nodes directly to manually create all\"\n\t\t\t\t\t\t\t\t \" necessary users and roles.\")));\n\t}\n\telse if (IsA(parsetree, SecLabelStmt) && !EnableAlterRolePropagation)\n\t{\n\t\tereport(NOTICE, (errmsg(\"not propagating SECURITY LABEL commands to other\"\n\t\t\t\t\t\t\t\t\" nodes\"),\n\t\t\t\t\t\t errhint(\"Connect to other nodes directly to manually assign\"\n\t\t\t\t\t\t\t\t \" necessary labels.\")));\n\t}\n\n\t/*\n\t * Make sure that on DROP EXTENSION we terminate the background daemon\n\t * associated with it.\n\t */\n\tif (IsDropCitusExtensionStmt(parsetree))\n\t{\n\t\tStopMaintenanceDaemon(MyDatabaseId);\n\t}\n\n\t/*\n\t * Make sure that dropping node-wide objects deletes the pg_dist_object\n\t * entries. There is a separate logic for node-wide objects (such as role\n\t * and databases), since they are not included as dropped objects in the\n\t * drop event trigger. To handle it both on worker and coordinator nodes,\n\t * it is not implemented as a part of process functions but here.\n\t */\n\tUnmarkNodeWideObjectsDistributed(parsetree);\n\n\tpstmt->utilityStmt = parsetree;\n\n\tPG_TRY();\n\t{\n\t\tIncrementUtilityHookCountersIfNecessary(parsetree);\n\n\t\t/*\n\t\t * Check if we are running ALTER EXTENSION citus UPDATE (TO \"<version>\") command and\n\t\t * the available version is different than the current version of Citus. In this case,\n\t\t * ALTER EXTENSION citus UPDATE command can actually update Citus to a new version.\n\t\t */\n\t\tbool isCreateAlterExtensionUpdateCitusStmt =\n\t\t\tIsCreateAlterExtensionUpdateCitusStmt(parsetree);\n\t\tbool isAlterExtensionUpdateCitusStmt = isCreateAlterExtensionUpdateCitusStmt &&\n\t\t\t\t\t\t\t\t\t\t\t   IsA(parsetree, AlterExtensionStmt);\n\n\t\tbool citusCanBeUpdatedToAvailableVersion = false;\n\n\t\tif (isAlterExtensionUpdateCitusStmt)\n\t\t{\n\t\t\tcitusCanBeUpdatedToAvailableVersion = !InstalledAndAvailableVersionsSame();\n\n\t\t\t/*\n\t\t\t * Check whether need to install/drop citus_columnar when upgrade/downgrade citus\n\t\t\t */\n\t\t\tPreprocessAlterExtensionCitusStmtForCitusColumnar(parsetree);\n\t\t}\n\n\t\tPrevProcessUtility(pstmt, queryString, false, context,\n\t\t\t\t\t\t   params, queryEnv, dest, completionTag);\n\n\t\tif (isAlterExtensionUpdateCitusStmt)\n\t\t{\n\t\t\t/*\n\t\t\t * Post process, upgrade citus_columnar from fake internal version to normal version if upgrade citus\n\t\t\t * or drop citus_columnar fake version when downgrade citus to older version that do not support\n\t\t\t * citus_columnar\n\t\t\t */\n\t\t\tPostprocessAlterExtensionCitusStmtForCitusColumnar(parsetree);\n\t\t}\n\n\t\t/*\n\t\t * if we are running ALTER EXTENSION citus UPDATE (to \"<version>\") command, we may need\n\t\t * to mark existing objects as distributed depending on the \"version\" parameter if\n\t\t * specified in \"ALTER EXTENSION citus UPDATE\" command\n\t\t */\n\t\tif (isAlterExtensionUpdateCitusStmt && citusCanBeUpdatedToAvailableVersion)\n\t\t{\n\t\t\tPostprocessAlterExtensionCitusUpdateStmt(parsetree);\n\t\t}\n\n\t\tPostStandardProcessUtility(parsetree);\n\t}\n\tPG_CATCH();\n\t{\n\t\tPostStandardProcessUtility(parsetree);\n\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n\n\t/*\n\t * Post process for ddl statements\n\t */\n\tif (EnableDDLPropagation)\n\t{\n\t\tif (ops && ops->postprocess)\n\t\t{\n\t\t\tList *processJobs = ops->postprocess(parsetree, queryString);\n\n\t\t\tif (processJobs)\n\t\t\t{\n\t\t\t\tAssert(ddlJobs == NIL); /* jobs should not have been set before */\n\t\t\t\tddlJobs = processJobs;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (IsA(parsetree, CreateStmt))\n\t{\n\t\tCreateStmt *createStatement = (CreateStmt *) parsetree;\n\n\t\tPostprocessCreateTableStmt(createStatement, queryString);\n\t}\n\n\tif (IsA(parsetree, CreateForeignTableStmt))\n\t{\n\t\tCreateForeignTableStmt *createForeignTableStmt =\n\t\t\t(CreateForeignTableStmt *) parsetree;\n\n\t\tCreateStmt *createTableStmt = (CreateStmt *) (&createForeignTableStmt->base);\n\n\n\t\tPostprocessCreateTableStmt(createTableStmt, queryString);\n\t}\n\n\t/* after local command has completed, finish by executing worker DDLJobs, if any */\n\tif (ddlJobs != NIL)\n\t{\n\t\tif (IsA(parsetree, AlterTableStmt))\n\t\t{\n\t\t\tPostprocessAlterTableStmt(castNode(AlterTableStmt, parsetree));\n\t\t}\n\t\tif (IsA(parsetree, GrantStmt))\n\t\t{\n\t\t\tGrantStmt *grantStmt = (GrantStmt *) parsetree;\n\t\t\tif (grantStmt->targtype == ACL_TARGET_ALL_IN_SCHEMA)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Grant .. IN SCHEMA causes a deadlock if we don't use local execution\n\t\t\t\t * because standard process utility processes the shard placements as well\n\t\t\t\t * and the row-level locks in pg_class will not be released until the current\n\t\t\t\t * transaction commits. We could skip the local shard placements after standard\n\t\t\t\t * process utility, but for simplicity we just prefer using local execution.\n\t\t\t\t */\n\t\t\t\tSetLocalExecutionStatus(LOCAL_EXECUTION_REQUIRED);\n\t\t\t}\n\t\t}\n\n\t\tDDLJob *ddlJob = NULL;\n\t\tforeach_declared_ptr(ddlJob, ddlJobs)\n\t\t{\n\t\t\tExecuteDistributedDDLJob(ddlJob);\n\t\t}\n\n\t\tif (IsA(parsetree, AlterTableStmt))\n\t\t{\n\t\t\t/*\n\t\t\t * This postprocess needs to be done after the distributed ddl jobs have\n\t\t\t * been executed in the workers, hence is separate from PostprocessAlterTableStmt.\n\t\t\t * We might have wrong index names generated on indexes of shards of partitions,\n\t\t\t * so we perform the relevant checks and index renaming here.\n\t\t\t */\n\t\t\tFixAlterTableStmtIndexNames(castNode(AlterTableStmt, parsetree));\n\t\t}\n\n\t\t/*\n\t\t * For CREATE/DROP/REINDEX CONCURRENTLY we mark the index as valid\n\t\t * after successfully completing the distributed DDL job.\n\t\t */\n\t\tif (IsA(parsetree, IndexStmt))\n\t\t{\n\t\t\tIndexStmt *indexStmt = (IndexStmt *) parsetree;\n\n\t\t\tif (indexStmt->concurrent)\n\t\t\t{\n\t\t\t\t/* no failures during CONCURRENTLY, mark the index as valid */\n\t\t\t\tMarkIndexValid(indexStmt);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * We make sure schema name is not null in the PreprocessIndexStmt.\n\t\t\t */\n\t\t\tOid schemaId = get_namespace_oid(indexStmt->relation->schemaname, true);\n\t\t\tOid relationId = get_relname_relid(indexStmt->relation->relname, schemaId);\n\n\t\t\t/*\n\t\t\t * If this a partitioned table, and CREATE INDEX was not run with ONLY,\n\t\t\t * we have wrong index names generated on indexes of shards of\n\t\t\t * partitions of this table, so we should fix them.\n\t\t\t */\n\t\t\tif (IsCitusTable(relationId) && PartitionedTable(relationId) &&\n\t\t\t\tindexStmt->relation->inh)\n\t\t\t{\n\t\t\t\t/* only fix this specific index */\n\t\t\t\tOid indexRelationId =\n\t\t\t\t\tget_relname_relid(indexStmt->idxname, schemaId);\n\n\t\t\t\tFixPartitionShardIndexNames(relationId, indexRelationId);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Since we must have objects on workers before distributing them,\n\t\t * mark object distributed as the last step.\n\t\t */\n\t\tif (ops && ops->markDistributed)\n\t\t{\n\t\t\tList *addresses = GetObjectAddressListFromParseTree(parsetree, false, true);\n\t\t\tObjectAddress *address = NULL;\n\t\t\tforeach_declared_ptr(address, addresses)\n\t\t\t{\n\t\t\t\tMarkObjectDistributed(address);\n\t\t\t\tTrackPropagatedObject(address);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * UndistributeDisconnectedCitusLocalTables undistributes citus local tables that\n * are not connected to any reference tables via their individual foreign key\n * subgraphs. Note that this function undistributes only the auto-converted tables,\n * i.e the ones that are converted by Citus by cascading through foreign keys.\n */\nvoid\nUndistributeDisconnectedCitusLocalTables(void)\n{\n\tList *citusLocalTableIdList = CitusTableTypeIdList(CITUS_LOCAL_TABLE);\n\tcitusLocalTableIdList = SortList(citusLocalTableIdList, CompareOids);\n\n\tOid citusLocalTableId = InvalidOid;\n\tforeach_declared_oid(citusLocalTableId, citusLocalTableIdList)\n\t{\n\t\t/* acquire ShareRowExclusiveLock to prevent concurrent foreign key creation */\n\t\tLOCKMODE lockMode = ShareRowExclusiveLock;\n\t\tLockRelationOid(citusLocalTableId, lockMode);\n\n\t\tHeapTuple heapTuple =\n\t\t\tSearchSysCache1(RELOID, ObjectIdGetDatum(citusLocalTableId));\n\t\tif (!HeapTupleIsValid(heapTuple))\n\t\t{\n\t\t\t/*\n\t\t\t * UndistributeTable drops relation, skip if already undistributed\n\t\t\t * via cascade.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\t\tReleaseSysCache(heapTuple);\n\n\t\tif (PartitionTable(citusLocalTableId))\n\t\t{\n\t\t\t/* we skip here, we'll undistribute from the parent if necessary */\n\t\t\tUnlockRelationOid(citusLocalTableId, lockMode);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!ShouldUndistributeCitusLocalTable(citusLocalTableId))\n\t\t{\n\t\t\t/* still connected to a reference table, skip it */\n\t\t\tUnlockRelationOid(citusLocalTableId, lockMode);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Citus local table is not connected to any reference tables, then\n\t\t * undistribute it via cascade. Here, instead of first dropping foreing\n\t\t * keys then undistributing the table, we just set cascadeViaForeignKeys\n\t\t * to true for simplicity.\n\t\t *\n\t\t * We suppress notices messages not to be too verbose. On the other hand,\n\t\t * as UndistributeTable moves data to a new table, we want to inform user\n\t\t * as it might take some time.\n\t\t */\n\t\tereport(NOTICE, (errmsg(\"removing table %s from metadata as it is not \"\n\t\t\t\t\t\t\t\t\"connected to any reference tables via foreign keys\",\n\t\t\t\t\t\t\t\tgenerate_qualified_relation_name(citusLocalTableId))));\n\t\tTableConversionParameters params = {\n\t\t\t.relationId = citusLocalTableId,\n\t\t\t.cascadeViaForeignKeys = true,\n\t\t\t.suppressNoticeMessages = true,\n\t\t\t.bypassTenantCheck = false\n\t\t};\n\t\tUndistributeTable(&params);\n\t}\n}\n\n\n/*\n * ShouldCheckUndistributeCitusLocalTables returns true if we might need to check\n * citus local tables for undistributing automatically.\n */\nstatic bool\nShouldCheckUndistributeCitusLocalTables(void)\n{\n\tif (!ConstraintDropped)\n\t{\n\t\t/*\n\t\t * citus_drop_trigger executes notify_constraint_dropped to set\n\t\t * ConstraintDropped to true, which means that last command dropped\n\t\t * a table constraint.\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (!CitusHasBeenLoaded())\n\t{\n\t\t/*\n\t\t * If we are dropping citus, we should not try to undistribute citus\n\t\t * local tables as they will also be dropped.\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (!InCoordinatedTransaction())\n\t{\n\t\t/* not interacting with any Citus objects */\n\t\treturn false;\n\t}\n\n\tif (IsCitusInternalBackend() || IsRebalancerInternalBackend())\n\t{\n\t\t/* connection from the coordinator operating on a shard */\n\t\treturn false;\n\t}\n\n\tif (!ShouldEnableLocalReferenceForeignKeys())\n\t{\n\t\t/*\n\t\t * If foreign keys between reference tables and local tables are\n\t\t * disabled, then user might be using citus_add_local_table_to_metadata for\n\t\t * their own purposes. In that case, we should not undistribute\n\t\t * citus local tables.\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (!IsCoordinator())\n\t{\n\t\t/* we should not perform this operation in worker nodes */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * NotifyUtilityHookConstraintDropped sets ConstraintDropped to true to tell us\n * last command dropped a table constraint.\n */\nvoid\nNotifyUtilityHookConstraintDropped(void)\n{\n\tConstraintDropped = true;\n}\n\n\n/*\n * ResetConstraintDropped sets ConstraintDropped to false.\n */\nvoid\nResetConstraintDropped(void)\n{\n\tConstraintDropped = false;\n}\n\n\n/*\n * IsDropSchemaOrDB returns true if parsetree represents DROP SCHEMA ...or\n * a DROP DATABASE.\n */\nstatic bool\nIsDropSchemaOrDB(Node *parsetree)\n{\n\tif (!IsA(parsetree, DropStmt))\n\t{\n\t\treturn false;\n\t}\n\n\tDropStmt *dropStatement = (DropStmt *) parsetree;\n\treturn (dropStatement->removeType == OBJECT_SCHEMA) ||\n\t\t   (dropStatement->removeType == OBJECT_DATABASE);\n}\n\n\n/*\n * ExecuteDistributedDDLJob simply executes a provided DDLJob in a distributed trans-\n * action, including metadata sync if needed. If the multi shard commit protocol is\n * in its default value of '1pc', then a notice message indicating that '2pc' might be\n * used for extra safety. In the commit protocol, a BEGIN is sent after connection to\n * each shard placement and COMMIT/ROLLBACK is handled by\n * CoordinatedTransactionCallback function.\n *\n * The function errors out if the DDL is on a partitioned table which has replication\n * factor > 1, or if the the coordinator is not added into metadata and we're on a\n * worker node because we want to make sure that distributed DDL jobs are executed\n * on the coordinator node too. See EnsurePropagationToCoordinator() for more details.\n */\nvoid\nExecuteDistributedDDLJob(DDLJob *ddlJob)\n{\n\tbool shouldSyncMetadata = false;\n\n\tEnsurePropagationToCoordinator();\n\n\tObjectAddress targetObjectAddress = ddlJob->targetObjectAddress;\n\n\tif (OidIsValid(targetObjectAddress.classId))\n\t{\n\t\t/*\n\t\t * Only for ddlJobs that are targetting an object we want to sync\n\t\t * its metadata.\n\t\t */\n\t\tshouldSyncMetadata = ShouldSyncUserCommandForObject(targetObjectAddress);\n\n\t\tif (targetObjectAddress.classId == RelationRelationId)\n\t\t{\n\t\t\tEnsurePartitionTableNotReplicated(targetObjectAddress.objectId);\n\t\t}\n\t}\n\n\tbool localExecutionSupported = true;\n\n\tif (!TaskListCannotBeExecutedInTransaction(ddlJob->taskList))\n\t{\n\t\tif (shouldSyncMetadata)\n\t\t{\n\t\t\tSendCommandToRemoteNodesWithMetadata(DISABLE_DDL_PROPAGATION);\n\n\t\t\tchar *currentSearchPath = CurrentSearchPath();\n\n\t\t\t/*\n\t\t\t * Given that we're relaying the query to the remote nodes directly,\n\t\t\t * we should set the search path exactly the same when necessary.\n\t\t\t */\n\t\t\tif (currentSearchPath != NULL)\n\t\t\t{\n\t\t\t\tSendCommandToRemoteNodesWithMetadata(\n\t\t\t\t\tpsprintf(\"SET LOCAL search_path TO %s;\", currentSearchPath));\n\t\t\t}\n\n\t\t\tif (ddlJob->metadataSyncCommand != NULL)\n\t\t\t{\n\t\t\t\tSendCommandToRemoteNodesWithMetadata(\n\t\t\t\t\t(char *) ddlJob->metadataSyncCommand);\n\t\t\t}\n\t\t}\n\n\t\tExecuteUtilityTaskList(ddlJob->taskList, localExecutionSupported);\n\t}\n\telse\n\t{\n\t\tlocalExecutionSupported = false;\n\n\t\t/*\n\t\t * Start a new transaction to make sure CONCURRENTLY commands\n\t\t * on localhost do not block waiting for this transaction to finish.\n\t\t *\n\t\t * In addition to doing that, we also need to tell other backends\n\t\t * --including the ones spawned for connections opened to localhost to\n\t\t * build indexes on shards of this relation-- that concurrent index\n\t\t * builds can safely ignore us.\n\t\t *\n\t\t * Normally, DefineIndex() only does that if index doesn't have any\n\t\t * predicates (i.e.: where clause) and no index expressions at all.\n\t\t * However, now that we already called standard process utility,\n\t\t * index build on the shell table is finished anyway.\n\t\t *\n\t\t * The reason behind doing so is that we cannot guarantee not\n\t\t * grabbing any snapshots via adaptive executor, and the backends\n\t\t * creating indexes on local shards (if any) might block on waiting\n\t\t * for current xact of the current backend to finish, which would\n\t\t * cause self deadlocks that are not detectable.\n\t\t */\n\t\tif (ddlJob->startNewTransaction)\n\t\t{\n\t\t\t/*\n\t\t\t * Since it is not certain whether the code-path that we followed\n\t\t\t * until reaching here caused grabbing any snapshots or not, we\n\t\t\t * need to pop the active snapshot if we had any, to ensure not\n\t\t\t * leaking any snapshots.\n\t\t\t *\n\t\t\t * For example, EnsureCoordinator might return without grabbing\n\t\t\t * any snapshots if we didn't receive any invalidation messages\n\t\t\t * but the otherwise is also possible.\n\t\t\t */\n\t\t\tif (ActiveSnapshotSet())\n\t\t\t{\n\t\t\t\tPopActiveSnapshot();\n\t\t\t}\n\n\t\t\tCommitTransactionCommand();\n\t\t\tStartTransactionCommand();\n\n\t\t\t/*\n\t\t\t * Tell other backends to ignore us, even if we grab any\n\t\t\t * snapshots via adaptive executor.\n\t\t\t */\n\t\t\tset_indexsafe_procflags();\n\n\t\t\t/*\n\t\t\t * We should not have any CREATE INDEX commands go through the\n\t\t\t * local backend as we signaled other backends that this backend\n\t\t\t * is executing a \"safe\" index command (PROC_IN_SAFE_IC), which\n\t\t\t * is NOT true, we are only faking postgres based on the reasoning\n\t\t\t * given above.\n\t\t\t */\n\t\t\tAssert(localExecutionSupported == false);\n\t\t}\n\n\t\tMemoryContext savedContext = CurrentMemoryContext;\n\n\t\tPG_TRY();\n\t\t{\n\t\t\tExecuteUtilityTaskList(ddlJob->taskList, localExecutionSupported);\n\n\t\t\tif (shouldSyncMetadata)\n\t\t\t{\n\t\t\t\tList *commandList = list_make1(DISABLE_DDL_PROPAGATION);\n\n\t\t\t\tchar *currentSearchPath = CurrentSearchPath();\n\n\t\t\t\t/*\n\t\t\t\t * Given that we're relaying the query to the remote nodes directly,\n\t\t\t\t * we should set the search path exactly the same when necessary.\n\t\t\t\t */\n\t\t\t\tif (currentSearchPath != NULL)\n\t\t\t\t{\n\t\t\t\t\tcommandList = lappend(commandList,\n\t\t\t\t\t\t\t\t\t\t  psprintf(\"SET search_path TO %s;\",\n\t\t\t\t\t\t\t\t\t\t\t\t   currentSearchPath));\n\t\t\t\t}\n\n\t\t\t\tcommandList = lappend(commandList, (char *) ddlJob->metadataSyncCommand);\n\n\t\t\t\tSendBareCommandListToRemoteMetadataNodes(commandList);\n\t\t\t}\n\t\t}\n\t\tPG_CATCH();\n\t\t{\n\t\t\t/* CopyErrorData() requires (CurrentMemoryContext != ErrorContext) */\n\t\t\tMemoryContextSwitchTo(savedContext);\n\t\t\tErrorData *edata = CopyErrorData();\n\n\t\t\t/*\n\t\t\t * In concurrent index creation, if a worker index with the same name already\n\t\t\t * exists, prompt to DROP the current index and retry the original command\n\t\t\t */\n\t\t\tif (edata->sqlerrcode == ERRCODE_DUPLICATE_TABLE)\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errmsg(\"CONCURRENTLY-enabled index command failed\"),\n\t\t\t\t\t\t errdetail(\n\t\t\t\t\t\t\t \"CONCURRENTLY-enabled index commands can fail partially, \"\n\t\t\t\t\t\t\t \"leaving behind an INVALID index.\"),\n\t\t\t\t\t\t errhint(\"Use DROP INDEX CONCURRENTLY IF EXISTS to remove the \"\n\t\t\t\t\t\t\t\t \"invalid index, then retry the original command.\")));\n\t\t\t}\n\t\t\telse if (ddlJob->warnForPartialFailure)\n\t\t\t{\n\t\t\t\tereport(WARNING,\n\t\t\t\t\t\t(errmsg(\n\t\t\t\t\t\t\t \"Commands that are not transaction-safe may result in \"\n\t\t\t\t\t\t\t \"partial failure, potentially leading to an inconsistent \"\n\t\t\t\t\t\t\t \"state.\\nIf the problematic command is a CREATE operation, \"\n\t\t\t\t\t\t\t \"consider using the 'IF EXISTS' syntax to drop the object,\"\n\t\t\t\t\t\t\t \"\\nif applicable, and then re-attempt \"\n\t\t\t\t\t\t\t \"the original command.\")));\n\t\t\t}\n\n\t\t\tPG_RE_THROW();\n\t\t}\n\t\tPG_END_TRY();\n\t}\n}\n\n\n/*\n * set_indexsafe_procflags sets PROC_IN_SAFE_IC flag in MyProc->statusFlags.\n *\n * The flag is reset automatically at transaction end, so it must be set\n * for each transaction.\n *\n * Copied from pg/src/backend/commands/indexcmds.c\n * Also see pg commit c98763bf51bf610b3ee7e209fc76c3ff9a6b3163.\n */\nstatic void\nset_indexsafe_procflags(void)\n{\n\tAssert(MyProc->xid == InvalidTransactionId &&\n\t\t   MyProc->xmin == InvalidTransactionId);\n\n\tLWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);\n\tMyProc->statusFlags |= PROC_IN_SAFE_IC;\n\tProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;\n\tLWLockRelease(ProcArrayLock);\n}\n\n\n/*\n * CurrentSearchPath is a C interface for calling current_schemas(bool) that\n * PostgreSQL exports.\n *\n * CurrentSchemas returns all the schemas in the seach_path that are seperated\n * with comma (,) sign. The returned string can be used to set the search_path.\n *\n * The function omits implicit schemas.\n *\n * The function returns NULL if there are no valid schemas in the search_path,\n * mimicing current_schemas(false) function.\n */\nstatic char *\nCurrentSearchPath(void)\n{\n\tStringInfo currentSearchPath = makeStringInfo();\n\tList *searchPathList = fetch_search_path(false);\n\tbool schemaAdded = false;\n\n\tOid searchPathOid = InvalidOid;\n\tforeach_declared_oid(searchPathOid, searchPathList)\n\t{\n\t\tchar *schemaName = get_namespace_name(searchPathOid);\n\n\t\t/* watch out for deleted namespace */\n\t\tif (schemaName)\n\t\t{\n\t\t\tif (schemaAdded)\n\t\t\t{\n\t\t\t\tappendStringInfoString(currentSearchPath, \",\");\n\t\t\t\tschemaAdded = false;\n\t\t\t}\n\n\t\t\tappendStringInfoString(currentSearchPath, quote_identifier(schemaName));\n\t\t\tschemaAdded = true;\n\t\t}\n\t}\n\n\t/* fetch_search_path() returns a palloc'd list that we should free now */\n\tlist_free(searchPathList);\n\n\treturn (currentSearchPath->len > 0 ? currentSearchPath->data : NULL);\n}\n\n\n/*\n * IncrementUtilityHookCountersIfNecessary increments activeAlterTables and\n * activeDropSchemaOrDBs counters if utility command being processed implies\n * to do so.\n */\nstatic void\nIncrementUtilityHookCountersIfNecessary(Node *parsetree)\n{\n\tif (IsA(parsetree, AlterTableStmt))\n\t{\n\t\tactiveAlterTables++;\n\t}\n\n\tif (IsDropSchemaOrDB(parsetree))\n\t{\n\t\tactiveDropSchemaOrDBs++;\n\t}\n}\n\n\n/*\n * PostStandardProcessUtility performs operations to alter (backend) global\n * state of citus utility hook. Those operations should be done after standard\n * process utility executes even if it errors out.\n */\nstatic void\nPostStandardProcessUtility(Node *parsetree)\n{\n\tDecrementUtilityHookCountersIfNecessary(parsetree);\n\n\t/*\n\t * Re-forming the foreign key graph relies on the command being executed\n\t * on the local table first. However, in order to decide whether the\n\t * command leads to an invalidation, we need to check before the command\n\t * is being executed since we read pg_constraint table. Thus, we maintain a\n\t * local flag and do the invalidation after citus_ProcessUtility,\n\t * before ExecuteDistributedDDLJob().\n\t */\n\tInvalidateForeignKeyGraphForDDL();\n}\n\n\n/*\n * DecrementUtilityHookCountersIfNecessary decrements activeAlterTables and\n * activeDropSchemaOrDBs counters if utility command being processed implies\n * to do so.\n */\nstatic void\nDecrementUtilityHookCountersIfNecessary(Node *parsetree)\n{\n\tif (IsA(parsetree, AlterTableStmt))\n\t{\n\t\tactiveAlterTables--;\n\t}\n\n\tif (IsDropSchemaOrDB(parsetree))\n\t{\n\t\tactiveDropSchemaOrDBs--;\n\t}\n}\n\n\n/*\n * MarkInvalidateForeignKeyGraph marks whether the foreign key graph should be\n * invalidated due to a DDL.\n */\nvoid\nMarkInvalidateForeignKeyGraph()\n{\n\tshouldInvalidateForeignKeyGraph = true;\n}\n\n\n/*\n * InvalidateForeignKeyGraphForDDL simply keeps track of whether\n * the foreign key graph should be invalidated due to a DDL.\n */\nvoid\nInvalidateForeignKeyGraphForDDL(void)\n{\n\tif (shouldInvalidateForeignKeyGraph)\n\t{\n\t\tInvalidateForeignKeyGraph();\n\n\t\tshouldInvalidateForeignKeyGraph = false;\n\t}\n}\n\n\n/*\n * DDLTaskList builds a list of tasks to execute a DDL command on a\n * given list of shards.\n */\nList *\nDDLTaskList(Oid relationId, const char *commandString)\n{\n\tList *taskList = NIL;\n\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\tOid schemaId = get_rel_namespace(relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\tchar *escapedSchemaName = quote_literal_cstr(schemaName);\n\tchar *escapedCommandString = quote_literal_cstr(commandString);\n\tuint64 jobId = INVALID_JOB_ID;\n\tint taskId = 1;\n\n\t/* lock metadata before getting placement lists */\n\tLockShardListMetadata(shardIntervalList, ShareLock);\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\t\tStringInfo applyCommand = makeStringInfo();\n\n\t\t/*\n\t\t * If rightRelationId is not InvalidOid, instead of worker_apply_shard_ddl_command\n\t\t * we use worker_apply_inter_shard_ddl_command.\n\t\t */\n\t\tappendStringInfo(applyCommand, WORKER_APPLY_SHARD_DDL_COMMAND, shardId,\n\t\t\t\t\t\t escapedSchemaName, escapedCommandString);\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->jobId = jobId;\n\t\ttask->taskId = taskId++;\n\t\ttask->taskType = DDL_TASK;\n\t\tSetTaskQueryString(task, applyCommand->data);\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\ttask->dependentTaskList = NULL;\n\t\ttask->anchorShardId = shardId;\n\t\ttask->taskPlacementList = ActiveShardPlacementList(shardId);\n\n\t\ttaskList = lappend(taskList, task);\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * NontransactionalNodeDDLTaskList builds a list of tasks to execute a DDL command on a\n * given target set of nodes with cannotBeExecutedInTransaction is set to make sure\n * that task list is executed outside a transaction block.\n *\n * Also sets warnForPartialFailure for the returned DDLJobs.\n */\nList *\nNontransactionalNodeDDLTaskList(TargetWorkerSet targets, List *commands,\n\t\t\t\t\t\t\t\tbool warnForPartialFailure)\n{\n\tList *ddlJobs = NodeDDLTaskList(targets, commands);\n\tDDLJob *ddlJob = NULL;\n\tforeach_declared_ptr(ddlJob, ddlJobs)\n\t{\n\t\tTask *task = NULL;\n\t\tforeach_declared_ptr(task, ddlJob->taskList)\n\t\t{\n\t\t\ttask->cannotBeExecutedInTransaction = true;\n\t\t}\n\n\t\tddlJob->warnForPartialFailure = warnForPartialFailure;\n\t}\n\treturn ddlJobs;\n}\n\n\n/*\n * NodeDDLTaskList builds a list of tasks to execute a DDL command on a\n * given target set of nodes.\n */\nList *\nNodeDDLTaskList(TargetWorkerSet targets, List *commands)\n{\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tddlJob->targetObjectAddress = InvalidObjectAddress;\n\tddlJob->metadataSyncCommand = NULL;\n\n\t/* don't allow concurrent node list changes that require an exclusive lock */\n\tList *workerNodes = TargetWorkerSetNodeList(targets, RowShareLock);\n\n\t/*\n\t * if there are no nodes we don't have to plan any ddl tasks. Planning them would\n\t * cause the executor to stop responding.\n\t */\n\tif (list_length(workerNodes) > 0)\n\t{\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->taskType = DDL_TASK;\n\t\tSetTaskQueryStringList(task, commands);\n\n\t\tWorkerNode *workerNode = NULL;\n\t\tforeach_declared_ptr(workerNode, workerNodes)\n\t\t{\n\t\t\tShardPlacement *targetPlacement = CitusMakeNode(ShardPlacement);\n\t\t\ttargetPlacement->nodeName = workerNode->workerName;\n\t\t\ttargetPlacement->nodePort = workerNode->workerPort;\n\t\t\ttargetPlacement->groupId = workerNode->groupId;\n\n\t\t\ttask->taskPlacementList = lappend(task->taskPlacementList, targetPlacement);\n\t\t}\n\n\t\tddlJob->taskList = list_make1(task);\n\t}\n\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * AlterTableInProgress returns true if we're processing an ALTER TABLE command\n * right now.\n */\nbool\nAlterTableInProgress(void)\n{\n\treturn activeAlterTables > 0;\n}\n\n\n/*\n * DropSchemaOrDBInProgress returns true if we're processing a DROP SCHEMA\n * or a DROP DATABASE command right now.\n */\nbool\nDropSchemaOrDBInProgress(void)\n{\n\treturn activeDropSchemaOrDBs > 0;\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/vacuum.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * vacuum.c\n *    Commands for vacuuming distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/xact.h\"\n#include \"commands/defrem.h\"\n#include \"commands/vacuum.h\"\n#include \"postmaster/bgworker_internals.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/version_compat.h\"\n\n\n#define VACUUM_PARALLEL_NOTSET -2\n\n/*\n * Subset of VacuumParams we care about\n */\ntypedef struct CitusVacuumParams\n{\n\tint options;\n\tVacOptValue truncate;\n\tVacOptValue index_cleanup;\n\tint nworkers;\n\tint ring_size;\n} CitusVacuumParams;\n\n/*\n * Information we track per VACUUM/ANALYZE target relation.\n */\ntypedef struct CitusVacuumRelation\n{\n\tVacuumRelation *vacuumRelation;\n\tOid relationId;\n} CitusVacuumRelation;\n\n/* Local functions forward declarations for processing distributed table commands */\nstatic bool IsDistributedVacuumStmt(List *vacuumRelationList);\nstatic List * VacuumTaskList(Oid relationId, CitusVacuumParams vacuumParams,\n\t\t\t\t\t\t\t List *vacuumColumnList);\nstatic char * DeparseVacuumStmtPrefix(CitusVacuumParams vacuumParams);\nstatic char * DeparseVacuumColumnNames(List *columnNameList);\nstatic void ExecuteVacuumOnDistributedTables(VacuumStmt *vacuumStmt, List *relationList,\n\t\t\t\t\t\t\t\t\t\t\t CitusVacuumParams vacuumParams);\nstatic void ExecuteUnqualifiedVacuumTasks(VacuumStmt *vacuumStmt,\n\t\t\t\t\t\t\t\t\t\t  CitusVacuumParams vacuumParams);\nstatic CitusVacuumParams VacuumStmtParams(VacuumStmt *vacstmt);\nstatic List * VacuumRelationList(VacuumStmt *vacuumStmt, CitusVacuumParams vacuumParams);\n\n/*\n * PostprocessVacuumStmt processes vacuum statements that may need propagation to\n * citus tables only if ddl propagation is enabled. If a VACUUM or ANALYZE command\n * references a citus table or no table, it is propagated to all involved nodes; otherwise,\n * the statements will not be propagated.\n *\n * Unlike most other Process functions within this file, this function does not\n * return a modified parse node, as it is expected that the local VACUUM or\n * ANALYZE has already been processed.\n */\nList *\nPostprocessVacuumStmt(Node *node, const char *vacuumCommand)\n{\n\tVacuumStmt *vacuumStmt = castNode(VacuumStmt, node);\n\n\tCitusVacuumParams vacuumParams = VacuumStmtParams(vacuumStmt);\n\n\tif (vacuumParams.options & VACOPT_VACUUM)\n\t{\n\t\t/*\n\t\t * We commit the current transaction here so that the global lock\n\t\t * taken from the shell table for VACUUM is released, which would block execution\n\t\t * of shard placements. We don't do this in case of \"ANALYZE <table>\" command because\n\t\t * its semantics are different than VACUUM and it doesn't acquire the global lock.\n\t\t */\n\t\tCommitTransactionCommand();\n\t\tStartTransactionCommand();\n\t}\n\n\t/*\n\t * when no table is specified propagate the command as it is;\n\t * otherwise, only propagate when there is at least 1 citus table\n\t */\n\tList *vacuumRelationList = VacuumRelationList(vacuumStmt, vacuumParams);\n\n\tif (list_length(vacuumStmt->rels) == 0)\n\t{\n\t\t/* no table is specified (unqualified vacuum) */\n\n\t\tExecuteUnqualifiedVacuumTasks(vacuumStmt, vacuumParams);\n\t}\n\telse if (IsDistributedVacuumStmt(vacuumRelationList))\n\t{\n\t\t/* there is at least 1 citus table specified */\n\n\t\tExecuteVacuumOnDistributedTables(vacuumStmt, vacuumRelationList,\n\t\t\t\t\t\t\t\t\t\t vacuumParams);\n\t}\n\n\t/* else only local tables are specified */\n\n\treturn NIL;\n}\n\n\n/*\n * VacuumRelationList returns the list of relations in the given vacuum statement,\n * along with their resolved Oids (if they can be locked).\n */\nstatic List *\nVacuumRelationList(VacuumStmt *vacuumStmt, CitusVacuumParams vacuumParams)\n{\n\tLOCKMODE lockMode = (vacuumParams.options & VACOPT_FULL) ? AccessExclusiveLock :\n\t\t\t\t\t\tShareUpdateExclusiveLock;\n\n\tbool skipLocked = (vacuumParams.options & VACOPT_SKIP_LOCKED);\n\n\tList *relationList = NIL;\n\n\tVacuumRelation *vacuumRelation = NULL;\n\tforeach_declared_ptr(vacuumRelation, vacuumStmt->rels)\n\t{\n\t\tOid relationId = InvalidOid;\n\n\t\t/*\n\t\t * If skip_locked option is enabled, we are skipping that relation\n\t\t * if the lock for it is currently not available; otherwise, we get the lock.\n\t\t */\n\t\tif (vacuumRelation->relation)\n\t\t{\n\t\t\trelationId = RangeVarGetRelidExtended(vacuumRelation->relation,\n\t\t\t\t\t\t\t\t\t\t\t\t  lockMode,\n\t\t\t\t\t\t\t\t\t\t\t\t  skipLocked ? RVR_SKIP_LOCKED : 0, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t  NULL);\n\t\t}\n\t\telse if (OidIsValid(vacuumRelation->oid))\n\t\t{\n\t\t\t/* fall back to the Oid directly when provided */\n\t\t\tif (!skipLocked || ConditionalLockRelationOid(vacuumRelation->oid, lockMode))\n\t\t\t{\n\t\t\t\tif (!skipLocked)\n\t\t\t\t{\n\t\t\t\t\tLockRelationOid(vacuumRelation->oid, lockMode);\n\t\t\t\t}\n\t\t\t\trelationId = vacuumRelation->oid;\n\t\t\t}\n\t\t}\n\n\t\tif (OidIsValid(relationId))\n\t\t{\n\t\t\tCitusVacuumRelation *relation = palloc(sizeof(CitusVacuumRelation));\n\t\t\trelation->vacuumRelation = vacuumRelation;\n\t\t\trelation->relationId = relationId;\n\t\t\trelationList = lappend(relationList, relation);\n\t\t}\n\t}\n\n\treturn relationList;\n}\n\n\n/*\n * IsDistributedVacuumStmt returns true if there is any citus table in the relation id list;\n * otherwise, it returns false.\n */\nstatic bool\nIsDistributedVacuumStmt(List *vacuumRelationList)\n{\n\tCitusVacuumRelation *vacuumRelation = NULL;\n\tforeach_declared_ptr(vacuumRelation, vacuumRelationList)\n\t{\n\t\tif (OidIsValid(vacuumRelation->relationId) &&\n\t\t\tIsCitusTable(vacuumRelation->relationId))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ExecuteVacuumOnDistributedTables executes the vacuum for the shard placements of given tables\n * if they are citus tables.\n */\nstatic void\nExecuteVacuumOnDistributedTables(VacuumStmt *vacuumStmt, List *relationList,\n\t\t\t\t\t\t\t\t CitusVacuumParams vacuumParams)\n{\n\tCitusVacuumRelation *vacuumRelationEntry = NULL;\n\tforeach_declared_ptr(vacuumRelationEntry, relationList)\n\t{\n\t\tOid relationId = vacuumRelationEntry->relationId;\n\t\tVacuumRelation *vacuumRelation = vacuumRelationEntry->vacuumRelation;\n\n\t\tRangeVar *relation = vacuumRelation->relation;\n\t\tif (relation != NULL && !relation->inh)\n\t\t{\n\t\t\t/* ONLY specified, so don't recurse to shard placements */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (IsCitusTable(relationId))\n\t\t{\n\t\t\tList *vacuumColumnList = vacuumRelation->va_cols;\n\t\t\tList *taskList = VacuumTaskList(relationId, vacuumParams, vacuumColumnList);\n\n\t\t\t/* local execution is not implemented for VACUUM commands */\n\t\t\tbool localExecutionSupported = false;\n\t\t\tExecuteUtilityTaskList(taskList, localExecutionSupported);\n\t\t}\n\t}\n}\n\n\n/*\n * VacuumTaskList returns a list of tasks to be executed as part of processing\n * a VacuumStmt which targets a distributed relation.\n */\nstatic List *\nVacuumTaskList(Oid relationId, CitusVacuumParams vacuumParams, List *vacuumColumnList)\n{\n\tLOCKMODE lockMode = (vacuumParams.options & VACOPT_FULL) ? AccessExclusiveLock :\n\t\t\t\t\t\tShareUpdateExclusiveLock;\n\n\t/* resulting task list */\n\tList *taskList = NIL;\n\n\t/* enumerate the tasks when putting them to the taskList */\n\tint taskId = 1;\n\n\tOid schemaId = get_rel_namespace(relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\tchar *relationName = get_rel_name(relationId);\n\n\tconst char *vacuumStringPrefix = DeparseVacuumStmtPrefix(vacuumParams);\n\tconst char *columnNames = DeparseVacuumColumnNames(vacuumColumnList);\n\n\t/*\n\t * We obtain ShareUpdateExclusiveLock here to not conflict with INSERT's\n\t * RowExclusiveLock. However if VACUUM FULL is used, we already obtain\n\t * AccessExclusiveLock before reaching to that point and INSERT's will be\n\t * blocked anyway. This is inline with PostgreSQL's own behaviour.\n\t * Also note that if skip locked option is enabled, we try to acquire the lock\n\t * in nonblocking way. If lock is not available, vacuum just skip that relation.\n\t */\n\tif (!(vacuumParams.options & VACOPT_SKIP_LOCKED))\n\t{\n\t\tLockRelationOid(relationId, lockMode);\n\t}\n\telse\n\t{\n\t\tif (!ConditionalLockRelationOid(relationId, lockMode))\n\t\t{\n\t\t\treturn NIL;\n\t\t}\n\t}\n\n\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\n\t/* grab shard lock before getting placement list */\n\tLockShardListMetadata(shardIntervalList, ShareLock);\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\t\tchar *shardRelationName = pstrdup(relationName);\n\n\t\t/* build shard relation name */\n\t\tAppendShardIdToName(&shardRelationName, shardId);\n\n\t\tchar *quotedShardName = quote_qualified_identifier(schemaName, shardRelationName);\n\n\t\t/* copy base vacuum string and build the shard specific command */\n\t\tStringInfo vacuumStringForShard = makeStringInfo();\n\t\tappendStringInfoString(vacuumStringForShard, vacuumStringPrefix);\n\n\t\tappendStringInfoString(vacuumStringForShard, quotedShardName);\n\t\tappendStringInfoString(vacuumStringForShard, columnNames);\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->jobId = INVALID_JOB_ID;\n\t\ttask->taskId = taskId++;\n\t\ttask->taskType = VACUUM_ANALYZE_TASK;\n\t\tSetTaskQueryString(task, vacuumStringForShard->data);\n\t\ttask->dependentTaskList = NULL;\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\ttask->anchorShardId = shardId;\n\t\ttask->taskPlacementList = ActiveShardPlacementList(shardId);\n\t\ttask->cannotBeExecutedInTransaction = ((vacuumParams.options) & VACOPT_VACUUM);\n\n\t\ttaskList = lappend(taskList, task);\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * DeparseVacuumStmtPrefix returns a StringInfo appropriate for use as a prefix\n * during distributed execution of a VACUUM or ANALYZE statement. Callers may\n * reuse this prefix within a loop to generate shard-specific VACUUM or ANALYZE\n * statements.\n */\nstatic char *\nDeparseVacuumStmtPrefix(CitusVacuumParams vacuumParams)\n{\n\tint vacuumFlags = vacuumParams.options;\n\tStringInfo vacuumPrefix = makeStringInfo();\n\n\t/* determine actual command and block out its bits */\n\tif (vacuumFlags & VACOPT_VACUUM)\n\t{\n\t\tappendStringInfoString(vacuumPrefix, \"VACUUM \");\n\t\tvacuumFlags &= ~VACOPT_VACUUM;\n\t}\n\telse\n\t{\n\t\tAssert((vacuumFlags & VACOPT_ANALYZE) != 0);\n\n\t\tappendStringInfoString(vacuumPrefix, \"ANALYZE \");\n\t\tvacuumFlags &= ~VACOPT_ANALYZE;\n\n\t\tif (vacuumFlags & VACOPT_VERBOSE)\n\t\t{\n\t\t\tappendStringInfoString(vacuumPrefix, \"VERBOSE \");\n\t\t\tvacuumFlags &= ~VACOPT_VERBOSE;\n\t\t}\n\t}\n\n\t/* if no flags remain, exit early */\n\tif (vacuumFlags & VACOPT_PROCESS_TOAST &&\n\t\tvacuumFlags & VACOPT_PROCESS_MAIN)\n\t{\n\t\t/* process toast and process main are true by default */\n\t\tif (((vacuumFlags & ~VACOPT_PROCESS_TOAST) & ~VACOPT_PROCESS_MAIN) == 0 &&\n\t\t\tvacuumParams.ring_size == -1 &&\n\t\t\tvacuumParams.truncate == VACOPTVALUE_UNSPECIFIED &&\n\t\t\tvacuumParams.index_cleanup == VACOPTVALUE_UNSPECIFIED &&\n\t\t\tvacuumParams.nworkers == VACUUM_PARALLEL_NOTSET\n\t\t\t)\n\t\t{\n\t\t\treturn vacuumPrefix->data;\n\t\t}\n\t}\n\n\t/* otherwise, handle options */\n\tappendStringInfoChar(vacuumPrefix, '(');\n\n\tif (vacuumFlags & VACOPT_ANALYZE)\n\t{\n\t\tappendStringInfoString(vacuumPrefix, \"ANALYZE,\");\n\t}\n\n\tif (vacuumFlags & VACOPT_DISABLE_PAGE_SKIPPING)\n\t{\n\t\tappendStringInfoString(vacuumPrefix, \"DISABLE_PAGE_SKIPPING,\");\n\t}\n\n\tif (vacuumFlags & VACOPT_FREEZE)\n\t{\n\t\tappendStringInfoString(vacuumPrefix, \"FREEZE,\");\n\t}\n\n\tif (vacuumFlags & VACOPT_FULL)\n\t{\n\t\tappendStringInfoString(vacuumPrefix, \"FULL,\");\n\t}\n\n\tif (vacuumFlags & VACOPT_VERBOSE)\n\t{\n\t\tappendStringInfoString(vacuumPrefix, \"VERBOSE,\");\n\t}\n\n\tif (vacuumFlags & VACOPT_SKIP_LOCKED)\n\t{\n\t\tappendStringInfoString(vacuumPrefix, \"SKIP_LOCKED,\");\n\t}\n\n\tif (!(vacuumFlags & VACOPT_PROCESS_TOAST))\n\t{\n\t\tappendStringInfoString(vacuumPrefix, \"PROCESS_TOAST FALSE,\");\n\t}\n\n\tif (!(vacuumFlags & VACOPT_PROCESS_MAIN))\n\t{\n\t\tappendStringInfoString(vacuumPrefix, \"PROCESS_MAIN FALSE,\");\n\t}\n\n\tif (vacuumFlags & VACOPT_SKIP_DATABASE_STATS)\n\t{\n\t\tappendStringInfoString(vacuumPrefix, \"SKIP_DATABASE_STATS,\");\n\t}\n\n\tif (vacuumFlags & VACOPT_ONLY_DATABASE_STATS)\n\t{\n\t\tappendStringInfoString(vacuumPrefix, \"ONLY_DATABASE_STATS,\");\n\t}\n\n\tif (vacuumParams.ring_size != -1)\n\t{\n\t\tappendStringInfo(vacuumPrefix, \"BUFFER_USAGE_LIMIT %d,\", vacuumParams.ring_size);\n\t}\n\n\tif (vacuumParams.truncate != VACOPTVALUE_UNSPECIFIED)\n\t{\n\t\tappendStringInfoString(vacuumPrefix,\n\t\t\t\t\t\t\t   vacuumParams.truncate == VACOPTVALUE_ENABLED ?\n\t\t\t\t\t\t\t   \"TRUNCATE,\" : \"TRUNCATE false,\"\n\t\t\t\t\t\t\t   );\n\t}\n\n\tif (vacuumParams.index_cleanup != VACOPTVALUE_UNSPECIFIED)\n\t{\n\t\tswitch (vacuumParams.index_cleanup)\n\t\t{\n\t\t\tcase VACOPTVALUE_ENABLED:\n\t\t\t{\n\t\t\t\tappendStringInfoString(vacuumPrefix, \"INDEX_CLEANUP true,\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase VACOPTVALUE_DISABLED:\n\t\t\t{\n\t\t\t\tappendStringInfoString(vacuumPrefix, \"INDEX_CLEANUP false,\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase VACOPTVALUE_AUTO:\n\t\t\t{\n\t\t\t\tappendStringInfoString(vacuumPrefix, \"INDEX_CLEANUP auto,\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (vacuumParams.nworkers != VACUUM_PARALLEL_NOTSET)\n\t{\n\t\tappendStringInfo(vacuumPrefix, \"PARALLEL %d,\", vacuumParams.nworkers);\n\t}\n\n\tvacuumPrefix->data[vacuumPrefix->len - 1] = ')';\n\n\tappendStringInfoChar(vacuumPrefix, ' ');\n\n\treturn vacuumPrefix->data;\n}\n\n\n/*\n * DeparseVacuumColumnNames joins the list of strings using commas as a\n * delimiter. The whole thing is placed in parenthesis and set off with a\n * single space in order to facilitate appending it to the end of any VACUUM\n * or ANALYZE command which uses explicit column names. If the provided list\n * is empty, this function returns an empty string to keep the calling code\n * simplest.\n */\nstatic char *\nDeparseVacuumColumnNames(List *columnNameList)\n{\n\tStringInfo columnNames = makeStringInfo();\n\n\tif (columnNameList == NIL)\n\t{\n\t\treturn columnNames->data;\n\t}\n\n\tappendStringInfoString(columnNames, \" (\");\n\n\tString *columnName = NULL;\n\tforeach_declared_ptr(columnName, columnNameList)\n\t{\n\t\tappendStringInfo(columnNames, \"%s,\", strVal(columnName));\n\t}\n\n\tcolumnNames->data[columnNames->len - 1] = ')';\n\n\treturn columnNames->data;\n}\n\n\n/*\n * VacuumStmtParams returns a CitusVacuumParams based on the supplied VacuumStmt.\n */\n\n/*\n * This is mostly ExecVacuum from Postgres's commands/vacuum.c\n * Note that ExecVacuum does an actual vacuum as well and we don't want\n * that to happen in the coordinator hence we copied the rest here.\n */\nstatic CitusVacuumParams\nVacuumStmtParams(VacuumStmt *vacstmt)\n{\n\tCitusVacuumParams params;\n\tbool verbose = false;\n\tbool skip_locked = false;\n\tbool analyze = false;\n\tbool freeze = false;\n\tbool full = false;\n\tbool disable_page_skipping = false;\n\tbool process_toast = true;\n\tbool process_main = true;\n\tbool skip_database_stats = false;\n\tbool only_database_stats = false;\n\tparams.ring_size = -1;\n\n\t/* Set default value */\n\tparams.index_cleanup = VACOPTVALUE_UNSPECIFIED;\n\tparams.truncate = VACOPTVALUE_UNSPECIFIED;\n\tparams.nworkers = VACUUM_PARALLEL_NOTSET;\n\n\t/* Parse options list */\n\tDefElem *opt = NULL;\n\tforeach_declared_ptr(opt, vacstmt->options)\n\t{\n\t\t/* Parse common options for VACUUM and ANALYZE */\n\t\tif (strcmp(opt->defname, \"verbose\") == 0)\n\t\t{\n\t\t\tverbose = defGetBoolean(opt);\n\t\t}\n\t\telse if (strcmp(opt->defname, \"skip_locked\") == 0)\n\t\t{\n\t\t\tskip_locked = defGetBoolean(opt);\n\t\t}\n\t\telse if (strcmp(opt->defname, \"buffer_usage_limit\") == 0)\n\t\t{\n\t\t\tchar *vac_buffer_size = defGetString(opt);\n\t\t\tparse_int(vac_buffer_size, &params.ring_size, GUC_UNIT_KB, NULL);\n\t\t}\n\t\telse if (!vacstmt->is_vacuumcmd)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t errmsg(\"unrecognized ANALYZE option \\\"%s\\\"\", opt->defname)));\n\t\t}\n\n\t\t/* Parse options available on VACUUM */\n\t\telse if (strcmp(opt->defname, \"analyze\") == 0)\n\t\t{\n\t\t\tanalyze = defGetBoolean(opt);\n\t\t}\n\t\telse if (strcmp(opt->defname, \"freeze\") == 0)\n\t\t{\n\t\t\tfreeze = defGetBoolean(opt);\n\t\t}\n\t\telse if (strcmp(opt->defname, \"full\") == 0)\n\t\t{\n\t\t\tfull = defGetBoolean(opt);\n\t\t}\n\t\telse if (strcmp(opt->defname, \"disable_page_skipping\") == 0)\n\t\t{\n\t\t\tdisable_page_skipping = defGetBoolean(opt);\n\t\t}\n\t\telse if (strcmp(opt->defname, \"process_main\") == 0)\n\t\t{\n\t\t\tprocess_main = defGetBoolean(opt);\n\t\t}\n\t\telse if (strcmp(opt->defname, \"skip_database_stats\") == 0)\n\t\t{\n\t\t\tskip_database_stats = defGetBoolean(opt);\n\t\t}\n\t\telse if (strcmp(opt->defname, \"only_database_stats\") == 0)\n\t\t{\n\t\t\tonly_database_stats = defGetBoolean(opt);\n\t\t}\n\t\telse if (strcmp(opt->defname, \"process_toast\") == 0)\n\t\t{\n\t\t\tprocess_toast = defGetBoolean(opt);\n\t\t}\n\t\telse if (strcmp(opt->defname, \"index_cleanup\") == 0)\n\t\t{\n\t\t\t/* Interpret no string as the default, which is 'auto' */\n\t\t\tif (!opt->arg)\n\t\t\t{\n\t\t\t\tparams.index_cleanup = VACOPTVALUE_AUTO;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar *sval = defGetString(opt);\n\n\t\t\t\t/* Try matching on 'auto' string, or fall back on boolean */\n\t\t\t\tif (pg_strcasecmp(sval, \"auto\") == 0)\n\t\t\t\t{\n\t\t\t\t\tparams.index_cleanup = VACOPTVALUE_AUTO;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tparams.index_cleanup = defGetBoolean(opt) ? VACOPTVALUE_ENABLED :\n\t\t\t\t\t\t\t\t\t\t   VACOPTVALUE_DISABLED;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (strcmp(opt->defname, \"truncate\") == 0)\n\t\t{\n\t\t\tparams.truncate = defGetBoolean(opt) ? VACOPTVALUE_ENABLED :\n\t\t\t\t\t\t\t  VACOPTVALUE_DISABLED;\n\t\t}\n\t\telse if (strcmp(opt->defname, \"parallel\") == 0)\n\t\t{\n\t\t\tif (opt->arg == NULL)\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t\t errmsg(\"parallel option requires a value between 0 and %d\",\n\t\t\t\t\t\t\t\tMAX_PARALLEL_WORKER_LIMIT)));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint nworkers = defGetInt32(opt);\n\t\t\t\tif (nworkers < 0 || nworkers > MAX_PARALLEL_WORKER_LIMIT)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t\t\t errmsg(\"parallel vacuum degree must be between 0 and %d\",\n\t\t\t\t\t\t\t\t\tMAX_PARALLEL_WORKER_LIMIT)));\n\t\t\t\t}\n\n\t\t\t\tparams.nworkers = nworkers;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t errmsg(\"unrecognized VACUUM option \\\"%s\\\"\", opt->defname)\n\t\t\t\t\t));\n\t\t}\n\t}\n\n\tparams.options = (vacstmt->is_vacuumcmd ? VACOPT_VACUUM : VACOPT_ANALYZE) |\n\t\t\t\t\t (verbose ? VACOPT_VERBOSE : 0) |\n\t\t\t\t\t (skip_locked ? VACOPT_SKIP_LOCKED : 0) |\n\t\t\t\t\t (analyze ? VACOPT_ANALYZE : 0) |\n\t\t\t\t\t (freeze ? VACOPT_FREEZE : 0) |\n\t\t\t\t\t (full ? VACOPT_FULL : 0) |\n\t\t\t\t\t (process_main ? VACOPT_PROCESS_MAIN : 0) |\n\t\t\t\t\t (skip_database_stats ? VACOPT_SKIP_DATABASE_STATS : 0) |\n\t\t\t\t\t (only_database_stats ? VACOPT_ONLY_DATABASE_STATS : 0) |\n\t\t\t\t\t (process_toast ? VACOPT_PROCESS_TOAST : 0) |\n\t\t\t\t\t (disable_page_skipping ? VACOPT_DISABLE_PAGE_SKIPPING : 0);\n\treturn params;\n}\n\n\n/*\n * ExecuteUnqualifiedVacuumTasks executes tasks for unqualified vacuum commands\n */\nstatic void\nExecuteUnqualifiedVacuumTasks(VacuumStmt *vacuumStmt, CitusVacuumParams vacuumParams)\n{\n\t/* don't allow concurrent node list changes that require an exclusive lock */\n\tList *workerNodes = TargetWorkerSetNodeList(ALL_SHARD_NODES, RowShareLock);\n\n\tif (list_length(workerNodes) == 0)\n\t{\n\t\treturn;\n\t}\n\n\tconst char *vacuumStringPrefix = DeparseVacuumStmtPrefix(vacuumParams);\n\n\tStringInfo vacuumCommand = makeStringInfo();\n\tappendStringInfoString(vacuumCommand, vacuumStringPrefix);\n\n\tList *unqualifiedVacuumCommands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t\t\t\t\t vacuumCommand->data,\n\t\t\t\t\t\t\t\t\t\t\t\t ENABLE_DDL_PROPAGATION);\n\n\tTask *task = CitusMakeNode(Task);\n\ttask->jobId = INVALID_JOB_ID;\n\ttask->taskType = VACUUM_ANALYZE_TASK;\n\tSetTaskQueryStringList(task, unqualifiedVacuumCommands);\n\ttask->dependentTaskList = NULL;\n\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\ttask->cannotBeExecutedInTransaction = ((vacuumParams.options) & VACOPT_VACUUM);\n\n\n\tbool hasPeerWorker = false;\n\tint32 localNodeGroupId = GetLocalGroupId();\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodes)\n\t{\n\t\tif (workerNode->groupId != localNodeGroupId)\n\t\t{\n\t\t\tShardPlacement *targetPlacement = CitusMakeNode(ShardPlacement);\n\t\t\ttargetPlacement->nodeName = workerNode->workerName;\n\t\t\ttargetPlacement->nodePort = workerNode->workerPort;\n\t\t\ttargetPlacement->groupId = workerNode->groupId;\n\n\t\t\ttask->taskPlacementList = lappend(task->taskPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t  targetPlacement);\n\t\t\thasPeerWorker = true;\n\t\t}\n\t}\n\n\tif (hasPeerWorker)\n\t{\n\t\tbool localExecution = false;\n\t\tExecuteUtilityTaskList(list_make1(task), localExecution);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/variableset.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * variableset.c\n *    Support for propagation of SET (commands to set variables)\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n\n#include \"common/string.h\"\n#include \"lib/ilist.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/varlena.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/version_compat.h\"\n\n/*\n * ShouldPropagateSetCommand determines whether a SET or RESET command should be\n * propagated to the workers.\n *\n * We currently propagate:\n * - SET LOCAL (for allowed settings)\n * - SET TRANSACTION\n * - RESET (for allowed settings)\n * - RESET ALL\n */\nbool\nShouldPropagateSetCommand(VariableSetStmt *setStmt)\n{\n\tif (PropagateSetCommands != PROPSETCMD_LOCAL)\n\t{\n\t\t/* SET propagation is disabled */\n\t\treturn false;\n\t}\n\n\tswitch (setStmt->kind)\n\t{\n\t\tcase VAR_SET_VALUE:\n\t\tcase VAR_SET_CURRENT:\n\t\tcase VAR_SET_DEFAULT:\n\t\t{\n\t\t\t/* SET LOCAL on a safe setting */\n\t\t\treturn setStmt->is_local && IsSettingSafeToPropagate(setStmt->name);\n\t\t}\n\n\t\tcase VAR_RESET:\n\t\t{\n\t\t\t/* may need to reset prior SET LOCAL */\n\t\t\treturn IsSettingSafeToPropagate(setStmt->name);\n\t\t}\n\n\t\tcase VAR_RESET_ALL:\n\t\t{\n\t\t\t/* always propagate RESET ALL since it might affect prior SET LOCALs */\n\t\t\treturn true;\n\t\t}\n\n\t\tcase VAR_SET_MULTI:\n\t\tdefault:\n\t\t{\n\t\t\t/* SET TRANSACTION is similar to SET LOCAL */\n\t\t\treturn strcmp(setStmt->name, \"TRANSACTION\") == 0;\n\t\t}\n\t}\n}\n\n\n/*\n * PostprocessVariableSetStmt actually does the work of propagating a provided SET stmt\n * to currently-participating worker nodes and adding the SET command test to a string\n * keeping track of all propagated SET commands since (sub-)xact start.\n */\nvoid\nPostprocessVariableSetStmt(VariableSetStmt *setStmt, const char *setStmtString)\n{\n\tdlist_iter iter;\n\tconst bool raiseInterrupts = true;\n\tList *connectionList = NIL;\n\n\t/* at present we only support SET LOCAL and SET TRANSACTION */\n\tAssert(ShouldPropagateSetCommand(setStmt));\n\n\t/* haven't seen any SET stmts so far in this (sub-)xact: initialize StringInfo */\n\tif (activeSetStmts == NULL)\n\t{\n\t\t/* see comments in PushSubXact on why we allocate this in TopTransactionContext */\n\t\tMemoryContext old_context = MemoryContextSwitchTo(TopTransactionContext);\n\t\tactiveSetStmts = makeStringInfo();\n\t\tMemoryContextSwitchTo(old_context);\n\t}\n\n\t/* send text of SET stmt to participating nodes... */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\t\tif (transaction->transactionFailed)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!SendRemoteCommand(connection, setStmtString))\n\t\t{\n\t\t\tconst bool raiseErrors = true;\n\t\t\tHandleRemoteTransactionConnectionError(connection, raiseErrors);\n\t\t}\n\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\tWaitForAllConnections(connectionList, raiseInterrupts);\n\n\t/* ... and wait for the results */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tconst bool raiseErrors = true;\n\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\t\tif (transaction->transactionFailed)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tClearResults(connection, raiseErrors);\n\t}\n\n\t/* SET propagation successful: add to active SET stmt string */\n\tappendStringInfoString(activeSetStmts, setStmtString);\n\n\t/* ensure semicolon on end to allow appending future SET stmts */\n\tif (!pg_str_endswith(setStmtString, \";\"))\n\t{\n\t\tappendStringInfoChar(activeSetStmts, ';');\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/commands/view.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * view.c\n *    Commands for distributing CREATE OR REPLACE VIEW statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"access/genam.h\"\n#include \"catalog/objectaddress.h\"\n#include \"commands/extension.h\"\n#include \"executor/spi.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"tcop/utility.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"distributed/worker_transaction.h\"\n\n/*\n * GUC controls some restrictions for local objects. For example,\n * if it is disabled, a local view with no distributed relation dependency\n * will be created even if it has circular dependency and will not\n * log error or warning. Citus will normally restrict that behaviour for the\n * local views. e.g CREATE TABLE local_t (a int);\n *                  CREATE VIEW vv1 as SELECT * FROM local_t;\n *\t\t\t\t\tCREATE OR REPLACE VIEW vv2 as SELECT * FROM vv1;\n *\n * When the GUC is disabled, citus also wont distribute the local\n * view which has no citus relation dependency to workers. Otherwise, it distributes\n * them by default. e.g create view v as select 1;\n */\nbool EnforceLocalObjectRestrictions = true;\n\nstatic List * FilterNameListForDistributedViews(List *viewNamesList, bool missing_ok);\nstatic void AppendQualifiedViewNameToCreateViewCommand(StringInfo buf, Oid viewOid);\nstatic void AppendViewDefinitionToCreateViewCommand(StringInfo buf, Oid viewOid);\nstatic void AppendAliasesToCreateViewCommand(StringInfo createViewCommand, Oid viewOid);\nstatic void AppendOptionsToCreateViewCommand(StringInfo createViewCommand, Oid viewOid);\nstatic bool ViewHasDistributedRelationDependency(ObjectAddress *viewObjectAddress);\n\n/*\n * ViewHasDistributedRelationDependency returns true if given view at address has\n * any distributed relation dependency.\n */\nstatic bool\nViewHasDistributedRelationDependency(ObjectAddress *viewObjectAddress)\n{\n\tList *dependencies = GetAllDependenciesForObject(viewObjectAddress);\n\tObjectAddress *dependency = NULL;\n\n\tforeach_declared_ptr(dependency, dependencies)\n\t{\n\t\tif (dependency->classId == RelationRelationId && IsAnyObjectDistributed(\n\t\t\t\tlist_make1(dependency)))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * PreprocessViewStmt is called during the planning phase for CREATE OR REPLACE VIEW\n * before it is created on the local node internally.\n */\nList *\nPreprocessViewStmt(Node *node, const char *queryString,\n\t\t\t\t   ProcessUtilityContext processUtilityContext)\n{\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* check creation against multi-statement transaction policy */\n\tif (!ShouldPropagateCreateInCoordinatedTransction())\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\treturn NIL;\n}\n\n\n/*\n * PostprocessViewStmt actually creates the commmands we need to run on workers to\n * propagate views.\n *\n * If view depends on any undistributable object, Citus can not distribute it. In order to\n * not to prevent users from creating local views on the coordinator WARNING message will\n * be sent to the customer about the case instead of erroring out. If no worker nodes exist\n * at all, view will be created locally without any WARNING message.\n *\n * Besides creating the plan we also make sure all (new) dependencies of the view are\n * created on all nodes.\n */\nList *\nPostprocessViewStmt(Node *node, const char *queryString)\n{\n\tViewStmt *stmt = castNode(ViewStmt, node);\n\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* check creation against multi-statement transaction policy */\n\tif (!ShouldPropagateCreateInCoordinatedTransction())\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *viewAddresses = GetObjectAddressListFromParseTree((Node *) stmt, false, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(viewAddresses) == 1);\n\n\tif (IsAnyObjectAddressOwnedByExtension(viewAddresses, NULL))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* If the view has any unsupported dependency, create it locally */\n\tif (ErrorOrWarnIfAnyObjectHasUnsupportedDependency(viewAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * If it is disabled, a local view with no distributed relation dependency\n\t * will be created even if it has circular dependency and will not\n\t * log error or warning. Citus will normally restrict that behaviour for the\n\t * local views. e.g CREATE TABLE local_t (a int);\n\t *                  CREATE VIEW vv1 as SELECT * FROM local_t;\n\t *\t\t\t\t\tCREATE OR REPLACE VIEW vv2 as SELECT * FROM vv1;\n\t *\n\t * When the GUC is disabled, citus also wont distribute the local\n\t * view which has no citus relation dependency to workers. Otherwise, it distributes\n\t * them by default. e.g create view v as select 1;\n\t *\n\t * We disable local object restrictions during pg vanilla tests to not diverge\n\t * from Postgres in terms of error messages.\n\t */\n\tif (!EnforceLocalObjectRestrictions)\n\t{\n\t\t/* we asserted that we have only one address. */\n\t\tObjectAddress *viewAddress = linitial(viewAddresses);\n\n\t\tif (!ViewHasDistributedRelationDependency(viewAddress))\n\t\t{\n\t\t\t/*\n\t\t\t * The local view has no distributed relation dependency, so we can allow\n\t\t\t * it to be created even if it has circular dependency.\n\t\t\t */\n\t\t\treturn NIL;\n\t\t}\n\t}\n\n\tEnsureAllObjectDependenciesExistOnAllNodes(viewAddresses);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *viewAddress = linitial(viewAddresses);\n\tchar *command = CreateViewDDLCommand(viewAddress->objectId);\n\n\t/*\n\t * We'd typically use NodeDDLTaskList() for generating node-level DDL commands,\n\t * such as when creating a type. However, views are different in a sense that\n\t * views do not depend on citus tables. Instead, they are `depending` on citus tables.\n\t *\n\t * When NodeDDLTaskList() used, it should be accompanied with sequential execution.\n\t * Here, we do something equivalent to NodeDDLTaskList(), but using metadataSyncCommand\n\t * field. This hack allows us to use the metadata connection\n\t * (see `REQUIRE_METADATA_CONNECTION` flag). Meaning that, view creation is treated as\n\t * a metadata operation.\n\t *\n\t * We do this mostly for performance reasons, because we cannot\tafford to switch to\n\t * sequential execution, for instance when we are altering or creating distributed\n\t * tables -- which may require significant resources.\n\t *\n\t * The downside of using this hack is that if a view is re-used in the same transaction\n\t * that creates the view on the workers, we might get errors such as the below which\n\t * we consider a decent trade-off currently:\n\t *\n\t * BEGIN;\n\t *      CREATE VIEW dist_view ..\n\t *      CRETAE TABLE t2(id int, val dist_view);\n\t *\n\t *      -- shard creation fails on one of the connections\n\t *      SELECT create_distributed_table('t2', 'id');\n\t * ERROR: type \"public.dist_view\" does not exist\n\t *\n\t */\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tddlJob->targetObjectAddress = *viewAddress;\n\tddlJob->metadataSyncCommand = command;\n\tddlJob->taskList = NIL;\n\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * ViewStmtObjectAddress returns the ObjectAddress for the subject of the\n * CREATE [OR REPLACE] VIEW statement.\n */\nList *\nViewStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tViewStmt *stmt = castNode(ViewStmt, node);\n\n\tOid viewOid = RangeVarGetRelid(stmt->view, NoLock, missing_ok);\n\n\tObjectAddress *viewAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*viewAddress, RelationRelationId, viewOid);\n\n\treturn list_make1(viewAddress);\n}\n\n\n/*\n * PreprocessDropViewStmt gets called during the planning phase of a DROP VIEW statement\n * and returns a list of DDLJob's that will drop any distributed view from the\n * workers.\n *\n * The DropStmt could have multiple objects to drop, the list of objects will be filtered\n * to only keep the distributed views for deletion on the workers. Non-distributed\n * views will still be dropped locally but not on the workers.\n */\nList *\nPreprocessDropViewStmt(Node *node, const char *queryString, ProcessUtilityContext\n\t\t\t\t\t   processUtilityContext)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\n\tif (!ShouldPropagate())\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *distributedViewNames = FilterNameListForDistributedViews(stmt->objects,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   stmt->missing_ok);\n\n\tif (list_length(distributedViewNames) < 1)\n\t{\n\t\t/* no distributed view to drop */\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\tEnsureSequentialMode(OBJECT_VIEW);\n\n\t/*\n\t * Swap the list of objects before deparsing and restore the old list after. This\n\t * ensures we only have distributed views in the deparsed drop statement.\n\t */\n\tDropStmt *stmtCopy = copyObject(stmt);\n\tstmtCopy->objects = distributedViewNames;\n\n\tQualifyTreeNode((Node *) stmtCopy);\n\tconst char *dropStmtSql = DeparseTreeNode((Node *) stmtCopy);\n\n\tList *commands = list_make3(DISABLE_DDL_PROPAGATION,\n\t\t\t\t\t\t\t\t(void *) dropStmtSql,\n\t\t\t\t\t\t\t\tENABLE_DDL_PROPAGATION);\n\n\treturn NodeDDLTaskList(NON_COORDINATOR_NODES, commands);\n}\n\n\n/*\n * DropViewStmtObjectAddress returns list of object addresses in the drop view\n * statement.\n */\nList *\nDropViewStmtObjectAddress(Node *stmt, bool missing_ok, bool isPostprocess)\n{\n\tDropStmt *dropStmt = castNode(DropStmt, stmt);\n\n\tList *objectAddresses = NIL;\n\n\tList *possiblyQualifiedViewName = NULL;\n\tforeach_declared_ptr(possiblyQualifiedViewName, dropStmt->objects)\n\t{\n\t\tRangeVar *viewRangeVar = makeRangeVarFromNameList(possiblyQualifiedViewName);\n\t\tOid viewOid = RangeVarGetRelid(viewRangeVar, AccessShareLock,\n\t\t\t\t\t\t\t\t\t   missing_ok);\n\n\t\tObjectAddress *objectAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*objectAddress, RelationRelationId, viewOid);\n\t\tobjectAddresses = lappend(objectAddresses, objectAddress);\n\t}\n\n\treturn objectAddresses;\n}\n\n\n/*\n * FilterNameListForDistributedViews takes a list of view names and filters against the\n * views that are distributed.\n *\n * The original list will not be touched, a new list will be created with only the objects\n * in there.\n */\nstatic List *\nFilterNameListForDistributedViews(List *viewNamesList, bool missing_ok)\n{\n\tList *distributedViewNames = NIL;\n\n\tList *possiblyQualifiedViewName = NULL;\n\tforeach_declared_ptr(possiblyQualifiedViewName, viewNamesList)\n\t{\n\t\tchar *viewName = NULL;\n\t\tchar *schemaName = NULL;\n\t\tDeconstructQualifiedName(possiblyQualifiedViewName, &schemaName, &viewName);\n\n\t\tif (schemaName == NULL)\n\t\t{\n\t\t\tchar *objName = NULL;\n\t\t\tOid schemaOid = QualifiedNameGetCreationNamespace(possiblyQualifiedViewName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &objName);\n\t\t\tschemaName = get_namespace_name(schemaOid);\n\t\t}\n\n\t\tOid schemaId = get_namespace_oid(schemaName, missing_ok);\n\t\tOid viewOid = get_relname_relid(viewName, schemaId);\n\n\t\tif (!OidIsValid(viewOid))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (IsViewDistributed(viewOid))\n\t\t{\n\t\t\tdistributedViewNames = lappend(distributedViewNames,\n\t\t\t\t\t\t\t\t\t\t   possiblyQualifiedViewName);\n\t\t}\n\t}\n\n\treturn distributedViewNames;\n}\n\n\n/*\n * CreateViewDDLCommand returns the DDL command to create the view addressed by\n * the viewAddress.\n */\nchar *\nCreateViewDDLCommand(Oid viewOid)\n{\n\tStringInfo createViewCommand = makeStringInfo();\n\n\tappendStringInfoString(createViewCommand, \"CREATE OR REPLACE VIEW \");\n\n\tAppendQualifiedViewNameToCreateViewCommand(createViewCommand, viewOid);\n\tAppendAliasesToCreateViewCommand(createViewCommand, viewOid);\n\tAppendOptionsToCreateViewCommand(createViewCommand, viewOid);\n\tAppendViewDefinitionToCreateViewCommand(createViewCommand, viewOid);\n\n\treturn createViewCommand->data;\n}\n\n\n/*\n * AppendQualifiedViewNameToCreateViewCommand adds the qualified view of the given view\n * oid to the given create view command.\n */\nstatic void\nAppendQualifiedViewNameToCreateViewCommand(StringInfo buf, Oid viewOid)\n{\n\tchar *qualifiedViewName = generate_qualified_relation_name(viewOid);\n\n\tappendStringInfo(buf, \"%s \", qualifiedViewName);\n}\n\n\n/*\n * AppendAliasesToCreateViewCommand appends aliases to the create view\n * command for the existing view.\n */\nstatic void\nAppendAliasesToCreateViewCommand(StringInfo createViewCommand, Oid viewOid)\n{\n\t/* Get column name aliases from pg_attribute */\n\tScanKeyData key[1];\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_attribute_attrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(viewOid));\n\n\tRelation maprel = table_open(AttributeRelationId, AccessShareLock);\n\tRelation mapidx = index_open(AttributeRelidNumIndexId, AccessShareLock);\n\tSysScanDesc pgAttributeScan = systable_beginscan_ordered(maprel, mapidx, NULL, 1,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t key);\n\n\tbool isInitialAlias = true;\n\tbool hasAlias = false;\n\tHeapTuple attributeTuple;\n\twhile (HeapTupleIsValid(attributeTuple = systable_getnext_ordered(pgAttributeScan,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ForwardScanDirection)))\n\t{\n\t\tForm_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);\n\t\tconst char *aliasName = quote_identifier(NameStr(att->attname));\n\n\t\tif (isInitialAlias)\n\t\t{\n\t\t\tappendStringInfoString(createViewCommand, \"(\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfoString(createViewCommand, \",\");\n\t\t}\n\n\t\tappendStringInfoString(createViewCommand, aliasName);\n\n\t\thasAlias = true;\n\t\tisInitialAlias = false;\n\t}\n\n\tif (hasAlias)\n\t{\n\t\tappendStringInfoString(createViewCommand, \") \");\n\t}\n\n\tsystable_endscan_ordered(pgAttributeScan);\n\tindex_close(mapidx, AccessShareLock);\n\ttable_close(maprel, AccessShareLock);\n}\n\n\n/*\n * AppendOptionsToCreateViewCommand add relation options to create view command\n * for an existing view\n */\nstatic void\nAppendOptionsToCreateViewCommand(StringInfo createViewCommand, Oid viewOid)\n{\n\t/* Add rel options to create view command */\n\tchar *relOptions = flatten_reloptions(viewOid);\n\tif (relOptions != NULL)\n\t{\n\t\tappendStringInfo(createViewCommand, \"WITH (%s) \", relOptions);\n\t}\n}\n\n\n/*\n * AppendViewDefinitionToCreateViewCommand adds the definition of the given view to the\n * given create view command.\n */\nstatic void\nAppendViewDefinitionToCreateViewCommand(StringInfo buf, Oid viewOid)\n{\n\t/*\n\t * Set search_path to NIL so that all objects outside of pg_catalog will be\n\t * schema-prefixed.\n\t */\n\tint saveNestLevel = PushEmptySearchPath();\n\n\t/*\n\t * Push the transaction snapshot to be able to get vief definition with pg_get_viewdef\n\t */\n\tPushActiveSnapshot(GetTransactionSnapshot());\n\n\tDatum viewDefinitionDatum = DirectFunctionCall1(pg_get_viewdef,\n\t\t\t\t\t\t\t\t\t\t\t\t\tObjectIdGetDatum(viewOid));\n\tchar *viewDefinition = TextDatumGetCString(viewDefinitionDatum);\n\n\tPopActiveSnapshot();\n\tPopEmptySearchPath(saveNestLevel);\n\n\tappendStringInfo(buf, \"AS %s \", viewDefinition);\n}\n\n\n/*\n * AlterViewOwnerCommand returns the command to alter view owner command for the\n * given view or materialized view oid.\n */\nchar *\nAlterViewOwnerCommand(Oid viewOid)\n{\n\t/* Add alter owner commmand */\n\tStringInfo alterOwnerCommand = makeStringInfo();\n\n\tchar *viewName = get_rel_name(viewOid);\n\tOid schemaOid = get_rel_namespace(viewOid);\n\tchar *schemaName = get_namespace_name(schemaOid);\n\n\tchar *viewOwnerName = TableOwner(viewOid);\n\tchar *qualifiedViewName = NameListToQuotedString(list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmakeString(viewName)));\n\n\tif (get_rel_relkind(viewOid) == RELKIND_MATVIEW)\n\t{\n\t\tappendStringInfo(alterOwnerCommand, \"ALTER MATERIALIZED VIEW %s \",\n\t\t\t\t\t\t qualifiedViewName);\n\t}\n\telse\n\t{\n\t\tappendStringInfo(alterOwnerCommand, \"ALTER VIEW %s \", qualifiedViewName);\n\t}\n\n\tappendStringInfo(alterOwnerCommand, \"OWNER TO %s\", quote_identifier(viewOwnerName));\n\n\treturn alterOwnerCommand->data;\n}\n\n\n/*\n * IsViewDistributed checks if a view is distributed\n */\nbool\nIsViewDistributed(Oid viewOid)\n{\n\tAssert(get_rel_relkind(viewOid) == RELKIND_VIEW ||\n\t\t   get_rel_relkind(viewOid) == RELKIND_MATVIEW);\n\n\tObjectAddress *viewAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*viewAddress, RelationRelationId, viewOid);\n\treturn IsAnyObjectDistributed(list_make1(viewAddress));\n}\n\n\n/*\n * PreprocessAlterViewStmt is invoked for alter view statements.\n */\nList *\nPreprocessAlterViewStmt(Node *node, const char *queryString, ProcessUtilityContext\n\t\t\t\t\t\tprocessUtilityContext)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\n\tList *viewAddresses = GetObjectAddressListFromParseTree((Node *) stmt, true, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(viewAddresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(viewAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tQualifyTreeNode((Node *) stmt);\n\n\tEnsureCoordinator();\n\n\t/* reconstruct alter statement in a portable fashion */\n\tconst char *alterViewStmtSql = DeparseTreeNode((Node *) stmt);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *viewAddress = linitial(viewAddresses);\n\n\t/*\n\t * To avoid sequential mode, we are using metadata connection. For the\n\t * detailed explanation, please check the comment on PostprocessViewStmt.\n\t */\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tddlJob->targetObjectAddress = *viewAddress;\n\tddlJob->metadataSyncCommand = alterViewStmtSql;\n\tddlJob->taskList = NIL;\n\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * PostprocessAlterViewStmt is invoked for alter view statements.\n */\nList *\nPostprocessAlterViewStmt(Node *node, const char *queryString)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tAssert(stmt->objtype == OBJECT_VIEW);\n\n\tList *viewAddresses = GetObjectAddressListFromParseTree((Node *) stmt, true, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(viewAddresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(viewAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tif (IsAnyObjectAddressOwnedByExtension(viewAddresses, NULL))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* If the view has any unsupported dependency, create it locally */\n\tif (ErrorOrWarnIfAnyObjectHasUnsupportedDependency(viewAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureAllObjectDependenciesExistOnAllNodes(viewAddresses);\n\n\treturn NIL;\n}\n\n\n/*\n * AlterViewStmtObjectAddress returns the ObjectAddress for the subject of the\n * ALTER VIEW statement.\n */\nList *\nAlterViewStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tOid viewOid = RangeVarGetRelid(stmt->relation, NoLock, missing_ok);\n\n\tObjectAddress *viewAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*viewAddress, RelationRelationId, viewOid);\n\n\treturn list_make1(viewAddress);\n}\n\n\n/*\n * PreprocessRenameViewStmt is called when the user is renaming the view or the column of\n * the view.\n */\nList *\nPreprocessRenameViewStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t ProcessUtilityContext processUtilityContext)\n{\n\tList *viewAddresses = GetObjectAddressListFromParseTree(node, true, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(viewAddresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(viewAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\t/* fully qualify */\n\tQualifyTreeNode(node);\n\n\t/* deparse sql*/\n\tconst char *renameStmtSql = DeparseTreeNode(node);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *viewAddress = linitial(viewAddresses);\n\n\t/*\n\t * To avoid sequential mode, we are using metadata connection. For the\n\t * detailed explanation, please check the comment on PostprocessViewStmt.\n\t */\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tddlJob->targetObjectAddress = *viewAddress;\n\tddlJob->metadataSyncCommand = renameStmtSql;\n\tddlJob->taskList = NIL;\n\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * RenameViewStmtObjectAddress returns the ObjectAddress of the view that is the object\n * of the RenameStmt. Errors if missing_ok is false.\n */\nList *\nRenameViewStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\n\tOid viewOid = RangeVarGetRelid(stmt->relation, NoLock, missing_ok);\n\n\tObjectAddress *viewAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*viewAddress, RelationRelationId, viewOid);\n\n\treturn list_make1(viewAddress);\n}\n\n\n/*\n * PreprocessAlterViewSchemaStmt is executed before the statement is applied to the local\n * postgres instance.\n */\nList *\nPreprocessAlterViewSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\n\tList *viewAddresses = GetObjectAddressListFromParseTree((Node *) stmt, true, false);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(viewAddresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(viewAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\tEnsureCoordinator();\n\n\tQualifyTreeNode((Node *) stmt);\n\n\tconst char *sql = DeparseTreeNode((Node *) stmt);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *viewAddress = linitial(viewAddresses);\n\n\t/*\n\t * To avoid sequential mode, we are using metadata connection. For the\n\t * detailed explanation, please check the comment on PostprocessViewStmt.\n\t */\n\tDDLJob *ddlJob = palloc0(sizeof(DDLJob));\n\tddlJob->targetObjectAddress = *viewAddress;\n\tddlJob->metadataSyncCommand = sql;\n\tddlJob->taskList = NIL;\n\n\treturn list_make1(ddlJob);\n}\n\n\n/*\n * PostprocessAlterViewSchemaStmt is executed after the change has been applied locally, we\n * can now use the new dependencies of the view to ensure all its dependencies exist on\n * the workers before we apply the commands remotely.\n */\nList *\nPostprocessAlterViewSchemaStmt(Node *node, const char *queryString)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\n\tList *viewAddresses = GetObjectAddressListFromParseTree((Node *) stmt, true, true);\n\n\t/*  the code-path only supports a single object */\n\tAssert(list_length(viewAddresses) == 1);\n\n\tif (!ShouldPropagateAnyObject(viewAddresses))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* dependencies have changed (schema) let's ensure they exist */\n\tEnsureAllObjectDependenciesExistOnAllNodes(viewAddresses);\n\n\treturn NIL;\n}\n\n\n/*\n * AlterViewSchemaStmtObjectAddress returns the ObjectAddress of the view that is the object\n * of the alter schema statement.\n */\nList *\nAlterViewSchemaStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\n\tOid viewOid = RangeVarGetRelid(stmt->relation, NoLock, true);\n\n\t/*\n\t * Since it can be called both before and after executing the standardProcess utility,\n\t * we need to check both old and new schemas\n\t */\n\tif (viewOid == InvalidOid)\n\t{\n\t\tOid schemaId = get_namespace_oid(stmt->newschema, missing_ok);\n\t\tviewOid = get_relname_relid(stmt->relation->relname, schemaId);\n\n\t\t/*\n\t\t * if the view is still invalid we couldn't find the view, error with the same\n\t\t * message postgres would error with it missing_ok is false (not ok to miss)\n\t\t */\n\t\tif (!missing_ok && viewOid == InvalidOid)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t\t\terrmsg(\"view \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t\t   stmt->relation->relname)));\n\t\t}\n\t}\n\n\tObjectAddress *viewAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*viewAddress, RelationRelationId, viewOid);\n\n\treturn list_make1(viewAddress);\n}\n\n\n/*\n * IsViewRenameStmt returns whether the passed-in RenameStmt is the following\n * form:\n *\n *   - ALTER VIEW RENAME\n *   - ALTER VIEW RENAME COLUMN\n */\nbool\nIsViewRenameStmt(RenameStmt *renameStmt)\n{\n\tbool isViewRenameStmt = false;\n\n\tif (renameStmt->renameType == OBJECT_VIEW ||\n\t\t(renameStmt->renameType == OBJECT_COLUMN &&\n\t\t renameStmt->relationType == OBJECT_VIEW))\n\t{\n\t\tisViewRenameStmt = true;\n\t}\n\n\treturn isViewRenameStmt;\n}\n"
  },
  {
    "path": "src/backend/distributed/connection/connection_configuration.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * connection_configuration.c\n *   Functions for controlling configuration of Citus connections\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/transam.h\"\n#include \"access/xact.h\"\n#include \"mb/pg_wchar.h\"\n#include \"postmaster/postmaster.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/intermediate_result_pruning.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/worker_manager.h\"\n\n/* stores the string representation of our node connection GUC */\n#ifdef USE_SSL\nchar *NodeConninfo = \"sslmode=require\";\n#else\nchar *NodeConninfo = \"sslmode=prefer\";\n#endif\n\n/*\n * Previously we would use an empty initial value for NodeConnInfo\n * PG16 however requires same initial and boot values for configuration parameters\n * Therefore we now use this flag in NodeConninfoGucAssignHook\n */\nbool checkAtBootPassed = false;\n\nchar *LocalHostName = \"localhost\";\n\n/* represents a list of libpq parameter settings */\ntypedef struct ConnParamsInfo\n{\n\tchar **keywords; /* libpq keywords */\n\tchar **values; /* desired values for above */\n\tSize size; /* current used size of arrays */\n\tSize maxSize; /* maximum allocated size of arrays (similar to e.g. StringInfo) */\n} ConnParamsInfo;\n\n/*\n * Stores parsed global libpq parameter settings. static because all access\n * is encapsulated in the other public functions in this file.\n */\nstatic ConnParamsInfo ConnParams;\n\n/* helper functions for processing connection info */\nstatic ConnectionHashKey * GetEffectiveConnKey(ConnectionHashKey *key);\nstatic Size CalculateMaxSize(void);\nstatic int uri_prefix_length(const char *connstr);\n\n/*\n * InitConnParams initializes the ConnParams field to point to enough memory to\n * store settings for every valid libpq value, though these regions are set to\n * zeros from the outset and the size appropriately also set to zero.\n *\n * This function must be called before others in this file, though calling it\n * after use of the initialized ConnParams structure will result in any\n * populated parameter settings being lost.\n */\nvoid\nInitConnParams()\n{\n\tSize maxSize = CalculateMaxSize();\n\tConnParamsInfo connParams = {\n\t\t.keywords = malloc(maxSize * sizeof(char *)),\n\t\t.values = malloc(maxSize * sizeof(char *)),\n\t\t.size = 0,\n\t\t.maxSize = maxSize\n\t};\n\n\tmemset(connParams.keywords, 0, maxSize * sizeof(char *));\n\tmemset(connParams.values, 0, maxSize * sizeof(char *));\n\n\tConnParams = connParams;\n}\n\n\n/*\n * ResetConnParams frees all strings in the keywords and parameters arrays,\n * sets their elements to null, and resets the ConnParamsSize to zero before\n * adding back any hardcoded global connection settings (at present, there\n * are no).\n */\nvoid\nResetConnParams()\n{\n\tfor (Size paramIdx = 0; paramIdx < ConnParams.size; paramIdx++)\n\t{\n\t\tfree((void *) ConnParams.keywords[paramIdx]);\n\t\tfree((void *) ConnParams.values[paramIdx]);\n\n\t\tConnParams.keywords[paramIdx] = ConnParams.values[paramIdx] = NULL;\n\t}\n\n\tConnParams.size = 0;\n\n\tInvalidateConnParamsHashEntries();\n}\n\n\n/*\n * AddConnParam adds a parameter setting to the global libpq settings according\n * to the provided keyword and value.\n */\nvoid\nAddConnParam(const char *keyword, const char *value)\n{\n\tif (ConnParams.size + 1 >= ConnParams.maxSize)\n\t{\n\t\t/* hopefully this error is only seen by developers */\n\t\tereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),\n\t\t\t\t\t\terrmsg(\"ConnParams arrays bound check failed\")));\n\t}\n\n\t/*\n\t * Don't use pstrdup here to avoid being tied to a memory context, we free\n\t * these later using ResetConnParams\n\t */\n\tConnParams.keywords[ConnParams.size] = strdup(keyword);\n\tConnParams.values[ConnParams.size] = strdup(value);\n\tConnParams.size++;\n\n\tConnParams.keywords[ConnParams.size] = ConnParams.values[ConnParams.size] = NULL;\n}\n\n\n/*\n * CheckConninfo is a building block to help implement check constraints and\n * other check hooks against libpq-like conninfo strings. In particular, the\n * provided conninfo must:\n *\n *   - Not use a uri-prefix such as postgres:// (it must be only keys and values)\n *   - Parse using PQconninfoParse\n *   - Only set keywords contained in the provided allowedConninfoKeywords\n *\n * This function returns true if all of the above are satisfied, otherwise it\n * returns false. If the provided errmsg pointer is not NULL, it will be set\n * to an appropriate message if the check fails.\n *\n * The provided allowedConninfoKeywords must be sorted in a manner usable by bsearch,\n * though this is only validated during assert-enabled builds.\n */\nbool\nCheckConninfo(const char *conninfo, const char **allowedConninfoKeywords,\n\t\t\t  Size allowedConninfoKeywordsLength, char **errorMsg)\n{\n\tPQconninfoOption *option = NULL;\n\tchar *errorMsgString = NULL;\n\n\t/*\n\t * If the user doesn't need a message, just overwrite errmsg with a stack\n\t * variable so we can always safely write to it.\n\t */\n\tif (errorMsg == NULL)\n\t{\n\t\terrorMsg = &errorMsgString;\n\t}\n\n\t/* sure, it can be null */\n\tif (conninfo == NULL)\n\t{\n\t\treturn true;\n\t}\n\n\t/* the libpq prefix form is more complex than we need; ban it */\n\tif (uri_prefix_length(conninfo) != 0)\n\t{\n\t\t*errorMsg = \"Citus connection info strings must be in \"\n\t\t\t\t\t\"'k1=v1 k2=v2 [...] kn=vn' format\";\n\n\t\treturn false;\n\t}\n\n\t/* this should at least parse */\n\tPQconninfoOption *optionArray = PQconninfoParse(conninfo, NULL);\n\tif (optionArray == NULL)\n\t{\n\t\t*errorMsg = \"Provided string is not a valid libpq connection info string\";\n\n\t\treturn false;\n\t}\n\n#ifdef USE_ASSERT_CHECKING\n\n\t/* verify that the allowedConninfoKeywords is in ascending order */\n\tfor (Size keywordIdx = 1; keywordIdx < allowedConninfoKeywordsLength; keywordIdx++)\n\t{\n\t\tconst char *prev = allowedConninfoKeywords[keywordIdx - 1];\n\t\tconst char *curr = allowedConninfoKeywords[keywordIdx];\n\n\t\tAssert(strcmp(prev, curr) < 0);\n\t}\n#endif\n\n\tfor (option = optionArray; option->keyword != NULL; option++)\n\t{\n\t\tif (option->val == NULL || option->val[0] == '\\0')\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tvoid *matchingKeyword = SafeBsearch(&option->keyword, allowedConninfoKeywords,\n\t\t\t\t\t\t\t\t\t\t\tallowedConninfoKeywordsLength, sizeof(char *),\n\t\t\t\t\t\t\t\t\t\t\tpg_qsort_strcmp);\n\t\tif (matchingKeyword == NULL)\n\t\t{\n\t\t\t/* the allowedConninfoKeywords lacks this keyword; error out! */\n\t\t\tStringInfoData msgString;\n\t\t\tinitStringInfo(&msgString);\n\n\t\t\tappendStringInfo(&msgString, \"Prohibited conninfo keyword detected: %s\",\n\t\t\t\t\t\t\t option->keyword);\n\n\t\t\t*errorMsg = msgString.data;\n\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tPQconninfoFree(optionArray);\n\n\t/* if error message is set we found an invalid keyword */\n\treturn (*errorMsg == NULL);\n}\n\n\n/*\n * GetConnParams uses the provided key to determine libpq parameters needed to\n * establish a connection using that key. The keywords and values are placed in\n * the like-named out parameters. All parameter strings are allocated in the\n * context provided by the caller, to save the caller needing to copy strings\n * into an appropriate context later.\n */\nvoid\nGetConnParams(ConnectionHashKey *key, char ***keywords, char ***values,\n\t\t\t  Index *runtimeParamStart, MemoryContext context)\n{\n\t/*\n\t * make space for the port as a string: sign, 10 digits, NUL. We keep it on the stack\n\t * till we can later copy it to the right context. By having the declaration here\n\t * already we can add a pointer to the runtimeValues.\n\t */\n\tchar nodePortString[12] = \"\";\n\tConnectionHashKey *effectiveKey = GetEffectiveConnKey(key);\n\n\tStringInfo applicationName = makeStringInfo();\n\tappendStringInfo(applicationName, \"%s%ld\", CITUS_APPLICATION_NAME_PREFIX,\n\t\t\t\t\t GetGlobalPID());\n\n\t/*\n\t * This function has three sections:\n\t *   - Initialize the keywords and values (to be copied later) of global parameters\n\t *   - Append user/host-specific parameters calculated from the given key\n\t *   - (Enterprise-only) append user/host-specific authentication params\n\t *\n\t * The global parameters have already been assigned from a GUC, so begin by\n\t * calculating the key-specific parameters (basically just the fields of\n\t * the key and the active database encoding).\n\t *\n\t * We allocate everything in the provided context so as to facilitate using\n\t * pfree on all runtime parameters when connections using these entries are\n\t * invalidated during config reloads.\n\t *\n\t * Also, when \"host\" is already provided in global parameters, we use hostname\n\t * from the key as \"hostaddr\" instead of \"host\" to avoid host name lookup. In\n\t * that case, the value for \"host\" becomes useful only if the authentication\n\t * method requires it.\n\t */\n\tbool gotHostParamFromGlobalParams = false;\n\tfor (Size paramIndex = 0; paramIndex < ConnParams.size; paramIndex++)\n\t{\n\t\tif (strcmp(ConnParams.keywords[paramIndex], \"host\") == 0)\n\t\t{\n\t\t\tgotHostParamFromGlobalParams = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst char *runtimeKeywords[] = {\n\t\tgotHostParamFromGlobalParams ? \"hostaddr\" : \"host\",\n\t\t\"port\",\n\t\t\"dbname\",\n\t\t\"user\",\n\t\t\"client_encoding\",\n\t\t\"application_name\"\n\t};\n\tconst char *runtimeValues[] = {\n\t\teffectiveKey->hostname,\n\t\tnodePortString,\n\t\teffectiveKey->database,\n\t\teffectiveKey->user,\n\t\tGetDatabaseEncodingName(),\n\t\tapplicationName->data\n\t};\n\n\t/*\n\t * remember where global/GUC params end and runtime ones start, all entries after this\n\t * point should be allocated in context and will be freed upon\n\t * FreeConnParamsHashEntryFields\n\t */\n\t*runtimeParamStart = ConnParams.size;\n\n\t/*\n\t * Declare local params for readability;\n\t *\n\t * assignment is done directly to not lose the pointers if any of the later\n\t * allocations cause an error. FreeConnParamsHashEntryFields knows about the\n\t * possibility of half initialized keywords or values and correctly reclaims them when\n\t * the cache is reused.\n\t *\n\t * Need to zero enough space for all possible libpq parameters.\n\t */\n\tchar **connKeywords = *keywords = MemoryContextAllocZero(context, ConnParams.maxSize *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t sizeof(char *));\n\tchar **connValues = *values = MemoryContextAllocZero(context, ConnParams.maxSize *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t sizeof(char *));\n\n\t/* auth keywords will begin after global and runtime ones are appended */\n\tIndex authParamsIdx = ConnParams.size + lengthof(runtimeKeywords);\n\n\tif (ConnParams.size + lengthof(runtimeKeywords) >= ConnParams.maxSize)\n\t{\n\t\t/* hopefully this error is only seen by developers */\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"too many connParams entries\")));\n\t}\n\n\tpg_ltoa(effectiveKey->port, nodePortString); /* populate node port string with port */\n\n\t/* first step: copy global parameters to beginning of array */\n\tfor (Size paramIndex = 0; paramIndex < ConnParams.size; paramIndex++)\n\t{\n\t\t/* copy the keyword&value pointers to the new array */\n\t\tconnKeywords[paramIndex] = ConnParams.keywords[paramIndex];\n\t\tconnValues[paramIndex] = ConnParams.values[paramIndex];\n\t}\n\n\t/* second step: begin after global params and copy runtime params into our context */\n\tfor (Index runtimeParamIndex = 0;\n\t\t runtimeParamIndex < lengthof(runtimeKeywords);\n\t\t runtimeParamIndex++)\n\t{\n\t\t/* copy the keyword & value into our context and append to the new array */\n\t\tconnKeywords[ConnParams.size + runtimeParamIndex] =\n\t\t\tMemoryContextStrdup(context, runtimeKeywords[runtimeParamIndex]);\n\t\tconnValues[ConnParams.size + runtimeParamIndex] =\n\t\t\tMemoryContextStrdup(context, runtimeValues[runtimeParamIndex]);\n\t}\n\n\t/* we look up authinfo by original key, not effective one */\n\tchar *authinfo = GetAuthinfo(key->hostname, key->port, key->user);\n\tchar *pqerr = NULL;\n\tPQconninfoOption *optionArray = PQconninfoParse(authinfo, &pqerr);\n\tif (optionArray == NULL)\n\t{\n\t\t/* PQconninfoParse failed, it's unsafe to continue as this has caused segfaults in production */\n\t\tif (pqerr == NULL)\n\t\t{\n\t\t\t/* parse failed without an error message, treat as OOM error  */\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_OUT_OF_MEMORY),\n\t\t\t\t\t errmsg(\"out of memory\"),\n\t\t\t\t\t errdetail(\"Failed to parse authentication information via libpq\")));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Parse error, should not be possible as the validity is checked upon insert into pg_dist_authinfo,\n\t\t\t * however, better safe than sorry\n\t\t\t */\n\n\t\t\t/*\n\t\t\t * errmsg is populated by PQconninfoParse which requires us to free the message. Since we want to\n\t\t\t * incorporate the parse error into the detail of our message we need to copy the error message before\n\t\t\t * freeing it. Not freeing the message will leak memory.\n\t\t\t */\n\t\t\tchar *pqerrcopy = pstrdup(pqerr);\n\t\t\tPQfreemem(pqerr);\n\n\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\"failed to parse node authentication information for %s@%s:%d\",\n\t\t\t\t\t\t\t\tkey->user, key->hostname, key->port),\n\t\t\t\t\t\t\terrdetail(\"%s\", pqerrcopy)));\n\t\t}\n\t}\n\n\tfor (PQconninfoOption *option = optionArray; option->keyword != NULL; option++)\n\t{\n\t\tif (option->val == NULL || option->val[0] == '\\0')\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tconnKeywords[authParamsIdx] = MemoryContextStrdup(context, option->keyword);\n\t\tconnValues[authParamsIdx] = MemoryContextStrdup(context, option->val);\n\n\t\tauthParamsIdx++;\n\t}\n\n\tif (key->replicationConnParam)\n\t{\n\t\tconnKeywords[authParamsIdx] = MemoryContextStrdup(context, \"replication\");\n\t\tconnValues[authParamsIdx] = MemoryContextStrdup(context, \"database\");\n\n\t\tauthParamsIdx++;\n\t}\n\n\tPQconninfoFree(optionArray);\n\n\t/* final step: add terminal NULL, required by libpq */\n\tconnKeywords[authParamsIdx] = connValues[authParamsIdx] = NULL;\n}\n\n\n/*\n * GetConnParam finds the keyword in the configured connection parameters and returns its\n * value.\n */\nconst char *\nGetConnParam(const char *keyword)\n{\n\tfor (Size i = 0; i < ConnParams.size; i++)\n\t{\n\t\tif (strcmp(keyword, ConnParams.keywords[i]) == 0)\n\t\t{\n\t\t\treturn ConnParams.values[i];\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * GetEffectiveConnKey checks whether there is any pooler configuration for the\n * provided key (host/port combination). If a corresponding row is found in the\n * poolinfo table, a modified (effective) key is returned with the node, port,\n * and dbname overridden, as applicable, otherwise, the original key is returned\n * unmodified.\n *\n * In the case of Citus non-main databases we just return the key, since we\n * would not have access to tables with worker information.\n */\nConnectionHashKey *\nGetEffectiveConnKey(ConnectionHashKey *key)\n{\n\tPQconninfoOption *option = NULL, *optionArray = NULL;\n\n\tif (!IsTransactionState())\n\t{\n\t\t/* we're in the task tracker, so should only see loopback */\n\t\tAssert(strncmp(LocalHostName, key->hostname, MAX_NODE_LENGTH) == 0 &&\n\t\t\t   PostPortNumber == key->port);\n\t\treturn key;\n\t}\n\n\tif (!CitusHasBeenLoaded())\n\t{\n\t\t/*\n\t\t * This happens when we connect to main database over localhost\n\t\t * from some non Citus database.\n\t\t */\n\t\treturn key;\n\t}\n\n\tWorkerNode *worker = FindWorkerNode(key->hostname, key->port);\n\n\tif (worker == NULL)\n\t{\n\t\t/* this can be hit when the key references an unknown node */\n\t\treturn key;\n\t}\n\n\tchar *poolinfo = GetPoolinfoViaCatalog(worker->nodeId);\n\tif (poolinfo == NULL)\n\t{\n\t\treturn key;\n\t}\n\n\t/* copy the key to provide defaults for all fields */\n\tConnectionHashKey *effectiveKey = palloc(sizeof(ConnectionHashKey));\n\t*effectiveKey = *key;\n\n\toptionArray = PQconninfoParse(poolinfo, NULL);\n\tfor (option = optionArray; option->keyword != NULL; option++)\n\t{\n\t\tif (option->val == NULL || option->val[0] == '\\0')\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (strcmp(option->keyword, \"host\") == 0)\n\t\t{\n\t\t\tstrlcpy(effectiveKey->hostname, option->val, MAX_NODE_LENGTH);\n\t\t}\n\t\telse if (strcmp(option->keyword, \"port\") == 0)\n\t\t{\n\t\t\teffectiveKey->port = pg_strtoint32(option->val);\n\t\t}\n\t\telse if (strcmp(option->keyword, \"dbname\") == 0)\n\t\t{\n\t\t\t/* permit dbname for poolers which can key pools based on dbname */\n\t\t\tstrlcpy(effectiveKey->database, option->val, NAMEDATALEN);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(FATAL, (errmsg(\"unrecognized poolinfo keyword\")));\n\t\t}\n\t}\n\n\tPQconninfoFree(optionArray);\n\n\treturn effectiveKey;\n}\n\n\n/*\n * GetAuthinfo simply returns the string representation of authentication info\n * for a specified hostname/port/user combination. If the current transaction\n * is valid, then we use the catalog, otherwise a shared memory hash is used,\n * a mode that is currently only useful for getting authentication information\n * to the Task Tracker, which lacks a database connection and transaction.\n */\nchar *\nGetAuthinfo(char *hostname, int32 port, char *user)\n{\n\tchar *authinfo = NULL;\n\tbool isLoopback = (strncmp(LocalHostName, hostname, MAX_NODE_LENGTH) == 0 &&\n\t\t\t\t\t   PostPortNumber == port);\n\n\t/*\n\t * Citus will not be loaded when we run a global DDL command from a\n\t * Citus non-main database.\n\t */\n\tif (!CitusHasBeenLoaded())\n\t{\n\t\t/*\n\t\t * We don't expect non-main databases to connect to a node other than\n\t\t * the local one.\n\t\t */\n\t\tAssert(isLoopback);\n\t\treturn \"\";\n\t}\n\n\tif (IsTransactionState())\n\t{\n\t\tint64 nodeId = WILDCARD_NODE_ID;\n\n\t\t/* -1 is a special value for loopback connections (task tracker) */\n\t\tif (isLoopback)\n\t\t{\n\t\t\tnodeId = LOCALHOST_NODE_ID;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tWorkerNode *worker = FindWorkerNode(hostname, port);\n\t\t\tif (worker != NULL)\n\t\t\t{\n\t\t\t\tnodeId = worker->nodeId;\n\t\t\t}\n\t\t}\n\n\t\tauthinfo = GetAuthinfoViaCatalog(user, nodeId);\n\t}\n\n\treturn (authinfo != NULL) ? authinfo : \"\";\n}\n\n\n/*\n * CalculateMaxSize simply counts the number of elements returned by\n * PQconnDefaults, including the final NULL. This helps us know how space would\n * be used if a connection utilizes every known libpq parameter.\n */\nstatic Size\nCalculateMaxSize()\n{\n\tPQconninfoOption *defaults = PQconndefaults();\n\tSize maxSize = 0;\n\n\tfor (PQconninfoOption *option = defaults;\n\t\t option->keyword != NULL;\n\t\t option++, maxSize++)\n\t{\n\t\t/* do nothing, we're just counting the elements */\n\t}\n\n\tPQconninfoFree(defaults);\n\n\t/* we've counted elements but libpq needs a final NULL, so add one */\n\tmaxSize++;\n\n\treturn maxSize;\n}\n\n\n/* *INDENT-OFF* */\n\n/*\n * Checks if connection string starts with either of the valid URI prefix\n * designators.\n *\n * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix.\n *\n * This implementation (mostly) taken from libpq/fe-connect.c.\n */\nstatic int\nuri_prefix_length(const char *connstr)\n{\n\tconst char uri_designator[] = \"postgresql://\";\n\tconst char short_uri_designator[] = \"postgres://\";\n\n\tif (strncmp(connstr, uri_designator,\n\t\t\t\tsizeof(uri_designator) - 1) == 0)\n\t\treturn sizeof(uri_designator) - 1;\n\n\tif (strncmp(connstr, short_uri_designator,\n\t\t\t\tsizeof(short_uri_designator) - 1) == 0)\n\t\treturn sizeof(short_uri_designator) - 1;\n\n\treturn 0;\n}\n\n/* *INDENT-ON* */\n"
  },
  {
    "path": "src/backend/distributed/connection/connection_management.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * connection_management.c\n *   Central management of connections and their life-cycle\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"pg_config.h\"\n#include \"pgstat.h\"\n#include \"safe_lib.h\"\n\n#include \"access/hash.h\"\n#include \"commands/dbcommands.h\"\n#include \"mb/pg_wchar.h\"\n#include \"portability/instr_time.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/ipc.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/memutils.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/cancel_utils.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/error_codes.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/memutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/run_from_same_connection.h\"\n#include \"distributed/shared_connection_stats.h\"\n#include \"distributed/stats/stat_counters.h\"\n#include \"distributed/time_constants.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_log_messages.h\"\n\n\nint NodeConnectionTimeout = 30000;\nint MaxCachedConnectionsPerWorker = 1;\nint MaxCachedConnectionLifetime = 10 * MS_PER_MINUTE;\n\nHTAB *ConnectionHash = NULL;\nHTAB *ConnParamsHash = NULL;\n\nMemoryContext ConnectionContext = NULL;\n\nstatic uint32 ConnectionHashHash(const void *key, Size keysize);\nstatic int ConnectionHashCompare(const void *a, const void *b, Size keysize);\nstatic void StartConnectionEstablishment(MultiConnection *connectionn,\n\t\t\t\t\t\t\t\t\t\t ConnectionHashKey *key);\nstatic MultiConnection * FindAvailableConnection(dlist_head *connections, uint32 flags);\nstatic void ErrorIfMultipleMetadataConnectionExists(dlist_head *connections);\nstatic void FreeConnParamsHashEntryFields(ConnParamsHashEntry *entry);\nstatic void AfterXactHostConnectionHandling(ConnectionHashEntry *entry, bool isCommit);\nstatic bool ShouldShutdownConnection(MultiConnection *connection, const int\n\t\t\t\t\t\t\t\t\t cachedConnectionCount);\nstatic bool RemoteTransactionIdle(MultiConnection *connection);\nstatic int EventSetSizeForConnectionList(List *connections);\n\n\n/* types for async connection management */\nenum MultiConnectionPhase\n{\n\tMULTI_CONNECTION_PHASE_CONNECTING,\n\tMULTI_CONNECTION_PHASE_CONNECTED,\n\tMULTI_CONNECTION_PHASE_ERROR,\n};\ntypedef struct MultiConnectionPollState\n{\n\tenum MultiConnectionPhase phase;\n\tMultiConnection *connection;\n\tPostgresPollingStatusType pollmode;\n} MultiConnectionPollState;\n\n\n/* helper functions for async connection management */\nstatic bool MultiConnectionStatePoll(MultiConnectionPollState *connectionState);\nstatic WaitEventSet * WaitEventSetFromMultiConnectionStates(List *connections,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tint *waitCount);\nstatic void CloseNotReadyMultiConnectionStates(List *connectionStates);\nstatic uint32 MultiConnectionStateEventMask(MultiConnectionPollState *connectionState);\nstatic void CitusPQFinish(MultiConnection *connection);\nstatic ConnParamsHashEntry * FindOrCreateConnParamsEntry(ConnectionHashKey *key);\n\n/*\n * Initialize per-backend connection management infrastructure.\n */\nvoid\nInitializeConnectionManagement(void)\n{\n\tHASHCTL info, connParamsInfo;\n\n\t/*\n\t * Create a single context for connection and transaction related memory\n\t * management. Doing so, instead of allocating in TopMemoryContext, makes\n\t * it easier to associate used memory.\n\t */\n\tConnectionContext = AllocSetContextCreateInternal(TopMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Connection Context\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MINSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_INITSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MAXSIZE);\n\n\t/* create (host,port,user,database) -> [connection] hash */\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(ConnectionHashKey);\n\tinfo.entrysize = sizeof(ConnectionHashEntry);\n\tinfo.hash = ConnectionHashHash;\n\tinfo.match = ConnectionHashCompare;\n\tinfo.hcxt = ConnectionContext;\n\tuint32 hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT | HASH_COMPARE);\n\n\t/* connParamsInfo is same as info, except for entrysize */\n\tconnParamsInfo = info;\n\tconnParamsInfo.entrysize = sizeof(ConnParamsHashEntry);\n\n\tConnectionHash = hash_create(\"citus connection cache (host,port,user,database)\",\n\t\t\t\t\t\t\t\t 64, &info, hashFlags);\n\n\tConnParamsHash = hash_create(\"citus connparams cache (host,port,user,database)\",\n\t\t\t\t\t\t\t\t 64, &connParamsInfo, hashFlags);\n}\n\n\n/*\n * InvalidateConnParamsHashEntries sets every hash entry's isValid flag to false.\n */\nvoid\nInvalidateConnParamsHashEntries(void)\n{\n\tif (ConnParamsHash != NULL)\n\t{\n\t\tConnParamsHashEntry *entry = NULL;\n\t\tHASH_SEQ_STATUS status;\n\n\t\thash_seq_init(&status, ConnParamsHash);\n\t\twhile ((entry = (ConnParamsHashEntry *) hash_seq_search(&status)) != NULL)\n\t\t{\n\t\t\tentry->isValid = false;\n\t\t}\n\t}\n}\n\n\n/*\n * AfterXactConnectionHandling performs connection management activity after the end of a transaction. Both\n * COMMIT and ABORT paths are handled here.\n *\n * This is called by Citus' global transaction callback.\n */\nvoid\nAfterXactConnectionHandling(bool isCommit)\n{\n\tHASH_SEQ_STATUS status;\n\tConnectionHashEntry *entry;\n\n\thash_seq_init(&status, ConnectionHash);\n\twhile ((entry = (ConnectionHashEntry *) hash_seq_search(&status)) != 0)\n\t{\n\t\tif (!entry->isValid)\n\t\t{\n\t\t\t/* skip invalid connection hash entries */\n\t\t\tcontinue;\n\t\t}\n\n\t\tAfterXactHostConnectionHandling(entry, isCommit);\n\n\t\t/*\n\t\t * NB: We leave the hash entry in place, even if there's no individual\n\t\t * connections in it anymore. There seems no benefit in deleting it,\n\t\t * and it'll save a bit of work in the next transaction.\n\t\t */\n\t}\n}\n\n\n/*\n * GetNodeConnection() establishes a connection to remote node, using default\n * user and database.\n *\n * See StartNodeUserDatabaseConnection for details.\n */\nMultiConnection *\nGetNodeConnection(uint32 flags, const char *hostname, int32 port)\n{\n\treturn GetNodeUserDatabaseConnection(flags, hostname, port, NULL, NULL);\n}\n\n\n/*\n * StartNodeConnection initiates a connection to remote node, using default\n * user and database.\n *\n * See StartNodeUserDatabaseConnection for details.\n */\nMultiConnection *\nStartNodeConnection(uint32 flags, const char *hostname, int32 port)\n{\n\tMultiConnection *connection = StartNodeUserDatabaseConnection(flags, hostname, port,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL, NULL);\n\n\t/*\n\t * connection can only be NULL for optional connections, which we don't\n\t * support in this codepath.\n\t */\n\tAssert((flags & OPTIONAL_CONNECTION) == 0);\n\tAssert(connection != NULL);\n\treturn connection;\n}\n\n\n/*\n * GetNodeUserDatabaseConnection establishes connection to remote node.\n *\n * See StartNodeUserDatabaseConnection for details.\n */\nMultiConnection *\nGetNodeUserDatabaseConnection(uint32 flags, const char *hostname, int32 port,\n\t\t\t\t\t\t\t  const char *user, const char *database)\n{\n\tMultiConnection *connection = StartNodeUserDatabaseConnection(flags, hostname, port,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  user, database);\n\n\t/*\n\t * connection can only be NULL for optional connections, which we don't\n\t * support in this codepath.\n\t */\n\tAssert((flags & OPTIONAL_CONNECTION) == 0);\n\tAssert(connection != NULL);\n\n\tFinishConnectionEstablishment(connection);\n\n\treturn connection;\n}\n\n\n/*\n * GetConnectionForLocalQueriesOutsideTransaction returns a localhost connection for\n * subtransaction. To avoid creating excessive connections, we reuse an\n * existing connection.\n */\nMultiConnection *\nGetConnectionForLocalQueriesOutsideTransaction(char *userName)\n{\n\tint connectionFlag = OUTSIDE_TRANSACTION;\n\tMultiConnection *connection =\n\t\tGetNodeUserDatabaseConnection(connectionFlag, LocalHostName, PostPortNumber,\n\t\t\t\t\t\t\t\t\t  userName, get_database_name(MyDatabaseId));\n\n\treturn connection;\n}\n\n\n/*\n * StartNodeUserDatabaseConnection() initiates a connection to a remote node.\n *\n * If user or database are NULL, the current session's defaults are used. The\n * following flags influence connection establishment behaviour:\n * - FORCE_NEW_CONNECTION - a new connection is required\n *\n * The returned connection has only been initiated, not fully\n * established. That's useful to allow parallel connection establishment. If\n * that's not desired use the Get* variant.\n */\nMultiConnection *\nStartNodeUserDatabaseConnection(uint32 flags, const char *hostname, int32 port,\n\t\t\t\t\t\t\t\tconst char *user, const char *database)\n{\n\tConnectionHashKey key;\n\tbool found;\n\n\t/* do some minimal input checks */\n\tif (strlen(hostname) > MAX_NODE_LENGTH)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"hostname exceeds the maximum length of %d\",\n\t\t\t\t\t\t\t   MAX_NODE_LENGTH)));\n\t}\n\n\tstrlcpy(key.hostname, hostname, MAX_NODE_LENGTH);\n\n\tkey.port = port;\n\tif (user)\n\t{\n\t\tstrlcpy(key.user, user, NAMEDATALEN);\n\t}\n\telse\n\t{\n\t\tstrlcpy(key.user, CurrentUserName(), NAMEDATALEN);\n\t}\n\tif (database)\n\t{\n\t\tstrlcpy(key.database, database, NAMEDATALEN);\n\t}\n\telse\n\t{\n\t\tstrlcpy(key.database, CurrentDatabaseName(), NAMEDATALEN);\n\t}\n\n\tif (flags & REQUIRE_REPLICATION_CONNECTION_PARAM)\n\t{\n\t\tkey.replicationConnParam = true;\n\t}\n\telse\n\t{\n\t\tkey.replicationConnParam = false;\n\t}\n\n\tif (CurrentCoordinatedTransactionState == COORD_TRANS_NONE)\n\t{\n\t\tCurrentCoordinatedTransactionState = COORD_TRANS_IDLE;\n\t}\n\n\t/*\n\t * Lookup relevant hash entry. We always enter. If only a cached\n\t * connection is desired, and there's none, we'll simply leave the\n\t * connection list empty.\n\t */\n\n\tConnectionHashEntry *entry = hash_search(ConnectionHash, &key, HASH_ENTER, &found);\n\tif (!found || !entry->isValid)\n\t{\n\t\t/*\n\t\t * We are just building hash entry or previously it was left in an\n\t\t * invalid state as we couldn't allocate memory for it.\n\t\t * So initialize entry->connections list here.\n\t\t */\n\t\tentry->isValid = false;\n\t\tentry->connections = MemoryContextAlloc(ConnectionContext,\n\t\t\t\t\t\t\t\t\t\t\t\tsizeof(dlist_head));\n\t\tdlist_init(entry->connections);\n\n\t\t/*\n\t\t * If MemoryContextAlloc errors out -e.g. during an OOM-, entry->connections\n\t\t * stays as NULL. So entry->isValid should be set to true right after we\n\t\t * initialize entry->connections properly.\n\t\t */\n\t\tentry->isValid = true;\n\t}\n\n\t/* if desired, check whether there's a usable connection */\n\tif (!(flags & FORCE_NEW_CONNECTION))\n\t{\n\t\t/* check connection cache for a connection that's not already in use */\n\t\tMultiConnection *connection = FindAvailableConnection(entry->connections, flags);\n\t\tif (connection)\n\t\t{\n\t\t\t/*\n\t\t\t * Increment the connection stat counter for the connections that are\n\t\t\t * reused only if the connection is in a good state. Here we don't\n\t\t\t * bother shutting down the connection or such if it is not in a good\n\t\t\t * state but we mostly want to avoid incrementing the connection stat\n\t\t\t * counter for a connection that the caller cannot really use.\n\t\t\t */\n\t\t\tif (PQstatus(connection->pgConn) == CONNECTION_OK)\n\t\t\t{\n\t\t\t\tIncrementStatCounterForMyDb(STAT_CONNECTION_REUSED);\n\t\t\t}\n\n\t\t\treturn connection;\n\t\t}\n\t}\n\telse if (flags & REQUIRE_METADATA_CONNECTION)\n\t{\n\t\t/* FORCE_NEW_CONNECTION and REQUIRE_METADATA_CONNECTION are incompatible */\n\t\tereport(ERROR, (errmsg(\"metadata connections cannot be forced to open \"\n\t\t\t\t\t\t\t   \"a new connection\")));\n\t}\n\n\n\t/*\n\t * Either no caching desired, or no pre-established, non-claimed,\n\t * connection present. Initiate connection establishment.\n\t */\n\tMultiConnection *connection = MemoryContextAllocZero(ConnectionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t sizeof(MultiConnection));\n\tconnection->initializationState = POOL_STATE_NOT_INITIALIZED;\n\tdlist_push_tail(entry->connections, &connection->connectionNode);\n\n\t/* these two flags are by nature cannot happen at the same time */\n\tAssert(!((flags & WAIT_FOR_CONNECTION) && (flags & OPTIONAL_CONNECTION)));\n\n\tif (flags & WAIT_FOR_CONNECTION)\n\t{\n\t\tWaitLoopForSharedConnection(hostname, port);\n\t}\n\telse if (flags & OPTIONAL_CONNECTION)\n\t{\n\t\t/*\n\t\t * We can afford to skip establishing an optional connection. For\n\t\t * non-optional connections, we first retry for some time. If we still\n\t\t * cannot reserve the right to establish a connection, we prefer to\n\t\t * error out.\n\t\t */\n\t\tif (!TryToIncrementSharedConnectionCounter(hostname, port))\n\t\t{\n\t\t\t/* do not track the connection anymore */\n\t\t\tdlist_delete(&connection->connectionNode);\n\t\t\tpfree(connection);\n\n\t\t\t/*\n\t\t\t * Here we don't increment the connection stat counter for the optional\n\t\t\t * connections that we gave up establishing due to connection throttling\n\t\t\t * because the callers who request optional connections know how to\n\t\t\t * survive without them.\n\t\t\t */\n\t\t\treturn NULL;\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * The caller doesn't want the connection manager to wait\n\t\t * until a connection slot is available on the remote node.\n\t\t * In the end, we might fail to establish connection to the\n\t\t * remote node as it might not have any space in\n\t\t * max_connections for this connection establishment.\n\t\t *\n\t\t * Still, we keep track of the connection counter.\n\t\t */\n\t\tIncrementSharedConnectionCounter(hostname, port);\n\t}\n\n\n\t/*\n\t * We've already incremented the counter above, so we should decrement\n\t * when we're done with the connection.\n\t */\n\tconnection->initializationState = POOL_STATE_COUNTER_INCREMENTED;\n\n\tStartConnectionEstablishment(connection, &key);\n\n\tResetShardPlacementAssociation(connection);\n\n\n\tif ((flags & REQUIRE_METADATA_CONNECTION))\n\t{\n\t\tconnection->useForMetadataOperations = true;\n\t}\n\n\t/* fully initialized the connection, record it */\n\tconnection->initializationState = POOL_STATE_INITIALIZED;\n\n\treturn connection;\n}\n\n\n/*\n * FindAvailableConnection searches the given list of connections for one that\n * is not claimed exclusively.\n *\n * If no connection is available, FindAvailableConnection returns NULL.\n */\nstatic MultiConnection *\nFindAvailableConnection(dlist_head *connections, uint32 flags)\n{\n\tList *metadataConnectionCandidateList = NIL;\n\n\tdlist_iter iter;\n\tdlist_foreach(iter, connections)\n\t{\n\t\tMultiConnection *connection =\n\t\t\tdlist_container(MultiConnection, connectionNode, iter.cur);\n\n\t\tif (flags & OUTSIDE_TRANSACTION)\n\t\t{\n\t\t\t/* don't return connections that are used in transactions */\n\t\t\tif (connection->\n\t\t\t\tremoteTransaction.transactionState != REMOTE_TRANS_NOT_STARTED)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t/* don't return claimed connections */\n\t\tif (connection->claimedExclusively)\n\t\t{\n\t\t\t/* connection is in use for an ongoing operation */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (connection->forceCloseAtTransactionEnd &&\n\t\t\t!connection->remoteTransaction.beginSent)\n\t\t{\n\t\t\t/*\n\t\t\t * This is a connection that should be closed, probably because\n\t\t\t * of old connection options or removing a node. This will\n\t\t\t * automatically be closed at the end of the transaction. But, if we are still\n\t\t\t * inside a transaction, we should keep using this connection as long as a remote\n\t\t\t * transaction is in progress over the connection. The main use for this case\n\t\t\t * is having some commands inside a transaction block after removing nodes. And, we\n\t\t\t * currently allow very limited operations after removing a node inside a\n\t\t\t * transaction block (e.g., no placement access can happen).\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (connection->initializationState != POOL_STATE_INITIALIZED)\n\t\t{\n\t\t\t/*\n\t\t\t * If the connection has not been initialized, it should not be\n\t\t\t * considered as available.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ((flags & REQUIRE_METADATA_CONNECTION) &&\n\t\t\t!connection->useForMetadataOperations)\n\t\t{\n\t\t\t/*\n\t\t\t * The caller requested a metadata connection, and this is not the\n\t\t\t * metadata connection. Still, this is a candidate for becoming a\n\t\t\t * metadata connection.\n\t\t\t */\n\t\t\tmetadataConnectionCandidateList =\n\t\t\t\tlappend(metadataConnectionCandidateList, connection);\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn connection;\n\t}\n\n\tif ((flags & REQUIRE_METADATA_CONNECTION) &&\n\t\tlist_length(metadataConnectionCandidateList) > 0)\n\t{\n\t\t/*\n\t\t * Caller asked a metadata connection, and we couldn't find a connection\n\t\t * that has already been used for metadata operations.\n\t\t *\n\t\t * So, we pick the first connection as the metadata connection.\n\t\t */\n\t\tMultiConnection *metadataConnection =\n\t\t\tlinitial(metadataConnectionCandidateList);\n\n\t\tAssert(!metadataConnection->claimedExclusively);\n\n\t\t/* remember that we use this connection for metadata operations */\n\t\tmetadataConnection->useForMetadataOperations = true;\n\n\t\t/*\n\t\t * We cannot have multiple metadata connections. If we see\n\t\t * this error, it is likely that there is a bug in connection\n\t\t * management.\n\t\t */\n\t\tErrorIfMultipleMetadataConnectionExists(connections);\n\n\t\treturn metadataConnection;\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * ErrorIfMultipleMetadataConnectionExists throws an error if the\n * input connection dlist contains more than one metadata connections.\n */\nstatic void\nErrorIfMultipleMetadataConnectionExists(dlist_head *connections)\n{\n\tbool foundMetadataConnection = false;\n\tdlist_iter iter;\n\tdlist_foreach(iter, connections)\n\t{\n\t\tMultiConnection *connection =\n\t\t\tdlist_container(MultiConnection, connectionNode, iter.cur);\n\n\t\tif (connection->useForMetadataOperations)\n\t\t{\n\t\t\tif (foundMetadataConnection)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"cannot have multiple metadata connections\")));\n\t\t\t}\n\n\t\t\tfoundMetadataConnection = true;\n\t\t}\n\t}\n}\n\n\n/*\n * CloseAllConnectionsAfterTransaction sets the forceClose flag of all the\n * connections. This is mainly done when citus.node_conninfo changes.\n */\nvoid\nCloseAllConnectionsAfterTransaction(void)\n{\n\tif (ConnectionHash == NULL)\n\t{\n\t\treturn;\n\t}\n\tHASH_SEQ_STATUS status;\n\tConnectionHashEntry *entry;\n\n\thash_seq_init(&status, ConnectionHash);\n\twhile ((entry = (ConnectionHashEntry *) hash_seq_search(&status)) != 0)\n\t{\n\t\tif (!entry->isValid)\n\t\t{\n\t\t\t/* skip invalid connection hash entries */\n\t\t\tcontinue;\n\t\t}\n\n\t\tdlist_iter iter;\n\n\t\tdlist_head *connections = entry->connections;\n\t\tdlist_foreach(iter, connections)\n\t\t{\n\t\t\tMultiConnection *connection =\n\t\t\t\tdlist_container(MultiConnection, connectionNode, iter.cur);\n\n\t\t\tconnection->forceCloseAtTransactionEnd = true;\n\t\t}\n\t}\n}\n\n\n/*\n * ConnectionAvailableToNode returns a MultiConnection if the session has at least\n * one connection established and avaliable to use to the give node. Else, returns\n * false.\n */\nMultiConnection *\nConnectionAvailableToNode(char *hostName, int nodePort, const char *userName,\n\t\t\t\t\t\t  const char *database)\n{\n\tConnectionHashKey key;\n\tbool found = false;\n\n\tstrlcpy(key.hostname, hostName, MAX_NODE_LENGTH);\n\tkey.port = nodePort;\n\tstrlcpy(key.user, userName, NAMEDATALEN);\n\tstrlcpy(key.database, database, NAMEDATALEN);\n\tkey.replicationConnParam = false;\n\n\tConnectionHashEntry *entry =\n\t\t(ConnectionHashEntry *) hash_search(ConnectionHash, &key, HASH_FIND, &found);\n\n\tif (!found || !entry->isValid)\n\t{\n\t\treturn false;\n\t}\n\n\tint flags = 0;\n\tMultiConnection *connection = FindAvailableConnection(entry->connections, flags);\n\n\treturn connection;\n}\n\n\n/*\n * CloseNodeConnectionsAfterTransaction sets the forceClose flag of the connections\n * to a particular node as true such that the connections are no longer cached. This\n * is mainly used when a worker leaves the cluster.\n */\nvoid\nCloseNodeConnectionsAfterTransaction(char *nodeName, int nodePort)\n{\n\tHASH_SEQ_STATUS status;\n\tConnectionHashEntry *entry;\n\n\thash_seq_init(&status, ConnectionHash);\n\twhile ((entry = (ConnectionHashEntry *) hash_seq_search(&status)) != 0)\n\t{\n\t\tif (!entry->isValid)\n\t\t{\n\t\t\t/* skip invalid connection hash entries */\n\t\t\tcontinue;\n\t\t}\n\n\t\tdlist_iter iter;\n\n\t\tif (strcmp(entry->key.hostname, nodeName) != 0 || entry->key.port != nodePort)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tdlist_head *connections = entry->connections;\n\t\tdlist_foreach(iter, connections)\n\t\t{\n\t\t\tMultiConnection *connection =\n\t\t\t\tdlist_container(MultiConnection, connectionNode, iter.cur);\n\n\t\t\tconnection->forceCloseAtTransactionEnd = true;\n\t\t}\n\t}\n}\n\n\n/*\n * Close a previously established connection.\n */\nvoid\nCloseConnection(MultiConnection *connection)\n{\n\tConnectionHashKey key;\n\tbool found;\n\n\t/* close connection */\n\tCitusPQFinish(connection);\n\n\tstrlcpy(key.hostname, connection->hostname, MAX_NODE_LENGTH);\n\tkey.port = connection->port;\n\tkey.replicationConnParam = connection->requiresReplication;\n\tstrlcpy(key.user, connection->user, NAMEDATALEN);\n\tstrlcpy(key.database, connection->database, NAMEDATALEN);\n\n\thash_search(ConnectionHash, &key, HASH_FIND, &found);\n\n\tif (found)\n\t{\n\t\t/* unlink from list of open connections */\n\t\tdlist_delete(&connection->connectionNode);\n\n\t\t/* same for transaction state and shard/placement machinery */\n\t\tCloseShardPlacementAssociation(connection);\n\t\tResetRemoteTransaction(connection);\n\n\t\t/* we leave the per-host entry alive */\n\t\tpfree(connection);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"closing untracked connection\")));\n\t}\n}\n\n\n/*\n * ShutdownAllConnections shutdowns all the MultiConnections in the\n * ConnectionHash.\n *\n * This function is intended to be called atexit() of the backend, so\n * that the cached connections are closed properly. Calling this function\n * at another point in the code could be dangerous, so think twice if you\n * need to call this function.\n */\nvoid\nShutdownAllConnections(void)\n{\n\tConnectionHashEntry *entry = NULL;\n\tHASH_SEQ_STATUS status;\n\n\thash_seq_init(&status, ConnectionHash);\n\twhile ((entry = (ConnectionHashEntry *) hash_seq_search(&status)) != NULL)\n\t{\n\t\tif (!entry->isValid)\n\t\t{\n\t\t\t/* skip invalid connection hash entries */\n\t\t\tcontinue;\n\t\t}\n\n\t\tdlist_iter iter;\n\n\t\tdlist_foreach(iter, entry->connections)\n\t\t{\n\t\t\tMultiConnection *connection =\n\t\t\t\tdlist_container(MultiConnection, connectionNode, iter.cur);\n\n\t\t\tShutdownConnection(connection);\n\t\t}\n\t}\n}\n\n\n/*\n * ShutdownConnection, if necessary cancels the currently running statement,\n * and then closes the underlying libpq connection.  The MultiConnection\n * itself is left intact.\n *\n * NB: Cancelling a statement requires network IO, and currently is not\n * interruptible. Unfortunately libpq does not provide a non-blocking\n * implementation of PQcancel(), so we don't have much choice for now.\n */\nvoid\nShutdownConnection(MultiConnection *connection)\n{\n\t/*\n\t * Only cancel statement if there's currently one running, and the\n\t * connection is in an OK state.\n\t */\n\tif (PQstatus(connection->pgConn) == CONNECTION_OK &&\n\t\tPQtransactionStatus(connection->pgConn) == PQTRANS_ACTIVE)\n\t{\n\t\tSendCancelationRequest(connection);\n\t}\n\tCitusPQFinish(connection);\n}\n\n\n/*\n * MultiConnectionStatePoll executes a PQconnectPoll on the connection to progress the\n * connection establishment. The return value of this function indicates if the\n * MultiConnectionPollState has been changed, which could require a change to the WaitEventSet\n */\nstatic bool\nMultiConnectionStatePoll(MultiConnectionPollState *connectionState)\n{\n\tMultiConnection *connection = connectionState->connection;\n\tConnStatusType status = PQstatus(connection->pgConn);\n\tPostgresPollingStatusType oldPollmode = connectionState->pollmode;\n\n\tAssert(connectionState->phase == MULTI_CONNECTION_PHASE_CONNECTING);\n\n\tif (status == CONNECTION_OK)\n\t{\n\t\tconnectionState->phase = MULTI_CONNECTION_PHASE_CONNECTED;\n\t\treturn true;\n\t}\n\telse if (status == CONNECTION_BAD)\n\t{\n\t\t/* FIXME: retries? */\n\t\tconnectionState->phase = MULTI_CONNECTION_PHASE_ERROR;\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tconnectionState->phase = MULTI_CONNECTION_PHASE_CONNECTING;\n\t}\n\n\tconnectionState->pollmode = PQconnectPoll(connection->pgConn);\n\n\t/*\n\t * FIXME: Do we want to add transparent retry support here?\n\t */\n\tif (connectionState->pollmode == PGRES_POLLING_FAILED)\n\t{\n\t\tconnectionState->phase = MULTI_CONNECTION_PHASE_ERROR;\n\t\treturn true;\n\t}\n\telse if (connectionState->pollmode == PGRES_POLLING_OK)\n\t{\n\t\tconnectionState->phase = MULTI_CONNECTION_PHASE_CONNECTED;\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tAssert(connectionState->pollmode == PGRES_POLLING_WRITING ||\n\t\t\t   connectionState->pollmode == PGRES_POLLING_READING);\n\t}\n\n\treturn (oldPollmode != connectionState->pollmode) ? true : false;\n}\n\n\n/*\n * EventSetSizeForConnectionList calculates the space needed for a WaitEventSet based on a\n * list of connections.\n */\ninline static int\nEventSetSizeForConnectionList(List *connections)\n{\n\t/* we need space for 2 postgres events in the waitset on top of the connections */\n\treturn list_length(connections) + 2;\n}\n\n\n/*\n * WaitEventSetFromMultiConnectionStates takes a list of MultiConnectionStates and adds\n * all sockets of the connections that are still in the connecting phase to a WaitSet,\n * taking into account the maximum number of connections that could be added in total to\n * a WaitSet.\n *\n * waitCount populates the number of connections added to the WaitSet in case when a\n * non-NULL pointer is provided.\n */\nstatic WaitEventSet *\nWaitEventSetFromMultiConnectionStates(List *connections, int *waitCount)\n{\n\tconst int eventSetSize = EventSetSizeForConnectionList(connections);\n\tint numEventsAdded = 0;\n\n\tif (waitCount)\n\t{\n\t\t*waitCount = 0;\n\t}\n\n\tWaitEventSet *waitEventSet = CreateWaitEventSet(WaitEventSetTracker_compat,\n\t\t\t\t\t\t\t\t\t\t\t\t\teventSetSize);\n\tEnsureReleaseResource((MemoryContextCallbackFunction) (&FreeWaitEventSet),\n\t\t\t\t\t\t  waitEventSet);\n\n\t/*\n\t * Put the wait events for the signal latch and postmaster death at the end such that\n\t * event index + pendingConnectionsStartIndex = the connection index in the array.\n\t */\n\tAddWaitEventToSet(waitEventSet, WL_POSTMASTER_DEATH, PGINVALID_SOCKET, NULL, NULL);\n\tAddWaitEventToSet(waitEventSet, WL_LATCH_SET, PGINVALID_SOCKET, MyLatch, NULL);\n\tnumEventsAdded += 2;\n\n\tMultiConnectionPollState *connectionState = NULL;\n\tforeach_declared_ptr(connectionState, connections)\n\t{\n\t\tif (numEventsAdded >= eventSetSize)\n\t\t{\n\t\t\t/* room for events to schedule is exhausted */\n\t\t\tbreak;\n\t\t}\n\n\t\tif (connectionState->phase != MULTI_CONNECTION_PHASE_CONNECTING)\n\t\t{\n\t\t\t/* connections that are not connecting will not be added to the WaitSet */\n\t\t\tcontinue;\n\t\t}\n\n\t\tint sock = PQsocket(connectionState->connection->pgConn);\n\n\t\tint eventMask = MultiConnectionStateEventMask(connectionState);\n\n\t\tint waitEventSetIndex =\n\t\t\tCitusAddWaitEventSetToSet(waitEventSet, eventMask, sock,\n\t\t\t\t\t\t\t\t\t  NULL, (void *) connectionState);\n\t\tif (waitEventSetIndex == WAIT_EVENT_SET_INDEX_FAILED)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t\terrmsg(\"connection establishment for node %s:%d failed\",\n\t\t\t\t\t\t\t\t   connectionState->connection->hostname,\n\t\t\t\t\t\t\t\t   connectionState->connection->port),\n\t\t\t\t\t\t\terrhint(\"Check both the local and remote server logs for the \"\n\t\t\t\t\t\t\t\t\t\"connection establishment errors.\")));\n\t\t}\n\n\t\tnumEventsAdded++;\n\n\t\tif (waitCount)\n\t\t{\n\t\t\t*waitCount = *waitCount + 1;\n\t\t}\n\t}\n\n\treturn waitEventSet;\n}\n\n\n/*\n * MultiConnectionStateEventMask returns the eventMask use by the WaitEventSet for the\n * for the socket associated with the connection based on the pollmode PQconnectPoll\n * returned in its last invocation\n */\nstatic uint32\nMultiConnectionStateEventMask(MultiConnectionPollState *connectionState)\n{\n\tuint32 eventMask = 0;\n\tif (connectionState->pollmode == PGRES_POLLING_READING)\n\t{\n\t\teventMask |= WL_SOCKET_READABLE;\n\t}\n\telse\n\t{\n\t\teventMask |= WL_SOCKET_WRITEABLE;\n\t}\n\treturn eventMask;\n}\n\n\n/*\n * FinishConnectionListEstablishment takes a list of MultiConnection and finishes the\n * connections establishment asynchronously for all connections not already fully\n * connected.\n */\nvoid\nFinishConnectionListEstablishment(List *multiConnectionList)\n{\n\tinstr_time connectionStart;\n\tINSTR_TIME_SET_CURRENT(connectionStart);\n\n\tList *connectionStates = NULL;\n\n\tWaitEventSet *waitEventSet = NULL;\n\tbool waitEventSetRebuild = true;\n\tint waitCount = 0;\n\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, multiConnectionList)\n\t{\n\t\tMultiConnectionPollState *connectionState =\n\t\t\tpalloc0(sizeof(MultiConnectionPollState));\n\n\t\tconnectionState->connection = connection;\n\n\t\t/*\n\t\t * before we can build the waitset to wait for asynchronous IO we need to know the\n\t\t * pollmode to use for the sockets. This is populated by executing one round of\n\t\t * PQconnectPoll. This updates the MultiConnectionPollState struct with its phase and\n\t\t * its next poll mode.\n\t\t */\n\t\tMultiConnectionStatePoll(connectionState);\n\n\t\tconnectionStates = lappend(connectionStates, connectionState);\n\t\tif (connectionState->phase == MULTI_CONNECTION_PHASE_CONNECTING)\n\t\t{\n\t\t\twaitCount++;\n\t\t}\n\t\telse if (connectionState->phase == MULTI_CONNECTION_PHASE_ERROR)\n\t\t{\n\t\t\t/*\n\t\t\t * Here we count the connections establishments that failed and that\n\t\t\t * we won't wait anymore.\n\t\t\t */\n\t\t\tIncrementStatCounterForMyDb(STAT_CONNECTION_ESTABLISHMENT_FAILED);\n\t\t}\n\t}\n\n\t/* prepare space for socket events */\n\tWaitEvent *events = (WaitEvent *) palloc0(EventSetSizeForConnectionList(\n\t\t\t\t\t\t\t\t\t\t\t\t  connectionStates) *\n\t\t\t\t\t\t\t\t\t\t\t  sizeof(WaitEvent));\n\n\t/*\n\t * for high connection counts with lots of round trips we could potentially have a lot\n\t * of (big) waitsets that we'd like to clean right after we have used them. To do this\n\t * we switch to a temporary memory context for this loop which gets reset at the end\n\t */\n\tMemoryContext oldContext = MemoryContextSwitchTo(\n\t\tAllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t  \"connection establishment temporary context\",\n\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_SIZES));\n\twhile (waitCount > 0)\n\t{\n\t\tlong timeout = MillisecondsToTimeout(connectionStart, NodeConnectionTimeout);\n\n\t\tif (waitEventSetRebuild)\n\t\t{\n\t\t\tMemoryContextReset(CurrentMemoryContext);\n\t\t\twaitEventSet = WaitEventSetFromMultiConnectionStates(connectionStates,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t &waitCount);\n\t\t\twaitEventSetRebuild = false;\n\n\t\t\tif (waitCount <= 0)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tint eventCount = WaitEventSetWait(waitEventSet, timeout, events, waitCount,\n\t\t\t\t\t\t\t\t\t\t  WAIT_EVENT_CLIENT_READ);\n\n\t\tfor (int eventIndex = 0; eventIndex < eventCount; eventIndex++)\n\t\t{\n\t\t\tWaitEvent *event = &events[eventIndex];\n\t\t\tMultiConnectionPollState *connectionState =\n\t\t\t\t(MultiConnectionPollState *) event->user_data;\n\n\t\t\tif (event->events & WL_POSTMASTER_DEATH)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Here we don't increment the connection stat counter for the\n\t\t\t\t * optional failed connections because this is not a connection\n\t\t\t\t * failure, but a postmaster death in the local node.\n\t\t\t\t */\n\t\t\t\tereport(ERROR, (errmsg(\"postmaster was shut down, exiting\")));\n\t\t\t}\n\n\t\t\tif (event->events & WL_LATCH_SET)\n\t\t\t{\n\t\t\t\tResetLatch(MyLatch);\n\n\t\t\t\tCHECK_FOR_INTERRUPTS();\n\n\t\t\t\tif (IsHoldOffCancellationReceived())\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * because we can't break from 2 loops easily we need to not forget to\n\t\t\t\t\t * reset the memory context\n\t\t\t\t\t */\n\t\t\t\t\tMemoryContextDelete(MemoryContextSwitchTo(oldContext));\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Similarly, we don't increment the connection stat counter for the\n\t\t\t\t\t * failed connections here because this is not a connection failure\n\t\t\t\t\t * but a cancellation request is received.\n\t\t\t\t\t */\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbool beforePollSocket = PQsocket(connectionState->connection->pgConn);\n\t\t\tbool connectionStateChanged = MultiConnectionStatePoll(connectionState);\n\n\t\t\tif (beforePollSocket != PQsocket(connectionState->connection->pgConn))\n\t\t\t{\n\t\t\t\t/* rebuild the wait events if MultiConnectionStatePoll() changed the socket */\n\t\t\t\twaitEventSetRebuild = true;\n\t\t\t}\n\n\t\t\tif (connectionStateChanged)\n\t\t\t{\n\t\t\t\tif (connectionState->phase != MULTI_CONNECTION_PHASE_CONNECTING)\n\t\t\t\t{\n\t\t\t\t\t/* we cannot stop waiting for connection, so rebuild the event set */\n\t\t\t\t\twaitEventSetRebuild = true;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* connection state changed, reset the event mask */\n\t\t\t\t\tuint32 eventMask = MultiConnectionStateEventMask(connectionState);\n\t\t\t\t\tbool success =\n\t\t\t\t\t\tCitusModifyWaitEvent(waitEventSet, event->pos,\n\t\t\t\t\t\t\t\t\t\t\t eventMask, NULL);\n\t\t\t\t\tif (!success)\n\t\t\t\t\t{\n\t\t\t\t\t\tIncrementStatCounterForMyDb(STAT_CONNECTION_ESTABLISHMENT_FAILED);\n\t\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t\t\t\t\terrmsg(\"connection establishment for node %s:%d \"\n\t\t\t\t\t\t\t\t\t\t\t   \"failed\", connection->hostname,\n\t\t\t\t\t\t\t\t\t\t\t   connection->port),\n\t\t\t\t\t\t\t\t\t\terrhint(\"Check both the local and remote server \"\n\t\t\t\t\t\t\t\t\t\t\t\t\"logs for the connection establishment \"\n\t\t\t\t\t\t\t\t\t\t\t\t\"errors.\")));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * The state has changed to connected, update the connection's\n\t\t\t\t * state as well.\n\t\t\t\t */\n\t\t\t\tif (connectionState->phase == MULTI_CONNECTION_PHASE_CONNECTED)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Since WaitEventSetFromMultiConnectionStates() only adds the\n\t\t\t\t\t * connections that we haven't completed the connection\n\t\t\t\t\t * establishment yet, here we always have a new connection.\n\t\t\t\t\t * In other words, at this point, we surely know that we're\n\t\t\t\t\t * not dealing with a cached connection.\n\t\t\t\t\t */\n\t\t\t\t\tbool newConnection = true;\n\t\t\t\t\tMarkConnectionConnected(connectionState->connection, newConnection);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (eventCount == 0)\n\t\t{\n\t\t\t/*\n\t\t\t * timeout has occurred on waitset, double check the timeout since\n\t\t\t * connectionStart and if passed close all non-finished connections\n\t\t\t */\n\n\t\t\tif (MillisecondsPassedSince(connectionStart) >= NodeConnectionTimeout)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * showing as a warning, can't be an error. In some cases queries can\n\t\t\t\t * proceed with only some of the connections being fully established.\n\t\t\t\t * Queries that can't will error then and there\n\t\t\t\t */\n\t\t\t\tereport(WARNING, (errmsg(\"could not establish connection after %u ms\",\n\t\t\t\t\t\t\t\t\t\t NodeConnectionTimeout)));\n\n\t\t\t\t/*\n\t\t\t\t * Close all connections that have not been fully established.\n\t\t\t\t */\n\t\t\t\tCloseNotReadyMultiConnectionStates(connectionStates);\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tMemoryContextDelete(MemoryContextSwitchTo(oldContext));\n}\n\n\n/*\n * MillisecondsPassedSince returns the number of milliseconds elapsed between an\n * instr_time & the current time.\n */\ndouble\nMillisecondsPassedSince(instr_time moment)\n{\n\tinstr_time timeSinceMoment;\n\tINSTR_TIME_SET_CURRENT(timeSinceMoment);\n\tINSTR_TIME_SUBTRACT(timeSinceMoment, moment);\n\treturn INSTR_TIME_GET_MILLISEC(timeSinceMoment);\n}\n\n\n/*\n * MillisecondsToTimeout returns the numer of milliseconds that still need to elapse\n * before msAfterStart milliseconds have passed since start. The outcome can be used to\n * pass to the Wait of an EventSet to make sure it returns after the timeout has passed.\n */\nlong\nMillisecondsToTimeout(instr_time start, long msAfterStart)\n{\n\treturn msAfterStart - MillisecondsPassedSince(start);\n}\n\n\n/*\n * CloseNotReadyMultiConnectionStates calls CloseConnection for all MultiConnection's\n * tracked in the MultiConnectionPollState list passed in, only if the connection is not yet\n * fully established.\n *\n * This function removes the pointer to the MultiConnection data after the Connections are\n * closed since they should not be used anymore.\n */\nstatic void\nCloseNotReadyMultiConnectionStates(List *connectionStates)\n{\n\tMultiConnectionPollState *connectionState = NULL;\n\tforeach_declared_ptr(connectionState, connectionStates)\n\t{\n\t\tMultiConnection *connection = connectionState->connection;\n\n\t\tif (connectionState->phase != MULTI_CONNECTION_PHASE_CONNECTING)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* close connection, otherwise we take up resource on the other side */\n\t\tCitusPQFinish(connection);\n\n\t\tIncrementStatCounterForMyDb(STAT_CONNECTION_ESTABLISHMENT_FAILED);\n\t}\n}\n\n\n/*\n * CitusPQFinish is a wrapper around PQfinish and does book keeping on shared connection\n * counters.\n */\nstatic void\nCitusPQFinish(MultiConnection *connection)\n{\n\tif (connection->pgConn != NULL)\n\t{\n\t\tPQfinish(connection->pgConn);\n\t\tconnection->pgConn = NULL;\n\t}\n\n\t/* behave idempotently, there is no gurantee that CitusPQFinish() is called once */\n\tif (connection->initializationState >= POOL_STATE_COUNTER_INCREMENTED)\n\t{\n\t\tDecrementSharedConnectionCounter(connection->hostname, connection->port);\n\t\tconnection->initializationState = POOL_STATE_NOT_INITIALIZED;\n\t}\n}\n\n\n/*\n * Close connections on timeout in FinishConnectionListEstablishment\n * Synchronously finish connection establishment of an individual connection.\n * This function is a convenience wrapped around FinishConnectionListEstablishment.\n */\nvoid\nFinishConnectionEstablishment(MultiConnection *connection)\n{\n\tFinishConnectionListEstablishment(list_make1(connection));\n}\n\n\n/*\n * ForceConnectionCloseAtTransactionEnd marks connection to be closed at the end of the\n * transaction.\n */\nvoid\nForceConnectionCloseAtTransactionEnd(MultiConnection *connection)\n{\n\tconnection->forceCloseAtTransactionEnd = true;\n}\n\n\n/*\n * ClaimConnectionExclusively signals that this connection is actively being\n * used. That means it'll not be, again, returned by\n * StartNodeUserDatabaseConnection() et al until releases with\n * UnclaimConnection().\n */\nvoid\nClaimConnectionExclusively(MultiConnection *connection)\n{\n\tAssert(!connection->claimedExclusively);\n\tconnection->claimedExclusively = true;\n}\n\n\n/*\n * UnclaimConnection signals that this connection is not being used\n * anymore. That means it again may be returned by\n * StartNodeUserDatabaseConnection() et al.\n */\nvoid\nUnclaimConnection(MultiConnection *connection)\n{\n\tconnection->claimedExclusively = false;\n}\n\n\nstatic uint32\nConnectionHashHash(const void *key, Size keysize)\n{\n\tConnectionHashKey *entry = (ConnectionHashKey *) key;\n\n\tuint32 hash = string_hash(entry->hostname, NAMEDATALEN);\n\thash = hash_combine(hash, hash_uint32(entry->port));\n\thash = hash_combine(hash, string_hash(entry->user, NAMEDATALEN));\n\thash = hash_combine(hash, string_hash(entry->database, NAMEDATALEN));\n\thash = hash_combine(hash, hash_uint32(entry->replicationConnParam));\n\n\treturn hash;\n}\n\n\nstatic int\nConnectionHashCompare(const void *a, const void *b, Size keysize)\n{\n\tConnectionHashKey *ca = (ConnectionHashKey *) a;\n\tConnectionHashKey *cb = (ConnectionHashKey *) b;\n\n\tif (strncmp(ca->hostname, cb->hostname, MAX_NODE_LENGTH) != 0 ||\n\t\tca->port != cb->port ||\n\t\tca->replicationConnParam != cb->replicationConnParam ||\n\t\tstrncmp(ca->user, cb->user, NAMEDATALEN) != 0 ||\n\t\tstrncmp(ca->database, cb->database, NAMEDATALEN) != 0)\n\t{\n\t\treturn 1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * Asynchronously establish connection to a remote node, but don't wait for\n * that to finish. DNS lookups etc. are performed synchronously though.\n */\nstatic void\nStartConnectionEstablishment(MultiConnection *connection, ConnectionHashKey *key)\n{\n\tstatic uint64 connectionId = 1;\n\n\tConnParamsHashEntry *entry = FindOrCreateConnParamsEntry(key);\n\n\tstrlcpy(connection->hostname, key->hostname, MAX_NODE_LENGTH);\n\tconnection->port = key->port;\n\tstrlcpy(connection->database, key->database, NAMEDATALEN);\n\tstrlcpy(connection->user, key->user, NAMEDATALEN);\n\tconnection->requiresReplication = key->replicationConnParam;\n\n\tconnection->pgConn = PQconnectStartParams((const char **) entry->keywords,\n\t\t\t\t\t\t\t\t\t\t\t  (const char **) entry->values,\n\t\t\t\t\t\t\t\t\t\t\t  false);\n\tINSTR_TIME_SET_CURRENT(connection->connectionEstablishmentStart);\n\n\t/* do not increment for restarted connections */\n\tif (connection->connectionId == 0)\n\t{\n\t\tconnection->connectionId = connectionId++;\n\t}\n\n\t/*\n\t * To avoid issues with interrupts not getting caught all our connections\n\t * are managed in a non-blocking manner. remote_commands.c provides\n\t * wrappers emulating blocking behaviour.\n\t */\n\tPQsetnonblocking(connection->pgConn, true);\n\n\tSetCitusNoticeReceiver(connection);\n}\n\n\n/*\n * FindOrCreateConnParamsEntry searches ConnParamsHash for the given key,\n * if it is not found, it is created.\n */\nstatic ConnParamsHashEntry *\nFindOrCreateConnParamsEntry(ConnectionHashKey *key)\n{\n\tbool found = false;\n\n\t/* search our cache for precomputed connection settings */\n\tConnParamsHashEntry *entry = hash_search(ConnParamsHash, key, HASH_ENTER, &found);\n\tif (!found || !entry->isValid)\n\t{\n\t\tif (!found)\n\t\t{\n\t\t\t/*\n\t\t\t * Zero out entry, but not the key part.\n\t\t\t * Avoids leaving invalid pointers in hash table if GetConnParam throws with MemoryContextAllocZero.\n\t\t\t */\n\t\t\tmemset(((char *) entry) + sizeof(ConnectionHashKey), 0,\n\t\t\t\t   sizeof(ConnParamsHashEntry) - sizeof(ConnectionHashKey));\n\t\t}\n\n\t\t/* avoid leaking memory in the keys and values arrays */\n\t\tif (found && !entry->isValid)\n\t\t{\n\t\t\tFreeConnParamsHashEntryFields(entry);\n\t\t}\n\n\t\t/* if not found or not valid, compute them from GUC, runtime, etc. */\n\t\tGetConnParams(key, &entry->keywords, &entry->values, &entry->runtimeParamStart,\n\t\t\t\t\t  ConnectionContext);\n\n\t\tentry->isValid = true;\n\t}\n\n\treturn entry;\n}\n\n\n/*\n * FreeConnParamsHashEntryFields frees any dynamically allocated memory reachable\n * from the fields of the provided ConnParamsHashEntry. This includes all runtime\n * libpq keywords and values, as well as the actual arrays storing them.\n */\nstatic void\nFreeConnParamsHashEntryFields(ConnParamsHashEntry *entry)\n{\n\t/*\n\t * if there was a memory error during the initialization of ConnParamHashEntry in\n\t * GetConnParams the keywords or values might not have been initialized completely.\n\t * We check if they have been initialized before freeing them.\n\t *\n\t * We only iteratively free the lists starting at the index pointed to by\n\t * entry->runtimeParamStart as all entries before are settings that are managed\n\t * separately.\n\t */\n\n\tif (entry->keywords != NULL)\n\t{\n\t\tchar **keyword = &entry->keywords[entry->runtimeParamStart];\n\t\twhile (*keyword != NULL)\n\t\t{\n\t\t\tpfree(*keyword);\n\t\t\tkeyword++;\n\t\t}\n\t\tpfree(entry->keywords);\n\t\tentry->keywords = NULL;\n\t}\n\n\tif (entry->values != NULL)\n\t{\n\t\tchar **value = &entry->values[entry->runtimeParamStart];\n\t\twhile (*value != NULL)\n\t\t{\n\t\t\tpfree(*value);\n\t\t\tvalue++;\n\t\t}\n\t\tpfree(entry->values);\n\t\tentry->values = NULL;\n\t}\n\n\tentry->runtimeParamStart = 0;\n}\n\n\n/*\n * AfterXactHostConnectionHandling closes all remote connections if not necessary anymore (i.e. not session\n * lifetime), or if in a failed state.\n */\nstatic void\nAfterXactHostConnectionHandling(ConnectionHashEntry *entry, bool isCommit)\n{\n\tif (!entry || !entry->isValid)\n\t{\n\t\t/* callers only pass valid hash entries but let's be on the safe side */\n\t\tereport(ERROR, (errmsg(\"connection hash entry is NULL or invalid\")));\n\t}\n\n\tdlist_mutable_iter iter;\n\tint cachedConnectionCount = 0;\n\n\tdlist_foreach_modify(iter, entry->connections)\n\t{\n\t\tMultiConnection *connection =\n\t\t\tdlist_container(MultiConnection, connectionNode, iter.cur);\n\n\t\t/*\n\t\t * To avoid leaking connections we warn if connections are\n\t\t * still claimed exclusively. We can only do so if the transaction is\n\t\t * committed, as it's normal that code didn't have chance to clean\n\t\t * up after errors.\n\t\t */\n\t\tif (isCommit && connection->claimedExclusively)\n\t\t{\n\t\t\tereport(WARNING,\n\t\t\t\t\t(errmsg(\"connection claimed exclusively at transaction commit\")));\n\t\t}\n\n\n\t\tif (ShouldShutdownConnection(connection, cachedConnectionCount))\n\t\t{\n\t\t\tShutdownConnection(connection);\n\n\t\t\t/* remove from transactionlist before free-ing */\n\t\t\tResetRemoteTransaction(connection);\n\n\t\t\t/* unlink from list */\n\t\t\tdlist_delete(iter.cur);\n\n\t\t\tpfree(connection);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * reset healthy session lifespan connections.\n\t\t\t */\n\t\t\tResetRemoteTransaction(connection);\n\n\t\t\tUnclaimConnection(connection);\n\n\n\t\t\tcachedConnectionCount++;\n\t\t}\n\t}\n}\n\n\n/*\n * ShouldShutdownConnection returns true if either one of the followings is true:\n * - The connection is citus initiated.\n * - Current cached connections is already at MaxCachedConnectionsPerWorker\n * - Connection is forced to close at the end of transaction\n * - Connection is not in OK state\n * - Connection has a replication origin setup\n * - A transaction is still in progress (usually because we are cancelling a distributed transaction)\n * - A connection reached its maximum lifetime\n */\nstatic bool\nShouldShutdownConnection(MultiConnection *connection, const int cachedConnectionCount)\n{\n\t/*\n\t * When we are in a backend that was created to serve an internal connection\n\t * from the coordinator or another worker, we disable connection caching to avoid\n\t * escalating the number of cached connections. We can recognize such backends\n\t * from their application name.\n\t */\n\treturn (IsCitusInternalBackend() || IsRebalancerInternalBackend()) ||\n\t\t   connection->initializationState != POOL_STATE_INITIALIZED ||\n\t\t   cachedConnectionCount >= MaxCachedConnectionsPerWorker ||\n\t\t   connection->forceCloseAtTransactionEnd ||\n\t\t   PQstatus(connection->pgConn) != CONNECTION_OK ||\n\t\t   !RemoteTransactionIdle(connection) ||\n\t\t   connection->requiresReplication ||\n\t\t   connection->isReplicationOriginSessionSetup ||\n\t\t   (MaxCachedConnectionLifetime >= 0 &&\n\t\t\tMillisecondsToTimeout(connection->connectionEstablishmentStart,\n\t\t\t\t\t\t\t\t  MaxCachedConnectionLifetime) <= 0);\n}\n\n\n/*\n * RestartConnection starts a new connection attempt for the given\n * MultiConnection.\n *\n * The internal state of the MultiConnection is preserved. For example, we\n * assume that we already went through all the other initialization steps in\n * StartNodeUserDatabaseConnection, such as incrementing shared connection\n * counters.\n *\n * This function should be used cautiously. If a connection is already\n * involved in a remote transaction, we cannot restart the underlying\n * connection. The caller is responsible for enforcing the restrictions\n * on this.\n */\nvoid\nRestartConnection(MultiConnection *connection)\n{\n\t/* we cannot restart any connection that refers to a placement */\n\tAssert(dlist_is_empty(&connection->referencedPlacements));\n\n\t/* we cannot restart any connection that is part of a transaction */\n\tAssert(connection->remoteTransaction.transactionState == REMOTE_TRANS_NOT_STARTED);\n\n\tConnectionHashKey key;\n\tstrlcpy(key.hostname, connection->hostname, MAX_NODE_LENGTH);\n\tkey.port = connection->port;\n\tstrlcpy(key.user, connection->user, NAMEDATALEN);\n\tstrlcpy(key.database, connection->database, NAMEDATALEN);\n\tkey.replicationConnParam = connection->requiresReplication;\n\n\t/*\n\t * With low-level APIs, we shutdown and restart the connection.\n\t * The main trick here is that we are using the same MultiConnection *\n\t * such that all the state of the connection is preserved.\n\t */\n\tShutdownConnection(connection);\n\tStartConnectionEstablishment(connection, &key);\n\n\t/*\n\t * We are restarting an already initialized connection which has\n\t * gone through StartNodeUserDatabaseConnection(). That's why we\n\t * can safely mark the state initialized.\n\t *\n\t * Not that we have to do this because ShutdownConnection() sets the\n\t * state to not initialized.\n\t */\n\tconnection->initializationState = POOL_STATE_INITIALIZED;\n\tconnection->connectionState = MULTI_CONNECTION_CONNECTING;\n}\n\n\n/*\n * RemoteTransactionIdle function returns true if we manually\n * set flag on run_commands_on_session_level_connection_to_node to true to\n * force connection API keeping connection open or the status of the connection\n * is idle.\n */\nstatic bool\nRemoteTransactionIdle(MultiConnection *connection)\n{\n\t/*\n\t * This is a very special case where we're running isolation tests on MX.\n\t * We don't care whether the transaction is idle or not when we're\n\t * running MX isolation tests. Thus, let the caller act as if the remote\n\t * transactions is idle.\n\t */\n\tif (AllowNonIdleTransactionOnXactHandling())\n\t{\n\t\treturn true;\n\t}\n\n\treturn PQtransactionStatus(connection->pgConn) == PQTRANS_IDLE;\n}\n\n\n/*\n * MarkConnectionConnected is a helper function which sets the  connection\n * connectionState to MULTI_CONNECTION_CONNECTED, and also updates connection\n * establishment time when necessary.\n */\nvoid\nMarkConnectionConnected(MultiConnection *connection, bool newConnection)\n{\n\tconnection->connectionState = MULTI_CONNECTION_CONNECTED;\n\n\tif (INSTR_TIME_IS_ZERO(connection->connectionEstablishmentEnd))\n\t{\n\t\tINSTR_TIME_SET_CURRENT(connection->connectionEstablishmentEnd);\n\t}\n\n\tif (newConnection)\n\t{\n\t\tIncrementStatCounterForMyDb(STAT_CONNECTION_ESTABLISHMENT_SUCCEEDED);\n\t}\n}\n\n\n/*\n * CitusAddWaitEventSetToSet is a wrapper around Postgres' AddWaitEventToSet().\n *\n * AddWaitEventToSet() may throw hard errors. For example, when the\n * underlying socket for a connection is closed by the remote server\n * and already reflected by the OS, however Citus hasn't had a chance\n * to get this information. In that case, if replication factor is >1,\n * Citus can failover to other nodes for executing the query. Even if\n * replication factor = 1, Citus can give much nicer errors.\n *\n * So CitusAddWaitEventSetToSet simply puts ModifyWaitEvent into a\n * PG_TRY/PG_CATCH block in order to catch any hard errors, and\n * returns this information to the caller.\n */\nint\nCitusAddWaitEventSetToSet(WaitEventSet *set, uint32 events, pgsocket fd,\n\t\t\t\t\t\t  Latch *latch, void *user_data)\n{\n\tvolatile int waitEventSetIndex = WAIT_EVENT_SET_INDEX_NOT_INITIALIZED;\n\tMemoryContext savedContext = CurrentMemoryContext;\n\n\tPG_TRY();\n\t{\n\t\twaitEventSetIndex =\n\t\t\tAddWaitEventToSet(set, events, fd, latch, (void *) user_data);\n\t}\n\tPG_CATCH();\n\t{\n\t\t/*\n\t\t * We might be in an arbitrary memory context when the\n\t\t * error is thrown and we should get back to one we had\n\t\t * at PG_TRY() time, especially because we are not\n\t\t * re-throwing the error.\n\t\t */\n\t\tMemoryContextSwitchTo(savedContext);\n\n\t\tFlushErrorState();\n\n\t\t/* let the callers know about the failure */\n\t\twaitEventSetIndex = WAIT_EVENT_SET_INDEX_FAILED;\n\t}\n\tPG_END_TRY();\n\n\treturn waitEventSetIndex;\n}\n\n\n/*\n * CitusModifyWaitEvent is a wrapper around Postgres' ModifyWaitEvent().\n *\n * ModifyWaitEvent may throw hard errors. For example, when the underlying\n * socket for a connection is closed by the remote server and already\n * reflected by the OS, however Citus hasn't had a chance to get this\n * information. In that case, if replication factor is >1, Citus can\n * failover to other nodes for executing the query. Even if replication\n * factor = 1, Citus can give much nicer errors.\n *\n * So CitusModifyWaitEvent simply puts ModifyWaitEvent into a PG_TRY/PG_CATCH\n * block in order to catch any hard errors, and returns this information to the\n * caller.\n */\nbool\nCitusModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, Latch *latch)\n{\n\tvolatile bool success = true;\n\tMemoryContext savedContext = CurrentMemoryContext;\n\n\tPG_TRY();\n\t{\n\t\tModifyWaitEvent(set, pos, events, latch);\n\t}\n\tPG_CATCH();\n\t{\n\t\t/*\n\t\t * We might be in an arbitrary memory context when the\n\t\t * error is thrown and we should get back to one we had\n\t\t * at PG_TRY() time, especially because we are not\n\t\t * re-throwing the error.\n\t\t */\n\t\tMemoryContextSwitchTo(savedContext);\n\n\t\tFlushErrorState();\n\n\t\t/* let the callers know about the failure */\n\t\tsuccess = false;\n\t}\n\tPG_END_TRY();\n\n\treturn success;\n}\n"
  },
  {
    "path": "src/backend/distributed/connection/locally_reserved_shared_connections.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * locally_reserved_shared_connections.c\n *\n *   Keeps track of the number of reserved connections to remote nodes\n *   for this backend. The primary goal is to complement the logic\n *   implemented in shared_connections.c which aims to prevent excessive\n *   number of connections (typically > max_connections) to any worker node.\n *   With this locally reserved connection stats, we enforce the same\n *   constraints considering these locally reserved shared connections.\n *\n *   To be more precise, shared connection stats are incremented only with two\n *   operations: (a) Establishing a connection to a remote node\n *               (b) Reserving connections, the logic that this\n *                   file implements.\n *\n *   Finally, as the name already implies, once a node has reserved a shared\n *   connection, it is guaranteed to have the right to establish a connection\n *   to the given remote node when needed.\n *\n *   For COPY command, we use this fact to reserve connections to the remote nodes\n *   in the same order as the adaptive executor in order to prevent any resource\n *   starvations. We need to do this because COPY establishes connections when it\n *   receives a tuple that targets a remote node. This is a valuable optimization\n *   to prevent unnecessary connection establishments, which are pretty expensive.\n *   Instead, COPY command can reserve connections upfront, and utilize them when\n *   they are actually needed.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/hash.h\"\n#include \"commands/dbcommands.h\"\n#include \"common/hashfn.h\"\n#include \"utils/builtins.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/listutils.h\"\n#include \"distributed/locally_reserved_shared_connections.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/shared_connection_stats.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/worker_manager.h\"\n\n\n#define RESERVED_CONNECTION_COLUMNS 4\n\n\n/* session specific hash map*/\nstatic HTAB *SessionLocalReservedConnections = NULL;\n\n\n/*\n * Hash key for connection reservations\n */\ntypedef struct ReservedConnectionHashKey\n{\n\tchar hostname[MAX_NODE_LENGTH];\n\tint32 port;\n\tOid databaseOid;\n\tOid userId;\n} ReservedConnectionHashKey;\n\n/*\n * Hash entry for per worker information. The rules are as follows:\n *  - If there is no entry in the hash, we can make a reservation.\n *  - If usedReservation is false, we have a reservation that we can use.\n *  - If usedReservation is true, we used the reservation and cannot make more reservations.\n */\ntypedef struct ReservedConnectionHashEntry\n{\n\tReservedConnectionHashKey key;\n\n\tbool usedReservation;\n} ReservedConnectionHashEntry;\n\n\nstatic void StoreAllReservedConnections(Tuplestorestate *tupleStore,\n\t\t\t\t\t\t\t\t\t\tTupleDesc tupleDescriptor);\nstatic ReservedConnectionHashEntry * AllocateOrGetReservedConnectionEntry(char *hostName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  int nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  userId, Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  databaseOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  bool *found);\nstatic void EnsureConnectionPossibilityForNodeList(List *nodeList);\nstatic bool EnsureConnectionPossibilityForNode(WorkerNode *workerNode,\n\t\t\t\t\t\t\t\t\t\t\t   bool waitForConnection);\nstatic uint32 LocalConnectionReserveHashHash(const void *key, Size keysize);\nstatic int LocalConnectionReserveHashCompare(const void *a, const void *b, Size keysize);\n\n\nPG_FUNCTION_INFO_V1(citus_reserved_connection_stats);\n\n/*\n * citus_reserved_connection_stats returns all the avaliable information about all\n * the reserved connections. This function is used mostly for testing.\n */\nDatum\ncitus_reserved_connection_stats(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tStoreAllReservedConnections(tupleStore, tupleDescriptor);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * StoreAllReservedConnections gets connections established from the current node\n * and inserts them into the given tuplestore.\n *\n * We don't need to enforce any access privileges as the number of backends\n * on any node is already visible on pg_stat_activity to all users.\n */\nstatic void\nStoreAllReservedConnections(Tuplestorestate *tupleStore, TupleDesc tupleDescriptor)\n{\n\tDatum values[RESERVED_CONNECTION_COLUMNS];\n\tbool isNulls[RESERVED_CONNECTION_COLUMNS];\n\n\tHASH_SEQ_STATUS status;\n\tReservedConnectionHashEntry *connectionEntry = NULL;\n\n\thash_seq_init(&status, SessionLocalReservedConnections);\n\twhile ((connectionEntry =\n\t\t\t\t(ReservedConnectionHashEntry *) hash_seq_search(&status)) != 0)\n\t{\n\t\t/* get ready for the next tuple */\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(isNulls, false, sizeof(isNulls));\n\n\t\tchar *databaseName = get_database_name(connectionEntry->key.databaseOid);\n\t\tif (databaseName == NULL)\n\t\t{\n\t\t\t/* database might have been dropped */\n\t\t\tcontinue;\n\t\t}\n\n\t\tvalues[0] = PointerGetDatum(cstring_to_text(connectionEntry->key.hostname));\n\t\tvalues[1] = Int32GetDatum(connectionEntry->key.port);\n\t\tvalues[2] = PointerGetDatum(cstring_to_text(databaseName));\n\t\tvalues[3] = BoolGetDatum(connectionEntry->usedReservation);\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);\n\t}\n}\n\n\n/*\n * InitializeLocallyReservedSharedConnections initializes the hashmap in\n * ConnectionContext.\n */\nvoid\nInitializeLocallyReservedSharedConnections(void)\n{\n\tHASHCTL reservedConnectionInfo;\n\n\tmemset(&reservedConnectionInfo, 0, sizeof(reservedConnectionInfo));\n\treservedConnectionInfo.keysize = sizeof(ReservedConnectionHashKey);\n\treservedConnectionInfo.entrysize = sizeof(ReservedConnectionHashEntry);\n\n\t/*\n\t * ConnectionContext is the session local memory context that is used for\n\t * tracking remote connections.\n\t */\n\treservedConnectionInfo.hcxt = ConnectionContext;\n\n\treservedConnectionInfo.hash = LocalConnectionReserveHashHash;\n\treservedConnectionInfo.match = LocalConnectionReserveHashCompare;\n\n\tuint32 hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT | HASH_COMPARE);\n\n\tSessionLocalReservedConnections =\n\t\thash_create(\"citus session level reserved connections (host,port,database,user)\",\n\t\t\t\t\t64, &reservedConnectionInfo, hashFlags);\n}\n\n\n/*\n *  CanUseReservedConnection returns true if we have already reserved at least\n *  one shared connection in this session that is not used.\n */\nbool\nCanUseReservedConnection(const char *hostName, int nodePort, Oid userId,\n\t\t\t\t\t\t Oid databaseOid)\n{\n\tReservedConnectionHashKey key;\n\n\tstrlcpy(key.hostname, hostName, MAX_NODE_LENGTH);\n\tkey.userId = userId;\n\tkey.port = nodePort;\n\tkey.databaseOid = databaseOid;\n\n\tbool found = false;\n\tReservedConnectionHashEntry *entry =\n\t\t(ReservedConnectionHashEntry *) hash_search(SessionLocalReservedConnections, &key,\n\t\t\t\t\t\t\t\t\t\t\t\t\tHASH_FIND, &found);\n\n\tif (!found || !entry)\n\t{\n\t\treturn false;\n\t}\n\n\treturn !entry->usedReservation;\n}\n\n\n/*\n * DeallocateReservedConnections is responsible for two things. First, if the operation\n * has reserved a connection but not used, it gives back the connection back to the\n * shared memory pool. Second, for all cases, it deallocates the session local entry from\n * the hash.\n */\nvoid\nDeallocateReservedConnections(void)\n{\n\tHASH_SEQ_STATUS status;\n\tReservedConnectionHashEntry *entry;\n\n\thash_seq_init(&status, SessionLocalReservedConnections);\n\twhile ((entry = (ReservedConnectionHashEntry *) hash_seq_search(&status)) != 0)\n\t{\n\t\tif (!entry->usedReservation)\n\t\t{\n\t\t\t/*\n\t\t\t * We have not used this reservation, make sure to clean-up from\n\t\t\t * the shared memory as well.\n\t\t\t */\n\t\t\tDecrementSharedConnectionCounter(entry->key.hostname, entry->key.port);\n\n\t\t\t/* for completeness, set it to true */\n\t\t\tentry->usedReservation = true;\n\t\t}\n\n\t\t/*\n\t\t * We cleaned up all the entries because we may not need reserved connections\n\t\t * in the next iteration.\n\t\t */\n\t\tbool found = false;\n\t\thash_search(SessionLocalReservedConnections, entry, HASH_REMOVE, &found);\n\t\tAssert(found);\n\t}\n}\n\n\n/*\n * MarkReservedConnectionUsed sets the local hash that the reservation is used.\n */\nvoid\nMarkReservedConnectionUsed(const char *hostName, int nodePort, Oid userId,\n\t\t\t\t\t\t   Oid databaseOid)\n{\n\tReservedConnectionHashKey key;\n\n\tstrlcpy(key.hostname, hostName, MAX_NODE_LENGTH);\n\tkey.userId = userId;\n\tkey.port = nodePort;\n\tkey.databaseOid = databaseOid;\n\n\tbool found = false;\n\tReservedConnectionHashEntry *entry =\n\t\t(ReservedConnectionHashEntry *) hash_search(\n\t\t\tSessionLocalReservedConnections, &key, HASH_FIND, &found);\n\n\tif (!found)\n\t{\n\t\tereport(ERROR, (errmsg(\"BUG: untracked reserved connection\"),\n\t\t\t\t\t\terrhint(\"Set citus.max_shared_pool_size TO -1 to \"\n\t\t\t\t\t\t\t\t\"disable reserved connection counters\")));\n\t}\n\n\t/* a reservation can only be used once */\n\tAssert(!entry->usedReservation);\n\n\tentry->usedReservation = true;\n}\n\n\n/*\n * EnsureConnectionPossibilityForRemotePrimaryNodes is a wrapper around\n * EnsureConnectionPossibilityForNodeList.\n */\nvoid\nEnsureConnectionPossibilityForRemotePrimaryNodes(void)\n{\n\t/*\n\t * By using NoLock there is a tiny risk of that we miss to reserve a\n\t * connection for a concurrently added node. However, that doesn't\n\t * seem to cause any problems as none of the placements that we are\n\t * going to access would be on the new node.\n\t */\n\tList *remoteNodeList = ActivePrimaryRemoteNodeList(NoLock);\n\tEnsureConnectionPossibilityForNodeList(remoteNodeList);\n}\n\n\n/*\n * TryConnectionPossibilityForLocalPrimaryNode returns true if the primary\n * local node is in the metadata an we can reserve a connection for the node.\n * If not, the function returns false.\n */\nbool\nTryConnectionPossibilityForLocalPrimaryNode(void)\n{\n\tbool nodeIsInMetadata = false;\n\tWorkerNode *localNode =\n\t\tPrimaryNodeForGroup(GetLocalGroupId(), &nodeIsInMetadata);\n\n\tif (localNode == NULL)\n\t{\n\t\t/*\n\t\t * If the local node is not a primary node, we should not try to\n\t\t * reserve a connection as there cannot be any shards.\n\t\t */\n\t\treturn false;\n\t}\n\n\tbool waitForConnection = false;\n\treturn EnsureConnectionPossibilityForNode(localNode, waitForConnection);\n}\n\n\n/*\n * EnsureConnectionPossibilityForNodeList reserves a shared connection\n * counter per node in the nodeList unless:\n *  - Reservation is possible/allowed (see IsReservationPossible())\n *  - there is at least one connection to the node so that we are guaranteed\n *    to get a connection\n *  - An earlier call already reserved a connection (e.g., we allow only a\n *    single reservation per backend)\n */\nstatic void\nEnsureConnectionPossibilityForNodeList(List *nodeList)\n{\n\t/*\n\t * We sort the workerList because adaptive connection management\n\t * (e.g., OPTIONAL_CONNECTION) requires any concurrent executions\n\t * to wait for the connections in the same order to prevent any\n\t * starvation. If we don't sort, we might end up with:\n\t *      Execution 1: Get connection for worker 1, wait for worker 2\n\t *      Execution 2: Get connection for worker 2, wait for worker 1\n\t *\n\t *  and, none could proceed. Instead, we enforce every execution establish\n\t *  the required connections to workers in the same order.\n\t */\n\tnodeList = SortList(nodeList, CompareWorkerNodes);\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, nodeList)\n\t{\n\t\tbool waitForConnection = true;\n\t\tEnsureConnectionPossibilityForNode(workerNode, waitForConnection);\n\t}\n}\n\n\n/*\n * EnsureConnectionPossibilityForNode reserves a shared connection\n * counter per node in the nodeList unless:\n *  - Reservation is not possible/allowed (see IsReservationPossible())\n *  - there is at least one connection to the node so that we are guranteed\n *    to get a connection\n *  - An earlier call already reserved a connection (e.g., we allow only a\n *    single reservation per backend)\n * - waitForConnection is false. When this is false, the function still tries\n *   to ensure connection possibility. If it fails (e.g., we\n *   reached max_shared_pool_size), it doesn't wait to get the connection. Instead,\n *   return false.\n */\nstatic bool\nEnsureConnectionPossibilityForNode(WorkerNode *workerNode, bool waitForConnection)\n{\n\tif (!IsReservationPossible())\n\t{\n\t\treturn false;\n\t}\n\n\tchar *databaseName = get_database_name(MyDatabaseId);\n\tOid userId = GetUserId();\n\tchar *userName = GetUserNameFromId(userId, false);\n\n\tif (ConnectionAvailableToNode(workerNode->workerName, workerNode->workerPort,\n\t\t\t\t\t\t\t\t  userName, databaseName) != NULL)\n\t{\n\t\t/*\n\t\t * The same user has already an active connection for the node. It\n\t\t * means that the execution can use the same connection, so reservation\n\t\t * is not necessary.\n\t\t */\n\t\treturn true;\n\t}\n\n\t/*\n\t * We are trying to be defensive here by ensuring that the required hash\n\t * table entry can be allocated. The main goal is that we don't want to be\n\t * in a situation where shared connection counter is incremented but not\n\t * the local reserved counter due to out-of-memory.\n\t *\n\t * Note that shared connection stats operate on the shared memory, and we\n\t * pre-allocate all the necessary memory. In other words, it would never\n\t * throw out of memory error.\n\t */\n\tbool found = false;\n\tReservedConnectionHashEntry *hashEntry =\n\t\tAllocateOrGetReservedConnectionEntry(workerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t workerNode->workerPort,\n\t\t\t\t\t\t\t\t\t\t\t userId, MyDatabaseId, &found);\n\n\tif (found)\n\t{\n\t\t/*\n\t\t * We have already reserved a connection for this user and database\n\t\t * on the worker. We only allow a single reservation per\n\t\t * transaction block. The reason is that the earlier command (either in\n\t\t * a transaction block or a function call triggered by a single command)\n\t\t * was able to reserve or establish a connection. That connection is\n\t\t * guranteed to be available for us.\n\t\t */\n\t\treturn true;\n\t}\n\n\tif (waitForConnection)\n\t{\n\t\t/*\n\t\t * Increment the shared counter, we may need to wait if there are\n\t\t * no space left.\n\t\t */\n\t\tWaitLoopForSharedConnection(workerNode->workerName, workerNode->workerPort);\n\t}\n\telse\n\t{\n\t\tbool incremented =\n\t\t\tTryToIncrementSharedConnectionCounter(workerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t  workerNode->workerPort);\n\t\tif (!incremented)\n\t\t{\n\t\t\t/*\n\t\t\t * We could not reserve a connection. First, remove the entry from the\n\t\t\t * hash. The reason is that we allow single reservation per transaction\n\t\t\t * block and leaving the entry in the hash would be qualified as there is a\n\t\t\t * reserved connection to the node.\n\t\t\t */\n\t\t\tbool foundForRemove = false;\n\t\t\thash_search(SessionLocalReservedConnections, hashEntry, HASH_REMOVE,\n\t\t\t\t\t\t&foundForRemove);\n\t\t\tAssert(foundForRemove);\n\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/* locally mark that we have one connection reserved */\n\thashEntry->usedReservation = false;\n\n\treturn true;\n}\n\n\n/*\n * IsReservationPossible returns true if the state of the current\n * session is eligible for shared connection reservation.\n */\nbool\nIsReservationPossible(void)\n{\n\tif (GetMaxSharedPoolSize() == DISABLE_CONNECTION_THROTTLING)\n\t{\n\t\t/* connection throttling disabled */\n\t\treturn false;\n\t}\n\n\tif (UseConnectionPerPlacement())\n\t{\n\t\t/*\n\t\t * For this case, we are not enforcing adaptive\n\t\t * connection management anyway.\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (SessionLocalReservedConnections == NULL)\n\t{\n\t\t/*\n\t\t * This is unexpected as SessionLocalReservedConnections hash table is\n\t\t * created at startup. Still, let's be defensive.\n\t\t */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * AllocateOrGetReservedConnectionEntry allocates the required entry in the hash\n * map by HASH_ENTER. The function throws an error if it cannot allocate\n * the entry.\n */\nstatic ReservedConnectionHashEntry *\nAllocateOrGetReservedConnectionEntry(char *hostName, int nodePort, Oid userId,\n\t\t\t\t\t\t\t\t\t Oid databaseOid, bool *found)\n{\n\tReservedConnectionHashKey key;\n\n\t*found = false;\n\n\tstrlcpy(key.hostname, hostName, MAX_NODE_LENGTH);\n\tkey.userId = userId;\n\tkey.port = nodePort;\n\tkey.databaseOid = databaseOid;\n\n\t/*\n\t * Entering a new entry with HASH_ENTER flag is enough as it would\n\t * throw out-of-memory error as it internally does palloc.\n\t */\n\tReservedConnectionHashEntry *entry =\n\t\t(ReservedConnectionHashEntry *) hash_search(SessionLocalReservedConnections,\n\t\t\t\t\t\t\t\t\t\t\t\t\t&key, HASH_ENTER, found);\n\n\tif (!*found)\n\t{\n\t\t/*\n\t\t * Until we reserve connection in the shared memory, we treat\n\t\t * as if have used the reservation.\n\t\t */\n\t\tentry->usedReservation = true;\n\t}\n\n\treturn entry;\n}\n\n\n/*\n * LocalConnectionReserveHashHash is a utilty function to calculate hash of\n * ReservedConnectionHashKey.\n */\nstatic uint32\nLocalConnectionReserveHashHash(const void *key, Size keysize)\n{\n\tReservedConnectionHashKey *entry = (ReservedConnectionHashKey *) key;\n\n\tuint32 hash = string_hash(entry->hostname, MAX_NODE_LENGTH);\n\thash = hash_combine(hash, hash_uint32(entry->userId));\n\thash = hash_combine(hash, hash_uint32(entry->port));\n\thash = hash_combine(hash, hash_uint32(entry->databaseOid));\n\n\treturn hash;\n}\n\n\n/*\n * LocalConnectionReserveHashCompare is a utilty function to compare\n * ReservedConnectionHashKeys.\n */\nstatic int\nLocalConnectionReserveHashCompare(const void *a, const void *b, Size keysize)\n{\n\tReservedConnectionHashKey *ca = (ReservedConnectionHashKey *) a;\n\tReservedConnectionHashKey *cb = (ReservedConnectionHashKey *) b;\n\n\tif (ca->port != cb->port ||\n\t\tca->databaseOid != cb->databaseOid ||\n\t\tca->userId != cb->userId ||\n\t\tstrncmp(ca->hostname, cb->hostname, MAX_NODE_LENGTH) != 0)\n\t{\n\t\treturn 1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/connection/placement_connection.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * placement_connection.c\n *   Per placement connection handling.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include \"postgres.h\"\n\n#include \"access/hash.h\"\n#include \"common/hashfn.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/memutils.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/relation_access_tracking.h\"\n\n\n/*\n * A connection reference is used to register that a connection has been used\n * to read or modify either a) a shard placement as a particular user b) a\n * group of colocated placements (which depend on whether the reference is\n * from ConnectionPlacementHashEntry or ColocatedPlacementHashEntry).\n */\ntypedef struct ConnectionReference\n{\n\t/*\n\t * The user used to read/modify the placement. We cannot reuse connections\n\t * that were performed using a different role, since it would not have the\n\t * right permissions.\n\t */\n\tconst char *userName;\n\n\t/* the connection */\n\tMultiConnection *connection;\n\n\t/*\n\t * Information about what the connection is used for. There can only be\n\t * one connection executing DDL/DML for a placement to avoid deadlock\n\t * issues/read-your-own-writes violations.  The difference between DDL/DML\n\t * currently is only used to emit more precise error messages.\n\t */\n\tbool hadDML;\n\tbool hadDDL;\n\n\t/* colocation group of the placement, if any */\n\tuint32 colocationGroupId;\n\tuint32 representativeValue;\n\n\t/* placementId of the placement, used only for append distributed tables */\n\tuint64 placementId;\n\n\t/* membership in MultiConnection->referencedPlacements */\n\tdlist_node connectionNode;\n} ConnectionReference;\n\n\nstruct ColocatedPlacementsHashEntry;\n\n\n/*\n * Hash table mapping placements to a list of connections.\n *\n * This stores a list of connections for each placement, because multiple\n * connections to the same placement may exist at the same time. E.g. an\n * adaptive executor query may reference the same placement in several\n * sub-tasks.\n *\n * We keep track about a connection having executed DML or DDL, since we can\n * only ever allow a single transaction to do either to prevent deadlocks and\n * consistency violations (e.g. read-your-own-writes).\n */\n\n/* hash key */\ntypedef struct ConnectionPlacementHashKey\n{\n\tuint64 placementId;\n} ConnectionPlacementHashKey;\n\n/* hash entry */\ntypedef struct ConnectionPlacementHashEntry\n{\n\tConnectionPlacementHashKey key;\n\n\t/* did any remote transactions fail? */\n\tbool failed;\n\n\t/* primary connection used to access the placement */\n\tConnectionReference *primaryConnection;\n\n\t/* are any other connections reading from the placements? */\n\tbool hasSecondaryConnections;\n\n\t/* entry for the set of co-located placements */\n\tstruct ColocatedPlacementsHashEntry *colocatedEntry;\n\n\t/* membership in ConnectionShardHashEntry->placementConnections */\n\tdlist_node shardNode;\n} ConnectionPlacementHashEntry;\n\n/* hash table */\nstatic HTAB *ConnectionPlacementHash;\n\n\n/*\n * A hash-table mapping colocated placements to connections. Colocated\n * placements being the set of placements on a single node that represent the\n * same value range. This is needed because connections for colocated\n * placements (i.e. the corresponding placements for different colocated\n * distributed tables) need to share connections.  Otherwise things like\n * foreign keys can very easily lead to unprincipled deadlocks.  This means\n * that there can only be one DML/DDL connection for a set of colocated\n * placements.\n *\n * A set of colocated placements is identified, besides node identifying\n * information, by the associated colocation group id and the placement's\n * 'representativeValue' which currently is the lower boundary of it's\n * hash-range.\n *\n * Note that this hash-table only contains entries for hash-partitioned\n * tables, because others so far don't support colocation.\n */\n\n/* hash key */\ntypedef struct ColocatedPlacementsHashKey\n{\n\t/* to identify host - database can't differ */\n\tuint32 nodeId;\n\n\t/* colocation group, or invalid */\n\tuint32 colocationGroupId;\n\n\t/* to represent the value range */\n\tuint32 representativeValue;\n} ColocatedPlacementsHashKey;\n\n/* hash entry */\ntypedef struct ColocatedPlacementsHashEntry\n{\n\tColocatedPlacementsHashKey key;\n\n\t/* primary connection used to access the co-located placements */\n\tConnectionReference *primaryConnection;\n\n\t/* are any other connections reading from the placements? */\n\tbool hasSecondaryConnections;\n}  ColocatedPlacementsHashEntry;\n\nstatic HTAB *ColocatedPlacementsHash;\n\n\n/*\n * Hash table mapping shard ids to placements.\n *\n * This is used to track whether placements of a shard have to be marked\n * invalid after a failure, or whether a coordinated transaction has to be\n * aborted, to avoid all placements of a shard to be marked invalid.\n */\n\n/* hash key */\ntypedef struct ConnectionShardHashKey\n{\n\tuint64 shardId;\n} ConnectionShardHashKey;\n\n/* hash entry */\ntypedef struct ConnectionShardHashEntry\n{\n\tConnectionShardHashKey key;\n\tdlist_head placementConnections;\n} ConnectionShardHashEntry;\n\n/* hash table */\nstatic HTAB *ConnectionShardHash;\n\n\nstatic MultiConnection * FindPlacementListConnection(int flags, List *placementAccessList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t const char *userName);\nstatic ConnectionPlacementHashEntry * FindOrCreatePlacementEntry(ShardPlacement *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t placement);\nstatic bool CanUseExistingConnection(uint32 flags, const char *userName,\n\t\t\t\t\t\t\t\t\t ConnectionReference *placementConnection);\nstatic bool ConnectionAccessedDifferentPlacement(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t\t ShardPlacement *placement);\nstatic void AssociatePlacementWithShard(ConnectionPlacementHashEntry *placementEntry,\n\t\t\t\t\t\t\t\t\t\tShardPlacement *placement);\nstatic bool HasModificationFailedForShard(ConnectionShardHashEntry *shardEntry);\nstatic uint32 ColocatedPlacementsHashHash(const void *key, Size keysize);\nstatic int ColocatedPlacementsHashCompare(const void *a, const void *b, Size keysize);\n\n\n/*\n * GetPlacementConnection establishes a connection for a placement.\n *\n * See StartPlacementConnection for details.\n */\nMultiConnection *\nGetPlacementConnection(uint32 flags, ShardPlacement *placement, const char *userName)\n{\n\tMultiConnection *connection = StartPlacementConnection(flags, placement, userName);\n\n\tif (connection == NULL)\n\t{\n\t\t/* connection can only be NULL for optional connections */\n\t\tAssert((flags & OPTIONAL_CONNECTION));\n\n\t\treturn NULL;\n\t}\n\n\tFinishConnectionEstablishment(connection);\n\treturn connection;\n}\n\n\n/*\n * StartPlacementConnection initiates a connection to a remote node,\n * associated with the placement and transaction.\n *\n * The connection is established for the current database. If userName is NULL\n * the current user is used, otherwise the provided one.\n *\n * See StartNodeUserDatabaseConnection for details.\n *\n * Flags have the corresponding meaning from StartNodeUserDatabaseConnection,\n * except that two additional flags have an effect:\n * - FOR_DML - signal that connection is going to be used for DML (modifications)\n * - FOR_DDL - signal that connection is going to be used for DDL\n *\n * Only one connection associated with the placement may have FOR_DML or\n * FOR_DDL set. For hash-partitioned tables only one connection for a set of\n * colocated placements may have FOR_DML/DDL set.  This restriction prevents\n * deadlocks and wrong results due to in-progress transactions.\n */\nMultiConnection *\nStartPlacementConnection(uint32 flags, ShardPlacement *placement, const char *userName)\n{\n\tShardPlacementAccess *placementAccess =\n\t\t(ShardPlacementAccess *) palloc0(sizeof(ShardPlacementAccess));\n\n\tplacementAccess->placement = placement;\n\n\tif (flags & FOR_DDL)\n\t{\n\t\tplacementAccess->accessType = PLACEMENT_ACCESS_DDL;\n\t}\n\telse if (flags & FOR_DML)\n\t{\n\t\tplacementAccess->accessType = PLACEMENT_ACCESS_DML;\n\t}\n\telse\n\t{\n\t\tplacementAccess->accessType = PLACEMENT_ACCESS_SELECT;\n\t}\n\n\treturn StartPlacementListConnection(flags, list_make1(placementAccess), userName);\n}\n\n\n/*\n * StartPlacementListConnection returns a connection to a remote node suitable for\n * a placement accesses (SELECT, DML, DDL) or throws an error if no suitable\n * connection can be established if would cause a self-deadlock or consistency\n * violation.\n */\nMultiConnection *\nStartPlacementListConnection(uint32 flags, List *placementAccessList,\n\t\t\t\t\t\t\t const char *userName)\n{\n\tchar *freeUserName = NULL;\n\n\tif (userName == NULL)\n\t{\n\t\tuserName = freeUserName = CurrentUserName();\n\t}\n\n\tMultiConnection *chosenConnection = FindPlacementListConnection(flags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplacementAccessList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tuserName);\n\tif (chosenConnection == NULL)\n\t{\n\t\t/* use the first placement from the list to extract nodename and nodeport */\n\t\tShardPlacementAccess *placementAccess =\n\t\t\t(ShardPlacementAccess *) linitial(placementAccessList);\n\t\tShardPlacement *placement = placementAccess->placement;\n\t\tchar *nodeName = placement->nodeName;\n\t\tint nodePort = placement->nodePort;\n\n\t\t/*\n\t\t * No suitable connection in the placement->connection mapping, get one from\n\t\t * the node->connection pool.\n\t\t */\n\t\tchosenConnection = StartNodeUserDatabaseConnection(flags, nodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   userName, NULL);\n\t\tif (chosenConnection == NULL)\n\t\t{\n\t\t\t/* connection can only be NULL for optional connections */\n\t\t\tAssert((flags & OPTIONAL_CONNECTION));\n\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif ((flags & REQUIRE_CLEAN_CONNECTION) &&\n\t\t\tConnectionAccessedDifferentPlacement(chosenConnection, placement))\n\t\t{\n\t\t\t/*\n\t\t\t * Cached connection accessed a non-co-located placement in the same\n\t\t\t * table or co-location group, while the caller asked for a clean\n\t\t\t * connection. Open a new connection instead.\n\t\t\t *\n\t\t\t * We use this for situations in which we want to use a different\n\t\t\t * connection for every placement, such as COPY. If we blindly returned\n\t\t\t * a cached connection that already modified a different, non-co-located\n\t\t\t * placement B in the same table or in a table with the same co-location\n\t\t\t * ID as the current placement, then we'd no longer able to write to\n\t\t\t * placement B later in the COPY.\n\t\t\t */\n\t\t\tchosenConnection = StartNodeUserDatabaseConnection(flags |\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   FORCE_NEW_CONNECTION,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   nodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   userName, NULL);\n\n\t\t\tif (chosenConnection == NULL)\n\t\t\t{\n\t\t\t\t/* connection can only be NULL for optional connections */\n\t\t\t\tAssert((flags & OPTIONAL_CONNECTION));\n\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\tAssert(!ConnectionAccessedDifferentPlacement(chosenConnection, placement));\n\t\t}\n\t}\n\n\t/* remember which connection we're going to use to access the placements */\n\tAssignPlacementListToConnection(placementAccessList, chosenConnection);\n\n\tif (freeUserName)\n\t{\n\t\tpfree(freeUserName);\n\t}\n\n\treturn chosenConnection;\n}\n\n\n/*\n * AssignPlacementListToConnection assigns a set of shard placement accesses to a\n * given connection, meaning that connection must be used for all (conflicting)\n * accesses of the same shard placements to make sure reads see writes and to\n * make sure we don't take conflicting locks.\n */\nvoid\nAssignPlacementListToConnection(List *placementAccessList, MultiConnection *connection)\n{\n\tconst char *userName = connection->user;\n\n\tShardPlacementAccess *placementAccess = NULL;\n\tforeach_declared_ptr(placementAccess, placementAccessList)\n\t{\n\t\tShardPlacement *placement = placementAccess->placement;\n\t\tShardPlacementAccessType accessType = placementAccess->accessType;\n\n\n\t\tif (placement->shardId == INVALID_SHARD_ID)\n\t\t{\n\t\t\t/*\n\t\t\t * When a SELECT prunes down to 0 shard, we use a dummy placement\n\t\t\t * which is only used to route the query to a worker node, but\n\t\t\t * the SELECT doesn't actually access any shard placement.\n\t\t\t *\n\t\t\t * FIXME: this can be removed if we evaluate empty SELECTs locally.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tConnectionPlacementHashEntry *placementEntry = FindOrCreatePlacementEntry(\n\t\t\tplacement);\n\t\tConnectionReference *placementConnection = placementEntry->primaryConnection;\n\n\t\tif (placementConnection->connection == connection)\n\t\t{\n\t\t\t/* using the connection that was already assigned to the placement */\n\t\t}\n\t\telse if (placementConnection->connection == NULL)\n\t\t{\n\t\t\t/* placement does not have a connection assigned yet */\n\t\t\tplacementConnection->connection = connection;\n\t\t\tplacementConnection->hadDDL = false;\n\t\t\tplacementConnection->hadDML = false;\n\t\t\tplacementConnection->userName = MemoryContextStrdup(TopTransactionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tuserName);\n\t\t\tplacementConnection->placementId = placementAccess->placement->placementId;\n\n\t\t\t/* record association with connection */\n\t\t\tdlist_push_tail(&connection->referencedPlacements,\n\t\t\t\t\t\t\t&placementConnection->connectionNode);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* using a different connection than the one assigned to the placement */\n\n\t\t\tif (accessType != PLACEMENT_ACCESS_SELECT)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We previously read from the placement, but now we're writing to\n\t\t\t\t * it (if we had written to the placement, we would have either chosen\n\t\t\t\t * the same connection, or errored out). Update the connection reference\n\t\t\t\t * to point to the connection used for writing. We don't need to remember\n\t\t\t\t * the existing connection since we won't be able to reuse it for\n\t\t\t\t * accessing the placement. However, we do register that it exists in\n\t\t\t\t * hasSecondaryConnections.\n\t\t\t\t */\n\t\t\t\tplacementConnection->connection = connection;\n\t\t\t\tplacementConnection->userName = MemoryContextStrdup(TopTransactionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tuserName);\n\n\t\t\t\tAssert(!placementConnection->hadDDL);\n\t\t\t\tAssert(!placementConnection->hadDML);\n\n\t\t\t\t/* record association with connection */\n\t\t\t\tdlist_push_tail(&connection->referencedPlacements,\n\t\t\t\t\t\t\t\t&placementConnection->connectionNode);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * There are now multiple connections that read from the placement\n\t\t\t * and DDL commands are forbidden.\n\t\t\t */\n\t\t\tplacementEntry->hasSecondaryConnections = true;\n\n\t\t\tif (placementEntry->colocatedEntry != NULL)\n\t\t\t{\n\t\t\t\t/* we also remember this for co-located placements */\n\t\t\t\tplacementEntry->colocatedEntry->hasSecondaryConnections = true;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Remember that we used the current connection for writes.\n\t\t */\n\t\tif (accessType == PLACEMENT_ACCESS_DDL)\n\t\t{\n\t\t\tplacementConnection->hadDDL = true;\n\t\t}\n\n\t\tif (accessType == PLACEMENT_ACCESS_DML)\n\t\t{\n\t\t\tplacementConnection->hadDML = true;\n\t\t}\n\n\t\t/* record the relation access */\n\t\tOid relationId = RelationIdForShard(placement->shardId);\n\t\tRecordRelationAccessIfNonDistTable(relationId, accessType);\n\t}\n}\n\n\n/*\n * GetConnectionIfPlacementAccessedInXact returns the connection over which\n * the placement has been access in the transaction. If not found, returns\n * NULL.\n */\nMultiConnection *\nGetConnectionIfPlacementAccessedInXact(int flags, List *placementAccessList,\n\t\t\t\t\t\t\t\t\t   const char *userName)\n{\n\tchar *freeUserName = NULL;\n\n\tif (userName == NULL)\n\t{\n\t\tuserName = freeUserName = CurrentUserName();\n\t}\n\n\tMultiConnection *connection = FindPlacementListConnection(flags, placementAccessList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  userName);\n\n\tif (freeUserName != NULL)\n\t{\n\t\tpfree(freeUserName);\n\t}\n\n\treturn connection;\n}\n\n\n/*\n * FindPlacementListConnection determines whether there is a connection that must\n * be used to perform the given placement accesses.\n *\n * If a placement was only read in this transaction, then the same connection must\n * be used for DDL to prevent self-deadlock. If a placement was modified in this\n * transaction, then the same connection must be used for all subsequent accesses\n * to ensure read-your-writes consistency and prevent self-deadlock. If those\n * conditions cannot be met, because a connection is in use or the placements in\n * the placement access list were modified over multiple connections, then this\n * function throws an error.\n *\n * The function returns the connection that needs to be used, if such a connection\n * exists.\n */\nstatic MultiConnection *\nFindPlacementListConnection(int flags, List *placementAccessList, const char *userName)\n{\n\tbool foundModifyingConnection = false;\n\tMultiConnection *chosenConnection = NULL;\n\n\t/*\n\t * Go through all placement accesses to find a suitable connection.\n\t *\n\t * If none of the placements have been accessed in this transaction, connection\n\t * remains NULL.\n\t *\n\t * If one or more of the placements have been modified in this transaction, then\n\t * use the connection that performed the write. If placements have been written\n\t * over multiple connections or the connection is not available, error out.\n\t *\n\t * If placements have only been read in this transaction, then use the last\n\t * suitable connection found for a placement in the placementAccessList.\n\t */\n\tShardPlacementAccess *placementAccess = NULL;\n\tforeach_declared_ptr(placementAccess, placementAccessList)\n\t{\n\t\tShardPlacement *placement = placementAccess->placement;\n\t\tShardPlacementAccessType accessType = placementAccess->accessType;\n\n\n\t\tif (placement->shardId == INVALID_SHARD_ID)\n\t\t{\n\t\t\t/*\n\t\t\t * When a SELECT prunes down to 0 shard, we use a dummy placement.\n\t\t\t * In that case, we can fall back to the default connection.\n\t\t\t *\n\t\t\t * FIXME: this can be removed if we evaluate empty SELECTs locally.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tConnectionPlacementHashEntry *placementEntry = FindOrCreatePlacementEntry(\n\t\t\tplacement);\n\t\tColocatedPlacementsHashEntry *colocatedEntry = placementEntry->colocatedEntry;\n\t\tConnectionReference *placementConnection = placementEntry->primaryConnection;\n\n\t\t/* note: the Asserts below are primarily for clarifying the conditions */\n\n\t\tif (placementConnection->connection == NULL)\n\t\t{\n\t\t\t/* no connection has been chosen for the placement */\n\t\t}\n\t\telse if (accessType == PLACEMENT_ACCESS_DDL &&\n\t\t\t\t placementEntry->hasSecondaryConnections)\n\t\t{\n\t\t\t/*\n\t\t\t * If a placement has been read over multiple connections (typically as\n\t\t\t * a result of a reference table join) then a DDL command on the placement\n\t\t\t * would create a self-deadlock.\n\t\t\t */\n\n\t\t\tAssert(placementConnection != NULL);\n\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),\n\t\t\t\t\t errmsg(\"cannot perform DDL on placement \" UINT64_FORMAT\n\t\t\t\t\t\t\t\", which has been read over multiple connections\",\n\t\t\t\t\t\t\tplacement->placementId)));\n\t\t}\n\t\telse if (accessType == PLACEMENT_ACCESS_DDL && colocatedEntry != NULL &&\n\t\t\t\t colocatedEntry->hasSecondaryConnections)\n\t\t{\n\t\t\t/*\n\t\t\t * If a placement has been read over multiple (uncommitted) connections\n\t\t\t * then a DDL command on a co-located placement may create a self-deadlock\n\t\t\t * if there exist some relationship between the co-located placements\n\t\t\t * (e.g. foreign key, partitioning).\n\t\t\t */\n\n\t\t\tAssert(placementConnection != NULL);\n\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),\n\t\t\t\t\t errmsg(\"cannot perform DDL on placement \" UINT64_FORMAT\n\t\t\t\t\t\t\t\" since a co-located placement has been read over multiple connections\",\n\t\t\t\t\t\t\tplacement->placementId)));\n\t\t}\n\t\telse if (foundModifyingConnection)\n\t\t{\n\t\t\t/*\n\t\t\t * We already found a connection that performed writes on of the placements\n\t\t\t * and must use it.\n\t\t\t */\n\n\t\t\tif ((placementConnection->hadDDL || placementConnection->hadDML) &&\n\t\t\t\tplacementConnection->connection != chosenConnection)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * The current placement may have been modified over a different\n\t\t\t\t * connection. Neither connection is guaranteed to see all uncomitted\n\t\t\t\t * writes and therefore we cannot proceed.\n\t\t\t\t */\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),\n\t\t\t\t\t\t errmsg(\"cannot perform query with placements that were \"\n\t\t\t\t\t\t\t\t\"modified over multiple connections\")));\n\t\t\t}\n\t\t}\n\t\telse if (accessType == PLACEMENT_ACCESS_SELECT &&\n\t\t\t\t placementEntry->hasSecondaryConnections &&\n\t\t\t\t !placementConnection->hadDDL && !placementConnection->hadDML)\n\t\t{\n\t\t\t/*\n\t\t\t * Two separate connections have already selected from this placement\n\t\t\t * and it was not modified. There is no benefit to using this connection.\n\t\t\t */\n\t\t}\n\t\telse if (CanUseExistingConnection(flags, userName, placementConnection))\n\t\t{\n\t\t\t/*\n\t\t\t * There is an existing connection for the placement and we can use it.\n\t\t\t */\n\n\t\t\tAssert(placementConnection != NULL);\n\t\t\tchosenConnection = placementConnection->connection;\n\n\t\t\tif (placementConnection->hadDDL || placementConnection->hadDML)\n\t\t\t{\n\t\t\t\t/* this connection performed writes, we must use it */\n\t\t\t\tfoundModifyingConnection = true;\n\t\t\t}\n\t\t}\n\t\telse if (placementConnection->hadDDL || placementConnection->hadDML)\n\t\t{\n\t\t\tif (strcmp(placementConnection->userName, userName) != 0)\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),\n\t\t\t\t\t\t errmsg(\"cannot perform query on placements that were \"\n\t\t\t\t\t\t\t\t\"modified in this transaction by a different \"\n\t\t\t\t\t\t\t\t\"user\")));\n\t\t\t}\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),\n\t\t\t\t\t errmsg(\"cannot perform query, because modifications were \"\n\t\t\t\t\t\t\t\"made over a connection that cannot be used at \"\n\t\t\t\t\t\t\t\"this time. This is most likely a Citus bug so \"\n\t\t\t\t\t\t\t\"please report it\"\n\t\t\t\t\t\t\t)));\n\t\t}\n\t}\n\n\treturn chosenConnection;\n}\n\n\n/*\n * FindOrCreatePlacementEntry finds a placement entry in either the\n * placement->connection hash or the co-located placements->connection hash,\n * or adds a new entry if the placement has not yet been accessed in the\n * current transaction.\n */\nstatic ConnectionPlacementHashEntry *\nFindOrCreatePlacementEntry(ShardPlacement *placement)\n{\n\tConnectionPlacementHashKey connKey;\n\tbool found = false;\n\n\tconnKey.placementId = placement->placementId;\n\n\tConnectionPlacementHashEntry *placementEntry = hash_search(ConnectionPlacementHash,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &connKey, HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &found);\n\tif (!found)\n\t{\n\t\t/* no connection has been chosen for this placement */\n\t\tplacementEntry->failed = false;\n\t\tplacementEntry->primaryConnection = NULL;\n\t\tplacementEntry->hasSecondaryConnections = false;\n\t\tplacementEntry->colocatedEntry = NULL;\n\n\t\tif (placement->partitionMethod == DISTRIBUTE_BY_HASH ||\n\t\t\tplacement->partitionMethod == DISTRIBUTE_BY_NONE)\n\t\t{\n\t\t\tColocatedPlacementsHashKey coloKey;\n\n\t\t\tcoloKey.nodeId = placement->nodeId;\n\t\t\tcoloKey.colocationGroupId = placement->colocationGroupId;\n\t\t\tcoloKey.representativeValue = placement->representativeValue;\n\n\t\t\t/* look for a connection assigned to co-located placements */\n\t\t\tColocatedPlacementsHashEntry *colocatedEntry = hash_search(\n\t\t\t\tColocatedPlacementsHash, &coloKey, HASH_ENTER,\n\t\t\t\t&found);\n\t\t\tif (!found)\n\t\t\t{\n\t\t\t\tvoid *conRef = MemoryContextAllocZero(TopTransactionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  sizeof(ConnectionReference));\n\n\t\t\t\tConnectionReference *connectionReference = (ConnectionReference *) conRef;\n\n\t\t\t\t/*\n\t\t\t\t * Store the co-location group information such that we can later\n\t\t\t\t * determine whether a connection accessed different placements\n\t\t\t\t * of the same co-location group.\n\t\t\t\t */\n\t\t\t\tconnectionReference->colocationGroupId = placement->colocationGroupId;\n\t\t\t\tconnectionReference->representativeValue = placement->representativeValue;\n\n\t\t\t\t/*\n\t\t\t\t * Create a connection reference that can be used for the entire\n\t\t\t\t * set of co-located placements.\n\t\t\t\t */\n\t\t\t\tcolocatedEntry->primaryConnection = connectionReference;\n\n\t\t\t\tcolocatedEntry->hasSecondaryConnections = false;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Assign the connection reference for the set of co-located placements\n\t\t\t * to the current placement.\n\t\t\t */\n\t\t\tplacementEntry->primaryConnection = colocatedEntry->primaryConnection;\n\t\t\tplacementEntry->colocatedEntry = colocatedEntry;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvoid *conRef = MemoryContextAllocZero(TopTransactionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t  sizeof(ConnectionReference));\n\n\t\t\tplacementEntry->primaryConnection = (ConnectionReference *) conRef;\n\t\t}\n\t}\n\n\t/* record association with shard, for invalidation */\n\tAssociatePlacementWithShard(placementEntry, placement);\n\n\treturn placementEntry;\n}\n\n\n/*\n * CanUseExistingConnection is a helper function for CheckExistingConnections()\n * that checks whether an existing connection can be reused.\n */\nstatic bool\nCanUseExistingConnection(uint32 flags, const char *userName,\n\t\t\t\t\t\t ConnectionReference *connectionReference)\n{\n\tMultiConnection *connection = connectionReference->connection;\n\n\tif (!connection)\n\t{\n\t\t/* if already closed connection obviously not usable */\n\t\treturn false;\n\t}\n\telse if (connection->claimedExclusively)\n\t{\n\t\t/* already used */\n\t\treturn false;\n\t}\n\telse if (flags & FORCE_NEW_CONNECTION)\n\t{\n\t\t/* no connection reuse desired */\n\t\treturn false;\n\t}\n\telse if (strcmp(connectionReference->userName, userName) != 0)\n\t{\n\t\t/* connection for different user, check for conflict */\n\t\treturn false;\n\t}\n\telse\n\t{\n\t\treturn true;\n\t}\n}\n\n\n/*\n * ConnectionAccessedDifferentPlacement returns true if the connection accessed another\n * placement in the same colocation group with a different representative value,\n * meaning it's not strictly colocated.\n */\nstatic bool\nConnectionAccessedDifferentPlacement(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t ShardPlacement *placement)\n{\n\tdlist_iter placementIter;\n\n\tdlist_foreach(placementIter, &connection->referencedPlacements)\n\t{\n\t\tConnectionReference *connectionReference =\n\t\t\tdlist_container(ConnectionReference, connectionNode, placementIter.cur);\n\n\t\t/* handle append and range distributed tables */\n\t\tif (placement->partitionMethod != DISTRIBUTE_BY_HASH &&\n\t\t\tplacement->placementId != connectionReference->placementId)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\t/* handle hash distributed tables */\n\t\tif (placement->colocationGroupId != INVALID_COLOCATION_ID &&\n\t\t\tplacement->colocationGroupId == connectionReference->colocationGroupId &&\n\t\t\tplacement->representativeValue != connectionReference->representativeValue)\n\t\t{\n\t\t\t/* non-co-located placements from the same co-location group */\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ConnectionModifiedPlacement returns true if any DML or DDL is executed over\n * the connection on any placement/table.\n */\nbool\nConnectionModifiedPlacement(MultiConnection *connection)\n{\n\tdlist_iter placementIter;\n\n\tif (connection->remoteTransaction.transactionState == REMOTE_TRANS_NOT_STARTED)\n\t{\n\t\t/*\n\t\t * When StartPlacementListConnection() is called, we set the\n\t\t * hadDDL/hadDML even before the actual command is sent to\n\t\t * remote nodes. And, if this function is called at that\n\t\t * point, we should not assume that the connection has already\n\t\t * done any modifications.\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (dlist_is_empty(&connection->referencedPlacements))\n\t{\n\t\t/*\n\t\t * When referencesPlacements are empty, it means that we come here\n\t\t * from an API that uses a node connection (e.g., not placement connection),\n\t\t * which doesn't set placements.\n\t\t * In that case, the command sent could be either write or read, so we assume\n\t\t * it is write to be on the safe side.\n\t\t */\n\t\treturn true;\n\t}\n\n\tdlist_foreach(placementIter, &connection->referencedPlacements)\n\t{\n\t\tConnectionReference *connectionReference =\n\t\t\tdlist_container(ConnectionReference, connectionNode, placementIter.cur);\n\n\t\tif (connectionReference->hadDDL || connectionReference->hadDML)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * AssociatePlacementWithShard records shard->placement relation in\n * ConnectionShardHash.\n *\n * That association is later used, in CheckForFailedPlacements, to invalidate\n * shard placements if necessary.\n */\nstatic void\nAssociatePlacementWithShard(ConnectionPlacementHashEntry *placementEntry,\n\t\t\t\t\t\t\tShardPlacement *placement)\n{\n\tConnectionShardHashKey shardKey;\n\tbool found = false;\n\tdlist_iter placementIter;\n\n\tshardKey.shardId = placement->shardId;\n\tConnectionShardHashEntry *shardEntry = hash_search(ConnectionShardHash, &shardKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER, &found);\n\tif (!found)\n\t{\n\t\tdlist_init(&shardEntry->placementConnections);\n\t}\n\n\t/*\n\t * Check if placement is already associated with shard (happens if there's\n\t * multiple connections for a placement).  There'll usually only be few\n\t * placement per shard, so the price of iterating isn't large.\n\t */\n\tdlist_foreach(placementIter, &shardEntry->placementConnections)\n\t{\n\t\tConnectionPlacementHashEntry *currPlacementEntry =\n\t\t\tdlist_container(ConnectionPlacementHashEntry, shardNode, placementIter.cur);\n\n\t\tif (currPlacementEntry->key.placementId == placement->placementId)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/* otherwise add */\n\tdlist_push_tail(&shardEntry->placementConnections, &placementEntry->shardNode);\n}\n\n\n/*\n * CloseShardPlacementAssociation handles a connection being closed before\n * transaction end.\n *\n * This should only be called by connection_management.c.\n */\nvoid\nCloseShardPlacementAssociation(struct MultiConnection *connection)\n{\n\tdlist_iter placementIter;\n\n\t/* set connection to NULL for all references to the connection */\n\tdlist_foreach(placementIter, &connection->referencedPlacements)\n\t{\n\t\tConnectionReference *reference =\n\t\t\tdlist_container(ConnectionReference, connectionNode, placementIter.cur);\n\n\t\treference->connection = NULL;\n\n\t\t/*\n\t\t * Note that we don't reset ConnectionPlacementHashEntry's\n\t\t * primaryConnection here, that'd be more complicated than it seems\n\t\t * worth.  That means we'll error out spuriously if a DML/DDL\n\t\t * executing connection is closed earlier in a transaction.\n\t\t */\n\t}\n}\n\n\n/*\n * ResetShardPlacementAssociation resets the association of connections to\n * shard placements at the end of a transaction.\n *\n * This should only be called by connection_management.c.\n */\nvoid\nResetShardPlacementAssociation(struct MultiConnection *connection)\n{\n\tdlist_init(&connection->referencedPlacements);\n}\n\n\n/*\n * ResetPlacementConnectionManagement() dissociates connections from\n * placements and shards. This will be called at the end of XACT_EVENT_COMMIT\n * and XACT_EVENT_ABORT.\n */\nvoid\nResetPlacementConnectionManagement(void)\n{\n\t/* Simply delete all entries */\n\thash_delete_all(ConnectionPlacementHash);\n\thash_delete_all(ConnectionShardHash);\n\thash_delete_all(ColocatedPlacementsHash);\n\n\t/*\n\t * NB: memory for ConnectionReference structs and subordinate data is\n\t * deleted by virtue of being allocated in TopTransactionContext.\n\t */\n}\n\n\n/*\n * ErrorIfPostCommitFailedShardPlacements throws an error if any of the placements\n * that modified the database and involved in the transaction has failed.\n *\n * Note that Citus already fails queries/commands in case of any failures during query\n * processing. However, there are certain failures that can only be detected on the\n * COMMIT time. And, this check mainly ensures to catch errors that happens on the\n * COMMIT time on the placements.\n *\n * The most common example for this case is the deferred errors that are thrown by\n * triggers or constraints at the COMMIT time.\n */\nvoid\nErrorIfPostCommitFailedShardPlacements(void)\n{\n\tHASH_SEQ_STATUS status;\n\tConnectionShardHashEntry *shardEntry = NULL;\n\n\thash_seq_init(&status, ConnectionShardHash);\n\twhile ((shardEntry = (ConnectionShardHashEntry *) hash_seq_search(&status)) != 0)\n\t{\n\t\tif (HasModificationFailedForShard(shardEntry))\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errmsg(\"could not commit transaction for shard \" INT64_FORMAT\n\t\t\t\t\t\t\t\" on at least one active node\", shardEntry->key.shardId)));\n\t\t}\n\t}\n}\n\n\n/*\n * HasModificationFailedForShard is a helper function for\n * ErrorIfPostCommitFailedShardPlacements that performs the per-shard work.\n *\n * The function returns true if any placement of the input shard is modified\n * and any failures has happened (either connection failures or transaction\n * failures).\n */\nstatic bool\nHasModificationFailedForShard(ConnectionShardHashEntry *shardEntry)\n{\n\tdlist_iter placementIter;\n\n\tdlist_foreach(placementIter, &shardEntry->placementConnections)\n\t{\n\t\tConnectionPlacementHashEntry *placementEntry =\n\t\t\tdlist_container(ConnectionPlacementHashEntry, shardNode, placementIter.cur);\n\t\tConnectionReference *primaryConnection = placementEntry->primaryConnection;\n\n\t\t/* we only consider shards that are modified */\n\t\tif (primaryConnection == NULL ||\n\t\t\t!(primaryConnection->hadDDL || primaryConnection->hadDML))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tMultiConnection *connection = primaryConnection->connection;\n\n\t\tif (!connection || connection->remoteTransaction.transactionFailed)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * InitPlacementConnectionManagement performs initialization of the\n * infrastructure in this file at server start.\n */\nvoid\nInitPlacementConnectionManagement(void)\n{\n\tHASHCTL info;\n\n\t/* create (placementId) -> [ConnectionReference] hash */\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(ConnectionPlacementHashKey);\n\tinfo.entrysize = sizeof(ConnectionPlacementHashEntry);\n\tinfo.hash = tag_hash;\n\tinfo.hcxt = ConnectionContext;\n\tuint32 hashFlags = (HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);\n\n\tConnectionPlacementHash = hash_create(\"citus connection cache (placementid)\",\n\t\t\t\t\t\t\t\t\t\t  64, &info, hashFlags);\n\n\t/* create (colocated placement identity) -> [ConnectionReference] hash */\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(ColocatedPlacementsHashKey);\n\tinfo.entrysize = sizeof(ColocatedPlacementsHashEntry);\n\tinfo.hash = ColocatedPlacementsHashHash;\n\tinfo.match = ColocatedPlacementsHashCompare;\n\tinfo.hcxt = ConnectionContext;\n\thashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT | HASH_COMPARE);\n\n\tColocatedPlacementsHash = hash_create(\"citus connection cache (colocated placements)\",\n\t\t\t\t\t\t\t\t\t\t  64, &info, hashFlags);\n\n\t/* create (shardId) -> [ConnectionShardHashEntry] hash */\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(ConnectionShardHashKey);\n\tinfo.entrysize = sizeof(ConnectionShardHashEntry);\n\tinfo.hash = tag_hash;\n\tinfo.hcxt = ConnectionContext;\n\thashFlags = (HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);\n\n\tConnectionShardHash = hash_create(\"citus connection cache (shardid)\",\n\t\t\t\t\t\t\t\t\t  64, &info, hashFlags);\n}\n\n\n/*\n * UseConnectionPerPlacement returns whether we should use as separate connection\n * per placement even if another connection is idle. We mostly use this in testing\n * scenarios.\n */\nbool\nUseConnectionPerPlacement(void)\n{\n\treturn ForceMaxQueryParallelization &&\n\t\t   MultiShardConnectionType != SEQUENTIAL_CONNECTION;\n}\n\n\nstatic uint32\nColocatedPlacementsHashHash(const void *key, Size keysize)\n{\n\tColocatedPlacementsHashKey *entry = (ColocatedPlacementsHashKey *) key;\n\n\tuint32 hash = hash_uint32(entry->nodeId);\n\thash = hash_combine(hash, hash_uint32(entry->colocationGroupId));\n\thash = hash_combine(hash, hash_uint32(entry->representativeValue));\n\n\treturn hash;\n}\n\n\nstatic int\nColocatedPlacementsHashCompare(const void *a, const void *b, Size keysize)\n{\n\tColocatedPlacementsHashKey *ca = (ColocatedPlacementsHashKey *) a;\n\tColocatedPlacementsHashKey *cb = (ColocatedPlacementsHashKey *) b;\n\n\tif (ca->nodeId != cb->nodeId ||\n\t\tca->colocationGroupId != cb->colocationGroupId ||\n\t\tca->representativeValue != cb->representativeValue)\n\t{\n\t\treturn 1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/connection/remote_commands.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * remote_commands.c\n *   Helpers to make it easier to execute command on remote nodes.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"catalog/pg_collation.h\"\n#include \"lib/stringinfo.h\"\n#include \"storage/latch.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgrprotos.h\"\n#include \"utils/palloc.h\"\n\n#include \"distributed/cancel_utils.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/remote_commands.h\"\n\n\n/*\n * Setting that controls how many bytes of COPY data libpq is allowed to buffer\n * internally before we force a flush.\n */\nint RemoteCopyFlushThreshold = 8 * 1024 * 1024;\n\n\n/* GUC, determining whether statements sent to remote nodes are logged */\nbool LogRemoteCommands = false;\nchar *GrepRemoteCommands = \"\";\n\n\nstatic bool ClearResultsInternal(MultiConnection *connection, bool raiseErrors,\n\t\t\t\t\t\t\t\t bool discardWarnings);\nstatic bool FinishConnectionIO(MultiConnection *connection, bool raiseInterrupts);\nstatic WaitEventSet * BuildWaitEventSet(MultiConnection **allConnections,\n\t\t\t\t\t\t\t\t\t\tint totalConnectionCount,\n\t\t\t\t\t\t\t\t\t\tint pendingConnectionsStartIndex);\n\n\n/* simple helpers */\n\n/*\n * IsResponseOK checks whether the result is a successful one.\n */\nbool\nIsResponseOK(PGresult *result)\n{\n\tExecStatusType resultStatus = PQresultStatus(result);\n\n\tif (resultStatus == PGRES_SINGLE_TUPLE || resultStatus == PGRES_TUPLES_OK ||\n\t\tresultStatus == PGRES_COMMAND_OK)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ForgetResults clears a connection from pending activity.\n *\n * Note that this might require network IO. If that's not acceptable, use\n * ClearResultsIfReady().\n *\n * ClearResults is variant of this function which can also raise errors.\n */\nvoid\nForgetResults(MultiConnection *connection)\n{\n\tClearResults(connection, false);\n}\n\n\n/*\n * ClearResults clears a connection from pending activity,\n * returns true if all pending commands return success. It raises\n * error if raiseErrors flag is set, any command fails and transaction\n * is marked critical.\n *\n * Note that this might require network IO. If that's not acceptable, use\n * ClearResultsIfReady().\n */\nbool\nClearResults(MultiConnection *connection, bool raiseErrors)\n{\n\treturn ClearResultsInternal(connection, raiseErrors, false);\n}\n\n\n/*\n * ClearResultsDiscardWarnings does the same thing as ClearResults, but doesn't\n * emit warnings.\n */\nbool\nClearResultsDiscardWarnings(MultiConnection *connection, bool raiseErrors)\n{\n\treturn ClearResultsInternal(connection, raiseErrors, true);\n}\n\n\n/*\n * ClearResultsInternal is used by ClearResults and ClearResultsDiscardWarnings.\n */\nstatic bool\nClearResultsInternal(MultiConnection *connection, bool raiseErrors, bool discardWarnings)\n{\n\tbool success = true;\n\n\twhile (true)\n\t{\n\t\tPGresult *result = GetRemoteCommandResult(connection, raiseErrors);\n\t\tif (result == NULL)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t/*\n\t\t * End any pending copy operation. Transaction will be marked\n\t\t * as failed by the following part.\n\t\t */\n\t\tif (PQresultStatus(result) == PGRES_COPY_IN)\n\t\t{\n\t\t\tPQputCopyEnd(connection->pgConn, NULL);\n\t\t}\n\n\t\tif (!IsResponseOK(result))\n\t\t{\n\t\t\tif (!discardWarnings)\n\t\t\t{\n\t\t\t\tReportResultError(connection, result, WARNING);\n\t\t\t}\n\n\t\t\tMarkRemoteTransactionFailed(connection, raiseErrors);\n\n\t\t\tsuccess = false;\n\n\t\t\t/* an error happened, there is nothing we can do more */\n\t\t\tif (PQresultStatus(result) == PGRES_FATAL_ERROR)\n\t\t\t{\n\t\t\t\tPQclear(result);\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tPQclear(result);\n\t}\n\n\treturn success;\n}\n\n\n/*\n * ClearResultsIfReady clears a connection from pending activity if doing\n * so does not require network IO. Returns true if successful, false\n * otherwise.\n */\nbool\nClearResultsIfReady(MultiConnection *connection)\n{\n\tPGconn *pgConn = connection->pgConn;\n\n\tif (PQstatus(pgConn) != CONNECTION_OK)\n\t{\n\t\treturn false;\n\t}\n\n\tAssert(PQisnonblocking(pgConn));\n\n\twhile (true)\n\t{\n\t\t/*\n\t\t * If busy, there might still be results already received and buffered\n\t\t * by the OS. As connection is in non-blocking mode, we can check for\n\t\t * that without blocking.\n\t\t */\n\t\tif (PQisBusy(pgConn))\n\t\t{\n\t\t\tif (PQflush(pgConn) == -1)\n\t\t\t{\n\t\t\t\t/* write failed */\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (PQconsumeInput(pgConn) == 0)\n\t\t\t{\n\t\t\t\t/* some low-level failure */\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t/* clearing would require blocking IO, return */\n\t\tif (PQisBusy(pgConn))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tPGresult *result = PQgetResult(pgConn);\n\t\tif (result == NULL)\n\t\t{\n\t\t\t/* no more results available */\n\t\t\treturn true;\n\t\t}\n\n\t\tExecStatusType resultStatus = PQresultStatus(result);\n\n\t\t/* only care about the status, can clear now */\n\t\tPQclear(result);\n\n\t\tif (resultStatus == PGRES_COPY_IN || resultStatus == PGRES_COPY_OUT)\n\t\t{\n\t\t\t/* in copy, can't reliably recover without blocking */\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!(resultStatus == PGRES_SINGLE_TUPLE || resultStatus == PGRES_TUPLES_OK ||\n\t\t\t  resultStatus == PGRES_COMMAND_OK))\n\t\t{\n\t\t\t/* an error occurred just when we were aborting */\n\t\t\treturn false;\n\t\t}\n\n\t\t/* check if there are more results to consume */\n\t}\n\n\tpg_unreachable();\n}\n\n\n/* report errors & warnings */\n\n/*\n * Report libpq failure that's not associated with a result.\n */\nvoid\nReportConnectionError(MultiConnection *connection, int elevel)\n{\n\tchar *userName = connection->user;\n\tchar *nodeName = connection->hostname;\n\tint nodePort = connection->port;\n\tPGconn *pgConn = connection->pgConn;\n\tchar *messageDetail = NULL;\n\n\tif (pgConn != NULL)\n\t{\n\t\tmessageDetail = pchomp(PQerrorMessage(pgConn));\n\t\tif (messageDetail == NULL || messageDetail[0] == '\\0')\n\t\t{\n\t\t\t/* give a similar messages to Postgres */\n\t\t\tmessageDetail = \"connection not open\";\n\t\t}\n\t}\n\n\tif (messageDetail)\n\t{\n\t\tereport(elevel, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t errmsg(\"connection to the remote node %s@%s:%d failed with the \"\n\t\t\t\t\t\t\t\t\"following error: %s\", userName, nodeName, nodePort,\n\t\t\t\t\t\t\t\tmessageDetail)));\n\t}\n\telse\n\t{\n\t\tereport(elevel, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t errmsg(\"connection to the remote node %s@%s:%d failed\",\n\t\t\t\t\t\t\t\tuserName, nodeName, nodePort)));\n\t}\n}\n\n\n/*\n * ReportResultError reports libpq failure associated with a result.\n */\nvoid\nReportResultError(MultiConnection *connection, PGresult *result, int elevel)\n{\n\t/* we release PQresult when throwing an error because the caller can't */\n\tPG_TRY();\n\t{\n\t\tchar *sqlStateString = PQresultErrorField(result, PG_DIAG_SQLSTATE);\n\t\tchar *messagePrimary = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);\n\t\tchar *messageDetail = PQresultErrorField(result, PG_DIAG_MESSAGE_DETAIL);\n\t\tchar *messageHint = PQresultErrorField(result, PG_DIAG_MESSAGE_HINT);\n\t\tchar *messageContext = PQresultErrorField(result, PG_DIAG_CONTEXT);\n\n\t\tchar *nodeName = connection->hostname;\n\t\tint nodePort = connection->port;\n\t\tint sqlState = ERRCODE_INTERNAL_ERROR;\n\n\t\tif (sqlStateString != NULL)\n\t\t{\n\t\t\tsqlState = MAKE_SQLSTATE(sqlStateString[0],\n\t\t\t\t\t\t\t\t\t sqlStateString[1],\n\t\t\t\t\t\t\t\t\t sqlStateString[2],\n\t\t\t\t\t\t\t\t\t sqlStateString[3],\n\t\t\t\t\t\t\t\t\t sqlStateString[4]);\n\t\t}\n\n\t\t/*\n\t\t * If the PGresult did not contain a message, the connection may provide a\n\t\t * suitable top level one. At worst, this is an empty string.\n\t\t */\n\t\tif (messagePrimary == NULL)\n\t\t{\n\t\t\tmessagePrimary = pchomp(PQerrorMessage(connection->pgConn));\n\t\t}\n\n\t\tereport(elevel, (errcode(sqlState), errmsg(\"%s\", messagePrimary),\n\t\t\t\t\t\t messageDetail ?\n\t\t\t\t\t\t errdetail(\"%s\", messageDetail) : 0,\n\t\t\t\t\t\t messageHint ? errhint(\"%s\", messageHint) : 0,\n\t\t\t\t\t\t messageContext ? errcontext(\"%s\", messageContext) : 0,\n\t\t\t\t\t\t errcontext(\"while executing command on %s:%d\",\n\t\t\t\t\t\t\t\t\tnodeName, nodePort)));\n\t}\n\tPG_CATCH();\n\t{\n\t\tPQclear(result);\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n}\n\n\n/* *INDENT-ON* */\n\n/*\n * LogRemoteCommand logs commands send to remote nodes if\n * citus.log_remote_commands wants us to do so.\n */\nvoid\nLogRemoteCommand(MultiConnection *connection, const char *command)\n{\n\tif (!LogRemoteCommands)\n\t{\n\t\treturn;\n\t}\n\n\tif (!CommandMatchesLogGrepPattern(command))\n\t{\n\t\treturn;\n\t}\n\n\tereport(NOTICE, (errmsg(\"issuing %s\", command),\n\t\t\t\t\t errdetail(\"on server %s@%s:%d connectionId: %ld\", connection->user,\n\t\t\t\t\t\t\t   connection->hostname,\n\t\t\t\t\t\t\t   connection->port, connection->connectionId)));\n}\n\n\n/*\n * CommandMatchesLogGrepPattern returns true of the input command matches\n * the pattern specified by citus.grep_remote_commands.\n *\n * If citus.grep_remote_commands set to an empty string, all commands are\n * considered as a match.\n */\nbool\nCommandMatchesLogGrepPattern(const char *command)\n{\n\tif (GrepRemoteCommands && strnlen(GrepRemoteCommands, NAMEDATALEN) > 0)\n\t{\n\t\tDatum boolDatum =\n\t\t\tDirectFunctionCall2Coll(textlike, DEFAULT_COLLATION_OID,\n\t\t\t\t\t\t\t\t\tCStringGetTextDatum(command),\n\t\t\t\t\t\t\t\t\tCStringGetTextDatum(GrepRemoteCommands));\n\n\t\treturn DatumGetBool(boolDatum);\n\t}\n\n\treturn true;\n}\n\n\n/* wrappers around libpq functions, with command logging support */\n\n\n/*\n * ExecuteCriticalRemoteCommandList calls ExecuteCriticalRemoteCommand for every\n * command in the commandList.\n */\nvoid\nExecuteCriticalRemoteCommandList(MultiConnection *connection, List *commandList)\n{\n\tconst char *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tExecuteCriticalRemoteCommand(connection, command);\n\t}\n}\n\n\n/*\n * ExecuteCriticalRemoteCommand executes a remote command that is critical\n * to the transaction. If the command fails then the transaction aborts.\n */\nvoid\nExecuteCriticalRemoteCommand(MultiConnection *connection, const char *command)\n{\n\tbool raiseInterrupts = true;\n\n\tint querySent = SendRemoteCommand(connection, command);\n\tif (querySent == 0)\n\t{\n\t\tReportConnectionError(connection, ERROR);\n\t}\n\n\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\tif (!IsResponseOK(result))\n\t{\n\t\tReportResultError(connection, result, ERROR);\n\t}\n\n\tPQclear(result);\n\tForgetResults(connection);\n}\n\n\n/*\n * ExecuteRemoteCommandInConnectionList executes a remote command, on all connections\n * given in the list, that is critical to the transaction. If the command fails then\n * the transaction aborts.\n */\nvoid\nExecuteRemoteCommandInConnectionList(List *nodeConnectionList, const char *command)\n{\n\tMultiConnection *connection = NULL;\n\n\tforeach_declared_ptr(connection, nodeConnectionList)\n\t{\n\t\tint querySent = SendRemoteCommand(connection, command);\n\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tReportConnectionError(connection, ERROR);\n\t\t}\n\t}\n\n\t/* Process the result */\n\tforeach_declared_ptr(connection, nodeConnectionList)\n\t{\n\t\tbool raiseInterrupts = true;\n\t\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\n\t\tif (!IsResponseOK(result))\n\t\t{\n\t\t\tReportResultError(connection, result, ERROR);\n\t\t}\n\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\t}\n}\n\n\n/*\n * ExecuteOptionalRemoteCommand executes a remote command. If the command fails a WARNING\n * is emitted but execution continues.\n *\n * could return 0, QUERY_SEND_FAILED, or RESPONSE_NOT_OKAY\n * result is only set if there was no error\n */\nint\nExecuteOptionalRemoteCommand(MultiConnection *connection, const char *command,\n\t\t\t\t\t\t\t PGresult **result)\n{\n\tbool raiseInterrupts = true;\n\n\tint querySent = SendRemoteCommand(connection, command);\n\tif (querySent == 0)\n\t{\n\t\tReportConnectionError(connection, WARNING);\n\t\treturn QUERY_SEND_FAILED;\n\t}\n\n\tPGresult *localResult = GetRemoteCommandResult(connection, raiseInterrupts);\n\tif (!IsResponseOK(localResult))\n\t{\n\t\tReportResultError(connection, localResult, WARNING);\n\t\tPQclear(localResult);\n\t\tForgetResults(connection);\n\t\treturn RESPONSE_NOT_OKAY;\n\t}\n\n\t/*\n\t * store result if result has been set, when the user is not interested in the result\n\t * a NULL pointer could be passed and the result will be cleared.\n\t */\n\tif (result != NULL)\n\t{\n\t\t*result = localResult;\n\t}\n\telse\n\t{\n\t\tPQclear(localResult);\n\t\tForgetResults(connection);\n\t}\n\n\treturn RESPONSE_OKAY;\n}\n\n\n/*\n * SendRemoteCommandParams is a PQsendQueryParams wrapper that logs remote commands,\n * and accepts a MultiConnection instead of a plain PGconn. It makes sure it can\n * send commands asynchronously without blocking (at the potential expense of\n * an additional memory allocation). The command string can only include a single\n * command since PQsendQueryParams() supports only that.\n */\nint\nSendRemoteCommandParams(MultiConnection *connection, const char *command,\n\t\t\t\t\t\tint parameterCount, const Oid *parameterTypes,\n\t\t\t\t\t\tconst char *const *parameterValues, bool binaryResults)\n{\n\tPGconn *pgConn = connection->pgConn;\n\n\tLogRemoteCommand(connection, command);\n\n\t/*\n\t * Don't try to send command if connection is entirely gone\n\t * (PQisnonblocking() would crash).\n\t */\n\tif (!pgConn || PQstatus(pgConn) != CONNECTION_OK)\n\t{\n\t\treturn 0;\n\t}\n\n\tAssert(PQisnonblocking(pgConn));\n\n\tint rc = PQsendQueryParams(pgConn, command, parameterCount, parameterTypes,\n\t\t\t\t\t\t\t   parameterValues, NULL, NULL, binaryResults ? 1 : 0);\n\n\treturn rc;\n}\n\n\n/*\n * SendRemoteCommand is a PQsendQuery wrapper that logs remote commands, and\n * accepts a MultiConnection instead of a plain PGconn. It makes sure it can\n * send commands asynchronously without blocking (at the potential expense of\n * an additional memory allocation). The command string can include multiple\n * commands since PQsendQuery() supports that.\n */\nint\nSendRemoteCommand(MultiConnection *connection, const char *command)\n{\n\tPGconn *pgConn = connection->pgConn;\n\n\tLogRemoteCommand(connection, command);\n\n\t/*\n\t * Don't try to send command if connection is entirely gone\n\t * (PQisnonblocking() would crash).\n\t */\n\tif (!pgConn || PQstatus(pgConn) != CONNECTION_OK)\n\t{\n\t\treturn 0;\n\t}\n\n\tAssert(PQisnonblocking(pgConn));\n\n\tint rc = PQsendQuery(pgConn, command);\n\n\treturn rc;\n}\n\n\n/*\n * ExecuteRemoteCommandAndCheckResult executes the given command in the remote node and\n * checks if the result is equal to the expected result. If the result is equal to the\n * expected result, the function returns true, otherwise it returns false.\n */\nbool\nExecuteRemoteCommandAndCheckResult(MultiConnection *connection, char *command,\n\t\t\t\t\t\t\t\t   char *expected)\n{\n\tif (!SendRemoteCommand(connection, command))\n\t{\n\t\t/* if we cannot connect, we warn and report false */\n\t\tReportConnectionError(connection, WARNING);\n\t\treturn false;\n\t}\n\tbool raiseInterrupts = true;\n\tPGresult *queryResult = GetRemoteCommandResult(connection, raiseInterrupts);\n\n\t/* if remote node throws an error, we also throw an error */\n\tif (!IsResponseOK(queryResult))\n\t{\n\t\tReportResultError(connection, queryResult, ERROR);\n\t}\n\n\tStringInfo queryResultString = makeStringInfo();\n\n\t/* Evaluate the queryResult and store it into the queryResultString */\n\tbool success = EvaluateSingleQueryResult(connection, queryResult, queryResultString);\n\tbool result = false;\n\tif (success && strcmp(queryResultString->data, expected) == 0)\n\t{\n\t\tresult = true;\n\t}\n\n\tPQclear(queryResult);\n\tForgetResults(connection);\n\n\treturn result;\n}\n\n\n/*\n * ReadFirstColumnAsText reads the first column of result tuples from the given\n * PGresult struct and returns them in a StringInfo list.\n */\nList *\nReadFirstColumnAsText(PGresult *queryResult)\n{\n\tList *resultRowList = NIL;\n\tconst int columnIndex = 0;\n\tint64 rowCount = 0;\n\n\tExecStatusType status = PQresultStatus(queryResult);\n\tif (status == PGRES_TUPLES_OK)\n\t{\n\t\trowCount = PQntuples(queryResult);\n\t}\n\n\tfor (int64 rowIndex = 0; rowIndex < rowCount; rowIndex++)\n\t{\n\t\tchar *rowValue = PQgetvalue(queryResult, rowIndex, columnIndex);\n\n\t\tStringInfo rowValueString = makeStringInfo();\n\t\tappendStringInfoString(rowValueString, rowValue);\n\n\t\tresultRowList = lappend(resultRowList, rowValueString);\n\t}\n\n\treturn resultRowList;\n}\n\n\n/*\n * GetRemoteCommandResult is a wrapper around PQgetResult() that handles interrupts.\n *\n * If raiseInterrupts is true and an interrupt arrives, e.g. the query is\n * being cancelled, CHECK_FOR_INTERRUPTS() will be called, which then throws\n * an error.\n *\n * If raiseInterrupts is false and an interrupt arrives that'd otherwise raise\n * an error, GetRemoteCommandResult returns NULL, and the transaction is\n * marked as having failed. While that's not a perfect way to signal failure,\n * callers will usually treat that as an error, and it's easy to use.\n *\n * Handling of interrupts is important to allow queries being cancelled while\n * waiting on remote nodes. In a distributed deadlock scenario cancelling\n * might be the only way to resolve the deadlock.\n */\nPGresult *\nGetRemoteCommandResult(MultiConnection *connection, bool raiseInterrupts)\n{\n\tPGconn *pgConn = connection->pgConn;\n\n\t/*\n\t * Short circuit tests around the more expensive parts of this\n\t * routine. This'd also trigger a return in the, unlikely, case of a\n\t * failed/nonexistant connection.\n\t */\n\tif (!PQisBusy(pgConn))\n\t{\n\t\treturn PQgetResult(connection->pgConn);\n\t}\n\n\tif (!FinishConnectionIO(connection, raiseInterrupts))\n\t{\n\t\t/* some error(s) happened while doing the I/O, signal the callers */\n\t\tif (PQstatus(pgConn) == CONNECTION_BAD)\n\t\t{\n\t\t\treturn PQmakeEmptyPGresult(pgConn, PGRES_FATAL_ERROR);\n\t\t}\n\n\t\treturn NULL;\n\t}\n\n\t/* no IO should be necessary to get result */\n\tAssert(!PQisBusy(pgConn));\n\n\tPGresult *result = PQgetResult(connection->pgConn);\n\n\treturn result;\n}\n\n\n/*\n * PutRemoteCopyData is a wrapper around PQputCopyData() that handles\n * interrupts.\n *\n * Returns false if PQputCopyData() failed, true otherwise.\n */\nbool\nPutRemoteCopyData(MultiConnection *connection, const char *buffer, int nbytes)\n{\n\tPGconn *pgConn = connection->pgConn;\n\tbool allowInterrupts = true;\n\n\tif (PQstatus(pgConn) != CONNECTION_OK)\n\t{\n\t\treturn false;\n\t}\n\n\tAssert(PQisnonblocking(pgConn));\n\n\tint copyState = PQputCopyData(pgConn, buffer, nbytes);\n\tif (copyState <= 0)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * PQputCopyData may have queued up part of the data even if it managed\n\t * to send some of it successfully. We provide back pressure by waiting\n\t * until the socket is writable to prevent the internal libpq buffers\n\t * from growing excessively.\n\t *\n\t * We currently allow the internal buffer to grow to 8MB before\n\t * providing back pressure based on experimentation that showed\n\t * throughput get worse at 4MB and lower due to the number of CPU\n\t * cycles spent in networking system calls.\n\t */\n\n\tconnection->copyBytesWrittenSinceLastFlush += nbytes;\n\tif (connection->copyBytesWrittenSinceLastFlush > RemoteCopyFlushThreshold)\n\t{\n\t\tconnection->copyBytesWrittenSinceLastFlush = 0;\n\t\treturn FinishConnectionIO(connection, allowInterrupts);\n\t}\n\n\treturn true;\n}\n\n\n/*\n * PutRemoteCopyEnd is a wrapper around PQputCopyEnd() that handles\n * interrupts.\n *\n * Returns false if PQputCopyEnd() failed, true otherwise.\n */\nbool\nPutRemoteCopyEnd(MultiConnection *connection, const char *errormsg)\n{\n\tPGconn *pgConn = connection->pgConn;\n\tbool allowInterrupts = true;\n\n\tif (PQstatus(pgConn) != CONNECTION_OK)\n\t{\n\t\treturn false;\n\t}\n\n\tAssert(PQisnonblocking(pgConn));\n\n\tint copyState = PQputCopyEnd(pgConn, errormsg);\n\tif (copyState == -1)\n\t{\n\t\treturn false;\n\t}\n\n\t/* see PutRemoteCopyData() */\n\n\tconnection->copyBytesWrittenSinceLastFlush = 0;\n\n\treturn FinishConnectionIO(connection, allowInterrupts);\n}\n\n\n/*\n * FinishConnectionIO performs pending IO for the connection, while accepting\n * interrupts.\n *\n * See GetRemoteCommandResult() for documentation of interrupt handling\n * behaviour.\n *\n * Returns true if IO was successfully completed, false otherwise.\n */\nstatic bool\nFinishConnectionIO(MultiConnection *connection, bool raiseInterrupts)\n{\n\tPGconn *pgConn = connection->pgConn;\n\tint sock = PQsocket(pgConn);\n\n\tAssert(pgConn);\n\tAssert(PQisnonblocking(pgConn));\n\n\tif (raiseInterrupts)\n\t{\n\t\tCHECK_FOR_INTERRUPTS();\n\t}\n\n\t/* perform the necessary IO */\n\twhile (true)\n\t{\n\t\tint waitFlags = WL_POSTMASTER_DEATH | WL_LATCH_SET;\n\n\t\t/* try to send all pending data */\n\t\tint sendStatus = PQflush(pgConn);\n\n\t\t/* if sending failed, there's nothing more we can do */\n\t\tif (sendStatus == -1)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\telse if (sendStatus == 1)\n\t\t{\n\t\t\twaitFlags |= WL_SOCKET_WRITEABLE;\n\t\t}\n\n\t\t/* if reading fails, there's not much we can do */\n\t\tif (PQconsumeInput(pgConn) == 0)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t\tif (PQisBusy(pgConn))\n\t\t{\n\t\t\twaitFlags |= WL_SOCKET_READABLE;\n\t\t}\n\n\t\tif ((waitFlags & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) == 0)\n\t\t{\n\t\t\t/* no IO necessary anymore, we're done */\n\t\t\treturn true;\n\t\t}\n\n\t\tint rc = WaitLatchOrSocket(MyLatch, waitFlags, sock, 0, PG_WAIT_EXTENSION);\n\t\tif (rc & WL_POSTMASTER_DEATH)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"postmaster was shut down, exiting\")));\n\t\t}\n\n\t\tif (rc & WL_LATCH_SET)\n\t\t{\n\t\t\tResetLatch(MyLatch);\n\n\t\t\t/* if allowed raise errors */\n\t\t\tif (raiseInterrupts)\n\t\t\t{\n\t\t\t\tCHECK_FOR_INTERRUPTS();\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If raising errors allowed, or called within in a section with\n\t\t\t * interrupts held, return instead, and mark the transaction as\n\t\t\t * failed.\n\t\t\t */\n\t\t\tif (IsHoldOffCancellationReceived())\n\t\t\t{\n\t\t\t\tconnection->remoteTransaction.transactionFailed = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * WaitForAllConnections blocks until all connections in the list are no\n * longer busy, meaning the pending command has either finished or failed.\n */\nvoid\nWaitForAllConnections(List *connectionList, bool raiseInterrupts)\n{\n\tint totalConnectionCount = list_length(connectionList);\n\tint pendingConnectionsStartIndex = 0;\n\tint connectionIndex = 0;\n\n\tMultiConnection **allConnections =\n\t\tpalloc(totalConnectionCount * sizeof(MultiConnection *));\n\tWaitEvent *events = palloc(totalConnectionCount * sizeof(WaitEvent));\n\tbool *connectionReady = palloc(totalConnectionCount * sizeof(bool));\n\tWaitEventSet *volatile waitEventSet = NULL;\n\n\t/* convert connection list to an array such that we can move items around */\n\tMultiConnection *connectionItem = NULL;\n\tforeach_declared_ptr(connectionItem, connectionList)\n\t{\n\t\tallConnections[connectionIndex] = connectionItem;\n\t\tconnectionReady[connectionIndex] = false;\n\t\tconnectionIndex++;\n\t}\n\n\t/* make an initial pass to check for failed and idle connections */\n\tfor (connectionIndex = 0; connectionIndex < totalConnectionCount; connectionIndex++)\n\t{\n\t\tMultiConnection *connection = allConnections[connectionIndex];\n\n\t\tif (PQstatus(connection->pgConn) == CONNECTION_BAD ||\n\t\t\t!PQisBusy(connection->pgConn))\n\t\t{\n\t\t\t/* connection is already done; keep non-ready connections at the end */\n\t\t\tallConnections[connectionIndex] =\n\t\t\t\tallConnections[pendingConnectionsStartIndex];\n\t\t\tpendingConnectionsStartIndex++;\n\t\t}\n\t}\n\n\tPG_TRY();\n\t{\n\t\tbool rebuildWaitEventSet = true;\n\n\t\twhile (pendingConnectionsStartIndex < totalConnectionCount)\n\t\t{\n\t\t\tbool cancellationReceived = false;\n\t\t\tint eventIndex = 0;\n\t\t\tlong timeout = -1;\n\t\t\tint pendingConnectionCount = totalConnectionCount -\n\t\t\t\t\t\t\t\t\t\t pendingConnectionsStartIndex;\n\n\t\t\t/* rebuild the WaitEventSet whenever connections are ready */\n\t\t\tif (rebuildWaitEventSet)\n\t\t\t{\n\t\t\t\tif (waitEventSet != NULL)\n\t\t\t\t{\n\t\t\t\t\tFreeWaitEventSet(waitEventSet);\n\t\t\t\t}\n\n\t\t\t\twaitEventSet = BuildWaitEventSet(allConnections, totalConnectionCount,\n\t\t\t\t\t\t\t\t\t\t\t\t pendingConnectionsStartIndex);\n\n\t\t\t\trebuildWaitEventSet = false;\n\t\t\t}\n\n\t\t\t/* wait for I/O events */\n\t\t\tint eventCount = WaitEventSetWait(waitEventSet, timeout, events,\n\t\t\t\t\t\t\t\t\t\t\t  pendingConnectionCount,\n\t\t\t\t\t\t\t\t\t\t\t  WAIT_EVENT_CLIENT_READ);\n\n\t\t\t/* process I/O events */\n\t\t\tfor (; eventIndex < eventCount; eventIndex++)\n\t\t\t{\n\t\t\t\tWaitEvent *event = &events[eventIndex];\n\t\t\t\tbool connectionIsReady = false;\n\n\t\t\t\tif (event->events & WL_POSTMASTER_DEATH)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errmsg(\"postmaster was shut down, exiting\")));\n\t\t\t\t}\n\n\t\t\t\tif (event->events & WL_LATCH_SET)\n\t\t\t\t{\n\t\t\t\t\tResetLatch(MyLatch);\n\n\t\t\t\t\tif (raiseInterrupts)\n\t\t\t\t\t{\n\t\t\t\t\t\tCHECK_FOR_INTERRUPTS();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (IsHoldOffCancellationReceived())\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Break out of event loop immediately in case of cancellation.\n\t\t\t\t\t\t * We cannot use \"return\" here inside a PG_TRY() block since\n\t\t\t\t\t\t * then the exception stack won't be reset.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tcancellationReceived = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tMultiConnection *connection = (MultiConnection *) event->user_data;\n\n\t\t\t\tif (event->events & WL_SOCKET_WRITEABLE)\n\t\t\t\t{\n\t\t\t\t\tint sendStatus = PQflush(connection->pgConn);\n\t\t\t\t\tif (sendStatus == -1)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* send failed, done with this connection */\n\t\t\t\t\t\tconnectionIsReady = true;\n\t\t\t\t\t}\n\t\t\t\t\telse if (sendStatus == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* done writing, only wait for read events */\n\t\t\t\t\t\tbool success =\n\t\t\t\t\t\t\tCitusModifyWaitEvent(waitEventSet, event->pos,\n\t\t\t\t\t\t\t\t\t\t\t\t WL_SOCKET_READABLE, NULL);\n\t\t\t\t\t\tif (!success)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t\t\t\t\t\terrmsg(\"connection establishment for \"\n\t\t\t\t\t\t\t\t\t\t\t\t   \"node %s:%d failed\",\n\t\t\t\t\t\t\t\t\t\t\t\t   connection->hostname,\n\t\t\t\t\t\t\t\t\t\t\t\t   connection->port),\n\t\t\t\t\t\t\t\t\t\t\terrhint(\"Check both the local and remote \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"server logs for the connection \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"establishment errors.\")));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * Check whether the connection is done is the socket is either readable\n\t\t\t\t * or writable. If it was only writable, we performed a PQflush which\n\t\t\t\t * might have read from the socket, meaning we may not see the socket\n\t\t\t\t * becoming readable again, so better to check it now.\n\t\t\t\t */\n\t\t\t\tif (event->events & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE))\n\t\t\t\t{\n\t\t\t\t\tint receiveStatus = PQconsumeInput(connection->pgConn);\n\t\t\t\t\tif (receiveStatus == 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* receive failed, done with this connection */\n\t\t\t\t\t\tconnectionIsReady = true;\n\t\t\t\t\t}\n\t\t\t\t\telse if (!PQisBusy(connection->pgConn))\n\t\t\t\t\t{\n\t\t\t\t\t\t/* result was received */\n\t\t\t\t\t\tconnectionIsReady = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (connectionIsReady)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * All pending connections are kept at the end of the allConnections\n\t\t\t\t\t * array and the connectionReady array matches the allConnections\n\t\t\t\t\t * array. The wait event set corresponds to the pending connections\n\t\t\t\t\t * subarray so we can get the index in the allConnections array by\n\t\t\t\t\t * taking the event index + the offset of the subarray.\n\t\t\t\t\t */\n\t\t\t\t\tconnectionIndex = event->pos + pendingConnectionsStartIndex;\n\n\t\t\t\t\tconnectionReady[connectionIndex] = true;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * When a connection is ready, we should build a new wait event\n\t\t\t\t\t * set that excludes this connection.\n\t\t\t\t\t */\n\t\t\t\t\trebuildWaitEventSet = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (cancellationReceived)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* move non-ready connections to the back of the array */\n\t\t\tfor (connectionIndex = pendingConnectionsStartIndex;\n\t\t\t\t connectionIndex < totalConnectionCount; connectionIndex++)\n\t\t\t{\n\t\t\t\tif (connectionReady[connectionIndex])\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Replace the ready connection with a connection from\n\t\t\t\t\t * the start of the pending connections subarray. This\n\t\t\t\t\t * may be the connection itself, in which case this is\n\t\t\t\t\t * a noop.\n\t\t\t\t\t */\n\t\t\t\t\tallConnections[connectionIndex] =\n\t\t\t\t\t\tallConnections[pendingConnectionsStartIndex];\n\n\t\t\t\t\t/* offset of the pending connections subarray is now 1 higher */\n\t\t\t\t\tpendingConnectionsStartIndex++;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * We've moved a pending connection into this position,\n\t\t\t\t\t * so we must reset the ready flag. Otherwise, we'd\n\t\t\t\t\t * falsely interpret it as ready in the next round.\n\t\t\t\t\t */\n\t\t\t\t\tconnectionReady[connectionIndex] = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (waitEventSet != NULL)\n\t\t{\n\t\t\tFreeWaitEventSet(waitEventSet);\n\t\t\twaitEventSet = NULL;\n\t\t}\n\n\t\tpfree(allConnections);\n\t\tpfree(events);\n\t\tpfree(connectionReady);\n\t}\n\tPG_CATCH();\n\t{\n\t\t/* make sure the epoll file descriptor is always closed */\n\t\tif (waitEventSet != NULL)\n\t\t{\n\t\t\tFreeWaitEventSet(waitEventSet);\n\t\t\twaitEventSet = NULL;\n\t\t}\n\n\t\tpfree(allConnections);\n\t\tpfree(events);\n\t\tpfree(connectionReady);\n\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n}\n\n\n/*\n * BuildWaitEventSet creates a WaitEventSet for the given array of connections\n * which can be used to wait for any of the sockets to become read-ready or\n * write-ready.\n */\nstatic WaitEventSet *\nBuildWaitEventSet(MultiConnection **allConnections, int totalConnectionCount,\n\t\t\t\t  int pendingConnectionsStartIndex)\n{\n\tint pendingConnectionCount = totalConnectionCount - pendingConnectionsStartIndex;\n\n\t/*\n\t * subtract 3 to make room for WL_POSTMASTER_DEATH, WL_LATCH_SET, and\n\t * pgwin32_signal_event.\n\t */\n\tif (pendingConnectionCount > FD_SETSIZE - 3)\n\t{\n\t\tpendingConnectionCount = FD_SETSIZE - 3;\n\t}\n\n\t/* allocate pending connections + 2 for the signal latch and postmaster death */\n\t/* (CreateWaitEventSet makes room for pgwin32_signal_event automatically) */\n\tWaitEventSet *waitEventSet = CreateWaitEventSet(WaitEventSetTracker_compat,\n\t\t\t\t\t\t\t\t\t\t\t\t\tpendingConnectionCount + 2);\n\n\tfor (int connectionIndex = 0; connectionIndex < pendingConnectionCount;\n\t\t connectionIndex++)\n\t{\n\t\tMultiConnection *connection = allConnections[pendingConnectionsStartIndex +\n\t\t\t\t\t\t\t\t\t\t\t\t\t connectionIndex];\n\t\tint sock = PQsocket(connection->pgConn);\n\n\t\t/*\n\t\t * Always start by polling for both readability (server sent bytes)\n\t\t * and writeability (server is ready to receive bytes).\n\t\t */\n\t\tint eventMask = WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE;\n\t\tint waitEventSetIndex =\n\t\t\tCitusAddWaitEventSetToSet(waitEventSet, eventMask, sock,\n\t\t\t\t\t\t\t\t\t  NULL, (void *) connection);\n\t\tif (waitEventSetIndex == WAIT_EVENT_SET_INDEX_FAILED)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t\terrmsg(\"connection establishment for node %s:%d failed\",\n\t\t\t\t\t\t\t\t   connection->hostname, connection->port),\n\t\t\t\t\t\t\terrhint(\"Check both the local and remote server logs for the \"\n\t\t\t\t\t\t\t\t\t\"connection establishment errors.\")));\n\t\t}\n\t}\n\n\t/*\n\t * Put the wait events for the signal latch and postmaster death at the end such that\n\t * event index + pendingConnectionsStartIndex = the connection index in the array.\n\t */\n\tAddWaitEventToSet(waitEventSet, WL_POSTMASTER_DEATH, PGINVALID_SOCKET, NULL, NULL);\n\tAddWaitEventToSet(waitEventSet, WL_LATCH_SET, PGINVALID_SOCKET, MyLatch, NULL);\n\n\treturn waitEventSet;\n}\n\n\n/*\n * SendCancelationRequest sends a cancelation request on the given connection.\n * Return value indicates whether the cancelation request was sent successfully.\n */\nbool\nSendCancelationRequest(MultiConnection *connection)\n{\n\tchar errorBuffer[ERROR_BUFFER_SIZE] = { 0 };\n\n\tPGcancel *cancelObject = PQgetCancel(connection->pgConn);\n\tif (cancelObject == NULL)\n\t{\n\t\t/* this can happen if connection is invalid */\n\t\treturn false;\n\t}\n\n\tbool cancelSent = PQcancel(cancelObject, errorBuffer, sizeof(errorBuffer));\n\tif (!cancelSent)\n\t{\n\t\tereport(WARNING, (errmsg(\"could not issue cancel request\"),\n\t\t\t\t\t\t  errdetail(\"Client error: %s\", errorBuffer)));\n\t}\n\n\tPQfreeCancel(cancelObject);\n\n\treturn cancelSent;\n}\n\n\n/*\n * EvaluateSingleQueryResult gets the query result from connection and returns\n * true if the query is executed successfully, false otherwise. A query result\n * or an error message is returned in queryResultString. The function requires\n * that the query returns a single column/single row result. It returns an\n * error otherwise.\n */\nbool\nEvaluateSingleQueryResult(MultiConnection *connection, PGresult *queryResult,\n\t\t\t\t\t\t  StringInfo queryResultString)\n{\n\tbool success = false;\n\n\tExecStatusType resultStatus = PQresultStatus(queryResult);\n\tif (resultStatus == PGRES_COMMAND_OK)\n\t{\n\t\tchar *commandStatus = PQcmdStatus(queryResult);\n\t\tappendStringInfo(queryResultString, \"%s\", commandStatus);\n\t\tsuccess = true;\n\t}\n\telse if (resultStatus == PGRES_TUPLES_OK)\n\t{\n\t\tint ntuples = PQntuples(queryResult);\n\t\tint nfields = PQnfields(queryResult);\n\n\t\t/* error if query returns more than 1 rows, or more than 1 fields */\n\t\tif (nfields != 1)\n\t\t{\n\t\t\tappendStringInfo(queryResultString,\n\t\t\t\t\t\t\t \"expected a single column in query target\");\n\t\t}\n\t\telse if (ntuples > 1)\n\t\t{\n\t\t\tappendStringInfo(queryResultString,\n\t\t\t\t\t\t\t \"expected a single row in query result\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint row = 0;\n\t\t\tint column = 0;\n\t\t\tif (!PQgetisnull(queryResult, row, column))\n\t\t\t{\n\t\t\t\tchar *queryResultValue = PQgetvalue(queryResult, row, column);\n\t\t\t\tappendStringInfo(queryResultString, \"%s\", queryResultValue);\n\t\t\t}\n\t\t\tsuccess = true;\n\t\t}\n\t}\n\telse\n\t{\n\t\tStoreErrorMessage(connection, queryResultString);\n\t}\n\n\treturn success;\n}\n\n\n/*\n * StoreErrorMessage gets the error message from connection and stores it\n * in queryResultString. It should be called only when error is present\n * otherwise it would return a default error message.\n */\nvoid\nStoreErrorMessage(MultiConnection *connection, StringInfo queryResultString)\n{\n\tchar *errorMessage = PQerrorMessage(connection->pgConn);\n\tif (errorMessage != NULL)\n\t{\n\t\t/* copy the error message to a writable memory */\n\t\terrorMessage = pnstrdup(errorMessage, strlen(errorMessage));\n\n\t\tchar *firstNewlineIndex = strchr(errorMessage, '\\n');\n\n\t\t/* trim the error message at the line break */\n\t\tif (firstNewlineIndex != NULL)\n\t\t{\n\t\t\t*firstNewlineIndex = '\\0';\n\t\t}\n\t}\n\telse\n\t{\n\t\t/* put a default error message if no error message is reported */\n\t\terrorMessage = \"An error occurred while running the query\";\n\t}\n\n\tappendStringInfo(queryResultString, \"%s\", errorMessage);\n}\n\n\n/*\n * IsSettingSafeToPropagate returns whether a SET LOCAL is safe to propagate.\n *\n * We exclude settings that are highly specific to the client or session and also\n * ban propagating the citus.propagate_set_commands setting (not for correctness,\n * more to avoid confusion).\n */\nbool\nIsSettingSafeToPropagate(const char *name)\n{\n\t/* if this list grows considerably we should switch to bsearch */\n\tconst char *skipSettings[] = {\n\t\t\"application_name\",\n\t\t\"citus.propagate_set_commands\",\n\t\t\"client_encoding\",\n\t\t\"exit_on_error\",\n\t\t\"max_stack_depth\"\n\t};\n\n\tfor (Index settingIndex = 0; settingIndex < lengthof(skipSettings); settingIndex++)\n\t{\n\t\tif (pg_strcasecmp(skipSettings[settingIndex], name) == 0)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/backend/distributed/connection/shared_connection_stats.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shared_connection_stats.c\n *   Keeps track of the number of connections to remote nodes across\n *   backends. The primary goal is to prevent excessive number of\n *   connections (typically > max_connections) to any worker node.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"access/hash.h\"\n#include \"access/htup_details.h\"\n#include \"catalog/pg_authid.h\"\n#include \"commands/dbcommands.h\"\n#include \"common/hashfn.h\"\n#include \"storage/ipc.h\"\n#include \"utils/builtins.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/cancel_utils.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/locally_reserved_shared_connections.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/shared_connection_stats.h\"\n#include \"distributed/time_constants.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/worker_manager.h\"\n\n\n#define REMOTE_CONNECTION_STATS_COLUMNS 4\n\n\n/*\n * The data structure used to store data in shared memory. This data structure is only\n * used for storing the lock. The actual statistics about the connections are stored\n * in the hashmap, which is allocated separately, as Postgres provides different APIs\n * for allocating hashmaps in the shared memory.\n */\ntypedef struct ConnectionStatsSharedData\n{\n\tint sharedConnectionHashTrancheId;\n\tchar *sharedConnectionHashTrancheName;\n\n\tLWLock sharedConnectionHashLock;\n\tConditionVariable waitersConditionVariable;\n} ConnectionStatsSharedData;\n\n\ntypedef struct SharedConnStatsHashKey\n{\n\t/*\n\t * We keep the entries in the shared memory even after master_update_node()\n\t * as there might be some cached connections to the old node.\n\t * That's why, we prefer to use \"hostname/port\" over nodeId.\n\t */\n\tchar hostname[MAX_NODE_LENGTH];\n\tint32 port;\n\n\t/*\n\t * Given that citus.shared_max_pool_size can be defined per database, we\n\t * should keep track of shared connections per database.\n\t */\n\tOid databaseOid;\n} SharedConnStatsHashKey;\n\n/* hash entry for per worker stats */\ntypedef struct SharedConnStatsHashEntry\n{\n\tSharedConnStatsHashKey key;\n\n\tint connectionCount;\n} SharedConnStatsHashEntry;\n\n\n/*\n * Controlled via a GUC, never access directly, use GetMaxSharedPoolSize().\n *  \"0\" means adjust MaxSharedPoolSize automatically by using MaxConnections.\n * \"-1\" means do not apply connection throttling\n * Anything else means use that number\n */\nint MaxSharedPoolSize = 0;\n\n/*\n * Controlled via a GUC, never access directly, use GetLocalSharedPoolSize().\n *  \"0\" means adjust LocalSharedPoolSize automatically by using MaxConnections.\n * \"-1\" means do not use any remote connections for local tasks\n * Anything else means use that number\n */\nint LocalSharedPoolSize = 0;\n\n/* number of connections reserved for Citus */\nint MaxClientConnections = ALLOW_ALL_EXTERNAL_CONNECTIONS;\n\n\n/* the following two structs are used for accessing shared memory */\nstatic HTAB *SharedConnStatsHash = NULL;\nstatic ConnectionStatsSharedData *ConnectionStatsSharedState = NULL;\n\n\nstatic shmem_startup_hook_type prev_shmem_startup_hook = NULL;\n\n\n/* local function declarations */\nstatic void StoreAllRemoteConnectionStats(Tuplestorestate *tupleStore, TupleDesc\n\t\t\t\t\t\t\t\t\t\t  tupleDescriptor);\nstatic void LockConnectionSharedMemory(LWLockMode lockMode);\nstatic void UnLockConnectionSharedMemory(void);\nstatic bool ShouldWaitForConnection(int currentConnectionCount);\nstatic uint32 SharedConnectionHashHash(const void *key, Size keysize);\nstatic int SharedConnectionHashCompare(const void *a, const void *b, Size keysize);\n\n\nPG_FUNCTION_INFO_V1(citus_remote_connection_stats);\n\n/*\n * citus_remote_connection_stats returns all the avaliable information about all\n * the remote connections (a.k.a., connections to remote nodes).\n */\nDatum\ncitus_remote_connection_stats(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tStoreAllRemoteConnectionStats(tupleStore, tupleDescriptor);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * StoreAllRemoteConnectionStats gets connections established from the current node\n * and inserts them into the given tuplestore.\n *\n * We don't need to enforce any access privileges as the number of backends\n * on any node is already visible on pg_stat_activity to all users.\n */\nstatic void\nStoreAllRemoteConnectionStats(Tuplestorestate *tupleStore, TupleDesc tupleDescriptor)\n{\n\tDatum values[REMOTE_CONNECTION_STATS_COLUMNS];\n\tbool isNulls[REMOTE_CONNECTION_STATS_COLUMNS];\n\n\t/* we're reading all shared connections, prevent any changes */\n\tLockConnectionSharedMemory(LW_SHARED);\n\n\tHASH_SEQ_STATUS status;\n\tSharedConnStatsHashEntry *connectionEntry = NULL;\n\n\thash_seq_init(&status, SharedConnStatsHash);\n\twhile ((connectionEntry = (SharedConnStatsHashEntry *) hash_seq_search(&status)) != 0)\n\t{\n\t\t/* get ready for the next tuple */\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(isNulls, false, sizeof(isNulls));\n\n\t\tchar *databaseName = get_database_name(connectionEntry->key.databaseOid);\n\t\tif (databaseName == NULL)\n\t\t{\n\t\t\t/* database might have been dropped */\n\t\t\tcontinue;\n\t\t}\n\n\t\tvalues[0] = PointerGetDatum(cstring_to_text(connectionEntry->key.hostname));\n\t\tvalues[1] = Int32GetDatum(connectionEntry->key.port);\n\t\tvalues[2] = PointerGetDatum(cstring_to_text(databaseName));\n\t\tvalues[3] = Int32GetDatum(connectionEntry->connectionCount);\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);\n\t}\n\n\tUnLockConnectionSharedMemory();\n}\n\n\n/*\n * GetMaxClientConnections returns the value of citus.max_client_connections,\n * or max_connections when it is -1 or when connecting as superuser.\n *\n * The latter is done because citus.max_client_connections does not apply to\n * superuser.\n */\nint\nGetMaxClientConnections(void)\n{\n\tif (MaxClientConnections == ALLOW_ALL_EXTERNAL_CONNECTIONS || superuser())\n\t{\n\t\treturn MaxConnections;\n\t}\n\n\treturn MaxClientConnections;\n}\n\n\n/*\n * GetMaxSharedPoolSize is a wrapper around MaxSharedPoolSize which is controlled\n * via a GUC.\n *  \"0\" means adjust MaxSharedPoolSize automatically by using MaxConnections\n * \"-1\" means do not apply connection throttling\n * Anything else means use that number\n */\nint\nGetMaxSharedPoolSize(void)\n{\n\tif (MaxSharedPoolSize == ADJUST_POOLSIZE_AUTOMATICALLY)\n\t{\n\t\treturn GetMaxClientConnections();\n\t}\n\n\treturn MaxSharedPoolSize;\n}\n\n\n/*\n * GetLocalSharedPoolSize is a wrapper around LocalSharedPoolSize which is\n * controlled via a GUC.\n *  \"0\" means adjust MaxSharedPoolSize automatically by using MaxConnections\n * \"-1\" means do not use any remote connections for local tasks\n * Anything else means use that number\n */\nint\nGetLocalSharedPoolSize(void)\n{\n\tif (LocalSharedPoolSize == ADJUST_POOLSIZE_AUTOMATICALLY)\n\t{\n\t\treturn GetMaxClientConnections() * 0.5;\n\t}\n\n\treturn LocalSharedPoolSize;\n}\n\n\n/*\n * WaitLoopForSharedConnection tries to increment the shared connection\n * counter for the given hostname/port and the current database in\n * SharedConnStatsHash.\n *\n * The function implements a retry mechanism via a condition variable.\n */\nvoid\nWaitLoopForSharedConnection(const char *hostname, int port)\n{\n\twhile (!TryToIncrementSharedConnectionCounter(hostname, port))\n\t{\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tWaitForSharedConnection();\n\t}\n\n\tConditionVariableCancelSleep();\n}\n\n\n/*\n * TryToIncrementSharedConnectionCounter tries to increment the shared\n * connection counter for the given nodeId and the current database in\n * SharedConnStatsHash.\n *\n * If the function returns true, the caller is allowed (and expected)\n * to establish a new connection to the given node. Else, the caller\n * is not allowed to establish a new connection.\n */\nbool\nTryToIncrementSharedConnectionCounter(const char *hostname, int port)\n{\n\tif (GetMaxSharedPoolSize() == DISABLE_CONNECTION_THROTTLING)\n\t{\n\t\t/* connection throttling disabled */\n\t\treturn true;\n\t}\n\n\tbool counterIncremented = false;\n\tSharedConnStatsHashKey connKey;\n\n\tstrlcpy(connKey.hostname, hostname, MAX_NODE_LENGTH);\n\tif (strlen(hostname) > MAX_NODE_LENGTH)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"hostname exceeds the maximum length of %d\",\n\t\t\t\t\t\t\t   MAX_NODE_LENGTH)));\n\t}\n\n\t/*\n\t * The local session might already have some reserved connections to the given\n\t * node. In that case, we don't need to go through the shared memory.\n\t */\n\tOid userId = GetUserId();\n\tif (CanUseReservedConnection(hostname, port, userId, MyDatabaseId))\n\t{\n\t\tMarkReservedConnectionUsed(hostname, port, userId, MyDatabaseId);\n\n\t\treturn true;\n\t}\n\n\tconnKey.port = port;\n\tconnKey.databaseOid = MyDatabaseId;\n\n\t/*\n\t * Handle adaptive connection management for the local node slightly different\n\t * as local node can failover to local execution.\n\t */\n\tbool connectionToLocalNode = false;\n\tint activeBackendCount = 0;\n\tWorkerNode *workerNode = FindWorkerNode(hostname, port);\n\tif (workerNode)\n\t{\n\t\tconnectionToLocalNode = (workerNode->groupId == GetLocalGroupId());\n\t\tif (connectionToLocalNode &&\n\t\t\tGetLocalSharedPoolSize() == DISABLE_REMOTE_CONNECTIONS_FOR_LOCAL_QUERIES)\n\t\t{\n\t\t\t/*\n\t\t\t * This early return is required as LocalNodeParallelExecutionFactor\n\t\t\t * is ignored for the first connection below. This check makes the\n\t\t\t * user experience is more accurate and also makes it easy for\n\t\t\t * having regression tests which emulates the local node adaptive\n\t\t\t * connection management.\n\t\t\t */\n\t\t\treturn false;\n\t\t}\n\n\t\tactiveBackendCount = GetExternalClientBackendCount();\n\t}\n\n\tLockConnectionSharedMemory(LW_EXCLUSIVE);\n\n\t/*\n\t * As the hash map is allocated in shared memory, it doesn't rely on palloc for\n\t * memory allocation, so we could get NULL via HASH_ENTER_NULL when there is no\n\t * space in the shared memory. That's why we prefer continuing the execution\n\t * instead of throwing an error.\n\t */\n\tbool entryFound = false;\n\tSharedConnStatsHashEntry *connectionEntry =\n\t\thash_search(SharedConnStatsHash, &connKey, HASH_ENTER_NULL, &entryFound);\n\n\t/*\n\t * It is possible to throw an error at this point, but that doesn't help us in anyway.\n\t * Instead, we try our best, let the connection establishment continue by-passing the\n\t * connection throttling.\n\t */\n\tif (!connectionEntry)\n\t{\n\t\tUnLockConnectionSharedMemory();\n\t\treturn true;\n\t}\n\n\tif (!entryFound)\n\t{\n\t\t/* we successfully allocated the entry for the first time, so initialize it */\n\t\tconnectionEntry->connectionCount = 1;\n\n\t\tcounterIncremented = true;\n\t}\n\telse if (connectionToLocalNode)\n\t{\n\t\t/*\n\t\t * For local nodes, solely relying on citus.max_shared_pool_size or\n\t\t * max_connections might not be sufficient. The former gives us\n\t\t * a preview of the future (e.g., we let the new connections to establish,\n\t\t * but they are not established yet). The latter gives us the close to\n\t\t * precise view of the past (e.g., the active number of client backends).\n\t\t *\n\t\t * Overall, we want to limit both of the metrics. The former limit typically\n\t\t * kicks in under regular loads, where the load of the database increases in\n\t\t * a reasonable pace. The latter limit typically kicks in when the database\n\t\t * is issued lots of concurrent sessions at the same time, such as benchmarks.\n\t\t */\n\t\tif (activeBackendCount + 1 > GetLocalSharedPoolSize())\n\t\t{\n\t\t\tcounterIncremented = false;\n\t\t}\n\t\telse if (connectionEntry->connectionCount + 1 > GetLocalSharedPoolSize())\n\t\t{\n\t\t\tcounterIncremented = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconnectionEntry->connectionCount++;\n\t\t\tcounterIncremented = true;\n\t\t}\n\t}\n\telse if (connectionEntry->connectionCount + 1 > GetMaxSharedPoolSize())\n\t{\n\t\t/* there is no space left for this connection */\n\t\tcounterIncremented = false;\n\t}\n\telse\n\t{\n\t\tconnectionEntry->connectionCount++;\n\t\tcounterIncremented = true;\n\t}\n\n\tUnLockConnectionSharedMemory();\n\n\treturn counterIncremented;\n}\n\n\n/*\n * IncrementSharedConnectionCounter increments the shared counter\n * for the given hostname and port.\n */\nvoid\nIncrementSharedConnectionCounter(const char *hostname, int port)\n{\n\tSharedConnStatsHashKey connKey;\n\n\tif (MaxSharedPoolSize == DISABLE_CONNECTION_THROTTLING)\n\t{\n\t\t/* connection throttling disabled */\n\t\treturn;\n\t}\n\n\tstrlcpy(connKey.hostname, hostname, MAX_NODE_LENGTH);\n\tif (strlen(hostname) > MAX_NODE_LENGTH)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"hostname exceeds the maximum length of %d\",\n\t\t\t\t\t\t\t   MAX_NODE_LENGTH)));\n\t}\n\n\tconnKey.port = port;\n\tconnKey.databaseOid = MyDatabaseId;\n\n\tLockConnectionSharedMemory(LW_EXCLUSIVE);\n\n\t/*\n\t * As the hash map is allocated in shared memory, it doesn't rely on palloc for\n\t * memory allocation, so we could get NULL via HASH_ENTER_NULL. That's why we prefer\n\t * continuing the execution instead of throwing an error.\n\t */\n\tbool entryFound = false;\n\tSharedConnStatsHashEntry *connectionEntry =\n\t\thash_search(SharedConnStatsHash, &connKey, HASH_ENTER_NULL, &entryFound);\n\n\t/*\n\t * It is possible to throw an error at this point, but that doesn't help us in anyway.\n\t * Instead, we try our best, let the connection establishment continue by-passing the\n\t * connection throttling.\n\t */\n\tif (!connectionEntry)\n\t{\n\t\tUnLockConnectionSharedMemory();\n\n\t\tereport(DEBUG4, (errmsg(\"No entry found for node %s:%d while incrementing \"\n\t\t\t\t\t\t\t\t\"connection counter\", hostname, port)));\n\n\t\treturn;\n\t}\n\n\tif (!entryFound)\n\t{\n\t\t/* we successfully allocated the entry for the first time, so initialize it */\n\t\tconnectionEntry->connectionCount = 0;\n\t}\n\n\tconnectionEntry->connectionCount += 1;\n\n\tUnLockConnectionSharedMemory();\n}\n\n\n/*\n * DecrementSharedConnectionCounter decrements the shared counter\n * for the given hostname and port for the given count.\n */\nvoid\nDecrementSharedConnectionCounter(const char *hostname, int port)\n{\n\tSharedConnStatsHashKey connKey;\n\n\t/*\n\t * Do not call GetMaxSharedPoolSize() here, since it may read from\n\t * the catalog and we may be in the process exit handler.\n\t */\n\tif (MaxSharedPoolSize == DISABLE_CONNECTION_THROTTLING)\n\t{\n\t\t/* connection throttling disabled */\n\t\treturn;\n\t}\n\n\tstrlcpy(connKey.hostname, hostname, MAX_NODE_LENGTH);\n\tif (strlen(hostname) > MAX_NODE_LENGTH)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"hostname exceeds the maximum length of %d\",\n\t\t\t\t\t\t\t   MAX_NODE_LENGTH)));\n\t}\n\n\tconnKey.port = port;\n\tconnKey.databaseOid = MyDatabaseId;\n\n\tLockConnectionSharedMemory(LW_EXCLUSIVE);\n\n\tbool entryFound = false;\n\tSharedConnStatsHashEntry *connectionEntry =\n\t\thash_search(SharedConnStatsHash, &connKey, HASH_FIND, &entryFound);\n\n\t/* this worker node is removed or updated, no need to care */\n\tif (!entryFound)\n\t{\n\t\tUnLockConnectionSharedMemory();\n\n\t\t/* wake up any waiters in case any backend is waiting for this node */\n\t\tWakeupWaiterBackendsForSharedConnection();\n\n\t\tereport(DEBUG4, (errmsg(\"No entry found for node %s:%d while decrementing \"\n\t\t\t\t\t\t\t\t\"connection counter\", hostname, port)));\n\n\t\treturn;\n\t}\n\n\t/* we should never go below 0 */\n\tAssert(connectionEntry->connectionCount > 0);\n\n\tconnectionEntry->connectionCount -= 1;\n\n\tif (connectionEntry->connectionCount == 0)\n\t{\n\t\t/*\n\t\t * We don't have to remove at this point as the node might be still active\n\t\t * and will have new connections open to it. Still, this seems like a convenient\n\t\t * place to remove the entry, as connectionCount == 0 implies that the server is\n\t\t * not busy, and given the default value of MaxCachedConnectionsPerWorker = 1,\n\t\t * we're unlikely to trigger this often.\n\t\t */\n\t\thash_search(SharedConnStatsHash, &connKey, HASH_REMOVE, &entryFound);\n\t}\n\n\tUnLockConnectionSharedMemory();\n\n\tWakeupWaiterBackendsForSharedConnection();\n}\n\n\n/*\n * LockConnectionSharedMemory is a utility function that should be used when\n * accessing to the SharedConnStatsHash, which is in the shared memory.\n */\nstatic void\nLockConnectionSharedMemory(LWLockMode lockMode)\n{\n\tLWLockAcquire(&ConnectionStatsSharedState->sharedConnectionHashLock, lockMode);\n}\n\n\n/*\n * UnLockConnectionSharedMemory is a utility function that should be used after\n * LockConnectionSharedMemory().\n */\nstatic void\nUnLockConnectionSharedMemory(void)\n{\n\tLWLockRelease(&ConnectionStatsSharedState->sharedConnectionHashLock);\n}\n\n\n/*\n * WakeupWaiterBackendsForSharedConnection is a wrapper around the condition variable\n * broadcast operation.\n *\n * We use a single condition variable, for all worker nodes, to implement the connection\n * throttling mechanism. Combination of all the backends are allowed to establish\n * MaxSharedPoolSize number of connections per worker node. If a backend requires a\n * non-optional connection (see WAIT_FOR_CONNECTION for details), it is not allowed\n * to establish it immediately if the total connections are equal to MaxSharedPoolSize.\n * Instead, the backend waits on the condition variable. When any other backend\n * terminates an existing connection to any remote node, this function is called.\n * The main goal is to trigger all waiting backends to try getting a connection slot\n * in MaxSharedPoolSize. The ones which can get connection slot are allowed to continue\n * with the connection establishments. Others should wait another backend to call\n * this function.\n */\nvoid\nWakeupWaiterBackendsForSharedConnection(void)\n{\n\tConditionVariableBroadcast(&ConnectionStatsSharedState->waitersConditionVariable);\n}\n\n\n/*\n * WaitForSharedConnection is a wrapper around the condition variable sleep operation.\n *\n * For the details of the use of the condition variable, see\n * WakeupWaiterBackendsForSharedConnection().\n */\nvoid\nWaitForSharedConnection(void)\n{\n\tConditionVariableSleep(&ConnectionStatsSharedState->waitersConditionVariable,\n\t\t\t\t\t\t   PG_WAIT_EXTENSION);\n}\n\n\n/*\n * InitializeSharedConnectionStats requests the necessary shared memory\n * from Postgres and sets up the shared memory startup hook.\n */\nvoid\nInitializeSharedConnectionStats(void)\n{\n\tprev_shmem_startup_hook = shmem_startup_hook;\n\tshmem_startup_hook = SharedConnectionStatsShmemInit;\n}\n\n\n/*\n * SharedConnectionStatsShmemSize returns the size that should be allocated\n * on the shared memory for shared connection stats.\n */\nsize_t\nSharedConnectionStatsShmemSize(void)\n{\n\tSize size = 0;\n\n\tsize = add_size(size, sizeof(ConnectionStatsSharedData));\n\n\tSize hashSize = hash_estimate_size(MaxWorkerNodesTracked,\n\t\t\t\t\t\t\t\t\t   sizeof(SharedConnStatsHashEntry));\n\n\tsize = add_size(size, hashSize);\n\n\treturn size;\n}\n\n\n/*\n * SharedConnectionStatsShmemInit initializes the shared memory used\n * for keeping track of connection stats across backends.\n */\nvoid\nSharedConnectionStatsShmemInit(void)\n{\n\tbool alreadyInitialized = false;\n\tHASHCTL info;\n\n\t/* create (hostname, port, database) -> [counter] */\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(SharedConnStatsHashKey);\n\tinfo.entrysize = sizeof(SharedConnStatsHashEntry);\n\tinfo.hash = SharedConnectionHashHash;\n\tinfo.match = SharedConnectionHashCompare;\n\tuint32 hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);\n\n\t/*\n\t * Currently the lock isn't required because allocation only happens at\n\t * startup in postmaster, but it doesn't hurt, and makes things more\n\t * consistent with other extensions.\n\t */\n\tLWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);\n\n\tConnectionStatsSharedState =\n\t\t(ConnectionStatsSharedData *) ShmemInitStruct(\n\t\t\t\"Shared Connection Stats Data\",\n\t\t\tsizeof(ConnectionStatsSharedData),\n\t\t\t&alreadyInitialized);\n\n\tif (!alreadyInitialized)\n\t{\n\t\tConnectionStatsSharedState->sharedConnectionHashTrancheId = LWLockNewTrancheId();\n\t\tConnectionStatsSharedState->sharedConnectionHashTrancheName =\n\t\t\t\"Shared Connection Tracking Hash Tranche\";\n\t\tLWLockRegisterTranche(\n\t\t\tConnectionStatsSharedState->sharedConnectionHashTrancheId,\n\t\t\tConnectionStatsSharedState->sharedConnectionHashTrancheName);\n\n\t\tLWLockInitialize(&ConnectionStatsSharedState->sharedConnectionHashLock,\n\t\t\t\t\t\t ConnectionStatsSharedState->sharedConnectionHashTrancheId);\n\n\t\tConditionVariableInit(&ConnectionStatsSharedState->waitersConditionVariable);\n\t}\n\n\t/* allocate hash table */\n\tSharedConnStatsHash =\n\t\tShmemInitHash(\"Shared Conn. Stats Hash\", MaxWorkerNodesTracked,\n\t\t\t\t\t  MaxWorkerNodesTracked, &info, hashFlags);\n\n\tLWLockRelease(AddinShmemInitLock);\n\n\tAssert(SharedConnStatsHash != NULL);\n\tAssert(ConnectionStatsSharedState->sharedConnectionHashTrancheId != 0);\n\n\tif (prev_shmem_startup_hook != NULL)\n\t{\n\t\tprev_shmem_startup_hook();\n\t}\n}\n\n\n/*\n * AdaptiveConnectionManagementFlag returns the appropriate connection flag,\n * regarding the adaptive connection management, based on the given\n * activeConnectionCount to remote nodes.\n *\n * This function should only be called if the code-path is capable of handling\n * optional connections.\n */\nint\nAdaptiveConnectionManagementFlag(bool connectToLocalNode, int activeConnectionCount)\n{\n\tif (UseConnectionPerPlacement())\n\t{\n\t\t/*\n\t\t * User wants one connection per placement, so no throttling is desired\n\t\t * and we do not set any flags.\n\t\t *\n\t\t * The primary reason for this is that allowing multiple backends to use\n\t\t * connection per placement could lead to unresolved self deadlocks. In other\n\t\t * words, each backend may stuck waiting for other backends to get a slot\n\t\t * in the shared connection counters.\n\t\t */\n\t\treturn 0;\n\t}\n\telse if (connectToLocalNode)\n\t{\n\t\t/*\n\t\t * Connection to local node is always optional because the executor is capable\n\t\t * of falling back to local execution.\n\t\t */\n\t\treturn OPTIONAL_CONNECTION;\n\t}\n\telse if (ShouldWaitForConnection(activeConnectionCount))\n\t{\n\t\t/*\n\t\t * We need this connection to finish the execution. If it is not\n\t\t * available based on the current number of connections to the worker\n\t\t * then wait for it.\n\t\t */\n\t\treturn WAIT_FOR_CONNECTION;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * The execution can be finished the execution with a single connection,\n\t\t * remaining are optional. If the execution can get more connections,\n\t\t * it can increase the parallelism.\n\t\t */\n\t\treturn OPTIONAL_CONNECTION;\n\t}\n}\n\n\n/*\n * ShouldWaitForConnection returns true if the workerPool should wait to\n * get the next connection until one slot is empty within\n * citus.max_shared_pool_size on the worker. Note that, if there is an\n * empty slot, the connection will not wait anyway.\n */\nstatic bool\nShouldWaitForConnection(int currentConnectionCount)\n{\n\tif (currentConnectionCount == 0)\n\t{\n\t\t/*\n\t\t * We definitely need at least 1 connection to finish the execution.\n\t\t * All single shard queries hit here with the default settings.\n\t\t */\n\t\treturn true;\n\t}\n\n\tif (currentConnectionCount < MaxCachedConnectionsPerWorker)\n\t{\n\t\t/*\n\t\t * Until this session caches MaxCachedConnectionsPerWorker connections,\n\t\t * this might lead some optional connections to be considered as non-optional\n\t\t * when MaxCachedConnectionsPerWorker > 1.\n\t\t *\n\t\t * However, once the session caches MaxCachedConnectionsPerWorker (which is\n\t\t * the second transaction executed in the session), Citus would utilize the\n\t\t * cached connections as much as possible.\n\t\t */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\nstatic uint32\nSharedConnectionHashHash(const void *key, Size keysize)\n{\n\tSharedConnStatsHashKey *entry = (SharedConnStatsHashKey *) key;\n\n\tuint32 hash = string_hash(entry->hostname, NAMEDATALEN);\n\thash = hash_combine(hash, hash_uint32(entry->port));\n\thash = hash_combine(hash, hash_uint32(entry->databaseOid));\n\n\treturn hash;\n}\n\n\nstatic int\nSharedConnectionHashCompare(const void *a, const void *b, Size keysize)\n{\n\tSharedConnStatsHashKey *ca = (SharedConnStatsHashKey *) a;\n\tSharedConnStatsHashKey *cb = (SharedConnStatsHashKey *) b;\n\n\tif (strncmp(ca->hostname, cb->hostname, MAX_NODE_LENGTH) != 0 ||\n\t\tca->port != cb->port ||\n\t\tca->databaseOid != cb->databaseOid)\n\t{\n\t\treturn 1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/connection/worker_log_messages.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_log_messages.c\n *   Logic for handling log messages from workers.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"utils/elog.h\"\n#include \"utils/memutils.h\"   /* for TopTransactionContext */\n\n#include \"distributed/connection_management.h\"\n#include \"distributed/error_codes.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/worker_log_messages.h\"\n\n\n/*\n * WorkerMinMessages reflects the value of the citus.worker_min_messages setting which\n * control the minimum log level of messages from the worker that are propagated to the\n * client and the log on the coordinator.\n */\nint WorkerMinMessages = NOTICE;\n\n/*\n * PreserveWorkerMessageLogLevel specifies whether to propagate messages from workers\n * to the client and the log on the coordinator with their original log level. When\n * false, messages are propagated using DEBUG1.\n *\n * This flag used to suppress redundant notices in some commands (e.g. VACUUM, DROP\n * TABLE).\n */\nstatic bool PreserveWorkerMessageLogLevel = false;\n\n/*\n * WorkerErrorIndication can contain a warning that arrives to use from one session, but occurred\n * because another session in the same distributed transaction threw an error. We store\n * this warning in case we do not get an error, in which case the warning should have\n * been an error (and usually indicates a bug).\n */\nDeferredErrorMessage *WorkerErrorIndication = NULL;\n\n/* list of log level names we might see from the worker */\nstatic const char *LogLevelNames[] = {\n\t\"DEBUG\",\n\t\"NOTICE\",\n\t\"INFO\",\n\t\"WARNING\",\n\t\"ERROR\",\n\t\"FATAL\",\n\t\"PANIC\",\n\tNULL\n};\n\n/* postgres log level values corresponding to LogLevelNames */\nstatic const int LogLevels[] = {\n\tDEBUG1,\n\tNOTICE,\n\tINFO,\n\tWARNING,\n\tERROR,\n\tFATAL,\n\tPANIC\n};\n\n\nstatic void DefaultCitusNoticeReceiver(void *arg, const PGresult *result);\nstatic int LogLevelNameToLogLevel(char *levelName);\nstatic char * TrimLogLevel(const char *message);\n\n\n/*\n * SetCitusNoticeReceiver sets the NoticeReceiver to DefaultCitusNoticeReceivere\n */\nvoid\nSetCitusNoticeReceiver(MultiConnection *connection)\n{\n\tPQsetNoticeReceiver(connection->pgConn, DefaultCitusNoticeReceiver,\n\t\t\t\t\t\tconnection);\n}\n\n\n/*\n * EnableWorkerMessagePropagation indicates that we want to propagate messages\n * from workers to the client using the same log level.\n */\nvoid\nEnableWorkerMessagePropagation(void)\n{\n\tPreserveWorkerMessageLogLevel = true;\n}\n\n\n/*\n * DisableWorkerMessagePropagation indiciates that we want all messages from the\n * workers to only be sent to the client as debug messages.\n */\nvoid\nDisableWorkerMessagePropagation(void)\n{\n\tPreserveWorkerMessageLogLevel = false;\n}\n\n\n/*\n * DefaultCitusNoticeReceiver is used to redirect worker notices\n * from logfile to console.\n */\nstatic void\nDefaultCitusNoticeReceiver(void *arg, const PGresult *result)\n{\n\tMultiConnection *connection = (MultiConnection *) arg;\n\tchar *nodeName = connection->hostname;\n\tuint32 nodePort = connection->port;\n\tchar *message = PQresultErrorMessage(result);\n\tchar *trimmedMessage = TrimLogLevel(message);\n\tchar *levelName = PQresultErrorField(result, PG_DIAG_SEVERITY);\n\tint logLevel = LogLevelNameToLogLevel(levelName);\n\tint sqlState = ERRCODE_INTERNAL_ERROR;\n\tchar *sqlStateString = PQresultErrorField(result, PG_DIAG_SQLSTATE);\n\n\tif (sqlStateString != NULL)\n\t{\n\t\tsqlState = MAKE_SQLSTATE(sqlStateString[0],\n\t\t\t\t\t\t\t\t sqlStateString[1],\n\t\t\t\t\t\t\t\t sqlStateString[2],\n\t\t\t\t\t\t\t\t sqlStateString[3],\n\t\t\t\t\t\t\t\t sqlStateString[4]);\n\t}\n\n\t/*\n\t * When read_intermediate_result cannot find a file it might mean that the\n\t * transaction that created the file already deleted it because it aborted.\n\t * That's an expected situation, unless there is no actual error. We\n\t * therefore store a DeferredError and raise it if we reach the end of\n\t * execution without errors.\n\t */\n\tif (sqlState == ERRCODE_CITUS_INTERMEDIATE_RESULT_NOT_FOUND && logLevel == WARNING)\n\t{\n\t\tif (WorkerErrorIndication == NULL)\n\t\t{\n\t\t\t/* we'll at most need this for the lifetime of the transaction */\n\t\t\tMemoryContext oldContext = MemoryContextSwitchTo(TopTransactionContext);\n\n\t\t\tWorkerErrorIndication = DeferredError(sqlState, pstrdup(trimmedMessage),\n\t\t\t\t\t\t\t\t\t\t\t\t  NULL, NULL);\n\n\t\t\tMemoryContextSwitchTo(oldContext);\n\t\t}\n\n\t\t/* if we get the error we're expecting, the user does not need to know */\n\t\tlogLevel = DEBUG4;\n\t}\n\n\tif (logLevel < WorkerMinMessages || WorkerMinMessages == CITUS_LOG_LEVEL_OFF)\n\t{\n\t\t/* user does not want to see message */\n\t\treturn;\n\t}\n\n\tif (!PreserveWorkerMessageLogLevel)\n\t{\n\t\t/*\n\t\t * We sometimes want to suppress notices (e.g. DROP TABLE cascading),\n\t\t * since the user already gets the relevant notices for the distributed\n\t\t * table. In that case, we change the log level to DEBUG1.\n\t\t */\n\t\tlogLevel = DEBUG1;\n\t}\n\n\tereport(logLevel,\n\t\t\t(errcode(sqlState),\n\t\t\t errmsg(\"%s\", trimmedMessage),\n\t\t\t errdetail(\"from %s:%d\", nodeName, nodePort)));\n}\n\n\n/*\n * TrimLogLevel returns a copy of the string with the leading log level\n * and spaces removed such as\n *      From:\n *          INFO:  \"normal2_102070\": scanned 0 of 0 pages...\n *      To:\n *          \"normal2_102070\": scanned 0 of 0 pages...\n */\nstatic char *\nTrimLogLevel(const char *message)\n{\n\tchar *chompedMessage = pchomp(message);\n\n\tsize_t n = 0;\n\twhile (n < strlen(chompedMessage) && chompedMessage[n] != ':')\n\t{\n\t\tn++;\n\t}\n\n\tdo {\n\t\tn++;\n\t} while (n < strlen(chompedMessage) && chompedMessage[n] == ' ');\n\n\treturn chompedMessage + n;\n}\n\n\n/*\n * LogLevelNameToLogLevel translates the prefix of Postgres log messages\n * back to a native log level.\n */\nstatic int\nLogLevelNameToLogLevel(char *levelName)\n{\n\tint levelIndex = 0;\n\n\twhile (LogLevelNames[levelIndex] != NULL)\n\t{\n\t\tif (strcmp(levelName, LogLevelNames[levelIndex]) == 0)\n\t\t{\n\t\t\treturn LogLevels[levelIndex];\n\t\t}\n\n\t\tlevelIndex++;\n\t}\n\n\treturn DEBUG1;\n}\n\n\n/*\n * ErrorIfWorkerErrorIndicationReceived throws the deferred error in\n * WorkerErrorIndication, if any.\n *\n * A fatal warning arrives to us as a WARNING in one session, that is triggered\n * by an ERROR in another session in the same distributed transaction. We therefore\n * do not expect to throw it, unless there is a bug in Citus.\n */\nvoid\nErrorIfWorkerErrorIndicationReceived(void)\n{\n\tif (WorkerErrorIndication != NULL)\n\t{\n\t\tRaiseDeferredError(WorkerErrorIndication, ERROR);\n\t}\n}\n\n\n/*\n * ResetWorkerErrorIndication resets the fatal warning if one was received.\n */\nvoid\nResetWorkerErrorIndication(void)\n{\n\tWorkerErrorIndication = NULL;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/citus_deparseutils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_deparseutils.c\n *\n *   This file contains common functions used for deparsing PostgreSQL\n *   statements to their equivalent SQL representation.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"commands/defrem.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/rel.h\"\n#include \"utils/relcache.h\"\n#include \"utils/syscache.h\"\n#include \"utils/typcache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/deparser.h\"\n\n\n/**\n * DefElemOptionToStatement converts a DefElem option to a SQL statement and\n * appends it to the given StringInfo buffer.\n *\n * @param buf The StringInfo buffer to append the SQL statement to.\n * @param option The DefElem option to convert to a SQL statement.\n * @param optionFormats The option format specification to use for the conversion.\n * @param optionFormatsLen The number of option formats in the opt_formats array.\n */\nvoid\nDefElemOptionToStatement(StringInfo buf, DefElem *option,\n\t\t\t\t\t\t const DefElemOptionFormat *optionFormats,\n\t\t\t\t\t\t int optionFormatsLen)\n{\n\tconst char *name = option->defname;\n\tint i;\n\n\tfor (i = 0; i < optionFormatsLen; i++)\n\t{\n\t\tif (strcmp(name, optionFormats[i].name) == 0)\n\t\t{\n\t\t\tswitch (optionFormats[i].type)\n\t\t\t{\n\t\t\t\tcase OPTION_FORMAT_STRING:\n\t\t\t\t{\n\t\t\t\t\tchar *value = defGetString(option);\n\t\t\t\t\tappendStringInfo(buf, optionFormats[i].format, quote_identifier(\n\t\t\t\t\t\t\t\t\t\t value));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase OPTION_FORMAT_INTEGER:\n\t\t\t\t{\n\t\t\t\t\tint32 value = defGetInt32(option);\n\t\t\t\t\tappendStringInfo(buf, optionFormats[i].format, value);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase OPTION_FORMAT_BOOLEAN:\n\t\t\t\t{\n\t\t\t\t\tbool value = defGetBoolean(option);\n\t\t\t\t\tappendStringInfo(buf, optionFormats[i].format, value ? \"true\" :\n\t\t\t\t\t\t\t\t\t \"false\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase OPTION_FORMAT_LITERAL_CSTR:\n\t\t\t\t{\n\t\t\t\t\tchar *value = defGetString(option);\n\t\t\t\t\tappendStringInfo(buf, optionFormats[i].format, quote_literal_cstr(\n\t\t\t\t\t\t\t\t\t\t value));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\telog(ERROR, \"unrecognized option type: %d\", optionFormats[i].type);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/citus_grantutils.c",
    "content": "#include \"postgres.h\"\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparser.h\"\n\n/*\n * Append the 'WITH GRANT OPTION' clause to the given buffer if the given\n * statement is a 'GRANT' statement and the grant option is specified.\n */\nvoid\nAppendWithGrantOption(StringInfo buf, GrantStmt *stmt)\n{\n\tif (stmt->is_grant && stmt->grant_option)\n\t{\n\t\tappendStringInfo(buf, \" WITH GRANT OPTION\");\n\t}\n}\n\n\n/*\n * Append the 'GRANT OPTION FOR' clause to the given buffer if the given\n * statement is a 'REVOKE' statement and the grant option is specified.\n */\nvoid\nAppendGrantOptionFor(StringInfo buf, GrantStmt *stmt)\n{\n\tif (!stmt->is_grant && stmt->grant_option)\n\t{\n\t\tappendStringInfo(buf, \"GRANT OPTION FOR \");\n\t}\n}\n\n\n/*\n * Append the 'RESTRICT' or 'CASCADE' clause to the given buffer if the given\n * statement is a 'REVOKE' statement and the behavior is specified.\n */\nvoid\nAppendGrantRestrictAndCascadeForRoleSpec(StringInfo buf, DropBehavior behavior, bool\n\t\t\t\t\t\t\t\t\t\t isGrant)\n{\n\tif (!isGrant)\n\t{\n\t\tif (behavior == DROP_RESTRICT)\n\t\t{\n\t\t\tappendStringInfo(buf, \" RESTRICT\");\n\t\t}\n\t\telse if (behavior == DROP_CASCADE)\n\t\t{\n\t\t\tappendStringInfo(buf, \" CASCADE\");\n\t\t}\n\t}\n}\n\n\n/*\n * Append the 'RESTRICT' or 'CASCADE' clause to the given buffer using 'GrantStmt',\n * if the given statement is a 'REVOKE' statement and the behavior is specified.\n */\nvoid\nAppendGrantRestrictAndCascade(StringInfo buf, GrantStmt *stmt)\n{\n\tAppendGrantRestrictAndCascadeForRoleSpec(buf, stmt->behavior, stmt->is_grant);\n}\n\n\n/*\n * Append the 'GRANTED BY' clause to the given buffer if the given statement is a\n * 'GRANT' statement and the grantor is specified.\n */\nvoid\nAppendGrantedByInGrantForRoleSpec(StringInfo buf, RoleSpec *grantor, bool isGrant)\n{\n\tif (grantor)\n\t{\n\t\tappendStringInfo(buf, \" GRANTED BY %s\", RoleSpecString(grantor, true));\n\t}\n}\n\n\n/*\n * Append the 'GRANTED BY' clause to the given buffer using 'GrantStmt',\n * if the given statement is a 'GRANT' statement and the grantor is specified.\n */\nvoid\nAppendGrantedByInGrant(StringInfo buf, GrantStmt *stmt)\n{\n\tAppendGrantedByInGrantForRoleSpec(buf, stmt->grantor, stmt->is_grant);\n}\n\n\nvoid\nAppendGrantSharedPrefix(StringInfo buf, GrantStmt *stmt)\n{\n\tappendStringInfo(buf, \"%s \", stmt->is_grant ? \"GRANT\" : \"REVOKE\");\n\tAppendGrantOptionFor(buf, stmt);\n\tAppendGrantPrivileges(buf, stmt);\n}\n\n\nvoid\nAppendGrantSharedSuffix(StringInfo buf, GrantStmt *stmt)\n{\n\tAppendGrantGrantees(buf, stmt);\n\tAppendWithGrantOption(buf, stmt);\n\tAppendGrantRestrictAndCascade(buf, stmt);\n\tAppendGrantedByInGrant(buf, stmt);\n\tappendStringInfo(buf, \";\");\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/citus_ruleutils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_ruleutils.c\n *\t  Version independent ruleutils wrapper\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include <stddef.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/attnum.h\"\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup.h\"\n#include \"access/htup_details.h\"\n#include \"access/skey.h\"\n#include \"access/stratnum.h\"\n#include \"access/sysattr.h\"\n#include \"access/toast_compression.h\"\n#include \"access/tupdesc.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_attribute.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_extension.h\"\n#include \"catalog/pg_foreign_data_wrapper.h\"\n#include \"catalog/pg_index.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/defrem.h\"\n#include \"commands/extension.h\"\n#include \"commands/sequence.h\"\n#include \"foreign/foreign.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"parser/parse_utilcmd.h\"\n#include \"parser/parser.h\"\n#include \"storage/lock.h\"\n#include \"utils/acl.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/errcodes.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/palloc.h\"\n#include \"utils/rel.h\"\n#include \"utils/relcache.h\"\n#include \"utils/ruleutils.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n\nstatic void deparse_index_columns(StringInfo buffer, List *indexParameterList,\n\t\t\t\t\t\t\t\t  List *deparseContext);\nstatic void AppendStorageParametersToString(StringInfo stringBuffer,\n\t\t\t\t\t\t\t\t\t\t\tList *optionList);\nstatic const char * convert_aclright_to_string(int aclright);\nstatic void simple_quote_literal(StringInfo buf, const char *val);\nstatic SubscriptingRef * TargetEntryExprFindSubsRef(Expr *expr);\nstatic void AddVacuumParams(ReindexStmt *reindexStmt, StringInfo buffer);\nstatic void process_acl_items(Acl *acl, const char *relationName,\n\t\t\t\t\t\t\t  const char *attributeName, List **defs);\n\n\n/*\n * pg_get_extensiondef_string finds the foreign data wrapper that corresponds to\n * the given foreign tableId, and checks if an extension owns this foreign data\n * wrapper. If it does, the function returns the extension's definition. If not,\n * the function returns null.\n */\nchar *\npg_get_extensiondef_string(Oid tableRelationId)\n{\n\tForeignTable *foreignTable = GetForeignTable(tableRelationId);\n\tForeignServer *server = GetForeignServer(foreignTable->serverid);\n\tForeignDataWrapper *foreignDataWrapper = GetForeignDataWrapper(server->fdwid);\n\tStringInfoData buffer = { NULL, 0, 0, 0 };\n\n\tOid classId = ForeignDataWrapperRelationId;\n\tOid objectId = server->fdwid;\n\n\tOid extensionId = getExtensionOfObject(classId, objectId);\n\tif (OidIsValid(extensionId))\n\t{\n\t\tchar *extensionName = get_extension_name(extensionId);\n\t\tOid extensionSchemaId = get_extension_schema(extensionId);\n\t\tchar *extensionSchema = get_namespace_name(extensionSchemaId);\n\n\t\tinitStringInfo(&buffer);\n\t\tappendStringInfo(&buffer, \"CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s\",\n\t\t\t\t\t\t quote_identifier(extensionName),\n\t\t\t\t\t\t quote_identifier(extensionSchema));\n\t}\n\telse\n\t{\n\t\tereport(NOTICE, (errmsg(\"foreign-data wrapper \\\"%s\\\" does not have an \"\n\t\t\t\t\t\t\t\t\"extension defined\", foreignDataWrapper->fdwname)));\n\t}\n\n\treturn (buffer.data);\n}\n\n\n/*\n * get_extension_version - given an extension OID, fetch its extversion\n * or NULL if not found.\n */\nchar *\nget_extension_version(Oid extensionId)\n{\n\tchar *versionName = NULL;\n\n\tRelation relation = table_open(ExtensionRelationId, AccessShareLock);\n\n\tScanKeyData entry[1];\n\tScanKeyInit(&entry[0],\n\t\t\t\tAnum_pg_extension_oid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(extensionId));\n\n\tSysScanDesc scanDesc = systable_beginscan(relation, ExtensionOidIndexId, true,\n\t\t\t\t\t\t\t\t\t\t\t  NULL, 1, entry);\n\n\tHeapTuple tuple = systable_getnext(scanDesc);\n\n\t/* We assume that there can be at most one matching tuple */\n\tif (HeapTupleIsValid(tuple))\n\t{\n\t\tbool isNull = false;\n\t\tDatum versionDatum = heap_getattr(tuple, Anum_pg_extension_extversion,\n\t\t\t\t\t\t\t\t\t\t  RelationGetDescr(relation), &isNull);\n\t\tif (!isNull)\n\t\t{\n\t\t\tversionName = text_to_cstring(DatumGetTextPP(versionDatum));\n\t\t}\n\t}\n\n\tsystable_endscan(scanDesc);\n\n\ttable_close(relation, AccessShareLock);\n\n\treturn versionName;\n}\n\n\n/*\n * get_extension_schema - given an extension OID, fetch its extnamespace\n *\n * Returns InvalidOid if no such extension.\n */\nOid\nget_extension_schema(Oid ext_oid)\n{\n\t/* *INDENT-OFF* */\n\tOid\t\t\tresult;\n\tRelation\trel;\n\tHeapTuple\ttuple;\n\tScanKeyData entry[1];\n\n\trel = table_open(ExtensionRelationId, AccessShareLock);\n\n\tScanKeyInit(&entry[0],\n\t\t\t\tAnum_pg_extension_oid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(ext_oid));\n\n\tSysScanDesc scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,\n\t\t\t\t\t\t\t\t  NULL, 1, entry);\n\n\ttuple = systable_getnext(scandesc);\n\n\t/* We assume that there can be at most one matching tuple */\n\tif (HeapTupleIsValid(tuple))\n\t\tresult = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace;\n\telse\n\t\tresult = InvalidOid;\n\n\tsystable_endscan(scandesc);\n\n\ttable_close(rel, AccessShareLock);\n\n\treturn result;\n\t/* *INDENT-ON* */\n}\n\n\n/*\n * pg_get_serverdef_string finds the foreign server that corresponds to the\n * given foreign tableId, and returns this server's definition.\n */\nchar *\npg_get_serverdef_string(Oid tableRelationId)\n{\n\tForeignTable *foreignTable = GetForeignTable(tableRelationId);\n\tForeignServer *server = GetForeignServer(foreignTable->serverid);\n\tForeignDataWrapper *foreignDataWrapper = GetForeignDataWrapper(server->fdwid);\n\n\tStringInfoData buffer = { NULL, 0, 0, 0 };\n\tinitStringInfo(&buffer);\n\n\tappendStringInfo(&buffer, \"CREATE SERVER IF NOT EXISTS %s\",\n\t\t\t\t\t quote_identifier(server->servername));\n\tif (server->servertype != NULL)\n\t{\n\t\tappendStringInfo(&buffer, \" TYPE %s\",\n\t\t\t\t\t\t quote_literal_cstr(server->servertype));\n\t}\n\tif (server->serverversion != NULL)\n\t{\n\t\tappendStringInfo(&buffer, \" VERSION %s\",\n\t\t\t\t\t\t quote_literal_cstr(server->serverversion));\n\t}\n\n\tappendStringInfo(&buffer, \" FOREIGN DATA WRAPPER %s\",\n\t\t\t\t\t quote_identifier(foreignDataWrapper->fdwname));\n\n\t/* append server options, if any */\n\tAppendOptionListToString(&buffer, server->options);\n\n\treturn (buffer.data);\n}\n\n\n/*\n * pg_get_sequencedef_string returns the definition of a given sequence. This\n * definition includes explicit values for all CREATE SEQUENCE options.\n */\nchar *\npg_get_sequencedef_string(Oid sequenceRelationId)\n{\n\tForm_pg_sequence pgSequenceForm = pg_get_sequencedef(sequenceRelationId);\n\n\t/* build our DDL command */\n\tchar *qualifiedSequenceName = generate_qualified_relation_name(sequenceRelationId);\n\tchar *typeName = format_type_be(pgSequenceForm->seqtypid);\n\n\tchar *sequenceDef = psprintf(CREATE_SEQUENCE_COMMAND,\n\t\t\t\t\t\t\t\t get_rel_persistence(sequenceRelationId) ==\n\t\t\t\t\t\t\t\t RELPERSISTENCE_UNLOGGED ? \"UNLOGGED \" : \"\",\n\t\t\t\t\t\t\t\t qualifiedSequenceName,\n\t\t\t\t\t\t\t\t typeName,\n\t\t\t\t\t\t\t\t pgSequenceForm->seqincrement, pgSequenceForm->seqmin,\n\t\t\t\t\t\t\t\t pgSequenceForm->seqmax, pgSequenceForm->seqstart,\n\t\t\t\t\t\t\t\t pgSequenceForm->seqcache,\n\t\t\t\t\t\t\t\t pgSequenceForm->seqcycle ? \"\" : \"NO \");\n\n\treturn sequenceDef;\n}\n\n\n/*\n * pg_get_sequencedef returns the Form_pg_sequence data about the sequence with the given\n * object id.\n */\nForm_pg_sequence\npg_get_sequencedef(Oid sequenceRelationId)\n{\n\tHeapTuple heapTuple = SearchSysCache1(SEQRELID, sequenceRelationId);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\telog(ERROR, \"cache lookup failed for sequence %u\", sequenceRelationId);\n\t}\n\n\tForm_pg_sequence pgSequenceForm = (Form_pg_sequence) GETSTRUCT(heapTuple);\n\n\tReleaseSysCache(heapTuple);\n\n\treturn pgSequenceForm;\n}\n\n\n/*\n * pg_get_tableschemadef_string returns the definition of a given table. This\n * definition includes table's schema, default column values, not null and check\n * constraints. The definition does not include constraints that trigger index\n * creations; specifically, unique and primary key constraints are excluded.\n * When includeSequenceDefaults is NEXTVAL_SEQUENCE_DEFAULTS, the function also creates\n * DEFAULT clauses for columns getting their default values from a sequence.\n * When it's WORKER_NEXTVAL_SEQUENCE_DEFAULTS, the function creates the DEFAULT\n * clause using worker_nextval('sequence') and not nextval('sequence')\n * When IncludeIdentities is NO_IDENTITY, the function does not include identity column\n * specifications. When it's INCLUDE_IDENTITY it creates GENERATED .. AS IDENTIY clauses.\n */\nchar *\npg_get_tableschemadef_string(Oid tableRelationId, IncludeSequenceDefaults\n\t\t\t\t\t\t\t includeSequenceDefaults, IncludeIdentities\n\t\t\t\t\t\t\t includeIdentityDefaults, char *accessMethod)\n{\n\tbool firstAttributePrinted = false;\n\tAttrNumber defaultValueIndex = 0;\n\tAttrNumber constraintIndex = 0;\n\tAttrNumber constraintCount = 0;\n\tbool relIsPartition = false;\n\tStringInfoData buffer = { NULL, 0, 0, 0 };\n\n\t/*\n\t * Instead of retrieving values from system catalogs as other functions in\n\t * ruleutils.c do, we follow an unusual approach here: we open the relation,\n\t * and fetch the relation's tuple descriptor. We do this because the tuple\n\t * descriptor already contains information harnessed from pg_attrdef,\n\t * pg_attribute, pg_constraint, and pg_class; and therefore using the\n\t * descriptor saves us from a lot of additional work.\n\t */\n\tRelation relation = relation_open(tableRelationId, AccessShareLock);\n\tchar *relationName = generate_relation_name(tableRelationId, NIL);\n\n\tEnsureRelationKindSupported(tableRelationId);\n\n\tinitStringInfo(&buffer);\n\n\tif (RegularTable(tableRelationId))\n\t{\n\t\tappendStringInfoString(&buffer, \"CREATE \");\n\n\t\tif (relation->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)\n\t\t{\n\t\t\tappendStringInfoString(&buffer, \"UNLOGGED \");\n\t\t}\n\n\t\tappendStringInfo(&buffer, \"TABLE %s (\", relationName);\n\n\t\trelIsPartition = relation->rd_rel->relispartition;\n\t}\n\telse\n\t{\n\t\tappendStringInfo(&buffer, \"CREATE FOREIGN TABLE %s (\", relationName);\n\t}\n\n\t/*\n\t * Iterate over the table's columns. If a particular column is not dropped\n\t * and is not inherited from another table, print the column's name and its\n\t * formatted type.\n\t */\n\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\tTupleConstr *tupleConstraints = tupleDescriptor->constr;\n\n\tfor (int attributeIndex = 0; attributeIndex < tupleDescriptor->natts;\n\t\t attributeIndex++)\n\t{\n\t\tForm_pg_attribute attributeForm = TupleDescAttr(tupleDescriptor, attributeIndex);\n\n\t\t/*\n\t\t * We disregard the inherited attributes (i.e., attinhcount > 0) here. The\n\t\t * reasoning behind this is that Citus implements declarative partitioning\n\t\t * by creating the partitions first and then sending\n\t\t * \"ALTER TABLE parent_table ATTACH PARTITION ..\" command. This may not play\n\t\t * well with regular inherited tables, which isn't a big concern from Citus'\n\t\t * perspective.\n\t\t */\n\t\tif (!attributeForm->attisdropped)\n\t\t{\n\t\t\tif (firstAttributePrinted)\n\t\t\t{\n\t\t\t\tappendStringInfoString(&buffer, \", \");\n\t\t\t}\n\t\t\tfirstAttributePrinted = true;\n\n\t\t\tconst char *attributeName = NameStr(attributeForm->attname);\n\t\t\tappendStringInfo(&buffer, \"%s \", quote_identifier(attributeName));\n\n\t\t\tconst char *attributeTypeName = format_type_with_typemod(\n\t\t\t\tattributeForm->atttypid,\n\t\t\t\tattributeForm->\n\t\t\t\tatttypmod);\n\t\t\tappendStringInfoString(&buffer, attributeTypeName);\n\n\t\t\tif (CompressionMethodIsValid(attributeForm->attcompression))\n\t\t\t{\n\t\t\t\tappendStringInfo(&buffer, \" COMPRESSION %s\",\n\t\t\t\t\t\t\t\t GetCompressionMethodName(attributeForm->attcompression));\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If this is an identity column include its identity definition in the\n\t\t\t * DDL only if its relation is not a partition. If it is a partition, any\n\t\t\t * identity is inherited from the parent table by ATTACH PARTITION. This\n\t\t\t * is Postgres 17+ behavior (commit 699586315); prior PG versions did not\n\t\t\t * support identity columns in partitioned tables.\n\t\t\t */\n\t\t\tif (attributeForm->attidentity && includeIdentityDefaults && !relIsPartition)\n\t\t\t{\n\t\t\t\tbool missing_ok = false;\n\t\t\t\tOid seqOid = getIdentitySequence(identitySequenceRelation_compat(\n\t\t\t\t\t\t\t\t\t\t\t\t\t relation),\n\t\t\t\t\t\t\t\t\t\t\t\t attributeForm->attnum, missing_ok);\n\n\t\t\t\tif (includeIdentityDefaults == INCLUDE_IDENTITY)\n\t\t\t\t{\n\t\t\t\t\tForm_pg_sequence pgSequenceForm = pg_get_sequencedef(seqOid);\n\t\t\t\t\tchar *sequenceDef = psprintf(\n\t\t\t\t\t\t\" GENERATED %s AS IDENTITY (INCREMENT BY \" INT64_FORMAT \\\n\t\t\t\t\t\t\" MINVALUE \" INT64_FORMAT \" MAXVALUE \"\n\t\t\t\t\t\tINT64_FORMAT \\\n\t\t\t\t\t\t\" START WITH \" INT64_FORMAT \" CACHE \"\n\t\t\t\t\t\tINT64_FORMAT \" %sCYCLE)\",\n\t\t\t\t\t\tattributeForm->attidentity == ATTRIBUTE_IDENTITY_ALWAYS ?\n\t\t\t\t\t\t\"ALWAYS\" : \"BY DEFAULT\",\n\t\t\t\t\t\tpgSequenceForm->seqincrement,\n\t\t\t\t\t\tpgSequenceForm->seqmin,\n\t\t\t\t\t\tpgSequenceForm->seqmax,\n\t\t\t\t\t\tpgSequenceForm->seqstart,\n\t\t\t\t\t\tpgSequenceForm->seqcache,\n\t\t\t\t\t\tpgSequenceForm->seqcycle ? \"\" : \"NO \");\n\n\t\t\t\t\tappendStringInfo(&buffer, \"%s\", sequenceDef);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* if this column has a default value, append the default value */\n\t\t\tif (attributeForm->atthasdef)\n\t\t\t{\n\t\t\t\tList *defaultContext = NULL;\n\t\t\t\tchar *defaultString = NULL;\n\n\t\t\t\tAssert(tupleConstraints != NULL);\n\n\t\t\t\tAttrDefault *defaultValueList = tupleConstraints->defval;\n\t\t\t\tAssert(defaultValueList != NULL);\n\n\t\t\t\tAttrDefault *defaultValue = &(defaultValueList[defaultValueIndex]);\n\t\t\t\tdefaultValueIndex++;\n\n\t\t\t\tAssert(defaultValue->adnum == (attributeIndex + 1));\n\t\t\t\tAssert(defaultValueIndex <= tupleConstraints->num_defval);\n\n\t\t\t\t/* convert expression to node tree, and prepare deparse context */\n\t\t\t\tNode *defaultNode = (Node *) stringToNode(defaultValue->adbin);\n\n\t\t\t\t/*\n\t\t\t\t * if column default value is explicitly requested, or it is\n\t\t\t\t * not set from a sequence then we include DEFAULT clause for\n\t\t\t\t * this column.\n\t\t\t\t */\n\t\t\t\tif (includeSequenceDefaults ||\n\t\t\t\t\t!contain_nextval_expression_walker(defaultNode, NULL))\n\t\t\t\t{\n\t\t\t\t\tdefaultContext = deparse_context_for(relationName, tableRelationId);\n\n\t\t\t\t\t/* deparse default value string */\n\t\t\t\t\tdefaultString = deparse_expression(defaultNode, defaultContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   false, false);\n\n\t\t\t\t\tif (attributeForm->attgenerated == ATTRIBUTE_GENERATED_STORED)\n\t\t\t\t\t{\n\t\t\t\t\t\tappendStringInfo(&buffer, \" GENERATED ALWAYS AS (%s) STORED\",\n\t\t\t\t\t\t\t\t\t\t defaultString);\n\t\t\t\t\t}\n#if PG_VERSION_NUM >= PG_VERSION_18\n\t\t\t\t\telse if (attributeForm->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)\n\t\t\t\t\t{\n\t\t\t\t\t\tappendStringInfo(&buffer, \" GENERATED ALWAYS AS (%s) VIRTUAL\",\n\t\t\t\t\t\t\t\t\t\t defaultString);\n\t\t\t\t\t}\n#endif\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tOid seqOid = GetSequenceOid(tableRelationId, defaultValue->adnum);\n\t\t\t\t\t\tif (includeSequenceDefaults == WORKER_NEXTVAL_SEQUENCE_DEFAULTS &&\n\t\t\t\t\t\t\tseqOid != InvalidOid &&\n\t\t\t\t\t\t\tpg_get_sequencedef(seqOid)->seqtypid != INT8OID)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/*\n\t\t\t\t\t\t\t * We use worker_nextval for int and smallint types.\n\t\t\t\t\t\t\t * Check issue #5126 and PR #5254 for details.\n\t\t\t\t\t\t\t * https://github.com/citusdata/citus/issues/5126\n\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\tchar *sequenceName = generate_qualified_relation_name(\n\t\t\t\t\t\t\t\tseqOid);\n\t\t\t\t\t\t\tappendStringInfo(&buffer,\n\t\t\t\t\t\t\t\t\t\t\t \" DEFAULT worker_nextval(%s::regclass)\",\n\t\t\t\t\t\t\t\t\t\t\t quote_literal_cstr(sequenceName));\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tappendStringInfo(&buffer, \" DEFAULT %s\", defaultString);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* if this column has a not null constraint, append the constraint */\n\t\t\tif (attributeForm->attnotnull)\n\t\t\t{\n\t\t\t\tappendStringInfoString(&buffer, \" NOT NULL\");\n\t\t\t}\n\n\t\t\tif (attributeForm->attcollation != InvalidOid &&\n\t\t\t\tattributeForm->attcollation != DEFAULT_COLLATION_OID)\n\t\t\t{\n\t\t\t\tappendStringInfo(&buffer, \" COLLATE %s\", generate_collation_name(\n\t\t\t\t\t\t\t\t\t attributeForm->attcollation));\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * Now check if the table has any constraints. If it does, set the number of\n\t * check constraints here. Then iterate over all check constraints and print\n\t * them.\n\t */\n\tif (tupleConstraints != NULL)\n\t{\n\t\tconstraintCount = tupleConstraints->num_check;\n\t}\n\n\tfor (constraintIndex = 0; constraintIndex < constraintCount; constraintIndex++)\n\t{\n\t\tConstrCheck *checkConstraintList = tupleConstraints->check;\n\t\tConstrCheck *checkConstraint = &(checkConstraintList[constraintIndex]);\n\n\n\t\t/* if an attribute or constraint has been printed, format properly */\n\t\tif (firstAttributePrinted || constraintIndex > 0)\n\t\t{\n\t\t\tappendStringInfoString(&buffer, \", \");\n\t\t}\n\n\t\tappendStringInfo(&buffer, \"CONSTRAINT %s CHECK \",\n\t\t\t\t\t\t quote_identifier(checkConstraint->ccname));\n\n\t\t/* convert expression to node tree, and prepare deparse context */\n\t\tNode *checkNode = (Node *) stringToNode(checkConstraint->ccbin);\n\t\tList *checkContext = deparse_context_for(relationName, tableRelationId);\n\n\t\t/* deparse check constraint string */\n\t\tchar *checkString = deparse_expression(checkNode, checkContext, false, false);\n\n\t\tappendStringInfoString(&buffer, \"(\");\n\t\tappendStringInfoString(&buffer, checkString);\n\t\tappendStringInfoString(&buffer, \")\");\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\t\tif (!checkConstraint->ccenforced)\n\t\t{\n\t\t\tappendStringInfoString(&buffer, \" NOT ENFORCED\");\n\t\t}\n#endif\n\t}\n\n\t/* close create table's outer parentheses */\n\tappendStringInfoString(&buffer, \")\");\n\n\t/*\n\t * If the relation is a foreign table, append the server name and options to\n\t * the create table statement.\n\t */\n\tchar relationKind = relation->rd_rel->relkind;\n\tif (relationKind == RELKIND_FOREIGN_TABLE)\n\t{\n\t\tForeignTable *foreignTable = GetForeignTable(tableRelationId);\n\t\tForeignServer *foreignServer = GetForeignServer(foreignTable->serverid);\n\n\t\tchar *serverName = foreignServer->servername;\n\t\tappendStringInfo(&buffer, \" SERVER %s\", quote_identifier(serverName));\n\t\tAppendOptionListToString(&buffer, foreignTable->options);\n\t}\n\telse if (relationKind == RELKIND_PARTITIONED_TABLE)\n\t{\n\t\tchar *partitioningInformation = GeneratePartitioningInformation(tableRelationId);\n\t\tappendStringInfo(&buffer, \" PARTITION BY %s \", partitioningInformation);\n\t}\n\n\t/*\n\t * Add table access methods when the table is configured with an\n\t * access method\n\t */\n\tif (accessMethod)\n\t{\n\t\tappendStringInfo(&buffer, \" USING %s\", quote_identifier(accessMethod));\n\t}\n\telse if (OidIsValid(relation->rd_rel->relam))\n\t{\n\t\tHeapTuple amTup = SearchSysCache1(AMOID, ObjectIdGetDatum(\n\t\t\t\t\t\t\t\t\t\t\t  relation->rd_rel->relam));\n\t\tif (!HeapTupleIsValid(amTup))\n\t\t{\n\t\t\telog(ERROR, \"cache lookup failed for access method %u\",\n\t\t\t\t relation->rd_rel->relam);\n\t\t}\n\t\tForm_pg_am amForm = (Form_pg_am) GETSTRUCT(amTup);\n\t\tappendStringInfo(&buffer, \" USING %s\", quote_identifier(NameStr(amForm->amname)));\n\t\tReleaseSysCache(amTup);\n\t}\n\n\t/*\n\t * Add any reloptions (storage parameters) defined on the table in a WITH\n\t * clause.\n\t */\n\t{\n\t\tchar *reloptions = flatten_reloptions(tableRelationId);\n\t\tif (reloptions)\n\t\t{\n\t\t\tappendStringInfo(&buffer, \" WITH (%s)\", reloptions);\n\t\t\tpfree(reloptions);\n\t\t}\n\t}\n\n\trelation_close(relation, AccessShareLock);\n\n\treturn (buffer.data);\n}\n\n\n/*\n * EnsureRelationKindSupported errors out if the given relation is not supported\n * as a distributed relation.\n */\nvoid\nEnsureRelationKindSupported(Oid relationId)\n{\n\tchar relationKind = get_rel_relkind(relationId);\n\tif (!relationKind)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"relation with OID %d does not exist\",\n\t\t\t\t\t\t\t   relationId)));\n\t}\n\n\tbool supportedRelationKind = RegularTable(relationId) ||\n\t\t\t\t\t\t\t\t relationKind == RELKIND_FOREIGN_TABLE;\n\n\t/*\n\t * Citus doesn't support bare inherited tables (i.e., not a partition or\n\t * partitioned table)\n\t */\n\tsupportedRelationKind = supportedRelationKind && !(IsChildTable(relationId) ||\n\t\t\t\t\t\t\t\t\t\t\t\t\t   IsParentTable(relationId));\n\n\tif (!supportedRelationKind)\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),\n\t\t\t\t\t\terrmsg(\"%s is not a regular, foreign or partitioned table\",\n\t\t\t\t\t\t\t   relationName)));\n\t}\n}\n\n\n/*\n * pg_get_tablecolumnoptionsdef_string returns column storage type and column\n * statistics definitions for given table, _if_ these definitions differ from\n * their default values. The function returns null if all columns use default\n * values for their storage types and statistics.\n */\nchar *\npg_get_tablecolumnoptionsdef_string(Oid tableRelationId)\n{\n\tList *columnOptionList = NIL;\n\tListCell *columnOptionCell = NULL;\n\tbool firstOptionPrinted = false;\n\tStringInfoData buffer = { NULL, 0, 0, 0 };\n\n\t/*\n\t * Instead of retrieving values from system catalogs, we open the relation,\n\t * and use the relation's tuple descriptor to access attribute information.\n\t * This is primarily to maintain symmetry with pg_get_tableschemadef.\n\t */\n\tRelation relation = relation_open(tableRelationId, AccessShareLock);\n\n\tEnsureRelationKindSupported(tableRelationId);\n\n\t/*\n\t * Iterate over the table's columns. If a particular column is not dropped\n\t * and is not inherited from another table, check if column storage or\n\t * statistics statements need to be printed.\n\t */\n\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\n\tif (tupleDescriptor->natts > MaxAttrNumber)\n\t{\n\t\tereport(ERROR, (errmsg(\"bad number of tuple descriptor attributes\")));\n\t}\n\n\tAttrNumber natts = tupleDescriptor->natts;\n\tfor (AttrNumber attributeIndex = 0;\n\t\t attributeIndex < natts;\n\t\t attributeIndex++)\n\t{\n\t\tForm_pg_attribute attributeForm = TupleDescAttr(tupleDescriptor, attributeIndex);\n\t\tchar *attributeName = NameStr(attributeForm->attname);\n\t\tchar defaultStorageType = get_typstorage(attributeForm->atttypid);\n\n\t\tif (!attributeForm->attisdropped && attributeForm->attinhcount == 0)\n\t\t{\n\t\t\t/*\n\t\t\t * If the user changed the column's default storage type, create\n\t\t\t * alter statement and add statement to a list for later processing.\n\t\t\t */\n\t\t\tif (attributeForm->attstorage != defaultStorageType)\n\t\t\t{\n\t\t\t\tchar *storageName = 0;\n\t\t\t\tStringInfoData statement = { NULL, 0, 0, 0 };\n\t\t\t\tinitStringInfo(&statement);\n\n\t\t\t\tswitch (attributeForm->attstorage)\n\t\t\t\t{\n\t\t\t\t\tcase 'p':\n\t\t\t\t\t{\n\t\t\t\t\t\tstorageName = \"PLAIN\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase 'e':\n\t\t\t\t\t{\n\t\t\t\t\t\tstorageName = \"EXTERNAL\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase 'm':\n\t\t\t\t\t{\n\t\t\t\t\t\tstorageName = \"MAIN\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase 'x':\n\t\t\t\t\t{\n\t\t\t\t\t\tstorageName = \"EXTENDED\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t{\n\t\t\t\t\t\tereport(ERROR, (errmsg(\"unrecognized storage type: %c\",\n\t\t\t\t\t\t\t\t\t\t\t   attributeForm->attstorage)));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tappendStringInfo(&statement, \"ALTER COLUMN %s \",\n\t\t\t\t\t\t\t\t quote_identifier(attributeName));\n\t\t\t\tappendStringInfo(&statement, \"SET STORAGE %s\", storageName);\n\n\t\t\t\tcolumnOptionList = lappend(columnOptionList, statement.data);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If the user changed the column's statistics target, create\n\t\t\t * alter statement and add statement to a list for later processing.\n\t\t\t */\n\t\t\tHeapTuple atttuple = SearchSysCache2(ATTNUM,\n\t\t\t\t\t\t\t\t\t\t\t\t ObjectIdGetDatum(tableRelationId),\n\t\t\t\t\t\t\t\t\t\t\t\t Int16GetDatum(attributeForm->attnum));\n\t\t\tif (!HeapTupleIsValid(atttuple))\n\t\t\t{\n\t\t\t\telog(ERROR, \"cache lookup failed for attribute %d of relation %u\",\n\t\t\t\t\t attributeForm->attnum, tableRelationId);\n\t\t\t}\n\n\t\t\tint32 targetAttstattarget = getAttstattarget_compat(atttuple);\n\t\t\tReleaseSysCache(atttuple);\n\t\t\tif (targetAttstattarget >= 0)\n\t\t\t{\n\t\t\t\tStringInfoData statement = { NULL, 0, 0, 0 };\n\t\t\t\tinitStringInfo(&statement);\n\n\t\t\t\tappendStringInfo(&statement, \"ALTER COLUMN %s \",\n\t\t\t\t\t\t\t\t quote_identifier(attributeName));\n\t\t\t\tappendStringInfo(&statement, \"SET STATISTICS %d\",\n\t\t\t\t\t\t\t\t targetAttstattarget);\n\n\t\t\t\tcolumnOptionList = lappend(columnOptionList, statement.data);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * Iterate over column storage and statistics statements that we created,\n\t * and append them to a single alter table statement.\n\t */\n\tforeach(columnOptionCell, columnOptionList)\n\t{\n\t\tif (!firstOptionPrinted)\n\t\t{\n\t\t\tinitStringInfo(&buffer);\n\t\t\tappendStringInfo(&buffer, \"ALTER TABLE ONLY %s \",\n\t\t\t\t\t\t\t generate_relation_name(tableRelationId, NIL));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfoString(&buffer, \", \");\n\t\t}\n\t\tfirstOptionPrinted = true;\n\n\t\tchar *columnOptionStatement = (char *) lfirst(columnOptionCell);\n\t\tappendStringInfoString(&buffer, columnOptionStatement);\n\n\t\tpfree(columnOptionStatement);\n\t}\n\n\tlist_free(columnOptionList);\n\trelation_close(relation, AccessShareLock);\n\n\treturn (buffer.data);\n}\n\n\n/*\n * deparse_shard_index_statement uses the provided CREATE INDEX node, dist.\n * relation, and shard identifier to populate a provided buffer with a string\n * representation of a shard-extended version of that command.\n */\nvoid\ndeparse_shard_index_statement(IndexStmt *origStmt, Oid distrelid, int64 shardid,\n\t\t\t\t\t\t\t  StringInfo buffer)\n{\n\tIndexStmt *indexStmt = copyObject(origStmt); /* copy to avoid modifications */\n\n\t/* extend relation and index name using shard identifier */\n\tAppendShardIdToName(&(indexStmt->relation->relname), shardid);\n\tAppendShardIdToName(&(indexStmt->idxname), shardid);\n\n\tchar *relationName = indexStmt->relation->relname;\n\tchar *indexName = indexStmt->idxname;\n\n\t/* use extended shard name and transformed stmt for deparsing */\n\tList *deparseContext = deparse_context_for(relationName, distrelid);\n\tindexStmt = transformIndexStmt(distrelid, indexStmt, NULL);\n\n\tappendStringInfo(buffer, \"CREATE %s INDEX %s %s %s ON %s %s USING %s \",\n\t\t\t\t\t (indexStmt->unique ? \"UNIQUE\" : \"\"),\n\t\t\t\t\t (indexStmt->concurrent ? \"CONCURRENTLY\" : \"\"),\n\t\t\t\t\t (indexStmt->if_not_exists ? \"IF NOT EXISTS\" : \"\"),\n\t\t\t\t\t quote_identifier(indexName),\n\t\t\t\t\t (indexStmt->relation->inh ? \"\" : \"ONLY\"),\n\t\t\t\t\t quote_qualified_identifier(indexStmt->relation->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\trelationName),\n\t\t\t\t\t indexStmt->accessMethod);\n\n\t/*\n\t * Switch to empty search_path to deparse_index_columns to produce fully-\n\t * qualified names in expressions.\n\t */\n\tint saveNestLevel = PushEmptySearchPath();\n\n\t/* index column or expression list begins here */\n\tappendStringInfoChar(buffer, '(');\n\tdeparse_index_columns(buffer, indexStmt->indexParams, deparseContext);\n\tappendStringInfoString(buffer, \") \");\n\n\t/* column/expressions for INCLUDE list */\n\tif (indexStmt->indexIncludingParams != NIL)\n\t{\n\t\tappendStringInfoString(buffer, \"INCLUDE (\");\n\t\tdeparse_index_columns(buffer, indexStmt->indexIncludingParams, deparseContext);\n\t\tappendStringInfoString(buffer, \") \");\n\t}\n\n\tif (indexStmt->nulls_not_distinct)\n\t{\n\t\tappendStringInfoString(buffer, \"NULLS NOT DISTINCT \");\n\t}\n\n\tif (indexStmt->options != NIL)\n\t{\n\t\tappendStringInfoString(buffer, \"WITH (\");\n\t\tAppendStorageParametersToString(buffer, indexStmt->options);\n\t\tappendStringInfoString(buffer, \") \");\n\t}\n\n\tif (indexStmt->whereClause != NULL)\n\t{\n\t\tappendStringInfo(buffer, \"WHERE %s\", deparse_expression(indexStmt->whereClause,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdeparseContext, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse));\n\t}\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n}\n\n\n/*\n * deparse_shard_reindex_statement uses the provided REINDEX node, dist.\n * relation, and shard identifier to populate a provided buffer with a string\n * representation of a shard-extended version of that command.\n */\nvoid\ndeparse_shard_reindex_statement(ReindexStmt *origStmt, Oid distrelid, int64 shardid,\n\t\t\t\t\t\t\t\tStringInfo buffer)\n{\n\tReindexStmt *reindexStmt = copyObject(origStmt); /* copy to avoid modifications */\n\tchar *relationName = NULL;\n\tconst char *concurrentlyString =\n\t\tIsReindexWithParam_compat(reindexStmt, \"concurrently\") ? \"CONCURRENTLY \" : \"\";\n\n\n\tif (reindexStmt->kind == REINDEX_OBJECT_INDEX ||\n\t\treindexStmt->kind == REINDEX_OBJECT_TABLE)\n\t{\n\t\t/* extend relation and index name using shard identifier */\n\t\tAppendShardIdToName(&(reindexStmt->relation->relname), shardid);\n\n\t\trelationName = reindexStmt->relation->relname;\n\t}\n\n\tappendStringInfoString(buffer, \"REINDEX \");\n\tAddVacuumParams(reindexStmt, buffer);\n\n\tswitch (reindexStmt->kind)\n\t{\n\t\tcase REINDEX_OBJECT_INDEX:\n\t\t{\n\t\t\tappendStringInfo(buffer, \"INDEX %s%s\", concurrentlyString,\n\t\t\t\t\t\t\t quote_qualified_identifier(reindexStmt->relation->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\trelationName));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase REINDEX_OBJECT_TABLE:\n\t\t{\n\t\t\tappendStringInfo(buffer, \"TABLE %s%s\", concurrentlyString,\n\t\t\t\t\t\t\t quote_qualified_identifier(reindexStmt->relation->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\trelationName));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase REINDEX_OBJECT_SCHEMA:\n\t\t{\n\t\t\tappendStringInfo(buffer, \"SCHEMA %s%s\", concurrentlyString,\n\t\t\t\t\t\t\t quote_identifier(reindexStmt->name));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase REINDEX_OBJECT_SYSTEM:\n\t\t{\n\t\t\tappendStringInfo(buffer, \"SYSTEM %s%s\", concurrentlyString,\n\t\t\t\t\t\t\t quote_identifier(reindexStmt->name));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase REINDEX_OBJECT_DATABASE:\n\t\t{\n\t\t\tappendStringInfo(buffer, \"DATABASE %s%s\", concurrentlyString,\n\t\t\t\t\t\t\t quote_identifier(reindexStmt->name));\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n/*\n * IsReindexWithParam_compat returns true if the given parameter\n * exists for the given reindexStmt.\n */\nbool\nIsReindexWithParam_compat(ReindexStmt *reindexStmt, char *param)\n{\n\tDefElem *opt = NULL;\n\tforeach_declared_ptr(opt, reindexStmt->params)\n\t{\n\t\tif (strcmp(opt->defname, param) == 0)\n\t\t{\n\t\t\treturn defGetBoolean(opt);\n\t\t}\n\t}\n\treturn false;\n}\n\n\n/*\n * AddVacuumParams adds vacuum params to the given buffer.\n */\nstatic void\nAddVacuumParams(ReindexStmt *reindexStmt, StringInfo buffer)\n{\n\tStringInfo temp = makeStringInfo();\n\tif (IsReindexWithParam_compat(reindexStmt, \"verbose\"))\n\t{\n\t\tappendStringInfoString(temp, \"VERBOSE\");\n\t}\n\n\tchar *tableSpaceName = NULL;\n\tDefElem *opt = NULL;\n\tforeach_declared_ptr(opt, reindexStmt->params)\n\t{\n\t\tif (strcmp(opt->defname, \"tablespace\") == 0)\n\t\t{\n\t\t\ttableSpaceName = defGetString(opt);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (tableSpaceName)\n\t{\n\t\tif (temp->len > 0)\n\t\t{\n\t\t\tappendStringInfo(temp, \", TABLESPACE %s\", tableSpaceName);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(temp, \"TABLESPACE %s\", tableSpaceName);\n\t\t}\n\t}\n\n\tif (temp->len > 0)\n\t{\n\t\tappendStringInfo(buffer, \"(%s) \", temp->data);\n\t}\n}\n\n\n/* deparse_index_columns appends index or include parameters to the provided buffer */\nstatic void\ndeparse_index_columns(StringInfo buffer, List *indexParameterList, List *deparseContext)\n{\n\tListCell *indexParameterCell = NULL;\n\tforeach(indexParameterCell, indexParameterList)\n\t{\n\t\tIndexElem *indexElement = (IndexElem *) lfirst(indexParameterCell);\n\n\t\t/* use commas to separate subsequent elements */\n\t\tif (indexParameterCell != list_head(indexParameterList))\n\t\t{\n\t\t\tappendStringInfoChar(buffer, ',');\n\t\t}\n\n\t\tif (indexElement->name)\n\t\t{\n\t\t\tappendStringInfo(buffer, \"%s \", quote_identifier(indexElement->name));\n\t\t}\n\t\telse if (indexElement->expr)\n\t\t{\n\t\t\tappendStringInfo(buffer, \"(%s)\", deparse_expression(indexElement->expr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdeparseContext, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse));\n\t\t}\n\n\t\tif (indexElement->collation != NIL)\n\t\t{\n\t\t\tappendStringInfo(buffer, \"COLLATE %s \",\n\t\t\t\t\t\t\t NameListToQuotedString(indexElement->collation));\n\t\t}\n\n\t\tif (indexElement->opclass != NIL)\n\t\t{\n\t\t\tappendStringInfo(buffer, \"%s \",\n\t\t\t\t\t\t\t NameListToQuotedString(indexElement->opclass));\n\t\t}\n\n\t\t/* Commit on postgres: 911e70207703799605f5a0e8aad9f06cff067c63*/\n\t\tif (indexElement->opclassopts != NIL)\n\t\t{\n\t\t\tappendStringInfoString(buffer, \"(\");\n\t\t\tAppendStorageParametersToString(buffer, indexElement->opclassopts);\n\t\t\tappendStringInfoString(buffer, \") \");\n\t\t}\n\n\t\tif (indexElement->ordering != SORTBY_DEFAULT)\n\t\t{\n\t\t\tbool sortAsc = (indexElement->ordering == SORTBY_ASC);\n\t\t\tappendStringInfo(buffer, \"%s \", (sortAsc ? \"ASC\" : \"DESC\"));\n\t\t}\n\n\t\tif (indexElement->nulls_ordering != SORTBY_NULLS_DEFAULT)\n\t\t{\n\t\t\tbool nullsFirst = (indexElement->nulls_ordering == SORTBY_NULLS_FIRST);\n\t\t\tappendStringInfo(buffer, \"NULLS %s \", (nullsFirst ? \"FIRST\" : \"LAST\"));\n\t\t}\n\t}\n}\n\n\n/*\n * pg_get_indexclusterdef_string returns the definition of a cluster statement\n * for given index. The function returns null if the table is not clustered on\n * given index.\n */\nchar *\npg_get_indexclusterdef_string(Oid indexRelationId)\n{\n\tStringInfoData buffer = { NULL, 0, 0, 0 };\n\n\tHeapTuple indexTuple = SearchSysCache(INDEXRELID, ObjectIdGetDatum(indexRelationId),\n\t\t\t\t\t\t\t\t\t\t  0, 0, 0);\n\tif (!HeapTupleIsValid(indexTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"cache lookup failed for index %u\", indexRelationId)));\n\t}\n\n\tForm_pg_index indexForm = (Form_pg_index) GETSTRUCT(indexTuple);\n\tOid tableRelationId = indexForm->indrelid;\n\n\t/* check if the table is clustered on this index */\n\tif (indexForm->indisclustered)\n\t{\n\t\tchar *qualifiedRelationName =\n\t\t\tgenerate_qualified_relation_name(tableRelationId);\n\t\tchar *indexName = get_rel_name(indexRelationId); /* needs to be quoted */\n\n\t\tinitStringInfo(&buffer);\n\t\tappendStringInfo(&buffer, \"ALTER TABLE %s CLUSTER ON %s\",\n\t\t\t\t\t\t qualifiedRelationName, quote_identifier(indexName));\n\t}\n\n\tReleaseSysCache(indexTuple);\n\n\treturn (buffer.data);\n}\n\n\n/*\n * pg_get_table_grants returns a list of sql statements which recreate the\n * permissions for a specific table, including attributes privileges.\n *\n */\nList *\npg_get_table_grants(Oid relationId)\n{\n\t/* *INDENT-OFF* */\n\tStringInfoData buffer;\n\tList *defs = NIL;\n\tbool isNull = false;\n\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\tchar *relationName = generate_relation_name(relationId, NIL);\n\n\tinitStringInfo(&buffer);\n\n\t/* lookup all table level grants */\n\tHeapTuple classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));\n\tif (!HeapTupleIsValid(classTuple))\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_UNDEFINED_TABLE),\n\t\t\t\t errmsg(\"relation with OID %u does not exist\",\n\t\t\t\t\t\trelationId)));\n\t}\n\tForm_pg_class classForm = (Form_pg_class) GETSTRUCT(classTuple);\n\tAttrNumber\tnattrs = classForm->relnatts;\n\n\tDatum aclDatum = SysCacheGetAttr(RELOID, classTuple, Anum_pg_class_relacl,\n\t\t\t\t\t\t\t   &isNull);\n\n\tReleaseSysCache(classTuple);\n\n\tif (!isNull)\n\t{\n\n\t\t/*\n\t\t * First revoke all default permissions, so we can start adding the\n\t\t * exact permissions from the master. Note that we only do so if there\n\t\t * are any actual grants; an empty grant set signals default\n\t\t * permissions.\n\t\t *\n\t\t * Note: This doesn't work correctly if default permissions have been\n\t\t * changed with ALTER DEFAULT PRIVILEGES - but that's hard to fix\n\t\t * properly currently.\n\t\t */\n\t\tappendStringInfo(&buffer, \"REVOKE ALL ON %s FROM PUBLIC\",\n\t\t\t\t\t\t relationName);\n\t\tdefs = lappend(defs, pstrdup(buffer.data));\n\t\tresetStringInfo(&buffer);\n\n\t\t/* iterate through the acl datastructure, emit GRANTs */\n\n\t\tAcl *acl = DatumGetAclP(aclDatum);\n\n\t\tprocess_acl_items(acl, relationName, NULL, &defs);\n\n\t\t/* if we have a detoasted copy, free it */\n\t\tif ((Pointer) acl != DatumGetPointer(aclDatum))\n\t\t\tpfree(acl);\n\t}\n\n\tresetStringInfo(&buffer);\n\n\t/* lookup all attribute level grants */\n\tfor (AttrNumber attNum = 1; attNum <= nattrs; attNum++)\n\t{\n\t\tHeapTuple attTuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(relationId),\n\t\t\t\t\t\t\t\t\t\t\tInt16GetDatum(attNum));\n\t\tif (!HeapTupleIsValid(attTuple))\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_UNDEFINED_COLUMN),\n\t\t\t\t\terrmsg(\"attribute with OID %u does not exist\",\n\t\t\t\t\t\t\tattNum)));\n\t\t}\n\n\t\tForm_pg_attribute thisAttribute = (Form_pg_attribute) GETSTRUCT(attTuple);\n\n\t\t/* ignore dropped columns */\n\t\tif (thisAttribute->attisdropped)\n\t\t{\n\t\t\tReleaseSysCache(attTuple);\n\t\t\tcontinue;\n\t\t}\n\n\t\tDatum aclAttDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,\n\t\t\t\t\t\t\t\t&isNull);\n\t\tif (!isNull)\n\t\t{\n\t\t\t/* iterate through the acl datastructure, emit GRANTs */\n\t\t\tAcl *acl = DatumGetAclP(aclAttDatum);\n\n\t\t\tprocess_acl_items(acl, relationName, NameStr(thisAttribute->attname), &defs);\n\n\t\t\t/* if we have a detoasted copy, free it */\n\t\t\tif ((Pointer) acl != DatumGetPointer(aclAttDatum))\n\t\t\t\tpfree(acl);\n\t\t}\n\t\tReleaseSysCache(attTuple);\n\t}\n\n\trelation_close(relation, NoLock);\n\treturn defs;\n}\n\n\n/*\n * Helper function to process ACL items.\n * If attributeName is NULL, the function emits table-level GRANT commands;\n * otherwise it emits column-level GRANT commands.\n * This function was modeled after aclexplode(), previously in pg_get_table_grants().\n */\nstatic void\nprocess_acl_items(Acl *acl, const char *relationName, const char *attributeName,\n\t\t\t\t  List **defs)\n{\n\tAclItem *aidat = ACL_DAT(acl);\n\tint i = 0;\n\tint offtype = -1;\n\tStringInfoData buffer;\n\n\tinitStringInfo(&buffer);\n\n\twhile (i < ACL_NUM(acl))\n\t{\n\t\tofftype++;\n\t\tif (offtype == N_ACL_RIGHTS)\n\t\t{\n\t\t\tofftype = 0;\n\t\t\ti++;\n\t\t\tif (i >= ACL_NUM(acl)) /* done */\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tAclItem *aidata = &aidat[i];\n\t\tAclMode priv_bit = 1 << offtype;\n\n\t\tif (ACLITEM_GET_PRIVS(*aidata) & priv_bit)\n\t\t{\n\t\t\tconst char *roleName = NULL;\n\t\t\tconst char *withGrant = \"\";\n\n\t\t\tif (aidata->ai_grantee != 0)\n\t\t\t{\n\t\t\t\troleName = quote_identifier(GetUserNameFromId(aidata->ai_grantee, false));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\troleName = \"PUBLIC\";\n\t\t\t}\n\n\t\t\tif ((ACLITEM_GET_GOPTIONS(*aidata) & priv_bit) != 0)\n\t\t\t{\n\t\t\t\twithGrant = \" WITH GRANT OPTION\";\n\t\t\t}\n\n\t\t\tif (attributeName)\n\t\t\t{\n\t\t\t\tappendStringInfo(&buffer, \"GRANT %s(%s) ON %s TO %s%s\",\n\t\t\t\t\t\t\t\t convert_aclright_to_string(priv_bit),\n\t\t\t\t\t\t\t\t quote_identifier(attributeName),\n\t\t\t\t\t\t\t\t relationName,\n\t\t\t\t\t\t\t\t roleName,\n\t\t\t\t\t\t\t\t withGrant);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(&buffer, \"GRANT %s ON %s TO %s%s\",\n\t\t\t\t\t\t\t\t convert_aclright_to_string(priv_bit),\n\t\t\t\t\t\t\t\t relationName,\n\t\t\t\t\t\t\t\t roleName,\n\t\t\t\t\t\t\t\t withGrant);\n\t\t\t}\n\t\t\t*defs = lappend(*defs, pstrdup(buffer.data));\n\t\t\tresetStringInfo(&buffer);\n\t\t}\n\t}\n}\n\n\n/*\n * generate_qualified_relation_name computes the schema-qualified name to display for a\n * relation specified by OID.\n */\nchar *\ngenerate_qualified_relation_name(Oid relid)\n{\n\tHeapTuple tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));\n\tif (!HeapTupleIsValid(tp))\n\t{\n\t\telog(ERROR, \"cache lookup failed for relation %u\", relid);\n\t}\n\tForm_pg_class reltup = (Form_pg_class) GETSTRUCT(tp);\n\tchar *relname = NameStr(reltup->relname);\n\n\tchar *nspname = get_namespace_name(reltup->relnamespace);\n\tif (!nspname)\n\t{\n\t\telog(ERROR, \"cache lookup failed for namespace %u\",\n\t\t\t reltup->relnamespace);\n\t}\n\n\tchar *result = quote_qualified_identifier(nspname, relname);\n\n\tReleaseSysCache(tp);\n\n\treturn result;\n}\n\n\n/*\n * AppendOptionListToString converts the option list to its textual format, and\n * appends this text to the given string buffer.\n */\nvoid\nAppendOptionListToString(StringInfo stringBuffer, List *optionList)\n{\n\tif (optionList != NIL)\n\t{\n\t\tListCell *optionCell = NULL;\n\t\tbool firstOptionPrinted = false;\n\n\t\tappendStringInfo(stringBuffer, \" OPTIONS (\");\n\n\t\tforeach(optionCell, optionList)\n\t\t{\n\t\t\tDefElem *option = (DefElem *) lfirst(optionCell);\n\t\t\tchar *optionName = option->defname;\n\t\t\tchar *optionValue = defGetString(option);\n\n\t\t\tif (firstOptionPrinted)\n\t\t\t{\n\t\t\t\tappendStringInfo(stringBuffer, \", \");\n\t\t\t}\n\t\t\tfirstOptionPrinted = true;\n\n\t\t\tappendStringInfo(stringBuffer, \"%s \", quote_identifier(optionName));\n\t\t\tappendStringInfo(stringBuffer, \"%s\", quote_literal_cstr(optionValue));\n\t\t}\n\n\t\tappendStringInfo(stringBuffer, \")\");\n\t}\n}\n\n\n/*\n * AppendStorageParametersToString converts the storage parameter list to its\n * textual format, and appends this text to the given string buffer.\n */\nstatic void\nAppendStorageParametersToString(StringInfo stringBuffer, List *optionList)\n{\n\tListCell *optionCell = NULL;\n\tbool firstOptionPrinted = false;\n\n\tforeach(optionCell, optionList)\n\t{\n\t\tDefElem *option = (DefElem *) lfirst(optionCell);\n\t\tchar *optionName = option->defname;\n\t\tchar *optionValue = defGetString(option);\n\n\t\tif (firstOptionPrinted)\n\t\t{\n\t\t\tappendStringInfo(stringBuffer, \", \");\n\t\t}\n\t\tfirstOptionPrinted = true;\n\n\t\tappendStringInfo(stringBuffer, \"%s = %s \",\n\t\t\t\t\t\t quote_identifier(optionName),\n\t\t\t\t\t\t quote_literal_cstr(optionValue));\n\t}\n}\n\n\n/* copy of postgresql's function, which is static as well */\nstatic const char *\nconvert_aclright_to_string(int aclright)\n{\n\t/* *INDENT-OFF* */\n\tswitch (aclright)\n\t{\n\t\tcase ACL_INSERT:\n\t\t\treturn \"INSERT\";\n\t\tcase ACL_SELECT:\n\t\t\treturn \"SELECT\";\n\t\tcase ACL_UPDATE:\n\t\t\treturn \"UPDATE\";\n\t\tcase ACL_DELETE:\n\t\t\treturn \"DELETE\";\n\t\tcase ACL_TRUNCATE:\n\t\t\treturn \"TRUNCATE\";\n\t\tcase ACL_REFERENCES:\n\t\t\treturn \"REFERENCES\";\n\t\tcase ACL_TRIGGER:\n\t\t\treturn \"TRIGGER\";\n\t\tcase ACL_EXECUTE:\n\t\t\treturn \"EXECUTE\";\n\t\tcase ACL_USAGE:\n\t\t\treturn \"USAGE\";\n\t\tcase ACL_CREATE:\n\t\t\treturn \"CREATE\";\n\t\tcase ACL_CREATE_TEMP:\n\t\t\treturn \"TEMPORARY\";\n\t\tcase ACL_CONNECT:\n\t\t\treturn \"CONNECT\";\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\tcase ACL_MAINTAIN:\n\t\t\treturn \"MAINTAIN\";\n#endif\n\t\tdefault:\n\t\t\telog(ERROR, \"unrecognized aclright: %d\", aclright);\n\t\t\treturn NULL;\n\t}\n\t/* *INDENT-ON* */\n}\n\n\n/*\n * contain_nextval_expression_walker walks over expression tree and returns\n * true if it contains call to 'nextval' function or it has an identity column.\n */\nbool\ncontain_nextval_expression_walker(Node *node, void *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\t/* check if the node contains an identity column */\n\tif (IsA(node, NextValueExpr))\n\t{\n\t\treturn true;\n\t}\n\n\t/* check if the node contains call to 'nextval' */\n\tif (IsA(node, FuncExpr))\n\t{\n\t\tFuncExpr *funcExpr = (FuncExpr *) node;\n\n\t\tif (funcExpr->funcid == F_NEXTVAL)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn expression_tree_walker(node, contain_nextval_expression_walker, context);\n}\n\n\n/*\n * pg_get_replica_identity_command function returns the required ALTER .. TABLE\n * command to define the replica identity.\n */\nchar *\npg_get_replica_identity_command(Oid tableRelationId)\n{\n\tStringInfo buf = makeStringInfo();\n\n\tRelation relation = table_open(tableRelationId, AccessShareLock);\n\n\tchar replicaIdentity = relation->rd_rel->relreplident;\n\n\tchar *relationName = generate_qualified_relation_name(tableRelationId);\n\n\tif (replicaIdentity == REPLICA_IDENTITY_INDEX)\n\t{\n\t\tOid indexId = RelationGetReplicaIndex(relation);\n\n\t\tif (OidIsValid(indexId))\n\t\t{\n\t\t\tappendStringInfo(buf, \"ALTER TABLE %s REPLICA IDENTITY USING INDEX %s \",\n\t\t\t\t\t\t\t relationName,\n\t\t\t\t\t\t\t quote_identifier(get_rel_name(indexId)));\n\t\t}\n\t}\n\telse if (replicaIdentity == REPLICA_IDENTITY_NOTHING)\n\t{\n\t\tappendStringInfo(buf, \"ALTER TABLE %s REPLICA IDENTITY NOTHING\",\n\t\t\t\t\t\t relationName);\n\t}\n\telse if (replicaIdentity == REPLICA_IDENTITY_FULL)\n\t{\n\t\tappendStringInfo(buf, \"ALTER TABLE %s REPLICA IDENTITY FULL\",\n\t\t\t\t\t\t relationName);\n\t}\n\n\ttable_close(relation, AccessShareLock);\n\n\treturn (buf->len > 0) ? buf->data : NULL;\n}\n\n\n/*\n * pg_get_row_level_security_commands function returns the required ALTER .. TABLE\n * commands to define the row level security settings for a relation.\n */\nList *\npg_get_row_level_security_commands(Oid relationId)\n{\n\tStringInfoData buffer;\n\tList *commands = NIL;\n\n\tinitStringInfo(&buffer);\n\n\tRelation relation = table_open(relationId, AccessShareLock);\n\n\tif (relation->rd_rel->relrowsecurity)\n\t{\n\t\tchar *relationName = generate_qualified_relation_name(relationId);\n\n\t\tappendStringInfo(&buffer, \"ALTER TABLE %s ENABLE ROW LEVEL SECURITY\",\n\t\t\t\t\t\t relationName);\n\t\tcommands = lappend(commands, pstrdup(buffer.data));\n\t\tresetStringInfo(&buffer);\n\t}\n\n\tif (relation->rd_rel->relforcerowsecurity)\n\t{\n\t\tchar *relationName = generate_qualified_relation_name(relationId);\n\n\t\tappendStringInfo(&buffer, \"ALTER TABLE %s FORCE ROW LEVEL SECURITY\",\n\t\t\t\t\t\t relationName);\n\t\tcommands = lappend(commands, pstrdup(buffer.data));\n\t\tresetStringInfo(&buffer);\n\t}\n\n\ttable_close(relation, AccessShareLock);\n\n\treturn commands;\n}\n\n\n/*\n * Generate a C string representing a relation's reloptions, or NULL if none.\n *\n * This function comes from PostgreSQL source code in\n * src/backend/utils/adt/ruleutils.c\n */\nchar *\nflatten_reloptions(Oid relid)\n{\n\tchar *result = NULL;\n\tbool isnull;\n\n\tHeapTuple tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\telog(ERROR, \"cache lookup failed for relation %u\", relid);\n\t}\n\n\tDatum reloptions = SysCacheGetAttr(RELOID, tuple,\n\t\t\t\t\t\t\t\t\t   Anum_pg_class_reloptions, &isnull);\n\tif (!isnull)\n\t{\n\t\tStringInfoData buf;\n\t\tDatum *options;\n\t\tint noptions;\n\t\tint i;\n\n\t\tinitStringInfo(&buf);\n\n\t\tdeconstruct_array(DatumGetArrayTypeP(reloptions),\n\t\t\t\t\t\t  TEXTOID, -1, false, 'i',\n\t\t\t\t\t\t  &options, NULL, &noptions);\n\n\t\tfor (i = 0; i < noptions; i++)\n\t\t{\n\t\t\tchar *option = TextDatumGetCString(options[i]);\n\t\t\tchar *value;\n\n\t\t\t/*\n\t\t\t * Each array element should have the form name=value.  If the \"=\"\n\t\t\t * is missing for some reason, treat it like an empty value.\n\t\t\t */\n\t\t\tchar *name = option;\n\t\t\tchar *separator = strchr(option, '=');\n\t\t\tif (separator)\n\t\t\t{\n\t\t\t\t*separator = '\\0';\n\t\t\t\tvalue = separator + 1;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tvalue = \"\";\n\t\t\t}\n\n\t\t\tif (i > 0)\n\t\t\t{\n\t\t\t\tappendStringInfoString(&buf, \", \");\n\t\t\t}\n\t\t\tappendStringInfo(&buf, \"%s=\", quote_identifier(name));\n\n\t\t\t/*\n\t\t\t * In general we need to quote the value; but to avoid unnecessary\n\t\t\t * clutter, do not quote if it is an identifier that would not\n\t\t\t * need quoting.  (We could also allow numbers, but that is a bit\n\t\t\t * trickier than it looks --- for example, are leading zeroes\n\t\t\t * significant?  We don't want to assume very much here about what\n\t\t\t * custom reloptions might mean.)\n\t\t\t */\n\t\t\tif (quote_identifier(value) == value)\n\t\t\t{\n\t\t\t\tappendStringInfoString(&buf, value);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsimple_quote_literal(&buf, value);\n\t\t\t}\n\n\t\t\tpfree(option);\n\t\t}\n\n\t\tresult = buf.data;\n\t}\n\n\tReleaseSysCache(tuple);\n\n\treturn result;\n}\n\n\n/*\n * simple_quote_literal - Format a string as a SQL literal, append to buf\n *\n * This function comes from PostgreSQL source code in\n * src/backend/utils/adt/ruleutils.c\n */\nstatic void\nsimple_quote_literal(StringInfo buf, const char *val)\n{\n\t/*\n\t * We form the string literal according to the prevailing setting of\n\t * standard_conforming_strings; we never use E''. User is responsible for\n\t * making sure result is used correctly.\n\t */\n\tappendStringInfoChar(buf, '\\'');\n\tfor (const char *valptr = val; *valptr; valptr++)\n\t{\n\t\tchar ch = *valptr;\n\n\t\tif (SQL_STR_DOUBLE(ch, !standard_conforming_strings))\n\t\t{\n\t\t\tappendStringInfoChar(buf, ch);\n\t\t}\n\t\tappendStringInfoChar(buf, ch);\n\t}\n\tappendStringInfoChar(buf, '\\'');\n}\n\n\n/*\n * RoleSpecString resolves the role specification to its string form that is suitable for transport to a worker node.\n * This function resolves the following identifiers from the current context so they are safe to transfer.\n *\n * CURRENT_USER - resolved to the user name of the current role being used\n * SESSION_USER - resolved to the user name of the user that opened the session\n * CURRENT_ROLE - same as CURRENT_USER, resolved to the user name of the current role being used\n * Postgres treats CURRENT_ROLE is equivalent to CURRENT_USER, and we follow the same approach.\n *\n * withQuoteIdentifier is used, because if the results will be used in a query the quotes are needed but if not there\n * should not be extra quotes.\n */\nconst char *\nRoleSpecString(RoleSpec *spec, bool withQuoteIdentifier)\n{\n\tswitch (spec->roletype)\n\t{\n\t\tcase ROLESPEC_CSTRING:\n\t\t{\n\t\t\treturn withQuoteIdentifier ?\n\t\t\t\t   quote_identifier(spec->rolename) :\n\t\t\t\t   spec->rolename;\n\t\t}\n\n\t\tcase ROLESPEC_CURRENT_ROLE:\n\t\tcase ROLESPEC_CURRENT_USER:\n\t\t{\n\t\t\treturn withQuoteIdentifier ?\n\t\t\t\t   quote_identifier(GetUserNameFromId(GetUserId(), false)) :\n\t\t\t\t   GetUserNameFromId(GetUserId(), false);\n\t\t}\n\n\t\tcase ROLESPEC_SESSION_USER:\n\t\t{\n\t\t\treturn withQuoteIdentifier ?\n\t\t\t\t   quote_identifier(GetUserNameFromId(GetSessionUserId(), false)) :\n\t\t\t\t   GetUserNameFromId(GetSessionUserId(), false);\n\t\t}\n\n\t\tcase ROLESPEC_PUBLIC:\n\t\t{\n\t\t\treturn \"PUBLIC\";\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"unexpected role type %d\", spec->roletype);\n\t\t}\n\t}\n}\n\n\n/*\n * Recursively search an expression for a Param and return its paramid\n * Intended for indirection management: UPDATE SET () = (SELECT )\n * Does not cover all options but those supported by Citus.\n */\nstatic int\nGetParamId(Node *expr)\n{\n\tint paramid = 0;\n\n\tif (expr == NULL)\n\t{\n\t\treturn paramid;\n\t}\n\n\t/* If it's a Param, return its attnum */\n\tif (IsA(expr, Param))\n\t{\n\t\tParam *param = (Param *) expr;\n\t\tparamid = param->paramid;\n\t}\n\t/* If it's a FuncExpr, search in arguments */\n\telse if (IsA(expr, FuncExpr))\n\t{\n\t\tFuncExpr *func = (FuncExpr *) expr;\n\t\tListCell *lc;\n\n\t\tforeach(lc, func->args)\n\t\t{\n\t\t\tparamid = GetParamId((Node *) lfirst(lc));\n\t\t\tif (paramid != 0)\n\t\t\t{\n\t\t\t\tbreak; /* Stop at the first valid paramid */\n\t\t\t}\n\t\t}\n\t}\n\n\treturn paramid;\n}\n\n\n/*\n * list_sort comparator to sort target list by paramid (in MULTIEXPR)\n * Intended for indirection management: UPDATE SET () = (SELECT )\n */\nstatic int\ntarget_list_cmp(const ListCell *a, const ListCell *b)\n{\n\tTargetEntry *tleA = lfirst(a);\n\tTargetEntry *tleB = lfirst(b);\n\n\t/*\n\t * Deal with resjunk entries; sublinks are marked resjunk and\n\t * are placed at the end of the target list so this logic\n\t * ensures they stay grouped at the end of the target list:\n\t */\n\tif (tleA->resjunk || tleB->resjunk)\n\t{\n\t\treturn tleA->resjunk - tleB->resjunk;\n\t}\n\n\tint la = GetParamId((Node *) tleA->expr);\n\tint lb = GetParamId((Node *) tleB->expr);\n\n\t/*\n\t * Should be looking at legitimate param ids\n\t */\n\tAssert(la > 0);\n\tAssert(lb > 0);\n\n\t/*\n\t * Return -1, 0 or 1 depending on if la is less than,\n\t * equal to or greater than lb\n\t */\n\treturn (la > lb) - (la < lb);\n}\n\n\n/*\n * Used by get_update_query_targetlist_def() (in ruleutils) to reorder the target\n * list on the left side of the update:\n * SET () = (SELECT )\n * Reordering the SELECT side only does not work, consider a case like:\n * SET (col_1, col3) = (SELECT 1, 3), (col_2) = (SELECT 2)\n * Without ensure_update_targetlist_in_param_order(), this will lead to an incorrect\n * deparsed query:\n * SET (col_1, col2) = (SELECT 1, 3), (col_3) = (SELECT 2)\n */\nvoid\nensure_update_targetlist_in_param_order(List *targetList)\n{\n\tbool need_to_sort_target_list = false;\n\tint previous_paramid = 0;\n\tListCell *l;\n\n\tforeach(l, targetList)\n\t{\n\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\n\t\tif (!tle->resjunk)\n\t\t{\n\t\t\tint paramid = GetParamId((Node *) tle->expr);\n\t\t\tif (paramid < previous_paramid)\n\t\t\t{\n\t\t\t\tneed_to_sort_target_list = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tprevious_paramid = paramid;\n\t\t}\n\t}\n\n\tif (need_to_sort_target_list)\n\t{\n\t\tlist_sort(targetList, target_list_cmp);\n\t}\n}\n\n\n/*\n * isSubsRef checks if a given node is a SubscriptingRef or can be\n * reached through an implicit coercion.\n */\nstatic\nbool\nisSubsRef(Node *node)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, CoerceToDomain))\n\t{\n\t\tCoerceToDomain *coerceToDomain = (CoerceToDomain *) node;\n\t\tif (coerceToDomain->coercionformat != COERCE_IMPLICIT_CAST)\n\t\t{\n\t\t\t/* not an implicit coercion, cannot reach to a SubscriptingRef */\n\t\t\treturn false;\n\t\t}\n\n\t\tnode = (Node *) coerceToDomain->arg;\n\t}\n\n\treturn (IsA(node, SubscriptingRef));\n}\n\n\n/*\n * checkTlistForSubsRef - checks if any target entry in the list contains a\n * SubscriptingRef or can be reached through an implicit coercion. Used by\n * ExpandMergedSubscriptingRefEntries() to identify if any target entries\n * need to be expanded - if not the original target list is preserved.\n */\nstatic\nbool\ncheckTlistForSubsRef(List *targetEntryList)\n{\n\tListCell *tgtCell = NULL;\n\n\tforeach(tgtCell, targetEntryList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(tgtCell);\n\t\tExpr *expr = targetEntry->expr;\n\n\t\tif (isSubsRef((Node *) expr))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ExpandMergedSubscriptingRefEntries takes a list of target entries and expands\n * each one that references a SubscriptingRef node that indicates multiple (field)\n * updates on the same attribute, which is applicable for array/json types atm.\n */\nList *\nExpandMergedSubscriptingRefEntries(List *targetEntryList)\n{\n\tList *newTargetEntryList = NIL;\n\tListCell *tgtCell = NULL;\n\n\tif (!checkTlistForSubsRef(targetEntryList))\n\t{\n\t\t/* No subscripting refs found, return original list */\n\t\treturn targetEntryList;\n\t}\n\n\tforeach(tgtCell, targetEntryList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(tgtCell);\n\t\tList *expandedTargetEntries = NIL;\n\n\t\tExpr *expr = targetEntry->expr;\n\t\twhile (expr)\n\t\t{\n\t\t\tSubscriptingRef *subsRef = TargetEntryExprFindSubsRef(expr);\n\t\t\tif (!subsRef)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Remove refexpr from the SubscriptingRef that we are about to\n\t\t\t * wrap in a new TargetEntry and save it for the next one.\n\t\t\t */\n\t\t\tExpr *refexpr = subsRef->refexpr;\n\t\t\tsubsRef->refexpr = NULL;\n\n\t\t\t/*\n\t\t\t * Wrap the Expr that holds SubscriptingRef (directly or indirectly)\n\t\t\t * in a new TargetEntry; note that it doesn't have a refexpr anymore.\n\t\t\t */\n\t\t\tTargetEntry *newTargetEntry = copyObject(targetEntry);\n\t\t\tnewTargetEntry->expr = expr;\n\t\t\texpandedTargetEntries = lappend(expandedTargetEntries, newTargetEntry);\n\n\t\t\t/* now inspect the refexpr that SubscriptingRef at hand were holding */\n\t\t\texpr = refexpr;\n\t\t}\n\n\t\tif (expandedTargetEntries == NIL)\n\t\t{\n\t\t\t/* return original entry since it doesn't hold a SubscriptingRef node */\n\t\t\tnewTargetEntryList = lappend(newTargetEntryList, targetEntry);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Need to concat expanded target list entries in reverse order\n\t\t\t * to preserve ordering of the original target entry list.\n\t\t\t */\n\t\t\tList *reversedTgtEntries = NIL;\n\t\t\tListCell *revCell = NULL;\n\t\t\tforeach(revCell, expandedTargetEntries)\n\t\t\t{\n\t\t\t\tTargetEntry *tgtEntry = (TargetEntry *) lfirst(revCell);\n\t\t\t\treversedTgtEntries = lcons(tgtEntry, reversedTgtEntries);\n\t\t\t}\n\t\t\tnewTargetEntryList = list_concat(newTargetEntryList, reversedTgtEntries);\n\t\t}\n\t}\n\n\treturn newTargetEntryList;\n}\n\n\n/*\n * TargetEntryExprFindSubsRef searches given Expr --assuming that it is part\n * of a target list entry-- to see if it directly (i.e.: itself) or indirectly\n * (e.g.: behind some level of coercions) holds a SubscriptingRef node.\n *\n * Returns the original SubscriptingRef node on success or NULL otherwise.\n *\n * Note that it wouldn't add much value to use expression_tree_walker here\n * since we are only interested in a subset of the fields of a few certain\n * node types.\n */\nstatic SubscriptingRef *\nTargetEntryExprFindSubsRef(Expr *expr)\n{\n\tNode *node = (Node *) expr;\n\twhile (node)\n\t{\n\t\tif (IsA(node, FieldStore))\n\t\t{\n\t\t\t/*\n\t\t\t * ModifyPartialQuerySupported doesn't allow INSERT/UPDATE via\n\t\t\t * FieldStore. If we decide supporting such commands, then we\n\t\t\t * should take the first element of \"newvals\" list into account\n\t\t\t * here. This is because, to support such commands, we will need\n\t\t\t * to expand merged FieldStore into separate target entries too.\n\t\t\t *\n\t\t\t * For this reason, this block is not reachable atm and need to\n\t\t\t * uncomment the following if we decide supporting such commands.\n\t\t\t *\n\t\t\t * \"\"\"\n\t\t\t *   FieldStore *fieldStore = (FieldStore *) node;\n\t\t\t *   node = (Node *) linitial(fieldStore->newvals);\n\t\t\t * \"\"\"\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\"unexpectedly got FieldStore object when \"\n\t\t\t\t\t\t\t\t   \"generating shard query\")));\n\t\t}\n\t\telse if (IsA(node, CoerceToDomain))\n\t\t{\n\t\t\tCoerceToDomain *coerceToDomain = (CoerceToDomain *) node;\n\t\t\tif (coerceToDomain->coercionformat != COERCE_IMPLICIT_CAST)\n\t\t\t{\n\t\t\t\t/* not an implicit coercion, cannot reach to a SubscriptingRef */\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tnode = (Node *) coerceToDomain->arg;\n\t\t}\n\t\telse if (IsA(node, SubscriptingRef))\n\t\t{\n\t\t\treturn (SubscriptingRef *) node;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* got a node that we are not interested in */\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/citus_setutils.c",
    "content": "#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"commands/defrem.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/print.h\"\n#include \"parser/parse_type.h\"\n#include \"utils/builtins.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/log_utils.h\"\n\n\nvoid AppendVarSetValue(StringInfo buf, VariableSetStmt *setStmt);\n\n/*\n * AppendVarSetValueDb deparses a VariableSetStmt with VAR_SET_VALUE kind.\n * It takes from flatten_set_variable_args in postgres's utils/misc/guc.c,\n * however flatten_set_variable_args does not apply correct quoting.\n */\nvoid\nAppendVarSetValue(StringInfo buf, VariableSetStmt *setStmt)\n{\n\tListCell *varArgCell = NULL;\n\tListCell *firstCell = list_head(setStmt->args);\n\n\tAssert(setStmt->kind == VAR_SET_VALUE);\n\n\tforeach(varArgCell, setStmt->args)\n\t{\n\t\tNode *varArgNode = lfirst(varArgCell);\n\t\tA_Const *varArgConst = NULL;\n\t\tTypeName *typeName = NULL;\n\n\t\tif (IsA(varArgNode, A_Const))\n\t\t{\n\t\t\tvarArgConst = (A_Const *) varArgNode;\n\t\t}\n\t\telse if (IsA(varArgNode, TypeCast))\n\t\t{\n\t\t\tTypeCast *varArgTypeCast = (TypeCast *) varArgNode;\n\n\t\t\tvarArgConst = castNode(A_Const, varArgTypeCast->arg);\n\t\t\ttypeName = varArgTypeCast->typeName;\n\t\t}\n\t\telse\n\t\t{\n\t\t\telog(ERROR, \"unrecognized node type: %d\", varArgNode->type);\n\t\t}\n\n\t\t/* don't know how to start SET until we inspect first arg */\n\t\tif (varArgCell != firstCell)\n\t\t{\n\t\t\tappendStringInfoChar(buf, ',');\n\t\t}\n\t\telse if (typeName != NULL)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" SET TIME ZONE\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(buf, \" SET %s =\", quote_identifier(setStmt->name));\n\t\t}\n\n\t\tNode *value = (Node *) &varArgConst->val;\n\t\tswitch (value->type)\n\t\t{\n\t\t\tcase T_Integer:\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \" %d\", intVal(value));\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase T_Float:\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \" %s\", nodeToString(value));\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase T_String:\n\t\t\t{\n\t\t\t\tif (typeName != NULL)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Must be a ConstInterval argument for TIME ZONE. Coerce\n\t\t\t\t\t * to interval and back to normalize the value and account\n\t\t\t\t\t * for any typmod.\n\t\t\t\t\t */\n\t\t\t\t\tOid typoid = InvalidOid;\n\t\t\t\t\tint32 typmod = -1;\n\n\t\t\t\t\ttypenameTypeIdAndMod(NULL, typeName, &typoid, &typmod);\n\t\t\t\t\tAssert(typoid == INTERVALOID);\n\n\t\t\t\t\tDatum interval =\n\t\t\t\t\t\tDirectFunctionCall3(interval_in,\n\t\t\t\t\t\t\t\t\t\t\tCStringGetDatum(strVal(value)),\n\t\t\t\t\t\t\t\t\t\t\tObjectIdGetDatum(InvalidOid),\n\t\t\t\t\t\t\t\t\t\t\tInt32GetDatum(typmod));\n\n\t\t\t\t\tchar *intervalout =\n\t\t\t\t\t\tDatumGetCString(DirectFunctionCall1(interval_out,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tinterval));\n\t\t\t\t\tappendStringInfo(buf, \" INTERVAL '%s'\", intervalout);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tappendStringInfo(buf, \" %s\", quote_literal_cstr(strVal(value)));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\telog(ERROR, \"Unexpected Value type in VAR_SET_VALUE arguments.\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * AppendVariableSetDb appends a string representing the VariableSetStmt to a buffer\n */\nvoid\nAppendVariableSet(StringInfo buf, VariableSetStmt *setStmt)\n{\n\tswitch (setStmt->kind)\n\t{\n\t\tcase VAR_SET_VALUE:\n\t\t{\n\t\t\tAppendVarSetValue(buf, setStmt);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase VAR_SET_CURRENT:\n\t\t{\n\t\t\tappendStringInfo(buf, \" SET %s FROM CURRENT\", quote_identifier(\n\t\t\t\t\t\t\t\t setStmt->name));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase VAR_SET_DEFAULT:\n\t\t{\n\t\t\tappendStringInfo(buf, \" SET %s TO DEFAULT\", quote_identifier(setStmt->name));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase VAR_RESET:\n\t\t{\n\t\t\tappendStringInfo(buf, \" RESET %s\", quote_identifier(setStmt->name));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase VAR_RESET_ALL:\n\t\t{\n\t\t\tappendStringInfoString(buf, \" RESET ALL\");\n\t\t\tbreak;\n\t\t}\n\n\t\t/* VAR_SET_MULTI is a special case for SET TRANSACTION that should not occur here */\n\t\tcase VAR_SET_MULTI:\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"Unable to deparse SET statement\")));\n\t\t\tbreak;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse.c\n *    Entrypoint for deparsing parsetrees.\n *\n *    The goal of deparsing parsetrees is to reconstruct sql statements\n *    from any parsed sql statement by ParseTreeNode. Deparsed statements\n *    can be used to reapply them on remote postgres nodes like the citus\n *    workers.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\n/*\n * DeparseTreeNode aims to be the inverse of postgres' ParseTreeNode. Currently with\n * limited support. Check support before using, and add support for new statements as\n * required.\n */\nchar *\nDeparseTreeNode(Node *stmt)\n{\n\tconst DistributeObjectOps *ops = GetDistributeObjectOps(stmt);\n\n\tif (!ops->deparse)\n\t{\n\t\tereport(ERROR, (errmsg(\"unsupported statement for deparsing\")));\n\t}\n\n\treturn ops->deparse(stmt);\n}\n\n\n/*\n * DeparseTreeNodes deparses all stmts in the list from the statement datastructure into\n * sql statements.\n */\nList *\nDeparseTreeNodes(List *stmts)\n{\n\tList *sqls = NIL;\n\tNode *stmt = NULL;\n\tforeach_declared_ptr(stmt, stmts)\n\t{\n\t\tsqls = lappend(sqls, DeparseTreeNode(stmt));\n\t}\n\treturn sqls;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_attribute_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_attribute_stmts.c\n *\t  All routines to deparse attribute statements.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"distributed/deparser.h\"\n\nchar *\nDeparseRenameAttributeStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_ATTRIBUTE);\n\n\tswitch (stmt->relationType)\n\t{\n\t\tcase OBJECT_TYPE:\n\t\t{\n\t\t\treturn DeparseRenameTypeAttributeStmt(node);\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unsupported rename attribute statement for\"\n\t\t\t\t\t\t\t\t   \" deparsing\")));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_collation_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_collation_stmts.c\n *\t  All routines to deparse collation statements.\n *\t  This file contains all entry points specific for type statement deparsing as well as\n *\t  functions that are currently only used for deparsing of the type statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/value.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparser.h\"\n\nstatic void AppendDropCollationStmt(StringInfo buf, DropStmt *stmt);\nstatic void AppendRenameCollationStmt(StringInfo buf, RenameStmt *stmt);\nstatic void AppendAlterCollationSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);\nstatic void AppendAlterCollationOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);\nstatic void AppendNameList(StringInfo buf, List *objects);\n\n\n/*\n * DeparseDropCollationStmt builds and returns a string representing the DropStmt\n */\nchar *\nDeparseDropCollationStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->removeType == OBJECT_COLLATION);\n\n\tAppendDropCollationStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendDropCollationStmt appends a string representing the DropStmt to a buffer\n */\nstatic void\nAppendDropCollationStmt(StringInfo buf, DropStmt *stmt)\n{\n\tappendStringInfoString(buf, \"DROP COLLATION \");\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\tAppendNameList(buf, stmt->objects);\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n}\n\n\n/*\n * DeparseRenameCollationStmt builds and returns a string representing the RenameStmt\n */\nchar *\nDeparseRenameCollationStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->renameType == OBJECT_COLLATION);\n\n\tAppendRenameCollationStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendRenameCollationStmt appends a string representing the RenameStmt to a buffer\n */\nstatic void\nAppendRenameCollationStmt(StringInfo buf, RenameStmt *stmt)\n{\n\tList *names = (List *) stmt->object;\n\n\tappendStringInfo(buf, \"ALTER COLLATION %s RENAME TO %s;\",\n\t\t\t\t\t NameListToQuotedString(names),\n\t\t\t\t\t quote_identifier(stmt->newname));\n}\n\n\n/*\n * DeparseAlterCollationSchemaStmt builds and returns a string representing the AlterObjectSchemaStmt\n */\nchar *\nDeparseAlterCollationSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objectType == OBJECT_COLLATION);\n\n\tAppendAlterCollationSchemaStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterCollationSchemaStmt appends a string representing the AlterObjectSchemaStmt to a buffer\n */\nstatic void\nAppendAlterCollationSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt)\n{\n\tAssert(stmt->objectType == OBJECT_COLLATION);\n\n\tList *names = (List *) stmt->object;\n\tappendStringInfo(buf, \"ALTER COLLATION %s SET SCHEMA %s;\", NameListToQuotedString(\n\t\t\t\t\t\t names),\n\t\t\t\t\t quote_identifier(stmt->newschema));\n}\n\n\n/*\n * DeparseAlterCollationOwnerStmt builds and returns a string representing the AlterOwnerStmt\n */\nchar *\nDeparseAlterCollationOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objectType == OBJECT_COLLATION);\n\n\tAppendAlterCollationOwnerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterCollationOwnerStmt appends a string representing the AlterOwnerStmt to a buffer\n */\nstatic void\nAppendAlterCollationOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)\n{\n\tAssert(stmt->objectType == OBJECT_COLLATION);\n\n\tList *names = (List *) stmt->object;\n\tappendStringInfo(buf, \"ALTER COLLATION %s OWNER TO %s;\", NameListToQuotedString(\n\t\t\t\t\t\t names),\n\t\t\t\t\t RoleSpecString(stmt->newowner, true));\n}\n\n\nstatic void\nAppendNameList(StringInfo buf, List *objects)\n{\n\tListCell *objectCell = NULL;\n\n\tforeach(objectCell, objects)\n\t{\n\t\tList *name = castNode(List, lfirst(objectCell));\n\n\t\tif (objectCell != list_head(objects))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\n\t\tappendStringInfoString(buf, NameListToQuotedString(name));\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_comment_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_coment_stmts.c\n *\n *\t  All routines to deparse comment statements.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"commands/defrem.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"parser/parse_type.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/comment.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n\n\nconst char *ObjectTypeNames[] =\n{\n\t[OBJECT_DATABASE] = \"DATABASE\",\n\t[OBJECT_ROLE] = \"ROLE\",\n\t[OBJECT_TSCONFIGURATION] = \"TEXT SEARCH CONFIGURATION\",\n\t[OBJECT_TSDICTIONARY] = \"TEXT SEARCH DICTIONARY\",\n\n\t/* When support for propagating comments to new objects is introduced, an entry for each\n\t * statement type should be added to this list. The first element in each entry is the 'object_type' keyword\n\t * that will be included in the 'COMMENT ON <object_type> ..' statement (i.e. DATABASE,). The second element is the type of\n\t * stmt->object, which represents the name of the propagated object.\n\t */\n};\n\nchar *\nDeparseCommentStmt(Node *node)\n{\n\tCommentStmt *stmt = castNode(CommentStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tconst char *objectName = NULL;\n\tif (IsA(stmt->object, String))\n\t{\n\t\tobjectName = quote_identifier(strVal(stmt->object));\n\t}\n\telse if (IsA(stmt->object, List))\n\t{\n\t\tobjectName = NameListToQuotedString(castNode(List, stmt->object));\n\t}\n\telse\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t errmsg(\"unknown object type\")));\n\t}\n\n\tconst char *objectType = ObjectTypeNames[stmt->objtype];\n\n\tchar *comment = stmt->comment != NULL ? quote_literal_cstr(stmt->comment) : \"NULL\";\n\n\n\tappendStringInfo(&str, \"COMMENT ON %s %s IS %s;\", objectType, objectName, comment);\n\n\treturn str.data;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_database_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_database_stmts.c\n *\n *\t  All routines to deparse database statements.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"commands/defrem.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"parser/parse_type.h\"\n#include \"utils/builtins.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n\n\nstatic void AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);\nstatic void AppendAlterDatabaseSetStmt(StringInfo buf, AlterDatabaseSetStmt *stmt);\nstatic void AppendAlterDatabaseStmt(StringInfo buf, AlterDatabaseStmt *stmt);\nstatic void AppendCreateDatabaseStmt(StringInfo buf, CreatedbStmt *stmt);\nstatic void AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt);\nstatic void AppendGrantOnDatabaseStmt(StringInfo buf, GrantStmt *stmt);\nstatic void AppendBasicAlterDatabaseOptions(StringInfo buf, AlterDatabaseStmt *stmt);\nstatic void AppendGrantDatabases(StringInfo buf, GrantStmt *stmt);\nstatic void AppendAlterDatabaseSetTablespace(StringInfo buf, DefElem *def, char *dbname);\n\nconst DefElemOptionFormat createDatabaseOptionFormats[] = {\n\t{ \"owner\", \" OWNER %s\", OPTION_FORMAT_STRING },\n\t{ \"template\", \" TEMPLATE %s\", OPTION_FORMAT_STRING },\n\t{ \"encoding\", \" ENCODING %s\", OPTION_FORMAT_LITERAL_CSTR },\n\t{ \"strategy\", \" STRATEGY %s\", OPTION_FORMAT_LITERAL_CSTR },\n\t{ \"locale\", \" LOCALE %s\", OPTION_FORMAT_LITERAL_CSTR },\n\t{ \"lc_collate\", \" LC_COLLATE %s\", OPTION_FORMAT_LITERAL_CSTR },\n\t{ \"lc_ctype\", \" LC_CTYPE %s\", OPTION_FORMAT_LITERAL_CSTR },\n\t{ \"icu_locale\", \" ICU_LOCALE %s\", OPTION_FORMAT_LITERAL_CSTR },\n\t{ \"icu_rules\", \" ICU_RULES %s\", OPTION_FORMAT_LITERAL_CSTR },\n\t{ \"locale_provider\", \" LOCALE_PROVIDER %s\", OPTION_FORMAT_LITERAL_CSTR },\n\t{ \"collation_version\", \" COLLATION_VERSION %s\", OPTION_FORMAT_LITERAL_CSTR },\n\t{ \"tablespace\", \" TABLESPACE %s\", OPTION_FORMAT_STRING },\n\t{ \"allow_connections\", \" ALLOW_CONNECTIONS %s\", OPTION_FORMAT_BOOLEAN },\n\t{ \"connection_limit\", \" CONNECTION LIMIT %d\", OPTION_FORMAT_INTEGER },\n\t{ \"is_template\", \" IS_TEMPLATE %s\", OPTION_FORMAT_BOOLEAN }\n};\n\n\nconst DefElemOptionFormat alterDatabaseOptionFormats[] = {\n\t{ \"is_template\", \" IS_TEMPLATE %s\", OPTION_FORMAT_BOOLEAN },\n\t{ \"allow_connections\", \" ALLOW_CONNECTIONS %s\", OPTION_FORMAT_BOOLEAN },\n\t{ \"connection_limit\", \" CONNECTION LIMIT %d\", OPTION_FORMAT_INTEGER },\n};\n\n\nchar *\nDeparseAlterDatabaseOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objectType == OBJECT_DATABASE);\n\n\tAppendAlterDatabaseOwnerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)\n{\n\tAssert(stmt->objectType == OBJECT_DATABASE);\n\n\tappendStringInfo(buf,\n\t\t\t\t\t \"ALTER DATABASE %s OWNER TO %s;\",\n\t\t\t\t\t quote_identifier(strVal((String *) stmt->object)),\n\t\t\t\t\t RoleSpecString(stmt->newowner, true));\n}\n\n\nstatic void\nAppendGrantDatabases(StringInfo buf, GrantStmt *stmt)\n{\n\tListCell *cell = NULL;\n\tappendStringInfo(buf, \" ON DATABASE \");\n\n\tforeach(cell, stmt->objects)\n\t{\n\t\tchar *database = strVal(lfirst(cell));\n\t\tappendStringInfoString(buf, quote_identifier(database));\n\t\tif (cell != list_tail(stmt->objects))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\t}\n}\n\n\nstatic void\nAppendGrantOnDatabaseStmt(StringInfo buf, GrantStmt *stmt)\n{\n\tAssert(stmt->objtype == OBJECT_DATABASE);\n\n\tAppendGrantSharedPrefix(buf, stmt);\n\n\tAppendGrantDatabases(buf, stmt);\n\n\tAppendGrantSharedSuffix(buf, stmt);\n}\n\n\nstatic void\nAppendAlterDatabaseStmt(StringInfo buf, AlterDatabaseStmt *stmt)\n{\n\tif (list_length(stmt->options) == 0)\n\t{\n\t\telog(ERROR, \"got unexpected number of options for ALTER DATABASE\");\n\t}\n\n\tif (stmt->options)\n\t{\n\t\tDefElem *firstOption = linitial(stmt->options);\n\t\tif (strcmp(firstOption->defname, \"tablespace\") == 0)\n\t\t{\n\t\t\tAppendAlterDatabaseSetTablespace(buf, firstOption, stmt->dbname);\n\n\t\t\t/* SET tablespace cannot be combined with other options */\n\t\t\treturn;\n\t\t}\n\n\n\t\tappendStringInfo(buf, \"ALTER DATABASE %s WITH\",\n\t\t\t\t\t\t quote_identifier(stmt->dbname));\n\n\t\tAppendBasicAlterDatabaseOptions(buf, stmt);\n\t}\n\n\tappendStringInfo(buf, \";\");\n}\n\n\nstatic void\nAppendAlterDatabaseSetTablespace(StringInfo buf, DefElem *def, char *dbname)\n{\n\tappendStringInfo(buf,\n\t\t\t\t\t \"ALTER DATABASE %s SET TABLESPACE %s\",\n\t\t\t\t\t quote_identifier(dbname), quote_identifier(defGetString(def)));\n}\n\n\n/*\n * AppendBasicAlterDatabaseOptions appends basic ALTER DATABASE options to a string buffer.\n * Basic options are those that can be appended to the ALTER DATABASE statement\n * after the \"WITH\" keyword.(i.e. ALLOW_CONNECTIONS, CONNECTION LIMIT, IS_TEMPLATE)\n * For example, the tablespace option is not a basic option since it is defined via SET keyword.\n *\n * This function takes a string buffer and an AlterDatabaseStmt as input.\n * It appends the basic options to the string buffer.\n *\n */\nstatic void\nAppendBasicAlterDatabaseOptions(StringInfo buf, AlterDatabaseStmt *stmt)\n{\n\tDefElem *def = NULL;\n\tforeach_declared_ptr(def, stmt->options)\n\t{\n\t\tDefElemOptionToStatement(buf, def, alterDatabaseOptionFormats, lengthof(\n\t\t\t\t\t\t\t\t\t alterDatabaseOptionFormats));\n\t}\n}\n\n\nchar *\nDeparseGrantOnDatabaseStmt(Node *node)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_DATABASE);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendGrantOnDatabaseStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterDatabaseStmt(Node *node)\n{\n\tAlterDatabaseStmt *stmt = castNode(AlterDatabaseStmt, node);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendAlterDatabaseStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterDatabaseRefreshCollStmt(Node *node)\n{\n\tAlterDatabaseRefreshCollStmt *stmt = (AlterDatabaseRefreshCollStmt *) node;\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tappendStringInfo(&str, \"ALTER DATABASE %s REFRESH COLLATION VERSION;\",\n\t\t\t\t\t quote_identifier(\n\t\t\t\t\t\t stmt->dbname));\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendAlterDatabaseSetStmt(StringInfo buf, AlterDatabaseSetStmt *stmt)\n{\n\tappendStringInfo(buf, \"ALTER DATABASE %s\", quote_identifier(stmt->dbname));\n\n\tVariableSetStmt *varSetStmt = castNode(VariableSetStmt, stmt->setstmt);\n\n\tAppendVariableSet(buf, varSetStmt);\n}\n\n\nchar *\nDeparseAlterDatabaseRenameStmt(Node *node)\n{\n\tRenameStmt *stmt = (RenameStmt *) node;\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tappendStringInfo(&str, \"ALTER DATABASE %s RENAME TO %s\",\n\t\t\t\t\t quote_identifier(stmt->subname),\n\t\t\t\t\t quote_identifier(stmt->newname));\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterDatabaseSetStmt(Node *node)\n{\n\tAlterDatabaseSetStmt *stmt = castNode(AlterDatabaseSetStmt, node);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendAlterDatabaseSetStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendCreateDatabaseStmt(StringInfo buf, CreatedbStmt *stmt)\n{\n\t/*\n\t * Make sure that we don't try to deparse something that this\n\t * function doesn't expect.\n\t *\n\t * This is also useful to throw an error for unsupported CREATE\n\t * DATABASE options when the command is issued from non-main dbs\n\t * because we use the same function to deparse CREATE DATABASE\n\t * commands there too.\n\t */\n\tEnsureSupportedCreateDatabaseCommand(stmt);\n\n\tappendStringInfo(buf,\n\t\t\t\t\t \"CREATE DATABASE %s\",\n\t\t\t\t\t quote_identifier(stmt->dbname));\n\n\tDefElem *option = NULL;\n\tforeach_declared_ptr(option, stmt->options)\n\t{\n\t\tDefElemOptionToStatement(buf, option, createDatabaseOptionFormats,\n\t\t\t\t\t\t\t\t lengthof(createDatabaseOptionFormats));\n\t}\n}\n\n\nchar *\nDeparseCreateDatabaseStmt(Node *node)\n{\n\tCreatedbStmt *stmt = castNode(CreatedbStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendCreateDatabaseStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt)\n{\n\tchar *ifExistsStatement = stmt->missing_ok ? \"IF EXISTS\" : \"\";\n\tappendStringInfo(buf,\n\t\t\t\t\t \"DROP DATABASE %s %s\",\n\t\t\t\t\t ifExistsStatement,\n\t\t\t\t\t quote_identifier(stmt->dbname));\n\n\tif (list_length(stmt->options) > 1)\n\t{\n\t\t/* FORCE is the only option that can be provided for this command */\n\t\telog(ERROR, \"got unexpected number of options for DROP DATABASE\");\n\t}\n\telse if (list_length(stmt->options) == 1)\n\t{\n\t\tDefElem *option = linitial(stmt->options);\n\t\tappendStringInfo(buf, \" WITH ( \");\n\n\t\tif (strcmp(option->defname, \"force\") == 0)\n\t\t{\n\t\t\tappendStringInfo(buf, \"FORCE\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* FORCE is the only option that can be provided for this command */\n\t\t\tereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t\t\terrmsg(\"unrecognized DROP DATABASE option \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t   option->defname)));\n\t\t}\n\n\t\tappendStringInfo(buf, \" )\");\n\t}\n}\n\n\nchar *\nDeparseDropDatabaseStmt(Node *node)\n{\n\tDropdbStmt *stmt = castNode(DropdbStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendDropDatabaseStmt(&str, stmt);\n\n\treturn str.data;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_domain_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_domain_stmts.c\n *    Functions to turn all Statement structures related to domains back\n *    into sql.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/htup_details.h\"\n#include \"catalog/heap.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"parser/parse_coerce.h\"\n#include \"parser/parse_collate.h\"\n#include \"parser/parse_expr.h\"\n#include \"parser/parse_node.h\"\n#include \"parser/parse_type.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/ruleutils.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/namespace_utils.h\"\n\nstatic void AppendConstraint(StringInfo buf, Constraint *constraint, List *domainName,\n\t\t\t\t\t\t\t TypeName *typeName);\nstatic Node * replace_domain_constraint_value(ParseState *pstate, ColumnRef *cref);\nstatic Node * TransformDefaultExpr(Node *expr, List *domainName, TypeName *typeName);\nstatic Node * TransformConstraintExpr(Node *expr, TypeName *typeName);\nstatic CoerceToDomainValue * GetCoerceDomainValue(TypeName *typeName);\nstatic char * TypeNameAsIdentifier(TypeName *typeName);\n\nstatic Oid DomainGetBaseTypeOid(List *names, int32 *baseTypeMod);\n\nstatic void AppendAlterDomainStmtSetDefault(StringInfo buf, AlterDomainStmt *stmt);\nstatic void AppendAlterDomainStmtAddConstraint(StringInfo buf, AlterDomainStmt *stmt);\nstatic void AppendAlterDomainStmtDropConstraint(StringInfo buf, AlterDomainStmt *stmt);\n\n\n/*\n * DeparseCreateDomainStmt returns the sql representation for the CREATE DOMAIN statement.\n */\nchar *\nDeparseCreateDomainStmt(Node *node)\n{\n\tCreateDomainStmt *stmt = castNode(CreateDomainStmt, node);\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tconst char *domainIdentifier = NameListToQuotedString(stmt->domainname);\n\tconst char *typeIdentifier = TypeNameAsIdentifier(stmt->typeName);\n\tappendStringInfo(&buf, \"CREATE DOMAIN %s AS %s\", domainIdentifier, typeIdentifier);\n\n\tif (stmt->collClause)\n\t{\n\t\tconst char *collateIdentifier =\n\t\t\tNameListToQuotedString(stmt->collClause->collname);\n\t\tappendStringInfo(&buf, \" COLLATE %s\", collateIdentifier);\n\t}\n\n\tConstraint *constraint = NULL;\n\tforeach_declared_ptr(constraint, stmt->constraints)\n\t{\n\t\tAppendConstraint(&buf, constraint, stmt->domainname, stmt->typeName);\n\t}\n\n\tappendStringInfoString(&buf, \";\");\n\n\treturn buf.data;\n}\n\n\n/*\n * TypeNameAsIdentifier returns the sql identifier of a TypeName. This is more complex\n * than concatenating the schema name and typename since certain types contain modifiers\n * that need to be correctly represented.\n */\nstatic char *\nTypeNameAsIdentifier(TypeName *typeName)\n{\n\tint32 typmod = 0;\n\tOid typeOid = InvalidOid;\n\tbits16 formatFlags = FORMAT_TYPE_TYPEMOD_GIVEN | FORMAT_TYPE_FORCE_QUALIFY;\n\n\ttypenameTypeIdAndMod(NULL, typeName, &typeOid, &typmod);\n\n\treturn format_type_extended(typeOid, typmod, formatFlags);\n}\n\n\n/*\n * DeparseDropDomainStmt returns the sql for teh DROP DOMAIN statement.\n */\nchar *\nDeparseDropDomainStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tappendStringInfoString(&buf, \"DROP DOMAIN \");\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(&buf, \"IF EXISTS \");\n\t}\n\n\tTypeName *domainName = NULL;\n\tbool first = true;\n\tforeach_declared_ptr(domainName, stmt->objects)\n\t{\n\t\tif (!first)\n\t\t{\n\t\t\tappendStringInfoString(&buf, \", \");\n\t\t}\n\t\tfirst = false;\n\n\t\tconst char *identifier = NameListToQuotedString(domainName->names);\n\t\tappendStringInfoString(&buf, identifier);\n\t}\n\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(&buf, \" CASCADE\");\n\t}\n\n\tappendStringInfoString(&buf, \";\");\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseAlterDomainStmt returns the sql representation of the DOMAIN specific ALTER\n * statements.\n */\nchar *\nDeparseAlterDomainStmt(Node *node)\n{\n\tAlterDomainStmt *stmt = castNode(AlterDomainStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tappendStringInfo(&buf, \"ALTER DOMAIN %s \", NameListToQuotedString(stmt->typeName));\n\tswitch (stmt->subtype)\n\t{\n\t\tcase 'T': /* SET DEFAULT */\n\t\t{\n\t\t\tAppendAlterDomainStmtSetDefault(&buf, stmt);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 'N': /* DROP NOT NULL */\n\t\t{\n\t\t\tappendStringInfoString(&buf, \"DROP NOT NULL\");\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 'O': /* SET NOT NULL */\n\t\t{\n\t\t\tappendStringInfoString(&buf, \"SET NOT NULL\");\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 'C': /* ADD [CONSTRAINT name] */\n\t\t{\n\t\t\tAppendAlterDomainStmtAddConstraint(&buf, stmt);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 'X': /* DROP CONSTRAINT */\n\t\t{\n\t\t\tAppendAlterDomainStmtDropConstraint(&buf, stmt);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 'V': /* VALIDATE CONSTRAINT */\n\t\t{\n\t\t\tappendStringInfo(&buf, \"VALIDATE CONSTRAINT %s\",\n\t\t\t\t\t\t\t quote_identifier(stmt->name));\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"unsupported alter domain statement for distribution\");\n\t\t}\n\t}\n\n\tappendStringInfoChar(&buf, ';');\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseDomainRenameConstraintStmt returns the sql representation of the domain\n * constraint renaming.\n */\nchar *\nDeparseDomainRenameConstraintStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tchar *domainIdentifier = NameListToQuotedString(castNode(List, stmt->object));\n\tappendStringInfo(&buf, \"ALTER DOMAIN %s RENAME CONSTRAINT %s TO %s;\",\n\t\t\t\t\t domainIdentifier,\n\t\t\t\t\t quote_identifier(stmt->subname),\n\t\t\t\t\t quote_identifier(stmt->newname));\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseAlterDomainOwnerStmt returns the sql representation of the ALTER DOMAIN OWNER\n * statement.\n */\nchar *\nDeparseAlterDomainOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tList *domainName = castNode(List, stmt->object);\n\tchar *domainIdentifier = NameListToQuotedString(domainName);\n\tappendStringInfo(&buf, \"ALTER DOMAIN %s OWNER TO %s;\",\n\t\t\t\t\t domainIdentifier,\n\t\t\t\t\t RoleSpecString(stmt->newowner, true));\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseRenameDomainStmt returns the sql representation of the ALTER DOMAIN RENAME\n * statement.\n */\nchar *\nDeparseRenameDomainStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tList *domainName = castNode(List, stmt->object);\n\tchar *domainIdentifier = NameListToQuotedString(domainName);\n\tappendStringInfo(&buf, \"ALTER DOMAIN %s RENAME TO %s;\",\n\t\t\t\t\t domainIdentifier,\n\t\t\t\t\t quote_identifier(stmt->newname));\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseAlterDomainSchemaStmt returns the sql representation of the ALTER DOMAIN SET\n * SCHEMA statement.\n */\nchar *\nDeparseAlterDomainSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tList *domainName = castNode(List, stmt->object);\n\tchar *domainIdentifier = NameListToQuotedString(domainName);\n\tappendStringInfo(&buf, \"ALTER DOMAIN %s SET SCHEMA %s;\",\n\t\t\t\t\t domainIdentifier,\n\t\t\t\t\t quote_identifier(stmt->newschema));\n\n\treturn buf.data;\n}\n\n\n/*\n * DomainGetBaseTypeOid returns the type Oid and the type modifiers of the type underlying\n * a domain addresses by the namelist provided as the names argument. The type modifier is\n * only provided if the baseTypeMod pointer is a valid pointer on where to write the\n * modifier (not a NULL pointer).\n *\n * If the type cannot be found this function will raise a non-userfacing error. Care needs\n * to be taken by the caller that the domain is actually existing.\n */\nstatic Oid\nDomainGetBaseTypeOid(List *names, int32 *baseTypeMod)\n{\n\tTypeName *domainName = makeTypeNameFromNameList(names);\n\tOid domainoid = typenameTypeId(NULL, domainName);\n\tHeapTuple tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(domainoid));\n\tif (!HeapTupleIsValid(tup))\n\t{\n\t\telog(ERROR, \"cache lookup failed for type %u\", domainoid);\n\t}\n\tForm_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);\n\tOid baseTypeOid = typTup->typbasetype;\n\tif (baseTypeMod)\n\t{\n\t\t*baseTypeMod = typTup->typtypmod;\n\t}\n\tReleaseSysCache(tup);\n\treturn baseTypeOid;\n}\n\n\n/*\n * AppendAlterDomainStmtSetDefault is a helper function that appends the default value\n * portion of an ALTER DOMAIN statement that is changing the default value of the domain.\n */\nstatic void\nAppendAlterDomainStmtSetDefault(StringInfo buf, AlterDomainStmt *stmt)\n{\n\tif (stmt->def == NULL)\n\t{\n\t\t/* no default expression is a DROP DEFAULT statment */\n\t\tappendStringInfoString(buf, \"DROP DEFAULT\");\n\t\treturn;\n\t}\n\n\tint32 baseTypMod = 0;\n\tOid baseOid = DomainGetBaseTypeOid(stmt->typeName, &baseTypMod);\n\tTypeName *baseTypeName = makeTypeNameFromOid(baseOid, baseTypMod);\n\n\t/* cook the default expression, without cooking we can't deparse */\n\tNode *expr = stmt->def;\n\texpr = TransformDefaultExpr(expr, stmt->typeName, baseTypeName);\n\n\t/* deparse while the searchpath is cleared to force qualification of identifiers */\n\tint saveNestLevel = PushEmptySearchPath();\n\tchar *exprSql = deparse_expression(expr, NIL, true, true);\n\tPopEmptySearchPath(saveNestLevel);\n\n\tappendStringInfo(buf, \"SET DEFAULT %s\", exprSql);\n}\n\n\n/*\n * AppendAlterDomainStmtAddConstraint is a helper function that appends the constraint\n * specification for an ALTER DOMAIN statement that adds a constraint to the domain.\n */\nstatic void\nAppendAlterDomainStmtAddConstraint(StringInfo buf, AlterDomainStmt *stmt)\n{\n\tif (stmt->def == NULL || !IsA(stmt->def, Constraint))\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to deparse ALTER DOMAIN statement due to \"\n\t\t\t\t\t\t\t   \"unexpected contents\")));\n\t}\n\n\tConstraint *constraint = castNode(Constraint, stmt->def);\n\tappendStringInfoString(buf, \"ADD\");\n\n\tint32 baseTypMod = 0;\n\tOid baseOid = DomainGetBaseTypeOid(stmt->typeName, &baseTypMod);\n\tTypeName *baseTypeName = makeTypeNameFromOid(baseOid, baseTypMod);\n\n\tAppendConstraint(buf, constraint, stmt->typeName, baseTypeName);\n\n\tif (!constraint->initially_valid)\n\t{\n\t\tappendStringInfoString(buf, \" NOT VALID\");\n\t}\n}\n\n\n/*\n * AppendAlterDomainStmtDropConstraint is a helper function that appends the DROP\n * CONSTRAINT part of an ALTER DOMAIN statement for an alter statement that drops a\n * constraint.\n */\nstatic void\nAppendAlterDomainStmtDropConstraint(StringInfo buf, AlterDomainStmt *stmt)\n{\n\tappendStringInfoString(buf, \"DROP CONSTRAINT \");\n\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\n\tappendStringInfoString(buf, quote_identifier(stmt->name));\n\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n}\n\n\n/*\n * AppendConstraint is a helper function that appends a constraint specification to a sql\n * string that is adding a constraint.\n *\n * There are multiple places where a constraint specification is appended to sql strings.\n *\n * Given the complexities of serializing a constraint they all use this routine.\n */\nstatic void\nAppendConstraint(StringInfo buf, Constraint *constraint, List *domainName,\n\t\t\t\t TypeName *typeName)\n{\n\tif (constraint->conname)\n\t{\n\t\tappendStringInfo(buf, \" CONSTRAINT %s\", quote_identifier(constraint->conname));\n\t}\n\n\tswitch (constraint->contype)\n\t{\n\t\tcase CONSTR_CHECK:\n\t\t{\n\t\t\tNode *expr = NULL;\n\t\t\tif (constraint->raw_expr)\n\t\t\t{\n\t\t\t\t/* the expression was parsed from sql, still needs to transform */\n\t\t\t\texpr = TransformConstraintExpr(constraint->raw_expr, typeName);\n\t\t\t}\n\t\t\telse if (constraint->cooked_expr)\n\t\t\t{\n\t\t\t\t/* expression was read from the catalog, no cooking required just parse */\n\t\t\t\texpr = stringToNode(constraint->cooked_expr);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\telog(ERROR, \"missing expression for domain constraint\");\n\t\t\t}\n\n\t\t\tint saveNestLevel = PushEmptySearchPath();\n\t\t\tchar *exprSql = deparse_expression(expr, NIL, true, true);\n\t\t\tPopEmptySearchPath(saveNestLevel);\n\n\t\t\tappendStringInfo(buf, \" CHECK (%s)\", exprSql);\n\t\t\treturn;\n\t\t}\n\n\t\tcase CONSTR_DEFAULT:\n\t\t{\n\t\t\tNode *expr = NULL;\n\t\t\tif (constraint->raw_expr)\n\t\t\t{\n\t\t\t\t/* the expression was parsed from sql, still needs to transform */\n\t\t\t\texpr = TransformDefaultExpr(constraint->raw_expr, domainName, typeName);\n\t\t\t}\n\t\t\telse if (constraint->cooked_expr)\n\t\t\t{\n\t\t\t\t/* expression was read from the catalog, no cooking required just parse */\n\t\t\t\texpr = stringToNode(constraint->cooked_expr);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\telog(ERROR, \"missing expression for domain default\");\n\t\t\t}\n\n\t\t\tint saveNestLevel = PushEmptySearchPath();\n\t\t\tchar *exprSql = deparse_expression(expr, NIL, true, true);\n\t\t\tPopEmptySearchPath(saveNestLevel);\n\n\t\t\tappendStringInfo(buf, \" DEFAULT %s\", exprSql);\n\t\t\treturn;\n\t\t}\n\n\t\tcase CONSTR_NOTNULL:\n\t\t{\n\t\t\tappendStringInfoString(buf, \" NOT NULL\");\n\t\t\treturn;\n\t\t}\n\n\t\tcase CONSTR_NULL:\n\t\t{\n\t\t\tappendStringInfoString(buf, \" NULL\");\n\t\t\treturn;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unsupported constraint for distributed domain\")));\n\t\t}\n\t}\n}\n\n\n/*\n * TransformDefaultExpr transforms a default expression from the expression passed on the\n * AST to a cooked version that postgres uses internally.\n *\n * Only the cooked version can be easily turned back into a sql string, hence its use in\n * the deparser. This is only called for default expressions that don't have a cooked\n * variant stored.\n */\nstatic Node *\nTransformDefaultExpr(Node *expr, List *domainName, TypeName *typeName)\n{\n\tconst char *domainNameStr = NameListToQuotedString(domainName);\n\tint32 basetypeMod = 0; /* capture typeMod during lookup */\n\tType tup = typenameType(NULL, typeName, &basetypeMod);\n\tOid basetypeoid = typeTypeId(tup);\n\n\tReleaseSysCache(tup);\n\n\tParseState *pstate = make_parsestate(NULL);\n\tNode *defaultExpr = cookDefault(pstate, expr,\n\t\t\t\t\t\t\t\t\tbasetypeoid,\n\t\t\t\t\t\t\t\t\tbasetypeMod,\n\t\t\t\t\t\t\t\t\tdomainNameStr,\n\t\t\t\t\t\t\t\t\t0);\n\n\treturn defaultExpr;\n}\n\n\n/*\n * TransformConstraintExpr transforms a constraint expression from the expression passed\n * on the AST to a cooked version that postgres uses internally.\n *\n * Only the cooked version can be easily turned back into a sql string, hence its use in\n * the deparser. This is only called for default expressions that don't have a cooked\n * variant stored.\n */\nstatic Node *\nTransformConstraintExpr(Node *expr, TypeName *typeName)\n{\n\t/*\n\t * Convert the A_EXPR in raw_expr into an EXPR\n\t */\n\tParseState *pstate = make_parsestate(NULL);\n\n\t/*\n\t * Set up a CoerceToDomainValue to represent the occurrence of VALUE in\n\t * the expression.  Note that it will appear to have the type of the base\n\t * type, not the domain.  This seems correct since within the check\n\t * expression, we should not assume the input value can be considered a\n\t * member of the domain.\n\t */\n\n\tCoerceToDomainValue *domVal = GetCoerceDomainValue(typeName);\n\n\tpstate->p_pre_columnref_hook = replace_domain_constraint_value;\n\tpstate->p_ref_hook_state = (void *) domVal;\n\n\texpr = transformExpr(pstate, expr, EXPR_KIND_DOMAIN_CHECK);\n\n\t/*\n\t * Make sure it yields a boolean result.\n\t */\n\texpr = coerce_to_boolean(pstate, expr, \"CHECK\");\n\n\t/*\n\t * Fix up collation information.\n\t */\n\tassign_expr_collations(pstate, expr);\n\n\treturn expr;\n}\n\n\n/*\n * GetCoerceDomainValue creates a stub CoerceToDomainValue struct representing the type\n * referenced by the typeName.\n */\nstatic CoerceToDomainValue *\nGetCoerceDomainValue(TypeName *typeName)\n{\n\tint32 typMod = 0; /* capture typeMod during lookup */\n\tType tup = LookupTypeName(NULL, typeName, &typMod, false);\n\tif (tup == NULL)\n\t{\n\t\telog(ERROR, \"unable to lookup type information for %s\",\n\t\t\t NameListToQuotedString(typeName->names));\n\t}\n\n\tCoerceToDomainValue *domVal = makeNode(CoerceToDomainValue);\n\tdomVal->typeId = typeTypeId(tup);\n\tdomVal->typeMod = typMod;\n\tdomVal->collation = typeTypeCollation(tup);\n\tdomVal->location = -1;\n\n\tReleaseSysCache(tup);\n\treturn domVal;\n}\n\n\n/* Parser pre_columnref_hook for domain CHECK constraint parsing */\nstatic Node *\nreplace_domain_constraint_value(ParseState *pstate, ColumnRef *cref)\n{\n\t/*\n\t * Check for a reference to \"value\", and if that's what it is, replace\n\t * with a CoerceToDomainValue as prepared for us by domainAddConstraint.\n\t * (We handle VALUE as a name, not a keyword, to avoid breaking a lot of\n\t * applications that have used VALUE as a column name in the past.)\n\t */\n\tif (list_length(cref->fields) == 1)\n\t{\n\t\tNode *field1 = (Node *) linitial(cref->fields);\n\t\tAssert(IsA(field1, String));\n\t\tchar *colname = strVal(field1);\n\n\t\tif (strcmp(colname, \"value\") == 0)\n\t\t{\n\t\t\tCoerceToDomainValue *domVal = copyObject(pstate->p_ref_hook_state);\n\n\t\t\t/* Propagate location knowledge, if any */\n\t\t\tdomVal->location = cref->location;\n\t\t\treturn (Node *) domVal;\n\t\t}\n\t}\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_extension_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_extension_stmts.c\n *\t  All routines to deparse extension statements.\n *\t  This file contains deparse functions for extension statement deparsing\n *    as well as related helper functions.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"commands/defrem.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\n/* Local functions forward declarations for helper functions */\nstatic void AppendCreateExtensionStmt(StringInfo buf, CreateExtensionStmt *stmt);\nstatic void AppendCreateExtensionStmtOptions(StringInfo buf, List *options);\nstatic void AppendDropExtensionStmt(StringInfo buf, DropStmt *stmt);\nstatic void AppendExtensionNameList(StringInfo buf, List *objects);\nstatic void AppendAlterExtensionSchemaStmt(StringInfo buf,\n\t\t\t\t\t\t\t\t\t\t   AlterObjectSchemaStmt *\n\t\t\t\t\t\t\t\t\t\t   alterExtensionSchemaStmt);\nstatic void AppendAlterExtensionStmt(StringInfo buf,\n\t\t\t\t\t\t\t\t\t AlterExtensionStmt *alterExtensionStmt);\n\n\n/*\n * GetExtensionOption returns DefElem * node with \"defname\" from \"options\" list\n */\nDefElem *\nGetExtensionOption(List *extensionOptions, const char *defname)\n{\n\tDefElem *defElement = NULL;\n\tforeach_declared_ptr(defElement, extensionOptions)\n\t{\n\t\tif (IsA(defElement, DefElem) &&\n\t\t\tstrncmp(defElement->defname, defname, NAMEDATALEN) == 0)\n\t\t{\n\t\t\treturn defElement;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * DeparseCreateExtensionStmt builds and returns a string representing the\n * CreateExtensionStmt to be sent to worker nodes.\n */\nchar *\nDeparseCreateExtensionStmt(Node *node)\n{\n\tCreateExtensionStmt *stmt = castNode(CreateExtensionStmt, node);\n\tStringInfoData sql = { 0 };\n\tinitStringInfo(&sql);\n\n\tAppendCreateExtensionStmt(&sql, stmt);\n\n\treturn sql.data;\n}\n\n\n/*\n * AppendCreateExtensionStmt appends a string representing the CreateExtensionStmt to a buffer\n */\nstatic void\nAppendCreateExtensionStmt(StringInfo buf, CreateExtensionStmt *createExtensionStmt)\n{\n\tappendStringInfoString(buf, \"CREATE EXTENSION \");\n\n\tif (createExtensionStmt->if_not_exists)\n\t{\n\t\tappendStringInfoString(buf, \"IF NOT EXISTS \");\n\t}\n\n\t/*\n\t * Up until here we have been ending the statement in a space, which makes it possible\n\t * to just append the quoted extname. From here onwards we will not have the string\n\t * ending in a space so appends should begin with a whitespace.\n\t */\n\tappendStringInfoString(buf, quote_identifier(createExtensionStmt->extname));\n\tAppendCreateExtensionStmtOptions(buf, createExtensionStmt->options);\n\tappendStringInfoString(buf, \";\");\n}\n\n\n/*\n * AppendCreateExtensionStmtOptions takes the option list of a CreateExtensionStmt and\n * loops over the options to add them to the statement we are building.\n *\n * An error will be thrown if we run into an unsupported option, comparable to how\n * postgres gives an error when parsing this list.\n */\nstatic void\nAppendCreateExtensionStmtOptions(StringInfo buf, List *options)\n{\n\tif (list_length(options) > 0)\n\t{\n\t\t/* only append WITH if we actual will add options to the statement */\n\t\tappendStringInfoString(buf, \" WITH\");\n\t}\n\n\t/* Add the options to the statement */\n\tDefElem *defElem = NULL;\n\tforeach_declared_ptr(defElem, options)\n\t{\n\t\tif (strcmp(defElem->defname, \"schema\") == 0)\n\t\t{\n\t\t\tconst char *schemaName = defGetString(defElem);\n\t\t\tappendStringInfo(buf, \" SCHEMA  %s\", quote_identifier(schemaName));\n\t\t}\n\t\telse if (strcmp(defElem->defname, \"new_version\") == 0)\n\t\t{\n\t\t\tconst char *newVersion = defGetString(defElem);\n\t\t\tappendStringInfo(buf, \" VERSION %s\", quote_identifier(newVersion));\n\t\t}\n\t\telse if (strcmp(defElem->defname, \"old_version\") == 0)\n\t\t{\n\t\t\tconst char *oldVersion = defGetString(defElem);\n\t\t\tappendStringInfo(buf, \" FROM %s\", quote_identifier(oldVersion));\n\t\t}\n\t\telse if (strcmp(defElem->defname, \"cascade\") == 0)\n\t\t{\n\t\t\tbool cascade = defGetBoolean(defElem);\n\t\t\tif (cascade)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" CASCADE\");\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\telog(ERROR, \"unrecognized option: %s\", defElem->defname);\n\t\t}\n\t}\n}\n\n\n/*\n * DeparseAlterExtensionStmt builds and returns a string representing the\n * AlterExtensionStmt to be sent to worker nodes.\n */\nchar *\nDeparseAlterExtensionStmt(Node *node)\n{\n\tAlterExtensionStmt *stmt = castNode(AlterExtensionStmt, node);\n\tStringInfoData sql = { 0 };\n\tinitStringInfo(&sql);\n\n\tAppendAlterExtensionStmt(&sql, stmt);\n\n\treturn sql.data;\n}\n\n\n/*\n * AppendAlterExtensionStmt appends a string representing the AlterExtensionStmt to a buffer\n */\nstatic void\nAppendAlterExtensionStmt(StringInfo buf, AlterExtensionStmt *alterExtensionStmt)\n{\n\tList *optionsList = alterExtensionStmt->options;\n\n\tconst char *extensionName = alterExtensionStmt->extname;\n\textensionName = quote_identifier(extensionName);\n\n\tappendStringInfo(buf, \"ALTER EXTENSION %s UPDATE\", extensionName);\n\n\t/*\n\t * Append the options for ALTER EXTENSION ... UPDATE\n\t * Currently there is only 1 option, but this structure follows how postgres parses\n\t * the options.\n\t */\n\tDefElem *option = NULL;\n\tforeach_declared_ptr(option, optionsList)\n\t{\n\t\tif (strcmp(option->defname, \"new_version\") == 0)\n\t\t{\n\t\t\tconst char *newVersion = defGetString(option);\n\t\t\tappendStringInfo(buf, \" TO %s\", quote_identifier(newVersion));\n\t\t}\n\t\telse\n\t\t{\n\t\t\telog(ERROR, \"unrecognized option: %s\", option->defname);\n\t\t}\n\t}\n\n\tappendStringInfoString(buf, \";\");\n}\n\n\n/*\n * DeparseDropExtensionStmt builds and returns a string representing the DropStmt\n */\nchar *\nDeparseDropExtensionStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendDropExtensionStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendDropExtensionStmt appends a string representing the DropStmt for\n * an extension to a buffer.\n */\nstatic void\nAppendDropExtensionStmt(StringInfo str, DropStmt *dropStmt)\n{\n\t/* we append \"IF NOT EXISTS\" clause regardless of the content of the statement. */\n\tappendStringInfoString(str, \"DROP EXTENSION IF EXISTS \");\n\n\t/*\n\t * Pick the distributed ones from the  \"objects\" list that is storing\n\t * the object names to be deleted.\n\t */\n\tAppendExtensionNameList(str, dropStmt->objects);\n\n\t/* depending on behaviour field of DropStmt, we should append CASCADE or RESTRICT */\n\tif (dropStmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(str, \" CASCADE;\");\n\t}\n\telse\n\t{\n\t\tappendStringInfoString(str, \" RESTRICT;\");\n\t}\n}\n\n\n/*\n * AppendExtensionNameList appends a string representing the list of\n * extension names to a buffer.\n */\nstatic void\nAppendExtensionNameList(StringInfo str, List *objects)\n{\n\tListCell *objectCell = NULL;\n\n\tforeach(objectCell, objects)\n\t{\n\t\tconst char *extensionName = strVal(lfirst(objectCell));\n\t\textensionName = quote_identifier(extensionName);\n\n\t\tif (objectCell != list_head(objects))\n\t\t{\n\t\t\tappendStringInfo(str, \", \");\n\t\t}\n\n\t\tappendStringInfoString(str, extensionName);\n\t}\n}\n\n\n/*\n * DeparseAlterExtensionSchemaStmt builds and returns a string representing the\n * AlterObjectSchemaStmt (ALTER EXTENSION SET SCHEMA).\n */\nchar *\nDeparseAlterExtensionSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objectType == OBJECT_EXTENSION);\n\n\tAppendAlterExtensionSchemaStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterExtensionSchemaStmt appends a string representing the AlterObjectSchemaStmt\n * for an extension to a buffer.\n */\nstatic void\nAppendAlterExtensionSchemaStmt(StringInfo buf,\n\t\t\t\t\t\t\t   AlterObjectSchemaStmt *alterExtensionSchemaStmt)\n{\n\tAssert(alterExtensionSchemaStmt->objectType == OBJECT_EXTENSION);\n\n\tconst char *extensionName = strVal(alterExtensionSchemaStmt->object);\n\tconst char *newSchemaName = alterExtensionSchemaStmt->newschema;\n\n\textensionName = quote_identifier(extensionName);\n\tnewSchemaName = quote_identifier(newSchemaName);\n\n\tappendStringInfo(buf, \"ALTER EXTENSION %s SET SCHEMA %s;\", extensionName,\n\t\t\t\t\t newSchemaName);\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_foreign_data_wrapper_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_foreign_data_wrapper_stmts.c\n *\t  All routines to deparse foreign data wrapper statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"commands/defrem.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/nodes.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/relay_utility.h\"\n\nstatic void AppendGrantOnFDWStmt(StringInfo buf, GrantStmt *stmt);\nstatic void AppendGrantOnFDWNames(StringInfo buf, GrantStmt *stmt);\n\nchar *\nDeparseGrantOnFDWStmt(Node *node)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_FDW);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendGrantOnFDWStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendGrantOnFDWStmt(StringInfo buf, GrantStmt *stmt)\n{\n\tAssert(stmt->objtype == OBJECT_FDW);\n\tAppendGrantSharedPrefix(buf, stmt);\n\tAppendGrantOnFDWNames(buf, stmt);\n\tAppendGrantSharedSuffix(buf, stmt);\n}\n\n\nstatic void\nAppendGrantOnFDWNames(StringInfo buf, GrantStmt *stmt)\n{\n\tListCell *cell = NULL;\n\tappendStringInfo(buf, \" ON FOREIGN DATA WRAPPER \");\n\n\tforeach(cell, stmt->objects)\n\t{\n\t\tchar *fdwname = strVal(lfirst(cell));\n\n\t\tappendStringInfoString(buf, quote_identifier(fdwname));\n\t\tif (cell != list_tail(stmt->objects))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_foreign_server_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_foreign_server_stmts.c\n *\t  All routines to deparse foreign server statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"commands/defrem.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/nodes.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/relay_utility.h\"\n\nstatic void AppendCreateForeignServerStmt(StringInfo buf, CreateForeignServerStmt *stmt);\nstatic void AppendAlterForeignServerStmt(StringInfo buf, AlterForeignServerStmt *stmt);\nstatic void AppendAlterForeignServerOptions(StringInfo buf, AlterForeignServerStmt *stmt);\nstatic void AppendAlterForeignServerRenameStmt(StringInfo buf, RenameStmt *stmt);\nstatic void AppendAlterForeignServerOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);\nstatic void AppendDropForeignServerStmt(StringInfo buf, DropStmt *stmt);\nstatic void AppendServerNames(StringInfo buf, DropStmt *stmt);\nstatic void AppendBehavior(StringInfo buf, DropStmt *stmt);\nstatic char * GetDefElemActionString(DefElemAction action);\nstatic void AppendGrantOnForeignServerStmt(StringInfo buf, GrantStmt *stmt);\nstatic void AppendGrantOnForeignServerServers(StringInfo buf, GrantStmt *stmt);\n\nchar *\nDeparseCreateForeignServerStmt(Node *node)\n{\n\tCreateForeignServerStmt *stmt = castNode(CreateForeignServerStmt, node);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tAppendCreateForeignServerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterForeignServerStmt(Node *node)\n{\n\tAlterForeignServerStmt *stmt = castNode(AlterForeignServerStmt, node);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tAppendAlterForeignServerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterForeignServerRenameStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\n\tAssert(stmt->renameType == OBJECT_FOREIGN_SERVER);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tAppendAlterForeignServerRenameStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterForeignServerOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\n\tAssert(stmt->objectType == OBJECT_FOREIGN_SERVER);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tAppendAlterForeignServerOwnerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseDropForeignServerStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\n\tAssert(stmt->removeType == OBJECT_FOREIGN_SERVER);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tAppendDropForeignServerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseGrantOnForeignServerStmt(Node *node)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_FOREIGN_SERVER);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendGrantOnForeignServerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendCreateForeignServerStmt(StringInfo buf, CreateForeignServerStmt *stmt)\n{\n\tappendStringInfoString(buf, \"CREATE SERVER \");\n\n\tif (stmt->if_not_exists)\n\t{\n\t\tappendStringInfoString(buf, \"IF NOT EXISTS \");\n\t}\n\n\tappendStringInfo(buf, \"%s \", quote_identifier(stmt->servername));\n\n\tif (stmt->servertype)\n\t{\n\t\tappendStringInfo(buf, \"TYPE %s \", quote_literal_cstr(stmt->servertype));\n\t}\n\n\tif (stmt->version)\n\t{\n\t\tappendStringInfo(buf, \"VERSION %s \", quote_literal_cstr(stmt->version));\n\t}\n\n\tappendStringInfo(buf, \"FOREIGN DATA WRAPPER %s \", quote_identifier(stmt->fdwname));\n\n\tAppendOptionListToString(buf, stmt->options);\n}\n\n\nstatic void\nAppendAlterForeignServerStmt(StringInfo buf, AlterForeignServerStmt *stmt)\n{\n\tappendStringInfo(buf, \"ALTER SERVER %s \", quote_identifier(stmt->servername));\n\n\tif (stmt->has_version)\n\t{\n\t\tappendStringInfo(buf, \"VERSION %s \", quote_literal_cstr(stmt->version));\n\t}\n\n\tAppendAlterForeignServerOptions(buf, stmt);\n}\n\n\nstatic void\nAppendAlterForeignServerOptions(StringInfo buf, AlterForeignServerStmt *stmt)\n{\n\tif (list_length(stmt->options) <= 0)\n\t{\n\t\treturn;\n\t}\n\n\tappendStringInfoString(buf, \"OPTIONS (\");\n\n\tDefElemAction action = DEFELEM_UNSPEC;\n\tDefElem *def = NULL;\n\tforeach_declared_ptr(def, stmt->options)\n\t{\n\t\tif (def->defaction != DEFELEM_UNSPEC)\n\t\t{\n\t\t\taction = def->defaction;\n\t\t\tchar *actionString = GetDefElemActionString(action);\n\t\t\tappendStringInfo(buf, \"%s \", actionString);\n\t\t}\n\n\t\tappendStringInfo(buf, \"%s\", quote_identifier(def->defname));\n\n\t\tif (action != DEFELEM_DROP)\n\t\t{\n\t\t\tconst char *value = quote_literal_cstr(defGetString(def));\n\t\t\tappendStringInfo(buf, \" %s\", value);\n\t\t}\n\n\t\tif (def != llast(stmt->options))\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\t}\n\n\tappendStringInfoString(buf, \")\");\n}\n\n\nstatic void\nAppendAlterForeignServerRenameStmt(StringInfo buf, RenameStmt *stmt)\n{\n\tappendStringInfo(buf, \"ALTER SERVER %s RENAME TO %s\",\n\t\t\t\t\t quote_identifier(strVal(stmt->object)),\n\t\t\t\t\t quote_identifier(stmt->newname));\n}\n\n\nstatic void\nAppendAlterForeignServerOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)\n{\n\tconst char *servername = quote_identifier(strVal(stmt->object));\n\tappendStringInfo(buf, \"ALTER SERVER %s OWNER TO \", servername);\n\n\tappendStringInfo(buf, \"%s\", RoleSpecString(stmt->newowner, true));\n}\n\n\nstatic void\nAppendDropForeignServerStmt(StringInfo buf, DropStmt *stmt)\n{\n\tappendStringInfoString(buf, \"DROP SERVER \");\n\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\n\tAppendServerNames(buf, stmt);\n\n\tAppendBehavior(buf, stmt);\n}\n\n\nstatic void\nAppendServerNames(StringInfo buf, DropStmt *stmt)\n{\n\tString *serverValue = NULL;\n\tforeach_declared_ptr(serverValue, stmt->objects)\n\t{\n\t\tconst char *serverString = quote_identifier(strVal(serverValue));\n\t\tappendStringInfo(buf, \"%s\", serverString);\n\n\t\tif (serverValue != llast(stmt->objects))\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\t}\n}\n\n\nstatic void\nAppendBehavior(StringInfo buf, DropStmt *stmt)\n{\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n\telse if (stmt->behavior == DROP_RESTRICT)\n\t{\n\t\tappendStringInfoString(buf, \" RESTRICT\");\n\t}\n}\n\n\nstatic char *\nGetDefElemActionString(DefElemAction action)\n{\n\tswitch (action)\n\t{\n\t\tcase DEFELEM_ADD:\n\t\t{\n\t\t\treturn \"ADD\";\n\t\t}\n\n\t\tcase DEFELEM_SET:\n\t\t{\n\t\t\treturn \"SET\";\n\t\t}\n\n\t\tcase DEFELEM_DROP:\n\t\t{\n\t\t\treturn \"DROP\";\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn \"\";\n\t\t}\n\t}\n}\n\n\nstatic void\nAppendGrantOnForeignServerStmt(StringInfo buf, GrantStmt *stmt)\n{\n\tAssert(stmt->objtype == OBJECT_FOREIGN_SERVER);\n\tAppendGrantSharedPrefix(buf, stmt);\n\tAppendGrantOnForeignServerServers(buf, stmt);\n\tAppendGrantSharedSuffix(buf, stmt);\n}\n\n\nstatic void\nAppendGrantOnForeignServerServers(StringInfo buf, GrantStmt *stmt)\n{\n\tListCell *cell = NULL;\n\tappendStringInfo(buf, \" ON FOREIGN SERVER \");\n\n\tforeach(cell, stmt->objects)\n\t{\n\t\tchar *servername = strVal(lfirst(cell));\n\t\tappendStringInfoString(buf, quote_identifier(servername));\n\t\tif (cell != list_tail(stmt->objects))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_function_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_function_stmts.c\n *\n *\t  All routines to deparse function and procedure statements.\n *\t  This file contains all entry points specific for function and procedure statement\n *    deparsing\n *\n *\t  Functions that could move later are AppendDefElem, AppendDefElemStrict, etc. These\n *\t  should be reused across multiple statements and should live in their own deparse\n *\t  file.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/htup_details.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/defrem.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/value.h\"\n#include \"parser/parse_func.h\"\n#include \"parser/parse_type.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/fmgrprotos.h\"\n#include \"utils/guc.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/regproc.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/version_compat.h\"\n\n\n/* forward declaration for deparse functions */\nstatic char * ObjectTypeToKeyword(ObjectType objtype);\n\nstatic void AppendAlterFunctionStmt(StringInfo buf, AlterFunctionStmt *stmt);\nstatic void AppendDropFunctionStmt(StringInfo buf, DropStmt *stmt);\nstatic void AppendFunctionName(StringInfo buf, ObjectWithArgs *func, ObjectType objtype);\nstatic void AppendFunctionNameList(StringInfo buf, List *objects, ObjectType objtype);\n\nstatic void AppendDefElem(StringInfo buf, DefElem *def);\nstatic void AppendDefElemStrict(StringInfo buf, DefElem *def);\nstatic void AppendDefElemVolatility(StringInfo buf, DefElem *def);\nstatic void AppendDefElemLeakproof(StringInfo buf, DefElem *def);\nstatic void AppendDefElemSecurity(StringInfo buf, DefElem *def);\nstatic void AppendDefElemParallel(StringInfo buf, DefElem *def);\nstatic void AppendDefElemCost(StringInfo buf, DefElem *def);\nstatic void AppendDefElemRows(StringInfo buf, DefElem *def);\nstatic void AppendDefElemSet(StringInfo buf, DefElem *def);\nstatic void AppendDefElemSupport(StringInfo buf, DefElem *def);\n\nstatic void AppendRenameFunctionStmt(StringInfo buf, RenameStmt *stmt);\nstatic void AppendAlterFunctionSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);\nstatic void AppendAlterFunctionOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);\nstatic void AppendAlterFunctionDependsStmt(StringInfo buf, AlterObjectDependsStmt *stmt);\n\nstatic void AppendGrantOnFunctionStmt(StringInfo buf, GrantStmt *stmt);\nstatic void AppendGrantOnFunctionFunctions(StringInfo buf, GrantStmt *stmt);\n\nstatic char * CopyAndConvertToUpperCase(const char *str);\n\n/*\n * DeparseAlterFunctionStmt builds and returns a string representing the AlterFunctionStmt\n */\nchar *\nDeparseAlterFunctionStmt(Node *node)\n{\n\tAlterFunctionStmt *stmt = castNode(AlterFunctionStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendAlterFunctionStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * ObjectTypeToKeyword returns an appropriate string for the given ObjectType\n * Where the string will be one of \"FUNCTION\", \"PROCEDURE\", or \"AGGREGATE\"\n */\nstatic char *\nObjectTypeToKeyword(ObjectType objtype)\n{\n\tswitch (objtype)\n\t{\n\t\tcase OBJECT_FUNCTION:\n\t\t{\n\t\t\treturn \"FUNCTION\";\n\t\t}\n\n\t\tcase OBJECT_PROCEDURE:\n\t\t{\n\t\t\treturn \"PROCEDURE\";\n\t\t}\n\n\t\tcase OBJECT_AGGREGATE:\n\t\t{\n\t\t\treturn \"AGGREGATE\";\n\t\t}\n\n\t\tcase OBJECT_ROUTINE:\n\t\t{\n\t\t\treturn \"ROUTINE\";\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"Unknown object type: %d\", objtype);\n\t\t\treturn NULL;\n\t\t}\n\t}\n}\n\n\n/*\n * AppendAlterFunctionStmt appends a string representing the AlterFunctionStmt to a buffer\n */\nstatic void\nAppendAlterFunctionStmt(StringInfo buf, AlterFunctionStmt *stmt)\n{\n\tListCell *actionCell = NULL;\n\n\tappendStringInfo(buf, \"ALTER %s \", ObjectTypeToKeyword(stmt->objtype));\n\tAppendFunctionName(buf, stmt->func, stmt->objtype);\n\n\tforeach(actionCell, stmt->actions)\n\t{\n\t\tDefElem *def = castNode(DefElem, lfirst(actionCell));\n\t\tAppendDefElem(buf, def);\n\t}\n\n\tappendStringInfoString(buf, \";\");\n}\n\n\n/*\n * AppendDefElem appends a string representing the DefElem to a buffer\n */\nstatic void\nAppendDefElem(StringInfo buf, DefElem *def)\n{\n\tif (strcmp(def->defname, \"strict\") == 0)\n\t{\n\t\tAppendDefElemStrict(buf, def);\n\t}\n\telse if (strcmp(def->defname, \"volatility\") == 0)\n\t{\n\t\tAppendDefElemVolatility(buf, def);\n\t}\n\telse if (strcmp(def->defname, \"leakproof\") == 0)\n\t{\n\t\tAppendDefElemLeakproof(buf, def);\n\t}\n\telse if (strcmp(def->defname, \"security\") == 0)\n\t{\n\t\tAppendDefElemSecurity(buf, def);\n\t}\n\telse if (strcmp(def->defname, \"parallel\") == 0)\n\t{\n\t\tAppendDefElemParallel(buf, def);\n\t}\n\telse if (strcmp(def->defname, \"cost\") == 0)\n\t{\n\t\tAppendDefElemCost(buf, def);\n\t}\n\telse if (strcmp(def->defname, \"rows\") == 0)\n\t{\n\t\tAppendDefElemRows(buf, def);\n\t}\n\telse if (strcmp(def->defname, \"set\") == 0)\n\t{\n\t\tAppendDefElemSet(buf, def);\n\t}\n\telse if (strcmp(def->defname, \"support\") == 0)\n\t{\n\t\tAppendDefElemSupport(buf, def);\n\t}\n}\n\n\n/*\n * AppendDefElemStrict appends a string representing the DefElem to a buffer\n */\nstatic void\nAppendDefElemStrict(StringInfo buf, DefElem *def)\n{\n\tif (boolVal(def->arg))\n\t{\n\t\tappendStringInfo(buf, \" STRICT\");\n\t}\n\telse\n\t{\n\t\tappendStringInfo(buf, \" CALLED ON NULL INPUT\");\n\t}\n}\n\n\n/*\n * AppendDefElemVolatility appends a string representing the DefElem to a buffer\n */\nstatic void\nAppendDefElemVolatility(StringInfo buf, DefElem *def)\n{\n\tappendStringInfo(buf, \" %s\", CopyAndConvertToUpperCase(strVal(def->arg)));\n}\n\n\n/*\n * AppendDefElemLeakproof appends a string representing the DefElem to a buffer\n */\nstatic void\nAppendDefElemLeakproof(StringInfo buf, DefElem *def)\n{\n\tif (!boolVal(def->arg))\n\t{\n\t\tappendStringInfo(buf, \" NOT\");\n\t}\n\tappendStringInfo(buf, \" LEAKPROOF\");\n}\n\n\n/*\n * AppendDefElemSecurity appends a string representing the DefElem to a buffer\n */\nstatic void\nAppendDefElemSecurity(StringInfo buf, DefElem *def)\n{\n\tif (!boolVal(def->arg))\n\t{\n\t\tappendStringInfo(buf, \" SECURITY INVOKER\");\n\t}\n\telse\n\t{\n\t\tappendStringInfo(buf, \" SECURITY DEFINER\");\n\t}\n}\n\n\n/*\n * AppendDefElemParallel appends a string representing the DefElem to a buffer\n */\nstatic void\nAppendDefElemParallel(StringInfo buf, DefElem *def)\n{\n\tappendStringInfo(buf, \" PARALLEL %s\", CopyAndConvertToUpperCase(strVal(def->arg)));\n}\n\n\n/*\n * AppendDefElemCost appends a string representing the DefElem to a buffer\n */\nstatic void\nAppendDefElemCost(StringInfo buf, DefElem *def)\n{\n\tappendStringInfo(buf, \" COST %lf\", defGetNumeric(def));\n}\n\n\n/*\n * AppendDefElemRows appends a string representing the DefElem to a buffer\n */\nstatic void\nAppendDefElemRows(StringInfo buf, DefElem *def)\n{\n\tappendStringInfo(buf, \" ROWS %lf\", defGetNumeric(def));\n}\n\n\n/*\n * AppendDefElemSet appends a string representing the DefElem to a buffer\n */\nstatic void\nAppendDefElemSet(StringInfo buf, DefElem *def)\n{\n\tVariableSetStmt *setStmt = castNode(VariableSetStmt, def->arg);\n\n\tAppendVariableSet(buf, setStmt);\n}\n\n\n/*\n * AppendDefElemSupport appends a string representing the DefElem to a buffer\n */\nstatic void\nAppendDefElemSupport(StringInfo buf, DefElem *def)\n{\n\tappendStringInfo(buf, \" SUPPORT %s\", defGetString(def));\n}\n\n\n/*\n * DeparseRenameFunctionStmt builds and returns a string representing the RenameStmt\n */\nchar *\nDeparseRenameFunctionStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssertObjectTypeIsFunctional(stmt->renameType);\n\n\tAppendRenameFunctionStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendRenameFunctionStmt appends a string representing the RenameStmt to a buffer\n */\nstatic void\nAppendRenameFunctionStmt(StringInfo buf, RenameStmt *stmt)\n{\n\tObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);\n\n\tappendStringInfo(buf, \"ALTER %s \", ObjectTypeToKeyword(stmt->renameType));\n\tAppendFunctionName(buf, func, stmt->renameType);\n\tappendStringInfo(buf, \" RENAME TO %s;\", quote_identifier(stmt->newname));\n}\n\n\n/*\n * DeparseAlterFunctionSchemaStmt builds and returns a string representing the AlterObjectSchemaStmt\n */\nchar *\nDeparseAlterFunctionSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssertObjectTypeIsFunctional(stmt->objectType);\n\n\tAppendAlterFunctionSchemaStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterFunctionSchemaStmt appends a string representing the AlterObjectSchemaStmt to a buffer\n */\nstatic void\nAppendAlterFunctionSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt)\n{\n\tObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);\n\n\tappendStringInfo(buf, \"ALTER %s \", ObjectTypeToKeyword(stmt->objectType));\n\tAppendFunctionName(buf, func, stmt->objectType);\n\tappendStringInfo(buf, \" SET SCHEMA %s;\", quote_identifier(stmt->newschema));\n}\n\n\n/*\n * DeparseAlterFunctionOwnerStmt builds and returns a string representing the AlterOwnerStmt\n */\nchar *\nDeparseAlterFunctionOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssertObjectTypeIsFunctional(stmt->objectType);\n\n\tAppendAlterFunctionOwnerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterFunctionOwnerStmt appends a string representing the AlterOwnerStmt to a buffer\n */\nstatic void\nAppendAlterFunctionOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)\n{\n\tObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);\n\n\tappendStringInfo(buf, \"ALTER %s \", ObjectTypeToKeyword(stmt->objectType));\n\tAppendFunctionName(buf, func, stmt->objectType);\n\tappendStringInfo(buf, \" OWNER TO %s;\", RoleSpecString(stmt->newowner, true));\n}\n\n\n/*\n * DeparseAlterFunctionDependsStmt builds and returns a string representing the AlterObjectDependsStmt\n */\nchar *\nDeparseAlterFunctionDependsStmt(Node *node)\n{\n\tAlterObjectDependsStmt *stmt = castNode(AlterObjectDependsStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssertObjectTypeIsFunctional(stmt->objectType);\n\n\tAppendAlterFunctionDependsStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterFunctionDependsStmt appends a string representing the AlterObjectDependsStmt to a buffer\n */\nstatic void\nAppendAlterFunctionDependsStmt(StringInfo buf, AlterObjectDependsStmt *stmt)\n{\n\tObjectWithArgs *func = castNode(ObjectWithArgs, stmt->object);\n\n\tappendStringInfo(buf, \"ALTER %s \", ObjectTypeToKeyword(stmt->objectType));\n\tAppendFunctionName(buf, func, stmt->objectType);\n\tappendStringInfo(buf, \" DEPENDS ON EXTENSION %s;\", strVal(stmt->extname));\n}\n\n\n/*\n * DeparseDropFunctionStmt builds and returns a string representing the DropStmt\n */\nchar *\nDeparseDropFunctionStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssertObjectTypeIsFunctional(stmt->removeType);\n\n\tAppendDropFunctionStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendDropFunctionStmt appends a string representing the DropStmt to a buffer\n */\nstatic void\nAppendDropFunctionStmt(StringInfo buf, DropStmt *stmt)\n{\n\tappendStringInfo(buf, \"DROP %s \", ObjectTypeToKeyword(stmt->removeType));\n\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\n\tAppendFunctionNameList(buf, stmt->objects, stmt->removeType);\n\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n\n\tappendStringInfoString(buf, \";\");\n}\n\n\n/*\n * AppendFunctionNameList appends a string representing the list of function names to a buffer\n */\nstatic void\nAppendFunctionNameList(StringInfo buf, List *objects, ObjectType objtype)\n{\n\tListCell *objectCell = NULL;\n\tforeach(objectCell, objects)\n\t{\n\t\tNode *object = lfirst(objectCell);\n\n\t\tif (objectCell != list_head(objects))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\n\t\tObjectWithArgs *func = castNode(ObjectWithArgs, object);\n\n\t\tAppendFunctionName(buf, func, objtype);\n\t}\n}\n\n\n/*\n * AppendFunctionName appends a string representing a single function name to a buffer\n */\nstatic void\nAppendFunctionName(StringInfo buf, ObjectWithArgs *func, ObjectType objtype)\n{\n\tOid funcid = LookupFuncWithArgs(objtype, func, true);\n\n\tif (funcid == InvalidOid)\n\t{\n\t\t/*\n\t\t * DROP FUNCTION IF EXISTS absent_function arrives here\n\t\t *\n\t\t * There is no namespace associated with the nonexistent function,\n\t\t * thus we return the function name as it is provided\n\t\t */\n\t\tchar *functionName = NULL;\n\t\tchar *schemaName = NULL;\n\n\t\tDeconstructQualifiedName(func->objname, &schemaName, &functionName);\n\n\t\tchar *qualifiedFunctionName = quote_qualified_identifier(schemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t functionName);\n\t\tappendStringInfoString(buf, qualifiedFunctionName);\n\n\t\tif (!func->args_unspecified)\n\t\t{\n\t\t\t/*\n\t\t\t * The function is not found, but there is an argument list specified, this has\n\t\t\t * some known issues with the \"any\" type. However this is mostly a bug in\n\t\t\t * postgres' TypeNameListToString. For now the best we can do until we understand\n\t\t\t * the underlying cause better.\n\t\t\t */\n\n\t\t\tconst char *args = TypeNameListToString(func->objargs);\n\t\t\tappendStringInfo(buf, \"(%s)\", args);\n\t\t}\n\t}\n\telse\n\t{\n\t\tchar *functionSignature = format_procedure_qualified(funcid);\n\t\tappendStringInfoString(buf, functionSignature);\n\t}\n}\n\n\n/*\n * CopyAndConvertToUpperCase copies a string and converts all characters to uppercase\n */\nstatic char *\nCopyAndConvertToUpperCase(const char *str)\n{\n\tchar *result, *p;\n\n\tresult = pstrdup(str);\n\n\tfor (p = result; *p; p++)\n\t{\n\t\t*p = pg_toupper((unsigned char) *p);\n\t}\n\n\treturn result;\n}\n\n\n/*\n * DeparseGrantOnFunctionStmt builds and returns a string representing the GrantOnFunctionStmt\n */\nchar *\nDeparseGrantOnFunctionStmt(Node *node)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(isFunction(stmt->objtype));\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendGrantOnFunctionStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendGrantOnFunctionStmt builds and returns an SQL command representing a\n * GRANT .. ON FUNCTION command from given GrantStmt object.\n */\nstatic void\nAppendGrantOnFunctionStmt(StringInfo buf, GrantStmt *stmt)\n{\n\tAssert(isFunction(stmt->objtype));\n\n\tif (stmt->targtype == ACL_TARGET_ALL_IN_SCHEMA)\n\t{\n\t\telog(ERROR,\n\t\t\t \"GRANT .. ALL FUNCTIONS/PROCEDURES IN SCHEMA is not supported for formatting.\");\n\t}\n\n\tAppendGrantSharedPrefix(buf, stmt);\n\n\tAppendGrantOnFunctionFunctions(buf, stmt);\n\n\tAppendGrantSharedSuffix(buf, stmt);\n}\n\n\n/*\n * AppendGrantOnFunctionFunctions appends the function names along with their arguments\n * to the given StringInfo from the given GrantStmt\n */\nstatic void\nAppendGrantOnFunctionFunctions(StringInfo buf, GrantStmt *stmt)\n{\n\tListCell *cell = NULL;\n\n\t/*\n\t * The FUNCTION syntax works for plain functions, aggregate functions, and window\n\t * functions, but not for procedures; use PROCEDURE for those. Alternatively, use\n\t * ROUTINE to refer to a function, aggregate function, window function, or procedure\n\t * regardless of its precise type.\n\t * https://www.postgresql.org/docs/current/sql-grant.html\n\t */\n\tappendStringInfo(buf, \" ON ROUTINE \");\n\n\tforeach(cell, stmt->objects)\n\t{\n\t\t/*\n\t\t * GrantOnFunction statement keeps its objects (functions) as\n\t\t * a list of ObjectWithArgs\n\t\t */\n\t\tObjectWithArgs *function = (ObjectWithArgs *) lfirst(cell);\n\n\t\tappendStringInfoString(buf, NameListToString(function->objname));\n\t\tif (!function->args_unspecified)\n\t\t{\n\t\t\t/* if args are specified, we should append \"(arg1, arg2, ...)\" to the function name */\n\t\t\tconst char *args = TypeNameListToString(function->objargs);\n\t\t\tappendStringInfo(buf, \"(%s)\", args);\n\t\t}\n\t\tif (cell != list_tail(stmt->objects))\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\t}\n}\n\n\n/*\n * isFunction returns true if the given ObjectType is a function, a procedure, a routine\n * or an aggregate otherwise returns false\n */\nbool\nisFunction(ObjectType objectType)\n{\n\treturn (objectType == OBJECT_FUNCTION || objectType == OBJECT_PROCEDURE ||\n\t\t\tobjectType == OBJECT_ROUTINE || objectType == OBJECT_AGGREGATE);\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_owned_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_owned_stmts.c\n *\t  Functions to turn all Statement structures related to owned back\n *    into sql.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/builtins.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparser.h\"\n\nstatic void AppendDropOwnedStmt(StringInfo buf, DropOwnedStmt *stmt);\nstatic void AppendRoleList(StringInfo buf, List *roleList);\n\n/*\n * DeparseDropOwnedStmt builds and returns a string representing of the\n * DropOwnedStmt for application on a remote server.\n */\nchar *\nDeparseDropOwnedStmt(Node *node)\n{\n\tDropOwnedStmt *stmt = castNode(DropOwnedStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tAppendDropOwnedStmt(&buf, stmt);\n\n\treturn buf.data;\n}\n\n\n/*\n * AppendDropOwnedStmt generates the string representation of the\n * DropOwnedStmt and appends it to the buffer.\n */\nstatic void\nAppendDropOwnedStmt(StringInfo buf, DropOwnedStmt *stmt)\n{\n\tappendStringInfo(buf, \"DROP OWNED BY \");\n\n\tAppendRoleList(buf, stmt->roles);\n\n\tif (stmt->behavior == DROP_RESTRICT)\n\t{\n\t\tappendStringInfo(buf, \" RESTRICT\");\n\t}\n\telse if (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfo(buf, \" CASCADE\");\n\t}\n}\n\n\nstatic void\nAppendRoleList(StringInfo buf, List *roleList)\n{\n\tListCell *cell = NULL;\n\tforeach(cell, roleList)\n\t{\n\t\tNode *roleNode = (Node *) lfirst(cell);\n\t\tAssert(IsA(roleNode, RoleSpec) || IsA(roleNode, AccessPriv));\n\t\tconst char *rolename = NULL;\n\t\tif (IsA(roleNode, RoleSpec))\n\t\t{\n\t\t\trolename = RoleSpecString((RoleSpec *) roleNode, true);\n\t\t}\n\t\tappendStringInfoString(buf, rolename);\n\t\tif (cell != list_tail(roleList))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\t}\n}\n\n\nstatic void\nAppendReassignOwnedStmt(StringInfo buf, ReassignOwnedStmt *stmt)\n{\n\tappendStringInfo(buf, \"REASSIGN OWNED BY \");\n\n\tAppendRoleList(buf, stmt->roles);\n\tconst char *newRoleName = RoleSpecString(stmt->newrole, true);\n\tappendStringInfo(buf, \" TO %s\", newRoleName);\n}\n\n\nchar *\nDeparseReassignOwnedStmt(Node *node)\n{\n\tReassignOwnedStmt *stmt = castNode(ReassignOwnedStmt, node);\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tAppendReassignOwnedStmt(&buf, stmt);\n\n\treturn buf.data;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_publication_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_publication_stmts.c\n *\t  All routines to deparse publication statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/relation.h\"\n#include \"catalog/namespace.h\"\n#include \"commands/defrem.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/value.h\"\n#include \"parser/parse_clause.h\"\n#include \"parser/parse_collate.h\"\n#include \"parser/parse_node.h\"\n#include \"parser/parse_relation.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/ruleutils.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/namespace_utils.h\"\n\n\nstatic void AppendCreatePublicationStmt(StringInfo buf, CreatePublicationStmt *stmt,\n\t\t\t\t\t\t\t\t\t\tbool whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\t\tbool includeLocalTables);\nstatic bool AppendPublicationObjects(StringInfo buf, List *publicationObjects,\n\t\t\t\t\t\t\t\t\t bool whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\t bool includeLocalTables);\nstatic void AppendWhereClauseExpression(StringInfo buf, RangeVar *tableName,\n\t\t\t\t\t\t\t\t\t\tNode *whereClause,\n\t\t\t\t\t\t\t\t\t\tbool whereClauseNeedsTransform);\nstatic void AppendAlterPublicationAction(StringInfo buf, AlterPublicationAction action);\nstatic bool AppendAlterPublicationStmt(StringInfo buf, AlterPublicationStmt *stmt,\n\t\t\t\t\t\t\t\t\t   bool whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\t   bool includeLocalTables);\nstatic void AppendDropPublicationStmt(StringInfo buf, DropStmt *stmt);\nstatic void AppendRenamePublicationStmt(StringInfo buf, RenameStmt *stmt);\nstatic void AppendAlterPublicationOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);\nstatic void AppendPublicationOptions(StringInfo stringBuffer, List *optionList);\nstatic void AppendIdentifierList(StringInfo buf, List *objects);\n\n\n/*\n * DeparseCreatePublicationStmt builds and returns a string representing a\n * CreatePublicationStmt.\n */\nchar *\nDeparseCreatePublicationStmt(Node *node)\n{\n\t/* regular deparsing function takes CREATE PUBLICATION from the parser */\n\tbool whereClauseNeedsTransform = false;\n\n\t/* for regular CREATE PUBLICATION we do not propagate local tables */\n\tbool includeLocalTables = false;\n\n\treturn DeparseCreatePublicationStmtExtended(node, whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\t\t\t\tincludeLocalTables);\n}\n\n\n/*\n * DeparseCreatePublicationStmtExtended builds and returns a string representing a\n * CreatePublicationStmt, which may have already-transformed expressions.\n */\nchar *\nDeparseCreatePublicationStmtExtended(Node *node, bool whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\t bool includeLocalTables)\n{\n\tCreatePublicationStmt *stmt = castNode(CreatePublicationStmt, node);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendCreatePublicationStmt(&str, stmt, whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\tincludeLocalTables);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendCreatePublicationStmt appends a string representing a\n * CreatePublicationStmt to a buffer.\n */\nstatic void\nAppendCreatePublicationStmt(StringInfo buf, CreatePublicationStmt *stmt,\n\t\t\t\t\t\t\tbool whereClauseNeedsTransform,\n\t\t\t\t\t\t\tbool includeLocalTables)\n{\n\tappendStringInfo(buf, \"CREATE PUBLICATION %s\",\n\t\t\t\t\t quote_identifier(stmt->pubname));\n\n\tif (stmt->for_all_tables)\n\t{\n\t\tappendStringInfoString(buf, \" FOR ALL TABLES\");\n\t}\n\telse if (stmt->pubobjects != NIL)\n\t{\n\t\tbool hasObjects = false;\n\t\tPublicationObjSpec *publicationObject = NULL;\n\n\t\t/*\n\t\t * Check whether there are objects to propagate, mainly to know whether\n\t\t * we should include \"FOR\".\n\t\t */\n\t\tforeach_declared_ptr(publicationObject, stmt->pubobjects)\n\t\t{\n\t\t\tif (publicationObject->pubobjtype == PUBLICATIONOBJ_TABLE)\n\t\t\t{\n\t\t\t\t/* FOR TABLE ... */\n\t\t\t\tPublicationTable *publicationTable = publicationObject->pubtable;\n\n\t\t\t\tif (includeLocalTables ||\n\t\t\t\t\tIsCitusTableRangeVar(publicationTable->relation, NoLock, false))\n\t\t\t\t{\n\t\t\t\t\thasObjects = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\thasObjects = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (hasObjects)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" FOR\");\n\t\t\tAppendPublicationObjects(buf, stmt->pubobjects, whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\t includeLocalTables);\n\t\t}\n\t}\n\n\tif (stmt->options != NIL)\n\t{\n\t\tappendStringInfoString(buf, \" WITH (\");\n\t\tAppendPublicationOptions(buf, stmt->options);\n\t\tappendStringInfoString(buf, \")\");\n\t}\n}\n\n\n/*\n * AppendPublicationObjects appends a string representing a list of publication\n * objects to a buffer.\n *\n * For instance: TABLE users, departments, TABLES IN SCHEMA production\n */\nstatic bool\nAppendPublicationObjects(StringInfo buf, List *publicationObjects,\n\t\t\t\t\t\t bool whereClauseNeedsTransform,\n\t\t\t\t\t\t bool includeLocalTables)\n{\n\tPublicationObjSpec *publicationObject = NULL;\n\tbool appendedObject = false;\n\n\tforeach_declared_ptr(publicationObject, publicationObjects)\n\t{\n\t\tif (publicationObject->pubobjtype == PUBLICATIONOBJ_TABLE)\n\t\t{\n\t\t\t/* FOR TABLE ... */\n\t\t\tPublicationTable *publicationTable = publicationObject->pubtable;\n\t\t\tRangeVar *rangeVar = publicationTable->relation;\n\t\t\tchar *schemaName = rangeVar->schemaname;\n\t\t\tchar *tableName = rangeVar->relname;\n\n\t\t\tif (!includeLocalTables && !IsCitusTableRangeVar(rangeVar, NoLock, false))\n\t\t\t{\n\t\t\t\t/* do not propagate local tables */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (schemaName != NULL)\n\t\t\t{\n\t\t\t\t/* qualified table name */\n\t\t\t\tappendStringInfo(buf, \"%s TABLE %s\",\n\t\t\t\t\t\t\t\t appendedObject ? \",\" : \"\",\n\t\t\t\t\t\t\t\t quote_qualified_identifier(schemaName, tableName));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* unqualified table name */\n\t\t\t\tappendStringInfo(buf, \"%s TABLE %s\",\n\t\t\t\t\t\t\t\t appendedObject ? \",\" : \"\",\n\t\t\t\t\t\t\t\t quote_identifier(tableName));\n\t\t\t}\n\n\t\t\tif (publicationTable->columns != NIL)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" (\");\n\t\t\t\tAppendIdentifierList(buf, publicationTable->columns);\n\t\t\t\tappendStringInfoString(buf, \")\");\n\t\t\t}\n\n\t\t\tif (publicationTable->whereClause != NULL)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" WHERE (\");\n\n\t\t\t\tAppendWhereClauseExpression(buf, rangeVar,\n\t\t\t\t\t\t\t\t\t\t\tpublicationTable->whereClause,\n\t\t\t\t\t\t\t\t\t\t\twhereClauseNeedsTransform);\n\n\t\t\t\tappendStringInfoString(buf, \")\");\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* FOR TABLES IN SCHEMA */\n\t\t\tchar *schemaName = publicationObject->name;\n\n\t\t\tif (publicationObject->pubobjtype == PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA)\n\t\t\t{\n\t\t\t\tList *searchPath = fetch_search_path(false);\n\t\t\t\tif (searchPath == NIL)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, errcode(ERRCODE_UNDEFINED_SCHEMA),\n\t\t\t\t\t\t\terrmsg(\"no schema has been selected for \"\n\t\t\t\t\t\t\t\t   \"CURRENT_SCHEMA\"));\n\t\t\t\t}\n\n\t\t\t\tschemaName = get_namespace_name(linitial_oid(searchPath));\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \"%s TABLES IN SCHEMA %s\",\n\t\t\t\t\t\t\t appendedObject ? \",\" : \"\",\n\t\t\t\t\t\t\t quote_identifier(schemaName));\n\t\t}\n\n\t\tappendedObject = true;\n\t}\n\n\treturn appendedObject;\n}\n\n\n/*\n * AppendWhereClauseExpression appends a deparsed expression that can\n * contain a filter on the given table. If whereClauseNeedsTransform is set\n * the expression is first tranformed.\n */\nstatic void\nAppendWhereClauseExpression(StringInfo buf, RangeVar *tableName,\n\t\t\t\t\t\t\tNode *whereClause, bool whereClauseNeedsTransform)\n{\n\tRelation relation = relation_openrv(tableName, AccessShareLock);\n\n\tif (whereClauseNeedsTransform)\n\t{\n\t\tParseState *pstate = make_parsestate(NULL);\n\t\tpstate->p_sourcetext = \"\";\n\t\tParseNamespaceItem *nsitem = addRangeTableEntryForRelation(pstate,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   AccessShareLock, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   false, false);\n\t\taddNSItemToQuery(pstate, nsitem, false, true, true);\n\n\t\twhereClause = transformWhereClause(pstate,\n\t\t\t\t\t\t\t\t\t\t   copyObject(whereClause),\n\t\t\t\t\t\t\t\t\t\t   EXPR_KIND_WHERE,\n\t\t\t\t\t\t\t\t\t\t   \"PUBLICATION WHERE\");\n\n\t\tassign_expr_collations(pstate, whereClause);\n\t}\n\n\tList *relationContext = deparse_context_for(tableName->relname, relation->rd_id);\n\n\tint saveNestLevel = PushEmptySearchPath();\n\tchar *whereClauseString = deparse_expression(whereClause,\n\t\t\t\t\t\t\t\t\t\t\t\t relationContext,\n\t\t\t\t\t\t\t\t\t\t\t\t true, true);\n\tPopEmptySearchPath(saveNestLevel);\n\n\tappendStringInfoString(buf, whereClauseString);\n\n\trelation_close(relation, AccessShareLock);\n}\n\n\n/*\n * DeparseAlterPublicationSchemaStmt builds and returns a string representing\n * an AlterPublicationStmt.\n */\nchar *\nDeparseAlterPublicationStmt(Node *node)\n{\n\t/* regular deparsing function takes ALTER PUBLICATION from the parser */\n\tbool whereClauseNeedsTransform = true;\n\n\t/* for regular ALTER PUBLICATION we do not propagate local tables */\n\tbool includeLocalTables = false;\n\n\treturn DeparseAlterPublicationStmtExtended(node, whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\t\t\t   includeLocalTables);\n}\n\n\n/*\n * DeparseAlterPublicationStmtExtended builds and returns a string representing a\n * AlterPublicationStmt, which may have already-transformed expressions.\n */\nchar *\nDeparseAlterPublicationStmtExtended(Node *node, bool whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\tbool includeLocalTables)\n{\n\tAlterPublicationStmt *stmt = castNode(AlterPublicationStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tif (!AppendAlterPublicationStmt(&str, stmt, whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\tincludeLocalTables))\n\t{\n\t\tAssert(!includeLocalTables);\n\n\t\t/*\n\t\t * When there are no objects to propagate, then there is no\n\t\t * valid ALTER PUBLICATION to construct.\n\t\t */\n\t\treturn NULL;\n\t}\n\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterPublicationStmt appends a string representing an AlterPublicationStmt\n * of the form ALTER PUBLICATION .. ADD/SET/DROP\n */\nstatic bool\nAppendAlterPublicationStmt(StringInfo buf, AlterPublicationStmt *stmt,\n\t\t\t\t\t\t   bool whereClauseNeedsTransform,\n\t\t\t\t\t\t   bool includeLocalTables)\n{\n\tappendStringInfo(buf, \"ALTER PUBLICATION %s\",\n\t\t\t\t\t quote_identifier(stmt->pubname));\n\n\tif (stmt->options)\n\t{\n\t\tappendStringInfoString(buf, \" SET (\");\n\t\tAppendPublicationOptions(buf, stmt->options);\n\t\tappendStringInfoString(buf, \")\");\n\n\t\t/* changing options cannot be combined with other actions */\n\t\treturn true;\n\t}\n\n\tAppendAlterPublicationAction(buf, stmt->action);\n\treturn AppendPublicationObjects(buf, stmt->pubobjects, whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\tincludeLocalTables);\n}\n\n\n/*\n * AppendAlterPublicationAction appends a string representing an AlterPublicationAction\n * to a buffer.\n */\nstatic void\nAppendAlterPublicationAction(StringInfo buf, AlterPublicationAction action)\n{\n\tswitch (action)\n\t{\n\t\tcase AP_AddObjects:\n\t\t{\n\t\t\tappendStringInfoString(buf, \" ADD\");\n\t\t\tbreak;\n\t\t}\n\n\t\tcase AP_DropObjects:\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DROP\");\n\t\t\tbreak;\n\t\t}\n\n\t\tcase AP_SetObjects:\n\t\t{\n\t\t\tappendStringInfoString(buf, \" SET\");\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unrecognized publication action: %d\", action)));\n\t\t}\n\t}\n}\n\n\n/*\n * DeparseDropPublicationStmt builds and returns a string representing the DropStmt\n */\nchar *\nDeparseDropPublicationStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->removeType == OBJECT_PUBLICATION);\n\n\tAppendDropPublicationStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendDropPublicationStmt appends a string representing the DropStmt to a buffer\n */\nstatic void\nAppendDropPublicationStmt(StringInfo buf, DropStmt *stmt)\n{\n\tappendStringInfoString(buf, \"DROP PUBLICATION \");\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\tAppendIdentifierList(buf, stmt->objects);\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n}\n\n\n/*\n * DeparseRenamePublicationStmt builds and returns a string representing the RenameStmt\n */\nchar *\nDeparseRenamePublicationStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->renameType == OBJECT_PUBLICATION);\n\n\tAppendRenamePublicationStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendRenamePublicationStmt appends a string representing the RenameStmt to a buffer\n */\nstatic void\nAppendRenamePublicationStmt(StringInfo buf, RenameStmt *stmt)\n{\n\tappendStringInfo(buf, \"ALTER PUBLICATION %s RENAME TO %s;\",\n\t\t\t\t\t quote_identifier(strVal(stmt->object)),\n\t\t\t\t\t quote_identifier(stmt->newname));\n}\n\n\n/*\n * DeparseAlterPublicationOwnerStmt builds and returns a string representing the AlterOwnerStmt\n */\nchar *\nDeparseAlterPublicationOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objectType == OBJECT_PUBLICATION);\n\n\tAppendAlterPublicationOwnerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterPublicationOwnerStmt appends a string representing the AlterOwnerStmt to a buffer\n */\nstatic void\nAppendAlterPublicationOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)\n{\n\tAssert(stmt->objectType == OBJECT_PUBLICATION);\n\n\tappendStringInfo(buf, \"ALTER PUBLICATION %s OWNER TO %s;\",\n\t\t\t\t\t quote_identifier(strVal(stmt->object)),\n\t\t\t\t\t RoleSpecString(stmt->newowner, true));\n}\n\n\n/*\n * AppendPublicationOptions appends a string representing a list of publication opions.\n */\nstatic void\nAppendPublicationOptions(StringInfo stringBuffer, List *optionList)\n{\n\tListCell *optionCell = NULL;\n\tbool firstOptionPrinted = false;\n\n\tforeach(optionCell, optionList)\n\t{\n\t\tDefElem *option = (DefElem *) lfirst(optionCell);\n\t\tchar *optionName = option->defname;\n\t\tchar *optionValue = defGetString(option);\n\t\tNodeTag valueType = nodeTag(option->arg);\n\n\t\tif (firstOptionPrinted)\n\t\t{\n\t\t\tappendStringInfo(stringBuffer, \", \");\n\t\t}\n\t\tfirstOptionPrinted = true;\n\n\t\tappendStringInfo(stringBuffer, \"%s = \",\n\t\t\t\t\t\t quote_identifier(optionName));\n\n\t\tif (valueType == T_Integer || valueType == T_Float || valueType == T_Boolean)\n\t\t{\n\t\t\t/* string escaping is unnecessary for numeric types and can cause issues */\n\t\t\tappendStringInfo(stringBuffer, \"%s\", optionValue);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(stringBuffer, \"%s\", quote_literal_cstr(optionValue));\n\t\t}\n\t}\n}\n\n\n/*\n * AppendIdentifierList appends a string representing a list of\n * identifiers (of String type).\n */\nstatic void\nAppendIdentifierList(StringInfo buf, List *objects)\n{\n\tListCell *objectCell = NULL;\n\n\tforeach(objectCell, objects)\n\t{\n\t\tchar *name = strVal(lfirst(objectCell));\n\n\t\tif (objectCell != list_head(objects))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\n\t\tappendStringInfoString(buf, quote_identifier(name));\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_role_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_role_stmts.c\n *\t  All routines to deparse role statements.\n *\t  This file contains all entry points specific for ALTER ROLE statement\n *    deparsing as well as functions that are currently only used for deparsing\n *    ALTER ROLE statements.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"commands/defrem.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/builtins.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\nstatic void AppendAlterRoleStmt(StringInfo buf, AlterRoleStmt *stmt);\nstatic void AppendAlterRoleSetStmt(StringInfo buf, AlterRoleSetStmt *stmt);\nstatic void AppendCreateRoleStmt(StringInfo buf, CreateRoleStmt *stmt);\nstatic void AppendRoleOption(StringInfo buf, ListCell *optionCell);\nstatic void AppendRoleList(StringInfo buf, List *roleList);\nstatic void AppendDropRoleStmt(StringInfo buf, DropRoleStmt *stmt);\nstatic void AppendGrantRoleStmt(StringInfo buf, GrantRoleStmt *stmt);\nstatic void AppendRevokeAdminOptionFor(StringInfo buf, GrantRoleStmt *stmt);\nstatic void AppendGrantWithAdminOption(StringInfo buf, GrantRoleStmt *stmt);\n\n\n/*\n * DeparseAlterRoleStmt builds and returns a string representing of the\n * AlterRoleStmt for application on a remote server.\n */\nchar *\nDeparseAlterRoleStmt(Node *node)\n{\n\tAlterRoleStmt *stmt = castNode(AlterRoleStmt, node);\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tAppendAlterRoleStmt(&buf, stmt);\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseAlterRoleSetStmt builds and returns a string representing of the\n * AlterRoleSetStmt for application on a remote server.\n */\nchar *\nDeparseAlterRoleSetStmt(Node *node)\n{\n\tAlterRoleSetStmt *stmt = castNode(AlterRoleSetStmt, node);\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tAppendAlterRoleSetStmt(&buf, stmt);\n\n\treturn buf.data;\n}\n\n\n/*\n * AppendAlterRoleStmt generates the string representation of the\n * AlterRoleStmt and appends it to the buffer.\n */\nstatic void\nAppendAlterRoleStmt(StringInfo buf, AlterRoleStmt *stmt)\n{\n\tListCell *optionCell = NULL;\n\tRoleSpec *role = stmt->role;\n\n\t/*\n\t * If the role_specification used is CURRENT_USER or SESSION_USER,\n\t * it will be converted to thats roles role name.\n\t */\n\tappendStringInfo(buf, \"ALTER ROLE %s\", RoleSpecString(role, true));\n\n\tforeach(optionCell, stmt->options)\n\t{\n\t\tAppendRoleOption(buf, optionCell);\n\t}\n}\n\n\n/*\n * AppendRoleOption generates the string representation for the role options\n * and appends it to the buffer.\n *\n * This function only generates strings for common role options of ALTER ROLE\n * and CREATE ROLE statements. The extra options for CREATE ROLE are handled\n * seperately.\n */\nstatic void\nAppendRoleOption(StringInfo buf, ListCell *optionCell)\n{\n\tDefElem *option = (DefElem *) lfirst(optionCell);\n\n\tif (strcmp(option->defname, \"superuser\") == 0 && boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" SUPERUSER\");\n\t}\n\telse if (strcmp(option->defname, \"superuser\") == 0 && !boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" NOSUPERUSER\");\n\t}\n\telse if (strcmp(option->defname, \"createdb\") == 0 && boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" CREATEDB\");\n\t}\n\telse if (strcmp(option->defname, \"createdb\") == 0 && !boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" NOCREATEDB\");\n\t}\n\telse if (strcmp(option->defname, \"createrole\") == 0 && boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" CREATEROLE\");\n\t}\n\telse if (strcmp(option->defname, \"createrole\") == 0 && !boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" NOCREATEROLE\");\n\t}\n\telse if (strcmp(option->defname, \"inherit\") == 0 && boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" INHERIT\");\n\t}\n\telse if (strcmp(option->defname, \"inherit\") == 0 && !boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" NOINHERIT\");\n\t}\n\telse if (strcmp(option->defname, \"canlogin\") == 0 && boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" LOGIN\");\n\t}\n\telse if (strcmp(option->defname, \"canlogin\") == 0 && !boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" NOLOGIN\");\n\t}\n\telse if (strcmp(option->defname, \"isreplication\") == 0 && boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" REPLICATION\");\n\t}\n\telse if (strcmp(option->defname, \"isreplication\") == 0 && !boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" NOREPLICATION\");\n\t}\n\telse if (strcmp(option->defname, \"bypassrls\") == 0 && boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" BYPASSRLS\");\n\t}\n\telse if (strcmp(option->defname, \"bypassrls\") == 0 && !boolVal(option->arg))\n\t{\n\t\tappendStringInfo(buf, \" NOBYPASSRLS\");\n\t}\n\telse if (strcmp(option->defname, \"connectionlimit\") == 0)\n\t{\n\t\tappendStringInfo(buf, \" CONNECTION LIMIT %d\", intVal(option->arg));\n\t}\n\telse if (strcmp(option->defname, \"password\") == 0)\n\t{\n\t\tif (option->arg != NULL)\n\t\t{\n\t\t\tappendStringInfo(buf, \" PASSWORD %s\", quote_literal_cstr(strVal(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t option->arg)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(buf, \" PASSWORD NULL\");\n\t\t}\n\t}\n\telse if (strcmp(option->defname, \"validUntil\") == 0)\n\t{\n\t\tappendStringInfo(buf, \" VALID UNTIL %s\", quote_literal_cstr(strVal(option->arg)));\n\t}\n}\n\n\n/*\n * DeparseCreateRoleStmt builds and returns a string representing of the\n * CreateRoleStmt for application on a remote server.\n */\nchar *\nDeparseCreateRoleStmt(Node *node)\n{\n\tCreateRoleStmt *stmt = castNode(CreateRoleStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tAppendCreateRoleStmt(&buf, stmt);\n\n\treturn buf.data;\n}\n\n\nstatic void\nAppendSysIdStatement(StringInfo buf, ListCell *optionCell)\n{\n\tDefElem *option = (DefElem *) lfirst(optionCell);\n\tif (strcmp(option->defname, \"sysid\") == 0)\n\t{\n\t\tappendStringInfo(buf, \" SYSID %d\", intVal(option->arg));\n\t}\n}\n\n\n/*\n * AppendInlinePriviliges generates the string representation for the inline\n * privileges of the role in create statement and appends it to the buffer.\n */\nstatic void\nAppendInlinePriviliges(StringInfo buf, ListCell *optionCell)\n{\n\tDefElem *option = (DefElem *) lfirst(optionCell);\n\n\tif (strcmp(option->defname, \"adminmembers\") == 0)\n\t{\n\t\tappendStringInfo(buf, \" ADMIN \");\n\t\tAppendRoleList(buf, (List *) option->arg);\n\t}\n\telse if (strcmp(option->defname, \"rolemembers\") == 0)\n\t{\n\t\tappendStringInfo(buf, \" ROLE \");\n\t\tAppendRoleList(buf, (List *) option->arg);\n\t}\n\telse if (strcmp(option->defname, \"addroleto\") == 0)\n\t{\n\t\tappendStringInfo(buf, \" IN ROLE \");\n\t\tAppendRoleList(buf, (List *) option->arg);\n\t}\n}\n\n\n/*\n * AppendStatementType generates the string representation for the statement\n * type (role, user or group) in alter/create statement and appends it to the buffer.\n */\nstatic void\nAppendStatementType(StringInfo buf, CreateRoleStmt *stmt)\n{\n\tswitch (stmt->stmt_type)\n\t{\n\t\tcase ROLESTMT_ROLE:\n\t\t{\n\t\t\tappendStringInfo(buf, \"ROLE \");\n\t\t\tbreak;\n\t\t}\n\n\t\tcase ROLESTMT_USER:\n\t\t{\n\t\t\tappendStringInfo(buf, \"USER \");\n\t\t\tbreak;\n\t\t}\n\n\t\tcase ROLESTMT_GROUP:\n\t\t{\n\t\t\tappendStringInfo(buf, \"GROUP \");\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n/*\n * AppendCreateRoleStmt generates the string representation of the\n * CreateRoleStmt and appends it to the buffer.\n */\nstatic void\nAppendCreateRoleStmt(StringInfo buf, CreateRoleStmt *stmt)\n{\n\tListCell *optionCell = NULL;\n\n\tappendStringInfo(buf, \"CREATE \");\n\n\tAppendStatementType(buf, stmt);\n\n\tappendStringInfo(buf, \"%s\", quote_identifier(stmt->role));\n\n\tforeach(optionCell, stmt->options)\n\t{\n\t\tAppendRoleOption(buf, optionCell);\n\t\tAppendInlinePriviliges(buf, optionCell);\n\t\tAppendSysIdStatement(buf, optionCell);\n\t}\n}\n\n\n/*\n * DeparseDropRoleStmt builds and returns a string representing of the\n * DropRoleStmt for application on a remote server.\n */\nchar *\nDeparseDropRoleStmt(Node *node)\n{\n\tDropRoleStmt *stmt = castNode(DropRoleStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tAppendDropRoleStmt(&buf, stmt);\n\n\treturn buf.data;\n}\n\n\n/*\n * AppendDropRoleStmt generates the string representation of the\n * DropRoleStmt and appends it to the buffer.\n */\nstatic void\nAppendDropRoleStmt(StringInfo buf, DropRoleStmt *stmt)\n{\n\tappendStringInfo(buf, \"DROP ROLE \");\n\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfo(buf, \"IF EXISTS \");\n\t}\n\n\tAppendRoleList(buf, stmt->roles);\n}\n\n\nstatic void\nAppendRoleList(StringInfo buf, List *roleList)\n{\n\tListCell *cell = NULL;\n\tforeach(cell, roleList)\n\t{\n\t\tNode *roleNode = (Node *) lfirst(cell);\n\t\tAssert(IsA(roleNode, RoleSpec) || IsA(roleNode, AccessPriv));\n\t\tchar const *rolename = NULL;\n\t\tif (IsA(roleNode, RoleSpec))\n\t\t{\n\t\t\trolename = RoleSpecString((RoleSpec *) roleNode, true);\n\t\t}\n\t\tif (IsA(roleNode, AccessPriv))\n\t\t{\n\t\t\trolename = quote_identifier(((AccessPriv *) roleNode)->priv_name);\n\t\t}\n\t\tappendStringInfoString(buf, rolename);\n\t\tif (cell != list_tail(roleList))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\t}\n}\n\n\nchar *\nDeparseRenameRoleStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->renameType == OBJECT_ROLE);\n\n\tappendStringInfo(&str, \"ALTER ROLE %s RENAME TO %s;\",\n\t\t\t\t\t quote_identifier(stmt->subname), quote_identifier(stmt->newname));\n\n\treturn str.data;\n}\n\n\n/*\n * DeparseGrantRoleStmt builds and returns a string representing of the\n * GrantRoleStmt for application on a remote server.\n */\nchar *\nDeparseGrantRoleStmt(Node *node)\n{\n\tGrantRoleStmt *stmt = castNode(GrantRoleStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tAppendGrantRoleStmt(&buf, stmt);\n\n\treturn buf.data;\n}\n\n\n/*\n * Append the 'RESTRICT' or 'CASCADE' clause to the given buffer if the given\n * statement is a 'REVOKE' statement and the behavior is specified.\n * After PostgreSQL 16, the behavior is specified in the 'opt' field of\n * GrantRoleStmt and may have multiple values.\n * Here, compile time version is checked to support both versions.\n */\nstatic void\nAppendRevokeAdminOptionFor(StringInfo buf, GrantRoleStmt *stmt)\n{\n\tif (!stmt->is_grant)\n\t{\n\t\tDefElem *opt = NULL;\n\t\tforeach_declared_ptr(opt, stmt->opt)\n\t\t{\n\t\t\tif (strcmp(opt->defname, \"admin\") == 0)\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"ADMIN OPTION FOR \");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (strcmp(opt->defname, \"inherit\") == 0)\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"INHERIT OPTION FOR \");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (strcmp(opt->defname, \"set\") == 0)\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"SET OPTION FOR \");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void\nAppendGrantWithAdminOption(StringInfo buf, GrantRoleStmt *stmt)\n{\n\tif (stmt->is_grant)\n\t{\n\t\tint opt_count = 0;\n\t\tDefElem *opt = NULL;\n\t\tforeach_declared_ptr(opt, stmt->opt)\n\t\t{\n\t\t\tchar *optval = defGetString(opt);\n\t\t\tbool option_value = false;\n\t\t\tif (parse_bool(optval, &option_value))\n\t\t\t{\n\t\t\t\topt_count++;\n\t\t\t\tchar *prefix = opt_count > 1 ? \",\" : \" WITH\";\n\t\t\t\tif (strcmp(opt->defname, \"inherit\") == 0)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfo(buf, \"%s INHERIT %s\", prefix, option_value ? \"TRUE\" :\n\t\t\t\t\t\t\t\t\t \"FALSE\");\n\t\t\t\t}\n\t\t\t\telse if (strcmp(opt->defname, \"admin\") == 0 && option_value)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfo(buf, \"%s ADMIN OPTION\", prefix);\n\t\t\t\t}\n\t\t\t\telse if (strcmp(opt->defname, \"set\") == 0 && !option_value)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfo(buf, \"%s SET FALSE\", prefix);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * AppendGrantRoleStmt generates the string representation of the\n * GrantRoleStmt and appends it to the buffer.\n */\nstatic void\nAppendGrantRoleStmt(StringInfo buf, GrantRoleStmt *stmt)\n{\n\tappendStringInfo(buf, \"%s \", stmt->is_grant ? \"GRANT\" : \"REVOKE\");\n\tAppendRevokeAdminOptionFor(buf, stmt);\n\tAppendRoleList(buf, stmt->granted_roles);\n\tappendStringInfo(buf, \"%s \", stmt->is_grant ? \" TO \" : \" FROM \");\n\tAppendRoleList(buf, stmt->grantee_roles);\n\tAppendGrantWithAdminOption(buf, stmt);\n\tAppendGrantedByInGrantForRoleSpec(buf, stmt->grantor, stmt->is_grant);\n\tAppendGrantRestrictAndCascadeForRoleSpec(buf, stmt->behavior, stmt->is_grant);\n\tappendStringInfo(buf, \";\");\n}\n\n\n/*\n * AppendAlterRoleSetStmt generates the string representation of the\n * AlterRoleSetStmt and appends it to the buffer.\n */\nstatic void\nAppendAlterRoleSetStmt(StringInfo buf, AlterRoleSetStmt *stmt)\n{\n\tRoleSpec *role = stmt->role;\n\tconst char *roleSpecStr = NULL;\n\n\tif (role == NULL)\n\t{\n\t\t/*\n\t\t * If all roles are be affected, role field is left blank in an\n\t\t * AlterRoleSetStmt.\n\t\t */\n\t\troleSpecStr = \"ALL\";\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * If the role_specification used is CURRENT_USER or SESSION_USER,\n\t\t * it will be converted to thats roles role name.\n\t\t *\n\t\t * We also set withQuoteIdentifier parameter to true. Since the\n\t\t * roleSpecStr will be used in a query, the quotes are needed.\n\t\t */\n\t\troleSpecStr = RoleSpecString(role, true);\n\t}\n\n\tappendStringInfo(buf, \"ALTER ROLE %s\", roleSpecStr);\n\n\tif (stmt->database != NULL)\n\t{\n\t\tappendStringInfo(buf, \" IN DATABASE %s\", quote_identifier(stmt->database));\n\t}\n\n\tVariableSetStmt *setStmt = castNode(VariableSetStmt, stmt->setstmt);\n\tAppendVariableSet(buf, setStmt);\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_schema_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_schema_stmts.c\n *\t  All routines to deparse schema statements.\n *\t  This file contains all entry points specific for schema statement deparsing\n *\t  as well as functions that are currently only used for deparsing of the\n *\t  schema statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/nodes.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\nstatic void AppendCreateSchemaStmt(StringInfo buf, CreateSchemaStmt *stmt);\nstatic void AppendDropSchemaStmt(StringInfo buf, DropStmt *stmt);\nstatic void AppendGrantOnSchemaStmt(StringInfo buf, GrantStmt *stmt);\nstatic void AppendGrantOnSchemaSchemas(StringInfo buf, GrantStmt *stmt);\nstatic void AppendAlterSchemaRenameStmt(StringInfo buf, RenameStmt *stmt);\nstatic void AppendAlterSchemaOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);\n\nchar *\nDeparseCreateSchemaStmt(Node *node)\n{\n\tCreateSchemaStmt *stmt = castNode(CreateSchemaStmt, node);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendCreateSchemaStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseDropSchemaStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendDropSchemaStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseGrantOnSchemaStmt(Node *node)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_SCHEMA);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendGrantOnSchemaStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterSchemaOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendAlterSchemaOwnerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendAlterSchemaOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)\n{\n\tAssert(stmt->objectType == OBJECT_SCHEMA);\n\n\tappendStringInfo(buf, \"ALTER SCHEMA %s OWNER TO %s;\",\n\t\t\t\t\t quote_identifier(strVal(stmt->object)),\n\t\t\t\t\t RoleSpecString(stmt->newowner, true));\n}\n\n\nchar *\nDeparseAlterSchemaRenameStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendAlterSchemaRenameStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendCreateSchemaStmt(StringInfo buf, CreateSchemaStmt *stmt)\n{\n\tappendStringInfoString(buf, \"CREATE SCHEMA \");\n\n\tif (stmt->if_not_exists)\n\t{\n\t\tappendStringInfoString(buf, \"IF NOT EXISTS \");\n\t}\n\n\tif (stmt->schemaname != NULL)\n\t{\n\t\tappendStringInfo(buf, \"%s \", quote_identifier(stmt->schemaname));\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * If the schema name is not provided, the schema will be created\n\t\t * with the name of the authorizated user.\n\t\t */\n\t\tAssert(stmt->authrole != NULL);\n\t}\n\n\tif (stmt->authrole != NULL)\n\t{\n\t\tappendStringInfo(buf, \"AUTHORIZATION %s\", RoleSpecString(stmt->authrole, true));\n\t}\n}\n\n\nstatic void\nAppendDropSchemaStmt(StringInfo buf, DropStmt *stmt)\n{\n\tAssert(stmt->removeType == OBJECT_SCHEMA);\n\n\tappendStringInfoString(buf, \"DROP SCHEMA \");\n\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\n\tString *schemaValue = NULL;\n\tforeach_declared_ptr(schemaValue, stmt->objects)\n\t{\n\t\tconst char *schemaString = quote_identifier(strVal(schemaValue));\n\t\tappendStringInfo(buf, \"%s\", schemaString);\n\n\t\tif (schemaValue != llast(stmt->objects))\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\t}\n\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n\telse if (stmt->behavior == DROP_RESTRICT)\n\t{\n\t\tappendStringInfoString(buf, \" RESTRICT\");\n\t}\n}\n\n\nstatic void\nAppendGrantOnSchemaStmt(StringInfo buf, GrantStmt *stmt)\n{\n\tAssert(stmt->objtype == OBJECT_SCHEMA);\n\n\tAppendGrantSharedPrefix(buf, stmt);\n\n\tAppendGrantOnSchemaSchemas(buf, stmt);\n\n\tAppendGrantSharedSuffix(buf, stmt);\n}\n\n\nvoid\nAppendGrantPrivileges(StringInfo buf, GrantStmt *stmt)\n{\n\tif (list_length(stmt->privileges) == 0)\n\t{\n\t\tappendStringInfo(buf, \"ALL PRIVILEGES\");\n\t}\n\telse\n\t{\n\t\tListCell *cell = NULL;\n\t\tforeach(cell, stmt->privileges)\n\t\t{\n\t\t\tAccessPriv *privilege = (AccessPriv *) lfirst(cell);\n\t\t\tappendStringInfoString(buf, privilege->priv_name);\n\t\t\tif (cell != list_tail(stmt->privileges))\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \", \");\n\t\t\t}\n\t\t}\n\t}\n}\n\n\nstatic void\nAppendGrantOnSchemaSchemas(StringInfo buf, GrantStmt *stmt)\n{\n\tListCell *cell = NULL;\n\tappendStringInfo(buf, \" ON SCHEMA \");\n\n\tforeach(cell, stmt->objects)\n\t{\n\t\tchar *schema = strVal(lfirst(cell));\n\t\tappendStringInfoString(buf, quote_identifier(schema));\n\t\tif (cell != list_tail(stmt->objects))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\t}\n}\n\n\nvoid\nAppendGrantGrantees(StringInfo buf, GrantStmt *stmt)\n{\n\tListCell *cell = NULL;\n\tappendStringInfo(buf, \" %s \", stmt->is_grant ? \"TO\" : \"FROM\");\n\n\tforeach(cell, stmt->grantees)\n\t{\n\t\tRoleSpec *grantee = (RoleSpec *) lfirst(cell);\n\t\tappendStringInfoString(buf, RoleSpecString(grantee, true));\n\t\tif (cell != list_tail(stmt->grantees))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\t}\n}\n\n\nstatic void\nAppendAlterSchemaRenameStmt(StringInfo buf, RenameStmt *stmt)\n{\n\tAssert(stmt->renameType == OBJECT_SCHEMA);\n\n\tappendStringInfo(buf, \"ALTER SCHEMA %s RENAME TO %s;\",\n\t\t\t\t\t quote_identifier(stmt->subname), quote_identifier(stmt->newname));\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_seclabel_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_seclabel_stmts.c\n *\t  All routines to deparse SECURITY LABEL statements.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/deparser.h\"\n\nstatic void\nBeginSecLabel(StringInfo buf, SecLabelStmt *stmt)\n{\n\tinitStringInfo(buf);\n\tappendStringInfoString(buf, \"SECURITY LABEL \");\n\n\tif (stmt->provider != NULL)\n\t{\n\t\tappendStringInfo(buf, \"FOR %s \", quote_identifier(stmt->provider));\n\t}\n\n\tappendStringInfoString(buf, \"ON \");\n}\n\n\nstatic void\nEndSecLabel(StringInfo buf, SecLabelStmt *stmt)\n{\n\tappendStringInfo(buf, \"IS %s\", (stmt->label != NULL) ?\n\t\t\t\t\t quote_literal_cstr(stmt->label) : \"NULL\");\n}\n\n\n/*\n * DeparseRoleSecLabelStmt builds and returns a string representation of the\n * SecLabelStmt for application on a remote server. The SecLabelStmt is for\n * a role object.\n */\nchar *\nDeparseRoleSecLabelStmt(Node *node)\n{\n\tSecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node);\n\tchar *role_name = strVal(secLabelStmt->object);\n\tStringInfoData buf = { 0 };\n\n\tBeginSecLabel(&buf, secLabelStmt);\n\tappendStringInfo(&buf, \"ROLE %s \", quote_identifier(role_name));\n\tEndSecLabel(&buf, secLabelStmt);\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseTableSecLabelStmt builds and returns a string representation of the\n * SecLabelStmt for application on a remote server. The SecLabelStmt is for\ta\n * table.\n */\nchar *\nDeparseTableSecLabelStmt(Node *node)\n{\n\tSecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node);\n\tList *names = (List *) secLabelStmt->object;\n\tStringInfoData buf = { 0 };\n\n\tBeginSecLabel(&buf, secLabelStmt);\n\tappendStringInfo(&buf, \"TABLE %s\", quote_identifier(strVal(linitial(names))));\n\tif (list_length(names) > 1)\n\t{\n\t\tappendStringInfo(&buf, \".%s\", quote_identifier(strVal(lsecond(names))));\n\t}\n\tappendStringInfoString(&buf, \" \");\n\tEndSecLabel(&buf, secLabelStmt);\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseColumnSecLabelStmt builds and returns a string representation of the\n * SecLabelStmt for application on a remote server. The SecLabelStmt is for\ta\n * column of a distributed table.\n */\nchar *\nDeparseColumnSecLabelStmt(Node *node)\n{\n\tSecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node);\n\tList *names = (List *) secLabelStmt->object;\n\tStringInfoData buf = { 0 };\n\n\tBeginSecLabel(&buf, secLabelStmt);\n\tappendStringInfo(&buf, \"COLUMN %s.%s\",\n\t\t\t\t\t quote_identifier(strVal(linitial(names))),\n\t\t\t\t\t quote_identifier(strVal(lsecond(names))));\n\tif (list_length(names) > 2)\n\t{\n\t\tappendStringInfo(&buf, \".%s\", quote_identifier(strVal(lthird(names))));\n\t}\n\tappendStringInfoString(&buf, \" \");\n\tEndSecLabel(&buf, secLabelStmt);\n\n\treturn buf.data;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_sequence_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_sequence_stmts.c\n *\n *\t  All routines to deparse sequence statements.\n *\t  This file contains all entry points specific for sequence statement\n *    deparsing\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"utils/acl.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/deparser.h\"\n#include \"distributed/version_compat.h\"\n\n\n/* forward declaration for deparse functions */\nstatic void AppendDropSequenceStmt(StringInfo buf, DropStmt *stmt);\nstatic void AppendSequenceNameList(StringInfo buf, List *objects, ObjectType objtype);\nstatic void AppendRenameSequenceStmt(StringInfo buf, RenameStmt *stmt);\nstatic void AppendAlterSequenceSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);\nstatic void AppendAlterSequenceOwnerStmt(StringInfo buf, AlterTableStmt *stmt);\nstatic void AppendAlterSequencePersistenceStmt(StringInfo buf, AlterTableStmt *stmt);\nstatic void AppendGrantOnSequenceStmt(StringInfo buf, GrantStmt *stmt);\nstatic void AppendGrantOnSequenceSequences(StringInfo buf, GrantStmt *stmt);\n\n/*\n * DeparseDropSequenceStmt builds and returns a string representing the DropStmt\n */\nchar *\nDeparseDropSequenceStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->removeType == OBJECT_SEQUENCE);\n\n\tAppendDropSequenceStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendDropSequenceStmt appends a string representing the DropStmt to a buffer\n */\nstatic void\nAppendDropSequenceStmt(StringInfo buf, DropStmt *stmt)\n{\n\tappendStringInfoString(buf, \"DROP SEQUENCE \");\n\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\n\tAppendSequenceNameList(buf, stmt->objects, stmt->removeType);\n\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n\n\tappendStringInfoString(buf, \";\");\n}\n\n\n/*\n * AppendSequenceNameList appends a string representing the list of sequence names to a buffer\n */\nstatic void\nAppendSequenceNameList(StringInfo buf, List *objects, ObjectType objtype)\n{\n\tListCell *objectCell = NULL;\n\tforeach(objectCell, objects)\n\t{\n\t\tif (objectCell != list_head(objects))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\n\t\tRangeVar *seq = makeRangeVarFromNameList((List *) lfirst(objectCell));\n\n\t\tchar *qualifiedSequenceName = quote_qualified_identifier(seq->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t seq->relname);\n\t\tappendStringInfoString(buf, qualifiedSequenceName);\n\t}\n}\n\n\n/*\n * DeparseRenameSequenceStmt builds and returns a string representing the RenameStmt\n */\nchar *\nDeparseRenameSequenceStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->renameType == OBJECT_SEQUENCE);\n\n\tAppendRenameSequenceStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendRenameSequenceStmt appends a string representing the RenameStmt to a buffer\n */\nstatic void\nAppendRenameSequenceStmt(StringInfo buf, RenameStmt *stmt)\n{\n\tRangeVar *seq = stmt->relation;\n\n\tchar *qualifiedSequenceName = quote_qualified_identifier(seq->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t seq->relname);\n\n\tappendStringInfoString(buf, \"ALTER SEQUENCE \");\n\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\n\tappendStringInfoString(buf, qualifiedSequenceName);\n\n\tappendStringInfo(buf, \" RENAME TO %s\", quote_identifier(stmt->newname));\n}\n\n\n/*\n * DeparseAlterSequenceSchemaStmt builds and returns a string representing the AlterObjectSchemaStmt\n */\nchar *\nDeparseAlterSequenceSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objectType == OBJECT_SEQUENCE);\n\n\tAppendAlterSequenceSchemaStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterSequenceSchemaStmt appends a string representing the AlterObjectSchemaStmt to a buffer\n */\nstatic void\nAppendAlterSequenceSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt)\n{\n\tRangeVar *seq = stmt->relation;\n\n\tchar *qualifiedSequenceName = quote_qualified_identifier(seq->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t seq->relname);\n\n\tappendStringInfoString(buf, \"ALTER SEQUENCE \");\n\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\n\tappendStringInfoString(buf, qualifiedSequenceName);\n\n\tappendStringInfo(buf, \" SET SCHEMA %s;\", quote_identifier(stmt->newschema));\n}\n\n\n/*\n * DeparseAlterSequenceOwnerStmt builds and returns a string representing the AlterTableStmt\n * consisting of changing the owner of a sequence\n */\nchar *\nDeparseAlterSequenceOwnerStmt(Node *node)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tAppendAlterSequenceOwnerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterSequenceOwnerStmt appends a string representing the AlterTableStmt to a buffer\n * consisting of changing the owner of a sequence\n */\nstatic void\nAppendAlterSequenceOwnerStmt(StringInfo buf, AlterTableStmt *stmt)\n{\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\tRangeVar *seq = stmt->relation;\n\tchar *qualifiedSequenceName = quote_qualified_identifier(seq->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t seq->relname);\n\tappendStringInfoString(buf, \"ALTER SEQUENCE \");\n\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\n\tappendStringInfoString(buf, qualifiedSequenceName);\n\n\tListCell *cmdCell = NULL;\n\tforeach(cmdCell, stmt->cmds)\n\t{\n\t\tif (cmdCell != list_head(stmt->cmds))\n\t\t{\n\t\t\t/*\n\t\t\t * normally we shouldn't ever reach this\n\t\t\t * because we enter this function after making sure we have only\n\t\t\t * one subcommand of the type AT_ChangeOwner\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\"More than one subcommand is not supported \"\n\t\t\t\t\t\t\t\t   \"for ALTER SEQUENCE\")));\n\t\t}\n\n\t\tAlterTableCmd *alterTableCmd = castNode(AlterTableCmd, lfirst(cmdCell));\n\t\tswitch (alterTableCmd->subtype)\n\t\t{\n\t\t\tcase AT_ChangeOwner:\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \" OWNER TO %s;\", get_rolespec_name(\n\t\t\t\t\t\t\t\t\t alterTableCmd->newowner));\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * normally we shouldn't ever reach this\n\t\t\t\t * because we enter this function after making sure this stmt is of the form\n\t\t\t\t * ALTER SEQUENCE .. OWNER TO ..\n\t\t\t\t */\n\t\t\t\tereport(ERROR, (errmsg(\"unsupported subtype for alter sequence command\"),\n\t\t\t\t\t\t\t\terrdetail(\"sub command type: %d\",\n\t\t\t\t\t\t\t\t\t\t  alterTableCmd->subtype)));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * DeparseAlterSequencePersistenceStmt builds and returns a string representing\n * the AlterTableStmt consisting of changing the persistence of a sequence\n */\nchar *\nDeparseAlterSequencePersistenceStmt(Node *node)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tAppendAlterSequencePersistenceStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterSequencePersistenceStmt appends a string representing the\n * AlterTableStmt to a buffer consisting of changing the persistence of a sequence\n */\nstatic void\nAppendAlterSequencePersistenceStmt(StringInfo buf, AlterTableStmt *stmt)\n{\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tRangeVar *seq = stmt->relation;\n\tchar *qualifiedSequenceName = quote_qualified_identifier(seq->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t seq->relname);\n\tappendStringInfoString(buf, \"ALTER SEQUENCE \");\n\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\n\tappendStringInfoString(buf, qualifiedSequenceName);\n\n\tListCell *cmdCell = NULL;\n\tforeach(cmdCell, stmt->cmds)\n\t{\n\t\tif (cmdCell != list_head(stmt->cmds))\n\t\t{\n\t\t\t/*\n\t\t\t * As of PG15, we cannot reach this code because ALTER SEQUENCE\n\t\t\t * is only supported for a single sequence. Still, let's be\n\t\t\t * defensive for future PG changes\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\"More than one subcommand is not supported \"\n\t\t\t\t\t\t\t\t   \"for ALTER SEQUENCE\")));\n\t\t}\n\n\t\tAlterTableCmd *alterTableCmd = castNode(AlterTableCmd, lfirst(cmdCell));\n\t\tswitch (alterTableCmd->subtype)\n\t\t{\n\t\t\tcase AT_SetLogged:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" SET LOGGED;\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase AT_SetUnLogged:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" SET UNLOGGED;\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * normally we shouldn't ever reach this\n\t\t\t\t * because we enter this function after making sure this stmt is of the form\n\t\t\t\t * ALTER SEQUENCE .. SET LOGGED/UNLOGGED\n\t\t\t\t */\n\t\t\t\tereport(ERROR, (errmsg(\"unsupported subtype for alter sequence command\"),\n\t\t\t\t\t\t\t\terrdetail(\"sub command type: %d\",\n\t\t\t\t\t\t\t\t\t\t  alterTableCmd->subtype)));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * DeparseGrantOnSequenceStmt builds and returns a string representing the GrantOnSequenceStmt\n */\nchar *\nDeparseGrantOnSequenceStmt(Node *node)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendGrantOnSequenceStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendGrantOnSequenceStmt builds and returns an SQL command representing a\n * GRANT .. ON SEQUENCE command from given GrantStmt object.\n */\nstatic void\nAppendGrantOnSequenceStmt(StringInfo buf, GrantStmt *stmt)\n{\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tif (stmt->targtype == ACL_TARGET_ALL_IN_SCHEMA)\n\t{\n\t\t/*\n\t\t * Normally we shouldn't reach this\n\t\t * We deparse a GrantStmt with OBJECT_SEQUENCE after setting targtype\n\t\t * to ACL_TARGET_OBJECT\n\t\t */\n\t\telog(ERROR,\n\t\t\t \"GRANT .. ALL SEQUENCES IN SCHEMA is not supported for formatting.\");\n\t}\n\n\tAppendGrantSharedPrefix(buf, stmt);\n\n\tAppendGrantOnSequenceSequences(buf, stmt);\n\n\tAppendGrantSharedSuffix(buf, stmt);\n}\n\n\n/*\n * AppendGrantOnSequenceSequences appends the sequence names along with their arguments\n * to the given StringInfo from the given GrantStmt\n */\nstatic void\nAppendGrantOnSequenceSequences(StringInfo buf, GrantStmt *stmt)\n{\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tappendStringInfoString(buf, \" ON SEQUENCE \");\n\n\tListCell *cell = NULL;\n\tforeach(cell, stmt->objects)\n\t{\n\t\t/*\n\t\t * GrantOnSequence statement keeps its objects (sequences) as\n\t\t * a list of RangeVar-s\n\t\t */\n\t\tRangeVar *sequence = (RangeVar *) lfirst(cell);\n\n\t\t/*\n\t\t * We have qualified the statement beforehand\n\t\t */\n\t\tappendStringInfoString(buf, quote_qualified_identifier(sequence->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   sequence->relname));\n\n\t\tif (cell != list_tail(stmt->objects))\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_statistics_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_statistics_stmts.c\n *\t  All routines to deparse statistics statements.\n *\t  This file contains all entry points specific for statistics statement deparsing\n *    as well as functions that are currently only used for deparsing of the statistics\n *    statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/nodes.h\"\n#include \"parser/parse_expr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/ruleutils.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/relay_utility.h\"\n\nstatic void AppendCreateStatisticsStmt(StringInfo buf, CreateStatsStmt *stmt);\nstatic void AppendDropStatisticsStmt(StringInfo buf, List *nameList, bool ifExists);\nstatic void AppendAlterStatisticsRenameStmt(StringInfo buf, RenameStmt *stmt);\nstatic void AppendAlterStatisticsSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);\nstatic void AppendAlterStatisticsStmt(StringInfo buf, AlterStatsStmt *stmt);\nstatic void AppendAlterStatisticsOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);\nstatic void AppendStatisticsName(StringInfo buf, CreateStatsStmt *stmt);\nstatic void AppendStatTypes(StringInfo buf, CreateStatsStmt *stmt);\nstatic void AppendColumnNames(StringInfo buf, CreateStatsStmt *stmt);\nstatic void AppendTableName(StringInfo buf, CreateStatsStmt *stmt);\n\nchar *\nDeparseCreateStatisticsStmt(Node *node)\n{\n\tCreateStatsStmt *stmt = castNode(CreateStatsStmt, node);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tAppendCreateStatisticsStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseDropStatisticsStmt(List *nameList, bool ifExists)\n{\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tAppendDropStatisticsStmt(&str, nameList, ifExists);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterStatisticsRenameStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tAppendAlterStatisticsRenameStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterStatisticsSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tAppendAlterStatisticsSchemaStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterStatisticsStmt(Node *node)\n{\n\tAlterStatsStmt *stmt = castNode(AlterStatsStmt, node);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tAppendAlterStatisticsStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterStatisticsOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_STATISTIC_EXT);\n\n\tStringInfoData str;\n\tinitStringInfo(&str);\n\n\tAppendAlterStatisticsOwnerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendCreateStatisticsStmt(StringInfo buf, CreateStatsStmt *stmt)\n{\n\tappendStringInfoString(buf, \"CREATE STATISTICS \");\n\n\tif (stmt->if_not_exists)\n\t{\n\t\tappendStringInfoString(buf, \"IF NOT EXISTS \");\n\t}\n\n\tAppendStatisticsName(buf, stmt);\n\n\tAppendStatTypes(buf, stmt);\n\n\tappendStringInfoString(buf, \" ON \");\n\n\tAppendColumnNames(buf, stmt);\n\n\tappendStringInfoString(buf, \" FROM \");\n\n\tAppendTableName(buf, stmt);\n}\n\n\nstatic void\nAppendDropStatisticsStmt(StringInfo buf, List *nameList, bool ifExists)\n{\n\tappendStringInfoString(buf, \"DROP STATISTICS \");\n\n\tif (ifExists)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\n\tappendStringInfo(buf, \"%s\", NameListToQuotedString(nameList));\n}\n\n\nstatic void\nAppendAlterStatisticsRenameStmt(StringInfo buf, RenameStmt *stmt)\n{\n\tappendStringInfo(buf, \"ALTER STATISTICS %s RENAME TO %s\",\n\t\t\t\t\t NameListToQuotedString((List *) stmt->object), quote_identifier(\n\t\t\t\t\t\t stmt->newname));\n}\n\n\nstatic void\nAppendAlterStatisticsSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt)\n{\n\tappendStringInfo(buf, \"ALTER STATISTICS %s SET SCHEMA %s\",\n\t\t\t\t\t NameListToQuotedString((List *) stmt->object), quote_identifier(\n\t\t\t\t\t\t stmt->newschema));\n}\n\n\nstatic void\nAppendAlterStatisticsStmt(StringInfo buf, AlterStatsStmt *stmt)\n{\n\tappendStringInfo(buf, \"ALTER STATISTICS %s SET STATISTICS %d\",\n\t\t\t\t\t NameListToQuotedString(stmt->defnames),\n\t\t\t\t\t getIntStxstattarget_compat(stmt->stxstattarget));\n}\n\n\nstatic void\nAppendAlterStatisticsOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)\n{\n\tList *names = (List *) stmt->object;\n\tappendStringInfo(buf, \"ALTER STATISTICS %s OWNER TO %s\",\n\t\t\t\t\t NameListToQuotedString(names),\n\t\t\t\t\t RoleSpecString(stmt->newowner, true));\n}\n\n\nstatic void\nAppendStatisticsName(StringInfo buf, CreateStatsStmt *stmt)\n{\n\tString *schemaNameVal = (String *) linitial(stmt->defnames);\n\tconst char *schemaName = quote_identifier(strVal(schemaNameVal));\n\n\tString *statNameVal = (String *) lsecond(stmt->defnames);\n\tconst char *statName = quote_identifier(strVal(statNameVal));\n\n\tappendStringInfo(buf, \"%s.%s\", schemaName, statName);\n}\n\n\nstatic void\nAppendStatTypes(StringInfo buf, CreateStatsStmt *stmt)\n{\n\tif (list_length(stmt->stat_types) == 0)\n\t{\n\t\treturn;\n\t}\n\n\tappendStringInfoString(buf, \" (\");\n\n\tString *statType = NULL;\n\tforeach_declared_ptr(statType, stmt->stat_types)\n\t{\n\t\tappendStringInfoString(buf, strVal(statType));\n\n\t\tif (statType != llast(stmt->stat_types))\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\t}\n\n\tappendStringInfoString(buf, \")\");\n}\n\n\n/* See ruleutils.c in postgres for the logic here. */\nstatic bool\nlooks_like_function(Node *node)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;           /* probably shouldn't happen */\n\t}\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_FuncExpr:\n\t\t{\n\t\t\t/* OK, unless it's going to deparse as a cast */\n\t\t\treturn (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||\n\t\t\t\t\t((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);\n\t\t}\n\n\t\tcase T_NullIfExpr:\n\t\tcase T_CoalesceExpr:\n\t\tcase T_MinMaxExpr:\n\t\tcase T_SQLValueFunction:\n\t\tcase T_XmlExpr:\n\t\t{\n\t\t\t/* these are all accepted by func_expr_common_subexpr */\n\t\t\treturn true;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn false;\n}\n\n\nstatic void\nAppendColumnNames(StringInfo buf, CreateStatsStmt *stmt)\n{\n\tStatsElem *column = NULL;\n\n\tforeach_declared_ptr(column, stmt->exprs)\n\t{\n\t\tif (!column->name)\n\t\t{\n\t\t\t/*\n\t\t\t * Since these expressions are parser statements, we first call\n\t\t\t * transform to get the transformed Expr tree, and then deparse\n\t\t\t * the transformed tree. This is similar to the logic found in\n\t\t\t * deparse_table_stmts for check constraints.\n\t\t\t */\n\t\t\tif (list_length(stmt->relations) != 1)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\"cannot use expressions in CREATE STATISTICS with multiple relations\")));\n\t\t\t}\n\n\t\t\tRangeVar *rel = (RangeVar *) linitial(stmt->relations);\n\t\t\tbool missingOk = false;\n\t\t\tOid relOid = RangeVarGetRelid(rel, AccessShareLock, missingOk);\n\n\t\t\t/* Add table name to the name space in parse state. Otherwise column names\n\t\t\t * cannot be found.\n\t\t\t */\n\t\t\tRelation relation = table_open(relOid, AccessShareLock);\n\t\t\tParseState *pstate = make_parsestate(NULL);\n\t\t\tAddRangeTableEntryToQueryCompat(pstate, relation);\n\t\t\tNode *exprCooked = transformExpr(pstate, column->expr,\n\t\t\t\t\t\t\t\t\t\t\t EXPR_KIND_STATS_EXPRESSION);\n\n\t\t\tchar *relationName = get_rel_name(relOid);\n\t\t\tList *relationCtx = deparse_context_for(relationName, relOid);\n\n\t\t\tchar *exprSql = deparse_expression(exprCooked, relationCtx, false, false);\n\t\t\trelation_close(relation, NoLock);\n\n\t\t\t/* Need parens if it's not a bare function call */\n\t\t\tif (looks_like_function(exprCooked))\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, exprSql);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"(%s)\", exprSql);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst char *columnName = quote_identifier(column->name);\n\n\t\t\tappendStringInfoString(buf, columnName);\n\t\t}\n\n\t\tif (column != llast(stmt->exprs))\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\t}\n}\n\n\nstatic void\nAppendTableName(StringInfo buf, CreateStatsStmt *stmt)\n{\n\t/* statistics' can be created with only one relation */\n\tAssert(list_length(stmt->relations) == 1);\n\tRangeVar *relation = (RangeVar *) linitial(stmt->relations);\n\tchar *relationName = relation->relname;\n\tchar *schemaName = relation->schemaname;\n\n\tappendStringInfoString(buf, quote_qualified_identifier(schemaName, relationName));\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_table_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_table_stmts.c\n *\t  All routines to deparse table statements.\n *\t  This file contains all entry points specific for table statement deparsing as well as\n *\t  functions that are currently only used for deparsing of the table statements.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"catalog/heap.h\"\n#include \"commands/defrem.h\"\n#include \"commands/tablecmds.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"parser/parse_expr.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parse_type.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/ruleutils.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"distributed/version_compat.h\"\n\nstatic void AppendAlterTableSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);\nstatic void AppendAlterTableStmt(StringInfo buf, AlterTableStmt *stmt);\nstatic void AppendAlterTableCmd(StringInfo buf, AlterTableCmd *alterTableCmd,\n\t\t\t\t\t\t\t\tAlterTableStmt *stmt);\nstatic void AppendAlterTableCmdAddColumn(StringInfo buf, AlterTableCmd *alterTableCmd,\n\t\t\t\t\t\t\t\t\t\t AlterTableStmt *stmt);\nstatic void AppendAlterTableCmdDropConstraint(StringInfo buf,\n\t\t\t\t\t\t\t\t\t\t\t  AlterTableCmd *alterTableCmd);\n\nchar *\nDeparseAlterTableSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objectType == OBJECT_TABLE || stmt->objectType == OBJECT_FOREIGN_TABLE);\n\n\tAppendAlterTableSchemaStmt(&str, stmt);\n\treturn str.data;\n}\n\n\nstatic void\nAppendAlterTableSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt)\n{\n\tAssert(stmt->objectType == OBJECT_TABLE || stmt->objectType == OBJECT_FOREIGN_TABLE);\n\n\tbool isForeignTable = stmt->objectType == OBJECT_FOREIGN_TABLE;\n\tappendStringInfo(buf, \"ALTER %sTABLE \", isForeignTable ? \"FOREIGN \" : \"\");\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfo(buf, \"IF EXISTS \");\n\t}\n\tchar *tableName = quote_qualified_identifier(stmt->relation->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t stmt->relation->relname);\n\tconst char *newSchemaName = quote_identifier(stmt->newschema);\n\tappendStringInfo(buf, \"%s SET SCHEMA %s;\", tableName, newSchemaName);\n}\n\n\n/*\n * DeparseAlterTableStmt builds and returns a string representing the\n * AlterTableStmt where the object acted upon is of kind OBJECT_TABLE\n */\nchar *\nDeparseAlterTableStmt(Node *node)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objtype == OBJECT_TABLE);\n\n\tAppendAlterTableStmt(&str, stmt);\n\treturn str.data;\n}\n\n\n/*\n * AppendAlterTableStmt builds and returns an SQL command representing an\n * ALTER TABLE statement from given AlterTableStmt object where the object\n * acted upon is of kind OBJECT_TABLE\n */\nstatic void\nAppendAlterTableStmt(StringInfo buf, AlterTableStmt *stmt)\n{\n\tconst char *identifier = quote_qualified_identifier(stmt->relation->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstmt->relation->relname);\n\tListCell *cmdCell = NULL;\n\n\tAssert(stmt->objtype == OBJECT_TABLE);\n\n\tappendStringInfo(buf, \"ALTER TABLE %s\", identifier);\n\tforeach(cmdCell, stmt->cmds)\n\t{\n\t\tif (cmdCell != list_head(stmt->cmds))\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\n\t\tAlterTableCmd *alterTableCmd = castNode(AlterTableCmd, lfirst(cmdCell));\n\t\tAppendAlterTableCmd(buf, alterTableCmd, stmt);\n\t}\n\n\tappendStringInfoString(buf, \";\");\n}\n\n\n/*\n * AppendColumnNameList converts a list of columns into comma separated string format\n * (colname_1, colname_2, .., colname_n).\n */\nvoid\nAppendColumnNameList(StringInfo buf, List *columns)\n{\n\tappendStringInfoString(buf, \" (\");\n\n\tListCell *lc;\n\tbool firstkey = true;\n\n\tforeach(lc, columns)\n\t{\n\t\tif (firstkey == false)\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\n\t\tappendStringInfo(buf, \"%s\", quote_identifier(strVal(lfirst(lc))));\n\t\tfirstkey = false;\n\t}\n\n\tappendStringInfoString(buf, \" )\");\n}\n\n\n/*\n * AppendAlterTableCmdConstraint builds a string required to create given\n * constraint as part of an ADD CONSTRAINT or an ADD COLUMN subcommand,\n * and appends it to the buf.\n */\nstatic void\nAppendAlterTableCmdConstraint(StringInfo buf, Constraint *constraint,\n\t\t\t\t\t\t\t  AlterTableStmt *stmt, AlterTableType subtype)\n{\n\tif (subtype != AT_AddConstraint && subtype != AT_AddColumn)\n\t{\n\t\tereport(ERROR, (errmsg(\"Unsupported alter table subtype: %d\", (int) subtype)));\n\t}\n\n\t/* Need to deparse the alter table constraint command only if we are adding a constraint name.*/\n\tif (constraint->conname == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"Constraint name can not be NULL when deparsing the constraint.\")));\n\t}\n\n\tif (subtype == AT_AddConstraint)\n\t{\n\t\tappendStringInfoString(buf, \" ADD CONSTRAINT \");\n\t}\n\telse\n\t{\n\t\tappendStringInfoString(buf, \" CONSTRAINT \");\n\t}\n\n\tappendStringInfo(buf, \"%s \", quote_identifier(constraint->conname));\n\n\tif (constraint->contype == CONSTR_PRIMARY || constraint->contype == CONSTR_UNIQUE)\n\t{\n\t\tif (constraint->contype == CONSTR_PRIMARY)\n\t\t{\n\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t   \" PRIMARY KEY \");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfoString(buf, \" UNIQUE\");\n\n\t\t\tif (constraint->nulls_not_distinct == true)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" NULLS NOT DISTINCT\");\n\t\t\t}\n\t\t}\n\n\t\tif (subtype == AT_AddConstraint)\n\t\t{\n\t\t\tAppendColumnNameList(buf, constraint->keys);\n\t\t}\n\n\t\tif (constraint->including != NULL)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" INCLUDE \");\n\n\t\t\tAppendColumnNameList(buf, constraint->including);\n\t\t}\n\n\t\tif (constraint->options != NIL)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" WITH(\");\n\n\t\t\tListCell *defListCell;\n\t\t\tforeach(defListCell, constraint->options)\n\t\t\t{\n\t\t\t\tDefElem *def = (DefElem *) lfirst(defListCell);\n\n\t\t\t\tbool first = (defListCell == list_head(constraint->options));\n\t\t\t\tappendStringInfo(buf, \"%s%s=%s\", first ? \"\" : \",\",\n\t\t\t\t\t\t\t\t quote_identifier(def->defname),\n\t\t\t\t\t\t\t\t quote_literal_cstr(defGetString(def)));\n\t\t\t}\n\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t}\n\telse if (constraint->contype == CONSTR_EXCLUSION)\n\t{\n\t\t/*\n\t\t * This block constructs the EXCLUDE clause which is in the following form:\n\t\t * EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] )\n\t\t */\n\t\tappendStringInfoString(buf, \" EXCLUDE \");\n\n\t\tif (constraint->access_method != NULL)\n\t\t{\n\t\t\tappendStringInfoString(buf, \"USING \");\n\t\t\tappendStringInfo(buf, \"%s \", quote_identifier(\n\t\t\t\t\t\t\t\t constraint->access_method));\n\t\t}\n\n\t\tappendStringInfoString(buf, \" (\");\n\n\t\tListCell *lc;\n\t\tbool firstOp = true;\n\n\t\tforeach(lc, constraint->exclusions)\n\t\t{\n\t\t\tList *pair = (List *) lfirst(lc);\n\n\t\t\tAssert(list_length(pair) == 2);\n\t\t\tIndexElem *elem = linitial_node(IndexElem, pair);\n\t\t\tList *opname = lsecond_node(List, pair);\n\t\t\tif (firstOp == false)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" ,\");\n\t\t\t}\n\n\t\t\tListCell *lc2;\n\n\t\t\tforeach(lc2, opname)\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"%s WITH %s\", quote_identifier(elem->name),\n\t\t\t\t\t\t\t\t strVal(lfirst(lc2)));\n\t\t\t}\n\n\t\t\tfirstOp = false;\n\t\t}\n\n\t\tappendStringInfoString(buf, \" )\");\n\t}\n\telse if (constraint->contype == CONSTR_CHECK)\n\t{\n\t\tif (subtype == AT_AddColumn)\n\t\t{\n\t\t\t/*\n\t\t\t * Preprocess should've rejected deparsing such an ALTER TABLE\n\t\t\t * command but be on the safe side.\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\"cannot add check constraint to column by \"\n\t\t\t\t\t\t\t\t   \"using ADD COLUMN command\"),\n\t\t\t\t\t\t\terrhint(\"Consider using ALTER TABLE ... ADD CONSTRAINT \"\n\t\t\t\t\t\t\t\t\t\"... CHECK command after adding the column\")));\n\t\t}\n\n\t\tLOCKMODE lockmode = AlterTableGetLockLevel(stmt->cmds);\n\t\tOid leftRelationId = AlterTableLookupRelation(stmt, lockmode);\n\n\t\t/* To be able to use deparse_expression function, which creates an expression string,\n\t\t * the expression should be provided in its cooked form. We transform the raw expression\n\t\t * to cooked form.\n\t\t */\n\t\tParseState *pstate = make_parsestate(NULL);\n\t\tRelation relation = table_open(leftRelationId, AccessShareLock);\n\n\t\t/* Add table name to the name space in  parse state. Otherwise column names\n\t\t * cannot be found.\n\t\t */\n\t\tAddRangeTableEntryToQueryCompat(pstate, relation);\n\n\t\tNode *exprCooked = transformExpr(pstate, constraint->raw_expr,\n\n\t\t\t\t\t\t\t\t\t\t EXPR_KIND_CHECK_CONSTRAINT);\n\n\t\tchar *relationName = get_rel_name(leftRelationId);\n\t\tList *relationCtx = deparse_context_for(relationName, leftRelationId);\n\n\t\tchar *exprSql = deparse_expression(exprCooked, relationCtx, false, false);\n\n\t\trelation_close(relation, NoLock);\n\n\t\tappendStringInfo(buf, \" CHECK (%s)\", exprSql);\n\n\t\tif (constraint->is_no_inherit)\n\t\t{\n\t\t\tappendStringInfo(buf, \" NO INHERIT\");\n\t\t}\n\t}\n\telse if (constraint->contype == CONSTR_FOREIGN)\n\t{\n\t\tif (subtype == AT_AddConstraint)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" FOREIGN KEY\");\n\n\t\t\tAppendColumnNameList(buf, constraint->fk_attrs);\n\t\t}\n\n\t\tappendStringInfoString(buf, \" REFERENCES\");\n\n\t\tappendStringInfo(buf, \" %s\", quote_qualified_identifier(\n\t\t\t\t\t\t\t constraint->pktable->schemaname,\n\t\t\t\t\t\t\t constraint->pktable->relname));\n\n\t\tif (list_length(constraint->pk_attrs) > 0)\n\t\t{\n\t\t\tAppendColumnNameList(buf, constraint->pk_attrs);\n\t\t}\n\n\t\t/* Append supported options if provided */\n\n\t\t/* FKCONSTR_MATCH_SIMPLE is default. Append matchtype if not default */\n\t\tif (constraint->fk_matchtype == FKCONSTR_MATCH_FULL)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" MATCH FULL\");\n\t\t}\n\n\t\tswitch (constraint->fk_del_action)\n\t\t{\n\t\t\tcase FKCONSTR_ACTION_SETDEFAULT:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" ON DELETE SET DEFAULT\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase FKCONSTR_ACTION_SETNULL:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" ON DELETE SET NULL\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase FKCONSTR_ACTION_NOACTION:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" ON DELETE NO ACTION\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase FKCONSTR_ACTION_RESTRICT:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" ON DELETE RESTRICT\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase FKCONSTR_ACTION_CASCADE:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" ON DELETE CASCADE\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\telog(ERROR, \"unsupported FK delete action type: %d\",\n\t\t\t\t\t (int) constraint->fk_del_action);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tswitch (constraint->fk_upd_action)\n\t\t{\n\t\t\tcase FKCONSTR_ACTION_SETDEFAULT:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" ON UPDATE SET DEFAULT\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase FKCONSTR_ACTION_SETNULL:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" ON UPDATE SET NULL\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase FKCONSTR_ACTION_NOACTION:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" ON UPDATE NO ACTION\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase FKCONSTR_ACTION_RESTRICT:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" ON UPDATE RESTRICT\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase FKCONSTR_ACTION_CASCADE:\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" ON UPDATE CASCADE\");\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\telog(ERROR, \"unsupported FK update action type: %d\",\n\t\t\t\t\t (int) constraint->fk_upd_action);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * For ADD CONSTRAINT subcommand, FOREIGN KEY and CHECK constraints migth\n\t * have NOT VALID option.\n\t *\n\t * Note that skip_validation might be true for an ADD COLUMN too but this\n\t * is not because Postgres supports this but because Citus sets this flag\n\t * to true for foreign key constraints added via ADD COLUMN. So we don't\n\t * check for skip_validation for ADD COLUMN subcommand.\n\t */\n\tif (subtype == AT_AddConstraint && constraint->skip_validation)\n\t{\n\t\tappendStringInfoString(buf, \" NOT VALID \");\n\t}\n\n\tif (subtype == AT_AddColumn &&\n\t\t(constraint->deferrable || constraint->initdeferred))\n\t{\n\t\t/*\n\t\t * For ADD COLUMN subcommand, the fact that whether given constraint\n\t\t * is deferrable or initially deferred is indicated by another Constraint\n\t\t * object, not via deferrable / initdeferred fields.\n\t\t */\n\t\tereport(ERROR, (errmsg(\"unexpected value set for deferrable/initdeferred \"\n\t\t\t\t\t\t\t   \"field for an ADD COLUMN subcommand\")));\n\t}\n\n\tif (constraint->deferrable)\n\t{\n\t\tappendStringInfoString(buf, \" DEFERRABLE\");\n\n\t\tif (constraint->initdeferred)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" INITIALLY DEFERRED\");\n\t\t}\n\t}\n}\n\n\n/*\n * AppendAlterTableCmd builds and appends to the given buffer a command\n * from given AlterTableCmd object. Currently supported commands are of type\n * AT_AddColumn, AT_SetNotNull and AT_AddConstraint {PRIMARY KEY, UNIQUE, EXCLUDE}.\n */\nstatic void\nAppendAlterTableCmd(StringInfo buf, AlterTableCmd *alterTableCmd, AlterTableStmt *stmt)\n{\n\tswitch (alterTableCmd->subtype)\n\t{\n\t\tcase AT_AddColumn:\n\t\t{\n\t\t\tAppendAlterTableCmdAddColumn(buf, alterTableCmd, stmt);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase AT_DropConstraint:\n\t\t{\n\t\t\tAppendAlterTableCmdDropConstraint(buf, alterTableCmd);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase AT_AddConstraint:\n\t\t{\n\t\t\tConstraint *constraint = (Constraint *) alterTableCmd->def;\n\n\t\t\t/* We need to deparse ALTER TABLE ... ADD {PRIMARY KEY, UNIQUE, EXCLUSION} commands into\n\t\t\t * ALTER TABLE ... ADD CONSTRAINT <conname> {PRIMARY KEY, UNIQUE, EXCLUSION} ... format to be able\n\t\t\t * add a constraint name.\n\t\t\t */\n\t\t\tif (ConstrTypeCitusCanDefaultName(constraint->contype))\n\t\t\t{\n\t\t\t\tAppendAlterTableCmdConstraint(buf, constraint, stmt, AT_AddConstraint);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/* fallthrough */\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unsupported subtype for alter table command\"),\n\t\t\t\t\t\t\terrdetail(\"sub command type: %d\", alterTableCmd->subtype)));\n\t\t}\n\t}\n}\n\n\n/*\n * GeneratedWhenStr returns the char representation of given generated_when\n * value.\n */\nstatic const char *\nGeneratedWhenStr(char generatedWhen)\n{\n\tswitch (generatedWhen)\n\t{\n\t\tcase 'a':\n\t\t{\n\t\t\treturn \"ALWAYS\";\n\t\t}\n\n\t\tcase 'd':\n\t\t{\n\t\t\treturn \"BY DEFAULT\";\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unrecognized generated_when: %d\",\n\t\t\t\t\t\t\t\t   generatedWhen)));\n\t\t}\n\t}\n}\n\n\n/*\n * DeparseRawExprForColumnDefault returns string representation of given\n * rawExpr based on given column type information.\n */\nstatic char *\nDeparseRawExprForColumnDefault(Oid relationId, Oid columnTypeId, int32 columnTypeMod,\n\t\t\t\t\t\t\t   char *columnName, char attgenerated, Node *rawExpr)\n{\n\tParseState *pstate = make_parsestate(NULL);\n\tRelation relation = RelationIdGetRelation(relationId);\n\tAddRangeTableEntryToQueryCompat(pstate, relation);\n\n\tNode *defaultExpr = cookDefault(pstate, rawExpr,\n\t\t\t\t\t\t\t\t\tcolumnTypeId, columnTypeMod,\n\t\t\t\t\t\t\t\t\tcolumnName, attgenerated);\n\n\tList *deparseContext = deparse_context_for(get_rel_name(relationId), relationId);\n\n\tint saveNestLevel = PushEmptySearchPath();\n\tchar *defaultExprStr = deparse_expression(defaultExpr, deparseContext, false, false);\n\tPopEmptySearchPath(saveNestLevel);\n\n\tRelationClose(relation);\n\n\treturn defaultExprStr;\n}\n\n\n/*\n * AppendAlterTableCmd builds and appends to the given buffer an AT_AddColumn command\n * from given AlterTableCmd object in the form ADD COLUMN ...\n */\nstatic void\nAppendAlterTableCmdAddColumn(StringInfo buf, AlterTableCmd *alterTableCmd,\n\t\t\t\t\t\t\t AlterTableStmt *stmt)\n{\n\tAssert(alterTableCmd->subtype == AT_AddColumn);\n\n\tOid relationId = AlterTableLookupRelation(stmt, NoLock);\n\n\tappendStringInfoString(buf, \" ADD COLUMN \");\n\n\tif (alterTableCmd->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF NOT EXISTS \");\n\t}\n\n\tColumnDef *columnDefinition = (ColumnDef *) alterTableCmd->def;\n\n\tif (columnDefinition->colname)\n\t{\n\t\tappendStringInfo(buf, \"%s \", quote_identifier(columnDefinition->colname));\n\t}\n\n\tint32 typmod = 0;\n\tOid typeOid = InvalidOid;\n\tbits16 formatFlags = FORMAT_TYPE_TYPEMOD_GIVEN | FORMAT_TYPE_FORCE_QUALIFY;\n\ttypenameTypeIdAndMod(NULL, columnDefinition->typeName, &typeOid, &typmod);\n\tappendStringInfo(buf, \"%s\", format_type_extended(typeOid, typmod,\n\t\t\t\t\t\t\t\t\t\t\t\t\t formatFlags));\n\n\tif (columnDefinition->compression)\n\t{\n\t\tappendStringInfo(buf, \" COMPRESSION %s\",\n\t\t\t\t\t\t quote_identifier(columnDefinition->compression));\n\t}\n\n\tOid collationOid = GetColumnDefCollation(NULL, columnDefinition, typeOid);\n\tif (OidIsValid(collationOid))\n\t{\n\t\tconst char *identifier = FormatCollateBEQualified(collationOid);\n\t\tappendStringInfo(buf, \" COLLATE %s\", identifier);\n\t}\n\n\tListCell *constraintCell = NULL;\n\tforeach(constraintCell, columnDefinition->constraints)\n\t{\n\t\tConstraint *constraint = (Constraint *) lfirst(constraintCell);\n\n\t\tif (constraint->contype == CONSTR_NOTNULL)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" NOT NULL\");\n\t\t}\n\t\telse if (constraint->contype == CONSTR_NULL)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" NULL\");\n\t\t}\n\t\telse if (constraint->contype == CONSTR_DEFAULT)\n\t\t{\n\t\t\tchar attgenerated = '\\0';\n\t\t\tappendStringInfo(buf, \" DEFAULT %s\",\n\t\t\t\t\t\t\t DeparseRawExprForColumnDefault(relationId, typeOid, typmod,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolumnDefinition->colname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tattgenerated,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconstraint->raw_expr));\n\t\t}\n\t\telse if (constraint->contype == CONSTR_IDENTITY)\n\t\t{\n\t\t\t/*\n\t\t\t * Citus doesn't support adding identity columns via ALTER TABLE,\n\t\t\t * so we don't bother teaching the deparser about them.\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\"unexpectedly found identity column \"\n\t\t\t\t\t\t\t\t   \"definition in ALTER TABLE command\")));\n\t\t}\n\t\telse if (constraint->contype == CONSTR_GENERATED)\n\t\t{\n\t\t\tchar attgenerated = ATTRIBUTE_GENERATED_STORED;\n#if PG_VERSION_NUM >= PG_VERSION_18\n\t\t\tattgenerated = constraint->generated_kind;\n#endif\n\t\t\tappendStringInfo(buf, \" GENERATED %s AS (%s) %s\",\n\t\t\t\t\t\t\t GeneratedWhenStr(constraint->generated_when),\n\t\t\t\t\t\t\t DeparseRawExprForColumnDefault(relationId, typeOid, typmod,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolumnDefinition->colname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tattgenerated,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconstraint->raw_expr),\n\t\t\t\t\t\t\t (attgenerated == ATTRIBUTE_GENERATED_STORED ? \"STORED\" :\n\t\t\t\t\t\t\t  \"VIRTUAL\"));\n\t\t}\n\t\telse if (constraint->contype == CONSTR_CHECK ||\n\t\t\t\t constraint->contype == CONSTR_PRIMARY ||\n\t\t\t\t constraint->contype == CONSTR_UNIQUE ||\n\t\t\t\t constraint->contype == CONSTR_EXCLUSION ||\n\t\t\t\t constraint->contype == CONSTR_FOREIGN)\n\t\t{\n\t\t\tAppendAlterTableCmdConstraint(buf, constraint, stmt, AT_AddColumn);\n\t\t}\n\t\telse if (constraint->contype == CONSTR_ATTR_DEFERRABLE)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DEFERRABLE\");\n\t\t}\n\t\telse if (constraint->contype == CONSTR_ATTR_NOT_DEFERRABLE)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" NOT DEFERRABLE\");\n\t\t}\n\t\telse if (constraint->contype == CONSTR_ATTR_DEFERRED)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" INITIALLY DEFERRED\");\n\t\t}\n\t\telse if (constraint->contype == CONSTR_ATTR_IMMEDIATE)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" INITIALLY IMMEDIATE\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unsupported constraint type\"),\n\t\t\t\t\t\t\terrdetail(\"constraint type: %d\", constraint->contype)));\n\t\t}\n\t}\n}\n\n\n/*\n * AppendAlterTableCmdDropConstraint builds and appends to the given buffer an\n * AT_DropConstraint command from given AlterTableCmd object in the form\n * DROP CONSTRAINT ...\n */\nstatic void\nAppendAlterTableCmdDropConstraint(StringInfo buf, AlterTableCmd *alterTableCmd)\n{\n\tappendStringInfoString(buf, \" DROP CONSTRAINT\");\n\n\tif (alterTableCmd->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \" IF EXISTS\");\n\t}\n\n\tappendStringInfo(buf, \" %s\", quote_identifier(alterTableCmd->name));\n\n\tif (alterTableCmd->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_text_search.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_text_search.c\n *\t  All routines to deparse text search statements.\n *\t  This file contains all entry points specific for text search statement deparsing.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"commands/defrem.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\nstatic void AppendDefElemList(StringInfo buf, List *defelems, char *objectName);\n\nstatic void AppendStringInfoTokentypeList(StringInfo buf, List *tokentypes);\nstatic void AppendStringInfoDictnames(StringInfo buf, List *dicts);\n\n\n/*\n * DeparseCreateTextSearchConfigurationStmt returns the sql for a DefineStmt defining a\n * TEXT SEARCH CONFIGURATION\n *\n * Although the syntax is mutually exclusive on the two arguments that can be passed in\n * the deparser will syntactically correct multiple definitions if provided. *\n */\nchar *\nDeparseCreateTextSearchConfigurationStmt(Node *node)\n{\n\tDefineStmt *stmt = castNode(DefineStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tconst char *identifier = NameListToQuotedString(stmt->defnames);\n\tappendStringInfo(&buf, \"CREATE TEXT SEARCH CONFIGURATION %s \", identifier);\n\tappendStringInfoString(&buf, \"(\");\n\tAppendDefElemList(&buf, stmt->definition, \"CONFIGURATION\");\n\tappendStringInfoString(&buf, \");\");\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseCreateTextSearchDictionaryStmt returns the sql for a DefineStmt defining a\n * TEXT SEARCH DICTIONARY\n *\n * Although the syntax is mutually exclusive on the two arguments that can be passed in\n * the deparser will syntactically correct multiple definitions if provided. *\n */\nchar *\nDeparseCreateTextSearchDictionaryStmt(Node *node)\n{\n\tDefineStmt *stmt = castNode(DefineStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tconst char *identifier = NameListToQuotedString(stmt->defnames);\n\tappendStringInfo(&buf, \"CREATE TEXT SEARCH DICTIONARY %s \", identifier);\n\tappendStringInfoString(&buf, \"(\");\n\tAppendDefElemList(&buf, stmt->definition, \"DICTIONARY\");\n\tappendStringInfoString(&buf, \");\");\n\n\treturn buf.data;\n}\n\n\n/*\n * AppendDefElemList is a helper to append a comma separated list of definitions to a\n * define statement.\n *\n * The extra objectName parameter is used to create meaningful error messages.\n */\nstatic void\nAppendDefElemList(StringInfo buf, List *defelems, char *objectName)\n{\n\tDefElem *defelem = NULL;\n\tbool first = true;\n\tforeach_declared_ptr(defelem, defelems)\n\t{\n\t\tif (!first)\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\t\tfirst = false;\n\n\t\t/*\n\t\t * There are some operations that can omit the argument. In that case, we only use\n\t\t * the defname.\n\t\t *\n\t\t * For example, omitting [ = value ] in the next query results in resetting the\n\t\t * option to defaults:\n\t\t * ALTER TEXT SEARCH DICTIONARY name ( option [ = value ] );\n\t\t */\n\t\tif (defelem->arg == NULL)\n\t\t{\n\t\t\tappendStringInfo(buf, \"%s\", defelem->defname);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* extract value from defelem */\n\t\tconst char *value = defGetString(defelem);\n\n\t\t/* stringify */\n\t\tappendStringInfo(buf, \"%s = %s\", defelem->defname, value);\n\t}\n}\n\n\n/*\n * DeparseDropTextSearchConfigurationStmt returns the sql representation for a DROP TEXT\n * SEARCH CONFIGURATION ... statment. Supports dropping multiple configurations at once.\n */\nchar *\nDeparseDropTextSearchConfigurationStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tAssert(stmt->removeType == OBJECT_TSCONFIGURATION);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tappendStringInfoString(&buf, \"DROP TEXT SEARCH CONFIGURATION \");\n\tList *nameList = NIL;\n\tbool first = true;\n\tforeach_declared_ptr(nameList, stmt->objects)\n\t{\n\t\tif (!first)\n\t\t{\n\t\t\tappendStringInfoString(&buf, \", \");\n\t\t}\n\t\tfirst = false;\n\n\t\tappendStringInfoString(&buf, NameListToQuotedString(nameList));\n\t}\n\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(&buf, \" CASCADE\");\n\t}\n\n\tappendStringInfoString(&buf, \";\");\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseDropTextSearchDictionaryStmt returns the sql representation for a DROP TEXT SEARCH\n * DICTIONARY ... statment. Supports dropping multiple dictionaries at once.\n */\nchar *\nDeparseDropTextSearchDictionaryStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tAssert(stmt->removeType == OBJECT_TSDICTIONARY);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tappendStringInfoString(&buf, \"DROP TEXT SEARCH DICTIONARY \");\n\tList *nameList = NIL;\n\tbool first = true;\n\tforeach_declared_ptr(nameList, stmt->objects)\n\t{\n\t\tif (!first)\n\t\t{\n\t\t\tappendStringInfoString(&buf, \", \");\n\t\t}\n\t\tfirst = false;\n\n\t\tappendStringInfoString(&buf, NameListToQuotedString(nameList));\n\t}\n\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(&buf, \" CASCADE\");\n\t}\n\n\tappendStringInfoString(&buf, \";\");\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseRenameTextSearchConfigurationStmt returns the sql representation of a ALTER TEXT\n * SEARCH CONFIGURATION ... RENAME TO ... statement.\n */\nchar *\nDeparseRenameTextSearchConfigurationStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_TSCONFIGURATION);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tchar *identifier = NameListToQuotedString(castNode(List, stmt->object));\n\tappendStringInfo(&buf, \"ALTER TEXT SEARCH CONFIGURATION %s RENAME TO %s;\",\n\t\t\t\t\t identifier, quote_identifier(stmt->newname));\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseRenameTextSearchDictionaryStmt returns the sql representation of a ALTER TEXT SEARCH\n * DICTIONARY ... RENAME TO ... statement.\n */\nchar *\nDeparseRenameTextSearchDictionaryStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_TSDICTIONARY);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tchar *identifier = NameListToQuotedString(castNode(List, stmt->object));\n\tappendStringInfo(&buf, \"ALTER TEXT SEARCH DICTIONARY %s RENAME TO %s;\",\n\t\t\t\t\t identifier, quote_identifier(stmt->newname));\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseAlterTextSearchConfigurationStmt returns the sql representation of any generic\n * ALTER TEXT SEARCH CONFIGURATION .... statement. The statements supported include:\n *  - ALTER TEXT SEARCH CONFIGURATIONS ... ADD MAPPING FOR [, ...] WITH [, ...]\n *  - ALTER TEXT SEARCH CONFIGURATIONS ... ALTER MAPPING FOR [, ...] WITH [, ...]\n *  - ALTER TEXT SEARCH CONFIGURATIONS ... ALTER MAPPING REPLACE ... WITH ...\n *  - ALTER TEXT SEARCH CONFIGURATIONS ... ALTER MAPPING FOR [, ...] REPLACE ... WITH ...\n *  - ALTER TEXT SEARCH CONFIGURATIONS ... DROP MAPPING [ IF EXISTS ] FOR ...\n */\nchar *\nDeparseAlterTextSearchConfigurationStmt(Node *node)\n{\n\tAlterTSConfigurationStmt *stmt = castNode(AlterTSConfigurationStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tchar *identifier = NameListToQuotedString(castNode(List, stmt->cfgname));\n\tappendStringInfo(&buf, \"ALTER TEXT SEARCH CONFIGURATION %s\", identifier);\n\n\tswitch (stmt->kind)\n\t{\n\t\tcase ALTER_TSCONFIG_ADD_MAPPING:\n\t\t{\n\t\t\tappendStringInfoString(&buf, \" ADD MAPPING FOR \");\n\t\t\tAppendStringInfoTokentypeList(&buf, stmt->tokentype);\n\n\t\t\tappendStringInfoString(&buf, \" WITH \");\n\t\t\tAppendStringInfoDictnames(&buf, stmt->dicts);\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN:\n\t\t{\n\t\t\tappendStringInfoString(&buf, \" ALTER MAPPING FOR \");\n\t\t\tAppendStringInfoTokentypeList(&buf, stmt->tokentype);\n\n\t\t\tappendStringInfoString(&buf, \" WITH \");\n\t\t\tAppendStringInfoDictnames(&buf, stmt->dicts);\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase ALTER_TSCONFIG_REPLACE_DICT:\n\t\tcase ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN:\n\t\t{\n\t\t\tappendStringInfoString(&buf, \" ALTER MAPPING\");\n\t\t\tif (list_length(stmt->tokentype) > 0)\n\t\t\t{\n\t\t\t\tappendStringInfoString(&buf, \" FOR \");\n\t\t\t\tAppendStringInfoTokentypeList(&buf, stmt->tokentype);\n\t\t\t}\n\n\t\t\tif (list_length(stmt->dicts) != 2)\n\t\t\t{\n\t\t\t\telog(ERROR, \"unexpected number of dictionaries while deparsing ALTER \"\n\t\t\t\t\t\t\t\"TEXT SEARCH CONFIGURATION ... ALTER MAPPING [FOR ...] REPLACE \"\n\t\t\t\t\t\t\t\"statement.\");\n\t\t\t}\n\n\t\t\tappendStringInfo(&buf, \" REPLACE %s\",\n\t\t\t\t\t\t\t NameListToQuotedString(linitial(stmt->dicts)));\n\n\t\t\tappendStringInfo(&buf, \" WITH %s\",\n\t\t\t\t\t\t\t NameListToQuotedString(lsecond(stmt->dicts)));\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase ALTER_TSCONFIG_DROP_MAPPING:\n\t\t{\n\t\t\tappendStringInfoString(&buf, \" DROP MAPPING\");\n\n\t\t\tif (stmt->missing_ok)\n\t\t\t{\n\t\t\t\tappendStringInfoString(&buf, \" IF EXISTS\");\n\t\t\t}\n\n\t\t\tappendStringInfoString(&buf, \" FOR \");\n\t\t\tAppendStringInfoTokentypeList(&buf, stmt->tokentype);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"unable to deparse unsupported ALTER TEXT SEARCH STATEMENT\");\n\t\t}\n\t}\n\n\tappendStringInfoString(&buf, \";\");\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseAlterTextSearchConfigurationStmt returns the sql representation of any generic\n * ALTER TEXT SEARCH DICTIONARY .... statement. The statements supported include\n * - ALTER TEXT SEARCH DICTIONARY name ( option [ = value ] [, ... ] )\n */\nchar *\nDeparseAlterTextSearchDictionaryStmt(Node *node)\n{\n\tAlterTSDictionaryStmt *stmt = castNode(AlterTSDictionaryStmt, node);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tchar *identifier = NameListToQuotedString(castNode(List, stmt->dictname));\n\tappendStringInfo(&buf, \"ALTER TEXT SEARCH DICTIONARY %s ( \", identifier);\n\n\tAppendDefElemList(&buf, stmt->options, \"DICTIONARY\");\n\tappendStringInfoString(&buf, \" );\");\n\treturn buf.data;\n}\n\n\n/*\n * DeparseAlterTextSearchConfigurationSchemaStmt returns the sql statement representing\n * ALTER TEXT SEARCH CONFIGURATION ... SET SCHEMA ... statements.\n */\nchar *\nDeparseAlterTextSearchConfigurationSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TSCONFIGURATION);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tappendStringInfo(&buf, \"ALTER TEXT SEARCH CONFIGURATION %s SET SCHEMA %s;\",\n\t\t\t\t\t NameListToQuotedString(castNode(List, stmt->object)),\n\t\t\t\t\t quote_identifier(stmt->newschema));\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseAlterTextSearchDictionarySchemaStmt returns the sql statement representing ALTER TEXT\n * SEARCH DICTIONARY ... SET SCHEMA ... statements.\n */\nchar *\nDeparseAlterTextSearchDictionarySchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TSDICTIONARY);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tappendStringInfo(&buf, \"ALTER TEXT SEARCH DICTIONARY %s SET SCHEMA %s;\",\n\t\t\t\t\t NameListToQuotedString(castNode(List, stmt->object)),\n\t\t\t\t\t quote_identifier(stmt->newschema));\n\n\treturn buf.data;\n}\n\n\n/*\n * AppendStringInfoTokentypeList specializes in adding a comma separated list of\n * token_tyoe's to TEXT SEARCH CONFIGURATION commands\n */\nstatic void\nAppendStringInfoTokentypeList(StringInfo buf, List *tokentypes)\n{\n\tString *tokentype = NULL;\n\tbool first = true;\n\tforeach_declared_ptr(tokentype, tokentypes)\n\t{\n\t\tif (nodeTag(tokentype) != T_String)\n\t\t{\n\t\t\telog(ERROR,\n\t\t\t\t \"unexpected tokentype for deparsing in text search configuration\");\n\t\t}\n\n\t\tif (!first)\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\t\tfirst = false;\n\n\t\tappendStringInfoString(buf, strVal(tokentype));\n\t}\n}\n\n\n/*\n * AppendStringInfoDictnames specializes in appending a comma separated list of\n * dictionaries to TEXT SEARCH CONFIGURATION commands.\n */\nstatic void\nAppendStringInfoDictnames(StringInfo buf, List *dicts)\n{\n\tList *dictNames = NIL;\n\tbool first = true;\n\tforeach_declared_ptr(dictNames, dicts)\n\t{\n\t\tif (!first)\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\t\tfirst = false;\n\n\t\tchar *dictIdentifier = NameListToQuotedString(dictNames);\n\t\tappendStringInfoString(buf, dictIdentifier);\n\t}\n}\n\n\n/*\n * DeparseAlterTextSearchConfigurationOwnerStmt returns the sql statement representing\n * ALTER TEXT SEARCH CONFIGURATION ... ONWER TO ... commands.\n */\nchar *\nDeparseAlterTextSearchConfigurationOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_TSCONFIGURATION);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tappendStringInfo(&buf, \"ALTER TEXT SEARCH CONFIGURATION %s OWNER TO %s;\",\n\t\t\t\t\t NameListToQuotedString(castNode(List, stmt->object)),\n\t\t\t\t\t RoleSpecString(stmt->newowner, true));\n\n\treturn buf.data;\n}\n\n\n/*\n * DeparseAlterTextSearchDictionaryOwnerStmt returns the sql statement representing ALTER TEXT\n * SEARCH DICTIONARY ... ONWER TO ... commands.\n */\nchar *\nDeparseAlterTextSearchDictionaryOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_TSDICTIONARY);\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tappendStringInfo(&buf, \"ALTER TEXT SEARCH DICTIONARY %s OWNER TO %s;\",\n\t\t\t\t\t NameListToQuotedString(castNode(List, stmt->object)),\n\t\t\t\t\t RoleSpecString(stmt->newowner, true));\n\n\treturn buf.data;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_type_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_type_stmts.c\n *\t  All routines to deparse type statements.\n *\t  This file contains all entry points specific for type statement deparsing as well as\n *\t  functions that are currently only used for deparsing of the type statements.\n *\n *\t  Functions that could move later are AppendColumnDef, AppendColumnDefList, etc. These\n *\t  should be reused across multiple statements and should live in their own deparse\n *\t  file.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"parser/parse_type.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/version_compat.h\"\n\n#define AlterEnumIsRename(stmt) (stmt->oldVal != NULL)\n#define AlterEnumIsAddValue(stmt) (stmt->oldVal == NULL)\n\n/* forward declaration for deparse functions */\nstatic void AppendCompositeTypeStmt(StringInfo str, CompositeTypeStmt *stmt);\nstatic void AppendColumnDef(StringInfo str, ColumnDef *columnDef);\nstatic void AppendColumnDefList(StringInfo str, List *columnDefs);\n\nstatic void AppendCreateEnumStmt(StringInfo str, CreateEnumStmt *stmt);\nstatic void AppendStringList(StringInfo str, List *strings);\n\nstatic void AppendDropTypeStmt(StringInfo buf, DropStmt *stmt);\nstatic void AppendTypeNameList(StringInfo buf, List *objects);\n\nstatic void AppendAlterEnumStmt(StringInfo buf, AlterEnumStmt *stmt);\n\nstatic void AppendAlterTypeStmt(StringInfo buf, AlterTableStmt *stmt);\nstatic void AppendAlterTypeCmd(StringInfo buf, AlterTableCmd *alterTableCmd);\nstatic void AppendAlterTypeCmdAddColumn(StringInfo buf, AlterTableCmd *alterTableCmd);\nstatic void AppendAlterTypeCmdDropColumn(StringInfo buf, AlterTableCmd *alterTableCmd);\nstatic void AppendAlterTypeCmdAlterColumnType(StringInfo buf,\n\t\t\t\t\t\t\t\t\t\t\t  AlterTableCmd *alterTableCmd);\n\nstatic void AppendRenameTypeStmt(StringInfo buf, RenameStmt *stmt);\nstatic void AppendRenameTypeAttributeStmt(StringInfo buf, RenameStmt *stmt);\nstatic void AppendAlterTypeSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);\nstatic void AppendAlterTypeOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);\n\n\n/*\n * DeparseCompositeTypeStmt builds and returns a string representing the\n * CompositeTypeStmt for application on a remote server.\n */\nchar *\nDeparseCompositeTypeStmt(Node *node)\n{\n\tCompositeTypeStmt *stmt = castNode(CompositeTypeStmt, node);\n\tStringInfoData sql = { 0 };\n\tinitStringInfo(&sql);\n\n\tAppendCompositeTypeStmt(&sql, stmt);\n\n\treturn sql.data;\n}\n\n\nchar *\nDeparseCreateEnumStmt(Node *node)\n{\n\tCreateEnumStmt *stmt = castNode(CreateEnumStmt, node);\n\tStringInfoData sql = { 0 };\n\tinitStringInfo(&sql);\n\n\tAppendCreateEnumStmt(&sql, stmt);\n\n\treturn sql.data;\n}\n\n\nchar *\nDeparseAlterEnumStmt(Node *node)\n{\n\tAlterEnumStmt *stmt = castNode(AlterEnumStmt, node);\n\tStringInfoData sql = { 0 };\n\tinitStringInfo(&sql);\n\n\tAppendAlterEnumStmt(&sql, stmt);\n\n\treturn sql.data;\n}\n\n\nchar *\nDeparseDropTypeStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->removeType == OBJECT_TYPE);\n\n\tAppendDropTypeStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nchar *\nDeparseAlterTypeStmt(Node *node)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objtype == OBJECT_TYPE);\n\n\tAppendAlterTypeStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendAlterTypeStmt(StringInfo buf, AlterTableStmt *stmt)\n{\n\tconst char *identifier = quote_qualified_identifier(stmt->relation->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstmt->relation->relname);\n\tListCell *cmdCell = NULL;\n\n\tAssert(stmt->objtype == OBJECT_TYPE);\n\n\tappendStringInfo(buf, \"ALTER TYPE %s\", identifier);\n\tforeach(cmdCell, stmt->cmds)\n\t{\n\t\tif (cmdCell != list_head(stmt->cmds))\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t}\n\n\t\tAlterTableCmd *alterTableCmd = castNode(AlterTableCmd, lfirst(cmdCell));\n\t\tAppendAlterTypeCmd(buf, alterTableCmd);\n\t}\n\n\tappendStringInfoString(buf, \";\");\n}\n\n\nstatic void\nAppendAlterTypeCmd(StringInfo buf, AlterTableCmd *alterTableCmd)\n{\n\tswitch (alterTableCmd->subtype)\n\t{\n\t\tcase AT_AddColumn:\n\t\t{\n\t\t\tAppendAlterTypeCmdAddColumn(buf, alterTableCmd);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase AT_DropColumn:\n\t\t{\n\t\t\tAppendAlterTypeCmdDropColumn(buf, alterTableCmd);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase AT_AlterColumnType:\n\t\t{\n\t\t\tAppendAlterTypeCmdAlterColumnType(buf, alterTableCmd);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unsupported subtype for alter table command\"),\n\t\t\t\t\t\t\terrdetail(\"sub command type: %d\", alterTableCmd->subtype)));\n\t\t}\n\t}\n}\n\n\nstatic void\nAppendAlterTypeCmdAddColumn(StringInfo buf, AlterTableCmd *alterTableCmd)\n{\n\tAssert(alterTableCmd->subtype == AT_AddColumn);\n\n\tappendStringInfoString(buf, \" ADD ATTRIBUTE \");\n\tAppendColumnDef(buf, castNode(ColumnDef, alterTableCmd->def));\n}\n\n\nstatic void\nAppendAlterTypeCmdDropColumn(StringInfo buf, AlterTableCmd *alterTableCmd)\n{\n\tAssert(alterTableCmd->subtype == AT_DropColumn);\n\tappendStringInfo(buf, \" DROP ATTRIBUTE %s\", quote_identifier(alterTableCmd->name));\n\n\tif (alterTableCmd->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n}\n\n\nstatic void\nAppendAlterTypeCmdAlterColumnType(StringInfo buf, AlterTableCmd *alterTableCmd)\n{\n\tAssert(alterTableCmd->subtype == AT_AlterColumnType);\n\tappendStringInfo(buf, \" ALTER ATTRIBUTE %s SET DATA TYPE \", quote_identifier(\n\t\t\t\t\t\t alterTableCmd->name));\n\tAppendColumnDef(buf, castNode(ColumnDef, alterTableCmd->def));\n\n\tif (alterTableCmd->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n}\n\n\nstatic void\nAppendAlterEnumStmt(StringInfo buf, AlterEnumStmt *stmt)\n{\n\tappendStringInfo(buf, \"ALTER TYPE %s\", NameListToQuotedString(stmt->typeName));\n\n\tif (AlterEnumIsRename(stmt))\n\t{\n\t\t/* Rename an existing label */\n\t\tappendStringInfo(buf, \" RENAME VALUE %s TO %s;\",\n\t\t\t\t\t\t quote_literal_cstr(stmt->oldVal),\n\t\t\t\t\t\t quote_literal_cstr(stmt->newVal));\n\t}\n\telse if (AlterEnumIsAddValue(stmt))\n\t{\n\t\t/* Add a new label */\n\t\tappendStringInfoString(buf, \" ADD VALUE \");\n\t\tif (stmt->skipIfNewValExists)\n\t\t{\n\t\t\tappendStringInfoString(buf, \"IF NOT EXISTS \");\n\t\t}\n\t\tappendStringInfoString(buf, quote_literal_cstr(stmt->newVal));\n\n\t\tif (stmt->newValNeighbor)\n\t\t{\n\t\t\tappendStringInfo(buf, \" %s %s\",\n\t\t\t\t\t\t\t stmt->newValIsAfter ? \"AFTER\" : \"BEFORE\",\n\t\t\t\t\t\t\t quote_literal_cstr(stmt->newValNeighbor));\n\t\t}\n\n\t\tappendStringInfoString(buf, \";\");\n\t}\n}\n\n\nstatic void\nAppendDropTypeStmt(StringInfo buf, DropStmt *stmt)\n{\n\t/*\n\t * already tested at call site, but for future it might be collapsed in a\n\t * DeparseDropStmt so be safe and check again\n\t */\n\tAssert(stmt->removeType == OBJECT_TYPE);\n\n\tappendStringInfo(buf, \"DROP TYPE \");\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\tAppendTypeNameList(buf, stmt->objects);\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n\tappendStringInfoString(buf, \";\");\n}\n\n\nstatic void\nAppendTypeNameList(StringInfo buf, List *objects)\n{\n\tListCell *objectCell = NULL;\n\tforeach(objectCell, objects)\n\t{\n\t\tTypeName *typeName = castNode(TypeName, lfirst(objectCell));\n\t\tOid typeOid = LookupTypeNameOid(NULL, typeName, false);\n\t\tconst char *identifier = format_type_be_qualified(typeOid);\n\n\t\tif (objectCell != list_head(objects))\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\n\t\tappendStringInfoString(buf, identifier);\n\t}\n}\n\n\n/*\n * AppendCompositeTypeStmt appends the sql string to recreate a CompositeTypeStmt to the\n * provided buffer, ending in a ; for concatination of multiple statements.\n */\nstatic void\nAppendCompositeTypeStmt(StringInfo str, CompositeTypeStmt *stmt)\n{\n\tconst char *identifier = quote_qualified_identifier(stmt->typevar->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstmt->typevar->relname);\n\tappendStringInfo(str, \"CREATE TYPE %s AS (\", identifier);\n\tAppendColumnDefList(str, stmt->coldeflist);\n\tappendStringInfo(str, \");\");\n}\n\n\nstatic void\nAppendCreateEnumStmt(StringInfo str, CreateEnumStmt *stmt)\n{\n\tRangeVar *typevar = makeRangeVarFromNameList(stmt->typeName);\n\n\t/* create the identifier from the fully qualified rangevar */\n\tconst char *identifier = quote_qualified_identifier(typevar->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttypevar->relname);\n\n\tappendStringInfo(str, \"CREATE TYPE %s AS ENUM (\", identifier);\n\tAppendStringList(str, stmt->vals);\n\tappendStringInfo(str, \");\");\n}\n\n\nstatic void\nAppendStringList(StringInfo str, List *strings)\n{\n\tListCell *stringCell = NULL;\n\tforeach(stringCell, strings)\n\t{\n\t\tconst char *string = strVal(lfirst(stringCell));\n\t\tif (stringCell != list_head(strings))\n\t\t{\n\t\t\tappendStringInfoString(str, \", \");\n\t\t}\n\n\t\tstring = quote_literal_cstr(string);\n\t\tappendStringInfoString(str, string);\n\t}\n}\n\n\n/*\n * AppendColumnDefList appends the definition of a list of ColumnDef items to the provided\n * buffer, adding separators as necessary.\n */\nstatic void\nAppendColumnDefList(StringInfo str, List *columnDefs)\n{\n\tListCell *columnDefCell = NULL;\n\tforeach(columnDefCell, columnDefs)\n\t{\n\t\tif (columnDefCell != list_head(columnDefs))\n\t\t{\n\t\t\tappendStringInfoString(str, \", \");\n\t\t}\n\t\tAppendColumnDef(str, castNode(ColumnDef, lfirst(columnDefCell)));\n\t}\n}\n\n\n/*\n * AppendColumnDef appends the definition of one ColumnDef completely qualified to the\n * provided buffer.\n *\n * If the colname is not set that part is ommitted. This is the case in alter column type\n * statements.\n */\nstatic void\nAppendColumnDef(StringInfo str, ColumnDef *columnDef)\n{\n\tint32 typmod = 0;\n\tOid typeOid = InvalidOid;\n\tbits16 formatFlags = FORMAT_TYPE_TYPEMOD_GIVEN | FORMAT_TYPE_FORCE_QUALIFY;\n\n\ttypenameTypeIdAndMod(NULL, columnDef->typeName, &typeOid, &typmod);\n\tOid collationOid = GetColumnDefCollation(NULL, columnDef, typeOid);\n\n\tAssert(!columnDef->is_not_null); /* not null is not supported on composite types */\n\n\tif (columnDef->colname)\n\t{\n\t\tappendStringInfo(str, \"%s \", quote_identifier(columnDef->colname));\n\t}\n\n\tappendStringInfo(str, \"%s\", format_type_extended(typeOid, typmod, formatFlags));\n\n\tif (OidIsValid(collationOid))\n\t{\n\t\tconst char *identifier = FormatCollateBEQualified(collationOid);\n\t\tappendStringInfo(str, \" COLLATE %s\", identifier);\n\t}\n}\n\n\nchar *\nDeparseRenameTypeStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->renameType == OBJECT_TYPE);\n\n\tAppendRenameTypeStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendRenameTypeStmt(StringInfo buf, RenameStmt *stmt)\n{\n\tList *names = (List *) stmt->object;\n\n\tappendStringInfo(buf, \"ALTER TYPE %s RENAME TO %s;\", NameListToQuotedString(names),\n\t\t\t\t\t quote_identifier(stmt->newname));\n}\n\n\nchar *\nDeparseRenameTypeAttributeStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->renameType == OBJECT_ATTRIBUTE);\n\tAssert(stmt->relationType == OBJECT_TYPE);\n\n\tAppendRenameTypeAttributeStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendRenameTypeAttributeStmt(StringInfo buf, RenameStmt *stmt)\n{\n\tappendStringInfo(buf, \"ALTER TYPE %s RENAME ATTRIBUTE %s TO %s\",\n\t\t\t\t\t quote_qualified_identifier(stmt->relation->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\tstmt->relation->relname),\n\t\t\t\t\t quote_identifier(stmt->subname),\n\t\t\t\t\t quote_identifier(stmt->newname));\n\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n\n\tappendStringInfoString(buf, \";\");\n}\n\n\nchar *\nDeparseAlterTypeSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objectType == OBJECT_TYPE);\n\n\tAppendAlterTypeSchemaStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendAlterTypeSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt)\n{\n\tAssert(stmt->objectType == OBJECT_TYPE);\n\n\tList *names = (List *) stmt->object;\n\tappendStringInfo(buf, \"ALTER TYPE %s SET SCHEMA %s;\", NameListToQuotedString(names),\n\t\t\t\t\t quote_identifier(stmt->newschema));\n}\n\n\nchar *\nDeparseAlterTypeOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->objectType == OBJECT_TYPE);\n\n\tAppendAlterTypeOwnerStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendAlterTypeOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)\n{\n\tAssert(stmt->objectType == OBJECT_TYPE);\n\n\tList *names = (List *) stmt->object;\n\tappendStringInfo(buf, \"ALTER TYPE %s OWNER TO %s;\", NameListToQuotedString(names),\n\t\t\t\t\t RoleSpecString(stmt->newowner, true));\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/deparse_view_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_view_stmts.c\n *\n *\t  All routines to deparse view statements.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"commands/defrem.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\nstatic void AppendDropViewStmt(StringInfo buf, DropStmt *stmt);\nstatic void AppendViewNameList(StringInfo buf, List *objects);\nstatic void AppendAlterViewStmt(StringInfo buf, AlterTableStmt *stmt);\nstatic void AppendAlterViewCmd(StringInfo buf, AlterTableCmd *alterTableCmd);\nstatic void AppendAlterViewOwnerStmt(StringInfo buf, AlterTableCmd *alterTableCmd);\nstatic void AppendAlterViewSetOptionsStmt(StringInfo buf, AlterTableCmd *alterTableCmd);\nstatic void AppendAlterViewResetOptionsStmt(StringInfo buf, AlterTableCmd *alterTableCmd);\nstatic void AppendRenameViewStmt(StringInfo buf, RenameStmt *stmt);\nstatic void AppendAlterViewSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt);\n\n/*\n * DeparseDropViewStmt deparses the given DROP VIEW statement.\n */\nchar *\nDeparseDropViewStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAssert(stmt->removeType == OBJECT_VIEW);\n\n\tAppendDropViewStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\n/*\n * AppendDropViewStmt appends the deparsed representation of given drop stmt\n * to the given string info buffer.\n */\nstatic void\nAppendDropViewStmt(StringInfo buf, DropStmt *stmt)\n{\n\t/*\n\t * already tested at call site, but for future it might be collapsed in a\n\t * DeparseDropStmt so be safe and check again\n\t */\n\tAssert(stmt->removeType == OBJECT_VIEW);\n\n\tappendStringInfo(buf, \"DROP VIEW \");\n\tif (stmt->missing_ok)\n\t{\n\t\tappendStringInfoString(buf, \"IF EXISTS \");\n\t}\n\tAppendViewNameList(buf, stmt->objects);\n\tif (stmt->behavior == DROP_CASCADE)\n\t{\n\t\tappendStringInfoString(buf, \" CASCADE\");\n\t}\n\tappendStringInfoString(buf, \";\");\n}\n\n\n/*\n * AppendViewNameList appends the qualified view names by constructing them from the given\n * objects list to the given string info buffer. Note that, objects must hold schema\n * qualified view names as its' members.\n */\nstatic void\nAppendViewNameList(StringInfo buf, List *viewNamesList)\n{\n\tbool isFirstView = true;\n\tList *qualifiedViewName = NULL;\n\tforeach_declared_ptr(qualifiedViewName, viewNamesList)\n\t{\n\t\tchar *quotedQualifiedVieName = NameListToQuotedString(qualifiedViewName);\n\t\tif (!isFirstView)\n\t\t{\n\t\t\tappendStringInfo(buf, \", \");\n\t\t}\n\n\t\tappendStringInfoString(buf, quotedQualifiedVieName);\n\t\tisFirstView = false;\n\t}\n}\n\n\n/*\n * DeparseAlterViewStmt deparses the given ALTER VIEW statement.\n */\nchar *\nDeparseAlterViewStmt(Node *node)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendAlterViewStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendAlterViewStmt(StringInfo buf, AlterTableStmt *stmt)\n{\n\tconst char *identifier = quote_qualified_identifier(stmt->relation->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstmt->relation->relname);\n\n\tappendStringInfo(buf, \"ALTER VIEW %s \", identifier);\n\n\tAlterTableCmd *alterTableCmd = castNode(AlterTableCmd, lfirst(list_head(stmt->cmds)));\n\tAppendAlterViewCmd(buf, alterTableCmd);\n\n\tappendStringInfoString(buf, \";\");\n}\n\n\nstatic void\nAppendAlterViewCmd(StringInfo buf, AlterTableCmd *alterTableCmd)\n{\n\tswitch (alterTableCmd->subtype)\n\t{\n\t\tcase AT_ChangeOwner:\n\t\t{\n\t\t\tAppendAlterViewOwnerStmt(buf, alterTableCmd);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase AT_SetRelOptions:\n\t\t{\n\t\t\tAppendAlterViewSetOptionsStmt(buf, alterTableCmd);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase AT_ResetRelOptions:\n\t\t{\n\t\t\tAppendAlterViewResetOptionsStmt(buf, alterTableCmd);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase AT_ColumnDefault:\n\t\t{\n\t\t\telog(ERROR, \"Citus doesn't support setting or resetting default values for a \"\n\t\t\t\t\t\t\"column of view\");\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\t/*\n\t\t\t * ALTER VIEW command only supports for the cases checked above but an\n\t\t\t * ALTER TABLE commands targeting views may have different cases. To let\n\t\t\t * PG throw the right error locally, we don't throw any error here\n\t\t\t */\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\nstatic void\nAppendAlterViewOwnerStmt(StringInfo buf, AlterTableCmd *alterTableCmd)\n{\n\tappendStringInfo(buf, \"OWNER TO %s\", RoleSpecString(alterTableCmd->newowner, true));\n}\n\n\nstatic void\nAppendAlterViewSetOptionsStmt(StringInfo buf, AlterTableCmd *alterTableCmd)\n{\n\tListCell *lc = NULL;\n\tbool initialOption = true;\n\tforeach(lc, (List *) alterTableCmd->def)\n\t{\n\t\tDefElem *def = (DefElem *) lfirst(lc);\n\n\t\tif (initialOption)\n\t\t{\n\t\t\tappendStringInfo(buf, \"SET (\");\n\t\t\tinitialOption = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(buf, \",\");\n\t\t}\n\n\t\tappendStringInfo(buf, \"%s\", def->defname);\n\t\tif (def->arg != NULL)\n\t\t{\n\t\t\tappendStringInfo(buf, \"=\");\n\t\t\tappendStringInfo(buf, \"%s\", defGetString(def));\n\t\t}\n\t}\n\n\tappendStringInfo(buf, \")\");\n}\n\n\nstatic void\nAppendAlterViewResetOptionsStmt(StringInfo buf, AlterTableCmd *alterTableCmd)\n{\n\tListCell *lc = NULL;\n\tbool initialOption = true;\n\tforeach(lc, (List *) alterTableCmd->def)\n\t{\n\t\tDefElem *def = (DefElem *) lfirst(lc);\n\n\t\tif (initialOption)\n\t\t{\n\t\t\tappendStringInfo(buf, \"RESET (\");\n\t\t\tinitialOption = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(buf, \",\");\n\t\t}\n\n\t\tappendStringInfo(buf, \"%s\", def->defname);\n\t}\n\n\tappendStringInfo(buf, \")\");\n}\n\n\nchar *\nDeparseRenameViewStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendRenameViewStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendRenameViewStmt(StringInfo buf, RenameStmt *stmt)\n{\n\tswitch (stmt->renameType)\n\t{\n\t\tcase OBJECT_COLUMN:\n\t\t{\n\t\t\tconst char *identifier =\n\t\t\t\tquote_qualified_identifier(stmt->relation->schemaname,\n\t\t\t\t\t\t\t\t\t\t   stmt->relation->relname);\n\t\t\tappendStringInfo(buf, \"ALTER VIEW %s RENAME COLUMN %s TO %s;\", identifier,\n\t\t\t\t\t\t\t quote_identifier(stmt->subname), quote_identifier(\n\t\t\t\t\t\t\t\t stmt->newname));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_VIEW:\n\t\t{\n\t\t\tconst char *identifier =\n\t\t\t\tquote_qualified_identifier(stmt->relation->schemaname,\n\t\t\t\t\t\t\t\t\t\t   stmt->relation->relname);\n\t\t\tappendStringInfo(buf, \"ALTER VIEW %s RENAME TO %s;\", identifier,\n\t\t\t\t\t\t\t quote_identifier(stmt->newname));\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unsupported subtype for alter view rename command\"),\n\t\t\t\t\t\t\terrdetail(\"sub command type: %d\", stmt->renameType)));\n\t\t}\n\t}\n}\n\n\nchar *\nDeparseAlterViewSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tStringInfoData str = { 0 };\n\tinitStringInfo(&str);\n\n\tAppendAlterViewSchemaStmt(&str, stmt);\n\n\treturn str.data;\n}\n\n\nstatic void\nAppendAlterViewSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt)\n{\n\tconst char *identifier = quote_qualified_identifier(stmt->relation->schemaname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tstmt->relation->relname);\n\tappendStringInfo(buf, \"ALTER VIEW %s SET SCHEMA %s;\", identifier, quote_identifier(\n\t\t\t\t\t\t stmt->newschema));\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/format_collate.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * format_collate.c\n *    Display collate names \"nicely\".\n *\n *    This file is modeled after postgres' utils/adt/format_*.c files\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/htup_details.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_collation.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/deparser.h\"\n\n\n/*\n * This version returns a name that is always qualified.\n */\nchar *\nFormatCollateBEQualified(Oid collate_oid)\n{\n\treturn FormatCollateExtended(collate_oid, FORMAT_COLLATE_FORCE_QUALIFY);\n}\n\n\n/*\n * FormatCollateExtended - inspired by format_type_extended\n *\t\tGenerate a possibly-qualified collate name.\n *\n * The default behavior is to only qualify if the type is not in the search\n * path, and to raise an error if a non-existent collate_oid is given.\n *\n * The following bits in 'flags' modify the behavior:\n * - FORMAT_COLLATE_FORCE_QUALIFY\n *\t\t\talways schema-qualify collate names, regardless of search_path\n *\n * Returns a palloc'd string.\n */\nchar *\nFormatCollateExtended(Oid collid, bits16 flags)\n{\n\tchar *nspname = NULL;\n\n\tif (collid == InvalidOid && (flags & FORMAT_COLLATE_ALLOW_INVALID) != 0)\n\t{\n\t\treturn pstrdup(\"-\");\n\t}\n\n\tHeapTuple tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\tif ((flags & FORMAT_COLLATE_ALLOW_INVALID) != 0)\n\t\t{\n\t\t\treturn pstrdup(\"???\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\telog(ERROR, \"cache lookup failed for collate %u\", collid);\n\t\t}\n\t}\n\tForm_pg_collation collform = (Form_pg_collation) GETSTRUCT(tuple);\n\n\tif ((flags & FORMAT_COLLATE_FORCE_QUALIFY) == 0 && CollationIsVisible(collid))\n\t{\n\t\tnspname = NULL;\n\t}\n\telse\n\t{\n\t\tnspname = get_namespace_name_or_temp(collform->collnamespace);\n\t}\n\n\tchar *typname = NameStr(collform->collname);\n\n\tchar *buf = quote_qualified_identifier(nspname, typname);\n\n\tReleaseSysCache(tuple);\n\n\treturn buf;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/objectaddress.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * objectaddress.c\n *    Parstrees almost always target a object that postgres can address by\n *    an ObjectAddress. Here we have a walker for parsetrees to find the\n *    address of the object targeted.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/objectaddress.h\"\n#include \"catalog/pg_extension_d.h\"\n#include \"commands/extension.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n\n\n/*\n * GetObjectAddressListFromParseTree returns the list of ObjectAddress of the main target of the parse\n * tree.\n */\nList *\nGetObjectAddressListFromParseTree(Node *parseTree, bool missing_ok, bool isPostprocess)\n{\n\tconst DistributeObjectOps *ops = GetDistributeObjectOps(parseTree);\n\n\tif (!ops->address)\n\t{\n\t\tereport(ERROR, (errmsg(\"unsupported statement to get object address for\")));\n\t}\n\n\treturn ops->address(parseTree, missing_ok, isPostprocess);\n}\n\n\nList *\nRenameAttributeStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_ATTRIBUTE);\n\n\tswitch (stmt->relationType)\n\t{\n\t\tcase OBJECT_TYPE:\n\t\t{\n\t\t\treturn RenameTypeAttributeStmtObjectAddress(node, missing_ok);\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unsupported alter rename attribute statement to get \"\n\t\t\t\t\t\t\t\t   \"object address for\")));\n\t\t}\n\t}\n}\n\n\n/*\n * CreateExtensionStmtObjectAddress finds the ObjectAddress for the extension described\n * by the CreateExtensionStmt. If missing_ok is false, then this function throws an\n * error if the extension does not exist.\n *\n * Never returns NULL, but the objid in the address could be invalid if missing_ok was set\n * to true.\n */\nList *\nCreateExtensionStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)\n{\n\tCreateExtensionStmt *stmt = castNode(CreateExtensionStmt, node);\n\tObjectAddress *address = palloc0(sizeof(ObjectAddress));\n\n\tconst char *extensionName = stmt->extname;\n\n\tOid extensionoid = get_extension_oid(extensionName, missing_ok);\n\n\t/* if we couldn't find the extension, error if missing_ok is false */\n\tif (!missing_ok && extensionoid == InvalidOid)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),\n\t\t\t\t\t\terrmsg(\"extension \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t   extensionName)));\n\t}\n\n\tObjectAddressSet(*address, ExtensionRelationId, extensionoid);\n\n\treturn list_make1(address);\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify.c\n *\t  The deparser relies on fully qualified names on all statements to\n *\t  work transparently on a remote worker node. Here we have helpers to\n *\t  fully qualify parsetrees.\n *\n *\t  Fully qualified parsetrees contain names for all identifiers that\n *\t  are search_path agnostic. Meaning we need to include the schema name\n *\t  for each and every identifier in the parsetree.\n *\n *\t  This file contains mostly the distpatching functions to specialized\n *\t  functions for each class of objects. eg qualify_type_stmt.c contains\n *\t  all functions related to fully qualifying parsetrees that interact\n *\t  with types.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n\n\n/*\n * QualifyTreeNode transforms the statement in place and makes all (supported) statements\n * fully qualified. Fully qualified statements allow for application on a remote postgres\n * server irregardless of their search_path.\n */\nvoid\nQualifyTreeNode(Node *stmt)\n{\n\tconst DistributeObjectOps *ops = GetDistributeObjectOps(stmt);\n\n\tif (ops && ops->qualify)\n\t{\n\t\tops->qualify(stmt);\n\t}\n}\n\n\n/*\n * QualifyRenameAttributeStmt transforms a RENAME ATTRIBUTE statement in place and makes all (supported)\n * statements fully qualified.\n */\nvoid\nQualifyRenameAttributeStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_ATTRIBUTE);\n\n\tswitch (stmt->relationType)\n\t{\n\t\tcase OBJECT_TYPE:\n\t\t{\n\t\t\tQualifyRenameTypeAttributeStmt(node);\n\t\t\treturn;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_aggregate_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_aggregate_stmts.c\n *\t  Functions specialized in fully qualifying all aggregate statements.\n *    These functions are dispatched from qualify.c\n *\n *\t  Fully qualifying aggregate statements consists of adding the schema name\n *\t  to the subject of the types as well as any other branch of the parsetree.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"nodes/makefuncs.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/deparser.h\"\n\nvoid\nQualifyDefineAggregateStmt(Node *node)\n{\n\tDefineStmt *stmt = castNode(DefineStmt, node);\n\n\tif (list_length(stmt->defnames) == 1)\n\t{\n\t\tchar *objname = NULL;\n\t\tOid creationSchema = QualifiedNameGetCreationNamespace(stmt->defnames, &objname);\n\t\tstmt->defnames = list_make2(makeString(get_namespace_name(creationSchema)),\n\t\t\t\t\t\t\t\t\tlinitial(stmt->defnames));\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_collation_stmt.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_collation_stmt.c\n *\t  Functions specialized in fully qualifying all collation statements. These\n *\t  functions are dispatched from qualify.c\n *\n *\t  Goal would be that the deparser functions for these statements can\n *\t  serialize the statement without any external lookups.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/htup_details.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_collation.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\nstatic Node * QualifyCollationName(List *func);\n\n\n/*\n * QualifyRenameCollationStmt transforms a\n * ALTER COLLATION .. RENAME TO ..\n * statement in place and makes the collation name fully qualified.\n */\nvoid\nQualifyRenameCollationStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_COLLATION);\n\n\tstmt->object = QualifyCollationName(castNode(List, stmt->object));\n}\n\n\n/*\n * QualifyAlterCollationSchemaStmt transforms a\n * ALTER COLLATION .. SET SCHEMA ..\n * statement in place and makes the collation name fully qualified.\n */\nvoid\nQualifyAlterCollationSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_COLLATION);\n\n\tstmt->object = QualifyCollationName(castNode(List, stmt->object));\n}\n\n\n/*\n * QualifyAlterCollationOwnerStmt transforms a\n * ALTER COLLATION .. OWNER TO ..\n * statement in place and makes the collation name fully qualified.\n */\nvoid\nQualifyAlterCollationOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_COLLATION);\n\n\tstmt->object = QualifyCollationName(castNode(List, stmt->object));\n}\n\n\n/*\n * QualifyDropCollationStmt transforms a\n * DROP COLLATION ..\n * statement in place and makes the collation name fully qualified.\n */\nvoid\nQualifyDropCollationStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tList *names = NIL;\n\tList *name = NIL;\n\n\tforeach_declared_ptr(name, stmt->objects)\n\t{\n\t\tnames = lappend(names, QualifyCollationName(name));\n\t}\n\n\tstmt->objects = names;\n}\n\n\n/*\n * QualifyCollation transforms a collation in place and makes its name fully qualified.\n */\nNode *\nQualifyCollationName(List *name)\n{\n\tchar *collationName = NULL;\n\tchar *schemaName = NULL;\n\n\t/* check if the collation name is already qualified */\n\tDeconstructQualifiedName(name, &schemaName, &collationName);\n\n\t/* do a lookup for the schema name if the statement does not include one */\n\tif (schemaName == NULL)\n\t{\n\t\tOid collid = get_collation_oid(name, true);\n\n\t\tif (collid == InvalidOid)\n\t\t{\n\t\t\treturn (Node *) name;\n\t\t}\n\n\t\tHeapTuple colltup = SearchSysCache1(COLLOID, collid);\n\n\t\tif (!HeapTupleIsValid(colltup))\n\t\t{\n\t\t\treturn (Node *) name;\n\t\t}\n\t\tForm_pg_collation collationForm =\n\t\t\t(Form_pg_collation) GETSTRUCT(colltup);\n\n\t\tschemaName = get_namespace_name(collationForm->collnamespace);\n\t\tname = list_make2(makeString(schemaName), makeString(collationName));\n\t\tReleaseSysCache(colltup);\n\t}\n\n\treturn (Node *) name;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_domain.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_domain.c\n *    Functions to fully qualify, make the statements independent of\n *    search_path settings, for all domain related statements. This\n *    mostly consists of adding the schema name to all the domain\n *    names referencing domains.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"parser/parse_type.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\n\nstatic void QualifyTypeName(TypeName *typeName, bool missing_ok);\nstatic void QualifyCollate(CollateClause *collClause, bool missing_ok);\n\n\n/*\n * QualifyCreateDomainStmt modifies the CreateDomainStmt passed to become search_path\n * independent.\n */\nvoid\nQualifyCreateDomainStmt(Node *node)\n{\n\tCreateDomainStmt *stmt = castNode(CreateDomainStmt, node);\n\n\tchar *schemaName = NULL;\n\tchar *domainName = NULL;\n\n\t/* fully qualify domain name */\n\tDeconstructQualifiedName(stmt->domainname, &schemaName, &domainName);\n\tif (!schemaName)\n\t{\n\t\tRangeVar *var = makeRangeVarFromNameList(stmt->domainname);\n\t\tOid creationSchema = RangeVarGetCreationNamespace(var);\n\t\tschemaName = get_namespace_name(creationSchema);\n\n\t\tstmt->domainname = list_make2(makeString(schemaName), makeString(domainName));\n\t}\n\n\t/* referenced types should be fully qualified */\n\tQualifyTypeName(stmt->typeName, false);\n\tQualifyCollate(stmt->collClause, false);\n}\n\n\n/*\n * QualifyDropDomainStmt modifies the DropStmt for DOMAIN's to be search_path independent.\n */\nvoid\nQualifyDropDomainStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\n\tTypeName *domainName = NULL;\n\tforeach_declared_ptr(domainName, stmt->objects)\n\t{\n\t\tQualifyTypeName(domainName, stmt->missing_ok);\n\t}\n}\n\n\n/*\n * QualifyAlterDomainStmt modifies the AlterDomainStmt to be search_path independent.\n */\nvoid\nQualifyAlterDomainStmt(Node *node)\n{\n\tAlterDomainStmt *stmt = castNode(AlterDomainStmt, node);\n\n\tif (list_length(stmt->typeName) == 1)\n\t{\n\t\tTypeName *typeName = makeTypeNameFromNameList(stmt->typeName);\n\t\tQualifyTypeName(typeName, false);\n\t\tstmt->typeName = typeName->names;\n\t}\n}\n\n\n/*\n * QualifyDomainRenameConstraintStmt modifies the RenameStmt for domain constraints to be\n * search_path independent.\n */\nvoid\nQualifyDomainRenameConstraintStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_DOMCONSTRAINT);\n\n\tList *domainName = castNode(List, stmt->object);\n\tif (list_length(domainName) == 1)\n\t{\n\t\tTypeName *typeName = makeTypeNameFromNameList(domainName);\n\t\tQualifyTypeName(typeName, false);\n\t\tstmt->object = (Node *) typeName->names;\n\t}\n}\n\n\n/*\n * QualifyAlterDomainOwnerStmt modifies the AlterOwnerStmt for DOMAIN's to be search_oath\n * independent.\n */\nvoid\nQualifyAlterDomainOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_DOMAIN);\n\n\tList *domainName = castNode(List, stmt->object);\n\tif (list_length(domainName) == 1)\n\t{\n\t\tTypeName *typeName = makeTypeNameFromNameList(domainName);\n\t\tQualifyTypeName(typeName, false);\n\t\tstmt->object = (Node *) typeName->names;\n\t}\n}\n\n\n/*\n * QualifyRenameDomainStmt modifies the RenameStmt for the Domain to be search_path\n * independent.\n */\nvoid\nQualifyRenameDomainStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_DOMAIN);\n\n\tList *domainName = castNode(List, stmt->object);\n\tif (list_length(domainName) == 1)\n\t{\n\t\tTypeName *typeName = makeTypeNameFromNameList(domainName);\n\t\tQualifyTypeName(typeName, false);\n\t\tstmt->object = (Node *) typeName->names;\n\t}\n}\n\n\n/*\n * QualifyAlterDomainSchemaStmt modifies the AlterObjectSchemaStmt to be search_path\n * independent.\n */\nvoid\nQualifyAlterDomainSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_DOMAIN);\n\n\tList *domainName = castNode(List, stmt->object);\n\tif (list_length(domainName) == 1)\n\t{\n\t\tTypeName *typeName = makeTypeNameFromNameList(domainName);\n\t\tQualifyTypeName(typeName, false);\n\t\tstmt->object = (Node *) typeName->names;\n\t}\n}\n\n\n/*\n * QualifyTypeName qualifies a TypeName object in place. When missing_ok is false it might\n * throw an error if the type can't be found based on its name. When an oid is provided\n * missing_ok is ignored and treated as false. Meaning, even if missing_ok is true the\n * function might raise an error for non-existing types if the oid can't be found.\n */\nstatic void\nQualifyTypeName(TypeName *typeName, bool missing_ok)\n{\n\tif (OidIsValid(typeName->typeOid))\n\t{\n\t\t/*\n\t\t * When the typeName is provided as oid, fill in the names.\n\t\t * missing_ok is ignored for oid's\n\t\t */\n\t\tType typeTup = typeidType(typeName->typeOid);\n\n\t\tchar *name = typeTypeName(typeTup);\n\n\t\tOid namespaceOid = TypeOidGetNamespaceOid(typeName->typeOid);\n\t\tchar *schemaName = get_namespace_name(namespaceOid);\n\n\t\ttypeName->names = list_make2(makeString(schemaName), makeString(name));\n\n\t\tReleaseSysCache(typeTup);\n\t}\n\telse\n\t{\n\t\tchar *name = NULL;\n\t\tchar *schemaName = NULL;\n\t\tDeconstructQualifiedName(typeName->names, &schemaName, &name);\n\t\tif (!schemaName)\n\t\t{\n\t\t\tOid typeOid = LookupTypeNameOid(NULL, typeName, missing_ok);\n\t\t\tif (OidIsValid(typeOid))\n\t\t\t{\n\t\t\t\tOid namespaceOid = TypeOidGetNamespaceOid(typeOid);\n\t\t\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\t\t\ttypeName->names = list_make2(makeString(schemaName), makeString(name));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * QualifyCollate qualifies any given CollateClause by adding any missing schema name to\n * the collation being identified.\n *\n * If collClause is a NULL pointer this function is a no-nop.\n */\nstatic void\nQualifyCollate(CollateClause *collClause, bool missing_ok)\n{\n\tif (collClause == NULL)\n\t{\n\t\t/* no collate clause, nothing to qualify*/\n\t\treturn;\n\t}\n\n\tif (list_length(collClause->collname) != 1)\n\t{\n\t\t/* already qualified */\n\t\treturn;\n\t}\n\n\tOid collOid = get_collation_oid(collClause->collname, missing_ok);\n\tObjectAddress collationAddress = { 0 };\n\tObjectAddressSet(collationAddress, CollationRelationId, collOid);\n\n\tList *objName = NIL;\n\tList *objArgs = NIL;\n\n\tgetObjectIdentityParts(&collationAddress, &objName, &objArgs, false);\n\n\tcollClause->collname = NIL;\n\tchar *name = NULL;\n\tforeach_declared_ptr(name, objName)\n\t{\n\t\tcollClause->collname = lappend(collClause->collname, makeString(name));\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_function_stmt.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_function_stmt.c\n *\t  Functions specialized in fully qualifying all function statements. These\n *\t  functions are dispatched from qualify.c\n *\n *\t  Fully qualifying function statements consists of adding the schema name\n *\t  to the subject of the function and types as well as any other branch of\n *    the parsetree.\n *\n *\t  Goal would be that the deparser functions for these statements can\n *\t  serialize the statement without any external lookups.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/htup_details.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_proc.h\"\n#include \"parser/parse_func.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/deparser.h\"\n#include \"distributed/version_compat.h\"\n\n/* forward declaration for qualify functions */\nstatic void QualifyFunction(ObjectWithArgs *func, ObjectType type);\nstatic void QualifyFunctionSchemaName(ObjectWithArgs *func, ObjectType type);\n\n\n/* AssertObjectTypeIsFunctionType asserts we aren't receiving something we shouldn't */\nvoid\nAssertObjectTypeIsFunctional(ObjectType type)\n{\n\tAssert(type == OBJECT_AGGREGATE ||\n\t\t   type == OBJECT_FUNCTION ||\n\t\t   type == OBJECT_PROCEDURE ||\n\t\t   type == OBJECT_ROUTINE);\n}\n\n\n/*\n * QualifyAlterFunctionStmt transforms a\n * ALTER {AGGREGATE|FUNCTION|PROCEDURE} ..\n * statement in place and makes all (supported) statements fully qualified.\n *\n * Note that not all queries of this form are valid AlterFunctionStmt\n * (e.g. ALTER FUNCTION .. RENAME .. queries are RenameStmt )\n */\nvoid\nQualifyAlterFunctionStmt(Node *node)\n{\n\tAlterFunctionStmt *stmt = castNode(AlterFunctionStmt, node);\n\tAssertObjectTypeIsFunctional(stmt->objtype);\n\n\tQualifyFunction(stmt->func, stmt->objtype);\n}\n\n\n/*\n * QualifyRenameFunctionStmt transforms a\n * ALTER {AGGREGATE|FUNCTION|PROCEDURE} .. RENAME TO ..\n * statement in place and makes the function name fully qualified.\n */\nvoid\nQualifyRenameFunctionStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssertObjectTypeIsFunctional(stmt->renameType);\n\n\tQualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->renameType);\n}\n\n\n/*\n * QualifyAlterFunctionSchemaStmt transforms a\n * ALTER {AGGREGATE|FUNCTION|PROCEDURE} .. SET SCHEMA ..\n * statement in place and makes the function name fully qualified.\n */\nvoid\nQualifyAlterFunctionSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssertObjectTypeIsFunctional(stmt->objectType);\n\n\tQualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->objectType);\n}\n\n\n/*\n * QualifyAlterFunctionOwnerStmt transforms a\n * ALTER {AGGREGATE|FUNCTION|PROCEDURE} .. OWNER TO ..\n * statement in place and makes the function name fully qualified.\n */\nvoid\nQualifyAlterFunctionOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssertObjectTypeIsFunctional(stmt->objectType);\n\n\tQualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->objectType);\n}\n\n\n/*\n * QualifyAlterFunctionDependsStmt transforms a\n * ALTER {FUNCTION|PROCEDURE} .. DEPENDS ON EXTENSION ..\n * statement in place and makes the function name fully qualified.\n */\nvoid\nQualifyAlterFunctionDependsStmt(Node *node)\n{\n\tAlterObjectDependsStmt *stmt = castNode(AlterObjectDependsStmt, node);\n\tAssertObjectTypeIsFunctional(stmt->objectType);\n\n\tQualifyFunction(castNode(ObjectWithArgs, stmt->object), stmt->objectType);\n}\n\n\n/*\n * QualifyFunction transforms a function in place and makes its name fully qualified.\n */\nvoid\nQualifyFunction(ObjectWithArgs *func, ObjectType type)\n{\n\tchar *functionName = NULL;\n\tchar *schemaName = NULL;\n\n\t/* check if the function name is already qualified */\n\tDeconstructQualifiedName(func->objname, &schemaName, &functionName);\n\n\t/* do a lookup for the schema name if the statement does not include one */\n\tif (schemaName == NULL)\n\t{\n\t\tQualifyFunctionSchemaName(func, type);\n\t}\n}\n\n\n/*\n * QualifyFunction transforms a function in place using a catalog lookup for its schema name to make it fully qualified.\n */\nvoid\nQualifyFunctionSchemaName(ObjectWithArgs *func, ObjectType type)\n{\n\tchar *schemaName = NULL;\n\tchar *functionName = NULL;\n\n\tOid funcid = LookupFuncWithArgs(type, func, true);\n\tHeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));\n\n\t/*\n\t * We can not qualify the function if the catalogs do not have any records.\n\t *\n\t * e.g. DROP FUNC IF EXISTS non_existent_func() do not result in a valid heap tuple\n\t */\n\tif (HeapTupleIsValid(proctup))\n\t{\n\t\tForm_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);\n\t\tschemaName = get_namespace_name(procform->pronamespace);\n\t\tfunctionName = NameStr(procform->proname);\n\t\tfunctionName = pstrdup(functionName); /* we release the tuple before used */\n\n\t\tReleaseSysCache(proctup);\n\n\t\t/* update the function using the schema name */\n\t\tfunc->objname = list_make2(makeString(schemaName), makeString(functionName));\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_publication_stmt.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_publication_stmt.c\n *\t  Functions specialized in fully qualifying all publication statements. These\n *\t  functions are dispatched from qualify.c\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"nodes/nodes.h\"\n#include \"utils/guc.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\nstatic void QualifyPublicationObjects(List *publicationObjects);\nstatic void QualifyPublicationRangeVar(RangeVar *publication);\n\n\n/*\n * QualifyCreatePublicationStmt quailifies the publication names of the\n * CREATE PUBLICATION statement.\n */\nvoid\nQualifyCreatePublicationStmt(Node *node)\n{\n\tCreatePublicationStmt *stmt = castNode(CreatePublicationStmt, node);\n\n\tQualifyPublicationObjects(stmt->pubobjects);\n}\n\n\n/*\n * QualifyPublicationObjects ensures all table names in a list of\n * publication objects are fully qualified.\n */\nstatic void\nQualifyPublicationObjects(List *publicationObjects)\n{\n\tPublicationObjSpec *publicationObject = NULL;\n\n\tforeach_declared_ptr(publicationObject, publicationObjects)\n\t{\n\t\tif (publicationObject->pubobjtype == PUBLICATIONOBJ_TABLE)\n\t\t{\n\t\t\t/* FOR TABLE ... */\n\t\t\tPublicationTable *publicationTable = publicationObject->pubtable;\n\n\t\t\tQualifyPublicationRangeVar(publicationTable->relation);\n\t\t}\n\t}\n}\n\n\n/*\n * QualifyPublicationObjects ensures all table names in a list of\n * publication objects are fully qualified.\n */\nvoid\nQualifyAlterPublicationStmt(Node *node)\n{\n\tAlterPublicationStmt *stmt = castNode(AlterPublicationStmt, node);\n\n\tQualifyPublicationObjects(stmt->pubobjects);\n}\n\n\n/*\n * QualifyPublicationRangeVar qualifies the given publication RangeVar if it is not qualified.\n */\nstatic void\nQualifyPublicationRangeVar(RangeVar *publication)\n{\n\tif (publication->schemaname == NULL)\n\t{\n\t\tOid publicationOid = RelnameGetRelid(publication->relname);\n\t\tOid schemaOid = get_rel_namespace(publicationOid);\n\t\tpublication->schemaname = get_namespace_name(schemaOid);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_role_stmt.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_role_stmt.c\n *\t  Functions specialized in fully qualifying all role statements. These\n *\t  functions are dispatched from qualify.c\n *\n *\t  Fully qualifying role statements consists of adding the current database\n *    name, session user, current use, and current configuration values.\n *\n *\t  Goal would be that the deparser functions for these statements can\n *\t  serialize the statement without any external lookups.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"nodes/nodes.h\"\n#include \"utils/guc.h\"\n\n#include \"distributed/deparser.h\"\n\n\nstatic void QualifyVarSetCurrent(VariableSetStmt *setStmt);\n\n/*\n * QualifyAlterRoleSetStmt transforms a\n * ALTER ROLE .. SET ..\n * statement in place and makes the settings fully qualified.\n */\nvoid\nQualifyAlterRoleSetStmt(Node *node)\n{\n\tAlterRoleSetStmt *stmt = castNode(AlterRoleSetStmt, node);\n\tVariableSetStmt *setStmt = stmt->setstmt;\n\n\tif (setStmt->kind == VAR_SET_CURRENT)\n\t{\n\t\tQualifyVarSetCurrent(setStmt);\n\t}\n}\n\n\n/*\n * QualifyVarSetCurrent transforms a\n * FROM CURRENT\n * into a\n * SET config_name TO 'config_value'\n * VariableSetStmt in place and hence makes it fully qualified.\n */\nstatic void\nQualifyVarSetCurrent(VariableSetStmt *setStmt)\n{\n\tchar *configurationName = setStmt->name;\n\tchar *configValue = GetConfigOptionByName(configurationName, NULL, false);\n\n\tsetStmt->kind = VAR_SET_VALUE;\n\tsetStmt->args = list_make1(MakeSetStatementArguments(configurationName, configValue));\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_sequence_stmt.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_sequence_stmt.c\n *\t  Functions specialized in fully qualifying all sequence statements. These\n *\t  functions are dispatched from qualify.c\n *\n *\t  Fully qualifying sequence statements consists of adding the schema name\n *\t  to the subject of the sequence.\n *\n *\t  Goal would be that the deparser functions for these statements can\n *\t  serialize the statement without any external lookups.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"parser/parse_func.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/version_compat.h\"\n\n\n/*\n * QualifyAlterSequenceOwnerStmt transforms a\n * ALTER SEQUENCE .. OWNER TO ..\n * statement in place and makes the sequence name fully qualified.\n */\nvoid\nQualifyAlterSequenceOwnerStmt(Node *node)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tRangeVar *seq = stmt->relation;\n\n\tif (seq->schemaname == NULL)\n\t{\n\t\tOid seqOid = RangeVarGetRelid(seq, NoLock, stmt->missing_ok);\n\n\t\tif (OidIsValid(seqOid))\n\t\t{\n\t\t\tOid schemaOid = get_rel_namespace(seqOid);\n\t\t\tseq->schemaname = get_namespace_name(schemaOid);\n\t\t}\n\t}\n}\n\n\n/*\n * QualifyAlterSequencePersistenceStmt transforms a\n * ALTER SEQUENCE .. SET LOGGED/UNLOGGED\n * statement in place and makes the sequence name fully qualified.\n */\nvoid\nQualifyAlterSequencePersistenceStmt(Node *node)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\tRangeVar *seq = stmt->relation;\n\n\tif (seq->schemaname == NULL)\n\t{\n\t\tOid seqOid = RangeVarGetRelid(seq, NoLock, stmt->missing_ok);\n\n\t\tif (OidIsValid(seqOid))\n\t\t{\n\t\t\tOid schemaOid = get_rel_namespace(seqOid);\n\t\t\tseq->schemaname = get_namespace_name(schemaOid);\n\t\t}\n\t}\n}\n\n\n/*\n * QualifyAlterSequenceSchemaStmt transforms a\n * ALTER SEQUENCE .. SET SCHEMA ..\n * statement in place and makes the sequence name fully qualified.\n */\nvoid\nQualifyAlterSequenceSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_SEQUENCE);\n\n\tRangeVar *seq = stmt->relation;\n\n\tif (seq->schemaname == NULL)\n\t{\n\t\tOid seqOid = RangeVarGetRelid(seq, NoLock, stmt->missing_ok);\n\n\t\tif (OidIsValid(seqOid))\n\t\t{\n\t\t\tOid schemaOid = get_rel_namespace(seqOid);\n\t\t\tseq->schemaname = get_namespace_name(schemaOid);\n\t\t}\n\t}\n}\n\n\n/*\n * QualifyRenameSequenceStmt transforms a\n * ALTER SEQUENCE .. RENAME TO ..\n * statement in place and makes the sequence name fully qualified.\n */\nvoid\nQualifyRenameSequenceStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_SEQUENCE);\n\n\tRangeVar *seq = stmt->relation;\n\n\tif (seq->schemaname == NULL)\n\t{\n\t\tOid seqOid = RangeVarGetRelid(seq, NoLock, stmt->missing_ok);\n\n\t\tif (OidIsValid(seqOid))\n\t\t{\n\t\t\tOid schemaOid = get_rel_namespace(seqOid);\n\t\t\tseq->schemaname = get_namespace_name(schemaOid);\n\t\t}\n\t}\n}\n\n\n/*\n * QualifyDropSequenceStmt transforms a DROP SEQUENCE\n * statement in place and makes the sequence name fully qualified.\n */\nvoid\nQualifyDropSequenceStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\n\tAssert(stmt->removeType == OBJECT_SEQUENCE);\n\n\tList *objectNameListWithSchema = NIL;\n\tList *objectNameList = NULL;\n\tforeach_declared_ptr(objectNameList, stmt->objects)\n\t{\n\t\tRangeVar *seq = makeRangeVarFromNameList(objectNameList);\n\n\t\tif (seq->schemaname == NULL)\n\t\t{\n\t\t\tOid seqOid = RangeVarGetRelid(seq, NoLock, stmt->missing_ok);\n\n\t\t\tif (OidIsValid(seqOid))\n\t\t\t{\n\t\t\t\tOid schemaOid = get_rel_namespace(seqOid);\n\t\t\t\tseq->schemaname = get_namespace_name(schemaOid);\n\t\t\t}\n\t\t}\n\n\t\tobjectNameListWithSchema = lappend(objectNameListWithSchema,\n\t\t\t\t\t\t\t\t\t\t   MakeNameListFromRangeVar(seq));\n\t}\n\n\tstmt->objects = objectNameListWithSchema;\n}\n\n\n/*\n * QualifyGrantOnSequenceStmt transforms a\n * GRANT ON SEQUENCE ...\n * statement in place and makes the sequence names fully qualified.\n */\nvoid\nQualifyGrantOnSequenceStmt(Node *node)\n{\n\tGrantStmt *stmt = castNode(GrantStmt, node);\n\tAssert(stmt->objtype == OBJECT_SEQUENCE);\n\n\t/*\n\t * The other option would be GRANT ALL SEQUENCES ON SCHEMA ...\n\t * For that we don't need to qualify\n\t */\n\tif (stmt->targtype != ACL_TARGET_OBJECT)\n\t{\n\t\treturn;\n\t}\n\tList *qualifiedSequenceRangeVars = NIL;\n\tRangeVar *sequenceRangeVar = NULL;\n\tforeach_declared_ptr(sequenceRangeVar, stmt->objects)\n\t{\n\t\tif (sequenceRangeVar->schemaname == NULL)\n\t\t{\n\t\t\tOid seqOid = RangeVarGetRelid(sequenceRangeVar, NoLock, false);\n\t\t\tOid schemaOid = get_rel_namespace(seqOid);\n\t\t\tsequenceRangeVar->schemaname = get_namespace_name(schemaOid);\n\t\t}\n\n\t\tqualifiedSequenceRangeVars = lappend(qualifiedSequenceRangeVars,\n\t\t\t\t\t\t\t\t\t\t\t sequenceRangeVar);\n\t}\n\n\tstmt->objects = qualifiedSequenceRangeVars;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_statistics_stmt.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_statistics_stmt.c\n *\t  Functions specialized in fully qualifying all statistics statements.\n *    These functions are dispatched from qualify.c\n *\n *\t  Goal would be that the deparser functions for these statements can\n *\t  serialize the statement without any external lookups.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_statistic_ext.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/value.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/relcache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\nstatic Oid GetStatsNamespaceOid(Oid statsOid);\n\nvoid\nQualifyCreateStatisticsStmt(Node *node)\n{\n\tCreateStatsStmt *stmt = castNode(CreateStatsStmt, node);\n\n\tNode *relationNode = (Node *) linitial(stmt->relations);\n\n\tif (!IsA(relationNode, RangeVar))\n\t{\n\t\treturn;\n\t}\n\n\tRangeVar *relation = (RangeVar *) relationNode;\n\n\tif (relation->schemaname == NULL)\n\t{\n\t\tOid tableOid = RelnameGetRelid(relation->relname);\n\t\tOid schemaOid = get_rel_namespace(tableOid);\n\t\trelation->schemaname = get_namespace_name(schemaOid);\n\t}\n\n\tif (list_length(stmt->defnames) < 1)\n\t{\n\t\t/* no name to qualify */\n\t\treturn;\n\t}\n\n\tRangeVar *stat = makeRangeVarFromNameList(stmt->defnames);\n\n\tif (stat->schemaname == NULL)\n\t{\n\t\tOid schemaOid = RangeVarGetCreationNamespace(stat);\n\t\tstat->schemaname = get_namespace_name(schemaOid);\n\n\t\tstmt->defnames = MakeNameListFromRangeVar(stat);\n\t}\n}\n\n\n/*\n * QualifyDropStatisticsStmt qualifies DropStmt's with schema name for\n * DROP STATISTICS statements.\n */\nvoid\nQualifyDropStatisticsStmt(Node *node)\n{\n\tDropStmt *dropStatisticsStmt = castNode(DropStmt, node);\n\tAssert(dropStatisticsStmt->removeType == OBJECT_STATISTIC_EXT);\n\n\tList *objectNameListWithSchema = NIL;\n\tList *objectNameList = NULL;\n\tforeach_declared_ptr(objectNameList, dropStatisticsStmt->objects)\n\t{\n\t\tRangeVar *stat = makeRangeVarFromNameList(objectNameList);\n\n\t\tif (stat->schemaname == NULL)\n\t\t{\n\t\t\tOid statsOid = get_statistics_object_oid(objectNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t dropStatisticsStmt->missing_ok);\n\n\t\t\tif (OidIsValid(statsOid))\n\t\t\t{\n\t\t\t\tOid schemaOid = GetStatsNamespaceOid(statsOid);\n\t\t\t\tstat->schemaname = get_namespace_name(schemaOid);\n\t\t\t}\n\t\t}\n\n\t\tobjectNameListWithSchema = lappend(objectNameListWithSchema,\n\t\t\t\t\t\t\t\t\t\t   MakeNameListFromRangeVar(stat));\n\t}\n\n\tdropStatisticsStmt->objects = objectNameListWithSchema;\n}\n\n\n/*\n * QualifyAlterStatisticsRenameStmt qualifies RenameStmt's with schema name for\n * ALTER STATISTICS RENAME statements.\n */\nvoid\nQualifyAlterStatisticsRenameStmt(Node *node)\n{\n\tRenameStmt *renameStmt = castNode(RenameStmt, node);\n\tAssert(renameStmt->renameType == OBJECT_STATISTIC_EXT);\n\n\tList *nameList = (List *) renameStmt->object;\n\tif (list_length(nameList) == 1)\n\t{\n\t\tRangeVar *stat = makeRangeVarFromNameList(nameList);\n\t\tOid statsOid = get_statistics_object_oid(nameList, renameStmt->missing_ok);\n\n\t\tif (!OidIsValid(statsOid))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tOid schemaOid = GetStatsNamespaceOid(statsOid);\n\t\tstat->schemaname = get_namespace_name(schemaOid);\n\t\trenameStmt->object = (Node *) MakeNameListFromRangeVar(stat);\n\t}\n}\n\n\n/*\n * QualifyAlterStatisticsSchemaStmt qualifies AlterObjectSchemaStmt's with schema name for\n * ALTER STATISTICS RENAME statements.\n */\nvoid\nQualifyAlterStatisticsSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_STATISTIC_EXT);\n\n\tList *nameList = (List *) stmt->object;\n\tif (list_length(nameList) == 1)\n\t{\n\t\tRangeVar *stat = makeRangeVarFromNameList(nameList);\n\t\tOid statsOid = get_statistics_object_oid(nameList, stmt->missing_ok);\n\n\t\tif (!OidIsValid(statsOid))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tOid schemaOid = GetStatsNamespaceOid(statsOid);\n\t\tstat->schemaname = get_namespace_name(schemaOid);\n\t\tstmt->object = (Node *) MakeNameListFromRangeVar(stat);\n\t}\n}\n\n\n/*\n * QualifyAlterStatisticsStmt qualifies AlterStatsStmt's with schema name for\n * ALTER STATISTICS .. SET STATISTICS statements.\n */\nvoid\nQualifyAlterStatisticsStmt(Node *node)\n{\n\tAlterStatsStmt *stmt = castNode(AlterStatsStmt, node);\n\n\tif (list_length(stmt->defnames) == 1)\n\t{\n\t\tRangeVar *stat = makeRangeVarFromNameList(stmt->defnames);\n\t\tOid statsOid = get_statistics_object_oid(stmt->defnames, stmt->missing_ok);\n\n\t\tif (!OidIsValid(statsOid))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tOid schemaOid = GetStatsNamespaceOid(statsOid);\n\t\tstat->schemaname = get_namespace_name(schemaOid);\n\t\tstmt->defnames = MakeNameListFromRangeVar(stat);\n\t}\n}\n\n\n/*\n * QualifyAlterStatisticsOwnerStmt qualifies AlterOwnerStmt's with schema\n * name for ALTER STATISTICS .. OWNER TO statements.\n */\nvoid\nQualifyAlterStatisticsOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_STATISTIC_EXT);\n\n\tList *nameList = (List *) stmt->object;\n\tif (list_length(nameList) == 1)\n\t{\n\t\tRangeVar *stat = makeRangeVarFromNameList(nameList);\n\t\tOid statsOid = get_statistics_object_oid(nameList, /* missing_ok */ true);\n\n\t\tif (!OidIsValid(statsOid))\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tOid schemaOid = GetStatsNamespaceOid(statsOid);\n\t\tstat->schemaname = get_namespace_name(schemaOid);\n\t\tstmt->object = (Node *) MakeNameListFromRangeVar(stat);\n\t}\n}\n\n\n/*\n * GetStatsNamespaceOid takes the id of a Statistics object and returns\n * the id of the schema that the statistics object belongs to.\n * Errors out if the stats object is not found.\n */\nstatic Oid\nGetStatsNamespaceOid(Oid statsOid)\n{\n\tHeapTuple heapTuple = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"cache lookup failed for statistics \"\n\t\t\t\t\t\t\t   \"object with oid %u\", statsOid)));\n\t}\n\tFormData_pg_statistic_ext *statisticsForm =\n\t\t(FormData_pg_statistic_ext *) GETSTRUCT(heapTuple);\n\n\tOid result = statisticsForm->stxnamespace;\n\n\tReleaseSysCache(heapTuple);\n\n\treturn result;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_table_stmt.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_table_stmt.c\n *\t  Functions specialized in fully qualifying all table statements. These\n *\t  functions are dispatched from qualify.c\n *\n *\t  Fully qualifying table statements consists of adding the schema name\n *\t  to the subject of the table as well as any other branch of the\n *\t  parsetree.\n *\n *\t  Goal would be that the deparser functions for these statements can\n *\t  serialize the statement without any external lookups.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/relcache.h\"\n\n#include \"distributed/deparser.h\"\n\nvoid\nQualifyAlterTableSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TABLE || stmt->objectType == OBJECT_FOREIGN_TABLE);\n\n\tif (stmt->relation->schemaname == NULL)\n\t{\n\t\tOid tableOid = RelnameGetRelid(stmt->relation->relname);\n\t\tOid schemaOid = get_rel_namespace(tableOid);\n\t\tstmt->relation->schemaname = get_namespace_name(schemaOid);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_text_search_stmts.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_text_search_stmts.c\n *\t  Functions specialized in fully qualifying all text search statements. These\n *\t  functions are dispatched from qualify.c\n *\n *\t  Fully qualifying text search statements consists of adding the schema name\n *\t  to the subject of the types as well as any other branch of the parsetree.\n *\n *\t  Goal would be that the deparser functions for these statements can\n *\t  serialize the statement without any external lookups.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/htup_details.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_ts_config.h\"\n#include \"catalog/pg_ts_dict.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\nstatic Oid get_ts_config_namespace(Oid tsconfigOid);\nstatic Oid get_ts_dict_namespace(Oid tsdictOid);\n\n\n/*\n * QualifyDropTextSearchConfigurationStmt adds any missing schema names to text search\n * configurations being dropped. All configurations are expected to exists before fully\n * qualifying the statement. Errors will be raised for objects not existing. Non-existing\n * objects are expected to not be distributed.\n */\nvoid\nQualifyDropTextSearchConfigurationStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tAssert(stmt->removeType == OBJECT_TSCONFIGURATION);\n\n\tList *qualifiedObjects = NIL;\n\tList *objName = NIL;\n\n\tforeach_declared_ptr(objName, stmt->objects)\n\t{\n\t\tchar *schemaName = NULL;\n\t\tchar *tsconfigName = NULL;\n\t\tDeconstructQualifiedName(objName, &schemaName, &tsconfigName);\n\n\t\tif (!schemaName)\n\t\t{\n\t\t\tOid tsconfigOid = get_ts_config_oid(objName, stmt->missing_ok);\n\t\t\tif (OidIsValid(tsconfigOid))\n\t\t\t{\n\t\t\t\tOid namespaceOid = get_ts_config_namespace(tsconfigOid);\n\t\t\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\t\t\tobjName = list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t makeString(tsconfigName));\n\t\t\t}\n\t\t}\n\n\t\tqualifiedObjects = lappend(qualifiedObjects, objName);\n\t}\n\n\tstmt->objects = qualifiedObjects;\n}\n\n\n/*\n * QualifyDropTextSearchDictionaryStmt adds any missing schema names to text search\n * dictionaries being dropped. All dictionaries are expected to exists before fully\n * qualifying the statement. Errors will be raised for objects not existing. Non-existing\n * objects are expected to not be distributed.\n */\nvoid\nQualifyDropTextSearchDictionaryStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tAssert(stmt->removeType == OBJECT_TSDICTIONARY);\n\n\tList *qualifiedObjects = NIL;\n\tList *objName = NIL;\n\n\tforeach_declared_ptr(objName, stmt->objects)\n\t{\n\t\tchar *schemaName = NULL;\n\t\tchar *tsdictName = NULL;\n\t\tDeconstructQualifiedName(objName, &schemaName, &tsdictName);\n\n\t\tif (!schemaName)\n\t\t{\n\t\t\tOid tsdictOid = get_ts_dict_oid(objName, stmt->missing_ok);\n\t\t\tif (OidIsValid(tsdictOid))\n\t\t\t{\n\t\t\t\tOid namespaceOid = get_ts_dict_namespace(tsdictOid);\n\t\t\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\t\t\tobjName = list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t makeString(tsdictName));\n\t\t\t}\n\t\t}\n\n\t\tqualifiedObjects = lappend(qualifiedObjects, objName);\n\t}\n\n\tstmt->objects = qualifiedObjects;\n}\n\n\n/*\n * QualifyAlterTextSearchConfigurationStmt adds the schema name (if missing) to the name\n * of the text search configurations, as well as the dictionaries referenced.\n */\nvoid\nQualifyAlterTextSearchConfigurationStmt(Node *node)\n{\n\tAlterTSConfigurationStmt *stmt = castNode(AlterTSConfigurationStmt, node);\n\n\tchar *schemaName = NULL;\n\tchar *objName = NULL;\n\tDeconstructQualifiedName(stmt->cfgname, &schemaName, &objName);\n\n\t/* fully qualify the cfgname being altered */\n\tif (!schemaName)\n\t{\n\t\tOid tsconfigOid = get_ts_config_oid(stmt->cfgname, false);\n\t\tOid namespaceOid = get_ts_config_namespace(tsconfigOid);\n\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\tstmt->cfgname = list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t   makeString(objName));\n\t}\n\n\t/* fully qualify the dicts */\n\tbool useNewDicts = false;\n\tList *dicts = NULL;\n\tList *dictName = NIL;\n\tforeach_declared_ptr(dictName, stmt->dicts)\n\t{\n\t\tDeconstructQualifiedName(dictName, &schemaName, &objName);\n\n\t\t/* fully qualify the cfgname being altered */\n\t\tif (!schemaName)\n\t\t{\n\t\t\tOid dictOid = get_ts_dict_oid(dictName, false);\n\t\t\tOid namespaceOid = get_ts_dict_namespace(dictOid);\n\t\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\t\tuseNewDicts = true;\n\t\t\tdictName = list_make2(makeString(schemaName), makeString(objName));\n\t\t}\n\n\t\tdicts = lappend(dicts, dictName);\n\t}\n\n\tif (useNewDicts)\n\t{\n\t\t/* swap original dicts with the new list */\n\t\tstmt->dicts = dicts;\n\t}\n\telse\n\t{\n\t\t/* we don't use the new list, everything was already qualified, free-ing */\n\t\tlist_free(dicts);\n\t}\n}\n\n\n/*\n * QualifyAlterTextSearchDictionaryStmt adds the schema name (if missing) to the name\n * of the text search dictionary.\n */\nvoid\nQualifyAlterTextSearchDictionaryStmt(Node *node)\n{\n\tAlterTSDictionaryStmt *stmt = castNode(AlterTSDictionaryStmt, node);\n\n\tchar *schemaName = NULL;\n\tchar *objName = NULL;\n\tDeconstructQualifiedName(stmt->dictname, &schemaName, &objName);\n\n\t/* fully qualify the dictname being altered */\n\tif (!schemaName)\n\t{\n\t\tOid tsdictOid = get_ts_dict_oid(stmt->dictname, false);\n\t\tOid namespaceOid = get_ts_dict_namespace(tsdictOid);\n\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\tstmt->dictname = list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\tmakeString(objName));\n\t}\n}\n\n\n/*\n * QualifyRenameTextSearchConfigurationStmt adds the schema name (if missing) to the\n * configuration being renamed. The new name will kept be without schema name since this\n * command cannot be used to change the schema of a configuration.\n */\nvoid\nQualifyRenameTextSearchConfigurationStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_TSCONFIGURATION);\n\n\tchar *schemaName = NULL;\n\tchar *objName = NULL;\n\tDeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);\n\n\t/* fully qualify the cfgname being altered */\n\tif (!schemaName)\n\t{\n\t\tOid tsconfigOid = get_ts_config_oid(castNode(List, stmt->object), false);\n\t\tOid namespaceOid = get_ts_config_namespace(tsconfigOid);\n\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\tstmt->object = (Node *) list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t\t   makeString(objName));\n\t}\n}\n\n\n/*\n * QualifyRenameTextSearchDictionaryStmt adds the schema name (if missing) to the\n * dictionary being renamed. The new name will kept be without schema name since this\n * command cannot be used to change the schema of a dictionary.\n */\nvoid\nQualifyRenameTextSearchDictionaryStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_TSDICTIONARY);\n\n\tchar *schemaName = NULL;\n\tchar *objName = NULL;\n\tDeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);\n\n\t/* fully qualify the dictname being altered */\n\tif (!schemaName)\n\t{\n\t\tOid tsdictOid = get_ts_dict_oid(castNode(List, stmt->object), false);\n\t\tOid namespaceOid = get_ts_dict_namespace(tsdictOid);\n\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\tstmt->object = (Node *) list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t\t   makeString(objName));\n\t}\n}\n\n\n/*\n * QualifyAlterTextSearchConfigurationSchemaStmt adds the schema name (if missing) for the\n * text search config being moved to a new schema.\n */\nvoid\nQualifyAlterTextSearchConfigurationSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TSCONFIGURATION);\n\n\tchar *schemaName = NULL;\n\tchar *objName = NULL;\n\tDeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);\n\n\tif (!schemaName)\n\t{\n\t\tOid tsconfigOid = get_ts_config_oid(castNode(List, stmt->object), false);\n\t\tOid namespaceOid = get_ts_config_namespace(tsconfigOid);\n\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\tstmt->object = (Node *) list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t\t   makeString(objName));\n\t}\n}\n\n\n/*\n * QualifyAlterTextSearchDictionarySchemaStmt adds the schema name (if missing) for the\n * text search dictionary being moved to a new schema.\n */\nvoid\nQualifyAlterTextSearchDictionarySchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TSDICTIONARY);\n\n\tchar *schemaName = NULL;\n\tchar *objName = NULL;\n\tDeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);\n\n\tif (!schemaName)\n\t{\n\t\tOid tsdictOid = get_ts_dict_oid(castNode(List, stmt->object), false);\n\t\tOid namespaceOid = get_ts_dict_namespace(tsdictOid);\n\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\tstmt->object = (Node *) list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t\t   makeString(objName));\n\t}\n}\n\n\n/*\n * QualifyTextSearchConfigurationCommentStmt adds the schema name (if missing) to the\n * configuration name on which the comment is created.\n */\nvoid\nQualifyTextSearchConfigurationCommentStmt(Node *node)\n{\n\tCommentStmt *stmt = castNode(CommentStmt, node);\n\tAssert(stmt->objtype == OBJECT_TSCONFIGURATION);\n\n\tchar *schemaName = NULL;\n\tchar *objName = NULL;\n\tDeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);\n\n\tif (!schemaName)\n\t{\n\t\tOid tsconfigOid = get_ts_config_oid(castNode(List, stmt->object), false);\n\t\tOid namespaceOid = get_ts_config_namespace(tsconfigOid);\n\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\tstmt->object = (Node *) list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t\t   makeString(objName));\n\t}\n}\n\n\n/*\n * QualifyTextSearchDictionaryCommentStmt adds the schema name (if missing) to the\n * dictionary name on which the comment is created.\n */\nvoid\nQualifyTextSearchDictionaryCommentStmt(Node *node)\n{\n\tCommentStmt *stmt = castNode(CommentStmt, node);\n\tAssert(stmt->objtype == OBJECT_TSDICTIONARY);\n\n\tchar *schemaName = NULL;\n\tchar *objName = NULL;\n\tDeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);\n\n\tif (!schemaName)\n\t{\n\t\tOid tsdictOid = get_ts_dict_oid(castNode(List, stmt->object), false);\n\t\tOid namespaceOid = get_ts_dict_namespace(tsdictOid);\n\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\tstmt->object = (Node *) list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t\t   makeString(objName));\n\t}\n}\n\n\n/*\n * QualifyAlterTextSearchConfigurationOwnerStmt adds the schema name (if missing) to the\n * configuration for which the owner is changing.\n */\nvoid\nQualifyAlterTextSearchConfigurationOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_TSCONFIGURATION);\n\n\tchar *schemaName = NULL;\n\tchar *objName = NULL;\n\tDeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);\n\n\tif (!schemaName)\n\t{\n\t\tOid tsconfigOid = get_ts_config_oid(castNode(List, stmt->object), false);\n\t\tOid namespaceOid = get_ts_config_namespace(tsconfigOid);\n\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\tstmt->object = (Node *) list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t\t   makeString(objName));\n\t}\n}\n\n\n/*\n * QualifyAlterTextSearchDictionaryOwnerStmt adds the schema name (if missing) to the\n * dictionary for which the owner is changing.\n */\nvoid\nQualifyAlterTextSearchDictionaryOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_TSDICTIONARY);\n\n\tchar *schemaName = NULL;\n\tchar *objName = NULL;\n\tDeconstructQualifiedName(castNode(List, stmt->object), &schemaName, &objName);\n\n\tif (!schemaName)\n\t{\n\t\tOid tsdictOid = get_ts_dict_oid(castNode(List, stmt->object), false);\n\t\tOid namespaceOid = get_ts_dict_namespace(tsdictOid);\n\t\tschemaName = get_namespace_name(namespaceOid);\n\n\t\tstmt->object = (Node *) list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t\t   makeString(objName));\n\t}\n}\n\n\n/*\n * get_ts_config_namespace returns the oid of the namespace which is housing the text\n * search configuration identified by tsconfigOid.\n */\nstatic Oid\nget_ts_config_namespace(Oid tsconfigOid)\n{\n\tHeapTuple tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(tsconfigOid));\n\n\tif (HeapTupleIsValid(tup))\n\t{\n\t\tForm_pg_ts_config cfgform = (Form_pg_ts_config) GETSTRUCT(tup);\n\t\tOid namespaceOid = cfgform->cfgnamespace;\n\t\tReleaseSysCache(tup);\n\n\t\treturn namespaceOid;\n\t}\n\n\treturn InvalidOid;\n}\n\n\n/*\n * get_ts_dict_namespace returns the oid of the namespace which is housing the text\n * search dictionary identified by tsdictOid.\n */\nstatic Oid\nget_ts_dict_namespace(Oid tsdictOid)\n{\n\tHeapTuple tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(tsdictOid));\n\n\tif (HeapTupleIsValid(tup))\n\t{\n\t\tForm_pg_ts_dict cfgform = (Form_pg_ts_dict) GETSTRUCT(tup);\n\t\tOid namespaceOid = cfgform->dictnamespace;\n\t\tReleaseSysCache(tup);\n\n\t\treturn namespaceOid;\n\t}\n\n\treturn InvalidOid;\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_type_stmt.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_type_stmt.c\n *\t  Functions specialized in fully qualifying all type statements. These\n *\t  functions are dispatched from qualify.c\n *\n *\t  Fully qualifying type statements consists of adding the schema name\n *\t  to the subject of the types as well as any other branch of the\n *\t  parsetree.\n *\n *\t  Goal would be that the deparser functions for these statements can\n *\t  serialize the statement without any external lookups.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/objectaddress.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"parser/parse_type.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/version_compat.h\"\n\n/*\n * GetTypeNamespaceNameByNameList resolved the schema name of a type by its namelist.\n */\nchar *\nGetTypeNamespaceNameByNameList(List *names)\n{\n\tTypeName *typeName = makeTypeNameFromNameList(names);\n\tOid typeOid = LookupTypeNameOid(NULL, typeName, false);\n\tOid namespaceOid = TypeOidGetNamespaceOid(typeOid);\n\tchar *nspname = get_namespace_name_or_temp(namespaceOid);\n\treturn nspname;\n}\n\n\n/*\n * TypeOidGetNamespaceOid resolves the namespace oid for a type identified by its type oid\n */\nOid\nTypeOidGetNamespaceOid(Oid typeOid)\n{\n\tHeapTuple typeTuple = SearchSysCache1(TYPEOID, typeOid);\n\n\tif (!HeapTupleIsValid(typeTuple))\n\t{\n\t\telog(ERROR, \"citus cache lookup failed\");\n\t\treturn InvalidOid;\n\t}\n\tForm_pg_type typeData = (Form_pg_type) GETSTRUCT(typeTuple);\n\tOid typnamespace = typeData->typnamespace;\n\n\tReleaseSysCache(typeTuple);\n\n\treturn typnamespace;\n}\n\n\nvoid\nQualifyRenameTypeStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tList *names = (List *) stmt->object;\n\n\tAssert(stmt->renameType == OBJECT_TYPE);\n\n\tif (list_length(names) == 1)\n\t{\n\t\t/* not qualified, lookup name and add namespace name to names */\n\t\tchar *nspname = GetTypeNamespaceNameByNameList(names);\n\t\tnames = list_make2(makeString(nspname), linitial(names));\n\n\t\tstmt->object = (Node *) names;\n\t}\n}\n\n\nvoid\nQualifyRenameTypeAttributeStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tAssert(stmt->renameType == OBJECT_ATTRIBUTE);\n\tAssert(stmt->relationType == OBJECT_TYPE);\n\n\tif (stmt->relation->schemaname == NULL)\n\t{\n\t\tList *names = list_make1(makeString(stmt->relation->relname));\n\t\tchar *nspname = GetTypeNamespaceNameByNameList(names);\n\t\tstmt->relation->schemaname = nspname;\n\t}\n}\n\n\nvoid\nQualifyAlterEnumStmt(Node *node)\n{\n\tAlterEnumStmt *stmt = castNode(AlterEnumStmt, node);\n\tList *names = stmt->typeName;\n\n\tif (list_length(names) == 1)\n\t{\n\t\t/* not qualified, lookup name and add namespace name to names */\n\t\tchar *nspname = GetTypeNamespaceNameByNameList(names);\n\t\tnames = list_make2(makeString(nspname), linitial(names));\n\n\t\tstmt->typeName = names;\n\t}\n}\n\n\nvoid\nQualifyAlterTypeStmt(Node *node)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tAssert(stmt->objtype == OBJECT_TYPE);\n\n\tif (stmt->relation->schemaname == NULL)\n\t{\n\t\tList *names = MakeNameListFromRangeVar(stmt->relation);\n\t\tchar *nspname = GetTypeNamespaceNameByNameList(names);\n\t\tstmt->relation->schemaname = nspname;\n\t}\n}\n\n\nvoid\nQualifyCompositeTypeStmt(Node *node)\n{\n\tCompositeTypeStmt *stmt = castNode(CompositeTypeStmt, node);\n\n\tif (stmt->typevar->schemaname == NULL)\n\t{\n\t\tOid creationSchema = RangeVarGetCreationNamespace(stmt->typevar);\n\t\tstmt->typevar->schemaname = get_namespace_name(creationSchema);\n\t}\n}\n\n\nvoid\nQualifyCreateEnumStmt(Node *node)\n{\n\tCreateEnumStmt *stmt = castNode(CreateEnumStmt, node);\n\n\tif (list_length(stmt->typeName) == 1)\n\t{\n\t\tchar *objname = NULL;\n\t\tOid creationSchema = QualifiedNameGetCreationNamespace(stmt->typeName, &objname);\n\t\tstmt->typeName = list_make2(makeString(get_namespace_name(creationSchema)),\n\t\t\t\t\t\t\t\t\tlinitial(stmt->typeName));\n\t}\n}\n\n\nvoid\nQualifyAlterTypeSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tAssert(stmt->objectType == OBJECT_TYPE);\n\n\tList *names = (List *) stmt->object;\n\tif (list_length(names) == 1)\n\t{\n\t\t/* not qualified with schema, lookup type and its schema s*/\n\t\tchar *nspname = GetTypeNamespaceNameByNameList(names);\n\t\tnames = list_make2(makeString(nspname), linitial(names));\n\t\tstmt->object = (Node *) names;\n\t}\n}\n\n\nvoid\nQualifyAlterTypeOwnerStmt(Node *node)\n{\n\tAlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);\n\tAssert(stmt->objectType == OBJECT_TYPE);\n\n\tList *names = (List *) stmt->object;\n\tif (list_length(names) == 1)\n\t{\n\t\t/* not qualified with schema, lookup type and its schema s*/\n\t\tchar *nspname = GetTypeNamespaceNameByNameList(names);\n\t\tnames = list_make2(makeString(nspname), linitial(names));\n\t\tstmt->object = (Node *) names;\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/qualify_view_stmt.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * qualify_view_stmt.c\n *\t  Functions specialized in fully qualifying all view statements. These\n *\t  functions are dispatched from qualify.c\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"nodes/nodes.h\"\n#include \"utils/guc.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n\nstatic void QualifyViewRangeVar(RangeVar *view);\n\n/*\n * QualifyDropViewStmt quailifies the view names of the DROP VIEW statement.\n */\nvoid\nQualifyDropViewStmt(Node *node)\n{\n\tDropStmt *stmt = castNode(DropStmt, node);\n\tList *qualifiedViewNames = NIL;\n\n\tList *possiblyQualifiedViewName = NULL;\n\tforeach_declared_ptr(possiblyQualifiedViewName, stmt->objects)\n\t{\n\t\tchar *viewName = NULL;\n\t\tchar *schemaName = NULL;\n\t\tList *viewNameToAdd = possiblyQualifiedViewName;\n\t\tDeconstructQualifiedName(possiblyQualifiedViewName, &schemaName, &viewName);\n\n\t\tif (schemaName == NULL)\n\t\t{\n\t\t\tRangeVar *viewRangeVar = makeRangeVarFromNameList(possiblyQualifiedViewName);\n\t\t\tOid viewOid = RangeVarGetRelid(viewRangeVar, AccessExclusiveLock,\n\t\t\t\t\t\t\t\t\t\t   stmt->missing_ok);\n\n\t\t\t/*\n\t\t\t * If DROP VIEW IF EXISTS called and the view doesn't exist, oid can be invalid.\n\t\t\t * Do not try to qualify it.\n\t\t\t */\n\t\t\tif (OidIsValid(viewOid))\n\t\t\t{\n\t\t\t\tOid schemaOid = get_rel_namespace(viewOid);\n\t\t\t\tschemaName = get_namespace_name(schemaOid);\n\n\t\t\t\tList *qualifiedViewName = list_make2(makeString(schemaName),\n\t\t\t\t\t\t\t\t\t\t\t\t\t makeString(viewName));\n\t\t\t\tviewNameToAdd = qualifiedViewName;\n\t\t\t}\n\t\t}\n\n\t\tqualifiedViewNames = lappend(qualifiedViewNames, viewNameToAdd);\n\t}\n\n\tstmt->objects = qualifiedViewNames;\n}\n\n\n/*\n * QualifyAlterViewStmt quailifies the view name of the ALTER VIEW statement.\n */\nvoid\nQualifyAlterViewStmt(Node *node)\n{\n\tAlterTableStmt *stmt = castNode(AlterTableStmt, node);\n\tRangeVar *view = stmt->relation;\n\tQualifyViewRangeVar(view);\n}\n\n\n/*\n * QualifyRenameViewStmt quailifies the view name of the ALTER VIEW ... RENAME statement.\n */\nvoid\nQualifyRenameViewStmt(Node *node)\n{\n\tRenameStmt *stmt = castNode(RenameStmt, node);\n\tRangeVar *view = stmt->relation;\n\tQualifyViewRangeVar(view);\n}\n\n\n/*\n * QualifyAlterViewSchemaStmt quailifies the view name of the ALTER VIEW ... SET SCHEMA statement.\n */\nvoid\nQualifyAlterViewSchemaStmt(Node *node)\n{\n\tAlterObjectSchemaStmt *stmt = castNode(AlterObjectSchemaStmt, node);\n\tRangeVar *view = stmt->relation;\n\tQualifyViewRangeVar(view);\n}\n\n\n/*\n * QualifyViewRangeVar qualifies the given view RangeVar if it is not qualified.\n */\nstatic void\nQualifyViewRangeVar(RangeVar *view)\n{\n\tif (view->schemaname == NULL)\n\t{\n\t\tOid viewOid = RelnameGetRelid(view->relname);\n\t\tOid schemaOid = get_rel_namespace(viewOid);\n\t\tview->schemaname = get_namespace_name(schemaOid);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/deparser/ruleutils_16.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * ruleutils_16.c\n *\t  Functions to convert stored expressions/querytrees back to\n *\t  source text\n *\n * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group\n * Portions Copyright (c) 1994, Regents of the University of California\n *\n *\n * IDENTIFICATION\n *\t  src/backend/distributed/deparser/ruleutils_16.c\n *\n * This needs to be closely in sync with the core code.\n *-------------------------------------------------------------------------\n */\n#include \"pg_version_constants.h\"\n\n#include \"pg_config.h\"\n\n#if (PG_VERSION_NUM >= PG_VERSION_16) && (PG_VERSION_NUM < PG_VERSION_17)\n\n#include \"postgres.h\"\n\n#include <ctype.h>\n#include <unistd.h>\n#include <fcntl.h>\n\n#include \"access/amapi.h\"\n#include \"access/htup_details.h\"\n#include \"access/relation.h\"\n#include \"access/sysattr.h\"\n#include \"access/table.h\"\n#include \"catalog/pg_aggregate.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_extension.h\"\n#include \"catalog/pg_foreign_data_wrapper.h\"\n#include \"catalog/pg_language.h\"\n#include \"catalog/pg_opclass.h\"\n#include \"catalog/pg_operator.h\"\n#include \"catalog/pg_partitioned_table.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_statistic_ext.h\"\n#include \"catalog/pg_trigger.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/defrem.h\"\n#include \"commands/extension.h\"\n#include \"commands/tablespace.h\"\n#include \"common/keywords.h\"\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"executor/spi.h\"\n#include \"foreign/foreign.h\"\n#include \"funcapi.h\"\n#include \"mb/pg_wchar.h\"\n#include \"miscadmin.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/pathnodes.h\"\n#include \"optimizer/optimizer.h\"\n#include \"parser/parse_node.h\"\n#include \"parser/parse_agg.h\"\n#include \"parser/parse_func.h\"\n#include \"parser/parse_oper.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parser.h\"\n#include \"parser/parsetree.h\"\n#include \"rewrite/rewriteHandler.h\"\n#include \"rewrite/rewriteManip.h\"\n#include \"rewrite/rewriteSupport.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/ruleutils.h\"\n#include \"utils/snapmgr.h\"\n#include \"utils/syscache.h\"\n#include \"utils/typcache.h\"\n#include \"utils/varlena.h\"\n#include \"utils/xml.h\"\n\n\n/* ----------\n * Pretty formatting constants\n * ----------\n */\n\n/* Indent counts */\n#define PRETTYINDENT_STD\t\t8\n#define PRETTYINDENT_JOIN\t\t4\n#define PRETTYINDENT_VAR\t\t4\n\n#define PRETTYINDENT_LIMIT\t\t40\t/* wrap limit */\n\n/* Pretty flags */\n#define PRETTYFLAG_PAREN\t\t0x0001\n#define PRETTYFLAG_INDENT\t\t0x0002\n\n/* Default line length for pretty-print wrapping: 0 means wrap always */\n#define WRAP_COLUMN_DEFAULT\t\t0\n\n/* macros to test if pretty action needed */\n#define PRETTY_PAREN(context)\t((context)->prettyFlags & PRETTYFLAG_PAREN)\n#define PRETTY_INDENT(context)\t((context)->prettyFlags & PRETTYFLAG_INDENT)\n\n\n/* ----------\n * Local data types\n * ----------\n */\n\n/* Context info needed for invoking a recursive querytree display routine */\ntypedef struct\n{\n\tStringInfo\tbuf;\t\t\t/* output buffer to append to */\n\tList\t   *namespaces;\t\t/* List of deparse_namespace nodes */\n\tTupleDesc\tresultDesc;\t\t/* if top level of a view, the view's tupdesc */\n\tList\t   *targetList;\t\t/* Current query level's SELECT targetlist */\n\tList\t   *windowClause;\t/* Current query level's WINDOW clause */\n\tint\t\t\tprettyFlags;\t/* enabling of pretty-print functions */\n\tint\t\t\twrapColumn;\t\t/* max line length, or -1 for no limit */\n\tint\t\t\tindentLevel;\t/* current indent level for prettyprint */\n\tbool\t\tvarprefix;\t\t/* true to print prefixes on Vars */\n\tOid\t\t\tdistrelid;\t\t/* the distributed table being modified, if valid */\n\tint64\t\tshardid;\t\t/* a distributed table's shardid, if positive */\n\tbool\t\tcolNamesVisible;\t/* do we care about output column names? */\n\tbool\t\tinGroupBy;\t\t/* deparsing GROUP BY clause? */\n\tbool\t\tvarInOrderBy;\t/* deparsing simple Var in ORDER BY? */\n\tBitmapset  *appendparents;\t/* if not null, map child Vars of these relids\n\t\t\t\t\t\t\t\t * back to the parent rel */\n} deparse_context;\n\n/*\n * Each level of query context around a subtree needs a level of Var namespace.\n * A Var having varlevelsup=N refers to the N'th item (counting from 0) in\n * the current context's namespaces list.\n *\n * The rangetable is the list of actual RTEs from the query tree, and the\n * cte list is the list of actual CTEs.\n *\n * rtable_names holds the alias name to be used for each RTE (either a C\n * string, or NULL for nameless RTEs such as unnamed joins).\n * rtable_columns holds the column alias names to be used for each RTE.\n *\n * In some cases we need to make names of merged JOIN USING columns unique\n * across the whole query, not only per-RTE.  If so, unique_using is true\n * and using_names is a list of C strings representing names already assigned\n * to USING columns.\n *\n * When deparsing plan trees, there is always just a single item in the\n * deparse_namespace list (since a plan tree never contains Vars with\n * varlevelsup > 0).  We store the PlanState node that is the immediate\n * parent of the expression to be deparsed, as well as a list of that\n * PlanState's ancestors.  In addition, we store its outer and inner subplan\n * state nodes, as well as their plan nodes' targetlists, and the index tlist\n * if the current plan node might contain INDEX_VAR Vars.  (These fields could\n * be derived on-the-fly from the current PlanState, but it seems notationally\n * clearer to set them up as separate fields.)\n */\ntypedef struct\n{\n\tList\t   *rtable;\t\t\t/* List of RangeTblEntry nodes */\n\tList\t   *rtable_names;\t/* Parallel list of names for RTEs */\n\tList\t   *rtable_columns; /* Parallel list of deparse_columns structs */\n\tList\t   *subplans;\t\t/* List of Plan trees for SubPlans */\n\tList\t   *ctes;\t\t\t/* List of CommonTableExpr nodes */\n\tAppendRelInfo **appendrels; /* Array of AppendRelInfo nodes, or NULL */\n\t/* Workspace for column alias assignment: */\n\tbool\t\tunique_using;\t/* Are we making USING names globally unique */\n\tList\t   *using_names;\t/* List of assigned names for USING columns */\n\t/* Remaining fields are used only when deparsing a Plan tree: */\n\tPlan\t   *plan;\t\t\t/* immediate parent of current expression */\n\tList\t   *ancestors;\t\t/* ancestors of planstate */\n\tPlan\t   *outer_plan;\t\t/* outer subnode, or NULL if none */\n\tPlan\t   *inner_plan;\t\t/* inner subnode, or NULL if none */\n\tList\t   *outer_tlist;\t/* referent for OUTER_VAR Vars */\n\tList\t   *inner_tlist;\t/* referent for INNER_VAR Vars */\n\tList\t   *index_tlist;\t/* referent for INDEX_VAR Vars */\n\t/* Special namespace representing a function signature: */\n\tchar\t   *funcname;\n\tint\t\t\tnumargs;\n\tchar\t  **argnames;\n} deparse_namespace;\n\n/* Callback signature for resolve_special_varno() */\ntypedef void (*rsv_callback) (Node *node, deparse_context *context,\n\t\t\t\t\t\t\t  void *callback_arg);\n\n/*\n * Per-relation data about column alias names.\n *\n * Selecting aliases is unreasonably complicated because of the need to dump\n * rules/views whose underlying tables may have had columns added, deleted, or\n * renamed since the query was parsed.  We must nonetheless print the rule/view\n * in a form that can be reloaded and will produce the same results as before.\n *\n * For each RTE used in the query, we must assign column aliases that are\n * unique within that RTE.  SQL does not require this of the original query,\n * but due to factors such as *-expansion we need to be able to uniquely\n * reference every column in a decompiled query.  As long as we qualify all\n * column references, per-RTE uniqueness is sufficient for that.\n *\n * However, we can't ensure per-column name uniqueness for unnamed join RTEs,\n * since they just inherit column names from their input RTEs, and we can't\n * rename the columns at the join level.  Most of the time this isn't an issue\n * because we don't need to reference the join's output columns as such; we\n * can reference the input columns instead.  That approach can fail for merged\n * JOIN USING columns, however, so when we have one of those in an unnamed\n * join, we have to make that column's alias globally unique across the whole\n * query to ensure it can be referenced unambiguously.\n *\n * Another problem is that a JOIN USING clause requires the columns to be\n * merged to have the same aliases in both input RTEs, and that no other\n * columns in those RTEs or their children conflict with the USING names.\n * To handle that, we do USING-column alias assignment in a recursive\n * traversal of the query's jointree.  When descending through a JOIN with\n * USING, we preassign the USING column names to the child columns, overriding\n * other rules for column alias assignment.  We also mark each RTE with a list\n * of all USING column names selected for joins containing that RTE, so that\n * when we assign other columns' aliases later, we can avoid conflicts.\n *\n * Another problem is that if a JOIN's input tables have had columns added or\n * deleted since the query was parsed, we must generate a column alias list\n * for the join that matches the current set of input columns --- otherwise, a\n * change in the number of columns in the left input would throw off matching\n * of aliases to columns of the right input.  Thus, positions in the printable\n * column alias list are not necessarily one-for-one with varattnos of the\n * JOIN, so we need a separate new_colnames[] array for printing purposes.\n */\ntypedef struct\n{\n\t/*\n\t * colnames is an array containing column aliases to use for columns that\n\t * existed when the query was parsed.  Dropped columns have NULL entries.\n\t * This array can be directly indexed by varattno to get a Var's name.\n\t *\n\t * Non-NULL entries are guaranteed unique within the RTE, *except* when\n\t * this is for an unnamed JOIN RTE.  In that case we merely copy up names\n\t * from the two input RTEs.\n\t *\n\t * During the recursive descent in set_using_names(), forcible assignment\n\t * of a child RTE's column name is represented by pre-setting that element\n\t * of the child's colnames array.  So at that stage, NULL entries in this\n\t * array just mean that no name has been preassigned, not necessarily that\n\t * the column is dropped.\n\t */\n\tint\t\t\tnum_cols;\t\t/* length of colnames[] array */\n\tchar\t  **colnames;\t\t/* array of C strings and NULLs */\n\n\t/*\n\t * new_colnames is an array containing column aliases to use for columns\n\t * that would exist if the query was re-parsed against the current\n\t * definitions of its base tables.  This is what to print as the column\n\t * alias list for the RTE.  This array does not include dropped columns,\n\t * but it will include columns added since original parsing.  Indexes in\n\t * it therefore have little to do with current varattno values.  As above,\n\t * entries are unique unless this is for an unnamed JOIN RTE.  (In such an\n\t * RTE, we never actually print this array, but we must compute it anyway\n\t * for possible use in computing column names of upper joins.) The\n\t * parallel array is_new_col marks which of these columns are new since\n\t * original parsing.  Entries with is_new_col false must match the\n\t * non-NULL colnames entries one-for-one.\n\t */\n\tint\t\t\tnum_new_cols;\t/* length of new_colnames[] array */\n\tchar\t  **new_colnames;\t/* array of C strings */\n\tbool\t   *is_new_col;\t\t/* array of bool flags */\n\n\t/* This flag tells whether we should actually print a column alias list */\n\tbool\t\tprintaliases;\n\n\t/* This list has all names used as USING names in joins above this RTE */\n\tList\t   *parentUsing;\t/* names assigned to parent merged columns */\n\n\t/*\n\t * If this struct is for a JOIN RTE, we fill these fields during the\n\t * set_using_names() pass to describe its relationship to its child RTEs.\n\t *\n\t * leftattnos and rightattnos are arrays with one entry per existing\n\t * output column of the join (hence, indexable by join varattno).  For a\n\t * simple reference to a column of the left child, leftattnos[i] is the\n\t * child RTE's attno and rightattnos[i] is zero; and conversely for a\n\t * column of the right child.  But for merged columns produced by JOIN\n\t * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.\n\t * Also, if the column has been dropped, both are zero.\n\t *\n\t * If it's a JOIN USING, usingNames holds the alias names selected for the\n\t * merged columns (these might be different from the original USING list,\n\t * if we had to modify names to achieve uniqueness).\n\t */\n\tint\t\t\tleftrti;\t\t/* rangetable index of left child */\n\tint\t\t\trightrti;\t\t/* rangetable index of right child */\n\tint\t\t   *leftattnos;\t\t/* left-child varattnos of join cols, or 0 */\n\tint\t\t   *rightattnos;\t/* right-child varattnos of join cols, or 0 */\n\tList\t   *usingNames;\t\t/* names assigned to merged columns */\n} deparse_columns;\n\n/* This macro is analogous to rt_fetch(), but for deparse_columns structs */\n#define deparse_columns_fetch(rangetable_index, dpns) \\\n\t((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))\n\n/*\n * Entry in set_rtable_names' hash table\n */\ntypedef struct\n{\n\tchar\t\tname[NAMEDATALEN];\t/* Hash key --- must be first */\n\tint\t\t\tcounter;\t\t/* Largest addition used so far for name */\n} NameHashEntry;\n\n\n/* ----------\n * Local functions\n *\n * Most of these functions used to use fixed-size buffers to build their\n * results.  Now, they take an (already initialized) StringInfo object\n * as a parameter, and append their text output to its contents.\n * ----------\n */\nstatic void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,\n\t\t\t\t Bitmapset *rels_used);\nstatic void set_deparse_for_query(deparse_namespace *dpns, Query *query,\n\t\t\t\t\t  List *parent_namespaces);\nstatic bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);\nstatic void set_using_names(deparse_namespace *dpns, Node *jtnode,\n\t\t\t\tList *parentUsing);\nstatic void set_relation_column_names(deparse_namespace *dpns,\n\t\t\t\t\t\t  RangeTblEntry *rte,\n\t\t\t\t\t\t  deparse_columns *colinfo);\nstatic void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,\n\t\t\t\t\t  deparse_columns *colinfo);\nstatic bool colname_is_unique(const char *colname, deparse_namespace *dpns,\n\t\t\t\t  deparse_columns *colinfo);\nstatic char *make_colname_unique(char *colname, deparse_namespace *dpns,\n\t\t\t\t\tdeparse_columns *colinfo);\nstatic void expand_colnames_array_to(deparse_columns *colinfo, int n);\nstatic void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,\n\t\t\t\t\t  deparse_columns *colinfo);\nstatic char *get_rtable_name(int rtindex, deparse_context *context);\nstatic void set_deparse_plan(deparse_namespace *dpns, Plan *plan);\nstatic Plan *find_recursive_union(deparse_namespace *dpns,\n\t\t\t\t\t\t\t\t  WorkTableScan *wtscan);\nstatic void push_child_plan(deparse_namespace *dpns, Plan *plan,\n\t\t\t\tdeparse_namespace *save_dpns);\nstatic void pop_child_plan(deparse_namespace *dpns,\n\t\t\t   deparse_namespace *save_dpns);\nstatic void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,\n\t\t\t\t   deparse_namespace *save_dpns);\nstatic void pop_ancestor_plan(deparse_namespace *dpns,\n\t\t\t\t  deparse_namespace *save_dpns);\nstatic void get_query_def(Query *query, StringInfo buf, List *parentnamespace,\n\t\t\t  TupleDesc resultDesc, bool colNamesVisible,\n\t\t\t  int prettyFlags, int wrapColumn, int startIndent);\nstatic void get_query_def_extended(Query *query, StringInfo buf,\n\t\t\t\tList *parentnamespace, Oid distrelid, int64 shardid,\n\t\t\t\tTupleDesc resultDesc, bool colNamesVisible,\n\t\t\t\tint prettyFlags, int wrapColumn,\n\t\t\t\tint startIndent);\nstatic void get_values_def(List *values_lists, deparse_context *context);\nstatic void get_with_clause(Query *query, deparse_context *context);\nstatic void get_select_query_def(Query *query, deparse_context *context);\nstatic void get_insert_query_def(Query *query, deparse_context *context);\nstatic void get_update_query_def(Query *query, deparse_context *context);\nstatic void get_update_query_targetlist_def(Query *query, List *targetList,\n\t\t\t\t\t\t\t\tdeparse_context *context,\n\t\t\t\t\t\t\t\tRangeTblEntry *rte);\nstatic void get_delete_query_def(Query *query, deparse_context *context);\nstatic void get_merge_query_def(Query *query, deparse_context *context);\nstatic void get_utility_query_def(Query *query, deparse_context *context);\nstatic void get_basic_select_query(Query *query, deparse_context *context);\nstatic void get_target_list(List *targetList, deparse_context *context);\nstatic void get_setop_query(Node *setOp, Query *query,\n\t\t\t\t\t\t\tdeparse_context *context);\nstatic Node *get_rule_sortgroupclause(Index ref, List *tlist,\n\t\t\t\t\t\t bool force_colno,\n\t\t\t\t\t\t deparse_context *context);\nstatic void get_rule_groupingset(GroupingSet *gset, List *targetlist,\n\t\t\t\t\t bool omit_parens, deparse_context *context);\nstatic void get_rule_orderby(List *orderList, List *targetList,\n\t\t\t\t bool force_colno, deparse_context *context);\nstatic void get_rule_windowclause(Query *query, deparse_context *context);\nstatic void get_rule_windowspec(WindowClause *wc, List *targetList,\n\t\t\t\t\tdeparse_context *context);\nstatic char *get_variable(Var *var, int levelsup, bool istoplevel,\n\t\t\t deparse_context *context);\nstatic void get_special_variable(Node *node, deparse_context *context,\n\t\t\t\t\t void *callback_arg);\nstatic void resolve_special_varno(Node *node, deparse_context *context,\n\t\t\t\t\trsv_callback callback, void *callback_arg);\nstatic Node *find_param_referent(Param *param, deparse_context *context,\n\t\t\t\t\tdeparse_namespace **dpns_p, ListCell **ancestor_cell_p);\nstatic void get_parameter(Param *param, deparse_context *context);\nstatic const char *get_simple_binary_op_name(OpExpr *expr);\nstatic bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);\nstatic void appendContextKeyword(deparse_context *context, const char *str,\n\t\t\t\t\t int indentBefore, int indentAfter, int indentPlus);\nstatic void removeStringInfoSpaces(StringInfo str);\nstatic void get_rule_expr(Node *node, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_rule_expr_toplevel(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit);\nstatic void get_rule_list_toplevel(List *lst, deparse_context *context,\n\t\t\t\t\t\t\t\t   bool showimplicit);\nstatic void get_rule_expr_funccall(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit);\nstatic bool looks_like_function(Node *node);\nstatic void get_oper_expr(OpExpr *expr, deparse_context *context);\nstatic void get_func_expr(FuncExpr *expr, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_proc_expr(CallStmt *stmt, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_agg_expr(Aggref *aggref, deparse_context *context,\n\t\t\t Aggref *original_aggref);\nstatic void get_agg_expr_helper(Aggref *aggref, deparse_context *context,\n\t\t\t\t\t\t\t\tAggref *original_aggref, const char *funcname,\n\t\t\t\t\t\t\t\tconst char *options, bool is_json_objectagg);\nstatic void get_agg_combine_expr(Node *node, deparse_context *context,\n\t\t\t\t\t void *callback_arg);\nstatic void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);\nstatic void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,\n\t\t\t\t\t\t\t\t\t   const char *funcname, const char *options,\n\t\t\t\t\t\t\t\t\t   bool is_json_objectagg);\nstatic bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);\nstatic void get_coercion_expr(Node *arg, deparse_context *context,\n\t\t\t\t  Oid resulttype, int32 resulttypmod,\n\t\t\t\t  Node *parentNode);\nstatic void get_const_expr(Const *constval, deparse_context *context,\n\t\t\t   int showtype);\nstatic void get_const_collation(Const *constval, deparse_context *context);\nstatic void get_json_format(JsonFormat *format, StringInfo buf);\nstatic void get_json_constructor(JsonConstructorExpr *ctor,\n\t\t\t\t\t\t\t\t deparse_context *context, bool showimplicit);\nstatic void get_json_constructor_options(JsonConstructorExpr *ctor,\n\t\t\t\t\t\t\t\t\t\t StringInfo buf);\nstatic void get_json_agg_constructor(JsonConstructorExpr *ctor,\n\t\t\t\t\t\t\t\t\t deparse_context *context,\n\t\t\t\t\t\t\t\t\t const char *funcname,\n\t\t\t\t\t\t\t\t\t bool is_json_objectagg);\nstatic void simple_quote_literal(StringInfo buf, const char *val);\nstatic void get_sublink_expr(SubLink *sublink, deparse_context *context);\nstatic void get_tablefunc(TableFunc *tf, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_from_clause(Query *query, const char *prefix,\n\t\t\t\tdeparse_context *context);\nstatic void get_from_clause_item(Node *jtnode, Query *query,\n\t\t\t\t\t deparse_context *context);\nstatic void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,\n\t\t\t\t\t\t  deparse_context *context);\nstatic void get_column_alias_list(deparse_columns *colinfo,\n\t\t\t\t\t  deparse_context *context);\nstatic void get_from_clause_coldeflist(RangeTblFunction *rtfunc,\n\t\t\t\t\t\t   deparse_columns *colinfo,\n\t\t\t\t\t\t   deparse_context *context);\nstatic void get_tablesample_def(TableSampleClause *tablesample,\n\t\t\t\t\tdeparse_context *context);\nstatic void get_opclass_name(Oid opclass, Oid actual_datatype,\n\t\t\t\t StringInfo buf);\nstatic Node *processIndirection(Node *node, deparse_context *context);\nstatic void printSubscripts(SubscriptingRef *aref, deparse_context *context);\nstatic char *get_relation_name(Oid relid);\nstatic char *generate_relation_or_shard_name(Oid relid, Oid distrelid,\n\t\t\t\tint64 shardid, List *namespaces);\nstatic char *generate_rte_shard_name(RangeTblEntry *rangeTableEntry);\nstatic char *generate_fragment_name(char *schemaName, char *tableName);\nstatic char *generate_function_name(Oid funcid, int nargs,\n\t\t\t\t\t   List *argnames, Oid *argtypes,\n\t\t\t\t\t   bool has_variadic, bool *use_variadic_p,\n\t\t\t\t\t   bool inGroupBy);\nstatic List *get_insert_column_names_list(List *targetList, StringInfo buf, deparse_context *context, RangeTblEntry *rte);\n\n#define only_marker(rte)  ((rte)->inh ? \"\" : \"ONLY \")\n\n\n\n/*\n * pg_get_query_def parses back one query tree, and outputs the resulting query\n * string into given buffer.\n */\nvoid\npg_get_query_def(Query *query, StringInfo buffer)\n{\n\tget_query_def(query, buffer, NIL, NULL, false, 0, WRAP_COLUMN_DEFAULT, 0);\n}\n\n/*\n * get_merged_argument_list merges both the IN and OUT arguments lists into one and\n * also eliminates the INOUT duplicates(present in both the lists). After merging both\n * the lists, it returns all the named-arguments in a list(mergedNamedArgList) along\n * with their types(mergedNamedArgTypes), final argument list(mergedArgumentList), and\n * the total number of arguments(totalArguments).\n */\nbool\nget_merged_argument_list(CallStmt *stmt, List **mergedNamedArgList,\n\t\t\t\t\t   Oid **mergedNamedArgTypes,\n\t\t\t\t\t   List **mergedArgumentList,\n\t\t\t\t\t   int *totalArguments)\n{\n\n\tOid  functionOid = stmt->funcexpr->funcid;\n\tList *namedArgList = NIL;\n\tList *finalArgumentList = NIL;\n\tOid  *finalArgTypes;\n\tOid  *argTypes = NULL;\n\tchar *argModes = NULL;\n\tchar **argNames = NULL;\n\tint  argIndex = 0;\n\n\tHeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));\n\tif (!HeapTupleIsValid(proctup))\n\t{\n\t\telog(ERROR, \"cache lookup failed for function %u\", functionOid);\n\t}\n\n\tint defArgs = get_func_arg_info(proctup, &argTypes, &argNames, &argModes);\n\tReleaseSysCache(proctup);\n\n\tif (argModes == NULL)\n\t{\n\t\t/* No OUT arguments */\n\t\treturn false;\n\t}\n\n\t/*\n\t * Passed arguments Includes IN, OUT, INOUT (in both the lists) and VARIADIC arguments,\n\t * which means INOUT arguments are double counted.\n\t */\n\tint numberOfArgs = list_length(stmt->funcexpr->args) + list_length(stmt->outargs);\n\tint totalInoutArgs = 0;\n\n\t/* Let's count INOUT arguments from the defined number of arguments */\n\tfor (argIndex=0; argIndex < defArgs; ++argIndex)\n\t{\n\t\tif (argModes[argIndex] == PROARGMODE_INOUT)\n\t\t\ttotalInoutArgs++;\n\t}\n\n\t/* Remove the duplicate INOUT counting */\n\tnumberOfArgs = numberOfArgs - totalInoutArgs;\n\tfinalArgTypes = palloc0(sizeof(Oid) * numberOfArgs);\n\n\tListCell *inArgCell = list_head(stmt->funcexpr->args);\n\tListCell *outArgCell = list_head(stmt->outargs);\n\n\tfor (argIndex=0; argIndex < numberOfArgs; ++argIndex)\n\t{\n\t\tswitch (argModes[argIndex])\n\t\t{\n\t\t\tcase PROARGMODE_IN:\n\t\t\tcase PROARGMODE_VARIADIC:\n\t\t\t{\n\t\t\t\tNode *arg = (Node *) lfirst(inArgCell);\n\n\t\t\t\tif (IsA(arg, NamedArgExpr))\n\t\t\t\t\tnamedArgList = lappend(namedArgList, ((NamedArgExpr *) arg)->name);\n\t\t\t\tfinalArgTypes[argIndex] = exprType(arg);\n\t\t\t\tfinalArgumentList = lappend(finalArgumentList, arg);\n\t\t\t\tinArgCell = lnext(stmt->funcexpr->args, inArgCell);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROARGMODE_OUT:\n\t\t\t{\n\t\t\t\tNode *arg = (Node *) lfirst(outArgCell);\n\n\t\t\t\tif (IsA(arg, NamedArgExpr))\n\t\t\t\t\tnamedArgList = lappend(namedArgList, ((NamedArgExpr *) arg)->name);\n\t\t\t\tfinalArgTypes[argIndex] = exprType(arg);\n\t\t\t\tfinalArgumentList = lappend(finalArgumentList, arg);\n\t\t\t\toutArgCell = lnext(stmt->outargs, outArgCell);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROARGMODE_INOUT:\n\t\t\t{\n\t\t\t\tNode *arg = (Node *) lfirst(inArgCell);\n\n\t\t\t\tif (IsA(arg, NamedArgExpr))\n\t\t\t\t\tnamedArgList = lappend(namedArgList, ((NamedArgExpr *) arg)->name);\n\t\t\t\tfinalArgTypes[argIndex] = exprType(arg);\n\t\t\t\tfinalArgumentList = lappend(finalArgumentList, arg);\n\t\t\t\tinArgCell = lnext(stmt->funcexpr->args, inArgCell);\n\t\t\t\toutArgCell = lnext(stmt->outargs, outArgCell);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROARGMODE_TABLE:\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\telog(ERROR, \"Unhandled procedure argument mode[%d]\", argModes[argIndex]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * After eliminating INOUT duplicates and merging OUT arguments, we now\n\t * have the final list of arguments.\n\t */\n\tif (defArgs != list_length(finalArgumentList))\n\t{\n\t\telog(ERROR, \"Insufficient number of args passed[%d] for function[%s]\",\n\t\t\t\t\t\t\t\t\t\t\tlist_length(finalArgumentList),\n\t\t\t\t\t\t\t\t\t\t\tget_func_name(functionOid));\n\t}\n\n\tif (list_length(finalArgumentList) > FUNC_MAX_ARGS)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_TOO_MANY_ARGUMENTS),\n\t\t\t\t errmsg(\"too many arguments[%d] for function[%s]\",\n\t\t\t\t\t\t\t\t\t\t\tlist_length(finalArgumentList),\n\t\t\t\t\t\t\t\t\t\t\tget_func_name(functionOid))));\n\t}\n\n\t*mergedNamedArgList =  namedArgList;\n\t*mergedNamedArgTypes = finalArgTypes;\n\t*mergedArgumentList = finalArgumentList;\n\t*totalArguments = numberOfArgs;\n\n\treturn true;\n}\n\n/*\n * pg_get_rule_expr deparses an expression and returns the result as a string.\n */\nchar *\npg_get_rule_expr(Node *expression)\n{\n\tbool showImplicitCasts = true;\n\tdeparse_context context;\n\tStringInfo buffer = makeStringInfo();\n\n\t/*\n\t * Set search_path to NIL so that all objects outside of pg_catalog will be\n\t * schema-prefixed. pg_catalog will be added automatically when we call\n\t * PushEmptySearchPath().\n\t */\n\tint saveNestLevel = PushEmptySearchPath();\n\n\tcontext.buf = buffer;\n\tcontext.namespaces = NIL;\n\tcontext.resultDesc = NULL;\n\tcontext.targetList = NIL;\n\tcontext.windowClause = NIL;\n\tcontext.varprefix = false;\n\tcontext.prettyFlags = 0;\n\tcontext.wrapColumn = WRAP_COLUMN_DEFAULT;\n\tcontext.indentLevel = 0;\n\tcontext.colNamesVisible = true;\n\tcontext.inGroupBy = false;\n\tcontext.varInOrderBy = false;\n\tcontext.distrelid = InvalidOid;\n\tcontext.shardid = INVALID_SHARD_ID;\n\n\tget_rule_expr(expression, &context, showImplicitCasts);\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n\n\treturn buffer->data;\n}\n\n/*\n * set_rtable_names: select RTE aliases to be used in printing a query\n *\n * We fill in dpns->rtable_names with a list of names that is one-for-one with\n * the already-filled dpns->rtable list.  Each RTE name is unique among those\n * in the new namespace plus any ancestor namespaces listed in\n * parent_namespaces.\n *\n * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.\n *\n * Note that this function is only concerned with relation names, not column\n * names.\n */\nstatic void\nset_rtable_names(deparse_namespace *dpns, List *parent_namespaces,\n\t\t\t\t Bitmapset *rels_used)\n{\n\tHASHCTL\t\thash_ctl;\n\tHTAB\t   *names_hash;\n\tNameHashEntry *hentry;\n\tbool\t\tfound;\n\tint\t\t\trtindex;\n\tListCell   *lc;\n\n\tdpns->rtable_names = NIL;\n\t/* nothing more to do if empty rtable */\n\tif (dpns->rtable == NIL)\n\t\treturn;\n\n\t/*\n\t * We use a hash table to hold known names, so that this process is O(N)\n\t * not O(N^2) for N names.\n\t */\n\thash_ctl.keysize = NAMEDATALEN;\n\thash_ctl.entrysize = sizeof(NameHashEntry);\n\thash_ctl.hcxt = CurrentMemoryContext;\n\tnames_hash = hash_create(\"set_rtable_names names\",\n\t\t\t\t\t\t\t list_length(dpns->rtable),\n\t\t\t\t\t\t\t &hash_ctl,\n\t\t\t\t\t\t\t HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);\n\n\t/* Preload the hash table with names appearing in parent_namespaces */\n\tforeach(lc, parent_namespaces)\n\t{\n\t\tdeparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);\n\t\tListCell   *lc2;\n\n\t\tforeach(lc2, olddpns->rtable_names)\n\t\t{\n\t\t\tchar\t   *oldname = (char *) lfirst(lc2);\n\n\t\t\tif (oldname == NULL)\n\t\t\t\tcontinue;\n\t\t\thentry = (NameHashEntry *) hash_search(names_hash,\n\t\t\t\t\t\t\t\t\t\t\t\t   oldname,\n\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t   &found);\n\t\t\t/* we do not complain about duplicate names in parent namespaces */\n\t\t\thentry->counter = 0;\n\t\t}\n\t}\n\n\t/* Now we can scan the rtable */\n\trtindex = 1;\n\tforeach(lc, dpns->rtable)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n\t\tchar\t   *refname;\n\n\t\t/* Just in case this takes an unreasonable amount of time ... */\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tif (rels_used && !bms_is_member(rtindex, rels_used))\n\t\t{\n\t\t\t/* Ignore unreferenced RTE */\n\t\t\trefname = NULL;\n\t\t}\n\t\telse if (rte->alias)\n\t\t{\n\t\t\t/* If RTE has a user-defined alias, prefer that */\n\t\t\trefname = rte->alias->aliasname;\n\t\t}\n\t\telse if (rte->rtekind == RTE_RELATION)\n\t\t{\n\t\t\t/* Use the current actual name of the relation */\n\t\t\trefname = get_rel_name(rte->relid);\n\t\t}\n\t\telse if (rte->rtekind == RTE_JOIN)\n\t\t{\n\t\t\t/* Unnamed join has no refname */\n\t\t\trefname = NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* Otherwise use whatever the parser assigned */\n\t\t\trefname = rte->eref->aliasname;\n\t\t}\n\n\t\t/*\n\t\t * If the selected name isn't unique, append digits to make it so, and\n\t\t * make a new hash entry for it once we've got a unique name.  For a\n\t\t * very long input name, we might have to truncate to stay within\n\t\t * NAMEDATALEN.\n\t\t */\n\t\tif (refname)\n\t\t{\n\t\t\thentry = (NameHashEntry *) hash_search(names_hash,\n\t\t\t\t\t\t\t\t\t\t\t\t   refname,\n\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t   &found);\n\t\t\tif (found)\n\t\t\t{\n\t\t\t\t/* Name already in use, must choose a new one */\n\t\t\t\tint\t\t\trefnamelen = strlen(refname);\n\t\t\t\tchar\t   *modname = (char *) palloc(refnamelen + 16);\n\t\t\t\tNameHashEntry *hentry2;\n\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\thentry->counter++;\n\t\t\t\t\tfor (;;)\n\t\t\t\t\t{\n\t\t\t\t\t\tmemcpy(modname, refname, refnamelen);\n\t\t\t\t\t\tsprintf(modname + refnamelen, \"_%d\", hentry->counter);\n\t\t\t\t\t\tif (strlen(modname) < NAMEDATALEN)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t/* drop chars from refname to keep all the digits */\n\t\t\t\t\t\trefnamelen = pg_mbcliplen(refname, refnamelen,\n\t\t\t\t\t\t\t\t\t\t\t\t  refnamelen - 1);\n\t\t\t\t\t}\n\t\t\t\t\thentry2 = (NameHashEntry *) hash_search(names_hash,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmodname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tHASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t&found);\n\t\t\t\t} while (found);\n\t\t\t\thentry2->counter = 0;\t/* init new hash entry */\n\t\t\t\trefname = modname;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* Name not previously used, need only initialize hentry */\n\t\t\t\thentry->counter = 0;\n\t\t\t}\n\t\t}\n\n\t\tdpns->rtable_names = lappend(dpns->rtable_names, refname);\n\t\trtindex++;\n\t}\n\n\thash_destroy(names_hash);\n}\n\n/*\n * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree\n *\n * For convenience, this is defined to initialize the deparse_namespace struct\n * from scratch.\n */\nstatic void\nset_deparse_for_query(deparse_namespace *dpns, Query *query,\n\t\t\t\t\t  List *parent_namespaces)\n{\n\tListCell   *lc;\n\tListCell   *lc2;\n\n\t/* Initialize *dpns and fill rtable/ctes links */\n\tmemset(dpns, 0, sizeof(deparse_namespace));\n\tdpns->rtable = query->rtable;\n\tdpns->subplans = NIL;\n\tdpns->ctes = query->cteList;\n\tdpns->appendrels = NULL;\n\n\t/* Assign a unique relation alias to each RTE */\n\tset_rtable_names(dpns, parent_namespaces, NULL);\n\n\t/* Initialize dpns->rtable_columns to contain zeroed structs */\n\tdpns->rtable_columns = NIL;\n\twhile (list_length(dpns->rtable_columns) < list_length(dpns->rtable))\n\t\tdpns->rtable_columns = lappend(dpns->rtable_columns,\n\t\t\t\t\t\t\t\t\t   palloc0(sizeof(deparse_columns)));\n\n\t/* If it's a utility query, it won't have a jointree */\n\tif (query->jointree)\n\t{\n\t\t/* Detect whether global uniqueness of USING names is needed */\n\t\tdpns->unique_using =\n\t\t\thas_dangerous_join_using(dpns, (Node *) query->jointree);\n\n\t\t/*\n\t\t * Select names for columns merged by USING, via a recursive pass over\n\t\t * the query jointree.\n\t\t */\n\t\tset_using_names(dpns, (Node *) query->jointree, NIL);\n\t}\n\n\t/*\n\t * Now assign remaining column aliases for each RTE.  We do this in a\n\t * linear scan of the rtable, so as to process RTEs whether or not they\n\t * are in the jointree (we mustn't miss NEW.*, INSERT target relations,\n\t * etc).  JOIN RTEs must be processed after their children, but this is\n\t * okay because they appear later in the rtable list than their children\n\t * (cf Asserts in identify_join_columns()).\n\t */\n\tforboth(lc, dpns->rtable, lc2, dpns->rtable_columns)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n\t\tdeparse_columns *colinfo = (deparse_columns *) lfirst(lc2);\n\n\t\tif (rte->rtekind == RTE_JOIN)\n\t\t\tset_join_column_names(dpns, rte, colinfo);\n\t\telse\n\t\t\tset_relation_column_names(dpns, rte, colinfo);\n\t}\n}\n\n/*\n * has_dangerous_join_using: search jointree for unnamed JOIN USING\n *\n * Merged columns of a JOIN USING may act differently from either of the input\n * columns, either because they are merged with COALESCE (in a FULL JOIN) or\n * because an implicit coercion of the underlying input column is required.\n * In such a case the column must be referenced as a column of the JOIN not as\n * a column of either input.  And this is problematic if the join is unnamed\n * (alias-less): we cannot qualify the column's name with an RTE name, since\n * there is none.  (Forcibly assigning an alias to the join is not a solution,\n * since that will prevent legal references to tables below the join.)\n * To ensure that every column in the query is unambiguously referenceable,\n * we must assign such merged columns names that are globally unique across\n * the whole query, aliasing other columns out of the way as necessary.\n *\n * Because the ensuing re-aliasing is fairly damaging to the readability of\n * the query, we don't do this unless we have to.  So, we must pre-scan\n * the join tree to see if we have to, before starting set_using_names().\n */\nstatic bool\nhas_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)\n{\n\tif (IsA(jtnode, RangeTblRef))\n\t{\n\t\t/* nothing to do here */\n\t}\n\telse if (IsA(jtnode, FromExpr))\n\t{\n\t\tFromExpr   *f = (FromExpr *) jtnode;\n\t\tListCell   *lc;\n\n\t\tforeach(lc, f->fromlist)\n\t\t{\n\t\t\tif (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))\n\t\t\t\treturn true;\n\t\t}\n\t}\n\telse if (IsA(jtnode, JoinExpr))\n\t{\n\t\tJoinExpr   *j = (JoinExpr *) jtnode;\n\n\t\t/* Is it an unnamed JOIN with USING? */\n\t\tif (j->alias == NULL && j->usingClause)\n\t\t{\n\t\t\t/*\n\t\t\t * Yes, so check each join alias var to see if any of them are not\n\t\t\t * simple references to underlying columns.  If so, we have a\n\t\t\t * dangerous situation and must pick unique aliases.\n\t\t\t */\n\t\t\tRangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);\n\n\t\t\t/* We need only examine the merged columns */\n\t\t\tfor (int i = 0; i < jrte->joinmergedcols; i++)\n\t\t\t{\n\t\t\t\tNode\t   *aliasvar = list_nth(jrte->joinaliasvars, i);\n\n\t\t\t\tif (!IsA(aliasvar, Var))\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t/* Nope, but inspect children */\n\t\tif (has_dangerous_join_using(dpns, j->larg))\n\t\t\treturn true;\n\t\tif (has_dangerous_join_using(dpns, j->rarg))\n\t\t\treturn true;\n\t}\n\telse\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(jtnode));\n\treturn false;\n}\n\n/*\n * set_using_names: select column aliases to be used for merged USING columns\n *\n * We do this during a recursive descent of the query jointree.\n * dpns->unique_using must already be set to determine the global strategy.\n *\n * Column alias info is saved in the dpns->rtable_columns list, which is\n * assumed to be filled with pre-zeroed deparse_columns structs.\n *\n * parentUsing is a list of all USING aliases assigned in parent joins of\n * the current jointree node.  (The passed-in list must not be modified.)\n */\nstatic void\nset_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)\n{\n\tif (IsA(jtnode, RangeTblRef))\n\t{\n\t\t/* nothing to do now */\n\t}\n\telse if (IsA(jtnode, FromExpr))\n\t{\n\t\tFromExpr   *f = (FromExpr *) jtnode;\n\t\tListCell   *lc;\n\n\t\tforeach(lc, f->fromlist)\n\t\t\tset_using_names(dpns, (Node *) lfirst(lc), parentUsing);\n\t}\n\telse if (IsA(jtnode, JoinExpr))\n\t{\n\t\tJoinExpr   *j = (JoinExpr *) jtnode;\n\t\tRangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);\n\t\tdeparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);\n\t\tint\t\t   *leftattnos;\n\t\tint\t\t   *rightattnos;\n\t\tdeparse_columns *leftcolinfo;\n\t\tdeparse_columns *rightcolinfo;\n\t\tint\t\t\ti;\n\t\tListCell   *lc;\n\n\t\t/* Get info about the shape of the join */\n\t\tidentify_join_columns(j, rte, colinfo);\n\t\tleftattnos = colinfo->leftattnos;\n\t\trightattnos = colinfo->rightattnos;\n\n\t\t/* Look up the not-yet-filled-in child deparse_columns structs */\n\t\tleftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);\n\t\trightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);\n\n\t\t/*\n\t\t * If this join is unnamed, then we cannot substitute new aliases at\n\t\t * this level, so any name requirements pushed down to here must be\n\t\t * pushed down again to the children.\n\t\t */\n\t\tif (rte->alias == NULL)\n\t\t{\n\t\t\tfor (i = 0; i < colinfo->num_cols; i++)\n\t\t\t{\n\t\t\t\tchar\t   *colname = colinfo->colnames[i];\n\n\t\t\t\tif (colname == NULL)\n\t\t\t\t\tcontinue;\n\n\t\t\t\t/* Push down to left column, unless it's a system column */\n\t\t\t\tif (leftattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(leftcolinfo, leftattnos[i]);\n\t\t\t\t\tleftcolinfo->colnames[leftattnos[i] - 1] = colname;\n\t\t\t\t}\n\n\t\t\t\t/* Same on the righthand side */\n\t\t\t\tif (rightattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(rightcolinfo, rightattnos[i]);\n\t\t\t\t\trightcolinfo->colnames[rightattnos[i] - 1] = colname;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * If there's a USING clause, select the USING column names and push\n\t\t * those names down to the children.  We have two strategies:\n\t\t *\n\t\t * If dpns->unique_using is true, we force all USING names to be\n\t\t * unique across the whole query level.  In principle we'd only need\n\t\t * the names of dangerous USING columns to be globally unique, but to\n\t\t * safely assign all USING names in a single pass, we have to enforce\n\t\t * the same uniqueness rule for all of them.  However, if a USING\n\t\t * column's name has been pushed down from the parent, we should use\n\t\t * it as-is rather than making a uniqueness adjustment.  This is\n\t\t * necessary when we're at an unnamed join, and it creates no risk of\n\t\t * ambiguity.  Also, if there's a user-written output alias for a\n\t\t * merged column, we prefer to use that rather than the input name;\n\t\t * this simplifies the logic and seems likely to lead to less aliasing\n\t\t * overall.\n\t\t *\n\t\t * If dpns->unique_using is false, we only need USING names to be\n\t\t * unique within their own join RTE.  We still need to honor\n\t\t * pushed-down names, though.\n\t\t *\n\t\t * Though significantly different in results, these two strategies are\n\t\t * implemented by the same code, with only the difference of whether\n\t\t * to put assigned names into dpns->using_names.\n\t\t */\n\t\tif (j->usingClause)\n\t\t{\n\t\t\t/* Copy the input parentUsing list so we don't modify it */\n\t\t\tparentUsing = list_copy(parentUsing);\n\n\t\t\t/* USING names must correspond to the first join output columns */\n\t\t\texpand_colnames_array_to(colinfo, list_length(j->usingClause));\n\t\t\ti = 0;\n\t\t\tforeach(lc, j->usingClause)\n\t\t\t{\n\t\t\t\tchar\t   *colname = strVal(lfirst(lc));\n\n\t\t\t\t/* Assert it's a merged column */\n\t\t\t\tAssert(leftattnos[i] != 0 && rightattnos[i] != 0);\n\n\t\t\t\t/* Adopt passed-down name if any, else select unique name */\n\t\t\t\tif (colinfo->colnames[i] != NULL)\n\t\t\t\t\tcolname = colinfo->colnames[i];\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* Prefer user-written output alias if any */\n\t\t\t\t\tif (rte->alias && i < list_length(rte->alias->colnames))\n\t\t\t\t\t\tcolname = strVal(list_nth(rte->alias->colnames, i));\n\t\t\t\t\t/* Make it appropriately unique */\n\t\t\t\t\tcolname = make_colname_unique(colname, dpns, colinfo);\n\t\t\t\t\tif (dpns->unique_using)\n\t\t\t\t\t\tdpns->using_names = lappend(dpns->using_names,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolname);\n\t\t\t\t\t/* Save it as output column name, too */\n\t\t\t\t\tcolinfo->colnames[i] = colname;\n\t\t\t\t}\n\n\t\t\t\t/* Remember selected names for use later */\n\t\t\t\tcolinfo->usingNames = lappend(colinfo->usingNames, colname);\n\t\t\t\tparentUsing = lappend(parentUsing, colname);\n\n\t\t\t\t/* Push down to left column, unless it's a system column */\n\t\t\t\tif (leftattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(leftcolinfo, leftattnos[i]);\n\t\t\t\t\tleftcolinfo->colnames[leftattnos[i] - 1] = colname;\n\t\t\t\t}\n\n\t\t\t\t/* Same on the righthand side */\n\t\t\t\tif (rightattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(rightcolinfo, rightattnos[i]);\n\t\t\t\t\trightcolinfo->colnames[rightattnos[i] - 1] = colname;\n\t\t\t\t}\n\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\n\t\t/* Mark child deparse_columns structs with correct parentUsing info */\n\t\tleftcolinfo->parentUsing = parentUsing;\n\t\trightcolinfo->parentUsing = parentUsing;\n\n\t\t/* Now recursively assign USING column names in children */\n\t\tset_using_names(dpns, j->larg, parentUsing);\n\t\tset_using_names(dpns, j->rarg, parentUsing);\n\t}\n\telse\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(jtnode));\n}\n\n/*\n * set_relation_column_names: select column aliases for a non-join RTE\n *\n * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.\n * If any colnames entries are already filled in, those override local\n * choices.\n */\nstatic void\nset_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,\n\t\t\t\t\t\t  deparse_columns *colinfo)\n{\n\tint\t\t\tncolumns;\n\tchar\t  **real_colnames;\n\tbool\t\tchanged_any;\n\tbool \t\thas_anonymous;\n\tint\t\t\tnoldcolumns;\n\tint\t\t\ti;\n\tint\t\t\tj;\n\n\t/*\n\t * Construct an array of the current \"real\" column names of the RTE.\n\t * real_colnames[] will be indexed by physical column number, with NULL\n\t * entries for dropped columns.\n\t */\n\tif (rte->rtekind == RTE_RELATION ||\n        GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\t/* Relation --- look to the system catalogs for up-to-date info */\n\t\tRelation\trel;\n\t\tTupleDesc\ttupdesc;\n\n\t\trel = relation_open(rte->relid, AccessShareLock);\n\t\ttupdesc = RelationGetDescr(rel);\n\n\t\tncolumns = tupdesc->natts;\n\t\treal_colnames = (char **) palloc(ncolumns * sizeof(char *));\n\n\t\tfor (i = 0; i < ncolumns; i++)\n\t\t{\n\t\t\tForm_pg_attribute attr = TupleDescAttr(tupdesc, i);\n\n\t\t\tif (attr->attisdropped)\n\t\t\t\treal_colnames[i] = NULL;\n\t\t\telse\n\t\t\t\treal_colnames[i] = pstrdup(NameStr(attr->attname));\n\t\t}\n\t\trelation_close(rel, AccessShareLock);\n\t}\n\telse\n\t{\n\t\t/* Otherwise get the column names from eref or expandRTE() */\n\t\tList\t   *colnames;\n\t\tListCell   *lc;\n\n\t\t/*\n\t\t * Functions returning composites have the annoying property that some\n\t\t * of the composite type's columns might have been dropped since the\n\t\t * query was parsed.  If possible, use expandRTE() to handle that\n\t\t * case, since it has the tedious logic needed to find out about\n\t\t * dropped columns.  However, if we're explaining a plan, then we\n\t\t * don't have rte->functions because the planner thinks that won't be\n\t\t * needed later, and that breaks expandRTE().  So in that case we have\n\t\t * to rely on rte->eref, which may lead us to report a dropped\n\t\t * column's old name; that seems close enough for EXPLAIN's purposes.\n\t\t *\n\t\t * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,\n\t\t * which should be sufficiently up-to-date: no other RTE types can\n\t\t * have columns get dropped from under them after parsing.\n\t\t */\n\t\tif (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)\n\t\t{\n\t\t\t/* Since we're not creating Vars, rtindex etc. don't matter */\n\t\t\texpandRTE(rte, 1, 0, -1, true /* include dropped */ ,\n\t\t\t\t\t  &colnames, NULL);\n\t\t}\n\t\telse\n\t\t\tcolnames = rte->eref->colnames;\n\n\t\tncolumns = list_length(colnames);\n\t\treal_colnames = (char **) palloc(ncolumns * sizeof(char *));\n\n\t\ti = 0;\n\t\tforeach(lc, colnames)\n\t\t{\n\t\t\t/*\n\t\t\t * If the column name we find here is an empty string, then it's a\n\t\t\t * dropped column, so change to NULL.\n\t\t\t */\n\t\t\tchar\t   *cname = strVal(lfirst(lc));\n\n\t\t\tif (cname[0] == '\\0')\n\t\t\t\tcname = NULL;\n\t\t\treal_colnames[i] = cname;\n\t\t\ti++;\n\t\t}\n\t}\n\n\t/*\n\t * Ensure colinfo->colnames has a slot for each column.  (It could be long\n\t * enough already, if we pushed down a name for the last column.)  Note:\n\t * it's possible that there are now more columns than there were when the\n\t * query was parsed, ie colnames could be longer than rte->eref->colnames.\n\t * We must assign unique aliases to the new columns too, else there could\n\t * be unresolved conflicts when the view/rule is reloaded.\n\t */\n\texpand_colnames_array_to(colinfo, ncolumns);\n\tAssert(colinfo->num_cols == ncolumns);\n\n\t/*\n\t * Make sufficiently large new_colnames and is_new_col arrays, too.\n\t *\n\t * Note: because we leave colinfo->num_new_cols zero until after the loop,\n\t * colname_is_unique will not consult that array, which is fine because it\n\t * would only be duplicate effort.\n\t */\n\tcolinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));\n\tcolinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));\n\n\t/*\n\t * Scan the columns, select a unique alias for each one, and store it in\n\t * colinfo->colnames and colinfo->new_colnames.  The former array has NULL\n\t * entries for dropped columns, the latter omits them.  Also mark\n\t * new_colnames entries as to whether they are new since parse time; this\n\t * is the case for entries beyond the length of rte->eref->colnames.\n\t */\n\tnoldcolumns = list_length(rte->eref->colnames);\n\tchanged_any = false;\n\thas_anonymous = false;\n\tj = 0;\n\tfor (i = 0; i < ncolumns; i++)\n\t{\n\t\tchar\t   *real_colname = real_colnames[i];\n\t\tchar\t   *colname = colinfo->colnames[i];\n\n\t\t/* Skip dropped columns */\n\t\tif (real_colname == NULL)\n\t\t{\n\t\t\tAssert(colname == NULL);\t/* colnames[i] is already NULL */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* If alias already assigned, that's what to use */\n\t\tif (colname == NULL)\n\t\t{\n\t\t\t/* If user wrote an alias, prefer that over real column name */\n\t\t\tif (rte->alias && i < list_length(rte->alias->colnames))\n\t\t\t\tcolname = strVal(list_nth(rte->alias->colnames, i));\n\t\t\telse\n\t\t\t\tcolname = real_colname;\n\n\t\t\t/* Unique-ify and insert into colinfo */\n\t\t\tcolname = make_colname_unique(colname, dpns, colinfo);\n\n\t\t\tcolinfo->colnames[i] = colname;\n\t\t}\n\n\t\t/* Put names of non-dropped columns in new_colnames[] too */\n\t\tcolinfo->new_colnames[j] = colname;\n\t\t/* And mark them as new or not */\n\t\tcolinfo->is_new_col[j] = (i >= noldcolumns);\n\t\tj++;\n\n\t\t/* Remember if any assigned aliases differ from \"real\" name */\n\t\tif (!changed_any && strcmp(colname, real_colname) != 0)\n\t\t\tchanged_any = true;\n\n\t\t/*\n\t\t * Remember if there is a reference to an anonymous column as named by\n\t\t * char * FigureColname(Node *node)\n\t\t */\n\t\tif (!has_anonymous && strcmp(real_colname, \"?column?\") == 0)\n\t\t\thas_anonymous = true;\n\t}\n\n\t/*\n\t * Set correct length for new_colnames[] array.  (Note: if columns have\n\t * been added, colinfo->num_cols includes them, which is not really quite\n\t * right but is harmless, since any new columns must be at the end where\n\t * they won't affect varattnos of pre-existing columns.)\n\t */\n\tcolinfo->num_new_cols = j;\n\n\t/*\n\t * For a relation RTE, we need only print the alias column names if any\n\t * are different from the underlying \"real\" names.  For a function RTE,\n\t * always emit a complete column alias list; this is to protect against\n\t * possible instability of the default column names (eg, from altering\n\t * parameter names).  For tablefunc RTEs, we never print aliases, because\n\t * the column names are part of the clause itself.  For other RTE types,\n\t * print if we changed anything OR if there were user-written column\n\t * aliases (since the latter would be part of the underlying \"reality\").\n\t */\n\tif (rte->rtekind == RTE_RELATION)\n\t\tcolinfo->printaliases = changed_any;\n\telse if (rte->rtekind == RTE_FUNCTION)\n\t\tcolinfo->printaliases = true;\n\telse if (rte->rtekind == RTE_TABLEFUNC)\n\t\tcolinfo->printaliases = false;\n\telse if (rte->alias && rte->alias->colnames != NIL)\n\t\tcolinfo->printaliases = true;\n\telse\n\t\tcolinfo->printaliases = changed_any || has_anonymous;\n}\n\n/*\n * set_join_column_names: select column aliases for a join RTE\n *\n * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.\n * If any colnames entries are already filled in, those override local\n * choices.  Also, names for USING columns were already chosen by\n * set_using_names().  We further expect that column alias selection has been\n * completed for both input RTEs.\n */\nstatic void\nset_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,\n\t\t\t\t\t  deparse_columns *colinfo)\n{\n\tdeparse_columns *leftcolinfo;\n\tdeparse_columns *rightcolinfo;\n\tbool\t\tchanged_any;\n\tint\t\t\tnoldcolumns;\n\tint\t\t\tnnewcolumns;\n\tBitmapset  *leftmerged = NULL;\n\tBitmapset  *rightmerged = NULL;\n\tint\t\t\ti;\n\tint\t\t\tj;\n\tint\t\t\tic;\n\tint\t\t\tjc;\n\n\t/* Look up the previously-filled-in child deparse_columns structs */\n\tleftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);\n\trightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);\n\n\t/*\n\t * Ensure colinfo->colnames has a slot for each column.  (It could be long\n\t * enough already, if we pushed down a name for the last column.)  Note:\n\t * it's possible that one or both inputs now have more columns than there\n\t * were when the query was parsed, but we'll deal with that below.  We\n\t * only need entries in colnames for pre-existing columns.\n\t */\n\tnoldcolumns = list_length(rte->eref->colnames);\n\texpand_colnames_array_to(colinfo, noldcolumns);\n\tAssert(colinfo->num_cols == noldcolumns);\n\n\t/*\n\t * Scan the join output columns, select an alias for each one, and store\n\t * it in colinfo->colnames.  If there are USING columns, set_using_names()\n\t * already selected their names, so we can start the loop at the first\n\t * non-merged column.\n\t */\n\tchanged_any = false;\n\tfor (i = list_length(colinfo->usingNames); i < noldcolumns; i++)\n\t{\n\t\tchar\t   *colname = colinfo->colnames[i];\n\t\tchar\t   *real_colname;\n\n\t\t/* Join column must refer to at least one input column */\n\t\tAssert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);\n\n\t\t/* Get the child column name */\n\t\tif (colinfo->leftattnos[i] > 0)\n\t\t\treal_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];\n\t\telse if (colinfo->rightattnos[i] > 0)\n\t\t\treal_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];\n\t\telse\n\t\t{\n\t\t\t/* We're joining system columns --- use eref name */\n\t\t\treal_colname = strVal(list_nth(rte->eref->colnames, i));\n\t\t}\n\t\t\t/* If child col has been dropped, no need to assign a join colname */\n\t\tif (real_colname == NULL)\n\t\t{\n\t\t\tcolinfo->colnames[i] = NULL;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* In an unnamed join, just report child column names as-is */\n\t\tif (rte->alias == NULL)\n\t\t{\n\t\t\tcolinfo->colnames[i] = real_colname;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* If alias already assigned, that's what to use */\n\t\tif (colname == NULL)\n\t\t{\n\t\t\t/* If user wrote an alias, prefer that over real column name */\n\t\t\tif (rte->alias && i < list_length(rte->alias->colnames))\n\t\t\t\tcolname = strVal(list_nth(rte->alias->colnames, i));\n\t\t\telse\n\t\t\t\tcolname = real_colname;\n\n\t\t\t/* Unique-ify and insert into colinfo */\n\t\t\tcolname = make_colname_unique(colname, dpns, colinfo);\n\n\t\t\tcolinfo->colnames[i] = colname;\n\t\t}\n\n\t\t/* Remember if any assigned aliases differ from \"real\" name */\n\t\tif (!changed_any && strcmp(colname, real_colname) != 0)\n\t\t\tchanged_any = true;\n\t}\n\n\t/*\n\t * Calculate number of columns the join would have if it were re-parsed\n\t * now, and create storage for the new_colnames and is_new_col arrays.\n\t *\n\t * Note: colname_is_unique will be consulting new_colnames[] during the\n\t * loops below, so its not-yet-filled entries must be zeroes.\n\t */\n\tnnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -\n\t\tlist_length(colinfo->usingNames);\n\tcolinfo->num_new_cols = nnewcolumns;\n\tcolinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));\n\tcolinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));\n\n\t/*\n\t * Generating the new_colnames array is a bit tricky since any new columns\n\t * added since parse time must be inserted in the right places.  This code\n\t * must match the parser, which will order a join's columns as merged\n\t * columns first (in USING-clause order), then non-merged columns from the\n\t * left input (in attnum order), then non-merged columns from the right\n\t * input (ditto).  If one of the inputs is itself a join, its columns will\n\t * be ordered according to the same rule, which means newly-added columns\n\t * might not be at the end.  We can figure out what's what by consulting\n\t * the leftattnos and rightattnos arrays plus the input is_new_col arrays.\n\t *\n\t * In these loops, i indexes leftattnos/rightattnos (so it's join varattno\n\t * less one), j indexes new_colnames/is_new_col, and ic/jc have similar\n\t * meanings for the current child RTE.\n\t */\n\n\t/* Handle merged columns; they are first and can't be new */\n\ti = j = 0;\n\twhile (i < noldcolumns &&\n\t\t   colinfo->leftattnos[i] != 0 &&\n\t\t   colinfo->rightattnos[i] != 0)\n\t{\n\t\t/* column name is already determined and known unique */\n\t\tcolinfo->new_colnames[j] = colinfo->colnames[i];\n\t\tcolinfo->is_new_col[j] = false;\n\n\t\t/* build bitmapsets of child attnums of merged columns */\n\t\tif (colinfo->leftattnos[i] > 0)\n\t\t\tleftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);\n\t\tif (colinfo->rightattnos[i] > 0)\n\t\t\trightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);\n\n\t\ti++, j++;\n\t}\n\n\t/* Handle non-merged left-child columns */\n\tic = 0;\n\tfor (jc = 0; jc < leftcolinfo->num_new_cols; jc++)\n\t{\n\t\tchar\t   *child_colname = leftcolinfo->new_colnames[jc];\n\n\t\tif (!leftcolinfo->is_new_col[jc])\n\t\t{\n\t\t\t/* Advance ic to next non-dropped old column of left child */\n\t\t\twhile (ic < leftcolinfo->num_cols &&\n\t\t\t\t   leftcolinfo->colnames[ic] == NULL)\n\t\t\t\tic++;\n\t\t\tAssert(ic < leftcolinfo->num_cols);\n\t\t\tic++;\n\t\t\t/* If it is a merged column, we already processed it */\n\t\t\tif (bms_is_member(ic, leftmerged))\n\t\t\t\tcontinue;\n\t\t\t/* Else, advance i to the corresponding existing join column */\n\t\t\twhile (i < colinfo->num_cols &&\n\t\t\t\t   colinfo->colnames[i] == NULL)\n\t\t\t\ti++;\n\t\t\tAssert(i < colinfo->num_cols);\n\t\t\tAssert(ic == colinfo->leftattnos[i]);\n\t\t\t/* Use the already-assigned name of this column */\n\t\t\tcolinfo->new_colnames[j] = colinfo->colnames[i];\n\t\t\ti++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Unique-ify the new child column name and assign, unless we're\n\t\t\t * in an unnamed join, in which case just copy\n\t\t\t */\n\t\t\tif (rte->alias != NULL)\n\t\t\t{\n\t\t\t\tcolinfo->new_colnames[j] =\n\t\t\t\t\tmake_colname_unique(child_colname, dpns, colinfo);\n\t\t\t\tif (!changed_any &&\n\t\t\t\t\tstrcmp(colinfo->new_colnames[j], child_colname) != 0)\n\t\t\t\t\tchanged_any = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcolinfo->new_colnames[j] = child_colname;\n\t\t}\n\n\t\tcolinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];\n\t\tj++;\n\t}\n\n\t/* Handle non-merged right-child columns in exactly the same way */\n\tic = 0;\n\tfor (jc = 0; jc < rightcolinfo->num_new_cols; jc++)\n\t{\n\t\tchar\t   *child_colname = rightcolinfo->new_colnames[jc];\n\n\t\tif (!rightcolinfo->is_new_col[jc])\n\t\t{\n\t\t\t/* Advance ic to next non-dropped old column of right child */\n\t\t\twhile (ic < rightcolinfo->num_cols &&\n\t\t\t\t   rightcolinfo->colnames[ic] == NULL)\n\t\t\t\tic++;\n\t\t\tAssert(ic < rightcolinfo->num_cols);\n\t\t\tic++;\n\t\t\t/* If it is a merged column, we already processed it */\n\t\t\tif (bms_is_member(ic, rightmerged))\n\t\t\t\tcontinue;\n\t\t\t/* Else, advance i to the corresponding existing join column */\n\t\t\twhile (i < colinfo->num_cols &&\n\t\t\t\t   colinfo->colnames[i] == NULL)\n\t\t\t\ti++;\n\t\t\tAssert(i < colinfo->num_cols);\n\t\t\tAssert(ic == colinfo->rightattnos[i]);\n\t\t\t/* Use the already-assigned name of this column */\n\t\t\tcolinfo->new_colnames[j] = colinfo->colnames[i];\n\t\t\ti++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Unique-ify the new child column name and assign, unless we're\n\t\t\t * in an unnamed join, in which case just copy\n\t\t\t */\n\t\t\tif (rte->alias != NULL)\n\t\t\t{\n\t\t\t\tcolinfo->new_colnames[j] =\n\t\t\t\t\tmake_colname_unique(child_colname, dpns, colinfo);\n\t\t\t\tif (!changed_any &&\n\t\t\t\t\tstrcmp(colinfo->new_colnames[j], child_colname) != 0)\n\t\t\t\t\tchanged_any = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcolinfo->new_colnames[j] = child_colname;\n\t\t}\n\n\t\tcolinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];\n\t\tj++;\n\t}\n\n\t/* Assert we processed the right number of columns */\n#ifdef USE_ASSERT_CHECKING\n\tfor (int col_index = 0; col_index < colinfo->num_cols; col_index++)\n\t{\n\t\t/*\n\t\t * In the above processing-loops, \"i\" advances only if\n\t\t * the column is not new, check if this is a new column.\n\t\t */\n\t\tif (colinfo->is_new_col[col_index])\n\t\t\ti++;\n\t}\n\tAssert(j == nnewcolumns);\n#endif\n\n\t/*\n\t * For a named join, print column aliases if we changed any from the child\n\t * names.  Unnamed joins cannot print aliases.\n\t */\n\tif (rte->alias != NULL)\n\t\tcolinfo->printaliases = changed_any;\n\telse\n\t\tcolinfo->printaliases = false;\n}\n\n/*\n * colname_is_unique: is colname distinct from already-chosen column names?\n *\n * dpns is query-wide info, colinfo is for the column's RTE\n */\nstatic bool\ncolname_is_unique(const char *colname, deparse_namespace *dpns,\n\t\t\t\t  deparse_columns *colinfo)\n{\n\tint\t\t\ti;\n\tListCell   *lc;\n\n\t/* Check against already-assigned column aliases within RTE */\n\tfor (i = 0; i < colinfo->num_cols; i++)\n\t{\n\t\tchar\t   *oldname = colinfo->colnames[i];\n\n\t\tif (oldname && strcmp(oldname, colname) == 0)\n\t\t\treturn false;\n\t}\n\n\t/*\n\t * If we're building a new_colnames array, check that too (this will be\n\t * partially but not completely redundant with the previous checks)\n\t */\n\tfor (i = 0; i < colinfo->num_new_cols; i++)\n\t{\n\t\tchar\t   *oldname = colinfo->new_colnames[i];\n\n\t\tif (oldname && strcmp(oldname, colname) == 0)\n\t\t\treturn false;\n\t}\n\n\t/* Also check against USING-column names that must be globally unique */\n\tforeach(lc, dpns->using_names)\n\t{\n\t\tchar\t   *oldname = (char *) lfirst(lc);\n\n\t\tif (strcmp(oldname, colname) == 0)\n\t\t\treturn false;\n\t}\n\n\t/* Also check against names already assigned for parent-join USING cols */\n\tforeach(lc, colinfo->parentUsing)\n\t{\n\t\tchar\t   *oldname = (char *) lfirst(lc);\n\n\t\tif (strcmp(oldname, colname) == 0)\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n/*\n * make_colname_unique: modify colname if necessary to make it unique\n *\n * dpns is query-wide info, colinfo is for the column's RTE\n */\nstatic char *\nmake_colname_unique(char *colname, deparse_namespace *dpns,\n\t\t\t\t\tdeparse_columns *colinfo)\n{\n\t/*\n\t * If the selected name isn't unique, append digits to make it so.  For a\n\t * very long input name, we might have to truncate to stay within\n\t * NAMEDATALEN.\n\t */\n\tif (!colname_is_unique(colname, dpns, colinfo))\n\t{\n\t\tint\t\t\tcolnamelen = strlen(colname);\n\t\tchar\t   *modname = (char *) palloc(colnamelen + 16);\n\t\tint\t\t\ti = 0;\n\n\t\tdo\n\t\t{\n\t\t\ti++;\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\tmemcpy(modname, colname, colnamelen);\n\t\t\t\tsprintf(modname + colnamelen, \"_%d\", i);\n\t\t\t\tif (strlen(modname) < NAMEDATALEN)\n\t\t\t\t\tbreak;\n\t\t\t\t/* drop chars from colname to keep all the digits */\n\t\t\t\tcolnamelen = pg_mbcliplen(colname, colnamelen,\n\t\t\t\t\t\t\t\t\t\t  colnamelen - 1);\n\t\t\t}\n\t\t} while (!colname_is_unique(modname, dpns, colinfo));\n\t\tcolname = modname;\n\t}\n\treturn colname;\n}\n\n/*\n * expand_colnames_array_to: make colinfo->colnames at least n items long\n *\n * Any added array entries are initialized to zero.\n */\nstatic void\nexpand_colnames_array_to(deparse_columns *colinfo, int n)\n{\n\tif (n > colinfo->num_cols)\n\t{\n\t\tif (colinfo->colnames == NULL)\n\t\t\tcolinfo->colnames = palloc0_array(char *, n);\n\t\telse\n\t\t{\n\t\t\tcolinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);\n\t\t}\n\t\tcolinfo->num_cols = n;\n\t}\n}\n\n/*\n * identify_join_columns: figure out where columns of a join come from\n *\n * Fills the join-specific fields of the colinfo struct, except for\n * usingNames which is filled later.\n */\nstatic void\nidentify_join_columns(JoinExpr *j, RangeTblEntry *jrte,\n\t\t\t\t\t  deparse_columns *colinfo)\n{\n\tint\t\t\tnumjoincols;\n\tint\t\t\tjcolno;\n\tint\t\t\trcolno;\n\tListCell   *lc;\n\n\t/* Extract left/right child RT indexes */\n\tif (IsA(j->larg, RangeTblRef))\n\t\tcolinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;\n\telse if (IsA(j->larg, JoinExpr))\n\t\tcolinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;\n\telse\n\t\telog(ERROR, \"unrecognized node type in jointree: %d\",\n\t\t\t (int) nodeTag(j->larg));\n\tif (IsA(j->rarg, RangeTblRef))\n\t\tcolinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;\n\telse if (IsA(j->rarg, JoinExpr))\n\t\tcolinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;\n\telse\n\t\telog(ERROR, \"unrecognized node type in jointree: %d\",\n\t\t\t (int) nodeTag(j->rarg));\n\n\t/* Assert children will be processed earlier than join in second pass */\n\tAssert(colinfo->leftrti < j->rtindex);\n\tAssert(colinfo->rightrti < j->rtindex);\n\n\t/* Initialize result arrays with zeroes */\n\tnumjoincols = list_length(jrte->joinaliasvars);\n\tAssert(numjoincols == list_length(jrte->eref->colnames));\n\tcolinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));\n\tcolinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));\n\n\t/*\n\t * Deconstruct RTE's joinleftcols/joinrightcols into desired format.\n\t * Recall that the column(s) merged due to USING are the first column(s)\n\t * of the join output.  We need not do anything special while scanning\n\t * joinleftcols, but while scanning joinrightcols we must distinguish\n\t * merged from unmerged columns.\n\t */\n\tjcolno = 0;\n\tforeach(lc, jrte->joinleftcols)\n\t{\n\t\tint\t\t\tleftattno = lfirst_int(lc);\n\n\t\tcolinfo->leftattnos[jcolno++] = leftattno;\n\t}\n\trcolno = 0;\n\tforeach(lc, jrte->joinrightcols)\n\t{\n\t\tint\t\t\trightattno = lfirst_int(lc);\n\n\t\tif (rcolno < jrte->joinmergedcols)\t/* merged column? */\n\t\t\tcolinfo->rightattnos[rcolno] = rightattno;\n\t\telse\n\t\t\tcolinfo->rightattnos[jcolno++] = rightattno;\n\t\trcolno++;\n\t}\n\tAssert(jcolno == numjoincols);\n}\n\n/*\n * get_rtable_name: convenience function to get a previously assigned RTE alias\n *\n * The RTE must belong to the topmost namespace level in \"context\".\n */\nstatic char *\nget_rtable_name(int rtindex, deparse_context *context)\n{\n\tdeparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);\n\n\tAssert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));\n\treturn (char *) list_nth(dpns->rtable_names, rtindex - 1);\n}\n\n/*\n * set_deparse_plan: set up deparse_namespace to parse subexpressions\n * of a given Plan node\n *\n * This sets the plan, outer_planstate, inner_planstate, outer_tlist,\n * inner_tlist, and index_tlist fields.  Caller is responsible for adjusting\n * the ancestors list if necessary.  Note that the rtable and ctes fields do\n * not need to change when shifting attention to different plan nodes in a\n * single plan tree.\n */\nstatic void\nset_deparse_plan(deparse_namespace *dpns, Plan *plan)\n{\n\tdpns->plan = plan;\n\n\t/*\n\t * We special-case Append and MergeAppend to pretend that the first child\n\t * plan is the OUTER referent; we have to interpret OUTER Vars in their\n\t * tlists according to one of the children, and the first one is the most\n\t * natural choice.\n\t */\n\tif (IsA(plan, Append))\n\t\tdpns->outer_plan = linitial(((Append *) plan)->appendplans);\n\telse if (IsA(plan, MergeAppend))\n\t\tdpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);\n\telse\n\t\tdpns->outer_plan = outerPlan(plan);\n\n\tif (dpns->outer_plan)\n\t\tdpns->outer_tlist = dpns->outer_plan->targetlist;\n\telse\n\t\tdpns->outer_tlist = NIL;\n\n\t/*\n\t * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't\n\t * use OUTER because that could someday conflict with the normal meaning.)\n\t * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.\n     * For a WorkTableScan, locate the parent RecursiveUnion plan node and use\n     * that as INNER referent.\n     *\n     * For MERGE, make the inner tlist point to the merge source tlist, which\n\t * is same as the targetlist that the ModifyTable's source plan provides.\n\t * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the\n\t * excluded expression's tlist. (Similar to the SubqueryScan we don't want\n\t * to reuse OUTER, it's used for RETURNING in some modify table cases,\n\t * although not INSERT .. CONFLICT).\n\t */\n\tif (IsA(plan, SubqueryScan))\n\t\tdpns->inner_plan = ((SubqueryScan *) plan)->subplan;\n\telse if (IsA(plan, CteScan))\n\t\tdpns->inner_plan = list_nth(dpns->subplans,\n\t\t\t\t\t\t\t\t\t((CteScan *) plan)->ctePlanId - 1);\n    else if (IsA(plan, WorkTableScan))\n\t\tdpns->inner_plan = find_recursive_union(dpns,\n\t\t\t\t\t\t\t\t\t\t\t\t(WorkTableScan *) plan);\n\telse if (IsA(plan, ModifyTable))\n\t\tdpns->inner_plan = plan;\n\telse\n\t\tdpns->inner_plan = innerPlan(plan);\n\n\tif (IsA(plan, ModifyTable))\n\t{\n\t\tif (((ModifyTable *) plan)->operation == CMD_MERGE)\n\t\t\tdpns->inner_tlist = dpns->outer_tlist;\n\t\telse\n\t\t\tdpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;\n\t}\n\telse if (dpns->inner_plan)\n\t\tdpns->inner_tlist = dpns->inner_plan->targetlist;\n\telse\n\t\tdpns->inner_tlist = NIL;\n\n\t/* Set up referent for INDEX_VAR Vars, if needed */\n\tif (IsA(plan, IndexOnlyScan))\n\t\tdpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;\n\telse if (IsA(plan, ForeignScan))\n\t\tdpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;\n\telse if (IsA(plan, CustomScan))\n\t\tdpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;\n\telse\n\t\tdpns->index_tlist = NIL;\n}\n\n/*\n * Locate the ancestor plan node that is the RecursiveUnion generating\n * the WorkTableScan's work table.  We can match on wtParam, since that\n * should be unique within the plan tree.\n */\nstatic Plan *\nfind_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)\n{\n\tListCell   *lc;\n\n\tforeach(lc, dpns->ancestors)\n\t{\n\t\tPlan\t   *ancestor = (Plan *) lfirst(lc);\n\n\t\tif (IsA(ancestor, RecursiveUnion) &&\n\t\t\t((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)\n\t\t\treturn ancestor;\n\t}\n\telog(ERROR, \"could not find RecursiveUnion for WorkTableScan with wtParam %d\",\n\t\t wtscan->wtParam);\n\treturn NULL;\n}\n\n/*\n * push_child_plan: temporarily transfer deparsing attention to a child plan\n *\n * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the\n * deparse context in case the referenced expression itself uses\n * OUTER_VAR/INNER_VAR.  We modify the top stack entry in-place to avoid\n * affecting levelsup issues (although in a Plan tree there really shouldn't\n * be any).\n *\n * Caller must provide a local deparse_namespace variable to save the\n * previous state for pop_child_plan.\n */\nstatic void\npush_child_plan(deparse_namespace *dpns, Plan *plan,\n\t\t\t\tdeparse_namespace *save_dpns)\n{\n\t/* Save state for restoration later */\n\t*save_dpns = *dpns;\n\n\t/* Link current plan node into ancestors list */\n\tdpns->ancestors = lcons(dpns->plan, dpns->ancestors);\n\n\t/* Set attention on selected child */\n\tset_deparse_plan(dpns, plan);\n}\n\n/*\n * pop_child_plan: undo the effects of push_child_plan\n */\nstatic void\npop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)\n{\n\tList\t   *ancestors;\n\n\t/* Get rid of ancestors list cell added by push_child_plan */\n\tancestors = list_delete_first(dpns->ancestors);\n\n\t/* Restore fields changed by push_child_plan */\n\t*dpns = *save_dpns;\n\n\t/* Make sure dpns->ancestors is right (may be unnecessary) */\n\tdpns->ancestors = ancestors;\n}\n\n/*\n * push_ancestor_plan: temporarily transfer deparsing attention to an\n * ancestor plan\n *\n * When expanding a Param reference, we must adjust the deparse context\n * to match the plan node that contains the expression being printed;\n * otherwise we'd fail if that expression itself contains a Param or\n * OUTER_VAR/INNER_VAR/INDEX_VAR variable.\n *\n * The target ancestor is conveniently identified by the ListCell holding it\n * in dpns->ancestors.\n *\n * Caller must provide a local deparse_namespace variable to save the\n * previous state for pop_ancestor_plan.\n */\nstatic void\npush_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,\n\t\t\t\t   deparse_namespace *save_dpns)\n{\n\tPlan\t   *plan = (Plan *) lfirst(ancestor_cell);\n\n\t/* Save state for restoration later */\n\t*save_dpns = *dpns;\n\n\t/* Build a new ancestor list with just this node's ancestors */\n\tdpns->ancestors =\n\t\tlist_copy_tail(dpns->ancestors,\n\t\t\t\t\t   list_cell_number(dpns->ancestors, ancestor_cell) + 1);\n\n\t/* Set attention on selected ancestor */\n\tset_deparse_plan(dpns, plan);\n}\n\n/*\n * pop_ancestor_plan: undo the effects of push_ancestor_plan\n */\nstatic void\npop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)\n{\n\t/* Free the ancestor list made in push_ancestor_plan */\n\tlist_free(dpns->ancestors);\n\n\t/* Restore fields changed by push_ancestor_plan */\n\t*dpns = *save_dpns;\n}\n\n/* ----------\n * deparse_shard_query\t\t- Parse back a query for execution on a shard\n *\n * Builds an SQL string to perform the provided query on a specific shard and\n * places this string into the provided buffer.\n * ----------\n */\nvoid\ndeparse_shard_query(Query *query, Oid distrelid, int64 shardid,\n\t\t\t\t\tStringInfo buffer)\n{\n\tget_query_def_extended(query, buffer, NIL, distrelid, shardid, NULL,\n\t                       false,\n\t\t\t\t\t\t   0, WRAP_COLUMN_DEFAULT, 0);\n}\n\n/* ----------\n * get_query_def\t\t\t- Parse back one query parsetree\n *\n * query: parsetree to be displayed\n * buf: output text is appended to buf\n * parentnamespace: list (initially empty) of outer-level deparse_namespace's\n * resultDesc: if not NULL, the output tuple descriptor for the view\n *\t\trepresented by a SELECT query.  We use the column names from it\n *\t\tto label SELECT output columns, in preference to names in the query\n * colNamesVisible: true if the surrounding context cares about the output\n *\t\tcolumn names at all (as, for example, an EXISTS() context does not);\n *\t\twhen false, we can suppress dummy column labels such as \"?column?\"\n * prettyFlags: bitmask of PRETTYFLAG_XXX options\n * wrapColumn: maximum line length, or -1 to disable wrapping\n * startIndent: initial indentation amount\n * ----------\n */\nstatic void\nget_query_def(Query *query, StringInfo buf, List *parentnamespace,\n\t\t\t  TupleDesc resultDesc, bool colNamesVisible,\n\t\t\t  int prettyFlags, int wrapColumn, int startIndent)\n{\n\tget_query_def_extended(query, buf, parentnamespace, InvalidOid, 0, resultDesc,\n\t                       colNamesVisible,\n\t\t\t\t\t\t   prettyFlags, wrapColumn, startIndent);\n}\n\n/* ----------\n * get_query_def_extended\t\t- Parse back one query parsetree, optionally\n * \t\t\t\t\t\t\t\t  with extension using a shard identifier.\n *\n * If distrelid is valid and shardid is positive, the provided shardid is added\n * any time the provided relid is deparsed, so that the query may be executed\n * on a placement for the given shard.\n * ----------\n */\nstatic void\nget_query_def_extended(Query *query, StringInfo buf, List *parentnamespace,\n\t\t\t\t\t   Oid distrelid, int64 shardid, TupleDesc resultDesc,\n\t\t\t\t\t   bool colNamesVisible,\n\t\t\t\t\t   int prettyFlags, int wrapColumn, int startIndent)\n{\n\tdeparse_context context;\n\tdeparse_namespace dpns;\n\n\t/* Guard against excessively long or deeply-nested queries */\n\tCHECK_FOR_INTERRUPTS();\n\tcheck_stack_depth();\n\n\t/*\n\t * Before we begin to examine the query, acquire locks on referenced\n\t * relations, and fix up deleted columns in JOIN RTEs.  This ensures\n\t * consistent results.  Note we assume it's OK to scribble on the passed\n\t * querytree!\n\t *\n\t * We are only deparsing the query (we are not about to execute it), so we\n\t * only need AccessShareLock on the relations it mentions.\n\t */\n\tAcquireRewriteLocks(query, false, false);\n\n\t/*\n\t * Set search_path to NIL so that all objects outside of pg_catalog will be\n\t * schema-prefixed. pg_catalog will be added automatically when we call\n\t * PushEmptySearchPath().\n\t */\n\tint saveNestLevel = PushEmptySearchPath();\n\n\tcontext.buf = buf;\n\tcontext.namespaces = lcons(&dpns, list_copy(parentnamespace));\n\tcontext.resultDesc = NULL;\n\tcontext.targetList = NIL;\n\tcontext.windowClause = NIL;\n\tcontext.varprefix = (parentnamespace != NIL ||\n\t\t\t\t\t\t list_length(query->rtable) != 1);\n\tcontext.prettyFlags = prettyFlags;\n\tcontext.wrapColumn = wrapColumn;\n\tcontext.indentLevel = startIndent;\n\tcontext.colNamesVisible = true;\n\tcontext.inGroupBy = false;\n\tcontext.varInOrderBy = false;\n\tcontext.appendparents = NULL;\n\tcontext.distrelid = distrelid;\n\tcontext.shardid = shardid;\n\n\tset_deparse_for_query(&dpns, query, parentnamespace);\n\n\tswitch (query->commandType)\n\t{\n\t\tcase CMD_SELECT:\n\t\t\t/* We set context.resultDesc only if it's a SELECT */\n\t\t\tcontext.resultDesc = resultDesc;\n\t\t\tget_select_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_UPDATE:\n\t\t\tget_update_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_INSERT:\n\t\t\tget_insert_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_DELETE:\n\t\t\tget_delete_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_MERGE:\n\t\t\tget_merge_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_NOTHING:\n\t\t\tappendStringInfoString(buf, \"NOTHING\");\n\t\t\tbreak;\n\n\t\tcase CMD_UTILITY:\n\t\t\tget_utility_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\telog(ERROR, \"unrecognized query command type: %d\",\n\t\t\t\t query->commandType);\n\t\t\tbreak;\n\t}\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n}\n\n/* ----------\n * get_values_def\t\t\t- Parse back a VALUES list\n * ----------\n */\nstatic void\nget_values_def(List *values_lists, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tfirst_list = true;\n\tListCell   *vtl;\n\n\tappendStringInfoString(buf, \"VALUES \");\n\n\tforeach(vtl, values_lists)\n\t{\n\t\tList\t   *sublist = (List *) lfirst(vtl);\n\t\tbool\t\tfirst_col = true;\n\t\tListCell   *lc;\n\n\t\tif (first_list)\n\t\t\tfirst_list = false;\n\t\telse\n\t\t\tappendStringInfoString(buf, \", \");\n\n\t\tappendStringInfoChar(buf, '(');\n\t\tforeach(lc, sublist)\n\t\t{\n\t\t\tNode\t   *col = (Node *) lfirst(lc);\n\n\t\t\tif (first_col)\n\t\t\t\tfirst_col = false;\n\t\t\telse\n\t\t\t\tappendStringInfoChar(buf, ',');\n\n\t\t\t/*\n\t\t\t * Print the value.  Whole-row Vars need special treatment.\n\t\t\t */\n\t\t\tget_rule_expr_toplevel(col, context, false);\n\t\t}\n\t\tappendStringInfoChar(buf, ')');\n\t}\n}\n\n/* ----------\n * get_with_clause\t\t\t- Parse back a WITH clause\n * ----------\n */\nstatic void\nget_with_clause(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *sep;\n\tListCell   *l;\n\n\tif (query->cteList == NIL)\n\t\treturn;\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t\tappendStringInfoChar(buf, ' ');\n\t}\n\n\tif (query->hasRecursive)\n\t\tsep = \"WITH RECURSIVE \";\n\telse\n\t\tsep = \"WITH \";\n\tforeach(l, query->cteList)\n\t{\n\t\tCommonTableExpr *cte = (CommonTableExpr *) lfirst(l);\n\n\t\tappendStringInfoString(buf, sep);\n\t\tappendStringInfoString(buf, quote_identifier(cte->ctename));\n\t\tif (cte->aliascolnames)\n\t\t{\n\t\t\tbool\t\tfirst = true;\n\t\t\tListCell   *col;\n\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tforeach(col, cte->aliascolnames)\n\t\t\t{\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(strVal(lfirst(col))));\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\tappendStringInfoString(buf, \" AS \");\n\t\tswitch (cte->ctematerialized)\n\t\t{\n\t\t\tcase CTEMaterializeDefault:\n\t\t\t\tbreak;\n\t\t\tcase CTEMaterializeAlways:\n\t\t\t\tappendStringInfoString(buf, \"MATERIALIZED \");\n\t\t\t\tbreak;\n\t\t\tcase CTEMaterializeNever:\n\t\t\t\tappendStringInfoString(buf, \"NOT MATERIALIZED \");\n\t\t\t\tbreak;\n\t\t}\n\t\tappendStringInfoChar(buf, '(');\n\t\tif (PRETTY_INDENT(context))\n\t\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\t\tget_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,\n\t\t              true,\n\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t  context->indentLevel);\n\t\tif (PRETTY_INDENT(context))\n\t\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\t\tappendStringInfoChar(buf, ')');\n\n\t\tif (cte->search_clause)\n\t\t{\n\t\t\tbool\t\tfirst = true;\n\t\t\tListCell   *lc;\n\n\t\t\tappendStringInfo(buf, \" SEARCH %s FIRST BY \",\n\t\t\t\t\t\t\t cte->search_clause->search_breadth_first ? \"BREADTH\" : \"DEPTH\");\n\n\t\t\tforeach(lc, cte->search_clause->search_col_list)\n\t\t\t{\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(strVal(lfirst(lc))));\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" SET %s\", quote_identifier(cte->search_clause->search_seq_column));\n\t\t}\n\n\t\tif (cte->cycle_clause)\n\t\t{\n\t\t\tbool\t\tfirst = true;\n\t\t\tListCell   *lc;\n\n\t\t\tappendStringInfoString(buf, \" CYCLE \");\n\n\t\t\tforeach(lc, cte->cycle_clause->cycle_col_list)\n\t\t\t{\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(strVal(lfirst(lc))));\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" SET %s\", quote_identifier(cte->cycle_clause->cycle_mark_column));\n\n\t\t\t{\n\t\t\t\tConst\t   *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);\n\t\t\t\tConst\t   *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);\n\n\t\t\t\tif (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&\n\t\t\t\t\t  cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(buf, \" TO \");\n\t\t\t\t\tget_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);\n\t\t\t\t\tappendStringInfoString(buf, \" DEFAULT \");\n\t\t\t\t\tget_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" USING %s\", quote_identifier(cte->cycle_clause->cycle_path_column));\n\t\t}\n\n\t\tsep = \", \";\n\t}\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel -= PRETTYINDENT_STD;\n\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\t}\n\telse\n\t\tappendStringInfoChar(buf, ' ');\n}\n\n/* ----------\n * get_select_query_def\t\t\t- Parse back a SELECT parsetree\n * ----------\n */\nstatic void\nget_select_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tforce_colno;\n\tListCell   *l;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/* Subroutines may need to consult the SELECT targetlist and windowClause */\n\tcontext->targetList = query->targetList;\n\tcontext->windowClause = query->windowClause;\n\n\t/*\n\t * If the Query node has a setOperations tree, then it's the top level of\n\t * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT\n\t * fields are interesting in the top query itself.\n\t */\n\tif (query->setOperations)\n\t{\n\t\tget_setop_query(query->setOperations, query, context);\n\t\t/* ORDER BY clauses must be simple in this case */\n\t\tforce_colno = true;\n\t}\n\telse\n\t{\n\t\tget_basic_select_query(query, context);\n\t\tforce_colno = false;\n\t}\n\n\t/* Add the ORDER BY clause if given */\n\tif (query->sortClause != NIL)\n\t{\n\t\tappendContextKeyword(context, \" ORDER BY \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_orderby(query->sortClause, query->targetList,\n\t\t\t\t\t\t force_colno, context);\n\t}\n\n\t/*\n\t * Add the LIMIT/OFFSET clauses if given. If non-default options, use the\n\t * standard spelling of LIMIT.\n\t */\n\tif (query->limitOffset != NULL)\n\t{\n\t\tappendContextKeyword(context, \" OFFSET \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\tget_rule_expr(query->limitOffset, context, false);\n\t}\n\tif (query->limitCount != NULL)\n\t{\n\t\tif (query->limitOption == LIMIT_OPTION_WITH_TIES)\n\t\t{\n\t\t\t// had to add '(' and ')' here because it fails with casting\n\t\t\tappendContextKeyword(context, \" FETCH FIRST (\",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\tget_rule_expr(query->limitCount, context, false);\n\t\t\tappendStringInfoString(buf, \") ROWS WITH TIES\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendContextKeyword(context, \" LIMIT \",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\tif (IsA(query->limitCount, Const) &&\n\t\t\t\t((Const *) query->limitCount)->constisnull)\n\t\t\t\tappendStringInfoString(buf, \"ALL\");\n\t\t\telse\n\t\t\t\tget_rule_expr(query->limitCount, context, false);\n\t\t}\n\t}\n\n\t/* Add FOR [KEY] UPDATE/SHARE clauses if present */\n\tif (query->hasForUpdate)\n\t{\n\t\tforeach(l, query->rowMarks)\n\t\t{\n\t\t\tRowMarkClause *rc = (RowMarkClause *) lfirst(l);\n\n\t\t\t/* don't print implicit clauses */\n\t\t\tif (rc->pushedDown)\n\t\t\t\tcontinue;\n\n\t\t\tswitch (rc->strength)\n\t\t\t{\n\t\t\t\tcase LCS_NONE:\n\t\t\t\t\t/* we intentionally throw an error for LCS_NONE */\n\t\t\t\t\telog(ERROR, \"unrecognized LockClauseStrength %d\",\n\t\t\t\t\t\t (int) rc->strength);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORKEYSHARE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR KEY SHARE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORSHARE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR SHARE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORNOKEYUPDATE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR NO KEY UPDATE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORUPDATE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR UPDATE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" OF %s\",\n\t\t\t\t\t\t\t quote_identifier(get_rtable_name(rc->rti,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  context)));\n\t\t\tif (rc->waitPolicy == LockWaitError)\n\t\t\t\tappendStringInfoString(buf, \" NOWAIT\");\n\t\t\telse if (rc->waitPolicy == LockWaitSkip)\n\t\t\t\tappendStringInfoString(buf, \" SKIP LOCKED\");\n\t\t}\n\t}\n}\n\n/*\n * Detect whether query looks like SELECT ... FROM VALUES();\n * if so, return the VALUES RTE.  Otherwise return NULL.\n */\nstatic RangeTblEntry *\nget_simple_values_rte(Query *query, TupleDesc resultDesc)\n{\n\tRangeTblEntry *result = NULL;\n\tListCell   *lc;\n\tint colno;\n\n\t/*\n\t * We want to return true even if the Query also contains OLD or NEW rule\n\t * RTEs.  So the idea is to scan the rtable and see if there is only one\n\t * inFromCl RTE that is a VALUES RTE.\n\t */\n\tforeach(lc, query->rtable)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n\n\t\tif (rte->rtekind == RTE_VALUES && rte->inFromCl)\n\t\t{\n\t\t\tif (result)\n\t\t\t\treturn NULL;\t/* multiple VALUES (probably not possible) */\n\t\t\tresult = rte;\n\t\t}\n\t\telse if (rte->rtekind == RTE_RELATION && !rte->inFromCl)\n\t\t\tcontinue;\t\t\t/* ignore rule entries */\n\t\telse\n\t\t\treturn NULL;\t\t/* something else -> not simple VALUES */\n\t}\n\n\t/*\n\t * We don't need to check the targetlist in any great detail, because\n\t * parser/analyze.c will never generate a \"bare\" VALUES RTE --- they only\n\t * appear inside auto-generated sub-queries with very restricted\n\t * structure.  However, DefineView might have modified the tlist by\n\t * injecting new column aliases; so compare tlist resnames against the\n\t * RTE's names to detect that.\n\t */\n\tif (result)\n\t{\n\t\tListCell   *lcn;\n\n\t\tif (list_length(query->targetList) != list_length(result->eref->colnames))\n\t\t\treturn NULL;\t\t/* this probably cannot happen */\n\t\tcolno = 0;\n\t\tforboth(lc, query->targetList, lcn, result->eref->colnames)\n\t\t{\n\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(lc);\n\t\t\tchar\t   *cname = strVal(lfirst(lcn));\n\t\t\tchar\t   *colname;\n\n\t\t\tif (tle->resjunk)\n\t\t\t\treturn NULL;\t/* this probably cannot happen */\n\t\t\t/* compute name that get_target_list would use for column */\n\t\t\tcolno++;\n\t\t\tif (resultDesc && colno <= resultDesc->natts)\n\t\t\t\tcolname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);\n\t\t\telse\n\t\t\t\tcolname = tle->resname;\n\n\t\t\t/* does it match the VALUES RTE? */\n\t\t\tif (colname == NULL || strcmp(colname, cname) != 0)\n\t\t\t\treturn NULL;\t/* column name has been changed */\n\t\t}\n\t}\n\n\treturn result;\n}\n\nstatic void\nget_basic_select_query(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *values_rte;\n\tchar\t   *sep;\n\tListCell   *l;\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t\tappendStringInfoChar(buf, ' ');\n\t}\n\n\t/*\n\t * If the query looks like SELECT * FROM (VALUES ...), then print just the\n\t * VALUES part.  This reverses what transformValuesClause() did at parse\n\t * time.\n\t */\n\tvalues_rte = get_simple_values_rte(query, context->resultDesc);\n\tif (values_rte)\n\t{\n\t\tget_values_def(values_rte->values_lists, context);\n\t\treturn;\n\t}\n\n\t/*\n\t * Build up the query string - first we say SELECT\n\t */\n\tif (query->isReturn)\n\t\tappendStringInfoString(buf, \"RETURN\");\n\telse\n\t\tappendStringInfoString(buf, \"SELECT\");\n\n\t/* Add the DISTINCT clause if given */\n\tif (query->distinctClause != NIL)\n\t{\n\t\tif (query->hasDistinctOn)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DISTINCT ON (\");\n\t\t\tsep = \"\";\n\t\t\tforeach(l, query->distinctClause)\n\t\t\t{\n\t\t\t\tSortGroupClause *srt = (SortGroupClause *) lfirst(l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,\n\t\t\t\t\t\t\t\t\t\t false, context);\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse\n\t\t\tappendStringInfoString(buf, \" DISTINCT\");\n\t}\n\n\t/* Then we tell what to select (the targetlist) */\n\tget_target_list(query->targetList, context);\n\n\t/* Add the FROM clause if needed */\n\tget_from_clause(query, \" FROM \", context);\n\n\t/* Add the WHERE clause if given */\n\tif (query->jointree->quals != NULL)\n\t{\n\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_expr(query->jointree->quals, context, false);\n\t}\n\n\t/* Add the GROUP BY clause if given */\n\tif (query->groupClause != NULL || query->groupingSets != NULL)\n\t{\n\t\tbool\t\tsave_ingroupby;\n\n\t\tappendContextKeyword(context, \" GROUP BY \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tif (query->groupDistinct)\n\t\t\tappendStringInfoString(buf, \"DISTINCT \");\n\n\t\tsave_ingroupby = context->inGroupBy;\n\t\tcontext->inGroupBy = true;\n\n\t\tif (query->groupingSets == NIL)\n\t\t{\n\t\t\tsep = \"\";\n\t\t\tforeach(l, query->groupClause)\n\t\t\t{\n\t\t\t\tSortGroupClause *grp = (SortGroupClause *) lfirst(l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,\n\t\t\t\t\t\t\t\t\t\t false, context);\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsep = \"\";\n\t\t\tforeach(l, query->groupingSets)\n\t\t\t{\n\t\t\t\tGroupingSet *grp = lfirst(l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_groupingset(grp, query->targetList, true, context);\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t}\n\n\t\tcontext->inGroupBy = save_ingroupby;\n\t}\n\n\t/* Add the HAVING clause if given */\n\tif (query->havingQual != NULL)\n\t{\n\t\tappendContextKeyword(context, \" HAVING \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\tget_rule_expr(query->havingQual, context, false);\n\t}\n\n\t/* Add the WINDOW clause if needed */\n\tif (query->windowClause != NIL)\n\t\tget_rule_windowclause(query, context);\n}\n\n/* ----------\n * get_target_list\t\t\t- Parse back a SELECT target list\n *\n * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.\n * ----------\n */\nstatic void\nget_target_list(List *targetList, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tStringInfoData targetbuf;\n\tbool\t\tlast_was_multiline = false;\n\tchar\t   *sep;\n\tint\t\t\tcolno;\n\tListCell   *l;\n\n\t/* we use targetbuf to hold each TLE's text temporarily */\n\tinitStringInfo(&targetbuf);\n\n\tsep = \" \";\n\tcolno = 0;\n\tforeach(l, targetList)\n\t{\n\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\t\tchar\t   *colname;\n\t\tchar\t   *attname;\n\n\t\tif (tle->resjunk)\n\t\t\tcontinue;\t\t\t/* ignore junk entries */\n\n\t\tappendStringInfoString(buf, sep);\n\t\tsep = \", \";\n\t\tcolno++;\n\n\t\t/*\n\t\t * Put the new field text into targetbuf so we can decide after we've\n\t\t * got it whether or not it needs to go on a new line.\n\t\t */\n\t\tresetStringInfo(&targetbuf);\n\t\tcontext->buf = &targetbuf;\n\n\t\t/*\n\t\t * We special-case Var nodes rather than using get_rule_expr. This is\n\t\t * needed because get_rule_expr will display a whole-row Var as\n\t\t * \"foo.*\", which is the preferred notation in most contexts, but at\n\t\t * the top level of a SELECT list it's not right (the parser will\n\t\t * expand that notation into multiple columns, yielding behavior\n\t\t * different from a whole-row Var).  We need to call get_variable\n\t\t * directly so that we can tell it to do the right thing, and so that\n\t\t * we can get the attribute name which is the default AS label.\n\t\t */\n\t\tif (tle->expr && (IsA(tle->expr, Var)))\n\t\t{\n\t\t\tattname = get_variable((Var *) tle->expr, 0, true, context);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tget_rule_expr((Node *) tle->expr, context, true);\n\n\t\t\t/*\n\t\t\t * When colNamesVisible is true, we should always show the\n\t\t\t * assigned column name explicitly.  Otherwise, show it only if\n\t\t\t * it's not FigureColname's fallback.\n\t\t\t */\n\t\t\tattname = context->colNamesVisible ? NULL : \"?column?\";\n\t\t}\n\n\t\t/*\n\t\t * Figure out what the result column should be called.  In the context\n\t\t * of a view, use the view's tuple descriptor (so as to pick up the\n\t\t * effects of any column RENAME that's been done on the view).\n\t\t * Otherwise, just use what we can find in the TLE.\n\t\t */\n\t\tif (context->resultDesc && colno <= context->resultDesc->natts)\n\t\t\tcolname = NameStr(TupleDescAttr(context->resultDesc,\n\t\t\t\t\t\t\t\t\t\t\tcolno - 1)->attname);\n\t\telse\n\t\t\tcolname = tle->resname;\n\n\t\t/* Show AS unless the column's name is correct as-is */\n\t\tif (colname)\t\t\t/* resname could be NULL */\n\t\t{\n\t\t\tif (attname == NULL || strcmp(attname, colname) != 0)\n\t\t\t\tappendStringInfo(&targetbuf, \" AS %s\", quote_identifier(colname));\n\t\t}\n\n\t\t/* Restore context's output buffer */\n\t\tcontext->buf = buf;\n\n\t\t/* Consider line-wrapping if enabled */\n\t\tif (PRETTY_INDENT(context) && context->wrapColumn >= 0)\n\t\t{\n\t\t\tint\t\t\tleading_nl_pos;\n\n\t\t\t/* Does the new field start with a new line? */\n\t\t\tif (targetbuf.len > 0 && targetbuf.data[0] == '\\n')\n\t\t\t\tleading_nl_pos = 0;\n\t\t\telse\n\t\t\t\tleading_nl_pos = -1;\n\n\t\t\t/* If so, we shouldn't add anything */\n\t\t\tif (leading_nl_pos >= 0)\n\t\t\t{\n\t\t\t\t/* instead, remove any trailing spaces currently in buf */\n\t\t\t\tremoveStringInfoSpaces(buf);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar\t   *trailing_nl;\n\n\t\t\t\t/* Locate the start of the current line in the output buffer */\n\t\t\t\ttrailing_nl = strrchr(buf->data, '\\n');\n\t\t\t\tif (trailing_nl == NULL)\n\t\t\t\t\ttrailing_nl = buf->data;\n\t\t\t\telse\n\t\t\t\t\ttrailing_nl++;\n\n\t\t\t\t/*\n\t\t\t\t * Add a newline, plus some indentation, if the new field is\n\t\t\t\t * not the first and either the new field would cause an\n\t\t\t\t * overflow or the last field used more than one line.\n\t\t\t\t */\n\t\t\t\tif (colno > 1 &&\n\t\t\t\t\t((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||\n\t\t\t\t\t last_was_multiline))\n\t\t\t\t\tappendContextKeyword(context, \"\", -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD, PRETTYINDENT_VAR);\n\t\t\t}\n\n\t\t\t/* Remember this field's multiline status for next iteration */\n\t\t\tlast_was_multiline =\n\t\t\t\t(strchr(targetbuf.data + leading_nl_pos + 1, '\\n') != NULL);\n\t\t}\n\n\t\t/* Add the new field */\n\t\tappendStringInfoString(buf, targetbuf.data);\n\t}\n\n\t/* clean up */\n\tpfree(targetbuf.data);\n}\n\nstatic void\nget_setop_query(Node *setOp, Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tneed_paren;\n\n\t/* Guard against excessively long or deeply-nested queries */\n\tCHECK_FOR_INTERRUPTS();\n\tcheck_stack_depth();\n\n\tif (IsA(setOp, RangeTblRef))\n\t{\n\t\tRangeTblRef *rtr = (RangeTblRef *) setOp;\n\t\tRangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);\n\t\tQuery\t   *subquery = rte->subquery;\n\n\t\tAssert(subquery != NULL);\n\t\tAssert(subquery->setOperations == NULL);\n\t\t/* Need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y */\n\t\tneed_paren = (subquery->cteList ||\n\t\t\t\t\t  subquery->sortClause ||\n\t\t\t\t\t  subquery->rowMarks ||\n\t\t\t\t\t  subquery->limitOffset ||\n\t\t\t\t\t  subquery->limitCount);\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_query_def(subquery, buf, context->namespaces,\n\t\t\t\t\t  context->resultDesc, context->colNamesVisible,\n\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t  context->indentLevel);\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(buf, ')');\n\t}\n\telse if (IsA(setOp, SetOperationStmt))\n\t{\n\t\tSetOperationStmt *op = (SetOperationStmt *) setOp;\n\t\tint\t\t\tsubindent;\n\t\tbool\t\tsave_colnamesvisible;\n\n\t\t/*\n\t\t * We force parens when nesting two SetOperationStmts, except when the\n\t\t * lefthand input is another setop of the same kind.  Syntactically,\n\t\t * we could omit parens in rather more cases, but it seems best to use\n\t\t * parens to flag cases where the setop operator changes.  If we use\n\t\t * parens, we also increase the indentation level for the child query.\n\t\t *\n\t\t * There are some cases in which parens are needed around a leaf query\n\t\t * too, but those are more easily handled at the next level down (see\n\t\t * code above).\n\t\t */\n\t\tif (IsA(op->larg, SetOperationStmt))\n\t\t{\n\t\t\tSetOperationStmt *lop = (SetOperationStmt *) op->larg;\n\n\t\t\tif (op->op == lop->op && op->all == lop->all)\n\t\t\t\tneed_paren = false;\n\t\t\telse\n\t\t\t\tneed_paren = true;\n\t\t}\n\t\telse\n\t\t\tneed_paren = false;\n\n\t\tif (need_paren)\n\t\t{\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tsubindent = PRETTYINDENT_STD;\n\t\t\tappendContextKeyword(context, \"\", subindent, 0, 0);\n\t\t}\n\t\telse\n\t\t\tsubindent = 0;\n\n\t\tget_setop_query(op->larg, query, context);\n\n\t\tif (need_paren)\n\t\t\tappendContextKeyword(context, \") \", -subindent, 0, 0);\n\t\telse if (PRETTY_INDENT(context))\n\t\t\tappendContextKeyword(context, \"\", -subindent, 0, 0);\n\t\telse\n\t\t\tappendStringInfoChar(buf, ' ');\n\n\t\tswitch (op->op)\n\t\t{\n\t\t\tcase SETOP_UNION:\n\t\t\t\tappendStringInfoString(buf, \"UNION \");\n\t\t\t\tbreak;\n\t\t\tcase SETOP_INTERSECT:\n\t\t\t\tappendStringInfoString(buf, \"INTERSECT \");\n\t\t\t\tbreak;\n\t\t\tcase SETOP_EXCEPT:\n\t\t\t\tappendStringInfoString(buf, \"EXCEPT \");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\telog(ERROR, \"unrecognized set op: %d\",\n\t\t\t\t\t (int) op->op);\n\t\t}\n\t\tif (op->all)\n\t\t\tappendStringInfoString(buf, \"ALL \");\n\n\t\t/* Always parenthesize if RHS is another setop */\n\t\tneed_paren = IsA(op->rarg, SetOperationStmt);\n\n\t\t/*\n\t\t * The indentation code here is deliberately a bit different from that\n\t\t * for the lefthand input, because we want the line breaks in\n\t\t * different places.\n\t\t */\n\t\tif (need_paren)\n\t\t{\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tsubindent = PRETTYINDENT_STD;\n\t\t}\n\t\telse\n\t\t\tsubindent = 0;\n\t\tappendContextKeyword(context, \"\", subindent, 0, 0);\n\n\t\t/*\n\t\t * The output column names of the RHS sub-select don't matter.\n\t\t */\n\t\tsave_colnamesvisible = context->colNamesVisible;\n\t\tcontext->colNamesVisible = false;\n\t\tget_setop_query(op->rarg, query, context);\n\t\tcontext->colNamesVisible = save_colnamesvisible;\n\n\t\tif (PRETTY_INDENT(context))\n\t\t\tcontext->indentLevel -= subindent;\n\t\tif (need_paren)\n\t\t\tappendContextKeyword(context, \")\", 0, 0, 0);\n\t}\n\telse\n\t{\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(setOp));\n\t}\n}\n\n/*\n * Display a sort/group clause.\n *\n * Also returns the expression tree, so caller need not find it again.\n */\nstatic Node *\nget_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,\n\t\t\t\t\t\t deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tTargetEntry *tle;\n\tNode\t   *expr;\n\n\ttle = get_sortgroupref_tle(ref, tlist);\n\texpr = (Node *) tle->expr;\n\n\t/*\n\t * Use column-number form if requested by caller.  Otherwise, if\n\t * expression is a constant, force it to be dumped with an explicit cast\n\t * as decoration --- this is because a simple integer constant is\n\t * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if\n\t * we dump it without any decoration.  Similarly, if it's just a Var,\n\t * there is risk of misinterpretation if the column name is reassigned in\n\t * the SELECT list, so we may need to force table qualification.  And, if\n\t * it's anything more complex than a simple Var, then force extra parens\n\t * around it, to ensure it can't be misinterpreted as a cube() or rollup()\n\t * construct.\n\t */\n\tif (force_colno)\n\t{\n\t\tAssert(!tle->resjunk);\n\t\tappendStringInfo(buf, \"%d\", tle->resno);\n\t}\n\telse if (!expr)\n\t\t /* do nothing, probably can't happen */ ;\n\telse if (IsA(expr, Const))\n\t\tget_const_expr((Const *) expr, context, 1);\n\telse if (IsA(expr, Var))\n\t{\n\t\t/* Tell get_variable to check for name conflict */\n\t\tbool\t\tsave_varinorderby = context->varInOrderBy;\n\t\tcontext->varInOrderBy = true;\n\t\t(void) get_variable((Var *) expr, 0, false, context);\n\t\tcontext->varInOrderBy = save_varinorderby;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * We must force parens for function-like expressions even if\n\t\t * PRETTY_PAREN is off, since those are the ones in danger of\n\t\t * misparsing. For other expressions we need to force them only if\n\t\t * PRETTY_PAREN is on, since otherwise the expression will output them\n\t\t * itself. (We can't skip the parens.)\n\t\t */\n\t\tbool\t\tneed_paren = (PRETTY_PAREN(context)\n\t\t\t\t\t\t\t\t  || IsA(expr, FuncExpr)\n\t\t\t\t\t\t\t\t  || IsA(expr, Aggref)\n\t\t\t\t\t\t\t\t  || IsA(expr, WindowFunc)\n\t\t\t\t\t\t\t\t  || IsA(expr, JsonConstructorExpr));\n\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, '(');\n\t\tget_rule_expr(expr, context, true);\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, ')');\n\t}\n\n\treturn expr;\n}\n\n/*\n * Display a GroupingSet\n */\nstatic void\nget_rule_groupingset(GroupingSet *gset, List *targetlist,\n\t\t\t\t\t bool omit_parens, deparse_context *context)\n{\n\tListCell   *l;\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tomit_child_parens = true;\n\tchar\t   *sep = \"\";\n\n\tswitch (gset->kind)\n\t{\n\t\tcase GROUPING_SET_EMPTY:\n\t\t\tappendStringInfoString(buf, \"()\");\n\t\t\treturn;\n\n\t\tcase GROUPING_SET_SIMPLE:\n\t\t\t{\n\t\t\t\tif (!omit_parens || list_length(gset->content) != 1)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\n\t\t\t\tforeach(l, gset->content)\n\t\t\t\t{\n\t\t\t\t\tIndex\t\tref = lfirst_int(l);\n\n\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\tget_rule_sortgroupclause(ref, targetlist,\n\t\t\t\t\t\t\t\t\t\t\t false, context);\n\t\t\t\t\tsep = \", \";\n\t\t\t\t}\n\n\t\t\t\tif (!omit_parens || list_length(gset->content) != 1)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\treturn;\n\n\t\tcase GROUPING_SET_ROLLUP:\n\t\t\tappendStringInfoString(buf, \"ROLLUP(\");\n\t\t\tbreak;\n\t\tcase GROUPING_SET_CUBE:\n\t\t\tappendStringInfoString(buf, \"CUBE(\");\n\t\t\tbreak;\n\t\tcase GROUPING_SET_SETS:\n\t\t\tappendStringInfoString(buf, \"GROUPING SETS (\");\n\t\t\tomit_child_parens = false;\n\t\t\tbreak;\n\t}\n\n\tforeach(l, gset->content)\n\t{\n\t\tappendStringInfoString(buf, sep);\n\t\tget_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);\n\t\tsep = \", \";\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * Display an ORDER BY list.\n */\nstatic void\nget_rule_orderby(List *orderList, List *targetList,\n\t\t\t\t bool force_colno, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *sep;\n\tListCell   *l;\n\n\tsep = \"\";\n\tforeach(l, orderList)\n\t{\n\t\tSortGroupClause *srt = (SortGroupClause *) lfirst(l);\n\t\tNode\t   *sortexpr;\n\t\tOid\t\t\tsortcoltype;\n\t\tTypeCacheEntry *typentry;\n\n\t\tappendStringInfoString(buf, sep);\n\t\tsortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,\n\t\t\t\t\t\t\t\t\t\t\tforce_colno, context);\n\t\tsortcoltype = exprType(sortexpr);\n\t\t/* See whether operator is default < or > for datatype */\n\t\ttypentry = lookup_type_cache(sortcoltype,\n\t\t\t\t\t\t\t\t\t TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);\n\t\tif (srt->sortop == typentry->lt_opr)\n\t\t{\n\t\t\t/* ASC is default, so emit nothing for it */\n\t\t\tif (srt->nulls_first)\n\t\t\t\tappendStringInfoString(buf, \" NULLS FIRST\");\n\t\t}\n\t\telse if (srt->sortop == typentry->gt_opr)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DESC\");\n\t\t\t/* DESC defaults to NULLS FIRST */\n\t\t\tif (!srt->nulls_first)\n\t\t\t\tappendStringInfoString(buf, \" NULLS LAST\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(buf, \" USING %s\",\n\t\t\t\t\t\t\t generate_operator_name(srt->sortop,\n\t\t\t\t\t\t\t\t\t\t\t\t\tsortcoltype,\n\t\t\t\t\t\t\t\t\t\t\t\t\tsortcoltype));\n\t\t\t/* be specific to eliminate ambiguity */\n\t\t\tif (srt->nulls_first)\n\t\t\t\tappendStringInfoString(buf, \" NULLS FIRST\");\n\t\t\telse\n\t\t\t\tappendStringInfoString(buf, \" NULLS LAST\");\n\t\t}\n\t\tsep = \", \";\n\t}\n}\n\n/*\n * Display a WINDOW clause.\n *\n * Note that the windowClause list might contain only anonymous window\n * specifications, in which case we should print nothing here.\n */\nstatic void\nget_rule_windowclause(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *sep;\n\tListCell   *l;\n\n\tsep = NULL;\n\tforeach(l, query->windowClause)\n\t{\n\t\tWindowClause *wc = (WindowClause *) lfirst(l);\n\n\t\tif (wc->name == NULL)\n\t\t\tcontinue;\t\t\t/* ignore anonymous windows */\n\n\t\tif (sep == NULL)\n\t\t\tappendContextKeyword(context, \" WINDOW \",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\telse\n\t\t\tappendStringInfoString(buf, sep);\n\n\t\tappendStringInfo(buf, \"%s AS \", quote_identifier(wc->name));\n\n\t\tget_rule_windowspec(wc, query->targetList, context);\n\n\t\tsep = \", \";\n\t}\n}\n\n/*\n * Display a window definition\n */\nstatic void\nget_rule_windowspec(WindowClause *wc, List *targetList,\n\t\t\t\t\tdeparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tneedspace = false;\n\tconst char *sep;\n\tListCell   *l;\n\n\tappendStringInfoChar(buf, '(');\n\tif (wc->refname)\n\t{\n\t\tappendStringInfoString(buf, quote_identifier(wc->refname));\n\t\tneedspace = true;\n\t}\n\t/* partition clauses are always inherited, so only print if no refname */\n\tif (wc->partitionClause && !wc->refname)\n\t{\n\t\tif (needspace)\n\t\t\tappendStringInfoChar(buf, ' ');\n\t\tappendStringInfoString(buf, \"PARTITION BY \");\n\t\tsep = \"\";\n\t\tforeach(l, wc->partitionClause)\n\t\t{\n\t\t\tSortGroupClause *grp = (SortGroupClause *) lfirst(l);\n\n\t\t\tappendStringInfoString(buf, sep);\n\t\t\tget_rule_sortgroupclause(grp->tleSortGroupRef, targetList,\n\t\t\t\t\t\t\t\t\t false, context);\n\t\t\tsep = \", \";\n\t\t}\n\t\tneedspace = true;\n\t}\n\t/* print ordering clause only if not inherited */\n\tif (wc->orderClause && !wc->copiedOrder)\n\t{\n\t\tif (needspace)\n\t\t\tappendStringInfoChar(buf, ' ');\n\t\tappendStringInfoString(buf, \"ORDER BY \");\n\t\tget_rule_orderby(wc->orderClause, targetList, false, context);\n\t\tneedspace = true;\n\t}\n\t/* framing clause is never inherited, so print unless it's default */\n\tif (wc->frameOptions & FRAMEOPTION_NONDEFAULT)\n\t{\n\t\tif (needspace)\n\t\t\tappendStringInfoChar(buf, ' ');\n\t\tif (wc->frameOptions & FRAMEOPTION_RANGE)\n\t\t\tappendStringInfoString(buf, \"RANGE \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_ROWS)\n\t\t\tappendStringInfoString(buf, \"ROWS \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_GROUPS)\n\t\t\tappendStringInfoString(buf, \"GROUPS \");\n\t\telse\n\t\t\tAssert(false);\n\t\tif (wc->frameOptions & FRAMEOPTION_BETWEEN)\n\t\t\tappendStringInfoString(buf, \"BETWEEN \");\n\t\tif (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)\n\t\t\tappendStringInfoString(buf, \"UNBOUNDED PRECEDING \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW)\n\t\t\tappendStringInfoString(buf, \"CURRENT ROW \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_START_OFFSET)\n\t\t{\n\t\t\tget_rule_expr(wc->startOffset, context, false);\n\t\t\tif (wc->frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)\n\t\t\t\tappendStringInfoString(buf, \" PRECEDING \");\n\t\t\telse if (wc->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)\n\t\t\t\tappendStringInfoString(buf, \" FOLLOWING \");\n\t\t\telse\n\t\t\t\tAssert(false);\n\t\t}\n\t\telse\n\t\t\tAssert(false);\n\t\tif (wc->frameOptions & FRAMEOPTION_BETWEEN)\n\t\t{\n\t\t\tappendStringInfoString(buf, \"AND \");\n\t\t\tif (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)\n\t\t\t\tappendStringInfoString(buf, \"UNBOUNDED FOLLOWING \");\n\t\t\telse if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW)\n\t\t\t\tappendStringInfoString(buf, \"CURRENT ROW \");\n\t\t\telse if (wc->frameOptions & FRAMEOPTION_END_OFFSET)\n\t\t\t{\n\t\t\t\tget_rule_expr(wc->endOffset, context, false);\n\t\t\t\tif (wc->frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)\n\t\t\t\t\tappendStringInfoString(buf, \" PRECEDING \");\n\t\t\t\telse if (wc->frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)\n\t\t\t\t\tappendStringInfoString(buf, \" FOLLOWING \");\n\t\t\t\telse\n\t\t\t\t\tAssert(false);\n\t\t\t}\n\t\t\telse\n\t\t\t\tAssert(false);\n\t\t}\n\t\tif (wc->frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)\n\t\t\tappendStringInfoString(buf, \"EXCLUDE CURRENT ROW \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_EXCLUDE_GROUP)\n\t\t\tappendStringInfoString(buf, \"EXCLUDE GROUP \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_EXCLUDE_TIES)\n\t\t\tappendStringInfoString(buf, \"EXCLUDE TIES \");\n\t\t/* we will now have a trailing space; remove it */\n\t\tbuf->len--;\n\t}\n\tappendStringInfoChar(buf, ')');\n}\n\n/* ----------\n * get_insert_query_def\t\t\t- Parse back an INSERT parsetree\n * ----------\n */\nstatic void\nget_insert_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *select_rte = NULL;\n\tRangeTblEntry *values_rte = NULL;\n\tRangeTblEntry *rte;\n\tListCell   *l;\n\tList\t   *strippedexprs = NIL;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * If it's an INSERT ... SELECT or multi-row VALUES, there will be a\n\t * single RTE for the SELECT or VALUES.  Plain VALUES has neither.\n\t */\n\tforeach(l, query->rtable)\n\t{\n\t\trte = (RangeTblEntry *) lfirst(l);\n\n\t\tif (rte->rtekind == RTE_SUBQUERY)\n\t\t{\n\t\t\tif (select_rte)\n\t\t\t\telog(ERROR, \"too many subquery RTEs in INSERT\");\n\t\t\tselect_rte = rte;\n\t\t}\n\n\t\tif (rte->rtekind == RTE_VALUES)\n\t\t{\n\t\t\tif (values_rte)\n\t\t\t\telog(ERROR, \"too many values RTEs in INSERT\");\n\t\t\tvalues_rte = rte;\n\t\t}\n\t}\n\tif (select_rte && values_rte)\n\t\telog(ERROR, \"both subquery and values RTEs in INSERT\");\n\n\t/*\n\t * Start the query with INSERT INTO relname\n\t */\n\trte = rt_fetch(query->resultRelation, query->rtable);\n\tAssert(rte->rtekind == RTE_RELATION);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t\tappendStringInfoChar(buf, ' ');\n\t}\n\tappendStringInfo(buf, \"INSERT INTO %s\",\n\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL));\n\n\t/* Print the relation alias, if needed; INSERT requires explicit AS */\n\tget_rte_alias(rte, query->resultRelation, true, context);\n\n\t/* always want a space here */\n\tappendStringInfoChar(buf, ' ');\n\n\t/*\n\t * Add the insert-column-names list.  Any indirection decoration needed on\n\t * the column names can be inferred from the top targetlist.\n\t */\n\tif (query->targetList)\n\t{\n\t\tstrippedexprs = get_insert_column_names_list(query->targetList,\n\t\t\t\t\t\t\t\tbuf, context, rte);\n\t}\n\n\tif (query->override)\n\t{\n\t\tif (query->override == OVERRIDING_SYSTEM_VALUE)\n\t\t\tappendStringInfoString(buf, \"OVERRIDING SYSTEM VALUE \");\n\t\telse if (query->override == OVERRIDING_USER_VALUE)\n\t\t\tappendStringInfoString(buf, \"OVERRIDING USER VALUE \");\n\t}\n\n\tif (select_rte)\n\t{\n\t\t/* Add the SELECT */\n\t\tget_query_def(select_rte->subquery, buf, context->namespaces, NULL,\n\t\t              false,\n\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t  context->indentLevel);\n\t}\n\telse if (values_rte)\n\t{\n\t\t/* Add the multi-VALUES expression lists */\n\t\tget_values_def(values_rte->values_lists, context);\n\t}\n\telse if (strippedexprs)\n\t{\n\t\t/* Add the single-VALUES expression list */\n\t\tappendContextKeyword(context, \"VALUES (\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\t\tget_rule_list_toplevel(strippedexprs, context, false);\n\t\tappendStringInfoChar(buf, ')');\n\t}\n\telse\n\t{\n\t\t/* No expressions, so it must be DEFAULT VALUES */\n\t\tappendStringInfoString(buf, \"DEFAULT VALUES\");\n\t}\n\n\t/* Add ON CONFLICT if present */\n\tif (query->onConflict)\n\t{\n\t\tOnConflictExpr *confl = query->onConflict;\n\n\t\tappendStringInfoString(buf, \" ON CONFLICT\");\n\n\t\tif (confl->arbiterElems)\n\t\t{\n\t\t\t/* Add the single-VALUES expression list */\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr((Node *) confl->arbiterElems, context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t/* Add a WHERE clause (for partial indexes) if given */\n\t\t\tif (confl->arbiterWhere != NULL)\n\t\t\t{\n\t\t\t\tbool\t\tsave_varprefix;\n\n\t\t\t\t/*\n\t\t\t\t * Force non-prefixing of Vars, since parser assumes that they\n\t\t\t\t * belong to target relation.  WHERE clause does not use\n\t\t\t\t * InferenceElem, so this is separately required.\n\t\t\t\t */\n\t\t\t\tsave_varprefix = context->varprefix;\n\t\t\t\tcontext->varprefix = false;\n\n\t\t\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\t\t\tget_rule_expr(confl->arbiterWhere, context, false);\n\n\t\t\t\tcontext->varprefix = save_varprefix;\n\t\t\t}\n\t\t}\n\t\telse if (OidIsValid(confl->constraint))\n\t\t{\n\t\t\tchar\t   *constraint = get_constraint_name(confl->constraint);\n\t\t\tint64 shardId = context->shardid;\n\n\t\t\tif (shardId > 0)\n\t\t\t{\n\t\t\t\tAppendShardIdToName(&constraint, shardId);\n\t\t\t}\n\n\t\t\tif (!constraint)\n\t\t\t\telog(ERROR, \"cache lookup failed for constraint %u\",\n\t\t\t\t\t confl->constraint);\n\t\t\tappendStringInfo(buf, \" ON CONSTRAINT %s\",\n\t\t\t\t\t\t\t quote_identifier(constraint));\n\t\t}\n\n\t\tif (confl->action == ONCONFLICT_NOTHING)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DO NOTHING\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DO UPDATE SET \");\n\t\t\t/* Deparse targetlist */\n\t\t\tget_update_query_targetlist_def(query, confl->onConflictSet,\n\t\t\t\t\t\t\t\t\t\t\tcontext, rte);\n\n\t\t\t/* Add a WHERE clause if given */\n\t\t\tif (confl->onConflictWhere != NULL)\n\t\t\t{\n\t\t\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\t\t\tget_rule_expr(confl->onConflictWhere, context, false);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Add RETURNING if present */\n\tif (query->returningList)\n\t{\n\t\tappendContextKeyword(context, \" RETURNING\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_target_list(query->returningList, context);\n\t}\n}\n\n/* ----------\n * get_update_query_def\t\t\t- Parse back an UPDATE parsetree\n * ----------\n */\nstatic void\nget_update_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * Start the query with UPDATE relname SET\n\t */\n\trte = rt_fetch(query->resultRelation, query->rtable);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tappendStringInfoChar(buf, ' ');\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t}\n\n\t/* if it's a shard, do differently */\n\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\tchar *fragmentSchemaName = NULL;\n\t\tchar *fragmentTableName = NULL;\n\n\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t/* use schema and table name from the remote alias */\n\t\tappendStringInfo(buf, \"UPDATE %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName, fragmentTableName));\n\n\t\tif(rte->eref != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\tquote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\telse\n\t{\n\t\tappendStringInfo(buf, \"UPDATE %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL));\n\n\t\t/* Print the relation alias, if needed */\n\t\tget_rte_alias(rte, query->resultRelation, false, context);\n\t}\n\n\tappendStringInfoString(buf, \" SET \");\n\n\t/* Deparse targetlist */\n\tget_update_query_targetlist_def(query, query->targetList, context, rte);\n\n\t/* Add the FROM clause if needed */\n\tget_from_clause(query, \" FROM \", context);\n\n\t/* Add a WHERE clause if given */\n\tif (query->jointree->quals != NULL)\n\t{\n\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_expr(query->jointree->quals, context, false);\n\t}\n\n\t/* Add RETURNING if present */\n\tif (query->returningList)\n\t{\n\t\tappendContextKeyword(context, \" RETURNING\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_target_list(query->returningList, context);\n\t}\n}\n\n/* ----------\n * get_update_query_targetlist_def\t\t\t- Parse back an UPDATE targetlist\n * ----------\n */\nstatic void\nget_update_query_targetlist_def(Query *query, List *targetList,\n\t\t\t\t\t\t\t\tdeparse_context *context, RangeTblEntry *rte)\n{\n\tStringInfo\tbuf = context->buf;\n\tListCell   *l;\n\tListCell   *next_ma_cell;\n\tint\t\t\tremaining_ma_columns;\n\tconst char *sep;\n\tSubLink    *cur_ma_sublink;\n\tList\t   *ma_sublinks;\n\n\ttargetList = ExpandMergedSubscriptingRefEntries(targetList);\n\n\t/*\n\t * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks\n\t * into a list.  We expect them to appear, in ID order, in resjunk tlist\n\t * entries.\n\t */\n\tma_sublinks = NIL;\n\tif (query->hasSubLinks)\t\t/* else there can't be any */\n\t{\n\t\tforeach(l, targetList)\n\t\t{\n\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\n\t\t\tif (tle->resjunk && IsA(tle->expr, SubLink))\n\t\t\t{\n\t\t\t\tSubLink    *sl = (SubLink *) tle->expr;\n\n\t\t\t\tif (sl->subLinkType == MULTIEXPR_SUBLINK)\n\t\t\t\t{\n\t\t\t\t\tma_sublinks = lappend(ma_sublinks, sl);\n\t\t\t\t\tAssert(sl->subLinkId == list_length(ma_sublinks));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t        ensure_update_targetlist_in_param_order(targetList);\n\t}\n\tnext_ma_cell = list_head(ma_sublinks);\n\tcur_ma_sublink = NULL;\n\tremaining_ma_columns = 0;\n\n\t/* Add the comma separated list of 'attname = value' */\n\tsep = \"\";\n\tforeach(l, targetList)\n\t{\n\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\t\tNode\t   *expr;\n\n\t\tif (tle->resjunk)\n\t\t\tcontinue;\t\t\t/* ignore junk entries */\n\n\t\t/* Emit separator (OK whether we're in multiassignment or not) */\n\t\tappendStringInfoString(buf, sep);\n\t\tsep = \", \";\n\n\t\t/*\n\t\t * Check to see if we're starting a multiassignment group: if so,\n\t\t * output a left paren.\n\t\t */\n\t\tif (next_ma_cell != NULL && cur_ma_sublink == NULL)\n\t\t{\n\t\t\t/*\n\t\t\t * We must dig down into the expr to see if it's a PARAM_MULTIEXPR\n\t\t\t * Param.  That could be buried under FieldStores and\n\t\t\t * SubscriptingRefs and CoerceToDomains (cf processIndirection()),\n\t\t\t * and underneath those there could be an implicit type coercion.\n\t\t\t * Because we would ignore implicit type coercions anyway, we\n\t\t\t * don't need to be as careful as processIndirection() is about\n\t\t\t * descending past implicit CoerceToDomains.\n\t\t\t */\n\t\t\texpr = (Node *) tle->expr;\n\t\t\twhile (expr)\n\t\t\t{\n\t\t\t\tif (IsA(expr, FieldStore))\n\t\t\t\t{\n\t\t\t\t\tFieldStore *fstore = (FieldStore *) expr;\n\n\t\t\t\t\texpr = (Node *) linitial(fstore->newvals);\n\t\t\t\t}\n\t\t\t\telse if (IsA(expr, SubscriptingRef))\n\t\t\t\t{\n\t\t\t\t\tSubscriptingRef *sbsref = (SubscriptingRef *) expr;\n\n\t\t\t\t\tif (sbsref->refassgnexpr == NULL)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\texpr = (Node *) sbsref->refassgnexpr;\n\t\t\t\t}\n\t\t\t\telse if (IsA(expr, CoerceToDomain))\n\t\t\t\t{\n\t\t\t\t\tCoerceToDomain *cdomain = (CoerceToDomain *) expr;\n\n\t\t\t\t\tif (cdomain->coercionformat != COERCE_IMPLICIT_CAST)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\texpr = (Node *) cdomain->arg;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\texpr = strip_implicit_coercions(expr);\n\n\t\t\tif (expr && IsA(expr, Param) &&\n\t\t\t\t((Param *) expr)->paramkind == PARAM_MULTIEXPR)\n\t\t\t{\n\t\t\t\tcur_ma_sublink = (SubLink *) lfirst(next_ma_cell);\n\t\t\t\tnext_ma_cell = lnext(ma_sublinks, next_ma_cell);\n\t\t\t\tremaining_ma_columns = count_nonjunk_tlist_entries(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ((Query *) cur_ma_sublink->subselect)->targetList);\n\t\t\t\tAssert(((Param *) expr)->paramid ==\n\t\t\t\t\t   ((cur_ma_sublink->subLinkId << 16) | 1));\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Put out name of target column; look in the catalogs, not at\n\t\t * tle->resname, since resname will fail to track RENAME.\n\t\t */\n\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t   quote_identifier(get_attname(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttle->resno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse)));\n\n\t\t/*\n\t\t * Print any indirection needed (subfields or subscripts), and strip\n\t\t * off the top-level nodes representing the indirection assignments.\n\t\t */\n\t\texpr = processIndirection((Node *) tle->expr, context);\n\n\t\t/*\n\t\t * If we're in a multiassignment, skip printing anything more, unless\n\t\t * this is the last column; in which case, what we print should be the\n\t\t * sublink, not the Param.\n\t\t */\n\t\tif (cur_ma_sublink != NULL)\n\t\t{\n\t\t\tif (--remaining_ma_columns > 0)\n\t\t\t\tcontinue;\t\t/* not the last column of multiassignment */\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\texpr = (Node *) cur_ma_sublink;\n\t\t\tcur_ma_sublink = NULL;\n\t\t}\n\n\t\tappendStringInfoString(buf, \" = \");\n\n\t\tget_rule_expr(expr, context, false);\n\t}\n}\n\n/* ----------\n * get_delete_query_def\t\t\t- Parse back a DELETE parsetree\n * ----------\n */\nstatic void\nget_delete_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * Start the query with DELETE FROM relname\n\t */\n\trte = rt_fetch(query->resultRelation, query->rtable);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tappendStringInfoChar(buf, ' ');\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t}\n\n\t/* if it's a shard, do differently */\n\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\tchar *fragmentSchemaName = NULL;\n\t\tchar *fragmentTableName = NULL;\n\n\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t/* use schema and table name from the remote alias */\n\t\tappendStringInfo(buf, \"DELETE FROM %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName, fragmentTableName));\n\n\t\tif(rte->eref != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\tquote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\telse\n\t{\n\t\tappendStringInfo(buf, \"DELETE FROM %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL));\n\n\t\t/* Print the relation alias, if needed */\n\t\tget_rte_alias(rte, query->resultRelation, false, context);\n\t}\n\n\t/* Add the USING clause if given */\n\tget_from_clause(query, \" USING \", context);\n\n\t/* Add a WHERE clause if given */\n\tif (query->jointree->quals != NULL)\n\t{\n\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_expr(query->jointree->quals, context, false);\n\t}\n\n\t/* Add RETURNING if present */\n\tif (query->returningList)\n\t{\n\t\tappendContextKeyword(context, \" RETURNING\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_target_list(query->returningList, context);\n\t}\n}\n\n\n/* ----------\n * get_merge_query_def\t\t\t\t- Parse back a MERGE parsetree\n * ----------\n */\nstatic void\nget_merge_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\tListCell   *lc;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * Start the query with MERGE INTO relname\n\t */\n\trte = ExtractResultRelationRTE(query);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tappendStringInfoChar(buf, ' ');\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t}\n\n\t/* if it's a shard, do differently */\n\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\tchar *fragmentSchemaName = NULL;\n\t\tchar *fragmentTableName = NULL;\n\n\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t/* use schema and table name from the remote alias */\n\t\tappendStringInfo(buf, \"MERGE INTO %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName, fragmentTableName));\n\n\t\tif(rte->eref != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\tquote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\telse\n\t{\n\t\tappendStringInfo(buf, \"MERGE INTO %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\tcontext->distrelid,\n\t\t\t\t\t\t\t\t\t\tcontext->shardid, NIL));\n\n\t\tif (rte->alias != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\t\t\t quote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\n\t/* Print the source relation and join clause */\n\tget_from_clause(query, \" USING \", context);\n\tappendContextKeyword(context, \" ON \",\n\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\tget_rule_expr(query->jointree->quals, context, false);\n\n\t/* Print each merge action */\n\tforeach(lc, query->mergeActionList)\n\t{\n\t\tMergeAction *action = lfirst_node(MergeAction, lc);\n\n\t\tappendContextKeyword(context, \" WHEN \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\t\tappendStringInfo(buf, \"%sMATCHED\", action->matched ? \"\" : \"NOT \");\n\n\t\tif (action->qual)\n\t\t{\n\t\t\tappendContextKeyword(context, \" AND \",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);\n\t\t\tget_rule_expr(action->qual, context, false);\n\t\t}\n\t\tappendContextKeyword(context, \" THEN \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);\n\n\t\tif (action->commandType == CMD_INSERT)\n\t\t{\n\t\t\t/* This generally matches get_insert_query_def() */\n\t\t\tList\t   *strippedexprs = NIL;\n\t\t\tconst char *sep = \"\";\n\t\t\tListCell   *lc2;\n\n\t\t\tappendStringInfoString(buf, \"INSERT\");\n\n\t\t\tif (action->targetList)\n\t\t\t\tappendStringInfoString(buf, \" (\");\n\t\t\tforeach(lc2, action->targetList)\n\t\t\t{\n\t\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(lc2);\n\n\t\t\t\tAssert(!tle->resjunk);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tsep = \", \";\n\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(get_attname(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttle->resno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse)));\n\t\t\t\tstrippedexprs = lappend(strippedexprs,\n\t\t\t\t\t\t\t\t\t\tprocessIndirection((Node *) tle->expr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   context));\n\t\t\t}\n\t\t\tif (action->targetList)\n\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\tif (action->override)\n\t\t\t{\n\t\t\t\tif (action->override == OVERRIDING_SYSTEM_VALUE)\n\t\t\t\t\tappendStringInfoString(buf, \" OVERRIDING SYSTEM VALUE\");\n\t\t\t\telse if (action->override == OVERRIDING_USER_VALUE)\n\t\t\t\t\tappendStringInfoString(buf, \" OVERRIDING USER VALUE\");\n\t\t\t}\n\n\t\t\tif (strippedexprs)\n\t\t\t{\n\t\t\t\tappendContextKeyword(context, \" VALUES (\",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 4);\n\t\t\t\tget_rule_list_toplevel(strippedexprs, context, false);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\telse\n\t\t\t\tappendStringInfoString(buf, \" DEFAULT VALUES\");\n\t\t}\n\t\telse if (action->commandType == CMD_UPDATE)\n\t\t{\n\t\t\tappendStringInfoString(buf, \"UPDATE SET \");\n\t\t\tget_update_query_targetlist_def(query, action->targetList,\n\t\t\t\t\t\t\t\t\t\t\tcontext, rte);\n\t\t}\n\t\telse if (action->commandType == CMD_DELETE)\n\t\t\tappendStringInfoString(buf, \"DELETE\");\n\t\telse if (action->commandType == CMD_NOTHING)\n\t\t\tappendStringInfoString(buf, \"DO NOTHING\");\n\t}\n\n\t/* No RETURNING support in MERGE yet */\n\tAssert(query->returningList == NIL);\n\n\tereport(DEBUG1, (errmsg(\"<Deparsed MERGE query: %s>\", buf->data)));\n}\n\n\n/* ----------\n * get_utility_query_def\t\t\t- Parse back a UTILITY parsetree\n * ----------\n */\nstatic void\nget_utility_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))\n\t{\n\t\tNotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;\n\n\t\tappendContextKeyword(context, \"\",\n\t\t\t\t\t\t\t 0, PRETTYINDENT_STD, 1);\n\t\tappendStringInfo(buf, \"NOTIFY %s\",\n\t\t\t\t\t\t quote_identifier(stmt->conditionname));\n\t\tif (stmt->payload)\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tsimple_quote_literal(buf, stmt->payload);\n\t\t}\n\t}\n\telse if (query->utilityStmt && IsA(query->utilityStmt, TruncateStmt))\n\t{\n\t\tTruncateStmt *stmt = (TruncateStmt *) query->utilityStmt;\n\t\tList *relationList = stmt->relations;\n\t\tListCell *relationCell = NULL;\n\n\t\tappendContextKeyword(context, \"\",\n\t\t\t\t\t\t\t 0, PRETTYINDENT_STD, 1);\n\n\t\tappendStringInfo(buf, \"TRUNCATE TABLE\");\n\n\t\tforeach(relationCell, relationList)\n\t\t{\n\t\t\tRangeVar *relationVar = (RangeVar *) lfirst(relationCell);\n\t\t\tOid relationId = RangeVarGetRelid(relationVar, NoLock, false);\n\t\t\tchar *relationName = generate_relation_or_shard_name(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL);\n\t\t\tappendStringInfo(buf, \" %s\", relationName);\n\n\t\t\tif (lnext(relationList, relationCell) != NULL)\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \",\");\n\t\t\t}\n\t\t}\n\n\t\tif (stmt->restart_seqs)\n\t\t{\n\t\t\tappendStringInfo(buf, \" RESTART IDENTITY\");\n\t\t}\n\n\t\tif (stmt->behavior == DROP_CASCADE)\n\t\t{\n\t\t\tappendStringInfo(buf, \" CASCADE\");\n\t\t}\n\t}\n\telse\n\t{\n\t\t/* Currently only NOTIFY utility commands can appear in rules */\n\t\telog(ERROR, \"unexpected utility statement type\");\n\t}\n}\n\n/*\n * Display a Var appropriately.\n *\n * In some cases (currently only when recursing into an unnamed join)\n * the Var's varlevelsup has to be interpreted with respect to a context\n * above the current one; levelsup indicates the offset.\n *\n * If istoplevel is true, the Var is at the top level of a SELECT's\n * targetlist, which means we need special treatment of whole-row Vars.\n * Instead of the normal \"tab.*\", we'll print \"tab.*::typename\", which is a\n * dirty hack to prevent \"tab.*\" from being expanded into multiple columns.\n * (The parser will strip the useless coercion, so no inefficiency is added in\n * dump and reload.)  We used to print just \"tab\" in such cases, but that is\n * ambiguous and will yield the wrong result if \"tab\" is also a plain column\n * name in the query.\n *\n * Returns the attname of the Var, or NULL if the Var has no attname (because\n * it is a whole-row Var or a subplan output reference).\n */\nstatic char *\nget_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\tAttrNumber\tattnum;\n\tint\t\tvarno;\n\tAttrNumber\tvarattno;\n\tint\t\t\tnetlevelsup;\n\tdeparse_namespace *dpns;\n\tdeparse_columns *colinfo;\n\tchar\t   *refname;\n\tchar\t   *attname;\n\tbool\t\tneed_prefix;\n\n\t/* Find appropriate nesting depth */\n\tnetlevelsup = var->varlevelsup + levelsup;\n\tif (netlevelsup >= list_length(context->namespaces))\n\t\telog(ERROR, \"bogus varlevelsup: %d offset %d\",\n\t\t\t var->varlevelsup, levelsup);\n\tdpns = (deparse_namespace *) list_nth(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t  netlevelsup);\n\n\tvarno = var->varno;\n\tvarattno = var->varattno;\n\n\n\tif (var->varnosyn > 0 && var->varnosyn <= list_length(dpns->rtable) && dpns->plan == NULL) {\n\t\trte = rt_fetch(var->varnosyn, dpns->rtable);\n\n\t\t/*\n\t\t * if the rte var->varnosyn points to is not a regular table and it is a join\n\t\t * then the correct relname will be found with var->varnosyn and var->varattnosyn\n\t\t */\n\t\tif (rte->rtekind == RTE_JOIN && rte->relid == 0 && var->varnosyn != var->varno) {\n\t\t\tvarno = var->varnosyn;\n\t\t\tvarattno = var->varattnosyn;\n\t\t}\n\t}\n\n\t/*\n\t * Try to find the relevant RTE in this rtable.  In a plan tree, it's\n\t * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig\n\t * down into the subplans, or INDEX_VAR, which is resolved similarly. Also\n\t * find the aliases previously assigned for this RTE.\n\t */\n\tif (varno >= 1 && varno <= list_length(dpns->rtable))\n\t{\n\n\t\t/*\n\t\t * We might have been asked to map child Vars to some parent relation.\n\t\t */\n\t\tif (context->appendparents && dpns->appendrels)\n\t\t{\n\n\t\t\tint\t\tpvarno = varno;\n\t\t\tAttrNumber\tpvarattno = varattno;\n\t\t\tAppendRelInfo *appinfo = dpns->appendrels[pvarno];\n\t\t\tbool\t\tfound = false;\n\n\t\t\t/* Only map up to inheritance parents, not UNION ALL appendrels */\n\t\t\twhile (appinfo &&\n\t\t\t\t   rt_fetch(appinfo->parent_relid,\n\t\t\t\t\t\t\tdpns->rtable)->rtekind == RTE_RELATION)\n\t\t\t{\n\t\t\t\tfound = false;\n\t\t\t\tif (pvarattno > 0)\t/* system columns stay as-is */\n\t\t\t\t{\n\t\t\t\t\tif (pvarattno > appinfo->num_child_cols)\n\t\t\t\t\t\tbreak;\t/* safety check */\n\t\t\t\t\tpvarattno = appinfo->parent_colnos[pvarattno - 1];\n\t\t\t\t\tif (pvarattno == 0)\n\t\t\t\t\t\tbreak;\t/* Var is local to child */\n\t\t\t\t}\n\n\t\t\t\tpvarno = appinfo->parent_relid;\n\t\t\t\tfound = true;\n\n\t\t\t\t/* If the parent is itself a child, continue up. */\n\t\t\t\tAssert(pvarno > 0 && pvarno <= list_length(dpns->rtable));\n\t\t\t\tappinfo = dpns->appendrels[pvarno];\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If we found an ancestral rel, and that rel is included in\n\t\t\t * appendparents, print that column not the original one.\n\t\t\t */\n\t\t\tif (found && bms_is_member(pvarno, context->appendparents))\n\t\t\t{\n\t\t\t\tvarno = pvarno;\n\t\t\t\tvarattno = pvarattno;\n\t\t\t}\n\t\t}\n\n\t\trte = rt_fetch(varno, dpns->rtable);\n\t\trefname = (char *) list_nth(dpns->rtable_names, varno - 1);\n\t\tcolinfo = deparse_columns_fetch(varno, dpns);\n\t\tattnum = varattno;\n\t}\n\telse\n\t{\n\t\tresolve_special_varno((Node *) var, context, get_special_variable,\n\t\t\t\t\t\t\t  NULL);\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * The planner will sometimes emit Vars referencing resjunk elements of a\n\t * subquery's target list (this is currently only possible if it chooses\n\t * to generate a \"physical tlist\" for a SubqueryScan or CteScan node).\n\t * Although we prefer to print subquery-referencing Vars using the\n\t * subquery's alias, that's not possible for resjunk items since they have\n\t * no alias.  So in that case, drill down to the subplan and print the\n\t * contents of the referenced tlist item.  This works because in a plan\n\t * tree, such Vars can only occur in a SubqueryScan or CteScan node, and\n\t * we'll have set dpns->inner_plan to reference the child plan node.\n\t */\n\tif ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&\n\t\tattnum > list_length(rte->eref->colnames) &&\n\t\tdpns->inner_plan)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\n\t\ttle = get_tle_by_resno(dpns->inner_tlist, attnum);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"invalid attnum %d for relation \\\"%s\\\"\",\n\t\t\t\t attnum, rte->eref->aliasname);\n\n\t\tAssert(netlevelsup == 0);\n\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\t/*\n\t\t * Force parentheses because our caller probably assumed a Var is a\n\t\t * simple expression.\n\t\t */\n\t\tif (!IsA(tle->expr, Var))\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_rule_expr((Node *) tle->expr, context, true);\n\t\tif (!IsA(tle->expr, Var))\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * If it's an unnamed join, look at the expansion of the alias variable.\n\t * If it's a simple reference to one of the input vars, then recursively\n\t * print the name of that var instead.  When it's not a simple reference,\n\t * we have to just print the unqualified join column name.  (This can only\n\t * happen with \"dangerous\" merged columns in a JOIN USING; we took pains\n\t * previously to make the unqualified column name unique in such cases.)\n\t *\n\t * This wouldn't work in decompiling plan trees, because we don't store\n\t * joinaliasvars lists after planning; but a plan tree should never\n\t * contain a join alias variable.\n\t */\n\tif (rte->rtekind == RTE_JOIN && rte->alias == NULL)\n\t{\n\t\tif (rte->joinaliasvars == NIL)\n\t\t\telog(ERROR, \"cannot decompile join alias var in plan tree\");\n\t\tif (attnum > 0)\n\t\t{\n\t\t\tVar\t\t   *aliasvar;\n\n\t\t\taliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);\n\t\t\t/* we intentionally don't strip implicit coercions here */\n\t\t\tif (aliasvar && IsA(aliasvar, Var))\n\t\t\t{\n\t\t\t\treturn get_variable(aliasvar, var->varlevelsup + levelsup,\n\t\t\t\t\t\t\t\t\tistoplevel, context);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Unnamed join has no refname.  (Note: since it's unnamed, there is\n\t\t * no way the user could have referenced it to create a whole-row Var\n\t\t * for it.  So we don't have to cover that case below.)\n\t\t */\n\t\tAssert(refname == NULL);\n\t}\n\n\tif (attnum == InvalidAttrNumber)\n\t\tattname = NULL;\n\telse if (attnum > 0)\n\t{\n\t\t/* Get column name to use from the colinfo struct */\n\t\tif (attnum > colinfo->num_cols)\n\t\t\telog(ERROR, \"invalid attnum %d for relation \\\"%s\\\"\",\n\t\t\t\t attnum, rte->eref->aliasname);\n\t\tattname = colinfo->colnames[attnum - 1];\n\n\t\t/*\n\t\t * If we find a Var referencing a dropped column, it seems better to\n\t\t * print something (anything) than to fail.  In general this should\n\t\t * not happen, but it used to be possible for some cases involving\n\t\t * functions returning named composite types, and perhaps there are\n\t\t * still bugs out there.\n\t\t */\n\t\tif (attname == NULL)\n\t\t\tattname = \"?dropped?column?\";\n\t}\n\telse if (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\t/* System column on a Citus shard */\n\t\tattname = get_attname(rte->relid, attnum, false);\n\t}\n\telse\n\t{\n\t\t/* System column - name is fixed, get it from the catalog */\n\t\tattname = get_rte_attribute_name(rte, attnum);\n\t}\n\n\tneed_prefix = (context->varprefix || attname == NULL);\n\t/*\n\t * If we're considering a plain Var in an ORDER BY (but not GROUP BY)\n\t * clause, we may need to add a table-name prefix to prevent\n\t * findTargetlistEntrySQL92 from misinterpreting the name as an\n\t * output-column name.  To avoid cluttering the output with unnecessary\n\t * prefixes, do so only if there is a name match to a SELECT tlist item\n\t * that is different from the Var.\n\t */\n\tif (context->varInOrderBy && !context->inGroupBy && !need_prefix)\n\t{\n\t\tint\t\t\tcolno = 0;\n\t\tListCell   *l;\n\t\tforeach(l, context->targetList)\n\t\t{\n\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\t\t\tchar\t   *colname;\n\t\t\tif (tle->resjunk)\n\t\t\t\tcontinue;\t\t/* ignore junk entries */\n\t\t\tcolno++;\n\t\t\t/* This must match colname-choosing logic in get_target_list() */\n\t\t\tif (context->resultDesc && colno <= context->resultDesc->natts)\n\t\t\t\tcolname = NameStr(TupleDescAttr(context->resultDesc,\n\t\t\t\t\t\t\t\t\t\t\t\tcolno - 1)->attname);\n\t\t\telse\n\t\t\t\tcolname = tle->resname;\n\t\t\tif (colname && strcmp(colname, attname) == 0 &&\n\t\t\t\t!equal(var, tle->expr))\n\t\t\t{\n\t\t\t\tneed_prefix = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (refname && need_prefix)\n\t{\n\t\tappendStringInfoString(buf, quote_identifier(refname));\n\t\tappendStringInfoChar(buf, '.');\n\t}\n\tif (attname)\n\t\tappendStringInfoString(buf, quote_identifier(attname));\n\telse\n\t{\n\t\tappendStringInfoChar(buf, '*');\n\n\t\tif (istoplevel)\n\t\t{\n\t\t\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t\t\t{\n\t\t\t\t/* use rel.*::shard_name instead of rel.*::table_name */\n\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t generate_rte_shard_name(rte));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t format_type_with_typemod(var->vartype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  var->vartypmod));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn attname;\n}\n\n/*\n * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR.  This\n * routine is actually a callback for get_special_varno, which handles finding\n * the correct TargetEntry.  We get the expression contained in that\n * TargetEntry and just need to deparse it, a job we can throw back on\n * get_rule_expr.\n */\nstatic void\nget_special_variable(Node *node, deparse_context *context, void *callback_arg)\n{\n\tStringInfo\tbuf = context->buf;\n\n\t/*\n\t * For a non-Var referent, force parentheses because our caller probably\n\t *  assumed a Var is a simple expression.\n\t */\n\tif (!IsA(node, Var))\n\t\tappendStringInfoChar(buf, '(');\n\tget_rule_expr(node, context, true);\n\tif (!IsA(node, Var))\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,\n * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,\n * invoke the callback provided.\n */\nstatic void\nresolve_special_varno(Node *node, deparse_context *context, rsv_callback callback, void *callback_arg)\n{\n\tVar\t\t   *var;\n\tdeparse_namespace *dpns;\n\n\t/* This function is recursive, so let's be paranoid. */\n\tcheck_stack_depth();\n\n\t/* If it's not a Var, invoke the callback. */\n\tif (!IsA(node, Var))\n\t{\n\t\t(*callback) (node, context, callback_arg);\n\t\treturn;\n\t}\n\n\t/* Find appropriate nesting depth */\n\tvar = (Var *) node;\n\tdpns = (deparse_namespace *) list_nth(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t  var->varlevelsup);\n\n\t/*\n\t * It's a special RTE, so recurse.\n\t */\n\tif (var->varno == OUTER_VAR && dpns->outer_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\t\tBitmapset  *save_appendparents;\n\n\t\ttle = get_tle_by_resno(dpns->outer_tlist, var->varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for OUTER_VAR var: %d\", var->varattno);\n\n\t\t/* If we're descending to the first child of an Append or MergeAppend,\n\t\t * update appendparents.  This will affect deparsing of all Vars\n\t\t * appearing within the eventually-resolved subexpression.\n\t\t */\n\t\tsave_appendparents = context->appendparents;\n\n\t\tif (IsA(dpns->plan, Append))\n\t\t\tcontext->appendparents = bms_union(context->appendparents,\n\t\t\t\t\t\t\t\t\t\t\t   ((Append *) dpns->plan)->apprelids);\n\t\telse if (IsA(dpns->plan, MergeAppend))\n\t\t\tcontext->appendparents = bms_union(context->appendparents,\n\t\t\t\t\t\t\t\t\t\t\t   ((MergeAppend *) dpns->plan)->apprelids);\n\n\t\tpush_child_plan(dpns, dpns->outer_plan, &save_dpns);\n\t\tresolve_special_varno((Node *) tle->expr, context,\n\t\t\t\t\t\t\t  callback, callback_arg);\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\tcontext->appendparents = save_appendparents;\n\t\treturn;\n\t}\n\telse if (var->varno == INNER_VAR && dpns->inner_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\n\t\ttle = get_tle_by_resno(dpns->inner_tlist, var->varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INNER_VAR var: %d\", var->varattno);\n\n\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\t\tresolve_special_varno((Node *) tle->expr, context, callback, callback_arg);\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn;\n\t}\n\telse if (var->varno == INDEX_VAR && dpns->index_tlist)\n\t{\n\t\tTargetEntry *tle;\n\n\t\ttle = get_tle_by_resno(dpns->index_tlist, var->varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INDEX_VAR var: %d\", var->varattno);\n\n\t\tresolve_special_varno((Node *) tle->expr, context, callback, callback_arg);\n\t\treturn;\n\t}\n\telse if (var->varno < 1 || var->varno > list_length(dpns->rtable))\n\t\telog(ERROR, \"bogus varno: %d\", var->varno);\n\n\t/* Not special.  Just invoke the callback. */\n\t(*callback) (node, context, callback_arg);\n}\n\n/*\n * Get the name of a field of an expression of composite type.  The\n * expression is usually a Var, but we handle other cases too.\n *\n * levelsup is an extra offset to interpret the Var's varlevelsup correctly.\n *\n * This is fairly straightforward when the expression has a named composite\n * type; we need only look up the type in the catalogs.  However, the type\n * could also be RECORD.  Since no actual table or view column is allowed to\n * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE\n * or to a subquery output.  We drill down to find the ultimate defining\n * expression and attempt to infer the field name from it.  We ereport if we\n * can't determine the name.\n *\n * Similarly, a PARAM of type RECORD has to refer to some expression of\n * a determinable composite type.\n */\nstatic const char *\nget_name_for_var_field(Var *var, int fieldno,\n\t\t\t\t\t   int levelsup, deparse_context *context)\n{\n\tRangeTblEntry *rte;\n\tAttrNumber\tattnum;\n\tint\t\t\tnetlevelsup;\n\tdeparse_namespace *dpns;\n\tint\t\tvarno;\n\tAttrNumber\tvarattno;\n\tTupleDesc\ttupleDesc;\n\tNode\t   *expr;\n\n\t/*\n\t * If it's a RowExpr that was expanded from a whole-row Var, use the\n\t * column names attached to it.  (We could let get_expr_result_tupdesc()\n\t * handle this, but it's much cheaper to just pull out the name we need.)\n\t */\n\tif (IsA(var, RowExpr))\n\t{\n\t\tRowExpr    *r = (RowExpr *) var;\n\n\t\tif (fieldno > 0 && fieldno <= list_length(r->colnames))\n\t\t\treturn strVal(list_nth(r->colnames, fieldno - 1));\n\t}\n\n\t/*\n\t * If it's a Param of type RECORD, try to find what the Param refers to.\n\t */\n\tif (IsA(var, Param))\n\t{\n\t\tParam\t   *param = (Param *) var;\n\t\tListCell   *ancestor_cell;\n\n\t\texpr = find_param_referent(param, context, &dpns, &ancestor_cell);\n\t\tif (expr)\n\t\t{\n\t\t\t/* Found a match, so recurse to decipher the field name */\n\t\t\tdeparse_namespace save_dpns;\n\t\t\tconst char *result;\n\n\t\t\tpush_ancestor_plan(dpns, ancestor_cell, &save_dpns);\n\t\t\tresult = get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t0, context);\n\t\t\tpop_ancestor_plan(dpns, &save_dpns);\n\t\t\treturn result;\n\t\t}\n\t}\n\n\t/*\n\t * If it's a Var of type RECORD, we have to find what the Var refers to;\n\t * if not, we can use get_expr_result_tupdesc().\n\t */\n\tif (!IsA(var, Var) ||\n\t\tvar->vartype != RECORDOID)\n\t{\n\t\ttupleDesc = get_expr_result_tupdesc((Node *) var, false);\n\t\t/* Got the tupdesc, so we can extract the field name */\n\t\tAssert(fieldno >= 1 && fieldno <= tupleDesc->natts);\n\t\treturn NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);\n\t}\n\n\t/* Find appropriate nesting depth */\n\tnetlevelsup = var->varlevelsup + levelsup;\n\tif (netlevelsup >= list_length(context->namespaces))\n\t\telog(ERROR, \"bogus varlevelsup: %d offset %d\",\n\t\t\t var->varlevelsup, levelsup);\n\tdpns = (deparse_namespace *) list_nth(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t  netlevelsup);\n\n\tvarno = var->varno;\n\tvarattno = var->varattno;\n\n\tif (var->varnosyn > 0 && var->varnosyn <= list_length(dpns->rtable) && dpns->plan == NULL) {\n\t\trte = rt_fetch(var->varnosyn, dpns->rtable);\n\n\t\t/*\n\t\t * if the rte var->varnosyn points to is not a regular table and it is a join\n\t\t * then the correct relname will be found with var->varnosyn and var->varattnosyn\n\t\t */\n\t\tif (rte->rtekind == RTE_JOIN && rte->relid == 0 && var->varnosyn != var->varno) {\n\t\t\tvarno = var->varnosyn;\n\t\t\tvarattno = var->varattnosyn;\n\t\t}\n\t}\n\n\t/*\n\t * Try to find the relevant RTE in this rtable.  In a plan tree, it's\n\t * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig\n\t * down into the subplans, or INDEX_VAR, which is resolved similarly.\n\t */\n\tif (varno >= 1 && varno <= list_length(dpns->rtable))\n\t{\n\t\trte = rt_fetch(varno, dpns->rtable);\n\t\tattnum = varattno;\n\t}\n\telse if (varno == OUTER_VAR && dpns->outer_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\t\tconst char *result;\n\n\t\ttle = get_tle_by_resno(dpns->outer_tlist, varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for OUTER_VAR var: %d\", varattno);\n\n\t\tAssert(netlevelsup == 0);\n\t\tpush_child_plan(dpns, dpns->outer_plan, &save_dpns);\n\n\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn result;\n\t}\n\telse if (varno == INNER_VAR && dpns->inner_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\t\tconst char *result;\n\n\t\ttle = get_tle_by_resno(dpns->inner_tlist, varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INNER_VAR var: %d\", varattno);\n\n\t\tAssert(netlevelsup == 0);\n\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn result;\n\t}\n\telse if (varno == INDEX_VAR && dpns->index_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tconst char *result;\n\n\t\ttle = get_tle_by_resno(dpns->index_tlist, varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INDEX_VAR var: %d\", varattno);\n\n\t\tAssert(netlevelsup == 0);\n\n\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\treturn result;\n\t}\n\telse\n\t{\n\t\telog(ERROR, \"bogus varno: %d\", varno);\n\t\treturn NULL;\t\t\t/* keep compiler quiet */\n\t}\n\n\tif (attnum == InvalidAttrNumber)\n\t{\n\t\t/* Var is whole-row reference to RTE, so select the right field */\n\t\treturn get_rte_attribute_name(rte, fieldno);\n\t}\n\n\t/*\n\t * This part has essentially the same logic as the parser's\n\t * expandRecordVariable() function, but we are dealing with a different\n\t * representation of the input context, and we only need one field name\n\t * not a TupleDesc.  Also, we need special cases for finding subquery and\n\t * CTE subplans when deparsing Plan trees.\n\t */\n\texpr = (Node *) var;\t\t/* default if we can't drill down */\n\n\tswitch (rte->rtekind)\n\t{\n\t\tcase RTE_RELATION:\n\t\tcase RTE_VALUES:\n\t\tcase RTE_NAMEDTUPLESTORE:\n\t\tcase RTE_RESULT:\n\n\t\t\t/*\n\t\t\t * This case should not occur: a column of a table or values list\n\t\t\t * shouldn't have type RECORD.  Fall through and fail (most\n\t\t\t * likely) at the bottom.\n\t\t\t */\n\t\t\tbreak;\n\t\tcase RTE_SUBQUERY:\n\t\t\t/* Subselect-in-FROM: examine sub-select's output expr */\n\t\t\t{\n\t\t\t\tif (rte->subquery)\n\t\t\t\t{\n\t\t\t\t\tTargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tattnum);\n\n\t\t\t\t\tif (ste == NULL || ste->resjunk)\n\t\t\t\t\t\telog(ERROR, \"subquery %s does not have attribute %d\",\n\t\t\t\t\t\t\t rte->eref->aliasname, attnum);\n\t\t\t\t\texpr = (Node *) ste->expr;\n\t\t\t\t\tif (IsA(expr, Var))\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Recurse into the sub-select to see what its Var\n\t\t\t\t\t\t * refers to. We have to build an additional level of\n\t\t\t\t\t\t * namespace to keep in step with varlevelsup in the\n\t\t\t\t\t\t * subselect.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tdeparse_namespace mydpns;\n\t\t\t\t\t\tconst char *result;\n\n\t\t\t\t\t\tset_deparse_for_query(&mydpns, rte->subquery,\n\t\t\t\t\t\t\t\t\t\t\t  context->namespaces);\n\n\t\t\t\t\t\tcontext->namespaces = lcons(&mydpns,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcontext->namespaces);\n\n\t\t\t\t\t\tresult = get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t0, context);\n\n\t\t\t\t\t\tcontext->namespaces =\n\t\t\t\t\t\t\tlist_delete_first(context->namespaces);\n\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\t\t\t\t\t/* else fall through to inspect the expression */\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * We're deparsing a Plan tree so we don't have complete\n\t\t\t\t\t * RTE entries (in particular, rte->subquery is NULL). But\n\t\t\t\t\t * the only place we'd see a Var directly referencing a\n\t\t\t\t\t * SUBQUERY RTE is in a SubqueryScan plan node, and we can\n\t\t\t\t\t * look into the child plan's tlist instead.\n\t\t\t\t\t */\n\t\t\t\t\tTargetEntry *tle;\n\t\t\t\t\tdeparse_namespace save_dpns;\n\t\t\t\t\tconst char *result;\n\n\t\t\t\t\tif (!dpns->inner_plan)\n\t\t\t\t\t\telog(ERROR, \"failed to find plan for subquery %s\",\n\t\t\t\t\t\t\t rte->eref->aliasname);\n\t\t\t\t\ttle = get_tle_by_resno(dpns->inner_tlist, attnum);\n\t\t\t\t\tif (!tle)\n\t\t\t\t\t\telog(ERROR, \"bogus varattno for subquery var: %d\",\n\t\t\t\t\t\t\t attnum);\n\t\t\t\t\tAssert(netlevelsup == 0);\n\t\t\t\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\t\t\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\t\t\t\tpop_child_plan(dpns, &save_dpns);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase RTE_JOIN:\n\t\t\t/* Join RTE --- recursively inspect the alias variable */\n\t\t\tif (rte->joinaliasvars == NIL)\n\t\t\t\telog(ERROR, \"cannot decompile join alias var in plan tree\");\n\t\t\tAssert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));\n\t\t\texpr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);\n\t\t\tAssert(expr != NULL);\n\t\t\t/* we intentionally don't strip implicit coercions here */\n\t\t\tif (IsA(expr, Var))\n\t\t\t\treturn get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t  var->varlevelsup + levelsup,\n\t\t\t\t\t\t\t\t\t\t\t  context);\n\t\t\t/* else fall through to inspect the expression */\n\t\t\tbreak;\n\t\tcase RTE_FUNCTION:\n\t\tcase RTE_TABLEFUNC:\n\n\t\t\t/*\n\t\t\t * We couldn't get here unless a function is declared with one of\n\t\t\t * its result columns as RECORD, which is not allowed.\n\t\t\t */\n\t\t\tbreak;\n\t\tcase RTE_CTE:\n\t\t\t/* CTE reference: examine subquery's output expr */\n\t\t\t{\n\t\t\t\tCommonTableExpr *cte = NULL;\n\t\t\t\tIndex\t\tctelevelsup;\n\t\t\t\tListCell   *lc;\n\n\t\t\t\t/*\n\t\t\t\t * Try to find the referenced CTE using the namespace stack.\n\t\t\t\t */\n\t\t\t\tctelevelsup = rte->ctelevelsup + netlevelsup;\n\t\t\t\tif (ctelevelsup >= list_length(context->namespaces))\n\t\t\t\t\tlc = NULL;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdeparse_namespace *ctedpns;\n\n\t\t\t\t\tctedpns = (deparse_namespace *)\n\t\t\t\t\t\tlist_nth(context->namespaces, ctelevelsup);\n\t\t\t\t\tforeach(lc, ctedpns->ctes)\n\t\t\t\t\t{\n\t\t\t\t\t\tcte = (CommonTableExpr *) lfirst(lc);\n\t\t\t\t\t\tif (strcmp(cte->ctename, rte->ctename) == 0)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (lc != NULL)\n\t\t\t\t{\n\t\t\t\t\tQuery\t   *ctequery = (Query *) cte->ctequery;\n\t\t\t\t\tTargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tattnum);\n\n\t\t\t\t\tif (ste == NULL || ste->resjunk)\n\t\t\t\t\t\telog(ERROR, \"subquery %s does not have attribute %d\",\n\t\t\t\t\t\t\t rte->eref->aliasname, attnum);\n\t\t\t\t\texpr = (Node *) ste->expr;\n\t\t\t\t\tif (IsA(expr, Var))\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Recurse into the CTE to see what its Var refers to.\n\t\t\t\t\t\t * We have to build an additional level of namespace\n\t\t\t\t\t\t * to keep in step with varlevelsup in the CTE.\n\t\t\t\t\t\t * Furthermore it could be an outer CTE, so we may\n\t\t\t\t\t\t * have to delete some levels of namespace.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tList\t   *save_nslist = context->namespaces;\n\t\t\t\t\t\tList\t   *new_nslist;\n\t\t\t\t\t\tdeparse_namespace mydpns;\n\t\t\t\t\t\tconst char *result;\n\n\t\t\t\t\t\tset_deparse_for_query(&mydpns, ctequery,\n\t\t\t\t\t\t\t\t\t\t\t  context->namespaces);\n\n\t\t\t\t\t\tnew_nslist = list_copy_tail(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t\t\t\tctelevelsup);\n\t\t\t\t\t\tcontext->namespaces = lcons(&mydpns, new_nslist);\n\n\t\t\t\t\t\tresult = get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t0, context);\n\n\t\t\t\t\t\tcontext->namespaces = save_nslist;\n\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\t\t\t\t\t/* else fall through to inspect the expression */\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * We're deparsing a Plan tree so we don't have a CTE\n\t\t\t\t\t * list.  But the only places we'd see a Var directly\n                     * referencing a CTE RTE are in CteScan or WorkTableScan\n\t\t\t\t\t * plan nodes.  For those cases, set_deparse_plan arranged\n\t\t\t\t\t * for dpns->inner_plan to be the plan node that emits the\n\t\t\t\t\t * CTE or RecursiveUnion result, and we can look at its\n\t\t\t\t\t * tlist instead.\n\t\t\t\t\t */\n\t\t\t\t\tTargetEntry *tle;\n\t\t\t\t\tdeparse_namespace save_dpns;\n\t\t\t\t\tconst char *result;\n\n\t\t\t\t\tif (!dpns->inner_plan)\n\t\t\t\t\t\telog(ERROR, \"failed to find plan for CTE %s\",\n\t\t\t\t\t\t\t rte->eref->aliasname);\n\t\t\t\t\ttle = get_tle_by_resno(dpns->inner_tlist, attnum);\n\t\t\t\t\tif (!tle)\n\t\t\t\t\t\telog(ERROR, \"bogus varattno for subquery var: %d\",\n\t\t\t\t\t\t\t attnum);\n\t\t\t\t\tAssert(netlevelsup == 0);\n\t\t\t\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\t\t\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\t\t\t\tpop_child_plan(dpns, &save_dpns);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t}\n\n\t/*\n\t * We now have an expression we can't expand any more, so see if\n\t * get_expr_result_tupdesc() can do anything with it.\n\t */\n\ttupleDesc = get_expr_result_tupdesc(expr, false);\n\t/* Got the tupdesc, so we can extract the field name */\n\tAssert(fieldno >= 1 && fieldno <= tupleDesc->natts);\n\treturn NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);\n}\n\n/*\n * Try to find the referenced expression for a PARAM_EXEC Param that might\n * reference a parameter supplied by an upper NestLoop or SubPlan plan node.\n *\n * If successful, return the expression and set *dpns_p and *ancestor_cell_p\n * appropriately for calling push_ancestor_plan().  If no referent can be\n * found, return NULL.\n */\nstatic Node *\nfind_param_referent(Param *param, deparse_context *context,\n\t\t\t\t\tdeparse_namespace **dpns_p, ListCell **ancestor_cell_p)\n{\n\t/* Initialize output parameters to prevent compiler warnings */\n\t*dpns_p = NULL;\n\t*ancestor_cell_p = NULL;\n\n\t/*\n\t * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or\n\t * SubPlan argument.  This will necessarily be in some ancestor of the\n\t * current expression's Plan.\n\t */\n\tif (param->paramkind == PARAM_EXEC)\n\t{\n\t\tdeparse_namespace *dpns;\n\t\tPlan  *child_plan;\n\t\tListCell   *lc;\n\n\t\tdpns = (deparse_namespace *) linitial(context->namespaces);\n\t\tchild_plan = dpns->plan;\n\n\t\tforeach(lc, dpns->ancestors)\n\t\t{\n\t\t\tNode\t   *ancestor = (Node *) lfirst(lc);\n\t\t\tListCell   *lc2;\n\n\t\t\t/*\n\t\t\t * NestLoops transmit params to their inner child only.\n\t\t\t */\n\t\t\tif (IsA(ancestor, NestLoop) &&\n\t\t\t\tchild_plan == innerPlan(ancestor))\n\t\t\t{\n\t\t\t\tNestLoop   *nl = (NestLoop *) ancestor;\n\n\t\t\t\tforeach(lc2, nl->nestParams)\n\t\t\t\t{\n\t\t\t\t\tNestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);\n\n\t\t\t\t\tif (nlp->paramno == param->paramid)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Found a match, so return it */\n\t\t\t\t\t\t*dpns_p = dpns;\n\t\t\t\t\t\t*ancestor_cell_p = lc;\n\t\t\t\t\t\treturn (Node *) nlp->paramval;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Check to see if we're crawling up from a subplan.\n\t\t\t */\n\t\t\tif(IsA(ancestor, SubPlan))\n\t\t\t{\n\t\t\t\tSubPlan    *subplan = (SubPlan *) ancestor;\n\t\t\t\tListCell   *lc3;\n\t\t\t\tListCell   *lc4;\n\n\t\t\t\t/* Matched subplan, so check its arguments */\n\t\t\t\tforboth(lc3, subplan->parParam, lc4, subplan->args)\n\t\t\t\t{\n\t\t\t\t\tint\t\t\tparamid = lfirst_int(lc3);\n\t\t\t\t\tNode\t   *arg = (Node *) lfirst(lc4);\n\n\t\t\t\t\tif (paramid == param->paramid)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Found a match, so return it.  But, since Vars in\n\t\t\t\t\t\t * the arg are to be evaluated in the surrounding\n\t\t\t\t\t\t * context, we have to point to the next ancestor item\n\t\t\t\t\t\t * that is *not* a SubPlan.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tListCell   *rest;\n\n\t\t\t\t\t\tfor_each_cell(rest, dpns->ancestors,\n\t\t\t\t\t\t\t\t\t  lnext(dpns->ancestors, lc))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tNode\t   *ancestor2 = (Node *) lfirst(rest);\n\n\t\t\t\t\t\t\tif (!IsA(ancestor2, SubPlan))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t*dpns_p = dpns;\n\t\t\t\t\t\t\t\t*ancestor_cell_p = rest;\n\t\t\t\t\t\t\t\treturn arg;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telog(ERROR, \"SubPlan cannot be outermost ancestor\");\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* SubPlan isn't a kind of Plan, so skip the rest */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * We need not consider the ancestor's initPlan list, since\n\t\t\t * initplans never have any parParams.\n\t\t\t */\n\n\t\t\t/* No luck, crawl up to next ancestor */\n\t\t\tchild_plan = (Plan *) ancestor;\n\t\t}\n\t}\n\n\t/* No referent found */\n\treturn NULL;\n}\n\n/*\n * Display a Param appropriately.\n */\nstatic void\nget_parameter(Param *param, deparse_context *context)\n{\n\tNode\t   *expr;\n\tdeparse_namespace *dpns;\n\tListCell   *ancestor_cell;\n\n\t/*\n\t * If it's a PARAM_EXEC parameter, try to locate the expression from which\n\t * the parameter was computed.  Note that failing to find a referent isn't\n\t * an error, since the Param might well be a subplan output rather than an\n\t * input.\n\t */\n\texpr = find_param_referent(param, context, &dpns, &ancestor_cell);\n\tif (expr)\n\t{\n\t\t/* Found a match, so print it */\n\t\tdeparse_namespace save_dpns;\n\t\tbool\t\tsave_varprefix;\n\t\tbool\t\tneed_paren;\n\n\t\t/* Switch attention to the ancestor plan node */\n\t\tpush_ancestor_plan(dpns, ancestor_cell, &save_dpns);\n\n\t\t/*\n\t\t * Force prefixing of Vars, since they won't belong to the relation\n\t\t * being scanned in the original plan node.\n\t\t */\n\t\tsave_varprefix = context->varprefix;\n\t\tcontext->varprefix = true;\n\n\t\t/*\n\t\t * A Param's expansion is typically a Var, Aggref, GroupingFunc, or\n\t\t * upper-level Param, which wouldn't need extra parentheses.\n\t\t * Otherwise, insert parens to ensure the expression looks atomic.\n\t\t */\n\t\tneed_paren = !(IsA(expr, Var) ||\n\t\t\t\t\t   IsA(expr, Aggref) ||\n                       IsA(expr, GroupingFunc) ||\n\t\t\t\t\t   IsA(expr, Param));\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, '(');\n\n\t\tget_rule_expr(expr, context, false);\n\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, ')');\n\n\t\tcontext->varprefix = save_varprefix;\n\n\t\tpop_ancestor_plan(dpns, &save_dpns);\n\n\t\treturn;\n\t}\n\n\t/*\n\t * If it's an external parameter, see if the outermost namespace provides\n\t * function argument names.\n\t */\n\tif (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)\n\t{\n\t\tdpns = llast(context->namespaces);\n\t\tif (dpns->argnames &&\n\t\t\tparam->paramid > 0 &&\n\t\t\tparam->paramid <= dpns->numargs)\n\t\t{\n\t\t\tchar\t   *argname = dpns->argnames[param->paramid - 1];\n\n\t\t\tif (argname)\n\t\t\t{\n\t\t\t\tbool\t\tshould_qualify = false;\n\t\t\t\tListCell   *lc;\n\n\t\t\t\t/*\n\t\t\t\t * Qualify the parameter name if there are any other deparse\n\t\t\t\t * namespaces with range tables.  This avoids qualifying in\n\t\t\t\t * trivial cases like \"RETURN a + b\", but makes it safe in all\n\t\t\t\t * other cases.\n\t\t\t\t */\n\t\t\t\tforeach(lc, context->namespaces)\n\t\t\t\t{\n\t\t\t\t\tdeparse_namespace *depns = lfirst(lc);\n\n\t\t\t\t\tif (depns->rtable_names != NIL)\n\t\t\t\t\t{\n\t\t\t\t\t\tshould_qualify = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (should_qualify)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(context->buf, quote_identifier(dpns->funcname));\n\t\t\t\t\tappendStringInfoChar(context->buf, '.');\n\t\t\t\t}\n\n\t\t\t\tappendStringInfoString(context->buf, quote_identifier(argname));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * Not PARAM_EXEC, or couldn't find referent: for base types just print $N.\n\t * For composite types, add cast to the parameter to ease remote node detect\n\t * the type.\n\t */\n\tif (param->paramtype >= FirstNormalObjectId)\n\t{\n\t\tchar *typeName = format_type_with_typemod(param->paramtype, param->paramtypmod);\n\n\t\tappendStringInfo(context->buf, \"$%d::%s\", param->paramid, typeName);\n\t}\n\telse\n\t{\n\t\tappendStringInfo(context->buf, \"$%d\", param->paramid);\n\t}\n}\n\n/*\n * get_simple_binary_op_name\n *\n * helper function for isSimpleNode\n * will return single char binary operator name, or NULL if it's not\n */\nstatic const char *\nget_simple_binary_op_name(OpExpr *expr)\n{\n\tList\t   *args = expr->args;\n\n\tif (list_length(args) == 2)\n\t{\n\t\t/* binary operator */\n\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\t\tconst char *op;\n\n\t\top = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));\n\t\tif (strlen(op) == 1)\n\t\t\treturn op;\n\t}\n\treturn NULL;\n}\n\n/*\n * isSimpleNode - check if given node is simple (doesn't need parenthesizing)\n *\n *\ttrue   : simple in the context of parent node's type\n *\tfalse  : not simple\n */\nstatic bool\nisSimpleNode(Node *node, Node *parentNode, int prettyFlags)\n{\n\tif (!node)\n\t\treturn false;\n\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_Var:\n\t\tcase T_Const:\n\t\tcase T_Param:\n\t\tcase T_CoerceToDomainValue:\n\t\tcase T_SetToDefault:\n\t\tcase T_CurrentOfExpr:\n\t\t\t/* single words: always simple */\n\t\t\treturn true;\n\n\t\tcase T_SubscriptingRef:\n\t\tcase T_ArrayExpr:\n\t\tcase T_RowExpr:\n\t\tcase T_CoalesceExpr:\n\t\tcase T_MinMaxExpr:\n\t\tcase T_SQLValueFunction:\n\t\tcase T_XmlExpr:\n\t\tcase T_NextValueExpr:\n\t\tcase T_NullIfExpr:\n\t\tcase T_Aggref:\n        case T_GroupingFunc:\n\t\tcase T_WindowFunc:\n\t\tcase T_FuncExpr:\n\t\tcase T_JsonConstructorExpr:\n\t\t\t/* function-like: name(..) or name[..] */\n\t\t\treturn true;\n\n\t\t\t/* CASE keywords act as parentheses */\n\t\tcase T_CaseExpr:\n\t\t\treturn true;\n\n\t\tcase T_FieldSelect:\n\n\t\t\t/*\n\t\t\t * appears simple since . has top precedence, unless parent is\n\t\t\t * T_FieldSelect itself!\n\t\t\t */\n\t\t\treturn !IsA(parentNode, FieldSelect);\n\n\t\tcase T_FieldStore:\n\n\t\t\t/*\n\t\t\t * treat like FieldSelect (probably doesn't matter)\n\t\t\t */\n\t\t\treturn !IsA(parentNode, FieldStore);\n\n\t\tcase T_CoerceToDomain:\n\t\t\t/* maybe simple, check args */\n\t\t\treturn isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_RelabelType:\n\t\t\treturn isSimpleNode((Node *) ((RelabelType *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_CoerceViaIO:\n\t\t\treturn isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_ArrayCoerceExpr:\n\t\t\treturn isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_ConvertRowtypeExpr:\n\t\t\treturn isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\n\t\tcase T_OpExpr:\n\t\t\t{\n\t\t\t\t/* depends on parent node type; needs further checking */\n\t\t\t\tif (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))\n\t\t\t\t{\n\t\t\t\t\tconst char *op;\n\t\t\t\t\tconst char *parentOp;\n\t\t\t\t\tbool\t\tis_lopriop;\n\t\t\t\t\tbool\t\tis_hipriop;\n\t\t\t\t\tbool\t\tis_lopriparent;\n\t\t\t\t\tbool\t\tis_hipriparent;\n\n\t\t\t\t\top = get_simple_binary_op_name((OpExpr *) node);\n\t\t\t\t\tif (!op)\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\t/* We know only the basic operators + - and * / % */\n\t\t\t\t\tis_lopriop = (strchr(\"+-\", *op) != NULL);\n\t\t\t\t\tis_hipriop = (strchr(\"*/%\", *op) != NULL);\n\t\t\t\t\tif (!(is_lopriop || is_hipriop))\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tparentOp = get_simple_binary_op_name((OpExpr *) parentNode);\n\t\t\t\t\tif (!parentOp)\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tis_lopriparent = (strchr(\"+-\", *parentOp) != NULL);\n\t\t\t\t\tis_hipriparent = (strchr(\"*/%\", *parentOp) != NULL);\n\t\t\t\t\tif (!(is_lopriparent || is_hipriparent))\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tif (is_hipriop && is_lopriparent)\n\t\t\t\t\t\treturn true;\t/* op binds tighter than parent */\n\n\t\t\t\t\tif (is_lopriop && is_hipriparent)\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Operators are same priority --- can skip parens only if\n\t\t\t\t\t * we have (a - b) - c, not a - (b - c).\n\t\t\t\t\t */\n\t\t\t\t\tif (node == (Node *) linitial(((OpExpr *) parentNode)->args))\n\t\t\t\t\t\treturn true;\n\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t/* else do the same stuff as for T_SubLink et al. */\n\t\t\t}\n\t\t\t/* FALLTHROUGH */\n\n\t\tcase T_SubLink:\n\t\tcase T_NullTest:\n\t\tcase T_BooleanTest:\n\t\tcase T_DistinctExpr:\n\t\tcase T_JsonIsPredicate:\n\t\t\tswitch (nodeTag(parentNode))\n\t\t\t{\n\t\t\t\tcase T_FuncExpr:\n\t\t\t\t\t{\n\t\t\t\t\t\t/* special handling for casts and COERCE_SQL_SYNTAX */\n\t\t\t\t\t\tCoercionForm type = ((FuncExpr *) parentNode)->funcformat;\n\n\t\t\t\t\t\tif (type == COERCE_EXPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_IMPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_SQL_SYNTAX)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\treturn true;\t/* own parentheses */\n\t\t\t\t\t}\n\t\t\t\tcase T_BoolExpr:\t/* lower precedence */\n\t\t\t\tcase T_SubscriptingRef:\t/* other separators */\n\t\t\t\tcase T_ArrayExpr:\t/* other separators */\n\t\t\t\tcase T_RowExpr: /* other separators */\n\t\t\t\tcase T_CoalesceExpr:\t/* own parentheses */\n\t\t\t\tcase T_MinMaxExpr:\t/* own parentheses */\n\t\t\t\tcase T_XmlExpr: /* own parentheses */\n\t\t\t\tcase T_NullIfExpr:\t/* other separators */\n\t\t\t\tcase T_Aggref:\t/* own parentheses */\n                case T_GroupingFunc: /* own parentheses */\n\t\t\t\tcase T_WindowFunc:\t/* own parentheses */\n\t\t\t\tcase T_CaseExpr:\t/* other separators */\n\t\t\t\t\treturn true;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\n\t\tcase T_BoolExpr:\n\t\t\tswitch (nodeTag(parentNode))\n\t\t\t{\n\t\t\t\tcase T_BoolExpr:\n\t\t\t\t\tif (prettyFlags & PRETTYFLAG_PAREN)\n\t\t\t\t\t{\n\t\t\t\t\t\tBoolExprType type;\n\t\t\t\t\t\tBoolExprType parentType;\n\n\t\t\t\t\t\ttype = ((BoolExpr *) node)->boolop;\n\t\t\t\t\t\tparentType = ((BoolExpr *) parentNode)->boolop;\n\t\t\t\t\t\tswitch (type)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase NOT_EXPR:\n\t\t\t\t\t\t\tcase AND_EXPR:\n\t\t\t\t\t\t\t\tif (parentType == AND_EXPR || parentType == OR_EXPR)\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase OR_EXPR:\n\t\t\t\t\t\t\t\tif (parentType == OR_EXPR)\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\tcase T_FuncExpr:\n\t\t\t\t\t{\n\t\t\t\t\t\t/* special handling for casts and COERCE_SQL_SYNTAX */\n\t\t\t\t\t\tCoercionForm type = ((FuncExpr *) parentNode)->funcformat;\n\n\t\t\t\t\t\tif (type == COERCE_EXPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_IMPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_SQL_SYNTAX)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\treturn true;\t/* own parentheses */\n\t\t\t\t\t}\n\t\t\t\tcase T_SubscriptingRef:\t/* other separators */\n\t\t\t\tcase T_ArrayExpr:\t/* other separators */\n\t\t\t\tcase T_RowExpr: /* other separators */\n\t\t\t\tcase T_CoalesceExpr:\t/* own parentheses */\n\t\t\t\tcase T_MinMaxExpr:\t/* own parentheses */\n\t\t\t\tcase T_XmlExpr: /* own parentheses */\n\t\t\t\tcase T_NullIfExpr:\t/* other separators */\n\t\t\t\tcase T_Aggref:\t/* own parentheses */\n                case T_GroupingFunc: /* own parentheses */\n\t\t\t\tcase T_WindowFunc:\t/* own parentheses */\n\t\t\t\tcase T_CaseExpr:\t/* other separators */\n\t\t\t\t\treturn true;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\n\t\tcase T_JsonValueExpr:\n\t\t\t/* maybe simple, check args */\n\t\t\treturn isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\t/* those we don't know: in dubio complexo */\n\treturn false;\n}\n\n/*\n * appendContextKeyword - append a keyword to buffer\n *\n * If prettyPrint is enabled, perform a line break, and adjust indentation.\n * Otherwise, just append the keyword.\n */\nstatic void\nappendContextKeyword(deparse_context *context, const char *str,\n\t\t\t\t\t int indentBefore, int indentAfter, int indentPlus)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tint\t\t\tindentAmount;\n\n\t\tcontext->indentLevel += indentBefore;\n\n\t\t/* remove any trailing spaces currently in the buffer ... */\n\t\tremoveStringInfoSpaces(buf);\n\t\t/* ... then add a newline and some spaces */\n\t\tappendStringInfoChar(buf, '\\n');\n\n\t\tif (context->indentLevel < PRETTYINDENT_LIMIT)\n\t\t\tindentAmount = Max(context->indentLevel, 0) + indentPlus;\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * If we're indented more than PRETTYINDENT_LIMIT characters, try\n\t\t\t * to conserve horizontal space by reducing the per-level\n\t\t\t * indentation.  For best results the scale factor here should\n\t\t\t * divide all the indent amounts that get added to indentLevel\n\t\t\t * (PRETTYINDENT_STD, etc).  It's important that the indentation\n\t\t\t * not grow unboundedly, else deeply-nested trees use O(N^2)\n\t\t\t * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.\n\t\t\t */\n\t\t\tindentAmount = PRETTYINDENT_LIMIT +\n\t\t\t\t(context->indentLevel - PRETTYINDENT_LIMIT) /\n\t\t\t\t(PRETTYINDENT_STD / 2);\n\t\t\tindentAmount %= PRETTYINDENT_LIMIT;\n\t\t\t/* scale/wrap logic affects indentLevel, but not indentPlus */\n\t\t\tindentAmount += indentPlus;\n\t\t}\n\t\tappendStringInfoSpaces(buf, indentAmount);\n\n\t\tappendStringInfoString(buf, str);\n\n\t\tcontext->indentLevel += indentAfter;\n\t\tif (context->indentLevel < 0)\n\t\t\tcontext->indentLevel = 0;\n\t}\n\telse\n\t\tappendStringInfoString(buf, str);\n}\n\n/*\n * removeStringInfoSpaces - delete trailing spaces from a buffer.\n *\n * Possibly this should move to stringinfo.c at some point.\n */\nstatic void\nremoveStringInfoSpaces(StringInfo str)\n{\n\twhile (str->len > 0 && str->data[str->len - 1] == ' ')\n\t\tstr->data[--(str->len)] = '\\0';\n}\n\n/*\n * get_rule_expr_paren\t- deparse expr using get_rule_expr,\n * embracing the string with parentheses if necessary for prettyPrint.\n *\n * Never embrace if prettyFlags=0, because it's done in the calling node.\n *\n * Any node that does *not* embrace its argument node by sql syntax (with\n * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should\n * use get_rule_expr_paren instead of get_rule_expr so parentheses can be\n * added.\n */\nstatic void\nget_rule_expr_paren(Node *node, deparse_context *context,\n\t\t\t\t\tbool showimplicit, Node *parentNode)\n{\n\tbool\t\tneed_paren;\n\n\tneed_paren = PRETTY_PAREN(context) &&\n\t\t!isSimpleNode(node, parentNode, context->prettyFlags);\n\n\tif (need_paren)\n\t\tappendStringInfoChar(context->buf, '(');\n\n\tget_rule_expr(node, context, showimplicit);\n\n\tif (need_paren)\n\t\tappendStringInfoChar(context->buf, ')');\n}\n\n/* ----------\n * get_rule_expr\t\t\t- Parse back an expression\n *\n * Note: showimplicit determines whether we display any implicit cast that\n * is present at the top of the expression tree.  It is a passed argument,\n * not a field of the context struct, because we change the value as we\n * recurse down into the expression.  In general we suppress implicit casts\n * when the result type is known with certainty (eg, the arguments of an\n * OR must be boolean).  We display implicit casts for arguments of functions\n * and operators, since this is needed to be certain that the same function\n * or operator will be chosen when the expression is re-parsed.\n * ----------\n */\nstatic void\nget_rule_expr(Node *node, deparse_context *context,\n\t\t\t  bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (node == NULL)\n\t\treturn;\n\n\t/* Guard against excessively long or deeply-nested queries */\n\tCHECK_FOR_INTERRUPTS();\n\tcheck_stack_depth();\n\n\t/*\n\t * Each level of get_rule_expr must emit an indivisible term\n\t * (parenthesized if necessary) to ensure result is reparsed into the same\n\t * expression tree.  The only exception is that when the input is a List,\n\t * we emit the component items comma-separated with no surrounding\n\t * decoration; this is convenient for most callers.\n\t */\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_Var:\n\t\t\t(void) get_variable((Var *) node, 0, false, context);\n\t\t\tbreak;\n\n\t\tcase T_Const:\n\t\t\tget_const_expr((Const *) node, context, 0);\n\t\t\tbreak;\n\n\t\tcase T_Param:\n\t\t\tget_parameter((Param *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_Aggref:\n\t\t\tget_agg_expr((Aggref *) node, context, (Aggref *) node);\n\t\t\tbreak;\n\n\t\tcase T_GroupingFunc:\n\t\t\t{\n\t\t\t\tGroupingFunc *gexpr = (GroupingFunc *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"GROUPING(\");\n\t\t\t\tget_rule_expr((Node *) gexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_WindowFunc:\n\t\t\tget_windowfunc_expr((WindowFunc *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_SubscriptingRef:\n\t\t\t{\n\t\t\t\tSubscriptingRef *sbsref = (SubscriptingRef *) node;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * If the argument is a CaseTestExpr, we must be inside a\n\t\t\t\t * FieldStore, ie, we are assigning to an element of an array\n\t\t\t\t * within a composite column.  Since we already punted on\n\t\t\t\t * displaying the FieldStore's target information, just punt\n\t\t\t\t * here too, and display only the assignment source\n\t\t\t\t * expression.\n\t\t\t\t */\n\t\t\t\tif (IsA(sbsref->refexpr, CaseTestExpr))\n\t\t\t\t{\n\t\t\t\t\tAssert(sbsref->refassgnexpr);\n\t\t\t\t\tget_rule_expr((Node *) sbsref->refassgnexpr,\n\t\t\t\t\t\t\t\t  context, showimplicit);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * Parenthesize the argument unless it's a simple Var or a\n\t\t\t\t * FieldSelect.  (In particular, if it's another\n\t\t\t\t * SubscriptingRef, we *must* parenthesize to avoid\n\t\t\t\t * confusion.)\n\t\t\t\t */\n\t\t\t\tneed_parens = !IsA(sbsref->refexpr, Var) &&\n\t\t\t\t\t!IsA(sbsref->refexpr, FieldSelect);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr((Node *) sbsref->refexpr, context, showimplicit);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t\t/*\n\t\t\t\t * If there's a refassgnexpr, we want to print the node in the\n\t\t\t\t * format \"container[subscripts] := refassgnexpr\".  This is\n\t\t\t\t * not legal SQL, so decompilation of INSERT or UPDATE\n\t\t\t\t * statements should always use processIndirection as part of\n\t\t\t\t * the statement-level syntax.  We should only see this when\n\t\t\t\t * EXPLAIN tries to print the targetlist of a plan resulting\n\t\t\t\t * from such a statement.\n\t\t\t\t */\n\t\t\t\tif (sbsref->refassgnexpr)\n\t\t\t\t{\n\t\t\t\t\tNode\t   *refassgnexpr;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Use processIndirection to print this node's subscripts\n\t\t\t\t\t * as well as any additional field selections or\n\t\t\t\t\t * subscripting in immediate descendants.  It returns the\n\t\t\t\t\t * RHS expr that is actually being \"assigned\".\n\t\t\t\t\t */\n\t\t\t\t\trefassgnexpr = processIndirection(node, context);\n\t\t\t\t\tappendStringInfoString(buf, \" := \");\n\t\t\t\t\tget_rule_expr(refassgnexpr, context, showimplicit);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* Just an ordinary container fetch, so print subscripts */\n\t\t\t\t\tprintSubscripts(sbsref, context);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_FuncExpr:\n\t\t\tget_func_expr((FuncExpr *) node, context, showimplicit);\n\t\t\tbreak;\n\n\t\tcase T_NamedArgExpr:\n\t\t\t{\n\t\t\t\tNamedArgExpr *na = (NamedArgExpr *) node;\n\n\t\t\t\tappendStringInfo(buf, \"%s => \", quote_identifier(na->name));\n\t\t\t\tget_rule_expr((Node *) na->arg, context, showimplicit);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_OpExpr:\n\t\t\tget_oper_expr((OpExpr *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_DistinctExpr:\n\t\t\t{\n\t\t\t\tDistinctExpr *expr = (DistinctExpr *) node;\n\t\t\t\tList\t   *args = expr->args;\n\t\t\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\t\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren(arg1, context, true, node);\n\t\t\t\tappendStringInfoString(buf, \" IS DISTINCT FROM \");\n\t\t\t\tget_rule_expr_paren(arg2, context, true, node);\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_NullIfExpr:\n\t\t\t{\n\t\t\t\tNullIfExpr *nullifexpr = (NullIfExpr *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"NULLIF(\");\n\t\t\t\tget_rule_expr((Node *) nullifexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ScalarArrayOpExpr:\n\t\t\t{\n\t\t\t\tScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;\n\t\t\t\tList\t   *args = expr->args;\n\t\t\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\t\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren(arg1, context, true, node);\n\t\t\t\tappendStringInfo(buf, \" %s %s (\",\n\t\t\t\t\t\t\t\t generate_operator_name(expr->opno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\texprType(arg1),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tget_base_element_type(exprType(arg2))),\n\t\t\t\t\t\t\t\t expr->useOr ? \"ANY\" : \"ALL\");\n\t\t\t\tget_rule_expr_paren(arg2, context, true, node);\n\n\t\t\t\t/*\n\t\t\t\t * There's inherent ambiguity in \"x op ANY/ALL (y)\" when y is\n\t\t\t\t * a bare sub-SELECT.  Since we're here, the sub-SELECT must\n\t\t\t\t * be meant as a scalar sub-SELECT yielding an array value to\n\t\t\t\t * be used in ScalarArrayOpExpr; but the grammar will\n\t\t\t\t * preferentially interpret such a construct as an ANY/ALL\n\t\t\t\t * SubLink.  To prevent misparsing the output that way, insert\n\t\t\t\t * a dummy coercion (which will be stripped by parse analysis,\n\t\t\t\t * so no inefficiency is added in dump and reload).  This is\n\t\t\t\t * indeed most likely what the user wrote to get the construct\n\t\t\t\t * accepted in the first place.\n\t\t\t\t */\n\t\t\t\tif (IsA(arg2, SubLink) &&\n\t\t\t\t\t((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)\n\t\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(exprType(arg2),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  exprTypmod(arg2)));\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_BoolExpr:\n\t\t\t{\n\t\t\t\tBoolExpr   *expr = (BoolExpr *) node;\n\t\t\t\tNode\t   *first_arg = linitial(expr->args);\n\t\t\t\tListCell   *arg;\n\n\t\t\t\tswitch (expr->boolop)\n\t\t\t\t{\n\t\t\t\t\tcase AND_EXPR:\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tget_rule_expr_paren(first_arg, context,\n\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\tfor_each_from(arg, expr->args, 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" AND \");\n\t\t\t\t\t\t\tget_rule_expr_paren((Node *) lfirst(arg), context,\n\t\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase OR_EXPR:\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tget_rule_expr_paren(first_arg, context,\n\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\tfor_each_from(arg, expr->args, 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" OR \");\n\t\t\t\t\t\t\tget_rule_expr_paren((Node *) lfirst(arg), context,\n\t\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase NOT_EXPR:\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tappendStringInfoString(buf, \"NOT \");\n\t\t\t\t\t\tget_rule_expr_paren(first_arg, context,\n\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telog(ERROR, \"unrecognized boolop: %d\",\n\t\t\t\t\t\t\t (int) expr->boolop);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_SubLink:\n\t\t\tget_sublink_expr((SubLink *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_SubPlan:\n\t\t\t{\n\t\t\t\tSubPlan    *subplan = (SubPlan *) node;\n\n\t\t\t\t/*\n\t\t\t\t * We cannot see an already-planned subplan in rule deparsing,\n\t\t\t\t * only while EXPLAINing a query plan.  We don't try to\n\t\t\t\t * reconstruct the original SQL, just reference the subplan\n\t\t\t\t * that appears elsewhere in EXPLAIN's result.\n\t\t\t\t */\n\t\t\t\tif (subplan->useHashTable)\n\t\t\t\t\tappendStringInfo(buf, \"(hashed %s)\", subplan->plan_name);\n\t\t\t\telse\n\t\t\t\t\tappendStringInfo(buf, \"(%s)\", subplan->plan_name);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_AlternativeSubPlan:\n\t\t\t{\n\t\t\t\tAlternativeSubPlan *asplan = (AlternativeSubPlan *) node;\n\t\t\t\tListCell   *lc;\n\n\t\t\t\t/*\n\t\t\t\t * This case cannot be reached in normal usage, since no\n\t\t\t\t * AlternativeSubPlan can appear either in parsetrees or\n\t\t\t\t * finished plan trees.  We keep it just in case somebody\n\t\t\t\t * wants to use this code to print planner data structures.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"(alternatives: \");\n\t\t\t\tforeach(lc, asplan->subplans)\n\t\t\t\t{\n\t\t\t\t\tSubPlan    *splan = lfirst_node(SubPlan, lc);\n\n\t\t\t\t\tif (splan->useHashTable)\n\t\t\t\t\t\tappendStringInfo(buf, \"hashed %s\", splan->plan_name);\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfoString(buf, splan->plan_name);\n\t\t\t\t\tif (lnext(asplan->subplans, lc))\n\t\t\t\t\t\tappendStringInfoString(buf, \" or \");\n\t\t\t\t}\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_FieldSelect:\n\t\t\t{\n\t\t\t\tFieldSelect *fselect = (FieldSelect *) node;\n\t\t\t\tNode\t   *arg = (Node *) fselect->arg;\n\t\t\t\tint\t\t\tfno = fselect->fieldnum;\n\t\t\t\tconst char *fieldname;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * Parenthesize the argument unless it's an SubscriptingRef or\n\t\t\t\t * another FieldSelect.  Note in particular that it would be\n\t\t\t\t * WRONG to not parenthesize a Var argument; simplicity is not\n\t\t\t\t * the issue here, having the right number of names is.\n\t\t\t\t */\n\t\t\t\tneed_parens = !IsA(arg, SubscriptingRef) &&\n\t\t\t\t\t!IsA(arg, FieldSelect);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr(arg, context, true);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t\t/*\n\t\t\t\t * Get and print the field name.\n\t\t\t\t */\n\t\t\t\tfieldname = get_name_for_var_field((Var *) arg, fno,\n\t\t\t\t\t\t\t\t\t\t\t\t   0, context);\n\t\t\t\tappendStringInfo(buf, \".%s\", quote_identifier(fieldname));\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_FieldStore:\n\t\t\t{\n\t\t\t\tFieldStore *fstore = (FieldStore *) node;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * There is no good way to represent a FieldStore as real SQL,\n\t\t\t\t * so decompilation of INSERT or UPDATE statements should\n\t\t\t\t * always use processIndirection as part of the\n\t\t\t\t * statement-level syntax.  We should only get here when\n\t\t\t\t * EXPLAIN tries to print the targetlist of a plan resulting\n\t\t\t\t * from such a statement.  The plan case is even harder than\n\t\t\t\t * ordinary rules would be, because the planner tries to\n\t\t\t\t * collapse multiple assignments to the same field or subfield\n\t\t\t\t * into one FieldStore; so we can see a list of target fields\n\t\t\t\t * not just one, and the arguments could be FieldStores\n\t\t\t\t * themselves.  We don't bother to try to print the target\n\t\t\t\t * field names; we just print the source arguments, with a\n\t\t\t\t * ROW() around them if there's more than one.  This isn't\n\t\t\t\t * terribly complete, but it's probably good enough for\n\t\t\t\t * EXPLAIN's purposes; especially since anything more would be\n\t\t\t\t * either hopelessly confusing or an even poorer\n\t\t\t\t * representation of what the plan is actually doing.\n\t\t\t\t */\n\t\t\t\tneed_parens = (list_length(fstore->newvals) != 1);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoString(buf, \"ROW(\");\n\t\t\t\tget_rule_expr((Node *) fstore->newvals, context, showimplicit);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_RelabelType:\n\t\t\t{\n\t\t\t\tRelabelType *relabel = (RelabelType *) node;\n\t\t\t\tNode\t   *arg = (Node *) relabel->arg;\n\n\t\t\t\tif (relabel->relabelformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  relabel->resulttype,\n\t\t\t\t\t\t\t\t\t  relabel->resulttypmod,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoerceViaIO:\n\t\t\t{\n\t\t\t\tCoerceViaIO *iocoerce = (CoerceViaIO *) node;\n\t\t\t\tNode\t   *arg = (Node *) iocoerce->arg;\n\n\t\t\t\tif (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  iocoerce->resulttype,\n\t\t\t\t\t\t\t\t\t  -1,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ArrayCoerceExpr:\n\t\t\t{\n\t\t\t\tArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;\n\t\t\t\tNode\t   *arg = (Node *) acoerce->arg;\n\n\t\t\t\tif (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  acoerce->resulttype,\n\t\t\t\t\t\t\t\t\t  acoerce->resulttypmod,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ConvertRowtypeExpr:\n\t\t\t{\n\t\t\t\tConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;\n\t\t\t\tNode\t   *arg = (Node *) convert->arg;\n\n\t\t\t\tif (convert->convertformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  convert->resulttype, -1,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CollateExpr:\n\t\t\t{\n\t\t\t\tCollateExpr *collate = (CollateExpr *) node;\n\t\t\t\tNode\t   *arg = (Node *) collate->arg;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren(arg, context, showimplicit, node);\n\t\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t\t generate_collation_name(collate->collOid));\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CaseExpr:\n\t\t\t{\n\t\t\t\tCaseExpr   *caseexpr = (CaseExpr *) node;\n\t\t\t\tListCell   *temp;\n\n\t\t\t\tappendContextKeyword(context, \"CASE\",\n\t\t\t\t\t\t\t\t\t 0, PRETTYINDENT_VAR, 0);\n\t\t\t\tif (caseexpr->arg)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\t\tget_rule_expr((Node *) caseexpr->arg, context, true);\n\t\t\t\t}\n\t\t\t\tforeach(temp, caseexpr->args)\n\t\t\t\t{\n\t\t\t\t\tCaseWhen   *when = (CaseWhen *) lfirst(temp);\n\t\t\t\t\tNode\t   *w = (Node *) when->expr;\n\n\t\t\t\t\tif (caseexpr->arg)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * The parser should have produced WHEN clauses of the\n\t\t\t\t\t\t * form \"CaseTestExpr = RHS\", possibly with an\n\t\t\t\t\t\t * implicit coercion inserted above the CaseTestExpr.\n\t\t\t\t\t\t * For accurate decompilation of rules it's essential\n\t\t\t\t\t\t * that we show just the RHS.  However in an\n\t\t\t\t\t\t * expression that's been through the optimizer, the\n\t\t\t\t\t\t * WHEN clause could be almost anything (since the\n\t\t\t\t\t\t * equality operator could have been expanded into an\n\t\t\t\t\t\t * inline function).  If we don't recognize the form\n\t\t\t\t\t\t * of the WHEN clause, just punt and display it as-is.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (IsA(w, OpExpr))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tList\t   *args = ((OpExpr *) w)->args;\n\n\t\t\t\t\t\t\tif (list_length(args) == 2 &&\n\t\t\t\t\t\t\t\tIsA(strip_implicit_coercions(linitial(args)),\n\t\t\t\t\t\t\t\t\tCaseTestExpr))\n\t\t\t\t\t\t\t\tw = (Node *) lsecond(args);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!PRETTY_INDENT(context))\n\t\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\t\tappendContextKeyword(context, \"WHEN \",\n\t\t\t\t\t\t\t\t\t\t 0, 0, 0);\n\t\t\t\t\tget_rule_expr(w, context, false);\n\t\t\t\t\tappendStringInfoString(buf, \" THEN \");\n\t\t\t\t\tget_rule_expr((Node *) when->result, context, true);\n\t\t\t\t}\n\t\t\t\tif (!PRETTY_INDENT(context))\n\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tappendContextKeyword(context, \"ELSE \",\n\t\t\t\t\t\t\t\t\t 0, 0, 0);\n\t\t\t\tget_rule_expr((Node *) caseexpr->defresult, context, true);\n\t\t\t\tif (!PRETTY_INDENT(context))\n\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tappendContextKeyword(context, \"END\",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_VAR, 0, 0);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CaseTestExpr:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Normally we should never get here, since for expressions\n\t\t\t\t * that can contain this node type we attempt to avoid\n\t\t\t\t * recursing to it.  But in an optimized expression we might\n\t\t\t\t * be unable to avoid that (see comments for CaseExpr).  If we\n\t\t\t\t * do see one, print it as CASE_TEST_EXPR.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"CASE_TEST_EXPR\");\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ArrayExpr:\n\t\t\t{\n\t\t\t\tArrayExpr  *arrayexpr = (ArrayExpr *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"ARRAY[\");\n\t\t\t\tget_rule_expr((Node *) arrayexpr->elements, context, true);\n\t\t\t\tappendStringInfoChar(buf, ']');\n\n\t\t\t\t/*\n\t\t\t\t * If the array isn't empty, we assume its elements are\n\t\t\t\t * coerced to the desired type.  If it's empty, though, we\n\t\t\t\t * need an explicit coercion to the array type.\n\t\t\t\t */\n\t\t\t\tif (arrayexpr->elements == NIL)\n\t\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(arrayexpr->array_typeid, -1));\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_RowExpr:\n\t\t\t{\n\t\t\t\tRowExpr    *rowexpr = (RowExpr *) node;\n\t\t\t\tTupleDesc\ttupdesc = NULL;\n\t\t\t\tListCell   *arg;\n\t\t\t\tint\t\t\ti;\n\t\t\t\tchar\t   *sep;\n\n\t\t\t\t/*\n\t\t\t\t * If it's a named type and not RECORD, we may have to skip\n\t\t\t\t * dropped columns and/or claim there are NULLs for added\n\t\t\t\t * columns.\n\t\t\t\t */\n\t\t\t\tif (rowexpr->row_typeid != RECORDOID)\n\t\t\t\t{\n\t\t\t\t\ttupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);\n\t\t\t\t\tAssert(list_length(rowexpr->args) <= tupdesc->natts);\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * SQL99 allows \"ROW\" to be omitted when there is more than\n\t\t\t\t * one column, but for simplicity we always print it.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"ROW(\");\n\t\t\t\tsep = \"\";\n\t\t\t\ti = 0;\n\t\t\t\tforeach(arg, rowexpr->args)\n\t\t\t\t{\n\t\t\t\t\tNode\t   *e = (Node *) lfirst(arg);\n\n\t\t\t\t\tif (tupdesc == NULL ||\n\t\t\t\t\t\t!TupleDescAttr(tupdesc, i)->attisdropped)\n\t\t\t\t\t{\n\t\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\t\t/* Whole-row Vars need special treatment here */\n\t\t\t\t\t\tget_rule_expr_toplevel(e, context, true);\n\t\t\t\t\t\tsep = \", \";\n\t\t\t\t\t}\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t\tif (tupdesc != NULL)\n\t\t\t\t{\n\t\t\t\t\twhile (i < tupdesc->natts)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!TupleDescAttr(tupdesc, i)->attisdropped)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\t\t\tappendStringInfoString(buf, \"NULL\");\n\t\t\t\t\t\t\tsep = \", \";\n\t\t\t\t\t\t}\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tReleaseTupleDesc(tupdesc);\n\t\t\t\t}\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tif (rowexpr->row_format == COERCE_EXPLICIT_CAST)\n\t\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(rowexpr->row_typeid, -1));\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_RowCompareExpr:\n\t\t\t{\n\t\t\t\tRowCompareExpr *rcexpr = (RowCompareExpr *) node;\n\n\t\t\t\t/*\n\t\t\t\t * SQL99 allows \"ROW\" to be omitted when there is more than\n\t\t\t\t * one column, but for simplicity we always print it.  Within\n\t\t\t\t * a ROW expression, whole-row Vars need special treatment, so\n\t\t\t\t * use get_rule_list_toplevel.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"(ROW(\");\n\t\t\t\tget_rule_list_toplevel(rcexpr->largs, context, true);\n\n\t\t\t\t/*\n\t\t\t\t * We assume that the name of the first-column operator will\n\t\t\t\t * do for all the rest too.  This is definitely open to\n\t\t\t\t * failure, eg if some but not all operators were renamed\n\t\t\t\t * since the construct was parsed, but there seems no way to\n\t\t\t\t * be perfect.\n\t\t\t\t */\n\t\t\t\tappendStringInfo(buf, \") %s ROW(\",\n\t\t\t\t\t\t\t\t generate_operator_name(linitial_oid(rcexpr->opnos),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->largs)),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->rargs))));\n\t\t\t\tget_rule_list_toplevel(rcexpr->rargs, context, true);\n\t\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoalesceExpr:\n\t\t\t{\n\t\t\t\tCoalesceExpr *coalesceexpr = (CoalesceExpr *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"COALESCE(\");\n\t\t\t\tget_rule_expr((Node *) coalesceexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_MinMaxExpr:\n\t\t\t{\n\t\t\t\tMinMaxExpr *minmaxexpr = (MinMaxExpr *) node;\n\n\t\t\t\tswitch (minmaxexpr->op)\n\t\t\t\t{\n\t\t\t\t\tcase IS_GREATEST:\n\t\t\t\t\t\tappendStringInfoString(buf, \"GREATEST(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_LEAST:\n\t\t\t\t\t\tappendStringInfoString(buf, \"LEAST(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tget_rule_expr((Node *) minmaxexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_SQLValueFunction:\n\t\t\t{\n\t\t\t\tSQLValueFunction *svf = (SQLValueFunction *) node;\n\n\t\t\t\t/*\n\t\t\t\t * Note: this code knows that typmod for time, timestamp, and\n\t\t\t\t * timestamptz just prints as integer.\n\t\t\t\t */\n\t\t\t\tswitch (svf->op)\n\t\t\t\t{\n\t\t\t\t\tcase SVFOP_CURRENT_DATE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_DATE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIME:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_TIME\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIME_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"CURRENT_TIME(%d)\", svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIMESTAMP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_TIMESTAMP\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIMESTAMP_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"CURRENT_TIMESTAMP(%d)\",\n\t\t\t\t\t\t\t\t\t\t svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIME:\n\t\t\t\t\t\tappendStringInfoString(buf, \"LOCALTIME\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIME_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"LOCALTIME(%d)\", svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIMESTAMP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"LOCALTIMESTAMP\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIMESTAMP_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"LOCALTIMESTAMP(%d)\",\n\t\t\t\t\t\t\t\t\t\t svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_ROLE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_ROLE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_USER:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_USER\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_USER:\n\t\t\t\t\t\tappendStringInfoString(buf, \"USER\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_SESSION_USER:\n\t\t\t\t\t\tappendStringInfoString(buf, \"SESSION_USER\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_CATALOG:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_CATALOG\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_SCHEMA:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_SCHEMA\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_XmlExpr:\n\t\t\t{\n\t\t\t\tXmlExpr    *xexpr = (XmlExpr *) node;\n\t\t\t\tbool\t\tneedcomma = false;\n\t\t\t\tListCell   *arg;\n\t\t\t\tListCell   *narg;\n\t\t\t\tConst\t   *con;\n\n\t\t\t\tswitch (xexpr->op)\n\t\t\t\t{\n\t\t\t\t\tcase IS_XMLCONCAT:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLCONCAT(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLELEMENT:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLELEMENT(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLFOREST:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLFOREST(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLPARSE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLPARSE(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLPI:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLPI(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLROOT:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLROOT(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLSERIALIZE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLSERIALIZE(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_DOCUMENT:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)\n\t\t\t\t{\n\t\t\t\t\tif (xexpr->xmloption == XMLOPTION_DOCUMENT)\n\t\t\t\t\t\tappendStringInfoString(buf, \"DOCUMENT \");\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfoString(buf, \"CONTENT \");\n\t\t\t\t}\n\t\t\t\tif (xexpr->name)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfo(buf, \"NAME %s\",\n\t\t\t\t\t\t\t\t\t quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));\n\t\t\t\t\tneedcomma = true;\n\t\t\t\t}\n\t\t\t\tif (xexpr->named_args)\n\t\t\t\t{\n\t\t\t\t\tif (xexpr->op != IS_XMLFOREST)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (needcomma)\n\t\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLATTRIBUTES(\");\n\t\t\t\t\t\tneedcomma = false;\n\t\t\t\t\t}\n\t\t\t\t\tforboth(arg, xexpr->named_args, narg, xexpr->arg_names)\n\t\t\t\t\t{\n\t\t\t\t\t\tNode\t   *e = (Node *) lfirst(arg);\n\t\t\t\t\t\tchar\t   *argname = strVal(lfirst(narg));\n\n\t\t\t\t\t\tif (needcomma)\n\t\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\t\tget_rule_expr((Node *) e, context, true);\n\t\t\t\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t\t\t\t quote_identifier(map_xml_name_to_sql_identifier(argname)));\n\t\t\t\t\t\tneedcomma = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (xexpr->op != IS_XMLFOREST)\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t}\n\t\t\t\tif (xexpr->args)\n\t\t\t\t{\n\t\t\t\t\tif (needcomma)\n\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\tswitch (xexpr->op)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase IS_XMLCONCAT:\n\t\t\t\t\t\tcase IS_XMLELEMENT:\n\t\t\t\t\t\tcase IS_XMLFOREST:\n\t\t\t\t\t\tcase IS_XMLPI:\n\t\t\t\t\t\tcase IS_XMLSERIALIZE:\n\t\t\t\t\t\t\t/* no extra decoration needed */\n\t\t\t\t\t\t\tget_rule_expr((Node *) xexpr->args, context, true);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_XMLPARSE:\n\t\t\t\t\t\t\tAssert(list_length(xexpr->args) == 2);\n\n\t\t\t\t\t\t\tget_rule_expr((Node *) linitial(xexpr->args),\n\t\t\t\t\t\t\t\t\t\t  context, true);\n\n\t\t\t\t\t\t\tcon = lsecond_node(Const, xexpr->args);\n\t\t\t\t\t\t\tAssert(!con->constisnull);\n\t\t\t\t\t\t\tif (DatumGetBool(con->constvalue))\n\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \" PRESERVE WHITESPACE\");\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \" STRIP WHITESPACE\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_XMLROOT:\n\t\t\t\t\t\t\tAssert(list_length(xexpr->args) == 3);\n\n\t\t\t\t\t\t\tget_rule_expr((Node *) linitial(xexpr->args),\n\t\t\t\t\t\t\t\t\t\t  context, true);\n\n\t\t\t\t\t\t\tappendStringInfoString(buf, \", VERSION \");\n\t\t\t\t\t\t\tcon = (Const *) lsecond(xexpr->args);\n\t\t\t\t\t\t\tif (IsA(con, Const) &&\n\t\t\t\t\t\t\t\tcon->constisnull)\n\t\t\t\t\t\t\t\tappendStringInfoString(buf, \"NO VALUE\");\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tget_rule_expr((Node *) con, context, false);\n\n\t\t\t\t\t\t\tcon = lthird_node(Const, xexpr->args);\n\t\t\t\t\t\t\tif (con->constisnull)\n\t\t\t\t\t\t\t\t /* suppress STANDALONE NO VALUE */ ;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tswitch (DatumGetInt32(con->constvalue))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tcase XML_STANDALONE_YES:\n\t\t\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \", STANDALONE YES\");\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase XML_STANDALONE_NO:\n\t\t\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \", STANDALONE NO\");\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase XML_STANDALONE_NO_VALUE:\n\t\t\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \", STANDALONE NO VALUE\");\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_DOCUMENT:\n\t\t\t\t\t\t\tget_rule_expr_paren((Node *) xexpr->args, context, false, node);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (xexpr->op == IS_XMLSERIALIZE)\n\t\t\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(xexpr->type,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  xexpr->typmod));\n\t\t\t\tif (xexpr->op == IS_DOCUMENT)\n\t\t\t\t\tappendStringInfoString(buf, \" IS DOCUMENT\");\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_NullTest:\n\t\t\t{\n\t\t\t\tNullTest   *ntest = (NullTest *) node;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren((Node *) ntest->arg, context, true, node);\n\n\t\t\t\t/*\n\t\t\t\t * For scalar inputs, we prefer to print as IS [NOT] NULL,\n\t\t\t\t * which is shorter and traditional.  If it's a rowtype input\n\t\t\t\t * but we're applying a scalar test, must print IS [NOT]\n\t\t\t\t * DISTINCT FROM NULL to be semantically correct.\n\t\t\t\t */\n\t\t\t\tif (ntest->argisrow ||\n\t\t\t\t\t!type_is_rowtype(exprType((Node *) ntest->arg)))\n\t\t\t\t{\n\t\t\t\t\tswitch (ntest->nulltesttype)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase IS_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_NOT_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\telog(ERROR, \"unrecognized nulltesttype: %d\",\n\t\t\t\t\t\t\t\t (int) ntest->nulltesttype);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tswitch (ntest->nulltesttype)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase IS_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT DISTINCT FROM NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_NOT_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS DISTINCT FROM NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\telog(ERROR, \"unrecognized nulltesttype: %d\",\n\t\t\t\t\t\t\t\t (int) ntest->nulltesttype);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_BooleanTest:\n\t\t\t{\n\t\t\t\tBooleanTest *btest = (BooleanTest *) node;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren((Node *) btest->arg, context, false, node);\n\t\t\t\tswitch (btest->booltesttype)\n\t\t\t\t{\n\t\t\t\t\tcase IS_TRUE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS TRUE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NOT_TRUE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT TRUE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_FALSE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS FALSE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NOT_FALSE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT FALSE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_UNKNOWN:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS UNKNOWN\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NOT_UNKNOWN:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT UNKNOWN\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telog(ERROR, \"unrecognized booltesttype: %d\",\n\t\t\t\t\t\t\t (int) btest->booltesttype);\n\t\t\t\t}\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoerceToDomain:\n\t\t\t{\n\t\t\t\tCoerceToDomain *ctest = (CoerceToDomain *) node;\n\t\t\t\tNode\t   *arg = (Node *) ctest->arg;\n\n\t\t\t\tif (ctest->coercionformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr(arg, context, false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  ctest->resulttype,\n\t\t\t\t\t\t\t\t\t  ctest->resulttypmod,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoerceToDomainValue:\n\t\t\tappendStringInfoString(buf, \"VALUE\");\n\t\t\tbreak;\n\n\t\tcase T_SetToDefault:\n\t\t\tappendStringInfoString(buf, \"DEFAULT\");\n\t\t\tbreak;\n\n\t\tcase T_CurrentOfExpr:\n\t\t\t{\n\t\t\t\tCurrentOfExpr *cexpr = (CurrentOfExpr *) node;\n\n\t\t\t\tif (cexpr->cursor_name)\n\t\t\t\t\tappendStringInfo(buf, \"CURRENT OF %s\",\n\t\t\t\t\t\t\t\t\t quote_identifier(cexpr->cursor_name));\n\t\t\t\telse\n\t\t\t\t\tappendStringInfo(buf, \"CURRENT OF $%d\",\n\t\t\t\t\t\t\t\t\t cexpr->cursor_param);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_NextValueExpr:\n\t\t\t{\n\t\t\t\tNextValueExpr *nvexpr = (NextValueExpr *) node;\n\n\t\t\t\t/*\n\t\t\t\t * This isn't exactly nextval(), but that seems close enough\n\t\t\t\t * for EXPLAIN's purposes.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"nextval(\");\n\t\t\t\tsimple_quote_literal(buf,\n\t\t\t\t\t\t\t\t\t generate_relation_name(nvexpr->seqid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNIL));\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_InferenceElem:\n\t\t\t{\n\t\t\t\tInferenceElem *iexpr = (InferenceElem *) node;\n\t\t\t\tbool\t\tsave_varprefix;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * InferenceElem can only refer to target relation, so a\n\t\t\t\t * prefix is not useful, and indeed would cause parse errors.\n\t\t\t\t */\n\t\t\t\tsave_varprefix = context->varprefix;\n\t\t\t\tcontext->varprefix = false;\n\n\t\t\t\t/*\n\t\t\t\t * Parenthesize the element unless it's a simple Var or a bare\n\t\t\t\t * function call.  Follows pg_get_indexdef_worker().\n\t\t\t\t */\n\t\t\t\tneed_parens = !IsA(iexpr->expr, Var);\n\t\t\t\tif (IsA(iexpr->expr, FuncExpr) &&\n\t\t\t\t\t((FuncExpr *) iexpr->expr)->funcformat ==\n\t\t\t\t\tCOERCE_EXPLICIT_CALL)\n\t\t\t\t\tneed_parens = false;\n\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr((Node *) iexpr->expr,\n\t\t\t\t\t\t\t  context, false);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t\tcontext->varprefix = save_varprefix;\n\n\t\t\t\tif (iexpr->infercollid)\n\t\t\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t\t\t generate_collation_name(iexpr->infercollid));\n\n\t\t\t\t/* Add the operator class name, if not default */\n\t\t\t\tif (iexpr->inferopclass)\n\t\t\t\t{\n\t\t\t\t\tOid\t\t\tinferopclass = iexpr->inferopclass;\n\t\t\t\t\tOid\t\t\tinferopcinputtype = get_opclass_input_type(iexpr->inferopclass);\n\n\t\t\t\t\tget_opclass_name(inferopclass, inferopcinputtype, buf);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_PartitionBoundSpec:\n\t\t\t{\n\t\t\t\tPartitionBoundSpec *spec = (PartitionBoundSpec *) node;\n\t\t\t\tListCell   *cell;\n\t\t\t\tchar\t   *sep;\n\n\t\t\t\tif (spec->is_default)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(buf, \"DEFAULT\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tswitch (spec->strategy)\n\t\t\t\t{\n\t\t\t\t\tcase PARTITION_STRATEGY_HASH:\n\t\t\t\t\t\tAssert(spec->modulus > 0 && spec->remainder >= 0);\n\t\t\t\t\t\tAssert(spec->modulus > spec->remainder);\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"FOR VALUES\");\n\t\t\t\t\t\tappendStringInfo(buf, \" WITH (modulus %d, remainder %d)\",\n\t\t\t\t\t\t\t\t\t\t spec->modulus, spec->remainder);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase PARTITION_STRATEGY_LIST:\n\t\t\t\t\t\tAssert(spec->listdatums != NIL);\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"FOR VALUES IN (\");\n\t\t\t\t\t\tsep = \"\";\n\t\t\t\t\t\tforeach(cell, spec->listdatums)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tConst\t   *val = lfirst_node(Const, cell);\n\n\t\t\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\t\t\tget_const_expr(val, context, -1);\n\t\t\t\t\t\t\tsep = \", \";\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase PARTITION_STRATEGY_RANGE:\n\t\t\t\t\t\tAssert(spec->lowerdatums != NIL &&\n\t\t\t\t\t\t\t   spec->upperdatums != NIL &&\n\t\t\t\t\t\t\t   list_length(spec->lowerdatums) ==\n\t\t\t\t\t\t\t   list_length(spec->upperdatums));\n\n\t\t\t\t\t\tappendStringInfo(buf, \"FOR VALUES FROM %s TO %s\",\n\t\t\t\t\t\t\t\t\t\t get_range_partbound_string(spec->lowerdatums),\n\t\t\t\t\t\t\t\t\t\t get_range_partbound_string(spec->upperdatums));\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telog(ERROR, \"unrecognized partition strategy: %d\",\n\t\t\t\t\t\t\t (int) spec->strategy);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_JsonValueExpr:\n\t\t\t{\n\t\t\t\tJsonValueExpr *jve = (JsonValueExpr *) node;\n\n\t\t\t\tget_rule_expr((Node *) jve->raw_expr, context, false);\n\t\t\t\tget_json_format(jve->format, context->buf);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_JsonConstructorExpr:\n\t\t\tget_json_constructor((JsonConstructorExpr *) node, context, false);\n\t\t\tbreak;\n\n\t\tcase T_JsonIsPredicate:\n\t\t\t{\n\t\t\t\tJsonIsPredicate *pred = (JsonIsPredicate *) node;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(context->buf, '(');\n\n\t\t\t\tget_rule_expr_paren(pred->expr, context, true, node);\n\n\t\t\t\tappendStringInfoString(context->buf, \" IS JSON\");\n\n\t\t\t\t/* TODO: handle FORMAT clause */\n\n\t\t\t\tswitch (pred->item_type)\n\t\t\t\t{\n\t\t\t\t\tcase JS_TYPE_SCALAR:\n\t\t\t\t\t\tappendStringInfoString(context->buf, \" SCALAR\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase JS_TYPE_ARRAY:\n\t\t\t\t\t\tappendStringInfoString(context->buf, \" ARRAY\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase JS_TYPE_OBJECT:\n\t\t\t\t\t\tappendStringInfoString(context->buf, \" OBJECT\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (pred->unique_keys)\n\t\t\t\t\tappendStringInfoString(context->buf, \" WITH UNIQUE KEYS\");\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(context->buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_List:\n\t\t\t{\n\t\t\t\tchar\t   *sep;\n\t\t\t\tListCell   *l;\n\n\t\t\t\tsep = \"\";\n\t\t\t\tforeach(l, (List *) node)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\tget_rule_expr((Node *) lfirst(l), context, showimplicit);\n\t\t\t\t\tsep = \", \";\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_TableFunc:\n\t\t\tget_tablefunc((TableFunc *) node, context, showimplicit);\n\t\t\tbreak;\n\n\t\tcase T_CallStmt:\n\t\t\tget_proc_expr((CallStmt *) node, context, showimplicit);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\telog(ERROR, \"unrecognized node type: %d\", (int) nodeTag(node));\n\t\t\tbreak;\n\t}\n}\n\n/*\n * get_rule_expr_toplevel\t\t- Parse back a toplevel expression\n *\n * Same as get_rule_expr(), except that if the expr is just a Var, we pass\n * istoplevel = true not false to get_variable().  This causes whole-row Vars\n * to get printed with decoration that will prevent expansion of \"*\".\n * We need to use this in contexts such as ROW() and VALUES(), where the\n * parser would expand \"foo.*\" appearing at top level.  (In principle we'd\n * use this in get_target_list() too, but that has additional worries about\n * whether to print AS, so it needs to invoke get_variable() directly anyway.)\n */\nstatic void\nget_rule_expr_toplevel(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit)\n{\n\tif (node && IsA(node, Var))\n\t\t(void) get_variable((Var *) node, 0, true, context);\n\telse\n\t\tget_rule_expr(node, context, showimplicit);\n}\n\n/*\n * get_rule_list_toplevel\t\t- Parse back a list of toplevel expressions\n *\n * Apply get_rule_expr_toplevel() to each element of a List.\n *\n * This adds commas between the expressions, but caller is responsible\n * for printing surrounding decoration.\n */\nstatic void\nget_rule_list_toplevel(List *lst, deparse_context *context,\n\t\t\t\t\t   bool showimplicit)\n{\n\tconst char *sep;\n\tListCell   *lc;\n\n\tsep = \"\";\n\tforeach(lc, lst)\n\t{\n\t\tNode\t   *e = (Node *) lfirst(lc);\n\n\t\tappendStringInfoString(context->buf, sep);\n\t\tget_rule_expr_toplevel(e, context, showimplicit);\n\t\tsep = \", \";\n\t}\n}\n\n/*\n * get_rule_expr_funccall\t\t- Parse back a function-call expression\n *\n * Same as get_rule_expr(), except that we guarantee that the output will\n * look like a function call, or like one of the things the grammar treats as\n * equivalent to a function call (see the func_expr_windowless production).\n * This is needed in places where the grammar uses func_expr_windowless and\n * you can't substitute a parenthesized a_expr.  If what we have isn't going\n * to look like a function call, wrap it in a dummy CAST() expression, which\n * will satisfy the grammar --- and, indeed, is likely what the user wrote to\n * produce such a thing.\n */\nstatic void\nget_rule_expr_funccall(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit)\n{\n\tif (looks_like_function(node))\n\t\tget_rule_expr(node, context, showimplicit);\n\telse\n\t{\n\t\tStringInfo\tbuf = context->buf;\n\n\t\tappendStringInfoString(buf, \"CAST(\");\n\t\t/* no point in showing any top-level implicit cast */\n\t\tget_rule_expr(node, context, false);\n\t\tappendStringInfo(buf, \" AS %s)\",\n\t\t\t\t\t\t format_type_with_typemod(exprType(node),\n\t\t\t\t\t\t\t\t\t\t\t\t  exprTypmod(node)));\n\t}\n}\n\n/*\n * Helper function to identify node types that satisfy func_expr_windowless.\n * If in doubt, \"false\" is always a safe answer.\n */\nstatic bool\nlooks_like_function(Node *node)\n{\n\tif (node == NULL)\n\t\treturn false;\t\t\t/* probably shouldn't happen */\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_FuncExpr:\n\t\t\t/* OK, unless it's going to deparse as a cast */\n\t\t\treturn (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||\n\t\t\t\t\t((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);\n\t\tcase T_NullIfExpr:\n\t\tcase T_CoalesceExpr:\n\t\tcase T_MinMaxExpr:\n\t\tcase T_SQLValueFunction:\n\t\tcase T_XmlExpr:\n\t\t\t/* these are all accepted by func_expr_common_subexpr */\n\t\t\treturn true;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\treturn false;\n}\n\n/*\n * get_oper_expr\t\t\t- Parse back an OpExpr node\n */\nstatic void\nget_oper_expr(OpExpr *expr, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\topno = expr->opno;\n\tList\t   *args = expr->args;\n\n\tif (!PRETTY_PAREN(context))\n\t\tappendStringInfoChar(buf, '(');\n\tif (list_length(args) == 2)\n\t{\n\t\t/* binary operator */\n\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\n\t\tget_rule_expr_paren(arg1, context, true, (Node *) expr);\n\t\tappendStringInfo(buf, \" %s \",\n\t\t\t\t\t\t generate_operator_name(opno,\n\t\t\t\t\t\t\t\t\t\t\t\texprType(arg1),\n\t\t\t\t\t\t\t\t\t\t\t\texprType(arg2)));\n\t\tget_rule_expr_paren(arg2, context, true, (Node *) expr);\n\t}\n\telse\n\t{\n\t\t/* prefix operator */\n\t\tNode\t   *arg = (Node *) linitial(args);\n\n\t\tappendStringInfo(buf, \"%s \",\n\t\t\t\t\t\t generate_operator_name(opno,\n\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid,\n\t\t\t\t\t\t\t\t\t\t\t\texprType(arg)));\n\t\tget_rule_expr_paren(arg, context, true, (Node *) expr);\n\t}\n\tif (!PRETTY_PAREN(context))\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_func_expr\t\t\t- Parse back a FuncExpr node\n */\nstatic void\nget_func_expr(FuncExpr *expr, deparse_context *context,\n\t\t\t  bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\tfuncoid = expr->funcid;\n\tOid\t\t\targtypes[FUNC_MAX_ARGS];\n\tint\t\t\tnargs;\n\tList\t   *argnames;\n\tbool\t\tuse_variadic;\n\tListCell   *l;\n\n\t/*\n\t * If the function call came from an implicit coercion, then just show the\n\t * first argument --- unless caller wants to see implicit coercions.\n\t */\n\tif (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)\n\t{\n\t\tget_rule_expr_paren((Node *) linitial(expr->args), context,\n\t\t\t\t\t\t\tfalse, (Node *) expr);\n\t\treturn;\n\t}\n\n\t/*\n\t * If the function call came from a cast, then show the first argument\n\t * plus an explicit cast operation.\n\t */\n\tif (expr->funcformat == COERCE_EXPLICIT_CAST ||\n\t\texpr->funcformat == COERCE_IMPLICIT_CAST)\n\t{\n\t\tNode\t   *arg = linitial(expr->args);\n\t\tOid\t\t\trettype = expr->funcresulttype;\n\t\tint32\t\tcoercedTypmod;\n\n\t\t/* Get the typmod if this is a length-coercion function */\n\t\t(void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);\n\n\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t  rettype, coercedTypmod,\n\t\t\t\t\t\t  (Node *) expr);\n\n\t\treturn;\n\t}\n\n\t/*\n\t * If the function was called using one of the SQL spec's random special\n\t * syntaxes, try to reproduce that.  If we don't recognize the function,\n\t * fall through.\n\t */\n\tif (expr->funcformat == COERCE_SQL_SYNTAX)\n\t{\n\t\tif (get_func_sql_syntax(expr, context))\n\t\t\treturn;\n\t}\n\n\n\t/*\n\t * Normal function: display as proname(args).  First we need to extract\n\t * the argument datatypes.\n\t */\n\tif (list_length(expr->args) > FUNC_MAX_ARGS)\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_TOO_MANY_ARGUMENTS),\n\t\t\t\t errmsg(\"too many arguments\")));\n\tnargs = 0;\n\targnames = NIL;\n\tforeach(l, expr->args)\n\t{\n\t\tNode\t   *arg = (Node *) lfirst(l);\n\n\t\tif (IsA(arg, NamedArgExpr))\n\t\t\targnames = lappend(argnames, ((NamedArgExpr *) arg)->name);\n\t\targtypes[nargs] = exprType(arg);\n\t\tnargs++;\n\t}\n\n\tappendStringInfo(buf, \"%s(\",\n\t\t\t\t\t generate_function_name(funcoid, nargs,\n\t\t\t\t\t\t\t\t\t\t\targnames, argtypes,\n\t\t\t\t\t\t\t\t\t\t\texpr->funcvariadic,\n\t\t\t\t\t\t\t\t\t\t\t&use_variadic,\n\t\t\t\t\t\t\t\t\t\t\tcontext->inGroupBy));\n\tnargs = 0;\n\tforeach(l, expr->args)\n\t{\n\t\tif (nargs++ > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tif (use_variadic && lnext(expr->args, l) == NULL)\n\t\t\tappendStringInfoString(buf, \"VARIADIC \");\n\t\tget_rule_expr((Node *) lfirst(l), context, true);\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_proc_expr\t\t\t- Parse back a CallStmt node\n */\nstatic void\nget_proc_expr(CallStmt *stmt, deparse_context *context,\n\t\t\t  bool showimplicit)\n{\n\tStringInfo buf = context->buf;\n\tOid        functionOid = stmt->funcexpr->funcid;\n\tbool       use_variadic;\n\tOid        *argumentTypes;\n\tList       *finalArgumentList = NIL;\n\tListCell   *argumentCell;\n\tList       *namedArgList = NIL;\n\tint        numberOfArgs = -1;\n\n\tif (!get_merged_argument_list(stmt, &namedArgList, &argumentTypes,\n\t\t\t\t\t\t\t\t\t\t&finalArgumentList, &numberOfArgs))\n\t{\n\t\t/* Nothing merged i.e. no OUT arguments */\n\t\tget_func_expr((FuncExpr *) stmt->funcexpr, context, showimplicit);\n\t\treturn;\n\t}\n\n\tappendStringInfo(buf, \"%s(\",\n\t\t\t\t\t generate_function_name(functionOid, numberOfArgs,\n\t\t\t\t\t\t\t\t\t\tnamedArgList, argumentTypes,\n\t\t\t\t\t\t\t\t\t\tstmt->funcexpr->funcvariadic,\n\t\t\t\t\t\t\t\t\t\t&use_variadic,\n\t\t\t\t\t\t\t\t\t\tcontext->inGroupBy));\n\tint argNumber = 0;\n\tforeach(argumentCell, finalArgumentList)\n\t{\n\t\tif (argNumber++ > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tif (use_variadic && lnext(finalArgumentList, argumentCell) == NULL)\n\t\t\tappendStringInfoString(buf, \"VARIADIC \");\n\t\tget_rule_expr((Node *) lfirst(argumentCell), context, true);\n\t\targNumber++;\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_agg_expr\t\t\t- Parse back an Aggref node\n */\nstatic void\nget_agg_expr(Aggref *aggref, deparse_context *context,\n\t\t\t Aggref *original_aggref)\n{\n\tget_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,\n\t\t\t\t\t\tfalse);\n}\n\n/*\n * get_agg_expr_helper\t\t- subroutine for get_agg_expr and\n *\t\t\t\t\t\t\tget_json_agg_constructor\n */\nstatic void\nget_agg_expr_helper(Aggref *aggref, deparse_context *context,\n\t\t\t\t\tAggref *original_aggref, const char *funcname,\n\t\t\t\t\tconst char *options, bool is_json_objectagg)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\targtypes[FUNC_MAX_ARGS];\n\tint\t\t\tnargs;\n\tbool use_variadic = false;\n\n\t/*\n\t * For a combining aggregate, we look up and deparse the corresponding\n\t * partial aggregate instead.  This is necessary because our input\n\t * argument list has been replaced; the new argument list always has just\n\t * one element, which will point to a partial Aggref that supplies us with\n\t * transition states to combine.\n\t */\n\tif (DO_AGGSPLIT_COMBINE(aggref->aggsplit))\n\t{\n\t\tTargetEntry *tle;\n\n\n\t\tAssert(list_length(aggref->args) == 1);\n\t\ttle = linitial_node(TargetEntry, aggref->args);\n\t\tresolve_special_varno((Node *) tle->expr, context,\n\t\t\t\t\t\t\t  get_agg_combine_expr, original_aggref);\n\t\treturn;\n\t}\n\n\t/*\n\t * Mark as PARTIAL, if appropriate.  We look to the original aggref so as\n\t * to avoid printing this when recursing from the code just above.\n\t */\n\tif (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit))\n\t\tappendStringInfoString(buf, \"PARTIAL \");\n\n\t/* Extract the argument types as seen by the parser */\n\tnargs = get_aggregate_argtypes(aggref, argtypes);\n\n\tif (!funcname)\n\t\tfuncname = generate_function_name(aggref->aggfnoid, nargs, NIL,\n\t\t\t\t\t\t\t\t\t\t  argtypes, aggref->aggvariadic,\n\t\t\t\t\t\t\t\t\t\t  &use_variadic,\n\t\t\t\t\t\t\t\t\t\t  context->inGroupBy);\n\n\t/* Print the aggregate name, schema-qualified if needed */\n\tappendStringInfo(buf, \"%s(%s\", funcname,\n\t\t\t\t\t (aggref->aggdistinct != NIL) ? \"DISTINCT \" : \"\");\n\n\tif (AGGKIND_IS_ORDERED_SET(aggref->aggkind))\n\t{\n\t\t/*\n\t\t * Ordered-set aggregates do not use \"*\" syntax.  Also, we needn't\n\t\t * worry about inserting VARIADIC.  So we can just dump the direct\n\t\t * args as-is.\n\t\t */\n\t\tAssert(!aggref->aggvariadic);\n\t\tget_rule_expr((Node *) aggref->aggdirectargs, context, true);\n\t\tAssert(aggref->aggorder != NIL);\n\t\tappendStringInfoString(buf, \") WITHIN GROUP (ORDER BY \");\n\t\tget_rule_orderby(aggref->aggorder, aggref->args, false, context);\n\t}\n\telse\n\t{\n\t\t/* aggstar can be set only in zero-argument aggregates */\n\t\tif (aggref->aggstar)\n\t\t\tappendStringInfoChar(buf, '*');\n\t\telse\n\t\t{\n\t\t\tListCell   *l;\n\t\t\tint\t\t\ti;\n\n\t\t\ti = 0;\n\t\t\tforeach(l, aggref->args)\n\t\t\t{\n\t\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\t\t\t\tNode\t   *arg = (Node *) tle->expr;\n\n\t\t\t\tAssert(!IsA(arg, NamedArgExpr));\n\t\t\t\tif (tle->resjunk)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (i++ > 0)\n\t\t\t\t{\n\t\t\t\t\tif (is_json_objectagg)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * the ABSENT ON NULL and WITH UNIQUE args are printed\n\t\t\t\t\t\t * separately, so ignore them here\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (i > 2)\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tappendStringInfoString(buf, \" : \");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t}\n\t\t\t\tif (use_variadic && i == nargs)\n\t\t\t\t\tappendStringInfoString(buf, \"VARIADIC \");\n\t\t\t\tget_rule_expr(arg, context, true);\n\t\t\t}\n\t\t}\n\n\t\tif (aggref->aggorder != NIL)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" ORDER BY \");\n\t\t\tget_rule_orderby(aggref->aggorder, aggref->args, false, context);\n\t\t}\n\t}\n\n\tif (options)\n\t\tappendStringInfoString(buf, options);\n\n\tif (aggref->aggfilter != NULL)\n\t{\n\t\tappendStringInfoString(buf, \") FILTER (WHERE \");\n\t\tget_rule_expr((Node *) aggref->aggfilter, context, false);\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * This is a helper function for get_agg_expr().  It's used when we deparse\n * a combining Aggref; resolve_special_varno locates the corresponding partial\n * Aggref and then calls this.\n */\nstatic void\nget_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)\n{\n\tAggref\t   *aggref;\n\tAggref\t   *original_aggref = callback_arg;\n\n\tif (!IsA(node, Aggref))\n\t\telog(ERROR, \"combining Aggref does not point to an Aggref\");\n\n\taggref = (Aggref *) node;\n\tget_agg_expr(aggref, context, original_aggref);\n}\n\n/*\n * get_windowfunc_expr\t- Parse back a WindowFunc node\n */\nstatic void\nget_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)\n{\n\tget_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);\n}\n\n\n/*\n * get_windowfunc_expr_helper\t- subroutine for get_windowfunc_expr and\n *\t\t\t\t\t\t\t\tget_json_agg_constructor\n */\nstatic void\nget_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,\n\t\t\t\t\t\t   const char *funcname, const char *options,\n\t\t\t\t\t\t   bool is_json_objectagg)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\targtypes[FUNC_MAX_ARGS];\n\tint\t\t\tnargs;\n\tList\t   *argnames;\n\tListCell   *l;\n\n\tif (list_length(wfunc->args) > FUNC_MAX_ARGS)\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_TOO_MANY_ARGUMENTS),\n\t\t\t\t errmsg(\"too many arguments\")));\n\tnargs = 0;\n\targnames = NIL;\n\tforeach(l, wfunc->args)\n\t{\n\t\tNode\t   *arg = (Node *) lfirst(l);\n\n\t\tif (IsA(arg, NamedArgExpr))\n\t\t\targnames = lappend(argnames, ((NamedArgExpr *) arg)->name);\n\t\targtypes[nargs] = exprType(arg);\n\t\tnargs++;\n\t}\n\n\tif (!funcname)\n\t\tfuncname = generate_function_name(wfunc->winfnoid, nargs, argnames,\n\t\t\t\t\t\t\t\t\t\t  argtypes, false, NULL,\n\t\t\t\t\t\t\t\t\t\t  context->inGroupBy);\n\n\tappendStringInfo(buf, \"%s(\", funcname);\n\n\t/* winstar can be set only in zero-argument aggregates */\n\tif (wfunc->winstar)\n\t\tappendStringInfoChar(buf, '*');\n\telse\n\t{\n\t\tif (is_json_objectagg)\n\t\t{\n\t\t\tget_rule_expr((Node *) linitial(wfunc->args), context, false);\n\t\t\tappendStringInfoString(buf, \" : \");\n\t\t\tget_rule_expr((Node *) lsecond(wfunc->args), context, false);\n\t\t}\n\t\telse\n\t\t\tget_rule_expr((Node *) wfunc->args, context, true);\n\t}\n\n\tif (options)\n\t\tappendStringInfoString(buf, options);\n\n\tif (wfunc->aggfilter != NULL)\n\t{\n\t\tappendStringInfoString(buf, \") FILTER (WHERE \");\n\t\tget_rule_expr((Node *) wfunc->aggfilter, context, false);\n\t}\n\n\tappendStringInfoString(buf, \") OVER \");\n\n\tforeach(l, context->windowClause)\n\t{\n\t\tWindowClause *wc = (WindowClause *) lfirst(l);\n\n\t\tif (wc->winref == wfunc->winref)\n\t\t{\n\t\t\tif (wc->name)\n\t\t\t\tappendStringInfoString(buf, quote_identifier(wc->name));\n\t\t\telse\n\t\t\t\tget_rule_windowspec(wc, context->targetList, context);\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (l == NULL)\n\t{\n\t\tif (context->windowClause)\n\t\t\telog(ERROR, \"could not find window clause for winref %u\",\n\t\t\t\t wfunc->winref);\n\n\t\t/*\n\t\t * In EXPLAIN, we don't have window context information available, so\n\t\t * we have to settle for this:\n\t\t */\n\t\tappendStringInfoString(buf, \"(?)\");\n\t}\n}\n\n/*\n * get_func_sql_syntax\t\t- Parse back a SQL-syntax function call\n *\n * Returns true if we successfully deparsed, false if we did not\n * recognize the function.\n */\nstatic bool\nget_func_sql_syntax(FuncExpr *expr, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\tfuncoid = expr->funcid;\n\n\tswitch (funcoid)\n\t{\n\t\tcase F_TIMEZONE_INTERVAL_TIMESTAMP:\n\t\tcase F_TIMEZONE_INTERVAL_TIMESTAMPTZ:\n\t\tcase F_TIMEZONE_INTERVAL_TIMETZ:\n\t\tcase F_TIMEZONE_TEXT_TIMESTAMP:\n\t\tcase F_TIMEZONE_TEXT_TIMESTAMPTZ:\n\t\tcase F_TIMEZONE_TEXT_TIMETZ:\n\t\t\t/* AT TIME ZONE ... note reversed argument order */\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr_paren((Node *) lsecond(expr->args), context, false,\n\t\t\t\t\t\t\t\t(Node *) expr);\n\t\t\tappendStringInfoString(buf, \" AT TIME ZONE \");\n\t\t\tget_rule_expr_paren((Node *) linitial(expr->args), context, false,\n\t\t\t\t\t\t\t\t(Node *) expr);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_TIMESTAMPTZ:\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ:\n\t\tcase F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_TIMESTAMP:\n\t\tcase F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_TIMESTAMP:\n\t\tcase F_OVERLAPS_TIMETZ_TIMETZ_TIMETZ_TIMETZ:\n\t\tcase F_OVERLAPS_TIME_INTERVAL_TIME_INTERVAL:\n\t\tcase F_OVERLAPS_TIME_INTERVAL_TIME_TIME:\n\t\tcase F_OVERLAPS_TIME_TIME_TIME_INTERVAL:\n\t\tcase F_OVERLAPS_TIME_TIME_TIME_TIME:\n\t\t\t/* (x1, x2) OVERLAPS (y1, y2) */\n\t\t\tappendStringInfoString(buf, \"((\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \") OVERLAPS (\");\n\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tget_rule_expr((Node *) lfourth(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\treturn true;\n\n\t\tcase F_EXTRACT_TEXT_DATE:\n\t\tcase F_EXTRACT_TEXT_TIME:\n\t\tcase F_EXTRACT_TEXT_TIMETZ:\n\t\tcase F_EXTRACT_TEXT_TIMESTAMP:\n\t\tcase F_EXTRACT_TEXT_TIMESTAMPTZ:\n\t\tcase F_EXTRACT_TEXT_INTERVAL:\n\t\t\t/* EXTRACT (x FROM y) */\n\t\t\tappendStringInfoString(buf, \"EXTRACT(\");\n\t\t\t{\n\t\t\t\tConst\t   *con = (Const *) linitial(expr->args);\n\n\t\t\t\tAssert(IsA(con, Const) &&\n\t\t\t\t\t   con->consttype == TEXTOID &&\n\t\t\t\t\t   !con->constisnull);\n\t\t\t\tappendStringInfoString(buf, TextDatumGetCString(con->constvalue));\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_IS_NORMALIZED:\n\t\t\t/* IS xxx NORMALIZED */\n\t\t\tappendStringInfoString(buf, \"(\");\n\t\t\tget_rule_expr_paren((Node *) linitial(expr->args), context, false,\n\t\t\t\t\t\t\t\t(Node *) expr);\n\t\t\tappendStringInfoString(buf, \" IS\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tConst\t   *con = (Const *) lsecond(expr->args);\n\n\t\t\t\tAssert(IsA(con, Const) &&\n\t\t\t\t\t   con->consttype == TEXTOID &&\n\t\t\t\t\t   !con->constisnull);\n\t\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\t\t\t\t TextDatumGetCString(con->constvalue));\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" NORMALIZED)\");\n\t\t\treturn true;\n\n\t\tcase F_PG_COLLATION_FOR:\n\t\t\t/* COLLATION FOR */\n\t\t\tappendStringInfoString(buf, \"COLLATION FOR (\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_NORMALIZE:\n\t\t\t/* NORMALIZE() */\n\t\t\tappendStringInfoString(buf, \"NORMALIZE(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tConst\t   *con = (Const *) lsecond(expr->args);\n\n\t\t\t\tAssert(IsA(con, Const) &&\n\t\t\t\t\t   con->consttype == TEXTOID &&\n\t\t\t\t\t   !con->constisnull);\n\t\t\t\tappendStringInfo(buf, \", %s\",\n\t\t\t\t\t\t\t\t TextDatumGetCString(con->constvalue));\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_OVERLAY_BIT_BIT_INT4:\n\t\tcase F_OVERLAY_BIT_BIT_INT4_INT4:\n\t\tcase F_OVERLAY_BYTEA_BYTEA_INT4:\n\t\tcase F_OVERLAY_BYTEA_BYTEA_INT4_INT4:\n\t\tcase F_OVERLAY_TEXT_TEXT_INT4:\n\t\tcase F_OVERLAY_TEXT_TEXT_INT4_INT4:\n\t\t\t/* OVERLAY() */\n\t\t\tappendStringInfoString(buf, \"OVERLAY(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" PLACING \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\tif (list_length(expr->args) == 4)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" FOR \");\n\t\t\t\tget_rule_expr((Node *) lfourth(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_POSITION_BIT_BIT:\n\t\tcase F_POSITION_BYTEA_BYTEA:\n\t\tcase F_POSITION_TEXT_TEXT:\n\t\t\t/* POSITION() ... extra parens since args are b_expr not a_expr */\n\t\t\tappendStringInfoString(buf, \"POSITION((\");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \") IN (\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\treturn true;\n\n\t\tcase F_SUBSTRING_BIT_INT4:\n\t\tcase F_SUBSTRING_BIT_INT4_INT4:\n\t\tcase F_SUBSTRING_BYTEA_INT4:\n\t\tcase F_SUBSTRING_BYTEA_INT4_INT4:\n\t\tcase F_SUBSTRING_TEXT_INT4:\n\t\tcase F_SUBSTRING_TEXT_INT4_INT4:\n\t\t\t/* SUBSTRING FROM/FOR (i.e., integer-position variants) */\n\t\t\tappendStringInfoString(buf, \"SUBSTRING(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tif (list_length(expr->args) == 3)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" FOR \");\n\t\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_SUBSTRING_TEXT_TEXT_TEXT:\n\t\t\t/* SUBSTRING SIMILAR/ESCAPE */\n\t\t\tappendStringInfoString(buf, \"SUBSTRING(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" SIMILAR \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" ESCAPE \");\n\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_BTRIM_BYTEA_BYTEA:\n\t\tcase F_BTRIM_TEXT:\n\t\tcase F_BTRIM_TEXT_TEXT:\n\t\t\t/* TRIM() */\n\t\t\tappendStringInfoString(buf, \"TRIM(BOTH\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_LTRIM_BYTEA_BYTEA:\n\t\tcase F_LTRIM_TEXT:\n\t\tcase F_LTRIM_TEXT_TEXT:\n\t\t\t/* TRIM() */\n\t\t\tappendStringInfoString(buf, \"TRIM(LEADING\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_RTRIM_BYTEA_BYTEA:\n\t\tcase F_RTRIM_TEXT:\n\t\tcase F_RTRIM_TEXT_TEXT:\n\t\t\t/* TRIM() */\n\t\t\tappendStringInfoString(buf, \"TRIM(TRAILING\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_SYSTEM_USER:\n\t\t\tappendStringInfoString(buf, \"SYSTEM_USER\");\n\t\t\treturn true;\n\n\t\tcase F_XMLEXISTS:\n\t\t\t/* XMLEXISTS ... extra parens because args are c_expr */\n\t\t\tappendStringInfoString(buf, \"XMLEXISTS((\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \") PASSING (\");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n/* ----------\n * get_coercion_expr\n *\n *\tMake a string representation of a value coerced to a specific type\n * ----------\n */\nstatic void\nget_coercion_expr(Node *arg, deparse_context *context,\n\t\t\t\t  Oid resulttype, int32 resulttypmod,\n\t\t\t\t  Node *parentNode)\n{\n\tStringInfo\tbuf = context->buf;\n\n\t/*\n\t * Since parse_coerce.c doesn't immediately collapse application of\n\t * length-coercion functions to constants, what we'll typically see in\n\t * such cases is a Const with typmod -1 and a length-coercion function\n\t * right above it.  Avoid generating redundant output. However, beware of\n\t * suppressing casts when the user actually wrote something like\n\t * 'foo'::text::char(3).\n\t *\n\t * Note: it might seem that we are missing the possibility of needing to\n\t * print a COLLATE clause for such a Const.  However, a Const could only\n\t * have nondefault collation in a post-constant-folding tree, in which the\n\t * length coercion would have been folded too.  See also the special\n\t * handling of CollateExpr in coerce_to_target_type(): any collation\n\t * marking will be above the coercion node, not below it.\n\t */\n\tif (arg && IsA(arg, Const) &&\n\t\t((Const *) arg)->consttype == resulttype &&\n\t\t((Const *) arg)->consttypmod == -1)\n\t{\n\t\t/* Show the constant without normal ::typename decoration */\n\t\tget_const_expr((Const *) arg, context, -1);\n\t}\n\telse\n\t{\n\t\tif (!PRETTY_PAREN(context))\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_rule_expr_paren(arg, context, false, parentNode);\n\t\tif (!PRETTY_PAREN(context))\n\t\t\tappendStringInfoChar(buf, ')');\n\t}\n\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t format_type_with_typemod(resulttype, resulttypmod));\n}\n\n/* ----------\n * get_const_expr\n *\n *\tMake a string representation of a Const\n *\n * showtype can be -1 to never show \"::typename\" decoration, or +1 to always\n * show it, or 0 to show it only if the constant wouldn't be assumed to be\n * the right type by default.\n *\n * If the Const's collation isn't default for its type, show that too.\n * We mustn't do this when showtype is -1 (since that means the caller will\n * print \"::typename\", and we can't put a COLLATE clause in between).  It's\n * caller's responsibility that collation isn't missed in such cases.\n * ----------\n */\nstatic void\nget_const_expr(Const *constval, deparse_context *context, int showtype)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\ttypoutput;\n\tbool\t\ttypIsVarlena;\n\tchar\t   *extval;\n\tbool\t\tneedlabel = false;\n\n\tif (constval->constisnull)\n\t{\n\t\t/*\n\t\t * Always label the type of a NULL constant to prevent misdecisions\n\t\t * about type when reparsing.\n\t\t */\n\t\tappendStringInfoString(buf, \"NULL\");\n\t\tif (showtype >= 0)\n\t\t{\n\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t format_type_with_typemod(constval->consttype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  constval->consttypmod));\n\t\t\tget_const_collation(constval, context);\n\t\t}\n\t\treturn;\n\t}\n\n\tgetTypeOutputInfo(constval->consttype,\n\t\t\t\t\t  &typoutput, &typIsVarlena);\n\n\textval = OidOutputFunctionCall(typoutput, constval->constvalue);\n\n\tswitch (constval->consttype)\n\t{\n\t\tcase INT4OID:\n\n\t\t\t/*\n\t\t\t * INT4 can be printed without any decoration, unless it is\n\t\t\t * negative; in that case print it as '-nnn'::integer to ensure\n\t\t\t * that the output will re-parse as a constant, not as a constant\n\t\t\t * plus operator.  In most cases we could get away with printing\n\t\t\t * (-nnn) instead, because of the way that gram.y handles negative\n\t\t\t * literals; but that doesn't work for INT_MIN, and it doesn't\n\t\t\t * seem that much prettier anyway.\n\t\t\t */\n\t\t\tif (extval[0] != '-')\n\t\t\t\tappendStringInfoString(buf, extval);\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"'%s'\", extval);\n\t\t\t\tneedlabel = true;\t/* we must attach a cast */\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase NUMERICOID:\n\n\t\t\t/*\n\t\t\t * NUMERIC can be printed without quotes if it looks like a float\n\t\t\t * constant (not an integer, and not Infinity or NaN) and doesn't\n\t\t\t * have a leading sign (for the same reason as for INT4).\n\t\t\t */\n\t\t\tif (isdigit((unsigned char) extval[0]) &&\n\t\t\t\tstrcspn(extval, \"eE.\") != strlen(extval))\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, extval);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"'%s'\", extval);\n\t\t\t\tneedlabel = true;\t/* we must attach a cast */\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase BITOID:\n\t\tcase VARBITOID:\n\t\t\tappendStringInfo(buf, \"B'%s'\", extval);\n\t\t\tbreak;\n\n\t\tcase BOOLOID:\n\t\t\tif (strcmp(extval, \"t\") == 0)\n\t\t\t\tappendStringInfoString(buf, \"true\");\n\t\t\telse\n\t\t\t\tappendStringInfoString(buf, \"false\");\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tsimple_quote_literal(buf, extval);\n\t\t\tbreak;\n\t}\n\n\tpfree(extval);\n\n\tif (showtype < 0)\n\t\treturn;\n\n\t/*\n\t * For showtype == 0, append ::typename unless the constant will be\n\t * implicitly typed as the right type when it is read in.\n\t *\n\t * XXX this code has to be kept in sync with the behavior of the parser,\n\t * especially make_const.\n\t */\n\tswitch (constval->consttype)\n\t{\n\t\tcase BOOLOID:\n\t\tcase UNKNOWNOID:\n\t\t\t/* These types can be left unlabeled */\n\t\t\tneedlabel = false;\n\t\t\tbreak;\n\t\tcase INT4OID:\n\t\t\t/* We determined above whether a label is needed */\n\t\t\tbreak;\n\t\tcase NUMERICOID:\n\n\t\t\t/*\n\t\t\t * Float-looking constants will be typed as numeric, which we\n\t\t\t * checked above; but if there's a nondefault typmod we need to\n\t\t\t * show it.\n\t\t\t */\n\t\t\tneedlabel |= (constval->consttypmod >= 0);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tneedlabel = true;\n\t\t\tbreak;\n\t}\n\tif (needlabel || showtype > 0)\n\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t format_type_with_typemod(constval->consttype,\n\t\t\t\t\t\t\t\t\t\t\t\t  constval->consttypmod));\n\n\tget_const_collation(constval, context);\n}\n\n/*\n * helper for get_const_expr: append COLLATE if needed\n */\nstatic void\nget_const_collation(Const *constval, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (OidIsValid(constval->constcollid))\n\t{\n\t\tOid\t\t\ttypcollation = get_typcollation(constval->consttype);\n\n\t\tif (constval->constcollid != typcollation)\n\t\t{\n\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t generate_collation_name(constval->constcollid));\n\t\t}\n\t}\n}\n\n/*\n * get_json_format\t\t\t- Parse back a JsonFormat node\n */\nstatic void\nget_json_format(JsonFormat *format, StringInfo buf)\n{\n\tif (format->format_type == JS_FORMAT_DEFAULT)\n\t\treturn;\n\n\tappendStringInfoString(buf,\n\t\t\t\t\t\t   format->format_type == JS_FORMAT_JSONB ?\n\t\t\t\t\t\t   \" FORMAT JSONB\" : \" FORMAT JSON\");\n\n\tif (format->encoding != JS_ENC_DEFAULT)\n\t{\n\t\tconst char *encoding;\n\n\t\tencoding =\n\t\t\tformat->encoding == JS_ENC_UTF16 ? \"UTF16\" :\n\t\t\tformat->encoding == JS_ENC_UTF32 ? \"UTF32\" : \"UTF8\";\n\n\t\tappendStringInfo(buf, \" ENCODING %s\", encoding);\n\t}\n}\n\n/*\n * get_json_returning\t\t- Parse back a JsonReturning structure\n */\nstatic void\nget_json_returning(JsonReturning *returning, StringInfo buf,\n\t\t\t\t   bool json_format_by_default)\n{\n\tif (!OidIsValid(returning->typid))\n\t\treturn;\n\n\tappendStringInfo(buf, \" RETURNING %s\",\n\t\t\t\t\t format_type_with_typemod(returning->typid,\n\t\t\t\t\t\t\t\t\t\t\t  returning->typmod));\n\n\tif (!json_format_by_default ||\n\t\treturning->format->format_type !=\n\t\t(returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))\n\t\tget_json_format(returning->format, buf);\n}\n\n/*\n * get_json_constructor\t\t- Parse back a JsonConstructorExpr node\n */\nstatic void\nget_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,\n\t\t\t\t\t bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *funcname;\n\tbool\t\tis_json_object;\n\tint\t\t\tcurridx;\n\tListCell   *lc;\n\n\tif (ctor->type == JSCTOR_JSON_OBJECTAGG)\n\t{\n\t\tget_json_agg_constructor(ctor, context, \"JSON_OBJECTAGG\", true);\n\t\treturn;\n\t}\n\telse if (ctor->type == JSCTOR_JSON_ARRAYAGG)\n\t{\n\t\tget_json_agg_constructor(ctor, context, \"JSON_ARRAYAGG\", false);\n\t\treturn;\n\t}\n\n\tswitch (ctor->type)\n\t{\n\t\tcase JSCTOR_JSON_OBJECT:\n\t\t\tfuncname = \"JSON_OBJECT\";\n\t\t\tbreak;\n\t\tcase JSCTOR_JSON_ARRAY:\n\t\t\tfuncname = \"JSON_ARRAY\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\telog(ERROR, \"invalid JsonConstructorType %d\", ctor->type);\n\t}\n\n\tappendStringInfo(buf, \"%s(\", funcname);\n\n\tis_json_object = ctor->type == JSCTOR_JSON_OBJECT;\n\tforeach(lc, ctor->args)\n\t{\n\t\tcurridx = foreach_current_index(lc);\n\t\tif (curridx > 0)\n\t\t{\n\t\t\tconst char *sep;\n\n\t\t\tsep = (is_json_object && (curridx % 2) != 0) ? \" : \" : \", \";\n\t\t\tappendStringInfoString(buf, sep);\n\t\t}\n\n\t\tget_rule_expr((Node *) lfirst(lc), context, true);\n\t}\n\n\tget_json_constructor_options(ctor, buf);\n\tappendStringInfo(buf, \")\");\n}\n\n/*\n * Append options, if any, to the JSON constructor being deparsed\n */\nstatic void\nget_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)\n{\n\tif (ctor->absent_on_null)\n\t{\n\t\tif (ctor->type == JSCTOR_JSON_OBJECT ||\n\t\t\tctor->type == JSCTOR_JSON_OBJECTAGG)\n\t\t\tappendStringInfoString(buf, \" ABSENT ON NULL\");\n\t}\n\telse\n\t{\n\t\tif (ctor->type == JSCTOR_JSON_ARRAY ||\n\t\t\tctor->type == JSCTOR_JSON_ARRAYAGG)\n\t\t\tappendStringInfoString(buf, \" NULL ON NULL\");\n\t}\n\n\tif (ctor->unique)\n\t\tappendStringInfoString(buf, \" WITH UNIQUE KEYS\");\n\n\tget_json_returning(ctor->returning, buf, true);\n}\n\n/*\n * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node\n */\nstatic void\nget_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,\n\t\t\t\t\t\t const char *funcname, bool is_json_objectagg)\n{\n\tStringInfoData options;\n\n\tinitStringInfo(&options);\n\tget_json_constructor_options(ctor, &options);\n\n\tif (IsA(ctor->func, Aggref))\n\t\tget_agg_expr_helper((Aggref *) ctor->func, context,\n\t\t\t\t\t\t\t(Aggref *) ctor->func,\n\t\t\t\t\t\t\tfuncname, options.data, is_json_objectagg);\n\telse if (IsA(ctor->func, WindowFunc))\n\t\tget_windowfunc_expr_helper((WindowFunc *) ctor->func, context,\n\t\t\t\t\t\t\t\t   funcname, options.data,\n\t\t\t\t\t\t\t\t   is_json_objectagg);\n\telse\n\t\telog(ERROR, \"invalid JsonConstructorExpr underlying node type: %d\",\n\t\t\t nodeTag(ctor->func));\n}\n\n/*\n * simple_quote_literal - Format a string as a SQL literal, append to buf\n */\nstatic void\nsimple_quote_literal(StringInfo buf, const char *val)\n{\n\tconst char *valptr;\n\n\t/*\n\t * We form the string literal according to the prevailing setting of\n\t * standard_conforming_strings; we never use E''. User is responsible for\n\t * making sure result is used correctly.\n\t */\n\tappendStringInfoChar(buf, '\\'');\n\tfor (valptr = val; *valptr; valptr++)\n\t{\n\t\tchar\t\tch = *valptr;\n\n\t\tif (SQL_STR_DOUBLE(ch, !standard_conforming_strings))\n\t\t\tappendStringInfoChar(buf, ch);\n\t\tappendStringInfoChar(buf, ch);\n\t}\n\tappendStringInfoChar(buf, '\\'');\n}\n\n/* ----------\n * get_sublink_expr\t\t\t- Parse back a sublink\n * ----------\n */\nstatic void\nget_sublink_expr(SubLink *sublink, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tQuery\t   *query = (Query *) (sublink->subselect);\n\tchar\t   *opname = NULL;\n\tbool\t\tneed_paren;\n\n\tif (sublink->subLinkType == ARRAY_SUBLINK)\n\t\tappendStringInfoString(buf, \"ARRAY(\");\n\telse\n\t\tappendStringInfoChar(buf, '(');\n\n\t/*\n\t * Note that we print the name of only the first operator, when there are\n\t * multiple combining operators.  This is an approximation that could go\n\t * wrong in various scenarios (operators in different schemas, renamed\n\t * operators, etc) but there is not a whole lot we can do about it, since\n\t * the syntax allows only one operator to be shown.\n\t */\n\tif (sublink->testexpr)\n\t{\n\t\tif (IsA(sublink->testexpr, OpExpr))\n\t\t{\n\t\t\t/* single combining operator */\n\t\t\tOpExpr\t   *opexpr = (OpExpr *) sublink->testexpr;\n\n\t\t\tget_rule_expr(linitial(opexpr->args), context, true);\n\t\t\topname = generate_operator_name(opexpr->opno,\n\t\t\t\t\t\t\t\t\t\t\texprType(linitial(opexpr->args)),\n\t\t\t\t\t\t\t\t\t\t\texprType(lsecond(opexpr->args)));\n\t\t}\n\t\telse if (IsA(sublink->testexpr, BoolExpr))\n\t\t{\n\t\t\t/* multiple combining operators, = or <> cases */\n\t\t\tchar\t   *sep;\n\t\t\tListCell   *l;\n\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tsep = \"\";\n\t\t\tforeach(l, ((BoolExpr *) sublink->testexpr)->args)\n\t\t\t{\n\t\t\t\tOpExpr\t   *opexpr = lfirst_node(OpExpr, l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_expr(linitial(opexpr->args), context, true);\n\t\t\t\tif (!opname)\n\t\t\t\t\topname = generate_operator_name(opexpr->opno,\n\t\t\t\t\t\t\t\t\t\t\t\t\texprType(linitial(opexpr->args)),\n\t\t\t\t\t\t\t\t\t\t\t\t\texprType(lsecond(opexpr->args)));\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse if (IsA(sublink->testexpr, RowCompareExpr))\n\t\t{\n\t\t\t/* multiple combining operators, < <= > >= cases */\n\t\t\tRowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;\n\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr((Node *) rcexpr->largs, context, true);\n\t\t\topname = generate_operator_name(linitial_oid(rcexpr->opnos),\n\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->largs)),\n\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->rargs)));\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse\n\t\t\telog(ERROR, \"unrecognized testexpr type: %d\",\n\t\t\t\t (int) nodeTag(sublink->testexpr));\n\t}\n\n\tneed_paren = true;\n\n\tswitch (sublink->subLinkType)\n\t{\n\t\tcase EXISTS_SUBLINK:\n\t\t\tappendStringInfoString(buf, \"EXISTS \");\n\t\t\tbreak;\n\n\t\tcase ANY_SUBLINK:\n\t\t\tif (strcmp(opname, \"=\") == 0)\t/* Represent = ANY as IN */\n\t\t\t\tappendStringInfoString(buf, \" IN \");\n\t\t\telse\n\t\t\t\tappendStringInfo(buf, \" %s ANY \", opname);\n\t\t\tbreak;\n\n\t\tcase ALL_SUBLINK:\n\t\t\tappendStringInfo(buf, \" %s ALL \", opname);\n\t\t\tbreak;\n\n\t\tcase ROWCOMPARE_SUBLINK:\n\t\t\tappendStringInfo(buf, \" %s \", opname);\n\t\t\tbreak;\n\n\t\tcase EXPR_SUBLINK:\n\t\tcase MULTIEXPR_SUBLINK:\n\t\tcase ARRAY_SUBLINK:\n\t\t\tneed_paren = false;\n\t\t\tbreak;\n\n\t\tcase CTE_SUBLINK:\t\t/* shouldn't occur in a SubLink */\n\t\tdefault:\n\t\t\telog(ERROR, \"unrecognized sublink type: %d\",\n\t\t\t\t (int) sublink->subLinkType);\n\t\t\tbreak;\n\t}\n\n\tif (need_paren)\n\t\tappendStringInfoChar(buf, '(');\n\n\tget_query_def(query, buf, context->namespaces, NULL, false,\n\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t  context->indentLevel);\n\n\tif (need_paren)\n\t\tappendStringInfoString(buf, \"))\");\n\telse\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/* ----------\n * get_tablefunc\t\t\t- Parse back a table function\n * ----------\n */\nstatic void\nget_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\n\t/* XMLTABLE is the only existing implementation. */\n\n\tappendStringInfoString(buf, \"XMLTABLE(\");\n\n\tif (tf->ns_uris != NIL)\n\t{\n\t\tListCell   *lc1,\n\t\t\t\t   *lc2;\n\t\tbool\t\tfirst = true;\n\n\t\tappendStringInfoString(buf, \"XMLNAMESPACES (\");\n\t\tforboth(lc1, tf->ns_uris, lc2, tf->ns_names)\n\t\t{\n\t\t\tNode\t   *expr = (Node *) lfirst(lc1);\n\t\t\tchar\t   *name = strVal(lfirst(lc2));\n\n\t\t\tif (!first)\n\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\telse\n\t\t\t\tfirst = false;\n\n\t\t\tif (name != NULL)\n\t\t\t{\n\t\t\t\tget_rule_expr(expr, context, showimplicit);\n\t\t\t\tappendStringInfo(buf, \" AS %s\", name);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \"DEFAULT \");\n\t\t\t\tget_rule_expr(expr, context, showimplicit);\n\t\t\t}\n\t\t}\n\t\tappendStringInfoString(buf, \"), \");\n\t}\n\n\tappendStringInfoChar(buf, '(');\n\tget_rule_expr((Node *) tf->rowexpr, context, showimplicit);\n\tappendStringInfoString(buf, \") PASSING (\");\n\tget_rule_expr((Node *) tf->docexpr, context, showimplicit);\n\tappendStringInfoChar(buf, ')');\n\n\tif (tf->colexprs != NIL)\n\t{\n\t\tListCell   *l1;\n\t\tListCell   *l2;\n\t\tListCell   *l3;\n\t\tListCell   *l4;\n\t\tListCell   *l5;\n\t\tint\t\t\tcolnum = 0;\n\n\t\tappendStringInfoString(buf, \" COLUMNS \");\n\t\tforfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,\n\t\t\t\tl4, tf->colexprs, l5, tf->coldefexprs)\n\t\t{\n\t\t\tchar\t   *colname = strVal(lfirst(l1));\n\t\t\tOid\t\t\ttypid = lfirst_oid(l2);\n\t\t\tint32\t\ttypmod = lfirst_int(l3);\n\t\t\tNode\t   *colexpr = (Node *) lfirst(l4);\n\t\t\tNode\t   *coldefexpr = (Node *) lfirst(l5);\n\t\t\tbool\t\tordinality = (tf->ordinalitycol == colnum);\n\t\t\tbool\t\tnotnull = bms_is_member(colnum, tf->notnulls);\n\n\t\t\tif (colnum > 0)\n\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tcolnum++;\n\n\t\t\tappendStringInfo(buf, \"%s %s\", quote_identifier(colname),\n\t\t\t\t\t\t\t ordinality ? \"FOR ORDINALITY\" :\n\t\t\t\t\t\t\t format_type_with_typemod(typid, typmod));\n\t\t\tif (ordinality)\n\t\t\t\tcontinue;\n\n\t\t\tif (coldefexpr != NULL)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" DEFAULT (\");\n\t\t\t\tget_rule_expr((Node *) coldefexpr, context, showimplicit);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tif (colexpr != NULL)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" PATH (\");\n\t\t\t\tget_rule_expr((Node *) colexpr, context, showimplicit);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tif (notnull)\n\t\t\t\tappendStringInfoString(buf, \" NOT NULL\");\n\t\t}\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/* ----------\n * get_from_clause\t\t\t- Parse back a FROM clause\n *\n * \"prefix\" is the keyword that denotes the start of the list of FROM\n * elements. It is FROM when used to parse back SELECT and UPDATE, but\n * is USING when parsing back DELETE.\n * ----------\n */\nstatic void\nget_from_clause(Query *query, const char *prefix, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tfirst = true;\n\tListCell   *l;\n\n\t/*\n\t * We use the query's jointree as a guide to what to print.  However, we\n\t * must ignore auto-added RTEs that are marked not inFromCl. (These can\n\t * only appear at the top level of the jointree, so it's sufficient to\n\t * check here.)  This check also ensures we ignore the rule pseudo-RTEs\n\t * for NEW and OLD.\n\t */\n\tforeach(l, query->jointree->fromlist)\n\t{\n\t\tNode\t   *jtnode = (Node *) lfirst(l);\n\n\t\tif (IsA(jtnode, RangeTblRef))\n\t\t{\n\t\t\tint\t\t\tvarno = ((RangeTblRef *) jtnode)->rtindex;\n\t\t\tRangeTblEntry *rte = rt_fetch(varno, query->rtable);\n\n\t\t\tif (!rte->inFromCl)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (first)\n\t\t{\n\t\t\tappendContextKeyword(context, prefix,\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\t\t\tfirst = false;\n\n\t\t\tget_from_clause_item(jtnode, query, context);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tStringInfoData itembuf;\n\n\t\t\tappendStringInfoString(buf, \", \");\n\n\t\t\t/*\n\t\t\t * Put the new FROM item's text into itembuf so we can decide\n\t\t\t * after we've got it whether or not it needs to go on a new line.\n\t\t\t */\n\t\t\tinitStringInfo(&itembuf);\n\t\t\tcontext->buf = &itembuf;\n\n\t\t\tget_from_clause_item(jtnode, query, context);\n\n\t\t\t/* Restore context's output buffer */\n\t\t\tcontext->buf = buf;\n\n\t\t\t/* Consider line-wrapping if enabled */\n\t\t\tif (PRETTY_INDENT(context) && context->wrapColumn >= 0)\n\t\t\t{\n\t\t\t\t/* Does the new item start with a new line? */\n\t\t\t\tif (itembuf.len > 0 && itembuf.data[0] == '\\n')\n\t\t\t\t{\n\t\t\t\t\t/* If so, we shouldn't add anything */\n\t\t\t\t\t/* instead, remove any trailing spaces currently in buf */\n\t\t\t\t\tremoveStringInfoSpaces(buf);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tchar\t   *trailing_nl;\n\n\t\t\t\t\t/* Locate the start of the current line in the buffer */\n\t\t\t\t\ttrailing_nl = strrchr(buf->data, '\\n');\n\t\t\t\t\tif (trailing_nl == NULL)\n\t\t\t\t\t\ttrailing_nl = buf->data;\n\t\t\t\t\telse\n\t\t\t\t\t\ttrailing_nl++;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Add a newline, plus some indentation, if the new item\n\t\t\t\t\t * would cause an overflow.\n\t\t\t\t\t */\n\t\t\t\t\tif (strlen(trailing_nl) + itembuf.len > context->wrapColumn)\n\t\t\t\t\t\tappendContextKeyword(context, \"\", -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_VAR);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Add the new item */\n\t\t\tappendStringInfoString(buf, itembuf.data);\n\n\t\t\t/* clean up */\n\t\t\tpfree(itembuf.data);\n\t\t}\n\t}\n}\n\nstatic void\nget_from_clause_item(Node *jtnode, Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tdeparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);\n\n\tif (IsA(jtnode, RangeTblRef))\n\t{\n\t\tint\t\t\tvarno = ((RangeTblRef *) jtnode)->rtindex;\n\t\tRangeTblEntry *rte = rt_fetch(varno, query->rtable);\n\t\tdeparse_columns *colinfo = deparse_columns_fetch(varno, dpns);\n\t\tRangeTblFunction *rtfunc1 = NULL;\n\t\tCitusRTEKind rteKind = GetRangeTblKind(rte);\n\n\t\tif (rte->lateral)\n\t\t\tappendStringInfoString(buf, \"LATERAL \");\n\n\t\t/* Print the FROM item proper */\n\t\tswitch (rte->rtekind)\n\t\t{\n\t\t\tcase RTE_RELATION:\n\t\t\t\t/* Normal relation RTE */\n\t\t\t\tappendStringInfo(buf, \"%s%s\",\n\t\t\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->namespaces));\n\t\t\t\tbreak;\n\t\t\tcase RTE_SUBQUERY:\n\t\t\t\t/* Subquery RTE */\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_query_def(rte->subquery, buf, context->namespaces, NULL,\n\t\t\t\t              true,\n\t\t\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t\t\t  context->indentLevel);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tbreak;\n\t\t\tcase RTE_FUNCTION:\n\t\t\t\t/* if it's a shard, do differently */\n\t\t\t\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t\t\t\t{\n\t\t\t\t\tchar *fragmentSchemaName = NULL;\n\t\t\t\t\tchar *fragmentTableName = NULL;\n\n\t\t\t\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t\t\t\t/* use schema and table name from the remote alias */\n\t\t\t\t\tappendStringInfo(buf, \"%s%s\",\n\t\t\t\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfragmentTableName));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t/* Function RTE */\n\t\t\t\trtfunc1 = (RangeTblFunction *) linitial(rte->functions);\n\n\t\t\t\t/*\n\t\t\t\t * Omit ROWS FROM() syntax for just one function, unless it\n\t\t\t\t * has both a coldeflist and WITH ORDINALITY. If it has both,\n\t\t\t\t * we must use ROWS FROM() syntax to avoid ambiguity about\n\t\t\t\t * whether the coldeflist includes the ordinality column.\n\t\t\t\t */\n\t\t\t\tif (list_length(rte->functions) == 1 &&\n\t\t\t\t\t(rtfunc1->funccolnames == NIL || !rte->funcordinality))\n\t\t\t\t{\n\t\t\t\t\tget_rule_expr_funccall(rtfunc1->funcexpr, context, true);\n\t\t\t\t\t/* we'll print the coldeflist below, if it has one */\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tbool\t\tall_unnest;\n\t\t\t\t\tListCell   *lc;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * If all the function calls in the list are to unnest,\n\t\t\t\t\t * and none need a coldeflist, then collapse the list back\n\t\t\t\t\t * down to UNNEST(args).  (If we had more than one\n\t\t\t\t\t * built-in unnest function, this would get more\n\t\t\t\t\t * difficult.)\n\t\t\t\t\t *\n\t\t\t\t\t * XXX This is pretty ugly, since it makes not-terribly-\n\t\t\t\t\t * future-proof assumptions about what the parser would do\n\t\t\t\t\t * with the output; but the alternative is to emit our\n\t\t\t\t\t * nonstandard ROWS FROM() notation for what might have\n\t\t\t\t\t * been a perfectly spec-compliant multi-argument\n\t\t\t\t\t * UNNEST().\n\t\t\t\t\t */\n\t\t\t\t\tall_unnest = true;\n\t\t\t\t\tforeach(lc, rte->functions)\n\t\t\t\t\t{\n\t\t\t\t\t\tRangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);\n\n\t\t\t\t\t\tif (!IsA(rtfunc->funcexpr, FuncExpr) ||\n\t\t\t\t\t\t\t((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||\n\t\t\t\t\t\t\trtfunc->funccolnames != NIL)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tall_unnest = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (all_unnest)\n\t\t\t\t\t{\n\t\t\t\t\t\tList\t   *allargs = NIL;\n\n\t\t\t\t\t\tforeach(lc, rte->functions)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);\n\t\t\t\t\t\t\tList\t   *args = ((FuncExpr *) rtfunc->funcexpr)->args;\n\n\t\t\t\t\t\t\tallargs = list_concat(allargs, args);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"UNNEST(\");\n\t\t\t\t\t\tget_rule_expr((Node *) allargs, context, true);\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tint\t\t\tfuncno = 0;\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"ROWS FROM(\");\n\t\t\t\t\t\tforeach(lc, rte->functions)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);\n\n\t\t\t\t\t\t\tif (funcno > 0)\n\t\t\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\t\t\tget_rule_expr_funccall(rtfunc->funcexpr, context, true);\n\t\t\t\t\t\t\tif (rtfunc->funccolnames != NIL)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t/* Reconstruct the column definition list */\n\t\t\t\t\t\t\t\tappendStringInfoString(buf, \" AS \");\n\t\t\t\t\t\t\t\tget_from_clause_coldeflist(rtfunc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   context);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfuncno++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t}\n\t\t\t\t\t/* prevent printing duplicate coldeflist below */\n\t\t\t\t\trtfunc1 = NULL;\n\t\t\t\t}\n\t\t\t\tif (rte->funcordinality)\n\t\t\t\t\tappendStringInfoString(buf, \" WITH ORDINALITY\");\n\t\t\t\tbreak;\n\t\t\tcase RTE_TABLEFUNC:\n\t\t\t\tget_tablefunc(rte->tablefunc, context, true);\n\t\t\t\tbreak;\n\t\t\tcase RTE_VALUES:\n\t\t\t\t/* Values list RTE */\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_values_def(rte->values_lists, context);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tbreak;\n\t\t\tcase RTE_CTE:\n\t\t\t\tappendStringInfoString(buf, quote_identifier(rte->ctename));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\telog(ERROR, \"unrecognized RTE kind: %d\", (int) rte->rtekind);\n\t\t\t\tbreak;\n\t\t}\n\n\t\t/* Print the relation alias, if needed */\n\t\tget_rte_alias(rte, varno, false, context);\n\n\t\t/* Print the column definitions or aliases, if needed */\n\t\tif (rtfunc1 && rtfunc1->funccolnames != NIL)\n\t\t{\n\t\t\t/* Reconstruct the columndef list, which is also the aliases */\n\t\t\tget_from_clause_coldeflist(rtfunc1, colinfo, context);\n\t\t}\n\t\telse if (GetRangeTblKind(rte) != CITUS_RTE_SHARD ||\n\t\t\t\t (rte->alias != NULL && rte->alias->colnames != NIL))\n\t\t{\n\t\t\t/* Else print column aliases as needed */\n\t\t\tget_column_alias_list(colinfo, context);\n\t\t}\n\t\t/* check if column's are given aliases in distributed tables */\n\t\telse if (colinfo->parentUsing != NIL)\n\t\t{\n\t\t\tAssert(colinfo->printaliases);\n\t\t\tget_column_alias_list(colinfo, context);\n\t\t}\n\n\t\t/* Tablesample clause must go after any alias */\n\t\tif ((rteKind == CITUS_RTE_RELATION || rteKind == CITUS_RTE_SHARD) &&\n\t\t\trte->tablesample)\n\t\t{\n\t\t\tget_tablesample_def(rte->tablesample, context);\n\t\t}\n\t}\n\telse if (IsA(jtnode, JoinExpr))\n\t{\n\t\tJoinExpr   *j = (JoinExpr *) jtnode;\n\t\tdeparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);\n\t\tbool\t\tneed_paren_on_right;\n\n\t\tneed_paren_on_right = PRETTY_PAREN(context) &&\n\t\t\t!IsA(j->rarg, RangeTblRef) &&\n\t\t\t!(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);\n\n\t\tif (!PRETTY_PAREN(context) || j->alias != NULL)\n\t\t\tappendStringInfoChar(buf, '(');\n\n\t\tget_from_clause_item(j->larg, query, context);\n\n\t\tswitch (j->jointype)\n\t\t{\n\t\t\tcase JOIN_INNER:\n\t\t\t\tif (j->quals)\n\t\t\t\t\tappendContextKeyword(context, \" JOIN \",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\telse\n\t\t\t\t\tappendContextKeyword(context, \" CROSS JOIN \",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tcase JOIN_LEFT:\n\t\t\t\tappendContextKeyword(context, \" LEFT JOIN \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tcase JOIN_FULL:\n\t\t\t\tappendContextKeyword(context, \" FULL JOIN \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tcase JOIN_RIGHT:\n\t\t\t\tappendContextKeyword(context, \" RIGHT JOIN \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\telog(ERROR, \"unrecognized join type: %d\",\n\t\t\t\t\t (int) j->jointype);\n\t\t}\n\n\t\tif (need_paren_on_right)\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_from_clause_item(j->rarg, query, context);\n\t\tif (need_paren_on_right)\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\tif (j->usingClause)\n\t\t{\n\t\t\tListCell   *lc;\n\t\t\tbool\t\tfirst = true;\n\n\t\t\tappendStringInfoString(buf, \" USING (\");\n\t\t\t/* Use the assigned names, not what's in usingClause */\n\t\t\tforeach(lc, colinfo->usingNames)\n\t\t\t{\n\t\t\t\tchar\t   *colname = (char *) lfirst(lc);\n\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf, quote_identifier(colname));\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\tif (j->join_using_alias)\n\t\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t\t quote_identifier(j->join_using_alias->aliasname));\n\t\t}\n\t\telse if (j->quals)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" ON \");\n\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr(j->quals, context, false);\n\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse if (j->jointype != JOIN_INNER)\n\t\t{\n\t\t\t/* If we didn't say CROSS JOIN above, we must provide an ON */\n\t\t\tappendStringInfoString(buf, \" ON TRUE\");\n\t\t}\n\n\t\tif (!PRETTY_PAREN(context) || j->alias != NULL)\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t/* Yes, it's correct to put alias after the right paren ... */\n\t\tif (j->alias != NULL)\n\t\t{\n\t\t\t/*\n\t\t\t * Note that it's correct to emit an alias clause if and only if\n\t\t\t * there was one originally.  Otherwise we'd be converting a named\n\t\t\t * join to unnamed or vice versa, which creates semantic\n\t\t\t * subtleties we don't want.  However, we might print a different\n\t\t\t * alias name than was there originally.\n\t\t\t */\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\t\t\t quote_identifier(get_rtable_name(j->rtindex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  context)));\n\t\t\tget_column_alias_list(colinfo, context);\n\t\t}\n\t}\n\telse\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(jtnode));\n}\n\n/*\n * get_rte_alias - print the relation's alias, if needed\n *\n * If printed, the alias is preceded by a space, or by \" AS \" if use_as is true.\n */\nstatic void\nget_rte_alias(RangeTblEntry *rte, int varno, bool use_as,\n\t\t\t  deparse_context *context)\n{\n\tdeparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);\n\tchar\t   *refname = get_rtable_name(varno, context);\n\tdeparse_columns *colinfo = deparse_columns_fetch(varno, dpns);\n\tbool\t\tprintalias = false;\n\n\tif (rte->alias != NULL)\n\t{\n\t\t/* Always print alias if user provided one */\n\t\tprintalias = true;\n\t}\n\telse if (colinfo->printaliases)\n\t{\n\t\t/* Always print alias if we need to print column aliases */\n\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_RELATION)\n\t{\n\t\t/*\n\t\t * No need to print alias if it's same as relation name (this would\n\t\t * normally be the case, but not if set_rtable_names had to resolve a\n\t\t * conflict).\n\t\t */\n\t\tif (strcmp(refname, get_relation_name(rte->relid)) != 0)\n\t\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_FUNCTION)\n\t{\n\t\t/*\n\t\t * For a function RTE, always print alias.  This covers possible\n\t\t * renaming of the function and/or instability of the FigureColname\n\t\t * rules for things that aren't simple functions.  Note we'd need to\n\t\t * force it anyway for the columndef list case.\n\t\t */\n\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_SUBQUERY ||\n\t\t\t rte->rtekind == RTE_VALUES)\n\t{\n\t\t/*\n\t\t * For a subquery, always print alias.  This makes the output\n\t\t * SQL-spec-compliant, even though we allow such aliases to be omitted\n\t\t * on input.\n\t\t */\n\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_CTE)\n\t{\n\t\t/*\n\t\t * No need to print alias if it's same as CTE name (this would\n\t\t * normally be the case, but not if set_rtable_names had to resolve a\n\t\t * conflict).\n\t\t */\n\t\tif (strcmp(refname, rte->ctename) != 0)\n\t\t\tprintalias = true;\n\t}\n\n\tif (printalias)\n\t\tappendStringInfo(context->buf, \"%s%s\",\n\t\t\t\t\t\t use_as ? \" AS \" : \" \",\n\t\t\t\t\t\t quote_identifier(refname));\n}\n\n/*\n * get_column_alias_list - print column alias list for an RTE\n *\n * Caller must already have printed the relation's alias name.\n */\nstatic void\nget_column_alias_list(deparse_columns *colinfo, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tint\t\t\ti;\n\tbool\t\tfirst = true;\n\n\t/* Don't print aliases if not needed */\n\tif (!colinfo->printaliases)\n\t\treturn;\n\n\tfor (i = 0; i < colinfo->num_new_cols; i++)\n\t{\n\t\tchar\t   *colname = colinfo->new_colnames[i];\n\n\t\tif (first)\n\t\t{\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tfirst = false;\n\t\t}\n\t\telse\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tappendStringInfoString(buf, quote_identifier(colname));\n\t}\n\tif (!first)\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_from_clause_coldeflist - reproduce FROM clause coldeflist\n *\n * When printing a top-level coldeflist (which is syntactically also the\n * relation's column alias list), use column names from colinfo.  But when\n * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the\n * original coldeflist's names, which are available in rtfunc->funccolnames.\n * Pass NULL for colinfo to select the latter behavior.\n *\n * The coldeflist is appended immediately (no space) to buf.  Caller is\n * responsible for ensuring that an alias or AS is present before it.\n */\nstatic void\nget_from_clause_coldeflist(RangeTblFunction *rtfunc,\n\t\t\t\t\t\t   deparse_columns *colinfo,\n\t\t\t\t\t\t   deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tListCell   *l1;\n\tListCell   *l2;\n\tListCell   *l3;\n\tListCell   *l4;\n\tint\t\t\ti;\n\n\tappendStringInfoChar(buf, '(');\n\n\ti = 0;\n\tforfour(l1, rtfunc->funccoltypes,\n\t\t\tl2, rtfunc->funccoltypmods,\n\t\t\tl3, rtfunc->funccolcollations,\n\t\t\tl4, rtfunc->funccolnames)\n\t{\n\t\tOid\t\t\tatttypid = lfirst_oid(l1);\n\t\tint32\t\tatttypmod = lfirst_int(l2);\n\t\tOid\t\t\tattcollation = lfirst_oid(l3);\n\t\tchar\t   *attname;\n\n\t\tif (colinfo)\n\t\t\tattname = colinfo->colnames[i];\n\t\telse\n\t\t\tattname = strVal(lfirst(l4));\n\n\t\tAssert(attname);\t\t/* shouldn't be any dropped columns here */\n\n\t\tif (i > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tappendStringInfo(buf, \"%s %s\",\n\t\t\t\t\t\t quote_identifier(attname),\n\t\t\t\t\t\t format_type_with_typemod(atttypid, atttypmod));\n\t\tif (OidIsValid(attcollation) &&\n\t\t\tattcollation != get_typcollation(atttypid))\n\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t generate_collation_name(attcollation));\n\n\t\ti++;\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_tablesample_def\t\t\t- print a TableSampleClause\n */\nstatic void\nget_tablesample_def(TableSampleClause *tablesample, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\targtypes[1];\n\tint\t\t\tnargs;\n\tListCell   *l;\n\n\t/*\n\t * We should qualify the handler's function name if it wouldn't be\n\t * resolved by lookup in the current search path.\n\t */\n\targtypes[0] = INTERNALOID;\n\tappendStringInfo(buf, \" TABLESAMPLE %s (\",\n\t\t\t\t\t generate_function_name(tablesample->tsmhandler, 1,\n\t\t\t\t\t\t\t\t\t\t\tNIL, argtypes,\n\t\t\t\t\t\t\t\t\t\t\tfalse, NULL, false));\n\n\tnargs = 0;\n\tforeach(l, tablesample->args)\n\t{\n\t\tif (nargs++ > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tget_rule_expr((Node *) lfirst(l), context, false);\n\t}\n\tappendStringInfoChar(buf, ')');\n\n\tif (tablesample->repeatable != NULL)\n\t{\n\t\tappendStringInfoString(buf, \" REPEATABLE (\");\n\t\tget_rule_expr((Node *) tablesample->repeatable, context, false);\n\t\tappendStringInfoChar(buf, ')');\n\t}\n}\n\n/*\n * get_opclass_name\t\t\t- fetch name of an index operator class\n *\n * The opclass name is appended (after a space) to buf.\n *\n * Output is suppressed if the opclass is the default for the given\n * actual_datatype.  (If you don't want this behavior, just pass\n * InvalidOid for actual_datatype.)\n */\nstatic void\nget_opclass_name(Oid opclass, Oid actual_datatype,\n\t\t\t\t StringInfo buf)\n{\n\tHeapTuple\tht_opc;\n\tForm_pg_opclass opcrec;\n\tchar\t   *opcname;\n\tchar\t   *nspname;\n\n\tht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));\n\tif (!HeapTupleIsValid(ht_opc))\n\t\telog(ERROR, \"cache lookup failed for opclass %u\", opclass);\n\topcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);\n\n\tif (!OidIsValid(actual_datatype) ||\n\t\tGetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)\n\t{\n\t\t/* Okay, we need the opclass name.  Do we need to qualify it? */\n\t\topcname = NameStr(opcrec->opcname);\n\t\tif (OpclassIsVisible(opclass))\n\t\t\tappendStringInfo(buf, \" %s\", quote_identifier(opcname));\n\t\telse\n\t\t{\n\t\t\tnspname = get_namespace_name_or_temp(opcrec->opcnamespace);\n\t\t\tappendStringInfo(buf, \" %s.%s\",\n\t\t\t\t\t\t\t quote_identifier(nspname),\n\t\t\t\t\t\t\t quote_identifier(opcname));\n\t\t}\n\t}\n\tReleaseSysCache(ht_opc);\n}\n\n/*\n * processIndirection - take care of array and subfield assignment\n *\n * We strip any top-level FieldStore or assignment SubscriptingRef nodes that\n * appear in the input, printing them as decoration for the base column\n * name (which we assume the caller just printed).  We might also need to\n * strip CoerceToDomain nodes, but only ones that appear above assignment\n * nodes.\n *\n * Returns the subexpression that's to be assigned.\n */\nstatic Node *\nprocessIndirection(Node *node, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tCoerceToDomain *cdomain = NULL;\n\n\tfor (;;)\n\t{\n\t\tif (node == NULL)\n\t\t\tbreak;\n\t\tif (IsA(node, FieldStore))\n\t\t{\n\t\t\tFieldStore *fstore = (FieldStore *) node;\n\t\t\tOid\t\t\ttyprelid;\n\t\t\tchar\t   *fieldname;\n\n\t\t\t/* lookup tuple type */\n\t\t\ttyprelid = get_typ_typrelid(fstore->resulttype);\n\t\t\tif (!OidIsValid(typrelid))\n\t\t\t\telog(ERROR, \"argument type %s of FieldStore is not a tuple type\",\n\t\t\t\t\t format_type_be(fstore->resulttype));\n\n\t\t\t/*\n\t\t\t * Print the field name.  There should only be one target field in\n\t\t\t * stored rules.  There could be more than that in executable\n\t\t\t * target lists, but this function cannot be used for that case.\n\t\t\t */\n\t\t\tAssert(list_length(fstore->fieldnums) == 1);\n\t\t\tfieldname = get_attname(typrelid,\n\t\t\t\t\t\t\t\t\tlinitial_int(fstore->fieldnums), false);\n\t\t\tappendStringInfo(buf, \".%s\", quote_identifier(fieldname));\n\n\t\t\t/*\n\t\t\t * We ignore arg since it should be an uninteresting reference to\n\t\t\t * the target column or subcolumn.\n\t\t\t */\n\t\t\tnode = (Node *) linitial(fstore->newvals);\n\t\t}\n\t\telse if (IsA(node, SubscriptingRef))\n\t\t{\n\t\t\tSubscriptingRef   *sbsref = (SubscriptingRef *) node;\n\n\t\t\tif (sbsref->refassgnexpr == NULL)\n\t\t\t\tbreak;\n\t\t\tprintSubscripts(sbsref, context);\n\n\t\t\t/*\n\t\t\t * We ignore refexpr since it should be an uninteresting reference\n\t\t\t * to the target column or subcolumn.\n\t\t\t */\n\t\t\tnode = (Node *) sbsref->refassgnexpr;\n\t\t}\n\t\telse if (IsA(node, CoerceToDomain))\n\t\t{\n\t\t\tcdomain = (CoerceToDomain *) node;\n\t\t\t/* If it's an explicit domain coercion, we're done */\n\t\t\tif (cdomain->coercionformat != COERCE_IMPLICIT_CAST)\n\t\t\t\tbreak;\n\t\t\t/* Tentatively descend past the CoerceToDomain */\n\t\t\tnode = (Node *) cdomain->arg;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\n\t/*\n\t * If we descended past a CoerceToDomain whose argument turned out not to\n\t * be a FieldStore or array assignment, back up to the CoerceToDomain.\n\t * (This is not enough to be fully correct if there are nested implicit\n\t * CoerceToDomains, but such cases shouldn't ever occur.)\n\t */\n\tif (cdomain && node == (Node *) cdomain->arg)\n\t\tnode = (Node *) cdomain;\n\n\treturn node;\n}\n\nstatic void\nprintSubscripts(SubscriptingRef *sbsref, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tListCell   *lowlist_item;\n\tListCell   *uplist_item;\n\n\tlowlist_item = list_head(sbsref->reflowerindexpr);\t/* could be NULL */\n\tforeach(uplist_item, sbsref->refupperindexpr)\n\t{\n\t\tappendStringInfoChar(buf, '[');\n\t\tif (lowlist_item)\n\t\t{\n\t\t\t/* If subexpression is NULL, get_rule_expr prints nothing */\n\t\t\tget_rule_expr((Node *) lfirst(lowlist_item), context, false);\n\t\t\tappendStringInfoChar(buf, ':');\n\t\t\tlowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item);\n\t\t}\n\t\t/* If subexpression is NULL, get_rule_expr prints nothing */\n\t\tget_rule_expr((Node *) lfirst(uplist_item), context, false);\n\t\tappendStringInfoChar(buf, ']');\n\t}\n}\n\n/*\n * get_relation_name\n *\t\tGet the unqualified name of a relation specified by OID\n *\n * This differs from the underlying get_rel_name() function in that it will\n * throw error instead of silently returning NULL if the OID is bad.\n */\nstatic char *\nget_relation_name(Oid relid)\n{\n\tchar\t   *relname = get_rel_name(relid);\n\n\tif (!relname)\n\t\telog(ERROR, \"cache lookup failed for relation %u\", relid);\n\treturn relname;\n}\n\n/*\n * generate_relation_or_shard_name\n *\t\tCompute the name to display for a relation or shard\n *\n * If the provided relid is equal to the provided distrelid, this function\n * returns a shard-extended relation name; otherwise, it falls through to a\n * simple generate_relation_name call.\n */\nstatic char *\ngenerate_relation_or_shard_name(Oid relid, Oid distrelid, int64 shardid,\n\t\t\t\t\t\t\t\tList *namespaces)\n{\n\tchar *relname = NULL;\n\n\tif (relid == distrelid)\n\t{\n\t\trelname = get_relation_name(relid);\n\n\t\tif (shardid > 0)\n\t\t{\n\t\t\tOid schemaOid = get_rel_namespace(relid);\n\t\t\tchar *schemaName = get_namespace_name_or_temp(schemaOid);\n\n\t\t\tAppendShardIdToName(&relname, shardid);\n\n\t\t\trelname = quote_qualified_identifier(schemaName, relname);\n\t\t}\n\t}\n\telse\n\t{\n\t\trelname = generate_relation_name(relid, namespaces);\n\t}\n\n\treturn relname;\n}\n\n/*\n * generate_relation_name\n *\t\tCompute the name to display for a relation specified by OID\n *\n * The result includes all necessary quoting and schema-prefixing.\n *\n * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.\n * We will forcibly qualify the relation name if it equals any CTE name\n * visible in the namespace list.\n */\nchar *\ngenerate_relation_name(Oid relid, List *namespaces)\n{\n\tHeapTuple\ttp;\n\tForm_pg_class reltup;\n\tbool\t\tneed_qual;\n\tListCell   *nslist;\n\tchar\t   *relname;\n\tchar\t   *nspname;\n\tchar\t   *result;\n\n\ttp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));\n\tif (!HeapTupleIsValid(tp))\n\t\telog(ERROR, \"cache lookup failed for relation %u\", relid);\n\treltup = (Form_pg_class) GETSTRUCT(tp);\n\trelname = NameStr(reltup->relname);\n\n\t/* Check for conflicting CTE name */\n\tneed_qual = false;\n\tforeach(nslist, namespaces)\n\t{\n\t\tdeparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);\n\t\tListCell   *ctlist;\n\n\t\tforeach(ctlist, dpns->ctes)\n\t\t{\n\t\t\tCommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);\n\n\t\t\tif (strcmp(cte->ctename, relname) == 0)\n\t\t\t{\n\t\t\t\tneed_qual = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (need_qual)\n\t\t\tbreak;\n\t}\n\n\t/* Otherwise, qualify the name if not visible in search path */\n\tif (!need_qual)\n\t\tneed_qual = !RelationIsVisible(relid);\n\n\tif (need_qual)\n\t\tnspname = get_namespace_name_or_temp(reltup->relnamespace);\n\telse\n\t\tnspname = NULL;\n\n\tresult = quote_qualified_identifier(nspname, relname);\n\n\tReleaseSysCache(tp);\n\n\treturn result;\n}\n\n/*\n * generate_rte_shard_name returns the qualified name of the shard given a\n * CITUS_RTE_SHARD range table entry.\n */\nstatic char *\ngenerate_rte_shard_name(RangeTblEntry *rangeTableEntry)\n{\n\tchar *shardSchemaName = NULL;\n\tchar *shardTableName = NULL;\n\n\tAssert(GetRangeTblKind(rangeTableEntry) == CITUS_RTE_SHARD);\n\n\tExtractRangeTblExtraData(rangeTableEntry, NULL, &shardSchemaName, &shardTableName,\n\t\t\t\t\t\t\t NULL);\n\n\treturn generate_fragment_name(shardSchemaName, shardTableName);\n}\n\n/*\n * generate_fragment_name\n *\t\tCompute the name to display for a shard or merged table\n *\n * The result includes all necessary quoting and schema-prefixing. The schema\n * name can be NULL for regular shards. For merged tables, they are always\n * declared within a job-specific schema, and therefore can't have null schema\n * names.\n */\nstatic char *\ngenerate_fragment_name(char *schemaName, char *tableName)\n{\n\tStringInfo fragmentNameString = makeStringInfo();\n\n\tif (schemaName != NULL)\n\t{\n\t\tappendStringInfo(fragmentNameString, \"%s.%s\", quote_identifier(schemaName),\n\t\t\t\t\t\t quote_identifier(tableName));\n\t}\n\telse\n\t{\n\t\tappendStringInfoString(fragmentNameString, quote_identifier(tableName));\n\t}\n\n\treturn fragmentNameString->data;\n}\n\n/*\n * generate_function_name\n *\t\tCompute the name to display for a function specified by OID,\n *\t\tgiven that it is being called with the specified actual arg names and\n *\t\ttypes.  (Those matter because of ambiguous-function resolution rules.)\n *\n * If we're dealing with a potentially variadic function (in practice, this\n * means a FuncExpr or Aggref, not some other way of calling a function), then\n * has_variadic must specify whether variadic arguments have been merged,\n * and *use_variadic_p will be set to indicate whether to print VARIADIC in\n * the output.  For non-FuncExpr cases, has_variadic should be false and\n * use_variadic_p can be NULL.\n *\n * inGroupBy must be true if we're deparsing a GROUP BY clause.\n *\n * The result includes all necessary quoting and schema-prefixing.\n */\nstatic char *\ngenerate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,\n\t\t\t\t\t   bool has_variadic, bool *use_variadic_p,\n\t\t\t\t\t   bool inGroupBy)\n{\n\tchar\t   *result;\n\tHeapTuple\tproctup;\n\tForm_pg_proc procform;\n\tchar\t   *proname;\n\tbool\t\tuse_variadic;\n\tchar\t   *nspname;\n\tFuncDetailCode p_result;\n\tOid\t\t\tp_funcid;\n\tOid\t\t\tp_rettype;\n\tbool\t\tp_retset;\n\tint\t\t\tp_nvargs;\n\tOid\t\t\tp_vatype;\n\tOid\t\t   *p_true_typeids;\n\tbool\t\tforce_qualify = false;\n\n\tproctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));\n\tif (!HeapTupleIsValid(proctup))\n\t\telog(ERROR, \"cache lookup failed for function %u\", funcid);\n\tprocform = (Form_pg_proc) GETSTRUCT(proctup);\n\tproname = NameStr(procform->proname);\n\n\t/*\n\t * Due to parser hacks to avoid needing to reserve CUBE, we need to force\n\t * qualification of some function names within GROUP BY.\n\t */\n\tif (inGroupBy)\n\t{\n\t\tif (strcmp(proname, \"cube\") == 0 || strcmp(proname, \"rollup\") == 0)\n\t\t\tforce_qualify = true;\n\t}\n\n\t/*\n\t * Determine whether VARIADIC should be printed.  We must do this first\n\t * since it affects the lookup rules in func_get_detail().\n\t *\n\t * Currently, we always print VARIADIC if the function has a merged\n\t * variadic-array argument.  Note that this is always the case for\n\t * functions taking a VARIADIC argument type other than VARIADIC ANY.\n\t *\n\t * In principle, if VARIADIC wasn't originally specified and the array\n\t * actual argument is deconstructable, we could print the array elements\n\t * separately and not print VARIADIC, thus more nearly reproducing the\n\t * original input.  For the moment that seems like too much complication\n\t * for the benefit, and anyway we do not know whether VARIADIC was\n\t * originally specified if it's a non-ANY type.\n\t */\n\tif (use_variadic_p)\n\t{\n\t\t/* Parser should not have set funcvariadic unless fn is variadic */\n\t\tAssert(!has_variadic || OidIsValid(procform->provariadic));\n\t\tuse_variadic = has_variadic;\n\t\t*use_variadic_p = use_variadic;\n\t}\n\telse\n\t{\n\t\tAssert(!has_variadic);\n\t\tuse_variadic = false;\n\t}\n\n\t/*\n\t * The idea here is to schema-qualify only if the parser would fail to\n\t * resolve the correct function given the unqualified func name with the\n\t * specified argtypes and VARIADIC flag.  But if we already decided to\n\t * force qualification, then we can skip the lookup and pretend we didn't\n\t * find it.\n\t */\n\tif (!force_qualify)\n\t\tp_result = func_get_detail(list_make1(makeString(proname)),\n\t\t\t\t\t\t\t\t   NIL, argnames, nargs, argtypes,\n\t\t\t\t\t\t\t\t   !use_variadic, true, false,\n\t\t\t\t\t\t\t\t   &p_funcid, &p_rettype,\n\t\t\t\t\t\t\t\t   &p_retset, &p_nvargs, &p_vatype,\n\t\t\t\t\t\t\t\t   &p_true_typeids, NULL);\n\telse\n\t{\n\t\tp_result = FUNCDETAIL_NOTFOUND;\n\t\tp_funcid = InvalidOid;\n\t}\n\n\tif ((p_result == FUNCDETAIL_NORMAL ||\n\t\t p_result == FUNCDETAIL_AGGREGATE ||\n\t\t p_result == FUNCDETAIL_WINDOWFUNC) &&\n\t\tp_funcid == funcid)\n\t\tnspname = NULL;\n\telse\n\t\tnspname = get_namespace_name_or_temp(procform->pronamespace);\n\n\tresult = quote_qualified_identifier(nspname, proname);\n\n\tReleaseSysCache(proctup);\n\n\treturn result;\n}\n\n/*\n * generate_operator_name\n *\t\tCompute the name to display for an operator specified by OID,\n *\t\tgiven that it is being called with the specified actual arg types.\n *\t\t(Arg types matter because of ambiguous-operator resolution rules.\n *\t\tPass InvalidOid for unused arg of a unary operator.)\n *\n * The result includes all necessary quoting and schema-prefixing,\n * plus the OPERATOR() decoration needed to use a qualified operator name\n * in an expression.\n */\nchar *\ngenerate_operator_name(Oid operid, Oid arg1, Oid arg2)\n{\n\tStringInfoData buf;\n\tHeapTuple\topertup;\n\tForm_pg_operator operform;\n\tchar\t   *oprname;\n\tchar\t   *nspname;\n\n\tinitStringInfo(&buf);\n\n\topertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));\n\tif (!HeapTupleIsValid(opertup))\n\t\telog(ERROR, \"cache lookup failed for operator %u\", operid);\n\toperform = (Form_pg_operator) GETSTRUCT(opertup);\n\toprname = NameStr(operform->oprname);\n\n\t/*\n\t * Unlike generate_operator_name() in postgres/src/backend/utils/adt/ruleutils.c,\n\t * we don't check if the operator is in current namespace or not. This is\n\t * because this check is costly when the operator is not in current namespace.\n\t */\n\tnspname = get_namespace_name_or_temp(operform->oprnamespace);\n\tAssert(nspname != NULL);\n\tappendStringInfo(&buf, \"OPERATOR(%s.\", quote_identifier(nspname));\n\tappendStringInfoString(&buf, oprname);\n\tappendStringInfoChar(&buf, ')');\n\n\tReleaseSysCache(opertup);\n\n\treturn buf.data;\n}\n\n/*\n * get_one_range_partition_bound_string\n *\t\tA C string representation of one range partition bound\n */\nchar *\nget_range_partbound_string(List *bound_datums)\n{\n\tdeparse_context context;\n\tStringInfo\tbuf = makeStringInfo();\n\tListCell   *cell;\n\tchar\t   *sep;\n\n\tmemset(&context, 0, sizeof(deparse_context));\n\tcontext.buf = buf;\n\n\tappendStringInfoChar(buf, '(');\n\tsep = \"\";\n\tforeach(cell, bound_datums)\n\t{\n\t\tPartitionRangeDatum *datum =\n\t\t\t\tlfirst_node(PartitionRangeDatum, cell);\n\n\t\tappendStringInfoString(buf, sep);\n\t\tif (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)\n\t\t\tappendStringInfoString(buf, \"MINVALUE\");\n\t\telse if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)\n\t\t\tappendStringInfoString(buf, \"MAXVALUE\");\n\t\telse\n\t\t{\n\t\t\tConst\t   *val = castNode(Const, datum->value);\n\n\t\t\tget_const_expr(val, &context, -1);\n\t\t}\n\t\tsep = \", \";\n\t}\n\tappendStringInfoChar(buf, ')');\n\n\treturn buf->data;\n}\n\n/*\n * Collect a list of OIDs of all sequences owned by the specified relation,\n * and column if specified.  If deptype is not zero, then only find sequences\n * with the specified dependency type.\n */\nList *\ngetOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)\n{\n\tList\t   *result = NIL;\n\tRelation\tdepRel;\n\tScanKeyData key[3];\n\tSysScanDesc scan;\n\tHeapTuple\ttup;\n\n\tdepRel = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_refclassid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(RelationRelationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_refobjid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(relid));\n\tif (attnum)\n\t\tScanKeyInit(&key[2],\n\t\t\t\t\tAnum_pg_depend_refobjsubid,\n\t\t\t\t\tBTEqualStrategyNumber, F_INT4EQ,\n\t\t\t\t\tInt32GetDatum(attnum));\n\n\tscan = systable_beginscan(depRel, DependReferenceIndexId, true,\n\t\t\t\t\t\t\t  NULL, attnum ? 3 : 2, key);\n\n\twhile (HeapTupleIsValid(tup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);\n\n\t\t/*\n\t\t * We assume any auto or internal dependency of a sequence on a column\n\t\t * must be what we are looking for.  (We need the relkind test because\n\t\t * indexes can also have auto dependencies on columns.)\n\t\t */\n\t\tif (deprec->classid == RelationRelationId &&\n\t\t\tdeprec->objsubid == 0 &&\n\t\t\tdeprec->refobjsubid != 0 &&\n\t\t\t(deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&\n\t\t\tget_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)\n\t\t{\n\t\t\tif (!deptype || deprec->deptype == deptype)\n\t\t\t\tresult = lappend_oid(result, deprec->objid);\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\n\ttable_close(depRel, AccessShareLock);\n\n\treturn result;\n}\n\n/*\n * get_insert_column_names_list Prepares the insert-column-names list. Any indirection\n * decoration needed on the column names can be inferred from the top targetlist.\n */\nstatic List *\nget_insert_column_names_list(List *targetList, StringInfo buf,\n\t\t\t\t\tdeparse_context *context, RangeTblEntry *rte)\n{\n\tchar *sep;\n\tListCell *l;\n\tList *strippedexprs;\n\n\tstrippedexprs = NIL;\n\tsep = \"\";\n\tappendStringInfoChar(buf, '(');\n\tforeach(l, targetList)\n\t{\n\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\n\t\tif (tle->resjunk)\n\t\t\tcontinue;\t\t\t/* ignore junk entries */\n\n\t\tappendStringInfoString(buf, sep);\n\t\tsep = \", \";\n\n\t\t/*\n\t\t * Put out name of target column; look in the catalogs, not at\n\t\t * tle->resname, since resname will fail to track RENAME.\n\t\t */\n\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t   quote_identifier(get_attname(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttle->resno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse)));\n\n\t\t/*\n\t\t * Print any indirection needed (subfields or subscripts), and strip\n\t\t * off the top-level nodes representing the indirection assignments.\n\t\t * Add the stripped expressions to strippedexprs.  (If it's a\n\t\t * single-VALUES statement, the stripped expressions are the VALUES to\n\t\t * print below.  Otherwise they're just Vars and not really\n\t\t * interesting.)\n\t\t */\n\t\tstrippedexprs = lappend(strippedexprs,\n\t\t\t\t\t\t\t\tprocessIndirection((Node *) tle->expr,\n\t\t\t\t\t\t\t\t\t\t\t\t   context));\n\t}\n\tappendStringInfoString(buf, \") \");\n\n\treturn strippedexprs;\n}\n#endif /* (PG_VERSION_NUM >= PG_VERSION_16) && (PG_VERSION_NUM < PG_VERSION_17) */\n"
  },
  {
    "path": "src/backend/distributed/deparser/ruleutils_17.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * ruleutils_16.c\n *\t  Functions to convert stored expressions/querytrees back to\n *\t  source text\n *\n * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group\n * Portions Copyright (c) 1994, Regents of the University of California\n *\n *\n * IDENTIFICATION\n *\t  src/backend/distributed/deparser/ruleutils_16.c\n *\n * This needs to be closely in sync with the core code.\n *-------------------------------------------------------------------------\n */\n#include \"pg_version_constants.h\"\n\n#include \"pg_config.h\"\n\n#if (PG_VERSION_NUM >= PG_VERSION_17) && (PG_VERSION_NUM < PG_VERSION_18)\n\n#include \"postgres.h\"\n\n#include <ctype.h>\n#include <unistd.h>\n#include <fcntl.h>\n\n#include \"access/amapi.h\"\n#include \"access/htup_details.h\"\n#include \"access/relation.h\"\n#include \"access/table.h\"\n#include \"catalog/pg_aggregate.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_extension.h\"\n#include \"catalog/pg_foreign_data_wrapper.h\"\n#include \"catalog/pg_language.h\"\n#include \"catalog/pg_opclass.h\"\n#include \"catalog/pg_operator.h\"\n#include \"catalog/pg_partitioned_table.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_statistic_ext.h\"\n#include \"catalog/pg_trigger.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/defrem.h\"\n#include \"commands/extension.h\"\n#include \"commands/tablespace.h\"\n#include \"common/keywords.h\"\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"executor/spi.h\"\n#include \"foreign/foreign.h\"\n#include \"funcapi.h\"\n#include \"mb/pg_wchar.h\"\n#include \"miscadmin.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/pathnodes.h\"\n#include \"optimizer/optimizer.h\"\n#include \"parser/parse_node.h\"\n#include \"parser/parse_agg.h\"\n#include \"parser/parse_func.h\"\n#include \"parser/parse_oper.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parser.h\"\n#include \"parser/parsetree.h\"\n#include \"rewrite/rewriteHandler.h\"\n#include \"rewrite/rewriteManip.h\"\n#include \"rewrite/rewriteSupport.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/ruleutils.h\"\n#include \"utils/snapmgr.h\"\n#include \"utils/syscache.h\"\n#include \"utils/typcache.h\"\n#include \"utils/varlena.h\"\n#include \"utils/xml.h\"\n\n\n/* ----------\n * Pretty formatting constants\n * ----------\n */\n\n/* Indent counts */\n#define PRETTYINDENT_STD\t\t8\n#define PRETTYINDENT_JOIN\t\t4\n#define PRETTYINDENT_VAR\t\t4\n\n#define PRETTYINDENT_LIMIT\t\t40\t/* wrap limit */\n\n/* Pretty flags */\n#define PRETTYFLAG_PAREN\t\t0x0001\n#define PRETTYFLAG_INDENT\t\t0x0002\n\n/* Default line length for pretty-print wrapping: 0 means wrap always */\n#define WRAP_COLUMN_DEFAULT\t\t0\n\n/* macros to test if pretty action needed */\n#define PRETTY_PAREN(context)\t((context)->prettyFlags & PRETTYFLAG_PAREN)\n#define PRETTY_INDENT(context)\t((context)->prettyFlags & PRETTYFLAG_INDENT)\n\n\n/* ----------\n * Local data types\n * ----------\n */\n\n/* Context info needed for invoking a recursive querytree display routine */\ntypedef struct\n{\n\tStringInfo\tbuf;\t\t\t/* output buffer to append to */\n\tList\t   *namespaces;\t\t/* List of deparse_namespace nodes */\n\tTupleDesc\tresultDesc;\t\t/* if top level of a view, the view's tupdesc */\n\tList\t   *targetList;\t\t/* Current query level's SELECT targetlist */\n\tList\t   *windowClause;\t/* Current query level's WINDOW clause */\n\tint\t\t\tprettyFlags;\t/* enabling of pretty-print functions */\n\tint\t\t\twrapColumn;\t\t/* max line length, or -1 for no limit */\n\tint\t\t\tindentLevel;\t/* current indent level for prettyprint */\n\tbool\t\tvarprefix;\t\t/* true to print prefixes on Vars */\n\tOid\t\t\tdistrelid;\t\t/* the distributed table being modified, if valid */\n\tint64\t\tshardid;\t\t/* a distributed table's shardid, if positive */\n\tbool\t\tcolNamesVisible;\t/* do we care about output column names? */\n\tbool\t\tinGroupBy;\t\t/* deparsing GROUP BY clause? */\n\tbool\t\tvarInOrderBy;\t/* deparsing simple Var in ORDER BY? */\n\tBitmapset  *appendparents;\t/* if not null, map child Vars of these relids\n\t\t\t\t\t\t\t\t * back to the parent rel */\n} deparse_context;\n\n/*\n * Each level of query context around a subtree needs a level of Var namespace.\n * A Var having varlevelsup=N refers to the N'th item (counting from 0) in\n * the current context's namespaces list.\n *\n * The rangetable is the list of actual RTEs from the query tree, and the\n * cte list is the list of actual CTEs.\n *\n * rtable_names holds the alias name to be used for each RTE (either a C\n * string, or NULL for nameless RTEs such as unnamed joins).\n * rtable_columns holds the column alias names to be used for each RTE.\n *\n * In some cases we need to make names of merged JOIN USING columns unique\n * across the whole query, not only per-RTE.  If so, unique_using is true\n * and using_names is a list of C strings representing names already assigned\n * to USING columns.\n *\n * When deparsing plan trees, there is always just a single item in the\n * deparse_namespace list (since a plan tree never contains Vars with\n * varlevelsup > 0).  We store the PlanState node that is the immediate\n * parent of the expression to be deparsed, as well as a list of that\n * PlanState's ancestors.  In addition, we store its outer and inner subplan\n * state nodes, as well as their plan nodes' targetlists, and the index tlist\n * if the current plan node might contain INDEX_VAR Vars.  (These fields could\n * be derived on-the-fly from the current PlanState, but it seems notationally\n * clearer to set them up as separate fields.)\n */\ntypedef struct\n{\n\tList\t   *rtable;\t\t\t/* List of RangeTblEntry nodes */\n\tList\t   *rtable_names;\t/* Parallel list of names for RTEs */\n\tList\t   *rtable_columns; /* Parallel list of deparse_columns structs */\n\tList\t   *subplans;\t\t/* List of Plan trees for SubPlans */\n\tList\t   *ctes;\t\t\t/* List of CommonTableExpr nodes */\n\tAppendRelInfo **appendrels; /* Array of AppendRelInfo nodes, or NULL */\n\t/* Workspace for column alias assignment: */\n\tbool\t\tunique_using;\t/* Are we making USING names globally unique */\n\tList\t   *using_names;\t/* List of assigned names for USING columns */\n\t/* Remaining fields are used only when deparsing a Plan tree: */\n\tPlan\t   *plan;\t\t\t/* immediate parent of current expression */\n\tList\t   *ancestors;\t\t/* ancestors of planstate */\n\tPlan\t   *outer_plan;\t\t/* outer subnode, or NULL if none */\n\tPlan\t   *inner_plan;\t\t/* inner subnode, or NULL if none */\n\tList\t   *outer_tlist;\t/* referent for OUTER_VAR Vars */\n\tList\t   *inner_tlist;\t/* referent for INNER_VAR Vars */\n\tList\t   *index_tlist;\t/* referent for INDEX_VAR Vars */\n\t/* Special namespace representing a function signature: */\n\tchar\t   *funcname;\n\tint\t\t\tnumargs;\n\tchar\t  **argnames;\n} deparse_namespace;\n\n/* Callback signature for resolve_special_varno() */\ntypedef void (*rsv_callback) (Node *node, deparse_context *context,\n\t\t\t\t\t\t\t  void *callback_arg);\n\n/*\n * Per-relation data about column alias names.\n *\n * Selecting aliases is unreasonably complicated because of the need to dump\n * rules/views whose underlying tables may have had columns added, deleted, or\n * renamed since the query was parsed.  We must nonetheless print the rule/view\n * in a form that can be reloaded and will produce the same results as before.\n *\n * For each RTE used in the query, we must assign column aliases that are\n * unique within that RTE.  SQL does not require this of the original query,\n * but due to factors such as *-expansion we need to be able to uniquely\n * reference every column in a decompiled query.  As long as we qualify all\n * column references, per-RTE uniqueness is sufficient for that.\n *\n * However, we can't ensure per-column name uniqueness for unnamed join RTEs,\n * since they just inherit column names from their input RTEs, and we can't\n * rename the columns at the join level.  Most of the time this isn't an issue\n * because we don't need to reference the join's output columns as such; we\n * can reference the input columns instead.  That approach can fail for merged\n * JOIN USING columns, however, so when we have one of those in an unnamed\n * join, we have to make that column's alias globally unique across the whole\n * query to ensure it can be referenced unambiguously.\n *\n * Another problem is that a JOIN USING clause requires the columns to be\n * merged to have the same aliases in both input RTEs, and that no other\n * columns in those RTEs or their children conflict with the USING names.\n * To handle that, we do USING-column alias assignment in a recursive\n * traversal of the query's jointree.  When descending through a JOIN with\n * USING, we preassign the USING column names to the child columns, overriding\n * other rules for column alias assignment.  We also mark each RTE with a list\n * of all USING column names selected for joins containing that RTE, so that\n * when we assign other columns' aliases later, we can avoid conflicts.\n *\n * Another problem is that if a JOIN's input tables have had columns added or\n * deleted since the query was parsed, we must generate a column alias list\n * for the join that matches the current set of input columns --- otherwise, a\n * change in the number of columns in the left input would throw off matching\n * of aliases to columns of the right input.  Thus, positions in the printable\n * column alias list are not necessarily one-for-one with varattnos of the\n * JOIN, so we need a separate new_colnames[] array for printing purposes.\n */\ntypedef struct\n{\n\t/*\n\t * colnames is an array containing column aliases to use for columns that\n\t * existed when the query was parsed.  Dropped columns have NULL entries.\n\t * This array can be directly indexed by varattno to get a Var's name.\n\t *\n\t * Non-NULL entries are guaranteed unique within the RTE, *except* when\n\t * this is for an unnamed JOIN RTE.  In that case we merely copy up names\n\t * from the two input RTEs.\n\t *\n\t * During the recursive descent in set_using_names(), forcible assignment\n\t * of a child RTE's column name is represented by pre-setting that element\n\t * of the child's colnames array.  So at that stage, NULL entries in this\n\t * array just mean that no name has been preassigned, not necessarily that\n\t * the column is dropped.\n\t */\n\tint\t\t\tnum_cols;\t\t/* length of colnames[] array */\n\tchar\t  **colnames;\t\t/* array of C strings and NULLs */\n\n\t/*\n\t * new_colnames is an array containing column aliases to use for columns\n\t * that would exist if the query was re-parsed against the current\n\t * definitions of its base tables.  This is what to print as the column\n\t * alias list for the RTE.  This array does not include dropped columns,\n\t * but it will include columns added since original parsing.  Indexes in\n\t * it therefore have little to do with current varattno values.  As above,\n\t * entries are unique unless this is for an unnamed JOIN RTE.  (In such an\n\t * RTE, we never actually print this array, but we must compute it anyway\n\t * for possible use in computing column names of upper joins.) The\n\t * parallel array is_new_col marks which of these columns are new since\n\t * original parsing.  Entries with is_new_col false must match the\n\t * non-NULL colnames entries one-for-one.\n\t */\n\tint\t\t\tnum_new_cols;\t/* length of new_colnames[] array */\n\tchar\t  **new_colnames;\t/* array of C strings */\n\tbool\t   *is_new_col;\t\t/* array of bool flags */\n\n\t/* This flag tells whether we should actually print a column alias list */\n\tbool\t\tprintaliases;\n\n\t/* This list has all names used as USING names in joins above this RTE */\n\tList\t   *parentUsing;\t/* names assigned to parent merged columns */\n\n\t/*\n\t * If this struct is for a JOIN RTE, we fill these fields during the\n\t * set_using_names() pass to describe its relationship to its child RTEs.\n\t *\n\t * leftattnos and rightattnos are arrays with one entry per existing\n\t * output column of the join (hence, indexable by join varattno).  For a\n\t * simple reference to a column of the left child, leftattnos[i] is the\n\t * child RTE's attno and rightattnos[i] is zero; and conversely for a\n\t * column of the right child.  But for merged columns produced by JOIN\n\t * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.\n\t * Also, if the column has been dropped, both are zero.\n\t *\n\t * If it's a JOIN USING, usingNames holds the alias names selected for the\n\t * merged columns (these might be different from the original USING list,\n\t * if we had to modify names to achieve uniqueness).\n\t */\n\tint\t\t\tleftrti;\t\t/* rangetable index of left child */\n\tint\t\t\trightrti;\t\t/* rangetable index of right child */\n\tint\t\t   *leftattnos;\t\t/* left-child varattnos of join cols, or 0 */\n\tint\t\t   *rightattnos;\t/* right-child varattnos of join cols, or 0 */\n\tList\t   *usingNames;\t\t/* names assigned to merged columns */\n} deparse_columns;\n\n/* This macro is analogous to rt_fetch(), but for deparse_columns structs */\n#define deparse_columns_fetch(rangetable_index, dpns) \\\n\t((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))\n\n/*\n * Entry in set_rtable_names' hash table\n */\ntypedef struct\n{\n\tchar\t\tname[NAMEDATALEN];\t/* Hash key --- must be first */\n\tint\t\t\tcounter;\t\t/* Largest addition used so far for name */\n} NameHashEntry;\n\n\n/* ----------\n * Local functions\n *\n * Most of these functions used to use fixed-size buffers to build their\n * results.  Now, they take an (already initialized) StringInfo object\n * as a parameter, and append their text output to its contents.\n * ----------\n */\nstatic void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,\n\t\t\t\t Bitmapset *rels_used);\nstatic void set_deparse_for_query(deparse_namespace *dpns, Query *query,\n\t\t\t\t\t  List *parent_namespaces);\nstatic bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);\nstatic void set_using_names(deparse_namespace *dpns, Node *jtnode,\n\t\t\t\tList *parentUsing);\nstatic void set_relation_column_names(deparse_namespace *dpns,\n\t\t\t\t\t\t  RangeTblEntry *rte,\n\t\t\t\t\t\t  deparse_columns *colinfo);\nstatic void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,\n\t\t\t\t\t  deparse_columns *colinfo);\nstatic bool colname_is_unique(const char *colname, deparse_namespace *dpns,\n\t\t\t\t  deparse_columns *colinfo);\nstatic char *make_colname_unique(char *colname, deparse_namespace *dpns,\n\t\t\t\t\tdeparse_columns *colinfo);\nstatic void expand_colnames_array_to(deparse_columns *colinfo, int n);\nstatic void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,\n\t\t\t\t\t  deparse_columns *colinfo);\nstatic char *get_rtable_name(int rtindex, deparse_context *context);\nstatic void set_deparse_plan(deparse_namespace *dpns, Plan *plan);\nstatic Plan *find_recursive_union(deparse_namespace *dpns,\n\t\t\t\t\t\t\t\t  WorkTableScan *wtscan);\nstatic void push_child_plan(deparse_namespace *dpns, Plan *plan,\n\t\t\t\tdeparse_namespace *save_dpns);\nstatic void pop_child_plan(deparse_namespace *dpns,\n\t\t\t   deparse_namespace *save_dpns);\nstatic void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,\n\t\t\t\t   deparse_namespace *save_dpns);\nstatic void pop_ancestor_plan(deparse_namespace *dpns,\n\t\t\t\t  deparse_namespace *save_dpns);\nstatic void get_query_def(Query *query, StringInfo buf, List *parentnamespace,\n\t\t\t  TupleDesc resultDesc, bool colNamesVisible,\n\t\t\t  int prettyFlags, int wrapColumn, int startIndent);\nstatic void get_query_def_extended(Query *query, StringInfo buf,\n\t\t\t\tList *parentnamespace, Oid distrelid, int64 shardid,\n\t\t\t\tTupleDesc resultDesc, bool colNamesVisible,\n\t\t\t\tint prettyFlags, int wrapColumn,\n\t\t\t\tint startIndent);\nstatic void get_values_def(List *values_lists, deparse_context *context);\nstatic void get_with_clause(Query *query, deparse_context *context);\nstatic void get_select_query_def(Query *query, deparse_context *context);\nstatic void get_insert_query_def(Query *query, deparse_context *context);\nstatic void get_update_query_def(Query *query, deparse_context *context);\nstatic void get_update_query_targetlist_def(Query *query, List *targetList,\n\t\t\t\t\t\t\t\tdeparse_context *context,\n\t\t\t\t\t\t\t\tRangeTblEntry *rte);\nstatic void get_delete_query_def(Query *query, deparse_context *context);\nstatic void get_merge_query_def(Query *query, deparse_context *context);\nstatic void get_utility_query_def(Query *query, deparse_context *context);\nstatic void get_basic_select_query(Query *query, deparse_context *context);\nstatic void get_target_list(List *targetList, deparse_context *context);\nstatic void get_setop_query(Node *setOp, Query *query,\n\t\t\t\t\t\t\tdeparse_context *context);\nstatic Node *get_rule_sortgroupclause(Index ref, List *tlist,\n\t\t\t\t\t\t bool force_colno,\n\t\t\t\t\t\t deparse_context *context);\nstatic void get_rule_groupingset(GroupingSet *gset, List *targetlist,\n\t\t\t\t\t bool omit_parens, deparse_context *context);\nstatic void get_rule_orderby(List *orderList, List *targetList,\n\t\t\t\t bool force_colno, deparse_context *context);\nstatic void get_rule_windowclause(Query *query, deparse_context *context);\nstatic void get_rule_windowspec(WindowClause *wc, List *targetList,\n\t\t\t\t\tdeparse_context *context);\nstatic char *get_variable(Var *var, int levelsup, bool istoplevel,\n\t\t\t deparse_context *context);\nstatic void get_special_variable(Node *node, deparse_context *context,\n\t\t\t\t\t void *callback_arg);\nstatic void resolve_special_varno(Node *node, deparse_context *context,\n\t\t\t\t\trsv_callback callback, void *callback_arg);\nstatic Node *find_param_referent(Param *param, deparse_context *context,\n\t\t\t\t\tdeparse_namespace **dpns_p, ListCell **ancestor_cell_p);\nstatic SubPlan *find_param_generator(Param *param, deparse_context *context,\n\t\t\t\t\t\t\t\t\t int *column_p);\nstatic SubPlan *find_param_generator_initplan(Param *param, Plan *plan,\n\t\t\t\t\t\t\t\t\t\t\t  int *column_p);\nstatic void get_parameter(Param *param, deparse_context *context);\nstatic const char *get_simple_binary_op_name(OpExpr *expr);\nstatic bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);\nstatic void appendContextKeyword(deparse_context *context, const char *str,\n\t\t\t\t\t int indentBefore, int indentAfter, int indentPlus);\nstatic void removeStringInfoSpaces(StringInfo str);\nstatic void get_rule_expr(Node *node, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_rule_expr_toplevel(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit);\nstatic void get_rule_list_toplevel(List *lst, deparse_context *context,\n\t\t\t\t\t\t\t\t   bool showimplicit);\nstatic void get_rule_expr_funccall(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit);\nstatic bool looks_like_function(Node *node);\nstatic void get_oper_expr(OpExpr *expr, deparse_context *context);\nstatic void get_func_expr(FuncExpr *expr, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_proc_expr(CallStmt *stmt, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_agg_expr(Aggref *aggref, deparse_context *context,\n\t\t\t Aggref *original_aggref);\nstatic void get_agg_expr_helper(Aggref *aggref, deparse_context *context,\n\t\t\t\t\t\t\t\tAggref *original_aggref, const char *funcname,\n\t\t\t\t\t\t\t\tconst char *options, bool is_json_objectagg);\nstatic void get_agg_combine_expr(Node *node, deparse_context *context,\n\t\t\t\t\t void *callback_arg);\nstatic void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);\nstatic void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,\n\t\t\t\t\t\t\t\t\t   const char *funcname, const char *options,\n\t\t\t\t\t\t\t\t\t   bool is_json_objectagg);\nstatic bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);\nstatic void get_coercion_expr(Node *arg, deparse_context *context,\n\t\t\t\t  Oid resulttype, int32 resulttypmod,\n\t\t\t\t  Node *parentNode);\nstatic void get_const_expr(Const *constval, deparse_context *context,\n\t\t\t   int showtype);\nstatic void get_const_collation(Const *constval, deparse_context *context);\nstatic void get_json_format(JsonFormat *format, StringInfo buf);\nstatic void get_json_returning(JsonReturning *returning, StringInfo buf,\n\t\t\t\t\t\t\t   bool json_format_by_default);\nstatic void get_json_constructor(JsonConstructorExpr *ctor,\n\t\t\t\t\t\t\t\t deparse_context *context, bool showimplicit);\nstatic void get_json_constructor_options(JsonConstructorExpr *ctor,\n\t\t\t\t\t\t\t\t\t\t StringInfo buf);\nstatic void get_json_agg_constructor(JsonConstructorExpr *ctor,\n\t\t\t\t\t\t\t\t\t deparse_context *context,\n\t\t\t\t\t\t\t\t\t const char *funcname,\n\t\t\t\t\t\t\t\t\t bool is_json_objectagg);\nstatic void simple_quote_literal(StringInfo buf, const char *val);\nstatic void get_sublink_expr(SubLink *sublink, deparse_context *context);\nstatic void get_tablefunc(TableFunc *tf, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_from_clause(Query *query, const char *prefix,\n\t\t\t\tdeparse_context *context);\nstatic void get_from_clause_item(Node *jtnode, Query *query,\n\t\t\t\t\t deparse_context *context);\nstatic void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,\n\t\t\t\t\t\t  deparse_context *context);\nstatic void get_column_alias_list(deparse_columns *colinfo,\n\t\t\t\t\t  deparse_context *context);\nstatic void get_from_clause_coldeflist(RangeTblFunction *rtfunc,\n\t\t\t\t\t\t   deparse_columns *colinfo,\n\t\t\t\t\t\t   deparse_context *context);\nstatic void get_tablesample_def(TableSampleClause *tablesample,\n\t\t\t\t\tdeparse_context *context);\nstatic void get_opclass_name(Oid opclass, Oid actual_datatype,\n\t\t\t\t StringInfo buf);\nstatic Node *processIndirection(Node *node, deparse_context *context);\nstatic void printSubscripts(SubscriptingRef *aref, deparse_context *context);\nstatic char *get_relation_name(Oid relid);\nstatic char *generate_relation_or_shard_name(Oid relid, Oid distrelid,\n\t\t\t\tint64 shardid, List *namespaces);\nstatic char *generate_rte_shard_name(RangeTblEntry *rangeTableEntry);\nstatic char *generate_fragment_name(char *schemaName, char *tableName);\nstatic char *generate_function_name(Oid funcid, int nargs,\n\t\t\t\t\t   List *argnames, Oid *argtypes,\n\t\t\t\t\t   bool has_variadic, bool *use_variadic_p,\n\t\t\t\t\t   bool inGroupBy);\nstatic List *get_insert_column_names_list(List *targetList, StringInfo buf, deparse_context *context, RangeTblEntry *rte);\nstatic void get_json_path_spec(Node *path_spec, deparse_context *context,\n\t\t\t\t\t\t\t   bool showimplicit);\nstatic void get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,\n\t\t\t\t\t\t\t\t   deparse_context *context,\n\t\t\t\t\t\t\t\t   bool showimplicit);\nstatic void get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,\n\t\t\t\t\t\t\t\t\t\t  deparse_context *context,\n\t\t\t\t\t\t\t\t\t\t  bool showimplicit,\n\t\t\t\t\t\t\t\t\t\t  bool needcomma);\n\n#define only_marker(rte)  ((rte)->inh ? \"\" : \"ONLY \")\n\n\n\n/*\n * pg_get_query_def parses back one query tree, and outputs the resulting query\n * string into given buffer.\n */\nvoid\npg_get_query_def(Query *query, StringInfo buffer)\n{\n\tget_query_def(query, buffer, NIL, NULL, false, 0, WRAP_COLUMN_DEFAULT, 0);\n}\n\n/*\n * get_merged_argument_list merges both the IN and OUT arguments lists into one and\n * also eliminates the INOUT duplicates(present in both the lists). After merging both\n * the lists, it returns all the named-arguments in a list(mergedNamedArgList) along\n * with their types(mergedNamedArgTypes), final argument list(mergedArgumentList), and\n * the total number of arguments(totalArguments).\n */\nbool\nget_merged_argument_list(CallStmt *stmt, List **mergedNamedArgList,\n\t\t\t\t\t   Oid **mergedNamedArgTypes,\n\t\t\t\t\t   List **mergedArgumentList,\n\t\t\t\t\t   int *totalArguments)\n{\n\n\tOid  functionOid = stmt->funcexpr->funcid;\n\tList *namedArgList = NIL;\n\tList *finalArgumentList = NIL;\n\tOid  *finalArgTypes;\n\tOid  *argTypes = NULL;\n\tchar *argModes = NULL;\n\tchar **argNames = NULL;\n\tint  argIndex = 0;\n\n\tHeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));\n\tif (!HeapTupleIsValid(proctup))\n\t{\n\t\telog(ERROR, \"cache lookup failed for function %u\", functionOid);\n\t}\n\n\tint defArgs = get_func_arg_info(proctup, &argTypes, &argNames, &argModes);\n\tReleaseSysCache(proctup);\n\n\tif (argModes == NULL)\n\t{\n\t\t/* No OUT arguments */\n\t\treturn false;\n\t}\n\n\t/*\n\t * Passed arguments Includes IN, OUT, INOUT (in both the lists) and VARIADIC arguments,\n\t * which means INOUT arguments are double counted.\n\t */\n\tint numberOfArgs = list_length(stmt->funcexpr->args) + list_length(stmt->outargs);\n\tint totalInoutArgs = 0;\n\n\t/* Let's count INOUT arguments from the defined number of arguments */\n\tfor (argIndex=0; argIndex < defArgs; ++argIndex)\n\t{\n\t\tif (argModes[argIndex] == PROARGMODE_INOUT)\n\t\t\ttotalInoutArgs++;\n\t}\n\n\t/* Remove the duplicate INOUT counting */\n\tnumberOfArgs = numberOfArgs - totalInoutArgs;\n\tfinalArgTypes = palloc0(sizeof(Oid) * numberOfArgs);\n\n\tListCell *inArgCell = list_head(stmt->funcexpr->args);\n\tListCell *outArgCell = list_head(stmt->outargs);\n\n\tfor (argIndex=0; argIndex < numberOfArgs; ++argIndex)\n\t{\n\t\tswitch (argModes[argIndex])\n\t\t{\n\t\t\tcase PROARGMODE_IN:\n\t\t\tcase PROARGMODE_VARIADIC:\n\t\t\t{\n\t\t\t\tNode *arg = (Node *) lfirst(inArgCell);\n\n\t\t\t\tif (IsA(arg, NamedArgExpr))\n\t\t\t\t\tnamedArgList = lappend(namedArgList, ((NamedArgExpr *) arg)->name);\n\t\t\t\tfinalArgTypes[argIndex] = exprType(arg);\n\t\t\t\tfinalArgumentList = lappend(finalArgumentList, arg);\n\t\t\t\tinArgCell = lnext(stmt->funcexpr->args, inArgCell);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROARGMODE_OUT:\n\t\t\t{\n\t\t\t\tNode *arg = (Node *) lfirst(outArgCell);\n\n\t\t\t\tif (IsA(arg, NamedArgExpr))\n\t\t\t\t\tnamedArgList = lappend(namedArgList, ((NamedArgExpr *) arg)->name);\n\t\t\t\tfinalArgTypes[argIndex] = exprType(arg);\n\t\t\t\tfinalArgumentList = lappend(finalArgumentList, arg);\n\t\t\t\toutArgCell = lnext(stmt->outargs, outArgCell);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROARGMODE_INOUT:\n\t\t\t{\n\t\t\t\tNode *arg = (Node *) lfirst(inArgCell);\n\n\t\t\t\tif (IsA(arg, NamedArgExpr))\n\t\t\t\t\tnamedArgList = lappend(namedArgList, ((NamedArgExpr *) arg)->name);\n\t\t\t\tfinalArgTypes[argIndex] = exprType(arg);\n\t\t\t\tfinalArgumentList = lappend(finalArgumentList, arg);\n\t\t\t\tinArgCell = lnext(stmt->funcexpr->args, inArgCell);\n\t\t\t\toutArgCell = lnext(stmt->outargs, outArgCell);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROARGMODE_TABLE:\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\telog(ERROR, \"Unhandled procedure argument mode[%d]\", argModes[argIndex]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * After eliminating INOUT duplicates and merging OUT arguments, we now\n\t * have the final list of arguments.\n\t */\n\tif (defArgs != list_length(finalArgumentList))\n\t{\n\t\telog(ERROR, \"Insufficient number of args passed[%d] for function[%s]\",\n\t\t\t\t\t\t\t\t\t\t\tlist_length(finalArgumentList),\n\t\t\t\t\t\t\t\t\t\t\tget_func_name(functionOid));\n\t}\n\n\tif (list_length(finalArgumentList) > FUNC_MAX_ARGS)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_TOO_MANY_ARGUMENTS),\n\t\t\t\t errmsg(\"too many arguments[%d] for function[%s]\",\n\t\t\t\t\t\t\t\t\t\t\tlist_length(finalArgumentList),\n\t\t\t\t\t\t\t\t\t\t\tget_func_name(functionOid))));\n\t}\n\n\t*mergedNamedArgList =  namedArgList;\n\t*mergedNamedArgTypes = finalArgTypes;\n\t*mergedArgumentList = finalArgumentList;\n\t*totalArguments = numberOfArgs;\n\n\treturn true;\n}\n\n/*\n * pg_get_rule_expr deparses an expression and returns the result as a string.\n */\nchar *\npg_get_rule_expr(Node *expression)\n{\n\tbool showImplicitCasts = true;\n\tdeparse_context context;\n\tStringInfo buffer = makeStringInfo();\n\n\t/*\n\t * Set search_path to NIL so that all objects outside of pg_catalog will be\n\t * schema-prefixed. pg_catalog will be added automatically when we call\n\t * PushEmptySearchPath().\n\t */\n\tint saveNestLevel = PushEmptySearchPath();\n\n\tcontext.buf = buffer;\n\tcontext.namespaces = NIL;\n\tcontext.resultDesc = NULL;\n\tcontext.targetList = NIL;\n\tcontext.windowClause = NIL;\n\tcontext.varprefix = false;\n\tcontext.prettyFlags = 0;\n\tcontext.wrapColumn = WRAP_COLUMN_DEFAULT;\n\tcontext.indentLevel = 0;\n\tcontext.colNamesVisible = true;\n\tcontext.inGroupBy = false;\n\tcontext.varInOrderBy = false;\n\tcontext.distrelid = InvalidOid;\n\tcontext.shardid = INVALID_SHARD_ID;\n\n\tget_rule_expr(expression, &context, showImplicitCasts);\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n\n\treturn buffer->data;\n}\n\n/*\n * set_rtable_names: select RTE aliases to be used in printing a query\n *\n * We fill in dpns->rtable_names with a list of names that is one-for-one with\n * the already-filled dpns->rtable list.  Each RTE name is unique among those\n * in the new namespace plus any ancestor namespaces listed in\n * parent_namespaces.\n *\n * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.\n *\n * Note that this function is only concerned with relation names, not column\n * names.\n */\nstatic void\nset_rtable_names(deparse_namespace *dpns, List *parent_namespaces,\n\t\t\t\t Bitmapset *rels_used)\n{\n\tHASHCTL\t\thash_ctl;\n\tHTAB\t   *names_hash;\n\tNameHashEntry *hentry;\n\tbool\t\tfound;\n\tint\t\t\trtindex;\n\tListCell   *lc;\n\n\tdpns->rtable_names = NIL;\n\t/* nothing more to do if empty rtable */\n\tif (dpns->rtable == NIL)\n\t\treturn;\n\n\t/*\n\t * We use a hash table to hold known names, so that this process is O(N)\n\t * not O(N^2) for N names.\n\t */\n\thash_ctl.keysize = NAMEDATALEN;\n\thash_ctl.entrysize = sizeof(NameHashEntry);\n\thash_ctl.hcxt = CurrentMemoryContext;\n\tnames_hash = hash_create(\"set_rtable_names names\",\n\t\t\t\t\t\t\t list_length(dpns->rtable),\n\t\t\t\t\t\t\t &hash_ctl,\n\t\t\t\t\t\t\t HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);\n\n\t/* Preload the hash table with names appearing in parent_namespaces */\n\tforeach(lc, parent_namespaces)\n\t{\n\t\tdeparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);\n\t\tListCell   *lc2;\n\n\t\tforeach(lc2, olddpns->rtable_names)\n\t\t{\n\t\t\tchar\t   *oldname = (char *) lfirst(lc2);\n\n\t\t\tif (oldname == NULL)\n\t\t\t\tcontinue;\n\t\t\thentry = (NameHashEntry *) hash_search(names_hash,\n\t\t\t\t\t\t\t\t\t\t\t\t   oldname,\n\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t   &found);\n\t\t\t/* we do not complain about duplicate names in parent namespaces */\n\t\t\thentry->counter = 0;\n\t\t}\n\t}\n\n\t/* Now we can scan the rtable */\n\trtindex = 1;\n\tforeach(lc, dpns->rtable)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n\t\tchar\t   *refname;\n\n\t\t/* Just in case this takes an unreasonable amount of time ... */\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tif (rels_used && !bms_is_member(rtindex, rels_used))\n\t\t{\n\t\t\t/* Ignore unreferenced RTE */\n\t\t\trefname = NULL;\n\t\t}\n\t\telse if (rte->alias)\n\t\t{\n\t\t\t/* If RTE has a user-defined alias, prefer that */\n\t\t\trefname = rte->alias->aliasname;\n\t\t}\n\t\telse if (rte->rtekind == RTE_RELATION)\n\t\t{\n\t\t\t/* Use the current actual name of the relation */\n\t\t\trefname = get_rel_name(rte->relid);\n\t\t}\n\t\telse if (rte->rtekind == RTE_JOIN)\n\t\t{\n\t\t\t/* Unnamed join has no refname */\n\t\t\trefname = NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* Otherwise use whatever the parser assigned */\n\t\t\trefname = rte->eref->aliasname;\n\t\t}\n\n\t\t/*\n\t\t * If the selected name isn't unique, append digits to make it so, and\n\t\t * make a new hash entry for it once we've got a unique name.  For a\n\t\t * very long input name, we might have to truncate to stay within\n\t\t * NAMEDATALEN.\n\t\t */\n\t\tif (refname)\n\t\t{\n\t\t\thentry = (NameHashEntry *) hash_search(names_hash,\n\t\t\t\t\t\t\t\t\t\t\t\t   refname,\n\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t   &found);\n\t\t\tif (found)\n\t\t\t{\n\t\t\t\t/* Name already in use, must choose a new one */\n\t\t\t\tint\t\t\trefnamelen = strlen(refname);\n\t\t\t\tchar\t   *modname = (char *) palloc(refnamelen + 16);\n\t\t\t\tNameHashEntry *hentry2;\n\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\thentry->counter++;\n\t\t\t\t\tfor (;;)\n\t\t\t\t\t{\n\t\t\t\t\t\tmemcpy(modname, refname, refnamelen);\n\t\t\t\t\t\tsprintf(modname + refnamelen, \"_%d\", hentry->counter);\n\t\t\t\t\t\tif (strlen(modname) < NAMEDATALEN)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t/* drop chars from refname to keep all the digits */\n\t\t\t\t\t\trefnamelen = pg_mbcliplen(refname, refnamelen,\n\t\t\t\t\t\t\t\t\t\t\t\t  refnamelen - 1);\n\t\t\t\t\t}\n\t\t\t\t\thentry2 = (NameHashEntry *) hash_search(names_hash,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmodname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tHASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t&found);\n\t\t\t\t} while (found);\n\t\t\t\thentry2->counter = 0;\t/* init new hash entry */\n\t\t\t\trefname = modname;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* Name not previously used, need only initialize hentry */\n\t\t\t\thentry->counter = 0;\n\t\t\t}\n\t\t}\n\n\t\tdpns->rtable_names = lappend(dpns->rtable_names, refname);\n\t\trtindex++;\n\t}\n\n\thash_destroy(names_hash);\n}\n\n/*\n * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree\n *\n * For convenience, this is defined to initialize the deparse_namespace struct\n * from scratch.\n */\nstatic void\nset_deparse_for_query(deparse_namespace *dpns, Query *query,\n\t\t\t\t\t  List *parent_namespaces)\n{\n\tListCell   *lc;\n\tListCell   *lc2;\n\n\t/* Initialize *dpns and fill rtable/ctes links */\n\tmemset(dpns, 0, sizeof(deparse_namespace));\n\tdpns->rtable = query->rtable;\n\tdpns->subplans = NIL;\n\tdpns->ctes = query->cteList;\n\tdpns->appendrels = NULL;\n\n\t/* Assign a unique relation alias to each RTE */\n\tset_rtable_names(dpns, parent_namespaces, NULL);\n\n\t/* Initialize dpns->rtable_columns to contain zeroed structs */\n\tdpns->rtable_columns = NIL;\n\twhile (list_length(dpns->rtable_columns) < list_length(dpns->rtable))\n\t\tdpns->rtable_columns = lappend(dpns->rtable_columns,\n\t\t\t\t\t\t\t\t\t   palloc0(sizeof(deparse_columns)));\n\n\t/* If it's a utility query, it won't have a jointree */\n\tif (query->jointree)\n\t{\n\t\t/* Detect whether global uniqueness of USING names is needed */\n\t\tdpns->unique_using =\n\t\t\thas_dangerous_join_using(dpns, (Node *) query->jointree);\n\n\t\t/*\n\t\t * Select names for columns merged by USING, via a recursive pass over\n\t\t * the query jointree.\n\t\t */\n\t\tset_using_names(dpns, (Node *) query->jointree, NIL);\n\t}\n\n\t/*\n\t * Now assign remaining column aliases for each RTE.  We do this in a\n\t * linear scan of the rtable, so as to process RTEs whether or not they\n\t * are in the jointree (we mustn't miss NEW.*, INSERT target relations,\n\t * etc).  JOIN RTEs must be processed after their children, but this is\n\t * okay because they appear later in the rtable list than their children\n\t * (cf Asserts in identify_join_columns()).\n\t */\n\tforboth(lc, dpns->rtable, lc2, dpns->rtable_columns)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n\t\tdeparse_columns *colinfo = (deparse_columns *) lfirst(lc2);\n\n\t\tif (rte->rtekind == RTE_JOIN)\n\t\t\tset_join_column_names(dpns, rte, colinfo);\n\t\telse\n\t\t\tset_relation_column_names(dpns, rte, colinfo);\n\t}\n}\n\n/*\n * has_dangerous_join_using: search jointree for unnamed JOIN USING\n *\n * Merged columns of a JOIN USING may act differently from either of the input\n * columns, either because they are merged with COALESCE (in a FULL JOIN) or\n * because an implicit coercion of the underlying input column is required.\n * In such a case the column must be referenced as a column of the JOIN not as\n * a column of either input.  And this is problematic if the join is unnamed\n * (alias-less): we cannot qualify the column's name with an RTE name, since\n * there is none.  (Forcibly assigning an alias to the join is not a solution,\n * since that will prevent legal references to tables below the join.)\n * To ensure that every column in the query is unambiguously referenceable,\n * we must assign such merged columns names that are globally unique across\n * the whole query, aliasing other columns out of the way as necessary.\n *\n * Because the ensuing re-aliasing is fairly damaging to the readability of\n * the query, we don't do this unless we have to.  So, we must pre-scan\n * the join tree to see if we have to, before starting set_using_names().\n */\nstatic bool\nhas_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)\n{\n\tif (IsA(jtnode, RangeTblRef))\n\t{\n\t\t/* nothing to do here */\n\t}\n\telse if (IsA(jtnode, FromExpr))\n\t{\n\t\tFromExpr   *f = (FromExpr *) jtnode;\n\t\tListCell   *lc;\n\n\t\tforeach(lc, f->fromlist)\n\t\t{\n\t\t\tif (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))\n\t\t\t\treturn true;\n\t\t}\n\t}\n\telse if (IsA(jtnode, JoinExpr))\n\t{\n\t\tJoinExpr   *j = (JoinExpr *) jtnode;\n\n\t\t/* Is it an unnamed JOIN with USING? */\n\t\tif (j->alias == NULL && j->usingClause)\n\t\t{\n\t\t\t/*\n\t\t\t * Yes, so check each join alias var to see if any of them are not\n\t\t\t * simple references to underlying columns.  If so, we have a\n\t\t\t * dangerous situation and must pick unique aliases.\n\t\t\t */\n\t\t\tRangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);\n\n\t\t\t/* We need only examine the merged columns */\n\t\t\tfor (int i = 0; i < jrte->joinmergedcols; i++)\n\t\t\t{\n\t\t\t\tNode\t   *aliasvar = list_nth(jrte->joinaliasvars, i);\n\n\t\t\t\tif (!IsA(aliasvar, Var))\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t/* Nope, but inspect children */\n\t\tif (has_dangerous_join_using(dpns, j->larg))\n\t\t\treturn true;\n\t\tif (has_dangerous_join_using(dpns, j->rarg))\n\t\t\treturn true;\n\t}\n\telse\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(jtnode));\n\treturn false;\n}\n\n/*\n * set_using_names: select column aliases to be used for merged USING columns\n *\n * We do this during a recursive descent of the query jointree.\n * dpns->unique_using must already be set to determine the global strategy.\n *\n * Column alias info is saved in the dpns->rtable_columns list, which is\n * assumed to be filled with pre-zeroed deparse_columns structs.\n *\n * parentUsing is a list of all USING aliases assigned in parent joins of\n * the current jointree node.  (The passed-in list must not be modified.)\n */\nstatic void\nset_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)\n{\n\tif (IsA(jtnode, RangeTblRef))\n\t{\n\t\t/* nothing to do now */\n\t}\n\telse if (IsA(jtnode, FromExpr))\n\t{\n\t\tFromExpr   *f = (FromExpr *) jtnode;\n\t\tListCell   *lc;\n\n\t\tforeach(lc, f->fromlist)\n\t\t\tset_using_names(dpns, (Node *) lfirst(lc), parentUsing);\n\t}\n\telse if (IsA(jtnode, JoinExpr))\n\t{\n\t\tJoinExpr   *j = (JoinExpr *) jtnode;\n\t\tRangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);\n\t\tdeparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);\n\t\tint\t\t   *leftattnos;\n\t\tint\t\t   *rightattnos;\n\t\tdeparse_columns *leftcolinfo;\n\t\tdeparse_columns *rightcolinfo;\n\t\tint\t\t\ti;\n\t\tListCell   *lc;\n\n\t\t/* Get info about the shape of the join */\n\t\tidentify_join_columns(j, rte, colinfo);\n\t\tleftattnos = colinfo->leftattnos;\n\t\trightattnos = colinfo->rightattnos;\n\n\t\t/* Look up the not-yet-filled-in child deparse_columns structs */\n\t\tleftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);\n\t\trightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);\n\n\t\t/*\n\t\t * If this join is unnamed, then we cannot substitute new aliases at\n\t\t * this level, so any name requirements pushed down to here must be\n\t\t * pushed down again to the children.\n\t\t */\n\t\tif (rte->alias == NULL)\n\t\t{\n\t\t\tfor (i = 0; i < colinfo->num_cols; i++)\n\t\t\t{\n\t\t\t\tchar\t   *colname = colinfo->colnames[i];\n\n\t\t\t\tif (colname == NULL)\n\t\t\t\t\tcontinue;\n\n\t\t\t\t/* Push down to left column, unless it's a system column */\n\t\t\t\tif (leftattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(leftcolinfo, leftattnos[i]);\n\t\t\t\t\tleftcolinfo->colnames[leftattnos[i] - 1] = colname;\n\t\t\t\t}\n\n\t\t\t\t/* Same on the righthand side */\n\t\t\t\tif (rightattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(rightcolinfo, rightattnos[i]);\n\t\t\t\t\trightcolinfo->colnames[rightattnos[i] - 1] = colname;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * If there's a USING clause, select the USING column names and push\n\t\t * those names down to the children.  We have two strategies:\n\t\t *\n\t\t * If dpns->unique_using is true, we force all USING names to be\n\t\t * unique across the whole query level.  In principle we'd only need\n\t\t * the names of dangerous USING columns to be globally unique, but to\n\t\t * safely assign all USING names in a single pass, we have to enforce\n\t\t * the same uniqueness rule for all of them.  However, if a USING\n\t\t * column's name has been pushed down from the parent, we should use\n\t\t * it as-is rather than making a uniqueness adjustment.  This is\n\t\t * necessary when we're at an unnamed join, and it creates no risk of\n\t\t * ambiguity.  Also, if there's a user-written output alias for a\n\t\t * merged column, we prefer to use that rather than the input name;\n\t\t * this simplifies the logic and seems likely to lead to less aliasing\n\t\t * overall.\n\t\t *\n\t\t * If dpns->unique_using is false, we only need USING names to be\n\t\t * unique within their own join RTE.  We still need to honor\n\t\t * pushed-down names, though.\n\t\t *\n\t\t * Though significantly different in results, these two strategies are\n\t\t * implemented by the same code, with only the difference of whether\n\t\t * to put assigned names into dpns->using_names.\n\t\t */\n\t\tif (j->usingClause)\n\t\t{\n\t\t\t/* Copy the input parentUsing list so we don't modify it */\n\t\t\tparentUsing = list_copy(parentUsing);\n\n\t\t\t/* USING names must correspond to the first join output columns */\n\t\t\texpand_colnames_array_to(colinfo, list_length(j->usingClause));\n\t\t\ti = 0;\n\t\t\tforeach(lc, j->usingClause)\n\t\t\t{\n\t\t\t\tchar\t   *colname = strVal(lfirst(lc));\n\n\t\t\t\t/* Assert it's a merged column */\n\t\t\t\tAssert(leftattnos[i] != 0 && rightattnos[i] != 0);\n\n\t\t\t\t/* Adopt passed-down name if any, else select unique name */\n\t\t\t\tif (colinfo->colnames[i] != NULL)\n\t\t\t\t\tcolname = colinfo->colnames[i];\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* Prefer user-written output alias if any */\n\t\t\t\t\tif (rte->alias && i < list_length(rte->alias->colnames))\n\t\t\t\t\t\tcolname = strVal(list_nth(rte->alias->colnames, i));\n\t\t\t\t\t/* Make it appropriately unique */\n\t\t\t\t\tcolname = make_colname_unique(colname, dpns, colinfo);\n\t\t\t\t\tif (dpns->unique_using)\n\t\t\t\t\t\tdpns->using_names = lappend(dpns->using_names,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolname);\n\t\t\t\t\t/* Save it as output column name, too */\n\t\t\t\t\tcolinfo->colnames[i] = colname;\n\t\t\t\t}\n\n\t\t\t\t/* Remember selected names for use later */\n\t\t\t\tcolinfo->usingNames = lappend(colinfo->usingNames, colname);\n\t\t\t\tparentUsing = lappend(parentUsing, colname);\n\n\t\t\t\t/* Push down to left column, unless it's a system column */\n\t\t\t\tif (leftattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(leftcolinfo, leftattnos[i]);\n\t\t\t\t\tleftcolinfo->colnames[leftattnos[i] - 1] = colname;\n\t\t\t\t}\n\n\t\t\t\t/* Same on the righthand side */\n\t\t\t\tif (rightattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(rightcolinfo, rightattnos[i]);\n\t\t\t\t\trightcolinfo->colnames[rightattnos[i] - 1] = colname;\n\t\t\t\t}\n\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\n\t\t/* Mark child deparse_columns structs with correct parentUsing info */\n\t\tleftcolinfo->parentUsing = parentUsing;\n\t\trightcolinfo->parentUsing = parentUsing;\n\n\t\t/* Now recursively assign USING column names in children */\n\t\tset_using_names(dpns, j->larg, parentUsing);\n\t\tset_using_names(dpns, j->rarg, parentUsing);\n\t}\n\telse\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(jtnode));\n}\n\n/*\n * set_relation_column_names: select column aliases for a non-join RTE\n *\n * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.\n * If any colnames entries are already filled in, those override local\n * choices.\n */\nstatic void\nset_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,\n\t\t\t\t\t\t  deparse_columns *colinfo)\n{\n\tint\t\t\tncolumns;\n\tchar\t  **real_colnames;\n\tbool\t\tchanged_any;\n\tbool \t\thas_anonymous;\n\tint\t\t\tnoldcolumns;\n\tint\t\t\ti;\n\tint\t\t\tj;\n\n\t/*\n\t * Construct an array of the current \"real\" column names of the RTE.\n\t * real_colnames[] will be indexed by physical column number, with NULL\n\t * entries for dropped columns.\n\t */\n\tif (rte->rtekind == RTE_RELATION ||\n        GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\t/* Relation --- look to the system catalogs for up-to-date info */\n\t\tRelation\trel;\n\t\tTupleDesc\ttupdesc;\n\n\t\trel = relation_open(rte->relid, AccessShareLock);\n\t\ttupdesc = RelationGetDescr(rel);\n\n\t\tncolumns = tupdesc->natts;\n\t\treal_colnames = (char **) palloc(ncolumns * sizeof(char *));\n\n\t\tfor (i = 0; i < ncolumns; i++)\n\t\t{\n\t\t\tForm_pg_attribute attr = TupleDescAttr(tupdesc, i);\n\n\t\t\tif (attr->attisdropped)\n\t\t\t\treal_colnames[i] = NULL;\n\t\t\telse\n\t\t\t\treal_colnames[i] = pstrdup(NameStr(attr->attname));\n\t\t}\n\t\trelation_close(rel, AccessShareLock);\n\t}\n\telse\n\t{\n\t\t/* Otherwise get the column names from eref or expandRTE() */\n\t\tList\t   *colnames;\n\t\tListCell   *lc;\n\n\t\t/*\n\t\t * Functions returning composites have the annoying property that some\n\t\t * of the composite type's columns might have been dropped since the\n\t\t * query was parsed.  If possible, use expandRTE() to handle that\n\t\t * case, since it has the tedious logic needed to find out about\n\t\t * dropped columns.  However, if we're explaining a plan, then we\n\t\t * don't have rte->functions because the planner thinks that won't be\n\t\t * needed later, and that breaks expandRTE().  So in that case we have\n\t\t * to rely on rte->eref, which may lead us to report a dropped\n\t\t * column's old name; that seems close enough for EXPLAIN's purposes.\n\t\t *\n\t\t * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,\n\t\t * which should be sufficiently up-to-date: no other RTE types can\n\t\t * have columns get dropped from under them after parsing.\n\t\t */\n\t\tif (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)\n\t\t{\n\t\t\t/* Since we're not creating Vars, rtindex etc. don't matter */\n\t\t\texpandRTE(rte, 1, 0, -1, true /* include dropped */ ,\n\t\t\t\t\t  &colnames, NULL);\n\t\t}\n\t\telse\n\t\t\tcolnames = rte->eref->colnames;\n\n\t\tncolumns = list_length(colnames);\n\t\treal_colnames = (char **) palloc(ncolumns * sizeof(char *));\n\n\t\ti = 0;\n\t\tforeach(lc, colnames)\n\t\t{\n\t\t\t/*\n\t\t\t * If the column name we find here is an empty string, then it's a\n\t\t\t * dropped column, so change to NULL.\n\t\t\t */\n\t\t\tchar\t   *cname = strVal(lfirst(lc));\n\n\t\t\tif (cname[0] == '\\0')\n\t\t\t\tcname = NULL;\n\t\t\treal_colnames[i] = cname;\n\t\t\ti++;\n\t\t}\n\t}\n\n\t/*\n\t * Ensure colinfo->colnames has a slot for each column.  (It could be long\n\t * enough already, if we pushed down a name for the last column.)  Note:\n\t * it's possible that there are now more columns than there were when the\n\t * query was parsed, ie colnames could be longer than rte->eref->colnames.\n\t * We must assign unique aliases to the new columns too, else there could\n\t * be unresolved conflicts when the view/rule is reloaded.\n\t */\n\texpand_colnames_array_to(colinfo, ncolumns);\n\tAssert(colinfo->num_cols == ncolumns);\n\n\t/*\n\t * Make sufficiently large new_colnames and is_new_col arrays, too.\n\t *\n\t * Note: because we leave colinfo->num_new_cols zero until after the loop,\n\t * colname_is_unique will not consult that array, which is fine because it\n\t * would only be duplicate effort.\n\t */\n\tcolinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));\n\tcolinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));\n\n\t/*\n\t * Scan the columns, select a unique alias for each one, and store it in\n\t * colinfo->colnames and colinfo->new_colnames.  The former array has NULL\n\t * entries for dropped columns, the latter omits them.  Also mark\n\t * new_colnames entries as to whether they are new since parse time; this\n\t * is the case for entries beyond the length of rte->eref->colnames.\n\t */\n\tnoldcolumns = list_length(rte->eref->colnames);\n\tchanged_any = false;\n\thas_anonymous = false;\n\tj = 0;\n\tfor (i = 0; i < ncolumns; i++)\n\t{\n\t\tchar\t   *real_colname = real_colnames[i];\n\t\tchar\t   *colname = colinfo->colnames[i];\n\n\t\t/* Skip dropped columns */\n\t\tif (real_colname == NULL)\n\t\t{\n\t\t\tAssert(colname == NULL);\t/* colnames[i] is already NULL */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* If alias already assigned, that's what to use */\n\t\tif (colname == NULL)\n\t\t{\n\t\t\t/* If user wrote an alias, prefer that over real column name */\n\t\t\tif (rte->alias && i < list_length(rte->alias->colnames))\n\t\t\t\tcolname = strVal(list_nth(rte->alias->colnames, i));\n\t\t\telse\n\t\t\t\tcolname = real_colname;\n\n\t\t\t/* Unique-ify and insert into colinfo */\n\t\t\tcolname = make_colname_unique(colname, dpns, colinfo);\n\n\t\t\tcolinfo->colnames[i] = colname;\n\t\t}\n\n\t\t/* Put names of non-dropped columns in new_colnames[] too */\n\t\tcolinfo->new_colnames[j] = colname;\n\t\t/* And mark them as new or not */\n\t\tcolinfo->is_new_col[j] = (i >= noldcolumns);\n\t\tj++;\n\n\t\t/* Remember if any assigned aliases differ from \"real\" name */\n\t\tif (!changed_any && strcmp(colname, real_colname) != 0)\n\t\t\tchanged_any = true;\n\n\t\t/*\n\t\t * Remember if there is a reference to an anonymous column as named by\n\t\t * char * FigureColname(Node *node)\n\t\t */\n\t\tif (!has_anonymous && strcmp(real_colname, \"?column?\") == 0)\n\t\t\thas_anonymous = true;\n\t}\n\n\t/*\n\t * Set correct length for new_colnames[] array.  (Note: if columns have\n\t * been added, colinfo->num_cols includes them, which is not really quite\n\t * right but is harmless, since any new columns must be at the end where\n\t * they won't affect varattnos of pre-existing columns.)\n\t */\n\tcolinfo->num_new_cols = j;\n\n\t/*\n\t * For a relation RTE, we need only print the alias column names if any\n\t * are different from the underlying \"real\" names.  For a function RTE,\n\t * always emit a complete column alias list; this is to protect against\n\t * possible instability of the default column names (eg, from altering\n\t * parameter names).  For tablefunc RTEs, we never print aliases, because\n\t * the column names are part of the clause itself.  For other RTE types,\n\t * print if we changed anything OR if there were user-written column\n\t * aliases (since the latter would be part of the underlying \"reality\").\n\t */\n\tif (rte->rtekind == RTE_RELATION)\n\t\tcolinfo->printaliases = changed_any;\n\telse if (rte->rtekind == RTE_FUNCTION)\n\t\tcolinfo->printaliases = true;\n\telse if (rte->rtekind == RTE_TABLEFUNC)\n\t\tcolinfo->printaliases = false;\n\telse if (rte->alias && rte->alias->colnames != NIL)\n\t\tcolinfo->printaliases = true;\n\telse\n\t\tcolinfo->printaliases = changed_any || has_anonymous;\n}\n\n/*\n * set_join_column_names: select column aliases for a join RTE\n *\n * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.\n * If any colnames entries are already filled in, those override local\n * choices.  Also, names for USING columns were already chosen by\n * set_using_names().  We further expect that column alias selection has been\n * completed for both input RTEs.\n */\nstatic void\nset_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,\n\t\t\t\t\t  deparse_columns *colinfo)\n{\n\tdeparse_columns *leftcolinfo;\n\tdeparse_columns *rightcolinfo;\n\tbool\t\tchanged_any;\n\tint\t\t\tnoldcolumns;\n\tint\t\t\tnnewcolumns;\n\tBitmapset  *leftmerged = NULL;\n\tBitmapset  *rightmerged = NULL;\n\tint\t\t\ti;\n\tint\t\t\tj;\n\tint\t\t\tic;\n\tint\t\t\tjc;\n\n\t/* Look up the previously-filled-in child deparse_columns structs */\n\tleftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);\n\trightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);\n\n\t/*\n\t * Ensure colinfo->colnames has a slot for each column.  (It could be long\n\t * enough already, if we pushed down a name for the last column.)  Note:\n\t * it's possible that one or both inputs now have more columns than there\n\t * were when the query was parsed, but we'll deal with that below.  We\n\t * only need entries in colnames for pre-existing columns.\n\t */\n\tnoldcolumns = list_length(rte->eref->colnames);\n\texpand_colnames_array_to(colinfo, noldcolumns);\n\tAssert(colinfo->num_cols == noldcolumns);\n\n\t/*\n\t * Scan the join output columns, select an alias for each one, and store\n\t * it in colinfo->colnames.  If there are USING columns, set_using_names()\n\t * already selected their names, so we can start the loop at the first\n\t * non-merged column.\n\t */\n\tchanged_any = false;\n\tfor (i = list_length(colinfo->usingNames); i < noldcolumns; i++)\n\t{\n\t\tchar\t   *colname = colinfo->colnames[i];\n\t\tchar\t   *real_colname;\n\n\t\t/* Join column must refer to at least one input column */\n\t\tAssert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);\n\n\t\t/* Get the child column name */\n\t\tif (colinfo->leftattnos[i] > 0)\n\t\t\treal_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];\n\t\telse if (colinfo->rightattnos[i] > 0)\n\t\t\treal_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];\n\t\telse\n\t\t{\n\t\t\t/* We're joining system columns --- use eref name */\n\t\t\treal_colname = strVal(list_nth(rte->eref->colnames, i));\n\t\t}\n\t\t\t/* If child col has been dropped, no need to assign a join colname */\n\t\tif (real_colname == NULL)\n\t\t{\n\t\t\tcolinfo->colnames[i] = NULL;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* In an unnamed join, just report child column names as-is */\n\t\tif (rte->alias == NULL)\n\t\t{\n\t\t\tcolinfo->colnames[i] = real_colname;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* If alias already assigned, that's what to use */\n\t\tif (colname == NULL)\n\t\t{\n\t\t\t/* If user wrote an alias, prefer that over real column name */\n\t\t\tif (rte->alias && i < list_length(rte->alias->colnames))\n\t\t\t\tcolname = strVal(list_nth(rte->alias->colnames, i));\n\t\t\telse\n\t\t\t\tcolname = real_colname;\n\n\t\t\t/* Unique-ify and insert into colinfo */\n\t\t\tcolname = make_colname_unique(colname, dpns, colinfo);\n\n\t\t\tcolinfo->colnames[i] = colname;\n\t\t}\n\n\t\t/* Remember if any assigned aliases differ from \"real\" name */\n\t\tif (!changed_any && strcmp(colname, real_colname) != 0)\n\t\t\tchanged_any = true;\n\t}\n\n\t/*\n\t * Calculate number of columns the join would have if it were re-parsed\n\t * now, and create storage for the new_colnames and is_new_col arrays.\n\t *\n\t * Note: colname_is_unique will be consulting new_colnames[] during the\n\t * loops below, so its not-yet-filled entries must be zeroes.\n\t */\n\tnnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -\n\t\tlist_length(colinfo->usingNames);\n\tcolinfo->num_new_cols = nnewcolumns;\n\tcolinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));\n\tcolinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));\n\n\t/*\n\t * Generating the new_colnames array is a bit tricky since any new columns\n\t * added since parse time must be inserted in the right places.  This code\n\t * must match the parser, which will order a join's columns as merged\n\t * columns first (in USING-clause order), then non-merged columns from the\n\t * left input (in attnum order), then non-merged columns from the right\n\t * input (ditto).  If one of the inputs is itself a join, its columns will\n\t * be ordered according to the same rule, which means newly-added columns\n\t * might not be at the end.  We can figure out what's what by consulting\n\t * the leftattnos and rightattnos arrays plus the input is_new_col arrays.\n\t *\n\t * In these loops, i indexes leftattnos/rightattnos (so it's join varattno\n\t * less one), j indexes new_colnames/is_new_col, and ic/jc have similar\n\t * meanings for the current child RTE.\n\t */\n\n\t/* Handle merged columns; they are first and can't be new */\n\ti = j = 0;\n\twhile (i < noldcolumns &&\n\t\t   colinfo->leftattnos[i] != 0 &&\n\t\t   colinfo->rightattnos[i] != 0)\n\t{\n\t\t/* column name is already determined and known unique */\n\t\tcolinfo->new_colnames[j] = colinfo->colnames[i];\n\t\tcolinfo->is_new_col[j] = false;\n\n\t\t/* build bitmapsets of child attnums of merged columns */\n\t\tif (colinfo->leftattnos[i] > 0)\n\t\t\tleftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);\n\t\tif (colinfo->rightattnos[i] > 0)\n\t\t\trightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);\n\n\t\ti++, j++;\n\t}\n\n\t/* Handle non-merged left-child columns */\n\tic = 0;\n\tfor (jc = 0; jc < leftcolinfo->num_new_cols; jc++)\n\t{\n\t\tchar\t   *child_colname = leftcolinfo->new_colnames[jc];\n\n\t\tif (!leftcolinfo->is_new_col[jc])\n\t\t{\n\t\t\t/* Advance ic to next non-dropped old column of left child */\n\t\t\twhile (ic < leftcolinfo->num_cols &&\n\t\t\t\t   leftcolinfo->colnames[ic] == NULL)\n\t\t\t\tic++;\n\t\t\tAssert(ic < leftcolinfo->num_cols);\n\t\t\tic++;\n\t\t\t/* If it is a merged column, we already processed it */\n\t\t\tif (bms_is_member(ic, leftmerged))\n\t\t\t\tcontinue;\n\t\t\t/* Else, advance i to the corresponding existing join column */\n\t\t\twhile (i < colinfo->num_cols &&\n\t\t\t\t   colinfo->colnames[i] == NULL)\n\t\t\t\ti++;\n\t\t\tAssert(i < colinfo->num_cols);\n\t\t\tAssert(ic == colinfo->leftattnos[i]);\n\t\t\t/* Use the already-assigned name of this column */\n\t\t\tcolinfo->new_colnames[j] = colinfo->colnames[i];\n\t\t\ti++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Unique-ify the new child column name and assign, unless we're\n\t\t\t * in an unnamed join, in which case just copy\n\t\t\t */\n\t\t\tif (rte->alias != NULL)\n\t\t\t{\n\t\t\t\tcolinfo->new_colnames[j] =\n\t\t\t\t\tmake_colname_unique(child_colname, dpns, colinfo);\n\t\t\t\tif (!changed_any &&\n\t\t\t\t\tstrcmp(colinfo->new_colnames[j], child_colname) != 0)\n\t\t\t\t\tchanged_any = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcolinfo->new_colnames[j] = child_colname;\n\t\t}\n\n\t\tcolinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];\n\t\tj++;\n\t}\n\n\t/* Handle non-merged right-child columns in exactly the same way */\n\tic = 0;\n\tfor (jc = 0; jc < rightcolinfo->num_new_cols; jc++)\n\t{\n\t\tchar\t   *child_colname = rightcolinfo->new_colnames[jc];\n\n\t\tif (!rightcolinfo->is_new_col[jc])\n\t\t{\n\t\t\t/* Advance ic to next non-dropped old column of right child */\n\t\t\twhile (ic < rightcolinfo->num_cols &&\n\t\t\t\t   rightcolinfo->colnames[ic] == NULL)\n\t\t\t\tic++;\n\t\t\tAssert(ic < rightcolinfo->num_cols);\n\t\t\tic++;\n\t\t\t/* If it is a merged column, we already processed it */\n\t\t\tif (bms_is_member(ic, rightmerged))\n\t\t\t\tcontinue;\n\t\t\t/* Else, advance i to the corresponding existing join column */\n\t\t\twhile (i < colinfo->num_cols &&\n\t\t\t\t   colinfo->colnames[i] == NULL)\n\t\t\t\ti++;\n\t\t\tAssert(i < colinfo->num_cols);\n\t\t\tAssert(ic == colinfo->rightattnos[i]);\n\t\t\t/* Use the already-assigned name of this column */\n\t\t\tcolinfo->new_colnames[j] = colinfo->colnames[i];\n\t\t\ti++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Unique-ify the new child column name and assign, unless we're\n\t\t\t * in an unnamed join, in which case just copy\n\t\t\t */\n\t\t\tif (rte->alias != NULL)\n\t\t\t{\n\t\t\t\tcolinfo->new_colnames[j] =\n\t\t\t\t\tmake_colname_unique(child_colname, dpns, colinfo);\n\t\t\t\tif (!changed_any &&\n\t\t\t\t\tstrcmp(colinfo->new_colnames[j], child_colname) != 0)\n\t\t\t\t\tchanged_any = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcolinfo->new_colnames[j] = child_colname;\n\t\t}\n\n\t\tcolinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];\n\t\tj++;\n\t}\n\n\t/* Assert we processed the right number of columns */\n#ifdef USE_ASSERT_CHECKING\n\tfor (int col_index = 0; col_index < colinfo->num_cols; col_index++)\n\t{\n\t\t/*\n\t\t * In the above processing-loops, \"i\" advances only if\n\t\t * the column is not new, check if this is a new column.\n\t\t */\n\t\tif (colinfo->is_new_col[col_index])\n\t\t\ti++;\n\t}\n\tAssert(j == nnewcolumns);\n#endif\n\n\t/*\n\t * For a named join, print column aliases if we changed any from the child\n\t * names.  Unnamed joins cannot print aliases.\n\t */\n\tif (rte->alias != NULL)\n\t\tcolinfo->printaliases = changed_any;\n\telse\n\t\tcolinfo->printaliases = false;\n}\n\n/*\n * colname_is_unique: is colname distinct from already-chosen column names?\n *\n * dpns is query-wide info, colinfo is for the column's RTE\n */\nstatic bool\ncolname_is_unique(const char *colname, deparse_namespace *dpns,\n\t\t\t\t  deparse_columns *colinfo)\n{\n\tint\t\t\ti;\n\tListCell   *lc;\n\n\t/* Check against already-assigned column aliases within RTE */\n\tfor (i = 0; i < colinfo->num_cols; i++)\n\t{\n\t\tchar\t   *oldname = colinfo->colnames[i];\n\n\t\tif (oldname && strcmp(oldname, colname) == 0)\n\t\t\treturn false;\n\t}\n\n\t/*\n\t * If we're building a new_colnames array, check that too (this will be\n\t * partially but not completely redundant with the previous checks)\n\t */\n\tfor (i = 0; i < colinfo->num_new_cols; i++)\n\t{\n\t\tchar\t   *oldname = colinfo->new_colnames[i];\n\n\t\tif (oldname && strcmp(oldname, colname) == 0)\n\t\t\treturn false;\n\t}\n\n\t/* Also check against USING-column names that must be globally unique */\n\tforeach(lc, dpns->using_names)\n\t{\n\t\tchar\t   *oldname = (char *) lfirst(lc);\n\n\t\tif (strcmp(oldname, colname) == 0)\n\t\t\treturn false;\n\t}\n\n\t/* Also check against names already assigned for parent-join USING cols */\n\tforeach(lc, colinfo->parentUsing)\n\t{\n\t\tchar\t   *oldname = (char *) lfirst(lc);\n\n\t\tif (strcmp(oldname, colname) == 0)\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n/*\n * make_colname_unique: modify colname if necessary to make it unique\n *\n * dpns is query-wide info, colinfo is for the column's RTE\n */\nstatic char *\nmake_colname_unique(char *colname, deparse_namespace *dpns,\n\t\t\t\t\tdeparse_columns *colinfo)\n{\n\t/*\n\t * If the selected name isn't unique, append digits to make it so.  For a\n\t * very long input name, we might have to truncate to stay within\n\t * NAMEDATALEN.\n\t */\n\tif (!colname_is_unique(colname, dpns, colinfo))\n\t{\n\t\tint\t\t\tcolnamelen = strlen(colname);\n\t\tchar\t   *modname = (char *) palloc(colnamelen + 16);\n\t\tint\t\t\ti = 0;\n\n\t\tdo\n\t\t{\n\t\t\ti++;\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\tmemcpy(modname, colname, colnamelen);\n\t\t\t\tsprintf(modname + colnamelen, \"_%d\", i);\n\t\t\t\tif (strlen(modname) < NAMEDATALEN)\n\t\t\t\t\tbreak;\n\t\t\t\t/* drop chars from colname to keep all the digits */\n\t\t\t\tcolnamelen = pg_mbcliplen(colname, colnamelen,\n\t\t\t\t\t\t\t\t\t\t  colnamelen - 1);\n\t\t\t}\n\t\t} while (!colname_is_unique(modname, dpns, colinfo));\n\t\tcolname = modname;\n\t}\n\treturn colname;\n}\n\n/*\n * expand_colnames_array_to: make colinfo->colnames at least n items long\n *\n * Any added array entries are initialized to zero.\n */\nstatic void\nexpand_colnames_array_to(deparse_columns *colinfo, int n)\n{\n\tif (n > colinfo->num_cols)\n\t{\n\t\tif (colinfo->colnames == NULL)\n\t\t\tcolinfo->colnames = palloc0_array(char *, n);\n\t\telse\n\t\t{\n\t\t\tcolinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);\n\t\t}\n\t\tcolinfo->num_cols = n;\n\t}\n}\n\n/*\n * identify_join_columns: figure out where columns of a join come from\n *\n * Fills the join-specific fields of the colinfo struct, except for\n * usingNames which is filled later.\n */\nstatic void\nidentify_join_columns(JoinExpr *j, RangeTblEntry *jrte,\n\t\t\t\t\t  deparse_columns *colinfo)\n{\n\tint\t\t\tnumjoincols;\n\tint\t\t\tjcolno;\n\tint\t\t\trcolno;\n\tListCell   *lc;\n\n\t/* Extract left/right child RT indexes */\n\tif (IsA(j->larg, RangeTblRef))\n\t\tcolinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;\n\telse if (IsA(j->larg, JoinExpr))\n\t\tcolinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;\n\telse\n\t\telog(ERROR, \"unrecognized node type in jointree: %d\",\n\t\t\t (int) nodeTag(j->larg));\n\tif (IsA(j->rarg, RangeTblRef))\n\t\tcolinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;\n\telse if (IsA(j->rarg, JoinExpr))\n\t\tcolinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;\n\telse\n\t\telog(ERROR, \"unrecognized node type in jointree: %d\",\n\t\t\t (int) nodeTag(j->rarg));\n\n\t/* Assert children will be processed earlier than join in second pass */\n\tAssert(colinfo->leftrti < j->rtindex);\n\tAssert(colinfo->rightrti < j->rtindex);\n\n\t/* Initialize result arrays with zeroes */\n\tnumjoincols = list_length(jrte->joinaliasvars);\n\tAssert(numjoincols == list_length(jrte->eref->colnames));\n\tcolinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));\n\tcolinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));\n\n\t/*\n\t * Deconstruct RTE's joinleftcols/joinrightcols into desired format.\n\t * Recall that the column(s) merged due to USING are the first column(s)\n\t * of the join output.  We need not do anything special while scanning\n\t * joinleftcols, but while scanning joinrightcols we must distinguish\n\t * merged from unmerged columns.\n\t */\n\tjcolno = 0;\n\tforeach(lc, jrte->joinleftcols)\n\t{\n\t\tint\t\t\tleftattno = lfirst_int(lc);\n\n\t\tcolinfo->leftattnos[jcolno++] = leftattno;\n\t}\n\trcolno = 0;\n\tforeach(lc, jrte->joinrightcols)\n\t{\n\t\tint\t\t\trightattno = lfirst_int(lc);\n\n\t\tif (rcolno < jrte->joinmergedcols)\t/* merged column? */\n\t\t\tcolinfo->rightattnos[rcolno] = rightattno;\n\t\telse\n\t\t\tcolinfo->rightattnos[jcolno++] = rightattno;\n\t\trcolno++;\n\t}\n\tAssert(jcolno == numjoincols);\n}\n\n/*\n * get_rtable_name: convenience function to get a previously assigned RTE alias\n *\n * The RTE must belong to the topmost namespace level in \"context\".\n */\nstatic char *\nget_rtable_name(int rtindex, deparse_context *context)\n{\n\tdeparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);\n\n\tAssert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));\n\treturn (char *) list_nth(dpns->rtable_names, rtindex - 1);\n}\n\n/*\n * set_deparse_plan: set up deparse_namespace to parse subexpressions\n * of a given Plan node\n *\n * This sets the plan, outer_planstate, inner_planstate, outer_tlist,\n * inner_tlist, and index_tlist fields.  Caller is responsible for adjusting\n * the ancestors list if necessary.  Note that the rtable and ctes fields do\n * not need to change when shifting attention to different plan nodes in a\n * single plan tree.\n */\nstatic void\nset_deparse_plan(deparse_namespace *dpns, Plan *plan)\n{\n\tdpns->plan = plan;\n\n\t/*\n\t * We special-case Append and MergeAppend to pretend that the first child\n\t * plan is the OUTER referent; we have to interpret OUTER Vars in their\n\t * tlists according to one of the children, and the first one is the most\n\t * natural choice.\n\t */\n\tif (IsA(plan, Append))\n\t\tdpns->outer_plan = linitial(((Append *) plan)->appendplans);\n\telse if (IsA(plan, MergeAppend))\n\t\tdpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);\n\telse\n\t\tdpns->outer_plan = outerPlan(plan);\n\n\tif (dpns->outer_plan)\n\t\tdpns->outer_tlist = dpns->outer_plan->targetlist;\n\telse\n\t\tdpns->outer_tlist = NIL;\n\n\t/*\n\t * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't\n\t * use OUTER because that could someday conflict with the normal meaning.)\n\t * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.\n     * For a WorkTableScan, locate the parent RecursiveUnion plan node and use\n     * that as INNER referent.\n     *\n     * For MERGE, pretend the ModifyTable's source plan (its outer plan) is\n\t * INNER referent.  This is the join from the target relation to the data\n\t * source, and all INNER_VAR Vars in other parts of the query refer to its\n\t * targetlist.\n\t *\n\t * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the\n\t * excluded expression's tlist. (Similar to the SubqueryScan we don't want\n\t * to reuse OUTER, it's used for RETURNING in some modify table cases,\n\t * although not INSERT .. CONFLICT).\n\t */\n\tif (IsA(plan, SubqueryScan))\n\t\tdpns->inner_plan = ((SubqueryScan *) plan)->subplan;\n\telse if (IsA(plan, CteScan))\n\t\tdpns->inner_plan = list_nth(dpns->subplans,\n\t\t\t\t\t\t\t\t\t((CteScan *) plan)->ctePlanId - 1);\n    else if (IsA(plan, WorkTableScan))\n\t\tdpns->inner_plan = find_recursive_union(dpns,\n\t\t\t\t\t\t\t\t\t\t\t\t(WorkTableScan *) plan);\n\telse if (IsA(plan, ModifyTable))\n\t{\n\t\tif (((ModifyTable *) plan)->operation == CMD_MERGE)\n\t\t\tdpns->inner_plan = outerPlan(plan);\n\t\telse\n\t\t\tdpns->inner_plan = plan;\n\t}\n\telse\n\t\tdpns->inner_plan = innerPlan(plan);\n\n\tif (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)\n\t\tdpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;\n\telse if (dpns->inner_plan)\n\t\tdpns->inner_tlist = dpns->inner_plan->targetlist;\n\telse\n\t\tdpns->inner_tlist = NIL;\n\n\t/* Set up referent for INDEX_VAR Vars, if needed */\n\tif (IsA(plan, IndexOnlyScan))\n\t\tdpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;\n\telse if (IsA(plan, ForeignScan))\n\t\tdpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;\n\telse if (IsA(plan, CustomScan))\n\t\tdpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;\n\telse\n\t\tdpns->index_tlist = NIL;\n}\n\n/*\n * Locate the ancestor plan node that is the RecursiveUnion generating\n * the WorkTableScan's work table.  We can match on wtParam, since that\n * should be unique within the plan tree.\n */\nstatic Plan *\nfind_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)\n{\n\tListCell   *lc;\n\n\tforeach(lc, dpns->ancestors)\n\t{\n\t\tPlan\t   *ancestor = (Plan *) lfirst(lc);\n\n\t\tif (IsA(ancestor, RecursiveUnion) &&\n\t\t\t((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)\n\t\t\treturn ancestor;\n\t}\n\telog(ERROR, \"could not find RecursiveUnion for WorkTableScan with wtParam %d\",\n\t\t wtscan->wtParam);\n\treturn NULL;\n}\n\n/*\n * push_child_plan: temporarily transfer deparsing attention to a child plan\n *\n * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the\n * deparse context in case the referenced expression itself uses\n * OUTER_VAR/INNER_VAR.  We modify the top stack entry in-place to avoid\n * affecting levelsup issues (although in a Plan tree there really shouldn't\n * be any).\n *\n * Caller must provide a local deparse_namespace variable to save the\n * previous state for pop_child_plan.\n */\nstatic void\npush_child_plan(deparse_namespace *dpns, Plan *plan,\n\t\t\t\tdeparse_namespace *save_dpns)\n{\n\t/* Save state for restoration later */\n\t*save_dpns = *dpns;\n\n\t/* Link current plan node into ancestors list */\n\tdpns->ancestors = lcons(dpns->plan, dpns->ancestors);\n\n\t/* Set attention on selected child */\n\tset_deparse_plan(dpns, plan);\n}\n\n/*\n * pop_child_plan: undo the effects of push_child_plan\n */\nstatic void\npop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)\n{\n\tList\t   *ancestors;\n\n\t/* Get rid of ancestors list cell added by push_child_plan */\n\tancestors = list_delete_first(dpns->ancestors);\n\n\t/* Restore fields changed by push_child_plan */\n\t*dpns = *save_dpns;\n\n\t/* Make sure dpns->ancestors is right (may be unnecessary) */\n\tdpns->ancestors = ancestors;\n}\n\n/*\n * push_ancestor_plan: temporarily transfer deparsing attention to an\n * ancestor plan\n *\n * When expanding a Param reference, we must adjust the deparse context\n * to match the plan node that contains the expression being printed;\n * otherwise we'd fail if that expression itself contains a Param or\n * OUTER_VAR/INNER_VAR/INDEX_VAR variable.\n *\n * The target ancestor is conveniently identified by the ListCell holding it\n * in dpns->ancestors.\n *\n * Caller must provide a local deparse_namespace variable to save the\n * previous state for pop_ancestor_plan.\n */\nstatic void\npush_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,\n\t\t\t\t   deparse_namespace *save_dpns)\n{\n\tPlan\t   *plan = (Plan *) lfirst(ancestor_cell);\n\n\t/* Save state for restoration later */\n\t*save_dpns = *dpns;\n\n\t/* Build a new ancestor list with just this node's ancestors */\n\tdpns->ancestors =\n\t\tlist_copy_tail(dpns->ancestors,\n\t\t\t\t\t   list_cell_number(dpns->ancestors, ancestor_cell) + 1);\n\n\t/* Set attention on selected ancestor */\n\tset_deparse_plan(dpns, plan);\n}\n\n/*\n * pop_ancestor_plan: undo the effects of push_ancestor_plan\n */\nstatic void\npop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)\n{\n\t/* Free the ancestor list made in push_ancestor_plan */\n\tlist_free(dpns->ancestors);\n\n\t/* Restore fields changed by push_ancestor_plan */\n\t*dpns = *save_dpns;\n}\n\n/* ----------\n * deparse_shard_query\t\t- Parse back a query for execution on a shard\n *\n * Builds an SQL string to perform the provided query on a specific shard and\n * places this string into the provided buffer.\n * ----------\n */\nvoid\ndeparse_shard_query(Query *query, Oid distrelid, int64 shardid,\n\t\t\t\t\tStringInfo buffer)\n{\n\tget_query_def_extended(query, buffer, NIL, distrelid, shardid, NULL,\n\t                       false,\n\t\t\t\t\t\t   0, WRAP_COLUMN_DEFAULT, 0);\n}\n\n/* ----------\n * get_query_def\t\t\t- Parse back one query parsetree\n *\n * query: parsetree to be displayed\n * buf: output text is appended to buf\n * parentnamespace: list (initially empty) of outer-level deparse_namespace's\n * resultDesc: if not NULL, the output tuple descriptor for the view\n *\t\trepresented by a SELECT query.  We use the column names from it\n *\t\tto label SELECT output columns, in preference to names in the query\n * colNamesVisible: true if the surrounding context cares about the output\n *\t\tcolumn names at all (as, for example, an EXISTS() context does not);\n *\t\twhen false, we can suppress dummy column labels such as \"?column?\"\n * prettyFlags: bitmask of PRETTYFLAG_XXX options\n * wrapColumn: maximum line length, or -1 to disable wrapping\n * startIndent: initial indentation amount\n * ----------\n */\nstatic void\nget_query_def(Query *query, StringInfo buf, List *parentnamespace,\n\t\t\t  TupleDesc resultDesc, bool colNamesVisible,\n\t\t\t  int prettyFlags, int wrapColumn, int startIndent)\n{\n\tget_query_def_extended(query, buf, parentnamespace, InvalidOid, 0, resultDesc,\n\t                       colNamesVisible,\n\t\t\t\t\t\t   prettyFlags, wrapColumn, startIndent);\n}\n\n/* ----------\n * get_query_def_extended\t\t- Parse back one query parsetree, optionally\n * \t\t\t\t\t\t\t\t  with extension using a shard identifier.\n *\n * If distrelid is valid and shardid is positive, the provided shardid is added\n * any time the provided relid is deparsed, so that the query may be executed\n * on a placement for the given shard.\n * ----------\n */\nstatic void\nget_query_def_extended(Query *query, StringInfo buf, List *parentnamespace,\n\t\t\t\t\t   Oid distrelid, int64 shardid, TupleDesc resultDesc,\n\t\t\t\t\t   bool colNamesVisible,\n\t\t\t\t\t   int prettyFlags, int wrapColumn, int startIndent)\n{\n\tdeparse_context context;\n\tdeparse_namespace dpns;\n\n\t/* Guard against excessively long or deeply-nested queries */\n\tCHECK_FOR_INTERRUPTS();\n\tcheck_stack_depth();\n\n\t/*\n\t * Before we begin to examine the query, acquire locks on referenced\n\t * relations, and fix up deleted columns in JOIN RTEs.  This ensures\n\t * consistent results.  Note we assume it's OK to scribble on the passed\n\t * querytree!\n\t *\n\t * We are only deparsing the query (we are not about to execute it), so we\n\t * only need AccessShareLock on the relations it mentions.\n\t */\n\tAcquireRewriteLocks(query, false, false);\n\n\t/*\n\t * Set search_path to NIL so that all objects outside of pg_catalog will be\n\t * schema-prefixed. pg_catalog will be added automatically when we call\n\t * PushEmptySearchPath().\n\t */\n\tint saveNestLevel = PushEmptySearchPath();\n\n\tcontext.buf = buf;\n\tcontext.namespaces = lcons(&dpns, list_copy(parentnamespace));\n\tcontext.resultDesc = NULL;\n\tcontext.targetList = NIL;\n\tcontext.windowClause = NIL;\n\tcontext.varprefix = (parentnamespace != NIL ||\n\t\t\t\t\t\t list_length(query->rtable) != 1);\n\tcontext.prettyFlags = prettyFlags;\n\tcontext.wrapColumn = wrapColumn;\n\tcontext.indentLevel = startIndent;\n\tcontext.colNamesVisible = true;\n\tcontext.inGroupBy = false;\n\tcontext.varInOrderBy = false;\n\tcontext.appendparents = NULL;\n\tcontext.distrelid = distrelid;\n\tcontext.shardid = shardid;\n\n\tset_deparse_for_query(&dpns, query, parentnamespace);\n\n\tswitch (query->commandType)\n\t{\n\t\tcase CMD_SELECT:\n\t\t\t/* We set context.resultDesc only if it's a SELECT */\n\t\t\tcontext.resultDesc = resultDesc;\n\t\t\tget_select_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_UPDATE:\n\t\t\tget_update_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_INSERT:\n\t\t\tget_insert_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_DELETE:\n\t\t\tget_delete_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_MERGE:\n\t\t\tget_merge_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_NOTHING:\n\t\t\tappendStringInfoString(buf, \"NOTHING\");\n\t\t\tbreak;\n\n\t\tcase CMD_UTILITY:\n\t\t\tget_utility_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\telog(ERROR, \"unrecognized query command type: %d\",\n\t\t\t\t query->commandType);\n\t\t\tbreak;\n\t}\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n}\n\n/* ----------\n * get_values_def\t\t\t- Parse back a VALUES list\n * ----------\n */\nstatic void\nget_values_def(List *values_lists, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tfirst_list = true;\n\tListCell   *vtl;\n\n\tappendStringInfoString(buf, \"VALUES \");\n\n\tforeach(vtl, values_lists)\n\t{\n\t\tList\t   *sublist = (List *) lfirst(vtl);\n\t\tbool\t\tfirst_col = true;\n\t\tListCell   *lc;\n\n\t\tif (first_list)\n\t\t\tfirst_list = false;\n\t\telse\n\t\t\tappendStringInfoString(buf, \", \");\n\n\t\tappendStringInfoChar(buf, '(');\n\t\tforeach(lc, sublist)\n\t\t{\n\t\t\tNode\t   *col = (Node *) lfirst(lc);\n\n\t\t\tif (first_col)\n\t\t\t\tfirst_col = false;\n\t\t\telse\n\t\t\t\tappendStringInfoChar(buf, ',');\n\n\t\t\t/*\n\t\t\t * Print the value.  Whole-row Vars need special treatment.\n\t\t\t */\n\t\t\tget_rule_expr_toplevel(col, context, false);\n\t\t}\n\t\tappendStringInfoChar(buf, ')');\n\t}\n}\n\n/* ----------\n * get_with_clause\t\t\t- Parse back a WITH clause\n * ----------\n */\nstatic void\nget_with_clause(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *sep;\n\tListCell   *l;\n\n\tif (query->cteList == NIL)\n\t\treturn;\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t\tappendStringInfoChar(buf, ' ');\n\t}\n\n\tif (query->hasRecursive)\n\t\tsep = \"WITH RECURSIVE \";\n\telse\n\t\tsep = \"WITH \";\n\tforeach(l, query->cteList)\n\t{\n\t\tCommonTableExpr *cte = (CommonTableExpr *) lfirst(l);\n\n\t\tappendStringInfoString(buf, sep);\n\t\tappendStringInfoString(buf, quote_identifier(cte->ctename));\n\t\tif (cte->aliascolnames)\n\t\t{\n\t\t\tbool\t\tfirst = true;\n\t\t\tListCell   *col;\n\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tforeach(col, cte->aliascolnames)\n\t\t\t{\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(strVal(lfirst(col))));\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\tappendStringInfoString(buf, \" AS \");\n\t\tswitch (cte->ctematerialized)\n\t\t{\n\t\t\tcase CTEMaterializeDefault:\n\t\t\t\tbreak;\n\t\t\tcase CTEMaterializeAlways:\n\t\t\t\tappendStringInfoString(buf, \"MATERIALIZED \");\n\t\t\t\tbreak;\n\t\t\tcase CTEMaterializeNever:\n\t\t\t\tappendStringInfoString(buf, \"NOT MATERIALIZED \");\n\t\t\t\tbreak;\n\t\t}\n\t\tappendStringInfoChar(buf, '(');\n\t\tif (PRETTY_INDENT(context))\n\t\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\t\tget_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,\n\t\t              true,\n\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t  context->indentLevel);\n\t\tif (PRETTY_INDENT(context))\n\t\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\t\tappendStringInfoChar(buf, ')');\n\n\t\tif (cte->search_clause)\n\t\t{\n\t\t\tbool\t\tfirst = true;\n\t\t\tListCell   *lc;\n\n\t\t\tappendStringInfo(buf, \" SEARCH %s FIRST BY \",\n\t\t\t\t\t\t\t cte->search_clause->search_breadth_first ? \"BREADTH\" : \"DEPTH\");\n\n\t\t\tforeach(lc, cte->search_clause->search_col_list)\n\t\t\t{\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(strVal(lfirst(lc))));\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" SET %s\", quote_identifier(cte->search_clause->search_seq_column));\n\t\t}\n\n\t\tif (cte->cycle_clause)\n\t\t{\n\t\t\tbool\t\tfirst = true;\n\t\t\tListCell   *lc;\n\n\t\t\tappendStringInfoString(buf, \" CYCLE \");\n\n\t\t\tforeach(lc, cte->cycle_clause->cycle_col_list)\n\t\t\t{\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(strVal(lfirst(lc))));\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" SET %s\", quote_identifier(cte->cycle_clause->cycle_mark_column));\n\n\t\t\t{\n\t\t\t\tConst\t   *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);\n\t\t\t\tConst\t   *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);\n\n\t\t\t\tif (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&\n\t\t\t\t\t  cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(buf, \" TO \");\n\t\t\t\t\tget_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);\n\t\t\t\t\tappendStringInfoString(buf, \" DEFAULT \");\n\t\t\t\t\tget_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" USING %s\", quote_identifier(cte->cycle_clause->cycle_path_column));\n\t\t}\n\n\t\tsep = \", \";\n\t}\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel -= PRETTYINDENT_STD;\n\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\t}\n\telse\n\t\tappendStringInfoChar(buf, ' ');\n}\n\n/* ----------\n * get_select_query_def\t\t\t- Parse back a SELECT parsetree\n * ----------\n */\nstatic void\nget_select_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tforce_colno;\n\tListCell   *l;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/* Subroutines may need to consult the SELECT targetlist and windowClause */\n\tcontext->targetList = query->targetList;\n\tcontext->windowClause = query->windowClause;\n\n\t/*\n\t * If the Query node has a setOperations tree, then it's the top level of\n\t * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT\n\t * fields are interesting in the top query itself.\n\t */\n\tif (query->setOperations)\n\t{\n\t\tget_setop_query(query->setOperations, query, context);\n\t\t/* ORDER BY clauses must be simple in this case */\n\t\tforce_colno = true;\n\t}\n\telse\n\t{\n\t\tget_basic_select_query(query, context);\n\t\tforce_colno = false;\n\t}\n\n\t/* Add the ORDER BY clause if given */\n\tif (query->sortClause != NIL)\n\t{\n\t\tappendContextKeyword(context, \" ORDER BY \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_orderby(query->sortClause, query->targetList,\n\t\t\t\t\t\t force_colno, context);\n\t}\n\n\t/*\n\t * Add the LIMIT/OFFSET clauses if given. If non-default options, use the\n\t * standard spelling of LIMIT.\n\t */\n\tif (query->limitOffset != NULL)\n\t{\n\t\tappendContextKeyword(context, \" OFFSET \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\tget_rule_expr(query->limitOffset, context, false);\n\t}\n\tif (query->limitCount != NULL)\n\t{\n\t\tif (query->limitOption == LIMIT_OPTION_WITH_TIES)\n\t\t{\n\t\t\t// had to add '(' and ')' here because it fails with casting\n\t\t\tappendContextKeyword(context, \" FETCH FIRST (\",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\tget_rule_expr(query->limitCount, context, false);\n\t\t\tappendStringInfoString(buf, \") ROWS WITH TIES\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendContextKeyword(context, \" LIMIT \",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\tif (IsA(query->limitCount, Const) &&\n\t\t\t\t((Const *) query->limitCount)->constisnull)\n\t\t\t\tappendStringInfoString(buf, \"ALL\");\n\t\t\telse\n\t\t\t\tget_rule_expr(query->limitCount, context, false);\n\t\t}\n\t}\n\n\t/* Add FOR [KEY] UPDATE/SHARE clauses if present */\n\tif (query->hasForUpdate)\n\t{\n\t\tforeach(l, query->rowMarks)\n\t\t{\n\t\t\tRowMarkClause *rc = (RowMarkClause *) lfirst(l);\n\n\t\t\t/* don't print implicit clauses */\n\t\t\tif (rc->pushedDown)\n\t\t\t\tcontinue;\n\n\t\t\tswitch (rc->strength)\n\t\t\t{\n\t\t\t\tcase LCS_NONE:\n\t\t\t\t\t/* we intentionally throw an error for LCS_NONE */\n\t\t\t\t\telog(ERROR, \"unrecognized LockClauseStrength %d\",\n\t\t\t\t\t\t (int) rc->strength);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORKEYSHARE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR KEY SHARE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORSHARE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR SHARE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORNOKEYUPDATE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR NO KEY UPDATE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORUPDATE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR UPDATE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" OF %s\",\n\t\t\t\t\t\t\t quote_identifier(get_rtable_name(rc->rti,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  context)));\n\t\t\tif (rc->waitPolicy == LockWaitError)\n\t\t\t\tappendStringInfoString(buf, \" NOWAIT\");\n\t\t\telse if (rc->waitPolicy == LockWaitSkip)\n\t\t\t\tappendStringInfoString(buf, \" SKIP LOCKED\");\n\t\t}\n\t}\n}\n\n/*\n * Detect whether query looks like SELECT ... FROM VALUES();\n * if so, return the VALUES RTE.  Otherwise return NULL.\n */\nstatic RangeTblEntry *\nget_simple_values_rte(Query *query, TupleDesc resultDesc)\n{\n\tRangeTblEntry *result = NULL;\n\tListCell   *lc;\n\tint colno;\n\n\t/*\n\t * We want to return true even if the Query also contains OLD or NEW rule\n\t * RTEs.  So the idea is to scan the rtable and see if there is only one\n\t * inFromCl RTE that is a VALUES RTE.\n\t */\n\tforeach(lc, query->rtable)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n\n\t\tif (rte->rtekind == RTE_VALUES && rte->inFromCl)\n\t\t{\n\t\t\tif (result)\n\t\t\t\treturn NULL;\t/* multiple VALUES (probably not possible) */\n\t\t\tresult = rte;\n\t\t}\n\t\telse if (rte->rtekind == RTE_RELATION && !rte->inFromCl)\n\t\t\tcontinue;\t\t\t/* ignore rule entries */\n\t\telse\n\t\t\treturn NULL;\t\t/* something else -> not simple VALUES */\n\t}\n\n\t/*\n\t * We don't need to check the targetlist in any great detail, because\n\t * parser/analyze.c will never generate a \"bare\" VALUES RTE --- they only\n\t * appear inside auto-generated sub-queries with very restricted\n\t * structure.  However, DefineView might have modified the tlist by\n\t * injecting new column aliases; so compare tlist resnames against the\n\t * RTE's names to detect that.\n\t */\n\tif (result)\n\t{\n\t\tListCell   *lcn;\n\n\t\tif (list_length(query->targetList) != list_length(result->eref->colnames))\n\t\t\treturn NULL;\t\t/* this probably cannot happen */\n\t\tcolno = 0;\n\t\tforboth(lc, query->targetList, lcn, result->eref->colnames)\n\t\t{\n\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(lc);\n\t\t\tchar\t   *cname = strVal(lfirst(lcn));\n\t\t\tchar\t   *colname;\n\n\t\t\tif (tle->resjunk)\n\t\t\t\treturn NULL;\t/* this probably cannot happen */\n\t\t\t/* compute name that get_target_list would use for column */\n\t\t\tcolno++;\n\t\t\tif (resultDesc && colno <= resultDesc->natts)\n\t\t\t\tcolname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);\n\t\t\telse\n\t\t\t\tcolname = tle->resname;\n\n\t\t\t/* does it match the VALUES RTE? */\n\t\t\tif (colname == NULL || strcmp(colname, cname) != 0)\n\t\t\t\treturn NULL;\t/* column name has been changed */\n\t\t}\n\t}\n\n\treturn result;\n}\n\nstatic void\nget_basic_select_query(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *values_rte;\n\tchar\t   *sep;\n\tListCell   *l;\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t\tappendStringInfoChar(buf, ' ');\n\t}\n\n\t/*\n\t * If the query looks like SELECT * FROM (VALUES ...), then print just the\n\t * VALUES part.  This reverses what transformValuesClause() did at parse\n\t * time.\n\t */\n\tvalues_rte = get_simple_values_rte(query, context->resultDesc);\n\tif (values_rte)\n\t{\n\t\tget_values_def(values_rte->values_lists, context);\n\t\treturn;\n\t}\n\n\t/*\n\t * Build up the query string - first we say SELECT\n\t */\n\tif (query->isReturn)\n\t\tappendStringInfoString(buf, \"RETURN\");\n\telse\n\t\tappendStringInfoString(buf, \"SELECT\");\n\n\t/* Add the DISTINCT clause if given */\n\tif (query->distinctClause != NIL)\n\t{\n\t\tif (query->hasDistinctOn)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DISTINCT ON (\");\n\t\t\tsep = \"\";\n\t\t\tforeach(l, query->distinctClause)\n\t\t\t{\n\t\t\t\tSortGroupClause *srt = (SortGroupClause *) lfirst(l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,\n\t\t\t\t\t\t\t\t\t\t false, context);\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse\n\t\t\tappendStringInfoString(buf, \" DISTINCT\");\n\t}\n\n\t/* Then we tell what to select (the targetlist) */\n\tget_target_list(query->targetList, context);\n\n\t/* Add the FROM clause if needed */\n\tget_from_clause(query, \" FROM \", context);\n\n\t/* Add the WHERE clause if given */\n\tif (query->jointree->quals != NULL)\n\t{\n\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_expr(query->jointree->quals, context, false);\n\t}\n\n\t/* Add the GROUP BY clause if given */\n\tif (query->groupClause != NULL || query->groupingSets != NULL)\n\t{\n\t\tbool\t\tsave_ingroupby;\n\n\t\tappendContextKeyword(context, \" GROUP BY \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tif (query->groupDistinct)\n\t\t\tappendStringInfoString(buf, \"DISTINCT \");\n\n\t\tsave_ingroupby = context->inGroupBy;\n\t\tcontext->inGroupBy = true;\n\n\t\tif (query->groupingSets == NIL)\n\t\t{\n\t\t\tsep = \"\";\n\t\t\tforeach(l, query->groupClause)\n\t\t\t{\n\t\t\t\tSortGroupClause *grp = (SortGroupClause *) lfirst(l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,\n\t\t\t\t\t\t\t\t\t\t false, context);\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsep = \"\";\n\t\t\tforeach(l, query->groupingSets)\n\t\t\t{\n\t\t\t\tGroupingSet *grp = lfirst(l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_groupingset(grp, query->targetList, true, context);\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t}\n\n\t\tcontext->inGroupBy = save_ingroupby;\n\t}\n\n\t/* Add the HAVING clause if given */\n\tif (query->havingQual != NULL)\n\t{\n\t\tappendContextKeyword(context, \" HAVING \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\tget_rule_expr(query->havingQual, context, false);\n\t}\n\n\t/* Add the WINDOW clause if needed */\n\tif (query->windowClause != NIL)\n\t\tget_rule_windowclause(query, context);\n}\n\n/* ----------\n * get_target_list\t\t\t- Parse back a SELECT target list\n *\n * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.\n * ----------\n */\nstatic void\nget_target_list(List *targetList, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tStringInfoData targetbuf;\n\tbool\t\tlast_was_multiline = false;\n\tchar\t   *sep;\n\tint\t\t\tcolno;\n\tListCell   *l;\n\n\t/* we use targetbuf to hold each TLE's text temporarily */\n\tinitStringInfo(&targetbuf);\n\n\tsep = \" \";\n\tcolno = 0;\n\tforeach(l, targetList)\n\t{\n\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\t\tchar\t   *colname;\n\t\tchar\t   *attname;\n\n\t\tif (tle->resjunk)\n\t\t\tcontinue;\t\t\t/* ignore junk entries */\n\n\t\tappendStringInfoString(buf, sep);\n\t\tsep = \", \";\n\t\tcolno++;\n\n\t\t/*\n\t\t * Put the new field text into targetbuf so we can decide after we've\n\t\t * got it whether or not it needs to go on a new line.\n\t\t */\n\t\tresetStringInfo(&targetbuf);\n\t\tcontext->buf = &targetbuf;\n\n\t\t/*\n\t\t * We special-case Var nodes rather than using get_rule_expr. This is\n\t\t * needed because get_rule_expr will display a whole-row Var as\n\t\t * \"foo.*\", which is the preferred notation in most contexts, but at\n\t\t * the top level of a SELECT list it's not right (the parser will\n\t\t * expand that notation into multiple columns, yielding behavior\n\t\t * different from a whole-row Var).  We need to call get_variable\n\t\t * directly so that we can tell it to do the right thing, and so that\n\t\t * we can get the attribute name which is the default AS label.\n\t\t */\n\t\tif (tle->expr && (IsA(tle->expr, Var)))\n\t\t{\n\t\t\tattname = get_variable((Var *) tle->expr, 0, true, context);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tget_rule_expr((Node *) tle->expr, context, true);\n\n\t\t\t/*\n\t\t\t * When colNamesVisible is true, we should always show the\n\t\t\t * assigned column name explicitly.  Otherwise, show it only if\n\t\t\t * it's not FigureColname's fallback.\n\t\t\t */\n\t\t\tattname = context->colNamesVisible ? NULL : \"?column?\";\n\t\t}\n\n\t\t/*\n\t\t * Figure out what the result column should be called.  In the context\n\t\t * of a view, use the view's tuple descriptor (so as to pick up the\n\t\t * effects of any column RENAME that's been done on the view).\n\t\t * Otherwise, just use what we can find in the TLE.\n\t\t */\n\t\tif (context->resultDesc && colno <= context->resultDesc->natts)\n\t\t\tcolname = NameStr(TupleDescAttr(context->resultDesc,\n\t\t\t\t\t\t\t\t\t\t\tcolno - 1)->attname);\n\t\telse\n\t\t\tcolname = tle->resname;\n\n\t\t/* Show AS unless the column's name is correct as-is */\n\t\tif (colname)\t\t\t/* resname could be NULL */\n\t\t{\n\t\t\tif (attname == NULL || strcmp(attname, colname) != 0)\n\t\t\t\tappendStringInfo(&targetbuf, \" AS %s\", quote_identifier(colname));\n\t\t}\n\n\t\t/* Restore context's output buffer */\n\t\tcontext->buf = buf;\n\n\t\t/* Consider line-wrapping if enabled */\n\t\tif (PRETTY_INDENT(context) && context->wrapColumn >= 0)\n\t\t{\n\t\t\tint\t\t\tleading_nl_pos;\n\n\t\t\t/* Does the new field start with a new line? */\n\t\t\tif (targetbuf.len > 0 && targetbuf.data[0] == '\\n')\n\t\t\t\tleading_nl_pos = 0;\n\t\t\telse\n\t\t\t\tleading_nl_pos = -1;\n\n\t\t\t/* If so, we shouldn't add anything */\n\t\t\tif (leading_nl_pos >= 0)\n\t\t\t{\n\t\t\t\t/* instead, remove any trailing spaces currently in buf */\n\t\t\t\tremoveStringInfoSpaces(buf);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar\t   *trailing_nl;\n\n\t\t\t\t/* Locate the start of the current line in the output buffer */\n\t\t\t\ttrailing_nl = strrchr(buf->data, '\\n');\n\t\t\t\tif (trailing_nl == NULL)\n\t\t\t\t\ttrailing_nl = buf->data;\n\t\t\t\telse\n\t\t\t\t\ttrailing_nl++;\n\n\t\t\t\t/*\n\t\t\t\t * Add a newline, plus some indentation, if the new field is\n\t\t\t\t * not the first and either the new field would cause an\n\t\t\t\t * overflow or the last field used more than one line.\n\t\t\t\t */\n\t\t\t\tif (colno > 1 &&\n\t\t\t\t\t((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||\n\t\t\t\t\t last_was_multiline))\n\t\t\t\t\tappendContextKeyword(context, \"\", -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD, PRETTYINDENT_VAR);\n\t\t\t}\n\n\t\t\t/* Remember this field's multiline status for next iteration */\n\t\t\tlast_was_multiline =\n\t\t\t\t(strchr(targetbuf.data + leading_nl_pos + 1, '\\n') != NULL);\n\t\t}\n\n\t\t/* Add the new field */\n\t\tappendStringInfoString(buf, targetbuf.data);\n\t}\n\n\t/* clean up */\n\tpfree(targetbuf.data);\n}\n\nstatic void\nget_setop_query(Node *setOp, Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tneed_paren;\n\n\t/* Guard against excessively long or deeply-nested queries */\n\tCHECK_FOR_INTERRUPTS();\n\tcheck_stack_depth();\n\n\tif (IsA(setOp, RangeTblRef))\n\t{\n\t\tRangeTblRef *rtr = (RangeTblRef *) setOp;\n\t\tRangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);\n\t\tQuery\t   *subquery = rte->subquery;\n\n\t\tAssert(subquery != NULL);\n\t\tAssert(subquery->setOperations == NULL);\n\t\t/* Need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y */\n\t\tneed_paren = (subquery->cteList ||\n\t\t\t\t\t  subquery->sortClause ||\n\t\t\t\t\t  subquery->rowMarks ||\n\t\t\t\t\t  subquery->limitOffset ||\n\t\t\t\t\t  subquery->limitCount);\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_query_def(subquery, buf, context->namespaces,\n\t\t\t\t\t  context->resultDesc, context->colNamesVisible,\n\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t  context->indentLevel);\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(buf, ')');\n\t}\n\telse if (IsA(setOp, SetOperationStmt))\n\t{\n\t\tSetOperationStmt *op = (SetOperationStmt *) setOp;\n\t\tint\t\t\tsubindent;\n\t\tbool\t\tsave_colnamesvisible;\n\n\t\t/*\n\t\t * We force parens when nesting two SetOperationStmts, except when the\n\t\t * lefthand input is another setop of the same kind.  Syntactically,\n\t\t * we could omit parens in rather more cases, but it seems best to use\n\t\t * parens to flag cases where the setop operator changes.  If we use\n\t\t * parens, we also increase the indentation level for the child query.\n\t\t *\n\t\t * There are some cases in which parens are needed around a leaf query\n\t\t * too, but those are more easily handled at the next level down (see\n\t\t * code above).\n\t\t */\n\t\tif (IsA(op->larg, SetOperationStmt))\n\t\t{\n\t\t\tSetOperationStmt *lop = (SetOperationStmt *) op->larg;\n\n\t\t\tif (op->op == lop->op && op->all == lop->all)\n\t\t\t\tneed_paren = false;\n\t\t\telse\n\t\t\t\tneed_paren = true;\n\t\t}\n\t\telse\n\t\t\tneed_paren = false;\n\n\t\tif (need_paren)\n\t\t{\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tsubindent = PRETTYINDENT_STD;\n\t\t\tappendContextKeyword(context, \"\", subindent, 0, 0);\n\t\t}\n\t\telse\n\t\t\tsubindent = 0;\n\n\t\tget_setop_query(op->larg, query, context);\n\n\t\tif (need_paren)\n\t\t\tappendContextKeyword(context, \") \", -subindent, 0, 0);\n\t\telse if (PRETTY_INDENT(context))\n\t\t\tappendContextKeyword(context, \"\", -subindent, 0, 0);\n\t\telse\n\t\t\tappendStringInfoChar(buf, ' ');\n\n\t\tswitch (op->op)\n\t\t{\n\t\t\tcase SETOP_UNION:\n\t\t\t\tappendStringInfoString(buf, \"UNION \");\n\t\t\t\tbreak;\n\t\t\tcase SETOP_INTERSECT:\n\t\t\t\tappendStringInfoString(buf, \"INTERSECT \");\n\t\t\t\tbreak;\n\t\t\tcase SETOP_EXCEPT:\n\t\t\t\tappendStringInfoString(buf, \"EXCEPT \");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\telog(ERROR, \"unrecognized set op: %d\",\n\t\t\t\t\t (int) op->op);\n\t\t}\n\t\tif (op->all)\n\t\t\tappendStringInfoString(buf, \"ALL \");\n\n\t\t/* Always parenthesize if RHS is another setop */\n\t\tneed_paren = IsA(op->rarg, SetOperationStmt);\n\n\t\t/*\n\t\t * The indentation code here is deliberately a bit different from that\n\t\t * for the lefthand input, because we want the line breaks in\n\t\t * different places.\n\t\t */\n\t\tif (need_paren)\n\t\t{\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tsubindent = PRETTYINDENT_STD;\n\t\t}\n\t\telse\n\t\t\tsubindent = 0;\n\t\tappendContextKeyword(context, \"\", subindent, 0, 0);\n\n\t\t/*\n\t\t * The output column names of the RHS sub-select don't matter.\n\t\t */\n\t\tsave_colnamesvisible = context->colNamesVisible;\n\t\tcontext->colNamesVisible = false;\n\t\tget_setop_query(op->rarg, query, context);\n\t\tcontext->colNamesVisible = save_colnamesvisible;\n\n\t\tif (PRETTY_INDENT(context))\n\t\t\tcontext->indentLevel -= subindent;\n\t\tif (need_paren)\n\t\t\tappendContextKeyword(context, \")\", 0, 0, 0);\n\t}\n\telse\n\t{\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(setOp));\n\t}\n}\n\n/*\n * Display a sort/group clause.\n *\n * Also returns the expression tree, so caller need not find it again.\n */\nstatic Node *\nget_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,\n\t\t\t\t\t\t deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tTargetEntry *tle;\n\tNode\t   *expr;\n\n\ttle = get_sortgroupref_tle(ref, tlist);\n\texpr = (Node *) tle->expr;\n\n\t/*\n\t * Use column-number form if requested by caller.  Otherwise, if\n\t * expression is a constant, force it to be dumped with an explicit cast\n\t * as decoration --- this is because a simple integer constant is\n\t * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if\n\t * we dump it without any decoration.  Similarly, if it's just a Var,\n\t * there is risk of misinterpretation if the column name is reassigned in\n\t * the SELECT list, so we may need to force table qualification.  And, if\n\t * it's anything more complex than a simple Var, then force extra parens\n\t * around it, to ensure it can't be misinterpreted as a cube() or rollup()\n\t * construct.\n\t */\n\tif (force_colno)\n\t{\n\t\tAssert(!tle->resjunk);\n\t\tappendStringInfo(buf, \"%d\", tle->resno);\n\t}\n\telse if (!expr)\n\t\t /* do nothing, probably can't happen */ ;\n\telse if (IsA(expr, Const))\n\t\tget_const_expr((Const *) expr, context, 1);\n\telse if (IsA(expr, Var))\n\t{\n\t\t/* Tell get_variable to check for name conflict */\n\t\tbool\t\tsave_varinorderby = context->varInOrderBy;\n\t\tcontext->varInOrderBy = true;\n\t\t(void) get_variable((Var *) expr, 0, false, context);\n\t\tcontext->varInOrderBy = save_varinorderby;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * We must force parens for function-like expressions even if\n\t\t * PRETTY_PAREN is off, since those are the ones in danger of\n\t\t * misparsing. For other expressions we need to force them only if\n\t\t * PRETTY_PAREN is on, since otherwise the expression will output them\n\t\t * itself. (We can't skip the parens.)\n\t\t */\n\t\tbool\t\tneed_paren = (PRETTY_PAREN(context)\n\t\t\t\t\t\t\t\t  || IsA(expr, FuncExpr)\n\t\t\t\t\t\t\t\t  || IsA(expr, Aggref)\n\t\t\t\t\t\t\t\t  || IsA(expr, WindowFunc)\n\t\t\t\t\t\t\t\t  || IsA(expr, JsonConstructorExpr));\n\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, '(');\n\t\tget_rule_expr(expr, context, true);\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, ')');\n\t}\n\n\treturn expr;\n}\n\n/*\n * Display a GroupingSet\n */\nstatic void\nget_rule_groupingset(GroupingSet *gset, List *targetlist,\n\t\t\t\t\t bool omit_parens, deparse_context *context)\n{\n\tListCell   *l;\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tomit_child_parens = true;\n\tchar\t   *sep = \"\";\n\n\tswitch (gset->kind)\n\t{\n\t\tcase GROUPING_SET_EMPTY:\n\t\t\tappendStringInfoString(buf, \"()\");\n\t\t\treturn;\n\n\t\tcase GROUPING_SET_SIMPLE:\n\t\t\t{\n\t\t\t\tif (!omit_parens || list_length(gset->content) != 1)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\n\t\t\t\tforeach(l, gset->content)\n\t\t\t\t{\n\t\t\t\t\tIndex\t\tref = lfirst_int(l);\n\n\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\tget_rule_sortgroupclause(ref, targetlist,\n\t\t\t\t\t\t\t\t\t\t\t false, context);\n\t\t\t\t\tsep = \", \";\n\t\t\t\t}\n\n\t\t\t\tif (!omit_parens || list_length(gset->content) != 1)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\treturn;\n\n\t\tcase GROUPING_SET_ROLLUP:\n\t\t\tappendStringInfoString(buf, \"ROLLUP(\");\n\t\t\tbreak;\n\t\tcase GROUPING_SET_CUBE:\n\t\t\tappendStringInfoString(buf, \"CUBE(\");\n\t\t\tbreak;\n\t\tcase GROUPING_SET_SETS:\n\t\t\tappendStringInfoString(buf, \"GROUPING SETS (\");\n\t\t\tomit_child_parens = false;\n\t\t\tbreak;\n\t}\n\n\tforeach(l, gset->content)\n\t{\n\t\tappendStringInfoString(buf, sep);\n\t\tget_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);\n\t\tsep = \", \";\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * Display an ORDER BY list.\n */\nstatic void\nget_rule_orderby(List *orderList, List *targetList,\n\t\t\t\t bool force_colno, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *sep;\n\tListCell   *l;\n\n\tsep = \"\";\n\tforeach(l, orderList)\n\t{\n\t\tSortGroupClause *srt = (SortGroupClause *) lfirst(l);\n\t\tNode\t   *sortexpr;\n\t\tOid\t\t\tsortcoltype;\n\t\tTypeCacheEntry *typentry;\n\n\t\tappendStringInfoString(buf, sep);\n\t\tsortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,\n\t\t\t\t\t\t\t\t\t\t\tforce_colno, context);\n\t\tsortcoltype = exprType(sortexpr);\n\t\t/* See whether operator is default < or > for datatype */\n\t\ttypentry = lookup_type_cache(sortcoltype,\n\t\t\t\t\t\t\t\t\t TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);\n\t\tif (srt->sortop == typentry->lt_opr)\n\t\t{\n\t\t\t/* ASC is default, so emit nothing for it */\n\t\t\tif (srt->nulls_first)\n\t\t\t\tappendStringInfoString(buf, \" NULLS FIRST\");\n\t\t}\n\t\telse if (srt->sortop == typentry->gt_opr)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DESC\");\n\t\t\t/* DESC defaults to NULLS FIRST */\n\t\t\tif (!srt->nulls_first)\n\t\t\t\tappendStringInfoString(buf, \" NULLS LAST\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(buf, \" USING %s\",\n\t\t\t\t\t\t\t generate_operator_name(srt->sortop,\n\t\t\t\t\t\t\t\t\t\t\t\t\tsortcoltype,\n\t\t\t\t\t\t\t\t\t\t\t\t\tsortcoltype));\n\t\t\t/* be specific to eliminate ambiguity */\n\t\t\tif (srt->nulls_first)\n\t\t\t\tappendStringInfoString(buf, \" NULLS FIRST\");\n\t\t\telse\n\t\t\t\tappendStringInfoString(buf, \" NULLS LAST\");\n\t\t}\n\t\tsep = \", \";\n\t}\n}\n\n/*\n * Display a WINDOW clause.\n *\n * Note that the windowClause list might contain only anonymous window\n * specifications, in which case we should print nothing here.\n */\nstatic void\nget_rule_windowclause(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *sep;\n\tListCell   *l;\n\n\tsep = NULL;\n\tforeach(l, query->windowClause)\n\t{\n\t\tWindowClause *wc = (WindowClause *) lfirst(l);\n\n\t\tif (wc->name == NULL)\n\t\t\tcontinue;\t\t\t/* ignore anonymous windows */\n\n\t\tif (sep == NULL)\n\t\t\tappendContextKeyword(context, \" WINDOW \",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\telse\n\t\t\tappendStringInfoString(buf, sep);\n\n\t\tappendStringInfo(buf, \"%s AS \", quote_identifier(wc->name));\n\n\t\tget_rule_windowspec(wc, query->targetList, context);\n\n\t\tsep = \", \";\n\t}\n}\n\n/*\n * Display a window definition\n */\nstatic void\nget_rule_windowspec(WindowClause *wc, List *targetList,\n\t\t\t\t\tdeparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tneedspace = false;\n\tconst char *sep;\n\tListCell   *l;\n\n\tappendStringInfoChar(buf, '(');\n\tif (wc->refname)\n\t{\n\t\tappendStringInfoString(buf, quote_identifier(wc->refname));\n\t\tneedspace = true;\n\t}\n\t/* partition clauses are always inherited, so only print if no refname */\n\tif (wc->partitionClause && !wc->refname)\n\t{\n\t\tif (needspace)\n\t\t\tappendStringInfoChar(buf, ' ');\n\t\tappendStringInfoString(buf, \"PARTITION BY \");\n\t\tsep = \"\";\n\t\tforeach(l, wc->partitionClause)\n\t\t{\n\t\t\tSortGroupClause *grp = (SortGroupClause *) lfirst(l);\n\n\t\t\tappendStringInfoString(buf, sep);\n\t\t\tget_rule_sortgroupclause(grp->tleSortGroupRef, targetList,\n\t\t\t\t\t\t\t\t\t false, context);\n\t\t\tsep = \", \";\n\t\t}\n\t\tneedspace = true;\n\t}\n\t/* print ordering clause only if not inherited */\n\tif (wc->orderClause && !wc->copiedOrder)\n\t{\n\t\tif (needspace)\n\t\t\tappendStringInfoChar(buf, ' ');\n\t\tappendStringInfoString(buf, \"ORDER BY \");\n\t\tget_rule_orderby(wc->orderClause, targetList, false, context);\n\t\tneedspace = true;\n\t}\n\t/* framing clause is never inherited, so print unless it's default */\n\tif (wc->frameOptions & FRAMEOPTION_NONDEFAULT)\n\t{\n\t\tif (needspace)\n\t\t\tappendStringInfoChar(buf, ' ');\n\t\tif (wc->frameOptions & FRAMEOPTION_RANGE)\n\t\t\tappendStringInfoString(buf, \"RANGE \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_ROWS)\n\t\t\tappendStringInfoString(buf, \"ROWS \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_GROUPS)\n\t\t\tappendStringInfoString(buf, \"GROUPS \");\n\t\telse\n\t\t\tAssert(false);\n\t\tif (wc->frameOptions & FRAMEOPTION_BETWEEN)\n\t\t\tappendStringInfoString(buf, \"BETWEEN \");\n\t\tif (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)\n\t\t\tappendStringInfoString(buf, \"UNBOUNDED PRECEDING \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW)\n\t\t\tappendStringInfoString(buf, \"CURRENT ROW \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_START_OFFSET)\n\t\t{\n\t\t\tget_rule_expr(wc->startOffset, context, false);\n\t\t\tif (wc->frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)\n\t\t\t\tappendStringInfoString(buf, \" PRECEDING \");\n\t\t\telse if (wc->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)\n\t\t\t\tappendStringInfoString(buf, \" FOLLOWING \");\n\t\t\telse\n\t\t\t\tAssert(false);\n\t\t}\n\t\telse\n\t\t\tAssert(false);\n\t\tif (wc->frameOptions & FRAMEOPTION_BETWEEN)\n\t\t{\n\t\t\tappendStringInfoString(buf, \"AND \");\n\t\t\tif (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)\n\t\t\t\tappendStringInfoString(buf, \"UNBOUNDED FOLLOWING \");\n\t\t\telse if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW)\n\t\t\t\tappendStringInfoString(buf, \"CURRENT ROW \");\n\t\t\telse if (wc->frameOptions & FRAMEOPTION_END_OFFSET)\n\t\t\t{\n\t\t\t\tget_rule_expr(wc->endOffset, context, false);\n\t\t\t\tif (wc->frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)\n\t\t\t\t\tappendStringInfoString(buf, \" PRECEDING \");\n\t\t\t\telse if (wc->frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)\n\t\t\t\t\tappendStringInfoString(buf, \" FOLLOWING \");\n\t\t\t\telse\n\t\t\t\t\tAssert(false);\n\t\t\t}\n\t\t\telse\n\t\t\t\tAssert(false);\n\t\t}\n\t\tif (wc->frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)\n\t\t\tappendStringInfoString(buf, \"EXCLUDE CURRENT ROW \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_EXCLUDE_GROUP)\n\t\t\tappendStringInfoString(buf, \"EXCLUDE GROUP \");\n\t\telse if (wc->frameOptions & FRAMEOPTION_EXCLUDE_TIES)\n\t\t\tappendStringInfoString(buf, \"EXCLUDE TIES \");\n\t\t/* we will now have a trailing space; remove it */\n\t\tbuf->len--;\n\t}\n\tappendStringInfoChar(buf, ')');\n}\n\n/* ----------\n * get_insert_query_def\t\t\t- Parse back an INSERT parsetree\n * ----------\n */\nstatic void\nget_insert_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *select_rte = NULL;\n\tRangeTblEntry *values_rte = NULL;\n\tRangeTblEntry *rte;\n\tListCell   *l;\n\tList\t   *strippedexprs = NIL;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * If it's an INSERT ... SELECT or multi-row VALUES, there will be a\n\t * single RTE for the SELECT or VALUES.  Plain VALUES has neither.\n\t */\n\tforeach(l, query->rtable)\n\t{\n\t\trte = (RangeTblEntry *) lfirst(l);\n\n\t\tif (rte->rtekind == RTE_SUBQUERY)\n\t\t{\n\t\t\tif (select_rte)\n\t\t\t\telog(ERROR, \"too many subquery RTEs in INSERT\");\n\t\t\tselect_rte = rte;\n\t\t}\n\n\t\tif (rte->rtekind == RTE_VALUES)\n\t\t{\n\t\t\tif (values_rte)\n\t\t\t\telog(ERROR, \"too many values RTEs in INSERT\");\n\t\t\tvalues_rte = rte;\n\t\t}\n\t}\n\tif (select_rte && values_rte)\n\t\telog(ERROR, \"both subquery and values RTEs in INSERT\");\n\n\t/*\n\t * Start the query with INSERT INTO relname\n\t */\n\trte = rt_fetch(query->resultRelation, query->rtable);\n\tAssert(rte->rtekind == RTE_RELATION);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t\tappendStringInfoChar(buf, ' ');\n\t}\n\tappendStringInfo(buf, \"INSERT INTO %s\",\n\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL));\n\n\t/* Print the relation alias, if needed; INSERT requires explicit AS */\n\tget_rte_alias(rte, query->resultRelation, true, context);\n\n\t/* always want a space here */\n\tappendStringInfoChar(buf, ' ');\n\n\t/*\n\t * Add the insert-column-names list.  Any indirection decoration needed on\n\t * the column names can be inferred from the top targetlist.\n\t */\n\tif (query->targetList)\n\t{\n\t\tstrippedexprs = get_insert_column_names_list(query->targetList,\n\t\t\t\t\t\t\t\tbuf, context, rte);\n\t}\n\n\tif (query->override)\n\t{\n\t\tif (query->override == OVERRIDING_SYSTEM_VALUE)\n\t\t\tappendStringInfoString(buf, \"OVERRIDING SYSTEM VALUE \");\n\t\telse if (query->override == OVERRIDING_USER_VALUE)\n\t\t\tappendStringInfoString(buf, \"OVERRIDING USER VALUE \");\n\t}\n\n\tif (select_rte)\n\t{\n\t\t/* Add the SELECT */\n\t\tget_query_def(select_rte->subquery, buf, context->namespaces, NULL,\n\t\t              false,\n\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t  context->indentLevel);\n\t}\n\telse if (values_rte)\n\t{\n\t\t/* Add the multi-VALUES expression lists */\n\t\tget_values_def(values_rte->values_lists, context);\n\t}\n\telse if (strippedexprs)\n\t{\n\t\t/* Add the single-VALUES expression list */\n\t\tappendContextKeyword(context, \"VALUES (\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\t\tget_rule_list_toplevel(strippedexprs, context, false);\n\t\tappendStringInfoChar(buf, ')');\n\t}\n\telse\n\t{\n\t\t/* No expressions, so it must be DEFAULT VALUES */\n\t\tappendStringInfoString(buf, \"DEFAULT VALUES\");\n\t}\n\n\t/* Add ON CONFLICT if present */\n\tif (query->onConflict)\n\t{\n\t\tOnConflictExpr *confl = query->onConflict;\n\n\t\tappendStringInfoString(buf, \" ON CONFLICT\");\n\n\t\tif (confl->arbiterElems)\n\t\t{\n\t\t\t/* Add the single-VALUES expression list */\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr((Node *) confl->arbiterElems, context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t/* Add a WHERE clause (for partial indexes) if given */\n\t\t\tif (confl->arbiterWhere != NULL)\n\t\t\t{\n\t\t\t\tbool\t\tsave_varprefix;\n\n\t\t\t\t/*\n\t\t\t\t * Force non-prefixing of Vars, since parser assumes that they\n\t\t\t\t * belong to target relation.  WHERE clause does not use\n\t\t\t\t * InferenceElem, so this is separately required.\n\t\t\t\t */\n\t\t\t\tsave_varprefix = context->varprefix;\n\t\t\t\tcontext->varprefix = false;\n\n\t\t\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\t\t\tget_rule_expr(confl->arbiterWhere, context, false);\n\n\t\t\t\tcontext->varprefix = save_varprefix;\n\t\t\t}\n\t\t}\n\t\telse if (OidIsValid(confl->constraint))\n\t\t{\n\t\t\tchar\t   *constraint = get_constraint_name(confl->constraint);\n\t\t\tint64 shardId = context->shardid;\n\n\t\t\tif (shardId > 0)\n\t\t\t{\n\t\t\t\tAppendShardIdToName(&constraint, shardId);\n\t\t\t}\n\n\t\t\tif (!constraint)\n\t\t\t\telog(ERROR, \"cache lookup failed for constraint %u\",\n\t\t\t\t\t confl->constraint);\n\t\t\tappendStringInfo(buf, \" ON CONSTRAINT %s\",\n\t\t\t\t\t\t\t quote_identifier(constraint));\n\t\t}\n\n\t\tif (confl->action == ONCONFLICT_NOTHING)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DO NOTHING\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DO UPDATE SET \");\n\t\t\t/* Deparse targetlist */\n\t\t\tget_update_query_targetlist_def(query, confl->onConflictSet,\n\t\t\t\t\t\t\t\t\t\t\tcontext, rte);\n\n\t\t\t/* Add a WHERE clause if given */\n\t\t\tif (confl->onConflictWhere != NULL)\n\t\t\t{\n\t\t\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\t\t\tget_rule_expr(confl->onConflictWhere, context, false);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Add RETURNING if present */\n\tif (query->returningList)\n\t{\n\t\tappendContextKeyword(context, \" RETURNING\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_target_list(query->returningList, context);\n\t}\n}\n\n/* ----------\n * get_update_query_def\t\t\t- Parse back an UPDATE parsetree\n * ----------\n */\nstatic void\nget_update_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * Start the query with UPDATE relname SET\n\t */\n\trte = rt_fetch(query->resultRelation, query->rtable);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tappendStringInfoChar(buf, ' ');\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t}\n\n\t/* if it's a shard, do differently */\n\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\tchar *fragmentSchemaName = NULL;\n\t\tchar *fragmentTableName = NULL;\n\n\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t/* use schema and table name from the remote alias */\n\t\tappendStringInfo(buf, \"UPDATE %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName, fragmentTableName));\n\n\t\tif(rte->eref != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\tquote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\telse\n\t{\n\t\tappendStringInfo(buf, \"UPDATE %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL));\n\n\t\t/* Print the relation alias, if needed */\n\t\tget_rte_alias(rte, query->resultRelation, false, context);\n\t}\n\n\tappendStringInfoString(buf, \" SET \");\n\n\t/* Deparse targetlist */\n\tget_update_query_targetlist_def(query, query->targetList, context, rte);\n\n\t/* Add the FROM clause if needed */\n\tget_from_clause(query, \" FROM \", context);\n\n\t/* Add a WHERE clause if given */\n\tif (query->jointree->quals != NULL)\n\t{\n\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_expr(query->jointree->quals, context, false);\n\t}\n\n\t/* Add RETURNING if present */\n\tif (query->returningList)\n\t{\n\t\tappendContextKeyword(context, \" RETURNING\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_target_list(query->returningList, context);\n\t}\n}\n\n/* ----------\n * get_update_query_targetlist_def\t\t\t- Parse back an UPDATE targetlist\n * ----------\n */\nstatic void\nget_update_query_targetlist_def(Query *query, List *targetList,\n\t\t\t\t\t\t\t\tdeparse_context *context, RangeTblEntry *rte)\n{\n\tStringInfo\tbuf = context->buf;\n\tListCell   *l;\n\tListCell   *next_ma_cell;\n\tint\t\t\tremaining_ma_columns;\n\tconst char *sep;\n\tSubLink    *cur_ma_sublink;\n\tList\t   *ma_sublinks;\n\n\ttargetList = ExpandMergedSubscriptingRefEntries(targetList);\n\n\t/*\n\t * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks\n\t * into a list.  We expect them to appear, in ID order, in resjunk tlist\n\t * entries.\n\t */\n\tma_sublinks = NIL;\n\tif (query->hasSubLinks)\t\t/* else there can't be any */\n\t{\n\t\tforeach(l, targetList)\n\t\t{\n\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\n\t\t\tif (tle->resjunk && IsA(tle->expr, SubLink))\n\t\t\t{\n\t\t\t\tSubLink    *sl = (SubLink *) tle->expr;\n\n\t\t\t\tif (sl->subLinkType == MULTIEXPR_SUBLINK)\n\t\t\t\t{\n\t\t\t\t\tma_sublinks = lappend(ma_sublinks, sl);\n\t\t\t\t\tAssert(sl->subLinkId == list_length(ma_sublinks));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tensure_update_targetlist_in_param_order(targetList);\n\t}\n\tnext_ma_cell = list_head(ma_sublinks);\n\tcur_ma_sublink = NULL;\n\tremaining_ma_columns = 0;\n\n\t/* Add the comma separated list of 'attname = value' */\n\tsep = \"\";\n\tforeach(l, targetList)\n\t{\n\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\t\tNode\t   *expr;\n\n\t\tif (tle->resjunk)\n\t\t\tcontinue;\t\t\t/* ignore junk entries */\n\n\t\t/* Emit separator (OK whether we're in multiassignment or not) */\n\t\tappendStringInfoString(buf, sep);\n\t\tsep = \", \";\n\n\t\t/*\n\t\t * Check to see if we're starting a multiassignment group: if so,\n\t\t * output a left paren.\n\t\t */\n\t\tif (next_ma_cell != NULL && cur_ma_sublink == NULL)\n\t\t{\n\t\t\t/*\n\t\t\t * We must dig down into the expr to see if it's a PARAM_MULTIEXPR\n\t\t\t * Param.  That could be buried under FieldStores and\n\t\t\t * SubscriptingRefs and CoerceToDomains (cf processIndirection()),\n\t\t\t * and underneath those there could be an implicit type coercion.\n\t\t\t * Because we would ignore implicit type coercions anyway, we\n\t\t\t * don't need to be as careful as processIndirection() is about\n\t\t\t * descending past implicit CoerceToDomains.\n\t\t\t */\n\t\t\texpr = (Node *) tle->expr;\n\t\t\twhile (expr)\n\t\t\t{\n\t\t\t\tif (IsA(expr, FieldStore))\n\t\t\t\t{\n\t\t\t\t\tFieldStore *fstore = (FieldStore *) expr;\n\n\t\t\t\t\texpr = (Node *) linitial(fstore->newvals);\n\t\t\t\t}\n\t\t\t\telse if (IsA(expr, SubscriptingRef))\n\t\t\t\t{\n\t\t\t\t\tSubscriptingRef *sbsref = (SubscriptingRef *) expr;\n\n\t\t\t\t\tif (sbsref->refassgnexpr == NULL)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\texpr = (Node *) sbsref->refassgnexpr;\n\t\t\t\t}\n\t\t\t\telse if (IsA(expr, CoerceToDomain))\n\t\t\t\t{\n\t\t\t\t\tCoerceToDomain *cdomain = (CoerceToDomain *) expr;\n\n\t\t\t\t\tif (cdomain->coercionformat != COERCE_IMPLICIT_CAST)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\texpr = (Node *) cdomain->arg;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\texpr = strip_implicit_coercions(expr);\n\n\t\t\tif (expr && IsA(expr, Param) &&\n\t\t\t\t((Param *) expr)->paramkind == PARAM_MULTIEXPR)\n\t\t\t{\n\t\t\t\tcur_ma_sublink = (SubLink *) lfirst(next_ma_cell);\n\t\t\t\tnext_ma_cell = lnext(ma_sublinks, next_ma_cell);\n\t\t\t\tremaining_ma_columns = count_nonjunk_tlist_entries(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ((Query *) cur_ma_sublink->subselect)->targetList);\n\t\t\t\tAssert(((Param *) expr)->paramid ==\n\t\t\t\t\t   ((cur_ma_sublink->subLinkId << 16) | 1));\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Put out name of target column; look in the catalogs, not at\n\t\t * tle->resname, since resname will fail to track RENAME.\n\t\t */\n\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t   quote_identifier(get_attname(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttle->resno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse)));\n\n\t\t/*\n\t\t * Print any indirection needed (subfields or subscripts), and strip\n\t\t * off the top-level nodes representing the indirection assignments.\n\t\t */\n\t\texpr = processIndirection((Node *) tle->expr, context);\n\n\t\t/*\n\t\t * If we're in a multiassignment, skip printing anything more, unless\n\t\t * this is the last column; in which case, what we print should be the\n\t\t * sublink, not the Param.\n\t\t */\n\t\tif (cur_ma_sublink != NULL)\n\t\t{\n\t\t\tif (--remaining_ma_columns > 0)\n\t\t\t\tcontinue;\t\t/* not the last column of multiassignment */\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\texpr = (Node *) cur_ma_sublink;\n\t\t\tcur_ma_sublink = NULL;\n\t\t}\n\n\t\tappendStringInfoString(buf, \" = \");\n\n\t\tget_rule_expr(expr, context, false);\n\t}\n}\n\n/* ----------\n * get_delete_query_def\t\t\t- Parse back a DELETE parsetree\n * ----------\n */\nstatic void\nget_delete_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * Start the query with DELETE FROM relname\n\t */\n\trte = rt_fetch(query->resultRelation, query->rtable);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tappendStringInfoChar(buf, ' ');\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t}\n\n\t/* if it's a shard, do differently */\n\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\tchar *fragmentSchemaName = NULL;\n\t\tchar *fragmentTableName = NULL;\n\n\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t/* use schema and table name from the remote alias */\n\t\tappendStringInfo(buf, \"DELETE FROM %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName, fragmentTableName));\n\n\t\tif(rte->eref != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\tquote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\telse\n\t{\n\t\tappendStringInfo(buf, \"DELETE FROM %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL));\n\n\t\t/* Print the relation alias, if needed */\n\t\tget_rte_alias(rte, query->resultRelation, false, context);\n\t}\n\n\t/* Add the USING clause if given */\n\tget_from_clause(query, \" USING \", context);\n\n\t/* Add a WHERE clause if given */\n\tif (query->jointree->quals != NULL)\n\t{\n\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_expr(query->jointree->quals, context, false);\n\t}\n\n\t/* Add RETURNING if present */\n\tif (query->returningList)\n\t{\n\t\tappendContextKeyword(context, \" RETURNING\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_target_list(query->returningList, context);\n\t}\n}\n\n\n/* ----------\n * get_merge_query_def\t\t\t\t- Parse back a MERGE parsetree\n * ----------\n */\nstatic void\nget_merge_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\tListCell   *lc;\n\tbool\thaveNotMatchedBySource;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * Start the query with MERGE INTO relname\n\t */\n\trte = ExtractResultRelationRTE(query);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tappendStringInfoChar(buf, ' ');\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t}\n\n\t/* if it's a shard, do differently */\n\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\tchar *fragmentSchemaName = NULL;\n\t\tchar *fragmentTableName = NULL;\n\n\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t/* use schema and table name from the remote alias */\n\t\tappendStringInfo(buf, \"MERGE INTO %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName, fragmentTableName));\n\n\t\tif(rte->eref != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\tquote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\telse\n\t{\n\t\tappendStringInfo(buf, \"MERGE INTO %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\tcontext->distrelid,\n\t\t\t\t\t\t\t\t\t\tcontext->shardid, NIL));\n\n\t\tif (rte->alias != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\t\t\t quote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\n\t/* Print the source relation and join clause */\n\tget_from_clause(query, \" USING \", context);\n\tappendContextKeyword(context, \" ON \",\n\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\tget_rule_expr(query->mergeJoinCondition, context, false);\n\n\t/*\n\t * Test for any NOT MATCHED BY SOURCE actions.  If there are none, then\n\t * any NOT MATCHED BY TARGET actions are output as \"WHEN NOT MATCHED\", per\n\t * SQL standard.  Otherwise, we have a non-SQL-standard query, so output\n\t * \"BY SOURCE\" / \"BY TARGET\" qualifiers for all NOT MATCHED actions, to be\n\t * more explicit.\n\t */\n\thaveNotMatchedBySource = false;\n\tforeach(lc, query->mergeActionList)\n\t{\n\t\tMergeAction *action = lfirst_node(MergeAction, lc);\n\n\t\tif (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)\n\t\t{\n\t\t\thaveNotMatchedBySource = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* Print each merge action */\n\tforeach(lc, query->mergeActionList)\n\t{\n\t\tMergeAction *action = lfirst_node(MergeAction, lc);\n\n\t\tappendContextKeyword(context, \" WHEN \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\t\tswitch (action->matchKind)\n\t\t{\n\t\t\tcase MERGE_WHEN_MATCHED:\n\t\t\t\tappendStringInfoString(buf, \"MATCHED\");\n\t\t\t\tbreak;\n\t\t\tcase MERGE_WHEN_NOT_MATCHED_BY_SOURCE:\n\t\t\t\tappendStringInfoString(buf, \"NOT MATCHED BY SOURCE\");\n\t\t\t\tbreak;\n\t\t\tcase MERGE_WHEN_NOT_MATCHED_BY_TARGET:\n\t\t\t\tif (haveNotMatchedBySource)\n\t\t\t\t\tappendStringInfoString(buf, \"NOT MATCHED BY TARGET\");\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \"NOT MATCHED\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\telog(ERROR, \"unrecognized matchKind: %d\",\n\t\t\t\t\t (int) action->matchKind);\n\t\t}\n\n\t\tif (action->qual)\n\t\t{\n\t\t\tappendContextKeyword(context, \" AND \",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);\n\t\t\tget_rule_expr(action->qual, context, false);\n\t\t}\n\t\tappendContextKeyword(context, \" THEN \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);\n\n\t\tif (action->commandType == CMD_INSERT)\n\t\t{\n\t\t\t/* This generally matches get_insert_query_def() */\n\t\t\tList\t   *strippedexprs = NIL;\n\t\t\tconst char *sep = \"\";\n\t\t\tListCell   *lc2;\n\n\t\t\tappendStringInfoString(buf, \"INSERT\");\n\n\t\t\tif (action->targetList)\n\t\t\t\tappendStringInfoString(buf, \" (\");\n\t\t\tforeach(lc2, action->targetList)\n\t\t\t{\n\t\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(lc2);\n\n\t\t\t\tAssert(!tle->resjunk);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tsep = \", \";\n\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(get_attname(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttle->resno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse)));\n\t\t\t\tstrippedexprs = lappend(strippedexprs,\n\t\t\t\t\t\t\t\t\t\tprocessIndirection((Node *) tle->expr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   context));\n\t\t\t}\n\t\t\tif (action->targetList)\n\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\tif (action->override)\n\t\t\t{\n\t\t\t\tif (action->override == OVERRIDING_SYSTEM_VALUE)\n\t\t\t\t\tappendStringInfoString(buf, \" OVERRIDING SYSTEM VALUE\");\n\t\t\t\telse if (action->override == OVERRIDING_USER_VALUE)\n\t\t\t\t\tappendStringInfoString(buf, \" OVERRIDING USER VALUE\");\n\t\t\t}\n\n\t\t\tif (strippedexprs)\n\t\t\t{\n\t\t\t\tappendContextKeyword(context, \" VALUES (\",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 4);\n\t\t\t\tget_rule_list_toplevel(strippedexprs, context, false);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\telse\n\t\t\t\tappendStringInfoString(buf, \" DEFAULT VALUES\");\n\t\t}\n\t\telse if (action->commandType == CMD_UPDATE)\n\t\t{\n\t\t\tappendStringInfoString(buf, \"UPDATE SET \");\n\t\t\tget_update_query_targetlist_def(query, action->targetList,\n\t\t\t\t\t\t\t\t\t\t\tcontext, rte);\n\t\t}\n\t\telse if (action->commandType == CMD_DELETE)\n\t\t\tappendStringInfoString(buf, \"DELETE\");\n\t\telse if (action->commandType == CMD_NOTHING)\n\t\t\tappendStringInfoString(buf, \"DO NOTHING\");\n\t}\n\n\t/* Add RETURNING if present */\n\tif (query->returningList)\n\t{\n\t\tappendContextKeyword(context, \" RETURNING\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_target_list(query->returningList, context);\n\t}\n\n\tereport(DEBUG1, (errmsg(\"<Deparsed MERGE query: %s>\", buf->data)));\n}\n\n\n/* ----------\n * get_utility_query_def\t\t\t- Parse back a UTILITY parsetree\n * ----------\n */\nstatic void\nget_utility_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))\n\t{\n\t\tNotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;\n\n\t\tappendContextKeyword(context, \"\",\n\t\t\t\t\t\t\t 0, PRETTYINDENT_STD, 1);\n\t\tappendStringInfo(buf, \"NOTIFY %s\",\n\t\t\t\t\t\t quote_identifier(stmt->conditionname));\n\t\tif (stmt->payload)\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tsimple_quote_literal(buf, stmt->payload);\n\t\t}\n\t}\n\telse if (query->utilityStmt && IsA(query->utilityStmt, TruncateStmt))\n\t{\n\t\tTruncateStmt *stmt = (TruncateStmt *) query->utilityStmt;\n\t\tList *relationList = stmt->relations;\n\t\tListCell *relationCell = NULL;\n\n\t\tappendContextKeyword(context, \"\",\n\t\t\t\t\t\t\t 0, PRETTYINDENT_STD, 1);\n\n\t\tappendStringInfo(buf, \"TRUNCATE TABLE\");\n\n\t\tforeach(relationCell, relationList)\n\t\t{\n\t\t\tRangeVar *relationVar = (RangeVar *) lfirst(relationCell);\n\t\t\tOid relationId = RangeVarGetRelid(relationVar, NoLock, false);\n\t\t\tchar *relationName = generate_relation_or_shard_name(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL);\n\t\t\tappendStringInfo(buf, \" %s\", relationName);\n\n\t\t\tif (lnext(relationList, relationCell) != NULL)\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \",\");\n\t\t\t}\n\t\t}\n\n\t\tif (stmt->restart_seqs)\n\t\t{\n\t\t\tappendStringInfo(buf, \" RESTART IDENTITY\");\n\t\t}\n\n\t\tif (stmt->behavior == DROP_CASCADE)\n\t\t{\n\t\t\tappendStringInfo(buf, \" CASCADE\");\n\t\t}\n\t}\n\telse\n\t{\n\t\t/* Currently only NOTIFY utility commands can appear in rules */\n\t\telog(ERROR, \"unexpected utility statement type\");\n\t}\n}\n\n/*\n * Display a Var appropriately.\n *\n * In some cases (currently only when recursing into an unnamed join)\n * the Var's varlevelsup has to be interpreted with respect to a context\n * above the current one; levelsup indicates the offset.\n *\n * If istoplevel is true, the Var is at the top level of a SELECT's\n * targetlist, which means we need special treatment of whole-row Vars.\n * Instead of the normal \"tab.*\", we'll print \"tab.*::typename\", which is a\n * dirty hack to prevent \"tab.*\" from being expanded into multiple columns.\n * (The parser will strip the useless coercion, so no inefficiency is added in\n * dump and reload.)  We used to print just \"tab\" in such cases, but that is\n * ambiguous and will yield the wrong result if \"tab\" is also a plain column\n * name in the query.\n *\n * Returns the attname of the Var, or NULL if the Var has no attname (because\n * it is a whole-row Var or a subplan output reference).\n */\nstatic char *\nget_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\tAttrNumber\tattnum;\n\tint\t\tvarno;\n\tAttrNumber\tvarattno;\n\tint\t\t\tnetlevelsup;\n\tdeparse_namespace *dpns;\n\tdeparse_columns *colinfo;\n\tchar\t   *refname;\n\tchar\t   *attname;\n\tbool\t\tneed_prefix;\n\n\t/* Find appropriate nesting depth */\n\tnetlevelsup = var->varlevelsup + levelsup;\n\tif (netlevelsup >= list_length(context->namespaces))\n\t\telog(ERROR, \"bogus varlevelsup: %d offset %d\",\n\t\t\t var->varlevelsup, levelsup);\n\tdpns = (deparse_namespace *) list_nth(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t  netlevelsup);\n\n\tvarno = var->varno;\n\tvarattno = var->varattno;\n\n\n\tif (var->varnosyn > 0 && var->varnosyn <= list_length(dpns->rtable) && dpns->plan == NULL) {\n\t\trte = rt_fetch(var->varnosyn, dpns->rtable);\n\n\t\t/*\n\t\t * if the rte var->varnosyn points to is not a regular table and it is a join\n\t\t * then the correct relname will be found with var->varnosyn and var->varattnosyn\n\t\t */\n\t\tif (rte->rtekind == RTE_JOIN && rte->relid == 0 && var->varnosyn != var->varno) {\n\t\t\tvarno = var->varnosyn;\n\t\t\tvarattno = var->varattnosyn;\n\t\t}\n\t}\n\n\t/*\n\t * Try to find the relevant RTE in this rtable.  In a plan tree, it's\n\t * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig\n\t * down into the subplans, or INDEX_VAR, which is resolved similarly. Also\n\t * find the aliases previously assigned for this RTE.\n\t */\n\tif (varno >= 1 && varno <= list_length(dpns->rtable))\n\t{\n\n\t\t/*\n\t\t * We might have been asked to map child Vars to some parent relation.\n\t\t */\n\t\tif (context->appendparents && dpns->appendrels)\n\t\t{\n\n\t\t\tint\t\tpvarno = varno;\n\t\t\tAttrNumber\tpvarattno = varattno;\n\t\t\tAppendRelInfo *appinfo = dpns->appendrels[pvarno];\n\t\t\tbool\t\tfound = false;\n\n\t\t\t/* Only map up to inheritance parents, not UNION ALL appendrels */\n\t\t\twhile (appinfo &&\n\t\t\t\t   rt_fetch(appinfo->parent_relid,\n\t\t\t\t\t\t\tdpns->rtable)->rtekind == RTE_RELATION)\n\t\t\t{\n\t\t\t\tfound = false;\n\t\t\t\tif (pvarattno > 0)\t/* system columns stay as-is */\n\t\t\t\t{\n\t\t\t\t\tif (pvarattno > appinfo->num_child_cols)\n\t\t\t\t\t\tbreak;\t/* safety check */\n\t\t\t\t\tpvarattno = appinfo->parent_colnos[pvarattno - 1];\n\t\t\t\t\tif (pvarattno == 0)\n\t\t\t\t\t\tbreak;\t/* Var is local to child */\n\t\t\t\t}\n\n\t\t\t\tpvarno = appinfo->parent_relid;\n\t\t\t\tfound = true;\n\n\t\t\t\t/* If the parent is itself a child, continue up. */\n\t\t\t\tAssert(pvarno > 0 && pvarno <= list_length(dpns->rtable));\n\t\t\t\tappinfo = dpns->appendrels[pvarno];\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If we found an ancestral rel, and that rel is included in\n\t\t\t * appendparents, print that column not the original one.\n\t\t\t */\n\t\t\tif (found && bms_is_member(pvarno, context->appendparents))\n\t\t\t{\n\t\t\t\tvarno = pvarno;\n\t\t\t\tvarattno = pvarattno;\n\t\t\t}\n\t\t}\n\n\t\trte = rt_fetch(varno, dpns->rtable);\n\t\trefname = (char *) list_nth(dpns->rtable_names, varno - 1);\n\t\tcolinfo = deparse_columns_fetch(varno, dpns);\n\t\tattnum = varattno;\n\t}\n\telse\n\t{\n\t\tresolve_special_varno((Node *) var, context, get_special_variable,\n\t\t\t\t\t\t\t  NULL);\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * The planner will sometimes emit Vars referencing resjunk elements of a\n\t * subquery's target list (this is currently only possible if it chooses\n\t * to generate a \"physical tlist\" for a SubqueryScan or CteScan node).\n\t * Although we prefer to print subquery-referencing Vars using the\n\t * subquery's alias, that's not possible for resjunk items since they have\n\t * no alias.  So in that case, drill down to the subplan and print the\n\t * contents of the referenced tlist item.  This works because in a plan\n\t * tree, such Vars can only occur in a SubqueryScan or CteScan node, and\n\t * we'll have set dpns->inner_plan to reference the child plan node.\n\t */\n\tif ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&\n\t\tattnum > list_length(rte->eref->colnames) &&\n\t\tdpns->inner_plan)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\n\t\ttle = get_tle_by_resno(dpns->inner_tlist, attnum);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"invalid attnum %d for relation \\\"%s\\\"\",\n\t\t\t\t attnum, rte->eref->aliasname);\n\n\t\tAssert(netlevelsup == 0);\n\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\t/*\n\t\t * Force parentheses because our caller probably assumed a Var is a\n\t\t * simple expression.\n\t\t */\n\t\tif (!IsA(tle->expr, Var))\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_rule_expr((Node *) tle->expr, context, true);\n\t\tif (!IsA(tle->expr, Var))\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * If it's an unnamed join, look at the expansion of the alias variable.\n\t * If it's a simple reference to one of the input vars, then recursively\n\t * print the name of that var instead.  When it's not a simple reference,\n\t * we have to just print the unqualified join column name.  (This can only\n\t * happen with \"dangerous\" merged columns in a JOIN USING; we took pains\n\t * previously to make the unqualified column name unique in such cases.)\n\t *\n\t * This wouldn't work in decompiling plan trees, because we don't store\n\t * joinaliasvars lists after planning; but a plan tree should never\n\t * contain a join alias variable.\n\t */\n\tif (rte->rtekind == RTE_JOIN && rte->alias == NULL)\n\t{\n\t\tif (rte->joinaliasvars == NIL)\n\t\t\telog(ERROR, \"cannot decompile join alias var in plan tree\");\n\t\tif (attnum > 0)\n\t\t{\n\t\t\tVar\t\t   *aliasvar;\n\n\t\t\taliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);\n\t\t\t/* we intentionally don't strip implicit coercions here */\n\t\t\tif (aliasvar && IsA(aliasvar, Var))\n\t\t\t{\n\t\t\t\treturn get_variable(aliasvar, var->varlevelsup + levelsup,\n\t\t\t\t\t\t\t\t\tistoplevel, context);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Unnamed join has no refname.  (Note: since it's unnamed, there is\n\t\t * no way the user could have referenced it to create a whole-row Var\n\t\t * for it.  So we don't have to cover that case below.)\n\t\t */\n\t\tAssert(refname == NULL);\n\t}\n\n\tif (attnum == InvalidAttrNumber)\n\t\tattname = NULL;\n\telse if (attnum > 0)\n\t{\n\t\t/* Get column name to use from the colinfo struct */\n\t\tif (attnum > colinfo->num_cols)\n\t\t\telog(ERROR, \"invalid attnum %d for relation \\\"%s\\\"\",\n\t\t\t\t attnum, rte->eref->aliasname);\n\t\tattname = colinfo->colnames[attnum - 1];\n\n\t\t/*\n\t\t * If we find a Var referencing a dropped column, it seems better to\n\t\t * print something (anything) than to fail.  In general this should\n\t\t * not happen, but it used to be possible for some cases involving\n\t\t * functions returning named composite types, and perhaps there are\n\t\t * still bugs out there.\n\t\t */\n\t\tif (attname == NULL)\n\t\t\tattname = \"?dropped?column?\";\n\t}\n\telse if (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\t/* System column on a Citus shard */\n\t\tattname = get_attname(rte->relid, attnum, false);\n\t}\n\telse\n\t{\n\t\t/* System column - name is fixed, get it from the catalog */\n\t\tattname = get_rte_attribute_name(rte, attnum);\n\t}\n\n\tneed_prefix = (context->varprefix || attname == NULL);\n\t/*\n\t * If we're considering a plain Var in an ORDER BY (but not GROUP BY)\n\t * clause, we may need to add a table-name prefix to prevent\n\t * findTargetlistEntrySQL92 from misinterpreting the name as an\n\t * output-column name.  To avoid cluttering the output with unnecessary\n\t * prefixes, do so only if there is a name match to a SELECT tlist item\n\t * that is different from the Var.\n\t */\n\tif (context->varInOrderBy && !context->inGroupBy && !need_prefix)\n\t{\n\t\tint\t\t\tcolno = 0;\n\t\tforeach_node(TargetEntry, tle, context->targetList)\n\t\t{\n\t\t\tchar\t   *colname;\n\t\t\tif (tle->resjunk)\n\t\t\t\tcontinue;\t\t/* ignore junk entries */\n\t\t\tcolno++;\n\t\t\t/* This must match colname-choosing logic in get_target_list() */\n\t\t\tif (context->resultDesc && colno <= context->resultDesc->natts)\n\t\t\t\tcolname = NameStr(TupleDescAttr(context->resultDesc,\n\t\t\t\t\t\t\t\t\t\t\t\tcolno - 1)->attname);\n\t\t\telse\n\t\t\t\tcolname = tle->resname;\n\t\t\tif (colname && strcmp(colname, attname) == 0 &&\n\t\t\t\t!equal(var, tle->expr))\n\t\t\t{\n\t\t\t\tneed_prefix = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (refname && need_prefix)\n\t{\n\t\tappendStringInfoString(buf, quote_identifier(refname));\n\t\tappendStringInfoChar(buf, '.');\n\t}\n\tif (attname)\n\t\tappendStringInfoString(buf, quote_identifier(attname));\n\telse\n\t{\n\t\tappendStringInfoChar(buf, '*');\n\n\t\tif (istoplevel)\n\t\t{\n\t\t\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t\t\t{\n\t\t\t\t/* use rel.*::shard_name instead of rel.*::table_name */\n\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t generate_rte_shard_name(rte));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t format_type_with_typemod(var->vartype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  var->vartypmod));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn attname;\n}\n\n/*\n * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR.  This\n * routine is actually a callback for get_special_varno, which handles finding\n * the correct TargetEntry.  We get the expression contained in that\n * TargetEntry and just need to deparse it, a job we can throw back on\n * get_rule_expr.\n */\nstatic void\nget_special_variable(Node *node, deparse_context *context, void *callback_arg)\n{\n\tStringInfo\tbuf = context->buf;\n\n\t/*\n\t * For a non-Var referent, force parentheses because our caller probably\n\t *  assumed a Var is a simple expression.\n\t */\n\tif (!IsA(node, Var))\n\t\tappendStringInfoChar(buf, '(');\n\tget_rule_expr(node, context, true);\n\tif (!IsA(node, Var))\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,\n * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,\n * invoke the callback provided.\n */\nstatic void\nresolve_special_varno(Node *node, deparse_context *context, rsv_callback callback, void *callback_arg)\n{\n\tVar\t\t   *var;\n\tdeparse_namespace *dpns;\n\n\t/* This function is recursive, so let's be paranoid. */\n\tcheck_stack_depth();\n\n\t/* If it's not a Var, invoke the callback. */\n\tif (!IsA(node, Var))\n\t{\n\t\t(*callback) (node, context, callback_arg);\n\t\treturn;\n\t}\n\n\t/* Find appropriate nesting depth */\n\tvar = (Var *) node;\n\tdpns = (deparse_namespace *) list_nth(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t  var->varlevelsup);\n\n\t/*\n\t * It's a special RTE, so recurse.\n\t */\n\tif (var->varno == OUTER_VAR && dpns->outer_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\t\tBitmapset  *save_appendparents;\n\n\t\ttle = get_tle_by_resno(dpns->outer_tlist, var->varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for OUTER_VAR var: %d\", var->varattno);\n\n\t\t/* If we're descending to the first child of an Append or MergeAppend,\n\t\t * update appendparents.  This will affect deparsing of all Vars\n\t\t * appearing within the eventually-resolved subexpression.\n\t\t */\n\t\tsave_appendparents = context->appendparents;\n\n\t\tif (IsA(dpns->plan, Append))\n\t\t\tcontext->appendparents = bms_union(context->appendparents,\n\t\t\t\t\t\t\t\t\t\t\t   ((Append *) dpns->plan)->apprelids);\n\t\telse if (IsA(dpns->plan, MergeAppend))\n\t\t\tcontext->appendparents = bms_union(context->appendparents,\n\t\t\t\t\t\t\t\t\t\t\t   ((MergeAppend *) dpns->plan)->apprelids);\n\n\t\tpush_child_plan(dpns, dpns->outer_plan, &save_dpns);\n\t\tresolve_special_varno((Node *) tle->expr, context,\n\t\t\t\t\t\t\t  callback, callback_arg);\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\tcontext->appendparents = save_appendparents;\n\t\treturn;\n\t}\n\telse if (var->varno == INNER_VAR && dpns->inner_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\n\t\ttle = get_tle_by_resno(dpns->inner_tlist, var->varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INNER_VAR var: %d\", var->varattno);\n\n\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\t\tresolve_special_varno((Node *) tle->expr, context, callback, callback_arg);\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn;\n\t}\n\telse if (var->varno == INDEX_VAR && dpns->index_tlist)\n\t{\n\t\tTargetEntry *tle;\n\n\t\ttle = get_tle_by_resno(dpns->index_tlist, var->varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INDEX_VAR var: %d\", var->varattno);\n\n\t\tresolve_special_varno((Node *) tle->expr, context, callback, callback_arg);\n\t\treturn;\n\t}\n\telse if (var->varno < 1 || var->varno > list_length(dpns->rtable))\n\t\telog(ERROR, \"bogus varno: %d\", var->varno);\n\n\t/* Not special.  Just invoke the callback. */\n\t(*callback) (node, context, callback_arg);\n}\n\n/*\n * Get the name of a field of an expression of composite type.  The\n * expression is usually a Var, but we handle other cases too.\n *\n * levelsup is an extra offset to interpret the Var's varlevelsup correctly.\n *\n * This is fairly straightforward when the expression has a named composite\n * type; we need only look up the type in the catalogs.  However, the type\n * could also be RECORD.  Since no actual table or view column is allowed to\n * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE\n * or to a subquery output.  We drill down to find the ultimate defining\n * expression and attempt to infer the field name from it.  We ereport if we\n * can't determine the name.\n *\n * Similarly, a PARAM of type RECORD has to refer to some expression of\n * a determinable composite type.\n */\nstatic const char *\nget_name_for_var_field(Var *var, int fieldno,\n\t\t\t\t\t   int levelsup, deparse_context *context)\n{\n\tRangeTblEntry *rte;\n\tAttrNumber\tattnum;\n\tint\t\t\tnetlevelsup;\n\tdeparse_namespace *dpns;\n\tint\t\tvarno;\n\tAttrNumber\tvarattno;\n\tTupleDesc\ttupleDesc;\n\tNode\t   *expr;\n\n\t/*\n\t * If it's a RowExpr that was expanded from a whole-row Var, use the\n\t * column names attached to it.  (We could let get_expr_result_tupdesc()\n\t * handle this, but it's much cheaper to just pull out the name we need.)\n\t */\n\tif (IsA(var, RowExpr))\n\t{\n\t\tRowExpr    *r = (RowExpr *) var;\n\n\t\tif (fieldno > 0 && fieldno <= list_length(r->colnames))\n\t\t\treturn strVal(list_nth(r->colnames, fieldno - 1));\n\t}\n\n\t/*\n\t * If it's a Param of type RECORD, try to find what the Param refers to.\n\t */\n\tif (IsA(var, Param))\n\t{\n\t\tParam\t   *param = (Param *) var;\n\t\tListCell   *ancestor_cell;\n\n\t\texpr = find_param_referent(param, context, &dpns, &ancestor_cell);\n\t\tif (expr)\n\t\t{\n\t\t\t/* Found a match, so recurse to decipher the field name */\n\t\t\tdeparse_namespace save_dpns;\n\t\t\tconst char *result;\n\n\t\t\tpush_ancestor_plan(dpns, ancestor_cell, &save_dpns);\n\t\t\tresult = get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t0, context);\n\t\t\tpop_ancestor_plan(dpns, &save_dpns);\n\t\t\treturn result;\n\t\t}\n\t}\n\n\t/*\n\t * If it's a Var of type RECORD, we have to find what the Var refers to;\n\t * if not, we can use get_expr_result_tupdesc().\n\t */\n\tif (!IsA(var, Var) ||\n\t\tvar->vartype != RECORDOID)\n\t{\n\t\ttupleDesc = get_expr_result_tupdesc((Node *) var, false);\n\t\t/* Got the tupdesc, so we can extract the field name */\n\t\tAssert(fieldno >= 1 && fieldno <= tupleDesc->natts);\n\t\treturn NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);\n\t}\n\n\t/* Find appropriate nesting depth */\n\tnetlevelsup = var->varlevelsup + levelsup;\n\tif (netlevelsup >= list_length(context->namespaces))\n\t\telog(ERROR, \"bogus varlevelsup: %d offset %d\",\n\t\t\t var->varlevelsup, levelsup);\n\tdpns = (deparse_namespace *) list_nth(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t  netlevelsup);\n\n\tvarno = var->varno;\n\tvarattno = var->varattno;\n\n\tif (var->varnosyn > 0 && var->varnosyn <= list_length(dpns->rtable) && dpns->plan == NULL) {\n\t\trte = rt_fetch(var->varnosyn, dpns->rtable);\n\n\t\t/*\n\t\t * if the rte var->varnosyn points to is not a regular table and it is a join\n\t\t * then the correct relname will be found with var->varnosyn and var->varattnosyn\n\t\t */\n\t\tif (rte->rtekind == RTE_JOIN && rte->relid == 0 && var->varnosyn != var->varno) {\n\t\t\tvarno = var->varnosyn;\n\t\t\tvarattno = var->varattnosyn;\n\t\t}\n\t}\n\n\t/*\n\t * Try to find the relevant RTE in this rtable.  In a plan tree, it's\n\t * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig\n\t * down into the subplans, or INDEX_VAR, which is resolved similarly.\n\t */\n\tif (varno >= 1 && varno <= list_length(dpns->rtable))\n\t{\n\t\trte = rt_fetch(varno, dpns->rtable);\n\t\tattnum = varattno;\n\t}\n\telse if (varno == OUTER_VAR && dpns->outer_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\t\tconst char *result;\n\n\t\ttle = get_tle_by_resno(dpns->outer_tlist, varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for OUTER_VAR var: %d\", varattno);\n\n\t\tAssert(netlevelsup == 0);\n\t\tpush_child_plan(dpns, dpns->outer_plan, &save_dpns);\n\n\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn result;\n\t}\n\telse if (varno == INNER_VAR && dpns->inner_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\t\tconst char *result;\n\n\t\ttle = get_tle_by_resno(dpns->inner_tlist, varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INNER_VAR var: %d\", varattno);\n\n\t\tAssert(netlevelsup == 0);\n\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn result;\n\t}\n\telse if (varno == INDEX_VAR && dpns->index_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tconst char *result;\n\n\t\ttle = get_tle_by_resno(dpns->index_tlist, varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INDEX_VAR var: %d\", varattno);\n\n\t\tAssert(netlevelsup == 0);\n\n\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\treturn result;\n\t}\n\telse\n\t{\n\t\telog(ERROR, \"bogus varno: %d\", varno);\n\t\treturn NULL;\t\t\t/* keep compiler quiet */\n\t}\n\n\tif (attnum == InvalidAttrNumber)\n\t{\n\t\t/* Var is whole-row reference to RTE, so select the right field */\n\t\treturn get_rte_attribute_name(rte, fieldno);\n\t}\n\n\t/*\n\t * This part has essentially the same logic as the parser's\n\t * expandRecordVariable() function, but we are dealing with a different\n\t * representation of the input context, and we only need one field name\n\t * not a TupleDesc.  Also, we need special cases for finding subquery and\n\t * CTE subplans when deparsing Plan trees.\n\t */\n\texpr = (Node *) var;\t\t/* default if we can't drill down */\n\n\tswitch (rte->rtekind)\n\t{\n\t\tcase RTE_RELATION:\n\t\tcase RTE_VALUES:\n\t\tcase RTE_NAMEDTUPLESTORE:\n\t\tcase RTE_RESULT:\n\n\t\t\t/*\n\t\t\t * This case should not occur: a column of a table or values list\n\t\t\t * shouldn't have type RECORD.  Fall through and fail (most\n\t\t\t * likely) at the bottom.\n\t\t\t */\n\t\t\tbreak;\n\t\tcase RTE_SUBQUERY:\n\t\t\t/* Subselect-in-FROM: examine sub-select's output expr */\n\t\t\t{\n\t\t\t\tif (rte->subquery)\n\t\t\t\t{\n\t\t\t\t\tTargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tattnum);\n\n\t\t\t\t\tif (ste == NULL || ste->resjunk)\n\t\t\t\t\t\telog(ERROR, \"subquery %s does not have attribute %d\",\n\t\t\t\t\t\t\t rte->eref->aliasname, attnum);\n\t\t\t\t\texpr = (Node *) ste->expr;\n\t\t\t\t\tif (IsA(expr, Var))\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Recurse into the sub-select to see what its Var\n\t\t\t\t\t\t * refers to. We have to build an additional level of\n\t\t\t\t\t\t * namespace to keep in step with varlevelsup in the\n\t\t\t\t\t\t * subselect; furthermore, the subquery RTE might be\n\t\t\t\t\t\t * from an outer query level, in which case the\n\t\t\t\t\t\t * namespace for the subselect must have that outer\n\t\t\t\t\t\t * level as parent namespace.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tList\t   *save_nslist = context->namespaces;\n\t\t\t\t\t\tList\t   *parent_namespaces;\n\t\t\t\t\t\tdeparse_namespace mydpns;\n\t\t\t\t\t\tconst char *result;\n\n\t\t\t\t\t\tparent_namespaces = list_copy_tail(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   netlevelsup);\n\n\t\t\t\t\t\tset_deparse_for_query(&mydpns, rte->subquery,\n\t\t\t\t\t\t\t\t\t\t\t  parent_namespaces);\n\n\t\t\t\t\t\tcontext->namespaces = lcons(&mydpns,\n\t\t\t\t\t\t\t\t\t\t\t\t\tparent_namespaces);\n\n\t\t\t\t\t\tresult = get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t0, context);\n\n\t\t\t\t\t\tcontext->namespaces = save_nslist;\n\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\t\t\t\t\t/* else fall through to inspect the expression */\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * We're deparsing a Plan tree so we don't have complete\n\t\t\t\t\t * RTE entries (in particular, rte->subquery is NULL). But\n\t\t\t\t\t * the only place we'd normally see a Var directly\n\t\t\t\t\t * referencing a SUBQUERY RTE is in a SubqueryScan plan\n\t\t\t\t\t * node, and we can look into the child plan's tlist\n\t\t\t\t\t * instead.  An exception occurs if the subquery was\n\t\t\t\t\t * proven empty and optimized away: then we'd find such a\n\t\t\t\t\t * Var in a childless Result node, and there's nothing in\n\t\t\t\t\t * the plan tree that would let us figure out what it had\n\t\t\t\t\t * originally referenced.  In that case, fall back on\n\t\t\t\t\t * printing \"fN\", analogously to the default column names\n\t\t\t\t\t * for RowExprs.\n\t\t\t\t\t */\n\t\t\t\t\tTargetEntry *tle;\n\t\t\t\t\tdeparse_namespace save_dpns;\n\t\t\t\t\tconst char *result;\n\n\t\t\t\t\tif (!dpns->inner_plan)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar\t   *dummy_name = palloc(32);\n\t\t\t\t\t\tAssert(dpns->plan && IsA(dpns->plan, Result));\n\t\t\t\t\t\tsnprintf(dummy_name, 32, \"f%d\", fieldno);\n\t\t\t\t\t\treturn dummy_name;\n\t\t\t\t\t}\n\t\t\t\t\tAssert(dpns->plan && IsA(dpns->plan, SubqueryScan));\n\n\t\t\t\t\ttle = get_tle_by_resno(dpns->inner_tlist, attnum);\n\t\t\t\t\tif (!tle)\n\t\t\t\t\t\telog(ERROR, \"bogus varattno for subquery var: %d\",\n\t\t\t\t\t\t\t attnum);\n\t\t\t\t\tAssert(netlevelsup == 0);\n\t\t\t\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\t\t\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\t\t\t\tpop_child_plan(dpns, &save_dpns);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase RTE_JOIN:\n\t\t\t/* Join RTE --- recursively inspect the alias variable */\n\t\t\tif (rte->joinaliasvars == NIL)\n\t\t\t\telog(ERROR, \"cannot decompile join alias var in plan tree\");\n\t\t\tAssert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));\n\t\t\texpr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);\n\t\t\tAssert(expr != NULL);\n\t\t\t/* we intentionally don't strip implicit coercions here */\n\t\t\tif (IsA(expr, Var))\n\t\t\t\treturn get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t  var->varlevelsup + levelsup,\n\t\t\t\t\t\t\t\t\t\t\t  context);\n\t\t\t/* else fall through to inspect the expression */\n\t\t\tbreak;\n\t\tcase RTE_FUNCTION:\n\t\tcase RTE_TABLEFUNC:\n\n\t\t\t/*\n\t\t\t * We couldn't get here unless a function is declared with one of\n\t\t\t * its result columns as RECORD, which is not allowed.\n\t\t\t */\n\t\t\tbreak;\n\t\tcase RTE_CTE:\n\t\t\t/* CTE reference: examine subquery's output expr */\n\t\t\t{\n\t\t\t\tCommonTableExpr *cte = NULL;\n\t\t\t\tIndex\t\tctelevelsup;\n\t\t\t\tListCell   *lc;\n\n\t\t\t\t/*\n\t\t\t\t * Try to find the referenced CTE using the namespace stack.\n\t\t\t\t */\n\t\t\t\tctelevelsup = rte->ctelevelsup + netlevelsup;\n\t\t\t\tif (ctelevelsup >= list_length(context->namespaces))\n\t\t\t\t\tlc = NULL;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdeparse_namespace *ctedpns;\n\n\t\t\t\t\tctedpns = (deparse_namespace *)\n\t\t\t\t\t\tlist_nth(context->namespaces, ctelevelsup);\n\t\t\t\t\tforeach(lc, ctedpns->ctes)\n\t\t\t\t\t{\n\t\t\t\t\t\tcte = (CommonTableExpr *) lfirst(lc);\n\t\t\t\t\t\tif (strcmp(cte->ctename, rte->ctename) == 0)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (lc != NULL)\n\t\t\t\t{\n\t\t\t\t\tQuery\t   *ctequery = (Query *) cte->ctequery;\n\t\t\t\t\tTargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tattnum);\n\n\t\t\t\t\tif (ste == NULL || ste->resjunk)\n\t\t\t\t\t\telog(ERROR, \"CTE %s does not have attribute %d\",\n\t\t\t\t\t\t\t rte->eref->aliasname, attnum);\n\t\t\t\t\texpr = (Node *) ste->expr;\n\t\t\t\t\tif (IsA(expr, Var))\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Recurse into the CTE to see what its Var refers to.\n\t\t\t\t\t\t * We have to build an additional level of namespace\n\t\t\t\t\t\t * to keep in step with varlevelsup in the CTE;\n\t\t\t\t\t\t * furthermore it could be an outer CTE (compare\n\t\t\t\t\t\t * SUBQUERY case above).\n\t\t\t\t\t\t */\n\t\t\t\t\t\tList\t   *save_nslist = context->namespaces;\n\t\t\t\t\t\tList\t   *parent_namespaces;\n\t\t\t\t\t\tdeparse_namespace mydpns;\n\t\t\t\t\t\tconst char *result;\n\n\t\t\t\t\t\tparent_namespaces = list_copy_tail(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ctelevelsup);\n\n\t\t\t\t\t\tset_deparse_for_query(&mydpns, ctequery,\n\t\t\t\t\t\t\t\t\t\t\t  parent_namespaces);\n\n\t\t\t\t\t\tcontext->namespaces = lcons(&mydpns, parent_namespaces);\n\n\t\t\t\t\t\tresult = get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t0, context);\n\n\t\t\t\t\t\tcontext->namespaces = save_nslist;\n\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\t\t\t\t\t/* else fall through to inspect the expression */\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * We're deparsing a Plan tree so we don't have a CTE\n\t\t\t\t\t * list.  But the only places we'd normally see a Var\n\t\t\t\t\t * directly referencing a CTE RTE are in CteScan or\n\t\t\t\t\t * WorkTableScan plan nodes.  For those cases,\n\t\t\t\t\t * set_deparse_plan arranged for dpns->inner_plan to be\n\t\t\t\t\t * the plan node that emits the CTE or RecursiveUnion\n\t\t\t\t\t * result, and we can look at its tlist instead.  As\n\t\t\t\t\t * above, this can fail if the CTE has been proven empty,\n\t\t\t\t\t * in which case fall back to \"fN\".\n\t\t\t\t\t */\n\t\t\t\t\tTargetEntry *tle;\n\t\t\t\t\tdeparse_namespace save_dpns;\n\t\t\t\t\tconst char *result;\n\n\t\t\t\t\tif (!dpns->inner_plan)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar\t   *dummy_name = palloc(32);\n\t\t\t\t\t\tAssert(dpns->plan && IsA(dpns->plan, Result));\n\t\t\t\t\t\tsnprintf(dummy_name, 32, \"f%d\", fieldno);\n\t\t\t\t\t\treturn dummy_name;\n\t\t\t\t\t}\n\t\t\t\t\tAssert(dpns->plan && (IsA(dpns->plan, CteScan) ||\n\t\t\t\t\t\t\t\t\t\t  IsA(dpns->plan, WorkTableScan)));\n\n\t\t\t\t\ttle = get_tle_by_resno(dpns->inner_tlist, attnum);\n\t\t\t\t\tif (!tle)\n\t\t\t\t\t\telog(ERROR, \"bogus varattno for subquery var: %d\",\n\t\t\t\t\t\t\t attnum);\n\t\t\t\t\tAssert(netlevelsup == 0);\n\t\t\t\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\t\t\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\t\t\t\tpop_child_plan(dpns, &save_dpns);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t}\n\n\t/*\n\t * We now have an expression we can't expand any more, so see if\n\t * get_expr_result_tupdesc() can do anything with it.\n\t */\n\ttupleDesc = get_expr_result_tupdesc(expr, false);\n\t/* Got the tupdesc, so we can extract the field name */\n\tAssert(fieldno >= 1 && fieldno <= tupleDesc->natts);\n\treturn NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);\n}\n\n/*\n * Try to find the referenced expression for a PARAM_EXEC Param that might\n * reference a parameter supplied by an upper NestLoop or SubPlan plan node.\n *\n * If successful, return the expression and set *dpns_p and *ancestor_cell_p\n * appropriately for calling push_ancestor_plan().  If no referent can be\n * found, return NULL.\n */\nstatic Node *\nfind_param_referent(Param *param, deparse_context *context,\n\t\t\t\t\tdeparse_namespace **dpns_p, ListCell **ancestor_cell_p)\n{\n\t/* Initialize output parameters to prevent compiler warnings */\n\t*dpns_p = NULL;\n\t*ancestor_cell_p = NULL;\n\n\t/*\n\t * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or\n\t * SubPlan argument.  This will necessarily be in some ancestor of the\n\t * current expression's Plan.\n\t */\n\tif (param->paramkind == PARAM_EXEC)\n\t{\n\t\tdeparse_namespace *dpns;\n\t\tPlan  *child_plan;\n\t\tListCell   *lc;\n\n\t\tdpns = (deparse_namespace *) linitial(context->namespaces);\n\t\tchild_plan = dpns->plan;\n\n\t\tforeach(lc, dpns->ancestors)\n\t\t{\n\t\t\tNode\t   *ancestor = (Node *) lfirst(lc);\n\t\t\tListCell   *lc2;\n\n\t\t\t/*\n\t\t\t * NestLoops transmit params to their inner child only.\n\t\t\t */\n\t\t\tif (IsA(ancestor, NestLoop) &&\n\t\t\t\tchild_plan == innerPlan(ancestor))\n\t\t\t{\n\t\t\t\tNestLoop   *nl = (NestLoop *) ancestor;\n\n\t\t\t\tforeach(lc2, nl->nestParams)\n\t\t\t\t{\n\t\t\t\t\tNestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);\n\n\t\t\t\t\tif (nlp->paramno == param->paramid)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Found a match, so return it */\n\t\t\t\t\t\t*dpns_p = dpns;\n\t\t\t\t\t\t*ancestor_cell_p = lc;\n\t\t\t\t\t\treturn (Node *) nlp->paramval;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Check to see if we're crawling up from a subplan.\n\t\t\t */\n\t\t\tif(IsA(ancestor, SubPlan))\n\t\t\t{\n\t\t\t\tSubPlan    *subplan = (SubPlan *) ancestor;\n\t\t\t\tListCell   *lc3;\n\t\t\t\tListCell   *lc4;\n\n\t\t\t\t/* Matched subplan, so check its arguments */\n\t\t\t\tforboth(lc3, subplan->parParam, lc4, subplan->args)\n\t\t\t\t{\n\t\t\t\t\tint\t\t\tparamid = lfirst_int(lc3);\n\t\t\t\t\tNode\t   *arg = (Node *) lfirst(lc4);\n\n\t\t\t\t\tif (paramid == param->paramid)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Found a match, so return it.  But, since Vars in\n\t\t\t\t\t\t * the arg are to be evaluated in the surrounding\n\t\t\t\t\t\t * context, we have to point to the next ancestor item\n\t\t\t\t\t\t * that is *not* a SubPlan.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tListCell   *rest;\n\n\t\t\t\t\t\tfor_each_cell(rest, dpns->ancestors,\n\t\t\t\t\t\t\t\t\t  lnext(dpns->ancestors, lc))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tNode\t   *ancestor2 = (Node *) lfirst(rest);\n\n\t\t\t\t\t\t\tif (!IsA(ancestor2, SubPlan))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t*dpns_p = dpns;\n\t\t\t\t\t\t\t\t*ancestor_cell_p = rest;\n\t\t\t\t\t\t\t\treturn arg;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telog(ERROR, \"SubPlan cannot be outermost ancestor\");\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* SubPlan isn't a kind of Plan, so skip the rest */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * We need not consider the ancestor's initPlan list, since\n\t\t\t * initplans never have any parParams.\n\t\t\t */\n\n\t\t\t/* No luck, crawl up to next ancestor */\n\t\t\tchild_plan = (Plan *) ancestor;\n\t\t}\n\t}\n\n\t/* No referent found */\n\treturn NULL;\n}\n\n/*\n * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param.\n *\n * If successful, return the generating subplan/initplan and set *column_p\n * to the subplan's 0-based output column number.\n * Otherwise, return NULL.\n */\nstatic SubPlan *\nfind_param_generator(Param *param, deparse_context *context, int *column_p)\n{\n\t/* Initialize output parameter to prevent compiler warnings */\n\t*column_p = 0;\n\n\t/*\n\t * If it's a PARAM_EXEC parameter, search the current plan node as well as\n\t * ancestor nodes looking for a subplan or initplan that emits the value\n\t * for the Param.  It could appear in the setParams of an initplan or\n\t * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.\n\t */\n\tif (param->paramkind == PARAM_EXEC)\n\t{\n\t\tSubPlan    *result;\n\t\tdeparse_namespace *dpns;\n\t\tListCell   *lc;\n\n\t\tdpns = (deparse_namespace *) linitial(context->namespaces);\n\n\t\t/* First check the innermost plan node's initplans */\n\t\tresult = find_param_generator_initplan(param, dpns->plan, column_p);\n\t\tif (result)\n\t\t\treturn result;\n\n\t\t/*\n\t\t * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,\n\t\t * which can be referenced by Params elsewhere in the targetlist.\n\t\t * (Such Params should always be in the same targetlist, so there's no\n\t\t * need to do this work at upper plan nodes.)\n\t\t */\n\t\tforeach_node(TargetEntry, tle, dpns->plan->targetlist)\n\t\t{\n\t\t\tif (tle->expr && IsA(tle->expr, SubPlan))\n\t\t\t{\n\t\t\t\tSubPlan    *subplan = (SubPlan *) tle->expr;\n\n\t\t\t\tif (subplan->subLinkType == MULTIEXPR_SUBLINK)\n\t\t\t\t{\n\t\t\t\t\tforeach_int(paramid, subplan->setParam)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (paramid == param->paramid)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/* Found a match, so return it. */\n\t\t\t\t\t\t\t*column_p = foreach_current_index(paramid);\n\t\t\t\t\t\t\treturn subplan;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/* No luck, so check the ancestor nodes */\n\t\tforeach(lc, dpns->ancestors)\n\t\t{\n\t\t\tNode\t   *ancestor = (Node *) lfirst(lc);\n\n\t\t\t/*\n\t\t\t * If ancestor is a SubPlan, check the paramIds it provides.\n\t\t\t */\n\t\t\tif (IsA(ancestor, SubPlan))\n\t\t\t{\n\t\t\t\tSubPlan    *subplan = (SubPlan *) ancestor;\n\n\t\t\t\tforeach_int(paramid, subplan->paramIds)\n\t\t\t\t{\n\t\t\t\t\tif (paramid == param->paramid)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Found a match, so return it. */\n\t\t\t\t\t\t*column_p = foreach_current_index(paramid);\n\t\t\t\t\t\treturn subplan;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* SubPlan isn't a kind of Plan, so skip the rest */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Otherwise, it's some kind of Plan node, so check its initplans.\n\t\t\t */\n\t\t\tresult = find_param_generator_initplan(param, (Plan *) ancestor,\n\t\t\t\t\t\t\t\t\t\t\t\t   column_p);\n\t\t\tif (result)\n\t\t\t\treturn result;\n\n\t\t\t/* No luck, crawl up to next ancestor */\n\t\t}\n\t}\n\n\t/* No generator found */\n\treturn NULL;\n}\n\n/*\n * Subroutine for find_param_generator: search one Plan node's initplans\n */\nstatic SubPlan *\nfind_param_generator_initplan(Param *param, Plan *plan, int *column_p)\n{\n\tforeach_node(SubPlan, subplan, plan->initPlan)\n\t{\n\t\tforeach_int(paramid, subplan->setParam)\n\t\t{\n\t\t\tif (paramid == param->paramid)\n\t\t\t{\n\t\t\t\t/* Found a match, so return it. */\n\t\t\t\t*column_p = foreach_current_index(paramid);\n\t\t\t\treturn subplan;\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\n\n/*\n * Display a Param appropriately.\n */\nstatic void\nget_parameter(Param *param, deparse_context *context)\n{\n\tNode\t   *expr;\n\tdeparse_namespace *dpns;\n\tListCell   *ancestor_cell;\n\tSubPlan    *subplan;\n\tint\t\t\tcolumn;\n\n\t/*\n\t * If it's a PARAM_EXEC parameter, try to locate the expression from which\n\t * the parameter was computed.  This stanza handles only cases in which\n\t * the Param represents an input to the subplan we are currently in.\n\t */\n\texpr = find_param_referent(param, context, &dpns, &ancestor_cell);\n\tif (expr)\n\t{\n\t\t/* Found a match, so print it */\n\t\tdeparse_namespace save_dpns;\n\t\tbool\t\tsave_varprefix;\n\t\tbool\t\tneed_paren;\n\n\t\t/* Switch attention to the ancestor plan node */\n\t\tpush_ancestor_plan(dpns, ancestor_cell, &save_dpns);\n\n\t\t/*\n\t\t * Force prefixing of Vars, since they won't belong to the relation\n\t\t * being scanned in the original plan node.\n\t\t */\n\t\tsave_varprefix = context->varprefix;\n\t\tcontext->varprefix = true;\n\n\t\t/*\n\t\t * A Param's expansion is typically a Var, Aggref, GroupingFunc, or\n\t\t * upper-level Param, which wouldn't need extra parentheses.\n\t\t * Otherwise, insert parens to ensure the expression looks atomic.\n\t\t */\n\t\tneed_paren = !(IsA(expr, Var) ||\n\t\t\t\t\t   IsA(expr, Aggref) ||\n                       IsA(expr, GroupingFunc) ||\n\t\t\t\t\t   IsA(expr, Param));\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, '(');\n\n\t\tget_rule_expr(expr, context, false);\n\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, ')');\n\n\t\tcontext->varprefix = save_varprefix;\n\n\t\tpop_ancestor_plan(dpns, &save_dpns);\n\n\t\treturn;\n\t}\n\n\t/*\n\t * Alternatively, maybe it's a subplan output, which we print as a\n\t * reference to the subplan.  (We could drill down into the subplan and\n\t * print the relevant targetlist expression, but that has been deemed too\n\t * confusing since it would violate normal SQL scope rules.  Also, we're\n\t * relying on this reference to show that the testexpr containing the\n\t * Param has anything to do with that subplan at all.)\n\t */\n\tsubplan = find_param_generator(param, context, &column);\n\tif (subplan)\n\t{\n\t\tappendStringInfo(context->buf, \"(%s%s).col%d\",\n\t\t\t\t\t\t subplan->useHashTable ? \"hashed \" : \"\",\n\t\t\t\t\t\t subplan->plan_name, column + 1);\n\n\t\treturn;\n\t}\n\n\t/*\n\t * If it's an external parameter, see if the outermost namespace provides\n\t * function argument names.\n\t */\n\tif (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)\n\t{\n\t\tdpns = llast(context->namespaces);\n\t\tif (dpns->argnames &&\n\t\t\tparam->paramid > 0 &&\n\t\t\tparam->paramid <= dpns->numargs)\n\t\t{\n\t\t\tchar\t   *argname = dpns->argnames[param->paramid - 1];\n\n\t\t\tif (argname)\n\t\t\t{\n\t\t\t\tbool\t\tshould_qualify = false;\n\t\t\t\tListCell   *lc;\n\n\t\t\t\t/*\n\t\t\t\t * Qualify the parameter name if there are any other deparse\n\t\t\t\t * namespaces with range tables.  This avoids qualifying in\n\t\t\t\t * trivial cases like \"RETURN a + b\", but makes it safe in all\n\t\t\t\t * other cases.\n\t\t\t\t */\n\t\t\t\tforeach(lc, context->namespaces)\n\t\t\t\t{\n\t\t\t\t\tdeparse_namespace *depns = lfirst(lc);\n\n\t\t\t\t\tif (depns->rtable_names != NIL)\n\t\t\t\t\t{\n\t\t\t\t\t\tshould_qualify = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (should_qualify)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(context->buf, quote_identifier(dpns->funcname));\n\t\t\t\t\tappendStringInfoChar(context->buf, '.');\n\t\t\t\t}\n\n\t\t\t\tappendStringInfoString(context->buf, quote_identifier(argname));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * Not PARAM_EXEC, or couldn't find referent: for base types just print $N.\n\t * For composite types, add cast to the parameter to ease remote node detect\n\t * the type.\n\t *\n\t * It's a bug if we get here for anything except PARAM_EXTERN Params, but\n\t * in production builds printing $N seems more useful than failing.\n\t */\n\tAssert(param->paramkind == PARAM_EXTERN);\n\n\tif (param->paramtype >= FirstNormalObjectId)\n\t{\n\t\tchar *typeName = format_type_with_typemod(param->paramtype, param->paramtypmod);\n\n\t\tappendStringInfo(context->buf, \"$%d::%s\", param->paramid, typeName);\n\t}\n\telse\n\t{\n\t\tappendStringInfo(context->buf, \"$%d\", param->paramid);\n\t}\n}\n\n/*\n * get_simple_binary_op_name\n *\n * helper function for isSimpleNode\n * will return single char binary operator name, or NULL if it's not\n */\nstatic const char *\nget_simple_binary_op_name(OpExpr *expr)\n{\n\tList\t   *args = expr->args;\n\n\tif (list_length(args) == 2)\n\t{\n\t\t/* binary operator */\n\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\t\tconst char *op;\n\n\t\top = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));\n\t\tif (strlen(op) == 1)\n\t\t\treturn op;\n\t}\n\treturn NULL;\n}\n\n/*\n * isSimpleNode - check if given node is simple (doesn't need parenthesizing)\n *\n *\ttrue   : simple in the context of parent node's type\n *\tfalse  : not simple\n */\nstatic bool\nisSimpleNode(Node *node, Node *parentNode, int prettyFlags)\n{\n\tif (!node)\n\t\treturn false;\n\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_Var:\n\t\tcase T_Const:\n\t\tcase T_Param:\n\t\tcase T_CoerceToDomainValue:\n\t\tcase T_SetToDefault:\n\t\tcase T_CurrentOfExpr:\n\t\t\t/* single words: always simple */\n\t\t\treturn true;\n\n\t\tcase T_SubscriptingRef:\n\t\tcase T_ArrayExpr:\n\t\tcase T_RowExpr:\n\t\tcase T_CoalesceExpr:\n\t\tcase T_MinMaxExpr:\n\t\tcase T_SQLValueFunction:\n\t\tcase T_XmlExpr:\n\t\tcase T_NextValueExpr:\n\t\tcase T_NullIfExpr:\n\t\tcase T_Aggref:\n        case T_GroupingFunc:\n\t\tcase T_WindowFunc:\n\t\tcase T_MergeSupportFunc:\n\t\tcase T_FuncExpr:\n\t\tcase T_JsonConstructorExpr:\n\t\tcase T_JsonExpr:\n\t\t\t/* function-like: name(..) or name[..] */\n\t\t\treturn true;\n\n\t\t\t/* CASE keywords act as parentheses */\n\t\tcase T_CaseExpr:\n\t\t\treturn true;\n\n\t\tcase T_FieldSelect:\n\n\t\t\t/*\n\t\t\t * appears simple since . has top precedence, unless parent is\n\t\t\t * T_FieldSelect itself!\n\t\t\t */\n\t\t\treturn !IsA(parentNode, FieldSelect);\n\n\t\tcase T_FieldStore:\n\n\t\t\t/*\n\t\t\t * treat like FieldSelect (probably doesn't matter)\n\t\t\t */\n\t\t\treturn !IsA(parentNode, FieldStore);\n\n\t\tcase T_CoerceToDomain:\n\t\t\t/* maybe simple, check args */\n\t\t\treturn isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_RelabelType:\n\t\t\treturn isSimpleNode((Node *) ((RelabelType *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_CoerceViaIO:\n\t\t\treturn isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_ArrayCoerceExpr:\n\t\t\treturn isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_ConvertRowtypeExpr:\n\t\t\treturn isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\n\t\tcase T_OpExpr:\n\t\t\t{\n\t\t\t\t/* depends on parent node type; needs further checking */\n\t\t\t\tif (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))\n\t\t\t\t{\n\t\t\t\t\tconst char *op;\n\t\t\t\t\tconst char *parentOp;\n\t\t\t\t\tbool\t\tis_lopriop;\n\t\t\t\t\tbool\t\tis_hipriop;\n\t\t\t\t\tbool\t\tis_lopriparent;\n\t\t\t\t\tbool\t\tis_hipriparent;\n\n\t\t\t\t\top = get_simple_binary_op_name((OpExpr *) node);\n\t\t\t\t\tif (!op)\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\t/* We know only the basic operators + - and * / % */\n\t\t\t\t\tis_lopriop = (strchr(\"+-\", *op) != NULL);\n\t\t\t\t\tis_hipriop = (strchr(\"*/%\", *op) != NULL);\n\t\t\t\t\tif (!(is_lopriop || is_hipriop))\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tparentOp = get_simple_binary_op_name((OpExpr *) parentNode);\n\t\t\t\t\tif (!parentOp)\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tis_lopriparent = (strchr(\"+-\", *parentOp) != NULL);\n\t\t\t\t\tis_hipriparent = (strchr(\"*/%\", *parentOp) != NULL);\n\t\t\t\t\tif (!(is_lopriparent || is_hipriparent))\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tif (is_hipriop && is_lopriparent)\n\t\t\t\t\t\treturn true;\t/* op binds tighter than parent */\n\n\t\t\t\t\tif (is_lopriop && is_hipriparent)\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Operators are same priority --- can skip parens only if\n\t\t\t\t\t * we have (a - b) - c, not a - (b - c).\n\t\t\t\t\t */\n\t\t\t\t\tif (node == (Node *) linitial(((OpExpr *) parentNode)->args))\n\t\t\t\t\t\treturn true;\n\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t/* else do the same stuff as for T_SubLink et al. */\n\t\t\t}\n\t\t\t/* FALLTHROUGH */\n\n\t\tcase T_SubLink:\n\t\tcase T_NullTest:\n\t\tcase T_BooleanTest:\n\t\tcase T_DistinctExpr:\n\t\tcase T_JsonIsPredicate:\n\t\t\tswitch (nodeTag(parentNode))\n\t\t\t{\n\t\t\t\tcase T_FuncExpr:\n\t\t\t\t\t{\n\t\t\t\t\t\t/* special handling for casts and COERCE_SQL_SYNTAX */\n\t\t\t\t\t\tCoercionForm type = ((FuncExpr *) parentNode)->funcformat;\n\n\t\t\t\t\t\tif (type == COERCE_EXPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_IMPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_SQL_SYNTAX)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\treturn true;\t/* own parentheses */\n\t\t\t\t\t}\n\t\t\t\tcase T_BoolExpr:\t/* lower precedence */\n\t\t\t\tcase T_SubscriptingRef:\t/* other separators */\n\t\t\t\tcase T_ArrayExpr:\t/* other separators */\n\t\t\t\tcase T_RowExpr: /* other separators */\n\t\t\t\tcase T_CoalesceExpr:\t/* own parentheses */\n\t\t\t\tcase T_MinMaxExpr:\t/* own parentheses */\n\t\t\t\tcase T_XmlExpr: /* own parentheses */\n\t\t\t\tcase T_NullIfExpr:\t/* other separators */\n\t\t\t\tcase T_Aggref:\t/* own parentheses */\n                case T_GroupingFunc: /* own parentheses */\n\t\t\t\tcase T_WindowFunc:\t/* own parentheses */\n\t\t\t\tcase T_CaseExpr:\t/* other separators */\n\t\t\t\t\treturn true;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\n\t\tcase T_BoolExpr:\n\t\t\tswitch (nodeTag(parentNode))\n\t\t\t{\n\t\t\t\tcase T_BoolExpr:\n\t\t\t\t\tif (prettyFlags & PRETTYFLAG_PAREN)\n\t\t\t\t\t{\n\t\t\t\t\t\tBoolExprType type;\n\t\t\t\t\t\tBoolExprType parentType;\n\n\t\t\t\t\t\ttype = ((BoolExpr *) node)->boolop;\n\t\t\t\t\t\tparentType = ((BoolExpr *) parentNode)->boolop;\n\t\t\t\t\t\tswitch (type)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase NOT_EXPR:\n\t\t\t\t\t\t\tcase AND_EXPR:\n\t\t\t\t\t\t\t\tif (parentType == AND_EXPR || parentType == OR_EXPR)\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase OR_EXPR:\n\t\t\t\t\t\t\t\tif (parentType == OR_EXPR)\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\tcase T_FuncExpr:\n\t\t\t\t\t{\n\t\t\t\t\t\t/* special handling for casts and COERCE_SQL_SYNTAX */\n\t\t\t\t\t\tCoercionForm type = ((FuncExpr *) parentNode)->funcformat;\n\n\t\t\t\t\t\tif (type == COERCE_EXPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_IMPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_SQL_SYNTAX)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\treturn true;\t/* own parentheses */\n\t\t\t\t\t}\n\t\t\t\tcase T_SubscriptingRef:\t/* other separators */\n\t\t\t\tcase T_ArrayExpr:\t/* other separators */\n\t\t\t\tcase T_RowExpr: /* other separators */\n\t\t\t\tcase T_CoalesceExpr:\t/* own parentheses */\n\t\t\t\tcase T_MinMaxExpr:\t/* own parentheses */\n\t\t\t\tcase T_XmlExpr: /* own parentheses */\n\t\t\t\tcase T_NullIfExpr:\t/* other separators */\n\t\t\t\tcase T_Aggref:\t/* own parentheses */\n                case T_GroupingFunc: /* own parentheses */\n\t\t\t\tcase T_WindowFunc:\t/* own parentheses */\n\t\t\t\tcase T_CaseExpr:\t/* other separators */\n\t\t\t\tcase T_JsonExpr: /* own parentheses */\n\t\t\t\t\treturn true;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\n\t\tcase T_JsonValueExpr:\n\t\t\t/* maybe simple, check args */\n\t\t\treturn isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\t/* those we don't know: in dubio complexo */\n\treturn false;\n}\n\n/*\n * appendContextKeyword - append a keyword to buffer\n *\n * If prettyPrint is enabled, perform a line break, and adjust indentation.\n * Otherwise, just append the keyword.\n */\nstatic void\nappendContextKeyword(deparse_context *context, const char *str,\n\t\t\t\t\t int indentBefore, int indentAfter, int indentPlus)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tint\t\t\tindentAmount;\n\n\t\tcontext->indentLevel += indentBefore;\n\n\t\t/* remove any trailing spaces currently in the buffer ... */\n\t\tremoveStringInfoSpaces(buf);\n\t\t/* ... then add a newline and some spaces */\n\t\tappendStringInfoChar(buf, '\\n');\n\n\t\tif (context->indentLevel < PRETTYINDENT_LIMIT)\n\t\t\tindentAmount = Max(context->indentLevel, 0) + indentPlus;\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * If we're indented more than PRETTYINDENT_LIMIT characters, try\n\t\t\t * to conserve horizontal space by reducing the per-level\n\t\t\t * indentation.  For best results the scale factor here should\n\t\t\t * divide all the indent amounts that get added to indentLevel\n\t\t\t * (PRETTYINDENT_STD, etc).  It's important that the indentation\n\t\t\t * not grow unboundedly, else deeply-nested trees use O(N^2)\n\t\t\t * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.\n\t\t\t */\n\t\t\tindentAmount = PRETTYINDENT_LIMIT +\n\t\t\t\t(context->indentLevel - PRETTYINDENT_LIMIT) /\n\t\t\t\t(PRETTYINDENT_STD / 2);\n\t\t\tindentAmount %= PRETTYINDENT_LIMIT;\n\t\t\t/* scale/wrap logic affects indentLevel, but not indentPlus */\n\t\t\tindentAmount += indentPlus;\n\t\t}\n\t\tappendStringInfoSpaces(buf, indentAmount);\n\n\t\tappendStringInfoString(buf, str);\n\n\t\tcontext->indentLevel += indentAfter;\n\t\tif (context->indentLevel < 0)\n\t\t\tcontext->indentLevel = 0;\n\t}\n\telse\n\t\tappendStringInfoString(buf, str);\n}\n\n/*\n * removeStringInfoSpaces - delete trailing spaces from a buffer.\n *\n * Possibly this should move to stringinfo.c at some point.\n */\nstatic void\nremoveStringInfoSpaces(StringInfo str)\n{\n\twhile (str->len > 0 && str->data[str->len - 1] == ' ')\n\t\tstr->data[--(str->len)] = '\\0';\n}\n\n/*\n * get_rule_expr_paren\t- deparse expr using get_rule_expr,\n * embracing the string with parentheses if necessary for prettyPrint.\n *\n * Never embrace if prettyFlags=0, because it's done in the calling node.\n *\n * Any node that does *not* embrace its argument node by sql syntax (with\n * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should\n * use get_rule_expr_paren instead of get_rule_expr so parentheses can be\n * added.\n */\nstatic void\nget_rule_expr_paren(Node *node, deparse_context *context,\n\t\t\t\t\tbool showimplicit, Node *parentNode)\n{\n\tbool\t\tneed_paren;\n\n\tneed_paren = PRETTY_PAREN(context) &&\n\t\t!isSimpleNode(node, parentNode, context->prettyFlags);\n\n\tif (need_paren)\n\t\tappendStringInfoChar(context->buf, '(');\n\n\tget_rule_expr(node, context, showimplicit);\n\n\tif (need_paren)\n\t\tappendStringInfoChar(context->buf, ')');\n}\n\nstatic void\nget_json_behavior(JsonBehavior *behavior, deparse_context *context,\n\t\t\t\t  const char *on)\n{\n\t/*\n\t * The order of array elements must correspond to the order of\n\t * JsonBehaviorType members.\n\t */\n\tconst char *behavior_names[] =\n\t{\n\t\t\" NULL\",\n\t\t\" ERROR\",\n\t\t\" EMPTY\",\n\t\t\" TRUE\",\n\t\t\" FALSE\",\n\t\t\" UNKNOWN\",\n\t\t\" EMPTY ARRAY\",\n\t\t\" EMPTY OBJECT\",\n\t\t\" DEFAULT \"\n\t};\n\n\tif ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))\n\t\telog(ERROR, \"invalid json behavior type: %d\", behavior->btype);\n\n\tappendStringInfoString(context->buf, behavior_names[behavior->btype]);\n\n\tif (behavior->btype == JSON_BEHAVIOR_DEFAULT)\n\t\tget_rule_expr(behavior->expr, context, false);\n\n\tappendStringInfo(context->buf, \" ON %s\", on);\n}\n\n/*\n * get_json_expr_options\n *\n * Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and\n * JSON_TABLE columns.\n */\nstatic void\nget_json_expr_options(JsonExpr *jsexpr, deparse_context *context,\n\t\t\t\t\t  JsonBehaviorType default_behavior)\n{\n\tif (jsexpr->op == JSON_QUERY_OP)\n\t{\n\t\tif (jsexpr->wrapper == JSW_CONDITIONAL)\n\t\t\tappendStringInfoString(context->buf, \" WITH CONDITIONAL WRAPPER\");\n\t\telse if (jsexpr->wrapper == JSW_UNCONDITIONAL)\n\t\t\tappendStringInfoString(context->buf, \" WITH UNCONDITIONAL WRAPPER\");\n\t\t/* The default */\n\t\telse if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)\n\t\t\tappendStringInfoString(context->buf, \" WITHOUT WRAPPER\");\n\n\t\tif (jsexpr->omit_quotes)\n\t\t\tappendStringInfoString(context->buf, \" OMIT QUOTES\");\n\t\t/* The default */\n\t\telse\n\t\t\tappendStringInfoString(context->buf, \" KEEP QUOTES\");\n\t}\n\n\tif (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)\n\t\tget_json_behavior(jsexpr->on_empty, context, \"EMPTY\");\n\n\tif (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)\n\t\tget_json_behavior(jsexpr->on_error, context, \"ERROR\");\n}\n\n/* ----------\n * get_rule_expr\t\t\t- Parse back an expression\n *\n * Note: showimplicit determines whether we display any implicit cast that\n * is present at the top of the expression tree.  It is a passed argument,\n * not a field of the context struct, because we change the value as we\n * recurse down into the expression.  In general we suppress implicit casts\n * when the result type is known with certainty (eg, the arguments of an\n * OR must be boolean).  We display implicit casts for arguments of functions\n * and operators, since this is needed to be certain that the same function\n * or operator will be chosen when the expression is re-parsed.\n * ----------\n */\nstatic void\nget_rule_expr(Node *node, deparse_context *context,\n\t\t\t  bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (node == NULL)\n\t\treturn;\n\n\t/* Guard against excessively long or deeply-nested queries */\n\tCHECK_FOR_INTERRUPTS();\n\tcheck_stack_depth();\n\n\t/*\n\t * Each level of get_rule_expr must emit an indivisible term\n\t * (parenthesized if necessary) to ensure result is reparsed into the same\n\t * expression tree.  The only exception is that when the input is a List,\n\t * we emit the component items comma-separated with no surrounding\n\t * decoration; this is convenient for most callers.\n\t */\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_Var:\n\t\t\t(void) get_variable((Var *) node, 0, false, context);\n\t\t\tbreak;\n\n\t\tcase T_Const:\n\t\t\tget_const_expr((Const *) node, context, 0);\n\t\t\tbreak;\n\n\t\tcase T_Param:\n\t\t\tget_parameter((Param *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_Aggref:\n\t\t\tget_agg_expr((Aggref *) node, context, (Aggref *) node);\n\t\t\tbreak;\n\n\t\tcase T_GroupingFunc:\n\t\t\t{\n\t\t\t\tGroupingFunc *gexpr = (GroupingFunc *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"GROUPING(\");\n\t\t\t\tget_rule_expr((Node *) gexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_WindowFunc:\n\t\t\tget_windowfunc_expr((WindowFunc *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_MergeSupportFunc:\n\t\t\tappendStringInfoString(buf, \"MERGE_ACTION()\");\n\t\t\tbreak;\n\n\t\tcase T_SubscriptingRef:\n\t\t\t{\n\t\t\t\tSubscriptingRef *sbsref = (SubscriptingRef *) node;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * If the argument is a CaseTestExpr, we must be inside a\n\t\t\t\t * FieldStore, ie, we are assigning to an element of an array\n\t\t\t\t * within a composite column.  Since we already punted on\n\t\t\t\t * displaying the FieldStore's target information, just punt\n\t\t\t\t * here too, and display only the assignment source\n\t\t\t\t * expression.\n\t\t\t\t */\n\t\t\t\tif (IsA(sbsref->refexpr, CaseTestExpr))\n\t\t\t\t{\n\t\t\t\t\tAssert(sbsref->refassgnexpr);\n\t\t\t\t\tget_rule_expr((Node *) sbsref->refassgnexpr,\n\t\t\t\t\t\t\t\t  context, showimplicit);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * Parenthesize the argument unless it's a simple Var or a\n\t\t\t\t * FieldSelect.  (In particular, if it's another\n\t\t\t\t * SubscriptingRef, we *must* parenthesize to avoid\n\t\t\t\t * confusion.)\n\t\t\t\t */\n\t\t\t\tneed_parens = !IsA(sbsref->refexpr, Var) &&\n\t\t\t\t\t!IsA(sbsref->refexpr, FieldSelect);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr((Node *) sbsref->refexpr, context, showimplicit);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t\t/*\n\t\t\t\t * If there's a refassgnexpr, we want to print the node in the\n\t\t\t\t * format \"container[subscripts] := refassgnexpr\".  This is\n\t\t\t\t * not legal SQL, so decompilation of INSERT or UPDATE\n\t\t\t\t * statements should always use processIndirection as part of\n\t\t\t\t * the statement-level syntax.  We should only see this when\n\t\t\t\t * EXPLAIN tries to print the targetlist of a plan resulting\n\t\t\t\t * from such a statement.\n\t\t\t\t */\n\t\t\t\tif (sbsref->refassgnexpr)\n\t\t\t\t{\n\t\t\t\t\tNode\t   *refassgnexpr;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Use processIndirection to print this node's subscripts\n\t\t\t\t\t * as well as any additional field selections or\n\t\t\t\t\t * subscripting in immediate descendants.  It returns the\n\t\t\t\t\t * RHS expr that is actually being \"assigned\".\n\t\t\t\t\t */\n\t\t\t\t\trefassgnexpr = processIndirection(node, context);\n\t\t\t\t\tappendStringInfoString(buf, \" := \");\n\t\t\t\t\tget_rule_expr(refassgnexpr, context, showimplicit);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* Just an ordinary container fetch, so print subscripts */\n\t\t\t\t\tprintSubscripts(sbsref, context);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_FuncExpr:\n\t\t\tget_func_expr((FuncExpr *) node, context, showimplicit);\n\t\t\tbreak;\n\n\t\tcase T_NamedArgExpr:\n\t\t\t{\n\t\t\t\tNamedArgExpr *na = (NamedArgExpr *) node;\n\n\t\t\t\tappendStringInfo(buf, \"%s => \", quote_identifier(na->name));\n\t\t\t\tget_rule_expr((Node *) na->arg, context, showimplicit);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_OpExpr:\n\t\t\tget_oper_expr((OpExpr *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_DistinctExpr:\n\t\t\t{\n\t\t\t\tDistinctExpr *expr = (DistinctExpr *) node;\n\t\t\t\tList\t   *args = expr->args;\n\t\t\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\t\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren(arg1, context, true, node);\n\t\t\t\tappendStringInfoString(buf, \" IS DISTINCT FROM \");\n\t\t\t\tget_rule_expr_paren(arg2, context, true, node);\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_NullIfExpr:\n\t\t\t{\n\t\t\t\tNullIfExpr *nullifexpr = (NullIfExpr *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"NULLIF(\");\n\t\t\t\tget_rule_expr((Node *) nullifexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ScalarArrayOpExpr:\n\t\t\t{\n\t\t\t\tScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;\n\t\t\t\tList\t   *args = expr->args;\n\t\t\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\t\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren(arg1, context, true, node);\n\t\t\t\tappendStringInfo(buf, \" %s %s (\",\n\t\t\t\t\t\t\t\t generate_operator_name(expr->opno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\texprType(arg1),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tget_base_element_type(exprType(arg2))),\n\t\t\t\t\t\t\t\t expr->useOr ? \"ANY\" : \"ALL\");\n\t\t\t\tget_rule_expr_paren(arg2, context, true, node);\n\n\t\t\t\t/*\n\t\t\t\t * There's inherent ambiguity in \"x op ANY/ALL (y)\" when y is\n\t\t\t\t * a bare sub-SELECT.  Since we're here, the sub-SELECT must\n\t\t\t\t * be meant as a scalar sub-SELECT yielding an array value to\n\t\t\t\t * be used in ScalarArrayOpExpr; but the grammar will\n\t\t\t\t * preferentially interpret such a construct as an ANY/ALL\n\t\t\t\t * SubLink.  To prevent misparsing the output that way, insert\n\t\t\t\t * a dummy coercion (which will be stripped by parse analysis,\n\t\t\t\t * so no inefficiency is added in dump and reload).  This is\n\t\t\t\t * indeed most likely what the user wrote to get the construct\n\t\t\t\t * accepted in the first place.\n\t\t\t\t */\n\t\t\t\tif (IsA(arg2, SubLink) &&\n\t\t\t\t\t((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)\n\t\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(exprType(arg2),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  exprTypmod(arg2)));\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_BoolExpr:\n\t\t\t{\n\t\t\t\tBoolExpr   *expr = (BoolExpr *) node;\n\t\t\t\tNode\t   *first_arg = linitial(expr->args);\n\t\t\t\tListCell   *arg;\n\n\t\t\t\tswitch (expr->boolop)\n\t\t\t\t{\n\t\t\t\t\tcase AND_EXPR:\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tget_rule_expr_paren(first_arg, context,\n\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\tfor_each_from(arg, expr->args, 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" AND \");\n\t\t\t\t\t\t\tget_rule_expr_paren((Node *) lfirst(arg), context,\n\t\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase OR_EXPR:\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tget_rule_expr_paren(first_arg, context,\n\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\tfor_each_from(arg, expr->args, 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" OR \");\n\t\t\t\t\t\t\tget_rule_expr_paren((Node *) lfirst(arg), context,\n\t\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase NOT_EXPR:\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tappendStringInfoString(buf, \"NOT \");\n\t\t\t\t\t\tget_rule_expr_paren(first_arg, context,\n\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telog(ERROR, \"unrecognized boolop: %d\",\n\t\t\t\t\t\t\t (int) expr->boolop);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_SubLink:\n\t\t\tget_sublink_expr((SubLink *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_SubPlan:\n\t\t\t{\n\t\t\t\tSubPlan    *subplan = (SubPlan *) node;\n\n\t\t\t\t/*\n\t\t\t\t * We cannot see an already-planned subplan in rule deparsing,\n\t\t\t\t * only while EXPLAINing a query plan.  We don't try to\n\t\t\t\t * reconstruct the original SQL, just reference the subplan\n\t\t\t\t * that appears elsewhere in EXPLAIN's result.  It does seem\n\t\t\t\t * useful to show the subLinkType and testexpr (if any), and\n\t\t\t\t * we also note whether the subplan will be hashed.\n\t\t\t\t */\n\t\t\t\tswitch (subplan->subLinkType)\n\t\t\t\t{\n\t\t\t\t\tcase EXISTS_SUBLINK:\n\t\t\t\t\t\tappendStringInfoString(buf, \"EXISTS(\");\n\t\t\t\t\t\tAssert(subplan->testexpr == NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ALL_SUBLINK:\n\t\t\t\t\t\tappendStringInfoString(buf, \"(ALL \");\n\t\t\t\t\t\tAssert(subplan->testexpr != NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ANY_SUBLINK:\n\t\t\t\t\t\tappendStringInfoString(buf, \"(ANY \");\n\t\t\t\t\t\tAssert(subplan->testexpr != NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ROWCOMPARE_SUBLINK:\n\t\t\t\t\t\t/* Parenthesizing the testexpr seems sufficient */\n\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tAssert(subplan->testexpr != NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase EXPR_SUBLINK:\n\t\t\t\t\t\t/* No need to decorate these subplan references */\n\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tAssert(subplan->testexpr == NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MULTIEXPR_SUBLINK:\n\t\t\t\t\t\t/* MULTIEXPR isn't executed in the normal way */\n\t\t\t\t\t\tappendStringInfoString(buf, \"(rescan \");\n\t\t\t\t\t\tAssert(subplan->testexpr == NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ARRAY_SUBLINK:\n\t\t\t\t\t\tappendStringInfoString(buf, \"ARRAY(\");\n\t\t\t\t\t\tAssert(subplan->testexpr == NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase CTE_SUBLINK:\n\t\t\t\t\t\t/* This case is unreachable within expressions */\n\t\t\t\t\t\tappendStringInfoString(buf, \"CTE(\");\n\t\t\t\t\t\tAssert(subplan->testexpr == NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (subplan->testexpr != NULL)\n\t\t\t\t{\n\t\t\t\t\tdeparse_namespace *dpns;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Push SubPlan into ancestors list while deparsing\n\t\t\t\t\t * testexpr, so that we can handle PARAM_EXEC references\n\t\t\t\t\t * to the SubPlan's paramIds.  (This makes it look like\n\t\t\t\t\t * the SubPlan is an \"ancestor\" of the current plan node,\n\t\t\t\t\t * which is a little weird, but it does no harm.)  In this\n\t\t\t\t\t * path, we don't need to mention the SubPlan explicitly,\n\t\t\t\t\t * because the referencing Params will show its existence.\n\t\t\t\t\t */\n\t\t\t\t\tdpns = (deparse_namespace *) linitial(context->namespaces);\n\t\t\t\t\tdpns->ancestors = lcons(subplan, dpns->ancestors);\n\n\t\t\t\t\tget_rule_expr(subplan->testexpr, context, showimplicit);\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t\t\tdpns->ancestors = list_delete_first(dpns->ancestors);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* No referencing Params, so show the SubPlan's name */\n\t\t\t\t\tif (subplan->useHashTable)\n\t\t\t\t\t\tappendStringInfo(buf, \"hashed %s)\", subplan->plan_name);\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfo(buf, \"%s)\", subplan->plan_name);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_AlternativeSubPlan:\n\t\t\t{\n\t\t\t\tAlternativeSubPlan *asplan = (AlternativeSubPlan *) node;\n\t\t\t\tListCell   *lc;\n\n\t\t\t\t/*\n\t\t\t\t * This case cannot be reached in normal usage, since no\n\t\t\t\t * AlternativeSubPlan can appear either in parsetrees or\n\t\t\t\t * finished plan trees.  We keep it just in case somebody\n\t\t\t\t * wants to use this code to print planner data structures.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"(alternatives: \");\n\t\t\t\tforeach(lc, asplan->subplans)\n\t\t\t\t{\n\t\t\t\t\tSubPlan    *splan = lfirst_node(SubPlan, lc);\n\n\t\t\t\t\tif (splan->useHashTable)\n\t\t\t\t\t\tappendStringInfo(buf, \"hashed %s\", splan->plan_name);\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfoString(buf, splan->plan_name);\n\t\t\t\t\tif (lnext(asplan->subplans, lc))\n\t\t\t\t\t\tappendStringInfoString(buf, \" or \");\n\t\t\t\t}\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_FieldSelect:\n\t\t\t{\n\t\t\t\tFieldSelect *fselect = (FieldSelect *) node;\n\t\t\t\tNode\t   *arg = (Node *) fselect->arg;\n\t\t\t\tint\t\t\tfno = fselect->fieldnum;\n\t\t\t\tconst char *fieldname;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * Parenthesize the argument unless it's an SubscriptingRef or\n\t\t\t\t * another FieldSelect.  Note in particular that it would be\n\t\t\t\t * WRONG to not parenthesize a Var argument; simplicity is not\n\t\t\t\t * the issue here, having the right number of names is.\n\t\t\t\t */\n\t\t\t\tneed_parens = !IsA(arg, SubscriptingRef) &&\n\t\t\t\t\t!IsA(arg, FieldSelect);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr(arg, context, true);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t\t/*\n\t\t\t\t * Get and print the field name.\n\t\t\t\t */\n\t\t\t\tfieldname = get_name_for_var_field((Var *) arg, fno,\n\t\t\t\t\t\t\t\t\t\t\t\t   0, context);\n\t\t\t\tappendStringInfo(buf, \".%s\", quote_identifier(fieldname));\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_FieldStore:\n\t\t\t{\n\t\t\t\tFieldStore *fstore = (FieldStore *) node;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * There is no good way to represent a FieldStore as real SQL,\n\t\t\t\t * so decompilation of INSERT or UPDATE statements should\n\t\t\t\t * always use processIndirection as part of the\n\t\t\t\t * statement-level syntax.  We should only get here when\n\t\t\t\t * EXPLAIN tries to print the targetlist of a plan resulting\n\t\t\t\t * from such a statement.  The plan case is even harder than\n\t\t\t\t * ordinary rules would be, because the planner tries to\n\t\t\t\t * collapse multiple assignments to the same field or subfield\n\t\t\t\t * into one FieldStore; so we can see a list of target fields\n\t\t\t\t * not just one, and the arguments could be FieldStores\n\t\t\t\t * themselves.  We don't bother to try to print the target\n\t\t\t\t * field names; we just print the source arguments, with a\n\t\t\t\t * ROW() around them if there's more than one.  This isn't\n\t\t\t\t * terribly complete, but it's probably good enough for\n\t\t\t\t * EXPLAIN's purposes; especially since anything more would be\n\t\t\t\t * either hopelessly confusing or an even poorer\n\t\t\t\t * representation of what the plan is actually doing.\n\t\t\t\t */\n\t\t\t\tneed_parens = (list_length(fstore->newvals) != 1);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoString(buf, \"ROW(\");\n\t\t\t\tget_rule_expr((Node *) fstore->newvals, context, showimplicit);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_RelabelType:\n\t\t\t{\n\t\t\t\tRelabelType *relabel = (RelabelType *) node;\n\t\t\t\tNode\t   *arg = (Node *) relabel->arg;\n\n\t\t\t\tif (relabel->relabelformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  relabel->resulttype,\n\t\t\t\t\t\t\t\t\t  relabel->resulttypmod,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoerceViaIO:\n\t\t\t{\n\t\t\t\tCoerceViaIO *iocoerce = (CoerceViaIO *) node;\n\t\t\t\tNode\t   *arg = (Node *) iocoerce->arg;\n\n\t\t\t\tif (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  iocoerce->resulttype,\n\t\t\t\t\t\t\t\t\t  -1,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ArrayCoerceExpr:\n\t\t\t{\n\t\t\t\tArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;\n\t\t\t\tNode\t   *arg = (Node *) acoerce->arg;\n\n\t\t\t\tif (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  acoerce->resulttype,\n\t\t\t\t\t\t\t\t\t  acoerce->resulttypmod,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ConvertRowtypeExpr:\n\t\t\t{\n\t\t\t\tConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;\n\t\t\t\tNode\t   *arg = (Node *) convert->arg;\n\n\t\t\t\tif (convert->convertformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  convert->resulttype, -1,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CollateExpr:\n\t\t\t{\n\t\t\t\tCollateExpr *collate = (CollateExpr *) node;\n\t\t\t\tNode\t   *arg = (Node *) collate->arg;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren(arg, context, showimplicit, node);\n\t\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t\t generate_collation_name(collate->collOid));\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CaseExpr:\n\t\t\t{\n\t\t\t\tCaseExpr   *caseexpr = (CaseExpr *) node;\n\t\t\t\tListCell   *temp;\n\n\t\t\t\tappendContextKeyword(context, \"CASE\",\n\t\t\t\t\t\t\t\t\t 0, PRETTYINDENT_VAR, 0);\n\t\t\t\tif (caseexpr->arg)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\t\tget_rule_expr((Node *) caseexpr->arg, context, true);\n\t\t\t\t}\n\t\t\t\tforeach(temp, caseexpr->args)\n\t\t\t\t{\n\t\t\t\t\tCaseWhen   *when = (CaseWhen *) lfirst(temp);\n\t\t\t\t\tNode\t   *w = (Node *) when->expr;\n\n\t\t\t\t\tif (caseexpr->arg)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * The parser should have produced WHEN clauses of the\n\t\t\t\t\t\t * form \"CaseTestExpr = RHS\", possibly with an\n\t\t\t\t\t\t * implicit coercion inserted above the CaseTestExpr.\n\t\t\t\t\t\t * For accurate decompilation of rules it's essential\n\t\t\t\t\t\t * that we show just the RHS.  However in an\n\t\t\t\t\t\t * expression that's been through the optimizer, the\n\t\t\t\t\t\t * WHEN clause could be almost anything (since the\n\t\t\t\t\t\t * equality operator could have been expanded into an\n\t\t\t\t\t\t * inline function).  If we don't recognize the form\n\t\t\t\t\t\t * of the WHEN clause, just punt and display it as-is.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (IsA(w, OpExpr))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tList\t   *args = ((OpExpr *) w)->args;\n\n\t\t\t\t\t\t\tif (list_length(args) == 2 &&\n\t\t\t\t\t\t\t\tIsA(strip_implicit_coercions(linitial(args)),\n\t\t\t\t\t\t\t\t\tCaseTestExpr))\n\t\t\t\t\t\t\t\tw = (Node *) lsecond(args);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!PRETTY_INDENT(context))\n\t\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\t\tappendContextKeyword(context, \"WHEN \",\n\t\t\t\t\t\t\t\t\t\t 0, 0, 0);\n\t\t\t\t\tget_rule_expr(w, context, false);\n\t\t\t\t\tappendStringInfoString(buf, \" THEN \");\n\t\t\t\t\tget_rule_expr((Node *) when->result, context, true);\n\t\t\t\t}\n\t\t\t\tif (!PRETTY_INDENT(context))\n\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tappendContextKeyword(context, \"ELSE \",\n\t\t\t\t\t\t\t\t\t 0, 0, 0);\n\t\t\t\tget_rule_expr((Node *) caseexpr->defresult, context, true);\n\t\t\t\tif (!PRETTY_INDENT(context))\n\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tappendContextKeyword(context, \"END\",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_VAR, 0, 0);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CaseTestExpr:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Normally we should never get here, since for expressions\n\t\t\t\t * that can contain this node type we attempt to avoid\n\t\t\t\t * recursing to it.  But in an optimized expression we might\n\t\t\t\t * be unable to avoid that (see comments for CaseExpr).  If we\n\t\t\t\t * do see one, print it as CASE_TEST_EXPR.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"CASE_TEST_EXPR\");\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ArrayExpr:\n\t\t\t{\n\t\t\t\tArrayExpr  *arrayexpr = (ArrayExpr *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"ARRAY[\");\n\t\t\t\tget_rule_expr((Node *) arrayexpr->elements, context, true);\n\t\t\t\tappendStringInfoChar(buf, ']');\n\n\t\t\t\t/*\n\t\t\t\t * If the array isn't empty, we assume its elements are\n\t\t\t\t * coerced to the desired type.  If it's empty, though, we\n\t\t\t\t * need an explicit coercion to the array type.\n\t\t\t\t */\n\t\t\t\tif (arrayexpr->elements == NIL)\n\t\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(arrayexpr->array_typeid, -1));\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_RowExpr:\n\t\t\t{\n\t\t\t\tRowExpr    *rowexpr = (RowExpr *) node;\n\t\t\t\tTupleDesc\ttupdesc = NULL;\n\t\t\t\tListCell   *arg;\n\t\t\t\tint\t\t\ti;\n\t\t\t\tchar\t   *sep;\n\n\t\t\t\t/*\n\t\t\t\t * If it's a named type and not RECORD, we may have to skip\n\t\t\t\t * dropped columns and/or claim there are NULLs for added\n\t\t\t\t * columns.\n\t\t\t\t */\n\t\t\t\tif (rowexpr->row_typeid != RECORDOID)\n\t\t\t\t{\n\t\t\t\t\ttupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);\n\t\t\t\t\tAssert(list_length(rowexpr->args) <= tupdesc->natts);\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * SQL99 allows \"ROW\" to be omitted when there is more than\n\t\t\t\t * one column, but for simplicity we always print it.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"ROW(\");\n\t\t\t\tsep = \"\";\n\t\t\t\ti = 0;\n\t\t\t\tforeach(arg, rowexpr->args)\n\t\t\t\t{\n\t\t\t\t\tNode\t   *e = (Node *) lfirst(arg);\n\n\t\t\t\t\tif (tupdesc == NULL ||\n\t\t\t\t\t\t!TupleDescAttr(tupdesc, i)->attisdropped)\n\t\t\t\t\t{\n\t\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\t\t/* Whole-row Vars need special treatment here */\n\t\t\t\t\t\tget_rule_expr_toplevel(e, context, true);\n\t\t\t\t\t\tsep = \", \";\n\t\t\t\t\t}\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t\tif (tupdesc != NULL)\n\t\t\t\t{\n\t\t\t\t\twhile (i < tupdesc->natts)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!TupleDescAttr(tupdesc, i)->attisdropped)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\t\t\tappendStringInfoString(buf, \"NULL\");\n\t\t\t\t\t\t\tsep = \", \";\n\t\t\t\t\t\t}\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tReleaseTupleDesc(tupdesc);\n\t\t\t\t}\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tif (rowexpr->row_format == COERCE_EXPLICIT_CAST)\n\t\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(rowexpr->row_typeid, -1));\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_RowCompareExpr:\n\t\t\t{\n\t\t\t\tRowCompareExpr *rcexpr = (RowCompareExpr *) node;\n\n\t\t\t\t/*\n\t\t\t\t * SQL99 allows \"ROW\" to be omitted when there is more than\n\t\t\t\t * one column, but for simplicity we always print it.  Within\n\t\t\t\t * a ROW expression, whole-row Vars need special treatment, so\n\t\t\t\t * use get_rule_list_toplevel.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"(ROW(\");\n\t\t\t\tget_rule_list_toplevel(rcexpr->largs, context, true);\n\n\t\t\t\t/*\n\t\t\t\t * We assume that the name of the first-column operator will\n\t\t\t\t * do for all the rest too.  This is definitely open to\n\t\t\t\t * failure, eg if some but not all operators were renamed\n\t\t\t\t * since the construct was parsed, but there seems no way to\n\t\t\t\t * be perfect.\n\t\t\t\t */\n\t\t\t\tappendStringInfo(buf, \") %s ROW(\",\n\t\t\t\t\t\t\t\t generate_operator_name(linitial_oid(rcexpr->opnos),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->largs)),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->rargs))));\n\t\t\t\tget_rule_list_toplevel(rcexpr->rargs, context, true);\n\t\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoalesceExpr:\n\t\t\t{\n\t\t\t\tCoalesceExpr *coalesceexpr = (CoalesceExpr *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"COALESCE(\");\n\t\t\t\tget_rule_expr((Node *) coalesceexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_MinMaxExpr:\n\t\t\t{\n\t\t\t\tMinMaxExpr *minmaxexpr = (MinMaxExpr *) node;\n\n\t\t\t\tswitch (minmaxexpr->op)\n\t\t\t\t{\n\t\t\t\t\tcase IS_GREATEST:\n\t\t\t\t\t\tappendStringInfoString(buf, \"GREATEST(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_LEAST:\n\t\t\t\t\t\tappendStringInfoString(buf, \"LEAST(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tget_rule_expr((Node *) minmaxexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_SQLValueFunction:\n\t\t\t{\n\t\t\t\tSQLValueFunction *svf = (SQLValueFunction *) node;\n\n\t\t\t\t/*\n\t\t\t\t * Note: this code knows that typmod for time, timestamp, and\n\t\t\t\t * timestamptz just prints as integer.\n\t\t\t\t */\n\t\t\t\tswitch (svf->op)\n\t\t\t\t{\n\t\t\t\t\tcase SVFOP_CURRENT_DATE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_DATE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIME:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_TIME\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIME_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"CURRENT_TIME(%d)\", svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIMESTAMP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_TIMESTAMP\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIMESTAMP_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"CURRENT_TIMESTAMP(%d)\",\n\t\t\t\t\t\t\t\t\t\t svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIME:\n\t\t\t\t\t\tappendStringInfoString(buf, \"LOCALTIME\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIME_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"LOCALTIME(%d)\", svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIMESTAMP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"LOCALTIMESTAMP\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIMESTAMP_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"LOCALTIMESTAMP(%d)\",\n\t\t\t\t\t\t\t\t\t\t svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_ROLE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_ROLE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_USER:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_USER\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_USER:\n\t\t\t\t\t\tappendStringInfoString(buf, \"USER\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_SESSION_USER:\n\t\t\t\t\t\tappendStringInfoString(buf, \"SESSION_USER\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_CATALOG:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_CATALOG\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_SCHEMA:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_SCHEMA\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_XmlExpr:\n\t\t\t{\n\t\t\t\tXmlExpr    *xexpr = (XmlExpr *) node;\n\t\t\t\tbool\t\tneedcomma = false;\n\t\t\t\tListCell   *arg;\n\t\t\t\tListCell   *narg;\n\t\t\t\tConst\t   *con;\n\n\t\t\t\tswitch (xexpr->op)\n\t\t\t\t{\n\t\t\t\t\tcase IS_XMLCONCAT:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLCONCAT(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLELEMENT:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLELEMENT(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLFOREST:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLFOREST(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLPARSE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLPARSE(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLPI:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLPI(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLROOT:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLROOT(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLSERIALIZE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLSERIALIZE(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_DOCUMENT:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)\n\t\t\t\t{\n\t\t\t\t\tif (xexpr->xmloption == XMLOPTION_DOCUMENT)\n\t\t\t\t\t\tappendStringInfoString(buf, \"DOCUMENT \");\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfoString(buf, \"CONTENT \");\n\t\t\t\t}\n\t\t\t\tif (xexpr->name)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfo(buf, \"NAME %s\",\n\t\t\t\t\t\t\t\t\t quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));\n\t\t\t\t\tneedcomma = true;\n\t\t\t\t}\n\t\t\t\tif (xexpr->named_args)\n\t\t\t\t{\n\t\t\t\t\tif (xexpr->op != IS_XMLFOREST)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (needcomma)\n\t\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLATTRIBUTES(\");\n\t\t\t\t\t\tneedcomma = false;\n\t\t\t\t\t}\n\t\t\t\t\tforboth(arg, xexpr->named_args, narg, xexpr->arg_names)\n\t\t\t\t\t{\n\t\t\t\t\t\tNode\t   *e = (Node *) lfirst(arg);\n\t\t\t\t\t\tchar\t   *argname = strVal(lfirst(narg));\n\n\t\t\t\t\t\tif (needcomma)\n\t\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\t\tget_rule_expr((Node *) e, context, true);\n\t\t\t\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t\t\t\t quote_identifier(map_xml_name_to_sql_identifier(argname)));\n\t\t\t\t\t\tneedcomma = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (xexpr->op != IS_XMLFOREST)\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t}\n\t\t\t\tif (xexpr->args)\n\t\t\t\t{\n\t\t\t\t\tif (needcomma)\n\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\tswitch (xexpr->op)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase IS_XMLCONCAT:\n\t\t\t\t\t\tcase IS_XMLELEMENT:\n\t\t\t\t\t\tcase IS_XMLFOREST:\n\t\t\t\t\t\tcase IS_XMLPI:\n\t\t\t\t\t\tcase IS_XMLSERIALIZE:\n\t\t\t\t\t\t\t/* no extra decoration needed */\n\t\t\t\t\t\t\tget_rule_expr((Node *) xexpr->args, context, true);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_XMLPARSE:\n\t\t\t\t\t\t\tAssert(list_length(xexpr->args) == 2);\n\n\t\t\t\t\t\t\tget_rule_expr((Node *) linitial(xexpr->args),\n\t\t\t\t\t\t\t\t\t\t  context, true);\n\n\t\t\t\t\t\t\tcon = lsecond_node(Const, xexpr->args);\n\t\t\t\t\t\t\tAssert(!con->constisnull);\n\t\t\t\t\t\t\tif (DatumGetBool(con->constvalue))\n\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \" PRESERVE WHITESPACE\");\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \" STRIP WHITESPACE\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_XMLROOT:\n\t\t\t\t\t\t\tAssert(list_length(xexpr->args) == 3);\n\n\t\t\t\t\t\t\tget_rule_expr((Node *) linitial(xexpr->args),\n\t\t\t\t\t\t\t\t\t\t  context, true);\n\n\t\t\t\t\t\t\tappendStringInfoString(buf, \", VERSION \");\n\t\t\t\t\t\t\tcon = (Const *) lsecond(xexpr->args);\n\t\t\t\t\t\t\tif (IsA(con, Const) &&\n\t\t\t\t\t\t\t\tcon->constisnull)\n\t\t\t\t\t\t\t\tappendStringInfoString(buf, \"NO VALUE\");\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tget_rule_expr((Node *) con, context, false);\n\n\t\t\t\t\t\t\tcon = lthird_node(Const, xexpr->args);\n\t\t\t\t\t\t\tif (con->constisnull)\n\t\t\t\t\t\t\t\t /* suppress STANDALONE NO VALUE */ ;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tswitch (DatumGetInt32(con->constvalue))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tcase XML_STANDALONE_YES:\n\t\t\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \", STANDALONE YES\");\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase XML_STANDALONE_NO:\n\t\t\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \", STANDALONE NO\");\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase XML_STANDALONE_NO_VALUE:\n\t\t\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \", STANDALONE NO VALUE\");\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_DOCUMENT:\n\t\t\t\t\t\t\tget_rule_expr_paren((Node *) xexpr->args, context, false, node);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (xexpr->op == IS_XMLSERIALIZE)\n\t\t\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(xexpr->type,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  xexpr->typmod));\n\t\t\t\tif (xexpr->op == IS_DOCUMENT)\n\t\t\t\t\tappendStringInfoString(buf, \" IS DOCUMENT\");\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_NullTest:\n\t\t\t{\n\t\t\t\tNullTest   *ntest = (NullTest *) node;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren((Node *) ntest->arg, context, true, node);\n\n\t\t\t\t/*\n\t\t\t\t * For scalar inputs, we prefer to print as IS [NOT] NULL,\n\t\t\t\t * which is shorter and traditional.  If it's a rowtype input\n\t\t\t\t * but we're applying a scalar test, must print IS [NOT]\n\t\t\t\t * DISTINCT FROM NULL to be semantically correct.\n\t\t\t\t */\n\t\t\t\tif (ntest->argisrow ||\n\t\t\t\t\t!type_is_rowtype(exprType((Node *) ntest->arg)))\n\t\t\t\t{\n\t\t\t\t\tswitch (ntest->nulltesttype)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase IS_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_NOT_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\telog(ERROR, \"unrecognized nulltesttype: %d\",\n\t\t\t\t\t\t\t\t (int) ntest->nulltesttype);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tswitch (ntest->nulltesttype)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase IS_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT DISTINCT FROM NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_NOT_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS DISTINCT FROM NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\telog(ERROR, \"unrecognized nulltesttype: %d\",\n\t\t\t\t\t\t\t\t (int) ntest->nulltesttype);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_BooleanTest:\n\t\t\t{\n\t\t\t\tBooleanTest *btest = (BooleanTest *) node;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren((Node *) btest->arg, context, false, node);\n\t\t\t\tswitch (btest->booltesttype)\n\t\t\t\t{\n\t\t\t\t\tcase IS_TRUE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS TRUE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NOT_TRUE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT TRUE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_FALSE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS FALSE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NOT_FALSE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT FALSE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_UNKNOWN:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS UNKNOWN\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NOT_UNKNOWN:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT UNKNOWN\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telog(ERROR, \"unrecognized booltesttype: %d\",\n\t\t\t\t\t\t\t (int) btest->booltesttype);\n\t\t\t\t}\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoerceToDomain:\n\t\t\t{\n\t\t\t\tCoerceToDomain *ctest = (CoerceToDomain *) node;\n\t\t\t\tNode\t   *arg = (Node *) ctest->arg;\n\n\t\t\t\tif (ctest->coercionformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr(arg, context, false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  ctest->resulttype,\n\t\t\t\t\t\t\t\t\t  ctest->resulttypmod,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoerceToDomainValue:\n\t\t\tappendStringInfoString(buf, \"VALUE\");\n\t\t\tbreak;\n\n\t\tcase T_SetToDefault:\n\t\t\tappendStringInfoString(buf, \"DEFAULT\");\n\t\t\tbreak;\n\n\t\tcase T_CurrentOfExpr:\n\t\t\t{\n\t\t\t\tCurrentOfExpr *cexpr = (CurrentOfExpr *) node;\n\n\t\t\t\tif (cexpr->cursor_name)\n\t\t\t\t\tappendStringInfo(buf, \"CURRENT OF %s\",\n\t\t\t\t\t\t\t\t\t quote_identifier(cexpr->cursor_name));\n\t\t\t\telse\n\t\t\t\t\tappendStringInfo(buf, \"CURRENT OF $%d\",\n\t\t\t\t\t\t\t\t\t cexpr->cursor_param);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_NextValueExpr:\n\t\t\t{\n\t\t\t\tNextValueExpr *nvexpr = (NextValueExpr *) node;\n\n\t\t\t\t/*\n\t\t\t\t * This isn't exactly nextval(), but that seems close enough\n\t\t\t\t * for EXPLAIN's purposes.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"nextval(\");\n\t\t\t\tsimple_quote_literal(buf,\n\t\t\t\t\t\t\t\t\t generate_relation_name(nvexpr->seqid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNIL));\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_InferenceElem:\n\t\t\t{\n\t\t\t\tInferenceElem *iexpr = (InferenceElem *) node;\n\t\t\t\tbool\t\tsave_varprefix;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * InferenceElem can only refer to target relation, so a\n\t\t\t\t * prefix is not useful, and indeed would cause parse errors.\n\t\t\t\t */\n\t\t\t\tsave_varprefix = context->varprefix;\n\t\t\t\tcontext->varprefix = false;\n\n\t\t\t\t/*\n\t\t\t\t * Parenthesize the element unless it's a simple Var or a bare\n\t\t\t\t * function call.  Follows pg_get_indexdef_worker().\n\t\t\t\t */\n\t\t\t\tneed_parens = !IsA(iexpr->expr, Var);\n\t\t\t\tif (IsA(iexpr->expr, FuncExpr) &&\n\t\t\t\t\t((FuncExpr *) iexpr->expr)->funcformat ==\n\t\t\t\t\tCOERCE_EXPLICIT_CALL)\n\t\t\t\t\tneed_parens = false;\n\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr((Node *) iexpr->expr,\n\t\t\t\t\t\t\t  context, false);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t\tcontext->varprefix = save_varprefix;\n\n\t\t\t\tif (iexpr->infercollid)\n\t\t\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t\t\t generate_collation_name(iexpr->infercollid));\n\n\t\t\t\t/* Add the operator class name, if not default */\n\t\t\t\tif (iexpr->inferopclass)\n\t\t\t\t{\n\t\t\t\t\tOid\t\t\tinferopclass = iexpr->inferopclass;\n\t\t\t\t\tOid\t\t\tinferopcinputtype = get_opclass_input_type(iexpr->inferopclass);\n\n\t\t\t\t\tget_opclass_name(inferopclass, inferopcinputtype, buf);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_PartitionBoundSpec:\n\t\t\t{\n\t\t\t\tPartitionBoundSpec *spec = (PartitionBoundSpec *) node;\n\t\t\t\tListCell   *cell;\n\t\t\t\tchar\t   *sep;\n\n\t\t\t\tif (spec->is_default)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(buf, \"DEFAULT\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tswitch (spec->strategy)\n\t\t\t\t{\n\t\t\t\t\tcase PARTITION_STRATEGY_HASH:\n\t\t\t\t\t\tAssert(spec->modulus > 0 && spec->remainder >= 0);\n\t\t\t\t\t\tAssert(spec->modulus > spec->remainder);\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"FOR VALUES\");\n\t\t\t\t\t\tappendStringInfo(buf, \" WITH (modulus %d, remainder %d)\",\n\t\t\t\t\t\t\t\t\t\t spec->modulus, spec->remainder);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase PARTITION_STRATEGY_LIST:\n\t\t\t\t\t\tAssert(spec->listdatums != NIL);\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"FOR VALUES IN (\");\n\t\t\t\t\t\tsep = \"\";\n\t\t\t\t\t\tforeach(cell, spec->listdatums)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tConst\t   *val = lfirst_node(Const, cell);\n\n\t\t\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\t\t\tget_const_expr(val, context, -1);\n\t\t\t\t\t\t\tsep = \", \";\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase PARTITION_STRATEGY_RANGE:\n\t\t\t\t\t\tAssert(spec->lowerdatums != NIL &&\n\t\t\t\t\t\t\t   spec->upperdatums != NIL &&\n\t\t\t\t\t\t\t   list_length(spec->lowerdatums) ==\n\t\t\t\t\t\t\t   list_length(spec->upperdatums));\n\n\t\t\t\t\t\tappendStringInfo(buf, \"FOR VALUES FROM %s TO %s\",\n\t\t\t\t\t\t\t\t\t\t get_range_partbound_string(spec->lowerdatums),\n\t\t\t\t\t\t\t\t\t\t get_range_partbound_string(spec->upperdatums));\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telog(ERROR, \"unrecognized partition strategy: %d\",\n\t\t\t\t\t\t\t (int) spec->strategy);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_JsonValueExpr:\n\t\t\t{\n\t\t\t\tJsonValueExpr *jve = (JsonValueExpr *) node;\n\n\t\t\t\tget_rule_expr((Node *) jve->raw_expr, context, false);\n\t\t\t\tget_json_format(jve->format, context->buf);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_JsonConstructorExpr:\n\t\t\tget_json_constructor((JsonConstructorExpr *) node, context, false);\n\t\t\tbreak;\n\n\t\tcase T_JsonIsPredicate:\n\t\t\t{\n\t\t\t\tJsonIsPredicate *pred = (JsonIsPredicate *) node;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(context->buf, '(');\n\n\t\t\t\tget_rule_expr_paren(pred->expr, context, true, node);\n\n\t\t\t\tappendStringInfoString(context->buf, \" IS JSON\");\n\n\t\t\t\t/* TODO: handle FORMAT clause */\n\n\t\t\t\tswitch (pred->item_type)\n\t\t\t\t{\n\t\t\t\t\tcase JS_TYPE_SCALAR:\n\t\t\t\t\t\tappendStringInfoString(context->buf, \" SCALAR\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase JS_TYPE_ARRAY:\n\t\t\t\t\t\tappendStringInfoString(context->buf, \" ARRAY\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase JS_TYPE_OBJECT:\n\t\t\t\t\t\tappendStringInfoString(context->buf, \" OBJECT\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (pred->unique_keys)\n\t\t\t\t\tappendStringInfoString(context->buf, \" WITH UNIQUE KEYS\");\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(context->buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_JsonExpr:\n\t\t\t{\n\t\t\t\tJsonExpr   *jexpr = (JsonExpr *) node;\n\n\t\t\t\tswitch (jexpr->op)\n\t\t\t\t{\n\t\t\t\t\tcase JSON_EXISTS_OP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"JSON_EXISTS(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase JSON_QUERY_OP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"JSON_QUERY(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase JSON_VALUE_OP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"JSON_VALUE(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telog(ERROR, \"unrecognized JsonExpr op: %d\",\n\t\t\t\t\t\t\t (int) jexpr->op);\n\t\t\t\t}\n\n\t\t\t\tget_rule_expr(jexpr->formatted_expr, context, showimplicit);\n\n\t\t\t\tappendStringInfoString(buf, \", \");\n\n\t\t\t\tget_json_path_spec(jexpr->path_spec, context, showimplicit);\n\n\t\t\t\tif (jexpr->passing_values)\n\t\t\t\t{\n\t\t\t\t\tListCell   *lc1,\n\t\t\t\t\t\t\t   *lc2;\n\t\t\t\t\tbool\t\tneedcomma = false;\n\n\t\t\t\t\tappendStringInfoString(buf, \" PASSING \");\n\n\t\t\t\t\tforboth(lc1, jexpr->passing_names,\n\t\t\t\t\t\t\tlc2, jexpr->passing_values)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (needcomma)\n\t\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\t\tneedcomma = true;\n\n\t\t\t\t\t\tget_rule_expr((Node *) lfirst(lc2), context, showimplicit);\n\t\t\t\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t\t\t\t ((String *) lfirst_node(String, lc1))->sval);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (jexpr->op != JSON_EXISTS_OP ||\n\t\t\t\t\tjexpr->returning->typid != BOOLOID)\n\t\t\t\t\tget_json_returning(jexpr->returning, context->buf,\n\t\t\t\t\t\t\t\t\t   jexpr->op == JSON_QUERY_OP);\n\n\t\t\t\tget_json_expr_options(jexpr, context,\n\t\t\t\t\t\t\t\t\t  jexpr->op != JSON_EXISTS_OP ?\n\t\t\t\t\t\t\t\t\t  JSON_BEHAVIOR_NULL :\n\t\t\t\t\t\t\t\t\t  JSON_BEHAVIOR_FALSE);\n\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_List:\n\t\t\t{\n\t\t\t\tchar\t   *sep;\n\t\t\t\tListCell   *l;\n\n\t\t\t\tsep = \"\";\n\t\t\t\tforeach(l, (List *) node)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\tget_rule_expr((Node *) lfirst(l), context, showimplicit);\n\t\t\t\t\tsep = \", \";\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_TableFunc:\n\t\t\tget_tablefunc((TableFunc *) node, context, showimplicit);\n\t\t\tbreak;\n\n\t\tcase T_CallStmt:\n\t\t\tget_proc_expr((CallStmt *) node, context, showimplicit);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\telog(ERROR, \"unrecognized node type: %d\", (int) nodeTag(node));\n\t\t\tbreak;\n\t}\n}\n\n/*\n * get_rule_expr_toplevel\t\t- Parse back a toplevel expression\n *\n * Same as get_rule_expr(), except that if the expr is just a Var, we pass\n * istoplevel = true not false to get_variable().  This causes whole-row Vars\n * to get printed with decoration that will prevent expansion of \"*\".\n * We need to use this in contexts such as ROW() and VALUES(), where the\n * parser would expand \"foo.*\" appearing at top level.  (In principle we'd\n * use this in get_target_list() too, but that has additional worries about\n * whether to print AS, so it needs to invoke get_variable() directly anyway.)\n */\nstatic void\nget_rule_expr_toplevel(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit)\n{\n\tif (node && IsA(node, Var))\n\t\t(void) get_variable((Var *) node, 0, true, context);\n\telse\n\t\tget_rule_expr(node, context, showimplicit);\n}\n\n/*\n * get_rule_list_toplevel\t\t- Parse back a list of toplevel expressions\n *\n * Apply get_rule_expr_toplevel() to each element of a List.\n *\n * This adds commas between the expressions, but caller is responsible\n * for printing surrounding decoration.\n */\nstatic void\nget_rule_list_toplevel(List *lst, deparse_context *context,\n\t\t\t\t\t   bool showimplicit)\n{\n\tconst char *sep;\n\tListCell   *lc;\n\n\tsep = \"\";\n\tforeach(lc, lst)\n\t{\n\t\tNode\t   *e = (Node *) lfirst(lc);\n\n\t\tappendStringInfoString(context->buf, sep);\n\t\tget_rule_expr_toplevel(e, context, showimplicit);\n\t\tsep = \", \";\n\t}\n}\n\n/*\n * get_rule_expr_funccall\t\t- Parse back a function-call expression\n *\n * Same as get_rule_expr(), except that we guarantee that the output will\n * look like a function call, or like one of the things the grammar treats as\n * equivalent to a function call (see the func_expr_windowless production).\n * This is needed in places where the grammar uses func_expr_windowless and\n * you can't substitute a parenthesized a_expr.  If what we have isn't going\n * to look like a function call, wrap it in a dummy CAST() expression, which\n * will satisfy the grammar --- and, indeed, is likely what the user wrote to\n * produce such a thing.\n */\nstatic void\nget_rule_expr_funccall(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit)\n{\n\tif (looks_like_function(node))\n\t\tget_rule_expr(node, context, showimplicit);\n\telse\n\t{\n\t\tStringInfo\tbuf = context->buf;\n\n\t\tappendStringInfoString(buf, \"CAST(\");\n\t\t/* no point in showing any top-level implicit cast */\n\t\tget_rule_expr(node, context, false);\n\t\tappendStringInfo(buf, \" AS %s)\",\n\t\t\t\t\t\t format_type_with_typemod(exprType(node),\n\t\t\t\t\t\t\t\t\t\t\t\t  exprTypmod(node)));\n\t}\n}\n\n/*\n * Helper function to identify node types that satisfy func_expr_windowless.\n * If in doubt, \"false\" is always a safe answer.\n */\nstatic bool\nlooks_like_function(Node *node)\n{\n\tif (node == NULL)\n\t\treturn false;\t\t\t/* probably shouldn't happen */\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_FuncExpr:\n\t\t\t/* OK, unless it's going to deparse as a cast */\n\t\t\treturn (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||\n\t\t\t\t\t((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);\n\t\tcase T_NullIfExpr:\n\t\tcase T_CoalesceExpr:\n\t\tcase T_MinMaxExpr:\n\t\tcase T_SQLValueFunction:\n\t\tcase T_XmlExpr:\n\t\tcase T_JsonExpr:\n\t\t\t/* these are all accepted by func_expr_common_subexpr */\n\t\t\treturn true;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\treturn false;\n}\n\n/*\n * get_oper_expr\t\t\t- Parse back an OpExpr node\n */\nstatic void\nget_oper_expr(OpExpr *expr, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\topno = expr->opno;\n\tList\t   *args = expr->args;\n\n\tif (!PRETTY_PAREN(context))\n\t\tappendStringInfoChar(buf, '(');\n\tif (list_length(args) == 2)\n\t{\n\t\t/* binary operator */\n\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\n\t\tget_rule_expr_paren(arg1, context, true, (Node *) expr);\n\t\tappendStringInfo(buf, \" %s \",\n\t\t\t\t\t\t generate_operator_name(opno,\n\t\t\t\t\t\t\t\t\t\t\t\texprType(arg1),\n\t\t\t\t\t\t\t\t\t\t\t\texprType(arg2)));\n\t\tget_rule_expr_paren(arg2, context, true, (Node *) expr);\n\t}\n\telse\n\t{\n\t\t/* prefix operator */\n\t\tNode\t   *arg = (Node *) linitial(args);\n\n\t\tappendStringInfo(buf, \"%s \",\n\t\t\t\t\t\t generate_operator_name(opno,\n\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid,\n\t\t\t\t\t\t\t\t\t\t\t\texprType(arg)));\n\t\tget_rule_expr_paren(arg, context, true, (Node *) expr);\n\t}\n\tif (!PRETTY_PAREN(context))\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_func_expr\t\t\t- Parse back a FuncExpr node\n */\nstatic void\nget_func_expr(FuncExpr *expr, deparse_context *context,\n\t\t\t  bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\tfuncoid = expr->funcid;\n\tOid\t\t\targtypes[FUNC_MAX_ARGS];\n\tint\t\t\tnargs;\n\tList\t   *argnames;\n\tbool\t\tuse_variadic;\n\tListCell   *l;\n\n\t/*\n\t * If the function call came from an implicit coercion, then just show the\n\t * first argument --- unless caller wants to see implicit coercions.\n\t */\n\tif (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)\n\t{\n\t\tget_rule_expr_paren((Node *) linitial(expr->args), context,\n\t\t\t\t\t\t\tfalse, (Node *) expr);\n\t\treturn;\n\t}\n\n\t/*\n\t * If the function call came from a cast, then show the first argument\n\t * plus an explicit cast operation.\n\t */\n\tif (expr->funcformat == COERCE_EXPLICIT_CAST ||\n\t\texpr->funcformat == COERCE_IMPLICIT_CAST)\n\t{\n\t\tNode\t   *arg = linitial(expr->args);\n\t\tOid\t\t\trettype = expr->funcresulttype;\n\t\tint32\t\tcoercedTypmod;\n\n\t\t/* Get the typmod if this is a length-coercion function */\n\t\t(void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);\n\n\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t  rettype, coercedTypmod,\n\t\t\t\t\t\t  (Node *) expr);\n\n\t\treturn;\n\t}\n\n\t/*\n\t * If the function was called using one of the SQL spec's random special\n\t * syntaxes, try to reproduce that.  If we don't recognize the function,\n\t * fall through.\n\t */\n\tif (expr->funcformat == COERCE_SQL_SYNTAX)\n\t{\n\t\tif (get_func_sql_syntax(expr, context))\n\t\t\treturn;\n\t}\n\n\n\t/*\n\t * Normal function: display as proname(args).  First we need to extract\n\t * the argument datatypes.\n\t */\n\tif (list_length(expr->args) > FUNC_MAX_ARGS)\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_TOO_MANY_ARGUMENTS),\n\t\t\t\t errmsg(\"too many arguments\")));\n\tnargs = 0;\n\targnames = NIL;\n\tforeach(l, expr->args)\n\t{\n\t\tNode\t   *arg = (Node *) lfirst(l);\n\n\t\tif (IsA(arg, NamedArgExpr))\n\t\t\targnames = lappend(argnames, ((NamedArgExpr *) arg)->name);\n\t\targtypes[nargs] = exprType(arg);\n\t\tnargs++;\n\t}\n\n\tappendStringInfo(buf, \"%s(\",\n\t\t\t\t\t generate_function_name(funcoid, nargs,\n\t\t\t\t\t\t\t\t\t\t\targnames, argtypes,\n\t\t\t\t\t\t\t\t\t\t\texpr->funcvariadic,\n\t\t\t\t\t\t\t\t\t\t\t&use_variadic,\n\t\t\t\t\t\t\t\t\t\t\tcontext->inGroupBy));\n\tnargs = 0;\n\tforeach(l, expr->args)\n\t{\n\t\tif (nargs++ > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tif (use_variadic && lnext(expr->args, l) == NULL)\n\t\t\tappendStringInfoString(buf, \"VARIADIC \");\n\t\tget_rule_expr((Node *) lfirst(l), context, true);\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_proc_expr\t\t\t- Parse back a CallStmt node\n */\nstatic void\nget_proc_expr(CallStmt *stmt, deparse_context *context,\n\t\t\t  bool showimplicit)\n{\n\tStringInfo buf = context->buf;\n\tOid        functionOid = stmt->funcexpr->funcid;\n\tbool       use_variadic;\n\tOid        *argumentTypes;\n\tList       *finalArgumentList = NIL;\n\tListCell   *argumentCell;\n\tList       *namedArgList = NIL;\n\tint        numberOfArgs = -1;\n\n\tif (!get_merged_argument_list(stmt, &namedArgList, &argumentTypes,\n\t\t\t\t\t\t\t\t\t\t&finalArgumentList, &numberOfArgs))\n\t{\n\t\t/* Nothing merged i.e. no OUT arguments */\n\t\tget_func_expr((FuncExpr *) stmt->funcexpr, context, showimplicit);\n\t\treturn;\n\t}\n\n\tappendStringInfo(buf, \"%s(\",\n\t\t\t\t\t generate_function_name(functionOid, numberOfArgs,\n\t\t\t\t\t\t\t\t\t\tnamedArgList, argumentTypes,\n\t\t\t\t\t\t\t\t\t\tstmt->funcexpr->funcvariadic,\n\t\t\t\t\t\t\t\t\t\t&use_variadic,\n\t\t\t\t\t\t\t\t\t\tcontext->inGroupBy));\n\tint argNumber = 0;\n\tforeach(argumentCell, finalArgumentList)\n\t{\n\t\tif (argNumber++ > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tif (use_variadic && lnext(finalArgumentList, argumentCell) == NULL)\n\t\t\tappendStringInfoString(buf, \"VARIADIC \");\n\t\tget_rule_expr((Node *) lfirst(argumentCell), context, true);\n\t\targNumber++;\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_agg_expr\t\t\t- Parse back an Aggref node\n */\nstatic void\nget_agg_expr(Aggref *aggref, deparse_context *context,\n\t\t\t Aggref *original_aggref)\n{\n\tget_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,\n\t\t\t\t\t\tfalse);\n}\n\n/*\n * get_agg_expr_helper\t\t- subroutine for get_agg_expr and\n *\t\t\t\t\t\t\tget_json_agg_constructor\n */\nstatic void\nget_agg_expr_helper(Aggref *aggref, deparse_context *context,\n\t\t\t\t\tAggref *original_aggref, const char *funcname,\n\t\t\t\t\tconst char *options, bool is_json_objectagg)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\targtypes[FUNC_MAX_ARGS];\n\tint\t\t\tnargs;\n\tbool use_variadic = false;\n\n\t/*\n\t * For a combining aggregate, we look up and deparse the corresponding\n\t * partial aggregate instead.  This is necessary because our input\n\t * argument list has been replaced; the new argument list always has just\n\t * one element, which will point to a partial Aggref that supplies us with\n\t * transition states to combine.\n\t */\n\tif (DO_AGGSPLIT_COMBINE(aggref->aggsplit))\n\t{\n\t\tTargetEntry *tle;\n\n\n\t\tAssert(list_length(aggref->args) == 1);\n\t\ttle = linitial_node(TargetEntry, aggref->args);\n\t\tresolve_special_varno((Node *) tle->expr, context,\n\t\t\t\t\t\t\t  get_agg_combine_expr, original_aggref);\n\t\treturn;\n\t}\n\n\t/*\n\t * Mark as PARTIAL, if appropriate.  We look to the original aggref so as\n\t * to avoid printing this when recursing from the code just above.\n\t */\n\tif (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit))\n\t\tappendStringInfoString(buf, \"PARTIAL \");\n\n\t/* Extract the argument types as seen by the parser */\n\tnargs = get_aggregate_argtypes(aggref, argtypes);\n\n\tif (!funcname)\n\t\tfuncname = generate_function_name(aggref->aggfnoid, nargs, NIL,\n\t\t\t\t\t\t\t\t\t\t  argtypes, aggref->aggvariadic,\n\t\t\t\t\t\t\t\t\t\t  &use_variadic,\n\t\t\t\t\t\t\t\t\t\t  context->inGroupBy);\n\n\t/* Print the aggregate name, schema-qualified if needed */\n\tappendStringInfo(buf, \"%s(%s\", funcname,\n\t\t\t\t\t (aggref->aggdistinct != NIL) ? \"DISTINCT \" : \"\");\n\n\tif (AGGKIND_IS_ORDERED_SET(aggref->aggkind))\n\t{\n\t\t/*\n\t\t * Ordered-set aggregates do not use \"*\" syntax.  Also, we needn't\n\t\t * worry about inserting VARIADIC.  So we can just dump the direct\n\t\t * args as-is.\n\t\t */\n\t\tAssert(!aggref->aggvariadic);\n\t\tget_rule_expr((Node *) aggref->aggdirectargs, context, true);\n\t\tAssert(aggref->aggorder != NIL);\n\t\tappendStringInfoString(buf, \") WITHIN GROUP (ORDER BY \");\n\t\tget_rule_orderby(aggref->aggorder, aggref->args, false, context);\n\t}\n\telse\n\t{\n\t\t/* aggstar can be set only in zero-argument aggregates */\n\t\tif (aggref->aggstar)\n\t\t\tappendStringInfoChar(buf, '*');\n\t\telse\n\t\t{\n\t\t\tListCell   *l;\n\t\t\tint\t\t\ti;\n\n\t\t\ti = 0;\n\t\t\tforeach(l, aggref->args)\n\t\t\t{\n\t\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\t\t\t\tNode\t   *arg = (Node *) tle->expr;\n\n\t\t\t\tAssert(!IsA(arg, NamedArgExpr));\n\t\t\t\tif (tle->resjunk)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (i++ > 0)\n\t\t\t\t{\n\t\t\t\t\tif (is_json_objectagg)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * the ABSENT ON NULL and WITH UNIQUE args are printed\n\t\t\t\t\t\t * separately, so ignore them here\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (i > 2)\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tappendStringInfoString(buf, \" : \");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t}\n\t\t\t\tif (use_variadic && i == nargs)\n\t\t\t\t\tappendStringInfoString(buf, \"VARIADIC \");\n\t\t\t\tget_rule_expr(arg, context, true);\n\t\t\t}\n\t\t}\n\n\t\tif (aggref->aggorder != NIL)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" ORDER BY \");\n\t\t\tget_rule_orderby(aggref->aggorder, aggref->args, false, context);\n\t\t}\n\t}\n\n\tif (options)\n\t\tappendStringInfoString(buf, options);\n\n\tif (aggref->aggfilter != NULL)\n\t{\n\t\tappendStringInfoString(buf, \") FILTER (WHERE \");\n\t\tget_rule_expr((Node *) aggref->aggfilter, context, false);\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * This is a helper function for get_agg_expr().  It's used when we deparse\n * a combining Aggref; resolve_special_varno locates the corresponding partial\n * Aggref and then calls this.\n */\nstatic void\nget_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)\n{\n\tAggref\t   *aggref;\n\tAggref\t   *original_aggref = callback_arg;\n\n\tif (!IsA(node, Aggref))\n\t\telog(ERROR, \"combining Aggref does not point to an Aggref\");\n\n\taggref = (Aggref *) node;\n\tget_agg_expr(aggref, context, original_aggref);\n}\n\n/*\n * get_windowfunc_expr\t- Parse back a WindowFunc node\n */\nstatic void\nget_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)\n{\n\tget_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);\n}\n\n\n/*\n * get_windowfunc_expr_helper\t- subroutine for get_windowfunc_expr and\n *\t\t\t\t\t\t\t\tget_json_agg_constructor\n */\nstatic void\nget_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,\n\t\t\t\t\t\t   const char *funcname, const char *options,\n\t\t\t\t\t\t   bool is_json_objectagg)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\targtypes[FUNC_MAX_ARGS];\n\tint\t\t\tnargs;\n\tList\t   *argnames;\n\tListCell   *l;\n\n\tif (list_length(wfunc->args) > FUNC_MAX_ARGS)\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_TOO_MANY_ARGUMENTS),\n\t\t\t\t errmsg(\"too many arguments\")));\n\tnargs = 0;\n\targnames = NIL;\n\tforeach(l, wfunc->args)\n\t{\n\t\tNode\t   *arg = (Node *) lfirst(l);\n\n\t\tif (IsA(arg, NamedArgExpr))\n\t\t\targnames = lappend(argnames, ((NamedArgExpr *) arg)->name);\n\t\targtypes[nargs] = exprType(arg);\n\t\tnargs++;\n\t}\n\n\tif (!funcname)\n\t\tfuncname = generate_function_name(wfunc->winfnoid, nargs, argnames,\n\t\t\t\t\t\t\t\t\t\t  argtypes, false, NULL,\n\t\t\t\t\t\t\t\t\t\t  context->inGroupBy);\n\n\tappendStringInfo(buf, \"%s(\", funcname);\n\n\t/* winstar can be set only in zero-argument aggregates */\n\tif (wfunc->winstar)\n\t\tappendStringInfoChar(buf, '*');\n\telse\n\t{\n\t\tif (is_json_objectagg)\n\t\t{\n\t\t\tget_rule_expr((Node *) linitial(wfunc->args), context, false);\n\t\t\tappendStringInfoString(buf, \" : \");\n\t\t\tget_rule_expr((Node *) lsecond(wfunc->args), context, false);\n\t\t}\n\t\telse\n\t\t\tget_rule_expr((Node *) wfunc->args, context, true);\n\t}\n\n\tif (options)\n\t\tappendStringInfoString(buf, options);\n\n\tif (wfunc->aggfilter != NULL)\n\t{\n\t\tappendStringInfoString(buf, \") FILTER (WHERE \");\n\t\tget_rule_expr((Node *) wfunc->aggfilter, context, false);\n\t}\n\n\tappendStringInfoString(buf, \") OVER \");\n\n\tforeach(l, context->windowClause)\n\t{\n\t\tWindowClause *wc = (WindowClause *) lfirst(l);\n\n\t\tif (wc->winref == wfunc->winref)\n\t\t{\n\t\t\tif (wc->name)\n\t\t\t\tappendStringInfoString(buf, quote_identifier(wc->name));\n\t\t\telse\n\t\t\t\tget_rule_windowspec(wc, context->targetList, context);\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (l == NULL)\n\t{\n\t\tif (context->windowClause)\n\t\t\telog(ERROR, \"could not find window clause for winref %u\",\n\t\t\t\t wfunc->winref);\n\n\t\t/*\n\t\t * In EXPLAIN, we don't have window context information available, so\n\t\t * we have to settle for this:\n\t\t */\n\t\tappendStringInfoString(buf, \"(?)\");\n\t}\n}\n\n/*\n * get_func_sql_syntax\t\t- Parse back a SQL-syntax function call\n *\n * Returns true if we successfully deparsed, false if we did not\n * recognize the function.\n */\nstatic bool\nget_func_sql_syntax(FuncExpr *expr, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\tfuncoid = expr->funcid;\n\n\tswitch (funcoid)\n\t{\n\t\tcase F_TIMEZONE_INTERVAL_TIMESTAMP:\n\t\tcase F_TIMEZONE_INTERVAL_TIMESTAMPTZ:\n\t\tcase F_TIMEZONE_INTERVAL_TIMETZ:\n\t\tcase F_TIMEZONE_TEXT_TIMESTAMP:\n\t\tcase F_TIMEZONE_TEXT_TIMESTAMPTZ:\n\t\tcase F_TIMEZONE_TEXT_TIMETZ:\n\t\t\t/* AT TIME ZONE ... note reversed argument order */\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr_paren((Node *) lsecond(expr->args), context, false,\n\t\t\t\t\t\t\t\t(Node *) expr);\n\t\t\tappendStringInfoString(buf, \" AT TIME ZONE \");\n\t\t\tget_rule_expr_paren((Node *) linitial(expr->args), context, false,\n\t\t\t\t\t\t\t\t(Node *) expr);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_TIMEZONE_TIMESTAMP:\n\t\tcase F_TIMEZONE_TIMESTAMPTZ:\n\t\tcase F_TIMEZONE_TIMETZ:\n\t\t\t/* AT LOCAL */\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr_paren((Node *) linitial(expr->args), context, false,\n\t\t\t\t\t\t\t\t(Node *) expr);\n\t\t\tappendStringInfoString(buf, \" AT LOCAL)\");\n\t\t\treturn true;\n\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_TIMESTAMPTZ:\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ:\n\t\tcase F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_TIMESTAMP:\n\t\tcase F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_TIMESTAMP:\n\t\tcase F_OVERLAPS_TIMETZ_TIMETZ_TIMETZ_TIMETZ:\n\t\tcase F_OVERLAPS_TIME_INTERVAL_TIME_INTERVAL:\n\t\tcase F_OVERLAPS_TIME_INTERVAL_TIME_TIME:\n\t\tcase F_OVERLAPS_TIME_TIME_TIME_INTERVAL:\n\t\tcase F_OVERLAPS_TIME_TIME_TIME_TIME:\n\t\t\t/* (x1, x2) OVERLAPS (y1, y2) */\n\t\t\tappendStringInfoString(buf, \"((\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \") OVERLAPS (\");\n\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tget_rule_expr((Node *) lfourth(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\treturn true;\n\n\t\tcase F_EXTRACT_TEXT_DATE:\n\t\tcase F_EXTRACT_TEXT_TIME:\n\t\tcase F_EXTRACT_TEXT_TIMETZ:\n\t\tcase F_EXTRACT_TEXT_TIMESTAMP:\n\t\tcase F_EXTRACT_TEXT_TIMESTAMPTZ:\n\t\tcase F_EXTRACT_TEXT_INTERVAL:\n\t\t\t/* EXTRACT (x FROM y) */\n\t\t\tappendStringInfoString(buf, \"EXTRACT(\");\n\t\t\t{\n\t\t\t\tConst\t   *con = (Const *) linitial(expr->args);\n\n\t\t\t\tAssert(IsA(con, Const) &&\n\t\t\t\t\t   con->consttype == TEXTOID &&\n\t\t\t\t\t   !con->constisnull);\n\t\t\t\tappendStringInfoString(buf, TextDatumGetCString(con->constvalue));\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_IS_NORMALIZED:\n\t\t\t/* IS xxx NORMALIZED */\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr_paren((Node *) linitial(expr->args), context, false,\n\t\t\t\t\t\t\t\t(Node *) expr);\n\t\t\tappendStringInfoString(buf, \" IS\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tConst\t   *con = (Const *) lsecond(expr->args);\n\n\t\t\t\tAssert(IsA(con, Const) &&\n\t\t\t\t\t   con->consttype == TEXTOID &&\n\t\t\t\t\t   !con->constisnull);\n\t\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\t\t\t\t TextDatumGetCString(con->constvalue));\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" NORMALIZED)\");\n\t\t\treturn true;\n\n\t\tcase F_PG_COLLATION_FOR:\n\t\t\t/* COLLATION FOR */\n\t\t\tappendStringInfoString(buf, \"COLLATION FOR (\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_NORMALIZE:\n\t\t\t/* NORMALIZE() */\n\t\t\tappendStringInfoString(buf, \"NORMALIZE(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tConst\t   *con = (Const *) lsecond(expr->args);\n\n\t\t\t\tAssert(IsA(con, Const) &&\n\t\t\t\t\t   con->consttype == TEXTOID &&\n\t\t\t\t\t   !con->constisnull);\n\t\t\t\tappendStringInfo(buf, \", %s\",\n\t\t\t\t\t\t\t\t TextDatumGetCString(con->constvalue));\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_OVERLAY_BIT_BIT_INT4:\n\t\tcase F_OVERLAY_BIT_BIT_INT4_INT4:\n\t\tcase F_OVERLAY_BYTEA_BYTEA_INT4:\n\t\tcase F_OVERLAY_BYTEA_BYTEA_INT4_INT4:\n\t\tcase F_OVERLAY_TEXT_TEXT_INT4:\n\t\tcase F_OVERLAY_TEXT_TEXT_INT4_INT4:\n\t\t\t/* OVERLAY() */\n\t\t\tappendStringInfoString(buf, \"OVERLAY(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" PLACING \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\tif (list_length(expr->args) == 4)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" FOR \");\n\t\t\t\tget_rule_expr((Node *) lfourth(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_POSITION_BIT_BIT:\n\t\tcase F_POSITION_BYTEA_BYTEA:\n\t\tcase F_POSITION_TEXT_TEXT:\n\t\t\t/* POSITION() ... extra parens since args are b_expr not a_expr */\n\t\t\tappendStringInfoString(buf, \"POSITION((\");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \") IN (\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\treturn true;\n\n\t\tcase F_SUBSTRING_BIT_INT4:\n\t\tcase F_SUBSTRING_BIT_INT4_INT4:\n\t\tcase F_SUBSTRING_BYTEA_INT4:\n\t\tcase F_SUBSTRING_BYTEA_INT4_INT4:\n\t\tcase F_SUBSTRING_TEXT_INT4:\n\t\tcase F_SUBSTRING_TEXT_INT4_INT4:\n\t\t\t/* SUBSTRING FROM/FOR (i.e., integer-position variants) */\n\t\t\tappendStringInfoString(buf, \"SUBSTRING(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tif (list_length(expr->args) == 3)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" FOR \");\n\t\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_SUBSTRING_TEXT_TEXT_TEXT:\n\t\t\t/* SUBSTRING SIMILAR/ESCAPE */\n\t\t\tappendStringInfoString(buf, \"SUBSTRING(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" SIMILAR \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" ESCAPE \");\n\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_BTRIM_BYTEA_BYTEA:\n\t\tcase F_BTRIM_TEXT:\n\t\tcase F_BTRIM_TEXT_TEXT:\n\t\t\t/* TRIM() */\n\t\t\tappendStringInfoString(buf, \"TRIM(BOTH\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_LTRIM_BYTEA_BYTEA:\n\t\tcase F_LTRIM_TEXT:\n\t\tcase F_LTRIM_TEXT_TEXT:\n\t\t\t/* TRIM() */\n\t\t\tappendStringInfoString(buf, \"TRIM(LEADING\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_RTRIM_BYTEA_BYTEA:\n\t\tcase F_RTRIM_TEXT:\n\t\tcase F_RTRIM_TEXT_TEXT:\n\t\t\t/* TRIM() */\n\t\t\tappendStringInfoString(buf, \"TRIM(TRAILING\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_SYSTEM_USER:\n\t\t\tappendStringInfoString(buf, \"SYSTEM_USER\");\n\t\t\treturn true;\n\n\t\tcase F_XMLEXISTS:\n\t\t\t/* XMLEXISTS ... extra parens because args are c_expr */\n\t\t\tappendStringInfoString(buf, \"XMLEXISTS((\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \") PASSING (\");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n/* ----------\n * get_coercion_expr\n *\n *\tMake a string representation of a value coerced to a specific type\n * ----------\n */\nstatic void\nget_coercion_expr(Node *arg, deparse_context *context,\n\t\t\t\t  Oid resulttype, int32 resulttypmod,\n\t\t\t\t  Node *parentNode)\n{\n\tStringInfo\tbuf = context->buf;\n\n\t/*\n\t * Since parse_coerce.c doesn't immediately collapse application of\n\t * length-coercion functions to constants, what we'll typically see in\n\t * such cases is a Const with typmod -1 and a length-coercion function\n\t * right above it.  Avoid generating redundant output. However, beware of\n\t * suppressing casts when the user actually wrote something like\n\t * 'foo'::text::char(3).\n\t *\n\t * Note: it might seem that we are missing the possibility of needing to\n\t * print a COLLATE clause for such a Const.  However, a Const could only\n\t * have nondefault collation in a post-constant-folding tree, in which the\n\t * length coercion would have been folded too.  See also the special\n\t * handling of CollateExpr in coerce_to_target_type(): any collation\n\t * marking will be above the coercion node, not below it.\n\t */\n\tif (arg && IsA(arg, Const) &&\n\t\t((Const *) arg)->consttype == resulttype &&\n\t\t((Const *) arg)->consttypmod == -1)\n\t{\n\t\t/* Show the constant without normal ::typename decoration */\n\t\tget_const_expr((Const *) arg, context, -1);\n\t}\n\telse\n\t{\n\t\tif (!PRETTY_PAREN(context))\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_rule_expr_paren(arg, context, false, parentNode);\n\t\tif (!PRETTY_PAREN(context))\n\t\t\tappendStringInfoChar(buf, ')');\n\t}\n\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t format_type_with_typemod(resulttype, resulttypmod));\n}\n\n/* ----------\n * get_const_expr\n *\n *\tMake a string representation of a Const\n *\n * showtype can be -1 to never show \"::typename\" decoration, or +1 to always\n * show it, or 0 to show it only if the constant wouldn't be assumed to be\n * the right type by default.\n *\n * If the Const's collation isn't default for its type, show that too.\n * We mustn't do this when showtype is -1 (since that means the caller will\n * print \"::typename\", and we can't put a COLLATE clause in between).  It's\n * caller's responsibility that collation isn't missed in such cases.\n * ----------\n */\nstatic void\nget_const_expr(Const *constval, deparse_context *context, int showtype)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\ttypoutput;\n\tbool\t\ttypIsVarlena;\n\tchar\t   *extval;\n\tbool\t\tneedlabel = false;\n\n\tif (constval->constisnull)\n\t{\n\t\t/*\n\t\t * Always label the type of a NULL constant to prevent misdecisions\n\t\t * about type when reparsing.\n\t\t */\n\t\tappendStringInfoString(buf, \"NULL\");\n\t\tif (showtype >= 0)\n\t\t{\n\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t format_type_with_typemod(constval->consttype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  constval->consttypmod));\n\t\t\tget_const_collation(constval, context);\n\t\t}\n\t\treturn;\n\t}\n\n\tgetTypeOutputInfo(constval->consttype,\n\t\t\t\t\t  &typoutput, &typIsVarlena);\n\n\textval = OidOutputFunctionCall(typoutput, constval->constvalue);\n\n\tswitch (constval->consttype)\n\t{\n\t\tcase INT4OID:\n\n\t\t\t/*\n\t\t\t * INT4 can be printed without any decoration, unless it is\n\t\t\t * negative; in that case print it as '-nnn'::integer to ensure\n\t\t\t * that the output will re-parse as a constant, not as a constant\n\t\t\t * plus operator.  In most cases we could get away with printing\n\t\t\t * (-nnn) instead, because of the way that gram.y handles negative\n\t\t\t * literals; but that doesn't work for INT_MIN, and it doesn't\n\t\t\t * seem that much prettier anyway.\n\t\t\t */\n\t\t\tif (extval[0] != '-')\n\t\t\t\tappendStringInfoString(buf, extval);\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"'%s'\", extval);\n\t\t\t\tneedlabel = true;\t/* we must attach a cast */\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase NUMERICOID:\n\n\t\t\t/*\n\t\t\t * NUMERIC can be printed without quotes if it looks like a float\n\t\t\t * constant (not an integer, and not Infinity or NaN) and doesn't\n\t\t\t * have a leading sign (for the same reason as for INT4).\n\t\t\t */\n\t\t\tif (isdigit((unsigned char) extval[0]) &&\n\t\t\t\tstrcspn(extval, \"eE.\") != strlen(extval))\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, extval);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"'%s'\", extval);\n\t\t\t\tneedlabel = true;\t/* we must attach a cast */\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase BITOID:\n\t\tcase VARBITOID:\n\t\t\tappendStringInfo(buf, \"B'%s'\", extval);\n\t\t\tbreak;\n\n\t\tcase BOOLOID:\n\t\t\tif (strcmp(extval, \"t\") == 0)\n\t\t\t\tappendStringInfoString(buf, \"true\");\n\t\t\telse\n\t\t\t\tappendStringInfoString(buf, \"false\");\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tsimple_quote_literal(buf, extval);\n\t\t\tbreak;\n\t}\n\n\tpfree(extval);\n\n\tif (showtype < 0)\n\t\treturn;\n\n\t/*\n\t * For showtype == 0, append ::typename unless the constant will be\n\t * implicitly typed as the right type when it is read in.\n\t *\n\t * XXX this code has to be kept in sync with the behavior of the parser,\n\t * especially make_const.\n\t */\n\tswitch (constval->consttype)\n\t{\n\t\tcase BOOLOID:\n\t\tcase UNKNOWNOID:\n\t\t\t/* These types can be left unlabeled */\n\t\t\tneedlabel = false;\n\t\t\tbreak;\n\t\tcase INT4OID:\n\t\t\t/* We determined above whether a label is needed */\n\t\t\tbreak;\n\t\tcase NUMERICOID:\n\n\t\t\t/*\n\t\t\t * Float-looking constants will be typed as numeric, which we\n\t\t\t * checked above; but if there's a nondefault typmod we need to\n\t\t\t * show it.\n\t\t\t */\n\t\t\tneedlabel |= (constval->consttypmod >= 0);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tneedlabel = true;\n\t\t\tbreak;\n\t}\n\tif (needlabel || showtype > 0)\n\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t format_type_with_typemod(constval->consttype,\n\t\t\t\t\t\t\t\t\t\t\t\t  constval->consttypmod));\n\n\tget_const_collation(constval, context);\n}\n\n/*\n * helper for get_const_expr: append COLLATE if needed\n */\nstatic void\nget_const_collation(Const *constval, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (OidIsValid(constval->constcollid))\n\t{\n\t\tOid\t\t\ttypcollation = get_typcollation(constval->consttype);\n\n\t\tif (constval->constcollid != typcollation)\n\t\t{\n\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t generate_collation_name(constval->constcollid));\n\t\t}\n\t}\n}\n\n/*\n * get_json_path_spec\t\t- Parse back a JSON path specification\n */\nstatic void\nget_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)\n{\n\tif (IsA(path_spec, Const))\n\t\tget_const_expr((Const *) path_spec, context, -1);\n\telse\n\t\tget_rule_expr(path_spec, context, showimplicit);\n}\n\n/*\n * get_json_format\t\t\t- Parse back a JsonFormat node\n */\nstatic void\nget_json_format(JsonFormat *format, StringInfo buf)\n{\n\tif (format->format_type == JS_FORMAT_DEFAULT)\n\t\treturn;\n\n\tappendStringInfoString(buf,\n\t\t\t\t\t\t   format->format_type == JS_FORMAT_JSONB ?\n\t\t\t\t\t\t   \" FORMAT JSONB\" : \" FORMAT JSON\");\n\n\tif (format->encoding != JS_ENC_DEFAULT)\n\t{\n\t\tconst char *encoding;\n\n\t\tencoding =\n\t\t\tformat->encoding == JS_ENC_UTF16 ? \"UTF16\" :\n\t\t\tformat->encoding == JS_ENC_UTF32 ? \"UTF32\" : \"UTF8\";\n\n\t\tappendStringInfo(buf, \" ENCODING %s\", encoding);\n\t}\n}\n\n/*\n * get_json_returning\t\t- Parse back a JsonReturning structure\n */\nstatic void\nget_json_returning(JsonReturning *returning, StringInfo buf,\n\t\t\t\t   bool json_format_by_default)\n{\n\tif (!OidIsValid(returning->typid))\n\t\treturn;\n\n\tappendStringInfo(buf, \" RETURNING %s\",\n\t\t\t\t\t format_type_with_typemod(returning->typid,\n\t\t\t\t\t\t\t\t\t\t\t  returning->typmod));\n\n\tif (!json_format_by_default ||\n\t\treturning->format->format_type !=\n\t\t(returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))\n\t\tget_json_format(returning->format, buf);\n}\n\n/*\n * get_json_constructor\t\t- Parse back a JsonConstructorExpr node\n */\nstatic void\nget_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,\n\t\t\t\t\t bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *funcname;\n\tbool\t\tis_json_object;\n\tint\t\t\tcurridx;\n\tListCell   *lc;\n\n\tif (ctor->type == JSCTOR_JSON_OBJECTAGG)\n\t{\n\t\tget_json_agg_constructor(ctor, context, \"JSON_OBJECTAGG\", true);\n\t\treturn;\n\t}\n\telse if (ctor->type == JSCTOR_JSON_ARRAYAGG)\n\t{\n\t\tget_json_agg_constructor(ctor, context, \"JSON_ARRAYAGG\", false);\n\t\treturn;\n\t}\n\n\tswitch (ctor->type)\n\t{\n\t\tcase JSCTOR_JSON_OBJECT:\n\t\t\tfuncname = \"JSON_OBJECT\";\n\t\t\tbreak;\n\t\tcase JSCTOR_JSON_ARRAY:\n\t\t\tfuncname = \"JSON_ARRAY\";\n\t\t\tbreak;\n\t\tcase JSCTOR_JSON_PARSE:\n\t\t\tfuncname = \"JSON\";\n\t\t\tbreak;\n\t\tcase JSCTOR_JSON_SCALAR:\n\t\t\tfuncname = \"JSON_SCALAR\";\n\t\t\tbreak;\n\t\tcase JSCTOR_JSON_SERIALIZE:\n\t\t\tfuncname = \"JSON_SERIALIZE\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\telog(ERROR, \"invalid JsonConstructorType %d\", ctor->type);\n\t}\n\n\tappendStringInfo(buf, \"%s(\", funcname);\n\n\tis_json_object = ctor->type == JSCTOR_JSON_OBJECT;\n\tforeach(lc, ctor->args)\n\t{\n\t\tcurridx = foreach_current_index(lc);\n\t\tif (curridx > 0)\n\t\t{\n\t\t\tconst char *sep;\n\n\t\t\tsep = (is_json_object && (curridx % 2) != 0) ? \" : \" : \", \";\n\t\t\tappendStringInfoString(buf, sep);\n\t\t}\n\n\t\tget_rule_expr((Node *) lfirst(lc), context, true);\n\t}\n\n\tget_json_constructor_options(ctor, buf);\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * Append options, if any, to the JSON constructor being deparsed\n */\nstatic void\nget_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)\n{\n\tif (ctor->absent_on_null)\n\t{\n\t\tif (ctor->type == JSCTOR_JSON_OBJECT ||\n\t\t\tctor->type == JSCTOR_JSON_OBJECTAGG)\n\t\t\tappendStringInfoString(buf, \" ABSENT ON NULL\");\n\t}\n\telse\n\t{\n\t\tif (ctor->type == JSCTOR_JSON_ARRAY ||\n\t\t\tctor->type == JSCTOR_JSON_ARRAYAGG)\n\t\t\tappendStringInfoString(buf, \" NULL ON NULL\");\n\t}\n\n\tif (ctor->unique)\n\t\tappendStringInfoString(buf, \" WITH UNIQUE KEYS\");\n\n\t/*\n\t * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't\n\t * support one.\n\t */\n\tif (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)\n\t\tget_json_returning(ctor->returning, buf, true);\n}\n\n/*\n * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node\n */\nstatic void\nget_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,\n\t\t\t\t\t\t const char *funcname, bool is_json_objectagg)\n{\n\tStringInfoData options;\n\n\tinitStringInfo(&options);\n\tget_json_constructor_options(ctor, &options);\n\n\tif (IsA(ctor->func, Aggref))\n\t\tget_agg_expr_helper((Aggref *) ctor->func, context,\n\t\t\t\t\t\t\t(Aggref *) ctor->func,\n\t\t\t\t\t\t\tfuncname, options.data, is_json_objectagg);\n\telse if (IsA(ctor->func, WindowFunc))\n\t\tget_windowfunc_expr_helper((WindowFunc *) ctor->func, context,\n\t\t\t\t\t\t\t\t   funcname, options.data,\n\t\t\t\t\t\t\t\t   is_json_objectagg);\n\telse\n\t\telog(ERROR, \"invalid JsonConstructorExpr underlying node type: %d\",\n\t\t\t nodeTag(ctor->func));\n}\n\n/*\n * simple_quote_literal - Format a string as a SQL literal, append to buf\n */\nstatic void\nsimple_quote_literal(StringInfo buf, const char *val)\n{\n\tconst char *valptr;\n\n\t/*\n\t * We form the string literal according to the prevailing setting of\n\t * standard_conforming_strings; we never use E''. User is responsible for\n\t * making sure result is used correctly.\n\t */\n\tappendStringInfoChar(buf, '\\'');\n\tfor (valptr = val; *valptr; valptr++)\n\t{\n\t\tchar\t\tch = *valptr;\n\n\t\tif (SQL_STR_DOUBLE(ch, !standard_conforming_strings))\n\t\t\tappendStringInfoChar(buf, ch);\n\t\tappendStringInfoChar(buf, ch);\n\t}\n\tappendStringInfoChar(buf, '\\'');\n}\n\n/* ----------\n * get_sublink_expr\t\t\t- Parse back a sublink\n * ----------\n */\nstatic void\nget_sublink_expr(SubLink *sublink, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tQuery\t   *query = (Query *) (sublink->subselect);\n\tchar\t   *opname = NULL;\n\tbool\t\tneed_paren;\n\n\tif (sublink->subLinkType == ARRAY_SUBLINK)\n\t\tappendStringInfoString(buf, \"ARRAY(\");\n\telse\n\t\tappendStringInfoChar(buf, '(');\n\n\t/*\n\t * Note that we print the name of only the first operator, when there are\n\t * multiple combining operators.  This is an approximation that could go\n\t * wrong in various scenarios (operators in different schemas, renamed\n\t * operators, etc) but there is not a whole lot we can do about it, since\n\t * the syntax allows only one operator to be shown.\n\t */\n\tif (sublink->testexpr)\n\t{\n\t\tif (IsA(sublink->testexpr, OpExpr))\n\t\t{\n\t\t\t/* single combining operator */\n\t\t\tOpExpr\t   *opexpr = (OpExpr *) sublink->testexpr;\n\n\t\t\tget_rule_expr(linitial(opexpr->args), context, true);\n\t\t\topname = generate_operator_name(opexpr->opno,\n\t\t\t\t\t\t\t\t\t\t\texprType(linitial(opexpr->args)),\n\t\t\t\t\t\t\t\t\t\t\texprType(lsecond(opexpr->args)));\n\t\t}\n\t\telse if (IsA(sublink->testexpr, BoolExpr))\n\t\t{\n\t\t\t/* multiple combining operators, = or <> cases */\n\t\t\tchar\t   *sep;\n\t\t\tListCell   *l;\n\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tsep = \"\";\n\t\t\tforeach(l, ((BoolExpr *) sublink->testexpr)->args)\n\t\t\t{\n\t\t\t\tOpExpr\t   *opexpr = lfirst_node(OpExpr, l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_expr(linitial(opexpr->args), context, true);\n\t\t\t\tif (!opname)\n\t\t\t\t\topname = generate_operator_name(opexpr->opno,\n\t\t\t\t\t\t\t\t\t\t\t\t\texprType(linitial(opexpr->args)),\n\t\t\t\t\t\t\t\t\t\t\t\t\texprType(lsecond(opexpr->args)));\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse if (IsA(sublink->testexpr, RowCompareExpr))\n\t\t{\n\t\t\t/* multiple combining operators, < <= > >= cases */\n\t\t\tRowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;\n\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr((Node *) rcexpr->largs, context, true);\n\t\t\topname = generate_operator_name(linitial_oid(rcexpr->opnos),\n\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->largs)),\n\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->rargs)));\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse\n\t\t\telog(ERROR, \"unrecognized testexpr type: %d\",\n\t\t\t\t (int) nodeTag(sublink->testexpr));\n\t}\n\n\tneed_paren = true;\n\n\tswitch (sublink->subLinkType)\n\t{\n\t\tcase EXISTS_SUBLINK:\n\t\t\tappendStringInfoString(buf, \"EXISTS \");\n\t\t\tbreak;\n\n\t\tcase ANY_SUBLINK:\n\t\t\tif (strcmp(opname, \"=\") == 0)\t/* Represent = ANY as IN */\n\t\t\t\tappendStringInfoString(buf, \" IN \");\n\t\t\telse\n\t\t\t\tappendStringInfo(buf, \" %s ANY \", opname);\n\t\t\tbreak;\n\n\t\tcase ALL_SUBLINK:\n\t\t\tappendStringInfo(buf, \" %s ALL \", opname);\n\t\t\tbreak;\n\n\t\tcase ROWCOMPARE_SUBLINK:\n\t\t\tappendStringInfo(buf, \" %s \", opname);\n\t\t\tbreak;\n\n\t\tcase EXPR_SUBLINK:\n\t\tcase MULTIEXPR_SUBLINK:\n\t\tcase ARRAY_SUBLINK:\n\t\t\tneed_paren = false;\n\t\t\tbreak;\n\n\t\tcase CTE_SUBLINK:\t\t/* shouldn't occur in a SubLink */\n\t\tdefault:\n\t\t\telog(ERROR, \"unrecognized sublink type: %d\",\n\t\t\t\t (int) sublink->subLinkType);\n\t\t\tbreak;\n\t}\n\n\tif (need_paren)\n\t\tappendStringInfoChar(buf, '(');\n\n\tget_query_def(query, buf, context->namespaces, NULL, false,\n\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t  context->indentLevel);\n\n\tif (need_paren)\n\t\tappendStringInfoString(buf, \"))\");\n\telse\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/* ----------\n * get_xmltable\t\t\t- Parse back a XMLTABLE function\n * ----------\n */\nstatic void\nget_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tappendStringInfoString(buf, \"XMLTABLE(\");\n\n\tif (tf->ns_uris != NIL)\n\t{\n\t\tListCell   *lc1,\n\t\t\t\t   *lc2;\n\t\tbool\t\tfirst = true;\n\n\t\tappendStringInfoString(buf, \"XMLNAMESPACES (\");\n\t\tforboth(lc1, tf->ns_uris, lc2, tf->ns_names)\n\t\t{\n\t\t\tNode\t   *expr = (Node *) lfirst(lc1);\n\t\t\tchar\t   *name = strVal(lfirst(lc2));\n\n\t\t\tif (!first)\n\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\telse\n\t\t\t\tfirst = false;\n\n\t\t\tif (name != NULL)\n\t\t\t{\n\t\t\t\tget_rule_expr(expr, context, showimplicit);\n\t\t\t\tappendStringInfo(buf, \" AS %s\", name);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \"DEFAULT \");\n\t\t\t\tget_rule_expr(expr, context, showimplicit);\n\t\t\t}\n\t\t}\n\t\tappendStringInfoString(buf, \"), \");\n\t}\n\n\tappendStringInfoChar(buf, '(');\n\tget_rule_expr((Node *) tf->rowexpr, context, showimplicit);\n\tappendStringInfoString(buf, \") PASSING (\");\n\tget_rule_expr((Node *) tf->docexpr, context, showimplicit);\n\tappendStringInfoChar(buf, ')');\n\n\tif (tf->colexprs != NIL)\n\t{\n\t\tListCell   *l1;\n\t\tListCell   *l2;\n\t\tListCell   *l3;\n\t\tListCell   *l4;\n\t\tListCell   *l5;\n\t\tint\t\t\tcolnum = 0;\n\n\t\tappendStringInfoString(buf, \" COLUMNS \");\n\t\tforfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,\n\t\t\t\tl4, tf->colexprs, l5, tf->coldefexprs)\n\t\t{\n\t\t\tchar\t   *colname = strVal(lfirst(l1));\n\t\t\tOid\t\t\ttypid = lfirst_oid(l2);\n\t\t\tint32\t\ttypmod = lfirst_int(l3);\n\t\t\tNode\t   *colexpr = (Node *) lfirst(l4);\n\t\t\tNode\t   *coldefexpr = (Node *) lfirst(l5);\n\t\t\tbool\t\tordinality = (tf->ordinalitycol == colnum);\n\t\t\tbool\t\tnotnull = bms_is_member(colnum, tf->notnulls);\n\n\t\t\tif (colnum > 0)\n\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tcolnum++;\n\n\t\t\tappendStringInfo(buf, \"%s %s\", quote_identifier(colname),\n\t\t\t\t\t\t\t ordinality ? \"FOR ORDINALITY\" :\n\t\t\t\t\t\t\t format_type_with_typemod(typid, typmod));\n\t\t\tif (ordinality)\n\t\t\t\tcontinue;\n\n\t\t\tif (coldefexpr != NULL)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" DEFAULT (\");\n\t\t\t\tget_rule_expr((Node *) coldefexpr, context, showimplicit);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tif (colexpr != NULL)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" PATH (\");\n\t\t\t\tget_rule_expr((Node *) colexpr, context, showimplicit);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tif (notnull)\n\t\t\t\tappendStringInfoString(buf, \" NOT NULL\");\n\t\t}\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_json_table_nested_columns - Parse back nested JSON_TABLE columns\n */\nstatic void\nget_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,\n\t\t\t\t\t\t\t  deparse_context *context, bool showimplicit,\n\t\t\t\t\t\t\t  bool needcomma)\n{\n\tif (IsA(plan, JsonTablePathScan))\n\t{\n\t\tJsonTablePathScan *scan = castNode(JsonTablePathScan, plan);\n\n\t\tif (needcomma)\n\t\t\tappendStringInfoChar(context->buf, ',');\n\n\t\tappendStringInfoChar(context->buf, ' ');\n\t\tappendContextKeyword(context, \"NESTED PATH \", 0, 0, 0);\n\t\tget_const_expr(scan->path->value, context, -1);\n\t\tappendStringInfo(context->buf, \" AS %s\", quote_identifier(scan->path->name));\n\t\tget_json_table_columns(tf, scan, context, showimplicit);\n\t}\n\telse if (IsA(plan, JsonTableSiblingJoin))\n\t{\n\t\tJsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;\n\n\t\tget_json_table_nested_columns(tf, join->lplan, context, showimplicit,\n\t\t\t\t\t\t\t\t\t  needcomma);\n\t\tget_json_table_nested_columns(tf, join->rplan, context, showimplicit,\n\t\t\t\t\t\t\t\t\t  true);\n\t}\n}\n\n/*\n * get_json_table_columns - Parse back JSON_TABLE columns\n */\nstatic void\nget_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,\n\t\t\t\t\t   deparse_context *context,\n\t\t\t\t\t   bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\tListCell   *lc_colname;\n\tListCell   *lc_coltype;\n\tListCell   *lc_coltypmod;\n\tListCell   *lc_colvalexpr;\n\tint\t\t\tcolnum = 0;\n\n\tappendStringInfoChar(buf, ' ');\n\tappendContextKeyword(context, \"COLUMNS (\", 0, 0, 0);\n\n\tif (PRETTY_INDENT(context))\n\t\tcontext->indentLevel += PRETTYINDENT_VAR;\n\n\tforfour(lc_colname, tf->colnames,\n\t\t\tlc_coltype, tf->coltypes,\n\t\t\tlc_coltypmod, tf->coltypmods,\n\t\t\tlc_colvalexpr, tf->colvalexprs)\n\t{\n\t\tchar\t   *colname = strVal(lfirst(lc_colname));\n\t\tJsonExpr   *colexpr;\n\t\tOid\t\t\ttypid;\n\t\tint32\t\ttypmod;\n\t\tbool\t\tordinality;\n\t\tJsonBehaviorType default_behavior;\n\n\t\ttypid = lfirst_oid(lc_coltype);\n\t\ttypmod = lfirst_int(lc_coltypmod);\n\t\tcolexpr = castNode(JsonExpr, lfirst(lc_colvalexpr));\n\n\t\t/* Skip columns that don't belong to this scan. */\n\t\tif (scan->colMin < 0 || colnum < scan->colMin)\n\t\t{\n\t\t\tcolnum++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (colnum > scan->colMax)\n\t\t\tbreak;\n\n\t\tif (colnum > scan->colMin)\n\t\t\tappendStringInfoString(buf, \", \");\n\n\t\tcolnum++;\n\n\t\tordinality = !colexpr;\n\n\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\n\t\tappendStringInfo(buf, \"%s %s\", quote_identifier(colname),\n\t\t\t\t\t\t ordinality ? \"FOR ORDINALITY\" :\n\t\t\t\t\t\t format_type_with_typemod(typid, typmod));\n\t\tif (ordinality)\n\t\t\tcontinue;\n\n\t\t/*\n\t\t * Set default_behavior to guide get_json_expr_options() on whether to\n\t\t * to emit the ON ERROR / EMPTY clauses.\n\t\t */\n\t\tif (colexpr->op == JSON_EXISTS_OP)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" EXISTS\");\n\t\t\tdefault_behavior = JSON_BEHAVIOR_FALSE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (colexpr->op == JSON_QUERY_OP)\n\t\t\t{\n\t\t\t\tchar\t\ttypcategory;\n\t\t\t\tbool\t\ttypispreferred;\n\n\t\t\t\tget_type_category_preferred(typid, &typcategory, &typispreferred);\n\n\t\t\t\tif (typcategory == TYPCATEGORY_STRING)\n\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t   colexpr->format->format_type == JS_FORMAT_JSONB ?\n\t\t\t\t\t\t\t\t\t\t   \" FORMAT JSONB\" : \" FORMAT JSON\");\n\t\t\t}\n\n\t\t\tdefault_behavior = JSON_BEHAVIOR_NULL;\n\t\t}\n\n\t\tappendStringInfoString(buf, \" PATH \");\n\n\t\tget_json_path_spec(colexpr->path_spec, context, showimplicit);\n\n\t\tget_json_expr_options(colexpr, context, default_behavior);\n\t}\n\n\tif (scan->child)\n\t\tget_json_table_nested_columns(tf, scan->child, context, showimplicit,\n\t\t\t\t\t\t\t\t\t  scan->colMin >= 0);\n\n\tif (PRETTY_INDENT(context))\n\t\tcontext->indentLevel -= PRETTYINDENT_VAR;\n\n\tappendContextKeyword(context, \")\", 0, 0, 0);\n}\n\n/* ----------\n * get_json_table\t\t\t- Parse back a JSON_TABLE function\n * ----------\n */\nstatic void\nget_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\tJsonExpr   *jexpr = castNode(JsonExpr, tf->docexpr);\n\tJsonTablePathScan *root = castNode(JsonTablePathScan, tf->plan);\n\n\tappendStringInfoString(buf, \"JSON_TABLE(\");\n\n\tif (PRETTY_INDENT(context))\n\t\tcontext->indentLevel += PRETTYINDENT_VAR;\n\n\tappendContextKeyword(context, \"\", 0, 0, 0);\n\n\tget_rule_expr(jexpr->formatted_expr, context, showimplicit);\n\n\tappendStringInfoString(buf, \", \");\n\n\tget_const_expr(root->path->value, context, -1);\n\n\tappendStringInfo(buf, \" AS %s\", quote_identifier(root->path->name));\n\n\tif (jexpr->passing_values)\n\t{\n\t\tListCell   *lc1,\n\t\t\t\t   *lc2;\n\t\tbool\t\tneedcomma = false;\n\n\t\tappendStringInfoChar(buf, ' ');\n\t\tappendContextKeyword(context, \"PASSING \", 0, 0, 0);\n\n\t\tif (PRETTY_INDENT(context))\n\t\t\tcontext->indentLevel += PRETTYINDENT_VAR;\n\n\t\tforboth(lc1, jexpr->passing_names,\n\t\t\t\tlc2, jexpr->passing_values)\n\t\t{\n\t\t\tif (needcomma)\n\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tneedcomma = true;\n\n\t\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\n\t\t\tget_rule_expr((Node *) lfirst(lc2), context, false);\n\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t quote_identifier((lfirst_node(String, lc1))->sval)\n\t\t\t\t);\n\t\t}\n\n\t\tif (PRETTY_INDENT(context))\n\t\t\tcontext->indentLevel -= PRETTYINDENT_VAR;\n\t}\n\n\tget_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,\n\t\t\t\t\t\t   showimplicit);\n\n\tif (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)\n\t\tget_json_behavior(jexpr->on_error, context, \"ERROR\");\n\n\tif (PRETTY_INDENT(context))\n\t\tcontext->indentLevel -= PRETTYINDENT_VAR;\n\n\tappendContextKeyword(context, \")\", 0, 0, 0);\n}\n\n/* ----------\n * get_tablefunc\t\t\t- Parse back a table function\n * ----------\n */\nstatic void\nget_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)\n{\n\t/* XMLTABLE and JSON_TABLE are the only existing implementations.  */\n\n\tif (tf->functype == TFT_XMLTABLE)\n\t\tget_xmltable(tf, context, showimplicit);\n\telse if (tf->functype == TFT_JSON_TABLE)\n\t\tget_json_table(tf, context, showimplicit);\n}\n\n/* ----------\n * get_from_clause\t\t\t- Parse back a FROM clause\n *\n * \"prefix\" is the keyword that denotes the start of the list of FROM\n * elements. It is FROM when used to parse back SELECT and UPDATE, but\n * is USING when parsing back DELETE.\n * ----------\n */\nstatic void\nget_from_clause(Query *query, const char *prefix, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tfirst = true;\n\tListCell   *l;\n\n\t/*\n\t * We use the query's jointree as a guide to what to print.  However, we\n\t * must ignore auto-added RTEs that are marked not inFromCl. (These can\n\t * only appear at the top level of the jointree, so it's sufficient to\n\t * check here.)  This check also ensures we ignore the rule pseudo-RTEs\n\t * for NEW and OLD.\n\t */\n\tforeach(l, query->jointree->fromlist)\n\t{\n\t\tNode\t   *jtnode = (Node *) lfirst(l);\n\n\t\tif (IsA(jtnode, RangeTblRef))\n\t\t{\n\t\t\tint\t\t\tvarno = ((RangeTblRef *) jtnode)->rtindex;\n\t\t\tRangeTblEntry *rte = rt_fetch(varno, query->rtable);\n\n\t\t\tif (!rte->inFromCl)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (first)\n\t\t{\n\t\t\tappendContextKeyword(context, prefix,\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\t\t\tfirst = false;\n\n\t\t\tget_from_clause_item(jtnode, query, context);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tStringInfoData itembuf;\n\n\t\t\tappendStringInfoString(buf, \", \");\n\n\t\t\t/*\n\t\t\t * Put the new FROM item's text into itembuf so we can decide\n\t\t\t * after we've got it whether or not it needs to go on a new line.\n\t\t\t */\n\t\t\tinitStringInfo(&itembuf);\n\t\t\tcontext->buf = &itembuf;\n\n\t\t\tget_from_clause_item(jtnode, query, context);\n\n\t\t\t/* Restore context's output buffer */\n\t\t\tcontext->buf = buf;\n\n\t\t\t/* Consider line-wrapping if enabled */\n\t\t\tif (PRETTY_INDENT(context) && context->wrapColumn >= 0)\n\t\t\t{\n\t\t\t\t/* Does the new item start with a new line? */\n\t\t\t\tif (itembuf.len > 0 && itembuf.data[0] == '\\n')\n\t\t\t\t{\n\t\t\t\t\t/* If so, we shouldn't add anything */\n\t\t\t\t\t/* instead, remove any trailing spaces currently in buf */\n\t\t\t\t\tremoveStringInfoSpaces(buf);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tchar\t   *trailing_nl;\n\n\t\t\t\t\t/* Locate the start of the current line in the buffer */\n\t\t\t\t\ttrailing_nl = strrchr(buf->data, '\\n');\n\t\t\t\t\tif (trailing_nl == NULL)\n\t\t\t\t\t\ttrailing_nl = buf->data;\n\t\t\t\t\telse\n\t\t\t\t\t\ttrailing_nl++;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Add a newline, plus some indentation, if the new item\n\t\t\t\t\t * would cause an overflow.\n\t\t\t\t\t */\n\t\t\t\t\tif (strlen(trailing_nl) + itembuf.len > context->wrapColumn)\n\t\t\t\t\t\tappendContextKeyword(context, \"\", -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_VAR);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Add the new item */\n\t\t\tappendStringInfoString(buf, itembuf.data);\n\n\t\t\t/* clean up */\n\t\t\tpfree(itembuf.data);\n\t\t}\n\t}\n}\n\nstatic void\nget_from_clause_item(Node *jtnode, Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tdeparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);\n\n\tif (IsA(jtnode, RangeTblRef))\n\t{\n\t\tint\t\t\tvarno = ((RangeTblRef *) jtnode)->rtindex;\n\t\tRangeTblEntry *rte = rt_fetch(varno, query->rtable);\n\t\tdeparse_columns *colinfo = deparse_columns_fetch(varno, dpns);\n\t\tRangeTblFunction *rtfunc1 = NULL;\n\t\tCitusRTEKind rteKind = GetRangeTblKind(rte);\n\n\t\tif (rte->lateral)\n\t\t\tappendStringInfoString(buf, \"LATERAL \");\n\n\t\t/* Print the FROM item proper */\n\t\tswitch (rte->rtekind)\n\t\t{\n\t\t\tcase RTE_RELATION:\n\t\t\t\t/* Normal relation RTE */\n\t\t\t\tappendStringInfo(buf, \"%s%s\",\n\t\t\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->namespaces));\n\t\t\t\tbreak;\n\t\t\tcase RTE_SUBQUERY:\n\t\t\t\t/* Subquery RTE */\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_query_def(rte->subquery, buf, context->namespaces, NULL,\n\t\t\t\t              true,\n\t\t\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t\t\t  context->indentLevel);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tbreak;\n\t\t\tcase RTE_FUNCTION:\n\t\t\t\t/* if it's a shard, do differently */\n\t\t\t\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t\t\t\t{\n\t\t\t\t\tchar *fragmentSchemaName = NULL;\n\t\t\t\t\tchar *fragmentTableName = NULL;\n\n\t\t\t\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t\t\t\t/* use schema and table name from the remote alias */\n\t\t\t\t\tappendStringInfo(buf, \"%s%s\",\n\t\t\t\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfragmentTableName));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t/* Function RTE */\n\t\t\t\trtfunc1 = (RangeTblFunction *) linitial(rte->functions);\n\n\t\t\t\t/*\n\t\t\t\t * Omit ROWS FROM() syntax for just one function, unless it\n\t\t\t\t * has both a coldeflist and WITH ORDINALITY. If it has both,\n\t\t\t\t * we must use ROWS FROM() syntax to avoid ambiguity about\n\t\t\t\t * whether the coldeflist includes the ordinality column.\n\t\t\t\t */\n\t\t\t\tif (list_length(rte->functions) == 1 &&\n\t\t\t\t\t(rtfunc1->funccolnames == NIL || !rte->funcordinality))\n\t\t\t\t{\n\t\t\t\t\tget_rule_expr_funccall(rtfunc1->funcexpr, context, true);\n\t\t\t\t\t/* we'll print the coldeflist below, if it has one */\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tbool\t\tall_unnest;\n\t\t\t\t\tListCell   *lc;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * If all the function calls in the list are to unnest,\n\t\t\t\t\t * and none need a coldeflist, then collapse the list back\n\t\t\t\t\t * down to UNNEST(args).  (If we had more than one\n\t\t\t\t\t * built-in unnest function, this would get more\n\t\t\t\t\t * difficult.)\n\t\t\t\t\t *\n\t\t\t\t\t * XXX This is pretty ugly, since it makes not-terribly-\n\t\t\t\t\t * future-proof assumptions about what the parser would do\n\t\t\t\t\t * with the output; but the alternative is to emit our\n\t\t\t\t\t * nonstandard ROWS FROM() notation for what might have\n\t\t\t\t\t * been a perfectly spec-compliant multi-argument\n\t\t\t\t\t * UNNEST().\n\t\t\t\t\t */\n\t\t\t\t\tall_unnest = true;\n\t\t\t\t\tforeach(lc, rte->functions)\n\t\t\t\t\t{\n\t\t\t\t\t\tRangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);\n\n\t\t\t\t\t\tif (!IsA(rtfunc->funcexpr, FuncExpr) ||\n\t\t\t\t\t\t\t((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||\n\t\t\t\t\t\t\trtfunc->funccolnames != NIL)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tall_unnest = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (all_unnest)\n\t\t\t\t\t{\n\t\t\t\t\t\tList\t   *allargs = NIL;\n\n\t\t\t\t\t\tforeach(lc, rte->functions)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);\n\t\t\t\t\t\t\tList\t   *args = ((FuncExpr *) rtfunc->funcexpr)->args;\n\n\t\t\t\t\t\t\tallargs = list_concat(allargs, args);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"UNNEST(\");\n\t\t\t\t\t\tget_rule_expr((Node *) allargs, context, true);\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tint\t\t\tfuncno = 0;\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"ROWS FROM(\");\n\t\t\t\t\t\tforeach(lc, rte->functions)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);\n\n\t\t\t\t\t\t\tif (funcno > 0)\n\t\t\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\t\t\tget_rule_expr_funccall(rtfunc->funcexpr, context, true);\n\t\t\t\t\t\t\tif (rtfunc->funccolnames != NIL)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t/* Reconstruct the column definition list */\n\t\t\t\t\t\t\t\tappendStringInfoString(buf, \" AS \");\n\t\t\t\t\t\t\t\tget_from_clause_coldeflist(rtfunc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   context);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfuncno++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t}\n\t\t\t\t\t/* prevent printing duplicate coldeflist below */\n\t\t\t\t\trtfunc1 = NULL;\n\t\t\t\t}\n\t\t\t\tif (rte->funcordinality)\n\t\t\t\t\tappendStringInfoString(buf, \" WITH ORDINALITY\");\n\t\t\t\tbreak;\n\t\t\tcase RTE_TABLEFUNC:\n\t\t\t\tget_tablefunc(rte->tablefunc, context, true);\n\t\t\t\tbreak;\n\t\t\tcase RTE_VALUES:\n\t\t\t\t/* Values list RTE */\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_values_def(rte->values_lists, context);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tbreak;\n\t\t\tcase RTE_CTE:\n\t\t\t\tappendStringInfoString(buf, quote_identifier(rte->ctename));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\telog(ERROR, \"unrecognized RTE kind: %d\", (int) rte->rtekind);\n\t\t\t\tbreak;\n\t\t}\n\n\t\t/* Print the relation alias, if needed */\n\t\tget_rte_alias(rte, varno, false, context);\n\n\t\t/* Print the column definitions or aliases, if needed */\n\t\tif (rtfunc1 && rtfunc1->funccolnames != NIL)\n\t\t{\n\t\t\t/* Reconstruct the columndef list, which is also the aliases */\n\t\t\tget_from_clause_coldeflist(rtfunc1, colinfo, context);\n\t\t}\n\t\telse if (GetRangeTblKind(rte) != CITUS_RTE_SHARD ||\n\t\t\t\t (rte->alias != NULL && rte->alias->colnames != NIL))\n\t\t{\n\t\t\t/* Else print column aliases as needed */\n\t\t\tget_column_alias_list(colinfo, context);\n\t\t}\n\t\t/* check if column's are given aliases in distributed tables */\n\t\telse if (colinfo->parentUsing != NIL)\n\t\t{\n\t\t\tAssert(colinfo->printaliases);\n\t\t\tget_column_alias_list(colinfo, context);\n\t\t}\n\n\t\t/* Tablesample clause must go after any alias */\n\t\tif ((rteKind == CITUS_RTE_RELATION || rteKind == CITUS_RTE_SHARD) &&\n\t\t\trte->tablesample)\n\t\t{\n\t\t\tget_tablesample_def(rte->tablesample, context);\n\t\t}\n\t}\n\telse if (IsA(jtnode, JoinExpr))\n\t{\n\t\tJoinExpr   *j = (JoinExpr *) jtnode;\n\t\tdeparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);\n\t\tbool\t\tneed_paren_on_right;\n\n\t\tneed_paren_on_right = PRETTY_PAREN(context) &&\n\t\t\t!IsA(j->rarg, RangeTblRef) &&\n\t\t\t!(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);\n\n\t\tif (!PRETTY_PAREN(context) || j->alias != NULL)\n\t\t\tappendStringInfoChar(buf, '(');\n\n\t\tget_from_clause_item(j->larg, query, context);\n\n\t\tswitch (j->jointype)\n\t\t{\n\t\t\tcase JOIN_INNER:\n\t\t\t\tif (j->quals)\n\t\t\t\t\tappendContextKeyword(context, \" JOIN \",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\telse\n\t\t\t\t\tappendContextKeyword(context, \" CROSS JOIN \",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tcase JOIN_LEFT:\n\t\t\t\tappendContextKeyword(context, \" LEFT JOIN \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tcase JOIN_FULL:\n\t\t\t\tappendContextKeyword(context, \" FULL JOIN \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tcase JOIN_RIGHT:\n\t\t\t\tappendContextKeyword(context, \" RIGHT JOIN \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\telog(ERROR, \"unrecognized join type: %d\",\n\t\t\t\t\t (int) j->jointype);\n\t\t}\n\n\t\tif (need_paren_on_right)\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_from_clause_item(j->rarg, query, context);\n\t\tif (need_paren_on_right)\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\tif (j->usingClause)\n\t\t{\n\t\t\tListCell   *lc;\n\t\t\tbool\t\tfirst = true;\n\n\t\t\tappendStringInfoString(buf, \" USING (\");\n\t\t\t/* Use the assigned names, not what's in usingClause */\n\t\t\tforeach(lc, colinfo->usingNames)\n\t\t\t{\n\t\t\t\tchar\t   *colname = (char *) lfirst(lc);\n\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf, quote_identifier(colname));\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\tif (j->join_using_alias)\n\t\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t\t quote_identifier(j->join_using_alias->aliasname));\n\t\t}\n\t\telse if (j->quals)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" ON \");\n\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr(j->quals, context, false);\n\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse if (j->jointype != JOIN_INNER)\n\t\t{\n\t\t\t/* If we didn't say CROSS JOIN above, we must provide an ON */\n\t\t\tappendStringInfoString(buf, \" ON TRUE\");\n\t\t}\n\n\t\tif (!PRETTY_PAREN(context) || j->alias != NULL)\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t/* Yes, it's correct to put alias after the right paren ... */\n\t\tif (j->alias != NULL)\n\t\t{\n\t\t\t/*\n\t\t\t * Note that it's correct to emit an alias clause if and only if\n\t\t\t * there was one originally.  Otherwise we'd be converting a named\n\t\t\t * join to unnamed or vice versa, which creates semantic\n\t\t\t * subtleties we don't want.  However, we might print a different\n\t\t\t * alias name than was there originally.\n\t\t\t */\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\t\t\t quote_identifier(get_rtable_name(j->rtindex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  context)));\n\t\t\tget_column_alias_list(colinfo, context);\n\t\t}\n\t}\n\telse\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(jtnode));\n}\n\n/*\n * get_rte_alias - print the relation's alias, if needed\n *\n * If printed, the alias is preceded by a space, or by \" AS \" if use_as is true.\n */\nstatic void\nget_rte_alias(RangeTblEntry *rte, int varno, bool use_as,\n\t\t\t  deparse_context *context)\n{\n\tdeparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);\n\tchar\t   *refname = get_rtable_name(varno, context);\n\tdeparse_columns *colinfo = deparse_columns_fetch(varno, dpns);\n\tbool\t\tprintalias = false;\n\n\tif (rte->alias != NULL)\n\t{\n\t\t/* Always print alias if user provided one */\n\t\tprintalias = true;\n\t}\n\telse if (colinfo->printaliases)\n\t{\n\t\t/* Always print alias if we need to print column aliases */\n\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_RELATION)\n\t{\n\t\t/*\n\t\t * No need to print alias if it's same as relation name (this would\n\t\t * normally be the case, but not if set_rtable_names had to resolve a\n\t\t * conflict).\n\t\t */\n\t\tif (strcmp(refname, get_relation_name(rte->relid)) != 0)\n\t\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_FUNCTION)\n\t{\n\t\t/*\n\t\t * For a function RTE, always print alias.  This covers possible\n\t\t * renaming of the function and/or instability of the FigureColname\n\t\t * rules for things that aren't simple functions.  Note we'd need to\n\t\t * force it anyway for the columndef list case.\n\t\t */\n\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_SUBQUERY ||\n\t\t\t rte->rtekind == RTE_VALUES)\n\t{\n\t\t/*\n\t\t * For a subquery, always print alias.  This makes the output\n\t\t * SQL-spec-compliant, even though we allow such aliases to be omitted\n\t\t * on input.\n\t\t */\n\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_CTE)\n\t{\n\t\t/*\n\t\t * No need to print alias if it's same as CTE name (this would\n\t\t * normally be the case, but not if set_rtable_names had to resolve a\n\t\t * conflict).\n\t\t */\n\t\tif (strcmp(refname, rte->ctename) != 0)\n\t\t\tprintalias = true;\n\t}\n\n\tif (printalias)\n\t\tappendStringInfo(context->buf, \"%s%s\",\n\t\t\t\t\t\t use_as ? \" AS \" : \" \",\n\t\t\t\t\t\t quote_identifier(refname));\n}\n\n/*\n * get_column_alias_list - print column alias list for an RTE\n *\n * Caller must already have printed the relation's alias name.\n */\nstatic void\nget_column_alias_list(deparse_columns *colinfo, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tint\t\t\ti;\n\tbool\t\tfirst = true;\n\n\t/* Don't print aliases if not needed */\n\tif (!colinfo->printaliases)\n\t\treturn;\n\n\tfor (i = 0; i < colinfo->num_new_cols; i++)\n\t{\n\t\tchar\t   *colname = colinfo->new_colnames[i];\n\n\t\tif (first)\n\t\t{\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tfirst = false;\n\t\t}\n\t\telse\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tappendStringInfoString(buf, quote_identifier(colname));\n\t}\n\tif (!first)\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_from_clause_coldeflist - reproduce FROM clause coldeflist\n *\n * When printing a top-level coldeflist (which is syntactically also the\n * relation's column alias list), use column names from colinfo.  But when\n * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the\n * original coldeflist's names, which are available in rtfunc->funccolnames.\n * Pass NULL for colinfo to select the latter behavior.\n *\n * The coldeflist is appended immediately (no space) to buf.  Caller is\n * responsible for ensuring that an alias or AS is present before it.\n */\nstatic void\nget_from_clause_coldeflist(RangeTblFunction *rtfunc,\n\t\t\t\t\t\t   deparse_columns *colinfo,\n\t\t\t\t\t\t   deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tListCell   *l1;\n\tListCell   *l2;\n\tListCell   *l3;\n\tListCell   *l4;\n\tint\t\t\ti;\n\n\tappendStringInfoChar(buf, '(');\n\n\ti = 0;\n\tforfour(l1, rtfunc->funccoltypes,\n\t\t\tl2, rtfunc->funccoltypmods,\n\t\t\tl3, rtfunc->funccolcollations,\n\t\t\tl4, rtfunc->funccolnames)\n\t{\n\t\tOid\t\t\tatttypid = lfirst_oid(l1);\n\t\tint32\t\tatttypmod = lfirst_int(l2);\n\t\tOid\t\t\tattcollation = lfirst_oid(l3);\n\t\tchar\t   *attname;\n\n\t\tif (colinfo)\n\t\t\tattname = colinfo->colnames[i];\n\t\telse\n\t\t\tattname = strVal(lfirst(l4));\n\n\t\tAssert(attname);\t\t/* shouldn't be any dropped columns here */\n\n\t\tif (i > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tappendStringInfo(buf, \"%s %s\",\n\t\t\t\t\t\t quote_identifier(attname),\n\t\t\t\t\t\t format_type_with_typemod(atttypid, atttypmod));\n\t\tif (OidIsValid(attcollation) &&\n\t\t\tattcollation != get_typcollation(atttypid))\n\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t generate_collation_name(attcollation));\n\n\t\ti++;\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_tablesample_def\t\t\t- print a TableSampleClause\n */\nstatic void\nget_tablesample_def(TableSampleClause *tablesample, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\targtypes[1];\n\tint\t\t\tnargs;\n\tListCell   *l;\n\n\t/*\n\t * We should qualify the handler's function name if it wouldn't be\n\t * resolved by lookup in the current search path.\n\t */\n\targtypes[0] = INTERNALOID;\n\tappendStringInfo(buf, \" TABLESAMPLE %s (\",\n\t\t\t\t\t generate_function_name(tablesample->tsmhandler, 1,\n\t\t\t\t\t\t\t\t\t\t\tNIL, argtypes,\n\t\t\t\t\t\t\t\t\t\t\tfalse, NULL, false));\n\n\tnargs = 0;\n\tforeach(l, tablesample->args)\n\t{\n\t\tif (nargs++ > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tget_rule_expr((Node *) lfirst(l), context, false);\n\t}\n\tappendStringInfoChar(buf, ')');\n\n\tif (tablesample->repeatable != NULL)\n\t{\n\t\tappendStringInfoString(buf, \" REPEATABLE (\");\n\t\tget_rule_expr((Node *) tablesample->repeatable, context, false);\n\t\tappendStringInfoChar(buf, ')');\n\t}\n}\n\n/*\n * get_opclass_name\t\t\t- fetch name of an index operator class\n *\n * The opclass name is appended (after a space) to buf.\n *\n * Output is suppressed if the opclass is the default for the given\n * actual_datatype.  (If you don't want this behavior, just pass\n * InvalidOid for actual_datatype.)\n */\nstatic void\nget_opclass_name(Oid opclass, Oid actual_datatype,\n\t\t\t\t StringInfo buf)\n{\n\tHeapTuple\tht_opc;\n\tForm_pg_opclass opcrec;\n\tchar\t   *opcname;\n\tchar\t   *nspname;\n\n\tht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));\n\tif (!HeapTupleIsValid(ht_opc))\n\t\telog(ERROR, \"cache lookup failed for opclass %u\", opclass);\n\topcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);\n\n\tif (!OidIsValid(actual_datatype) ||\n\t\tGetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)\n\t{\n\t\t/* Okay, we need the opclass name.  Do we need to qualify it? */\n\t\topcname = NameStr(opcrec->opcname);\n\t\tif (OpclassIsVisible(opclass))\n\t\t\tappendStringInfo(buf, \" %s\", quote_identifier(opcname));\n\t\telse\n\t\t{\n\t\t\tnspname = get_namespace_name_or_temp(opcrec->opcnamespace);\n\t\t\tappendStringInfo(buf, \" %s.%s\",\n\t\t\t\t\t\t\t quote_identifier(nspname),\n\t\t\t\t\t\t\t quote_identifier(opcname));\n\t\t}\n\t}\n\tReleaseSysCache(ht_opc);\n}\n\n/*\n * processIndirection - take care of array and subfield assignment\n *\n * We strip any top-level FieldStore or assignment SubscriptingRef nodes that\n * appear in the input, printing them as decoration for the base column\n * name (which we assume the caller just printed).  We might also need to\n * strip CoerceToDomain nodes, but only ones that appear above assignment\n * nodes.\n *\n * Returns the subexpression that's to be assigned.\n */\nstatic Node *\nprocessIndirection(Node *node, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tCoerceToDomain *cdomain = NULL;\n\n\tfor (;;)\n\t{\n\t\tif (node == NULL)\n\t\t\tbreak;\n\t\tif (IsA(node, FieldStore))\n\t\t{\n\t\t\tFieldStore *fstore = (FieldStore *) node;\n\t\t\tOid\t\t\ttyprelid;\n\t\t\tchar\t   *fieldname;\n\n\t\t\t/* lookup tuple type */\n\t\t\ttyprelid = get_typ_typrelid(fstore->resulttype);\n\t\t\tif (!OidIsValid(typrelid))\n\t\t\t\telog(ERROR, \"argument type %s of FieldStore is not a tuple type\",\n\t\t\t\t\t format_type_be(fstore->resulttype));\n\n\t\t\t/*\n\t\t\t * Print the field name.  There should only be one target field in\n\t\t\t * stored rules.  There could be more than that in executable\n\t\t\t * target lists, but this function cannot be used for that case.\n\t\t\t */\n\t\t\tAssert(list_length(fstore->fieldnums) == 1);\n\t\t\tfieldname = get_attname(typrelid,\n\t\t\t\t\t\t\t\t\tlinitial_int(fstore->fieldnums), false);\n\t\t\tappendStringInfo(buf, \".%s\", quote_identifier(fieldname));\n\n\t\t\t/*\n\t\t\t * We ignore arg since it should be an uninteresting reference to\n\t\t\t * the target column or subcolumn.\n\t\t\t */\n\t\t\tnode = (Node *) linitial(fstore->newvals);\n\t\t}\n\t\telse if (IsA(node, SubscriptingRef))\n\t\t{\n\t\t\tSubscriptingRef   *sbsref = (SubscriptingRef *) node;\n\n\t\t\tif (sbsref->refassgnexpr == NULL)\n\t\t\t\tbreak;\n\t\t\tprintSubscripts(sbsref, context);\n\n\t\t\t/*\n\t\t\t * We ignore refexpr since it should be an uninteresting reference\n\t\t\t * to the target column or subcolumn.\n\t\t\t */\n\t\t\tnode = (Node *) sbsref->refassgnexpr;\n\t\t}\n\t\telse if (IsA(node, CoerceToDomain))\n\t\t{\n\t\t\tcdomain = (CoerceToDomain *) node;\n\t\t\t/* If it's an explicit domain coercion, we're done */\n\t\t\tif (cdomain->coercionformat != COERCE_IMPLICIT_CAST)\n\t\t\t\tbreak;\n\t\t\t/* Tentatively descend past the CoerceToDomain */\n\t\t\tnode = (Node *) cdomain->arg;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\n\t/*\n\t * If we descended past a CoerceToDomain whose argument turned out not to\n\t * be a FieldStore or array assignment, back up to the CoerceToDomain.\n\t * (This is not enough to be fully correct if there are nested implicit\n\t * CoerceToDomains, but such cases shouldn't ever occur.)\n\t */\n\tif (cdomain && node == (Node *) cdomain->arg)\n\t\tnode = (Node *) cdomain;\n\n\treturn node;\n}\n\nstatic void\nprintSubscripts(SubscriptingRef *sbsref, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tListCell   *lowlist_item;\n\tListCell   *uplist_item;\n\n\tlowlist_item = list_head(sbsref->reflowerindexpr);\t/* could be NULL */\n\tforeach(uplist_item, sbsref->refupperindexpr)\n\t{\n\t\tappendStringInfoChar(buf, '[');\n\t\tif (lowlist_item)\n\t\t{\n\t\t\t/* If subexpression is NULL, get_rule_expr prints nothing */\n\t\t\tget_rule_expr((Node *) lfirst(lowlist_item), context, false);\n\t\t\tappendStringInfoChar(buf, ':');\n\t\t\tlowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item);\n\t\t}\n\t\t/* If subexpression is NULL, get_rule_expr prints nothing */\n\t\tget_rule_expr((Node *) lfirst(uplist_item), context, false);\n\t\tappendStringInfoChar(buf, ']');\n\t}\n}\n\n/*\n * get_relation_name\n *\t\tGet the unqualified name of a relation specified by OID\n *\n * This differs from the underlying get_rel_name() function in that it will\n * throw error instead of silently returning NULL if the OID is bad.\n */\nstatic char *\nget_relation_name(Oid relid)\n{\n\tchar\t   *relname = get_rel_name(relid);\n\n\tif (!relname)\n\t\telog(ERROR, \"cache lookup failed for relation %u\", relid);\n\treturn relname;\n}\n\n/*\n * generate_relation_or_shard_name\n *\t\tCompute the name to display for a relation or shard\n *\n * If the provided relid is equal to the provided distrelid, this function\n * returns a shard-extended relation name; otherwise, it falls through to a\n * simple generate_relation_name call.\n */\nstatic char *\ngenerate_relation_or_shard_name(Oid relid, Oid distrelid, int64 shardid,\n\t\t\t\t\t\t\t\tList *namespaces)\n{\n\tchar *relname = NULL;\n\n\tif (relid == distrelid)\n\t{\n\t\trelname = get_relation_name(relid);\n\n\t\tif (shardid > 0)\n\t\t{\n\t\t\tOid schemaOid = get_rel_namespace(relid);\n\t\t\tchar *schemaName = get_namespace_name_or_temp(schemaOid);\n\n\t\t\tAppendShardIdToName(&relname, shardid);\n\n\t\t\trelname = quote_qualified_identifier(schemaName, relname);\n\t\t}\n\t}\n\telse\n\t{\n\t\trelname = generate_relation_name(relid, namespaces);\n\t}\n\n\treturn relname;\n}\n\n/*\n * generate_relation_name\n *\t\tCompute the name to display for a relation specified by OID\n *\n * The result includes all necessary quoting and schema-prefixing.\n *\n * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.\n * We will forcibly qualify the relation name if it equals any CTE name\n * visible in the namespace list.\n */\nchar *\ngenerate_relation_name(Oid relid, List *namespaces)\n{\n\tHeapTuple\ttp;\n\tForm_pg_class reltup;\n\tbool\t\tneed_qual;\n\tListCell   *nslist;\n\tchar\t   *relname;\n\tchar\t   *nspname;\n\tchar\t   *result;\n\n\ttp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));\n\tif (!HeapTupleIsValid(tp))\n\t\telog(ERROR, \"cache lookup failed for relation %u\", relid);\n\treltup = (Form_pg_class) GETSTRUCT(tp);\n\trelname = NameStr(reltup->relname);\n\n\t/* Check for conflicting CTE name */\n\tneed_qual = false;\n\tforeach(nslist, namespaces)\n\t{\n\t\tdeparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);\n\t\tListCell   *ctlist;\n\n\t\tforeach(ctlist, dpns->ctes)\n\t\t{\n\t\t\tCommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);\n\n\t\t\tif (strcmp(cte->ctename, relname) == 0)\n\t\t\t{\n\t\t\t\tneed_qual = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (need_qual)\n\t\t\tbreak;\n\t}\n\n\t/* Otherwise, qualify the name if not visible in search path */\n\tif (!need_qual)\n\t\tneed_qual = !RelationIsVisible(relid);\n\n\tif (need_qual)\n\t\tnspname = get_namespace_name_or_temp(reltup->relnamespace);\n\telse\n\t\tnspname = NULL;\n\n\tresult = quote_qualified_identifier(nspname, relname);\n\n\tReleaseSysCache(tp);\n\n\treturn result;\n}\n\n/*\n * generate_rte_shard_name returns the qualified name of the shard given a\n * CITUS_RTE_SHARD range table entry.\n */\nstatic char *\ngenerate_rte_shard_name(RangeTblEntry *rangeTableEntry)\n{\n\tchar *shardSchemaName = NULL;\n\tchar *shardTableName = NULL;\n\n\tAssert(GetRangeTblKind(rangeTableEntry) == CITUS_RTE_SHARD);\n\n\tExtractRangeTblExtraData(rangeTableEntry, NULL, &shardSchemaName, &shardTableName,\n\t\t\t\t\t\t\t NULL);\n\n\treturn generate_fragment_name(shardSchemaName, shardTableName);\n}\n\n/*\n * generate_fragment_name\n *\t\tCompute the name to display for a shard or merged table\n *\n * The result includes all necessary quoting and schema-prefixing. The schema\n * name can be NULL for regular shards. For merged tables, they are always\n * declared within a job-specific schema, and therefore can't have null schema\n * names.\n */\nstatic char *\ngenerate_fragment_name(char *schemaName, char *tableName)\n{\n\tStringInfo fragmentNameString = makeStringInfo();\n\n\tif (schemaName != NULL)\n\t{\n\t\tappendStringInfo(fragmentNameString, \"%s.%s\", quote_identifier(schemaName),\n\t\t\t\t\t\t quote_identifier(tableName));\n\t}\n\telse\n\t{\n\t\tappendStringInfoString(fragmentNameString, quote_identifier(tableName));\n\t}\n\n\treturn fragmentNameString->data;\n}\n\n/*\n * generate_function_name\n *\t\tCompute the name to display for a function specified by OID,\n *\t\tgiven that it is being called with the specified actual arg names and\n *\t\ttypes.  (Those matter because of ambiguous-function resolution rules.)\n *\n * If we're dealing with a potentially variadic function (in practice, this\n * means a FuncExpr or Aggref, not some other way of calling a function), then\n * has_variadic must specify whether variadic arguments have been merged,\n * and *use_variadic_p will be set to indicate whether to print VARIADIC in\n * the output.  For non-FuncExpr cases, has_variadic should be false and\n * use_variadic_p can be NULL.\n *\n * inGroupBy must be true if we're deparsing a GROUP BY clause.\n *\n * The result includes all necessary quoting and schema-prefixing.\n */\nstatic char *\ngenerate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,\n\t\t\t\t\t   bool has_variadic, bool *use_variadic_p,\n\t\t\t\t\t   bool inGroupBy)\n{\n\tchar\t   *result;\n\tHeapTuple\tproctup;\n\tForm_pg_proc procform;\n\tchar\t   *proname;\n\tbool\t\tuse_variadic;\n\tchar\t   *nspname;\n\tFuncDetailCode p_result;\n\tOid\t\t\tp_funcid;\n\tOid\t\t\tp_rettype;\n\tbool\t\tp_retset;\n\tint\t\t\tp_nvargs;\n\tOid\t\t\tp_vatype;\n\tOid\t\t   *p_true_typeids;\n\tbool\t\tforce_qualify = false;\n\n\tproctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));\n\tif (!HeapTupleIsValid(proctup))\n\t\telog(ERROR, \"cache lookup failed for function %u\", funcid);\n\tprocform = (Form_pg_proc) GETSTRUCT(proctup);\n\tproname = NameStr(procform->proname);\n\n\t/*\n\t * Due to parser hacks to avoid needing to reserve CUBE, we need to force\n\t * qualification of some function names within GROUP BY.\n\t */\n\tif (inGroupBy)\n\t{\n\t\tif (strcmp(proname, \"cube\") == 0 || strcmp(proname, \"rollup\") == 0)\n\t\t\tforce_qualify = true;\n\t}\n\n\t/*\n\t * Determine whether VARIADIC should be printed.  We must do this first\n\t * since it affects the lookup rules in func_get_detail().\n\t *\n\t * Currently, we always print VARIADIC if the function has a merged\n\t * variadic-array argument.  Note that this is always the case for\n\t * functions taking a VARIADIC argument type other than VARIADIC ANY.\n\t *\n\t * In principle, if VARIADIC wasn't originally specified and the array\n\t * actual argument is deconstructable, we could print the array elements\n\t * separately and not print VARIADIC, thus more nearly reproducing the\n\t * original input.  For the moment that seems like too much complication\n\t * for the benefit, and anyway we do not know whether VARIADIC was\n\t * originally specified if it's a non-ANY type.\n\t */\n\tif (use_variadic_p)\n\t{\n\t\t/* Parser should not have set funcvariadic unless fn is variadic */\n\t\tAssert(!has_variadic || OidIsValid(procform->provariadic));\n\t\tuse_variadic = has_variadic;\n\t\t*use_variadic_p = use_variadic;\n\t}\n\telse\n\t{\n\t\tAssert(!has_variadic);\n\t\tuse_variadic = false;\n\t}\n\n\t/*\n\t * The idea here is to schema-qualify only if the parser would fail to\n\t * resolve the correct function given the unqualified func name with the\n\t * specified argtypes and VARIADIC flag.  But if we already decided to\n\t * force qualification, then we can skip the lookup and pretend we didn't\n\t * find it.\n\t */\n\tif (!force_qualify)\n\t\tp_result = func_get_detail(list_make1(makeString(proname)),\n\t\t\t\t\t\t\t\t   NIL, argnames, nargs, argtypes,\n\t\t\t\t\t\t\t\t   !use_variadic, true, false,\n\t\t\t\t\t\t\t\t   &p_funcid, &p_rettype,\n\t\t\t\t\t\t\t\t   &p_retset, &p_nvargs, &p_vatype,\n\t\t\t\t\t\t\t\t   &p_true_typeids, NULL);\n\telse\n\t{\n\t\tp_result = FUNCDETAIL_NOTFOUND;\n\t\tp_funcid = InvalidOid;\n\t}\n\n\tif ((p_result == FUNCDETAIL_NORMAL ||\n\t\t p_result == FUNCDETAIL_AGGREGATE ||\n\t\t p_result == FUNCDETAIL_WINDOWFUNC) &&\n\t\tp_funcid == funcid)\n\t\tnspname = NULL;\n\telse\n\t\tnspname = get_namespace_name_or_temp(procform->pronamespace);\n\n\tresult = quote_qualified_identifier(nspname, proname);\n\n\tReleaseSysCache(proctup);\n\n\treturn result;\n}\n\n/*\n * generate_operator_name\n *\t\tCompute the name to display for an operator specified by OID,\n *\t\tgiven that it is being called with the specified actual arg types.\n *\t\t(Arg types matter because of ambiguous-operator resolution rules.\n *\t\tPass InvalidOid for unused arg of a unary operator.)\n *\n * The result includes all necessary quoting and schema-prefixing,\n * plus the OPERATOR() decoration needed to use a qualified operator name\n * in an expression.\n */\nchar *\ngenerate_operator_name(Oid operid, Oid arg1, Oid arg2)\n{\n\tStringInfoData buf;\n\tHeapTuple\topertup;\n\tForm_pg_operator operform;\n\tchar\t   *oprname;\n\tchar\t   *nspname;\n\n\tinitStringInfo(&buf);\n\n\topertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));\n\tif (!HeapTupleIsValid(opertup))\n\t\telog(ERROR, \"cache lookup failed for operator %u\", operid);\n\toperform = (Form_pg_operator) GETSTRUCT(opertup);\n\toprname = NameStr(operform->oprname);\n\n\t/*\n\t * Unlike generate_operator_name() in postgres/src/backend/utils/adt/ruleutils.c,\n\t * we don't check if the operator is in current namespace or not. This is\n\t * because this check is costly when the operator is not in current namespace.\n\t */\n\tnspname = get_namespace_name_or_temp(operform->oprnamespace);\n\tAssert(nspname != NULL);\n\tappendStringInfo(&buf, \"OPERATOR(%s.\", quote_identifier(nspname));\n\tappendStringInfoString(&buf, oprname);\n\tappendStringInfoChar(&buf, ')');\n\n\tReleaseSysCache(opertup);\n\n\treturn buf.data;\n}\n\n/*\n * get_one_range_partition_bound_string\n *\t\tA C string representation of one range partition bound\n */\nchar *\nget_range_partbound_string(List *bound_datums)\n{\n\tdeparse_context context;\n\tStringInfo\tbuf = makeStringInfo();\n\tListCell   *cell;\n\tchar\t   *sep;\n\n\tmemset(&context, 0, sizeof(deparse_context));\n\tcontext.buf = buf;\n\n\tappendStringInfoChar(buf, '(');\n\tsep = \"\";\n\tforeach(cell, bound_datums)\n\t{\n\t\tPartitionRangeDatum *datum =\n\t\t\t\tlfirst_node(PartitionRangeDatum, cell);\n\n\t\tappendStringInfoString(buf, sep);\n\t\tif (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)\n\t\t\tappendStringInfoString(buf, \"MINVALUE\");\n\t\telse if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)\n\t\t\tappendStringInfoString(buf, \"MAXVALUE\");\n\t\telse\n\t\t{\n\t\t\tConst\t   *val = castNode(Const, datum->value);\n\n\t\t\tget_const_expr(val, &context, -1);\n\t\t}\n\t\tsep = \", \";\n\t}\n\tappendStringInfoChar(buf, ')');\n\n\treturn buf->data;\n}\n\n/*\n * Collect a list of OIDs of all sequences owned by the specified relation,\n * and column if specified.  If deptype is not zero, then only find sequences\n * with the specified dependency type.\n */\nList *\ngetOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)\n{\n\tList\t   *result = NIL;\n\tRelation\tdepRel;\n\tScanKeyData key[3];\n\tSysScanDesc scan;\n\tHeapTuple\ttup;\n\n\tdepRel = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_refclassid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(RelationRelationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_refobjid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(relid));\n\tif (attnum)\n\t\tScanKeyInit(&key[2],\n\t\t\t\t\tAnum_pg_depend_refobjsubid,\n\t\t\t\t\tBTEqualStrategyNumber, F_INT4EQ,\n\t\t\t\t\tInt32GetDatum(attnum));\n\n\tscan = systable_beginscan(depRel, DependReferenceIndexId, true,\n\t\t\t\t\t\t\t  NULL, attnum ? 3 : 2, key);\n\n\twhile (HeapTupleIsValid(tup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);\n\n\t\t/*\n\t\t * We assume any auto or internal dependency of a sequence on a column\n\t\t * must be what we are looking for.  (We need the relkind test because\n\t\t * indexes can also have auto dependencies on columns.)\n\t\t */\n\t\tif (deprec->classid == RelationRelationId &&\n\t\t\tdeprec->objsubid == 0 &&\n\t\t\tdeprec->refobjsubid != 0 &&\n\t\t\t(deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&\n\t\t\tget_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)\n\t\t{\n\t\t\tif (!deptype || deprec->deptype == deptype)\n\t\t\t\tresult = lappend_oid(result, deprec->objid);\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\n\ttable_close(depRel, AccessShareLock);\n\n\treturn result;\n}\n\n/*\n * get_insert_column_names_list Prepares the insert-column-names list. Any indirection\n * decoration needed on the column names can be inferred from the top targetlist.\n */\nstatic List *\nget_insert_column_names_list(List *targetList, StringInfo buf,\n\t\t\t\t\tdeparse_context *context, RangeTblEntry *rte)\n{\n\tchar *sep;\n\tListCell *l;\n\tList *strippedexprs;\n\n\tstrippedexprs = NIL;\n\tsep = \"\";\n\tappendStringInfoChar(buf, '(');\n\tforeach(l, targetList)\n\t{\n\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\n\t\tif (tle->resjunk)\n\t\t\tcontinue;\t\t\t/* ignore junk entries */\n\n\t\tappendStringInfoString(buf, sep);\n\t\tsep = \", \";\n\n\t\t/*\n\t\t * Put out name of target column; look in the catalogs, not at\n\t\t * tle->resname, since resname will fail to track RENAME.\n\t\t */\n\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t   quote_identifier(get_attname(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttle->resno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse)));\n\n\t\t/*\n\t\t * Print any indirection needed (subfields or subscripts), and strip\n\t\t * off the top-level nodes representing the indirection assignments.\n\t\t * Add the stripped expressions to strippedexprs.  (If it's a\n\t\t * single-VALUES statement, the stripped expressions are the VALUES to\n\t\t * print below.  Otherwise they're just Vars and not really\n\t\t * interesting.)\n\t\t */\n\t\tstrippedexprs = lappend(strippedexprs,\n\t\t\t\t\t\t\t\tprocessIndirection((Node *) tle->expr,\n\t\t\t\t\t\t\t\t\t\t\t\t   context));\n\t}\n\tappendStringInfoString(buf, \") \");\n\n\treturn strippedexprs;\n}\n#endif /* (PG_VERSION_NUM >= PG_VERSION_17) && (PG_VERSION_NUM < PG_VERSION_18) */\n"
  },
  {
    "path": "src/backend/distributed/deparser/ruleutils_18.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * ruleutils_18.c\n *\t  Functions to convert stored expressions/querytrees back to\n *\t  source text\n *\n * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group\n * Portions Copyright (c) 1994, Regents of the University of California\n *\n *\n * IDENTIFICATION\n *\t  src/backend/distributed/deparser/ruleutils_18.c\n *\n * This needs to be closely in sync with the core code.\n *-------------------------------------------------------------------------\n */\n#include \"pg_version_constants.h\"\n\n#include \"pg_config.h\"\n\n#if (PG_VERSION_NUM >= PG_VERSION_18) && (PG_VERSION_NUM < PG_VERSION_19)\n\n#include \"postgres.h\"\n\n#include <ctype.h>\n#include <unistd.h>\n#include <fcntl.h>\n\n#include \"access/amapi.h\"\n#include \"access/htup_details.h\"\n#include \"access/relation.h\"\n#include \"access/table.h\"\n#include \"catalog/pg_aggregate.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_extension.h\"\n#include \"catalog/pg_foreign_data_wrapper.h\"\n#include \"catalog/pg_language.h\"\n#include \"catalog/pg_opclass.h\"\n#include \"catalog/pg_operator.h\"\n#include \"catalog/pg_partitioned_table.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_statistic_ext.h\"\n#include \"catalog/pg_trigger.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/defrem.h\"\n#include \"commands/extension.h\"\n#include \"commands/tablespace.h\"\n#include \"common/keywords.h\"\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"executor/spi.h\"\n#include \"foreign/foreign.h\"\n#include \"funcapi.h\"\n#include \"mb/pg_wchar.h\"\n#include \"miscadmin.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/pathnodes.h\"\n#include \"optimizer/optimizer.h\"\n#include \"parser/parse_node.h\"\n#include \"parser/parse_agg.h\"\n#include \"parser/parse_func.h\"\n#include \"parser/parse_oper.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parser.h\"\n#include \"parser/parsetree.h\"\n#include \"rewrite/rewriteHandler.h\"\n#include \"rewrite/rewriteManip.h\"\n#include \"rewrite/rewriteSupport.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/ruleutils.h\"\n#include \"utils/snapmgr.h\"\n#include \"utils/syscache.h\"\n#include \"utils/typcache.h\"\n#include \"utils/varlena.h\"\n#include \"utils/xml.h\"\n\n\n/* ----------\n * Pretty formatting constants\n * ----------\n */\n\n/* Indent counts */\n#define PRETTYINDENT_STD\t\t8\n#define PRETTYINDENT_JOIN\t\t4\n#define PRETTYINDENT_VAR\t\t4\n\n#define PRETTYINDENT_LIMIT\t\t40\t/* wrap limit */\n\n/* Pretty flags */\n#define PRETTYFLAG_PAREN\t\t0x0001\n#define PRETTYFLAG_INDENT\t\t0x0002\n\n/* Default line length for pretty-print wrapping: 0 means wrap always */\n#define WRAP_COLUMN_DEFAULT\t\t0\n\n/* macros to test if pretty action needed */\n#define PRETTY_PAREN(context)\t((context)->prettyFlags & PRETTYFLAG_PAREN)\n#define PRETTY_INDENT(context)\t((context)->prettyFlags & PRETTYFLAG_INDENT)\n\n\n/* ----------\n * Local data types\n * ----------\n */\n\n/* Context info needed for invoking a recursive querytree display routine */\ntypedef struct\n{\n\tStringInfo\tbuf;\t\t\t/* output buffer to append to */\n\tList\t   *namespaces;\t\t/* List of deparse_namespace nodes */\n\tTupleDesc\tresultDesc;\t\t/* if top level of a view, the view's tupdesc */\n\tList\t   *targetList;\t\t/* Current query level's SELECT targetlist */\n\tList\t   *windowClause;\t/* Current query level's WINDOW clause */\n\tint\t\t\tprettyFlags;\t/* enabling of pretty-print functions */\n\tint\t\t\twrapColumn;\t\t/* max line length, or -1 for no limit */\n\tint\t\t\tindentLevel;\t/* current indent level for prettyprint */\n\tbool\t\tvarprefix;\t\t/* true to print prefixes on Vars */\n\tOid\t\t\tdistrelid;\t\t/* the distributed table being modified, if valid */\n\tint64\t\tshardid;\t\t/* a distributed table's shardid, if positive */\n\tbool\t\tcolNamesVisible;\t/* do we care about output column names? */\n\tbool\t\tinGroupBy;\t\t/* deparsing GROUP BY clause? */\n\tbool\t\tvarInOrderBy;\t/* deparsing simple Var in ORDER BY? */\n\tBitmapset  *appendparents;\t/* if not null, map child Vars of these relids\n\t\t\t\t\t\t\t\t * back to the parent rel */\n} deparse_context;\n\n/*\n * Each level of query context around a subtree needs a level of Var namespace.\n * A Var having varlevelsup=N refers to the N'th item (counting from 0) in\n * the current context's namespaces list.\n *\n * The rangetable is the list of actual RTEs from the query tree, and the\n * cte list is the list of actual CTEs.\n *\n * rtable_names holds the alias name to be used for each RTE (either a C\n * string, or NULL for nameless RTEs such as unnamed joins).\n * rtable_columns holds the column alias names to be used for each RTE.\n *\n * In some cases we need to make names of merged JOIN USING columns unique\n * across the whole query, not only per-RTE.  If so, unique_using is true\n * and using_names is a list of C strings representing names already assigned\n * to USING columns.\n *\n * When deparsing plan trees, there is always just a single item in the\n * deparse_namespace list (since a plan tree never contains Vars with\n * varlevelsup > 0).  We store the PlanState node that is the immediate\n * parent of the expression to be deparsed, as well as a list of that\n * PlanState's ancestors.  In addition, we store its outer and inner subplan\n * state nodes, as well as their plan nodes' targetlists, and the index tlist\n * if the current plan node might contain INDEX_VAR Vars.  (These fields could\n * be derived on-the-fly from the current PlanState, but it seems notationally\n * clearer to set them up as separate fields.)\n */\ntypedef struct\n{\n\tList\t   *rtable;\t\t\t/* List of RangeTblEntry nodes */\n\tList\t   *rtable_names;\t/* Parallel list of names for RTEs */\n\tList\t   *rtable_columns; /* Parallel list of deparse_columns structs */\n\tList\t   *subplans;\t\t/* List of Plan trees for SubPlans */\n\tList\t   *ctes;\t\t\t/* List of CommonTableExpr nodes */\n\tAppendRelInfo **appendrels; /* Array of AppendRelInfo nodes, or NULL */\n\tchar\t   *ret_old_alias;\t/* alias for OLD in RETURNING list */\n\tchar\t   *ret_new_alias;\t/* alias for NEW in RETURNING list */\n\t/* Workspace for column alias assignment: */\n\tbool\t\tunique_using;\t/* Are we making USING names globally unique */\n\tList\t   *using_names;\t/* List of assigned names for USING columns */\n\t/* Remaining fields are used only when deparsing a Plan tree: */\n\tPlan\t   *plan;\t\t\t/* immediate parent of current expression */\n\tList\t   *ancestors;\t\t/* ancestors of planstate */\n\tPlan\t   *outer_plan;\t\t/* outer subnode, or NULL if none */\n\tPlan\t   *inner_plan;\t\t/* inner subnode, or NULL if none */\n\tList\t   *outer_tlist;\t/* referent for OUTER_VAR Vars */\n\tList\t   *inner_tlist;\t/* referent for INNER_VAR Vars */\n\tList\t   *index_tlist;\t/* referent for INDEX_VAR Vars */\n\t/* Special namespace representing a function signature: */\n\tchar\t   *funcname;\n\tint\t\t\tnumargs;\n\tchar\t  **argnames;\n} deparse_namespace;\n\n/* Callback signature for resolve_special_varno() */\ntypedef void (*rsv_callback) (Node *node, deparse_context *context,\n\t\t\t\t\t\t\t  void *callback_arg);\n\n/*\n * Per-relation data about column alias names.\n *\n * Selecting aliases is unreasonably complicated because of the need to dump\n * rules/views whose underlying tables may have had columns added, deleted, or\n * renamed since the query was parsed.  We must nonetheless print the rule/view\n * in a form that can be reloaded and will produce the same results as before.\n *\n * For each RTE used in the query, we must assign column aliases that are\n * unique within that RTE.  SQL does not require this of the original query,\n * but due to factors such as *-expansion we need to be able to uniquely\n * reference every column in a decompiled query.  As long as we qualify all\n * column references, per-RTE uniqueness is sufficient for that.\n *\n * However, we can't ensure per-column name uniqueness for unnamed join RTEs,\n * since they just inherit column names from their input RTEs, and we can't\n * rename the columns at the join level.  Most of the time this isn't an issue\n * because we don't need to reference the join's output columns as such; we\n * can reference the input columns instead.  That approach can fail for merged\n * JOIN USING columns, however, so when we have one of those in an unnamed\n * join, we have to make that column's alias globally unique across the whole\n * query to ensure it can be referenced unambiguously.\n *\n * Another problem is that a JOIN USING clause requires the columns to be\n * merged to have the same aliases in both input RTEs, and that no other\n * columns in those RTEs or their children conflict with the USING names.\n * To handle that, we do USING-column alias assignment in a recursive\n * traversal of the query's jointree.  When descending through a JOIN with\n * USING, we preassign the USING column names to the child columns, overriding\n * other rules for column alias assignment.  We also mark each RTE with a list\n * of all USING column names selected for joins containing that RTE, so that\n * when we assign other columns' aliases later, we can avoid conflicts.\n *\n * Another problem is that if a JOIN's input tables have had columns added or\n * deleted since the query was parsed, we must generate a column alias list\n * for the join that matches the current set of input columns --- otherwise, a\n * change in the number of columns in the left input would throw off matching\n * of aliases to columns of the right input.  Thus, positions in the printable\n * column alias list are not necessarily one-for-one with varattnos of the\n * JOIN, so we need a separate new_colnames[] array for printing purposes.\n *\n* Finally, when dealing with wide tables we risk O(N^2) costs in assigning\n * non-duplicate column names.  We ameliorate that by using a hash table that\n * holds all the strings appearing in colnames, new_colnames, and parentUsing.\n */\ntypedef struct\n{\n\t/*\n\t * colnames is an array containing column aliases to use for columns that\n\t * existed when the query was parsed.  Dropped columns have NULL entries.\n\t * This array can be directly indexed by varattno to get a Var's name.\n\t *\n\t * Non-NULL entries are guaranteed unique within the RTE, *except* when\n\t * this is for an unnamed JOIN RTE.  In that case we merely copy up names\n\t * from the two input RTEs.\n\t *\n\t * During the recursive descent in set_using_names(), forcible assignment\n\t * of a child RTE's column name is represented by pre-setting that element\n\t * of the child's colnames array.  So at that stage, NULL entries in this\n\t * array just mean that no name has been preassigned, not necessarily that\n\t * the column is dropped.\n\t */\n\tint\t\t\tnum_cols;\t\t/* length of colnames[] array */\n\tchar\t  **colnames;\t\t/* array of C strings and NULLs */\n\n\t/*\n\t * new_colnames is an array containing column aliases to use for columns\n\t * that would exist if the query was re-parsed against the current\n\t * definitions of its base tables.  This is what to print as the column\n\t * alias list for the RTE.  This array does not include dropped columns,\n\t * but it will include columns added since original parsing.  Indexes in\n\t * it therefore have little to do with current varattno values.  As above,\n\t * entries are unique unless this is for an unnamed JOIN RTE.  (In such an\n\t * RTE, we never actually print this array, but we must compute it anyway\n\t * for possible use in computing column names of upper joins.) The\n\t * parallel array is_new_col marks which of these columns are new since\n\t * original parsing.  Entries with is_new_col false must match the\n\t * non-NULL colnames entries one-for-one.\n\t */\n\tint\t\t\tnum_new_cols;\t/* length of new_colnames[] array */\n\tchar\t  **new_colnames;\t/* array of C strings */\n\tbool\t   *is_new_col;\t\t/* array of bool flags */\n\n\t/* This flag tells whether we should actually print a column alias list */\n\tbool\t\tprintaliases;\n\n\t/* This list has all names used as USING names in joins above this RTE */\n\tList\t   *parentUsing;\t/* names assigned to parent merged columns */\n\n\t/*\n\t * If this struct is for a JOIN RTE, we fill these fields during the\n\t * set_using_names() pass to describe its relationship to its child RTEs.\n\t *\n\t * leftattnos and rightattnos are arrays with one entry per existing\n\t * output column of the join (hence, indexable by join varattno).  For a\n\t * simple reference to a column of the left child, leftattnos[i] is the\n\t * child RTE's attno and rightattnos[i] is zero; and conversely for a\n\t * column of the right child.  But for merged columns produced by JOIN\n\t * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.\n\t * Also, if the column has been dropped, both are zero.\n\t *\n\t * If it's a JOIN USING, usingNames holds the alias names selected for the\n\t * merged columns (these might be different from the original USING list,\n\t * if we had to modify names to achieve uniqueness).\n\t */\n\tint\t\t\tleftrti;\t\t/* rangetable index of left child */\n\tint\t\t\trightrti;\t\t/* rangetable index of right child */\n\tint\t\t   *leftattnos;\t\t/* left-child varattnos of join cols, or 0 */\n\tint\t\t   *rightattnos;\t/* right-child varattnos of join cols, or 0 */\n\tList\t   *usingNames;\t\t/* names assigned to merged columns */\n\n\t/*\n\t * Hash table holding copies of all the strings appearing in this struct's\n\t * colnames, new_colnames, and parentUsing.  We use a hash table only for\n\t * sufficiently wide relations, and only during the colname-assignment\n\t * functions set_relation_column_names and set_join_column_names;\n\t * otherwise, names_hash is NULL.\n\t */\n\tHTAB\t   *names_hash;\t\t/* entries are just strings */\n} deparse_columns;\n\n/* This macro is analogous to rt_fetch(), but for deparse_columns structs */\n#define deparse_columns_fetch(rangetable_index, dpns) \\\n\t((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))\n\n/*\n * Entry in set_rtable_names' hash table\n */\ntypedef struct\n{\n\tchar\t\tname[NAMEDATALEN];\t/* Hash key --- must be first */\n\tint\t\t\tcounter;\t\t/* Largest addition used so far for name */\n} NameHashEntry;\n\n\n/* ----------\n * Local functions\n *\n * Most of these functions used to use fixed-size buffers to build their\n * results.  Now, they take an (already initialized) StringInfo object\n * as a parameter, and append their text output to its contents.\n * ----------\n */\nstatic void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,\n\t\t\t\t Bitmapset *rels_used);\nstatic void set_deparse_for_query(deparse_namespace *dpns, Query *query,\n\t\t\t\t\t  List *parent_namespaces);\nstatic bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);\nstatic void set_using_names(deparse_namespace *dpns, Node *jtnode,\n\t\t\t\tList *parentUsing);\nstatic void set_relation_column_names(deparse_namespace *dpns,\n\t\t\t\t\t\t  RangeTblEntry *rte,\n\t\t\t\t\t\t  deparse_columns *colinfo);\nstatic void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,\n\t\t\t\t\t  deparse_columns *colinfo);\nstatic bool colname_is_unique(const char *colname, deparse_namespace *dpns,\n\t\t\t\t  deparse_columns *colinfo);\nstatic char *make_colname_unique(char *colname, deparse_namespace *dpns,\n\t\t\t\t\tdeparse_columns *colinfo);\nstatic void expand_colnames_array_to(deparse_columns *colinfo, int n);\nstatic void build_colinfo_names_hash(deparse_columns *colinfo);\nstatic void add_to_names_hash(deparse_columns *colinfo, const char *name);\nstatic void destroy_colinfo_names_hash(deparse_columns *colinfo);\nstatic void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,\n\t\t\t\t\t  deparse_columns *colinfo);\nstatic char *get_rtable_name(int rtindex, deparse_context *context);\nstatic void set_deparse_plan(deparse_namespace *dpns, Plan *plan);\nstatic Plan *find_recursive_union(deparse_namespace *dpns,\n\t\t\t\t\t\t\t\t  WorkTableScan *wtscan);\nstatic void push_child_plan(deparse_namespace *dpns, Plan *plan,\n\t\t\t\tdeparse_namespace *save_dpns);\nstatic void pop_child_plan(deparse_namespace *dpns,\n\t\t\t   deparse_namespace *save_dpns);\nstatic void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,\n\t\t\t\t   deparse_namespace *save_dpns);\nstatic void pop_ancestor_plan(deparse_namespace *dpns,\n\t\t\t\t  deparse_namespace *save_dpns);\nstatic void get_query_def(Query *query, StringInfo buf, List *parentnamespace,\n\t\t\t  TupleDesc resultDesc, bool colNamesVisible,\n\t\t\t  int prettyFlags, int wrapColumn, int startIndent);\nstatic void get_query_def_extended(Query *query, StringInfo buf,\n\t\t\t\tList *parentnamespace, Oid distrelid, int64 shardid,\n\t\t\t\tTupleDesc resultDesc, bool colNamesVisible,\n\t\t\t\tint prettyFlags, int wrapColumn,\n\t\t\t\tint startIndent);\nstatic void get_values_def(List *values_lists, deparse_context *context);\nstatic void get_with_clause(Query *query, deparse_context *context);\nstatic void get_select_query_def(Query *query, deparse_context *context);\nstatic void get_insert_query_def(Query *query, deparse_context *context);\nstatic void get_update_query_def(Query *query, deparse_context *context);\nstatic void get_update_query_targetlist_def(Query *query, List *targetList,\n\t\t\t\t\t\t\t\tdeparse_context *context,\n\t\t\t\t\t\t\t\tRangeTblEntry *rte);\nstatic void get_delete_query_def(Query *query, deparse_context *context);\nstatic void get_merge_query_def(Query *query, deparse_context *context);\nstatic void get_utility_query_def(Query *query, deparse_context *context);\nstatic void get_basic_select_query(Query *query, deparse_context *context);\nstatic void get_target_list(List *targetList, deparse_context *context);\nstatic void get_returning_clause(Query *query, deparse_context *context);\nstatic void get_setop_query(Node *setOp, Query *query,\n\t\t\t\t\t\t\tdeparse_context *context);\nstatic Node *get_rule_sortgroupclause(Index ref, List *tlist,\n\t\t\t\t\t\t bool force_colno,\n\t\t\t\t\t\t deparse_context *context);\nstatic void get_rule_groupingset(GroupingSet *gset, List *targetlist,\n\t\t\t\t\t bool omit_parens, deparse_context *context);\nstatic void get_rule_orderby(List *orderList, List *targetList,\n\t\t\t\t bool force_colno, deparse_context *context);\nstatic void get_rule_windowclause(Query *query, deparse_context *context);\nstatic void get_rule_windowspec(WindowClause *wc, List *targetList,\n\t\t\t\t\tdeparse_context *context);\nstatic void get_window_frame_options(int frameOptions,\n\t\t\t\t\t\t\t\t\t Node *startOffset, Node *endOffset,\n\t\t\t\t\t\t\t\t\t deparse_context *context);\nstatic char *get_variable(Var *var, int levelsup, bool istoplevel,\n\t\t\t deparse_context *context);\nstatic void get_special_variable(Node *node, deparse_context *context,\n\t\t\t\t\t void *callback_arg);\nstatic void resolve_special_varno(Node *node, deparse_context *context,\n\t\t\t\t\trsv_callback callback, void *callback_arg);\nstatic Node *find_param_referent(Param *param, deparse_context *context,\n\t\t\t\t\tdeparse_namespace **dpns_p, ListCell **ancestor_cell_p);\nstatic SubPlan *find_param_generator(Param *param, deparse_context *context,\n\t\t\t\t\t\t\t\t\t int *column_p);\nstatic SubPlan *find_param_generator_initplan(Param *param, Plan *plan,\n\t\t\t\t\t\t\t\t\t\t\t  int *column_p);\nstatic void get_parameter(Param *param, deparse_context *context);\nstatic const char *get_simple_binary_op_name(OpExpr *expr);\nstatic bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);\nstatic void appendContextKeyword(deparse_context *context, const char *str,\n\t\t\t\t\t int indentBefore, int indentAfter, int indentPlus);\nstatic void removeStringInfoSpaces(StringInfo str);\nstatic void get_rule_expr(Node *node, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_rule_expr_toplevel(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit);\nstatic void get_rule_list_toplevel(List *lst, deparse_context *context,\n\t\t\t\t\t\t\t\t   bool showimplicit);\nstatic void get_rule_expr_funccall(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit);\nstatic bool looks_like_function(Node *node);\nstatic void get_oper_expr(OpExpr *expr, deparse_context *context);\nstatic void get_func_expr(FuncExpr *expr, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_proc_expr(CallStmt *stmt, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_agg_expr(Aggref *aggref, deparse_context *context,\n\t\t\t Aggref *original_aggref);\nstatic void get_agg_expr_helper(Aggref *aggref, deparse_context *context,\n\t\t\t\t\t\t\t\tAggref *original_aggref, const char *funcname,\n\t\t\t\t\t\t\t\tconst char *options, bool is_json_objectagg);\nstatic void get_agg_combine_expr(Node *node, deparse_context *context,\n\t\t\t\t\t void *callback_arg);\nstatic void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);\nstatic void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,\n\t\t\t\t\t\t\t\t\t   const char *funcname, const char *options,\n\t\t\t\t\t\t\t\t\t   bool is_json_objectagg);\nstatic bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);\nstatic void get_coercion_expr(Node *arg, deparse_context *context,\n\t\t\t\t  Oid resulttype, int32 resulttypmod,\n\t\t\t\t  Node *parentNode);\nstatic void get_const_expr(Const *constval, deparse_context *context,\n\t\t\t   int showtype);\nstatic void get_const_collation(Const *constval, deparse_context *context);\nstatic void get_json_format(JsonFormat *format, StringInfo buf);\nstatic void get_json_returning(JsonReturning *returning, StringInfo buf,\n\t\t\t\t\t\t\t   bool json_format_by_default);\nstatic void get_json_constructor(JsonConstructorExpr *ctor,\n\t\t\t\t\t\t\t\t deparse_context *context, bool showimplicit);\nstatic void get_json_constructor_options(JsonConstructorExpr *ctor,\n\t\t\t\t\t\t\t\t\t\t StringInfo buf);\nstatic void get_json_agg_constructor(JsonConstructorExpr *ctor,\n\t\t\t\t\t\t\t\t\t deparse_context *context,\n\t\t\t\t\t\t\t\t\t const char *funcname,\n\t\t\t\t\t\t\t\t\t bool is_json_objectagg);\nstatic void simple_quote_literal(StringInfo buf, const char *val);\nstatic void get_sublink_expr(SubLink *sublink, deparse_context *context);\nstatic void get_tablefunc(TableFunc *tf, deparse_context *context,\n\t\t\t  bool showimplicit);\nstatic void get_from_clause(Query *query, const char *prefix,\n\t\t\t\tdeparse_context *context);\nstatic void get_from_clause_item(Node *jtnode, Query *query,\n\t\t\t\t\t deparse_context *context);\nstatic void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,\n\t\t\t\t\t\t  deparse_context *context);\nstatic void get_column_alias_list(deparse_columns *colinfo,\n\t\t\t\t\t  deparse_context *context);\nstatic void get_from_clause_coldeflist(RangeTblFunction *rtfunc,\n\t\t\t\t\t\t   deparse_columns *colinfo,\n\t\t\t\t\t\t   deparse_context *context);\nstatic void get_tablesample_def(TableSampleClause *tablesample,\n\t\t\t\t\tdeparse_context *context);\nstatic void get_opclass_name(Oid opclass, Oid actual_datatype,\n\t\t\t\t StringInfo buf);\nstatic Node *processIndirection(Node *node, deparse_context *context);\nstatic void printSubscripts(SubscriptingRef *aref, deparse_context *context);\nstatic char *get_relation_name(Oid relid);\nstatic char *generate_relation_or_shard_name(Oid relid, Oid distrelid,\n\t\t\t\tint64 shardid, List *namespaces);\nstatic char *generate_rte_shard_name(RangeTblEntry *rangeTableEntry);\nstatic char *generate_fragment_name(char *schemaName, char *tableName);\nstatic char *generate_function_name(Oid funcid, int nargs,\n\t\t\t\t\t   List *argnames, Oid *argtypes,\n\t\t\t\t\t   bool has_variadic, bool *use_variadic_p,\n\t\t\t\t\t   bool inGroupBy);\nstatic List *get_insert_column_names_list(List *targetList, StringInfo buf, deparse_context *context, RangeTblEntry *rte);\nstatic void get_json_path_spec(Node *path_spec, deparse_context *context,\n\t\t\t\t\t\t\t   bool showimplicit);\nstatic void get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,\n\t\t\t\t\t\t\t\t   deparse_context *context,\n\t\t\t\t\t\t\t\t   bool showimplicit);\nstatic void get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,\n\t\t\t\t\t\t\t\t\t\t  deparse_context *context,\n\t\t\t\t\t\t\t\t\t\t  bool showimplicit,\n\t\t\t\t\t\t\t\t\t\t  bool needcomma);\nstatic void\nmap_var_through_join_alias(deparse_namespace *dpns, Var *v);\nstatic Var *unwrap_simple_var(Node *expr);\nstatic bool dpns_has_named_join(const deparse_namespace *dpns);\nstatic inline bool\nvar_matches_base(const Var *v, Index want_varno, AttrNumber want_attno);\n\n#define only_marker(rte)  ((rte)->inh ? \"\" : \"ONLY \")\n\n\n\n/*\n * pg_get_query_def parses back one query tree, and outputs the resulting query\n * string into given buffer.\n */\nvoid\npg_get_query_def(Query *query, StringInfo buffer)\n{\n\tget_query_def(query, buffer, NIL, NULL, false, 0, WRAP_COLUMN_DEFAULT, 0);\n}\n\n/*\n * get_merged_argument_list merges both the IN and OUT arguments lists into one and\n * also eliminates the INOUT duplicates(present in both the lists). After merging both\n * the lists, it returns all the named-arguments in a list(mergedNamedArgList) along\n * with their types(mergedNamedArgTypes), final argument list(mergedArgumentList), and\n * the total number of arguments(totalArguments).\n */\nbool\nget_merged_argument_list(CallStmt *stmt, List **mergedNamedArgList,\n\t\t\t\t\t   Oid **mergedNamedArgTypes,\n\t\t\t\t\t   List **mergedArgumentList,\n\t\t\t\t\t   int *totalArguments)\n{\n\n\tOid  functionOid = stmt->funcexpr->funcid;\n\tList *namedArgList = NIL;\n\tList *finalArgumentList = NIL;\n\tOid  *finalArgTypes;\n\tOid  *argTypes = NULL;\n\tchar *argModes = NULL;\n\tchar **argNames = NULL;\n\tint  argIndex = 0;\n\n\tHeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));\n\tif (!HeapTupleIsValid(proctup))\n\t{\n\t\telog(ERROR, \"cache lookup failed for function %u\", functionOid);\n\t}\n\n\tint defArgs = get_func_arg_info(proctup, &argTypes, &argNames, &argModes);\n\tReleaseSysCache(proctup);\n\n\tif (argModes == NULL)\n\t{\n\t\t/* No OUT arguments */\n\t\treturn false;\n\t}\n\n\t/*\n\t * Passed arguments Includes IN, OUT, INOUT (in both the lists) and VARIADIC arguments,\n\t * which means INOUT arguments are double counted.\n\t */\n\tint numberOfArgs = list_length(stmt->funcexpr->args) + list_length(stmt->outargs);\n\tint totalInoutArgs = 0;\n\n\t/* Let's count INOUT arguments from the defined number of arguments */\n\tfor (argIndex=0; argIndex < defArgs; ++argIndex)\n\t{\n\t\tif (argModes[argIndex] == PROARGMODE_INOUT)\n\t\t\ttotalInoutArgs++;\n\t}\n\n\t/* Remove the duplicate INOUT counting */\n\tnumberOfArgs = numberOfArgs - totalInoutArgs;\n\tfinalArgTypes = palloc0(sizeof(Oid) * numberOfArgs);\n\n\tListCell *inArgCell = list_head(stmt->funcexpr->args);\n\tListCell *outArgCell = list_head(stmt->outargs);\n\n\tfor (argIndex=0; argIndex < numberOfArgs; ++argIndex)\n\t{\n\t\tswitch (argModes[argIndex])\n\t\t{\n\t\t\tcase PROARGMODE_IN:\n\t\t\tcase PROARGMODE_VARIADIC:\n\t\t\t{\n\t\t\t\tNode *arg = (Node *) lfirst(inArgCell);\n\n\t\t\t\tif (IsA(arg, NamedArgExpr))\n\t\t\t\t\tnamedArgList = lappend(namedArgList, ((NamedArgExpr *) arg)->name);\n\t\t\t\tfinalArgTypes[argIndex] = exprType(arg);\n\t\t\t\tfinalArgumentList = lappend(finalArgumentList, arg);\n\t\t\t\tinArgCell = lnext(stmt->funcexpr->args, inArgCell);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROARGMODE_OUT:\n\t\t\t{\n\t\t\t\tNode *arg = (Node *) lfirst(outArgCell);\n\n\t\t\t\tif (IsA(arg, NamedArgExpr))\n\t\t\t\t\tnamedArgList = lappend(namedArgList, ((NamedArgExpr *) arg)->name);\n\t\t\t\tfinalArgTypes[argIndex] = exprType(arg);\n\t\t\t\tfinalArgumentList = lappend(finalArgumentList, arg);\n\t\t\t\toutArgCell = lnext(stmt->outargs, outArgCell);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROARGMODE_INOUT:\n\t\t\t{\n\t\t\t\tNode *arg = (Node *) lfirst(inArgCell);\n\n\t\t\t\tif (IsA(arg, NamedArgExpr))\n\t\t\t\t\tnamedArgList = lappend(namedArgList, ((NamedArgExpr *) arg)->name);\n\t\t\t\tfinalArgTypes[argIndex] = exprType(arg);\n\t\t\t\tfinalArgumentList = lappend(finalArgumentList, arg);\n\t\t\t\tinArgCell = lnext(stmt->funcexpr->args, inArgCell);\n\t\t\t\toutArgCell = lnext(stmt->outargs, outArgCell);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase PROARGMODE_TABLE:\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\telog(ERROR, \"Unhandled procedure argument mode[%d]\", argModes[argIndex]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * After eliminating INOUT duplicates and merging OUT arguments, we now\n\t * have the final list of arguments.\n\t */\n\tif (defArgs != list_length(finalArgumentList))\n\t{\n\t\telog(ERROR, \"Insufficient number of args passed[%d] for function[%s]\",\n\t\t\t\t\t\t\t\t\t\t\tlist_length(finalArgumentList),\n\t\t\t\t\t\t\t\t\t\t\tget_func_name(functionOid));\n\t}\n\n\tif (list_length(finalArgumentList) > FUNC_MAX_ARGS)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_TOO_MANY_ARGUMENTS),\n\t\t\t\t errmsg(\"too many arguments[%d] for function[%s]\",\n\t\t\t\t\t\t\t\t\t\t\tlist_length(finalArgumentList),\n\t\t\t\t\t\t\t\t\t\t\tget_func_name(functionOid))));\n\t}\n\n\t*mergedNamedArgList =  namedArgList;\n\t*mergedNamedArgTypes = finalArgTypes;\n\t*mergedArgumentList = finalArgumentList;\n\t*totalArguments = numberOfArgs;\n\n\treturn true;\n}\n\n/*\n * pg_get_rule_expr deparses an expression and returns the result as a string.\n */\nchar *\npg_get_rule_expr(Node *expression)\n{\n\tbool showImplicitCasts = true;\n\tdeparse_context context;\n\tStringInfo buffer = makeStringInfo();\n\n\t/*\n\t * Set search_path to NIL so that all objects outside of pg_catalog will be\n\t * schema-prefixed. pg_catalog will be added automatically when we call\n\t * PushEmptySearchPath().\n\t */\n\tint saveNestLevel = PushEmptySearchPath();\n\n\tcontext.buf = buffer;\n\tcontext.namespaces = NIL;\n\tcontext.resultDesc = NULL;\n\tcontext.targetList = NIL;\n\tcontext.windowClause = NIL;\n\tcontext.varprefix = false;\n\tcontext.prettyFlags = 0;\n\tcontext.wrapColumn = WRAP_COLUMN_DEFAULT;\n\tcontext.indentLevel = 0;\n\tcontext.colNamesVisible = true;\n\tcontext.inGroupBy = false;\n\tcontext.varInOrderBy = false;\n\tcontext.distrelid = InvalidOid;\n\tcontext.shardid = INVALID_SHARD_ID;\n\n\tget_rule_expr(expression, &context, showImplicitCasts);\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n\n\treturn buffer->data;\n}\n\n/*\n * set_rtable_names: select RTE aliases to be used in printing a query\n *\n * We fill in dpns->rtable_names with a list of names that is one-for-one with\n * the already-filled dpns->rtable list.  Each RTE name is unique among those\n * in the new namespace plus any ancestor namespaces listed in\n * parent_namespaces.\n *\n * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.\n *\n * Note that this function is only concerned with relation names, not column\n * names.\n */\nstatic void\nset_rtable_names(deparse_namespace *dpns, List *parent_namespaces,\n\t\t\t\t Bitmapset *rels_used)\n{\n\tHASHCTL\t\thash_ctl;\n\tHTAB\t   *names_hash;\n\tNameHashEntry *hentry;\n\tbool\t\tfound;\n\tint\t\t\trtindex;\n\tListCell   *lc;\n\n\tdpns->rtable_names = NIL;\n\t/* nothing more to do if empty rtable */\n\tif (dpns->rtable == NIL)\n\t\treturn;\n\n\t/*\n\t * We use a hash table to hold known names, so that this process is O(N)\n\t * not O(N^2) for N names.\n\t */\n\thash_ctl.keysize = NAMEDATALEN;\n\thash_ctl.entrysize = sizeof(NameHashEntry);\n\thash_ctl.hcxt = CurrentMemoryContext;\n\tnames_hash = hash_create(\"set_rtable_names names\",\n\t\t\t\t\t\t\t list_length(dpns->rtable),\n\t\t\t\t\t\t\t &hash_ctl,\n\t\t\t\t\t\t\t HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);\n\n\t/* Preload the hash table with names appearing in parent_namespaces */\n\tforeach(lc, parent_namespaces)\n\t{\n\t\tdeparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);\n\t\tListCell   *lc2;\n\n\t\tforeach(lc2, olddpns->rtable_names)\n\t\t{\n\t\t\tchar\t   *oldname = (char *) lfirst(lc2);\n\n\t\t\tif (oldname == NULL)\n\t\t\t\tcontinue;\n\t\t\thentry = (NameHashEntry *) hash_search(names_hash,\n\t\t\t\t\t\t\t\t\t\t\t\t   oldname,\n\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t   &found);\n\t\t\t/* we do not complain about duplicate names in parent namespaces */\n\t\t\thentry->counter = 0;\n\t\t}\n\t}\n\n\t/* Now we can scan the rtable */\n\trtindex = 1;\n\tforeach(lc, dpns->rtable)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n\t\tchar\t   *refname;\n\n\t\t/* Just in case this takes an unreasonable amount of time ... */\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tif (rels_used && !bms_is_member(rtindex, rels_used))\n\t\t{\n\t\t\t/* Ignore unreferenced RTE */\n\t\t\trefname = NULL;\n\t\t}\n\t\telse if (rte->alias)\n\t\t{\n\t\t\t/* If RTE has a user-defined alias, prefer that */\n\t\t\trefname = rte->alias->aliasname;\n\t\t}\n\t\telse if (rte->rtekind == RTE_RELATION)\n\t\t{\n\t\t\t/* Use the current actual name of the relation */\n\t\t\trefname = get_rel_name(rte->relid);\n\t\t}\n\t\telse if (rte->rtekind == RTE_JOIN)\n\t\t{\n\t\t\t/* Unnamed join has no refname */\n\t\t\trefname = NULL;\n\t\t}\n\t\telse if (rte->rtekind == RTE_GROUP)\n\t\t{\n\t\t\t/* Use the name of the group */\n\t\t\trefname = NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* Otherwise use whatever the parser assigned */\n\t\t\trefname = rte->eref->aliasname;\n\t\t}\n\n\t\t/*\n\t\t * If the selected name isn't unique, append digits to make it so, and\n\t\t * make a new hash entry for it once we've got a unique name.  For a\n\t\t * very long input name, we might have to truncate to stay within\n\t\t * NAMEDATALEN.\n\t\t */\n\t\tif (refname)\n\t\t{\n\t\t\thentry = (NameHashEntry *) hash_search(names_hash,\n\t\t\t\t\t\t\t\t\t\t\t\t   refname,\n\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t   &found);\n\t\t\tif (found)\n\t\t\t{\n\t\t\t\t/* Name already in use, must choose a new one */\n\t\t\t\tint\t\t\trefnamelen = strlen(refname);\n\t\t\t\tchar\t   *modname = (char *) palloc(refnamelen + 16);\n\t\t\t\tNameHashEntry *hentry2;\n\n\t\t\t\tdo\n\t\t\t\t{\n\t\t\t\t\thentry->counter++;\n\t\t\t\t\tfor (;;)\n\t\t\t\t\t{\n\t\t\t\t\t\tmemcpy(modname, refname, refnamelen);\n\t\t\t\t\t\tsprintf(modname + refnamelen, \"_%d\", hentry->counter);\n\t\t\t\t\t\tif (strlen(modname) < NAMEDATALEN)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t/* drop chars from refname to keep all the digits */\n\t\t\t\t\t\trefnamelen = pg_mbcliplen(refname, refnamelen,\n\t\t\t\t\t\t\t\t\t\t\t\t  refnamelen - 1);\n\t\t\t\t\t}\n\t\t\t\t\thentry2 = (NameHashEntry *) hash_search(names_hash,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmodname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tHASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t&found);\n\t\t\t\t} while (found);\n\t\t\t\thentry2->counter = 0;\t/* init new hash entry */\n\t\t\t\trefname = modname;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* Name not previously used, need only initialize hentry */\n\t\t\t\thentry->counter = 0;\n\t\t\t}\n\t\t}\n\n\t\tdpns->rtable_names = lappend(dpns->rtable_names, refname);\n\t\trtindex++;\n\t}\n\n\thash_destroy(names_hash);\n}\n\n/*\n * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree\n *\n * For convenience, this is defined to initialize the deparse_namespace struct\n * from scratch.\n */\nstatic void\nset_deparse_for_query(deparse_namespace *dpns, Query *query,\n\t\t\t\t\t  List *parent_namespaces)\n{\n\tListCell   *lc;\n\tListCell   *lc2;\n\n\t/* Initialize *dpns and fill rtable/ctes links */\n\tmemset(dpns, 0, sizeof(deparse_namespace));\n\tdpns->rtable = query->rtable;\n\tdpns->subplans = NIL;\n\tdpns->ctes = query->cteList;\n\tdpns->appendrels = NULL;\n\tdpns->ret_old_alias = query->returningOldAlias;\n\tdpns->ret_new_alias = query->returningNewAlias;\n\n\t/* Assign a unique relation alias to each RTE */\n\tset_rtable_names(dpns, parent_namespaces, NULL);\n\n\t/* Initialize dpns->rtable_columns to contain zeroed structs */\n\tdpns->rtable_columns = NIL;\n\twhile (list_length(dpns->rtable_columns) < list_length(dpns->rtable))\n\t\tdpns->rtable_columns = lappend(dpns->rtable_columns,\n\t\t\t\t\t\t\t\t\t   palloc0(sizeof(deparse_columns)));\n\n\t/* If it's a utility query, it won't have a jointree */\n\tif (query->jointree)\n\t{\n\t\t/* Detect whether global uniqueness of USING names is needed */\n\t\tdpns->unique_using =\n\t\t\thas_dangerous_join_using(dpns, (Node *) query->jointree);\n\n\t\t/*\n\t\t * Select names for columns merged by USING, via a recursive pass over\n\t\t * the query jointree.\n\t\t */\n\t\tset_using_names(dpns, (Node *) query->jointree, NIL);\n\t}\n\n\t/*\n\t * Now assign remaining column aliases for each RTE.  We do this in a\n\t * linear scan of the rtable, so as to process RTEs whether or not they\n\t * are in the jointree (we mustn't miss NEW.*, INSERT target relations,\n\t * etc).  JOIN RTEs must be processed after their children, but this is\n\t * okay because they appear later in the rtable list than their children\n\t * (cf Asserts in identify_join_columns()).\n\t */\n\tforboth(lc, dpns->rtable, lc2, dpns->rtable_columns)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n\t\tdeparse_columns *colinfo = (deparse_columns *) lfirst(lc2);\n\n\t\tif (rte->rtekind == RTE_JOIN)\n\t\t\tset_join_column_names(dpns, rte, colinfo);\n\t\telse\n\t\t\tset_relation_column_names(dpns, rte, colinfo);\n\t}\n}\n\n/*\n * has_dangerous_join_using: search jointree for unnamed JOIN USING\n *\n * Merged columns of a JOIN USING may act differently from either of the input\n * columns, either because they are merged with COALESCE (in a FULL JOIN) or\n * because an implicit coercion of the underlying input column is required.\n * In such a case the column must be referenced as a column of the JOIN not as\n * a column of either input.  And this is problematic if the join is unnamed\n * (alias-less): we cannot qualify the column's name with an RTE name, since\n * there is none.  (Forcibly assigning an alias to the join is not a solution,\n * since that will prevent legal references to tables below the join.)\n * To ensure that every column in the query is unambiguously referenceable,\n * we must assign such merged columns names that are globally unique across\n * the whole query, aliasing other columns out of the way as necessary.\n *\n * Because the ensuing re-aliasing is fairly damaging to the readability of\n * the query, we don't do this unless we have to.  So, we must pre-scan\n * the join tree to see if we have to, before starting set_using_names().\n */\nstatic bool\nhas_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)\n{\n\tif (IsA(jtnode, RangeTblRef))\n\t{\n\t\t/* nothing to do here */\n\t}\n\telse if (IsA(jtnode, FromExpr))\n\t{\n\t\tFromExpr   *f = (FromExpr *) jtnode;\n\t\tListCell   *lc;\n\n\t\tforeach(lc, f->fromlist)\n\t\t{\n\t\t\tif (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))\n\t\t\t\treturn true;\n\t\t}\n\t}\n\telse if (IsA(jtnode, JoinExpr))\n\t{\n\t\tJoinExpr   *j = (JoinExpr *) jtnode;\n\n\t\t/* Is it an unnamed JOIN with USING? */\n\t\tif (j->alias == NULL && j->usingClause)\n\t\t{\n\t\t\t/*\n\t\t\t * Yes, so check each join alias var to see if any of them are not\n\t\t\t * simple references to underlying columns.  If so, we have a\n\t\t\t * dangerous situation and must pick unique aliases.\n\t\t\t */\n\t\t\tRangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);\n\n\t\t\t/* We need only examine the merged columns */\n\t\t\tfor (int i = 0; i < jrte->joinmergedcols; i++)\n\t\t\t{\n\t\t\t\tNode\t   *aliasvar = list_nth(jrte->joinaliasvars, i);\n\n\t\t\t\tif (!IsA(aliasvar, Var))\n\t\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t/* Nope, but inspect children */\n\t\tif (has_dangerous_join_using(dpns, j->larg))\n\t\t\treturn true;\n\t\tif (has_dangerous_join_using(dpns, j->rarg))\n\t\t\treturn true;\n\t}\n\telse\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(jtnode));\n\treturn false;\n}\n\n/*\n * set_using_names: select column aliases to be used for merged USING columns\n *\n * We do this during a recursive descent of the query jointree.\n * dpns->unique_using must already be set to determine the global strategy.\n *\n * Column alias info is saved in the dpns->rtable_columns list, which is\n * assumed to be filled with pre-zeroed deparse_columns structs.\n *\n * parentUsing is a list of all USING aliases assigned in parent joins of\n * the current jointree node.  (The passed-in list must not be modified.)\n *\n * Note that we do not use per-deparse_columns hash tables in this function.\n * The number of names that need to be assigned should be small enough that\n * we don't need to trouble with that.\n */\nstatic void\nset_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)\n{\n\tif (IsA(jtnode, RangeTblRef))\n\t{\n\t\t/* nothing to do now */\n\t}\n\telse if (IsA(jtnode, FromExpr))\n\t{\n\t\tFromExpr   *f = (FromExpr *) jtnode;\n\t\tListCell   *lc;\n\n\t\tforeach(lc, f->fromlist)\n\t\t\tset_using_names(dpns, (Node *) lfirst(lc), parentUsing);\n\t}\n\telse if (IsA(jtnode, JoinExpr))\n\t{\n\t\tJoinExpr   *j = (JoinExpr *) jtnode;\n\t\tRangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);\n\t\tdeparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);\n\t\tint\t\t   *leftattnos;\n\t\tint\t\t   *rightattnos;\n\t\tdeparse_columns *leftcolinfo;\n\t\tdeparse_columns *rightcolinfo;\n\t\tint\t\t\ti;\n\t\tListCell   *lc;\n\n\t\t/* Get info about the shape of the join */\n\t\tidentify_join_columns(j, rte, colinfo);\n\t\tleftattnos = colinfo->leftattnos;\n\t\trightattnos = colinfo->rightattnos;\n\n\t\t/* Look up the not-yet-filled-in child deparse_columns structs */\n\t\tleftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);\n\t\trightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);\n\n\t\t/*\n\t\t * If this join is unnamed, then we cannot substitute new aliases at\n\t\t * this level, so any name requirements pushed down to here must be\n\t\t * pushed down again to the children.\n\t\t */\n\t\tif (rte->alias == NULL)\n\t\t{\n\t\t\tfor (i = 0; i < colinfo->num_cols; i++)\n\t\t\t{\n\t\t\t\tchar\t   *colname = colinfo->colnames[i];\n\n\t\t\t\tif (colname == NULL)\n\t\t\t\t\tcontinue;\n\n\t\t\t\t/* Push down to left column, unless it's a system column */\n\t\t\t\tif (leftattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(leftcolinfo, leftattnos[i]);\n\t\t\t\t\tleftcolinfo->colnames[leftattnos[i] - 1] = colname;\n\t\t\t\t}\n\n\t\t\t\t/* Same on the righthand side */\n\t\t\t\tif (rightattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(rightcolinfo, rightattnos[i]);\n\t\t\t\t\trightcolinfo->colnames[rightattnos[i] - 1] = colname;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * If there's a USING clause, select the USING column names and push\n\t\t * those names down to the children.  We have two strategies:\n\t\t *\n\t\t * If dpns->unique_using is true, we force all USING names to be\n\t\t * unique across the whole query level.  In principle we'd only need\n\t\t * the names of dangerous USING columns to be globally unique, but to\n\t\t * safely assign all USING names in a single pass, we have to enforce\n\t\t * the same uniqueness rule for all of them.  However, if a USING\n\t\t * column's name has been pushed down from the parent, we should use\n\t\t * it as-is rather than making a uniqueness adjustment.  This is\n\t\t * necessary when we're at an unnamed join, and it creates no risk of\n\t\t * ambiguity.  Also, if there's a user-written output alias for a\n\t\t * merged column, we prefer to use that rather than the input name;\n\t\t * this simplifies the logic and seems likely to lead to less aliasing\n\t\t * overall.\n\t\t *\n\t\t * If dpns->unique_using is false, we only need USING names to be\n\t\t * unique within their own join RTE.  We still need to honor\n\t\t * pushed-down names, though.\n\t\t *\n\t\t * Though significantly different in results, these two strategies are\n\t\t * implemented by the same code, with only the difference of whether\n\t\t * to put assigned names into dpns->using_names.\n\t\t */\n\t\tif (j->usingClause)\n\t\t{\n\t\t\t/* Copy the input parentUsing list so we don't modify it */\n\t\t\tparentUsing = list_copy(parentUsing);\n\n\t\t\t/* USING names must correspond to the first join output columns */\n\t\t\texpand_colnames_array_to(colinfo, list_length(j->usingClause));\n\t\t\ti = 0;\n\t\t\tforeach(lc, j->usingClause)\n\t\t\t{\n\t\t\t\tchar\t   *colname = strVal(lfirst(lc));\n\n\t\t\t\t/* Assert it's a merged column */\n\t\t\t\tAssert(leftattnos[i] != 0 && rightattnos[i] != 0);\n\n\t\t\t\t/* Adopt passed-down name if any, else select unique name */\n\t\t\t\tif (colinfo->colnames[i] != NULL)\n\t\t\t\t\tcolname = colinfo->colnames[i];\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* Prefer user-written output alias if any */\n\t\t\t\t\tif (rte->alias && i < list_length(rte->alias->colnames))\n\t\t\t\t\t\tcolname = strVal(list_nth(rte->alias->colnames, i));\n\t\t\t\t\t/* Make it appropriately unique */\n\t\t\t\t\tcolname = make_colname_unique(colname, dpns, colinfo);\n\t\t\t\t\tif (dpns->unique_using)\n\t\t\t\t\t\tdpns->using_names = lappend(dpns->using_names,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcolname);\n\t\t\t\t\t/* Save it as output column name, too */\n\t\t\t\t\tcolinfo->colnames[i] = colname;\n\t\t\t\t}\n\n\t\t\t\t/* Remember selected names for use later */\n\t\t\t\tcolinfo->usingNames = lappend(colinfo->usingNames, colname);\n\t\t\t\tparentUsing = lappend(parentUsing, colname);\n\n\t\t\t\t/* Push down to left column, unless it's a system column */\n\t\t\t\tif (leftattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(leftcolinfo, leftattnos[i]);\n\t\t\t\t\tleftcolinfo->colnames[leftattnos[i] - 1] = colname;\n\t\t\t\t}\n\n\t\t\t\t/* Same on the righthand side */\n\t\t\t\tif (rightattnos[i] > 0)\n\t\t\t\t{\n\t\t\t\t\texpand_colnames_array_to(rightcolinfo, rightattnos[i]);\n\t\t\t\t\trightcolinfo->colnames[rightattnos[i] - 1] = colname;\n\t\t\t\t}\n\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\n\t\t/* Mark child deparse_columns structs with correct parentUsing info */\n\t\tleftcolinfo->parentUsing = parentUsing;\n\t\trightcolinfo->parentUsing = parentUsing;\n\n\t\t/* Now recursively assign USING column names in children */\n\t\tset_using_names(dpns, j->larg, parentUsing);\n\t\tset_using_names(dpns, j->rarg, parentUsing);\n\t}\n\telse\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(jtnode));\n}\n\n/*\n * set_relation_column_names: select column aliases for a non-join RTE\n *\n * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.\n * If any colnames entries are already filled in, those override local\n * choices.\n */\nstatic void\nset_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,\n\t\t\t\t\t\t  deparse_columns *colinfo)\n{\n\tint\t\t\tncolumns;\n\tchar\t  **real_colnames;\n\tbool\t\tchanged_any;\n\tbool \t\thas_anonymous;\n\tint\t\t\tnoldcolumns;\n\tint\t\t\ti;\n\tint\t\t\tj;\n\n\t/*\n\t * Construct an array of the current \"real\" column names of the RTE.\n\t * real_colnames[] will be indexed by physical column number, with NULL\n\t * entries for dropped columns.\n\t */\n\tif ((rte->rtekind == RTE_RELATION ||\n\t\tGetRangeTblKind(rte) == CITUS_RTE_SHARD) &&\n\t\tOidIsValid(rte->relid))\n\t{\n\t\t/* Relation --- look to the system catalogs for up-to-date info */\n\t\tRelation\trel;\n\t\tTupleDesc\ttupdesc;\n\n\t\trel = relation_open(rte->relid, AccessShareLock);\n\t\ttupdesc = RelationGetDescr(rel);\n\n\t\tncolumns = tupdesc->natts;\n\t\treal_colnames = (char **) palloc(ncolumns * sizeof(char *));\n\n\t\tfor (i = 0; i < ncolumns; i++)\n\t\t{\n\t\t\tForm_pg_attribute attr = TupleDescAttr(tupdesc, i);\n\n\t\t\tif (attr->attisdropped)\n\t\t\t\treal_colnames[i] = NULL;\n\t\t\telse\n\t\t\t\treal_colnames[i] = pstrdup(NameStr(attr->attname));\n\t\t}\n\t\trelation_close(rel, AccessShareLock);\n\t}\n\telse if (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\t/* shard RTE without relid (pulled-up clone in PG18)            */\n\t\t/* use the column aliases already stored in rte->eref->colnames */\n\n\t\tncolumns      = list_length(rte->eref->colnames);\n\t\treal_colnames = (char **) palloc0(ncolumns * sizeof(char *));\n\n\t\tfor (i = 0; i < ncolumns; i++)\n\t\t\treal_colnames[i] = pstrdup(strVal(list_nth(rte->eref->colnames, i)));\n\n\t\t/* keep changed_any / has_anonymous defaults */\n\t}\n    else if (rte->rtekind == RTE_GROUP)\n    {\n        /* ----- synthetic PG 18 RTE for GROUP BY / HAVING ----- */\n        ncolumns      = list_length(rte->eref->colnames);\n        real_colnames = (char **) palloc0(ncolumns * sizeof(char *));\n\n        for (i = 0; i < ncolumns; i++)\n            real_colnames[i] = pstrdup(strVal(list_nth(rte->eref->colnames, i)));\n    }\n\telse\n\t{\n\t\t/* Otherwise get the column names from eref or expandRTE() */\n\t\tList\t   *colnames;\n\t\tListCell   *lc;\n\n\t\t/*\n\t\t * Functions returning composites have the annoying property that some\n\t\t * of the composite type's columns might have been dropped since the\n\t\t * query was parsed.  If possible, use expandRTE() to handle that\n\t\t * case, since it has the tedious logic needed to find out about\n\t\t * dropped columns.  However, if we're explaining a plan, then we\n\t\t * don't have rte->functions because the planner thinks that won't be\n\t\t * needed later, and that breaks expandRTE().  So in that case we have\n\t\t * to rely on rte->eref, which may lead us to report a dropped\n\t\t * column's old name; that seems close enough for EXPLAIN's purposes.\n\t\t *\n\t\t * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,\n\t\t * which should be sufficiently up-to-date: no other RTE types can\n\t\t * have columns get dropped from under them after parsing.\n\t\t */\n\t\tif (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)\n\t\t{\n\t\t\t/* Since we're not creating Vars, rtindex etc. don't matter */\n\t\t\texpandRTE(rte, 1, 0, VAR_RETURNING_DEFAULT, -1, true /* include dropped */ ,\n\t\t\t\t\t  &colnames, NULL);\n\t\t}\n\t\telse\n\t\t\tcolnames = rte->eref->colnames;\n\n\t\tncolumns = list_length(colnames);\n\t\treal_colnames = (char **) palloc(ncolumns * sizeof(char *));\n\n\t\ti = 0;\n\t\tforeach(lc, colnames)\n\t\t{\n\t\t\t/*\n\t\t\t * If the column name we find here is an empty string, then it's a\n\t\t\t * dropped column, so change to NULL.\n\t\t\t */\n\t\t\tchar\t   *cname = strVal(lfirst(lc));\n\n\t\t\tif (cname[0] == '\\0')\n\t\t\t\tcname = NULL;\n\t\t\treal_colnames[i] = cname;\n\t\t\ti++;\n\t\t}\n\t}\n\n\t/*\n\t * Ensure colinfo->colnames has a slot for each column.  (It could be long\n\t * enough already, if we pushed down a name for the last column.)  Note:\n\t * it's possible that there are now more columns than there were when the\n\t * query was parsed, ie colnames could be longer than rte->eref->colnames.\n\t * We must assign unique aliases to the new columns too, else there could\n\t * be unresolved conflicts when the view/rule is reloaded.\n\t */\n\texpand_colnames_array_to(colinfo, ncolumns);\n\tAssert(colinfo->num_cols == ncolumns);\n\n\t/*\n\t * Make sufficiently large new_colnames and is_new_col arrays, too.\n\t *\n\t * Note: because we leave colinfo->num_new_cols zero until after the loop,\n\t * colname_is_unique will not consult that array, which is fine because it\n\t * would only be duplicate effort.\n\t */\n\tcolinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));\n\tcolinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));\n\n\t/* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */\n\tbuild_colinfo_names_hash(colinfo);\n\n\t/*\n\t * Scan the columns, select a unique alias for each one, and store it in\n\t * colinfo->colnames and colinfo->new_colnames.  The former array has NULL\n\t * entries for dropped columns, the latter omits them.  Also mark\n\t * new_colnames entries as to whether they are new since parse time; this\n\t * is the case for entries beyond the length of rte->eref->colnames.\n\t */\n\tnoldcolumns = list_length(rte->eref->colnames);\n\tchanged_any = false;\n\thas_anonymous = false;\n\tj = 0;\n\tfor (i = 0; i < ncolumns; i++)\n\t{\n\t\tchar\t   *real_colname = real_colnames[i];\n\t\tchar\t   *colname = colinfo->colnames[i];\n\n\t\t/* Skip dropped columns */\n\t\tif (real_colname == NULL)\n\t\t{\n\t\t\tAssert(colname == NULL);\t/* colnames[i] is already NULL */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* If alias already assigned, that's what to use */\n\t\tif (colname == NULL)\n\t\t{\n\t\t\t/* If user wrote an alias, prefer that over real column name */\n\t\t\tif (rte->alias && i < list_length(rte->alias->colnames))\n\t\t\t\tcolname = strVal(list_nth(rte->alias->colnames, i));\n\t\t\telse\n\t\t\t\tcolname = real_colname;\n\n\t\t\t/* Unique-ify and insert into colinfo */\n\t\t\tcolname = make_colname_unique(colname, dpns, colinfo);\n\n\t\t\tcolinfo->colnames[i] = colname;\n\t\t\tadd_to_names_hash(colinfo, colname);\n\t\t}\n\n\t\t/* Put names of non-dropped columns in new_colnames[] too */\n\t\tcolinfo->new_colnames[j] = colname;\n\t\t/* And mark them as new or not */\n\t\tcolinfo->is_new_col[j] = (i >= noldcolumns);\n\t\tj++;\n\n\t\t/* Remember if any assigned aliases differ from \"real\" name */\n\t\tif (!changed_any && strcmp(colname, real_colname) != 0)\n\t\t\tchanged_any = true;\n\n\t\t/*\n\t\t * Remember if there is a reference to an anonymous column as named by\n\t\t * char * FigureColname(Node *node)\n\t\t */\n\t\tif (!has_anonymous && strcmp(real_colname, \"?column?\") == 0)\n\t\t\thas_anonymous = true;\n\t}\n\n\t/* We're now done needing the colinfo's names_hash */\n\tdestroy_colinfo_names_hash(colinfo);\n\n\t/*\n\t * Set correct length for new_colnames[] array.  (Note: if columns have\n\t * been added, colinfo->num_cols includes them, which is not really quite\n\t * right but is harmless, since any new columns must be at the end where\n\t * they won't affect varattnos of pre-existing columns.)\n\t */\n\tcolinfo->num_new_cols = j;\n\n\t/*\n\t * For a relation RTE, we need only print the alias column names if any\n\t * are different from the underlying \"real\" names.  For a function RTE,\n\t * always emit a complete column alias list; this is to protect against\n\t * possible instability of the default column names (eg, from altering\n\t * parameter names).  For tablefunc RTEs, we never print aliases, because\n\t * the column names are part of the clause itself.  For other RTE types,\n\t * print if we changed anything OR if there were user-written column\n\t * aliases (since the latter would be part of the underlying \"reality\").\n\t */\n\tif (rte->rtekind == RTE_RELATION)\n\t\tcolinfo->printaliases = changed_any;\n\telse if (rte->rtekind == RTE_FUNCTION)\n\t\tcolinfo->printaliases = true;\n\telse if (rte->rtekind == RTE_TABLEFUNC)\n\t\tcolinfo->printaliases = false;\n\telse if (rte->alias && rte->alias->colnames != NIL)\n\t\tcolinfo->printaliases = true;\n\telse\n\t\tcolinfo->printaliases = changed_any || has_anonymous;\n}\n\n/*\n * set_join_column_names: select column aliases for a join RTE\n *\n * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.\n * If any colnames entries are already filled in, those override local\n * choices.  Also, names for USING columns were already chosen by\n * set_using_names().  We further expect that column alias selection has been\n * completed for both input RTEs.\n */\nstatic void\nset_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,\n\t\t\t\t\t  deparse_columns *colinfo)\n{\n\tdeparse_columns *leftcolinfo;\n\tdeparse_columns *rightcolinfo;\n\tbool\t\tchanged_any;\n\tint\t\t\tnoldcolumns;\n\tint\t\t\tnnewcolumns;\n\tBitmapset  *leftmerged = NULL;\n\tBitmapset  *rightmerged = NULL;\n\tint\t\t\ti;\n\tint\t\t\tj;\n\tint\t\t\tic;\n\tint\t\t\tjc;\n\n\t/* Look up the previously-filled-in child deparse_columns structs */\n\tleftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);\n\trightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);\n\n\t/*\n\t * Ensure colinfo->colnames has a slot for each column.  (It could be long\n\t * enough already, if we pushed down a name for the last column.)  Note:\n\t * it's possible that one or both inputs now have more columns than there\n\t * were when the query was parsed, but we'll deal with that below.  We\n\t * only need entries in colnames for pre-existing columns.\n\t */\n\tnoldcolumns = list_length(rte->eref->colnames);\n\texpand_colnames_array_to(colinfo, noldcolumns);\n\tAssert(colinfo->num_cols == noldcolumns);\n\n\t/* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */\n\tbuild_colinfo_names_hash(colinfo);\n\n\t/*\n\t * Scan the join output columns, select an alias for each one, and store\n\t * it in colinfo->colnames.  If there are USING columns, set_using_names()\n\t * already selected their names, so we can start the loop at the first\n\t * non-merged column.\n\t */\n\tchanged_any = false;\n\tfor (i = list_length(colinfo->usingNames); i < noldcolumns; i++)\n\t{\n\t\tchar\t   *colname = colinfo->colnames[i];\n\t\tchar\t   *real_colname;\n\n\t\t/* Join column must refer to at least one input column */\n\t\tAssert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);\n\n\t\t/* Get the child column name */\n\t\tif (colinfo->leftattnos[i] > 0)\n\t\t\treal_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];\n\t\telse if (colinfo->rightattnos[i] > 0)\n\t\t\treal_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];\n\t\telse\n\t\t{\n\t\t\t/* We're joining system columns --- use eref name */\n\t\t\treal_colname = strVal(list_nth(rte->eref->colnames, i));\n\t\t}\n\t\t\t/* If child col has been dropped, no need to assign a join colname */\n\t\tif (real_colname == NULL)\n\t\t{\n\t\t\tcolinfo->colnames[i] = NULL;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* In an unnamed join, just report child column names as-is */\n\t\tif (rte->alias == NULL)\n\t\t{\n\t\t\tcolinfo->colnames[i] = real_colname;\n\t\t\tadd_to_names_hash(colinfo, real_colname);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* If alias already assigned, that's what to use */\n\t\tif (colname == NULL)\n\t\t{\n\t\t\t/* If user wrote an alias, prefer that over real column name */\n\t\t\tif (rte->alias && i < list_length(rte->alias->colnames))\n\t\t\t\tcolname = strVal(list_nth(rte->alias->colnames, i));\n\t\t\telse\n\t\t\t\tcolname = real_colname;\n\n\t\t\t/* Unique-ify and insert into colinfo */\n\t\t\tcolname = make_colname_unique(colname, dpns, colinfo);\n\n\t\t\tcolinfo->colnames[i] = colname;\n\t\t\tadd_to_names_hash(colinfo, colname);\n\t\t}\n\n\t\t/* Remember if any assigned aliases differ from \"real\" name */\n\t\tif (!changed_any && strcmp(colname, real_colname) != 0)\n\t\t\tchanged_any = true;\n\t}\n\n\t/*\n\t * Calculate number of columns the join would have if it were re-parsed\n\t * now, and create storage for the new_colnames and is_new_col arrays.\n\t *\n\t * Note: colname_is_unique will be consulting new_colnames[] during the\n\t * loops below, so its not-yet-filled entries must be zeroes.\n\t */\n\tnnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -\n\t\tlist_length(colinfo->usingNames);\n\tcolinfo->num_new_cols = nnewcolumns;\n\tcolinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));\n\tcolinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));\n\n\t/*\n\t * Generating the new_colnames array is a bit tricky since any new columns\n\t * added since parse time must be inserted in the right places.  This code\n\t * must match the parser, which will order a join's columns as merged\n\t * columns first (in USING-clause order), then non-merged columns from the\n\t * left input (in attnum order), then non-merged columns from the right\n\t * input (ditto).  If one of the inputs is itself a join, its columns will\n\t * be ordered according to the same rule, which means newly-added columns\n\t * might not be at the end.  We can figure out what's what by consulting\n\t * the leftattnos and rightattnos arrays plus the input is_new_col arrays.\n\t *\n\t * In these loops, i indexes leftattnos/rightattnos (so it's join varattno\n\t * less one), j indexes new_colnames/is_new_col, and ic/jc have similar\n\t * meanings for the current child RTE.\n\t */\n\n\t/* Handle merged columns; they are first and can't be new */\n\ti = j = 0;\n\twhile (i < noldcolumns &&\n\t\t   colinfo->leftattnos[i] != 0 &&\n\t\t   colinfo->rightattnos[i] != 0)\n\t{\n\t\t/* column name is already determined and known unique */\n\t\tcolinfo->new_colnames[j] = colinfo->colnames[i];\n\t\tcolinfo->is_new_col[j] = false;\n\n\t\t/* build bitmapsets of child attnums of merged columns */\n\t\tif (colinfo->leftattnos[i] > 0)\n\t\t\tleftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);\n\t\tif (colinfo->rightattnos[i] > 0)\n\t\t\trightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);\n\n\t\ti++, j++;\n\t}\n\n\t/* Handle non-merged left-child columns */\n\tic = 0;\n\tfor (jc = 0; jc < leftcolinfo->num_new_cols; jc++)\n\t{\n\t\tchar\t   *child_colname = leftcolinfo->new_colnames[jc];\n\n\t\tif (!leftcolinfo->is_new_col[jc])\n\t\t{\n\t\t\t/* Advance ic to next non-dropped old column of left child */\n\t\t\twhile (ic < leftcolinfo->num_cols &&\n\t\t\t\t   leftcolinfo->colnames[ic] == NULL)\n\t\t\t\tic++;\n\t\t\tAssert(ic < leftcolinfo->num_cols);\n\t\t\tic++;\n\t\t\t/* If it is a merged column, we already processed it */\n\t\t\tif (bms_is_member(ic, leftmerged))\n\t\t\t\tcontinue;\n\t\t\t/* Else, advance i to the corresponding existing join column */\n\t\t\twhile (i < colinfo->num_cols &&\n\t\t\t\t   colinfo->colnames[i] == NULL)\n\t\t\t\ti++;\n\t\t\tAssert(i < colinfo->num_cols);\n\t\t\tAssert(ic == colinfo->leftattnos[i]);\n\t\t\t/* Use the already-assigned name of this column */\n\t\t\tcolinfo->new_colnames[j] = colinfo->colnames[i];\n\t\t\ti++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Unique-ify the new child column name and assign, unless we're\n\t\t\t * in an unnamed join, in which case just copy\n\t\t\t */\n\t\t\tif (rte->alias != NULL)\n\t\t\t{\n\t\t\t\tcolinfo->new_colnames[j] =\n\t\t\t\t\tmake_colname_unique(child_colname, dpns, colinfo);\n\t\t\t\tif (!changed_any &&\n\t\t\t\t\tstrcmp(colinfo->new_colnames[j], child_colname) != 0)\n\t\t\t\t\tchanged_any = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcolinfo->new_colnames[j] = child_colname;\n\t\t\tadd_to_names_hash(colinfo, colinfo->new_colnames[j]);\n\t\t}\n\n\t\tcolinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];\n\t\tj++;\n\t}\n\n\t/* Handle non-merged right-child columns in exactly the same way */\n\tic = 0;\n\tfor (jc = 0; jc < rightcolinfo->num_new_cols; jc++)\n\t{\n\t\tchar\t   *child_colname = rightcolinfo->new_colnames[jc];\n\n\t\tif (!rightcolinfo->is_new_col[jc])\n\t\t{\n\t\t\t/* Advance ic to next non-dropped old column of right child */\n\t\t\twhile (ic < rightcolinfo->num_cols &&\n\t\t\t\t   rightcolinfo->colnames[ic] == NULL)\n\t\t\t\tic++;\n\t\t\tAssert(ic < rightcolinfo->num_cols);\n\t\t\tic++;\n\t\t\t/* If it is a merged column, we already processed it */\n\t\t\tif (bms_is_member(ic, rightmerged))\n\t\t\t\tcontinue;\n\t\t\t/* Else, advance i to the corresponding existing join column */\n\t\t\twhile (i < colinfo->num_cols &&\n\t\t\t\t   colinfo->colnames[i] == NULL)\n\t\t\t\ti++;\n\t\t\tAssert(i < colinfo->num_cols);\n\t\t\tAssert(ic == colinfo->rightattnos[i]);\n\t\t\t/* Use the already-assigned name of this column */\n\t\t\tcolinfo->new_colnames[j] = colinfo->colnames[i];\n\t\t\ti++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Unique-ify the new child column name and assign, unless we're\n\t\t\t * in an unnamed join, in which case just copy\n\t\t\t */\n\t\t\tif (rte->alias != NULL)\n\t\t\t{\n\t\t\t\tcolinfo->new_colnames[j] =\n\t\t\t\t\tmake_colname_unique(child_colname, dpns, colinfo);\n\t\t\t\tif (!changed_any &&\n\t\t\t\t\tstrcmp(colinfo->new_colnames[j], child_colname) != 0)\n\t\t\t\t\tchanged_any = true;\n\t\t\t}\n\t\t\telse\n\t\t\t\tcolinfo->new_colnames[j] = child_colname;\n\t\t\tadd_to_names_hash(colinfo, colinfo->new_colnames[j]);\n\t\t}\n\n\t\tcolinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];\n\t\tj++;\n\t}\n\n\t/* Assert we processed the right number of columns */\n#ifdef USE_ASSERT_CHECKING\n\tfor (int col_index = 0; col_index < colinfo->num_cols; col_index++)\n\t{\n\t\t/*\n\t\t * In the above processing-loops, \"i\" advances only if\n\t\t * the column is not new, check if this is a new column.\n\t\t */\n\t\tif (colinfo->is_new_col[col_index])\n\t\t\ti++;\n\t}\n\tAssert(j == nnewcolumns);\n#endif\n\n\t/* We're now done needing the colinfo's names_hash */\n\tdestroy_colinfo_names_hash(colinfo);\n\n\t/*\n\t * For a named join, print column aliases if we changed any from the child\n\t * names.  Unnamed joins cannot print aliases.\n\t */\n\tif (rte->alias != NULL)\n\t\tcolinfo->printaliases = changed_any;\n\telse\n\t\tcolinfo->printaliases = false;\n}\n\n/*\n * colname_is_unique: is colname distinct from already-chosen column names?\n *\n * dpns is query-wide info, colinfo is for the column's RTE\n */\nstatic bool\ncolname_is_unique(const char *colname, deparse_namespace *dpns,\n\t\t\t\t  deparse_columns *colinfo)\n{\n\tint\t\t\ti;\n\tListCell   *lc;\n\n\t/*\n\t * If we have a hash table, consult that instead of linearly scanning the\n\t * colinfo's strings.\n\t */\n\tif (colinfo->names_hash)\n\t{\n\t\tif (hash_search(colinfo->names_hash,\n\t\t\t\t\t\tcolname,\n\t\t\t\t\t\tHASH_FIND,\n\t\t\t\t\t\tNULL) != NULL)\n\t\t\treturn false;\n\t}\n\telse\n\t{\n\t\t/* Check against already-assigned column aliases within RTE */\n\t\tfor (i = 0; i < colinfo->num_cols; i++)\n\t\t{\n\t\t\tchar\t   *oldname = colinfo->colnames[i];\n\n\t\t\tif (oldname && strcmp(oldname, colname) == 0)\n\t\t\t\treturn false;\n\t\t}\n\n\t\t/*\n\t\t * If we're building a new_colnames array, check that too (this will\n\t\t * be partially but not completely redundant with the previous checks)\n\t\t */\n\t\tfor (i = 0; i < colinfo->num_new_cols; i++)\n\t\t{\n\t\t\tchar\t   *oldname = colinfo->new_colnames[i];\n\n\t\t\tif (oldname && strcmp(oldname, colname) == 0)\n\t\t\t\treturn false;\n\t\t}\n\n\t\t/*\n\t\t * Also check against names already assigned for parent-join USING\n\t\t * cols\n\t\t */\n\t\tforeach(lc, colinfo->parentUsing)\n\t\t{\n\t\t\tchar\t   *oldname = (char *) lfirst(lc);\n\n\t\t\tif (strcmp(oldname, colname) == 0)\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\t/*\n\t * Also check against USING-column names that must be globally unique.\n\t * These are not hashed, but there should be few of them.\n\t */\n\tforeach(lc, dpns->using_names)\n\t{\n\t\tchar\t   *oldname = (char *) lfirst(lc);\n\n\t\tif (strcmp(oldname, colname) == 0)\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n/*\n * make_colname_unique: modify colname if necessary to make it unique\n *\n * dpns is query-wide info, colinfo is for the column's RTE\n */\nstatic char *\nmake_colname_unique(char *colname, deparse_namespace *dpns,\n\t\t\t\t\tdeparse_columns *colinfo)\n{\n\t/*\n\t * If the selected name isn't unique, append digits to make it so.  For a\n\t * very long input name, we might have to truncate to stay within\n\t * NAMEDATALEN.\n\t */\n\tif (!colname_is_unique(colname, dpns, colinfo))\n\t{\n\t\tint\t\t\tcolnamelen = strlen(colname);\n\t\tchar\t   *modname = (char *) palloc(colnamelen + 16);\n\t\tint\t\t\ti = 0;\n\n\t\tdo\n\t\t{\n\t\t\ti++;\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\tmemcpy(modname, colname, colnamelen);\n\t\t\t\tsprintf(modname + colnamelen, \"_%d\", i);\n\t\t\t\tif (strlen(modname) < NAMEDATALEN)\n\t\t\t\t\tbreak;\n\t\t\t\t/* drop chars from colname to keep all the digits */\n\t\t\t\tcolnamelen = pg_mbcliplen(colname, colnamelen,\n\t\t\t\t\t\t\t\t\t\t  colnamelen - 1);\n\t\t\t}\n\t\t} while (!colname_is_unique(modname, dpns, colinfo));\n\t\tcolname = modname;\n\t}\n\treturn colname;\n}\n\n/*\n * expand_colnames_array_to: make colinfo->colnames at least n items long\n *\n * Any added array entries are initialized to zero.\n */\nstatic void\nexpand_colnames_array_to(deparse_columns *colinfo, int n)\n{\n\tif (n > colinfo->num_cols)\n\t{\n\t\tif (colinfo->colnames == NULL)\n\t\t\tcolinfo->colnames = palloc0_array(char *, n);\n\t\telse\n\t\t{\n\t\t\tcolinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);\n\t\t}\n\t\tcolinfo->num_cols = n;\n\t}\n}\n\n/*\n * build_colinfo_names_hash: optionally construct a hash table for colinfo\n */\nstatic void\nbuild_colinfo_names_hash(deparse_columns *colinfo)\n{\n\tHASHCTL\t\thash_ctl;\n\tint\t\t\ti;\n\tListCell   *lc;\n\n\t/*\n\t * Use a hash table only for RTEs with at least 32 columns.  (The cutoff\n\t * is somewhat arbitrary, but let's choose it so that this code does get\n\t * exercised in the regression tests.)\n\t */\n\tif (colinfo->num_cols < 32)\n\t\treturn;\n\n\t/*\n\t * Set up the hash table.  The entries are just strings with no other\n\t * payload.\n\t */\n\thash_ctl.keysize = NAMEDATALEN;\n\thash_ctl.entrysize = NAMEDATALEN;\n\thash_ctl.hcxt = CurrentMemoryContext;\n\tcolinfo->names_hash = hash_create(\"deparse_columns names\",\n\t\t\t\t\t\t\t\t\t  colinfo->num_cols + colinfo->num_new_cols,\n\t\t\t\t\t\t\t\t\t  &hash_ctl,\n\t\t\t\t\t\t\t\t\t  HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);\n\n\t/*\n\t * Preload the hash table with any names already present (these would have\n\t * come from set_using_names).\n\t */\n\tfor (i = 0; i < colinfo->num_cols; i++)\n\t{\n\t\tchar\t   *oldname = colinfo->colnames[i];\n\n\t\tif (oldname)\n\t\t\tadd_to_names_hash(colinfo, oldname);\n\t}\n\n\tfor (i = 0; i < colinfo->num_new_cols; i++)\n\t{\n\t\tchar\t   *oldname = colinfo->new_colnames[i];\n\n\t\tif (oldname)\n\t\t\tadd_to_names_hash(colinfo, oldname);\n\t}\n\n\tforeach(lc, colinfo->parentUsing)\n\t{\n\t\tchar\t   *oldname = (char *) lfirst(lc);\n\n\t\tadd_to_names_hash(colinfo, oldname);\n\t}\n}\n\n/*\n * add_to_names_hash: add a string to the names_hash, if we're using one\n */\nstatic void\nadd_to_names_hash(deparse_columns *colinfo, const char *name)\n{\n\tif (colinfo->names_hash)\n\t\t(void) hash_search(colinfo->names_hash,\n\t\t\t\t\t\t   name,\n\t\t\t\t\t\t   HASH_ENTER,\n\t\t\t\t\t\t   NULL);\n}\n\n/*\n * destroy_colinfo_names_hash: destroy hash table when done with it\n */\nstatic void\ndestroy_colinfo_names_hash(deparse_columns *colinfo)\n{\n\tif (colinfo->names_hash)\n\t{\n\t\thash_destroy(colinfo->names_hash);\n\t\tcolinfo->names_hash = NULL;\n\t}\n}\n\n/*\n * identify_join_columns: figure out where columns of a join come from\n *\n * Fills the join-specific fields of the colinfo struct, except for\n * usingNames which is filled later.\n */\nstatic void\nidentify_join_columns(JoinExpr *j, RangeTblEntry *jrte,\n\t\t\t\t\t  deparse_columns *colinfo)\n{\n\tint\t\t\tnumjoincols;\n\tint\t\t\tjcolno;\n\tint\t\t\trcolno;\n\tListCell   *lc;\n\n\t/* Extract left/right child RT indexes */\n\tif (IsA(j->larg, RangeTblRef))\n\t\tcolinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;\n\telse if (IsA(j->larg, JoinExpr))\n\t\tcolinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;\n\telse\n\t\telog(ERROR, \"unrecognized node type in jointree: %d\",\n\t\t\t (int) nodeTag(j->larg));\n\tif (IsA(j->rarg, RangeTblRef))\n\t\tcolinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;\n\telse if (IsA(j->rarg, JoinExpr))\n\t\tcolinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;\n\telse\n\t\telog(ERROR, \"unrecognized node type in jointree: %d\",\n\t\t\t (int) nodeTag(j->rarg));\n\n\t/* Assert children will be processed earlier than join in second pass */\n\tAssert(colinfo->leftrti < j->rtindex);\n\tAssert(colinfo->rightrti < j->rtindex);\n\n\t/* Initialize result arrays with zeroes */\n\tnumjoincols = list_length(jrte->joinaliasvars);\n\tAssert(numjoincols == list_length(jrte->eref->colnames));\n\tcolinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));\n\tcolinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));\n\n\t/*\n\t * Deconstruct RTE's joinleftcols/joinrightcols into desired format.\n\t * Recall that the column(s) merged due to USING are the first column(s)\n\t * of the join output.  We need not do anything special while scanning\n\t * joinleftcols, but while scanning joinrightcols we must distinguish\n\t * merged from unmerged columns.\n\t */\n\tjcolno = 0;\n\tforeach(lc, jrte->joinleftcols)\n\t{\n\t\tint\t\t\tleftattno = lfirst_int(lc);\n\n\t\tcolinfo->leftattnos[jcolno++] = leftattno;\n\t}\n\trcolno = 0;\n\tforeach(lc, jrte->joinrightcols)\n\t{\n\t\tint\t\t\trightattno = lfirst_int(lc);\n\n\t\tif (rcolno < jrte->joinmergedcols)\t/* merged column? */\n\t\t\tcolinfo->rightattnos[rcolno] = rightattno;\n\t\telse\n\t\t\tcolinfo->rightattnos[jcolno++] = rightattno;\n\t\trcolno++;\n\t}\n\tAssert(jcolno == numjoincols);\n}\n\n/*\n * get_rtable_name: convenience function to get a previously assigned RTE alias\n *\n * The RTE must belong to the topmost namespace level in \"context\".\n */\nstatic char *\nget_rtable_name(int rtindex, deparse_context *context)\n{\n\tdeparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);\n\n\tAssert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));\n\treturn (char *) list_nth(dpns->rtable_names, rtindex - 1);\n}\n\n/*\n * set_deparse_plan: set up deparse_namespace to parse subexpressions\n * of a given Plan node\n *\n * This sets the plan, outer_planstate, inner_planstate, outer_tlist,\n * inner_tlist, and index_tlist fields.  Caller is responsible for adjusting\n * the ancestors list if necessary.  Note that the rtable and ctes fields do\n * not need to change when shifting attention to different plan nodes in a\n * single plan tree.\n */\nstatic void\nset_deparse_plan(deparse_namespace *dpns, Plan *plan)\n{\n\tdpns->plan = plan;\n\n\t/*\n\t * We special-case Append and MergeAppend to pretend that the first child\n\t * plan is the OUTER referent; we have to interpret OUTER Vars in their\n\t * tlists according to one of the children, and the first one is the most\n\t * natural choice.\n\t */\n\tif (IsA(plan, Append))\n\t\tdpns->outer_plan = linitial(((Append *) plan)->appendplans);\n\telse if (IsA(plan, MergeAppend))\n\t\tdpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);\n\telse\n\t\tdpns->outer_plan = outerPlan(plan);\n\n\tif (dpns->outer_plan)\n\t\tdpns->outer_tlist = dpns->outer_plan->targetlist;\n\telse\n\t\tdpns->outer_tlist = NIL;\n\n\t/*\n\t * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't\n\t * use OUTER because that could someday conflict with the normal meaning.)\n\t * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.\n     * For a WorkTableScan, locate the parent RecursiveUnion plan node and use\n     * that as INNER referent.\n     *\n     * For MERGE, pretend the ModifyTable's source plan (its outer plan) is\n\t * INNER referent.  This is the join from the target relation to the data\n\t * source, and all INNER_VAR Vars in other parts of the query refer to its\n\t * targetlist.\n\t *\n\t * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the\n\t * excluded expression's tlist. (Similar to the SubqueryScan we don't want\n\t * to reuse OUTER, it's used for RETURNING in some modify table cases,\n\t * although not INSERT .. CONFLICT).\n\t */\n\tif (IsA(plan, SubqueryScan))\n\t\tdpns->inner_plan = ((SubqueryScan *) plan)->subplan;\n\telse if (IsA(plan, CteScan))\n\t\tdpns->inner_plan = list_nth(dpns->subplans,\n\t\t\t\t\t\t\t\t\t((CteScan *) plan)->ctePlanId - 1);\n    else if (IsA(plan, WorkTableScan))\n\t\tdpns->inner_plan = find_recursive_union(dpns,\n\t\t\t\t\t\t\t\t\t\t\t\t(WorkTableScan *) plan);\n\telse if (IsA(plan, ModifyTable))\n\t{\n\t\tif (((ModifyTable *) plan)->operation == CMD_MERGE)\n\t\t\tdpns->inner_plan = outerPlan(plan);\n\t\telse\n\t\t\tdpns->inner_plan = plan;\n\t}\n\telse\n\t\tdpns->inner_plan = innerPlan(plan);\n\n\tif (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)\n\t\tdpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;\n\telse if (dpns->inner_plan)\n\t\tdpns->inner_tlist = dpns->inner_plan->targetlist;\n\telse\n\t\tdpns->inner_tlist = NIL;\n\n\t/* Set up referent for INDEX_VAR Vars, if needed */\n\tif (IsA(plan, IndexOnlyScan))\n\t\tdpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;\n\telse if (IsA(plan, ForeignScan))\n\t\tdpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;\n\telse if (IsA(plan, CustomScan))\n\t\tdpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;\n\telse\n\t\tdpns->index_tlist = NIL;\n}\n\n/*\n * Locate the ancestor plan node that is the RecursiveUnion generating\n * the WorkTableScan's work table.  We can match on wtParam, since that\n * should be unique within the plan tree.\n */\nstatic Plan *\nfind_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)\n{\n\tListCell   *lc;\n\n\tforeach(lc, dpns->ancestors)\n\t{\n\t\tPlan\t   *ancestor = (Plan *) lfirst(lc);\n\n\t\tif (IsA(ancestor, RecursiveUnion) &&\n\t\t\t((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)\n\t\t\treturn ancestor;\n\t}\n\telog(ERROR, \"could not find RecursiveUnion for WorkTableScan with wtParam %d\",\n\t\t wtscan->wtParam);\n\treturn NULL;\n}\n\n/*\n * push_child_plan: temporarily transfer deparsing attention to a child plan\n *\n * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the\n * deparse context in case the referenced expression itself uses\n * OUTER_VAR/INNER_VAR.  We modify the top stack entry in-place to avoid\n * affecting levelsup issues (although in a Plan tree there really shouldn't\n * be any).\n *\n * Caller must provide a local deparse_namespace variable to save the\n * previous state for pop_child_plan.\n */\nstatic void\npush_child_plan(deparse_namespace *dpns, Plan *plan,\n\t\t\t\tdeparse_namespace *save_dpns)\n{\n\t/* Save state for restoration later */\n\t*save_dpns = *dpns;\n\n\t/* Link current plan node into ancestors list */\n\tdpns->ancestors = lcons(dpns->plan, dpns->ancestors);\n\n\t/* Set attention on selected child */\n\tset_deparse_plan(dpns, plan);\n}\n\n/*\n * pop_child_plan: undo the effects of push_child_plan\n */\nstatic void\npop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)\n{\n\tList\t   *ancestors;\n\n\t/* Get rid of ancestors list cell added by push_child_plan */\n\tancestors = list_delete_first(dpns->ancestors);\n\n\t/* Restore fields changed by push_child_plan */\n\t*dpns = *save_dpns;\n\n\t/* Make sure dpns->ancestors is right (may be unnecessary) */\n\tdpns->ancestors = ancestors;\n}\n\n/*\n * push_ancestor_plan: temporarily transfer deparsing attention to an\n * ancestor plan\n *\n * When expanding a Param reference, we must adjust the deparse context\n * to match the plan node that contains the expression being printed;\n * otherwise we'd fail if that expression itself contains a Param or\n * OUTER_VAR/INNER_VAR/INDEX_VAR variable.\n *\n * The target ancestor is conveniently identified by the ListCell holding it\n * in dpns->ancestors.\n *\n * Caller must provide a local deparse_namespace variable to save the\n * previous state for pop_ancestor_plan.\n */\nstatic void\npush_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,\n\t\t\t\t   deparse_namespace *save_dpns)\n{\n\tPlan\t   *plan = (Plan *) lfirst(ancestor_cell);\n\n\t/* Save state for restoration later */\n\t*save_dpns = *dpns;\n\n\t/* Build a new ancestor list with just this node's ancestors */\n\tdpns->ancestors =\n\t\tlist_copy_tail(dpns->ancestors,\n\t\t\t\t\t   list_cell_number(dpns->ancestors, ancestor_cell) + 1);\n\n\t/* Set attention on selected ancestor */\n\tset_deparse_plan(dpns, plan);\n}\n\n/*\n * pop_ancestor_plan: undo the effects of push_ancestor_plan\n */\nstatic void\npop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)\n{\n\t/* Free the ancestor list made in push_ancestor_plan */\n\tlist_free(dpns->ancestors);\n\n\t/* Restore fields changed by push_ancestor_plan */\n\t*dpns = *save_dpns;\n}\n\n/* ----------\n * deparse_shard_query\t\t- Parse back a query for execution on a shard\n *\n * Builds an SQL string to perform the provided query on a specific shard and\n * places this string into the provided buffer.\n * ----------\n */\nvoid\ndeparse_shard_query(Query *query, Oid distrelid, int64 shardid,\n\t\t\t\t\tStringInfo buffer)\n{\n\tget_query_def_extended(query, buffer, NIL, distrelid, shardid, NULL,\n\t                       false,\n\t\t\t\t\t\t   0, WRAP_COLUMN_DEFAULT, 0);\n}\n\n/* ----------\n * get_query_def\t\t\t- Parse back one query parsetree\n *\n * query: parsetree to be displayed\n * buf: output text is appended to buf\n * parentnamespace: list (initially empty) of outer-level deparse_namespace's\n * resultDesc: if not NULL, the output tuple descriptor for the view\n *\t\trepresented by a SELECT query.  We use the column names from it\n *\t\tto label SELECT output columns, in preference to names in the query\n * colNamesVisible: true if the surrounding context cares about the output\n *\t\tcolumn names at all (as, for example, an EXISTS() context does not);\n *\t\twhen false, we can suppress dummy column labels such as \"?column?\"\n * prettyFlags: bitmask of PRETTYFLAG_XXX options\n * wrapColumn: maximum line length, or -1 to disable wrapping\n * startIndent: initial indentation amount\n * ----------\n */\nstatic void\nget_query_def(Query *query, StringInfo buf, List *parentnamespace,\n\t\t\t  TupleDesc resultDesc, bool colNamesVisible,\n\t\t\t  int prettyFlags, int wrapColumn, int startIndent)\n{\n\tget_query_def_extended(query, buf, parentnamespace, InvalidOid, 0, resultDesc,\n\t                       colNamesVisible,\n\t\t\t\t\t\t   prettyFlags, wrapColumn, startIndent);\n}\n\n/* ----------\n * get_query_def_extended\t\t- Parse back one query parsetree, optionally\n * \t\t\t\t\t\t\t\t  with extension using a shard identifier.\n *\n * If distrelid is valid and shardid is positive, the provided shardid is added\n * any time the provided relid is deparsed, so that the query may be executed\n * on a placement for the given shard.\n * ----------\n */\nstatic void\nget_query_def_extended(Query *query, StringInfo buf, List *parentnamespace,\n\t\t\t\t\t   Oid distrelid, int64 shardid, TupleDesc resultDesc,\n\t\t\t\t\t   bool colNamesVisible,\n\t\t\t\t\t   int prettyFlags, int wrapColumn, int startIndent)\n{\n\tdeparse_context context;\n\tdeparse_namespace dpns;\n\tint\t\t\trtable_size;\n\n\t/* Guard against excessively long or deeply-nested queries */\n\tCHECK_FOR_INTERRUPTS();\n\tcheck_stack_depth();\n\n\trtable_size = query->hasGroupRTE ?\n\t\tlist_length(query->rtable) - 1 :\n\t\tlist_length(query->rtable);\n\n\t/*\n\t * Replace any Vars in the query's targetlist and havingQual that\n\t * reference GROUP outputs with the underlying grouping expressions.\n\t */\n\tif (query->hasGroupRTE)\n\t{\n\t\tquery->targetList = (List *)\n\t\t\tflatten_group_exprs(NULL, query, (Node *) query->targetList);\n\t\tquery->havingQual =\n\t\t\tflatten_group_exprs(NULL, query, query->havingQual);\n\t}\n\n\t/*\n\t * Before we begin to examine the query, acquire locks on referenced\n\t * relations, and fix up deleted columns in JOIN RTEs.  This ensures\n\t * consistent results.  Note we assume it's OK to scribble on the passed\n\t * querytree!\n\t *\n\t * We are only deparsing the query (we are not about to execute it), so we\n\t * only need AccessShareLock on the relations it mentions.\n\t */\n\tAcquireRewriteLocks(query, false, false);\n\n\t/*\n\t * Set search_path to NIL so that all objects outside of pg_catalog will be\n\t * schema-prefixed. pg_catalog will be added automatically when we call\n\t * PushEmptySearchPath().\n\t */\n\tint saveNestLevel = PushEmptySearchPath();\n\n\tcontext.buf = buf;\n\tcontext.namespaces = lcons(&dpns, list_copy(parentnamespace));\n\tcontext.resultDesc = NULL;\n\tcontext.targetList = NIL;\n\tcontext.windowClause = NIL;\n\tcontext.varprefix = (parentnamespace != NIL ||\n\t\t\t\t\t\t rtable_size != 1);\n\tcontext.prettyFlags = prettyFlags;\n\tcontext.wrapColumn = wrapColumn;\n\tcontext.indentLevel = startIndent;\n\tcontext.colNamesVisible = true;\n\tcontext.inGroupBy = false;\n\tcontext.varInOrderBy = false;\n\tcontext.appendparents = NULL;\n\tcontext.distrelid = distrelid;\n\tcontext.shardid = shardid;\n\n\tset_deparse_for_query(&dpns, query, parentnamespace);\n\n\tswitch (query->commandType)\n\t{\n\t\tcase CMD_SELECT:\n\t\t\t/* We set context.resultDesc only if it's a SELECT */\n\t\t\tcontext.resultDesc = resultDesc;\n\t\t\tget_select_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_UPDATE:\n\t\t\tget_update_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_INSERT:\n\t\t\tget_insert_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_DELETE:\n\t\t\tget_delete_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_MERGE:\n\t\t\tget_merge_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tcase CMD_NOTHING:\n\t\t\tappendStringInfoString(buf, \"NOTHING\");\n\t\t\tbreak;\n\n\t\tcase CMD_UTILITY:\n\t\t\tget_utility_query_def(query, &context);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\telog(ERROR, \"unrecognized query command type: %d\",\n\t\t\t\t query->commandType);\n\t\t\tbreak;\n\t}\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n}\n\n/* ----------\n * get_values_def\t\t\t- Parse back a VALUES list\n * ----------\n */\nstatic void\nget_values_def(List *values_lists, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tfirst_list = true;\n\tListCell   *vtl;\n\n\tappendStringInfoString(buf, \"VALUES \");\n\n\tforeach(vtl, values_lists)\n\t{\n\t\tList\t   *sublist = (List *) lfirst(vtl);\n\t\tbool\t\tfirst_col = true;\n\t\tListCell   *lc;\n\n\t\tif (first_list)\n\t\t\tfirst_list = false;\n\t\telse\n\t\t\tappendStringInfoString(buf, \", \");\n\n\t\tappendStringInfoChar(buf, '(');\n\t\tforeach(lc, sublist)\n\t\t{\n\t\t\tNode\t   *col = (Node *) lfirst(lc);\n\n\t\t\tif (first_col)\n\t\t\t\tfirst_col = false;\n\t\t\telse\n\t\t\t\tappendStringInfoChar(buf, ',');\n\n\t\t\t/*\n\t\t\t * Print the value.  Whole-row Vars need special treatment.\n\t\t\t */\n\t\t\tget_rule_expr_toplevel(col, context, false);\n\t\t}\n\t\tappendStringInfoChar(buf, ')');\n\t}\n}\n\n/* ----------\n * get_with_clause\t\t\t- Parse back a WITH clause\n * ----------\n */\nstatic void\nget_with_clause(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *sep;\n\tListCell   *l;\n\n\tif (query->cteList == NIL)\n\t\treturn;\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t\tappendStringInfoChar(buf, ' ');\n\t}\n\n\tif (query->hasRecursive)\n\t\tsep = \"WITH RECURSIVE \";\n\telse\n\t\tsep = \"WITH \";\n\tforeach(l, query->cteList)\n\t{\n\t\tCommonTableExpr *cte = (CommonTableExpr *) lfirst(l);\n\n\t\tappendStringInfoString(buf, sep);\n\t\tappendStringInfoString(buf, quote_identifier(cte->ctename));\n\t\tif (cte->aliascolnames)\n\t\t{\n\t\t\tbool\t\tfirst = true;\n\t\t\tListCell   *col;\n\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tforeach(col, cte->aliascolnames)\n\t\t\t{\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(strVal(lfirst(col))));\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\tappendStringInfoString(buf, \" AS \");\n\t\tswitch (cte->ctematerialized)\n\t\t{\n\t\t\tcase CTEMaterializeDefault:\n\t\t\t\tbreak;\n\t\t\tcase CTEMaterializeAlways:\n\t\t\t\tappendStringInfoString(buf, \"MATERIALIZED \");\n\t\t\t\tbreak;\n\t\t\tcase CTEMaterializeNever:\n\t\t\t\tappendStringInfoString(buf, \"NOT MATERIALIZED \");\n\t\t\t\tbreak;\n\t\t}\n\t\tappendStringInfoChar(buf, '(');\n\t\tif (PRETTY_INDENT(context))\n\t\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\t\tget_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,\n\t\t              true,\n\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t  context->indentLevel);\n\t\tif (PRETTY_INDENT(context))\n\t\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\t\tappendStringInfoChar(buf, ')');\n\n\t\tif (cte->search_clause)\n\t\t{\n\t\t\tbool\t\tfirst = true;\n\t\t\tListCell   *lc;\n\n\t\t\tappendStringInfo(buf, \" SEARCH %s FIRST BY \",\n\t\t\t\t\t\t\t cte->search_clause->search_breadth_first ? \"BREADTH\" : \"DEPTH\");\n\n\t\t\tforeach(lc, cte->search_clause->search_col_list)\n\t\t\t{\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(strVal(lfirst(lc))));\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" SET %s\", quote_identifier(cte->search_clause->search_seq_column));\n\t\t}\n\n\t\tif (cte->cycle_clause)\n\t\t{\n\t\t\tbool\t\tfirst = true;\n\t\t\tListCell   *lc;\n\n\t\t\tappendStringInfoString(buf, \" CYCLE \");\n\n\t\t\tforeach(lc, cte->cycle_clause->cycle_col_list)\n\t\t\t{\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(strVal(lfirst(lc))));\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" SET %s\", quote_identifier(cte->cycle_clause->cycle_mark_column));\n\n\t\t\t{\n\t\t\t\tConst\t   *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);\n\t\t\t\tConst\t   *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);\n\n\t\t\t\tif (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&\n\t\t\t\t\t  cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(buf, \" TO \");\n\t\t\t\t\tget_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);\n\t\t\t\t\tappendStringInfoString(buf, \" DEFAULT \");\n\t\t\t\t\tget_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" USING %s\", quote_identifier(cte->cycle_clause->cycle_path_column));\n\t\t}\n\n\t\tsep = \", \";\n\t}\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel -= PRETTYINDENT_STD;\n\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\t}\n\telse\n\t\tappendStringInfoChar(buf, ' ');\n}\n\n/* ----------\n * get_select_query_def\t\t\t- Parse back a SELECT parsetree\n * ----------\n */\nstatic void\nget_select_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tforce_colno;\n\tListCell   *l;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/* Subroutines may need to consult the SELECT targetlist and windowClause */\n\tcontext->targetList = query->targetList;\n\tcontext->windowClause = query->windowClause;\n\n\t/*\n\t * If the Query node has a setOperations tree, then it's the top level of\n\t * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT\n\t * fields are interesting in the top query itself.\n\t */\n\tif (query->setOperations)\n\t{\n\t\tget_setop_query(query->setOperations, query, context);\n\t\t/* ORDER BY clauses must be simple in this case */\n\t\tforce_colno = true;\n\t}\n\telse\n\t{\n\t\tget_basic_select_query(query, context);\n\t\tforce_colno = false;\n\t}\n\n\t/* Add the ORDER BY clause if given */\n\tif (query->sortClause != NIL)\n\t{\n\t\tappendContextKeyword(context, \" ORDER BY \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_orderby(query->sortClause, query->targetList,\n\t\t\t\t\t\t force_colno, context);\n\t}\n\n\t/*\n\t * Add the LIMIT/OFFSET clauses if given. If non-default options, use the\n\t * standard spelling of LIMIT.\n\t */\n\tif (query->limitOffset != NULL)\n\t{\n\t\tappendContextKeyword(context, \" OFFSET \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\tget_rule_expr(query->limitOffset, context, false);\n\t}\n\tif (query->limitCount != NULL)\n\t{\n\t\tif (query->limitOption == LIMIT_OPTION_WITH_TIES)\n\t\t{\n\t\t\t/*\n\t\t\t * The limitCount arg is a c_expr, so it needs parens. Simple\n\t\t\t * literals and function expressions would not need parens, but\n\t\t\t * unfortunately it's hard to tell if the expression will be\n\t\t\t * printed as a simple literal like 123 or as a typecast\n\t\t\t * expression, like '-123'::int4. The grammar accepts the former\n\t\t\t * without quoting, but not the latter.\n\t\t\t */\n\t\t\t// had to add '(' and ')' here because it fails with casting\n\t\t\tappendContextKeyword(context, \" FETCH FIRST (\",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr(query->limitCount, context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\tappendStringInfoString(buf, \") ROWS WITH TIES\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendContextKeyword(context, \" LIMIT \",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\tif (IsA(query->limitCount, Const) &&\n\t\t\t\t((Const *) query->limitCount)->constisnull)\n\t\t\t\tappendStringInfoString(buf, \"ALL\");\n\t\t\telse\n\t\t\t\tget_rule_expr(query->limitCount, context, false);\n\t\t}\n\t}\n\n\t/* Add FOR [KEY] UPDATE/SHARE clauses if present */\n\tif (query->hasForUpdate)\n\t{\n\t\tforeach(l, query->rowMarks)\n\t\t{\n\t\t\tRowMarkClause *rc = (RowMarkClause *) lfirst(l);\n\n\t\t\t/* don't print implicit clauses */\n\t\t\tif (rc->pushedDown)\n\t\t\t\tcontinue;\n\n\t\t\tswitch (rc->strength)\n\t\t\t{\n\t\t\t\tcase LCS_NONE:\n\t\t\t\t\t/* we intentionally throw an error for LCS_NONE */\n\t\t\t\t\telog(ERROR, \"unrecognized LockClauseStrength %d\",\n\t\t\t\t\t\t (int) rc->strength);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORKEYSHARE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR KEY SHARE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORSHARE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR SHARE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORNOKEYUPDATE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR NO KEY UPDATE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase LCS_FORUPDATE:\n\t\t\t\t\tappendContextKeyword(context, \" FOR UPDATE\",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tappendStringInfo(buf, \" OF %s\",\n\t\t\t\t\t\t\t quote_identifier(get_rtable_name(rc->rti,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  context)));\n\t\t\tif (rc->waitPolicy == LockWaitError)\n\t\t\t\tappendStringInfoString(buf, \" NOWAIT\");\n\t\t\telse if (rc->waitPolicy == LockWaitSkip)\n\t\t\t\tappendStringInfoString(buf, \" SKIP LOCKED\");\n\t\t}\n\t}\n}\n\n/*\n * Detect whether query looks like SELECT ... FROM VALUES();\n * if so, return the VALUES RTE.  Otherwise return NULL.\n */\nstatic RangeTblEntry *\nget_simple_values_rte(Query *query, TupleDesc resultDesc)\n{\n\tRangeTblEntry *result = NULL;\n\tListCell   *lc;\n\tint colno;\n\n\t/*\n\t * We want to return true even if the Query also contains OLD or NEW rule\n\t * RTEs.  So the idea is to scan the rtable and see if there is only one\n\t * inFromCl RTE that is a VALUES RTE.\n\t */\n\tforeach(lc, query->rtable)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n\n\t\tif (rte->rtekind == RTE_VALUES && rte->inFromCl)\n\t\t{\n\t\t\tif (result)\n\t\t\t\treturn NULL;\t/* multiple VALUES (probably not possible) */\n\t\t\tresult = rte;\n\t\t}\n\t\telse if (rte->rtekind == RTE_RELATION && !rte->inFromCl)\n\t\t\tcontinue;\t\t\t/* ignore rule entries */\n\t\telse\n\t\t\treturn NULL;\t\t/* something else -> not simple VALUES */\n\t}\n\n\t/*\n\t * We don't need to check the targetlist in any great detail, because\n\t * parser/analyze.c will never generate a \"bare\" VALUES RTE --- they only\n\t * appear inside auto-generated sub-queries with very restricted\n\t * structure.  However, DefineView might have modified the tlist by\n\t * injecting new column aliases; so compare tlist resnames against the\n\t * RTE's names to detect that.\n\t */\n\tif (result)\n\t{\n\t\tListCell   *lcn;\n\n\t\tif (list_length(query->targetList) != list_length(result->eref->colnames))\n\t\t\treturn NULL;\t\t/* this probably cannot happen */\n\t\tcolno = 0;\n\t\tforboth(lc, query->targetList, lcn, result->eref->colnames)\n\t\t{\n\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(lc);\n\t\t\tchar\t   *cname = strVal(lfirst(lcn));\n\t\t\tchar\t   *colname;\n\n\t\t\tif (tle->resjunk)\n\t\t\t\treturn NULL;\t/* this probably cannot happen */\n\t\t\t/* compute name that get_target_list would use for column */\n\t\t\tcolno++;\n\t\t\tif (resultDesc && colno <= resultDesc->natts)\n\t\t\t\tcolname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);\n\t\t\telse\n\t\t\t\tcolname = tle->resname;\n\n\t\t\t/* does it match the VALUES RTE? */\n\t\t\tif (colname == NULL || strcmp(colname, cname) != 0)\n\t\t\t\treturn NULL;\t/* column name has been changed */\n\t\t}\n\t}\n\n\treturn result;\n}\n\nstatic void\nget_basic_select_query(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *values_rte;\n\tchar\t   *sep;\n\tListCell   *l;\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t\tappendStringInfoChar(buf, ' ');\n\t}\n\n\t/*\n\t * If the query looks like SELECT * FROM (VALUES ...), then print just the\n\t * VALUES part.  This reverses what transformValuesClause() did at parse\n\t * time.\n\t */\n\tvalues_rte = get_simple_values_rte(query, context->resultDesc);\n\tif (values_rte)\n\t{\n\t\tget_values_def(values_rte->values_lists, context);\n\t\treturn;\n\t}\n\n\t/*\n\t * Build up the query string - first we say SELECT\n\t */\n\tif (query->isReturn)\n\t\tappendStringInfoString(buf, \"RETURN\");\n\telse\n\t\tappendStringInfoString(buf, \"SELECT\");\n\n\t/* Add the DISTINCT clause if given */\n\tif (query->distinctClause != NIL)\n\t{\n\t\tif (query->hasDistinctOn)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DISTINCT ON (\");\n\t\t\tsep = \"\";\n\t\t\tforeach(l, query->distinctClause)\n\t\t\t{\n\t\t\t\tSortGroupClause *srt = (SortGroupClause *) lfirst(l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,\n\t\t\t\t\t\t\t\t\t\t false, context);\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse\n\t\t\tappendStringInfoString(buf, \" DISTINCT\");\n\t}\n\n\t/* Then we tell what to select (the targetlist) */\n\tget_target_list(query->targetList, context);\n\n\t/* Add the FROM clause if needed */\n\tget_from_clause(query, \" FROM \", context);\n\n\t/* Add the WHERE clause if given */\n\tif (query->jointree->quals != NULL)\n\t{\n\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_expr(query->jointree->quals, context, false);\n\t}\n\n\t/* Add the GROUP BY clause if given */\n\tif (query->groupClause != NULL || query->groupingSets != NULL)\n\t{\n\t\tbool\t\tsave_ingroupby;\n\n\t\tappendContextKeyword(context, \" GROUP BY \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tif (query->groupDistinct)\n\t\t\tappendStringInfoString(buf, \"DISTINCT \");\n\n\t\tsave_ingroupby = context->inGroupBy;\n\t\tcontext->inGroupBy = true;\n\n\t\tif (query->groupingSets == NIL)\n\t\t{\n\t\t\tsep = \"\";\n\t\t\tforeach(l, query->groupClause)\n\t\t\t{\n\t\t\t\tSortGroupClause *grp = (SortGroupClause *) lfirst(l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,\n\t\t\t\t\t\t\t\t\t\t false, context);\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsep = \"\";\n\t\t\tforeach(l, query->groupingSets)\n\t\t\t{\n\t\t\t\tGroupingSet *grp = lfirst(l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_groupingset(grp, query->targetList, true, context);\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t}\n\n\t\tcontext->inGroupBy = save_ingroupby;\n\t}\n\n\t/* Add the HAVING clause if given */\n\tif (query->havingQual != NULL)\n\t{\n\t\tappendContextKeyword(context, \" HAVING \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);\n\t\tget_rule_expr(query->havingQual, context, false);\n\t}\n\n\t/* Add the WINDOW clause if needed */\n\tif (query->windowClause != NIL)\n\t\tget_rule_windowclause(query, context);\n}\n\n/* ----------\n * get_target_list\t\t\t- Parse back a SELECT target list\n *\n * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.\n * ----------\n */\nstatic void\nget_target_list(List *targetList, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tStringInfoData targetbuf;\n\tbool\t\tlast_was_multiline = false;\n\tchar\t   *sep;\n\tint\t\t\tcolno;\n\tListCell   *l;\n\n\t/* we use targetbuf to hold each TLE's text temporarily */\n\tinitStringInfo(&targetbuf);\n\n\tsep = \" \";\n\tcolno = 0;\n\tforeach(l, targetList)\n\t{\n\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\t\tchar\t   *colname;\n\t\tchar\t   *attname;\n\n\t\tif (tle->resjunk)\n\t\t\tcontinue;\t\t\t/* ignore junk entries */\n\n\t\tappendStringInfoString(buf, sep);\n\t\tsep = \", \";\n\t\tcolno++;\n\n\t\t/*\n\t\t * Put the new field text into targetbuf so we can decide after we've\n\t\t * got it whether or not it needs to go on a new line.\n\t\t */\n\t\tresetStringInfo(&targetbuf);\n\t\tcontext->buf = &targetbuf;\n\n\t\t/*\n\t\t * We special-case Var nodes rather than using get_rule_expr. This is\n\t\t * needed because get_rule_expr will display a whole-row Var as\n\t\t * \"foo.*\", which is the preferred notation in most contexts, but at\n\t\t * the top level of a SELECT list it's not right (the parser will\n\t\t * expand that notation into multiple columns, yielding behavior\n\t\t * different from a whole-row Var).  We need to call get_variable\n\t\t * directly so that we can tell it to do the right thing, and so that\n\t\t * we can get the attribute name which is the default AS label.\n\t\t */\n\t\tif (tle->expr && (IsA(tle->expr, Var)))\n\t\t{\n\t\t\tattname = get_variable((Var *) tle->expr, 0, true, context);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tget_rule_expr((Node *) tle->expr, context, true);\n\n\t\t\t/*\n\t\t\t * When colNamesVisible is true, we should always show the\n\t\t\t * assigned column name explicitly.  Otherwise, show it only if\n\t\t\t * it's not FigureColname's fallback.\n\t\t\t */\n\t\t\tattname = context->colNamesVisible ? NULL : \"?column?\";\n\t\t}\n\n\t\t/*\n\t\t * Figure out what the result column should be called.  In the context\n\t\t * of a view, use the view's tuple descriptor (so as to pick up the\n\t\t * effects of any column RENAME that's been done on the view).\n\t\t * Otherwise, just use what we can find in the TLE.\n\t\t */\n\t\tif (context->resultDesc && colno <= context->resultDesc->natts)\n\t\t\tcolname = NameStr(TupleDescAttr(context->resultDesc,\n\t\t\t\t\t\t\t\t\t\t\tcolno - 1)->attname);\n\t\telse\n\t\t\tcolname = tle->resname;\n\n\t\t/* Show AS unless the column's name is correct as-is */\n\t\tif (colname)\t\t\t/* resname could be NULL */\n\t\t{\n\t\t\tif (attname == NULL || strcmp(attname, colname) != 0)\n\t\t\t\tappendStringInfo(&targetbuf, \" AS %s\", quote_identifier(colname));\n\t\t}\n\n\t\t/* Restore context's output buffer */\n\t\tcontext->buf = buf;\n\n\t\t/* Consider line-wrapping if enabled */\n\t\tif (PRETTY_INDENT(context) && context->wrapColumn >= 0)\n\t\t{\n\t\t\tint\t\t\tleading_nl_pos;\n\n\t\t\t/* Does the new field start with a new line? */\n\t\t\tif (targetbuf.len > 0 && targetbuf.data[0] == '\\n')\n\t\t\t\tleading_nl_pos = 0;\n\t\t\telse\n\t\t\t\tleading_nl_pos = -1;\n\n\t\t\t/* If so, we shouldn't add anything */\n\t\t\tif (leading_nl_pos >= 0)\n\t\t\t{\n\t\t\t\t/* instead, remove any trailing spaces currently in buf */\n\t\t\t\tremoveStringInfoSpaces(buf);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar\t   *trailing_nl;\n\n\t\t\t\t/* Locate the start of the current line in the output buffer */\n\t\t\t\ttrailing_nl = strrchr(buf->data, '\\n');\n\t\t\t\tif (trailing_nl == NULL)\n\t\t\t\t\ttrailing_nl = buf->data;\n\t\t\t\telse\n\t\t\t\t\ttrailing_nl++;\n\n\t\t\t\t/*\n\t\t\t\t * Add a newline, plus some indentation, if the new field is\n\t\t\t\t * not the first and either the new field would cause an\n\t\t\t\t * overflow or the last field used more than one line.\n\t\t\t\t */\n\t\t\t\tif (colno > 1 &&\n\t\t\t\t\t((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||\n\t\t\t\t\t last_was_multiline))\n\t\t\t\t\tappendContextKeyword(context, \"\", -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD, PRETTYINDENT_VAR);\n\t\t\t}\n\n\t\t\t/* Remember this field's multiline status for next iteration */\n\t\t\tlast_was_multiline =\n\t\t\t\t(strchr(targetbuf.data + leading_nl_pos + 1, '\\n') != NULL);\n\t\t}\n\n\t\t/* Add the new field */\n\t\tappendStringInfoString(buf, targetbuf.data);\n\t}\n\n\t/* clean up */\n\tpfree(targetbuf.data);\n}\n\nstatic void\nget_returning_clause(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (query->returningList)\n\t{\n\t\tbool\t\thave_with = false;\n\n\t\tappendContextKeyword(context, \" RETURNING\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\n\t\t/* Add WITH (OLD/NEW) options, if they're not the defaults */\n\t\tif (query->returningOldAlias && strcmp(query->returningOldAlias, \"old\") != 0)\n\t\t{\n\t\t\tappendStringInfo(buf, \" WITH (OLD AS %s\",\n\t\t\t\t\t\t\t quote_identifier(query->returningOldAlias));\n\t\t\thave_with = true;\n\t\t}\n\t\tif (query->returningNewAlias && strcmp(query->returningNewAlias, \"new\") != 0)\n\t\t{\n\t\t\tif (have_with)\n\t\t\t\tappendStringInfo(buf, \", NEW AS %s\",\n\t\t\t\t\t\t\t\t quote_identifier(query->returningNewAlias));\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \" WITH (NEW AS %s\",\n\t\t\t\t\t\t\t\t quote_identifier(query->returningNewAlias));\n\t\t\t\thave_with = true;\n\t\t\t}\n\t\t}\n\t\tif (have_with)\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t/* Add the returning expressions themselves */\n\t\tget_target_list(query->returningList, context);\n\t}\n}\n\nstatic void\nget_setop_query(Node *setOp, Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tneed_paren;\n\n\t/* Guard against excessively long or deeply-nested queries */\n\tCHECK_FOR_INTERRUPTS();\n\tcheck_stack_depth();\n\n\tif (IsA(setOp, RangeTblRef))\n\t{\n\t\tRangeTblRef *rtr = (RangeTblRef *) setOp;\n\t\tRangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);\n\t\tQuery\t   *subquery = rte->subquery;\n\n\t\tAssert(subquery != NULL);\n\t\t/*\n\t\t * We need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y.\n\t\t * Also add parens if the leaf query contains its own set operations.\n\t\t * (That shouldn't happen unless one of the other clauses is also\n\t\t * present, see transformSetOperationTree; but let's be safe.)\n\t\t */\n\t\tneed_paren = (subquery->cteList ||\n\t\t\t\t\t  subquery->sortClause ||\n\t\t\t\t\t  subquery->rowMarks ||\n\t\t\t\t\t  subquery->limitOffset ||\n\t\t\t\t\t  subquery->limitCount ||\n\t\t\t\t\t  subquery->setOperations);\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_query_def(subquery, buf, context->namespaces,\n\t\t\t\t\t  context->resultDesc, context->colNamesVisible,\n\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t  context->indentLevel);\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(buf, ')');\n\t}\n\telse if (IsA(setOp, SetOperationStmt))\n\t{\n\t\tSetOperationStmt *op = (SetOperationStmt *) setOp;\n\t\tint\t\t\tsubindent;\n\t\tbool\t\tsave_colnamesvisible;\n\n\t\t/*\n\t\t * We force parens when nesting two SetOperationStmts, except when the\n\t\t * lefthand input is another setop of the same kind.  Syntactically,\n\t\t * we could omit parens in rather more cases, but it seems best to use\n\t\t * parens to flag cases where the setop operator changes.  If we use\n\t\t * parens, we also increase the indentation level for the child query.\n\t\t *\n\t\t * There are some cases in which parens are needed around a leaf query\n\t\t * too, but those are more easily handled at the next level down (see\n\t\t * code above).\n\t\t */\n\t\tif (IsA(op->larg, SetOperationStmt))\n\t\t{\n\t\t\tSetOperationStmt *lop = (SetOperationStmt *) op->larg;\n\n\t\t\tif (op->op == lop->op && op->all == lop->all)\n\t\t\t\tneed_paren = false;\n\t\t\telse\n\t\t\t\tneed_paren = true;\n\t\t}\n\t\telse\n\t\t\tneed_paren = false;\n\n\t\tif (need_paren)\n\t\t{\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tsubindent = PRETTYINDENT_STD;\n\t\t\tappendContextKeyword(context, \"\", subindent, 0, 0);\n\t\t}\n\t\telse\n\t\t\tsubindent = 0;\n\n\t\tget_setop_query(op->larg, query, context);\n\n\t\tif (need_paren)\n\t\t\tappendContextKeyword(context, \") \", -subindent, 0, 0);\n\t\telse if (PRETTY_INDENT(context))\n\t\t\tappendContextKeyword(context, \"\", -subindent, 0, 0);\n\t\telse\n\t\t\tappendStringInfoChar(buf, ' ');\n\n\t\tswitch (op->op)\n\t\t{\n\t\t\tcase SETOP_UNION:\n\t\t\t\tappendStringInfoString(buf, \"UNION \");\n\t\t\t\tbreak;\n\t\t\tcase SETOP_INTERSECT:\n\t\t\t\tappendStringInfoString(buf, \"INTERSECT \");\n\t\t\t\tbreak;\n\t\t\tcase SETOP_EXCEPT:\n\t\t\t\tappendStringInfoString(buf, \"EXCEPT \");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\telog(ERROR, \"unrecognized set op: %d\",\n\t\t\t\t\t (int) op->op);\n\t\t}\n\t\tif (op->all)\n\t\t\tappendStringInfoString(buf, \"ALL \");\n\n\t\t/* Always parenthesize if RHS is another setop */\n\t\tneed_paren = IsA(op->rarg, SetOperationStmt);\n\n\t\t/*\n\t\t * The indentation code here is deliberately a bit different from that\n\t\t * for the lefthand input, because we want the line breaks in\n\t\t * different places.\n\t\t */\n\t\tif (need_paren)\n\t\t{\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tsubindent = PRETTYINDENT_STD;\n\t\t}\n\t\telse\n\t\t\tsubindent = 0;\n\t\tappendContextKeyword(context, \"\", subindent, 0, 0);\n\n\t\t/*\n\t\t * The output column names of the RHS sub-select don't matter.\n\t\t */\n\t\tsave_colnamesvisible = context->colNamesVisible;\n\t\tcontext->colNamesVisible = false;\n\t\tget_setop_query(op->rarg, query, context);\n\t\tcontext->colNamesVisible = save_colnamesvisible;\n\n\t\tif (PRETTY_INDENT(context))\n\t\t\tcontext->indentLevel -= subindent;\n\t\tif (need_paren)\n\t\t\tappendContextKeyword(context, \")\", 0, 0, 0);\n\t}\n\telse\n\t{\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(setOp));\n\t}\n}\n\n/*\n * Display a sort/group clause.\n *\n * Also returns the expression tree, so caller need not find it again.\n */\nstatic Node *\nget_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,\n\t\t\t\t\t\t deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tTargetEntry *tle;\n\tNode\t   *expr;\n\n\ttle = get_sortgroupref_tle(ref, tlist);\n\texpr = (Node *) tle->expr;\n\n\t/*\n\t * Use column-number form if requested by caller.  Otherwise, if\n\t * expression is a constant, force it to be dumped with an explicit cast\n\t * as decoration --- this is because a simple integer constant is\n\t * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if\n\t * we dump it without any decoration.  Similarly, if it's just a Var,\n\t * there is risk of misinterpretation if the column name is reassigned in\n\t * the SELECT list, so we may need to force table qualification.  And, if\n\t * it's anything more complex than a simple Var, then force extra parens\n\t * around it, to ensure it can't be misinterpreted as a cube() or rollup()\n\t * construct.\n\t */\n\tif (force_colno)\n\t{\n\t\tAssert(!tle->resjunk);\n\t\tappendStringInfo(buf, \"%d\", tle->resno);\n\t}\n\telse if (!expr)\n\t\t /* do nothing, probably can't happen */ ;\n\telse if (IsA(expr, Const))\n\t\tget_const_expr((Const *) expr, context, 1);\n\telse if (IsA(expr, Var))\n\t{\n\t\t/* Tell get_variable to check for name conflict */\n\t\tbool\t\tsave_varinorderby = context->varInOrderBy;\n\t\tcontext->varInOrderBy = true;\n\t\t(void) get_variable((Var *) expr, 0, false, context);\n\t\tcontext->varInOrderBy = save_varinorderby;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * We must force parens for function-like expressions even if\n\t\t * PRETTY_PAREN is off, since those are the ones in danger of\n\t\t * misparsing. For other expressions we need to force them only if\n\t\t * PRETTY_PAREN is on, since otherwise the expression will output them\n\t\t * itself. (We can't skip the parens.)\n\t\t */\n\t\tbool\t\tneed_paren = (PRETTY_PAREN(context)\n\t\t\t\t\t\t\t\t  || IsA(expr, FuncExpr)\n\t\t\t\t\t\t\t\t  || IsA(expr, Aggref)\n\t\t\t\t\t\t\t\t  || IsA(expr, WindowFunc)\n\t\t\t\t\t\t\t\t  || IsA(expr, JsonConstructorExpr));\n\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, '(');\n\t\tget_rule_expr(expr, context, true);\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, ')');\n\t}\n\n\treturn expr;\n}\n\n/*\n * Display a GroupingSet\n */\nstatic void\nget_rule_groupingset(GroupingSet *gset, List *targetlist,\n\t\t\t\t\t bool omit_parens, deparse_context *context)\n{\n\tListCell   *l;\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tomit_child_parens = true;\n\tchar\t   *sep = \"\";\n\n\tswitch (gset->kind)\n\t{\n\t\tcase GROUPING_SET_EMPTY:\n\t\t\tappendStringInfoString(buf, \"()\");\n\t\t\treturn;\n\n\t\tcase GROUPING_SET_SIMPLE:\n\t\t\t{\n\t\t\t\tif (!omit_parens || list_length(gset->content) != 1)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\n\t\t\t\tforeach(l, gset->content)\n\t\t\t\t{\n\t\t\t\t\tIndex\t\tref = lfirst_int(l);\n\n\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\tget_rule_sortgroupclause(ref, targetlist,\n\t\t\t\t\t\t\t\t\t\t\t false, context);\n\t\t\t\t\tsep = \", \";\n\t\t\t\t}\n\n\t\t\t\tif (!omit_parens || list_length(gset->content) != 1)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\treturn;\n\n\t\tcase GROUPING_SET_ROLLUP:\n\t\t\tappendStringInfoString(buf, \"ROLLUP(\");\n\t\t\tbreak;\n\t\tcase GROUPING_SET_CUBE:\n\t\t\tappendStringInfoString(buf, \"CUBE(\");\n\t\t\tbreak;\n\t\tcase GROUPING_SET_SETS:\n\t\t\tappendStringInfoString(buf, \"GROUPING SETS (\");\n\t\t\tomit_child_parens = false;\n\t\t\tbreak;\n\t}\n\n\tforeach(l, gset->content)\n\t{\n\t\tappendStringInfoString(buf, sep);\n\t\tget_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);\n\t\tsep = \", \";\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * Display an ORDER BY list.\n */\nstatic void\nget_rule_orderby(List *orderList, List *targetList,\n\t\t\t\t bool force_colno, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *sep;\n\tListCell   *l;\n\n\tsep = \"\";\n\tforeach(l, orderList)\n\t{\n\t\tSortGroupClause *srt = (SortGroupClause *) lfirst(l);\n\t\tNode\t   *sortexpr;\n\t\tOid\t\t\tsortcoltype;\n\t\tTypeCacheEntry *typentry;\n\n\t\tappendStringInfoString(buf, sep);\n\t\tsortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,\n\t\t\t\t\t\t\t\t\t\t\tforce_colno, context);\n\t\tsortcoltype = exprType(sortexpr);\n\t\t/* See whether operator is default < or > for datatype */\n\t\ttypentry = lookup_type_cache(sortcoltype,\n\t\t\t\t\t\t\t\t\t TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);\n\t\tif (srt->sortop == typentry->lt_opr)\n\t\t{\n\t\t\t/* ASC is default, so emit nothing for it */\n\t\t\tif (srt->nulls_first)\n\t\t\t\tappendStringInfoString(buf, \" NULLS FIRST\");\n\t\t}\n\t\telse if (srt->sortop == typentry->gt_opr)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DESC\");\n\t\t\t/* DESC defaults to NULLS FIRST */\n\t\t\tif (!srt->nulls_first)\n\t\t\t\tappendStringInfoString(buf, \" NULLS LAST\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(buf, \" USING %s\",\n\t\t\t\t\t\t\t generate_operator_name(srt->sortop,\n\t\t\t\t\t\t\t\t\t\t\t\t\tsortcoltype,\n\t\t\t\t\t\t\t\t\t\t\t\t\tsortcoltype));\n\t\t\t/* be specific to eliminate ambiguity */\n\t\t\tif (srt->nulls_first)\n\t\t\t\tappendStringInfoString(buf, \" NULLS FIRST\");\n\t\t\telse\n\t\t\t\tappendStringInfoString(buf, \" NULLS LAST\");\n\t\t}\n\t\tsep = \", \";\n\t}\n}\n\n/*\n * Display a WINDOW clause.\n *\n * Note that the windowClause list might contain only anonymous window\n * specifications, in which case we should print nothing here.\n */\nstatic void\nget_rule_windowclause(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *sep;\n\tListCell   *l;\n\n\tsep = NULL;\n\tforeach(l, query->windowClause)\n\t{\n\t\tWindowClause *wc = (WindowClause *) lfirst(l);\n\n\t\tif (wc->name == NULL)\n\t\t\tcontinue;\t\t\t/* ignore anonymous windows */\n\n\t\tif (sep == NULL)\n\t\t\tappendContextKeyword(context, \" WINDOW \",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\telse\n\t\t\tappendStringInfoString(buf, sep);\n\n\t\tappendStringInfo(buf, \"%s AS \", quote_identifier(wc->name));\n\n\t\tget_rule_windowspec(wc, query->targetList, context);\n\n\t\tsep = \", \";\n\t}\n}\n\n/*\n * Display a window definition\n */\nstatic void\nget_rule_windowspec(WindowClause *wc, List *targetList,\n\t\t\t\t\tdeparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tneedspace = false;\n\tconst char *sep;\n\tListCell   *l;\n\n\tappendStringInfoChar(buf, '(');\n\tif (wc->refname)\n\t{\n\t\tappendStringInfoString(buf, quote_identifier(wc->refname));\n\t\tneedspace = true;\n\t}\n\t/* partition clauses are always inherited, so only print if no refname */\n\tif (wc->partitionClause && !wc->refname)\n\t{\n\t\tif (needspace)\n\t\t\tappendStringInfoChar(buf, ' ');\n\t\tappendStringInfoString(buf, \"PARTITION BY \");\n\t\tsep = \"\";\n\t\tforeach(l, wc->partitionClause)\n\t\t{\n\t\t\tSortGroupClause *grp = (SortGroupClause *) lfirst(l);\n\n\t\t\tappendStringInfoString(buf, sep);\n\t\t\tget_rule_sortgroupclause(grp->tleSortGroupRef, targetList,\n\t\t\t\t\t\t\t\t\t false, context);\n\t\t\tsep = \", \";\n\t\t}\n\t\tneedspace = true;\n\t}\n\t/* print ordering clause only if not inherited */\n\tif (wc->orderClause && !wc->copiedOrder)\n\t{\n\t\tif (needspace)\n\t\t\tappendStringInfoChar(buf, ' ');\n\t\tappendStringInfoString(buf, \"ORDER BY \");\n\t\tget_rule_orderby(wc->orderClause, targetList, false, context);\n\t\tneedspace = true;\n\t}\n\t/* framing clause is never inherited, so print unless it's default */\n\tif (wc->frameOptions & FRAMEOPTION_NONDEFAULT)\n\t{\n\t\tif (needspace)\n\t\t\tappendStringInfoChar(buf, ' ');\n\t\tget_window_frame_options(wc->frameOptions,\n\t\t\t\t\t\t\t\t wc->startOffset, wc->endOffset,\n\t\t\t\t\t\t\t\t context);\n\t}\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * Append the description of a window's framing options to context->buf\n */\nstatic void\nget_window_frame_options(int frameOptions,\n\t\t\t\t\t\t Node *startOffset, Node *endOffset,\n\t\t\t\t\t\t deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (frameOptions & FRAMEOPTION_NONDEFAULT)\n\t{\n\t\tif (frameOptions & FRAMEOPTION_RANGE)\n\t\t\tappendStringInfoString(buf, \"RANGE \");\n\t\telse if (frameOptions & FRAMEOPTION_ROWS)\n\t\t\tappendStringInfoString(buf, \"ROWS \");\n\t\telse if (frameOptions & FRAMEOPTION_GROUPS)\n\t\t\tappendStringInfoString(buf, \"GROUPS \");\n\t\telse\n\t\t\tAssert(false);\n\t\tif (frameOptions & FRAMEOPTION_BETWEEN)\n\t\t\tappendStringInfoString(buf, \"BETWEEN \");\n\t\tif (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)\n\t\t\tappendStringInfoString(buf, \"UNBOUNDED PRECEDING \");\n\t\telse if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)\n\t\t\tappendStringInfoString(buf, \"CURRENT ROW \");\n\t\telse if (frameOptions & FRAMEOPTION_START_OFFSET)\n\t\t{\n\t\t\tget_rule_expr(startOffset, context, false);\n\t\t\tif (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)\n\t\t\t\tappendStringInfoString(buf, \" PRECEDING \");\n\t\t\telse if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)\n\t\t\t\tappendStringInfoString(buf, \" FOLLOWING \");\n\t\t\telse\n\t\t\t\tAssert(false);\n\t\t}\n\t\telse\n\t\t\tAssert(false);\n\t\tif (frameOptions & FRAMEOPTION_BETWEEN)\n\t\t{\n\t\t\tappendStringInfoString(buf, \"AND \");\n\t\t\tif (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)\n\t\t\t\tappendStringInfoString(buf, \"UNBOUNDED FOLLOWING \");\n\t\t\telse if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)\n\t\t\t\tappendStringInfoString(buf, \"CURRENT ROW \");\n\t\t\telse if (frameOptions & FRAMEOPTION_END_OFFSET)\n\t\t\t{\n\t\t\t\tget_rule_expr(endOffset, context, false);\n\t\t\t\tif (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)\n\t\t\t\t\tappendStringInfoString(buf, \" PRECEDING \");\n\t\t\t\telse if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)\n\t\t\t\t\tappendStringInfoString(buf, \" FOLLOWING \");\n\t\t\t\telse\n\t\t\t\t\tAssert(false);\n\t\t\t}\n\t\t\telse\n\t\t\t\tAssert(false);\n\t\t}\n\t\tif (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)\n\t\t\tappendStringInfoString(buf, \"EXCLUDE CURRENT ROW \");\n\t\telse if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP)\n\t\t\tappendStringInfoString(buf, \"EXCLUDE GROUP \");\n\t\telse if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)\n\t\t\tappendStringInfoString(buf, \"EXCLUDE TIES \");\n\t\t/* we will now have a trailing space; remove it */\n\t\tbuf->data[--(buf->len)] = '\\0';\n\t}\n}\n\n/* ----------\n * get_insert_query_def\t\t\t- Parse back an INSERT parsetree\n * ----------\n */\nstatic void\nget_insert_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *select_rte = NULL;\n\tRangeTblEntry *values_rte = NULL;\n\tRangeTblEntry *rte;\n\tListCell   *l;\n\tList\t   *strippedexprs = NIL;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * If it's an INSERT ... SELECT or multi-row VALUES, there will be a\n\t * single RTE for the SELECT or VALUES.  Plain VALUES has neither.\n\t */\n\tforeach(l, query->rtable)\n\t{\n\t\trte = (RangeTblEntry *) lfirst(l);\n\n\t\tif (rte->rtekind == RTE_SUBQUERY)\n\t\t{\n\t\t\tif (select_rte)\n\t\t\t\telog(ERROR, \"too many subquery RTEs in INSERT\");\n\t\t\tselect_rte = rte;\n\t\t}\n\n\t\tif (rte->rtekind == RTE_VALUES)\n\t\t{\n\t\t\tif (values_rte)\n\t\t\t\telog(ERROR, \"too many values RTEs in INSERT\");\n\t\t\tvalues_rte = rte;\n\t\t}\n\t}\n\tif (select_rte && values_rte)\n\t\telog(ERROR, \"both subquery and values RTEs in INSERT\");\n\n\t/*\n\t * Start the query with INSERT INTO relname\n\t */\n\trte = rt_fetch(query->resultRelation, query->rtable);\n\tAssert(rte->rtekind == RTE_RELATION);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t\tappendStringInfoChar(buf, ' ');\n\t}\n\tappendStringInfo(buf, \"INSERT INTO %s\",\n\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL));\n\n\t/* Print the relation alias, if needed; INSERT requires explicit AS */\n\tget_rte_alias(rte, query->resultRelation, true, context);\n\n\t/* always want a space here */\n\tappendStringInfoChar(buf, ' ');\n\n\t/*\n\t * Add the insert-column-names list.  Any indirection decoration needed on\n\t * the column names can be inferred from the top targetlist.\n\t */\n\tif (query->targetList)\n\t{\n\t\tstrippedexprs = get_insert_column_names_list(query->targetList,\n\t\t\t\t\t\t\t\tbuf, context, rte);\n\t}\n\n\tif (query->override)\n\t{\n\t\tif (query->override == OVERRIDING_SYSTEM_VALUE)\n\t\t\tappendStringInfoString(buf, \"OVERRIDING SYSTEM VALUE \");\n\t\telse if (query->override == OVERRIDING_USER_VALUE)\n\t\t\tappendStringInfoString(buf, \"OVERRIDING USER VALUE \");\n\t}\n\n\tif (select_rte)\n\t{\n\t\t/* Add the SELECT */\n\t\tget_query_def(select_rte->subquery, buf, context->namespaces, NULL,\n\t\t              false,\n\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t  context->indentLevel);\n\t}\n\telse if (values_rte)\n\t{\n\t\t/* Add the multi-VALUES expression lists */\n\t\tget_values_def(values_rte->values_lists, context);\n\t}\n\telse if (strippedexprs)\n\t{\n\t\t/* Add the single-VALUES expression list */\n\t\tappendContextKeyword(context, \"VALUES (\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\t\tget_rule_list_toplevel(strippedexprs, context, false);\n\t\tappendStringInfoChar(buf, ')');\n\t}\n\telse\n\t{\n\t\t/* No expressions, so it must be DEFAULT VALUES */\n\t\tappendStringInfoString(buf, \"DEFAULT VALUES\");\n\t}\n\n\t/* Add ON CONFLICT if present */\n\tif (query->onConflict)\n\t{\n\t\tOnConflictExpr *confl = query->onConflict;\n\n\t\tappendStringInfoString(buf, \" ON CONFLICT\");\n\n\t\tif (confl->arbiterElems)\n\t\t{\n\t\t\t/* Add the single-VALUES expression list */\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr((Node *) confl->arbiterElems, context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t/* Add a WHERE clause (for partial indexes) if given */\n\t\t\tif (confl->arbiterWhere != NULL)\n\t\t\t{\n\t\t\t\tbool\t\tsave_varprefix;\n\n\t\t\t\t/*\n\t\t\t\t * Force non-prefixing of Vars, since parser assumes that they\n\t\t\t\t * belong to target relation.  WHERE clause does not use\n\t\t\t\t * InferenceElem, so this is separately required.\n\t\t\t\t */\n\t\t\t\tsave_varprefix = context->varprefix;\n\t\t\t\tcontext->varprefix = false;\n\n\t\t\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\t\t\tget_rule_expr(confl->arbiterWhere, context, false);\n\n\t\t\t\tcontext->varprefix = save_varprefix;\n\t\t\t}\n\t\t}\n\t\telse if (OidIsValid(confl->constraint))\n\t\t{\n\t\t\tchar\t   *constraint = get_constraint_name(confl->constraint);\n\t\t\tint64 shardId = context->shardid;\n\n\t\t\tif (shardId > 0)\n\t\t\t{\n\t\t\t\tAppendShardIdToName(&constraint, shardId);\n\t\t\t}\n\n\t\t\tif (!constraint)\n\t\t\t\telog(ERROR, \"cache lookup failed for constraint %u\",\n\t\t\t\t\t confl->constraint);\n\t\t\tappendStringInfo(buf, \" ON CONSTRAINT %s\",\n\t\t\t\t\t\t\t quote_identifier(constraint));\n\t\t}\n\n\t\tif (confl->action == ONCONFLICT_NOTHING)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DO NOTHING\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfoString(buf, \" DO UPDATE SET \");\n\t\t\t/* Deparse targetlist */\n\t\t\tget_update_query_targetlist_def(query, confl->onConflictSet,\n\t\t\t\t\t\t\t\t\t\t\tcontext, rte);\n\n\t\t\t/* Add a WHERE clause if given */\n\t\t\tif (confl->onConflictWhere != NULL)\n\t\t\t{\n\t\t\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\t\t\tget_rule_expr(confl->onConflictWhere, context, false);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Add RETURNING if present */\n\tif (query->returningList)\n\t{\n\t\tget_returning_clause(query, context);\n\t}\n}\n\n/* ----------\n * get_update_query_def\t\t\t- Parse back an UPDATE parsetree\n * ----------\n */\nstatic void\nget_update_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * Start the query with UPDATE relname SET\n\t */\n\trte = rt_fetch(query->resultRelation, query->rtable);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tappendStringInfoChar(buf, ' ');\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t}\n\n\t/* if it's a shard, do differently */\n\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\tchar *fragmentSchemaName = NULL;\n\t\tchar *fragmentTableName = NULL;\n\n\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t/* use schema and table name from the remote alias */\n\t\tappendStringInfo(buf, \"UPDATE %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName, fragmentTableName));\n\n\t\tif(rte->eref != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\tquote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\telse\n\t{\n\t\tappendStringInfo(buf, \"UPDATE %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL));\n\n\t\t/* Print the relation alias, if needed */\n\t\tget_rte_alias(rte, query->resultRelation, false, context);\n\t}\n\n\tappendStringInfoString(buf, \" SET \");\n\n\t/* Deparse targetlist */\n\tget_update_query_targetlist_def(query, query->targetList, context, rte);\n\n\t/* Add the FROM clause if needed */\n\tget_from_clause(query, \" FROM \", context);\n\n\t/* Add a WHERE clause if given */\n\tif (query->jointree->quals != NULL)\n\t{\n\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_expr(query->jointree->quals, context, false);\n\t}\n\n\t/* Add RETURNING if present */\n\tif (query->returningList)\n\t{\n\t\tget_returning_clause(query, context);\n\t}\n}\n\n/* ----------\n * get_update_query_targetlist_def\t\t\t- Parse back an UPDATE targetlist\n * ----------\n */\nstatic void\nget_update_query_targetlist_def(Query *query, List *targetList,\n\t\t\t\t\t\t\t\tdeparse_context *context, RangeTblEntry *rte)\n{\n\tStringInfo\tbuf = context->buf;\n\tListCell   *l;\n\tListCell   *next_ma_cell;\n\tint\t\t\tremaining_ma_columns;\n\tconst char *sep;\n\tSubLink    *cur_ma_sublink;\n\tList\t   *ma_sublinks;\n\n\ttargetList = ExpandMergedSubscriptingRefEntries(targetList);\n\n\t/*\n\t * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks\n\t * into a list.  We expect them to appear, in ID order, in resjunk tlist\n\t * entries.\n\t */\n\tma_sublinks = NIL;\n\tif (query->hasSubLinks)\t\t/* else there can't be any */\n\t{\n\t\tforeach(l, targetList)\n\t\t{\n\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\n\t\t\tif (tle->resjunk && IsA(tle->expr, SubLink))\n\t\t\t{\n\t\t\t\tSubLink    *sl = (SubLink *) tle->expr;\n\n\t\t\t\tif (sl->subLinkType == MULTIEXPR_SUBLINK)\n\t\t\t\t{\n\t\t\t\t\tma_sublinks = lappend(ma_sublinks, sl);\n\t\t\t\t\tAssert(sl->subLinkId == list_length(ma_sublinks));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tensure_update_targetlist_in_param_order(targetList);\n\t}\n\tnext_ma_cell = list_head(ma_sublinks);\n\tcur_ma_sublink = NULL;\n\tremaining_ma_columns = 0;\n\n\t/* Add the comma separated list of 'attname = value' */\n\tsep = \"\";\n\tforeach(l, targetList)\n\t{\n\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\t\tNode\t   *expr;\n\n\t\tif (tle->resjunk)\n\t\t\tcontinue;\t\t\t/* ignore junk entries */\n\n\t\t/* Emit separator (OK whether we're in multiassignment or not) */\n\t\tappendStringInfoString(buf, sep);\n\t\tsep = \", \";\n\n\t\t/*\n\t\t * Check to see if we're starting a multiassignment group: if so,\n\t\t * output a left paren.\n\t\t */\n\t\tif (next_ma_cell != NULL && cur_ma_sublink == NULL)\n\t\t{\n\t\t\t/*\n\t\t\t * We must dig down into the expr to see if it's a PARAM_MULTIEXPR\n\t\t\t * Param.  That could be buried under FieldStores and\n\t\t\t * SubscriptingRefs and CoerceToDomains (cf processIndirection()),\n\t\t\t * and underneath those there could be an implicit type coercion.\n\t\t\t * Because we would ignore implicit type coercions anyway, we\n\t\t\t * don't need to be as careful as processIndirection() is about\n\t\t\t * descending past implicit CoerceToDomains.\n\t\t\t */\n\t\t\texpr = (Node *) tle->expr;\n\t\t\twhile (expr)\n\t\t\t{\n\t\t\t\tif (IsA(expr, FieldStore))\n\t\t\t\t{\n\t\t\t\t\tFieldStore *fstore = (FieldStore *) expr;\n\n\t\t\t\t\texpr = (Node *) linitial(fstore->newvals);\n\t\t\t\t}\n\t\t\t\telse if (IsA(expr, SubscriptingRef))\n\t\t\t\t{\n\t\t\t\t\tSubscriptingRef *sbsref = (SubscriptingRef *) expr;\n\n\t\t\t\t\tif (sbsref->refassgnexpr == NULL)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\texpr = (Node *) sbsref->refassgnexpr;\n\t\t\t\t}\n\t\t\t\telse if (IsA(expr, CoerceToDomain))\n\t\t\t\t{\n\t\t\t\t\tCoerceToDomain *cdomain = (CoerceToDomain *) expr;\n\n\t\t\t\t\tif (cdomain->coercionformat != COERCE_IMPLICIT_CAST)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\texpr = (Node *) cdomain->arg;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\texpr = strip_implicit_coercions(expr);\n\n\t\t\tif (expr && IsA(expr, Param) &&\n\t\t\t\t((Param *) expr)->paramkind == PARAM_MULTIEXPR)\n\t\t\t{\n\t\t\t\tcur_ma_sublink = (SubLink *) lfirst(next_ma_cell);\n\t\t\t\tnext_ma_cell = lnext(ma_sublinks, next_ma_cell);\n\t\t\t\tremaining_ma_columns = count_nonjunk_tlist_entries(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ((Query *) cur_ma_sublink->subselect)->targetList);\n\t\t\t\tAssert(((Param *) expr)->paramid ==\n\t\t\t\t\t   ((cur_ma_sublink->subLinkId << 16) | 1));\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Put out name of target column; look in the catalogs, not at\n\t\t * tle->resname, since resname will fail to track RENAME.\n\t\t */\n\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t   quote_identifier(get_attname(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttle->resno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse)));\n\n\t\t/*\n\t\t * Print any indirection needed (subfields or subscripts), and strip\n\t\t * off the top-level nodes representing the indirection assignments.\n\t\t */\n\t\texpr = processIndirection((Node *) tle->expr, context);\n\n\t\t/*\n\t\t * If we're in a multiassignment, skip printing anything more, unless\n\t\t * this is the last column; in which case, what we print should be the\n\t\t * sublink, not the Param.\n\t\t */\n\t\tif (cur_ma_sublink != NULL)\n\t\t{\n\t\t\tif (--remaining_ma_columns > 0)\n\t\t\t\tcontinue;\t\t/* not the last column of multiassignment */\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\texpr = (Node *) cur_ma_sublink;\n\t\t\tcur_ma_sublink = NULL;\n\t\t}\n\n\t\tappendStringInfoString(buf, \" = \");\n\n\t\tget_rule_expr(expr, context, false);\n\t}\n}\n\n/* ----------\n * get_delete_query_def\t\t\t- Parse back a DELETE parsetree\n * ----------\n */\nstatic void\nget_delete_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * Start the query with DELETE FROM relname\n\t */\n\trte = rt_fetch(query->resultRelation, query->rtable);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tappendStringInfoChar(buf, ' ');\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t}\n\n\t/* if it's a shard, do differently */\n\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\tchar *fragmentSchemaName = NULL;\n\t\tchar *fragmentTableName = NULL;\n\n\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t/* use schema and table name from the remote alias */\n\t\tappendStringInfo(buf, \"DELETE FROM %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName, fragmentTableName));\n\n\t\tif(rte->eref != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\tquote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\telse\n\t{\n\t\tappendStringInfo(buf, \"DELETE FROM %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL));\n\n\t\t/* Print the relation alias, if needed */\n\t\tget_rte_alias(rte, query->resultRelation, false, context);\n\t}\n\n\t/* Add the USING clause if given */\n\tget_from_clause(query, \" USING \", context);\n\n\t/* Add a WHERE clause if given */\n\tif (query->jointree->quals != NULL)\n\t{\n\t\tappendContextKeyword(context, \" WHERE \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_rule_expr(query->jointree->quals, context, false);\n\t}\n\n\t/* Add RETURNING if present */\n\tif (query->returningList)\n\t{\n\t\tget_returning_clause(query, context);\n\t}\n}\n\n\n/* ----------\n * get_merge_query_def\t\t\t\t- Parse back a MERGE parsetree\n * ----------\n */\nstatic void\nget_merge_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\tListCell   *lc;\n\tbool\thaveNotMatchedBySource;\n\n\t/* Insert the WITH clause if given */\n\tget_with_clause(query, context);\n\n\t/*\n\t * Start the query with MERGE INTO relname\n\t */\n\trte = ExtractResultRelationRTE(query);\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tappendStringInfoChar(buf, ' ');\n\t\tcontext->indentLevel += PRETTYINDENT_STD;\n\t}\n\n\t/* if it's a shard, do differently */\n\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\tchar *fragmentSchemaName = NULL;\n\t\tchar *fragmentTableName = NULL;\n\n\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t/* use schema and table name from the remote alias */\n\t\tappendStringInfo(buf, \"MERGE INTO %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName, fragmentTableName));\n\n\t\tif(rte->eref != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\tquote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\telse\n\t{\n\t\tappendStringInfo(buf, \"MERGE INTO %s%s\",\n\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\tcontext->distrelid,\n\t\t\t\t\t\t\t\t\t\tcontext->shardid, NIL));\n\n\t\tif (rte->alias != NULL)\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\t\t\t quote_identifier(get_rtable_name(query->resultRelation, context)));\n\t}\n\n\t/* Print the source relation and join clause */\n\tget_from_clause(query, \" USING \", context);\n\tappendContextKeyword(context, \" ON \",\n\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\tget_rule_expr(query->mergeJoinCondition, context, false);\n\n\t/*\n\t * Test for any NOT MATCHED BY SOURCE actions.  If there are none, then\n\t * any NOT MATCHED BY TARGET actions are output as \"WHEN NOT MATCHED\", per\n\t * SQL standard.  Otherwise, we have a non-SQL-standard query, so output\n\t * \"BY SOURCE\" / \"BY TARGET\" qualifiers for all NOT MATCHED actions, to be\n\t * more explicit.\n\t */\n\thaveNotMatchedBySource = false;\n\tforeach(lc, query->mergeActionList)\n\t{\n\t\tMergeAction *action = lfirst_node(MergeAction, lc);\n\n\t\tif (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)\n\t\t{\n\t\t\thaveNotMatchedBySource = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* Print each merge action */\n\tforeach(lc, query->mergeActionList)\n\t{\n\t\tMergeAction *action = lfirst_node(MergeAction, lc);\n\n\t\tappendContextKeyword(context, \" WHEN \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\t\tswitch (action->matchKind)\n\t\t{\n\t\t\tcase MERGE_WHEN_MATCHED:\n\t\t\t\tappendStringInfoString(buf, \"MATCHED\");\n\t\t\t\tbreak;\n\t\t\tcase MERGE_WHEN_NOT_MATCHED_BY_SOURCE:\n\t\t\t\tappendStringInfoString(buf, \"NOT MATCHED BY SOURCE\");\n\t\t\t\tbreak;\n\t\t\tcase MERGE_WHEN_NOT_MATCHED_BY_TARGET:\n\t\t\t\tif (haveNotMatchedBySource)\n\t\t\t\t\tappendStringInfoString(buf, \"NOT MATCHED BY TARGET\");\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \"NOT MATCHED\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\telog(ERROR, \"unrecognized matchKind: %d\",\n\t\t\t\t\t (int) action->matchKind);\n\t\t}\n\n\t\tif (action->qual)\n\t\t{\n\t\t\tappendContextKeyword(context, \" AND \",\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);\n\t\t\tget_rule_expr(action->qual, context, false);\n\t\t}\n\t\tappendContextKeyword(context, \" THEN \",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);\n\n\t\tif (action->commandType == CMD_INSERT)\n\t\t{\n\t\t\t/* This generally matches get_insert_query_def() */\n\t\t\tList\t   *strippedexprs = NIL;\n\t\t\tconst char *sep = \"\";\n\t\t\tListCell   *lc2;\n\n\t\t\tappendStringInfoString(buf, \"INSERT\");\n\n\t\t\tif (action->targetList)\n\t\t\t\tappendStringInfoString(buf, \" (\");\n\t\t\tforeach(lc2, action->targetList)\n\t\t\t{\n\t\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(lc2);\n\n\t\t\t\tAssert(!tle->resjunk);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tsep = \", \";\n\n\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t   quote_identifier(get_attname(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttle->resno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse)));\n\t\t\t\tstrippedexprs = lappend(strippedexprs,\n\t\t\t\t\t\t\t\t\t\tprocessIndirection((Node *) tle->expr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   context));\n\t\t\t}\n\t\t\tif (action->targetList)\n\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\tif (action->override)\n\t\t\t{\n\t\t\t\tif (action->override == OVERRIDING_SYSTEM_VALUE)\n\t\t\t\t\tappendStringInfoString(buf, \" OVERRIDING SYSTEM VALUE\");\n\t\t\t\telse if (action->override == OVERRIDING_USER_VALUE)\n\t\t\t\t\tappendStringInfoString(buf, \" OVERRIDING USER VALUE\");\n\t\t\t}\n\n\t\t\tif (strippedexprs)\n\t\t\t{\n\t\t\t\tappendContextKeyword(context, \" VALUES (\",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 4);\n\t\t\t\tget_rule_list_toplevel(strippedexprs, context, false);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\telse\n\t\t\t\tappendStringInfoString(buf, \" DEFAULT VALUES\");\n\t\t}\n\t\telse if (action->commandType == CMD_UPDATE)\n\t\t{\n\t\t\tappendStringInfoString(buf, \"UPDATE SET \");\n\t\t\tget_update_query_targetlist_def(query, action->targetList,\n\t\t\t\t\t\t\t\t\t\t\tcontext, rte);\n\t\t}\n\t\telse if (action->commandType == CMD_DELETE)\n\t\t\tappendStringInfoString(buf, \"DELETE\");\n\t\telse if (action->commandType == CMD_NOTHING)\n\t\t\tappendStringInfoString(buf, \"DO NOTHING\");\n\t}\n\n\t/* Add RETURNING if present */\n\tif (query->returningList)\n\t{\n\t\tappendContextKeyword(context, \" RETURNING\",\n\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);\n\t\tget_target_list(query->returningList, context);\n\t}\n\n\tereport(DEBUG1, (errmsg(\"<Deparsed MERGE query: %s>\", buf->data)));\n}\n\n\n/* ----------\n * get_utility_query_def\t\t\t- Parse back a UTILITY parsetree\n * ----------\n */\nstatic void\nget_utility_query_def(Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))\n\t{\n\t\tNotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;\n\n\t\tappendContextKeyword(context, \"\",\n\t\t\t\t\t\t\t 0, PRETTYINDENT_STD, 1);\n\t\tappendStringInfo(buf, \"NOTIFY %s\",\n\t\t\t\t\t\t quote_identifier(stmt->conditionname));\n\t\tif (stmt->payload)\n\t\t{\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tsimple_quote_literal(buf, stmt->payload);\n\t\t}\n\t}\n\telse if (query->utilityStmt && IsA(query->utilityStmt, TruncateStmt))\n\t{\n\t\tTruncateStmt *stmt = (TruncateStmt *) query->utilityStmt;\n\t\tList *relationList = stmt->relations;\n\t\tListCell *relationCell = NULL;\n\n\t\tappendContextKeyword(context, \"\",\n\t\t\t\t\t\t\t 0, PRETTYINDENT_STD, 1);\n\n\t\tappendStringInfo(buf, \"TRUNCATE TABLE\");\n\n\t\tforeach(relationCell, relationList)\n\t\t{\n\t\t\tRangeVar *relationVar = (RangeVar *) lfirst(relationCell);\n\t\t\tOid relationId = RangeVarGetRelid(relationVar, NoLock, false);\n\t\t\tchar *relationName = generate_relation_or_shard_name(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid, NIL);\n\t\t\tappendStringInfo(buf, \" %s\", relationName);\n\n\t\t\tif (lnext(relationList, relationCell) != NULL)\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \",\");\n\t\t\t}\n\t\t}\n\n\t\tif (stmt->restart_seqs)\n\t\t{\n\t\t\tappendStringInfo(buf, \" RESTART IDENTITY\");\n\t\t}\n\n\t\tif (stmt->behavior == DROP_CASCADE)\n\t\t{\n\t\t\tappendStringInfo(buf, \" CASCADE\");\n\t\t}\n\t}\n\telse\n\t{\n\t\t/* Currently only NOTIFY utility commands can appear in rules */\n\t\telog(ERROR, \"unexpected utility statement type\");\n\t}\n}\n\n/*\n * Display a Var appropriately.\n *\n * In some cases (currently only when recursing into an unnamed join)\n * the Var's varlevelsup has to be interpreted with respect to a context\n * above the current one; levelsup indicates the offset.\n *\n * If istoplevel is true, the Var is at the top level of a SELECT's\n * targetlist, which means we need special treatment of whole-row Vars.\n * Instead of the normal \"tab.*\", we'll print \"tab.*::typename\", which is a\n * dirty hack to prevent \"tab.*\" from being expanded into multiple columns.\n * (The parser will strip the useless coercion, so no inefficiency is added in\n * dump and reload.)  We used to print just \"tab\" in such cases, but that is\n * ambiguous and will yield the wrong result if \"tab\" is also a plain column\n * name in the query.\n *\n * Returns the attname of the Var, or NULL if the Var has no attname (because\n * it is a whole-row Var or a subplan output reference).\n */\nstatic char *\nget_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tRangeTblEntry *rte;\n\tAttrNumber\tattnum;\n\tint\t\tvarno;\n\tAttrNumber\tvarattno;\n\tint\t\t\tnetlevelsup;\n\tdeparse_namespace *dpns;\n\tdeparse_columns *colinfo;\n\tchar\t   *refname;\n\tchar\t   *attname;\n\tbool\t\tneed_prefix;\n\n\t/* Find appropriate nesting depth */\n\tnetlevelsup = var->varlevelsup + levelsup;\n\tif (netlevelsup >= list_length(context->namespaces))\n\t\telog(ERROR, \"bogus varlevelsup: %d offset %d\",\n\t\t\t var->varlevelsup, levelsup);\n\tdpns = (deparse_namespace *) list_nth(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t  netlevelsup);\n\n\tvarno = var->varno;\n\tvarattno = var->varattno;\n\n\n\tif (var->varnosyn > 0 && var->varnosyn <= list_length(dpns->rtable) && dpns->plan == NULL) {\n\t\trte = rt_fetch(var->varnosyn, dpns->rtable);\n\n\t\t/*\n\t\t * if the rte var->varnosyn points to is not a regular table and it is a join\n\t\t * then the correct relname will be found with var->varnosyn and var->varattnosyn\n\t\t */\n\t\tif (rte->rtekind == RTE_JOIN && rte->relid == 0 && var->varnosyn != var->varno) {\n\t\t\tvarno = var->varnosyn;\n\t\t\tvarattno = var->varattnosyn;\n\t\t}\n\t}\n\n\t/*\n\t * Try to find the relevant RTE in this rtable.  In a plan tree, it's\n\t * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig\n\t * down into the subplans, or INDEX_VAR, which is resolved similarly. Also\n\t * find the aliases previously assigned for this RTE.\n\t */\n\tif (varno >= 1 && varno <= list_length(dpns->rtable))\n\t{\n\n\t\t/*\n\t\t * We might have been asked to map child Vars to some parent relation.\n\t\t */\n\t\tif (context->appendparents && dpns->appendrels)\n\t\t{\n\n\t\t\tint\t\tpvarno = varno;\n\t\t\tAttrNumber\tpvarattno = varattno;\n\t\t\tAppendRelInfo *appinfo = dpns->appendrels[pvarno];\n\t\t\tbool\t\tfound = false;\n\n\t\t\t/* Only map up to inheritance parents, not UNION ALL appendrels */\n\t\t\twhile (appinfo &&\n\t\t\t\t   rt_fetch(appinfo->parent_relid,\n\t\t\t\t\t\t\tdpns->rtable)->rtekind == RTE_RELATION)\n\t\t\t{\n\t\t\t\tfound = false;\n\t\t\t\tif (pvarattno > 0)\t/* system columns stay as-is */\n\t\t\t\t{\n\t\t\t\t\tif (pvarattno > appinfo->num_child_cols)\n\t\t\t\t\t\tbreak;\t/* safety check */\n\t\t\t\t\tpvarattno = appinfo->parent_colnos[pvarattno - 1];\n\t\t\t\t\tif (pvarattno == 0)\n\t\t\t\t\t\tbreak;\t/* Var is local to child */\n\t\t\t\t}\n\n\t\t\t\tpvarno = appinfo->parent_relid;\n\t\t\t\tfound = true;\n\n\t\t\t\t/* If the parent is itself a child, continue up. */\n\t\t\t\tAssert(pvarno > 0 && pvarno <= list_length(dpns->rtable));\n\t\t\t\tappinfo = dpns->appendrels[pvarno];\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If we found an ancestral rel, and that rel is included in\n\t\t\t * appendparents, print that column not the original one.\n\t\t\t */\n\t\t\tif (found && bms_is_member(pvarno, context->appendparents))\n\t\t\t{\n\t\t\t\tvarno = pvarno;\n\t\t\t\tvarattno = pvarattno;\n\t\t\t}\n\t\t}\n\n\t\trte = rt_fetch(varno, dpns->rtable);\n\n\t\t/* might be returning old/new column value */\n\t\tif (var->varreturningtype == VAR_RETURNING_OLD)\n\t\t\trefname = dpns->ret_old_alias;\n\t\telse if (var->varreturningtype == VAR_RETURNING_NEW)\n\t\t\trefname = dpns->ret_new_alias;\n\t\telse\n\t\t\trefname = (char *) list_nth(dpns->rtable_names, varno - 1);\n\n\t\tcolinfo = deparse_columns_fetch(varno, dpns);\n\t\tattnum = varattno;\n\t}\n\telse\n\t{\n\t\tresolve_special_varno((Node *) var, context, get_special_variable,\n\t\t\t\t\t\t\t  NULL);\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * The planner will sometimes emit Vars referencing resjunk elements of a\n\t * subquery's target list (this is currently only possible if it chooses\n\t * to generate a \"physical tlist\" for a SubqueryScan or CteScan node).\n\t * Although we prefer to print subquery-referencing Vars using the\n\t * subquery's alias, that's not possible for resjunk items since they have\n\t * no alias.  So in that case, drill down to the subplan and print the\n\t * contents of the referenced tlist item.  This works because in a plan\n\t * tree, such Vars can only occur in a SubqueryScan or CteScan node, and\n\t * we'll have set dpns->inner_plan to reference the child plan node.\n\t */\n\tif ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&\n\t\tattnum > list_length(rte->eref->colnames) &&\n\t\tdpns->inner_plan)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\n\t\ttle = get_tle_by_resno(dpns->inner_tlist, attnum);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"invalid attnum %d for relation \\\"%s\\\"\",\n\t\t\t\t attnum, rte->eref->aliasname);\n\n\t\tAssert(netlevelsup == 0);\n\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\t/*\n\t\t * Force parentheses because our caller probably assumed a Var is a\n\t\t * simple expression.\n\t\t */\n\t\tif (!IsA(tle->expr, Var))\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_rule_expr((Node *) tle->expr, context, true);\n\t\tif (!IsA(tle->expr, Var))\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * If it's an unnamed join, look at the expansion of the alias variable.\n\t * If it's a simple reference to one of the input vars, then recursively\n\t * print the name of that var instead.  When it's not a simple reference,\n\t * we have to just print the unqualified join column name.  (This can only\n\t * happen with \"dangerous\" merged columns in a JOIN USING; we took pains\n\t * previously to make the unqualified column name unique in such cases.)\n\t *\n\t * This wouldn't work in decompiling plan trees, because we don't store\n\t * joinaliasvars lists after planning; but a plan tree should never\n\t * contain a join alias variable.\n\t */\n\tif (rte->rtekind == RTE_JOIN && rte->alias == NULL)\n\t{\n\t\tif (rte->joinaliasvars == NIL)\n\t\t\telog(ERROR, \"cannot decompile join alias var in plan tree\");\n\t\tif (attnum > 0)\n\t\t{\n\t\t\tVar\t\t   *aliasvar;\n\n\t\t\taliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);\n\t\t\t/* we intentionally don't strip implicit coercions here */\n\t\t\tif (aliasvar && IsA(aliasvar, Var))\n\t\t\t{\n\t\t\t\treturn get_variable(aliasvar, var->varlevelsup + levelsup,\n\t\t\t\t\t\t\t\t\tistoplevel, context);\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Unnamed join has no refname.  (Note: since it's unnamed, there is\n\t\t * no way the user could have referenced it to create a whole-row Var\n\t\t * for it.  So we don't have to cover that case below.)\n\t\t */\n\t\tAssert(refname == NULL);\n\t}\n\n\tif (attnum == InvalidAttrNumber)\n\t\tattname = NULL;\n\telse if (attnum > 0)\n\t{\n\t\t/* Get column name to use from the colinfo struct */\n\t\tif (attnum > colinfo->num_cols)\n\t\t\telog(ERROR, \"invalid attnum %d for relation \\\"%s\\\"\",\n\t\t\t\t attnum, rte->eref->aliasname);\n\t\tattname = colinfo->colnames[attnum - 1];\n\n\t\t/*\n\t\t * If we find a Var referencing a dropped column, it seems better to\n\t\t * print something (anything) than to fail.  In general this should\n\t\t * not happen, but it used to be possible for some cases involving\n\t\t * functions returning named composite types, and perhaps there are\n\t\t * still bugs out there.\n\t\t */\n\t\tif (attname == NULL)\n\t\t\tattname = \"?dropped?column?\";\n\t}\n\telse if (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t{\n\t\t/* System column on a Citus shard */\n\t\tattname = get_attname(rte->relid, attnum, false);\n\t}\n\telse\n\t{\n\t\t/* System column - name is fixed, get it from the catalog */\n\t\tattname = get_rte_attribute_name(rte, attnum);\n\t}\n\n\tneed_prefix = (context->varprefix || attname == NULL ||\n\t\t\t\t   var->varreturningtype != VAR_RETURNING_DEFAULT);\n\t/*\n\t * If we're considering a plain Var in an ORDER BY (but not GROUP BY)\n\t * clause, we may need to add a table-name prefix to prevent\n\t * findTargetlistEntrySQL92 from misinterpreting the name as an\n\t * output-column name.  To avoid cluttering the output with unnecessary\n\t * prefixes, do so only if there is a name match to a SELECT tlist item\n\t * that is different from the Var.\n\t */\n\tif (context->varInOrderBy && !context->inGroupBy && !need_prefix)\n\t{\n\t\tint\t\t\tcolno = 0;\n\t\tforeach_node(TargetEntry, tle, context->targetList)\n\t\t{\n\t\t\tchar\t   *colname;\n\t\t\tif (tle->resjunk)\n\t\t\t\tcontinue;\t\t/* ignore junk entries */\n\t\t\tcolno++;\n\t\t\t/* This must match colname-choosing logic in get_target_list() */\n\t\t\tif (context->resultDesc && colno <= context->resultDesc->natts)\n\t\t\t\tcolname = NameStr(TupleDescAttr(context->resultDesc,\n\t\t\t\t\t\t\t\t\t\t\t\tcolno - 1)->attname);\n\t\t\telse\n\t\t\t\tcolname = tle->resname;\n\t\t\tif (colname && strcmp(colname, attname) == 0 &&\n\t\t\t\t!equal(var, tle->expr))\n\t\t\t{\n\t\t\t\tneed_prefix = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (refname && need_prefix)\n\t{\n\t\tappendStringInfoString(buf, quote_identifier(refname));\n\t\tappendStringInfoChar(buf, '.');\n\t}\n\tif (attname)\n\t\tappendStringInfoString(buf, quote_identifier(attname));\n\telse\n\t{\n\t\tappendStringInfoChar(buf, '*');\n\n\t\tif (istoplevel)\n\t\t{\n\t\t\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t\t\t{\n\t\t\t\t/* use rel.*::shard_name instead of rel.*::table_name */\n\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t generate_rte_shard_name(rte));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t format_type_with_typemod(var->vartype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  var->vartypmod));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn attname;\n}\n\n\n/* Any named join with joinaliasvars hides its inner aliases. */\nstatic inline bool\ndpns_has_named_join(const deparse_namespace *dpns)\n{\n    if (!dpns || dpns->rtable == NIL)\n        return false;\n\n    ListCell *lc;\n    foreach (lc, dpns->rtable)\n    {\n        RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n        if (rte && rte->rtekind == RTE_JOIN &&\n            rte->alias != NULL && rte->joinaliasvars != NIL)\n            return true;\n    }\n    return false;\n}\n\n\n/* Unwrap trivial wrappers around a Var; return Var* or NULL. */\nstatic Var *\nunwrap_simple_var(Node *expr)\n{\n\tfor (;;)\n\t{\n\t\tif (expr == NULL)\n\t\t\treturn NULL;\n\t\tif (IsA(expr, Var))\n\t\t\treturn (Var *) expr;\n\t\tif (IsA(expr, RelabelType))\n\t\t{ expr = (Node *) ((RelabelType *) expr)->arg; continue; }\n\t\tif (IsA(expr, CoerceToDomain))\n\t\t{ expr = (Node *) ((CoerceToDomain *) expr)->arg; continue; }\n\t\tif (IsA(expr, CollateExpr))\n\t\t{ expr = (Node *) ((CollateExpr *) expr)->arg; continue; }\n\t\t/* Not a simple Var */\n\t\treturn NULL;\n\t}\n}\n\n\n/* Base identity (canonical/synonym) match against a wanted (varno,attno) pair. */\nstatic inline bool\nvar_matches_base(const Var *v, Index want_varno, AttrNumber want_attno)\n{\n    if (v->varlevelsup != 0)\n        return false;\n    if (v->varno == want_varno && v->varattno == want_attno)\n        return true;\n    if (v->varnosyn > 0 && v->varattnosyn > 0 &&\n        v->varnosyn == want_varno && v->varattnosyn == want_attno)\n        return true;\n    return false;\n}\n\n\n\n/* Mutate v in place: if v maps to a named JOIN's column, set varnosyn/varattnosyn.\n * Returns true iff SYN fields were set. Query deparse only (dpns->plan == NULL). */\nstatic void\nmap_var_through_join_alias(deparse_namespace *dpns, Var *v)\n{\n    if (!dpns ||  dpns->plan != NULL || !v ||\n\t\tv->varlevelsup != 0 || v->varattno <= 0)\n        return;\n\n    int rti = 0;\n    ListCell *lc;\n    foreach (lc, dpns->rtable)\n    {\n        rti++;\n        RangeTblEntry *jrte = (RangeTblEntry *) lfirst(lc);\n        if (!jrte || jrte->rtekind != RTE_JOIN ||\n            jrte->alias == NULL || jrte->joinaliasvars == NIL)\n            continue;\n\n        AttrNumber jattno = 0;\n        ListCell *vlc;\n        foreach (vlc, jrte->joinaliasvars)\n        {\n            jattno++;\n            Var *aliasVar = unwrap_simple_var((Node *) lfirst(vlc));\n            if (!aliasVar)\n                continue;\n\n            if (var_matches_base(aliasVar, v->varno, v->varattno))\n            {\n                v->varnosyn    = (Index) rti;\n                v->varattnosyn = jattno;\n                return;\n            }\n        }\n    }\n}\n\n\n/*\n * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR.  This\n * routine is actually a callback for get_special_varno, which handles finding\n * the correct TargetEntry.  We get the expression contained in that\n * TargetEntry and just need to deparse it, a job we can throw back on\n * get_rule_expr.\n */\nstatic void\nget_special_variable(Node *node, deparse_context *context, void *callback_arg)\n{\n\tStringInfo\tbuf = context->buf;\n\n\t/*\n\t * For a non-Var referent, force parentheses because our caller probably\n\t *  assumed a Var is a simple expression.\n\t */\n\tif (!IsA(node, Var))\n\t\tappendStringInfoChar(buf, '(');\n\tget_rule_expr(node, context, true);\n\tif (!IsA(node, Var))\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,\n * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,\n * invoke the callback provided.\n */\nstatic void\nresolve_special_varno(Node *node, deparse_context *context, rsv_callback callback, void *callback_arg)\n{\n\tVar\t\t   *var;\n\tdeparse_namespace *dpns;\n\n\t/* This function is recursive, so let's be paranoid. */\n\tcheck_stack_depth();\n\n\t/* If it's not a Var, invoke the callback. */\n\tif (!IsA(node, Var))\n\t{\n\t\t(*callback) (node, context, callback_arg);\n\t\treturn;\n\t}\n\n\t/* Find appropriate nesting depth */\n\tvar = (Var *) node;\n\tdpns = (deparse_namespace *) list_nth(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t  var->varlevelsup);\n\n\t/*\n\t * It's a special RTE, so recurse.\n\t */\n\tif (var->varno == OUTER_VAR && dpns->outer_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\t\tBitmapset  *save_appendparents;\n\n\t\ttle = get_tle_by_resno(dpns->outer_tlist, var->varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for OUTER_VAR var: %d\", var->varattno);\n\n\t\t/* If we're descending to the first child of an Append or MergeAppend,\n\t\t * update appendparents.  This will affect deparsing of all Vars\n\t\t * appearing within the eventually-resolved subexpression.\n\t\t */\n\t\tsave_appendparents = context->appendparents;\n\n\t\tif (IsA(dpns->plan, Append))\n\t\t\tcontext->appendparents = bms_union(context->appendparents,\n\t\t\t\t\t\t\t\t\t\t\t   ((Append *) dpns->plan)->apprelids);\n\t\telse if (IsA(dpns->plan, MergeAppend))\n\t\t\tcontext->appendparents = bms_union(context->appendparents,\n\t\t\t\t\t\t\t\t\t\t\t   ((MergeAppend *) dpns->plan)->apprelids);\n\n\t\tpush_child_plan(dpns, dpns->outer_plan, &save_dpns);\n\t\tresolve_special_varno((Node *) tle->expr, context,\n\t\t\t\t\t\t\t  callback, callback_arg);\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\tcontext->appendparents = save_appendparents;\n\t\treturn;\n\t}\n\telse if (var->varno == INNER_VAR && dpns->inner_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\n\t\ttle = get_tle_by_resno(dpns->inner_tlist, var->varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INNER_VAR var: %d\", var->varattno);\n\n\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\t\tresolve_special_varno((Node *) tle->expr, context, callback, callback_arg);\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn;\n\t}\n\telse if (var->varno == INDEX_VAR && dpns->index_tlist)\n\t{\n\t\tTargetEntry *tle;\n\n\t\ttle = get_tle_by_resno(dpns->index_tlist, var->varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INDEX_VAR var: %d\", var->varattno);\n\n\t\tresolve_special_varno((Node *) tle->expr, context, callback, callback_arg);\n\t\treturn;\n\t}\n\telse if (var->varno < 1 || var->varno > list_length(dpns->rtable))\n\t\telog(ERROR, \"bogus varno: %d\", var->varno);\n\n\t/* Not special.  Just invoke the callback. */\n\t(*callback) (node, context, callback_arg);\n}\n\n/*\n * Get the name of a field of an expression of composite type.  The\n * expression is usually a Var, but we handle other cases too.\n *\n * levelsup is an extra offset to interpret the Var's varlevelsup correctly.\n *\n * This is fairly straightforward when the expression has a named composite\n * type; we need only look up the type in the catalogs.  However, the type\n * could also be RECORD.  Since no actual table or view column is allowed to\n * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE\n * or to a subquery output.  We drill down to find the ultimate defining\n * expression and attempt to infer the field name from it.  We ereport if we\n * can't determine the name.\n *\n * Similarly, a PARAM of type RECORD has to refer to some expression of\n * a determinable composite type.\n */\nstatic const char *\nget_name_for_var_field(Var *var, int fieldno,\n\t\t\t\t\t   int levelsup, deparse_context *context)\n{\n\tRangeTblEntry *rte;\n\tAttrNumber\tattnum;\n\tint\t\t\tnetlevelsup;\n\tdeparse_namespace *dpns;\n\tint\t\tvarno;\n\tAttrNumber\tvarattno;\n\tTupleDesc\ttupleDesc;\n\tNode\t   *expr;\n\n\t/*\n\t * If it's a RowExpr that was expanded from a whole-row Var, use the\n\t * column names attached to it.  (We could let get_expr_result_tupdesc()\n\t * handle this, but it's much cheaper to just pull out the name we need.)\n\t */\n\tif (IsA(var, RowExpr))\n\t{\n\t\tRowExpr    *r = (RowExpr *) var;\n\n\t\tif (fieldno > 0 && fieldno <= list_length(r->colnames))\n\t\t\treturn strVal(list_nth(r->colnames, fieldno - 1));\n\t}\n\n\t/*\n\t * If it's a Param of type RECORD, try to find what the Param refers to.\n\t */\n\tif (IsA(var, Param))\n\t{\n\t\tParam\t   *param = (Param *) var;\n\t\tListCell   *ancestor_cell;\n\n\t\texpr = find_param_referent(param, context, &dpns, &ancestor_cell);\n\t\tif (expr)\n\t\t{\n\t\t\t/* Found a match, so recurse to decipher the field name */\n\t\t\tdeparse_namespace save_dpns;\n\t\t\tconst char *result;\n\n\t\t\tpush_ancestor_plan(dpns, ancestor_cell, &save_dpns);\n\t\t\tresult = get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t0, context);\n\t\t\tpop_ancestor_plan(dpns, &save_dpns);\n\t\t\treturn result;\n\t\t}\n\t}\n\n\t/*\n\t * If it's a Var of type RECORD, we have to find what the Var refers to;\n\t * if not, we can use get_expr_result_tupdesc().\n\t */\n\tif (!IsA(var, Var) ||\n\t\tvar->vartype != RECORDOID)\n\t{\n\t\ttupleDesc = get_expr_result_tupdesc((Node *) var, false);\n\t\t/* Got the tupdesc, so we can extract the field name */\n\t\tAssert(fieldno >= 1 && fieldno <= tupleDesc->natts);\n\t\treturn NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);\n\t}\n\n\t/* Find appropriate nesting depth */\n\tnetlevelsup = var->varlevelsup + levelsup;\n\tif (netlevelsup >= list_length(context->namespaces))\n\t\telog(ERROR, \"bogus varlevelsup: %d offset %d\",\n\t\t\t var->varlevelsup, levelsup);\n\tdpns = (deparse_namespace *) list_nth(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t  netlevelsup);\n\n\tvarno = var->varno;\n\tvarattno = var->varattno;\n\n\tif (var->varnosyn > 0 && var->varnosyn <= list_length(dpns->rtable) && dpns->plan == NULL) {\n\t\trte = rt_fetch(var->varnosyn, dpns->rtable);\n\n\t\t/*\n\t\t * if the rte var->varnosyn points to is not a regular table and it is a join\n\t\t * then the correct relname will be found with var->varnosyn and var->varattnosyn\n\t\t */\n\t\tif (rte->rtekind == RTE_JOIN && rte->relid == 0 && var->varnosyn != var->varno) {\n\t\t\tvarno = var->varnosyn;\n\t\t\tvarattno = var->varattnosyn;\n\t\t}\n\t}\n\n\t/*\n\t * Try to find the relevant RTE in this rtable.  In a plan tree, it's\n\t * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig\n\t * down into the subplans, or INDEX_VAR, which is resolved similarly.\n\t */\n\tif (varno >= 1 && varno <= list_length(dpns->rtable))\n\t{\n\t\trte = rt_fetch(varno, dpns->rtable);\n\t\tattnum = varattno;\n\t}\n\telse if (varno == OUTER_VAR && dpns->outer_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\t\tconst char *result;\n\n\t\ttle = get_tle_by_resno(dpns->outer_tlist, varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for OUTER_VAR var: %d\", varattno);\n\n\t\tAssert(netlevelsup == 0);\n\t\tpush_child_plan(dpns, dpns->outer_plan, &save_dpns);\n\n\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn result;\n\t}\n\telse if (varno == INNER_VAR && dpns->inner_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tdeparse_namespace save_dpns;\n\t\tconst char *result;\n\n\t\ttle = get_tle_by_resno(dpns->inner_tlist, varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INNER_VAR var: %d\", varattno);\n\n\t\tAssert(netlevelsup == 0);\n\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\tpop_child_plan(dpns, &save_dpns);\n\t\treturn result;\n\t}\n\telse if (varno == INDEX_VAR && dpns->index_tlist)\n\t{\n\t\tTargetEntry *tle;\n\t\tconst char *result;\n\n\t\ttle = get_tle_by_resno(dpns->index_tlist, varattno);\n\t\tif (!tle)\n\t\t\telog(ERROR, \"bogus varattno for INDEX_VAR var: %d\", varattno);\n\n\t\tAssert(netlevelsup == 0);\n\n\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\treturn result;\n\t}\n\telse\n\t{\n\t\telog(ERROR, \"bogus varno: %d\", varno);\n\t\treturn NULL;\t\t\t/* keep compiler quiet */\n\t}\n\n\tif (attnum == InvalidAttrNumber)\n\t{\n\t\t/* Var is whole-row reference to RTE, so select the right field */\n\t\treturn get_rte_attribute_name(rte, fieldno);\n\t}\n\n\t/*\n\t * This part has essentially the same logic as the parser's\n\t * expandRecordVariable() function, but we are dealing with a different\n\t * representation of the input context, and we only need one field name\n\t * not a TupleDesc.  Also, we need special cases for finding subquery and\n\t * CTE subplans when deparsing Plan trees.\n\t */\n\texpr = (Node *) var;\t\t/* default if we can't drill down */\n\n\tswitch (rte->rtekind)\n\t{\n\t\tcase RTE_RELATION:\n\t\tcase RTE_VALUES:\n\t\tcase RTE_NAMEDTUPLESTORE:\n\t\tcase RTE_RESULT:\n\t\tcase RTE_SUBQUERY:\n\t\t\t/* Subselect-in-FROM: examine sub-select's output expr */\n\t\t\t{\n\t\t\t\tif (rte->subquery)\n\t\t\t\t{\n\t\t\t\t\tTargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tattnum);\n\n\t\t\t\t\tif (ste == NULL || ste->resjunk)\n\t\t\t\t\t\telog(ERROR, \"subquery %s does not have attribute %d\",\n\t\t\t\t\t\t\t rte->eref->aliasname, attnum);\n\t\t\t\t\texpr = (Node *) ste->expr;\n\t\t\t\t\tif (IsA(expr, Var))\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Recurse into the sub-select to see what its Var\n\t\t\t\t\t\t * refers to. We have to build an additional level of\n\t\t\t\t\t\t * namespace to keep in step with varlevelsup in the\n\t\t\t\t\t\t * subselect; furthermore, the subquery RTE might be\n\t\t\t\t\t\t * from an outer query level, in which case the\n\t\t\t\t\t\t * namespace for the subselect must have that outer\n\t\t\t\t\t\t * level as parent namespace.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tList\t   *save_nslist = context->namespaces;\n\t\t\t\t\t\tList\t   *parent_namespaces;\n\t\t\t\t\t\tdeparse_namespace mydpns;\n\t\t\t\t\t\tconst char *result;\n\n\t\t\t\t\t\tparent_namespaces = list_copy_tail(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   netlevelsup);\n\n\t\t\t\t\t\tset_deparse_for_query(&mydpns, rte->subquery,\n\t\t\t\t\t\t\t\t\t\t\t  parent_namespaces);\n\n\t\t\t\t\t\tcontext->namespaces = lcons(&mydpns,\n\t\t\t\t\t\t\t\t\t\t\t\t\tparent_namespaces);\n\n\t\t\t\t\t\tresult = get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t0, context);\n\n\t\t\t\t\t\tcontext->namespaces = save_nslist;\n\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\t\t\t\t\t/* else fall through to inspect the expression */\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * We're deparsing a Plan tree so we don't have complete\n\t\t\t\t\t * RTE entries (in particular, rte->subquery is NULL). But\n\t\t\t\t\t * the only place we'd normally see a Var directly\n\t\t\t\t\t * referencing a SUBQUERY RTE is in a SubqueryScan plan\n\t\t\t\t\t * node, and we can look into the child plan's tlist\n\t\t\t\t\t * instead.  An exception occurs if the subquery was\n\t\t\t\t\t * proven empty and optimized away: then we'd find such a\n\t\t\t\t\t * Var in a childless Result node, and there's nothing in\n\t\t\t\t\t * the plan tree that would let us figure out what it had\n\t\t\t\t\t * originally referenced.  In that case, fall back on\n\t\t\t\t\t * printing \"fN\", analogously to the default column names\n\t\t\t\t\t * for RowExprs.\n\t\t\t\t\t */\n\t\t\t\t\tTargetEntry *tle;\n\t\t\t\t\tdeparse_namespace save_dpns;\n\t\t\t\t\tconst char *result;\n\n\t\t\t\t\tif (!dpns->inner_plan)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar\t   *dummy_name = palloc(32);\n\t\t\t\t\t\tAssert(dpns->plan && IsA(dpns->plan, Result));\n\t\t\t\t\t\tsnprintf(dummy_name, 32, \"f%d\", fieldno);\n\t\t\t\t\t\treturn dummy_name;\n\t\t\t\t\t}\n\t\t\t\t\tAssert(dpns->plan && IsA(dpns->plan, SubqueryScan));\n\n\t\t\t\t\ttle = get_tle_by_resno(dpns->inner_tlist, attnum);\n\t\t\t\t\tif (!tle)\n\t\t\t\t\t\telog(ERROR, \"bogus varattno for subquery var: %d\",\n\t\t\t\t\t\t\t attnum);\n\t\t\t\t\tAssert(netlevelsup == 0);\n\t\t\t\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\t\t\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\t\t\t\tpop_child_plan(dpns, &save_dpns);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase RTE_JOIN:\n\t\t\t/* Join RTE --- recursively inspect the alias variable */\n\t\t\tif (rte->joinaliasvars == NIL)\n\t\t\t\telog(ERROR, \"cannot decompile join alias var in plan tree\");\n\t\t\tAssert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));\n\t\t\texpr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);\n\t\t\tAssert(expr != NULL);\n\t\t\t/* we intentionally don't strip implicit coercions here */\n\t\t\tif (IsA(expr, Var))\n\t\t\t\treturn get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t  var->varlevelsup + levelsup,\n\t\t\t\t\t\t\t\t\t\t\t  context);\n\t\t\t/* else fall through to inspect the expression */\n\t\t\tbreak;\n\t\tcase RTE_FUNCTION:\n\t\tcase RTE_TABLEFUNC:\n\n\t\t\t/*\n\t\t\t * We couldn't get here unless a function is declared with one of\n\t\t\t * its result columns as RECORD, which is not allowed.\n\t\t\t */\n\t\t\tbreak;\n\t\tcase RTE_CTE:\n\t\t\t/* CTE reference: examine subquery's output expr */\n\t\t\t{\n\t\t\t\tCommonTableExpr *cte = NULL;\n\t\t\t\tIndex\t\tctelevelsup;\n\t\t\t\tListCell   *lc;\n\n\t\t\t\t/*\n\t\t\t\t * Try to find the referenced CTE using the namespace stack.\n\t\t\t\t */\n\t\t\t\tctelevelsup = rte->ctelevelsup + netlevelsup;\n\t\t\t\tif (ctelevelsup >= list_length(context->namespaces))\n\t\t\t\t\tlc = NULL;\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdeparse_namespace *ctedpns;\n\n\t\t\t\t\tctedpns = (deparse_namespace *)\n\t\t\t\t\t\tlist_nth(context->namespaces, ctelevelsup);\n\t\t\t\t\tforeach(lc, ctedpns->ctes)\n\t\t\t\t\t{\n\t\t\t\t\t\tcte = (CommonTableExpr *) lfirst(lc);\n\t\t\t\t\t\tif (strcmp(cte->ctename, rte->ctename) == 0)\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (lc != NULL)\n\t\t\t\t{\n\t\t\t\t\tQuery\t   *ctequery = (Query *) cte->ctequery;\n\t\t\t\t\tTargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tattnum);\n\n\t\t\t\t\tif (ste == NULL || ste->resjunk)\n\t\t\t\t\t\telog(ERROR, \"CTE %s does not have attribute %d\",\n\t\t\t\t\t\t\t rte->eref->aliasname, attnum);\n\t\t\t\t\texpr = (Node *) ste->expr;\n\t\t\t\t\tif (IsA(expr, Var))\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Recurse into the CTE to see what its Var refers to.\n\t\t\t\t\t\t * We have to build an additional level of namespace\n\t\t\t\t\t\t * to keep in step with varlevelsup in the CTE;\n\t\t\t\t\t\t * furthermore it could be an outer CTE (compare\n\t\t\t\t\t\t * SUBQUERY case above).\n\t\t\t\t\t\t */\n\t\t\t\t\t\tList\t   *save_nslist = context->namespaces;\n\t\t\t\t\t\tList\t   *parent_namespaces;\n\t\t\t\t\t\tdeparse_namespace mydpns;\n\t\t\t\t\t\tconst char *result;\n\n\t\t\t\t\t\tparent_namespaces = list_copy_tail(context->namespaces,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ctelevelsup);\n\n\t\t\t\t\t\tset_deparse_for_query(&mydpns, ctequery,\n\t\t\t\t\t\t\t\t\t\t\t  parent_namespaces);\n\n\t\t\t\t\t\tcontext->namespaces = lcons(&mydpns, parent_namespaces);\n\n\t\t\t\t\t\tresult = get_name_for_var_field((Var *) expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t0, context);\n\n\t\t\t\t\t\tcontext->namespaces = save_nslist;\n\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\t\t\t\t\t/* else fall through to inspect the expression */\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * We're deparsing a Plan tree so we don't have a CTE\n\t\t\t\t\t * list.  But the only places we'd normally see a Var\n\t\t\t\t\t * directly referencing a CTE RTE are in CteScan or\n\t\t\t\t\t * WorkTableScan plan nodes.  For those cases,\n\t\t\t\t\t * set_deparse_plan arranged for dpns->inner_plan to be\n\t\t\t\t\t * the plan node that emits the CTE or RecursiveUnion\n\t\t\t\t\t * result, and we can look at its tlist instead.  As\n\t\t\t\t\t * above, this can fail if the CTE has been proven empty,\n\t\t\t\t\t * in which case fall back to \"fN\".\n\t\t\t\t\t */\n\t\t\t\t\tTargetEntry *tle;\n\t\t\t\t\tdeparse_namespace save_dpns;\n\t\t\t\t\tconst char *result;\n\n\t\t\t\t\tif (!dpns->inner_plan)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar\t   *dummy_name = palloc(32);\n\t\t\t\t\t\tAssert(dpns->plan && IsA(dpns->plan, Result));\n\t\t\t\t\t\tsnprintf(dummy_name, 32, \"f%d\", fieldno);\n\t\t\t\t\t\treturn dummy_name;\n\t\t\t\t\t}\n\t\t\t\t\tAssert(dpns->plan && (IsA(dpns->plan, CteScan) ||\n\t\t\t\t\t\t\t\t\t\t  IsA(dpns->plan, WorkTableScan)));\n\n\t\t\t\t\ttle = get_tle_by_resno(dpns->inner_tlist, attnum);\n\t\t\t\t\tif (!tle)\n\t\t\t\t\t\telog(ERROR, \"bogus varattno for subquery var: %d\",\n\t\t\t\t\t\t\t attnum);\n\t\t\t\t\tAssert(netlevelsup == 0);\n\t\t\t\t\tpush_child_plan(dpns, dpns->inner_plan, &save_dpns);\n\n\t\t\t\t\tresult = get_name_for_var_field((Var *) tle->expr, fieldno,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlevelsup, context);\n\n\t\t\t\t\tpop_child_plan(dpns, &save_dpns);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase RTE_GROUP:\n\n\t\t\t/*\n\t\t\t * We couldn't get here: any Vars that reference the RTE_GROUP RTE\n\t\t\t * should have been replaced with the underlying grouping\n\t\t\t * expressions.\n\t\t\t */\n\t\t\tbreak;\n\t}\n\n\t/*\n\t * We now have an expression we can't expand any more, so see if\n\t * get_expr_result_tupdesc() can do anything with it.\n\t */\n\ttupleDesc = get_expr_result_tupdesc(expr, false);\n\t/* Got the tupdesc, so we can extract the field name */\n\tAssert(fieldno >= 1 && fieldno <= tupleDesc->natts);\n\treturn NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);\n}\n\n/*\n * Try to find the referenced expression for a PARAM_EXEC Param that might\n * reference a parameter supplied by an upper NestLoop or SubPlan plan node.\n *\n * If successful, return the expression and set *dpns_p and *ancestor_cell_p\n * appropriately for calling push_ancestor_plan().  If no referent can be\n * found, return NULL.\n */\nstatic Node *\nfind_param_referent(Param *param, deparse_context *context,\n\t\t\t\t\tdeparse_namespace **dpns_p, ListCell **ancestor_cell_p)\n{\n\t/* Initialize output parameters to prevent compiler warnings */\n\t*dpns_p = NULL;\n\t*ancestor_cell_p = NULL;\n\n\t/*\n\t * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or\n\t * SubPlan argument.  This will necessarily be in some ancestor of the\n\t * current expression's Plan.\n\t */\n\tif (param->paramkind == PARAM_EXEC)\n\t{\n\t\tdeparse_namespace *dpns;\n\t\tPlan  *child_plan;\n\t\tListCell   *lc;\n\n\t\tdpns = (deparse_namespace *) linitial(context->namespaces);\n\t\tchild_plan = dpns->plan;\n\n\t\tforeach(lc, dpns->ancestors)\n\t\t{\n\t\t\tNode\t   *ancestor = (Node *) lfirst(lc);\n\t\t\tListCell   *lc2;\n\n\t\t\t/*\n\t\t\t * NestLoops transmit params to their inner child only.\n\t\t\t */\n\t\t\tif (IsA(ancestor, NestLoop) &&\n\t\t\t\tchild_plan == innerPlan(ancestor))\n\t\t\t{\n\t\t\t\tNestLoop   *nl = (NestLoop *) ancestor;\n\n\t\t\t\tforeach(lc2, nl->nestParams)\n\t\t\t\t{\n\t\t\t\t\tNestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);\n\n\t\t\t\t\tif (nlp->paramno == param->paramid)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Found a match, so return it */\n\t\t\t\t\t\t*dpns_p = dpns;\n\t\t\t\t\t\t*ancestor_cell_p = lc;\n\t\t\t\t\t\treturn (Node *) nlp->paramval;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Check to see if we're crawling up from a subplan.\n\t\t\t */\n\t\t\tif(IsA(ancestor, SubPlan))\n\t\t\t{\n\t\t\t\tSubPlan    *subplan = (SubPlan *) ancestor;\n\t\t\t\tListCell   *lc3;\n\t\t\t\tListCell   *lc4;\n\n\t\t\t\t/* Matched subplan, so check its arguments */\n\t\t\t\tforboth(lc3, subplan->parParam, lc4, subplan->args)\n\t\t\t\t{\n\t\t\t\t\tint\t\t\tparamid = lfirst_int(lc3);\n\t\t\t\t\tNode\t   *arg = (Node *) lfirst(lc4);\n\n\t\t\t\t\tif (paramid == param->paramid)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Found a match, so return it.  But, since Vars in\n\t\t\t\t\t\t * the arg are to be evaluated in the surrounding\n\t\t\t\t\t\t * context, we have to point to the next ancestor item\n\t\t\t\t\t\t * that is *not* a SubPlan.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tListCell   *rest;\n\n\t\t\t\t\t\tfor_each_cell(rest, dpns->ancestors,\n\t\t\t\t\t\t\t\t\t  lnext(dpns->ancestors, lc))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tNode\t   *ancestor2 = (Node *) lfirst(rest);\n\n\t\t\t\t\t\t\tif (!IsA(ancestor2, SubPlan))\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t*dpns_p = dpns;\n\t\t\t\t\t\t\t\t*ancestor_cell_p = rest;\n\t\t\t\t\t\t\t\treturn arg;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telog(ERROR, \"SubPlan cannot be outermost ancestor\");\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* SubPlan isn't a kind of Plan, so skip the rest */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * We need not consider the ancestor's initPlan list, since\n\t\t\t * initplans never have any parParams.\n\t\t\t */\n\n\t\t\t/* No luck, crawl up to next ancestor */\n\t\t\tchild_plan = (Plan *) ancestor;\n\t\t}\n\t}\n\n\t/* No referent found */\n\treturn NULL;\n}\n\n/*\n * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param.\n *\n * If successful, return the generating subplan/initplan and set *column_p\n * to the subplan's 0-based output column number.\n * Otherwise, return NULL.\n */\nstatic SubPlan *\nfind_param_generator(Param *param, deparse_context *context, int *column_p)\n{\n\t/* Initialize output parameter to prevent compiler warnings */\n\t*column_p = 0;\n\n\t/*\n\t * If it's a PARAM_EXEC parameter, search the current plan node as well as\n\t * ancestor nodes looking for a subplan or initplan that emits the value\n\t * for the Param.  It could appear in the setParams of an initplan or\n\t * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.\n\t */\n\tif (param->paramkind == PARAM_EXEC)\n\t{\n\t\tSubPlan    *result;\n\t\tdeparse_namespace *dpns;\n\t\tListCell   *lc;\n\n\t\tdpns = (deparse_namespace *) linitial(context->namespaces);\n\n\t\t/* First check the innermost plan node's initplans */\n\t\tresult = find_param_generator_initplan(param, dpns->plan, column_p);\n\t\tif (result)\n\t\t\treturn result;\n\n\t\t/*\n\t\t * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,\n\t\t * which can be referenced by Params elsewhere in the targetlist.\n\t\t * (Such Params should always be in the same targetlist, so there's no\n\t\t * need to do this work at upper plan nodes.)\n\t\t */\n\t\tforeach_node(TargetEntry, tle, dpns->plan->targetlist)\n\t\t{\n\t\t\tif (tle->expr && IsA(tle->expr, SubPlan))\n\t\t\t{\n\t\t\t\tSubPlan    *subplan = (SubPlan *) tle->expr;\n\n\t\t\t\tif (subplan->subLinkType == MULTIEXPR_SUBLINK)\n\t\t\t\t{\n\t\t\t\t\tforeach_int(paramid, subplan->setParam)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (paramid == param->paramid)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t/* Found a match, so return it. */\n\t\t\t\t\t\t\t*column_p = foreach_current_index(paramid);\n\t\t\t\t\t\t\treturn subplan;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/* No luck, so check the ancestor nodes */\n\t\tforeach(lc, dpns->ancestors)\n\t\t{\n\t\t\tNode\t   *ancestor = (Node *) lfirst(lc);\n\n\t\t\t/*\n\t\t\t * If ancestor is a SubPlan, check the paramIds it provides.\n\t\t\t */\n\t\t\tif (IsA(ancestor, SubPlan))\n\t\t\t{\n\t\t\t\tSubPlan    *subplan = (SubPlan *) ancestor;\n\n\t\t\t\tforeach_int(paramid, subplan->paramIds)\n\t\t\t\t{\n\t\t\t\t\tif (paramid == param->paramid)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Found a match, so return it. */\n\t\t\t\t\t\t*column_p = foreach_current_index(paramid);\n\t\t\t\t\t\treturn subplan;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* SubPlan isn't a kind of Plan, so skip the rest */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Otherwise, it's some kind of Plan node, so check its initplans.\n\t\t\t */\n\t\t\tresult = find_param_generator_initplan(param, (Plan *) ancestor,\n\t\t\t\t\t\t\t\t\t\t\t\t   column_p);\n\t\t\tif (result)\n\t\t\t\treturn result;\n\n\t\t\t/* No luck, crawl up to next ancestor */\n\t\t}\n\t}\n\n\t/* No generator found */\n\treturn NULL;\n}\n\n/*\n * Subroutine for find_param_generator: search one Plan node's initplans\n */\nstatic SubPlan *\nfind_param_generator_initplan(Param *param, Plan *plan, int *column_p)\n{\n\tforeach_node(SubPlan, subplan, plan->initPlan)\n\t{\n\t\tforeach_int(paramid, subplan->setParam)\n\t\t{\n\t\t\tif (paramid == param->paramid)\n\t\t\t{\n\t\t\t\t/* Found a match, so return it. */\n\t\t\t\t*column_p = foreach_current_index(paramid);\n\t\t\t\treturn subplan;\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\n\n/*\n * Display a Param appropriately.\n */\nstatic void\nget_parameter(Param *param, deparse_context *context)\n{\n\tNode\t   *expr;\n\tdeparse_namespace *dpns;\n\tListCell   *ancestor_cell;\n\tSubPlan    *subplan;\n\tint\t\t\tcolumn;\n\n\t/*\n\t * If it's a PARAM_EXEC parameter, try to locate the expression from which\n\t * the parameter was computed.  This stanza handles only cases in which\n\t * the Param represents an input to the subplan we are currently in.\n\t */\n\texpr = find_param_referent(param, context, &dpns, &ancestor_cell);\n\tif (expr)\n\t{\n\t\t/* Found a match, so print it */\n\t\tdeparse_namespace save_dpns;\n\t\tbool\t\tsave_varprefix;\n\t\tbool\t\tneed_paren;\n\n\t\t/* Switch attention to the ancestor plan node */\n\t\tpush_ancestor_plan(dpns, ancestor_cell, &save_dpns);\n\n\t\t/*\n\t\t * Force prefixing of Vars, since they won't belong to the relation\n\t\t * being scanned in the original plan node.\n\t\t */\n\t\tsave_varprefix = context->varprefix;\n\t\tcontext->varprefix = true;\n\n\t\t/*\n\t\t * A Param's expansion is typically a Var, Aggref, GroupingFunc, or\n\t\t * upper-level Param, which wouldn't need extra parentheses.\n\t\t * Otherwise, insert parens to ensure the expression looks atomic.\n\t\t */\n\t\tneed_paren = !(IsA(expr, Var) ||\n\t\t\t\t\t   IsA(expr, Aggref) ||\n                       IsA(expr, GroupingFunc) ||\n\t\t\t\t\t   IsA(expr, Param));\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, '(');\n\n\t\tget_rule_expr(expr, context, false);\n\n\t\tif (need_paren)\n\t\t\tappendStringInfoChar(context->buf, ')');\n\n\t\tcontext->varprefix = save_varprefix;\n\n\t\tpop_ancestor_plan(dpns, &save_dpns);\n\n\t\treturn;\n\t}\n\n\t/*\n\t * Alternatively, maybe it's a subplan output, which we print as a\n\t * reference to the subplan.  (We could drill down into the subplan and\n\t * print the relevant targetlist expression, but that has been deemed too\n\t * confusing since it would violate normal SQL scope rules.  Also, we're\n\t * relying on this reference to show that the testexpr containing the\n\t * Param has anything to do with that subplan at all.)\n\t */\n\tsubplan = find_param_generator(param, context, &column);\n\tif (subplan)\n\t{\n\t\tappendStringInfo(context->buf, \"(%s%s).col%d\",\n\t\t\t\t\t\t subplan->useHashTable ? \"hashed \" : \"\",\n\t\t\t\t\t\t subplan->plan_name, column + 1);\n\n\t\treturn;\n\t}\n\n\t/*\n\t * If it's an external parameter, see if the outermost namespace provides\n\t * function argument names.\n\t */\n\tif (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)\n\t{\n\t\tdpns = llast(context->namespaces);\n\t\tif (dpns->argnames &&\n\t\t\tparam->paramid > 0 &&\n\t\t\tparam->paramid <= dpns->numargs)\n\t\t{\n\t\t\tchar\t   *argname = dpns->argnames[param->paramid - 1];\n\n\t\t\tif (argname)\n\t\t\t{\n\t\t\t\tbool\t\tshould_qualify = false;\n\t\t\t\tListCell   *lc;\n\n\t\t\t\t/*\n\t\t\t\t * Qualify the parameter name if there are any other deparse\n\t\t\t\t * namespaces with range tables.  This avoids qualifying in\n\t\t\t\t * trivial cases like \"RETURN a + b\", but makes it safe in all\n\t\t\t\t * other cases.\n\t\t\t\t */\n\t\t\t\tforeach(lc, context->namespaces)\n\t\t\t\t{\n\t\t\t\t\tdeparse_namespace *depns = lfirst(lc);\n\n\t\t\t\t\tif (depns->rtable_names != NIL)\n\t\t\t\t\t{\n\t\t\t\t\t\tshould_qualify = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (should_qualify)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(context->buf, quote_identifier(dpns->funcname));\n\t\t\t\t\tappendStringInfoChar(context->buf, '.');\n\t\t\t\t}\n\n\t\t\t\tappendStringInfoString(context->buf, quote_identifier(argname));\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * Not PARAM_EXEC, or couldn't find referent: for base types just print $N.\n\t * For composite types, add cast to the parameter to ease remote node detect\n\t * the type.\n\t *\n\t * It's a bug if we get here for anything except PARAM_EXTERN Params, but\n\t * in production builds printing $N seems more useful than failing.\n\t */\n\tAssert(param->paramkind == PARAM_EXTERN);\n\n\tif (param->paramtype >= FirstNormalObjectId)\n\t{\n\t\tchar *typeName = format_type_with_typemod(param->paramtype, param->paramtypmod);\n\n\t\tappendStringInfo(context->buf, \"$%d::%s\", param->paramid, typeName);\n\t}\n\telse\n\t{\n\t\tappendStringInfo(context->buf, \"$%d\", param->paramid);\n\t}\n}\n\n/*\n * get_simple_binary_op_name\n *\n * helper function for isSimpleNode\n * will return single char binary operator name, or NULL if it's not\n */\nstatic const char *\nget_simple_binary_op_name(OpExpr *expr)\n{\n\tList\t   *args = expr->args;\n\n\tif (list_length(args) == 2)\n\t{\n\t\t/* binary operator */\n\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\t\tconst char *op;\n\n\t\top = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));\n\t\tif (strlen(op) == 1)\n\t\t\treturn op;\n\t}\n\treturn NULL;\n}\n\n/*\n * isSimpleNode - check if given node is simple (doesn't need parenthesizing)\n *\n *\ttrue   : simple in the context of parent node's type\n *\tfalse  : not simple\n */\nstatic bool\nisSimpleNode(Node *node, Node *parentNode, int prettyFlags)\n{\n\tif (!node)\n\t\treturn false;\n\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_Var:\n\t\tcase T_Const:\n\t\tcase T_Param:\n\t\tcase T_CoerceToDomainValue:\n\t\tcase T_SetToDefault:\n\t\tcase T_CurrentOfExpr:\n\t\t\t/* single words: always simple */\n\t\t\treturn true;\n\n\t\tcase T_SubscriptingRef:\n\t\tcase T_ArrayExpr:\n\t\tcase T_RowExpr:\n\t\tcase T_CoalesceExpr:\n\t\tcase T_MinMaxExpr:\n\t\tcase T_SQLValueFunction:\n\t\tcase T_XmlExpr:\n\t\tcase T_NextValueExpr:\n\t\tcase T_NullIfExpr:\n\t\tcase T_Aggref:\n        case T_GroupingFunc:\n\t\tcase T_WindowFunc:\n\t\tcase T_MergeSupportFunc:\n\t\tcase T_FuncExpr:\n\t\tcase T_JsonConstructorExpr:\n\t\tcase T_JsonExpr:\n\t\t\t/* function-like: name(..) or name[..] */\n\t\t\treturn true;\n\n\t\t\t/* CASE keywords act as parentheses */\n\t\tcase T_CaseExpr:\n\t\t\treturn true;\n\n\t\tcase T_FieldSelect:\n\n\t\t\t/*\n\t\t\t * appears simple since . has top precedence, unless parent is\n\t\t\t * T_FieldSelect itself!\n\t\t\t */\n\t\t\treturn !IsA(parentNode, FieldSelect);\n\n\t\tcase T_FieldStore:\n\n\t\t\t/*\n\t\t\t * treat like FieldSelect (probably doesn't matter)\n\t\t\t */\n\t\t\treturn !IsA(parentNode, FieldStore);\n\n\t\tcase T_CoerceToDomain:\n\t\t\t/* maybe simple, check args */\n\t\t\treturn isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_RelabelType:\n\t\t\treturn isSimpleNode((Node *) ((RelabelType *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_CoerceViaIO:\n\t\t\treturn isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_ArrayCoerceExpr:\n\t\t\treturn isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_ConvertRowtypeExpr:\n\t\t\treturn isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\t\tcase T_ReturningExpr:\n\t\t\treturn isSimpleNode((Node *) ((ReturningExpr *) node)->retexpr,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\n\t\tcase T_OpExpr:\n\t\t\t{\n\t\t\t\t/* depends on parent node type; needs further checking */\n\t\t\t\tif (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))\n\t\t\t\t{\n\t\t\t\t\tconst char *op;\n\t\t\t\t\tconst char *parentOp;\n\t\t\t\t\tbool\t\tis_lopriop;\n\t\t\t\t\tbool\t\tis_hipriop;\n\t\t\t\t\tbool\t\tis_lopriparent;\n\t\t\t\t\tbool\t\tis_hipriparent;\n\n\t\t\t\t\top = get_simple_binary_op_name((OpExpr *) node);\n\t\t\t\t\tif (!op)\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\t/* We know only the basic operators + - and * / % */\n\t\t\t\t\tis_lopriop = (strchr(\"+-\", *op) != NULL);\n\t\t\t\t\tis_hipriop = (strchr(\"*/%\", *op) != NULL);\n\t\t\t\t\tif (!(is_lopriop || is_hipriop))\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tparentOp = get_simple_binary_op_name((OpExpr *) parentNode);\n\t\t\t\t\tif (!parentOp)\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tis_lopriparent = (strchr(\"+-\", *parentOp) != NULL);\n\t\t\t\t\tis_hipriparent = (strchr(\"*/%\", *parentOp) != NULL);\n\t\t\t\t\tif (!(is_lopriparent || is_hipriparent))\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\tif (is_hipriop && is_lopriparent)\n\t\t\t\t\t\treturn true;\t/* op binds tighter than parent */\n\n\t\t\t\t\tif (is_lopriop && is_hipriparent)\n\t\t\t\t\t\treturn false;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Operators are same priority --- can skip parens only if\n\t\t\t\t\t * we have (a - b) - c, not a - (b - c).\n\t\t\t\t\t */\n\t\t\t\t\tif (node == (Node *) linitial(((OpExpr *) parentNode)->args))\n\t\t\t\t\t\treturn true;\n\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t/* else do the same stuff as for T_SubLink et al. */\n\t\t\t}\n\t\t\t/* FALLTHROUGH */\n\n\t\tcase T_SubLink:\n\t\tcase T_NullTest:\n\t\tcase T_BooleanTest:\n\t\tcase T_DistinctExpr:\n\t\tcase T_JsonIsPredicate:\n\t\t\tswitch (nodeTag(parentNode))\n\t\t\t{\n\t\t\t\tcase T_FuncExpr:\n\t\t\t\t\t{\n\t\t\t\t\t\t/* special handling for casts and COERCE_SQL_SYNTAX */\n\t\t\t\t\t\tCoercionForm type = ((FuncExpr *) parentNode)->funcformat;\n\n\t\t\t\t\t\tif (type == COERCE_EXPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_IMPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_SQL_SYNTAX)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\treturn true;\t/* own parentheses */\n\t\t\t\t\t}\n\t\t\t\tcase T_BoolExpr:\t/* lower precedence */\n\t\t\t\tcase T_SubscriptingRef:\t/* other separators */\n\t\t\t\tcase T_ArrayExpr:\t/* other separators */\n\t\t\t\tcase T_RowExpr: /* other separators */\n\t\t\t\tcase T_CoalesceExpr:\t/* own parentheses */\n\t\t\t\tcase T_MinMaxExpr:\t/* own parentheses */\n\t\t\t\tcase T_XmlExpr: /* own parentheses */\n\t\t\t\tcase T_NullIfExpr:\t/* other separators */\n\t\t\t\tcase T_Aggref:\t/* own parentheses */\n                case T_GroupingFunc: /* own parentheses */\n\t\t\t\tcase T_WindowFunc:\t/* own parentheses */\n\t\t\t\tcase T_CaseExpr:\t/* other separators */\n\t\t\t\t\treturn true;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\n\t\tcase T_BoolExpr:\n\t\t\tswitch (nodeTag(parentNode))\n\t\t\t{\n\t\t\t\tcase T_BoolExpr:\n\t\t\t\t\tif (prettyFlags & PRETTYFLAG_PAREN)\n\t\t\t\t\t{\n\t\t\t\t\t\tBoolExprType type;\n\t\t\t\t\t\tBoolExprType parentType;\n\n\t\t\t\t\t\ttype = ((BoolExpr *) node)->boolop;\n\t\t\t\t\t\tparentType = ((BoolExpr *) parentNode)->boolop;\n\t\t\t\t\t\tswitch (type)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcase NOT_EXPR:\n\t\t\t\t\t\t\tcase AND_EXPR:\n\t\t\t\t\t\t\t\tif (parentType == AND_EXPR || parentType == OR_EXPR)\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase OR_EXPR:\n\t\t\t\t\t\t\t\tif (parentType == OR_EXPR)\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\tcase T_FuncExpr:\n\t\t\t\t\t{\n\t\t\t\t\t\t/* special handling for casts and COERCE_SQL_SYNTAX */\n\t\t\t\t\t\tCoercionForm type = ((FuncExpr *) parentNode)->funcformat;\n\n\t\t\t\t\t\tif (type == COERCE_EXPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_IMPLICIT_CAST ||\n\t\t\t\t\t\t\ttype == COERCE_SQL_SYNTAX)\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\treturn true;\t/* own parentheses */\n\t\t\t\t\t}\n\t\t\t\tcase T_SubscriptingRef:\t/* other separators */\n\t\t\t\tcase T_ArrayExpr:\t/* other separators */\n\t\t\t\tcase T_RowExpr: /* other separators */\n\t\t\t\tcase T_CoalesceExpr:\t/* own parentheses */\n\t\t\t\tcase T_MinMaxExpr:\t/* own parentheses */\n\t\t\t\tcase T_XmlExpr: /* own parentheses */\n\t\t\t\tcase T_NullIfExpr:\t/* other separators */\n\t\t\t\tcase T_Aggref:\t/* own parentheses */\n                case T_GroupingFunc: /* own parentheses */\n\t\t\t\tcase T_WindowFunc:\t/* own parentheses */\n\t\t\t\tcase T_CaseExpr:\t/* other separators */\n\t\t\t\tcase T_JsonExpr: /* own parentheses */\n\t\t\t\t\treturn true;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\n\t\tcase T_JsonValueExpr:\n\t\t\t/* maybe simple, check args */\n\t\t\treturn isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,\n\t\t\t\t\t\t\t\tnode, prettyFlags);\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\t/* those we don't know: in dubio complexo */\n\treturn false;\n}\n\n/*\n * appendContextKeyword - append a keyword to buffer\n *\n * If prettyPrint is enabled, perform a line break, and adjust indentation.\n * Otherwise, just append the keyword.\n */\nstatic void\nappendContextKeyword(deparse_context *context, const char *str,\n\t\t\t\t\t int indentBefore, int indentAfter, int indentPlus)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (PRETTY_INDENT(context))\n\t{\n\t\tint\t\t\tindentAmount;\n\n\t\tcontext->indentLevel += indentBefore;\n\n\t\t/* remove any trailing spaces currently in the buffer ... */\n\t\tremoveStringInfoSpaces(buf);\n\t\t/* ... then add a newline and some spaces */\n\t\tappendStringInfoChar(buf, '\\n');\n\n\t\tif (context->indentLevel < PRETTYINDENT_LIMIT)\n\t\t\tindentAmount = Max(context->indentLevel, 0) + indentPlus;\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * If we're indented more than PRETTYINDENT_LIMIT characters, try\n\t\t\t * to conserve horizontal space by reducing the per-level\n\t\t\t * indentation.  For best results the scale factor here should\n\t\t\t * divide all the indent amounts that get added to indentLevel\n\t\t\t * (PRETTYINDENT_STD, etc).  It's important that the indentation\n\t\t\t * not grow unboundedly, else deeply-nested trees use O(N^2)\n\t\t\t * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.\n\t\t\t */\n\t\t\tindentAmount = PRETTYINDENT_LIMIT +\n\t\t\t\t(context->indentLevel - PRETTYINDENT_LIMIT) /\n\t\t\t\t(PRETTYINDENT_STD / 2);\n\t\t\tindentAmount %= PRETTYINDENT_LIMIT;\n\t\t\t/* scale/wrap logic affects indentLevel, but not indentPlus */\n\t\t\tindentAmount += indentPlus;\n\t\t}\n\t\tappendStringInfoSpaces(buf, indentAmount);\n\n\t\tappendStringInfoString(buf, str);\n\n\t\tcontext->indentLevel += indentAfter;\n\t\tif (context->indentLevel < 0)\n\t\t\tcontext->indentLevel = 0;\n\t}\n\telse\n\t\tappendStringInfoString(buf, str);\n}\n\n/*\n * removeStringInfoSpaces - delete trailing spaces from a buffer.\n *\n * Possibly this should move to stringinfo.c at some point.\n */\nstatic void\nremoveStringInfoSpaces(StringInfo str)\n{\n\twhile (str->len > 0 && str->data[str->len - 1] == ' ')\n\t\tstr->data[--(str->len)] = '\\0';\n}\n\n/*\n * get_rule_expr_paren\t- deparse expr using get_rule_expr,\n * embracing the string with parentheses if necessary for prettyPrint.\n *\n * Never embrace if prettyFlags=0, because it's done in the calling node.\n *\n * Any node that does *not* embrace its argument node by sql syntax (with\n * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should\n * use get_rule_expr_paren instead of get_rule_expr so parentheses can be\n * added.\n */\nstatic void\nget_rule_expr_paren(Node *node, deparse_context *context,\n\t\t\t\t\tbool showimplicit, Node *parentNode)\n{\n\tbool\t\tneed_paren;\n\n\tneed_paren = PRETTY_PAREN(context) &&\n\t\t!isSimpleNode(node, parentNode, context->prettyFlags);\n\n\tif (need_paren)\n\t\tappendStringInfoChar(context->buf, '(');\n\n\tget_rule_expr(node, context, showimplicit);\n\n\tif (need_paren)\n\t\tappendStringInfoChar(context->buf, ')');\n}\n\nstatic void\nget_json_behavior(JsonBehavior *behavior, deparse_context *context,\n\t\t\t\t  const char *on)\n{\n\t/*\n\t * The order of array elements must correspond to the order of\n\t * JsonBehaviorType members.\n\t */\n\tconst char *behavior_names[] =\n\t{\n\t\t\" NULL\",\n\t\t\" ERROR\",\n\t\t\" EMPTY\",\n\t\t\" TRUE\",\n\t\t\" FALSE\",\n\t\t\" UNKNOWN\",\n\t\t\" EMPTY ARRAY\",\n\t\t\" EMPTY OBJECT\",\n\t\t\" DEFAULT \"\n\t};\n\n\tif ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))\n\t\telog(ERROR, \"invalid json behavior type: %d\", behavior->btype);\n\n\tappendStringInfoString(context->buf, behavior_names[behavior->btype]);\n\n\tif (behavior->btype == JSON_BEHAVIOR_DEFAULT)\n\t\tget_rule_expr(behavior->expr, context, false);\n\n\tappendStringInfo(context->buf, \" ON %s\", on);\n}\n\n/*\n * get_json_expr_options\n *\n * Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and\n * JSON_TABLE columns.\n */\nstatic void\nget_json_expr_options(JsonExpr *jsexpr, deparse_context *context,\n\t\t\t\t\t  JsonBehaviorType default_behavior)\n{\n\tif (jsexpr->op == JSON_QUERY_OP)\n\t{\n\t\tif (jsexpr->wrapper == JSW_CONDITIONAL)\n\t\t\tappendStringInfoString(context->buf, \" WITH CONDITIONAL WRAPPER\");\n\t\telse if (jsexpr->wrapper == JSW_UNCONDITIONAL)\n\t\t\tappendStringInfoString(context->buf, \" WITH UNCONDITIONAL WRAPPER\");\n\t\t/* The default */\n\t\telse if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)\n\t\t\tappendStringInfoString(context->buf, \" WITHOUT WRAPPER\");\n\n\t\tif (jsexpr->omit_quotes)\n\t\t\tappendStringInfoString(context->buf, \" OMIT QUOTES\");\n\t\t/* The default */\n\t\telse\n\t\t\tappendStringInfoString(context->buf, \" KEEP QUOTES\");\n\t}\n\n\tif (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)\n\t\tget_json_behavior(jsexpr->on_empty, context, \"EMPTY\");\n\n\tif (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)\n\t\tget_json_behavior(jsexpr->on_error, context, \"ERROR\");\n}\n\n/* ----------\n * get_rule_expr\t\t\t- Parse back an expression\n *\n * Note: showimplicit determines whether we display any implicit cast that\n * is present at the top of the expression tree.  It is a passed argument,\n * not a field of the context struct, because we change the value as we\n * recurse down into the expression.  In general we suppress implicit casts\n * when the result type is known with certainty (eg, the arguments of an\n * OR must be boolean).  We display implicit casts for arguments of functions\n * and operators, since this is needed to be certain that the same function\n * or operator will be chosen when the expression is re-parsed.\n * ----------\n */\nstatic void\nget_rule_expr(Node *node, deparse_context *context,\n\t\t\t  bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (node == NULL)\n\t\treturn;\n\n\t/* Guard against excessively long or deeply-nested queries */\n\tCHECK_FOR_INTERRUPTS();\n\tcheck_stack_depth();\n\n\t/*\n\t * Each level of get_rule_expr must emit an indivisible term\n\t * (parenthesized if necessary) to ensure result is reparsed into the same\n\t * expression tree.  The only exception is that when the input is a List,\n\t * we emit the component items comma-separated with no surrounding\n\t * decoration; this is convenient for most callers.\n\t */\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_Var:\n\t\t\t(void) get_variable((Var *) node, 0, false, context);\n\t\t\tbreak;\n\n\t\tcase T_Const:\n\t\t\tget_const_expr((Const *) node, context, 0);\n\t\t\tbreak;\n\n\t\tcase T_Param:\n\t\t\tget_parameter((Param *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_Aggref:\n\t\t\tget_agg_expr((Aggref *) node, context, (Aggref *) node);\n\t\t\tbreak;\n\n\t\tcase T_GroupingFunc:\n\t\t\t{\n\t\t\t\tGroupingFunc *gexpr = (GroupingFunc *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"GROUPING(\");\n\t\t\t\tget_rule_expr((Node *) gexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_WindowFunc:\n\t\t\tget_windowfunc_expr((WindowFunc *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_MergeSupportFunc:\n\t\t\tappendStringInfoString(buf, \"MERGE_ACTION()\");\n\t\t\tbreak;\n\n\t\tcase T_SubscriptingRef:\n\t\t\t{\n\t\t\t\tSubscriptingRef *sbsref = (SubscriptingRef *) node;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * If the argument is a CaseTestExpr, we must be inside a\n\t\t\t\t * FieldStore, ie, we are assigning to an element of an array\n\t\t\t\t * within a composite column.  Since we already punted on\n\t\t\t\t * displaying the FieldStore's target information, just punt\n\t\t\t\t * here too, and display only the assignment source\n\t\t\t\t * expression.\n\t\t\t\t */\n\t\t\t\tif (IsA(sbsref->refexpr, CaseTestExpr))\n\t\t\t\t{\n\t\t\t\t\tAssert(sbsref->refassgnexpr);\n\t\t\t\t\tget_rule_expr((Node *) sbsref->refassgnexpr,\n\t\t\t\t\t\t\t\t  context, showimplicit);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * Parenthesize the argument unless it's a simple Var or a\n\t\t\t\t * FieldSelect.  (In particular, if it's another\n\t\t\t\t * SubscriptingRef, we *must* parenthesize to avoid\n\t\t\t\t * confusion.)\n\t\t\t\t */\n\t\t\t\tneed_parens = !IsA(sbsref->refexpr, Var) &&\n\t\t\t\t\t!IsA(sbsref->refexpr, FieldSelect);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr((Node *) sbsref->refexpr, context, showimplicit);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t\t/*\n\t\t\t\t * If there's a refassgnexpr, we want to print the node in the\n\t\t\t\t * format \"container[subscripts] := refassgnexpr\".  This is\n\t\t\t\t * not legal SQL, so decompilation of INSERT or UPDATE\n\t\t\t\t * statements should always use processIndirection as part of\n\t\t\t\t * the statement-level syntax.  We should only see this when\n\t\t\t\t * EXPLAIN tries to print the targetlist of a plan resulting\n\t\t\t\t * from such a statement.\n\t\t\t\t */\n\t\t\t\tif (sbsref->refassgnexpr)\n\t\t\t\t{\n\t\t\t\t\tNode\t   *refassgnexpr;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Use processIndirection to print this node's subscripts\n\t\t\t\t\t * as well as any additional field selections or\n\t\t\t\t\t * subscripting in immediate descendants.  It returns the\n\t\t\t\t\t * RHS expr that is actually being \"assigned\".\n\t\t\t\t\t */\n\t\t\t\t\trefassgnexpr = processIndirection(node, context);\n\t\t\t\t\tappendStringInfoString(buf, \" := \");\n\t\t\t\t\tget_rule_expr(refassgnexpr, context, showimplicit);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* Just an ordinary container fetch, so print subscripts */\n\t\t\t\t\tprintSubscripts(sbsref, context);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_FuncExpr:\n\t\t\tget_func_expr((FuncExpr *) node, context, showimplicit);\n\t\t\tbreak;\n\n\t\tcase T_NamedArgExpr:\n\t\t\t{\n\t\t\t\tNamedArgExpr *na = (NamedArgExpr *) node;\n\n\t\t\t\tappendStringInfo(buf, \"%s => \", quote_identifier(na->name));\n\t\t\t\tget_rule_expr((Node *) na->arg, context, showimplicit);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_OpExpr:\n\t\t\tget_oper_expr((OpExpr *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_DistinctExpr:\n\t\t\t{\n\t\t\t\tDistinctExpr *expr = (DistinctExpr *) node;\n\t\t\t\tList\t   *args = expr->args;\n\t\t\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\t\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren(arg1, context, true, node);\n\t\t\t\tappendStringInfoString(buf, \" IS DISTINCT FROM \");\n\t\t\t\tget_rule_expr_paren(arg2, context, true, node);\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_NullIfExpr:\n\t\t\t{\n\t\t\t\tNullIfExpr *nullifexpr = (NullIfExpr *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"NULLIF(\");\n\t\t\t\tget_rule_expr((Node *) nullifexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ScalarArrayOpExpr:\n\t\t\t{\n\t\t\t\tScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;\n\t\t\t\tList\t   *args = expr->args;\n\t\t\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\t\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren(arg1, context, true, node);\n\t\t\t\tappendStringInfo(buf, \" %s %s (\",\n\t\t\t\t\t\t\t\t generate_operator_name(expr->opno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\texprType(arg1),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tget_base_element_type(exprType(arg2))),\n\t\t\t\t\t\t\t\t expr->useOr ? \"ANY\" : \"ALL\");\n\t\t\t\tget_rule_expr_paren(arg2, context, true, node);\n\n\t\t\t\t/*\n\t\t\t\t * There's inherent ambiguity in \"x op ANY/ALL (y)\" when y is\n\t\t\t\t * a bare sub-SELECT.  Since we're here, the sub-SELECT must\n\t\t\t\t * be meant as a scalar sub-SELECT yielding an array value to\n\t\t\t\t * be used in ScalarArrayOpExpr; but the grammar will\n\t\t\t\t * preferentially interpret such a construct as an ANY/ALL\n\t\t\t\t * SubLink.  To prevent misparsing the output that way, insert\n\t\t\t\t * a dummy coercion (which will be stripped by parse analysis,\n\t\t\t\t * so no inefficiency is added in dump and reload).  This is\n\t\t\t\t * indeed most likely what the user wrote to get the construct\n\t\t\t\t * accepted in the first place.\n\t\t\t\t */\n\t\t\t\tif (IsA(arg2, SubLink) &&\n\t\t\t\t\t((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)\n\t\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(exprType(arg2),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  exprTypmod(arg2)));\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_BoolExpr:\n\t\t\t{\n\t\t\t\tBoolExpr   *expr = (BoolExpr *) node;\n\t\t\t\tNode\t   *first_arg = linitial(expr->args);\n\t\t\t\tListCell   *arg;\n\n\t\t\t\tswitch (expr->boolop)\n\t\t\t\t{\n\t\t\t\t\tcase AND_EXPR:\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tget_rule_expr_paren(first_arg, context,\n\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\tfor_each_from(arg, expr->args, 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" AND \");\n\t\t\t\t\t\t\tget_rule_expr_paren((Node *) lfirst(arg), context,\n\t\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase OR_EXPR:\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tget_rule_expr_paren(first_arg, context,\n\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\tfor_each_from(arg, expr->args, 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" OR \");\n\t\t\t\t\t\t\tget_rule_expr_paren((Node *) lfirst(arg), context,\n\t\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase NOT_EXPR:\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tappendStringInfoString(buf, \"NOT \");\n\t\t\t\t\t\tget_rule_expr_paren(first_arg, context,\n\t\t\t\t\t\t\t\t\t\t\tfalse, node);\n\t\t\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telog(ERROR, \"unrecognized boolop: %d\",\n\t\t\t\t\t\t\t (int) expr->boolop);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_SubLink:\n\t\t\tget_sublink_expr((SubLink *) node, context);\n\t\t\tbreak;\n\n\t\tcase T_SubPlan:\n\t\t\t{\n\t\t\t\tSubPlan    *subplan = (SubPlan *) node;\n\n\t\t\t\t/*\n\t\t\t\t * We cannot see an already-planned subplan in rule deparsing,\n\t\t\t\t * only while EXPLAINing a query plan.  We don't try to\n\t\t\t\t * reconstruct the original SQL, just reference the subplan\n\t\t\t\t * that appears elsewhere in EXPLAIN's result.  It does seem\n\t\t\t\t * useful to show the subLinkType and testexpr (if any), and\n\t\t\t\t * we also note whether the subplan will be hashed.\n\t\t\t\t */\n\t\t\t\tswitch (subplan->subLinkType)\n\t\t\t\t{\n\t\t\t\t\tcase EXISTS_SUBLINK:\n\t\t\t\t\t\tappendStringInfoString(buf, \"EXISTS(\");\n\t\t\t\t\t\tAssert(subplan->testexpr == NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ALL_SUBLINK:\n\t\t\t\t\t\tappendStringInfoString(buf, \"(ALL \");\n\t\t\t\t\t\tAssert(subplan->testexpr != NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ANY_SUBLINK:\n\t\t\t\t\t\tappendStringInfoString(buf, \"(ANY \");\n\t\t\t\t\t\tAssert(subplan->testexpr != NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ROWCOMPARE_SUBLINK:\n\t\t\t\t\t\t/* Parenthesizing the testexpr seems sufficient */\n\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tAssert(subplan->testexpr != NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase EXPR_SUBLINK:\n\t\t\t\t\t\t/* No need to decorate these subplan references */\n\t\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\t\t\tAssert(subplan->testexpr == NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase MULTIEXPR_SUBLINK:\n\t\t\t\t\t\t/* MULTIEXPR isn't executed in the normal way */\n\t\t\t\t\t\tappendStringInfoString(buf, \"(rescan \");\n\t\t\t\t\t\tAssert(subplan->testexpr == NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ARRAY_SUBLINK:\n\t\t\t\t\t\tappendStringInfoString(buf, \"ARRAY(\");\n\t\t\t\t\t\tAssert(subplan->testexpr == NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase CTE_SUBLINK:\n\t\t\t\t\t\t/* This case is unreachable within expressions */\n\t\t\t\t\t\tappendStringInfoString(buf, \"CTE(\");\n\t\t\t\t\t\tAssert(subplan->testexpr == NULL);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (subplan->testexpr != NULL)\n\t\t\t\t{\n\t\t\t\t\tdeparse_namespace *dpns;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Push SubPlan into ancestors list while deparsing\n\t\t\t\t\t * testexpr, so that we can handle PARAM_EXEC references\n\t\t\t\t\t * to the SubPlan's paramIds.  (This makes it look like\n\t\t\t\t\t * the SubPlan is an \"ancestor\" of the current plan node,\n\t\t\t\t\t * which is a little weird, but it does no harm.)  In this\n\t\t\t\t\t * path, we don't need to mention the SubPlan explicitly,\n\t\t\t\t\t * because the referencing Params will show its existence.\n\t\t\t\t\t */\n\t\t\t\t\tdpns = (deparse_namespace *) linitial(context->namespaces);\n\t\t\t\t\tdpns->ancestors = lcons(subplan, dpns->ancestors);\n\n\t\t\t\t\tget_rule_expr(subplan->testexpr, context, showimplicit);\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t\t\tdpns->ancestors = list_delete_first(dpns->ancestors);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* No referencing Params, so show the SubPlan's name */\n\t\t\t\t\tif (subplan->useHashTable)\n\t\t\t\t\t\tappendStringInfo(buf, \"hashed %s)\", subplan->plan_name);\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfo(buf, \"%s)\", subplan->plan_name);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_AlternativeSubPlan:\n\t\t\t{\n\t\t\t\tAlternativeSubPlan *asplan = (AlternativeSubPlan *) node;\n\t\t\t\tListCell   *lc;\n\n\t\t\t\t/*\n\t\t\t\t * This case cannot be reached in normal usage, since no\n\t\t\t\t * AlternativeSubPlan can appear either in parsetrees or\n\t\t\t\t * finished plan trees.  We keep it just in case somebody\n\t\t\t\t * wants to use this code to print planner data structures.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"(alternatives: \");\n\t\t\t\tforeach(lc, asplan->subplans)\n\t\t\t\t{\n\t\t\t\t\tSubPlan    *splan = lfirst_node(SubPlan, lc);\n\n\t\t\t\t\tif (splan->useHashTable)\n\t\t\t\t\t\tappendStringInfo(buf, \"hashed %s\", splan->plan_name);\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfoString(buf, splan->plan_name);\n\t\t\t\t\tif (lnext(asplan->subplans, lc))\n\t\t\t\t\t\tappendStringInfoString(buf, \" or \");\n\t\t\t\t}\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_FieldSelect:\n\t\t\t{\n\t\t\t\tFieldSelect *fselect = (FieldSelect *) node;\n\t\t\t\tNode\t   *arg = (Node *) fselect->arg;\n\t\t\t\tint\t\t\tfno = fselect->fieldnum;\n\t\t\t\tconst char *fieldname;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * Parenthesize the argument unless it's an SubscriptingRef or\n\t\t\t\t * another FieldSelect.  Note in particular that it would be\n\t\t\t\t * WRONG to not parenthesize a Var argument; simplicity is not\n\t\t\t\t * the issue here, having the right number of names is.\n\t\t\t\t */\n\t\t\t\tneed_parens = !IsA(arg, SubscriptingRef) &&\n\t\t\t\t\t!IsA(arg, FieldSelect);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr(arg, context, true);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t\t/*\n\t\t\t\t * Get and print the field name.\n\t\t\t\t */\n\t\t\t\tfieldname = get_name_for_var_field((Var *) arg, fno,\n\t\t\t\t\t\t\t\t\t\t\t\t   0, context);\n\t\t\t\tappendStringInfo(buf, \".%s\", quote_identifier(fieldname));\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_FieldStore:\n\t\t\t{\n\t\t\t\tFieldStore *fstore = (FieldStore *) node;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * There is no good way to represent a FieldStore as real SQL,\n\t\t\t\t * so decompilation of INSERT or UPDATE statements should\n\t\t\t\t * always use processIndirection as part of the\n\t\t\t\t * statement-level syntax.  We should only get here when\n\t\t\t\t * EXPLAIN tries to print the targetlist of a plan resulting\n\t\t\t\t * from such a statement.  The plan case is even harder than\n\t\t\t\t * ordinary rules would be, because the planner tries to\n\t\t\t\t * collapse multiple assignments to the same field or subfield\n\t\t\t\t * into one FieldStore; so we can see a list of target fields\n\t\t\t\t * not just one, and the arguments could be FieldStores\n\t\t\t\t * themselves.  We don't bother to try to print the target\n\t\t\t\t * field names; we just print the source arguments, with a\n\t\t\t\t * ROW() around them if there's more than one.  This isn't\n\t\t\t\t * terribly complete, but it's probably good enough for\n\t\t\t\t * EXPLAIN's purposes; especially since anything more would be\n\t\t\t\t * either hopelessly confusing or an even poorer\n\t\t\t\t * representation of what the plan is actually doing.\n\t\t\t\t */\n\t\t\t\tneed_parens = (list_length(fstore->newvals) != 1);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoString(buf, \"ROW(\");\n\t\t\t\tget_rule_expr((Node *) fstore->newvals, context, showimplicit);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_RelabelType:\n\t\t\t{\n\t\t\t\tRelabelType *relabel = (RelabelType *) node;\n\t\t\t\tNode\t   *arg = (Node *) relabel->arg;\n\n\t\t\t\tif (relabel->relabelformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  relabel->resulttype,\n\t\t\t\t\t\t\t\t\t  relabel->resulttypmod,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoerceViaIO:\n\t\t\t{\n\t\t\t\tCoerceViaIO *iocoerce = (CoerceViaIO *) node;\n\t\t\t\tNode\t   *arg = (Node *) iocoerce->arg;\n\n\t\t\t\tif (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  iocoerce->resulttype,\n\t\t\t\t\t\t\t\t\t  -1,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ArrayCoerceExpr:\n\t\t\t{\n\t\t\t\tArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;\n\t\t\t\tNode\t   *arg = (Node *) acoerce->arg;\n\n\t\t\t\tif (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  acoerce->resulttype,\n\t\t\t\t\t\t\t\t\t  acoerce->resulttypmod,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ConvertRowtypeExpr:\n\t\t\t{\n\t\t\t\tConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;\n\t\t\t\tNode\t   *arg = (Node *) convert->arg;\n\n\t\t\t\tif (convert->convertformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr_paren(arg, context, false, node);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  convert->resulttype, -1,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CollateExpr:\n\t\t\t{\n\t\t\t\tCollateExpr *collate = (CollateExpr *) node;\n\t\t\t\tNode\t   *arg = (Node *) collate->arg;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren(arg, context, showimplicit, node);\n\t\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t\t generate_collation_name(collate->collOid));\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CaseExpr:\n\t\t\t{\n\t\t\t\tCaseExpr   *caseexpr = (CaseExpr *) node;\n\t\t\t\tListCell   *temp;\n\n\t\t\t\tappendContextKeyword(context, \"CASE\",\n\t\t\t\t\t\t\t\t\t 0, PRETTYINDENT_VAR, 0);\n\t\t\t\tif (caseexpr->arg)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\t\tget_rule_expr((Node *) caseexpr->arg, context, true);\n\t\t\t\t}\n\t\t\t\tforeach(temp, caseexpr->args)\n\t\t\t\t{\n\t\t\t\t\tCaseWhen   *when = (CaseWhen *) lfirst(temp);\n\t\t\t\t\tNode\t   *w = (Node *) when->expr;\n\n\t\t\t\t\tif (caseexpr->arg)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * The parser should have produced WHEN clauses of the\n\t\t\t\t\t\t * form \"CaseTestExpr = RHS\", possibly with an\n\t\t\t\t\t\t * implicit coercion inserted above the CaseTestExpr.\n\t\t\t\t\t\t * For accurate decompilation of rules it's essential\n\t\t\t\t\t\t * that we show just the RHS.  However in an\n\t\t\t\t\t\t * expression that's been through the optimizer, the\n\t\t\t\t\t\t * WHEN clause could be almost anything (since the\n\t\t\t\t\t\t * equality operator could have been expanded into an\n\t\t\t\t\t\t * inline function).  If we don't recognize the form\n\t\t\t\t\t\t * of the WHEN clause, just punt and display it as-is.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (IsA(w, OpExpr))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tList\t   *args = ((OpExpr *) w)->args;\n\n\t\t\t\t\t\t\tif (list_length(args) == 2 &&\n\t\t\t\t\t\t\t\tIsA(strip_implicit_coercions(linitial(args)),\n\t\t\t\t\t\t\t\t\tCaseTestExpr))\n\t\t\t\t\t\t\t\tw = (Node *) lsecond(args);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!PRETTY_INDENT(context))\n\t\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\t\tappendContextKeyword(context, \"WHEN \",\n\t\t\t\t\t\t\t\t\t\t 0, 0, 0);\n\t\t\t\t\tget_rule_expr(w, context, false);\n\t\t\t\t\tappendStringInfoString(buf, \" THEN \");\n\t\t\t\t\tget_rule_expr((Node *) when->result, context, true);\n\t\t\t\t}\n\t\t\t\tif (!PRETTY_INDENT(context))\n\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tappendContextKeyword(context, \"ELSE \",\n\t\t\t\t\t\t\t\t\t 0, 0, 0);\n\t\t\t\tget_rule_expr((Node *) caseexpr->defresult, context, true);\n\t\t\t\tif (!PRETTY_INDENT(context))\n\t\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tappendContextKeyword(context, \"END\",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_VAR, 0, 0);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CaseTestExpr:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Normally we should never get here, since for expressions\n\t\t\t\t * that can contain this node type we attempt to avoid\n\t\t\t\t * recursing to it.  But in an optimized expression we might\n\t\t\t\t * be unable to avoid that (see comments for CaseExpr).  If we\n\t\t\t\t * do see one, print it as CASE_TEST_EXPR.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"CASE_TEST_EXPR\");\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ArrayExpr:\n\t\t\t{\n\t\t\t\tArrayExpr  *arrayexpr = (ArrayExpr *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"ARRAY[\");\n\t\t\t\tget_rule_expr((Node *) arrayexpr->elements, context, true);\n\t\t\t\tappendStringInfoChar(buf, ']');\n\n\t\t\t\t/*\n\t\t\t\t * If the array isn't empty, we assume its elements are\n\t\t\t\t * coerced to the desired type.  If it's empty, though, we\n\t\t\t\t * need an explicit coercion to the array type.\n\t\t\t\t */\n\t\t\t\tif (arrayexpr->elements == NIL)\n\t\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(arrayexpr->array_typeid, -1));\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_RowExpr:\n\t\t\t{\n\t\t\t\tRowExpr    *rowexpr = (RowExpr *) node;\n\t\t\t\tTupleDesc\ttupdesc = NULL;\n\t\t\t\tListCell   *arg;\n\t\t\t\tint\t\t\ti;\n\t\t\t\tchar\t   *sep;\n\n\t\t\t\t/*\n\t\t\t\t * If it's a named type and not RECORD, we may have to skip\n\t\t\t\t * dropped columns and/or claim there are NULLs for added\n\t\t\t\t * columns.\n\t\t\t\t */\n\t\t\t\tif (rowexpr->row_typeid != RECORDOID)\n\t\t\t\t{\n\t\t\t\t\ttupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);\n\t\t\t\t\tAssert(list_length(rowexpr->args) <= tupdesc->natts);\n\t\t\t\t}\n\n\t\t\t\t/* Precompute deparse ns and whether we even need to try mapping */\n\t\t\t\tdeparse_namespace *dpns = (context->namespaces != NIL)\n\t\t\t\t\t? (deparse_namespace *) linitial(context->namespaces) : NULL;\n\t\t\t\tbool try_map = (dpns && dpns->plan == NULL && dpns_has_named_join(dpns));\n\n\t\t\t\t/*\n\t\t\t\t * SQL99 allows \"ROW\" to be omitted when there is more than\n\t\t\t\t * one column, but for simplicity we always print it.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"ROW(\");\n\t\t\t\tsep = \"\";\n\t\t\t\ti = 0;\n\t\t\t\tforeach(arg, rowexpr->args)\n\t\t\t\t{\n\t\t\t\t\tNode\t   *e = (Node *) lfirst(arg);\n\n\t\t\t\t\tif (tupdesc == NULL ||\n\t\t\t\t\t\t!TupleDescAttr(tupdesc, i)->attisdropped)\n\t\t\t\t\t{\n\t\t\t\t\t\tappendStringInfoString(buf, sep);\n\n\t\t\t\t\t\t/* PG18: if element is a simple base Var, set its SYN to the JOIN alias */\n\t\t\t\t\t\tif (try_map)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tVar *v = unwrap_simple_var(e);\n\t\t\t\t\t\t\tif (v)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmap_var_through_join_alias(dpns, v);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t/* Whole-row Vars need special treatment here */\n\t\t\t\t\t\tget_rule_expr_toplevel(e, context, true);\n\t\t\t\t\t\tsep = \", \";\n\t\t\t\t\t}\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t\tif (tupdesc != NULL)\n\t\t\t\t{\n\t\t\t\t\twhile (i < tupdesc->natts)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!TupleDescAttr(tupdesc, i)->attisdropped)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\t\t\tappendStringInfoString(buf, \"NULL\");\n\t\t\t\t\t\t\tsep = \", \";\n\t\t\t\t\t\t}\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tReleaseTupleDesc(tupdesc);\n\t\t\t\t}\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tif (rowexpr->row_format == COERCE_EXPLICIT_CAST)\n\t\t\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(rowexpr->row_typeid, -1));\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_RowCompareExpr:\n\t\t\t{\n\t\t\t\tRowCompareExpr *rcexpr = (RowCompareExpr *) node;\n\n\t\t\t\t/*\n\t\t\t\t * SQL99 allows \"ROW\" to be omitted when there is more than\n\t\t\t\t * one column, but for simplicity we always print it.  Within\n\t\t\t\t * a ROW expression, whole-row Vars need special treatment, so\n\t\t\t\t * use get_rule_list_toplevel.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"(ROW(\");\n\t\t\t\tget_rule_list_toplevel(rcexpr->largs, context, true);\n\n\t\t\t\t/*\n\t\t\t\t * We assume that the name of the first-column operator will\n\t\t\t\t * do for all the rest too.  This is definitely open to\n\t\t\t\t * failure, eg if some but not all operators were renamed\n\t\t\t\t * since the construct was parsed, but there seems no way to\n\t\t\t\t * be perfect.\n\t\t\t\t */\n\t\t\t\tappendStringInfo(buf, \") %s ROW(\",\n\t\t\t\t\t\t\t\t generate_operator_name(linitial_oid(rcexpr->opnos),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->largs)),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->rargs))));\n\t\t\t\tget_rule_list_toplevel(rcexpr->rargs, context, true);\n\t\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoalesceExpr:\n\t\t\t{\n\t\t\t\tCoalesceExpr *coalesceexpr = (CoalesceExpr *) node;\n\n\t\t\t\tappendStringInfoString(buf, \"COALESCE(\");\n\t\t\t\tget_rule_expr((Node *) coalesceexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_MinMaxExpr:\n\t\t\t{\n\t\t\t\tMinMaxExpr *minmaxexpr = (MinMaxExpr *) node;\n\n\t\t\t\tswitch (minmaxexpr->op)\n\t\t\t\t{\n\t\t\t\t\tcase IS_GREATEST:\n\t\t\t\t\t\tappendStringInfoString(buf, \"GREATEST(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_LEAST:\n\t\t\t\t\t\tappendStringInfoString(buf, \"LEAST(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tget_rule_expr((Node *) minmaxexpr->args, context, true);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_SQLValueFunction:\n\t\t\t{\n\t\t\t\tSQLValueFunction *svf = (SQLValueFunction *) node;\n\n\t\t\t\t/*\n\t\t\t\t * Note: this code knows that typmod for time, timestamp, and\n\t\t\t\t * timestamptz just prints as integer.\n\t\t\t\t */\n\t\t\t\tswitch (svf->op)\n\t\t\t\t{\n\t\t\t\t\tcase SVFOP_CURRENT_DATE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_DATE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIME:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_TIME\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIME_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"CURRENT_TIME(%d)\", svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIMESTAMP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_TIMESTAMP\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_TIMESTAMP_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"CURRENT_TIMESTAMP(%d)\",\n\t\t\t\t\t\t\t\t\t\t svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIME:\n\t\t\t\t\t\tappendStringInfoString(buf, \"LOCALTIME\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIME_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"LOCALTIME(%d)\", svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIMESTAMP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"LOCALTIMESTAMP\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_LOCALTIMESTAMP_N:\n\t\t\t\t\t\tappendStringInfo(buf, \"LOCALTIMESTAMP(%d)\",\n\t\t\t\t\t\t\t\t\t\t svf->typmod);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_ROLE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_ROLE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_USER:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_USER\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_USER:\n\t\t\t\t\t\tappendStringInfoString(buf, \"USER\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_SESSION_USER:\n\t\t\t\t\t\tappendStringInfoString(buf, \"SESSION_USER\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_CATALOG:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_CATALOG\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase SVFOP_CURRENT_SCHEMA:\n\t\t\t\t\t\tappendStringInfoString(buf, \"CURRENT_SCHEMA\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_XmlExpr:\n\t\t\t{\n\t\t\t\tXmlExpr    *xexpr = (XmlExpr *) node;\n\t\t\t\tbool\t\tneedcomma = false;\n\t\t\t\tListCell   *arg;\n\t\t\t\tListCell   *narg;\n\t\t\t\tConst\t   *con;\n\n\t\t\t\tswitch (xexpr->op)\n\t\t\t\t{\n\t\t\t\t\tcase IS_XMLCONCAT:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLCONCAT(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLELEMENT:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLELEMENT(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLFOREST:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLFOREST(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLPARSE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLPARSE(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLPI:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLPI(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLROOT:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLROOT(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_XMLSERIALIZE:\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLSERIALIZE(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_DOCUMENT:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)\n\t\t\t\t{\n\t\t\t\t\tif (xexpr->xmloption == XMLOPTION_DOCUMENT)\n\t\t\t\t\t\tappendStringInfoString(buf, \"DOCUMENT \");\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfoString(buf, \"CONTENT \");\n\t\t\t\t}\n\t\t\t\tif (xexpr->name)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfo(buf, \"NAME %s\",\n\t\t\t\t\t\t\t\t\t quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));\n\t\t\t\t\tneedcomma = true;\n\t\t\t\t}\n\t\t\t\tif (xexpr->named_args)\n\t\t\t\t{\n\t\t\t\t\tif (xexpr->op != IS_XMLFOREST)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (needcomma)\n\t\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\t\tappendStringInfoString(buf, \"XMLATTRIBUTES(\");\n\t\t\t\t\t\tneedcomma = false;\n\t\t\t\t\t}\n\t\t\t\t\tforboth(arg, xexpr->named_args, narg, xexpr->arg_names)\n\t\t\t\t\t{\n\t\t\t\t\t\tNode\t   *e = (Node *) lfirst(arg);\n\t\t\t\t\t\tchar\t   *argname = strVal(lfirst(narg));\n\n\t\t\t\t\t\tif (needcomma)\n\t\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\t\tget_rule_expr((Node *) e, context, true);\n\t\t\t\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t\t\t\t quote_identifier(map_xml_name_to_sql_identifier(argname)));\n\t\t\t\t\t\tneedcomma = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (xexpr->op != IS_XMLFOREST)\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t}\n\t\t\t\tif (xexpr->args)\n\t\t\t\t{\n\t\t\t\t\tif (needcomma)\n\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\tswitch (xexpr->op)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase IS_XMLCONCAT:\n\t\t\t\t\t\tcase IS_XMLELEMENT:\n\t\t\t\t\t\tcase IS_XMLFOREST:\n\t\t\t\t\t\tcase IS_XMLPI:\n\t\t\t\t\t\tcase IS_XMLSERIALIZE:\n\t\t\t\t\t\t\t/* no extra decoration needed */\n\t\t\t\t\t\t\tget_rule_expr((Node *) xexpr->args, context, true);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_XMLPARSE:\n\t\t\t\t\t\t\tAssert(list_length(xexpr->args) == 2);\n\n\t\t\t\t\t\t\tget_rule_expr((Node *) linitial(xexpr->args),\n\t\t\t\t\t\t\t\t\t\t  context, true);\n\n\t\t\t\t\t\t\tcon = lsecond_node(Const, xexpr->args);\n\t\t\t\t\t\t\tAssert(!con->constisnull);\n\t\t\t\t\t\t\tif (DatumGetBool(con->constvalue))\n\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \" PRESERVE WHITESPACE\");\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \" STRIP WHITESPACE\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_XMLROOT:\n\t\t\t\t\t\t\tAssert(list_length(xexpr->args) == 3);\n\n\t\t\t\t\t\t\tget_rule_expr((Node *) linitial(xexpr->args),\n\t\t\t\t\t\t\t\t\t\t  context, true);\n\n\t\t\t\t\t\t\tappendStringInfoString(buf, \", VERSION \");\n\t\t\t\t\t\t\tcon = (Const *) lsecond(xexpr->args);\n\t\t\t\t\t\t\tif (IsA(con, Const) &&\n\t\t\t\t\t\t\t\tcon->constisnull)\n\t\t\t\t\t\t\t\tappendStringInfoString(buf, \"NO VALUE\");\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tget_rule_expr((Node *) con, context, false);\n\n\t\t\t\t\t\t\tcon = lthird_node(Const, xexpr->args);\n\t\t\t\t\t\t\tif (con->constisnull)\n\t\t\t\t\t\t\t\t /* suppress STANDALONE NO VALUE */ ;\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tswitch (DatumGetInt32(con->constvalue))\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tcase XML_STANDALONE_YES:\n\t\t\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \", STANDALONE YES\");\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase XML_STANDALONE_NO:\n\t\t\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \", STANDALONE NO\");\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase XML_STANDALONE_NO_VALUE:\n\t\t\t\t\t\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \", STANDALONE NO VALUE\");\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_DOCUMENT:\n\t\t\t\t\t\t\tget_rule_expr_paren((Node *) xexpr->args, context, false, node);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (xexpr->op == IS_XMLSERIALIZE)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t\t\t format_type_with_typemod(xexpr->type,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  xexpr->typmod));\n\t\t\t\t\tif (xexpr->indent)\n\t\t\t\t\t\tappendStringInfoString(buf, \" INDENT\");\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfoString(buf, \" NO INDENT\");\n\t\t\t\t}\n\n\t\t\t\tif (xexpr->op == IS_DOCUMENT)\n\t\t\t\t\tappendStringInfoString(buf, \" IS DOCUMENT\");\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_NullTest:\n\t\t\t{\n\t\t\t\tNullTest   *ntest = (NullTest *) node;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren((Node *) ntest->arg, context, true, node);\n\n\t\t\t\t/*\n\t\t\t\t * For scalar inputs, we prefer to print as IS [NOT] NULL,\n\t\t\t\t * which is shorter and traditional.  If it's a rowtype input\n\t\t\t\t * but we're applying a scalar test, must print IS [NOT]\n\t\t\t\t * DISTINCT FROM NULL to be semantically correct.\n\t\t\t\t */\n\t\t\t\tif (ntest->argisrow ||\n\t\t\t\t\t!type_is_rowtype(exprType((Node *) ntest->arg)))\n\t\t\t\t{\n\t\t\t\t\tswitch (ntest->nulltesttype)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase IS_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_NOT_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\telog(ERROR, \"unrecognized nulltesttype: %d\",\n\t\t\t\t\t\t\t\t (int) ntest->nulltesttype);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tswitch (ntest->nulltesttype)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase IS_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT DISTINCT FROM NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase IS_NOT_NULL:\n\t\t\t\t\t\t\tappendStringInfoString(buf, \" IS DISTINCT FROM NULL\");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\telog(ERROR, \"unrecognized nulltesttype: %d\",\n\t\t\t\t\t\t\t\t (int) ntest->nulltesttype);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_BooleanTest:\n\t\t\t{\n\t\t\t\tBooleanTest *btest = (BooleanTest *) node;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr_paren((Node *) btest->arg, context, false, node);\n\t\t\t\tswitch (btest->booltesttype)\n\t\t\t\t{\n\t\t\t\t\tcase IS_TRUE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS TRUE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NOT_TRUE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT TRUE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_FALSE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS FALSE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NOT_FALSE:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT FALSE\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_UNKNOWN:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS UNKNOWN\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase IS_NOT_UNKNOWN:\n\t\t\t\t\t\tappendStringInfoString(buf, \" IS NOT UNKNOWN\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telog(ERROR, \"unrecognized booltesttype: %d\",\n\t\t\t\t\t\t\t (int) btest->booltesttype);\n\t\t\t\t}\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoerceToDomain:\n\t\t\t{\n\t\t\t\tCoerceToDomain *ctest = (CoerceToDomain *) node;\n\t\t\t\tNode\t   *arg = (Node *) ctest->arg;\n\n\t\t\t\tif (ctest->coercionformat == COERCE_IMPLICIT_CAST &&\n\t\t\t\t\t!showimplicit)\n\t\t\t\t{\n\t\t\t\t\t/* don't show the implicit cast */\n\t\t\t\t\tget_rule_expr(arg, context, false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t\t\t\t  ctest->resulttype,\n\t\t\t\t\t\t\t\t\t  ctest->resulttypmod,\n\t\t\t\t\t\t\t\t\t  node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_CoerceToDomainValue:\n\t\t\tappendStringInfoString(buf, \"VALUE\");\n\t\t\tbreak;\n\n\t\tcase T_SetToDefault:\n\t\t\tappendStringInfoString(buf, \"DEFAULT\");\n\t\t\tbreak;\n\n\t\tcase T_CurrentOfExpr:\n\t\t\t{\n\t\t\t\tCurrentOfExpr *cexpr = (CurrentOfExpr *) node;\n\n\t\t\t\tif (cexpr->cursor_name)\n\t\t\t\t\tappendStringInfo(buf, \"CURRENT OF %s\",\n\t\t\t\t\t\t\t\t\t quote_identifier(cexpr->cursor_name));\n\t\t\t\telse\n\t\t\t\t\tappendStringInfo(buf, \"CURRENT OF $%d\",\n\t\t\t\t\t\t\t\t\t cexpr->cursor_param);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_NextValueExpr:\n\t\t\t{\n\t\t\t\tNextValueExpr *nvexpr = (NextValueExpr *) node;\n\n\t\t\t\t/*\n\t\t\t\t * This isn't exactly nextval(), but that seems close enough\n\t\t\t\t * for EXPLAIN's purposes.\n\t\t\t\t */\n\t\t\t\tappendStringInfoString(buf, \"nextval(\");\n\t\t\t\tsimple_quote_literal(buf,\n\t\t\t\t\t\t\t\t\t generate_relation_name(nvexpr->seqid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNIL));\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_InferenceElem:\n\t\t\t{\n\t\t\t\tInferenceElem *iexpr = (InferenceElem *) node;\n\t\t\t\tbool\t\tsave_varprefix;\n\t\t\t\tbool\t\tneed_parens;\n\n\t\t\t\t/*\n\t\t\t\t * InferenceElem can only refer to target relation, so a\n\t\t\t\t * prefix is not useful, and indeed would cause parse errors.\n\t\t\t\t */\n\t\t\t\tsave_varprefix = context->varprefix;\n\t\t\t\tcontext->varprefix = false;\n\n\t\t\t\t/*\n\t\t\t\t * Parenthesize the element unless it's a simple Var or a bare\n\t\t\t\t * function call.  Follows pg_get_indexdef_worker().\n\t\t\t\t */\n\t\t\t\tneed_parens = !IsA(iexpr->expr, Var);\n\t\t\t\tif (IsA(iexpr->expr, FuncExpr) &&\n\t\t\t\t\t((FuncExpr *) iexpr->expr)->funcformat ==\n\t\t\t\t\tCOERCE_EXPLICIT_CALL)\n\t\t\t\t\tneed_parens = false;\n\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_rule_expr((Node *) iexpr->expr,\n\t\t\t\t\t\t\t  context, false);\n\t\t\t\tif (need_parens)\n\t\t\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\t\tcontext->varprefix = save_varprefix;\n\n\t\t\t\tif (iexpr->infercollid)\n\t\t\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t\t\t generate_collation_name(iexpr->infercollid));\n\n\t\t\t\t/* Add the operator class name, if not default */\n\t\t\t\tif (iexpr->inferopclass)\n\t\t\t\t{\n\t\t\t\t\tOid\t\t\tinferopclass = iexpr->inferopclass;\n\t\t\t\t\tOid\t\t\tinferopcinputtype = get_opclass_input_type(iexpr->inferopclass);\n\n\t\t\t\t\tget_opclass_name(inferopclass, inferopcinputtype, buf);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_ReturningExpr:\n\t\t\t{\n\t\t\t\tReturningExpr *retExpr = (ReturningExpr *) node;\n\n\t\t\t\t/*\n\t\t\t\t * We cannot see a ReturningExpr in rule deparsing, only while\n\t\t\t\t * EXPLAINing a query plan (ReturningExpr nodes are only ever\n\t\t\t\t * adding during query rewriting). Just display the expression\n\t\t\t\t * returned (an expanded view column).\n\t\t\t\t */\n\t\t\t\tget_rule_expr((Node *) retExpr->retexpr, context, showimplicit);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_PartitionBoundSpec:\n\t\t\t{\n\t\t\t\tPartitionBoundSpec *spec = (PartitionBoundSpec *) node;\n\t\t\t\tListCell   *cell;\n\t\t\t\tchar\t   *sep;\n\n\t\t\t\tif (spec->is_default)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(buf, \"DEFAULT\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tswitch (spec->strategy)\n\t\t\t\t{\n\t\t\t\t\tcase PARTITION_STRATEGY_HASH:\n\t\t\t\t\t\tAssert(spec->modulus > 0 && spec->remainder >= 0);\n\t\t\t\t\t\tAssert(spec->modulus > spec->remainder);\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"FOR VALUES\");\n\t\t\t\t\t\tappendStringInfo(buf, \" WITH (modulus %d, remainder %d)\",\n\t\t\t\t\t\t\t\t\t\t spec->modulus, spec->remainder);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase PARTITION_STRATEGY_LIST:\n\t\t\t\t\t\tAssert(spec->listdatums != NIL);\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"FOR VALUES IN (\");\n\t\t\t\t\t\tsep = \"\";\n\t\t\t\t\t\tforeach(cell, spec->listdatums)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tConst\t   *val = lfirst_node(Const, cell);\n\n\t\t\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\t\t\tget_const_expr(val, context, -1);\n\t\t\t\t\t\t\tsep = \", \";\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase PARTITION_STRATEGY_RANGE:\n\t\t\t\t\t\tAssert(spec->lowerdatums != NIL &&\n\t\t\t\t\t\t\t   spec->upperdatums != NIL &&\n\t\t\t\t\t\t\t   list_length(spec->lowerdatums) ==\n\t\t\t\t\t\t\t   list_length(spec->upperdatums));\n\n\t\t\t\t\t\tappendStringInfo(buf, \"FOR VALUES FROM %s TO %s\",\n\t\t\t\t\t\t\t\t\t\t get_range_partbound_string(spec->lowerdatums),\n\t\t\t\t\t\t\t\t\t\t get_range_partbound_string(spec->upperdatums));\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telog(ERROR, \"unrecognized partition strategy: %d\",\n\t\t\t\t\t\t\t (int) spec->strategy);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_JsonValueExpr:\n\t\t\t{\n\t\t\t\tJsonValueExpr *jve = (JsonValueExpr *) node;\n\n\t\t\t\tget_rule_expr((Node *) jve->raw_expr, context, false);\n\t\t\t\tget_json_format(jve->format, context->buf);\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_JsonConstructorExpr:\n\t\t\tget_json_constructor((JsonConstructorExpr *) node, context, false);\n\t\t\tbreak;\n\n\t\tcase T_JsonIsPredicate:\n\t\t\t{\n\t\t\t\tJsonIsPredicate *pred = (JsonIsPredicate *) node;\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(context->buf, '(');\n\n\t\t\t\tget_rule_expr_paren(pred->expr, context, true, node);\n\n\t\t\t\tappendStringInfoString(context->buf, \" IS JSON\");\n\n\t\t\t\t/* TODO: handle FORMAT clause */\n\n\t\t\t\tswitch (pred->item_type)\n\t\t\t\t{\n\t\t\t\t\tcase JS_TYPE_SCALAR:\n\t\t\t\t\t\tappendStringInfoString(context->buf, \" SCALAR\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase JS_TYPE_ARRAY:\n\t\t\t\t\t\tappendStringInfoString(context->buf, \" ARRAY\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase JS_TYPE_OBJECT:\n\t\t\t\t\t\tappendStringInfoString(context->buf, \" OBJECT\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (pred->unique_keys)\n\t\t\t\t\tappendStringInfoString(context->buf, \" WITH UNIQUE KEYS\");\n\n\t\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\t\tappendStringInfoChar(context->buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_JsonExpr:\n\t\t\t{\n\t\t\t\tJsonExpr   *jexpr = (JsonExpr *) node;\n\n\t\t\t\tswitch (jexpr->op)\n\t\t\t\t{\n\t\t\t\t\tcase JSON_EXISTS_OP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"JSON_EXISTS(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase JSON_QUERY_OP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"JSON_QUERY(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase JSON_VALUE_OP:\n\t\t\t\t\t\tappendStringInfoString(buf, \"JSON_VALUE(\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\telog(ERROR, \"unrecognized JsonExpr op: %d\",\n\t\t\t\t\t\t\t (int) jexpr->op);\n\t\t\t\t}\n\n\t\t\t\tget_rule_expr(jexpr->formatted_expr, context, showimplicit);\n\n\t\t\t\tappendStringInfoString(buf, \", \");\n\n\t\t\t\tget_json_path_spec(jexpr->path_spec, context, showimplicit);\n\n\t\t\t\tif (jexpr->passing_values)\n\t\t\t\t{\n\t\t\t\t\tListCell   *lc1,\n\t\t\t\t\t\t\t   *lc2;\n\t\t\t\t\tbool\t\tneedcomma = false;\n\n\t\t\t\t\tappendStringInfoString(buf, \" PASSING \");\n\n\t\t\t\t\tforboth(lc1, jexpr->passing_names,\n\t\t\t\t\t\t\tlc2, jexpr->passing_values)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (needcomma)\n\t\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\t\tneedcomma = true;\n\n\t\t\t\t\t\tget_rule_expr((Node *) lfirst(lc2), context, showimplicit);\n\t\t\t\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t\t\t\t quote_identifier(lfirst_node(String, lc1)->sval));\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (jexpr->op != JSON_EXISTS_OP ||\n\t\t\t\t\tjexpr->returning->typid != BOOLOID)\n\t\t\t\t\tget_json_returning(jexpr->returning, context->buf,\n\t\t\t\t\t\t\t\t\t   jexpr->op == JSON_QUERY_OP);\n\n\t\t\t\tget_json_expr_options(jexpr, context,\n\t\t\t\t\t\t\t\t\t  jexpr->op != JSON_EXISTS_OP ?\n\t\t\t\t\t\t\t\t\t  JSON_BEHAVIOR_NULL :\n\t\t\t\t\t\t\t\t\t  JSON_BEHAVIOR_FALSE);\n\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_List:\n\t\t\t{\n\t\t\t\tchar\t   *sep;\n\t\t\t\tListCell   *l;\n\n\t\t\t\tsep = \"\";\n\t\t\t\tforeach(l, (List *) node)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\t\tget_rule_expr((Node *) lfirst(l), context, showimplicit);\n\t\t\t\t\tsep = \", \";\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase T_TableFunc:\n\t\t\tget_tablefunc((TableFunc *) node, context, showimplicit);\n\t\t\tbreak;\n\n\t\tcase T_CallStmt:\n\t\t\tget_proc_expr((CallStmt *) node, context, showimplicit);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\telog(ERROR, \"unrecognized node type: %d\", (int) nodeTag(node));\n\t\t\tbreak;\n\t}\n}\n\n/*\n * get_rule_expr_toplevel\t\t- Parse back a toplevel expression\n *\n * Same as get_rule_expr(), except that if the expr is just a Var, we pass\n * istoplevel = true not false to get_variable().  This causes whole-row Vars\n * to get printed with decoration that will prevent expansion of \"*\".\n * We need to use this in contexts such as ROW() and VALUES(), where the\n * parser would expand \"foo.*\" appearing at top level.  (In principle we'd\n * use this in get_target_list() too, but that has additional worries about\n * whether to print AS, so it needs to invoke get_variable() directly anyway.)\n */\nstatic void\nget_rule_expr_toplevel(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit)\n{\n\tif (node && IsA(node, Var))\n\t\t(void) get_variable((Var *) node, 0, true, context);\n\telse\n\t\tget_rule_expr(node, context, showimplicit);\n}\n\n/*\n * get_rule_list_toplevel\t\t- Parse back a list of toplevel expressions\n *\n * Apply get_rule_expr_toplevel() to each element of a List.\n *\n * This adds commas between the expressions, but caller is responsible\n * for printing surrounding decoration.\n */\nstatic void\nget_rule_list_toplevel(List *lst, deparse_context *context,\n\t\t\t\t\t   bool showimplicit)\n{\n\tconst char *sep;\n\tListCell   *lc;\n\n\tsep = \"\";\n\tforeach(lc, lst)\n\t{\n\t\tNode\t   *e = (Node *) lfirst(lc);\n\n\t\tappendStringInfoString(context->buf, sep);\n\t\tget_rule_expr_toplevel(e, context, showimplicit);\n\t\tsep = \", \";\n\t}\n}\n\n/*\n * get_rule_expr_funccall\t\t- Parse back a function-call expression\n *\n * Same as get_rule_expr(), except that we guarantee that the output will\n * look like a function call, or like one of the things the grammar treats as\n * equivalent to a function call (see the func_expr_windowless production).\n * This is needed in places where the grammar uses func_expr_windowless and\n * you can't substitute a parenthesized a_expr.  If what we have isn't going\n * to look like a function call, wrap it in a dummy CAST() expression, which\n * will satisfy the grammar --- and, indeed, is likely what the user wrote to\n * produce such a thing.\n */\nstatic void\nget_rule_expr_funccall(Node *node, deparse_context *context,\n\t\t\t\t\t   bool showimplicit)\n{\n\tif (looks_like_function(node))\n\t\tget_rule_expr(node, context, showimplicit);\n\telse\n\t{\n\t\tStringInfo\tbuf = context->buf;\n\n\t\tappendStringInfoString(buf, \"CAST(\");\n\t\t/* no point in showing any top-level implicit cast */\n\t\tget_rule_expr(node, context, false);\n\t\tappendStringInfo(buf, \" AS %s)\",\n\t\t\t\t\t\t format_type_with_typemod(exprType(node),\n\t\t\t\t\t\t\t\t\t\t\t\t  exprTypmod(node)));\n\t}\n}\n\n/*\n * Helper function to identify node types that satisfy func_expr_windowless.\n * If in doubt, \"false\" is always a safe answer.\n */\nstatic bool\nlooks_like_function(Node *node)\n{\n\tif (node == NULL)\n\t\treturn false;\t\t\t/* probably shouldn't happen */\n\tswitch (nodeTag(node))\n\t{\n\t\tcase T_FuncExpr:\n\t\t\t/* OK, unless it's going to deparse as a cast */\n\t\t\treturn (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||\n\t\t\t\t\t((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);\n\t\tcase T_NullIfExpr:\n\t\tcase T_CoalesceExpr:\n\t\tcase T_MinMaxExpr:\n\t\tcase T_SQLValueFunction:\n\t\tcase T_XmlExpr:\n\t\tcase T_JsonExpr:\n\t\t\t/* these are all accepted by func_expr_common_subexpr */\n\t\t\treturn true;\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\treturn false;\n}\n\n/*\n * get_oper_expr\t\t\t- Parse back an OpExpr node\n */\nstatic void\nget_oper_expr(OpExpr *expr, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\topno = expr->opno;\n\tList\t   *args = expr->args;\n\n\tif (!PRETTY_PAREN(context))\n\t\tappendStringInfoChar(buf, '(');\n\tif (list_length(args) == 2)\n\t{\n\t\t/* binary operator */\n\t\tNode\t   *arg1 = (Node *) linitial(args);\n\t\tNode\t   *arg2 = (Node *) lsecond(args);\n\n\t\tget_rule_expr_paren(arg1, context, true, (Node *) expr);\n\t\tappendStringInfo(buf, \" %s \",\n\t\t\t\t\t\t generate_operator_name(opno,\n\t\t\t\t\t\t\t\t\t\t\t\texprType(arg1),\n\t\t\t\t\t\t\t\t\t\t\t\texprType(arg2)));\n\t\tget_rule_expr_paren(arg2, context, true, (Node *) expr);\n\t}\n\telse\n\t{\n\t\t/* prefix operator */\n\t\tNode\t   *arg = (Node *) linitial(args);\n\n\t\tappendStringInfo(buf, \"%s \",\n\t\t\t\t\t\t generate_operator_name(opno,\n\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid,\n\t\t\t\t\t\t\t\t\t\t\t\texprType(arg)));\n\t\tget_rule_expr_paren(arg, context, true, (Node *) expr);\n\t}\n\tif (!PRETTY_PAREN(context))\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_func_expr\t\t\t- Parse back a FuncExpr node\n */\nstatic void\nget_func_expr(FuncExpr *expr, deparse_context *context,\n\t\t\t  bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\tfuncoid = expr->funcid;\n\tOid\t\t\targtypes[FUNC_MAX_ARGS];\n\tint\t\t\tnargs;\n\tList\t   *argnames;\n\tbool\t\tuse_variadic;\n\tListCell   *l;\n\n\t/*\n\t * If the function call came from an implicit coercion, then just show the\n\t * first argument --- unless caller wants to see implicit coercions.\n\t */\n\tif (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)\n\t{\n\t\tget_rule_expr_paren((Node *) linitial(expr->args), context,\n\t\t\t\t\t\t\tfalse, (Node *) expr);\n\t\treturn;\n\t}\n\n\t/*\n\t * If the function call came from a cast, then show the first argument\n\t * plus an explicit cast operation.\n\t */\n\tif (expr->funcformat == COERCE_EXPLICIT_CAST ||\n\t\texpr->funcformat == COERCE_IMPLICIT_CAST)\n\t{\n\t\tNode\t   *arg = linitial(expr->args);\n\t\tOid\t\t\trettype = expr->funcresulttype;\n\t\tint32\t\tcoercedTypmod;\n\n\t\t/* Get the typmod if this is a length-coercion function */\n\t\t(void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);\n\n\t\tget_coercion_expr(arg, context,\n\t\t\t\t\t\t  rettype, coercedTypmod,\n\t\t\t\t\t\t  (Node *) expr);\n\n\t\treturn;\n\t}\n\n\t/*\n\t * If the function was called using one of the SQL spec's random special\n\t * syntaxes, try to reproduce that.  If we don't recognize the function,\n\t * fall through.\n\t */\n\tif (expr->funcformat == COERCE_SQL_SYNTAX)\n\t{\n\t\tif (get_func_sql_syntax(expr, context))\n\t\t\treturn;\n\t}\n\n\n\t/*\n\t * Normal function: display as proname(args).  First we need to extract\n\t * the argument datatypes.\n\t */\n\tif (list_length(expr->args) > FUNC_MAX_ARGS)\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_TOO_MANY_ARGUMENTS),\n\t\t\t\t errmsg(\"too many arguments\")));\n\tnargs = 0;\n\targnames = NIL;\n\tforeach(l, expr->args)\n\t{\n\t\tNode\t   *arg = (Node *) lfirst(l);\n\n\t\tif (IsA(arg, NamedArgExpr))\n\t\t\targnames = lappend(argnames, ((NamedArgExpr *) arg)->name);\n\t\targtypes[nargs] = exprType(arg);\n\t\tnargs++;\n\t}\n\n\tappendStringInfo(buf, \"%s(\",\n\t\t\t\t\t generate_function_name(funcoid, nargs,\n\t\t\t\t\t\t\t\t\t\t\targnames, argtypes,\n\t\t\t\t\t\t\t\t\t\t\texpr->funcvariadic,\n\t\t\t\t\t\t\t\t\t\t\t&use_variadic,\n\t\t\t\t\t\t\t\t\t\t\tcontext->inGroupBy));\n\tnargs = 0;\n\tforeach(l, expr->args)\n\t{\n\t\tif (nargs++ > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tif (use_variadic && lnext(expr->args, l) == NULL)\n\t\t\tappendStringInfoString(buf, \"VARIADIC \");\n\t\tget_rule_expr((Node *) lfirst(l), context, true);\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_proc_expr\t\t\t- Parse back a CallStmt node\n */\nstatic void\nget_proc_expr(CallStmt *stmt, deparse_context *context,\n\t\t\t  bool showimplicit)\n{\n\tStringInfo buf = context->buf;\n\tOid        functionOid = stmt->funcexpr->funcid;\n\tbool       use_variadic;\n\tOid        *argumentTypes;\n\tList       *finalArgumentList = NIL;\n\tListCell   *argumentCell;\n\tList       *namedArgList = NIL;\n\tint        numberOfArgs = -1;\n\n\tif (!get_merged_argument_list(stmt, &namedArgList, &argumentTypes,\n\t\t\t\t\t\t\t\t\t\t&finalArgumentList, &numberOfArgs))\n\t{\n\t\t/* Nothing merged i.e. no OUT arguments */\n\t\tget_func_expr((FuncExpr *) stmt->funcexpr, context, showimplicit);\n\t\treturn;\n\t}\n\n\tappendStringInfo(buf, \"%s(\",\n\t\t\t\t\t generate_function_name(functionOid, numberOfArgs,\n\t\t\t\t\t\t\t\t\t\tnamedArgList, argumentTypes,\n\t\t\t\t\t\t\t\t\t\tstmt->funcexpr->funcvariadic,\n\t\t\t\t\t\t\t\t\t\t&use_variadic,\n\t\t\t\t\t\t\t\t\t\tcontext->inGroupBy));\n\tint argNumber = 0;\n\tforeach(argumentCell, finalArgumentList)\n\t{\n\t\tif (argNumber++ > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tif (use_variadic && lnext(finalArgumentList, argumentCell) == NULL)\n\t\t\tappendStringInfoString(buf, \"VARIADIC \");\n\t\tget_rule_expr((Node *) lfirst(argumentCell), context, true);\n\t\targNumber++;\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_agg_expr\t\t\t- Parse back an Aggref node\n */\nstatic void\nget_agg_expr(Aggref *aggref, deparse_context *context,\n\t\t\t Aggref *original_aggref)\n{\n\tget_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,\n\t\t\t\t\t\tfalse);\n}\n\n/*\n * get_agg_expr_helper\t\t- subroutine for get_agg_expr and\n *\t\t\t\t\t\t\tget_json_agg_constructor\n */\nstatic void\nget_agg_expr_helper(Aggref *aggref, deparse_context *context,\n\t\t\t\t\tAggref *original_aggref, const char *funcname,\n\t\t\t\t\tconst char *options, bool is_json_objectagg)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\targtypes[FUNC_MAX_ARGS];\n\tint\t\t\tnargs;\n\tbool use_variadic = false;\n\n\t/*\n\t * For a combining aggregate, we look up and deparse the corresponding\n\t * partial aggregate instead.  This is necessary because our input\n\t * argument list has been replaced; the new argument list always has just\n\t * one element, which will point to a partial Aggref that supplies us with\n\t * transition states to combine.\n\t */\n\tif (DO_AGGSPLIT_COMBINE(aggref->aggsplit))\n\t{\n\t\tTargetEntry *tle;\n\n\n\t\tAssert(list_length(aggref->args) == 1);\n\t\ttle = linitial_node(TargetEntry, aggref->args);\n\t\tresolve_special_varno((Node *) tle->expr, context,\n\t\t\t\t\t\t\t  get_agg_combine_expr, original_aggref);\n\t\treturn;\n\t}\n\n\t/*\n\t * Mark as PARTIAL, if appropriate.  We look to the original aggref so as\n\t * to avoid printing this when recursing from the code just above.\n\t */\n\tif (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit))\n\t\tappendStringInfoString(buf, \"PARTIAL \");\n\n\t/* Extract the argument types as seen by the parser */\n\tnargs = get_aggregate_argtypes(aggref, argtypes);\n\n\tif (!funcname)\n\t\tfuncname = generate_function_name(aggref->aggfnoid, nargs, NIL,\n\t\t\t\t\t\t\t\t\t\t  argtypes, aggref->aggvariadic,\n\t\t\t\t\t\t\t\t\t\t  &use_variadic,\n\t\t\t\t\t\t\t\t\t\t  context->inGroupBy);\n\n\t/* Print the aggregate name, schema-qualified if needed */\n\tappendStringInfo(buf, \"%s(%s\", funcname,\n\t\t\t\t\t (aggref->aggdistinct != NIL) ? \"DISTINCT \" : \"\");\n\n\tif (AGGKIND_IS_ORDERED_SET(aggref->aggkind))\n\t{\n\t\t/*\n\t\t * Ordered-set aggregates do not use \"*\" syntax.  Also, we needn't\n\t\t * worry about inserting VARIADIC.  So we can just dump the direct\n\t\t * args as-is.\n\t\t */\n\t\tAssert(!aggref->aggvariadic);\n\t\tget_rule_expr((Node *) aggref->aggdirectargs, context, true);\n\t\tAssert(aggref->aggorder != NIL);\n\t\tappendStringInfoString(buf, \") WITHIN GROUP (ORDER BY \");\n\t\tget_rule_orderby(aggref->aggorder, aggref->args, false, context);\n\t}\n\telse\n\t{\n\t\t/* aggstar can be set only in zero-argument aggregates */\n\t\tif (aggref->aggstar)\n\t\t\tappendStringInfoChar(buf, '*');\n\t\telse\n\t\t{\n\t\t\tListCell   *l;\n\t\t\tint\t\t\ti;\n\n\t\t\ti = 0;\n\t\t\tforeach(l, aggref->args)\n\t\t\t{\n\t\t\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\t\t\t\tNode\t   *arg = (Node *) tle->expr;\n\n\t\t\t\tAssert(!IsA(arg, NamedArgExpr));\n\t\t\t\tif (tle->resjunk)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (i++ > 0)\n\t\t\t\t{\n\t\t\t\t\tif (is_json_objectagg)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * the ABSENT ON NULL and WITH UNIQUE args are printed\n\t\t\t\t\t\t * separately, so ignore them here\n\t\t\t\t\t\t */\n\t\t\t\t\t\tif (i > 2)\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tappendStringInfoString(buf, \" : \");\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t}\n\t\t\t\tif (use_variadic && i == nargs)\n\t\t\t\t\tappendStringInfoString(buf, \"VARIADIC \");\n\t\t\t\tget_rule_expr(arg, context, true);\n\t\t\t}\n\t\t}\n\n\t\tif (aggref->aggorder != NIL)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" ORDER BY \");\n\t\t\tget_rule_orderby(aggref->aggorder, aggref->args, false, context);\n\t\t}\n\t}\n\n\tif (options)\n\t\tappendStringInfoString(buf, options);\n\n\tif (aggref->aggfilter != NULL)\n\t{\n\t\tappendStringInfoString(buf, \") FILTER (WHERE \");\n\t\tget_rule_expr((Node *) aggref->aggfilter, context, false);\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * This is a helper function for get_agg_expr().  It's used when we deparse\n * a combining Aggref; resolve_special_varno locates the corresponding partial\n * Aggref and then calls this.\n */\nstatic void\nget_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)\n{\n\tAggref\t   *aggref;\n\tAggref\t   *original_aggref = callback_arg;\n\n\tif (!IsA(node, Aggref))\n\t\telog(ERROR, \"combining Aggref does not point to an Aggref\");\n\n\taggref = (Aggref *) node;\n\tget_agg_expr(aggref, context, original_aggref);\n}\n\n/*\n * get_windowfunc_expr\t- Parse back a WindowFunc node\n */\nstatic void\nget_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)\n{\n\tget_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);\n}\n\n\n/*\n * get_windowfunc_expr_helper\t- subroutine for get_windowfunc_expr and\n *\t\t\t\t\t\t\t\tget_json_agg_constructor\n */\nstatic void\nget_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,\n\t\t\t\t\t\t   const char *funcname, const char *options,\n\t\t\t\t\t\t   bool is_json_objectagg)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\targtypes[FUNC_MAX_ARGS];\n\tint\t\t\tnargs;\n\tList\t   *argnames;\n\tListCell   *l;\n\n\tif (list_length(wfunc->args) > FUNC_MAX_ARGS)\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_TOO_MANY_ARGUMENTS),\n\t\t\t\t errmsg(\"too many arguments\")));\n\tnargs = 0;\n\targnames = NIL;\n\tforeach(l, wfunc->args)\n\t{\n\t\tNode\t   *arg = (Node *) lfirst(l);\n\n\t\tif (IsA(arg, NamedArgExpr))\n\t\t\targnames = lappend(argnames, ((NamedArgExpr *) arg)->name);\n\t\targtypes[nargs] = exprType(arg);\n\t\tnargs++;\n\t}\n\n\tif (!funcname)\n\t\tfuncname = generate_function_name(wfunc->winfnoid, nargs, argnames,\n\t\t\t\t\t\t\t\t\t\t  argtypes, false, NULL,\n\t\t\t\t\t\t\t\t\t\t  context->inGroupBy);\n\n\tappendStringInfo(buf, \"%s(\", funcname);\n\n\t/* winstar can be set only in zero-argument aggregates */\n\tif (wfunc->winstar)\n\t\tappendStringInfoChar(buf, '*');\n\telse\n\t{\n\t\tif (is_json_objectagg)\n\t\t{\n\t\t\tget_rule_expr((Node *) linitial(wfunc->args), context, false);\n\t\t\tappendStringInfoString(buf, \" : \");\n\t\t\tget_rule_expr((Node *) lsecond(wfunc->args), context, false);\n\t\t}\n\t\telse\n\t\t\tget_rule_expr((Node *) wfunc->args, context, true);\n\t}\n\n\tif (options)\n\t\tappendStringInfoString(buf, options);\n\n\tif (wfunc->aggfilter != NULL)\n\t{\n\t\tappendStringInfoString(buf, \") FILTER (WHERE \");\n\t\tget_rule_expr((Node *) wfunc->aggfilter, context, false);\n\t}\n\n\tappendStringInfoString(buf, \") OVER \");\n\n\tif (context->windowClause)\n\t{\n\t\t/* Query-decompilation case: search the windowClause list */\n\t\tforeach(l, context->windowClause)\n\t\t{\n\t\t\tWindowClause *wc = (WindowClause *) lfirst(l);\n\n\t\t\tif (wc->winref == wfunc->winref)\n\t\t\t{\n\t\t\t\tif (wc->name)\n\t\t\t\t\tappendStringInfoString(buf, quote_identifier(wc->name));\n\t\t\t\telse\n\t\t\t\t\tget_rule_windowspec(wc, context->targetList, context);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (l == NULL)\n\t\t\telog(ERROR, \"could not find window clause for winref %u\",\n\t\t\t\t wfunc->winref);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * In EXPLAIN, search the namespace stack for a matching WindowAgg\n\t\t * node (probably it's always the first entry), and print winname.\n\t\t */\n\t\tforeach(l, context->namespaces)\n\t\t{\n\t\t\tdeparse_namespace *dpns = (deparse_namespace *) lfirst(l);\n\n\t\t\tif (dpns->plan && IsA(dpns->plan, WindowAgg))\n\t\t\t{\n\t\t\t\tWindowAgg  *wagg = (WindowAgg *) dpns->plan;\n\n\t\t\t\tif (wagg->winref == wfunc->winref)\n\t\t\t\t{\n\t\t\t\t\tappendStringInfoString(buf, quote_identifier(wagg->winname));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (l == NULL)\n\t\t\telog(ERROR, \"could not find window clause for winref %u\",\n\t\t\t\t wfunc->winref);\n\t}\n}\n\n/*\n * get_func_sql_syntax\t\t- Parse back a SQL-syntax function call\n *\n * Returns true if we successfully deparsed, false if we did not\n * recognize the function.\n */\nstatic bool\nget_func_sql_syntax(FuncExpr *expr, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\tfuncoid = expr->funcid;\n\n\tswitch (funcoid)\n\t{\n\t\tcase F_TIMEZONE_INTERVAL_TIMESTAMP:\n\t\tcase F_TIMEZONE_INTERVAL_TIMESTAMPTZ:\n\t\tcase F_TIMEZONE_INTERVAL_TIMETZ:\n\t\tcase F_TIMEZONE_TEXT_TIMESTAMP:\n\t\tcase F_TIMEZONE_TEXT_TIMESTAMPTZ:\n\t\tcase F_TIMEZONE_TEXT_TIMETZ:\n\t\t\t/* AT TIME ZONE ... note reversed argument order */\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr_paren((Node *) lsecond(expr->args), context, false,\n\t\t\t\t\t\t\t\t(Node *) expr);\n\t\t\tappendStringInfoString(buf, \" AT TIME ZONE \");\n\t\t\tget_rule_expr_paren((Node *) linitial(expr->args), context, false,\n\t\t\t\t\t\t\t\t(Node *) expr);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_TIMEZONE_TIMESTAMP:\n\t\tcase F_TIMEZONE_TIMESTAMPTZ:\n\t\tcase F_TIMEZONE_TIMETZ:\n\t\t\t/* AT LOCAL */\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr_paren((Node *) linitial(expr->args), context, false,\n\t\t\t\t\t\t\t\t(Node *) expr);\n\t\t\tappendStringInfoString(buf, \" AT LOCAL)\");\n\t\t\treturn true;\n\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_TIMESTAMPTZ:\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ:\n\t\tcase F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_TIMESTAMP:\n\t\tcase F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_INTERVAL:\n\t\tcase F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_TIMESTAMP:\n\t\tcase F_OVERLAPS_TIMETZ_TIMETZ_TIMETZ_TIMETZ:\n\t\tcase F_OVERLAPS_TIME_INTERVAL_TIME_INTERVAL:\n\t\tcase F_OVERLAPS_TIME_INTERVAL_TIME_TIME:\n\t\tcase F_OVERLAPS_TIME_TIME_TIME_INTERVAL:\n\t\tcase F_OVERLAPS_TIME_TIME_TIME_TIME:\n\t\t\t/* (x1, x2) OVERLAPS (y1, y2) */\n\t\t\tappendStringInfoString(buf, \"((\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \") OVERLAPS (\");\n\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tget_rule_expr((Node *) lfourth(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\treturn true;\n\n\t\tcase F_EXTRACT_TEXT_DATE:\n\t\tcase F_EXTRACT_TEXT_TIME:\n\t\tcase F_EXTRACT_TEXT_TIMETZ:\n\t\tcase F_EXTRACT_TEXT_TIMESTAMP:\n\t\tcase F_EXTRACT_TEXT_TIMESTAMPTZ:\n\t\tcase F_EXTRACT_TEXT_INTERVAL:\n\t\t\t/* EXTRACT (x FROM y) */\n\t\t\tappendStringInfoString(buf, \"EXTRACT(\");\n\t\t\t{\n\t\t\t\tConst\t   *con = (Const *) linitial(expr->args);\n\n\t\t\t\tAssert(IsA(con, Const) &&\n\t\t\t\t\t   con->consttype == TEXTOID &&\n\t\t\t\t\t   !con->constisnull);\n\t\t\t\tappendStringInfoString(buf, TextDatumGetCString(con->constvalue));\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_IS_NORMALIZED:\n\t\t\t/* IS xxx NORMALIZED */\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr_paren((Node *) linitial(expr->args), context, false,\n\t\t\t\t\t\t\t\t(Node *) expr);\n\t\t\tappendStringInfoString(buf, \" IS\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tConst\t   *con = (Const *) lsecond(expr->args);\n\n\t\t\t\tAssert(IsA(con, Const) &&\n\t\t\t\t\t   con->consttype == TEXTOID &&\n\t\t\t\t\t   !con->constisnull);\n\t\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\t\t\t\t TextDatumGetCString(con->constvalue));\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" NORMALIZED)\");\n\t\t\treturn true;\n\n\t\tcase F_PG_COLLATION_FOR:\n\t\t\t/* COLLATION FOR */\n\t\t\tappendStringInfoString(buf, \"COLLATION FOR (\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_NORMALIZE:\n\t\t\t/* NORMALIZE() */\n\t\t\tappendStringInfoString(buf, \"NORMALIZE(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tConst\t   *con = (Const *) lsecond(expr->args);\n\n\t\t\t\tAssert(IsA(con, Const) &&\n\t\t\t\t\t   con->consttype == TEXTOID &&\n\t\t\t\t\t   !con->constisnull);\n\t\t\t\tappendStringInfo(buf, \", %s\",\n\t\t\t\t\t\t\t\t TextDatumGetCString(con->constvalue));\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_OVERLAY_BIT_BIT_INT4:\n\t\tcase F_OVERLAY_BIT_BIT_INT4_INT4:\n\t\tcase F_OVERLAY_BYTEA_BYTEA_INT4:\n\t\tcase F_OVERLAY_BYTEA_BYTEA_INT4_INT4:\n\t\tcase F_OVERLAY_TEXT_TEXT_INT4:\n\t\tcase F_OVERLAY_TEXT_TEXT_INT4_INT4:\n\t\t\t/* OVERLAY() */\n\t\t\tappendStringInfoString(buf, \"OVERLAY(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" PLACING \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\tif (list_length(expr->args) == 4)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" FOR \");\n\t\t\t\tget_rule_expr((Node *) lfourth(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_POSITION_BIT_BIT:\n\t\tcase F_POSITION_BYTEA_BYTEA:\n\t\tcase F_POSITION_TEXT_TEXT:\n\t\t\t/* POSITION() ... extra parens since args are b_expr not a_expr */\n\t\t\tappendStringInfoString(buf, \"POSITION((\");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \") IN (\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\treturn true;\n\n\t\tcase F_SUBSTRING_BIT_INT4:\n\t\tcase F_SUBSTRING_BIT_INT4_INT4:\n\t\tcase F_SUBSTRING_BYTEA_INT4:\n\t\tcase F_SUBSTRING_BYTEA_INT4_INT4:\n\t\tcase F_SUBSTRING_TEXT_INT4:\n\t\tcase F_SUBSTRING_TEXT_INT4_INT4:\n\t\t\t/* SUBSTRING FROM/FOR (i.e., integer-position variants) */\n\t\t\tappendStringInfoString(buf, \"SUBSTRING(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tif (list_length(expr->args) == 3)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" FOR \");\n\t\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_SUBSTRING_TEXT_TEXT_TEXT:\n\t\t\t/* SUBSTRING SIMILAR/ESCAPE */\n\t\t\tappendStringInfoString(buf, \"SUBSTRING(\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" SIMILAR \");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \" ESCAPE \");\n\t\t\tget_rule_expr((Node *) lthird(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_BTRIM_BYTEA_BYTEA:\n\t\tcase F_BTRIM_TEXT:\n\t\tcase F_BTRIM_TEXT_TEXT:\n\t\t\t/* TRIM() */\n\t\t\tappendStringInfoString(buf, \"TRIM(BOTH\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_LTRIM_BYTEA_BYTEA:\n\t\tcase F_LTRIM_TEXT:\n\t\tcase F_LTRIM_TEXT_TEXT:\n\t\t\t/* TRIM() */\n\t\t\tappendStringInfoString(buf, \"TRIM(LEADING\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_RTRIM_BYTEA_BYTEA:\n\t\tcase F_RTRIM_TEXT:\n\t\tcase F_RTRIM_TEXT_TEXT:\n\t\t\t/* TRIM() */\n\t\t\tappendStringInfoString(buf, \"TRIM(TRAILING\");\n\t\t\tif (list_length(expr->args) == 2)\n\t\t\t{\n\t\t\t\tappendStringInfoChar(buf, ' ');\n\t\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\t}\n\t\t\tappendStringInfoString(buf, \" FROM \");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t\treturn true;\n\n\t\tcase F_SYSTEM_USER:\n\t\t\tappendStringInfoString(buf, \"SYSTEM_USER\");\n\t\t\treturn true;\n\n\t\tcase F_XMLEXISTS:\n\t\t\t/* XMLEXISTS ... extra parens because args are c_expr */\n\t\t\tappendStringInfoString(buf, \"XMLEXISTS((\");\n\t\t\tget_rule_expr((Node *) linitial(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \") PASSING (\");\n\t\t\tget_rule_expr((Node *) lsecond(expr->args), context, false);\n\t\t\tappendStringInfoString(buf, \"))\");\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n/* ----------\n * get_coercion_expr\n *\n *\tMake a string representation of a value coerced to a specific type\n * ----------\n */\nstatic void\nget_coercion_expr(Node *arg, deparse_context *context,\n\t\t\t\t  Oid resulttype, int32 resulttypmod,\n\t\t\t\t  Node *parentNode)\n{\n\tStringInfo\tbuf = context->buf;\n\n\t/*\n\t * Since parse_coerce.c doesn't immediately collapse application of\n\t * length-coercion functions to constants, what we'll typically see in\n\t * such cases is a Const with typmod -1 and a length-coercion function\n\t * right above it.  Avoid generating redundant output. However, beware of\n\t * suppressing casts when the user actually wrote something like\n\t * 'foo'::text::char(3).\n\t *\n\t * Note: it might seem that we are missing the possibility of needing to\n\t * print a COLLATE clause for such a Const.  However, a Const could only\n\t * have nondefault collation in a post-constant-folding tree, in which the\n\t * length coercion would have been folded too.  See also the special\n\t * handling of CollateExpr in coerce_to_target_type(): any collation\n\t * marking will be above the coercion node, not below it.\n\t */\n\tif (arg && IsA(arg, Const) &&\n\t\t((Const *) arg)->consttype == resulttype &&\n\t\t((Const *) arg)->consttypmod == -1)\n\t{\n\t\t/* Show the constant without normal ::typename decoration */\n\t\tget_const_expr((Const *) arg, context, -1);\n\t}\n\telse\n\t{\n\t\tif (!PRETTY_PAREN(context))\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_rule_expr_paren(arg, context, false, parentNode);\n\t\tif (!PRETTY_PAREN(context))\n\t\t\tappendStringInfoChar(buf, ')');\n\t}\n\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t format_type_with_typemod(resulttype, resulttypmod));\n}\n\n/* ----------\n * get_const_expr\n *\n *\tMake a string representation of a Const\n *\n * showtype can be -1 to never show \"::typename\" decoration, or +1 to always\n * show it, or 0 to show it only if the constant wouldn't be assumed to be\n * the right type by default.\n *\n * If the Const's collation isn't default for its type, show that too.\n * We mustn't do this when showtype is -1 (since that means the caller will\n * print \"::typename\", and we can't put a COLLATE clause in between).  It's\n * caller's responsibility that collation isn't missed in such cases.\n * ----------\n */\nstatic void\nget_const_expr(Const *constval, deparse_context *context, int showtype)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\ttypoutput;\n\tbool\t\ttypIsVarlena;\n\tchar\t   *extval;\n\tbool\t\tneedlabel = false;\n\n\tif (constval->constisnull)\n\t{\n\t\t/*\n\t\t * Always label the type of a NULL constant to prevent misdecisions\n\t\t * about type when reparsing.\n\t\t */\n\t\tappendStringInfoString(buf, \"NULL\");\n\t\tif (showtype >= 0)\n\t\t{\n\t\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t\t format_type_with_typemod(constval->consttype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  constval->consttypmod));\n\t\t\tget_const_collation(constval, context);\n\t\t}\n\t\treturn;\n\t}\n\n\tgetTypeOutputInfo(constval->consttype,\n\t\t\t\t\t  &typoutput, &typIsVarlena);\n\n\textval = OidOutputFunctionCall(typoutput, constval->constvalue);\n\n\tswitch (constval->consttype)\n\t{\n\t\tcase INT4OID:\n\n\t\t\t/*\n\t\t\t * INT4 can be printed without any decoration, unless it is\n\t\t\t * negative; in that case print it as '-nnn'::integer to ensure\n\t\t\t * that the output will re-parse as a constant, not as a constant\n\t\t\t * plus operator.  In most cases we could get away with printing\n\t\t\t * (-nnn) instead, because of the way that gram.y handles negative\n\t\t\t * literals; but that doesn't work for INT_MIN, and it doesn't\n\t\t\t * seem that much prettier anyway.\n\t\t\t */\n\t\t\tif (extval[0] != '-')\n\t\t\t\tappendStringInfoString(buf, extval);\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"'%s'\", extval);\n\t\t\t\tneedlabel = true;\t/* we must attach a cast */\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase NUMERICOID:\n\n\t\t\t/*\n\t\t\t * NUMERIC can be printed without quotes if it looks like a float\n\t\t\t * constant (not an integer, and not Infinity or NaN) and doesn't\n\t\t\t * have a leading sign (for the same reason as for INT4).\n\t\t\t */\n\t\t\tif (isdigit((unsigned char) extval[0]) &&\n\t\t\t\tstrcspn(extval, \"eE.\") != strlen(extval))\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, extval);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(buf, \"'%s'\", extval);\n\t\t\t\tneedlabel = true;\t/* we must attach a cast */\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase BITOID:\n\t\tcase VARBITOID:\n\t\t\tappendStringInfo(buf, \"B'%s'\", extval);\n\t\t\tbreak;\n\n\t\tcase BOOLOID:\n\t\t\tif (strcmp(extval, \"t\") == 0)\n\t\t\t\tappendStringInfoString(buf, \"true\");\n\t\t\telse\n\t\t\t\tappendStringInfoString(buf, \"false\");\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tsimple_quote_literal(buf, extval);\n\t\t\tbreak;\n\t}\n\n\tpfree(extval);\n\n\tif (showtype < 0)\n\t\treturn;\n\n\t/*\n\t * For showtype == 0, append ::typename unless the constant will be\n\t * implicitly typed as the right type when it is read in.\n\t *\n\t * XXX this code has to be kept in sync with the behavior of the parser,\n\t * especially make_const.\n\t */\n\tswitch (constval->consttype)\n\t{\n\t\tcase BOOLOID:\n\t\tcase UNKNOWNOID:\n\t\t\t/* These types can be left unlabeled */\n\t\t\tneedlabel = false;\n\t\t\tbreak;\n\t\tcase INT4OID:\n\t\t\t/* We determined above whether a label is needed */\n\t\t\tbreak;\n\t\tcase NUMERICOID:\n\n\t\t\t/*\n\t\t\t * Float-looking constants will be typed as numeric, which we\n\t\t\t * checked above; but if there's a nondefault typmod we need to\n\t\t\t * show it.\n\t\t\t */\n\t\t\tneedlabel |= (constval->consttypmod >= 0);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tneedlabel = true;\n\t\t\tbreak;\n\t}\n\tif (needlabel || showtype > 0)\n\t\tappendStringInfo(buf, \"::%s\",\n\t\t\t\t\t\t format_type_with_typemod(constval->consttype,\n\t\t\t\t\t\t\t\t\t\t\t\t  constval->consttypmod));\n\n\tget_const_collation(constval, context);\n}\n\n/*\n * helper for get_const_expr: append COLLATE if needed\n */\nstatic void\nget_const_collation(Const *constval, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tif (OidIsValid(constval->constcollid))\n\t{\n\t\tOid\t\t\ttypcollation = get_typcollation(constval->consttype);\n\n\t\tif (constval->constcollid != typcollation)\n\t\t{\n\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t generate_collation_name(constval->constcollid));\n\t\t}\n\t}\n}\n\n/*\n * get_json_path_spec\t\t- Parse back a JSON path specification\n */\nstatic void\nget_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)\n{\n\tif (IsA(path_spec, Const))\n\t\tget_const_expr((Const *) path_spec, context, -1);\n\telse\n\t\tget_rule_expr(path_spec, context, showimplicit);\n}\n\n/*\n * get_json_format\t\t\t- Parse back a JsonFormat node\n */\nstatic void\nget_json_format(JsonFormat *format, StringInfo buf)\n{\n\tif (format->format_type == JS_FORMAT_DEFAULT)\n\t\treturn;\n\n\tappendStringInfoString(buf,\n\t\t\t\t\t\t   format->format_type == JS_FORMAT_JSONB ?\n\t\t\t\t\t\t   \" FORMAT JSONB\" : \" FORMAT JSON\");\n\n\tif (format->encoding != JS_ENC_DEFAULT)\n\t{\n\t\tconst char *encoding;\n\n\t\tencoding =\n\t\t\tformat->encoding == JS_ENC_UTF16 ? \"UTF16\" :\n\t\t\tformat->encoding == JS_ENC_UTF32 ? \"UTF32\" : \"UTF8\";\n\n\t\tappendStringInfo(buf, \" ENCODING %s\", encoding);\n\t}\n}\n\n/*\n * get_json_returning\t\t- Parse back a JsonReturning structure\n */\nstatic void\nget_json_returning(JsonReturning *returning, StringInfo buf,\n\t\t\t\t   bool json_format_by_default)\n{\n\tif (!OidIsValid(returning->typid))\n\t\treturn;\n\n\tappendStringInfo(buf, \" RETURNING %s\",\n\t\t\t\t\t format_type_with_typemod(returning->typid,\n\t\t\t\t\t\t\t\t\t\t\t  returning->typmod));\n\n\tif (!json_format_by_default ||\n\t\treturning->format->format_type !=\n\t\t(returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))\n\t\tget_json_format(returning->format, buf);\n}\n\n/*\n * get_json_constructor\t\t- Parse back a JsonConstructorExpr node\n */\nstatic void\nget_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,\n\t\t\t\t\t bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\tconst char *funcname;\n\tbool\t\tis_json_object;\n\tint\t\t\tcurridx;\n\tListCell   *lc;\n\n\tif (ctor->type == JSCTOR_JSON_OBJECTAGG)\n\t{\n\t\tget_json_agg_constructor(ctor, context, \"JSON_OBJECTAGG\", true);\n\t\treturn;\n\t}\n\telse if (ctor->type == JSCTOR_JSON_ARRAYAGG)\n\t{\n\t\tget_json_agg_constructor(ctor, context, \"JSON_ARRAYAGG\", false);\n\t\treturn;\n\t}\n\n\tswitch (ctor->type)\n\t{\n\t\tcase JSCTOR_JSON_OBJECT:\n\t\t\tfuncname = \"JSON_OBJECT\";\n\t\t\tbreak;\n\t\tcase JSCTOR_JSON_ARRAY:\n\t\t\tfuncname = \"JSON_ARRAY\";\n\t\t\tbreak;\n\t\tcase JSCTOR_JSON_PARSE:\n\t\t\tfuncname = \"JSON\";\n\t\t\tbreak;\n\t\tcase JSCTOR_JSON_SCALAR:\n\t\t\tfuncname = \"JSON_SCALAR\";\n\t\t\tbreak;\n\t\tcase JSCTOR_JSON_SERIALIZE:\n\t\t\tfuncname = \"JSON_SERIALIZE\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\telog(ERROR, \"invalid JsonConstructorType %d\", ctor->type);\n\t}\n\n\tappendStringInfo(buf, \"%s(\", funcname);\n\n\tis_json_object = ctor->type == JSCTOR_JSON_OBJECT;\n\tforeach(lc, ctor->args)\n\t{\n\t\tcurridx = foreach_current_index(lc);\n\t\tif (curridx > 0)\n\t\t{\n\t\t\tconst char *sep;\n\n\t\t\tsep = (is_json_object && (curridx % 2) != 0) ? \" : \" : \", \";\n\t\t\tappendStringInfoString(buf, sep);\n\t\t}\n\n\t\tget_rule_expr((Node *) lfirst(lc), context, true);\n\t}\n\n\tget_json_constructor_options(ctor, buf);\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * Append options, if any, to the JSON constructor being deparsed\n */\nstatic void\nget_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)\n{\n\tif (ctor->absent_on_null)\n\t{\n\t\tif (ctor->type == JSCTOR_JSON_OBJECT ||\n\t\t\tctor->type == JSCTOR_JSON_OBJECTAGG)\n\t\t\tappendStringInfoString(buf, \" ABSENT ON NULL\");\n\t}\n\telse\n\t{\n\t\tif (ctor->type == JSCTOR_JSON_ARRAY ||\n\t\t\tctor->type == JSCTOR_JSON_ARRAYAGG)\n\t\t\tappendStringInfoString(buf, \" NULL ON NULL\");\n\t}\n\n\tif (ctor->unique)\n\t\tappendStringInfoString(buf, \" WITH UNIQUE KEYS\");\n\n\t/*\n\t * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't\n\t * support one.\n\t */\n\tif (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)\n\t\tget_json_returning(ctor->returning, buf, true);\n}\n\n/*\n * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node\n */\nstatic void\nget_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,\n\t\t\t\t\t\t const char *funcname, bool is_json_objectagg)\n{\n\tStringInfoData options;\n\n\tinitStringInfo(&options);\n\tget_json_constructor_options(ctor, &options);\n\n\tif (IsA(ctor->func, Aggref))\n\t\tget_agg_expr_helper((Aggref *) ctor->func, context,\n\t\t\t\t\t\t\t(Aggref *) ctor->func,\n\t\t\t\t\t\t\tfuncname, options.data, is_json_objectagg);\n\telse if (IsA(ctor->func, WindowFunc))\n\t\tget_windowfunc_expr_helper((WindowFunc *) ctor->func, context,\n\t\t\t\t\t\t\t\t   funcname, options.data,\n\t\t\t\t\t\t\t\t   is_json_objectagg);\n\telse\n\t\telog(ERROR, \"invalid JsonConstructorExpr underlying node type: %d\",\n\t\t\t nodeTag(ctor->func));\n}\n\n/*\n * simple_quote_literal - Format a string as a SQL literal, append to buf\n */\nstatic void\nsimple_quote_literal(StringInfo buf, const char *val)\n{\n\tconst char *valptr;\n\n\t/*\n\t * We form the string literal according to the prevailing setting of\n\t * standard_conforming_strings; we never use E''. User is responsible for\n\t * making sure result is used correctly.\n\t */\n\tappendStringInfoChar(buf, '\\'');\n\tfor (valptr = val; *valptr; valptr++)\n\t{\n\t\tchar\t\tch = *valptr;\n\n\t\tif (SQL_STR_DOUBLE(ch, !standard_conforming_strings))\n\t\t\tappendStringInfoChar(buf, ch);\n\t\tappendStringInfoChar(buf, ch);\n\t}\n\tappendStringInfoChar(buf, '\\'');\n}\n\n/* ----------\n * get_sublink_expr\t\t\t- Parse back a sublink\n * ----------\n */\nstatic void\nget_sublink_expr(SubLink *sublink, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tQuery\t   *query = (Query *) (sublink->subselect);\n\tchar\t   *opname = NULL;\n\tbool\t\tneed_paren;\n\n\tif (sublink->subLinkType == ARRAY_SUBLINK)\n\t\tappendStringInfoString(buf, \"ARRAY(\");\n\telse\n\t\tappendStringInfoChar(buf, '(');\n\n\t/*\n\t * Note that we print the name of only the first operator, when there are\n\t * multiple combining operators.  This is an approximation that could go\n\t * wrong in various scenarios (operators in different schemas, renamed\n\t * operators, etc) but there is not a whole lot we can do about it, since\n\t * the syntax allows only one operator to be shown.\n\t */\n\tif (sublink->testexpr)\n\t{\n\t\tif (IsA(sublink->testexpr, OpExpr))\n\t\t{\n\t\t\t/* single combining operator */\n\t\t\tOpExpr\t   *opexpr = (OpExpr *) sublink->testexpr;\n\n\t\t\tget_rule_expr(linitial(opexpr->args), context, true);\n\t\t\topname = generate_operator_name(opexpr->opno,\n\t\t\t\t\t\t\t\t\t\t\texprType(linitial(opexpr->args)),\n\t\t\t\t\t\t\t\t\t\t\texprType(lsecond(opexpr->args)));\n\t\t}\n\t\telse if (IsA(sublink->testexpr, BoolExpr))\n\t\t{\n\t\t\t/* multiple combining operators, = or <> cases */\n\t\t\tchar\t   *sep;\n\t\t\tListCell   *l;\n\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tsep = \"\";\n\t\t\tforeach(l, ((BoolExpr *) sublink->testexpr)->args)\n\t\t\t{\n\t\t\t\tOpExpr\t   *opexpr = lfirst_node(OpExpr, l);\n\n\t\t\t\tappendStringInfoString(buf, sep);\n\t\t\t\tget_rule_expr(linitial(opexpr->args), context, true);\n\t\t\t\tif (!opname)\n\t\t\t\t\topname = generate_operator_name(opexpr->opno,\n\t\t\t\t\t\t\t\t\t\t\t\t\texprType(linitial(opexpr->args)),\n\t\t\t\t\t\t\t\t\t\t\t\t\texprType(lsecond(opexpr->args)));\n\t\t\t\tsep = \", \";\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse if (IsA(sublink->testexpr, RowCompareExpr))\n\t\t{\n\t\t\t/* multiple combining operators, < <= > >= cases */\n\t\t\tRowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;\n\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr((Node *) rcexpr->largs, context, true);\n\t\t\topname = generate_operator_name(linitial_oid(rcexpr->opnos),\n\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->largs)),\n\t\t\t\t\t\t\t\t\t\t\texprType(linitial(rcexpr->rargs)));\n\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse\n\t\t\telog(ERROR, \"unrecognized testexpr type: %d\",\n\t\t\t\t (int) nodeTag(sublink->testexpr));\n\t}\n\n\tneed_paren = true;\n\n\tswitch (sublink->subLinkType)\n\t{\n\t\tcase EXISTS_SUBLINK:\n\t\t\tappendStringInfoString(buf, \"EXISTS \");\n\t\t\tbreak;\n\n\t\tcase ANY_SUBLINK:\n\t\t\tif (strcmp(opname, \"=\") == 0)\t/* Represent = ANY as IN */\n\t\t\t\tappendStringInfoString(buf, \" IN \");\n\t\t\telse\n\t\t\t\tappendStringInfo(buf, \" %s ANY \", opname);\n\t\t\tbreak;\n\n\t\tcase ALL_SUBLINK:\n\t\t\tappendStringInfo(buf, \" %s ALL \", opname);\n\t\t\tbreak;\n\n\t\tcase ROWCOMPARE_SUBLINK:\n\t\t\tappendStringInfo(buf, \" %s \", opname);\n\t\t\tbreak;\n\n\t\tcase EXPR_SUBLINK:\n\t\tcase MULTIEXPR_SUBLINK:\n\t\tcase ARRAY_SUBLINK:\n\t\t\tneed_paren = false;\n\t\t\tbreak;\n\n\t\tcase CTE_SUBLINK:\t\t/* shouldn't occur in a SubLink */\n\t\tdefault:\n\t\t\telog(ERROR, \"unrecognized sublink type: %d\",\n\t\t\t\t (int) sublink->subLinkType);\n\t\t\tbreak;\n\t}\n\n\tif (need_paren)\n\t\tappendStringInfoChar(buf, '(');\n\n\tget_query_def(query, buf, context->namespaces, NULL, false,\n\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t  context->indentLevel);\n\n\tif (need_paren)\n\t\tappendStringInfoString(buf, \"))\");\n\telse\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/* ----------\n * get_xmltable\t\t\t- Parse back a XMLTABLE function\n * ----------\n */\nstatic void\nget_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\n\tappendStringInfoString(buf, \"XMLTABLE(\");\n\n\tif (tf->ns_uris != NIL)\n\t{\n\t\tListCell   *lc1,\n\t\t\t\t   *lc2;\n\t\tbool\t\tfirst = true;\n\n\t\tappendStringInfoString(buf, \"XMLNAMESPACES (\");\n\t\tforboth(lc1, tf->ns_uris, lc2, tf->ns_names)\n\t\t{\n\t\t\tNode\t   *expr = (Node *) lfirst(lc1);\n\t\t\tchar\t   *name = strVal(lfirst(lc2));\n\n\t\t\tif (!first)\n\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\telse\n\t\t\t\tfirst = false;\n\n\t\t\tif (name != NULL)\n\t\t\t{\n\t\t\t\tget_rule_expr(expr, context, showimplicit);\n\t\t\t\tappendStringInfo(buf, \" AS %s\", quote_identifier(name));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \"DEFAULT \");\n\t\t\t\tget_rule_expr(expr, context, showimplicit);\n\t\t\t}\n\t\t}\n\t\tappendStringInfoString(buf, \"), \");\n\t}\n\n\tappendStringInfoChar(buf, '(');\n\tget_rule_expr((Node *) tf->rowexpr, context, showimplicit);\n\tappendStringInfoString(buf, \") PASSING (\");\n\tget_rule_expr((Node *) tf->docexpr, context, showimplicit);\n\tappendStringInfoChar(buf, ')');\n\n\tif (tf->colexprs != NIL)\n\t{\n\t\tListCell   *l1;\n\t\tListCell   *l2;\n\t\tListCell   *l3;\n\t\tListCell   *l4;\n\t\tListCell   *l5;\n\t\tint\t\t\tcolnum = 0;\n\n\t\tappendStringInfoString(buf, \" COLUMNS \");\n\t\tforfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,\n\t\t\t\tl4, tf->colexprs, l5, tf->coldefexprs)\n\t\t{\n\t\t\tchar\t   *colname = strVal(lfirst(l1));\n\t\t\tOid\t\t\ttypid = lfirst_oid(l2);\n\t\t\tint32\t\ttypmod = lfirst_int(l3);\n\t\t\tNode\t   *colexpr = (Node *) lfirst(l4);\n\t\t\tNode\t   *coldefexpr = (Node *) lfirst(l5);\n\t\t\tbool\t\tordinality = (tf->ordinalitycol == colnum);\n\t\t\tbool\t\tnotnull = bms_is_member(colnum, tf->notnulls);\n\n\t\t\tif (colnum > 0)\n\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tcolnum++;\n\n\t\t\tappendStringInfo(buf, \"%s %s\", quote_identifier(colname),\n\t\t\t\t\t\t\t ordinality ? \"FOR ORDINALITY\" :\n\t\t\t\t\t\t\t format_type_with_typemod(typid, typmod));\n\t\t\tif (ordinality)\n\t\t\t\tcontinue;\n\n\t\t\tif (coldefexpr != NULL)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" DEFAULT (\");\n\t\t\t\tget_rule_expr((Node *) coldefexpr, context, showimplicit);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tif (colexpr != NULL)\n\t\t\t{\n\t\t\t\tappendStringInfoString(buf, \" PATH (\");\n\t\t\t\tget_rule_expr((Node *) colexpr, context, showimplicit);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t}\n\t\t\tif (notnull)\n\t\t\t\tappendStringInfoString(buf, \" NOT NULL\");\n\t\t}\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_json_table_nested_columns - Parse back nested JSON_TABLE columns\n */\nstatic void\nget_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,\n\t\t\t\t\t\t\t  deparse_context *context, bool showimplicit,\n\t\t\t\t\t\t\t  bool needcomma)\n{\n\tif (IsA(plan, JsonTablePathScan))\n\t{\n\t\tJsonTablePathScan *scan = castNode(JsonTablePathScan, plan);\n\n\t\tif (needcomma)\n\t\t\tappendStringInfoChar(context->buf, ',');\n\n\t\tappendStringInfoChar(context->buf, ' ');\n\t\tappendContextKeyword(context, \"NESTED PATH \", 0, 0, 0);\n\t\tget_const_expr(scan->path->value, context, -1);\n\t\tappendStringInfo(context->buf, \" AS %s\", quote_identifier(scan->path->name));\n\t\tget_json_table_columns(tf, scan, context, showimplicit);\n\t}\n\telse if (IsA(plan, JsonTableSiblingJoin))\n\t{\n\t\tJsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;\n\n\t\tget_json_table_nested_columns(tf, join->lplan, context, showimplicit,\n\t\t\t\t\t\t\t\t\t  needcomma);\n\t\tget_json_table_nested_columns(tf, join->rplan, context, showimplicit,\n\t\t\t\t\t\t\t\t\t  true);\n\t}\n}\n\n/*\n * get_json_table_columns - Parse back JSON_TABLE columns\n */\nstatic void\nget_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,\n\t\t\t\t\t   deparse_context *context,\n\t\t\t\t\t   bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\tListCell   *lc_colname;\n\tListCell   *lc_coltype;\n\tListCell   *lc_coltypmod;\n\tListCell   *lc_colvalexpr;\n\tint\t\t\tcolnum = 0;\n\n\tappendStringInfoChar(buf, ' ');\n\tappendContextKeyword(context, \"COLUMNS (\", 0, 0, 0);\n\n\tif (PRETTY_INDENT(context))\n\t\tcontext->indentLevel += PRETTYINDENT_VAR;\n\n\tforfour(lc_colname, tf->colnames,\n\t\t\tlc_coltype, tf->coltypes,\n\t\t\tlc_coltypmod, tf->coltypmods,\n\t\t\tlc_colvalexpr, tf->colvalexprs)\n\t{\n\t\tchar\t   *colname = strVal(lfirst(lc_colname));\n\t\tJsonExpr   *colexpr;\n\t\tOid\t\t\ttypid;\n\t\tint32\t\ttypmod;\n\t\tbool\t\tordinality;\n\t\tJsonBehaviorType default_behavior;\n\n\t\ttypid = lfirst_oid(lc_coltype);\n\t\ttypmod = lfirst_int(lc_coltypmod);\n\t\tcolexpr = castNode(JsonExpr, lfirst(lc_colvalexpr));\n\n\t\t/* Skip columns that don't belong to this scan. */\n\t\tif (scan->colMin < 0 || colnum < scan->colMin)\n\t\t{\n\t\t\tcolnum++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (colnum > scan->colMax)\n\t\t\tbreak;\n\n\t\tif (colnum > scan->colMin)\n\t\t\tappendStringInfoString(buf, \", \");\n\n\t\tcolnum++;\n\n\t\tordinality = !colexpr;\n\n\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\n\t\tappendStringInfo(buf, \"%s %s\", quote_identifier(colname),\n\t\t\t\t\t\t ordinality ? \"FOR ORDINALITY\" :\n\t\t\t\t\t\t format_type_with_typemod(typid, typmod));\n\t\tif (ordinality)\n\t\t\tcontinue;\n\n\t\t/*\n\t\t * Set default_behavior to guide get_json_expr_options() on whether to\n\t\t * emit the ON ERROR / EMPTY clauses.\n\t\t */\n\t\tif (colexpr->op == JSON_EXISTS_OP)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" EXISTS\");\n\t\t\tdefault_behavior = JSON_BEHAVIOR_FALSE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (colexpr->op == JSON_QUERY_OP)\n\t\t\t{\n\t\t\t\tchar\t\ttypcategory;\n\t\t\t\tbool\t\ttypispreferred;\n\n\t\t\t\tget_type_category_preferred(typid, &typcategory, &typispreferred);\n\n\t\t\t\tif (typcategory == TYPCATEGORY_STRING)\n\t\t\t\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t\t\t\t   colexpr->format->format_type == JS_FORMAT_JSONB ?\n\t\t\t\t\t\t\t\t\t\t   \" FORMAT JSONB\" : \" FORMAT JSON\");\n\t\t\t}\n\n\t\t\tdefault_behavior = JSON_BEHAVIOR_NULL;\n\t\t}\n\n\t\tappendStringInfoString(buf, \" PATH \");\n\n\t\tget_json_path_spec(colexpr->path_spec, context, showimplicit);\n\n\t\tget_json_expr_options(colexpr, context, default_behavior);\n\t}\n\n\tif (scan->child)\n\t\tget_json_table_nested_columns(tf, scan->child, context, showimplicit,\n\t\t\t\t\t\t\t\t\t  scan->colMin >= 0);\n\n\tif (PRETTY_INDENT(context))\n\t\tcontext->indentLevel -= PRETTYINDENT_VAR;\n\n\tappendContextKeyword(context, \")\", 0, 0, 0);\n}\n\n/* ----------\n * get_json_table\t\t\t- Parse back a JSON_TABLE function\n * ----------\n */\nstatic void\nget_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)\n{\n\tStringInfo\tbuf = context->buf;\n\tJsonExpr   *jexpr = castNode(JsonExpr, tf->docexpr);\n\tJsonTablePathScan *root = castNode(JsonTablePathScan, tf->plan);\n\n\tappendStringInfoString(buf, \"JSON_TABLE(\");\n\n\tif (PRETTY_INDENT(context))\n\t\tcontext->indentLevel += PRETTYINDENT_VAR;\n\n\tappendContextKeyword(context, \"\", 0, 0, 0);\n\n\tget_rule_expr(jexpr->formatted_expr, context, showimplicit);\n\n\tappendStringInfoString(buf, \", \");\n\n\tget_const_expr(root->path->value, context, -1);\n\n\tappendStringInfo(buf, \" AS %s\", quote_identifier(root->path->name));\n\n\tif (jexpr->passing_values)\n\t{\n\t\tListCell   *lc1,\n\t\t\t\t   *lc2;\n\t\tbool\t\tneedcomma = false;\n\n\t\tappendStringInfoChar(buf, ' ');\n\t\tappendContextKeyword(context, \"PASSING \", 0, 0, 0);\n\n\t\tif (PRETTY_INDENT(context))\n\t\t\tcontext->indentLevel += PRETTYINDENT_VAR;\n\n\t\tforboth(lc1, jexpr->passing_names,\n\t\t\t\tlc2, jexpr->passing_values)\n\t\t{\n\t\t\tif (needcomma)\n\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\tneedcomma = true;\n\n\t\t\tappendContextKeyword(context, \"\", 0, 0, 0);\n\n\t\t\tget_rule_expr((Node *) lfirst(lc2), context, false);\n\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t quote_identifier((lfirst_node(String, lc1))->sval)\n\t\t\t\t);\n\t\t}\n\n\t\tif (PRETTY_INDENT(context))\n\t\t\tcontext->indentLevel -= PRETTYINDENT_VAR;\n\t}\n\n\tget_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,\n\t\t\t\t\t\t   showimplicit);\n\n\tif (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)\n\t\tget_json_behavior(jexpr->on_error, context, \"ERROR\");\n\n\tif (PRETTY_INDENT(context))\n\t\tcontext->indentLevel -= PRETTYINDENT_VAR;\n\n\tappendContextKeyword(context, \")\", 0, 0, 0);\n}\n\n/* ----------\n * get_tablefunc\t\t\t- Parse back a table function\n * ----------\n */\nstatic void\nget_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)\n{\n\t/* XMLTABLE and JSON_TABLE are the only existing implementations.  */\n\n\tif (tf->functype == TFT_XMLTABLE)\n\t\tget_xmltable(tf, context, showimplicit);\n\telse if (tf->functype == TFT_JSON_TABLE)\n\t\tget_json_table(tf, context, showimplicit);\n}\n\n/* ----------\n * get_from_clause\t\t\t- Parse back a FROM clause\n *\n * \"prefix\" is the keyword that denotes the start of the list of FROM\n * elements. It is FROM when used to parse back SELECT and UPDATE, but\n * is USING when parsing back DELETE.\n * ----------\n */\nstatic void\nget_from_clause(Query *query, const char *prefix, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tbool\t\tfirst = true;\n\tListCell   *l;\n\n\t/*\n\t * We use the query's jointree as a guide to what to print.  However, we\n\t * must ignore auto-added RTEs that are marked not inFromCl. (These can\n\t * only appear at the top level of the jointree, so it's sufficient to\n\t * check here.)  This check also ensures we ignore the rule pseudo-RTEs\n\t * for NEW and OLD.\n\t */\n\tforeach(l, query->jointree->fromlist)\n\t{\n\t\tNode\t   *jtnode = (Node *) lfirst(l);\n\n\t\tif (IsA(jtnode, RangeTblRef))\n\t\t{\n\t\t\tint\t\t\tvarno = ((RangeTblRef *) jtnode)->rtindex;\n\t\t\tRangeTblEntry *rte = rt_fetch(varno, query->rtable);\n\n\t\t\tif (!rte->inFromCl)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tif (first)\n\t\t{\n\t\t\tappendContextKeyword(context, prefix,\n\t\t\t\t\t\t\t\t -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);\n\t\t\tfirst = false;\n\n\t\t\tget_from_clause_item(jtnode, query, context);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tStringInfoData itembuf;\n\n\t\t\tappendStringInfoString(buf, \", \");\n\n\t\t\t/*\n\t\t\t * Put the new FROM item's text into itembuf so we can decide\n\t\t\t * after we've got it whether or not it needs to go on a new line.\n\t\t\t */\n\t\t\tinitStringInfo(&itembuf);\n\t\t\tcontext->buf = &itembuf;\n\n\t\t\tget_from_clause_item(jtnode, query, context);\n\n\t\t\t/* Restore context's output buffer */\n\t\t\tcontext->buf = buf;\n\n\t\t\t/* Consider line-wrapping if enabled */\n\t\t\tif (PRETTY_INDENT(context) && context->wrapColumn >= 0)\n\t\t\t{\n\t\t\t\t/* Does the new item start with a new line? */\n\t\t\t\tif (itembuf.len > 0 && itembuf.data[0] == '\\n')\n\t\t\t\t{\n\t\t\t\t\t/* If so, we shouldn't add anything */\n\t\t\t\t\t/* instead, remove any trailing spaces currently in buf */\n\t\t\t\t\tremoveStringInfoSpaces(buf);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tchar\t   *trailing_nl;\n\n\t\t\t\t\t/* Locate the start of the current line in the buffer */\n\t\t\t\t\ttrailing_nl = strrchr(buf->data, '\\n');\n\t\t\t\t\tif (trailing_nl == NULL)\n\t\t\t\t\t\ttrailing_nl = buf->data;\n\t\t\t\t\telse\n\t\t\t\t\t\ttrailing_nl++;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Add a newline, plus some indentation, if the new item\n\t\t\t\t\t * would cause an overflow.\n\t\t\t\t\t */\n\t\t\t\t\tif (strlen(trailing_nl) + itembuf.len > context->wrapColumn)\n\t\t\t\t\t\tappendContextKeyword(context, \"\", -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_VAR);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Add the new item */\n\t\t\tappendStringInfoString(buf, itembuf.data);\n\n\t\t\t/* clean up */\n\t\t\tpfree(itembuf.data);\n\t\t}\n\t}\n}\n\nstatic void\nget_from_clause_item(Node *jtnode, Query *query, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tdeparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);\n\n\tif (IsA(jtnode, RangeTblRef))\n\t{\n\t\tint\t\t\tvarno = ((RangeTblRef *) jtnode)->rtindex;\n\t\tRangeTblEntry *rte = rt_fetch(varno, query->rtable);\n\t\tdeparse_columns *colinfo = deparse_columns_fetch(varno, dpns);\n\t\tRangeTblFunction *rtfunc1 = NULL;\n\t\tCitusRTEKind rteKind = GetRangeTblKind(rte);\n\n\t\tif (rte->lateral)\n\t\t\tappendStringInfoString(buf, \"LATERAL \");\n\n\t\t/* Print the FROM item proper */\n\t\tswitch (rte->rtekind)\n\t\t{\n\t\t\tcase RTE_RELATION:\n\t\t\t\t/* Normal relation RTE */\n\t\t\t\tappendStringInfo(buf, \"%s%s\",\n\t\t\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t\t\t generate_relation_or_shard_name(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->distrelid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->shardid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t context->namespaces));\n\t\t\t\tbreak;\n\t\t\tcase RTE_SUBQUERY:\n\t\t\t\t/* Subquery RTE */\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_query_def(rte->subquery, buf, context->namespaces, NULL,\n\t\t\t\t              true,\n\t\t\t\t\t\t\t  context->prettyFlags, context->wrapColumn,\n\t\t\t\t\t\t\t  context->indentLevel);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tbreak;\n\t\t\tcase RTE_FUNCTION:\n\t\t\t\t/* if it's a shard, do differently */\n\t\t\t\tif (GetRangeTblKind(rte) == CITUS_RTE_SHARD)\n\t\t\t\t{\n\t\t\t\t\tchar *fragmentSchemaName = NULL;\n\t\t\t\t\tchar *fragmentTableName = NULL;\n\n\t\t\t\t\tExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL);\n\n\t\t\t\t\t/* use schema and table name from the remote alias */\n\t\t\t\t\tappendStringInfo(buf, \"%s%s\",\n\t\t\t\t\t\t\t\t\t only_marker(rte),\n\t\t\t\t\t\t\t\t\t generate_fragment_name(fragmentSchemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfragmentTableName));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t/* Function RTE */\n\t\t\t\trtfunc1 = (RangeTblFunction *) linitial(rte->functions);\n\n\t\t\t\t/*\n\t\t\t\t * Omit ROWS FROM() syntax for just one function, unless it\n\t\t\t\t * has both a coldeflist and WITH ORDINALITY. If it has both,\n\t\t\t\t * we must use ROWS FROM() syntax to avoid ambiguity about\n\t\t\t\t * whether the coldeflist includes the ordinality column.\n\t\t\t\t */\n\t\t\t\tif (list_length(rte->functions) == 1 &&\n\t\t\t\t\t(rtfunc1->funccolnames == NIL || !rte->funcordinality))\n\t\t\t\t{\n\t\t\t\t\tget_rule_expr_funccall(rtfunc1->funcexpr, context, true);\n\t\t\t\t\t/* we'll print the coldeflist below, if it has one */\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tbool\t\tall_unnest;\n\t\t\t\t\tListCell   *lc;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * If all the function calls in the list are to unnest,\n\t\t\t\t\t * and none need a coldeflist, then collapse the list back\n\t\t\t\t\t * down to UNNEST(args).  (If we had more than one\n\t\t\t\t\t * built-in unnest function, this would get more\n\t\t\t\t\t * difficult.)\n\t\t\t\t\t *\n\t\t\t\t\t * XXX This is pretty ugly, since it makes not-terribly-\n\t\t\t\t\t * future-proof assumptions about what the parser would do\n\t\t\t\t\t * with the output; but the alternative is to emit our\n\t\t\t\t\t * nonstandard ROWS FROM() notation for what might have\n\t\t\t\t\t * been a perfectly spec-compliant multi-argument\n\t\t\t\t\t * UNNEST().\n\t\t\t\t\t */\n\t\t\t\t\tall_unnest = true;\n\t\t\t\t\tforeach(lc, rte->functions)\n\t\t\t\t\t{\n\t\t\t\t\t\tRangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);\n\n\t\t\t\t\t\tif (!IsA(rtfunc->funcexpr, FuncExpr) ||\n\t\t\t\t\t\t\t((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||\n\t\t\t\t\t\t\trtfunc->funccolnames != NIL)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tall_unnest = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (all_unnest)\n\t\t\t\t\t{\n\t\t\t\t\t\tList\t   *allargs = NIL;\n\n\t\t\t\t\t\tforeach(lc, rte->functions)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);\n\t\t\t\t\t\t\tList\t   *args = ((FuncExpr *) rtfunc->funcexpr)->args;\n\n\t\t\t\t\t\t\tallargs = list_concat(allargs, args);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"UNNEST(\");\n\t\t\t\t\t\tget_rule_expr((Node *) allargs, context, true);\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tint\t\t\tfuncno = 0;\n\n\t\t\t\t\t\tappendStringInfoString(buf, \"ROWS FROM(\");\n\t\t\t\t\t\tforeach(lc, rte->functions)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tRangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);\n\n\t\t\t\t\t\t\tif (funcno > 0)\n\t\t\t\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\t\t\t\tget_rule_expr_funccall(rtfunc->funcexpr, context, true);\n\t\t\t\t\t\t\tif (rtfunc->funccolnames != NIL)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t/* Reconstruct the column definition list */\n\t\t\t\t\t\t\t\tappendStringInfoString(buf, \" AS \");\n\t\t\t\t\t\t\t\tget_from_clause_coldeflist(rtfunc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   context);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tfuncno++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\t\t}\n\t\t\t\t\t/* prevent printing duplicate coldeflist below */\n\t\t\t\t\trtfunc1 = NULL;\n\t\t\t\t}\n\t\t\t\tif (rte->funcordinality)\n\t\t\t\t\tappendStringInfoString(buf, \" WITH ORDINALITY\");\n\t\t\t\tbreak;\n\t\t\tcase RTE_TABLEFUNC:\n\t\t\t\tget_tablefunc(rte->tablefunc, context, true);\n\t\t\t\tbreak;\n\t\t\tcase RTE_VALUES:\n\t\t\t\t/* Values list RTE */\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\t\tget_values_def(rte->values_lists, context);\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t\t\tbreak;\n\t\t\tcase RTE_CTE:\n\t\t\t\tappendStringInfoString(buf, quote_identifier(rte->ctename));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\telog(ERROR, \"unrecognized RTE kind: %d\", (int) rte->rtekind);\n\t\t\t\tbreak;\n\t\t}\n\n\t\t/* Print the relation alias, if needed */\n\t\tget_rte_alias(rte, varno, false, context);\n\n\t\t/* Print the column definitions or aliases, if needed */\n\t\tif (rtfunc1 && rtfunc1->funccolnames != NIL)\n\t\t{\n\t\t\t/* Reconstruct the columndef list, which is also the aliases */\n\t\t\tget_from_clause_coldeflist(rtfunc1, colinfo, context);\n\t\t}\n\t\telse if (GetRangeTblKind(rte) != CITUS_RTE_SHARD ||\n\t\t\t\t (rte->alias != NULL && rte->alias->colnames != NIL))\n\t\t{\n\t\t\t/* Else print column aliases as needed */\n\t\t\tget_column_alias_list(colinfo, context);\n\t\t}\n\t\t/* check if column's are given aliases in distributed tables */\n\t\telse if (colinfo->parentUsing != NIL)\n\t\t{\n\t\t\tAssert(colinfo->printaliases);\n\t\t\tget_column_alias_list(colinfo, context);\n\t\t}\n\n\t\t/* Tablesample clause must go after any alias */\n\t\tif ((rteKind == CITUS_RTE_RELATION || rteKind == CITUS_RTE_SHARD) &&\n\t\t\trte->tablesample)\n\t\t{\n\t\t\tget_tablesample_def(rte->tablesample, context);\n\t\t}\n\t}\n\telse if (IsA(jtnode, JoinExpr))\n\t{\n\t\tJoinExpr   *j = (JoinExpr *) jtnode;\n\t\tdeparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);\n\t\tbool\t\tneed_paren_on_right;\n\n\t\tneed_paren_on_right = PRETTY_PAREN(context) &&\n\t\t\t!IsA(j->rarg, RangeTblRef) &&\n\t\t\t!(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);\n\n\t\tif (!PRETTY_PAREN(context) || j->alias != NULL)\n\t\t\tappendStringInfoChar(buf, '(');\n\n\t\tget_from_clause_item(j->larg, query, context);\n\n\t\tswitch (j->jointype)\n\t\t{\n\t\t\tcase JOIN_INNER:\n\t\t\t\tif (j->quals)\n\t\t\t\t\tappendContextKeyword(context, \" JOIN \",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\telse\n\t\t\t\t\tappendContextKeyword(context, \" CROSS JOIN \",\n\t\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tcase JOIN_LEFT:\n\t\t\t\tappendContextKeyword(context, \" LEFT JOIN \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tcase JOIN_FULL:\n\t\t\t\tappendContextKeyword(context, \" FULL JOIN \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tcase JOIN_RIGHT:\n\t\t\t\tappendContextKeyword(context, \" RIGHT JOIN \",\n\t\t\t\t\t\t\t\t\t -PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_STD,\n\t\t\t\t\t\t\t\t\t PRETTYINDENT_JOIN);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\telog(ERROR, \"unrecognized join type: %d\",\n\t\t\t\t\t (int) j->jointype);\n\t\t}\n\n\t\tif (need_paren_on_right)\n\t\t\tappendStringInfoChar(buf, '(');\n\t\tget_from_clause_item(j->rarg, query, context);\n\t\tif (need_paren_on_right)\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\tif (j->usingClause)\n\t\t{\n\t\t\tListCell   *lc;\n\t\t\tbool\t\tfirst = true;\n\n\t\t\tappendStringInfoString(buf, \" USING (\");\n\t\t\t/* Use the assigned names, not what's in usingClause */\n\t\t\tforeach(lc, colinfo->usingNames)\n\t\t\t{\n\t\t\t\tchar\t   *colname = (char *) lfirst(lc);\n\n\t\t\t\tif (first)\n\t\t\t\t\tfirst = false;\n\t\t\t\telse\n\t\t\t\t\tappendStringInfoString(buf, \", \");\n\t\t\t\tappendStringInfoString(buf, quote_identifier(colname));\n\t\t\t}\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t\tif (j->join_using_alias)\n\t\t\t\tappendStringInfo(buf, \" AS %s\",\n\t\t\t\t\t\t\t\t quote_identifier(j->join_using_alias->aliasname));\n\t\t}\n\t\telse if (j->quals)\n\t\t{\n\t\t\tappendStringInfoString(buf, \" ON \");\n\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tget_rule_expr(j->quals, context, false);\n\t\t\tif (!PRETTY_PAREN(context))\n\t\t\t\tappendStringInfoChar(buf, ')');\n\t\t}\n\t\telse if (j->jointype != JOIN_INNER)\n\t\t{\n\t\t\t/* If we didn't say CROSS JOIN above, we must provide an ON */\n\t\t\tappendStringInfoString(buf, \" ON TRUE\");\n\t\t}\n\n\t\tif (!PRETTY_PAREN(context) || j->alias != NULL)\n\t\t\tappendStringInfoChar(buf, ')');\n\n\t\t/* Yes, it's correct to put alias after the right paren ... */\n\t\tif (j->alias != NULL)\n\t\t{\n\t\t\t/*\n\t\t\t * Note that it's correct to emit an alias clause if and only if\n\t\t\t * there was one originally.  Otherwise we'd be converting a named\n\t\t\t * join to unnamed or vice versa, which creates semantic\n\t\t\t * subtleties we don't want.  However, we might print a different\n\t\t\t * alias name than was there originally.\n\t\t\t */\n\t\t\tappendStringInfo(buf, \" %s\",\n\t\t\t\t\t\t\t quote_identifier(get_rtable_name(j->rtindex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  context)));\n\t\t\tget_column_alias_list(colinfo, context);\n\t\t}\n\t}\n\telse\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(jtnode));\n}\n\n/*\n * get_rte_alias - print the relation's alias, if needed\n *\n * If printed, the alias is preceded by a space, or by \" AS \" if use_as is true.\n */\nstatic void\nget_rte_alias(RangeTblEntry *rte, int varno, bool use_as,\n\t\t\t  deparse_context *context)\n{\n\tdeparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);\n\tchar\t   *refname = get_rtable_name(varno, context);\n\tdeparse_columns *colinfo = deparse_columns_fetch(varno, dpns);\n\tbool\t\tprintalias = false;\n\n\tif (rte->alias != NULL)\n\t{\n\t\t/* Always print alias if user provided one */\n\t\tprintalias = true;\n\t}\n\telse if (colinfo->printaliases)\n\t{\n\t\t/* Always print alias if we need to print column aliases */\n\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_RELATION)\n\t{\n\t\t/*\n\t\t * No need to print alias if it's same as relation name (this would\n\t\t * normally be the case, but not if set_rtable_names had to resolve a\n\t\t * conflict).\n\t\t */\n\t\tif (strcmp(refname, get_relation_name(rte->relid)) != 0)\n\t\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_FUNCTION)\n\t{\n\t\t/*\n\t\t * For a function RTE, always print alias.  This covers possible\n\t\t * renaming of the function and/or instability of the FigureColname\n\t\t * rules for things that aren't simple functions.  Note we'd need to\n\t\t * force it anyway for the columndef list case.\n\t\t */\n\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_SUBQUERY ||\n\t\t\t rte->rtekind == RTE_VALUES)\n\t{\n\t\t/*\n\t\t * For a subquery, always print alias.  This makes the output\n\t\t * SQL-spec-compliant, even though we allow such aliases to be omitted\n\t\t * on input.\n\t\t */\n\t\tprintalias = true;\n\t}\n\telse if (rte->rtekind == RTE_CTE)\n\t{\n\t\t/*\n\t\t * No need to print alias if it's same as CTE name (this would\n\t\t * normally be the case, but not if set_rtable_names had to resolve a\n\t\t * conflict).\n\t\t */\n\t\tif (strcmp(refname, rte->ctename) != 0)\n\t\t\tprintalias = true;\n\t}\n\n\tif (printalias)\n\t\tappendStringInfo(context->buf, \"%s%s\",\n\t\t\t\t\t\t use_as ? \" AS \" : \" \",\n\t\t\t\t\t\t quote_identifier(refname));\n}\n\n/*\n * get_column_alias_list - print column alias list for an RTE\n *\n * Caller must already have printed the relation's alias name.\n */\nstatic void\nget_column_alias_list(deparse_columns *colinfo, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tint\t\t\ti;\n\tbool\t\tfirst = true;\n\n\t/* Don't print aliases if not needed */\n\tif (!colinfo->printaliases)\n\t\treturn;\n\n\tfor (i = 0; i < colinfo->num_new_cols; i++)\n\t{\n\t\tchar\t   *colname = colinfo->new_colnames[i];\n\n\t\tif (first)\n\t\t{\n\t\t\tappendStringInfoChar(buf, '(');\n\t\t\tfirst = false;\n\t\t}\n\t\telse\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tappendStringInfoString(buf, quote_identifier(colname));\n\t}\n\tif (!first)\n\t\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_from_clause_coldeflist - reproduce FROM clause coldeflist\n *\n * When printing a top-level coldeflist (which is syntactically also the\n * relation's column alias list), use column names from colinfo.  But when\n * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the\n * original coldeflist's names, which are available in rtfunc->funccolnames.\n * Pass NULL for colinfo to select the latter behavior.\n *\n * The coldeflist is appended immediately (no space) to buf.  Caller is\n * responsible for ensuring that an alias or AS is present before it.\n */\nstatic void\nget_from_clause_coldeflist(RangeTblFunction *rtfunc,\n\t\t\t\t\t\t   deparse_columns *colinfo,\n\t\t\t\t\t\t   deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tListCell   *l1;\n\tListCell   *l2;\n\tListCell   *l3;\n\tListCell   *l4;\n\tint\t\t\ti;\n\n\tappendStringInfoChar(buf, '(');\n\n\ti = 0;\n\tforfour(l1, rtfunc->funccoltypes,\n\t\t\tl2, rtfunc->funccoltypmods,\n\t\t\tl3, rtfunc->funccolcollations,\n\t\t\tl4, rtfunc->funccolnames)\n\t{\n\t\tOid\t\t\tatttypid = lfirst_oid(l1);\n\t\tint32\t\tatttypmod = lfirst_int(l2);\n\t\tOid\t\t\tattcollation = lfirst_oid(l3);\n\t\tchar\t   *attname;\n\n\t\tif (colinfo)\n\t\t\tattname = colinfo->colnames[i];\n\t\telse\n\t\t\tattname = strVal(lfirst(l4));\n\n\t\tAssert(attname);\t\t/* shouldn't be any dropped columns here */\n\n\t\tif (i > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tappendStringInfo(buf, \"%s %s\",\n\t\t\t\t\t\t quote_identifier(attname),\n\t\t\t\t\t\t format_type_with_typemod(atttypid, atttypmod));\n\t\tif (OidIsValid(attcollation) &&\n\t\t\tattcollation != get_typcollation(atttypid))\n\t\t\tappendStringInfo(buf, \" COLLATE %s\",\n\t\t\t\t\t\t\t generate_collation_name(attcollation));\n\n\t\ti++;\n\t}\n\n\tappendStringInfoChar(buf, ')');\n}\n\n/*\n * get_tablesample_def\t\t\t- print a TableSampleClause\n */\nstatic void\nget_tablesample_def(TableSampleClause *tablesample, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tOid\t\t\targtypes[1];\n\tint\t\t\tnargs;\n\tListCell   *l;\n\n\t/*\n\t * We should qualify the handler's function name if it wouldn't be\n\t * resolved by lookup in the current search path.\n\t */\n\targtypes[0] = INTERNALOID;\n\tappendStringInfo(buf, \" TABLESAMPLE %s (\",\n\t\t\t\t\t generate_function_name(tablesample->tsmhandler, 1,\n\t\t\t\t\t\t\t\t\t\t\tNIL, argtypes,\n\t\t\t\t\t\t\t\t\t\t\tfalse, NULL, false));\n\n\tnargs = 0;\n\tforeach(l, tablesample->args)\n\t{\n\t\tif (nargs++ > 0)\n\t\t\tappendStringInfoString(buf, \", \");\n\t\tget_rule_expr((Node *) lfirst(l), context, false);\n\t}\n\tappendStringInfoChar(buf, ')');\n\n\tif (tablesample->repeatable != NULL)\n\t{\n\t\tappendStringInfoString(buf, \" REPEATABLE (\");\n\t\tget_rule_expr((Node *) tablesample->repeatable, context, false);\n\t\tappendStringInfoChar(buf, ')');\n\t}\n}\n\n/*\n * get_opclass_name\t\t\t- fetch name of an index operator class\n *\n * The opclass name is appended (after a space) to buf.\n *\n * Output is suppressed if the opclass is the default for the given\n * actual_datatype.  (If you don't want this behavior, just pass\n * InvalidOid for actual_datatype.)\n */\nstatic void\nget_opclass_name(Oid opclass, Oid actual_datatype,\n\t\t\t\t StringInfo buf)\n{\n\tHeapTuple\tht_opc;\n\tForm_pg_opclass opcrec;\n\tchar\t   *opcname;\n\tchar\t   *nspname;\n\n\tht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));\n\tif (!HeapTupleIsValid(ht_opc))\n\t\telog(ERROR, \"cache lookup failed for opclass %u\", opclass);\n\topcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);\n\n\tif (!OidIsValid(actual_datatype) ||\n\t\tGetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)\n\t{\n\t\t/* Okay, we need the opclass name.  Do we need to qualify it? */\n\t\topcname = NameStr(opcrec->opcname);\n\t\tif (OpclassIsVisible(opclass))\n\t\t\tappendStringInfo(buf, \" %s\", quote_identifier(opcname));\n\t\telse\n\t\t{\n\t\t\tnspname = get_namespace_name_or_temp(opcrec->opcnamespace);\n\t\t\tappendStringInfo(buf, \" %s.%s\",\n\t\t\t\t\t\t\t quote_identifier(nspname),\n\t\t\t\t\t\t\t quote_identifier(opcname));\n\t\t}\n\t}\n\tReleaseSysCache(ht_opc);\n}\n\n/*\n * processIndirection - take care of array and subfield assignment\n *\n * We strip any top-level FieldStore or assignment SubscriptingRef nodes that\n * appear in the input, printing them as decoration for the base column\n * name (which we assume the caller just printed).  We might also need to\n * strip CoerceToDomain nodes, but only ones that appear above assignment\n * nodes.\n *\n * Returns the subexpression that's to be assigned.\n */\nstatic Node *\nprocessIndirection(Node *node, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tCoerceToDomain *cdomain = NULL;\n\n\tfor (;;)\n\t{\n\t\tif (node == NULL)\n\t\t\tbreak;\n\t\tif (IsA(node, FieldStore))\n\t\t{\n\t\t\tFieldStore *fstore = (FieldStore *) node;\n\t\t\tOid\t\t\ttyprelid;\n\t\t\tchar\t   *fieldname;\n\n\t\t\t/* lookup tuple type */\n\t\t\ttyprelid = get_typ_typrelid(fstore->resulttype);\n\t\t\tif (!OidIsValid(typrelid))\n\t\t\t\telog(ERROR, \"argument type %s of FieldStore is not a tuple type\",\n\t\t\t\t\t format_type_be(fstore->resulttype));\n\n\t\t\t/*\n\t\t\t * Print the field name.  There should only be one target field in\n\t\t\t * stored rules.  There could be more than that in executable\n\t\t\t * target lists, but this function cannot be used for that case.\n\t\t\t */\n\t\t\tAssert(list_length(fstore->fieldnums) == 1);\n\t\t\tfieldname = get_attname(typrelid,\n\t\t\t\t\t\t\t\t\tlinitial_int(fstore->fieldnums), false);\n\t\t\tappendStringInfo(buf, \".%s\", quote_identifier(fieldname));\n\n\t\t\t/*\n\t\t\t * We ignore arg since it should be an uninteresting reference to\n\t\t\t * the target column or subcolumn.\n\t\t\t */\n\t\t\tnode = (Node *) linitial(fstore->newvals);\n\t\t}\n\t\telse if (IsA(node, SubscriptingRef))\n\t\t{\n\t\t\tSubscriptingRef   *sbsref = (SubscriptingRef *) node;\n\n\t\t\tif (sbsref->refassgnexpr == NULL)\n\t\t\t\tbreak;\n\t\t\tprintSubscripts(sbsref, context);\n\n\t\t\t/*\n\t\t\t * We ignore refexpr since it should be an uninteresting reference\n\t\t\t * to the target column or subcolumn.\n\t\t\t */\n\t\t\tnode = (Node *) sbsref->refassgnexpr;\n\t\t}\n\t\telse if (IsA(node, CoerceToDomain))\n\t\t{\n\t\t\tcdomain = (CoerceToDomain *) node;\n\t\t\t/* If it's an explicit domain coercion, we're done */\n\t\t\tif (cdomain->coercionformat != COERCE_IMPLICIT_CAST)\n\t\t\t\tbreak;\n\t\t\t/* Tentatively descend past the CoerceToDomain */\n\t\t\tnode = (Node *) cdomain->arg;\n\t\t}\n\t\telse\n\t\t\tbreak;\n\t}\n\n\t/*\n\t * If we descended past a CoerceToDomain whose argument turned out not to\n\t * be a FieldStore or array assignment, back up to the CoerceToDomain.\n\t * (This is not enough to be fully correct if there are nested implicit\n\t * CoerceToDomains, but such cases shouldn't ever occur.)\n\t */\n\tif (cdomain && node == (Node *) cdomain->arg)\n\t\tnode = (Node *) cdomain;\n\n\treturn node;\n}\n\nstatic void\nprintSubscripts(SubscriptingRef *sbsref, deparse_context *context)\n{\n\tStringInfo\tbuf = context->buf;\n\tListCell   *lowlist_item;\n\tListCell   *uplist_item;\n\n\tlowlist_item = list_head(sbsref->reflowerindexpr);\t/* could be NULL */\n\tforeach(uplist_item, sbsref->refupperindexpr)\n\t{\n\t\tappendStringInfoChar(buf, '[');\n\t\tif (lowlist_item)\n\t\t{\n\t\t\t/* If subexpression is NULL, get_rule_expr prints nothing */\n\t\t\tget_rule_expr((Node *) lfirst(lowlist_item), context, false);\n\t\t\tappendStringInfoChar(buf, ':');\n\t\t\tlowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item);\n\t\t}\n\t\t/* If subexpression is NULL, get_rule_expr prints nothing */\n\t\tget_rule_expr((Node *) lfirst(uplist_item), context, false);\n\t\tappendStringInfoChar(buf, ']');\n\t}\n}\n\n/*\n * get_relation_name\n *\t\tGet the unqualified name of a relation specified by OID\n *\n * This differs from the underlying get_rel_name() function in that it will\n * throw error instead of silently returning NULL if the OID is bad.\n */\nstatic char *\nget_relation_name(Oid relid)\n{\n\tchar\t   *relname = get_rel_name(relid);\n\n\tif (!relname)\n\t\telog(ERROR, \"cache lookup failed for relation %u\", relid);\n\treturn relname;\n}\n\n/*\n * generate_relation_or_shard_name\n *\t\tCompute the name to display for a relation or shard\n *\n * If the provided relid is equal to the provided distrelid, this function\n * returns a shard-extended relation name; otherwise, it falls through to a\n * simple generate_relation_name call.\n */\nstatic char *\ngenerate_relation_or_shard_name(Oid relid, Oid distrelid, int64 shardid,\n\t\t\t\t\t\t\t\tList *namespaces)\n{\n\tchar *relname = NULL;\n\n\tif (relid == distrelid)\n\t{\n\t\trelname = get_relation_name(relid);\n\n\t\tif (shardid > 0)\n\t\t{\n\t\t\tOid schemaOid = get_rel_namespace(relid);\n\t\t\tchar *schemaName = get_namespace_name_or_temp(schemaOid);\n\n\t\t\tAppendShardIdToName(&relname, shardid);\n\n\t\t\trelname = quote_qualified_identifier(schemaName, relname);\n\t\t}\n\t}\n\telse\n\t{\n\t\trelname = generate_relation_name(relid, namespaces);\n\t}\n\n\treturn relname;\n}\n\n/*\n * generate_relation_name\n *\t\tCompute the name to display for a relation specified by OID\n *\n * The result includes all necessary quoting and schema-prefixing.\n *\n * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.\n * We will forcibly qualify the relation name if it equals any CTE name\n * visible in the namespace list.\n */\nchar *\ngenerate_relation_name(Oid relid, List *namespaces)\n{\n\tHeapTuple\ttp;\n\tForm_pg_class reltup;\n\tbool\t\tneed_qual;\n\tListCell   *nslist;\n\tchar\t   *relname;\n\tchar\t   *nspname;\n\tchar\t   *result;\n\n\ttp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));\n\tif (!HeapTupleIsValid(tp))\n\t\telog(ERROR, \"cache lookup failed for relation %u\", relid);\n\treltup = (Form_pg_class) GETSTRUCT(tp);\n\trelname = NameStr(reltup->relname);\n\n\t/* Check for conflicting CTE name */\n\tneed_qual = false;\n\tforeach(nslist, namespaces)\n\t{\n\t\tdeparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);\n\t\tListCell   *ctlist;\n\n\t\tforeach(ctlist, dpns->ctes)\n\t\t{\n\t\t\tCommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);\n\n\t\t\tif (strcmp(cte->ctename, relname) == 0)\n\t\t\t{\n\t\t\t\tneed_qual = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (need_qual)\n\t\t\tbreak;\n\t}\n\n\t/* Otherwise, qualify the name if not visible in search path */\n\tif (!need_qual)\n\t\tneed_qual = !RelationIsVisible(relid);\n\n\tif (need_qual)\n\t\tnspname = get_namespace_name_or_temp(reltup->relnamespace);\n\telse\n\t\tnspname = NULL;\n\n\tresult = quote_qualified_identifier(nspname, relname);\n\n\tReleaseSysCache(tp);\n\n\treturn result;\n}\n\n/*\n * generate_rte_shard_name returns the qualified name of the shard given a\n * CITUS_RTE_SHARD range table entry.\n */\nstatic char *\ngenerate_rte_shard_name(RangeTblEntry *rangeTableEntry)\n{\n\tchar *shardSchemaName = NULL;\n\tchar *shardTableName = NULL;\n\n\tAssert(GetRangeTblKind(rangeTableEntry) == CITUS_RTE_SHARD);\n\n\tExtractRangeTblExtraData(rangeTableEntry, NULL, &shardSchemaName, &shardTableName,\n\t\t\t\t\t\t\t NULL);\n\n\treturn generate_fragment_name(shardSchemaName, shardTableName);\n}\n\n/*\n * generate_fragment_name\n *\t\tCompute the name to display for a shard or merged table\n *\n * The result includes all necessary quoting and schema-prefixing. The schema\n * name can be NULL for regular shards. For merged tables, they are always\n * declared within a job-specific schema, and therefore can't have null schema\n * names.\n */\nstatic char *\ngenerate_fragment_name(char *schemaName, char *tableName)\n{\n\tStringInfo fragmentNameString = makeStringInfo();\n\n\tif (schemaName != NULL)\n\t{\n\t\tappendStringInfo(fragmentNameString, \"%s.%s\", quote_identifier(schemaName),\n\t\t\t\t\t\t quote_identifier(tableName));\n\t}\n\telse\n\t{\n\t\tappendStringInfoString(fragmentNameString, quote_identifier(tableName));\n\t}\n\n\treturn fragmentNameString->data;\n}\n\n/*\n * generate_function_name\n *\t\tCompute the name to display for a function specified by OID,\n *\t\tgiven that it is being called with the specified actual arg names and\n *\t\ttypes.  (Those matter because of ambiguous-function resolution rules.)\n *\n * If we're dealing with a potentially variadic function (in practice, this\n * means a FuncExpr or Aggref, not some other way of calling a function), then\n * has_variadic must specify whether variadic arguments have been merged,\n * and *use_variadic_p will be set to indicate whether to print VARIADIC in\n * the output.  For non-FuncExpr cases, has_variadic should be false and\n * use_variadic_p can be NULL.\n *\n * inGroupBy must be true if we're deparsing a GROUP BY clause.\n *\n * The result includes all necessary quoting and schema-prefixing.\n */\nstatic char *\ngenerate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,\n\t\t\t\t\t   bool has_variadic, bool *use_variadic_p,\n\t\t\t\t\t   bool inGroupBy)\n{\n\tchar\t   *result;\n\tHeapTuple\tproctup;\n\tForm_pg_proc procform;\n\tchar\t   *proname;\n\tbool\t\tuse_variadic;\n\tchar\t   *nspname;\n\tFuncDetailCode p_result;\n\tOid\t\t\tp_funcid;\n\tOid\t\t\tp_rettype;\n\tbool\t\tp_retset;\n\tint\t\t\tp_nvargs;\n\tOid\t\t\tp_vatype;\n\tOid\t\t   *p_true_typeids;\n\tbool\t\tforce_qualify = false;\n\n\tproctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));\n\tif (!HeapTupleIsValid(proctup))\n\t\telog(ERROR, \"cache lookup failed for function %u\", funcid);\n\tprocform = (Form_pg_proc) GETSTRUCT(proctup);\n\tproname = NameStr(procform->proname);\n\n\t/*\n\t * Due to parser hacks to avoid needing to reserve CUBE, we need to force\n\t * qualification of some function names within GROUP BY.\n\t */\n\tif (inGroupBy)\n\t{\n\t\tif (strcmp(proname, \"cube\") == 0 || strcmp(proname, \"rollup\") == 0)\n\t\t\tforce_qualify = true;\n\t}\n\n\t/*\n\t * Determine whether VARIADIC should be printed.  We must do this first\n\t * since it affects the lookup rules in func_get_detail().\n\t *\n\t * Currently, we always print VARIADIC if the function has a merged\n\t * variadic-array argument.  Note that this is always the case for\n\t * functions taking a VARIADIC argument type other than VARIADIC ANY.\n\t *\n\t * In principle, if VARIADIC wasn't originally specified and the array\n\t * actual argument is deconstructable, we could print the array elements\n\t * separately and not print VARIADIC, thus more nearly reproducing the\n\t * original input.  For the moment that seems like too much complication\n\t * for the benefit, and anyway we do not know whether VARIADIC was\n\t * originally specified if it's a non-ANY type.\n\t */\n\tif (use_variadic_p)\n\t{\n\t\t/* Parser should not have set funcvariadic unless fn is variadic */\n\t\tAssert(!has_variadic || OidIsValid(procform->provariadic));\n\t\tuse_variadic = has_variadic;\n\t\t*use_variadic_p = use_variadic;\n\t}\n\telse\n\t{\n\t\tAssert(!has_variadic);\n\t\tuse_variadic = false;\n\t}\n\n\t/*\n\t * The idea here is to schema-qualify only if the parser would fail to\n\t * resolve the correct function given the unqualified func name with the\n\t * specified argtypes and VARIADIC flag.  But if we already decided to\n\t * force qualification, then we can skip the lookup and pretend we didn't\n\t * find it.\n\t */\n\tif (!force_qualify)\n\t\tp_result = func_get_detail(list_make1(makeString(proname)),\n\t\t\t\t\t\t\t\t   NIL, argnames, nargs, argtypes,\n\t\t\t\t\t\t\t\t   !use_variadic, true, false,\n\t\t\t\t\t\t\t\t   &p_funcid, &p_rettype,\n\t\t\t\t\t\t\t\t   &p_retset, &p_nvargs, &p_vatype,\n\t\t\t\t\t\t\t\t   &p_true_typeids, NULL);\n\telse\n\t{\n\t\tp_result = FUNCDETAIL_NOTFOUND;\n\t\tp_funcid = InvalidOid;\n\t}\n\n\tif ((p_result == FUNCDETAIL_NORMAL ||\n\t\t p_result == FUNCDETAIL_AGGREGATE ||\n\t\t p_result == FUNCDETAIL_WINDOWFUNC) &&\n\t\tp_funcid == funcid)\n\t\tnspname = NULL;\n\telse\n\t\tnspname = get_namespace_name_or_temp(procform->pronamespace);\n\n\tresult = quote_qualified_identifier(nspname, proname);\n\n\tReleaseSysCache(proctup);\n\n\treturn result;\n}\n\n/*\n * generate_operator_name\n *\t\tCompute the name to display for an operator specified by OID,\n *\t\tgiven that it is being called with the specified actual arg types.\n *\t\t(Arg types matter because of ambiguous-operator resolution rules.\n *\t\tPass InvalidOid for unused arg of a unary operator.)\n *\n * The result includes all necessary quoting and schema-prefixing,\n * plus the OPERATOR() decoration needed to use a qualified operator name\n * in an expression.\n */\nchar *\ngenerate_operator_name(Oid operid, Oid arg1, Oid arg2)\n{\n\tStringInfoData buf;\n\tHeapTuple\topertup;\n\tForm_pg_operator operform;\n\tchar\t   *oprname;\n\tchar\t   *nspname;\n\n\tinitStringInfo(&buf);\n\n\topertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));\n\tif (!HeapTupleIsValid(opertup))\n\t\telog(ERROR, \"cache lookup failed for operator %u\", operid);\n\toperform = (Form_pg_operator) GETSTRUCT(opertup);\n\toprname = NameStr(operform->oprname);\n\n\t/*\n\t * Unlike generate_operator_name() in postgres/src/backend/utils/adt/ruleutils.c,\n\t * we don't check if the operator is in current namespace or not. This is\n\t * because this check is costly when the operator is not in current namespace.\n\t */\n\tnspname = get_namespace_name_or_temp(operform->oprnamespace);\n\tAssert(nspname != NULL);\n\tappendStringInfo(&buf, \"OPERATOR(%s.\", quote_identifier(nspname));\n\tappendStringInfoString(&buf, oprname);\n\tappendStringInfoChar(&buf, ')');\n\n\tReleaseSysCache(opertup);\n\n\treturn buf.data;\n}\n\n/*\n * get_one_range_partition_bound_string\n *\t\tA C string representation of one range partition bound\n */\nchar *\nget_range_partbound_string(List *bound_datums)\n{\n\tdeparse_context context;\n\tStringInfo\tbuf = makeStringInfo();\n\tListCell   *cell;\n\tchar\t   *sep;\n\n\tmemset(&context, 0, sizeof(deparse_context));\n\tcontext.buf = buf;\n\n\tappendStringInfoChar(buf, '(');\n\tsep = \"\";\n\tforeach(cell, bound_datums)\n\t{\n\t\tPartitionRangeDatum *datum =\n\t\t\t\tlfirst_node(PartitionRangeDatum, cell);\n\n\t\tappendStringInfoString(buf, sep);\n\t\tif (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)\n\t\t\tappendStringInfoString(buf, \"MINVALUE\");\n\t\telse if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)\n\t\t\tappendStringInfoString(buf, \"MAXVALUE\");\n\t\telse\n\t\t{\n\t\t\tConst\t   *val = castNode(Const, datum->value);\n\n\t\t\tget_const_expr(val, &context, -1);\n\t\t}\n\t\tsep = \", \";\n\t}\n\tappendStringInfoChar(buf, ')');\n\n\treturn buf->data;\n}\n\n/*\n * Collect a list of OIDs of all sequences owned by the specified relation,\n * and column if specified.  If deptype is not zero, then only find sequences\n * with the specified dependency type.\n */\nList *\ngetOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)\n{\n\tList\t   *result = NIL;\n\tRelation\tdepRel;\n\tScanKeyData key[3];\n\tSysScanDesc scan;\n\tHeapTuple\ttup;\n\n\tdepRel = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_refclassid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(RelationRelationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_refobjid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(relid));\n\tif (attnum)\n\t\tScanKeyInit(&key[2],\n\t\t\t\t\tAnum_pg_depend_refobjsubid,\n\t\t\t\t\tBTEqualStrategyNumber, F_INT4EQ,\n\t\t\t\t\tInt32GetDatum(attnum));\n\n\tscan = systable_beginscan(depRel, DependReferenceIndexId, true,\n\t\t\t\t\t\t\t  NULL, attnum ? 3 : 2, key);\n\n\twhile (HeapTupleIsValid(tup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);\n\n\t\t/*\n\t\t * We assume any auto or internal dependency of a sequence on a column\n\t\t * must be what we are looking for.  (We need the relkind test because\n\t\t * indexes can also have auto dependencies on columns.)\n\t\t */\n\t\tif (deprec->classid == RelationRelationId &&\n\t\t\tdeprec->objsubid == 0 &&\n\t\t\tdeprec->refobjsubid != 0 &&\n\t\t\t(deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&\n\t\t\tget_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)\n\t\t{\n\t\t\tif (!deptype || deprec->deptype == deptype)\n\t\t\t\tresult = lappend_oid(result, deprec->objid);\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\n\ttable_close(depRel, AccessShareLock);\n\n\treturn result;\n}\n\n/*\n * get_insert_column_names_list Prepares the insert-column-names list. Any indirection\n * decoration needed on the column names can be inferred from the top targetlist.\n */\nstatic List *\nget_insert_column_names_list(List *targetList, StringInfo buf,\n\t\t\t\t\tdeparse_context *context, RangeTblEntry *rte)\n{\n\tchar *sep;\n\tListCell *l;\n\tList *strippedexprs;\n\n\tstrippedexprs = NIL;\n\tsep = \"\";\n\tappendStringInfoChar(buf, '(');\n\tforeach(l, targetList)\n\t{\n\t\tTargetEntry *tle = (TargetEntry *) lfirst(l);\n\n\t\tif (tle->resjunk)\n\t\t\tcontinue;\t\t\t/* ignore junk entries */\n\n\t\tappendStringInfoString(buf, sep);\n\t\tsep = \", \";\n\n\t\t/*\n\t\t * Put out name of target column; look in the catalogs, not at\n\t\t * tle->resname, since resname will fail to track RENAME.\n\t\t */\n\t\tappendStringInfoString(buf,\n\t\t\t\t\t\t\t   quote_identifier(get_attname(rte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttle->resno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse)));\n\n\t\t/*\n\t\t * Print any indirection needed (subfields or subscripts), and strip\n\t\t * off the top-level nodes representing the indirection assignments.\n\t\t * Add the stripped expressions to strippedexprs.  (If it's a\n\t\t * single-VALUES statement, the stripped expressions are the VALUES to\n\t\t * print below.  Otherwise they're just Vars and not really\n\t\t * interesting.)\n\t\t */\n\t\tstrippedexprs = lappend(strippedexprs,\n\t\t\t\t\t\t\t\tprocessIndirection((Node *) tle->expr,\n\t\t\t\t\t\t\t\t\t\t\t\t   context));\n\t}\n\tappendStringInfoString(buf, \") \");\n\n\treturn strippedexprs;\n}\n#endif /* (PG_VERSION_NUM >= PG_VERSION_17) && (PG_VERSION_NUM < PG_VERSION_18) */\n"
  },
  {
    "path": "src/backend/distributed/executor/adaptive_executor.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * adaptive_executor.c\n *\n * The adaptive executor executes a list of tasks (queries on shards) over\n * a connection pool per worker node. The results of the queries, if any,\n * are written to a tuple store.\n *\n * The concepts in the executor are modelled in a set of structs:\n *\n * - DistributedExecution:\n *     Execution of a Task list over a set of WorkerPools.\n * - WorkerPool\n *     Pool of WorkerSessions for the same worker which opportunistically\n *     executes \"unassigned\" tasks from a queue.\n * - WorkerSession:\n *     Connection to a worker that is used to execute \"assigned\" tasks\n *     from a queue and may execute unassigned tasks from the WorkerPool.\n * - ShardCommandExecution:\n *     Execution of a Task across a list of placements.\n * - TaskPlacementExecution:\n *     Execution of a Task on a specific placement.\n *     Used in the WorkerPool and WorkerSession queues.\n *\n * Every connection pool (WorkerPool) and every connection (WorkerSession)\n * have a queue of tasks that are ready to execute (readyTaskQueue) and a\n * queue/set of pending tasks that may become ready later in the execution\n * (pendingTaskQueue). The tasks are wrapped in a ShardCommandExecution,\n * which keeps track of the state of execution and is referenced from a\n * TaskPlacementExecution, which is the data structure that is actually\n * added to the queues and describes the state of the execution of a task\n * on a particular worker node.\n *\n * When the task list is part of a bigger distributed transaction, the\n * shards that are accessed or modified by the task may have already been\n * accessed earlier in the transaction. We need to make sure we use the\n * same connection since it may hold relevant locks or have uncommitted\n * writes. In that case we \"assign\" the task to a connection by adding\n * it to the task queue of specific connection (in\n * AssignTasksToConnectionsOrWorkerPool). Otherwise we consider the task\n * unassigned and add it to the task queue of a worker pool, which means\n * that it can be executed over any connection in the pool.\n *\n * A task may be executed on multiple placements in case of a reference\n * table or a replicated distributed table. Depending on the type of\n * task, it may not be ready to be executed on a worker node immediately.\n * For instance, INSERTs on a reference table are executed serially across\n * placements to avoid deadlocks when concurrent INSERTs take conflicting\n * locks. At the beginning, only the \"first\" placement is ready to execute\n * and therefore added to the readyTaskQueue in the pool or connection.\n * The remaining placements are added to the pendingTaskQueue. Once\n * execution on the first placement is done the second placement moves\n * from pendingTaskQueue to readyTaskQueue. The same approach is used to\n * fail over read-only tasks to another placement.\n *\n * Once all the tasks are added to a queue, the main loop in\n * RunDistributedExecution repeatedly does the following:\n *\n * For each pool:\n * - ManageWorkPool evaluates whether to open additional connections\n *   based on the number unassigned tasks that are ready to execute\n *   and the targetPoolSize of the execution.\n *\n * Poll all connections:\n * - We use a WaitEventSet that contains all (non-failed) connections\n *   and is rebuilt whenever the set of active connections or any of\n *   their wait flags change.\n *\n *   We almost always check for WL_SOCKET_READABLE because a session\n *   can emit notices at any time during execution, but it will only\n *   wake up WaitEventSetWait when there are actual bytes to read.\n *\n *   We check for WL_SOCKET_WRITEABLE just after sending bytes in case\n *   there is not enough space in the TCP buffer. Since a socket is\n *   almost always writable we also use WL_SOCKET_WRITEABLE as a\n *   mechanism to wake up WaitEventSetWait for non-I/O events, e.g.\n *   when a task moves from pending to ready.\n *\n * For each connection that is ready:\n * - ConnectionStateMachine handles connection establishment and failure\n *   as well as command execution via TransactionStateMachine.\n *\n * When a connection is ready to execute a new task, it first checks its\n * own readyTaskQueue and otherwise takes a task from the worker pool's\n * readyTaskQueue (on a first-come-first-serve basis).\n *\n * In cases where the tasks finish quickly (e.g. <1ms), a single\n * connection will often be sufficient to finish all tasks. It is\n * therefore not necessary that all connections are established\n * successfully or open a transaction (which may be blocked by an\n * intermediate pgbouncer in transaction pooling mode). It is therefore\n * essential that we take a task from the queue only after opening a\n * transaction block.\n *\n * When a command on a worker finishes or the connection is lost, we call\n * PlacementExecutionDone, which then updates the state of the task\n * based on whether we need to run it on other placements. When a\n * connection fails or all connections to a worker fail, we also call\n * PlacementExecutionDone for all queued tasks to try the next placement\n * and, if necessary, mark shard placements as inactive. If a task fails\n * to execute on all placements, the execution fails and the distributed\n * transaction rolls back.\n *\n * For multi-row INSERTs, tasks are executed sequentially by\n * SequentialRunDistributedExecution instead of in parallel, which allows\n * a high degree of concurrency without high risk of deadlocks.\n * Conversely, multi-row UPDATE/DELETE/DDL commands take aggressive locks\n * which forbids concurrency, but allows parallelism without high risk\n * of deadlocks. Note that this is unrelated to SEQUENTIAL_CONNECTION,\n * which indicates that we should use at most one connection per node, but\n * can run tasks in parallel across nodes. This is used when there are\n * writes to a reference table that has foreign keys from a distributed\n * table.\n *\n * Execution finishes when all tasks are done, the query errors out, or\n * the user cancels the query.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <math.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"access/htup_details.h\"\n#include \"access/transam.h\"\n#include \"access/xact.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/dbcommands.h\"\n#include \"commands/schemacmds.h\"\n#include \"lib/ilist.h\"\n#include \"portability/instr_time.h\"\n#include \"storage/fd.h\"\n#include \"storage/latch.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/syscache.h\"\n#include \"utils/timestamp.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/backend_data.h\"\n#include \"distributed/cancel_utils.h\"\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/distributed_execution_locks.h\"\n#include \"distributed/executor_util.h\"\n#include \"distributed/intermediate_result_pruning.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_explain.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/param_utils.h\"\n#include \"distributed/placement_access.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/repartition_join_execution.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shared_connection_stats.h\"\n#include \"distributed/stats/stat_counters.h\"\n#include \"distributed/subplan_execution.h\"\n#include \"distributed/transaction_identifier.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/tuple_destination.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n#define SLOW_START_DISABLED 0\n\n\n/*\n * DistributedExecution represents the execution of a distributed query\n * plan.\n */\ntypedef struct DistributedExecution\n{\n\t/* the corresponding distributed plan's modLevel */\n\tRowModifyLevel modLevel;\n\n\t/*\n\t * remoteAndLocalTaskList contains all the tasks required to finish the\n\t * execution. remoteTaskList contains all the tasks required to\n\t * finish the remote execution. localTaskList contains all the\n\t * local tasks required to finish the local execution.\n\t *\n\t * remoteAndLocalTaskList is the union of remoteTaskList and localTaskList.\n\t */\n\tList *remoteAndLocalTaskList;\n\tList *remoteTaskList;\n\tList *localTaskList;\n\n\t/*\n\t * If a task specific destination is not provided for a task, then use\n\t * defaultTupleDest.\n\t */\n\tTupleDestination *defaultTupleDest;\n\n\t/* Parameters for parameterized plans. Can be NULL. */\n\tParamListInfo paramListInfo;\n\n\t/* list of workers involved in the execution */\n\tList *workerList;\n\n\t/* list of all connections used for distributed execution */\n\tList *sessionList;\n\n\t/*\n\t * Flag to indiciate that the set of connections we are interested\n\t * in has changed and waitEventSet needs to be rebuilt.\n\t */\n\tbool rebuildWaitEventSet;\n\n\t/*\n\t * Flag to indiciate that the set of wait events we are interested\n\t * in might have changed and waitEventSet needs to be updated.\n\t *\n\t * Note that we set this flag whenever we assign a value to waitFlags,\n\t * but we don't check that the waitFlags is actually different from the\n\t * previous value. So we might have some false positives for this flag,\n\t * which is OK, because in this case ModifyWaitEvent() is noop.\n\t */\n\tbool waitFlagsChanged;\n\n\t/*\n\t * WaitEventSet used for waiting for I/O events.\n\t *\n\t * This could also be local to RunDistributedExecution(), but in that case\n\t * we had to mark it as \"volatile\" to avoid PG_TRY()/PG_CATCH() issues, and\n\t * cast it to non-volatile when doing WaitEventSetFree(). We thought that\n\t * would make code a bit harder to read than making this non-local, so we\n\t * move it here. See comments for PG_TRY() in postgres/src/include/elog.h\n\t * and \"man 3 siglongjmp\" for more context.\n\t *\n\t * Another reason for keeping these here is to cache a single\n\t * WaitEventSet/WaitEvent within the execution pair until we\n\t * need to rebuild the waitEvents.\n\t */\n\tWaitEventSet *waitEventSet;\n\tWaitEvent *events;\n\tint eventSetSize;\n\n\t/*\n\t * The number of connections we aim to open per worker.\n\t *\n\t * If there are no more tasks to assigned, the actual number may be lower.\n\t * If there are already more connections, the actual number may be higher.\n\t */\n\tint targetPoolSize;\n\n\t/* total number of tasks to execute */\n\tint totalTaskCount;\n\n\t/* number of tasks that still need to be executed */\n\tint unfinishedTaskCount;\n\n\t/*\n\t * Flag to indicate whether throwing errors on cancellation is\n\t * allowed.\n\t */\n\tbool raiseInterrupts;\n\n\t/* transactional properties of the current execution */\n\tTransactionProperties *transactionProperties;\n\n\t/* indicates whether distributed execution has failed */\n\tbool failed;\n\n\t/*\n\t * For SELECT commands or INSERT/UPDATE/DELETE commands with RETURNING,\n\t * the total number of rows received from the workers. For\n\t * INSERT/UPDATE/DELETE commands without RETURNING, the total number of\n\t * tuples modified.\n\t *\n\t * Note that for replicated tables (e.g., reference tables), we only consider\n\t * a single replica's rows that are processed.\n\t */\n\tuint64 rowsProcessed;\n\n\t/*\n\t * The following fields are used while receiving results from remote nodes.\n\t * We store this information here to avoid re-allocating it every time.\n\t *\n\t * columnArray field is reset/calculated per row, so might be useless for\n\t * other contexts. The benefit of keeping it here is to avoid allocating\n\t * the array over and over again.\n\t */\n\tuint32 allocatedColumnCount;\n\tvoid **columnArray;\n\tStringInfoData *stringInfoDataArray;\n\n\t/*\n\t * jobIdList contains all jobs in the job tree, this is used to\n\t * do cleanup for repartition queries.\n\t */\n\tList *jobIdList;\n\n\t/*\n\t * Indicates whether we can execute tasks locally during distributed\n\t * execution. In other words, this flag must be set to false when\n\t * executing a command that we surely know that local execution would\n\t * fail, such as CREATE INDEX CONCURRENTLY.\n\t */\n\tbool localExecutionSupported;\n} DistributedExecution;\n\n\n/*\n * WorkerPoolFailureState indicates the current state of the\n * pool.\n */\ntypedef enum WorkerPoolFailureState\n{\n\t/* safe to continue execution*/\n\tWORKER_POOL_NOT_FAILED,\n\n\t/* if a pool fails, the execution fails */\n\tWORKER_POOL_FAILED,\n\n\t/*\n\t * The remote execution over the pool failed, but we failed over\n\t * to the local execution and still finish the execution.\n\t */\n\tWORKER_POOL_FAILED_OVER_TO_LOCAL\n} WorkerPoolFailureState;\n\n/*\n * WorkerPool represents a pool of sessions on the same worker.\n *\n * A WorkerPool has two queues containing the TaskPlacementExecutions that need\n * to be executed on the worker.\n *\n * TaskPlacementExecutions that are ready to execute are in readyTaskQueue.\n * TaskPlacementExecutions that may need to be executed once execution on\n * another worker finishes or fails are in pendingTaskQueue.\n *\n * In TransactionStateMachine, the sessions opportunistically take\n * TaskPlacementExecutions from the readyQueue when they are ready and have no\n * assigned tasks.\n *\n * We track connection timeouts per WorkerPool. When the first connection is\n * established we set the poolStartTime and if no connection can be established\n * before NodeConnectionTime, the WorkerPool fails. There is some specialised\n * logic in case citus.force_max_query_parallelization is enabled because we\n * may fail to establish a connection per placement after already establishing\n * some connections earlier in the execution.\n *\n * A WorkerPool fails if all connection attempts failed or all connections\n * are lost. In that case, all TaskPlacementExecutions in the queues are\n * marked as failed in PlacementExecutionDone, which typically causes the\n * task and therefore the distributed execution to fail. In case of a\n * replicated table or a SELECT on a reference table, the remaining placements\n * will be tried by moving them from a pendingTaskQueue to a readyTaskQueue.\n */\ntypedef struct WorkerPool\n{\n\t/* distributed execution in which the worker participates */\n\tDistributedExecution *distributedExecution;\n\n\t/* worker node on which we have a pool of sessions */\n\tchar *nodeName;\n\tint nodePort;\n\n\t/* all sessions on the worker that are part of the current execution */\n\tList *sessionList;\n\n\t/* number of connections that were established */\n\tint activeConnectionCount;\n\n\t/*\n\t * Keep track of how many connections are ready for execution, in\n\t * order to (efficiently) know whether more connections to the worker\n\t * are needed.\n\t */\n\tint idleConnectionCount;\n\n\t/* number of connections that did not send a command */\n\tint unusedConnectionCount;\n\n\t/* number of failed connections */\n\tint failedConnectionCount;\n\n\t/*\n\t * Placement executions destined for worker node, but not assigned to any\n\t * connection and not yet ready to start (depends on other placement\n\t * executions).\n\t */\n\tdlist_head pendingTaskQueue;\n\n\t/*\n\t * Placement executions destined for worker node, but not assigned to any\n\t * connection and ready to start.\n\t */\n\tdlist_head readyTaskQueue;\n\tint readyTaskCount;\n\n\t/*\n\t * We keep this for enforcing the connection timeouts. In our definition, a pool\n\t * starts when the first connection establishment starts.\n\t */\n\tinstr_time poolStartTime;\n\n\t/* indicates whether to check for the connection timeout */\n\tbool checkForPoolTimeout;\n\n\t/* last time we opened a connection */\n\tinstr_time lastConnectionOpenTime;\n\n\t/* maximum number of connections we are allowed to open at once */\n\tuint32 maxNewConnectionsPerCycle;\n\n\t/*\n\t * Set to true if the pool is to local node. We use this value to\n\t * avoid re-calculating often.\n\t */\n\tbool poolToLocalNode;\n\n\t/*\n\t * This is only set in WorkerPoolFailed() function. Once a pool fails, we do not\n\t * use it anymore.\n\t */\n\tWorkerPoolFailureState failureState;\n\n\t/* execution statistics per pool, in microseconds */\n\tuint64 totalTaskExecutionTime;\n\tint totalExecutedTasks;\n} WorkerPool;\n\nstruct TaskPlacementExecution;\n\n/*\n * WorkerSession represents a session on a worker that can execute tasks\n * (sequentially) and is part of a WorkerPool.\n *\n * Each WorkerSession has two queues containing TaskPlacementExecutions that\n * need to be executed within this particular session because the session\n * accessed the same or co-located placements earlier in the transaction.\n *\n * TaskPlacementExecutions that are ready to execute are in readyTaskQueue.\n * TaskPlacementExecutions that may need to be executed once execution on\n * another worker finishes or fails are in pendingTaskQueue.\n */\ntypedef struct WorkerSession\n{\n\t/* only useful for debugging */\n\tuint64 sessionId;\n\n\t/* worker pool of which this session is part */\n\tWorkerPool *workerPool;\n\n\t/* connection over which the session is established */\n\tMultiConnection *connection;\n\n\t/* tasks that need to be executed on this connection, but are not ready to start  */\n\tdlist_head pendingTaskQueue;\n\n\t/* tasks that need to be executed on this connection and are ready to start */\n\tdlist_head readyTaskQueue;\n\n\t/* task the worker should work on or NULL */\n\tstruct TaskPlacementExecution *currentTask;\n\n\t/*\n\t * The number of commands sent to the worker over the session. Excludes\n\t * distributed transaction related commands such as BEGIN/COMMIT etc.\n\t */\n\tuint64 commandsSent;\n\n\t/* index in the wait event set */\n\tint waitEventSetIndex;\n\n\t/* events reported by the latest call to WaitEventSetWait */\n\tint latestUnconsumedWaitEvents;\n\n\t/* for some restricted scenarios, we allow a single connection retry */\n\tbool connectionRetried;\n\n\t/* keep track of if the session has an active connection */\n\tbool sessionHasActiveConnection;\n} WorkerSession;\n\n\n/* GUC, determining whether Citus opens 1 connection per task */\nbool ForceMaxQueryParallelization = false;\nint MaxAdaptiveExecutorPoolSize = 16;\nbool EnableBinaryProtocol = true;\n\n/* GUC, number of ms to wait between opening connections to the same worker */\nint ExecutorSlowStartInterval = 10;\nbool EnableCostBasedConnectionEstablishment = true;\nbool PreventIncompleteConnectionEstablishment = true;\n\n\n/*\n * TaskExecutionState indicates whether or not a command on a shard\n * has finished, or whether it has failed.\n */\ntypedef enum TaskExecutionState\n{\n\tTASK_EXECUTION_NOT_FINISHED,\n\tTASK_EXECUTION_FINISHED,\n\tTASK_EXECUTION_FAILED,\n\tTASK_EXECUTION_FAILOVER_TO_LOCAL_EXECUTION\n} TaskExecutionState;\n\n/*\n * PlacementExecutionOrder indicates whether a command should be executed\n * on any replica, on all replicas sequentially (in order), or on all\n * replicas in parallel. In other words, EXECUTION_ORDER_ANY is used for\n * SELECTs, EXECUTION_ORDER_SEQUENTIAL/EXECUTION_ORDER_PARALLEL is used for\n * DML/DDL.\n */\ntypedef enum PlacementExecutionOrder\n{\n\tEXECUTION_ORDER_ANY,\n\tEXECUTION_ORDER_SEQUENTIAL,\n\tEXECUTION_ORDER_PARALLEL,\n} PlacementExecutionOrder;\n\n\n/*\n * ShardCommandExecution represents an execution of a command on a shard\n * that may (need to) run across multiple placements.\n */\ntypedef struct ShardCommandExecution\n{\n\t/* description of the task */\n\tTask *task;\n\n\t/* cached AttInMetadata for task */\n\tAttInMetadata **attributeInputMetadata;\n\n\t/* indicates whether the attributeInputMetadata has binary or text\n\t * encoding/decoding functions */\n\tbool binaryResults;\n\n\t/* order in which the command should be replicated on replicas */\n\tPlacementExecutionOrder executionOrder;\n\n\t/* executions of the command on the placements of the shard */\n\tstruct TaskPlacementExecution **placementExecutions;\n\tint placementExecutionCount;\n\n\t/*\n\t * RETURNING results from other shard placements can be ignored\n\t * after we got results from the first placements.\n\t */\n\tbool gotResults;\n\n\tTaskExecutionState executionState;\n\n\t/*\n\t * Indicates whether given shard command can be executed locally on\n\t * placements. Normally determined by DistributedExecution's same field.\n\t */\n\tbool localExecutionSupported;\n} ShardCommandExecution;\n\n/*\n * TaskPlacementExecutionState indicates whether a command is running\n * on a shard placement, or finished or failed.\n */\ntypedef enum TaskPlacementExecutionState\n{\n\tPLACEMENT_EXECUTION_NOT_READY,\n\tPLACEMENT_EXECUTION_READY,\n\tPLACEMENT_EXECUTION_RUNNING,\n\tPLACEMENT_EXECUTION_FINISHED,\n\tPLACEMENT_EXECUTION_FAILOVER_TO_LOCAL_EXECUTION,\n\tPLACEMENT_EXECUTION_FAILED\n} TaskPlacementExecutionState;\n\n/*\n * TaskPlacementExecution represents the execution of a command\n * on a shard placement.\n */\ntypedef struct TaskPlacementExecution\n{\n\t/* shard command execution of which this placement execution is part */\n\tShardCommandExecution *shardCommandExecution;\n\n\t/* shard placement on which this command runs */\n\tShardPlacement *shardPlacement;\n\n\t/* state of the execution of the command on the placement */\n\tTaskPlacementExecutionState executionState;\n\n\t/*\n\t * Task query can contain multiple queries. queryIndex tracks results of\n\t * which query we are waiting for.\n\t */\n\tuint32 queryIndex;\n\n\t/* worker pool on which the placement needs to be executed */\n\tWorkerPool *workerPool;\n\n\t/* the session the placement execution is assigned to or NULL */\n\tWorkerSession *assignedSession;\n\n\t/* membership in assigned task queue of a particular session */\n\tdlist_node sessionPendingQueueNode;\n\n\t/* membership in ready-to-start assigned task queue of a particular session */\n\tdlist_node sessionReadyQueueNode;\n\n\t/* membership in assigned task queue of worker */\n\tdlist_node workerPendingQueueNode;\n\n\t/* membership in ready-to-start task queue of worker */\n\tdlist_node workerReadyQueueNode;\n\n\t/* index in array of placement executions in a ShardCommandExecution */\n\tint placementExecutionIndex;\n\n\t/* execution time statistics for this placement execution */\n\tinstr_time startTime;\n\tinstr_time endTime;\n} TaskPlacementExecution;\n\n\n/* local functions */\nstatic DistributedExecution * CreateDistributedExecution(RowModifyLevel modLevel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t List *taskList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t ParamListInfo paramListInfo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t int targetPoolSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t TupleDestination *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t defaultTupleDest,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t TransactionProperties *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t xactProperties,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t List *jobIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t bool localExecutionSupported);\nstatic TransactionProperties DecideTaskListTransactionProperties(RowModifyLevel\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t modLevel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t List *taskList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t excludeFromTransaction);\nstatic void StartDistributedExecution(DistributedExecution *execution);\nstatic void RunLocalExecution(CitusScanState *scanState, DistributedExecution *execution);\nstatic void RunDistributedExecution(DistributedExecution *execution);\nstatic void SequentialRunDistributedExecution(DistributedExecution *execution);\nstatic void FinishDistributedExecution(DistributedExecution *execution);\nstatic void CleanUpSessions(DistributedExecution *execution);\n\nstatic bool DistributedExecutionModifiesDatabase(DistributedExecution *execution);\nstatic void AssignTasksToConnectionsOrWorkerPool(DistributedExecution *execution);\nstatic void UnclaimAllSessionConnections(List *sessionList);\nstatic PlacementExecutionOrder ExecutionOrderForTask(RowModifyLevel modLevel, Task *task);\nstatic WorkerPool * FindOrCreateWorkerPool(DistributedExecution *execution,\n\t\t\t\t\t\t\t\t\t\t   char *nodeName, int nodePort);\nstatic WorkerSession * FindOrCreateWorkerSession(WorkerPool *workerPool,\n\t\t\t\t\t\t\t\t\t\t\t\t MultiConnection *connection);\nstatic void ManageWorkerPool(WorkerPool *workerPool);\nstatic bool ShouldWaitForSlowStart(WorkerPool *workerPool);\nstatic int CalculateNewConnectionCount(WorkerPool *workerPool);\nstatic bool UsingExistingSessionsCheaperThanEstablishingNewConnections(int\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   readyTaskCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   WorkerPool *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   workerPool);\nstatic double AvgTaskExecutionTimeApproximation(WorkerPool *workerPool);\nstatic double AvgConnectionEstablishmentTime(WorkerPool *workerPool);\nstatic void OpenNewConnections(WorkerPool *workerPool, int newConnectionCount,\n\t\t\t\t\t\t\t   TransactionProperties *transactionProperties);\nstatic void CheckConnectionTimeout(WorkerPool *workerPool);\nstatic void MarkEstablishingSessionsTimedOut(WorkerPool *workerPool);\nstatic int UsableConnectionCount(WorkerPool *workerPool);\nstatic long NextEventTimeout(DistributedExecution *execution);\nstatic WaitEventSet * BuildWaitEventSet(List *sessionList);\nstatic void FreeExecutionWaitEvents(DistributedExecution *execution);\nstatic void AddSessionToWaitEventSet(WorkerSession *session,\n\t\t\t\t\t\t\t\t\t WaitEventSet *waitEventSet);\nstatic void RebuildWaitEventSetFlags(WaitEventSet *waitEventSet, List *sessionList);\nstatic TaskPlacementExecution * PopPlacementExecution(WorkerSession *session);\nstatic TaskPlacementExecution * PopAssignedPlacementExecution(WorkerSession *session);\nstatic TaskPlacementExecution * PopUnassignedPlacementExecution(WorkerPool *workerPool);\nstatic bool StartPlacementExecutionOnSession(TaskPlacementExecution *placementExecution,\n\t\t\t\t\t\t\t\t\t\t\t WorkerSession *session);\nstatic bool SendNextQuery(TaskPlacementExecution *placementExecution,\n\t\t\t\t\t\t  WorkerSession *session);\nstatic void ConnectionStateMachine(WorkerSession *session);\nstatic bool HasUnfinishedTaskForSession(WorkerSession *session);\nstatic void HandleMultiConnectionSuccess(WorkerSession *session, bool newConnection);\nstatic bool HasAnyConnectionFailure(WorkerPool *workerPool);\nstatic void Activate2PCIfModifyingTransactionExpandsToNewNode(WorkerSession *session);\nstatic bool TransactionModifiedDistributedTable(DistributedExecution *execution);\nstatic void TransactionStateMachine(WorkerSession *session);\nstatic void UpdateConnectionWaitFlags(WorkerSession *session, int waitFlags);\nstatic bool CheckConnectionReady(WorkerSession *session);\nstatic bool ReceiveResults(WorkerSession *session, bool storeRows);\nstatic void WorkerSessionFailed(WorkerSession *session);\nstatic void WorkerPoolFailed(WorkerPool *workerPool);\nstatic void PlacementExecutionDone(TaskPlacementExecution *placementExecution,\n\t\t\t\t\t\t\t\t   bool succeeded);\nstatic void ScheduleNextPlacementExecution(TaskPlacementExecution *placementExecution,\n\t\t\t\t\t\t\t\t\t\t   bool succeeded);\nstatic bool CanFailoverPlacementExecutionToLocalExecution(TaskPlacementExecution *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  placementExecution);\nstatic void PlacementExecutionReady(TaskPlacementExecution *placementExecution);\nstatic TaskExecutionState TaskExecutionStateMachine(ShardCommandExecution *\n\t\t\t\t\t\t\t\t\t\t\t\t\tshardCommandExecution);\nstatic int GetEventSetSize(List *sessionList);\nstatic bool ProcessSessionsWithFailedWaitEventSetOperations(DistributedExecution *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\texecution);\nstatic bool HasIncompleteConnectionEstablishment(DistributedExecution *execution);\nstatic void RebuildWaitEventSet(DistributedExecution *execution);\nstatic void RebuildWaitEventSetForSessions(DistributedExecution *execution);\nstatic void AddLatchWaitEventToExecution(DistributedExecution *execution);\nstatic void ProcessWaitEvents(DistributedExecution *execution, WaitEvent *events, int\n\t\t\t\t\t\t\t  eventCount, bool *cancellationReceived);\nstatic void RemoteSocketClosedForAnySession(DistributedExecution *execution);\nstatic void ProcessWaitEventsForSocketClosed(WaitEvent *events, int eventCount);\nstatic long MillisecondsBetweenTimestamps(instr_time startTime, instr_time endTime);\nstatic uint64 MicrosecondsBetweenTimestamps(instr_time startTime, instr_time endTime);\nstatic int WorkerPoolCompare(const void *lhsKey, const void *rhsKey);\nstatic void SetAttributeInputMetadata(DistributedExecution *execution,\n\t\t\t\t\t\t\t\t\t  ShardCommandExecution *shardCommandExecution);\nstatic ExecutionParams * CreateDefaultExecutionParams(RowModifyLevel modLevel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *taskList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  TupleDestination *tupleDest,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  bool expectResults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ParamListInfo paramListInfo);\n\n\n/*\n * AdaptiveExecutorPreExecutorRun gets called right before postgres starts its executor\n * run. Given that the result of our subplans would be evaluated before the first call to\n * the exec function of our custom scan we make sure our subplans have executed before.\n */\nvoid\nAdaptiveExecutorPreExecutorRun(CitusScanState *scanState)\n{\n\tif (scanState->finishedPreScan)\n\t{\n\t\t/*\n\t\t * Cursors (and hence RETURN QUERY syntax in pl/pgsql functions)\n\t\t * may trigger AdaptiveExecutorPreExecutorRun() on every fetch\n\t\t * operation. Though, we should only execute PreScan once.\n\t\t */\n\t\treturn;\n\t}\n\n\tDistributedPlan *distributedPlan = scanState->distributedPlan;\n\n\t/*\n\t * PostgreSQL takes locks on all partitions in the executor. It's not entirely\n\t * clear why this is necessary (instead of locking the parent during DDL), but\n\t * we do the same for consistency.\n\t */\n\tLockPartitionsForDistributedPlan(distributedPlan);\n\n\tExecuteSubPlans(distributedPlan, RequestedForExplainAnalyze(scanState));\n\n\tscanState->finishedPreScan = true;\n}\n\n\n/*\n * AdaptiveExecutor is called via CitusExecScan on the\n * first call of CitusExecScan. The function fills the tupleStore\n * of the input scanScate.\n */\nTupleTableSlot *\nAdaptiveExecutor(CitusScanState *scanState)\n{\n\tTupleTableSlot *resultSlot = NULL;\n\n\tDistributedPlan *distributedPlan = scanState->distributedPlan;\n\tEState *executorState = ScanStateGetExecutorState(scanState);\n\tParamListInfo paramListInfo = executorState->es_param_list_info;\n\tbool randomAccess = true;\n\tbool interTransactions = false;\n\tint targetPoolSize = MaxAdaptiveExecutorPoolSize;\n\tList *jobIdList = NIL;\n\n\tJob *job = distributedPlan->workerJob;\n\tList *taskList = job->taskList;\n\n\t/* we should only call this once before the scan finished */\n\tAssert(!scanState->finishedRemoteScan);\n\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"AdaptiveExecutor\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\n\t/* Reset Task fields that are only valid for a single execution */\n\tResetExplainAnalyzeData(taskList);\n\n\tscanState->tuplestorestate =\n\t\ttuplestore_begin_heap(randomAccess, interTransactions, work_mem);\n\n\tTupleDesc tupleDescriptor = ScanStateGetTupleDescriptor(scanState);\n\tTupleDestination *defaultTupleDest =\n\t\tCreateTupleStoreTupleDest(scanState->tuplestorestate, tupleDescriptor);\n\n\tbool localExecutionSupported = true;\n\n\tif (RequestedForExplainAnalyze(scanState))\n\t{\n\t\t/*\n\t\t * We use multiple queries per task in EXPLAIN ANALYZE which need to\n\t\t * be part of the same transaction.\n\t\t */\n\t\tUseCoordinatedTransaction();\n\t\ttaskList = ExplainAnalyzeTaskList(taskList, defaultTupleDest, tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t  paramListInfo);\n\n\t\t/*\n\t\t * Multiple queries per task is not supported with local execution. See the Assert in\n\t\t * TupleDestDestReceiverReceive.\n\t\t */\n\t\tlocalExecutionSupported = false;\n\t}\n\n\tbool hasDependentJobs = job->dependentJobList != NIL;\n\tif (hasDependentJobs)\n\t{\n\t\t/* jobs use intermediate results, which require a distributed transaction */\n\t\tUseCoordinatedTransaction();\n\n\t\tjobIdList = ExecuteDependentTasks(taskList, job);\n\t}\n\n\tif (MultiShardConnectionType == SEQUENTIAL_CONNECTION)\n\t{\n\t\t/* defer decision after ExecuteSubPlans() */\n\t\ttargetPoolSize = 1;\n\t}\n\n\tbool excludeFromXact = false;\n\n\tTransactionProperties xactProperties = DecideTaskListTransactionProperties(\n\t\tdistributedPlan->modLevel, taskList, excludeFromXact);\n\n\t/*\n\t * In some rare cases, we have prepared statements that pass a parameter\n\t * and never used in the query, mark such parameters' type as Invalid(0),\n\t * which will be used later in ExtractParametersFromParamList() to map them\n\t * to a generic datatype. Skip for dynamic parameters.\n\t */\n\tif (paramListInfo && !paramListInfo->paramFetch)\n\t{\n\t\tparamListInfo = copyParamList(paramListInfo);\n\t\tMarkUnreferencedExternParams((Node *) job->jobQuery, paramListInfo);\n\t}\n\n\tDistributedExecution *execution = CreateDistributedExecution(\n\t\tdistributedPlan->modLevel,\n\t\ttaskList,\n\t\tparamListInfo,\n\t\ttargetPoolSize,\n\t\tdefaultTupleDest,\n\t\t&xactProperties,\n\t\tjobIdList,\n\t\tlocalExecutionSupported);\n\n\t/*\n\t * Make sure that we acquire the appropriate locks even if the local tasks\n\t * are going to be executed with local execution.\n\t */\n\tStartDistributedExecution(execution);\n\n\tif (ShouldRunTasksSequentially(execution->remoteTaskList))\n\t{\n\t\tSequentialRunDistributedExecution(execution);\n\t}\n\telse\n\t{\n\t\tRunDistributedExecution(execution);\n\t}\n\n\t/* execute tasks local to the node (if any) */\n\tif (list_length(execution->localTaskList) > 0)\n\t{\n\t\t/* now execute the local tasks */\n\t\tRunLocalExecution(scanState, execution);\n\t}\n\n\tCmdType commandType = job->jobQuery->commandType;\n\tif (commandType != CMD_SELECT)\n\t{\n\t\texecutorState->es_processed = execution->rowsProcessed;\n\t}\n\n\tFinishDistributedExecution(execution);\n\n\tif (SortReturning && distributedPlan->expectResults && commandType != CMD_SELECT)\n\t{\n\t\tSortTupleStore(scanState);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\n\treturn resultSlot;\n}\n\n\n/*\n * RunLocalExecution runs the localTaskList in the execution, fills the tuplestore\n * and sets the es_processed if necessary.\n *\n * It also sorts the tuplestore if there are no remote tasks remaining.\n */\nstatic void\nRunLocalExecution(CitusScanState *scanState, DistributedExecution *execution)\n{\n\tEState *estate = ScanStateGetExecutorState(scanState);\n\tbool isUtilityCommand = false;\n\tuint64 rowsProcessed = ExecuteLocalTaskListExtended(execution->localTaskList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\testate->es_param_list_info,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tscanState->distributedPlan,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\texecution->defaultTupleDest,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tisUtilityCommand);\n\n\texecution->rowsProcessed += rowsProcessed;\n}\n\n\n/*\n * ExecuteUtilityTaskList is a wrapper around executing task\n * list for utility commands.\n */\nuint64\nExecuteUtilityTaskList(List *utilityTaskList, bool localExecutionSupported)\n{\n\tRowModifyLevel modLevel = ROW_MODIFY_NONE;\n\tExecutionParams *executionParams = CreateBasicExecutionParams(\n\t\tmodLevel, utilityTaskList, MaxAdaptiveExecutorPoolSize, localExecutionSupported\n\t\t);\n\texecutionParams->xactProperties =\n\t\tDecideTaskListTransactionProperties(modLevel, utilityTaskList, false);\n\texecutionParams->isUtilityCommand = true;\n\n\treturn ExecuteTaskListExtended(executionParams);\n}\n\n\n/*\n * ExecuteUtilityTaskListExtended is a wrapper around executing task\n * list for utility commands.\n */\nuint64\nExecuteUtilityTaskListExtended(List *utilityTaskList, int poolSize,\n\t\t\t\t\t\t\t   bool localExecutionSupported)\n{\n\tRowModifyLevel modLevel = ROW_MODIFY_NONE;\n\tExecutionParams *executionParams = CreateBasicExecutionParams(\n\t\tmodLevel, utilityTaskList, poolSize, localExecutionSupported\n\t\t);\n\n\tbool excludeFromXact = false;\n\texecutionParams->xactProperties =\n\t\tDecideTaskListTransactionProperties(modLevel, utilityTaskList,\n\t\t\t\t\t\t\t\t\t\t\texcludeFromXact);\n\texecutionParams->isUtilityCommand = true;\n\n\treturn ExecuteTaskListExtended(executionParams);\n}\n\n\n/*\n * ExecuteTaskList is a proxy to ExecuteTaskListExtended\n * with defaults for some of the arguments.\n */\nuint64\nExecuteTaskList(RowModifyLevel modLevel, List *taskList)\n{\n\tbool localExecutionSupported = true;\n\tExecutionParams *executionParams = CreateBasicExecutionParams(\n\t\tmodLevel, taskList, MaxAdaptiveExecutorPoolSize, localExecutionSupported\n\t\t);\n\n\tbool excludeFromXact = false;\n\texecutionParams->xactProperties = DecideTaskListTransactionProperties(\n\t\tmodLevel, taskList, excludeFromXact);\n\n\treturn ExecuteTaskListExtended(executionParams);\n}\n\n\n/*\n * ExecuteTaskListOutsideTransaction is a proxy to ExecuteTaskListExtended\n * with defaults for some of the arguments.\n */\nuint64\nExecuteTaskListOutsideTransaction(RowModifyLevel modLevel, List *taskList,\n\t\t\t\t\t\t\t\t  int targetPoolSize, List *jobIdList)\n{\n\t/*\n\t * As we are going to run the tasks outside transaction, we shouldn't use local execution.\n\t * However, there is some problem when using local execution related to\n\t * repartition joins, when we solve that problem, we can execute the tasks\n\t * coming to this path with local execution. See PR:3711\n\t */\n\tbool localExecutionSupported = false;\n\tExecutionParams *executionParams = CreateBasicExecutionParams(\n\t\tmodLevel, taskList, targetPoolSize, localExecutionSupported\n\t\t);\n\n\texecutionParams->xactProperties = DecideTaskListTransactionProperties(\n\t\tmodLevel, taskList, true);\n\treturn ExecuteTaskListExtended(executionParams);\n}\n\n\n/*\n * CreateDefaultExecutionParams returns execution params based on given (possibly null)\n * bind params (presumably from executor state) with defaults for some of the arguments.\n */\nstatic ExecutionParams *\nCreateDefaultExecutionParams(RowModifyLevel modLevel, List *taskList,\n\t\t\t\t\t\t\t TupleDestination *tupleDest,\n\t\t\t\t\t\t\t bool expectResults,\n\t\t\t\t\t\t\t ParamListInfo paramListInfo)\n{\n\tint targetPoolSize = MaxAdaptiveExecutorPoolSize;\n\tbool localExecutionSupported = true;\n\tExecutionParams *executionParams = CreateBasicExecutionParams(\n\t\tmodLevel, taskList, targetPoolSize, localExecutionSupported\n\t\t);\n\n\texecutionParams->xactProperties = DecideTaskListTransactionProperties(\n\t\tmodLevel, taskList, false);\n\texecutionParams->expectResults = expectResults;\n\texecutionParams->tupleDestination = tupleDest;\n\texecutionParams->paramListInfo = paramListInfo;\n\n\treturn executionParams;\n}\n\n\n/*\n * ExecuteTaskListIntoTupleDestWithParam is a proxy to ExecuteTaskListExtended() which uses\n * bind params from executor state, and with defaults for some of the arguments.\n */\nuint64\nExecuteTaskListIntoTupleDestWithParam(RowModifyLevel modLevel, List *taskList,\n\t\t\t\t\t\t\t\t\t  TupleDestination *tupleDest,\n\t\t\t\t\t\t\t\t\t  bool expectResults,\n\t\t\t\t\t\t\t\t\t  ParamListInfo paramListInfo)\n{\n\tExecutionParams *executionParams = CreateDefaultExecutionParams(modLevel, taskList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttupleDest,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\texpectResults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tparamListInfo);\n\treturn ExecuteTaskListExtended(executionParams);\n}\n\n\n/*\n * ExecuteTaskListIntoTupleDest is a proxy to ExecuteTaskListExtended() with defaults\n * for some of the arguments.\n */\nuint64\nExecuteTaskListIntoTupleDest(RowModifyLevel modLevel, List *taskList,\n\t\t\t\t\t\t\t TupleDestination *tupleDest,\n\t\t\t\t\t\t\t bool expectResults)\n{\n\tParamListInfo paramListInfo = NULL;\n\tExecutionParams *executionParams = CreateDefaultExecutionParams(modLevel, taskList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttupleDest,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\texpectResults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tparamListInfo);\n\treturn ExecuteTaskListExtended(executionParams);\n}\n\n\n/*\n * ExecuteTaskListExtended sets up the execution for given task list and\n * runs it.\n */\nuint64\nExecuteTaskListExtended(ExecutionParams *executionParams)\n{\n\t/* if there are no tasks to execute, we can return early */\n\tif (list_length(executionParams->taskList) == 0)\n\t{\n\t\treturn 0;\n\t}\n\n\tuint64 locallyProcessedRows = 0;\n\n\tTupleDestination *defaultTupleDest = executionParams->tupleDestination;\n\n\tif (MultiShardConnectionType == SEQUENTIAL_CONNECTION)\n\t{\n\t\texecutionParams->targetPoolSize = 1;\n\t}\n\n\tDistributedExecution *execution =\n\t\tCreateDistributedExecution(\n\t\t\texecutionParams->modLevel, executionParams->taskList,\n\t\t\texecutionParams->paramListInfo, executionParams->targetPoolSize,\n\t\t\tdefaultTupleDest, &executionParams->xactProperties,\n\t\t\texecutionParams->jobIdList, executionParams->localExecutionSupported);\n\n\t/*\n\t * If current transaction accessed local placements and task list includes\n\t * tasks that should be executed locally (accessing any of the local placements),\n\t * then we should error out as it would cause inconsistencies across the\n\t * remote connection and local execution.\n\t */\n\tEnsureCompatibleLocalExecutionState(execution->remoteTaskList);\n\n\t/* run the remote execution */\n\tStartDistributedExecution(execution);\n\tRunDistributedExecution(execution);\n\tFinishDistributedExecution(execution);\n\n\t/* now, switch back to the local execution */\n\tif (executionParams->isUtilityCommand)\n\t{\n\t\tlocallyProcessedRows += ExecuteLocalUtilityTaskList(execution->localTaskList);\n\t}\n\telse\n\t{\n\t\tlocallyProcessedRows += ExecuteLocalTaskList(execution->localTaskList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t defaultTupleDest);\n\t}\n\n\treturn execution->rowsProcessed + locallyProcessedRows;\n}\n\n\n/*\n * CreateBasicExecutionParams creates basic execution parameters with some common\n * fields.\n */\nExecutionParams *\nCreateBasicExecutionParams(RowModifyLevel modLevel,\n\t\t\t\t\t\t   List *taskList,\n\t\t\t\t\t\t   int targetPoolSize,\n\t\t\t\t\t\t   bool localExecutionSupported)\n{\n\tExecutionParams *executionParams = palloc0(sizeof(ExecutionParams));\n\texecutionParams->modLevel = modLevel;\n\texecutionParams->taskList = taskList;\n\texecutionParams->targetPoolSize = targetPoolSize;\n\texecutionParams->localExecutionSupported = localExecutionSupported;\n\n\texecutionParams->tupleDestination = CreateTupleDestNone();\n\texecutionParams->expectResults = false;\n\texecutionParams->isUtilityCommand = false;\n\texecutionParams->jobIdList = NIL;\n\texecutionParams->paramListInfo = NULL;\n\n\treturn executionParams;\n}\n\n\n/*\n * CreateDistributedExecution creates a distributed execution data structure for\n * a distributed plan.\n */\nstatic DistributedExecution *\nCreateDistributedExecution(RowModifyLevel modLevel, List *taskList,\n\t\t\t\t\t\t   ParamListInfo paramListInfo,\n\t\t\t\t\t\t   int targetPoolSize, TupleDestination *defaultTupleDest,\n\t\t\t\t\t\t   TransactionProperties *xactProperties,\n\t\t\t\t\t\t   List *jobIdList, bool localExecutionSupported)\n{\n\tDistributedExecution *execution =\n\t\t(DistributedExecution *) palloc0(sizeof(DistributedExecution));\n\n\texecution->modLevel = modLevel;\n\texecution->remoteAndLocalTaskList = taskList;\n\texecution->transactionProperties = xactProperties;\n\n\t/* we are going to calculate this values below */\n\texecution->localTaskList = NIL;\n\texecution->remoteTaskList = NIL;\n\n\texecution->paramListInfo = paramListInfo;\n\texecution->workerList = NIL;\n\texecution->sessionList = NIL;\n\texecution->targetPoolSize = targetPoolSize;\n\texecution->defaultTupleDest = defaultTupleDest;\n\n\texecution->rowsProcessed = 0;\n\n\texecution->raiseInterrupts = true;\n\n\texecution->rebuildWaitEventSet = false;\n\texecution->waitFlagsChanged = false;\n\n\texecution->jobIdList = jobIdList;\n\n\texecution->localExecutionSupported = localExecutionSupported;\n\n\t/*\n\t * Since task can have multiple queries, we are not sure how many columns we should\n\t * allocate for. We start with 16, and reallocate when we need more.\n\t */\n\texecution->allocatedColumnCount = 16;\n\texecution->columnArray = palloc0(execution->allocatedColumnCount * sizeof(void *));\n\tif (EnableBinaryProtocol)\n\t{\n\t\t/*\n\t\t * Initialize enough StringInfos for each column. These StringInfos\n\t\t * (and thus the backing buffers) will be reused for each row.\n\t\t * We will reference these StringInfos in the columnArray if the value\n\t\t * is not NULL.\n\t\t *\n\t\t * NOTE: StringInfos are always grown in the memory context in which\n\t\t * they were initially created. So appending in any memory context will\n\t\t * result in bufferes that are still valid after removing that memory\n\t\t * context.\n\t\t */\n\t\texecution->stringInfoDataArray = palloc0(\n\t\t\texecution->allocatedColumnCount *\n\t\t\tsizeof(StringInfoData));\n\t\tfor (int i = 0; i < execution->allocatedColumnCount; i++)\n\t\t{\n\t\t\tinitStringInfo(&execution->stringInfoDataArray[i]);\n\t\t}\n\t}\n\n\tif (execution->localExecutionSupported &&\n\t\tShouldExecuteTasksLocally(taskList))\n\t{\n\t\tbool readOnlyPlan = !TaskListModifiesDatabase(modLevel, taskList);\n\t\tExtractLocalAndRemoteTasks(readOnlyPlan, taskList, &execution->localTaskList,\n\t\t\t\t\t\t\t\t   &execution->remoteTaskList);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * Get a shallow copy of the list as we rely on remoteAndLocalTaskList\n\t\t * across the execution.\n\t\t */\n\t\texecution->remoteTaskList = list_copy(execution->remoteAndLocalTaskList);\n\t}\n\n\texecution->totalTaskCount = list_length(execution->remoteTaskList);\n\texecution->unfinishedTaskCount = list_length(execution->remoteTaskList);\n\n\treturn execution;\n}\n\n\n/*\n * DecideTaskListTransactionProperties decides whether to use remote transaction\n * blocks, whether to use 2PC for the given task list, and whether to error on any\n * failure.\n *\n * Since these decisions have specific dependencies on each other (e.g. 2PC implies\n * errorOnAnyFailure, but not the other way around) we keep them in the same place.\n */\nstatic TransactionProperties\nDecideTaskListTransactionProperties(RowModifyLevel modLevel, List *taskList, bool\n\t\t\t\t\t\t\t\t\texcludeFromTransaction)\n{\n\tTransactionProperties xactProperties;\n\n\t/* ensure uninitialized padding doesn't escape the function */\n\tmemset_struct_0(xactProperties);\n\txactProperties.errorOnAnyFailure = false;\n\txactProperties.useRemoteTransactionBlocks = TRANSACTION_BLOCKS_ALLOWED;\n\txactProperties.requires2PC = false;\n\n\tif (taskList == NIL)\n\t{\n\t\t/* nothing to do, return defaults */\n\t\treturn xactProperties;\n\t}\n\n\tif (excludeFromTransaction)\n\t{\n\t\txactProperties.useRemoteTransactionBlocks = TRANSACTION_BLOCKS_DISALLOWED;\n\t\treturn xactProperties;\n\t}\n\n\tif (TaskListCannotBeExecutedInTransaction(taskList))\n\t{\n\t\t/*\n\t\t * We prefer to error on any failures for CREATE INDEX\n\t\t * CONCURRENTLY or VACUUM//VACUUM ANALYZE (e.g., COMMIT_PROTOCOL_BARE).\n\t\t */\n\t\txactProperties.errorOnAnyFailure = true;\n\t\txactProperties.useRemoteTransactionBlocks = TRANSACTION_BLOCKS_DISALLOWED;\n\t\treturn xactProperties;\n\t}\n\n\tif (TaskListRequiresRollback(taskList))\n\t{\n\t\t/* transaction blocks are required if the task list needs to roll back */\n\t\txactProperties.useRemoteTransactionBlocks = TRANSACTION_BLOCKS_REQUIRED;\n\n\t\tif (TaskListRequires2PC(taskList))\n\t\t{\n\t\t\t/*\n\t\t\t * Although using two phase commit protocol is an independent decision than\n\t\t\t * failing on any error, we prefer to couple them. Our motivation is that\n\t\t\t * the failures are rare, and we prefer to avoid marking placements invalid\n\t\t\t * in case of failures.\n\t\t\t */\n\t\t\txactProperties.errorOnAnyFailure = true;\n\t\t\txactProperties.requires2PC = true;\n\t\t}\n\t}\n\telse if (InCoordinatedTransaction())\n\t{\n\t\t/*\n\t\t * If we are already in a coordinated transaction then transaction blocks\n\t\t * are required even if they are not strictly required for the current\n\t\t * execution.\n\t\t */\n\t\txactProperties.useRemoteTransactionBlocks = TRANSACTION_BLOCKS_REQUIRED;\n\t}\n\n\treturn xactProperties;\n}\n\n\n/*\n * StartDistributedExecution sets up the coordinated transaction and 2PC for\n * the execution whenever necessary. It also keeps track of parallel relation\n * accesses to enforce restrictions that arise due to foreign keys to reference\n * tables.\n */\nvoid\nStartDistributedExecution(DistributedExecution *execution)\n{\n\tTransactionProperties *xactProperties = execution->transactionProperties;\n\n\tif (xactProperties->useRemoteTransactionBlocks == TRANSACTION_BLOCKS_REQUIRED)\n\t{\n\t\tUseCoordinatedTransaction();\n\t}\n\n\tif (xactProperties->requires2PC)\n\t{\n\t\tUse2PCForCoordinatedTransaction();\n\t}\n\n\t/*\n\t * Prevent unsafe concurrent modifications of replicated shards by taking\n\t * locks.\n\t *\n\t * When modifying a reference tables in MX mode, we take the lock via RPC\n\t * to the first worker in a transaction block, which activates a coordinated\n\t * transaction. We need to do this before determining whether the execution\n\t * should use transaction blocks (see below).\n\t *\n\t * We acquire the locks for both the remote and local tasks.\n\t */\n\tAcquireExecutorShardLocksForExecution(execution->modLevel,\n\t\t\t\t\t\t\t\t\t\t  execution->remoteAndLocalTaskList);\n\n\t/*\n\t * We should not record parallel access if the target pool size is less than 2.\n\t * The reason is that we define parallel access as at least two connections\n\t * accessing established to worker node.\n\t *\n\t * It is not ideal to have this check here, it'd have been better if we simply passed\n\t * DistributedExecution directly to the RecordParallelAccess*() function. However,\n\t * since we have two other executors that rely on the function, we had to only pass\n\t * the tasklist to have a common API.\n\t */\n\tif (execution->targetPoolSize > 1)\n\t{\n\t\t/*\n\t\t * Record the access for both the local and remote tasks. The main goal\n\t\t * is to make sure that Citus behaves consistently even if the local\n\t\t * shards are moved away.\n\t\t */\n\t\tRecordParallelRelationAccessForTaskList(execution->remoteAndLocalTaskList);\n\t}\n\n\t/* make sure we are not doing remote execution from within a task */\n\tif (execution->remoteTaskList != NIL)\n\t{\n\t\tbool isRemote = true;\n\t\tEnsureTaskExecutionAllowed(isRemote);\n\t}\n}\n\n\n/*\n *  DistributedExecutionModifiesDatabase returns true if the execution modifies the data\n *  or the schema.\n */\nstatic bool\nDistributedExecutionModifiesDatabase(DistributedExecution *execution)\n{\n\treturn TaskListModifiesDatabase(execution->modLevel,\n\t\t\t\t\t\t\t\t\texecution->remoteAndLocalTaskList);\n}\n\n\n/*\n * FinishDistributedExecution cleans up resources associated with a\n * distributed execution.\n */\nstatic void\nFinishDistributedExecution(DistributedExecution *execution)\n{\n\tif (DistributedExecutionModifiesDatabase(execution))\n\t{\n\t\t/* prevent copying shards in same transaction */\n\t\tXactModificationLevel = XACT_MODIFICATION_DATA;\n\t}\n}\n\n\n/*\n * AssignTasksToConnectionsOrWorkerPool goes through the list of tasks to determine whether any\n * task placements need to be assigned to particular connections because of preceding\n * operations in the transaction. It then adds those connections to the pool and adds\n * the task placement executions to the assigned task queue of the connection.\n */\nstatic void\nAssignTasksToConnectionsOrWorkerPool(DistributedExecution *execution)\n{\n\tRowModifyLevel modLevel = execution->modLevel;\n\tList *taskList = execution->remoteTaskList;\n\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tbool placementExecutionReady = true;\n\t\tint placementExecutionIndex = 0;\n\t\tint placementExecutionCount = list_length(task->taskPlacementList);\n\n\t\t/*\n\t\t * Execution of a command on a shard, which may have multiple replicas.\n\t\t */\n\t\tShardCommandExecution *shardCommandExecution =\n\t\t\t(ShardCommandExecution *) palloc0(sizeof(ShardCommandExecution));\n\t\tshardCommandExecution->task = task;\n\t\tshardCommandExecution->executionOrder = ExecutionOrderForTask(modLevel, task);\n\t\tshardCommandExecution->executionState = TASK_EXECUTION_NOT_FINISHED;\n\t\tshardCommandExecution->localExecutionSupported =\n\t\t\texecution->localExecutionSupported;\n\t\tshardCommandExecution->placementExecutions =\n\t\t\t(TaskPlacementExecution **) palloc0(placementExecutionCount *\n\t\t\t\t\t\t\t\t\t\t\t\tsizeof(TaskPlacementExecution *));\n\t\tshardCommandExecution->placementExecutionCount = placementExecutionCount;\n\n\t\tSetAttributeInputMetadata(execution, shardCommandExecution);\n\t\tShardPlacement *taskPlacement = NULL;\n\t\tforeach_declared_ptr(taskPlacement, task->taskPlacementList)\n\t\t{\n\t\t\tint connectionFlags = 0;\n\t\t\tchar *nodeName = NULL;\n\t\t\tint nodePort = 0;\n\t\t\tLookupTaskPlacementHostAndPort(taskPlacement, &nodeName, &nodePort);\n\n\t\t\tWorkerPool *workerPool = FindOrCreateWorkerPool(execution, nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodePort);\n\n\t\t\t/*\n\t\t\t * Execution of a command on a shard placement, which may not always\n\t\t\t * happen if the query is read-only and the shard has multiple placements.\n\t\t\t */\n\t\t\tTaskPlacementExecution *placementExecution =\n\t\t\t\t(TaskPlacementExecution *) palloc0(sizeof(TaskPlacementExecution));\n\t\t\tplacementExecution->shardCommandExecution = shardCommandExecution;\n\t\t\tplacementExecution->shardPlacement = taskPlacement;\n\t\t\tplacementExecution->workerPool = workerPool;\n\t\t\tplacementExecution->placementExecutionIndex = placementExecutionIndex;\n\t\t\tplacementExecution->queryIndex = 0;\n\t\t\tINSTR_TIME_SET_ZERO(placementExecution->startTime);\n\t\t\tINSTR_TIME_SET_ZERO(placementExecution->endTime);\n\n\t\t\tif (placementExecutionReady)\n\t\t\t{\n\t\t\t\tplacementExecution->executionState = PLACEMENT_EXECUTION_READY;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tplacementExecution->executionState = PLACEMENT_EXECUTION_NOT_READY;\n\t\t\t}\n\n\t\t\tshardCommandExecution->placementExecutions[placementExecutionIndex] =\n\t\t\t\tplacementExecution;\n\n\t\t\tplacementExecutionIndex++;\n\n\t\t\tList *placementAccessList = PlacementAccessListForTask(task, taskPlacement);\n\n\t\t\tMultiConnection *connection = NULL;\n\t\t\tif (execution->transactionProperties->useRemoteTransactionBlocks !=\n\t\t\t\tTRANSACTION_BLOCKS_DISALLOWED)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Determine whether the task has to be assigned to a particular connection\n\t\t\t\t * due to a preceding access to the placement in the same transaction.\n\t\t\t\t */\n\t\t\t\tconnection = GetConnectionIfPlacementAccessedInXact(\n\t\t\t\t\tconnectionFlags,\n\t\t\t\t\tplacementAccessList,\n\t\t\t\t\tNULL);\n\t\t\t}\n\n\t\t\tif (connection != NULL)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Note: We may get the same connection for multiple task placements.\n\t\t\t\t * FindOrCreateWorkerSession ensures that we only have one session per\n\t\t\t\t * connection.\n\t\t\t\t */\n\t\t\t\tWorkerSession *session =\n\t\t\t\t\tFindOrCreateWorkerSession(workerPool, connection);\n\n\t\t\t\tereport(DEBUG4, (errmsg(\"Session %ld (%s:%d) has an assigned task\",\n\t\t\t\t\t\t\t\t\t\tsession->sessionId, connection->hostname,\n\t\t\t\t\t\t\t\t\t\tconnection->port)));\n\n\t\t\t\tplacementExecution->assignedSession = session;\n\n\t\t\t\t/* if executed, this task placement must use this session */\n\t\t\t\tif (placementExecutionReady)\n\t\t\t\t{\n\t\t\t\t\tdlist_push_tail(&session->readyTaskQueue,\n\t\t\t\t\t\t\t\t\t&placementExecution->sessionReadyQueueNode);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tdlist_push_tail(&session->pendingTaskQueue,\n\t\t\t\t\t\t\t\t\t&placementExecution->sessionPendingQueueNode);\n\t\t\t\t}\n\n\t\t\t\t/* always poll the connection in the first round */\n\t\t\t\tUpdateConnectionWaitFlags(session,\n\t\t\t\t\t\t\t\t\t\t  WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE);\n\n\t\t\t\t/* If the connections are already avaliable, make sure to activate\n\t\t\t\t * 2PC when necessary.\n\t\t\t\t */\n\t\t\t\tActivate2PCIfModifyingTransactionExpandsToNewNode(session);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tplacementExecution->assignedSession = NULL;\n\n\t\t\t\tif (placementExecutionReady)\n\t\t\t\t{\n\t\t\t\t\t/* task is ready to execute on any session */\n\t\t\t\t\tdlist_push_tail(&workerPool->readyTaskQueue,\n\t\t\t\t\t\t\t\t\t&placementExecution->workerReadyQueueNode);\n\n\t\t\t\t\tworkerPool->readyTaskCount++;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* task can be executed on any session, but is not yet ready */\n\t\t\t\t\tdlist_push_tail(&workerPool->pendingTaskQueue,\n\t\t\t\t\t\t\t\t\t&placementExecution->workerPendingQueueNode);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (shardCommandExecution->executionOrder != EXECUTION_ORDER_PARALLEL)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Except for commands that can be executed across all placements\n\t\t\t\t * in parallel, only the first placement execution is immediately\n\t\t\t\t * ready. Set placementExecutionReady to false for the remaining\n\t\t\t\t * placements.\n\t\t\t\t */\n\t\t\t\tplacementExecutionReady = false;\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * We sort the workerList because adaptive connection management\n\t * (e.g., OPTIONAL_CONNECTION) requires any concurrent executions\n\t * to wait for the connections in the same order to prevent any\n\t * starvation. If we don't sort, we might end up with:\n\t *      Execution 1: Get connection for worker 1, wait for worker 2\n\t *      Execution 2: Get connection for worker 2, wait for worker 1\n\t *\n\t *  and, none could proceed. Instead, we enforce every execution establish\n\t *  the required connections to workers in the same order.\n\t */\n\texecution->workerList = SortList(execution->workerList, WorkerPoolCompare);\n\n\t/*\n\t * The executor claims connections exclusively to make sure that calls to\n\t * StartNodeUserDatabaseConnection do not return the same connections.\n\t *\n\t * We need to do this after assigning tasks to connections because the same\n\t * connection may be be returned multiple times by GetPlacementListConnectionIfCached.\n\t */\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, execution->sessionList)\n\t{\n\t\tMultiConnection *connection = session->connection;\n\n\t\tClaimConnectionExclusively(connection);\n\t}\n}\n\n\n/*\n * WorkerPoolCompare is based on WorkerNodeCompare function. The function\n * compares two worker nodes by their host name and port number.\n */\nstatic int\nWorkerPoolCompare(const void *lhsKey, const void *rhsKey)\n{\n\tconst WorkerPool *workerLhs = *(const WorkerPool **) lhsKey;\n\tconst WorkerPool *workerRhs = *(const WorkerPool **) rhsKey;\n\n\treturn NodeNamePortCompare(workerLhs->nodeName, workerRhs->nodeName,\n\t\t\t\t\t\t\t   workerLhs->nodePort, workerRhs->nodePort);\n}\n\n\n/*\n * SetAttributeInputMetadata sets attributeInputMetadata in\n * shardCommandExecution for all the queries that are part of its task.\n * This contains the deserialization functions for the tuples that will be\n * received. It also sets binaryResults when applicable.\n */\nstatic void\nSetAttributeInputMetadata(DistributedExecution *execution,\n\t\t\t\t\t\t  ShardCommandExecution *shardCommandExecution)\n{\n\tTupleDestination *tupleDest = shardCommandExecution->task->tupleDest ?\n\t\t\t\t\t\t\t\t  shardCommandExecution->task->tupleDest :\n\t\t\t\t\t\t\t\t  execution->defaultTupleDest;\n\tuint32 queryCount = shardCommandExecution->task->queryCount;\n\tshardCommandExecution->attributeInputMetadata = palloc0(queryCount *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsizeof(AttInMetadata *));\n\n\tfor (uint32 queryIndex = 0; queryIndex < queryCount; queryIndex++)\n\t{\n\t\tAttInMetadata *attInMetadata = NULL;\n\t\tTupleDesc tupleDescriptor = tupleDest->tupleDescForQuery(tupleDest,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t queryIndex);\n\t\tif (tupleDescriptor == NULL)\n\t\t{\n\t\t\tattInMetadata = NULL;\n\t\t}\n\t\telse if (EnableBinaryProtocol && CanUseBinaryCopyFormat(tupleDescriptor))\n\t\t{\n\t\t\tattInMetadata = TupleDescGetAttBinaryInMetadata(tupleDescriptor);\n\t\t\tshardCommandExecution->binaryResults = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tattInMetadata = TupleDescGetAttInMetadata(tupleDescriptor);\n\t\t}\n\n\t\tshardCommandExecution->attributeInputMetadata[queryIndex] = attInMetadata;\n\t}\n}\n\n\n/*\n * ExecutionOrderForTask gives the appropriate execution order for a task.\n */\nstatic PlacementExecutionOrder\nExecutionOrderForTask(RowModifyLevel modLevel, Task *task)\n{\n\tswitch (task->taskType)\n\t{\n\t\tcase READ_TASK:\n\t\t{\n\t\t\treturn EXECUTION_ORDER_ANY;\n\t\t}\n\n\t\tcase MODIFY_TASK:\n\t\t{\n\t\t\t/*\n\t\t\t * For non-commutative modifications we take aggressive locks, so\n\t\t\t * there is no risk of deadlock and we can run them in parallel.\n\t\t\t * When the modification is commutative, we take no additional\n\t\t\t * locks, so we take a conservative approach and execute sequentially\n\t\t\t * to avoid deadlocks.\n\t\t\t */\n\t\t\tif (modLevel < ROW_MODIFY_NONCOMMUTATIVE)\n\t\t\t{\n\t\t\t\treturn EXECUTION_ORDER_SEQUENTIAL;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn EXECUTION_ORDER_PARALLEL;\n\t\t\t}\n\t\t}\n\n\t\tcase DDL_TASK:\n\t\tcase VACUUM_ANALYZE_TASK:\n\t\tcase MAP_TASK:\n\t\tcase MERGE_TASK:\n\t\tcase MAP_OUTPUT_FETCH_TASK:\n\t\tcase MERGE_FETCH_TASK:\n\t\t{\n\t\t\treturn EXECUTION_ORDER_PARALLEL;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unsupported task type %d in adaptive executor\",\n\t\t\t\t\t\t\t\t   task->taskType)));\n\t\t}\n\t}\n}\n\n\n/*\n * FindOrCreateWorkerPool gets the pool of connections for a particular worker.\n */\nstatic WorkerPool *\nFindOrCreateWorkerPool(DistributedExecution *execution, char *nodeName, int nodePort)\n{\n\tWorkerPool *workerPool = NULL;\n\tforeach_declared_ptr(workerPool, execution->workerList)\n\t{\n\t\tif (strncmp(nodeName, workerPool->nodeName, WORKER_LENGTH) == 0 &&\n\t\t\tnodePort == workerPool->nodePort)\n\t\t{\n\t\t\treturn workerPool;\n\t\t}\n\t}\n\n\tworkerPool = (WorkerPool *) palloc0(sizeof(WorkerPool));\n\tworkerPool->nodeName = pstrdup(nodeName);\n\tworkerPool->nodePort = nodePort;\n\n\tWorkerNode *workerNode = FindWorkerNode(nodeName, nodePort);\n\tif (workerNode)\n\t{\n\t\tworkerPool->poolToLocalNode =\n\t\t\tworkerNode->groupId == GetLocalGroupId();\n\t}\n\n\t/* \"open\" connections aggressively when there are cached connections */\n\tint nodeConnectionCount = MaxCachedConnectionsPerWorker;\n\tworkerPool->maxNewConnectionsPerCycle = Max(1, nodeConnectionCount);\n\n\tdlist_init(&workerPool->pendingTaskQueue);\n\tdlist_init(&workerPool->readyTaskQueue);\n\n\tworkerPool->distributedExecution = execution;\n\n\texecution->workerList = lappend(execution->workerList, workerPool);\n\n\treturn workerPool;\n}\n\n\n/*\n * FindOrCreateWorkerSession returns a session with the given connection,\n * either existing or new. New sessions are added to the worker pool and\n * the distributed execution.\n */\nstatic WorkerSession *\nFindOrCreateWorkerSession(WorkerPool *workerPool, MultiConnection *connection)\n{\n\tDistributedExecution *execution = workerPool->distributedExecution;\n\tstatic uint64 sessionId = 1;\n\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, workerPool->sessionList)\n\t{\n\t\tif (session->connection == connection)\n\t\t{\n\t\t\treturn session;\n\t\t}\n\t}\n\n\n\tsession = (WorkerSession *) palloc0(sizeof(WorkerSession));\n\tsession->sessionId = sessionId++;\n\tsession->connection = connection;\n\tsession->workerPool = workerPool;\n\tsession->commandsSent = 0;\n\tsession->waitEventSetIndex = WAIT_EVENT_SET_INDEX_NOT_INITIALIZED;\n\n\t/* always detect closed sockets */\n\tUpdateConnectionWaitFlags(session, WL_SOCKET_CLOSED);\n\n\tdlist_init(&session->pendingTaskQueue);\n\tdlist_init(&session->readyTaskQueue);\n\n\tif (connection->connectionState == MULTI_CONNECTION_CONNECTED)\n\t{\n\t\t/* keep track of how many connections are ready */\n\t\tworkerPool->activeConnectionCount++;\n\t\tworkerPool->idleConnectionCount++;\n\n\t\tsession->sessionHasActiveConnection = true;\n\t}\n\n\tworkerPool->unusedConnectionCount++;\n\n\t/*\n\t * Record the first connection establishment time to the pool. We need this\n\t * to enforce NodeConnectionTimeout.\n\t */\n\tif (list_length(workerPool->sessionList) == 0)\n\t{\n\t\tINSTR_TIME_SET_CURRENT(workerPool->poolStartTime);\n\t\tworkerPool->checkForPoolTimeout = true;\n\t}\n\n\tworkerPool->sessionList = lappend(workerPool->sessionList, session);\n\texecution->sessionList = lappend(execution->sessionList, session);\n\n\treturn session;\n}\n\n\n/*\n * RemoteSocketClosedForNewSession is a helper function for detecting whether\n * the remote socket corresponding to the input session is closed. This is\n * mostly common there is a cached connection and remote server restarted\n * (due to failover or restart etc.).\n *\n * The function is not a generic function that can be called at the start of\n * the execution. The function is not generic because it does not check all\n * the events, even ignores cancellation events. Future callers of this\n * function should consider its limitations.\n */\nstatic void\nRemoteSocketClosedForAnySession(DistributedExecution *execution)\n{\n\tif (!WaitEventSetCanReportClosed())\n\t{\n\t\t/* we cannot detect for this OS */\n\t\treturn;\n\t}\n\n\tlong timeout = 0;/* don't wait */\n\n\tint eventCount = WaitEventSetWait(execution->waitEventSet, timeout, execution->events,\n\t\t\t\t\t\t\t\t\t  execution->eventSetSize, WAIT_EVENT_CLIENT_READ);\n\tProcessWaitEventsForSocketClosed(execution->events, eventCount);\n}\n\n\n/*\n * SequentialRunDistributedExecution gets a distributed execution and\n * executes each individual task in the execution sequentially, one\n * task at a time. See related function ShouldRunTasksSequentially()\n * for more detail on the definition of SequentialRun.\n */\nstatic void\nSequentialRunDistributedExecution(DistributedExecution *execution)\n{\n\tList *taskList = execution->remoteTaskList;\n\tint connectionMode = MultiShardConnectionType;\n\n\t/*\n\t * There are some implicit assumptions about this setting for the sequential\n\t * executions, so make sure to set it.\n\t */\n\tMultiShardConnectionType = SEQUENTIAL_CONNECTION;\n\tTask *taskToExecute = NULL;\n\tforeach_declared_ptr(taskToExecute, taskList)\n\t{\n\t\texecution->remoteAndLocalTaskList = list_make1(taskToExecute);\n\t\texecution->remoteTaskList = list_make1(taskToExecute);\n\t\texecution->totalTaskCount = 1;\n\t\texecution->unfinishedTaskCount = 1;\n\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tif (IsHoldOffCancellationReceived())\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t/* simply call the regular execution function */\n\t\tRunDistributedExecution(execution);\n\t}\n\n\t/* set back the original execution mode */\n\tMultiShardConnectionType = connectionMode;\n}\n\n\n/*\n * RunDistributedExecution runs a distributed execution to completion. It first opens\n * connections for distributed execution and assigns each task with shard placements\n * that have previously been modified in the current transaction to the connection\n * that modified them. Then, it creates a wait event set to listen for events on\n * any of the connections and runs the connection state machine when a connection\n * has an event.\n */\nvoid\nRunDistributedExecution(DistributedExecution *execution)\n{\n\tAssignTasksToConnectionsOrWorkerPool(execution);\n\n\tPG_TRY();\n\t{\n\t\t/* Preemptively step state machines in case of immediate errors */\n\t\tWorkerSession *session = NULL;\n\t\tforeach_declared_ptr(session, execution->sessionList)\n\t\t{\n\t\t\tConnectionStateMachine(session);\n\t\t}\n\n\t\tbool cancellationReceived = false;\n\n\t\t/* always (re)build the wait event set the first time */\n\t\texecution->rebuildWaitEventSet = true;\n\n\t\t/*\n\t\t * Iterate until all the tasks are finished. Once all the tasks\n\t\t * are finished, ensure that all the connection initializations\n\t\t * are also finished. Otherwise, those connections are terminated\n\t\t * abruptly before they are established (or failed). Instead, we let\n\t\t * the ConnectionStateMachine() to properly handle them.\n\t\t *\n\t\t * Note that we could have the connections that are not established\n\t\t * as a side effect of slow-start algorithm. At the time the algorithm\n\t\t * decides to establish new connections, the execution might have tasks\n\t\t * to finish. But, the execution might finish before the new connections\n\t\t * are established.\n\t\t *\n\t\t * Note that the rules explained above could be overriden by any\n\t\t * cancellation to the query. In that case, we terminate the execution\n\t\t * irrespective of the current status of the tasks or the connections.\n\t\t */\n\t\twhile (!cancellationReceived &&\n\t\t\t   (execution->unfinishedTaskCount > 0 ||\n\t\t\t\tHasIncompleteConnectionEstablishment(execution)))\n\t\t{\n\t\t\tWorkerPool *workerPool = NULL;\n\t\t\tforeach_declared_ptr(workerPool, execution->workerList)\n\t\t\t{\n\t\t\t\tManageWorkerPool(workerPool);\n\t\t\t}\n\n\t\t\tbool skipWaitEvents = false;\n\t\t\tif (execution->remoteTaskList == NIL)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * All the tasks are failed over to the local execution, no need\n\t\t\t\t * to wait for any connection activity.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (execution->rebuildWaitEventSet)\n\t\t\t{\n\t\t\t\tRebuildWaitEventSet(execution);\n\n\t\t\t\tskipWaitEvents =\n\t\t\t\t\tProcessSessionsWithFailedWaitEventSetOperations(execution);\n\t\t\t}\n\t\t\telse if (execution->waitFlagsChanged)\n\t\t\t{\n\t\t\t\tRebuildWaitEventSetFlags(execution->waitEventSet, execution->sessionList);\n\t\t\t\texecution->waitFlagsChanged = false;\n\n\t\t\t\tskipWaitEvents =\n\t\t\t\t\tProcessSessionsWithFailedWaitEventSetOperations(execution);\n\t\t\t}\n\n\t\t\tif (skipWaitEvents)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Some operation on the wait event set is failed, retry\n\t\t\t\t * as we already removed the problematic connections.\n\t\t\t\t */\n\t\t\t\texecution->rebuildWaitEventSet = true;\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* wait for I/O events */\n\t\t\tlong timeout = NextEventTimeout(execution);\n\t\t\tint eventCount =\n\t\t\t\tWaitEventSetWait(execution->waitEventSet, timeout, execution->events,\n\t\t\t\t\t\t\t\t execution->eventSetSize, WAIT_EVENT_CLIENT_READ);\n\n\t\t\tProcessWaitEvents(execution, execution->events, eventCount,\n\t\t\t\t\t\t\t  &cancellationReceived);\n\t\t}\n\n\t\tFreeExecutionWaitEvents(execution);\n\n\t\tCleanUpSessions(execution);\n\t}\n\tPG_CATCH();\n\t{\n\t\t/*\n\t\t * We can still recover from error using ROLLBACK TO SAVEPOINT,\n\t\t * unclaim all connections to allow that.\n\t\t */\n\t\tUnclaimAllSessionConnections(execution->sessionList);\n\n\t\tFreeExecutionWaitEvents(execution);\n\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n}\n\n\n/*\n * ProcessSessionsWithFailedWaitEventSetOperations goes over the session list\n * and processes sessions with failed wait event set operations.\n *\n * Failed sessions are not going to generate any further events, so it is our\n * only chance to process the failure by calling into `ConnectionStateMachine`.\n *\n * The function returns true if any session failed.\n */\nstatic bool\nProcessSessionsWithFailedWaitEventSetOperations(DistributedExecution *execution)\n{\n\tbool foundFailedSession = false;\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, execution->sessionList)\n\t{\n\t\tif (session->waitEventSetIndex == WAIT_EVENT_SET_INDEX_FAILED)\n\t\t{\n\t\t\t/*\n\t\t\t * We can only lost only already connected connections,\n\t\t\t * others are regular failures.\n\t\t\t */\n\t\t\tMultiConnection *connection = session->connection;\n\t\t\tif (connection->connectionState == MULTI_CONNECTION_CONNECTED)\n\t\t\t{\n\t\t\t\tconnection->connectionState = MULTI_CONNECTION_LOST;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tconnection->connectionState = MULTI_CONNECTION_FAILED;\n\t\t\t\tIncrementStatCounterForMyDb(STAT_CONNECTION_ESTABLISHMENT_FAILED);\n\t\t\t}\n\n\n\t\t\tConnectionStateMachine(session);\n\n\t\t\tsession->waitEventSetIndex = WAIT_EVENT_SET_INDEX_NOT_INITIALIZED;\n\n\t\t\tfoundFailedSession = true;\n\t\t}\n\t}\n\n\treturn foundFailedSession;\n}\n\n\n/*\n * HasIncompleteConnectionEstablishment returns true if any of the connections\n * that has been initiated by the executor is in initialization stage.\n */\nstatic bool\nHasIncompleteConnectionEstablishment(DistributedExecution *execution)\n{\n\tif (!PreventIncompleteConnectionEstablishment)\n\t{\n\t\treturn false;\n\t}\n\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, execution->sessionList)\n\t{\n\t\tMultiConnection *connection = session->connection;\n\t\tif (connection->connectionState == MULTI_CONNECTION_INITIAL ||\n\t\t\tconnection->connectionState == MULTI_CONNECTION_CONNECTING)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * RebuildWaitEventSet updates the waitEventSet for the distributed execution.\n * This happens when the connection set for the distributed execution is changed,\n * which means that we need to update which connections we wait on for events.\n */\nstatic void\nRebuildWaitEventSet(DistributedExecution *execution)\n{\n\tRebuildWaitEventSetForSessions(execution);\n\n\tAddLatchWaitEventToExecution(execution);\n}\n\n\n/*\n * AddLatchWaitEventToExecution is a helper function that adds the latch\n * wait event to the execution->waitEventSet. Note that this function assumes\n * that execution->waitEventSet has already allocated enough slot for latch\n * event.\n */\nstatic void\nAddLatchWaitEventToExecution(DistributedExecution *execution)\n{\n\tCitusAddWaitEventSetToSet(execution->waitEventSet, WL_LATCH_SET, PGINVALID_SOCKET,\n\t\t\t\t\t\t\t  MyLatch, NULL);\n}\n\n\n/*\n * RebuildWaitEventSetForSessions re-creates the waitEventSet for the\n * sessions involved in the distributed execution.\n *\n * Most of the time you need RebuildWaitEventSet() which also includes\n * adds the Latch wait event to the set.\n */\nstatic void\nRebuildWaitEventSetForSessions(DistributedExecution *execution)\n{\n\tFreeExecutionWaitEvents(execution);\n\n\texecution->waitEventSet = BuildWaitEventSet(execution->sessionList);\n\n\texecution->eventSetSize = GetEventSetSize(execution->sessionList);\n\texecution->events = palloc0(execution->eventSetSize * sizeof(WaitEvent));\n\n\tCitusAddWaitEventSetToSet(execution->waitEventSet, WL_POSTMASTER_DEATH,\n\t\t\t\t\t\t\t  PGINVALID_SOCKET, NULL, NULL);\n\n\texecution->rebuildWaitEventSet = false;\n\texecution->waitFlagsChanged = false;\n}\n\n\n/*\n * ProcessWaitEvents processes the received events from connections.\n */\nstatic void\nProcessWaitEvents(DistributedExecution *execution, WaitEvent *events, int eventCount,\n\t\t\t\t  bool *cancellationReceived)\n{\n\tint eventIndex = 0;\n\n\t/* process I/O events */\n\tfor (; eventIndex < eventCount; eventIndex++)\n\t{\n\t\tWaitEvent *event = &events[eventIndex];\n\n\t\tif (event->events & WL_POSTMASTER_DEATH)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"postmaster was shut down, exiting\")));\n\t\t}\n\n\t\tif (event->events & WL_LATCH_SET)\n\t\t{\n\t\t\tResetLatch(MyLatch);\n\n\t\t\tif (execution->raiseInterrupts)\n\t\t\t{\n\t\t\t\tCHECK_FOR_INTERRUPTS();\n\t\t\t}\n\n\t\t\tif (IsHoldOffCancellationReceived())\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Break out of event loop immediately in case of cancellation.\n\t\t\t\t * We cannot use \"return\" here inside a PG_TRY() block since\n\t\t\t\t * then the exception stack won't be reset.\n\t\t\t\t */\n\t\t\t\t*cancellationReceived = true;\n\t\t\t}\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tWorkerSession *session = (WorkerSession *) event->user_data;\n\t\tsession->latestUnconsumedWaitEvents = event->events;\n\n\t\tConnectionStateMachine(session);\n\t}\n}\n\n\n/*\n * ProcessWaitEventsForSocketClosed mainly checks for WL_SOCKET_CLOSED event.\n * If WL_SOCKET_CLOSED is found, the function sets the underlying connection's\n * state as MULTI_CONNECTION_LOST.\n */\nstatic void\nProcessWaitEventsForSocketClosed(WaitEvent *events, int eventCount)\n{\n\tint eventIndex = 0;\n\n\t/* process I/O events */\n\tfor (; eventIndex < eventCount; eventIndex++)\n\t{\n\t\tWaitEvent *event = &events[eventIndex];\n\n\t\tif (event->events & WL_POSTMASTER_DEATH)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"postmaster was shut down, exiting\")));\n\t\t}\n\n\t\tWorkerSession *session = (WorkerSession *) event->user_data;\n\t\tsession->latestUnconsumedWaitEvents = event->events;\n\n\t\tif (session->latestUnconsumedWaitEvents & WL_SOCKET_CLOSED)\n\t\t{\n\t\t\t/* let the ConnectionStateMachine handle the rest */\n\t\t\tsession->connection->connectionState = MULTI_CONNECTION_LOST;\n\t\t}\n\t}\n}\n\n\n/*\n * ManageWorkerPool ensures the worker pool has the appropriate number of connections\n * based on the number of pending tasks.\n */\nstatic void\nManageWorkerPool(WorkerPool *workerPool)\n{\n\tDistributedExecution *execution = workerPool->distributedExecution;\n\n\t/* we do not expand the pool further if there was any failure */\n\tif (HasAnyConnectionFailure(workerPool))\n\t{\n\t\treturn;\n\t}\n\n\t/* we wait until a slow start interval has passed before expanding the pool */\n\tif (ShouldWaitForSlowStart(workerPool))\n\t{\n\t\treturn;\n\t}\n\n\tint newConnectionCount = CalculateNewConnectionCount(workerPool);\n\tif (newConnectionCount <= 0)\n\t{\n\t\treturn;\n\t}\n\n\t/* increase the open rate every cycle (like TCP slow start) */\n\tworkerPool->maxNewConnectionsPerCycle += 1;\n\n\tOpenNewConnections(workerPool, newConnectionCount, execution->transactionProperties);\n\n\t/*\n\t * Cannot establish new connections to the local host, most probably because the\n\t * local node cannot accept new connections (e.g., hit max_connections). Switch\n\t * the tasks to the local execution.\n\t *\n\t * We prefer initiatedConnectionCount over the new connection establishments happen\n\t * in this iteration via OpenNewConnections(). The reason is that it is expected for\n\t * OpenNewConnections() to not open any new connections as long as the connections\n\t * are optional (e.g., the second or later connections in the pool). But, for\n\t * initiatedConnectionCount to be zero, the connection to the local pool should have\n\t * been failed.\n\t */\n\tint initiatedConnectionCount = list_length(workerPool->sessionList);\n\tif (initiatedConnectionCount == 0)\n\t{\n\t\t/*\n\t\t * Only the pools to the local node are allowed to have optional\n\t\t * connections for the first connection. Hence, initiatedConnectionCount\n\t\t * could only be zero for poolToLocalNode. For other pools, the connection\n\t\t * manager would wait until it gets at least one connection.\n\t\t */\n\t\tAssert(workerPool->poolToLocalNode);\n\n\t\tWorkerPoolFailed(workerPool);\n\n\t\tif (execution->failed)\n\t\t{\n\t\t\tconst char *errHint =\n\t\t\t\texecution->localExecutionSupported ?\n\t\t\t\t\"This command supports local execution. Consider enabling \"\n\t\t\t\t\"local execution using SET citus.enable_local_execution \"\n\t\t\t\t\"TO true;\" :\n\t\t\t\t\"Consider using a higher value for max_connections\";\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t\terrmsg(\"the total number of connections on the \"\n\t\t\t\t\t\t\t\t   \"server is more than max_connections(%d)\",\n\t\t\t\t\t\t\t\t   MaxConnections),\n\t\t\t\t\t\t\terrhint(\"%s\", errHint)));\n\t\t}\n\n\t\treturn;\n\t}\n\n\tINSTR_TIME_SET_CURRENT(workerPool->lastConnectionOpenTime);\n}\n\n\n/*\n * HasAnyConnectionFailure returns true if worker pool has failed,\n * or connection timed out or we have a failure in connections.\n */\nstatic bool\nHasAnyConnectionFailure(WorkerPool *workerPool)\n{\n\tif (workerPool->failureState == WORKER_POOL_FAILED ||\n\t\tworkerPool->failureState == WORKER_POOL_FAILED_OVER_TO_LOCAL)\n\t{\n\t\t/* connection pool failed */\n\t\treturn true;\n\t}\n\n\t/* we might fail the execution or warn the user about connection timeouts */\n\tif (workerPool->checkForPoolTimeout)\n\t{\n\t\tCheckConnectionTimeout(workerPool);\n\t}\n\n\tint failedConnectionCount = workerPool->failedConnectionCount;\n\tif (failedConnectionCount >= 1)\n\t{\n\t\t/* do not attempt to open more connections after one failed */\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n\n/*\n * ShouldWaitForSlowStart returns true if we should wait before\n * opening a new connection because of slow start algorithm.\n */\nstatic bool\nShouldWaitForSlowStart(WorkerPool *workerPool)\n{\n\t/* if we can use a connection per placement, we don't need to wait for slowstart */\n\tif (UseConnectionPerPlacement())\n\t{\n\t\treturn false;\n\t}\n\n\t/* if slow start is disabled, we can open new connections */\n\tif (ExecutorSlowStartInterval == SLOW_START_DISABLED)\n\t{\n\t\treturn false;\n\t}\n\n\tdouble milliSecondsPassedSince = MillisecondsPassedSince(\n\t\tworkerPool->lastConnectionOpenTime);\n\tif (milliSecondsPassedSince < ExecutorSlowStartInterval)\n\t{\n\t\treturn true;\n\t}\n\n\t/*\n\t * Refrain from establishing new connections unless we have already\n\t * finalized all the earlier connection attempts. This prevents unnecessary\n\t * load on the remote nodes and emulates the TCP slow-start algorithm.\n\t */\n\tint initiatedConnectionCount = list_length(workerPool->sessionList);\n\tint finalizedConnectionCount =\n\t\tworkerPool->activeConnectionCount + workerPool->failedConnectionCount;\n\tif (finalizedConnectionCount < initiatedConnectionCount)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CalculateNewConnectionCount returns the amount of connections\n * that we can currently open.\n */\nstatic int\nCalculateNewConnectionCount(WorkerPool *workerPool)\n{\n\tDistributedExecution *execution = workerPool->distributedExecution;\n\n\tint targetPoolSize = execution->targetPoolSize;\n\tint initiatedConnectionCount = list_length(workerPool->sessionList);\n\tint activeConnectionCount PG_USED_FOR_ASSERTS_ONLY =\n\t\tworkerPool->activeConnectionCount;\n\tint idleConnectionCount PG_USED_FOR_ASSERTS_ONLY =\n\t\tworkerPool->idleConnectionCount;\n\tint readyTaskCount = workerPool->readyTaskCount;\n\tint newConnectionCount = 0;\n\n\n\t/* we should always have more (or equal) active connections than idle connections */\n\tAssert(activeConnectionCount >= idleConnectionCount);\n\n\t/* we should always have more (or equal) initiated connections than active connections */\n\tAssert(initiatedConnectionCount >= activeConnectionCount);\n\n\t/* we should never have less than 0 connections ever */\n\tAssert(activeConnectionCount >= 0 && idleConnectionCount >= 0);\n\n\tif (UseConnectionPerPlacement())\n\t{\n\t\tint unusedConnectionCount = workerPool->unusedConnectionCount;\n\n\t\t/*\n\t\t * If force_max_query_parallelization is enabled then we ignore pool size\n\t\t * and idle connections. Instead, we open new connections as long as there\n\t\t * are more tasks than unused connections.\n\t\t */\n\n\t\tnewConnectionCount = Max(readyTaskCount - unusedConnectionCount, 0);\n\t}\n\telse\n\t{\n\t\t/* cannot open more than targetPoolSize connections */\n\t\tint maxNewConnectionCount = targetPoolSize - initiatedConnectionCount;\n\n\t\t/* total number of connections that are (almost) available for tasks */\n\t\tint usableConnectionCount = UsableConnectionCount(workerPool);\n\n\t\t/*\n\t\t * Number of additional connections we would need to run all ready tasks in\n\t\t * parallel.\n\t\t */\n\t\tint newConnectionsForReadyTasks = Max(0, readyTaskCount - usableConnectionCount);\n\n\t\t/* If Slow start is enabled we need to update the maxNewConnection to the current cycle's maximum.*/\n\t\tif (ExecutorSlowStartInterval != SLOW_START_DISABLED)\n\t\t{\n\t\t\tmaxNewConnectionCount = Min(workerPool->maxNewConnectionsPerCycle,\n\t\t\t\t\t\t\t\t\t\tmaxNewConnectionCount);\n\t\t}\n\n\t\t/*\n\t\t * Open enough connections to handle all tasks that are ready, but no more\n\t\t * than the target pool size.\n\t\t */\n\t\tnewConnectionCount = Min(newConnectionsForReadyTasks, maxNewConnectionCount);\n\t\tif (EnableCostBasedConnectionEstablishment && newConnectionCount > 0 &&\n\t\t\tinitiatedConnectionCount <= MaxCachedConnectionsPerWorker &&\n\t\t\tUsingExistingSessionsCheaperThanEstablishingNewConnections(\n\t\t\t\treadyTaskCount, workerPool))\n\t\t{\n\t\t\t/*\n\t\t\t * Before giving the decision, we do one more check. If the cost of\n\t\t\t * executing the remaining tasks over the existing sessions in the\n\t\t\t * pool is cheaper than establishing new connections and executing\n\t\t\t * the tasks over the new connections, we prefer the former.\n\t\t\t *\n\t\t\t * For cached connections we should ignore any optimizations as\n\t\t\t * cached connections are almost free to get. In other words,\n\t\t\t * as long as there are cached connections that the pool has\n\t\t\t * not used yet, aggressively use these already established\n\t\t\t * connections.\n\t\t\t *\n\t\t\t * Note that until MaxCachedConnectionsPerWorker has already been\n\t\t\t * established within the session, we still need to establish\n\t\t\t * the connections right now.\n\t\t\t *\n\t\t\t * Also remember that we are not trying to find the optimal number\n\t\t\t * of connections for the remaining tasks here. Our goal is to prevent\n\t\t\t * connection establishments that are absolutely unnecessary. In the\n\t\t\t * future, we may improve the calculations below to find the optimal\n\t\t\t * number of new connections required.\n\t\t\t */\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\treturn newConnectionCount;\n}\n\n\n/*\n * UsingExistingSessionsCheaperThanEstablishingNewConnections returns true if\n * using the already established connections takes less time compared to opening\n * new connections based on the current execution's stats.\n *\n * The function returns false if the current execution has not established any connections\n * or finished any tasks (e.g., no stats to act on).\n */\nstatic bool\nUsingExistingSessionsCheaperThanEstablishingNewConnections(int readyTaskCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   WorkerPool *workerPool)\n{\n\tint activeConnectionCount = workerPool->activeConnectionCount;\n\tif (workerPool->totalExecutedTasks < 1 || activeConnectionCount < 1)\n\t{\n\t\t/*\n\t\t * The pool has not finished any connection establishment or\n\t\t * task yet. So, we refrain from optimizing the execution.\n\t\t */\n\t\treturn false;\n\t}\n\n\tdouble avgTaskExecutionTime = AvgTaskExecutionTimeApproximation(workerPool);\n\tdouble avgConnectionEstablishmentTime = AvgConnectionEstablishmentTime(workerPool);\n\n\t/* we assume that we are halfway through the execution */\n\tdouble remainingTimeForActiveTaskExecutionsToFinish = avgTaskExecutionTime / 2;\n\n\t/*\n\t * We use \"newConnectionCount\" as if it is the task count as\n\t * we are only interested in this iteration of CalculateNewConnectionCount().\n\t */\n\tdouble totalTimeToExecuteNewTasks = avgTaskExecutionTime * readyTaskCount;\n\n\tdouble estimatedExecutionTimeForNewTasks =\n\t\tfloor(totalTimeToExecuteNewTasks / activeConnectionCount);\n\n\t/*\n\t * First finish the already running tasks, and then use the connections\n\t * to execute the new tasks.\n\t */\n\tdouble costOfExecutingTheTasksOverExistingConnections =\n\t\tremainingTimeForActiveTaskExecutionsToFinish +\n\t\testimatedExecutionTimeForNewTasks;\n\n\t/*\n\t * For every task, the executor is supposed to establish one\n\t * connection and then execute the task over the connection.\n\t */\n\tdouble costOfExecutingTheTasksOverNewConnection =\n\t\t(avgTaskExecutionTime + avgConnectionEstablishmentTime);\n\n\treturn (costOfExecutingTheTasksOverExistingConnections <=\n\t\t\tcostOfExecutingTheTasksOverNewConnection);\n}\n\n\n/*\n * AvgTaskExecutionTimeApproximation returns the approximation of the average task\n * execution times on the workerPool.\n */\nstatic double\nAvgTaskExecutionTimeApproximation(WorkerPool *workerPool)\n{\n\tuint64 totalTaskExecutionTime = workerPool->totalTaskExecutionTime;\n\tint taskCount = workerPool->totalExecutedTasks;\n\n\tinstr_time now;\n\tINSTR_TIME_SET_CURRENT(now);\n\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, workerPool->sessionList)\n\t{\n\t\t/*\n\t\t * Involve the tasks that are currently running. We do this to\n\t\t * make sure that the execution responds with new connections\n\t\t * quickly if the actively running tasks\n\t\t */\n\t\tTaskPlacementExecution *placementExecution = session->currentTask;\n\t\tif (placementExecution != NULL &&\n\t\t\tplacementExecution->executionState == PLACEMENT_EXECUTION_RUNNING)\n\t\t{\n\t\t\tuint64 durationInMicroSecs =\n\t\t\t\tMicrosecondsBetweenTimestamps(placementExecution->startTime, now);\n\n\t\t\t/*\n\t\t\t * Our approximation is that we assume that the task execution is\n\t\t\t * just in the halfway through.\n\t\t\t */\n\t\t\ttotalTaskExecutionTime += (2 * durationInMicroSecs);\n\t\t\ttaskCount += 1;\n\t\t}\n\t}\n\n\treturn taskCount == 0 ? 0 : ((double) totalTaskExecutionTime / taskCount);\n}\n\n\n/*\n * AvgConnectionEstablishmentTime calculates the average connection establishment times\n * for the input workerPool.\n */\nstatic double\nAvgConnectionEstablishmentTime(WorkerPool *workerPool)\n{\n\tdouble totalTimeMicrosec = 0;\n\tint sessionCount = 0;\n\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, workerPool->sessionList)\n\t{\n\t\tMultiConnection *connection = session->connection;\n\n\t\t/*\n\t\t * There could be MaxCachedConnectionsPerWorker connections that are\n\t\t * already connected. Those connections might skew the average\n\t\t * connection establishment times for the current execution. The reason\n\t\t * is that they are established earlier and the connection establishment\n\t\t * times might be different at the moment those connections are established.\n\t\t */\n\t\tif (connection->connectionState == MULTI_CONNECTION_CONNECTED)\n\t\t{\n\t\t\tlong connectionEstablishmentTime =\n\t\t\t\tMicrosecondsBetweenTimestamps(connection->connectionEstablishmentStart,\n\t\t\t\t\t\t\t\t\t\t\t  connection->connectionEstablishmentEnd);\n\n\t\t\ttotalTimeMicrosec += connectionEstablishmentTime;\n\t\t\t++sessionCount;\n\t\t}\n\t}\n\n\treturn (sessionCount == 0) ? 0 : (totalTimeMicrosec / sessionCount);\n}\n\n\n/*\n * OpenNewConnections opens the given amount of connections for the given workerPool.\n */\nstatic void\nOpenNewConnections(WorkerPool *workerPool, int newConnectionCount,\n\t\t\t\t   TransactionProperties *transactionProperties)\n{\n\tereport(DEBUG4, (errmsg(\"opening %d new connections to %s:%d\", newConnectionCount,\n\t\t\t\t\t\t\tworkerPool->nodeName, workerPool->nodePort)));\n\n\tList *newSessionsList = NIL;\n\tfor (int connectionIndex = 0; connectionIndex < newConnectionCount; connectionIndex++)\n\t{\n\t\t/* experimental: just to see the perf benefits of caching connections */\n\t\tint connectionFlags = 0;\n\n\t\tif (transactionProperties->useRemoteTransactionBlocks ==\n\t\t\tTRANSACTION_BLOCKS_DISALLOWED)\n\t\t{\n\t\t\tconnectionFlags |= OUTSIDE_TRANSACTION;\n\t\t}\n\n\t\t/*\n\t\t * Enforce the requirements for adaptive connection management (a.k.a.,\n\t\t * throttle connections if citus.max_shared_pool_size reached)\n\t\t */\n\t\tint adaptiveConnectionManagementFlag =\n\t\t\tAdaptiveConnectionManagementFlag(workerPool->poolToLocalNode,\n\t\t\t\t\t\t\t\t\t\t\t list_length(workerPool->sessionList));\n\t\tconnectionFlags |= adaptiveConnectionManagementFlag;\n\n\t\t/* open a new connection to the worker */\n\t\tMultiConnection *connection = StartNodeUserDatabaseConnection(\n\t\t\tconnectionFlags, workerPool->nodeName, workerPool->nodePort, NULL, NULL);\n\t\tif (!connection)\n\t\t{\n\t\t\t/* connection can only be NULL for optional connections */\n\t\t\tAssert((connectionFlags & OPTIONAL_CONNECTION));\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Assign the initial state in the connection state machine. The connection\n\t\t * may already be open, but ConnectionStateMachine will immediately detect\n\t\t * this.\n\t\t */\n\t\tconnection->connectionState = MULTI_CONNECTION_CONNECTING;\n\n\t\t/*\n\t\t * Ensure that subsequent calls to StartNodeUserDatabaseConnection get a\n\t\t * different connection.\n\t\t */\n\t\tconnection->claimedExclusively = true;\n\n\t\tif (list_length(workerPool->sessionList) == 0)\n\t\t{\n\t\t\t/*\n\t\t\t * The worker pool has just started to establish connections. We need to\n\t\t\t * defer this initialization after StartNodeUserDatabaseConnection()\n\t\t\t * because for non-optional connections, we have some logic to wait\n\t\t\t * until a connection is allowed to be established.\n\t\t\t */\n\t\t\tINSTR_TIME_SET_ZERO(workerPool->poolStartTime);\n\t\t}\n\n\t\t/* create a session for the connection */\n\t\tWorkerSession *session = FindOrCreateWorkerSession(workerPool, connection);\n\t\tnewSessionsList = lappend(newSessionsList, session);\n\t}\n\n\tif (list_length(newSessionsList) == 0)\n\t{\n\t\t/* nothing to do as no new connections happened */\n\t\treturn;\n\t}\n\n\tDistributedExecution *execution = workerPool->distributedExecution;\n\n\t/* we added new connections, rebuild the waitEventSet */\n\tRebuildWaitEventSetForSessions(execution);\n\n\t/*\n\t * If there are any closed sockets, mark connection lost such that\n\t * we can re-connect.\n\t */\n\tRemoteSocketClosedForAnySession(execution);\n\n\t/*\n\t * For RemoteSocketClosedForAnySession() purposes, we explicitly skip\n\t * the latch because we want to handle all cancellations to be caught\n\t * on the main execution loop, not here. We mostly skip cancellations\n\t * on RemoteSocketClosedForAnySession() for simplicity. Handling\n\t * cancellations on the main execution loop much easier to break out\n\t * of the execution.\n\t */\n\tAddLatchWaitEventToExecution(execution);\n\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, newSessionsList)\n\t{\n\t\t/* immediately run the state machine to handle potential failure */\n\t\tConnectionStateMachine(session);\n\t}\n}\n\n\n/*\n * CheckConnectionTimeout makes sure that the execution enforces the connection\n * establishment timeout defined by the user (NodeConnectionTimeout).\n *\n * The rule is that if a worker pool has already initiated connection establishment\n * and has not succeeded to finish establishments that are necessary to execute tasks,\n * take an action. For the types of actions, see the comments in the function.\n *\n * Enforcing the timeout per pool (over per session) helps the execution to continue\n * even if we can establish a single connection as we expect to have target pool size\n * number of connections. In the end, the executor is capable of using one connection\n * to execute multiple tasks.\n */\nstatic void\nCheckConnectionTimeout(WorkerPool *workerPool)\n{\n\tDistributedExecution *execution = workerPool->distributedExecution;\n\tinstr_time poolStartTime = workerPool->poolStartTime;\n\tinstr_time now;\n\tINSTR_TIME_SET_CURRENT(now);\n\n\tint initiatedConnectionCount = list_length(workerPool->sessionList);\n\tint activeConnectionCount = workerPool->activeConnectionCount;\n\tint requiredActiveConnectionCount = 1;\n\n\tif (initiatedConnectionCount == 0)\n\t{\n\t\t/* no connection has been planned for the pool yet */\n\t\tAssert(INSTR_TIME_IS_ZERO(poolStartTime));\n\t\treturn;\n\t}\n\n\t/*\n\t * This is a special case where we assign tasks to sessions even before\n\t * the connections are established. So, make sure to apply similar\n\t * restrictions. In this case, make sure that we get all the connections\n\t * established.\n\t */\n\tif (UseConnectionPerPlacement())\n\t{\n\t\trequiredActiveConnectionCount = initiatedConnectionCount;\n\t}\n\n\tif (MillisecondsBetweenTimestamps(poolStartTime, now) >= NodeConnectionTimeout)\n\t{\n\t\tif (activeConnectionCount < requiredActiveConnectionCount)\n\t\t{\n\t\t\tint logLevel = WARNING;\n\n\t\t\t/*\n\t\t\t * First fail the pool and create an opportunity to execute tasks\n\t\t\t * over other pools when tasks have more than one placement to execute.\n\t\t\t */\n\t\t\tWorkerPoolFailed(workerPool);\n\n\t\t\tif (workerPool->failureState == WORKER_POOL_FAILED_OVER_TO_LOCAL)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t *\n\t\t\t\t * When the pool is failed over to local execution, warning\n\t\t\t\t * the user just creates chatter as the executor is capable of\n\t\t\t\t * finishing the execution.\n\t\t\t\t */\n\t\t\t\tlogLevel = DEBUG1;\n\t\t\t}\n\t\t\telse if (execution->transactionProperties->errorOnAnyFailure ||\n\t\t\t\t\t execution->failed)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * The enforcement is not always erroring out. For example, if a SELECT task\n\t\t\t\t * has two different placements, we'd warn the user, fail the pool and continue\n\t\t\t\t * with the next placement.\n\t\t\t\t */\n\t\t\t\tlogLevel = ERROR;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * We hit the connection timeout. In that case, we should not let the\n\t\t\t * connection establishment to continue because the execution logic\n\t\t\t * pretends that failed sessions are not going to be used anymore.\n\t\t\t *\n\t\t\t * That's why we mark the connection as timed out to trigger the state\n\t\t\t * changes in the executor, if we don't throw an error below.\n\t\t\t */\n\t\t\tMarkEstablishingSessionsTimedOut(workerPool);\n\n\t\t\tereport(logLevel, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t\t   errmsg(\"could not establish any connections to the node \"\n\t\t\t\t\t\t\t\t\t  \"%s:%d after %u ms\", workerPool->nodeName,\n\t\t\t\t\t\t\t\t\t  workerPool->nodePort,\n\t\t\t\t\t\t\t\t\t  NodeConnectionTimeout)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* stop interrupting WaitEventSetWait for timeouts */\n\t\t\tworkerPool->checkForPoolTimeout = false;\n\t\t}\n\t}\n}\n\n\n/*\n * MarkEstablishingSessionsTimedOut goes over the sessions in the given\n * workerPool and marks them timed out. ConnectionStateMachine()\n * later cleans up the sessions.\n */\nstatic void\nMarkEstablishingSessionsTimedOut(WorkerPool *workerPool)\n{\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, workerPool->sessionList)\n\t{\n\t\tMultiConnection *connection = session->connection;\n\n\t\tif (connection->connectionState == MULTI_CONNECTION_CONNECTING ||\n\t\t\tconnection->connectionState == MULTI_CONNECTION_INITIAL)\n\t\t{\n\t\t\tconnection->connectionState = MULTI_CONNECTION_TIMED_OUT;\n\t\t\tIncrementStatCounterForMyDb(STAT_CONNECTION_ESTABLISHMENT_FAILED);\n\t\t}\n\t}\n}\n\n\n/*\n * UsableConnectionCount returns the number of connections in the worker pool\n * that are (soon to be) usable for sending commands, this includes both idle\n * connections and connections that are still establishing.\n */\nstatic int\nUsableConnectionCount(WorkerPool *workerPool)\n{\n\tint initiatedConnectionCount = list_length(workerPool->sessionList);\n\tint activeConnectionCount = workerPool->activeConnectionCount;\n\tint failedConnectionCount = workerPool->failedConnectionCount;\n\tint idleConnectionCount = workerPool->idleConnectionCount;\n\n\t/* connections that are still establishing will soon be available for tasks */\n\tint establishingConnectionCount =\n\t\tinitiatedConnectionCount - activeConnectionCount - failedConnectionCount;\n\n\tint usableConnectionCount = idleConnectionCount + establishingConnectionCount;\n\n\treturn usableConnectionCount;\n}\n\n\n/*\n * NextEventTimeout finds the earliest time at which we need to interrupt\n * WaitEventSetWait because of a timeout and returns the number of milliseconds\n * until that event with a minimum of 1ms and a maximum of 1000ms.\n *\n * This code may be sensitive to clock jumps, but only has the effect of waking\n * up WaitEventSetWait slightly earlier to later.\n */\nstatic long\nNextEventTimeout(DistributedExecution *execution)\n{\n\tinstr_time now;\n\tINSTR_TIME_SET_CURRENT(now);\n\tlong eventTimeout = 1000; /* milliseconds */\n\n\tWorkerPool *workerPool = NULL;\n\tforeach_declared_ptr(workerPool, execution->workerList)\n\t{\n\t\tif (workerPool->failureState == WORKER_POOL_FAILED)\n\t\t{\n\t\t\t/* worker pool may have already timed out */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!INSTR_TIME_IS_ZERO(workerPool->poolStartTime) &&\n\t\t\tworkerPool->checkForPoolTimeout)\n\t\t{\n\t\t\tlong timeSincePoolStartMs =\n\t\t\t\tMillisecondsBetweenTimestamps(workerPool->poolStartTime, now);\n\n\t\t\t/*\n\t\t\t * This could go into the negative if the connection timeout just passed.\n\t\t\t * In that case we want to wake up as soon as possible. Once the timeout\n\t\t\t * has been processed, checkForPoolTimeout will be false so we will skip\n\t\t\t * this check.\n\t\t\t */\n\t\t\tlong timeUntilConnectionTimeoutMs =\n\t\t\t\tNodeConnectionTimeout - timeSincePoolStartMs;\n\n\t\t\tif (timeUntilConnectionTimeoutMs < eventTimeout)\n\t\t\t{\n\t\t\t\teventTimeout = timeUntilConnectionTimeoutMs;\n\t\t\t}\n\t\t}\n\n\t\tint initiatedConnectionCount = list_length(workerPool->sessionList);\n\n\t\t/*\n\t\t * If there are connections to open we wait at most up to the end of the\n\t\t * current slow start interval.\n\t\t */\n\t\tif (workerPool->readyTaskCount > UsableConnectionCount(workerPool) &&\n\t\t\tinitiatedConnectionCount < execution->targetPoolSize)\n\t\t{\n\t\t\tlong timeSinceLastConnectMs =\n\t\t\t\tMillisecondsBetweenTimestamps(workerPool->lastConnectionOpenTime, now);\n\t\t\tlong timeUntilSlowStartInterval =\n\t\t\t\tExecutorSlowStartInterval - timeSinceLastConnectMs;\n\n\t\t\tif (timeUntilSlowStartInterval < eventTimeout)\n\t\t\t{\n\t\t\t\teventTimeout = timeUntilSlowStartInterval;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn Max(1, eventTimeout);\n}\n\n\n/*\n * MillisecondsBetweenTimestamps is a helper to get the number of milliseconds\n * between timestamps when it is expected to be small enough to fit in a\n * long.\n */\nstatic long\nMillisecondsBetweenTimestamps(instr_time startTime, instr_time endTime)\n{\n\tINSTR_TIME_SUBTRACT(endTime, startTime);\n\treturn INSTR_TIME_GET_MILLISEC(endTime);\n}\n\n\n/*\n * MicrosecondsBetweenTimestamps is a helper to get the number of microseconds\n * between timestamps.\n */\nstatic uint64\nMicrosecondsBetweenTimestamps(instr_time startTime, instr_time endTime)\n{\n\tINSTR_TIME_SUBTRACT(endTime, startTime);\n\treturn INSTR_TIME_GET_MICROSEC(endTime);\n}\n\n\n/*\n * ConnectionStateMachine opens a connection and descends into the transaction\n * state machine when ready.\n */\nstatic void\nConnectionStateMachine(WorkerSession *session)\n{\n\tWorkerPool *workerPool = session->workerPool;\n\tDistributedExecution *execution = workerPool->distributedExecution;\n\n\tMultiConnection *connection = session->connection;\n\tMultiConnectionState currentState;\n\n\tdo {\n\t\tcurrentState = connection->connectionState;\n\n\t\tswitch (currentState)\n\t\t{\n\t\t\tcase MULTI_CONNECTION_INITIAL:\n\t\t\t{\n\t\t\t\t/* simply iterate the state machine */\n\t\t\t\tconnection->connectionState = MULTI_CONNECTION_CONNECTING;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MULTI_CONNECTION_TIMED_OUT:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * When the connection timeout happens, the connection\n\t\t\t\t * might still be able to successfuly established. However,\n\t\t\t\t * the executor should not try to use this connection as\n\t\t\t\t * the state machines might have already progressed and used\n\t\t\t\t * new pools/sessions instead. That's why we terminate the\n\t\t\t\t * connection, clear any state associated with it.\n\t\t\t\t *\n\t\t\t\t * Note that here we don't increment the failed connection\n\t\t\t\t * stat counter because MarkEstablishingSessionsTimedOut()\n\t\t\t\t * already did that.\n\t\t\t\t */\n\t\t\t\tconnection->connectionState = MULTI_CONNECTION_FAILED;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MULTI_CONNECTION_CONNECTING:\n\t\t\t{\n\t\t\t\tConnStatusType status = PQstatus(connection->pgConn);\n\t\t\t\tif (status == CONNECTION_OK)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Connection was already established, possibly a cached\n\t\t\t\t\t * connection.\n\t\t\t\t\t */\n\t\t\t\t\tbool newConnection = false;\n\t\t\t\t\tHandleMultiConnectionSuccess(session, newConnection);\n\t\t\t\t\tUpdateConnectionWaitFlags(session,\n\t\t\t\t\t\t\t\t\t\t\t  WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\telse if (status == CONNECTION_BAD)\n\t\t\t\t{\n\t\t\t\t\tconnection->connectionState = MULTI_CONNECTION_FAILED;\n\t\t\t\t\tIncrementStatCounterForMyDb(STAT_CONNECTION_ESTABLISHMENT_FAILED);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tint beforePollSocket = PQsocket(connection->pgConn);\n\t\t\t\tPostgresPollingStatusType pollMode = PQconnectPoll(connection->pgConn);\n\n\t\t\t\tif (beforePollSocket != PQsocket(connection->pgConn))\n\t\t\t\t{\n\t\t\t\t\t/* rebuild the wait events if PQconnectPoll() changed the socket */\n\t\t\t\t\texecution->rebuildWaitEventSet = true;\n\t\t\t\t}\n\n\t\t\t\tif (pollMode == PGRES_POLLING_FAILED)\n\t\t\t\t{\n\t\t\t\t\tconnection->connectionState = MULTI_CONNECTION_FAILED;\n\t\t\t\t\tIncrementStatCounterForMyDb(STAT_CONNECTION_ESTABLISHMENT_FAILED);\n\t\t\t\t}\n\t\t\t\telse if (pollMode == PGRES_POLLING_READING)\n\t\t\t\t{\n\t\t\t\t\tUpdateConnectionWaitFlags(session, WL_SOCKET_READABLE);\n\n\t\t\t\t\t/* we should have a valid socket */\n\t\t\t\t\tAssert(PQsocket(connection->pgConn) != -1);\n\t\t\t\t}\n\t\t\t\telse if (pollMode == PGRES_POLLING_WRITING)\n\t\t\t\t{\n\t\t\t\t\tUpdateConnectionWaitFlags(session, WL_SOCKET_WRITEABLE);\n\n\t\t\t\t\t/* we should have a valid socket */\n\t\t\t\t\tAssert(PQsocket(connection->pgConn) != -1);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Connection was not established befoore (!= CONNECTION_OK)\n\t\t\t\t\t * but PQconnectPoll() did so now.\n\t\t\t\t\t */\n\t\t\t\t\tbool newConnection = true;\n\t\t\t\t\tHandleMultiConnectionSuccess(session, newConnection);\n\t\t\t\t\tUpdateConnectionWaitFlags(session,\n\t\t\t\t\t\t\t\t\t\t\t  WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE);\n\n\t\t\t\t\t/* we should have a valid socket */\n\t\t\t\t\tAssert(PQsocket(connection->pgConn) != -1);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MULTI_CONNECTION_CONNECTED:\n\t\t\t{\n\t\t\t\tif (HasUnfinishedTaskForSession(session))\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Connection is ready, and we have unfinished tasks.\n\t\t\t\t\t * So, run the transaction state machine.\n\t\t\t\t\t */\n\t\t\t\t\tTransactionStateMachine(session);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Connection is ready, but we don't have any unfinished\n\t\t\t\t\t * tasks that this session can execute.\n\t\t\t\t\t *\n\t\t\t\t\t * Note that we can be in a situation where the executor\n\t\t\t\t\t * decides to establish a connection, but not need to\n\t\t\t\t\t * use it at the time the connection is established. This could\n\t\t\t\t\t * happen when the earlier connections manages to finish all the\n\t\t\t\t\t * tasks after this connection\n\t\t\t\t\t *\n\t\t\t\t\t * As no tasks are ready to be executed at the moment, we\n\t\t\t\t\t * mark the socket readable to get any notices if exists.\n\t\t\t\t\t */\n\t\t\t\t\tUpdateConnectionWaitFlags(session, WL_SOCKET_READABLE);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MULTI_CONNECTION_LOST:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * If a connection is lost, we retry the connection for some\n\t\t\t\t * very restricted scenarios. The main use case is to retry\n\t\t\t\t * connection establishment when a cached connection is used\n\t\t\t\t * in the executor while remote server has restarted / failedover\n\t\t\t\t * etc.\n\t\t\t\t *\n\t\t\t\t * For simplicity, we only allow retrying connection establishment\n\t\t\t\t * a single time.\n\t\t\t\t *\n\t\t\t\t * We can only retry connection when the remote transaction has\n\t\t\t\t * not started over the connection. Otherwise, we'd have to deal\n\t\t\t\t * with restoring the transaction state, which is beyond our\n\t\t\t\t * purpose at this time.\n\t\t\t\t */\n\t\t\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\t\t\t\tif (!session->connectionRetried &&\n\t\t\t\t\ttransaction->transactionState == REMOTE_TRANS_NOT_STARTED)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Try to connect again, we will reuse the same MultiConnection\n\t\t\t\t\t * and keep it as claimed.\n\t\t\t\t\t */\n\t\t\t\t\tRestartConnection(connection);\n\n\t\t\t\t\t/* socket have changed */\n\t\t\t\t\texecution->rebuildWaitEventSet = true;\n\t\t\t\t\tsession->latestUnconsumedWaitEvents = 0;\n\n\t\t\t\t\tsession->connectionRetried = true;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * Here we don't increment the connection stat counter for failed\n\t\t\t\t * connections because we don't track the connections that we could\n\t\t\t\t * establish but lost later.\n\t\t\t\t */\n\t\t\t\tconnection->connectionState = MULTI_CONNECTION_FAILED;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MULTI_CONNECTION_FAILED:\n\t\t\t{\n\t\t\t\t/* managed to connect, but connection was lost */\n\t\t\t\tif (session->sessionHasActiveConnection)\n\t\t\t\t{\n\t\t\t\t\tworkerPool->activeConnectionCount--;\n\n\t\t\t\t\tif (session->currentTask == NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* this was an idle connection */\n\t\t\t\t\t\tworkerPool->idleConnectionCount--;\n\t\t\t\t\t}\n\n\t\t\t\t\tsession->sessionHasActiveConnection = false;\n\t\t\t\t}\n\n\t\t\t\t/* connection failed or was lost */\n\t\t\t\tint totalConnectionCount = list_length(workerPool->sessionList);\n\n\t\t\t\tworkerPool->failedConnectionCount++;\n\n\t\t\t\t/* if the connection executed a critical command it should fail */\n\t\t\t\tMarkRemoteTransactionFailed(connection, false);\n\n\t\t\t\t/* mark all assigned placement executions as failed */\n\t\t\t\tWorkerSessionFailed(session);\n\n\t\t\t\tif (workerPool->failedConnectionCount >= totalConnectionCount)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * All current connection attempts have failed.\n\t\t\t\t\t * Mark all unassigned placement executions as failed.\n\t\t\t\t\t *\n\t\t\t\t\t * We do not currently retry if the first connection\n\t\t\t\t\t * attempt fails.\n\t\t\t\t\t */\n\t\t\t\t\tWorkerPoolFailed(workerPool);\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * The execution may have failed as a result of WorkerSessionFailed\n\t\t\t\t * or WorkerPoolFailed.\n\t\t\t\t *\n\t\t\t\t * Even if this execution has not failed -- but just a single session is\n\t\t\t\t * failed -- and an earlier execution in this transaction which marked\n\t\t\t\t * the remote transaction as critical, we should fail right away as the\n\t\t\t\t * transaction will fail anyway on PREPARE/COMMIT time.\n\t\t\t\t */\n\t\t\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t\t\t\tif (transaction->transactionCritical ||\n\t\t\t\t\texecution->failed ||\n\t\t\t\t\t(execution->transactionProperties->errorOnAnyFailure &&\n\t\t\t\t\t workerPool->failureState != WORKER_POOL_FAILED_OVER_TO_LOCAL))\n\t\t\t\t{\n\t\t\t\t\t/* a task has failed due to this connection failure */\n\t\t\t\t\tReportConnectionError(connection, ERROR);\n\t\t\t\t}\n\t\t\t\telse if (workerPool->activeConnectionCount > 0 ||\n\t\t\t\t\t\t workerPool->failureState == WORKER_POOL_FAILED_OVER_TO_LOCAL)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * We already have active connection(s) to the node, and the\n\t\t\t\t\t * executor is capable of using those connections to successfully\n\t\t\t\t\t * finish the execution. So, there is not much value in warning\n\t\t\t\t\t * the user.\n\t\t\t\t\t *\n\t\t\t\t\t * Similarly when the pool is failed over to local execution, warning\n\t\t\t\t\t * the user just creates chatter.\n\t\t\t\t\t */\n\t\t\t\t\tReportConnectionError(connection, DEBUG1);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tReportConnectionError(connection, WARNING);\n\t\t\t\t}\n\n\t\t\t\t/* remove the connection */\n\t\t\t\tUnclaimConnection(connection);\n\n\t\t\t\t/*\n\t\t\t\t * We forcefully close the underlying libpq connection because\n\t\t\t\t * we don't want any subsequent execution (either subPlan executions\n\t\t\t\t * or new command executions within a transaction block) use the\n\t\t\t\t * connection.\n\t\t\t\t *\n\t\t\t\t * However, we prefer to keep the MultiConnection around until\n\t\t\t\t * the end of FinishDistributedExecution() to simplify the code.\n\t\t\t\t * Thus, we prefer ShutdownConnection() over CloseConnection().\n\t\t\t\t */\n\t\t\t\tShutdownConnection(connection);\n\n\t\t\t\t/* remove connection from wait event set */\n\t\t\t\texecution->rebuildWaitEventSet = true;\n\n\t\t\t\t/*\n\t\t\t\t * Reset the transaction state machine since CloseConnection()\n\t\t\t\t * relies on it and even if we're not inside a distributed transaction\n\t\t\t\t * we set the transaction state (e.g., REMOTE_TRANS_SENT_COMMAND).\n\t\t\t\t */\n\t\t\t\tif (!connection->remoteTransaction.beginSent)\n\t\t\t\t{\n\t\t\t\t\tconnection->remoteTransaction.transactionState =\n\t\t\t\t\t\tREMOTE_TRANS_NOT_STARTED;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} while (connection->connectionState != currentState);\n}\n\n\n/*\n * HasUnfinishedTaskForSession gets a session and returns true if there\n * are any tasks that this session can execute.\n */\nstatic bool\nHasUnfinishedTaskForSession(WorkerSession *session)\n{\n\tif (session->currentTask != NULL)\n\t{\n\t\t/* the session is executing a command right now */\n\t\treturn true;\n\t}\n\n\tdlist_head *sessionReadyTaskQueue = &(session->readyTaskQueue);\n\tif (!dlist_is_empty(sessionReadyTaskQueue))\n\t{\n\t\t/* session has an assigned task, which is ready for execution */\n\t\treturn true;\n\t}\n\n\tWorkerPool *workerPool = session->workerPool;\n\tdlist_head *poolReadyTaskQueue = &(workerPool->readyTaskQueue);\n\tif (!dlist_is_empty(poolReadyTaskQueue))\n\t{\n\t\t/*\n\t\t * Pool has unassigned tasks that can be executed\n\t\t * by the input session.\n\t\t */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * HandleMultiConnectionSuccess logs the established connection and updates\n * connection's state.\n */\nstatic void\nHandleMultiConnectionSuccess(WorkerSession *session, bool newConnection)\n{\n\tMultiConnection *connection = session->connection;\n\tWorkerPool *workerPool = session->workerPool;\n\n\tMarkConnectionConnected(connection, newConnection);\n\n\tereport(DEBUG4, (errmsg(\"established connection to %s:%d for \"\n\t\t\t\t\t\t\t\"session %ld in %ld microseconds\",\n\t\t\t\t\t\t\tconnection->hostname, connection->port,\n\t\t\t\t\t\t\tsession->sessionId,\n\t\t\t\t\t\t\tMicrosecondsBetweenTimestamps(\n\t\t\t\t\t\t\t\tconnection->connectionEstablishmentStart,\n\t\t\t\t\t\t\t\tconnection->connectionEstablishmentEnd))));\n\n\tworkerPool->activeConnectionCount++;\n\tworkerPool->idleConnectionCount++;\n\tsession->sessionHasActiveConnection = true;\n}\n\n\n/*\n * Activate2PCIfModifyingTransactionExpandsToNewNode sets the coordinated\n * transaction to use 2PC under the following circumstances:\n *     - We're already in a transaction block\n *     - At least one of the previous commands in the transaction block\n *       made a modification, which have not set 2PC itself because it\n *       was a single shard command\n *     - The input \"session\" is used for a distributed execution which\n *       modifies the database. However, the session (and hence the\n *       connection) is established to a different worker than the ones\n *       that is used previously in the transaction.\n *\n *  To give an example,\n *      BEGIN;\n *          -- assume that the following INSERT goes to worker-A\n *          -- also note that this single command does not activate\n *          -- 2PC itself since it is a single shard modification\n *          INSERT INTO distributed_table (dist_key) VALUES (1);\n *\n *          -- do one more single shard UPDATE hitting the same\n *          shard (or worker node in general)\n *          -- this wouldn't activate 2PC, since we're operating on the\n *          -- same worker node that we've modified earlier\n *          -- so the executor would use the same connection\n *\t\t\tUPDATE distributed_table SET value = 10 WHERE dist_key = 1;\n *\n *          -- now, do one more INSERT, which goes to worker-B\n *          -- At this point, this function would activate 2PC\n *          -- since we're now expanding to a new node\n *          -- for example, if this command were a SELECT, we wouldn't\n *          -- activate 2PC since we're only interested in modifications/DDLs\n *          INSERT INTO distributed_table (dist_key) VALUES (2);\n */\nstatic void\nActivate2PCIfModifyingTransactionExpandsToNewNode(WorkerSession *session)\n{\n\tDistributedExecution *execution = session->workerPool->distributedExecution;\n\tif (TransactionModifiedDistributedTable(execution) &&\n\t\tDistributedExecutionModifiesDatabase(execution) &&\n\t\t!ConnectionModifiedPlacement(session->connection))\n\t{\n\t\t/*\n\t\t * We already did a modification, but not on the connection that we\n\t\t * just opened, which means we're now going to make modifications\n\t\t * over multiple connections. Activate 2PC!\n\t\t */\n\t\tUse2PCForCoordinatedTransaction();\n\t}\n}\n\n\n/*\n * TransactionModifiedDistributedTable returns true if the current transaction already\n * executed a command which modified at least one distributed table in the current\n * transaction.\n */\nstatic bool\nTransactionModifiedDistributedTable(DistributedExecution *execution)\n{\n\t/*\n\t * We need to explicitly check for TRANSACTION_BLOCKS_REQUIRED due to\n\t * citus.function_opens_transaction_block flag. When set to false, we\n\t * should not be pretending that we're in a coordinated transaction even\n\t * if XACT_MODIFICATION_DATA is set. That's why we implemented this workaround.\n\t */\n\treturn execution->transactionProperties->useRemoteTransactionBlocks ==\n\t\t   TRANSACTION_BLOCKS_REQUIRED &&\n\t\t   XactModificationLevel == XACT_MODIFICATION_DATA;\n}\n\n\n/*\n * TransactionStateMachine manages the execution of tasks over a connection.\n */\nstatic void\nTransactionStateMachine(WorkerSession *session)\n{\n\tWorkerPool *workerPool = session->workerPool;\n\tDistributedExecution *execution = workerPool->distributedExecution;\n\tTransactionBlocksUsage useRemoteTransactionBlocks =\n\t\texecution->transactionProperties->useRemoteTransactionBlocks;\n\n\tMultiConnection *connection = session->connection;\n\tRemoteTransaction *transaction = &(connection->remoteTransaction);\n\tRemoteTransactionState currentState;\n\n\tdo {\n\t\tcurrentState = transaction->transactionState;\n\n\t\tif (!CheckConnectionReady(session))\n\t\t{\n\t\t\t/* connection is busy, no state transitions to make */\n\t\t\tbreak;\n\t\t}\n\n\t\tswitch (currentState)\n\t\t{\n\t\t\tcase REMOTE_TRANS_NOT_STARTED:\n\t\t\t{\n\t\t\t\tif (useRemoteTransactionBlocks == TRANSACTION_BLOCKS_REQUIRED)\n\t\t\t\t{\n\t\t\t\t\t/* if we're expanding the nodes in a transaction, use 2PC */\n\t\t\t\t\tActivate2PCIfModifyingTransactionExpandsToNewNode(session);\n\n\t\t\t\t\t/* need to open a transaction block first */\n\t\t\t\t\tStartRemoteTransactionBegin(connection);\n\n\t\t\t\t\ttransaction->transactionState = REMOTE_TRANS_CLEARING_RESULTS;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tTaskPlacementExecution *placementExecution = PopPlacementExecution(\n\t\t\t\t\t\tsession);\n\t\t\t\t\tif (placementExecution == NULL)\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * No tasks are ready to be executed at the moment. But we\n\t\t\t\t\t\t * still mark the socket readable to get any notices if exists.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tUpdateConnectionWaitFlags(session, WL_SOCKET_READABLE);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tbool placementExecutionStarted =\n\t\t\t\t\t\tStartPlacementExecutionOnSession(placementExecution, session);\n\t\t\t\t\tif (!placementExecutionStarted)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* no need to continue, connection is lost */\n\t\t\t\t\t\tAssert(session->connection->connectionState ==\n\t\t\t\t\t\t\t   MULTI_CONNECTION_LOST);\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\ttransaction->transactionState = REMOTE_TRANS_SENT_COMMAND;\n\t\t\t\t}\n\n\t\t\t\tUpdateConnectionWaitFlags(session,\n\t\t\t\t\t\t\t\t\t\t  WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase REMOTE_TRANS_SENT_BEGIN:\n\t\t\tcase REMOTE_TRANS_CLEARING_RESULTS:\n\t\t\t{\n\t\t\t\tPGresult *result = PQgetResult(connection->pgConn);\n\t\t\t\tif (result != NULL)\n\t\t\t\t{\n\t\t\t\t\tif (!IsResponseOK(result))\n\t\t\t\t\t{\n\t\t\t\t\t\t/* query failures are always hard errors */\n\t\t\t\t\t\tReportResultError(connection, result, ERROR);\n\t\t\t\t\t}\n\n\t\t\t\t\tPQclear(result);\n\n\t\t\t\t\t/* wake up WaitEventSetWait */\n\t\t\t\t\tUpdateConnectionWaitFlags(session,\n\t\t\t\t\t\t\t\t\t\t\t  WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE);\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (session->currentTask != NULL)\n\t\t\t\t{\n\t\t\t\t\tTaskPlacementExecution *placementExecution = session->currentTask;\n\t\t\t\t\tbool succeeded = true;\n\n\t\t\t\t\t/*\n\t\t\t\t\t * Once we finished a task on a connection, we no longer\n\t\t\t\t\t * allow that connection to fail.\n\t\t\t\t\t */\n\t\t\t\t\tMarkRemoteTransactionCritical(connection);\n\n\t\t\t\t\tsession->currentTask = NULL;\n\n\t\t\t\t\tPlacementExecutionDone(placementExecution, succeeded);\n\n\t\t\t\t\t/* connection is ready to use for executing commands */\n\t\t\t\t\tworkerPool->idleConnectionCount++;\n\t\t\t\t}\n\n\t\t\t\t/* connection needs to be writeable to send next command */\n\t\t\t\tUpdateConnectionWaitFlags(session,\n\t\t\t\t\t\t\t\t\t\t  WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE);\n\n\t\t\t\tif (transaction->beginSent)\n\t\t\t\t{\n\t\t\t\t\ttransaction->transactionState = REMOTE_TRANS_STARTED;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttransaction->transactionState = REMOTE_TRANS_NOT_STARTED;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase REMOTE_TRANS_STARTED:\n\t\t\t{\n\t\t\t\tTaskPlacementExecution *placementExecution = PopPlacementExecution(\n\t\t\t\t\tsession);\n\t\t\t\tif (placementExecution == NULL)\n\t\t\t\t{\n\t\t\t\t\t/* no tasks are ready to be executed at the moment */\n\t\t\t\t\tUpdateConnectionWaitFlags(session, WL_SOCKET_READABLE);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tbool placementExecutionStarted =\n\t\t\t\t\tStartPlacementExecutionOnSession(placementExecution, session);\n\t\t\t\tif (!placementExecutionStarted)\n\t\t\t\t{\n\t\t\t\t\t/* no need to continue, connection is lost */\n\t\t\t\t\tAssert(session->connection->connectionState == MULTI_CONNECTION_LOST);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\ttransaction->transactionState = REMOTE_TRANS_SENT_COMMAND;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase REMOTE_TRANS_SENT_COMMAND:\n\t\t\t{\n\t\t\t\tTaskPlacementExecution *placementExecution = session->currentTask;\n\t\t\t\tif (placementExecution == NULL)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * We have seen accounts in production where the placementExecution\n\t\t\t\t\t * could inadvertently be not set. Investigation documented on\n\t\t\t\t\t * https://github.com/citusdata/citus-enterprise/issues/493\n\t\t\t\t\t * (due to sensitive data in the initial report it is not discussed\n\t\t\t\t\t * in our community repository)\n\t\t\t\t\t *\n\t\t\t\t\t * Currently we don't have a reliable way of reproducing this issue.\n\t\t\t\t\t * Erroring here seems to be a more desirable approach compared to a\n\t\t\t\t\t * SEGFAULT on the dereference of placementExecution, with a possible\n\t\t\t\t\t * crash recovery as a result.\n\t\t\t\t\t */\n\t\t\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\t\t\"unable to recover from inconsistent state in \"\n\t\t\t\t\t\t\t\t\t\t\"the connection state machine on coordinator\")));\n\t\t\t\t}\n\n\t\t\t\tShardCommandExecution *shardCommandExecution =\n\t\t\t\t\tplacementExecution->shardCommandExecution;\n\t\t\t\tTask *task = shardCommandExecution->task;\n\n\t\t\t\t/*\n\t\t\t\t * In EXPLAIN ANALYZE we need to store results except for multiple placements,\n\t\t\t\t * regardless of query type. In other cases, doing the same doesn't seem to have\n\t\t\t\t * a drawback.\n\t\t\t\t */\n\t\t\t\tbool storeRows = true;\n\n\t\t\t\tif (shardCommandExecution->gotResults)\n\t\t\t\t{\n\t\t\t\t\t/* already received results from another replica */\n\t\t\t\t\tstoreRows = false;\n\t\t\t\t}\n\t\t\t\telse if (task->partiallyLocalOrRemote)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * For the tasks that involves placements from both\n\t\t\t\t\t * remote and local placments, such as modifications\n\t\t\t\t\t * to reference tables, we store the rows during the\n\t\t\t\t\t * local placement/execution.\n\t\t\t\t\t */\n\t\t\t\t\tstoreRows = false;\n\t\t\t\t}\n\n\t\t\t\tbool fetchDone = ReceiveResults(session, storeRows);\n\t\t\t\tif (!fetchDone)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t/* if this is a multi-query task, send the next query */\n\t\t\t\tif (placementExecution->queryIndex < task->queryCount)\n\t\t\t\t{\n\t\t\t\t\tbool querySent = SendNextQuery(placementExecution, session);\n\t\t\t\t\tif (!querySent)\n\t\t\t\t\t{\n\t\t\t\t\t\t/* no need to continue, connection is lost */\n\t\t\t\t\t\tAssert(session->connection->connectionState ==\n\t\t\t\t\t\t\t   MULTI_CONNECTION_LOST);\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t * At this point the query might be just in pgconn buffers. We\n\t\t\t\t\t * need to wait until it becomes writeable to actually send\n\t\t\t\t\t * the query.\n\t\t\t\t\t */\n\t\t\t\t\tUpdateConnectionWaitFlags(session,\n\t\t\t\t\t\t\t\t\t\t\t  WL_SOCKET_WRITEABLE | WL_SOCKET_READABLE);\n\n\t\t\t\t\ttransaction->transactionState = REMOTE_TRANS_SENT_COMMAND;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tshardCommandExecution->gotResults = true;\n\t\t\t\ttransaction->transactionState = REMOTE_TRANS_CLEARING_RESULTS;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t/* iterate in case we can perform multiple transitions at once */\n\twhile (transaction->transactionState != currentState);\n}\n\n\n/*\n * UpdateConnectionWaitFlags is a wrapper around setting waitFlags of the connection.\n *\n * This function might further improved in a sense that to use use ModifyWaitEvent on\n * waitFlag changes as opposed to what we do now: always rebuild the wait event sets.\n * Our initial benchmarks didn't show any significant performance improvements, but\n * good to keep in mind the potential improvements.\n */\nstatic void\nUpdateConnectionWaitFlags(WorkerSession *session, int waitFlags)\n{\n\tMultiConnection *connection = session->connection;\n\tDistributedExecution *execution = session->workerPool->distributedExecution;\n\n\t/* do not take any actions if the flags not changed */\n\tif (connection->waitFlags == waitFlags)\n\t{\n\t\treturn;\n\t}\n\n\t/* always detect closed sockets */\n\tconnection->waitFlags = waitFlags | WL_SOCKET_CLOSED;\n\n\t/* without signalling the execution, the flag changes won't be reflected */\n\texecution->waitFlagsChanged = true;\n}\n\n\n/*\n * CheckConnectionReady returns true if the connection is ready to\n * read or write, or false if it still has bytes to send/receive.\n */\nstatic bool\nCheckConnectionReady(WorkerSession *session)\n{\n\tMultiConnection *connection = session->connection;\n\tint waitFlags = WL_SOCKET_READABLE;\n\tbool connectionReady = false;\n\n\tConnStatusType status = PQstatus(connection->pgConn);\n\tif (status == CONNECTION_BAD)\n\t{\n\t\tconnection->connectionState = MULTI_CONNECTION_LOST;\n\t\treturn false;\n\t}\n\n\tif ((session->latestUnconsumedWaitEvents & WL_SOCKET_CLOSED) != 0)\n\t{\n\t\tconnection->connectionState = MULTI_CONNECTION_LOST;\n\t\treturn false;\n\t}\n\n\t/* try to send all pending data */\n\tint sendStatus = PQflush(connection->pgConn);\n\tif (sendStatus == -1)\n\t{\n\t\tconnection->connectionState = MULTI_CONNECTION_LOST;\n\t\treturn false;\n\t}\n\telse if (sendStatus == 1)\n\t{\n\t\t/* more data to send, wait for socket to become writable */\n\t\twaitFlags = waitFlags | WL_SOCKET_WRITEABLE;\n\t}\n\n\tif ((session->latestUnconsumedWaitEvents & WL_SOCKET_READABLE) != 0)\n\t{\n\t\tif (PQconsumeInput(connection->pgConn) == 0)\n\t\t{\n\t\t\tconnection->connectionState = MULTI_CONNECTION_LOST;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (!PQisBusy(connection->pgConn))\n\t{\n\t\tconnectionReady = true;\n\t}\n\n\tUpdateConnectionWaitFlags(session, waitFlags);\n\n\t/* don't consume input redundantly if we cycle back into CheckConnectionReady */\n\tsession->latestUnconsumedWaitEvents = 0;\n\n\treturn connectionReady;\n}\n\n\n/*\n * PopPlacementExecution returns the next available assigned or unassigned\n * placement execution for the given session.\n */\nstatic TaskPlacementExecution *\nPopPlacementExecution(WorkerSession *session)\n{\n\tWorkerPool *workerPool = session->workerPool;\n\n\tTaskPlacementExecution *placementExecution = PopAssignedPlacementExecution(session);\n\tif (placementExecution == NULL)\n\t{\n\t\tif (session->commandsSent > 0 && UseConnectionPerPlacement())\n\t\t{\n\t\t\t/*\n\t\t\t * Only send one command per connection if force_max_query_parallelisation\n\t\t\t * is enabled, unless it's an assigned placement execution.\n\t\t\t */\n\t\t\treturn NULL;\n\t\t}\n\n\t\t/* no more assigned tasks, pick an unassigned task */\n\t\tplacementExecution = PopUnassignedPlacementExecution(workerPool);\n\t}\n\n\treturn placementExecution;\n}\n\n\n/*\n * PopAssignedPlacementExecution finds an executable task from the queue of assigned tasks.\n */\nstatic TaskPlacementExecution *\nPopAssignedPlacementExecution(WorkerSession *session)\n{\n\tdlist_head *readyTaskQueue = &(session->readyTaskQueue);\n\n\tif (dlist_is_empty(readyTaskQueue))\n\t{\n\t\treturn NULL;\n\t}\n\n\tTaskPlacementExecution *placementExecution = dlist_container(TaskPlacementExecution,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t sessionReadyQueueNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t dlist_pop_head_node(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t readyTaskQueue));\n\n\treturn placementExecution;\n}\n\n\n/*\n * PopUnAssignedPlacementExecution finds an executable task from the queue of unassigned tasks.\n */\nstatic TaskPlacementExecution *\nPopUnassignedPlacementExecution(WorkerPool *workerPool)\n{\n\tdlist_head *readyTaskQueue = &(workerPool->readyTaskQueue);\n\n\tif (dlist_is_empty(readyTaskQueue))\n\t{\n\t\treturn NULL;\n\t}\n\n\tTaskPlacementExecution *placementExecution = dlist_container(TaskPlacementExecution,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t workerReadyQueueNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t dlist_pop_head_node(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t readyTaskQueue));\n\n\tworkerPool->readyTaskCount--;\n\n\treturn placementExecution;\n}\n\n\n/*\n * StartPlacementExecutionOnSession gets a TaskPlacementExecution and\n * WorkerSession, the task's query is sent to the worker via the session.\n *\n * The function does some bookkeeping such as associating the placement\n * accesses with the connection and updating session's local variables. For\n * details read the comments in the function.\n *\n * The function returns true if the query is successfully sent over the\n * connection, otherwise false.\n */\nstatic bool\nStartPlacementExecutionOnSession(TaskPlacementExecution *placementExecution,\n\t\t\t\t\t\t\t\t WorkerSession *session)\n{\n\tWorkerPool *workerPool = session->workerPool;\n\tDistributedExecution *execution = workerPool->distributedExecution;\n\tMultiConnection *connection = session->connection;\n\tShardCommandExecution *shardCommandExecution =\n\t\tplacementExecution->shardCommandExecution;\n\tTask *task = shardCommandExecution->task;\n\tShardPlacement *taskPlacement = placementExecution->shardPlacement;\n\tList *placementAccessList = PlacementAccessListForTask(task, taskPlacement);\n\n\tif (execution->transactionProperties->useRemoteTransactionBlocks !=\n\t\tTRANSACTION_BLOCKS_DISALLOWED)\n\t{\n\t\t/*\n\t\t * Make sure that subsequent commands on the same placement\n\t\t * use the same connection.\n\t\t */\n\t\tAssignPlacementListToConnection(placementAccessList, connection);\n\t}\n\n\tif (session->commandsSent == 0)\n\t{\n\t\t/* first time we send a command, consider the connection used (not unused) */\n\t\tworkerPool->unusedConnectionCount--;\n\t}\n\n\t/* connection is going to be in use */\n\tworkerPool->idleConnectionCount--;\n\tsession->currentTask = placementExecution;\n\tplacementExecution->executionState = PLACEMENT_EXECUTION_RUNNING;\n\n\tAssert(INSTR_TIME_IS_ZERO(placementExecution->startTime));\n\n\t/*\n\t * The same TaskPlacementExecution can be used to have\n\t * call SendNextQuery() several times if queryIndex is\n\t * non-zero. Still, all are executed under the current\n\t * placementExecution, so we can start the timer right\n\t * now.\n\t */\n\tINSTR_TIME_SET_CURRENT(placementExecution->startTime);\n\n\tbool querySent = SendNextQuery(placementExecution, session);\n\tif (querySent)\n\t{\n\t\tsession->commandsSent++;\n\n\t\tif (workerPool->poolToLocalNode)\n\t\t{\n\t\t\t/*\n\t\t\t * As we started remote execution to the local node,\n\t\t\t * we cannot switch back to local execution as that\n\t\t\t * would cause self-deadlocks and breaking\n\t\t\t * read-your-own-writes consistency.\n\t\t\t */\n\t\t\tSetLocalExecutionStatus(LOCAL_EXECUTION_DISABLED);\n\t\t}\n\t}\n\n\treturn querySent;\n}\n\n\n/*\n * SendNextQuery sends the next query for placementExecution on the given\n * session.\n */\nstatic bool\nSendNextQuery(TaskPlacementExecution *placementExecution,\n\t\t\t  WorkerSession *session)\n{\n\tWorkerPool *workerPool = session->workerPool;\n\tDistributedExecution *execution = workerPool->distributedExecution;\n\tMultiConnection *connection = session->connection;\n\tShardCommandExecution *shardCommandExecution =\n\t\tplacementExecution->shardCommandExecution;\n\tbool binaryResults = shardCommandExecution->binaryResults;\n\tTask *task = shardCommandExecution->task;\n\tParamListInfo paramListInfo = execution->paramListInfo;\n\tint querySent = 0;\n\tuint32 queryIndex = placementExecution->queryIndex;\n\n\tAssert(queryIndex < task->queryCount);\n\tchar *queryString = TaskQueryStringAtIndex(task, queryIndex);\n\n\tif (paramListInfo != NULL && !task->parametersInQueryStringResolved)\n\t{\n\t\tint parameterCount = paramListInfo->numParams;\n\t\tOid *parameterTypes = NULL;\n\t\tconst char **parameterValues = NULL;\n\n\t\t/* force evaluation of bound params */\n\t\tparamListInfo = copyParamList(paramListInfo);\n\n\t\tExtractParametersForRemoteExecution(paramListInfo, &parameterTypes,\n\t\t\t\t\t\t\t\t\t\t\t&parameterValues);\n\t\tquerySent = SendRemoteCommandParams(connection, queryString, parameterCount,\n\t\t\t\t\t\t\t\t\t\t\tparameterTypes, parameterValues,\n\t\t\t\t\t\t\t\t\t\t\tbinaryResults);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * We only need to use SendRemoteCommandParams when we desire\n\t\t * binaryResults. One downside of SendRemoteCommandParams is that it\n\t\t * only supports one query in the query string. In some cases we have\n\t\t * more than one query. In those cases we already make sure before that\n\t\t * binaryResults is false.\n\t\t *\n\t\t * XXX: It also seems that SendRemoteCommandParams does something\n\t\t * strange/incorrectly with select statements. In\n\t\t * isolation_select_vs_all.spec, when doing an s1-router-select in one\n\t\t * session blocked an s2-ddl-create-index-concurrently in another.\n\t\t */\n\t\tif (!binaryResults)\n\t\t{\n\t\t\tquerySent = SendRemoteCommand(connection, queryString);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tquerySent = SendRemoteCommandParams(connection, queryString, 0, NULL, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\tbinaryResults);\n\t\t}\n\t}\n\n\tif (querySent == 0)\n\t{\n\t\tconnection->connectionState = MULTI_CONNECTION_LOST;\n\t\treturn false;\n\t}\n\n\tint singleRowMode = PQsetSingleRowMode(connection->pgConn);\n\tif (singleRowMode == 0)\n\t{\n\t\tconnection->connectionState = MULTI_CONNECTION_LOST;\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ReceiveResults reads the result of a command or query and writes returned\n * rows to the tuple store of the scan state. It returns whether fetching results\n * were done. On failure, it throws an error.\n */\nstatic bool\nReceiveResults(WorkerSession *session, bool storeRows)\n{\n\tbool fetchDone = false;\n\tMultiConnection *connection = session->connection;\n\tWorkerPool *workerPool = session->workerPool;\n\tDistributedExecution *execution = workerPool->distributedExecution;\n\tTaskPlacementExecution *placementExecution = session->currentTask;\n\tShardCommandExecution *shardCommandExecution =\n\t\tplacementExecution->shardCommandExecution;\n\tTask *task = placementExecution->shardCommandExecution->task;\n\tTupleDestination *tupleDest = task->tupleDest ?\n\t\t\t\t\t\t\t\t  task->tupleDest :\n\t\t\t\t\t\t\t\t  execution->defaultTupleDest;\n\n\t/*\n\t * We use this context while converting each row fetched from remote node\n\t * into tuple. The context is reseted on every row, thus we create it at the\n\t * start of the loop and reset on every iteration.\n\t */\n\tMemoryContext rowContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"RowContext\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_MINSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_INITSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_MAXSIZE);\n\n\twhile (!PQisBusy(connection->pgConn))\n\t{\n\t\tuint32 columnIndex = 0;\n\t\tuint32 rowsProcessed = 0;\n\n\t\tPGresult *result = PQgetResult(connection->pgConn);\n\t\tif (result == NULL)\n\t\t{\n\t\t\t/* no more results, break out of loop and free allocated memory */\n\t\t\tfetchDone = true;\n\t\t\tbreak;\n\t\t}\n\n\t\tExecStatusType resultStatus = PQresultStatus(result);\n\t\tif (resultStatus == PGRES_COMMAND_OK)\n\t\t{\n\t\t\tchar *currentAffectedTupleString = PQcmdTuples(result);\n\t\t\tint64 currentAffectedTupleCount = 0;\n\n\t\t\t/* if there are multiple replicas, make sure to consider only one */\n\t\t\tif (storeRows && *currentAffectedTupleString != '\\0')\n\t\t\t{\n\t\t\t\tcurrentAffectedTupleCount = pg_strtoint64(currentAffectedTupleString);\n\t\t\t\tAssert(currentAffectedTupleCount >= 0);\n\t\t\t\texecution->rowsProcessed += currentAffectedTupleCount;\n\t\t\t}\n\n\t\t\tPQclear(result);\n\n\t\t\t/* task query might contain multiple queries, so fetch until we reach NULL */\n\t\t\tplacementExecution->queryIndex++;\n\t\t\tcontinue;\n\t\t}\n\t\telse if (resultStatus == PGRES_TUPLES_OK)\n\t\t{\n\t\t\t/*\n\t\t\t * We've already consumed all the tuples, no more results. Break out\n\t\t\t * of loop and free allocated memory before returning.\n\t\t\t */\n\t\t\tAssert(PQntuples(result) == 0);\n\t\t\tPQclear(result);\n\n\t\t\t/* task query might contain multiple queries, so fetch until we reach NULL */\n\t\t\tplacementExecution->queryIndex++;\n\t\t\tcontinue;\n\t\t}\n\t\telse if (resultStatus != PGRES_SINGLE_TUPLE)\n\t\t{\n\t\t\t/* query failures are always hard errors */\n\t\t\tReportResultError(connection, result, ERROR);\n\t\t}\n\t\telse if (!storeRows)\n\t\t{\n\t\t\t/*\n\t\t\t * Already receieved rows from executing on another shard placement or\n\t\t\t * doesn't need at all (e.g., DDL).\n\t\t\t */\n\t\t\tPQclear(result);\n\t\t\tcontinue;\n\t\t}\n\n\t\tuint32 queryIndex = placementExecution->queryIndex;\n\t\tif (queryIndex >= task->queryCount)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unexpected query index while processing\"\n\t\t\t\t\t\t\t\t   \" query results\")));\n\t\t}\n\n\t\tTupleDesc tupleDescriptor = tupleDest->tupleDescForQuery(tupleDest, queryIndex);\n\t\tif (tupleDescriptor == NULL)\n\t\t{\n\t\t\tPQclear(result);\n\t\t\tcontinue;\n\t\t}\n\n\t\trowsProcessed = PQntuples(result);\n\t\tuint32 columnCount = PQnfields(result);\n\t\tuint32 expectedColumnCount = tupleDescriptor->natts;\n\n\t\tif (columnCount != expectedColumnCount)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unexpected number of columns from worker: %d, \"\n\t\t\t\t\t\t\t\t   \"expected %d\",\n\t\t\t\t\t\t\t\t   columnCount, expectedColumnCount)));\n\t\t}\n\n\t\tif (columnCount > execution->allocatedColumnCount)\n\t\t{\n\t\t\tpfree(execution->columnArray);\n\t\t\tint oldColumnCount = execution->allocatedColumnCount;\n\t\t\texecution->allocatedColumnCount = columnCount;\n\t\t\texecution->columnArray = palloc0(execution->allocatedColumnCount *\n\t\t\t\t\t\t\t\t\t\t\t sizeof(void *));\n\t\t\tif (EnableBinaryProtocol)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Using repalloc here, to not throw away any previously\n\t\t\t\t * created StringInfos.\n\t\t\t\t */\n\t\t\t\texecution->stringInfoDataArray = repalloc(\n\t\t\t\t\texecution->stringInfoDataArray,\n\t\t\t\t\texecution->allocatedColumnCount *\n\t\t\t\t\tsizeof(StringInfoData));\n\t\t\t\tfor (int i = oldColumnCount; i < columnCount; i++)\n\t\t\t\t{\n\t\t\t\t\tinitStringInfo(&execution->stringInfoDataArray[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvoid **columnArray = execution->columnArray;\n\t\tStringInfoData *stringInfoDataArray = execution->stringInfoDataArray;\n\t\tbool binaryResults = shardCommandExecution->binaryResults;\n\n\t\t/*\n\t\t * stringInfoDataArray is NULL when EnableBinaryProtocol is false. So\n\t\t * we make sure binaryResults is also false in that case. Otherwise we\n\t\t * cannot store them anywhere.\n\t\t */\n\t\tAssert(EnableBinaryProtocol || !binaryResults);\n\n\t\tfor (uint32 rowIndex = 0; rowIndex < rowsProcessed; rowIndex++)\n\t\t{\n\t\t\tuint64 tupleLibpqSize = 0;\n\n\t\t\t/*\n\t\t\t * Switch to a temporary memory context that we reset after each\n\t\t\t * tuple. This protects us from any memory leaks that might be\n\t\t\t * present in anything we do to parse a tuple.\n\t\t\t */\n\t\t\tMemoryContext oldContext = MemoryContextSwitchTo(rowContext);\n\n\t\t\tmemset(columnArray, 0, columnCount * sizeof(void *));\n\n\t\t\tfor (columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t\t\t{\n\t\t\t\tif (PQgetisnull(result, rowIndex, columnIndex))\n\t\t\t\t{\n\t\t\t\t\tcolumnArray[columnIndex] = NULL;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tint valueLength = PQgetlength(result, rowIndex, columnIndex);\n\t\t\t\t\tchar *value = PQgetvalue(result, rowIndex, columnIndex);\n\t\t\t\t\tif (binaryResults)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (PQfformat(result, columnIndex) == 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tereport(ERROR, (errmsg(\"unexpected text result\")));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresetStringInfo(&stringInfoDataArray[columnIndex]);\n\t\t\t\t\t\tappendBinaryStringInfo(&stringInfoDataArray[columnIndex],\n\t\t\t\t\t\t\t\t\t\t\t   value, valueLength);\n\t\t\t\t\t\tcolumnArray[columnIndex] = &stringInfoDataArray[columnIndex];\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (PQfformat(result, columnIndex) == 1)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tereport(ERROR, (errmsg(\"unexpected binary result\")));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcolumnArray[columnIndex] = value;\n\t\t\t\t\t}\n\n\t\t\t\t\ttupleLibpqSize += valueLength;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tAttInMetadata *attInMetadata =\n\t\t\t\tshardCommandExecution->attributeInputMetadata[queryIndex];\n\t\t\tHeapTuple heapTuple;\n\t\t\tif (binaryResults)\n\t\t\t{\n\t\t\t\theapTuple = BuildTupleFromBytes(attInMetadata,\n\t\t\t\t\t\t\t\t\t\t\t\t(fmStringInfo *) columnArray);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\theapTuple = BuildTupleFromCStrings(attInMetadata,\n\t\t\t\t\t\t\t\t\t\t\t\t   (char **) columnArray);\n\t\t\t}\n\n\t\t\tMemoryContextSwitchTo(oldContext);\n\n\t\t\ttupleDest->putTuple(tupleDest, task,\n\t\t\t\t\t\t\t\tplacementExecution->placementExecutionIndex, queryIndex,\n\t\t\t\t\t\t\t\theapTuple, tupleLibpqSize);\n\n\t\t\tMemoryContextReset(rowContext);\n\n\t\t\texecution->rowsProcessed++;\n\t\t}\n\n\t\tPQclear(result);\n\t}\n\n\t/* the context is local to the function, so not needed anymore */\n\tMemoryContextDelete(rowContext);\n\n\treturn fetchDone;\n}\n\n\n/*\n * WorkerPoolFailed marks a worker pool and all the placement executions scheduled\n * on it as failed.\n */\nstatic void\nWorkerPoolFailed(WorkerPool *workerPool)\n{\n\tbool succeeded = false;\n\tdlist_iter iter;\n\n\t/*\n\t * A pool cannot fail multiple times, the necessary actions\n\t * has already be taken, so bail out.\n\t */\n\tif (workerPool->failureState == WORKER_POOL_FAILED ||\n\t\tworkerPool->failureState == WORKER_POOL_FAILED_OVER_TO_LOCAL)\n\t{\n\t\treturn;\n\t}\n\n\tdlist_foreach(iter, &workerPool->pendingTaskQueue)\n\t{\n\t\tTaskPlacementExecution *placementExecution =\n\t\t\tdlist_container(TaskPlacementExecution, workerPendingQueueNode, iter.cur);\n\n\t\tPlacementExecutionDone(placementExecution, succeeded);\n\t}\n\n\tdlist_foreach(iter, &workerPool->readyTaskQueue)\n\t{\n\t\tTaskPlacementExecution *placementExecution =\n\t\t\tdlist_container(TaskPlacementExecution, workerReadyQueueNode, iter.cur);\n\n\t\tPlacementExecutionDone(placementExecution, succeeded);\n\t}\n\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, workerPool->sessionList)\n\t{\n\t\tWorkerSessionFailed(session);\n\t}\n\n\t/* we do not want more connections in this pool */\n\tworkerPool->readyTaskCount = 0;\n\tif (workerPool->failureState != WORKER_POOL_FAILED_OVER_TO_LOCAL)\n\t{\n\t\t/* we prefer not to override WORKER_POOL_FAILED_OVER_TO_LOCAL */\n\t\tworkerPool->failureState = WORKER_POOL_FAILED;\n\t}\n\n\t/*\n\t * The reason is that when replication factor is > 1 and we are performing\n\t * a SELECT, then we only establish connections for the specific placements\n\t * that we will read from. However, when a worker pool fails, we will need\n\t * to establish multiple new connection to other workers and the query\n\t * can only succeed if all those connections are established.\n\t */\n\tif (UseConnectionPerPlacement())\n\t{\n\t\tList *workerList = workerPool->distributedExecution->workerList;\n\n\t\tWorkerPool *pool = NULL;\n\t\tforeach_declared_ptr(pool, workerList)\n\t\t{\n\t\t\t/* failed pools or pools without any connection attempts ignored */\n\t\t\tif (pool->failureState == WORKER_POOL_FAILED ||\n\t\t\t\tINSTR_TIME_IS_ZERO(pool->poolStartTime))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * This should give another NodeConnectionTimeout until all\n\t\t\t * the necessary connections are established.\n\t\t\t */\n\t\t\tINSTR_TIME_SET_CURRENT(pool->poolStartTime);\n\t\t\tpool->checkForPoolTimeout = true;\n\t\t}\n\t}\n}\n\n\n/*\n * WorkerSessionFailed marks all placement executions scheduled on the\n * connection as failed.\n */\nstatic void\nWorkerSessionFailed(WorkerSession *session)\n{\n\tTaskPlacementExecution *placementExecution = session->currentTask;\n\tbool succeeded = false;\n\tdlist_iter iter;\n\n\tif (placementExecution != NULL)\n\t{\n\t\t/* connection failed while a task was active */\n\t\tPlacementExecutionDone(placementExecution, succeeded);\n\t}\n\n\tdlist_foreach(iter, &session->pendingTaskQueue)\n\t{\n\t\tplacementExecution =\n\t\t\tdlist_container(TaskPlacementExecution, sessionPendingQueueNode, iter.cur);\n\n\t\tPlacementExecutionDone(placementExecution, succeeded);\n\t}\n\n\tdlist_foreach(iter, &session->readyTaskQueue)\n\t{\n\t\tplacementExecution =\n\t\t\tdlist_container(TaskPlacementExecution, sessionReadyQueueNode, iter.cur);\n\n\t\tPlacementExecutionDone(placementExecution, succeeded);\n\t}\n}\n\n\n/*\n * PlacementExecutionDone marks the given placement execution as done when\n * the results have been received or a failure occurred and sets the succeeded\n * flag accordingly. It also adds other placement executions of the same\n * task to the appropriate ready queues.\n */\nstatic void\nPlacementExecutionDone(TaskPlacementExecution *placementExecution, bool succeeded)\n{\n\tWorkerPool *workerPool = placementExecution->workerPool;\n\tDistributedExecution *execution = workerPool->distributedExecution;\n\tShardCommandExecution *shardCommandExecution =\n\t\tplacementExecution->shardCommandExecution;\n\tTaskExecutionState executionState = shardCommandExecution->executionState;\n\tbool failedPlacementExecutionIsOnPendingQueue = false;\n\n\tif (placementExecution->executionState == PLACEMENT_EXECUTION_FAILED)\n\t{\n\t\t/*\n\t\t * We may mark placements as failed multiple times, but should only act\n\t\t * the first time. Nor should we accept success after failure.\n\t\t */\n\t\treturn;\n\t}\n\n\tif (succeeded)\n\t{\n\t\t/* mark the placement execution as finished */\n\t\tplacementExecution->executionState = PLACEMENT_EXECUTION_FINISHED;\n\n\t\tAssert(INSTR_TIME_IS_ZERO(placementExecution->endTime));\n\t\tINSTR_TIME_SET_CURRENT(placementExecution->endTime);\n\t\tuint64 durationMicrosecs =\n\t\t\tMicrosecondsBetweenTimestamps(placementExecution->startTime,\n\t\t\t\t\t\t\t\t\t\t  placementExecution->endTime);\n\t\tworkerPool->totalTaskExecutionTime += durationMicrosecs;\n\t\tworkerPool->totalExecutedTasks += 1;\n\n\t\tif (IsLoggableLevel(DEBUG4))\n\t\t{\n\t\t\tereport(DEBUG4, (errmsg(\"task execution (%d) for placement (%ld) on anchor \"\n\t\t\t\t\t\t\t\t\t\"shard (%ld) finished in %ld microseconds on worker \"\n\t\t\t\t\t\t\t\t\t\"node %s:%d\", shardCommandExecution->task->taskId,\n\t\t\t\t\t\t\t\t\tplacementExecution->shardPlacement->placementId,\n\t\t\t\t\t\t\t\t\tshardCommandExecution->task->anchorShardId,\n\t\t\t\t\t\t\t\t\tdurationMicrosecs, workerPool->nodeName,\n\t\t\t\t\t\t\t\t\tworkerPool->nodePort)));\n\t\t}\n\t}\n\telse if (CanFailoverPlacementExecutionToLocalExecution(placementExecution))\n\t{\n\t\t/*\n\t\t * The placement execution can be done over local execution, so it is a soft\n\t\t * failure for now.\n\t\t */\n\t\tplacementExecution->executionState =\n\t\t\tPLACEMENT_EXECUTION_FAILOVER_TO_LOCAL_EXECUTION;\n\t}\n\telse\n\t{\n\t\tif (placementExecution->executionState == PLACEMENT_EXECUTION_NOT_READY)\n\t\t{\n\t\t\t/*\n\t\t\t * If the placement is in NOT_READY state, it means that the placement\n\t\t\t * execution is assigned to the pending queue of a failed pool or\n\t\t\t * session. So, we should not schedule the next placement execution based\n\t\t\t * on this failure.\n\t\t\t */\n\t\t\tfailedPlacementExecutionIsOnPendingQueue = true;\n\t\t}\n\n\t\tplacementExecution->executionState = PLACEMENT_EXECUTION_FAILED;\n\t}\n\n\tif (executionState != TASK_EXECUTION_NOT_FINISHED)\n\t{\n\t\t/*\n\t\t * Task execution has already been finished, no need to continue the\n\t\t * next placement.\n\t\t */\n\t\treturn;\n\t}\n\n\t/*\n\t * Update unfinishedTaskCount only when state changes from not finished to\n\t * finished or failed state.\n\t */\n\tTaskExecutionState newExecutionState =\n\t\tTaskExecutionStateMachine(shardCommandExecution);\n\tif (newExecutionState == TASK_EXECUTION_FINISHED)\n\t{\n\t\texecution->unfinishedTaskCount--;\n\t\treturn;\n\t}\n\telse if (newExecutionState == TASK_EXECUTION_FAILOVER_TO_LOCAL_EXECUTION)\n\t{\n\t\texecution->unfinishedTaskCount--;\n\n\t\t/* move the task to the local execution */\n\t\tTask *task = shardCommandExecution->task;\n\t\texecution->localTaskList = lappend(execution->localTaskList, task);\n\n\t\t/* remove the task from the remote execution list */\n\t\texecution->remoteTaskList = list_delete_ptr(execution->remoteTaskList, task);\n\n\t\t/*\n\t\t * As we decided to failover this task to local execution, we cannot\n\t\t * allow remote execution to this pool during this distributedExecution.\n\t\t */\n\t\tSetLocalExecutionStatus(LOCAL_EXECUTION_REQUIRED);\n\t\tworkerPool->failureState = WORKER_POOL_FAILED_OVER_TO_LOCAL;\n\n\t\tereport(DEBUG4, (errmsg(\"Task %d execution is failed over to local execution\",\n\t\t\t\t\t\t\t\ttask->taskId)));\n\n\t\treturn;\n\t}\n\telse if (newExecutionState == TASK_EXECUTION_FAILED)\n\t{\n\t\texecution->unfinishedTaskCount--;\n\n\t\t/*\n\t\t * Even if a single task execution fails, there is no way to\n\t\t * successfully finish the execution.\n\t\t */\n\t\texecution->failed = true;\n\t\treturn;\n\t}\n\telse if (!failedPlacementExecutionIsOnPendingQueue)\n\t{\n\t\tScheduleNextPlacementExecution(placementExecution, succeeded);\n\t}\n}\n\n\n/*\n * CanFailoverPlacementExecutionToLocalExecution returns true if the input\n * TaskPlacementExecution can be fail overed to local execution. In other words,\n * the execution can be deferred to local execution.\n */\nstatic bool\nCanFailoverPlacementExecutionToLocalExecution(TaskPlacementExecution *placementExecution)\n{\n\tif (!EnableLocalExecution)\n\t{\n\t\t/* the user explicitly disabled local execution */\n\t\treturn false;\n\t}\n\n\tif (!placementExecution->shardCommandExecution->localExecutionSupported)\n\t{\n\t\t/* cannot execute given task locally */\n\t\treturn false;\n\t}\n\n\tif (GetCurrentLocalExecutionStatus() == LOCAL_EXECUTION_DISABLED)\n\t{\n\t\t/*\n\t\t * If the current transaction accessed the local node over a connection\n\t\t * then we can't use local execution because of visibility issues.\n\t\t */\n\t\treturn false;\n\t}\n\n\tWorkerPool *workerPool = placementExecution->workerPool;\n\tif (!workerPool->poolToLocalNode)\n\t{\n\t\t/* we can only fail over tasks to local execution for local pools */\n\t\treturn false;\n\t}\n\n\tif (workerPool->activeConnectionCount > 0)\n\t{\n\t\t/*\n\t\t * The pool has already active connections, the executor is capable\n\t\t * of using those active connections. So, no need to failover\n\t\t * to the local execution.\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (placementExecution->assignedSession != NULL)\n\t{\n\t\t/*\n\t\t * If the placement execution has been assigned to a specific session,\n\t\t * it has to be executed over that session. Otherwise, it would cause\n\t\t * self-deadlocks and break read-your-own-writes consistency.\n\t\t */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ScheduleNextPlacementExecution is triggered if the query needs to be\n * executed on any or all placements in order and there is a placement on\n * which the execution has not happened yet. If so make that placement\n * ready-to-start by adding it to the appropriate queue.\n */\nstatic void\nScheduleNextPlacementExecution(TaskPlacementExecution *placementExecution, bool succeeded)\n{\n\tShardCommandExecution *shardCommandExecution =\n\t\tplacementExecution->shardCommandExecution;\n\tPlacementExecutionOrder executionOrder = shardCommandExecution->executionOrder;\n\n\tif ((executionOrder == EXECUTION_ORDER_ANY && !succeeded) ||\n\t\texecutionOrder == EXECUTION_ORDER_SEQUENTIAL)\n\t{\n\t\tTaskPlacementExecution *nextPlacementExecution = NULL;\n\n\t\t/* find a placement execution that is not yet marked as failed */\n\t\tdo {\n\t\t\tint nextPlacementExecutionIndex =\n\t\t\t\tplacementExecution->placementExecutionIndex + 1;\n\n\t\t\t/*\n\t\t\t * If all tasks failed then we should already have errored out.\n\t\t\t * Still, be defensive and throw error instead of crashes.\n\t\t\t */\n\t\t\tint placementExecutionCount = shardCommandExecution->placementExecutionCount;\n\t\t\tif (nextPlacementExecutionIndex >= placementExecutionCount)\n\t\t\t{\n\t\t\t\tWorkerPool *workerPool = placementExecution->workerPool;\n\t\t\t\tereport(ERROR, (errmsg(\"execution cannot recover from multiple \"\n\t\t\t\t\t\t\t\t\t   \"connection failures. Last node failed \"\n\t\t\t\t\t\t\t\t\t   \"%s:%d\", workerPool->nodeName,\n\t\t\t\t\t\t\t\t\t   workerPool->nodePort)));\n\t\t\t}\n\n\t\t\t/* get the next placement in the planning order */\n\t\t\tnextPlacementExecution =\n\t\t\t\tshardCommandExecution->placementExecutions[nextPlacementExecutionIndex];\n\n\t\t\tif (nextPlacementExecution->executionState == PLACEMENT_EXECUTION_NOT_READY)\n\t\t\t{\n\t\t\t\t/* move the placement execution to the ready queue */\n\t\t\t\tPlacementExecutionReady(nextPlacementExecution);\n\t\t\t}\n\t\t} while (nextPlacementExecution->executionState == PLACEMENT_EXECUTION_FAILED);\n\t}\n}\n\n\n/*\n * PlacementExecutionReady adds a placement execution to the ready queue when\n * its dependent placement executions have finished.\n */\nstatic void\nPlacementExecutionReady(TaskPlacementExecution *placementExecution)\n{\n\tWorkerPool *workerPool = placementExecution->workerPool;\n\n\tif (placementExecution->assignedSession != NULL)\n\t{\n\t\tWorkerSession *session = placementExecution->assignedSession;\n\t\tMultiConnection *connection = session->connection;\n\t\tRemoteTransaction *transaction = &(connection->remoteTransaction);\n\t\tRemoteTransactionState transactionState = transaction->transactionState;\n\n\t\tif (placementExecution->executionState == PLACEMENT_EXECUTION_NOT_READY)\n\t\t{\n\t\t\t/* remove from not-ready task queue */\n\t\t\tdlist_delete(&placementExecution->sessionPendingQueueNode);\n\n\t\t\t/* add to ready-to-start task queue */\n\t\t\tdlist_push_tail(&session->readyTaskQueue,\n\t\t\t\t\t\t\t&placementExecution->sessionReadyQueueNode);\n\t\t}\n\n\t\tif (transactionState == REMOTE_TRANS_NOT_STARTED ||\n\t\t\ttransactionState == REMOTE_TRANS_STARTED)\n\t\t{\n\t\t\t/*\n\t\t\t * If the connection is idle, wake it up by checking whether\n\t\t\t * the connection is writeable.\n\t\t\t */\n\t\t\tUpdateConnectionWaitFlags(session, WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE);\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (placementExecution->executionState == PLACEMENT_EXECUTION_NOT_READY)\n\t\t{\n\t\t\t/* remove from not-ready task queue */\n\t\t\tdlist_delete(&placementExecution->workerPendingQueueNode);\n\n\t\t\t/* add to ready-to-start task queue */\n\t\t\tdlist_push_tail(&workerPool->readyTaskQueue,\n\t\t\t\t\t\t\t&placementExecution->workerReadyQueueNode);\n\t\t}\n\n\t\tworkerPool->readyTaskCount++;\n\n\t\t/* wake up an idle connection by checking whether the connection is writeable */\n\t\tWorkerSession *session = NULL;\n\t\tforeach_declared_ptr(session, workerPool->sessionList)\n\t\t{\n\t\t\tMultiConnection *connection = session->connection;\n\t\t\tRemoteTransaction *transaction = &(connection->remoteTransaction);\n\t\t\tRemoteTransactionState transactionState = transaction->transactionState;\n\n\t\t\tif (transactionState == REMOTE_TRANS_NOT_STARTED ||\n\t\t\t\ttransactionState == REMOTE_TRANS_STARTED)\n\t\t\t{\n\t\t\t\tUpdateConnectionWaitFlags(session,\n\t\t\t\t\t\t\t\t\t\t  WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE);\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* update the state to ready for further processing */\n\tplacementExecution->executionState = PLACEMENT_EXECUTION_READY;\n}\n\n\n/*\n * TaskExecutionStateMachine returns whether a shard command execution\n * finished or failed according to its execution order. If the task is\n * already finished, simply return the state. Else, calculate the state\n * and return it.\n */\nstatic TaskExecutionState\nTaskExecutionStateMachine(ShardCommandExecution *shardCommandExecution)\n{\n\tPlacementExecutionOrder executionOrder = shardCommandExecution->executionOrder;\n\tint donePlacementCount = 0;\n\tint failedPlacementCount = 0;\n\tint failedOverPlacementCount = 0;\n\tint placementCount = 0;\n\tint placementExecutionIndex = 0;\n\tint placementExecutionCount = shardCommandExecution->placementExecutionCount;\n\tTaskExecutionState currentTaskExecutionState = shardCommandExecution->executionState;\n\n\tif (currentTaskExecutionState != TASK_EXECUTION_NOT_FINISHED)\n\t{\n\t\t/* we've already calculated the state, simply return it */\n\t\treturn currentTaskExecutionState;\n\t}\n\n\tfor (; placementExecutionIndex < placementExecutionCount; placementExecutionIndex++)\n\t{\n\t\tTaskPlacementExecution *placementExecution =\n\t\t\tshardCommandExecution->placementExecutions[placementExecutionIndex];\n\t\tTaskPlacementExecutionState executionState = placementExecution->executionState;\n\n\t\tif (executionState == PLACEMENT_EXECUTION_FINISHED)\n\t\t{\n\t\t\tdonePlacementCount++;\n\t\t}\n\t\telse if (executionState == PLACEMENT_EXECUTION_FAILED)\n\t\t{\n\t\t\tfailedPlacementCount++;\n\t\t}\n\t\telse if (executionState == PLACEMENT_EXECUTION_FAILOVER_TO_LOCAL_EXECUTION)\n\t\t{\n\t\t\tfailedOverPlacementCount++;\n\t\t}\n\n\t\tplacementCount++;\n\t}\n\n\tif (failedPlacementCount == placementCount)\n\t{\n\t\tcurrentTaskExecutionState = TASK_EXECUTION_FAILED;\n\t}\n\telse if (executionOrder != EXECUTION_ORDER_ANY && failedPlacementCount > 0)\n\t{\n\t\tcurrentTaskExecutionState = TASK_EXECUTION_FAILED;\n\t}\n\telse if (executionOrder == EXECUTION_ORDER_ANY && donePlacementCount > 0)\n\t{\n\t\tcurrentTaskExecutionState = TASK_EXECUTION_FINISHED;\n\t}\n\telse if (donePlacementCount + failedPlacementCount == placementCount)\n\t{\n\t\tcurrentTaskExecutionState = TASK_EXECUTION_FINISHED;\n\t}\n\telse if (failedOverPlacementCount + donePlacementCount + failedPlacementCount ==\n\t\t\t placementCount)\n\t{\n\t\t/*\n\t\t * For any given task, we could have 3 end states:\n\t\t *  - \"donePlacementCount\" indicates the successful placement executions\n\t\t *  - \"failedPlacementCount\" indicates the failed placement executions\n\t\t *  - \"failedOverPlacementCount\" indicates the placement executions that\n\t\t *     are failed when using remote execution due to connection errors,\n\t\t *     but there is still a possibility of being successful via\n\t\t *     local execution. So, for now they are considered as soft\n\t\t *     errors.\n\t\t */\n\t\tcurrentTaskExecutionState = TASK_EXECUTION_FAILOVER_TO_LOCAL_EXECUTION;\n\t}\n\telse\n\t{\n\t\tcurrentTaskExecutionState = TASK_EXECUTION_NOT_FINISHED;\n\t}\n\n\tshardCommandExecution->executionState = currentTaskExecutionState;\n\n\treturn shardCommandExecution->executionState;\n}\n\n\n/*\n * BuildWaitEventSet creates a WaitEventSet for the given array of connections\n * which can be used to wait for any of the sockets to become read-ready or\n * write-ready.\n */\nstatic WaitEventSet *\nBuildWaitEventSet(List *sessionList)\n{\n\t/* additional 2 is for postmaster and latch */\n\tint eventSetSize = GetEventSetSize(sessionList);\n\n\tWaitEventSet *waitEventSet =\n\t\tCreateWaitEventSet(WaitEventSetTracker_compat, eventSetSize);\n\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, sessionList)\n\t{\n\t\tAddSessionToWaitEventSet(session, waitEventSet);\n\t}\n\n\treturn waitEventSet;\n}\n\n\n/*\n * FreeExecutionWaitEvents is a helper function that gets\n * a DistributedExecution and frees events and waitEventSet.\n */\nstatic void\nFreeExecutionWaitEvents(DistributedExecution *execution)\n{\n\tif (execution->events != NULL)\n\t{\n\t\tpfree(execution->events);\n\t\texecution->events = NULL;\n\t}\n\n\tif (execution->waitEventSet != NULL)\n\t{\n\t\tFreeWaitEventSet(execution->waitEventSet);\n\t\texecution->waitEventSet = NULL;\n\t}\n}\n\n\n/*\n * AddSessionToWaitEventSet is a helper function which adds the session to\n * the waitEventSet. The function does certain checks before adding the session\n * to the waitEventSet.\n */\nstatic void\nAddSessionToWaitEventSet(WorkerSession *session, WaitEventSet *waitEventSet)\n{\n\tMultiConnection *connection = session->connection;\n\n\tif (connection->pgConn == NULL)\n\t{\n\t\t/* connection died earlier in the transaction */\n\t\treturn;\n\t}\n\n\tif (connection->waitFlags == 0)\n\t{\n\t\t/* not currently waiting for this connection */\n\t\treturn;\n\t}\n\n\tint sock = PQsocket(connection->pgConn);\n\tif (sock == -1)\n\t{\n\t\t/* connection was closed */\n\t\treturn;\n\t}\n\n\tint waitEventSetIndex =\n\t\tCitusAddWaitEventSetToSet(waitEventSet, connection->waitFlags, sock,\n\t\t\t\t\t\t\t\t  NULL, (void *) session);\n\tsession->waitEventSetIndex = waitEventSetIndex;\n\n\t/*\n\t * Inform failed to add to wait event set with a debug message as this\n\t * is too detailed information for users.\n\t */\n\tif (session->waitEventSetIndex == WAIT_EVENT_SET_INDEX_FAILED)\n\t{\n\t\tereport(DEBUG1, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t errmsg(\"Adding wait event for node %s:%d failed. \"\n\t\t\t\t\t\t\t\t\"The socket was: %d\",\n\t\t\t\t\t\t\t\tsession->workerPool->nodeName,\n\t\t\t\t\t\t\t\tsession->workerPool->nodePort, sock)));\n\t}\n}\n\n\n/*\n * GetEventSetSize returns the event set size for a list of sessions.\n */\nstatic int\nGetEventSetSize(List *sessionList)\n{\n\t/* additional 2 is for postmaster and latch */\n\treturn list_length(sessionList) + 2;\n}\n\n\n/*\n * RebuildWaitEventSetFlags modifies the given waitEventSet with the wait flags\n * for connections in the sessionList.\n */\nstatic void\nRebuildWaitEventSetFlags(WaitEventSet *waitEventSet, List *sessionList)\n{\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, sessionList)\n\t{\n\t\tMultiConnection *connection = session->connection;\n\t\tint waitEventSetIndex = session->waitEventSetIndex;\n\n\t\tif (connection->pgConn == NULL)\n\t\t{\n\t\t\t/* connection died earlier in the transaction */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (connection->waitFlags == 0)\n\t\t{\n\t\t\t/* not currently waiting for this connection */\n\t\t\tcontinue;\n\t\t}\n\n\t\tint sock = PQsocket(connection->pgConn);\n\t\tif (sock == -1)\n\t\t{\n\t\t\t/* connection was closed */\n\t\t\tcontinue;\n\t\t}\n\n\t\tbool success =\n\t\t\tCitusModifyWaitEvent(waitEventSet, waitEventSetIndex,\n\t\t\t\t\t\t\t\t connection->waitFlags, NULL);\n\t\tif (!success)\n\t\t{\n\t\t\tereport(DEBUG1, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t\t errmsg(\"modifying wait event for node %s:%d failed. \"\n\t\t\t\t\t\t\t\t\t\"The wait event index was: %d\",\n\t\t\t\t\t\t\t\t\tconnection->hostname, connection->port,\n\t\t\t\t\t\t\t\t\twaitEventSetIndex)));\n\n\t\t\tsession->waitEventSetIndex = WAIT_EVENT_SET_INDEX_FAILED;\n\t\t}\n\t}\n}\n\n\n/*\n * CleanUpSessions does any clean-up necessary for the session used\n * during the execution. We only reach the function after successfully\n * completing all the tasks and we expect no tasks are still in progress.\n */\nstatic void\nCleanUpSessions(DistributedExecution *execution)\n{\n\tList *sessionList = execution->sessionList;\n\n\t/* we get to this function only after successful executions */\n\tAssert(!execution->failed && execution->unfinishedTaskCount == 0);\n\n\t/* always trigger wait event set in the first round */\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, sessionList)\n\t{\n\t\tMultiConnection *connection = session->connection;\n\n\t\tereport(DEBUG4, (errmsg(\"Total number of commands sent over the session %ld: %ld \"\n\t\t\t\t\t\t\t\t\"to node %s:%d\", session->sessionId,\n\t\t\t\t\t\t\t\tsession->commandsSent,\n\t\t\t\t\t\t\t\tconnection->hostname, connection->port)));\n\n\t\tUnclaimConnection(connection);\n\n\t\tif (connection->connectionState == MULTI_CONNECTION_CONNECTING ||\n\t\t\tconnection->connectionState == MULTI_CONNECTION_FAILED ||\n\t\t\tconnection->connectionState == MULTI_CONNECTION_LOST ||\n\t\t\tconnection->connectionState == MULTI_CONNECTION_TIMED_OUT)\n\t\t{\n\t\t\t/*\n\t\t\t * We want the MultiConnection go away and not used in\n\t\t\t * the subsequent executions.\n\t\t\t *\n\t\t\t * We cannot get MULTI_CONNECTION_LOST via the ConnectionStateMachine,\n\t\t\t * but we might get it via the connection API and find us here before\n\t\t\t * changing any states in the ConnectionStateMachine.\n\t\t\t *\n\t\t\t */\n\t\t\tCloseConnection(connection);\n\t\t}\n\t\telse if (connection->connectionState == MULTI_CONNECTION_CONNECTED)\n\t\t{\n\t\t\tRemoteTransaction *transaction = &(connection->remoteTransaction);\n\t\t\tRemoteTransactionState transactionState = transaction->transactionState;\n\n\t\t\tif (transactionState == REMOTE_TRANS_CLEARING_RESULTS)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We might have established the connection, and even sent BEGIN, but not\n\t\t\t\t * get to the point where we assigned a task to this specific connection\n\t\t\t\t * (because other connections in the pool already finished all the tasks).\n\t\t\t\t */\n\t\t\t\tAssert(session->commandsSent == 0);\n\n\t\t\t\tClearResults(connection, false);\n\t\t\t}\n\t\t\telse if (!(transactionState == REMOTE_TRANS_NOT_STARTED ||\n\t\t\t\t\t   transactionState == REMOTE_TRANS_STARTED))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We don't have to handle anything else. Note that the execution\n\t\t\t\t * could only finish on connectionStates of MULTI_CONNECTION_CONNECTING,\n\t\t\t\t * MULTI_CONNECTION_FAILED and MULTI_CONNECTION_CONNECTED. The first two\n\t\t\t\t * are already handled above.\n\t\t\t\t *\n\t\t\t\t * When we're on MULTI_CONNECTION_CONNECTED, TransactionStateMachine\n\t\t\t\t * ensures that all the necessary commands are successfully sent over\n\t\t\t\t * the connection and everything is cleared up. Otherwise, we'd have been\n\t\t\t\t * on MULTI_CONNECTION_FAILED state.\n\t\t\t\t */\n\t\t\t\tereport(WARNING, (errmsg(\"unexpected transaction state at the end of \"\n\t\t\t\t\t\t\t\t\t\t \"execution: %d\", transactionState)));\n\t\t\t}\n\n\t\t\t/* get ready for the next executions if we need use the same connection */\n\t\t\tconnection->waitFlags = WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"unexpected connection state at the end of \"\n\t\t\t\t\t\t\t\t\t \"execution: %d\", connection->connectionState)));\n\t\t}\n\t}\n}\n\n\n/*\n * UnclaimAllSessionConnections unclaims all of the connections for the given\n * sessionList.\n */\nstatic void\nUnclaimAllSessionConnections(List *sessionList)\n{\n\tWorkerSession *session = NULL;\n\tforeach_declared_ptr(session, sessionList)\n\t{\n\t\tMultiConnection *connection = session->connection;\n\n\t\tUnclaimConnection(connection);\n\t}\n}\n\n\n/*\n * SetLocalForceMaxQueryParallelization is simply a C interface for setting\n * the following:\n *      SET LOCAL citus.force_max_query_parallelization TO on;\n */\nvoid\nSetLocalForceMaxQueryParallelization(void)\n{\n\tset_config_option(\"citus.force_max_query_parallelization\", \"on\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/citus_custom_scan.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_custom_scan.c\n *\n * Definitions of custom scan methods for all executor types.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/pg_operator.h\"\n#include \"commands/copy.h\"\n#include \"executor/executor.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"utils/datum.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/rel.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/citus_clauses.h\"\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/distributed_execution_locks.h\"\n#include \"distributed/function_call_delegation.h\"\n#include \"distributed/insert_select_executor.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/local_plan_cache.h\"\n#include \"distributed/merge_executor.h\"\n#include \"distributed/merge_planner.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/shard_utils.h\"\n#include \"distributed/stats/query_stats.h\"\n#include \"distributed/stats/stat_counters.h\"\n#include \"distributed/subplan_execution.h\"\n#include \"distributed/worker_log_messages.h\"\n#include \"distributed/worker_protocol.h\"\n\n\nextern AllowedDistributionColumn AllowedDistributionColumnValue;\n\n/* functions for creating custom scan nodes */\nstatic Node * AdaptiveExecutorCreateScan(CustomScan *scan);\nstatic Node * NonPushableInsertSelectCreateScan(CustomScan *scan);\nstatic Node * DelayedErrorCreateScan(CustomScan *scan);\nstatic Node * NonPushableMergeCommandCreateScan(CustomScan *scan);\n\n/* functions that are common to different scans */\nstatic void CitusBeginScan(CustomScanState *node, EState *estate, int eflags);\nstatic void CitusBeginReadOnlyScan(CustomScanState *node, EState *estate, int eflags);\nstatic void CitusBeginModifyScan(CustomScanState *node, EState *estate, int eflags);\nstatic void CitusPreExecScan(CitusScanState *scanState);\nstatic bool ModifyJobNeedsEvaluation(Job *workerJob);\nstatic void RegenerateTaskForFasthPathQuery(Job *workerJob);\nstatic void RegenerateTaskListForInsert(Job *workerJob);\nstatic DistributedPlan * CopyDistributedPlanWithoutCache(DistributedPlan *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t originalDistributedPlan);\nstatic void CitusEndScan(CustomScanState *node);\nstatic void CitusReScan(CustomScanState *node);\nstatic void EnsureForceDelegationDistributionKey(Job *job);\nstatic void EnsureAnchorShardsInJobExist(Job *job);\nstatic bool AnchorShardsInTaskListExist(List *taskList);\nstatic void TryToRerouteFastPathModifyQuery(Job *job);\nstatic void CheckQueryDeparseSafety(Query *query);\n\n\n/* create custom scan methods for all executors */\nCustomScanMethods AdaptiveExecutorCustomScanMethods = {\n\t\"Citus Adaptive\",\n\tAdaptiveExecutorCreateScan\n};\n\nCustomScanMethods NonPushableInsertSelectCustomScanMethods = {\n\t\"Citus INSERT ... SELECT\",\n\tNonPushableInsertSelectCreateScan\n};\n\nCustomScanMethods DelayedErrorCustomScanMethods = {\n\t\"Citus Delayed Error\",\n\tDelayedErrorCreateScan\n};\n\nCustomScanMethods NonPushableMergeCommandCustomScanMethods = {\n\t\"Citus MERGE INTO ...\",\n\tNonPushableMergeCommandCreateScan\n};\n\n\n/*\n * Define executor methods for the different executor types.\n */\nstatic CustomExecMethods AdaptiveExecutorCustomExecMethods = {\n\t.CustomName = \"AdaptiveExecutorScan\",\n\t.BeginCustomScan = CitusBeginScan,\n\t.ExecCustomScan = CitusExecScan,\n\t.EndCustomScan = CitusEndScan,\n\t.ReScanCustomScan = CitusReScan,\n\t.ExplainCustomScan = CitusExplainScan\n};\n\nstatic CustomExecMethods NonPushableInsertSelectCustomExecMethods = {\n\t.CustomName = \"NonPushableInsertSelectScan\",\n\t.BeginCustomScan = CitusBeginScan,\n\t.ExecCustomScan = NonPushableInsertSelectExecScan,\n\t.EndCustomScan = CitusEndScan,\n\t.ReScanCustomScan = CitusReScan,\n\t.ExplainCustomScan = NonPushableInsertSelectExplainScan\n};\n\n\nstatic CustomExecMethods NonPushableMergeCommandCustomExecMethods = {\n\t.CustomName = \"NonPushableMergeCommandScan\",\n\t.BeginCustomScan = CitusBeginScan,\n\t.ExecCustomScan = NonPushableMergeCommandExecScan,\n\t.EndCustomScan = CitusEndScan,\n\t.ReScanCustomScan = CitusReScan,\n\t.ExplainCustomScan = NonPushableMergeCommandExplainScan\n};\n\n\n/*\n * IsCitusCustomState returns if a given PlanState node is a CitusCustomState node.\n */\nbool\nIsCitusCustomState(PlanState *planState)\n{\n\tif (!IsA(planState, CustomScanState))\n\t{\n\t\treturn false;\n\t}\n\n\tCustomScanState *css = castNode(CustomScanState, planState);\n\tif (css->methods == &AdaptiveExecutorCustomExecMethods ||\n\t\tcss->methods == &NonPushableInsertSelectCustomExecMethods ||\n\t\tcss->methods == &NonPushableMergeCommandCustomExecMethods)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * Let PostgreSQL know about Citus' custom scan nodes.\n */\nvoid\nRegisterCitusCustomScanMethods(void)\n{\n\tRegisterCustomScanMethods(&AdaptiveExecutorCustomScanMethods);\n\tRegisterCustomScanMethods(&NonPushableInsertSelectCustomScanMethods);\n\tRegisterCustomScanMethods(&DelayedErrorCustomScanMethods);\n\tRegisterCustomScanMethods(&NonPushableMergeCommandCustomScanMethods);\n}\n\n\n/*\n * CitusBeginScan sets the coordinator backend initiated by Citus for queries using\n * that function as the BeginCustomScan callback.\n *\n * The function also handles deferred shard pruning along with function evaluations.\n */\nstatic void\nCitusBeginScan(CustomScanState *node, EState *estate, int eflags)\n{\n\tCitusScanState *scanState = (CitusScanState *) node;\n\n\t/*\n\t * Make sure we can see notices during regular queries, which would typically\n\t * be the result of a function that raises a notices being called.\n\t */\n\tEnableWorkerMessagePropagation();\n\n\n\t/*\n\t * Since we are using a tuplestore we cannot use the virtual tuples postgres had\n\t * already setup on the CustomScan. Instead we need to reinitialize the tuples as\n\t * minimal.\n\t *\n\t * During initialization postgres also created the projection information and the\n\t * quals, but both are 'compiled' to be executed on virtual tuples. Since we replaced\n\t * the tuples with minimal tuples we also compile both the projection and the quals\n\t * on to these 'new' tuples.\n\t */\n\tExecInitResultSlot(&scanState->customScanState.ss.ps, &TTSOpsMinimalTuple);\n\n\tExecInitScanTupleSlot(node->ss.ps.state, &node->ss, node->ss.ps.scandesc,\n\t\t\t\t\t\t  &TTSOpsMinimalTuple);\n\tExecAssignScanProjectionInfoWithVarno(&node->ss, INDEX_VAR);\n\n\tnode->ss.ps.qual = ExecInitQual(node->ss.ps.plan->qual, (PlanState *) node);\n\n\tDistributedPlan *distributedPlan = scanState->distributedPlan;\n\tif (distributedPlan->modifyQueryViaCoordinatorOrRepartition != NULL)\n\t{\n\t\t/*\n\t\t * INSERT..SELECT / MERGE via coordinator or re-partitioning are special because\n\t\t * the SELECT part is planned separately.\n\t\t */\n\t\treturn;\n\t}\n\telse if (distributedPlan->modLevel == ROW_MODIFY_READONLY)\n\t{\n\t\tCitusBeginReadOnlyScan(node, estate, eflags);\n\t}\n\telse\n\t{\n\t\tCitusBeginModifyScan(node, estate, eflags);\n\t}\n\n\n\t/*\n\t * If there is force_delgation functions' distribution argument set,\n\t * enforce it\n\t */\n\tif (AllowedDistributionColumnValue.isActive)\n\t{\n\t\tJob *workerJob = scanState->distributedPlan->workerJob;\n\t\tEnsureForceDelegationDistributionKey(workerJob);\n\t}\n\n\t/*\n\t * In case of a prepared statement, we will see this distributed plan again\n\t * on the next execution with a higher usage counter.\n\t */\n\tdistributedPlan->numberOfTimesExecuted++;\n}\n\n\n/*\n * CitusPreExecScan is called right before postgres' executor starts pulling tuples.\n */\nstatic void\nCitusPreExecScan(CitusScanState *scanState)\n{\n\tAdaptiveExecutorPreExecutorRun(scanState);\n}\n\n\n/*\n * CitusExecScan is called when a tuple is pulled from a custom scan.\n * On the first call, it executes the distributed query and writes the\n * results to a tuple store. The postgres executor calls this function\n * repeatedly to read tuples from the tuple store.\n */\nTupleTableSlot *\nCitusExecScan(CustomScanState *node)\n{\n\tCitusScanState *scanState = (CitusScanState *) node;\n\n\tif (!scanState->finishedRemoteScan)\n\t{\n\t\tbool isMultiTaskPlan = IsMultiTaskPlan(scanState->distributedPlan);\n\n\t\tAdaptiveExecutor(scanState);\n\n\t\tif (isMultiTaskPlan)\n\t\t{\n\t\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_MULTI_SHARD);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_SINGLE_SHARD);\n\t\t}\n\n\t\tscanState->finishedRemoteScan = true;\n\t}\n\n\treturn ReturnTupleFromTuplestore(scanState);\n}\n\n\n/*\n * CitusBeginReadOnlyScan handles deferred pruning and plan caching for SELECTs.\n */\nstatic void\nCitusBeginReadOnlyScan(CustomScanState *node, EState *estate, int eflags)\n{\n\tCitusScanState *scanState = (CitusScanState *) node;\n\tDistributedPlan *originalDistributedPlan = scanState->distributedPlan;\n\n\tAssert(originalDistributedPlan->workerJob->jobQuery->commandType == CMD_SELECT);\n\n\tif (!originalDistributedPlan->workerJob->deferredPruning)\n\t{\n\t\t/*\n\t\t * For SELECT queries that have already been pruned we can proceed straight\n\t\t * to execution, since none of the prepared statement logic applies.\n\t\t */\n\t\treturn;\n\t}\n\n\t/*\n\t * Create a copy of the generic plan for the current execution, but make a shallow\n\t * copy of the plan cache. That means we'll be able to access the plan cache via\n\t * currentPlan->workerJob->localPlannedStatements, but it will be preserved across\n\t * executions by the prepared statement logic.\n\t */\n\tDistributedPlan *currentPlan =\n\t\tCopyDistributedPlanWithoutCache(originalDistributedPlan);\n\tscanState->distributedPlan = currentPlan;\n\n\tJob *workerJob = currentPlan->workerJob;\n\tQuery *jobQuery = workerJob->jobQuery;\n\tPlanState *planState = &(scanState->customScanState.ss.ps);\n\n\t/*\n\t * We only do deferred pruning for fast path queries, which have a single\n\t * partition column value.\n\t */\n\tAssert(currentPlan->fastPathRouterPlan || !EnableFastPathRouterPlanner);\n\n\t/*\n\t * Evaluate parameters, because the parameters are only available on the\n\t * coordinator and are required for pruning.\n\t *\n\t * We don't evaluate functions for read-only queries on the coordinator\n\t * at the moment. Most function calls would be in a context where they\n\t * should be re-evaluated for every row in case of volatile functions.\n\t *\n\t * TODO: evaluate stable functions\n\t */\n\tExecuteCoordinatorEvaluableExpressions(jobQuery, planState);\n\n\t/* job query no longer has parameters, so we should not send any */\n\tworkerJob->parametersInJobQueryResolved = true;\n\n\t/* parameters are filled in, so we can generate a task for this execution */\n\tRegenerateTaskForFasthPathQuery(workerJob);\n\n\tif (IsLocalPlanCachingSupported(workerJob, originalDistributedPlan))\n\t{\n\t\tTask *task = linitial(workerJob->taskList);\n\n\t\t/*\n\t\t * We are going to execute this task locally. If it's not already in\n\t\t * the cache, create a local plan now and add it to the cache. During\n\t\t * execution, we will get the plan from the cache.\n\t\t *\n\t\t * The plan will be cached across executions when originalDistributedPlan\n\t\t * represents a prepared statement.\n\t\t */\n\t\tCacheLocalPlanForShardQuery(task, originalDistributedPlan,\n\t\t\t\t\t\t\t\t\testate->es_param_list_info);\n\t}\n}\n\n\n/*\n * CitusBeginModifyScan prepares the scan state for a modification.\n *\n * Modifications are special because:\n * a) we evaluate function calls (e.g. nextval) here and the outcome may\n *    determine which shards are affected by this query.\n * b) we need to take metadata locks to make sure no write is left behind\n *    when finalizing a shard move.\n */\nstatic void\nCitusBeginModifyScan(CustomScanState *node, EState *estate, int eflags)\n{\n\tCitusScanState *scanState = (CitusScanState *) node;\n\tPlanState *planState = &(scanState->customScanState.ss.ps);\n\tDistributedPlan *originalDistributedPlan = scanState->distributedPlan;\n\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"CitusBeginModifyScan\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\tDistributedPlan *currentPlan =\n\t\tCopyDistributedPlanWithoutCache(originalDistributedPlan);\n\tscanState->distributedPlan = currentPlan;\n\n\tJob *workerJob = currentPlan->workerJob;\n\n\tQuery *jobQuery = workerJob->jobQuery;\n\n\tif (ModifyJobNeedsEvaluation(workerJob))\n\t{\n\t\tExecuteCoordinatorEvaluableExpressions(jobQuery, planState);\n\n\t\t/* job query no longer has parameters, so we should not send any */\n\t\tworkerJob->parametersInJobQueryResolved = true;\n\t}\n\n\tif (workerJob->deferredPruning)\n\t{\n\t\t/*\n\t\t * At this point, we're about to do the shard pruning for fast-path queries.\n\t\t * Given that pruning is deferred always for INSERTs, we get here\n\t\t * !EnableFastPathRouterPlanner  as well. Given that INSERT statements with\n\t\t * CTEs/sublinks etc are not eligible for fast-path router plan, we get here\n\t\t * jobQuery->commandType == CMD_INSERT as well.\n\t\t */\n\t\tAssert(currentPlan->fastPathRouterPlan || !EnableFastPathRouterPlanner ||\n\t\t\t   jobQuery->commandType == CMD_INSERT);\n\n\t\t/*\n\t\t * We can only now decide which shard to use, so we need to build a new task\n\t\t * list.\n\t\t */\n\t\tif (jobQuery->commandType == CMD_INSERT)\n\t\t{\n\t\t\tRegenerateTaskListForInsert(workerJob);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tRegenerateTaskForFasthPathQuery(workerJob);\n\t\t}\n\t}\n\telse if (workerJob->requiresCoordinatorEvaluation)\n\t{\n\t\t/*\n\t\t * When there is no deferred pruning, but we did evaluate functions, then\n\t\t * we only rebuild the query strings in the existing tasks.\n\t\t *\n\t\t * Note that because we did evaluate functions we should also\n\t\t * sanity check the query first, to prevent issues like #8198\n\t\t * where 'false IN (SELECT ..)' is constant folded to 'NOT\n\t\t * (SELECT ..)' triggering an assert in ruleutils.\n\t\t */\n\n\t\tCheckQueryDeparseSafety(workerJob->jobQuery);\n\n\t\tRebuildQueryStrings(workerJob);\n\t}\n\n\n\t/* We skip shard related things if the job contains only local tables */\n\tif (!ModifyLocalTableJob(workerJob))\n\t{\n\t\t/*\n\t\t * Now that we know the shard ID(s) we can acquire the necessary shard metadata\n\t\t * locks. Once we have the locks it's safe to load the placement metadata.\n\t\t */\n\n\t\t/* prevent concurrent placement changes */\n\t\tAcquireMetadataLocks(workerJob->taskList);\n\n\t\t/*\n\t\t * In case of a split, the shard might no longer be available. In that\n\t\t * case try to reroute. We can only do this for fast path queries.\n\t\t */\n\t\tif (currentPlan->fastPathRouterPlan &&\n\t\t\t!AnchorShardsInTaskListExist(workerJob->taskList))\n\t\t{\n\t\t\tTryToRerouteFastPathModifyQuery(workerJob);\n\t\t}\n\n\t\t/* ensure there is no invalid shard */\n\t\tEnsureAnchorShardsInJobExist(workerJob);\n\n\t\t/* modify tasks are always assigned using first-replica policy */\n\t\tworkerJob->taskList = FirstReplicaAssignTaskList(workerJob->taskList);\n\t}\n\n\n\t/*\n\t * Now that we have populated the task placements we can determine whether\n\t * any of them are local to this node and cache a plan if needed.\n\t */\n\tif (IsLocalPlanCachingSupported(workerJob, originalDistributedPlan))\n\t{\n\t\tTask *task = linitial(workerJob->taskList);\n\n\t\t/*\n\t\t * We are going to execute this task locally. If it's not already in\n\t\t * the cache, create a local plan now and add it to the cache. During\n\t\t * execution, we will get the plan from the cache.\n\t\t *\n\t\t * WARNING: In this function we'll use the original plan with the original\n\t\t * query tree, meaning parameters and function calls are back and we'll\n\t\t * redo evaluation in the local (Postgres) executor. The reason we do this\n\t\t * is that we only need to cache one generic plan per shard.\n\t\t *\n\t\t * The plan will be cached across executions when originalDistributedPlan\n\t\t * represents a prepared statement.\n\t\t */\n\t\tCacheLocalPlanForShardQuery(task, originalDistributedPlan,\n\t\t\t\t\t\t\t\t\testate->es_param_list_info);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * TryToRerouteFastPathModifyQuery tries to reroute non-existent shards in given job if it finds any such shard,\n * only for fastpath queries.\n *\n * Should only be called if the job belongs to a fastpath modify query\n */\nstatic void\nTryToRerouteFastPathModifyQuery(Job *job)\n{\n\tif (job->jobQuery->commandType == CMD_INSERT)\n\t{\n\t\tRegenerateTaskListForInsert(job);\n\t}\n\telse\n\t{\n\t\tRegenerateTaskForFasthPathQuery(job);\n\t\tRebuildQueryStrings(job);\n\t}\n}\n\n\n/*\n * EnsureAnchorShardsInJobExist ensures all shards are valid in job.\n * If it finds a non-existent shard in given job, it throws an error.\n */\nstatic void\nEnsureAnchorShardsInJobExist(Job *job)\n{\n\tif (!AnchorShardsInTaskListExist(job->taskList))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),\n\t\t\t\t\t\terrmsg(\"shard for the given value does not exist\"),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"A concurrent shard split may have moved the data into a new set of shards.\"),\n\t\t\t\t\t\terrhint(\"Retry the query.\")));\n\t}\n}\n\n\n/*\n * AnchorShardsInTaskListExist checks whether all the anchor shards in the task list\n * still exist.\n */\nstatic bool\nAnchorShardsInTaskListExist(List *taskList)\n{\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tif (!ShardExists(task->anchorShardId))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ModifyJobNeedsEvaluation checks whether the functions and parameters in the job query\n * need to be evaluated before we can build task query strings.\n */\nstatic bool\nModifyJobNeedsEvaluation(Job *workerJob)\n{\n\tif (workerJob->requiresCoordinatorEvaluation)\n\t{\n\t\t/* query contains functions that need to be evaluated on the coordinator */\n\t\treturn true;\n\t}\n\n\tif (workerJob->partitionKeyValue != NULL)\n\t{\n\t\t/* the value of the distribution column is already known */\n\t\treturn false;\n\t}\n\n\t/* pruning was deferred due to a parameter in the partition column */\n\treturn workerJob->deferredPruning;\n}\n\n\n/*\n * CopyDistributedPlanWithoutCache is a helper function which copies the\n * distributedPlan into the current memory context.\n *\n * We must not change the distributed plan since it may be reused across multiple\n * executions of a prepared statement. Instead we create a deep copy that we only\n * use for the current execution.\n *\n * We also exclude localPlannedStatements from the copyObject call for performance\n * reasons, as they are immutable, so no need to have a deep copy.\n */\nstatic DistributedPlan *\nCopyDistributedPlanWithoutCache(DistributedPlan *originalDistributedPlan)\n{\n\tList *localPlannedStatements =\n\t\toriginalDistributedPlan->workerJob->localPlannedStatements;\n\toriginalDistributedPlan->workerJob->localPlannedStatements = NIL;\n\n\tDistributedPlan *distributedPlan = copyObject(originalDistributedPlan);\n\n\t/* set back the immutable field */\n\toriginalDistributedPlan->workerJob->localPlannedStatements = localPlannedStatements;\n\tdistributedPlan->workerJob->localPlannedStatements = localPlannedStatements;\n\n\treturn distributedPlan;\n}\n\n\n/*\n * RegenerateTaskListForInsert does the shard pruning for an INSERT query\n * queries and rebuilds the query strings.\n */\nstatic void\nRegenerateTaskListForInsert(Job *workerJob)\n{\n\tQuery *jobQuery = workerJob->jobQuery;\n\tbool parametersInJobQueryResolved = workerJob->parametersInJobQueryResolved;\n\tDeferredErrorMessage *planningError = NULL;\n\n\t/* need to perform shard pruning, rebuild the task list from scratch */\n\tList *taskList = RouterInsertTaskList(jobQuery, parametersInJobQueryResolved,\n\t\t\t\t\t\t\t\t\t\t  &planningError);\n\tif (planningError != NULL)\n\t{\n\t\tRaiseDeferredError(planningError, ERROR);\n\t}\n\n\tworkerJob->taskList = taskList;\n\n\tif (workerJob->partitionKeyValue == NULL)\n\t{\n\t\t/*\n\t\t * If we were not able to determine the partition key value in the planner,\n\t\t * take another shot now. It may still be NULL in case of a multi-row\n\t\t * insert.\n\t\t */\n\t\tworkerJob->partitionKeyValue = ExtractInsertPartitionKeyValue(jobQuery);\n\t}\n\n\tRebuildQueryStrings(workerJob);\n}\n\n\n/*\n * RegenerateTaskForFasthPathQuery does the shard pruning for\n * UPDATE/DELETE/SELECT fast path router queries and rebuilds the query strings.\n */\nstatic void\nRegenerateTaskForFasthPathQuery(Job *workerJob)\n{\n\tbool isMultiShardQuery = false;\n\tList *shardIntervalList =\n\t\tTargetShardIntervalForFastPathQuery(workerJob->jobQuery,\n\t\t\t\t\t\t\t\t\t\t\t&isMultiShardQuery, NULL,\n\t\t\t\t\t\t\t\t\t\t\t&workerJob->partitionKeyValue);\n\n\t/*\n\t * A fast-path router query can only yield multiple shards when the parameter\n\t * cannot be resolved properly, which can be triggered by SQL function.\n\t */\n\tif (isMultiShardQuery)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot perform distributed planning on this \"\n\t\t\t\t\t\t\t   \"query because parameterized queries for SQL \"\n\t\t\t\t\t\t\t   \"functions referencing distributed tables are \"\n\t\t\t\t\t\t\t   \"not supported\"),\n\t\t\t\t\t\terrhint(\"Consider using PL/pgSQL functions instead.\")));\n\t}\n\n\tbool shardsPresent = false;\n\tList *relationShardList =\n\t\tRelationShardListForShardIntervalList(shardIntervalList, &shardsPresent);\n\n\tUpdateRelationToShardNames((Node *) workerJob->jobQuery, relationShardList);\n\n\t/* fast path queries cannot have local tables */\n\tbool hasLocalRelation = false;\n\n\tList *placementList =\n\t\tCreateTaskPlacementListForShardIntervals(shardIntervalList, shardsPresent, true,\n\t\t\t\t\t\t\t\t\t\t\t\t hasLocalRelation);\n\tuint64 shardId = INVALID_SHARD_ID;\n\n\tif (shardsPresent)\n\t{\n\t\tshardId = GetAnchorShardId(shardIntervalList);\n\t}\n\n\tbool isLocalTableModification = false;\n\tbool delayedFastPath = false;\n\tGenerateSingleShardRouterTaskList(workerJob,\n\t\t\t\t\t\t\t\t\t  relationShardList,\n\t\t\t\t\t\t\t\t\t  placementList,\n\t\t\t\t\t\t\t\t\t  shardId,\n\t\t\t\t\t\t\t\t\t  isLocalTableModification,\n\t\t\t\t\t\t\t\t\t  delayedFastPath);\n}\n\n\n/*\n * AdaptiveExecutorCreateScan creates the scan state for the adaptive executor.\n */\nstatic Node *\nAdaptiveExecutorCreateScan(CustomScan *scan)\n{\n\tCitusScanState *scanState = palloc0(sizeof(CitusScanState));\n\n\tscanState->executorType = MULTI_EXECUTOR_ADAPTIVE;\n\tscanState->customScanState.ss.ps.type = T_CustomScanState;\n\tscanState->distributedPlan = GetDistributedPlan(scan);\n\n\tscanState->customScanState.methods = &AdaptiveExecutorCustomExecMethods;\n\tscanState->PreExecScan = &CitusPreExecScan;\n\n\tscanState->finishedPreScan = false;\n\tscanState->finishedRemoteScan = false;\n\n\treturn (Node *) scanState;\n}\n\n\n/*\n * NonPushableInsertSelectCrateScan creates the scan state for executing\n * INSERT..SELECT into a distributed table via the coordinator.\n */\nstatic Node *\nNonPushableInsertSelectCreateScan(CustomScan *scan)\n{\n\tCitusScanState *scanState = palloc0(sizeof(CitusScanState));\n\n\tscanState->executorType = MULTI_EXECUTOR_NON_PUSHABLE_INSERT_SELECT;\n\tscanState->customScanState.ss.ps.type = T_CustomScanState;\n\tscanState->distributedPlan = GetDistributedPlan(scan);\n\n\tscanState->customScanState.methods =\n\t\t&NonPushableInsertSelectCustomExecMethods;\n\n\tscanState->finishedPreScan = false;\n\tscanState->finishedRemoteScan = false;\n\n\treturn (Node *) scanState;\n}\n\n\n/*\n * DelayedErrorCreateScan is only called if we could not plan for the given\n * query. This is the case when a plan is not ready for execution because\n * CreateDistributedPlan() couldn't find a plan due to unresolved prepared\n * statement parameters, but didn't error out, because we expect custom plans\n * to come to our rescue. But sql (not plpgsql) functions unfortunately don't\n * go through a codepath supporting custom plans. Here, we error out with this\n * delayed error message.\n */\nstatic Node *\nDelayedErrorCreateScan(CustomScan *scan)\n{\n\tDistributedPlan *distributedPlan = GetDistributedPlan(scan);\n\n\t/* raise the deferred error */\n\tRaiseDeferredError(distributedPlan->planningError, ERROR);\n\n\treturn NULL;\n}\n\n\n/*\n * NonPushableMergeCommandCreateScan creates the scan state for executing\n * MERGE INTO ... into a distributed table with repartition of source rows.\n */\nstatic Node *\nNonPushableMergeCommandCreateScan(CustomScan *scan)\n{\n\tCitusScanState *scanState = palloc0(sizeof(CitusScanState));\n\n\tscanState->executorType = MULTI_EXECUTOR_NON_PUSHABLE_MERGE_QUERY;\n\tscanState->customScanState.ss.ps.type = T_CustomScanState;\n\tscanState->distributedPlan = GetDistributedPlan(scan);\n\tscanState->customScanState.methods = &NonPushableMergeCommandCustomExecMethods;\n\tscanState->finishedPreScan = false;\n\tscanState->finishedRemoteScan = false;\n\n\treturn (Node *) scanState;\n}\n\n\n/*\n * CitusEndScan is used to clean up tuple store of the given custom scan state.\n */\nstatic void\nCitusEndScan(CustomScanState *node)\n{\n\tCitusScanState *scanState = (CitusScanState *) node;\n\tJob *workerJob = scanState->distributedPlan->workerJob;\n\tuint64 queryId = scanState->distributedPlan->queryId;\n\tMultiExecutorType executorType = scanState->executorType;\n\tConst *partitionKeyConst = NULL;\n\tchar *partitionKeyString = NULL;\n\n\t/* stop propagating notices */\n\tDisableWorkerMessagePropagation();\n\n\t/*\n\t * Check whether we received warnings that should not have been\n\t * ignored.\n\t */\n\tErrorIfWorkerErrorIndicationReceived();\n\n\tif (workerJob != NULL)\n\t{\n\t\tpartitionKeyConst = workerJob->partitionKeyValue;\n\t}\n\n\t/*\n\t * queryId is not set if pg_stat_statements is not installed,\n\t * it can be set with as of pg14: set compute_query_id to on;\n\t */\n\tif (queryId != 0)\n\t{\n\t\tif (partitionKeyConst != NULL && executorType == MULTI_EXECUTOR_ADAPTIVE)\n\t\t{\n\t\t\tpartitionKeyString = DatumToString(partitionKeyConst->constvalue,\n\t\t\t\t\t\t\t\t\t\t\t   partitionKeyConst->consttype);\n\t\t}\n\n\t\t/* queries without partition key are also recorded */\n\t\tCitusQueryStatsExecutorsEntry(queryId, executorType, partitionKeyString);\n\t}\n\n\tif (scanState->tuplestorestate)\n\t{\n\t\ttuplestore_end(scanState->tuplestorestate);\n\t\tscanState->tuplestorestate = NULL;\n\t}\n}\n\n\n/*\n * CitusReScan is not normally called, except in certain cases of\n * DECLARE .. CURSOR WITH HOLD ..\n */\nstatic void\nCitusReScan(CustomScanState *node)\n{\n\tif (node->ss.ps.ps_ResultTupleSlot)\n\t{\n\t\tExecClearTuple(node->ss.ps.ps_ResultTupleSlot);\n\t}\n\tExecScanReScan(&node->ss);\n\n\tCitusScanState *scanState = (CitusScanState *) node;\n\tif (scanState->tuplestorestate)\n\t{\n\t\ttuplestore_rescan(scanState->tuplestorestate);\n\t}\n}\n\n\n/*\n * ScanStateGetTupleDescriptor returns the tuple descriptor for the given\n * scan state.\n */\nTupleDesc\nScanStateGetTupleDescriptor(CitusScanState *scanState)\n{\n\treturn scanState->customScanState.ss.ss_ScanTupleSlot->tts_tupleDescriptor;\n}\n\n\n/*\n * ScanStateGetExecutorState returns the executor state for the given scan\n * state.\n */\nEState *\nScanStateGetExecutorState(CitusScanState *scanState)\n{\n\treturn scanState->customScanState.ss.ps.state;\n}\n\n\n/*\n * FetchCitusCustomScanIfExists traverses a given plan and returns a Citus CustomScan\n * if it has any.\n */\nCustomScan *\nFetchCitusCustomScanIfExists(Plan *plan)\n{\n\tif (plan == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (IsCitusCustomScan(plan))\n\t{\n\t\treturn (CustomScan *) plan;\n\t}\n\n\tCustomScan *customScan = FetchCitusCustomScanIfExists(plan->lefttree);\n\n\tif (customScan == NULL)\n\t{\n\t\tcustomScan = FetchCitusCustomScanIfExists(plan->righttree);\n\t}\n\n\treturn customScan;\n}\n\n\n/*\n * IsCitusPlan returns whether a Plan contains a CustomScan generated by Citus\n * by recursively walking through the plan tree.\n */\nbool\nIsCitusPlan(Plan *plan)\n{\n\tif (plan == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsCitusCustomScan(plan))\n\t{\n\t\treturn true;\n\t}\n\n\treturn IsCitusPlan(plan->lefttree) || IsCitusPlan(plan->righttree);\n}\n\n\n/*\n * IsCitusCustomScan returns whether Plan node is a CustomScan generated by Citus.\n */\nbool\nIsCitusCustomScan(Plan *plan)\n{\n\tif (plan == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (!IsA(plan, CustomScan))\n\t{\n\t\treturn false;\n\t}\n\n\tCustomScan *customScan = (CustomScan *) plan;\n\tif (list_length(customScan->custom_private) == 0)\n\t{\n\t\treturn false;\n\t}\n\n\tNode *privateNode = (Node *) linitial(customScan->custom_private);\n\tif (!CitusIsA(privateNode, DistributedPlan))\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * In a Job, given a list of relations, if all them belong to the same\n * colocation group, the Job's colocation ID is set to the group ID, else,\n * it will be set to INVALID_COLOCATION_ID.\n */\nvoid\nSetJobColocationId(Job *job)\n{\n\tuint32 jobColocationId = INVALID_COLOCATION_ID;\n\n\tList *rangeTableList = ExtractRangeTableEntryList(job->jobQuery);\n\tListCell *rangeTableCell = NULL;\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\t\tOid relationId = rangeTableEntry->relid;\n\n\t\tif (!IsCitusTable(relationId))\n\t\t{\n\t\t\t/* ignore the non distributed table */\n\t\t\tcontinue;\n\t\t}\n\n\t\tuint32 colocationId = TableColocationId(relationId);\n\n\t\tif (jobColocationId == INVALID_COLOCATION_ID)\n\t\t{\n\t\t\t/* Initialize the ID */\n\t\t\tjobColocationId = colocationId;\n\t\t}\n\t\telse if (jobColocationId != colocationId)\n\t\t{\n\t\t\t/* Tables' colocationId is not the same */\n\t\t\tjobColocationId = INVALID_COLOCATION_ID;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tjob->colocationId = jobColocationId;\n}\n\n\n/*\n * Any function with force_delegate flag(true) must ensure that the Job's\n * partition key match with the functions' distribution argument.\n */\nstatic void\nEnsureForceDelegationDistributionKey(Job *job)\n{\n\t/* If the Job has the subquery, punt the shard-key-check to the subquery */\n\tif (job->subqueryPushdown)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * If the query doesn't have shard key, nothing to check, only exception is when\n\t * the query doesn't have distributed tables but an RTE with intermediate_results\n\t * function (a subquery plan).\n\t */\n\tif (!job->partitionKeyValue)\n\t{\n\t\tbool queryContainsDistributedTable =\n\t\t\tFindNodeMatchingCheckFunction((Node *) job->jobQuery, IsDistributedTableRTE);\n\n\t\tif (!queryContainsDistributedTable)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/* We should match both the key and the colocation ID */\n\tSetJobColocationId(job);\n\n\tif (!IsShardKeyValueAllowed(job->partitionKeyValue, job->colocationId))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"queries must filter by the distribution argument in the same \"\n\t\t\t\t\t\t\t\"colocation group when using the forced function pushdown\"),\n\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\"consider disabling forced delegation through \"\n\t\t\t\t\t\t\t\"create_distributed_table(..., force_delegation := false)\")));\n\t}\n}\n\n\n/*\n * CheckDeparseWalker is used to walk over an expression tree and ensure\n * that any SubLink testexpr is safe to deparse after coordinator-side\n * evaluation.\n *\n * Specifically, we convert any non-OpExpr testexpr into an OpExpr of the\n * form TRUE = testexpr, and NOT expressions into FALSE = arg. If the deparser\n * sees something other than an OpExpr in a SubLink testexpr, it will\n * raise an assertion failure or error out.\n */\nstatic bool\nCheckDeparseWalker(Node *expr, void *context)\n{\n\tif (expr == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(expr, SubLink))\n\t{\n\t\tSubLink *sublink = (SubLink *) expr;\n\t\tNode *testexpr = sublink->testexpr;\n\n\t\tif (testexpr && IsA(testexpr, BoolExpr) && ((BoolExpr *) testexpr)->boolop ==\n\t\t\tNOT_EXPR)\n\t\t{\n\t\t\tNode *arg = linitial(((BoolExpr *) testexpr)->args);\n\n\t\t\t/*\n\t\t\t * testexpr is of the form: NOT (arg)\n\t\t\t * convert NOT (arg) to FALSE = arg\n\t\t\t */\n\t\t\tsublink->testexpr = (Node *) make_opclause(BooleanEqualOperator, BOOLOID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   (Expr *) makeBoolConst(false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  false),\n\t\t\t\t\t\t\t\t\t\t\t\t\t   (Expr *) arg,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   InvalidOid, InvalidOid);\n\t\t}\n\t\telse if (testexpr && !IsA(testexpr, OpExpr))\n\t\t{\n\t\t\t/*\n\t\t\t * testexpr is something other than an OpExpr\n\t\t\t * convert testexpr to TRUE = testexpr\n\t\t\t */\n\t\t\tsublink->testexpr = (Node *) make_opclause(BooleanEqualOperator, BOOLOID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   (Expr *) makeBoolConst(true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  false),\n\t\t\t\t\t\t\t\t\t\t\t\t\t   (Expr *) testexpr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   InvalidOid, InvalidOid);\n\t\t}\n\t}\n\n\treturn expression_tree_walker(expr, CheckDeparseWalker, NULL);\n}\n\n\n/*\n * CheckQueryDeparseSafety is used by CitusBeginModifyScan to ensure that the\n * query's quals are safe to deparse after coordinator-required evaluation;\n * ExecuteCoordinatorEvaluableExpressions() may have reduced or constant-folded\n * expressions in a way that could trigger asserts in the deparser.\n *\n * Note that this is only needed for UPDATE and DELTE queries, since INSERTs always\n * use deferred pruning. So we just need to check the query's quals.\n */\nstatic\nvoid\nCheckQueryDeparseSafety(Query *query)\n{\n\tCheckDeparseWalker(query->jointree->quals, NULL);\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/directed_acyclic_graph_execution.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * directed_acyclic_graph_execution_logic.c\n *\n * Logic to run tasks in their dependency order.\n *\n * Copyright (c) Citus Data, Inc.\n */\n\n#include \"postgres.h\"\n\n#include \"access/hash.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/directed_acyclic_graph_execution.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/transmit.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_transaction.h\"\n\ntypedef struct TaskHashKey\n{\n\tuint64 jobId;\n\tuint32 taskId;\n\n\t/*\n\t * The padding field is needed to make sure the struct contains no\n\t * automatic padding, which is not allowed for hashmap keys.\n\t */\n\tuint32 padding;\n}TaskHashKey;\n\ntypedef struct TaskHashEntry\n{\n\tTaskHashKey key;\n\tTask *task;\n}TaskHashEntry;\n\nstatic bool IsAllDependencyCompleted(Task *task, HTAB *completedTasks);\nstatic void AddCompletedTasks(List *curCompletedTasks, HTAB *completedTasks);\nstatic List * FindExecutableTasks(List *allTasks, HTAB *completedTasks);\nstatic List * RemoveMergeTasks(List *taskList);\nstatic bool IsTaskAlreadyCompleted(Task *task, HTAB *completedTasks);\n\n/*\n * ExecuteTasksInDependencyOrder executes the given tasks except the excluded\n * tasks in their dependency order. To do so, it iterates all\n * the tasks and finds the ones that can be executed at that time, it tries to\n * execute all of them in parallel. The parallelism is bound by MaxAdaptiveExecutorPoolSize.\n */\nvoid\nExecuteTasksInDependencyOrder(List *allTasks, List *excludedTasks, List *jobIds)\n{\n\tassert_valid_hash_key3(TaskHashKey, jobId, taskId, padding);\n\tHTAB *completedTasks = CreateSimpleHash(TaskHashKey, TaskHashEntry);\n\n\t/* We only execute depended jobs' tasks, therefore to not execute */\n\t/* top level tasks, we add them to the completedTasks. */\n\tAddCompletedTasks(excludedTasks, completedTasks);\n\twhile (true)\n\t{\n\t\tList *curTasks = FindExecutableTasks(allTasks, completedTasks);\n\t\tif (list_length(curTasks) == 0)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t/* merge tasks do not need to be executed */\n\t\tList *executableTasks = RemoveMergeTasks(curTasks);\n\t\tif (list_length(executableTasks) > 0)\n\t\t{\n\t\t\tExecuteTaskList(ROW_MODIFY_NONE, executableTasks);\n\t\t}\n\n\t\tAddCompletedTasks(curTasks, completedTasks);\n\t\tcurTasks = NIL;\n\t}\n}\n\n\n/*\n * FindExecutableTasks finds the tasks that can be executed currently,\n * which means that all of their dependencies are executed. If a task\n * is already executed, it is not added to the result.\n */\nstatic List *\nFindExecutableTasks(List *allTasks, HTAB *completedTasks)\n{\n\tList *curTasks = NIL;\n\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, allTasks)\n\t{\n\t\tif (IsAllDependencyCompleted(task, completedTasks) &&\n\t\t\t!IsTaskAlreadyCompleted(task, completedTasks))\n\t\t{\n\t\t\tcurTasks = lappend(curTasks, task);\n\t\t}\n\t}\n\n\treturn curTasks;\n}\n\n\n/*\n * RemoveMergeTasks returns a copy of taskList that excludes all the\n * merge tasks. We do this because merge tasks are currently only a\n * logical concept that does not need to be executed.\n */\nstatic List *\nRemoveMergeTasks(List *taskList)\n{\n\tList *prunedTaskList = NIL;\n\tTask *task = NULL;\n\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tif (task->taskType != MERGE_TASK)\n\t\t{\n\t\t\tprunedTaskList = lappend(prunedTaskList, task);\n\t\t}\n\t}\n\n\treturn prunedTaskList;\n}\n\n\n/*\n * AddCompletedTasks adds the givens tasks to completedTasks HTAB.\n */\nstatic void\nAddCompletedTasks(List *curCompletedTasks, HTAB *completedTasks)\n{\n\tbool found;\n\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, curCompletedTasks)\n\t{\n\t\tTaskHashKey taskKey = { task->jobId, task->taskId };\n\t\thash_search(completedTasks, &taskKey, HASH_ENTER, &found);\n\t}\n}\n\n\n/*\n * IsTaskAlreadyCompleted returns true if the given task\n * is found in the completedTasks HTAB.\n */\nstatic bool\nIsTaskAlreadyCompleted(Task *task, HTAB *completedTasks)\n{\n\tbool found;\n\n\tTaskHashKey taskKey = { task->jobId, task->taskId };\n\thash_search(completedTasks, &taskKey, HASH_ENTER, &found);\n\treturn found;\n}\n\n\n/*\n * IsAllDependencyCompleted return true if the given task's\n * dependencies are completed.\n */\nstatic bool\nIsAllDependencyCompleted(Task *targetTask, HTAB *completedTasks)\n{\n\tbool found = false;\n\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, targetTask->dependentTaskList)\n\t{\n\t\tTaskHashKey taskKey = { task->jobId, task->taskId };\n\n\t\thash_search(completedTasks, &taskKey, HASH_FIND, &found);\n\t\tif (!found)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/distributed_execution_locks.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * distributed_execution_locks.c\n *\n * Definitions of the functions used in executing distributed\n * execution locking.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/distributed_execution_locks.h\"\n#include \"distributed/executor_util.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/transaction_management.h\"\n\n\n/*\n * AcquireExecutorShardLocksForExecution acquires advisory lock on shard IDs\n * to prevent unsafe concurrent modifications of shards.\n *\n * We prevent concurrent modifications of shards in two cases:\n * 1. Any non-commutative writes to a replicated table\n * 2. Multi-shard writes that are executed in parallel\n *\n * The first case ensures we do not apply updates in different orders on\n * different replicas (e.g. of a reference table), which could lead the\n * replicas to diverge.\n *\n * The second case prevents deadlocks due to out-of-order execution.\n *\n * There are two GUCs that can override the default behaviors.\n *  'citus.all_modifications_commutative' relaxes locking\n *  that's done for the purpose of keeping replicas consistent.\n *  'citus.enable_deadlock_prevention' relaxes locking done for\n *  the purpose of avoiding deadlocks between concurrent\n *  multi-shard commands.\n *\n * We do not take executor shard locks for utility commands such as\n * TRUNCATE because the table locks already prevent concurrent access.\n */\nvoid\nAcquireExecutorShardLocksForExecution(RowModifyLevel modLevel, List *taskList)\n{\n\tif (modLevel <= ROW_MODIFY_READONLY &&\n\t\t!SelectForUpdateOnReferenceTable(taskList))\n\t{\n\t\t/*\n\t\t * Executor locks only apply to DML commands and SELECT FOR UPDATE queries\n\t\t * touching reference tables.\n\t\t */\n\t\treturn;\n\t}\n\n\tbool requiresParallelExecutionLocks =\n\t\t!(list_length(taskList) == 1 || ShouldRunTasksSequentially(taskList));\n\n\tbool modifiedTableReplicated = ModifiedTableReplicated(taskList);\n\tif (!modifiedTableReplicated && !requiresParallelExecutionLocks)\n\t{\n\t\t/*\n\t\t * When a distributed query on tables with replication\n\t\t * factor == 1 and command hits only a single shard, we\n\t\t * rely on Postgres to handle the serialization of the\n\t\t * concurrent modifications on the workers.\n\t\t *\n\t\t * For reference tables, even if their placements are replicated\n\t\t * ones (e.g., single node), we acquire the distributed execution\n\t\t * locks to be consistent when new node(s) are added. So, they\n\t\t * do not return at this point.\n\t\t */\n\t\treturn;\n\t}\n\n\t/*\n\t * We first assume that all the remaining modifications are going to\n\t * be serialized. So, start with an ExclusiveLock and lower the lock level\n\t * as much as possible.\n\t */\n\tint lockMode = ExclusiveLock;\n\n\t/*\n\t * In addition to honouring commutativity rules, we currently only\n\t * allow a single multi-shard command on a shard at a time. Otherwise,\n\t * concurrent multi-shard commands may take row-level locks on the\n\t * shard placements in a different order and create a distributed\n\t * deadlock. This applies even when writes are commutative and/or\n\t * there is no replication. This can be relaxed via\n\t * EnableDeadlockPrevention.\n\t *\n\t * 1. If citus.all_modifications_commutative is set to true, then all locks\n\t * are acquired as RowExclusiveLock.\n\t *\n\t * 2. If citus.all_modifications_commutative is false, then only the shards\n\t * with more than one replicas are locked with ExclusiveLock. Otherwise, the\n\t * lock is acquired with ShareUpdateExclusiveLock.\n\t *\n\t * ShareUpdateExclusiveLock conflicts with itself such that only one\n\t * multi-shard modification at a time is allowed on a shard. It also conflicts\n\t * with ExclusiveLock, which ensures that updates/deletes/upserts are applied\n\t * in the same order on all placements. It does not conflict with\n\t * RowExclusiveLock, which is normally obtained by single-shard, commutative\n\t * writes.\n\t */\n\tif (!modifiedTableReplicated && requiresParallelExecutionLocks)\n\t{\n\t\t/*\n\t\t * When there is no replication then we only need to prevent\n\t\t * concurrent multi-shard commands on the same shards. This is\n\t\t * because concurrent, parallel commands may modify the same\n\t\t * set of shards, but in different orders. The order of the\n\t\t * accesses might trigger distributed deadlocks that are not\n\t\t * possible to happen on non-distributed systems such\n\t\t * regular Postgres.\n\t\t *\n\t\t * As an example, assume that we have two queries: query-1 and query-2.\n\t\t * Both queries access shard-1 and shard-2. If query-1 first accesses to\n\t\t * shard-1 then shard-2, and query-2 accesses shard-2 then shard-1, these\n\t\t * two commands might block each other in case they modify the same rows\n\t\t * (e.g., cause distributed deadlocks).\n\t\t *\n\t\t * In either case, ShareUpdateExclusive has the desired effect, since\n\t\t * it conflicts with itself and ExclusiveLock (taken by non-commutative\n\t\t * writes).\n\t\t *\n\t\t * However, some users find this too restrictive, so we allow them to\n\t\t * reduce to a RowExclusiveLock when citus.enable_deadlock_prevention\n\t\t * is enabled, which lets multi-shard modifications run in parallel as\n\t\t * long as they all disable the GUC.\n\t\t */\n\t\tlockMode =\n\t\t\tEnableDeadlockPrevention ? ShareUpdateExclusiveLock : RowExclusiveLock;\n\n\t\tif (!IsCoordinator())\n\t\t{\n\t\t\t/*\n\t\t\t * We also skip taking a heavy-weight lock when running a multi-shard\n\t\t\t * commands from workers, since we currently do not prevent concurrency\n\t\t\t * across workers anyway.\n\t\t\t */\n\t\t\tlockMode = RowExclusiveLock;\n\t\t}\n\t}\n\telse if (modifiedTableReplicated)\n\t{\n\t\t/*\n\t\t * When we are executing distributed queries on replicated tables, our\n\t\t * default behaviour is to prevent any concurrency. This is valid\n\t\t * for when parallel execution is happening or not.\n\t\t *\n\t\t * The reason is that we cannot control the order of the placement accesses\n\t\t * of two distributed queries to the same shards. The order of the accesses\n\t\t * might cause the replicas of the same shard placements diverge. This is\n\t\t * not possible to happen on non-distributed systems such regular Postgres.\n\t\t *\n\t\t * As an example, assume that we have two queries: query-1 and query-2.\n\t\t * Both queries only access the placements of shard-1, say p-1 and p-2.\n\t\t *\n\t\t * And, assume that these queries are non-commutative, such as:\n\t\t *  query-1: UPDATE table SET b = 1 WHERE key = 1;\n\t\t *  query-2: UPDATE table SET b = 2 WHERE key = 1;\n\t\t *\n\t\t * If query-1 accesses to p-1 then p-2, and query-2 accesses\n\t\t * p-2 then p-1, these two commands would leave the p-1 and p-2\n\t\t * diverged (e.g., the values for the column \"b\" would be different).\n\t\t *\n\t\t * The only exception to this rule is the single shard commutative\n\t\t * modifications, such as INSERTs. In that case, we can allow\n\t\t * concurrency among such backends, hence lowering the lock level\n\t\t * to RowExclusiveLock.\n\t\t */\n\t\tif (!requiresParallelExecutionLocks && modLevel < ROW_MODIFY_NONCOMMUTATIVE)\n\t\t{\n\t\t\tlockMode = RowExclusiveLock;\n\t\t}\n\t}\n\n\tif (AllModificationsCommutative)\n\t{\n\t\t/*\n\t\t * The mapping is overridden when all_modifications_commutative is set to true.\n\t\t * In that case, all modifications are treated as commutative, which can be used\n\t\t * to communicate that the application is only generating commutative\n\t\t * UPDATE/DELETE/UPSERT commands and exclusive locks are unnecessary. This\n\t\t * is irrespective of single-shard/multi-shard or replicated tables.\n\t\t */\n\t\tlockMode = RowExclusiveLock;\n\t}\n\n\t/* now, iterate on the tasks and acquire the executor locks on the shards */\n\tList *anchorShardIntervalList = NIL;\n\tList *relationRowLockList = NIL;\n\tList *requiresConsistentSnapshotRelationShardList = NIL;\n\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tShardInterval *anchorShardInterval = LoadShardInterval(task->anchorShardId);\n\t\tanchorShardIntervalList = lappend(anchorShardIntervalList, anchorShardInterval);\n\n\t\t/* Acquire additional locks for SELECT .. FOR UPDATE on reference tables */\n\t\tAcquireExecutorShardLocksForRelationRowLockList(task->relationRowLockList);\n\n\t\trelationRowLockList =\n\t\t\tlist_concat(relationRowLockList,\n\t\t\t\t\t\ttask->relationRowLockList);\n\n\t\t/*\n\t\t * If the task has a subselect, then we may need to lock the shards from which\n\t\t * the query selects as well to prevent the subselects from seeing different\n\t\t * results on different replicas.\n\t\t */\n\t\tif (RequiresConsistentSnapshot(task))\n\t\t{\n\t\t\t/*\n\t\t\t * ExclusiveLock conflicts with all lock types used by modifications\n\t\t\t * and therefore prevents other modifications from running\n\t\t\t * concurrently.\n\t\t\t */\n\t\t\trequiresConsistentSnapshotRelationShardList =\n\t\t\t\tlist_concat(requiresConsistentSnapshotRelationShardList,\n\t\t\t\t\t\t\ttask->relationShardList);\n\t\t}\n\t}\n\n\t/*\n\t * Acquire the locks in a sorted way to avoid deadlocks due to lock\n\t * ordering across concurrent sessions.\n\t */\n\tanchorShardIntervalList =\n\t\tSortList(anchorShardIntervalList, CompareShardIntervalsById);\n\n\t/*\n\t * If we are dealing with a partition we are also taking locks on parent table\n\t * to prevent deadlocks on concurrent operations on a partition and its parent.\n\t *\n\t * Note that this function currently does not acquire any remote locks as that\n\t * is necessary to control the concurrency across multiple nodes for replicated\n\t * tables. That is because Citus currently does not allow modifications to\n\t * partitions from any node other than the coordinator.\n\t */\n\tLockParentShardResourceIfPartition(anchorShardIntervalList, lockMode);\n\n\t/* Acquire distribution execution locks on the affected shards */\n\tSerializeNonCommutativeWrites(anchorShardIntervalList, lockMode);\n\n\tif (relationRowLockList != NIL)\n\t{\n\t\t/* Acquire additional locks for SELECT .. FOR UPDATE on reference tables */\n\t\tAcquireExecutorShardLocksForRelationRowLockList(relationRowLockList);\n\t}\n\n\n\tif (requiresConsistentSnapshotRelationShardList != NIL)\n\t{\n\t\t/*\n\t\t * If the task has a subselect, then we may need to lock the shards from which\n\t\t * the query selects as well to prevent the subselects from seeing different\n\t\t * results on different replicas.\n\t\t *\n\t\t * ExclusiveLock conflicts with all lock types used by modifications\n\t\t * and therefore prevents other modifications from running\n\t\t * concurrently.\n\t\t */\n\t\tLockRelationShardResources(requiresConsistentSnapshotRelationShardList,\n\t\t\t\t\t\t\t\t   ExclusiveLock);\n\t}\n}\n\n\n/*\n * RequiresConsistentSnapshot returns true if the given task need to take\n * the necessary locks to ensure that a subquery in the modify query\n * returns the same output for all task placements.\n */\nbool\nRequiresConsistentSnapshot(Task *task)\n{\n\tbool requiresIsolation = false;\n\n\tif (!task->modifyWithSubquery)\n\t{\n\t\t/*\n\t\t * Other commands do not read from other shards.\n\t\t */\n\n\t\trequiresIsolation = false;\n\t}\n\telse if (list_length(task->taskPlacementList) == 1)\n\t{\n\t\t/*\n\t\t * If there is only one replica then we fully rely on PostgreSQL to\n\t\t * provide SELECT isolation. In this case, we do not provide isolation\n\t\t * across the shards, but that was never our intention.\n\t\t */\n\n\t\trequiresIsolation = false;\n\t}\n\telse if (AllModificationsCommutative)\n\t{\n\t\t/*\n\t\t * An INSERT/SELECT is commutative with other writes if it excludes\n\t\t * any ongoing writes based on the filter conditions. Without knowing\n\t\t * whether this is true, we assume the user took this into account\n\t\t * when enabling citus.all_modifications_commutative. This option\n\t\t * gives users an escape from aggressive locking during INSERT/SELECT.\n\t\t */\n\n\t\trequiresIsolation = false;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * If this is a non-commutative write, then we need to block ongoing\n\t\t * writes to make sure that the subselect returns the same result\n\t\t * on all placements.\n\t\t */\n\n\t\trequiresIsolation = true;\n\t}\n\n\treturn requiresIsolation;\n}\n\n\n/*\n * AcquireMetadataLocks acquires metadata locks on each of the anchor\n * shards in the task list to prevent a shard being modified while it\n * is being copied.\n */\nvoid\nAcquireMetadataLocks(List *taskList)\n{\n\t/*\n\t * Note: to avoid the overhead of additional sorting, we assume tasks\n\t * to be already sorted by shard ID such that deadlocks are avoided.\n\t * This is true for INSERT/SELECT, which is the only multi-shard\n\t * command right now.\n\t */\n\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tLockShardDistributionMetadata(task->anchorShardId, ShareLock);\n\t}\n}\n\n\nvoid\nAcquireExecutorShardLocksForRelationRowLockList(List *relationRowLockList)\n{\n\tLOCKMODE rowLockMode = NoLock;\n\n\tif (relationRowLockList == NIL)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * If lock clause exists and it affects any reference table, we need to get\n\t * lock on shard resource. Type of lock is determined by the type of row lock\n\t * given in the query. If the type of row lock is either FOR NO KEY UPDATE or\n\t * FOR UPDATE we get ExclusiveLock on shard resource. We get ShareLock if it\n\t * is FOR KEY SHARE or FOR KEY SHARE.\n\t *\n\t * We have selected these lock types according to conflict table given in the\n\t * Postgres documentation. It is given that FOR UPDATE and FOR NO KEY UPDATE\n\t * must be conflict with each other modify command. By getting ExlcusiveLock\n\t * we guarantee that. Note that, getting ExclusiveLock does not mimic the\n\t * behaviour of Postgres exactly. Getting row lock with FOR NO KEY UPDATE and\n\t * FOR KEY SHARE do not conflict in Postgres, yet they block each other in\n\t * our implementation. Since FOR SHARE and FOR KEY SHARE does not conflict\n\t * with each other but conflicts with modify commands, we get ShareLock for\n\t * them.\n\t */\n\tRelationRowLock *relationRowLock = NULL;\n\tforeach_declared_ptr(relationRowLock, relationRowLockList)\n\t{\n\t\tLockClauseStrength rowLockStrength = relationRowLock->rowLockStrength;\n\t\tOid relationId = relationRowLock->relationId;\n\n\t\tif (IsCitusTableType(relationId, REFERENCE_TABLE))\n\t\t{\n\t\t\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\n\t\t\tif (rowLockStrength == LCS_FORKEYSHARE || rowLockStrength == LCS_FORSHARE)\n\t\t\t{\n\t\t\t\trowLockMode = ShareLock;\n\t\t\t}\n\t\t\telse if (rowLockStrength == LCS_FORNOKEYUPDATE ||\n\t\t\t\t\t rowLockStrength == LCS_FORUPDATE)\n\t\t\t{\n\t\t\t\trowLockMode = ExclusiveLock;\n\t\t\t}\n\n\t\t\tSerializeNonCommutativeWrites(shardIntervalList, rowLockMode);\n\t\t}\n\t}\n}\n\n\n/*\n * LockPartitionsInRelationList iterates over given list and acquires locks on\n * partitions of each partitioned table. It does nothing for non-partitioned tables.\n */\nvoid\nLockPartitionsInRelationList(List *relationIdList, LOCKMODE lockmode)\n{\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tif (PartitionedTable(relationId))\n\t\t{\n\t\t\tLockPartitionRelations(relationId, lockmode);\n\t\t}\n\t}\n}\n\n\n/*\n * LockPartitionRelations acquires relation lock on all partitions of given\n * partitioned relation. This function expects that given relation is a\n * partitioned relation.\n */\nvoid\nLockPartitionRelations(Oid relationId, LOCKMODE lockMode)\n{\n\t/*\n\t * PartitionList function generates partition list in the same order\n\t * as PostgreSQL. Therefore we do not need to sort it before acquiring\n\t * locks.\n\t */\n\tList *partitionList = PartitionList(relationId);\n\tOid partitionRelationId = InvalidOid;\n\tforeach_declared_oid(partitionRelationId, partitionList)\n\t{\n\t\tLockRelationOid(partitionRelationId, lockMode);\n\t}\n}\n\n\n/*\n * LockPartitionsForDistributedPlan ensures commands take locks on all partitions\n * of a distributed table that appears in the query. We do this primarily out of\n * consistency with PostgreSQL locking.\n */\nvoid\nLockPartitionsForDistributedPlan(DistributedPlan *plan)\n{\n\tif (TaskListModifiesDatabase(plan->modLevel, plan->workerJob->taskList))\n\t{\n\t\tOid targetRelationId = plan->targetRelationId;\n\n\t\tLockPartitionsInRelationList(list_make1_oid(targetRelationId), RowExclusiveLock);\n\t}\n\n\t/*\n\t * Lock partitions of tables that appear in a SELECT or subquery. In the\n\t * DML case this also includes the target relation, but since we already\n\t * have a stronger lock this doesn't do any harm.\n\t */\n\tLockPartitionsInRelationList(plan->relationIdList, AccessShareLock);\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/distributed_intermediate_results.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * distributed_intermediate_results.c\n *   Functions for reading and writing distributed intermediate results.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n#include \"port.h\"\n\n#include \"access/htup_details.h\"\n#include \"access/tupdesc.h\"\n#include \"catalog/pg_type.h\"\n#include \"tcop/pquery.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/tuple_destination.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/*\n * PartitioningTupleDest is internal representation of a TupleDestination\n * which consumes queries constructed in WrapTasksForPartitioning.\n */\ntypedef struct PartitioningTupleDest\n{\n\tTupleDestination pub;\n\n\tCitusTableCacheEntry *targetRelation;\n\n\t/* MemoryContext in which we add new fragments */\n\tMemoryContext fragmentContext;\n\n\t/* list of DistributedResultFragment pointer */\n\tList *fragmentList;\n\n\t/* what do tuples look like */\n\tTupleDesc tupleDesc;\n} PartitioningTupleDest;\n\n\n/* forward declarations of local functions */\nstatic List * WrapTasksForPartitioning(const char *resultIdPrefix,\n\t\t\t\t\t\t\t\t\t   List *selectTaskList,\n\t\t\t\t\t\t\t\t\t   int partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t   CitusTableCacheEntry *targetRelation,\n\t\t\t\t\t\t\t\t\t   bool binaryFormat);\nstatic List * ExecutePartitionTaskList(List *partitionTaskList,\n\t\t\t\t\t\t\t\t\t   CitusTableCacheEntry *targetRelation);\nstatic PartitioningTupleDest * CreatePartitioningTupleDest(CitusTableCacheEntry *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   targetRelation);\nstatic void PartitioningTupleDestPutTuple(TupleDestination *self, Task *task,\n\t\t\t\t\t\t\t\t\t\t  int placementIndex, int queryNumber,\n\t\t\t\t\t\t\t\t\t\t  HeapTuple heapTuple, uint64 tupleLibpqSize);\nstatic TupleDesc PartitioningTupleDestTupleDescForQuery(TupleDestination *self, int\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tqueryNumber);\nstatic char * SourceShardPrefix(const char *resultPrefix, uint64 shardId);\nstatic DistributedResultFragment * TupleToDistributedResultFragment(HeapTuple heapTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tTupleDesc tupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCitusTableCacheEntry *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetRelation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tuint32 sourceNodeId);\nstatic void ExecuteSelectTasksIntoTupleDest(List *taskList,\n\t\t\t\t\t\t\t\t\t\t\tTupleDestination *tupleDestination,\n\t\t\t\t\t\t\t\t\t\t\tbool errorOnAnyFailure);\nstatic List ** ColocateFragmentsWithRelation(List *fragmentList,\n\t\t\t\t\t\t\t\t\t\t\t CitusTableCacheEntry *targetRelation);\nstatic List * ColocationTransfers(List *fragmentList,\n\t\t\t\t\t\t\t\t  CitusTableCacheEntry *targetRelation);\nstatic List * FragmentTransferTaskList(List *fragmentListTransfers);\nstatic void ExecuteFetchTaskList(List *fetchTaskList);\n\n\n/*\n * RedistributeTaskListResults partitions the results of given task list using\n * shard ranges and partition method of given targetRelation, and then colocates\n * the result files with shards.\n *\n * If a shard has a replication factor > 1, corresponding result files are copied\n * to all nodes containing that shard.\n *\n * returnValue[shardIndex] is list of cstrings each of which is a resultId which\n * correspond to targetRelation->sortedShardIntervalArray[shardIndex].\n *\n * partitionColumnIndex determines the column in the selectTaskList to use for\n * partitioning.\n */\nList **\nRedistributeTaskListResults(const char *resultIdPrefix, List *selectTaskList,\n\t\t\t\t\t\t\tint partitionColumnIndex,\n\t\t\t\t\t\t\tCitusTableCacheEntry *targetRelation,\n\t\t\t\t\t\t\tbool binaryFormat)\n{\n\t/*\n\t * Make sure that this transaction has a distributed transaction ID.\n\t *\n\t * Intermediate results will be stored in a directory that is derived\n\t * from the distributed transaction ID.\n\t */\n\tUseCoordinatedTransaction();\n\n\tList *fragmentList = PartitionTasklistResults(resultIdPrefix, selectTaskList,\n\t\t\t\t\t\t\t\t\t\t\t\t  partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t  targetRelation, binaryFormat);\n\treturn ColocateFragmentsWithRelation(fragmentList, targetRelation);\n}\n\n\n/*\n * PartitionTasklistResults executes the given task list, and partitions results\n * of each task based on targetRelation's distribution method and intervals.\n * Each of the result partitions are stored in the node where task was executed,\n * and are named as $resultIdPrefix_from_$sourceShardId_to_$targetShardIndex.\n *\n * Result is list of DistributedResultFragment, each of which represents a\n * partition of results. Empty results are omitted. Therefore, if we have N tasks\n * and target relation has M shards, we will have NxM-(number of empty results)\n * fragments.\n *\n * partitionColumnIndex determines the column in the selectTaskList to use for\n * partitioning.\n */\nList *\nPartitionTasklistResults(const char *resultIdPrefix, List *selectTaskList,\n\t\t\t\t\t\t int partitionColumnIndex,\n\t\t\t\t\t\t CitusTableCacheEntry *targetRelation,\n\t\t\t\t\t\t bool binaryFormat)\n{\n\tif (!IsCitusTableTypeCacheEntry(targetRelation, HASH_DISTRIBUTED) &&\n\t\t!IsCitusTableTypeCacheEntry(targetRelation, RANGE_DISTRIBUTED))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"repartitioning results of a tasklist is only supported \"\n\t\t\t\t\t\t\t   \"when target relation is hash or range partitioned.\")));\n\t}\n\n\t/*\n\t * Make sure that this transaction has a distributed transaction ID.\n\t *\n\t * Intermediate results will be stored in a directory that is derived\n\t * from the distributed transaction ID.\n\t */\n\tUseCoordinatedTransaction();\n\n\tselectTaskList = WrapTasksForPartitioning(resultIdPrefix, selectTaskList,\n\t\t\t\t\t\t\t\t\t\t\t  partitionColumnIndex, targetRelation,\n\t\t\t\t\t\t\t\t\t\t\t  binaryFormat);\n\treturn ExecutePartitionTaskList(selectTaskList, targetRelation);\n}\n\n\n/*\n * WrapTasksForPartitioning wraps the query for each of the tasks by a call\n * to worker_partition_query_result(). Target list of the wrapped query should\n * match the tuple descriptor in ExecutePartitionTaskList().\n */\nstatic List *\nWrapTasksForPartitioning(const char *resultIdPrefix, List *selectTaskList,\n\t\t\t\t\t\t int partitionColumnIndex,\n\t\t\t\t\t\t CitusTableCacheEntry *targetRelation,\n\t\t\t\t\t\t bool binaryFormat)\n{\n\tList *wrappedTaskList = NIL;\n\tShardInterval **shardIntervalArray = targetRelation->sortedShardIntervalArray;\n\tint shardCount = targetRelation->shardIntervalArrayLength;\n\n\tArrayType *minValueArray = NULL;\n\tArrayType *maxValueArray = NULL;\n\tVar *partitionColumn = targetRelation->partitionColumn;\n\tOid intervalTypeId = InvalidOid;\n\tint32 intervalTypeMod = 0;\n\tOid intervalTypeOutFunc = InvalidOid;\n\tbool intervalTypeVarlena = false;\n\n\tGetIntervalTypeInfo(targetRelation->partitionMethod, partitionColumn,\n\t\t\t\t\t\t&intervalTypeId, &intervalTypeMod);\n\tgetTypeOutputInfo(intervalTypeId, &intervalTypeOutFunc, &intervalTypeVarlena);\n\n\tShardMinMaxValueArrays(shardIntervalArray, shardCount, intervalTypeOutFunc,\n\t\t\t\t\t\t   &minValueArray, &maxValueArray);\n\tStringInfo minValuesString = ArrayObjectToString(minValueArray, TEXTOID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t intervalTypeMod);\n\tStringInfo maxValuesString = ArrayObjectToString(maxValueArray, TEXTOID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t intervalTypeMod);\n\n\tTask *selectTask = NULL;\n\tforeach_declared_ptr(selectTask, selectTaskList)\n\t{\n\t\tchar *taskPrefix = SourceShardPrefix(resultIdPrefix, selectTask->anchorShardId);\n\t\tchar *partitionMethodString = targetRelation->partitionMethod == 'h' ?\n\t\t\t\t\t\t\t\t\t  \"hash\" : \"range\";\n\t\tconst char *binaryFormatString = binaryFormat ? \"true\" : \"false\";\n\n\t\tTask *wrappedSelectTask = copyObject(selectTask);\n\n\t\tStringInfo wrappedQuery = makeStringInfo();\n\t\tappendStringInfo(wrappedQuery,\n\t\t\t\t\t\t \"SELECT partition_index\"\n\t\t\t\t\t\t \", %s || '_' || partition_index::text \"\n\t\t\t\t\t\t \", rows_written \"\n\t\t\t\t\t\t \"FROM worker_partition_query_result\"\n\t\t\t\t\t\t \"(%s,%s,%d,%s,%s,%s,%s) WHERE rows_written > 0\",\n\t\t\t\t\t\t quote_literal_cstr(taskPrefix),\n\t\t\t\t\t\t quote_literal_cstr(taskPrefix),\n\t\t\t\t\t\t quote_literal_cstr(TaskQueryString(selectTask)),\n\t\t\t\t\t\t partitionColumnIndex,\n\t\t\t\t\t\t quote_literal_cstr(partitionMethodString),\n\t\t\t\t\t\t minValuesString->data, maxValuesString->data,\n\t\t\t\t\t\t binaryFormatString);\n\n\t\tSetTaskQueryString(wrappedSelectTask, wrappedQuery->data);\n\t\twrappedTaskList = lappend(wrappedTaskList, wrappedSelectTask);\n\t}\n\n\treturn wrappedTaskList;\n}\n\n\n/*\n * CreatePartitioningTupleDest creates a TupleDestination which consumes results of\n * tasks constructed in WrapTasksForPartitioning.\n */\nstatic PartitioningTupleDest *\nCreatePartitioningTupleDest(CitusTableCacheEntry *targetRelation)\n{\n\tint resultColumnCount = 3;\n\n\tTupleDesc tupleDescriptor = CreateTemplateTupleDesc(resultColumnCount);\n\n\tTupleDescInitEntry(tupleDescriptor, (AttrNumber) 1, \"partition_index\",\n\t\t\t\t\t   INT4OID, -1, 0);\n\tTupleDescInitEntry(tupleDescriptor, (AttrNumber) 2, \"result_id\",\n\t\t\t\t\t   TEXTOID, -1, 0);\n\tTupleDescInitEntry(tupleDescriptor, (AttrNumber) 3, \"rows_written\",\n\t\t\t\t\t   INT8OID, -1, 0);\n\n\n\tPartitioningTupleDest *tupleDest = palloc0(sizeof(PartitioningTupleDest));\n\ttupleDest->targetRelation = targetRelation;\n\ttupleDest->tupleDesc = tupleDescriptor;\n\ttupleDest->fragmentContext = CurrentMemoryContext;\n\ttupleDest->pub.putTuple = PartitioningTupleDestPutTuple;\n\ttupleDest->pub.tupleDescForQuery =\n\t\tPartitioningTupleDestTupleDescForQuery;\n\n\treturn tupleDest;\n}\n\n\n/*\n * PartitioningTupleDestPutTuple implements TupleDestination->putTuple for\n * PartitioningTupleDest.\n */\nstatic void\nPartitioningTupleDestPutTuple(TupleDestination *self, Task *task,\n\t\t\t\t\t\t\t  int placementIndex, int queryNumber,\n\t\t\t\t\t\t\t  HeapTuple heapTuple, uint64 tupleLibpqSize)\n{\n\tPartitioningTupleDest *tupleDest = (PartitioningTupleDest *) self;\n\n\tShardPlacement *placement = list_nth(task->taskPlacementList, placementIndex);\n\n\t/*\n\t * We may be deep inside a nested execution, make sure we can use the\n\t * fragment list at the top.\n\t */\n\tMemoryContext oldContext = MemoryContextSwitchTo(tupleDest->fragmentContext);\n\n\tDistributedResultFragment *fragment =\n\t\tTupleToDistributedResultFragment(heapTuple, tupleDest->tupleDesc,\n\t\t\t\t\t\t\t\t\t\t tupleDest->targetRelation,\n\t\t\t\t\t\t\t\t\t\t placement->nodeId);\n\n\ttupleDest->fragmentList = lappend(tupleDest->fragmentList, fragment);\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * PartitioningTupleDestTupleDescForQuery implements TupleDestination->TupleDescForQuery\n * for PartitioningTupleDest.\n */\nstatic TupleDesc\nPartitioningTupleDestTupleDescForQuery(TupleDestination *self, int queryNumber)\n{\n\tAssert(queryNumber == 0);\n\n\tPartitioningTupleDest *tupleDest = (PartitioningTupleDest *) self;\n\n\treturn tupleDest->tupleDesc;\n}\n\n\n/*\n * SourceShardPrefix returns result id prefix for partitions which have the\n * given anchor shard id.\n */\nstatic char *\nSourceShardPrefix(const char *resultPrefix, uint64 shardId)\n{\n\tStringInfo taskPrefix = makeStringInfo();\n\n\tappendStringInfo(taskPrefix, \"%s_from_\" UINT64_FORMAT \"_to\", resultPrefix, shardId);\n\n\treturn taskPrefix->data;\n}\n\n\n/*\n * ShardMinMaxValueArrays returns min values and max values of given shard\n * intervals. Returned arrays are text arrays.\n */\nvoid\nShardMinMaxValueArrays(ShardInterval **shardIntervalArray, int shardCount,\n\t\t\t\t\t   Oid intervalTypeOutFunc, ArrayType **minValueArray,\n\t\t\t\t\t   ArrayType **maxValueArray)\n{\n\tDatum *minValues = palloc0(shardCount * sizeof(Datum));\n\tbool *minValueNulls = palloc0(shardCount * sizeof(bool));\n\tDatum *maxValues = palloc0(shardCount * sizeof(Datum));\n\tbool *maxValueNulls = palloc0(shardCount * sizeof(bool));\n\tfor (int shardIndex = 0; shardIndex < shardCount; shardIndex++)\n\t{\n\t\tminValueNulls[shardIndex] = !shardIntervalArray[shardIndex]->minValueExists;\n\t\tmaxValueNulls[shardIndex] = !shardIntervalArray[shardIndex]->maxValueExists;\n\n\t\tif (!minValueNulls[shardIndex])\n\t\t{\n\t\t\tDatum minValue = shardIntervalArray[shardIndex]->minValue;\n\t\t\tchar *minValueStr = DatumGetCString(OidFunctionCall1(intervalTypeOutFunc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t minValue));\n\t\t\tminValues[shardIndex] = CStringGetTextDatum(minValueStr);\n\t\t}\n\n\t\tif (!maxValueNulls[shardIndex])\n\t\t{\n\t\t\tDatum maxValue = shardIntervalArray[shardIndex]->maxValue;\n\t\t\tchar *maxValueStr = DatumGetCString(OidFunctionCall1(intervalTypeOutFunc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t maxValue));\n\t\t\tmaxValues[shardIndex] = CStringGetTextDatum(maxValueStr);\n\t\t}\n\t}\n\n\t*minValueArray = CreateArrayFromDatums(minValues, minValueNulls, shardCount, TEXTOID);\n\t*maxValueArray = CreateArrayFromDatums(maxValues, maxValueNulls, shardCount, TEXTOID);\n}\n\n\n/*\n * CreateArrayFromDatums creates an array consisting of given values and nulls.\n */\nArrayType *\nCreateArrayFromDatums(Datum *datumArray, bool *nullsArray, int datumCount, Oid typeId)\n{\n\tbool typeByValue = false;\n\tchar typeAlignment = 0;\n\tint16 typeLength = 0;\n\tint dimensions[1] = { datumCount };\n\tint lowerbounds[1] = { 1 };\n\n\tget_typlenbyvalalign(typeId, &typeLength, &typeByValue, &typeAlignment);\n\n\tArrayType *datumArrayObject = construct_md_array(datumArray, nullsArray, 1,\n\t\t\t\t\t\t\t\t\t\t\t\t\t dimensions,\n\t\t\t\t\t\t\t\t\t\t\t\t\t lowerbounds, typeId, typeLength,\n\t\t\t\t\t\t\t\t\t\t\t\t\t typeByValue, typeAlignment);\n\n\treturn datumArrayObject;\n}\n\n\n/*\n * ExecutePartitionTaskList executes the queries formed in WrapTasksForPartitioning(),\n * and returns its results as a list of DistributedResultFragment.\n */\nstatic List *\nExecutePartitionTaskList(List *taskList, CitusTableCacheEntry *targetRelation)\n{\n\tPartitioningTupleDest *tupleDest = CreatePartitioningTupleDest(targetRelation);\n\n\tbool errorOnAnyFailure = false;\n\tExecuteSelectTasksIntoTupleDest(taskList, (TupleDestination *) tupleDest,\n\t\t\t\t\t\t\t\t\terrorOnAnyFailure);\n\n\treturn tupleDest->fragmentList;\n}\n\n\n/*\n * TupleToDistributedResultFragment converts a tuple returned by the query in\n * WrapTasksForPartitioning() to a DistributedResultFragment.\n */\nstatic DistributedResultFragment *\nTupleToDistributedResultFragment(HeapTuple tuple,\n\t\t\t\t\t\t\t\t TupleDesc tupleDesc,\n\t\t\t\t\t\t\t\t CitusTableCacheEntry *targetRelation,\n\t\t\t\t\t\t\t\t uint32 sourceNodeId)\n{\n\tbool isNull = false;\n\tuint32 targetShardIndex = DatumGetUInt32(heap_getattr(tuple, 1, tupleDesc, &isNull));\n\ttext *resultId = DatumGetTextP(heap_getattr(tuple, 2, tupleDesc, &isNull));\n\tint64 rowCount = DatumGetInt64(heap_getattr(tuple, 3, tupleDesc, &isNull));\n\n\tAssert(targetShardIndex < targetRelation->shardIntervalArrayLength);\n\tShardInterval *shardInterval =\n\t\ttargetRelation->sortedShardIntervalArray[targetShardIndex];\n\n\tDistributedResultFragment *distributedResultFragment =\n\t\tpalloc0(sizeof(DistributedResultFragment));\n\n\tdistributedResultFragment->nodeId = sourceNodeId;\n\tdistributedResultFragment->targetShardIndex = targetShardIndex;\n\tdistributedResultFragment->targetShardId = shardInterval->shardId;\n\tdistributedResultFragment->resultId = text_to_cstring(resultId);\n\tdistributedResultFragment->rowCount = rowCount;\n\n\treturn distributedResultFragment;\n}\n\n\n/*\n * ExecuteSelectTasksIntoTupleDest executes the given tasks and forwards its result\n * to the given destination.\n */\nstatic void\nExecuteSelectTasksIntoTupleDest(List *taskList, TupleDestination *tupleDestination,\n\t\t\t\t\t\t\t\tbool errorOnAnyFailure)\n{\n\tbool expectResults = true;\n\tint targetPoolSize = MaxAdaptiveExecutorPoolSize;\n\tTransactionProperties xactProperties = {\n\t\t.errorOnAnyFailure = errorOnAnyFailure,\n\t\t.useRemoteTransactionBlocks = TRANSACTION_BLOCKS_REQUIRED,\n\t\t.requires2PC = false\n\t};\n\n\tbool localExecutionSupported = true;\n\tExecutionParams *executionParams = CreateBasicExecutionParams(\n\t\tROW_MODIFY_READONLY, taskList, targetPoolSize, localExecutionSupported\n\t\t);\n\texecutionParams->tupleDestination = tupleDestination;\n\texecutionParams->xactProperties = xactProperties;\n\texecutionParams->expectResults = expectResults;\n\n\tExecuteTaskListExtended(executionParams);\n}\n\n\n/*\n * ColocateFragmentsWithRelation moves the fragments in the cluster so they are\n * colocated with the shards of target relation. These transfers are done by\n * calls to fetch_intermediate_results() between nodes.\n *\n * returnValue[shardIndex] is list of result Ids that are colocated with\n * targetRelation->sortedShardIntervalArray[shardIndex] after fetch tasks are\n * done.\n */\nstatic List **\nColocateFragmentsWithRelation(List *fragmentList, CitusTableCacheEntry *targetRelation)\n{\n\tList *fragmentListTransfers = ColocationTransfers(fragmentList, targetRelation);\n\tList *fragmentTransferTaskList = FragmentTransferTaskList(fragmentListTransfers);\n\n\tExecuteFetchTaskList(fragmentTransferTaskList);\n\n\tint shardCount = targetRelation->shardIntervalArrayLength;\n\tList **shardResultIdList = palloc0(shardCount * sizeof(List *));\n\n\tDistributedResultFragment *sourceFragment = NULL;\n\tforeach_declared_ptr(sourceFragment, fragmentList)\n\t{\n\t\tint shardIndex = sourceFragment->targetShardIndex;\n\n\t\tAssert(shardIndex < shardCount);\n\t\tshardResultIdList[shardIndex] = lappend(shardResultIdList[shardIndex],\n\t\t\t\t\t\t\t\t\t\t\t\tsourceFragment->resultId);\n\t}\n\n\treturn shardResultIdList;\n}\n\n\n/*\n * ColocationTransfers returns a list of transfers to colocate given fragments with\n * shards of the target relation. These transfers also take into account replicated\n * target relations. This prunes away transfers with same source and target\n */\nstatic List *\nColocationTransfers(List *fragmentList, CitusTableCacheEntry *targetRelation)\n{\n\tHASHCTL transferHashInfo;\n\tMemSet(&transferHashInfo, 0, sizeof(HASHCTL));\n\ttransferHashInfo.keysize = sizeof(NodePair);\n\ttransferHashInfo.entrysize = sizeof(NodeToNodeFragmentsTransfer);\n\ttransferHashInfo.hcxt = CurrentMemoryContext;\n\tHTAB *transferHash = hash_create(\"Fragment Transfer Hash\", 32, &transferHashInfo,\n\t\t\t\t\t\t\t\t\t HASH_ELEM | HASH_CONTEXT | HASH_BLOBS);\n\n\tDistributedResultFragment *fragment = NULL;\n\tforeach_declared_ptr(fragment, fragmentList)\n\t{\n\t\tList *placementList = ActiveShardPlacementList(fragment->targetShardId);\n\t\tShardPlacement *placement = NULL;\n\t\tforeach_declared_ptr(placement, placementList)\n\t\t{\n\t\t\tNodePair transferKey = {\n\t\t\t\t.sourceNodeId = fragment->nodeId,\n\t\t\t\t.targetNodeId = placement->nodeId\n\t\t\t};\n\n\t\t\tif (transferKey.sourceNodeId == transferKey.targetNodeId)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tbool foundInCache = false;\n\t\t\tNodeToNodeFragmentsTransfer *fragmentListTransfer =\n\t\t\t\thash_search(transferHash, &transferKey, HASH_ENTER, &foundInCache);\n\t\t\tif (!foundInCache)\n\t\t\t{\n\t\t\t\tfragmentListTransfer->nodes = transferKey;\n\t\t\t\tfragmentListTransfer->fragmentList = NIL;\n\t\t\t}\n\n\t\t\tfragmentListTransfer->fragmentList =\n\t\t\t\tlappend(fragmentListTransfer->fragmentList, fragment);\n\t\t}\n\t}\n\n\tList *fragmentListTransfers = NIL;\n\tNodeToNodeFragmentsTransfer *transfer = NULL;\n\tHASH_SEQ_STATUS hashSeqStatus;\n\n\thash_seq_init(&hashSeqStatus, transferHash);\n\n\twhile ((transfer = hash_seq_search(&hashSeqStatus)) != NULL)\n\t{\n\t\tfragmentListTransfers = lappend(fragmentListTransfers, transfer);\n\t}\n\n\treturn fragmentListTransfers;\n}\n\n\n/*\n * FragmentTransferTaskList returns a list of tasks which performs the given list of\n * transfers. Each of the transfers are done by a SQL call to fetch_intermediate_results.\n * See QueryStringForFragmentsTransfer for how the query is constructed.\n */\nstatic List *\nFragmentTransferTaskList(List *fragmentListTransfers)\n{\n\tList *fetchTaskList = NIL;\n\n\tNodeToNodeFragmentsTransfer *fragmentsTransfer = NULL;\n\tforeach_declared_ptr(fragmentsTransfer, fragmentListTransfers)\n\t{\n\t\tuint32 targetNodeId = fragmentsTransfer->nodes.targetNodeId;\n\n\t\t/* these should have already been pruned away in ColocationTransfers */\n\t\tAssert(targetNodeId != fragmentsTransfer->nodes.sourceNodeId);\n\n\t\tWorkerNode *workerNode = LookupNodeByNodeIdOrError(targetNodeId);\n\n\t\tShardPlacement *targetPlacement = CitusMakeNode(ShardPlacement);\n\t\tSetPlacementNodeMetadata(targetPlacement, workerNode);\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->taskType = READ_TASK;\n\t\tSetTaskQueryString(task, QueryStringForFragmentsTransfer(fragmentsTransfer));\n\t\ttask->taskPlacementList = list_make1(targetPlacement);\n\n\t\tfetchTaskList = lappend(fetchTaskList, task);\n\t}\n\n\treturn fetchTaskList;\n}\n\n\n/*\n * QueryStringForFragmentsTransfer returns a query which fetches distributed\n * result fragments from source node to target node. See the structure of\n * NodeToNodeFragmentsTransfer for details of how these are decided.\n */\nchar *\nQueryStringForFragmentsTransfer(NodeToNodeFragmentsTransfer *fragmentsTransfer)\n{\n\tStringInfo queryString = makeStringInfo();\n\tStringInfo fragmentNamesArrayString = makeStringInfo();\n\tint fragmentCount = 0;\n\tNodePair *nodePair = &fragmentsTransfer->nodes;\n\tuint32 sourceNodeId = nodePair->sourceNodeId;\n\n\t/*\n\t * If the placement is dummy, for example, queries that generate\n\t * intermediate results at the coordinator that need to be redistributed\n\t * to worker nodes, we need the local id.\n\t */\n\tif (sourceNodeId == LOCAL_NODE_ID)\n\t{\n\t\tnodePair->sourceNodeId = GetLocalNodeId();\n\t}\n\n\tWorkerNode *sourceNode = LookupNodeByNodeIdOrError(nodePair->sourceNodeId);\n\n\tappendStringInfoString(fragmentNamesArrayString, \"ARRAY[\");\n\n\tDistributedResultFragment *fragment = NULL;\n\tforeach_declared_ptr(fragment, fragmentsTransfer->fragmentList)\n\t{\n\t\tconst char *fragmentName = fragment->resultId;\n\n\t\tif (fragmentCount > 0)\n\t\t{\n\t\t\tappendStringInfoString(fragmentNamesArrayString, \",\");\n\t\t}\n\n\t\tappendStringInfoString(fragmentNamesArrayString,\n\t\t\t\t\t\t\t   quote_literal_cstr(fragmentName));\n\n\t\tfragmentCount++;\n\t}\n\n\tappendStringInfoString(fragmentNamesArrayString, \"]::text[]\");\n\n\tappendStringInfo(queryString,\n\t\t\t\t\t \"SELECT bytes FROM fetch_intermediate_results(%s,%s,%d) bytes\",\n\t\t\t\t\t fragmentNamesArrayString->data,\n\t\t\t\t\t quote_literal_cstr(sourceNode->workerName),\n\t\t\t\t\t sourceNode->workerPort);\n\n\tereport(DEBUG4, (errmsg(\"fetch task on %s:%d: %s\", sourceNode->workerName,\n\t\t\t\t\t\t\tsourceNode->workerPort, queryString->data)));\n\n\treturn queryString->data;\n}\n\n\n/*\n * ExecuteFetchTaskList executes a list of fetch_intermediate_results() tasks.\n * It ignores the byte_count result of the fetch_intermediate_results() calls.\n */\nstatic void\nExecuteFetchTaskList(List *taskList)\n{\n\tint resultColumnCount = 1;\n\n\tTupleDesc resultDescriptor = CreateTemplateTupleDesc(resultColumnCount);\n\n\tTupleDescInitEntry(resultDescriptor, (AttrNumber) 1, \"byte_count\", INT8OID, -1, 0);\n\n\tTupleDestination *tupleDestination = CreateTupleDestNone();\n\n\tbool errorOnAnyFailure = true;\n\tExecuteSelectTasksIntoTupleDest(taskList, tupleDestination, errorOnAnyFailure);\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/executor_util_params.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * executor_util_tasks.c\n *\n * Utility functions for dealing with task lists in the executor.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/executor_util.h\"\n\n\n/*\n * ExtractParametersForRemoteExecution extracts parameter types and values from\n * the given ParamListInfo structure, and fills parameter type and value arrays.\n * It changes oid of custom types to InvalidOid so that they are the same in workers\n * and coordinators.\n */\nvoid\nExtractParametersForRemoteExecution(ParamListInfo paramListInfo, Oid **parameterTypes,\n\t\t\t\t\t\t\t\t\tconst char ***parameterValues)\n{\n\tExtractParametersFromParamList(paramListInfo, parameterTypes,\n\t\t\t\t\t\t\t\t   parameterValues, false);\n}\n\n\n/*\n * ExtractParametersFromParamList extracts parameter types and values from\n * the given ParamListInfo structure, and fills parameter type and value arrays.\n * If useOriginalCustomTypeOids is true, it uses the original oids for custom types.\n */\nvoid\nExtractParametersFromParamList(ParamListInfo paramListInfo,\n\t\t\t\t\t\t\t   Oid **parameterTypes,\n\t\t\t\t\t\t\t   const char ***parameterValues, bool\n\t\t\t\t\t\t\t   useOriginalCustomTypeOids)\n{\n\tint parameterCount = paramListInfo->numParams;\n\n\t*parameterTypes = (Oid *) palloc0(parameterCount * sizeof(Oid));\n\t*parameterValues = (const char **) palloc0(parameterCount * sizeof(char *));\n\n\t/* get parameter types and values */\n\tfor (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++)\n\t{\n\t\tParamExternData *parameterData = &paramListInfo->params[parameterIndex];\n\t\tOid typeOutputFunctionId = InvalidOid;\n\t\tbool variableLengthType = false;\n\n\t\t/*\n\t\t * Use 0 for data types where the oid values can be different on\n\t\t * the coordinator and worker nodes. Therefore, the worker nodes can\n\t\t * infer the correct oid.\n\t\t */\n\t\tif (parameterData->ptype >= FirstNormalObjectId && !useOriginalCustomTypeOids)\n\t\t{\n\t\t\t(*parameterTypes)[parameterIndex] = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t(*parameterTypes)[parameterIndex] = parameterData->ptype;\n\t\t}\n\n\t\t/*\n\t\t * If the parameter is not referenced / used (ptype == 0) and\n\t\t * would otherwise have errored out inside standard_planner()),\n\t\t * don't pass a value to the remote side, and pass text oid to prevent\n\t\t * undetermined data type errors on workers.\n\t\t */\n\t\tif (parameterData->ptype == 0)\n\t\t{\n\t\t\t(*parameterValues)[parameterIndex] = NULL;\n\t\t\t(*parameterTypes)[parameterIndex] = TEXTOID;\n\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * If the parameter is NULL then we preserve its type, but\n\t\t * don't need to evaluate its value.\n\t\t */\n\t\tif (parameterData->isnull)\n\t\t{\n\t\t\t(*parameterValues)[parameterIndex] = NULL;\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tgetTypeOutputInfo(parameterData->ptype, &typeOutputFunctionId,\n\t\t\t\t\t\t  &variableLengthType);\n\n\t\t(*parameterValues)[parameterIndex] = OidOutputFunctionCall(typeOutputFunctionId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   parameterData->value);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/executor_util_tasks.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * executor_util_tasks.c\n *\n * Utility functions for dealing with task lists in the executor.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"distributed/executor_util.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/shardinterval_utils.h\"\n\n\n/*\n *  TaskListModifiesDatabase is a helper function for DistributedExecutionModifiesDatabase and\n *  DistributedPlanModifiesDatabase.\n */\nbool\nTaskListModifiesDatabase(RowModifyLevel modLevel, List *taskList)\n{\n\tif (modLevel > ROW_MODIFY_READONLY)\n\t{\n\t\treturn true;\n\t}\n\n\t/*\n\t * If we cannot decide by only checking the row modify level,\n\t * we should look closer to the tasks.\n\t */\n\tif (list_length(taskList) < 1)\n\t{\n\t\t/* is this ever possible? */\n\t\treturn false;\n\t}\n\n\tTask *firstTask = (Task *) linitial(taskList);\n\n\treturn !ReadOnlyTask(firstTask->taskType);\n}\n\n\n/*\n * TaskListRequiresRollback returns true if the distributed\n * execution should start a CoordinatedTransaction. In other words, if the\n * function returns true, the execution sends BEGIN; to every connection\n * involved in the distributed execution.\n */\nbool\nTaskListRequiresRollback(List *taskList)\n{\n\tint taskCount = list_length(taskList);\n\n\tif (taskCount == 0)\n\t{\n\t\treturn false;\n\t}\n\n\tTask *task = (Task *) linitial(taskList);\n\tif (task->cannotBeExecutedInTransaction)\n\t{\n\t\t/* vacuum, create index concurrently etc. */\n\t\treturn false;\n\t}\n\n\tbool selectForUpdate = task->relationRowLockList != NIL;\n\tif (selectForUpdate)\n\t{\n\t\t/*\n\t\t * Do not check SelectOpensTransactionBlock, always open transaction block\n\t\t * if SELECT FOR UPDATE is executed inside a distributed transaction.\n\t\t */\n\t\treturn IsMultiStatementTransaction();\n\t}\n\n\tif (ReadOnlyTask(task->taskType))\n\t{\n\t\treturn SelectOpensTransactionBlock &&\n\t\t\t   IsTransactionBlock();\n\t}\n\n\tif (IsMultiStatementTransaction())\n\t{\n\t\treturn true;\n\t}\n\n\tif (list_length(taskList) > 1)\n\t{\n\t\treturn true;\n\t}\n\n\tif (list_length(task->taskPlacementList) > 1)\n\t{\n\t\t/*\n\t\t * Single DML/DDL tasks with replicated tables (including\n\t\t * reference and non-reference tables) should require\n\t\t * BEGIN/COMMIT/ROLLBACK.\n\t\t */\n\t\treturn true;\n\t}\n\n\tif (task->queryCount > 1)\n\t{\n\t\t/*\n\t\t * When there are multiple sequential queries in a task\n\t\t * we need to run those as a transaction.\n\t\t */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * TaskListRequires2PC determines whether the given task list requires 2PC.\n */\nbool\nTaskListRequires2PC(List *taskList)\n{\n\tif (taskList == NIL)\n\t{\n\t\treturn false;\n\t}\n\n\tTask *task = (Task *) linitial(taskList);\n\tif (ReadOnlyTask(task->taskType))\n\t{\n\t\t/* we do not trigger 2PC for ReadOnly queries */\n\t\treturn false;\n\t}\n\n\tbool singleTask = list_length(taskList) == 1;\n\tif (singleTask && list_length(task->taskPlacementList) == 1)\n\t{\n\t\t/* we do not trigger 2PC for modifications that are:\n\t\t *    - single task\n\t\t *    - single placement\n\t\t */\n\t\treturn false;\n\t}\n\n\t/*\n\t * Otherwise, all modifications are done via 2PC. This includes:\n\t *    - Multi-shard commands irrespective of the replication factor\n\t *    - Single-shard commands that are targeting more than one replica\n\t */\n\treturn true;\n}\n\n\n/*\n * TaskListCannotBeExecutedInTransaction returns true if any of the\n * tasks in the input cannot be executed in a transaction. These are\n * tasks like VACUUM or CREATE INDEX CONCURRENTLY etc.\n */\nbool\nTaskListCannotBeExecutedInTransaction(List *taskList)\n{\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tif (task->cannotBeExecutedInTransaction)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * SelectForUpdateOnReferenceTable returns true if the input task\n * contains a FOR UPDATE clause that locks any reference tables.\n */\nbool\nSelectForUpdateOnReferenceTable(List *taskList)\n{\n\tif (list_length(taskList) != 1)\n\t{\n\t\t/* we currently do not support SELECT FOR UPDATE on multi task queries */\n\t\treturn false;\n\t}\n\n\tTask *task = (Task *) linitial(taskList);\n\tRelationRowLock *relationRowLock = NULL;\n\tforeach_declared_ptr(relationRowLock, task->relationRowLockList)\n\t{\n\t\tOid relationId = relationRowLock->relationId;\n\n\t\tif (IsCitusTableType(relationId, REFERENCE_TABLE))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ReadOnlyTask returns true if the input task does a read-only operation\n * on the database.\n */\nbool\nReadOnlyTask(TaskType taskType)\n{\n\tswitch (taskType)\n\t{\n\t\tcase READ_TASK:\n\t\tcase MAP_OUTPUT_FETCH_TASK:\n\t\tcase MAP_TASK:\n\t\tcase MERGE_TASK:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\n\n/*\n * ModifiedTableReplicated iterates on the task list and returns true\n * if any of the tasks' anchor shard is a replicated table. We qualify\n * replicated tables as any reference table or any distributed table with\n * replication factor > 1.\n */\nbool\nModifiedTableReplicated(List *taskList)\n{\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tint64 shardId = task->anchorShardId;\n\n\t\tif (shardId == INVALID_SHARD_ID)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (ReferenceTableShardId(shardId))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tOid relationId = RelationIdForShard(shardId);\n\t\tif (!SingleReplicatedTable(relationId))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ShouldRunTasksSequentially returns true if each of the individual tasks\n * should be executed one by one. Note that this is different than\n * MultiShardConnectionType == SEQUENTIAL_CONNECTION case. In that case,\n * running the tasks across the nodes in parallel is acceptable and implemented\n * in that way.\n *\n * However, the executions that are qualified here would perform poorly if the\n * tasks across the workers are executed in parallel. We currently qualify only\n * one class of distributed queries here, multi-row INSERTs. If we do not enforce\n * true sequential execution, concurrent multi-row upserts could easily form\n * a distributed deadlock when the upserts touch the same rows.\n */\nbool\nShouldRunTasksSequentially(List *taskList)\n{\n\tif (list_length(taskList) < 2)\n\t{\n\t\t/* single task plans are already qualified as sequential by definition */\n\t\treturn false;\n\t}\n\n\t/* all the tasks are the same, so we only look one */\n\tTask *initialTask = (Task *) linitial(taskList);\n\tif (initialTask->rowValuesLists != NIL)\n\t{\n\t\t/* found a multi-row INSERT */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/executor_util_tuples.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * executor_util_tuples.c\n *\n * Utility functions for handling tuples during remote execution.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/executor_util.h\"\n\n\n/*\n * TupleDescGetAttBinaryInMetadata - Build an AttInMetadata structure based on\n * the supplied TupleDesc. AttInMetadata can be used in conjunction with\n * fmStringInfos containing binary encoded types to produce a properly formed\n * tuple.\n *\n * NOTE: This function is a copy of the PG function TupleDescGetAttInMetadata,\n * except that it uses getTypeBinaryInputInfo instead of getTypeInputInfo.\n */\nAttInMetadata *\nTupleDescGetAttBinaryInMetadata(TupleDesc tupdesc)\n{\n\tint natts = tupdesc->natts;\n\tint i;\n\tOid atttypeid;\n\tOid attinfuncid;\n\n\tAttInMetadata *attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));\n\n\t/* \"Bless\" the tupledesc so that we can make rowtype datums with it */\n\tattinmeta->tupdesc = BlessTupleDesc(tupdesc);\n\n\t/*\n\t * Gather info needed later to call the \"in\" function for each attribute\n\t */\n\tFmgrInfo *attinfuncinfo = (FmgrInfo *) palloc0(natts * sizeof(FmgrInfo));\n\tOid *attioparams = (Oid *) palloc0(natts * sizeof(Oid));\n\tint32 *atttypmods = (int32 *) palloc0(natts * sizeof(int32));\n\n\tfor (i = 0; i < natts; i++)\n\t{\n\t\tForm_pg_attribute att = TupleDescAttr(tupdesc, i);\n\n\t\t/* Ignore dropped attributes */\n\t\tif (!att->attisdropped)\n\t\t{\n\t\t\tatttypeid = att->atttypid;\n\t\t\tgetTypeBinaryInputInfo(atttypeid, &attinfuncid, &attioparams[i]);\n\t\t\tfmgr_info(attinfuncid, &attinfuncinfo[i]);\n\t\t\tatttypmods[i] = att->atttypmod;\n\t\t}\n\t}\n\tattinmeta->attinfuncs = attinfuncinfo;\n\tattinmeta->attioparams = attioparams;\n\tattinmeta->atttypmods = atttypmods;\n\n\treturn attinmeta;\n}\n\n\n/*\n * BuildTupleFromBytes - build a HeapTuple given user data in binary form.\n * values is an array of StringInfos, one for each attribute of the return\n * tuple. A NULL StringInfo pointer indicates we want to create a NULL field.\n *\n * NOTE: This function is a copy of the PG function BuildTupleFromCStrings,\n * except that it uses ReceiveFunctionCall instead of InputFunctionCall.\n */\nHeapTuple\nBuildTupleFromBytes(AttInMetadata *attinmeta, fmStringInfo *values)\n{\n\tTupleDesc tupdesc = attinmeta->tupdesc;\n\tint natts = tupdesc->natts;\n\tint i;\n\n\tDatum *dvalues = (Datum *) palloc(natts * sizeof(Datum));\n\tbool *nulls = (bool *) palloc(natts * sizeof(bool));\n\n\t/*\n\t * Call the \"in\" function for each non-dropped attribute, even for nulls,\n\t * to support domains.\n\t */\n\tfor (i = 0; i < natts; i++)\n\t{\n\t\tif (!TupleDescAttr(tupdesc, i)->attisdropped)\n\t\t{\n\t\t\t/* Non-dropped attributes */\n\t\t\tdvalues[i] = ReceiveFunctionCall(&attinmeta->attinfuncs[i],\n\t\t\t\t\t\t\t\t\t\t\t values[i],\n\t\t\t\t\t\t\t\t\t\t\t attinmeta->attioparams[i],\n\t\t\t\t\t\t\t\t\t\t\t attinmeta->atttypmods[i]);\n\t\t\tif (values[i] != NULL)\n\t\t\t{\n\t\t\t\tnulls[i] = false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnulls[i] = true;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* Handle dropped attributes by setting to NULL */\n\t\t\tdvalues[i] = (Datum) 0;\n\t\t\tnulls[i] = true;\n\t\t}\n\t}\n\n\t/*\n\t * Form a tuple\n\t */\n\tHeapTuple tuple = heap_form_tuple(tupdesc, dvalues, nulls);\n\n\t/*\n\t * Release locally palloc'd space.  XXX would probably be good to pfree\n\t * values of pass-by-reference datums, as well.\n\t */\n\tpfree(dvalues);\n\tpfree(nulls);\n\n\treturn tuple;\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/insert_select_executor.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * insert_select_executor.c\n *\n * Executor logic for INSERT..SELECT.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"executor/executor.h\"\n#include \"nodes/execnodes.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/plannodes.h\"\n#include \"parser/parse_coerce.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parsetree.h\"\n#include \"tcop/pquery.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/portal.h\"\n#include \"utils/rel.h\"\n#include \"utils/snapmgr.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/distributed_execution_locks.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/insert_select_executor.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/merge_planner.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_explain.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/repartition_executor.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/stats/stat_counters.h\"\n#include \"distributed/subplan_execution.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/version_compat.h\"\n\n/* Config variables managed via guc.c */\nbool EnableRepartitionedInsertSelect = true;\n\n\nstatic void ExecutePlanIntoRelation(Oid targetRelationId, List *insertTargetList,\n\t\t\t\t\t\t\t\t\tPlannedStmt *selectPlan, EState *executorState);\nstatic HTAB * ExecutePlanIntoColocatedIntermediateResults(Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  List *insertTargetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  PlannedStmt *selectPlan,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  EState *executorState,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  char *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  intermediateResultIdPrefix);\nstatic int PartitionColumnIndexFromColumnList(Oid relationId, List *columnNameList);\nstatic void WrapTaskListForProjection(List *taskList, List *projectedTargetEntries);\n\n\n/*\n * NonPushableInsertSelectExecScan executes an INSERT INTO distributed_table\n * SELECT .. query either by routing via coordinator or by repartitioning\n * task results and moving data directly between nodes.\n */\nTupleTableSlot *\nNonPushableInsertSelectExecScan(CustomScanState *node)\n{\n\tCitusScanState *scanState = (CitusScanState *) node;\n\n\tif (!scanState->finishedRemoteScan)\n\t{\n\t\tEState *executorState = ScanStateGetExecutorState(scanState);\n\t\tDistributedPlan *distributedPlan = scanState->distributedPlan;\n\t\tQuery *insertSelectQuery =\n\t\t\tcopyObject(distributedPlan->modifyQueryViaCoordinatorOrRepartition);\n\t\tList *insertTargetList = insertSelectQuery->targetList;\n\t\tRangeTblEntry *selectRte = ExtractSelectRangeTableEntry(insertSelectQuery);\n\t\tRangeTblEntry *insertRte = ExtractResultRelationRTE(insertSelectQuery);\n\t\tOid targetRelationId = insertRte->relid;\n\t\tchar *intermediateResultIdPrefix = distributedPlan->intermediateResultIdPrefix;\n\t\tbool hasReturning = distributedPlan->expectResults;\n\t\tHTAB *shardStateHash = NULL;\n\n\t\tQuery *selectQuery = selectRte->subquery;\n\t\tPlannedStmt *selectPlan =\n\t\t\tcopyObject(distributedPlan->selectPlanForModifyViaCoordinatorOrRepartition);\n\n\t\t/*\n\t\t * If we are dealing with partitioned table, we also need to lock its\n\t\t * partitions. Here we only lock targetRelation, we acquire necessary\n\t\t * locks on selected tables during execution of those select queries.\n\t\t */\n\t\tif (PartitionedTable(targetRelationId))\n\t\t{\n\t\t\tLockPartitionRelations(targetRelationId, RowExclusiveLock);\n\t\t}\n\n\t\tif (distributedPlan->modifyWithSelectMethod == MODIFY_WITH_SELECT_REPARTITION)\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"performing repartitioned INSERT ... SELECT\")));\n\n\t\t\tDistributedPlan *distSelectPlan =\n\t\t\t\tGetDistributedPlan((CustomScan *) selectPlan->planTree);\n\t\t\tJob *distSelectJob = distSelectPlan->workerJob;\n\t\t\tList *distSelectTaskList = distSelectJob->taskList;\n\t\t\tbool randomAccess = true;\n\t\t\tbool interTransactions = false;\n\t\t\tbool binaryFormat =\n\t\t\t\tCanUseBinaryCopyFormatForTargetList(selectQuery->targetList);\n\n\t\t\tExecuteSubPlans(distSelectPlan, RequestedForExplainAnalyze(scanState));\n\n\t\t\t/*\n\t\t\t * We have a separate directory for each transaction, so choosing\n\t\t\t * the same result prefix won't cause filename conflicts. Results\n\t\t\t * directory name also includes node id and database id, so we don't\n\t\t\t * need to include them in the filename. We include job id here for\n\t\t\t * the case \"INSERT/SELECTs\" are executed recursively.\n\t\t\t */\n\t\t\tStringInfo distResultPrefixString = makeStringInfo();\n\t\t\tappendStringInfo(distResultPrefixString,\n\t\t\t\t\t\t\t \"repartitioned_results_\" UINT64_FORMAT,\n\t\t\t\t\t\t\t distSelectJob->jobId);\n\t\t\tchar *distResultPrefix = distResultPrefixString->data;\n\n\t\t\tCitusTableCacheEntry *targetRelation =\n\t\t\t\tGetCitusTableCacheEntry(targetRelationId);\n\n\t\t\tint distributionColumnIndex =\n\t\t\t\tDistributionColumnIndex(insertTargetList,\n\t\t\t\t\t\t\t\t\t\ttargetRelation->partitionColumn);\n\t\t\tif (distributionColumnIndex == -1)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),\n\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\"the partition column of table %s should have a value\",\n\t\t\t\t\t\t\t\t\tgenerate_qualified_relation_name(targetRelationId))));\n\t\t\t}\n\n\t\t\tTargetEntry *selectPartitionTE = list_nth(selectQuery->targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  distributionColumnIndex);\n\t\t\tconst char *partitionColumnName = selectPartitionTE->resname ?\n\t\t\t\t\t\t\t\t\t\t\t  selectPartitionTE->resname : \"(none)\";\n\n\t\t\tereport(DEBUG2, (errmsg(\n\t\t\t\t\t\t\t\t \"partitioning SELECT query by column index %d with name %s\",\n\t\t\t\t\t\t\t\t distributionColumnIndex, quote_literal_cstr(\n\t\t\t\t\t\t\t\t\t partitionColumnName))));\n\n\t\t\t/*\n\t\t\t * ExpandWorkerTargetEntry() can add additional columns to the worker\n\t\t\t * query. Modify the task queries to only select columns we need.\n\t\t\t */\n\t\t\tint requiredColumnCount = list_length(insertTargetList);\n\t\t\tList *jobTargetList = distSelectJob->jobQuery->targetList;\n\t\t\tif (list_length(jobTargetList) > requiredColumnCount)\n\t\t\t{\n\t\t\t\tList *projectedTargetEntries = ListTake(jobTargetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\trequiredColumnCount);\n\t\t\t\tWrapTaskListForProjection(distSelectTaskList, projectedTargetEntries);\n\t\t\t}\n\n\t\t\tList **redistributedResults = RedistributeTaskListResults(distResultPrefix,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  distSelectTaskList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  distributionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  targetRelation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  binaryFormat);\n\n\t\t\tif (list_length(distSelectTaskList) <= 1)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Probably we will never get here for a repartitioned\n\t\t\t\t * INSERT..SELECT because when the source is a single shard\n\t\t\t\t * table, we should most probably choose to use\n\t\t\t\t * MODIFY_WITH_SELECT_VIA_COORDINATOR, but we still keep this\n\t\t\t\t * here.\n\t\t\t\t */\n\t\t\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_SINGLE_SHARD);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_MULTI_SHARD);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * At this point select query has been executed on workers and results\n\t\t\t * have been fetched in such a way that they are colocated with corresponding\n\t\t\t * target shard. Create and execute a list of tasks of form\n\t\t\t * INSERT INTO ... SELECT * FROM read_intermediate_results(...);\n\t\t\t */\n\t\t\tList *taskList = GenerateTaskListWithRedistributedResults(insertSelectQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  targetRelation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  redistributedResults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  binaryFormat);\n\n\t\t\tscanState->tuplestorestate =\n\t\t\t\ttuplestore_begin_heap(randomAccess, interTransactions, work_mem);\n\t\t\tTupleDesc tupleDescriptor = ScanStateGetTupleDescriptor(scanState);\n\t\t\tTupleDestination *tupleDest = CreateTupleStoreTupleDest(\n\t\t\t\tscanState->tuplestorestate, tupleDescriptor);\n\t\t\tuint64 rowsInserted = ExecuteTaskListIntoTupleDest(ROW_MODIFY_COMMUTATIVE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   taskList, tupleDest,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   hasReturning);\n\n\t\t\tif (list_length(taskList) <= 1)\n\t\t\t{\n\t\t\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_SINGLE_SHARD);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_MULTI_SHARD);\n\t\t\t}\n\n\t\t\texecutorState->es_processed = rowsInserted;\n\n\t\t\tif (SortReturning && hasReturning)\n\t\t\t{\n\t\t\t\tSortTupleStore(scanState);\n\t\t\t}\n\t\t}\n\t\telse if (insertSelectQuery->onConflict || hasReturning)\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t\t\t \"Collecting INSERT ... SELECT results on coordinator\")));\n\n\t\t\t/*\n\t\t\t * If we also have a workerJob that means there is a second step\n\t\t\t * to the INSERT...SELECT. This happens when there is a RETURNING\n\t\t\t * or ON CONFLICT clause which is implemented as a separate\n\t\t\t * distributed INSERT...SELECT from a set of intermediate results\n\t\t\t * to the target relation.\n\t\t\t */\n\t\t\tList *prunedTaskList = NIL;\n\n\t\t\tshardStateHash = ExecutePlanIntoColocatedIntermediateResults(\n\t\t\t\ttargetRelationId,\n\t\t\t\tinsertTargetList,\n\t\t\t\tselectPlan,\n\t\t\t\texecutorState,\n\t\t\t\tintermediateResultIdPrefix);\n\n\t\t\t/* generate tasks for the INSERT..SELECT phase */\n\t\t\tList *taskList =\n\t\t\t\tGenerateTaskListWithColocatedIntermediateResults(\n\t\t\t\t\ttargetRelationId, insertSelectQuery,\n\t\t\t\t\tintermediateResultIdPrefix);\n\n\t\t\t/*\n\t\t\t * We cannot actually execute INSERT...SELECT tasks that read from\n\t\t\t * intermediate results that weren't created because no rows were\n\t\t\t * written to them. Prune those tasks out by only including tasks\n\t\t\t * on shards with connections.\n\t\t\t */\n\t\t\tTask *task = NULL;\n\t\t\tforeach_declared_ptr(task, taskList)\n\t\t\t{\n\t\t\t\tuint64 shardId = task->anchorShardId;\n\t\t\t\tbool shardModified = false;\n\n\t\t\t\thash_search(shardStateHash, &shardId, HASH_FIND, &shardModified);\n\t\t\t\tif (shardModified)\n\t\t\t\t{\n\t\t\t\t\tprunedTaskList = lappend(prunedTaskList, task);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (prunedTaskList != NIL)\n\t\t\t{\n\t\t\t\tbool randomAccess = true;\n\t\t\t\tbool interTransactions = false;\n\n\t\t\t\tAssert(scanState->tuplestorestate == NULL);\n\t\t\t\tscanState->tuplestorestate =\n\t\t\t\t\ttuplestore_begin_heap(randomAccess, interTransactions, work_mem);\n\n\t\t\t\tTupleDesc tupleDescriptor = ScanStateGetTupleDescriptor(scanState);\n\t\t\t\tTupleDestination *tupleDest = CreateTupleStoreTupleDest(\n\t\t\t\t\tscanState->tuplestorestate, tupleDescriptor);\n\n\t\t\t\tExecuteTaskListIntoTupleDest(ROW_MODIFY_COMMUTATIVE, prunedTaskList,\n\t\t\t\t\t\t\t\t\t\t\t tupleDest, hasReturning);\n\n\t\t\t\tif (SortReturning && hasReturning)\n\t\t\t\t{\n\t\t\t\t\tSortTupleStore(scanState);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (list_length(prunedTaskList) <= 1)\n\t\t\t{\n\t\t\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_SINGLE_SHARD);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_MULTI_SHARD);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t\t\t \"Collecting INSERT ... SELECT results on coordinator\")));\n\n\t\t\tExecutePlanIntoRelation(targetRelationId, insertTargetList, selectPlan,\n\t\t\t\t\t\t\t\t\texecutorState);\n\t\t}\n\n\t\tscanState->finishedRemoteScan = true;\n\t}\n\n\tTupleTableSlot *resultSlot = ReturnTupleFromTuplestore(scanState);\n\n\treturn resultSlot;\n}\n\n\n/*\n * ExecutePlanIntoColocatedIntermediateResults executes the given PlannedStmt\n * and inserts tuples into a set of intermediate results that are colocated with\n * the target table for further processing of ON CONFLICT or RETURNING. It also\n * returns the hash of shard states that were used to insert tuplesinto the target\n * relation.\n */\nstatic HTAB *\nExecutePlanIntoColocatedIntermediateResults(Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\tList *insertTargetList,\n\t\t\t\t\t\t\t\t\t\t\tPlannedStmt *selectPlan,\n\t\t\t\t\t\t\t\t\t\t\tEState *executorState,\n\t\t\t\t\t\t\t\t\t\t\tchar *intermediateResultIdPrefix)\n{\n\tParamListInfo paramListInfo = executorState->es_param_list_info;\n\n\t/* Get column name list and partition column index for the target table */\n\tList *columnNameList = BuildColumnNameListFromTargetList(targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t insertTargetList);\n\tint partitionColumnIndex = PartitionColumnIndexFromColumnList(targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  columnNameList);\n\n\t/*\n\t * We don't track query counters for the COPY commands that are executed to\n\t * prepare intermediate results.\n\t */\n\tconst bool trackQueryCounters = false;\n\n\t/* set up a DestReceiver that copies into the intermediate table */\n\tconst bool publishableData = true;\n\tCitusCopyDestReceiver *copyDest = CreateCitusCopyDestReceiver(targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  columnNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  executorState,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  intermediateResultIdPrefix,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  publishableData,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  trackQueryCounters);\n\n\tExecutePlanIntoDestReceiver(selectPlan, paramListInfo, (DestReceiver *) copyDest);\n\n\texecutorState->es_processed = copyDest->tuplesSent;\n\n\tXactModificationLevel = XACT_MODIFICATION_DATA;\n\n\treturn copyDest->shardStateHash;\n}\n\n\n/*\n * ExecutePlanIntoRelation executes the given plan and inserts the\n * results into the target relation, which is assumed to be a distributed\n * table.\n */\nstatic void\nExecutePlanIntoRelation(Oid targetRelationId, List *insertTargetList,\n\t\t\t\t\t\tPlannedStmt *selectPlan, EState *executorState)\n{\n\tParamListInfo paramListInfo = executorState->es_param_list_info;\n\n\t/* Get column name list and partition column index for the target table */\n\tList *columnNameList = BuildColumnNameListFromTargetList(targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t insertTargetList);\n\tint partitionColumnIndex = PartitionColumnIndexFromColumnList(targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  columnNameList);\n\n\t/*\n\t * We want to track query counters for the COPY commands that are executed to\n\t * perform the final INSERT for such INSERT..SELECT queries.\n\t */\n\tconst bool trackQueryCounters = true;\n\n\t/* set up a DestReceiver that copies into the distributed table */\n\tconst bool publishableData = true;\n\tCitusCopyDestReceiver *copyDest = CreateCitusCopyDestReceiver(targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  columnNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  executorState, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  publishableData,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  trackQueryCounters);\n\n\tExecutePlanIntoDestReceiver(selectPlan, paramListInfo, (DestReceiver *) copyDest);\n\n\texecutorState->es_processed = copyDest->tuplesSent;\n\n\tXactModificationLevel = XACT_MODIFICATION_DATA;\n}\n\n\n/*\n * BuildColumnNameListForCopyStatement build the column name list given the insert\n * target list.\n */\nList *\nBuildColumnNameListFromTargetList(Oid targetRelationId, List *insertTargetList)\n{\n\tList *columnNameList = NIL;\n\n\t/* build the list of column names for the COPY statement */\n\tTargetEntry *insertTargetEntry = NULL;\n\tforeach_declared_ptr(insertTargetEntry, insertTargetList)\n\t{\n\t\tcolumnNameList = lappend(columnNameList, insertTargetEntry->resname);\n\t}\n\n\treturn columnNameList;\n}\n\n\n/*\n * PartitionColumnIndexFromColumnList returns the index of partition column from given\n * column name list and relation ID. If given list doesn't contain the partition\n * column, it returns -1.\n */\nstatic int\nPartitionColumnIndexFromColumnList(Oid relationId, List *columnNameList)\n{\n\tVar *partitionColumn = PartitionColumn(relationId, 0);\n\tint partitionColumnIndex = 0;\n\n\tconst char *columnName = NULL;\n\tforeach_declared_ptr(columnName, columnNameList)\n\t{\n\t\tAttrNumber attrNumber = get_attnum(relationId, columnName);\n\n\t\t/* check whether this is the partition column */\n\t\tif (partitionColumn != NULL && attrNumber == partitionColumn->varattno)\n\t\t{\n\t\t\treturn partitionColumnIndex;\n\t\t}\n\n\t\tpartitionColumnIndex++;\n\t}\n\n\treturn -1;\n}\n\n\n/*\n * DistributionColumnIndex finds the index of given distribution column in the\n * given target list.\n */\nint\nDistributionColumnIndex(List *insertTargetList, Var *distributionColumn)\n{\n\tTargetEntry *insertTargetEntry = NULL;\n\tint targetEntryIndex = 0;\n\tforeach_declared_ptr(insertTargetEntry, insertTargetList)\n\t{\n\t\tif (insertTargetEntry->resno == distributionColumn->varattno)\n\t\t{\n\t\t\treturn targetEntryIndex;\n\t\t}\n\n\t\ttargetEntryIndex++;\n\t}\n\n\treturn -1;\n}\n\n\n/*\n * WrapTaskListForProjection wraps task query string to only select given\n * projected columns. It modifies the taskList.\n */\nstatic void\nWrapTaskListForProjection(List *taskList, List *projectedTargetEntries)\n{\n\tStringInfo projectedColumnsString = makeStringInfo();\n\tint entryIndex = 0;\n\tTargetEntry *targetEntry = NULL;\n\tforeach_declared_ptr(targetEntry, projectedTargetEntries)\n\t{\n\t\tif (entryIndex != 0)\n\t\t{\n\t\t\tappendStringInfoChar(projectedColumnsString, ',');\n\t\t}\n\n\t\tchar *columnName = targetEntry->resname;\n\t\tAssert(columnName != NULL);\n\t\tappendStringInfoString(projectedColumnsString, quote_identifier(columnName));\n\n\t\tentryIndex++;\n\t}\n\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tStringInfo wrappedQuery = makeStringInfo();\n\t\tappendStringInfo(wrappedQuery, \"SELECT %s FROM (%s) subquery\",\n\t\t\t\t\t\t projectedColumnsString->data,\n\t\t\t\t\t\t TaskQueryString(task));\n\t\tSetTaskQueryString(task, wrappedQuery->data);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/intermediate_results.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * intermediate_results.c\n *   Functions for writing and reading intermediate results.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"catalog/pg_enum.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/copy.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/primnodes.h\"\n#include \"storage/fd.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/error_codes.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/transaction_identifier.h\"\n#include \"distributed/transmit.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/utils/directory.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n\nstatic List *CreatedResultsDirectories = NIL;\n\n\n/* CopyDestReceiver can be used to stream results into a distributed table */\ntypedef struct RemoteFileDestReceiver\n{\n\t/* public DestReceiver interface */\n\tDestReceiver pub;\n\n\tconst char *resultId;\n\n\t/* descriptor of the tuples that are sent to the worker */\n\tTupleDesc tupleDescriptor;\n\n\t/* EState for per-tuple memory allocation */\n\tEState *executorState;\n\n\t/* MemoryContext for DestReceiver session */\n\tMemoryContext memoryContext;\n\n\t/* worker nodes to send data to */\n\tList *initialNodeList;\n\tList *connectionList;\n\n\t/* whether to write to a local file */\n\tbool writeLocalFile;\n\tFileCompat fileCompat;\n\n\t/* state on how to copy out data types */\n\tCopyOutState copyOutState;\n\tFmgrInfo *columnOutputFunctions;\n\n\t/* statistics */\n\tuint64 tuplesSent;\n\tuint64 bytesSent;\n} RemoteFileDestReceiver;\n\n/* Enumeration to track one copy query's status on the client */\ntypedef enum CopyStatus\n{\n\tCLIENT_INVALID_COPY = 0,\n\tCLIENT_COPY_MORE = 1,\n\tCLIENT_COPY_FAILED = 2,\n\tCLIENT_COPY_DONE = 3\n} CopyStatus;\n\n\nstatic void RemoteFileDestReceiverStartup(DestReceiver *dest, int operation,\n\t\t\t\t\t\t\t\t\t\t  TupleDesc inputTupleDescriptor);\nstatic void PrepareIntermediateResultBroadcast(RemoteFileDestReceiver *resultDest);\nstatic StringInfo ConstructCopyResultStatement(const char *resultId);\nstatic bool RemoteFileDestReceiverReceive(TupleTableSlot *slot, DestReceiver *dest);\nstatic void BroadcastCopyData(StringInfo dataBuffer, List *connectionList);\nstatic void SendCopyDataOverConnection(StringInfo dataBuffer,\n\t\t\t\t\t\t\t\t\t   MultiConnection *connection);\nstatic void RemoteFileDestReceiverShutdown(DestReceiver *destReceiver);\nstatic void RemoteFileDestReceiverDestroy(DestReceiver *destReceiver);\n\nstatic char * IntermediateResultsDirectory(void);\nstatic void ReadIntermediateResultsIntoFuncOutput(FunctionCallInfo fcinfo,\n\t\t\t\t\t\t\t\t\t\t\t\t  char *copyFormat,\n\t\t\t\t\t\t\t\t\t\t\t\t  Datum *resultIdArray,\n\t\t\t\t\t\t\t\t\t\t\t\t  int resultCount);\nstatic uint64 FetchRemoteIntermediateResult(MultiConnection *connection, char *resultId);\nstatic CopyStatus CopyDataFromConnection(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t FileCompat *fileCompat,\n\t\t\t\t\t\t\t\t\t\t uint64 *bytesReceived);\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(read_intermediate_result);\nPG_FUNCTION_INFO_V1(read_intermediate_result_array);\nPG_FUNCTION_INFO_V1(broadcast_intermediate_result);\nPG_FUNCTION_INFO_V1(create_intermediate_result);\nPG_FUNCTION_INFO_V1(fetch_intermediate_results);\n\n\n/*\n * broadcast_intermediate_result executes a query and streams the results\n * into a file on all workers.\n */\nDatum\nbroadcast_intermediate_result(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *resultIdText = PG_GETARG_TEXT_P(0);\n\tchar *resultIdString = text_to_cstring(resultIdText);\n\ttext *queryText = PG_GETARG_TEXT_P(1);\n\tchar *queryString = text_to_cstring(queryText);\n\tbool writeLocalFile = false;\n\tParamListInfo paramListInfo = NULL;\n\n\t/*\n\t * Make sure that this transaction has a distributed transaction ID.\n\t *\n\t * Intermediate results will be stored in a directory that is derived\n\t * from the distributed transaction ID.\n\t */\n\tUseCoordinatedTransaction();\n\n\tList *nodeList = ActivePrimaryNonCoordinatorNodeList(NoLock);\n\tEState *estate = CreateExecutorState();\n\tRemoteFileDestReceiver *resultDest =\n\t\t(RemoteFileDestReceiver *) CreateRemoteFileDestReceiver(resultIdString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\testate,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twriteLocalFile);\n\n\tExecuteQueryStringIntoDestReceiver(queryString, paramListInfo,\n\t\t\t\t\t\t\t\t\t   (DestReceiver *) resultDest);\n\n\tFreeExecutorState(estate);\n\n\tPG_RETURN_INT64(resultDest->tuplesSent);\n}\n\n\n/*\n * create_intermediate_result executes a query and writes the results\n * into a local file.\n */\nDatum\ncreate_intermediate_result(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *resultIdText = PG_GETARG_TEXT_P(0);\n\tchar *resultIdString = text_to_cstring(resultIdText);\n\ttext *queryText = PG_GETARG_TEXT_P(1);\n\tchar *queryString = text_to_cstring(queryText);\n\tList *nodeList = NIL;\n\tbool writeLocalFile = true;\n\tParamListInfo paramListInfo = NULL;\n\n\t/*\n\t * Make sure that this transaction has a distributed transaction ID.\n\t *\n\t * Intermediate results will be stored in a directory that is derived\n\t * from the distributed transaction ID.\n\t */\n\tUseCoordinatedTransaction();\n\n\tEState *estate = CreateExecutorState();\n\tRemoteFileDestReceiver *resultDest =\n\t\t(RemoteFileDestReceiver *) CreateRemoteFileDestReceiver(resultIdString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\testate,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twriteLocalFile);\n\n\tExecuteQueryStringIntoDestReceiver(queryString, paramListInfo,\n\t\t\t\t\t\t\t\t\t   (DestReceiver *) resultDest);\n\n\tFreeExecutorState(estate);\n\n\tPG_RETURN_INT64(resultDest->tuplesSent);\n}\n\n\n/*\n * CreateRemoteFileDestReceiver creates a DestReceiver that streams results\n * to a set of worker nodes. If the scope of the intermediate result is a\n * distributed transaction, then it's up to the caller to ensure that a\n * coordinated transaction is started prior to using the DestReceiver.\n */\nDestReceiver *\nCreateRemoteFileDestReceiver(const char *resultId, EState *executorState,\n\t\t\t\t\t\t\t List *initialNodeList, bool writeLocalFile)\n{\n\tRemoteFileDestReceiver *resultDest = (RemoteFileDestReceiver *) palloc0(\n\t\tsizeof(RemoteFileDestReceiver));\n\n\t/* set up the DestReceiver function pointers */\n\tresultDest->pub.receiveSlot = RemoteFileDestReceiverReceive;\n\tresultDest->pub.rStartup = RemoteFileDestReceiverStartup;\n\tresultDest->pub.rShutdown = RemoteFileDestReceiverShutdown;\n\tresultDest->pub.rDestroy = RemoteFileDestReceiverDestroy;\n\tresultDest->pub.mydest = DestCopyOut;\n\n\t/* set up output parameters */\n\tresultDest->resultId = resultId;\n\tresultDest->executorState = executorState;\n\tresultDest->initialNodeList = initialNodeList;\n\tresultDest->memoryContext = CurrentMemoryContext;\n\tresultDest->writeLocalFile = writeLocalFile;\n\n\treturn (DestReceiver *) resultDest;\n}\n\n\n/*\n * RemoteFileDestReceiverBytesSent returns number of bytes sent per remote worker.\n */\nuint64\nRemoteFileDestReceiverBytesSent(DestReceiver *destReceiver)\n{\n\tRemoteFileDestReceiver *remoteDestReceiver = (RemoteFileDestReceiver *) destReceiver;\n\treturn remoteDestReceiver->bytesSent;\n}\n\n\n/*\n * RemoteFileDestReceiverStartup implements the rStartup interface of\n * RemoteFileDestReceiver. It opens connections to the nodes in initialNodeList,\n * and sends the COPY command on all connections.\n */\nstatic void\nRemoteFileDestReceiverStartup(DestReceiver *dest, int operation,\n\t\t\t\t\t\t\t  TupleDesc inputTupleDescriptor)\n{\n\tRemoteFileDestReceiver *resultDest = (RemoteFileDestReceiver *) dest;\n\n\tconst char *delimiterCharacter = \"\\t\";\n\tconst char *nullPrintCharacter = \"\\\\N\";\n\n\tresultDest->tupleDescriptor = inputTupleDescriptor;\n\n\t/* define how tuples will be serialised */\n\tCopyOutState copyOutState = (CopyOutState) palloc0(sizeof(CopyOutStateData));\n\tcopyOutState->delim = (char *) delimiterCharacter;\n\tcopyOutState->null_print = (char *) nullPrintCharacter;\n\tcopyOutState->null_print_client = (char *) nullPrintCharacter;\n\tcopyOutState->binary = CanUseBinaryCopyFormat(inputTupleDescriptor);\n\tcopyOutState->fe_msgbuf = makeStringInfo();\n\tcopyOutState->rowcontext = GetPerTupleMemoryContext(resultDest->executorState);\n\tresultDest->copyOutState = copyOutState;\n\n\tresultDest->columnOutputFunctions = ColumnOutputFunctions(inputTupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  copyOutState->binary);\n}\n\n\n/*\n * PrepareIntermediateResultBroadcast gets a RemoteFileDestReceiver and does\n * the necessary initializations including initiating the remote connections\n * and creating the local file, which is necessary (it might be both).\n */\nstatic void\nPrepareIntermediateResultBroadcast(RemoteFileDestReceiver *resultDest)\n{\n\tList *initialNodeList = resultDest->initialNodeList;\n\tconst char *resultId = resultDest->resultId;\n\tList *connectionList = NIL;\n\tCopyOutState copyOutState = resultDest->copyOutState;\n\n\tif (resultDest->writeLocalFile)\n\t{\n\t\tconst int fileFlags = (O_APPEND | O_CREAT | O_RDWR | O_TRUNC | PG_BINARY);\n\n\t\t/* make sure the directory exists */\n\t\tCreateIntermediateResultsDirectory();\n\n\t\tconst char *fileName = QueryResultFileName(resultId);\n\n\t\tresultDest->fileCompat = FileCompatFromFileStart(FileOpenForTransmit(fileName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t fileFlags));\n\t}\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, initialNodeList)\n\t{\n\t\tint flags = 0;\n\n\t\tconst char *nodeName = workerNode->workerName;\n\t\tint nodePort = workerNode->workerPort;\n\n\t\tMultiConnection *connection = StartNodeConnection(flags, nodeName, nodePort);\n\t\tClaimConnectionExclusively(connection);\n\t\tMarkRemoteTransactionCritical(connection);\n\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\tFinishConnectionListEstablishment(connectionList);\n\n\t/* must open transaction blocks to use intermediate results */\n\tRemoteTransactionsBeginIfNecessary(connectionList);\n\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tStringInfo copyCommand = ConstructCopyResultStatement(resultId);\n\n\t\tbool querySent = SendRemoteCommand(connection, copyCommand->data);\n\t\tif (!querySent)\n\t\t{\n\t\t\tReportConnectionError(connection, ERROR);\n\t\t}\n\t}\n\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tbool raiseInterrupts = true;\n\n\t\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\t\tif (PQresultStatus(result) != PGRES_COPY_IN)\n\t\t{\n\t\t\tReportResultError(connection, result, ERROR);\n\t\t}\n\n\t\tPQclear(result);\n\t}\n\n\tif (copyOutState->binary)\n\t{\n\t\t/* send headers when using binary encoding */\n\t\tresetStringInfo(copyOutState->fe_msgbuf);\n\t\tAppendCopyBinaryHeaders(copyOutState);\n\t\tBroadcastCopyData(copyOutState->fe_msgbuf, connectionList);\n\n\t\tif (resultDest->writeLocalFile)\n\t\t{\n\t\t\tWriteToLocalFile(copyOutState->fe_msgbuf, &resultDest->fileCompat);\n\t\t}\n\t}\n\n\tresultDest->connectionList = connectionList;\n}\n\n\n/*\n * ConstructCopyResultStatement constructs the text of a COPY statement\n * for copying into a result file.\n */\nstatic StringInfo\nConstructCopyResultStatement(const char *resultId)\n{\n\tStringInfo command = makeStringInfo();\n\n\tappendStringInfo(command, \"COPY \\\"%s\\\" FROM STDIN WITH (format result)\",\n\t\t\t\t\t resultId);\n\n\treturn command;\n}\n\n\n/*\n * RemoteFileDestReceiverReceive implements the receiveSlot function of\n * RemoteFileDestReceiver. It takes a TupleTableSlot and sends the contents to\n * all worker nodes.\n */\nstatic bool\nRemoteFileDestReceiverReceive(TupleTableSlot *slot, DestReceiver *dest)\n{\n\tRemoteFileDestReceiver *resultDest = (RemoteFileDestReceiver *) dest;\n\n\tif (resultDest->tuplesSent == 0)\n\t{\n\t\t/*\n\t\t *  We get the first tuple, lets initialize the remote connections\n\t\t *  and/or the local file.\n\t\t */\n\t\tPrepareIntermediateResultBroadcast(resultDest);\n\t}\n\n\tTupleDesc tupleDescriptor = resultDest->tupleDescriptor;\n\n\tList *connectionList = resultDest->connectionList;\n\tCopyOutState copyOutState = resultDest->copyOutState;\n\tFmgrInfo *columnOutputFunctions = resultDest->columnOutputFunctions;\n\n\tStringInfo copyData = copyOutState->fe_msgbuf;\n\n\tEState *executorState = resultDest->executorState;\n\tMemoryContext executorTupleContext = GetPerTupleMemoryContext(executorState);\n\tMemoryContext oldContext = MemoryContextSwitchTo(executorTupleContext);\n\n\tslot_getallattrs(slot);\n\n\tDatum *columnValues = slot->tts_values;\n\tbool *columnNulls = slot->tts_isnull;\n\n\tresetStringInfo(copyData);\n\n\t/* construct row in COPY format */\n\tAppendCopyRowData(columnValues, columnNulls, tupleDescriptor,\n\t\t\t\t\t  copyOutState, columnOutputFunctions, NULL);\n\n\t/* send row to nodes */\n\tBroadcastCopyData(copyData, connectionList);\n\n\t/* write to local file (if applicable) */\n\tif (resultDest->writeLocalFile)\n\t{\n\t\tWriteToLocalFile(copyOutState->fe_msgbuf, &resultDest->fileCompat);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\n\tresultDest->tuplesSent++;\n\tresultDest->bytesSent += copyData->len;\n\n\tResetPerTupleExprContext(executorState);\n\n\treturn true;\n}\n\n\n/*\n * WriteToLocalResultsFile writes the bytes in a StringInfo to a local file.\n */\nvoid\nWriteToLocalFile(StringInfo copyData, FileCompat *fileCompat)\n{\n\tint bytesWritten = FileWriteCompat(fileCompat, copyData->data,\n\t\t\t\t\t\t\t\t\t   copyData->len,\n\t\t\t\t\t\t\t\t\t   PG_WAIT_IO);\n\tif (bytesWritten < 0)\n\t{\n\t\tereport(ERROR, (errcode_for_file_access(),\n\t\t\t\t\t\terrmsg(\"could not append to file: %m\")));\n\t}\n}\n\n\n/*\n * RemoteFileDestReceiverShutdown implements the rShutdown interface of\n * RemoteFileDestReceiver. It ends the COPY on all the open connections and closes\n * the relation.\n */\nstatic void\nRemoteFileDestReceiverShutdown(DestReceiver *destReceiver)\n{\n\tRemoteFileDestReceiver *resultDest = (RemoteFileDestReceiver *) destReceiver;\n\n\tif (resultDest->tuplesSent == 0)\n\t{\n\t\t/*\n\t\t *  We have not received any tuples (when the intermediate result\n\t\t *  returns zero rows). Still, we want to create the necessary\n\t\t *  intermediate result files even if they are empty, as the query\n\t\t *  execution requires the files to be present.\n\t\t */\n\t\tPrepareIntermediateResultBroadcast(resultDest);\n\t}\n\n\tList *connectionList = resultDest->connectionList;\n\tCopyOutState copyOutState = resultDest->copyOutState;\n\n\tif (copyOutState->binary)\n\t{\n\t\t/* send footers when using binary encoding */\n\t\tresetStringInfo(copyOutState->fe_msgbuf);\n\t\tAppendCopyBinaryFooters(copyOutState);\n\t\tBroadcastCopyData(copyOutState->fe_msgbuf, connectionList);\n\n\t\tif (resultDest->writeLocalFile)\n\t\t{\n\t\t\tWriteToLocalFile(copyOutState->fe_msgbuf, &resultDest->fileCompat);\n\t\t}\n\t}\n\n\t/* close the COPY input */\n\tEndRemoteCopy(0, connectionList);\n\n\tif (resultDest->writeLocalFile)\n\t{\n\t\tFileClose(resultDest->fileCompat.fd);\n\t}\n}\n\n\n/*\n * BroadcastCopyData sends copy data to all connections in a list.\n */\nstatic void\nBroadcastCopyData(StringInfo dataBuffer, List *connectionList)\n{\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tSendCopyDataOverConnection(dataBuffer, connection);\n\t}\n}\n\n\n/*\n * SendCopyDataOverConnection sends serialized COPY data over the given\n * connection.\n */\nstatic void\nSendCopyDataOverConnection(StringInfo dataBuffer, MultiConnection *connection)\n{\n\tif (!PutRemoteCopyData(connection, dataBuffer->data, dataBuffer->len))\n\t{\n\t\tReportConnectionError(connection, ERROR);\n\t}\n}\n\n\n/*\n * RemoteFileDestReceiverDestroy frees memory allocated as part of the\n * RemoteFileDestReceiver and closes file descriptors.\n */\nstatic void\nRemoteFileDestReceiverDestroy(DestReceiver *destReceiver)\n{\n\tRemoteFileDestReceiver *resultDest = (RemoteFileDestReceiver *) destReceiver;\n\n\tif (resultDest->copyOutState)\n\t{\n\t\tpfree(resultDest->copyOutState);\n\t}\n\n\tif (resultDest->columnOutputFunctions)\n\t{\n\t\tpfree(resultDest->columnOutputFunctions);\n\t}\n\n\tpfree(resultDest);\n}\n\n\n/*\n * SendQueryResultViaCopy is called when a COPY \"resultid\" TO STDOUT\n * WITH (format result) command is received from the client. The\n * contents of the file are sent directly to the client.\n */\nvoid\nSendQueryResultViaCopy(const char *resultId)\n{\n\tconst char *resultFileName = QueryResultFileName(resultId);\n\n\tSendRegularFile(resultFileName);\n}\n\n\n/*\n * ReceiveQueryResultViaCopy is called when a COPY \"resultid\" FROM\n * STDIN WITH (format result) command is received from the client.\n * The command is followed by the raw copy data stream, which is\n * redirected to a file.\n *\n * File names are automatically prefixed with the user OID. Users\n * are only allowed to read query results from their own directory.\n */\nvoid\nReceiveQueryResultViaCopy(const char *resultId)\n{\n\tCreateIntermediateResultsDirectory();\n\n\tconst char *resultFileName = QueryResultFileName(resultId);\n\n\tRedirectCopyDataToRegularFile(resultFileName);\n}\n\n\n/*\n * CreateIntermediateResultsDirectory creates the intermediate result\n * directory for the current transaction if it does not exist and ensures\n * that the directory is removed at the end of the transaction.\n */\nchar *\nCreateIntermediateResultsDirectory(void)\n{\n\tchar *resultDirectory = IntermediateResultsDirectory();\n\n\tint makeOK = MakePGDirectory(resultDirectory);\n\tif (makeOK != 0)\n\t{\n\t\tif (errno == EEXIST)\n\t\t{\n\t\t\t/* someone else beat us to it, that's ok */\n\t\t\treturn resultDirectory;\n\t\t}\n\n\t\tereport(ERROR, (errcode_for_file_access(),\n\t\t\t\t\t\terrmsg(\"could not create intermediate results directory \"\n\t\t\t\t\t\t\t   \"\\\"%s\\\": %m\",\n\t\t\t\t\t\t\t   resultDirectory)));\n\t}\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(TopTransactionContext);\n\n\tCreatedResultsDirectories =\n\t\tlappend(CreatedResultsDirectories, pstrdup(resultDirectory));\n\n\tMemoryContextSwitchTo(oldContext);\n\n\treturn resultDirectory;\n}\n\n\n/*\n * QueryResultFileName returns the file name in which to store\n * an intermediate result with the given key in the per transaction\n * result directory.\n */\nchar *\nQueryResultFileName(const char *resultId)\n{\n\tStringInfo resultFileName = makeStringInfo();\n\tconst char *resultDirectory = IntermediateResultsDirectory();\n\tchar *checkChar = (char *) resultId;\n\n\tfor (; *checkChar; checkChar++)\n\t{\n\t\tif (!((*checkChar >= 'a' && *checkChar <= 'z') ||\n\t\t\t  (*checkChar >= 'A' && *checkChar <= 'Z') ||\n\t\t\t  (*checkChar >= '0' && *checkChar <= '9') ||\n\t\t\t  (*checkChar == '_') || (*checkChar == '-')))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_INVALID_NAME),\n\t\t\t\t\t\t\terrmsg(\"result key \\\"%s\\\" contains invalid character\",\n\t\t\t\t\t\t\t\t   resultId),\n\t\t\t\t\t\t\terrhint(\"Result keys may only contain letters, numbers, \"\n\t\t\t\t\t\t\t\t\t\"underscores and hyphens.\")));\n\t\t}\n\t}\n\n\tappendStringInfo(resultFileName, \"%s/%s.data\",\n\t\t\t\t\t resultDirectory, resultId);\n\n\treturn resultFileName->data;\n}\n\n\n/*\n * IntermediateResultsDirectory returns the directory to use for a query result\n * file with a particular key. The filename includes the user OID, such\n * that users can never read each other's files.\n *\n * In a distributed transaction, the directory has the form:\n * base/pgsql_job_cache/<user id>_<coordinator node id>_<transaction number>/\n *\n * In a non-distributed transaction, the directory has the form:\n * base/pgsql_job_cache/<user id>_<process id>/\n *\n * The latter form can be used for testing COPY ... WITH (format result) without\n * assigning a distributed transaction ID.\n *\n * The pgsql_job_cache directory is emptied on restart in case of failure.\n */\nstatic char *\nIntermediateResultsDirectory(void)\n{\n\tStringInfo resultFileName = makeStringInfo();\n\tOid userId = GetUserId();\n\tDistributedTransactionId *transactionId = GetCurrentDistributedTransactionId();\n\tint initiatorNodeIdentifier = transactionId->initiatorNodeIdentifier;\n\tuint64 transactionNumber = transactionId->transactionNumber;\n\n\tif (transactionNumber > 0)\n\t{\n\t\tappendStringInfo(resultFileName, \"base/\" PG_JOB_CACHE_DIR \"/%u_%u_%lu\",\n\t\t\t\t\t\t userId, initiatorNodeIdentifier, transactionNumber);\n\t}\n\telse\n\t{\n\t\tappendStringInfo(resultFileName, \"base/\" PG_JOB_CACHE_DIR \"/%u_%u\",\n\t\t\t\t\t\t userId, MyProcPid);\n\t}\n\n\treturn resultFileName->data;\n}\n\n\n/*\n * RemoveIntermediateResultsDirectories removes the intermediate result directory\n * for the current distributed transaction, if any was created.\n */\nvoid\nRemoveIntermediateResultsDirectories(void)\n{\n\tchar *directoryElement = NULL;\n\tforeach_declared_ptr(directoryElement, CreatedResultsDirectories)\n\t{\n\t\t/*\n\t\t * The shared directory is renamed before deleting it. Otherwise it\n\t\t * would be possible for another backend to write a file, while we are\n\t\t * deleting the directory. Since rename is atomic by POSIX standards\n\t\t * that's not possible. The current PID is included in the new\n\t\t * filename, so there can be no collisions with other backends.\n\t\t */\n\t\tchar *sharedName = directoryElement;\n\t\tStringInfo privateName = makeStringInfo();\n\t\tappendStringInfo(privateName, \"%s.removed-by-%d\", sharedName, MyProcPid);\n\t\tif (rename(sharedName, privateName->data))\n\t\t{\n\t\t\tereport(LOG,\n\t\t\t\t\t(errcode_for_file_access(),\n\t\t\t\t\t errmsg(\n\t\t\t\t\t\t \"could not rename intermediate results directory \\\"%s\\\" to \\\"%s\\\": %m\",\n\t\t\t\t\t\t sharedName, privateName->data)));\n\n\t\t\t/* rename failed for some reason, we do a best effort removal of\n\t\t\t * the shared directory */\n\n\t\t\tPathNameDeleteTemporaryDir(sharedName);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tPathNameDeleteTemporaryDir(privateName->data);\n\t\t}\n\t}\n\n\t/* cleanup */\n\tlist_free_deep(CreatedResultsDirectories);\n\n\tCreatedResultsDirectories = NIL;\n}\n\n\n/*\n * IntermediateResultSize returns the file size of the intermediate result\n * or -1 if the file does not exist.\n */\nint64\nIntermediateResultSize(const char *resultId)\n{\n\tstruct stat fileStat;\n\n\tchar *resultFileName = QueryResultFileName(resultId);\n\tint statOK = stat(resultFileName, &fileStat);\n\tif (statOK < 0)\n\t{\n\t\treturn -1;\n\t}\n\n\treturn (int64) fileStat.st_size;\n}\n\n\n/*\n * read_intermediate_result is a UDF that returns a COPY-formatted intermediate\n * result file as a set of records. The file is parsed according to the columns\n * definition list specified by the user, e.g.:\n *\n * SELECT * FROM read_intermediate_result('foo', 'csv') AS (a int, b int)\n *\n * The file is read from the directory returned by IntermediateResultsDirectory,\n * which includes the user ID.\n *\n * read_intermediate_result is a volatile function because it cannot be\n * evaluated until execution time, but for distributed planning purposes we can\n * treat it in the same way as immutable functions and reference tables, since\n * we know it will return the same result on all nodes.\n */\nDatum\nread_intermediate_result(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tDatum resultId = PG_GETARG_DATUM(0);\n\tDatum copyFormatOidDatum = PG_GETARG_DATUM(1);\n\tDatum copyFormatLabelDatum = DirectFunctionCall1(enum_out, copyFormatOidDatum);\n\tchar *copyFormatLabel = DatumGetCString(copyFormatLabelDatum);\n\n\tReadIntermediateResultsIntoFuncOutput(fcinfo, copyFormatLabel, &resultId, 1);\n\n\tPG_RETURN_DATUM(0);\n}\n\n\n/*\n * read_intermediate_result_array returns the set of records in a set of given\n * COPY-formatted intermediate result files.\n *\n * The usage and semantics of this is same as read_intermediate_result(), except\n * that its first argument is an array of result ids.\n */\nDatum\nread_intermediate_result_array(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tArrayType *resultIdObject = PG_GETARG_ARRAYTYPE_P(0);\n\tDatum copyFormatOidDatum = PG_GETARG_DATUM(1);\n\n\tDatum copyFormatLabelDatum = DirectFunctionCall1(enum_out, copyFormatOidDatum);\n\tchar *copyFormatLabel = DatumGetCString(copyFormatLabelDatum);\n\n\tint32 resultCount = ArrayGetNItems(ARR_NDIM(resultIdObject), ARR_DIMS(\n\t\t\t\t\t\t\t\t\t\t   resultIdObject));\n\tDatum *resultIdArray = DeconstructArrayObject(resultIdObject);\n\n\tReadIntermediateResultsIntoFuncOutput(fcinfo, copyFormatLabel,\n\t\t\t\t\t\t\t\t\t\t  resultIdArray, resultCount);\n\n\tPG_RETURN_DATUM(0);\n}\n\n\n/*\n * ReadIntermediateResultsIntoFuncOutput reads the given result files and stores\n * them at the function's output tuple store. Errors out if any of the result files\n * don't exist.\n */\nstatic void\nReadIntermediateResultsIntoFuncOutput(FunctionCallInfo fcinfo, char *copyFormat,\n\t\t\t\t\t\t\t\t\t  Datum *resultIdArray, int resultCount)\n{\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tfor (int resultIndex = 0; resultIndex < resultCount; resultIndex++)\n\t{\n\t\tchar *resultId = TextDatumGetCString(resultIdArray[resultIndex]);\n\t\tchar *resultFileName = QueryResultFileName(resultId);\n\t\tstruct stat fileStat;\n\n\t\tint statOK = stat(resultFileName, &fileStat);\n\t\tif (statOK != 0)\n\t\t{\n\t\t\t/*\n\t\t\t * When the file does not exist, it could mean two different things.\n\t\t\t * First -- and a lot more common -- case is that a failure happened\n\t\t\t * in a concurrent backend on the same distributed transaction. And,\n\t\t\t * one of the backends in that transaction has already been roll\n\t\t\t * backed, which has already removed the file. If we throw an error\n\t\t\t * here, the user might see this error instead of the actual error\n\t\t\t * message. Instead, we prefer to WARN the user and pretend that the\n\t\t\t * file has no data in it. In the end, the user would see the actual\n\t\t\t * error message for the failure.\n\t\t\t *\n\t\t\t * Second, in case of any bugs in intermediate result broadcasts,\n\t\t\t * we could try to read a non-existing file. That is most likely\n\t\t\t * to happen during development.\n\t\t\t */\n\t\t\tereport(WARNING, (errcode(ERRCODE_CITUS_INTERMEDIATE_RESULT_NOT_FOUND),\n\t\t\t\t\t\t\t  errmsg(\"Query could not find the intermediate result file \"\n\t\t\t\t\t\t\t\t\t \"\\\"%s\\\", it was mostly likely deleted due to an \"\n\t\t\t\t\t\t\t\t\t \"error in a parallel process within the same \"\n\t\t\t\t\t\t\t\t\t \"distributed transaction\", resultId)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tReadFileIntoTupleStore(resultFileName, copyFormat, tupleDescriptor,\n\t\t\t\t\t\t\t\t   tupleStore);\n\t\t}\n\t}\n}\n\n\n/*\n * fetch_intermediate_results fetches a set of intermediate results defined in an\n * array of result IDs from a remote node and writes them to a local intermediate\n * result with the same ID.\n */\nDatum\nfetch_intermediate_results(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tArrayType *resultIdObject = PG_GETARG_ARRAYTYPE_P(0);\n\tDatum *resultIdArray = DeconstructArrayObject(resultIdObject);\n\tint32 resultCount = ArrayObjectCount(resultIdObject);\n\ttext *remoteHostText = PG_GETARG_TEXT_P(1);\n\tchar *remoteHost = text_to_cstring(remoteHostText);\n\tint remotePort = PG_GETARG_INT32(2);\n\n\tint connectionFlags = FORCE_NEW_CONNECTION;\n\tint resultIndex = 0;\n\tint64 totalBytesWritten = 0L;\n\n\tif (resultCount == 0)\n\t{\n\t\tPG_RETURN_INT64(0);\n\t}\n\n\tif (!IsMultiStatementTransaction())\n\t{\n\t\tereport(ERROR, (errmsg(\"fetch_intermediate_results can only be used in a \"\n\t\t\t\t\t\t\t   \"distributed transaction\")));\n\t}\n\n\t/*\n\t * Make sure that this transaction has a distributed transaction ID.\n\t *\n\t * Intermediate results will be stored in a directory that is derived\n\t * from the distributed transaction ID.\n\t */\n\tEnsureDistributedTransactionId();\n\n\tMultiConnection *connection = GetNodeConnection(connectionFlags, remoteHost,\n\t\t\t\t\t\t\t\t\t\t\t\t\tremotePort);\n\n\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot connect to %s:%d to fetch intermediate results\",\n\t\t\t\t\t\t\t   remoteHost, remotePort)));\n\t}\n\n\tStringInfo beginAndSetXactId = BeginAndSetDistributedTransactionIdCommand();\n\tExecuteCriticalRemoteCommand(connection, beginAndSetXactId->data);\n\n\tCreateIntermediateResultsDirectory();\n\n\tfor (resultIndex = 0; resultIndex < resultCount; resultIndex++)\n\t{\n\t\tchar *resultId = TextDatumGetCString(resultIdArray[resultIndex]);\n\n\t\ttotalBytesWritten += FetchRemoteIntermediateResult(connection, resultId);\n\t}\n\n\tExecuteCriticalRemoteCommand(connection, \"END\");\n\n\tCloseConnection(connection);\n\n\tPG_RETURN_INT64(totalBytesWritten);\n}\n\n\n/*\n * FetchRemoteIntermediateResult fetches a remote intermediate result over\n * the given connection.\n */\nstatic uint64\nFetchRemoteIntermediateResult(MultiConnection *connection, char *resultId)\n{\n\tchar *localPath = QueryResultFileName(resultId);\n\n\tstruct stat fileStat;\n\tint statOK = stat(localPath, &fileStat);\n\tif (statOK == 0)\n\t{\n\t\t/*\n\t\t * File exists, most likely because we are trying to fetch a\n\t\t * a file from a node to itself. Skip doing work.\n\t\t */\n\t\treturn fileStat.st_size;\n\t}\n\n\tuint64 totalBytesWritten = 0;\n\n\tStringInfo copyCommand = makeStringInfo();\n\tconst int fileFlags = (O_APPEND | O_CREAT | O_RDWR | O_TRUNC | PG_BINARY);\n\n\tPGconn *pgConn = connection->pgConn;\n\tint socket = PQsocket(pgConn);\n\tbool raiseErrors = true;\n\n\tappendStringInfo(copyCommand, \"COPY \\\"%s\\\" TO STDOUT WITH (format result)\",\n\t\t\t\t\t resultId);\n\n\tif (!SendRemoteCommand(connection, copyCommand->data))\n\t{\n\t\tReportConnectionError(connection, ERROR);\n\t}\n\n\tPGresult *result = GetRemoteCommandResult(connection, raiseErrors);\n\tif (PQresultStatus(result) != PGRES_COPY_OUT)\n\t{\n\t\tReportResultError(connection, result, ERROR);\n\t}\n\n\tPQclear(result);\n\n\tFile fileDesc = FileOpenForTransmit(localPath, fileFlags);\n\tFileCompat fileCompat = FileCompatFromFileStart(fileDesc);\n\n\twhile (true)\n\t{\n\t\tint waitFlags = WL_SOCKET_READABLE | WL_POSTMASTER_DEATH;\n\n\t\tCopyStatus copyStatus = CopyDataFromConnection(connection, &fileCompat,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   &totalBytesWritten);\n\t\tif (copyStatus == CLIENT_COPY_FAILED)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"failed to read result \\\"%s\\\" from node %s:%d\",\n\t\t\t\t\t\t\t\t   resultId, connection->hostname, connection->port)));\n\t\t}\n\t\telse if (copyStatus == CLIENT_COPY_DONE)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tAssert(copyStatus == CLIENT_COPY_MORE);\n\n\t\tint rc = WaitLatchOrSocket(MyLatch, waitFlags, socket, 0, PG_WAIT_EXTENSION);\n\t\tif (rc & WL_POSTMASTER_DEATH)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"postmaster was shut down, exiting\")));\n\t\t}\n\n\t\tif (rc & WL_LATCH_SET)\n\t\t{\n\t\t\tResetLatch(MyLatch);\n\t\t\tCHECK_FOR_INTERRUPTS();\n\t\t}\n\t}\n\n\tFileClose(fileDesc);\n\n\tClearResults(connection, raiseErrors);\n\n\treturn totalBytesWritten;\n}\n\n\n/*\n * CopyDataFromConnection reads a row of copy data from connection and writes it\n * to the given file.\n */\nstatic CopyStatus\nCopyDataFromConnection(MultiConnection *connection, FileCompat *fileCompat,\n\t\t\t\t\t   uint64 *bytesReceived)\n{\n\t/*\n\t * Consume input to handle the case where previous copy operation might have\n\t * received zero bytes.\n\t */\n\tint consumed = PQconsumeInput(connection->pgConn);\n\tif (consumed == 0)\n\t{\n\t\treturn CLIENT_COPY_FAILED;\n\t}\n\n\t/* receive copy data message in an asynchronous manner */\n\tchar *receiveBuffer = NULL;\n\tbool asynchronous = true;\n\tint receiveLength = PQgetCopyData(connection->pgConn, &receiveBuffer, asynchronous);\n\twhile (receiveLength > 0)\n\t{\n\t\t/* received copy data; append these data to file */\n\t\terrno = 0;\n\n\t\tint bytesWritten = FileWriteCompat(fileCompat, receiveBuffer,\n\t\t\t\t\t\t\t\t\t\t   receiveLength, PG_WAIT_IO);\n\t\tif (bytesWritten != receiveLength)\n\t\t{\n\t\t\tereport(ERROR, (errcode_for_file_access(),\n\t\t\t\t\t\t\terrmsg(\"could not append to file: %m\")));\n\t\t}\n\n\t\t*bytesReceived += receiveLength;\n\t\tPQfreemem(receiveBuffer);\n\t\treceiveLength = PQgetCopyData(connection->pgConn, &receiveBuffer, asynchronous);\n\t}\n\n\tif (receiveLength == 0)\n\t{\n\t\t/* we cannot read more data without blocking */\n\t\treturn CLIENT_COPY_MORE;\n\t}\n\telse if (receiveLength == -1)\n\t{\n\t\t/* received copy done message */\n\t\tbool raiseInterrupts = true;\n\t\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\t\tExecStatusType resultStatus = PQresultStatus(result);\n\t\tCopyStatus copyStatus = 0;\n\n\t\tif (resultStatus == PGRES_COMMAND_OK)\n\t\t{\n\t\t\tcopyStatus = CLIENT_COPY_DONE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcopyStatus = CLIENT_COPY_FAILED;\n\n\t\t\tReportResultError(connection, result, WARNING);\n\t\t}\n\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\n\t\treturn copyStatus;\n\t}\n\telse\n\t{\n\t\tAssert(receiveLength == -2);\n\t\tReportConnectionError(connection, WARNING);\n\n\t\treturn CLIENT_COPY_FAILED;\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/local_executor.c",
    "content": "/*\n * local_executor.c\n *\n * The scope of the local execution is locally executing the queries on the\n * shards. In other words, local execution does not deal with any local tables\n * that are not on shards on the node that the query is being executed. In that\n * sense, the local executor is only triggered if the node has both the metadata\n * and the shards (e.g., only Citus MX worker nodes).\n *\n * The goal of the local execution is to skip the unnecessary network round-trip\n * happening on the node itself. Instead, identify the locally executable tasks\n * and simply call PostgreSQL's planner and executor.\n *\n * The local executor is an extension of the adaptive executor. So, the executor\n * uses adaptive executor's custom scan nodes.\n *\n * One thing to note is that Citus MX is only supported with replication factor\n * to be equal to 1, so keep that in mind while continuing the comments below.\n *\n * On the high level, there are 3 slightly different ways of utilizing local\n * execution:\n *\n * (1) Execution of local single shard queries of a distributed table\n *\n *      This is the simplest case. The executor kicks at the start of the adaptive\n *      executor, and since the query is only a single task the execution finishes\n *      without going to the network at all.\n *\n *      Even if there is a transaction block (or recursively planned CTEs), as\n *      long as the queries hit the shards on the same node, the local execution\n *      will kick in.\n *\n * (2) Execution of local single queries and remote multi-shard queries\n *\n *      The rule is simple. If a transaction block starts with a local query\n *      execution,\n *      all the other queries in the same transaction block that touch any local\n *      shard have to use the local execution. Although this sounds restrictive,\n *      we prefer to implement it in this way, otherwise we'd end-up with as\n *      complex scenarios as we have in the connection managements due to foreign\n *      keys.\n *\n *      See the following example:\n *      BEGIN;\n *          -- assume that the query is executed locally\n *          SELECT count(*) FROM test WHERE key = 1;\n *\n *          -- at this point, all the shards that reside on the\n *          -- node is executed locally one-by-one. After those finishes\n *          -- the remaining tasks are handled by adaptive executor\n *          SELECT count(*) FROM test;\n *\n *\n * (3) Modifications of reference tables\n *\n *\t\tModifications to reference tables have to be executed on all nodes. So,\n *      after the local execution, the adaptive executor keeps continuing the\n *      execution on the other nodes.\n *\n *\t\tNote that for read-only queries, after the local execution, there is no\n *      need to kick in adaptive executor.\n *\n * (4) Execution of multi shards local queries and\n *     remote multi-shard queries within a transaction block\n *\n *      We prefer local execution when we are inside a transaction block, because not using\n *      local execution might create some limitations for other commands in the transaction\n *      block. To simplify things, whenever we are inside a transaction block, we prefer local\n *      execution if possible.\n *\n *  There are also a few limitations/trade-offs that are worth mentioning.\n *  - The local execution on multiple shards might be slow because the execution\n *  has to happen one task at a time (e.g., no parallelism).\n *  - Related with the previous item, COPY command cannot be mixed with local\n *  execution in a transaction. The implication of that is any part of INSERT..SELECT\n *  via coordinator cannot happen via the local execution.\n */\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"executor/tstoreReceiver.h\"\n#include \"executor/tuptable.h\"\n#include \"nodes/params.h\"\n#include \"optimizer/optimizer.h\"\n#include \"utils/snapmgr.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/executor_util.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/local_plan_cache.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/remote_commands.h\" /* to access LogRemoteCommands */\n#include \"distributed/stats/stat_tenants.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n/* controlled via a GUC */\nbool EnableLocalExecution = true;\nbool LogLocalCommands = false;\n\n/* global variable that tracks whether the local execution is on a shard */\nuint64 LocalExecutorShardId = INVALID_SHARD_ID;\n\nstatic LocalExecutionStatus CurrentLocalExecutionStatus = LOCAL_EXECUTION_OPTIONAL;\n\nstatic void SplitLocalAndRemotePlacements(List *taskPlacementList,\n\t\t\t\t\t\t\t\t\t\t  List **localTaskPlacementList,\n\t\t\t\t\t\t\t\t\t\t  List **remoteTaskPlacementList);\nstatic uint64 LocallyExecuteTaskPlan(PlannedStmt *taskPlan, char *queryString,\n\t\t\t\t\t\t\t\t\t TupleDestination *tupleDest, Task *task,\n\t\t\t\t\t\t\t\t\t ParamListInfo paramListInfo);\nstatic uint64 ExecuteTaskPlan(PlannedStmt *taskPlan, char *queryString,\n\t\t\t\t\t\t\t  TupleDestination *tupleDest, Task *task,\n\t\t\t\t\t\t\t  ParamListInfo paramListInfo);\nstatic void RecordNonDistTableAccessesForTask(Task *task);\nstatic void LogLocalCommand(Task *task);\nstatic uint64 LocallyPlanAndExecuteMultipleQueries(List *queryStrings,\n\t\t\t\t\t\t\t\t\t\t\t\t   TupleDestination *tupleDest,\n\t\t\t\t\t\t\t\t\t\t\t\t   Task *task);\nstatic void SetColocationIdAndPartitionKeyValueForTasks(List *taskList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tJob *distributedPlan);\nstatic void LocallyExecuteUtilityTask(Task *task);\nstatic void ExecuteUdfTaskQuery(Query *localUdfCommandQuery);\nstatic void EnsureTransitionPossible(LocalExecutionStatus from,\n\t\t\t\t\t\t\t\t\t LocalExecutionStatus to);\n\n\n/*\n * GetCurrentLocalExecutionStatus returns the current local execution status.\n */\nLocalExecutionStatus\nGetCurrentLocalExecutionStatus(void)\n{\n\treturn CurrentLocalExecutionStatus;\n}\n\n\n/*\n * ExecuteLocalTaskList executes the given tasks locally.\n *\n * The function goes over the task list and executes them locally.\n * The returning tuples (if any) is stored in the tupleStoreState.\n *\n * The function returns totalRowsProcessed.\n */\nuint64\nExecuteLocalTaskList(List *taskList, TupleDestination *defaultTupleDest)\n{\n\tif (list_length(taskList) == 0)\n\t{\n\t\treturn 0;\n\t}\n\tDistributedPlan *distributedPlan = NULL;\n\tParamListInfo paramListInfo = NULL;\n\tbool isUtilityCommand = false;\n\treturn ExecuteLocalTaskListExtended(taskList, paramListInfo, distributedPlan,\n\t\t\t\t\t\t\t\t\t\tdefaultTupleDest, isUtilityCommand);\n}\n\n\n/*\n * ExecuteLocalUtilityTaskList executes the given tasks locally.\n *\n * The function returns totalRowsProcessed.\n */\nuint64\nExecuteLocalUtilityTaskList(List *utilityTaskList)\n{\n\tif (list_length(utilityTaskList) == 0)\n\t{\n\t\treturn 0;\n\t}\n\tDistributedPlan *distributedPlan = NULL;\n\tParamListInfo paramListInfo = NULL;\n\tTupleDestination *defaultTupleDest = CreateTupleDestNone();\n\tbool isUtilityCommand = true;\n\treturn ExecuteLocalTaskListExtended(utilityTaskList, paramListInfo, distributedPlan,\n\t\t\t\t\t\t\t\t\t\tdefaultTupleDest, isUtilityCommand);\n}\n\n\n/*\n * ExecuteLocalTaskListExtended executes the given tasks locally.\n *\n * The function goes over the task list and executes them locally.\n * The returning tuples (if any) is stored in the tupleStoreState.\n *\n * It uses a cached plan if distributedPlan is found in cache.\n *\n * The function returns totalRowsProcessed.\n */\nuint64\nExecuteLocalTaskListExtended(List *taskList,\n\t\t\t\t\t\t\t ParamListInfo orig_paramListInfo,\n\t\t\t\t\t\t\t DistributedPlan *distributedPlan,\n\t\t\t\t\t\t\t TupleDestination *defaultTupleDest,\n\t\t\t\t\t\t\t bool isUtilityCommand)\n{\n\tParamListInfo paramListInfo = copyParamList(orig_paramListInfo);\n\tuint64 totalRowsProcessed = 0;\n\tint numParams = 0;\n\tOid *parameterTypes = NULL;\n\n\tif (paramListInfo != NULL)\n\t{\n\t\t/* not used anywhere, so declare here */\n\t\tconst char **parameterValues = NULL;\n\n\t\tExtractParametersForLocalExecution(paramListInfo, &parameterTypes,\n\t\t\t\t\t\t\t\t\t\t   &parameterValues);\n\n\t\tnumParams = paramListInfo->numParams;\n\t}\n\n\tif (taskList != NIL)\n\t{\n\t\tbool isRemote = false;\n\t\tEnsureTaskExecutionAllowed(isRemote);\n\t}\n\n\t/*\n\t * If workerJob has a partitionKeyValue, we need to set the colocation id\n\t * and partition key value for each task before we start executing them\n\t * because tenant stats are collected based on these values of a task.\n\t */\n\tif (distributedPlan != NULL && distributedPlan->workerJob != NULL && taskList != NIL)\n\t{\n\t\tSetJobColocationId(distributedPlan->workerJob);\n\t\tSetColocationIdAndPartitionKeyValueForTasks(taskList, distributedPlan->workerJob);\n\t}\n\n\t/*\n\t * Use a new memory context that gets reset after every task to free\n\t * the deparsed query string and query plan.\n\t */\n\tMemoryContext loopContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"ExecuteLocalTaskListExtended\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_SIZES);\n\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(loopContext);\n\n\t\tTupleDestination *tupleDest = task->tupleDest ?\n\t\t\t\t\t\t\t\t\t  task->tupleDest :\n\t\t\t\t\t\t\t\t\t  defaultTupleDest;\n\n\t\t/*\n\t\t * If we have a valid shard id, a distributed table will be accessed\n\t\t * during execution. Record it to apply the restrictions related to\n\t\t * local execution.\n\t\t */\n\t\tif (task->anchorShardId != INVALID_SHARD_ID)\n\t\t{\n\t\t\tSetLocalExecutionStatus(LOCAL_EXECUTION_REQUIRED);\n\t\t}\n\n\t\tif (!ReadOnlyTask(task->taskType))\n\t\t{\n\t\t\t/*\n\t\t\t * Any modification on the local execution should enable 2PC. If remote\n\t\t\t * queries are also ReadOnly, our 2PC logic is smart enough to skip sending\n\t\t\t * PREPARE to those connections.\n\t\t\t */\n\t\t\tUse2PCForCoordinatedTransaction();\n\t\t}\n\n\t\tLogLocalCommand(task);\n\n\t\tif (isUtilityCommand)\n\t\t{\n\t\t\tLocallyExecuteUtilityTask(task);\n\n\t\t\tMemoryContextSwitchTo(oldContext);\n\t\t\tMemoryContextReset(loopContext);\n\t\t\tcontinue;\n\t\t}\n\n\t\tPlannedStmt *localPlan = GetCachedLocalPlan(task, distributedPlan);\n\n\t\t/*\n\t\t * If the plan is already cached, don't need to re-plan, just\n\t\t * acquire necessary locks.\n\t\t */\n\t\tif (localPlan != NULL)\n\t\t{\n\t\t\tQuery *jobQuery = distributedPlan->workerJob->jobQuery;\n\t\t\tLOCKMODE lockMode = GetQueryLockMode(jobQuery);\n\n\t\t\tOid relationId = InvalidOid;\n\t\t\tforeach_declared_oid(relationId, localPlan->relationOids)\n\t\t\t{\n\t\t\t\tLockRelationOid(relationId, lockMode);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint taskNumParams = numParams;\n\t\t\tOid *taskParameterTypes = parameterTypes;\n\t\t\tint taskType = GetTaskQueryType(task);\n\n\t\t\tif (task->parametersInQueryStringResolved)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Parameters were removed from the query string so do not pass them\n\t\t\t\t * here. Otherwise, we might see errors when passing custom types,\n\t\t\t\t * since their OIDs were set to 0 and their type is normally\n\t\t\t\t * inferred from\n\t\t\t\t */\n\t\t\t\ttaskNumParams = 0;\n\t\t\t\ttaskParameterTypes = NULL;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * for concatenated strings, we set queryStringList so that we can access\n\t\t\t * each query string.\n\t\t\t */\n\t\t\tif (taskType == TASK_QUERY_TEXT_LIST)\n\t\t\t{\n\t\t\t\tList *queryStringList = task->taskQuery.data.queryStringList;\n\t\t\t\ttotalRowsProcessed +=\n\t\t\t\t\tLocallyPlanAndExecuteMultipleQueries(queryStringList, tupleDest,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t task);\n\n\t\t\t\tMemoryContextSwitchTo(oldContext);\n\t\t\t\tMemoryContextReset(loopContext);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (taskType != TASK_QUERY_LOCAL_PLAN)\n\t\t\t{\n\t\t\t\tQuery *shardQuery = ParseQueryString(TaskQueryString(task),\n\t\t\t\t\t\t\t\t\t\t\t\t\t taskParameterTypes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t taskNumParams);\n\n\t\t\t\tint cursorOptions = CURSOR_OPT_PARALLEL_OK;\n\n\t\t\t\t/*\n\t\t\t\t * Altough the shardQuery is local to this node, we prefer planner()\n\t\t\t\t * over standard_planner(). The primary reason for that is Citus itself\n\t\t\t\t * is not very tolarent standard_planner() calls that doesn't go through\n\t\t\t\t * distributed_planner() because of the way that restriction hooks are\n\t\t\t\t * implemented. So, let planner to call distributed_planner() which\n\t\t\t\t * eventually calls standard_planner().\n\t\t\t\t */\n\t\t\t\tlocalPlan = planner(shardQuery, NULL, cursorOptions, paramListInfo);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tereport(DEBUG2, (errmsg(\n\t\t\t\t\t\t\t\t\t \"Local executor: Using task's cached local plan for task %u\",\n\t\t\t\t\t\t\t\t\t task->taskId)));\n\t\t\t\tlocalPlan = TaskQueryLocalPlan(task);\n\t\t\t}\n\t\t}\n\n\t\tchar *shardQueryString = NULL;\n\t\tif (GetTaskQueryType(task) == TASK_QUERY_TEXT)\n\t\t{\n\t\t\tshardQueryString = TaskQueryString(task);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* avoid the overhead of deparsing when using local execution */\n\t\t\tshardQueryString = \"<optimized out by local execution>\";\n\t\t}\n\n\t\ttotalRowsProcessed +=\n\t\t\tLocallyExecuteTaskPlan(localPlan, shardQueryString,\n\t\t\t\t\t\t\t\t   tupleDest, task, paramListInfo);\n\n\t\tMemoryContextSwitchTo(oldContext);\n\t\tMemoryContextReset(loopContext);\n\t}\n\n\treturn totalRowsProcessed;\n}\n\n\n/*\n * SetColocationIdAndPartitionKeyValueForTasks sets colocationId and partitionKeyValue\n * for the tasks in the taskList.\n */\nstatic void\nSetColocationIdAndPartitionKeyValueForTasks(List *taskList, Job *workerJob)\n{\n\tif (workerJob->colocationId != INVALID_COLOCATION_ID)\n\t{\n\t\tTask *task = NULL;\n\t\tforeach_declared_ptr(task, taskList)\n\t\t{\n\t\t\ttask->colocationId = workerJob->colocationId;\n\t\t\ttask->partitionKeyValue = workerJob->partitionKeyValue;\n\t\t}\n\t}\n}\n\n\n/*\n * LocallyPlanAndExecuteMultipleQueries plans and executes the given query strings\n * one by one.\n */\nstatic uint64\nLocallyPlanAndExecuteMultipleQueries(List *queryStrings, TupleDestination *tupleDest,\n\t\t\t\t\t\t\t\t\t Task *task)\n{\n\tchar *queryString = NULL;\n\tuint64 totalProcessedRows = 0;\n\tforeach_declared_ptr(queryString, queryStrings)\n\t{\n\t\tQuery *shardQuery = ParseQueryString(queryString,\n\t\t\t\t\t\t\t\t\t\t\t NULL,\n\t\t\t\t\t\t\t\t\t\t\t 0);\n\t\tint cursorOptions = 0;\n\t\tParamListInfo paramListInfo = NULL;\n\t\tPlannedStmt *localPlan = planner(shardQuery, NULL, cursorOptions,\n\t\t\t\t\t\t\t\t\t\t paramListInfo);\n\t\ttotalProcessedRows += LocallyExecuteTaskPlan(localPlan, queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t tupleDest, task,\n\t\t\t\t\t\t\t\t\t\t\t\t\t paramListInfo);\n\t}\n\treturn totalProcessedRows;\n}\n\n\n/*\n * ExtractParametersForLocalExecution extracts parameter types and values\n * from the given ParamListInfo structure, and fills parameter type and\n * value arrays. It does not change the oid of custom types, because the\n * query will be run locally.\n */\nvoid\nExtractParametersForLocalExecution(ParamListInfo paramListInfo, Oid **parameterTypes,\n\t\t\t\t\t\t\t\t   const char ***parameterValues)\n{\n\tExtractParametersFromParamList(paramListInfo, parameterTypes,\n\t\t\t\t\t\t\t\t   parameterValues, true);\n}\n\n\n/*\n * LocallyExecuteUtilityTask runs a utility command via local execution.\n */\nstatic void\nLocallyExecuteUtilityTask(Task *task)\n{\n\t/* keep the parity with multi-node clusters */\n\tRecordNonDistTableAccessesForTask(task);\n\n\t/*\n\t * If we roll back to a savepoint, we may no longer be in a query on\n\t * a shard. Reset the value as we go back up the stack.\n\t */\n\tuint64 prevLocalExecutorShardId = LocalExecutorShardId;\n\n\tif (task->anchorShardId != INVALID_SHARD_ID)\n\t{\n\t\tLocalExecutorShardId = task->anchorShardId;\n\t}\n\n\tPG_TRY();\n\t{\n\t\tExecuteUtilityCommand(TaskQueryString(task));\n\t}\n\tPG_CATCH();\n\t{\n\t\tLocalExecutorShardId = prevLocalExecutorShardId;\n\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n\n\tLocalExecutorShardId = prevLocalExecutorShardId;\n}\n\n\n/*\n * ExecuteUtilityCommand executes the given task query in the current\n * session.\n */\nvoid\nExecuteUtilityCommand(const char *taskQueryCommand)\n{\n\tList *parseTreeList = pg_parse_query(taskQueryCommand);\n\tRawStmt *taskRawStmt = NULL;\n\n\tforeach_declared_ptr(taskRawStmt, parseTreeList)\n\t{\n\t\tNode *taskRawParseTree = taskRawStmt->stmt;\n\n\t\t/*\n\t\t * The query passed to this function would mostly be a utility\n\t\t * command. However, some utility commands trigger udf calls\n\t\t * (e.g alter_columnar_table_set()). In that case, we execute\n\t\t * the query with the udf call in below conditional block.\n\t\t */\n\t\tif (IsA(taskRawParseTree, SelectStmt))\n\t\t{\n\t\t\t/* we have no external parameters to rewrite the UDF call RawStmt */\n\t\t\tQuery *udfTaskQuery =\n\t\t\t\tRewriteRawQueryStmt(taskRawStmt, taskQueryCommand, NULL, 0);\n\n\t\t\tExecuteUdfTaskQuery(udfTaskQuery);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * It is a regular utility command we should execute it via\n\t\t\t * process utility.\n\t\t\t */\n\t\t\tProcessUtilityParseTree(taskRawParseTree, taskQueryCommand,\n\t\t\t\t\t\t\t\t\tPROCESS_UTILITY_QUERY, NULL, None_Receiver,\n\t\t\t\t\t\t\t\t\tNULL);\n\t\t}\n\t}\n}\n\n\n/*\n * ExecuteUdfTaskQuery executes the given udf command. A udf command\n * is simply a \"SELECT udf_call()\" query and so it cannot be executed\n * via process utility.\n */\nstatic void\nExecuteUdfTaskQuery(Query *udfTaskQuery)\n{\n\t/* we do not expect any results */\n\tExecuteQueryIntoDestReceiver(udfTaskQuery, NULL, None_Receiver);\n}\n\n\n/*\n * LogLocalCommand logs commands executed locally on this node. Although we're\n * talking about local execution, the function relies on citus.log_remote_commands\n * GUC. This makes sense because the local execution is still on a shard of a\n * distributed table, meaning it is part of distributed execution.\n */\nstatic void\nLogLocalCommand(Task *task)\n{\n\tif (!(LogRemoteCommands || LogLocalCommands))\n\t{\n\t\treturn;\n\t}\n\n\tconst char *command = TaskQueryString(task);\n\tif (!CommandMatchesLogGrepPattern(command))\n\t{\n\t\treturn;\n\t}\n\n\tereport(NOTICE, (errmsg(\"executing the command locally: %s\",\n\t\t\t\t\t\t\tcommand)));\n}\n\n\n/*\n * ExtractLocalAndRemoteTasks gets a taskList and generates two\n * task lists namely localTaskList and remoteTaskList. The function goes\n * over the input taskList and puts the tasks that are local to the node\n * into localTaskList and the remaining to the remoteTaskList. Either of\n * the lists could be NIL depending on the input taskList.\n *\n * One slightly different case is modifications to replicated tables\n * (e.g., reference tables) where a single task ends in two separate tasks\n * and the local task is added to localTaskList and the remaining ones to\n * the remoteTaskList.\n */\nvoid\nExtractLocalAndRemoteTasks(bool readOnly, List *taskList, List **localTaskList,\n\t\t\t\t\t\t   List **remoteTaskList)\n{\n\t*remoteTaskList = NIL;\n\t*localTaskList = NIL;\n\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tList *localTaskPlacementList = NULL;\n\t\tList *remoteTaskPlacementList = NULL;\n\n\t\tSplitLocalAndRemotePlacements(\n\t\t\ttask->taskPlacementList, &localTaskPlacementList, &remoteTaskPlacementList);\n\n\t\t/* either the local or the remote should be non-nil */\n\t\tAssert(!(localTaskPlacementList == NIL && remoteTaskPlacementList == NIL));\n\n\t\tif (localTaskPlacementList == NIL)\n\t\t{\n\t\t\t*remoteTaskList = lappend(*remoteTaskList, task);\n\t\t}\n\t\telse if (remoteTaskPlacementList == NIL)\n\t\t{\n\t\t\t*localTaskList = lappend(*localTaskList, task);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * At this point, we're dealing with a task that has placements on both\n\t\t\t * local and remote nodes.\n\t\t\t */\n\t\t\tTask *localTask = copyObject(task);\n\t\t\tlocalTask->partiallyLocalOrRemote = true;\n\n\t\t\tlocalTask->taskPlacementList = localTaskPlacementList;\n\t\t\t*localTaskList = lappend(*localTaskList, localTask);\n\n\t\t\tif (readOnly)\n\t\t\t{\n\t\t\t\t/* read-only tasks should only be executed on the local machine */\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* since shard replication factor > 1, we should have at least 1 remote task */\n\t\t\t\tAssert(remoteTaskPlacementList != NIL);\n\t\t\t\tTask *remoteTask = copyObject(task);\n\t\t\t\tremoteTask->partiallyLocalOrRemote = true;\n\t\t\t\tremoteTask->taskPlacementList = remoteTaskPlacementList;\n\n\t\t\t\t*remoteTaskList = lappend(*remoteTaskList, remoteTask);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * SplitLocalAndRemotePlacements is a helper function which iterates over the\n * input taskPlacementList and puts the placements into corresponding list of\n * either localTaskPlacementList or remoteTaskPlacementList.\n */\nstatic void\nSplitLocalAndRemotePlacements(List *taskPlacementList, List **localTaskPlacementList,\n\t\t\t\t\t\t\t  List **remoteTaskPlacementList)\n{\n\tint32 localGroupId = GetLocalGroupId();\n\n\t*localTaskPlacementList = NIL;\n\t*remoteTaskPlacementList = NIL;\n\n\tShardPlacement *taskPlacement = NULL;\n\tforeach_declared_ptr(taskPlacement, taskPlacementList)\n\t{\n\t\tif (taskPlacement->groupId == localGroupId)\n\t\t{\n\t\t\t*localTaskPlacementList = lappend(*localTaskPlacementList, taskPlacement);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t*remoteTaskPlacementList = lappend(*remoteTaskPlacementList, taskPlacement);\n\t\t}\n\t}\n}\n\n\n/*\n * ExecuteLocalTaskPlan gets a planned statement which can be executed locally.\n * The function simply follows the steps to have a local execution, sets the\n * tupleStore if necessary. The function returns the number of rows affected in\n * case of DML.\n */\nstatic uint64\nLocallyExecuteTaskPlan(PlannedStmt *taskPlan, char *queryString,\n\t\t\t\t\t   TupleDestination *tupleDest, Task *task,\n\t\t\t\t\t   ParamListInfo paramListInfo)\n{\n\tvolatile uint64 processedRows = 0;\n\n\t/*\n\t * If we roll back to a savepoint, we may no longer be in a query on\n\t * a shard. Reset the value as we go back up the stack.\n\t */\n\tuint64 prevLocalExecutorShardId = LocalExecutorShardId;\n\n\tif (task->anchorShardId != INVALID_SHARD_ID)\n\t{\n\t\tLocalExecutorShardId = task->anchorShardId;\n\t}\n\n\n\tchar *partitionKeyValueString = NULL;\n\tif (task->partitionKeyValue != NULL)\n\t{\n\t\tpartitionKeyValueString = DatumToString(task->partitionKeyValue->constvalue,\n\t\t\t\t\t\t\t\t\t\t\t\ttask->partitionKeyValue->consttype);\n\t}\n\n\tAttributeTask(partitionKeyValueString, task->colocationId, taskPlan->commandType);\n\n\tPG_TRY();\n\t{\n\t\tprocessedRows = ExecuteTaskPlan(taskPlan, queryString, tupleDest, task,\n\t\t\t\t\t\t\t\t\t\tparamListInfo);\n\t}\n\tPG_CATCH();\n\t{\n\t\tLocalExecutorShardId = prevLocalExecutorShardId;\n\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n\n\tLocalExecutorShardId = prevLocalExecutorShardId;\n\n\treturn processedRows;\n}\n\n\n/*\n * ExecuteTaskPlan executes the given planned statement and writes the results\n * to tupleDest.\n */\nstatic uint64\nExecuteTaskPlan(PlannedStmt *taskPlan, char *queryString,\n\t\t\t\tTupleDestination *tupleDest, Task *task,\n\t\t\t\tParamListInfo paramListInfo)\n{\n\tScanDirection scanDirection = ForwardScanDirection;\n\tQueryEnvironment *queryEnv = create_queryEnv();\n\tint eflags = 0;\n\tuint64 totalRowsProcessed = 0;\n\n\tRecordNonDistTableAccessesForTask(task);\n\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"ExecuteTaskPlan\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\t/*\n\t * Some tuple destinations look at task->taskPlacementList to determine\n\t * where the result came from using the placement index. Since a local\n\t * task can only ever have 1 placement, we set the index to 0.\n\t */\n\tint localPlacementIndex = 0;\n\n\t/*\n\t * Use the tupleStore provided by the scanState because it is shared across\n\t * the other task executions and the adaptive executor.\n\t *\n\t * Also note that as long as the tupleDest is provided, local execution always\n\t * stores the tuples. This is also valid for partiallyLocalOrRemote tasks\n\t * as well.\n\t */\n\tDestReceiver *destReceiver = tupleDest ?\n\t\t\t\t\t\t\t\t CreateTupleDestDestReceiver(tupleDest, task,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t localPlacementIndex) :\n\t\t\t\t\t\t\t\t CreateDestReceiver(DestNone);\n\n\tQueryDesc *queryDesc = CreateQueryDesc(\n\t\ttaskPlan,          /* PlannedStmt *plannedstmt */\n\t\tqueryString,       /* const char *sourceText */\n\t\tGetActiveSnapshot(),   /* Snapshot snapshot */\n\t\tInvalidSnapshot,       /* Snapshot crosscheck_snapshot */\n\t\tdestReceiver,      /* DestReceiver *dest */\n\t\tparamListInfo,     /* ParamListInfo params */\n\t\tqueryEnv,          /* QueryEnvironment *queryEnv */\n\t\t0                  /* int instrument_options */\n\t\t);\n\n\tExecutorStart(queryDesc, eflags);\n\n/* run the plan: count = 0 (all rows) */\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t/* PG 18+ dropped the “execute_once” boolean */\n\tExecutorRun(queryDesc, scanDirection, 0L);\n#else\n\n\t/* PG 17 and prevs still expect the 4th ‘once’ argument */\n\tExecutorRun(queryDesc, scanDirection, 0L, true);\n#endif\n\n\t/*\n\t * We'll set the executorState->es_processed later, for now only remember\n\t * the count.\n\t */\n\tif (taskPlan->commandType != CMD_SELECT)\n\t{\n\t\ttotalRowsProcessed = queryDesc->estate->es_processed;\n\t}\n\n\tExecutorFinish(queryDesc);\n\tExecutorEnd(queryDesc);\n\n\tFreeQueryDesc(queryDesc);\n\n\tMemoryContextSwitchTo(oldContext);\n\tMemoryContextDelete(localContext);\n\n\treturn totalRowsProcessed;\n}\n\n\n/*\n * RecordNonDistTableAccessesForTask records relation accesses for the non-distributed\n * relations that given task will access (if any).\n */\nstatic void\nRecordNonDistTableAccessesForTask(Task *task)\n{\n\tList *taskPlacementList = task->taskPlacementList;\n\tif (list_length(taskPlacementList) == 0)\n\t{\n\t\t/*\n\t\t * We should never get here, but prefer to throw an error over crashing\n\t\t * if we're wrong.\n\t\t */\n\t\tereport(ERROR, (errmsg(\"shard \" UINT64_FORMAT \" does not have any shard \"\n\t\t\t\t\t\t\t   \"placements\",\n\t\t\t\t\t\t\t   task->anchorShardId)));\n\t}\n\n\t/*\n\t * We use only the first placement to find the relation accesses. It is\n\t * sufficient as PlacementAccessListForTask iterates relationShardList\n\t * field of the task and generates accesses per relation in the task.\n\t * As we are only interested in relations, not the placements, we can\n\t * skip rest of the placements.\n\t * Also, here we don't need to iterate relationShardList field of task\n\t * to mark each accessed relation because PlacementAccessListForTask\n\t * already computes and returns relations that task accesses.\n\t */\n\tShardPlacement *taskPlacement = linitial(taskPlacementList);\n\tList *placementAccessList = PlacementAccessListForTask(task, taskPlacement);\n\n\tShardPlacementAccess *placementAccess = NULL;\n\tforeach_declared_ptr(placementAccess, placementAccessList)\n\t{\n\t\tuint64 placementAccessShardId = placementAccess->placement->shardId;\n\t\tif (placementAccessShardId == INVALID_SHARD_ID)\n\t\t{\n\t\t\t/*\n\t\t\t * When a SELECT prunes down to 0 shard, we still may pass through\n\t\t\t * the local executor. In that case, we don't need to record any\n\t\t\t * relation access as we don't actually access any shard placement.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tOid accessedRelationId = RelationIdForShard(placementAccessShardId);\n\t\tShardPlacementAccessType shardPlacementAccessType = placementAccess->accessType;\n\t\tRecordRelationAccessIfNonDistTable(accessedRelationId, shardPlacementAccessType);\n\t}\n}\n\n\n/*\n * SetLocalExecutionStatus sets the local execution status to\n * the given status, it errors if the transition is not possible from the\n * current status.\n */\nvoid\nSetLocalExecutionStatus(LocalExecutionStatus newStatus)\n{\n\tEnsureTransitionPossible(GetCurrentLocalExecutionStatus(), newStatus);\n\n\tCurrentLocalExecutionStatus = newStatus;\n}\n\n\n/*\n * EnsureTransitionPossible errors if we cannot switch to the 'to' status\n * from the 'from' status.\n */\nstatic void\nEnsureTransitionPossible(LocalExecutionStatus from, LocalExecutionStatus\n\t\t\t\t\t\t to)\n{\n\tif (from == LOCAL_EXECUTION_REQUIRED && to == LOCAL_EXECUTION_DISABLED)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\n\t\t\t\t\t \"cannot switch local execution status from local execution required \"\n\t\t\t\t\t \"to local execution disabled since it can cause \"\n\t\t\t\t\t \"visibility problems in the current transaction\")));\n\t}\n\tif (from == LOCAL_EXECUTION_DISABLED && to == LOCAL_EXECUTION_REQUIRED)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\n\t\t\t\t\t \"cannot switch local execution status from local execution disabled \"\n\t\t\t\t\t \"to local execution enabled since it can cause \"\n\t\t\t\t\t \"visibility problems in the current transaction\")));\n\t}\n}\n\n\n/*\n *  ShouldExecuteTasksLocally gets a task list and returns true if the\n *  any of the tasks should be executed locally. This function does not\n *  guarantee that any task have to be executed locally.\n */\nbool\nShouldExecuteTasksLocally(List *taskList)\n{\n\tif (!EnableLocalExecution)\n\t{\n\t\treturn false;\n\t}\n\n\tif (GetCurrentLocalExecutionStatus() == LOCAL_EXECUTION_DISABLED)\n\t{\n\t\t/*\n\t\t * if the current transaction accessed the local node over a connection\n\t\t * then we can't use local execution because of visibility problems.\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (GetCurrentLocalExecutionStatus() == LOCAL_EXECUTION_REQUIRED)\n\t{\n\t\t/*\n\t\t * If we already used local execution for a previous command\n\t\t * we should stick to it for read-your-writes policy, this can be a\n\t\t * case when we are inside a transaction block. Such as:\n\t\t *\n\t\t * BEGIN;\n\t\t * some-command; -- executed via local execution\n\t\t * another-command; -- this should be executed via local execution for visibility\n\t\t * COMMIT;\n\t\t *\n\t\t * We may need to use local execution even if we are not inside a transaction block,\n\t\t * however the state will go back to LOCAL_EXECUTION_OPTIONAL at the end of transaction.\n\t\t */\n\t\treturn true;\n\t}\n\n\tbool singleTask = (list_length(taskList) == 1);\n\n\tif (singleTask && TaskAccessesLocalNode((Task *) linitial(taskList)))\n\t{\n\t\t/*\n\t\t * This is the valuable time to use the local execution. We are likely\n\t\t * to avoid any network round-trips by simply executing the command\n\t\t * within this session.\n\t\t *\n\t\t * We cannot avoid network round trips if the task is not a read only\n\t\t * task and accesses multiple placements. For example, modifications to\n\t\t * distributed tables (with replication factor == 1) would avoid network\n\t\t * round-trips. However, modifications to reference tables still needs\n\t\t * to go to over the network to do the modification on the other placements.\n\t\t * Still, we'll be avoding the network round trip for this node.\n\t\t *\n\t\t * Note that we shouldn't use local execution if any distributed execution\n\t\t * has happened because that'd break transaction visibility rules and\n\t\t * many other things.\n\t\t */\n\t\treturn true;\n\t}\n\n\tif (!singleTask)\n\t{\n\t\t/*\n\t\t * For multi-task executions, we prefer to use connections for parallelism,\n\t\t * except for two cases. First, when in a multi-statement transaction since\n\t\t * there could be other commands that require local execution. Second, the\n\t\t * task list already requires sequential execution. In that case, connection\n\t\t * establishment becomes an unnecessary operation.\n\t\t */\n\n\t\treturn (IsMultiStatementTransaction() || ShouldRunTasksSequentially(taskList)) &&\n\t\t\t   AnyTaskAccessesLocalNode(taskList);\n\t}\n\n\treturn false;\n}\n\n\n/*\n * AnyTaskAccessesLocalNode returns true if a task within the task list accesses\n * to the local node.\n */\nbool\nAnyTaskAccessesLocalNode(List *taskList)\n{\n\tTask *task = NULL;\n\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tif (TaskAccessesLocalNode(task))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * TaskAccessesLocalNode returns true if any placements of the task reside on\n * the node that we're executing the query.\n */\nbool\nTaskAccessesLocalNode(Task *task)\n{\n\tint32 localGroupId = GetLocalGroupId();\n\n\tShardPlacement *taskPlacement = NULL;\n\tforeach_declared_ptr(taskPlacement, task->taskPlacementList)\n\t{\n\t\tif (taskPlacement->groupId == localGroupId)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * EnsureCompatibleLocalExecutionState makes sure that the tasks won't have\n * any visibility problems because of local execution.\n */\nvoid\nEnsureCompatibleLocalExecutionState(List *taskList)\n{\n\t/*\n\t * We have LOCAL_EXECUTION_REQUIRED check here to avoid unnecessarily\n\t * iterating the task list in AnyTaskAccessesLocalNode.\n\t */\n\tif (GetCurrentLocalExecutionStatus() == LOCAL_EXECUTION_REQUIRED &&\n\t\tAnyTaskAccessesLocalNode(taskList))\n\t{\n\t\tErrorIfTransactionAccessedPlacementsLocally();\n\t}\n}\n\n\n/*\n * ErrorIfTransactionAccessedPlacementsLocally errors out if a local query\n * on any shard has already been executed in the same transaction.\n *\n * This check is required because Citus currently hasn't implemented local\n * execution infrastructure for all the commands/executors. As we implement\n * local execution for the command/executor that this function call exists,\n * we should simply  remove the check.\n */\nvoid\nErrorIfTransactionAccessedPlacementsLocally(void)\n{\n\tif (GetCurrentLocalExecutionStatus() == LOCAL_EXECUTION_REQUIRED)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"cannot execute command because a local execution has \"\n\t\t\t\t\t\t\"accessed a placement in the transaction\"),\n\t\t\t\t errhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t \"\\\"SET LOCAL citus.enable_local_execution TO OFF;\\\"\"),\n\t\t\t\t errdetail(\"Some parallel commands cannot be executed if a \"\n\t\t\t\t\t\t   \"previous command has already been executed locally\")));\n\t}\n}\n\n\n/*\n * DisableLocalExecution is simply a C interface for setting the following:\n *      SET LOCAL citus.enable_local_execution TO off;\n */\nvoid\nDisableLocalExecution(void)\n{\n\tset_config_option(\"citus.enable_local_execution\", \"off\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/merge_executor.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * merge_executor.c\n *\n * Executor logic for MERGE SQL statement.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"nodes/execnodes.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n\n#include \"distributed/distributed_execution_locks.h\"\n#include \"distributed/insert_select_executor.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/merge_executor.h\"\n#include \"distributed/merge_planner.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_explain.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/repartition_executor.h\"\n#include \"distributed/stats/stat_counters.h\"\n#include \"distributed/subplan_execution.h\"\n\nstatic void ExecuteSourceAtWorkerAndRepartition(CitusScanState *scanState);\nstatic void ExecuteSourceAtCoordAndRedistribution(CitusScanState *scanState);\nstatic HTAB * ExecuteMergeSourcePlanIntoColocatedIntermediateResults(Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Query *mergeQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t sourceTargetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t PlannedStmt *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t sourcePlan,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t EState *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t executorState,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t char *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t intermediateResultIdPrefix,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t int\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t partitionColumnIndex)\n;\n\n\n/*\n * NonPushableMergeCommandExecScan performs an MERGE INTO distributed_table\n * USING (source-query) ... command. This can be done either by aggregating\n * task results at the coordinator and repartitioning the results, or by\n * repartitioning task results and directly transferring data between nodes.\n */\nTupleTableSlot *\nNonPushableMergeCommandExecScan(CustomScanState *node)\n{\n\tCitusScanState *scanState = (CitusScanState *) node;\n\tDistributedPlan *distributedPlan = scanState->distributedPlan;\n\n\tif (!scanState->finishedRemoteScan)\n\t{\n\t\tswitch (distributedPlan->modifyWithSelectMethod)\n\t\t{\n\t\t\tcase MODIFY_WITH_SELECT_REPARTITION:\n\t\t\t{\n\t\t\t\tExecuteSourceAtWorkerAndRepartition(scanState);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MODIFY_WITH_SELECT_VIA_COORDINATOR:\n\t\t\t{\n\t\t\t\tExecuteSourceAtCoordAndRedistribution(scanState);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"Unexpected MERGE execution method(%d)\",\n\t\t\t\t\t\t\t\t\t   distributedPlan->modifyWithSelectMethod)));\n\t\t\t}\n\t\t}\n\n\t\tscanState->finishedRemoteScan = true;\n\t}\n\n\tTupleTableSlot *resultSlot = ReturnTupleFromTuplestore(scanState);\n\n\treturn resultSlot;\n}\n\n\n/*\n * ExecuteSourceAtWorkerAndRepartition Executes the Citus distributed plan, including any\n * sub-plans, and captures the results in intermediate files. Subsequently, redistributes\n * the result files to ensure colocation with the target, and directs the MERGE SQL\n * operation to the target shards on the worker nodes, utilizing the colocated\n * intermediate files as the data source.\n */\nstatic void\nExecuteSourceAtWorkerAndRepartition(CitusScanState *scanState)\n{\n\tDistributedPlan *distributedPlan = scanState->distributedPlan;\n\tQuery *mergeQuery =\n\t\tcopyObject(distributedPlan->modifyQueryViaCoordinatorOrRepartition);\n\tRangeTblEntry *targetRte = ExtractResultRelationRTE(mergeQuery);\n\tRangeTblEntry *sourceRte = ExtractMergeSourceRangeTableEntry(mergeQuery, false);\n\tOid targetRelationId = targetRte->relid;\n\tbool hasReturning = distributedPlan->expectResults;\n\tQuery *sourceQuery = sourceRte->subquery;\n\tPlannedStmt *sourcePlan =\n\t\tcopyObject(distributedPlan->selectPlanForModifyViaCoordinatorOrRepartition);\n\tEState *executorState = ScanStateGetExecutorState(scanState);\n\n\t/*\n\t * If we are dealing with partitioned table, we also need to lock its\n\t * partitions. Here we only lock targetRelation, we acquire necessary\n\t * locks on source tables during execution of those source queries.\n\t */\n\tif (PartitionedTable(targetRelationId))\n\t{\n\t\tLockPartitionRelations(targetRelationId, RowExclusiveLock);\n\t}\n\n\tbool randomAccess = true;\n\tbool interTransactions = false;\n\tDistributedPlan *distSourcePlan =\n\t\tGetDistributedPlan((CustomScan *) sourcePlan->planTree);\n\tJob *distSourceJob = distSourcePlan->workerJob;\n\tList *distSourceTaskList = distSourceJob->taskList;\n\tbool binaryFormat =\n\t\tCanUseBinaryCopyFormatForTargetList(sourceQuery->targetList);\n\n\tereport(DEBUG1, (errmsg(\"Executing subplans of the source query and \"\n\t\t\t\t\t\t\t\"storing the results at the respective node(s)\")));\n\n\tExecuteSubPlans(distSourcePlan, RequestedForExplainAnalyze(scanState));\n\n\t/*\n\t * We have a separate directory for each transaction, so choosing\n\t * the same result prefix won't cause filename conflicts. Results\n\t * directory name also includes node id and database id, so we don't\n\t * need to include them in the filename. We include job id here for\n\t * the case \"MERGE USING <source query>\" is executed recursively.\n\t */\n\tStringInfo distResultPrefixString = makeStringInfo();\n\tappendStringInfo(distResultPrefixString,\n\t\t\t\t\t \"repartitioned_results_\" UINT64_FORMAT,\n\t\t\t\t\t distSourceJob->jobId);\n\tchar *distResultPrefix = distResultPrefixString->data;\n\tCitusTableCacheEntry *targetRelation = GetCitusTableCacheEntry(targetRelationId);\n\n\tereport(DEBUG1, (errmsg(\"Redistributing source result rows across nodes\")));\n\n\t/*\n\t * partitionColumnIndex determines the column in the selectTaskList to\n\t * use for (re)partitioning of the source result, which will colocate\n\t * the result data with the target.\n\t */\n\tint partitionColumnIndex = distributedPlan->sourceResultRepartitionColumnIndex;\n\n\t/*\n\t * Below call partitions the results using shard ranges and partition method of\n\t * targetRelation, and then colocates the result files with shards. These\n\t * transfers are done by calls to fetch_intermediate_results() between nodes.\n\t */\n\tList **redistributedResults =\n\t\tRedistributeTaskListResults(distResultPrefix,\n\t\t\t\t\t\t\t\t\tdistSourceTaskList, partitionColumnIndex,\n\t\t\t\t\t\t\t\t\ttargetRelation, binaryFormat);\n\n\tif (list_length(distSourceTaskList) <= 1)\n\t{\n\t\t/*\n\t\t * Probably we will never get here for a repartitioned MERGE\n\t\t * because when the source is a single shard table, we should\n\t\t * most probably choose to use ExecuteSourceAtCoordAndRedistribution(),\n\t\t * but we still keep this here.\n\t\t */\n\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_SINGLE_SHARD);\n\t}\n\telse\n\t{\n\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_MULTI_SHARD);\n\t}\n\n\tereport(DEBUG1, (errmsg(\"Executing final MERGE on workers using \"\n\t\t\t\t\t\t\t\"intermediate results\")));\n\n\t/*\n\t * At this point source query has been executed on workers and results\n\t * have been fetched in such a way that they are colocated with corresponding\n\t * target shard(s). Create and execute a list of tasks of form\n\t * MERGE  INTO ... USING SELECT * FROM read_intermediate_results(...);\n\t */\n\tList *taskList =\n\t\tGenerateTaskListWithRedistributedResults(mergeQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t targetRelation,\n\t\t\t\t\t\t\t\t\t\t\t\t redistributedResults,\n\t\t\t\t\t\t\t\t\t\t\t\t binaryFormat);\n\n\tscanState->tuplestorestate =\n\t\ttuplestore_begin_heap(randomAccess, interTransactions, work_mem);\n\tParamListInfo paramListInfo = executorState->es_param_list_info;\n\tTupleDesc tupleDescriptor = ScanStateGetTupleDescriptor(scanState);\n\tTupleDestination *tupleDest =\n\t\tCreateTupleStoreTupleDest(scanState->tuplestorestate,\n\t\t\t\t\t\t\t\t  tupleDescriptor);\n\tuint64 rowsMerged =\n\t\tExecuteTaskListIntoTupleDestWithParam(ROW_MODIFY_NONCOMMUTATIVE, taskList,\n\t\t\t\t\t\t\t\t\t\t\t  tupleDest,\n\t\t\t\t\t\t\t\t\t\t\t  hasReturning,\n\t\t\t\t\t\t\t\t\t\t\t  paramListInfo);\n\n\tif (list_length(taskList) <= 1)\n\t{\n\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_SINGLE_SHARD);\n\t}\n\telse\n\t{\n\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_MULTI_SHARD);\n\t}\n\n\texecutorState->es_processed = rowsMerged;\n}\n\n\n/*\n * ExecuteSourceAtCoordAndRedistribution Executes the plan that necessitates evaluation\n * at the coordinator and redistributes the resulting rows to intermediate files,\n * ensuring colocation with the target shards. Directs the MERGE SQL operation to the\n * target shards on the worker nodes, utilizing the colocated intermediate files as the\n * data source.\n */\nvoid\nExecuteSourceAtCoordAndRedistribution(CitusScanState *scanState)\n{\n\tEState *executorState = ScanStateGetExecutorState(scanState);\n\tDistributedPlan *distributedPlan = scanState->distributedPlan;\n\tQuery *mergeQuery =\n\t\tcopyObject(distributedPlan->modifyQueryViaCoordinatorOrRepartition);\n\tRangeTblEntry *targetRte = ExtractResultRelationRTE(mergeQuery);\n\tRangeTblEntry *sourceRte = ExtractMergeSourceRangeTableEntry(mergeQuery, false);\n\tQuery *sourceQuery = sourceRte->subquery;\n\tOid targetRelationId = targetRte->relid;\n\tPlannedStmt *sourcePlan =\n\t\tcopyObject(distributedPlan->selectPlanForModifyViaCoordinatorOrRepartition);\n\tchar *intermediateResultIdPrefix = distributedPlan->intermediateResultIdPrefix;\n\tbool hasReturning = distributedPlan->expectResults;\n\tbool hasNotMatchedBySource = HasMergeNotMatchedBySource(mergeQuery);\n\tint partitionColumnIndex = distributedPlan->sourceResultRepartitionColumnIndex;\n\n\t/*\n\t * If we are dealing with partitioned table, we also need to lock its\n\t * partitions. Here we only lock targetRelation, we acquire necessary\n\t * locks on source tables during execution of those source queries.\n\t */\n\tif (PartitionedTable(targetRelationId))\n\t{\n\t\tLockPartitionRelations(targetRelationId, RowExclusiveLock);\n\t}\n\n\tereport(DEBUG1, (errmsg(\"Collect source query results on coordinator\")));\n\n\tList *prunedTaskList = NIL, *emptySourceTaskList = NIL;\n\tHTAB *shardStateHash =\n\t\tExecuteMergeSourcePlanIntoColocatedIntermediateResults(\n\t\t\ttargetRelationId,\n\t\t\tmergeQuery,\n\t\t\tsourceQuery->targetList,\n\t\t\tsourcePlan,\n\t\t\texecutorState,\n\t\t\tintermediateResultIdPrefix,\n\t\t\tpartitionColumnIndex);\n\n\tereport(DEBUG1, (errmsg(\"Create a MERGE task list that needs to be routed\")));\n\n\t/* generate tasks for the .. phase */\n\tList *taskList =\n\t\tGenerateTaskListWithColocatedIntermediateResults(targetRelationId, mergeQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t intermediateResultIdPrefix);\n\n\t/*\n\t * We cannot actually execute MERGE INTO ... tasks that read from\n\t * intermediate results that weren't created because no rows were\n\t * written to them. Prune those tasks out by only including tasks\n\t * on shards with connections; however, if the MERGE INTO includes\n\t * a NOT MATCHED BY SOURCE clause we need to include the task.\n\t */\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tuint64 shardId = task->anchorShardId;\n\t\tbool shardModified = false;\n\n\t\thash_search(shardStateHash, &shardId, HASH_FIND, &shardModified);\n\t\tif (shardModified)\n\t\t{\n\t\t\tprunedTaskList = lappend(prunedTaskList, task);\n\t\t}\n\t\telse if (hasNotMatchedBySource)\n\t\t{\n\t\t\temptySourceTaskList = lappend(emptySourceTaskList, task);\n\t\t}\n\t}\n\n\tif (emptySourceTaskList != NIL)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"MERGE has NOT MATCHED BY SOURCE clause, \"\n\t\t\t\t\t\t\t\t\"execute MERGE on all shards\")));\n\t\tAdjustTaskQueryForEmptySource(targetRelationId, mergeQuery, emptySourceTaskList,\n\t\t\t\t\t\t\t\t\t  intermediateResultIdPrefix);\n\t\tprunedTaskList = list_concat(prunedTaskList, emptySourceTaskList);\n\t}\n\n\tif (prunedTaskList == NIL)\n\t{\n\t\t/*\n\t\t * No task to execute, but we still increment STAT_QUERY_EXECUTION_SINGLE_SHARD\n\t\t * as per our convention.\n\t\t */\n\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_SINGLE_SHARD);\n\t\treturn;\n\t}\n\n\tereport(DEBUG1, (errmsg(\"Execute MERGE task list\")));\n\tbool randomAccess = true;\n\tbool interTransactions = false;\n\tAssert(scanState->tuplestorestate == NULL);\n\tscanState->tuplestorestate = tuplestore_begin_heap(randomAccess, interTransactions,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   work_mem);\n\tTupleDesc tupleDescriptor = ScanStateGetTupleDescriptor(scanState);\n\tParamListInfo paramListInfo = executorState->es_param_list_info;\n\tTupleDestination *tupleDest =\n\t\tCreateTupleStoreTupleDest(scanState->tuplestorestate, tupleDescriptor);\n\tuint64 rowsMerged =\n\t\tExecuteTaskListIntoTupleDestWithParam(ROW_MODIFY_NONCOMMUTATIVE,\n\t\t\t\t\t\t\t\t\t\t\t  prunedTaskList,\n\t\t\t\t\t\t\t\t\t\t\t  tupleDest,\n\t\t\t\t\t\t\t\t\t\t\t  hasReturning,\n\t\t\t\t\t\t\t\t\t\t\t  paramListInfo);\n\n\tif (list_length(prunedTaskList) == 1)\n\t{\n\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_SINGLE_SHARD);\n\t}\n\telse\n\t{\n\t\tIncrementStatCounterForMyDb(STAT_QUERY_EXECUTION_MULTI_SHARD);\n\t}\n\n\texecutorState->es_processed = rowsMerged;\n}\n\n\n/*\n * ExecuteMergeSourcePlanIntoColocatedIntermediateResults Executes the given PlannedStmt\n * and inserts tuples into a set of intermediate results that are colocated with the\n * target table for further processing MERGE INTO. It also returns the hash of shard\n * states that were used to insert tuplesinto the target relation.\n */\nstatic HTAB *\nExecuteMergeSourcePlanIntoColocatedIntermediateResults(Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   Query *mergeQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   List *sourceTargetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   PlannedStmt *sourcePlan,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   EState *executorState,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   char *intermediateResultIdPrefix,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   int partitionColumnIndex)\n{\n\tParamListInfo paramListInfo = executorState->es_param_list_info;\n\n\t/* Get column name list and partition column index for the target table */\n\tList *columnNameList =\n\t\tBuildColumnNameListFromTargetList(targetRelationId, sourceTargetList);\n\n\t/*\n\t * We don't track query counters for the COPY commands that are executed to\n\t * prepare intermediate results.\n\t */\n\tconst bool trackQueryCounters = false;\n\n\t/* set up a DestReceiver that copies into the intermediate file */\n\tconst bool publishableData = false;\n\tCitusCopyDestReceiver *copyDest = CreateCitusCopyDestReceiver(targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  columnNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  executorState,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  intermediateResultIdPrefix,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  publishableData,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  trackQueryCounters);\n\n\t/* We can skip when writing to intermediate files */\n\tcopyDest->skipCoercions = true;\n\n\tExecutePlanIntoDestReceiver(sourcePlan, paramListInfo, (DestReceiver *) copyDest);\n\n\texecutorState->es_processed = copyDest->tuplesSent;\n\tXactModificationLevel = XACT_MODIFICATION_DATA;\n\n\treturn copyDest->shardStateHash;\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/multi_executor.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_executor.c\n *\n * Entrypoint into distributed query execution.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_class.h\"\n#include \"commands/copy.h\"\n#include \"executor/execdebug.h\"\n#include \"nodes/execnodes.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"parser/parse_oper.h\"\n#include \"parser/parsetree.h\"\n#include \"storage/lmgr.h\"\n#include \"tcop/dest.h\"\n#include \"tcop/pquery.h\"\n#include \"tcop/utility.h\"\n#include \"utils/fmgrprotos.h\"\n#include \"utils/memutils.h\"\n#include \"utils/snapmgr.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/combine_query_planner.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/function_call_delegation.h\"\n#include \"distributed/insert_select_executor.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_shard_visibility.h\"\n\n\n/*\n * Controls the connection type for multi shard modifications, DDLs\n * TRUNCATE and multi-shard SELECT queries.\n */\nint MultiShardConnectionType = PARALLEL_CONNECTION;\nbool WritableStandbyCoordinator = false;\nbool AllowModificationsFromWorkersToReplicatedTables = true;\n\n/*\n * Controlled by the GUC citus.skip_constraint_validation\n */\nbool SkipConstraintValidation = false;\n\n/*\n * Setting that controls whether distributed queries should be\n * allowed within a task execution.\n */\nbool AllowNestedDistributedExecution = false;\n\n/*\n * Pointer to bound parameters of the current ongoing call to ExecutorRun.\n * If executor is not running, then this value is meaningless.\n */\nParamListInfo executorBoundParams = NULL;\n\n\n/* sort the returning to get consistent outputs, used only for testing */\nbool SortReturning = false;\n\n/*\n * How many nested executors have we started? This can happen for SQL\n * UDF calls. The outer query starts an executor, then postgres opens\n * another executor to run the SQL UDF.\n */\nint ExecutorLevel = 0;\n\n\n/* local function forward declarations */\nstatic Relation StubRelation(TupleDesc tupleDescriptor);\nstatic char * GetObjectTypeString(ObjectType objType);\nstatic bool AlterTableConstraintCheck(QueryDesc *queryDesc);\nstatic List * FindCitusCustomScanStates(PlanState *planState);\nstatic bool CitusCustomScanStateWalker(PlanState *planState,\n\t\t\t\t\t\t\t\t\t   List **citusCustomScanStates);\nstatic bool IsTaskExecutionAllowed(bool isRemote);\nstatic bool InLocalTaskExecutionOnShard(void);\nstatic bool MaybeInRemoteTaskExecution(void);\nstatic bool InTrigger(void);\n\n\n/*\n * CitusExecutorStart is the ExecutorStart_hook that gets called when\n * Postgres prepares for execution or EXPLAIN.\n */\nvoid\nCitusExecutorStart(QueryDesc *queryDesc, int eflags)\n{\n\tPlannedStmt *plannedStmt = queryDesc->plannedstmt;\n\n\t/*\n\t * We cannot modify XactReadOnly on Windows because it is not\n\t * declared with PGDLLIMPORT.\n\t */\n#ifndef WIN32\n\tif (RecoveryInProgress() && WritableStandbyCoordinator &&\n\t\tIsCitusPlan(plannedStmt->planTree))\n\t{\n\t\tPG_TRY();\n\t\t{\n\t\t\t/*\n\t\t\t * To enable writes from a hot standby we cheat our way through\n\t\t\t * the checks in standard_ExecutorStart by temporarily setting\n\t\t\t * XactReadOnly to false.\n\t\t\t */\n\t\t\tXactReadOnly = false;\n\t\t\tstandard_ExecutorStart(queryDesc, eflags);\n\t\t\tXactReadOnly = true;\n\t\t}\n\t\tPG_CATCH();\n\t\t{\n\t\t\tXactReadOnly = true;\n\t\t\tPG_RE_THROW();\n\t\t}\n\t\tPG_END_TRY();\n\t}\n\telse\n#endif\n\t{\n\t\tstandard_ExecutorStart(queryDesc, eflags);\n\t}\n}\n\n\n/*\n * CitusExecutorRun is the ExecutorRun_hook that gets called when postgres\n * executes a query.\n */\nvoid\nCitusExecutorRun(QueryDesc *queryDesc,\n\t\t\t\t ScanDirection direction, uint64 count, bool execute_once)\n{\n\tDestReceiver *dest = queryDesc->dest;\n\n\tParamListInfo savedBoundParams = executorBoundParams;\n\n\t/*\n\t * Save a pointer to query params so UDFs can access them by calling\n\t * ExecutorBoundParams().\n\t */\n\texecutorBoundParams = queryDesc->params;\n\n\t/*\n\t * We do some potentially time consuming operations ourself now before we hand off\n\t * control to postgres' executor. To make sure that time spent is accurately measured\n\t * we remove the totaltime instrumentation from the queryDesc. Instead we will start\n\t * and stop the instrumentation of the total time and put it back on the queryDesc\n\t * before returning (or rethrowing) from this function.\n\t */\n\tInstrumentation *volatile totalTime = queryDesc->totaltime;\n\tqueryDesc->totaltime = NULL;\n\n\tPG_TRY();\n\t{\n\t\tExecutorLevel++;\n\n\t\tif (totalTime)\n\t\t{\n\t\t\tInstrStartNode(totalTime);\n\t\t}\n\n\t\t/*\n\t\t * Disable execution of ALTER TABLE constraint validation queries. These\n\t\t * constraints will be validated in worker nodes, so running these queries\n\t\t * from the coordinator would be redundant.\n\t\t *\n\t\t * For example, ALTER TABLE ... ATTACH PARTITION checks that the new\n\t\t * partition doesn't violate constraints of the parent table, which\n\t\t * might involve running some SELECT queries.\n\t\t *\n\t\t * Ideally we'd completely skip these checks in the coordinator, but we don't\n\t\t * have any means to tell postgres to skip the checks. So the best we can do is\n\t\t * to not execute the queries and return an empty result set, as if this table has\n\t\t * no rows, so no constraints will be violated.\n\t\t */\n\t\tif (AlterTableConstraintCheck(queryDesc))\n\t\t{\n\t\t\tEState *estate = queryDesc->estate;\n\n\t\t\testate->es_processed = 0;\n\n\t\t\t/* start and shutdown tuple receiver to simulate empty result */\n\t\t\tdest->rStartup(queryDesc->dest, CMD_SELECT, queryDesc->tupDesc);\n\t\t\tdest->rShutdown(dest);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* switch into per-query memory context before calling PreExecScan */\n\t\t\tMemoryContext oldcontext = MemoryContextSwitchTo(\n\t\t\t\tqueryDesc->estate->es_query_cxt);\n\n\t\t\t/*\n\t\t\t * Call PreExecScan for all citus custom scan nodes prior to starting the\n\t\t\t * postgres exec scan to give some citus scan nodes some time to initialize\n\t\t\t * state that would be too late if it were to initialize when the first tuple\n\t\t\t * would need to return.\n\t\t\t */\n\t\t\tList *citusCustomScanStates = FindCitusCustomScanStates(queryDesc->planstate);\n\t\t\tCitusScanState *citusScanState = NULL;\n\t\t\tforeach_declared_ptr(citusScanState, citusCustomScanStates)\n\t\t\t{\n\t\t\t\tif (citusScanState->PreExecScan)\n\t\t\t\t{\n\t\t\t\t\tcitusScanState->PreExecScan(citusScanState);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* postgres will switch here again and will restore back on its own */\n\t\t\tMemoryContextSwitchTo(oldcontext);\n\n\t\t\t#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t\t\t/* PG18+ drops the “execute_once” argument */\n\t\t\tstandard_ExecutorRun(queryDesc,\n\t\t\t\t\t\t\t\t direction,\n\t\t\t\t\t\t\t\t count);\n\t\t#else\n\n\t\t\t/* PG17-: original four-arg signature */\n\t\t\tstandard_ExecutorRun(queryDesc,\n\t\t\t\t\t\t\t\t direction,\n\t\t\t\t\t\t\t\t count,\n\t\t\t\t\t\t\t\t execute_once);\n\t\t#endif\n\t\t}\n\n\t\tif (totalTime)\n\t\t{\n\t\t\tInstrStopNode(totalTime, queryDesc->estate->es_processed);\n\t\t\tqueryDesc->totaltime = totalTime;\n\t\t}\n\n\t\texecutorBoundParams = savedBoundParams;\n\t\tExecutorLevel--;\n\n\t\tif (ExecutorLevel == 0 && PlannerLevel == 0)\n\t\t{\n\t\t\t/*\n\t\t\t * We are leaving Citus code so no one should have any references to\n\t\t\t * cache entries. Release them now to not hold onto memory in long\n\t\t\t * transactions.\n\t\t\t */\n\t\t\tCitusTableCacheFlushInvalidatedEntries();\n\t\t\tInTopLevelDelegatedFunctionCall = false;\n\t\t}\n\n\t\t/*\n\t\t * Within a 2PC, when a function is delegated to a remote node, we pin\n\t\t * the distribution argument as the shard key for all the SQL in the\n\t\t * function's block. The restriction is imposed to not to access other\n\t\t * nodes from the current node, and violate the transactional integrity\n\t\t * of the 2PC. Now that the query is ending, reset the shard key to NULL.\n\t\t */\n\t\tCheckAndResetAllowedShardKeyValueIfNeeded();\n\t}\n\tPG_CATCH();\n\t{\n\t\tif (totalTime)\n\t\t{\n\t\t\tqueryDesc->totaltime = totalTime;\n\t\t}\n\n\t\texecutorBoundParams = savedBoundParams;\n\t\tExecutorLevel--;\n\n\t\tif (ExecutorLevel == 0 && PlannerLevel == 0)\n\t\t{\n\t\t\tInTopLevelDelegatedFunctionCall = false;\n\t\t}\n\n\t\t/*\n\t\t * In case of an exception, reset the pinned shard-key, for more\n\t\t * details see the function header.\n\t\t */\n\t\tCheckAndResetAllowedShardKeyValueIfNeeded();\n\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n}\n\n\n/*\n * FindCitusCustomScanStates returns a list of all citus custom scan states in it.\n */\nstatic List *\nFindCitusCustomScanStates(PlanState *planState)\n{\n\tList *citusCustomScanStates = NIL;\n\tCitusCustomScanStateWalker(planState, &citusCustomScanStates);\n\treturn citusCustomScanStates;\n}\n\n\n/*\n * CitusCustomScanStateWalker walks a planState tree structure and adds all\n * CitusCustomState nodes to the list passed by reference as the second argument.\n */\nstatic bool\nCitusCustomScanStateWalker(PlanState *planState, List **citusCustomScanStates)\n{\n\tif (IsCitusCustomState(planState))\n\t{\n\t\tCitusScanState *css = (CitusScanState *) planState;\n\t\t*citusCustomScanStates = lappend(*citusCustomScanStates, css);\n\n\t\t/* breaks the walking of this tree */\n\t\treturn true;\n\t}\n\treturn planstate_tree_walker(planState, CitusCustomScanStateWalker,\n\t\t\t\t\t\t\t\t citusCustomScanStates);\n}\n\n\n/*\n * ReturnTupleFromTuplestore reads the next tuple from the tuple store of the\n * given Citus scan node and returns it. It returns null if all tuples are read\n * from the tuple store.\n */\nTupleTableSlot *\nReturnTupleFromTuplestore(CitusScanState *scanState)\n{\n\tTuplestorestate *tupleStore = scanState->tuplestorestate;\n\tbool forwardScanDirection = true;\n\n\tif (tupleStore == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tEState *executorState = ScanStateGetExecutorState(scanState);\n\tScanDirection scanDirection = executorState->es_direction;\n\tAssert(ScanDirectionIsValid(scanDirection));\n\n\tif (ScanDirectionIsBackward(scanDirection))\n\t{\n\t\tforwardScanDirection = false;\n\t}\n\n\tExprState *qual = scanState->customScanState.ss.ps.qual;\n\tProjectionInfo *projInfo = scanState->customScanState.ss.ps.ps_ProjInfo;\n\tExprContext *econtext = scanState->customScanState.ss.ps.ps_ExprContext;\n\n\tif (!qual && !projInfo)\n\t{\n\t\t/* no quals, nor projections return directly from the tuple store. */\n\t\tTupleTableSlot *slot = scanState->customScanState.ss.ss_ScanTupleSlot;\n\t\ttuplestore_gettupleslot(tupleStore, forwardScanDirection, false, slot);\n\t\treturn slot;\n\t}\n\n\tfor (;;)\n\t{\n\t\t/*\n\t\t * If there is a very selective qual on the Citus Scan node we might block\n\t\t * interrupts for a longer time if we would not check for interrupts in this loop\n\t\t */\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\t/*\n\t\t * Reset per-tuple memory context to free any expression evaluation\n\t\t * storage allocated in the previous tuple cycle.\n\t\t */\n\t\tResetExprContext(econtext);\n\n\t\tTupleTableSlot *slot = scanState->customScanState.ss.ss_ScanTupleSlot;\n\t\ttuplestore_gettupleslot(tupleStore, forwardScanDirection, false, slot);\n\n\t\tif (TupIsNull(slot))\n\t\t{\n\t\t\t/*\n\t\t\t * When the tuple is null we have reached the end of the tuplestore. We will\n\t\t\t * return a null tuple, however, depending on the existence of a projection we\n\t\t\t * need to either return the scan tuple or the projected tuple.\n\t\t\t */\n\t\t\tif (projInfo)\n\t\t\t{\n\t\t\t\treturn ExecClearTuple(projInfo->pi_state.resultslot);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn slot;\n\t\t\t}\n\t\t}\n\n\t\t/* place the current tuple into the expr context */\n\t\tecontext->ecxt_scantuple = slot;\n\n\t\tif (!ExecQual(qual, econtext))\n\t\t{\n\t\t\t/* skip nodes that do not satisfy the qual (filter) */\n\t\t\tInstrCountFiltered1(scanState, 1);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* found a satisfactory scan tuple */\n\t\tif (projInfo)\n\t\t{\n\t\t\t/*\n\t\t\t * Form a projection tuple, store it in the result tuple slot and return it.\n\t\t\t * ExecProj works on the ecxt_scantuple on the context stored earlier.\n\t\t\t */\n\t\t\treturn ExecProject(projInfo);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* Here, we aren't projecting, so just return scan tuple */\n\t\t\treturn slot;\n\t\t}\n\t}\n}\n\n\n/*\n * ReadFileIntoTupleStore parses the records in a COPY-formatted file according\n * according to the given tuple descriptor and stores the records in a tuple\n * store.\n */\nvoid\nReadFileIntoTupleStore(char *fileName, char *copyFormat, TupleDesc tupleDescriptor,\n\t\t\t\t\t   Tuplestorestate *tupstore)\n{\n\t/*\n\t * Trick BeginCopyFrom into using our tuple descriptor by pretending it belongs\n\t * to a relation.\n\t */\n\tRelation stubRelation = StubRelation(tupleDescriptor);\n\n\tEState *executorState = CreateExecutorState();\n\tMemoryContext executorTupleContext = GetPerTupleMemoryContext(executorState);\n\tExprContext *executorExpressionContext = GetPerTupleExprContext(executorState);\n\n\tint columnCount = tupleDescriptor->natts;\n\tDatum *columnValues = palloc0(columnCount * sizeof(Datum));\n\tbool *columnNulls = palloc0(columnCount * sizeof(bool));\n\n\tList *copyOptions = NIL;\n\n\tint location = -1; /* \"unknown\" token location */\n\tDefElem *copyOption = makeDefElem(\"format\", (Node *) makeString(copyFormat),\n\t\t\t\t\t\t\t\t\t  location);\n\tcopyOptions = lappend(copyOptions, copyOption);\n\n\tCopyFromState copyState = BeginCopyFrom(NULL, stubRelation, NULL,\n\t\t\t\t\t\t\t\t\t\t\tfileName, false, NULL,\n\t\t\t\t\t\t\t\t\t\t\tNULL, copyOptions);\n\n\twhile (true)\n\t{\n\t\tResetPerTupleExprContext(executorState);\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(executorTupleContext);\n\n\t\tbool nextRowFound = NextCopyFrom(copyState, executorExpressionContext,\n\t\t\t\t\t\t\t\t\t\t columnValues, columnNulls);\n\t\tif (!nextRowFound)\n\t\t{\n\t\t\tMemoryContextSwitchTo(oldContext);\n\t\t\tbreak;\n\t\t}\n\n\t\ttuplestore_putvalues(tupstore, tupleDescriptor, columnValues, columnNulls);\n\t\tMemoryContextSwitchTo(oldContext);\n\t}\n\n\tEndCopyFrom(copyState);\n\tpfree(columnValues);\n\tpfree(columnNulls);\n}\n\n\n/*\n * SortTupleStore gets a CitusScanState and sorts the tuplestore by all the\n * entries in the target entry list, starting from the first one and\n * ending with the last entry.\n *\n * The sorting is done in ASC order.\n */\nvoid\nSortTupleStore(CitusScanState *scanState)\n{\n\tTupleDesc tupleDescriptor = ScanStateGetTupleDescriptor(scanState);\n\tTuplestorestate *tupleStore = scanState->tuplestorestate;\n\n\tList *targetList = scanState->customScanState.ss.ps.plan->targetlist;\n\tuint32 expectedColumnCount = list_length(targetList);\n\n\t/* Convert list-ish representation to arrays wanted by executor */\n\tint numberOfSortKeys = expectedColumnCount;\n\tAttrNumber *sortColIdx = (AttrNumber *) palloc(numberOfSortKeys * sizeof(AttrNumber));\n\tOid *sortOperators = (Oid *) palloc(numberOfSortKeys * sizeof(Oid));\n\tOid *collations = (Oid *) palloc(numberOfSortKeys * sizeof(Oid));\n\tbool *nullsFirst = (bool *) palloc(numberOfSortKeys * sizeof(bool));\n\n\tint sortKeyIndex = 0;\n\n\t/*\n\t * Iterate on the returning target list and generate the necessary information\n\t * for sorting the tuples.\n\t */\n\tTargetEntry *returningEntry = NULL;\n\tforeach_declared_ptr(returningEntry, targetList)\n\t{\n\t\tOid sortop = InvalidOid;\n\n\t\t/* determine the sortop, we don't need anything else */\n\t\tget_sort_group_operators(exprType((Node *) returningEntry->expr),\n\t\t\t\t\t\t\t\t true, false, false,\n\t\t\t\t\t\t\t\t &sortop, NULL, NULL,\n\t\t\t\t\t\t\t\t NULL);\n\n\t\tsortColIdx[sortKeyIndex] = sortKeyIndex + 1;\n\t\tsortOperators[sortKeyIndex] = sortop;\n\t\tcollations[sortKeyIndex] = exprCollation((Node *) returningEntry->expr);\n\t\tnullsFirst[sortKeyIndex] = false;\n\n\t\tsortKeyIndex++;\n\t}\n\n\tTuplesortstate *tuplesortstate =\n\t\ttuplesort_begin_heap(tupleDescriptor, numberOfSortKeys, sortColIdx, sortOperators,\n\t\t\t\t\t\t\t collations, nullsFirst, work_mem, NULL, false);\n\n\twhile (true)\n\t{\n\t\tTupleTableSlot *slot = ReturnTupleFromTuplestore(scanState);\n\n\t\tif (TupIsNull(slot))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t/* tuplesort_puttupleslot copies the slot into sort context */\n\t\ttuplesort_puttupleslot(tuplesortstate, slot);\n\t}\n\n\t/* perform the actual sort operation */\n\ttuplesort_performsort(tuplesortstate);\n\n\t/*\n\t * Truncate the existing tupleStore, because we'll fill it back\n\t * from the sorted tuplestore.\n\t */\n\ttuplestore_clear(tupleStore);\n\n\t/* iterate over all the sorted tuples, add them to original tuplestore */\n\twhile (true)\n\t{\n\t\tTupleTableSlot *newSlot = MakeSingleTupleTableSlot(tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &TTSOpsMinimalTuple);\n\t\tbool found = tuplesort_gettupleslot(tuplesortstate, true, false, newSlot, NULL);\n\n\t\tif (!found)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t/* tuplesort_puttupleslot copies the slot into the tupleStore context */\n\t\ttuplestore_puttupleslot(tupleStore, newSlot);\n\t}\n\n\ttuplestore_rescan(scanState->tuplestorestate);\n\n\t/* terminate the sort, clear unnecessary resources */\n\ttuplesort_end(tuplesortstate);\n}\n\n\n/*\n * StubRelation creates a stub Relation from the given tuple descriptor.\n * To be able to use copy.c, we need a Relation descriptor. As there is no\n * relation corresponding to the data loaded from workers, we need to fake one.\n * We just need the bare minimal set of fields accessed by BeginCopyFrom().\n */\nstatic Relation\nStubRelation(TupleDesc tupleDescriptor)\n{\n\tRelation stubRelation = palloc0(sizeof(RelationData));\n\tstubRelation->rd_att = tupleDescriptor;\n\tstubRelation->rd_rel = palloc0(sizeof(FormData_pg_class));\n\tstubRelation->rd_rel->relkind = RELKIND_RELATION;\n\n\treturn stubRelation;\n}\n\n\n/*\n * ExecuteQueryStringIntoDestReceiver plans and executes a query and sends results\n * to the given DestReceiver.\n */\nvoid\nExecuteQueryStringIntoDestReceiver(const char *queryString, ParamListInfo params,\n\t\t\t\t\t\t\t\t   DestReceiver *dest)\n{\n\tQuery *query = ParseQueryString(queryString, NULL, 0);\n\n\tExecuteQueryIntoDestReceiver(query, params, dest);\n}\n\n\n/*\n * ParseQuery parses query string and returns a Query struct.\n */\nQuery *\nParseQueryString(const char *queryString, Oid *paramOids, int numParams)\n{\n\tRawStmt *rawStmt = (RawStmt *) ParseTreeRawStmt(queryString);\n\n\t/* rewrite the parsed RawStmt to produce a Query */\n\tQuery *query = RewriteRawQueryStmt(rawStmt, queryString, paramOids, numParams);\n\n\treturn query;\n}\n\n\n/*\n * RewriteRawQueryStmt rewrites the given parsed RawStmt according to the other\n * parameters and returns a Query struct.\n */\nQuery *\nRewriteRawQueryStmt(RawStmt *rawStmt, const char *queryString, Oid *paramOids, int\n\t\t\t\t\tnumParams)\n{\n\tList *queryTreeList =\n\t\tpg_analyze_and_rewrite_fixedparams(rawStmt, queryString, paramOids, numParams,\n\t\t\t\t\t\t\t\t\t\t   NULL);\n\n\tif (list_length(queryTreeList) != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"can only execute a single query\")));\n\t}\n\n\tQuery *query = (Query *) linitial(queryTreeList);\n\n\treturn query;\n}\n\n\n/*\n * ExecuteQueryIntoDestReceiver plans and executes a query and sends results to the given\n * DestReceiver.\n */\nvoid\nExecuteQueryIntoDestReceiver(Query *query, ParamListInfo params, DestReceiver *dest)\n{\n\tint cursorOptions = CURSOR_OPT_PARALLEL_OK;\n\n\tif (query->commandType == CMD_UTILITY)\n\t{\n\t\t/* can only execute DML/SELECT via this path */\n\t\tereport(ERROR, (errmsg(\"cannot execute utility commands\")));\n\t}\n\n\t/* plan the subquery, this may be another distributed query */\n\tPlannedStmt *queryPlan = pg_plan_query(query, NULL, cursorOptions, params);\n\n\tExecutePlanIntoDestReceiver(queryPlan, params, dest);\n}\n\n\n/*\n * ExecutePlanIntoDestReceiver executes a query plan and sends results to the given\n * DestReceiver.\n */\nuint64\nExecutePlanIntoDestReceiver(PlannedStmt *queryPlan, ParamListInfo params,\n\t\t\t\t\t\t\tDestReceiver *dest)\n{\n\tint eflags = 0;\n\tlong count = FETCH_ALL;\n\n\t/* create a new portal for executing the query */\n\tPortal portal = CreateNewPortal();\n\n\t/* don't display the portal in pg_cursors, it is for internal use only */\n\tportal->visible = false;\n\n\tPortalDefineQuery(\n\t\tportal,\n\t\tNULL,                 /* no prepared statement name */\n\t\t\"\",                   /* query text */\n\t\tCMDTAG_SELECT,        /* command tag */\n\t\tlist_make1(queryPlan),/* list of PlannedStmt* */\n\t\tNULL                  /* no CachedPlan */\n\t\t);\n\n\tPortalStart(portal, params, eflags, GetActiveSnapshot());\n\n\n\tQueryCompletion qc = { 0 };\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n/* PG 18+: six-arg signature (drop the run_once bool) */\n\tPortalRun(portal,\n\t\t\t  count,  /* how many rows to fetch */\n\t\t\t  false,  /* isTopLevel */\n\t\t\t  dest,   /* DestReceiver *dest */\n\t\t\t  dest,   /* DestReceiver *altdest */\n\t\t\t  &qc);  /* QueryCompletion *qc */\n#else\n\n/* PG 17-: original seven-arg signature */\n\tPortalRun(portal,\n\t\t\t  count,  /* how many rows to fetch */\n\t\t\t  false,  /* isTopLevel */\n\t\t\t  true,   /* run_once */\n\t\t\t  dest,   /* DestReceiver *dest */\n\t\t\t  dest,   /* DestReceiver *altdest */\n\t\t\t  &qc);  /* QueryCompletion *qc */\n#endif\n\n\tPortalDrop(portal, false);\n\n\treturn qc.nprocessed;\n}\n\n\n/*\n * SetLocalMultiShardModifyModeToSequential is simply a C interface for setting\n * the following:\n *      SET LOCAL citus.multi_shard_modify_mode = 'sequential';\n */\nvoid\nSetLocalMultiShardModifyModeToSequential()\n{\n\tset_config_option(\"citus.multi_shard_modify_mode\", \"sequential\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n}\n\n\n/*\n * EnsureSequentialMode makes sure that the current transaction is already in\n * sequential mode, or can still safely be put in sequential mode, it errors if that is\n * not possible. The error contains information for the user to retry the transaction with\n * sequential mode set from the beginning.\n *\n * Takes an ObjectType to use in the error/debug messages.\n */\nvoid\nEnsureSequentialMode(ObjectType objType)\n{\n\tchar *objTypeString = GetObjectTypeString(objType);\n\n\tif (ParallelQueryExecutedInTransaction())\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot run %s command because there was a \"\n\t\t\t\t\t\t\t   \"parallel operation on a distributed table in the \"\n\t\t\t\t\t\t\t   \"transaction\", objTypeString),\n\t\t\t\t\t\terrdetail(\"When running command on/for a distributed %s, Citus \"\n\t\t\t\t\t\t\t\t  \"needs to perform all operations over a single \"\n\t\t\t\t\t\t\t\t  \"connection per node to ensure consistency.\",\n\t\t\t\t\t\t\t\t  objTypeString),\n\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t}\n\n\tereport(DEBUG1, (errmsg(\"switching to sequential query execution mode\"),\n\t\t\t\t\t errdetail(\n\t\t\t\t\t\t \"A command for a distributed %s is run. To make sure subsequent \"\n\t\t\t\t\t\t \"commands see the %s correctly we need to make sure to \"\n\t\t\t\t\t\t \"use only one connection for all future commands\",\n\t\t\t\t\t\t objTypeString, objTypeString)));\n\n\tSetLocalMultiShardModifyModeToSequential();\n}\n\n\n/*\n * GetObjectTypeString takes an ObjectType and returns the string version of it.\n * We (for now) call this function only in EnsureSequentialMode, and use the returned\n * string to generate error/debug messages.\n *\n * If GetObjectTypeString gets called with an ObjectType that is not in the switch\n * statement, the function will return the string \"object\", and emit a debug message.\n * In that case, make sure you've added the newly supported type to the switch statement.\n */\nstatic char *\nGetObjectTypeString(ObjectType objType)\n{\n\tswitch (objType)\n\t{\n\t\tcase OBJECT_AGGREGATE:\n\t\t{\n\t\t\treturn \"aggregate\";\n\t\t}\n\n\t\tcase OBJECT_COLLATION:\n\t\t{\n\t\t\treturn \"collation\";\n\t\t}\n\n\t\tcase OBJECT_DATABASE:\n\t\t{\n\t\t\treturn \"database\";\n\t\t}\n\n\t\tcase OBJECT_DOMAIN:\n\t\t{\n\t\t\treturn \"domain\";\n\t\t}\n\n\t\tcase OBJECT_EXTENSION:\n\t\t{\n\t\t\treturn \"extension\";\n\t\t}\n\n\t\tcase OBJECT_FOREIGN_SERVER:\n\t\t{\n\t\t\treturn \"foreign server\";\n\t\t}\n\n\t\tcase OBJECT_FUNCTION:\n\t\t{\n\t\t\treturn \"function\";\n\t\t}\n\n\t\tcase OBJECT_PUBLICATION:\n\t\t{\n\t\t\treturn \"publication\";\n\t\t}\n\n\t\tcase OBJECT_SCHEMA:\n\t\t{\n\t\t\treturn \"schema\";\n\t\t}\n\n\t\tcase OBJECT_TSCONFIGURATION:\n\t\t{\n\t\t\treturn \"text search configuration\";\n\t\t}\n\n\t\tcase OBJECT_TSDICTIONARY:\n\t\t{\n\t\t\treturn \"text search dictionary\";\n\t\t}\n\n\t\tcase OBJECT_TYPE:\n\t\t{\n\t\t\treturn \"type\";\n\t\t}\n\n\t\tcase OBJECT_VIEW:\n\t\t{\n\t\t\treturn \"view\";\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"unsupported object type\"),\n\t\t\t\t\t\t\t errdetail(\"Please add string conversion for the object.\")));\n\t\t\treturn \"object\";\n\t\t}\n\t}\n}\n\n\n/*\n * AlterTableConstraintCheck returns if the given query is an ALTER TABLE\n * constraint check query.\n *\n * Postgres uses SPI to execute these queries. To see examples of how these\n * constraint check queries look like, see RI_Initial_Check() and RI_Fkey_check().\n */\nstatic bool\nAlterTableConstraintCheck(QueryDesc *queryDesc)\n{\n\tif (!AlterTableInProgress())\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * These queries are one or more SELECT queries, where postgres checks\n\t * their results either for NULL values or existence of a row at all.\n\t */\n\tif (queryDesc->plannedstmt->commandType != CMD_SELECT)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * While an ALTER TABLE is in progress, we might do SELECTs on some\n\t * catalog tables too. For example, when dropping a column, citus_drop_trigger()\n\t * runs some SELECTs on catalog tables. These are not constraint check queries.\n\t */\n\tif (!IsCitusPlan(queryDesc->plannedstmt->planTree))\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ExecutorBoundParams returns the bound parameters of the current ongoing call\n * to ExecutorRun. This is meant to be used by UDFs which need to access bound\n * parameters.\n */\nParamListInfo\nExecutorBoundParams(void)\n{\n\tAssert(ExecutorLevel > 0);\n\treturn executorBoundParams;\n}\n\n\n/*\n * EnsureTaskExecutionAllowed ensures that we do not perform remote\n * execution from within a task. That could happen when the user calls\n * a function in a query that gets pushed down to the worker, and the\n * function performs a query on a distributed table.\n */\nvoid\nEnsureTaskExecutionAllowed(bool isRemote)\n{\n\tif (IsTaskExecutionAllowed(isRemote))\n\t{\n\t\treturn;\n\t}\n\n\tereport(ERROR, (errmsg(\"cannot execute a distributed query from a query on a \"\n\t\t\t\t\t\t   \"shard\"),\n\t\t\t\t\terrdetail(\"Executing a distributed query in a function call that \"\n\t\t\t\t\t\t\t  \"may be pushed to a remote node can lead to incorrect \"\n\t\t\t\t\t\t\t  \"results.\"),\n\t\t\t\t\terrhint(\"Avoid nesting of distributed queries or use alter user \"\n\t\t\t\t\t\t\t\"current_user set citus.allow_nested_distributed_execution \"\n\t\t\t\t\t\t\t\"to on to allow it with possible incorrectness.\")));\n}\n\n\n/*\n * IsTaskExecutionAllowed determines whether task execution is currently allowed.\n * In general, nested distributed execution is not allowed, except in a few cases\n * (forced function call delegation, triggers).\n *\n * We distinguish between local and remote tasks because triggers only disallow\n * remote task execution.\n */\nstatic bool\nIsTaskExecutionAllowed(bool isRemote)\n{\n\tif (AllowNestedDistributedExecution)\n\t{\n\t\t/* user explicitly allows nested execution */\n\t\treturn true;\n\t}\n\n\tif (!isRemote)\n\t{\n\t\tif (AllowedDistributionColumnValue.isActive)\n\t\t{\n\t\t\t/*\n\t\t\t * When we are in a forced delegated function call, we explicitly check\n\t\t\t * whether local tasks use the same distribution column value in\n\t\t\t * EnsureForceDelegationDistributionKey.\n\t\t\t */\n\t\t\treturn true;\n\t\t}\n\n\t\tif (InTrigger())\n\t\t{\n\t\t\t/*\n\t\t\t * In triggers on shards we only disallow remote tasks. This has a few\n\t\t\t * reasons:\n\t\t\t *\n\t\t\t * - We want to enable access to co-located shards, but do not have additional\n\t\t\t *   checks yet.\n\t\t\t * - Users need to explicitly set enable_unsafe_triggers in order to create\n\t\t\t *   triggers on distributed tables.\n\t\t\t * - Triggers on Citus local tables should be able to access other Citus local\n\t\t\t *   tables.\n\t\t\t */\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn !InLocalTaskExecutionOnShard() && !MaybeInRemoteTaskExecution();\n}\n\n\n/*\n * InLocalTaskExecutionOnShard returns whether we are currently in the local executor\n * and it is working on a shard of a distributed table.\n *\n * In general, we can allow distributed queries inside of local executor, because\n * we can correctly assign tasks to connections. However, we preemptively protect\n * against distributed queries inside of queries on shards of a distributed table,\n * because those might start failing after a shard move.\n */\nstatic bool\nInLocalTaskExecutionOnShard(void)\n{\n\tif (LocalExecutorShardId == INVALID_SHARD_ID)\n\t{\n\t\t/* local executor is not active or is processing a task without shards */\n\t\treturn false;\n\t}\n\n\tif (!DistributedTableShardId(LocalExecutorShardId))\n\t{\n\t\t/*\n\t\t * Local executor is processing a query on a shard, but the shard belongs\n\t\t * to a reference table or Citus local table. We do not expect those to\n\t\t * move.\n\t\t */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * MaybeInRemoteTaskExecution returns whether we could in a remote task execution.\n *\n * We consider anything that happens in a Citus-internal backend, except deleged\n * function or procedure calls as a potential task execution.\n *\n * This function will also return true in other scenarios, such as during metadata\n * syncing. However, since this function is mainly used for restricting (dangerous)\n * nested executions, it is good to be pessimistic.\n */\nstatic bool\nMaybeInRemoteTaskExecution(void)\n{\n\tif (!IsCitusInternalBackend())\n\t{\n\t\t/* in a regular, client-initiated backend doing a regular task */\n\t\treturn false;\n\t}\n\n\tif (InTopLevelDelegatedFunctionCall || InDelegatedProcedureCall)\n\t{\n\t\t/* in a citus-initiated backend, but also in a delegated a procedure call */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * InTrigger returns whether the execution is currently in a trigger.\n */\nstatic bool\nInTrigger(void)\n{\n\treturn DatumGetInt32(pg_trigger_depth(NULL)) > 0;\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/multi_server_executor.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_server_executor.c\n *\n * Function definitions for distributed task execution for adaptive\n * executor.\n * routines are implemented backend-side logic; and they trigger executions\n * on the client-side via function hooks that they load.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/subplan_execution.h\"\n#include \"distributed/tuple_destination.h\"\n#include \"distributed/worker_protocol.h\"\n\nint RemoteTaskCheckInterval = 10; /* per cycle sleep interval in millisecs */\nint TaskExecutorType = MULTI_EXECUTOR_ADAPTIVE; /* distributed executor type */\nbool EnableRepartitionJoins = false;\n\n\n/*\n * JobExecutorType selects the executor type for the given distributedPlan using the task\n * executor type config value. The function then checks if the given distributedPlan needs\n * more resources than those provided to it by other config values, and issues\n * warnings accordingly. If the selected executor type cannot execute the given\n * distributedPlan, the function errors out.\n */\nMultiExecutorType\nJobExecutorType(DistributedPlan *distributedPlan)\n{\n\tJob *job = distributedPlan->workerJob;\n\n\tif (distributedPlan->modifyQueryViaCoordinatorOrRepartition != NULL)\n\t{\n\t\tif (IsMergeQuery(distributedPlan->modifyQueryViaCoordinatorOrRepartition))\n\t\t{\n\t\t\treturn MULTI_EXECUTOR_NON_PUSHABLE_MERGE_QUERY;\n\t\t}\n\n\t\t/*\n\t\t * We go through\n\t\t * MULTI_EXECUTOR_NON_PUSHABLE_INSERT_SELECT because\n\t\t * the executor already knows how to handle adaptive\n\t\t * executor when necessary.\n\t\t */\n\t\treturn MULTI_EXECUTOR_NON_PUSHABLE_INSERT_SELECT;\n\t}\n\n\t/*\n\t * If we have repartition jobs with adaptive executor and repartition\n\t * joins are not enabled, error out.\n\t */\n\tint dependentJobCount = list_length(job->dependentJobList);\n\tif (!EnableRepartitionJoins && dependentJobCount > 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"the query contains a join that requires repartitioning\"),\n\t\t\t\t\t\terrhint(\"Set citus.enable_repartition_joins to on to enable \"\n\t\t\t\t\t\t\t\t\"repartitioning\")));\n\t}\n\n\t/*\n\t * Debug distribution column value if possible. The distributed planner sometimes\n\t * defers creating the tasks, so the task list might be NIL. Still, it sets the\n\t * partitionKeyValue and we print it here.\n\t */\n\tif (list_length(job->taskList) <= 1 && IsLoggableLevel(DEBUG2))\n\t{\n\t\tConst *partitionValueConst = job->partitionKeyValue;\n\n\t\tif (partitionValueConst != NULL && !partitionValueConst->constisnull)\n\t\t{\n\t\t\tDatum partitionColumnValue = partitionValueConst->constvalue;\n\t\t\tOid partitionColumnType = partitionValueConst->consttype;\n\t\t\tchar *partitionColumnString = DatumToString(partitionColumnValue,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionColumnType);\n\n\t\t\tereport(DEBUG2, (errmsg(\"query has a single distribution column value: \"\n\t\t\t\t\t\t\t\t\t\"%s\", partitionColumnString)));\n\t\t}\n\t}\n\n\treturn MULTI_EXECUTOR_ADAPTIVE;\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/partitioned_intermediate_results.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * partition_intermediate_results.c\n *   Functions for writing partitioned intermediate results.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"port.h\"\n\n#include \"access/hash.h\"\n#include \"access/nbtree.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/primnodes.h\"\n#include \"tcop/pquery.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/typcache.h\"\n\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/utils/function.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/*\n * PartitionedResultDestReceiver is used for streaming tuples into a set of\n * partitioned result files.\n */\ntypedef struct PartitionedResultDestReceiver\n{\n\t/* public DestReceiver interface */\n\tDestReceiver pub;\n\n\t/* on lazy startup we only startup the DestReceiver once they receive a tuple */\n\tbool lazyStartup;\n\n\t/*\n\t * Stores the arguments passed to the PartidionedResultDestReceiver's rStarup\n\t * function. These arguments are reused when lazyStartup has been set to true.\n\t * On the processing of a first tuple for a partitionDestReceiver since rStartup it\n\t * will pass the arguments here to the rStartup function of partitionDestReceiver to\n\t * prepare it for receiving tuples.\n\t *\n\t * Even though not used without lazyStartup we just always populate these with the\n\t * last invoked arguments for our rStartup.\n\t */\n\tstruct\n\t{\n\t\t/*\n\t\t * operation as passed to rStartup, mostly the CmdType of the command being\n\t\t * streamed into this DestReceiver\n\t\t */\n\t\tint operation;\n\n\t\t/*\n\t\t * TupleDesc describing the layout of the tuples being streamed into the\n\t\t * DestReceiver.\n\t\t */\n\t\tTupleDesc tupleDescriptor;\n\t} startupArguments;\n\n\t/* which column of streamed tuples to use as partition column */\n\tint partitionColumnIndex;\n\n\t/* The number of partitions being partitioned into */\n\tint partitionCount;\n\n\t/* used for deciding which partition a shard belongs to. */\n\tCitusTableCacheEntry *shardSearchInfo;\n\n\t/* Tuples matching shardSearchInfo[i] are sent to partitionDestReceivers[i]. */\n\tDestReceiver **partitionDestReceivers;\n\n\t/* keeping track of which partitionDestReceivers have been started */\n\tBitmapset *startedDestReceivers;\n\n\t/* whether NULL partition column values are allowed */\n\tbool allowNullPartitionColumnValues;\n} PartitionedResultDestReceiver;\n\nstatic Portal StartPortalForQueryExecution(const char *queryString);\nstatic void PartitionedResultDestReceiverStartup(DestReceiver *dest, int operation,\n\t\t\t\t\t\t\t\t\t\t\t\t TupleDesc inputTupleDescriptor);\nstatic bool PartitionedResultDestReceiverReceive(TupleTableSlot *slot,\n\t\t\t\t\t\t\t\t\t\t\t\t DestReceiver *dest);\nstatic void PartitionedResultDestReceiverShutdown(DestReceiver *dest);\nstatic void PartitionedResultDestReceiverDestroy(DestReceiver *copyDest);\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(worker_partition_query_result);\n\n\n/*\n * worker_partition_query_result executes a query and writes the results into a\n * set of local files according to the partition scheme and the partition column.\n */\nDatum\nworker_partition_query_result(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tReturnSetInfo *resultInfo = (ReturnSetInfo *) fcinfo->resultinfo;\n\n\ttext *resultIdPrefixText = PG_GETARG_TEXT_P(0);\n\tchar *resultIdPrefixString = text_to_cstring(resultIdPrefixText);\n\n\t/* verify that resultIdPrefix doesn't contain invalid characters */\n\tQueryResultFileName(resultIdPrefixString);\n\n\ttext *queryText = PG_GETARG_TEXT_P(1);\n\tchar *queryString = text_to_cstring(queryText);\n\n\tint partitionColumnIndex = PG_GETARG_INT32(2);\n\tOid partitionMethodOid = PG_GETARG_OID(3);\n\n\tchar partitionMethod = LookupDistributionMethod(partitionMethodOid);\n\tif (partitionMethod != DISTRIBUTE_BY_HASH && partitionMethod != DISTRIBUTE_BY_RANGE)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"only hash and range partitiong schemes are supported\")));\n\t}\n\n\tArrayType *minValuesArray = PG_GETARG_ARRAYTYPE_P(4);\n\tint32 minValuesCount = ArrayObjectCount(minValuesArray);\n\n\tArrayType *maxValuesArray = PG_GETARG_ARRAYTYPE_P(5);\n\tint32 maxValuesCount = ArrayObjectCount(maxValuesArray);\n\n\tbool binaryCopy = PG_GETARG_BOOL(6);\n\tbool allowNullPartitionColumnValues = PG_GETARG_BOOL(7);\n\tbool generateEmptyResults = PG_GETARG_BOOL(8);\n\n\tif (!IsMultiStatementTransaction())\n\t{\n\t\tereport(ERROR, (errmsg(\"worker_partition_query_result can only be used in a \"\n\t\t\t\t\t\t\t   \"transaction block\")));\n\t}\n\n\t/*\n\t * Make sure that this transaction has a distributed transaction ID.\n\t *\n\t * Intermediate results will be stored in a directory that is derived\n\t * from the distributed transaction ID.\n\t */\n\tEnsureDistributedTransactionId();\n\n\tCreateIntermediateResultsDirectory();\n\n\tif (minValuesCount != maxValuesCount)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t errmsg(\n\t\t\t\t\t \"min values and max values must have the same number of elements\")));\n\t}\n\n\tint partitionCount = minValuesCount;\n\tif (partitionCount == 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"number of partitions cannot be 0\")));\n\t}\n\n\t/* start execution early in order to extract the tuple descriptor */\n\tPortal portal = StartPortalForQueryExecution(queryString);\n\n\t/* extract the partition column */\n\tTupleDesc tupleDescriptor = portal->tupDesc;\n\tif (tupleDescriptor == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"query must generate a set of rows\")));\n\t}\n\n\tif (partitionColumnIndex < 0 || partitionColumnIndex >= tupleDescriptor->natts)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"partition column index must be between 0 and %d\",\n\t\t\t\t\t\t\t   tupleDescriptor->natts - 1)));\n\t}\n\n\tFormData_pg_attribute *partitionColumnAttr = TupleDescAttr(tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   partitionColumnIndex);\n\tVar *partitionColumn = makeVar(partitionColumnIndex, partitionColumnIndex,\n\t\t\t\t\t\t\t\t   partitionColumnAttr->atttypid,\n\t\t\t\t\t\t\t\t   partitionColumnAttr->atttypmod,\n\t\t\t\t\t\t\t\t   partitionColumnAttr->attcollation, 0);\n\n\t/* construct an artificial CitusTableCacheEntry for shard pruning */\n\tCitusTableCacheEntry *shardSearchInfo =\n\t\tQueryTupleShardSearchInfo(minValuesArray, maxValuesArray,\n\t\t\t\t\t\t\t\t  partitionMethod, partitionColumn);\n\n\t/* prepare the output destination */\n\tEState *estate = CreateExecutorState();\n\tMemoryContext tupleContext = GetPerTupleMemoryContext(estate);\n\n\t/* create all dest receivers */\n\tDestReceiver **dests = palloc0(partitionCount * sizeof(DestReceiver *));\n\tfor (int partitionIndex = 0; partitionIndex < partitionCount; partitionIndex++)\n\t{\n\t\tStringInfo resultId = makeStringInfo();\n\t\tappendStringInfo(resultId, \"%s_%d\", resultIdPrefixString, partitionIndex);\n\t\tchar *filePath = QueryResultFileName(resultId->data);\n\t\tDestReceiver *partitionDest = CreateFileDestReceiver(filePath, tupleContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t binaryCopy);\n\t\tdests[partitionIndex] = partitionDest;\n\t}\n\n\t/*\n\t * If we are asked to generated empty results, use non-lazy startup.\n\t *\n\t * The rStartup of the FileDestReceiver will be called for all partitions\n\t * and generate empty files, which may still have binary header/footer.\n\t */\n\tconst bool lazyStartup = !generateEmptyResults;\n\n\tDestReceiver *dest = CreatePartitionedResultDestReceiver(\n\t\tpartitionColumnIndex,\n\t\tpartitionCount,\n\t\tshardSearchInfo,\n\t\tdests,\n\t\tlazyStartup,\n\t\tallowNullPartitionColumnValues);\n\n\t/* execute the query */\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t/* PG18+: drop the “run_once” bool */\n\tPortalRun(portal,\n\t\t\t  FETCH_ALL,   /* count */\n\t\t\t  false,       /* isTopLevel */\n\t\t\t  dest,        /* dest receiver */\n\t\t\t  dest,        /* alternative dest */\n\t\t\t  NULL);       /* QueryCompletion *qc */\n#else\n\n\t/* PG15–17: original seven‐arg signature */\n\tPortalRun(portal,\n\t\t\t  FETCH_ALL,   /* count */\n\t\t\t  false,       /* isTopLevel */\n\t\t\t  true,        /* run_once */\n\t\t\t  dest,        /* dest receiver */\n\t\t\t  dest,        /* alternative dest */\n\t\t\t  NULL);       /* QueryCompletion *qc */\n#endif\n\n\n\t/* construct the output result */\n\tTupleDesc returnTupleDesc = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &returnTupleDesc);\n\tresultInfo->returnMode = SFRM_Materialize;\n\tresultInfo->setResult = tupleStore;\n\tresultInfo->setDesc = returnTupleDesc;\n\n\tfor (int partitionIndex = 0; partitionIndex < partitionCount; partitionIndex++)\n\t{\n\t\tuint64 recordsWritten = 0;\n\t\tuint64 bytesWritten = 0;\n\t\tDatum values[3];\n\t\tbool nulls[3];\n\n\t\tFileDestReceiverStats(dests[partitionIndex], &recordsWritten, &bytesWritten);\n\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(nulls, 0, sizeof(nulls));\n\n\t\tvalues[0] = Int32GetDatum(partitionIndex);\n\t\tvalues[1] = UInt64GetDatum(recordsWritten);\n\t\tvalues[2] = UInt64GetDatum(bytesWritten);\n\n\t\ttuplestore_putvalues(tupleStore, returnTupleDesc, values, nulls);\n\t}\n\tPortalDrop(portal, false);\n\tFreeExecutorState(estate);\n\n\tdest->rDestroy(dest);\n\n\tPG_RETURN_INT64(1);\n}\n\n\n/*\n * StartPortalForQueryExecution creates and starts a portal which can be\n * used for running the given query.\n */\nstatic Portal\nStartPortalForQueryExecution(const char *queryString)\n{\n\tQuery *query = ParseQueryString(queryString, NULL, 0);\n\n\tint cursorOptions = CURSOR_OPT_PARALLEL_OK;\n\tPlannedStmt *queryPlan = pg_plan_query(query, NULL, cursorOptions, NULL);\n\n\tPortal portal = CreateNewPortal();\n\n\t/* don't display the portal in pg_cursors, it is for internal use only */\n\tportal->visible = false;\n\n\tPortalDefineQuery(\n\t\tportal,\n\t\tNULL,\n\t\tqueryString,\n\t\tCMDTAG_SELECT,\n\t\tlist_make1(queryPlan),\n\t\tNULL                   /* no CachedPlan */\n\t\t);\n\n\tint eflags = 0;\n\tPortalStart(portal, NULL, eflags, GetActiveSnapshot());\n\n\treturn portal;\n}\n\n\n/*\n * QueryTupleShardSearchInfo returns a CitusTableCacheEntry which has enough\n * information so that FindShardInterval() can find the shard corresponding\n * to a tuple.\n */\nCitusTableCacheEntry *\nQueryTupleShardSearchInfo(ArrayType *minValuesArray, ArrayType *maxValuesArray,\n\t\t\t\t\t\t  char partitionMethod, Var *partitionColumn)\n{\n\tDatum *minValues = 0;\n\tDatum *maxValues = 0;\n\tbool *minValueNulls = 0;\n\tbool *maxValueNulls = 0;\n\tint minValuesCount = 0;\n\tint maxValuesCount = 0;\n\tOid intervalTypeId = InvalidOid;\n\tint32 intervalTypeMod = 0;\n\tdeconstruct_array(minValuesArray, TEXTOID, -1, false, 'i', &minValues,\n\t\t\t\t\t  &minValueNulls, &minValuesCount);\n\tdeconstruct_array(maxValuesArray, TEXTOID, -1, false, 'i', &maxValues,\n\t\t\t\t\t  &maxValueNulls, &maxValuesCount);\n\tint partitionCount = minValuesCount;\n\tAssert(maxValuesCount == partitionCount);\n\n\tGetIntervalTypeInfo(partitionMethod, partitionColumn,\n\t\t\t\t\t\t&intervalTypeId, &intervalTypeMod);\n\tFmgrInfo *shardColumnCompare = GetFunctionInfo(partitionColumn->vartype,\n\t\t\t\t\t\t\t\t\t\t\t\t   BTREE_AM_OID, BTORDER_PROC);\n\tFmgrInfo *shardIntervalCompare = GetFunctionInfo(intervalTypeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t BTREE_AM_OID, BTORDER_PROC);\n\tFmgrInfo *hashFunction = NULL;\n\tif (partitionMethod == DISTRIBUTE_BY_HASH)\n\t{\n\t\tTypeCacheEntry *typeEntry = lookup_type_cache(partitionColumn->vartype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  TYPECACHE_HASH_PROC_FINFO);\n\n\t\thashFunction = palloc0(sizeof(FmgrInfo));\n\t\tfmgr_info_copy(hashFunction, &(typeEntry->hash_proc_finfo), CurrentMemoryContext);\n\n\t\tif (!OidIsValid(hashFunction->fn_oid))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"no hash function defined for type %s\",\n\t\t\t\t\t\t\t\t   format_type_be(partitionColumn->vartype))));\n\t\t}\n\t}\n\n\tShardInterval **shardIntervalArray = palloc0(partitionCount *\n\t\t\t\t\t\t\t\t\t\t\t\t sizeof(ShardInterval *));\n\tfor (int partitionIndex = 0; partitionIndex < partitionCount; partitionIndex++)\n\t{\n\t\tDatum datumArray[Natts_pg_dist_shard] = {\n\t\t\t[Anum_pg_dist_shard_logicalrelid - 1] = InvalidOid,\n\t\t\t[Anum_pg_dist_shard_shardid - 1] = partitionIndex,\n\t\t\t[Anum_pg_dist_shard_shardstorage - 1] = SHARD_STORAGE_VIRTUAL,\n\t\t\t[Anum_pg_dist_shard_shardminvalue - 1] = minValues[partitionIndex],\n\t\t\t[Anum_pg_dist_shard_shardmaxvalue - 1] = maxValues[partitionIndex]\n\t\t};\n\t\tbool nullsArray[Natts_pg_dist_shard] = {\n\t\t\t[Anum_pg_dist_shard_shardminvalue - 1] = minValueNulls[partitionIndex],\n\t\t\t[Anum_pg_dist_shard_shardmaxvalue - 1] = maxValueNulls[partitionIndex]\n\t\t};\n\n\t\tshardIntervalArray[partitionIndex] =\n\t\t\tDeformedDistShardTupleToShardInterval(datumArray, nullsArray,\n\t\t\t\t\t\t\t\t\t\t\t\t  intervalTypeId, intervalTypeMod);\n\t\tshardIntervalArray[partitionIndex]->shardIndex = partitionIndex;\n\t}\n\n\tCitusTableCacheEntry *result = palloc0(sizeof(CitusTableCacheEntry));\n\tresult->partitionMethod = partitionMethod;\n\tresult->partitionColumn = partitionColumn;\n\tresult->shardIntervalCompareFunction = shardIntervalCompare;\n\tresult->shardColumnCompareFunction = shardColumnCompare;\n\tresult->hashFunction = hashFunction;\n\tresult->sortedShardIntervalArray =\n\t\tSortShardIntervalArray(shardIntervalArray, partitionCount,\n\t\t\t\t\t\t\t   partitionColumn->varcollid, shardIntervalCompare);\n\tresult->hasUninitializedShardInterval =\n\t\tHasUninitializedShardInterval(result->sortedShardIntervalArray, partitionCount);\n\tresult->hasOverlappingShardInterval =\n\t\tresult->hasUninitializedShardInterval ||\n\t\tHasOverlappingShardInterval(result->sortedShardIntervalArray, partitionCount,\n\t\t\t\t\t\t\t\t\tpartitionColumn->varcollid, shardIntervalCompare);\n\tErrorIfInconsistentShardIntervals(result);\n\n\tresult->shardIntervalArrayLength = partitionCount;\n\n\treturn result;\n}\n\n\n/*\n * CreatePartitionedResultDestReceiver sets up a partitioned dest receiver.\n */\nDestReceiver *\nCreatePartitionedResultDestReceiver(int partitionColumnIndex,\n\t\t\t\t\t\t\t\t\tint partitionCount,\n\t\t\t\t\t\t\t\t\tCitusTableCacheEntry *shardSearchInfo,\n\t\t\t\t\t\t\t\t\tDestReceiver **partitionedDestReceivers,\n\t\t\t\t\t\t\t\t\tbool lazyStartup,\n\t\t\t\t\t\t\t\t\tbool allowNullPartitionColumnValues)\n{\n\tPartitionedResultDestReceiver *resultDest =\n\t\tpalloc0(sizeof(PartitionedResultDestReceiver));\n\n\t/* set up the DestReceiver function pointers */\n\tresultDest->pub.receiveSlot = PartitionedResultDestReceiverReceive;\n\tresultDest->pub.rStartup = PartitionedResultDestReceiverStartup;\n\tresultDest->pub.rShutdown = PartitionedResultDestReceiverShutdown;\n\tresultDest->pub.rDestroy = PartitionedResultDestReceiverDestroy;\n\tresultDest->pub.mydest = DestCopyOut;\n\n\t/* setup routing parameters */\n\tresultDest->partitionColumnIndex = partitionColumnIndex;\n\tresultDest->partitionCount = partitionCount;\n\tresultDest->shardSearchInfo = shardSearchInfo;\n\tresultDest->partitionDestReceivers = partitionedDestReceivers;\n\tresultDest->startedDestReceivers = NULL;\n\tresultDest->lazyStartup = lazyStartup;\n\tresultDest->allowNullPartitionColumnValues = allowNullPartitionColumnValues;\n\n\treturn (DestReceiver *) resultDest;\n}\n\n\n/*\n * PartitionedResultDestReceiverStartup implements the rStartup interface of\n * PartitionedResultDestReceiver.\n */\nstatic void\nPartitionedResultDestReceiverStartup(DestReceiver *dest, int operation,\n\t\t\t\t\t\t\t\t\t TupleDesc inputTupleDescriptor)\n{\n\tPartitionedResultDestReceiver *self = (PartitionedResultDestReceiver *) dest;\n\n\tself->startupArguments.tupleDescriptor = CreateTupleDescCopy(inputTupleDescriptor);\n\tself->startupArguments.operation = operation;\n\n\tif (self->lazyStartup)\n\t{\n\t\t/* we are initialized, rest happens when needed*/\n\t\treturn;\n\t}\n\n\t/* no lazy startup, lets startup our partitionedDestReceivers */\n\tfor (int partitionIndex = 0; partitionIndex < self->partitionCount; partitionIndex++)\n\t{\n\t\tDestReceiver *partitionDest = self->partitionDestReceivers[partitionIndex];\n\t\tpartitionDest->rStartup(partitionDest, operation, inputTupleDescriptor);\n\t\tself->startedDestReceivers = bms_add_member(self->startedDestReceivers,\n\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionIndex);\n\t}\n}\n\n\n/*\n * PartitionedResultDestReceiverReceive implements the receiveSlot interface of\n * PartitionedResultDestReceiver.\n */\nstatic bool\nPartitionedResultDestReceiverReceive(TupleTableSlot *slot, DestReceiver *dest)\n{\n\tPartitionedResultDestReceiver *self = (PartitionedResultDestReceiver *) dest;\n\n\tslot_getallattrs(slot);\n\n\tDatum *columnValues = slot->tts_values;\n\tbool *columnNulls = slot->tts_isnull;\n\n\tint partitionIndex;\n\n\tif (columnNulls[self->partitionColumnIndex])\n\t{\n\t\tif (self->allowNullPartitionColumnValues)\n\t\t{\n\t\t\t/*\n\t\t\t * NULL values go into the first partition for both hash- and range-\n\t\t\t * partitioning, since that is the only way to guarantee that there is\n\t\t\t * always a partition for NULL and that it is always the same partition.\n\t\t\t */\n\t\t\tpartitionIndex = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),\n\t\t\t\t\t\t\terrmsg(\"the partition column value cannot be NULL\")));\n\t\t}\n\t}\n\telse\n\t{\n\t\tDatum partitionColumnValue = columnValues[self->partitionColumnIndex];\n\t\tShardInterval *shardInterval = FindShardInterval(partitionColumnValue,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t self->shardSearchInfo);\n\t\tif (shardInterval == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"could not find shard for partition column \"\n\t\t\t\t\t\t\t\t   \"value\")));\n\t\t}\n\n\t\tpartitionIndex = shardInterval->shardIndex;\n\t}\n\n\tDestReceiver *partitionDest = self->partitionDestReceivers[partitionIndex];\n\n\t/* check if this partitionDestReceiver has been started before, start if not */\n\tif (!bms_is_member(partitionIndex, self->startedDestReceivers))\n\t{\n\t\tpartitionDest->rStartup(partitionDest,\n\t\t\t\t\t\t\t\tself->startupArguments.operation,\n\t\t\t\t\t\t\t\tself->startupArguments.tupleDescriptor);\n\t\tself->startedDestReceivers = bms_add_member(self->startedDestReceivers,\n\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionIndex);\n\t}\n\n\t/* forward the tuple to the appropriate dest receiver */\n\tpartitionDest->receiveSlot(slot, partitionDest);\n\n\treturn true;\n}\n\n\n/*\n * PartitionedResultDestReceiverShutdown implements the rShutdown interface of\n * PartitionedResultDestReceiver by calling rShutdown on all started\n * partitionedDestReceivers.\n */\nstatic void\nPartitionedResultDestReceiverShutdown(DestReceiver *dest)\n{\n\tPartitionedResultDestReceiver *self = (PartitionedResultDestReceiver *) dest;\n\n\tint i = -1;\n\twhile ((i = bms_next_member(self->startedDestReceivers, i)) >= 0)\n\t{\n\t\tDestReceiver *partitionDest = self->partitionDestReceivers[i];\n\t\tpartitionDest->rShutdown(partitionDest);\n\t}\n\n\t/* empty the set of started receivers which allows them to be restarted again */\n\tbms_free(self->startedDestReceivers);\n\tself->startedDestReceivers = NULL;\n}\n\n\n/*\n * PartitionedResultDestReceiverDestroy implements the rDestroy interface of\n * PartitionedResultDestReceiver.\n */\nstatic void\nPartitionedResultDestReceiverDestroy(DestReceiver *dest)\n{\n\tPartitionedResultDestReceiver *self = (PartitionedResultDestReceiver *) dest;\n\n\t/* we destroy all dest receivers, irregardless if they have been started or not */\n\tfor (int partitionIndex = 0; partitionIndex < self->partitionCount; partitionIndex++)\n\t{\n\t\tDestReceiver *partitionDest = self->partitionDestReceivers[partitionIndex];\n\t\tif (partitionDest != NULL)\n\t\t{\n\t\t\tpartitionDest->rDestroy(partitionDest);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/placement_access.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_custom_scan.c\n *\n * Definitions of the functions used in generating the placement accesses\n * for distributed query execution.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/placement_access.h\"\n\nstatic List * BuildPlacementSelectList(int32 groupId, List *relationShardList);\nstatic List * BuildPlacementDDLList(int32 groupId, List *relationShardList);\nstatic List * BuildPlacementAccessList(int32 groupId, List *relationShardList,\n\t\t\t\t\t\t\t\t\t   ShardPlacementAccessType accessType);\n\n\n/*\n * PlacementAccessListForTask returns a list of placement accesses for a given\n * task and task placement.\n */\nList *\nPlacementAccessListForTask(Task *task, ShardPlacement *taskPlacement)\n{\n\tList *placementAccessList = NIL;\n\tList *relationShardList = task->relationShardList;\n\tbool addAnchorAccess = false;\n\tShardPlacementAccessType accessType = PLACEMENT_ACCESS_SELECT;\n\n\tif (task->taskType == MODIFY_TASK)\n\t{\n\t\t/* DML command */\n\t\taddAnchorAccess = true;\n\t\taccessType = PLACEMENT_ACCESS_DML;\n\t}\n\telse if (task->taskType == DDL_TASK || task->taskType == VACUUM_ANALYZE_TASK)\n\t{\n\t\t/* DDL command */\n\t\taddAnchorAccess = true;\n\t\taccessType = PLACEMENT_ACCESS_DDL;\n\t}\n\telse if (relationShardList == NIL)\n\t{\n\t\t/* SELECT query that does not touch any shard placements */\n\t\taddAnchorAccess = true;\n\t\taccessType = PLACEMENT_ACCESS_SELECT;\n\t}\n\n\tif (addAnchorAccess)\n\t{\n\t\tShardPlacementAccess *placementAccess =\n\t\t\tCreatePlacementAccess(taskPlacement, accessType);\n\n\t\tplacementAccessList = lappend(placementAccessList, placementAccess);\n\t}\n\n\t/*\n\t * We've already added anchor shardId's placement access to the list. Now,\n\t * add the other placements in the relationShardList.\n\t */\n\tif (accessType == PLACEMENT_ACCESS_DDL)\n\t{\n\t\t/*\n\t\t * All relations appearing inter-shard DDL commands should be marked\n\t\t * with DDL access.\n\t\t */\n\t\tList *relationShardAccessList =\n\t\t\tBuildPlacementDDLList(taskPlacement->groupId, relationShardList);\n\n\t\tplacementAccessList = list_concat(placementAccessList, relationShardAccessList);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * In case of SELECTs or DML's, we add SELECT placement accesses to the\n\t\t * elements in relationShardList. For SELECT queries, it is trivial, since\n\t\t * the query is literally accesses the relationShardList in the same query.\n\t\t *\n\t\t * For DMLs, create placement accesses for placements that appear in a\n\t\t * subselect.\n\t\t */\n\t\tList *relationShardAccessList =\n\t\t\tBuildPlacementSelectList(taskPlacement->groupId, relationShardList);\n\n\t\tplacementAccessList = list_concat(placementAccessList, relationShardAccessList);\n\t}\n\n\treturn placementAccessList;\n}\n\n\n/*\n * BuildPlacementSelectList builds a list of SELECT placement accesses\n * which can be used to call StartPlacementListConnection or\n * GetPlacementListConnection. If the node group does not have a placement\n * (e.g. in case of a broadcast join) then the shard is skipped.\n */\nstatic List *\nBuildPlacementSelectList(int32 groupId, List *relationShardList)\n{\n\treturn BuildPlacementAccessList(groupId, relationShardList, PLACEMENT_ACCESS_SELECT);\n}\n\n\n/*\n * BuildPlacementDDLList is a warpper around BuildPlacementAccessList() for DDL access.\n */\nstatic List *\nBuildPlacementDDLList(int32 groupId, List *relationShardList)\n{\n\treturn BuildPlacementAccessList(groupId, relationShardList, PLACEMENT_ACCESS_DDL);\n}\n\n\n/*\n * BuildPlacementAccessList returns a list of placement accesses for the given\n * relationShardList and the access type.\n */\nstatic List *\nBuildPlacementAccessList(int32 groupId, List *relationShardList,\n\t\t\t\t\t\t ShardPlacementAccessType accessType)\n{\n\tList *placementAccessList = NIL;\n\n\tRelationShard *relationShard = NULL;\n\tforeach_declared_ptr(relationShard, relationShardList)\n\t{\n\t\tShardPlacement *placement = ActiveShardPlacementOnGroup(groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trelationShard->shardId);\n\t\tif (placement == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tShardPlacementAccess *placementAccess = CreatePlacementAccess(placement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  accessType);\n\t\tplacementAccessList = lappend(placementAccessList, placementAccess);\n\t}\n\n\treturn placementAccessList;\n}\n\n\n/*\n * CreatePlacementAccess returns a new ShardPlacementAccess for the given placement\n * and access type.\n */\nShardPlacementAccess *\nCreatePlacementAccess(ShardPlacement *placement, ShardPlacementAccessType accessType)\n{\n\tShardPlacementAccess *placementAccess = (ShardPlacementAccess *) palloc0(\n\t\tsizeof(ShardPlacementAccess));\n\tplacementAccess->placement = placement;\n\tplacementAccess->accessType = accessType;\n\n\treturn placementAccess;\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/repartition_executor.c",
    "content": "/*-------------------------------------------------------------------\n *\n * repartition_executor.c\n *\n * Definitions for public functions and types related to repartition\n * of select query results.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"nodes/makefuncs.h\"\n#include \"nodes/parsenodes.h\"\n\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/repartition_executor.h\"\n#include \"distributed/resource_lock.h\"\n\n\n/*\n * IsSupportedRedistributionTarget determines whether re-partitioning into the\n * given target relation is supported.\n */\nbool\nIsSupportedRedistributionTarget(Oid targetRelationId)\n{\n\tCitusTableCacheEntry *tableEntry = GetCitusTableCacheEntry(targetRelationId);\n\n\tif (!IsCitusTableTypeCacheEntry(tableEntry, HASH_DISTRIBUTED) &&\n\t\t!IsCitusTableTypeCacheEntry(tableEntry, RANGE_DISTRIBUTED))\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * IsRedistributablePlan returns true if the given plan is a distributable plan.\n */\nbool\nIsRedistributablePlan(Plan *selectPlan)\n{\n\tif (!EnableRepartitionedInsertSelect)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * Don't redistribute if query is not distributed or requires\n\t * merge on coordinator\n\t */\n\tif (!IsCitusCustomScan(selectPlan))\n\t{\n\t\treturn false;\n\t}\n\n\tDistributedPlan *distSelectPlan =\n\t\tGetDistributedPlan((CustomScan *) selectPlan);\n\tJob *distSelectJob = distSelectPlan->workerJob;\n\tList *distSelectTaskList = distSelectJob->taskList;\n\n\t/*\n\t * Don't use redistribution if only one task. This is to keep the existing\n\t * behaviour for CTEs that the last step is a read_intermediate_result()\n\t * call. It doesn't hurt much in other cases too.\n\t */\n\tif (list_length(distSelectTaskList) <= 1)\n\t{\n\t\treturn false;\n\t}\n\n\t/* don't use redistribution for repartition joins for now */\n\tif (distSelectJob->dependentJobList != NIL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (distSelectPlan->combineQuery != NULL)\n\t{\n\t\tQuery *combineQuery = (Query *) distSelectPlan->combineQuery;\n\n\t\tif (contain_nextval_expression_walker((Node *) combineQuery->targetList, NULL))\n\t\t{\n\t\t\t/* nextval needs to be evaluated on the coordinator */\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * HasMergeNotMatchedBySource returns true if the MERGE query has a\n * WHEN NOT MATCHED BY SOURCE clause. If it does, we need to execute\n * the MERGE query on all shards of the target table, regardless of\n * whether or not the source shard has any rows.\n */\nbool\nHasMergeNotMatchedBySource(Query *query)\n{\n\tif (!IsMergeQuery(query))\n\t{\n\t\treturn false;\n\t}\n\n\tbool haveNotMatchedBySource = false;\n\n\t#if PG_VERSION_NUM >= PG_VERSION_17\n\tListCell *lc;\n\tforeach(lc, query->mergeActionList)\n\t{\n\t\tMergeAction *action = lfirst_node(MergeAction, lc);\n\n\t\tif (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)\n\t\t{\n\t\t\thaveNotMatchedBySource = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\t#endif\n\n\treturn haveNotMatchedBySource;\n}\n\n\n/*\n * GenerateTaskListWithColocatedIntermediateResults generates a list of tasks\n * for a query that inserts into a target relation and selects from a set of\n * co-located intermediate results.\n */\nList *\nGenerateTaskListWithColocatedIntermediateResults(Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t Query *\n\t\t\t\t\t\t\t\t\t\t\t\t modifyQueryViaCoordinatorOrRepartition,\n\t\t\t\t\t\t\t\t\t\t\t\t char *resultIdPrefix)\n{\n\tList *taskList = NIL;\n\n\t/*\n\t * Make a copy of the <MODIFY-SQL> ... SELECT. We'll repeatedly replace\n\t * the subquery of modifyResultQuery for different intermediate results and\n\t * then deparse it.\n\t */\n\tQuery *modifyWithResultQuery = copyObject(modifyQueryViaCoordinatorOrRepartition);\n\tRangeTblEntry *insertRte = ExtractResultRelationRTE(modifyWithResultQuery);\n\tRangeTblEntry *selectRte = ExtractSourceResultRangeTableEntry(modifyWithResultQuery);\n\n\tCitusTableCacheEntry *targetCacheEntry = GetCitusTableCacheEntry(targetRelationId);\n\tint shardCount = targetCacheEntry->shardIntervalArrayLength;\n\tuint32 taskIdIndex = 1;\n\tuint64 jobId = INVALID_JOB_ID;\n\n\tfor (int shardOffset = 0; shardOffset < shardCount; shardOffset++)\n\t{\n\t\tShardInterval *targetShardInterval =\n\t\t\ttargetCacheEntry->sortedShardIntervalArray[shardOffset];\n\t\tuint64 shardId = targetShardInterval->shardId;\n\t\tList *columnAliasList = NIL;\n\t\tStringInfo queryString = makeStringInfo();\n\t\tStringInfo resultId = makeStringInfo();\n\n\t\t/* during COPY, the shard ID is appended to the result name */\n\t\tappendStringInfo(resultId, \"%s_\" UINT64_FORMAT, resultIdPrefix, shardId);\n\n\t\t/*\n\t\t * For MERGE SQL, use the USING clause list, the main query target list\n\t\t * is NULL\n\t\t */\n\t\tList *targetList = IsMergeQuery(modifyQueryViaCoordinatorOrRepartition) ?\n\t\t\t\t\t\t   selectRte->subquery->targetList :\n\t\t\t\t\t\t   modifyQueryViaCoordinatorOrRepartition->targetList;\n\n\t\t/* generate the query on the intermediate result */\n\t\tQuery *resultSelectQuery = BuildSubPlanResultQuery(targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   columnAliasList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   resultId->data);\n\n\t\t/* put the intermediate result query in the INSERT..SELECT */\n\t\tselectRte->subquery = resultSelectQuery;\n\n\t\t/* setting an alias simplifies deparsing of RETURNING */\n\t\tif (insertRte->alias == NULL)\n\t\t{\n\t\t\tAlias *alias = makeAlias(CITUS_TABLE_ALIAS, NIL);\n\t\t\tinsertRte->alias = alias;\n\t\t}\n\n\t\t/*\n\t\t * Generate a query string for the query that inserts into a shard and reads\n\t\t * from an intermediate result.\n\t\t *\n\t\t * Since CTEs have already been converted to intermediate results, they need\n\t\t * to removed from the query. Otherwise, worker queries include both\n\t\t * intermediate results and CTEs in the query.\n\t\t */\n\t\tmodifyWithResultQuery->cteList = NIL;\n\t\tdeparse_shard_query(modifyWithResultQuery, targetRelationId, shardId,\n\t\t\t\t\t\t\tqueryString);\n\t\tereport(DEBUG2, (errmsg(\"distributed statement: %s\", queryString->data)));\n\n\t\tLockShardDistributionMetadata(shardId, ShareLock);\n\t\tList *insertShardPlacementList = ActiveShardPlacementList(shardId);\n\n\t\tRelationShard *relationShard = CitusMakeNode(RelationShard);\n\t\trelationShard->relationId = targetShardInterval->relationId;\n\t\trelationShard->shardId = targetShardInterval->shardId;\n\n\t\tTask *modifyTask = CreateBasicTask(jobId, taskIdIndex, MODIFY_TASK,\n\t\t\t\t\t\t\t\t\t\t   queryString->data);\n\t\tmodifyTask->dependentTaskList = NIL;\n\t\tmodifyTask->anchorShardId = shardId;\n\t\tmodifyTask->taskPlacementList = insertShardPlacementList;\n\t\tmodifyTask->relationShardList = list_make1(relationShard);\n\t\tmodifyTask->replicationModel = targetCacheEntry->replicationModel;\n\n\t\ttaskList = lappend(taskList, modifyTask);\n\n\t\ttaskIdIndex++;\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * AdjustTaskQueryForEmptySource adjusts the query for tasks that read from an\n * intermediate result to instead read from an empty relation. This ensures that\n * the MERGE query is executed on all shards of the target table, because it has\n * a NOT MATCHED BY SOURCE clause, which will be true for all target shards where\n * the source shard has no rows.\n */\nvoid\nAdjustTaskQueryForEmptySource(Oid targetRelationId,\n\t\t\t\t\t\t\t  Query *mergeQuery,\n\t\t\t\t\t\t\t  List *tasks,\n\t\t\t\t\t\t\t  char *resultIdPrefix)\n{\n\tQuery *mergeQueryCopy = copyObject(mergeQuery);\n\tRangeTblEntry *selectRte = ExtractSourceResultRangeTableEntry(mergeQueryCopy);\n\tRangeTblEntry *mergeRte = ExtractResultRelationRTE(mergeQueryCopy);\n\tList *targetList = selectRte->subquery->targetList;\n\tListCell *taskCell = NULL;\n\n\tforeach(taskCell, tasks)\n\t{\n\t\tTask *task = lfirst(taskCell);\n\t\tuint64 shardId = task->anchorShardId;\n\t\tStringInfo queryString = makeStringInfo();\n\t\tStringInfo resultId = makeStringInfo();\n\n\t\tappendStringInfo(resultId, \"%s_\" UINT64_FORMAT, resultIdPrefix, shardId);\n\n\t\t/* Generate a query for an empty relation */\n\t\tselectRte->subquery = BuildEmptyResultQuery(targetList, resultId->data);\n\n\t\t/* setting an alias simplifies deparsing of RETURNING */\n\t\tif (mergeRte->alias == NULL)\n\t\t{\n\t\t\tAlias *alias = makeAlias(CITUS_TABLE_ALIAS, NIL);\n\t\t\tmergeRte->alias = alias;\n\t\t}\n\n\t\t/*\n\t\t * Generate a query string for the query that merges into a shard and reads\n\t\t * from an empty relation.\n\t\t *\n\t\t * Since CTEs have already been converted to intermediate results, they need\n\t\t * to removed from the query. Otherwise, worker queries include both\n\t\t * intermediate results and CTEs in the query.\n\t\t */\n\t\tmergeQueryCopy->cteList = NIL;\n\t\tdeparse_shard_query(mergeQueryCopy, targetRelationId, shardId, queryString);\n\t\tereport(DEBUG2, (errmsg(\"distributed statement: %s\", queryString->data)));\n\n\t\tSetTaskQueryString(task, queryString->data);\n\t}\n}\n\n\n/*\n * GenerateTaskListWithRedistributedResults returns a task list to insert given\n * redistributedResults into the given target relation.\n * redistributedResults[shardIndex] is list of cstrings each of which is\n * a result name which should be inserted into\n * targetRelation->sortedShardIntervalArray[shardIndex].\n */\nList *\nGenerateTaskListWithRedistributedResults(Query *modifyQueryViaCoordinatorOrRepartition,\n\t\t\t\t\t\t\t\t\t\t CitusTableCacheEntry *targetRelation,\n\t\t\t\t\t\t\t\t\t\t List **redistributedResults, bool\n\t\t\t\t\t\t\t\t\t\t useBinaryFormat)\n{\n\tList *taskList = NIL;\n\n\t/*\n\t * Make a copy of the <MODIFY-SQL> ... SELECT. We'll repeatedly replace\n\t * the subquery of modifyResultQuery for different intermediate results and\n\t * then deparse it.\n\t */\n\tQuery *modifyResultQuery = copyObject(modifyQueryViaCoordinatorOrRepartition);\n\tRangeTblEntry *insertRte = ExtractResultRelationRTE(modifyResultQuery);\n\tOid targetRelationId = targetRelation->relationId;\n\tbool hasNotMatchedBySource = HasMergeNotMatchedBySource(modifyResultQuery);\n\n\tint shardCount = targetRelation->shardIntervalArrayLength;\n\tint shardOffset = 0;\n\tuint32 taskIdIndex = 1;\n\tuint64 jobId = INVALID_JOB_ID;\n\n\tRangeTblEntry *selectRte =\n\t\tExtractSourceResultRangeTableEntry(modifyResultQuery);\n\tList *selectTargetList = selectRte->subquery->targetList;\n\n\tfor (shardOffset = 0; shardOffset < shardCount; shardOffset++)\n\t{\n\t\tShardInterval *targetShardInterval =\n\t\t\ttargetRelation->sortedShardIntervalArray[shardOffset];\n\t\tList *resultIdList = redistributedResults[targetShardInterval->shardIndex];\n\t\tuint64 shardId = targetShardInterval->shardId;\n\t\tStringInfo queryString = makeStringInfo();\n\n\t\t/* skip empty tasks */\n\t\tif (resultIdList == NIL && !hasNotMatchedBySource)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tQuery *fragmentSetQuery = NULL;\n\n\t\tif (resultIdList != NIL)\n\t\t{\n\t\t\t/* sort result ids for consistent test output */\n\t\t\tList *sortedResultIds = SortList(resultIdList, pg_qsort_strcmp);\n\n\t\t\t/* generate the query on the intermediate result */\n\t\t\tfragmentSetQuery = BuildReadIntermediateResultsArrayQuery(selectTargetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  NIL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  sortedResultIds,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  useBinaryFormat);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* No source data, but MERGE query has NOT MATCHED BY SOURCE */\n\t\t\tStringInfo emptyFragmentId = makeStringInfo();\n\t\t\tappendStringInfo(emptyFragmentId, \"%s_\" UINT64_FORMAT, \"temp_empty_rel_\",\n\t\t\t\t\t\t\t shardId);\n\t\t\tfragmentSetQuery = BuildEmptyResultQuery(selectTargetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t emptyFragmentId->data);\n\t\t}\n\n\t\t/* put the intermediate result query in the INSERT..SELECT */\n\t\tselectRte->subquery = fragmentSetQuery;\n\n\t\t/* setting an alias simplifies deparsing of RETURNING */\n\t\tif (insertRte->alias == NULL)\n\t\t{\n\t\t\tAlias *alias = makeAlias(CITUS_TABLE_ALIAS, NIL);\n\t\t\tinsertRte->alias = alias;\n\t\t}\n\n\t\t/*\n\t\t * Generate a query string for the query that inserts into a shard and reads\n\t\t * from an intermediate result.\n\t\t *\n\t\t * Since CTEs have already been converted to intermediate results, they need\n\t\t * to removed from the query. Otherwise, worker queries include both\n\t\t * intermediate results and CTEs in the query.\n\t\t */\n\t\tmodifyResultQuery->cteList = NIL;\n\t\tdeparse_shard_query(modifyResultQuery, targetRelationId, shardId, queryString);\n\t\tereport(DEBUG2, (errmsg(\"distributed statement: %s\", queryString->data)));\n\n\t\tLockShardDistributionMetadata(shardId, ShareLock);\n\t\tList *insertShardPlacementList = ActiveShardPlacementList(shardId);\n\n\t\tRelationShard *relationShard = CitusMakeNode(RelationShard);\n\t\trelationShard->relationId = targetShardInterval->relationId;\n\t\trelationShard->shardId = targetShardInterval->shardId;\n\n\t\tTask *modifyTask = CreateBasicTask(jobId, taskIdIndex, MODIFY_TASK,\n\t\t\t\t\t\t\t\t\t\t   queryString->data);\n\t\tmodifyTask->dependentTaskList = NIL;\n\t\tmodifyTask->anchorShardId = shardId;\n\t\tmodifyTask->taskPlacementList = insertShardPlacementList;\n\t\tmodifyTask->relationShardList = list_make1(relationShard);\n\t\tmodifyTask->replicationModel = targetRelation->replicationModel;\n\n\t\ttaskList = lappend(taskList, modifyTask);\n\n\t\ttaskIdIndex++;\n\t}\n\n\treturn taskList;\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/repartition_join_execution.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * repartition_join_execution.c\n *\n * This file contains repartition specific logic.\n * ExecuteDependentTasks takes a list of top level tasks. Its logic is as follows:\n * - It generates all the tasks by descending in the tasks tree. Note that each task\n *  has a dependentTaskList.\n * - It generates FetchTask queryStrings with the MapTask queries. It uses the first replicate to\n *  fetch data when replication factor is > 1. Note that if a task fails in any replica adaptive executor\n *  gives an error, so if we come to a fetchTask we know for sure that its dependedMapTask is executed in all\n *  replicas.\n * - It creates schemas in each worker in a single transaction to store intermediate results.\n * - It iterates all tasks and finds the ones whose dependencies are already executed, and executes them with\n *  adaptive executor logic.\n *\n *\n * Repartition queries do not begin a transaction even if we are in\n * a transaction block. As we don't begin a transaction, they won't see the\n * DDLs that happened earlier in the transaction because we don't have that\n * transaction id with repartition queries. Therefore we error in this case.\n *\n * Copyright (c) Citus Data, Inc.\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/hash.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/directed_acyclic_graph_execution.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/repartition_join_execution.h\"\n#include \"distributed/task_execution_utils.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/transmit.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_transaction.h\"\n\n\nstatic List * ExtractJobsInJobTree(Job *job);\nstatic void TraverseJobTree(Job *curJob, List **jobs);\n\n\n/*\n * ExecuteDependentTasks executes all tasks except the top level tasks\n * in order from the task tree. At a time, it can execute different tasks from\n * different jobs.\n */\nList *\nExecuteDependentTasks(List *topLevelTasks, Job *topLevelJob)\n{\n\tList *allTasks = CreateTaskListForJobTree(topLevelTasks);\n\tList *jobIds = ExtractJobsInJobTree(topLevelJob);\n\n\tExecuteTasksInDependencyOrder(allTasks, topLevelTasks, jobIds);\n\n\treturn jobIds;\n}\n\n\n/*\n * ExtractJobsInJobTree returns all job ids in the job tree\n * where the given job is root.\n */\nstatic List *\nExtractJobsInJobTree(Job *job)\n{\n\tList *jobIds = NIL;\n\tTraverseJobTree(job, &jobIds);\n\treturn jobIds;\n}\n\n\n/*\n * TraverseJobTree does a dfs in the current job and adds\n * all of its job ids.\n */\nstatic void\nTraverseJobTree(Job *curJob, List **jobIds)\n{\n\tuint64 *jobIdPointer = palloc(sizeof(uint64));\n\t*jobIdPointer = curJob->jobId;\n\n\t*jobIds = lappend(*jobIds, jobIdPointer);\n\n\tJob *childJob = NULL;\n\tforeach_declared_ptr(childJob, curJob->dependentJobList)\n\t{\n\t\tTraverseJobTree(childJob, jobIds);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/subplan_execution.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * subplan_execution.c\n *\n * Functions for execution subplans prior to distributed table execution.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"executor/executor.h\"\n#include \"utils/datetime.h\"\n\n#include \"distributed/intermediate_result_pruning.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/subplan_execution.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/worker_manager.h\"\n\n#define SECOND_TO_MILLI_SECOND 1000\n#define MICRO_TO_MILLI_SECOND 0.001\n\nint MaxIntermediateResult = 1048576; /* maximum size in KB the intermediate result can grow to */\n/* when this is true, we enforce intermediate result size limit in all executors */\nint SubPlanLevel = 0;\n\n/*\n * SubPlanExplainAnalyzeContext is both a memory context for storing\n * subplans’ EXPLAIN ANALYZE output and a flag indicating that execution\n * is running under EXPLAIN ANALYZE for subplans.\n */\nMemoryContext SubPlanExplainAnalyzeContext = NULL;\nSubPlanExplainOutputData *SubPlanExplainOutput;\nextern uint8 TotalExplainOutputCapacity;\nextern uint8 NumTasksOutput;\n\n/*\n * ExecuteSubPlans executes a list of subplans from a distributed plan\n * by sequentially executing each plan from the top.\n */\nvoid\nExecuteSubPlans(DistributedPlan *distributedPlan, bool explainAnalyzeEnabled)\n{\n\tuint64 planId = distributedPlan->planId;\n\tList *subPlanList = distributedPlan->subPlanList;\n\n\tif (subPlanList == NIL)\n\t{\n\t\t/* no subplans to execute */\n\t\treturn;\n\t}\n\n\t/*\n\t * If the root DistributedPlan has EXPLAIN ANALYZE enabled,\n\t * its subplans should also have EXPLAIN ANALYZE enabled.\n\t */\n\tif (explainAnalyzeEnabled)\n\t{\n\t\tSubPlanExplainAnalyzeContext = GetMemoryChunkContext(distributedPlan);\n\t}\n\telse\n\t{\n\t\tSubPlanExplainAnalyzeContext = NULL;\n\t}\n\n\tHTAB *intermediateResultsHash = MakeIntermediateResultHTAB();\n\tRecordSubplanExecutionsOnNodes(intermediateResultsHash, distributedPlan);\n\n\t/*\n\t * Make sure that this transaction has a distributed transaction ID.\n\t *\n\t * Intermediate results of subplans will be stored in a directory that is\n\t * derived from the distributed transaction ID.\n\t */\n\tUseCoordinatedTransaction();\n\n\tDistributedSubPlan *subPlan = NULL;\n\tforeach_declared_ptr(subPlan, subPlanList)\n\t{\n\t\tPlannedStmt *plannedStmt = subPlan->plan;\n\t\tuint32 subPlanId = subPlan->subPlanId;\n\t\tParamListInfo params = NULL;\n\t\tchar *resultId = GenerateResultId(planId, subPlanId);\n\t\tList *remoteWorkerNodeList =\n\t\t\tFindAllWorkerNodesUsingSubplan(intermediateResultsHash, resultId);\n\n\t\tIntermediateResultsHashEntry *entry =\n\t\t\tSearchIntermediateResult(intermediateResultsHash, resultId);\n\n\t\tSubPlanLevel++;\n\t\tEState *estate = CreateExecutorState();\n\t\tDestReceiver *copyDest =\n\t\t\tCreateRemoteFileDestReceiver(resultId, estate, remoteWorkerNodeList,\n\t\t\t\t\t\t\t\t\t\t entry->writeLocalFile);\n\n\t\tTimestampTz startTimestamp = GetCurrentTimestamp();\n\n\t\tuint64 nprocessed;\n\n\t\tPG_TRY();\n\t\t{\n\t\t\tnprocessed =\n\t\t\t\tExecutePlanIntoDestReceiver(plannedStmt, params, copyDest);\n\t\t}\n\t\tPG_CATCH();\n\t\t{\n\t\t\tSubPlanExplainAnalyzeContext = NULL;\n\t\t\tSubPlanExplainOutput = NULL;\n\t\t\tTotalExplainOutputCapacity = 0;\n\t\t\tNumTasksOutput = 0;\n\t\t\tPG_RE_THROW();\n\t\t}\n\t\tPG_END_TRY();\n\n\n\t\t/*\n\t\t * EXPLAIN ANALYZE instrumentations. Calculating these are very light-weight,\n\t\t * so always populate them regardless of EXPLAIN ANALYZE or not.\n\t\t */\n\t\tlong durationSeconds = 0.0;\n\t\tint durationMicrosecs = 0;\n\t\tTimestampDifference(startTimestamp, GetCurrentTimestamp(), &durationSeconds,\n\t\t\t\t\t\t\t&durationMicrosecs);\n\n\t\tsubPlan->durationMillisecs = durationSeconds * SECOND_TO_MILLI_SECOND;\n\t\tsubPlan->durationMillisecs += durationMicrosecs * MICRO_TO_MILLI_SECOND;\n\n\t\tsubPlan->bytesSentPerWorker = RemoteFileDestReceiverBytesSent(copyDest);\n\t\tsubPlan->ntuples = nprocessed;\n\t\tsubPlan->remoteWorkerCount = list_length(remoteWorkerNodeList);\n\t\tsubPlan->writeLocalFile = entry->writeLocalFile;\n\n\t\tSubPlanLevel--;\n\n\t\t/*\n\t\t * Save the EXPLAIN ANALYZE output(s) for later extraction in ExplainSubPlans().\n\t\t * Because the SubPlan context isn’t available during distributed execution,\n\t\t * pass the pointer as a global variable in SubPlanExplainOutput.\n\t\t */\n\t\tsubPlan->totalExplainOutput = SubPlanExplainOutput;\n\t\tsubPlan->numTasksOutput = NumTasksOutput;\n\t\tSubPlanExplainOutput = NULL;\n\t\tTotalExplainOutputCapacity = 0;\n\t\tNumTasksOutput = 0;\n\t\tFreeExecutorState(estate);\n\t}\n\n\tSubPlanExplainAnalyzeContext = NULL;\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/transmit.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * transmit.c\n *\t  Routines for transmitting regular files between two nodes.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"commands/defrem.h\"\n#include \"common/file_perm.h\"\n#include \"libpq/libpq.h\"\n#include \"libpq/pqformat.h\"\n#include \"storage/fd.h\"\n\n#include \"distributed/listutils.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/transmit.h\"\n#include \"distributed/utils/directory.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/* Local functions forward declarations */\nstatic void SendCopyInStart(void);\nstatic void SendCopyOutStart(void);\nstatic void SendCopyDone(void);\nstatic void SendCopyData(StringInfo fileBuffer);\nstatic bool ReceiveCopyData(StringInfo copyData);\nstatic void FreeStringInfo(StringInfo stringInfo);\n\n\n/*\n * RedirectCopyDataToRegularFile receives data from stdin using the standard copy\n * protocol. The function then creates or truncates a file with the given\n * filename, and appends received data to this file.\n */\nvoid\nRedirectCopyDataToRegularFile(const char *filename)\n{\n\tStringInfo copyData = makeStringInfo();\n\tconst int fileFlags = (O_APPEND | O_CREAT | O_RDWR | O_TRUNC | PG_BINARY);\n\tFile fileDesc = FileOpenForTransmit(filename, fileFlags);\n\tFileCompat fileCompat = FileCompatFromFileStart(fileDesc);\n\n\tSendCopyInStart();\n\n\tbool copyDone = ReceiveCopyData(copyData);\n\twhile (!copyDone)\n\t{\n\t\t/* if received data has contents, append to regular file */\n\t\tif (copyData->len > 0)\n\t\t{\n\t\t\tint appended = FileWriteCompat(&fileCompat, copyData->data,\n\t\t\t\t\t\t\t\t\t\t   copyData->len, PG_WAIT_IO);\n\n\t\t\tif (appended != copyData->len)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode_for_file_access(),\n\t\t\t\t\t\t\t\terrmsg(\"could not append to received file: %m\")));\n\t\t\t}\n\t\t}\n\n\t\tresetStringInfo(copyData);\n\t\tcopyDone = ReceiveCopyData(copyData);\n\t}\n\n\tFreeStringInfo(copyData);\n\tFileClose(fileDesc);\n}\n\n\n/*\n * SendRegularFile reads data from the given file, and sends these data to\n * stdout using the standard copy protocol. After all file data are sent, the\n * function ends the copy protocol and closes the file.\n */\nvoid\nSendRegularFile(const char *filename)\n{\n\tconst uint32 fileBufferSize = 32768; /* 32 KB */\n\tconst int fileFlags = (O_RDONLY | PG_BINARY);\n\tconst int fileMode = 0;\n\n\t/* we currently do not check if the caller has permissions for this file */\n\tFile fileDesc = FileOpenForTransmitPerm(filename, fileFlags, fileMode);\n\tFileCompat fileCompat = FileCompatFromFileStart(fileDesc);\n\n\t/*\n\t * We read file's contents into buffers of 32 KB. This buffer size is twice\n\t * as large as Hadoop's default buffer size, and may later be configurable.\n\t */\n\tStringInfo fileBuffer = makeStringInfo();\n\tenlargeStringInfo(fileBuffer, fileBufferSize);\n\n\tSendCopyOutStart();\n\n\tint readBytes = FileReadCompat(&fileCompat, fileBuffer->data, fileBufferSize,\n\t\t\t\t\t\t\t\t   PG_WAIT_IO);\n\twhile (readBytes > 0)\n\t{\n\t\tfileBuffer->len = readBytes;\n\n\t\tSendCopyData(fileBuffer);\n\n\t\tresetStringInfo(fileBuffer);\n\t\treadBytes = FileReadCompat(&fileCompat, fileBuffer->data, fileBufferSize,\n\t\t\t\t\t\t\t\t   PG_WAIT_IO);\n\t}\n\n\tSendCopyDone();\n\n\tFreeStringInfo(fileBuffer);\n\tFileClose(fileDesc);\n}\n\n\n/* Helper function that deallocates string info object. */\nstatic void\nFreeStringInfo(StringInfo stringInfo)\n{\n\tresetStringInfo(stringInfo);\n\n\tpfree(stringInfo->data);\n\tpfree(stringInfo);\n}\n\n\n/*\n * Open a file with FileOpenForTransmitPerm() and pass default file mode for\n * the fileMode parameter.\n */\nFile\nFileOpenForTransmit(const char *filename, int fileFlags)\n{\n\treturn FileOpenForTransmitPerm(filename, fileFlags, pg_file_create_mode);\n}\n\n\n/*\n * FileOpenForTransmitPerm opens file with the given filename and flags. On\n * success, the function returns the internal file handle for the opened file.\n * On failure the function errors out.\n */\nFile\nFileOpenForTransmitPerm(const char *filename, int fileFlags, int fileMode)\n{\n\tstruct stat fileStat;\n\n\tint statOK = stat(filename, &fileStat);\n\tif (statOK >= 0)\n\t{\n\t\tif (S_ISDIR(fileStat.st_mode))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),\n\t\t\t\t\t\t\terrmsg(\"\\\"%s\\\" is a directory\", filename)));\n\t\t}\n\t}\n\n\tFile fileDesc = PathNameOpenFilePerm((char *) filename, fileFlags, fileMode);\n\tif (fileDesc < 0)\n\t{\n\t\tereport(ERROR, (errcode_for_file_access(),\n\t\t\t\t\t\terrmsg(\"could not open file \\\"%s\\\": %m\", filename)));\n\t}\n\n\treturn fileDesc;\n}\n\n\n/*\n * SendCopyInStart sends the start copy in message to initiate receiving data\n * from stdin. The frontend should now send copy data.\n */\nstatic void\nSendCopyInStart(void)\n{\n\tStringInfoData copyInStart = { NULL, 0, 0, 0 };\n\tconst char copyFormat = 1; /* binary copy format */\n\n\tpq_beginmessage(&copyInStart, 'G');\n\tpq_sendbyte(&copyInStart, copyFormat);\n\tpq_sendint(&copyInStart, 0, 2);\n\tpq_endmessage(&copyInStart);\n\n\t/* flush here to ensure that FE knows it can send data */\n\tint flushed = pq_flush();\n\tif (flushed != 0)\n\t{\n\t\tereport(WARNING, (errmsg(\"could not flush copy start data\")));\n\t}\n}\n\n\n/*\n * SendCopyOutStart sends the start copy out message to initiate sending data to\n * stdout. After this message, the backend will continue by sending copy data.\n */\nstatic void\nSendCopyOutStart(void)\n{\n\tStringInfoData copyOutStart = { NULL, 0, 0, 0 };\n\tconst char copyFormat = 1; /* binary copy format */\n\n\tpq_beginmessage(&copyOutStart, 'H');\n\tpq_sendbyte(&copyOutStart, copyFormat);\n\tpq_sendint(&copyOutStart, 0, 2);\n\tpq_endmessage(&copyOutStart);\n}\n\n\n/* Sends the copy-complete message. */\nstatic void\nSendCopyDone(void)\n{\n\tStringInfoData copyDone = { NULL, 0, 0, 0 };\n\n\tpq_beginmessage(&copyDone, 'c');\n\tpq_endmessage(&copyDone);\n\n\t/* flush here to signal to FE that we are done */\n\tint flushed = pq_flush();\n\tif (flushed != 0)\n\t{\n\t\tereport(WARNING, (errmsg(\"could not flush copy start data\")));\n\t}\n}\n\n\n/* Sends the copy data message to stdout. */\nstatic void\nSendCopyData(StringInfo fileBuffer)\n{\n\tStringInfoData copyData = { NULL, 0, 0, 0 };\n\n\tpq_beginmessage(&copyData, 'd');\n\tpq_sendbytes(&copyData, fileBuffer->data, fileBuffer->len);\n\tpq_endmessage(&copyData);\n}\n\n\n/*\n * ReceiveCopyData receives one copy data message from stdin, and writes this\n * message's contents into the given argument. The function then checks if the\n * copy protocol has been completed, and if it has, the function returns true.\n * If not, the function returns false indicating there are more data to read.\n * If the received message does not conform to the copy protocol, the function\n * mirrors copy.c's error behavior.\n */\nstatic bool\nReceiveCopyData(StringInfo copyData)\n{\n\tbool copyDone = true;\n\tconst int unlimitedSize = PQ_LARGE_MESSAGE_LIMIT;\n\n\tHOLD_CANCEL_INTERRUPTS();\n\tpq_startmsgread();\n\tint messageType = pq_getbyte();\n\tif (messageType == EOF)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\"unexpected EOF on client connection\")));\n\t}\n\n\t/* consume the rest of message before checking for message type */\n\tint messageCopied = pq_getmessage(copyData, unlimitedSize);\n\tif (messageCopied == EOF)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\"unexpected EOF on client connection\")));\n\t}\n\n\tRESUME_CANCEL_INTERRUPTS();\n\n\tswitch (messageType)\n\t{\n\t\tcase 'd':       /* CopyData */\n\t\t{\n\t\t\tcopyDone = false;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 'c':       /* CopyDone */\n\t\t{\n\t\t\tcopyDone = true;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 'f':       /* CopyFail */\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED),\n\t\t\t\t\t\t\terrmsg(\"COPY data failed: %s\", pq_getmsgstring(copyData))));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 'H':       /* Flush */\n\t\tcase 'S':       /* Sync */\n\t\t{\n\t\t\t/*\n\t\t\t * Ignore Flush/Sync for the convenience of client libraries (such\n\t\t\t * as libpq) that may send those without noticing that the command\n\t\t\t * they just sent was COPY.\n\t\t\t */\n\t\t\tcopyDone = false;\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION),\n\t\t\t\t\t\t\terrmsg(\"unexpected message type 0x%02X during COPY data\",\n\t\t\t\t\t\t\t\t   messageType)));\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn copyDone;\n}\n"
  },
  {
    "path": "src/backend/distributed/executor/tuple_destination.c",
    "content": "#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"access/htup_details.h\"\n\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/subplan_execution.h\"\n#include \"distributed/tuple_destination.h\"\n\n\n/*\n * TupleStoreTupleDestination is internal representation of a TupleDestination\n * which forwards tuples to a tuple store.\n */\ntypedef struct TupleStoreTupleDestination\n{\n\tTupleDestination pub;\n\n\t/* destination of tuples */\n\tTuplestorestate *tupleStore;\n\n\t/* how does tuples look like? */\n\tTupleDesc tupleDesc;\n} TupleStoreTupleDestination;\n\n/*\n * TupleDestDestReceiver is internal representation of a DestReceiver which\n * forards tuples to a tuple destination.\n */\ntypedef struct TupleDestDestReceiver\n{\n\tDestReceiver pub;\n\tTupleDestination *tupleDest;\n\n\t/* parameters to pass to tupleDest->putTuple() */\n\tTask *task;\n\tint placementIndex;\n} TupleDestDestReceiver;\n\n\n/* forward declarations for local functions */\nstatic void TupleStoreTupleDestPutTuple(TupleDestination *self, Task *task,\n\t\t\t\t\t\t\t\t\t\tint placementIndex, int queryNumber,\n\t\t\t\t\t\t\t\t\t\tHeapTuple heapTuple, uint64 tupleLibpqSize);\nstatic void EnsureIntermediateSizeLimitNotExceeded(TupleDestinationStats *\n\t\t\t\t\t\t\t\t\t\t\t\t   tupleDestinationStats);\nstatic TupleDesc TupleStoreTupleDestTupleDescForQuery(TupleDestination *self, int\n\t\t\t\t\t\t\t\t\t\t\t\t\t  queryNumber);\nstatic void TupleDestNonePutTuple(TupleDestination *self, Task *task,\n\t\t\t\t\t\t\t\t  int placementIndex, int queryNumber,\n\t\t\t\t\t\t\t\t  HeapTuple heapTuple, uint64 tupleLibpqSize);\nstatic TupleDesc TupleDestNoneTupleDescForQuery(TupleDestination *self, int queryNumber);\nstatic void TupleDestDestReceiverStartup(DestReceiver *copyDest, int operation,\n\t\t\t\t\t\t\t\t\t\t TupleDesc inputTupleDesc);\nstatic bool TupleDestDestReceiverReceive(TupleTableSlot *slot,\n\t\t\t\t\t\t\t\t\t\t DestReceiver *copyDest);\nstatic void TupleDestDestReceiverShutdown(DestReceiver *destReceiver);\nstatic void TupleDestDestReceiverDestroy(DestReceiver *destReceiver);\n\n\n/*\n * CreateTupleStoreTupleDest creates a TupleDestination which forwards tuples to\n * a tupleStore.\n */\nTupleDestination *\nCreateTupleStoreTupleDest(Tuplestorestate *tupleStore, TupleDesc tupleDescriptor)\n{\n\tTupleStoreTupleDestination *tupleStoreTupleDest = palloc0(\n\t\tsizeof(TupleStoreTupleDestination));\n\n\ttupleStoreTupleDest->tupleStore = tupleStore;\n\ttupleStoreTupleDest->tupleDesc = tupleDescriptor;\n\ttupleStoreTupleDest->pub.putTuple = TupleStoreTupleDestPutTuple;\n\ttupleStoreTupleDest->pub.tupleDescForQuery =\n\t\tTupleStoreTupleDestTupleDescForQuery;\n\n\tTupleDestination *tupleDestination = &tupleStoreTupleDest->pub;\n\ttupleDestination->tupleDestinationStats =\n\t\t(TupleDestinationStats *) palloc0(sizeof(TupleDestinationStats));\n\n\treturn (TupleDestination *) tupleStoreTupleDest;\n}\n\n\n/*\n * TupleStoreTupleDestPutTuple implements TupleDestination->putTuple for\n * TupleStoreTupleDestination.\n */\nstatic void\nTupleStoreTupleDestPutTuple(TupleDestination *self, Task *task,\n\t\t\t\t\t\t\tint placementIndex, int queryNumber,\n\t\t\t\t\t\t\tHeapTuple heapTuple, uint64 tupleLibpqSize)\n{\n\tTupleStoreTupleDestination *tupleDest = (TupleStoreTupleDestination *) self;\n\n\t/*\n\t * Remote execution sets tupleLibpqSize, however it is 0 for local execution. We prefer\n\t * to use tupleLibpqSize for  the remote execution because that reflects the exact data\n\t * transfer size over the network. For local execution, we rely on the size of the\n\t * tuple.\n\t */\n\tuint64 tupleSize = tupleLibpqSize;\n\tif (tupleSize == 0)\n\t{\n\t\ttupleSize = heapTuple->t_len;\n\t}\n\n\t/*\n\t * Enfoce citus.max_intermediate_result_size for subPlans if\n\t * the caller requested.\n\t */\n\tTupleDestinationStats *tupleDestinationStats = self->tupleDestinationStats;\n\tif (SubPlanLevel > 0 && tupleDestinationStats != NULL)\n\t{\n\t\ttupleDestinationStats->totalIntermediateResultSize += tupleSize;\n\t\tEnsureIntermediateSizeLimitNotExceeded(tupleDestinationStats);\n\t}\n\n\t/* do the actual work */\n\ttuplestore_puttuple(tupleDest->tupleStore, heapTuple);\n\n\t/* we record tuples received over network */\n\ttask->totalReceivedTupleData += tupleLibpqSize;\n}\n\n\n/*\n * EnsureIntermediateSizeLimitNotExceeded is a helper function for checking the current\n * state of the tupleDestinationStats and throws error if necessary.\n */\nstatic void\nEnsureIntermediateSizeLimitNotExceeded(TupleDestinationStats *tupleDestinationStats)\n{\n\tif (!tupleDestinationStats)\n\t{\n\t\t/* unexpected, still prefer defensive approach */\n\t\treturn;\n\t}\n\n\t/*\n\t * We only care about subPlans. Also, if user disabled, no need to\n\t * check  further.\n\t */\n\tif (SubPlanLevel == 0 || MaxIntermediateResult < 0)\n\t{\n\t\treturn;\n\t}\n\n\tuint64 maxIntermediateResultInBytes = MaxIntermediateResult * 1024L;\n\tif (tupleDestinationStats->totalIntermediateResultSize < maxIntermediateResultInBytes)\n\t{\n\t\t/*\n\t\t * We have not reached the size limit that the user requested, so\n\t\t * nothing to do for now.\n\t\t */\n\t\treturn;\n\t}\n\n\tereport(ERROR, (errmsg(\"the intermediate result size exceeds \"\n\t\t\t\t\t\t   \"citus.max_intermediate_result_size (currently %d kB)\",\n\t\t\t\t\t\t   MaxIntermediateResult),\n\t\t\t\t\terrdetail(\"Citus restricts the size of intermediate \"\n\t\t\t\t\t\t\t  \"results of complex subqueries and CTEs to \"\n\t\t\t\t\t\t\t  \"avoid accidentally pulling large result sets \"\n\t\t\t\t\t\t\t  \"into once place.\"),\n\t\t\t\t\terrhint(\"To run the current query, set \"\n\t\t\t\t\t\t\t\"citus.max_intermediate_result_size to a higher\"\n\t\t\t\t\t\t\t\" value or -1 to disable.\")));\n}\n\n\n/*\n * TupleStoreTupleDestTupleDescForQuery implements TupleDestination->TupleDescForQuery\n * for TupleStoreTupleDestination.\n */\nstatic TupleDesc\nTupleStoreTupleDestTupleDescForQuery(TupleDestination *self, int queryNumber)\n{\n\tAssert(queryNumber == 0);\n\n\tTupleStoreTupleDestination *tupleDest = (TupleStoreTupleDestination *) self;\n\n\treturn tupleDest->tupleDesc;\n}\n\n\n/*\n * CreateTupleDestNone creates a tuple destination which ignores the tuples.\n */\nTupleDestination *\nCreateTupleDestNone(void)\n{\n\tTupleDestination *tupleDest = palloc0(\n\t\tsizeof(TupleDestination));\n\ttupleDest->putTuple = TupleDestNonePutTuple;\n\ttupleDest->tupleDescForQuery = TupleDestNoneTupleDescForQuery;\n\n\treturn (TupleDestination *) tupleDest;\n}\n\n\n/*\n * TupleDestNonePutTuple implements TupleDestination->putTuple for\n * no-op tuple destination.\n */\nstatic void\nTupleDestNonePutTuple(TupleDestination *self, Task *task,\n\t\t\t\t\t  int placementIndex, int queryNumber,\n\t\t\t\t\t  HeapTuple heapTuple, uint64 tupleLibpqSize)\n{\n\t/* nothing to do */\n}\n\n\n/*\n * TupleDestNoneTupleDescForQuery implements TupleDestination->TupleDescForQuery\n * for no-op tuple destination.\n */\nstatic TupleDesc\nTupleDestNoneTupleDescForQuery(TupleDestination *self, int queryNumber)\n{\n\treturn NULL;\n}\n\n\n/*\n * CreateTupleDestDestReceiver creates a dest receiver which forwards tuples\n * to a tuple destination.\n */\nDestReceiver *\nCreateTupleDestDestReceiver(TupleDestination *tupleDest, Task *task, int placementIndex)\n{\n\tTupleDestDestReceiver *destReceiver = palloc0(sizeof(TupleDestDestReceiver));\n\tdestReceiver->pub.rStartup = TupleDestDestReceiverStartup;\n\tdestReceiver->pub.receiveSlot = TupleDestDestReceiverReceive;\n\tdestReceiver->pub.rShutdown = TupleDestDestReceiverShutdown;\n\tdestReceiver->pub.rDestroy = TupleDestDestReceiverDestroy;\n\n\tdestReceiver->tupleDest = tupleDest;\n\tdestReceiver->task = task;\n\tdestReceiver->placementIndex = placementIndex;\n\n\treturn (DestReceiver *) destReceiver;\n}\n\n\n/*\n * TupleDestDestReceiverStartup implements DestReceiver->rStartup for\n * TupleDestDestReceiver.\n */\nstatic void\nTupleDestDestReceiverStartup(DestReceiver *destReceiver, int operation,\n\t\t\t\t\t\t\t TupleDesc inputTupleDesc)\n{\n\t/* nothing to do */\n}\n\n\n/*\n * TupleDestDestReceiverReceive implements DestReceiver->receiveSlot for\n * TupleDestDestReceiver.\n */\nstatic bool\nTupleDestDestReceiverReceive(TupleTableSlot *slot,\n\t\t\t\t\t\t\t DestReceiver *destReceiver)\n{\n\tTupleDestDestReceiver *tupleDestReceiver = (TupleDestDestReceiver *) destReceiver;\n\tTupleDestination *tupleDest = tupleDestReceiver->tupleDest;\n\tTask *task = tupleDestReceiver->task;\n\tint placementIndex = tupleDestReceiver->placementIndex;\n\n\t/*\n\t * DestReceiver doesn't support multiple result sets with different shapes.\n\t */\n\tAssert(task->queryCount == 1);\n\tint queryNumber = 0;\n\n\tHeapTuple heapTuple = ExecFetchSlotHeapTuple(slot, true, NULL);\n\n\tuint64 tupleLibpqSize = 0;\n\n\ttupleDest->putTuple(tupleDest, task, placementIndex, queryNumber, heapTuple,\n\t\t\t\t\t\ttupleLibpqSize);\n\n\treturn true;\n}\n\n\n/*\n * TupleDestDestReceiverShutdown implements DestReceiver->rShutdown for\n * TupleDestDestReceiver.\n */\nstatic void\nTupleDestDestReceiverShutdown(DestReceiver *destReceiver)\n{\n\t/* nothing to do */\n}\n\n\n/*\n * TupleDestDestReceiverDestroy implements DestReceiver->rDestroy for\n * TupleDestDestReceiver.\n */\nstatic void\nTupleDestDestReceiverDestroy(DestReceiver *destReceiver)\n{\n\t/* nothing to do */\n}\n"
  },
  {
    "path": "src/backend/distributed/metadata/dependency.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * dependency.c\n *\t  Functions to reason about distributed objects and their dependencies\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/skey.h\"\n#include \"access/sysattr.h\"\n#include \"catalog/catalog.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/pg_auth_members.h\"\n#include \"catalog/pg_authid_d.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_extension_d.h\"\n#include \"catalog/pg_foreign_data_wrapper_d.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_proc_d.h\"\n#include \"catalog/pg_rewrite.h\"\n#include \"catalog/pg_rewrite_d.h\"\n#include \"catalog/pg_shdepend.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/extension.h\"\n#include \"common/hashfn.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_depended_object.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/version_compat.h\"\n\n/*\n * ObjectAddressCollector keeps track of collected ObjectAddresses. This can be used\n * together with RecurseObjectDependencies.\n *\n * We keep three different datastructures for the following reasons\n *  - A List ordered by insert/collect order\n *  - A Set to quickly O(1) check if an ObjectAddress has already been collected\n *  - A set to check which objects are already visited\n */\ntypedef struct ObjectAddressCollector\n{\n\tList *dependencyList;\n\tHTAB *dependencySet;\n\n\tHTAB *visitedObjects;\n} ObjectAddressCollector;\n\n/*\n * DependencyMode distinguishes the data stored in DependencyDefinition. For details see\n * DependencyDefinition's inline comments in the data union.\n */\ntypedef enum DependencyMode\n{\n\tDependencyObjectAddress,\n\tDependencyPgDepend,\n\tDependencyPgShDepend\n} DependencyMode;\n\ntypedef struct DependencyDefinition\n{\n\t/* describe how the dependency data is stored in the data field */\n\tDependencyMode mode;\n\n\t/*\n\t * Dependencies can be found in different ways and therefore stored differently on the\n\t * definition.\n\t */\n\tunion\n\t{\n\t\t/*\n\t\t * pg_depend is used for dependencies found in the database local pg_depend table.\n\t\t * The entry is copied while scanning the table. The record can be inspected\n\t\t * during the chasing algorithm to follow dependencies of different classes, or\n\t\t * based on dependency type.\n\t\t */\n\t\tFormData_pg_depend pg_depend;\n\n\t\t/*\n\t\t * pg_shdepend is used for dependencies found in the global pg_shdepend table.\n\t\t * The entry is copied while scanning the table. The record can be inspected\n\t\t * during the chasing algorithm to follow dependencies of different classes, or\n\t\t * based on dependency type.\n\t\t */\n\t\tFormData_pg_shdepend pg_shdepend;\n\n\t\t/*\n\t\t * address is used for dependencies that are artificially added during the\n\t\t * chasing. Since they are added by citus code we assume the dependency needs to\n\t\t * be chased anyway, of course it will only actually be chased if the object is a\n\t\t * supported object by citus\n\t\t */\n\t\tObjectAddress address;\n\t} data;\n} DependencyDefinition;\n\n/*\n * ViewDependencyNode represents a view (or possibly a table) in a dependency graph of\n * views.\n */\ntypedef struct ViewDependencyNode\n{\n\tOid id;\n\tint remainingDependencyCount;\n\tList *dependingNodes;\n}ViewDependencyNode;\n\n\nstatic List * GetRelationSequenceDependencyList(Oid relationId);\nstatic List * GetRelationFunctionDependencyList(Oid relationId);\nstatic List * GetRelationTriggerFunctionDependencyList(Oid relationId);\nstatic List * GetPublicationRelationsDependencyList(Oid relationId);\nstatic List * GetRelationStatsSchemaDependencyList(Oid relationId);\nstatic List * GetRelationIndicesDependencyList(Oid relationId);\nstatic DependencyDefinition * CreateObjectAddressDependencyDef(Oid classId, Oid objectId);\nstatic List * GetTypeConstraintDependencyDefinition(Oid typeId);\nstatic List * CreateObjectAddressDependencyDefList(Oid classId, List *objectIdList);\nstatic ObjectAddress DependencyDefinitionObjectAddress(DependencyDefinition *definition);\nstatic DeferredErrorMessage * DeferErrorIfHasUnsupportedDependency(const ObjectAddress *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   objectAddress);\n\n/* forward declarations for functions to interact with the ObjectAddressCollector */\nstatic void InitObjectAddressCollector(ObjectAddressCollector *collector);\nstatic void CollectObjectAddress(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t\t const ObjectAddress *address);\nstatic bool IsObjectAddressCollected(ObjectAddress findAddress,\n\t\t\t\t\t\t\t\t\t ObjectAddressCollector *collector);\nstatic ObjectAddress * GetUndistributableDependency(const ObjectAddress *objectAddress);\nstatic bool ObjectAddressHasExtensionDependency(const ObjectAddress *target,\n\t\t\t\t\t\t\t\t\t\t\t\tObjectAddress *extensionAddress,\n\t\t\t\t\t\t\t\t\t\t\t\tint extensionDependency);\nstatic void MarkObjectVisited(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t  ObjectAddress target);\nstatic bool TargetObjectVisited(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t\tObjectAddress target);\n\ntypedef List *(*expandFn)(ObjectAddressCollector *collector, ObjectAddress target);\ntypedef bool (*followFn)(ObjectAddressCollector *collector,\n\t\t\t\t\t\t DependencyDefinition *definition);\ntypedef void (*applyFn)(ObjectAddressCollector *collector,\n\t\t\t\t\t\tDependencyDefinition *definition);\n\n/* forward declaration of functions that recurse pg_depend */\nstatic void RecurseObjectDependencies(ObjectAddress target, expandFn expand,\n\t\t\t\t\t\t\t\t\t  followFn follow, applyFn apply,\n\t\t\t\t\t\t\t\t\t  ObjectAddressCollector *collector);\nstatic List * DependencyDefinitionFromPgDepend(ObjectAddress target);\nstatic List * DependencyDefinitionFromPgShDepend(ObjectAddress target);\nstatic bool FollowAllSupportedDependencies(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t\t\t\t   DependencyDefinition *definition);\nstatic bool FollowNewSupportedDependencies(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t\t\t\t   DependencyDefinition *definition);\nstatic bool FollowAllDependencies(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t\t  DependencyDefinition *definition);\nstatic bool FollowExtAndInternalDependencies(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t\t\t\t\t DependencyDefinition *definition);\nstatic void ApplyAddToDependencyList(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t\t\t DependencyDefinition *definition);\nstatic void ApplyAddCitusDependedObjectsToDependencyList(ObjectAddressCollector *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t collector,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t DependencyDefinition *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t definition);\nstatic List * GetViewRuleReferenceDependencyList(Oid relationId);\nstatic List * ExpandCitusSupportedTypes(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t\t\t\tObjectAddress target);\nstatic List * ExpandForPgVanilla(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t\t ObjectAddress target);\nstatic List * GetDependentRoleIdsFDW(Oid FDWOid);\nstatic List * ExpandRolesToGroups(Oid roleid);\nstatic ViewDependencyNode * BuildViewDependencyGraph(Oid relationId, HTAB *nodeMap);\nstatic bool IsObjectAddressOwnedByExtension(const ObjectAddress *target,\n\t\t\t\t\t\t\t\t\t\t\tObjectAddress *extensionAddress);\nstatic bool ErrorOrWarnIfObjectHasUnsupportedDependency(const\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tObjectAddress *objectAddress);\n\n/*\n * GetUniqueDependenciesList takes a list of object addresses and returns a new list\n * of ObjectAddesses whose elements are unique.\n */\nList *\nGetUniqueDependenciesList(List *objectAddressesList)\n{\n\tObjectAddressCollector objectAddressCollector = { 0 };\n\tInitObjectAddressCollector(&objectAddressCollector);\n\n\tObjectAddress *objectAddress = NULL;\n\tforeach_declared_ptr(objectAddress, objectAddressesList)\n\t{\n\t\tif (IsObjectAddressCollected(*objectAddress, &objectAddressCollector))\n\t\t{\n\t\t\t/* skip objects that are already collected */\n\t\t\tcontinue;\n\t\t}\n\n\t\tCollectObjectAddress(&objectAddressCollector, objectAddress);\n\t}\n\n\treturn objectAddressCollector.dependencyList;\n}\n\n\n/*\n * GetDependenciesForObject returns a list of ObjectAddesses to be created in order\n * before the target object could safely be created on a worker. Some of the object might\n * already be created on a worker. It should be created in an idempotent way.\n */\nList *\nGetDependenciesForObject(const ObjectAddress *target)\n{\n\tObjectAddressCollector collector = { 0 };\n\tInitObjectAddressCollector(&collector);\n\n\tRecurseObjectDependencies(*target,\n\t\t\t\t\t\t\t  &ExpandCitusSupportedTypes,\n\t\t\t\t\t\t\t  &FollowNewSupportedDependencies,\n\t\t\t\t\t\t\t  &ApplyAddToDependencyList,\n\t\t\t\t\t\t\t  &collector);\n\n\treturn collector.dependencyList;\n}\n\n\n/*\n * GetAllSupportedDependenciesForObject returns a list of all the ObjectAddresses to be\n * created in order before the target object could safely be created on a worker, if all\n * dependent objects are distributable. As a caller, you probably need to use\n * GetDependenciesForObject() which eliminates already distributed objects from the returned\n * list.\n *\n * Some of the object might already be created on a worker. It should be created\n * in an idempotent way.\n */\nList *\nGetAllSupportedDependenciesForObject(const ObjectAddress *target)\n{\n\tObjectAddressCollector collector = { 0 };\n\tInitObjectAddressCollector(&collector);\n\n\tRecurseObjectDependencies(*target,\n\t\t\t\t\t\t\t  &ExpandCitusSupportedTypes,\n\t\t\t\t\t\t\t  &FollowAllSupportedDependencies,\n\t\t\t\t\t\t\t  &ApplyAddToDependencyList,\n\t\t\t\t\t\t\t  &collector);\n\n\treturn collector.dependencyList;\n}\n\n\n/*\n * GetAllDependenciesForObject returns a list of all the dependent objects of the given\n * object irrespective of whether the dependent object is supported by Citus or not, if\n * the object can be found as dependency with RecurseObjectDependencies and\n * ExpandCitusSupportedTypes.\n *\n * This function will be used to provide meaningful error messages if any dependent\n * object for a given object is not supported. If you want to create dependencies for\n * an object, you probably need to use GetDependenciesForObject().\n */\nList *\nGetAllDependenciesForObject(const ObjectAddress *target)\n{\n\tObjectAddressCollector collector = { 0 };\n\tInitObjectAddressCollector(&collector);\n\n\tRecurseObjectDependencies(*target,\n\t\t\t\t\t\t\t  &ExpandCitusSupportedTypes,\n\t\t\t\t\t\t\t  &FollowAllDependencies,\n\t\t\t\t\t\t\t  &ApplyAddToDependencyList,\n\t\t\t\t\t\t\t  &collector);\n\n\treturn collector.dependencyList;\n}\n\n\n/*\n * GetAllCitusDependedDependenciesForObject returns all the dependencies\n * which are owned by citus extension for the target.\n */\nList *\nGetAllCitusDependedDependenciesForObject(const ObjectAddress *target)\n{\n\tObjectAddressCollector collector = { 0 };\n\tInitObjectAddressCollector(&collector);\n\n\tRecurseObjectDependencies(*target,\n\t\t\t\t\t\t\t  &ExpandForPgVanilla,\n\t\t\t\t\t\t\t  &FollowExtAndInternalDependencies,\n\t\t\t\t\t\t\t  &ApplyAddCitusDependedObjectsToDependencyList,\n\t\t\t\t\t\t\t  &collector);\n\n\treturn collector.dependencyList;\n}\n\n\n/*\n * OrderObjectAddressListInDependencyOrder given a list of ObjectAddresses return a new\n * list of the same ObjectAddresses ordered on dependency order where dependencies\n * precedes the corresponding object in the list.\n *\n * The algortihm traveses pg_depend in a depth first order starting at the first object in\n * the provided list. By traversing depth first it will put the first dependency at the\n * head of the list with dependencies depending on them later.\n *\n * If the object is already in the list it is skipped for traversal. This happens when an\n * object was already added to the target list before it occurred in the input list.\n */\nList *\nOrderObjectAddressListInDependencyOrder(List *objectAddressList)\n{\n\tObjectAddressCollector collector = { 0 };\n\tInitObjectAddressCollector(&collector);\n\n\tObjectAddress *objectAddress = NULL;\n\tforeach_declared_ptr(objectAddress, objectAddressList)\n\t{\n\t\tif (IsObjectAddressCollected(*objectAddress, &collector))\n\t\t{\n\t\t\t/* skip objects that are already ordered */\n\t\t\tcontinue;\n\t\t}\n\n\t\tRecurseObjectDependencies(*objectAddress,\n\t\t\t\t\t\t\t\t  &ExpandCitusSupportedTypes,\n\t\t\t\t\t\t\t\t  &FollowAllSupportedDependencies,\n\t\t\t\t\t\t\t\t  &ApplyAddToDependencyList,\n\t\t\t\t\t\t\t\t  &collector);\n\n\t\tCollectObjectAddress(&collector, objectAddress);\n\t}\n\n\treturn collector.dependencyList;\n}\n\n\n/*\n * RecurseObjectDependencies recursively visits all dependencies of an object. It sources\n * the dependencies from pg_depend and pg_shdepend while 'expanding' the list via an\n * optional `expand` function.\n *\n * Starting from the target ObjectAddress. For every dependency found the `follow`\n * function will be called. When `follow` returns true it will recursively visit the\n * dependencies for that object.\n *\n * Visiting will happen in depth first order, which is useful to create or sorted lists of\n * dependencies to create.\n *\n * For all dependencies that should be visited the apply function will be called. This\n * function is designed to be the mutating function for the context being passed. Although\n * nothing prevents the follow function to also mutate the context.\n *\n *  - follow will be called on the way down, so the invocation order is top to bottom of\n *    the dependency tree\n *  - apply is called on the way back, so the invocation order is bottom to top. Apply is\n *    not called for entries for which follow has returned false.\n */\nstatic void\nRecurseObjectDependencies(ObjectAddress target, expandFn expand, followFn follow,\n\t\t\t\t\t\t  applyFn apply, ObjectAddressCollector *collector)\n{\n\tif (TargetObjectVisited(collector, target))\n\t{\n\t\t/* prevent infinite loops due to circular dependencies */\n\t\treturn;\n\t}\n\n\tMarkObjectVisited(collector, target);\n\n\t/* lookup both pg_depend and pg_shdepend for dependencies */\n\tList *pgDependDefinitions = DependencyDefinitionFromPgDepend(target);\n\tList *pgShDependDefinitions = DependencyDefinitionFromPgShDepend(target);\n\tList *dependenyDefinitionList = list_concat(pgDependDefinitions,\n\t\t\t\t\t\t\t\t\t\t\t\tpgShDependDefinitions);\n\n\t/* concat expanded entries if applicable */\n\tif (expand != NULL)\n\t{\n\t\tList *expandedEntries = expand(collector, target);\n\t\tdependenyDefinitionList = list_concat(dependenyDefinitionList, expandedEntries);\n\t}\n\n\t/* iterate all entries and recurse depth first */\n\tDependencyDefinition *dependencyDefinition = NULL;\n\tforeach_declared_ptr(dependencyDefinition, dependenyDefinitionList)\n\t{\n\t\tif (follow == NULL || !follow(collector, dependencyDefinition))\n\t\t{\n\t\t\t/* skip all pg_depend entries the user didn't want to follow */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * recurse depth first, this makes sure we call apply for the deepest dependency\n\t\t * first.\n\t\t */\n\t\tObjectAddress address = DependencyDefinitionObjectAddress(dependencyDefinition);\n\t\tRecurseObjectDependencies(address, expand, follow, apply, collector);\n\n\t\t/* now apply changes for current entry */\n\t\tif (apply != NULL)\n\t\t{\n\t\t\tapply(collector, dependencyDefinition);\n\t\t}\n\t}\n}\n\n\n/*\n * DependencyDefinitionFromPgDepend loads all pg_depend records describing the\n * dependencies of target.\n */\nstatic List *\nDependencyDefinitionFromPgDepend(ObjectAddress target)\n{\n\tScanKeyData key[2];\n\tHeapTuple depTup = NULL;\n\tList *dependenyDefinitionList = NIL;\n\n\t/*\n\t * iterate the actual pg_depend catalog\n\t */\n\tRelation depRel = table_open(DependRelationId, AccessShareLock);\n\n\t/* scan pg_depend for classid = $1 AND objid = $2 using pg_depend_depender_index */\n\tScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(target.classId));\n\tScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(target.objectId));\n\tSysScanDesc depScan = systable_beginscan(depRel, DependDependerIndexId, true, NULL, 2,\n\t\t\t\t\t\t\t\t\t\t\t key);\n\n\twhile (HeapTupleIsValid(depTup = systable_getnext(depScan)))\n\t{\n\t\tForm_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);\n\t\tDependencyDefinition *dependency = palloc0(sizeof(DependencyDefinition));\n\n\t\t/* keep track of all pg_depend records as dependency definitions */\n\t\tdependency->mode = DependencyPgDepend;\n\t\tdependency->data.pg_depend = *pg_depend;\n\t\tdependenyDefinitionList = lappend(dependenyDefinitionList, dependency);\n\t}\n\n\tsystable_endscan(depScan);\n\trelation_close(depRel, AccessShareLock);\n\n\treturn dependenyDefinitionList;\n}\n\n\n/*\n * DependencyDefinitionFromPgShDepend loads all pg_shdepend records describing the\n * dependencies of target.\n */\nstatic List *\nDependencyDefinitionFromPgShDepend(ObjectAddress target)\n{\n\tScanKeyData key[3];\n\tHeapTuple depTup = NULL;\n\tList *dependenyDefinitionList = NIL;\n\n\t/*\n\t * iterate the actual pg_shdepend catalog\n\t */\n\tRelation shdepRel = table_open(SharedDependRelationId, AccessShareLock);\n\n\t/*\n\t * Scan pg_shdepend for dbid = $1 AND classid = $2 AND objid = $3 using\n\t * pg_shdepend_depender_index\n\t *\n\t * where $1 is decided as follows:\n\t *   - shared dependencies $1 = InvalidOid\n\t *   - other dependencies $1 = MyDatabaseId\n\t * This is consistent with postgres' static classIdGetDbId function\n\t */\n\tOid dbid = InvalidOid;\n\tif (!IsSharedRelation(target.classId))\n\t{\n\t\tdbid = MyDatabaseId;\n\t}\n\tScanKeyInit(&key[0], Anum_pg_shdepend_dbid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(dbid));\n\tScanKeyInit(&key[1], Anum_pg_shdepend_classid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(target.classId));\n\tScanKeyInit(&key[2], Anum_pg_shdepend_objid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(target.objectId));\n\tSysScanDesc shdepScan = systable_beginscan(shdepRel, SharedDependDependerIndexId,\n\t\t\t\t\t\t\t\t\t\t\t   true, NULL, 3, key);\n\n\twhile (HeapTupleIsValid(depTup = systable_getnext(shdepScan)))\n\t{\n\t\tForm_pg_shdepend pg_shdepend = (Form_pg_shdepend) GETSTRUCT(depTup);\n\t\tDependencyDefinition *dependency = palloc0(sizeof(DependencyDefinition));\n\n\t\t/* keep track of all pg_shdepend records as dependency definitions */\n\t\tdependency->mode = DependencyPgShDepend;\n\t\tdependency->data.pg_shdepend = *pg_shdepend;\n\t\tdependenyDefinitionList = lappend(dependenyDefinitionList, dependency);\n\t}\n\n\tsystable_endscan(shdepScan);\n\trelation_close(shdepRel, AccessShareLock);\n\n\treturn dependenyDefinitionList;\n}\n\n\n/*\n * InitObjectAddressCollector takes a pointer to an already allocated (possibly stack)\n * ObjectAddressCollector struct. It makes sure this struct is ready to be used for object\n * collection.\n *\n * If an already initialized collector is passed the collector will be cleared from its\n * contents to be reused.\n */\nstatic void\nInitObjectAddressCollector(ObjectAddressCollector *collector)\n{\n\tassert_valid_hash_key3(ObjectAddress, classId, objectId, objectSubId);\n\tcollector->dependencySet = CreateSimpleHashSetWithName(ObjectAddress,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"dependency set\");\n\tcollector->dependencyList = NULL;\n\n\tcollector->visitedObjects = CreateSimpleHashSetWithName(ObjectAddress,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"visited object set\");\n}\n\n\n/*\n * TargetObjectVisited returns true if the input target has been visited while\n * traversing pg_depend.\n */\nstatic bool\nTargetObjectVisited(ObjectAddressCollector *collector, ObjectAddress target)\n{\n\tbool found = false;\n\n\t/* find in set */\n\thash_search(collector->visitedObjects, &target, HASH_FIND, &found);\n\n\treturn found;\n}\n\n\n/*\n * MarkObjectVisited marks the object as visited during the traversal of\n * pg_depend.\n */\nstatic void\nMarkObjectVisited(ObjectAddressCollector *collector, ObjectAddress target)\n{\n\tbool found = false;\n\n\t/* add to set */\n\tObjectAddress *address = (ObjectAddress *) hash_search(collector->visitedObjects,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &target,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER, &found);\n\n\tif (!found)\n\t{\n\t\t/* copy object address in */\n\t\t*address = target;\n\t}\n}\n\n\n/*\n * CollectObjectAddress adds an ObjectAddress to the collector.\n */\nstatic void\nCollectObjectAddress(ObjectAddressCollector *collector, const ObjectAddress *collect)\n{\n\tbool found = false;\n\n\t/* add to set */\n\tObjectAddress *address = (ObjectAddress *) hash_search(collector->dependencySet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   collect,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER, &found);\n\n\tif (!found)\n\t{\n\t\t/* copy object address in */\n\t\t*address = *collect;\n\t}\n\n\t/* add to list*/\n\tcollector->dependencyList = lappend(collector->dependencyList, address);\n}\n\n\n/*\n * IsObjectAddressCollected is a helper function that can check if an ObjectAddress is\n * already in a (unsorted) list of ObjectAddresses\n */\nstatic bool\nIsObjectAddressCollected(ObjectAddress findAddress,\n\t\t\t\t\t\t ObjectAddressCollector *collector)\n{\n\tbool found = false;\n\n\t/* add to set */\n\thash_search(collector->dependencySet, &findAddress, HASH_FIND, &found);\n\n\treturn found;\n}\n\n\n/*\n * SupportedDependencyByCitus returns whether citus has support to distribute the object\n * addressed.\n */\nbool\nSupportedDependencyByCitus(const ObjectAddress *address)\n{\n\tif (!EnableMetadataSync)\n\t{\n\t\t/*\n\t\t * If the user has disabled object propagation we need to fall back to the legacy\n\t\t * behaviour in which we only support schema creation\n\t\t */\n\t\tswitch (getObjectClass(address))\n\t\t{\n\t\t\tcase OCLASS_SCHEMA:\n\t\t\t{\n\t\t\t\treturn !isTempNamespace(address->objectId);\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t/* should be unreachable */\n\t\tAssert(false);\n\t}\n\n\t/*\n\t * looking at the type of a object to see if we know how to create the object on the\n\t * workers.\n\t */\n\tswitch (getObjectClass(address))\n\t{\n\t\tcase OCLASS_AM:\n\t\t{\n\t\t\t/*\n\t\t\t * Only support access methods if they came from extensions\n\t\t\t * During the dependency resolution it will cascade into the extension and\n\t\t\t * distributed that one instead of the Access Method. Now access methods can\n\t\t\t * be configured on tables on the workers.\n\t\t\t */\n\t\t\treturn IsObjectAddressOwnedByExtension(address, NULL);\n\t\t}\n\n\t\tcase OCLASS_CONSTRAINT:\n\t\t{\n\t\t\t/*\n\t\t\t * Constraints are only supported when on domain types. Other constraints have\n\t\t\t * their typid set to InvalidOid.\n\t\t\t */\n\t\t\treturn OidIsValid(get_constraint_typid(address->objectId));\n\t\t}\n\n\t\tcase OCLASS_COLLATION:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tcase OCLASS_SCHEMA:\n\t\t{\n\t\t\treturn !isTempNamespace(address->objectId);\n\t\t}\n\n\t\tcase OCLASS_PROC:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tcase OCLASS_DATABASE:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tcase OCLASS_FOREIGN_SERVER:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tcase OCLASS_ROLE:\n\t\t{\n\t\t\t/* if it is a reserved role do not propagate */\n\t\t\tif (IsReservedName(GetUserNameFromId(address->objectId, false)))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\tcase OCLASS_EXTENSION:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tcase OCLASS_PUBLICATION:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tcase OCLASS_TSCONFIG:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tcase OCLASS_TSDICT:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tcase OCLASS_TYPE:\n\t\t{\n\t\t\tswitch (get_typtype(address->objectId))\n\t\t\t{\n\t\t\t\tcase TYPTYPE_ENUM:\n\t\t\t\tcase TYPTYPE_COMPOSITE:\n\t\t\t\tcase TYPTYPE_DOMAIN:\n\t\t\t\t{\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tcase TYPTYPE_BASE:\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * array types should be followed but not created, as they get created\n\t\t\t\t\t * by the original type.\n\t\t\t\t\t */\n\t\t\t\t\treturn type_is_array(address->objectId);\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\t/* type not supported */\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * should be unreachable, break here is to make sure the function has a path\n\t\t\t * without return, instead of falling through to the next block */\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OCLASS_CLASS:\n\t\t{\n\t\t\tchar relKind = get_rel_relkind(address->objectId);\n\n\t\t\t/*\n\t\t\t * composite types have a reference to a relation of composite type, we need\n\t\t\t * to follow those to get the dependencies of type fields.\n\t\t\t *\n\t\t\t * As we also handle tables as objects as well, follow dependencies\n\t\t\t * for tables.\n\t\t\t */\n\t\t\tif (relKind == RELKIND_COMPOSITE_TYPE ||\n\t\t\t\trelKind == RELKIND_RELATION ||\n\t\t\t\trelKind == RELKIND_PARTITIONED_TABLE ||\n\t\t\t\trelKind == RELKIND_FOREIGN_TABLE ||\n\t\t\t\trelKind == RELKIND_SEQUENCE ||\n\t\t\t\trelKind == RELKIND_INDEX ||\n\t\t\t\trelKind == RELKIND_PARTITIONED_INDEX ||\n\t\t\t\trelKind == RELKIND_VIEW)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\t/* unsupported type */\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\n\n/*\n * ErrorOrWarnIfObjectHasUnsupportedDependency returns false without throwing any message if\n * object doesn't have any unsupported dependency, else throws a message with proper level\n * (except the cluster doesn't have any node) and return true.\n */\nstatic bool\nErrorOrWarnIfObjectHasUnsupportedDependency(const ObjectAddress *objectAddress)\n{\n\tDeferredErrorMessage *errMsg = DeferErrorIfHasUnsupportedDependency(objectAddress);\n\tif (errMsg != NULL)\n\t{\n\t\t/*\n\t\t * Don't need to give any messages if there is no worker nodes in\n\t\t * the cluster as user's experience won't be affected on the single node even\n\t\t * if the object won't be distributed.\n\t\t */\n\t\tif (!HasAnyNodes())\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\t/*\n\t\t * Since Citus drops and recreates some object while converting a table type\n\t\t * giving a DEBUG1 message is enough if the process in table type conversion\n\t\t * function call\n\t\t */\n\t\tif (InTableTypeConversionFunctionCall)\n\t\t{\n\t\t\tRaiseDeferredError(errMsg, DEBUG1);\n\t\t}\n\t\t/*\n\t\t * If the view is object distributed, we should provide an error to not have\n\t\t * different definition of object on coordinator and worker nodes. If the object\n\t\t * is not distributed yet, we can create it locally to not affect user's local\n\t\t * usage experience.\n\t\t */\n\t\telse if (IsAnyObjectDistributed(list_make1((ObjectAddress *) objectAddress)))\n\t\t{\n\t\t\tRaiseDeferredError(errMsg, ERROR);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (EnableUnsupportedFeatureMessages)\n\t\t\t{\n\t\t\t\tRaiseDeferredError(errMsg, WARNING);\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ErrorOrWarnIfAnyObjectHasUnsupportedDependency iteratively calls\n * ErrorOrWarnIfObjectHasUnsupportedDependency for given addresses.\n */\nbool\nErrorOrWarnIfAnyObjectHasUnsupportedDependency(List *objectAddresses)\n{\n\tObjectAddress *objectAddress = NULL;\n\tforeach_declared_ptr(objectAddress, objectAddresses)\n\t{\n\t\tif (ErrorOrWarnIfObjectHasUnsupportedDependency(objectAddress))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * DeferErrorIfHasUnsupportedDependency returns deferred error message if the given\n * object has any undistributable dependency.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfHasUnsupportedDependency(const ObjectAddress *objectAddress)\n{\n\tObjectAddress *undistributableDependency = GetUndistributableDependency(\n\t\tobjectAddress);\n\n\tif (undistributableDependency == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tStringInfo errorInfo = makeStringInfo();\n\tStringInfo detailInfo = makeStringInfo();\n\n\tchar *objectDescription = getObjectDescription(objectAddress, false);\n\tchar *dependencyDescription = getObjectDescription(undistributableDependency, false);\n\n\t/*\n\t * We expect callers to interpret the error returned from this function\n\t * as a warning if the object itself is just being created. In that case,\n\t * we expect them to report below error detail as well to indicate that\n\t * object itself will not be propagated but will still be created locally.\n\t *\n\t * Otherwise, callers are expected to throw the error returned from this\n\t * function as a hard one by ignoring the detail part.\n\t */\n\tif (!IsAnyObjectDistributed(list_make1((ObjectAddress *) objectAddress)))\n\t{\n\t\tappendStringInfo(detailInfo, \"\\\"%s\\\" will be created only locally\",\n\t\t\t\t\t\t objectDescription);\n\t}\n\n\tif (SupportedDependencyByCitus(undistributableDependency))\n\t{\n\t\tStringInfo hintInfo = makeStringInfo();\n\n\t\tappendStringInfo(errorInfo, \"\\\"%s\\\" has dependency to \\\"%s\\\" that is not in \"\n\t\t\t\t\t\t\t\t\t\"Citus' metadata\",\n\t\t\t\t\t\t objectDescription,\n\t\t\t\t\t\t dependencyDescription);\n\n\t\tif (IsAnyObjectDistributed(list_make1((ObjectAddress *) objectAddress)))\n\t\t{\n\t\t\tappendStringInfo(hintInfo,\n\t\t\t\t\t\t\t \"Distribute \\\"%s\\\" first to modify \\\"%s\\\" on worker nodes\",\n\t\t\t\t\t\t\t dependencyDescription,\n\t\t\t\t\t\t\t objectDescription);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(hintInfo, \"Distribute \\\"%s\\\" first to distribute \\\"%s\\\"\",\n\t\t\t\t\t\t\t dependencyDescription,\n\t\t\t\t\t\t\t objectDescription);\n\t\t}\n\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t errorInfo->data, detailInfo->data, hintInfo->data);\n\t}\n\n\tappendStringInfo(errorInfo, \"\\\"%s\\\" has dependency on unsupported \"\n\t\t\t\t\t\t\t\t\"object \\\"%s\\\"\", objectDescription,\n\t\t\t\t\t dependencyDescription);\n\n\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t errorInfo->data, detailInfo->data, NULL);\n}\n\n\n/*\n * DeferErrorIfAnyObjectHasUnsupportedDependency iteratively calls\n * DeferErrorIfHasUnsupportedDependency for given addresses.\n */\nDeferredErrorMessage *\nDeferErrorIfAnyObjectHasUnsupportedDependency(const List *objectAddresses)\n{\n\tDeferredErrorMessage *deferredErrorMessage = NULL;\n\tObjectAddress *objectAddress = NULL;\n\tforeach_declared_ptr(objectAddress, objectAddresses)\n\t{\n\t\tdeferredErrorMessage = DeferErrorIfHasUnsupportedDependency(objectAddress);\n\t\tif (deferredErrorMessage)\n\t\t{\n\t\t\treturn deferredErrorMessage;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * GetUndistributableDependency checks whether object has any non-distributable\n * dependency. If any one found, it will be returned.\n */\nstatic ObjectAddress *\nGetUndistributableDependency(const ObjectAddress *objectAddress)\n{\n\tList *dependencies = GetAllDependenciesForObject(objectAddress);\n\tObjectAddress *dependency = NULL;\n\n\t/*\n\t * Users can disable metadata sync by their own risk. If it is disabled, Citus\n\t * doesn't propagate dependencies. So, if it is disabled, there is no undistributable\n\t * dependency.\n\t */\n\tif (!EnableMetadataSync)\n\t{\n\t\treturn NULL;\n\t}\n\n\tforeach_declared_ptr(dependency, dependencies)\n\t{\n\t\t/*\n\t\t * Objects with the id smaller than FirstNormalObjectId should be created within\n\t\t * initdb. Citus needs to have such objects as distributed, so we can not add\n\t\t * such check to dependency resolution logic. Though, Citus shouldn't error\n\t\t * out if such dependency is not supported. So, skip them here.\n\t\t */\n\t\tif (dependency->objectId < FirstNormalObjectId)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * If object is distributed already, ignore it.\n\t\t */\n\t\tif (IsAnyObjectDistributed(list_make1(dependency)))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * If the dependency is not supported with Citus, return the dependency.\n\t\t */\n\t\tif (!SupportedDependencyByCitus(dependency))\n\t\t{\n\t\t\t/*\n\t\t\t * Since we do not yet support distributed TS TEMPLATE and AM objects, we skip\n\t\t\t * dependency checks for text search templates. The user is expected to\n\t\t\t * manually create the TS TEMPLATE and AM objects.\n\t\t\t */\n\t\t\tif (getObjectClass(dependency) != OCLASS_TSTEMPLATE &&\n\t\t\t\tgetObjectClass(dependency) != OCLASS_AM)\n\t\t\t{\n\t\t\t\treturn dependency;\n\t\t\t}\n\t\t}\n\n\t\tif (getObjectClass(dependency) == OCLASS_CLASS)\n\t\t{\n\t\t\tchar relKind = get_rel_relkind(dependency->objectId);\n\n\t\t\tif (relKind == RELKIND_SEQUENCE ||\n\t\t\t\trelKind == RELKIND_COMPOSITE_TYPE ||\n\t\t\t\trelKind == RELKIND_VIEW)\n\t\t\t{\n\t\t\t\t/* citus knows how to auto-distribute these dependencies */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (relKind == RELKIND_INDEX || relKind == RELKIND_PARTITIONED_INDEX)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Indexes are only qualified for distributed objects for dependency\n\t\t\t\t * tracking purposes, so we can ignore those.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Citus doesn't know how to auto-distribute the rest of the RELKINDs\n\t\t\t\t * via dependency resolution\n\t\t\t\t */\n\t\t\t\treturn dependency;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * IsTableOwnedByExtension returns whether the table with the given relation ID is\n * owned by an extension.\n */\nbool\nIsTableOwnedByExtension(Oid relationId)\n{\n\tObjectAddress tableAddress = { 0 };\n\tObjectAddressSet(tableAddress, RelationRelationId, relationId);\n\n\treturn IsObjectAddressOwnedByExtension(&tableAddress, NULL);\n}\n\n\n/*\n * ObjectAddressDependsOnExtension returns whether or not the object depends\n * on an extension. It is assumed that \"an object having a dependency of type\n * DEPENDENCY_AUTO_EXTENSION to an extension\" depends on that extension.\n */\nbool\nObjectAddressDependsOnExtension(const ObjectAddress *target)\n{\n\treturn ObjectAddressHasExtensionDependency(target, NULL,\n\t\t\t\t\t\t\t\t\t\t\t   DEPENDENCY_AUTO_EXTENSION);\n}\n\n\n/*\n * IsObjectAddressOwnedByExtension returns whether or not the object is owned by an\n * extension. It is assumed that an object having a dependency on an extension is created\n * by that extension and therefore owned by that extension.\n *\n * If extensionAddress is not set to a NULL pointer the function will write the extension\n * address this function depends on into this location.\n */\nstatic bool\nIsObjectAddressOwnedByExtension(const ObjectAddress *target,\n\t\t\t\t\t\t\t\tObjectAddress *extensionAddress)\n{\n\treturn ObjectAddressHasExtensionDependency(target, extensionAddress,\n\t\t\t\t\t\t\t\t\t\t\t   DEPENDENCY_EXTENSION);\n}\n\n\n/*\n * ObjectAddressHasExtensionDependency is a helper function that returns true if\n * given object has a dependency record (of type DEPENDENCY_EXTENSION or\n * DEPENDENCY_AUTO_EXTENSION) for an extension.\n *\n * If extensionAddress is not set to a NULL pointer the function will write the\n * extension address this function depends on into this location.\n */\nstatic bool\nObjectAddressHasExtensionDependency(const ObjectAddress *target,\n\t\t\t\t\t\t\t\t\tObjectAddress *extensionAddress,\n\t\t\t\t\t\t\t\t\tint extensionDependency)\n{\n\tAssert(extensionDependency == DEPENDENCY_EXTENSION ||\n\t\t   extensionDependency == DEPENDENCY_AUTO_EXTENSION);\n\n\tScanKeyData key[2];\n\tHeapTuple depTup = NULL;\n\tbool result = false;\n\n\tRelation depRel = table_open(DependRelationId, AccessShareLock);\n\n\t/* scan pg_depend for classid = $1 AND objid = $2 using pg_depend_depender_index */\n\tScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(target->classId));\n\tScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(target->objectId));\n\tSysScanDesc depScan = systable_beginscan(depRel, DependDependerIndexId, true, NULL, 2,\n\t\t\t\t\t\t\t\t\t\t\t key);\n\n\twhile (HeapTupleIsValid(depTup = systable_getnext(depScan)))\n\t{\n\t\tForm_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);\n\t\tif (pg_depend->deptype == extensionDependency)\n\t\t{\n\t\t\tresult = true;\n\t\t\tif (extensionAddress != NULL)\n\t\t\t{\n\t\t\t\tObjectAddressSubSet(*extensionAddress, pg_depend->refclassid,\n\t\t\t\t\t\t\t\t\tpg_depend->refobjid, pg_depend->refobjsubid);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tsystable_endscan(depScan);\n\ttable_close(depRel, AccessShareLock);\n\n\treturn result;\n}\n\n\n/*\n * IsAnyObjectAddressOwnedByExtension iteratively calls IsObjectAddressOwnedByExtension\n * for given addresses to determine if any address is owned by an extension.\n */\nbool\nIsAnyObjectAddressOwnedByExtension(const List *targets,\n\t\t\t\t\t\t\t\t   ObjectAddress *extensionAddress)\n{\n\tObjectAddress *target = NULL;\n\tforeach_declared_ptr(target, targets)\n\t{\n\t\tif (IsObjectAddressOwnedByExtension(target, extensionAddress))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * FirstExtensionWithSchema returns the first extension address whose schema is the same\n * as given schema. If no extension depends on the schema, it returns NULL.\n * i.e. decide if given schema is an extension schema as in\n *  `CREATE EXTENSION <ext> [WITH] SCHEMA <schema>;`\n */\nObjectAddress *\nFirstExtensionWithSchema(Oid schemaId)\n{\n\tObjectAddress *extensionAddress = NULL;\n\n\tRelation relation = table_open(ExtensionRelationId, AccessShareLock);\n\n\tScanKeyData entry[1];\n\tScanKeyInit(&entry[0], Anum_pg_extension_extnamespace, BTEqualStrategyNumber,\n\t\t\t\tF_OIDEQ, ObjectIdGetDatum(schemaId));\n\n\tSysScanDesc scan = systable_beginscan(relation, InvalidOid, false, NULL, 1, entry);\n\tHeapTuple extensionTuple = systable_getnext(scan);\n\tif (HeapTupleIsValid(extensionTuple))\n\t{\n\t\tint extensionIdIndex = Anum_pg_extension_oid;\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\t\tbool isNull = false;\n\t\tDatum extensionIdDatum = heap_getattr(extensionTuple, extensionIdIndex,\n\t\t\t\t\t\t\t\t\t\t\t  tupleDescriptor, &isNull);\n\t\tOid extensionId = DatumGetObjectId(extensionIdDatum);\n\n\t\textensionAddress = palloc0(sizeof(ObjectAddress));\n\t\textensionAddress->objectId = extensionId;\n\t\textensionAddress->classId = ExtensionRelationId;\n\t\textensionAddress->objectSubId = 0;\n\t}\n\n\tsystable_endscan(scan);\n\ttable_close(relation, AccessShareLock);\n\n\treturn extensionAddress;\n}\n\n\n/*\n * IsObjectAddressOwnedByCitus returns true if the given object address\n * is owned by the citus or citus_columnar extensions.\n */\nbool\nIsObjectAddressOwnedByCitus(const ObjectAddress *objectAddress)\n{\n\tOid citusId = get_extension_oid(\"citus\", true);\n\tOid citusColumnarId = get_extension_oid(\"citus_columnar\", true);\n\n\t/* return false because we could not find any citus extension */\n\tif (!OidIsValid(citusId) && !OidIsValid(citusColumnarId))\n\t{\n\t\treturn false;\n\t}\n\n\tObjectAddress extObjectAddress = InvalidObjectAddress;\n\tbool ownedByExt = IsObjectAddressOwnedByExtension(objectAddress,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  &extObjectAddress);\n\tif (!ownedByExt)\n\t{\n\t\treturn false;\n\t}\n\n\tbool ownedByCitus = OidIsValid(citusId) && extObjectAddress.objectId == citusId;\n\tbool ownedByCitusColumnar = OidIsValid(citusColumnarId) &&\n\t\t\t\t\t\t\t\textObjectAddress.objectId == citusColumnarId;\n\n\treturn ownedByCitus || ownedByCitusColumnar;\n}\n\n\n/*\n * FollowNewSupportedDependencies applies filters on pg_depend entries to follow all\n * objects which should be distributed before the root object can safely be created.\n */\nstatic bool\nFollowNewSupportedDependencies(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t   DependencyDefinition *definition)\n{\n\tif (definition->mode == DependencyPgDepend)\n\t{\n\t\t/*\n\t\t *  For dependencies found in pg_depend:\n\t\t *\n\t\t *  Follow only normal and extension dependencies. The latter is used to reach the\n\t\t *  extensions, the objects that directly depend on the extension are eliminated\n\t\t *  during the \"apply\" phase.\n\t\t *\n\t\t *  Other dependencies are internal dependencies and managed by postgres.\n\t\t */\n\t\tif (definition->data.pg_depend.deptype != DEPENDENCY_NORMAL &&\n\t\t\tdefinition->data.pg_depend.deptype != DEPENDENCY_EXTENSION)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/* rest of the tests are to see if we want to follow the actual dependency */\n\tObjectAddress address = DependencyDefinitionObjectAddress(definition);\n\n\t/*\n\t * If the object is already in our dependency list we do not have to follow any\n\t * further\n\t */\n\tif (IsObjectAddressCollected(address, collector))\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * If the object is already distributed it is not a `new` object that needs to be\n\t * distributed before we create a dependent object\n\t */\n\tObjectAddress *copyAddress = palloc0(sizeof(ObjectAddress));\n\t*copyAddress = address;\n\tif (IsAnyObjectDistributed(list_make1(copyAddress)))\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * We can only distribute dependencies that citus knows how to distribute.\n\t *\n\t * But we don't want to bail out if the object is owned by extension, because\n\t * Citus can create the extension.\n\t */\n\tif (!SupportedDependencyByCitus(&address) &&\n\t\t!IsObjectAddressOwnedByExtension(&address, NULL))\n\t{\n\t\treturn false;\n\t}\n\n\tif (CitusExtensionObject(&address))\n\t{\n\t\t/* following citus extension could complicate role management */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * FollowAllSupportedDependencies applies filters on pg_depend entries to follow the\n * dependency tree of objects in depth first order. We will visit all supported objects.\n * This is used to sort a list of dependencies in dependency order.\n */\nstatic bool\nFollowAllSupportedDependencies(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t   DependencyDefinition *definition)\n{\n\tif (definition->mode == DependencyPgDepend)\n\t{\n\t\t/*\n\t\t *  For dependencies found in pg_depend:\n\t\t *\n\t\t *  Follow only normal and extension dependencies. The latter is used to reach the\n\t\t *  extensions, the objects that directly depend on the extension are eliminated\n\t\t *  during the \"apply\" phase.\n\t\t *\n\t\t *  Other dependencies are internal dependencies and managed by postgres.\n\t\t */\n\t\tif (definition->data.pg_depend.deptype != DEPENDENCY_NORMAL &&\n\t\t\tdefinition->data.pg_depend.deptype != DEPENDENCY_EXTENSION)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/* rest of the tests are to see if we want to follow the actual dependency */\n\tObjectAddress address = DependencyDefinitionObjectAddress(definition);\n\n\t/*\n\t * If the object is already in our dependency list we do not have to follow any\n\t * further\n\t */\n\tif (IsObjectAddressCollected(address, collector))\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * We can only distribute dependencies that citus knows how to distribute.\n\t *\n\t * But we don't want to bail out if the object is owned by extension, because\n\t * Citus can create the extension.\n\t */\n\tif (!SupportedDependencyByCitus(&address) &&\n\t\t!IsObjectAddressOwnedByExtension(&address, NULL))\n\t{\n\t\treturn false;\n\t}\n\n\tif (CitusExtensionObject(&address))\n\t{\n\t\t/* following citus extension could complicate role management */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * FollowAllDependencies applies filters on pg_depend entries to follow the dependency\n * tree of objects in depth first order. We will visit all objects irrespective of it is\n * supported by Citus or not.\n */\nstatic bool\nFollowAllDependencies(ObjectAddressCollector *collector,\n\t\t\t\t\t  DependencyDefinition *definition)\n{\n\tif (definition->mode == DependencyPgDepend)\n\t{\n\t\t/*\n\t\t *  For dependencies found in pg_depend:\n\t\t *\n\t\t *  Follow only normal and extension dependencies. The latter is used to reach the\n\t\t *  extensions, the objects that directly depend on the extension are eliminated\n\t\t *  during the \"apply\" phase.\n\t\t *\n\t\t *  Other dependencies are internal dependencies and managed by postgres.\n\t\t */\n\t\tif (definition->data.pg_depend.deptype != DEPENDENCY_NORMAL &&\n\t\t\tdefinition->data.pg_depend.deptype != DEPENDENCY_EXTENSION)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/* rest of the tests are to see if we want to follow the actual dependency */\n\tObjectAddress address = DependencyDefinitionObjectAddress(definition);\n\n\t/*\n\t * If the object is already in our dependency list we do not have to follow any\n\t * further\n\t */\n\tif (IsObjectAddressCollected(address, collector))\n\t{\n\t\treturn false;\n\t}\n\n\tif (CitusExtensionObject(&address))\n\t{\n\t\t/* following citus extension could complicate role management */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * FollowExtAndInternalDependencies applies filters on pg_depend entries to follow\n * the dependency tree of objects in depth first order. We will visit all objects\n * irrespective of it is supported by Citus or not and it is internal or not.\n */\nstatic bool\nFollowExtAndInternalDependencies(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t\t DependencyDefinition *definition)\n{\n\tObjectAddress address = DependencyDefinitionObjectAddress(definition);\n\n\t/*\n\t * If the object is already in our dependency list we do not have to follow any\n\t * further\n\t */\n\tif (IsObjectAddressCollected(address, collector))\n\t{\n\t\treturn false;\n\t}\n\n\tif (CitusExtensionObject(&address))\n\t{\n\t\t/*\n\t\t * We do not need to follow citus extension because the purpose\n\t\t * of our walk is to find if an object is owned by citus.\n\t\t */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ApplyAddToDependencyList is an apply function for RecurseObjectDependencies that will\n * collect all the ObjectAddresses for pg_depend entries to the context, except it is\n * extension owned one.\n *\n * The context here is assumed to be a (ObjectAddressCollector *) to the location where\n * all ObjectAddresses will be collected.\n */\nstatic void\nApplyAddToDependencyList(ObjectAddressCollector *collector,\n\t\t\t\t\t\t DependencyDefinition *definition)\n{\n\tObjectAddress address = DependencyDefinitionObjectAddress(definition);\n\n\t/*\n\t * Objects owned by an extension are assumed to be created on the workers by creating\n\t * the extension in the cluster, we we don't want explicitly create them.\n\t *\n\t * Since we do need to capture the extension as a dependency we are following the\n\t * object instead of breaking the traversal there.\n\t */\n\tif (IsObjectAddressOwnedByExtension(&address, NULL))\n\t{\n\t\treturn;\n\t}\n\n\tCollectObjectAddress(collector, &address);\n}\n\n\n/*\n * ApplyAddCitusDependedObjectsToDependencyList is an apply function for\n * RecurseObjectDependencies that will collect all the ObjectAddresses for\n * pg_depend entries to the context if it is citus extension owned one.\n *\n * The context here is assumed to be a (ObjectAddressCollector *) to the location where\n * all ObjectAddresses will be collected.\n */\nstatic void\nApplyAddCitusDependedObjectsToDependencyList(ObjectAddressCollector *collector,\n\t\t\t\t\t\t\t\t\t\t\t DependencyDefinition *definition)\n{\n\tObjectAddress address = DependencyDefinitionObjectAddress(definition);\n\n\t/*\n\t * We only collect the object if it is owned by citus extension.\n\t */\n\tif (IsObjectAddressOwnedByCitus(&address))\n\t{\n\t\tCollectObjectAddress(collector, &address);\n\t}\n}\n\n\n/*\n * ExpandCitusSupportedTypes base on supported types by citus we might want to expand\n * the list of objects to visit in pg_depend.\n *\n * An example where we want to expand is for types. Their dependencies are not captured\n * with an entry in pg_depend from their object address, but by the object address of the\n * relation describing the type.\n */\nstatic List *\nExpandCitusSupportedTypes(ObjectAddressCollector *collector, ObjectAddress target)\n{\n\tList *result = NIL;\n\n\tswitch (target.classId)\n\t{\n\t\tcase AuthIdRelationId:\n\t\t{\n\t\t\t/*\n\t\t\t * Roles are members of other roles. These relations are not recorded directly\n\t\t\t * but can be deduced from pg_auth_members\n\t\t\t */\n\t\t\treturn ExpandRolesToGroups(target.objectId);\n\t\t}\n\n\t\tcase ExtensionRelationId:\n\t\t{\n\t\t\t/*\n\t\t\t * FDWs get propagated along with the extensions they belong to.\n\t\t\t * In case there are GRANTed privileges on FDWs to roles, those\n\t\t\t * GRANT statements will be propagated to. In order to make sure\n\t\t\t * that those GRANT statements work, the privileged roles should\n\t\t\t * exist on the worker nodes. Hence, here we find these dependent\n\t\t\t * roles and add them as dependencies.\n\t\t\t */\n\n\t\t\tOid extensionId = target.objectId;\n\t\t\tList *FDWOids = GetDependentFDWsToExtension(extensionId);\n\n\t\t\tOid FDWOid = InvalidOid;\n\t\t\tforeach_declared_oid(FDWOid, FDWOids)\n\t\t\t{\n\t\t\t\tList *dependentRoleIds = GetDependentRoleIdsFDW(FDWOid);\n\t\t\t\tList *dependencies =\n\t\t\t\t\tCreateObjectAddressDependencyDefList(AuthIdRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t dependentRoleIds);\n\t\t\t\tresult = list_concat(result, dependencies);\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase TypeRelationId:\n\t\t{\n\t\t\tswitch (get_typtype(target.objectId))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * types depending on other types are not captured in pg_depend, instead\n\t\t\t\t * they are described with their dependencies by the relation that\n\t\t\t\t * describes the composite type.\n\t\t\t\t */\n\t\t\t\tcase TYPTYPE_COMPOSITE:\n\t\t\t\t{\n\t\t\t\t\tOid typeRelationId = get_typ_typrelid(target.objectId);\n\t\t\t\t\tDependencyDefinition *dependency =\n\t\t\t\t\t\tCreateObjectAddressDependencyDef(RelationRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t typeRelationId);\n\t\t\t\t\tresult = lappend(result, dependency);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * Domains can have constraints associated with them. Constraints themself\n\t\t\t\t * can depend on things like functions. To support the propagation of\n\t\t\t\t * these functions we will add the constraints to the list of objects to\n\t\t\t\t * be created.\n\t\t\t\t */\n\t\t\t\tcase TYPTYPE_DOMAIN:\n\t\t\t\t{\n\t\t\t\t\tList *dependencies =\n\t\t\t\t\t\tGetTypeConstraintDependencyDefinition(target.objectId);\n\t\t\t\t\tresult = list_concat(result, dependencies);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * array types don't have a normal dependency on their element type, instead\n\t\t\t * their dependency is an internal one. We can't follow interal dependencies\n\t\t\t * as that would cause a cyclic dependency on others, instead we expand here\n\t\t\t * to follow the dependency on the element type.\n\t\t\t */\n\t\t\tif (type_is_array(target.objectId))\n\t\t\t{\n\t\t\t\tOid typeId = get_element_type(target.objectId);\n\t\t\t\tDependencyDefinition *dependency =\n\t\t\t\t\tCreateObjectAddressDependencyDef(TypeRelationId, typeId);\n\t\t\t\tresult = lappend(result, dependency);\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase RelationRelationId:\n\t\t{\n\t\t\t/*\n\t\t\t * Triggers both depend to the relations and to the functions they\n\t\t\t * execute. Also, pg_depend records dependencies from triggers to the\n\t\t\t * functions but not from relations to their triggers. Given above two,\n\t\t\t * we directly expand depencies for the relations to trigger functions.\n\t\t\t * That way, we won't attempt to create the trigger as a dependency of\n\t\t\t * the relation, which would fail as the relation itself is not created\n\t\t\t * yet when ensuring dependencies.\n\t\t\t */\n\t\t\tOid relationId = target.objectId;\n\t\t\tList *triggerFunctionDepencyList =\n\t\t\t\tGetRelationTriggerFunctionDependencyList(relationId);\n\t\t\tresult = list_concat(result, triggerFunctionDepencyList);\n\n\t\t\t/*\n\t\t\t * Statistics' both depend to the relations and to the schemas they belong\n\t\t\t * to. Also, pg_depend records dependencies from statistics to their schemas\n\t\t\t * but not from relations to their statistics' schemas. Given above two,\n\t\t\t * we directly expand dependencies for the relations to schemas of\n\t\t\t * statistics.\n\t\t\t */\n\t\t\tList *statisticsSchemaDependencyList =\n\t\t\t\tGetRelationStatsSchemaDependencyList(relationId);\n\t\t\tresult = list_concat(result, statisticsSchemaDependencyList);\n\n\t\t\t/*\n\t\t\t * Get the dependent sequences for tables (both as serial columns and\n\t\t\t * columns have nextval with existing sequences) and expand dependency list\n\t\t\t * with them.\n\t\t\t */\n\t\t\tList *sequenceDependencyList = GetRelationSequenceDependencyList(relationId);\n\t\t\tresult = list_concat(result, sequenceDependencyList);\n\n\t\t\t/*\n\t\t\t * Get the dependent functions for tables as columns has default values\n\t\t\t * and contraints, then expand dependency list with them.\n\t\t\t */\n\t\t\tList *functionDependencyList = GetRelationFunctionDependencyList(relationId);\n\t\t\tresult = list_concat(result, functionDependencyList);\n\n\t\t\t/*\n\t\t\t * Tables could have indexes. Indexes themself could have dependencies that\n\t\t\t * need to be propagated. eg. TEXT SEARCH CONFIGURATIONS. Here we add the\n\t\t\t * addresses of all indices to the list of objects to vist, as to make sure we\n\t\t\t * create all objects required by the indices before we create the table\n\t\t\t * including indices.\n\t\t\t */\n\t\t\tList *indexDependencyList = GetRelationIndicesDependencyList(relationId);\n\t\t\tresult = list_concat(result, indexDependencyList);\n\n\t\t\t/*\n\t\t\t * Get the dependencies of the rule for the given view. PG keeps internal\n\t\t\t * dependency between view and rule. As it is stated on the PG doc, if\n\t\t\t * there is an internal dependency, dependencies of the dependent object\n\t\t\t * behave much like they were dependencies of the referenced object.\n\t\t\t *\n\t\t\t * We need to expand dependencies by including dependencies of the rule\n\t\t\t * internally dependent to the view. PG doesn't keep any dependencies\n\t\t\t * from view to any object, but it keeps an internal dependency to the\n\t\t\t * rule and that rule has dependencies to other objects.\n\t\t\t */\n\t\t\tchar relKind = get_rel_relkind(relationId);\n\t\t\tif (relKind == RELKIND_VIEW || relKind == RELKIND_MATVIEW)\n\t\t\t{\n\t\t\t\tList *ruleRefDepList = GetViewRuleReferenceDependencyList(relationId);\n\t\t\t\tresult = list_concat(result, ruleRefDepList);\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase PublicationRelationId:\n\t\t{\n\t\t\tOid publicationId = target.objectId;\n\n\t\t\t/*\n\t\t\t * Publications do not depend directly on relations, because dropping\n\t\t\t * the relation will only remove it from the publications. However,\n\t\t\t * we add a dependency to ensure the relation is created first when\n\t\t\t * adding a node.\n\t\t\t */\n\t\t\tList *relationDependencyList =\n\t\t\t\tGetPublicationRelationsDependencyList(publicationId);\n\t\t\tresult = list_concat(result, relationDependencyList);\n\n\t\t\t/*\n\t\t\t * As of PostgreSQL 15, the same applies to schemas.\n\t\t\t */\n\t\t\tList *schemaIdList =\n\t\t\t\tGetPublicationSchemas(publicationId);\n\t\t\tList *schemaDependencyList =\n\t\t\t\tCreateObjectAddressDependencyDefList(NamespaceRelationId, schemaIdList);\n\t\t\tresult = list_concat(result, schemaDependencyList);\n\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\t/* no expansion for unsupported types */\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn result;\n}\n\n\n/*\n * ExpandForPgVanilla only expands only comosite types because other types\n * will find their dependencies in pg_depend. The method should only be called by\n * is_citus_depended_object udf.\n */\nstatic List *\nExpandForPgVanilla(ObjectAddressCollector *collector,\n\t\t\t\t   ObjectAddress target)\n{\n\t/* should only be called if GUC is enabled */\n\tAssert(HideCitusDependentObjects == true);\n\n\tList *result = NIL;\n\n\tif (target.classId == TypeRelationId && get_typtype(target.objectId) ==\n\t\tTYPTYPE_COMPOSITE)\n\t{\n\t\t/*\n\t\t * types depending on other types are not captured in pg_depend, instead\n\t\t * they are described with their dependencies by the relation that\n\t\t * describes the composite type.\n\t\t */\n\t\tOid typeRelationId = get_typ_typrelid(target.objectId);\n\t\tDependencyDefinition *dependency =\n\t\t\tCreateObjectAddressDependencyDef(RelationRelationId,\n\t\t\t\t\t\t\t\t\t\t\t typeRelationId);\n\t\tresult = lappend(result, dependency);\n\t}\n\n\treturn result;\n}\n\n\n/*\n * GetDependentRoleIdsFDW returns a list of role oids that has privileges on the\n * FDW with the given object id.\n */\nstatic List *\nGetDependentRoleIdsFDW(Oid FDWOid)\n{\n\tList *roleIds = NIL;\n\n\tAcl *aclEntry = GetPrivilegesForFDW(FDWOid);\n\n\tif (aclEntry == NULL)\n\t{\n\t\treturn NIL;\n\t}\n\n\tAclItem *privileges = ACL_DAT(aclEntry);\n\tint numberOfPrivsGranted = ACL_NUM(aclEntry);\n\n\tfor (int i = 0; i < numberOfPrivsGranted; i++)\n\t{\n\t\troleIds = lappend_oid(roleIds, privileges[i].ai_grantee);\n\t}\n\n\treturn roleIds;\n}\n\n\n/*\n * ExpandRolesToGroups returns a list of object addresses pointing to roles that roleid\n * depends on.\n */\nstatic List *\nExpandRolesToGroups(Oid roleid)\n{\n\tRelation pgAuthMembers = table_open(AuthMemRelationId, AccessShareLock);\n\tHeapTuple tuple = NULL;\n\n\tScanKeyData scanKey[1];\n\tconst int scanKeyCount = 1;\n\n\t/* scan pg_auth_members for member = $1 via index pg_auth_members_member_role_index */\n\tScanKeyInit(&scanKey[0], Anum_pg_auth_members_member, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(roleid));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgAuthMembers, AuthMemMemRoleIndexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttrue, NULL, scanKeyCount, scanKey);\n\n\tList *roles = NIL;\n\twhile ((tuple = systable_getnext(scanDescriptor)) != NULL)\n\t{\n\t\tForm_pg_auth_members membership = (Form_pg_auth_members) GETSTRUCT(tuple);\n\n\t\tDependencyDefinition *definition = palloc0(sizeof(DependencyDefinition));\n\t\tdefinition->mode = DependencyObjectAddress;\n\t\tObjectAddressSet(definition->data.address, AuthIdRelationId, membership->roleid);\n\n\t\troles = lappend(roles, definition);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgAuthMembers, AccessShareLock);\n\n\treturn roles;\n}\n\n\n/*\n * GetViewRuleReferenceDependencyList returns the dependencies of the view's\n * internal rule dependencies.\n */\nstatic List *\nGetViewRuleReferenceDependencyList(Oid viewId)\n{\n\tList *dependencyTupleList = GetPgDependTuplesForDependingObjects(RelationRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t viewId);\n\tList *nonInternalDependenciesOfDependingRules = NIL;\n\n\tHeapTuple depTup = NULL;\n\tforeach_declared_ptr(depTup, dependencyTupleList)\n\t{\n\t\tForm_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);\n\n\t\t/*\n\t\t * Dependencies of the internal rule dependency should be handled as the dependency\n\t\t * of referenced view object.\n\t\t *\n\t\t * PG doesn't keep dependency relation between views and dependent objects directly\n\t\t * but it keeps an internal dependency relation between the view and the rule, then\n\t\t * keeps the dependent objects of the view as non-internal dependencies of the\n\t\t * internally dependent rule object.\n\t\t */\n\t\tif (pg_depend->deptype == DEPENDENCY_INTERNAL && pg_depend->classid ==\n\t\t\tRewriteRelationId)\n\t\t{\n\t\t\tObjectAddress ruleAddress = { 0 };\n\t\t\tObjectAddressSet(ruleAddress, RewriteRelationId, pg_depend->objid);\n\n\t\t\t/* Expand results with the noninternal dependencies of it */\n\t\t\tList *ruleDependencies = DependencyDefinitionFromPgDepend(ruleAddress);\n\n\t\t\tDependencyDefinition *dependencyDef = NULL;\n\t\t\tforeach_declared_ptr(dependencyDef, ruleDependencies)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Follow all dependencies of the internally dependent rule dependencies\n\t\t\t\t * except it is an internal dependency of view itself.\n\t\t\t\t */\n\t\t\t\tif (dependencyDef->data.pg_depend.deptype == DEPENDENCY_INTERNAL ||\n\t\t\t\t\t(dependencyDef->data.pg_depend.refclassid == RelationRelationId &&\n\t\t\t\t\t dependencyDef->data.pg_depend.refobjid == viewId))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tnonInternalDependenciesOfDependingRules =\n\t\t\t\t\tlappend(nonInternalDependenciesOfDependingRules, dependencyDef);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nonInternalDependenciesOfDependingRules;\n}\n\n\n/*\n * GetRelationSequenceDependencyList returns the sequence dependency definition\n * list for the given relation.\n */\nstatic List *\nGetRelationSequenceDependencyList(Oid relationId)\n{\n\tList *seqInfoList = NIL;\n\tGetDependentSequencesWithRelation(relationId, &seqInfoList, 0, DEPENDENCY_AUTO);\n\n\tList *seqIdList = NIL;\n\tSequenceInfo *seqInfo = NULL;\n\tforeach_declared_ptr(seqInfo, seqInfoList)\n\t{\n\t\tseqIdList = lappend_oid(seqIdList, seqInfo->sequenceOid);\n\t}\n\n\tList *sequenceDependencyDefList =\n\t\tCreateObjectAddressDependencyDefList(RelationRelationId, seqIdList);\n\n\treturn sequenceDependencyDefList;\n}\n\n\n/*\n * GetRelationFunctionDependencyList returns the function dependency definition\n * list for the given relation.\n */\nstatic List *\nGetRelationFunctionDependencyList(Oid relationId)\n{\n\tList *dependentFunctionOids = GetDependentFunctionsWithRelation(relationId);\n\tList *functionDependencyDefList =\n\t\tCreateObjectAddressDependencyDefList(ProcedureRelationId, dependentFunctionOids);\n\n\treturn functionDependencyDefList;\n}\n\n\n/*\n * GetRelationStatsSchemaDependencyList returns a list of DependencyDefinition\n * objects for the schemas that statistics' of the relation with relationId depends.\n */\nstatic List *\nGetRelationStatsSchemaDependencyList(Oid relationId)\n{\n\tList *schemaIds = GetExplicitStatisticsSchemaIdList(relationId);\n\n\treturn CreateObjectAddressDependencyDefList(NamespaceRelationId, schemaIds);\n}\n\n\n/*\n * CollectIndexOids implements PGIndexProcessor to create a list of all index oids\n */\nstatic void\nCollectIndexOids(Form_pg_index formPgIndex, List **oids, int flags)\n{\n\t*oids = lappend_oid(*oids, formPgIndex->indexrelid);\n}\n\n\n/*\n * GetRelationIndicesDependencyList creates a list of ObjectAddressDependencies for the\n * indexes on a given relation.\n */\nstatic List *\nGetRelationIndicesDependencyList(Oid relationId)\n{\n\tList *indexIds = ExecuteFunctionOnEachTableIndex(relationId, CollectIndexOids, 0);\n\treturn CreateObjectAddressDependencyDefList(RelationRelationId, indexIds);\n}\n\n\n/*\n * GetRelationTriggerFunctionDependencyList returns a list of DependencyDefinition\n * objects for the functions that triggers of the relation with relationId depends.\n */\nstatic List *\nGetRelationTriggerFunctionDependencyList(Oid relationId)\n{\n\tList *dependencyList = NIL;\n\n\tList *triggerIdList = GetExplicitTriggerIdList(relationId);\n\tOid triggerId = InvalidOid;\n\tforeach_declared_oid(triggerId, triggerIdList)\n\t{\n\t\tOid functionId = GetTriggerFunctionId(triggerId);\n\t\tDependencyDefinition *dependency =\n\t\t\tCreateObjectAddressDependencyDef(ProcedureRelationId, functionId);\n\t\tdependencyList = lappend(dependencyList, dependency);\n\t}\n\n\treturn dependencyList;\n}\n\n\n/*\n * GetPublicationRelationsDependencyList creates a list of ObjectAddressDependencies for\n * a publication on the Citus relations it contains. This helps make sure we distribute\n * Citus tables before local tables.\n */\nstatic List *\nGetPublicationRelationsDependencyList(Oid publicationId)\n{\n\tList *allRelationIds = GetPublicationRelations(publicationId, PUBLICATION_PART_ROOT);\n\tList *citusRelationIds = NIL;\n\n\tOid relationId = InvalidOid;\n\n\tforeach_declared_oid(relationId, allRelationIds)\n\t{\n\t\tif (!IsCitusTable(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tcitusRelationIds = lappend_oid(citusRelationIds, relationId);\n\t}\n\n\treturn CreateObjectAddressDependencyDefList(RelationRelationId, citusRelationIds);\n}\n\n\n/*\n * GetTypeConstraintDependencyDefinition creates a list of constraint dependency\n * definitions for a given type\n */\nstatic List *\nGetTypeConstraintDependencyDefinition(Oid typeId)\n{\n\t/* lookup and look all constraints to add them to the CreateDomainStmt */\n\tRelation conRel = table_open(ConstraintRelationId, AccessShareLock);\n\n\t/* Look for CHECK Constraints on this domain */\n\tScanKeyData key[1];\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_constraint_contypid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(typeId));\n\n\tSysScanDesc scan = systable_beginscan(conRel, ConstraintTypidIndexId, true, NULL, 1,\n\t\t\t\t\t\t\t\t\t\t  key);\n\n\tList *dependencies = NIL;\n\tHeapTuple conTup = NULL;\n\twhile (HeapTupleIsValid(conTup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);\n\n\t\tif (c->contype != CONSTRAINT_CHECK)\n\t\t{\n\t\t\t/* Ignore non-CHECK constraints, shouldn't be any */\n\t\t\tcontinue;\n\t\t}\n\n\t\tdependencies = lappend(dependencies, CreateObjectAddressDependencyDef(\n\t\t\t\t\t\t\t\t   ConstraintRelationId, c->oid));\n\t}\n\n\tsystable_endscan(scan);\n\ttable_close(conRel, NoLock);\n\n\treturn dependencies;\n}\n\n\n/*\n * CreateObjectAddressDependencyDef returns DependencyDefinition object that\n * stores the ObjectAddress for the database object identified by classId and\n * objectId.\n */\nstatic DependencyDefinition *\nCreateObjectAddressDependencyDef(Oid classId, Oid objectId)\n{\n\tDependencyDefinition *dependency = palloc0(sizeof(DependencyDefinition));\n\tdependency->mode = DependencyObjectAddress;\n\tObjectAddressSet(dependency->data.address, classId, objectId);\n\treturn dependency;\n}\n\n\n/*\n * CreateObjectAddressDependencyDefList is a wrapper function for\n * CreateObjectAddressDependencyDef to operate on a list of relation oids,\n * instead of a single oid.\n */\nstatic List *\nCreateObjectAddressDependencyDefList(Oid classId, List *objectIdList)\n{\n\tList *dependencyList = NIL;\n\tOid objectId = InvalidOid;\n\tforeach_declared_oid(objectId, objectIdList)\n\t{\n\t\tDependencyDefinition *dependency =\n\t\t\tCreateObjectAddressDependencyDef(classId, objectId);\n\t\tdependencyList = lappend(dependencyList, dependency);\n\t}\n\n\treturn dependencyList;\n}\n\n\n/*\n * DependencyDefinitionObjectAddress returns the object address of the dependency defined\n * by the dependency definition, irregardless what the source of the definition is\n */\nstatic ObjectAddress\nDependencyDefinitionObjectAddress(DependencyDefinition *definition)\n{\n\tswitch (definition->mode)\n\t{\n\t\tcase DependencyObjectAddress:\n\t\t{\n\t\t\treturn definition->data.address;\n\t\t}\n\n\t\tcase DependencyPgDepend:\n\t\t{\n\t\t\tObjectAddress address = { 0 };\n\t\t\tObjectAddressSet(address,\n\t\t\t\t\t\t\t definition->data.pg_depend.refclassid,\n\t\t\t\t\t\t\t definition->data.pg_depend.refobjid);\n\t\t\treturn address;\n\t\t}\n\n\t\tcase DependencyPgShDepend:\n\t\t{\n\t\t\tObjectAddress address = { 0 };\n\t\t\tObjectAddressSet(address,\n\t\t\t\t\t\t\t definition->data.pg_shdepend.refclassid,\n\t\t\t\t\t\t\t definition->data.pg_shdepend.refobjid);\n\t\t\treturn address;\n\t\t}\n\t}\n\n\tereport(ERROR, (errmsg(\"unsupported dependency definition mode\")));\n}\n\n\n/*\n * BuildViewDependencyGraph gets a relation (or a view) and builds a dependency graph for the\n * depending views.\n */\nstatic ViewDependencyNode *\nBuildViewDependencyGraph(Oid relationId, HTAB *nodeMap)\n{\n\tbool found = false;\n\tViewDependencyNode *node = (ViewDependencyNode *) hash_search(nodeMap, &relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  HASH_ENTER, &found);\n\n\tif (found)\n\t{\n\t\treturn node;\n\t}\n\n\tnode->id = relationId;\n\tnode->remainingDependencyCount = 0;\n\tnode->dependingNodes = NIL;\n\n\tOid targetObjectClassId = RelationRelationId;\n\tOid targetObjectId = relationId;\n\tList *dependencyTupleList = GetPgDependTuplesForDependingObjects(targetObjectClassId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t targetObjectId);\n\n\tHeapTuple depTup = NULL;\n\tforeach_declared_ptr(depTup, dependencyTupleList)\n\t{\n\t\tForm_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);\n\n\t\tOid dependingView = GetDependingView(pg_depend);\n\t\tif (dependingView != InvalidOid)\n\t\t{\n\t\t\tViewDependencyNode *dependingNode = BuildViewDependencyGraph(dependingView,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t nodeMap);\n\n\t\t\tnode->dependingNodes = lappend(node->dependingNodes, dependingNode);\n\t\t\tdependingNode->remainingDependencyCount++;\n\t\t}\n\t}\n\n\treturn node;\n}\n\n\n/*\n * GetPgDependTuplesForDependingObjects scans pg_depend for given object and\n * returns a list of heap tuples for the objects depending on it.\n */\nList *\nGetPgDependTuplesForDependingObjects(Oid targetObjectClassId, Oid targetObjectId)\n{\n\tList *dependencyTupleList = NIL;\n\n\tRelation pgDepend = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyData key[2];\n\tint scanKeyCount = 2;\n\n\tScanKeyInit(&key[0], Anum_pg_depend_refclassid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(targetObjectClassId));\n\tScanKeyInit(&key[1], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(targetObjectId));\n\n\tbool useIndex = true;\n\tSysScanDesc depScan = systable_beginscan(pgDepend, DependReferenceIndexId,\n\t\t\t\t\t\t\t\t\t\t\t useIndex, NULL, scanKeyCount, key);\n\n\tHeapTuple dependencyTuple = NULL;\n\twhile (HeapTupleIsValid(dependencyTuple = systable_getnext(depScan)))\n\t{\n\t\t/* copy the tuple first */\n\t\tdependencyTuple = heap_copytuple(dependencyTuple);\n\t\tdependencyTupleList = lappend(dependencyTupleList, dependencyTuple);\n\t}\n\n\tsystable_endscan(depScan);\n\trelation_close(pgDepend, NoLock);\n\n\treturn dependencyTupleList;\n}\n\n\n/*\n * GetDependingViews takes a relation id, finds the views that depend on the relation\n * and returns list of the oids of those views. It recurses on the pg_depend table to\n * find the views that recursively depend on the table.\n *\n * The returned views will have the correct order for creating them, from the point of\n * dependencies between.\n */\nList *\nGetDependingViews(Oid relationId)\n{\n\tHTAB *nodeMap = CreateSimpleHashWithName(Oid, ViewDependencyNode,\n\t\t\t\t\t\t\t\t\t\t\t \"view dependency map (oid)\");\n\n\tViewDependencyNode *tableNode = BuildViewDependencyGraph(relationId, nodeMap);\n\n\tList *dependingViews = NIL;\n\tList *nodeQueue = list_make1(tableNode);\n\tViewDependencyNode *node = NULL;\n\tforeach_ptr_append(node, nodeQueue)\n\t{\n\t\tViewDependencyNode *dependingNode = NULL;\n\t\tforeach_declared_ptr(dependingNode, node->dependingNodes)\n\t\t{\n\t\t\tObjectAddress relationAddress = { 0 };\n\t\t\tObjectAddressSet(relationAddress, RelationRelationId, dependingNode->id);\n\n\t\t\t/*\n\t\t\t * This function does not catch views with circular dependencies,\n\t\t\t * because of the remaining dependency count check below.\n\t\t\t * Here we check if the view has a circular dependency or not.\n\t\t\t * If yes, we error out with a message that tells the user that\n\t\t\t * Citus does not handle circular dependencies.\n\t\t\t */\n\t\t\tDeferredErrorMessage *depError =\n\t\t\t\tDeferErrorIfCircularDependencyExists(&relationAddress);\n\t\t\tif (depError != NULL)\n\t\t\t{\n\t\t\t\tRaiseDeferredError(depError, ERROR);\n\t\t\t}\n\n\t\t\tdependingNode->remainingDependencyCount--;\n\t\t\tif (dependingNode->remainingDependencyCount == 0)\n\t\t\t{\n\t\t\t\tnodeQueue = lappend(nodeQueue, dependingNode);\n\t\t\t\tdependingViews = lappend_oid(dependingViews, dependingNode->id);\n\t\t\t}\n\t\t}\n\t}\n\treturn dependingViews;\n}\n\n\n/*\n * GetDependingView gets a row of pg_depend and returns the oid of the view that is depended.\n * If the depended object is not a rewrite object, the object to rewrite is not a view or it\n * is the same view with the depending one InvalidOid is returned.\n */\nOid\nGetDependingView(Form_pg_depend pg_depend)\n{\n\tif (pg_depend->classid != RewriteRelationId)\n\t{\n\t\treturn InvalidOid;\n\t}\n\n\tRelation rewriteRel = table_open(RewriteRelationId, AccessShareLock);\n\tScanKeyData rkey[1];\n\n\tScanKeyInit(&rkey[0],\n\t\t\t\tAnum_pg_rewrite_oid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(pg_depend->objid));\n\n\tSysScanDesc rscan = systable_beginscan(rewriteRel, RewriteOidIndexId,\n\t\t\t\t\t\t\t\t\t\t   true, NULL, 1, rkey);\n\n\tHeapTuple rewriteTup = systable_getnext(rscan);\n\tif (!HeapTupleIsValid(rewriteTup))\n\t{\n\t\t/*\n\t\t * This function already verified that objid's classid is\n\t\t * RewriteRelationId, so it should exists. But be on the\n\t\t * safe side.\n\t\t */\n\t\tereport(ERROR, (errmsg(\"catalog lookup failed for view %u\",\n\t\t\t\t\t\t\t   pg_depend->objid)));\n\t}\n\n\tForm_pg_rewrite pg_rewrite = (Form_pg_rewrite) GETSTRUCT(rewriteTup);\n\n\tbool isView = get_rel_relkind(pg_rewrite->ev_class) == RELKIND_VIEW;\n\tbool isMatView = get_rel_relkind(pg_rewrite->ev_class) == RELKIND_MATVIEW;\n\tbool isDifferentThanRef = pg_rewrite->ev_class != pg_depend->refobjid;\n\n\tOid dependingView = InvalidOid;\n\tif ((isView || isMatView) && isDifferentThanRef)\n\t{\n\t\tdependingView = pg_rewrite->ev_class;\n\t}\n\n\tsystable_endscan(rscan);\n\trelation_close(rewriteRel, AccessShareLock);\n\n\treturn dependingView;\n}\n"
  },
  {
    "path": "src/backend/distributed/metadata/distobject.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * distobject.c\n *\t  Functions to interact with distributed objects by their ObjectAddress\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/skey.h\"\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/objectaddress.h\"\n#include \"catalog/pg_database.h\"\n#include \"catalog/pg_extension_d.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/dbcommands.h\"\n#include \"commands/extension.h\"\n#include \"executor/spi.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/pg_list.h\"\n#include \"parser/parse_type.h\"\n#include \"postmaster/postmaster.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/regproc.h\"\n#include \"utils/rel.h\"\n\n#include \"citus_version.h\"\n#include \"pg_version_constants.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata/pg_dist_object.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_transaction.h\"\n\nstatic char * CreatePgDistObjectEntryCommand(const ObjectAddress *objectAddress,\n\t\t\t\t\t\t\t\t\t\t\t char *objectName);\nstatic int ExecuteCommandAsSuperuser(char *query, int paramCount, Oid *paramTypes,\n\t\t\t\t\t\t\t\t\t Datum *paramValues);\nstatic bool IsObjectDistributed(const ObjectAddress *address);\n\nPG_FUNCTION_INFO_V1(mark_object_distributed);\nPG_FUNCTION_INFO_V1(citus_unmark_object_distributed);\nPG_FUNCTION_INFO_V1(master_unmark_object_distributed);\n\n\n/*\n * mark_object_distributed adds an object to pg_dist_object\n * in all of the nodes, for the connections to the other nodes this function\n * uses the user passed.\n */\nDatum\nmark_object_distributed(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureSuperUser();\n\n\tOid classId = PG_GETARG_OID(0);\n\ttext *objectNameText = PG_GETARG_TEXT_P(1);\n\tchar *objectName = text_to_cstring(objectNameText);\n\tOid objectId = PG_GETARG_OID(2);\n\tObjectAddress *objectAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*objectAddress, classId, objectId);\n\ttext *connectionUserText = PG_GETARG_TEXT_P(3);\n\tchar *connectionUser = text_to_cstring(connectionUserText);\n\n\t/*\n\t * This function is called when a query is run from a Citus non-main database.\n\t * We need to insert into local pg_dist_object over a connection to make sure\n\t * 2PC still works.\n\t */\n\tbool useConnectionForLocalQuery = true;\n\tMarkObjectDistributedWithName(objectAddress, objectName, useConnectionForLocalQuery,\n\t\t\t\t\t\t\t\t  connectionUser);\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_unmark_object_distributed(classid oid, objid oid, objsubid int,checkobjectexistence bool)\n *\n * Removes the entry for an object address from pg_dist_object. If checkobjectexistence is true,\n * throws an error if the object still exists.\n */\nDatum\ncitus_unmark_object_distributed(PG_FUNCTION_ARGS)\n{\n\tOid classid = PG_GETARG_OID(0);\n\tOid objid = PG_GETARG_OID(1);\n\tint32 objsubid = PG_GETARG_INT32(2);\n\n\t/*\n\t * SQL function master_unmark_object_distributed doesn't expect the\n\t * 4th argument but SQL function citus_unmark_object_distributed does\n\t * so as checkobjectexistence argument. For this reason, we try to\n\t * get the 4th argument only if this C function is called with 4\n\t * arguments.\n\t */\n\tbool checkObjectExistence = true;\n\tif (PG_NARGS() == 4)\n\t{\n\t\tcheckObjectExistence = PG_GETARG_BOOL(3);\n\t}\n\n\tObjectAddress address = { 0 };\n\tObjectAddressSubSet(address, classid, objid, objsubid);\n\n\tif (!IsObjectDistributed(&address))\n\t{\n\t\t/* if the object is not distributed there is no need to unmark it */\n\t\tPG_RETURN_VOID();\n\t}\n\n\tif (checkObjectExistence && ObjectExists(&address))\n\t{\n\t\tereport(ERROR, (errmsg(\"object still exists\"),\n\t\t\t\t\t\terrdetail(\"the %s \\\"%s\\\" still exists\",\n\t\t\t\t\t\t\t\t  getObjectTypeDescription(&address,\n\n\t\t                                                   /* missingOk: */ false),\n\t\t\t\t\t\t\t\t  getObjectIdentity(&address,\n\n\t\t                                            /* missingOk: */ false)),\n\t\t\t\t\t\terrhint(\"drop the object via a DROP command\")));\n\t}\n\n\tUnmarkObjectDistributed(&address);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * master_unmark_object_distributed is a wrapper function for old UDF name.\n */\nDatum\nmaster_unmark_object_distributed(PG_FUNCTION_ARGS)\n{\n\treturn citus_unmark_object_distributed(fcinfo);\n}\n\n\n/*\n * ObjectExists checks if an object given by its object address exists\n *\n * This is done by opening the catalog for the object and search the catalog for the\n * objects' oid. If we can find a tuple the object is existing. If no tuple is found, or\n * we don't have the information to find the tuple by its oid we assume the object does\n * not exist.\n */\nbool\nObjectExists(const ObjectAddress *address)\n{\n\tif (address == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (is_objectclass_supported(address->classId))\n\t{\n\t\tRelation catalog = table_open(address->classId, AccessShareLock);\n\n\t\tHeapTuple objtup = get_catalog_object_by_oid(catalog, get_object_attnum_oid(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t address->classId),\n\t\t\t\t\t\t\t\t\t\t\t\t\t address->objectId);\n\t\ttable_close(catalog, AccessShareLock);\n\t\tif (objtup != NULL)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * MarkObjectDistributed marks an object as a distributed object. Marking is done\n * by adding appropriate entries to citus.pg_dist_object and also marking the object\n * as distributed by opening a connection using current user to all remote nodes\n * with metadata if object propagation is on.\n *\n * This function should be used if the user creating the given object. If you want\n * to mark dependent objects as distributed check MarkObjectDistributedViaSuperUser.\n */\nvoid\nMarkObjectDistributed(const ObjectAddress *distAddress)\n{\n\tbool useConnectionForLocalQuery = false;\n\tMarkObjectDistributedWithName(distAddress, \"\", useConnectionForLocalQuery,\n\t\t\t\t\t\t\t\t  CurrentUserName());\n}\n\n\n/*\n * MarkObjectDistributedWithName marks an object as a distributed object.\n * Same as MarkObjectDistributed but this function also allows passing an objectName\n * that is used in case the object does not exists for the current transaction.\n */\nvoid\nMarkObjectDistributedWithName(const ObjectAddress *distAddress, char *objectName,\n\t\t\t\t\t\t\t  bool useConnectionForLocalQuery, char *connectionUser)\n{\n\tif (!CitusHasBeenLoaded())\n\t{\n\t\telog(ERROR, \"Cannot mark object distributed because Citus has not been loaded.\");\n\t}\n\n\t/*\n\t * When a query is run from a Citus non-main database we need to insert into pg_dist_object\n\t * over a connection to make sure 2PC still works.\n\t */\n\tif (useConnectionForLocalQuery)\n\t{\n\t\tStringInfo insertQuery = makeStringInfo();\n\t\tappendStringInfo(insertQuery,\n\t\t\t\t\t\t \"INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid)\"\n\t\t\t\t\t\t \"VALUES (%d, %d, %d) ON CONFLICT DO NOTHING\",\n\t\t\t\t\t\t distAddress->classId, distAddress->objectId,\n\t\t\t\t\t\t distAddress->objectSubId);\n\t\tSendCommandToWorker(LocalHostName, PostPortNumber, insertQuery->data);\n\t}\n\telse\n\t{\n\t\tMarkObjectDistributedLocally(distAddress);\n\t}\n\n\tif (EnableMetadataSync)\n\t{\n\t\tchar *workerPgDistObjectUpdateCommand =\n\t\t\tCreatePgDistObjectEntryCommand(distAddress, objectName);\n\t\tSendCommandToRemoteMetadataNodesParams(workerPgDistObjectUpdateCommand,\n\t\t\t\t\t\t\t\t\t\t\t   connectionUser, 0, NULL, NULL);\n\t}\n}\n\n\n/*\n * MarkObjectDistributedViaSuperUser marks an object as a distributed object. Marking\n * is done by adding appropriate entries to citus.pg_dist_object and also marking the\n * object as distributed by opening a connection using super user to all remote nodes\n * with metadata if object propagation is on.\n *\n * This function should be used to mark dependent object as distributed. If you want\n * to mark the object you are creating please check MarkObjectDistributed.\n */\nvoid\nMarkObjectDistributedViaSuperUser(const ObjectAddress *distAddress)\n{\n\tMarkObjectDistributedLocally(distAddress);\n\n\tif (EnableMetadataSync)\n\t{\n\t\tchar *workerPgDistObjectUpdateCommand =\n\t\t\tCreatePgDistObjectEntryCommand(distAddress, \"\");\n\t\tSendCommandToRemoteNodesWithMetadataViaSuperUser(workerPgDistObjectUpdateCommand);\n\t}\n}\n\n\n/*\n * MarkObjectDistributedLocally marks an object as a distributed object by citus.\n * Marking is done by adding appropriate entries to citus.pg_dist_object.\n *\n * This function should never be called alone, MarkObjectDistributed() or\n * MarkObjectDistributedViaSuperUser() should be called.\n */\nvoid\nMarkObjectDistributedLocally(const ObjectAddress *distAddress)\n{\n\tint paramCount = 3;\n\tOid paramTypes[3] = {\n\t\tOIDOID,\n\t\tOIDOID,\n\t\tINT4OID\n\t};\n\tDatum paramValues[3] = {\n\t\tObjectIdGetDatum(distAddress->classId),\n\t\tObjectIdGetDatum(distAddress->objectId),\n\t\tInt32GetDatum(distAddress->objectSubId)\n\t};\n\tchar *insertQuery =\n\t\t\"INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid) \"\n\t\t\"VALUES ($1, $2, $3) ON CONFLICT DO NOTHING\";\n\tint spiStatus = ExecuteCommandAsSuperuser(insertQuery, paramCount, paramTypes,\n\t\t\t\t\t\t\t\t\t\t\t  paramValues);\n\tif (spiStatus < 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"failed to insert object into citus.pg_dist_object\")));\n\t}\n}\n\n\n/*\n * ShouldMarkRelationDistributed is a helper function that\n * decides whether the input relation should be marked as distributed.\n */\nbool\nShouldMarkRelationDistributed(Oid relationId)\n{\n\tif (!EnableMetadataSync)\n\t{\n\t\t/*\n\t\t * Just in case anything goes wrong, we should still be able\n\t\t * to continue to the version upgrade.\n\t\t */\n\t\treturn false;\n\t}\n\n\tObjectAddress *relationAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*relationAddress, RelationRelationId, relationId);\n\n\tbool pgObject = (relationId < FirstNormalObjectId);\n\tbool isObjectSupported = SupportedDependencyByCitus(relationAddress);\n\tbool ownedByExtension = IsTableOwnedByExtension(relationId);\n\tbool alreadyDistributed = IsObjectDistributed(relationAddress);\n\tbool hasUnsupportedDependency =\n\t\tDeferErrorIfAnyObjectHasUnsupportedDependency(\n\t\t\tlist_make1(relationAddress)) != NULL;\n\tbool hasCircularDependency =\n\t\tDeferErrorIfCircularDependencyExists(relationAddress) != NULL;\n\n\t/*\n\t * pgObject: Citus never marks pg objects as distributed\n\t * isObjectSupported: Citus does not support propagation of some objects\n\t * ownedByExtension: let extensions manage its own objects\n\t * alreadyDistributed: most likely via earlier versions\n\t * hasUnsupportedDependency: Citus doesn't know how to distribute its dependencies\n\t * hasCircularDependency: Citus cannot handle circular dependencies\n\t */\n\tif (pgObject || !isObjectSupported || ownedByExtension || alreadyDistributed ||\n\t\thasUnsupportedDependency || hasCircularDependency)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * CreatePgDistObjectEntryCommand creates command to insert pg_dist_object tuple\n * for the given object address.\n */\nstatic char *\nCreatePgDistObjectEntryCommand(const ObjectAddress *objectAddress, char *objectName)\n{\n\t/* create a list by adding the address of value to not to have warning */\n\tList *objectAddressList =\n\t\tlist_make1((ObjectAddress *) objectAddress);\n\n\t/* names also require a list so we create a nested list here */\n\tList *objectNameList = list_make1(list_make1((char *) objectName));\n\tList *distArgumetIndexList = list_make1_int(INVALID_DISTRIBUTION_ARGUMENT_INDEX);\n\tList *colocationIdList = list_make1_int(INVALID_COLOCATION_ID);\n\tList *forceDelegationList = list_make1_int(NO_FORCE_PUSHDOWN);\n\n\tchar *workerPgDistObjectUpdateCommand =\n\t\tMarkObjectsDistributedCreateCommand(objectAddressList,\n\t\t\t\t\t\t\t\t\t\t\tobjectNameList,\n\t\t\t\t\t\t\t\t\t\t\tdistArgumetIndexList,\n\t\t\t\t\t\t\t\t\t\t\tcolocationIdList,\n\t\t\t\t\t\t\t\t\t\t\tforceDelegationList);\n\n\treturn workerPgDistObjectUpdateCommand;\n}\n\n\n/*\n * CitusExtensionObject returns true if the objectAddress represents\n * the Citus extension.\n */\nbool\nCitusExtensionObject(const ObjectAddress *objectAddress)\n{\n\tif (objectAddress->classId != ExtensionRelationId)\n\t{\n\t\treturn false;\n\t}\n\n\tchar *extensionName = get_extension_name(objectAddress->objectId);\n\tif (extensionName != NULL &&\n\t\tstrncasecmp(extensionName, \"citus\", NAMEDATALEN) == 0)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ExecuteCommandAsSuperuser executes a command via SPI as superuser. Using this\n * function (and in general SPI/SQL with superuser) should be avoided as much as\n * possible. This is to prevent any user to exploit the superuser access via\n * triggers.\n */\nstatic int\nExecuteCommandAsSuperuser(char *query, int paramCount, Oid *paramTypes,\n\t\t\t\t\t\t  Datum *paramValues)\n{\n\tOid savedUserId = InvalidOid;\n\tint savedSecurityContext = 0;\n\n\tint spiConnected = SPI_connect();\n\tif (spiConnected != SPI_OK_CONNECT)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not connect to SPI manager\")));\n\t}\n\n\t/* make sure we have write access */\n\tGetUserIdAndSecContext(&savedUserId, &savedSecurityContext);\n\tSetUserIdAndSecContext(CitusExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE);\n\n\tint spiStatus = SPI_execute_with_args(query, paramCount, paramTypes, paramValues,\n\t\t\t\t\t\t\t\t\t\t  NULL, false, 0);\n\n\tSetUserIdAndSecContext(savedUserId, savedSecurityContext);\n\n\tint spiFinished = SPI_finish();\n\tif (spiFinished != SPI_OK_FINISH)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not disconnect from SPI manager\")));\n\t}\n\n\treturn spiStatus;\n}\n\n\n/*\n * UnmarkNodeWideObjectsDistributed deletes pg_dist_object records\n * for all distributed objects in given Drop stmt node.\n *\n * Today we only expect DropRoleStmt and DropdbStmt to get here.\n */\nvoid\nUnmarkNodeWideObjectsDistributed(Node *node)\n{\n\tif (IsA(node, DropRoleStmt))\n\t{\n\t\tDropRoleStmt *stmt = castNode(DropRoleStmt, node);\n\t\tList *allDropRoles = stmt->roles;\n\n\t\tList *distributedDropRoles = FilterDistributedRoles(allDropRoles);\n\t\tif (list_length(distributedDropRoles) > 0)\n\t\t{\n\t\t\tUnmarkRolesDistributed(distributedDropRoles);\n\t\t}\n\t}\n\telse if (IsA(node, DropdbStmt))\n\t{\n\t\tDropdbStmt *stmt = castNode(DropdbStmt, node);\n\t\tchar *dbName = stmt->dbname;\n\n\t\tOid dbOid = get_database_oid(dbName, stmt->missing_ok);\n\t\tObjectAddress *dbObjectAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*dbObjectAddress, DatabaseRelationId, dbOid);\n\t\tif (IsAnyObjectDistributed(list_make1(dbObjectAddress)))\n\t\t{\n\t\t\tUnmarkObjectDistributed(dbObjectAddress);\n\t\t}\n\t}\n}\n\n\n/*\n * UnmarkObjectDistributed removes the entry from pg_dist_object that marks this object as\n * distributed. This will prevent updates to that object to be propagated to the worker.\n */\nvoid\nUnmarkObjectDistributed(const ObjectAddress *address)\n{\n\tint paramCount = 3;\n\tOid paramTypes[3] = {\n\t\tOIDOID,\n\t\tOIDOID,\n\t\tINT4OID\n\t};\n\tDatum paramValues[3] = {\n\t\tObjectIdGetDatum(address->classId),\n\t\tObjectIdGetDatum(address->objectId),\n\t\tInt32GetDatum(address->objectSubId)\n\t};\n\n\tchar *deleteQuery = \"DELETE FROM pg_catalog.pg_dist_object WHERE classid = $1 AND \"\n\t\t\t\t\t\t\"objid = $2 AND objsubid = $3\";\n\n\tint spiStatus = ExecuteCommandAsSuperuser(deleteQuery, paramCount, paramTypes,\n\t\t\t\t\t\t\t\t\t\t\t  paramValues);\n\tif (spiStatus < 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"failed to delete object from citus.pg_dist_object\")));\n\t}\n}\n\n\n/*\n * IsObjectDistributed returns if the object addressed is already distributed in the\n * cluster. This performs a local indexed lookup in pg_dist_object.\n */\nstatic bool\nIsObjectDistributed(const ObjectAddress *address)\n{\n\tScanKeyData key[3];\n\tbool result = false;\n\n\tRelation pgDistObjectRel = table_open(DistObjectRelationId(), AccessShareLock);\n\n\t/* scan pg_dist_object for classid = $1 AND objid = $2 AND objsubid = $3 via index */\n\tScanKeyInit(&key[0], Anum_pg_dist_object_classid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(address->classId));\n\tScanKeyInit(&key[1], Anum_pg_dist_object_objid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(address->objectId));\n\tScanKeyInit(&key[2], Anum_pg_dist_object_objsubid, BTEqualStrategyNumber, F_INT4EQ,\n\t\t\t\tInt32GetDatum(address->objectSubId));\n\tSysScanDesc pgDistObjectScan = systable_beginscan(pgDistObjectRel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  DistObjectPrimaryKeyIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  true, NULL, 3, key);\n\n\tHeapTuple pgDistObjectTup = systable_getnext(pgDistObjectScan);\n\tif (HeapTupleIsValid(pgDistObjectTup))\n\t{\n\t\tresult = true;\n\t}\n\n\tsystable_endscan(pgDistObjectScan);\n\trelation_close(pgDistObjectRel, AccessShareLock);\n\n\treturn result;\n}\n\n\n/*\n * IsAnyObjectDistributed iteratively calls IsObjectDistributed for given addresses to\n * determine if any object is distributed.\n */\nbool\nIsAnyObjectDistributed(const List *addresses)\n{\n\tObjectAddress *address = NULL;\n\tforeach_declared_ptr(address, addresses)\n\t{\n\t\tif (IsObjectDistributed(address))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * IsAnyParentObjectDistributed - true if at least one of the\n * given addresses is distributed. If an address has a non-zero\n * objectSubId, it checks the parent object (the object with\n * the same classId and objid, but with objectSubId = 0). For\n * example, a column address will check the table address.\n * If the address has a zero objectSubId, it checks the address\n * itself.\n */\nbool\nIsAnyParentObjectDistributed(const List *addresses)\n{\n\tbool isDistributed = false;\n\tListCell *lc = NULL;\n\tforeach(lc, addresses)\n\t{\n\t\tObjectAddress *address = (ObjectAddress *) lfirst(lc);\n\t\tint32 savedObjectSubId = address->objectSubId;\n\t\taddress->objectSubId = 0;\n\t\tisDistributed = IsObjectDistributed(address);\n\t\taddress->objectSubId = savedObjectSubId;\n\n\t\tif (isDistributed)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn isDistributed;\n}\n\n\n/*\n * GetDistributedObjectAddressList returns a list of ObjectAddresses that contains all\n * distributed objects as marked in pg_dist_object\n */\nList *\nGetDistributedObjectAddressList(void)\n{\n\tHeapTuple pgDistObjectTup = NULL;\n\tList *objectAddressList = NIL;\n\n\tRelation pgDistObjectRel = table_open(DistObjectRelationId(), AccessShareLock);\n\tSysScanDesc pgDistObjectScan = systable_beginscan(pgDistObjectRel, InvalidOid, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL, 0,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL);\n\twhile (HeapTupleIsValid(pgDistObjectTup = systable_getnext(pgDistObjectScan)))\n\t{\n\t\tForm_pg_dist_object pg_dist_object =\n\t\t\t(Form_pg_dist_object) GETSTRUCT(pgDistObjectTup);\n\t\tObjectAddress *objectAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSubSet(*objectAddress,\n\t\t\t\t\t\t\tpg_dist_object->classid,\n\t\t\t\t\t\t\tpg_dist_object->objid,\n\t\t\t\t\t\t\tpg_dist_object->objsubid);\n\t\tobjectAddressList = lappend(objectAddressList, objectAddress);\n\t}\n\n\tsystable_endscan(pgDistObjectScan);\n\trelation_close(pgDistObjectRel, AccessShareLock);\n\n\treturn objectAddressList;\n}\n\n\n/*\n * GetRoleSpecObjectForUser creates a RoleSpec object for the given roleOid.\n */\nRoleSpec *\nGetRoleSpecObjectForUser(Oid roleOid)\n{\n\tRoleSpec *roleSpec = makeNode(RoleSpec);\n\troleSpec->roletype = OidIsValid(roleOid) ? ROLESPEC_CSTRING : ROLESPEC_PUBLIC;\n\troleSpec->rolename = OidIsValid(roleOid) ? GetUserNameFromId(roleOid, false) : NULL;\n\troleSpec->location = -1;\n\n\treturn roleSpec;\n}\n\n\n/*\n * UpdateDistributedObjectColocationId gets an old and a new colocationId\n * and updates the colocationId of all tuples in citus.pg_dist_object which\n * have the old colocationId to the new colocationId.\n */\nvoid\nUpdateDistributedObjectColocationId(uint32 oldColocationId,\n\t\t\t\t\t\t\t\t\tuint32 newColocationId)\n{\n\tconst bool indexOK = false;\n\tScanKeyData scanKey[1];\n\tRelation pgDistObjectRel = table_open(DistObjectRelationId(),\n\t\t\t\t\t\t\t\t\t\t  RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistObjectRel);\n\n\t/* scan pg_dist_object for colocationId equal to old colocationId */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_object_colocationid,\n\t\t\t\tBTEqualStrategyNumber,\n\t\t\t\tF_INT4EQ, Int32GetDatum(oldColocationId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistObjectRel,\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, 1, scanKey);\n\tHeapTuple heapTuple;\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tDatum *values = palloc0(tupleDescriptor->natts * sizeof(Datum));\n\t\tbool *isnull = palloc0(tupleDescriptor->natts * sizeof(bool));\n\t\tbool *replace = palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\t\treplace[Anum_pg_dist_object_colocationid - 1] = true;\n\n\t\t/* update the colocationId to the new one */\n\t\tvalues[Anum_pg_dist_object_colocationid - 1] = UInt32GetDatum(newColocationId);\n\n\t\tisnull[Anum_pg_dist_object_colocationid - 1] = false;\n\n\t\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull,\n\t\t\t\t\t\t\t\t\t  replace);\n\n\t\tCatalogTupleUpdate(pgDistObjectRel, &heapTuple->t_self, heapTuple);\n\t\tCitusInvalidateRelcacheByRelid(DistObjectRelationId());\n\n\t\tpfree(values);\n\t\tpfree(isnull);\n\t\tpfree(replace);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistObjectRel, NoLock);\n\tCommandCounterIncrement();\n}\n\n\n/*\n * DistributedFunctionList returns the list of ObjectAddress-es of all the\n * distributed functions found in pg_dist_object\n */\nList *\nDistributedFunctionList(void)\n{\n\tList *distributedFunctionList = NIL;\n\n\tScanKeyData key[1];\n\tRelation pgDistObjectRel = table_open(DistObjectRelationId(), AccessShareLock);\n\n\t/* scan pg_dist_object for classid = ProcedureRelationId via index */\n\tScanKeyInit(&key[0], Anum_pg_dist_object_classid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(ProcedureRelationId));\n\tSysScanDesc pgDistObjectScan = systable_beginscan(pgDistObjectRel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  DistObjectPrimaryKeyIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  true, NULL, 1, key);\n\n\tHeapTuple pgDistObjectTup = NULL;\n\twhile (HeapTupleIsValid(pgDistObjectTup = systable_getnext(pgDistObjectScan)))\n\t{\n\t\tForm_pg_dist_object pg_dist_object =\n\t\t\t(Form_pg_dist_object) GETSTRUCT(pgDistObjectTup);\n\n\t\tObjectAddress *functionAddress = palloc0(sizeof(ObjectAddress));\n\t\tfunctionAddress->classId = ProcedureRelationId;\n\t\tfunctionAddress->objectId = pg_dist_object->objid;\n\t\tfunctionAddress->objectSubId = pg_dist_object->objsubid;\n\t\tdistributedFunctionList = lappend(distributedFunctionList, functionAddress);\n\t}\n\n\tsystable_endscan(pgDistObjectScan);\n\trelation_close(pgDistObjectRel, AccessShareLock);\n\treturn distributedFunctionList;\n}\n\n\n/*\n * DistributedSequenceList returns the list of ObjectAddress-es of all the\n * distributed sequences found in pg_dist_object\n */\nList *\nDistributedSequenceList(void)\n{\n\tList *distributedSequenceList = NIL;\n\n\tScanKeyData key[1];\n\tRelation pgDistObjectRel = table_open(DistObjectRelationId(), AccessShareLock);\n\n\t/* scan pg_dist_object for classid = RelationRelationId via index */\n\tScanKeyInit(&key[0], Anum_pg_dist_object_classid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(RelationRelationId));\n\tSysScanDesc pgDistObjectScan = systable_beginscan(pgDistObjectRel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  DistObjectPrimaryKeyIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  true, NULL, 1, key);\n\n\tHeapTuple pgDistObjectTup = NULL;\n\twhile (HeapTupleIsValid(pgDistObjectTup = systable_getnext(pgDistObjectScan)))\n\t{\n\t\tForm_pg_dist_object pg_dist_object =\n\t\t\t(Form_pg_dist_object) GETSTRUCT(pgDistObjectTup);\n\n\t\tif (get_rel_relkind(pg_dist_object->objid) == RELKIND_SEQUENCE)\n\t\t{\n\t\t\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\t\t\tsequenceAddress->classId = RelationRelationId;\n\t\t\tsequenceAddress->objectId = pg_dist_object->objid;\n\t\t\tsequenceAddress->objectSubId = pg_dist_object->objsubid;\n\t\t\tdistributedSequenceList = lappend(distributedSequenceList, sequenceAddress);\n\t\t}\n\t}\n\n\tsystable_endscan(pgDistObjectScan);\n\trelation_close(pgDistObjectRel, AccessShareLock);\n\treturn distributedSequenceList;\n}\n\n\n/*\n * GetForceDelegationAttrIndexInPgDistObject returns attrnum for force_delegation attr.\n *\n * force_delegation attr was added to table pg_dist_object using alter operation after\n * the version where Citus started supporting downgrades, and it's only column that we've\n * introduced to pg_dist_object since then.\n *\n * And in case of a downgrade + upgrade, tupleDesc->natts becomes greater than\n * Natts_pg_dist_object and when this happens, then we know that attrnum force_delegation is\n * not Anum_pg_dist_object_force_delegation anymore but tupleDesc->natts - 1.\n */\nint\nGetForceDelegationAttrIndexInPgDistObject(TupleDesc tupleDesc)\n{\n\treturn tupleDesc->natts == Natts_pg_dist_object\n\t\t   ? (Anum_pg_dist_object_force_delegation - 1)\n\t\t   : tupleDesc->natts - 1;\n}\n"
  },
  {
    "path": "src/backend/distributed/metadata/metadata_cache.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * metadata_cache.c\n *\t  Distributed table metadata cache\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"stdint.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/nbtree.h\"\n#include \"access/sysattr.h\"\n#include \"access/xact.h\"\n#include \"catalog/index.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_enum.h\"\n#include \"catalog/pg_extension.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/dbcommands.h\"\n#include \"commands/extension.h\"\n#include \"commands/trigger.h\"\n#include \"common/hashfn.h\"\n#include \"executor/executor.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/memnodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"parser/parse_func.h\"\n#include \"parser/parse_type.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/catcache.h\"\n#include \"utils/datum.h\"\n#include \"utils/elog.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/inval.h\"\n#include \"utils/jsonb.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/palloc.h\"\n#include \"utils/rel.h\"\n#include \"utils/relmapper.h\"\n#include \"utils/resowner.h\"\n#include \"utils/syscache.h\"\n#include \"utils/typcache.h\"\n\n#include \"citus_version.h\"\n#include \"pg_version_compat.h\"\n#include \"pg_version_constants.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/citus_depended_object.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/foreign_key_relationship.h\"\n#include \"distributed/function_utils.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/pg_dist_object.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/pg_dist_local_group.h\"\n#include \"distributed/pg_dist_node.h\"\n#include \"distributed/pg_dist_node_metadata.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/pg_dist_placement.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/shared_library_init.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/utils/function.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/* user configuration */\nint ReadFromSecondaries = USE_SECONDARY_NODES_NEVER;\n\n\n/*\n * CitusTableCacheEntrySlot is entry type for DistTableCacheHash,\n * entry data outlives slot on invalidation, so requires indirection.\n */\ntypedef struct CitusTableCacheEntrySlot\n{\n\t/* lookup key - must be first. A pg_class.oid oid. */\n\tOid relationId;\n\n\t/* Citus table metadata (NULL for local tables) */\n\tCitusTableCacheEntry *citusTableMetadata;\n\n\t/*\n\t * If isValid is false, we need to recheck whether the relation ID\n\t * belongs to a Citus or not.\n\t */\n\tbool isValid;\n} CitusTableCacheEntrySlot;\n\n\n/*\n * ShardIdCacheEntry is the entry type for ShardIdCacheHash.\n *\n * This should never be used outside of this file. Use ShardInterval instead.\n */\ntypedef struct ShardIdCacheEntry\n{\n\t/* hash key, needs to be first */\n\tuint64 shardId;\n\n\t/* pointer to the table entry to which this shard currently belongs */\n\tCitusTableCacheEntry *tableEntry;\n\n\t/* index of the shard interval in the sortedShardIntervalArray of the table entry */\n\tint shardIndex;\n} ShardIdCacheEntry;\n\n/*\n * ExtensionCreatedState is used to track if citus extension has been created\n * using CREATE EXTENSION command.\n *  UNKNOWN     : MetadataCache is invalid. State is UNKNOWN.\n *  CREATED     : Citus is created.\n *  NOTCREATED\t: Citus is not created.\n */\ntypedef enum ExtensionCreatedState\n{\n\tUNKNOWN = 0,\n\tCREATED = 1,\n\tNOTCREATED = 2,\n} ExtensionCreatedState;\n\n/*\n * State which should be cleared upon DROP EXTENSION. When the configuration\n * changes, e.g. because extension is dropped, these summarily get set to 0.\n */\ntypedef struct MetadataCacheData\n{\n\tExtensionCreatedState extensionCreatedState;\n\tOid distShardRelationId;\n\tOid distPlacementRelationId;\n\tOid distBackgroundJobRelationId;\n\tOid distBackgroundJobPKeyIndexId;\n\tOid distBackgroundJobJobIdSequenceId;\n\tOid distBackgroundTaskRelationId;\n\tOid distBackgroundTaskPKeyIndexId;\n\tOid distBackgroundTaskJobIdTaskIdIndexId;\n\tOid distBackgroundTaskStatusTaskIdIndexId;\n\tOid distBackgroundTaskTaskIdSequenceId;\n\tOid distBackgroundTaskDependRelationId;\n\tOid distBackgroundTaskDependTaskIdIndexId;\n\tOid distBackgroundTaskDependDependsOnIndexId;\n\tOid citusJobStatusScheduledId;\n\tOid citusJobStatusRunningId;\n\tOid citusJobStatusCancellingId;\n\tOid citusJobStatusFinishedId;\n\tOid citusJobStatusCancelledId;\n\tOid citusJobStatusFailedId;\n\tOid citusJobStatusFailingId;\n\tOid citusTaskStatusBlockedId;\n\tOid citusTaskStatusRunnableId;\n\tOid citusTaskStatusRunningId;\n\tOid citusTaskStatusDoneId;\n\tOid citusTaskStatusErrorId;\n\tOid citusTaskStatusUnscheduledId;\n\tOid citusTaskStatusCancelledId;\n\tOid citusTaskStatusCancellingId;\n\tOid distRebalanceStrategyRelationId;\n\tOid distNodeRelationId;\n\tOid distNodeNodeIdIndexId;\n\tOid distLocalGroupRelationId;\n\tOid distObjectRelationId;\n\tOid distObjectPrimaryKeyIndexId;\n\tOid distCleanupRelationId;\n\tOid distCleanupPrimaryKeyIndexId;\n\tOid distColocationRelationId;\n\tOid distColocationConfigurationIndexId;\n\tOid distPartitionRelationId;\n\tOid distTenantSchemaRelationId;\n\tOid distPartitionLogicalRelidIndexId;\n\tOid distPartitionColocationidIndexId;\n\tOid distShardLogicalRelidIndexId;\n\tOid distShardShardidIndexId;\n\tOid distPlacementShardidIndexId;\n\tOid distPlacementPlacementidIndexId;\n\tOid distColocationidIndexId;\n\tOid distPlacementGroupidIndexId;\n\tOid distTransactionRelationId;\n\tOid distTransactionGroupIndexId;\n\tOid distTenantSchemaPrimaryKeyIndexId;\n\tOid distTenantSchemaUniqueColocationIdIndexId;\n\tOid citusCatalogNamespaceId;\n\tOid copyFormatTypeId;\n\tOid readIntermediateResultFuncId;\n\tOid readIntermediateResultArrayFuncId;\n\tOid extraDataContainerFuncId;\n\tOid workerHashFunctionId;\n\tOid anyValueFunctionId;\n\tOid textSendAsJsonbFunctionId;\n\tOid textoutFunctionId;\n\tOid extensionOwner;\n\tOid binaryCopyFormatId;\n\tOid textCopyFormatId;\n\tOid primaryNodeRoleId;\n\tOid secondaryNodeRoleId;\n\tOid unavailableNodeRoleId;\n\tOid pgTableIsVisibleFuncId;\n\tOid citusTableIsVisibleFuncId;\n\tOid distAuthinfoRelationId;\n\tOid distAuthinfoIndexId;\n\tOid distPoolinfoRelationId;\n\tOid distPoolinfoIndexId;\n\tOid relationIsAKnownShardFuncId;\n\tOid jsonbExtractPathFuncId;\n\tOid jsonbExtractPathTextFuncId;\n\tOid CitusDependentObjectFuncId;\n\tOid distClockLogicalSequenceId;\n\tbool databaseNameValid;\n\tchar databaseName[NAMEDATALEN];\n} MetadataCacheData;\n\n\nstatic MetadataCacheData MetadataCache;\n\n/* Citus extension version variables */\nbool EnableVersionChecks = true; /* version checks are enabled */\n\nstatic bool citusVersionKnownCompatible = false;\n\n/* Variable to determine if we are in the process of creating citus */\nstatic int CreateCitusTransactionLevel = 0;\n\n/* Hash table for informations about each partition */\nstatic HTAB *DistTableCacheHash = NULL;\nstatic List *DistTableCacheExpired = NIL;\n\n/* Hash table for informations about each shard */\nstatic HTAB *ShardIdCacheHash = NULL;\n\nstatic MemoryContext MetadataCacheMemoryContext = NULL;\n\n/* Hash table for information about each object */\nstatic HTAB *DistObjectCacheHash = NULL;\n\n/* Hash table for informations about worker nodes */\nstatic HTAB *WorkerNodeHash = NULL;\nstatic WorkerNode **WorkerNodeArray = NULL;\nstatic int WorkerNodeCount = 0;\nstatic bool workerNodeHashValid = false;\n\n/* default value is -1, for coordinator it's 0 and for worker nodes > 0 */\nstatic int32 LocalGroupId = -1;\n\n/* default value is -1, increases with every node starting from 1 */\nstatic int32 LocalNodeId = -1;\n\n/* built first time through in InitializeDistCache */\nstatic ScanKeyData DistPartitionScanKey[1];\nstatic ScanKeyData DistShardScanKey[1];\nstatic ScanKeyData DistObjectScanKey[3];\n\n\n/* local function forward declarations */\nstatic HeapTuple PgDistPartitionTupleViaCatalog(Oid relationId);\nstatic ShardIdCacheEntry * LookupShardIdCacheEntry(int64 shardId, bool missingOk);\nstatic CitusTableCacheEntry * BuildCitusTableCacheEntry(Oid relationId);\nstatic void BuildCachedShardList(CitusTableCacheEntry *cacheEntry);\nstatic void PrepareWorkerNodeCache(void);\nstatic bool CheckInstalledVersion(int elevel);\nstatic char * AvailableExtensionVersion(void);\nstatic char * InstalledExtensionVersion(void);\nstatic bool CitusHasBeenLoadedInternal(void);\nstatic void InitializeCaches(void);\nstatic void InitializeDistCache(void);\nstatic void InitializeDistObjectCache(void);\nstatic void InitializeWorkerNodeCache(void);\nstatic void RegisterForeignKeyGraphCacheCallbacks(void);\nstatic void RegisterWorkerNodeCacheCallbacks(void);\nstatic void RegisterLocalGroupIdCacheCallbacks(void);\nstatic void RegisterAuthinfoCacheCallbacks(void);\nstatic void RegisterCitusTableCacheEntryReleaseCallbacks(void);\nstatic void ResetCitusTableCacheEntry(CitusTableCacheEntry *cacheEntry);\nstatic void RemoveStaleShardIdCacheEntries(CitusTableCacheEntry *tableEntry);\nstatic void CreateDistTableCache(void);\nstatic void CreateShardIdCache(void);\nstatic void CreateDistObjectCache(void);\nstatic void InvalidateForeignRelationGraphCacheCallback(Datum argument, Oid relationId);\nstatic void InvalidateNodeRelationCacheCallback(Datum argument, Oid relationId);\nstatic void InvalidateLocalGroupIdRelationCacheCallback(Datum argument, Oid relationId);\nstatic void InvalidateConnParamsCacheCallback(Datum argument, Oid relationId);\nstatic void CitusTableCacheEntryReleaseCallback(ResourceReleasePhase phase, bool isCommit,\n\t\t\t\t\t\t\t\t\t\t\t\tbool isTopLevel, void *arg);\nstatic HeapTuple LookupDistPartitionTuple(Relation pgDistPartition, Oid relationId);\nstatic void GetPartitionTypeInputInfo(char *partitionKeyString, char partitionMethod,\n\t\t\t\t\t\t\t\t\t  Oid *columnTypeId, int32 *columnTypeMod,\n\t\t\t\t\t\t\t\t\t  Oid *intervalTypeId, int32 *intervalTypeMod);\nstatic void CachedNamespaceLookup(const char *nspname, Oid *cachedOid);\nstatic void CachedRelationLookup(const char *relationName, Oid *cachedOid);\nstatic void CachedRelationLookupExtended(const char *relationName, Oid *cachedOid,\n\t\t\t\t\t\t\t\t\t\t bool missing_ok);\nstatic void CachedRelationNamespaceLookup(const char *relationName, Oid relnamespace,\n\t\t\t\t\t\t\t\t\t\t  Oid *cachedOid);\nstatic void CachedRelationNamespaceLookupExtended(const char *relationName,\n\t\t\t\t\t\t\t\t\t\t\t\t  Oid renamespace, Oid *cachedOid,\n\t\t\t\t\t\t\t\t\t\t\t\t  bool missing_ok);\nstatic ShardPlacement * ResolveGroupShardPlacement(GroupShardPlacement *\n\t\t\t\t\t\t\t\t\t\t\t\t   groupShardPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t   CitusTableCacheEntry *tableEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t   int shardIndex);\nstatic Oid LookupEnumValueId(Oid typeId, char *valueName);\nstatic void InvalidateCitusTableCacheEntrySlot(CitusTableCacheEntrySlot *cacheSlot);\nstatic void InvalidateDistTableCache(void);\nstatic void InvalidateDistObjectCache(void);\nstatic bool InitializeTableCacheEntry(int64 shardId, bool missingOk);\nstatic bool IsCitusTableTypeInternal(char partitionMethod, char replicationModel,\n\t\t\t\t\t\t\t\t\t uint32 colocationId, CitusTableType tableType);\nstatic bool RefreshTableCacheEntryIfInvalid(ShardIdCacheEntry *shardEntry, bool\n\t\t\t\t\t\t\t\t\t\t\tmissingOk);\n\nstatic Oid DistAuthinfoRelationId(void);\nstatic Oid DistAuthinfoIndexId(void);\nstatic Oid DistPoolinfoRelationId(void);\nstatic Oid DistPoolinfoIndexId(void);\nstatic int ParseVersionComponent(const char *version, char **endPtr);\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(citus_dist_partition_cache_invalidate);\nPG_FUNCTION_INFO_V1(master_dist_partition_cache_invalidate);\nPG_FUNCTION_INFO_V1(citus_dist_shard_cache_invalidate);\nPG_FUNCTION_INFO_V1(master_dist_shard_cache_invalidate);\nPG_FUNCTION_INFO_V1(citus_dist_placement_cache_invalidate);\nPG_FUNCTION_INFO_V1(master_dist_placement_cache_invalidate);\nPG_FUNCTION_INFO_V1(citus_dist_node_cache_invalidate);\nPG_FUNCTION_INFO_V1(master_dist_node_cache_invalidate);\nPG_FUNCTION_INFO_V1(citus_dist_local_group_cache_invalidate);\nPG_FUNCTION_INFO_V1(master_dist_local_group_cache_invalidate);\nPG_FUNCTION_INFO_V1(citus_conninfo_cache_invalidate);\nPG_FUNCTION_INFO_V1(master_dist_authinfo_cache_invalidate);\nPG_FUNCTION_INFO_V1(citus_dist_object_cache_invalidate);\nPG_FUNCTION_INFO_V1(master_dist_object_cache_invalidate);\nPG_FUNCTION_INFO_V1(role_exists);\nPG_FUNCTION_INFO_V1(authinfo_valid);\nPG_FUNCTION_INFO_V1(poolinfo_valid);\n\n\n/*\n * EnsureModificationsCanRun checks if the current node is in recovery mode or\n * citus.use_secondary_nodes is 'always'. If either is true the function errors out.\n */\nvoid\nEnsureModificationsCanRun(void)\n{\n\tif (RecoveryInProgress() && !WritableStandbyCoordinator)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),\n\t\t\t\t\t\terrmsg(\"writing to worker nodes is not currently allowed\"),\n\t\t\t\t\t\terrdetail(\"the database is read-only\")));\n\t}\n\n\tif (ReadFromSecondaries == USE_SECONDARY_NODES_ALWAYS)\n\t{\n\t\tereport(ERROR, (errmsg(\"writing to worker nodes is not currently allowed\"),\n\t\t\t\t\t\terrdetail(\"citus.use_secondary_nodes is set to 'always'\")));\n\t}\n}\n\n\n/*\n * EnsureModificationsCanRunOnRelation first calls into EnsureModificationsCanRun() and\n * then does one more additional check. The additional check is to give a proper error\n * message if any relation that is modified is replicated, as replicated tables use\n * 2PC and 2PC cannot happen when recovery is in progress.\n */\nvoid\nEnsureModificationsCanRunOnRelation(Oid relationId)\n{\n\tEnsureModificationsCanRun();\n\n\tif (!OidIsValid(relationId) || !IsCitusTable(relationId))\n\t{\n\t\t/* we are not interested in PG tables */\n\t\treturn;\n\t}\n\n\tbool modifiedTableReplicated =\n\t\tIsCitusTableType(relationId, REFERENCE_TABLE) ||\n\t\t!SingleReplicatedTable(relationId);\n\n\tif (!IsCoordinator() && !AllowModificationsFromWorkersToReplicatedTables &&\n\t\tmodifiedTableReplicated)\n\t{\n\t\tereport(ERROR, (errmsg(\"modifications via the worker nodes are not \"\n\t\t\t\t\t\t\t   \"allowed for replicated tables such as reference \"\n\t\t\t\t\t\t\t   \"tables or hash distributed tables with replication \"\n\t\t\t\t\t\t\t   \"factor greater than 1.\"),\n\t\t\t\t\t\terrhint(\"All modifications to replicated tables should \"\n\t\t\t\t\t\t\t\t\"happen via the coordinator unless \"\n\t\t\t\t\t\t\t\t\"citus.allow_modifications_from_workers_to_replicated_tables \"\n\t\t\t\t\t\t\t\t\" = true.\"),\n\t\t\t\t\t\terrdetail(\"Allowing modifications from the worker nodes \"\n\t\t\t\t\t\t\t\t  \"requires extra locking which might decrease \"\n\t\t\t\t\t\t\t\t  \"the throughput.\")));\n\t}\n\n\t/*\n\t * Even if user allows writes from standby, we should not allow for\n\t * replicated tables as they require 2PC. And, 2PC needs to write a log\n\t * record on the coordinator.\n\t */\n\tif (!(RecoveryInProgress() && WritableStandbyCoordinator))\n\t{\n\t\treturn;\n\t}\n\n\tif (modifiedTableReplicated)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),\n\t\t\t\t\t\terrmsg(\"writing to worker nodes is not currently \"\n\t\t\t\t\t\t\t   \"allowed for replicated tables such as reference \"\n\t\t\t\t\t\t\t   \"tables or hash distributed tables with replication \"\n\t\t\t\t\t\t\t   \"factor greater than 1.\"),\n\t\t\t\t\t\terrhint(\"All modifications to replicated tables \"\n\t\t\t\t\t\t\t\t\"happen via 2PC, and 2PC requires the \"\n\t\t\t\t\t\t\t\t\"database to be in a writable state.\"),\n\t\t\t\t\t\terrdetail(\"the database is read-only\")));\n\t}\n}\n\n\n/*\n * IsCitusTableType returns true if the given table with relationId\n * belongs to a citus table that matches the given table type. If cache\n * entry already exists, prefer using IsCitusTableTypeCacheEntry to avoid\n * an extra lookup.\n */\nbool\nIsCitusTableType(Oid relationId, CitusTableType tableType)\n{\n\tCitusTableCacheEntry *tableEntry = LookupCitusTableCacheEntry(relationId);\n\n\t/* we are not interested in postgres tables */\n\tif (tableEntry == NULL)\n\t{\n\t\treturn false;\n\t}\n\treturn IsCitusTableTypeCacheEntry(tableEntry, tableType);\n}\n\n\n/*\n * GetCitusTableType is a helper function that returns the CitusTableType\n * for the given relationId.\n * Note that a single table can be qualified as multiple CitusTableType, such\n * as hash distributed tables are both HASH_DISTRIBUTED and DISTRIBUTED_TABLE.\n * This function returns the base type for a given table.\n *\n * If the table is not a Citus table, ANY_CITUS_TABLE_TYPE is returned.\n */\nCitusTableType\nGetCitusTableType(CitusTableCacheEntry *tableEntry)\n{\n\t/* we do not expect local tables here */\n\tAssert(tableEntry != NULL);\n\n\tif (IsCitusTableTypeCacheEntry(tableEntry, HASH_DISTRIBUTED))\n\t{\n\t\treturn HASH_DISTRIBUTED;\n\t}\n\telse if (IsCitusTableTypeCacheEntry(tableEntry, SINGLE_SHARD_DISTRIBUTED))\n\t{\n\t\treturn SINGLE_SHARD_DISTRIBUTED;\n\t}\n\telse if (IsCitusTableTypeCacheEntry(tableEntry, REFERENCE_TABLE))\n\t{\n\t\treturn REFERENCE_TABLE;\n\t}\n\telse if (IsCitusTableTypeCacheEntry(tableEntry, CITUS_LOCAL_TABLE))\n\t{\n\t\treturn CITUS_LOCAL_TABLE;\n\t}\n\telse if (IsCitusTableTypeCacheEntry(tableEntry, APPEND_DISTRIBUTED))\n\t{\n\t\treturn APPEND_DISTRIBUTED;\n\t}\n\telse if (IsCitusTableTypeCacheEntry(tableEntry, RANGE_DISTRIBUTED))\n\t{\n\t\treturn RANGE_DISTRIBUTED;\n\t}\n\telse\n\t{\n\t\treturn ANY_CITUS_TABLE_TYPE;\n\t}\n}\n\n\n/*\n * IsCitusTableTypeCacheEntry returns true if the given table cache entry\n * belongs to a citus table that matches the given table type.\n */\nbool\nIsCitusTableTypeCacheEntry(CitusTableCacheEntry *tableEntry, CitusTableType tableType)\n{\n\treturn IsCitusTableTypeInternal(tableEntry->partitionMethod,\n\t\t\t\t\t\t\t\t\ttableEntry->replicationModel,\n\t\t\t\t\t\t\t\t\ttableEntry->colocationId, tableType);\n}\n\n\n/*\n * IsFirstShard returns true if the given shardId is the first shard.\n */\nbool\nIsFirstShard(CitusTableCacheEntry *tableEntry, uint64 shardId)\n{\n\tif (tableEntry == NULL || tableEntry->sortedShardIntervalArray == NULL)\n\t{\n\t\treturn false;\n\t}\n\tif (tableEntry->sortedShardIntervalArray[0]->shardId == INVALID_SHARD_ID)\n\t{\n\t\treturn false;\n\t}\n\n\tif (shardId == tableEntry->sortedShardIntervalArray[0]->shardId)\n\t{\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\treturn false;\n\t}\n}\n\n\n/*\n * HasDistributionKey returns true if given Citus table has a distribution key.\n */\nbool\nHasDistributionKey(Oid relationId)\n{\n\tCitusTableCacheEntry *tableEntry = LookupCitusTableCacheEntry(relationId);\n\tif (tableEntry == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"relation with oid %u is not a Citus table\", relationId)));\n\t}\n\n\treturn HasDistributionKeyCacheEntry(tableEntry);\n}\n\n\n/*\n * HasDistributionKeyCacheEntry returns true if given cache entry identifies a\n * Citus table that has a distribution key.\n */\nbool\nHasDistributionKeyCacheEntry(CitusTableCacheEntry *tableEntry)\n{\n\treturn tableEntry->partitionMethod != DISTRIBUTE_BY_NONE;\n}\n\n\n/*\n * IsCitusTableTypeInternal returns true if the given table entry belongs to\n * the given table type group. For definition of table types, see CitusTableType.\n */\nstatic bool\nIsCitusTableTypeInternal(char partitionMethod, char replicationModel,\n\t\t\t\t\t\t uint32 colocationId, CitusTableType tableType)\n{\n\tswitch (tableType)\n\t{\n\t\tcase HASH_DISTRIBUTED:\n\t\t{\n\t\t\treturn partitionMethod == DISTRIBUTE_BY_HASH;\n\t\t}\n\n\t\tcase APPEND_DISTRIBUTED:\n\t\t{\n\t\t\treturn partitionMethod == DISTRIBUTE_BY_APPEND;\n\t\t}\n\n\t\tcase RANGE_DISTRIBUTED:\n\t\t{\n\t\t\treturn partitionMethod == DISTRIBUTE_BY_RANGE;\n\t\t}\n\n\t\tcase SINGLE_SHARD_DISTRIBUTED:\n\t\t{\n\t\t\treturn partitionMethod == DISTRIBUTE_BY_NONE &&\n\t\t\t\t   replicationModel != REPLICATION_MODEL_2PC &&\n\t\t\t\t   colocationId != INVALID_COLOCATION_ID;\n\t\t}\n\n\t\tcase DISTRIBUTED_TABLE:\n\t\t{\n\t\t\treturn partitionMethod == DISTRIBUTE_BY_HASH ||\n\t\t\t\t   partitionMethod == DISTRIBUTE_BY_RANGE ||\n\t\t\t\t   partitionMethod == DISTRIBUTE_BY_APPEND ||\n\t\t\t\t   (partitionMethod == DISTRIBUTE_BY_NONE &&\n\t\t\t\t\treplicationModel != REPLICATION_MODEL_2PC &&\n\t\t\t\t\tcolocationId != INVALID_COLOCATION_ID);\n\t\t}\n\n\t\tcase STRICTLY_PARTITIONED_DISTRIBUTED_TABLE:\n\t\t{\n\t\t\treturn partitionMethod == DISTRIBUTE_BY_HASH ||\n\t\t\t\t   partitionMethod == DISTRIBUTE_BY_RANGE;\n\t\t}\n\n\t\tcase REFERENCE_TABLE:\n\t\t{\n\t\t\treturn partitionMethod == DISTRIBUTE_BY_NONE &&\n\t\t\t\t   replicationModel == REPLICATION_MODEL_2PC;\n\t\t}\n\n\t\tcase CITUS_LOCAL_TABLE:\n\t\t{\n\t\t\treturn partitionMethod == DISTRIBUTE_BY_NONE &&\n\t\t\t\t   replicationModel != REPLICATION_MODEL_2PC &&\n\t\t\t\t   colocationId == INVALID_COLOCATION_ID;\n\t\t}\n\n\t\tcase ANY_CITUS_TABLE_TYPE:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"Unknown table type %d\", tableType)));\n\t\t}\n\t}\n\treturn false;\n}\n\n\n/*\n * GetTableTypeName returns string representation of the table type.\n */\nchar *\nGetTableTypeName(Oid tableId)\n{\n\tif (!IsCitusTable(tableId))\n\t{\n\t\treturn \"regular table\";\n\t}\n\n\tCitusTableCacheEntry *tableCacheEntry = GetCitusTableCacheEntry(tableId);\n\tif (IsCitusTableTypeCacheEntry(tableCacheEntry, HASH_DISTRIBUTED))\n\t{\n\t\treturn \"distributed table\";\n\t}\n\telse if (IsCitusTableTypeCacheEntry(tableCacheEntry, REFERENCE_TABLE))\n\t{\n\t\treturn \"reference table\";\n\t}\n\telse if (IsCitusTableTypeCacheEntry(tableCacheEntry, CITUS_LOCAL_TABLE))\n\t{\n\t\treturn \"citus local table\";\n\t}\n\telse\n\t{\n\t\treturn \"unknown table\";\n\t}\n}\n\n\n/*\n * IsCitusTable returns whether relationId is a distributed relation or\n * not.\n */\nbool\nIsCitusTable(Oid relationId)\n{\n\t/*\n\t * PostgreSQL's OID generator assigns user operation OIDs starting\n\t * from FirstNormalObjectId. This means no user object can have\n\t * an OID lower than FirstNormalObjectId. Therefore, if the\n\t * relationId is less than FirstNormalObjectId\n\t * (i.e. in PostgreSQL's reserved range), we can immediately\n\t * return false, since such objects cannot be Citus tables.\n\t */\n\tif (relationId < FirstNormalObjectId)\n\t{\n\t\treturn false;\n\t}\n\treturn LookupCitusTableCacheEntry(relationId) != NULL;\n}\n\n\n/*\n * IsCitusTableRangeVar returns whether the table named in the given\n * rangeVar is a Citus table.\n */\nbool\nIsCitusTableRangeVar(RangeVar *rangeVar, LOCKMODE lockMode, bool missingOK)\n{\n\tOid relationId = RangeVarGetRelid(rangeVar, lockMode, missingOK);\n\treturn IsCitusTable(relationId);\n}\n\n\n/*\n * IsCitusTableViaCatalog returns whether the given relation is a\n * distributed table or not.\n *\n * It does so by searching pg_dist_partition, explicitly bypassing caches,\n * because this function is designed to be used in cases where accessing\n * metadata tables is not safe.\n *\n * NB: Currently this still hardcodes pg_dist_partition logicalrelid column\n * offset and the corresponding index.  If we ever come close to changing\n * that, we'll have to work a bit harder.\n */\nbool\nIsCitusTableViaCatalog(Oid relationId)\n{\n\tHeapTuple partitionTuple = PgDistPartitionTupleViaCatalog(relationId);\n\n\tbool heapTupleIsValid = HeapTupleIsValid(partitionTuple);\n\n\tif (heapTupleIsValid)\n\t{\n\t\theap_freetuple(partitionTuple);\n\t}\n\treturn heapTupleIsValid;\n}\n\n\n/*\n * PartitionMethodViaCatalog gets a relationId and returns the partition\n * method column from pg_dist_partition via reading from catalog.\n */\nchar\nPartitionMethodViaCatalog(Oid relationId)\n{\n\tHeapTuple partitionTuple = PgDistPartitionTupleViaCatalog(relationId);\n\tif (!HeapTupleIsValid(partitionTuple))\n\t{\n\t\treturn DISTRIBUTE_BY_INVALID;\n\t}\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), AccessShareLock);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\n\tDatum *datumArray = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNullArray = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\n\theap_deform_tuple(partitionTuple, tupleDescriptor, datumArray, isNullArray);\n\n\tif (isNullArray[Anum_pg_dist_partition_partmethod - 1])\n\t{\n\t\t/* partition method cannot be NULL, still let's make sure */\n\t\theap_freetuple(partitionTuple);\n\t\ttable_close(pgDistPartition, NoLock);\n\t\tpfree(datumArray);\n\t\tpfree(isNullArray);\n\t\treturn DISTRIBUTE_BY_INVALID;\n\t}\n\n\tDatum partitionMethodDatum = datumArray[Anum_pg_dist_partition_partmethod - 1];\n\tchar partitionMethodChar = DatumGetChar(partitionMethodDatum);\n\n\theap_freetuple(partitionTuple);\n\ttable_close(pgDistPartition, NoLock);\n\tpfree(datumArray);\n\tpfree(isNullArray);\n\n\treturn partitionMethodChar;\n}\n\n\n/*\n * PartitionColumnViaCatalog gets a relationId and returns the partition\n * key column from pg_dist_partition via reading from catalog.\n */\nVar *\nPartitionColumnViaCatalog(Oid relationId)\n{\n\tHeapTuple partitionTuple = PgDistPartitionTupleViaCatalog(relationId);\n\tif (!HeapTupleIsValid(partitionTuple))\n\t{\n\t\treturn NULL;\n\t}\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), AccessShareLock);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\tDatum *datumArray = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNullArray = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\n\theap_deform_tuple(partitionTuple, tupleDescriptor, datumArray, isNullArray);\n\n\tif (isNullArray[Anum_pg_dist_partition_partkey - 1])\n\t{\n\t\t/* partition key cannot be NULL, still let's make sure */\n\t\theap_freetuple(partitionTuple);\n\t\ttable_close(pgDistPartition, NoLock);\n\t\tpfree(datumArray);\n\t\tpfree(isNullArray);\n\t\treturn NULL;\n\t}\n\n\tDatum partitionKeyDatum = datumArray[Anum_pg_dist_partition_partkey - 1];\n\tchar *partitionKeyString = TextDatumGetCString(partitionKeyDatum);\n\n\t/* convert the string to a Node and ensure it is a Var */\n\tNode *partitionNode = stringToNode(partitionKeyString);\n\tAssert(IsA(partitionNode, Var));\n\n\tVar *partitionColumn = (Var *) partitionNode;\n\n\theap_freetuple(partitionTuple);\n\ttable_close(pgDistPartition, NoLock);\n\tpfree(datumArray);\n\tpfree(isNullArray);\n\n\treturn partitionColumn;\n}\n\n\n/*\n * ColocationIdViaCatalog gets a relationId and returns the colocation\n * id column from pg_dist_partition via reading from catalog.\n */\nuint32\nColocationIdViaCatalog(Oid relationId)\n{\n\tHeapTuple partitionTuple = PgDistPartitionTupleViaCatalog(relationId);\n\tif (!HeapTupleIsValid(partitionTuple))\n\t{\n\t\treturn INVALID_COLOCATION_ID;\n\t}\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), AccessShareLock);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\n\tDatum *datumArray = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNullArray = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\n\theap_deform_tuple(partitionTuple, tupleDescriptor, datumArray, isNullArray);\n\n\tif (isNullArray[Anum_pg_dist_partition_colocationid - 1])\n\t{\n\t\t/* colocation id cannot be NULL, still let's make sure */\n\t\theap_freetuple(partitionTuple);\n\t\ttable_close(pgDistPartition, NoLock);\n\t\tpfree(datumArray);\n\t\tpfree(isNullArray);\n\t\treturn INVALID_COLOCATION_ID;\n\t}\n\n\tDatum colocationIdDatum = datumArray[Anum_pg_dist_partition_colocationid - 1];\n\tuint32 colocationId = DatumGetUInt32(colocationIdDatum);\n\n\theap_freetuple(partitionTuple);\n\ttable_close(pgDistPartition, NoLock);\n\tpfree(datumArray);\n\tpfree(isNullArray);\n\n\treturn colocationId;\n}\n\n\n/*\n * PgDistPartitionTupleViaCatalog is a helper function that searches\n * pg_dist_partition for the given relationId. The caller is responsible\n * for ensuring that the returned heap tuple is valid before accessing\n * its fields.\n */\nstatic HeapTuple\nPgDistPartitionTupleViaCatalog(Oid relationId)\n{\n\tconst int scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\tbool indexOK = true;\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_partition_logicalrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPartitionLogicalRelidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK, NULL, scanKeyCount, scanKey);\n\n\tHeapTuple partitionTuple = systable_getnext(scanDescriptor);\n\n\tif (HeapTupleIsValid(partitionTuple))\n\t{\n\t\t/* callers should have the tuple in their memory contexts */\n\t\tpartitionTuple = heap_copytuple(partitionTuple);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistPartition, AccessShareLock);\n\n\treturn partitionTuple;\n}\n\n\n/*\n * IsReferenceTableByDistParams returns true if given partitionMethod and\n * replicationModel would identify a reference table.\n */\nbool\nIsReferenceTableByDistParams(char partitionMethod, char replicationModel)\n{\n\treturn partitionMethod == DISTRIBUTE_BY_NONE &&\n\t\t   replicationModel == REPLICATION_MODEL_2PC;\n}\n\n\n/*\n * IsCitusLocalTableByDistParams returns true if given partitionMethod,\n * replicationModel and colocationId would identify a citus local table.\n */\nbool\nIsCitusLocalTableByDistParams(char partitionMethod, char replicationModel,\n\t\t\t\t\t\t\t  uint32 colocationId)\n{\n\treturn partitionMethod == DISTRIBUTE_BY_NONE &&\n\t\t   replicationModel != REPLICATION_MODEL_2PC &&\n\t\t   colocationId == INVALID_COLOCATION_ID;\n}\n\n\n/*\n * IsSingleShardTableByDistParams returns true if given partitionMethod,\n * replicationModel and colocationId would identify a single-shard distributed\n * table that has a null shard key.\n */\nbool\nIsSingleShardTableByDistParams(char partitionMethod, char replicationModel,\n\t\t\t\t\t\t\t   uint32 colocationId)\n{\n\treturn partitionMethod == DISTRIBUTE_BY_NONE &&\n\t\t   replicationModel != REPLICATION_MODEL_2PC &&\n\t\t   colocationId != INVALID_COLOCATION_ID;\n}\n\n\n/*\n * CitusTableList returns a list that includes all the valid distributed table\n * cache entries.\n */\nList *\nCitusTableList(void)\n{\n\tList *distributedTableList = NIL;\n\n\tAssert(CitusHasBeenLoaded() && CheckCitusVersion(WARNING));\n\n\t/* first, we need to iterate over pg_dist_partition */\n\tList *citusTableIdList = CitusTableTypeIdList(ANY_CITUS_TABLE_TYPE);\n\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, citusTableIdList)\n\t{\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\n\t\tdistributedTableList = lappend(distributedTableList, cacheEntry);\n\t}\n\n\treturn distributedTableList;\n}\n\n\n/*\n * LoadShardInterval returns the, cached, metadata about a shard.\n *\n * The return value is a copy of the cached ShardInterval struct and may\n * therefore be modified and/or freed.\n */\nShardInterval *\nLoadShardInterval(uint64 shardId)\n{\n\tbool missingOk = false;\n\tShardIdCacheEntry *shardIdEntry = LookupShardIdCacheEntry(shardId, missingOk);\n\tCitusTableCacheEntry *tableEntry = shardIdEntry->tableEntry;\n\tint shardIndex = shardIdEntry->shardIndex;\n\n\t/* the offset better be in a valid range */\n\tAssert(shardIndex < tableEntry->shardIntervalArrayLength);\n\n\tShardInterval *sourceShardInterval =\n\t\ttableEntry->sortedShardIntervalArray[shardIndex];\n\n\t/* copy value to return */\n\tShardInterval *shardInterval = CopyShardInterval(sourceShardInterval);\n\n\treturn shardInterval;\n}\n\n\n/*\n * ShardExists returns whether given shard exists or not. It fails if missingOk is false\n * and shard is not found.\n */\nbool\nShardExists(uint64 shardId)\n{\n\tbool missingOk = true;\n\tShardIdCacheEntry *shardIdEntry = LookupShardIdCacheEntry(shardId, missingOk);\n\n\tif (!shardIdEntry)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * RelationIdOfShard returns the relationId of the given shardId.\n */\nOid\nRelationIdForShard(uint64 shardId)\n{\n\tbool missingOk = false;\n\tShardIdCacheEntry *shardIdEntry = LookupShardIdCacheEntry(shardId, missingOk);\n\tCitusTableCacheEntry *tableEntry = shardIdEntry->tableEntry;\n\treturn tableEntry->relationId;\n}\n\n\n/*\n * ReferenceTableShardId returns true if the given shardId belongs to\n * a reference table.\n */\nbool\nReferenceTableShardId(uint64 shardId)\n{\n\tbool missingOk = false;\n\tShardIdCacheEntry *shardIdEntry = LookupShardIdCacheEntry(shardId, missingOk);\n\tCitusTableCacheEntry *tableEntry = shardIdEntry->tableEntry;\n\treturn IsCitusTableTypeCacheEntry(tableEntry, REFERENCE_TABLE);\n}\n\n\n/*\n * DistributedTableShardId returns true if the given shardId belongs to\n * a distributed table.\n */\nbool\nDistributedTableShardId(uint64 shardId)\n{\n\tif (shardId == INVALID_SHARD_ID)\n\t{\n\t\treturn false;\n\t}\n\n\tbool missingOk = false;\n\tShardIdCacheEntry *shardIdEntry = LookupShardIdCacheEntry(shardId, missingOk);\n\tCitusTableCacheEntry *tableEntry = shardIdEntry->tableEntry;\n\treturn IsCitusTableTypeCacheEntry(tableEntry, DISTRIBUTED_TABLE);\n}\n\n\n/*\n * LoadGroupShardPlacement returns the cached shard placement metadata\n *\n * The return value is a copy of the cached GroupShardPlacement struct and may\n * therefore be modified and/or freed.\n */\nGroupShardPlacement *\nLoadGroupShardPlacement(uint64 shardId, uint64 placementId)\n{\n\tbool missingOk = false;\n\tShardIdCacheEntry *shardIdEntry = LookupShardIdCacheEntry(shardId, missingOk);\n\tCitusTableCacheEntry *tableEntry = shardIdEntry->tableEntry;\n\tint shardIndex = shardIdEntry->shardIndex;\n\n\t/* the offset better be in a valid range */\n\tAssert(shardIndex < tableEntry->shardIntervalArrayLength);\n\n\tGroupShardPlacement *placementArray =\n\t\ttableEntry->arrayOfPlacementArrays[shardIndex];\n\tint numberOfPlacements =\n\t\ttableEntry->arrayOfPlacementArrayLengths[shardIndex];\n\n\tfor (int i = 0; i < numberOfPlacements; i++)\n\t{\n\t\tif (placementArray[i].placementId == placementId)\n\t\t{\n\t\t\tGroupShardPlacement *shardPlacement = CitusMakeNode(GroupShardPlacement);\n\n\t\t\t*shardPlacement = placementArray[i];\n\n\t\t\treturn shardPlacement;\n\t\t}\n\t}\n\n\tereport(ERROR, (errmsg(\"could not find valid entry for shard placement \"\n\t\t\t\t\t\t   UINT64_FORMAT, placementId)));\n}\n\n\n/*\n * LoadShardPlacement returns a shard placement for the primary node.\n */\nShardPlacement *\nLoadShardPlacement(uint64 shardId, uint64 placementId)\n{\n\tbool missingOk = false;\n\tShardIdCacheEntry *shardIdEntry = LookupShardIdCacheEntry(shardId, missingOk);\n\tCitusTableCacheEntry *tableEntry = shardIdEntry->tableEntry;\n\tint shardIndex = shardIdEntry->shardIndex;\n\tGroupShardPlacement *groupPlacement = LoadGroupShardPlacement(shardId, placementId);\n\tShardPlacement *nodePlacement = ResolveGroupShardPlacement(groupPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   tableEntry, shardIndex);\n\n\treturn nodePlacement;\n}\n\n\n/*\n * ShardPlacementOnGroupIncludingOrphanedPlacements returns the shard placement\n * for the given shard on the given group, or returns NULL if no placement for\n * the shard exists on the group.\n *\n * NOTE: This can return inactive or orphaned placements.\n */\nShardPlacement *\nShardPlacementOnGroupIncludingOrphanedPlacements(int32 groupId, uint64 shardId)\n{\n\tShardPlacement *placementOnNode = NULL;\n\n\tbool missingOk = false;\n\tShardIdCacheEntry *shardIdEntry = LookupShardIdCacheEntry(shardId, missingOk);\n\tCitusTableCacheEntry *tableEntry = shardIdEntry->tableEntry;\n\tint shardIndex = shardIdEntry->shardIndex;\n\tGroupShardPlacement *placementArray =\n\t\ttableEntry->arrayOfPlacementArrays[shardIndex];\n\tint numberOfPlacements =\n\t\ttableEntry->arrayOfPlacementArrayLengths[shardIndex];\n\n\tfor (int placementIndex = 0; placementIndex < numberOfPlacements; placementIndex++)\n\t{\n\t\tGroupShardPlacement *placement = &placementArray[placementIndex];\n\t\tif (placement->groupId == groupId)\n\t\t{\n\t\t\tplacementOnNode = ResolveGroupShardPlacement(placement, tableEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardIndex);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn placementOnNode;\n}\n\n\n/*\n * ActiveShardPlacementOnGroup returns the active shard placement for the\n * given shard on the given group, or returns NULL if no active placement for\n * the shard exists on the group.\n */\nShardPlacement *\nActiveShardPlacementOnGroup(int32 groupId, uint64 shardId)\n{\n\tShardPlacement *placement =\n\t\tShardPlacementOnGroupIncludingOrphanedPlacements(groupId, shardId);\n\tif (placement == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\treturn placement;\n}\n\n\n/*\n * ResolveGroupShardPlacement takes a GroupShardPlacement and adds additional data to it,\n * such as the node we should consider it to be on.\n */\nstatic ShardPlacement *\nResolveGroupShardPlacement(GroupShardPlacement *groupShardPlacement,\n\t\t\t\t\t\t   CitusTableCacheEntry *tableEntry,\n\t\t\t\t\t\t   int shardIndex)\n{\n\tShardInterval *shardInterval = tableEntry->sortedShardIntervalArray[shardIndex];\n\n\tShardPlacement *shardPlacement = CitusMakeNode(ShardPlacement);\n\tint32 groupId = groupShardPlacement->groupId;\n\tWorkerNode *workerNode = LookupNodeForGroup(groupId);\n\n\t/* copy everything into shardPlacement but preserve the header */\n\tCitusNode header = shardPlacement->type;\n\tGroupShardPlacement *shardPlacementAsGroupPlacement =\n\t\t(GroupShardPlacement *) shardPlacement;\n\t*shardPlacementAsGroupPlacement = *groupShardPlacement;\n\tshardPlacement->type = header;\n\n\tSetPlacementNodeMetadata(shardPlacement, workerNode);\n\n\t/* fill in remaining fields */\n\tAssert(tableEntry->partitionMethod != 0);\n\tshardPlacement->partitionMethod = tableEntry->partitionMethod;\n\tshardPlacement->colocationGroupId = tableEntry->colocationId;\n\tif (tableEntry->partitionMethod == DISTRIBUTE_BY_HASH)\n\t{\n\t\tAssert(shardInterval->minValueExists);\n\t\tAssert(shardInterval->valueTypeId == INT4OID);\n\n\t\t/*\n\t\t * Use the lower boundary of the interval's range to identify\n\t\t * it for colocation purposes. That remains meaningful even if\n\t\t * a concurrent session splits a shard.\n\t\t */\n\t\tshardPlacement->representativeValue = DatumGetInt32(shardInterval->minValue);\n\t}\n\telse\n\t{\n\t\tshardPlacement->representativeValue = 0;\n\t}\n\n\treturn shardPlacement;\n}\n\n\n/*\n * HasAnyNodes returns whether there are any nodes in pg_dist_node.\n */\nbool\nHasAnyNodes(void)\n{\n\tPrepareWorkerNodeCache();\n\n\treturn WorkerNodeCount > 0;\n}\n\n\n/*\n * LookupNodeByNodeId returns a worker node by nodeId or NULL if the node\n * cannot be found.\n */\nWorkerNode *\nLookupNodeByNodeId(uint32 nodeId)\n{\n\tPrepareWorkerNodeCache();\n\n\tfor (int workerNodeIndex = 0; workerNodeIndex < WorkerNodeCount; workerNodeIndex++)\n\t{\n\t\tWorkerNode *workerNode = WorkerNodeArray[workerNodeIndex];\n\t\tif (workerNode->nodeId == nodeId)\n\t\t{\n\t\t\tWorkerNode *workerNodeCopy = palloc0(sizeof(WorkerNode));\n\t\t\t*workerNodeCopy = *workerNode;\n\n\t\t\treturn workerNodeCopy;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * LookupNodeByNodeIdOrError returns a worker node by nodeId or errors out if the\n * node cannot be found.\n */\nWorkerNode *\nLookupNodeByNodeIdOrError(uint32 nodeId)\n{\n\tWorkerNode *node = LookupNodeByNodeId(nodeId);\n\tif (node == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"node %d could not be found\", nodeId)));\n\t}\n\treturn node;\n}\n\n\n/*\n * LookupNodeForGroup searches the WorkerNodeHash for a worker which is a member of the\n * given group and also readable (a primary if we're reading from primaries, a secondary\n * if we're reading from secondaries). If such a node does not exist it emits an\n * appropriate error message.\n */\nWorkerNode *\nLookupNodeForGroup(int32 groupId)\n{\n\tbool foundAnyNodes = false;\n\n\tPrepareWorkerNodeCache();\n\n\tfor (int workerNodeIndex = 0; workerNodeIndex < WorkerNodeCount; workerNodeIndex++)\n\t{\n\t\tWorkerNode *workerNode = WorkerNodeArray[workerNodeIndex];\n\t\tint32 workerNodeGroupId = workerNode->groupId;\n\t\tif (workerNodeGroupId != groupId)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tfoundAnyNodes = true;\n\n\t\tif (NodeIsReadable(workerNode))\n\t\t{\n\t\t\treturn workerNode;\n\t\t}\n\t}\n\n\tif (!foundAnyNodes)\n\t{\n\t\tereport(ERROR, (errmsg(\"there is a shard placement in node group %d but \"\n\t\t\t\t\t\t\t   \"there are no nodes in that group\", groupId)));\n\t}\n\n\tswitch (ReadFromSecondaries)\n\t{\n\t\tcase USE_SECONDARY_NODES_NEVER:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"node group %d does not have a primary node\",\n\t\t\t\t\t\t\t\t   groupId)));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase USE_SECONDARY_NODES_ALWAYS:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"node group %d does not have a secondary node\",\n\t\t\t\t\t\t\t\t   groupId)));\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(FATAL, (errmsg(\"unrecognized value for use_secondary_nodes\")));\n\t\t}\n\t}\n}\n\n\n/*\n * ShardPlacementList returns the list of placements for the given shard from\n * the cache.\n *\n * The returned list is deep copied from the cache and thus can be modified\n * and pfree()d freely.\n */\nList *\nShardPlacementList(uint64 shardId)\n{\n\tList *placementList = NIL;\n\n\tbool missingOk = false;\n\tShardIdCacheEntry *shardIdEntry = LookupShardIdCacheEntry(shardId, missingOk);\n\tCitusTableCacheEntry *tableEntry = shardIdEntry->tableEntry;\n\tint shardIndex = shardIdEntry->shardIndex;\n\n\t/* the offset better be in a valid range */\n\tAssert(shardIndex < tableEntry->shardIntervalArrayLength);\n\n\tGroupShardPlacement *placementArray =\n\t\ttableEntry->arrayOfPlacementArrays[shardIndex];\n\tint numberOfPlacements =\n\t\ttableEntry->arrayOfPlacementArrayLengths[shardIndex];\n\n\tfor (int i = 0; i < numberOfPlacements; i++)\n\t{\n\t\tGroupShardPlacement *groupShardPlacement = &placementArray[i];\n\t\tShardPlacement *shardPlacement = ResolveGroupShardPlacement(groupShardPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttableEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardIndex);\n\n\t\tplacementList = lappend(placementList, shardPlacement);\n\t}\n\n\t/* if no shard placements are found, warn the user */\n\tif (numberOfPlacements == 0)\n\t{\n\t\tereport(WARNING, (errmsg(\"could not find any shard placements for shardId \"\n\t\t\t\t\t\t\t\t UINT64_FORMAT, shardId)));\n\t}\n\n\treturn placementList;\n}\n\n\n/*\n * InitializeTableCacheEntry initializes a shard in cache.  A possible reason\n * for not finding an entry in the cache is that the distributed table's cache\n * entry hasn't been accessed yet. Thus look up the distributed table, and\n * build the cache entry. Afterwards we know that the shard has to be in the\n * cache if it exists. If the shard does *not* exist, this function errors\n * (because LookupShardRelationFromCatalog errors out).\n *\n * If missingOk is true and the shard cannot be found, the function returns false.\n */\nstatic bool\nInitializeTableCacheEntry(int64 shardId, bool missingOk)\n{\n\tOid relationId = LookupShardRelationFromCatalog(shardId, missingOk);\n\n\tif (!OidIsValid(relationId))\n\t{\n\t\tAssert(missingOk);\n\t\treturn false;\n\t}\n\n\t/* trigger building the cache for the shard id */\n\tGetCitusTableCacheEntry(relationId); /* lgtm[cpp/return-value-ignored] */\n\n\treturn true;\n}\n\n\n/*\n * RefreshTableCacheEntryIfInvalid checks if the cache entry is still valid and\n * refreshes it in cache when it's not. It returns true if it refreshed the\n * entry in the cache and false if it didn't.\n */\nstatic bool\nRefreshTableCacheEntryIfInvalid(ShardIdCacheEntry *shardEntry, bool missingOk)\n{\n\t/*\n\t * We might have some concurrent metadata changes. In order to get the changes,\n\t * we first need to accept the cache invalidation messages.\n\t */\n\tAcceptInvalidationMessages();\n\tif (shardEntry->tableEntry->isValid)\n\t{\n\t\treturn false;\n\t}\n\tOid oldRelationId = shardEntry->tableEntry->relationId;\n\tOid currentRelationId = LookupShardRelationFromCatalog(shardEntry->shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   missingOk);\n\n\t/*\n\t * The relation OID to which the shard belongs could have changed,\n\t * most notably when the extension is dropped and a shard ID is\n\t * reused. Reload the cache entries for both old and new relation\n\t * ID and then look up the shard entry again.\n\t */\n\tLookupCitusTableCacheEntry(oldRelationId);\n\tLookupCitusTableCacheEntry(currentRelationId);\n\treturn true;\n}\n\n\n/*\n * LookupShardCacheEntry returns the cache entry belonging to a shard.\n * It errors out if that shard is unknown and missingOk is false. Else,\n * it will return a NULL cache entry.\n */\nstatic ShardIdCacheEntry *\nLookupShardIdCacheEntry(int64 shardId, bool missingOk)\n{\n\tbool foundInCache = false;\n\tbool recheck = false;\n\n\tAssert(CitusHasBeenLoaded() && CheckCitusVersion(WARNING));\n\n\tInitializeCaches();\n\n\tShardIdCacheEntry *shardEntry =\n\t\thash_search(ShardIdCacheHash, &shardId, HASH_FIND, &foundInCache);\n\n\tif (!foundInCache)\n\t{\n\t\tif (!InitializeTableCacheEntry(shardId, missingOk))\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\trecheck = true;\n\t}\n\telse\n\t{\n\t\trecheck = RefreshTableCacheEntryIfInvalid(shardEntry, missingOk);\n\t}\n\n\t/*\n\t * If we (re-)loaded the table cache, re-search the shard cache - the\n\t * shard index might have changed.  If we still can't find the entry, it\n\t * can't exist.\n\t */\n\tif (recheck)\n\t{\n\t\tshardEntry = hash_search(ShardIdCacheHash, &shardId, HASH_FIND, &foundInCache);\n\n\t\tif (!foundInCache)\n\t\t{\n\t\t\tint eflag = (missingOk) ? DEBUG1 : ERROR;\n\t\t\tereport(eflag, (errmsg(\"could not find valid entry for shard \"\n\t\t\t\t\t\t\t\t   UINT64_FORMAT, shardId)));\n\t\t}\n\t}\n\n\treturn shardEntry;\n}\n\n\n/*\n * GetCitusTableCacheEntry looks up a pg_dist_partition entry for a\n * relation.\n *\n * Errors out if no relation matching the criteria could be found.\n */\nCitusTableCacheEntry *\nGetCitusTableCacheEntry(Oid distributedRelationId)\n{\n\tCitusTableCacheEntry *cacheEntry =\n\t\tLookupCitusTableCacheEntry(distributedRelationId);\n\n\tif (cacheEntry)\n\t{\n\t\treturn cacheEntry;\n\t}\n\telse\n\t{\n\t\tchar *relationName = get_rel_name(distributedRelationId);\n\n\t\tif (relationName == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"relation with OID %u does not exist\",\n\t\t\t\t\t\t\t\t   distributedRelationId)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"relation %s is not distributed\", relationName)));\n\t\t}\n\t}\n}\n\n\n/*\n * GetCitusTableCacheEntry returns the distributed table metadata for the\n * passed relationId. For efficiency it caches lookups. This function returns\n * NULL if the relation isn't a distributed table.\n */\nCitusTableCacheEntry *\nLookupCitusTableCacheEntry(Oid relationId)\n{\n\tbool foundInCache = false;\n\tvoid *hashKey = (void *) &relationId;\n\n\t/*\n\t * Can't be a distributed relation if the extension hasn't been loaded\n\t * yet. As we can't do lookups in nonexistent tables, directly return NULL\n\t * here.\n\t */\n\tif (!CitusHasBeenLoaded())\n\t{\n\t\treturn NULL;\n\t}\n\n\tInitializeCaches();\n\n\t/*\n\t * If the version is not known to be compatible, perform thorough check,\n\t * unless such checks are disabled.\n\t */\n\tif (!citusVersionKnownCompatible && EnableVersionChecks)\n\t{\n\t\tbool isCitusTable = IsCitusTableViaCatalog(relationId);\n\t\tint reportLevel = DEBUG1;\n\n\t\t/*\n\t\t * If there's a version-mismatch, and we're dealing with a distributed\n\t\t * table, we have to error out as we can't return a valid entry.  We\n\t\t * want to check compatibility in the non-distributed case as well, so\n\t\t * future lookups can use the cache if compatible.\n\t\t */\n\t\tif (isCitusTable)\n\t\t{\n\t\t\treportLevel = ERROR;\n\t\t}\n\n\t\tif (!CheckCitusVersion(reportLevel))\n\t\t{\n\t\t\t/* incompatible, can't access cache, so return before doing so */\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\t/*\n\t * We might have some concurrent metadata changes. In order to get the changes,\n\t * we first need to accept the cache invalidation messages.\n\t */\n\tAcceptInvalidationMessages();\n\tCitusTableCacheEntrySlot *cacheSlot =\n\t\thash_search(DistTableCacheHash, hashKey, HASH_ENTER, &foundInCache);\n\n\t/* return valid matches */\n\tif (foundInCache)\n\t{\n\t\tif (cacheSlot->isValid)\n\t\t{\n\t\t\treturn cacheSlot->citusTableMetadata;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * An invalidation was received or we encountered an OOM while building\n\t\t\t * the cache entry. We need to rebuild it.\n\t\t\t */\n\n\t\t\tif (cacheSlot->citusTableMetadata)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * The CitusTableCacheEntry might still be in use. We therefore do\n\t\t\t\t * not reset it until the end of the transaction.\n\t\t\t\t */\n\t\t\t\tMemoryContext oldContext =\n\t\t\t\t\tMemoryContextSwitchTo(MetadataCacheMemoryContext);\n\n\t\t\t\tDistTableCacheExpired = lappend(DistTableCacheExpired,\n\t\t\t\t\t\t\t\t\t\t\t\tcacheSlot->citusTableMetadata);\n\n\t\t\t\tMemoryContextSwitchTo(oldContext);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* zero out entry, but not the key part */\n\tmemset(((char *) cacheSlot) + sizeof(Oid), 0,\n\t\t   sizeof(CitusTableCacheEntrySlot) - sizeof(Oid));\n\n\t/*\n\t * We disable interrupts while creating the cache entry because loading\n\t * shard metadata can take a while, and if statement_timeout is too low,\n\t * this will get canceled on each call and we won't be able to run any\n\t * queries on the table.\n\t */\n\tHOLD_INTERRUPTS();\n\n\tcacheSlot->citusTableMetadata = BuildCitusTableCacheEntry(relationId);\n\n\t/*\n\t * Mark it as valid only after building the full entry, such that any\n\t * error that happened during the build would trigger a rebuild.\n\t */\n\tcacheSlot->isValid = true;\n\n\tRESUME_INTERRUPTS();\n\n\treturn cacheSlot->citusTableMetadata;\n}\n\n\n/*\n * LookupDistObjectCacheEntry returns the distributed table metadata for the\n * passed relationId. For efficiency it caches lookups.\n */\nDistObjectCacheEntry *\nLookupDistObjectCacheEntry(Oid classid, Oid objid, int32 objsubid)\n{\n\tbool foundInCache = false;\n\tDistObjectCacheEntryKey hashKey;\n\tScanKeyData pgDistObjectKey[3];\n\n\tmemset(&hashKey, 0, sizeof(DistObjectCacheEntryKey));\n\thashKey.classid = classid;\n\thashKey.objid = objid;\n\thashKey.objsubid = objsubid;\n\n\t/*\n\t * Can't be a distributed relation if the extension hasn't been loaded\n\t * yet. As we can't do lookups in nonexistent tables, directly return NULL\n\t * here.\n\t */\n\tif (!CitusHasBeenLoaded())\n\t{\n\t\treturn NULL;\n\t}\n\n\tInitializeCaches();\n\n\tDistObjectCacheEntry *cacheEntry = hash_search(DistObjectCacheHash, &hashKey,\n\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER, &foundInCache);\n\n\t/* return valid matches */\n\tif (foundInCache)\n\t{\n\t\t/*\n\t\t * We might have some concurrent metadata changes. In order to get the changes,\n\t\t * we first need to accept the cache invalidation messages.\n\t\t */\n\t\tAcceptInvalidationMessages();\n\n\t\tif (cacheEntry->isValid)\n\t\t{\n\t\t\treturn cacheEntry;\n\t\t}\n\n\t\t/*\n\t\t * This is where we'd free the old entry's out of band data if it had any.\n\t\t * Right now we don't have anything to free.\n\t\t */\n\t}\n\n\t/* zero out entry, but not the key part */\n\tmemset(((char *) cacheEntry), 0, sizeof(DistObjectCacheEntry));\n\tcacheEntry->key.classid = classid;\n\tcacheEntry->key.objid = objid;\n\tcacheEntry->key.objsubid = objsubid;\n\n\tRelation pgDistObjectRel = table_open(DistObjectRelationId(), AccessShareLock);\n\tTupleDesc pgDistObjectTupleDesc = RelationGetDescr(pgDistObjectRel);\n\n\tScanKeyInit(&pgDistObjectKey[0], Anum_pg_dist_object_classid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(classid));\n\tScanKeyInit(&pgDistObjectKey[1], Anum_pg_dist_object_objid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(objid));\n\tScanKeyInit(&pgDistObjectKey[2], Anum_pg_dist_object_objsubid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(objsubid));\n\n\tSysScanDesc pgDistObjectScan = systable_beginscan(pgDistObjectRel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  DistObjectPrimaryKeyIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  true, NULL, 3, pgDistObjectKey);\n\tHeapTuple pgDistObjectTup = systable_getnext(pgDistObjectScan);\n\n\tif (HeapTupleIsValid(pgDistObjectTup))\n\t{\n\t\tDatum *datumArray = palloc(pgDistObjectTupleDesc->natts * sizeof(Datum));\n\t\tbool *isNullArray = palloc(pgDistObjectTupleDesc->natts * sizeof(bool));\n\n\t\tint forseDelegationIndex =\n\t\t\tGetForceDelegationAttrIndexInPgDistObject(pgDistObjectTupleDesc);\n\n\t\theap_deform_tuple(pgDistObjectTup, pgDistObjectTupleDesc, datumArray,\n\t\t\t\t\t\t  isNullArray);\n\n\t\tcacheEntry->isValid = true;\n\t\tcacheEntry->isDistributed = true;\n\n\t\tcacheEntry->distributionArgIndex =\n\t\t\tDatumGetInt32(datumArray[Anum_pg_dist_object_distribution_argument_index -\n\t\t\t\t\t\t\t\t\t 1]);\n\t\tcacheEntry->colocationId =\n\t\t\tDatumGetInt32(datumArray[Anum_pg_dist_object_colocationid - 1]);\n\n\t\tcacheEntry->forceDelegation =\n\t\t\tDatumGetBool(datumArray[forseDelegationIndex]);\n\n\t\tpfree(datumArray);\n\t\tpfree(isNullArray);\n\t}\n\telse\n\t{\n\t\tcacheEntry->isValid = true;\n\t\tcacheEntry->isDistributed = false;\n\t}\n\n\tsystable_endscan(pgDistObjectScan);\n\trelation_close(pgDistObjectRel, AccessShareLock);\n\n\treturn cacheEntry;\n}\n\n\n/*\n * BuildCitusTableCacheEntry is a helper routine for\n * LookupCitusTableCacheEntry() for building the cache contents.\n * This function returns NULL if the relation isn't a distributed table.\n */\nstatic CitusTableCacheEntry *\nBuildCitusTableCacheEntry(Oid relationId)\n{\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), AccessShareLock);\n\tHeapTuple distPartitionTuple =\n\t\tLookupDistPartitionTuple(pgDistPartition, relationId);\n\n\tif (distPartitionTuple == NULL)\n\t{\n\t\t/* not a distributed table, done */\n\t\ttable_close(pgDistPartition, NoLock);\n\t\treturn NULL;\n\t}\n\n\tMemoryContext oldContext = NULL;\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\tDatum *datumArray = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNullArray = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\n\theap_deform_tuple(distPartitionTuple, tupleDescriptor, datumArray, isNullArray);\n\n\tCitusTableCacheEntry *cacheEntry =\n\t\tMemoryContextAllocZero(MetadataCacheMemoryContext, sizeof(CitusTableCacheEntry));\n\n\tcacheEntry->relationId = relationId;\n\n\tcacheEntry->partitionMethod = datumArray[Anum_pg_dist_partition_partmethod - 1];\n\tDatum partitionKeyDatum = datumArray[Anum_pg_dist_partition_partkey - 1];\n\tbool partitionKeyIsNull = isNullArray[Anum_pg_dist_partition_partkey - 1];\n\n\t/* note that for reference tables partitionKeyisNull is true */\n\tif (!partitionKeyIsNull)\n\t{\n\t\toldContext = MemoryContextSwitchTo(MetadataCacheMemoryContext);\n\n\t\t/* get the string representation of the partition column Var */\n\t\tcacheEntry->partitionKeyString = TextDatumGetCString(partitionKeyDatum);\n\n\t\t/* convert the string to a Node and ensure it is a Var */\n\t\tNode *partitionNode = stringToNode(cacheEntry->partitionKeyString);\n\t\tAssert(IsA(partitionNode, Var));\n\n\t\tcacheEntry->partitionColumn = (Var *) partitionNode;\n\n\t\tMemoryContextSwitchTo(oldContext);\n\t}\n\telse\n\t{\n\t\tcacheEntry->partitionKeyString = NULL;\n\t}\n\n\tcacheEntry->colocationId = datumArray[Anum_pg_dist_partition_colocationid - 1];\n\tif (isNullArray[Anum_pg_dist_partition_colocationid - 1])\n\t{\n\t\tcacheEntry->colocationId = INVALID_COLOCATION_ID;\n\t}\n\n\tDatum replicationModelDatum = datumArray[Anum_pg_dist_partition_repmodel - 1];\n\tif (isNullArray[Anum_pg_dist_partition_repmodel - 1])\n\t{\n\t\t/*\n\t\t * repmodel is NOT NULL but before ALTER EXTENSION citus UPGRADE the column\n\t\t * doesn't exist\n\t\t */\n\t\tcacheEntry->replicationModel = 'c';\n\t}\n\telse\n\t{\n\t\tcacheEntry->replicationModel = DatumGetChar(replicationModelDatum);\n\t}\n\n\tif (isNullArray[GetAutoConvertedAttrIndexInPgDistPartition(tupleDescriptor)])\n\t{\n\t\t/*\n\t\t * We don't expect this to happen, but set it to false (the default value)\n\t\t * to not break if anything goes wrong.\n\t\t */\n\t\tcacheEntry->autoConverted = false;\n\t}\n\telse\n\t{\n\t\tcacheEntry->autoConverted = DatumGetBool(\n\t\t\tdatumArray[GetAutoConvertedAttrIndexInPgDistPartition(tupleDescriptor)]);\n\t}\n\n\theap_freetuple(distPartitionTuple);\n\n\tBuildCachedShardList(cacheEntry);\n\n\t/* we only need hash functions for hash distributed tables */\n\tif (cacheEntry->partitionMethod == DISTRIBUTE_BY_HASH)\n\t{\n\t\tVar *partitionColumn = cacheEntry->partitionColumn;\n\n\t\tTypeCacheEntry *typeEntry = lookup_type_cache(partitionColumn->vartype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  TYPECACHE_HASH_PROC_FINFO);\n\n\t\tFmgrInfo *hashFunction = MemoryContextAllocZero(MetadataCacheMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsizeof(FmgrInfo));\n\n\t\tfmgr_info_copy(hashFunction, &(typeEntry->hash_proc_finfo),\n\t\t\t\t\t   MetadataCacheMemoryContext);\n\n\t\tcacheEntry->hashFunction = hashFunction;\n\n\t\t/* check the shard distribution for hash partitioned tables */\n\t\tcacheEntry->hasUniformHashDistribution =\n\t\t\tHasUniformHashDistribution(cacheEntry->sortedShardIntervalArray,\n\t\t\t\t\t\t\t\t\t   cacheEntry->shardIntervalArrayLength);\n\t}\n\telse\n\t{\n\t\tcacheEntry->hashFunction = NULL;\n\t}\n\n\toldContext = MemoryContextSwitchTo(MetadataCacheMemoryContext);\n\n\tcacheEntry->referencedRelationsViaForeignKey = ReferencedRelationIdList(\n\t\tcacheEntry->relationId);\n\tcacheEntry->referencingRelationsViaForeignKey = ReferencingRelationIdList(\n\t\tcacheEntry->relationId);\n\n\tMemoryContextSwitchTo(oldContext);\n\n\ttable_close(pgDistPartition, NoLock);\n\n\tpfree(datumArray);\n\tpfree(isNullArray);\n\n\tcacheEntry->isValid = true;\n\n\treturn cacheEntry;\n}\n\n\n/*\n * BuildCachedShardList() is a helper routine for BuildCitusTableCacheEntry()\n * building up the list of shards in a distributed relation.\n */\nstatic void\nBuildCachedShardList(CitusTableCacheEntry *cacheEntry)\n{\n\tShardInterval **shardIntervalArray = NULL;\n\tShardInterval **sortedShardIntervalArray = NULL;\n\tFmgrInfo *shardIntervalCompareFunction = NULL;\n\tFmgrInfo *shardColumnCompareFunction = NULL;\n\tOid columnTypeId = InvalidOid;\n\tint32 columnTypeMod = -1;\n\tOid intervalTypeId = InvalidOid;\n\tint32 intervalTypeMod = -1;\n\n\tGetPartitionTypeInputInfo(cacheEntry->partitionKeyString,\n\t\t\t\t\t\t\t  cacheEntry->partitionMethod,\n\t\t\t\t\t\t\t  &columnTypeId,\n\t\t\t\t\t\t\t  &columnTypeMod,\n\t\t\t\t\t\t\t  &intervalTypeId,\n\t\t\t\t\t\t\t  &intervalTypeMod);\n\n\tList *distShardTupleList = LookupDistShardTuples(cacheEntry->relationId);\n\tint shardIntervalArrayLength = list_length(distShardTupleList);\n\tif (shardIntervalArrayLength > 0)\n\t{\n\t\tRelation distShardRelation = table_open(DistShardRelationId(), AccessShareLock);\n\t\tTupleDesc distShardTupleDesc = RelationGetDescr(distShardRelation);\n\t\tint arrayIndex = 0;\n\n\t\tshardIntervalArray = MemoryContextAllocZero(MetadataCacheMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\tshardIntervalArrayLength *\n\t\t\t\t\t\t\t\t\t\t\t\t\tsizeof(ShardInterval *));\n\n\t\tcacheEntry->arrayOfPlacementArrays =\n\t\t\tMemoryContextAllocZero(MetadataCacheMemoryContext,\n\t\t\t\t\t\t\t\t   shardIntervalArrayLength *\n\t\t\t\t\t\t\t\t   sizeof(GroupShardPlacement *));\n\t\tcacheEntry->arrayOfPlacementArrayLengths =\n\t\t\tMemoryContextAllocZero(MetadataCacheMemoryContext,\n\t\t\t\t\t\t\t\t   shardIntervalArrayLength *\n\t\t\t\t\t\t\t\t   sizeof(int));\n\n\t\tHeapTuple shardTuple = NULL;\n\t\tforeach_declared_ptr(shardTuple, distShardTupleList)\n\t\t{\n\t\t\tShardInterval *shardInterval = TupleToShardInterval(shardTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdistShardTupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tintervalTypeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tintervalTypeMod);\n\t\t\tMemoryContext oldContext = MemoryContextSwitchTo(MetadataCacheMemoryContext);\n\n\t\t\tshardIntervalArray[arrayIndex] = CopyShardInterval(shardInterval);\n\n\t\t\tMemoryContextSwitchTo(oldContext);\n\n\t\t\theap_freetuple(shardTuple);\n\n\t\t\tarrayIndex++;\n\t\t}\n\n\t\ttable_close(distShardRelation, AccessShareLock);\n\t}\n\n\t/* look up value comparison function */\n\tif (columnTypeId != InvalidOid)\n\t{\n\t\t/* allocate the comparison function in the cache context */\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(MetadataCacheMemoryContext);\n\n\t\tshardColumnCompareFunction = GetFunctionInfo(columnTypeId, BTREE_AM_OID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t BTORDER_PROC);\n\t\tMemoryContextSwitchTo(oldContext);\n\t}\n\telse\n\t{\n\t\tshardColumnCompareFunction = NULL;\n\t}\n\n\t/* look up interval comparison function */\n\tif (intervalTypeId != InvalidOid)\n\t{\n\t\t/* allocate the comparison function in the cache context */\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(MetadataCacheMemoryContext);\n\n\t\tshardIntervalCompareFunction = GetFunctionInfo(intervalTypeId, BTREE_AM_OID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   BTORDER_PROC);\n\t\tMemoryContextSwitchTo(oldContext);\n\t}\n\telse\n\t{\n\t\tshardIntervalCompareFunction = NULL;\n\t}\n\n\t/* reference tables has a single shard which is not initialized */\n\tif (cacheEntry->partitionMethod == DISTRIBUTE_BY_NONE)\n\t{\n\t\tcacheEntry->hasUninitializedShardInterval = true;\n\t\tcacheEntry->hasOverlappingShardInterval = true;\n\n\t\t/*\n\t\t * Note that during create_reference_table() call,\n\t\t * the reference table do not have any shards.\n\t\t */\n\t\tif (shardIntervalArrayLength > 1)\n\t\t{\n\t\t\tchar *relationName = get_rel_name(cacheEntry->relationId);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"reference table \\\"%s\\\" has more than 1 shard\",\n\t\t\t\t\t\t\t\t   relationName)));\n\t\t}\n\n\t\t/* since there is a zero or one shard, it is already sorted */\n\t\tsortedShardIntervalArray = shardIntervalArray;\n\t}\n\telse\n\t{\n\t\t/* sort the interval array */\n\t\tsortedShardIntervalArray = SortShardIntervalArray(shardIntervalArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardIntervalArrayLength,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  cacheEntry->partitionColumn->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  varcollid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardIntervalCompareFunction);\n\n\t\t/* check if there exists any shard intervals with no min/max values */\n\t\tcacheEntry->hasUninitializedShardInterval =\n\t\t\tHasUninitializedShardInterval(sortedShardIntervalArray,\n\t\t\t\t\t\t\t\t\t\t  shardIntervalArrayLength);\n\n\t\tif (!cacheEntry->hasUninitializedShardInterval)\n\t\t{\n\t\t\tcacheEntry->hasOverlappingShardInterval =\n\t\t\t\tHasOverlappingShardInterval(sortedShardIntervalArray,\n\t\t\t\t\t\t\t\t\t\t\tshardIntervalArrayLength,\n\t\t\t\t\t\t\t\t\t\t\tcacheEntry->partitionColumn->varcollid,\n\t\t\t\t\t\t\t\t\t\t\tshardIntervalCompareFunction);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcacheEntry->hasOverlappingShardInterval = true;\n\t\t}\n\n\t\tErrorIfInconsistentShardIntervals(cacheEntry);\n\t}\n\n\tcacheEntry->sortedShardIntervalArray = sortedShardIntervalArray;\n\tcacheEntry->shardIntervalArrayLength = 0;\n\n\t/* maintain shardId->(table,ShardInterval) cache */\n\tfor (int shardIndex = 0; shardIndex < shardIntervalArrayLength; shardIndex++)\n\t{\n\t\tShardInterval *shardInterval = sortedShardIntervalArray[shardIndex];\n\t\tint64 shardId = shardInterval->shardId;\n\t\tint placementOffset = 0;\n\n\t\t/*\n\t\t * Enable quick lookups of this shard ID by adding it to ShardIdCacheHash\n\t\t * or overwriting the previous values.\n\t\t */\n\t\tShardIdCacheEntry *shardIdCacheEntry =\n\t\t\thash_search(ShardIdCacheHash, &shardId, HASH_ENTER, NULL);\n\n\t\tshardIdCacheEntry->tableEntry = cacheEntry;\n\t\tshardIdCacheEntry->shardIndex = shardIndex;\n\n\t\t/*\n\t\t * We should increment this only after we are sure this hasn't already\n\t\t * been assigned to any other relations. ResetCitusTableCacheEntry()\n\t\t * depends on this.\n\t\t */\n\t\tcacheEntry->shardIntervalArrayLength++;\n\n\t\t/* build list of shard placements */\n\t\tList *placementList = BuildShardPlacementList(shardId);\n\t\tint numberOfPlacements = list_length(placementList);\n\n\t\t/* and copy that list into the cache entry */\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(MetadataCacheMemoryContext);\n\t\tGroupShardPlacement *placementArray = palloc0(numberOfPlacements *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  sizeof(GroupShardPlacement));\n\t\tGroupShardPlacement *srcPlacement = NULL;\n\t\tforeach_declared_ptr(srcPlacement, placementList)\n\t\t{\n\t\t\tplacementArray[placementOffset] = *srcPlacement;\n\t\t\tplacementOffset++;\n\t\t}\n\t\tMemoryContextSwitchTo(oldContext);\n\n\t\tcacheEntry->arrayOfPlacementArrays[shardIndex] = placementArray;\n\t\tcacheEntry->arrayOfPlacementArrayLengths[shardIndex] = numberOfPlacements;\n\n\t\t/* store the shard index in the ShardInterval */\n\t\tshardInterval->shardIndex = shardIndex;\n\t}\n\n\tcacheEntry->shardColumnCompareFunction = shardColumnCompareFunction;\n\tcacheEntry->shardIntervalCompareFunction = shardIntervalCompareFunction;\n}\n\n\n/*\n * ErrorIfInconsistentShardIntervals checks if shard intervals are consistent with\n * our expectations.\n */\nvoid\nErrorIfInconsistentShardIntervals(CitusTableCacheEntry *cacheEntry)\n{\n\t/*\n\t * If table is hash-partitioned and has shards, there never should be any\n\t * uninitalized shards.  Historically we've not prevented that for range\n\t * partitioned tables, but it might be a good idea to start doing so.\n\t */\n\tif (cacheEntry->partitionMethod == DISTRIBUTE_BY_HASH &&\n\t\tcacheEntry->hasUninitializedShardInterval)\n\t{\n\t\tereport(ERROR, (errmsg(\"hash partitioned table has uninitialized shards\")));\n\t}\n\tif (cacheEntry->partitionMethod == DISTRIBUTE_BY_HASH &&\n\t\tcacheEntry->hasOverlappingShardInterval)\n\t{\n\t\tereport(ERROR, (errmsg(\"hash partitioned table has overlapping shards\")));\n\t}\n}\n\n\n/*\n * HasUniformHashDistribution determines whether the given list of sorted shards\n * has a uniform hash distribution, as produced by master_create_worker_shards for\n * hash partitioned tables.\n */\nbool\nHasUniformHashDistribution(ShardInterval **shardIntervalArray,\n\t\t\t\t\t\t   int shardIntervalArrayLength)\n{\n\t/* if there are no shards, there is no uniform distribution */\n\tif (shardIntervalArrayLength == 0)\n\t{\n\t\treturn false;\n\t}\n\n\t/* calculate the hash token increment */\n\tuint64 hashTokenIncrement = HASH_TOKEN_COUNT / shardIntervalArrayLength;\n\n\tfor (int shardIndex = 0; shardIndex < shardIntervalArrayLength; shardIndex++)\n\t{\n\t\tShardInterval *shardInterval = shardIntervalArray[shardIndex];\n\t\tint32 shardMinHashToken = PG_INT32_MIN + (shardIndex * hashTokenIncrement);\n\t\tint32 shardMaxHashToken = shardMinHashToken + (hashTokenIncrement - 1);\n\n\t\tif (shardIndex == (shardIntervalArrayLength - 1))\n\t\t{\n\t\t\tshardMaxHashToken = PG_INT32_MAX;\n\t\t}\n\n\t\tif (DatumGetInt32(shardInterval->minValue) != shardMinHashToken ||\n\t\t\tDatumGetInt32(shardInterval->maxValue) != shardMaxHashToken)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * HasUninitializedShardInterval returns true if all the elements of the\n * sortedShardIntervalArray has min/max values. Callers of the function must\n * ensure that input shard interval array is sorted on shardminvalue and uninitialized\n * shard intervals are at the end of the array.\n */\nbool\nHasUninitializedShardInterval(ShardInterval **sortedShardIntervalArray, int shardCount)\n{\n\tbool hasUninitializedShardInterval = false;\n\n\tif (shardCount == 0)\n\t{\n\t\treturn hasUninitializedShardInterval;\n\t}\n\n\tAssert(sortedShardIntervalArray != NULL);\n\n\t/*\n\t * Since the shard interval array is sorted, and uninitialized ones stored\n\t * in the end of the array, checking the last element is enough.\n\t */\n\tShardInterval *lastShardInterval = sortedShardIntervalArray[shardCount - 1];\n\tif (!lastShardInterval->minValueExists || !lastShardInterval->maxValueExists)\n\t{\n\t\thasUninitializedShardInterval = true;\n\t}\n\n\treturn hasUninitializedShardInterval;\n}\n\n\n/*\n * HasOverlappingShardInterval determines whether the given list of sorted\n * shards has overlapping ranges.\n */\nbool\nHasOverlappingShardInterval(ShardInterval **shardIntervalArray,\n\t\t\t\t\t\t\tint shardIntervalArrayLength,\n\t\t\t\t\t\t\tOid shardIntervalCollation,\n\t\t\t\t\t\t\tFmgrInfo *shardIntervalSortCompareFunction)\n{\n\tDatum comparisonDatum = 0;\n\tint comparisonResult = 0;\n\n\t/* zero/a single shard can't overlap */\n\tif (shardIntervalArrayLength < 2)\n\t{\n\t\treturn false;\n\t}\n\n\tShardInterval *lastShardInterval = shardIntervalArray[0];\n\tfor (int shardIndex = 1; shardIndex < shardIntervalArrayLength; shardIndex++)\n\t{\n\t\tShardInterval *curShardInterval = shardIntervalArray[shardIndex];\n\n\t\t/* only called if !hasUninitializedShardInterval */\n\t\tAssert(lastShardInterval->minValueExists && lastShardInterval->maxValueExists);\n\t\tAssert(curShardInterval->minValueExists && curShardInterval->maxValueExists);\n\n\t\tcomparisonDatum = FunctionCall2Coll(shardIntervalSortCompareFunction,\n\t\t\t\t\t\t\t\t\t\t\tshardIntervalCollation,\n\t\t\t\t\t\t\t\t\t\t\tlastShardInterval->maxValue,\n\t\t\t\t\t\t\t\t\t\t\tcurShardInterval->minValue);\n\t\tcomparisonResult = DatumGetInt32(comparisonDatum);\n\n\t\tif (comparisonResult >= 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tlastShardInterval = curShardInterval;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CitusHasBeenLoaded returns true if the citus extension has been created\n * in the current database and the extension script has been executed. Otherwise,\n * it returns false. The result is cached as this is called very frequently.\n */\nbool\nCitusHasBeenLoaded(void)\n{\n\t/*\n\t * We do not use Citus hooks during CREATE/ALTER EXTENSION citus\n\t * since the objects used by the C code might be not be there yet.\n\t */\n\tif (creating_extension)\n\t{\n\t\tOid citusExtensionOid = get_extension_oid(\"citus\", true);\n\n\t\tif (CurrentExtensionObject == citusExtensionOid)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/*\n\t * If extensionCreatedState is UNKNOWN, query pg_extension for Citus\n\t * and cache the result. Otherwise return the value extensionCreatedState\n\t * indicates.\n\t */\n\tif (MetadataCache.extensionCreatedState == UNKNOWN)\n\t{\n\t\tbool extensionCreated = CitusHasBeenLoadedInternal();\n\n\t\tif (extensionCreated)\n\t\t{\n\t\t\t/*\n\t\t\t * Loaded Citus for the first time in this session, or first time after\n\t\t\t * CREATE/ALTER EXTENSION citus. Do some initialisation.\n\t\t\t */\n\n\t\t\t/*\n\t\t\t * Make sure the maintenance daemon is running if it was not already.\n\t\t\t */\n\t\t\tStartupCitusBackend();\n\n\t\t\t/*\n\t\t\t * This needs to be initialized so we can receive foreign relation graph\n\t\t\t * invalidation messages in InvalidateForeignRelationGraphCacheCallback().\n\t\t\t * See the comments of InvalidateForeignKeyGraph for more context.\n\t\t\t */\n\t\t\tDistColocationRelationId();\n\n\t\t\tMetadataCache.extensionCreatedState = CREATED;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tMetadataCache.extensionCreatedState = NOTCREATED;\n\t\t}\n\t}\n\n\treturn (MetadataCache.extensionCreatedState == CREATED) ? true : false;\n}\n\n\n/*\n * CitusHasBeenLoadedInternal returns true if the citus extension has been created\n * in the current database and the extension script has been executed. Otherwise,\n * it returns false.\n */\nstatic bool\nCitusHasBeenLoadedInternal(void)\n{\n\tif (IsBinaryUpgrade)\n\t{\n\t\t/* never use Citus logic during pg_upgrade */\n\t\treturn false;\n\t}\n\n\tOid citusExtensionOid = get_extension_oid(\"citus\", true);\n\tif (citusExtensionOid == InvalidOid)\n\t{\n\t\t/* Citus extension does not exist yet */\n\t\treturn false;\n\t}\n\n\t/* citus extension exists and has been created */\n\treturn true;\n}\n\n\n/*\n * GetCitusCreationLevel returns the level of the transaction creating citus\n */\nint\nGetCitusCreationLevel(void)\n{\n\treturn CreateCitusTransactionLevel;\n}\n\n\n/*\n * Sets the value of CreateCitusTransactionLevel based on int received which represents the\n * nesting level of the transaction that created the Citus extension\n */\nvoid\nSetCreateCitusTransactionLevel(int val)\n{\n\tCreateCitusTransactionLevel = val;\n}\n\n\n/*\n * CheckCitusVersion checks whether there is a version mismatch between the\n * available version and the loaded version or between the installed version\n * and the loaded version. Returns true if compatible, false otherwise.\n *\n * As a side effect, this function also sets citusVersionKnownCompatible global\n * variable to true which reduces version check cost of next calls.\n */\nbool\nCheckCitusVersion(int elevel)\n{\n\tif (citusVersionKnownCompatible ||\n\t\t!CitusHasBeenLoaded() ||\n\t\t!EnableVersionChecks)\n\t{\n\t\treturn true;\n\t}\n\n\tif (CheckAvailableVersion(elevel) && CheckInstalledVersion(elevel))\n\t{\n\t\tcitusVersionKnownCompatible = true;\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\treturn false;\n\t}\n}\n\n\n/*\n * CheckAvailableVersion compares CITUS_EXTENSIONVERSION and the currently\n * available version from the citus.control file. If they are not compatible,\n * this function logs an error with the specified elevel and returns false,\n * otherwise it returns true.\n */\nbool\nCheckAvailableVersion(int elevel)\n{\n\tif (!EnableVersionChecks)\n\t{\n\t\treturn true;\n\t}\n\n\tchar *availableVersion = AvailableExtensionVersion();\n\n\tif (!MajorVersionsCompatible(availableVersion, CITUS_EXTENSIONVERSION))\n\t{\n\t\tereport(elevel, (errmsg(\"loaded Citus library version differs from latest \"\n\t\t\t\t\t\t\t\t\"available extension version\"),\n\t\t\t\t\t\t errdetail(\"Loaded library requires %s, but the latest control \"\n\t\t\t\t\t\t\t\t   \"file specifies %s.\", CITUS_MAJORVERSION,\n\t\t\t\t\t\t\t\t   availableVersion),\n\t\t\t\t\t\t errhint(\"Restart the database to load the latest Citus \"\n\t\t\t\t\t\t\t\t \"library.\")));\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * CheckInstalledVersion compares CITUS_EXTENSIONVERSION and the\n * extension's current version from the pg_extension catalog table. If they\n * are not compatible, this function logs an error with the specified elevel,\n * otherwise it returns true.\n */\nstatic bool\nCheckInstalledVersion(int elevel)\n{\n\tAssert(CitusHasBeenLoaded());\n\tAssert(EnableVersionChecks);\n\n\tchar *installedVersion = InstalledExtensionVersion();\n\n\tif (!MinorVersionsCompatibleRelaxed(installedVersion, CITUS_EXTENSIONVERSION))\n\t{\n\t\tereport(elevel, (errmsg(\"loaded Citus library version differs from installed \"\n\t\t\t\t\t\t\t\t\"extension version\"),\n\t\t\t\t\t\t errdetail(\"Loaded library requires %s, but the installed \"\n\t\t\t\t\t\t\t\t   \"extension version is %s.\", CITUS_MAJORVERSION,\n\t\t\t\t\t\t\t\t   installedVersion),\n\t\t\t\t\t\t errhint(\"Run ALTER EXTENSION citus UPDATE and try again.\")));\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * InstalledAndAvailableVersionsSame compares extension's available version and\n * its current version from the pg_extension catalog table. If they are not same\n * returns false, otherwise returns true.\n */\nbool\nInstalledAndAvailableVersionsSame()\n{\n\tchar *installedVersion = InstalledExtensionVersion();\n\tchar *availableVersion = AvailableExtensionVersion();\n\n\tif (strncmp(installedVersion, availableVersion, NAMEDATALEN) == 0)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * MajorVersionsCompatible checks whether both versions are compatible. They\n * are if major and minor version numbers match, the schema version is\n * ignored.  Returns true if compatible, false otherwise.\n */\nbool\nMajorVersionsCompatible(char *leftVersion, char *rightVersion)\n{\n\tconst char schemaVersionSeparator = '-';\n\n\tchar *leftSeperatorPosition = strchr(leftVersion, schemaVersionSeparator);\n\tchar *rightSeperatorPosition = strchr(rightVersion, schemaVersionSeparator);\n\tint leftComparisionLimit = 0;\n\tint rightComparisionLimit = 0;\n\n\tif (leftSeperatorPosition != NULL)\n\t{\n\t\tleftComparisionLimit = leftSeperatorPosition - leftVersion;\n\t}\n\telse\n\t{\n\t\tleftComparisionLimit = strlen(leftVersion);\n\t}\n\n\tif (rightSeperatorPosition != NULL)\n\t{\n\t\trightComparisionLimit = rightSeperatorPosition - rightVersion;\n\t}\n\telse\n\t{\n\t\trightComparisionLimit = strlen(leftVersion);\n\t}\n\n\t/* we can error out early if hypens are not in the same position */\n\tif (leftComparisionLimit != rightComparisionLimit)\n\t{\n\t\treturn false;\n\t}\n\n\treturn strncmp(leftVersion, rightVersion, leftComparisionLimit) == 0;\n}\n\n\n/*\n * ParseVersionComponent parses the integer at the current position and\n * advances endPtr past the parsed digits to the next character.\n */\nstatic int\nParseVersionComponent(const char *version, char **endPtr)\n{\n\terrno = 0;\n\tlong int val = strtol(version, endPtr, 10);\n\n\tif (errno == ERANGE || val > INT_MAX || val < INT_MIN)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),\n\t\t\t\t\t\terrmsg(\"Invalid integer in version string\")));\n\t}\n\treturn (int) val;\n}\n\n\n/*\n * MinorVersionsCompatibleRelaxed checks if two versions have the same major\n * version and their minor versions differ by at most 1. The schema version\n * (after '-') is ignored. Returns true if compatible, false otherwise.\n *\n * Version format expected: \"major.minor-schema\" (e.g., \"13.1-2\")\n */\nbool\nMinorVersionsCompatibleRelaxed(char *leftVersion, char *rightVersion)\n{\n\tchar *leftSep;\n\tchar *rightSep;\n\n\tint leftMajor = ParseVersionComponent(leftVersion, &leftSep);\n\tint rightMajor = ParseVersionComponent(rightVersion, &rightSep);\n\n\tif (leftMajor != rightMajor)\n\t{\n\t\treturn false;\n\t}\n\n\tint leftMinor = (*leftSep == '.') ? ParseVersionComponent(leftSep + 1, &leftSep) : 0;\n\tint rightMinor = (*rightSep == '.') ? ParseVersionComponent(rightSep + 1, &rightSep) :\n\t\t\t\t\t 0;\n\n\tint diff = leftMinor - rightMinor;\n\treturn diff >= -1 && diff <= 1;\n}\n\n\n/*\n * AvailableExtensionVersion returns the Citus version from citus.control file. It also\n * saves the result, thus consecutive calls to CitusExtensionAvailableVersion will\n * not read the citus.control file again.\n */\nstatic char *\nAvailableExtensionVersion(void)\n{\n\tLOCAL_FCINFO(fcinfo, 0);\n\tFmgrInfo flinfo;\n\n\tbool goForward = true;\n\tbool doCopy = false;\n\tchar *availableExtensionVersion;\n\n\tInitializeCaches();\n\n\tEState *estate = CreateExecutorState();\n\tReturnSetInfo *extensionsResultSet = makeNode(ReturnSetInfo);\n\textensionsResultSet->econtext = GetPerTupleExprContext(estate);\n\textensionsResultSet->allowedModes = SFRM_Materialize;\n\n\tfmgr_info(F_PG_AVAILABLE_EXTENSIONS, &flinfo);\n\tInitFunctionCallInfoData(*fcinfo, &flinfo, 0, InvalidOid, NULL,\n\t\t\t\t\t\t\t (Node *) extensionsResultSet);\n\n\t/* pg_available_extensions returns result set containing all available extensions */\n\t(*pg_available_extensions)(fcinfo);\n\n\tTupleTableSlot *tupleTableSlot = MakeSingleTupleTableSlot(\n\t\textensionsResultSet->setDesc,\n\t\t&TTSOpsMinimalTuple);\n\tbool hasTuple = tuplestore_gettupleslot(extensionsResultSet->setResult, goForward,\n\t\t\t\t\t\t\t\t\t\t\tdoCopy,\n\t\t\t\t\t\t\t\t\t\t\ttupleTableSlot);\n\twhile (hasTuple)\n\t{\n\t\tbool isNull = false;\n\n\t\tDatum extensionNameDatum = slot_getattr(tupleTableSlot, 1, &isNull);\n\t\tchar *extensionName = NameStr(*DatumGetName(extensionNameDatum));\n\t\tif (strcmp(extensionName, \"citus\") == 0)\n\t\t{\n\t\t\tDatum availableVersion = slot_getattr(tupleTableSlot, 2, &isNull);\n\n\t\t\t/* we will cache the result of citus version to prevent catalog access */\n\t\t\tMemoryContext oldMemoryContext = MemoryContextSwitchTo(\n\t\t\t\tMetadataCacheMemoryContext);\n\n\t\t\tavailableExtensionVersion = text_to_cstring(DatumGetTextPP(availableVersion));\n\n\t\t\tMemoryContextSwitchTo(oldMemoryContext);\n\n\t\t\tExecClearTuple(tupleTableSlot);\n\t\t\tExecDropSingleTupleTableSlot(tupleTableSlot);\n\n\t\t\treturn availableExtensionVersion;\n\t\t}\n\n\t\tExecClearTuple(tupleTableSlot);\n\t\thasTuple = tuplestore_gettupleslot(extensionsResultSet->setResult, goForward,\n\t\t\t\t\t\t\t\t\t\t   doCopy, tupleTableSlot);\n\t}\n\n\tExecDropSingleTupleTableSlot(tupleTableSlot);\n\n\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\terrmsg(\"citus extension is not found\")));\n\n\treturn NULL; /* keep compiler happy */\n}\n\n\n/*\n * InstalledExtensionVersion returns the Citus version in PostgreSQL pg_extension table.\n */\nstatic char *\nInstalledExtensionVersion(void)\n{\n\tScanKeyData entry[1];\n\tchar *installedExtensionVersion = NULL;\n\n\tInitializeCaches();\n\n\tRelation relation = table_open(ExtensionRelationId, AccessShareLock);\n\n\tScanKeyInit(&entry[0], Anum_pg_extension_extname, BTEqualStrategyNumber, F_NAMEEQ,\n\t\t\t\tCStringGetDatum(\"citus\"));\n\n\tSysScanDesc scandesc = systable_beginscan(relation, ExtensionNameIndexId, true,\n\t\t\t\t\t\t\t\t\t\t\t  NULL, 1, entry);\n\n\tHeapTuple extensionTuple = systable_getnext(scandesc);\n\n\t/* We assume that there can be at most one matching tuple */\n\tif (HeapTupleIsValid(extensionTuple))\n\t{\n\t\tint extensionIndex = Anum_pg_extension_extversion;\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\t\tbool isNull = false;\n\n\t\tDatum installedVersion = heap_getattr(extensionTuple, extensionIndex,\n\t\t\t\t\t\t\t\t\t\t\t  tupleDescriptor, &isNull);\n\n\t\tif (isNull)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"citus extension version is null\")));\n\t\t}\n\n\t\t/* we will cache the result of citus version to prevent catalog access */\n\t\tMemoryContext oldMemoryContext = MemoryContextSwitchTo(\n\t\t\tMetadataCacheMemoryContext);\n\n\t\tinstalledExtensionVersion = text_to_cstring(DatumGetTextPP(installedVersion));\n\n\t\tMemoryContextSwitchTo(oldMemoryContext);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"citus extension is not loaded\")));\n\t}\n\n\tsystable_endscan(scandesc);\n\n\ttable_close(relation, AccessShareLock);\n\n\treturn installedExtensionVersion;\n}\n\n\n/* return oid of pg_dist_shard relation */\nOid\nDistShardRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_shard\",\n\t\t\t\t\t\t &MetadataCache.distShardRelationId);\n\n\treturn MetadataCache.distShardRelationId;\n}\n\n\n/* return oid of pg_dist_placement relation */\nOid\nDistPlacementRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_placement\",\n\t\t\t\t\t\t &MetadataCache.distPlacementRelationId);\n\n\treturn MetadataCache.distPlacementRelationId;\n}\n\n\n/* return oid of pg_dist_node relation */\nOid\nDistNodeRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_node\",\n\t\t\t\t\t\t &MetadataCache.distNodeRelationId);\n\n\treturn MetadataCache.distNodeRelationId;\n}\n\n\n/* return oid of pg_dist_node's primary key index */\nOid\nDistNodeNodeIdIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_node_pkey\",\n\t\t\t\t\t\t &MetadataCache.distNodeNodeIdIndexId);\n\n\treturn MetadataCache.distNodeNodeIdIndexId;\n}\n\n\n/* return oid of pg_dist_local_group relation */\nOid\nDistLocalGroupIdRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_local_group\",\n\t\t\t\t\t\t &MetadataCache.distLocalGroupRelationId);\n\n\treturn MetadataCache.distLocalGroupRelationId;\n}\n\n\nOid\nDistBackgroundJobRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_background_job\",\n\t\t\t\t\t\t &MetadataCache.distBackgroundJobRelationId);\n\n\treturn MetadataCache.distBackgroundJobRelationId;\n}\n\n\nOid\nDistBackgroundJobPKeyIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_background_job_pkey\",\n\t\t\t\t\t\t &MetadataCache.distBackgroundJobPKeyIndexId);\n\n\treturn MetadataCache.distBackgroundJobPKeyIndexId;\n}\n\n\nOid\nDistBackgroundJobJobIdSequenceId(void)\n{\n\tCachedRelationLookup(\"pg_dist_background_job_job_id_seq\",\n\t\t\t\t\t\t &MetadataCache.distBackgroundJobJobIdSequenceId);\n\n\treturn MetadataCache.distBackgroundJobJobIdSequenceId;\n}\n\n\nOid\nDistBackgroundTaskRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_background_task\",\n\t\t\t\t\t\t &MetadataCache.distBackgroundTaskRelationId);\n\n\treturn MetadataCache.distBackgroundTaskRelationId;\n}\n\n\nOid\nDistBackgroundTaskPKeyIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_background_task_pkey\",\n\t\t\t\t\t\t &MetadataCache.distBackgroundTaskPKeyIndexId);\n\n\treturn MetadataCache.distBackgroundTaskPKeyIndexId;\n}\n\n\nOid\nDistBackgroundTaskJobIdTaskIdIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_background_task_job_id_task_id\",\n\t\t\t\t\t\t &MetadataCache.distBackgroundTaskJobIdTaskIdIndexId);\n\n\treturn MetadataCache.distBackgroundTaskJobIdTaskIdIndexId;\n}\n\n\nOid\nDistBackgroundTaskStatusTaskIdIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_background_task_status_task_id_index\",\n\t\t\t\t\t\t &MetadataCache.distBackgroundTaskStatusTaskIdIndexId);\n\n\treturn MetadataCache.distBackgroundTaskStatusTaskIdIndexId;\n}\n\n\nOid\nDistBackgroundTaskTaskIdSequenceId(void)\n{\n\tCachedRelationLookup(\"pg_dist_background_task_task_id_seq\",\n\t\t\t\t\t\t &MetadataCache.distBackgroundTaskTaskIdSequenceId);\n\n\treturn MetadataCache.distBackgroundTaskTaskIdSequenceId;\n}\n\n\nOid\nDistClockLogicalSequenceId(void)\n{\n\tCachedRelationLookup(\"pg_dist_clock_logical_seq\",\n\t\t\t\t\t\t &MetadataCache.distClockLogicalSequenceId);\n\n\treturn MetadataCache.distClockLogicalSequenceId;\n}\n\n\nOid\nDistBackgroundTaskDependRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_background_task_depend\",\n\t\t\t\t\t\t &MetadataCache.distBackgroundTaskDependRelationId);\n\n\treturn MetadataCache.distBackgroundTaskDependRelationId;\n}\n\n\nOid\nDistBackgroundTaskDependTaskIdIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_background_task_depend_task_id\",\n\t\t\t\t\t\t &MetadataCache.distBackgroundTaskDependTaskIdIndexId);\n\n\treturn MetadataCache.distBackgroundTaskDependTaskIdIndexId;\n}\n\n\nOid\nDistBackgroundTaskDependDependsOnIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_background_task_depend_depends_on\",\n\t\t\t\t\t\t &MetadataCache.distBackgroundTaskDependDependsOnIndexId);\n\n\treturn MetadataCache.distBackgroundTaskDependDependsOnIndexId;\n}\n\n\n/* return oid of pg_dist_rebalance_strategy relation */\nOid\nDistRebalanceStrategyRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_rebalance_strategy\",\n\t\t\t\t\t\t &MetadataCache.distRebalanceStrategyRelationId);\n\n\treturn MetadataCache.distRebalanceStrategyRelationId;\n}\n\n\n/* return the oid of citus namespace */\nOid\nCitusCatalogNamespaceId(void)\n{\n\tCachedNamespaceLookup(\"citus\", &MetadataCache.citusCatalogNamespaceId);\n\treturn MetadataCache.citusCatalogNamespaceId;\n}\n\n\n/* return oid of pg_dist_object relation */\nOid\nDistObjectRelationId(void)\n{\n\t/*\n\t * In older versions pg_dist_object was living in the `citus` namespace, With Citus 11\n\t * this has been moved to pg_dist_catalog.\n\t *\n\t * During upgrades it could therefore be that we simply need to look in the old\n\t * catalog. Since we expect to find it most of the time in the pg_catalog schema from\n\t * now on we will start there.\n\t *\n\t * even after the table has been moved, the oid's stay the same, so we don't have to\n\t * invalidate the cache after a move\n\t *\n\t * Note: during testing we also up/downgrade the extension, and sometimes interact\n\t * with the database when the schema and the binary are not in sync. Hance we always\n\t * allow the catalog to be missing on our first lookup. The error message might\n\t * therefore become misleading as it will complain about citus.pg_dist_object not\n\t * being found when called too early.\n\t */\n\tCachedRelationLookupExtended(\"pg_dist_object\",\n\t\t\t\t\t\t\t\t &MetadataCache.distObjectRelationId,\n\t\t\t\t\t\t\t\t true);\n\tif (!OidIsValid(MetadataCache.distObjectRelationId))\n\t{\n\t\t/*\n\t\t * We can only ever reach here while we are creating/altering our extension before\n\t\t * the table is moved to pg_catalog.\n\t\t */\n\t\tCachedRelationNamespaceLookupExtended(\"pg_dist_object\",\n\t\t\t\t\t\t\t\t\t\t\t  CitusCatalogNamespaceId(),\n\t\t\t\t\t\t\t\t\t\t\t  &MetadataCache.distObjectRelationId,\n\t\t\t\t\t\t\t\t\t\t\t  false);\n\t}\n\n\treturn MetadataCache.distObjectRelationId;\n}\n\n\n/* return oid of pg_dist_object_pkey */\nOid\nDistObjectPrimaryKeyIndexId(void)\n{\n\t/*\n\t * In older versions pg_dist_object was living in the `citus` namespace, With Citus 11\n\t * this has been moved to pg_dist_catalog.\n\t *\n\t * During upgrades it could therefore be that we simply need to look in the old\n\t * catalog. Since we expect to find it most of the time in the pg_catalog schema from\n\t * now on we will start there.\n\t *\n\t * even after the table has been moved, the oid's stay the same, so we don't have to\n\t * invalidate the cache after a move\n\t *\n\t * Note: during testing we also up/downgrade the extension, and sometimes interact\n\t * with the database when the schema and the binary are not in sync. Hance we always\n\t * allow the catalog to be missing on our first lookup. The error message might\n\t * therefore become misleading as it will complain about citus.pg_dist_object not\n\t * being found when called too early.\n\t */\n\tCachedRelationLookupExtended(\"pg_dist_object_pkey\",\n\t\t\t\t\t\t\t\t &MetadataCache.distObjectPrimaryKeyIndexId,\n\t\t\t\t\t\t\t\t true);\n\n\tif (!OidIsValid(MetadataCache.distObjectPrimaryKeyIndexId))\n\t{\n\t\t/*\n\t\t * We can only ever reach here while we are creating/altering our extension before\n\t\t * the table is moved to pg_catalog.\n\t\t */\n\t\tCachedRelationNamespaceLookupExtended(\"pg_dist_object_pkey\",\n\t\t\t\t\t\t\t\t\t\t\t  CitusCatalogNamespaceId(),\n\t\t\t\t\t\t\t\t\t\t\t  &MetadataCache.distObjectPrimaryKeyIndexId,\n\t\t\t\t\t\t\t\t\t\t\t  false);\n\t}\n\n\treturn MetadataCache.distObjectPrimaryKeyIndexId;\n}\n\n\n/* return oid of pg_dist_cleanup relation */\nOid\nDistCleanupRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_cleanup\",\n\t\t\t\t\t\t &MetadataCache.distCleanupRelationId);\n\n\treturn MetadataCache.distCleanupRelationId;\n}\n\n\n/* return oid of pg_dist_cleanup primary key index */\nOid\nDistCleanupPrimaryKeyIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_cleanup_pkey\",\n\t\t\t\t\t\t &MetadataCache.distCleanupPrimaryKeyIndexId);\n\n\treturn MetadataCache.distCleanupPrimaryKeyIndexId;\n}\n\n\n/* return oid of pg_dist_colocation relation */\nOid\nDistColocationRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_colocation\",\n\t\t\t\t\t\t &MetadataCache.distColocationRelationId);\n\n\treturn MetadataCache.distColocationRelationId;\n}\n\n\n/* return oid of pg_dist_colocation_configuration_index index */\nOid\nDistColocationConfigurationIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_colocation_configuration_index\",\n\t\t\t\t\t\t &MetadataCache.distColocationConfigurationIndexId);\n\n\treturn MetadataCache.distColocationConfigurationIndexId;\n}\n\n\n/* return oid of pg_dist_schema relation */\nOid\nDistTenantSchemaRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_schema\",\n\t\t\t\t\t\t &MetadataCache.distTenantSchemaRelationId);\n\n\treturn MetadataCache.distTenantSchemaRelationId;\n}\n\n\n/* return oid of pg_dist_schema_pkey index */\nOid\nDistTenantSchemaPrimaryKeyIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_schema_pkey\",\n\t\t\t\t\t\t &MetadataCache.distTenantSchemaPrimaryKeyIndexId);\n\n\treturn MetadataCache.distTenantSchemaPrimaryKeyIndexId;\n}\n\n\n/* return oid of pg_dist_schema_unique_colocationid_index index */\nOid\nDistTenantSchemaUniqueColocationIdIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_schema_unique_colocationid_index\",\n\t\t\t\t\t\t &MetadataCache.distTenantSchemaUniqueColocationIdIndexId);\n\n\treturn MetadataCache.distTenantSchemaUniqueColocationIdIndexId;\n}\n\n\n/* return oid of pg_dist_partition relation */\nOid\nDistPartitionRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_partition\",\n\t\t\t\t\t\t &MetadataCache.distPartitionRelationId);\n\n\treturn MetadataCache.distPartitionRelationId;\n}\n\n\n/* return oid of pg_dist_partition_logical_relid_index index */\nOid\nDistPartitionLogicalRelidIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_partition_logical_relid_index\",\n\t\t\t\t\t\t &MetadataCache.distPartitionLogicalRelidIndexId);\n\n\treturn MetadataCache.distPartitionLogicalRelidIndexId;\n}\n\n\n/* return oid of pg_dist_partition_colocationid_index index */\nOid\nDistPartitionColocationidIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_partition_colocationid_index\",\n\t\t\t\t\t\t &MetadataCache.distPartitionColocationidIndexId);\n\n\treturn MetadataCache.distPartitionColocationidIndexId;\n}\n\n\n/* return oid of pg_dist_shard_logical_relid_index index */\nOid\nDistShardLogicalRelidIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_shard_logical_relid_index\",\n\t\t\t\t\t\t &MetadataCache.distShardLogicalRelidIndexId);\n\n\treturn MetadataCache.distShardLogicalRelidIndexId;\n}\n\n\n/* return oid of pg_dist_shard_shardid_index index */\nOid\nDistShardShardidIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_shard_shardid_index\",\n\t\t\t\t\t\t &MetadataCache.distShardShardidIndexId);\n\n\treturn MetadataCache.distShardShardidIndexId;\n}\n\n\n/* return oid of pg_dist_placement_shardid_index */\nOid\nDistPlacementShardidIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_placement_shardid_index\",\n\t\t\t\t\t\t &MetadataCache.distPlacementShardidIndexId);\n\n\treturn MetadataCache.distPlacementShardidIndexId;\n}\n\n\n/* return oid of pg_dist_placement_placementid_index */\nOid\nDistPlacementPlacementidIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_placement_placementid_index\",\n\t\t\t\t\t\t &MetadataCache.distPlacementPlacementidIndexId);\n\n\treturn MetadataCache.distPlacementPlacementidIndexId;\n}\n\n\n/* return oid of pg_dist_colocation_pkey */\nOid\nDistColocationIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_colocation_pkey\",\n\t\t\t\t\t\t &MetadataCache.distColocationidIndexId);\n\n\treturn MetadataCache.distColocationidIndexId;\n}\n\n\n/* return oid of pg_dist_transaction relation */\nOid\nDistTransactionRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_transaction\",\n\t\t\t\t\t\t &MetadataCache.distTransactionRelationId);\n\n\treturn MetadataCache.distTransactionRelationId;\n}\n\n\n/* return oid of pg_dist_transaction_group_index */\nOid\nDistTransactionGroupIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_transaction_group_index\",\n\t\t\t\t\t\t &MetadataCache.distTransactionGroupIndexId);\n\n\treturn MetadataCache.distTransactionGroupIndexId;\n}\n\n\n/* return oid of pg_dist_placement_groupid_index */\nOid\nDistPlacementGroupidIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_placement_groupid_index\",\n\t\t\t\t\t\t &MetadataCache.distPlacementGroupidIndexId);\n\n\treturn MetadataCache.distPlacementGroupidIndexId;\n}\n\n\n/* return oid of pg_dist_authinfo relation */\nstatic Oid\nDistAuthinfoRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_authinfo\",\n\t\t\t\t\t\t &MetadataCache.distAuthinfoRelationId);\n\n\treturn MetadataCache.distAuthinfoRelationId;\n}\n\n\n/* return oid of pg_dist_authinfo identification index */\nstatic Oid\nDistAuthinfoIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_authinfo_identification_index\",\n\t\t\t\t\t\t &MetadataCache.distAuthinfoIndexId);\n\n\treturn MetadataCache.distAuthinfoIndexId;\n}\n\n\n/* return oid of pg_dist_poolinfo relation */\nstatic Oid\nDistPoolinfoRelationId(void)\n{\n\tCachedRelationLookup(\"pg_dist_poolinfo\",\n\t\t\t\t\t\t &MetadataCache.distPoolinfoRelationId);\n\n\treturn MetadataCache.distPoolinfoRelationId;\n}\n\n\n/* return oid of pg_dist_poolinfo primary key index */\nstatic Oid\nDistPoolinfoIndexId(void)\n{\n\tCachedRelationLookup(\"pg_dist_poolinfo_pkey\",\n\t\t\t\t\t\t &MetadataCache.distPoolinfoIndexId);\n\n\treturn MetadataCache.distPoolinfoIndexId;\n}\n\n\n/* return oid of the read_intermediate_result(text,citus_copy_format) function */\nOid\nCitusReadIntermediateResultFuncId(void)\n{\n\tif (MetadataCache.readIntermediateResultFuncId == InvalidOid)\n\t{\n\t\tList *functionNameList = list_make2(makeString(\"pg_catalog\"),\n\t\t\t\t\t\t\t\t\t\t\tmakeString(\"read_intermediate_result\"));\n\t\tOid copyFormatTypeOid = CitusCopyFormatTypeId();\n\t\tOid paramOids[2] = { TEXTOID, copyFormatTypeOid };\n\t\tbool missingOK = false;\n\n\t\tMetadataCache.readIntermediateResultFuncId =\n\t\t\tLookupFuncName(functionNameList, 2, paramOids, missingOK);\n\t}\n\n\treturn MetadataCache.readIntermediateResultFuncId;\n}\n\n\n/* return oid of the read_intermediate_results(text[],citus_copy_format) function */\nOid\nCitusReadIntermediateResultArrayFuncId(void)\n{\n\tif (MetadataCache.readIntermediateResultArrayFuncId == InvalidOid)\n\t{\n\t\tList *functionNameList = list_make2(makeString(\"pg_catalog\"),\n\t\t\t\t\t\t\t\t\t\t\tmakeString(\"read_intermediate_results\"));\n\t\tOid copyFormatTypeOid = CitusCopyFormatTypeId();\n\t\tOid paramOids[2] = { TEXTARRAYOID, copyFormatTypeOid };\n\t\tbool missingOK = false;\n\n\t\tMetadataCache.readIntermediateResultArrayFuncId =\n\t\t\tLookupFuncName(functionNameList, 2, paramOids, missingOK);\n\t}\n\n\treturn MetadataCache.readIntermediateResultArrayFuncId;\n}\n\n\n/* return oid of the citus.copy_format enum type */\nOid\nCitusCopyFormatTypeId(void)\n{\n\tif (MetadataCache.copyFormatTypeId == InvalidOid)\n\t{\n\t\tchar *typeName = \"citus_copy_format\";\n\t\tMetadataCache.copyFormatTypeId = GetSysCacheOid2(TYPENAMENSP,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t Anum_pg_enum_oid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t PointerGetDatum(typeName),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t PG_CATALOG_NAMESPACE);\n\t}\n\n\treturn MetadataCache.copyFormatTypeId;\n}\n\n\n/* return oid of the 'binary' citus_copy_format enum value */\nOid\nBinaryCopyFormatId(void)\n{\n\tif (MetadataCache.binaryCopyFormatId == InvalidOid)\n\t{\n\t\tOid copyFormatTypeId = CitusCopyFormatTypeId();\n\t\tMetadataCache.binaryCopyFormatId = LookupEnumValueId(copyFormatTypeId, \"binary\");\n\t}\n\n\treturn MetadataCache.binaryCopyFormatId;\n}\n\n\n/* return oid of the 'text' citus_copy_format enum value */\nOid\nTextCopyFormatId(void)\n{\n\tif (MetadataCache.textCopyFormatId == InvalidOid)\n\t{\n\t\tOid copyFormatTypeId = CitusCopyFormatTypeId();\n\t\tMetadataCache.textCopyFormatId = LookupEnumValueId(copyFormatTypeId, \"text\");\n\t}\n\n\treturn MetadataCache.textCopyFormatId;\n}\n\n\n/* return oid of the citus_extradata_container(internal) function */\nOid\nCitusExtraDataContainerFuncId(void)\n{\n\tList *nameList = NIL;\n\tOid paramOids[1] = { INTERNALOID };\n\n\tif (MetadataCache.extraDataContainerFuncId == InvalidOid)\n\t{\n\t\tnameList = list_make2(makeString(\"pg_catalog\"),\n\t\t\t\t\t\t\t  makeString(\"citus_extradata_container\"));\n\t\tMetadataCache.extraDataContainerFuncId =\n\t\t\tLookupFuncName(nameList, 1, paramOids, false);\n\t}\n\n\treturn MetadataCache.extraDataContainerFuncId;\n}\n\n\n/* return oid of the any_value aggregate function */\nOid\nCitusAnyValueFunctionId(void)\n{\n\tif (MetadataCache.anyValueFunctionId == InvalidOid)\n\t{\n\t\tconst int argCount = 1;\n\t\tMetadataCache.anyValueFunctionId =\n\t\t\tFunctionOid(\"pg_catalog\", \"any_value\", argCount);\n\t}\n\n\treturn MetadataCache.anyValueFunctionId;\n}\n\n\n/* return oid of the citus_text_send_as_jsonb(text) function */\nOid\nCitusTextSendAsJsonbFunctionId(void)\n{\n\tif (MetadataCache.textSendAsJsonbFunctionId == InvalidOid)\n\t{\n\t\tList *nameList = list_make2(makeString(\"pg_catalog\"),\n\t\t\t\t\t\t\t\t\tmakeString(\"citus_text_send_as_jsonb\"));\n\t\tOid paramOids[1] = { TEXTOID };\n\n\t\tMetadataCache.textSendAsJsonbFunctionId =\n\t\t\tLookupFuncName(nameList, 1, paramOids, false);\n\t}\n\n\treturn MetadataCache.textSendAsJsonbFunctionId;\n}\n\n\n/* return oid of the textout(text) function */\nOid\nTextOutFunctionId(void)\n{\n\tif (MetadataCache.textoutFunctionId == InvalidOid)\n\t{\n\t\tList *nameList = list_make2(makeString(\"pg_catalog\"),\n\t\t\t\t\t\t\t\t\tmakeString(\"textout\"));\n\t\tOid paramOids[1] = { TEXTOID };\n\n\t\tMetadataCache.textoutFunctionId =\n\t\t\tLookupFuncName(nameList, 1, paramOids, false);\n\t}\n\n\treturn MetadataCache.textoutFunctionId;\n}\n\n\n/*\n * RelationIsAKnownShardFuncId returns oid of the relation_is_a_known_shard function.\n */\nOid\nRelationIsAKnownShardFuncId(void)\n{\n\tif (MetadataCache.relationIsAKnownShardFuncId == InvalidOid)\n\t{\n\t\tconst int argCount = 1;\n\n\t\tMetadataCache.relationIsAKnownShardFuncId =\n\t\t\tFunctionOid(\"pg_catalog\", \"relation_is_a_known_shard\", argCount);\n\t}\n\n\treturn MetadataCache.relationIsAKnownShardFuncId;\n}\n\n\n/*\n * JsonbExtractPathFuncId returns oid of the jsonb_extract_path function.\n */\nOid\nJsonbExtractPathFuncId(void)\n{\n\tif (MetadataCache.jsonbExtractPathFuncId == InvalidOid)\n\t{\n\t\tconst int argCount = 2;\n\n\t\tMetadataCache.jsonbExtractPathFuncId =\n\t\t\tFunctionOid(\"pg_catalog\", \"jsonb_extract_path\", argCount);\n\t}\n\n\treturn MetadataCache.jsonbExtractPathFuncId;\n}\n\n\n/*\n * JsonbExtractPathTextFuncId returns oid of the jsonb_extract_path_text function.\n */\nOid\nJsonbExtractPathTextFuncId(void)\n{\n\tif (MetadataCache.jsonbExtractPathTextFuncId == InvalidOid)\n\t{\n\t\tconst int argCount = 2;\n\n\t\tMetadataCache.jsonbExtractPathTextFuncId =\n\t\t\tFunctionOid(\"pg_catalog\", \"jsonb_extract_path_text\", argCount);\n\t}\n\n\treturn MetadataCache.jsonbExtractPathTextFuncId;\n}\n\n\n/*\n * CitusDependentObjectFuncId returns oid of the is_citus_depended_object function.\n */\nOid\nCitusDependentObjectFuncId(void)\n{\n\tif (!HideCitusDependentObjects)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"is_citus_depended_object can only be used while running the regression tests\")));\n\t}\n\n\tif (MetadataCache.CitusDependentObjectFuncId == InvalidOid)\n\t{\n\t\tconst int argCount = 2;\n\n\t\tMetadataCache.CitusDependentObjectFuncId =\n\t\t\tFunctionOid(\"pg_catalog\", \"is_citus_depended_object\", argCount);\n\t}\n\n\treturn MetadataCache.CitusDependentObjectFuncId;\n}\n\n\n/*\n * CurrentDatabaseName gets the name of the current database and caches\n * the result.\n *\n * Given that the database name cannot be changed when there is at least\n * one session connected to it, we do not need to implement any invalidation\n * mechanism.\n */\nconst char *\nCurrentDatabaseName(void)\n{\n\tif (!MetadataCache.databaseNameValid)\n\t{\n\t\tchar *databaseName = get_database_name(MyDatabaseId);\n\t\tif (databaseName == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"database that is connected to does not exist\")));\n\t\t}\n\n\t\tstrlcpy(MetadataCache.databaseName, databaseName, NAMEDATALEN);\n\t\tMetadataCache.databaseNameValid = true;\n\t}\n\n\treturn MetadataCache.databaseName;\n}\n\n\n/*\n * CitusExtensionOwner() returns the owner of the 'citus' extension. That user\n * is, amongst others, used to perform actions a normal user might not be\n * allowed to perform.\n */\nextern Oid\nCitusExtensionOwner(void)\n{\n\tScanKeyData entry[1];\n\tForm_pg_extension extensionForm = NULL;\n\n\tif (MetadataCache.extensionOwner != InvalidOid)\n\t{\n\t\treturn MetadataCache.extensionOwner;\n\t}\n\n\tRelation relation = table_open(ExtensionRelationId, AccessShareLock);\n\n\tScanKeyInit(&entry[0],\n\t\t\t\tAnum_pg_extension_extname,\n\t\t\t\tBTEqualStrategyNumber, F_NAMEEQ,\n\t\t\t\tCStringGetDatum(\"citus\"));\n\n\tSysScanDesc scandesc = systable_beginscan(relation, ExtensionNameIndexId, true,\n\t\t\t\t\t\t\t\t\t\t\t  NULL, 1, entry);\n\n\tHeapTuple extensionTuple = systable_getnext(scandesc);\n\n\t/* We assume that there can be at most one matching tuple */\n\tif (HeapTupleIsValid(extensionTuple))\n\t{\n\t\textensionForm = (Form_pg_extension) GETSTRUCT(extensionTuple);\n\n\t\t/*\n\t\t * For some operations Citus requires superuser permissions; we use\n\t\t * the extension owner for that. The extension owner is guaranteed to\n\t\t * be a superuser (otherwise C functions can't be created), but it'd\n\t\t * be possible to change the owner. So check that this still a\n\t\t * superuser.\n\t\t */\n\t\tif (!superuser_arg(extensionForm->extowner))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"citus extension needs to be owned by superuser\")));\n\t\t}\n\t\tMetadataCache.extensionOwner = extensionForm->extowner;\n\t\tAssert(OidIsValid(MetadataCache.extensionOwner));\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"citus extension not loaded\")));\n\t}\n\n\tsystable_endscan(scandesc);\n\n\ttable_close(relation, AccessShareLock);\n\n\treturn MetadataCache.extensionOwner;\n}\n\n\n/*\n * CitusExtensionOwnerName returns the name of the owner of the extension.\n */\nchar *\nCitusExtensionOwnerName(void)\n{\n\tOid superUserId = CitusExtensionOwner();\n\n\treturn GetUserNameFromId(superUserId, false);\n}\n\n\n/* return the username of the currently active role */\nchar *\nCurrentUserName(void)\n{\n\tOid userId = GetUserId();\n\n\treturn GetUserNameFromId(userId, false);\n}\n\n\n/*\n * LookupTypeOid returns the Oid of the \"{schemaNameSting}.{typeNameString}\" type, or\n * InvalidOid if it does not exist.\n */\nOid\nLookupTypeOid(char *schemaNameSting, char *typeNameString)\n{\n\tString *schemaName = makeString(schemaNameSting);\n\tString *typeName = makeString(typeNameString);\n\tList *qualifiedName = list_make2(schemaName, typeName);\n\tTypeName *enumTypeName = makeTypeNameFromNameList(qualifiedName);\n\n\n\t/* typenameTypeId but instead of raising an error return InvalidOid */\n\tType tup = LookupTypeName(NULL, enumTypeName, NULL, false);\n\tif (tup == NULL)\n\t{\n\t\treturn InvalidOid;\n\t}\n\n\tOid nodeRoleTypId = ((Form_pg_type) GETSTRUCT(tup))->oid;\n\tReleaseSysCache(tup);\n\n\treturn nodeRoleTypId;\n}\n\n\n/*\n * LookupStringEnumValueId returns the Oid of the value in \"pg_catalog.{enumName}\"\n * which matches the provided valueName, or InvalidOid if the enum doesn't exist yet.\n */\nstatic Oid\nLookupStringEnumValueId(char *enumName, char *valueName)\n{\n\tOid enumTypeId = LookupTypeOid(\"pg_catalog\", enumName);\n\n\tif (enumTypeId == InvalidOid)\n\t{\n\t\treturn InvalidOid;\n\t}\n\telse\n\t{\n\t\tOid valueId = LookupEnumValueId(enumTypeId, valueName);\n\t\treturn valueId;\n\t}\n}\n\n\n/*\n * LookupEnumValueId looks up the OID of an enum value.\n */\nstatic Oid\nLookupEnumValueId(Oid typeId, char *valueName)\n{\n\tDatum typeIdDatum = ObjectIdGetDatum(typeId);\n\tDatum valueDatum = CStringGetDatum(valueName);\n\tDatum valueIdDatum = DirectFunctionCall2(enum_in, valueDatum, typeIdDatum);\n\tOid valueId = DatumGetObjectId(valueIdDatum);\n\n\treturn valueId;\n}\n\n\n/* return the Oid of the 'primary' nodeRole enum value */\nOid\nPrimaryNodeRoleId(void)\n{\n\tif (!MetadataCache.primaryNodeRoleId)\n\t{\n\t\tMetadataCache.primaryNodeRoleId = LookupStringEnumValueId(\"noderole\", \"primary\");\n\t}\n\n\treturn MetadataCache.primaryNodeRoleId;\n}\n\n\n/* return the Oid of the 'secodary' nodeRole enum value */\nOid\nSecondaryNodeRoleId(void)\n{\n\tif (!MetadataCache.secondaryNodeRoleId)\n\t{\n\t\tMetadataCache.secondaryNodeRoleId = LookupStringEnumValueId(\"noderole\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"secondary\");\n\t}\n\n\treturn MetadataCache.secondaryNodeRoleId;\n}\n\n\n/* return the Oid of the 'unavailable' nodeRole enum value */\nOid\nUnavailableNodeRoleId(void)\n{\n\tif (!MetadataCache.unavailableNodeRoleId)\n\t{\n\t\tMetadataCache.unavailableNodeRoleId = LookupStringEnumValueId(\"noderole\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"unavailable\");\n\t}\n\n\treturn MetadataCache.unavailableNodeRoleId;\n}\n\n\nOid\nCitusJobStatusScheduledId(void)\n{\n\tif (!MetadataCache.citusJobStatusScheduledId)\n\t{\n\t\tMetadataCache.citusJobStatusScheduledId =\n\t\t\tLookupStringEnumValueId(\"citus_job_status\", \"scheduled\");\n\t}\n\n\treturn MetadataCache.citusJobStatusScheduledId;\n}\n\n\nOid\nCitusJobStatusRunningId(void)\n{\n\tif (!MetadataCache.citusJobStatusRunningId)\n\t{\n\t\tMetadataCache.citusJobStatusRunningId =\n\t\t\tLookupStringEnumValueId(\"citus_job_status\", \"running\");\n\t}\n\n\treturn MetadataCache.citusJobStatusRunningId;\n}\n\n\nOid\nCitusJobStatusCancellingId(void)\n{\n\tif (!MetadataCache.citusJobStatusCancellingId)\n\t{\n\t\tMetadataCache.citusJobStatusCancellingId =\n\t\t\tLookupStringEnumValueId(\"citus_job_status\", \"cancelling\");\n\t}\n\n\treturn MetadataCache.citusJobStatusCancellingId;\n}\n\n\nOid\nCitusJobStatusFinishedId(void)\n{\n\tif (!MetadataCache.citusJobStatusFinishedId)\n\t{\n\t\tMetadataCache.citusJobStatusFinishedId =\n\t\t\tLookupStringEnumValueId(\"citus_job_status\", \"finished\");\n\t}\n\n\treturn MetadataCache.citusJobStatusFinishedId;\n}\n\n\nOid\nCitusJobStatusCancelledId(void)\n{\n\tif (!MetadataCache.citusJobStatusCancelledId)\n\t{\n\t\tMetadataCache.citusJobStatusCancelledId =\n\t\t\tLookupStringEnumValueId(\"citus_job_status\", \"cancelled\");\n\t}\n\n\treturn MetadataCache.citusJobStatusCancelledId;\n}\n\n\nOid\nCitusJobStatusFailedId(void)\n{\n\tif (!MetadataCache.citusJobStatusFailedId)\n\t{\n\t\tMetadataCache.citusJobStatusFailedId =\n\t\t\tLookupStringEnumValueId(\"citus_job_status\", \"failed\");\n\t}\n\n\treturn MetadataCache.citusJobStatusFailedId;\n}\n\n\nOid\nCitusJobStatusFailingId(void)\n{\n\tif (!MetadataCache.citusJobStatusFailingId)\n\t{\n\t\tMetadataCache.citusJobStatusFailingId =\n\t\t\tLookupStringEnumValueId(\"citus_job_status\", \"failing\");\n\t}\n\n\treturn MetadataCache.citusJobStatusFailingId;\n}\n\n\nOid\nCitusTaskStatusBlockedId(void)\n{\n\tif (!MetadataCache.citusTaskStatusBlockedId)\n\t{\n\t\tMetadataCache.citusTaskStatusBlockedId =\n\t\t\tLookupStringEnumValueId(\"citus_task_status\", \"blocked\");\n\t}\n\n\treturn MetadataCache.citusTaskStatusBlockedId;\n}\n\n\nOid\nCitusTaskStatusCancelledId(void)\n{\n\tif (!MetadataCache.citusTaskStatusCancelledId)\n\t{\n\t\tMetadataCache.citusTaskStatusCancelledId =\n\t\t\tLookupStringEnumValueId(\"citus_task_status\", \"cancelled\");\n\t}\n\n\treturn MetadataCache.citusTaskStatusCancelledId;\n}\n\n\nOid\nCitusTaskStatusCancellingId(void)\n{\n\tif (!MetadataCache.citusTaskStatusCancellingId)\n\t{\n\t\tMetadataCache.citusTaskStatusCancellingId =\n\t\t\tLookupStringEnumValueId(\"citus_task_status\", \"cancelling\");\n\t}\n\n\treturn MetadataCache.citusTaskStatusCancellingId;\n}\n\n\nOid\nCitusTaskStatusRunnableId(void)\n{\n\tif (!MetadataCache.citusTaskStatusRunnableId)\n\t{\n\t\tMetadataCache.citusTaskStatusRunnableId =\n\t\t\tLookupStringEnumValueId(\"citus_task_status\", \"runnable\");\n\t}\n\n\treturn MetadataCache.citusTaskStatusRunnableId;\n}\n\n\nOid\nCitusTaskStatusRunningId(void)\n{\n\tif (!MetadataCache.citusTaskStatusRunningId)\n\t{\n\t\tMetadataCache.citusTaskStatusRunningId =\n\t\t\tLookupStringEnumValueId(\"citus_task_status\", \"running\");\n\t}\n\n\treturn MetadataCache.citusTaskStatusRunningId;\n}\n\n\nOid\nCitusTaskStatusDoneId(void)\n{\n\tif (!MetadataCache.citusTaskStatusDoneId)\n\t{\n\t\tMetadataCache.citusTaskStatusDoneId =\n\t\t\tLookupStringEnumValueId(\"citus_task_status\", \"done\");\n\t}\n\n\treturn MetadataCache.citusTaskStatusDoneId;\n}\n\n\nOid\nCitusTaskStatusErrorId(void)\n{\n\tif (!MetadataCache.citusTaskStatusErrorId)\n\t{\n\t\tMetadataCache.citusTaskStatusErrorId =\n\t\t\tLookupStringEnumValueId(\"citus_task_status\", \"error\");\n\t}\n\n\treturn MetadataCache.citusTaskStatusErrorId;\n}\n\n\nOid\nCitusTaskStatusUnscheduledId(void)\n{\n\tif (!MetadataCache.citusTaskStatusUnscheduledId)\n\t{\n\t\tMetadataCache.citusTaskStatusUnscheduledId =\n\t\t\tLookupStringEnumValueId(\"citus_task_status\", \"unscheduled\");\n\t}\n\n\treturn MetadataCache.citusTaskStatusUnscheduledId;\n}\n\n\n/*\n * citus_dist_partition_cache_invalidate is a trigger function that performs\n * relcache invalidations when the contents of pg_dist_partition are changed\n * on the SQL level.\n *\n * NB: We decided there is little point in checking permissions here, there\n * are much easier ways to waste CPU than causing cache invalidations.\n */\nDatum\ncitus_dist_partition_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tTriggerData *triggerData = (TriggerData *) fcinfo->context;\n\tOid oldLogicalRelationId = InvalidOid;\n\tOid newLogicalRelationId = InvalidOid;\n\n\tif (!CALLED_AS_TRIGGER(fcinfo))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),\n\t\t\t\t\t\terrmsg(\"must be called as trigger\")));\n\t}\n\n\tif (RelationGetRelid(triggerData->tg_relation) != DistPartitionRelationId())\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),\n\t\t\t\t\t\terrmsg(\"triggered on incorrect relation\")));\n\t}\n\n\tHeapTuple newTuple = triggerData->tg_newtuple;\n\tHeapTuple oldTuple = triggerData->tg_trigtuple;\n\n\t/* collect logicalrelid for OLD and NEW tuple */\n\tif (oldTuple != NULL)\n\t{\n\t\tForm_pg_dist_partition distPart = (Form_pg_dist_partition) GETSTRUCT(oldTuple);\n\n\t\toldLogicalRelationId = distPart->logicalrelid;\n\t}\n\n\tif (newTuple != NULL)\n\t{\n\t\tForm_pg_dist_partition distPart = (Form_pg_dist_partition) GETSTRUCT(newTuple);\n\n\t\tnewLogicalRelationId = distPart->logicalrelid;\n\t}\n\n\t/*\n\t * Invalidate relcache for the relevant relation(s). In theory\n\t * logicalrelid should never change, but it doesn't hurt to be\n\t * paranoid.\n\t */\n\tif (oldLogicalRelationId != InvalidOid &&\n\t\toldLogicalRelationId != newLogicalRelationId)\n\t{\n\t\tCitusInvalidateRelcacheByRelid(oldLogicalRelationId);\n\t}\n\n\tif (newLogicalRelationId != InvalidOid)\n\t{\n\t\tCitusInvalidateRelcacheByRelid(newLogicalRelationId);\n\t}\n\n\tPG_RETURN_DATUM(PointerGetDatum(NULL));\n}\n\n\n/*\n * master_dist_partition_cache_invalidate is a wrapper function for old UDF name.\n */\nDatum\nmaster_dist_partition_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\treturn citus_dist_partition_cache_invalidate(fcinfo);\n}\n\n\n/*\n * citus_dist_shard_cache_invalidate is a trigger function that performs\n * relcache invalidations when the contents of pg_dist_shard are changed\n * on the SQL level.\n *\n * NB: We decided there is little point in checking permissions here, there\n * are much easier ways to waste CPU than causing cache invalidations.\n */\nDatum\ncitus_dist_shard_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tTriggerData *triggerData = (TriggerData *) fcinfo->context;\n\tOid oldLogicalRelationId = InvalidOid;\n\tOid newLogicalRelationId = InvalidOid;\n\n\tif (!CALLED_AS_TRIGGER(fcinfo))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),\n\t\t\t\t\t\terrmsg(\"must be called as trigger\")));\n\t}\n\n\tif (RelationGetRelid(triggerData->tg_relation) != DistShardRelationId())\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),\n\t\t\t\t\t\terrmsg(\"triggered on incorrect relation\")));\n\t}\n\n\tHeapTuple newTuple = triggerData->tg_newtuple;\n\tHeapTuple oldTuple = triggerData->tg_trigtuple;\n\n\t/* collect logicalrelid for OLD and NEW tuple */\n\tif (oldTuple != NULL)\n\t{\n\t\tForm_pg_dist_shard distShard = (Form_pg_dist_shard) GETSTRUCT(oldTuple);\n\n\t\toldLogicalRelationId = distShard->logicalrelid;\n\t}\n\n\tif (newTuple != NULL)\n\t{\n\t\tForm_pg_dist_shard distShard = (Form_pg_dist_shard) GETSTRUCT(newTuple);\n\n\t\tnewLogicalRelationId = distShard->logicalrelid;\n\t}\n\n\t/*\n\t * Invalidate relcache for the relevant relation(s). In theory\n\t * logicalrelid should never change, but it doesn't hurt to be\n\t * paranoid.\n\t */\n\tif (oldLogicalRelationId != InvalidOid &&\n\t\toldLogicalRelationId != newLogicalRelationId)\n\t{\n\t\tCitusInvalidateRelcacheByRelid(oldLogicalRelationId);\n\t}\n\n\tif (newLogicalRelationId != InvalidOid)\n\t{\n\t\tCitusInvalidateRelcacheByRelid(newLogicalRelationId);\n\t}\n\n\tPG_RETURN_DATUM(PointerGetDatum(NULL));\n}\n\n\n/*\n * master_dist_shard_cache_invalidate is a wrapper function for old UDF name.\n */\nDatum\nmaster_dist_shard_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\treturn citus_dist_shard_cache_invalidate(fcinfo);\n}\n\n\n/*\n * citus_dist_placement_cache_invalidate is a trigger function that performs\n * relcache invalidations when the contents of pg_dist_placement are\n * changed on the SQL level.\n *\n * NB: We decided there is little point in checking permissions here, there\n * are much easier ways to waste CPU than causing cache invalidations.\n */\nDatum\ncitus_dist_placement_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tTriggerData *triggerData = (TriggerData *) fcinfo->context;\n\tOid oldShardId = InvalidOid;\n\tOid newShardId = InvalidOid;\n\n\tif (!CALLED_AS_TRIGGER(fcinfo))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),\n\t\t\t\t\t\terrmsg(\"must be called as trigger\")));\n\t}\n\n\t/*\n\t * Before 7.0-2 this trigger is on pg_dist_shard_placement,\n\t * ignore trigger in this scenario.\n\t */\n\tOid pgDistShardPlacementId = get_relname_relid(\"pg_dist_shard_placement\",\n\t\t\t\t\t\t\t\t\t\t\t\t   PG_CATALOG_NAMESPACE);\n\tif (RelationGetRelid(triggerData->tg_relation) == pgDistShardPlacementId)\n\t{\n\t\tPG_RETURN_DATUM(PointerGetDatum(NULL));\n\t}\n\n\tif (RelationGetRelid(triggerData->tg_relation) != DistPlacementRelationId())\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),\n\t\t\t\t\t\terrmsg(\"triggered on incorrect relation\")));\n\t}\n\n\tHeapTuple newTuple = triggerData->tg_newtuple;\n\tHeapTuple oldTuple = triggerData->tg_trigtuple;\n\n\t/* collect shardid for OLD and NEW tuple */\n\tif (oldTuple != NULL)\n\t{\n\t\tForm_pg_dist_placement distPlacement =\n\t\t\t(Form_pg_dist_placement) GETSTRUCT(oldTuple);\n\n\t\toldShardId = distPlacement->shardid;\n\t}\n\n\tif (newTuple != NULL)\n\t{\n\t\tForm_pg_dist_placement distPlacement =\n\t\t\t(Form_pg_dist_placement) GETSTRUCT(newTuple);\n\n\t\tnewShardId = distPlacement->shardid;\n\t}\n\n\t/*\n\t * Invalidate relcache for the relevant relation(s). In theory shardId\n\t * should never change, but it doesn't hurt to be paranoid.\n\t */\n\tif (oldShardId != InvalidOid &&\n\t\toldShardId != newShardId)\n\t{\n\t\tCitusInvalidateRelcacheByShardId(oldShardId);\n\t}\n\n\tif (newShardId != InvalidOid)\n\t{\n\t\tCitusInvalidateRelcacheByShardId(newShardId);\n\t}\n\n\tPG_RETURN_DATUM(PointerGetDatum(NULL));\n}\n\n\n/*\n * master_dist_placement_cache_invalidate is a wrapper function for old UDF name.\n */\nDatum\nmaster_dist_placement_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\treturn citus_dist_placement_cache_invalidate(fcinfo);\n}\n\n\n/*\n * citus_dist_node_cache_invalidate is a trigger function that performs\n * relcache invalidations when the contents of pg_dist_node are changed\n * on the SQL level.\n *\n * NB: We decided there is little point in checking permissions here, there\n * are much easier ways to waste CPU than causing cache invalidations.\n */\nDatum\ncitus_dist_node_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tif (!CALLED_AS_TRIGGER(fcinfo))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),\n\t\t\t\t\t\terrmsg(\"must be called as trigger\")));\n\t}\n\n\tCitusInvalidateRelcacheByRelid(DistNodeRelationId());\n\n\tPG_RETURN_DATUM(PointerGetDatum(NULL));\n}\n\n\n/*\n * master_dist_node_cache_invalidate is a wrapper function for old UDF name.\n */\nDatum\nmaster_dist_node_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\treturn citus_dist_node_cache_invalidate(fcinfo);\n}\n\n\n/*\n * citus_conninfo_cache_invalidate is a trigger function that performs\n * relcache invalidations when the contents of pg_dist_authinfo are changed\n * on the SQL level.\n *\n * NB: We decided there is little point in checking permissions here, there\n * are much easier ways to waste CPU than causing cache invalidations.\n */\nDatum\ncitus_conninfo_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tif (!CALLED_AS_TRIGGER(fcinfo))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),\n\t\t\t\t\t\terrmsg(\"must be called as trigger\")));\n\t}\n\n\tCitusInvalidateRelcacheByRelid(DistAuthinfoRelationId());\n\n\tPG_RETURN_DATUM(PointerGetDatum(NULL));\n}\n\n\n/*\n * master_dist_authinfo_cache_invalidate is a wrapper function for old UDF name.\n */\nDatum\nmaster_dist_authinfo_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\treturn citus_conninfo_cache_invalidate(fcinfo);\n}\n\n\n/*\n * citus_dist_local_group_cache_invalidate is a trigger function that performs\n * relcache invalidations when the contents of pg_dist_local_group are changed\n * on the SQL level.\n *\n * NB: We decided there is little point in checking permissions here, there\n * are much easier ways to waste CPU than causing cache invalidations.\n */\nDatum\ncitus_dist_local_group_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tif (!CALLED_AS_TRIGGER(fcinfo))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),\n\t\t\t\t\t\terrmsg(\"must be called as trigger\")));\n\t}\n\n\tCitusInvalidateRelcacheByRelid(DistLocalGroupIdRelationId());\n\n\tPG_RETURN_DATUM(PointerGetDatum(NULL));\n}\n\n\n/*\n * master_dist_local_group_cache_invalidate is a wrapper function for old UDF name.\n */\nDatum\nmaster_dist_local_group_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\treturn citus_dist_local_group_cache_invalidate(fcinfo);\n}\n\n\n/*\n * citus_dist_object_cache_invalidate is a trigger function that performs relcache\n * invalidation when the contents of pg_dist_object are changed on the SQL\n * level.\n *\n * NB: We decided there is little point in checking permissions here, there\n * are much easier ways to waste CPU than causing cache invalidations.\n */\nDatum\ncitus_dist_object_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tif (!CALLED_AS_TRIGGER(fcinfo))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),\n\t\t\t\t\t\terrmsg(\"must be called as trigger\")));\n\t}\n\n\tCitusInvalidateRelcacheByRelid(DistObjectRelationId());\n\n\tPG_RETURN_DATUM(PointerGetDatum(NULL));\n}\n\n\n/*\n * master_dist_object_cache_invalidate is a wrapper function for old UDF name.\n */\nDatum\nmaster_dist_object_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\treturn citus_dist_object_cache_invalidate(fcinfo);\n}\n\n\n/*\n * InitializeCaches() registers invalidation handlers for metadata_cache.c's\n * caches.\n */\nstatic void\nInitializeCaches(void)\n{\n\tstatic bool performedInitialization = false;\n\n\tif (!performedInitialization)\n\t{\n\t\tMetadataCacheMemoryContext = NULL;\n\n\t\t/*\n\t\t * If either of dist table cache or shard cache\n\t\t * allocation and initializations fail due to an exception\n\t\t * that is caused by OOM or any other reason,\n\t\t * we reset the flag, and delete the shard cache memory\n\t\t * context to reclaim partially allocated memory.\n\t\t *\n\t\t * Command will continue to fail since we re-throw the exception.\n\t\t */\n\t\tPG_TRY();\n\t\t{\n\t\t\t/* set first, to avoid recursion dangers */\n\t\t\tperformedInitialization = true;\n\n\t\t\t/* make sure we've initialized CacheMemoryContext */\n\t\t\tif (CacheMemoryContext == NULL)\n\t\t\t{\n\t\t\t\tCreateCacheMemoryContext();\n\t\t\t}\n\n\t\t\tMetadataCacheMemoryContext = AllocSetContextCreate(\n\t\t\t\tCacheMemoryContext,\n\t\t\t\t\"MetadataCacheMemoryContext\",\n\t\t\t\tALLOCSET_DEFAULT_SIZES);\n\n\t\t\tInitializeDistCache();\n\t\t\tRegisterForeignKeyGraphCacheCallbacks();\n\t\t\tRegisterWorkerNodeCacheCallbacks();\n\t\t\tRegisterLocalGroupIdCacheCallbacks();\n\t\t\tRegisterAuthinfoCacheCallbacks();\n\t\t\tRegisterCitusTableCacheEntryReleaseCallbacks();\n\t\t}\n\t\tPG_CATCH();\n\t\t{\n\t\t\tperformedInitialization = false;\n\n\t\t\tif (MetadataCacheMemoryContext != NULL)\n\t\t\t{\n\t\t\t\tMemoryContextDelete(MetadataCacheMemoryContext);\n\t\t\t}\n\n\t\t\tMetadataCacheMemoryContext = NULL;\n\t\t\tDistTableCacheHash = NULL;\n\t\t\tDistTableCacheExpired = NIL;\n\t\t\tShardIdCacheHash = NULL;\n\n\t\t\tPG_RE_THROW();\n\t\t}\n\t\tPG_END_TRY();\n\t}\n}\n\n\n/* initialize the infrastructure for the metadata cache */\nstatic void\nInitializeDistCache(void)\n{\n\t/* build initial scan keys, copied for every relation scan */\n\tmemset(&DistPartitionScanKey, 0, sizeof(DistPartitionScanKey));\n\n\tfmgr_info_cxt(F_OIDEQ,\n\t\t\t\t  &DistPartitionScanKey[0].sk_func,\n\t\t\t\t  MetadataCacheMemoryContext);\n\tDistPartitionScanKey[0].sk_strategy = BTEqualStrategyNumber;\n\tDistPartitionScanKey[0].sk_subtype = InvalidOid;\n\tDistPartitionScanKey[0].sk_collation = InvalidOid;\n\tDistPartitionScanKey[0].sk_attno = Anum_pg_dist_partition_logicalrelid;\n\n\tmemset(&DistShardScanKey, 0, sizeof(DistShardScanKey));\n\n\tfmgr_info_cxt(F_OIDEQ,\n\t\t\t\t  &DistShardScanKey[0].sk_func,\n\t\t\t\t  MetadataCacheMemoryContext);\n\tDistShardScanKey[0].sk_strategy = BTEqualStrategyNumber;\n\tDistShardScanKey[0].sk_subtype = InvalidOid;\n\tDistShardScanKey[0].sk_collation = InvalidOid;\n\tDistShardScanKey[0].sk_attno = Anum_pg_dist_shard_logicalrelid;\n\n\tCreateDistTableCache();\n\tCreateShardIdCache();\n\n\tInitializeDistObjectCache();\n}\n\n\nstatic void\nInitializeDistObjectCache(void)\n{\n\t/* build initial scan keys, copied for every relation scan */\n\tmemset(&DistObjectScanKey, 0, sizeof(DistObjectScanKey));\n\n\tfmgr_info_cxt(F_OIDEQ,\n\t\t\t\t  &DistObjectScanKey[0].sk_func,\n\t\t\t\t  MetadataCacheMemoryContext);\n\tDistObjectScanKey[0].sk_strategy = BTEqualStrategyNumber;\n\tDistObjectScanKey[0].sk_subtype = InvalidOid;\n\tDistObjectScanKey[0].sk_collation = InvalidOid;\n\tDistObjectScanKey[0].sk_attno = Anum_pg_dist_object_classid;\n\n\tfmgr_info_cxt(F_OIDEQ,\n\t\t\t\t  &DistObjectScanKey[1].sk_func,\n\t\t\t\t  MetadataCacheMemoryContext);\n\tDistObjectScanKey[1].sk_strategy = BTEqualStrategyNumber;\n\tDistObjectScanKey[1].sk_subtype = InvalidOid;\n\tDistObjectScanKey[1].sk_collation = InvalidOid;\n\tDistObjectScanKey[1].sk_attno = Anum_pg_dist_object_objid;\n\n\tfmgr_info_cxt(F_INT4EQ,\n\t\t\t\t  &DistObjectScanKey[2].sk_func,\n\t\t\t\t  MetadataCacheMemoryContext);\n\tDistObjectScanKey[2].sk_strategy = BTEqualStrategyNumber;\n\tDistObjectScanKey[2].sk_subtype = InvalidOid;\n\tDistObjectScanKey[2].sk_collation = InvalidOid;\n\tDistObjectScanKey[2].sk_attno = Anum_pg_dist_object_objsubid;\n\n\tCreateDistObjectCache();\n}\n\n\n/*\n * GetWorkerNodeHash returns the worker node data as a hash with the nodename and\n * nodeport as a key.\n *\n * The hash is returned from the cache, if the cache is not (yet) valid, it is first\n * rebuilt.\n */\nHTAB *\nGetWorkerNodeHash(void)\n{\n\tPrepareWorkerNodeCache();\n\n\treturn WorkerNodeHash;\n}\n\n\n/*\n * PrepareWorkerNodeCache makes sure the worker node data from pg_dist_node is cached,\n * if it is not already cached.\n */\nstatic void\nPrepareWorkerNodeCache(void)\n{\n\tInitializeCaches(); /* ensure relevant callbacks are registered */\n\n\t/*\n\t * Simulate a SELECT from pg_dist_node, ensure pg_dist_node doesn't change while our\n\t * caller is using WorkerNodeHash.\n\t */\n\tLockRelationOid(DistNodeRelationId(), AccessShareLock);\n\n\t/*\n\t * We might have some concurrent metadata changes. In order to get the changes,\n\t * we first need to accept the cache invalidation messages.\n\t */\n\tAcceptInvalidationMessages();\n\n\tif (!workerNodeHashValid)\n\t{\n\t\tInitializeWorkerNodeCache();\n\n\t\tworkerNodeHashValid = true;\n\t}\n}\n\n\n/*\n * InitializeWorkerNodeCache initialize the infrastructure for the worker node cache.\n * The function reads the worker nodes from the metadata table, adds them to the hash and\n * finally registers an invalidation callback.\n */\nstatic void\nInitializeWorkerNodeCache(void)\n{\n\tHASHCTL info;\n\tlong maxTableSize = (long) MaxWorkerNodesTracked;\n\tbool includeNodesFromOtherClusters = false;\n\tint workerNodeIndex = 0;\n\n\tInitializeCaches();\n\n\t/*\n\t * Create the hash that holds the worker nodes. The key is the combination of\n\t * nodename and nodeport, instead of the unique nodeid because worker nodes are\n\t * searched by the nodename and nodeport in every physical plan creation.\n\t */\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(uint32) + WORKER_LENGTH + sizeof(uint32);\n\tinfo.entrysize = sizeof(WorkerNode);\n\tinfo.hcxt = MetadataCacheMemoryContext;\n\tinfo.hash = WorkerNodeHashCode;\n\tinfo.match = WorkerNodeCompare;\n\tint hashFlags = HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT | HASH_COMPARE;\n\n\tHTAB *newWorkerNodeHash = hash_create(\"Worker Node Hash\", maxTableSize, &info,\n\t\t\t\t\t\t\t\t\t\t  hashFlags);\n\n\t/* read the list from pg_dist_node */\n\tList *workerNodeList = ReadDistNode(includeNodesFromOtherClusters);\n\n\tint newWorkerNodeCount = list_length(workerNodeList);\n\tWorkerNode **newWorkerNodeArray = MemoryContextAlloc(MetadataCacheMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t sizeof(WorkerNode *) *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t newWorkerNodeCount);\n\n\t/* iterate over the worker node list */\n\tWorkerNode *currentNode = NULL;\n\tforeach_declared_ptr(currentNode, workerNodeList)\n\t{\n\t\tbool handleFound = false;\n\n\t\t/* search for the worker node in the hash, and then insert the values */\n\t\tvoid *hashKey = (void *) currentNode;\n\t\tWorkerNode *workerNode = (WorkerNode *) hash_search(newWorkerNodeHash, hashKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tHASH_ENTER, &handleFound);\n\n\t\t/* fill the newly allocated workerNode in the cache */\n\t\tstrlcpy(workerNode->workerName, currentNode->workerName, WORKER_LENGTH);\n\t\tworkerNode->workerPort = currentNode->workerPort;\n\t\tworkerNode->groupId = currentNode->groupId;\n\t\tworkerNode->nodeId = currentNode->nodeId;\n\t\tstrlcpy(workerNode->workerRack, currentNode->workerRack, WORKER_LENGTH);\n\t\tworkerNode->hasMetadata = currentNode->hasMetadata;\n\t\tworkerNode->metadataSynced = currentNode->metadataSynced;\n\t\tworkerNode->isActive = currentNode->isActive;\n\t\tworkerNode->nodeRole = currentNode->nodeRole;\n\t\tworkerNode->shouldHaveShards = currentNode->shouldHaveShards;\n\t\tworkerNode->nodeprimarynodeid = currentNode->nodeprimarynodeid;\n\t\tworkerNode->nodeisclone = currentNode->nodeisclone;\n\t\tstrlcpy(workerNode->nodeCluster, currentNode->nodeCluster, NAMEDATALEN);\n\n\t\tnewWorkerNodeArray[workerNodeIndex++] = workerNode;\n\n\t\tif (handleFound)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"multiple lines for worker node: \\\"%s:%u\\\"\",\n\t\t\t\t\t\t\t\t\t workerNode->workerName,\n\t\t\t\t\t\t\t\t\t workerNode->workerPort)));\n\t\t}\n\n\t\t/* we do not need the currentNode anymore */\n\t\tpfree(currentNode);\n\t}\n\n\t/* now, safe to destroy the old hash */\n\thash_destroy(WorkerNodeHash);\n\n\tif (WorkerNodeArray != NULL)\n\t{\n\t\tpfree(WorkerNodeArray);\n\t}\n\n\tWorkerNodeCount = newWorkerNodeCount;\n\tWorkerNodeArray = newWorkerNodeArray;\n\tWorkerNodeHash = newWorkerNodeHash;\n}\n\n\n/*\n * RegisterForeignKeyGraphCacheCallbacks registers callbacks required for\n * the foreign key graph cache.\n */\nstatic void\nRegisterForeignKeyGraphCacheCallbacks(void)\n{\n\t/* Watch for invalidation events. */\n\tCacheRegisterRelcacheCallback(InvalidateForeignRelationGraphCacheCallback,\n\t\t\t\t\t\t\t\t  (Datum) 0);\n}\n\n\n/*\n * RegisterWorkerNodeCacheCallbacks registers the callbacks required for the\n * worker node cache.  It's separate from InitializeWorkerNodeCache so the\n * callback can be registered early, before the metadata tables exist.\n */\nstatic void\nRegisterWorkerNodeCacheCallbacks(void)\n{\n\t/* Watch for invalidation events. */\n\tCacheRegisterRelcacheCallback(InvalidateNodeRelationCacheCallback,\n\t\t\t\t\t\t\t\t  (Datum) 0);\n}\n\n\n/*\n * RegisterCitusTableCacheEntryReleaseCallbacks registers callbacks to release\n * cache entries. Data should be locked by callers to avoid staleness.\n */\nstatic void\nRegisterCitusTableCacheEntryReleaseCallbacks(void)\n{\n\tRegisterResourceReleaseCallback(CitusTableCacheEntryReleaseCallback, NULL);\n}\n\n\n/*\n * GetLocalGroupId returns the group identifier of the local node. The function\n * assumes that pg_dist_local_group has exactly one row and has at least one\n * column. Otherwise, the function errors out.\n */\nint32\nGetLocalGroupId(void)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 0;\n\tint32 groupId = 0;\n\n\tInitializeCaches();\n\n\t/*\n\t * Already set the group id, no need to read the heap again.\n\t */\n\tif (LocalGroupId != -1)\n\t{\n\t\treturn LocalGroupId;\n\t}\n\n\tOid localGroupTableOid = DistLocalGroupIdRelationId();\n\tif (localGroupTableOid == InvalidOid)\n\t{\n\t\treturn 0;\n\t}\n\n\tRelation pgDistLocalGroupId = table_open(localGroupTableOid, AccessShareLock);\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistLocalGroupId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistLocalGroupId);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tbool isNull = false;\n\t\tDatum groupIdDatum = heap_getattr(heapTuple,\n\t\t\t\t\t\t\t\t\t\t  Anum_pg_dist_local_groupid,\n\t\t\t\t\t\t\t\t\t\t  tupleDescriptor, &isNull);\n\n\t\tgroupId = DatumGetInt32(groupIdDatum);\n\n\t\t/* set the local cache variable */\n\t\tLocalGroupId = groupId;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * Upgrade is happening. When upgrading postgres, pg_dist_local_group is\n\t\t * temporarily empty before citus_finish_pg_upgrade() finishes execution.\n\t\t */\n\t\tgroupId = GROUP_ID_UPGRADING;\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistLocalGroupId, AccessShareLock);\n\n\treturn groupId;\n}\n\n\n/*\n * GetNodeId returns the node identifier of the local node.\n */\nint32\nGetLocalNodeId(void)\n{\n\tInitializeCaches();\n\n\t/*\n\t * Already set the node id, no need to read the heap again.\n\t */\n\tif (LocalNodeId != -1)\n\t{\n\t\treturn LocalNodeId;\n\t}\n\n\tuint32 nodeId = -1;\n\n\tint32 localGroupId = GetLocalGroupId();\n\n\tbool includeNodesFromOtherClusters = false;\n\tList *workerNodeList = ReadDistNode(includeNodesFromOtherClusters);\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tif (workerNode->groupId == localGroupId &&\n\t\t\tworkerNode->isActive)\n\t\t{\n\t\t\tnodeId = workerNode->nodeId;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/*\n\t * nodeId is -1 if we cannot find an active node whose group id is\n\t * localGroupId in pg_dist_node.\n\t */\n\tif (nodeId == -1)\n\t{\n\t\telog(DEBUG4, \"there is no active node with group id '%d' on pg_dist_node\",\n\t\t\t localGroupId);\n\n\t\t/*\n\t\t * This is expected if the coordinator is not added to the metadata.\n\t\t * We'll return GLOBAL_PID_NODE_ID_FOR_NODES_NOT_IN_METADATA for this case and\n\t\t * for all cases so views can function almost normally\n\t\t */\n\t\tnodeId = GLOBAL_PID_NODE_ID_FOR_NODES_NOT_IN_METADATA;\n\t}\n\n\tLocalNodeId = nodeId;\n\n\treturn nodeId;\n}\n\n\n/*\n * RegisterLocalGroupIdCacheCallbacks registers the callbacks required to\n * maintain LocalGroupId at a consistent value. It's separate from\n * GetLocalGroupId so the callback can be registered early, before metadata\n * tables exist.\n */\nstatic void\nRegisterLocalGroupIdCacheCallbacks(void)\n{\n\t/* Watch for invalidation events. */\n\tCacheRegisterRelcacheCallback(InvalidateLocalGroupIdRelationCacheCallback,\n\t\t\t\t\t\t\t\t  (Datum) 0);\n}\n\n\n/*\n * RegisterAuthinfoCacheCallbacks registers the callbacks required to\n * maintain cached connection parameters at fresh values.\n */\nstatic void\nRegisterAuthinfoCacheCallbacks(void)\n{\n\t/* Watch for invalidation events. */\n\tCacheRegisterRelcacheCallback(InvalidateConnParamsCacheCallback, (Datum) 0);\n}\n\n\n/*\n * ResetCitusTableCacheEntry frees any out-of-band memory used by a cache entry,\n * but does not free the entry itself.\n */\nstatic void\nResetCitusTableCacheEntry(CitusTableCacheEntry *cacheEntry)\n{\n\tif (cacheEntry->partitionKeyString != NULL)\n\t{\n\t\tpfree(cacheEntry->partitionKeyString);\n\t\tcacheEntry->partitionKeyString = NULL;\n\t}\n\n\tif (cacheEntry->shardIntervalCompareFunction != NULL)\n\t{\n\t\tpfree(cacheEntry->shardIntervalCompareFunction);\n\t\tcacheEntry->shardIntervalCompareFunction = NULL;\n\t}\n\n\tif (cacheEntry->hashFunction)\n\t{\n\t\tpfree(cacheEntry->hashFunction);\n\t\tcacheEntry->hashFunction = NULL;\n\t}\n\n\tif (cacheEntry->partitionColumn != NULL)\n\t{\n\t\tpfree(cacheEntry->partitionColumn);\n\t\tcacheEntry->partitionColumn = NULL;\n\t}\n\n\tif (cacheEntry->shardIntervalArrayLength == 0)\n\t{\n\t\treturn;\n\t}\n\n\t/* clean up ShardIdCacheHash */\n\tRemoveStaleShardIdCacheEntries(cacheEntry);\n\n\tfor (int shardIndex = 0; shardIndex < cacheEntry->shardIntervalArrayLength;\n\t\t shardIndex++)\n\t{\n\t\tShardInterval *shardInterval = cacheEntry->sortedShardIntervalArray[shardIndex];\n\t\tGroupShardPlacement *placementArray =\n\t\t\tcacheEntry->arrayOfPlacementArrays[shardIndex];\n\t\tbool valueByVal = shardInterval->valueByVal;\n\n\t\t/* delete the shard's placements */\n\t\tif (placementArray != NULL)\n\t\t{\n\t\t\tpfree(placementArray);\n\t\t}\n\n\t\t/* delete data pointed to by ShardInterval */\n\t\tif (!valueByVal)\n\t\t{\n\t\t\tif (shardInterval->minValueExists)\n\t\t\t{\n\t\t\t\tpfree(DatumGetPointer(shardInterval->minValue));\n\t\t\t}\n\n\t\t\tif (shardInterval->maxValueExists)\n\t\t\t{\n\t\t\t\tpfree(DatumGetPointer(shardInterval->maxValue));\n\t\t\t}\n\t\t}\n\n\t\t/* and finally the ShardInterval itself */\n\t\tpfree(shardInterval);\n\t}\n\n\tif (cacheEntry->sortedShardIntervalArray)\n\t{\n\t\tpfree(cacheEntry->sortedShardIntervalArray);\n\t\tcacheEntry->sortedShardIntervalArray = NULL;\n\t}\n\tif (cacheEntry->arrayOfPlacementArrayLengths)\n\t{\n\t\tpfree(cacheEntry->arrayOfPlacementArrayLengths);\n\t\tcacheEntry->arrayOfPlacementArrayLengths = NULL;\n\t}\n\tif (cacheEntry->arrayOfPlacementArrays)\n\t{\n\t\tpfree(cacheEntry->arrayOfPlacementArrays);\n\t\tcacheEntry->arrayOfPlacementArrays = NULL;\n\t}\n\tif (cacheEntry->referencedRelationsViaForeignKey)\n\t{\n\t\tlist_free(cacheEntry->referencedRelationsViaForeignKey);\n\t\tcacheEntry->referencedRelationsViaForeignKey = NIL;\n\t}\n\tif (cacheEntry->referencingRelationsViaForeignKey)\n\t{\n\t\tlist_free(cacheEntry->referencingRelationsViaForeignKey);\n\t\tcacheEntry->referencingRelationsViaForeignKey = NIL;\n\t}\n\n\tcacheEntry->shardIntervalArrayLength = 0;\n\tcacheEntry->hasUninitializedShardInterval = false;\n\tcacheEntry->hasUniformHashDistribution = false;\n\tcacheEntry->hasOverlappingShardInterval = false;\n\tcacheEntry->autoConverted = false;\n\n\tpfree(cacheEntry);\n}\n\n\n/*\n * RemoveStaleShardIdCacheEntries removes all shard ID cache entries belonging to the\n * given table entry. If the shard ID belongs to a different (newer) table entry,\n * we leave it in place.\n */\nstatic void\nRemoveStaleShardIdCacheEntries(CitusTableCacheEntry *invalidatedTableEntry)\n{\n\tint shardIndex = 0;\n\tint shardCount = invalidatedTableEntry->shardIntervalArrayLength;\n\n\tfor (shardIndex = 0; shardIndex < shardCount; shardIndex++)\n\t{\n\t\tShardInterval *shardInterval =\n\t\t\tinvalidatedTableEntry->sortedShardIntervalArray[shardIndex];\n\t\tint64 shardId = shardInterval->shardId;\n\t\tbool foundInCache = false;\n\n\t\tShardIdCacheEntry *shardIdCacheEntry =\n\t\t\thash_search(ShardIdCacheHash, &shardId, HASH_FIND, &foundInCache);\n\n\t\tif (foundInCache && shardIdCacheEntry->tableEntry == invalidatedTableEntry)\n\t\t{\n\t\t\thash_search(ShardIdCacheHash, &shardId, HASH_REMOVE, &foundInCache);\n\t\t}\n\t}\n}\n\n\n/*\n * InvalidateForeignRelationGraphCacheCallback invalidates the foreign key relation\n * graph and entire distributed cache entries.\n */\nstatic void\nInvalidateForeignRelationGraphCacheCallback(Datum argument, Oid relationId)\n{\n\tif (relationId == MetadataCache.distColocationRelationId)\n\t{\n\t\tSetForeignConstraintRelationshipGraphInvalid();\n\t\tInvalidateDistTableCache();\n\t}\n}\n\n\n/*\n * InvalidateForeignKeyGraph is used to invalidate the cached foreign key\n * graph (see ForeignKeyRelationGraph @ utils/foreign_key_relationship.c).\n *\n * To invalidate the foreign key graph, we hack around relcache invalidation\n * callbacks. Given that there is no metadata table associated with the foreign\n * key graph cache, we use pg_dist_colocation, which is never invalidated for\n * other purposes.\n *\n * We acknowledge that it is not a very intuitive way of implementing this cache\n * invalidation, but, seems acceptable for now. If this becomes problematic, we\n * could try using a magic oid where we're sure that no relation would ever use\n * that oid.\n */\nvoid\nInvalidateForeignKeyGraph(void)\n{\n\tif (!CitusHasBeenLoaded())\n\t{\n\t\t/*\n\t\t * We should not try to invalidate foreign key graph\n\t\t * if citus is not loaded.\n\t\t */\n\t\treturn;\n\t}\n\n\tCitusInvalidateRelcacheByRelid(DistColocationRelationId());\n\n\t/* bump command counter to force invalidation to take effect */\n\tCommandCounterIncrement();\n}\n\n\n/*\n * InvalidateDistRelationCacheCallback flushes cache entries when a relation\n * is updated (or flushes the entire cache).\n */\nvoid\nInvalidateDistRelationCacheCallback(Datum argument, Oid relationId)\n{\n\t/* invalidate either entire cache or a specific entry */\n\tif (relationId == InvalidOid)\n\t{\n\t\tInvalidateDistTableCache();\n\t\tInvalidateDistObjectCache();\n\t\tInvalidateMetadataSystemCache();\n\t}\n\telse\n\t{\n\t\tvoid *hashKey = (void *) &relationId;\n\t\tbool foundInCache = false;\n\n\t\tif (DistTableCacheHash == NULL)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tCitusTableCacheEntrySlot *cacheSlot =\n\t\t\thash_search(DistTableCacheHash, hashKey, HASH_FIND, &foundInCache);\n\t\tif (foundInCache)\n\t\t{\n\t\t\tInvalidateCitusTableCacheEntrySlot(cacheSlot);\n\t\t}\n\n\t\t/*\n\t\t * if pg_dist_partition relcache is invalidated for some reason,\n\t\t * invalidate the MetadataCache. It is likely an overkill to invalidate\n\t\t * the entire cache here. But until a better fix, we keep it this way\n\t\t * for postgres regression tests that includes\n\t\t *   REINDEX SCHEMA CONCURRENTLY pg_catalog\n\t\t * command.\n\t\t */\n\t\tif (relationId == MetadataCache.distPartitionRelationId)\n\t\t{\n\t\t\tInvalidateMetadataSystemCache();\n\t\t}\n\n\n\t\tif (relationId == MetadataCache.distObjectRelationId)\n\t\t{\n\t\t\tInvalidateDistObjectCache();\n\t\t}\n\t}\n}\n\n\n/*\n * InvalidateCitusTableCacheEntrySlot marks a CitusTableCacheEntrySlot as invalid,\n * meaning it needs to be rebuilt and the citusTableMetadata (if any) should be\n * released.\n */\nstatic void\nInvalidateCitusTableCacheEntrySlot(CitusTableCacheEntrySlot *cacheSlot)\n{\n\t/* recheck whether this is a distributed table */\n\tcacheSlot->isValid = false;\n\n\tif (cacheSlot->citusTableMetadata != NULL)\n\t{\n\t\t/* reload the metadata */\n\t\tcacheSlot->citusTableMetadata->isValid = false;\n\n\t\t/* clean up ShardIdCacheHash */\n\t\tRemoveStaleShardIdCacheEntries(cacheSlot->citusTableMetadata);\n\t}\n}\n\n\n/*\n * InvalidateDistTableCache marks all DistTableCacheHash entries invalid.\n */\nstatic void\nInvalidateDistTableCache(void)\n{\n\tCitusTableCacheEntrySlot *cacheSlot = NULL;\n\tHASH_SEQ_STATUS status;\n\n\tif (DistTableCacheHash == NULL)\n\t{\n\t\treturn;\n\t}\n\n\thash_seq_init(&status, DistTableCacheHash);\n\n\twhile ((cacheSlot = (CitusTableCacheEntrySlot *) hash_seq_search(&status)) != NULL)\n\t{\n\t\tInvalidateCitusTableCacheEntrySlot(cacheSlot);\n\t}\n}\n\n\n/*\n * InvalidateDistObjectCache marks all DistObjectCacheHash entries invalid.\n */\nstatic void\nInvalidateDistObjectCache(void)\n{\n\tDistObjectCacheEntry *cacheEntry = NULL;\n\tHASH_SEQ_STATUS status;\n\n\tif (DistObjectCacheHash == NULL)\n\t{\n\t\treturn;\n\t}\n\n\thash_seq_init(&status, DistObjectCacheHash);\n\n\twhile ((cacheEntry = (DistObjectCacheEntry *) hash_seq_search(&status)) != NULL)\n\t{\n\t\tcacheEntry->isValid = false;\n\t}\n}\n\n\n/*\n * FlushDistTableCache flushes the entire distributed relation cache, frees\n * all entries, and recreates the cache.\n */\nvoid\nFlushDistTableCache(void)\n{\n\tCitusTableCacheEntrySlot *cacheSlot = NULL;\n\tHASH_SEQ_STATUS status;\n\n\thash_seq_init(&status, DistTableCacheHash);\n\n\twhile ((cacheSlot = (CitusTableCacheEntrySlot *) hash_seq_search(&status)) != NULL)\n\t{\n\t\tResetCitusTableCacheEntry(cacheSlot->citusTableMetadata);\n\t}\n\n\thash_destroy(DistTableCacheHash);\n\thash_destroy(ShardIdCacheHash);\n\tCreateDistTableCache();\n\tCreateShardIdCache();\n}\n\n\n/* CreateDistTableCache initializes the per-table hash table */\nstatic void\nCreateDistTableCache(void)\n{\n\tHASHCTL info;\n\tMemSet(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(Oid);\n\tinfo.entrysize = sizeof(CitusTableCacheEntrySlot);\n\tinfo.hash = tag_hash;\n\tinfo.hcxt = MetadataCacheMemoryContext;\n\tDistTableCacheHash =\n\t\thash_create(\"Distributed Relation Cache\", 32, &info,\n\t\t\t\t\tHASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);\n}\n\n\n/* CreateShardIdCache initializes the shard ID mapping */\nstatic void\nCreateShardIdCache(void)\n{\n\tHASHCTL info;\n\tMemSet(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(int64);\n\tinfo.entrysize = sizeof(ShardIdCacheEntry);\n\tinfo.hash = tag_hash;\n\tinfo.hcxt = MetadataCacheMemoryContext;\n\tShardIdCacheHash =\n\t\thash_create(\"Shard Id Cache\", 128, &info,\n\t\t\t\t\tHASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);\n}\n\n\n/* CreateDistObjectCache initializes the per-object hash table */\nstatic void\nCreateDistObjectCache(void)\n{\n\tHASHCTL info;\n\tMemSet(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(DistObjectCacheEntryKey);\n\tinfo.entrysize = sizeof(DistObjectCacheEntry);\n\tinfo.hash = tag_hash;\n\tinfo.hcxt = MetadataCacheMemoryContext;\n\tDistObjectCacheHash =\n\t\thash_create(\"Distributed Object Cache\", 32, &info,\n\t\t\t\t\tHASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);\n}\n\n\n/*\n * InvalidateMetadataSystemCache resets all the cached OIDs and the extensionCreatedState\n * flag and invalidates the worker node, ConnParams, and local group ID caches.\n */\nvoid\nInvalidateMetadataSystemCache(void)\n{\n\tInvalidateConnParamsHashEntries();\n\n\tmemset(&MetadataCache, 0, sizeof(MetadataCache));\n\tworkerNodeHashValid = false;\n\tLocalGroupId = -1;\n\tLocalNodeId = -1;\n}\n\n\n/*\n * AllCitusTableIds returns all citus table ids.\n */\nList *\nAllCitusTableIds(void)\n{\n\treturn CitusTableTypeIdList(ANY_CITUS_TABLE_TYPE);\n}\n\n\n/*\n * CitusTableTypeIdList function scans pg_dist_partition and returns a\n * list of OID's for the tables matching given citusTableType.\n * To create the list, it performs sequential scan. Since it is not expected\n * that this function will be called frequently, it is OK not to use index\n * scan. If this function becomes performance bottleneck, it is possible to\n * modify this function to perform index scan.\n */\nList *\nCitusTableTypeIdList(CitusTableType citusTableType)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 0;\n\tList *relationIdList = NIL;\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), AccessShareLock);\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tDatum *datumArray = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNullArray = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tmemset(datumArray, 0, tupleDescriptor->natts * sizeof(Datum));\n\t\tmemset(isNullArray, 0, tupleDescriptor->natts * sizeof(bool));\n\n\t\theap_deform_tuple(heapTuple, tupleDescriptor, datumArray, isNullArray);\n\n\t\tDatum partMethodDatum = datumArray[Anum_pg_dist_partition_partmethod - 1];\n\t\tDatum replicationModelDatum = datumArray[Anum_pg_dist_partition_repmodel - 1];\n\t\tDatum colocationIdDatum = datumArray[Anum_pg_dist_partition_colocationid - 1];\n\n\t\tchar partitionMethod = DatumGetChar(partMethodDatum);\n\t\tchar replicationModel = DatumGetChar(replicationModelDatum);\n\t\tuint32 colocationId = DatumGetUInt32(colocationIdDatum);\n\n\t\tif (IsCitusTableTypeInternal(partitionMethod, replicationModel, colocationId,\n\t\t\t\t\t\t\t\t\t citusTableType))\n\t\t{\n\t\t\tDatum relationIdDatum = datumArray[Anum_pg_dist_partition_logicalrelid - 1];\n\n\t\t\tOid relationId = DatumGetObjectId(relationIdDatum);\n\n\t\t\trelationIdList = lappend_oid(relationIdList, relationId);\n\t\t}\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tpfree(datumArray);\n\tpfree(isNullArray);\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistPartition, AccessShareLock);\n\n\treturn relationIdList;\n}\n\n\n/*\n * InvalidateNodeRelationCacheCallback destroys the WorkerNodeHash when\n * any change happens on pg_dist_node table. It also set WorkerNodeHash to\n * NULL, which allows consequent accesses to the hash read from the\n * pg_dist_node from scratch.\n */\nstatic void\nInvalidateNodeRelationCacheCallback(Datum argument, Oid relationId)\n{\n\tif (relationId == InvalidOid || relationId == MetadataCache.distNodeRelationId)\n\t{\n\t\tworkerNodeHashValid = false;\n\t\tLocalNodeId = -1;\n\t}\n}\n\n\n/*\n * InvalidateLocalGroupIdRelationCacheCallback sets the LocalGroupId to\n * the default value.\n */\nstatic void\nInvalidateLocalGroupIdRelationCacheCallback(Datum argument, Oid relationId)\n{\n\t/* when invalidation happens simply set the LocalGroupId to the default value */\n\tif (relationId == InvalidOid || relationId == MetadataCache.distLocalGroupRelationId)\n\t{\n\t\tLocalGroupId = -1;\n\t}\n}\n\n\n/*\n * InvalidateConnParamsCacheCallback sets isValid flag to false for all entries\n * in ConnParamsHash, a cache used during connection establishment.\n */\nstatic void\nInvalidateConnParamsCacheCallback(Datum argument, Oid relationId)\n{\n\tif (relationId == MetadataCache.distAuthinfoRelationId ||\n\t\trelationId == MetadataCache.distPoolinfoRelationId ||\n\t\trelationId == InvalidOid)\n\t{\n\t\tConnParamsHashEntry *entry = NULL;\n\t\tHASH_SEQ_STATUS status;\n\n\t\thash_seq_init(&status, ConnParamsHash);\n\n\t\twhile ((entry = (ConnParamsHashEntry *) hash_seq_search(&status)) != NULL)\n\t\t{\n\t\t\tentry->isValid = false;\n\t\t}\n\t}\n}\n\n\n/*\n * CitusTableCacheFlushInvalidatedEntries frees invalidated cache entries.\n * Invalidated entries aren't freed immediately as callers expect their lifetime\n * to extend beyond that scope.\n */\nvoid\nCitusTableCacheFlushInvalidatedEntries()\n{\n\tif (DistTableCacheHash != NULL && DistTableCacheExpired != NIL)\n\t{\n\t\tCitusTableCacheEntry *cacheEntry = NULL;\n\t\tforeach_declared_ptr(cacheEntry, DistTableCacheExpired)\n\t\t{\n\t\t\tResetCitusTableCacheEntry(cacheEntry);\n\t\t}\n\t\tlist_free(DistTableCacheExpired);\n\t\tDistTableCacheExpired = NIL;\n\t}\n}\n\n\n/*\n * CitusTableCacheEntryReleaseCallback frees invalidated cache entries.\n */\nstatic void\nCitusTableCacheEntryReleaseCallback(ResourceReleasePhase phase, bool isCommit,\n\t\t\t\t\t\t\t\t\tbool isTopLevel, void *arg)\n{\n\tif (isTopLevel && phase == RESOURCE_RELEASE_LOCKS)\n\t{\n\t\tCitusTableCacheFlushInvalidatedEntries();\n\t}\n}\n\n\n/*\n * LookupDistPartitionTuple searches pg_dist_partition for relationId's entry\n * and returns that or, if no matching entry was found, NULL.\n */\nstatic HeapTuple\nLookupDistPartitionTuple(Relation pgDistPartition, Oid relationId)\n{\n\tHeapTuple distPartitionTuple = NULL;\n\tScanKeyData scanKey[1];\n\n\t/* copy scankey to local copy, it will be modified during the scan */\n\tscanKey[0] = DistPartitionScanKey[0];\n\n\t/* set scan arguments */\n\tscanKey[0].sk_argument = ObjectIdGetDatum(relationId);\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPartitionLogicalRelidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\ttrue, NULL, 1, scanKey);\n\n\tHeapTuple currentPartitionTuple = systable_getnext(scanDescriptor);\n\tif (HeapTupleIsValid(currentPartitionTuple))\n\t{\n\t\tdistPartitionTuple = heap_copytuple(currentPartitionTuple);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\n\treturn distPartitionTuple;\n}\n\n\n/*\n * LookupDistShardTuples returns a list of all dist_shard tuples for the\n * specified relation.\n */\nList *\nLookupDistShardTuples(Oid relationId)\n{\n\tList *distShardTupleList = NIL;\n\tScanKeyData scanKey[1];\n\n\tRelation pgDistShard = table_open(DistShardRelationId(), AccessShareLock);\n\n\t/* copy scankey to local copy, it will be modified during the scan */\n\tscanKey[0] = DistShardScanKey[0];\n\n\t/* set scan arguments */\n\tscanKey[0].sk_argument = ObjectIdGetDatum(relationId);\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistShard,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistShardLogicalRelidIndexId(), true,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, 1, scanKey);\n\n\tHeapTuple currentShardTuple = systable_getnext(scanDescriptor);\n\twhile (HeapTupleIsValid(currentShardTuple))\n\t{\n\t\tHeapTuple shardTupleCopy = heap_copytuple(currentShardTuple);\n\t\tdistShardTupleList = lappend(distShardTupleList, shardTupleCopy);\n\n\t\tcurrentShardTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistShard, AccessShareLock);\n\n\treturn distShardTupleList;\n}\n\n\n/*\n * LookupShardRelationFromCatalog returns the logical relation oid a shard belongs to.\n *\n * Errors out if the shardId does not exist and missingOk is false.\n * Returns InvalidOid if the shardId does not exist and missingOk is true.\n */\nOid\nLookupShardRelationFromCatalog(int64 shardId, bool missingOk)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tForm_pg_dist_shard shardForm = NULL;\n\tRelation pgDistShard = table_open(DistShardRelationId(), AccessShareLock);\n\tOid relationId = InvalidOid;\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_shard_shardid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(shardId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistShard,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistShardShardidIndexId(), true,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple) && !missingOk)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for shard \"\n\t\t\t\t\t\t\t   UINT64_FORMAT, shardId)));\n\t}\n\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\trelationId = InvalidOid;\n\t}\n\telse\n\t{\n\t\tshardForm = (Form_pg_dist_shard) GETSTRUCT(heapTuple);\n\t\trelationId = shardForm->logicalrelid;\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistShard, NoLock);\n\n\treturn relationId;\n}\n\n\n/*\n * GetPartitionTypeInputInfo populates output parameters with the interval type\n * identifier and modifier for the specified partition key/method combination.\n */\nstatic void\nGetPartitionTypeInputInfo(char *partitionKeyString, char partitionMethod,\n\t\t\t\t\t\t  Oid *columnTypeId, int32 *columnTypeMod,\n\t\t\t\t\t\t  Oid *intervalTypeId, int32 *intervalTypeMod)\n{\n\t*columnTypeId = InvalidOid;\n\t*columnTypeMod = -1;\n\t*intervalTypeId = InvalidOid;\n\t*intervalTypeMod = -1;\n\n\tswitch (partitionMethod)\n\t{\n\t\tcase DISTRIBUTE_BY_APPEND:\n\t\tcase DISTRIBUTE_BY_RANGE:\n\t\tcase DISTRIBUTE_BY_HASH:\n\t\t{\n\t\t\tNode *partitionNode = stringToNode(partitionKeyString);\n\t\t\tVar *partitionColumn = (Var *) partitionNode;\n\t\t\tAssert(IsA(partitionNode, Var));\n\n\t\t\tGetIntervalTypeInfo(partitionMethod, partitionColumn,\n\t\t\t\t\t\t\t\tintervalTypeId, intervalTypeMod);\n\n\t\t\t*columnTypeId = partitionColumn->vartype;\n\t\t\t*columnTypeMod = partitionColumn->vartypmod;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase DISTRIBUTE_BY_NONE:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"unsupported table partition type: %c\",\n\t\t\t\t\t\t\t\t   partitionMethod)));\n\t\t}\n\t}\n}\n\n\n/*\n * GetIntervalTypeInfo gets type id and type mod of the min/max values\n * of shard intervals for a distributed table with given partition method\n * and partition column.\n */\nvoid\nGetIntervalTypeInfo(char partitionMethod, Var *partitionColumn,\n\t\t\t\t\tOid *intervalTypeId, int32 *intervalTypeMod)\n{\n\t*intervalTypeId = InvalidOid;\n\t*intervalTypeMod = -1;\n\n\tswitch (partitionMethod)\n\t{\n\t\tcase DISTRIBUTE_BY_APPEND:\n\t\tcase DISTRIBUTE_BY_RANGE:\n\t\t{\n\t\t\t/* we need a valid partition column Var in this case */\n\t\t\tif (partitionColumn == NULL)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t\t\t\t\terrmsg(\"unexpected partition column value: null\"),\n\t\t\t\t\t\t\t\terrdetail(\"Please report this to the Citus core team.\")));\n\t\t\t}\n\t\t\t*intervalTypeId = partitionColumn->vartype;\n\t\t\t*intervalTypeMod = partitionColumn->vartypmod;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase DISTRIBUTE_BY_HASH:\n\t\t{\n\t\t\t*intervalTypeId = INT4OID;\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n/*\n * TupleToShardInterval transforms the specified dist_shard tuple into a new\n * ShardInterval using the provided descriptor and partition type information.\n */\nShardInterval *\nTupleToShardInterval(HeapTuple heapTuple, TupleDesc tupleDescriptor, Oid\n\t\t\t\t\t intervalTypeId,\n\t\t\t\t\t int32 intervalTypeMod)\n{\n\tDatum datumArray[Natts_pg_dist_shard];\n\tbool isNullArray[Natts_pg_dist_shard];\n\n\t/*\n\t * We use heap_deform_tuple() instead of heap_getattr() to expand tuple\n\t * to contain missing values when ALTER TABLE ADD COLUMN happens.\n\t */\n\theap_deform_tuple(heapTuple, tupleDescriptor, datumArray, isNullArray);\n\n\tShardInterval *shardInterval =\n\t\tDeformedDistShardTupleToShardInterval(datumArray, isNullArray,\n\t\t\t\t\t\t\t\t\t\t\t  intervalTypeId, intervalTypeMod);\n\n\treturn shardInterval;\n}\n\n\n/*\n * DeformedDistShardTupleToShardInterval transforms the specified deformed\n * pg_dist_shard tuple into a new ShardInterval.\n */\nShardInterval *\nDeformedDistShardTupleToShardInterval(Datum *datumArray, bool *isNullArray,\n\t\t\t\t\t\t\t\t\t  Oid intervalTypeId, int32 intervalTypeMod)\n{\n\tOid inputFunctionId = InvalidOid;\n\tOid typeIoParam = InvalidOid;\n\tDatum minValue = 0;\n\tDatum maxValue = 0;\n\tbool minValueExists = false;\n\tbool maxValueExists = false;\n\tint16 intervalTypeLen = 0;\n\tbool intervalByVal = false;\n\tchar intervalAlign = '0';\n\tchar intervalDelim = '0';\n\n\tOid relationId =\n\t\tDatumGetObjectId(datumArray[Anum_pg_dist_shard_logicalrelid - 1]);\n\tint64 shardId = DatumGetInt64(datumArray[Anum_pg_dist_shard_shardid - 1]);\n\tchar storageType = DatumGetChar(datumArray[Anum_pg_dist_shard_shardstorage - 1]);\n\tDatum minValueTextDatum = datumArray[Anum_pg_dist_shard_shardminvalue - 1];\n\tDatum maxValueTextDatum = datumArray[Anum_pg_dist_shard_shardmaxvalue - 1];\n\n\tbool minValueNull = isNullArray[Anum_pg_dist_shard_shardminvalue - 1];\n\tbool maxValueNull = isNullArray[Anum_pg_dist_shard_shardmaxvalue - 1];\n\n\tif (!minValueNull && !maxValueNull)\n\t{\n\t\tchar *minValueString = TextDatumGetCString(minValueTextDatum);\n\t\tchar *maxValueString = TextDatumGetCString(maxValueTextDatum);\n\n\t\t/* TODO: move this up the call stack to avoid per-tuple invocation? */\n\t\tget_type_io_data(intervalTypeId, IOFunc_input, &intervalTypeLen,\n\t\t\t\t\t\t &intervalByVal,\n\t\t\t\t\t\t &intervalAlign, &intervalDelim, &typeIoParam,\n\t\t\t\t\t\t &inputFunctionId);\n\n\t\t/* finally convert min/max values to their actual types */\n\t\tminValue = OidInputFunctionCall(inputFunctionId, minValueString,\n\t\t\t\t\t\t\t\t\t\ttypeIoParam, intervalTypeMod);\n\t\tmaxValue = OidInputFunctionCall(inputFunctionId, maxValueString,\n\t\t\t\t\t\t\t\t\t\ttypeIoParam, intervalTypeMod);\n\n\t\tminValueExists = true;\n\t\tmaxValueExists = true;\n\t}\n\n\tShardInterval *shardInterval = CitusMakeNode(ShardInterval);\n\tshardInterval->relationId = relationId;\n\tshardInterval->storageType = storageType;\n\tshardInterval->valueTypeId = intervalTypeId;\n\tshardInterval->valueTypeLen = intervalTypeLen;\n\tshardInterval->valueByVal = intervalByVal;\n\tshardInterval->minValueExists = minValueExists;\n\tshardInterval->maxValueExists = maxValueExists;\n\tshardInterval->minValue = minValue;\n\tshardInterval->maxValue = maxValue;\n\tshardInterval->shardId = shardId;\n\n\treturn shardInterval;\n}\n\n\n/*\n * CachedNamespaceLookup performs a cached lookup for the namespace (schema), with the\n * result cached in cachedOid.\n */\nstatic void\nCachedNamespaceLookup(const char *nspname, Oid *cachedOid)\n{\n\t/* force callbacks to be registered, so we always get notified upon changes */\n\tInitializeCaches();\n\n\tif (*cachedOid == InvalidOid)\n\t{\n\t\t*cachedOid = get_namespace_oid(nspname, true);\n\n\t\tif (*cachedOid == InvalidOid)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\"cache lookup failed for namespace %s, called too early?\",\n\t\t\t\t\t\t\t\tnspname)));\n\t\t}\n\t}\n}\n\n\n/*\n * CachedRelationLookup performs a cached lookup for the relation\n * relationName, with the result cached in *cachedOid.\n */\nstatic void\nCachedRelationLookup(const char *relationName, Oid *cachedOid)\n{\n\tCachedRelationNamespaceLookup(relationName, PG_CATALOG_NAMESPACE, cachedOid);\n}\n\n\n/*\n * CachedRelationLookupExtended performs a cached lookup for the relation\n * relationName, with the result cached in *cachedOid. Will _not_ throw an error when\n * missing_ok is set to true.\n */\nstatic void\nCachedRelationLookupExtended(const char *relationName, Oid *cachedOid, bool missing_ok)\n{\n\tCachedRelationNamespaceLookupExtended(relationName, PG_CATALOG_NAMESPACE, cachedOid,\n\t\t\t\t\t\t\t\t\t\t  missing_ok);\n}\n\n\nstatic void\nCachedRelationNamespaceLookup(const char *relationName, Oid relnamespace,\n\t\t\t\t\t\t\t  Oid *cachedOid)\n{\n\tCachedRelationNamespaceLookupExtended(relationName, relnamespace, cachedOid, false);\n}\n\n\nstatic void\nCachedRelationNamespaceLookupExtended(const char *relationName, Oid relnamespace,\n\t\t\t\t\t\t\t\t\t  Oid *cachedOid, bool missing_ok)\n{\n\t/* force callbacks to be registered, so we always get notified upon changes */\n\tInitializeCaches();\n\n\tif (*cachedOid == InvalidOid)\n\t{\n\t\t*cachedOid = get_relname_relid(relationName, relnamespace);\n\n\t\tif (*cachedOid == InvalidOid && !missing_ok)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\"cache lookup failed for %s, called too early?\",\n\t\t\t\t\t\t\t\trelationName)));\n\t\t}\n\t}\n}\n\n\n/*\n * RelationExists returns whether a relation with the given OID exists.\n */\nbool\nRelationExists(Oid relationId)\n{\n\tHeapTuple relTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));\n\n\tbool relationExists = HeapTupleIsValid(relTuple);\n\tif (relationExists)\n\t{\n\t\tReleaseSysCache(relTuple);\n\t}\n\n\treturn relationExists;\n}\n\n\n/*\n * Register a relcache invalidation for a non-shared relation.\n *\n * We ignore the case that there's no corresponding pg_class entry - that\n * happens if we register a relcache invalidation (e.g. for a\n * pg_dist_partition deletion) after the relation has been dropped. That's ok,\n * because in those cases we're guaranteed to already have registered an\n * invalidation for the target relation.\n */\nvoid\nCitusInvalidateRelcacheByRelid(Oid relationId)\n{\n\tHeapTuple classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));\n\n\tif (HeapTupleIsValid(classTuple))\n\t{\n\t\tCacheInvalidateRelcacheByTuple(classTuple);\n\t\tReleaseSysCache(classTuple);\n\t}\n}\n\n\n/*\n * Register a relcache invalidation for the distributed relation associated\n * with the shard.\n */\nvoid\nCitusInvalidateRelcacheByShardId(int64 shardId)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tForm_pg_dist_shard shardForm = NULL;\n\tRelation pgDistShard = table_open(DistShardRelationId(), AccessShareLock);\n\n\t/*\n\t * Load shard, to find the associated relation id. Can't use\n\t * LoadShardInterval directly because that'd fail if the shard doesn't\n\t * exist anymore, which we can't have. Also lower overhead is desirable\n\t * here.\n\t */\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_shard_shardid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(shardId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistShard,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistShardShardidIndexId(), true,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tshardForm = (Form_pg_dist_shard) GETSTRUCT(heapTuple);\n\t\tCitusInvalidateRelcacheByRelid(shardForm->logicalrelid);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * Couldn't find associated relation. That can primarily happen in two cases:\n\t\t *\n\t\t * 1) A placement row is inserted before the shard row. That's fine,\n\t\t *\t  since we don't need invalidations via placements in that case.\n\t\t *\n\t\t * 2) The shard has been deleted, but some placements were\n\t\t *    unreachable, and the user is manually deleting the rows. Not\n\t\t *    much point in WARNING or ERRORing in that case either, there's\n\t\t *    nothing to invalidate.\n\t\t *\n\t\t * Hence we just emit a DEBUG5 message.\n\t\t */\n\t\tereport(DEBUG5, (errmsg(\n\t\t\t\t\t\t\t \"could not find distributed relation to invalidate for \"\n\t\t\t\t\t\t\t \"shard \"INT64_FORMAT, shardId)));\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistShard, NoLock);\n\n\t/* bump command counter, to force invalidation to take effect */\n\tCommandCounterIncrement();\n}\n\n\n/*\n * DistNodeMetadata returns the single metadata jsonb object stored in\n * pg_dist_node_metadata.\n */\nDatum\nDistNodeMetadata(void)\n{\n\tDatum metadata = 0;\n\tScanKeyData scanKey[1];\n\tconst int scanKeyCount = 0;\n\n\tOid metadataTableOid = get_relname_relid(\"pg_dist_node_metadata\",\n\t\t\t\t\t\t\t\t\t\t\t PG_CATALOG_NAMESPACE);\n\tif (metadataTableOid == InvalidOid)\n\t{\n\t\tereport(ERROR, (errmsg(\"pg_dist_node_metadata was not found\")));\n\t}\n\n\tRelation pgDistNodeMetadata = table_open(metadataTableOid, AccessShareLock);\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistNodeMetadata,\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistNodeMetadata);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tbool isNull = false;\n\t\tmetadata = heap_getattr(heapTuple, Anum_pg_dist_node_metadata_metadata,\n\t\t\t\t\t\t\t\ttupleDescriptor, &isNull);\n\t\tAssert(!isNull);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"could not find any entries in pg_dist_metadata\")));\n\t}\n\n\t/*\n\t * Copy the jsonb result before closing the table\n\t * since that memory can be freed.\n\t */\n\tmetadata = JsonbPGetDatum(DatumGetJsonbPCopy(metadata));\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistNodeMetadata, AccessShareLock);\n\n\treturn metadata;\n}\n\n\n/*\n * role_exists is a check constraint which ensures that roles referenced in the\n * pg_dist_authinfo catalog actually exist (at least at the time of insertion).\n */\nDatum\nrole_exists(PG_FUNCTION_ARGS)\n{\n\tName roleName = PG_GETARG_NAME(0);\n\tbool roleExists = SearchSysCacheExists1(AUTHNAME, NameGetDatum(roleName));\n\n\tPG_RETURN_BOOL(roleExists);\n}\n\n\n/*\n * GetPoolinfoViaCatalog searches the pg_dist_poolinfo table for a row matching\n * the provided nodeId and returns the poolinfo field of this row if found.\n * Otherwise, this function returns NULL.\n */\nchar *\nGetPoolinfoViaCatalog(int32 nodeId)\n{\n\tScanKeyData scanKey[1];\n\tconst int scanKeyCount = 1;\n\tconst AttrNumber nodeIdIdx = 1, poolinfoIdx = 2;\n\tRelation pgDistPoolinfo = table_open(DistPoolinfoRelationId(), AccessShareLock);\n\tbool indexOK = true;\n\tchar *poolinfo = NULL;\n\n\t/* set scan arguments */\n\tScanKeyInit(&scanKey[0], nodeIdIdx, BTEqualStrategyNumber, F_INT4EQ,\n\t\t\t\tInt32GetDatum(nodeId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPoolinfo, DistPoolinfoIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPoolinfo);\n\t\tbool isNull = false;\n\n\t\tDatum poolinfoDatum = heap_getattr(heapTuple, poolinfoIdx, tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t   &isNull);\n\n\t\tAssert(!isNull);\n\n\t\tpoolinfo = TextDatumGetCString(poolinfoDatum);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistPoolinfo, AccessShareLock);\n\n\treturn poolinfo;\n}\n\n\n/*\n * GetAuthinfoViaCatalog searches pg_dist_authinfo for a row matching a pro-\n * vided role and node id. Three types of rules are currently permitted: those\n * matching a specific node (non-zero nodeid), those matching all nodes (a\n * nodeid of zero), and those denoting a loopback connection (nodeid of -1).\n * Rolename must always be specified. If both types of rules exist for a given\n * user/host, the more specific (host-specific) rule wins. This means that when\n * both a zero and non-zero row exist for a given rolename, the non-zero row\n * has precedence.\n *\n * In short, this function will return a rule matching nodeId, or if that's\n * absent the rule for 0, or if that's absent, an empty string. Callers can\n * just use the returned authinfo and know the precedence has been honored.\n */\nchar *\nGetAuthinfoViaCatalog(const char *roleName, int64 nodeId)\n{\n\tchar *authinfo = \"\";\n\tDatum nodeIdDatumArray[2] = {\n\t\tInt32GetDatum(nodeId),\n\t\tInt32GetDatum(WILDCARD_NODE_ID)\n\t};\n\tArrayType *nodeIdArrayType = DatumArrayToArrayType(nodeIdDatumArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   lengthof(nodeIdDatumArray),\n\t\t\t\t\t\t\t\t\t\t\t\t\t   INT4OID);\n\tScanKeyData scanKey[2];\n\tconst AttrNumber nodeIdIdx = 1, roleIdx = 2, authinfoIdx = 3;\n\n\t/*\n\t * Our index's definition ensures correct precedence for positive nodeIds,\n\t * but when handling a negative value we need to traverse backwards to keep\n\t * the invariant that the zero rule has lowest precedence.\n\t */\n\tScanDirection direction = (nodeId < 0) ? BackwardScanDirection : ForwardScanDirection;\n\n\tif (ReindexIsProcessingIndex(DistAuthinfoIndexId()))\n\t{\n\t\tereport(ERROR, (errmsg(\"authinfo is being reindexed; try again\")));\n\t}\n\n\tmemset(&scanKey, 0, sizeof(scanKey));\n\n\t/* first column in index is rolename, need exact match there ... */\n\tScanKeyInit(&scanKey[0], roleIdx, BTEqualStrategyNumber,\n\t\t\t\tF_NAMEEQ, CStringGetDatum(roleName));\n\n\t/* second column is nodeId, match against array of nodeid and zero (any node) ... */\n\tScanKeyInit(&scanKey[1], nodeIdIdx, BTEqualStrategyNumber,\n\t\t\t\tF_INT4EQ, PointerGetDatum(nodeIdArrayType));\n\tscanKey[1].sk_flags |= SK_SEARCHARRAY;\n\n\t/*\n\t * It's important that we traverse the index in order: we need to ensure\n\t * that rules with nodeid 0 are encountered last. We'll use the first tuple\n\t * we find. This ordering defines the precedence order of authinfo rules.\n\t */\n\tRelation pgDistAuthinfo = table_open(DistAuthinfoRelationId(), AccessShareLock);\n\tRelation pgDistAuthinfoIdx = index_open(DistAuthinfoIndexId(), AccessShareLock);\n\tSysScanDesc scanDescriptor = systable_beginscan_ordered(pgDistAuthinfo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpgDistAuthinfoIdx,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, lengthof(scanKey),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tscanKey);\n\n\t/* first tuple represents highest-precedence rule for this node */\n\tHeapTuple authinfoTuple = systable_getnext_ordered(scanDescriptor, direction);\n\tif (HeapTupleIsValid(authinfoTuple))\n\t{\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistAuthinfo);\n\t\tbool isNull = false;\n\n\t\tDatum authinfoDatum = heap_getattr(authinfoTuple, authinfoIdx,\n\t\t\t\t\t\t\t\t\t\t   tupleDescriptor, &isNull);\n\n\t\tAssert(!isNull);\n\n\t\tauthinfo = TextDatumGetCString(authinfoDatum);\n\t}\n\n\tsystable_endscan_ordered(scanDescriptor);\n\tindex_close(pgDistAuthinfoIdx, AccessShareLock);\n\ttable_close(pgDistAuthinfo, AccessShareLock);\n\n\treturn authinfo;\n}\n\n\n/*\n * authinfo_valid is a check constraint to verify that an inserted authinfo row\n * uses only permitted libpq parameters.\n */\nDatum\nauthinfo_valid(PG_FUNCTION_ARGS)\n{\n\tchar *authinfo = TextDatumGetCString(PG_GETARG_DATUM(0));\n\n\t/* this array _must_ be kept in an order usable by bsearch */\n\tconst char *allowList[] = { \"password\", \"sslcert\", \"sslkey\" };\n\tbool authinfoValid = CheckConninfo(authinfo, allowList, lengthof(allowList), NULL);\n\n\tPG_RETURN_BOOL(authinfoValid);\n}\n\n\n/*\n * poolinfo_valid is a check constraint to verify that an inserted poolinfo row\n * uses only permitted libpq parameters.\n */\nDatum\npoolinfo_valid(PG_FUNCTION_ARGS)\n{\n\tchar *poolinfo = TextDatumGetCString(PG_GETARG_DATUM(0));\n\n\t/* this array _must_ be kept in an order usable by bsearch */\n\tconst char *allowList[] = { \"dbname\", \"host\", \"port\" };\n\tbool poolinfoValid = CheckConninfo(poolinfo, allowList, lengthof(allowList), NULL);\n\n\tPG_RETURN_BOOL(poolinfoValid);\n}\n"
  },
  {
    "path": "src/backend/distributed/metadata/metadata_sync.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * metadata_sync.c\n *\n * Routines for synchronizing metadata to all workers.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <signal.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/nbtree.h\"\n#include \"access/sysattr.h\"\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_attrdef.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_database.h\"\n#include \"catalog/pg_database_d.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_foreign_server.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/async.h\"\n#include \"commands/dbcommands.h\"\n#include \"executor/spi.h\"\n#include \"foreign/foreign.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/pg_list.h\"\n#include \"parser/parse_type.h\"\n#include \"postmaster/bgworker.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/snapmgr.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/argutils.h\"\n#include \"distributed/backend_data.h\"\n#include \"distributed/background_worker_utils.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/distribution_column.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/maintenanced.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata/pg_dist_object.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/pg_dist_colocation.h\"\n#include \"distributed/pg_dist_node.h\"\n#include \"distributed/pg_dist_schema.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/remote_transaction.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/tenant_schema_metadata.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/utils/function.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_transaction.h\"\n\n\n/* managed via a GUC */\nchar *EnableManualMetadataChangesForUser = \"\";\nint MetadataSyncTransMode = METADATA_SYNC_TRANSACTIONAL;\n\n\nstatic void EnsureObjectMetadataIsSane(int distributionArgumentIndex,\n\t\t\t\t\t\t\t\t\t   int colocationId);\nstatic List * GetFunctionDependenciesForObjects(ObjectAddress *objectAddress);\nstatic char * SchemaOwnerName(Oid objectId);\nstatic bool HasMetadataWorkers(void);\nstatic void CreateShellTableOnWorkers(Oid relationId);\nstatic void CreateTableMetadataOnWorkers(Oid relationId);\nstatic void CreateDependingViewsOnWorkers(Oid relationId);\nstatic void AddTableToPublications(Oid relationId);\nstatic NodeMetadataSyncResult SyncNodeMetadataToNodesOptional(void);\nstatic bool ShouldSyncTableMetadataInternal(bool hashDistributed,\n\t\t\t\t\t\t\t\t\t\t\tbool citusTableWithNoDistKey);\nstatic bool SyncNodeMetadataSnapshotToNode(WorkerNode *workerNode, bool raiseOnError);\nstatic void DropMetadataSnapshotOnNode(WorkerNode *workerNode);\nstatic char * CreateSequenceDependencyCommand(Oid relationId, Oid sequenceId,\n\t\t\t\t\t\t\t\t\t\t\t  char *columnName);\nstatic GrantStmt * GenerateGrantStmtForRights(ObjectType objectType,\n\t\t\t\t\t\t\t\t\t\t\t  Oid roleOid,\n\t\t\t\t\t\t\t\t\t\t\t  Oid objectId,\n\t\t\t\t\t\t\t\t\t\t\t  char *permission,\n\t\t\t\t\t\t\t\t\t\t\t  bool withGrantOption);\nstatic List * GetObjectsForGrantStmt(ObjectType objectType, Oid objectId);\nstatic AccessPriv * GetAccessPrivObjectForGrantStmt(char *permission);\nstatic List * GenerateGrantOnSchemaQueriesFromAclItem(Oid schemaOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  AclItem *aclItem);\nstatic List * GenerateGrantOnDatabaseFromAclItem(Oid databaseOid, AclItem *aclItem);\nstatic List * GenerateGrantOnFunctionQueriesFromAclItem(Oid schemaOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tAclItem *aclItem);\nstatic List * GrantOnSequenceDDLCommands(Oid sequenceOid);\nstatic List * GenerateGrantOnSequenceQueriesFromAclItem(Oid sequenceOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tAclItem *aclItem);\nstatic char * GenerateSetRoleQuery(Oid roleOid);\nstatic void MetadataSyncSigTermHandler(SIGNAL_ARGS);\nstatic void MetadataSyncSigAlrmHandler(SIGNAL_ARGS);\n\n\nstatic bool ShouldSkipMetadataChecks(void);\nstatic void EnsurePartitionMetadataIsSane(Oid relationId, char distributionMethod,\n\t\t\t\t\t\t\t\t\t\t  int colocationId, char replicationModel,\n\t\t\t\t\t\t\t\t\t\t  Var *distributionKey);\nstatic void EnsureCitusInitiatedOperation(void);\nstatic void EnsureShardMetadataIsSane(Oid relationId, int64 shardId, char storageType,\n\t\t\t\t\t\t\t\t\t  text *shardMinValue,\n\t\t\t\t\t\t\t\t\t  text *shardMaxValue);\nstatic void EnsureShardPlacementMetadataIsSane(Oid relationId, int64 shardId,\n\t\t\t\t\t\t\t\t\t\t\t   int64 placementId,\n\t\t\t\t\t\t\t\t\t\t\t   int64 shardLength, int32 groupId);\nstatic char * ColocationGroupCreateCommand(uint32 colocationId, int shardCount,\n\t\t\t\t\t\t\t\t\t\t   int replicationFactor,\n\t\t\t\t\t\t\t\t\t\t   Oid distributionColumnType,\n\t\t\t\t\t\t\t\t\t\t   Oid distributionColumnCollation);\nstatic char * ColocationGroupDeleteCommand(uint32 colocationId);\nstatic char * RemoteSchemaIdExpressionById(Oid schemaId);\nstatic char * RemoteSchemaIdExpressionByName(char *schemaName);\nstatic char * GetRemoteTypeName(Oid typeId);\nstatic char * GetRemoteTypeNamespace(Oid typeId);\nstatic char * RemoteCollationIdExpression(Oid colocationId);\nstatic char * RemoteTableIdExpression(Oid relationId);\n\n\nPG_FUNCTION_INFO_V1(start_metadata_sync_to_all_nodes);\nPG_FUNCTION_INFO_V1(start_metadata_sync_to_node);\nPG_FUNCTION_INFO_V1(stop_metadata_sync_to_node);\nPG_FUNCTION_INFO_V1(worker_record_sequence_dependency);\n\n\n/*\n * Functions to modify metadata. Normally modifying metadata requires\n * superuser. However, these functions can be called with superusers\n * or regular users as long as the regular user owns the input object.\n */\nPG_FUNCTION_INFO_V1(citus_internal_add_partition_metadata);\nPG_FUNCTION_INFO_V1(citus_internal_delete_partition_metadata);\nPG_FUNCTION_INFO_V1(citus_internal_add_shard_metadata);\nPG_FUNCTION_INFO_V1(citus_internal_add_placement_metadata);\nPG_FUNCTION_INFO_V1(citus_internal_delete_placement_metadata);\nPG_FUNCTION_INFO_V1(citus_internal_add_placement_metadata_legacy);\nPG_FUNCTION_INFO_V1(citus_internal_update_placement_metadata);\nPG_FUNCTION_INFO_V1(citus_internal_delete_shard_metadata);\nPG_FUNCTION_INFO_V1(citus_internal_update_relation_colocation);\nPG_FUNCTION_INFO_V1(citus_internal_add_object_metadata);\nPG_FUNCTION_INFO_V1(citus_internal_add_colocation_metadata);\nPG_FUNCTION_INFO_V1(citus_internal_delete_colocation_metadata);\nPG_FUNCTION_INFO_V1(citus_internal_add_tenant_schema);\nPG_FUNCTION_INFO_V1(citus_internal_delete_tenant_schema);\nPG_FUNCTION_INFO_V1(citus_internal_update_none_dist_table_metadata);\nPG_FUNCTION_INFO_V1(citus_internal_database_command);\n\n\nstatic bool got_SIGTERM = false;\nstatic bool got_SIGALRM = false;\n\n#define METADATA_SYNC_APP_NAME \"Citus Metadata Sync Daemon\"\n\n\n/*\n * start_metadata_sync_to_node function sets hasmetadata column of the given\n * node to true, and then activate node without replicating reference tables.\n */\nDatum\nstart_metadata_sync_to_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *nodeName = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\n\tEnsureSuperUser();\n\tEnsureCoordinator();\n\n\tchar *nodeNameString = text_to_cstring(nodeName);\n\tWorkerNode *workerNode = ModifiableWorkerNode(nodeNameString, nodePort);\n\n\t/*\n\t * Create MetadataSyncContext which is used throughout nodes' activation.\n\t * It contains activated nodes, bare connections if the mode is nontransactional,\n\t * and a memory context for allocation.\n\t */\n\tbool collectCommands = false;\n\tbool nodesAddedInSameTransaction = false;\n\tMetadataSyncContext *context = CreateMetadataSyncContext(list_make1(workerNode),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t collectCommands,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t nodesAddedInSameTransaction);\n\n\tActivateNodeList(context);\n\tTransactionModifiedNodeMetadata = true;\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * start_metadata_sync_to_all_nodes function sets hasmetadata column of\n * all the primary worker nodes to true, and then activate nodes without\n * replicating reference tables.\n */\nDatum\nstart_metadata_sync_to_all_nodes(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tEnsureSuperUser();\n\tEnsureCoordinator();\n\n\tList *nodeList = ActivePrimaryNonCoordinatorNodeList(RowShareLock);\n\n\t/*\n\t * Create MetadataSyncContext which is used throughout nodes' activation.\n\t * It contains activated nodes, bare connections if the mode is nontransactional,\n\t * and a memory context for allocation.\n\t */\n\tbool collectCommands = false;\n\tbool nodesAddedInSameTransaction = false;\n\tMetadataSyncContext *context = CreateMetadataSyncContext(nodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t collectCommands,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t nodesAddedInSameTransaction);\n\n\tActivateNodeList(context);\n\tTransactionModifiedNodeMetadata = true;\n\n\tPG_RETURN_BOOL(true);\n}\n\n\n/*\n * SyncCitusTableMetadata syncs citus table metadata to worker nodes with metadata.\n * Our definition of metadata includes the shell table and its inter relations with\n * other shell tables, corresponding pg_dist_object, pg_dist_partiton, pg_dist_shard\n * and pg_dist_shard placement entries. This function also propagates the views that\n * depend on the given relation, to the metadata workers, and adds the relation to\n * the appropriate publications.\n */\nvoid\nSyncCitusTableMetadata(Oid relationId)\n{\n\tCreateShellTableOnWorkers(relationId);\n\tCreateTableMetadataOnWorkers(relationId);\n\tCreateInterTableRelationshipOfRelationOnWorkers(relationId);\n\n\tif (!IsTableOwnedByExtension(relationId))\n\t{\n\t\tObjectAddress relationAddress = { 0 };\n\t\tObjectAddressSet(relationAddress, RelationRelationId, relationId);\n\t\tMarkObjectDistributed(&relationAddress);\n\t}\n\n\tCreateDependingViewsOnWorkers(relationId);\n\tAddTableToPublications(relationId);\n}\n\n\n/*\n * CreateDependingViewsOnWorkers takes a relationId and creates the views that depend on\n * that relation on workers with metadata. Propagated views are marked as distributed.\n */\nstatic void\nCreateDependingViewsOnWorkers(Oid relationId)\n{\n\tList *views = GetDependingViews(relationId);\n\n\tif (list_length(views) < 1)\n\t{\n\t\t/* no view to propagate */\n\t\treturn;\n\t}\n\n\tSendCommandToWorkersWithMetadata(DISABLE_DDL_PROPAGATION);\n\n\tOid viewOid = InvalidOid;\n\tforeach_declared_oid(viewOid, views)\n\t{\n\t\tif (!ShouldMarkRelationDistributed(viewOid))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tObjectAddress *viewAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*viewAddress, RelationRelationId, viewOid);\n\t\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(viewAddress));\n\n\t\tchar *createViewCommand = CreateViewDDLCommand(viewOid);\n\t\tchar *alterViewOwnerCommand = AlterViewOwnerCommand(viewOid);\n\n\t\tSendCommandToWorkersWithMetadata(createViewCommand);\n\t\tSendCommandToWorkersWithMetadata(alterViewOwnerCommand);\n\n\t\tMarkObjectDistributed(viewAddress);\n\t}\n\n\tSendCommandToWorkersWithMetadata(ENABLE_DDL_PROPAGATION);\n}\n\n\n/*\n * AddTableToPublications adds the table to a publication on workers with metadata.\n */\nstatic void\nAddTableToPublications(Oid relationId)\n{\n\tList *publicationIds = GetRelationPublications(relationId);\n\tif (publicationIds == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tOid publicationId = InvalidOid;\n\n\tSendCommandToWorkersWithMetadata(DISABLE_DDL_PROPAGATION);\n\n\tforeach_declared_oid(publicationId, publicationIds)\n\t{\n\t\tObjectAddress *publicationAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*publicationAddress, PublicationRelationId, publicationId);\n\t\tList *addresses = list_make1(publicationAddress);\n\n\t\tif (!ShouldPropagateAnyObject(addresses))\n\t\t{\n\t\t\t/* skip non-distributed publications */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* ensure schemas exist */\n\t\tEnsureAllObjectDependenciesExistOnAllNodes(addresses);\n\n\t\tbool isAdd = true;\n\t\tchar *alterPublicationCommand =\n\t\t\tGetAlterPublicationTableDDLCommand(publicationId, relationId, isAdd);\n\n\t\t/* send ALTER PUBLICATION .. ADD to workers with metadata */\n\t\tSendCommandToWorkersWithMetadata(alterPublicationCommand);\n\t}\n\n\tSendCommandToWorkersWithMetadata(ENABLE_DDL_PROPAGATION);\n}\n\n\n/*\n * EnsureSequentialModeMetadataOperations makes sure that the current transaction is\n * already in sequential mode, or can still safely be put in sequential mode,\n * it errors if that is not possible. The error contains information for the user to\n * retry the transaction with sequential mode set from the beginning.\n *\n * Metadata objects (e.g., distributed table on the workers) exists only 1 instance of\n * the type used by potentially multiple other shards/connections. To make sure all\n * shards/connections in the transaction can interact with the metadata needs to be\n * visible on all connections used by the transaction, meaning we can only use 1\n * connection per node.\n */\nvoid\nEnsureSequentialModeMetadataOperations(void)\n{\n\tif (!IsTransactionBlock())\n\t{\n\t\t/* we do not need to switch to sequential mode if we are not in a transaction */\n\t\treturn;\n\t}\n\n\tif (ParallelQueryExecutedInTransaction())\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"cannot execute metadata syncing operation because there was a \"\n\t\t\t\t\t\t\t\"parallel operation on a distributed table in the \"\n\t\t\t\t\t\t\t\"transaction\"),\n\t\t\t\t\t\terrdetail(\"When modifying metadata, Citus needs to \"\n\t\t\t\t\t\t\t\t  \"perform all operations over a single connection per \"\n\t\t\t\t\t\t\t\t  \"node to ensure consistency.\"),\n\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t}\n\n\tereport(DEBUG1, (errmsg(\"switching to sequential query execution mode\"),\n\t\t\t\t\t errdetail(\"Metadata synced or stopped syncing. To make \"\n\t\t\t\t\t\t\t   \"sure subsequent commands see the metadata correctly \"\n\t\t\t\t\t\t\t   \"we need to make sure to use only one connection for \"\n\t\t\t\t\t\t\t   \"all future commands\")));\n\tSetLocalMultiShardModifyModeToSequential();\n}\n\n\n/*\n * stop_metadata_sync_to_node function sets the hasmetadata column of the specified node\n * to false in pg_dist_node table, thus indicating that the specified worker node does not\n * receive DDL changes anymore and cannot be used for issuing queries.\n */\nDatum\nstop_metadata_sync_to_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\tEnsureSuperUser();\n\n\ttext *nodeName = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\tbool clearMetadata = PG_GETARG_BOOL(2);\n\tchar *nodeNameString = text_to_cstring(nodeName);\n\n\tLockRelationOid(DistNodeRelationId(), ExclusiveLock);\n\n\tWorkerNode *workerNode = FindWorkerNodeAnyCluster(nodeNameString, nodePort);\n\tif (workerNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"node (%s,%d) does not exist\", nodeNameString, nodePort)));\n\t}\n\n\tif (NodeIsCoordinator(workerNode))\n\t{\n\t\tereport(NOTICE, (errmsg(\"node (%s,%d) is the coordinator and should have \"\n\t\t\t\t\t\t\t\t\"metadata, skipping stopping the metadata sync\",\n\t\t\t\t\t\t\t\tnodeNameString, nodePort)));\n\t\tPG_RETURN_VOID();\n\t}\n\n\tif (clearMetadata)\n\t{\n\t\tif (NodeIsPrimary(workerNode))\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\"dropping metadata on the node (%s,%d)\",\n\t\t\t\t\t\t\t\t\tnodeNameString, nodePort)));\n\t\t\tDropMetadataSnapshotOnNode(workerNode);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * If this is a secondary node we can't actually clear metadata from it,\n\t\t\t * we assume the primary node is cleared.\n\t\t\t */\n\t\t\tereport(NOTICE, (errmsg(\"(%s,%d) is a secondary node: to clear the metadata,\"\n\t\t\t\t\t\t\t\t\t\" you should clear metadata from the primary node\",\n\t\t\t\t\t\t\t\t\tnodeNameString, nodePort)));\n\t\t}\n\t}\n\n\tworkerNode = SetWorkerColumn(workerNode, Anum_pg_dist_node_hasmetadata, BoolGetDatum(\n\t\t\t\t\t\t\t\t\t false));\n\tworkerNode = SetWorkerColumn(workerNode, Anum_pg_dist_node_metadatasynced,\n\t\t\t\t\t\t\t\t BoolGetDatum(false));\n\n\tTransactionModifiedNodeMetadata = true;\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * ClusterHasKnownMetadataWorkers returns true if the node executing the function\n * knows at least one worker with metadata. We do it\n * (a) by checking the node that executes the function is a worker with metadata\n * (b) the coordinator knows at least one worker with metadata.\n */\nbool\nClusterHasKnownMetadataWorkers()\n{\n\treturn !IsCoordinator() || HasMetadataWorkers();\n}\n\n\n/*\n * ShouldSyncUserCommandForObject checks if the user command should be synced to the\n * worker nodes for the given object.\n */\nbool\nShouldSyncUserCommandForObject(ObjectAddress objectAddress)\n{\n\tif (objectAddress.classId == RelationRelationId)\n\t{\n\t\tOid relOid = objectAddress.objectId;\n\t\treturn ShouldSyncTableMetadata(relOid) ||\n\t\t\t   ShouldSyncSequenceMetadata(relOid) ||\n\t\t\t   get_rel_relkind(relOid) == RELKIND_VIEW;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ShouldSyncTableMetadata checks if the metadata of a distributed table should be\n * propagated to metadata workers, i.e. the table is a hash distributed table or\n * a Citus table that doesn't have shard key.\n */\nbool\nShouldSyncTableMetadata(Oid relationId)\n{\n\tif (!EnableMetadataSync ||\n\t\t!OidIsValid(relationId) || !IsCitusTable(relationId))\n\t{\n\t\treturn false;\n\t}\n\n\tCitusTableCacheEntry *tableEntry = GetCitusTableCacheEntry(relationId);\n\n\tbool hashDistributed = IsCitusTableTypeCacheEntry(tableEntry, HASH_DISTRIBUTED);\n\tbool citusTableWithNoDistKey =\n\t\t!HasDistributionKeyCacheEntry(tableEntry);\n\n\treturn ShouldSyncTableMetadataInternal(hashDistributed, citusTableWithNoDistKey);\n}\n\n\n/*\n * ShouldSyncTableMetadataViaCatalog checks if the metadata of a Citus table should\n * be propagated to metadata workers, i.e. the table is an MX table or Citus table\n * that doesn't have shard key.\n * Tables with streaming replication model (which means RF=1) and hash distribution are\n * considered as MX tables.\n *\n * ShouldSyncTableMetadataViaCatalog does not use the CitusTableCache and instead reads\n * from catalog tables directly.\n */\nbool\nShouldSyncTableMetadataViaCatalog(Oid relationId)\n{\n\tif (!OidIsValid(relationId) || !IsCitusTableViaCatalog(relationId))\n\t{\n\t\treturn false;\n\t}\n\n\tchar partitionMethod = PartitionMethodViaCatalog(relationId);\n\tbool hashDistributed = partitionMethod == DISTRIBUTE_BY_HASH;\n\tbool citusTableWithNoDistKey = partitionMethod == DISTRIBUTE_BY_NONE;\n\n\treturn ShouldSyncTableMetadataInternal(hashDistributed, citusTableWithNoDistKey);\n}\n\n\n/*\n * FetchRelationIdFromPgPartitionHeapTuple returns relation id from given heap tuple.\n */\nOid\nFetchRelationIdFromPgPartitionHeapTuple(HeapTuple heapTuple, TupleDesc tupleDesc)\n{\n\tAssert(heapTuple->t_tableOid == DistPartitionRelationId());\n\n\tDatum *datumArray = (Datum *) palloc(tupleDesc->natts * sizeof(Datum));\n\tbool *isNullArray = (bool *) palloc(tupleDesc->natts * sizeof(bool));\n\n\theap_deform_tuple(heapTuple, tupleDesc, datumArray, isNullArray);\n\n\tDatum relationIdDatum = datumArray[Anum_pg_dist_partition_logicalrelid - 1];\n\tOid relationId = DatumGetObjectId(relationIdDatum);\n\n\tpfree(datumArray);\n\tpfree(isNullArray);\n\n\treturn relationId;\n}\n\n\n/*\n * ShouldSyncTableMetadataInternal decides whether we should sync the metadata for a table\n * based on whether it is a hash distributed table, or a citus table with no distribution\n * key.\n *\n * This function is here to make sure that ShouldSyncTableMetadata and\n * ShouldSyncTableMetadataViaCatalog behaves the same way.\n */\nstatic bool\nShouldSyncTableMetadataInternal(bool hashDistributed, bool citusTableWithNoDistKey)\n{\n\treturn hashDistributed || citusTableWithNoDistKey;\n}\n\n\n/*\n * ShouldSyncSequenceMetadata checks if the metadata of a sequence should be\n * propagated to metadata workers, i.e. the sequence is marked as distributed\n */\nbool\nShouldSyncSequenceMetadata(Oid relationId)\n{\n\tif (!OidIsValid(relationId) || !(get_rel_relkind(relationId) == RELKIND_SEQUENCE))\n\t{\n\t\treturn false;\n\t}\n\n\tObjectAddress *sequenceAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*sequenceAddress, RelationRelationId, relationId);\n\n\treturn IsAnyObjectDistributed(list_make1(sequenceAddress));\n}\n\n\n/*\n * SyncMetadataSnapshotToNode does the following:\n * SyncNodeMetadataSnapshotToNode does the following:\n *  1. Sets the localGroupId on the worker so the worker knows which tuple in\n *     pg_dist_node represents itself.\n *  2. Recreates the node metadata on the given worker.\n * If raiseOnError is true, it errors out if synchronization fails.\n */\nstatic bool\nSyncNodeMetadataSnapshotToNode(WorkerNode *workerNode, bool raiseOnError)\n{\n\tchar *currentUser = CurrentUserName();\n\n\t/* generate and add the local group id's update query */\n\tchar *localGroupIdUpdateCommand = LocalGroupIdUpdateCommand(workerNode->groupId);\n\n\t/* generate the queries which drop the node metadata */\n\tList *dropMetadataCommandList = NodeMetadataDropCommands();\n\n\t/* generate the queries which create the node metadata from scratch */\n\tList *createMetadataCommandList = NodeMetadataCreateCommands();\n\n\tList *recreateMetadataSnapshotCommandList = list_make1(localGroupIdUpdateCommand);\n\trecreateMetadataSnapshotCommandList = list_concat(recreateMetadataSnapshotCommandList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  dropMetadataCommandList);\n\trecreateMetadataSnapshotCommandList = list_concat(recreateMetadataSnapshotCommandList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  createMetadataCommandList);\n\n\t/*\n\t * Send the snapshot recreation commands in a single remote transaction and\n\t * if requested, error out in any kind of failure. Note that it is not\n\t * required to send createMetadataSnapshotCommandList in the same transaction\n\t * that we send nodeDeleteCommand and nodeInsertCommand commands below.\n\t */\n\tif (raiseOnError)\n\t{\n\t\tSendMetadataCommandListToWorkerListInCoordinatedTransaction(list_make1(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerNode),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcurrentUser,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trecreateMetadataSnapshotCommandList);\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tbool success =\n\t\t\tSendOptionalMetadataCommandListToWorkerInCoordinatedTransaction(\n\t\t\t\tworkerNode->workerName, workerNode->workerPort,\n\t\t\t\tcurrentUser, recreateMetadataSnapshotCommandList);\n\n\t\treturn success;\n\t}\n}\n\n\n/*\n * DropMetadataSnapshotOnNode creates the queries which drop the metadata and sends them\n * to the worker given as parameter.\n */\nstatic void\nDropMetadataSnapshotOnNode(WorkerNode *workerNode)\n{\n\tEnsureSequentialModeMetadataOperations();\n\n\tchar *userName = CurrentUserName();\n\n\t/*\n\t * Detach partitions, break dependencies between sequences and table then\n\t * remove shell tables first.\n\t */\n\tbool singleTransaction = true;\n\tList *dropMetadataCommandList = DetachPartitionCommandList();\n\tdropMetadataCommandList = lappend(dropMetadataCommandList,\n\t\t\t\t\t\t\t\t\t  BREAK_ALL_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND);\n\tdropMetadataCommandList = lappend(dropMetadataCommandList,\n\t\t\t\t\t\t\t\t\t  WorkerDropAllShellTablesCommand(singleTransaction));\n\tdropMetadataCommandList = list_concat(dropMetadataCommandList,\n\t\t\t\t\t\t\t\t\t\t  NodeMetadataDropCommands());\n\tdropMetadataCommandList = lappend(dropMetadataCommandList,\n\t\t\t\t\t\t\t\t\t  LocalGroupIdUpdateCommand(0));\n\n\t/* remove all dist table and object/table related metadata afterwards */\n\tdropMetadataCommandList = lappend(dropMetadataCommandList, DELETE_ALL_PARTITIONS);\n\tdropMetadataCommandList = lappend(dropMetadataCommandList, DELETE_ALL_SHARDS);\n\tdropMetadataCommandList = lappend(dropMetadataCommandList, DELETE_ALL_PLACEMENTS);\n\tdropMetadataCommandList = lappend(dropMetadataCommandList,\n\t\t\t\t\t\t\t\t\t  DELETE_ALL_DISTRIBUTED_OBJECTS);\n\tdropMetadataCommandList = lappend(dropMetadataCommandList, DELETE_ALL_COLOCATION);\n\n\tAssert(superuser());\n\tSendOptionalMetadataCommandListToWorkerInCoordinatedTransaction(\n\t\tworkerNode->workerName,\n\t\tworkerNode->workerPort,\n\t\tuserName,\n\t\tdropMetadataCommandList);\n}\n\n\n/*\n * NodeMetadataCreateCommands returns list of queries that are\n * required to create the current metadata snapshot of the node that the\n * function is called. The metadata snapshot commands includes the\n * following queries:\n *\n * (i)   Query that populates pg_dist_node table\n */\nList *\nNodeMetadataCreateCommands(void)\n{\n\tList *metadataSnapshotCommandList = NIL;\n\tbool includeNodesFromOtherClusters = true;\n\tList *workerNodeList = ReadDistNode(includeNodesFromOtherClusters);\n\n\t/* make sure we have deterministic output for our tests */\n\tworkerNodeList = SortList(workerNodeList, CompareWorkerNodes);\n\n\t/* generate insert command for pg_dist_node table */\n\tchar *nodeListInsertCommand = NodeListInsertCommand(workerNodeList);\n\tmetadataSnapshotCommandList = lappend(metadataSnapshotCommandList,\n\t\t\t\t\t\t\t\t\t\t  nodeListInsertCommand);\n\n\treturn metadataSnapshotCommandList;\n}\n\n\n/*\n * CitusTableMetadataCreateCommandList returns the set of commands necessary to\n * create the given distributed table metadata on a worker.\n */\nList *\nCitusTableMetadataCreateCommandList(Oid relationId)\n{\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\n\tList *commandList = NIL;\n\n\t/* command to insert pg_dist_partition entry */\n\tchar *metadataCommand = DistributionCreateCommand(cacheEntry);\n\tcommandList = lappend(commandList, metadataCommand);\n\n\t/* commands to insert pg_dist_shard & pg_dist_placement entries */\n\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\tList *shardMetadataInsertCommandList = ShardListInsertCommand(shardIntervalList);\n\tcommandList = list_concat(commandList, shardMetadataInsertCommandList);\n\n\treturn commandList;\n}\n\n\n/*\n * NodeMetadataDropCommands returns list of queries that are required to\n * drop all the metadata of the node that are not related to clustered tables.\n * The drop metadata snapshot commands includes the following queries:\n *\n * (i) Queries that delete all the rows from pg_dist_node table\n */\nList *\nNodeMetadataDropCommands(void)\n{\n\tList *dropSnapshotCommandList = NIL;\n\n\tdropSnapshotCommandList = lappend(dropSnapshotCommandList, DELETE_ALL_NODES);\n\n\treturn dropSnapshotCommandList;\n}\n\n\n/*\n * NodeListInsertCommand generates a single multi-row INSERT command that can be\n * executed to insert the nodes that are in workerNodeList to pg_dist_node table.\n */\nchar *\nNodeListInsertCommand(List *workerNodeList)\n{\n\tStringInfo nodeListInsertCommand = makeStringInfo();\n\tint workerCount = list_length(workerNodeList);\n\tint processedWorkerNodeCount = 0;\n\tOid primaryRole = PrimaryNodeRoleId();\n\n\t/* if there are no workers, return NULL */\n\tif (workerCount == 0)\n\t{\n\t\treturn nodeListInsertCommand->data;\n\t}\n\n\tif (primaryRole == InvalidOid)\n\t{\n\t\tereport(ERROR, (errmsg(\"bad metadata, noderole does not exist\"),\n\t\t\t\t\t\terrdetail(\"you should never see this, please submit \"\n\t\t\t\t\t\t\t\t  \"a bug report\"),\n\t\t\t\t\t\terrhint(\"run ALTER EXTENSION citus UPDATE and try again\")));\n\t}\n\n\t/* generate the query without any values yet */\n\tappendStringInfo(nodeListInsertCommand,\n\t\t\t\t\t \"INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, \"\n\t\t\t\t\t \"noderack, hasmetadata, metadatasynced, isactive, noderole, \"\n\t\t\t\t\t \"nodecluster, shouldhaveshards, nodeisclone, nodeprimarynodeid) VALUES \");\n\n\t/* iterate over the worker nodes, add the values */\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tchar *hasMetadataString = workerNode->hasMetadata ? \"TRUE\" : \"FALSE\";\n\t\tchar *metadataSyncedString = workerNode->metadataSynced ? \"TRUE\" : \"FALSE\";\n\t\tchar *isActiveString = workerNode->isActive ? \"TRUE\" : \"FALSE\";\n\t\tchar *shouldHaveShards = workerNode->shouldHaveShards ? \"TRUE\" : \"FALSE\";\n\t\tchar *nodeiscloneString = workerNode->nodeisclone ? \"TRUE\" : \"FALSE\";\n\n\t\tDatum nodeRoleOidDatum = ObjectIdGetDatum(workerNode->nodeRole);\n\t\tDatum nodeRoleStringDatum = DirectFunctionCall1(enum_out, nodeRoleOidDatum);\n\t\tchar *nodeRoleString = DatumGetCString(nodeRoleStringDatum);\n\n\t\tappendStringInfo(nodeListInsertCommand,\n\t\t\t\t\t\t \"(%d, %d, %s, %d, %s, %s, %s, %s, '%s'::noderole, %s, %s, %s, %d)\",\n\t\t\t\t\t\t workerNode->nodeId,\n\t\t\t\t\t\t workerNode->groupId,\n\t\t\t\t\t\t quote_literal_cstr(workerNode->workerName),\n\t\t\t\t\t\t workerNode->workerPort,\n\t\t\t\t\t\t quote_literal_cstr(workerNode->workerRack),\n\t\t\t\t\t\t hasMetadataString,\n\t\t\t\t\t\t metadataSyncedString,\n\t\t\t\t\t\t isActiveString,\n\t\t\t\t\t\t nodeRoleString,\n\t\t\t\t\t\t quote_literal_cstr(workerNode->nodeCluster),\n\t\t\t\t\t\t shouldHaveShards,\n\t\t\t\t\t\t nodeiscloneString,\n\t\t\t\t\t\t workerNode->nodeprimarynodeid);\n\n\t\tprocessedWorkerNodeCount++;\n\t\tif (processedWorkerNodeCount != workerCount)\n\t\t{\n\t\t\tappendStringInfo(nodeListInsertCommand, \",\");\n\t\t}\n\t}\n\n\treturn nodeListInsertCommand->data;\n}\n\n\n/*\n * NodeListIdempotentInsertCommand generates an idempotent multi-row INSERT command that\n * can be executed to insert the nodes that are in workerNodeList to pg_dist_node table.\n * It would insert new nodes or replace current nodes with new nodes if nodename-nodeport\n * pairs already exist.\n */\nchar *\nNodeListIdempotentInsertCommand(List *workerNodeList)\n{\n\tStringInfo nodeInsertIdempotentCommand = makeStringInfo();\n\tchar *nodeInsertStr = NodeListInsertCommand(workerNodeList);\n\tappendStringInfoString(nodeInsertIdempotentCommand, nodeInsertStr);\n\tchar *onConflictStr = \" ON CONFLICT ON CONSTRAINT pg_dist_node_nodename_nodeport_key \"\n\t\t\t\t\t\t  \"DO UPDATE SET nodeid = EXCLUDED.nodeid, \"\n\t\t\t\t\t\t  \"groupid = EXCLUDED.groupid, \"\n\t\t\t\t\t\t  \"nodename = EXCLUDED.nodename, \"\n\t\t\t\t\t\t  \"nodeport = EXCLUDED.nodeport, \"\n\t\t\t\t\t\t  \"noderack = EXCLUDED.noderack, \"\n\t\t\t\t\t\t  \"hasmetadata = EXCLUDED.hasmetadata, \"\n\t\t\t\t\t\t  \"isactive = EXCLUDED.isactive, \"\n\t\t\t\t\t\t  \"noderole = EXCLUDED.noderole, \"\n\t\t\t\t\t\t  \"nodecluster = EXCLUDED.nodecluster, \"\n\t\t\t\t\t\t  \"metadatasynced = EXCLUDED.metadatasynced, \"\n\t\t\t\t\t\t  \"shouldhaveshards = EXCLUDED.shouldhaveshards, \"\n\t\t\t\t\t\t  \"nodeisclone = EXCLUDED.nodeisclone, \"\n\t\t\t\t\t\t  \"nodeprimarynodeid = EXCLUDED.nodeprimarynodeid\";\n\tappendStringInfoString(nodeInsertIdempotentCommand, onConflictStr);\n\treturn nodeInsertIdempotentCommand->data;\n}\n\n\n/*\n * MarkObjectsDistributedCreateCommand generates a command that can be executed to\n * insert or update the provided objects into pg_dist_object on a worker node.\n */\nchar *\nMarkObjectsDistributedCreateCommand(List *addresses,\n\t\t\t\t\t\t\t\t\tList *namesArg,\n\t\t\t\t\t\t\t\t\tList *distributionArgumentIndexes,\n\t\t\t\t\t\t\t\t\tList *colocationIds,\n\t\t\t\t\t\t\t\t\tList *forceDelegations)\n{\n\tStringInfo insertDistributedObjectsCommand = makeStringInfo();\n\n\tAssert(list_length(addresses) == list_length(distributionArgumentIndexes));\n\tAssert(list_length(distributionArgumentIndexes) == list_length(colocationIds));\n\n\tappendStringInfo(insertDistributedObjectsCommand,\n\t\t\t\t\t \"WITH distributed_object_data(typetext, objnames, \"\n\t\t\t\t\t \"objargs, distargumentindex, colocationid, force_delegation)  AS (VALUES \");\n\n\tbool isFirstObject = true;\n\tfor (int currentObjectCounter = 0; currentObjectCounter < list_length(addresses);\n\t\t currentObjectCounter++)\n\t{\n\t\tObjectAddress *address = list_nth(addresses, currentObjectCounter);\n\t\tint distributionArgumentIndex = list_nth_int(distributionArgumentIndexes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t currentObjectCounter);\n\t\tint colocationId = list_nth_int(colocationIds, currentObjectCounter);\n\t\tint forceDelegation = list_nth_int(forceDelegations, currentObjectCounter);\n\t\tList *names = NIL;\n\t\tList *args = NIL;\n\t\tchar *objectType = NULL;\n\n\t\tif (IsMainDBCommand)\n\t\t{\n\t\t\t/*\n\t\t\t * When we try to distribute an object that's being created in a non Citus\n\t\t\t * main database, we cannot find the name, since the object is not visible\n\t\t\t * in Citus main database.\n\t\t\t * Because of that we need to pass the name to this function.\n\t\t\t */\n\t\t\tnames = list_nth(namesArg, currentObjectCounter);\n\t\t\tbool missingOk = false;\n\t\t\tobjectType = getObjectTypeDescription(address, missingOk);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tobjectType = getObjectTypeDescription(address, false);\n\t\t\tgetObjectIdentityParts(address, &names, &args, IsMainDBCommand);\n\t\t}\n\n\t\tif (!isFirstObject)\n\t\t{\n\t\t\tappendStringInfo(insertDistributedObjectsCommand, \", \");\n\t\t}\n\t\tisFirstObject = false;\n\n\t\tappendStringInfo(insertDistributedObjectsCommand,\n\t\t\t\t\t\t \"(%s, ARRAY[\",\n\t\t\t\t\t\t quote_literal_cstr(objectType));\n\n\t\tchar *name = NULL;\n\t\tbool firstInNameLoop = true;\n\t\tforeach_declared_ptr(name, names)\n\t\t{\n\t\t\tif (!firstInNameLoop)\n\t\t\t{\n\t\t\t\tappendStringInfo(insertDistributedObjectsCommand, \", \");\n\t\t\t}\n\t\t\tfirstInNameLoop = false;\n\t\t\tappendStringInfoString(insertDistributedObjectsCommand,\n\t\t\t\t\t\t\t\t   quote_literal_cstr(name));\n\t\t}\n\n\t\tappendStringInfo(insertDistributedObjectsCommand, \"]::text[], ARRAY[\");\n\n\t\tchar *arg;\n\t\tbool firstInArgLoop = true;\n\t\tforeach_declared_ptr(arg, args)\n\t\t{\n\t\t\tif (!firstInArgLoop)\n\t\t\t{\n\t\t\t\tappendStringInfo(insertDistributedObjectsCommand, \", \");\n\t\t\t}\n\t\t\tfirstInArgLoop = false;\n\t\t\tappendStringInfoString(insertDistributedObjectsCommand,\n\t\t\t\t\t\t\t\t   quote_literal_cstr(arg));\n\t\t}\n\n\t\tappendStringInfo(insertDistributedObjectsCommand, \"]::text[], \");\n\n\t\tappendStringInfo(insertDistributedObjectsCommand, \"%d, \",\n\t\t\t\t\t\t distributionArgumentIndex);\n\n\t\tappendStringInfo(insertDistributedObjectsCommand, \"%d, \",\n\t\t\t\t\t\t colocationId);\n\n\t\tappendStringInfo(insertDistributedObjectsCommand, \"%s)\",\n\t\t\t\t\t\t forceDelegation ? \"true\" : \"false\");\n\t}\n\n\tappendStringInfo(insertDistributedObjectsCommand, \") \");\n\n\tappendStringInfo(insertDistributedObjectsCommand,\n\t\t\t\t\t \"SELECT citus_internal.add_object_metadata(\"\n\t\t\t\t\t \"typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) \"\n\t\t\t\t\t \"FROM distributed_object_data;\");\n\n\treturn insertDistributedObjectsCommand->data;\n}\n\n\n/*\n * citus_internal_add_object_metadata is an internal UDF to\n * add a row to pg_dist_object.\n */\nDatum\ncitus_internal_add_object_metadata(PG_FUNCTION_ARGS)\n{\n\tchar *textType = TextDatumGetCString(PG_GETARG_DATUM(0));\n\tArrayType *nameArray = PG_GETARG_ARRAYTYPE_P(1);\n\tArrayType *argsArray = PG_GETARG_ARRAYTYPE_P(2);\n\tint distributionArgumentIndex = PG_GETARG_INT32(3);\n\tint colocationId = PG_GETARG_INT32(4);\n\tbool forceDelegation = PG_GETARG_INT32(5);\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\t/* this UDF is not allowed for executing as a separate command */\n\t\tEnsureCitusInitiatedOperation();\n\n\t\t/*\n\t\t * Ensure given distributionArgumentIndex and colocationId values are\n\t\t * sane. Since we check sanity of object related parameters within\n\t\t * PgGetObjectAddress below, we are not checking them here.\n\t\t */\n\t\tEnsureObjectMetadataIsSane(distributionArgumentIndex, colocationId);\n\t}\n\n\t/*\n\t * We check the acl/ownership while getting the object address. That\n\t * funtion also checks the sanity of given textType, nameArray and\n\t * argsArray parameters\n\t */\n\tObjectAddress objectAddress = PgGetObjectAddress(textType, nameArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t argsArray);\n\n\t/* First, disable propagation off to not to cause infinite propagation */\n\tbool prevDependencyCreationValue = EnableMetadataSync;\n\tSetLocalEnableMetadataSync(false);\n\n\tMarkObjectDistributed(&objectAddress);\n\n\tif (distributionArgumentIndex != INVALID_DISTRIBUTION_ARGUMENT_INDEX ||\n\t\tcolocationId != INVALID_COLOCATION_ID)\n\t{\n\t\tint *distributionArgumentIndexAddress =\n\t\t\tdistributionArgumentIndex == INVALID_DISTRIBUTION_ARGUMENT_INDEX ?\n\t\t\tNULL :\n\t\t\t&distributionArgumentIndex;\n\n\t\tint *colocationIdAddress =\n\t\t\tcolocationId == INVALID_COLOCATION_ID ?\n\t\t\tNULL :\n\t\t\t&colocationId;\n\n\t\tbool *forceDelegationAddress =\n\t\t\tforceDelegation == false ?\n\t\t\tNULL :\n\t\t\t&forceDelegation;\n\t\tUpdateFunctionDistributionInfo(&objectAddress,\n\t\t\t\t\t\t\t\t\t   distributionArgumentIndexAddress,\n\t\t\t\t\t\t\t\t\t   colocationIdAddress,\n\t\t\t\t\t\t\t\t\t   forceDelegationAddress);\n\t}\n\n\tSetLocalEnableMetadataSync(prevDependencyCreationValue);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * EnsureObjectMetadataIsSane checks whether the distribution argument index and\n * colocation id metadata params for distributed object is sane. You can look\n * PgGetObjectAddress to find checks related to object sanity.\n */\nstatic void\nEnsureObjectMetadataIsSane(int distributionArgumentIndex, int colocationId)\n{\n\tif (distributionArgumentIndex != INVALID_DISTRIBUTION_ARGUMENT_INDEX)\n\t{\n\t\tif (distributionArgumentIndex < 0 ||\n\t\t\tdistributionArgumentIndex > FUNC_MAX_ARGS)\n\t\t{\n\t\t\tereport(ERROR, errmsg(\"distribution_argument_index must be between\"\n\t\t\t\t\t\t\t\t  \" 0 and %d\", FUNC_MAX_ARGS));\n\t\t}\n\t}\n\n\tif (colocationId != INVALID_COLOCATION_ID)\n\t{\n\t\tif (colocationId < 0)\n\t\t{\n\t\t\tereport(ERROR, errmsg(\"colocationId must be a positive number\"));\n\t\t}\n\t}\n}\n\n\n/*\n * DistributionCreateCommands generates a commands that can be\n * executed to replicate the metadata for a Citus table.\n */\nchar *\nDistributionCreateCommand(CitusTableCacheEntry *cacheEntry)\n{\n\tStringInfo insertDistributionCommand = makeStringInfo();\n\tOid relationId = cacheEntry->relationId;\n\tchar distributionMethod = cacheEntry->partitionMethod;\n\tchar *qualifiedRelationName =\n\t\tgenerate_qualified_relation_name(relationId);\n\tuint32 colocationId = cacheEntry->colocationId;\n\tchar replicationModel = cacheEntry->replicationModel;\n\tStringInfo tablePartitionKeyNameString = makeStringInfo();\n\n\tif (!HasDistributionKeyCacheEntry(cacheEntry))\n\t{\n\t\tappendStringInfo(tablePartitionKeyNameString, \"NULL\");\n\t}\n\telse\n\t{\n\t\tchar *partitionKeyColumnName =\n\t\t\tColumnToColumnName(relationId, (Node *) cacheEntry->partitionColumn);\n\t\tappendStringInfo(tablePartitionKeyNameString, \"%s\",\n\t\t\t\t\t\t quote_literal_cstr(partitionKeyColumnName));\n\t}\n\n\tappendStringInfo(insertDistributionCommand,\n\t\t\t\t\t \"SELECT citus_internal.add_partition_metadata \"\n\t\t\t\t\t \"(%s::regclass, '%c', %s, %d, '%c')\",\n\t\t\t\t\t quote_literal_cstr(qualifiedRelationName),\n\t\t\t\t\t distributionMethod,\n\t\t\t\t\t tablePartitionKeyNameString->data,\n\t\t\t\t\t colocationId,\n\t\t\t\t\t replicationModel);\n\n\treturn insertDistributionCommand->data;\n}\n\n\n/*\n * DistributionDeleteCommand generates a command that can be executed\n * to drop a distributed table and its metadata on a remote node.\n */\nchar *\nDistributionDeleteCommand(const char *schemaName, const char *tableName)\n{\n\tStringInfo deleteDistributionCommand = makeStringInfo();\n\n\tchar *distributedRelationName = quote_qualified_identifier(schemaName, tableName);\n\n\tappendStringInfo(deleteDistributionCommand,\n\t\t\t\t\t \"SELECT worker_drop_distributed_table(%s)\",\n\t\t\t\t\t quote_literal_cstr(distributedRelationName));\n\n\treturn deleteDistributionCommand->data;\n}\n\n\n/*\n * DistributionDeleteMetadataCommand returns a query to delete pg_dist_partition\n * metadata from a worker node for a given table.\n */\nchar *\nDistributionDeleteMetadataCommand(Oid relationId)\n{\n\tStringInfo deleteCommand = makeStringInfo();\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\n\tappendStringInfo(deleteCommand,\n\t\t\t\t\t \"SELECT citus_internal.delete_partition_metadata(%s)\",\n\t\t\t\t\t quote_literal_cstr(qualifiedRelationName));\n\n\treturn deleteCommand->data;\n}\n\n\n/*\n * TableOwnerResetCommand generates a commands that can be executed\n * to reset the table owner.\n */\nchar *\nTableOwnerResetCommand(Oid relationId)\n{\n\tStringInfo ownerResetCommand = makeStringInfo();\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\tchar *tableOwnerName = TableOwner(relationId);\n\n\tappendStringInfo(ownerResetCommand,\n\t\t\t\t\t \"ALTER TABLE %s OWNER TO %s\",\n\t\t\t\t\t qualifiedRelationName,\n\t\t\t\t\t quote_identifier(tableOwnerName));\n\n\treturn ownerResetCommand->data;\n}\n\n\n/*\n * ShardListInsertCommand generates a single command that can be\n * executed to replicate shard and shard placement metadata for the\n * given shard intervals. The function assumes that each shard has a\n * single placement, and asserts this information.\n */\nList *\nShardListInsertCommand(List *shardIntervalList)\n{\n\tList *commandList = NIL;\n\tint shardCount = list_length(shardIntervalList);\n\n\t/* if there are no shards, return empty list */\n\tif (shardCount == 0)\n\t{\n\t\treturn commandList;\n\t}\n\n\t/* add placements to insertPlacementCommand */\n\tStringInfo insertPlacementCommand = makeStringInfo();\n\tappendStringInfo(insertPlacementCommand,\n\t\t\t\t\t \"WITH placement_data(shardid, \"\n\t\t\t\t\t \"shardlength, groupid, placementid)  AS (VALUES \");\n\n\tShardInterval *shardInterval = NULL;\n\tbool firstPlacementProcessed = false;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\t\tList *shardPlacementList = ActiveShardPlacementList(shardId);\n\n\t\tShardPlacement *placement = NULL;\n\t\tforeach_declared_ptr(placement, shardPlacementList)\n\t\t{\n\t\t\tif (firstPlacementProcessed)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * As long as this is not the first placement of the first shard,\n\t\t\t\t * append the comma.\n\t\t\t\t */\n\t\t\t\tappendStringInfo(insertPlacementCommand, \", \");\n\t\t\t}\n\t\t\tfirstPlacementProcessed = true;\n\n\t\t\tappendStringInfo(insertPlacementCommand,\n\t\t\t\t\t\t\t \"(%ld, %ld, %d, %ld)\",\n\t\t\t\t\t\t\t shardId,\n\t\t\t\t\t\t\t placement->shardLength,\n\t\t\t\t\t\t\t placement->groupId,\n\t\t\t\t\t\t\t placement->placementId);\n\t\t}\n\t}\n\n\tappendStringInfo(insertPlacementCommand, \") \");\n\n\tappendStringInfo(insertPlacementCommand,\n\t\t\t\t\t \"SELECT citus_internal.add_placement_metadata(\"\n\t\t\t\t\t \"shardid, shardlength, groupid, placementid) \"\n\t\t\t\t\t \"FROM placement_data;\");\n\n\t/* now add shards to insertShardCommand */\n\tStringInfo insertShardCommand = makeStringInfo();\n\tappendStringInfo(insertShardCommand,\n\t\t\t\t\t \"WITH shard_data(relationname, shardid, storagetype, \"\n\t\t\t\t\t \"shardminvalue, shardmaxvalue)  AS (VALUES \");\n\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\t\tOid distributedRelationId = shardInterval->relationId;\n\t\tchar *qualifiedRelationName = generate_qualified_relation_name(\n\t\t\tdistributedRelationId);\n\t\tStringInfo minHashToken = makeStringInfo();\n\t\tStringInfo maxHashToken = makeStringInfo();\n\n\t\tif (shardInterval->minValueExists)\n\t\t{\n\t\t\tappendStringInfo(minHashToken, \"'%d'\", DatumGetInt32(\n\t\t\t\t\t\t\t\t shardInterval->minValue));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(minHashToken, \"NULL\");\n\t\t}\n\n\t\tif (shardInterval->maxValueExists)\n\t\t{\n\t\t\tappendStringInfo(maxHashToken, \"'%d'\", DatumGetInt32(\n\t\t\t\t\t\t\t\t shardInterval->maxValue));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(maxHashToken, \"NULL\");\n\t\t}\n\n\t\tappendStringInfo(insertShardCommand,\n\t\t\t\t\t\t \"(%s::regclass, %ld, '%c'::\\\"char\\\", %s, %s)\",\n\t\t\t\t\t\t quote_literal_cstr(qualifiedRelationName),\n\t\t\t\t\t\t shardId,\n\t\t\t\t\t\t shardInterval->storageType,\n\t\t\t\t\t\t minHashToken->data,\n\t\t\t\t\t\t maxHashToken->data);\n\n\t\tif (llast(shardIntervalList) != shardInterval)\n\t\t{\n\t\t\tappendStringInfo(insertShardCommand, \", \");\n\t\t}\n\t}\n\n\tappendStringInfo(insertShardCommand, \") \");\n\n\tappendStringInfo(insertShardCommand,\n\t\t\t\t\t \"SELECT citus_internal.add_shard_metadata(relationname, shardid, \"\n\t\t\t\t\t \"storagetype, shardminvalue, shardmaxvalue) \"\n\t\t\t\t\t \"FROM shard_data;\");\n\n\t/*\n\t * There are no active placements for the table, so do not create the\n\t * command as it'd lead to syntax error.\n\t *\n\t * This is normally not an expected situation, however the current\n\t * implementation of citus_disable_node allows to disable nodes with\n\t * the only active placements. So, for example a single shard/placement\n\t * distributed table on a disabled node might trigger zero placement\n\t * case.\n\t *\n\t * TODO: remove this check once citus_disable_node errors out for\n\t * the above scenario.\n\t */\n\tif (firstPlacementProcessed)\n\t{\n\t\t/* first insert shards, than the placements */\n\t\tcommandList = lappend(commandList, insertShardCommand->data);\n\t\tcommandList = lappend(commandList, insertPlacementCommand->data);\n\t}\n\n\treturn commandList;\n}\n\n\n/*\n * ShardListDeleteCommand generates a command list that can be executed to delete\n * shard and shard placement metadata for the given shard.\n */\nList *\nShardDeleteCommandList(ShardInterval *shardInterval)\n{\n\tuint64 shardId = shardInterval->shardId;\n\n\tStringInfo deleteShardCommand = makeStringInfo();\n\tappendStringInfo(deleteShardCommand,\n\t\t\t\t\t \"SELECT citus_internal.delete_shard_metadata(%ld);\", shardId);\n\n\treturn list_make1(deleteShardCommand->data);\n}\n\n\n/*\n * NodeDeleteCommand generate a command that can be\n * executed to delete the metadata for a worker node.\n */\nchar *\nNodeDeleteCommand(uint32 nodeId)\n{\n\tStringInfo nodeDeleteCommand = makeStringInfo();\n\n\tappendStringInfo(nodeDeleteCommand,\n\t\t\t\t\t \"DELETE FROM pg_dist_node \"\n\t\t\t\t\t \"WHERE nodeid = %u\", nodeId);\n\n\treturn nodeDeleteCommand->data;\n}\n\n\n/*\n * NodeStateUpdateCommand generates a command that can be executed to update\n * isactive column of a node in pg_dist_node table.\n */\nchar *\nNodeStateUpdateCommand(uint32 nodeId, bool isActive)\n{\n\tStringInfo nodeStateUpdateCommand = makeStringInfo();\n\tchar *isActiveString = isActive ? \"TRUE\" : \"FALSE\";\n\n\tappendStringInfo(nodeStateUpdateCommand,\n\t\t\t\t\t \"UPDATE pg_dist_node SET isactive = %s \"\n\t\t\t\t\t \"WHERE nodeid = %u\", isActiveString, nodeId);\n\n\treturn nodeStateUpdateCommand->data;\n}\n\n\n/*\n * ShouldHaveShardsUpdateCommand generates a command that can be executed to\n * update the shouldhaveshards column of a node in pg_dist_node table.\n */\nchar *\nShouldHaveShardsUpdateCommand(uint32 nodeId, bool shouldHaveShards)\n{\n\tStringInfo nodeStateUpdateCommand = makeStringInfo();\n\tchar *shouldHaveShardsString = shouldHaveShards ? \"TRUE\" : \"FALSE\";\n\n\tappendStringInfo(nodeStateUpdateCommand,\n\t\t\t\t\t \"UPDATE pg_catalog.pg_dist_node SET shouldhaveshards = %s \"\n\t\t\t\t\t \"WHERE nodeid = %u\", shouldHaveShardsString, nodeId);\n\n\treturn nodeStateUpdateCommand->data;\n}\n\n\n/*\n * ColocationIdUpdateCommand creates the SQL command to change the colocationId\n * of the table with the given name to the given colocationId in pg_dist_partition\n * table.\n */\nchar *\nColocationIdUpdateCommand(Oid relationId, uint32 colocationId)\n{\n\tStringInfo command = makeStringInfo();\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\tappendStringInfo(command,\n\t\t\t\t\t \"SELECT citus_internal.update_relation_colocation(%s::regclass, %d)\",\n\t\t\t\t\t quote_literal_cstr(qualifiedRelationName), colocationId);\n\n\treturn command->data;\n}\n\n\n/*\n * PlacementUpsertCommand creates a SQL command for upserting a pg_dist_placment\n * entry with the given properties. In the case of a conflict on placementId, the command\n * updates all properties (excluding the placementId) with the given ones.\n */\nchar *\nPlacementUpsertCommand(uint64 shardId, uint64 placementId,\n\t\t\t\t\t   uint64 shardLength, int32 groupId)\n{\n\tStringInfo command = makeStringInfo();\n\n\tappendStringInfo(command, UPSERT_PLACEMENT, shardId, shardLength,\n\t\t\t\t\t groupId, placementId);\n\n\treturn command->data;\n}\n\n\n/*\n * LocalGroupIdUpdateCommand creates the SQL command required to set the local group id\n * of a worker and returns the command in a string.\n */\nchar *\nLocalGroupIdUpdateCommand(int32 groupId)\n{\n\tStringInfo updateCommand = makeStringInfo();\n\n\tappendStringInfo(updateCommand, \"UPDATE pg_dist_local_group SET groupid = %d\",\n\t\t\t\t\t groupId);\n\n\treturn updateCommand->data;\n}\n\n\n/*\n * DDLCommandsForSequence returns the DDL commands needs to be run to create the\n * sequence and alter the owner to the given owner name.\n */\nList *\nDDLCommandsForSequence(Oid sequenceOid, char *ownerName)\n{\n\tList *sequenceDDLList = NIL;\n\tchar *sequenceDef = pg_get_sequencedef_string(sequenceOid);\n\tchar *escapedSequenceDef = quote_literal_cstr(sequenceDef);\n\tStringInfo wrappedSequenceDef = makeStringInfo();\n\tStringInfo sequenceGrantStmt = makeStringInfo();\n\tchar *sequenceName = generate_qualified_relation_name(sequenceOid);\n\tForm_pg_sequence sequenceData = pg_get_sequencedef(sequenceOid);\n\tOid sequenceTypeOid = sequenceData->seqtypid;\n\tchar *typeName = format_type_be(sequenceTypeOid);\n\n\t/* create schema if needed */\n\tappendStringInfo(wrappedSequenceDef,\n\t\t\t\t\t WORKER_APPLY_SEQUENCE_COMMAND,\n\t\t\t\t\t escapedSequenceDef,\n\t\t\t\t\t quote_literal_cstr(typeName));\n\n\tappendStringInfo(sequenceGrantStmt,\n\t\t\t\t\t \"ALTER SEQUENCE %s OWNER TO %s\", sequenceName,\n\t\t\t\t\t quote_identifier(ownerName));\n\n\tsequenceDDLList = lappend(sequenceDDLList, wrappedSequenceDef->data);\n\tsequenceDDLList = lappend(sequenceDDLList, sequenceGrantStmt->data);\n\tsequenceDDLList = list_concat(sequenceDDLList, GrantOnSequenceDDLCommands(\n\t\t\t\t\t\t\t\t\t  sequenceOid));\n\n\treturn sequenceDDLList;\n}\n\n\n/*\n * GetAttributeTypeOid returns the OID of the type of the attribute of\n * provided relationId that has the provided attnum\n */\nOid\nGetAttributeTypeOid(Oid relationId, AttrNumber attnum)\n{\n\tOid resultOid = InvalidOid;\n\n\tScanKeyData key[2];\n\n\t/* Grab an appropriate lock on the pg_attribute relation */\n\tRelation attrel = table_open(AttributeRelationId, AccessShareLock);\n\n\t/* Use the index to scan only system attributes of the target relation */\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_attribute_attrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(relationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_attribute_attnum,\n\t\t\t\tBTLessEqualStrategyNumber, F_INT2LE,\n\t\t\t\tInt16GetDatum(attnum));\n\n\tSysScanDesc scan = systable_beginscan(attrel, AttributeRelidNumIndexId, true, NULL, 2,\n\t\t\t\t\t\t\t\t\t\t  key);\n\n\tHeapTuple attributeTuple;\n\twhile (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))\n\t{\n\t\tForm_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);\n\t\tresultOid = att->atttypid;\n\t}\n\n\tsystable_endscan(scan);\n\ttable_close(attrel, AccessShareLock);\n\n\treturn resultOid;\n}\n\n\n/*\n * GetDependentSequencesWithRelation appends the attnum and id of sequences that\n * have direct (owned sequences) or indirect dependency with the given relationId,\n * to the lists passed as NIL initially.\n * For both cases, we use the intermediate AttrDefault object from pg_depend.\n * If attnum is specified, we only return the sequences related to that\n * attribute of the relationId.\n * See DependencyType for the possible values of depType.\n * We use DEPENDENCY_INTERNAL for sequences created by identity column.\n * DEPENDENCY_AUTO for regular sequences.\n */\nvoid\nGetDependentSequencesWithRelation(Oid relationId, List **seqInfoList,\n\t\t\t\t\t\t\t\t  AttrNumber attnum, char depType)\n{\n\tAssert(*seqInfoList == NIL);\n\n\tList *attrdefResult = NIL;\n\tList *attrdefAttnumResult = NIL;\n\tScanKeyData key[3];\n\tHeapTuple tup;\n\n\tRelation depRel = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_refclassid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(RelationRelationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_refobjid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(relationId));\n\tif (attnum)\n\t{\n\t\tScanKeyInit(&key[2],\n\t\t\t\t\tAnum_pg_depend_refobjsubid,\n\t\t\t\t\tBTEqualStrategyNumber, F_INT4EQ,\n\t\t\t\t\tInt32GetDatum(attnum));\n\t}\n\n\tSysScanDesc scan = systable_beginscan(depRel, DependReferenceIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, attnum ? 3 : 2, key);\n\n\twhile (HeapTupleIsValid(tup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);\n\n\t\tif (deprec->classid == AttrDefaultRelationId &&\n\t\t\tdeprec->objsubid == 0 &&\n\t\t\tdeprec->refobjsubid != 0 &&\n\t\t\tdeprec->deptype == depType)\n\t\t{\n\t\t\t/*\n\t\t\t * We are going to generate corresponding SequenceInfo\n\t\t\t * in the following loop.\n\t\t\t */\n\t\t\tattrdefResult = lappend_oid(attrdefResult, deprec->objid);\n\t\t\tattrdefAttnumResult = lappend_int(attrdefAttnumResult, deprec->refobjsubid);\n\t\t}\n\t\telse if (deprec->deptype == depType &&\n\t\t\t\t deprec->refobjsubid != 0 &&\n\t\t\t\t deprec->classid == RelationRelationId &&\n\t\t\t\t get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)\n\t\t{\n\t\t\tSequenceInfo *seqInfo = (SequenceInfo *) palloc(sizeof(SequenceInfo));\n\n\t\t\tseqInfo->sequenceOid = deprec->objid;\n\t\t\tseqInfo->attributeNumber = deprec->refobjsubid;\n\t\t\tseqInfo->isNextValDefault = false;\n\n\t\t\t*seqInfoList = lappend(*seqInfoList, seqInfo);\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\n\ttable_close(depRel, AccessShareLock);\n\n\tAttrNumber attrdefAttnum = InvalidAttrNumber;\n\tOid attrdefOid = InvalidOid;\n\tforboth_int_oid(attrdefAttnum, attrdefAttnumResult, attrdefOid, attrdefResult)\n\t{\n\t\tList *sequencesFromAttrDef = GetSequencesFromAttrDef(attrdefOid);\n\n\t\t/* to simplify and eliminate cases like \"DEFAULT nextval('..') - nextval('..')\" */\n\t\tif (list_length(sequencesFromAttrDef) > 1)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\"More than one sequence in a column default\"\n\t\t\t\t\t\t\t\t\" is not supported for distribution \"\n\t\t\t\t\t\t\t\t\"or for adding local tables to metadata\")));\n\t\t}\n\n\t\tif (list_length(sequencesFromAttrDef) == 1)\n\t\t{\n\t\t\tSequenceInfo *seqInfo = (SequenceInfo *) palloc(sizeof(SequenceInfo));\n\n\t\t\tseqInfo->sequenceOid = linitial_oid(sequencesFromAttrDef);\n\t\t\tseqInfo->attributeNumber = attrdefAttnum;\n\t\t\tseqInfo->isNextValDefault = true;\n\n\t\t\t*seqInfoList = lappend(*seqInfoList, seqInfo);\n\t\t}\n\t}\n}\n\n\n/*\n * GetDependentDependentRelationsWithSequence returns a list of oids of\n * relations that have have a dependency on the given sequence.\n * There are three types of dependencies:\n * 1. direct auto (owned sequences), created using SERIAL or BIGSERIAL\n * 2. indirect auto (through an AttrDef), created using DEFAULT nextval('..')\n * 3. internal, created using GENERATED ALWAYS AS IDENTITY\n *\n * Depending on the passed deptype, we return the relations that have the\n * given type(s):\n * - DEPENDENCY_AUTO returns both 1 and 2\n * - DEPENDENCY_INTERNAL returns 3\n *\n * The returned list can contain duplicates, as the same relation can have\n * multiple dependencies on the sequence.\n */\nList *\nGetDependentRelationsWithSequence(Oid sequenceOid, char depType)\n{\n\tList *relations = NIL;\n\tScanKeyData key[2];\n\tHeapTuple tup;\n\n\tRelation depRel = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_classid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(RelationRelationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_objid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(sequenceOid));\n\tSysScanDesc scan = systable_beginscan(depRel, DependDependerIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, lengthof(key), key);\n\twhile (HeapTupleIsValid(tup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);\n\n\t\tif (\n\t\t\tdeprec->refclassid == RelationRelationId &&\n\t\t\tdeprec->refobjsubid != 0 &&\n\t\t\tdeprec->deptype == depType)\n\t\t{\n\t\t\trelations = lappend_oid(relations, deprec->refobjid);\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\n\ttable_close(depRel, AccessShareLock);\n\n\tif (depType == DEPENDENCY_AUTO)\n\t{\n\t\tOid attrDefOid;\n\t\tList *attrDefOids = GetAttrDefsFromSequence(sequenceOid);\n\n\t\tforeach_declared_oid(attrDefOid, attrDefOids)\n\t\t{\n\t\t\tObjectAddress columnAddress = GetAttrDefaultColumnAddress(attrDefOid);\n\t\t\trelations = lappend_oid(relations, columnAddress.objectId);\n\t\t}\n\t}\n\n\treturn relations;\n}\n\n\n/*\n * GetSequencesFromAttrDef returns a list of sequence OIDs that have\n * dependency with the given attrdefOid in pg_depend\n */\nList *\nGetSequencesFromAttrDef(Oid attrdefOid)\n{\n\tList *sequencesResult = NIL;\n\tScanKeyData key[2];\n\tHeapTuple tup;\n\n\tRelation depRel = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_classid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(AttrDefaultRelationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_objid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(attrdefOid));\n\n\tSysScanDesc scan = systable_beginscan(depRel, DependDependerIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, 2, key);\n\n\twhile (HeapTupleIsValid(tup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);\n\n\t\tif (deprec->refclassid == RelationRelationId &&\n\t\t\tdeprec->deptype == DEPENDENCY_NORMAL &&\n\t\t\tget_rel_relkind(deprec->refobjid) == RELKIND_SEQUENCE)\n\t\t{\n\t\t\tsequencesResult = lappend_oid(sequencesResult, deprec->refobjid);\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\n\ttable_close(depRel, AccessShareLock);\n\n\treturn sequencesResult;\n}\n\n\n/*\n * GetAttrDefsFromSequence returns a list of attrdef OIDs that have\n * a dependency on the given sequence\n */\nList *\nGetAttrDefsFromSequence(Oid seqOid)\n{\n\tList *attrDefsResult = NIL;\n\tScanKeyData key[2];\n\tHeapTuple tup;\n\n\tRelation depRel = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_refclassid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(RelationRelationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_refobjid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(seqOid));\n\tSysScanDesc scan = systable_beginscan(depRel, DependReferenceIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, lengthof(key), key);\n\twhile (HeapTupleIsValid(tup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);\n\n\t\tif (deprec->classid == AttrDefaultRelationId &&\n\t\t\tdeprec->deptype == DEPENDENCY_NORMAL)\n\t\t{\n\t\t\tattrDefsResult = lappend_oid(attrDefsResult, deprec->objid);\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\n\ttable_close(depRel, AccessShareLock);\n\n\treturn attrDefsResult;\n}\n\n\n/*\n * GetDependentFunctionsWithRelation returns the dependent functions for the\n * given relation id.\n */\nList *\nGetDependentFunctionsWithRelation(Oid relationId)\n{\n\tList *referencingObjects = NIL;\n\tList *functionOids = NIL;\n\tScanKeyData key[2];\n\tHeapTuple tup;\n\n\tRelation depRel = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_refclassid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(RelationRelationId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_refobjid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(relationId));\n\n\tSysScanDesc scan = systable_beginscan(depRel, DependReferenceIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, 2, key);\n\n\twhile (HeapTupleIsValid(tup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);\n\n\t\t/*\n\t\t * objsubid is nonzero only for table columns and zero for anything else.\n\t\t * Since we are trying to find a dependency from the column of a table to\n\t\t * function we've added deprec->refobjsubid != 0 check.\n\t\t *\n\t\t * We are following DEPENDENCY_AUTO for dependencies via column and\n\t\t * DEPENDENCY_NORMAL anything else. Since only procedure dependencies\n\t\t * for those dependencies will be obtained in GetFunctionDependenciesForObjects\n\t\t * following both dependency types are not harmful.\n\t\t */\n\t\tif ((deprec->refobjsubid != 0 && deprec->deptype == DEPENDENCY_AUTO) ||\n\t\t\tdeprec->deptype == DEPENDENCY_NORMAL)\n\t\t{\n\t\t\tObjectAddress *refAddress = palloc(sizeof(ObjectAddress));\n\t\t\tObjectAddressSubSet(*refAddress, deprec->classid,\n\t\t\t\t\t\t\t\tdeprec->objid,\n\t\t\t\t\t\t\t\tdeprec->objsubid);\n\t\t\treferencingObjects = lappend(referencingObjects, refAddress);\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\n\ttable_close(depRel, AccessShareLock);\n\n\tObjectAddress *referencingObject = NULL;\n\tforeach_declared_ptr(referencingObject, referencingObjects)\n\t{\n\t\tfunctionOids = list_concat(functionOids,\n\t\t\t\t\t\t\t\t   GetFunctionDependenciesForObjects(referencingObject));\n\t}\n\n\treturn functionOids;\n}\n\n\n/*\n * GetFunctionDependenciesForObjects returns a list of function OIDs that have\n * dependency with the given object\n */\nstatic List *\nGetFunctionDependenciesForObjects(ObjectAddress *objectAddress)\n{\n\tList *functionOids = NIL;\n\tScanKeyData key[3];\n\tHeapTuple tup;\n\n\tRelation depRel = table_open(DependRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0],\n\t\t\t\tAnum_pg_depend_classid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(objectAddress->classId));\n\tScanKeyInit(&key[1],\n\t\t\t\tAnum_pg_depend_objid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(objectAddress->objectId));\n\tScanKeyInit(&key[2],\n\t\t\t\tAnum_pg_depend_objsubid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ,\n\t\t\t\tInt32GetDatum(objectAddress->objectSubId));\n\n\tSysScanDesc scan = systable_beginscan(depRel, DependDependerIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, 3, key);\n\n\twhile (HeapTupleIsValid(tup = systable_getnext(scan)))\n\t{\n\t\tForm_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);\n\n\t\tif (deprec->refclassid == ProcedureRelationId)\n\t\t{\n\t\t\tfunctionOids = lappend_oid(functionOids, deprec->refobjid);\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\n\ttable_close(depRel, AccessShareLock);\n\n\treturn functionOids;\n}\n\n\n/*\n * SequenceDependencyCommandList generates commands to record the dependency\n * of sequences on tables on the worker. This dependency does not exist by\n * default since the sequences and table are created separately, but it is\n * necessary to ensure that the sequence is dropped when the table is\n * dropped.\n */\nList *\nSequenceDependencyCommandList(Oid relationId)\n{\n\tList *sequenceCommandList = NIL;\n\tList *columnNameList = NIL;\n\tList *sequenceIdList = NIL;\n\n\tExtractDefaultColumnsAndOwnedSequences(relationId, &columnNameList, &sequenceIdList);\n\n\tchar *columnName = NULL;\n\tOid sequenceId = InvalidOid;\n\tforboth_ptr_oid(columnName, columnNameList, sequenceId, sequenceIdList)\n\t{\n\t\tif (!OidIsValid(sequenceId))\n\t\t{\n\t\t\t/*\n\t\t\t * ExtractDefaultColumnsAndOwnedSequences returns entries for all columns,\n\t\t\t * but with 0 sequence ID unless there is default nextval(..).\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tchar *sequenceDependencyCommand =\n\t\t\tCreateSequenceDependencyCommand(relationId, sequenceId, columnName);\n\n\t\tsequenceCommandList = lappend(sequenceCommandList,\n\t\t\t\t\t\t\t\t\t  makeTableDDLCommandString(\n\t\t\t\t\t\t\t\t\t\t  sequenceDependencyCommand));\n\t}\n\n\treturn sequenceCommandList;\n}\n\n\n/*\n * IdentitySequenceDependencyCommandList generate a command to execute\n * a UDF (WORKER_ADJUST_IDENTITY_COLUMN_SEQ_RANGES) on workers to modify the identity\n * columns min/max values to produce unique values on workers.\n */\nList *\nIdentitySequenceDependencyCommandList(Oid targetRelationId)\n{\n\tList *commandList = NIL;\n\n\tRelation relation = relation_open(targetRelationId, AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\n\tbool tableHasIdentityColumn = false;\n\tfor (int attributeIndex = 0; attributeIndex < tupleDescriptor->natts;\n\t\t attributeIndex++)\n\t{\n\t\tForm_pg_attribute attributeForm = TupleDescAttr(tupleDescriptor, attributeIndex);\n\n\t\tif (attributeForm->attidentity)\n\t\t{\n\t\t\ttableHasIdentityColumn = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\trelation_close(relation, NoLock);\n\n\tif (tableHasIdentityColumn)\n\t{\n\t\tStringInfo stringInfo = makeStringInfo();\n\t\tchar *tableName = generate_qualified_relation_name(targetRelationId);\n\n\t\tappendStringInfo(stringInfo,\n\t\t\t\t\t\t WORKER_ADJUST_IDENTITY_COLUMN_SEQ_RANGES,\n\t\t\t\t\t\t quote_literal_cstr(tableName));\n\n\n\t\tcommandList = lappend(commandList,\n\t\t\t\t\t\t\t  makeTableDDLCommandString(\n\t\t\t\t\t\t\t\t  stringInfo->data));\n\t}\n\n\treturn commandList;\n}\n\n\n/*\n * CreateSequenceDependencyCommand generates a query string for calling\n * worker_record_sequence_dependency on the worker to recreate a sequence->table\n * dependency.\n */\nstatic char *\nCreateSequenceDependencyCommand(Oid relationId, Oid sequenceId, char *columnName)\n{\n\tchar *relationName = generate_qualified_relation_name(relationId);\n\tchar *sequenceName = generate_qualified_relation_name(sequenceId);\n\n\tStringInfo sequenceDependencyCommand = makeStringInfo();\n\n\tappendStringInfo(sequenceDependencyCommand,\n\t\t\t\t\t \"SELECT pg_catalog.worker_record_sequence_dependency\"\n\t\t\t\t\t \"(%s::regclass,%s::regclass,%s)\",\n\t\t\t\t\t quote_literal_cstr(sequenceName),\n\t\t\t\t\t quote_literal_cstr(relationName),\n\t\t\t\t\t quote_literal_cstr(columnName));\n\n\treturn sequenceDependencyCommand->data;\n}\n\n\n/*\n * worker_record_sequence_dependency records the fact that the sequence depends on\n * the table in pg_depend, such that it will be automatically dropped.\n */\nDatum\nworker_record_sequence_dependency(PG_FUNCTION_ARGS)\n{\n\tOid sequenceOid = PG_GETARG_OID(0);\n\tOid relationOid = PG_GETARG_OID(1);\n\tName columnName = PG_GETARG_NAME(2);\n\tconst char *columnNameStr = NameStr(*columnName);\n\n\t/* lookup column definition */\n\tHeapTuple columnTuple = SearchSysCacheAttName(relationOid, columnNameStr);\n\tif (!HeapTupleIsValid(columnTuple))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN),\n\t\t\t\t\t\terrmsg(\"column \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t   columnNameStr)));\n\t}\n\n\tForm_pg_attribute columnForm = (Form_pg_attribute) GETSTRUCT(columnTuple);\n\tif (columnForm->attnum <= 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot create dependency on system column \\\"%s\\\"\",\n\t\t\t\t\t\t\t   columnNameStr)));\n\t}\n\n\tObjectAddress sequenceAddr = {\n\t\t.classId = RelationRelationId,\n\t\t.objectId = sequenceOid,\n\t\t.objectSubId = 0\n\t};\n\tObjectAddress relationAddr = {\n\t\t.classId = RelationRelationId,\n\t\t.objectId = relationOid,\n\t\t.objectSubId = columnForm->attnum\n\t};\n\n\n\tEnsureTableOwner(sequenceOid);\n\tEnsureTableOwner(relationOid);\n\n\t/* dependency from sequence to table */\n\trecordDependencyOn(&sequenceAddr, &relationAddr, DEPENDENCY_AUTO);\n\n\tReleaseSysCache(columnTuple);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * CreateSchemaDDLCommand returns a \"CREATE SCHEMA...\" SQL string for creating the given\n * schema if not exists and with proper authorization.\n */\nchar *\nCreateSchemaDDLCommand(Oid schemaId)\n{\n\tchar *schemaName = get_namespace_name(schemaId);\n\n\tStringInfo schemaNameDef = makeStringInfo();\n\tconst char *quotedSchemaName = quote_identifier(schemaName);\n\tconst char *ownerName = quote_identifier(SchemaOwnerName(schemaId));\n\tappendStringInfo(schemaNameDef, CREATE_SCHEMA_COMMAND, quotedSchemaName, ownerName);\n\n\treturn schemaNameDef->data;\n}\n\n\n/*\n * GrantOnSchemaDDLCommands creates a list of ddl command for replicating the permissions\n * of roles on schemas.\n */\nList *\nGrantOnSchemaDDLCommands(Oid schemaOid)\n{\n\tHeapTuple schemaTuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(schemaOid));\n\tbool isNull = true;\n\tDatum aclDatum = SysCacheGetAttr(NAMESPACEOID, schemaTuple, Anum_pg_namespace_nspacl,\n\t\t\t\t\t\t\t\t\t &isNull);\n\tif (isNull)\n\t{\n\t\tReleaseSysCache(schemaTuple);\n\t\treturn NIL;\n\t}\n\tAcl *acl = DatumGetAclPCopy(aclDatum);\n\tAclItem *aclDat = ACL_DAT(acl);\n\tint aclNum = ACL_NUM(acl);\n\tList *commands = NIL;\n\n\tReleaseSysCache(schemaTuple);\n\n\tfor (int i = 0; i < aclNum; i++)\n\t{\n\t\tcommands = list_concat(commands,\n\t\t\t\t\t\t\t   GenerateGrantOnSchemaQueriesFromAclItem(\n\t\t\t\t\t\t\t\t   schemaOid,\n\t\t\t\t\t\t\t\t   &aclDat[i]));\n\t}\n\n\treturn commands;\n}\n\n\n/*\n * GenerateGrantOnSchemaQueryFromACLItem generates a query string for replicating a users permissions\n * on a schema.\n */\nList *\nGenerateGrantOnSchemaQueriesFromAclItem(Oid schemaOid, AclItem *aclItem)\n{\n\tAclMode permissions = ACLITEM_GET_PRIVS(*aclItem) & ACL_ALL_RIGHTS_SCHEMA;\n\tAclMode grants = ACLITEM_GET_GOPTIONS(*aclItem) & ACL_ALL_RIGHTS_SCHEMA;\n\n\t/*\n\t * seems unlikely but we check if there is a grant option in the list without the actual permission\n\t */\n\tAssert(!(grants & ACL_USAGE) || (permissions & ACL_USAGE));\n\tAssert(!(grants & ACL_CREATE) || (permissions & ACL_CREATE));\n\tOid granteeOid = aclItem->ai_grantee;\n\tList *queries = NIL;\n\n\tqueries = lappend(queries, GenerateSetRoleQuery(aclItem->ai_grantor));\n\n\tif (permissions & ACL_USAGE)\n\t{\n\t\tchar *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights(\n\t\t\t\t\t\t\t\t\t\t  OBJECT_SCHEMA, granteeOid, schemaOid, \"USAGE\",\n\t\t\t\t\t\t\t\t\t\t  grants & ACL_USAGE));\n\t\tqueries = lappend(queries, query);\n\t}\n\tif (permissions & ACL_CREATE)\n\t{\n\t\tchar *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights(\n\t\t\t\t\t\t\t\t\t\t  OBJECT_SCHEMA, granteeOid, schemaOid, \"CREATE\",\n\t\t\t\t\t\t\t\t\t\t  grants & ACL_CREATE));\n\t\tqueries = lappend(queries, query);\n\t}\n\n\tqueries = lappend(queries, \"RESET ROLE\");\n\n\treturn queries;\n}\n\n\n/*\n * GrantOnDatabaseDDLCommands creates a list of ddl command for replicating the permissions\n * of roles on databases.\n */\nList *\nGrantOnDatabaseDDLCommands(Oid databaseOid)\n{\n\tHeapTuple databaseTuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(databaseOid));\n\tbool isNull = true;\n\tDatum aclDatum = SysCacheGetAttr(DATABASEOID, databaseTuple, Anum_pg_database_datacl,\n\t\t\t\t\t\t\t\t\t &isNull);\n\tif (isNull)\n\t{\n\t\tReleaseSysCache(databaseTuple);\n\t\treturn NIL;\n\t}\n\tAcl *acl = DatumGetAclPCopy(aclDatum);\n\tAclItem *aclDat = ACL_DAT(acl);\n\tint aclNum = ACL_NUM(acl);\n\tList *commands = NIL;\n\n\tReleaseSysCache(databaseTuple);\n\n\tfor (int i = 0; i < aclNum; i++)\n\t{\n\t\tcommands = list_concat(commands,\n\t\t\t\t\t\t\t   GenerateGrantOnDatabaseFromAclItem(\n\t\t\t\t\t\t\t\t   databaseOid, &aclDat[i]));\n\t}\n\n\treturn commands;\n}\n\n\n/*\n * GenerateGrantOnDatabaseFromAclItem generates a query string for replicating a users permissions\n * on a database.\n */\nList *\nGenerateGrantOnDatabaseFromAclItem(Oid databaseOid, AclItem *aclItem)\n{\n\tAclMode permissions = ACLITEM_GET_PRIVS(*aclItem) & ACL_ALL_RIGHTS_DATABASE;\n\tAclMode grants = ACLITEM_GET_GOPTIONS(*aclItem) & ACL_ALL_RIGHTS_DATABASE;\n\n\t/*\n\t * seems unlikely but we check if there is a grant option in the list without the actual permission\n\t */\n\tAssert(!(grants & ACL_CONNECT) || (permissions & ACL_CONNECT));\n\tAssert(!(grants & ACL_CREATE) || (permissions & ACL_CREATE));\n\tAssert(!(grants & ACL_CREATE_TEMP) || (permissions & ACL_CREATE_TEMP));\n\tOid granteeOid = aclItem->ai_grantee;\n\tList *queries = NIL;\n\n\tqueries = lappend(queries, GenerateSetRoleQuery(aclItem->ai_grantor));\n\n\tif (permissions & ACL_CONNECT)\n\t{\n\t\tchar *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights(\n\t\t\t\t\t\t\t\t\t\t  OBJECT_DATABASE, granteeOid, databaseOid,\n\t\t\t\t\t\t\t\t\t\t  \"CONNECT\",\n\t\t\t\t\t\t\t\t\t\t  grants & ACL_CONNECT));\n\t\tqueries = lappend(queries, query);\n\t}\n\tif (permissions & ACL_CREATE)\n\t{\n\t\tchar *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights(\n\t\t\t\t\t\t\t\t\t\t  OBJECT_DATABASE, granteeOid, databaseOid,\n\t\t\t\t\t\t\t\t\t\t  \"CREATE\",\n\t\t\t\t\t\t\t\t\t\t  grants & ACL_CREATE));\n\t\tqueries = lappend(queries, query);\n\t}\n\tif (permissions & ACL_CREATE_TEMP)\n\t{\n\t\tchar *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights(\n\t\t\t\t\t\t\t\t\t\t  OBJECT_DATABASE, granteeOid, databaseOid,\n\t\t\t\t\t\t\t\t\t\t  \"TEMPORARY\",\n\t\t\t\t\t\t\t\t\t\t  grants & ACL_CREATE_TEMP));\n\t\tqueries = lappend(queries, query);\n\t}\n\n\tqueries = lappend(queries, \"RESET ROLE\");\n\n\treturn queries;\n}\n\n\n/*\n * GenerateGrantStmtForRights is the function for creating GrantStmt's for all\n * types of objects that are supported. It takes parameters to fill a GrantStmt's\n * fields and returns the GrantStmt.\n * The field `objects` of GrantStmt doesn't have a common structure for all types.\n * Make sure you have added your object type to GetObjectsForGrantStmt.\n */\nstatic GrantStmt *\nGenerateGrantStmtForRights(ObjectType objectType,\n\t\t\t\t\t\t   Oid roleOid,\n\t\t\t\t\t\t   Oid objectId,\n\t\t\t\t\t\t   char *permission,\n\t\t\t\t\t\t   bool withGrantOption)\n{\n\tGrantStmt *stmt = makeNode(GrantStmt);\n\tstmt->is_grant = true;\n\tstmt->targtype = ACL_TARGET_OBJECT;\n\tstmt->objtype = objectType;\n\tstmt->objects = GetObjectsForGrantStmt(objectType, objectId);\n\tstmt->privileges = list_make1(GetAccessPrivObjectForGrantStmt(permission));\n\tstmt->grantees = list_make1(GetRoleSpecObjectForUser(roleOid));\n\tstmt->grant_option = withGrantOption;\n\n\treturn stmt;\n}\n\n\n/*\n * GetObjectsForGrantStmt takes an object type and object id and returns the 'objects'\n * field to be used when creating GrantStmt. We have only one object here (the one with\n * the oid = objectId) but we pass it into the GrantStmt as a list with one element,\n * as GrantStmt->objects field is actually a list.\n */\nstatic List *\nGetObjectsForGrantStmt(ObjectType objectType, Oid objectId)\n{\n\tswitch (objectType)\n\t{\n\t\t/* supported object types */\n\t\tcase OBJECT_SCHEMA:\n\t\t{\n\t\t\treturn list_make1(makeString(get_namespace_name(objectId)));\n\t\t}\n\n\t\t/* enterprise supported object types */\n\t\tcase OBJECT_FUNCTION:\n\t\tcase OBJECT_AGGREGATE:\n\t\tcase OBJECT_PROCEDURE:\n\t\t{\n\t\t\tObjectWithArgs *owa = ObjectWithArgsFromOid(objectId);\n\t\t\treturn list_make1(owa);\n\t\t}\n\n\t\tcase OBJECT_FDW:\n\t\t{\n\t\t\tForeignDataWrapper *fdw = GetForeignDataWrapper(objectId);\n\t\t\treturn list_make1(makeString(fdw->fdwname));\n\t\t}\n\n\t\tcase OBJECT_FOREIGN_SERVER:\n\t\t{\n\t\t\tForeignServer *server = GetForeignServer(objectId);\n\t\t\treturn list_make1(makeString(server->servername));\n\t\t}\n\n\t\tcase OBJECT_SEQUENCE:\n\t\t{\n\t\t\tOid namespaceOid = get_rel_namespace(objectId);\n\t\t\tRangeVar *sequence = makeRangeVar(get_namespace_name(namespaceOid),\n\t\t\t\t\t\t\t\t\t\t\t  get_rel_name(objectId), -1);\n\t\t\treturn list_make1(sequence);\n\t\t}\n\n\t\tcase OBJECT_DATABASE:\n\t\t{\n\t\t\treturn list_make1(makeString(get_database_name(objectId)));\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"unsupported object type for GRANT\");\n\t\t}\n\t}\n\n\treturn NIL;\n}\n\n\n/*\n * GrantOnFunctionDDLCommands creates a list of ddl command for replicating the permissions\n * of roles on distributed functions.\n */\nList *\nGrantOnFunctionDDLCommands(Oid functionOid)\n{\n\tHeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));\n\n\tbool isNull = true;\n\tDatum aclDatum = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proacl,\n\t\t\t\t\t\t\t\t\t &isNull);\n\tif (isNull)\n\t{\n\t\tReleaseSysCache(proctup);\n\t\treturn NIL;\n\t}\n\n\tAcl *acl = DatumGetAclPCopy(aclDatum);\n\tAclItem *aclDat = ACL_DAT(acl);\n\tint aclNum = ACL_NUM(acl);\n\tList *commands = NIL;\n\n\tReleaseSysCache(proctup);\n\n\tfor (int i = 0; i < aclNum; i++)\n\t{\n\t\tcommands = list_concat(commands,\n\t\t\t\t\t\t\t   GenerateGrantOnFunctionQueriesFromAclItem(\n\t\t\t\t\t\t\t\t   functionOid,\n\t\t\t\t\t\t\t\t   &aclDat[i]));\n\t}\n\n\treturn commands;\n}\n\n\n/*\n * GrantOnForeignServerDDLCommands creates a list of ddl command for replicating the\n * permissions of roles on distributed foreign servers.\n */\nList *\nGrantOnForeignServerDDLCommands(Oid serverId)\n{\n\tHeapTuple servertup = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverId));\n\n\tbool isNull = true;\n\tDatum aclDatum = SysCacheGetAttr(FOREIGNSERVEROID, servertup,\n\t\t\t\t\t\t\t\t\t Anum_pg_foreign_server_srvacl, &isNull);\n\tif (isNull)\n\t{\n\t\tReleaseSysCache(servertup);\n\t\treturn NIL;\n\t}\n\n\tAcl *aclEntry = DatumGetAclPCopy(aclDatum);\n\tAclItem *privileges = ACL_DAT(aclEntry);\n\tint numberOfPrivsGranted = ACL_NUM(aclEntry);\n\tList *commands = NIL;\n\n\tReleaseSysCache(servertup);\n\n\tfor (int i = 0; i < numberOfPrivsGranted; i++)\n\t{\n\t\tcommands = list_concat(commands,\n\t\t\t\t\t\t\t   GenerateGrantOnForeignServerQueriesFromAclItem(\n\t\t\t\t\t\t\t\t   serverId,\n\t\t\t\t\t\t\t\t   &privileges[i]));\n\t}\n\n\treturn commands;\n}\n\n\n/*\n * GenerateGrantOnForeignServerQueriesFromAclItem generates a query string for\n * replicating a users permissions on a foreign server.\n */\nList *\nGenerateGrantOnForeignServerQueriesFromAclItem(Oid serverId, AclItem *aclItem)\n{\n\t/* privileges to be granted */\n\tAclMode permissions = ACLITEM_GET_PRIVS(*aclItem) & ACL_ALL_RIGHTS_FOREIGN_SERVER;\n\n\t/* WITH GRANT OPTION clause */\n\tAclMode grants = ACLITEM_GET_GOPTIONS(*aclItem) & ACL_ALL_RIGHTS_FOREIGN_SERVER;\n\n\t/*\n\t * seems unlikely but we check if there is a grant option in the list without the actual permission\n\t */\n\tAssert(!(grants & ACL_USAGE) || (permissions & ACL_USAGE));\n\n\tOid granteeOid = aclItem->ai_grantee;\n\tList *queries = NIL;\n\n\t/* switch to the role which had granted acl */\n\tqueries = lappend(queries, GenerateSetRoleQuery(aclItem->ai_grantor));\n\n\t/* generate the GRANT stmt that will be executed by the grantor role */\n\tif (permissions & ACL_USAGE)\n\t{\n\t\tchar *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights(\n\t\t\t\t\t\t\t\t\t\t  OBJECT_FOREIGN_SERVER, granteeOid, serverId,\n\t\t\t\t\t\t\t\t\t\t  \"USAGE\", grants & ACL_USAGE));\n\t\tqueries = lappend(queries, query);\n\t}\n\n\t/* reset the role back */\n\tqueries = lappend(queries, \"RESET ROLE\");\n\n\treturn queries;\n}\n\n\n/*\n * GenerateGrantOnFunctionQueryFromACLItem generates a query string for replicating a users permissions\n * on a distributed function.\n */\nList *\nGenerateGrantOnFunctionQueriesFromAclItem(Oid functionOid, AclItem *aclItem)\n{\n\tAclMode permissions = ACLITEM_GET_PRIVS(*aclItem) & ACL_ALL_RIGHTS_FUNCTION;\n\tAclMode grants = ACLITEM_GET_GOPTIONS(*aclItem) & ACL_ALL_RIGHTS_FUNCTION;\n\n\t/*\n\t * seems unlikely but we check if there is a grant option in the list without the actual permission\n\t */\n\tAssert(!(grants & ACL_EXECUTE) || (permissions & ACL_EXECUTE));\n\tOid granteeOid = aclItem->ai_grantee;\n\tList *queries = NIL;\n\n\tqueries = lappend(queries, GenerateSetRoleQuery(aclItem->ai_grantor));\n\n\tif (permissions & ACL_EXECUTE)\n\t{\n\t\tchar prokind = get_func_prokind(functionOid);\n\t\tObjectType objectType;\n\n\t\tif (prokind == PROKIND_FUNCTION)\n\t\t{\n\t\t\tobjectType = OBJECT_FUNCTION;\n\t\t}\n\t\telse if (prokind == PROKIND_PROCEDURE)\n\t\t{\n\t\t\tobjectType = OBJECT_PROCEDURE;\n\t\t}\n\t\telse if (prokind == PROKIND_AGGREGATE)\n\t\t{\n\t\t\tobjectType = OBJECT_AGGREGATE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unsupported prokind\"),\n\t\t\t\t\t\t\terrdetail(\"GRANT commands on procedures are propagated only \"\n\t\t\t\t\t\t\t\t\t  \"for procedures, functions, and aggregates.\")));\n\t\t}\n\n\t\tchar *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights(\n\t\t\t\t\t\t\t\t\t\t  objectType, granteeOid, functionOid, \"EXECUTE\",\n\t\t\t\t\t\t\t\t\t\t  grants & ACL_EXECUTE));\n\t\tqueries = lappend(queries, query);\n\t}\n\n\tqueries = lappend(queries, \"RESET ROLE\");\n\n\treturn queries;\n}\n\n\n/*\n * GenerateGrantOnFDWQueriesFromAclItem generates a query string for\n * replicating a users permissions on a foreign data wrapper.\n */\nList *\nGenerateGrantOnFDWQueriesFromAclItem(Oid FDWId, AclItem *aclItem)\n{\n\t/* privileges to be granted */\n\tAclMode permissions = ACLITEM_GET_PRIVS(*aclItem) & ACL_ALL_RIGHTS_FDW;\n\n\t/* WITH GRANT OPTION clause */\n\tAclMode grants = ACLITEM_GET_GOPTIONS(*aclItem) & ACL_ALL_RIGHTS_FDW;\n\n\t/*\n\t * seems unlikely but we check if there is a grant option in the list without the actual permission\n\t */\n\tAssert(!(grants & ACL_USAGE) || (permissions & ACL_USAGE));\n\n\tOid granteeOid = aclItem->ai_grantee;\n\tList *queries = NIL;\n\n\t/* switch to the role which had granted acl */\n\tqueries = lappend(queries, GenerateSetRoleQuery(aclItem->ai_grantor));\n\n\t/* generate the GRANT stmt that will be executed by the grantor role */\n\tif (permissions & ACL_USAGE)\n\t{\n\t\tchar *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights(\n\t\t\t\t\t\t\t\t\t\t  OBJECT_FDW, granteeOid, FDWId, \"USAGE\",\n\t\t\t\t\t\t\t\t\t\t  grants & ACL_USAGE));\n\t\tqueries = lappend(queries, query);\n\t}\n\n\t/* reset the role back */\n\tqueries = lappend(queries, \"RESET ROLE\");\n\n\treturn queries;\n}\n\n\n/*\n * GetAccessPrivObjectForGrantStmt creates an AccessPriv object for the given permission.\n * It will be used when creating GrantStmt objects.\n */\nstatic AccessPriv *\nGetAccessPrivObjectForGrantStmt(char *permission)\n{\n\tAccessPriv *accessPriv = makeNode(AccessPriv);\n\taccessPriv->priv_name = pstrdup(permission);\n\taccessPriv->cols = NULL;\n\n\treturn accessPriv;\n}\n\n\n/*\n * GrantOnSequenceDDLCommands creates a list of ddl command for replicating the permissions\n * of roles on distributed sequences.\n */\nstatic List *\nGrantOnSequenceDDLCommands(Oid sequenceOid)\n{\n\tHeapTuple seqtup = SearchSysCache1(RELOID, ObjectIdGetDatum(sequenceOid));\n\tbool isNull = false;\n\tDatum aclDatum = SysCacheGetAttr(RELOID, seqtup, Anum_pg_class_relacl,\n\t\t\t\t\t\t\t\t\t &isNull);\n\tif (isNull)\n\t{\n\t\tReleaseSysCache(seqtup);\n\t\treturn NIL;\n\t}\n\n\tAcl *acl = DatumGetAclPCopy(aclDatum);\n\tAclItem *aclDat = ACL_DAT(acl);\n\tint aclNum = ACL_NUM(acl);\n\tList *commands = NIL;\n\n\tReleaseSysCache(seqtup);\n\n\tfor (int i = 0; i < aclNum; i++)\n\t{\n\t\tcommands = list_concat(commands,\n\t\t\t\t\t\t\t   GenerateGrantOnSequenceQueriesFromAclItem(\n\t\t\t\t\t\t\t\t   sequenceOid,\n\t\t\t\t\t\t\t\t   &aclDat[i]));\n\t}\n\n\treturn commands;\n}\n\n\n/*\n * GenerateGrantOnSequenceQueriesFromAclItem generates a query string for replicating a users permissions\n * on a distributed sequence.\n */\nstatic List *\nGenerateGrantOnSequenceQueriesFromAclItem(Oid sequenceOid, AclItem *aclItem)\n{\n\tAclMode permissions = ACLITEM_GET_PRIVS(*aclItem) & ACL_ALL_RIGHTS_SEQUENCE;\n\tAclMode grants = ACLITEM_GET_GOPTIONS(*aclItem) & ACL_ALL_RIGHTS_SEQUENCE;\n\n\t/*\n\t * seems unlikely but we check if there is a grant option in the list without the actual permission\n\t */\n\tAssert(!(grants & ACL_USAGE) || (permissions & ACL_USAGE));\n\tAssert(!(grants & ACL_SELECT) || (permissions & ACL_SELECT));\n\tAssert(!(grants & ACL_UPDATE) || (permissions & ACL_UPDATE));\n\n\tOid granteeOid = aclItem->ai_grantee;\n\tList *queries = NIL;\n\tqueries = lappend(queries, GenerateSetRoleQuery(aclItem->ai_grantor));\n\n\tif (permissions & ACL_USAGE)\n\t{\n\t\tchar *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights(\n\t\t\t\t\t\t\t\t\t\t  OBJECT_SEQUENCE, granteeOid, sequenceOid,\n\t\t\t\t\t\t\t\t\t\t  \"USAGE\", grants & ACL_USAGE));\n\t\tqueries = lappend(queries, query);\n\t}\n\n\tif (permissions & ACL_SELECT)\n\t{\n\t\tchar *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights(\n\t\t\t\t\t\t\t\t\t\t  OBJECT_SEQUENCE, granteeOid, sequenceOid,\n\t\t\t\t\t\t\t\t\t\t  \"SELECT\", grants & ACL_SELECT));\n\t\tqueries = lappend(queries, query);\n\t}\n\n\tif (permissions & ACL_UPDATE)\n\t{\n\t\tchar *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights(\n\t\t\t\t\t\t\t\t\t\t  OBJECT_SEQUENCE, granteeOid, sequenceOid,\n\t\t\t\t\t\t\t\t\t\t  \"UPDATE\", grants & ACL_UPDATE));\n\t\tqueries = lappend(queries, query);\n\t}\n\n\tqueries = lappend(queries, \"RESET ROLE\");\n\n\treturn queries;\n}\n\n\n/*\n * SetLocalEnableMetadataSync sets the enable_metadata_sync locally\n */\nvoid\nSetLocalEnableMetadataSync(bool state)\n{\n\tset_config_option(\"citus.enable_metadata_sync\", state == true ? \"on\" : \"off\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n}\n\n\nstatic char *\nGenerateSetRoleQuery(Oid roleOid)\n{\n\tStringInfo buf = makeStringInfo();\n\tappendStringInfo(buf, \"SET ROLE %s\", quote_identifier(GetUserNameFromId(roleOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse)));\n\treturn buf->data;\n}\n\n\n/*\n * TruncateTriggerCreateCommand creates a SQL query calling worker_create_truncate_trigger\n * function, which creates the truncate trigger on the worker.\n */\nTableDDLCommand *\nTruncateTriggerCreateCommand(Oid relationId)\n{\n\tStringInfo triggerCreateCommand = makeStringInfo();\n\tchar *tableName = generate_qualified_relation_name(relationId);\n\n\tappendStringInfo(triggerCreateCommand,\n\t\t\t\t\t \"SELECT worker_create_truncate_trigger(%s)\",\n\t\t\t\t\t quote_literal_cstr(tableName));\n\n\tTableDDLCommand *triggerDDLCommand = makeTableDDLCommandString(\n\t\ttriggerCreateCommand->data);\n\n\treturn triggerDDLCommand;\n}\n\n\n/*\n * SchemaOwnerName returns the name of the owner of the specified schema.\n */\nstatic char *\nSchemaOwnerName(Oid objectId)\n{\n\tOid ownerId = InvalidOid;\n\n\tHeapTuple tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(objectId));\n\tif (HeapTupleIsValid(tuple))\n\t{\n\t\townerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;\n\t}\n\telse\n\t{\n\t\townerId = GetUserId();\n\t}\n\n\tchar *ownerName = GetUserNameFromId(ownerId, false);\n\n\tReleaseSysCache(tuple);\n\n\treturn ownerName;\n}\n\n\n/*\n * HasMetadataWorkers returns true if any of the workers in the cluster has its\n * hasmetadata column set to true, which happens when start_metadata_sync_to_node\n * command is run.\n */\nstatic bool\nHasMetadataWorkers(void)\n{\n\tList *workerNodeList = ActiveReadableNonCoordinatorNodeList();\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tif (workerNode->hasMetadata)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CreateInterTableRelationshipOfRelationOnWorkers create inter table relationship\n * for the the given relation id on each worker node with metadata.\n */\nvoid\nCreateInterTableRelationshipOfRelationOnWorkers(Oid relationId)\n{\n\t/* if the table is owned by an extension we don't create */\n\tbool tableOwnedByExtension = IsTableOwnedByExtension(relationId);\n\tif (tableOwnedByExtension)\n\t{\n\t\treturn;\n\t}\n\n\tList *commandList =\n\t\tInterTableRelationshipOfRelationCommandList(relationId);\n\n\t/* prevent recursive propagation */\n\tSendCommandToWorkersWithMetadata(DISABLE_DDL_PROPAGATION);\n\n\tconst char *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tSendCommandToWorkersWithMetadata(command);\n\t}\n}\n\n\n/*\n * InterTableRelationshipOfRelationCommandList returns the command list to create\n * inter table relationship for the given relation.\n */\nList *\nInterTableRelationshipOfRelationCommandList(Oid relationId)\n{\n\t/* commands to create foreign key constraints */\n\tList *commandList = GetReferencingForeignConstaintCommands(relationId);\n\n\t/* commands to create partitioning hierarchy */\n\tif (PartitionTable(relationId))\n\t{\n\t\tchar *alterTableAttachPartitionCommands =\n\t\t\tGenerateAlterTableAttachPartitionCommand(relationId);\n\t\tcommandList = lappend(commandList, alterTableAttachPartitionCommands);\n\t}\n\n\treturn commandList;\n}\n\n\n/*\n * CreateShellTableOnWorkers creates the shell table on each worker node with metadata\n * including sequence dependency and truncate triggers.\n */\nstatic void\nCreateShellTableOnWorkers(Oid relationId)\n{\n\tif (IsTableOwnedByExtension(relationId))\n\t{\n\t\treturn;\n\t}\n\n\tList *commandList = list_make1(DISABLE_DDL_PROPAGATION);\n\n\tIncludeSequenceDefaults includeSequenceDefaults = WORKER_NEXTVAL_SEQUENCE_DEFAULTS;\n\tIncludeIdentities includeIdentityDefaults = INCLUDE_IDENTITY;\n\n\tbool creatingShellTableOnRemoteNode = true;\n\tList *tableDDLCommands = GetFullTableCreationCommands(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  includeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  includeIdentityDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  creatingShellTableOnRemoteNode);\n\n\tTableDDLCommand *tableDDLCommand = NULL;\n\tforeach_declared_ptr(tableDDLCommand, tableDDLCommands)\n\t{\n\t\tAssert(CitusIsA(tableDDLCommand, TableDDLCommand));\n\t\tcommandList = lappend(commandList, GetTableDDLCommand(tableDDLCommand));\n\t}\n\n\tconst char *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tSendCommandToWorkersWithMetadata(command);\n\t}\n}\n\n\n/*\n * CreateTableMetadataOnWorkers creates the list of commands needed to create the\n * metadata of the given distributed table and sends these commands to all metadata\n * workers i.e. workers with hasmetadata=true. Before sending the commands, in order\n * to prevent recursive propagation, DDL propagation on workers are disabled with a\n * `SET citus.enable_ddl_propagation TO off;` command.\n */\nstatic void\nCreateTableMetadataOnWorkers(Oid relationId)\n{\n\tList *commandList = CitusTableMetadataCreateCommandList(relationId);\n\n\t/* prevent recursive propagation */\n\tSendCommandToWorkersWithMetadata(DISABLE_DDL_PROPAGATION);\n\n\t/* send the commands one by one */\n\tconst char *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tSendCommandToWorkersWithMetadata(command);\n\t}\n}\n\n\n/*\n * DetachPartitionCommandList returns list of DETACH commands to detach partitions\n * of all distributed tables. This function is used for detaching partitions in MX\n * workers before DROPping distributed partitioned tables in them. Thus, we are\n * disabling DDL propagation to the beginning of the commands (we are also enabling\n * DDL propagation at the end of command list to swtich back to original state). As\n * an extra step, if there are no partitions to DETACH, this function simply returns\n * empty list to not disable/enable DDL propagation for nothing.\n */\nList *\nDetachPartitionCommandList(void)\n{\n\tList *detachPartitionCommandList = NIL;\n\tList *distributedTableList = CitusTableList();\n\n\t/* we iterate over all distributed partitioned tables and DETACH their partitions */\n\tCitusTableCacheEntry *cacheEntry = NULL;\n\tforeach_declared_ptr(cacheEntry, distributedTableList)\n\t{\n\t\tif (!PartitionedTable(cacheEntry->relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tList *partitionList = PartitionList(cacheEntry->relationId);\n\t\tList *detachCommands =\n\t\t\tGenerateDetachPartitionCommandRelationIdList(partitionList);\n\t\tdetachPartitionCommandList = list_concat(detachPartitionCommandList,\n\t\t\t\t\t\t\t\t\t\t\t\t detachCommands);\n\t}\n\n\tif (list_length(detachPartitionCommandList) == 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tdetachPartitionCommandList =\n\t\tlcons(DISABLE_DDL_PROPAGATION, detachPartitionCommandList);\n\n\t/*\n\t * We probably do not need this but as an extra precaution, we are enabling\n\t * DDL propagation to switch back to original state.\n\t */\n\tdetachPartitionCommandList = lappend(detachPartitionCommandList,\n\t\t\t\t\t\t\t\t\t\t ENABLE_DDL_PROPAGATION);\n\n\treturn detachPartitionCommandList;\n}\n\n\n/*\n * SyncNodeMetadataToNodesOptional tries recreating the metadata\n * snapshot in the metadata workers that are out of sync.\n * Returns the result of synchronization.\n *\n * This function must be called within coordinated transaction\n * since updates on the pg_dist_node metadata must be rollbacked if anything\n * goes wrong.\n */\nstatic NodeMetadataSyncResult\nSyncNodeMetadataToNodesOptional(void)\n{\n\tNodeMetadataSyncResult result = NODE_METADATA_SYNC_SUCCESS;\n\tif (!IsCoordinator())\n\t{\n\t\treturn NODE_METADATA_SYNC_SUCCESS;\n\t}\n\n\t/*\n\t * Request a RowExclusiveLock so we don't run concurrently with other\n\t * functions updating pg_dist_node, but allow concurrency with functions\n\t * which are just reading from pg_dist_node.\n\t */\n\tif (!ConditionalLockRelationOid(DistNodeRelationId(), RowExclusiveLock))\n\t{\n\t\treturn NODE_METADATA_SYNC_FAILED_LOCK;\n\t}\n\n\tList *syncedWorkerList = NIL;\n\tList *workerList = ActivePrimaryNonCoordinatorNodeList(NoLock);\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerList)\n\t{\n\t\tif (workerNode->hasMetadata && !workerNode->metadataSynced)\n\t\t{\n\t\t\tbool raiseInterrupts = false;\n\t\t\tif (!SyncNodeMetadataSnapshotToNode(workerNode, raiseInterrupts))\n\t\t\t{\n\t\t\t\tereport(WARNING, (errmsg(\"failed to sync metadata to %s:%d\",\n\t\t\t\t\t\t\t\t\t\t workerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t workerNode->workerPort)));\n\t\t\t\tresult = NODE_METADATA_SYNC_FAILED_SYNC;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* we add successfully synced nodes to set metadatasynced column later */\n\t\t\t\tsyncedWorkerList = lappend(syncedWorkerList, workerNode);\n\t\t\t}\n\t\t}\n\t}\n\n\tforeach_declared_ptr(workerNode, syncedWorkerList)\n\t{\n\t\tSetWorkerColumnOptional(workerNode, Anum_pg_dist_node_metadatasynced,\n\t\t\t\t\t\t\t\tBoolGetDatum(true));\n\n\t\t/* we fetch the same node again to check if it's synced or not */\n\t\tWorkerNode *nodeUpdated = FindWorkerNode(workerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t workerNode->workerPort);\n\t\tif (!nodeUpdated->metadataSynced)\n\t\t{\n\t\t\t/* set the result to FAILED to trigger the sync again */\n\t\t\tresult = NODE_METADATA_SYNC_FAILED_SYNC;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n\n/*\n * SyncNodeMetadataToNodes recreates the node metadata snapshot in all the\n * metadata workers.\n *\n * This function runs within a coordinated transaction since updates on\n * the pg_dist_node metadata must be rollbacked if anything\n * goes wrong.\n */\nvoid\nSyncNodeMetadataToNodes(void)\n{\n\tEnsureCoordinator();\n\n\t/*\n\t * Request a RowExclusiveLock so we don't run concurrently with other\n\t * functions updating pg_dist_node, but allow concurrency with functions\n\t * which are just reading from pg_dist_node.\n\t */\n\tif (!ConditionalLockRelationOid(DistNodeRelationId(), RowExclusiveLock))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot sync metadata because a concurrent \"\n\t\t\t\t\t\t\t   \"metadata syncing operation is in progress\")));\n\t}\n\n\tList *workerList = ActivePrimaryNonCoordinatorNodeList(NoLock);\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerList)\n\t{\n\t\tif (workerNode->hasMetadata)\n\t\t{\n\t\t\tSetWorkerColumnLocalOnly(workerNode, Anum_pg_dist_node_metadatasynced,\n\t\t\t\t\t\t\t\t\t BoolGetDatum(true));\n\n\t\t\tbool raiseOnError = true;\n\t\t\tSyncNodeMetadataSnapshotToNode(workerNode, raiseOnError);\n\t\t}\n\t}\n}\n\n\n/*\n * SyncNodeMetadataToNodesMain is the main function for syncing node metadata to\n * MX nodes. It retries until success and then exits.\n */\nvoid\nSyncNodeMetadataToNodesMain(Datum main_arg)\n{\n\tOid databaseOid = DatumGetObjectId(main_arg);\n\n\t/* extension owner is passed via bgw_extra */\n\tOid extensionOwner = InvalidOid;\n\tmemcpy_s(&extensionOwner, sizeof(extensionOwner),\n\t\t\t MyBgworkerEntry->bgw_extra, sizeof(Oid));\n\n\tpqsignal(SIGTERM, MetadataSyncSigTermHandler);\n\tpqsignal(SIGALRM, MetadataSyncSigAlrmHandler);\n\tBackgroundWorkerUnblockSignals();\n\n\t/* connect to database, after that we can actually access catalogs */\n\tBackgroundWorkerInitializeConnectionByOid(databaseOid, extensionOwner, 0);\n\n\t/* make worker recognizable in pg_stat_activity */\n\tpgstat_report_appname(METADATA_SYNC_APP_NAME);\n\n\tbool syncedAllNodes = false;\n\n\twhile (!syncedAllNodes)\n\t{\n\t\tInvalidateMetadataSystemCache();\n\t\tStartTransactionCommand();\n\n\t\t/*\n\t\t * Some functions in ruleutils.c, which we use to get the DDL for\n\t\t * metadata propagation, require an active snapshot.\n\t\t */\n\t\tPushActiveSnapshot(GetTransactionSnapshot());\n\n\t\tif (!LockCitusExtension())\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"could not lock the citus extension, \"\n\t\t\t\t\t\t\t\t\t\"skipping metadata sync\")));\n\t\t}\n\t\telse if (CheckCitusVersion(DEBUG1) && CitusHasBeenLoaded())\n\t\t{\n\t\t\tUseCoordinatedTransaction();\n\n\t\t\tNodeMetadataSyncResult result = SyncNodeMetadataToNodesOptional();\n\t\t\tsyncedAllNodes = (result == NODE_METADATA_SYNC_SUCCESS);\n\n\t\t\t/* we use LISTEN/NOTIFY to wait for metadata syncing in tests */\n\t\t\tif (result != NODE_METADATA_SYNC_FAILED_LOCK)\n\t\t\t{\n\t\t\t\tAsync_Notify(METADATA_SYNC_CHANNEL, NULL);\n\t\t\t}\n\t\t}\n\n\t\tPopActiveSnapshot();\n\t\tCommitTransactionCommand();\n\n\t\tif (syncedAllNodes)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t/*\n\t\t * If backend is cancelled (e.g. bacause of distributed deadlock),\n\t\t * CHECK_FOR_INTERRUPTS() will raise a cancellation error which will\n\t\t * result in exit(1).\n\t\t */\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\t/*\n\t\t * SIGTERM is used for when maintenance daemon tries to clean-up\n\t\t * metadata sync daemons spawned by terminated maintenance daemons.\n\t\t */\n\t\tif (got_SIGTERM)\n\t\t{\n\t\t\texit(0);\n\t\t}\n\n\t\t/*\n\t\t * SIGALRM is used for testing purposes and it simulates an error in metadata\n\t\t * sync daemon.\n\t\t */\n\t\tif (got_SIGALRM)\n\t\t{\n\t\t\telog(ERROR, \"Error in metadata sync daemon\");\n\t\t}\n\n\t\tpg_usleep(MetadataSyncRetryInterval * 1000);\n\t}\n}\n\n\n/*\n * MetadataSyncSigTermHandler set a flag to request termination of metadata\n * sync daemon.\n */\nstatic void\nMetadataSyncSigTermHandler(SIGNAL_ARGS)\n{\n\tint save_errno = errno;\n\n\tgot_SIGTERM = true;\n\tif (MyProc != NULL)\n\t{\n\t\tSetLatch(&MyProc->procLatch);\n\t}\n\n\terrno = save_errno;\n}\n\n\n/*\n * MetadataSyncSigAlrmHandler set a flag to request error at metadata\n * sync daemon. This is used for testing purposes.\n */\nstatic void\nMetadataSyncSigAlrmHandler(SIGNAL_ARGS)\n{\n\tint save_errno = errno;\n\n\tgot_SIGALRM = true;\n\tif (MyProc != NULL)\n\t{\n\t\tSetLatch(&MyProc->procLatch);\n\t}\n\n\terrno = save_errno;\n}\n\n\n/*\n * SpawnSyncNodeMetadataToNodes starts a background worker which runs node metadata\n * sync. On success it returns workers' handle. Otherwise it returns NULL.\n */\nBackgroundWorkerHandle *\nSpawnSyncNodeMetadataToNodes(Oid database, Oid extensionOwner)\n{\n\tchar workerName[BGW_MAXLEN];\n\n\tSafeSnprintf(workerName, BGW_MAXLEN,\n\t\t\t\t \"Citus Metadata Sync: %u/%u\",\n\t\t\t\t database, extensionOwner);\n\n\tCitusBackgroundWorkerConfig config = {\n\t\t.workerName = workerName,\n\t\t.functionName = \"SyncNodeMetadataToNodesMain\",\n\t\t.mainArg = ObjectIdGetDatum(MyDatabaseId),\n\t\t.extensionOwner = extensionOwner,\n\t\t.needsNotification = true,\n\t\t.waitForStartup = false,\n\t\t.restartTime = CITUS_BGW_NEVER_RESTART,\n\t\t.startTime = CITUS_BGW_DEFAULT_START_TIME,\n\t\t.workerType = NULL, /* use default */\n\t\t.extraData = NULL,\n\t\t.extraDataSize = 0\n\t};\n\treturn RegisterCitusBackgroundWorker(&config);\n}\n\n\n/*\n * SignalMetadataSyncDaemon signals metadata sync daemons belonging to\n * the given database.\n */\nvoid\nSignalMetadataSyncDaemon(Oid database, int sig)\n{\n\tint backendCount = pgstat_fetch_stat_numbackends();\n\tfor (int backend = 1; backend <= backendCount; backend++)\n\t{\n\t\tLocalPgBackendStatus *localBeEntry = pgstat_get_local_beentry_by_index(backend);\n\t\tif (!localBeEntry)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tPgBackendStatus *beStatus = &localBeEntry->backendStatus;\n\t\tif (beStatus->st_databaseid == database &&\n\t\t\tstrncmp(beStatus->st_appname, METADATA_SYNC_APP_NAME, BGW_MAXLEN) == 0)\n\t\t{\n\t\t\tkill(beStatus->st_procpid, sig);\n\t\t}\n\t}\n}\n\n\n/*\n * ShouldInitiateMetadataSync returns if metadata sync daemon should be initiated.\n * It sets lockFailure to true if pg_dist_node lock couldn't be acquired for the\n * check.\n */\nbool\nShouldInitiateMetadataSync(bool *lockFailure)\n{\n\tif (!IsCoordinator())\n\t{\n\t\t*lockFailure = false;\n\t\treturn false;\n\t}\n\n\tOid distNodeOid = DistNodeRelationId();\n\tif (!ConditionalLockRelationOid(distNodeOid, AccessShareLock))\n\t{\n\t\t*lockFailure = true;\n\t\treturn false;\n\t}\n\n\tbool shouldSyncMetadata = false;\n\n\tList *workerList = ActivePrimaryNonCoordinatorNodeList(NoLock);\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerList)\n\t{\n\t\tif (workerNode->hasMetadata && !workerNode->metadataSynced)\n\t\t{\n\t\t\tshouldSyncMetadata = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tUnlockRelationOid(distNodeOid, AccessShareLock);\n\n\t*lockFailure = false;\n\treturn shouldSyncMetadata;\n}\n\n\n/*\n * citus_internal_add_partition_metadata is an internal UDF to\n * add a row to pg_dist_partition.\n */\nDatum\ncitus_internal_add_partition_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tPG_ENSURE_ARGNOTNULL(0, \"relation\");\n\tOid relationId = PG_GETARG_OID(0);\n\n\tPG_ENSURE_ARGNOTNULL(1, \"distribution method\");\n\tchar distributionMethod = PG_GETARG_CHAR(1);\n\n\tPG_ENSURE_ARGNOTNULL(3, \"Colocation ID\");\n\tint colocationId = PG_GETARG_INT32(3);\n\n\tPG_ENSURE_ARGNOTNULL(4, \"replication model\");\n\tchar replicationModel = PG_GETARG_CHAR(4);\n\n\ttext *distributionColumnText = NULL;\n\tchar *distributionColumnString = NULL;\n\tVar *distributionColumnVar = NULL;\n\n\t/* this flag is only valid for citus local tables, so set it to false */\n\tbool autoConverted = false;\n\n\t/* only owner of the table (or superuser) is allowed to add the Citus metadata */\n\tEnsureTableOwner(relationId);\n\n\t/* we want to serialize all the metadata changes to this table */\n\tLockRelationOid(relationId, ShareUpdateExclusiveLock);\n\n\tif (!PG_ARGISNULL(2))\n\t{\n\t\tdistributionColumnText = PG_GETARG_TEXT_P(2);\n\t\tdistributionColumnString = text_to_cstring(distributionColumnText);\n\n\t\tdistributionColumnVar =\n\t\t\tBuildDistributionKeyFromColumnName(relationId, distributionColumnString,\n\t\t\t\t\t\t\t\t\t\t\t   AccessShareLock);\n\t\tAssert(distributionColumnVar != NULL);\n\t}\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\t/* this UDF is not allowed allowed for executing as a separate command */\n\t\tEnsureCitusInitiatedOperation();\n\n\t\tif (distributionMethod == DISTRIBUTE_BY_NONE && distributionColumnVar != NULL)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"Reference or local tables cannot have \"\n\t\t\t\t\t\t\t\t   \"distribution columns\")));\n\t\t}\n\t\telse if (distributionMethod != DISTRIBUTE_BY_NONE &&\n\t\t\t\t distributionColumnVar == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"Distribution column cannot be NULL for \"\n\t\t\t\t\t\t\t\t   \"relation \\\"%s\\\"\", get_rel_name(relationId))));\n\t\t}\n\n\t\t/*\n\t\t * Even if the table owner is a malicious user and the partition\n\t\t * metadata is not sane, the user can only affect its own tables.\n\t\t * Given that the user is owner of the table, we should allow.\n\t\t */\n\t\tEnsurePartitionMetadataIsSane(relationId, distributionMethod, colocationId,\n\t\t\t\t\t\t\t\t\t  replicationModel, distributionColumnVar);\n\t}\n\n\tInsertIntoPgDistPartition(relationId, distributionMethod, distributionColumnVar,\n\t\t\t\t\t\t\t  colocationId, replicationModel, autoConverted);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * EnsurePartitionMetadataIsSane ensures that the input values are safe\n * for inserting into pg_dist_partition metadata.\n */\nstatic void\nEnsurePartitionMetadataIsSane(Oid relationId, char distributionMethod, int colocationId,\n\t\t\t\t\t\t\t  char replicationModel, Var *distributionColumnVar)\n{\n\tif (!(distributionMethod == DISTRIBUTE_BY_HASH ||\n\t\t  distributionMethod == DISTRIBUTE_BY_NONE))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Metadata syncing is only allowed for hash, reference \"\n\t\t\t\t\t\t\t   \"and local tables:%c\", distributionMethod)));\n\t}\n\n\tif (colocationId < INVALID_COLOCATION_ID)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Metadata syncing is only allowed for valid \"\n\t\t\t\t\t\t\t   \"colocation id values.\")));\n\t}\n\telse if (colocationId != INVALID_COLOCATION_ID &&\n\t\t\t distributionMethod == DISTRIBUTE_BY_HASH)\n\t{\n\t\tint count = 1;\n\t\tList *targetColocatedTableList =\n\t\t\tColocationGroupTableList(colocationId, count);\n\n\t\t/*\n\t\t * If we have any colocated hash tables, ensure if they share the\n\t\t * same distribution key properties.\n\t\t */\n\t\tif (list_length(targetColocatedTableList) >= 1)\n\t\t{\n\t\t\tOid targetRelationId = linitial_oid(targetColocatedTableList);\n\n\t\t\tEnsureColumnTypeEquality(relationId, targetRelationId, distributionColumnVar,\n\t\t\t\t\t\t\t\t\t DistPartitionKeyOrError(targetRelationId));\n\t\t}\n\t}\n\n\n\tif (!(replicationModel == REPLICATION_MODEL_2PC ||\n\t\t  replicationModel == REPLICATION_MODEL_STREAMING ||\n\t\t  replicationModel == REPLICATION_MODEL_COORDINATOR))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Metadata syncing is only allowed for \"\n\t\t\t\t\t\t\t   \"known replication models.\")));\n\t}\n\n\tif (distributionMethod == DISTRIBUTE_BY_NONE &&\n\t\t!(replicationModel == REPLICATION_MODEL_STREAMING ||\n\t\t  replicationModel == REPLICATION_MODEL_2PC))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Local or references tables can only have '%c' or '%c' \"\n\t\t\t\t\t\t\t   \"as the replication model.\",\n\t\t\t\t\t\t\t   REPLICATION_MODEL_STREAMING, REPLICATION_MODEL_2PC)));\n\t}\n}\n\n\n/*\n * citus_internal_delete_partition_metadata is an internal UDF to\n * delete a row in pg_dist_partition.\n */\nDatum\ncitus_internal_delete_partition_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tPG_ENSURE_ARGNOTNULL(0, \"relation\");\n\tOid relationId = PG_GETARG_OID(0);\n\n\t/* only owner of the table (or superuser) is allowed to add the Citus metadata */\n\tEnsureTableOwner(relationId);\n\n\t/* we want to serialize all the metadata changes to this table */\n\tLockRelationOid(relationId, ShareUpdateExclusiveLock);\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\tEnsureCitusInitiatedOperation();\n\t}\n\n\tDeletePartitionRow(relationId);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_add_shard_metadata is an internal UDF to\n * add a row to pg_dist_shard.\n */\nDatum\ncitus_internal_add_shard_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tPG_ENSURE_ARGNOTNULL(0, \"relation\");\n\tOid relationId = PG_GETARG_OID(0);\n\n\tPG_ENSURE_ARGNOTNULL(1, \"shard id\");\n\tint64 shardId = PG_GETARG_INT64(1);\n\n\tPG_ENSURE_ARGNOTNULL(2, \"storage type\");\n\tchar storageType = PG_GETARG_CHAR(2);\n\n\ttext *shardMinValue = NULL;\n\tif (!PG_ARGISNULL(3))\n\t{\n\t\tshardMinValue = PG_GETARG_TEXT_P(3);\n\t}\n\n\ttext *shardMaxValue = NULL;\n\tif (!PG_ARGISNULL(4))\n\t{\n\t\tshardMaxValue = PG_GETARG_TEXT_P(4);\n\t}\n\n\t/* only owner of the table (or superuser) is allowed to add the Citus metadata */\n\tEnsureTableOwner(relationId);\n\n\t/* we want to serialize all the metadata changes to this table */\n\tLockRelationOid(relationId, ShareUpdateExclusiveLock);\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\t/* this UDF is not allowed allowed for executing as a separate command */\n\t\tEnsureCitusInitiatedOperation();\n\n\t\t/*\n\t\t * Even if the table owner is a malicious user and the shard metadata is\n\t\t * not sane, the user can only affect its own tables. Given that the\n\t\t * user is owner of the table, we should allow.\n\t\t */\n\t\tEnsureShardMetadataIsSane(relationId, shardId, storageType, shardMinValue,\n\t\t\t\t\t\t\t\t  shardMaxValue);\n\t}\n\n\tInsertShardRow(relationId, shardId, storageType, shardMinValue, shardMaxValue);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * EnsureCitusInitiatedOperation is a helper function which ensures that\n * the execution is initiated by Citus.\n */\nstatic void\nEnsureCitusInitiatedOperation(void)\n{\n\tif (!(IsCitusInternalBackend() || IsRebalancerInternalBackend()))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"This is an internal Citus function can only be \"\n\t\t\t\t\t\t\t   \"used in a distributed transaction\")));\n\t}\n}\n\n\n/*\n * EnsureShardMetadataIsSane ensures that the input values are safe\n * for inserting into pg_dist_shard metadata.\n */\nstatic void\nEnsureShardMetadataIsSane(Oid relationId, int64 shardId, char storageType,\n\t\t\t\t\t\t  text *shardMinValue, text *shardMaxValue)\n{\n\tif (shardId <= INVALID_SHARD_ID)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Invalid shard id: %ld\", shardId)));\n\t}\n\n\tif (!(storageType == SHARD_STORAGE_TABLE ||\n\t\t  storageType == SHARD_STORAGE_FOREIGN))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Invalid shard storage type: %c\", storageType)));\n\t}\n\n\tchar partitionMethod = PartitionMethodViaCatalog(relationId);\n\tif (partitionMethod == DISTRIBUTE_BY_INVALID)\n\t{\n\t\t/* connection from the coordinator operating on a shard */\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"The relation \\\"%s\\\" does not have a valid \"\n\t\t\t\t\t\t\t   \"entry in pg_dist_partition.\",\n\t\t\t\t\t\t\t   get_rel_name(relationId))));\n\t}\n\telse if (!(partitionMethod == DISTRIBUTE_BY_HASH ||\n\t\t\t   partitionMethod == DISTRIBUTE_BY_NONE))\n\t{\n\t\t/* connection from the coordinator operating on a shard */\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Metadata syncing is only allowed for hash, \"\n\t\t\t\t\t\t\t   \"reference and local tables: %c\", partitionMethod)));\n\t}\n\n\tList *distShardTupleList = LookupDistShardTuples(relationId);\n\tif (partitionMethod == DISTRIBUTE_BY_NONE)\n\t{\n\t\tif (shardMinValue != NULL || shardMaxValue != NULL)\n\t\t{\n\t\t\tchar *relationName = get_rel_name(relationId);\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"Shards of reference or local table \\\"%s\\\" should \"\n\t\t\t\t\t\t\t\t   \"have NULL shard ranges\", relationName)));\n\t\t}\n\t\telse if (list_length(distShardTupleList) != 0)\n\t\t{\n\t\t\tchar *relationName = get_rel_name(relationId);\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"relation \\\"%s\\\" has already at least one shard, \"\n\t\t\t\t\t\t\t\t   \"adding more is not allowed\", relationName)));\n\t\t}\n\t}\n\telse if (partitionMethod == DISTRIBUTE_BY_HASH)\n\t{\n\t\tif (shardMinValue == NULL || shardMaxValue == NULL)\n\t\t{\n\t\t\tchar *relationName = get_rel_name(relationId);\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"Shards of has distributed table  \\\"%s\\\" \"\n\t\t\t\t\t\t\t\t   \"cannot have NULL shard ranges\", relationName)));\n\t\t}\n\n\t\tchar *shardMinValueString = text_to_cstring(shardMinValue);\n\t\tchar *shardMaxValueString = text_to_cstring(shardMaxValue);\n\n\t\t/* pg_strtoint32 does the syntax and out of bound checks for us */\n\t\tint32 shardMinValueInt = pg_strtoint32(shardMinValueString);\n\t\tint32 shardMaxValueInt = pg_strtoint32(shardMaxValueString);\n\n\t\tif (shardMinValueInt > shardMaxValueInt)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"shardMinValue=%d is greater than \"\n\t\t\t\t\t\t\t\t   \"shardMaxValue=%d for table \\\"%s\\\", which is \"\n\t\t\t\t\t\t\t\t   \"not allowed\", shardMinValueInt,\n\t\t\t\t\t\t\t\t   shardMaxValueInt, get_rel_name(relationId))));\n\t\t}\n\n\t\t/*\n\t\t * We are only dealing with hash distributed tables, that's why we\n\t\t * can hard code data type and typemod.\n\t\t */\n\t\tconst int intervalTypeId = INT4OID;\n\t\tconst int intervalTypeMod = -1;\n\n\t\tRelation distShardRelation = table_open(DistShardRelationId(), AccessShareLock);\n\t\tTupleDesc distShardTupleDesc = RelationGetDescr(distShardRelation);\n\n\t\tFmgrInfo *shardIntervalCompareFunction =\n\t\t\tGetFunctionInfo(intervalTypeId, BTREE_AM_OID, BTORDER_PROC);\n\n\t\tHeapTuple shardTuple = NULL;\n\t\tforeach_declared_ptr(shardTuple, distShardTupleList)\n\t\t{\n\t\t\tShardInterval *shardInterval =\n\t\t\t\tTupleToShardInterval(shardTuple, distShardTupleDesc,\n\t\t\t\t\t\t\t\t\t intervalTypeId, intervalTypeMod);\n\n\t\t\tDatum firstMin = Int32GetDatum(shardMinValueInt);\n\t\t\tDatum firstMax = Int32GetDatum(shardMaxValueInt);\n\t\t\tDatum secondMin = shardInterval->minValue;\n\t\t\tDatum secondMax = shardInterval->maxValue;\n\t\t\tOid collationId = InvalidOid;\n\n\t\t\t/*\n\t\t\t * This is an unexpected case as we are reading the metadata, which has\n\t\t\t * already been verified for being not NULL. Still, lets be extra\n\t\t\t * cautious to avoid any crashes.\n\t\t\t */\n\t\t\tif (!shardInterval->minValueExists || !shardInterval->maxValueExists)\n\t\t\t{\n\t\t\t\tchar *relationName = get_rel_name(relationId);\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\t\terrmsg(\"Shards of has distributed table  \\\"%s\\\" \"\n\t\t\t\t\t\t\t\t\t   \"cannot have NULL shard ranges\", relationName)));\n\t\t\t}\n\n\t\t\tif (ShardIntervalsOverlapWithParams(firstMin, firstMax, secondMin, secondMax,\n\t\t\t\t\t\t\t\t\t\t\t\tshardIntervalCompareFunction,\n\t\t\t\t\t\t\t\t\t\t\t\tcollationId))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\t\terrmsg(\"Shard intervals overlap for table \\\"%s\\\": \"\n\t\t\t\t\t\t\t\t\t   \"%ld and %ld\", get_rel_name(relationId),\n\t\t\t\t\t\t\t\t\t   shardId, shardInterval->shardId)));\n\t\t\t}\n\t\t}\n\n\t\ttable_close(distShardRelation, NoLock);\n\t}\n}\n\n\n/*\n * citus_internal_add_placement_metadata is an internal UDF to\n * add a row to pg_dist_placement.\n */\nDatum\ncitus_internal_add_placement_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint64 shardId = PG_GETARG_INT64(0);\n\tint64 shardLength = PG_GETARG_INT64(1);\n\tint32 groupId = PG_GETARG_INT32(2);\n\tint64 placementId = PG_GETARG_INT64(3);\n\n\tcitus_internal_add_placement_metadata_internal(shardId, shardLength,\n\t\t\t\t\t\t\t\t\t\t\t\t   groupId, placementId);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_add_placement_metadata is an internal UDF to\n * delete a row from pg_dist_placement.\n */\nDatum\ncitus_internal_delete_placement_metadata(PG_FUNCTION_ARGS)\n{\n\tPG_ENSURE_ARGNOTNULL(0, \"placement_id\");\n\tint64 placementId = PG_GETARG_INT64(0);\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\t/* this UDF is not allowed allowed for executing as a separate command */\n\t\tEnsureCitusInitiatedOperation();\n\t}\n\n\tDeleteShardPlacementRow(placementId);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_add_placement_metadata_legacy is the old function that will be dropped.\n */\nDatum\ncitus_internal_add_placement_metadata_legacy(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint64 shardId = PG_GETARG_INT64(0);\n\tint64 shardLength = PG_GETARG_INT64(2);\n\tint32 groupId = PG_GETARG_INT32(3);\n\tint64 placementId = PG_GETARG_INT64(4);\n\n\tcitus_internal_add_placement_metadata_internal(shardId, shardLength,\n\t\t\t\t\t\t\t\t\t\t\t\t   groupId, placementId);\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_add_placement_metadata_internal is the internal function\n * too insert a row into pg_dist_placement\n */\nvoid\ncitus_internal_add_placement_metadata_internal(int64 shardId, int64 shardLength,\n\t\t\t\t\t\t\t\t\t\t\t   int32 groupId, int64 placementId)\n{\n\tbool missingOk = false;\n\tOid relationId = LookupShardRelationFromCatalog(shardId, missingOk);\n\n\t/* only owner of the table is allowed to modify the metadata */\n\tEnsureTableOwner(relationId);\n\n\t/* we want to serialize all the metadata changes to this table */\n\tLockRelationOid(relationId, ShareUpdateExclusiveLock);\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\t/* this UDF is not allowed allowed for executing as a separate command */\n\t\tEnsureCitusInitiatedOperation();\n\n\t\t/*\n\t\t * Even if the table owner is a malicious user, as long as the shard placements\n\t\t * fit into basic requirements of Citus metadata, the user can only affect its\n\t\t * own tables. Given that the user is owner of the table, we should allow.\n\t\t */\n\t\tEnsureShardPlacementMetadataIsSane(relationId, shardId, placementId,\n\t\t\t\t\t\t\t\t\t\t   shardLength, groupId);\n\t}\n\n\tInsertShardPlacementRow(shardId, placementId, shardLength, groupId);\n}\n\n\n/*\n * EnsureShardPlacementMetadataIsSane ensures if the input parameters for\n * the shard placement metadata is sane.\n */\nstatic void\nEnsureShardPlacementMetadataIsSane(Oid relationId, int64 shardId, int64 placementId,\n\t\t\t\t\t\t\t\t   int64 shardLength, int32 groupId)\n{\n\t/* we have just read the metadata, so we are sure that the shard exists */\n\tAssert(ShardExists(shardId));\n\n\tif (placementId <= INVALID_PLACEMENT_ID)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Shard placement has invalid placement id \"\n\t\t\t\t\t\t\t   \"(%ld) for shard(%ld)\", placementId, shardId)));\n\t}\n\n\tbool nodeIsInMetadata = false;\n\tWorkerNode *workerNode =\n\t\tPrimaryNodeForGroup(groupId, &nodeIsInMetadata);\n\tif (!workerNode)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Node with group id %d for shard placement \"\n\t\t\t\t\t\t\t   \"%ld does not exist\", groupId, shardId)));\n\t}\n}\n\n\n/*\n * ShouldSkipMetadataChecks returns true if the current user is allowed to\n * make any\n */\nstatic bool\nShouldSkipMetadataChecks(void)\n{\n\tif (strcmp(EnableManualMetadataChangesForUser, \"\") != 0)\n\t{\n\t\t/*\n\t\t * EnableManualMetadataChangesForUser is a GUC which\n\t\t * can be changed by a super user. We use this GUC as\n\t\t * a safety belt in case the current metadata checks are\n\t\t * too restrictive and the operator can allow users to skip\n\t\t * the checks.\n\t\t */\n\n\t\t/*\n\t\t * Make sure that the user exists, and print it to prevent any\n\t\t * optimization skipping the get_role_oid call.\n\t\t */\n\t\tbool missingOK = false;\n\t\tOid allowedUserId = get_role_oid(EnableManualMetadataChangesForUser, missingOK);\n\t\tif (allowedUserId == GetUserId())\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * citus_internal_update_placement_metadata is an internal UDF to\n * update a row in pg_dist_placement.\n */\nDatum\ncitus_internal_update_placement_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint64 shardId = PG_GETARG_INT64(0);\n\tint32 sourceGroupId = PG_GETARG_INT32(1);\n\tint32 targetGroupId = PG_GETARG_INT32(2);\n\n\tShardPlacement *placement = NULL;\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\t/* this UDF is not allowed allowed for executing as a separate command */\n\t\tEnsureCitusInitiatedOperation();\n\n\t\tif (!ShardExists(shardId))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"Shard id does not exists: %ld\", shardId)));\n\t\t}\n\n\t\tbool missingOk = false;\n\t\tEnsureShardOwner(shardId, missingOk);\n\n\t\t/*\n\t\t * This function ensures that the source group exists hence we\n\t\t * call it from this code-block.\n\t\t */\n\t\tplacement = ActiveShardPlacementOnGroup(sourceGroupId, shardId);\n\n\t\tbool nodeIsInMetadata = false;\n\t\tWorkerNode *workerNode =\n\t\t\tPrimaryNodeForGroup(targetGroupId, &nodeIsInMetadata);\n\t\tif (!workerNode)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"Node with group id %d for shard placement \"\n\t\t\t\t\t\t\t\t   \"%ld does not exist\", targetGroupId, shardId)));\n\t\t}\n\t}\n\telse\n\t{\n\t\tplacement = ActiveShardPlacementOnGroup(sourceGroupId, shardId);\n\t}\n\n\t/*\n\t * Updating pg_dist_placement ensures that the node with targetGroupId\n\t * exists and this is the only placement on that group.\n\t */\n\tif (placement == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Active placement for shard %ld is not \"\n\t\t\t\t\t\t\t   \"found on group:%d\", shardId, targetGroupId)));\n\t}\n\n\tUpdatePlacementGroupId(placement->placementId, targetGroupId);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_delete_shard_metadata is an internal UDF to\n * delete a row in pg_dist_shard and corresponding placement rows\n * from pg_dist_shard_placement.\n */\nDatum\ncitus_internal_delete_shard_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint64 shardId = PG_GETARG_INT64(0);\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\t/* this UDF is not allowed allowed for executing as a separate command */\n\t\tEnsureCitusInitiatedOperation();\n\n\t\tif (!ShardExists(shardId))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"Shard id does not exists: %ld\", shardId)));\n\t\t}\n\n\t\tbool missingOk = false;\n\t\tEnsureShardOwner(shardId, missingOk);\n\t}\n\n\tList *shardPlacementList = ShardPlacementList(shardId);\n\tShardPlacement *shardPlacement = NULL;\n\tforeach_declared_ptr(shardPlacement, shardPlacementList)\n\t{\n\t\tDeleteShardPlacementRow(shardPlacement->placementId);\n\t}\n\n\tDeleteShardRow(shardId);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_update_relation_colocation is an internal UDF to\n * delete a row in pg_dist_shard and corresponding placement rows\n * from pg_dist_shard_placement.\n */\nDatum\ncitus_internal_update_relation_colocation(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\tuint32 targetColocationId = PG_GETARG_UINT32(1);\n\n\tEnsureTableOwner(relationId);\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\t/* this UDF is not allowed allowed for executing as a separate command */\n\t\tEnsureCitusInitiatedOperation();\n\n\t\t/* ensure that the table is in pg_dist_partition */\n\t\tchar partitionMethod = PartitionMethodViaCatalog(relationId);\n\t\tif (partitionMethod == DISTRIBUTE_BY_INVALID)\n\t\t{\n\t\t\t/* connection from the coordinator operating on a shard */\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"The relation \\\"%s\\\" does not have a valid \"\n\t\t\t\t\t\t\t\t   \"entry in pg_dist_partition.\",\n\t\t\t\t\t\t\t\t   get_rel_name(relationId))));\n\t\t}\n\t\telse if (!IsCitusTableType(relationId, HASH_DISTRIBUTED) &&\n\t\t\t\t !IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED))\n\t\t{\n\t\t\t/* connection from the coordinator operating on a shard */\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"Updating colocation ids are only allowed for hash \"\n\t\t\t\t\t\t\t\t   \"and single shard distributed tables: %c\",\n\t\t\t\t\t\t\t\t   partitionMethod)));\n\t\t}\n\n\t\tint count = 1;\n\t\tList *targetColocatedTableList =\n\t\t\tColocationGroupTableList(targetColocationId, count);\n\n\t\tif (list_length(targetColocatedTableList) == 0)\n\t\t{\n\t\t\t/* the table is colocated with none, so nothing to check */\n\t\t}\n\t\telse\n\t\t{\n\t\t\tOid targetRelationId = linitial_oid(targetColocatedTableList);\n\n\t\t\tErrorIfShardPlacementsNotColocated(relationId, targetRelationId);\n\t\t\tCheckReplicationModel(relationId, targetRelationId);\n\t\t\tCheckDistributionColumnType(relationId, targetRelationId);\n\t\t}\n\t}\n\n\tbool localOnly = true;\n\tUpdateRelationColocationGroup(relationId, targetColocationId, localOnly);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_add_colocation_metadata is an internal UDF to\n * add a row to pg_dist_colocation.\n */\nDatum\ncitus_internal_add_colocation_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureSuperUser();\n\n\tint colocationId = PG_GETARG_INT32(0);\n\tint shardCount = PG_GETARG_INT32(1);\n\tint replicationFactor = PG_GETARG_INT32(2);\n\tOid distributionColumnType = PG_GETARG_INT32(3);\n\tOid distributionColumnCollation = PG_GETARG_INT32(4);\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\t/* this UDF is not allowed allowed for executing as a separate command */\n\t\tEnsureCitusInitiatedOperation();\n\t}\n\n\tInsertColocationGroupLocally(colocationId, shardCount, replicationFactor,\n\t\t\t\t\t\t\t\t distributionColumnType, distributionColumnCollation);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_delete_colocation_metadata is an internal UDF to\n * delte row from pg_dist_colocation.\n */\nDatum\ncitus_internal_delete_colocation_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureSuperUser();\n\n\tint colocationId = PG_GETARG_INT32(0);\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\t/* this UDF is not allowed allowed for executing as a separate command */\n\t\tEnsureCitusInitiatedOperation();\n\t}\n\n\tDeleteColocationGroupLocally(colocationId);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_add_tenant_schema is an internal UDF to\n * call InsertTenantSchemaLocally on a remote node.\n *\n * None of the parameters are allowed to be NULL. To set the colocation\n * id to NULL in metadata, use INVALID_COLOCATION_ID.\n */\nDatum\ncitus_internal_add_tenant_schema(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tPG_ENSURE_ARGNOTNULL(0, \"schema_id\");\n\tOid schemaId = PG_GETARG_OID(0);\n\n\tPG_ENSURE_ARGNOTNULL(1, \"colocation_id\");\n\tuint32 colocationId = PG_GETARG_INT32(1);\n\n\tInsertTenantSchemaLocally(schemaId, colocationId);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_delete_tenant_schema is an internal UDF to\n * call DeleteTenantSchemaLocally on a remote node.\n *\n * The schemaId parameter is not allowed to be NULL. Morever, input schema is\n * expected to be dropped already because this function is called from Citus\n * drop hook and only used to clean up metadata after the schema is dropped.\n */\nDatum\ncitus_internal_delete_tenant_schema(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tPG_ENSURE_ARGNOTNULL(0, \"schema_id\");\n\tOid schemaId = PG_GETARG_OID(0);\n\n\tDeleteTenantSchemaLocally(schemaId);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_update_none_dist_table_metadata is an internal UDF to\n * update a row in pg_dist_partition that belongs to given none-distributed\n * table.\n */\nDatum\ncitus_internal_update_none_dist_table_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tPG_ENSURE_ARGNOTNULL(0, \"relation_id\");\n\tOid relationId = PG_GETARG_OID(0);\n\n\tPG_ENSURE_ARGNOTNULL(1, \"replication_model\");\n\tchar replicationModel = PG_GETARG_CHAR(1);\n\n\tPG_ENSURE_ARGNOTNULL(2, \"colocation_id\");\n\tuint32 colocationId = PG_GETARG_INT32(2);\n\n\tPG_ENSURE_ARGNOTNULL(3, \"auto_converted\");\n\tbool autoConverted = PG_GETARG_BOOL(3);\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\tEnsureCitusInitiatedOperation();\n\t}\n\n\tUpdateNoneDistTableMetadata(relationId, replicationModel,\n\t\t\t\t\t\t\t\tcolocationId, autoConverted);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_database_command is an internal UDF to\n * create a database in an idempotent maner without\n * transaction block restrictions.\n */\nDatum\ncitus_internal_database_command(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tif (!ShouldSkipMetadataChecks())\n\t{\n\t\tEnsureCitusInitiatedOperation();\n\t}\n\n\tPG_ENSURE_ARGNOTNULL(0, \"command\");\n\n\ttext *commandText = PG_GETARG_TEXT_P(0);\n\tchar *command = text_to_cstring(commandText);\n\tNode *parseTree = ParseTreeNode(command);\n\n\tint saveNestLevel = NewGUCNestLevel();\n\n\tset_config_option(\"citus.enable_ddl_propagation\", \"off\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n\n\tset_config_option(\"citus.enable_create_database_propagation\", \"off\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n\n\t/*\n\t * createdb() uses ParseState to report the error position for the\n\t * input command and the position is reported to be 0 when it's provided as NULL.\n\t * We're okay with that because we don't expect this UDF to be called with an incorrect\n\t * DDL command.\n\t */\n\tParseState *pstate = NULL;\n\n\tif (IsA(parseTree, CreatedbStmt))\n\t{\n\t\tCreatedbStmt *stmt = castNode(CreatedbStmt, parseTree);\n\n\t\tbool missingOk = true;\n\t\tOid databaseOid = get_database_oid(stmt->dbname, missingOk);\n\n\t\tif (!OidIsValid(databaseOid))\n\t\t{\n\t\t\tcreatedb(pstate, (CreatedbStmt *) parseTree);\n\t\t}\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"citus_internal.database_command() can only be used \"\n\t\t\t\t\t\t\t   \"for CREATE DATABASE command by Citus.\")));\n\t}\n\n\t/* rollback GUCs to the state before this session */\n\tAtEOXact_GUC(true, saveNestLevel);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * SyncNewColocationGroup synchronizes a new pg_dist_colocation entry to a worker.\n */\nvoid\nSyncNewColocationGroupToNodes(uint32 colocationId, int shardCount, int replicationFactor,\n\t\t\t\t\t\t\t  Oid distributionColumnType, Oid distributionColumnCollation)\n{\n\tchar *command = ColocationGroupCreateCommand(colocationId, shardCount,\n\t\t\t\t\t\t\t\t\t\t\t\t replicationFactor,\n\t\t\t\t\t\t\t\t\t\t\t\t distributionColumnType,\n\t\t\t\t\t\t\t\t\t\t\t\t distributionColumnCollation);\n\n\t/*\n\t * We require superuser for all pg_dist_colocation operations because we have\n\t * no reasonable way of restricting access.\n\t */\n\tSendCommandToWorkersWithMetadataViaSuperUser(command);\n}\n\n\n/*\n * ColocationGroupCreateCommand returns a command for creating a colocation group.\n */\nstatic char *\nColocationGroupCreateCommand(uint32 colocationId, int shardCount, int replicationFactor,\n\t\t\t\t\t\t\t Oid distributionColumnType, Oid distributionColumnCollation)\n{\n\tStringInfo insertColocationCommand = makeStringInfo();\n\n\t/*\n\t * Get type name and schema separately to defer type resolution.\n\t * This approach matches how SendColocationMetadataCommands handles types.\n\t */\n\tchar *typeName = GetRemoteTypeName(distributionColumnType);\n\tchar *typeSchemaName = GetRemoteTypeNamespace(distributionColumnType);\n\n\tappendStringInfo(insertColocationCommand,\n\t\t\t\t\t \"WITH colocation_data(\"\n\t\t\t\t\t \"colocationid, shardcount, replicationfactor, \"\n\t\t\t\t\t \"typeschema, typename, collationid) \"\n\t\t\t\t\t \"AS (VALUES (%d, %d, %d, \",\n\t\t\t\t\t colocationId,\n\t\t\t\t\t shardCount,\n\t\t\t\t\t replicationFactor);\n\n\tif (typeSchemaName != NULL && typeName != NULL)\n\t{\n\t\t/* Use quote_identifier so the schema name can be cast to regnamespace */\n\t\tappendStringInfo(insertColocationCommand,\n\t\t\t\t\t\t \"%s, %s, \",\n\t\t\t\t\t\t quote_literal_cstr(quote_identifier(typeSchemaName)),\n\t\t\t\t\t\t quote_literal_cstr(typeName));\n\t}\n\telse if (typeName != NULL)\n\t{\n\t\tappendStringInfo(insertColocationCommand,\n\t\t\t\t\t\t \"NULL, %s, \",\n\t\t\t\t\t\t quote_literal_cstr(typeName));\n\t}\n\telse\n\t{\n\t\tappendStringInfo(insertColocationCommand,\n\t\t\t\t\t\t \"NULL, NULL, \");\n\t}\n\n\tappendStringInfo(insertColocationCommand,\n\t\t\t\t\t \"%s)) \"\n\t\t\t\t\t \"SELECT citus_internal.add_colocation_metadata(\"\n\t\t\t\t\t \"colocationid, shardcount, replicationfactor, \"\n\t\t\t\t\t \"coalesce(t.oid, 0), collationid) \"\n\t\t\t\t\t \"FROM colocation_data \"\n\t\t\t\t\t \"LEFT JOIN pg_type t ON (\"\n\t\t\t\t\t \"typename = t.typname \"\n\t\t\t\t\t \"AND (typeschema IS NULL OR \"\n\t\t\t\t\t \"t.typnamespace = \"\n\t\t\t\t\t \"(SELECT oid FROM pg_namespace WHERE nspname = typeschema)))\",\n\t\t\t\t\t RemoteCollationIdExpression(distributionColumnCollation));\n\n\treturn insertColocationCommand->data;\n}\n\n\n/*\n * GetRemoteTypeName returns the unqualified name of a type.\n * Returns NULL for InvalidOid.\n */\nstatic char *\nGetRemoteTypeName(Oid typeId)\n{\n\tif (typeId == InvalidOid)\n\t{\n\t\treturn NULL;\n\t}\n\n\tHeapTuple typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeId));\n\tif (!HeapTupleIsValid(typeTuple))\n\t{\n\t\treturn NULL;\n\t}\n\n\tForm_pg_type typeForm = (Form_pg_type) GETSTRUCT(typeTuple);\n\tchar *typeName = pstrdup(NameStr(typeForm->typname));\n\n\tReleaseSysCache(typeTuple);\n\treturn typeName;\n}\n\n\n/*\n * GetRemoteTypeNamespace returns the schema name of a type.\n * Returns NULL for InvalidOid or types in pg_catalog.\n */\nstatic char *\nGetRemoteTypeNamespace(Oid typeId)\n{\n\tif (typeId == InvalidOid)\n\t{\n\t\treturn NULL;\n\t}\n\n\tHeapTuple typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeId));\n\tif (!HeapTupleIsValid(typeTuple))\n\t{\n\t\treturn NULL;\n\t}\n\n\tForm_pg_type typeForm = (Form_pg_type) GETSTRUCT(typeTuple);\n\tOid typeNamespace = typeForm->typnamespace;\n\n\tReleaseSysCache(typeTuple);\n\n\t/* Don't include schema for pg_catalog types for backward compatibility */\n\tif (typeNamespace == PG_CATALOG_NAMESPACE)\n\t{\n\t\treturn NULL;\n\t}\n\n\treturn get_namespace_name(typeNamespace);\n}\n\n\n/*\n * RemoteCollationIdExpression returns an expression in text form that can\n * be used to obtain the OID of a collation on a different node when included\n * in a query string.\n */\nstatic char *\nRemoteCollationIdExpression(Oid colocationId)\n{\n\t/* by default, use 0 (InvalidOid) */\n\tchar *expression = \"0\";\n\n\tif (colocationId != InvalidOid)\n\t{\n\t\tDatum collationIdDatum = ObjectIdGetDatum(colocationId);\n\t\tHeapTuple collationTuple = SearchSysCache1(COLLOID, collationIdDatum);\n\n\t\tif (HeapTupleIsValid(collationTuple))\n\t\t{\n\t\t\tForm_pg_collation collationform =\n\t\t\t\t(Form_pg_collation) GETSTRUCT(collationTuple);\n\t\t\tchar *collationName = NameStr(collationform->collname);\n\t\t\tchar *collationSchemaName = get_namespace_name(collationform->collnamespace);\n\t\t\tchar *qualifiedCollationName = quote_qualified_identifier(collationSchemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  collationName);\n\n\t\t\tStringInfo regcollationExpression = makeStringInfo();\n\t\t\tappendStringInfo(regcollationExpression,\n\t\t\t\t\t\t\t \"%s::regcollation\",\n\t\t\t\t\t\t\t quote_literal_cstr(qualifiedCollationName));\n\n\t\t\texpression = regcollationExpression->data;\n\t\t}\n\n\t\tReleaseSysCache(collationTuple);\n\t}\n\n\treturn expression;\n}\n\n\n/*\n * SyncDeleteColocationGroupToNodes deletes a pg_dist_colocation record from workers.\n */\nvoid\nSyncDeleteColocationGroupToNodes(uint32 colocationId)\n{\n\tchar *command = ColocationGroupDeleteCommand(colocationId);\n\n\t/*\n\t * We require superuser for all pg_dist_colocation operations because we have\n\t * no reasonable way of restricting access.\n\t */\n\tSendCommandToWorkersWithMetadataViaSuperUser(command);\n}\n\n\n/*\n * ColocationGroupDeleteCommand returns a command for deleting a colocation group.\n */\nstatic char *\nColocationGroupDeleteCommand(uint32 colocationId)\n{\n\tStringInfo deleteColocationCommand = makeStringInfo();\n\n\tappendStringInfo(deleteColocationCommand,\n\t\t\t\t\t \"SELECT citus_internal.delete_colocation_metadata(%d)\",\n\t\t\t\t\t colocationId);\n\n\treturn deleteColocationCommand->data;\n}\n\n\n/*\n * TenantSchemaInsertCommand returns a command to call\n * citus_internal_add_tenant_schema().\n */\nchar *\nTenantSchemaInsertCommand(Oid schemaId, uint32 colocationId)\n{\n\tStringInfo command = makeStringInfo();\n\tappendStringInfo(command,\n\t\t\t\t\t \"SELECT citus_internal.add_tenant_schema(%s, %u)\",\n\t\t\t\t\t RemoteSchemaIdExpressionById(schemaId), colocationId);\n\n\treturn command->data;\n}\n\n\n/*\n * TenantSchemaDeleteCommand returns a command to call\n * citus_internal_delete_tenant_schema().\n */\nchar *\nTenantSchemaDeleteCommand(char *schemaName)\n{\n\tStringInfo command = makeStringInfo();\n\tappendStringInfo(command,\n\t\t\t\t\t \"SELECT citus_internal.delete_tenant_schema(%s)\",\n\t\t\t\t\t RemoteSchemaIdExpressionByName(schemaName));\n\n\treturn command->data;\n}\n\n\n/*\n * UpdateNoneDistTableMetadataCommand returns a command to call\n * citus_internal_update_none_dist_table_metadata().\n */\nchar *\nUpdateNoneDistTableMetadataCommand(Oid relationId, char replicationModel,\n\t\t\t\t\t\t\t\t   uint32 colocationId, bool autoConverted)\n{\n\tStringInfo command = makeStringInfo();\n\tappendStringInfo(command,\n\t\t\t\t\t \"SELECT citus_internal.update_none_dist_table_metadata(%s, '%c', %u, %s)\",\n\t\t\t\t\t RemoteTableIdExpression(relationId), replicationModel, colocationId,\n\t\t\t\t\t autoConverted ? \"true\" : \"false\");\n\n\treturn command->data;\n}\n\n\n/*\n * AddPlacementMetadataCommand returns a command to call\n * citus_internal_add_placement_metadata().\n */\nchar *\nAddPlacementMetadataCommand(uint64 shardId, uint64 placementId,\n\t\t\t\t\t\t\tuint64 shardLength, int32 groupId)\n{\n\tStringInfo command = makeStringInfo();\n\tappendStringInfo(command,\n\t\t\t\t\t \"SELECT citus_internal.add_placement_metadata(%ld, %ld, %d, %ld)\",\n\t\t\t\t\t shardId, shardLength, groupId, placementId);\n\treturn command->data;\n}\n\n\n/*\n * DeletePlacementMetadataCommand returns a command to call\n * citus_internal_delete_placement_metadata().\n */\nchar *\nDeletePlacementMetadataCommand(uint64 placementId)\n{\n\tStringInfo command = makeStringInfo();\n\tappendStringInfo(command,\n\t\t\t\t\t \"SELECT citus_internal.delete_placement_metadata(%ld)\",\n\t\t\t\t\t placementId);\n\treturn command->data;\n}\n\n\n/*\n * RemoteSchemaIdExpressionById returns an expression in text form that\n * can be used to obtain the OID of the schema with given schema id on a\n * different node when included in a query string.\n */\nstatic char *\nRemoteSchemaIdExpressionById(Oid schemaId)\n{\n\tchar *schemaName = get_namespace_name(schemaId);\n\tif (schemaName == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"schema with OID %u does not exist\", schemaId)));\n\t}\n\n\treturn RemoteSchemaIdExpressionByName(schemaName);\n}\n\n\n/*\n * RemoteSchemaIdExpressionByName returns an expression in text form that\n * can be used to obtain the OID of the schema with given schema name on a\n * different node when included in a query string.\n */\nstatic char *\nRemoteSchemaIdExpressionByName(char *schemaName)\n{\n\tStringInfo regnamespaceExpr = makeStringInfo();\n\tappendStringInfo(regnamespaceExpr, \"%s::regnamespace\",\n\t\t\t\t\t quote_literal_cstr(quote_identifier(schemaName)));\n\n\treturn regnamespaceExpr->data;\n}\n\n\n/*\n * RemoteTableIdExpression returns an expression in text form that\n * can be used to obtain the OID of given table on a different node\n * when included in a query string.\n */\nstatic char *\nRemoteTableIdExpression(Oid relationId)\n{\n\tStringInfo regclassExpr = makeStringInfo();\n\tappendStringInfo(regclassExpr, \"%s::regclass\",\n\t\t\t\t\t quote_literal_cstr(generate_qualified_relation_name(relationId)));\n\n\treturn regclassExpr->data;\n}\n\n\n/*\n * SetMetadataSyncNodesFromNodeList sets list of nodes that needs to be metadata\n * synced among given node list into metadataSyncContext.\n */\nvoid\nSetMetadataSyncNodesFromNodeList(MetadataSyncContext *context, List *nodeList)\n{\n\t/* sync is disabled, then no nodes to sync */\n\tif (!EnableMetadataSync)\n\t{\n\t\treturn;\n\t}\n\n\tList *activatedWorkerNodeList = NIL;\n\n\tWorkerNode *node = NULL;\n\tforeach_declared_ptr(node, nodeList)\n\t{\n\t\tif (NodeIsPrimary(node))\n\t\t{\n\t\t\t/* warn if we have coordinator in nodelist */\n\t\t\tif (NodeIsCoordinator(node))\n\t\t\t{\n\t\t\t\tereport(NOTICE, (errmsg(\"%s:%d is the coordinator and already contains \"\n\t\t\t\t\t\t\t\t\t\t\"metadata, skipping syncing the metadata\",\n\t\t\t\t\t\t\t\t\t\tnode->workerName, node->workerPort)));\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tactivatedWorkerNodeList = lappend(activatedWorkerNodeList, node);\n\t\t}\n\t}\n\n\tcontext->activatedWorkerNodeList = activatedWorkerNodeList;\n}\n\n\n/*\n * EstablishAndSetMetadataSyncBareConnections establishes and sets\n * connections used throughout nontransactional metadata sync.\n */\nvoid\nEstablishAndSetMetadataSyncBareConnections(MetadataSyncContext *context)\n{\n\tAssert(MetadataSyncTransMode == METADATA_SYNC_NON_TRANSACTIONAL);\n\n\tint connectionFlags = REQUIRE_METADATA_CONNECTION;\n\n\t/* establish bare connections to activated worker nodes */\n\tList *bareConnectionList = NIL;\n\tWorkerNode *node = NULL;\n\tforeach_declared_ptr(node, context->activatedWorkerNodeList)\n\t{\n\t\tMultiConnection *connection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnode->workerPort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCurrentUserName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL);\n\n\t\tAssert(connection != NULL);\n\t\tForceConnectionCloseAtTransactionEnd(connection);\n\t\tbareConnectionList = lappend(bareConnectionList, connection);\n\t}\n\n\tcontext->activatedWorkerBareConnections = bareConnectionList;\n}\n\n\n/*\n * CreateMetadataSyncContext creates a context which contains worker connections\n * and a MemoryContext to be used throughout the metadata sync.\n *\n * If we collect commands, connections will not be established as caller's intent\n * is to collect sync commands.\n *\n * If the nodes are newly added before activation, we would not try to unset\n * metadatasynced in separate transaction during nontransactional metadatasync.\n */\nMetadataSyncContext *\nCreateMetadataSyncContext(List *nodeList, bool collectCommands,\n\t\t\t\t\t\t  bool nodesAddedInSameTransaction)\n{\n\t/* should be alive during local transaction during the sync */\n\tMemoryContext context = AllocSetContextCreate(TopTransactionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t  \"metadata_sync_context\",\n\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_SIZES);\n\n\tMetadataSyncContext *metadataSyncContext = (MetadataSyncContext *) palloc0(\n\t\tsizeof(MetadataSyncContext));\n\n\tmetadataSyncContext->context = context;\n\tmetadataSyncContext->transactionMode = MetadataSyncTransMode;\n\tmetadataSyncContext->collectCommands = collectCommands;\n\tmetadataSyncContext->collectedCommands = NIL;\n\tmetadataSyncContext->nodesAddedInSameTransaction = nodesAddedInSameTransaction;\n\n\t/* filter the nodes that needs to be activated from given node list */\n\tSetMetadataSyncNodesFromNodeList(metadataSyncContext, nodeList);\n\n\t/*\n\t * establish connections only for nontransactional mode to prevent connection\n\t * open-close for each command\n\t */\n\tif (!collectCommands && MetadataSyncTransMode == METADATA_SYNC_NON_TRANSACTIONAL)\n\t{\n\t\tEstablishAndSetMetadataSyncBareConnections(metadataSyncContext);\n\t}\n\n\t/* use 2PC coordinated transactions if we operate in transactional mode */\n\tif (MetadataSyncTransMode == METADATA_SYNC_TRANSACTIONAL)\n\t{\n\t\tUse2PCForCoordinatedTransaction();\n\t}\n\n\treturn metadataSyncContext;\n}\n\n\n/*\n * ResetMetadataSyncMemoryContext resets memory context inside metadataSyncContext, if\n * we are not collecting commands.\n */\nvoid\nResetMetadataSyncMemoryContext(MetadataSyncContext *context)\n{\n\tif (!MetadataSyncCollectsCommands(context))\n\t{\n\t\tMemoryContextReset(context->context);\n\t}\n}\n\n\n/*\n * MetadataSyncCollectsCommands returns whether context is used for collecting\n * commands instead of sending them to workers.\n */\nbool\nMetadataSyncCollectsCommands(MetadataSyncContext *context)\n{\n\treturn context->collectCommands;\n}\n\n\n/*\n * SendOrCollectCommandListToActivatedNodes sends the commands to the activated nodes with\n * bare connections inside metadatacontext or via coordinated connections.\n * Note that when context only collects commands, we add commands into the context\n * without sending the commands.\n */\nvoid\nSendOrCollectCommandListToActivatedNodes(MetadataSyncContext *context, List *commands)\n{\n\t/* do nothing if no commands */\n\tif (commands == NIL)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * do not send any command to workers if we collect commands.\n\t * Collect commands into metadataSyncContext's collected command\n\t * list.\n\t */\n\tif (MetadataSyncCollectsCommands(context))\n\t{\n\t\tcontext->collectedCommands = list_concat(context->collectedCommands, commands);\n\t\treturn;\n\t}\n\n\t/* send commands to new workers, the current user should be a superuser */\n\tAssert(superuser());\n\n\tif (context->transactionMode == METADATA_SYNC_TRANSACTIONAL)\n\t{\n\t\tList *workerNodes = context->activatedWorkerNodeList;\n\t\tSendMetadataCommandListToWorkerListInCoordinatedTransaction(workerNodes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCurrentUserName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommands);\n\t}\n\telse if (context->transactionMode == METADATA_SYNC_NON_TRANSACTIONAL)\n\t{\n\t\tList *workerConnections = context->activatedWorkerBareConnections;\n\t\tSendCommandListToWorkerListWithBareConnections(workerConnections, commands);\n\t}\n\telse\n\t{\n\t\tpg_unreachable();\n\t}\n}\n\n\n/*\n * SendOrCollectCommandListToMetadataNodes sends the commands to the metadata nodes with\n * bare connections inside metadatacontext or via coordinated connections.\n * Note that when context only collects commands, we add commands into the context\n * without sending the commands.\n */\nvoid\nSendOrCollectCommandListToMetadataNodes(MetadataSyncContext *context, List *commands)\n{\n\t/*\n\t * do not send any command to workers if we collect commands.\n\t * Collect commands into metadataSyncContext's collected command\n\t * list.\n\t */\n\tif (MetadataSyncCollectsCommands(context))\n\t{\n\t\tcontext->collectedCommands = list_concat(context->collectedCommands, commands);\n\t\treturn;\n\t}\n\n\t/* send commands to new workers, the current user should be a superuser */\n\tAssert(superuser());\n\n\tif (context->transactionMode == METADATA_SYNC_TRANSACTIONAL)\n\t{\n\t\tList *metadataNodes = TargetWorkerSetNodeList(NON_COORDINATOR_METADATA_NODES,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  RowShareLock);\n\t\tSendMetadataCommandListToWorkerListInCoordinatedTransaction(metadataNodes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCurrentUserName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommands);\n\t}\n\telse if (context->transactionMode == METADATA_SYNC_NON_TRANSACTIONAL)\n\t{\n\t\tSendBareCommandListToMetadataWorkers(commands);\n\t}\n\telse\n\t{\n\t\tpg_unreachable();\n\t}\n}\n\n\n/*\n * SendOrCollectCommandListToSingleNode sends the commands to the specific worker\n * indexed by nodeIdx with bare connection inside metadatacontext or via coordinated\n * connection. Note that when context only collects commands, we add commands into\n * the context without sending the commands.\n */\nvoid\nSendOrCollectCommandListToSingleNode(MetadataSyncContext *context, List *commands,\n\t\t\t\t\t\t\t\t\t int nodeIdx)\n{\n\t/*\n\t * Do not send any command to workers if we collect commands.\n\t * Collect commands into metadataSyncContext's collected command\n\t * list.\n\t */\n\tif (MetadataSyncCollectsCommands(context))\n\t{\n\t\tcontext->collectedCommands = list_concat(context->collectedCommands, commands);\n\t\treturn;\n\t}\n\n\t/* send commands to new workers, the current user should be a superuser */\n\tAssert(superuser());\n\n\tif (context->transactionMode == METADATA_SYNC_TRANSACTIONAL)\n\t{\n\t\tList *workerNodes = context->activatedWorkerNodeList;\n\t\tAssert(nodeIdx < list_length(workerNodes));\n\n\t\tWorkerNode *node = list_nth(workerNodes, nodeIdx);\n\t\tSendMetadataCommandListToWorkerListInCoordinatedTransaction(list_make1(node),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCurrentUserName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommands);\n\t}\n\telse if (context->transactionMode == METADATA_SYNC_NON_TRANSACTIONAL)\n\t{\n\t\tList *workerConnections = context->activatedWorkerBareConnections;\n\t\tAssert(nodeIdx < list_length(workerConnections));\n\n\t\tMultiConnection *workerConnection = list_nth(workerConnections, nodeIdx);\n\t\tList *connectionList = list_make1(workerConnection);\n\t\tSendCommandListToWorkerListWithBareConnections(connectionList, commands);\n\t}\n\telse\n\t{\n\t\tpg_unreachable();\n\t}\n}\n\n\n/*\n * WorkerDropAllShellTablesCommand returns command required to drop shell tables\n * from workers. When singleTransaction is false, we create transaction per shell\n * table. Otherwise, we drop all shell tables within single transaction.\n */\nchar *\nWorkerDropAllShellTablesCommand(bool singleTransaction)\n{\n\tchar *singleTransactionString = (singleTransaction) ? \"true\" : \"false\";\n\tStringInfo removeAllShellTablesCommand = makeStringInfo();\n\tappendStringInfo(removeAllShellTablesCommand, WORKER_DROP_ALL_SHELL_TABLES,\n\t\t\t\t\t singleTransactionString);\n\treturn removeAllShellTablesCommand->data;\n}\n\n\n/*\n * WorkerDropSequenceDependencyCommand returns command to drop sequence dependencies for\n * given table.\n */\nchar *\nWorkerDropSequenceDependencyCommand(Oid relationId)\n{\n\tchar *qualifiedTableName = generate_qualified_relation_name(relationId);\n\tStringInfo breakSequenceDepCommand = makeStringInfo();\n\tappendStringInfo(breakSequenceDepCommand,\n\t\t\t\t\t BREAK_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND,\n\t\t\t\t\t quote_literal_cstr(qualifiedTableName));\n\treturn breakSequenceDepCommand->data;\n}\n\n\n/*\n * PropagateNodeWideObjectsCommandList is called during node activation to\n * propagate any object that should be propagated for every node. These are\n * generally not linked to any distributed object but change system wide behaviour.\n */\nstatic List *\nPropagateNodeWideObjectsCommandList(void)\n{\n\t/* collect all commands */\n\tList *ddlCommands = NIL;\n\n\tif (EnableAlterRoleSetPropagation)\n\t{\n\t\t/*\n\t\t * Get commands for database and postgres wide settings. Since these settings are not\n\t\t * linked to any role that can be distributed we need to distribute them seperately\n\t\t */\n\t\tList *alterRoleSetCommands = GenerateAlterRoleSetCommandForRole(InvalidOid);\n\t\tddlCommands = list_concat(ddlCommands, alterRoleSetCommands);\n\t}\n\n\treturn ddlCommands;\n}\n\n\n/*\n * SyncDistributedObjects sync the distributed objects to the nodes in metadataSyncContext\n * with transactional or nontransactional mode according to transactionMode inside\n * metadataSyncContext.\n *\n * Transactions should be ordered like below:\n * - Nodewide objects (only roles for now),\n * - Deletion of sequence and shell tables and metadata entries\n * - All dependencies (e.g., types, schemas, sequences) and all shell distributed\n *   table and their pg_dist_xx metadata entries\n * - Inter relation between those shell tables\n *\n * Note that we do not create the distributed dependencies on the coordinator\n * since all the dependencies should be present in the coordinator already.\n */\nvoid\nSyncDistributedObjects(MetadataSyncContext *context)\n{\n\tif (context->activatedWorkerNodeList == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tEnsureSequentialModeMetadataOperations();\n\n\tAssert(ShouldPropagate());\n\n\t/* Send systemwide objects, only roles for now */\n\tSendNodeWideObjectsSyncCommands(context);\n\n\t/*\n\t * Break dependencies between sequences-shell tables, then remove shell tables,\n\t * and metadata tables respectively.\n\t * We should delete shell tables before metadata entries as we look inside\n\t * pg_dist_partition to figure out shell tables.\n\t */\n\tSendShellTableDeletionCommands(context);\n\tSendMetadataDeletionCommands(context);\n\n\t/*\n\t * Commands to insert pg_dist_colocation entries.\n\t * Replicating dist objects and their metadata depends on this step.\n\t */\n\tSendColocationMetadataCommands(context);\n\n\t/*\n\t * Replicate all objects of the pg_dist_object to the remote node and\n\t * create metadata entries for Citus tables (pg_dist_shard, pg_dist_shard_placement,\n\t * pg_dist_partition, pg_dist_object).\n\t */\n\tSendDependencyCreationCommands(context);\n\tSendDistTableMetadataCommands(context);\n\tSendDistObjectCommands(context);\n\n\t/*\n\t * Commands to insert pg_dist_schema entries.\n\t *\n\t * Need to be done after syncing distributed objects because the schemas\n\t * need to exist on the worker.\n\t */\n\tSendTenantSchemaMetadataCommands(context);\n\n\t/*\n\t * After creating each table, handle the inter table relationship between\n\t * those tables.\n\t */\n\tSendInterTableRelationshipCommands(context);\n}\n\n\n/*\n * SendNodeWideObjectsSyncCommands sends systemwide objects to workers with\n * transactional or nontransactional mode according to transactionMode inside\n * metadataSyncContext.\n */\nvoid\nSendNodeWideObjectsSyncCommands(MetadataSyncContext *context)\n{\n\t/* propagate node wide objects. It includes only roles for now. */\n\tList *commandList = PropagateNodeWideObjectsCommandList();\n\n\tif (commandList == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tcommandList = lcons(DISABLE_DDL_PROPAGATION, commandList);\n\tcommandList = lappend(commandList, ENABLE_DDL_PROPAGATION);\n\tSendOrCollectCommandListToActivatedNodes(context, commandList);\n}\n\n\n/*\n * SendShellTableDeletionCommands sends sequence, and shell table deletion\n * commands to workers with transactional or nontransactional mode according to\n * transactionMode inside metadataSyncContext.\n */\nvoid\nSendShellTableDeletionCommands(MetadataSyncContext *context)\n{\n\t/* break all sequence deps for citus tables */\n\tchar *breakSeqDepsCommand = BREAK_ALL_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND;\n\tSendOrCollectCommandListToActivatedNodes(context, list_make1(breakSeqDepsCommand));\n\n\t/* remove shell tables */\n\tbool singleTransaction = (context->transactionMode == METADATA_SYNC_TRANSACTIONAL);\n\tchar *dropShellTablesCommand = WorkerDropAllShellTablesCommand(singleTransaction);\n\tSendOrCollectCommandListToActivatedNodes(context, list_make1(dropShellTablesCommand));\n}\n\n\n/*\n * SendMetadataDeletionCommands sends metadata entry deletion commands to workers\n * with transactional or nontransactional mode according to transactionMode inside\n * metadataSyncContext.\n */\nvoid\nSendMetadataDeletionCommands(MetadataSyncContext *context)\n{\n\t/* remove pg_dist_partition entries */\n\tSendOrCollectCommandListToActivatedNodes(context, list_make1(DELETE_ALL_PARTITIONS));\n\n\t/* remove pg_dist_shard entries */\n\tSendOrCollectCommandListToActivatedNodes(context, list_make1(DELETE_ALL_SHARDS));\n\n\t/* remove pg_dist_placement entries */\n\tSendOrCollectCommandListToActivatedNodes(context, list_make1(DELETE_ALL_PLACEMENTS));\n\n\t/* remove pg_dist_object entries */\n\tSendOrCollectCommandListToActivatedNodes(context,\n\t\t\t\t\t\t\t\t\t\t\t list_make1(DELETE_ALL_DISTRIBUTED_OBJECTS));\n\n\t/* remove pg_dist_colocation entries */\n\tSendOrCollectCommandListToActivatedNodes(context, list_make1(DELETE_ALL_COLOCATION));\n\n\t/* remove pg_dist_schema entries */\n\tSendOrCollectCommandListToActivatedNodes(context,\n\t\t\t\t\t\t\t\t\t\t\t list_make1(DELETE_ALL_TENANT_SCHEMAS));\n}\n\n\n/*\n * SendColocationMetadataCommands sends colocation metadata with transactional or\n * nontransactional mode according to transactionMode inside metadataSyncContext.\n */\nvoid\nSendColocationMetadataCommands(MetadataSyncContext *context)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 0;\n\n\tRelation relation = table_open(DistColocationRelationId(), AccessShareLock);\n\tSysScanDesc scanDesc = systable_beginscan(relation, InvalidOid, false, NULL,\n\t\t\t\t\t\t\t\t\t\t\t  scanKeyCount, scanKey);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(context->context);\n\tHeapTuple nextTuple = NULL;\n\twhile (true)\n\t{\n\t\tResetMetadataSyncMemoryContext(context);\n\n\t\tnextTuple = systable_getnext(scanDesc);\n\t\tif (!HeapTupleIsValid(nextTuple))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tStringInfo colocationGroupCreateCommand = makeStringInfo();\n\t\tappendStringInfo(colocationGroupCreateCommand,\n\t\t\t\t\t\t \"WITH colocation_group_data (colocationid, shardcount, \"\n\t\t\t\t\t\t \"replicationfactor, distributioncolumntypeschema, \"\n\t\t\t\t\t\t \"distributioncolumntypename, \"\n\t\t\t\t\t\t \"distributioncolumncollationname, \"\n\t\t\t\t\t\t \"distributioncolumncollationschema)  AS (VALUES \");\n\n\t\tForm_pg_dist_colocation colocationForm =\n\t\t\t(Form_pg_dist_colocation) GETSTRUCT(nextTuple);\n\n\t\t/*\n\t\t * Get the type name and schema separately to defer type resolution.\n\t\t * This is necessary when the type (e.g., a domain) is defined in a\n\t\t * non-public schema that may not exist on the worker yet.\n\t\t */\n\t\tchar *typeName =\n\t\t\tGetRemoteTypeName(colocationForm->distributioncolumntype);\n\t\tchar *typeSchemaName =\n\t\t\tGetRemoteTypeNamespace(colocationForm->distributioncolumntype);\n\n\t\tappendStringInfo(colocationGroupCreateCommand,\n\t\t\t\t\t\t \"(%d, %d, %d, \",\n\t\t\t\t\t\t colocationForm->colocationid,\n\t\t\t\t\t\t colocationForm->shardcount,\n\t\t\t\t\t\t colocationForm->replicationfactor);\n\n\t\t/* Add type schema and name */\n\t\tif (typeSchemaName != NULL && typeName != NULL)\n\t\t{\n\t\t\t/* Use quote_identifier so the schema name can be cast to regnamespace */\n\t\t\tappendStringInfo(colocationGroupCreateCommand,\n\t\t\t\t\t\t\t \"%s, %s, \",\n\t\t\t\t\t\t\t quote_literal_cstr(quote_identifier(typeSchemaName)),\n\t\t\t\t\t\t\t quote_literal_cstr(typeName));\n\t\t}\n\t\telse if (typeName != NULL)\n\t\t{\n\t\t\t/* Type is in pg_catalog or no schema qualifier needed */\n\t\t\tappendStringInfo(colocationGroupCreateCommand,\n\t\t\t\t\t\t\t \"NULL, %s, \",\n\t\t\t\t\t\t\t quote_literal_cstr(typeName));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* InvalidOid or unknown type */\n\t\t\tappendStringInfo(colocationGroupCreateCommand,\n\t\t\t\t\t\t\t \"NULL, NULL, \");\n\t\t}\n\n\t\t/*\n\t\t * For collations, include the names in the VALUES section and then\n\t\t * join with pg_collation.\n\t\t */\n\t\tOid distributionColumCollation = colocationForm->distributioncolumncollation;\n\t\tif (distributionColumCollation != InvalidOid)\n\t\t{\n\t\t\tDatum collationIdDatum = ObjectIdGetDatum(distributionColumCollation);\n\t\t\tHeapTuple collationTuple = SearchSysCache1(COLLOID, collationIdDatum);\n\t\t\tif (HeapTupleIsValid(collationTuple))\n\t\t\t{\n\t\t\t\tForm_pg_collation collationform =\n\t\t\t\t\t(Form_pg_collation) GETSTRUCT(collationTuple);\n\t\t\t\tchar *collationName = NameStr(collationform->collname);\n\t\t\t\tchar *collationSchemaName =\n\t\t\t\t\tget_namespace_name(collationform->collnamespace);\n\t\t\t\tappendStringInfo(colocationGroupCreateCommand,\n\t\t\t\t\t\t\t\t \"%s, %s)\",\n\t\t\t\t\t\t\t\t quote_literal_cstr(collationName),\n\t\t\t\t\t\t\t\t quote_literal_cstr(collationSchemaName));\n\t\t\t\tReleaseSysCache(collationTuple);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(colocationGroupCreateCommand,\n\t\t\t\t\t\t\t\t \"NULL, NULL)\");\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(colocationGroupCreateCommand,\n\t\t\t\t\t\t\t \"NULL, NULL)\");\n\t\t}\n\n\t\t/*\n\t\t * Use LEFT JOIN with pg_type to resolve the type OID at runtime.\n\t\t * This defers type resolution until execution on the worker, allowing\n\t\t * the type and its schema to be created first by dependency commands.\n\t\t */\n\t\tappendStringInfo(colocationGroupCreateCommand,\n\t\t\t\t\t\t \") SELECT citus_internal.add_colocation_metadata(\"\n\t\t\t\t\t\t \"colocationid, shardcount, replicationfactor, \"\n\t\t\t\t\t\t \"coalesce(t.oid, 0), coalesce(c.oid, 0)) \"\n\t\t\t\t\t\t \"FROM colocation_group_data d \"\n\t\t\t\t\t\t \"LEFT JOIN pg_type t ON (\"\n\t\t\t\t\t\t \"d.distributioncolumntypename = t.typname \"\n\t\t\t\t\t\t \"AND (d.distributioncolumntypeschema IS NULL OR \"\n\t\t\t\t\t\t \"t.typnamespace = (SELECT oid FROM pg_namespace WHERE \"\n\t\t\t\t\t\t \"nspname = d.distributioncolumntypeschema))) \"\n\t\t\t\t\t\t \"LEFT JOIN pg_collation c \"\n\t\t\t\t\t\t \"ON (d.distributioncolumncollationname = c.collname \"\n\t\t\t\t\t\t \"AND c.collnamespace = (SELECT oid FROM pg_namespace WHERE \"\n\t\t\t\t\t\t \"nspname = d.distributioncolumncollationschema))\");\n\n\t\tList *commandList = list_make1(colocationGroupCreateCommand->data);\n\t\tSendOrCollectCommandListToActivatedNodes(context, commandList);\n\t}\n\tMemoryContextSwitchTo(oldContext);\n\n\tsystable_endscan(scanDesc);\n\ttable_close(relation, AccessShareLock);\n}\n\n\n/*\n * SendTenantSchemaMetadataCommands sends tenant schema metadata entries with\n * transactional or nontransactional mode according to transactionMode inside\n * metadataSyncContext.\n */\nvoid\nSendTenantSchemaMetadataCommands(MetadataSyncContext *context)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 0;\n\n\tRelation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(),\n\t\t\t\t\t\t\t\t\t\t\t AccessShareLock);\n\tSysScanDesc scanDesc = systable_beginscan(pgDistTenantSchema, InvalidOid, false, NULL,\n\t\t\t\t\t\t\t\t\t\t\t  scanKeyCount, scanKey);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(context->context);\n\tHeapTuple heapTuple = NULL;\n\twhile (true)\n\t{\n\t\tResetMetadataSyncMemoryContext(context);\n\n\t\theapTuple = systable_getnext(scanDesc);\n\t\tif (!HeapTupleIsValid(heapTuple))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tForm_pg_dist_schema tenantSchemaForm =\n\t\t\t(Form_pg_dist_schema) GETSTRUCT(heapTuple);\n\n\t\tStringInfo insertTenantSchemaCommand = makeStringInfo();\n\t\tappendStringInfo(insertTenantSchemaCommand,\n\t\t\t\t\t\t \"SELECT citus_internal.add_tenant_schema(%s, %u)\",\n\t\t\t\t\t\t RemoteSchemaIdExpressionById(tenantSchemaForm->schemaid),\n\t\t\t\t\t\t tenantSchemaForm->colocationid);\n\n\t\tList *commandList = list_make1(insertTenantSchemaCommand->data);\n\t\tSendOrCollectCommandListToActivatedNodes(context, commandList);\n\t}\n\tMemoryContextSwitchTo(oldContext);\n\n\tsystable_endscan(scanDesc);\n\ttable_close(pgDistTenantSchema, AccessShareLock);\n}\n\n\n/*\n * SendDependencyCreationCommands sends dependency creation commands to workers\n * with transactional or nontransactional mode according to transactionMode\n * inside metadataSyncContext.\n */\nvoid\nSendDependencyCreationCommands(MetadataSyncContext *context)\n{\n\t/* disable ddl propagation */\n\tSendOrCollectCommandListToActivatedNodes(context,\n\t\t\t\t\t\t\t\t\t\t\t list_make1(DISABLE_DDL_PROPAGATION));\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(context->context);\n\n\t/* collect all dependencies in creation order and get their ddl commands */\n\tList *dependencies = GetDistributedObjectAddressList();\n\n\t/*\n\t * Depending on changes in the environment, such as the enable_metadata_sync guc\n\t * there might be objects in the distributed object address list that should currently\n\t * not be propagated by citus as they are 'not supported'.\n\t */\n\tdependencies = FilterObjectAddressListByPredicate(dependencies,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  &SupportedDependencyByCitus);\n\n\tdependencies = OrderObjectAddressListInDependencyOrder(dependencies);\n\n\t/*\n\t * We need to create a subcontext as we reset the context after each dependency\n\t * creation but we want to preserve all dependency objects at metadataSyncContext.\n\t */\n\tMemoryContext commandsContext = AllocSetContextCreate(context->context,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"dependency commands context\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_SIZES);\n\tMemoryContextSwitchTo(commandsContext);\n\tObjectAddress *dependency = NULL;\n\tforeach_declared_ptr(dependency, dependencies)\n\t{\n\t\tif (!MetadataSyncCollectsCommands(context))\n\t\t{\n\t\t\tMemoryContextReset(commandsContext);\n\t\t}\n\n\t\tif (IsAnyObjectAddressOwnedByExtension(list_make1(dependency), NULL))\n\t\t{\n\t\t\t/*\n\t\t\t * We expect extension-owned objects to be created as a result\n\t\t\t * of the extension being created.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* dependency creation commands */\n\t\tList *ddlCommands = GetAllDependencyCreateDDLCommands(list_make1(dependency));\n\t\tSendOrCollectCommandListToActivatedNodes(context, ddlCommands);\n\t}\n\tMemoryContextSwitchTo(oldContext);\n\n\tif (!MetadataSyncCollectsCommands(context))\n\t{\n\t\tMemoryContextDelete(commandsContext);\n\t}\n\tResetMetadataSyncMemoryContext(context);\n\n\t/* enable ddl propagation */\n\tSendOrCollectCommandListToActivatedNodes(context, list_make1(ENABLE_DDL_PROPAGATION));\n}\n\n\n/*\n * SendDistTableMetadataCommands sends commands related to pg_dist_shard and,\n * pg_dist_shard_placement entries to workers with transactional or nontransactional\n * mode according to transactionMode inside metadataSyncContext.\n */\nvoid\nSendDistTableMetadataCommands(MetadataSyncContext *context)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 0;\n\n\tRelation relation = table_open(DistPartitionRelationId(), AccessShareLock);\n\tTupleDesc tupleDesc = RelationGetDescr(relation);\n\n\tSysScanDesc scanDesc = systable_beginscan(relation, InvalidOid, false, NULL,\n\t\t\t\t\t\t\t\t\t\t\t  scanKeyCount, scanKey);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(context->context);\n\tHeapTuple nextTuple = NULL;\n\twhile (true)\n\t{\n\t\tResetMetadataSyncMemoryContext(context);\n\n\t\tnextTuple = systable_getnext(scanDesc);\n\t\tif (!HeapTupleIsValid(nextTuple))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\t/*\n\t\t * Create Citus table metadata commands (pg_dist_shard, pg_dist_shard_placement,\n\t\t * pg_dist_partition). Only Citus tables have shard metadata.\n\t\t */\n\t\tOid relationId = FetchRelationIdFromPgPartitionHeapTuple(nextTuple, tupleDesc);\n\t\tif (!ShouldSyncTableMetadata(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tList *commandList = CitusTableMetadataCreateCommandList(relationId);\n\t\tSendOrCollectCommandListToActivatedNodes(context, commandList);\n\t}\n\tMemoryContextSwitchTo(oldContext);\n\n\tsystable_endscan(scanDesc);\n\ttable_close(relation, AccessShareLock);\n}\n\n\n/*\n * SendDistObjectCommands sends commands related to pg_dist_object entries to\n * workers with transactional or nontransactional mode according to transactionMode\n * inside metadataSyncContext.\n */\nvoid\nSendDistObjectCommands(MetadataSyncContext *context)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 0;\n\n\tRelation relation = table_open(DistObjectRelationId(), AccessShareLock);\n\tTupleDesc tupleDesc = RelationGetDescr(relation);\n\n\tSysScanDesc scanDesc = systable_beginscan(relation, InvalidOid, false, NULL,\n\t\t\t\t\t\t\t\t\t\t\t  scanKeyCount, scanKey);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(context->context);\n\tHeapTuple nextTuple = NULL;\n\twhile (true)\n\t{\n\t\tResetMetadataSyncMemoryContext(context);\n\n\t\tnextTuple = systable_getnext(scanDesc);\n\t\tif (!HeapTupleIsValid(nextTuple))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tForm_pg_dist_object pg_dist_object = (Form_pg_dist_object) GETSTRUCT(nextTuple);\n\n\t\tObjectAddress *address = palloc(sizeof(ObjectAddress));\n\n\t\tObjectAddressSubSet(*address, pg_dist_object->classid, pg_dist_object->objid,\n\t\t\t\t\t\t\tpg_dist_object->objsubid);\n\n\t\tbool distributionArgumentIndexIsNull = false;\n\t\tDatum distributionArgumentIndexDatum =\n\t\t\theap_getattr(nextTuple,\n\t\t\t\t\t\t Anum_pg_dist_object_distribution_argument_index,\n\t\t\t\t\t\t tupleDesc,\n\t\t\t\t\t\t &distributionArgumentIndexIsNull);\n\t\tint32 distributionArgumentIndex = DatumGetInt32(distributionArgumentIndexDatum);\n\n\t\tbool colocationIdIsNull = false;\n\t\tDatum colocationIdDatum =\n\t\t\theap_getattr(nextTuple,\n\t\t\t\t\t\t Anum_pg_dist_object_colocationid,\n\t\t\t\t\t\t tupleDesc,\n\t\t\t\t\t\t &colocationIdIsNull);\n\t\tint32 colocationId = DatumGetInt32(colocationIdDatum);\n\n\t\tbool forceDelegationIsNull = false;\n\t\tDatum forceDelegationDatum =\n\t\t\theap_getattr(nextTuple,\n\t\t\t\t\t\t GetForceDelegationAttrIndexInPgDistObject(tupleDesc) + 1,\n\t\t\t\t\t\t tupleDesc,\n\t\t\t\t\t\t &forceDelegationIsNull);\n\t\tbool forceDelegation = DatumGetBool(forceDelegationDatum);\n\n\t\tif (distributionArgumentIndexIsNull)\n\t\t{\n\t\t\tdistributionArgumentIndex = INVALID_DISTRIBUTION_ARGUMENT_INDEX;\n\t\t}\n\n\t\tif (colocationIdIsNull)\n\t\t{\n\t\t\tcolocationId = INVALID_COLOCATION_ID;\n\t\t}\n\n\t\tif (forceDelegationIsNull)\n\t\t{\n\t\t\tforceDelegation = NO_FORCE_PUSHDOWN;\n\t\t}\n\n\t\tchar *workerMetadataUpdateCommand =\n\t\t\tMarkObjectsDistributedCreateCommand(list_make1(address),\n\t\t\t\t\t\t\t\t\t\t\t\tNIL,\n\t\t\t\t\t\t\t\t\t\t\t\tlist_make1_int(distributionArgumentIndex),\n\t\t\t\t\t\t\t\t\t\t\t\tlist_make1_int(colocationId),\n\t\t\t\t\t\t\t\t\t\t\t\tlist_make1_int(forceDelegation));\n\t\tSendOrCollectCommandListToActivatedNodes(context,\n\t\t\t\t\t\t\t\t\t\t\t\t list_make1(workerMetadataUpdateCommand));\n\t}\n\tMemoryContextSwitchTo(oldContext);\n\n\tsystable_endscan(scanDesc);\n\trelation_close(relation, NoLock);\n}\n\n\n/*\n * SendInterTableRelationshipCommands sends inter-table relationship commands\n * (e.g. constraints, attach partitions) to workers with transactional or\n * nontransactional mode per inter table relationship according to transactionMode\n * inside metadataSyncContext.\n */\nvoid\nSendInterTableRelationshipCommands(MetadataSyncContext *context)\n{\n\t/* disable ddl propagation */\n\tSendOrCollectCommandListToActivatedNodes(context,\n\t\t\t\t\t\t\t\t\t\t\t list_make1(DISABLE_DDL_PROPAGATION));\n\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 0;\n\n\tRelation relation = table_open(DistPartitionRelationId(), AccessShareLock);\n\tTupleDesc tupleDesc = RelationGetDescr(relation);\n\n\tSysScanDesc scanDesc = systable_beginscan(relation, InvalidOid, false, NULL,\n\t\t\t\t\t\t\t\t\t\t\t  scanKeyCount, scanKey);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(context->context);\n\tHeapTuple nextTuple = NULL;\n\twhile (true)\n\t{\n\t\tResetMetadataSyncMemoryContext(context);\n\n\t\tnextTuple = systable_getnext(scanDesc);\n\t\tif (!HeapTupleIsValid(nextTuple))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tOid relationId = FetchRelationIdFromPgPartitionHeapTuple(nextTuple, tupleDesc);\n\t\tif (!ShouldSyncTableMetadata(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Skip foreign key and partition creation when the Citus table is\n\t\t * owned by an extension.\n\t\t */\n\t\tif (IsTableOwnedByExtension(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tList *commandList = InterTableRelationshipOfRelationCommandList(relationId);\n\t\tSendOrCollectCommandListToActivatedNodes(context, commandList);\n\t}\n\tMemoryContextSwitchTo(oldContext);\n\n\tsystable_endscan(scanDesc);\n\ttable_close(relation, AccessShareLock);\n\n\t/* enable ddl propagation */\n\tSendOrCollectCommandListToActivatedNodes(context, list_make1(ENABLE_DDL_PROPAGATION));\n}\n"
  },
  {
    "path": "src/backend/distributed/metadata/metadata_utility.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * metadata_utility.c\n *    Routines for reading and modifying master node's metadata.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <sys/statvfs.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"access/sysattr.h\"\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/index.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_extension.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_proc_d.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/extension.h\"\n#include \"commands/sequence.h\"\n#include \"nodes/makefuncs.h\"\n#include \"parser/scansup.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/procarray.h\"\n#include \"utils/acl.h\"\n#include \"utils/builtins.h\"\n#include \"utils/datum.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/fmgrprotos.h\"\n#include \"utils/inval.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/background_jobs.h\"\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/pg_dist_background_job.h\"\n#include \"distributed/pg_dist_background_task.h\"\n#include \"distributed/pg_dist_backrgound_task_depend.h\"\n#include \"distributed/pg_dist_colocation.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/pg_dist_placement.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_rebalancer.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_protocol.h\"\n\n#define DISK_SPACE_FIELDS 2\n\n/* Local functions forward declarations */\nstatic uint64 * AllocateUint64(uint64 value);\nstatic void RecordDistributedRelationDependencies(Oid distributedRelationId);\nstatic GroupShardPlacement * TupleToGroupShardPlacement(TupleDesc tupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tHeapTuple heapTuple);\nstatic bool DistributedRelationSize(Oid relationId, SizeQueryType sizeQueryType,\n\t\t\t\t\t\t\t\t\tbool failOnError, uint64 *relationSize);\nstatic bool DistributedRelationSizeOnWorker(WorkerNode *workerNode, Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\tSizeQueryType sizeQueryType, bool failOnError,\n\t\t\t\t\t\t\t\t\t\t\tuint64 *relationSize);\nstatic List * ShardIntervalsOnWorkerGroup(WorkerNode *workerNode, Oid relationId);\nstatic char * GenerateShardIdNameValuesForShardList(List *shardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbool firstValue);\nstatic char * GenerateSizeQueryForRelationNameList(List *quotedShardNames,\n\t\t\t\t\t\t\t\t\t\t\t\t   char *sizeFunction);\nstatic char * GetWorkerPartitionedSizeUDFNameBySizeQueryType(SizeQueryType sizeQueryType);\nstatic char * GetSizeQueryBySizeQueryType(SizeQueryType sizeQueryType);\nstatic char * GenerateAllShardStatisticsQueryForNode(WorkerNode *workerNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t List *citusTableIds);\nstatic List * GenerateShardStatisticsQueryList(List *workerNodeList, List *citusTableIds);\nstatic void ErrorIfNotSuitableToGetSize(Oid relationId);\nstatic List * OpenConnectionToNodes(List *workerNodeList);\nstatic void ReceiveShardIdAndSizeResults(List *connectionList,\n\t\t\t\t\t\t\t\t\t\t Tuplestorestate *tupleStore,\n\t\t\t\t\t\t\t\t\t\t TupleDesc tupleDescriptor);\nstatic void AppendShardIdNameValues(StringInfo selectQuery, ShardInterval *shardInterval);\n\nstatic HeapTuple CreateDiskSpaceTuple(TupleDesc tupleDesc, uint64 availableBytes,\n\t\t\t\t\t\t\t\t\t  uint64 totalBytes);\nstatic bool GetLocalDiskSpaceStats(uint64 *availableBytes, uint64 *totalBytes);\nstatic BackgroundTask * DeformBackgroundTaskHeapTuple(TupleDesc tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  HeapTuple taskTuple);\n\nstatic bool SetFieldValue(int attno, Datum values[], bool isnull[], bool replace[],\n\t\t\t\t\t\t  Datum newValue);\nstatic bool SetFieldText(int attno, Datum values[], bool isnull[], bool replace[],\n\t\t\t\t\t\t const char *newValue);\nstatic bool SetFieldNull(int attno, Datum values[], bool isnull[], bool replace[]);\n\n#define InitFieldValue(attno, values, isnull, initValue) \\\n\t\t(void) SetFieldValue((attno), (values), (isnull), NULL, (initValue))\n#define InitFieldText(attno, values, isnull, initValue) \\\n\t\t(void) SetFieldText((attno), (values), (isnull), NULL, (initValue))\n#define InitFieldNull(attno, values, isnull) \\\n\t\t(void) SetFieldNull((attno), (values), (isnull), NULL)\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(citus_local_disk_space_stats);\nPG_FUNCTION_INFO_V1(citus_table_size);\nPG_FUNCTION_INFO_V1(citus_total_relation_size);\nPG_FUNCTION_INFO_V1(citus_relation_size);\nPG_FUNCTION_INFO_V1(citus_shard_sizes);\n\n\n/*\n * CreateDiskSpaceTuple creates a tuple that is used as the return value\n * for citus_local_disk_space_stats.\n */\nstatic HeapTuple\nCreateDiskSpaceTuple(TupleDesc tupleDescriptor, uint64 availableBytes, uint64 totalBytes)\n{\n\tDatum values[DISK_SPACE_FIELDS];\n\tbool isNulls[DISK_SPACE_FIELDS];\n\n\t/* form heap tuple for remote disk space statistics */\n\tmemset(values, 0, sizeof(values));\n\tmemset(isNulls, false, sizeof(isNulls));\n\n\tvalues[0] = UInt64GetDatum(availableBytes);\n\tvalues[1] = UInt64GetDatum(totalBytes);\n\n\tHeapTuple diskSpaceTuple = heap_form_tuple(tupleDescriptor, values, isNulls);\n\n\treturn diskSpaceTuple;\n}\n\n\n/*\n * citus_local_disk_space_stats returns total disk space and available disk\n * space for the disk that contains PGDATA.\n */\nDatum\ncitus_local_disk_space_stats(PG_FUNCTION_ARGS)\n{\n\tuint64 availableBytes = 0;\n\tuint64 totalBytes = 0;\n\n\tif (!GetLocalDiskSpaceStats(&availableBytes, &totalBytes))\n\t{\n\t\tereport(WARNING, (errmsg(\"could not get disk space\")));\n\t}\n\n\tTupleDesc tupleDescriptor = NULL;\n\n\tTypeFuncClass resultTypeClass = get_call_result_type(fcinfo, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t &tupleDescriptor);\n\tif (resultTypeClass != TYPEFUNC_COMPOSITE)\n\t{\n\t\tereport(ERROR, (errmsg(\"return type must be a row type\")));\n\t}\n\n\tHeapTuple diskSpaceTuple = CreateDiskSpaceTuple(tupleDescriptor, availableBytes,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttotalBytes);\n\n\tPG_RETURN_DATUM(HeapTupleGetDatum(diskSpaceTuple));\n}\n\n\n/*\n * GetLocalDiskSpaceStats returns total and available disk space for the disk containing\n * PGDATA (not considering tablespaces, quota).\n */\nstatic bool\nGetLocalDiskSpaceStats(uint64 *availableBytes, uint64 *totalBytes)\n{\n\tstruct statvfs buffer;\n\tif (statvfs(DataDir, &buffer) != 0)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * f_bfree: number of free blocks\n\t * f_frsize: fragment size, same as f_bsize usually\n\t * f_blocks: Size of fs in f_frsize units\n\t */\n\t*availableBytes = buffer.f_bfree * buffer.f_frsize;\n\t*totalBytes = buffer.f_blocks * buffer.f_frsize;\n\n\treturn true;\n}\n\n\n/*\n * GetNodeDiskSpaceStatsForConnection fetches the disk space statistics for the node\n * that is on the given connection, or returns false if unsuccessful.\n */\nbool\nGetNodeDiskSpaceStatsForConnection(MultiConnection *connection, uint64 *availableBytes,\n\t\t\t\t\t\t\t\t   uint64 *totalBytes)\n{\n\tPGresult *result = NULL;\n\n\tchar *sizeQuery = \"SELECT available_disk_size, total_disk_size \"\n\t\t\t\t\t  \"FROM pg_catalog.citus_local_disk_space_stats()\";\n\n\n\tint queryResult = ExecuteOptionalRemoteCommand(connection, sizeQuery, &result);\n\tif (queryResult != RESPONSE_OKAY || !IsResponseOK(result) || PQntuples(result) != 1)\n\t{\n\t\tereport(WARNING, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t  errmsg(\"cannot get the disk space statistics for node %s:%d\",\n\t\t\t\t\t\t\t\t connection->hostname, connection->port)));\n\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\n\t\treturn false;\n\t}\n\n\tchar *availableBytesString = PQgetvalue(result, 0, 0);\n\tchar *totalBytesString = PQgetvalue(result, 0, 1);\n\n\t*availableBytes = SafeStringToUint64(availableBytesString);\n\t*totalBytes = SafeStringToUint64(totalBytesString);\n\n\tPQclear(result);\n\tForgetResults(connection);\n\n\treturn true;\n}\n\n\n/*\n * citus_shard_sizes returns all shard ids and their sizes.\n */\nDatum\ncitus_shard_sizes(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tList *allCitusTableIds = AllCitusTableIds();\n\n\t/* we don't need a distributed transaction here */\n\tbool useDistributedTransaction = false;\n\n\tList *connectionList =\n\t\tSendShardStatisticsQueriesInParallel(allCitusTableIds, useDistributedTransaction);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tReceiveShardIdAndSizeResults(connectionList, tupleStore, tupleDescriptor);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_total_relation_size accepts a distributed table name and returns a distributed table\n * and its indexes' total relation size.\n */\nDatum\ncitus_total_relation_size(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\tbool failOnError = PG_GETARG_BOOL(1);\n\n\tSizeQueryType sizeQueryType = TOTAL_RELATION_SIZE;\n\tuint64 relationSize = 0;\n\n\tif (!DistributedRelationSize(relationId, sizeQueryType, failOnError, &relationSize))\n\t{\n\t\tAssert(!failOnError);\n\t\tPG_RETURN_NULL();\n\t}\n\n\tPG_RETURN_INT64(relationSize);\n}\n\n\n/*\n * citus_table_size accepts a distributed table name and returns a distributed table's total\n * relation size.\n */\nDatum\ncitus_table_size(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\tbool failOnError = true;\n\tSizeQueryType sizeQueryType = TABLE_SIZE;\n\tuint64 relationSize = 0;\n\n\t/* We do not check if relation is really a table, like PostgreSQL is doing. */\n\tif (!DistributedRelationSize(relationId, sizeQueryType, failOnError, &relationSize))\n\t{\n\t\tAssert(!failOnError);\n\t\tPG_RETURN_NULL();\n\t}\n\n\tPG_RETURN_INT64(relationSize);\n}\n\n\n/*\n * citus_relation_size accept a distributed relation name and returns a relation's 'main'\n * fork's size.\n *\n * Input relation is allowed to be an index on a distributed table too.\n */\nDatum\ncitus_relation_size(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\tbool failOnError = true;\n\tSizeQueryType sizeQueryType = RELATION_SIZE;\n\tuint64 relationSize = 0;\n\n\tif (!DistributedRelationSize(relationId, sizeQueryType, failOnError, &relationSize))\n\t{\n\t\tAssert(!failOnError);\n\t\tPG_RETURN_NULL();\n\t}\n\n\tPG_RETURN_INT64(relationSize);\n}\n\n\n/*\n * SendShardStatisticsQueriesInParallel generates query lists for obtaining shard\n * statistics and then sends the commands in parallel by opening connections\n * to available nodes. It returns the connection list.\n */\nList *\nSendShardStatisticsQueriesInParallel(List *citusTableIds, bool useDistributedTransaction)\n{\n\tList *workerNodeList = ActivePrimaryNodeList(NoLock);\n\n\tList *shardSizesQueryList = GenerateShardStatisticsQueryList(workerNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t citusTableIds);\n\n\tList *connectionList = OpenConnectionToNodes(workerNodeList);\n\tFinishConnectionListEstablishment(connectionList);\n\n\tif (useDistributedTransaction)\n\t{\n\t\t/*\n\t\t * For now, in the case we want to include shard min and max values, we also\n\t\t * want to update the entries in pg_dist_placement and pg_dist_shard with the\n\t\t * latest statistics. In order to detect distributed deadlocks, we assign a\n\t\t * distributed transaction ID to the current transaction\n\t\t */\n\t\tUseCoordinatedTransaction();\n\t}\n\n\t/* send commands in parallel */\n\tfor (int i = 0; i < list_length(connectionList); i++)\n\t{\n\t\tMultiConnection *connection = (MultiConnection *) list_nth(connectionList, i);\n\t\tchar *shardSizesQuery = (char *) list_nth(shardSizesQueryList, i);\n\n\t\tif (useDistributedTransaction)\n\t\t{\n\t\t\t/* run the size query in a distributed transaction */\n\t\t\tRemoteTransactionBeginIfNecessary(connection);\n\t\t}\n\n\t\tint querySent = SendRemoteCommand(connection, shardSizesQuery);\n\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tReportConnectionError(connection, WARNING);\n\t\t}\n\t}\n\treturn connectionList;\n}\n\n\n/*\n * OpenConnectionToNodes opens a single connection per node\n * for the given workerNodeList.\n */\nstatic List *\nOpenConnectionToNodes(List *workerNodeList)\n{\n\tList *connectionList = NIL;\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tconst char *nodeName = workerNode->workerName;\n\t\tint nodePort = workerNode->workerPort;\n\t\tint connectionFlags = 0;\n\n\t\tMultiConnection *connection = StartNodeConnection(connectionFlags, nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodePort);\n\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\treturn connectionList;\n}\n\n\n/*\n * GenerateShardStatisticsQueryList generates a query per node that will return:\n * shard_id, shard_name, shard_size for all shard placements on the node\n */\nstatic List *\nGenerateShardStatisticsQueryList(List *workerNodeList, List *citusTableIds)\n{\n\tList *shardStatisticsQueryList = NIL;\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tchar *shardStatisticsQuery =\n\t\t\tGenerateAllShardStatisticsQueryForNode(workerNode, citusTableIds);\n\n\t\tshardStatisticsQueryList = lappend(shardStatisticsQueryList,\n\t\t\t\t\t\t\t\t\t\t   shardStatisticsQuery);\n\t}\n\treturn shardStatisticsQueryList;\n}\n\n\n/*\n * ReceiveShardIdAndSizeResults receives shard id and size results from the given\n * connection list.\n */\nstatic void\nReceiveShardIdAndSizeResults(List *connectionList, Tuplestorestate *tupleStore,\n\t\t\t\t\t\t\t TupleDesc tupleDescriptor)\n{\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tbool raiseInterrupts = true;\n\t\tDatum values[SHARD_SIZES_COLUMN_COUNT];\n\t\tbool isNulls[SHARD_SIZES_COLUMN_COUNT];\n\n\t\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\t\tif (!IsResponseOK(result))\n\t\t{\n\t\t\tReportResultError(connection, result, WARNING);\n\t\t\tcontinue;\n\t\t}\n\n\t\tint64 rowCount = PQntuples(result);\n\t\tint64 colCount = PQnfields(result);\n\n\t\t/* Although it is not expected */\n\t\tif (colCount != SHARD_SIZES_COLUMN_COUNT)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"unexpected number of columns from \"\n\t\t\t\t\t\t\t\t\t \"citus_shard_sizes\")));\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (int64 rowIndex = 0; rowIndex < rowCount; rowIndex++)\n\t\t{\n\t\t\tmemset(values, 0, sizeof(values));\n\t\t\tmemset(isNulls, false, sizeof(isNulls));\n\n\t\t\t/* format is [0] shard id, [1] size */\n\t\t\tvalues[0] = ParseIntField(result, rowIndex, 0);\n\t\t\tvalues[1] = ParseIntField(result, rowIndex, 1);\n\n\t\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);\n\t\t}\n\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\t}\n}\n\n\n/*\n * DistributedRelationSize is helper function for each kind of citus size\n * functions. It first checks whether the relation is a distributed table or an\n * index belonging to a distributed table and size query can be run on it.\n * Connection to each node has to be established to get the size of the\n * relation.\n * Input relation is allowed to be an index on a distributed table too.\n */\nstatic bool\nDistributedRelationSize(Oid relationId, SizeQueryType sizeQueryType,\n\t\t\t\t\t\tbool failOnError, uint64 *relationSize)\n{\n\tint logLevel = WARNING;\n\n\tif (failOnError)\n\t{\n\t\tlogLevel = ERROR;\n\t}\n\n\tuint64 sumOfSizes = 0;\n\n\tif (XactModificationLevel == XACT_MODIFICATION_DATA)\n\t{\n\t\tereport(logLevel, (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),\n\t\t\t\t\t\t   errmsg(\"citus size functions cannot be called in transaction \"\n\t\t\t\t\t\t\t\t  \"blocks which contain multi-shard data \"\n\t\t\t\t\t\t\t\t  \"modifications\")));\n\n\t\treturn false;\n\t}\n\n\tRelation relation = try_relation_open(relationId, AccessShareLock);\n\n\tif (relation == NULL)\n\t{\n\t\tereport(logLevel,\n\t\t\t\t(errmsg(\"could not compute relation size: relation does not exist\")));\n\n\t\treturn false;\n\t}\n\n\tErrorIfNotSuitableToGetSize(relationId);\n\n\ttable_close(relation, AccessShareLock);\n\n\tList *workerNodeList = ActiveReadableNodeList();\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tuint64 relationSizeOnNode = 0;\n\n\t\tbool gotSize = DistributedRelationSizeOnWorker(workerNode, relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   sizeQueryType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   failOnError, &relationSizeOnNode);\n\t\tif (!gotSize)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tsumOfSizes += relationSizeOnNode;\n\t}\n\n\t*relationSize = sumOfSizes;\n\n\treturn true;\n}\n\n\n/*\n * DistributedRelationSizeOnWorker gets the workerNode and relationId to calculate\n * size of that relation on the given workerNode by summing up the size of each\n * shard placement.\n * Input relation is allowed to be an index on a distributed table too.\n */\nstatic bool\nDistributedRelationSizeOnWorker(WorkerNode *workerNode, Oid relationId,\n\t\t\t\t\t\t\t\tSizeQueryType sizeQueryType,\n\t\t\t\t\t\t\t\tbool failOnError, uint64 *relationSize)\n{\n\tint logLevel = WARNING;\n\n\tif (failOnError)\n\t{\n\t\tlogLevel = ERROR;\n\t}\n\n\tchar *workerNodeName = workerNode->workerName;\n\tuint32 workerNodePort = workerNode->workerPort;\n\tuint32 connectionFlag = 0;\n\tPGresult *result = NULL;\n\n\t/* if the relation is an index, update relationId and define indexId */\n\tOid indexId = InvalidOid;\n\tOid relKind = get_rel_relkind(relationId);\n\tif (relKind == RELKIND_INDEX || relKind == RELKIND_PARTITIONED_INDEX)\n\t{\n\t\tindexId = relationId;\n\n\t\tbool missingOk = false;\n\t\trelationId = IndexGetRelation(indexId, missingOk);\n\t}\n\n\tList *shardIntervalsOnNode = ShardIntervalsOnWorkerGroup(workerNode, relationId);\n\n\t/*\n\t * We pass false here, because if we optimize this, we would include child tables.\n\t * But citus size functions shouldn't include them, like PG.\n\t */\n\tbool optimizePartitionCalculations = false;\n\tStringInfo relationSizeQuery = GenerateSizeQueryOnMultiplePlacements(\n\t\tshardIntervalsOnNode,\n\t\tindexId,\n\t\tsizeQueryType,\n\t\toptimizePartitionCalculations);\n\n\tMultiConnection *connection = GetNodeConnection(connectionFlag, workerNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tworkerNodePort);\n\tint queryResult = ExecuteOptionalRemoteCommand(connection, relationSizeQuery->data,\n\t\t\t\t\t\t\t\t\t\t\t\t   &result);\n\n\tif (queryResult != 0)\n\t{\n\t\tereport(logLevel, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t   errmsg(\"could not connect to %s:%d to get size of \"\n\t\t\t\t\t\t\t\t  \"relation \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t  workerNodeName, workerNodePort,\n\t\t\t\t\t\t\t\t  get_rel_name(relationId))));\n\n\t\treturn false;\n\t}\n\n\tList *sizeList = ReadFirstColumnAsText(result);\n\tif (list_length(sizeList) != 1)\n\t{\n\t\tPQclear(result);\n\t\tClearResults(connection, failOnError);\n\n\t\tereport(logLevel, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\t   errmsg(\"cannot parse size of relation \\\"%s\\\" from %s:%d\",\n\t\t\t\t\t\t\t\t  get_rel_name(relationId), workerNodeName,\n\t\t\t\t\t\t\t\t  workerNodePort)));\n\n\t\treturn false;\n\t}\n\n\tStringInfo relationSizeStringInfo = (StringInfo) linitial(sizeList);\n\tchar *relationSizeString = relationSizeStringInfo->data;\n\n\tif (strlen(relationSizeString) > 0)\n\t{\n\t\t*relationSize = SafeStringToUint64(relationSizeString);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * This means the shard is moved or dropped while citus_total_relation_size is\n\t\t * being executed. For this case we get an empty string as table size.\n\t\t * We can take that as zero to prevent any unnecessary errors.\n\t\t */\n\t\t*relationSize = 0;\n\t}\n\n\tPQclear(result);\n\tClearResults(connection, failOnError);\n\n\treturn true;\n}\n\n\n/*\n * GroupShardPlacementsForTableOnGroup accepts a relationId and a group and returns a list\n * of GroupShardPlacement's representing all of the placements for the table which reside\n * on the group.\n */\nList *\nGroupShardPlacementsForTableOnGroup(Oid relationId, int32 groupId)\n{\n\tCitusTableCacheEntry *distTableCacheEntry = GetCitusTableCacheEntry(relationId);\n\tList *resultList = NIL;\n\n\tint shardIntervalArrayLength = distTableCacheEntry->shardIntervalArrayLength;\n\n\tfor (int shardIndex = 0; shardIndex < shardIntervalArrayLength; shardIndex++)\n\t{\n\t\tGroupShardPlacement *placementArray =\n\t\t\tdistTableCacheEntry->arrayOfPlacementArrays[shardIndex];\n\t\tint numberOfPlacements =\n\t\t\tdistTableCacheEntry->arrayOfPlacementArrayLengths[shardIndex];\n\n\t\tfor (int placementIndex = 0; placementIndex < numberOfPlacements;\n\t\t\t placementIndex++)\n\t\t{\n\t\t\tif (placementArray[placementIndex].groupId == groupId)\n\t\t\t{\n\t\t\t\tGroupShardPlacement *placement = palloc0(sizeof(GroupShardPlacement));\n\t\t\t\t*placement = placementArray[placementIndex];\n\t\t\t\tresultList = lappend(resultList, placement);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn resultList;\n}\n\n\n/*\n * ShardIntervalsOnWorkerGroup accepts a WorkerNode and returns a list of the shard\n * intervals of the given table which are placed on the group the node is a part of.\n */\nstatic List *\nShardIntervalsOnWorkerGroup(WorkerNode *workerNode, Oid relationId)\n{\n\tCitusTableCacheEntry *distTableCacheEntry = GetCitusTableCacheEntry(relationId);\n\tList *shardIntervalList = NIL;\n\tint shardIntervalArrayLength = distTableCacheEntry->shardIntervalArrayLength;\n\n\tfor (int shardIndex = 0; shardIndex < shardIntervalArrayLength; shardIndex++)\n\t{\n\t\tGroupShardPlacement *placementArray =\n\t\t\tdistTableCacheEntry->arrayOfPlacementArrays[shardIndex];\n\t\tint numberOfPlacements =\n\t\t\tdistTableCacheEntry->arrayOfPlacementArrayLengths[shardIndex];\n\n\t\tfor (int placementIndex = 0; placementIndex < numberOfPlacements;\n\t\t\t placementIndex++)\n\t\t{\n\t\t\tGroupShardPlacement *placement = &placementArray[placementIndex];\n\n\t\t\tif (placement->groupId == workerNode->groupId)\n\t\t\t{\n\t\t\t\tShardInterval *cachedShardInterval =\n\t\t\t\t\tdistTableCacheEntry->sortedShardIntervalArray[shardIndex];\n\t\t\t\tShardInterval *shardInterval = CopyShardInterval(cachedShardInterval);\n\t\t\t\tshardIntervalList = lappend(shardIntervalList, shardInterval);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn shardIntervalList;\n}\n\n\n/*\n * GenerateSizeQueryOnMultiplePlacements generates a select size query to get\n * size of multiple relations. Note that, different size functions supported by PG\n * are also supported by this function changing the size query type given as the\n * last parameter to function. Depending on the sizeQueryType enum parameter, the\n * generated query will call one of the functions: pg_relation_size,\n * pg_total_relation_size, pg_table_size and cstore_table_size.\n * This function uses UDFs named worker_partitioned_*_size for partitioned tables,\n * if the parameter optimizePartitionCalculations is true. The UDF to be called is\n * determined by the parameter sizeQueryType.\n *\n * indexId is provided if we're interested in the size of an index, not the whole\n * table.\n */\nStringInfo\nGenerateSizeQueryOnMultiplePlacements(List *shardIntervalList,\n\t\t\t\t\t\t\t\t\t  Oid indexId,\n\t\t\t\t\t\t\t\t\t  SizeQueryType sizeQueryType,\n\t\t\t\t\t\t\t\t\t  bool optimizePartitionCalculations)\n{\n\tStringInfo selectQuery = makeStringInfo();\n\n\tList *partitionedShardNames = NIL;\n\tList *nonPartitionedShardNames = NIL;\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tif (optimizePartitionCalculations && PartitionTable(shardInterval->relationId))\n\t\t{\n\t\t\t/*\n\t\t\t * Skip child tables of a partitioned table as they are already counted in\n\t\t\t * worker_partitioned_*_size UDFs, if optimizePartitionCalculations is true.\n\t\t\t * We don't expect this case to happen, since we don't send the child tables\n\t\t\t * to this function. Because they are all eliminated in\n\t\t\t * ColocatedNonPartitionShardIntervalList. Therefore we can't cover here with\n\t\t\t * a test currently. This is added for possible future usages.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* we need to build the shard relation name, being an index or table */\n\t\tOid objectId = OidIsValid(indexId) ? indexId : shardInterval->relationId;\n\n\t\tuint64 shardId = shardInterval->shardId;\n\t\tOid schemaId = get_rel_namespace(objectId);\n\t\tchar *schemaName = get_namespace_name(schemaId);\n\t\tchar *shardName = get_rel_name(objectId);\n\t\tAppendShardIdToName(&shardName, shardId);\n\n\t\tchar *shardQualifiedName = quote_qualified_identifier(schemaName, shardName);\n\t\tchar *quotedShardName = quote_literal_cstr(shardQualifiedName);\n\n\t\t/* for partitioned tables, we will call worker_partitioned_... size functions */\n\t\tif (optimizePartitionCalculations && PartitionedTable(shardInterval->relationId))\n\t\t{\n\t\t\tpartitionedShardNames = lappend(partitionedShardNames, quotedShardName);\n\t\t}\n\n\t\t/* for non-partitioned tables, we will use Postgres' size functions */\n\t\telse\n\t\t{\n\t\t\tnonPartitionedShardNames = lappend(nonPartitionedShardNames, quotedShardName);\n\t\t}\n\t}\n\n\t/* SELECT SUM(worker_partitioned_...) FROM VALUES (...) */\n\tchar *subqueryForPartitionedShards =\n\t\tGenerateSizeQueryForRelationNameList(partitionedShardNames,\n\t\t\t\t\t\t\t\t\t\t\t GetWorkerPartitionedSizeUDFNameBySizeQueryType\n\t\t\t\t\t\t\t\t\t\t\t\t (sizeQueryType));\n\n\t/* SELECT SUM(pg_..._size) FROM VALUES (...) */\n\tchar *subqueryForNonPartitionedShards =\n\t\tGenerateSizeQueryForRelationNameList(nonPartitionedShardNames,\n\t\t\t\t\t\t\t\t\t\t\t GetSizeQueryBySizeQueryType(sizeQueryType));\n\n\tappendStringInfo(selectQuery, \"SELECT (%s) + (%s);\",\n\t\t\t\t\t subqueryForPartitionedShards, subqueryForNonPartitionedShards);\n\n\telog(DEBUG4, \"Size Query: %s\", selectQuery->data);\n\n\treturn selectQuery;\n}\n\n\n/*\n * GenerateSizeQueryForPartitionedShards generates and returns a query with a template:\n * SELECT SUM( <sizeFunction>(relid) ) FROM (VALUES (<shardName>), (<shardName>), ...) as q(relid)\n */\nstatic char *\nGenerateSizeQueryForRelationNameList(List *quotedShardNames, char *sizeFunction)\n{\n\tif (list_length(quotedShardNames) == 0)\n\t{\n\t\treturn \"SELECT 0\";\n\t}\n\n\tStringInfo selectQuery = makeStringInfo();\n\n\tappendStringInfo(selectQuery, \"SELECT SUM(\");\n\tappendStringInfo(selectQuery, sizeFunction, \"relid\");\n\tappendStringInfo(selectQuery, \") FROM (VALUES \");\n\n\tbool addComma = false;\n\tchar *quotedShardName = NULL;\n\tforeach_declared_ptr(quotedShardName, quotedShardNames)\n\t{\n\t\tif (addComma)\n\t\t{\n\t\t\tappendStringInfoString(selectQuery, \", \");\n\t\t}\n\t\taddComma = true;\n\n\t\tappendStringInfo(selectQuery, \"(%s)\", quotedShardName);\n\t}\n\n\tappendStringInfoString(selectQuery, \") as q(relid)\");\n\n\treturn selectQuery->data;\n}\n\n\n/*\n * GetWorkerPartitionedSizeUDFNameBySizeQueryType returns the corresponding worker\n * partitioned size query for given query type.\n * Errors out for an invalid query type.\n * Currently this function is only called with the type TOTAL_RELATION_SIZE.\n * The others are added for possible future usages. Since they are not used anywhere,\n * currently we can't cover them with tests.\n */\nstatic char *\nGetWorkerPartitionedSizeUDFNameBySizeQueryType(SizeQueryType sizeQueryType)\n{\n\tswitch (sizeQueryType)\n\t{\n\t\tcase RELATION_SIZE:\n\t\t{\n\t\t\treturn WORKER_PARTITIONED_RELATION_SIZE_FUNCTION;\n\t\t}\n\n\t\tcase TOTAL_RELATION_SIZE:\n\t\t{\n\t\t\treturn WORKER_PARTITIONED_RELATION_TOTAL_SIZE_FUNCTION;\n\t\t}\n\n\t\tcase TABLE_SIZE:\n\t\t{\n\t\t\treturn WORKER_PARTITIONED_TABLE_SIZE_FUNCTION;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"Size query type couldn't be found.\");\n\t\t}\n\t}\n}\n\n\n/*\n * GetSizeQueryBySizeQueryType returns the corresponding size query for given query type.\n * Errors out for an invalid query type.\n */\nstatic char *\nGetSizeQueryBySizeQueryType(SizeQueryType sizeQueryType)\n{\n\tswitch (sizeQueryType)\n\t{\n\t\tcase RELATION_SIZE:\n\t\t{\n\t\t\treturn PG_RELATION_SIZE_FUNCTION;\n\t\t}\n\n\t\tcase TOTAL_RELATION_SIZE:\n\t\t{\n\t\t\treturn PG_TOTAL_RELATION_SIZE_FUNCTION;\n\t\t}\n\n\t\tcase TABLE_SIZE:\n\t\t{\n\t\t\treturn PG_TABLE_SIZE_FUNCTION;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\telog(ERROR, \"Size query type couldn't be found.\");\n\t\t}\n\t}\n}\n\n\n/*\n * GenerateAllShardStatisticsQueryForNode generates a query that returns:\n * shard_id, shard_name, shard_size for all shard placements on the node\n */\nstatic char *\nGenerateAllShardStatisticsQueryForNode(WorkerNode *workerNode, List *citusTableIds)\n{\n\tStringInfo allShardStatisticsQuery = makeStringInfo();\n\tbool insertedValues = false;\n\n\tappendStringInfoString(allShardStatisticsQuery, \"SELECT shard_id, \");\n\tappendStringInfo(allShardStatisticsQuery, PG_TOTAL_RELATION_SIZE_FUNCTION,\n\t\t\t\t\t \"table_name\");\n\tappendStringInfoString(allShardStatisticsQuery, \" FROM (VALUES \");\n\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, citusTableIds)\n\t{\n\t\t/*\n\t\t * Ensure the table still exists by trying to acquire a lock on it\n\t\t * If function returns NULL, it means the table doesn't exist\n\t\t * hence we should skip\n\t\t */\n\t\tRelation relation = try_relation_open(relationId, AccessShareLock);\n\t\tif (relation != NULL)\n\t\t{\n\t\t\tList *shardIntervalsOnNode = ShardIntervalsOnWorkerGroup(workerNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t relationId);\n\t\t\tif (list_length(shardIntervalsOnNode) == 0)\n\t\t\t{\n\t\t\t\trelation_close(relation, AccessShareLock);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tchar *shardIdNameValues =\n\t\t\t\tGenerateShardIdNameValuesForShardList(shardIntervalsOnNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  !insertedValues);\n\t\t\tinsertedValues = true;\n\t\t\tappendStringInfoString(allShardStatisticsQuery, shardIdNameValues);\n\t\t\trelation_close(relation, AccessShareLock);\n\t\t}\n\t}\n\n\tif (!insertedValues)\n\t{\n\t\treturn \"SELECT 0 AS shard_id, '' AS table_name LIMIT 0\";\n\t}\n\n\tappendStringInfoString(allShardStatisticsQuery, \") t(shard_id, table_name) \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"WHERE to_regclass(table_name) IS NOT NULL\");\n\treturn allShardStatisticsQuery->data;\n}\n\n\n/*\n * GenerateShardIdNameValuesForShardList generates a list of (shard_id, shard_name) values\n * for all shards in the list\n */\nstatic char *\nGenerateShardIdNameValuesForShardList(List *shardIntervalList, bool firstValue)\n{\n\tStringInfo selectQuery = makeStringInfo();\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tif (!firstValue)\n\t\t{\n\t\t\tappendStringInfoString(selectQuery, \", \");\n\t\t}\n\t\tfirstValue = false;\n\t\tAppendShardIdNameValues(selectQuery, shardInterval);\n\t}\n\n\treturn selectQuery->data;\n}\n\n\n/*\n * AppendShardIdNameValues appends (shard_id, shard_name) for shard\n */\nstatic void\nAppendShardIdNameValues(StringInfo selectQuery, ShardInterval *shardInterval)\n{\n\tuint64 shardId = shardInterval->shardId;\n\tOid schemaId = get_rel_namespace(shardInterval->relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\tchar *shardName = get_rel_name(shardInterval->relationId);\n\n\tAppendShardIdToName(&shardName, shardId);\n\n\tchar *shardQualifiedName = quote_qualified_identifier(schemaName, shardName);\n\tchar *quotedShardName = quote_literal_cstr(shardQualifiedName);\n\n\tappendStringInfo(selectQuery, \"(\" UINT64_FORMAT \", %s)\", shardId, quotedShardName);\n}\n\n\n/*\n * ErrorIfNotSuitableToGetSize determines whether the relation is suitable to find\n * its' size with internal functions.\n */\nstatic void\nErrorIfNotSuitableToGetSize(Oid relationId)\n{\n\tif (!IsCitusTable(relationId))\n\t{\n\t\tOid relKind = get_rel_relkind(relationId);\n\t\tif (relKind != RELKIND_INDEX && relKind != RELKIND_PARTITIONED_INDEX)\n\t\t{\n\t\t\tchar *relationName = get_rel_name(relationId);\n\t\t\tchar *escapedRelationName = quote_literal_cstr(relationName);\n\t\t\tereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION),\n\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\"cannot calculate the size because relation %s \"\n\t\t\t\t\t\t\t\t\"is not distributed\",\n\t\t\t\t\t\t\t\tescapedRelationName)));\n\t\t}\n\t\tbool missingOk = false;\n\t\tOid indexId = relationId;\n\t\trelationId = IndexGetRelation(relationId, missingOk);\n\t\tif (!IsCitusTable(relationId))\n\t\t{\n\t\t\tchar *tableName = get_rel_name(relationId);\n\t\t\tchar *escapedTableName = quote_literal_cstr(tableName);\n\t\t\tchar *indexName = get_rel_name(indexId);\n\t\t\tchar *escapedIndexName = quote_literal_cstr(indexName);\n\t\t\tereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),\n\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\"cannot calculate the size because table %s for \"\n\t\t\t\t\t\t\t\t\"index %s is not distributed\",\n\t\t\t\t\t\t\t\tescapedTableName, escapedIndexName)));\n\t\t}\n\t}\n}\n\n\n/*\n * CompareShardPlacementsByWorker compares two shard placements by their\n * worker node name and port.\n */\nint\nCompareShardPlacementsByWorker(const void *leftElement, const void *rightElement)\n{\n\tconst ShardPlacement *leftPlacement = *((const ShardPlacement **) leftElement);\n\tconst ShardPlacement *rightPlacement = *((const ShardPlacement **) rightElement);\n\n\tint nodeNameCmp = strncmp(leftPlacement->nodeName, rightPlacement->nodeName,\n\t\t\t\t\t\t\t  WORKER_LENGTH);\n\tif (nodeNameCmp != 0)\n\t{\n\t\treturn nodeNameCmp;\n\t}\n\telse if (leftPlacement->nodePort > rightPlacement->nodePort)\n\t{\n\t\treturn 1;\n\t}\n\telse if (leftPlacement->nodePort < rightPlacement->nodePort)\n\t{\n\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n\n\n/*\n * CompareShardPlacementsByGroupId compares two shard placements by their\n * group id.\n */\nint\nCompareShardPlacementsByGroupId(const void *leftElement, const void *rightElement)\n{\n\tconst ShardPlacement *leftPlacement = *((const ShardPlacement **) leftElement);\n\tconst ShardPlacement *rightPlacement = *((const ShardPlacement **) rightElement);\n\n\tif (leftPlacement->groupId > rightPlacement->groupId)\n\t{\n\t\treturn 1;\n\t}\n\telse if (leftPlacement->groupId < rightPlacement->groupId)\n\t{\n\t\treturn -1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * TableShardReplicationFactor returns the current replication factor of the\n * given relation by looking into shard placements. It errors out if there\n * are different number of shard placements for different shards. It also\n * errors out if the table does not have any shards.\n */\nuint32\nTableShardReplicationFactor(Oid relationId)\n{\n\tuint32 replicationCount = 0;\n\n\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\n\t\tList *shardPlacementList = ShardPlacementListSortedByWorker(shardId);\n\t\tuint32 shardPlacementCount = list_length(shardPlacementList);\n\n\t\t/*\n\t\t * Get the replication count of the first shard in the list, and error\n\t\t * out if there is a shard with different replication count.\n\t\t */\n\t\tif (replicationCount == 0)\n\t\t{\n\t\t\treplicationCount = shardPlacementCount;\n\t\t}\n\t\telse if (replicationCount != shardPlacementCount)\n\t\t{\n\t\t\tchar *relationName = get_rel_name(relationId);\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot find the replication factor of the \"\n\t\t\t\t\t\t\t\t   \"table %s\", relationName),\n\t\t\t\t\t\t\terrdetail(\"The shard \" UINT64_FORMAT\n\t\t\t\t\t\t\t\t\t  \" has different shards replication counts from \"\n\t\t\t\t\t\t\t\t\t  \"other shards.\", shardId)));\n\t\t}\n\t}\n\n\t/* error out if the table does not have any shards */\n\tif (replicationCount == 0)\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot find the replication factor of the \"\n\t\t\t\t\t\t\t   \"table %s\", relationName),\n\t\t\t\t\t\terrdetail(\"The table %s does not have any shards.\",\n\t\t\t\t\t\t\t\t  relationName)));\n\t}\n\n\treturn replicationCount;\n}\n\n\n/*\n * LoadShardIntervalList returns a list of shard intervals related for a given\n * distributed table. The function returns an empty list if no shards can be\n * found for the given relation.\n * Since LoadShardIntervalList relies on sortedShardIntervalArray, it returns\n * a shard interval list whose elements are sorted on shardminvalue. Shard intervals\n * with uninitialized shard min/max values are placed in the end of the list.\n */\nList *\nLoadShardIntervalList(Oid relationId)\n{\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\tList *shardList = NIL;\n\n\tfor (int i = 0; i < cacheEntry->shardIntervalArrayLength; i++)\n\t{\n\t\tShardInterval *newShardInterval =\n\t\t\tCopyShardInterval(cacheEntry->sortedShardIntervalArray[i]);\n\t\tshardList = lappend(shardList, newShardInterval);\n\t}\n\n\treturn shardList;\n}\n\n\n/*\n * LoadUnsortedShardIntervalListViaCatalog returns a list of shard intervals related for a\n * given distributed table. The function returns an empty list if no shards can be found\n * for the given relation.\n *\n * This function does not use CitusTableCache and instead reads from catalog tables\n * directly.\n */\nList *\nLoadUnsortedShardIntervalListViaCatalog(Oid relationId)\n{\n\tList *shardIntervalList = NIL;\n\tList *distShardTuples = LookupDistShardTuples(relationId);\n\tRelation distShardRelation = table_open(DistShardRelationId(), AccessShareLock);\n\tTupleDesc distShardTupleDesc = RelationGetDescr(distShardRelation);\n\tOid intervalTypeId = InvalidOid;\n\tint32 intervalTypeMod = -1;\n\n\tchar partitionMethod = PartitionMethodViaCatalog(relationId);\n\tVar *partitionColumn = PartitionColumnViaCatalog(relationId);\n\tGetIntervalTypeInfo(partitionMethod, partitionColumn, &intervalTypeId,\n\t\t\t\t\t\t&intervalTypeMod);\n\n\tHeapTuple distShardTuple = NULL;\n\tforeach_declared_ptr(distShardTuple, distShardTuples)\n\t{\n\t\tShardInterval *interval = TupleToShardInterval(distShardTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   distShardTupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   intervalTypeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   intervalTypeMod);\n\t\tshardIntervalList = lappend(shardIntervalList, interval);\n\t}\n\ttable_close(distShardRelation, AccessShareLock);\n\n\treturn shardIntervalList;\n}\n\n\n/*\n * LoadShardIntervalWithLongestShardName is a utility function that returns\n * the shard interaval with the largest shardId for the given relationId. Note\n * that largest shardId implies longest shard name.\n */\nShardInterval *\nLoadShardIntervalWithLongestShardName(Oid relationId)\n{\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\tint shardIntervalCount = cacheEntry->shardIntervalArrayLength;\n\n\tint maxShardIndex = shardIntervalCount - 1;\n\tuint64 largestShardId = INVALID_SHARD_ID;\n\n\tfor (int shardIndex = 0; shardIndex <= maxShardIndex; ++shardIndex)\n\t{\n\t\tShardInterval *currentShardInterval =\n\t\t\tcacheEntry->sortedShardIntervalArray[shardIndex];\n\n\t\tif (largestShardId < currentShardInterval->shardId)\n\t\t{\n\t\t\tlargestShardId = currentShardInterval->shardId;\n\t\t}\n\t}\n\n\treturn LoadShardInterval(largestShardId);\n}\n\n\n/*\n * ShardIntervalCount returns number of shard intervals for a given distributed table.\n * The function returns 0 if no shards can be found for the given relation id.\n */\nint\nShardIntervalCount(Oid relationId)\n{\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\n\treturn cacheEntry->shardIntervalArrayLength;\n}\n\n\n/*\n * LoadShardList reads list of shards for given relationId from pg_dist_shard,\n * and returns the list of found shardIds.\n * Since LoadShardList relies on sortedShardIntervalArray, it returns a shard\n * list whose elements are sorted on shardminvalue. Shards with uninitialized\n * shard min/max values are placed in the end of the list.\n */\nList *\nLoadShardList(Oid relationId)\n{\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\tList *shardList = NIL;\n\n\tfor (int i = 0; i < cacheEntry->shardIntervalArrayLength; i++)\n\t{\n\t\tShardInterval *currentShardInterval = cacheEntry->sortedShardIntervalArray[i];\n\t\tuint64 *shardIdPointer = AllocateUint64(currentShardInterval->shardId);\n\n\t\tshardList = lappend(shardList, shardIdPointer);\n\t}\n\n\treturn shardList;\n}\n\n\n/* Allocates eight bytes, and copies given value's contents those bytes. */\nstatic uint64 *\nAllocateUint64(uint64 value)\n{\n\tuint64 *allocatedValue = (uint64 *) palloc0(sizeof(uint64));\n\tAssert(sizeof(uint64) >= 8);\n\n\t(*allocatedValue) = value;\n\n\treturn allocatedValue;\n}\n\n\n/*\n * CopyShardInterval creates a copy of the specified source ShardInterval.\n */\nShardInterval *\nCopyShardInterval(ShardInterval *srcInterval)\n{\n\tShardInterval *destInterval = palloc0(sizeof(ShardInterval));\n\n\tdestInterval->type = srcInterval->type;\n\tdestInterval->relationId = srcInterval->relationId;\n\tdestInterval->storageType = srcInterval->storageType;\n\tdestInterval->valueTypeId = srcInterval->valueTypeId;\n\tdestInterval->valueTypeLen = srcInterval->valueTypeLen;\n\tdestInterval->valueByVal = srcInterval->valueByVal;\n\tdestInterval->minValueExists = srcInterval->minValueExists;\n\tdestInterval->maxValueExists = srcInterval->maxValueExists;\n\tdestInterval->shardId = srcInterval->shardId;\n\tdestInterval->shardIndex = srcInterval->shardIndex;\n\n\tdestInterval->minValue = 0;\n\tif (destInterval->minValueExists)\n\t{\n\t\tdestInterval->minValue = datumCopy(srcInterval->minValue,\n\t\t\t\t\t\t\t\t\t\t   srcInterval->valueByVal,\n\t\t\t\t\t\t\t\t\t\t   srcInterval->valueTypeLen);\n\t}\n\n\tdestInterval->maxValue = 0;\n\tif (destInterval->maxValueExists)\n\t{\n\t\tdestInterval->maxValue = datumCopy(srcInterval->maxValue,\n\t\t\t\t\t\t\t\t\t\t   srcInterval->valueByVal,\n\t\t\t\t\t\t\t\t\t\t   srcInterval->valueTypeLen);\n\t}\n\n\treturn destInterval;\n}\n\n\n/*\n * ShardLength finds shard placements for the given shardId, extracts the length\n * of an active shard, and returns the shard's length. This function errors\n * out if we cannot find any active shard placements for the given shardId.\n */\nuint64\nShardLength(uint64 shardId)\n{\n\tuint64 shardLength = 0;\n\n\tList *shardPlacementList = ActiveShardPlacementList(shardId);\n\tif (shardPlacementList == NIL)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find length of shard \" UINT64_FORMAT, shardId),\n\t\t\t\t\t\terrdetail(\"Could not find any shard placements for the shard.\")));\n\t}\n\telse\n\t{\n\t\tShardPlacement *shardPlacement = (ShardPlacement *) linitial(shardPlacementList);\n\t\tshardLength = shardPlacement->shardLength;\n\t}\n\n\treturn shardLength;\n}\n\n\n/*\n * NodeGroupHasShardPlacements returns whether any active shards are placed on the group\n */\nbool\nNodeGroupHasShardPlacements(int32 groupId)\n{\n\tconst int scanKeyCount = 1;\n\tconst bool indexOK = false;\n\n\tScanKeyData scanKey[1];\n\n\tRelation pgPlacement = table_open(DistPlacementRelationId(),\n\t\t\t\t\t\t\t\t\t  AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_placement_groupid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(groupId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPlacementGroupidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tbool hasActivePlacements = HeapTupleIsValid(heapTuple);\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgPlacement, NoLock);\n\n\treturn hasActivePlacements;\n}\n\n\n/*\n * IsActiveShardPlacement checks if the shard placement is labelled as\n * active, and that it is placed in an active worker.\n * Expects shard worker to not be NULL.\n */\nbool\nIsActiveShardPlacement(ShardPlacement *shardPlacement)\n{\n\tWorkerNode *workerNode =\n\t\tFindWorkerNode(shardPlacement->nodeName, shardPlacement->nodePort);\n\n\tif (!workerNode)\n\t{\n\t\tereport(ERROR, (errmsg(\"There is a shard placement on node %s:%d but \"\n\t\t\t\t\t\t\t   \"could not find the node.\", shardPlacement->nodeName,\n\t\t\t\t\t\t\t   shardPlacement->nodePort)));\n\t}\n\n\treturn workerNode->isActive;\n}\n\n\n/*\n * IsRemoteShardPlacement returns true if the shard placement is on a remote\n * node.\n */\nbool\nIsRemoteShardPlacement(ShardPlacement *shardPlacement)\n{\n\treturn shardPlacement->groupId != GetLocalGroupId();\n}\n\n\n/*\n * IsPlacementOnWorkerNode checks if the shard placement is for to the given\n * workenode.\n */\nbool\nIsPlacementOnWorkerNode(ShardPlacement *placement, WorkerNode *workerNode)\n{\n\tif (strncmp(workerNode->workerName, placement->nodeName, WORKER_LENGTH) != 0)\n\t{\n\t\treturn false;\n\t}\n\treturn workerNode->workerPort == placement->nodePort;\n}\n\n\n/*\n * FilterShardPlacementList filters a list of shard placements based on a filter.\n * Keep only the shard for which the filter function returns true.\n */\nList *\nFilterShardPlacementList(List *shardPlacementList, bool (*filter)(ShardPlacement *))\n{\n\tList *filteredShardPlacementList = NIL;\n\tShardPlacement *shardPlacement = NULL;\n\n\tforeach_declared_ptr(shardPlacement, shardPlacementList)\n\t{\n\t\tif (filter(shardPlacement))\n\t\t{\n\t\t\tfilteredShardPlacementList = lappend(filteredShardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t shardPlacement);\n\t\t}\n\t}\n\n\treturn filteredShardPlacementList;\n}\n\n\n/*\n * FilterActiveShardPlacementListByNode filters a list of active shard placements based on given nodeName and nodePort.\n */\nList *\nFilterActiveShardPlacementListByNode(List *shardPlacementList, WorkerNode *workerNode)\n{\n\tList *activeShardPlacementList = FilterShardPlacementList(shardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  IsActiveShardPlacement);\n\tList *filteredShardPlacementList = NIL;\n\tShardPlacement *shardPlacement = NULL;\n\n\tforeach_declared_ptr(shardPlacement, activeShardPlacementList)\n\t{\n\t\tif (IsPlacementOnWorkerNode(shardPlacement, workerNode))\n\t\t{\n\t\t\tfilteredShardPlacementList = lappend(filteredShardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t shardPlacement);\n\t\t}\n\t}\n\n\treturn filteredShardPlacementList;\n}\n\n\n/*\n * ActiveShardPlacementListOnGroup returns a list of active shard placements\n * that are sitting on group with groupId for given shardId.\n */\nList *\nActiveShardPlacementListOnGroup(uint64 shardId, int32 groupId)\n{\n\tList *activeShardPlacementListOnGroup = NIL;\n\n\tList *activePlacementList = ActiveShardPlacementList(shardId);\n\tShardPlacement *shardPlacement = NULL;\n\tforeach_declared_ptr(shardPlacement, activePlacementList)\n\t{\n\t\tif (shardPlacement->groupId == groupId)\n\t\t{\n\t\t\tactiveShardPlacementListOnGroup = lappend(activeShardPlacementListOnGroup,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  shardPlacement);\n\t\t}\n\t}\n\n\treturn activeShardPlacementListOnGroup;\n}\n\n\n/*\n * ActiveShardPlacementList finds shard placements for the given shardId from\n * system catalogs, chooses placements that are in active state, and returns\n * these shard placements in a new list.\n */\nList *\nActiveShardPlacementList(uint64 shardId)\n{\n\tList *shardPlacementList = ShardPlacementList(shardId);\n\n\tList *activePlacementList = FilterShardPlacementList(shardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t IsActiveShardPlacement);\n\n\treturn SortList(activePlacementList, CompareShardPlacementsByWorker);\n}\n\n\n/*\n * ShardPlacementListSortedByWorker returns shard placements sorted by worker port.\n */\nList *\nShardPlacementListSortedByWorker(uint64 shardId)\n{\n\tList *shardPlacementList = ShardPlacementList(shardId);\n\n\treturn SortList(shardPlacementList, CompareShardPlacementsByWorker);\n}\n\n\n/*\n * ActiveShardPlacement finds a shard placement for the given shardId from\n * system catalog, chooses a placement that is in active state and returns\n * that shard placement. If this function cannot find a healthy shard placement\n * and missingOk is set to false it errors out.\n */\nShardPlacement *\nActiveShardPlacement(uint64 shardId, bool missingOk)\n{\n\tList *activePlacementList = ActiveShardPlacementList(shardId);\n\tShardPlacement *shardPlacement = NULL;\n\n\tif (list_length(activePlacementList) == 0)\n\t{\n\t\tif (!missingOk)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t\terrmsg(\"could not find any healthy placement for shard \"\n\t\t\t\t\t\t\t\t   UINT64_FORMAT, shardId)));\n\t\t}\n\n\t\treturn shardPlacement;\n\t}\n\n\tshardPlacement = (ShardPlacement *) linitial(activePlacementList);\n\n\treturn shardPlacement;\n}\n\n\n/*\n * ActiveShardPlacementWorkerNode returns the worker node of the first placement of\n * a shard.\n */\nWorkerNode *\nActiveShardPlacementWorkerNode(uint64 shardId)\n{\n\tbool missingOK = false;\n\n\tList *sourcePlacementList = ActiveShardPlacementList(shardId);\n\n\tAssert(sourcePlacementList->length == 1);\n\n\tShardPlacement *sourceShardPlacement = linitial(sourcePlacementList);\n\tWorkerNode *sourceShardToCopyNode = FindNodeWithNodeId(sourceShardPlacement->nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   missingOK);\n\n\treturn sourceShardToCopyNode;\n}\n\n\n/*\n * BuildShardPlacementList finds shard placements for the given shardId from\n * system catalogs, converts these placements to their in-memory\n * representation, and returns the converted shard placements in a new list.\n *\n * This probably only should be called from metadata_cache.c.  Resides here\n * because it shares code with other routines in this file.\n */\nList *\nBuildShardPlacementList(int64 shardId)\n{\n\tList *shardPlacementList = NIL;\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tbool indexOK = true;\n\n\tRelation pgPlacement = table_open(DistPlacementRelationId(), AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_placement_shardid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(shardId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPlacementShardidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(pgPlacement);\n\n\t\tGroupShardPlacement *placement =\n\t\t\tTupleToGroupShardPlacement(tupleDescriptor, heapTuple);\n\n\t\tshardPlacementList = lappend(shardPlacementList, placement);\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgPlacement, NoLock);\n\n\treturn shardPlacementList;\n}\n\n\n/*\n * BuildShardPlacementListForGroup finds shard placements for the given groupId\n * from system catalogs, converts these placements to their in-memory\n * representation, and returns the converted shard placements in a new list.\n */\nList *\nAllShardPlacementsOnNodeGroup(int32 groupId)\n{\n\tList *shardPlacementList = NIL;\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tbool indexOK = true;\n\n\tRelation pgPlacement = table_open(DistPlacementRelationId(), AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_placement_groupid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(groupId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPlacementGroupidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(pgPlacement);\n\n\t\tGroupShardPlacement *placement =\n\t\t\tTupleToGroupShardPlacement(tupleDescriptor, heapTuple);\n\n\t\tshardPlacementList = lappend(shardPlacementList, placement);\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgPlacement, NoLock);\n\n\treturn shardPlacementList;\n}\n\n\n/*\n * TupleToGroupShardPlacement takes in a heap tuple from pg_dist_placement,\n * and converts this tuple to in-memory struct. The function assumes the\n * caller already has locks on the tuple, and doesn't perform any locking.\n */\nstatic GroupShardPlacement *\nTupleToGroupShardPlacement(TupleDesc tupleDescriptor, HeapTuple heapTuple)\n{\n\tbool isNullArray[Natts_pg_dist_placement];\n\tDatum datumArray[Natts_pg_dist_placement];\n\n\tif (HeapTupleHeaderGetNatts(heapTuple->t_data) != Natts_pg_dist_placement ||\n\t\tHeapTupleHasNulls(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"unexpected null in pg_dist_placement tuple\")));\n\t}\n\n\t/*\n\t * We use heap_deform_tuple() instead of heap_getattr() to expand tuple\n\t * to contain missing values when ALTER TABLE ADD COLUMN happens.\n\t */\n\theap_deform_tuple(heapTuple, tupleDescriptor, datumArray, isNullArray);\n\n\tGroupShardPlacement *shardPlacement = CitusMakeNode(GroupShardPlacement);\n\tshardPlacement->placementId = DatumGetInt64(\n\t\tdatumArray[Anum_pg_dist_placement_placementid - 1]);\n\tshardPlacement->shardId = DatumGetInt64(\n\t\tdatumArray[Anum_pg_dist_placement_shardid - 1]);\n\tshardPlacement->shardLength = DatumGetInt64(\n\t\tdatumArray[Anum_pg_dist_placement_shardlength - 1]);\n\tshardPlacement->groupId = DatumGetInt32(\n\t\tdatumArray[Anum_pg_dist_placement_groupid - 1]);\n\n\treturn shardPlacement;\n}\n\n\n/*\n * LookupTaskPlacementHostAndPort sets the nodename and nodeport for the given task placement\n * with a lookup.\n */\nvoid\nLookupTaskPlacementHostAndPort(ShardPlacement *taskPlacement, char **nodeName,\n\t\t\t\t\t\t\t   int *nodePort)\n{\n\tif (IsDummyPlacement(taskPlacement))\n\t{\n\t\t/*\n\t\t * If we create a dummy placement for the local node, it is possible\n\t\t * that the entry doesn't exist in pg_dist_node, hence a lookup will fail.\n\t\t * In that case we want to use the dummy placements values.\n\t\t */\n\t\t*nodeName = taskPlacement->nodeName;\n\t\t*nodePort = taskPlacement->nodePort;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * We want to lookup the node information again since it is possible that\n\t\t * there were changes in pg_dist_node and we will get those invalidations\n\t\t * in LookupNodeForGroup.\n\t\t */\n\t\tWorkerNode *workerNode = LookupNodeForGroup(taskPlacement->groupId);\n\t\t*nodeName = workerNode->workerName;\n\t\t*nodePort = workerNode->workerPort;\n\t}\n}\n\n\n/*\n * IsDummyPlacement returns true if the given placement is a dummy placement.\n */\nbool\nIsDummyPlacement(ShardPlacement *taskPlacement)\n{\n\treturn taskPlacement->nodeId == LOCAL_NODE_ID;\n}\n\n\n/*\n * InsertShardRow opens the shard system catalog, and inserts a new row with the\n * given values into that system catalog. Note that we allow the user to pass in\n * null min/max values in case they are creating an empty shard.\n */\nvoid\nInsertShardRow(Oid relationId, uint64 shardId, char storageType,\n\t\t\t   text *shardMinValue, text *shardMaxValue)\n{\n\tDatum values[Natts_pg_dist_shard];\n\tbool isNulls[Natts_pg_dist_shard];\n\n\t/* form new shard tuple */\n\tmemset(values, 0, sizeof(values));\n\tmemset(isNulls, false, sizeof(isNulls));\n\n\tvalues[Anum_pg_dist_shard_logicalrelid - 1] = ObjectIdGetDatum(relationId);\n\tvalues[Anum_pg_dist_shard_shardid - 1] = Int64GetDatum(shardId);\n\tvalues[Anum_pg_dist_shard_shardstorage - 1] = CharGetDatum(storageType);\n\n\t/* dropped shardalias column must also be set; it is still part of the tuple */\n\tisNulls[Anum_pg_dist_shard_shardalias_DROPPED - 1] = true;\n\n\t/* check if shard min/max values are null */\n\tif (shardMinValue != NULL && shardMaxValue != NULL)\n\t{\n\t\tvalues[Anum_pg_dist_shard_shardminvalue - 1] = PointerGetDatum(shardMinValue);\n\t\tvalues[Anum_pg_dist_shard_shardmaxvalue - 1] = PointerGetDatum(shardMaxValue);\n\t}\n\telse\n\t{\n\t\tisNulls[Anum_pg_dist_shard_shardminvalue - 1] = true;\n\t\tisNulls[Anum_pg_dist_shard_shardmaxvalue - 1] = true;\n\t}\n\n\t/* open shard relation and insert new tuple */\n\tRelation pgDistShard = table_open(DistShardRelationId(), RowExclusiveLock);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistShard);\n\tHeapTuple heapTuple = heap_form_tuple(tupleDescriptor, values, isNulls);\n\n\tCatalogTupleInsert(pgDistShard, heapTuple);\n\n\t/* invalidate previous cache entry and close relation */\n\tCitusInvalidateRelcacheByRelid(relationId);\n\n\tCommandCounterIncrement();\n\ttable_close(pgDistShard, NoLock);\n}\n\n\n/*\n * InsertShardPlacementRowGlobally inserts shard placement that has given\n * parameters into pg_dist_placement globally.\n */\nShardPlacement *\nInsertShardPlacementRowGlobally(uint64 shardId, uint64 placementId,\n\t\t\t\t\t\t\t\tuint64 shardLength, int32 groupId)\n{\n\tInsertShardPlacementRow(shardId, placementId, shardLength, groupId);\n\n\tchar *insertPlacementCommand =\n\t\tAddPlacementMetadataCommand(shardId, placementId, shardLength, groupId);\n\tSendCommandToWorkersWithMetadata(insertPlacementCommand);\n\n\treturn LoadShardPlacement(shardId, placementId);\n}\n\n\n/*\n * InsertShardPlacementRow opens the shard placement system catalog, and inserts\n * a new row with the given values into that system catalog. If placementId is\n * INVALID_PLACEMENT_ID, a new placement id will be assigned.Then, returns the\n * placement id of the added shard placement.\n */\nuint64\nInsertShardPlacementRow(uint64 shardId, uint64 placementId,\n\t\t\t\t\t\tuint64 shardLength, int32 groupId)\n{\n\tDatum values[Natts_pg_dist_placement];\n\tbool isNulls[Natts_pg_dist_placement];\n\n\t/* form new shard placement tuple */\n\tmemset(values, 0, sizeof(values));\n\tmemset(isNulls, false, sizeof(isNulls));\n\n\tif (placementId == INVALID_PLACEMENT_ID)\n\t{\n\t\tplacementId = master_get_new_placementid(NULL);\n\t}\n\tvalues[Anum_pg_dist_placement_placementid - 1] = Int64GetDatum(placementId);\n\tvalues[Anum_pg_dist_placement_shardid - 1] = Int64GetDatum(shardId);\n\tvalues[Anum_pg_dist_placement_shardstate - 1] = Int32GetDatum(1);\n\tvalues[Anum_pg_dist_placement_shardlength - 1] = Int64GetDatum(shardLength);\n\tvalues[Anum_pg_dist_placement_groupid - 1] = Int32GetDatum(groupId);\n\n\t/* open shard placement relation and insert new tuple */\n\tRelation pgDistPlacement = table_open(DistPlacementRelationId(), RowExclusiveLock);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPlacement);\n\tHeapTuple heapTuple = heap_form_tuple(tupleDescriptor, values, isNulls);\n\n\tCatalogTupleInsert(pgDistPlacement, heapTuple);\n\n\tCitusInvalidateRelcacheByShardId(shardId);\n\n\tCommandCounterIncrement();\n\ttable_close(pgDistPlacement, NoLock);\n\n\treturn placementId;\n}\n\n\n/*\n * InsertIntoPgDistPartition inserts a new tuple into pg_dist_partition.\n */\nvoid\nInsertIntoPgDistPartition(Oid relationId, char distributionMethod,\n\t\t\t\t\t\t  Var *distributionColumn, uint32 colocationId,\n\t\t\t\t\t\t  char replicationModel, bool autoConverted)\n{\n\tchar *distributionColumnString = NULL;\n\n\t/* open system catalog and insert new tuple */\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\n\tDatum *newValues = (Datum *) palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *newNulls = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\t/* form new tuple for pg_dist_partition */\n\tnewValues[Anum_pg_dist_partition_logicalrelid - 1] =\n\t\tObjectIdGetDatum(relationId);\n\tnewValues[Anum_pg_dist_partition_partmethod - 1] =\n\t\tCharGetDatum(distributionMethod);\n\tnewValues[Anum_pg_dist_partition_colocationid - 1] = UInt32GetDatum(colocationId);\n\tnewValues[Anum_pg_dist_partition_repmodel - 1] = CharGetDatum(replicationModel);\n\tnewValues[GetAutoConvertedAttrIndexInPgDistPartition(tupleDescriptor)] =\n\t\tBoolGetDatum(autoConverted);\n\n\t/* set partkey column to NULL for reference tables */\n\tif (distributionMethod != DISTRIBUTE_BY_NONE)\n\t{\n\t\tdistributionColumnString = nodeToString((Node *) distributionColumn);\n\n\t\tnewValues[Anum_pg_dist_partition_partkey - 1] =\n\t\t\tCStringGetTextDatum(distributionColumnString);\n\t}\n\telse\n\t{\n\t\tnewValues[Anum_pg_dist_partition_partkey - 1] = PointerGetDatum(NULL);\n\t\tnewNulls[Anum_pg_dist_partition_partkey - 1] = true;\n\t}\n\n\tHeapTuple newTuple = heap_form_tuple(tupleDescriptor, newValues,\n\t\t\t\t\t\t\t\t\t\t newNulls);\n\n\t/* finally insert tuple, build index entries & register cache invalidation */\n\tCatalogTupleInsert(pgDistPartition, newTuple);\n\n\tCitusInvalidateRelcacheByRelid(relationId);\n\n\tRecordDistributedRelationDependencies(relationId);\n\n\tCommandCounterIncrement();\n\ttable_close(pgDistPartition, NoLock);\n\n\tpfree(newValues);\n\tpfree(newNulls);\n}\n\n\n/*\n * RecordDistributedRelationDependencies creates the dependency entries\n * necessary for a distributed relation in addition to the preexisting ones\n * for a normal relation.\n *\n * We create one dependency from the (now distributed) relation to the citus\n * extension to prevent the extension from being dropped while distributed\n * tables exist. Furthermore a dependency from pg_dist_partition's\n * distribution clause to the underlying columns is created, but it's marked\n * as being owned by the relation itself. That means the entire table can be\n * dropped, but the column itself can't. Neither can the type of the\n * distribution column be changed (c.f. ATExecAlterColumnType).\n */\nstatic void\nRecordDistributedRelationDependencies(Oid distributedRelationId)\n{\n\tObjectAddress relationAddr = { 0, 0, 0 };\n\tObjectAddress citusExtensionAddr = { 0, 0, 0 };\n\n\trelationAddr.classId = RelationRelationId;\n\trelationAddr.objectId = distributedRelationId;\n\trelationAddr.objectSubId = 0;\n\n\tcitusExtensionAddr.classId = ExtensionRelationId;\n\tcitusExtensionAddr.objectId = get_extension_oid(\"citus\", false);\n\tcitusExtensionAddr.objectSubId = 0;\n\n\t/* dependency from table entry to extension */\n\trecordDependencyOn(&relationAddr, &citusExtensionAddr, DEPENDENCY_NORMAL);\n}\n\n\n/*\n * DeletePartitionRow removes the row from pg_dist_partition where the logicalrelid\n * field equals to distributedRelationId. Then, the function invalidates the\n * metadata cache.\n */\nvoid\nDeletePartitionRow(Oid distributedRelationId)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), RowExclusiveLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_partition_logicalrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(distributedRelationId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPartition, InvalidOid, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\tscanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for partition %d\",\n\t\t\t\t\t\t\t   distributedRelationId)));\n\t}\n\n\tsimple_heap_delete(pgDistPartition, &heapTuple->t_self);\n\n\tsystable_endscan(scanDescriptor);\n\n\t/* invalidate the cache */\n\tCitusInvalidateRelcacheByRelid(distributedRelationId);\n\n\t/* increment the counter so that next command can see the row */\n\tCommandCounterIncrement();\n\n\ttable_close(pgDistPartition, NoLock);\n}\n\n\n/*\n * DeleteShardRow opens the shard system catalog, finds the unique row that has\n * the given shardId, and deletes this row.\n */\nvoid\nDeleteShardRow(uint64 shardId)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tbool indexOK = true;\n\n\tRelation pgDistShard = table_open(DistShardRelationId(), RowExclusiveLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_shard_shardid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(shardId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistShard,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistShardShardidIndexId(), indexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for shard \"\n\t\t\t\t\t\t\t   UINT64_FORMAT, shardId)));\n\t}\n\n\tForm_pg_dist_shard pgDistShardForm = (Form_pg_dist_shard) GETSTRUCT(heapTuple);\n\tOid distributedRelationId = pgDistShardForm->logicalrelid;\n\n\tsimple_heap_delete(pgDistShard, &heapTuple->t_self);\n\n\tsystable_endscan(scanDescriptor);\n\n\t/* invalidate previous cache entry */\n\tCitusInvalidateRelcacheByRelid(distributedRelationId);\n\n\tCommandCounterIncrement();\n\ttable_close(pgDistShard, NoLock);\n}\n\n\n/*\n * DeleteShardPlacementRowGlobally deletes shard placement that has given\n * parameters from pg_dist_placement globally.\n */\nvoid\nDeleteShardPlacementRowGlobally(uint64 placementId)\n{\n\tDeleteShardPlacementRow(placementId);\n\n\tchar *deletePlacementCommand =\n\t\tDeletePlacementMetadataCommand(placementId);\n\tSendCommandToWorkersWithMetadata(deletePlacementCommand);\n}\n\n\n/*\n * DeleteShardPlacementRow opens the shard placement system catalog, finds the placement\n * with the given placementId, and deletes it.\n */\nvoid\nDeleteShardPlacementRow(uint64 placementId)\n{\n\tconst int scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\tbool indexOK = true;\n\tbool isNull = false;\n\n\tRelation pgDistPlacement = table_open(DistPlacementRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPlacement);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_placement_placementid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(placementId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPlacementPlacementidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (heapTuple == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for shard placement \"\n\t\t\t\t\t\t\t   INT64_FORMAT, placementId)));\n\t}\n\n\tuint64 shardId = heap_getattr(heapTuple, Anum_pg_dist_placement_shardid,\n\t\t\t\t\t\t\t\t  tupleDescriptor, &isNull);\n\tif (HeapTupleHeaderGetNatts(heapTuple->t_data) != Natts_pg_dist_placement ||\n\t\tHeapTupleHasNulls(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"unexpected null in pg_dist_placement tuple\")));\n\t}\n\n\tsimple_heap_delete(pgDistPlacement, &heapTuple->t_self);\n\tsystable_endscan(scanDescriptor);\n\n\tCitusInvalidateRelcacheByShardId(shardId);\n\n\tCommandCounterIncrement();\n\ttable_close(pgDistPlacement, NoLock);\n}\n\n\n/*\n * UpdatePlacementGroupId sets the groupId for the placement identified\n * by placementId.\n */\nvoid\nUpdatePlacementGroupId(uint64 placementId, int groupId)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tbool indexOK = true;\n\tbool colIsNull = false;\n\n\tRelation pgDistPlacement = table_open(DistPlacementRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPlacement);\n\tDatum *values = (Datum *) palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isnull = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_placement_placementid,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(placementId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPlacementPlacementidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for shard placement \"\n\t\t\t\t\t\t\t   UINT64_FORMAT,\n\t\t\t\t\t\t\t   placementId)));\n\t}\n\n\tvalues[Anum_pg_dist_placement_groupid - 1] = Int32GetDatum(groupId);\n\tisnull[Anum_pg_dist_placement_groupid - 1] = false;\n\treplace[Anum_pg_dist_placement_groupid - 1] = true;\n\n\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull, replace);\n\n\tCatalogTupleUpdate(pgDistPlacement, &heapTuple->t_self, heapTuple);\n\n\tuint64 shardId = DatumGetInt64(heap_getattr(heapTuple,\n\t\t\t\t\t\t\t\t\t\t\t\tAnum_pg_dist_placement_shardid,\n\t\t\t\t\t\t\t\t\t\t\t\ttupleDescriptor, &colIsNull));\n\tAssert(!colIsNull);\n\tCitusInvalidateRelcacheByShardId(shardId);\n\n\tCommandCounterIncrement();\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistPlacement, NoLock);\n\n\tpfree(values);\n\tpfree(isnull);\n\tpfree(replace);\n}\n\n\n/*\n * UpdatePgDistPartitionAutoConverted sets the autoConverted for the partition identified\n * by citusTableId.\n */\nvoid\nUpdatePgDistPartitionAutoConverted(Oid citusTableId, bool autoConverted)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tbool indexOK = true;\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\tDatum *values = (Datum *) palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isnull = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_partition_logicalrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(citusTableId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPartitionLogicalRelidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for citus table with oid: %u\",\n\t\t\t\t\t\t\t   citusTableId)));\n\t}\n\n\tint autoconvertedindex = GetAutoConvertedAttrIndexInPgDistPartition(tupleDescriptor);\n\tvalues[autoconvertedindex] = BoolGetDatum(autoConverted);\n\tisnull[autoconvertedindex] = false;\n\treplace[autoconvertedindex] = true;\n\n\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull, replace);\n\n\tCatalogTupleUpdate(pgDistPartition, &heapTuple->t_self, heapTuple);\n\n\tCitusInvalidateRelcacheByRelid(citusTableId);\n\n\tCommandCounterIncrement();\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistPartition, NoLock);\n\n\tpfree(values);\n\tpfree(isnull);\n\tpfree(replace);\n}\n\n\n/*\n * UpdateDistributionColumnGlobally sets the distribution column and colocation ID\n * for a table in pg_dist_partition on all nodes\n */\nvoid\nUpdateDistributionColumnGlobally(Oid relationId, char distributionMethod,\n\t\t\t\t\t\t\t\t Var *distributionColumn, int colocationId)\n{\n\tUpdateDistributionColumn(relationId, distributionMethod, distributionColumn,\n\t\t\t\t\t\t\t colocationId);\n\n\tif (ShouldSyncTableMetadata(relationId))\n\t{\n\t\t/* we use delete+insert because syncing uses specialized RPCs */\n\t\tchar *deleteMetadataCommand = DistributionDeleteMetadataCommand(relationId);\n\t\tSendCommandToWorkersWithMetadata(deleteMetadataCommand);\n\n\t\t/* pick up the new metadata (updated above) */\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\t\tchar *insertMetadataCommand = DistributionCreateCommand(cacheEntry);\n\t\tSendCommandToWorkersWithMetadata(insertMetadataCommand);\n\t}\n}\n\n\n/*\n * UpdateDistributionColumn sets the distribution column and colocation ID for a table\n * in pg_dist_partition.\n */\nvoid\nUpdateDistributionColumn(Oid relationId, char distributionMethod, Var *distributionColumn,\n\t\t\t\t\t\t int colocationId)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tbool indexOK = true;\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\tDatum *values = (Datum *) palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isnull = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_partition_logicalrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPartitionLogicalRelidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for citus table with oid: %u\",\n\t\t\t\t\t\t\t   relationId)));\n\t}\n\n\treplace[Anum_pg_dist_partition_partmethod - 1] = true;\n\tvalues[Anum_pg_dist_partition_partmethod - 1] = CharGetDatum(distributionMethod);\n\tisnull[Anum_pg_dist_partition_partmethod - 1] = false;\n\n\treplace[Anum_pg_dist_partition_colocationid - 1] = true;\n\tvalues[Anum_pg_dist_partition_colocationid - 1] = UInt32GetDatum(colocationId);\n\tisnull[Anum_pg_dist_partition_colocationid - 1] = false;\n\n\tint autoconvertedindex = GetAutoConvertedAttrIndexInPgDistPartition(tupleDescriptor);\n\treplace[autoconvertedindex] = true;\n\tvalues[autoconvertedindex] = BoolGetDatum(false);\n\tisnull[autoconvertedindex] = false;\n\n\tchar *distributionColumnString = nodeToString((Node *) distributionColumn);\n\n\treplace[Anum_pg_dist_partition_partkey - 1] = true;\n\tvalues[Anum_pg_dist_partition_partkey - 1] =\n\t\tCStringGetTextDatum(distributionColumnString);\n\tisnull[Anum_pg_dist_partition_partkey - 1] = false;\n\n\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull, replace);\n\n\tCatalogTupleUpdate(pgDistPartition, &heapTuple->t_self, heapTuple);\n\n\tCitusInvalidateRelcacheByRelid(relationId);\n\tCommandCounterIncrement();\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistPartition, NoLock);\n\n\tpfree(values);\n\tpfree(isnull);\n\tpfree(replace);\n}\n\n\n/*\n * UpdateNoneDistTableMetadataGlobally globally updates pg_dist_partition for\n * given none-distributed table.\n */\nvoid\nUpdateNoneDistTableMetadataGlobally(Oid relationId, char replicationModel,\n\t\t\t\t\t\t\t\t\tuint32 colocationId, bool autoConverted)\n{\n\tUpdateNoneDistTableMetadata(relationId, replicationModel,\n\t\t\t\t\t\t\t\tcolocationId, autoConverted);\n\n\tif (ShouldSyncTableMetadata(relationId))\n\t{\n\t\tchar *metadataCommand =\n\t\t\tUpdateNoneDistTableMetadataCommand(relationId,\n\t\t\t\t\t\t\t\t\t\t\t   replicationModel,\n\t\t\t\t\t\t\t\t\t\t\t   colocationId,\n\t\t\t\t\t\t\t\t\t\t\t   autoConverted);\n\t\tSendCommandToWorkersWithMetadata(metadataCommand);\n\t}\n}\n\n\n/*\n * UpdateNoneDistTableMetadata locally updates pg_dist_partition for given\n * none-distributed table.\n */\nvoid\nUpdateNoneDistTableMetadata(Oid relationId, char replicationModel, uint32 colocationId,\n\t\t\t\t\t\t\tbool autoConverted)\n{\n\tif (HasDistributionKey(relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot update metadata for a distributed \"\n\t\t\t\t\t\t\t   \"table that has a distribution column\")));\n\t}\n\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tbool indexOK = true;\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\tDatum *values = (Datum *) palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isnull = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_partition_logicalrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPartitionLogicalRelidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for Citus table with oid: %u\",\n\t\t\t\t\t\t\t   relationId)));\n\t}\n\n\tvalues[Anum_pg_dist_partition_colocationid - 1] = UInt32GetDatum(colocationId);\n\tisnull[Anum_pg_dist_partition_colocationid - 1] = false;\n\treplace[Anum_pg_dist_partition_colocationid - 1] = true;\n\n\tvalues[Anum_pg_dist_partition_repmodel - 1] = CharGetDatum(replicationModel);\n\tisnull[Anum_pg_dist_partition_repmodel - 1] = false;\n\treplace[Anum_pg_dist_partition_repmodel - 1] = true;\n\n\tint autoconvertedindex = GetAutoConvertedAttrIndexInPgDistPartition(tupleDescriptor);\n\tvalues[autoconvertedindex] = BoolGetDatum(autoConverted);\n\tisnull[autoconvertedindex] = false;\n\treplace[autoconvertedindex] = true;\n\n\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull, replace);\n\n\tCatalogTupleUpdate(pgDistPartition, &heapTuple->t_self, heapTuple);\n\n\tCitusInvalidateRelcacheByRelid(relationId);\n\tCommandCounterIncrement();\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistPartition, NoLock);\n\n\tpfree(values);\n\tpfree(isnull);\n\tpfree(replace);\n}\n\n\n/*\n * Check that the current user has `mode` permissions on relationId.\n * If not, also check relationId's attributes with `mask`, error out\n * privileges are not defined.\n * ACL mask is used because we assume that user has enough privilege\n * to distribute a table when either ACL_INSERT on the TABLE or\n * ACL_INSERT on ALL attributes.\n * In other situations, having a single attribute privilege is enough.\n * Superusers always have such permissions.\n */\nvoid\nEnsureTablePermissions(Oid relationId, AclMode mode, AclMaskHow mask)\n{\n\tAclResult aclresult = pg_class_aclcheck(relationId, GetUserId(), mode);\n\n\tif (aclresult == ACLCHECK_OK)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * Also check the attributes: for example \"GRANT ALL(a)\" has no table level\n\t * right but user is still allowed to lock table as needed. PostgreSQL will\n\t * still enforce ACL later so it's safe.\n\t */\n\taclresult = pg_attribute_aclcheck_all(relationId, GetUserId(), mode, mask);\n\n\tif (aclresult != ACLCHECK_OK)\n\t{\n\t\taclcheck_error(aclresult, OBJECT_TABLE, get_rel_name(relationId));\n\t}\n}\n\n\n/*\n * Check that the current user has owner rights to relationId, error out if\n * not. Superusers are regarded as owners.\n */\nvoid\nEnsureTableOwner(Oid relationId)\n{\n\tif (!object_ownercheck(RelationRelationId, relationId, GetUserId()))\n\t{\n\t\taclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TABLE,\n\t\t\t\t\t   get_rel_name(relationId));\n\t}\n}\n\n\n/*\n * Check that the current user has owner rights to schemaId, error out if\n * not. Superusers are regarded as owners.\n */\nvoid\nEnsureSchemaOwner(Oid schemaId)\n{\n\tif (!object_ownercheck(NamespaceRelationId, schemaId, GetUserId()))\n\t{\n\t\taclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,\n\t\t\t\t\t   get_namespace_name(schemaId));\n\t}\n}\n\n\n/*\n * Check that the current user has owner rights to functionId, error out if\n * not. Superusers are regarded as owners. Functions and procedures are\n * treated equally.\n */\nvoid\nEnsureFunctionOwner(Oid functionId)\n{\n\tif (!object_ownercheck(ProcedureRelationId, functionId, GetUserId()))\n\t{\n\t\taclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,\n\t\t\t\t\t   get_func_name(functionId));\n\t}\n}\n\n\n/*\n * EnsureHashDistributedTable error out if the given relation is not a hash distributed table\n * with the given message.\n */\nvoid\nEnsureHashDistributedTable(Oid relationId)\n{\n\tif (!IsCitusTableType(relationId, HASH_DISTRIBUTED))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"relation %s should be a \"\n\t\t\t\t\t\t\t   \"hash distributed table\", get_rel_name(relationId))));\n\t}\n}\n\n\n/*\n * EnsureHashOrSingleShardDistributedTable error out if the given relation is not a\n * hash or single shard distributed table with the given message.\n */\nvoid\nEnsureHashOrSingleShardDistributedTable(Oid relationId)\n{\n\tif (!IsCitusTableType(relationId, HASH_DISTRIBUTED) &&\n\t\t!IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"relation %s should be a \"\n\t\t\t\t\t\t\t   \"hash or single shard distributed table\",\n\t\t\t\t\t\t\t   get_rel_name(relationId))));\n\t}\n}\n\n\n/*\n * EnsureSuperUser check that the current user is a superuser and errors out if not.\n */\nvoid\nEnsureSuperUser(void)\n{\n\tif (!superuser())\n\t{\n\t\tereport(ERROR, (errmsg(\"operation is not allowed\"),\n\t\t\t\t\t\terrhint(\"Run the command with a superuser.\")));\n\t}\n}\n\n\nOid\nTableOwnerOid(Oid relationId)\n{\n\tHeapTuple tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationId));\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE),\n\t\t\t\t\t\terrmsg(\"relation with OID %u does not exist\", relationId)));\n\t}\n\n\tOid userId = ((Form_pg_class) GETSTRUCT(tuple))->relowner;\n\n\tReleaseSysCache(tuple);\n\treturn userId;\n}\n\n\n/*\n * Return a table's owner as a string.\n */\nchar *\nTableOwner(Oid relationId)\n{\n\treturn GetUserNameFromId(TableOwnerOid(relationId), false);\n}\n\n\n/*\n * IsForeignTable takes a relation id and returns true if it's a foreign table.\n * Returns false otherwise.\n */\nbool\nIsForeignTable(Oid relationId)\n{\n\treturn get_rel_relkind(relationId) == RELKIND_FOREIGN_TABLE;\n}\n\n\n/*\n * HasRunnableBackgroundTask looks in the catalog if there are any tasks that can be run.\n * For a task to be able to run the following conditions apply:\n *  - Task is in Running state. This could happen when a Background Tasks Queue Monitor\n *    had crashed or is otherwise restarted. To recover from such a failure tasks in\n *    Running state are deemed Runnable.\n *  - Task is in Runnable state with either _no_ value set in not_before, or a value that\n *    has currently passed. If the not_before field is set to a time in the future the\n *    task is currently not ready to be started.\n */\nbool\nHasRunnableBackgroundTask()\n{\n\tRelation pgDistBackgroundTasks =\n\t\ttable_open(DistBackgroundTaskRelationId(), AccessShareLock);\n\n\t/* find any job in states listed here */\n\tBackgroundTaskStatus taskStatus[] = {\n\t\tBACKGROUND_TASK_STATUS_RUNNABLE,\n\t\tBACKGROUND_TASK_STATUS_RUNNING\n\t};\n\n\tbool hasScheduledTask = false;\n\tfor (int i = 0; !hasScheduledTask && i < lengthof(taskStatus); i++)\n\t{\n\t\tconst int scanKeyCount = 1;\n\t\tScanKeyData scanKey[1] = { 0 };\n\t\tconst bool indexOK = true;\n\n\t\t/* pg_dist_background_task.status == taskStatus[i] */\n\t\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_status,\n\t\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\t\tObjectIdGetDatum(BackgroundTaskStatusOid(taskStatus[i])));\n\n\t\tSysScanDesc scanDescriptor =\n\t\t\tsystable_beginscan(pgDistBackgroundTasks,\n\t\t\t\t\t\t\t   DistBackgroundTaskStatusTaskIdIndexId(),\n\t\t\t\t\t\t\t   indexOK, NULL, scanKeyCount,\n\t\t\t\t\t\t\t   scanKey);\n\n\t\tHeapTuple taskTuple = NULL;\n\t\twhile (HeapTupleIsValid(taskTuple = systable_getnext(scanDescriptor)))\n\t\t{\n\t\t\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTasks);\n\t\t\tBackgroundTask *task = DeformBackgroundTaskHeapTuple(tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t taskTuple);\n\n\t\t\tif (task->not_before && *(task->not_before) > GetCurrentTimestamp())\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\thasScheduledTask = true;\n\t\t\tbreak;\n\t\t}\n\n\t\tsystable_endscan(scanDescriptor);\n\t}\n\n\ttable_close(pgDistBackgroundTasks, NoLock);\n\n\treturn hasScheduledTask;\n}\n\n\n/*\n * BackgroundJobStatusByOid returns the C enum representation of a BackgroundJobsStatus\n * based on the Oid of the SQL enum value.\n */\nBackgroundJobStatus\nBackgroundJobStatusByOid(Oid enumOid)\n{\n\tif (enumOid == CitusJobStatusScheduledId())\n\t{\n\t\treturn BACKGROUND_JOB_STATUS_SCHEDULED;\n\t}\n\telse if (enumOid == CitusJobStatusRunningId())\n\t{\n\t\treturn BACKGROUND_JOB_STATUS_RUNNING;\n\t}\n\telse if (enumOid == CitusJobStatusFinishedId())\n\t{\n\t\treturn BACKGROUND_JOB_STATUS_FINISHED;\n\t}\n\telse if (enumOid == CitusJobStatusCancelledId())\n\t{\n\t\treturn BACKGROUND_JOB_STATUS_CANCELLED;\n\t}\n\telse if (enumOid == CitusJobStatusFailingId())\n\t{\n\t\treturn BACKGROUND_JOB_STATUS_FAILING;\n\t}\n\telse if (enumOid == CitusJobStatusFailedId())\n\t{\n\t\treturn BACKGROUND_JOB_STATUS_FAILED;\n\t}\n\telse if (enumOid == CitusJobStatusCancellingId())\n\t{\n\t\treturn BACKGROUND_JOB_STATUS_CANCELLING;\n\t}\n\telog(ERROR, \"unknown enum value for citus_job_status\");\n}\n\n\n/*\n * BackgroundTaskStatusByOid returns the C enum representation of a BackgroundTaskStatus\n * based on the Oid of the SQL enum value.\n */\nBackgroundTaskStatus\nBackgroundTaskStatusByOid(Oid enumOid)\n{\n\tif (enumOid == CitusTaskStatusDoneId())\n\t{\n\t\treturn BACKGROUND_TASK_STATUS_DONE;\n\t}\n\telse if (enumOid == CitusTaskStatusRunnableId())\n\t{\n\t\treturn BACKGROUND_TASK_STATUS_RUNNABLE;\n\t}\n\telse if (enumOid == CitusTaskStatusRunningId())\n\t{\n\t\treturn BACKGROUND_TASK_STATUS_RUNNING;\n\t}\n\telse if (enumOid == CitusTaskStatusErrorId())\n\t{\n\t\treturn BACKGROUND_TASK_STATUS_ERROR;\n\t}\n\telse if (enumOid == CitusTaskStatusUnscheduledId())\n\t{\n\t\treturn BACKGROUND_TASK_STATUS_UNSCHEDULED;\n\t}\n\telse if (enumOid == CitusTaskStatusBlockedId())\n\t{\n\t\treturn BACKGROUND_TASK_STATUS_BLOCKED;\n\t}\n\telse if (enumOid == CitusTaskStatusCancelledId())\n\t{\n\t\treturn BACKGROUND_TASK_STATUS_CANCELLED;\n\t}\n\telse if (enumOid == CitusTaskStatusCancellingId())\n\t{\n\t\treturn BACKGROUND_TASK_STATUS_CANCELLING;\n\t}\n\tereport(ERROR, (errmsg(\"unknown enum value for citus_task_status\")));\n}\n\n\n/*\n * IsBackgroundJobStatusTerminal is a predicate returning if the BackgroundJobStatus\n * passed is a terminal state of the Background Job state machine.\n *\n * For a Job to be in it's terminal state, all tasks from that job should also be in their\n * terminal state.\n */\nbool\nIsBackgroundJobStatusTerminal(BackgroundJobStatus status)\n{\n\tswitch (status)\n\t{\n\t\tcase BACKGROUND_JOB_STATUS_CANCELLED:\n\t\tcase BACKGROUND_JOB_STATUS_FAILED:\n\t\tcase BACKGROUND_JOB_STATUS_FINISHED:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tcase BACKGROUND_JOB_STATUS_CANCELLING:\n\t\tcase BACKGROUND_JOB_STATUS_FAILING:\n\t\tcase BACKGROUND_JOB_STATUS_RUNNING:\n\t\tcase BACKGROUND_JOB_STATUS_SCHEDULED:\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t\t/* no default to make sure we explicitly add every state here */\n\t}\n\telog(ERROR, \"unknown BackgroundJobStatus\");\n}\n\n\n/*\n * IsBackgroundTaskStatusTerminal is a predicate returning if the BackgroundTaskStatus\n * passed is a terminal state of the Background Task state machine.\n */\nbool\nIsBackgroundTaskStatusTerminal(BackgroundTaskStatus status)\n{\n\tswitch (status)\n\t{\n\t\tcase BACKGROUND_TASK_STATUS_CANCELLED:\n\t\tcase BACKGROUND_TASK_STATUS_DONE:\n\t\tcase BACKGROUND_TASK_STATUS_ERROR:\n\t\tcase BACKGROUND_TASK_STATUS_UNSCHEDULED:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tcase BACKGROUND_TASK_STATUS_BLOCKED:\n\t\tcase BACKGROUND_TASK_STATUS_CANCELLING:\n\t\tcase BACKGROUND_TASK_STATUS_RUNNABLE:\n\t\tcase BACKGROUND_TASK_STATUS_RUNNING:\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t\t/* no default to make sure we explicitly add every state here */\n\t}\n\telog(ERROR, \"unknown BackgroundTaskStatus\");\n}\n\n\n/*\n * BackgroundJobStatusOid returns the Oid corresponding to SQL enum value corresponding to\n * the BackgroundJobStatus.\n */\nOid\nBackgroundJobStatusOid(BackgroundJobStatus status)\n{\n\tswitch (status)\n\t{\n\t\tcase BACKGROUND_JOB_STATUS_SCHEDULED:\n\t\t{\n\t\t\treturn CitusJobStatusScheduledId();\n\t\t}\n\n\t\tcase BACKGROUND_JOB_STATUS_RUNNING:\n\t\t{\n\t\t\treturn CitusJobStatusRunningId();\n\t\t}\n\n\t\tcase BACKGROUND_JOB_STATUS_CANCELLING:\n\t\t{\n\t\t\treturn CitusJobStatusCancellingId();\n\t\t}\n\n\t\tcase BACKGROUND_JOB_STATUS_FINISHED:\n\t\t{\n\t\t\treturn CitusJobStatusFinishedId();\n\t\t}\n\n\t\tcase BACKGROUND_JOB_STATUS_CANCELLED:\n\t\t{\n\t\t\treturn CitusJobStatusCancelledId();\n\t\t}\n\n\t\tcase BACKGROUND_JOB_STATUS_FAILING:\n\t\t{\n\t\t\treturn CitusJobStatusFailingId();\n\t\t}\n\n\t\tcase BACKGROUND_JOB_STATUS_FAILED:\n\t\t{\n\t\t\treturn CitusJobStatusFailedId();\n\t\t}\n\t}\n\n\telog(ERROR, \"unknown BackgroundJobStatus\");\n}\n\n\n/*\n * BackgroundTaskStatusOid returns the Oid corresponding to SQL enum value corresponding to\n * the BackgroundTaskStatus.\n */\nOid\nBackgroundTaskStatusOid(BackgroundTaskStatus status)\n{\n\tswitch (status)\n\t{\n\t\tcase BACKGROUND_TASK_STATUS_BLOCKED:\n\t\t{\n\t\t\treturn CitusTaskStatusBlockedId();\n\t\t}\n\n\t\tcase BACKGROUND_TASK_STATUS_RUNNABLE:\n\t\t{\n\t\t\treturn CitusTaskStatusRunnableId();\n\t\t}\n\n\t\tcase BACKGROUND_TASK_STATUS_RUNNING:\n\t\t{\n\t\t\treturn CitusTaskStatusRunningId();\n\t\t}\n\n\t\tcase BACKGROUND_TASK_STATUS_DONE:\n\t\t{\n\t\t\treturn CitusTaskStatusDoneId();\n\t\t}\n\n\t\tcase BACKGROUND_TASK_STATUS_ERROR:\n\t\t{\n\t\t\treturn CitusTaskStatusErrorId();\n\t\t}\n\n\t\tcase BACKGROUND_TASK_STATUS_UNSCHEDULED:\n\t\t{\n\t\t\treturn CitusTaskStatusUnscheduledId();\n\t\t}\n\n\t\tcase BACKGROUND_TASK_STATUS_CANCELLED:\n\t\t{\n\t\t\treturn CitusTaskStatusCancelledId();\n\t\t}\n\n\t\tcase BACKGROUND_TASK_STATUS_CANCELLING:\n\t\t{\n\t\t\treturn CitusTaskStatusCancellingId();\n\t\t}\n\t}\n\n\telog(ERROR, \"unknown BackgroundTaskStatus\");\n\treturn InvalidOid;\n}\n\n\n/*\n * GetNextBackgroundJobsJobId reads and increments the SQL sequence associated with the\n * background job's job_id. After incrementing the counter it returns the counter back to\n * the caller.\n *\n * The return value is typically used to insert new jobs into the catalog.\n */\nstatic int64\nGetNextBackgroundJobsJobId(void)\n{\n\treturn DatumGetInt64(nextval_internal(DistBackgroundJobJobIdSequenceId(), false));\n}\n\n\n/*\n * GetNextBackgroundTaskTaskId reads and increments the SQL sequence associated with the\n * background job tasks' task_id. After incrementing the counter it returns the counter\n * back to the caller.\n *\n * The return value is typically used to insert new tasks into the catalog.\n */\nstatic int64\nGetNextBackgroundTaskTaskId(void)\n{\n\treturn DatumGetInt64(nextval_internal(DistBackgroundTaskTaskIdSequenceId(), false));\n}\n\n\n/*\n * HasNonTerminalJobOfType returns true if there is a job of a given type that is not in\n * its terminal state.\n *\n * Some jobs would want a single instance to be able to run at once. Before submitting a\n * new job if could see if there is a job of their type already executing.\n *\n * If a job is found the options jobIdOut is populated with the jobId.\n */\nbool\nHasNonTerminalJobOfType(const char *jobType, int64 *jobIdOut)\n{\n\tRelation pgDistBackgroundJob =\n\t\ttable_open(DistBackgroundJobRelationId(), AccessShareLock);\n\n\t/* find any job in states listed here */\n\tBackgroundJobStatus jobStatus[] = {\n\t\tBACKGROUND_JOB_STATUS_RUNNING,\n\t\tBACKGROUND_JOB_STATUS_CANCELLING,\n\t\tBACKGROUND_JOB_STATUS_FAILING,\n\t\tBACKGROUND_JOB_STATUS_SCHEDULED\n\t};\n\n\tNameData jobTypeName = { 0 };\n\tnamestrcpy(&jobTypeName, jobType);\n\n\tbool foundJob = false;\n\tfor (int i = 0; !foundJob && i < lengthof(jobStatus); i++)\n\t{\n\t\tScanKeyData scanKey[2] = { 0 };\n\t\tconst bool indexOK = true;\n\n\t\t/* pg_dist_background_job.status == jobStatus[i] */\n\t\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_job_state,\n\t\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\t\tObjectIdGetDatum(BackgroundJobStatusOid(jobStatus[i])));\n\n\t\t/* pg_dist_background_job.job_type == jobType */\n\t\tScanKeyInit(&scanKey[1], Anum_pg_dist_background_job_job_type,\n\t\t\t\t\tBTEqualStrategyNumber, F_NAMEEQ,\n\t\t\t\t\tNameGetDatum(&jobTypeName));\n\n\t\tSysScanDesc scanDescriptor =\n\t\t\tsystable_beginscan(pgDistBackgroundJob,\n\t\t\t\t\t\t\t   InvalidOid,     /* TODO use an actual index here */\n\t\t\t\t\t\t\t   indexOK, NULL, lengthof(scanKey), scanKey);\n\n\t\tHeapTuple taskTuple = NULL;\n\t\tif (HeapTupleIsValid(taskTuple = systable_getnext(scanDescriptor)))\n\t\t{\n\t\t\tfoundJob = true;\n\n\t\t\tif (jobIdOut)\n\t\t\t{\n\t\t\t\tDatum values[Natts_pg_dist_background_job] = { 0 };\n\t\t\t\tbool isnull[Natts_pg_dist_background_job] = { 0 };\n\n\t\t\t\tTupleDesc tupleDesc = RelationGetDescr(pgDistBackgroundJob);\n\t\t\t\theap_deform_tuple(taskTuple, tupleDesc, values, isnull);\n\n\t\t\t\t*jobIdOut = DatumGetInt64(values[Anum_pg_dist_background_job_job_id - 1]);\n\t\t\t}\n\t\t}\n\n\t\tsystable_endscan(scanDescriptor);\n\t}\n\n\ttable_close(pgDistBackgroundJob, NoLock);\n\n\treturn foundJob;\n}\n\n\n/*\n * CreateBackgroundJob is a helper function to insert a new Background Job into Citus'\n * catalog. After inserting the new job's metadataa into the catalog it returns the job_id\n * assigned to the new job. This is typically used to associate new tasks with the newly\n * created job.\n */\nint64\nCreateBackgroundJob(const char *jobType, const char *description)\n{\n\tRelation pgDistBackgroundJobs =\n\t\ttable_open(DistBackgroundJobRelationId(), RowExclusiveLock);\n\n\t/* insert new job */\n\tDatum values[Natts_pg_dist_background_job] = { 0 };\n\tbool isnull[Natts_pg_dist_background_job] = { 0 };\n\n\tNameData jobTypeName = { 0 };\n\tmemset(isnull, true, sizeof(isnull));\n\n\tint64 jobId = GetNextBackgroundJobsJobId();\n\n\tInitFieldValue(Anum_pg_dist_background_job_job_id, values, isnull,\n\t\t\t\t   Int64GetDatum(jobId));\n\n\tInitFieldValue(Anum_pg_dist_background_job_state, values, isnull,\n\t\t\t\t   ObjectIdGetDatum(CitusJobStatusScheduledId()));\n\n\tif (jobType)\n\t{\n\t\tnamestrcpy(&jobTypeName, jobType);\n\t\tInitFieldValue(Anum_pg_dist_background_job_job_type, values, isnull,\n\t\t\t\t\t   NameGetDatum(&jobTypeName));\n\t}\n\n\tif (description)\n\t{\n\t\tInitFieldText(Anum_pg_dist_background_job_description, values, isnull,\n\t\t\t\t\t  description);\n\t}\n\n\tHeapTuple newTuple = heap_form_tuple(RelationGetDescr(pgDistBackgroundJobs),\n\t\t\t\t\t\t\t\t\t\t values, isnull);\n\tCatalogTupleInsert(pgDistBackgroundJobs, newTuple);\n\n\tCommandCounterIncrement();\n\n\ttable_close(pgDistBackgroundJobs, NoLock);\n\n\treturn jobId;\n}\n\n\n/*\n * ScheduleBackgroundTask creates a new background task to be executed in the background.\n *\n * The new task is associated with an existing job based on it's id.\n *\n * Optionally the new task can depend on separate tasks associated with the same job. When\n * a new task is created with dependencies on previous tasks we assume this task is\n * blocked on its depending tasks.\n */\nBackgroundTask *\nScheduleBackgroundTask(int64 jobId, Oid owner, char *command, int dependingTaskCount,\n\t\t\t\t\t   int64 dependingTaskIds[], int nodesInvolvedCount, int32\n\t\t\t\t\t   nodesInvolved[])\n{\n\tBackgroundTask *task = NULL;\n\n\tRelation pgDistBackgroundJob =\n\t\ttable_open(DistBackgroundJobRelationId(), ExclusiveLock);\n\tRelation pgDistBackgroundTask =\n\t\ttable_open(DistBackgroundTaskRelationId(), ExclusiveLock);\n\tRelation pgDistbackgroundTasksDepend = NULL;\n\tif (dependingTaskCount > 0)\n\t{\n\t\tpgDistbackgroundTasksDepend =\n\t\t\ttable_open(DistBackgroundTaskDependRelationId(), ExclusiveLock);\n\t}\n\n\t/* 1. verify job exist */\n\t{\n\t\tScanKeyData scanKey[1] = { 0 };\n\t\tbool indexOK = true;\n\n\t\t/* pg_dist_background_job.job_id == $jobId */\n\t\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_job_job_id,\n\t\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(jobId));\n\n\t\tSysScanDesc scanDescriptor =\n\t\t\tsystable_beginscan(pgDistBackgroundJob,\n\t\t\t\t\t\t\t   DistBackgroundJobPKeyIndexId(),\n\t\t\t\t\t\t\t   indexOK, NULL, lengthof(scanKey), scanKey);\n\n\t\tHeapTuple jobTuple = systable_getnext(scanDescriptor);\n\t\tif (!HeapTupleIsValid(jobTuple))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"job for newly created task does not exist.\")));\n\t\t}\n\n\t\tsystable_endscan(scanDescriptor);\n\t}\n\n\t/* 2. insert new task */\n\t{\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTask);\n\n\t\tDatum *values = (Datum *) palloc0(tupleDescriptor->natts * sizeof(Datum));\n\t\tbool *nulls = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\t\tint64 taskId = GetNextBackgroundTaskTaskId();\n\n\t\tvalues[Anum_pg_dist_background_task_job_id - 1] = Int64GetDatum(jobId);\n\t\tnulls[Anum_pg_dist_background_task_job_id - 1] = false;\n\n\t\tvalues[Anum_pg_dist_background_task_task_id - 1] = Int64GetDatum(taskId);\n\t\tnulls[Anum_pg_dist_background_task_task_id - 1] = false;\n\n\t\tvalues[Anum_pg_dist_background_task_owner - 1] = ObjectIdGetDatum(owner);\n\t\tnulls[Anum_pg_dist_background_task_owner - 1] = false;\n\n\t\tOid statusOid = InvalidOid;\n\t\tif (dependingTaskCount == 0)\n\t\t{\n\t\t\tstatusOid = CitusTaskStatusRunnableId();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstatusOid = CitusTaskStatusBlockedId();\n\t\t}\n\t\tvalues[Anum_pg_dist_background_task_status - 1] = ObjectIdGetDatum(statusOid);\n\t\tnulls[Anum_pg_dist_background_task_status - 1] = false;\n\n\t\tvalues[Anum_pg_dist_background_task_command - 1] = CStringGetTextDatum(command);\n\t\tnulls[Anum_pg_dist_background_task_command - 1] = false;\n\n\t\tvalues[Anum_pg_dist_background_task_message - 1] = CStringGetTextDatum(\"\");\n\t\tnulls[Anum_pg_dist_background_task_message - 1] = false;\n\n\t\tint nodesInvolvedIndex =\n\t\t\tGetNodesInvolvedAttrIndexInPgDistBackgroundTask(tupleDescriptor);\n\t\tvalues[nodesInvolvedIndex] = IntArrayToDatum(nodesInvolvedCount, nodesInvolved);\n\t\tnulls[nodesInvolvedIndex] = (nodesInvolvedCount == 0);\n\n\t\tHeapTuple newTuple = heap_form_tuple(tupleDescriptor, values, nulls);\n\t\tCatalogTupleInsert(pgDistBackgroundTask, newTuple);\n\n\t\tpfree(values);\n\t\tpfree(nulls);\n\n\t\ttask = palloc0(sizeof(BackgroundTask));\n\t\ttask->taskid = taskId;\n\t\ttask->status = BACKGROUND_TASK_STATUS_RUNNABLE;\n\t\ttask->command = pstrdup(command);\n\t}\n\n\t/* 3. insert dependencies into catalog */\n\t{\n\t\tfor (int i = 0; i < dependingTaskCount; i++)\n\t\t{\n\t\t\t/* 3.1 after verifying the task exists for this job */\n\t\t\t{\n\t\t\t\tScanKeyData scanKey[2] = { 0 };\n\t\t\t\tbool indexOK = true;\n\n\t\t\t\t/* pg_dist_background_task.job_id == $jobId */\n\t\t\t\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_job_id,\n\t\t\t\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(jobId));\n\n\t\t\t\t/* pg_dist_background_task.task_id == $taskId */\n\t\t\t\tScanKeyInit(&scanKey[1], Anum_pg_dist_background_task_task_id,\n\t\t\t\t\t\t\tBTEqualStrategyNumber, F_INT8EQ,\n\t\t\t\t\t\t\tInt64GetDatum(dependingTaskIds[i]));\n\n\t\t\t\tSysScanDesc scanDescriptor =\n\t\t\t\t\tsystable_beginscan(pgDistBackgroundTask,\n\t\t\t\t\t\t\t\t\t   DistBackgroundTaskJobIdTaskIdIndexId(),\n\t\t\t\t\t\t\t\t\t   indexOK, NULL, lengthof(scanKey), scanKey);\n\n\t\t\t\tHeapTuple taskTuple = systable_getnext(scanDescriptor);\n\t\t\t\tif (!HeapTupleIsValid(taskTuple))\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR, (errmsg(\"depending task for newly scheduled task does \"\n\t\t\t\t\t\t\t\t\t\t   \"not exist\")));\n\t\t\t\t}\n\n\t\t\t\tsystable_endscan(scanDescriptor);\n\t\t\t}\n\n\t\t\tAssert(pgDistbackgroundTasksDepend != NULL);\n\n\t\t\tDatum values[Natts_pg_dist_background_task_depend] = { 0 };\n\t\t\tbool nulls[Natts_pg_dist_background_task_depend] = { 0 };\n\t\t\tmemset(nulls, true, sizeof(nulls));\n\n\t\t\tvalues[Anum_pg_dist_background_task_depend_job_id - 1] =\n\t\t\t\tInt64GetDatum(jobId);\n\t\t\tnulls[Anum_pg_dist_background_task_depend_job_id - 1] = false;\n\n\t\t\tvalues[Anum_pg_dist_background_task_depend_task_id - 1] =\n\t\t\t\tInt64GetDatum(task->taskid);\n\t\t\tnulls[Anum_pg_dist_background_task_depend_task_id - 1] = false;\n\n\t\t\tvalues[Anum_pg_dist_background_task_depend_depends_on - 1] =\n\t\t\t\tInt64GetDatum(dependingTaskIds[i]);\n\t\t\tnulls[Anum_pg_dist_background_task_depend_depends_on - 1] = false;\n\n\t\t\tHeapTuple newTuple = heap_form_tuple(\n\t\t\t\tRelationGetDescr(pgDistbackgroundTasksDepend), values, nulls);\n\t\t\tCatalogTupleInsert(pgDistbackgroundTasksDepend, newTuple);\n\t\t}\n\t}\n\n\tif (pgDistbackgroundTasksDepend)\n\t{\n\t\ttable_close(pgDistbackgroundTasksDepend, NoLock);\n\t}\n\ttable_close(pgDistBackgroundTask, NoLock);\n\ttable_close(pgDistBackgroundJob, NoLock);\n\n\tCommandCounterIncrement();\n\n\treturn task;\n}\n\n\n/*\n * ResetRunningBackgroundTasks finds all tasks currently in Running state and resets their\n * state back to runnable.\n *\n * While marking running tasks as runnable we check if the task might still be locked and\n * the pid is managed by our current postmaster. If both are the case we terminate the\n * backend. This will make sure that if a task was still running after a monitor crash or\n * restart we stop the executor before we start a new one.\n *\n * Any pid associated with the running tasks will be cleared back to the NULL value.\n */\nvoid\nResetRunningBackgroundTasks(void)\n{\n\tconst int scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\tconst bool indexOK = true;\n\n\tRelation pgDistBackgroundTasks =\n\t\ttable_open(DistBackgroundTaskRelationId(), ExclusiveLock);\n\n\t/* pg_dist_background_task.status == 'running' */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_status,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(CitusTaskStatusRunningId()));\n\n\tSysScanDesc scanDescriptor =\n\t\tsystable_beginscan(pgDistBackgroundTasks,\n\t\t\t\t\t\t   DistBackgroundTaskStatusTaskIdIndexId(),\n\t\t\t\t\t\t   indexOK, NULL, scanKeyCount,\n\t\t\t\t\t\t   scanKey);\n\n\tHeapTuple taskTuple = NULL;\n\tList *taskIdsToWait = NIL;\n\twhile (HeapTupleIsValid(taskTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTasks);\n\n\t\tDatum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\t\tbool *isnull = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\t\tbool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\t\theap_deform_tuple(taskTuple, tupleDescriptor, values, isnull);\n\n\t\tvalues[Anum_pg_dist_background_task_status - 1] =\n\t\t\tObjectIdGetDatum(CitusTaskStatusRunnableId());\n\t\tisnull[Anum_pg_dist_background_task_status - 1] = false;\n\t\treplace[Anum_pg_dist_background_task_status - 1] = true;\n\n\t\t/* if there is a pid we need to signal the backend to stop */\n\t\tif (!isnull[Anum_pg_dist_background_task_pid - 1])\n\t\t{\n\t\t\t/*\n\t\t\t * Before signalling the pid we check if the task lock is held, otherwise we\n\t\t\t * might cancel an arbitrary postgres backend\n\t\t\t */\n\n\t\t\tint64 taskId =\n\t\t\t\tDatumGetInt64(values[Anum_pg_dist_background_task_task_id - 1]);\n\n\t\t\t/* No need to release lock, will get unlocked once our changes commit */\n\t\t\tLOCKTAG locktag = { 0 };\n\t\t\tSET_LOCKTAG_BACKGROUND_TASK(locktag, taskId);\n\t\t\tconst bool sessionLock = false;\n\t\t\tconst bool dontWait = true;\n\t\t\tLockAcquireResult locked = LockAcquire(&locktag, AccessExclusiveLock,\n\t\t\t\t\t\t\t\t\t\t\t\t   sessionLock, dontWait);\n\t\t\tif (locked == LOCKACQUIRE_NOT_AVAIL)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * There is still an executor holding the lock, needs a SIGTERM.\n\t\t\t\t */\n\t\t\t\tDatum pidDatum = values[Anum_pg_dist_background_task_pid - 1];\n\t\t\t\tconst Datum timeoutDatum = Int64GetDatum(0);\n\t\t\t\tDatum signalSuccessDatum = DirectFunctionCall2(pg_terminate_backend,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   pidDatum, timeoutDatum);\n\t\t\t\tbool signalSuccess = DatumGetBool(signalSuccessDatum);\n\t\t\t\tif (!signalSuccess)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * We run this backend as superuser, any failure will probably cause\n\t\t\t\t\t * long delays waiting on the task lock before we can commit.\n\t\t\t\t\t */\n\t\t\t\t\tereport(WARNING,\n\t\t\t\t\t\t\t(errmsg(\"could not send signal to process %d: %m\",\n\t\t\t\t\t\t\t\t\tDatumGetInt32(pidDatum)),\n\t\t\t\t\t\t\t errdetail(\"failing to signal an old executor could cause \"\n\t\t\t\t\t\t\t\t\t   \"delays starting the background task monitor\")));\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * Since we didn't already acquire the lock here we need to wait on this\n\t\t\t\t * lock before committing the change to the catalog. However, we first\n\t\t\t\t * want to signal all backends before waiting on the lock, hence we keep a\n\t\t\t\t * list for later\n\t\t\t\t */\n\t\t\t\tint64 *taskIdTarget = palloc0(sizeof(int64));\n\t\t\t\t*taskIdTarget = taskId;\n\t\t\t\ttaskIdsToWait = lappend(taskIdsToWait, taskIdTarget);\n\t\t\t}\n\t\t}\n\n\t\tvalues[Anum_pg_dist_background_task_pid - 1] = InvalidOid;\n\t\tisnull[Anum_pg_dist_background_task_pid - 1] = true;\n\t\treplace[Anum_pg_dist_background_task_pid - 1] = true;\n\n\t\ttaskTuple = heap_modify_tuple(taskTuple, tupleDescriptor, values, isnull,\n\t\t\t\t\t\t\t\t\t  replace);\n\n\t\tCatalogTupleUpdate(pgDistBackgroundTasks, &taskTuple->t_self, taskTuple);\n\n\t\tpfree(values);\n\t\tpfree(isnull);\n\t\tpfree(replace);\n\t}\n\n\tif (list_length(taskIdsToWait) > 0)\n\t{\n\t\tereport(LOG, (errmsg(\"waiting till all tasks release their lock before \"\n\t\t\t\t\t\t\t \"continuing with the background task monitor\")));\n\n\t\t/* there are tasks that need to release their lock before we can continue */\n\t\tint64 *taskId = NULL;\n\t\tforeach_declared_ptr(taskId, taskIdsToWait)\n\t\t{\n\t\t\tLOCKTAG locktag = { 0 };\n\t\t\tSET_LOCKTAG_BACKGROUND_TASK(locktag, *taskId);\n\t\t\tconst bool sessionLock = false;\n\t\t\tconst bool dontWait = false;\n\t\t\t(void) LockAcquire(&locktag, AccessExclusiveLock, sessionLock, dontWait);\n\t\t}\n\t}\n\n\tCommandCounterIncrement();\n\n\tsystable_endscan(scanDescriptor);\n\n\ttable_close(pgDistBackgroundTasks, NoLock);\n}\n\n\n/*\n * DeformBackgroundJobHeapTuple pareses a HeapTuple from pg_dist_background_job into its\n * inmemory representation. This can be used while scanning a heap to quickly get access\n * to all fields of a Job.\n */\nstatic BackgroundJob *\nDeformBackgroundJobHeapTuple(TupleDesc tupleDescriptor, HeapTuple jobTuple)\n{\n\tDatum values[Natts_pg_dist_background_job] = { 0 };\n\tbool nulls[Natts_pg_dist_background_job] = { 0 };\n\theap_deform_tuple(jobTuple, tupleDescriptor, values, nulls);\n\n\tBackgroundJob *job = palloc0(sizeof(BackgroundJob));\n\tjob->jobid = DatumGetInt64(values[Anum_pg_dist_background_job_job_id - 1]);\n\tjob->state = BackgroundJobStatusByOid(\n\t\tDatumGetObjectId(values[Anum_pg_dist_background_job_state - 1]));\n\n\tif (!nulls[Anum_pg_dist_background_job_job_type - 1])\n\t{\n\t\tName jobTypeName = DatumGetName(values[Anum_pg_dist_background_job_job_type -\n\t\t\t\t\t\t\t\t\t\t\t   1]);\n\t\tjob->jobType = pstrdup(NameStr(*jobTypeName));\n\t}\n\n\tif (!nulls[Anum_pg_dist_background_job_description - 1])\n\t{\n\t\tjob->description = text_to_cstring(\n\t\t\tDatumGetTextP(values[Anum_pg_dist_background_job_description - 1]));\n\t}\n\n\tif (!nulls[Anum_pg_dist_background_job_started_at - 1])\n\t{\n\t\tTimestampTz startedAt =\n\t\t\tDatumGetTimestampTz(values[Anum_pg_dist_background_job_started_at - 1]);\n\t\tSET_NULLABLE_FIELD(job, started_at, startedAt);\n\t}\n\n\tif (!nulls[Anum_pg_dist_background_job_finished_at - 1])\n\t{\n\t\tTimestampTz finishedAt =\n\t\t\tDatumGetTimestampTz(values[Anum_pg_dist_background_job_finished_at - 1]);\n\t\tSET_NULLABLE_FIELD(job, finished_at, finishedAt);\n\t}\n\n\treturn job;\n}\n\n\n/*\n * DeformBackgroundTaskHeapTuple parses a HeapTuple from pg_dist_background_task into its\n * inmemory representation. This can be used while scanning a heap to quickly get access\n * to all fields of a Task.\n */\nstatic BackgroundTask *\nDeformBackgroundTaskHeapTuple(TupleDesc tupleDescriptor, HeapTuple taskTuple)\n{\n\tDatum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *nulls = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\n\theap_deform_tuple(taskTuple, tupleDescriptor, values, nulls);\n\n\tBackgroundTask *task = palloc0(sizeof(BackgroundTask));\n\ttask->jobid = DatumGetInt64(values[Anum_pg_dist_background_task_job_id - 1]);\n\ttask->taskid = DatumGetInt64(values[Anum_pg_dist_background_task_task_id - 1]);\n\ttask->owner = DatumGetObjectId(values[Anum_pg_dist_background_task_owner - 1]);\n\tif (!nulls[Anum_pg_dist_background_task_pid - 1])\n\t{\n\t\tint32 pid = DatumGetInt32(values[Anum_pg_dist_background_task_pid - 1]);\n\t\tSET_NULLABLE_FIELD(task, pid, pid);\n\t}\n\ttask->status = BackgroundTaskStatusByOid(\n\t\tDatumGetObjectId(values[Anum_pg_dist_background_task_status - 1]));\n\n\ttask->command = text_to_cstring(\n\t\tDatumGetTextP(values[Anum_pg_dist_background_task_command - 1]));\n\n\tif (!nulls[Anum_pg_dist_background_task_retry_count - 1])\n\t{\n\t\tint32 retryCount =\n\t\t\tDatumGetInt32(values[Anum_pg_dist_background_task_retry_count - 1]);\n\t\tSET_NULLABLE_FIELD(task, retry_count, retryCount);\n\t}\n\n\tif (!nulls[Anum_pg_dist_background_task_not_before - 1])\n\t{\n\t\tTimestampTz notBefore =\n\t\t\tDatumGetTimestampTz(values[Anum_pg_dist_background_task_not_before - 1]);\n\t\tSET_NULLABLE_FIELD(task, not_before, notBefore);\n\t}\n\n\tif (!nulls[Anum_pg_dist_background_task_message - 1])\n\t{\n\t\ttask->message =\n\t\t\tTextDatumGetCString(values[Anum_pg_dist_background_task_message - 1]);\n\t}\n\n\tint nodesInvolvedIndex =\n\t\tGetNodesInvolvedAttrIndexInPgDistBackgroundTask(tupleDescriptor);\n\tif (!nulls[nodesInvolvedIndex])\n\t{\n\t\tArrayType *nodesInvolvedArrayObject =\n\t\t\tDatumGetArrayTypeP(values[nodesInvolvedIndex]);\n\t\ttask->nodesInvolved = IntegerArrayTypeToList(nodesInvolvedArrayObject);\n\t}\n\n\tpfree(values);\n\tpfree(nulls);\n\n\treturn task;\n}\n\n\n/*\n * BackgroundTaskHasUmnetDependencies checks if a task from the given job has any unmet\n * dependencies. An unmet dependency is a Task that the task in question depends on and\n * has not reached its Done state.\n */\nstatic bool\nBackgroundTaskHasUmnetDependencies(int64 jobId, int64 taskId)\n{\n\tbool hasUnmetDependency = false;\n\n\tRelation pgDistBackgroundTasksDepend =\n\t\ttable_open(DistBackgroundTaskDependRelationId(), AccessShareLock);\n\n\tScanKeyData scanKey[2] = { 0 };\n\tbool indexOK = true;\n\n\t/* pg_catalog.pg_dist_background_task_depend.job_id = jobId */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_depend_job_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(jobId));\n\n\t/* pg_catalog.pg_dist_background_task_depend.task_id = $taskId */\n\tScanKeyInit(&scanKey[1], Anum_pg_dist_background_task_depend_task_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(taskId));\n\n\tSysScanDesc scanDescriptor =\n\t\tsystable_beginscan(pgDistBackgroundTasksDepend,\n\t\t\t\t\t\t   DistBackgroundTaskDependTaskIdIndexId(),\n\t\t\t\t\t\t   indexOK, NULL, lengthof(scanKey),\n\t\t\t\t\t\t   scanKey);\n\n\tHeapTuple dependTuple = NULL;\n\twhile (HeapTupleIsValid(dependTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tForm_pg_dist_background_task_depend depends =\n\t\t\t(Form_pg_dist_background_task_depend) GETSTRUCT(dependTuple);\n\n\t\tBackgroundTask *dependingJob = GetBackgroundTaskByTaskId(depends->depends_on);\n\n\t\t/*\n\t\t * Only when the status of all depending jobs is done we clear this job and say\n\t\t * that is has no unmet dependencies.\n\t\t */\n\t\tif (dependingJob->status == BACKGROUND_TASK_STATUS_DONE)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\thasUnmetDependency = true;\n\t\tbreak;\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistBackgroundTasksDepend, AccessShareLock);\n\n\treturn hasUnmetDependency;\n}\n\n\n/*\n * BackgroundTaskReadyToRun checks if a task is ready to run. This consists of two checks\n *  - the task has no unmet dependencies\n *  - the task either has no not_before value set, or the not_before time has passed.\n *\n * Due to the costs of checking we check them in reverse order, but conceptually they\n * should be thought of in the above order.\n */\nstatic bool\nBackgroundTaskReadyToRun(BackgroundTask *task)\n{\n\tif (task->not_before)\n\t{\n\t\tif (*(task->not_before) > GetCurrentTimestamp())\n\t\t{\n\t\t\t/* task should not yet be run */\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (BackgroundTaskHasUmnetDependencies(task->jobid, task->taskid))\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * GetRunnableBackgroundTask returns the first candidate for a task to be run. When a task\n * is returned it has been checked for all the preconditions to hold.\n *\n * That means, if there is no task returned the background worker should close and let the\n * maintenance daemon start a new background tasks queue monitor once task become\n * available.\n */\nBackgroundTask *\nGetRunnableBackgroundTask(void)\n{\n\tRelation pgDistBackgroundTasks =\n\t\ttable_open(DistBackgroundTaskRelationId(), ExclusiveLock);\n\n\tBackgroundTaskStatus taskStatus[] = {\n\t\tBACKGROUND_TASK_STATUS_RUNNABLE\n\t};\n\n\tBackgroundTask *task = NULL;\n\tfor (int i = 0; !task && i < sizeof(taskStatus) / sizeof(taskStatus[0]); i++)\n\t{\n\t\tconst int scanKeyCount = 1;\n\t\tScanKeyData scanKey[1] = { 0 };\n\t\tconst bool indexOK = true;\n\n\t\t/* pg_dist_background_task.status == taskStatus[i] */\n\t\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_status,\n\t\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(\n\t\t\t\t\t\tBackgroundTaskStatusOid(taskStatus[i])));\n\n\t\tSysScanDesc scanDescriptor =\n\t\t\tsystable_beginscan(pgDistBackgroundTasks,\n\t\t\t\t\t\t\t   DistBackgroundTaskStatusTaskIdIndexId(),\n\t\t\t\t\t\t\t   indexOK, NULL, scanKeyCount,\n\t\t\t\t\t\t\t   scanKey);\n\n\t\tHeapTuple taskTuple = NULL;\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTasks);\n\t\twhile (HeapTupleIsValid(taskTuple = systable_getnext(scanDescriptor)))\n\t\t{\n\t\t\ttask = DeformBackgroundTaskHeapTuple(tupleDescriptor, taskTuple);\n\t\t\tif (BackgroundTaskReadyToRun(task) &&\n\t\t\t\tIncrementParallelTaskCountForNodesInvolved(task))\n\t\t\t{\n\t\t\t\t/* found task, close table and return */\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttask = NULL;\n\t\t}\n\n\t\tsystable_endscan(scanDescriptor);\n\t}\n\n\ttable_close(pgDistBackgroundTasks, NoLock);\n\n\treturn task;\n}\n\n\n/*\n * GetBackgroundJobByJobId loads a BackgroundJob from the catalog into memory. Return's a\n * null pointer if no job exist with the given JobId.\n */\nBackgroundJob *\nGetBackgroundJobByJobId(int64 jobId)\n{\n\tScanKeyData scanKey[1] = { 0 };\n\tbool indexOK = true;\n\n\tRelation pgDistBackgroundJobs =\n\t\ttable_open(DistBackgroundJobRelationId(), AccessShareLock);\n\n\t/* pg_dist_background_task.job_id == $jobId */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_job_job_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(jobId));\n\n\tSysScanDesc scanDescriptor =\n\t\tsystable_beginscan(pgDistBackgroundJobs, DistBackgroundJobPKeyIndexId(),\n\t\t\t\t\t\t   indexOK, NULL, lengthof(scanKey), scanKey);\n\n\tHeapTuple taskTuple = systable_getnext(scanDescriptor);\n\tBackgroundJob *job = NULL;\n\tif (HeapTupleIsValid(taskTuple))\n\t{\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundJobs);\n\t\tjob = DeformBackgroundJobHeapTuple(tupleDescriptor, taskTuple);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistBackgroundJobs, AccessShareLock);\n\n\treturn job;\n}\n\n\n/*\n * GetBackgroundTaskByTaskId loads a BackgroundTask from the catalog into memory. Return's\n * a null pointer if no job exist with the given JobId and TaskId.\n */\nBackgroundTask *\nGetBackgroundTaskByTaskId(int64 taskId)\n{\n\tScanKeyData scanKey[1] = { 0 };\n\tbool indexOK = true;\n\n\tRelation pgDistBackgroundTasks =\n\t\ttable_open(DistBackgroundTaskRelationId(), AccessShareLock);\n\n\t/* pg_dist_background_task.task_id == $taskId */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_task_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(taskId));\n\n\tSysScanDesc scanDescriptor =\n\t\tsystable_beginscan(pgDistBackgroundTasks,\n\t\t\t\t\t\t   DistBackgroundTaskPKeyIndexId(),\n\t\t\t\t\t\t   indexOK, NULL, lengthof(scanKey), scanKey);\n\n\tHeapTuple taskTuple = systable_getnext(scanDescriptor);\n\tBackgroundTask *task = NULL;\n\tif (HeapTupleIsValid(taskTuple))\n\t{\n\t\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTasks);\n\t\ttask = DeformBackgroundTaskHeapTuple(tupleDescriptor, taskTuple);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistBackgroundTasks, AccessShareLock);\n\n\treturn task;\n}\n\n\ntypedef struct JobTaskStatusCounts\n{\n\tint blocked;\n\tint runnable;\n\tint running;\n\tint done;\n\tint error;\n\tint unscheduled;\n\tint cancelled;\n\tint cancelling;\n} JobTaskStatusCounts;\n\n\n/*\n * JobTasksStatusCount scans all tasks associated with the provided job and count's the\n * number of tasks that are tracked in each state. Effectively grouping and counting the\n * tasks by their state.\n */\nstatic JobTaskStatusCounts\nJobTasksStatusCount(int64 jobId)\n{\n\tRelation pgDistBackgroundTasks =\n\t\ttable_open(DistBackgroundTaskRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTasks);\n\n\tScanKeyData scanKey[1] = { 0 };\n\tconst bool indexOK = true;\n\n\t/* WHERE job_id = $task->jobId */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_job_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(jobId));\n\n\tSysScanDesc scanDescriptor =\n\t\tsystable_beginscan(pgDistBackgroundTasks,\n\t\t\t\t\t\t   DistBackgroundTaskJobIdTaskIdIndexId(),\n\t\t\t\t\t\t   indexOK, NULL, lengthof(scanKey), scanKey);\n\n\tJobTaskStatusCounts counts = { 0 };\n\tHeapTuple heapTuple = NULL;\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tDatum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\t\tbool *isnull = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\n\t\theap_deform_tuple(heapTuple, tupleDescriptor, values, isnull);\n\n\t\tOid statusOid = DatumGetObjectId(values[Anum_pg_dist_background_task_status -\n\t\t\t\t\t\t\t\t\t\t\t\t1]);\n\t\tBackgroundTaskStatus status = BackgroundTaskStatusByOid(statusOid);\n\n\t\tpfree(values);\n\t\tpfree(isnull);\n\n\t\tswitch (status)\n\t\t{\n\t\t\tcase BACKGROUND_TASK_STATUS_BLOCKED:\n\t\t\t{\n\t\t\t\tcounts.blocked++;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BACKGROUND_TASK_STATUS_RUNNABLE:\n\t\t\t{\n\t\t\t\tcounts.runnable++;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BACKGROUND_TASK_STATUS_RUNNING:\n\t\t\t{\n\t\t\t\tcounts.running++;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BACKGROUND_TASK_STATUS_DONE:\n\t\t\t{\n\t\t\t\tcounts.done++;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BACKGROUND_TASK_STATUS_ERROR:\n\t\t\t{\n\t\t\t\tcounts.error++;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BACKGROUND_TASK_STATUS_UNSCHEDULED:\n\t\t\t{\n\t\t\t\tcounts.unscheduled++;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BACKGROUND_TASK_STATUS_CANCELLED:\n\t\t\t{\n\t\t\t\tcounts.cancelled++;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BACKGROUND_TASK_STATUS_CANCELLING:\n\t\t\t{\n\t\t\t\tcounts.cancelling++;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\telog(ERROR, \"unknown state in pg_dist_background_task\");\n\t\t\t}\n\t\t}\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistBackgroundTasks, NoLock);\n\n\treturn counts;\n}\n\n\n/*\n * SetFieldValue populates values, isnull, replace according to the newValue passed,\n * returning if the value has been updated or not. The replace argument can be omitted if\n * we are simply initializing a field.\n *\n * suggested use would be:\n *   bool updated = false;\n *   updated |= SetFieldValue(Anum_...._...., isnull, replace, values, newValue);\n *   updated |= SetFieldText(Anum_...._...., isnull, replace, values, \"hello world\");\n *   updated |= SetFieldNull(Anum_...._...., isnull, replace, values);\n *\n * Only if updated is set in the end the tuple has to be updated in the catalog.\n */\nstatic bool\nSetFieldValue(int attno, Datum values[], bool isnull[], bool replace[], Datum newValue)\n{\n\tint idx = attno - 1;\n\tbool updated = false;\n\n\tif (!isnull[idx] && newValue == values[idx])\n\t{\n\t\treturn updated;\n\t}\n\n\tvalues[idx] = newValue;\n\tisnull[idx] = false;\n\tupdated = true;\n\n\tif (replace)\n\t{\n\t\treplace[idx] = true;\n\t}\n\treturn updated;\n}\n\n\n/*\n * SetFieldText populates values, isnull, replace according to the newValue passed,\n * returning if the value has been updated or not. The replace argument can be omitted if\n * we are simply initializing a field.\n *\n * suggested use would be:\n *   bool updated = false;\n *   updated |= SetFieldValue(Anum_...._...., isnull, replace, values, newValue);\n *   updated |= SetFieldText(Anum_...._...., isnull, replace, values, \"hello world\");\n *   updated |= SetFieldNull(Anum_...._...., isnull, replace, values);\n *\n * Only if updated is set in the end the tuple has to be updated in the catalog.\n */\nstatic bool\nSetFieldText(int attno, Datum values[], bool isnull[], bool replace[],\n\t\t\t const char *newValue)\n{\n\tint idx = attno - 1;\n\tbool updated = false;\n\n\tif (!isnull[idx])\n\t{\n\t\tchar *oldText = TextDatumGetCString(values[idx]);\n\t\tif (strcmp(oldText, newValue) == 0)\n\t\t{\n\t\t\treturn updated;\n\t\t}\n\t}\n\n\tvalues[idx] = CStringGetTextDatum(newValue);\n\tisnull[idx] = false;\n\tupdated = true;\n\n\tif (replace)\n\t{\n\t\treplace[idx] = true;\n\t}\n\treturn updated;\n}\n\n\n/*\n * SetFieldNull populates values, isnull and replace according to a null value,\n * returning if the value has been updated or not. The replace argument can be omitted if\n * we are simply initializing a field.\n *\n * suggested use would be:\n *   bool updated = false;\n *   updated |= SetFieldValue(Anum_...._...., isnull, replace, values, newValue);\n *   updated |= SetFieldText(Anum_...._...., isnull, replace, values, \"hello world\");\n *   updated |= SetFieldNull(Anum_...._...., isnull, replace, values);\n *\n * Only if updated is set in the end the tuple has to be updated in the catalog.\n */\nstatic bool\nSetFieldNull(int attno, Datum values[], bool isnull[], bool replace[])\n{\n\tint idx = attno - 1;\n\tbool updated = false;\n\n\tif (isnull[idx])\n\t{\n\t\treturn updated;\n\t}\n\n\tisnull[idx] = true;\n\tvalues[idx] = InvalidOid;\n\tupdated = true;\n\n\tif (replace)\n\t{\n\t\treplace[idx] = true;\n\t}\n\treturn updated;\n}\n\n\n/*\n * UpdateBackgroundJob updates the job's metadata based on the most recent status of all\n * its associated tasks.\n *\n * Since the state of a job is a function of the state of all associated tasks this\n * function projects the tasks states into the job's state.\n *\n * When Citus makes a change to any of the tasks associated with the job it should call\n * this function to correctly project the task updates onto the jobs metadata.\n */\nvoid\nUpdateBackgroundJob(int64 jobId)\n{\n\tJobTaskStatusCounts counts = JobTasksStatusCount(jobId);\n\tBackgroundJobStatus status = BACKGROUND_JOB_STATUS_RUNNING;\n\n\tif (counts.cancelling > 0)\n\t{\n\t\tstatus = BACKGROUND_JOB_STATUS_CANCELLING;\n\t}\n\telse if (counts.cancelled > 0)\n\t{\n\t\tstatus = BACKGROUND_JOB_STATUS_CANCELLED;\n\t}\n\telse if (counts.blocked + counts.runnable + counts.running + counts.error +\n\t\t\t counts.unscheduled == 0)\n\t{\n\t\t/* all tasks are done, job is finished */\n\t\tstatus = BACKGROUND_JOB_STATUS_FINISHED;\n\t}\n\telse if (counts.error + counts.unscheduled > 0)\n\t{\n\t\t/* we are either failing, or failed */\n\t\tif (counts.blocked + counts.runnable + counts.running > 0)\n\t\t{\n\t\t\t/* failing, as there are still tasks to be run */\n\t\t\tstatus = BACKGROUND_JOB_STATUS_FAILING;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstatus = BACKGROUND_JOB_STATUS_FAILED;\n\t\t}\n\t}\n\telse if (counts.blocked + counts.runnable + counts.running > 0)\n\t{\n\t\tstatus = BACKGROUND_JOB_STATUS_RUNNING;\n\t}\n\telse\n\t{\n\t\treturn;\n\t}\n\n\tRelation pgDistBackgroundJobs =\n\t\ttable_open(DistBackgroundJobRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundJobs);\n\n\tScanKeyData scanKey[1] = { 0 };\n\tconst bool indexOK = true;\n\n\t/* WHERE job_id = $task->jobId */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_job_job_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(jobId));\n\n\tSysScanDesc scanDescriptor =\n\t\tsystable_beginscan(pgDistBackgroundJobs,\n\t\t\t\t\t\t   DistBackgroundJobPKeyIndexId(),\n\t\t\t\t\t\t   indexOK, NULL, lengthof(scanKey), scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find background jobs entry for job_id: \"\n\t\t\t\t\t\t\t   UINT64_FORMAT, jobId)));\n\t}\n\n\tDatum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isnull = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\theap_deform_tuple(heapTuple, tupleDescriptor, values, isnull);\n\n\tbool updated = false;\n\n\tOid stateOid = BackgroundJobStatusOid(status);\n\tupdated |= SetFieldValue(Anum_pg_dist_background_job_state, values, isnull, replace,\n\t\t\t\t\t\t\t ObjectIdGetDatum(stateOid));\n\n\tif (status == BACKGROUND_JOB_STATUS_RUNNING)\n\t{\n\t\tif (isnull[Anum_pg_dist_background_job_started_at - 1])\n\t\t{\n\t\t\t/* first time status has been updated and was running, updating started_at */\n\t\t\tTimestampTz startedAt = GetCurrentTimestamp();\n\t\t\tupdated |= SetFieldValue(Anum_pg_dist_background_job_started_at, values,\n\t\t\t\t\t\t\t\t\t isnull, replace, TimestampTzGetDatum(startedAt));\n\t\t}\n\t}\n\n\tif (IsBackgroundJobStatusTerminal(status))\n\t{\n\t\tif (isnull[Anum_pg_dist_background_job_finished_at - 1])\n\t\t{\n\t\t\t/* didn't have a finished at time just yet, updating to now */\n\t\t\tTimestampTz finishedAt = GetCurrentTimestamp();\n\t\t\tupdated |= SetFieldValue(Anum_pg_dist_background_job_finished_at, values,\n\t\t\t\t\t\t\t\t\t isnull, replace, TimestampTzGetDatum(finishedAt));\n\t\t}\n\t}\n\n\tif (updated)\n\t{\n\t\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull,\n\t\t\t\t\t\t\t\t\t  replace);\n\n\t\tCatalogTupleUpdate(pgDistBackgroundJobs, &heapTuple->t_self, heapTuple);\n\n\t\tCommandCounterIncrement();\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistBackgroundJobs, NoLock);\n\n\tpfree(values);\n\tpfree(isnull);\n\tpfree(replace);\n}\n\n\n/*\n * UpdateBackgroundTask updates the catalog entry for the passed task, preventing an\n * actual update when the inmemory representation is the same as the one stored in the\n * catalog.\n */\nvoid\nUpdateBackgroundTask(BackgroundTask *task)\n{\n\tRelation pgDistBackgroundTasks =\n\t\ttable_open(DistBackgroundTaskRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTasks);\n\n\tScanKeyData scanKey[1] = { 0 };\n\tconst bool indexOK = true;\n\n\t/* WHERE task_id = $task->taskid */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_task_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(task->taskid));\n\n\tSysScanDesc scanDescriptor =\n\t\tsystable_beginscan(pgDistBackgroundTasks,\n\t\t\t\t\t\t   DistBackgroundTaskPKeyIndexId(),\n\t\t\t\t\t\t   indexOK, NULL, lengthof(scanKey), scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find background task entry for :\"\n\t\t\t\t\t\t\t   \"job_id/task_id: \" UINT64_FORMAT \"/\" UINT64_FORMAT,\n\t\t\t\t\t\t\t   task->jobid, task->taskid)));\n\t}\n\n\tDatum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isnull = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\theap_deform_tuple(heapTuple, tupleDescriptor, values, isnull);\n\n\tbool updated = false;\n\n\tupdated |= SetFieldValue(Anum_pg_dist_background_task_owner, values, isnull, replace,\n\t\t\t\t\t\t\t task->owner);\n\n\tif (task->pid)\n\t{\n\t\tupdated |= SetFieldValue(Anum_pg_dist_background_task_pid, values, isnull,\n\t\t\t\t\t\t\t\t replace, Int32GetDatum(*task->pid));\n\t}\n\telse\n\t{\n\t\tupdated |= SetFieldNull(Anum_pg_dist_background_task_pid, values, isnull,\n\t\t\t\t\t\t\t\treplace);\n\t}\n\n\tOid statusOid = ObjectIdGetDatum(BackgroundTaskStatusOid(task->status));\n\tupdated |= SetFieldValue(Anum_pg_dist_background_task_status, values, isnull, replace,\n\t\t\t\t\t\t\t statusOid);\n\n\tif (task->retry_count)\n\t{\n\t\tupdated |= SetFieldValue(Anum_pg_dist_background_task_retry_count, values, isnull,\n\t\t\t\t\t\t\t\t replace, Int32GetDatum(*task->retry_count));\n\t}\n\telse\n\t{\n\t\tupdated |= SetFieldNull(Anum_pg_dist_background_task_retry_count, values, isnull,\n\t\t\t\t\t\t\t\treplace);\n\t}\n\n\tif (task->not_before)\n\t{\n\t\tupdated |= SetFieldValue(Anum_pg_dist_background_task_not_before, values, isnull,\n\t\t\t\t\t\t\t\t replace, TimestampTzGetDatum(*task->not_before));\n\t}\n\telse\n\t{\n\t\tupdated |= SetFieldNull(Anum_pg_dist_background_task_not_before, values, isnull,\n\t\t\t\t\t\t\t\treplace);\n\t}\n\n\tif (task->message)\n\t{\n\t\tupdated |= SetFieldText(Anum_pg_dist_background_task_message, values, isnull,\n\t\t\t\t\t\t\t\treplace, task->message);\n\t}\n\telse\n\t{\n\t\tupdated |= SetFieldNull(Anum_pg_dist_background_task_message, values, isnull,\n\t\t\t\t\t\t\t\treplace);\n\t}\n\n\tif (updated)\n\t{\n\t\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull,\n\t\t\t\t\t\t\t\t\t  replace);\n\n\t\tCatalogTupleUpdate(pgDistBackgroundTasks, &heapTuple->t_self, heapTuple);\n\n\t\tCommandCounterIncrement();\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistBackgroundTasks, NoLock);\n\n\tpfree(values);\n\tpfree(isnull);\n\tpfree(replace);\n}\n\n\n/*\n * GetDependantTasks returns a list of taskId's containing all tasks depending on the task\n * passed via its arguments.\n *\n * Becasue tasks are int64 we allocate and return a List of int64 pointers.\n */\nstatic List *\nGetDependantTasks(int64 jobId, int64 taskId)\n{\n\tRelation pgDistBackgroundTasksDepends =\n\t\ttable_open(DistBackgroundTaskDependRelationId(), RowExclusiveLock);\n\n\tScanKeyData scanKey[2] = { 0 };\n\tconst bool indexOK = true;\n\n\t/* pg_dist_background_task_depend.job_id = $jobId */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_depend_job_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(jobId));\n\n\t/* pg_dist_background_task_depend.depends_on = $taskId */\n\tScanKeyInit(&scanKey[1], Anum_pg_dist_background_task_depend_depends_on,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(taskId));\n\n\tSysScanDesc scanDescriptor =\n\t\tsystable_beginscan(pgDistBackgroundTasksDepends,\n\t\t\t\t\t\t   DistBackgroundTaskDependDependsOnIndexId(),\n\t\t\t\t\t\t   indexOK,\n\t\t\t\t\t\t   NULL, lengthof(scanKey), scanKey);\n\n\tList *dependantTasks = NIL;\n\tHeapTuple heapTuple = NULL;\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tForm_pg_dist_background_task_depend depend =\n\t\t\t(Form_pg_dist_background_task_depend) GETSTRUCT(heapTuple);\n\n\t\tint64 *dTaskId = palloc0(sizeof(int64));\n\t\t*dTaskId = depend->task_id;\n\n\t\tdependantTasks = lappend(dependantTasks, dTaskId);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistBackgroundTasksDepends, NoLock);\n\n\treturn dependantTasks;\n}\n\n\n/*\n * CancelTasksForJob cancels all tasks associated with a job that are not currently\n * running and are not already in their terminal state. Canceling these tasks consist of\n * updating the status of the task in the catalog.\n *\n * For all other tasks, namely the ones that are currently running, it returns the list of\n * Pid's of the tasks running. These backends should be signalled for cancellation.\n *\n * Since we are either signalling or changing the status of a task we perform appropriate\n * permission checks. This currently includes the exact same checks pg_cancel_backend\n * would perform.\n */\nList *\nCancelTasksForJob(int64 jobid)\n{\n\tRelation pgDistBackgroundTasks =\n\t\ttable_open(DistBackgroundTaskRelationId(), ExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTasks);\n\n\tScanKeyData scanKey[1] = { 0 };\n\n\t/* WHERE jobId = $jobid */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_job_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(jobid));\n\n\tconst bool indexOK = true;\n\tSysScanDesc scanDescriptor = systable_beginscan(\n\t\tpgDistBackgroundTasks, DistBackgroundTaskJobIdTaskIdIndexId(),\n\t\tindexOK, NULL, lengthof(scanKey), scanKey);\n\n\tList *runningTaskPids = NIL;\n\tHeapTuple taskTuple = NULL;\n\twhile (HeapTupleIsValid(taskTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tDatum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\t\tbool *nulls = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\t\tbool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\t\theap_deform_tuple(taskTuple, tupleDescriptor, values, nulls);\n\n\t\tOid statusOid =\n\t\t\tDatumGetObjectId(values[Anum_pg_dist_background_task_status - 1]);\n\t\tBackgroundTaskStatus status = BackgroundTaskStatusByOid(statusOid);\n\n\t\tif (IsBackgroundTaskStatusTerminal(status))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* make sure the current user has the rights to cancel this task */\n\t\tOid taskOwner = DatumGetObjectId(values[Anum_pg_dist_background_task_owner - 1]);\n\t\tif (superuser_arg(taskOwner) && !superuser())\n\t\t{\n\t\t\t/* must be a superuser to cancel tasks owned by superuser */\n\t\t\tereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),\n\t\t\t\t\t\t\terrmsg(\"must be a superuser to cancel superuser tasks\")));\n\t\t}\n\t\telse if (!has_privs_of_role(GetUserId(), taskOwner) &&\n\t\t\t\t !has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_BACKEND))\n\t\t{\n\t\t\t/* user doesn't have the permissions to cancel this job */\n\t\t\tereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),\n\t\t\t\t\t\t\terrmsg(\"must be a member of the role whose task is being \"\n\t\t\t\t\t\t\t\t   \"canceled or member of pg_signal_backend\")));\n\t\t}\n\n\t\tBackgroundTaskStatus newStatus = BACKGROUND_TASK_STATUS_CANCELLED;\n\t\tif (status == BACKGROUND_TASK_STATUS_RUNNING)\n\t\t{\n\t\t\tif (!nulls[Anum_pg_dist_background_task_pid - 1])\n\t\t\t{\n\t\t\t\tint32 pid = DatumGetInt32(values[Anum_pg_dist_background_task_pid - 1]);\n\t\t\t\trunningTaskPids = lappend_int(runningTaskPids, pid);\n\t\t\t\tnewStatus = BACKGROUND_TASK_STATUS_CANCELLING;\n\t\t\t}\n\t\t}\n\n\t\t/* update task to new status */\n\t\tnulls[Anum_pg_dist_background_task_status - 1] = false;\n\t\tvalues[Anum_pg_dist_background_task_status - 1] = ObjectIdGetDatum(\n\t\t\tBackgroundTaskStatusOid(newStatus));\n\t\treplace[Anum_pg_dist_background_task_status - 1] = true;\n\n\t\ttaskTuple = heap_modify_tuple(taskTuple, tupleDescriptor, values, nulls,\n\t\t\t\t\t\t\t\t\t  replace);\n\t\tCatalogTupleUpdate(pgDistBackgroundTasks, &taskTuple->t_self, taskTuple);\n\n\t\tpfree(values);\n\t\tpfree(nulls);\n\t\tpfree(replace);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistBackgroundTasks, NoLock);\n\n\tCommandCounterIncrement();\n\n\treturn runningTaskPids;\n}\n\n\n/*\n * UnscheduleDependentTasks follows the dependency tree of the provided task recursively\n * to unschedule any task depending on the current task.\n *\n * This is useful to unschedule any task that can never run because it will never satisfy\n * the unmet dependency constraint.\n */\nvoid\nUnscheduleDependentTasks(BackgroundTask *task)\n{\n\tRelation pgDistBackgroundTasks =\n\t\ttable_open(DistBackgroundTaskRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistBackgroundTasks);\n\n\tList *dependantTasks = GetDependantTasks(task->jobid, task->taskid);\n\twhile (list_length(dependantTasks) > 0)\n\t{\n\t\t/* pop last item from stack */\n\t\tint64 cTaskId = *(int64 *) llast(dependantTasks);\n\t\tdependantTasks = list_delete_last(dependantTasks);\n\n\t\t/* push new dependant tasks on to stack */\n\t\tdependantTasks = list_concat(dependantTasks,\n\t\t\t\t\t\t\t\t\t GetDependantTasks(task->jobid, cTaskId));\n\n\t\t/* unschedule current task */\n\t\t{\n\t\t\tScanKeyData scanKey[1] = { 0 };\n\n\t\t\t/* WHERE taskId = dependentTask->taskId */\n\t\t\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_task_id,\n\t\t\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(cTaskId));\n\t\t\tconst bool indexOK = true;\n\t\t\tSysScanDesc scanDescriptor = systable_beginscan(pgDistBackgroundTasks,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tDistBackgroundTaskPKeyIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlengthof(scanKey), scanKey);\n\n\t\t\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\t\t\tif (!HeapTupleIsValid(heapTuple))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"could not find background task entry for \"\n\t\t\t\t\t\t\t\t\t   \"task_id: \" UINT64_FORMAT, cTaskId)));\n\t\t\t}\n\n\t\t\tDatum *values = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\t\t\tbool *isnull = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\t\t\tbool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\t\t\tvalues[Anum_pg_dist_background_task_status - 1] =\n\t\t\t\tObjectIdGetDatum(CitusTaskStatusUnscheduledId());\n\t\t\tisnull[Anum_pg_dist_background_task_status - 1] = false;\n\t\t\treplace[Anum_pg_dist_background_task_status - 1] = true;\n\n\t\t\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull,\n\t\t\t\t\t\t\t\t\t\t  replace);\n\t\t\tCatalogTupleUpdate(pgDistBackgroundTasks, &heapTuple->t_self, heapTuple);\n\n\t\t\tsystable_endscan(scanDescriptor);\n\n\t\t\tpfree(values);\n\t\t\tpfree(isnull);\n\t\t\tpfree(replace);\n\t\t}\n\t}\n\n\tCommandCounterIncrement();\n\n\ttable_close(pgDistBackgroundTasks, NoLock);\n}\n\n\n/*\n * UnblockDependingBackgroundTasks unblocks any depending task that now satisfies the\n * constraaint that it doesn't have unmet dependencies anymore. For this to be done we\n * will find all tasks depending on the current task. Per found task we check if it has\n * any unmet dependencies. If no tasks are found that would block the execution of this\n * task we transition the task to Runnable state.\n */\nvoid\nUnblockDependingBackgroundTasks(BackgroundTask *task)\n{\n\tRelation pgDistBackgroundTasksDepend =\n\t\ttable_open(DistBackgroundTaskDependRelationId(), RowExclusiveLock);\n\n\tScanKeyData scanKey[2] = { 0 };\n\n\t/* WHERE jobId = $jobId */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_background_task_depend_job_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(task->jobid));\n\n\t/* WHERE depends_on = $taskId */\n\tScanKeyInit(&scanKey[1], Anum_pg_dist_background_task_depend_depends_on,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(task->taskid));\n\tconst bool indexOK = true;\n\tSysScanDesc scanDescriptor = systable_beginscan(\n\t\tpgDistBackgroundTasksDepend, DistBackgroundTaskDependDependsOnIndexId(), indexOK,\n\t\tNULL, lengthof(scanKey), scanKey);\n\n\tHeapTuple heapTuple = NULL;\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tForm_pg_dist_background_task_depend depend =\n\t\t\t(Form_pg_dist_background_task_depend) GETSTRUCT(heapTuple);\n\n\t\tif (!BackgroundTaskHasUmnetDependencies(task->jobid, depend->task_id))\n\t\t{\n\t\t\t/*\n\t\t\t * The task does not have any unmet dependencies anymore and should become\n\t\t\t * runnable\n\t\t\t */\n\n\t\t\tBackgroundTask *unblockedTask = GetBackgroundTaskByTaskId(depend->task_id);\n\t\t\tif (unblockedTask->status == BACKGROUND_TASK_STATUS_CANCELLED)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tAssert(unblockedTask->status == BACKGROUND_TASK_STATUS_BLOCKED);\n\t\t\tunblockedTask->status = BACKGROUND_TASK_STATUS_RUNNABLE;\n\t\t\tUpdateBackgroundTask(unblockedTask);\n\t\t}\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\n\ttable_close(pgDistBackgroundTasksDepend, NoLock);\n}\n\n\n/*\n * GetAutoConvertedAttrIndexInPgDistPartition returns attrnum for autoconverted attr.\n *\n * autoconverted attr was added to table pg_dist_partition using alter operation after\n * the version where Citus started supporting downgrades, and it's only column that we've\n * introduced to pg_dist_partition since then.\n *\n * And in case of a downgrade + upgrade, tupleDesc->natts becomes greater than\n * Natts_pg_dist_partition and when this happens, then we know that attrnum autoconverted is\n * not Anum_pg_dist_partition_autoconverted anymore but tupleDesc->natts - 1.\n */\nint\nGetAutoConvertedAttrIndexInPgDistPartition(TupleDesc tupleDesc)\n{\n\treturn tupleDesc->natts == Natts_pg_dist_partition\n\t\t   ? (Anum_pg_dist_partition_autoconverted - 1)\n\t\t   : tupleDesc->natts - 1;\n}\n\n\n/*\n * GetNodesInvolvedAttrIndexInPgDistBackgroundTask returns attrnum for nodes_involved attr.\n *\n * nodes_involved attr was added to table pg_dist_background_task using alter operation after\n * the version where Citus started supporting downgrades, and it's only column that we've\n * introduced to pg_dist_background_task since then.\n *\n * And in case of a downgrade + upgrade, tupleDesc->natts becomes greater than\n * Natts_pg_dist_background_task and when this happens, then we know that attrnum nodes_involved is\n * not Anum_pg_dist_background_task_nodes_involved anymore but tupleDesc->natts - 1.\n */\nint\nGetNodesInvolvedAttrIndexInPgDistBackgroundTask(TupleDesc tupleDesc)\n{\n\treturn tupleDesc->natts == Natts_pg_dist_background_task\n\t\t   ? (Anum_pg_dist_background_task_nodes_involved - 1)\n\t\t   : tupleDesc->natts - 1;\n}\n"
  },
  {
    "path": "src/backend/distributed/metadata/node_metadata.c",
    "content": "/*\n * node_metadata.c\n *\t  Functions that operate on pg_dist_node\n *\n * Copyright (c) Citus Data, Inc.\n */\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup.h\"\n#include \"access/htup_details.h\"\n#include \"access/skey.h\"\n#include \"access/tupmacs.h\"\n#include \"access/xact.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/namespace.h\"\n#include \"commands/sequence.h\"\n#include \"executor/spi.h\"\n#include \"lib/stringinfo.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/bufmgr.h\"\n#include \"storage/fd.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/lock.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/plancache.h\"\n#include \"utils/rel.h\"\n#include \"utils/relcache.h\"\n\n#include \"distributed/citus_acquire_lock.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/clonenode_utils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/maintenanced.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata/pg_dist_object.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/pg_dist_node.h\"\n#include \"distributed/pg_dist_node_metadata.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/shared_connection_stats.h\"\n#include \"distributed/string_utils.h\"\n#include \"distributed/transaction_recovery.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_transaction.h\"\n\n#define INVALID_GROUP_ID -1\n\n/* default group size */\nint GroupSize = 1;\n\n/* config variable managed via guc.c */\nchar *CurrentCluster = \"default\";\n\n/* did current transaction modify pg_dist_node? */\nbool TransactionModifiedNodeMetadata = false;\n\nbool EnableMetadataSync = true;\n\ntypedef struct NodeMetadata\n{\n\tint32 groupId;\n\tchar *nodeRack;\n\tbool hasMetadata;\n\tbool metadataSynced;\n\tbool isActive;\n\tOid nodeRole;\n\tbool shouldHaveShards;\n\tuint32 nodeprimarynodeid;\n\tbool nodeisclone;\n\tchar *nodeCluster;\n} NodeMetadata;\n\n/* local function forward declarations */\nstatic void RemoveNodeFromCluster(char *nodeName, int32 nodePort);\nstatic void ErrorIfNodeContainsNonRemovablePlacements(WorkerNode *workerNode);\nstatic bool PlacementHasActivePlacementOnAnotherGroup(GroupShardPlacement\n\t\t\t\t\t\t\t\t\t\t\t\t\t  *sourcePlacement);\nstatic int AddNodeMetadata(char *nodeName, int32 nodePort, NodeMetadata *nodeMetadata,\n\t\t\t\t\t\t   bool *nodeAlreadyExists, bool localOnly);\nstatic int AddNodeMetadataViaMetadataContext(char *nodeName, int32 nodePort,\n\t\t\t\t\t\t\t\t\t\t\t NodeMetadata *nodeMetadata,\n\t\t\t\t\t\t\t\t\t\t\t bool *nodeAlreadyExists);\nstatic HeapTuple GetNodeTuple(const char *nodeName, int32 nodePort);\nstatic HeapTuple GetNodeByNodeId(int32 nodeId);\nstatic int32 GetNextGroupId(void);\nstatic int GetNextNodeId(void);\nstatic void InsertPlaceholderCoordinatorRecord(void);\nstatic void InsertNodeRow(int nodeid, char *nodename, int32 nodeport,\n\t\t\t\t\t\t  NodeMetadata *nodeMetadata);\nstatic void DeleteNodeRow(char *nodename, int32 nodeport);\nstatic void BlockDistributedQueriesOnMetadataNodes(void);\nstatic WorkerNode * TupleToWorkerNode(Relation pgDistNode, TupleDesc tupleDescriptor,\n\t\t\t\t\t\t\t\t\t  HeapTuple heapTuple);\nstatic bool NodeIsLocal(WorkerNode *worker);\nstatic void SetLockTimeoutLocally(int32 lock_cooldown);\nstatic void UpdateNodeLocation(int32 nodeId, char *newNodeName, int32 newNodePort,\n\t\t\t\t\t\t\t   bool localOnly);\nstatic int GetNodePrimaryNodeIdAttrIndexInPgDistNode(TupleDesc tupleDesc);\nstatic int GetNodeIsCloneAttrIndexInPgDistNode(TupleDesc tupleDesc);\nstatic bool UnsetMetadataSyncedForAllWorkers(void);\nstatic char * GetMetadataSyncCommandToSetNodeColumn(WorkerNode *workerNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\tint columnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDatum value);\nstatic char * NodeHasmetadataUpdateCommand(uint32 nodeId, bool hasMetadata);\nstatic char * NodeMetadataSyncedUpdateCommand(uint32 nodeId, bool metadataSynced);\nstatic void ErrorIfCoordinatorMetadataSetFalse(WorkerNode *workerNode, Datum value,\n\t\t\t\t\t\t\t\t\t\t\t   char *field);\nstatic WorkerNode * SetShouldHaveShards(WorkerNode *workerNode, bool shouldHaveShards);\nstatic void ErrorIfAnyNodeNotExist(List *nodeList);\nstatic void UpdateLocalGroupIdsViaMetadataContext(MetadataSyncContext *context);\nstatic void SendDeletionCommandsForReplicatedTablePlacements(MetadataSyncContext *context)\n;\nstatic void SyncNodeMetadata(MetadataSyncContext *context);\nstatic void SetNodeStateViaMetadataContext(MetadataSyncContext *context,\n\t\t\t\t\t\t\t\t\t\t   WorkerNode *workerNode,\n\t\t\t\t\t\t\t\t\t\t   Datum value);\nstatic void MarkNodesNotSyncedInLoopBackConnection(MetadataSyncContext *context,\n\t\t\t\t\t\t\t\t\t\t\t\t   pid_t parentSessionPid);\nstatic void EnsureParentSessionHasExclusiveLockOnPgDistNode(pid_t parentSessionPid);\nstatic void SetNodeMetadata(MetadataSyncContext *context, bool localOnly);\nstatic void EnsureTransactionalMetadataSyncMode(void);\nstatic BackgroundWorkerHandle * CheckBackgroundWorkerToObtainLocks(int32 lock_cooldown);\nstatic BackgroundWorkerHandle * LockPlacementsWithBackgroundWorkersInPrimaryNode(\n\tWorkerNode *workerNode, bool force, int32 lock_cooldown);\n\n\nstatic int32 CitusAddCloneNode(WorkerNode *primaryWorkerNode,\n\t\t\t\t\t\t\t   char *cloneHostname, int32 clonePort);\nstatic void RemoveCloneNode(WorkerNode *cloneNode);\n\n/* Function definitions go here */\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(citus_set_coordinator_host);\nPG_FUNCTION_INFO_V1(citus_add_node);\nPG_FUNCTION_INFO_V1(master_add_node);\nPG_FUNCTION_INFO_V1(citus_add_inactive_node);\nPG_FUNCTION_INFO_V1(master_add_inactive_node);\nPG_FUNCTION_INFO_V1(citus_add_secondary_node);\nPG_FUNCTION_INFO_V1(master_add_secondary_node);\nPG_FUNCTION_INFO_V1(citus_set_node_property);\nPG_FUNCTION_INFO_V1(master_set_node_property);\nPG_FUNCTION_INFO_V1(citus_remove_node);\nPG_FUNCTION_INFO_V1(master_remove_node);\nPG_FUNCTION_INFO_V1(citus_disable_node);\nPG_FUNCTION_INFO_V1(master_disable_node);\nPG_FUNCTION_INFO_V1(citus_activate_node);\nPG_FUNCTION_INFO_V1(master_activate_node);\nPG_FUNCTION_INFO_V1(citus_update_node);\nPG_FUNCTION_INFO_V1(citus_pause_node_within_txn);\nPG_FUNCTION_INFO_V1(master_update_node);\nPG_FUNCTION_INFO_V1(get_shard_id_for_distribution_column);\nPG_FUNCTION_INFO_V1(citus_nodename_for_nodeid);\nPG_FUNCTION_INFO_V1(citus_nodeport_for_nodeid);\nPG_FUNCTION_INFO_V1(citus_coordinator_nodeid);\nPG_FUNCTION_INFO_V1(citus_is_coordinator);\nPG_FUNCTION_INFO_V1(citus_internal_mark_node_not_synced);\nPG_FUNCTION_INFO_V1(citus_is_primary_node);\nPG_FUNCTION_INFO_V1(citus_add_clone_node);\nPG_FUNCTION_INFO_V1(citus_add_clone_node_with_nodeid);\nPG_FUNCTION_INFO_V1(citus_remove_clone_node);\nPG_FUNCTION_INFO_V1(citus_remove_clone_node_with_nodeid);\n\n/*\n * DefaultNodeMetadata creates a NodeMetadata struct with the fields set to\n * sane defaults, e.g. nodeRack = WORKER_DEFAULT_RACK.\n */\nstatic NodeMetadata\nDefaultNodeMetadata()\n{\n\tNodeMetadata nodeMetadata;\n\n\t/* ensure uninitialized padding doesn't escape the function */\n\tmemset_struct_0(nodeMetadata);\n\tnodeMetadata.nodeRack = WORKER_DEFAULT_RACK;\n\tnodeMetadata.shouldHaveShards = true;\n\tnodeMetadata.groupId = INVALID_GROUP_ID;\n\tnodeMetadata.nodeisclone = false;\n\tnodeMetadata.nodeprimarynodeid = 0; /* 0 typically means InvalidNodeId */\n\n\treturn nodeMetadata;\n}\n\n\n/*\n * citus_set_coordinator_host configures the hostname and port through which worker\n * nodes can connect to the coordinator.\n */\nDatum\ncitus_set_coordinator_host(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *nodeName = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\tchar *nodeNameString = text_to_cstring(nodeName);\n\n\tNodeMetadata nodeMetadata = DefaultNodeMetadata();\n\tnodeMetadata.groupId = 0;\n\tnodeMetadata.shouldHaveShards = false;\n\tnodeMetadata.nodeRole = PG_GETARG_OID(2);\n\n\tName nodeClusterName = PG_GETARG_NAME(3);\n\tnodeMetadata.nodeCluster = NameStr(*nodeClusterName);\n\n\t/*\n\t * We do not allow metadata operations on secondary nodes in nontransactional\n\t * sync mode.\n\t */\n\tif (nodeMetadata.nodeRole == SecondaryNodeRoleId())\n\t{\n\t\tEnsureTransactionalMetadataSyncMode();\n\t}\n\n\t/* prevent concurrent modification */\n\tLockRelationOid(DistNodeRelationId(), RowExclusiveLock);\n\n\tbool isCoordinatorInMetadata = false;\n\tWorkerNode *coordinatorNode = PrimaryNodeForGroup(COORDINATOR_GROUP_ID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  &isCoordinatorInMetadata);\n\tif (!isCoordinatorInMetadata)\n\t{\n\t\tbool nodeAlreadyExists = false;\n\t\tbool localOnly = false;\n\n\t\t/* add the coordinator to pg_dist_node if it was not already added */\n\t\tAddNodeMetadata(nodeNameString, nodePort, &nodeMetadata,\n\t\t\t\t\t\t&nodeAlreadyExists, localOnly);\n\n\t\t/* we just checked */\n\t\tAssert(!nodeAlreadyExists);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * since AddNodeMetadata takes an exclusive lock on pg_dist_node, we\n\t\t * do not need to worry about concurrent changes (e.g. deletion) and\n\t\t * can proceed to update immediately.\n\t\t */\n\t\tbool localOnly = false;\n\t\tUpdateNodeLocation(coordinatorNode->nodeId, nodeNameString, nodePort, localOnly);\n\n\t\t/* clear cached plans that have the old host/port */\n\t\tResetPlanCache();\n\t}\n\n\tTransactionModifiedNodeMetadata = true;\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * EnsureTransactionalMetadataSyncMode ensures metadata sync mode is transactional.\n */\nstatic void\nEnsureTransactionalMetadataSyncMode(void)\n{\n\tif (MetadataSyncTransMode == METADATA_SYNC_NON_TRANSACTIONAL)\n\t{\n\t\tereport(ERROR, (errmsg(\"this operation cannot be completed in nontransactional \"\n\t\t\t\t\t\t\t   \"metadata sync mode\"),\n\t\t\t\t\t\terrhint(\"SET citus.metadata_sync_mode to 'transactional'\")));\n\t}\n}\n\n\n/*\n * citus_add_node function adds a new node to the cluster and returns its id. It also\n * replicates all reference tables to the new node.\n */\nDatum\ncitus_add_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tEnsureSuperUser();\n\tEnsureCoordinator();\n\n\ttext *nodeName = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\tchar *nodeNameString = text_to_cstring(nodeName);\n\n\tNodeMetadata nodeMetadata = DefaultNodeMetadata();\n\tbool nodeAlreadyExists = false;\n\tnodeMetadata.groupId = PG_GETARG_INT32(2);\n\n\t/*\n\t * During tests this function is called before nodeRole and nodeCluster have been\n\t * created.\n\t */\n\tif (PG_NARGS() == 3)\n\t{\n\t\tnodeMetadata.nodeRole = InvalidOid;\n\t\tnodeMetadata.nodeCluster = \"default\";\n\t}\n\telse\n\t{\n\t\tName nodeClusterName = PG_GETARG_NAME(4);\n\t\tnodeMetadata.nodeCluster = NameStr(*nodeClusterName);\n\n\t\tnodeMetadata.nodeRole = PG_GETARG_OID(3);\n\t}\n\n\tif (nodeMetadata.groupId == COORDINATOR_GROUP_ID)\n\t{\n\t\t/* by default, we add the coordinator without shards */\n\t\tnodeMetadata.shouldHaveShards = false;\n\t}\n\n\t/*\n\t * We do not allow metadata operations on secondary nodes in nontransactional\n\t * sync mode.\n\t */\n\tif (nodeMetadata.nodeRole == SecondaryNodeRoleId())\n\t{\n\t\tEnsureTransactionalMetadataSyncMode();\n\t}\n\n\tif (MetadataSyncTransMode == METADATA_SYNC_NON_TRANSACTIONAL &&\n\t\tIsMultiStatementTransaction())\n\t{\n\t\t/*\n\t\t * prevent inside transaction block as we use bare connections which can\n\t\t * lead deadlock\n\t\t */\n\t\tereport(ERROR, (errmsg(\"do not add node in transaction block \"\n\t\t\t\t\t\t\t   \"when the sync mode is nontransactional\"),\n\t\t\t\t\t\terrhint(\"add the node after SET citus.metadata_sync_mode \"\n\t\t\t\t\t\t\t\t\"TO 'transactional'\")));\n\t}\n\n\tint nodeId = AddNodeMetadataViaMetadataContext(nodeNameString, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t   &nodeMetadata,\n\t\t\t\t\t\t\t\t\t\t\t\t   &nodeAlreadyExists);\n\tTransactionModifiedNodeMetadata = true;\n\n\tPG_RETURN_INT32(nodeId);\n}\n\n\n/*\n * master_add_node is a wrapper function for old UDF name.\n */\nDatum\nmaster_add_node(PG_FUNCTION_ARGS)\n{\n\treturn citus_add_node(fcinfo);\n}\n\n\n/*\n * citus_add_inactive_node function adds a new node to the cluster as inactive node\n * and returns id of the newly added node. It does not replicate reference\n * tables to the new node, it only adds new node to the pg_dist_node table.\n */\nDatum\ncitus_add_inactive_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *nodeName = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\tchar *nodeNameString = text_to_cstring(nodeName);\n\tName nodeClusterName = PG_GETARG_NAME(4);\n\n\tNodeMetadata nodeMetadata = DefaultNodeMetadata();\n\tbool nodeAlreadyExists = false;\n\tnodeMetadata.groupId = PG_GETARG_INT32(2);\n\tnodeMetadata.nodeRole = PG_GETARG_OID(3);\n\tnodeMetadata.nodeCluster = NameStr(*nodeClusterName);\n\n\tif (nodeMetadata.groupId == COORDINATOR_GROUP_ID)\n\t{\n\t\tereport(ERROR, (errmsg(\"coordinator node cannot be added as inactive node\")));\n\t}\n\n\t/*\n\t * We do not allow metadata operations on secondary nodes in nontransactional\n\t * sync mode.\n\t */\n\tif (nodeMetadata.nodeRole == SecondaryNodeRoleId())\n\t{\n\t\tEnsureTransactionalMetadataSyncMode();\n\t}\n\n\tbool localOnly = false;\n\tint nodeId = AddNodeMetadata(nodeNameString, nodePort, &nodeMetadata,\n\t\t\t\t\t\t\t\t &nodeAlreadyExists, localOnly);\n\tTransactionModifiedNodeMetadata = true;\n\n\tPG_RETURN_INT32(nodeId);\n}\n\n\n/*\n * master_add_inactive_node is a wrapper function for old UDF name.\n */\nDatum\nmaster_add_inactive_node(PG_FUNCTION_ARGS)\n{\n\treturn citus_add_inactive_node(fcinfo);\n}\n\n\n/*\n * citus_add_secondary_node adds a new secondary node to the cluster. It accepts as\n * arguments the primary node it should share a group with.\n */\nDatum\ncitus_add_secondary_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *nodeName = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\tchar *nodeNameString = text_to_cstring(nodeName);\n\n\ttext *primaryName = PG_GETARG_TEXT_P(2);\n\tint32 primaryPort = PG_GETARG_INT32(3);\n\tchar *primaryNameString = text_to_cstring(primaryName);\n\n\tName nodeClusterName = PG_GETARG_NAME(4);\n\tNodeMetadata nodeMetadata = DefaultNodeMetadata();\n\tbool nodeAlreadyExists = false;\n\n\tnodeMetadata.groupId = GroupForNode(primaryNameString, primaryPort);\n\tnodeMetadata.nodeCluster = NameStr(*nodeClusterName);\n\tnodeMetadata.nodeRole = SecondaryNodeRoleId();\n\tnodeMetadata.isActive = true;\n\n\t/*\n\t * We do not allow metadata operations on secondary nodes in nontransactional\n\t * sync mode.\n\t */\n\tEnsureTransactionalMetadataSyncMode();\n\n\tbool localOnly = false;\n\tint nodeId = AddNodeMetadata(nodeNameString, nodePort, &nodeMetadata,\n\t\t\t\t\t\t\t\t &nodeAlreadyExists, localOnly);\n\tTransactionModifiedNodeMetadata = true;\n\n\tPG_RETURN_INT32(nodeId);\n}\n\n\n/*\n * master_add_secondary_node is a wrapper function for old UDF name.\n */\nDatum\nmaster_add_secondary_node(PG_FUNCTION_ARGS)\n{\n\treturn citus_add_secondary_node(fcinfo);\n}\n\n\n/*\n * citus_remove_node function removes the provided node from the pg_dist_node table of\n * the master node and all nodes with metadata.\n * The call to the citus_remove_node should be done by the super user and the specified\n * node should not have any active placements.\n * This function also deletes all reference table placements belong to the given node from\n * pg_dist_placement, but it does not drop actual placement at the node. In the case of\n * re-adding the node, citus_add_node first drops and re-creates the reference tables.\n */\nDatum\ncitus_remove_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *nodeNameText = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\n\tRemoveNodeFromCluster(text_to_cstring(nodeNameText), nodePort);\n\tTransactionModifiedNodeMetadata = true;\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * master_remove_node is a wrapper function for old UDF name.\n */\nDatum\nmaster_remove_node(PG_FUNCTION_ARGS)\n{\n\treturn citus_remove_node(fcinfo);\n}\n\n\n/*\n * citus_disable_node function sets isactive value of the provided node as inactive at\n * coordinator and all nodes with metadata regardless of the node having an active shard\n * placement.\n *\n * The call to the citus_disable_node must be done by the super user.\n *\n * This function also deletes all reference table placements belong to the given node\n * from pg_dist_placement, but it does not drop actual placement at the node. In the case\n * of re-activating the node, citus_add_node first drops and re-creates the reference\n * tables.\n */\nDatum\ncitus_disable_node(PG_FUNCTION_ARGS)\n{\n\ttext *nodeNameText = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\n\tbool synchronousDisableNode = 1;\n\tAssert(PG_NARGS() == 2 || PG_NARGS() == 3);\n\tif (PG_NARGS() == 3)\n\t{\n\t\tsynchronousDisableNode = PG_GETARG_BOOL(2);\n\t}\n\n\tchar *nodeName = text_to_cstring(nodeNameText);\n\tWorkerNode *workerNode = ModifiableWorkerNode(nodeName, nodePort);\n\n\t/* there is no concept of invalid coordinator */\n\tbool isActive = false;\n\tErrorIfCoordinatorMetadataSetFalse(workerNode, BoolGetDatum(isActive),\n\t\t\t\t\t\t\t\t\t   \"isactive\");\n\n\t/*\n\t * We do not allow metadata operations on secondary nodes in nontransactional\n\t * sync mode.\n\t */\n\tif (NodeIsSecondary(workerNode))\n\t{\n\t\tEnsureTransactionalMetadataSyncMode();\n\t}\n\n\tWorkerNode *firstWorkerNode = GetFirstPrimaryWorkerNode();\n\tbool disablingFirstNode =\n\t\t(firstWorkerNode && firstWorkerNode->nodeId == workerNode->nodeId);\n\n\tif (disablingFirstNode && !synchronousDisableNode)\n\t{\n\t\t/*\n\t\t * We sync metadata async and optionally in the background worker,\n\t\t * it would mean that some nodes might get the updates while other\n\t\t * not. And, if the node metadata that is changing is the first\n\t\t * worker node, the problem gets nasty. We serialize modifications\n\t\t * to replicated tables by acquiring locks on the first worker node.\n\t\t *\n\t\t * If some nodes get the metadata changes and some do not, they'd be\n\t\t * acquiring the locks on different nodes. Hence, having the\n\t\t * possibility of diverged shard placements for the same shard.\n\t\t *\n\t\t * To prevent that, we currently do not allow disabling the first\n\t\t * worker node unless it is explicitly opted synchronous.\n\t\t */\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"disabling the first worker node in the \"\n\t\t\t\t\t\t\t   \"metadata is not allowed\"),\n\t\t\t\t\t\terrhint(\"You can force disabling node, SELECT \"\n\t\t\t\t\t\t\t\t\"citus_disable_node('%s', %d, \"\n\t\t\t\t\t\t\t\t\"synchronous:=true);\",\n\t\t\t\t\t\t\t\tworkerNode->workerName,\n\t\t\t\t\t\t\t\tnodePort),\n\t\t\t\t\t\terrdetail(\"Citus uses the first worker node in the \"\n\t\t\t\t\t\t\t\t  \"metadata for certain internal operations when \"\n\t\t\t\t\t\t\t\t  \"replicated tables are modified. Synchronous mode \"\n\t\t\t\t\t\t\t\t  \"ensures that all nodes have the same view of the \"\n\t\t\t\t\t\t\t\t  \"first worker node, which is used for certain \"\n\t\t\t\t\t\t\t\t  \"locking operations.\")));\n\t}\n\n\t/*\n\t * First, locally mark the node as inactive. We'll later trigger background\n\t * worker to sync the metadata changes to the relevant nodes.\n\t */\n\tworkerNode =\n\t\tSetWorkerColumnLocalOnly(workerNode,\n\t\t\t\t\t\t\t\t Anum_pg_dist_node_isactive,\n\t\t\t\t\t\t\t\t BoolGetDatum(isActive));\n\tif (NodeIsPrimary(workerNode))\n\t{\n\t\t/*\n\t\t * We do not allow disabling nodes if it contains any\n\t\t * primary placement that is the \"only\" active placement\n\t\t * for any given shard.\n\t\t */\n\t\tErrorIfNodeContainsNonRemovablePlacements(workerNode);\n\t}\n\n\tTransactionModifiedNodeMetadata = true;\n\n\tif (synchronousDisableNode)\n\t{\n\t\t/*\n\t\t * The user might pick between sync vs async options.\n\t\t *      - Pros for the sync option:\n\t\t *          (a) the changes become visible on the cluster immediately\n\t\t *          (b) even if the first worker node is disabled, there is no\n\t\t *              risk of divergence of the placements of replicated shards\n\t\t *      - Cons for the sync options:\n\t\t *          (a) Does not work within 2PC transaction (e.g., BEGIN;\n\t\t *              citus_disable_node(); PREPARE TRANSACTION ...);\n\t\t *          (b) If there are multiple node failures (e.g., one another node\n\t\t *              than the current node being disabled), the sync option would\n\t\t *              fail because it'd try to sync the metadata changes to a node\n\t\t *              that is not up and running.\n\t\t */\n\t\tif (firstWorkerNode && firstWorkerNode->nodeId == workerNode->nodeId)\n\t\t{\n\t\t\t/*\n\t\t\t * We cannot let any modification query on a replicated table to run\n\t\t\t * concurrently with citus_disable_node() on the first worker node. If\n\t\t\t * we let that, some worker nodes might calculate FirstWorkerNode()\n\t\t\t * different than others. See LockShardListResourcesOnFirstWorker()\n\t\t\t * for the details.\n\t\t\t */\n\t\t\tBlockDistributedQueriesOnMetadataNodes();\n\t\t}\n\n\t\tSyncNodeMetadataToNodes();\n\t}\n\telse if (UnsetMetadataSyncedForAllWorkers())\n\t{\n\t\t/*\n\t\t * We have not propagated the node metadata changes yet, make sure that all the\n\t\t * active nodes get the metadata updates. We defer this operation to the\n\t\t * background worker to make it possible disabling nodes when multiple nodes\n\t\t * are down.\n\t\t *\n\t\t * Note that the active placements reside on the active nodes. Hence, when\n\t\t * Citus finds active placements, it filters out the placements that are on\n\t\t * the disabled nodes. That's why, we don't have to change/sync placement\n\t\t * metadata at this point. Instead, we defer that to citus_activate_node()\n\t\t * where we expect all nodes up and running.\n\t\t */\n\n\t\tTriggerNodeMetadataSyncOnCommit();\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * BlockDistributedQueriesOnMetadataNodes blocks all the modification queries on\n * all nodes. Hence, should be used with caution.\n */\nstatic void\nBlockDistributedQueriesOnMetadataNodes(void)\n{\n\t/* first, block on the coordinator */\n\tLockRelationOid(DistNodeRelationId(), ExclusiveLock);\n\n\t/*\n\t * Note that we might re-design this lock to be more granular than\n\t * pg_dist_node, scoping only for modifications on the replicated\n\t * tables. However, we currently do not have any such mechanism and\n\t * given that citus_disable_node() runs instantly, it seems acceptable\n\t * to block reads (or modifications on non-replicated tables) for\n\t * a while.\n\t */\n\n\t/* only superuser can disable node */\n\tAssert(superuser());\n\n\tSendCommandToWorkersWithMetadata(\n\t\t\"LOCK TABLE pg_catalog.pg_dist_node IN EXCLUSIVE MODE;\");\n}\n\n\n/*\n * master_disable_node is a wrapper function for old UDF name.\n */\nDatum\nmaster_disable_node(PG_FUNCTION_ARGS)\n{\n\treturn citus_disable_node(fcinfo);\n}\n\n\n/*\n * citus_set_node_property sets a property of the node\n */\nDatum\ncitus_set_node_property(PG_FUNCTION_ARGS)\n{\n\ttext *nodeNameText = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\ttext *propertyText = PG_GETARG_TEXT_P(2);\n\tbool value = PG_GETARG_BOOL(3);\n\n\tWorkerNode *workerNode = ModifiableWorkerNode(text_to_cstring(nodeNameText),\n\t\t\t\t\t\t\t\t\t\t\t\t  nodePort);\n\n\t/*\n\t * We do not allow metadata operations on secondary nodes in nontransactional\n\t * sync mode.\n\t */\n\tif (NodeIsSecondary(workerNode))\n\t{\n\t\tEnsureTransactionalMetadataSyncMode();\n\t}\n\n\tif (strcmp(text_to_cstring(propertyText), \"shouldhaveshards\") == 0)\n\t{\n\t\tSetShouldHaveShards(workerNode, value);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"only the 'shouldhaveshards' property can be set using this function\")));\n\t}\n\n\tTransactionModifiedNodeMetadata = true;\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * master_set_node_property is a wrapper function for old UDF name.\n */\nDatum\nmaster_set_node_property(PG_FUNCTION_ARGS)\n{\n\treturn citus_set_node_property(fcinfo);\n}\n\n\n/*\n * ModifiableWorkerNode gets the requested WorkerNode and also gets locks\n * required for modifying it. This fails if the node does not exist.\n */\nWorkerNode *\nModifiableWorkerNode(const char *nodeName, int32 nodePort)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\t/* take an exclusive lock on pg_dist_node to serialize pg_dist_node changes */\n\tLockRelationOid(DistNodeRelationId(), ExclusiveLock);\n\n\tWorkerNode *workerNode = FindWorkerNodeAnyCluster(nodeName, nodePort);\n\tif (workerNode == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"node at \\\"%s:%u\\\" does not exist\", nodeName, nodePort)));\n\t}\n\n\treturn workerNode;\n}\n\n\n/*\n * citus_activate_node UDF activates the given node. It sets the node's isactive\n * value to active and replicates all reference tables to that node.\n */\nDatum\ncitus_activate_node(PG_FUNCTION_ARGS)\n{\n\ttext *nodeNameText = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\n\tchar *nodeNameString = text_to_cstring(nodeNameText);\n\tWorkerNode *workerNode = ModifiableWorkerNode(nodeNameString, nodePort);\n\n\t/*\n\t * We do not allow metadata operations on secondary nodes in nontransactional\n\t * sync mode.\n\t */\n\tif (NodeIsSecondary(workerNode))\n\t{\n\t\tEnsureTransactionalMetadataSyncMode();\n\t}\n\n\t/*\n\t * Create MetadataSyncContext which is used throughout nodes' activation.\n\t * It contains activated nodes, bare connections if the mode is nontransactional,\n\t * and a memory context for allocation.\n\t */\n\tbool collectCommands = false;\n\tbool nodesAddedInSameTransaction = false;\n\tMetadataSyncContext *context = CreateMetadataSyncContext(list_make1(workerNode),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t collectCommands,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t nodesAddedInSameTransaction);\n\n\tActivateNodeList(context);\n\tTransactionModifiedNodeMetadata = true;\n\n\tPG_RETURN_INT32(workerNode->nodeId);\n}\n\n\n/*\n * master_activate_node is a wrapper function for old UDF name.\n */\nDatum\nmaster_activate_node(PG_FUNCTION_ARGS)\n{\n\treturn citus_activate_node(fcinfo);\n}\n\n\n/*\n * GroupForNode returns the group which a given node belongs to.\n *\n * It only works if the requested node is a part of CurrentCluster.\n */\nuint32\nGroupForNode(char *nodeName, int nodePort)\n{\n\tWorkerNode *workerNode = FindWorkerNode(nodeName, nodePort);\n\n\tif (workerNode == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"node at \\\"%s:%u\\\" does not exist\", nodeName, nodePort)));\n\t}\n\n\treturn workerNode->groupId;\n}\n\n\n/*\n * NodeIsPrimaryAndRemote returns whether the argument represents the remote\n * primary node.\n */\nbool\nNodeIsPrimaryAndRemote(WorkerNode *worker)\n{\n\treturn NodeIsPrimary(worker) && !NodeIsLocal(worker);\n}\n\n\n/*\n * NodeIsPrimary returns whether the argument represents a primary node.\n */\nbool\nNodeIsPrimary(WorkerNode *worker)\n{\n\tOid primaryRole = PrimaryNodeRoleId();\n\n\t/* if nodeRole does not yet exist, all nodes are primary nodes */\n\tif (primaryRole == InvalidOid)\n\t{\n\t\treturn true;\n\t}\n\n\treturn worker->nodeRole == primaryRole;\n}\n\n\n/*\n * NodeIsLocal returns whether the argument represents the local node.\n */\nstatic bool\nNodeIsLocal(WorkerNode *worker)\n{\n\treturn worker->groupId == GetLocalGroupId();\n}\n\n\n/*\n * NodeIsSecondary returns whether the argument represents a secondary node.\n */\nbool\nNodeIsSecondary(WorkerNode *worker)\n{\n\tOid secondaryRole = SecondaryNodeRoleId();\n\n\t/* if nodeRole does not yet exist, all nodes are primary nodes */\n\tif (secondaryRole == InvalidOid)\n\t{\n\t\treturn false;\n\t}\n\n\treturn worker->nodeRole == secondaryRole;\n}\n\n\n/*\n * NodeIsReadable returns whether we're allowed to send SELECT queries to this\n * node.\n */\nbool\nNodeIsReadable(WorkerNode *workerNode)\n{\n\tif (ReadFromSecondaries == USE_SECONDARY_NODES_NEVER &&\n\t\tNodeIsPrimary(workerNode))\n\t{\n\t\treturn true;\n\t}\n\n\tif (ReadFromSecondaries == USE_SECONDARY_NODES_ALWAYS &&\n\t\tNodeIsSecondary(workerNode))\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * PrimaryNodeForGroup returns the (unique) primary in the specified group.\n *\n * If there are any nodes in the requested group and groupContainsNodes is not NULL\n * it will set the bool groupContainsNodes references to true.\n */\nWorkerNode *\nPrimaryNodeForGroup(int32 groupId, bool *groupContainsNodes)\n{\n\tWorkerNode *workerNode = NULL;\n\tHASH_SEQ_STATUS status;\n\tHTAB *workerNodeHash = GetWorkerNodeHash();\n\n\thash_seq_init(&status, workerNodeHash);\n\n\twhile ((workerNode = hash_seq_search(&status)) != NULL)\n\t{\n\t\tint32 workerNodeGroupId = workerNode->groupId;\n\t\tif (workerNodeGroupId != groupId)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (groupContainsNodes != NULL)\n\t\t{\n\t\t\t*groupContainsNodes = true;\n\t\t}\n\n\t\tif (NodeIsPrimary(workerNode))\n\t\t{\n\t\t\thash_seq_term(&status);\n\t\t\treturn workerNode;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * MarkNodesNotSyncedInLoopBackConnection unsets metadatasynced flag in separate\n * connection to localhost by calling the udf `citus_internal_mark_node_not_synced`.\n */\nstatic void\nMarkNodesNotSyncedInLoopBackConnection(MetadataSyncContext *context,\n\t\t\t\t\t\t\t\t\t   pid_t parentSessionPid)\n{\n\tAssert(context->transactionMode == METADATA_SYNC_NON_TRANSACTIONAL);\n\tAssert(!MetadataSyncCollectsCommands(context));\n\n\t/*\n\t * Set metadatasynced to false for all activated nodes to mark the nodes as not synced\n\t * in case nontransactional metadata sync fails before we activate the nodes inside\n\t * metadataSyncContext.\n\t * We set metadatasynced to false at coordinator to mark the nodes as not synced. But we\n\t * do not set isactive and hasmetadata flags to false as we still want to route queries\n\t * to the nodes if their isactive flag is true and propagate DDL to the nodes if possible.\n\t *\n\t * NOTES:\n\t * 1) We use separate connection to localhost as we would rollback the local\n\t * transaction in case of failure.\n\t * 2) Operator should handle problems at workers if any. Wworkers probably fail\n\t * due to improper metadata when a query hits. Or DDL might fail due to desynced\n\t * nodes. (when hasmetadata = true, metadatasynced = false)\n\t * In those cases, proper metadata sync for the workers should be done.)\n\t */\n\n\t/*\n\t * Because we try to unset metadatasynced flag with a separate transaction,\n\t * we could not find the new node if the node is added in the current local\n\t * transaction. But, hopefully, we do not need to unset metadatasynced for\n\t * the new node as local transaction would rollback in case of a failure.\n\t */\n\tif (context->nodesAddedInSameTransaction)\n\t{\n\t\treturn;\n\t}\n\n\tif (context->activatedWorkerNodeList == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tint connectionFlag = FORCE_NEW_CONNECTION;\n\tMultiConnection *connection = GetNodeConnection(connectionFlag, LocalHostName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tPostPortNumber);\n\n\tList *commandList = NIL;\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, context->activatedWorkerNodeList)\n\t{\n\t\t/*\n\t\t * We need to prevent self deadlock when we access pg_dist_node using separate\n\t\t * connection to localhost. To achieve this, we check if the caller session's\n\t\t * pid holds the Exclusive lock on pg_dist_node. After ensuring that (we are\n\t\t * called from parent session which holds the Exclusive lock), we can safely\n\t\t * update node metadata by acquiring the relaxed lock.\n\t\t */\n\t\tStringInfo metadatasyncCommand = makeStringInfo();\n\t\tappendStringInfo(metadatasyncCommand, CITUS_INTERNAL_MARK_NODE_NOT_SYNCED,\n\t\t\t\t\t\t parentSessionPid, workerNode->nodeId);\n\t\tcommandList = lappend(commandList, metadatasyncCommand->data);\n\t}\n\n\tSendCommandListToWorkerOutsideTransactionWithConnection(connection, commandList);\n\tCloseConnection(connection);\n}\n\n\n/*\n * SetNodeMetadata sets isactive, metadatasynced and hasmetadata flags locally\n * and, if required, remotely.\n */\nstatic void\nSetNodeMetadata(MetadataSyncContext *context, bool localOnly)\n{\n\t/* do not execute local transaction if we collect commands */\n\tif (!MetadataSyncCollectsCommands(context))\n\t{\n\t\tList *updatedActivatedNodeList = NIL;\n\n\t\tWorkerNode *node = NULL;\n\t\tforeach_declared_ptr(node, context->activatedWorkerNodeList)\n\t\t{\n\t\t\tnode = SetWorkerColumnLocalOnly(node, Anum_pg_dist_node_isactive,\n\t\t\t\t\t\t\t\t\t\t\tBoolGetDatum(true));\n\t\t\tnode = SetWorkerColumnLocalOnly(node, Anum_pg_dist_node_metadatasynced,\n\t\t\t\t\t\t\t\t\t\t\tBoolGetDatum(true));\n\t\t\tnode = SetWorkerColumnLocalOnly(node, Anum_pg_dist_node_hasmetadata,\n\t\t\t\t\t\t\t\t\t\t\tBoolGetDatum(true));\n\n\t\t\tupdatedActivatedNodeList = lappend(updatedActivatedNodeList, node);\n\t\t}\n\n\t\t/* reset activated nodes inside metadataSyncContext afer local update */\n\t\tSetMetadataSyncNodesFromNodeList(context, updatedActivatedNodeList);\n\t}\n\n\tif (!localOnly && EnableMetadataSync)\n\t{\n\t\tWorkerNode *node = NULL;\n\t\tforeach_declared_ptr(node, context->activatedWorkerNodeList)\n\t\t{\n\t\t\tSetNodeStateViaMetadataContext(context, node, BoolGetDatum(true));\n\t\t}\n\t}\n}\n\n\n/*\n * ActivateNodeList does some sanity checks and acquire Exclusive lock on pg_dist_node,\n * and then activates the nodes inside given metadataSyncContext.\n *\n * The function operates in 3 different modes according to transactionMode inside\n * metadataSyncContext.\n *\n * 1. MetadataSyncCollectsCommands(context):\n *      Only collect commands instead of sending them to workers,\n * 2. context.transactionMode == METADATA_SYNC_TRANSACTIONAL:\n *      Send all commands using coordinated transaction,\n * 3. context.transactionMode == METADATA_SYNC_NON_TRANSACTIONAL:\n *      Send all commands using bare (no transaction block) connections.\n */\nvoid\nActivateNodeList(MetadataSyncContext *context)\n{\n\tif (context->transactionMode == METADATA_SYNC_NON_TRANSACTIONAL &&\n\t\tIsMultiStatementTransaction())\n\t{\n\t\t/*\n\t\t * prevent inside transaction block as we use bare connections which can\n\t\t * lead deadlock\n\t\t */\n\t\tereport(ERROR, (errmsg(\"do not sync metadata in transaction block \"\n\t\t\t\t\t\t\t   \"when the sync mode is nontransactional\"),\n\t\t\t\t\t\terrhint(\"resync after SET citus.metadata_sync_mode \"\n\t\t\t\t\t\t\t\t\"TO 'transactional'\")));\n\t}\n\n\t/*\n\t * We currently require the object propagation to happen via superuser,\n\t * see #5139. While activating a node, we sync both metadata and object\n\t * propagation.\n\t *\n\t * In order to have a fully transactional semantics with add/activate\n\t * node operations, we require superuser. Note that for creating\n\t * non-owned objects, we already require a superuser connection.\n\t * By ensuring the current user to be a superuser, we can guarantee\n\t * to send all commands within the same remote transaction.\n\t */\n\tEnsureSuperUser();\n\n\t/*\n\t * Take an exclusive lock on pg_dist_node to serialize pg_dist_node\n\t * changes.\n\t */\n\tLockRelationOid(DistNodeRelationId(), ExclusiveLock);\n\n\t/*\n\t * Error if there is concurrent change to node table before acquiring\n\t * the lock\n\t */\n\tErrorIfAnyNodeNotExist(context->activatedWorkerNodeList);\n\n\t/*\n\t * we need to unset metadatasynced flag to false at coordinator in separate\n\t * transaction only at nontransactional sync mode and if we do not collect\n\t * commands.\n\t *\n\t * We make sure we set the flag to false at the start of nontransactional\n\t * metadata sync to mark those nodes are not synced in case of a failure in\n\t * the middle of the sync.\n\t */\n\tif (context->transactionMode == METADATA_SYNC_NON_TRANSACTIONAL &&\n\t\t!MetadataSyncCollectsCommands(context))\n\t{\n\t\tMarkNodesNotSyncedInLoopBackConnection(context, MyProcPid);\n\t}\n\n\t/*\n\t * Delete existing reference and replicated table placements on the\n\t * given groupId if the group has been disabled earlier (e.g., isActive\n\t * set to false).\n\t */\n\tSendDeletionCommandsForReplicatedTablePlacements(context);\n\n\t/*\n\t * SetNodeMetadata sets isactive, metadatasynced and hasmetadata flags\n\t * locally for following reasons:\n\t *\n\t * 1) Set isactive to true locally so that we can find activated nodes amongst\n\t *    active workers,\n\t * 2) Do not fail just because the current metadata is not synced. (see\n\t *    ErrorIfAnyMetadataNodeOutOfSync),\n\t * 3) To propagate activated nodes nodemetadata correctly.\n\t *\n\t * We are going to sync the metadata anyway in this transaction, set\n\t * isactive, metadatasynced, and hasmetadata to true locally.\n\t * The changes would rollback in case of failure.\n\t */\n\tbool localOnly = true;\n\tSetNodeMetadata(context, localOnly);\n\n\t/*\n\t * Update local group ids so that upcoming transactions can see its effect.\n\t * Object dependency logic requires to have updated local group id.\n\t */\n\tUpdateLocalGroupIdsViaMetadataContext(context);\n\n\t/*\n\t * Sync node metadata so that placement insertion does not fail due to\n\t * EnsureShardPlacementMetadataIsSane.\n\t */\n\tSyncNodeMetadata(context);\n\n\t/*\n\t * Sync all dependencies and distributed objects with their pg_dist_xx tables to\n\t * metadata nodes inside metadataSyncContext. Depends on node metadata.\n\t */\n\tSyncDistributedObjects(context);\n\n\t/*\n\t * Let all nodes to be active and synced after all operations succeeded.\n\t * we make sure that the metadata sync is idempotent and safe overall with multiple\n\t * other transactions, if nontransactional mode is used.\n\t *\n\t * We already took Exclusive lock on node metadata, which prevents modification\n\t * on node metadata on coordinator. The step will rollback, in case of a failure,\n\t * to the state where metadatasynced=false.\n\t */\n\tlocalOnly = false;\n\tSetNodeMetadata(context, localOnly);\n}\n\n\n/*\n * ActivateCloneNodeAsPrimary sets the given worker node as primary and active\n * in the pg_dist_node catalog and make the clone node as first class citizen.\n */\nvoid\nActivateCloneNodeAsPrimary(WorkerNode *workerNode)\n{\n\tRelation pgDistNode = table_open(DistNodeRelationId(), AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistNode);\n\tTupleDesc copiedTupleDescriptor = CreateTupleDescCopy(tupleDescriptor);\n\ttable_close(pgDistNode, AccessShareLock);\n\n\t/*\n\t * Set the node as primary and active.\n\t */\n\tSetWorkerColumnLocalOnly(workerNode, Anum_pg_dist_node_noderole,\n\t\t\t\t\t\t\t ObjectIdGetDatum(PrimaryNodeRoleId()));\n\tSetWorkerColumnLocalOnly(workerNode, Anum_pg_dist_node_isactive,\n\t\t\t\t\t\t\t BoolGetDatum(true));\n\tSetWorkerColumnLocalOnly(workerNode,\n\t\t\t\t\t\t\t GetNodeIsCloneAttrIndexInPgDistNode(copiedTupleDescriptor) +\n\t\t\t\t\t\t\t 1,\n\t\t\t\t\t\t\t BoolGetDatum(false));\n\tSetWorkerColumnLocalOnly(workerNode,\n\t\t\t\t\t\t\t GetNodePrimaryNodeIdAttrIndexInPgDistNode(\n\t\t\t\t\t\t\t\t copiedTupleDescriptor) + 1,\n\t\t\t\t\t\t\t Int32GetDatum(0));\n\tSetWorkerColumnLocalOnly(workerNode, Anum_pg_dist_node_hasmetadata,\n\t\t\t\t\t\t\t BoolGetDatum(true));\n\tSetWorkerColumnLocalOnly(workerNode, Anum_pg_dist_node_metadatasynced,\n\t\t\t\t\t\t\t BoolGetDatum(true));\n\tSetWorkerColumnLocalOnly(workerNode, Anum_pg_dist_node_shouldhaveshards,\n\t\t\t\t\t\t\t BoolGetDatum(true));\n}\n\n\n/*\n * Acquires shard metadata locks on all shards residing in the given worker node\n *\n * TODO: This function is not compatible with query from any node feature.\n * To ensure proper behavior, it is essential to acquire locks on placements across all nodes\n * rather than limiting it to just the coordinator (or the specific node from which this function is called)\n */\nvoid\nLockShardsInWorkerPlacementList(WorkerNode *workerNode, LOCKMODE lockMode)\n{\n\tList *placementList = AllShardPlacementsOnNodeGroup(workerNode->groupId);\n\tLockShardsInPlacementListMetadata(placementList, lockMode);\n}\n\n\n/*\n * This function is used to start a background worker to kill backends holding conflicting\n * locks with this backend. It returns NULL if the background worker could not be started.\n */\nBackgroundWorkerHandle *\nCheckBackgroundWorkerToObtainLocks(int32 lock_cooldown)\n{\n\tBackgroundWorkerHandle *handle = StartLockAcquireHelperBackgroundWorker(MyProcPid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlock_cooldown)\n\t;\n\tif (!handle)\n\t{\n\t\t/*\n\t\t * We failed to start a background worker, which probably means that we exceeded\n\t\t * max_worker_processes, and this is unlikely to be resolved by retrying. We do not want\n\t\t * to repeatedly throw an error because if citus_update_node is called to complete a\n\t\t * failover then finishing is the only way to bring the cluster back up. Therefore we\n\t\t * give up on killing other backends and simply wait for the lock. We do set\n\t\t * lock_timeout to lock_cooldown, because we don't want to wait forever to get a lock.\n\t\t */\n\t\tSetLockTimeoutLocally(lock_cooldown);\n\t\tereport(WARNING, (errmsg(\n\t\t\t\t\t\t\t  \"could not start background worker to kill backends with conflicting\"\n\t\t\t\t\t\t\t  \" locks to force the update. Degrading to acquiring locks \"\n\t\t\t\t\t\t\t  \"with a lock time out.\"),\n\t\t\t\t\t\t  errhint(\n\t\t\t\t\t\t\t  \"Increasing max_worker_processes might help.\")));\n\t}\n\treturn handle;\n}\n\n\n/*\n * This function is used to lock shards in a primary node.\n * If force is true, we start a background worker to kill backends holding\n * conflicting locks with this backend.\n *\n * If the node is a primary node we block reads and writes.\n *\n * This lock has two purposes:\n *\n * - Ensure buggy code in Citus doesn't cause failures when the\n *   nodename/nodeport of a node changes mid-query\n *\n * - Provide fencing during failover, after this function returns all\n *   connections will use the new node location.\n *\n * Drawback:\n *\n * - This function blocks until all previous queries have finished. This\n *   means that long-running queries will prevent failover.\n *\n *   In case of node failure said long-running queries will fail in the end\n *   anyway as they will be unable to commit successfully on the failed\n *   machine. To cause quick failure of these queries use force => true\n *   during the invocation of citus_update_node to terminate conflicting\n *   backends proactively.\n *\n * It might be worth blocking reads to a secondary for the same reasons,\n * though we currently only query secondaries on follower clusters\n * where these locks will have no effect.\n */\nBackgroundWorkerHandle *\nLockPlacementsWithBackgroundWorkersInPrimaryNode(WorkerNode *workerNode, bool force, int32\n\t\t\t\t\t\t\t\t\t\t\t\t lock_cooldown)\n{\n\tBackgroundWorkerHandle *handle = NULL;\n\n\tif (NodeIsPrimary(workerNode))\n\t{\n\t\tif (force)\n\t\t{\n\t\t\thandle = CheckBackgroundWorkerToObtainLocks(lock_cooldown);\n\t\t}\n\t\tLockShardsInWorkerPlacementList(workerNode, AccessExclusiveLock);\n\t}\n\treturn handle;\n}\n\n\n/*\n * citus_update_node moves the requested node to a different nodename and nodeport. It\n * locks to ensure no queries are running concurrently; and is intended for customers who\n * are running their own failover solution.\n */\nDatum\ncitus_update_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint32 nodeId = PG_GETARG_INT32(0);\n\n\ttext *newNodeName = PG_GETARG_TEXT_P(1);\n\tint32 newNodePort = PG_GETARG_INT32(2);\n\n\t/*\n\t * force is used when an update needs to happen regardless of conflicting locks. This\n\t * feature is important to force the update during a failover due to failure, eg. by\n\t * a high-availability system such as pg_auto_failover. The strategy is to start a\n\t * background worker that actively cancels backends holding conflicting locks with\n\t * this backend.\n\t *\n\t * Defaults to false\n\t */\n\tbool force = PG_GETARG_BOOL(3);\n\tint32 lock_cooldown = PG_GETARG_INT32(4);\n\n\tchar *newNodeNameString = text_to_cstring(newNodeName);\n\n\tWorkerNode *workerNodeWithSameAddress = FindWorkerNodeAnyCluster(newNodeNameString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t newNodePort);\n\tif (workerNodeWithSameAddress != NULL)\n\t{\n\t\t/* a node with the given hostname and port already exists in the metadata */\n\n\t\tif (workerNodeWithSameAddress->nodeId == nodeId)\n\t\t{\n\t\t\t/* it's the node itself, meaning this is a noop update */\n\t\t\tPG_RETURN_VOID();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"there is already another node with the specified \"\n\t\t\t\t\t\t\t\t   \"hostname and port\")));\n\t\t}\n\t}\n\n\tWorkerNode *workerNode = FindNodeAnyClusterByNodeId(nodeId);\n\tif (workerNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_NO_DATA_FOUND),\n\t\t\t\t\t\terrmsg(\"node %u not found\", nodeId)));\n\t}\n\n\t/*\n\t * We do not allow metadata operations on secondary nodes in nontransactional\n\t * sync mode.\n\t */\n\tif (NodeIsSecondary(workerNode))\n\t{\n\t\tEnsureTransactionalMetadataSyncMode();\n\t}\n\n\tBackgroundWorkerHandle *handle = LockPlacementsWithBackgroundWorkersInPrimaryNode(\n\t\tworkerNode, force,\n\t\tlock_cooldown);\n\n\t/*\n\t * if we have planned statements such as prepared statements, we should clear the cache so that\n\t * the planned cache doesn't return the old nodename/nodepost.\n\t */\n\tResetPlanCache();\n\n\tbool localOnly = true;\n\tUpdateNodeLocation(nodeId, newNodeNameString, newNodePort, localOnly);\n\n\t/* we should be able to find the new node from the metadata */\n\tworkerNode = FindWorkerNodeAnyCluster(newNodeNameString, newNodePort);\n\tAssert(workerNode->nodeId == nodeId);\n\n\t/*\n\t * Propagate the updated pg_dist_node entry to all metadata workers.\n\t * citus-ha uses citus_update_node() in a prepared transaction, and\n\t * we don't support coordinated prepared transactions, so we cannot\n\t * propagate the changes to the worker nodes here. Instead we mark\n\t * all metadata nodes as not-synced and ask maintenanced to do the\n\t * propagation.\n\t *\n\t * It is possible that maintenance daemon does the first resync too\n\t * early, but that's fine, since this will start a retry loop with\n\t * 5 second intervals until sync is complete.\n\t */\n\tif (UnsetMetadataSyncedForAllWorkers())\n\t{\n\t\tTriggerNodeMetadataSyncOnCommit();\n\t}\n\n\tif (handle != NULL)\n\t{\n\t\t/*\n\t\t * this will be called on memory context cleanup as well, if the worker has been\n\t\t * terminated already this will be a noop\n\t\t */\n\t\tTerminateBackgroundWorker(handle);\n\t}\n\n\tTransactionModifiedNodeMetadata = true;\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * This function is designed to obtain locks for all the shards in a worker placement list.\n * Once the transaction is committed, the acquired locks will be automatically released.\n * Therefore, it is essential to invoke this function within a transaction.\n * This function proves beneficial when there is a need to temporarily disable writes to a specific node within a transaction.\n */\nDatum\ncitus_pause_node_within_txn(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint32 nodeId = PG_GETARG_INT32(0);\n\tbool force = PG_GETARG_BOOL(1);\n\tint32 lock_cooldown = PG_GETARG_INT32(2);\n\n\tWorkerNode *workerNode = FindNodeAnyClusterByNodeId(nodeId);\n\tif (workerNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_NO_DATA_FOUND),\n\t\t\t\t\t\terrmsg(\"node %u not found\", nodeId)));\n\t}\n\n\tLockPlacementsWithBackgroundWorkersInPrimaryNode(workerNode, force, lock_cooldown);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * master_update_node is a wrapper function for old UDF name.\n */\nDatum\nmaster_update_node(PG_FUNCTION_ARGS)\n{\n\treturn citus_update_node(fcinfo);\n}\n\n\n/*\n * citus_add_clone_node adds a new node as a clone of an existing primary node.\n */\nDatum\ncitus_add_clone_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureSuperUser();\n\tEnsureCoordinator();\n\n\ttext *cloneHostnameText = PG_GETARG_TEXT_P(0);\n\tint32 clonePort = PG_GETARG_INT32(1);\n\ttext *primaryHostnameText = PG_GETARG_TEXT_P(2);\n\tint32 primaryPort = PG_GETARG_INT32(3);\n\n\tchar *cloneHostname = text_to_cstring(cloneHostnameText);\n\tchar *primaryHostname = text_to_cstring(primaryHostnameText);\n\n\tWorkerNode *primaryWorker = FindWorkerNodeAnyCluster(primaryHostname, primaryPort);\n\n\tif (primaryWorker == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"primary node %s:%d not found in pg_dist_node\",\n\t\t\t\t\t\t\t   primaryHostname, primaryPort)));\n\t}\n\n\tint32 cloneNodeId = CitusAddCloneNode(primaryWorker, cloneHostname, clonePort);\n\n\tPG_RETURN_INT32(cloneNodeId);\n}\n\n\n/*\n * citus_add_clone_node_with_nodeid adds a new node as a clone of an existing primary node\n * using the primary node's ID. It records the clone's hostname, port, and links it to the\n * primary node's ID.\n *\n * This function is useful when you already know the primary node's ID and want to add a clone\n * without needing to look it up by hostname and port.\n */\nDatum\ncitus_add_clone_node_with_nodeid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureSuperUser();\n\tEnsureCoordinator();\n\n\ttext *cloneHostnameText = PG_GETARG_TEXT_P(0);\n\tint32 clonePort = PG_GETARG_INT32(1);\n\tint32 primaryNodeId = PG_GETARG_INT32(2);\n\n\tchar *cloneHostname = text_to_cstring(cloneHostnameText);\n\n\tbool missingOk = false;\n\tWorkerNode *primaryWorkerNode = FindNodeWithNodeId(primaryNodeId, missingOk);\n\n\tif (primaryWorkerNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"primary node with ID %d does not exist\", primaryNodeId)));\n\t}\n\n\tint32 cloneNodeId = CitusAddCloneNode(primaryWorkerNode, cloneHostname, clonePort);\n\n\tPG_RETURN_INT32(cloneNodeId);\n}\n\n\n/*\n * CitusAddCloneNode function adds a new node as a clone of an existing primary node.\n * It records the clone's hostname, port, and links it to the primary node's ID.\n * The clone is initially marked as inactive and not having shards.\n */\nstatic int32\nCitusAddCloneNode(WorkerNode *primaryWorkerNode,\n\t\t\t\t  char *cloneHostname, int32 clonePort)\n{\n\tAssert(primaryWorkerNode != NULL);\n\n\t/* Future-proofing: Ideally, a primary node should not itself be a clone.\n\t * This check might be more relevant once replica promotion logic exists.\n\t * For now, pg_dist_node.nodeisclone defaults to false for existing nodes.\n\t */\n\tif (primaryWorkerNode->nodeisclone)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"primary node %s:%d is itself a clone and cannot have clones\",\n\t\t\t\t\t\t\tprimaryWorkerNode->workerName, primaryWorkerNode->\n\t\t\t\t\t\t\tworkerPort)));\n\t}\n\n\tif (!primaryWorkerNode->shouldHaveShards)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"primary node %s:%d does not have shards, node without shards cannot have clones\",\n\t\t\t\t\t\t\tprimaryWorkerNode->workerName, primaryWorkerNode->\n\t\t\t\t\t\t\tworkerPort)));\n\t}\n\n\tWorkerNode *existingCloneNode = FindWorkerNodeAnyCluster(cloneHostname, clonePort);\n\tif (existingCloneNode != NULL)\n\t{\n\t\t/*\n\t\t * Idempotency check: If the node already exists, is it already correctly\n\t\t * registered as a clone for THIS primary?\n\t\t */\n\t\tif (existingCloneNode->nodeisclone &&\n\t\t\texistingCloneNode->nodeprimarynodeid == primaryWorkerNode->nodeId)\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t\t\t \"node %s:%d is already registered as a clone for primary %s:%d (nodeid %d)\",\n\t\t\t\t\t\t\t\t cloneHostname, clonePort,\n\t\t\t\t\t\t\t\t primaryWorkerNode->workerName, primaryWorkerNode->\n\t\t\t\t\t\t\t\t workerPort, primaryWorkerNode->nodeId)));\n\t\t\tPG_RETURN_INT32(existingCloneNode->nodeId);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\"a different node %s:%d (nodeid %d) already exists or is a clone for a different primary\",\n\t\t\t\t\t\t\t\tcloneHostname, clonePort, existingCloneNode->nodeId)));\n\t\t}\n\t}\n\tEnsureValidStreamingReplica(primaryWorkerNode, cloneHostname, clonePort);\n\n\tchar *operation = \"add\";\n\tEnsureValidCloneMode(primaryWorkerNode, cloneHostname, clonePort, operation);\n\n\tNodeMetadata nodeMetadata = DefaultNodeMetadata();\n\n\tnodeMetadata.nodeisclone = true;\n\tnodeMetadata.nodeprimarynodeid = primaryWorkerNode->nodeId;\n\tnodeMetadata.isActive = false; /* Replicas start as inactive */\n\tnodeMetadata.shouldHaveShards = false; /* Replicas do not directly own primary shards */\n\tnodeMetadata.groupId = INVALID_GROUP_ID; /* Replicas get a new group ID and do not belong to any existing group */\n\tnodeMetadata.nodeRole = UnavailableNodeRoleId(); /* The node role is set to 'unavailable' */\n\tnodeMetadata.nodeCluster = primaryWorkerNode->nodeCluster; /* Same cluster as primary */\n\n\t/* Other fields like hasMetadata, metadataSynced will take defaults from DefaultNodeMetadata\n\t * (typically true, true for hasMetadata and metadataSynced if it's a new node,\n\t * or might need adjustment based on replica strategy)\n\t * For now, let's assume DefaultNodeMetadata provides suitable defaults for these\n\t * or they will be set by AddNodeMetadata/ActivateNodeList if needed.\n\t * Specifically, hasMetadata is often true, and metadataSynced true after activation.\n\t * Since this replica is inactive, metadata sync status might be less critical initially.\n\t */\n\n\tbool nodeAlreadyExists = false;\n\tbool localOnly = false; /* Propagate change to other workers with metadata */\n\n\t/*\n\t * AddNodeMetadata will take an ExclusiveLock on pg_dist_node.\n\t * It also checks again if the node already exists after acquiring the lock.\n\t */\n\tint cloneNodeId = AddNodeMetadata(cloneHostname, clonePort, &nodeMetadata,\n\t\t\t\t\t\t\t\t\t  &nodeAlreadyExists, localOnly);\n\n\tif (nodeAlreadyExists)\n\t{\n\t\t/* This case should ideally be caught by the FindWorkerNodeAnyCluster check above,\n\t\t * but AddNodeMetadata does its own check after locking.\n\t\t * If it already exists and is correctly configured, we might have returned NOTICE above.\n\t\t * If it exists but is NOT correctly configured as our replica, an ERROR would be more appropriate.\n\t\t * AddNodeMetadata returns the existing node's ID if it finds one.\n\t\t * We need to ensure it is the *correct* replica.\n\t\t */\n\t\tWorkerNode *fetchedExistingNode = FindNodeAnyClusterByNodeId(cloneNodeId);\n\t\tif (fetchedExistingNode != NULL && fetchedExistingNode->nodeisclone &&\n\t\t\tfetchedExistingNode->nodeprimarynodeid == primaryWorkerNode->nodeId)\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t\t\t \"node %s:%d was already correctly registered as a clone for primary %s:%d (nodeid %d)\",\n\t\t\t\t\t\t\t\t cloneHostname, clonePort,\n\t\t\t\t\t\t\t\t primaryWorkerNode->workerName, primaryWorkerNode->\n\t\t\t\t\t\t\t\t workerPort, primaryWorkerNode->nodeId)));\n\n\t\t\t/* Intentional fall-through to return cloneNodeId */\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* This state is less expected if our initial check passed or errored. */\n\t\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\"node %s:%d already exists but is not correctly configured as a clone for primary %s:%d\",\n\t\t\t\t\t\t\t\tcloneHostname, clonePort, primaryWorkerNode->workerName,\n\t\t\t\t\t\t\t\tprimaryWorkerNode->workerPort)));\n\t\t}\n\t}\n\n\tTransactionModifiedNodeMetadata = true;\n\n\t/*\n\t * Note: Clones added this way are inactive.\n\t * A separate UDF citus_promote_clone_and_rebalance\n\t * would be needed to activate them.\n\t */\n\n\treturn cloneNodeId;\n}\n\n\n/*\n * citus_remove_clone_node removes an inactive streaming clone node from Citus metadata.\n */\nDatum\ncitus_remove_clone_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureSuperUser();\n\tEnsureCoordinator();\n\n\ttext *nodeNameText = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\tchar *nodeName = text_to_cstring(nodeNameText);\n\n\tWorkerNode *workerNode = FindWorkerNodeAnyCluster(nodeName, nodePort);\n\n\tif (workerNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"node \\\"%s:%d\\\" does not exist\", nodeName, nodePort)));\n\t}\n\n\tRemoveCloneNode(workerNode);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_remove_clone_node_with_nodeid removes an inactive clone node from Citus metadata\n * using the node's ID.\n */\nDatum\ncitus_remove_clone_node_with_nodeid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureSuperUser();\n\tEnsureCoordinator();\n\n\tuint32 replicaNodeId = PG_GETARG_INT32(0);\n\n\tWorkerNode *replicaNode = FindNodeAnyClusterByNodeId(replicaNodeId);\n\n\tif (replicaNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Clone node with ID %d does not exist\", replicaNodeId)));\n\t}\n\tRemoveCloneNode(replicaNode);\n\n\tPG_RETURN_VOID();\n}\n\n\nstatic void\nRemoveCloneNode(WorkerNode *cloneNode)\n{\n\tAssert(cloneNode != NULL);\n\n\tif (!cloneNode->nodeisclone)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Node %s:%d (ID %d) is not a clone node. \"\n\t\t\t\t\t\t\t   \"Use citus_remove_node() to remove primary or already promoted nodes.\",\n\t\t\t\t\t\t\t   cloneNode->workerName, cloneNode->workerPort, cloneNode->\n\t\t\t\t\t\t\t   nodeId)));\n\t}\n\n\tif (cloneNode->isActive)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Clone node %s:%d (ID %d) is marked as active and cannot be removed with this function. \"\n\t\t\t\t\t\t\t\"This might indicate a promoted clone. Consider using citus_remove_node() if you are sure, \"\n\t\t\t\t\t\t\t\"or ensure it's properly deactivated if it's an unpromoted clone in an unexpected state.\",\n\t\t\t\t\t\t\tcloneNode->workerName, cloneNode->workerPort, cloneNode->\n\t\t\t\t\t\t\tnodeId)));\n\t}\n\n\t/*\n\t * All checks passed, proceed with removal.\n\t * RemoveNodeFromCluster handles locking, catalog changes, connection closing, and metadata sync.\n\t */\n\tereport(NOTICE, (errmsg(\"Removing inactive clone node %s:%d (ID %d)\",\n\t\t\t\t\t\t\tcloneNode->workerName, cloneNode->workerPort, cloneNode->\n\t\t\t\t\t\t\tnodeId)));\n\n\tRemoveNodeFromCluster(cloneNode->workerName, cloneNode->workerPort);\n\n\t/* RemoveNodeFromCluster might set this, but setting it here ensures it's marked for this UDF's transaction. */\n\tTransactionModifiedNodeMetadata = true;\n}\n\n\n/*\n * SetLockTimeoutLocally sets the lock_timeout to the given value.\n * This setting is local.\n */\nstatic void\nSetLockTimeoutLocally(int32 lockCooldown)\n{\n\tset_config_option(\"lock_timeout\", ConvertIntToString(lockCooldown),\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n}\n\n\nstatic void\nUpdateNodeLocation(int32 nodeId, char *newNodeName, int32 newNodePort, bool localOnly)\n{\n\tconst bool indexOK = true;\n\n\tRelation pgDistNode = table_open(DistNodeRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistNode);\n\n\tScanKeyData scanKey[1];\n\tDatum *values = palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isnull = palloc0(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_node_nodeid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(nodeId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistNode, DistNodeNodeIdIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, 1, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for node \\\"%s:%d\\\"\",\n\t\t\t\t\t\t\t   newNodeName, newNodePort)));\n\t}\n\n\tvalues[Anum_pg_dist_node_nodeport - 1] = Int32GetDatum(newNodePort);\n\tisnull[Anum_pg_dist_node_nodeport - 1] = false;\n\treplace[Anum_pg_dist_node_nodeport - 1] = true;\n\n\tvalues[Anum_pg_dist_node_nodename - 1] = CStringGetTextDatum(newNodeName);\n\tisnull[Anum_pg_dist_node_nodename - 1] = false;\n\treplace[Anum_pg_dist_node_nodename - 1] = true;\n\n\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull, replace);\n\n\tCatalogTupleUpdate(pgDistNode, &heapTuple->t_self, heapTuple);\n\n\tCitusInvalidateRelcacheByRelid(DistNodeRelationId());\n\n\tCommandCounterIncrement();\n\n\tif (!localOnly && EnableMetadataSync)\n\t{\n\t\tWorkerNode *updatedNode = FindWorkerNodeAnyCluster(newNodeName, newNodePort);\n\t\tAssert(updatedNode->nodeId == nodeId);\n\n\t\t/* send the delete command to all primary nodes with metadata */\n\t\tchar *nodeDeleteCommand = NodeDeleteCommand(updatedNode->nodeId);\n\t\tSendCommandToWorkersWithMetadata(nodeDeleteCommand);\n\n\t\t/* send the insert command to all primary nodes with metadata */\n\t\tchar *nodeInsertCommand = NodeListInsertCommand(list_make1(updatedNode));\n\t\tSendCommandToWorkersWithMetadata(nodeInsertCommand);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistNode, NoLock);\n\n\tpfree(values);\n\tpfree(isnull);\n\tpfree(replace);\n}\n\n\n/*\n * get_shard_id_for_distribution_column function takes a distributed table name and a\n * distribution value then returns shard id of the shard which belongs to given table and\n * contains given value. This function only works for hash distributed tables.\n */\nDatum\nget_shard_id_for_distribution_column(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tShardInterval *shardInterval = NULL;\n\n\t/*\n\t * To have optional parameter as NULL, we defined this UDF as not strict, therefore\n\t * we need to check all parameters for NULL values.\n\t */\n\tif (PG_ARGISNULL(0))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),\n\t\t\t\t\t\terrmsg(\"relation cannot be NULL\")));\n\t}\n\n\tOid relationId = PG_GETARG_OID(0);\n\tEnsureTablePermissions(relationId, ACL_SELECT, ACLMASK_ANY);\n\n\tif (!IsCitusTable(relationId))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION),\n\t\t\t\t\t\terrmsg(\"relation is not distributed\")));\n\t}\n\n\tif (!HasDistributionKey(relationId))\n\t{\n\t\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\t\tif (shardIntervalList == NIL)\n\t\t{\n\t\t\tPG_RETURN_INT64(0);\n\t\t}\n\n\t\tshardInterval = (ShardInterval *) linitial(shardIntervalList);\n\t}\n\telse if (IsCitusTableType(relationId, HASH_DISTRIBUTED) ||\n\t\t\t IsCitusTableType(relationId, RANGE_DISTRIBUTED))\n\t{\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\n\t\t/* if given table is not reference table, distributionValue cannot be NULL */\n\t\tif (PG_ARGISNULL(1))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),\n\t\t\t\t\t\t\terrmsg(\"distribution value cannot be NULL for tables other \"\n\t\t\t\t\t\t\t\t   \"than reference tables.\")));\n\t\t}\n\n\t\tDatum inputDatum = PG_GETARG_DATUM(1);\n\t\tOid inputDataType = get_fn_expr_argtype(fcinfo->flinfo, 1);\n\t\tchar *distributionValueString = DatumToString(inputDatum, inputDataType);\n\n\t\tVar *distributionColumn = DistPartitionKeyOrError(relationId);\n\t\tOid distributionDataType = distributionColumn->vartype;\n\n\t\tDatum distributionValueDatum = StringToDatum(distributionValueString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t distributionDataType);\n\n\t\tshardInterval = FindShardInterval(distributionValueDatum, cacheEntry);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"finding shard id of given distribution value is only \"\n\t\t\t\t\t\t\t   \"supported for hash partitioned tables, range partitioned \"\n\t\t\t\t\t\t\t   \"tables and reference tables.\")));\n\t}\n\n\tif (shardInterval != NULL)\n\t{\n\t\tPG_RETURN_INT64(shardInterval->shardId);\n\t}\n\n\tPG_RETURN_INT64(0);\n}\n\n\n/*\n * citus_nodename_for_nodeid returns the node name for the node with given node id\n */\nDatum\ncitus_nodename_for_nodeid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint nodeId = PG_GETARG_INT32(0);\n\n\tWorkerNode *node = FindNodeAnyClusterByNodeId(nodeId);\n\n\tif (node == NULL)\n\t{\n\t\tPG_RETURN_NULL();\n\t}\n\n\tPG_RETURN_TEXT_P(cstring_to_text(node->workerName));\n}\n\n\n/*\n * citus_nodeport_for_nodeid returns the node port for the node with given node id\n */\nDatum\ncitus_nodeport_for_nodeid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint nodeId = PG_GETARG_INT32(0);\n\n\tWorkerNode *node = FindNodeAnyClusterByNodeId(nodeId);\n\n\tif (node == NULL)\n\t{\n\t\tPG_RETURN_NULL();\n\t}\n\n\tPG_RETURN_INT32(node->workerPort);\n}\n\n\n/*\n * citus_coordinator_nodeid returns the node id of the coordinator node\n */\nDatum\ncitus_coordinator_nodeid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint coordinatorNodeId = FindCoordinatorNodeId();\n\n\tif (coordinatorNodeId == -1)\n\t{\n\t\tPG_RETURN_INT32(0);\n\t}\n\n\tPG_RETURN_INT32(coordinatorNodeId);\n}\n\n\n/*\n * citus_is_coordinator returns whether the current node is a coordinator.\n * We consider the node a coordinator if its group ID is 0 and it has\n * pg_dist_node entries (only group ID 0 could indicate a worker without\n * metadata).\n */\nDatum\ncitus_is_coordinator(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tbool isCoordinator = false;\n\n\tif (GetLocalGroupId() == COORDINATOR_GROUP_ID &&\n\t\tActiveReadableNodeCount() > 0)\n\t{\n\t\tisCoordinator = true;\n\t}\n\n\tPG_RETURN_BOOL(isCoordinator);\n}\n\n\n/*\n * citus_is_primary_node returns whether the current node is a primary for\n * a given group_id. We consider the node a primary if it has\n * pg_dist_node entries marked as primary\n */\nDatum\ncitus_is_primary_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint32 groupId = GetLocalGroupId();\n\tWorkerNode *workerNode = PrimaryNodeForGroup(groupId, NULL);\n\tif (workerNode == NULL)\n\t{\n\t\tereport(WARNING, (errmsg(\"could not find the current node in pg_dist_node\"),\n\t\t\t\t\t\t  errdetail(\"If this is the coordinator node, consider adding it \"\n\t\t\t\t\t\t\t\t\t\"into the metadata by using citus_set_coordinator_host() \"\n\t\t\t\t\t\t\t\t\t\"UDF. Otherwise, if you're going to use this node as a \"\n\t\t\t\t\t\t\t\t\t\"worker node for a new cluster, make sure to add this \"\n\t\t\t\t\t\t\t\t\t\"node into the metadata from the coordinator by using \"\n\t\t\t\t\t\t\t\t\t\"citus_add_node() UDF.\")));\n\t\tPG_RETURN_NULL();\n\t}\n\n\tbool isPrimary = workerNode->nodeId == GetLocalNodeId();\n\n\tPG_RETURN_BOOL(isPrimary);\n}\n\n\n/*\n * EnsureParentSessionHasExclusiveLockOnPgDistNode ensures given session id\n * holds Exclusive lock on pg_dist_node.\n */\nstatic void\nEnsureParentSessionHasExclusiveLockOnPgDistNode(pid_t parentSessionPid)\n{\n\tStringInfo checkIfParentLockCommandStr = makeStringInfo();\n\n\tint spiConnectionResult = SPI_connect();\n\tif (spiConnectionResult != SPI_OK_CONNECT)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not connect to SPI manager\")));\n\t}\n\n\tchar *checkIfParentLockCommand = \"SELECT pid FROM pg_locks WHERE \"\n\t\t\t\t\t\t\t\t\t \"pid = %d AND database = %d AND relation = %d AND \"\n\t\t\t\t\t\t\t\t\t \"mode = 'ExclusiveLock' AND granted = TRUE\";\n\tappendStringInfo(checkIfParentLockCommandStr, checkIfParentLockCommand,\n\t\t\t\t\t parentSessionPid, MyDatabaseId, DistNodeRelationId());\n\n\tbool readOnly = true;\n\tint spiQueryResult = SPI_execute(checkIfParentLockCommandStr->data, readOnly, 0);\n\tif (spiQueryResult != SPI_OK_SELECT)\n\t{\n\t\tereport(ERROR, (errmsg(\"execution was not successful \\\"%s\\\"\",\n\t\t\t\t\t\t\t   checkIfParentLockCommandStr->data)));\n\t}\n\n\tbool parentHasExclusiveLock = SPI_processed > 0;\n\n\tSPI_finish();\n\n\tif (!parentHasExclusiveLock)\n\t{\n\t\tereport(ERROR, (errmsg(\"lock is not held by the caller. Unexpected caller \"\n\t\t\t\t\t\t\t   \"for citus_internal.mark_node_not_synced\")));\n\t}\n}\n\n\n/*\n * citus_internal_mark_node_not_synced unsets metadatasynced flag in separate connection\n * to localhost. Should only be called by `MarkNodesNotSyncedInLoopBackConnection`.\n * See it for details.\n */\nDatum\ncitus_internal_mark_node_not_synced(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\t/* only called by superuser */\n\tEnsureSuperUser();\n\n\tpid_t parentSessionPid = PG_GETARG_INT32(0);\n\n\t/* fetch node by id */\n\tint nodeId = PG_GETARG_INT32(1);\n\tHeapTuple heapTuple = GetNodeByNodeId(nodeId);\n\n\t/* ensure that parent session holds Exclusive lock to pg_dist_node */\n\tEnsureParentSessionHasExclusiveLockOnPgDistNode(parentSessionPid);\n\n\t/*\n\t * We made sure parent session holds the ExclusiveLock, so we can unset\n\t * metadatasynced for the node safely with the relaxed lock here.\n\t */\n\tRelation pgDistNode = table_open(DistNodeRelationId(), AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistNode);\n\n\tDatum *values = palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isnull = palloc0(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tvalues[Anum_pg_dist_node_metadatasynced - 1] = DatumGetBool(false);\n\tisnull[Anum_pg_dist_node_metadatasynced - 1] = false;\n\treplace[Anum_pg_dist_node_metadatasynced - 1] = true;\n\n\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull, replace);\n\n\tCatalogTupleUpdate(pgDistNode, &heapTuple->t_self, heapTuple);\n\n\tCitusInvalidateRelcacheByRelid(DistNodeRelationId());\n\tCommandCounterIncrement();\n\n\ttable_close(pgDistNode, NoLock);\n\n\tpfree(values);\n\tpfree(isnull);\n\tpfree(replace);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * FindWorkerNode searches over the worker nodes and returns the workerNode\n * if it already exists. Else, the function returns NULL.\n *\n * NOTE: A special case that this handles is when nodeName and nodePort are set\n * to LocalHostName and PostPortNumber. In that case we return the primary node\n * for the local group.\n */\nWorkerNode *\nFindWorkerNode(const char *nodeName, int32 nodePort)\n{\n\tHTAB *workerNodeHash = GetWorkerNodeHash();\n\tbool handleFound = false;\n\n\tWorkerNode *searchedNode = (WorkerNode *) palloc0(sizeof(WorkerNode));\n\tstrlcpy(searchedNode->workerName, nodeName, WORKER_LENGTH);\n\tsearchedNode->workerPort = nodePort;\n\n\tvoid *hashKey = (void *) searchedNode;\n\tWorkerNode *cachedWorkerNode = (WorkerNode *) hash_search(workerNodeHash, hashKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  HASH_FIND,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &handleFound);\n\tif (handleFound)\n\t{\n\t\tWorkerNode *workerNode = (WorkerNode *) palloc(sizeof(WorkerNode));\n\t\t*workerNode = *cachedWorkerNode;\n\t\treturn workerNode;\n\t}\n\n\tif (strcmp(LocalHostName, nodeName) == 0 && nodePort == PostPortNumber)\n\t{\n\t\treturn PrimaryNodeForGroup(GetLocalGroupId(), NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * FindWorkerNode searches over the worker nodes and returns the workerNode\n * if it exists otherwise it errors out.\n */\nWorkerNode *\nFindWorkerNodeOrError(const char *nodeName, int32 nodePort)\n{\n\tWorkerNode *node = FindWorkerNode(nodeName, nodePort);\n\tif (node == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_NO_DATA_FOUND),\n\t\t\t\t\t\terrmsg(\"node %s:%d not found\", nodeName, nodePort)));\n\t}\n\treturn node;\n}\n\n\n/*\n * FindWorkerNodeAnyCluster returns the workerNode no matter which cluster it is a part\n * of. FindWorkerNodes, like almost every other function, acts as if nodes in other\n * clusters do not exist.\n */\nWorkerNode *\nFindWorkerNodeAnyCluster(const char *nodeName, int32 nodePort)\n{\n\tWorkerNode *workerNode = NULL;\n\n\tRelation pgDistNode = table_open(DistNodeRelationId(), AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistNode);\n\n\tHeapTuple heapTuple = GetNodeTuple(nodeName, nodePort);\n\tif (heapTuple != NULL)\n\t{\n\t\tworkerNode = TupleToWorkerNode(pgDistNode, tupleDescriptor, heapTuple);\n\t}\n\n\ttable_close(pgDistNode, NoLock);\n\treturn workerNode;\n}\n\n\n/*\n * FindNodeAnyClusterByNodeId searches pg_dist_node and returns the node with\n * the nodeId. If the node can't be found returns NULL.\n */\nWorkerNode *\nFindNodeAnyClusterByNodeId(uint32 nodeId)\n{\n\tbool includeNodesFromOtherClusters = true;\n\tList *nodeList = ReadDistNode(includeNodesFromOtherClusters);\n\tWorkerNode *node = NULL;\n\n\tforeach_declared_ptr(node, nodeList)\n\t{\n\t\tif (node->nodeId == nodeId)\n\t\t{\n\t\t\treturn node;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * FindNodeWithNodeId searches pg_dist_node and returns the node with the nodeId.\n * If the node cannot be found this functions errors.\n */\nWorkerNode *\nFindNodeWithNodeId(int nodeId, bool missingOk)\n{\n\tList *nodeList = ActiveReadableNodeList();\n\tWorkerNode *node = NULL;\n\n\tforeach_declared_ptr(node, nodeList)\n\t{\n\t\tif (node->nodeId == nodeId)\n\t\t{\n\t\t\treturn node;\n\t\t}\n\t}\n\n\t/* there isn't any node with nodeId in pg_dist_node */\n\tif (!missingOk)\n\t{\n\t\telog(ERROR, \"node with node id %d could not be found\", nodeId);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * FindCoordinatorNodeId returns the node id of the coordinator node\n */\nint\nFindCoordinatorNodeId()\n{\n\tbool includeNodesFromOtherClusters = false;\n\tList *nodeList = ReadDistNode(includeNodesFromOtherClusters);\n\tWorkerNode *node = NULL;\n\n\tforeach_declared_ptr(node, nodeList)\n\t{\n\t\tif (NodeIsCoordinator(node))\n\t\t{\n\t\t\treturn node->nodeId;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\n\n/*\n * ReadDistNode iterates over pg_dist_node table, converts each row\n * into its memory representation (i.e., WorkerNode) and adds them into\n * a list. Lastly, the list is returned to the caller.\n *\n * It skips nodes which are not in the current clusters unless requested to do otherwise\n * by includeNodesFromOtherClusters.\n */\nList *\nReadDistNode(bool includeNodesFromOtherClusters)\n{\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 0;\n\tList *workerNodeList = NIL;\n\n\tRelation pgDistNode = table_open(DistNodeRelationId(), AccessShareLock);\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistNode);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tWorkerNode *workerNode = TupleToWorkerNode(pgDistNode, tupleDescriptor, heapTuple)\n\t\t;\n\n\t\tif (includeNodesFromOtherClusters ||\n\t\t\tstrncmp(workerNode->nodeCluster, CurrentCluster, WORKER_LENGTH) == 0)\n\t\t{\n\t\t\t/* the coordinator acts as if it never sees nodes not in its cluster */\n\t\t\tworkerNodeList = lappend(workerNodeList, workerNode);\n\t\t}\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistNode, NoLock);\n\n\treturn workerNodeList;\n}\n\n\n/*\n * RemoveNodeFromCluster removes the provided node from the pg_dist_node table of\n * the master node and all nodes with metadata.\n * The call to the master_remove_node should be done by the super user. If there are\n * active shard placements on the node; the function errors out.\n * This function also deletes all reference table placements belong to the given node from\n * pg_dist_placement, but it does not drop actual placement at the node. It also\n * modifies replication factor of the colocation group of reference tables, so that\n * replication factor will be equal to worker count.\n */\nstatic void\nRemoveNodeFromCluster(char *nodeName, int32 nodePort)\n{\n\tWorkerNode *workerNode = ModifiableWorkerNode(nodeName, nodePort);\n\n\t/*\n\t * We do not allow metadata operations on secondary nodes in nontransactional\n\t * sync mode.\n\t */\n\tif (NodeIsSecondary(workerNode))\n\t{\n\t\tEnsureTransactionalMetadataSyncMode();\n\t}\n\n\tif (NodeIsPrimary(workerNode))\n\t{\n\t\tErrorIfNodeContainsNonRemovablePlacements(workerNode);\n\n\t\t/*\n\t\t * Delete reference table placements so they are not taken into account\n\t\t * for the check if there are placements after this.\n\t\t */\n\t\tbool localOnly = false;\n\t\tDeleteAllReplicatedTablePlacementsFromNodeGroup(workerNode->groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tlocalOnly);\n\n\t\t/*\n\t\t * Secondary nodes are read-only, never 2PC is used.\n\t\t * Hence, no items can be inserted to pg_dist_transaction\n\t\t * for secondary nodes.\n\t\t */\n\t\tDeleteWorkerTransactions(workerNode);\n\t}\n\n\tDeleteNodeRow(workerNode->workerName, nodePort);\n\n\t/* make sure we don't have any lingering session lifespan connections */\n\tCloseNodeConnectionsAfterTransaction(workerNode->workerName, nodePort);\n\n\tif (EnableMetadataSync)\n\t{\n\t\tchar *nodeDeleteCommand = NodeDeleteCommand(workerNode->nodeId);\n\n\t\tSendCommandToWorkersWithMetadata(nodeDeleteCommand);\n\t}\n}\n\n\n/*\n * ErrorIfNodeContainsNonRemovablePlacements throws an error if the input node\n * contains at least one placement on the node that is the last active\n * placement.\n */\nstatic void\nErrorIfNodeContainsNonRemovablePlacements(WorkerNode *workerNode)\n{\n\tint32 groupId = workerNode->groupId;\n\tList *shardPlacements = AllShardPlacementsOnNodeGroup(groupId);\n\n\t/* sort the list to prevent regression tests getting flaky */\n\tshardPlacements = SortList(shardPlacements, CompareGroupShardPlacements);\n\n\tGroupShardPlacement *placement = NULL;\n\tforeach_declared_ptr(placement, shardPlacements)\n\t{\n\t\tif (!PlacementHasActivePlacementOnAnotherGroup(placement))\n\t\t{\n\t\t\tOid relationId = RelationIdForShard(placement->shardId);\n\t\t\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\n\t\t\tereport(ERROR, (errmsg(\"cannot remove or disable the node \"\n\t\t\t\t\t\t\t\t   \"%s:%d because because it contains \"\n\t\t\t\t\t\t\t\t   \"the only shard placement for \"\n\t\t\t\t\t\t\t\t   \"shard \" UINT64_FORMAT,\n\t\t\t\t\t\t\t\t   workerNode->workerName,\n\t\t\t\t\t\t\t\t   workerNode->workerPort, placement->shardId),\n\t\t\t\t\t\t\terrdetail(\"One of the table(s) that prevents the operation \"\n\t\t\t\t\t\t\t\t\t  \"complete successfully is %s\",\n\t\t\t\t\t\t\t\t\t  qualifiedRelationName),\n\t\t\t\t\t\t\terrhint(\"To proceed, either drop the tables or use \"\n\t\t\t\t\t\t\t\t\t\"undistribute_table() function to convert \"\n\t\t\t\t\t\t\t\t\t\"them to local tables\")));\n\t\t}\n\t}\n}\n\n\n/*\n * PlacementHasActivePlacementOnAnotherGroup returns true if there is at least\n * one more active placement of the input sourcePlacement on another group.\n */\nstatic bool\nPlacementHasActivePlacementOnAnotherGroup(GroupShardPlacement *sourcePlacement)\n{\n\tuint64 shardId = sourcePlacement->shardId;\n\tList *activePlacementList = ActiveShardPlacementList(shardId);\n\n\tbool foundActivePlacementOnAnotherGroup = false;\n\tShardPlacement *activePlacement = NULL;\n\tforeach_declared_ptr(activePlacement, activePlacementList)\n\t{\n\t\tif (activePlacement->groupId != sourcePlacement->groupId)\n\t\t{\n\t\t\tfoundActivePlacementOnAnotherGroup = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn foundActivePlacementOnAnotherGroup;\n}\n\n\n/* CountPrimariesWithMetadata returns the number of primary nodes which have metadata. */\nuint32\nCountPrimariesWithMetadata(void)\n{\n\tuint32 primariesWithMetadata = 0;\n\tWorkerNode *workerNode = NULL;\n\n\tHASH_SEQ_STATUS status;\n\tHTAB *workerNodeHash = GetWorkerNodeHash();\n\n\thash_seq_init(&status, workerNodeHash);\n\n\twhile ((workerNode = hash_seq_search(&status)) != NULL)\n\t{\n\t\tif (workerNode->hasMetadata && NodeIsPrimary(workerNode))\n\t\t{\n\t\t\tprimariesWithMetadata++;\n\t\t}\n\t}\n\n\treturn primariesWithMetadata;\n}\n\n\n/*\n * AddNodeMetadata checks the given node information and adds the specified node to the\n * pg_dist_node table of the master and workers with metadata.\n * If the node already exists, the function returns the id of the node.\n * If not, the following procedure is followed while adding a node: If the groupId is not\n * explicitly given by the user, the function picks the group that the new node should\n * be in with respect to GroupSize. Then, the new node is inserted into the local\n * pg_dist_node as well as the nodes with hasmetadata=true if localOnly is false.\n */\nstatic int\nAddNodeMetadata(char *nodeName, int32 nodePort, NodeMetadata *nodeMetadata,\n\t\t\t\tbool *nodeAlreadyExists, bool localOnly)\n{\n\tEnsureCoordinator();\n\n\t*nodeAlreadyExists = false;\n\n\tWorkerNode *workerNode = FindWorkerNodeAnyCluster(nodeName, nodePort);\n\tif (workerNode != NULL)\n\t{\n\t\t/* return early without holding locks when the node already exists */\n\t\t*nodeAlreadyExists = true;\n\n\t\treturn workerNode->nodeId;\n\t}\n\n\t/*\n\t * We are going to change pg_dist_node, prevent any concurrent reads that\n\t * are not tolerant to concurrent node addition by taking an exclusive\n\t * lock (conflicts with all but AccessShareLock).\n\t *\n\t * We may want to relax or have more fine-grained locking in the future\n\t * to allow users to add multiple nodes concurrently.\n\t */\n\tLockRelationOid(DistNodeRelationId(), ExclusiveLock);\n\n\t/* recheck in case 2 node additions pass the first check concurrently */\n\tworkerNode = FindWorkerNodeAnyCluster(nodeName, nodePort);\n\tif (workerNode != NULL)\n\t{\n\t\t*nodeAlreadyExists = true;\n\n\t\treturn workerNode->nodeId;\n\t}\n\n\tif (nodeMetadata->groupId != COORDINATOR_GROUP_ID &&\n\t\tstrcmp(nodeName, \"localhost\") != 0)\n\t{\n\t\t/*\n\t\t * User tries to add a worker with a non-localhost address. If the coordinator\n\t\t * is added with \"localhost\" as well, the worker won't be able to connect.\n\t\t */\n\n\t\tbool isCoordinatorInMetadata = false;\n\t\tWorkerNode *coordinatorNode = PrimaryNodeForGroup(COORDINATOR_GROUP_ID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &isCoordinatorInMetadata);\n\t\tif (isCoordinatorInMetadata &&\n\t\t\tstrcmp(coordinatorNode->workerName, \"localhost\") == 0)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot add a worker node when the coordinator \"\n\t\t\t\t\t\t\t\t   \"hostname is set to localhost\"),\n\t\t\t\t\t\t\terrdetail(\"Worker nodes need to be able to connect to the \"\n\t\t\t\t\t\t\t\t\t  \"coordinator to transfer data.\"),\n\t\t\t\t\t\t\terrhint(\"Use SELECT citus_set_coordinator_host('<hostname>') \"\n\t\t\t\t\t\t\t\t\t\"to configure the coordinator hostname\")));\n\t\t}\n\t}\n\n\t/*\n\t * When adding the first worker when the coordinator has shard placements,\n\t * print a notice on how to drain the coordinator.\n\t */\n\tif (nodeMetadata->groupId != COORDINATOR_GROUP_ID && CoordinatorAddedAsWorkerNode() &&\n\t\tActivePrimaryNonCoordinatorNodeCount() == 0 &&\n\t\tNodeGroupHasShardPlacements(COORDINATOR_GROUP_ID))\n\t{\n\t\tWorkerNode *coordinator = CoordinatorNodeIfAddedAsWorkerOrError();\n\n\t\tereport(NOTICE, (errmsg(\"shards are still on the coordinator after adding the \"\n\t\t\t\t\t\t\t\t\"new node\"),\n\t\t\t\t\t\t errhint(\"Use SELECT rebalance_table_shards(); to balance \"\n\t\t\t\t\t\t\t\t \"shards data between workers and coordinator or \"\n\t\t\t\t\t\t\t\t \"SELECT citus_drain_node(%s,%d); to permanently \"\n\t\t\t\t\t\t\t\t \"move shards away from the coordinator.\",\n\t\t\t\t\t\t\t\t quote_literal_cstr(coordinator->workerName),\n\t\t\t\t\t\t\t\t coordinator->workerPort)));\n\t}\n\n\t/* user lets Citus to decide on the group that the newly added node should be in */\n\tif (nodeMetadata->groupId == INVALID_GROUP_ID)\n\t{\n\t\tnodeMetadata->groupId = GetNextGroupId();\n\t}\n\n\tif (nodeMetadata->groupId == COORDINATOR_GROUP_ID)\n\t{\n\t\t/*\n\t\t * Coordinator has always the authoritative metadata, reflect this\n\t\t * fact in the pg_dist_node.\n\t\t */\n\t\tnodeMetadata->hasMetadata = true;\n\t\tnodeMetadata->metadataSynced = true;\n\n\t\t/*\n\t\t * There is no concept of \"inactive\" coordinator, so hard code it.\n\t\t */\n\t\tnodeMetadata->isActive = true;\n\t}\n\n\t/* if nodeRole hasn't been added yet there's a constraint for one-node-per-group */\n\tif (nodeMetadata->nodeRole != InvalidOid && nodeMetadata->nodeRole ==\n\t\tPrimaryNodeRoleId())\n\t{\n\t\tWorkerNode *existingPrimaryNode = PrimaryNodeForGroup(nodeMetadata->groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL);\n\n\t\tif (existingPrimaryNode != NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"group %d already has a primary node\",\n\t\t\t\t\t\t\t\t   nodeMetadata->groupId)));\n\t\t}\n\t}\n\n\tif (nodeMetadata->nodeRole == PrimaryNodeRoleId())\n\t{\n\t\tif (strncmp(nodeMetadata->nodeCluster,\n\t\t\t\t\tWORKER_DEFAULT_CLUSTER,\n\t\t\t\t\tWORKER_LENGTH) != 0)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"primaries must be added to the default cluster\")));\n\t\t}\n\t}\n\n\t/* generate the new node id from the sequence */\n\tint nextNodeIdInt = GetNextNodeId();\n\n\tInsertNodeRow(nextNodeIdInt, nodeName, nodePort, nodeMetadata);\n\n\tworkerNode = FindWorkerNodeAnyCluster(nodeName, nodePort);\n\n\tif (EnableMetadataSync && !localOnly)\n\t{\n\t\t/* send the delete command to all primary nodes with metadata */\n\t\tchar *nodeDeleteCommand = NodeDeleteCommand(workerNode->nodeId);\n\t\tSendCommandToWorkersWithMetadata(nodeDeleteCommand);\n\n\t\t/* finally prepare the insert command and send it to all primary nodes */\n\t\tuint32 primariesWithMetadata = CountPrimariesWithMetadata();\n\t\tif (primariesWithMetadata != 0)\n\t\t{\n\t\t\tList *workerNodeList = list_make1(workerNode);\n\t\t\tchar *nodeInsertCommand = NodeListInsertCommand(workerNodeList);\n\n\t\t\tSendCommandToWorkersWithMetadata(nodeInsertCommand);\n\t\t}\n\t}\n\n\treturn workerNode->nodeId;\n}\n\n\n/*\n * AddNodeMetadataViaMetadataContext does the same thing as AddNodeMetadata but\n * make use of metadata sync context to send commands to workers to support both\n * transactional and nontransactional sync modes.\n */\nstatic int\nAddNodeMetadataViaMetadataContext(char *nodeName, int32 nodePort,\n\t\t\t\t\t\t\t\t  NodeMetadata *nodeMetadata, bool *nodeAlreadyExists)\n{\n\tbool localOnly = true;\n\tint nodeId = AddNodeMetadata(nodeName, nodePort, nodeMetadata, nodeAlreadyExists,\n\t\t\t\t\t\t\t\t localOnly);\n\n\t/* do nothing as the node already exists */\n\tif (*nodeAlreadyExists)\n\t{\n\t\treturn nodeId;\n\t}\n\n\t/*\n\t * Create metadata sync context that is used throughout node addition\n\t * and activation if necessary.\n\t */\n\tWorkerNode *node = ModifiableWorkerNode(nodeName, nodePort);\n\n\t/* we should always set active flag to true if we call citus_add_node */\n\tnode = SetWorkerColumnLocalOnly(node, Anum_pg_dist_node_isactive, DatumGetBool(true));\n\n\t/*\n\t * After adding new node, if the node did not already exist, we will activate\n\t * the node.\n\t * If the worker is not marked as a coordinator, check that\n\t * the node is not trying to add itself\n\t */\n\tif (node != NULL &&\n\t\tnode->groupId != COORDINATOR_GROUP_ID &&\n\t\tnode->nodeRole != SecondaryNodeRoleId() &&\n\t\tIsWorkerTheCurrentNode(node))\n\t{\n\t\tereport(ERROR, (errmsg(\"Node cannot add itself as a worker.\"),\n\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\"Add the node as a coordinator by using: \"\n\t\t\t\t\t\t\t\"SELECT citus_set_coordinator_host('%s', %d);\",\n\t\t\t\t\t\t\tnode->workerName, node->workerPort)));\n\t}\n\n\tList *nodeList = list_make1(node);\n\tbool collectCommands = false;\n\tbool nodesAddedInSameTransaction = true;\n\tMetadataSyncContext *context = CreateMetadataSyncContext(nodeList, collectCommands,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t nodesAddedInSameTransaction);\n\n\tif (EnableMetadataSync)\n\t{\n\t\t/* send the delete command to all primary nodes with metadata */\n\t\tchar *nodeDeleteCommand = NodeDeleteCommand(node->nodeId);\n\t\tSendOrCollectCommandListToMetadataNodes(context, list_make1(nodeDeleteCommand));\n\n\t\t/* finally prepare the insert command and send it to all primary nodes */\n\t\tuint32 primariesWithMetadata = CountPrimariesWithMetadata();\n\t\tif (primariesWithMetadata != 0)\n\t\t{\n\t\t\tchar *nodeInsertCommand = NULL;\n\t\t\tif (context->transactionMode == METADATA_SYNC_TRANSACTIONAL)\n\t\t\t{\n\t\t\t\tnodeInsertCommand = NodeListInsertCommand(nodeList);\n\t\t\t}\n\t\t\telse if (context->transactionMode == METADATA_SYNC_NON_TRANSACTIONAL)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We need to ensure node insertion is idempotent in nontransactional\n\t\t\t\t * sync mode.\n\t\t\t\t */\n\t\t\t\tnodeInsertCommand = NodeListIdempotentInsertCommand(nodeList);\n\t\t\t}\n\t\t\tAssert(nodeInsertCommand != NULL);\n\t\t\tSendOrCollectCommandListToMetadataNodes(context,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlist_make1(nodeInsertCommand));\n\t\t}\n\t}\n\n\tActivateNodeList(context);\n\n\treturn nodeId;\n}\n\n\n/*\n * SetWorkerColumn function sets the column with the specified index\n * on the worker in pg_dist_node, by calling SetWorkerColumnLocalOnly.\n * It also sends the same command for node update to other metadata nodes.\n * If anything fails during the transaction, we rollback it.\n * Returns the new worker node after the modification.\n */\nWorkerNode *\nSetWorkerColumn(WorkerNode *workerNode, int columnIndex, Datum value)\n{\n\tworkerNode = SetWorkerColumnLocalOnly(workerNode, columnIndex, value);\n\n\tif (EnableMetadataSync)\n\t{\n\t\tchar *metadataSyncCommand =\n\t\t\tGetMetadataSyncCommandToSetNodeColumn(workerNode, columnIndex, value);\n\n\t\tSendCommandToWorkersWithMetadata(metadataSyncCommand);\n\t}\n\n\treturn workerNode;\n}\n\n\n/*\n * SetNodeStateViaMetadataContext sets or unsets isactive, metadatasynced, and hasmetadata\n * flags via metadataSyncContext.\n */\nstatic void\nSetNodeStateViaMetadataContext(MetadataSyncContext *context, WorkerNode *workerNode,\n\t\t\t\t\t\t\t   Datum value)\n{\n\tchar *isActiveCommand =\n\t\tGetMetadataSyncCommandToSetNodeColumn(workerNode, Anum_pg_dist_node_isactive,\n\t\t\t\t\t\t\t\t\t\t\t  value);\n\tchar *metadatasyncedCommand =\n\t\tGetMetadataSyncCommandToSetNodeColumn(workerNode,\n\t\t\t\t\t\t\t\t\t\t\t  Anum_pg_dist_node_metadatasynced, value);\n\tchar *hasmetadataCommand =\n\t\tGetMetadataSyncCommandToSetNodeColumn(workerNode, Anum_pg_dist_node_hasmetadata,\n\t\t\t\t\t\t\t\t\t\t\t  value);\n\tList *commandList = list_make3(isActiveCommand, metadatasyncedCommand,\n\t\t\t\t\t\t\t\t   hasmetadataCommand);\n\n\tSendOrCollectCommandListToMetadataNodes(context, commandList);\n}\n\n\n/*\n * SetWorkerColumnOptional function sets the column with the specified index\n * on the worker in pg_dist_node, by calling SetWorkerColumnLocalOnly.\n * It also sends the same command optionally for node update to other metadata nodes,\n * meaning that failures are ignored. Returns the new worker node after the modification.\n */\nWorkerNode *\nSetWorkerColumnOptional(WorkerNode *workerNode, int columnIndex, Datum value)\n{\n\tchar *metadataSyncCommand = GetMetadataSyncCommandToSetNodeColumn(workerNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  columnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  value);\n\n\tList *workerNodeList = TargetWorkerSetNodeList(NON_COORDINATOR_METADATA_NODES,\n\t\t\t\t\t\t\t\t\t\t\t\t   ShareLock);\n\n\t/* open connections in parallel */\n\tWorkerNode *worker = NULL;\n\tforeach_declared_ptr(worker, workerNodeList)\n\t{\n\t\tbool success = SendOptionalMetadataCommandListToWorkerInCoordinatedTransaction(\n\t\t\tworker->workerName, worker->workerPort,\n\t\t\tCurrentUserName(),\n\t\t\tlist_make1(metadataSyncCommand));\n\n\t\tif (!success)\n\t\t{\n\t\t\t/* metadata out of sync, mark the worker as not synced */\n\t\t\tereport(WARNING, (errmsg(\"Updating the metadata of the node %s:%d \"\n\t\t\t\t\t\t\t\t\t \"is failed on node %s:%d. \"\n\t\t\t\t\t\t\t\t\t \"Metadata on %s:%d is marked as out of sync.\",\n\t\t\t\t\t\t\t\t\t workerNode->workerName, workerNode->workerPort,\n\t\t\t\t\t\t\t\t\t worker->workerName, worker->workerPort,\n\t\t\t\t\t\t\t\t\t worker->workerName, worker->workerPort)));\n\n\t\t\tSetWorkerColumnLocalOnly(worker, Anum_pg_dist_node_metadatasynced,\n\t\t\t\t\t\t\t\t\t BoolGetDatum(false));\n\t\t}\n\t\telse if (workerNode->nodeId == worker->nodeId)\n\t\t{\n\t\t\t/*\n\t\t\t * If this is the node we want to update and it is updated succesfully,\n\t\t\t * then we can safely update the flag on the coordinator as well.\n\t\t\t */\n\t\t\tSetWorkerColumnLocalOnly(workerNode, columnIndex, value);\n\t\t}\n\t}\n\n\treturn FindWorkerNode(workerNode->workerName, workerNode->workerPort);\n}\n\n\n/*\n * SetWorkerColumnLocalOnly function sets the column with the specified index\n * (see pg_dist_node.h) on the worker in pg_dist_node.\n * It returns the new worker node after the modification.\n */\nWorkerNode *\nSetWorkerColumnLocalOnly(WorkerNode *workerNode, int columnIndex, Datum value)\n{\n\tRelation pgDistNode = table_open(DistNodeRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistNode);\n\tHeapTuple heapTuple = GetNodeTuple(workerNode->workerName, workerNode->workerPort);\n\n\tDatum *values = palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isnull = palloc0(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tif (heapTuple == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for node \\\"%s:%d\\\"\",\n\t\t\t\t\t\t\t   workerNode->workerName, workerNode->workerPort)));\n\t}\n\n\tvalues[columnIndex - 1] = value;\n\tisnull[columnIndex - 1] = false;\n\treplace[columnIndex - 1] = true;\n\n\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isnull, replace);\n\n\tCatalogTupleUpdate(pgDistNode, &heapTuple->t_self, heapTuple);\n\n\tCitusInvalidateRelcacheByRelid(DistNodeRelationId());\n\tCommandCounterIncrement();\n\n\tWorkerNode *newWorkerNode = TupleToWorkerNode(pgDistNode, tupleDescriptor, heapTuple);\n\n\ttable_close(pgDistNode, NoLock);\n\n\tpfree(values);\n\tpfree(isnull);\n\tpfree(replace);\n\n\treturn newWorkerNode;\n}\n\n\n/*\n * GetMetadataSyncCommandToSetNodeColumn checks if the given workerNode and value is\n * valid or not. Then it returns the necessary metadata sync command as a string.\n */\nstatic char *\nGetMetadataSyncCommandToSetNodeColumn(WorkerNode *workerNode, int columnIndex, Datum\n\t\t\t\t\t\t\t\t\t  value)\n{\n\tchar *metadataSyncCommand = NULL;\n\n\tswitch (columnIndex)\n\t{\n\t\tcase Anum_pg_dist_node_hasmetadata:\n\t\t{\n\t\t\tErrorIfCoordinatorMetadataSetFalse(workerNode, value, \"hasmetadata\");\n\t\t\tmetadataSyncCommand = NodeHasmetadataUpdateCommand(workerNode->nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   DatumGetBool(value));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase Anum_pg_dist_node_isactive:\n\t\t{\n\t\t\tErrorIfCoordinatorMetadataSetFalse(workerNode, value, \"isactive\");\n\n\t\t\tmetadataSyncCommand = NodeStateUpdateCommand(workerNode->nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t DatumGetBool(value));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase Anum_pg_dist_node_shouldhaveshards:\n\t\t{\n\t\t\tmetadataSyncCommand = ShouldHaveShardsUpdateCommand(workerNode->nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tDatumGetBool(value));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase Anum_pg_dist_node_metadatasynced:\n\t\t{\n\t\t\tErrorIfCoordinatorMetadataSetFalse(workerNode, value, \"metadatasynced\");\n\t\t\tmetadataSyncCommand = NodeMetadataSyncedUpdateCommand(workerNode->nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  DatumGetBool(value));\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"could not find valid entry for node \\\"%s:%d\\\"\",\n\t\t\t\t\t\t\t\t   workerNode->workerName, workerNode->workerPort)));\n\t\t}\n\t}\n\n\treturn metadataSyncCommand;\n}\n\n\n/*\n * NodeHasmetadataUpdateCommand generates and returns a SQL UPDATE command\n * that updates the hasmetada column of pg_dist_node, for the given nodeid.\n */\nstatic char *\nNodeHasmetadataUpdateCommand(uint32 nodeId, bool hasMetadata)\n{\n\tStringInfo updateCommand = makeStringInfo();\n\tchar *hasMetadataString = hasMetadata ? \"TRUE\" : \"FALSE\";\n\tappendStringInfo(updateCommand,\n\t\t\t\t\t \"UPDATE pg_dist_node SET hasmetadata = %s \"\n\t\t\t\t\t \"WHERE nodeid = %u\",\n\t\t\t\t\t hasMetadataString, nodeId);\n\treturn updateCommand->data;\n}\n\n\n/*\n * NodeMetadataSyncedUpdateCommand generates and returns a SQL UPDATE command\n * that updates the metadataSynced column of pg_dist_node, for the given nodeid.\n */\nstatic char *\nNodeMetadataSyncedUpdateCommand(uint32 nodeId, bool metadataSynced)\n{\n\tStringInfo updateCommand = makeStringInfo();\n\tchar *hasMetadataString = metadataSynced ? \"TRUE\" : \"FALSE\";\n\tappendStringInfo(updateCommand,\n\t\t\t\t\t \"UPDATE pg_dist_node SET metadatasynced = %s \"\n\t\t\t\t\t \"WHERE nodeid = %u\",\n\t\t\t\t\t hasMetadataString, nodeId);\n\treturn updateCommand->data;\n}\n\n\n/*\n * ErrorIfCoordinatorMetadataSetFalse throws an error if the input node\n * is the coordinator and the value is false.\n */\nstatic void\nErrorIfCoordinatorMetadataSetFalse(WorkerNode *workerNode, Datum value, char *field)\n{\n\tbool valueBool = DatumGetBool(value);\n\tif (!valueBool && workerNode->groupId == COORDINATOR_GROUP_ID)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot change \\\"%s\\\" field of the \"\n\t\t\t\t\t\t\t   \"coordinator node\",\n\t\t\t\t\t\t\t   field)));\n\t}\n}\n\n\n/*\n * SetShouldHaveShards function sets the shouldhaveshards column of the\n * specified worker in pg_dist_node. also propagates this to other metadata nodes.\n * It returns the new worker node after the modification.\n */\nstatic WorkerNode *\nSetShouldHaveShards(WorkerNode *workerNode, bool shouldHaveShards)\n{\n\treturn SetWorkerColumn(workerNode, Anum_pg_dist_node_shouldhaveshards, BoolGetDatum(\n\t\t\t\t\t\t\t   shouldHaveShards));\n}\n\n\n/*\n * GetNodeTuple function returns the heap tuple of given nodeName and nodePort. If the\n * node is not found this function returns NULL.\n *\n * This function may return worker nodes from other clusters.\n */\nstatic HeapTuple\nGetNodeTuple(const char *nodeName, int32 nodePort)\n{\n\tRelation pgDistNode = table_open(DistNodeRelationId(), AccessShareLock);\n\tconst int scanKeyCount = 2;\n\tconst bool indexOK = false;\n\n\tScanKeyData scanKey[2];\n\tHeapTuple nodeTuple = NULL;\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_node_nodename,\n\t\t\t\tBTEqualStrategyNumber, F_TEXTEQ, CStringGetTextDatum(nodeName));\n\tScanKeyInit(&scanKey[1], Anum_pg_dist_node_nodeport,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(nodePort));\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistNode, InvalidOid, indexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tnodeTuple = heap_copytuple(heapTuple);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistNode, NoLock);\n\n\treturn nodeTuple;\n}\n\n\n/*\n * GetNodeByNodeId returns the heap tuple for given node id by looking up catalog.\n */\nstatic HeapTuple\nGetNodeByNodeId(int32 nodeId)\n{\n\tRelation pgDistNode = table_open(DistNodeRelationId(), AccessShareLock);\n\tconst int scanKeyCount = 1;\n\tconst bool indexOK = false;\n\n\tScanKeyData scanKey[1];\n\tHeapTuple nodeTuple = NULL;\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_node_nodeid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(nodeId));\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistNode, InvalidOid, indexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tnodeTuple = heap_copytuple(heapTuple);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for node id %d\", nodeId)));\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistNode, NoLock);\n\n\treturn nodeTuple;\n}\n\n\n/*\n * GetNextGroupId allocates and returns a unique groupId for the group\n * to be created. This allocation occurs both in shared memory and in write\n * ahead logs; writing to logs avoids the risk of having groupId collisions.\n *\n * Please note that the caller is still responsible for finalizing node data\n * and the groupId with the master node. Further note that this function relies\n * on an internal sequence created in initdb to generate unique identifiers.\n */\nint32\nGetNextGroupId()\n{\n\ttext *sequenceName = cstring_to_text(GROUPID_SEQUENCE_NAME);\n\tOid sequenceId = ResolveRelationId(sequenceName, false);\n\tDatum sequenceIdDatum = ObjectIdGetDatum(sequenceId);\n\tOid savedUserId = InvalidOid;\n\tint savedSecurityContext = 0;\n\n\tGetUserIdAndSecContext(&savedUserId, &savedSecurityContext);\n\tSetUserIdAndSecContext(CitusExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE);\n\n\t/* generate new and unique shardId from sequence */\n\tDatum groupIdDatum = DirectFunctionCall1(nextval_oid, sequenceIdDatum);\n\n\tSetUserIdAndSecContext(savedUserId, savedSecurityContext);\n\n\tint32 groupId = DatumGetInt32(groupIdDatum);\n\n\treturn groupId;\n}\n\n\n/*\n * GetNextNodeId allocates and returns a unique nodeId for the node\n * to be added. This allocation occurs both in shared memory and in write\n * ahead logs; writing to logs avoids the risk of having nodeId collisions.\n *\n * Please note that the caller is still responsible for finalizing node data\n * and the nodeId with the master node. Further note that this function relies\n * on an internal sequence created in initdb to generate unique identifiers.\n */\nint\nGetNextNodeId()\n{\n\ttext *sequenceName = cstring_to_text(NODEID_SEQUENCE_NAME);\n\tOid sequenceId = ResolveRelationId(sequenceName, false);\n\tDatum sequenceIdDatum = ObjectIdGetDatum(sequenceId);\n\tOid savedUserId = InvalidOid;\n\tint savedSecurityContext = 0;\n\n\tGetUserIdAndSecContext(&savedUserId, &savedSecurityContext);\n\tSetUserIdAndSecContext(CitusExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE);\n\n\t/* generate new and unique shardId from sequence */\n\tDatum nextNodeIdDatum = DirectFunctionCall1(nextval_oid, sequenceIdDatum);\n\n\tSetUserIdAndSecContext(savedUserId, savedSecurityContext);\n\n\tint nextNodeId = DatumGetUInt32(nextNodeIdDatum);\n\n\treturn nextNodeId;\n}\n\n\n/*\n * EnsureCoordinator checks if the current node is the coordinator. If it does not,\n * the function errors out.\n */\nvoid\nEnsureCoordinator(void)\n{\n\tint32 localGroupId = GetLocalGroupId();\n\n\tif (localGroupId != 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"operation is not allowed on this node\"),\n\t\t\t\t\t\terrhint(\"Connect to the coordinator and run it again.\")));\n\t}\n}\n\n\n/*\n * EnsurePropagationToCoordinator checks whether the coordinator is added to the\n * metadata if we're not on the coordinator.\n *\n * Given that metadata syncing skips syncing metadata to the coordinator, we need\n * too make sure that the coordinator is added to the metadata before propagating\n * a command from a worker. For this reason, today we use this only for the commands\n * that we support propagating from workers.\n */\nvoid\nEnsurePropagationToCoordinator(void)\n{\n\tif (!IsCoordinator())\n\t{\n\t\tEnsureCoordinatorIsInMetadata();\n\t}\n}\n\n\n/*\n * EnsureCoordinatorIsInMetadata checks whether the coordinator is added to the\n * metadata, which is required for many operations.\n */\nvoid\nEnsureCoordinatorIsInMetadata(void)\n{\n\tbool isCoordinatorInMetadata = false;\n\tPrimaryNodeForGroup(COORDINATOR_GROUP_ID, &isCoordinatorInMetadata);\n\tif (isCoordinatorInMetadata)\n\t{\n\t\treturn;\n\t}\n\n\t/* be more descriptive when we're not on coordinator */\n\tif (IsCoordinator())\n\t{\n\t\tereport(ERROR, (errmsg(\"coordinator is not added to the metadata\"),\n\t\t\t\t\t\terrhint(\"Use SELECT citus_set_coordinator_host('<hostname>') \"\n\t\t\t\t\t\t\t\t\"to configure the coordinator hostname\")));\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"coordinator is not added to the metadata\"),\n\t\t\t\t\t\terrhint(\"Use SELECT citus_set_coordinator_host('<hostname>') \"\n\t\t\t\t\t\t\t\t\"on coordinator to configure the coordinator hostname\")));\n\t}\n}\n\n\n/*\n * InsertCoordinatorIfClusterEmpty can be used to ensure Citus tables can be\n * created even on a node that has just performed CREATE EXTENSION citus;\n */\nvoid\nInsertCoordinatorIfClusterEmpty(void)\n{\n\t/* prevent concurrent node additions */\n\tRelation pgDistNode = table_open(DistNodeRelationId(), RowShareLock);\n\n\tif (!HasAnyNodes())\n\t{\n\t\t/*\n\t\t * create_distributed_table being called for the first time and there are\n\t\t * no pg_dist_node records. Add a record for the coordinator.\n\t\t */\n\t\tInsertPlaceholderCoordinatorRecord();\n\t}\n\n\t/*\n\t * We release the lock, if InsertPlaceholderCoordinatorRecord was called\n\t * we already have a strong (RowExclusive) lock.\n\t */\n\ttable_close(pgDistNode, RowShareLock);\n}\n\n\n/*\n * InsertPlaceholderCoordinatorRecord inserts a placeholder record for the coordinator\n * to be able to create distributed tables on a single node.\n */\nstatic void\nInsertPlaceholderCoordinatorRecord(void)\n{\n\tNodeMetadata nodeMetadata = DefaultNodeMetadata();\n\tnodeMetadata.groupId = 0;\n\tnodeMetadata.shouldHaveShards = true;\n\tnodeMetadata.nodeRole = PrimaryNodeRoleId();\n\tnodeMetadata.nodeCluster = \"default\";\n\n\tbool nodeAlreadyExists = false;\n\tbool localOnly = false;\n\n\t/* as long as there is a single node, localhost should be ok */\n\tAddNodeMetadata(LocalHostName, PostPortNumber, &nodeMetadata, &nodeAlreadyExists,\n\t\t\t\t\tlocalOnly);\n}\n\n\n/*\n * InsertNodeRow opens the node system catalog, and inserts a new row with the\n * given values into that system catalog.\n *\n * NOTE: If you call this function you probably need to have taken a\n * ShareRowExclusiveLock then checked that you're not adding a second primary to\n * an existing group. If you don't it's possible for the metadata to become inconsistent.\n */\nstatic void\nInsertNodeRow(int nodeid, char *nodeName, int32 nodePort, NodeMetadata *nodeMetadata)\n{\n\tRelation pgDistNode = table_open(DistNodeRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistNode);\n\n\tDatum *values = palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNulls = palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tDatum nodeClusterStringDatum = CStringGetDatum(nodeMetadata->nodeCluster);\n\tDatum nodeClusterNameDatum = DirectFunctionCall1(namein, nodeClusterStringDatum);\n\n\tvalues[Anum_pg_dist_node_nodeid - 1] = UInt32GetDatum(nodeid);\n\tvalues[Anum_pg_dist_node_groupid - 1] = Int32GetDatum(nodeMetadata->groupId);\n\tvalues[Anum_pg_dist_node_nodename - 1] = CStringGetTextDatum(nodeName);\n\tvalues[Anum_pg_dist_node_nodeport - 1] = UInt32GetDatum(nodePort);\n\tvalues[Anum_pg_dist_node_noderack - 1] = CStringGetTextDatum(nodeMetadata->nodeRack);\n\tvalues[Anum_pg_dist_node_hasmetadata - 1] = BoolGetDatum(nodeMetadata->hasMetadata);\n\tvalues[Anum_pg_dist_node_metadatasynced - 1] = BoolGetDatum(\n\t\tnodeMetadata->metadataSynced);\n\tvalues[Anum_pg_dist_node_isactive - 1] = BoolGetDatum(nodeMetadata->isActive);\n\tvalues[Anum_pg_dist_node_noderole - 1] = ObjectIdGetDatum(nodeMetadata->nodeRole);\n\tvalues[Anum_pg_dist_node_nodecluster - 1] = nodeClusterNameDatum;\n\tvalues[Anum_pg_dist_node_shouldhaveshards - 1] = BoolGetDatum(\n\t\tnodeMetadata->shouldHaveShards);\n\tvalues[GetNodeIsCloneAttrIndexInPgDistNode(tupleDescriptor)] =\n\t\tBoolGetDatum(nodeMetadata->nodeisclone);\n\tvalues[GetNodePrimaryNodeIdAttrIndexInPgDistNode(tupleDescriptor)] =\n\t\tInt32GetDatum(nodeMetadata->nodeprimarynodeid);\n\tHeapTuple heapTuple = heap_form_tuple(tupleDescriptor, values, isNulls);\n\n\tPushActiveSnapshot(GetTransactionSnapshot());\n\tCatalogTupleInsert(pgDistNode, heapTuple);\n\tPopActiveSnapshot();\n\n\tCitusInvalidateRelcacheByRelid(DistNodeRelationId());\n\n\t/* increment the counter so that next command can see the row */\n\tCommandCounterIncrement();\n\n\t/* close relation */\n\ttable_close(pgDistNode, NoLock);\n\n\tpfree(values);\n\tpfree(isNulls);\n}\n\n\n/*\n * DeleteNodeRow removes the requested row from pg_dist_node table if it exists.\n */\nstatic void\nDeleteNodeRow(char *nodeName, int32 nodePort)\n{\n\tconst int scanKeyCount = 2;\n\tbool indexOK = false;\n\n\tScanKeyData scanKey[2];\n\tRelation pgDistNode = table_open(DistNodeRelationId(), RowExclusiveLock);\n\n\t/*\n\t * simple_heap_delete() expects that the caller has at least an\n\t * AccessShareLock on primary key index.\n\t *\n\t * XXX: This does not seem required, do we really need to acquire this lock?\n\t * Postgres doesn't acquire such locks on indexes before deleting catalog tuples.\n\t * Linking here the reasons we added this lock acquirement:\n\t * https://github.com/citusdata/citus/pull/2851#discussion_r306569462\n\t * https://github.com/citusdata/citus/pull/2855#discussion_r313628554\n\t * https://github.com/citusdata/citus/issues/1890\n\t */\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t/* PG 18+ adds a bool “deferrable_ok” parameter */\n\tRelation replicaIndex =\n\t\tindex_open(RelationGetPrimaryKeyIndex(pgDistNode, false),\n\t\t\t\t   AccessShareLock);\n#else\n\tRelation replicaIndex =\n\t\tindex_open(RelationGetPrimaryKeyIndex(pgDistNode),\n\t\t\t\t   AccessShareLock);\n#endif\n\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_node_nodename,\n\t\t\t\tBTEqualStrategyNumber, F_TEXTEQ, CStringGetTextDatum(nodeName));\n\tScanKeyInit(&scanKey[1], Anum_pg_dist_node_nodeport,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(nodePort));\n\n\tSysScanDesc heapScan = systable_beginscan(pgDistNode, InvalidOid, indexOK,\n\t\t\t\t\t\t\t\t\t\t\t  NULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(heapScan);\n\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for node \\\"%s:%d\\\"\",\n\t\t\t\t\t\t\t   nodeName, nodePort)));\n\t}\n\n\tsimple_heap_delete(pgDistNode, &(heapTuple->t_self));\n\n\tsystable_endscan(heapScan);\n\n\t/* ensure future commands don't use the node we just removed */\n\tCitusInvalidateRelcacheByRelid(DistNodeRelationId());\n\n\t/* increment the counter so that next command won't see the row */\n\tCommandCounterIncrement();\n\n\ttable_close(replicaIndex, AccessShareLock);\n\ttable_close(pgDistNode, NoLock);\n}\n\n\n/*\n * TupleToWorkerNode takes in a heap tuple from pg_dist_node, and\n * converts this tuple to an equivalent struct in memory. The function assumes\n * the caller already has locks on the tuple, and doesn't perform any locking.\n */\nstatic WorkerNode *\nTupleToWorkerNode(Relation pgDistNode, TupleDesc tupleDescriptor, HeapTuple heapTuple)\n{\n\t/* we add remove columns from pg_dist_node during extension upgrade and\n\t * and downgrads. Now the issue here is PostgreSQL never reuses the old\n\t * attnum. Dropped columns leave “holes” (attributes with attisdropped = true),\n\t * and a re-added column with the same name gets a new attnum at the end. So\n\t * we cannot use the deined Natts_pg_dist_node to allocate memory and also\n\t * we need to cater for the holes when fetching the column values\n\t */\n\tint nAtts = tupleDescriptor->natts;\n\tDatum *datumArray = palloc0(sizeof(Datum) * nAtts);\n\tbool *isNullArray = palloc0(sizeof(bool) * nAtts);\n\n\t/*\n\t * We use heap_deform_tuple() instead of heap_getattr() to expand tuple\n\t * to contain missing values when ALTER TABLE ADD COLUMN happens.\n\t */\n\theap_deform_tuple(heapTuple, tupleDescriptor, datumArray, isNullArray);\n\n\tchar *nodeName = TextDatumGetCString(datumArray[Anum_pg_dist_node_nodename - 1]);\n\tchar *nodeRack = TextDatumGetCString(datumArray[Anum_pg_dist_node_noderack - 1]);\n\n\tWorkerNode *workerNode = (WorkerNode *) palloc0(sizeof(WorkerNode));\n\tworkerNode->nodeId = DatumGetUInt32(datumArray[Anum_pg_dist_node_nodeid - 1]);\n\tworkerNode->workerPort = DatumGetUInt32(datumArray[Anum_pg_dist_node_nodeport - 1]);\n\tworkerNode->groupId = DatumGetInt32(datumArray[Anum_pg_dist_node_groupid - 1]);\n\tstrlcpy(workerNode->workerName, nodeName, WORKER_LENGTH);\n\tstrlcpy(workerNode->workerRack, nodeRack, WORKER_LENGTH);\n\tworkerNode->hasMetadata = DatumGetBool(datumArray[Anum_pg_dist_node_hasmetadata - 1]);\n\tworkerNode->metadataSynced =\n\t\tDatumGetBool(datumArray[Anum_pg_dist_node_metadatasynced - 1]);\n\tworkerNode->isActive = DatumGetBool(datumArray[Anum_pg_dist_node_isactive - 1]);\n\tworkerNode->nodeRole = DatumGetObjectId(datumArray[Anum_pg_dist_node_noderole - 1]);\n\tworkerNode->shouldHaveShards = DatumGetBool(\n\t\tdatumArray[Anum_pg_dist_node_shouldhaveshards -\n\t\t\t\t   1]);\n\n\t/*\n\t * nodecluster, nodeisclone and nodeprimarynodeid columns can be missing. In case\n\t * of extension creation/upgrade, master_initialize_node_metadata function is\n\t * called before the nodecluster column is added to pg_dist_node table.\n\t */\n\n\tif (!isNullArray[Anum_pg_dist_node_nodecluster - 1])\n\t{\n\t\tName nodeClusterName =\n\t\t\tDatumGetName(datumArray[Anum_pg_dist_node_nodecluster - 1]);\n\t\tchar *nodeClusterString = NameStr(*nodeClusterName);\n\t\tstrlcpy(workerNode->nodeCluster, nodeClusterString, NAMEDATALEN);\n\t}\n\n\tint nodeIsCloneIdx = GetNodeIsCloneAttrIndexInPgDistNode(tupleDescriptor);\n\tint nodePrimaryNodeIdIdx = GetNodePrimaryNodeIdAttrIndexInPgDistNode(tupleDescriptor);\n\n\tif (!isNullArray[nodeIsCloneIdx])\n\t{\n\t\tworkerNode->nodeisclone = DatumGetBool(datumArray[nodeIsCloneIdx]);\n\t}\n\n\tif (!isNullArray[nodePrimaryNodeIdIdx])\n\t{\n\t\tworkerNode->nodeprimarynodeid = DatumGetInt32(datumArray[nodePrimaryNodeIdIdx]);\n\t}\n\n\tpfree(datumArray);\n\tpfree(isNullArray);\n\n\treturn workerNode;\n}\n\n\n/*\n * GetNodePrimaryNodeIdAttrIndexInPgDistNode returns attrnum for nodeprimarynodeid attr.\n *\n * nodeprimarynodeid attr was added to table pg_dist_node using alter operation\n * after the version where Citus started supporting downgrades, and it's one of\n * the two columns that we've introduced to pg_dist_node since then.\n *\n * And in case of a downgrade + upgrade, tupleDesc->natts becomes greater than\n * Natts_pg_dist_node and when this happens, then we know that attrnum\n * nodeprimarynodeid is not Anum_pg_dist_node_nodeprimarynodeid anymore but\n * tupleDesc->natts - 1.\n */\nstatic int\nGetNodePrimaryNodeIdAttrIndexInPgDistNode(TupleDesc tupleDesc)\n{\n\treturn tupleDesc->natts == Natts_pg_dist_node\n\t\t   ? (Anum_pg_dist_node_nodeprimarynodeid - 1)\n\t\t   : tupleDesc->natts - 1;\n}\n\n\n/*\n * GetNodeIsCloneAttrIndexInPgDistNode returns attrnum for nodeisclone attr.\n *\n * Like, GetNodePrimaryNodeIdAttrIndexInPgDistNode(), performs a similar\n * calculation for nodeisclone attribute because this is column added to\n * pg_dist_node after we started supporting downgrades.\n *\n * Only difference with the mentioned function is that we know\n * the attrnum for nodeisclone is not Anum_pg_dist_node_nodeisclone anymore\n * but tupleDesc->natts - 2 because we added these columns consecutively\n * and we first add nodeisclone attribute and then nodeprimarynodeid attribute.\n */\nstatic int\nGetNodeIsCloneAttrIndexInPgDistNode(TupleDesc tupleDesc)\n{\n\treturn tupleDesc->natts == Natts_pg_dist_node\n\t\t   ? (Anum_pg_dist_node_nodeisclone - 1)\n\t\t   : tupleDesc->natts - 2;\n}\n\n\n/*\n * StringToDatum transforms a string representation into a Datum.\n */\nDatum\nStringToDatum(char *inputString, Oid dataType)\n{\n\tOid typIoFunc = InvalidOid;\n\tOid typIoParam = InvalidOid;\n\tint32 typeModifier = -1;\n\n\tgetTypeInputInfo(dataType, &typIoFunc, &typIoParam);\n\tgetBaseTypeAndTypmod(dataType, &typeModifier);\n\n\tDatum datum = OidInputFunctionCall(typIoFunc, inputString, typIoParam, typeModifier);\n\n\treturn datum;\n}\n\n\n/*\n * DatumToString returns the string representation of the given datum.\n */\nchar *\nDatumToString(Datum datum, Oid dataType)\n{\n\tOid typIoFunc = InvalidOid;\n\tbool typIsVarlena = false;\n\n\tgetTypeOutputInfo(dataType, &typIoFunc, &typIsVarlena);\n\tchar *outputString = OidOutputFunctionCall(typIoFunc, datum);\n\n\treturn outputString;\n}\n\n\n/*\n * UnsetMetadataSyncedForAllWorkers sets the metadatasynced column of all metadata\n * worker nodes to false. It returns true if it updated at least a node.\n */\nstatic bool\nUnsetMetadataSyncedForAllWorkers(void)\n{\n\tbool updatedAtLeastOne = false;\n\tScanKeyData scanKey[3];\n\tint scanKeyCount = 3;\n\tbool indexOK = false;\n\n\t/*\n\t * Concurrent citus_update_node() calls might iterate and try to update\n\t * pg_dist_node in different orders. To protect against deadlock, we\n\t * get an exclusive lock here.\n\t */\n\tRelation relation = table_open(DistNodeRelationId(), ExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_node_hasmetadata,\n\t\t\t\tBTEqualStrategyNumber, F_BOOLEQ, BoolGetDatum(true));\n\tScanKeyInit(&scanKey[1], Anum_pg_dist_node_metadatasynced,\n\t\t\t\tBTEqualStrategyNumber, F_BOOLEQ, BoolGetDatum(true));\n\n\t/* coordinator always has the up to date metadata */\n\tScanKeyInit(&scanKey[2], Anum_pg_dist_node_groupid,\n\t\t\t\tBTGreaterStrategyNumber, F_INT4GT,\n\t\t\t\tInt32GetDatum(COORDINATOR_GROUP_ID));\n\n\tCatalogIndexState indstate = CatalogOpenIndexes(relation);\n\n\tSysScanDesc scanDescriptor = systable_beginscan(relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid, indexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tupdatedAtLeastOne = true;\n\t}\n\n\tDatum *values = palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isnull = palloc(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = palloc(tupleDescriptor->natts * sizeof(bool));\n\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tmemset(values, 0, tupleDescriptor->natts * sizeof(Datum));\n\t\tmemset(isnull, 0, tupleDescriptor->natts * sizeof(bool));\n\t\tmemset(replace, 0, tupleDescriptor->natts * sizeof(bool));\n\n\t\tvalues[Anum_pg_dist_node_metadatasynced - 1] = BoolGetDatum(false);\n\t\treplace[Anum_pg_dist_node_metadatasynced - 1] = true;\n\n\t\tHeapTuple newHeapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values,\n\t\t\t\t\t\t\t\t\t\t\t\t   isnull,\n\t\t\t\t\t\t\t\t\t\t\t\t   replace);\n\n\t\tCatalogTupleUpdateWithInfo(relation, &newHeapTuple->t_self, newHeapTuple,\n\t\t\t\t\t\t\t\t   indstate);\n\n\t\tCommandCounterIncrement();\n\n\t\theap_freetuple(newHeapTuple);\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\tCatalogCloseIndexes(indstate);\n\ttable_close(relation, NoLock);\n\n\tpfree(values);\n\tpfree(isnull);\n\tpfree(replace);\n\n\treturn updatedAtLeastOne;\n}\n\n\n/*\n * ErrorIfAnyNodeNotExist errors if any node in given list not found.\n */\nstatic void\nErrorIfAnyNodeNotExist(List *nodeList)\n{\n\tWorkerNode *node = NULL;\n\tforeach_declared_ptr(node, nodeList)\n\t{\n\t\t/*\n\t\t * First, locally mark the node is active, if everything goes well,\n\t\t * we are going to sync this information to all the metadata nodes.\n\t\t */\n\t\tWorkerNode *workerNode =\n\t\t\tFindWorkerNodeAnyCluster(node->workerName, node->workerPort);\n\t\tif (workerNode == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"node at \\\"%s:%u\\\" does not exist\", node->workerName,\n\t\t\t\t\t\t\t\t   node->workerPort)));\n\t\t}\n\t}\n}\n\n\n/*\n * UpdateLocalGroupIdsViaMetadataContext updates local group ids for given list\n * of nodes with transactional or nontransactional mode according to transactionMode\n * inside metadataSyncContext.\n */\nstatic void\nUpdateLocalGroupIdsViaMetadataContext(MetadataSyncContext *context)\n{\n\tint activatedPrimaryCount = list_length(context->activatedWorkerNodeList);\n\tint nodeIdx = 0;\n\tfor (nodeIdx = 0; nodeIdx < activatedPrimaryCount; nodeIdx++)\n\t{\n\t\tWorkerNode *node = list_nth(context->activatedWorkerNodeList, nodeIdx);\n\t\tList *commandList = list_make1(LocalGroupIdUpdateCommand(node->groupId));\n\n\t\t/* send commands to new workers, the current user should be a superuser */\n\t\tAssert(superuser());\n\n\t\tSendOrCollectCommandListToSingleNode(context, commandList, nodeIdx);\n\t}\n}\n\n\n/*\n * SendDeletionCommandsForReplicatedTablePlacements sends commands to delete replicated\n * placement for the metadata nodes with transactional or nontransactional mode according\n * to transactionMode inside metadataSyncContext.\n */\nstatic void\nSendDeletionCommandsForReplicatedTablePlacements(MetadataSyncContext *context)\n{\n\tWorkerNode *node = NULL;\n\tforeach_declared_ptr(node, context->activatedWorkerNodeList)\n\t{\n\t\tif (!node->isActive)\n\t\t{\n\t\t\tbool localOnly = false;\n\t\t\tint32 groupId = node->groupId;\n\t\t\tDeleteAllReplicatedTablePlacementsFromNodeGroupViaMetadataContext(context,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  localOnly);\n\t\t}\n\t}\n}\n\n\n/*\n * SyncNodeMetadata syncs node metadata with transactional or nontransactional\n * mode according to transactionMode inside metadataSyncContext.\n */\nstatic void\nSyncNodeMetadata(MetadataSyncContext *context)\n{\n\tCheckCitusVersion(ERROR);\n\n\tif (!EnableMetadataSync)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * Do not fail when we call this method from activate_node_snapshot\n\t * from workers.\n\t */\n\tif (!MetadataSyncCollectsCommands(context))\n\t{\n\t\tEnsureCoordinator();\n\t}\n\n\tEnsureModificationsCanRun();\n\tEnsureSequentialModeMetadataOperations();\n\n\tLockRelationOid(DistNodeRelationId(), ExclusiveLock);\n\n\t/* generate the queries which drop the node metadata */\n\tList *dropMetadataCommandList = NodeMetadataDropCommands();\n\n\t/* generate the queries which create the node metadata from scratch */\n\tList *createMetadataCommandList = NodeMetadataCreateCommands();\n\n\tList *recreateNodeSnapshotCommandList = dropMetadataCommandList;\n\trecreateNodeSnapshotCommandList = list_concat(recreateNodeSnapshotCommandList,\n\t\t\t\t\t\t\t\t\t\t\t\t  createMetadataCommandList);\n\n\t/*\n\t * We should have already added node metadata to metadata workers. Sync node\n\t * metadata just for activated workers.\n\t */\n\tSendOrCollectCommandListToActivatedNodes(context, recreateNodeSnapshotCommandList);\n}\n"
  },
  {
    "path": "src/backend/distributed/metadata/pg_get_object_address_16_17_18.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_get_object_address_16_17_18.c\n *\n * Copied functions from Postgres pg_get_object_address with acl/owner check.\n * Since we need to use intermediate data types Relation and Node from\n * the pg_get_object_address, we've copied that function from PG code and\n * added required owner/acl checks for our own purposes.\n *\n * We need to make sure that function works with future PG versions. Update\n * the function name according to supported PG versions as well.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/objectaddress.h\"\n#include \"catalog/pg_type.h\"\n#include \"mb/pg_wchar.h\"\n#include \"nodes/value.h\"\n#include \"parser/parse_type.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/varlena.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/version_compat.h\"\n\nstatic void ErrorIfCurrentUserCanNotDistributeObject(char *textType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t ObjectType type,\n\t\t\t\t\t\t\t\t\t\t\t\t\t ObjectAddress *addr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t Relation *relation);\nstatic List * textarray_to_strvaluelist(ArrayType *arr);\n\n/*\n * PgGetObjectAddress gets the object address. This function is mostly copied from\n * pg_get_object_address of the PG code. We need to copy that function to use\n * intermediate data types Relation and Node to check acl or ownership.\n *\n * Codes added by Citus are tagged with CITUS CODE BEGIN/END.\n */\nObjectAddress\nPgGetObjectAddress(char *ttype, ArrayType *namearr, ArrayType *argsarr)\n{\n\tList *name = NIL;\n\tTypeName *typename = NULL;\n\tList *args = NIL;\n\tNode *objnode = NULL;\n\tRelation relation;\n\n\t/* Decode object type, raise error if unknown */\n\tint itype = read_objtype_from_string(ttype);\n\tif (itype < 0)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t errmsg(\"unsupported object type \\\"%s\\\"\", ttype)));\n\t}\n\tObjectType type = (ObjectType) itype;\n\n\t/*\n\t * Convert the text array to the representation appropriate for the given\n\t * object type.  Most use a simple string Values list, but there are some\n\t * exceptions.\n\t */\n\tif (type == OBJECT_TYPE || type == OBJECT_DOMAIN || type == OBJECT_CAST ||\n\t\ttype == OBJECT_TRANSFORM || type == OBJECT_DOMCONSTRAINT)\n\t{\n\t\tDatum *elems;\n\t\tbool *nulls;\n\t\tint nelems;\n\n\t\tdeconstruct_array(namearr, TEXTOID, -1, false, TYPALIGN_INT,\n\t\t\t\t\t\t  &elems, &nulls, &nelems);\n\t\tif (nelems != 1)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t errmsg(\"name list length must be exactly %d\", 1)));\n\t\t}\n\t\tif (nulls[0])\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t errmsg(\"name or argument lists may not contain nulls\")));\n\t\t}\n\t\ttypename = typeStringToTypeName(TextDatumGetCString(elems[0]), NULL);\n\t}\n\telse if (type == OBJECT_LARGEOBJECT)\n\t{\n\t\tDatum *elems;\n\t\tbool *nulls;\n\t\tint nelems;\n\n\t\tdeconstruct_array(namearr, TEXTOID, -1, false, TYPALIGN_INT,\n\t\t\t\t\t\t  &elems, &nulls, &nelems);\n\t\tif (nelems != 1)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t errmsg(\"name list length must be exactly %d\", 1)));\n\t\t}\n\t\tif (nulls[0])\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t errmsg(\"large object OID may not be null\")));\n\t\t}\n\t\tobjnode = (Node *) makeFloat(TextDatumGetCString(elems[0]));\n\t}\n\telse\n\t{\n\t\tname = textarray_to_strvaluelist(namearr);\n\t\tif (list_length(name) < 1)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t errmsg(\"name list length must be at least %d\", 1)));\n\t\t}\n\t}\n\n\t/*\n\t * If args are given, decode them according to the object type.\n\t */\n\tif (type == OBJECT_AGGREGATE ||\n\t\ttype == OBJECT_FUNCTION ||\n\t\ttype == OBJECT_PROCEDURE ||\n\t\ttype == OBJECT_ROUTINE ||\n\t\ttype == OBJECT_OPERATOR ||\n\t\ttype == OBJECT_CAST ||\n\t\ttype == OBJECT_AMOP ||\n\t\ttype == OBJECT_AMPROC)\n\t{\n\t\t/* in these cases, the args list must be of TypeName */\n\t\tDatum *elems;\n\t\tbool *nulls;\n\t\tint nelems;\n\t\tint i;\n\n\t\tdeconstruct_array(argsarr, TEXTOID, -1, false,\n\t\t\t\t\t\t  TYPALIGN_INT,\n\t\t\t\t\t\t  &elems, &nulls, &nelems);\n\n\t\targs = NIL;\n\t\tfor (i = 0; i < nelems; i++)\n\t\t{\n\t\t\tif (nulls[i])\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t errmsg(\"name or argument lists may not contain nulls\")));\n\t\t\t}\n\t\t\targs = lappend(args,\n\t\t\t\t\t\t   typeStringToTypeName(TextDatumGetCString(elems[i]),\n\t\t\t\t\t\t\t\t\t\t\t\tNULL));\n\t\t}\n\t}\n\telse\n\t{\n\t\t/* For all other object types, use string Values */\n\t\targs = textarray_to_strvaluelist(argsarr);\n\t}\n\n\t/*\n\t * get_object_address is pretty sensitive to the length of its input\n\t * lists; check that they're what it wants.\n\t */\n\tswitch (type)\n\t{\n\t\tcase OBJECT_DOMCONSTRAINT:\n\t\tcase OBJECT_CAST:\n\t\tcase OBJECT_USER_MAPPING:\n\t\tcase OBJECT_PUBLICATION_REL:\n\t\tcase OBJECT_DEFACL:\n\t\tcase OBJECT_TRANSFORM:\n\t\t{\n\t\t\tif (list_length(args) != 1)\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t errmsg(\"argument list length must be exactly %d\", 1)));\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_OPFAMILY:\n\t\tcase OBJECT_OPCLASS:\n\t\t{\n\t\t\tif (list_length(name) < 2)\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t errmsg(\"name list length must be at least %d\", 2)));\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_AMOP:\n\t\tcase OBJECT_AMPROC:\n\t\t{\n\t\t\tif (list_length(name) < 3)\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t errmsg(\"name list length must be at least %d\", 3)));\n\t\t\t}\n\n\t\t\tif (list_length(args) != 2)\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t errmsg(\"argument list length must be exactly %d\", 2)));\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_OPERATOR:\n\t\t{\n\t\t\tif (list_length(args) != 2)\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t errmsg(\"argument list length must be exactly %d\", 2)));\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/*\n\t * Now build the Node type that get_object_address() expects for the given\n\t * type.\n\t */\n\tswitch (type)\n\t{\n\t\tcase OBJECT_TABLE:\n\t\tcase OBJECT_SEQUENCE:\n\t\tcase OBJECT_VIEW:\n\t\tcase OBJECT_MATVIEW:\n\t\tcase OBJECT_INDEX:\n\t\tcase OBJECT_FOREIGN_TABLE:\n\t\tcase OBJECT_COLUMN:\n\t\tcase OBJECT_ATTRIBUTE:\n\t\tcase OBJECT_COLLATION:\n\t\tcase OBJECT_CONVERSION:\n\t\tcase OBJECT_STATISTIC_EXT:\n\t\tcase OBJECT_TSPARSER:\n\t\tcase OBJECT_TSDICTIONARY:\n\t\tcase OBJECT_TSTEMPLATE:\n\t\tcase OBJECT_TSCONFIGURATION:\n\t\tcase OBJECT_DEFAULT:\n\t\tcase OBJECT_POLICY:\n\t\tcase OBJECT_RULE:\n\t\tcase OBJECT_TRIGGER:\n\t\tcase OBJECT_TABCONSTRAINT:\n\t\tcase OBJECT_OPCLASS:\n\t\tcase OBJECT_OPFAMILY:\n\t\t{\n\t\t\tobjnode = (Node *) name;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_ACCESS_METHOD:\n\t\tcase OBJECT_DATABASE:\n\t\tcase OBJECT_EVENT_TRIGGER:\n\t\tcase OBJECT_EXTENSION:\n\t\tcase OBJECT_FDW:\n\t\tcase OBJECT_FOREIGN_SERVER:\n\t\tcase OBJECT_LANGUAGE:\n\t\tcase OBJECT_PARAMETER_ACL:\n\t\tcase OBJECT_PUBLICATION:\n\t\tcase OBJECT_ROLE:\n\t\tcase OBJECT_SCHEMA:\n\t\tcase OBJECT_SUBSCRIPTION:\n\t\tcase OBJECT_TABLESPACE:\n\t\t{\n\t\t\tif (list_length(name) != 1)\n\t\t\t{\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t errmsg(\"name list length must be exactly %d\", 1)));\n\t\t\t}\n\t\t\tobjnode = linitial(name);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_TYPE:\n\t\tcase OBJECT_DOMAIN:\n\t\t{\n\t\t\tobjnode = (Node *) typename;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_CAST:\n\t\tcase OBJECT_DOMCONSTRAINT:\n\t\tcase OBJECT_TRANSFORM:\n\t\t{\n\t\t\tobjnode = (Node *) list_make2(typename, linitial(args));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_PUBLICATION_REL:\n\t\t{\n\t\t\tobjnode = (Node *) list_make2(name, linitial(args));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_PUBLICATION_NAMESPACE:\n\t\tcase OBJECT_USER_MAPPING:\n\t\t{\n\t\t\tobjnode = (Node *) list_make2(linitial(name), linitial(args));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_DEFACL:\n\t\t{\n\t\t\tobjnode = (Node *) lcons(linitial(args), name);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_AMOP:\n\t\tcase OBJECT_AMPROC:\n\t\t{\n\t\t\tobjnode = (Node *) list_make2(name, args);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_FUNCTION:\n\t\tcase OBJECT_PROCEDURE:\n\t\tcase OBJECT_ROUTINE:\n\t\tcase OBJECT_AGGREGATE:\n\t\tcase OBJECT_OPERATOR:\n\t\t{\n\t\t\tObjectWithArgs *owa = makeNode(ObjectWithArgs);\n\n\t\t\towa->objname = name;\n\t\t\towa->objargs = args;\n\t\t\tobjnode = (Node *) owa;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBJECT_LARGEOBJECT:\n\t\t{\n\t\t\t/* already handled above */\n\t\t\tbreak;\n\t\t}\n\n\t\t\t/* no default, to let compiler warn about missing case */\n\t}\n\n\tif (objnode == NULL)\n\t{\n\t\telog(ERROR, \"unrecognized object type: %d\", type);\n\t}\n\n\tObjectAddress addr = get_object_address(type, objnode,\n\t\t\t\t\t\t\t\t\t\t\t&relation, AccessShareLock, false);\n\n\t/* CITUS CODE BEGIN */\n\tErrorIfCurrentUserCanNotDistributeObject(ttype, type, &addr, objnode, &relation);\n\n\t/* CITUS CODE END */\n\n\t/* We don't need the relcache entry, thank you very much */\n\tif (relation)\n\t{\n\t\trelation_close(relation, AccessShareLock);\n\t}\n\n\t/* CITUS CODE BEGIN */\n\treturn addr;\n\n\t/* CITUS CODE END */\n}\n\n\n/*\n * ErrorIfCurrentUserCanNotDistributeObject checks whether current user can\n * distribute object, if not errors out.\n */\nstatic void\nErrorIfCurrentUserCanNotDistributeObject(char *textType, ObjectType type,\n\t\t\t\t\t\t\t\t\t\t ObjectAddress *addr, Node *node,\n\t\t\t\t\t\t\t\t\t\t Relation *relation)\n{\n\tOid userId = GetUserId();\n\n\tif (!SupportedDependencyByCitus(addr))\n\t{\n\t\tereport(ERROR, (errmsg(\"%s object can not be distributed by Citus\", textType),\n\t\t\t\t\t\terrdetail(\"Object type id is %d\", type)));\n\t}\n\n\tswitch (type)\n\t{\n\t\tcase OBJECT_SCHEMA:\n\t\tcase OBJECT_DATABASE:\n\t\tcase OBJECT_FUNCTION:\n\t\tcase OBJECT_PROCEDURE:\n\t\tcase OBJECT_AGGREGATE:\n\t\tcase OBJECT_TSCONFIGURATION:\n\t\tcase OBJECT_TSDICTIONARY:\n\t\tcase OBJECT_TYPE:\n\t\tcase OBJECT_FOREIGN_SERVER:\n\t\tcase OBJECT_SEQUENCE:\n\t\tcase OBJECT_FOREIGN_TABLE:\n\t\tcase OBJECT_TABLE:\n\t\tcase OBJECT_EXTENSION:\n\t\tcase OBJECT_COLLATION:\n\t\tcase OBJECT_VIEW:\n\t\tcase OBJECT_ROLE:\n\t\tcase OBJECT_PUBLICATION:\n\t\t{\n\t\t\tcheck_object_ownership(userId, type, *addr, node, *relation);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"%d object type is not supported within \"\n\t\t\t\t\t\t\t\t   \"object propagation\", type)));\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n/*\n * Copied from PG code.\n *\n * Convert an array of TEXT into a List of string Values, as emitted by the\n * parser, which is what get_object_address uses as input.\n */\nstatic List *\ntextarray_to_strvaluelist(ArrayType *arr)\n{\n\tDatum *elems;\n\tbool *nulls;\n\tint nelems;\n\tList *list = NIL;\n\tint i;\n\n\tdeconstruct_array(arr, TEXTOID, -1, false, TYPALIGN_INT,\n\t\t\t\t\t  &elems, &nulls, &nelems);\n\n\tfor (i = 0; i < nelems; i++)\n\t{\n\t\tif (nulls[i])\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t errmsg(\"name or argument lists may not contain nulls\")));\n\t\t}\n\t\tlist = lappend(list, makeString(TextDatumGetCString(elems[i])));\n\t}\n\n\treturn list;\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/citus_create_restore_point.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_create_restore_point.c\n *\n * UDF for creating a consistent restore point across all nodes.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n\n#include \"access/xlog.h\"\n#include \"access/xlog_internal.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/pg_list.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/lock.h\"\n#include \"utils/builtins.h\"\n#include \"utils/pg_lsn.h\"\n\n#include \"distributed/connection_management.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/remote_commands.h\"\n\n\n#define CREATE_RESTORE_POINT_COMMAND \"SELECT pg_catalog.pg_create_restore_point($1::text)\"\n\n/*\n * BLOCK_DISTRIBUTED_WRITES_COMMAND acquires ExclusiveLock on:\n * 1. pg_dist_transaction - blocks 2PC commit decisions\n * 2. pg_dist_partition - blocks DDL operations on distributed tables\n *\n * This ensures both DML (via 2PC) and DDL are blocked on metadata nodes.\n */\n#define BLOCK_DISTRIBUTED_WRITES_COMMAND \\\n\t\t\"LOCK TABLE pg_catalog.pg_dist_transaction IN EXCLUSIVE MODE; \" \\\n\t\t\"LOCK TABLE pg_catalog.pg_dist_partition IN EXCLUSIVE MODE\"\n\n/* local functions forward declarations */\nstatic List * OpenConnectionsToAllWorkerNodes(LOCKMODE lockMode);\nstatic void BlockDistributedTransactions(void);\nstatic void CreateRemoteRestorePoints(char *restoreName, List *connectionList);\nstatic void BlockDistributedTransactionsOnAllMetadataNodes(List *connectionList);\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(citus_create_restore_point);\n\n\n/*\n * citus_create_restore_point creates a cluster-consistent restore point\n * across all nodes in the Citus cluster.\n *\n * In coordinator-only mode, this function blocks new distributed writes\n * at the coordinator and creates restore points on all worker nodes.\n *\n * In MX mode (multi-writer), this function blocks both DML and DDL\n * operations on all metadata nodes by acquiring ExclusiveLock on:\n *   - pg_dist_transaction: blocks 2PC commit decisions (DML)\n *   - pg_dist_partition: blocks DDL on distributed tables\n *\n * This prevents new distributed transactions from recording commit decisions\n * and blocks schema changes, ensuring all restore points represent the same\n * consistent cluster state.\n *\n * The function returns the LSN of the restore point on the coordinator,\n * maintaining backward compatibility with the original implementation.\n *\n * Key insight: We do NOT need to drain in-flight transactions. The commit\n * decision in Citus 2PC happens when LogTransactionRecord() writes to\n * pg_dist_transaction, which occurs BEFORE the writer's local commit.\n * By blocking writes to pg_dist_transaction, we prevent commit decisions\n * from being made. Transactions that have already recorded their commit\n * decision will complete normally, while those that haven't will\n * be blocked. This creates a clean cut point for consistency.\n */\nDatum\ncitus_create_restore_point(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureSuperUser();\n\tEnsureCoordinator();\n\n\ttext *restoreNameText = PG_GETARG_TEXT_P(0);\n\n\tif (RecoveryInProgress())\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t (errmsg(\"recovery is in progress\"),\n\t\t\t\t  errhint(\"WAL control functions cannot be executed during recovery.\"))));\n\t}\n\n\tif (!XLogIsNeeded())\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t errmsg(\"WAL level not sufficient for creating a restore point\"),\n\t\t\t\t errhint(\"wal_level must be set to \\\"replica\\\" or \\\"logical\\\" at server \"\n\t\t\t\t\t\t \"start.\")));\n\t}\n\n\tchar *restoreNameString = text_to_cstring(restoreNameText);\n\tif (strlen(restoreNameString) >= MAXFNAMELEN)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t errmsg(\"value too long for restore point (maximum %d characters)\",\n\t\t\t\t\t\tMAXFNAMELEN - 1)));\n\t}\n\n\t/*\n\t * establish connections to all nodes before taking any locks\n\t * ShareLock prevents new nodes being added, rendering connectionList incomplete\n\t */\n\tList *connectionList = OpenConnectionsToAllWorkerNodes(ShareLock);\n\tXLogRecPtr localRestorePoint = InvalidXLogRecPtr;\n\n\tPG_TRY();\n\t{\n\t\t/*\n\t\t * Send a BEGIN to bust through pgbouncer. We won't actually commit since\n\t\t * that takes time. Instead we just close the connections and roll back,\n\t\t * which doesn't undo pg_create_restore_point.\n\t\t */\n\t\tRemoteTransactionListBegin(connectionList);\n\n\t\t/* DANGER: finish as quickly as possible after this */\n\t\tBlockDistributedTransactions();\n\n\t\tBlockDistributedTransactionsOnAllMetadataNodes(connectionList);\n\n\t\t/* do local restore point first to bail out early if something goes wrong */\n\t\tlocalRestorePoint = XLogRestorePoint(restoreNameString);\n\n\t\t/* run pg_create_restore_point on all nodes */\n\t\tCreateRemoteRestorePoints(restoreNameString, connectionList);\n\n\t\t/* close connections to all nodes and\n\t\t * all locks gets released as part of the transaction rollback\n\t\t */\n\t\tMultiConnection *conn = NULL;\n\t\tforeach_declared_ptr(conn, connectionList)\n\t\t{\n\t\t\tForgetResults(conn);\n\t\t\tCloseConnection(conn);\n\t\t}\n\t\tconnectionList = NIL;\n\t}\n\tPG_CATCH();\n\t{\n\t\t/*\n\t\t * On error, ensure we clean up connections and release locks.\n\t\t * Rolling back the metadata node transactions releases the\n\t\t * ExclusiveLocks on pg_dist_transaction cluster-wide.\n\t\t */\n\t\tMultiConnection *conn = NULL;\n\t\tforeach_declared_ptr(conn, connectionList)\n\t\t{\n\t\t\tForgetResults(conn);\n\t\t\tCloseConnection(conn);\n\t\t}\n\t\tconnectionList = NIL;\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n\n\tPG_RETURN_LSN(localRestorePoint);\n}\n\n\n/*\n * OpenConnectionsToAllNodes opens connections to all nodes and returns the list\n * of connections.\n */\nstatic List *\nOpenConnectionsToAllWorkerNodes(LOCKMODE lockMode)\n{\n\tList *connectionList = NIL;\n\tint connectionFlags = FORCE_NEW_CONNECTION;\n\n\tList *workerNodeList = ActivePrimaryNonCoordinatorNodeList(lockMode);\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tMultiConnection *connection = StartNodeConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  workerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  workerNode->workerPort);\n\t\tMarkRemoteTransactionCritical(connection);\n\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\tFinishConnectionListEstablishment(connectionList);\n\n\treturn connectionList;\n}\n\n\n/*\n * BlockDistributedTransactions blocks distributed transactions that use 2PC\n * and changes to pg_dist_node (e.g. node addition) and pg_dist_partition\n * (table creation).\n */\nstatic void\nBlockDistributedTransactions(void)\n{\n\tLockRelationOid(DistNodeRelationId(), ExclusiveLock);\n\tLockRelationOid(DistPartitionRelationId(), ExclusiveLock);\n\tLockRelationOid(DistTransactionRelationId(), ExclusiveLock);\n}\n\n\n/*\n * BlockDistributedTransactionsOnAllMetadataNodes blocks distributed transactions\n * on all metadata nodes by executing pg_lock_table remotely.\n *\n * This is the MX-mode equivalent of BlockDistributedTransactions(), extended\n * to all nodes capable of initiating distributed transactions. We must hold\n * these locks across the cluster to prevent commit decisions from being made\n * on any node.\n *\n * The function expects that connections are already in a transaction block\n * (BEGIN has been sent). The locks will be held until the transaction is\n * rolled back or committed.\n */\nstatic void\nBlockDistributedTransactionsOnAllMetadataNodes(List *connectionList)\n{\n\t/*\n\t * Send LOCK TABLE commands to all metadata nodes in parallel. We use\n\t * standard SQL LOCK TABLE syntax to acquire ExclusiveLock on catalog\n\t * tables, mirroring what BlockDistributedTransactions() does on the\n\t * coordinator via LockRelationOid().\n\t *\n\t * The BLOCK_DISTRIBUTED_WRITES_COMMAND acquires:\n\t * 1. ExclusiveLock on pg_dist_transaction (blocks 2PC commit decisions)\n\t * 2. ExclusiveLock on pg_dist_partition (blocks DDL on distributed tables)\n\t *\n\t * Note: Unlike the local coordinator lock which also locks pg_dist_node,\n\t * we don't lock pg_dist_node on remote nodes because node management\n\t * operations (adding/removing nodes) are still coordinator-only.\n\t *\n\t * These locks naturally serialize concurrent restore point operations\n\t * cluster-wide, so no additional advisory lock is needed.\n\t */\n\n\t/* Build list of remote metadata node connections */\n\tList *metadataConnectionList = NIL;\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tWorkerNode *workerNode = FindWorkerNode(connection->hostname, connection->port);\n\t\tbool isRemoteMetadataNode = workerNode != NULL &&\n\t\t\t\t\t\t\t\t\tNodeIsPrimaryAndRemote(workerNode);\n\n\t\tif (isRemoteMetadataNode)\n\t\t{\n\t\t\tmetadataConnectionList = lappend(metadataConnectionList, connection);\n\t\t}\n\t}\n\n\t/* Send lock commands in parallel to all remote metadata nodes */\n\tforeach_declared_ptr(connection, metadataConnectionList)\n\t{\n\t\t/*\n\t\t * We could use ExecuteCriticalRemoteCommand instead, but it would\n\t\t * not allow us to execute the commands in parallel. So for sake of\n\t\t * performance, we use SendRemoteCommand and send lock commands in parallel\n\t\t * to all metadata nodes, and later wait for all lock acquisitions to complete.\n\t\t */\n\t\tint querySent = SendRemoteCommand(connection, BLOCK_DISTRIBUTED_WRITES_COMMAND);\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tReportConnectionError(connection, ERROR);\n\t\t}\n\t}\n\n\t/*\n\t * Wait for all lock acquisitions to complete. If any node fails to\n\t * acquire locks (e.g., due to a conflicting lock), this will error out.\n\t */\n\tforeach_declared_ptr(connection, metadataConnectionList)\n\t{\n\t\tPGresult *result = GetRemoteCommandResult(connection, true);\n\t\tif (!IsResponseOK(result))\n\t\t{\n\t\t\tReportResultError(connection, result, ERROR);\n\t\t}\n\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\t}\n}\n\n\n/*\n * CreateRemoteRestorePoints creates a restore point via each of the\n * connections in the list in parallel.\n */\nstatic void\nCreateRemoteRestorePoints(char *restoreName, List *connectionList)\n{\n\tint parameterCount = 1;\n\tOid parameterTypes[1] = { TEXTOID };\n\tconst char *parameterValues[1] = { restoreName };\n\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tint querySent = SendRemoteCommandParams(connection, CREATE_RESTORE_POINT_COMMAND,\n\t\t\t\t\t\t\t\t\t\t\t\tparameterCount, parameterTypes,\n\t\t\t\t\t\t\t\t\t\t\t\tparameterValues, false);\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tReportConnectionError(connection, ERROR);\n\t\t}\n\t}\n\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tPGresult *result = GetRemoteCommandResult(connection, true);\n\t\tif (!IsResponseOK(result))\n\t\t{\n\t\t\tReportResultError(connection, result, ERROR);\n\t\t}\n\n\t\tPQclear(result);\n\n\t\tForgetResults(connection);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/citus_split_shard_by_split_points.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_split_shard_by_split_points.c\n *\n * This file contains functions to split a shard.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/pg_type.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/pg_list.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/shard_split.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/utils/distribution_column_map.h\"\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(citus_split_shard_by_split_points);\n\n/*\n * citus_split_shard_by_split_points(shard_id bigint, split_points text[], node_ids integer[], shard_transfer_mode citus.shard_transfer_mode)\n * Split source shard into multiple shards using the given split points.\n * 'shard_id' is the id of source shard to split.\n * 'split_points' is an array that represents the split points.\n * 'node_ids' is an array that represents the placement node ids of the new shards.\n * 'shard_transfer_mode citus.shard_transfer_mode' is the transfer mode for split.\n */\nDatum\ncitus_split_shard_by_split_points(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tuint64 shardIdToSplit = DatumGetUInt64(PG_GETARG_DATUM(0));\n\n\tArrayType *splitPointsArrayObject = PG_GETARG_ARRAYTYPE_P(1);\n\tList *shardSplitPointsList = TextArrayTypeToIntegerList(splitPointsArrayObject);\n\n\tArrayType *nodeIdsArrayObject = PG_GETARG_ARRAYTYPE_P(2);\n\tList *nodeIdsForPlacementList = IntegerArrayTypeToList(nodeIdsArrayObject);\n\n\tOid shardTransferModeOid = PG_GETARG_OID(3);\n\tSplitMode shardSplitMode = LookupSplitMode(shardTransferModeOid);\n\n\tDistributionColumnMap *distributionColumnOverrides = NULL;\n\tList *sourceColocatedShardIntervalList = NIL;\n\tSplitShard(\n\t\tshardSplitMode,\n\t\tSHARD_SPLIT_API,\n\t\tshardIdToSplit,\n\t\tshardSplitPointsList,\n\t\tnodeIdsForPlacementList,\n\t\tdistributionColumnOverrides,\n\t\tsourceColocatedShardIntervalList,\n\t\tINVALID_COLOCATION_ID);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * LookupSplitMode maps the oids of citus.shard_transfer_mode to SplitMode enum.\n */\nSplitMode\nLookupSplitMode(Oid shardTransferModeOid)\n{\n\tSplitMode shardSplitMode = BLOCKING_SPLIT;\n\n\tDatum enumLabelDatum = DirectFunctionCall1(enum_out, shardTransferModeOid);\n\tchar *enumLabel = DatumGetCString(enumLabelDatum);\n\n\t/* Extend with other modes as we support them */\n\tif (strncmp(enumLabel, \"block_writes\", NAMEDATALEN) == 0)\n\t{\n\t\tshardSplitMode = BLOCKING_SPLIT;\n\t}\n\telse if (strncmp(enumLabel, \"force_logical\", NAMEDATALEN) == 0)\n\t{\n\t\tshardSplitMode = NON_BLOCKING_SPLIT;\n\t}\n\telse if (strncmp(enumLabel, \"auto\", NAMEDATALEN) == 0)\n\t{\n\t\tshardSplitMode = AUTO_SPLIT;\n\t}\n\telse\n\t{\n\t\t/* We will not get here as postgres will validate the enum value. */\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"Invalid shard tranfer mode: '%s'. Expected split mode is 'block_writes/auto/force_logical'.\",\n\t\t\t\t\t\t\tenumLabel)));\n\t}\n\n\treturn shardSplitMode;\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/citus_tools.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_tools.c\n *\t  UDF to run multi shard/worker queries\n *\n * This file contains functions to run commands on other worker/shards.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/htup_details.h\"\n#include \"catalog/pg_type.h\"\n#include \"lib/stringinfo.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/utils/function.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n\nPG_FUNCTION_INFO_V1(master_run_on_worker);\n\nstatic int ParseCommandParameters(FunctionCallInfo fcinfo, StringInfo **nodeNameArray,\n\t\t\t\t\t\t\t\t  int **nodePortsArray, StringInfo **commandStringArray,\n\t\t\t\t\t\t\t\t  bool *parallel);\nstatic void ExecuteCommandsInParallelAndStoreResults(StringInfo *nodeNameArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t int *nodePortArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t StringInfo *commandStringArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t bool *statusArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t StringInfo *resultStringArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t int commandCount);\nstatic bool GetConnectionStatusAndResult(MultiConnection *connection, bool *resultStatus,\n\t\t\t\t\t\t\t\t\t\t StringInfo queryResultString);\nstatic void ExecuteCommandsAndStoreResults(StringInfo *nodeNameArray,\n\t\t\t\t\t\t\t\t\t\t   int *nodePortArray,\n\t\t\t\t\t\t\t\t\t\t   StringInfo *commandStringArray,\n\t\t\t\t\t\t\t\t\t\t   bool *statusArray,\n\t\t\t\t\t\t\t\t\t\t   StringInfo *resultStringArray,\n\t\t\t\t\t\t\t\t\t\t   int commandCount);\nstatic bool ExecuteOptionalSingleResultCommand(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t   char *queryString, StringInfo\n\t\t\t\t\t\t\t\t\t\t\t   queryResultString);\nstatic Tuplestorestate * CreateTupleStore(TupleDesc tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t  StringInfo *nodeNameArray, int *nodePortArray,\n\t\t\t\t\t\t\t\t\t\t  bool *statusArray,\n\t\t\t\t\t\t\t\t\t\t  StringInfo *resultArray, int commandCount);\n\n\n/*\n * master_run_on_worker executes queries/commands to run on specified worker and\n * returns success status and query/command result. Expected input is 3 arrays\n * containing node names, node ports, and query strings, and boolean flag to specify\n * parallel execution. The function then returns node_name, node_port, success,\n * result tuples upon completion of the query. The same user credentials are used\n * to connect to remote nodes.\n */\nDatum\nmaster_run_on_worker(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;\n\tbool parallelExecution = false;\n\tStringInfo *nodeNameArray = NULL;\n\tint *nodePortArray = NULL;\n\tStringInfo *commandStringArray = NULL;\n\n\t/* check to see if caller supports us returning a tuplestore */\n\tif (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t errmsg(\"materialize mode required, but it is not \"\n\t\t\t\t\t\t\"allowed in this context\")));\n\t}\n\n\tint commandCount = ParseCommandParameters(fcinfo, &nodeNameArray, &nodePortArray,\n\t\t\t\t\t\t\t\t\t\t\t  &commandStringArray, &parallelExecution);\n\n\tMemoryContext per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;\n\tMemoryContext oldcontext = MemoryContextSwitchTo(per_query_ctx);\n\n\t/* get the requested return tuple description */\n\tTupleDesc tupleDescriptor = CreateTupleDescCopy(rsinfo->expectedDesc);\n\n\t/*\n\t * Check to make sure we have correct tuple descriptor\n\t */\n\tif (tupleDescriptor->natts != 4 ||\n\t\tTupleDescAttr(tupleDescriptor, 0)->atttypid != TEXTOID ||\n\t\tTupleDescAttr(tupleDescriptor, 1)->atttypid != INT4OID ||\n\t\tTupleDescAttr(tupleDescriptor, 2)->atttypid != BOOLOID ||\n\t\tTupleDescAttr(tupleDescriptor, 3)->atttypid != TEXTOID)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_INVALID_COLUMN_DEFINITION),\n\t\t\t\t errmsg(\"query-specified return tuple and \"\n\t\t\t\t\t\t\"function return type are not compatible\")));\n\t}\n\n\t/*\n\t * prepare storage for status and result values.\n\t * commandCount is based on user input however, it is the length of list\n\t * instead of a user given integer, hence this should be safe here in terms\n\t * of memory allocation.\n\t */\n\tbool *statusArray = palloc0(commandCount * sizeof(bool));\n\tStringInfo *resultArray = palloc0(commandCount * sizeof(StringInfo));\n\tfor (int commandIndex = 0; commandIndex < commandCount; commandIndex++)\n\t{\n\t\tresultArray[commandIndex] = makeStringInfo();\n\t}\n\n\tif (parallelExecution)\n\t{\n\t\tExecuteCommandsInParallelAndStoreResults(nodeNameArray, nodePortArray,\n\t\t\t\t\t\t\t\t\t\t\t\t commandStringArray,\n\t\t\t\t\t\t\t\t\t\t\t\t statusArray, resultArray, commandCount);\n\t}\n\telse\n\t{\n\t\tExecuteCommandsAndStoreResults(nodeNameArray, nodePortArray, commandStringArray,\n\t\t\t\t\t\t\t\t\t   statusArray, resultArray, commandCount);\n\t}\n\n\t/* let the caller know we're sending back a tuplestore */\n\trsinfo->returnMode = SFRM_Materialize;\n\tTuplestorestate *tupleStore = CreateTupleStore(tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t   nodeNameArray, nodePortArray,\n\t\t\t\t\t\t\t\t\t\t\t\t   statusArray,\n\t\t\t\t\t\t\t\t\t\t\t\t   resultArray, commandCount);\n\trsinfo->setResult = tupleStore;\n\trsinfo->setDesc = tupleDescriptor;\n\n\tMemoryContextSwitchTo(oldcontext);\n\n\tPG_RETURN_VOID();\n}\n\n\n/* ParseCommandParameters reads call parameters and fills in data structures */\nstatic int\nParseCommandParameters(FunctionCallInfo fcinfo, StringInfo **nodeNameArray,\n\t\t\t\t\t   int **nodePortsArray, StringInfo **commandStringArray,\n\t\t\t\t\t   bool *parallel)\n{\n\tArrayType *nodeNameArrayObject = PG_GETARG_ARRAYTYPE_P(0);\n\tArrayType *nodePortArrayObject = PG_GETARG_ARRAYTYPE_P(1);\n\tArrayType *commandStringArrayObject = PG_GETARG_ARRAYTYPE_P(2);\n\tbool parallelExecution = PG_GETARG_BOOL(3);\n\tint nodeNameCount = ArrayObjectCount(nodeNameArrayObject);\n\tint nodePortCount = ArrayObjectCount(nodePortArrayObject);\n\tint commandStringCount = ArrayObjectCount(commandStringArrayObject);\n\tDatum *nodeNameDatumArray = DeconstructArrayObject(nodeNameArrayObject);\n\tDatum *nodePortDatumArray = DeconstructArrayObject(nodePortArrayObject);\n\tDatum *commandStringDatumArray = DeconstructArrayObject(commandStringArrayObject);\n\n\tif (nodeNameCount != nodePortCount || nodeNameCount != commandStringCount)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t errmsg(\"expected same number of node name, port, and query string\")));\n\t}\n\n\tStringInfo *nodeNames = palloc0(nodeNameCount * sizeof(StringInfo));\n\tint *nodePorts = palloc0(nodeNameCount * sizeof(int));\n\tStringInfo *commandStrings = palloc0(nodeNameCount * sizeof(StringInfo));\n\n\tfor (int index = 0; index < nodeNameCount; index++)\n\t{\n\t\ttext *nodeNameText = DatumGetTextP(nodeNameDatumArray[index]);\n\t\tchar *nodeName = text_to_cstring(nodeNameText);\n\t\tint32 nodePort = DatumGetInt32(nodePortDatumArray[index]);\n\t\ttext *commandText = DatumGetTextP(commandStringDatumArray[index]);\n\t\tchar *commandString = text_to_cstring(commandText);\n\n\t\tnodeNames[index] = makeStringInfo();\n\t\tcommandStrings[index] = makeStringInfo();\n\n\t\tappendStringInfo(nodeNames[index], \"%s\", nodeName);\n\t\tnodePorts[index] = nodePort;\n\t\tappendStringInfo(commandStrings[index], \"%s\", commandString);\n\t}\n\n\t*nodeNameArray = nodeNames;\n\t*nodePortsArray = nodePorts;\n\t*commandStringArray = commandStrings;\n\t*parallel = parallelExecution;\n\n\treturn nodeNameCount;\n}\n\n\n/*\n * ExecuteCommandsInParallelAndStoreResults connects to each node specified in\n * nodeNameArray and nodePortArray, and executes command in commandStringArray\n * in parallel fashion. Execution success status and result is reported for\n * each command in statusArray and resultStringArray. Each array contains\n * commandCount items.\n */\nstatic void\nExecuteCommandsInParallelAndStoreResults(StringInfo *nodeNameArray, int *nodePortArray,\n\t\t\t\t\t\t\t\t\t\t StringInfo *commandStringArray,\n\t\t\t\t\t\t\t\t\t\t bool *statusArray, StringInfo *resultStringArray,\n\t\t\t\t\t\t\t\t\t\t int commandCount)\n{\n\tMultiConnection **connectionArray =\n\t\tpalloc0(commandCount * sizeof(MultiConnection *));\n\tint finishedCount = 0;\n\n\t/* start connections asynchronously */\n\tfor (int commandIndex = 0; commandIndex < commandCount; commandIndex++)\n\t{\n\t\tchar *nodeName = nodeNameArray[commandIndex]->data;\n\t\tint nodePort = nodePortArray[commandIndex];\n\t\tint connectionFlags = FORCE_NEW_CONNECTION;\n\t\tconnectionArray[commandIndex] =\n\t\t\tStartNodeConnection(connectionFlags, nodeName, nodePort);\n\t}\n\n\t/* establish connections */\n\tfor (int commandIndex = 0; commandIndex < commandCount; commandIndex++)\n\t{\n\t\tMultiConnection *connection = connectionArray[commandIndex];\n\t\tStringInfo queryResultString = resultStringArray[commandIndex];\n\t\tchar *nodeName = nodeNameArray[commandIndex]->data;\n\t\tint nodePort = nodePortArray[commandIndex];\n\n\t\tFinishConnectionEstablishment(connection);\n\n\t\t/* check whether connection attempt was successful */\n\t\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t\t{\n\t\t\tappendStringInfo(queryResultString, \"failed to connect to %s:%d\", nodeName,\n\t\t\t\t\t\t\t nodePort);\n\t\t\tstatusArray[commandIndex] = false;\n\t\t\tCloseConnection(connection);\n\t\t\tconnectionArray[commandIndex] = NULL;\n\t\t\tfinishedCount++;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* set the application_name to avoid nested execution checks */\n\t\tint querySent = SendRemoteCommand(connection, psprintf(\n\t\t\t\t\t\t\t\t\t\t\t  \"SET application_name TO '%s%ld'\",\n\t\t\t\t\t\t\t\t\t\t\t  CITUS_RUN_COMMAND_APPLICATION_NAME_PREFIX,\n\t\t\t\t\t\t\t\t\t\t\t  GetGlobalPID()));\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tStoreErrorMessage(connection, queryResultString);\n\t\t\tstatusArray[commandIndex] = false;\n\t\t\tCloseConnection(connection);\n\t\t\tconnectionArray[commandIndex] = NULL;\n\t\t\tfinishedCount++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tstatusArray[commandIndex] = true;\n\t}\n\n\t/* send queries at once */\n\tfor (int commandIndex = 0; commandIndex < commandCount; commandIndex++)\n\t{\n\t\tMultiConnection *connection = connectionArray[commandIndex];\n\t\tif (connection == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tbool raiseInterrupts = true;\n\t\tPGresult *queryResult = GetRemoteCommandResult(connection, raiseInterrupts);\n\n\t\t/* write the result value or error message to queryResultString */\n\t\tStringInfo queryResultString = resultStringArray[commandIndex];\n\t\tbool success = EvaluateSingleQueryResult(connection, queryResult,\n\t\t\t\t\t\t\t\t\t\t\t\t queryResultString);\n\t\tif (!success)\n\t\t{\n\t\t\tstatusArray[commandIndex] = false;\n\t\t\tCloseConnection(connection);\n\t\t\tconnectionArray[commandIndex] = NULL;\n\t\t\tfinishedCount++;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* clear results for the next command */\n\t\tPQclear(queryResult);\n\n\t\tbool raiseErrors = false;\n\t\tClearResults(connection, raiseErrors);\n\n\t\t/* we only care about the SET application_name result on failure */\n\t\tresetStringInfo(queryResultString);\n\t}\n\n\t/* send queries at once */\n\tfor (int commandIndex = 0; commandIndex < commandCount; commandIndex++)\n\t{\n\t\tMultiConnection *connection = connectionArray[commandIndex];\n\t\tchar *queryString = commandStringArray[commandIndex]->data;\n\t\tStringInfo queryResultString = resultStringArray[commandIndex];\n\n\t\t/*\n\t\t * If we don't have a connection, nothing to send, error string should already\n\t\t * been filled.\n\t\t */\n\t\tif (connection == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tint querySent = SendRemoteCommand(connection, queryString);\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tStoreErrorMessage(connection, queryResultString);\n\t\t\tstatusArray[commandIndex] = false;\n\t\t\tCloseConnection(connection);\n\t\t\tconnectionArray[commandIndex] = NULL;\n\t\t\tfinishedCount++;\n\t\t}\n\t}\n\n\t/* check for query results */\n\twhile (finishedCount < commandCount)\n\t{\n\t\tfor (int commandIndex = 0; commandIndex < commandCount; commandIndex++)\n\t\t{\n\t\t\tMultiConnection *connection = connectionArray[commandIndex];\n\t\t\tStringInfo queryResultString = resultStringArray[commandIndex];\n\t\t\tbool success = false;\n\n\t\t\tif (connection == NULL)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tbool queryFinished = GetConnectionStatusAndResult(connection, &success,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  queryResultString);\n\n\t\t\tif (queryFinished)\n\t\t\t{\n\t\t\t\tfinishedCount++;\n\t\t\t\tstatusArray[commandIndex] = success;\n\t\t\t\tconnectionArray[commandIndex] = NULL;\n\t\t\t\tCloseConnection(connection);\n\t\t\t}\n\t\t}\n\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tif (finishedCount < commandCount)\n\t\t{\n\t\t\tlong sleepIntervalPerCycle = RemoteTaskCheckInterval * 1000L;\n\t\t\tpg_usleep(sleepIntervalPerCycle);\n\t\t}\n\t}\n\n\tpfree(connectionArray);\n}\n\n\n/*\n * GetConnectionStatusAndResult checks the active connection and returns true if\n * query execution is finished (either success or fail).\n * Query success/fail in resultStatus, and query result in queryResultString are\n * reported upon completion of the query.\n */\nstatic bool\nGetConnectionStatusAndResult(MultiConnection *connection, bool *resultStatus,\n\t\t\t\t\t\t\t StringInfo queryResultString)\n{\n\tbool finished = true;\n\tConnStatusType connectionStatus = PQstatus(connection->pgConn);\n\n\t*resultStatus = false;\n\tresetStringInfo(queryResultString);\n\n\tif (connectionStatus == CONNECTION_BAD)\n\t{\n\t\tappendStringInfo(queryResultString, \"connection lost\");\n\t\treturn finished;\n\t}\n\n\tint consumeInput = PQconsumeInput(connection->pgConn);\n\tif (consumeInput == 0)\n\t{\n\t\tappendStringInfo(queryResultString, \"query result unavailable\");\n\t\treturn finished;\n\t}\n\n\t/* check later if busy */\n\tif (PQisBusy(connection->pgConn) != 0)\n\t{\n\t\tfinished = false;\n\t\treturn finished;\n\t}\n\n\t/* query result is available at this point */\n\tPGresult *queryResult = PQgetResult(connection->pgConn);\n\tbool success = EvaluateSingleQueryResult(connection, queryResult, queryResultString);\n\tPQclear(queryResult);\n\n\t*resultStatus = success;\n\tfinished = true;\n\treturn true;\n}\n\n\n/*\n * ExecuteCommandsAndStoreResults connects to each node specified in\n * nodeNameArray and nodePortArray, and executes command in commandStringArray\n * in sequential order. Execution success status and result is reported for\n * each command in statusArray and resultStringArray. Each array contains\n * commandCount items.\n */\nstatic void\nExecuteCommandsAndStoreResults(StringInfo *nodeNameArray, int *nodePortArray,\n\t\t\t\t\t\t\t   StringInfo *commandStringArray, bool *statusArray,\n\t\t\t\t\t\t\t   StringInfo *resultStringArray, int commandCount)\n{\n\tfor (int commandIndex = 0; commandIndex < commandCount; commandIndex++)\n\t{\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tchar *nodeName = nodeNameArray[commandIndex]->data;\n\t\tint32 nodePort = nodePortArray[commandIndex];\n\t\tchar *queryString = commandStringArray[commandIndex]->data;\n\t\tStringInfo queryResultString = resultStringArray[commandIndex];\n\n\t\tint connectionFlags = FORCE_NEW_CONNECTION;\n\t\tMultiConnection *connection =\n\t\t\tGetNodeConnection(connectionFlags, nodeName, nodePort);\n\n\t\t/* set the application_name to avoid nested execution checks */\n\t\tbool success = ExecuteOptionalSingleResultCommand(\n\t\t\tconnection,\n\t\t\tpsprintf(\n\t\t\t\t\"SET application_name TO '%s%ld'\",\n\t\t\t\tCITUS_RUN_COMMAND_APPLICATION_NAME_PREFIX,\n\t\t\t\tGetGlobalPID()),\n\t\t\tqueryResultString\n\t\t\t);\n\t\tif (!success)\n\t\t{\n\t\t\tstatusArray[commandIndex] = false;\n\t\t\tCloseConnection(connection);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* we only care about the SET application_name result on failure */\n\t\tresetStringInfo(queryResultString);\n\n\t\t/* send the actual query string */\n\t\tsuccess = ExecuteOptionalSingleResultCommand(connection, queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t queryResultString);\n\n\t\tstatusArray[commandIndex] = success;\n\t\tCloseConnection(connection);\n\t}\n}\n\n\n/*\n * ExecuteOptionalSingleResultCommand executes a query at specified remote node using\n * the calling user's credentials. The function returns the query status\n * (success/failure), and query result. The query is expected to return a single\n * target containing zero or one rows.\n */\nstatic bool\nExecuteOptionalSingleResultCommand(MultiConnection *connection, char *queryString,\n\t\t\t\t\t\t\t\t   StringInfo queryResultString)\n{\n\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t{\n\t\tappendStringInfo(queryResultString, \"failed to connect to %s:%d\",\n\t\t\t\t\t\t connection->hostname, connection->port);\n\t\treturn false;\n\t}\n\n\tif (!SendRemoteCommand(connection, queryString))\n\t{\n\t\tappendStringInfo(queryResultString, \"failed to send query to %s:%d\",\n\t\t\t\t\t\t connection->hostname, connection->port);\n\t\treturn false;\n\t}\n\n\tbool raiseInterrupts = true;\n\tPGresult *queryResult = GetRemoteCommandResult(connection, raiseInterrupts);\n\n\t/* write the result value or error message to queryResultString */\n\tbool success = EvaluateSingleQueryResult(connection, queryResult, queryResultString);\n\n\t/* clear result and close the connection */\n\tPQclear(queryResult);\n\n\tbool raiseErrors = false;\n\tClearResults(connection, raiseErrors);\n\n\treturn success;\n}\n\n\n/* CreateTupleStore prepares result tuples from individual query results */\nstatic Tuplestorestate *\nCreateTupleStore(TupleDesc tupleDescriptor,\n\t\t\t\t StringInfo *nodeNameArray, int *nodePortArray, bool *statusArray,\n\t\t\t\t StringInfo *resultArray, int commandCount)\n{\n\tTuplestorestate *tupleStore = tuplestore_begin_heap(true, false, work_mem);\n\tbool nulls[4] = { false, false, false, false };\n\n\tfor (int commandIndex = 0; commandIndex < commandCount; commandIndex++)\n\t{\n\t\tDatum values[4];\n\t\tStringInfo nodeNameString = nodeNameArray[commandIndex];\n\t\tStringInfo resultString = resultArray[commandIndex];\n\t\ttext *nodeNameText = cstring_to_text_with_len(nodeNameString->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  nodeNameString->len);\n\t\ttext *resultText = cstring_to_text_with_len(resultString->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\tresultString->len);\n\n\t\tvalues[0] = PointerGetDatum(nodeNameText);\n\t\tvalues[1] = Int32GetDatum(nodePortArray[commandIndex]);\n\t\tvalues[2] = BoolGetDatum(statusArray[commandIndex]);\n\t\tvalues[3] = PointerGetDatum(resultText);\n\n\t\tHeapTuple tuple = heap_form_tuple(tupleDescriptor, values, nulls);\n\t\ttuplestore_puttuple(tupleStore, tuple);\n\n\t\theap_freetuple(tuple);\n\t\tpfree(nodeNameText);\n\t\tpfree(resultText);\n\t}\n\treturn tupleStore;\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/create_shards.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * create_shards.c\n *\n * This file contains functions to distribute a table by creating shards for it\n * across a set of worker nodes.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <ctype.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"port.h\"\n\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_class.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/fd.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/lock.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/errcodes.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/palloc.h\"\n\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/worker_manager.h\"\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(master_create_worker_shards);\n\n\n/*\n * master_create_worker_shards is a deprecated UDF that was used to\n * create shards for a hash-distributed table.\n */\nDatum\nmaster_create_worker_shards(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"master_create_worker_shards has been deprecated\")));\n}\n\n\n/*\n * CreateShardsWithRoundRobinPolicy creates empty shards for the given table\n * based on the specified number of initial shards. The function first updates\n * metadata on the coordinator node to make this shard (and its placements)\n * visible. Note that the function assumes the table is hash partitioned and\n * calculates the min/max hash token ranges for each shard, giving them an equal\n * split of the hash space. Finally, function creates empty shard placements on\n * worker nodes.\n */\nvoid\nCreateShardsWithRoundRobinPolicy(Oid distributedTableId, int32 shardCount,\n\t\t\t\t\t\t\t\t int32 replicationFactor, bool useExclusiveConnections)\n{\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(distributedTableId);\n\tList *insertedShardPlacements = NIL;\n\tList *insertedShardIds = NIL;\n\n\t/* make sure table is hash partitioned */\n\tCheckHashPartitionedTable(distributedTableId);\n\n\t/*\n\t * In contrast to append/range partitioned tables it makes more sense to\n\t * require ownership privileges - shards for hash-partitioned tables are\n\t * only created once, not continually during ingest as for the other\n\t * partitioning types.\n\t */\n\tEnsureTableOwner(distributedTableId);\n\n\t/* we plan to add shards: get an exclusive lock on relation oid */\n\tLockRelationOid(distributedTableId, ExclusiveLock);\n\n\t/* validate that shards haven't already been created for this table */\n\tList *existingShardList = LoadShardList(distributedTableId);\n\tif (existingShardList != NIL)\n\t{\n\t\tchar *tableName = get_rel_name(distributedTableId);\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"table \\\"%s\\\" has already had shards created for it\",\n\t\t\t\t\t\t\t   tableName)));\n\t}\n\n\t/* make sure that at least one shard is specified */\n\tif (shardCount <= 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"shard_count must be positive\")));\n\t}\n\n\t/* make sure that at least one replica is specified */\n\tif (replicationFactor <= 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"replication_factor must be positive\")));\n\t}\n\n\t/* make sure that RF=1 if the table is streaming replicated */\n\tif (cacheEntry->replicationModel == REPLICATION_MODEL_STREAMING &&\n\t\treplicationFactor > 1)\n\t{\n\t\tchar *relationName = get_rel_name(cacheEntry->relationId);\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"using replication factor %d with the streaming \"\n\t\t\t\t\t\t\t   \"replication model is not supported\",\n\t\t\t\t\t\t\t   replicationFactor),\n\t\t\t\t\t\terrdetail(\"The table %s is marked as streaming replicated and \"\n\t\t\t\t\t\t\t\t  \"the shard replication factor of streaming replicated \"\n\t\t\t\t\t\t\t\t  \"tables must be 1.\", relationName),\n\t\t\t\t\t\terrhint(\"Use replication factor 1.\")));\n\t}\n\n\t/* calculate the split of the hash space */\n\tuint64 hashTokenIncrement = HASH_TOKEN_COUNT / shardCount;\n\n\t/* don't allow concurrent node list changes that require an exclusive lock */\n\tLockRelationOid(DistNodeRelationId(), RowShareLock);\n\n\t/* load and sort the worker node list for deterministic placement */\n\tList *workerNodeList = DistributedTablePlacementNodeList(NoLock);\n\tworkerNodeList = SortList(workerNodeList, CompareWorkerNodes);\n\n\tint32 workerNodeCount = list_length(workerNodeList);\n\tif (replicationFactor > workerNodeCount)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"replication_factor (%d) exceeds number of worker nodes \"\n\t\t\t\t\t\t\t   \"(%d)\", replicationFactor, workerNodeCount),\n\t\t\t\t\t\terrhint(\"Add more worker nodes or try again with a lower \"\n\t\t\t\t\t\t\t\t\"replication factor.\")));\n\t}\n\n\t/* set shard storage type according to relation type */\n\tchar shardStorageType = ShardStorageType(distributedTableId);\n\n\tfor (int64 shardIndex = 0; shardIndex < shardCount; shardIndex++)\n\t{\n\t\tuint32 roundRobinNodeIndex = shardIndex % workerNodeCount;\n\n\t\t/* initialize the hash token space for this shard */\n\t\tint32 shardMinHashToken = PG_INT32_MIN + (shardIndex * hashTokenIncrement);\n\t\tint32 shardMaxHashToken = shardMinHashToken + (hashTokenIncrement - 1);\n\t\tuint64 *shardIdPtr = (uint64 *) palloc0(sizeof(uint64));\n\t\t*shardIdPtr = GetNextShardId();\n\t\tinsertedShardIds = lappend(insertedShardIds, shardIdPtr);\n\n\t\t/* if we are at the last shard, make sure the max token value is INT_MAX */\n\t\tif (shardIndex == (shardCount - 1))\n\t\t{\n\t\t\tshardMaxHashToken = PG_INT32_MAX;\n\t\t}\n\n\t\t/* insert the shard metadata row along with its min/max values */\n\t\ttext *minHashTokenText = IntegerToText(shardMinHashToken);\n\t\ttext *maxHashTokenText = IntegerToText(shardMaxHashToken);\n\n\t\tInsertShardRow(distributedTableId, *shardIdPtr, shardStorageType,\n\t\t\t\t\t   minHashTokenText, maxHashTokenText);\n\n\t\tInsertShardPlacementRows(distributedTableId,\n\t\t\t\t\t\t\t\t *shardIdPtr,\n\t\t\t\t\t\t\t\t workerNodeList,\n\t\t\t\t\t\t\t\t roundRobinNodeIndex,\n\t\t\t\t\t\t\t\t replicationFactor);\n\t}\n\n\t/*\n\t * load shard placements for the shard at once after all placement insertions\n\t * finished. This prevents MetadataCache from rebuilding unnecessarily after\n\t * each placement insertion.\n\t */\n\tuint64 *shardIdPtr;\n\tforeach_declared_ptr(shardIdPtr, insertedShardIds)\n\t{\n\t\tList *placementsForShard = ShardPlacementList(*shardIdPtr);\n\t\tinsertedShardPlacements = list_concat(insertedShardPlacements,\n\t\t\t\t\t\t\t\t\t\t\t  placementsForShard);\n\t}\n\n\tCreateShardsOnWorkers(distributedTableId, insertedShardPlacements,\n\t\t\t\t\t\t  useExclusiveConnections);\n}\n\n\n/*\n * CreateColocatedShards creates shards for the target relation colocated with\n * the source relation.\n */\nvoid\nCreateColocatedShards(Oid targetRelationId, Oid sourceRelationId, bool\n\t\t\t\t\t  useExclusiveConnections)\n{\n\tList *insertedShardPlacements = NIL;\n\tList *insertedShardIds = NIL;\n\n\tCitusTableCacheEntry *targetCacheEntry = GetCitusTableCacheEntry(targetRelationId);\n\tAssert(targetCacheEntry->partitionMethod == DISTRIBUTE_BY_HASH ||\n\t\t   targetCacheEntry->partitionMethod == DISTRIBUTE_BY_NONE);\n\n\t/*\n\t * In contrast to append/range partitioned tables it makes more sense to\n\t * require ownership privileges - shards for hash-partitioned tables are\n\t * only created once, not continually during ingest as for the other\n\t * partitioning types.\n\t */\n\tEnsureTableOwner(targetRelationId);\n\n\t/* we plan to add shards: get an exclusive lock on target relation oid */\n\tLockRelationOid(targetRelationId, ExclusiveLock);\n\n\t/* we don't want source table to get dropped before we colocate with it */\n\tLockRelationOid(sourceRelationId, AccessShareLock);\n\n\t/* prevent placement changes of the source relation until we colocate with them */\n\tList *sourceShardIntervalList = LoadShardIntervalList(sourceRelationId);\n\tLockShardListMetadata(sourceShardIntervalList, ShareLock);\n\n\t/* validate that shards haven't already been created for this table */\n\tList *existingShardList = LoadShardList(targetRelationId);\n\tif (existingShardList != NIL)\n\t{\n\t\tchar *targetRelationName = get_rel_name(targetRelationId);\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"table \\\"%s\\\" has already had shards created for it\",\n\t\t\t\t\t\t\t   targetRelationName)));\n\t}\n\n\tchar targetShardStorageType = ShardStorageType(targetRelationId);\n\n\tShardInterval *sourceShardInterval = NULL;\n\tforeach_declared_ptr(sourceShardInterval, sourceShardIntervalList)\n\t{\n\t\tuint64 sourceShardId = sourceShardInterval->shardId;\n\t\tuint64 *newShardIdPtr = (uint64 *) palloc0(sizeof(uint64));\n\t\t*newShardIdPtr = GetNextShardId();\n\t\tinsertedShardIds = lappend(insertedShardIds, newShardIdPtr);\n\n\t\ttext *shardMinValueText = NULL;\n\t\ttext *shardMaxValueText = NULL;\n\t\tif (targetCacheEntry->partitionMethod == DISTRIBUTE_BY_NONE)\n\t\t{\n\t\t\tAssert(list_length(sourceShardIntervalList) == 1);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint32 shardMinValue = DatumGetInt32(sourceShardInterval->minValue);\n\t\t\tint32 shardMaxValue = DatumGetInt32(sourceShardInterval->maxValue);\n\t\t\tshardMinValueText = IntegerToText(shardMinValue);\n\t\t\tshardMaxValueText = IntegerToText(shardMaxValue);\n\t\t}\n\n\t\tList *sourceShardPlacementList = ShardPlacementListSortedByWorker(\n\t\t\tsourceShardId);\n\n\t\tInsertShardRow(targetRelationId, *newShardIdPtr, targetShardStorageType,\n\t\t\t\t\t   shardMinValueText, shardMaxValueText);\n\n\t\tShardPlacement *sourcePlacement = NULL;\n\t\tforeach_declared_ptr(sourcePlacement, sourceShardPlacementList)\n\t\t{\n\t\t\tint32 groupId = sourcePlacement->groupId;\n\t\t\tconst uint64 shardSize = 0;\n\n\t\t\tInsertShardPlacementRow(*newShardIdPtr,\n\t\t\t\t\t\t\t\t\tINVALID_PLACEMENT_ID,\n\t\t\t\t\t\t\t\t\tshardSize,\n\t\t\t\t\t\t\t\t\tgroupId);\n\t\t}\n\t}\n\n\t/*\n\t * load shard placements for the shard at once after all placement insertions\n\t * finished. This prevents MetadataCache from rebuilding unnecessarily after\n\t * each placement insertion.\n\t */\n\tuint64 *shardIdPtr;\n\tforeach_declared_ptr(shardIdPtr, insertedShardIds)\n\t{\n\t\tList *placementsForShard = ShardPlacementList(*shardIdPtr);\n\t\tinsertedShardPlacements = list_concat(insertedShardPlacements,\n\t\t\t\t\t\t\t\t\t\t\t  placementsForShard);\n\t}\n\n\tCreateShardsOnWorkers(targetRelationId, insertedShardPlacements,\n\t\t\t\t\t\t  useExclusiveConnections);\n}\n\n\n/*\n * CreateReferenceTableShard creates a single shard for the given\n * distributedTableId. The created shard does not have min/max values.\n * Also, the shard is replicated to the all active nodes in the cluster.\n */\nvoid\nCreateReferenceTableShard(Oid distributedTableId)\n{\n\tint workerStartIndex = 0;\n\ttext *shardMinValue = NULL;\n\ttext *shardMaxValue = NULL;\n\tbool useExclusiveConnection = false;\n\n\t/*\n\t * In contrast to append/range partitioned tables it makes more sense to\n\t * require ownership privileges - shards for reference tables are\n\t * only created once, not continually during ingest as for the other\n\t * partitioning types such as append and range.\n\t */\n\tEnsureTableOwner(distributedTableId);\n\n\t/* we plan to add shards: get an exclusive lock on relation oid */\n\tLockRelationOid(distributedTableId, ExclusiveLock);\n\n\t/* set shard storage type according to relation type */\n\tchar shardStorageType = ShardStorageType(distributedTableId);\n\n\t/* validate that shards haven't already been created for this table */\n\tList *existingShardList = LoadShardList(distributedTableId);\n\tif (existingShardList != NIL)\n\t{\n\t\tchar *tableName = get_rel_name(distributedTableId);\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"table \\\"%s\\\" has already had shards created for it\",\n\t\t\t\t\t\t\t   tableName)));\n\t}\n\n\t/*\n\t * load and sort the worker node list for deterministic placements\n\t * create_reference_table has already acquired pg_dist_node lock\n\t */\n\tList *nodeList = ReferenceTablePlacementNodeList(ShareLock);\n\tnodeList = SortList(nodeList, CompareWorkerNodes);\n\n\tint replicationFactor = list_length(nodeList);\n\n\t/* get the next shard id */\n\tuint64 shardId = GetNextShardId();\n\n\tInsertShardRow(distributedTableId, shardId, shardStorageType, shardMinValue,\n\t\t\t\t   shardMaxValue);\n\n\tInsertShardPlacementRows(distributedTableId,\n\t\t\t\t\t\t\t shardId,\n\t\t\t\t\t\t\t nodeList,\n\t\t\t\t\t\t\t workerStartIndex,\n\t\t\t\t\t\t\t replicationFactor);\n\n\t/*\n\t * load shard placements for the shard at once after all placement insertions\n\t * finished. This prevents MetadataCache from rebuilding unnecessarily after\n\t * each placement insertion.\n\t */\n\tList *insertedShardPlacements = ShardPlacementList(shardId);\n\n\tCreateShardsOnWorkers(distributedTableId, insertedShardPlacements,\n\t\t\t\t\t\t  useExclusiveConnection);\n}\n\n\n/*\n * CreateSingleShardTableShardWithRoundRobinPolicy creates a single\n * shard for the given distributedTableId. The created shard does not\n * have min/max values. Unlike CreateReferenceTableShard, the shard is\n * _not_ replicated to all nodes but would have a single placement like\n * Citus local tables.\n *\n * However, this placement doesn't necessarily need to be placed on\n * coordinator. This is determined based on modulo of the colocation\n * id that given table has been associated to.\n */\nvoid\nCreateSingleShardTableShardWithRoundRobinPolicy(Oid relationId, uint32 colocationId)\n{\n\tEnsureTableOwner(relationId);\n\n\t/* we plan to add shards: get an exclusive lock on relation oid */\n\tLockRelationOid(relationId, ExclusiveLock);\n\n\t/*\n\t * Load and sort the worker node list for deterministic placement.\n\t *\n\t * Also take a RowShareLock on pg_dist_node to disallow concurrent\n\t * node list changes that require an exclusive lock.\n\t */\n\tList *workerNodeList = DistributedTablePlacementNodeList(RowShareLock);\n\tworkerNodeList = SortList(workerNodeList, CompareWorkerNodes);\n\n\tint roundRobinNodeIdx =\n\t\tEmptySingleShardTableColocationDecideNodeId(colocationId);\n\n\tchar shardStorageType = ShardStorageType(relationId);\n\ttext *minHashTokenText = NULL;\n\ttext *maxHashTokenText = NULL;\n\tuint64 shardId = GetNextShardId();\n\tInsertShardRow(relationId, shardId, shardStorageType,\n\t\t\t\t   minHashTokenText, maxHashTokenText);\n\n\tint replicationFactor = 1;\n\tInsertShardPlacementRows(relationId,\n\t\t\t\t\t\t\t shardId,\n\t\t\t\t\t\t\t workerNodeList,\n\t\t\t\t\t\t\t roundRobinNodeIdx,\n\t\t\t\t\t\t\t replicationFactor);\n\n\t/*\n\t * load shard placements for the shard at once after all placement insertions\n\t * finished. This prevents MetadataCache from rebuilding unnecessarily after\n\t * each placement insertion.\n\t */\n\tList *insertedShardPlacements = ShardPlacementList(shardId);\n\n\t/*\n\t * We don't need to force using exclusive connections because we're anyway\n\t * creating a single shard.\n\t */\n\tbool useExclusiveConnection = false;\n\tCreateShardsOnWorkers(relationId, insertedShardPlacements,\n\t\t\t\t\t\t  useExclusiveConnection);\n}\n\n\n/*\n * EmptySingleShardTableColocationDecideNodeId returns index of the node\n * that first shard to be created in given \"single-shard table colocation\n * group\" should be placed on.\n *\n * This is determined by modulo of the colocation id by the length of the\n * list returned by DistributedTablePlacementNodeList().\n */\nint\nEmptySingleShardTableColocationDecideNodeId(uint32 colocationId)\n{\n\tList *workerNodeList = DistributedTablePlacementNodeList(RowShareLock);\n\tint32 workerNodeCount = list_length(workerNodeList);\n\tif (workerNodeCount == 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"couldn't find any worker nodes\"),\n\t\t\t\t\t\terrhint(\"Add more worker nodes\")));\n\t}\n\n\treturn colocationId % workerNodeCount;\n}\n\n\n/*\n * CheckHashPartitionedTable looks up the partition information for the given\n * tableId and checks if the table is hash partitioned. If not, the function\n * throws an error.\n */\nvoid\nCheckHashPartitionedTable(Oid distributedTableId)\n{\n\tchar partitionType = PartitionMethod(distributedTableId);\n\tif (partitionType != DISTRIBUTE_BY_HASH)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"unsupported table partition type: %c\", partitionType)));\n\t}\n}\n\n\n/* Helper function to convert an integer value to a text type */\ntext *\nIntegerToText(int32 value)\n{\n\tStringInfo valueString = makeStringInfo();\n\tappendStringInfo(valueString, \"%d\", value);\n\n\ttext *valueText = cstring_to_text(valueString->data);\n\n\treturn valueText;\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/delete_protocol.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * delete_protocol.c\n *\n * Routine for deleting shards in the distributed cluster. This function takes\n * in a delete command and deletes a shard if and only if all rows in the shard\n * satisfy the conditions in the delete command.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <stddef.h>\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"port.h\"\n\n#include \"access/xact.h\"\n#include \"catalog/namespace.h\"\n#include \"commands/dbcommands.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pathnodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/restrictinfo.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/lock.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/errcodes.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/varlena.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_transaction.h\"\n\n\n/* Local functions forward declarations */\nstatic int DropShards(Oid relationId, char *schemaName, char *relationName,\n\t\t\t\t\t  List *deletableShardIntervalList, bool dropShardsMetadataOnly);\nstatic List * DropTaskList(Oid relationId, char *schemaName, char *relationName,\n\t\t\t\t\t\t   List *deletableShardIntervalList);\nstatic void ExecuteDropShardPlacementCommandRemotely(ShardPlacement *shardPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t const char *shardRelationName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t const char *\n\t\t\t\t\t\t\t\t\t\t\t\t\t dropShardPlacementCommand);\nstatic char * CreateDropShardPlacementCommand(const char *schemaName,\n\t\t\t\t\t\t\t\t\t\t\t  const char *shardRelationName,\n\t\t\t\t\t\t\t\t\t\t\t  char storageType);\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(master_apply_delete_command);\nPG_FUNCTION_INFO_V1(citus_drop_all_shards);\nPG_FUNCTION_INFO_V1(master_drop_all_shards);\nPG_FUNCTION_INFO_V1(master_drop_sequences);\n\n\n/*\n * master_apply_delete_command is a deprecated function for dropping shards\n * in an append-distributed tables.\n */\nDatum\nmaster_apply_delete_command(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"master_apply_delete_command has been deprecated\")));\n}\n\n\n/*\n * citus_drop_all_shards attempts to drop all shards for a given relation.\n * This function can be called even if the table has already been dropped.\n * In that case, the schema name and relation name arguments are used to\n * determine that table name. Otherwise, the relation ID is used and the\n * other arguments are ignored.\n */\nDatum\ncitus_drop_all_shards(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\ttext *schemaNameText = PG_GETARG_TEXT_P(1);\n\ttext *relationNameText = PG_GETARG_TEXT_P(2);\n\tbool dropShardsMetadataOnly = PG_GETARG_BOOL(3);\n\n\tchar *schemaName = text_to_cstring(schemaNameText);\n\tchar *relationName = text_to_cstring(relationNameText);\n\n\t/*\n\t * The SQL_DROP trigger calls this function even for tables that are\n\t * not distributed. In that case, silently ignore and return -1.\n\t */\n\tif (!IsCitusTableViaCatalog(relationId) || !EnableDDLPropagation)\n\t{\n\t\tPG_RETURN_INT32(-1);\n\t}\n\n\tEnsureCoordinator();\n\tCheckTableSchemaNameForDrop(relationId, &schemaName, &relationName);\n\n\t/*\n\t * citus_drop_all_shards is typically called from the DROP TABLE trigger,\n\t * but could be called by a user directly. Make sure we have an\n\t * AccessExclusiveLock to prevent any other commands from running on this table\n\t * concurrently.\n\t */\n\tLockRelationOid(relationId, AccessExclusiveLock);\n\n\tList *shardIntervalList = LoadUnsortedShardIntervalListViaCatalog(relationId);\n\tint droppedShardCount = DropShards(relationId, schemaName, relationName,\n\t\t\t\t\t\t\t\t\t   shardIntervalList, dropShardsMetadataOnly);\n\n\tPG_RETURN_INT32(droppedShardCount);\n}\n\n\n/*\n * master_drop_all_shards is a wrapper function for old UDF name.\n */\nDatum\nmaster_drop_all_shards(PG_FUNCTION_ARGS)\n{\n\tOid relationId = PG_GETARG_OID(0);\n\ttext *schemaNameText = PG_GETARG_TEXT_P(1);\n\ttext *relationNameText = PG_GETARG_TEXT_P(2);\n\tbool dropShardsMetadataOnly = false;\n\n\tLOCAL_FCINFO(local_fcinfo, 4);\n\n\tInitFunctionCallInfoData(*local_fcinfo, NULL, 4, InvalidOid, NULL, NULL);\n\n\tlocal_fcinfo->args[0].value = ObjectIdGetDatum(relationId);\n\tlocal_fcinfo->args[0].isnull = false;\n\tlocal_fcinfo->args[1].value = PointerGetDatum(schemaNameText);\n\tlocal_fcinfo->args[1].isnull = false;\n\tlocal_fcinfo->args[2].value = PointerGetDatum(relationNameText);\n\tlocal_fcinfo->args[2].isnull = false;\n\tlocal_fcinfo->args[3].value = BoolGetDatum(dropShardsMetadataOnly);\n\tlocal_fcinfo->args[3].isnull = false;\n\n\treturn citus_drop_all_shards(local_fcinfo);\n}\n\n\n/*\n * master_drop_sequences was previously used to drop sequences on workers\n * when using metadata syncing.\n *\n * It may still be called when dropping objects during CREATE EXTENSION,\n * hence the function remains in place.\n */\nDatum\nmaster_drop_sequences(PG_FUNCTION_ARGS)\n{\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * CheckTableSchemaNameForDrop errors out if the current user does not\n * have permission to un-distribute the given relation, taking into\n * account that it may be called from the drop trigger. If the table exists,\n * the function rewrites the given table and schema name.\n */\nvoid\nCheckTableSchemaNameForDrop(Oid relationId, char **schemaName, char **tableName)\n{\n\tchar *tempTableName = get_rel_name(relationId);\n\n\tif (tempTableName != NULL)\n\t{\n\t\t/* ensure proper values are used if the table exists */\n\t\tOid schemaId = get_rel_namespace(relationId);\n\t\t(*schemaName) = get_namespace_name(schemaId);\n\t\t(*tableName) = tempTableName;\n\n\t\tEnsureTableOwner(relationId);\n\t}\n}\n\n\n/*\n * DropShards drops all given shards in a relation. The id, name and schema\n * for the relation are explicitly provided, since this function may be\n * called when the table is already dropped.\n *\n * We mark shard placements that we couldn't drop as to be deleted later, but\n * we do delete the shard metadadata.\n *\n * If dropShardsMetadataOnly is true, then we don't send remote commands to drop the shards:\n * we only remove pg_dist_placement and pg_dist_shard rows.\n */\nstatic int\nDropShards(Oid relationId, char *schemaName, char *relationName,\n\t\t   List *deletableShardIntervalList, bool dropShardsMetadataOnly)\n{\n\tAssert(OidIsValid(relationId));\n\tAssert(schemaName != NULL);\n\tAssert(relationName != NULL);\n\n\tUseCoordinatedTransaction();\n\n\t/*\n\t * We will use below variable across this function to decide if we can\n\t * use local execution\n\t */\n\tint32 localGroupId = GetLocalGroupId();\n\n\t/* DROP table commands are currently only supported from the coordinator */\n\tAssert(localGroupId == COORDINATOR_GROUP_ID);\n\n\tUse2PCForCoordinatedTransaction();\n\n\tList *dropTaskList = DropTaskList(relationId, schemaName, relationName,\n\t\t\t\t\t\t\t\t\t  deletableShardIntervalList);\n\tbool shouldExecuteTasksLocally = ShouldExecuteTasksLocally(dropTaskList);\n\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, dropTaskList)\n\t{\n\t\tuint64 shardId = task->anchorShardId;\n\n\t\tShardPlacement *shardPlacement = NULL;\n\t\tforeach_declared_ptr(shardPlacement, task->taskPlacementList)\n\t\t{\n\t\t\tuint64 shardPlacementId = shardPlacement->placementId;\n\t\t\tint32 shardPlacementGroupId = shardPlacement->groupId;\n\n\t\t\tbool isLocalShardPlacement = (shardPlacementGroupId == localGroupId);\n\n\t\t\t/*\n\t\t\t * If this variable is true, that means the active DROP SCHEMA/DATABASE ... CASCADE\n\t\t\t * will drop the shard. If we try to drop it over another connection, we will\n\t\t\t * get into a distributed deadlock. Hence, if this variable is true we should just\n\t\t\t * delete the shard placement metadata and skip dropping the shard for now.\n\t\t\t */\n\t\t\tbool skipIfDropSchemaOrDBInProgress = isLocalShardPlacement &&\n\t\t\t\t\t\t\t\t\t\t\t\t  DropSchemaOrDBInProgress() &&\n\t\t\t\t\t\t\t\t\t\t\t\t  localGroupId == COORDINATOR_GROUP_ID;\n\n\t\t\t/*\n\t\t\t * We want to send commands to drop shards when both\n\t\t\t * skipIfDropSchemaOrDBInProgress and dropShardsMetadataOnly are false.\n\t\t\t */\n\t\t\tbool applyRemoteShardsDrop =\n\t\t\t\t!skipIfDropSchemaOrDBInProgress && !dropShardsMetadataOnly;\n\n\t\t\tif (applyRemoteShardsDrop)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * If it is a local placement of a distributed table or a reference table,\n\t\t\t\t * then execute the DROP command locally.\n\t\t\t\t */\n\t\t\t\tif (isLocalShardPlacement && shouldExecuteTasksLocally)\n\t\t\t\t{\n\t\t\t\t\tList *singleTaskList = list_make1(task);\n\n\t\t\t\t\tExecuteLocalUtilityTaskList(singleTaskList);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Either it was not a local placement or we could not use\n\t\t\t\t\t * local execution even if it was a local placement.\n\t\t\t\t\t * If it is the second case, then it is possibly because in\n\t\t\t\t\t * current transaction, some commands or queries connected\n\t\t\t\t\t * to local group as well.\n\t\t\t\t\t *\n\t\t\t\t\t * Regardless of the node is a remote node or the current node,\n\t\t\t\t\t * try to open a new connection (or use an existing one) to\n\t\t\t\t\t * connect to that node to drop the shard placement over that\n\t\t\t\t\t * remote connection.\n\t\t\t\t\t */\n\t\t\t\t\tconst char *dropShardPlacementCommand = TaskQueryString(task);\n\t\t\t\t\tExecuteDropShardPlacementCommandRemotely(shardPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t relationName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t dropShardPlacementCommand);\n\n\t\t\t\t\tif (isLocalShardPlacement)\n\t\t\t\t\t{\n\t\t\t\t\t\tSetLocalExecutionStatus(LOCAL_EXECUTION_DISABLED);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tDeleteShardPlacementRow(shardPlacementId);\n\t\t}\n\n\t\t/*\n\t\t * Now that we deleted all placements of the shard (or their metadata),\n\t\t * delete the shard metadata as well.\n\t\t */\n\t\tDeleteShardRow(shardId);\n\t}\n\n\tint droppedShardCount = list_length(deletableShardIntervalList);\n\n\treturn droppedShardCount;\n}\n\n\n/*\n * DropTaskList returns a list of tasks to execute a DROP command on shard\n * placements of distributed table. This is handled separately from other\n * DDL commands because we handle it via the DROP trigger, which is called\n * whenever a drop cascades.\n */\nstatic List *\nDropTaskList(Oid relationId, char *schemaName, char *relationName,\n\t\t\t List *deletableShardIntervalList)\n{\n\t/* resulting task list */\n\tList *taskList = NIL;\n\n\t/* enumerate the tasks when putting them to the taskList */\n\tint taskId = 1;\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, deletableShardIntervalList)\n\t{\n\t\tAssert(shardInterval->relationId == relationId);\n\n\t\tuint64 shardId = shardInterval->shardId;\n\t\tchar storageType = shardInterval->storageType;\n\n\t\tchar *shardRelationName = pstrdup(relationName);\n\n\t\t/* build shard relation name */\n\t\tAppendShardIdToName(&shardRelationName, shardId);\n\n\t\tchar *dropShardPlacementCommand =\n\t\t\tCreateDropShardPlacementCommand(schemaName, shardRelationName,\n\t\t\t\t\t\t\t\t\t\t\tstorageType);\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->jobId = INVALID_JOB_ID;\n\t\ttask->taskId = taskId++;\n\t\ttask->taskType = DDL_TASK;\n\t\tSetTaskQueryString(task, dropShardPlacementCommand);\n\t\ttask->dependentTaskList = NULL;\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\ttask->anchorShardId = shardId;\n\t\ttask->taskPlacementList = ShardPlacementList(shardId);\n\n\t\ttaskList = lappend(taskList, task);\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * ExecuteDropShardPlacementCommandRemotely executes the given DROP shard command\n * via remote critical connection.\n */\nstatic void\nExecuteDropShardPlacementCommandRemotely(ShardPlacement *shardPlacement,\n\t\t\t\t\t\t\t\t\t\t const char *relationName,\n\t\t\t\t\t\t\t\t\t\t const char *dropShardPlacementCommand)\n{\n\tAssert(shardPlacement != NULL);\n\tAssert(relationName != NULL);\n\tAssert(dropShardPlacementCommand != NULL);\n\n\tuint32 connectionFlags = FOR_DDL;\n\tMultiConnection *connection = GetPlacementConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t NULL);\n\n\t/*\n\t * This code-path doesn't support optional connections, so we don't expect\n\t * NULL connections.\n\t */\n\tAssert(connection != NULL);\n\n\tRemoteTransactionBeginIfNecessary(connection);\n\n\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t{\n\t\tchar *workerName = shardPlacement->nodeName;\n\t\tuint32 workerPort = shardPlacement->nodePort;\n\n\t\t/* build shard relation name */\n\t\tuint64 shardId = shardPlacement->shardId;\n\t\tchar *shardRelationName = pstrdup(relationName);\n\n\t\tAppendShardIdToName(&shardRelationName, shardId);\n\n\t\tereport(WARNING, (errmsg(\"could not connect to shard \\\"%s\\\" on node \"\n\t\t\t\t\t\t\t\t \"\\\"%s:%u\\\"\", shardRelationName, workerName,\n\t\t\t\t\t\t\t\t workerPort),\n\t\t\t\t\t\t  errdetail(\"Marking this shard placement for \"\n\t\t\t\t\t\t\t\t\t\"deletion\")));\n\n\t\tInsertCleanupOnSuccessRecordInCurrentTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardRelationName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardPlacement->groupId);\n\n\t\treturn;\n\t}\n\n\tMarkRemoteTransactionCritical(connection);\n\n\tExecuteCriticalRemoteCommand(connection, dropShardPlacementCommand);\n}\n\n\n/*\n * CreateDropShardPlacementCommand function builds the DROP command to drop\n * the given shard relation by qualifying it with schema name according to\n * shard relation's storage type.\n */\nstatic char *\nCreateDropShardPlacementCommand(const char *schemaName, const char *shardRelationName,\n\t\t\t\t\t\t\t\tchar storageType)\n{\n\tAssert(schemaName != NULL);\n\tAssert(shardRelationName != NULL);\n\n\tStringInfo workerDropQuery = makeStringInfo();\n\n\tconst char *quotedShardName = quote_qualified_identifier(schemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardRelationName);\n\n\t/* build workerDropQuery according to shard storage type */\n\tif (storageType == SHARD_STORAGE_TABLE)\n\t{\n\t\tappendStringInfo(workerDropQuery, DROP_REGULAR_TABLE_COMMAND,\n\t\t\t\t\t\t quotedShardName);\n\t}\n\telse if (storageType == SHARD_STORAGE_FOREIGN)\n\t{\n\t\tappendStringInfo(workerDropQuery, DROP_FOREIGN_TABLE_COMMAND,\n\t\t\t\t\t\t quotedShardName);\n\t}\n\telse\n\t{\n\t\t/* no other storage type is expected here */\n\t\tAssert(false);\n\t}\n\n\treturn workerDropQuery->data;\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/health_check.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * health_check.c\n *\n * UDFs to run health check operations by coordinating simple queries to test connectivity\n * between connection pairs in the cluster.\n *\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"utils/builtins.h\"\n\n#include \"distributed/argutils.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/worker_manager.h\"\n\n/* simple query to run on workers to check connectivity */\n#define CONNECTIVITY_CHECK_QUERY \"SELECT 1\"\n#define CONNECTIVITY_CHECK_COLUMNS 5\n\nPG_FUNCTION_INFO_V1(citus_check_connection_to_node);\nPG_FUNCTION_INFO_V1(citus_check_cluster_node_health);\n\nstatic bool CheckConnectionToNode(char *nodeName, uint32 nodePort);\n\nstatic void StoreAllConnectivityChecks(Tuplestorestate *tupleStore,\n\t\t\t\t\t\t\t\t\t   TupleDesc tupleDescriptor);\nstatic char * GetConnectivityCheckCommand(const char *nodeName, const uint32 nodePort);\n\n\n/*\n * citus_check_connection_to_node sends a simple query from a worker node to another\n * node, and returns success status.\n */\nDatum\ncitus_check_connection_to_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tchar *nodeName = PG_GETARG_TEXT_TO_CSTRING(0);\n\tuint32 nodePort = PG_GETARG_UINT32(1);\n\n\tbool success = CheckConnectionToNode(nodeName, nodePort);\n\tPG_RETURN_BOOL(success);\n}\n\n\n/*\n * CheckConnectionToNode sends a simple query to a node and returns success status\n */\nstatic bool\nCheckConnectionToNode(char *nodeName, uint32 nodePort)\n{\n\tint connectionFlags = 0;\n\tMultiConnection *connection = GetNodeConnection(connectionFlags, nodeName, nodePort);\n\tint responseStatus = ExecuteOptionalRemoteCommand(connection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  CONNECTIVITY_CHECK_QUERY, NULL);\n\n\treturn responseStatus == RESPONSE_OKAY;\n}\n\n\n/*\n * citus_check_cluster_node_health UDF performs connectivity checks from all the nodes to\n * all the nodes, and report success status\n */\nDatum\ncitus_check_cluster_node_health(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tStoreAllConnectivityChecks(tupleStore, tupleDescriptor);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * StoreAllConnectivityChecks performs connectivity checks from all the nodes to all the\n * nodes, and report success status.\n *\n * Algorithm is:\n * for sourceNode in activeReadableNodeList:\n *   c = connectToNode(sourceNode)\n *   for targetNode in activeReadableNodeList:\n *     result = c.execute(\"SELECT citus_check_connection_to_node(targetNode.name, targetNode.port\")\n *     emit sourceNode.name, sourceNode.port, targetNode.name, targetNode.port, result\n *\n * -- result -> true  -> connection attempt from source to target succeeded\n * -- result -> false -> connection attempt from source to target failed\n * -- result -> NULL  -> connection attempt from the current node to source node failed\n */\nstatic void\nStoreAllConnectivityChecks(Tuplestorestate *tupleStore, TupleDesc tupleDescriptor)\n{\n\tDatum values[CONNECTIVITY_CHECK_COLUMNS];\n\tbool isNulls[CONNECTIVITY_CHECK_COLUMNS];\n\n\t/*\n\t * Get all the readable node list so that we will check connectivity to followers in\n\t * the cluster as well.\n\t */\n\tList *workerNodeList = ActiveReadableNodeList();\n\n\t/* we want to check for connectivity in a deterministic order */\n\tworkerNodeList = SortList(workerNodeList, CompareWorkerNodes);\n\n\t/*\n\t * We iterate over the workerNodeList twice, for source and target worker nodes. This\n\t * operation is safe for foreach_declared_ptr macro, as long as we use different variables for\n\t * each iteration.\n\t */\n\tWorkerNode *sourceWorkerNode = NULL;\n\tforeach_declared_ptr(sourceWorkerNode, workerNodeList)\n\t{\n\t\tconst char *sourceNodeName = sourceWorkerNode->workerName;\n\t\tconst int sourceNodePort = sourceWorkerNode->workerPort;\n\t\tint32 connectionFlags = 0;\n\n\t\t/* open a connection to the source node using the synchronous api */\n\t\tMultiConnection *connectionToSourceNode =\n\t\t\tGetNodeConnection(connectionFlags, sourceNodeName, sourceNodePort);\n\n\t\t/* the second iteration over workerNodeList for the target worker nodes. */\n\t\tWorkerNode *targetWorkerNode = NULL;\n\t\tforeach_declared_ptr(targetWorkerNode, workerNodeList)\n\t\t{\n\t\t\tconst char *targetNodeName = targetWorkerNode->workerName;\n\t\t\tconst int targetNodePort = targetWorkerNode->workerPort;\n\n\t\t\tchar *connectivityCheckCommandToTargetNode =\n\t\t\t\tGetConnectivityCheckCommand(targetNodeName, targetNodePort);\n\n\t\t\tPGresult *result = NULL;\n\t\t\tint executionResult =\n\t\t\t\tExecuteOptionalRemoteCommand(connectionToSourceNode,\n\t\t\t\t\t\t\t\t\t\t\t connectivityCheckCommandToTargetNode,\n\t\t\t\t\t\t\t\t\t\t\t &result);\n\n\t\t\t/* get ready for the next tuple */\n\t\t\tmemset(values, 0, sizeof(values));\n\t\t\tmemset(isNulls, false, sizeof(isNulls));\n\n\t\t\tvalues[0] = PointerGetDatum(cstring_to_text(sourceNodeName));\n\t\t\tvalues[1] = Int32GetDatum(sourceNodePort);\n\t\t\tvalues[2] = PointerGetDatum(cstring_to_text(targetNodeName));\n\t\t\tvalues[3] = Int32GetDatum(targetNodePort);\n\n\t\t\t/*\n\t\t\t * If we could not send the query or the result was not ok, set success field\n\t\t\t * to NULL. This may indicate connection errors to a worker node, however that\n\t\t\t * node can potentially connect to other nodes.\n\t\t\t *\n\t\t\t * Therefore, we mark the success as NULL to indicate that the connectivity\n\t\t\t * status is unknown.\n\t\t\t */\n\t\t\tif (executionResult != RESPONSE_OKAY)\n\t\t\t{\n\t\t\t\tisNulls[4] = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tint rowIndex = 0;\n\t\t\t\tint columnIndex = 0;\n\t\t\t\tvalues[4] = BoolGetDatum(ParseBoolField(result, rowIndex, columnIndex));\n\t\t\t}\n\n\t\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);\n\n\t\t\tPQclear(result);\n\t\t\tForgetResults(connectionToSourceNode);\n\t\t}\n\t}\n}\n\n\n/*\n * GetConnectivityCheckCommand returns the command to check connections to a node\n */\nstatic char *\nGetConnectivityCheckCommand(const char *nodeName, const uint32 nodePort)\n{\n\tStringInfo connectivityCheckCommand = makeStringInfo();\n\tappendStringInfo(connectivityCheckCommand,\n\t\t\t\t\t \"SELECT citus_check_connection_to_node('%s', %d)\",\n\t\t\t\t\t nodeName, nodePort);\n\n\treturn connectivityCheckCommand->data;\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/isolate_shards.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * split_shards.c\n *\n * This file contains functions to split a shard according to a given\n * distribution column value.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n#include \"libpq-fe.h\"\n\n#include \"catalog/pg_class.h\"\n#include \"nodes/pg_list.h\"\n#include \"storage/lock.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/errcodes.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/typcache.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_split.h\"\n#include \"distributed/utils/distribution_column_map.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_transaction.h\"\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(isolate_tenant_to_new_shard);\nPG_FUNCTION_INFO_V1(worker_hash);\n\n\n/*\n * isolate_tenant_to_new_shard isolates a tenant to its own shard by spliting\n * the current matching shard.\n */\nDatum\nisolate_tenant_to_new_shard(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tOid relationId = PG_GETARG_OID(0);\n\tDatum inputDatum = PG_GETARG_DATUM(1);\n\ttext *cascadeOptionText = PG_GETARG_TEXT_P(2);\n\tOid shardTransferModeOid = PG_GETARG_OID(3);\n\n\tEnsureTableOwner(relationId);\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\n\tchar partitionMethod = cacheEntry->partitionMethod;\n\tif (partitionMethod != DISTRIBUTE_BY_HASH)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot isolate tenant because tenant isolation \"\n\t\t\t\t\t\t\t   \"is only support for hash distributed tables\")));\n\t}\n\n\tList *colocatedTableList = ColocatedTableList(relationId);\n\tint colocatedTableCount = list_length(colocatedTableList);\n\n\tOid inputDataType = get_fn_expr_argtype(fcinfo->flinfo, 1);\n\tchar *tenantIdString = DatumToString(inputDatum, inputDataType);\n\n\tchar *cascadeOptionString = text_to_cstring(cascadeOptionText);\n\tif (pg_strncasecmp(cascadeOptionString, \"CASCADE\", NAMEDATALEN) != 0 &&\n\t\tcolocatedTableCount > 1)\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot isolate tenant because \\\"%s\\\" has colocated \"\n\t\t\t\t\t\t\t   \"tables\", relationName),\n\t\t\t\t\t\terrhint(\"Use CASCADE option to isolate tenants for the \"\n\t\t\t\t\t\t\t\t\"colocated tables too. Example usage: \"\n\t\t\t\t\t\t\t\t\"isolate_tenant_to_new_shard('%s', '%s', 'CASCADE')\",\n\t\t\t\t\t\t\t\trelationName, tenantIdString)));\n\t}\n\n\tEnsureReferenceTablesExistOnAllNodes();\n\n\tVar *distributionColumn = DistPartitionKey(relationId);\n\n\t/* earlier we checked that the table was hash partitioned, so there should be a distribution column */\n\tAssert(distributionColumn != NULL);\n\n\tOid distributionColumnType = distributionColumn->vartype;\n\n\tDatum tenantIdDatum = StringToDatum(tenantIdString, distributionColumnType);\n\tShardInterval *sourceShard = FindShardInterval(tenantIdDatum, cacheEntry);\n\tif (sourceShard == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"tenant does not have a shard\")));\n\t}\n\n\tint shardMinValue = DatumGetInt32(sourceShard->minValue);\n\tint shardMaxValue = DatumGetInt32(sourceShard->maxValue);\n\tif (shardMinValue == shardMaxValue)\n\t{\n\t\tchar *tableName = get_rel_name(relationId);\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t(errmsg(\"table %s has already been isolated for the given value\",\n\t\t\t\t\t\t\t\tquote_identifier(tableName)))));\n\t}\n\n\tList *sourcePlacementList = ActiveShardPlacementList(sourceShard->shardId);\n\tif (list_length(sourcePlacementList) > 1)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot isolate tenants when using shard replication\")));\n\t}\n\n\tShardPlacement *sourceShardPlacement = linitial(sourcePlacementList);\n\n\t/* get hash function name */\n\tFmgrInfo *hashFunction = cacheEntry->hashFunction;\n\n\t/* get hashed value of the distribution value */\n\tDatum hashedValueDatum = FunctionCall1Coll(hashFunction,\n\t\t\t\t\t\t\t\t\t\t\t   cacheEntry->partitionColumn->varcollid,\n\t\t\t\t\t\t\t\t\t\t\t   tenantIdDatum);\n\tint hashedValue = DatumGetInt32(hashedValueDatum);\n\n\tList *shardSplitPointsList = NIL;\n\n\t/*\n\t * If the hash value lies at one of the boundaries, we only have a single\n\t * split point.\n\t */\n\tif (hashedValue == shardMinValue)\n\t{\n\t\tshardSplitPointsList = list_make1_int(hashedValue);\n\t}\n\telse if (hashedValue == shardMaxValue)\n\t{\n\t\tshardSplitPointsList = list_make1_int(hashedValue - 1);\n\t}\n\telse\n\t{\n\t\tshardSplitPointsList = list_make2_int(hashedValue - 1, hashedValue);\n\t}\n\n\t/* we currently place the isolated hash value into the same node */\n\tint sourceNodeId = sourceShardPlacement->nodeId;\n\tList *nodeIdsForPlacementList = list_make2_int(sourceNodeId, sourceNodeId);\n\n\tif (list_length(shardSplitPointsList) > 1)\n\t{\n\t\tnodeIdsForPlacementList = lappend_int(nodeIdsForPlacementList, sourceNodeId);\n\t}\n\n\tDistributionColumnMap *distributionColumnOverrides = NULL;\n\tList *sourceColocatedShardIntervalList = NIL;\n\tSplitMode splitMode = LookupSplitMode(shardTransferModeOid);\n\tSplitShard(splitMode,\n\t\t\t   ISOLATE_TENANT_TO_NEW_SHARD,\n\t\t\t   sourceShard->shardId,\n\t\t\t   shardSplitPointsList,\n\t\t\t   nodeIdsForPlacementList,\n\t\t\t   distributionColumnOverrides,\n\t\t\t   sourceColocatedShardIntervalList,\n\t\t\t   INVALID_COLOCATION_ID);\n\n\tcacheEntry = GetCitusTableCacheEntry(relationId);\n\tShardInterval *newShard = FindShardInterval(tenantIdDatum, cacheEntry);\n\n\tPG_RETURN_INT64(newShard->shardId);\n}\n\n\n/*\n * worker_hash returns the hashed value of the given value.\n */\nDatum\nworker_hash(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tDatum valueDatum = PG_GETARG_DATUM(0);\n\n\t/* figure out hash function from the data type */\n\tOid valueDataType = get_fn_expr_argtype(fcinfo->flinfo, 0);\n\tTypeCacheEntry *typeEntry = lookup_type_cache(valueDataType,\n\t\t\t\t\t\t\t\t\t\t\t\t  TYPECACHE_HASH_PROC_FINFO);\n\n\tif (typeEntry->hash_proc_finfo.fn_oid == InvalidOid)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot find a hash function for the input type\"),\n\t\t\t\t\t\terrhint(\"Cast input to a data type with a hash function.\")));\n\t}\n\n\tFmgrInfo *hashFunction = palloc0(sizeof(FmgrInfo));\n\tfmgr_info_copy(hashFunction, &(typeEntry->hash_proc_finfo), CurrentMemoryContext);\n\n\t/* calculate hash value */\n\tDatum hashedValueDatum =\n\t\tFunctionCall1Coll(hashFunction, PG_GET_COLLATION(), valueDatum);\n\n\tPG_RETURN_INT32(hashedValueDatum);\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/modify_multiple_shards.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * modify_multiple_shards.c\n *\t  UDF to run multi shard update/delete queries\n *\n * This file contains master_modify_multiple_shards function, which takes a update\n * or delete query and runs it worker shards of the distributed table. The distributed\n * modify operation can be done within a distributed transaction.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"catalog/pg_class.h\"\n#include \"commands/dbcommands.h\"\n#include \"commands/event_trigger.h\"\n#include \"nodes/makefuncs.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/restrictinfo.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/builtins.h\"\n#include \"utils/datum.h\"\n#include \"utils/inval.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_clauses.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_transaction.h\"\n\n\nPG_FUNCTION_INFO_V1(master_modify_multiple_shards);\n\n\n/*\n * master_modify_multiple_shards takes in a DELETE or UPDATE query string and\n * executes it. This is mainly provided for backwards compatibility, users\n * should use regular UPDATE and DELETE commands.\n */\nDatum\nmaster_modify_multiple_shards(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *queryText = PG_GETARG_TEXT_P(0);\n\tchar *queryString = text_to_cstring(queryText);\n\tRawStmt *rawStmt = (RawStmt *) ParseTreeRawStmt(queryString);\n\tNode *queryTreeNode = rawStmt->stmt;\n\n\tif (!IsA(queryTreeNode, DeleteStmt) && !IsA(queryTreeNode, UpdateStmt))\n\t{\n\t\tereport(ERROR, (errmsg(\"query \\\"%s\\\" is not a delete or update \"\n\t\t\t\t\t\t\t   \"statement\", queryString)));\n\t}\n\n\tereport(WARNING, (errmsg(\"master_modify_multiple_shards is deprecated and will be \"\n\t\t\t\t\t\t\t \"removed in a future release.\"),\n\t\t\t\t\t  errhint(\"Run the command directly\")));\n\n\tExecuteQueryStringIntoDestReceiver(queryString, NULL, None_Receiver);\n\n\tPG_RETURN_INT32(0);\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/node_promotion.c",
    "content": "#include \"postgres.h\"\n\n#include \"utils/fmgrprotos.h\"\n#include \"utils/pg_lsn.h\"\n\n#include \"distributed/argutils.h\"\n#include \"distributed/clonenode_utils.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/shard_rebalancer.h\"\n\n\nstatic void BlockAllWritesToWorkerNode(WorkerNode *workerNode);\nstatic bool GetNodeIsInRecoveryStatus(WorkerNode *workerNode);\nstatic void PromoteCloneNode(WorkerNode *cloneWorkerNode);\nstatic void EnsureSingleNodePromotion(WorkerNode *primaryNode);\n\nPG_FUNCTION_INFO_V1(citus_promote_clone_and_rebalance);\n\n/*\n * citus_promote_clone_and_rebalance promotes an inactive clone node to become\n * the new primary node, replacing its original primary node.\n *\n * This function performs the following steps:\n * 1. Validates that the clone node exists and is properly configured\n * 2. Ensures the clone is inactive and has a valid primary node reference\n * 3. Blocks all writes to the primary node to prevent data divergence\n * 4. Waits for the clone to catch up with the primary's WAL position\n * 5. Promotes the clone node to become a standalone primary\n * 6. Updates metadata to mark the clone as active and primary\n * 7. Rebalances shards between the old primary and new primary\n * 8. Returns information about the promotion and any shard movements\n *\n * Arguments:\n * - clone_nodeid: The node ID of the clone to promote\n * - catchUpTimeoutSeconds: Maximum time to wait for clone to catch up (default: 300)\n *\n * The function ensures data consistency by blocking writes during the promotion\n * process and verifying replication lag before proceeding.\n */\nDatum\ncitus_promote_clone_and_rebalance(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\t/* Ensure superuser and coordinator */\n\tEnsureSuperUser();\n\tEnsureCoordinator();\n\n\t/* Get clone_nodeid argument */\n\tint32 cloneNodeIdArg = PG_GETARG_INT32(0);\n\n\t/* Get catchUpTimeoutSeconds argument with default value of 300 */\n\tint32 catchUpTimeoutSeconds = PG_ARGISNULL(2) ? 300 : PG_GETARG_INT32(2);\n\n\t/* Lock pg_dist_node to prevent concurrent modifications during this operation */\n\tLockRelationOid(DistNodeRelationId(), RowExclusiveLock);\n\n\tWorkerNode *cloneNode = FindNodeAnyClusterByNodeId(cloneNodeIdArg);\n\tif (cloneNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Clone node with ID %d not found.\", cloneNodeIdArg)));\n\t}\n\n\tif (!cloneNode->nodeisclone || cloneNode->nodeprimarynodeid == 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Node %s:%d (ID %d) is not a valid clone or its primary node ID is not set.\",\n\t\t\t\t\t\t\tcloneNode->workerName, cloneNode->workerPort, cloneNode->\n\t\t\t\t\t\t\tnodeId)));\n\t}\n\n\tif (cloneNode->isActive)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Clone node %s:%d (ID %d) is already active and cannot be promoted.\",\n\t\t\t\t\t\t\tcloneNode->workerName, cloneNode->workerPort, cloneNode->\n\t\t\t\t\t\t\tnodeId)));\n\t}\n\n\tWorkerNode *primaryNode = FindNodeAnyClusterByNodeId(cloneNode->nodeprimarynodeid);\n\tif (primaryNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Primary node with ID %d (for clone %s:%d) not found.\",\n\t\t\t\t\t\t\t   cloneNode->nodeprimarynodeid, cloneNode->workerName,\n\t\t\t\t\t\t\t   cloneNode->workerPort)));\n\t}\n\n\tif (primaryNode->nodeisclone)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Primary node %s:%d (ID %d) is itself a clone.\",\n\t\t\t\t\t\t\t   primaryNode->workerName, primaryNode->workerPort,\n\t\t\t\t\t\t\t   primaryNode->nodeId)));\n\t}\n\n\tif (!primaryNode->isActive)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Primary node %s:%d (ID %d) is not active.\",\n\t\t\t\t\t\t\t   primaryNode->workerName, primaryNode->workerPort,\n\t\t\t\t\t\t\t   primaryNode->nodeId)));\n\t}\n\n\t/* Ensure the primary node is related to the clone node */\n\tif (primaryNode->nodeId != cloneNode->nodeprimarynodeid)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Clone node %s:%d (ID %d) is not a clone of the primary node %s:%d (ID %d).\",\n\t\t\t\t\t\t\tcloneNode->workerName, cloneNode->workerPort, cloneNode->\n\t\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t\tprimaryNode->workerName, primaryNode->workerPort,\n\t\t\t\t\t\t\tprimaryNode->nodeId)));\n\t}\n\n\tEnsureSingleNodePromotion(primaryNode);\n\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t \"Starting promotion process for clone node %s:%d (ID %d), original primary %s:%d (ID %d)\",\n\t\t\t\t\t\t cloneNode->workerName, cloneNode->workerPort, cloneNode->\n\t\t\t\t\t\t nodeId,\n\t\t\t\t\t\t primaryNode->workerName, primaryNode->workerPort, primaryNode\n\t\t\t\t\t\t ->nodeId)));\n\n\t/* Step 0: Check if clone is replica of provided primary node and is not synchronous */\n\tchar *operation = \"promote\";\n\tEnsureValidCloneMode(primaryNode, cloneNode->workerName, cloneNode->workerPort,\n\t\t\t\t\t\t operation);\n\n\t/* Step 1: Block Writes on Original Primary's Shards */\n\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t \"Blocking writes on shards of original primary node %s:%d (group %d)\",\n\t\t\t\t\t\t primaryNode->workerName, primaryNode->workerPort, primaryNode\n\t\t\t\t\t\t ->groupId)));\n\n\tBlockAllWritesToWorkerNode(primaryNode);\n\n\t/* Step 2: Wait for Clone to Catch Up */\n\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t \"Waiting for clone %s:%d to catch up with primary %s:%d (timeout: %d seconds)\",\n\t\t\t\t\t\t cloneNode->workerName, cloneNode->workerPort,\n\t\t\t\t\t\t primaryNode->workerName, primaryNode->workerPort,\n\t\t\t\t\t\t catchUpTimeoutSeconds)));\n\n\tbool caughtUp = false;\n\tconst int sleepIntervalSeconds = 5;\n\tint elapsedTimeSeconds = 0;\n\n\twhile (elapsedTimeSeconds < catchUpTimeoutSeconds)\n\t{\n\t\tuint64 repLag = GetReplicationLag(primaryNode, cloneNode);\n\t\tif (repLag <= 0)\n\t\t{\n\t\t\tcaughtUp = true;\n\t\t\tbreak;\n\t\t}\n\t\tpg_usleep(sleepIntervalSeconds * 1000000L);\n\t\telapsedTimeSeconds += sleepIntervalSeconds;\n\t}\n\n\tif (!caughtUp)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Clone %s:%d failed to catch up with primary %s:%d within %d seconds.\",\n\t\t\t\t\t\t\tcloneNode->workerName, cloneNode->workerPort,\n\t\t\t\t\t\t\tprimaryNode->workerName, primaryNode->workerPort,\n\t\t\t\t\t\t\tcatchUpTimeoutSeconds)));\n\t}\n\n\tereport(NOTICE, (errmsg(\"Clone %s:%d is now caught up with primary %s:%d.\",\n\t\t\t\t\t\t\tcloneNode->workerName, cloneNode->workerPort,\n\t\t\t\t\t\t\tprimaryNode->workerName, primaryNode->workerPort)));\n\n\n\t/* Step 3: PostgreSQL Clone Promotion */\n\tereport(NOTICE, (errmsg(\"Attempting to promote clone %s:%d via pg_promote().\",\n\t\t\t\t\t\t\tcloneNode->workerName, cloneNode->workerPort)));\n\n\tPromoteCloneNode(cloneNode);\n\n\t/* Step 4: Update Clone Metadata in pg_dist_node on Coordinator */\n\n\tereport(NOTICE, (errmsg(\"Updating metadata for promoted clone %s:%d (ID %d)\",\n\t\t\t\t\t\t\tcloneNode->workerName, cloneNode->workerPort, cloneNode->\n\t\t\t\t\t\t\tnodeId)));\n\tActivateCloneNodeAsPrimary(cloneNode);\n\n\t/* We need to sync metadata changes to all nodes before rebalancing shards\n\t * since the rebalancing algorithm depends on the latest metadata.\n\t */\n\tSyncNodeMetadataToNodes();\n\n\t/* Step 5: Split Shards Between Primary and Clone */\n\tSplitShardsBetweenPrimaryAndClone(primaryNode, cloneNode, PG_GETARG_NAME_OR_NULL(1))\n\t;\n\n\n\tTransactionModifiedNodeMetadata = true; /* Inform Citus about metadata change */\n\tTriggerNodeMetadataSyncOnCommit();      /* Ensure changes are propagated */\n\n\n\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t \"Clone node %s:%d (ID %d) metadata updated. It is now a primary\",\n\t\t\t\t\t\t cloneNode->workerName, cloneNode->workerPort, cloneNode->\n\t\t\t\t\t\t nodeId)));\n\n\n\t/* Step 6: Unblock Writes (should be handled by transaction commit) */\n\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t \"Clone node %s:%d (ID %d) successfully registered as a worker node\",\n\t\t\t\t\t\t cloneNode->workerName, cloneNode->workerPort, cloneNode->\n\t\t\t\t\t\t nodeId)));\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * PromoteCloneNode promotes a clone node to a primary node using PostgreSQL's\n * pg_promote() function.\n *\n * This function performs the following steps:\n * 1. Connects to the clone node\n * 2. Executes pg_promote(wait := true) to promote the clone to primary\n * 3. Reconnects to verify the promotion was successful\n * 4. Checks if the node is still in recovery mode (which would indicate failure)\n *\n * The function throws an ERROR if:\n * - Connection to the clone node fails\n * - The pg_promote() command fails\n * - The clone is still in recovery mode after promotion attempt\n *\n * On success, it logs a NOTICE message confirming the promotion.\n *\n * Note: This function assumes the clone has already been validated for promotion\n * (e.g., replication lag is acceptable, clone is not synchronous, etc.)\n */\nstatic void\nPromoteCloneNode(WorkerNode *cloneWorkerNode)\n{\n\t/* Step 1: Connect to the clone node */\n\tint connectionFlag = 0;\n\tMultiConnection *cloneConnection = GetNodeConnection(connectionFlag,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t cloneWorkerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t cloneWorkerNode->workerPort);\n\n\tif (PQstatus(cloneConnection->pgConn) != CONNECTION_OK)\n\t{\n\t\tReportConnectionError(cloneConnection, ERROR);\n\t}\n\n\t/* Step 2: Execute pg_promote() to promote the clone to primary */\n\tconst char *promoteQuery = \"SELECT pg_promote(wait := true);\";\n\tint resultCode = SendRemoteCommand(cloneConnection, promoteQuery);\n\tif (resultCode == 0)\n\t{\n\t\tReportConnectionError(cloneConnection, ERROR);\n\t}\n\tForgetResults(cloneConnection);\n\tCloseConnection(cloneConnection);\n\n\t/* Step 3: Reconnect and verify the promotion was successful */\n\tif (GetNodeIsInRecoveryStatus(cloneWorkerNode))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Failed to promote clone %s:%d (ID %d). It is still in recovery.\",\n\t\t\t\t\t\t\tcloneWorkerNode->workerName, cloneWorkerNode->workerPort,\n\t\t\t\t\t\t\tcloneWorkerNode->nodeId)));\n\t}\n\telse\n\t{\n\t\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t\t \"Clone node %s:%d (ID %d) has been successfully promoted.\",\n\t\t\t\t\t\t\t cloneWorkerNode->workerName, cloneWorkerNode->workerPort,\n\t\t\t\t\t\t\t cloneWorkerNode->nodeId)));\n\t}\n}\n\n\nstatic void\nBlockAllWritesToWorkerNode(WorkerNode *workerNode)\n{\n\tereport(NOTICE, (errmsg(\"Blocking all writes to worker node %s:%d (ID %d)\",\n\t\t\t\t\t\t\tworkerNode->workerName, workerNode->workerPort, workerNode->\n\t\t\t\t\t\t\tnodeId)));\n\n\tLockShardsInWorkerPlacementList(workerNode, AccessExclusiveLock);\n}\n\n\n/*\n * GetNodeIsInRecoveryStatus checks if a PostgreSQL node is currently in recovery mode.\n *\n * This function connects to the specified worker node and executes pg_is_in_recovery()\n * to determine if the node is still acting as a replica (in recovery) or has been\n * promoted to a primary (not in recovery).\n *\n * Arguments:\n * - workerNode: The WorkerNode to check recovery status for\n *\n * Returns:\n * - true if the node is in recovery mode (acting as a replica)\n * - false if the node is not in recovery mode (acting as a primary)\n *\n * The function will ERROR if:\n * - Cannot establish connection to the node\n * - The remote query fails\n * - The query result cannot be parsed\n *\n * This is used after promoting a clone node to verify that the\n * promotion was successful and the node is no longer in recovery mode.\n */\nstatic bool\nGetNodeIsInRecoveryStatus(WorkerNode *workerNode)\n{\n\tint connectionFlag = 0;\n\tMultiConnection *nodeConnection = GetNodeConnection(connectionFlag,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerNode->workerPort);\n\n\tif (PQstatus(nodeConnection->pgConn) != CONNECTION_OK)\n\t{\n\t\tReportConnectionError(nodeConnection, ERROR);\n\t}\n\n\tconst char *recoveryQuery = \"SELECT pg_is_in_recovery();\";\n\tint resultCode = SendRemoteCommand(nodeConnection, recoveryQuery);\n\tif (resultCode == 0)\n\t{\n\t\tReportConnectionError(nodeConnection, ERROR);\n\t}\n\n\tPGresult *result = GetRemoteCommandResult(nodeConnection, true);\n\tif (!IsResponseOK(result))\n\t{\n\t\tReportResultError(nodeConnection, result, ERROR);\n\t}\n\n\tList *recoveryStatusList = ReadFirstColumnAsText(result);\n\tif (list_length(recoveryStatusList) != 1)\n\t{\n\t\tPQclear(result);\n\t\tClearResults(nodeConnection, true);\n\t\tCloseConnection(nodeConnection);\n\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\"cannot parse recovery status result from %s:%d\",\n\t\t\t\t\t\t\t   workerNode->workerName,\n\t\t\t\t\t\t\t   workerNode->workerPort)));\n\t}\n\n\tStringInfo recoveryStatusInfo = (StringInfo) linitial(recoveryStatusList);\n\tbool isInRecovery = (strcmp(recoveryStatusInfo->data, \"t\") == 0) || (strcmp(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t recoveryStatusInfo\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"true\") == 0)\n\t;\n\n\tPQclear(result);\n\tForgetResults(nodeConnection);\n\tCloseConnection(nodeConnection);\n\n\treturn isInRecovery;\n}\n\n\n/*\n * EnsureSingleNodePromotion ensures that only one node promotion operation\n * can proceed at a time by acquiring necessary locks and checking for\n * conflicting operations.\n *\n * This function performs the following safety checks:\n * 1. Verifies no rebalance operations are currently running, as they would\n *    conflict with the shard redistribution that occurs during promotion\n * 2. Acquires exclusive placement colocation locks on all shards residing\n *    on the primary node's group to prevent concurrent shard operations\n *\n * The locks are acquired in shard ID order to prevent deadlocks when\n * multiple operations attempt to lock the same set of shards.\n *\n * Arguments:\n * - primaryNode: The primary node whose shards need to be locked\n *\n * Throws ERROR if:\n * - A rebalance operation is already running\n * - Unable to acquire necessary locks\n */\nstatic void\nEnsureSingleNodePromotion(WorkerNode *primaryNode)\n{\n\t/* Error out if some rebalancer is running */\n\tint64 jobId = 0;\n\tif (HasNonTerminalJobOfType(\"rebalance\", &jobId))\n\t{\n\t\tereport(ERROR, (\n\t\t\t\t\terrmsg(\"A rebalance operation is already running as job %ld\", jobId),\n\t\t\t\t\terrdetail(\"A rebalance was already scheduled as background job\"),\n\t\t\t\t\terrhint(\"To monitor progress, run: SELECT * FROM \"\n\t\t\t\t\t\t\t\"citus_rebalance_status();\")));\n\t}\n\tList *placementList = AllShardPlacementsOnNodeGroup(primaryNode->groupId);\n\n\t/* lock shards in order of shard id to prevent deadlock */\n\tplacementList = SortList(placementList, CompareShardPlacementsByShardId);\n\n\tGroupShardPlacement *placement = NULL;\n\tforeach_declared_ptr(placement, placementList)\n\t{\n\t\tint64 shardId = placement->shardId;\n\t\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\t\tOid distributedTableId = shardInterval->relationId;\n\n\t\tAcquirePlacementColocationLock(distributedTableId, ExclusiveLock, \"promote clone\")\n\t\t;\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/node_protocol.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * node_protocol.c\n *\t  Routines for requesting information from the master node for creating or\n *\t  updating shards.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <string.h>\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"access/attnum.h\"\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup.h\"\n#include \"access/htup_details.h\"\n#include \"access/skey.h\"\n#include \"access/stratnum.h\"\n#include \"access/sysattr.h\"\n#include \"access/tupdesc.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_index.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_seclabel.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/sequence.h\"\n#include \"foreign/foreign.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n#include \"storage/lock.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/palloc.h\"\n#include \"utils/relcache.h\"\n#include \"utils/ruleutils.h\"\n#include \"utils/varlena.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/namespace_utils.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/shared_library_init.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n\n/* Shard related configuration */\nint ShardCount = 32;\nint ShardReplicationFactor = 1; /* desired replication factor for shards */\nint NextShardId = 0;\nint NextPlacementId = 0;\n\nstatic void GatherIndexAndConstraintDefinitionListExcludingReplicaIdentity(Form_pg_index\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   indexForm,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   List **\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   indexDDLEventList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   int\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   indexFlags);\nstatic Datum WorkerNodeGetDatum(WorkerNode *workerNode, TupleDesc tupleDescriptor);\n\nstatic char * CitusCreateAlterColumnarTableSet(char *qualifiedRelationName,\n\t\t\t\t\t\t\t\t\t\t\t   const ColumnarOptions *options);\nstatic char * GetTableDDLCommandColumnar(void *context);\nstatic TableDDLCommand * ColumnarGetTableOptionsDDL(Oid relationId);\nstatic List * CreateSecurityLabelCommands(Oid relationId);\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(master_get_table_metadata);\nPG_FUNCTION_INFO_V1(master_get_table_ddl_events);\nPG_FUNCTION_INFO_V1(master_get_new_shardid);\nPG_FUNCTION_INFO_V1(master_get_new_placementid);\nPG_FUNCTION_INFO_V1(master_get_active_worker_nodes);\nPG_FUNCTION_INFO_V1(citus_get_active_worker_nodes);\nPG_FUNCTION_INFO_V1(master_get_round_robin_candidate_nodes);\nPG_FUNCTION_INFO_V1(master_stage_shard_row);\nPG_FUNCTION_INFO_V1(master_stage_shard_placement_row);\n\n\n/*\n * master_get_table_metadata is a deprecated UDF.\n */\nDatum\nmaster_get_table_metadata(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\terrmsg(\"master_get_table_metadata is deprecated\")));\n}\n\n\n/*\n * master_get_table_ddl_events takes in a relation name, and returns the set of\n * DDL commands needed to reconstruct the relation. The returned DDL commands\n * are similar in flavor to schema definitions that pgdump returns. The function\n * errors if given relation does not exist.\n */\nDatum\nmaster_get_table_ddl_events(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tFuncCallContext *functionContext = NULL;\n\tListCell *tableDDLEventCell = NULL;\n\n\t/*\n\t * On the very first call to this function, we first use the given relation\n\t * name to get to the relation. We then recreate the list of DDL statements\n\t * issued for this relation, and save the first statement's position in the\n\t * function context.\n\t */\n\tif (SRF_IS_FIRSTCALL())\n\t{\n\t\ttext *relationName = PG_GETARG_TEXT_P(0);\n\t\tOid relationId = ResolveRelationId(relationName, false);\n\t\tIncludeSequenceDefaults includeSequenceDefaults = NEXTVAL_SEQUENCE_DEFAULTS;\n\t\tIncludeIdentities includeIdentityDefaults = INCLUDE_IDENTITY;\n\n\n\t\t/* create a function context for cross-call persistence */\n\t\tfunctionContext = SRF_FIRSTCALL_INIT();\n\n\t\t/* switch to memory context appropriate for multiple function calls */\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(\n\t\t\tfunctionContext->multi_call_memory_ctx);\n\n\t\t/* allocate DDL statements, and then save position in DDL statements */\n\t\tbool creatingShellTableOnRemoteNode = false;\n\t\tList *tableDDLEventList = GetFullTableCreationCommands(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   includeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   includeIdentityDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   creatingShellTableOnRemoteNode);\n\t\ttableDDLEventCell = list_head(tableDDLEventList);\n\t\tListCellAndListWrapper *wrapper = palloc0(sizeof(ListCellAndListWrapper));\n\t\twrapper->list = tableDDLEventList;\n\t\twrapper->listCell = tableDDLEventCell;\n\t\tfunctionContext->user_fctx = wrapper;\n\n\t\tMemoryContextSwitchTo(oldContext);\n\t}\n\n\t/*\n\t * On every call to this function, we get the current position in the\n\t * statement list. We then iterate to the next position in the list and\n\t * return the current statement, if we have not yet reached the end of\n\t * list.\n\t */\n\tfunctionContext = SRF_PERCALL_SETUP();\n\n\tListCellAndListWrapper *wrapper =\n\t\t(ListCellAndListWrapper *) functionContext->user_fctx;\n\tif (wrapper->listCell != NULL)\n\t{\n\t\tTableDDLCommand *ddlStatement = (TableDDLCommand *) lfirst(wrapper->listCell);\n\t\tAssert(CitusIsA(ddlStatement, TableDDLCommand));\n\t\ttext *ddlStatementText = cstring_to_text(GetTableDDLCommand(ddlStatement));\n\n\t\twrapper->listCell = lnext(wrapper->list, wrapper->listCell);\n\n\t\tSRF_RETURN_NEXT(functionContext, PointerGetDatum(ddlStatementText));\n\t}\n\telse\n\t{\n\t\tSRF_RETURN_DONE(functionContext);\n\t}\n}\n\n\n/*\n * master_get_new_shardid is a user facing wrapper function around GetNextShardId()\n * which allocates and returns a unique shardId for the shard to be created.\n *\n * NB: This can be called by any user; for now we have decided that that's\n * ok. We might want to restrict this to users part of a specific role or such\n * at some later point.\n */\nDatum\nmaster_get_new_shardid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tuint64 shardId = GetNextShardId();\n\tDatum shardIdDatum = Int64GetDatum(shardId);\n\n\tPG_RETURN_DATUM(shardIdDatum);\n}\n\n\n/*\n * GetNextShardId allocates and returns a unique shardId for the shard to be\n * created. This allocation occurs both in shared memory and in write ahead\n * logs; writing to logs avoids the risk of having shardId collisions.\n *\n * Please note that the caller is still responsible for finalizing shard data\n * and the shardId with the master node.\n */\nuint64\nGetNextShardId()\n{\n\tOid savedUserId = InvalidOid;\n\tint savedSecurityContext = 0;\n\tuint64 shardId = 0;\n\n\t/*\n\t * In regression tests, we would like to generate shard IDs consistently\n\t * even if the tests run in parallel. Instead of the sequence, we can use\n\t * the next_shard_id GUC to specify which shard ID the current session should\n\t * generate next. The GUC is automatically increased by 1 every time a new\n\t * shard ID is generated.\n\t */\n\tif (NextShardId > 0)\n\t{\n\t\tshardId = NextShardId;\n\t\tNextShardId += 1;\n\n\t\treturn shardId;\n\t}\n\n\ttext *sequenceName = cstring_to_text(SHARDID_SEQUENCE_NAME);\n\tOid sequenceId = ResolveRelationId(sequenceName, false);\n\tDatum sequenceIdDatum = ObjectIdGetDatum(sequenceId);\n\n\tGetUserIdAndSecContext(&savedUserId, &savedSecurityContext);\n\tSetUserIdAndSecContext(CitusExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE);\n\n\t/* generate new and unique shardId from sequence */\n\tDatum shardIdDatum = DirectFunctionCall1(nextval_oid, sequenceIdDatum);\n\n\tSetUserIdAndSecContext(savedUserId, savedSecurityContext);\n\n\tshardId = DatumGetInt64(shardIdDatum);\n\n\treturn shardId;\n}\n\n\n/*\n * master_get_new_placementid is a user facing wrapper function around\n * GetNextPlacementId() which allocates and returns a unique placement id for the\n * placement to be created.\n *\n * NB: This can be called by any user; for now we have decided that that's\n * ok. We might want to restrict this to users part of a specific role or such\n * at some later point.\n */\nDatum\nmaster_get_new_placementid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tuint64 placementId = GetNextPlacementId();\n\tDatum placementIdDatum = Int64GetDatum(placementId);\n\n\tPG_RETURN_DATUM(placementIdDatum);\n}\n\n\n/*\n * GetNextPlacementId allocates and returns a unique placementId for\n * the placement to be created. This allocation occurs both in shared memory\n * and in write ahead logs; writing to logs avoids the risk of having placementId\n * collisions.\n *\n * NB: This can be called by any user; for now we have decided that that's\n * ok. We might want to restrict this to users part of a specific role or such\n * at some later point.\n */\nuint64\nGetNextPlacementId(void)\n{\n\tOid savedUserId = InvalidOid;\n\tint savedSecurityContext = 0;\n\tuint64 placementId = 0;\n\n\t/*\n\t * In regression tests, we would like to generate placement IDs consistently\n\t * even if the tests run in parallel. Instead of the sequence, we can use\n\t * the next_placement_id GUC to specify which shard ID the current session\n\t * should generate next. The GUC is automatically increased by 1 every time\n\t * a new placement ID is generated.\n\t */\n\tif (NextPlacementId > 0)\n\t{\n\t\tplacementId = NextPlacementId;\n\t\tNextPlacementId += 1;\n\n\t\treturn placementId;\n\t}\n\n\ttext *sequenceName = cstring_to_text(PLACEMENTID_SEQUENCE_NAME);\n\tOid sequenceId = ResolveRelationId(sequenceName, false);\n\tDatum sequenceIdDatum = ObjectIdGetDatum(sequenceId);\n\n\tGetUserIdAndSecContext(&savedUserId, &savedSecurityContext);\n\tSetUserIdAndSecContext(CitusExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE);\n\n\t/* generate new and unique placement id from sequence */\n\tDatum placementIdDatum = DirectFunctionCall1(nextval_oid, sequenceIdDatum);\n\n\tSetUserIdAndSecContext(savedUserId, savedSecurityContext);\n\n\tplacementId = DatumGetInt64(placementIdDatum);\n\n\treturn placementId;\n}\n\n\n/*\n * master_get_round_robin_candidate_nodes is a stub UDF to make pg_upgrade\n * work flawlessly while upgrading servers from 6.1. This implementation\n * will be removed after the UDF dropped on the sql side properly.\n */\nDatum\nmaster_get_round_robin_candidate_nodes(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\terrmsg(\"this function is deprecated and no longer is used\")));\n}\n\n\n/*\n * master_stage_shard_row is a stub UDF to make pg_upgrade\n * work flawlessly while upgrading servers from 6.1. This implementation\n * will be removed after the UDF dropped on the sql side properly.\n */\nDatum\nmaster_stage_shard_row(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\terrmsg(\"this function is deprecated and no longer is used\")));\n}\n\n\n/*\n * master_stage_shard_placement_row is a stub UDF to make pg_upgrade\n * work flawlessly while upgrading servers from 6.1. This implementation\n * will be removed after the UDF dropped on the sql side properly.\n */\nDatum\nmaster_stage_shard_placement_row(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\terrmsg(\"this function is deprecated and no longer is used\")));\n}\n\n\n/*\n * citus_get_active_worker_nodes returns a set of active worker host names and\n * port numbers in deterministic order. Currently we assume that all worker\n * nodes in pg_dist_node are active.\n */\nDatum\ncitus_get_active_worker_nodes(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tFuncCallContext *functionContext = NULL;\n\tuint32 workerNodeCount = 0;\n\n\tif (SRF_IS_FIRSTCALL())\n\t{\n\t\t/* create a function context for cross-call persistence */\n\t\tfunctionContext = SRF_FIRSTCALL_INIT();\n\n\t\t/* switch to memory context appropriate for multiple function calls */\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(\n\t\t\tfunctionContext->multi_call_memory_ctx);\n\n\t\tList *workerNodeList = ActiveReadableNonCoordinatorNodeList();\n\t\tworkerNodeCount = (uint32) list_length(workerNodeList);\n\n\t\tfunctionContext->user_fctx = workerNodeList;\n\t\tfunctionContext->max_calls = workerNodeCount;\n\n\t\t/*\n\t\t * This tuple descriptor must match the output parameters declared for\n\t\t * the function in pg_proc.\n\t\t */\n\t\tTupleDesc tupleDescriptor = CreateTemplateTupleDesc(WORKER_NODE_FIELDS);\n\t\tTupleDescInitEntry(tupleDescriptor, (AttrNumber) 1, \"node_name\",\n\t\t\t\t\t\t   TEXTOID, -1, 0);\n\t\tTupleDescInitEntry(tupleDescriptor, (AttrNumber) 2, \"node_port\",\n\t\t\t\t\t\t   INT8OID, -1, 0);\n\n\t\tfunctionContext->tuple_desc = BlessTupleDesc(tupleDescriptor);\n\n\t\tMemoryContextSwitchTo(oldContext);\n\t}\n\n\tfunctionContext = SRF_PERCALL_SETUP();\n\tuint32 workerNodeIndex = functionContext->call_cntr;\n\tworkerNodeCount = functionContext->max_calls;\n\n\tif (workerNodeIndex < workerNodeCount)\n\t{\n\t\tList *workerNodeList = functionContext->user_fctx;\n\t\tWorkerNode *workerNode = list_nth(workerNodeList, workerNodeIndex);\n\n\t\tDatum workerNodeDatum = WorkerNodeGetDatum(workerNode,\n\t\t\t\t\t\t\t\t\t\t\t\t   functionContext->tuple_desc);\n\n\t\tSRF_RETURN_NEXT(functionContext, workerNodeDatum);\n\t}\n\telse\n\t{\n\t\tSRF_RETURN_DONE(functionContext);\n\t}\n}\n\n\n/*\n * master_get_active_worker_nodes is a wrapper function for old UDF name.\n */\nDatum\nmaster_get_active_worker_nodes(PG_FUNCTION_ARGS)\n{\n\treturn citus_get_active_worker_nodes(fcinfo);\n}\n\n\n/* Finds the relationId from a potentially qualified relation name. */\nOid\nResolveRelationId(text *relationName, bool missingOk)\n{\n\t/* resolve relationId from passed in schema and relation name */\n\tList *relationNameList = textToQualifiedNameList(relationName);\n\tRangeVar *relation = makeRangeVarFromNameList(relationNameList);\n\tOid relationId = RangeVarGetRelid(relation, NoLock, missingOk);\n\n\treturn relationId;\n}\n\n\n/*\n * GetFullTableCreationCommands takes in a relationId, includeSequenceDefaults,\n * and returns the list of DDL commands needed to reconstruct the relation.\n * When includeSequenceDefaults is NEXTVAL_SEQUENCE_DEFAULTS, the function also creates\n * DEFAULT clauses for columns getting their default values from a sequence.\n * When it's WORKER_NEXTVAL_SEQUENCE_DEFAULTS, the function creates the DEFAULT\n * clause using worker_nextval('sequence') and not nextval('sequence')\n * These DDL commands are all palloced; and include the table's schema\n * definition, optional column storage and statistics definitions, and index\n * constraint and trigger definitions.\n * When IncludeIdentities is NO_IDENTITY, the function does not include identity column\n * specifications. When it's INCLUDE_IDENTITY it creates GENERATED .. AS IDENTIY clauses.\n */\nList *\nGetFullTableCreationCommands(Oid relationId,\n\t\t\t\t\t\t\t IncludeSequenceDefaults includeSequenceDefaults,\n\t\t\t\t\t\t\t IncludeIdentities includeIdentityDefaults,\n\t\t\t\t\t\t\t bool creatingShellTableOnRemoteNode)\n{\n\tList *tableDDLEventList = NIL;\n\n\tList *preLoadCreationCommandList =\n\t\tGetPreLoadTableCreationCommands(relationId, includeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\tincludeIdentityDefaults, NULL);\n\n\ttableDDLEventList = list_concat(tableDDLEventList, preLoadCreationCommandList);\n\n\tList *postLoadCreationCommandList =\n\t\tGetPostLoadTableCreationCommands(relationId, true, true);\n\n\tif (creatingShellTableOnRemoteNode)\n\t{\n\t\t/*\n\t\t * While creating shell tables, we need to associate dependencies between\n\t\t * sequences and the relation. We also need to add truncate trigger for it\n\t\t * if it is not the foreign table.\n\t\t */\n\t\tList *sequenceDependencyCommandList = SequenceDependencyCommandList(relationId);\n\t\ttableDDLEventList = list_concat(tableDDLEventList, sequenceDependencyCommandList);\n\n\t\tif (!IsForeignTable(relationId))\n\t\t{\n\t\t\tTableDDLCommand *truncateTriggerCommand = TruncateTriggerCreateCommand(\n\t\t\t\trelationId);\n\t\t\ttableDDLEventList = lappend(tableDDLEventList,\n\t\t\t\t\t\t\t\t\t\ttruncateTriggerCommand);\n\t\t}\n\n\t\t/*\n\t\t * For identity column sequences, we only need to modify\n\t\t * their min/max values to produce unique values on the worker nodes.\n\t\t */\n\t\tList *identitySequenceDependencyCommandList =\n\t\t\tIdentitySequenceDependencyCommandList(relationId);\n\t\ttableDDLEventList = list_concat(tableDDLEventList,\n\t\t\t\t\t\t\t\t\t\tidentitySequenceDependencyCommandList);\n\t}\n\n\ttableDDLEventList = list_concat(tableDDLEventList, postLoadCreationCommandList);\n\n\treturn tableDDLEventList;\n}\n\n\n/*\n * GetPostLoadTableCreationCommands takes in a relationId and returns the list\n * of DDL commands that should be applied after loading the data.\n */\nList *\nGetPostLoadTableCreationCommands(Oid relationId, bool includeIndexes,\n\t\t\t\t\t\t\t\t bool includeReplicaIdentity)\n{\n\tList *tableDDLEventList = NIL;\n\n\t/*\n\t * Include all the commands (e.g., create index, set index clustered\n\t * and set index statistics) regarding the indexes. Note that\n\t * running all these commands in parallel might fail as the\n\t * latter two depends on the first one. So, the caller should\n\t * execute the commands sequentially.\n\t */\n\tint indexFlags = INCLUDE_INDEX_ALL_STATEMENTS;\n\n\tif (includeIndexes && includeReplicaIdentity)\n\t{\n\t\tList *indexAndConstraintCommandList =\n\t\t\tGetTableIndexAndConstraintCommands(relationId, indexFlags);\n\t\ttableDDLEventList = list_concat(tableDDLEventList, indexAndConstraintCommandList);\n\t}\n\telse if (includeIndexes && !includeReplicaIdentity)\n\t{\n\t\t/*\n\t\t * Do not include the indexes/constraints that backs\n\t\t * replica identity, if any.\n\t\t */\n\t\tList *indexAndConstraintCommandList =\n\t\t\tGetTableIndexAndConstraintCommandsExcludingReplicaIdentity(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   indexFlags);\n\t\ttableDDLEventList = list_concat(tableDDLEventList, indexAndConstraintCommandList);\n\t}\n\n\tif (includeReplicaIdentity)\n\t{\n\t\tList *replicaIdentityEvents = GetTableReplicaIdentityCommand(relationId);\n\t\ttableDDLEventList = list_concat(tableDDLEventList, replicaIdentityEvents);\n\t}\n\n\tList *triggerCommands = GetExplicitTriggerCommandList(relationId);\n\ttableDDLEventList = list_concat(tableDDLEventList, triggerCommands);\n\n\tList *statisticsCommands = GetExplicitStatisticsCommandList(relationId);\n\ttableDDLEventList = list_concat(tableDDLEventList, statisticsCommands);\n\n\treturn tableDDLEventList;\n}\n\n\n/*\n * GetTableReplicaIdentityCommand returns the list of DDL commands to\n * (re)define the replica identity choice for a given table.\n */\nList *\nGetTableReplicaIdentityCommand(Oid relationId)\n{\n\tList *replicaIdentityCreateCommandList = NIL;\n\n\t/*\n\t * We skip non-relations because postgres does not support\n\t * ALTER TABLE .. REPLICA IDENTITY on non-relations.\n\t */\n\tchar relationKind = get_rel_relkind(relationId);\n\tif (relationKind != RELKIND_RELATION)\n\t{\n\t\treturn NIL;\n\t}\n\n\tchar *replicaIdentityCreateCommand = pg_get_replica_identity_command(relationId);\n\n\tif (replicaIdentityCreateCommand)\n\t{\n\t\treplicaIdentityCreateCommandList = lappend(\n\t\t\treplicaIdentityCreateCommandList,\n\t\t\tmakeTableDDLCommandString(replicaIdentityCreateCommand));\n\t}\n\n\treturn replicaIdentityCreateCommandList;\n}\n\n\n/*\n * GetPreLoadTableCreationCommands takes in a relationId, and returns the list of DDL\n * commands needed to reconstruct the relation, excluding indexes and constraints,\n * to facilitate faster data load.\n */\nList *\nGetPreLoadTableCreationCommands(Oid relationId,\n\t\t\t\t\t\t\t\tIncludeSequenceDefaults includeSequenceDefaults,\n\t\t\t\t\t\t\t\tIncludeIdentities includeIdentityDefaults,\n\t\t\t\t\t\t\t\tchar *accessMethod)\n{\n\tList *tableDDLEventList = NIL;\n\n\tint saveNestLevel = PushEmptySearchPath();\n\n\t/* fetch table schema and column option definitions */\n\tchar *tableSchemaDef = pg_get_tableschemadef_string(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tincludeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tincludeIdentityDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\taccessMethod);\n\tchar *tableColumnOptionsDef = pg_get_tablecolumnoptionsdef_string(relationId);\n\n\ttableDDLEventList = lappend(tableDDLEventList, makeTableDDLCommandString(\n\t\t\t\t\t\t\t\t\ttableSchemaDef));\n\tif (tableColumnOptionsDef != NULL)\n\t{\n\t\ttableDDLEventList = lappend(tableDDLEventList, makeTableDDLCommandString(\n\t\t\t\t\t\t\t\t\t\ttableColumnOptionsDef));\n\t}\n\n\n\t/* add columnar options for cstore tables */\n\tif (accessMethod == NULL && extern_IsColumnarTableAmTable(relationId))\n\t{\n\t\tTableDDLCommand *cstoreOptionsDDL = ColumnarGetTableOptionsDDL(relationId);\n\t\tif (cstoreOptionsDDL != NULL)\n\t\t{\n\t\t\ttableDDLEventList = lappend(tableDDLEventList, cstoreOptionsDDL);\n\t\t}\n\t}\n\n\tList *tableACLList = pg_get_table_grants(relationId);\n\tif (tableACLList != NIL)\n\t{\n\t\tchar *tableACLCommand = NULL;\n\t\tforeach_declared_ptr(tableACLCommand, tableACLList)\n\t\t{\n\t\t\ttableDDLEventList = lappend(tableDDLEventList,\n\t\t\t\t\t\t\t\t\t\tmakeTableDDLCommandString(tableACLCommand));\n\t\t}\n\t}\n\n\tchar *tableOwnerDef = TableOwnerResetCommand(relationId);\n\tif (tableOwnerDef != NULL)\n\t{\n\t\ttableDDLEventList = lappend(tableDDLEventList, makeTableDDLCommandString(\n\t\t\t\t\t\t\t\t\t\ttableOwnerDef));\n\t}\n\n\tList *tableRowLevelSecurityCommands = GetTableRowLevelSecurityCommands(relationId);\n\ttableDDLEventList = list_concat(tableDDLEventList, tableRowLevelSecurityCommands);\n\n\tList *policyCommands = CreatePolicyCommands(relationId);\n\ttableDDLEventList = list_concat(tableDDLEventList, policyCommands);\n\n\tList *securityLabelCommands = CreateSecurityLabelCommands(relationId);\n\ttableDDLEventList = list_concat(tableDDLEventList, securityLabelCommands);\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n\n\treturn tableDDLEventList;\n}\n\n\n/*\n * GetTableIndexAndConstraintCommands returns the list of DDL commands to\n * (re)create indexes and constraints for a given table.\n */\nList *\nGetTableIndexAndConstraintCommands(Oid relationId, int indexFlags)\n{\n\treturn ExecuteFunctionOnEachTableIndex(relationId,\n\t\t\t\t\t\t\t\t\t\t   GatherIndexAndConstraintDefinitionList,\n\t\t\t\t\t\t\t\t\t\t   indexFlags);\n}\n\n\n/*\n * GetTableIndexAndConstraintCommands returns the list of DDL commands to\n * (re)create indexes and constraints for a given table.\n */\nList *\nGetTableIndexAndConstraintCommandsExcludingReplicaIdentity(Oid relationId, int indexFlags)\n{\n\treturn ExecuteFunctionOnEachTableIndex(relationId,\n\t\t\t\t\t\t\t\t\t\t   GatherIndexAndConstraintDefinitionListExcludingReplicaIdentity,\n\t\t\t\t\t\t\t\t\t\t   indexFlags);\n}\n\n\n/*\n * GatherIndexAndConstraintDefinitionListExcludingReplicaIdentity is a wrapper around\n * GatherIndexAndConstraintDefinitionList(), which only excludes the indexes or\n * constraints that back the replica identity.\n */\nstatic void\nGatherIndexAndConstraintDefinitionListExcludingReplicaIdentity(Form_pg_index indexForm,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   List **indexDDLEventList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   int indexFlags)\n{\n\tOid relationId = indexForm->indrelid;\n\tRelation relation = table_open(relationId, AccessShareLock);\n\n\tOid replicaIdentityIndex = GetRelationIdentityOrPK(relation);\n\n\tif (replicaIdentityIndex == indexForm->indexrelid)\n\t{\n\t\t/* this index is backing the replica identity, so skip */\n\t\ttable_close(relation, NoLock);\n\t\treturn;\n\t}\n\n\tGatherIndexAndConstraintDefinitionList(indexForm, indexDDLEventList, indexFlags);\n\n\ttable_close(relation, NoLock);\n}\n\n\n/*\n * Get replica identity index or if it is not defined a primary key.\n *\n * If neither is defined, returns InvalidOid.\n *\n * Inspired from postgres/src/backend/replication/logical/worker.c\n */\nOid\nGetRelationIdentityOrPK(Relation rel)\n{\n\tOid idxoid = RelationGetReplicaIndex(rel);\n\n\tif (!OidIsValid(idxoid))\n\t{\n/* Determine the index OID of the primary key (PG18 adds a second parameter) */\n#if PG_VERSION_NUM >= PG_VERSION_18\n\t\tidxoid = RelationGetPrimaryKeyIndex(rel, false /* deferred_ok */);\n#else\n\t\tidxoid = RelationGetPrimaryKeyIndex(rel);\n#endif\n\t}\n\n\treturn idxoid;\n}\n\n\n/*\n * GatherIndexAndConstraintDefinitionList adds the DDL command for the given index.\n */\nvoid\nGatherIndexAndConstraintDefinitionList(Form_pg_index indexForm, List **indexDDLEventList,\n\t\t\t\t\t\t\t\t\t   int indexFlags)\n{\n\t/* generate fully-qualified names */\n\tint saveNestLevel = PushEmptySearchPath();\n\n\tOid indexId = indexForm->indexrelid;\n\tbool indexImpliedByConstraint = IndexImpliedByAConstraint(indexForm);\n\n\t/* get the corresponding constraint or index statement */\n\tif (indexImpliedByConstraint)\n\t{\n\t\tif (indexFlags & INCLUDE_CREATE_CONSTRAINT_STATEMENTS)\n\t\t{\n\t\t\tOid constraintId = get_index_constraint(indexId);\n\t\t\tAssert(constraintId != InvalidOid);\n\n\t\t\t/* include constraints backed by indexes only when explicitly asked */\n\t\t\tchar *statementDef = pg_get_constraintdef_command(constraintId);\n\t\t\t*indexDDLEventList =\n\t\t\t\tlappend(*indexDDLEventList,\n\t\t\t\t\t\tmakeTableDDLCommandString(statementDef));\n\t\t}\n\t}\n\telse if (indexFlags & INCLUDE_CREATE_INDEX_STATEMENTS)\n\t{\n\t\t/*\n\t\t * Include indexes that are not backing constraints only when\n\t\t * explicitly asked.\n\t\t */\n\t\tchar *statementDef = pg_get_indexdef_string(indexId);\n\t\t*indexDDLEventList = lappend(*indexDDLEventList,\n\t\t\t\t\t\t\t\t\t makeTableDDLCommandString(statementDef));\n\t}\n\n\t/* if table is clustered on this index, append definition to the list */\n\tif ((indexFlags & INCLUDE_INDEX_CLUSTERED_STATEMENTS) &&\n\t\tindexForm->indisclustered)\n\t{\n\t\tchar *clusteredDef = pg_get_indexclusterdef_string(indexId);\n\t\tAssert(clusteredDef != NULL);\n\n\t\t*indexDDLEventList = lappend(*indexDDLEventList, makeTableDDLCommandString(\n\t\t\t\t\t\t\t\t\t\t clusteredDef));\n\t}\n\n\t/* we need alter index commands for altered targets on expression indexes */\n\tif (indexFlags & INCLUDE_INDEX_STATISTICS_STATEMENTTS)\n\t{\n\t\tList *alterIndexStatisticsCommands = GetAlterIndexStatisticsCommands(indexId);\n\t\t*indexDDLEventList = list_concat(*indexDDLEventList,\n\t\t\t\t\t\t\t\t\t\t alterIndexStatisticsCommands);\n\t}\n\n\t/* revert back to original search_path */\n\tPopEmptySearchPath(saveNestLevel);\n}\n\n\n/*\n * GetTableRowLevelSecurityCommands takes in a relationId, and returns the list of\n * commands needed to reconstruct the row level security policy.\n */\nList *\nGetTableRowLevelSecurityCommands(Oid relationId)\n{\n\tList *rowLevelSecurityCommandList = NIL;\n\n\tList *rowLevelSecurityEnableCommands = pg_get_row_level_security_commands(relationId);\n\n\tchar *rowLevelSecurityCommand = NULL;\n\tforeach_declared_ptr(rowLevelSecurityCommand, rowLevelSecurityEnableCommands)\n\t{\n\t\trowLevelSecurityCommandList = lappend(\n\t\t\trowLevelSecurityCommandList,\n\t\t\tmakeTableDDLCommandString(rowLevelSecurityCommand));\n\t}\n\n\treturn rowLevelSecurityCommandList;\n}\n\n\n/*\n * CreateSecurityLabelCommands - return the SECURITY LABEL commands on\n * the table identified by relationId. It is used by GetPreLoadTableCreationCommands()\n * to reconstruct the security labels on the table and its columns.\n */\nstatic List *\nCreateSecurityLabelCommands(Oid relationId)\n{\n\tList *securityLabelCommands = NIL;\n\n\tif (!RegularTable(relationId)) /* should be an Assert ? */\n\t{\n\t\treturn securityLabelCommands;\n\t}\n\n\tRelation pg_seclabel = table_open(SecLabelRelationId, AccessShareLock);\n\tScanKeyData skey[1];\n\tScanKeyInit(&skey[0], Anum_pg_seclabel_objoid, BTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(relationId));\n\tSysScanDesc scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId,\n\t\t\t\t\t\t\t\t\t\t  true, NULL, 1, &skey[0]);\n\tHeapTuple tuple = NULL;\n\tList *table_name = NIL;\n\tRelation relation = NULL;\n\tTupleDesc tupleDescriptor = NULL;\n\tList *securityLabelStmts = NULL;\n\tListCell *lc;\n\n\twhile (HeapTupleIsValid(tuple = systable_getnext(scan)))\n\t{\n\t\tSecLabelStmt *secLabelStmt = makeNode(SecLabelStmt);\n\n\t\tif (relation == NULL)\n\t\t{\n\t\t\trelation = relation_open(relationId, AccessShareLock);\n\t\t\tif (!RelationIsVisible(relationId))\n\t\t\t{\n\t\t\t\tchar *nsname = get_namespace_name(RelationGetNamespace(relation));\n\t\t\t\ttable_name = lappend(table_name, makeString(nsname));\n\t\t\t}\n\t\t\tchar *relname = get_rel_name(relationId);\n\t\t\ttable_name = lappend(table_name, makeString(relname));\n\t\t}\n\n\t\tDatum datumArray[Natts_pg_seclabel];\n\t\tbool isNullArray[Natts_pg_seclabel];\n\n\t\theap_deform_tuple(tuple, RelationGetDescr(pg_seclabel), datumArray,\n\t\t\t\t\t\t  isNullArray);\n\t\tint subObjectId = DatumGetInt32(\n\t\t\tdatumArray[Anum_pg_seclabel_objsubid - 1]);\n\t\tsecLabelStmt->provider = TextDatumGetCString(\n\t\t\tdatumArray[Anum_pg_seclabel_provider - 1]);\n\t\tsecLabelStmt->label = TextDatumGetCString(\n\t\t\tdatumArray[Anum_pg_seclabel_label - 1]);\n\n\t\tif (subObjectId > 0)\n\t\t{\n\t\t\t/* Its a column; construct the name */\n\t\t\tsecLabelStmt->objtype = OBJECT_COLUMN;\n\t\t\tList *col_name = list_copy(table_name);\n\n\t\t\tif (tupleDescriptor == NULL)\n\t\t\t{\n\t\t\t\ttupleDescriptor = RelationGetDescr(relation);\n\t\t\t}\n\n\t\t\tForm_pg_attribute attrForm = TupleDescAttr(tupleDescriptor, subObjectId - 1);\n\t\t\tchar *attributeName = NameStr(attrForm->attname);\n\t\t\tcol_name = lappend(col_name, makeString(attributeName));\n\n\t\t\tsecLabelStmt->object = (Node *) col_name;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tAssert(subObjectId == 0);\n\t\t\tsecLabelStmt->objtype = OBJECT_TABLE;\n\t\t\tsecLabelStmt->object = (Node *) table_name;\n\t\t}\n\n\t\tsecurityLabelStmts = lappend(securityLabelStmts, secLabelStmt);\n\t}\n\n\tforeach(lc, securityLabelStmts)\n\t{\n\t\tNode *stmt = (Node *) lfirst(lc);\n\t\tchar *secLabelStmtString = DeparseTreeNode(stmt);\n\t\tTableDDLCommand *secLabelCommand = makeTableDDLCommandString(secLabelStmtString);\n\t\tsecurityLabelCommands = lappend(securityLabelCommands, secLabelCommand);\n\t}\n\n\tsystable_endscan(scan);\n\ttable_close(pg_seclabel, AccessShareLock);\n\n\tif (relation != NULL)\n\t{\n\t\trelation_close(relation, AccessShareLock);\n\t}\n\n\treturn securityLabelCommands;\n}\n\n\n/*\n * IndexImpliedByAConstraint is a helper function to be used while scanning\n * pg_index. It returns true if the index identified by the given indexForm is\n * implied by a constraint. Note that caller is responsible for passing a valid\n * indexFrom, which means an alive heap tuple which is of form Form_pg_index.\n */\nbool\nIndexImpliedByAConstraint(Form_pg_index indexForm)\n{\n\tAssert(indexForm != NULL);\n\n\tbool indexImpliedByConstraint = false;\n\n\t/*\n\t * A primary key index is always created by a constraint statement.\n\t * A unique key index or exclusion index is created by a constraint\n\t * if and only if the index has a corresponding constraint entry in\n\t * pg_depend. Any other index form is never associated with a constraint.\n\t */\n\tif (indexForm->indisprimary)\n\t{\n\t\tindexImpliedByConstraint = true;\n\t}\n\telse if (indexForm->indisunique || indexForm->indisexclusion)\n\t{\n\t\tOid constraintId = get_index_constraint(indexForm->indexrelid);\n\n\t\tindexImpliedByConstraint = OidIsValid(constraintId);\n\t}\n\n\treturn indexImpliedByConstraint;\n}\n\n\n/*\n * ShardStorageType returns the shard storage type according to relation type.\n */\nchar\nShardStorageType(Oid relationId)\n{\n\tchar shardStorageType = 0;\n\n\tchar relationType = get_rel_relkind(relationId);\n\tif (RegularTable(relationId))\n\t{\n\t\tshardStorageType = SHARD_STORAGE_TABLE;\n\t}\n\telse if (relationType == RELKIND_FOREIGN_TABLE)\n\t{\n\t\tshardStorageType = SHARD_STORAGE_FOREIGN;\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"unexpected relation type: %c\", relationType)));\n\t}\n\n\treturn shardStorageType;\n}\n\n\n/*\n * IsCoordinator function returns true if this node is identified as the\n * schema/coordinator/master node of the cluster.\n */\nbool\nIsCoordinator(void)\n{\n\treturn (GetLocalGroupId() == COORDINATOR_GROUP_ID);\n}\n\n\n/*\n * WorkerNodeGetDatum converts the worker node passed to it into its datum\n * representation. To do this, the function first creates the heap tuple from\n * the worker node name and port. Then, the function converts the heap tuple\n * into a datum and returns it.\n */\nstatic Datum\nWorkerNodeGetDatum(WorkerNode *workerNode, TupleDesc tupleDescriptor)\n{\n\tDatum values[WORKER_NODE_FIELDS];\n\tbool isNulls[WORKER_NODE_FIELDS];\n\n\tmemset(values, 0, sizeof(values));\n\tmemset(isNulls, false, sizeof(isNulls));\n\n\tvalues[0] = CStringGetTextDatum(workerNode->workerName);\n\tvalues[1] = Int64GetDatum((int64) workerNode->workerPort);\n\n\tHeapTuple workerNodeTuple = heap_form_tuple(tupleDescriptor, values, isNulls);\n\tDatum workerNodeDatum = HeapTupleGetDatum(workerNodeTuple);\n\n\treturn workerNodeDatum;\n}\n\n\n/*\n * DistributedTableReplicationIsEnabled returns true if distributed table shards\n * are replicated according to ShardReplicationFactor.\n */\nbool\nDistributedTableReplicationIsEnabled()\n{\n\treturn (ShardReplicationFactor > 1);\n}\n\n\n/*\n * makeTableDDLCommandString creates a TableDDLCommand based on a constant string. If the\n * TableDDLCommand is turned into a sharded table command the constant will be wrapped in\n * worker_apply_shard_ddl_command with the target shardId. If the command applies to an\n * un-sharded table (eg. mx) the command is applied as is.\n */\nTableDDLCommand *\nmakeTableDDLCommandString(char *commandStr)\n{\n\tTableDDLCommand *command = CitusMakeNode(TableDDLCommand);\n\n\tcommand->type = TABLE_DDL_COMMAND_STRING;\n\tcommand->commandStr = commandStr;\n\n\treturn command;\n}\n\n\n/*\n * makeTableDDLCommandString creates an implementation of TableDDLCommand that creates the\n * final sql command based on function pointers being passed.\n */\nTableDDLCommand *\nmakeTableDDLCommandFunction(TableDDLFunction function,\n\t\t\t\t\t\t\tTableDDLShardedFunction shardedFunction,\n\t\t\t\t\t\t\tvoid *context)\n{\n\tTableDDLCommand *command = CitusMakeNode(TableDDLCommand);\n\n\t/*\n\t * Function pointers are called later without verifying them not being NULL. Guard\n\t * developers from making a mistake with them directly when they could be made.\n\t */\n\tAssert(function != NULL);\n\tAssert(shardedFunction != NULL);\n\n\tcommand->type = TABLE_DDL_COMMAND_FUNCTION;\n\tcommand->function.function = function;\n\tcommand->function.shardedFunction = shardedFunction;\n\tcommand->function.context = context;\n\n\treturn command;\n}\n\n\n/*\n * GetShardedTableDDLCommandString is the internal function for TableDDLCommand objects\n * created with makeTableDDLCommandString.\n */\nstatic char *\nGetShardedTableDDLCommandString(TableDDLCommand *command, uint64 shardId,\n\t\t\t\t\t\t\t\tchar *schemaName)\n{\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tAssert(command->type == TABLE_DDL_COMMAND_STRING);\n\n\tchar *escapedDDLCommand = quote_literal_cstr(command->commandStr);\n\n\tif (schemaName != NULL && strcmp(schemaName, \"public\") != 0)\n\t{\n\t\tchar *escapedSchemaName = quote_literal_cstr(schemaName);\n\t\tappendStringInfo(&buf, WORKER_APPLY_SHARD_DDL_COMMAND, shardId, escapedSchemaName,\n\t\t\t\t\t\t escapedDDLCommand);\n\t}\n\telse\n\t{\n\t\tappendStringInfo(&buf, WORKER_APPLY_SHARD_DDL_COMMAND_WITHOUT_SCHEMA, shardId,\n\t\t\t\t\t\t escapedDDLCommand);\n\t}\n\n\treturn buf.data;\n}\n\n\n/*\n * GetTableDDLCommandString is the internal function for TableDDLCommand objects created\n * with makeTableDDLCommandString to return the non-sharded version of the ddl command.\n */\nstatic char *\nGetTableDDLCommandString(TableDDLCommand *command)\n{\n\tAssert(command->type == TABLE_DDL_COMMAND_STRING);\n\treturn command->commandStr;\n}\n\n\n/*\n * GetShardedTableDDLCommand returns the ddl command expressed by this TableDDLCommand\n * where all applicable names are transformed into the names for a shard identified by\n * shardId\n *\n * schemaName is deprecated but used for TableDDLCommandString. All other implementations\n * will need to rely solely on the shardId.\n */\nchar *\nGetShardedTableDDLCommand(TableDDLCommand *command, uint64 shardId, char *schemaName)\n{\n\tswitch (command->type)\n\t{\n\t\tcase TABLE_DDL_COMMAND_STRING:\n\t\t{\n\t\t\treturn GetShardedTableDDLCommandString(command, shardId, schemaName);\n\t\t}\n\n\t\tcase TABLE_DDL_COMMAND_FUNCTION:\n\t\t{\n\t\t\treturn command->function.shardedFunction(shardId, command->function.context);\n\t\t}\n\t}\n\n\t/* unreachable: compiler should warn/error when not all cases are covered above */\n\tereport(ERROR, (errmsg(\"unsupported TableDDLCommand: %d\", command->type)));\n}\n\n\n/*\n * GetTableDDLCommand returns the ddl command expressed by this TableDDLCommand where all\n * table names are targeting the base table, not any shards.\n */\nchar *\nGetTableDDLCommand(TableDDLCommand *command)\n{\n\tswitch (command->type)\n\t{\n\t\tcase TABLE_DDL_COMMAND_STRING:\n\t\t{\n\t\t\treturn GetTableDDLCommandString(command);\n\t\t}\n\n\t\tcase TABLE_DDL_COMMAND_FUNCTION:\n\t\t{\n\t\t\treturn command->function.function(command->function.context);\n\t\t}\n\t}\n\n\t/* unreachable: compiler should warn/error when not all cases are covered above */\n\tereport(ERROR, (errmsg(\"unsupported TableDDLCommand: %d\", command->type)));\n}\n\n\n/*\n * CitusCreateAlterColumnarTableSet generates a portable\n */\nstatic char *\nCitusCreateAlterColumnarTableSet(char *qualifiedRelationName,\n\t\t\t\t\t\t\t\t const ColumnarOptions *options)\n{\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tappendStringInfo(&buf,\n\t\t\t\t\t \"ALTER TABLE %s SET (\"\n\t\t\t\t\t \"columnar.chunk_group_row_limit = %d, \"\n\t\t\t\t\t \"columnar.stripe_row_limit = %lu, \"\n\t\t\t\t\t \"columnar.compression_level = %d, \"\n\t\t\t\t\t \"columnar.compression = %s);\",\n\t\t\t\t\t qualifiedRelationName,\n\t\t\t\t\t options->chunkRowCount,\n\t\t\t\t\t options->stripeRowCount,\n\t\t\t\t\t options->compressionLevel,\n\t\t\t\t\t quote_literal_cstr(extern_CompressionTypeStr(\n\t\t\t\t\t\t\t\t\t\t\toptions->compressionType)));\n\n\treturn buf.data;\n}\n\n\n/*\n * GetTableDDLCommandColumnar is an internal function used to turn a\n * ColumnarTableDDLContext stored on the context of a TableDDLCommandFunction into a sql\n * command that will be executed against a table. The resulting command will set the\n * options of the table to the same options as the relation on the coordinator.\n */\nstatic char *\nGetTableDDLCommandColumnar(void *context)\n{\n\tColumnarTableDDLContext *tableDDLContext = (ColumnarTableDDLContext *) context;\n\n\tchar *qualifiedShardName = quote_qualified_identifier(tableDDLContext->schemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  tableDDLContext->relationName);\n\n\treturn CitusCreateAlterColumnarTableSet(qualifiedShardName,\n\t\t\t\t\t\t\t\t\t\t\t&tableDDLContext->options);\n}\n\n\n/*\n * GetShardedTableDDLCommandColumnar is an internal function used to turn a\n * ColumnarTableDDLContext stored on the context of a TableDDLCommandFunction into a sql\n * command that will be executed against a shard. The resulting command will set the\n * options of the shard to the same options as the relation the shard is based on.\n */\nchar *\nGetShardedTableDDLCommandColumnar(uint64 shardId, void *context)\n{\n\tColumnarTableDDLContext *tableDDLContext = (ColumnarTableDDLContext *) context;\n\n\t/*\n\t * AppendShardId is destructive of the original cahr *, given we want to serialize\n\t * more than once we copy it before appending the shard id.\n\t */\n\tchar *relationName = pstrdup(tableDDLContext->relationName);\n\tAppendShardIdToName(&relationName, shardId);\n\n\tchar *qualifiedShardName = quote_qualified_identifier(tableDDLContext->schemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  relationName);\n\n\treturn CitusCreateAlterColumnarTableSet(qualifiedShardName,\n\t\t\t\t\t\t\t\t\t\t\t&tableDDLContext->options);\n}\n\n\n/*\n * ColumnarGetCustomTableOptionsDDL returns a TableDDLCommand representing a command that\n * will apply the passed columnar options to the relation identified by relationId on a\n * new table or shard.\n */\nTableDDLCommand *\nColumnarGetCustomTableOptionsDDL(char *schemaName, char *relationName,\n\t\t\t\t\t\t\t\t ColumnarOptions options)\n{\n\tColumnarTableDDLContext *context = (ColumnarTableDDLContext *) palloc0(\n\t\tsizeof(ColumnarTableDDLContext));\n\n\t/* build the context */\n\tcontext->schemaName = schemaName;\n\tcontext->relationName = relationName;\n\tcontext->options = options;\n\n\t/* create TableDDLCommand based on the context build above */\n\treturn makeTableDDLCommandFunction(\n\t\tGetTableDDLCommandColumnar,\n\t\tGetShardedTableDDLCommandColumnar,\n\t\tcontext);\n}\n\n\n/*\n * ColumnarGetTableOptionsDDL returns a TableDDLCommand representing a command that will\n * apply the columnar options currently applicable to the relation identified by\n * relationId on a new table or shard.\n */\nstatic TableDDLCommand *\nColumnarGetTableOptionsDDL(Oid relationId)\n{\n\tOid namespaceId = get_rel_namespace(relationId);\n\tchar *schemaName = get_namespace_name(namespaceId);\n\tchar *relationName = get_rel_name(relationId);\n\n\tColumnarOptions options = { 0 };\n\textern_ReadColumnarOptions(relationId, &options);\n\n\treturn ColumnarGetCustomTableOptionsDDL(schemaName, relationName, options);\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/partitioning.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * partitioning.c\n *\t  Functions for dealing with partitioned tables.\n *\n * Copyright (c) Microsoft Corporation. All rights reserved.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"funcapi.h\"\n\n#include \"access/htup.h\"\n#include \"access/htup_details.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(time_partition_range);\n\n\n/*\n * time_partition_range returns the lower and upper bound of partition\n * key values for the partition of a time-partitioned table.\n */\nDatum\ntime_partition_range(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\n\t/* create tuple descriptor for return value */\n\tTupleDesc metadataDescriptor = NULL;\n\tTypeFuncClass resultTypeClass = get_call_result_type(fcinfo, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t &metadataDescriptor);\n\tif (resultTypeClass != TYPEFUNC_COMPOSITE)\n\t{\n\t\tereport(ERROR, (errmsg(\"return type must be a row type\")));\n\t}\n\n\t/* get the pg_class record */\n\tHeapTuple tuple = SearchSysCache1(RELOID, relationId);\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"relation with OID %u does not exist\", relationId)));\n\t}\n\n\t/* get the pg_class record */\n\tbool isNull = false;\n\tDatum partitionBoundDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relpartbound,\n\t\t\t\t\t\t\t\t\t\t\t\t&isNull);\n\tif (isNull)\n\t{\n\t\tereport(ERROR, (errmsg(\"relation \\\"%s\\\" is not a partition\",\n\t\t\t\t\t\t\t   get_rel_name(relationId))));\n\t}\n\n\tPartitionBoundSpec *partitionBoundSpec =\n\t\t(PartitionBoundSpec *) stringToNode(TextDatumGetCString(partitionBoundDatum));\n\n\tif (!IsA(partitionBoundSpec, PartitionBoundSpec))\n\t{\n\t\tereport(ERROR, (errmsg(\"expected PartitionBoundSpec\")));\n\t}\n\n\tif (partitionBoundSpec->strategy != PARTITION_STRATEGY_RANGE)\n\t{\n\t\tereport(ERROR, (errmsg(\"relation \\\"%s\\\" is not a range partition\",\n\t\t\t\t\t\t\t   get_rel_name(relationId)),\n\t\t\t\t\t\terrdetail(\"time_partition_range can only be used for \"\n\t\t\t\t\t\t\t\t  \"partitions of range-partitioned tables with a single \"\n\t\t\t\t\t\t\t\t  \"partition column\")));\n\t}\n\n\tDatum values[2];\n\tbool isNulls[2];\n\n\tmemset(values, 0, sizeof(values));\n\tmemset(isNulls, false, sizeof(isNulls));\n\n\tif (partitionBoundSpec->is_default)\n\t{\n\t\t/* return NULL for default partition */\n\t\tisNulls[0] = true;\n\t\tisNulls[1] = true;\n\t}\n\telse\n\t{\n\t\tif (list_length(partitionBoundSpec->lowerdatums) != 1 ||\n\t\t\tlist_length(partitionBoundSpec->upperdatums) != 1)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"relation \\\"%s\\\" is a partition with multiple \"\n\t\t\t\t\t\t\t\t   \"partition columns\",\n\t\t\t\t\t\t\t\t   get_rel_name(relationId)),\n\t\t\t\t\t\t\terrdetail(\"time_partition_range can only be used for \"\n\t\t\t\t\t\t\t\t\t  \"partitions of range-partitioned tables with a \"\n\t\t\t\t\t\t\t\t\t  \"single partition column\")));\n\t\t}\n\n\t\tPartitionRangeDatum *lowerBoundDatum =\n\t\t\tcastNode(PartitionRangeDatum, linitial(partitionBoundSpec->lowerdatums));\n\t\tPartitionRangeDatum *upperBoundDatum =\n\t\t\tcastNode(PartitionRangeDatum, linitial(partitionBoundSpec->upperdatums));\n\n\t\tConst *lowerConst = castNode(Const, lowerBoundDatum->value);\n\t\tConst *upperConst = castNode(Const, upperBoundDatum->value);\n\n\t\tchar *lowerConstStr = DatumToString(lowerConst->constvalue,\n\t\t\t\t\t\t\t\t\t\t\tlowerConst->consttype);\n\n\t\tchar *upperConstStr = DatumToString(upperConst->constvalue,\n\t\t\t\t\t\t\t\t\t\t\tupperConst->consttype);\n\n\t\tvalues[0] = CStringGetTextDatum(lowerConstStr);\n\t\tvalues[1] = CStringGetTextDatum(upperConstStr);\n\t}\n\n\tHeapTuple metadataTuple = heap_form_tuple(metadataDescriptor, values, isNulls);\n\tDatum metadataDatum = HeapTupleGetDatum(metadataTuple);\n\n\tReleaseSysCache(tuple);\n\n\tPG_RETURN_DATUM(metadataDatum);\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/replicate_none_dist_table_shard.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * replicate_none_dist_table_shard.c\n *\t  Routines to replicate shard of none-distributed table to\n *    a remote node.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/replicate_none_dist_table_shard.h\"\n#include \"distributed/shard_utils.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_protocol.h\"\n\n\nstatic void CreateForeignKeysFromReferenceTablesOnShards(Oid noneDistTableId);\nstatic Oid ForeignConstraintGetReferencingTableId(const char *queryString);\nstatic void EnsureNoneDistTableWithCoordinatorPlacement(Oid noneDistTableId);\nstatic void SetLocalEnableManualChangesToShard(bool state);\n\n\n/*\n * NoneDistTableReplicateCoordinatorPlacement replicates local (presumably\n * coordinator) shard placement of given none-distributed table to given\n * target nodes and inserts records for new placements into pg_dist_placement.\n */\nvoid\nNoneDistTableReplicateCoordinatorPlacement(Oid noneDistTableId,\n\t\t\t\t\t\t\t\t\t\t   List *targetNodeList)\n{\n\tEnsureCoordinator();\n\tEnsureNoneDistTableWithCoordinatorPlacement(noneDistTableId);\n\n\t/*\n\t * We don't expect callers try to replicate the shard to remote nodes\n\t * if some of the remote nodes have a placement for the shard already.\n\t */\n\tint64 shardId = GetFirstShardId(noneDistTableId);\n\tList *remoteShardPlacementList =\n\t\tFilterShardPlacementList(ActiveShardPlacementList(shardId),\n\t\t\t\t\t\t\t\t IsRemoteShardPlacement);\n\tif (list_length(remoteShardPlacementList) > 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"table already has a remote shard placement\")));\n\t}\n\n\tuint64 shardLength = ShardLength(shardId);\n\n\t/* insert new placements to pg_dist_placement */\n\tList *insertedPlacementList = NIL;\n\tWorkerNode *targetNode = NULL;\n\tforeach_declared_ptr(targetNode, targetNodeList)\n\t{\n\t\tShardPlacement *shardPlacement =\n\t\t\tInsertShardPlacementRowGlobally(shardId, GetNextPlacementId(),\n\t\t\t\t\t\t\t\t\t\t\tshardLength, targetNode->groupId);\n\n\t\t/* and save the placement for shard creation on workers */\n\t\tinsertedPlacementList = lappend(insertedPlacementList, shardPlacement);\n\t}\n\n\t/* create new placements */\n\tbool useExclusiveConnection = false;\n\tCreateShardsOnWorkers(noneDistTableId, insertedPlacementList,\n\t\t\t\t\t\t  useExclusiveConnection);\n\n\t/* fetch coordinator placement before deleting it */\n\tOid localPlacementTableId = GetTableLocalShardOid(noneDistTableId, shardId);\n\tShardPlacement *coordinatorPlacement =\n\t\tlinitial(ActiveShardPlacementListOnGroup(shardId, COORDINATOR_GROUP_ID));\n\n\t/*\n\t * CreateForeignKeysFromReferenceTablesOnShards and CopyFromLocalTableIntoDistTable\n\t * need to ignore the local placement, hence we temporarily delete it before\n\t * calling them.\n\t */\n\tDeleteShardPlacementRowGlobally(coordinatorPlacement->placementId);\n\n\t/* and copy data from local placement to new placements */\n\tCopyFromLocalTableIntoDistTable(\n\t\tlocalPlacementTableId, noneDistTableId\n\t\t);\n\n\t/*\n\t * CreateShardsOnWorkers only creates the foreign keys where given relation\n\t * is the referencing one, so we need to create the foreign keys where given\n\t * relation is the referenced one as well. We're only interested in the cases\n\t * where the referencing relation is a reference table because the other\n\t * possible table types --i.e., Citus local tables atm-- cannot have placements\n\t * on remote nodes.\n\t *\n\t * Note that we need to create the foreign keys where given relation is the\n\t * referenced one after copying the data so that constraint checks can pass.\n\t */\n\tCreateForeignKeysFromReferenceTablesOnShards(noneDistTableId);\n\n\t/* using the same placement id, re-insert the deleted placement */\n\tInsertShardPlacementRowGlobally(shardId, coordinatorPlacement->placementId,\n\t\t\t\t\t\t\t\t\tshardLength, COORDINATOR_GROUP_ID);\n}\n\n\n/*\n * NoneDistTableDeleteCoordinatorPlacement deletes pg_dist_placement record for\n * local (presumably coordinator) shard placement of given none-distributed table.\n */\nvoid\nNoneDistTableDeleteCoordinatorPlacement(Oid noneDistTableId)\n{\n\tEnsureCoordinator();\n\tEnsureNoneDistTableWithCoordinatorPlacement(noneDistTableId);\n\n\tint64 shardId = GetFirstShardId(noneDistTableId);\n\n\t/* we've already verified that table has a coordinator placement */\n\tShardPlacement *coordinatorPlacement =\n\t\tlinitial(ActiveShardPlacementListOnGroup(shardId, COORDINATOR_GROUP_ID));\n\n\t/* remove the old placement from metadata of local node, i.e., coordinator */\n\tDeleteShardPlacementRowGlobally(coordinatorPlacement->placementId);\n}\n\n\n/*\n * NoneDistTableDropCoordinatorPlacementTable drops local (presumably coordinator)\n * shard placement table of given none-distributed table.\n */\nvoid\nNoneDistTableDropCoordinatorPlacementTable(Oid noneDistTableId)\n{\n\tEnsureCoordinator();\n\n\tif (HasDistributionKey(noneDistTableId))\n\t{\n\t\tereport(ERROR, (errmsg(\"table is not a none-distributed table\")));\n\t}\n\n\t/*\n\t * We undistribute Citus local tables that are not chained with any reference\n\t * tables via foreign keys at the end of the utility hook.\n\t * Here we temporarily set the related GUC to off to disable the logic for\n\t * internally executed DDL's that might invoke this mechanism unnecessarily.\n\t *\n\t * We also temporarily disable citus.enable_manual_changes_to_shards GUC to\n\t * allow given command to modify shard. Note that we disable it only for\n\t * local session because changes made to shards are allowed for Citus internal\n\t * backends anyway.\n\t */\n\tint saveNestLevel = NewGUCNestLevel();\n\n\tSetLocalEnableLocalReferenceForeignKeys(false);\n\tSetLocalEnableManualChangesToShard(true);\n\n\tStringInfo dropShardCommand = makeStringInfo();\n\tint64 shardId = GetFirstShardId(noneDistTableId);\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\tappendStringInfo(dropShardCommand, DROP_REGULAR_TABLE_COMMAND,\n\t\t\t\t\t ConstructQualifiedShardName(shardInterval));\n\n\tTask *task = CitusMakeNode(Task);\n\ttask->jobId = INVALID_JOB_ID;\n\ttask->taskId = INVALID_TASK_ID;\n\ttask->taskType = DDL_TASK;\n\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\tSetTaskQueryString(task, dropShardCommand->data);\n\n\tShardPlacement *targetPlacement = CitusMakeNode(ShardPlacement);\n\tSetPlacementNodeMetadata(targetPlacement, CoordinatorNodeIfAddedAsWorkerOrError());\n\n\ttask->taskPlacementList = list_make1(targetPlacement);\n\n\tbool localExecutionSupported = true;\n\tExecuteUtilityTaskList(list_make1(task), localExecutionSupported);\n\n\tAtEOXact_GUC(true, saveNestLevel);\n}\n\n\n/*\n * CreateForeignKeysFromReferenceTablesOnShards creates foreign keys on shards\n * where given none-distributed table is the referenced table and the referencing\n * one is a reference table.\n */\nstatic void\nCreateForeignKeysFromReferenceTablesOnShards(Oid noneDistTableId)\n{\n\tEnsureCoordinator();\n\n\tif (HasDistributionKey(noneDistTableId))\n\t{\n\t\tereport(ERROR, (errmsg(\"table is not a none-distributed table\")));\n\t}\n\n\tList *ddlCommandList =\n\t\tGetForeignConstraintFromOtherReferenceTablesCommands(noneDistTableId);\n\tif (list_length(ddlCommandList) == 0)\n\t{\n\t\treturn;\n\t}\n\n\tList *taskList = NIL;\n\n\tchar *command = NULL;\n\tforeach_declared_ptr(command, ddlCommandList)\n\t{\n\t\tList *commandTaskList = InterShardDDLTaskList(\n\t\t\tForeignConstraintGetReferencingTableId(command),\n\t\t\tnoneDistTableId, command\n\t\t\t);\n\t\ttaskList = list_concat(taskList, commandTaskList);\n\t}\n\n\tif (list_length(taskList) == 0)\n\t{\n\t\treturn;\n\t}\n\n\tbool localExecutionSupported = true;\n\tExecuteUtilityTaskList(taskList, localExecutionSupported);\n}\n\n\n/*\n * ForeignConstraintGetReferencedTableId parses given foreign constraint command and\n * extracts refenrencing table id from it.\n */\nstatic Oid\nForeignConstraintGetReferencingTableId(const char *queryString)\n{\n\tNode *queryNode = ParseTreeNode(queryString);\n\tif (!IsA(queryNode, AlterTableStmt))\n\t{\n\t\tereport(ERROR, (errmsg(\"command is not an ALTER TABLE statement\")));\n\t}\n\n\tAlterTableStmt *foreignConstraintStmt = (AlterTableStmt *) queryNode;\n\tif (list_length(foreignConstraintStmt->cmds) != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"command does not contain a single command\")));\n\t}\n\n\tAlterTableCmd *command = (AlterTableCmd *) linitial(foreignConstraintStmt->cmds);\n\tif (command->subtype == AT_AddConstraint)\n\t{\n\t\tConstraint *constraint = (Constraint *) command->def;\n\t\tif (constraint && constraint->contype == CONSTR_FOREIGN)\n\t\t{\n\t\t\tbool missingOk = false;\n\t\t\treturn RangeVarGetRelid(foreignConstraintStmt->relation, NoLock,\n\t\t\t\t\t\t\t\t\tmissingOk);\n\t\t}\n\t}\n\n\tereport(ERROR, (errmsg(\"command does not contain a foreign constraint\")));\n}\n\n\n/*\n * EnsureNoneDistTableWithCoordinatorPlacement throws an error if given\n * table is not a none-distributed that has a coordinator placement.\n */\nstatic void\nEnsureNoneDistTableWithCoordinatorPlacement(Oid noneDistTableId)\n{\n\tif (HasDistributionKey(noneDistTableId))\n\t{\n\t\tereport(ERROR, (errmsg(\"table is not a none-distributed table\")));\n\t}\n\n\tint64 shardId = GetFirstShardId(noneDistTableId);\n\tif (!ActiveShardPlacementListOnGroup(shardId, COORDINATOR_GROUP_ID))\n\t{\n\t\tereport(ERROR, (errmsg(\"table does not have a coordinator placement\")));\n\t}\n}\n\n\n/*\n * SetLocalEnableManualChangesToShard locally enables\n * citus.enable_manual_changes_to_shards GUC.\n */\nstatic void\nSetLocalEnableManualChangesToShard(bool state)\n{\n\tset_config_option(\"citus.enable_manual_changes_to_shards\",\n\t\t\t\t\t  state ? \"on\" : \"off\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/shard_cleaner.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_cleaner.c\n *\t  This implements the background process that cleans shards and resources\n *\t  that are left around.\n *\n * Copyright (c) 2018, Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/xact.h\"\n#include \"catalog/namespace.h\"\n#include \"commands/dbcommands.h\"\n#include \"commands/sequence.h\"\n#include \"nodes/makefuncs.h\"\n#include \"postmaster/postmaster.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/pg_dist_cleanup.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/shard_rebalancer.h\"\n#include \"distributed/worker_transaction.h\"\n\n#define REPLICATION_SLOT_CATALOG_TABLE_NAME \"pg_replication_slots\"\n#define STR_ERRCODE_OBJECT_IN_USE \"55006\"\n#define STR_ERRCODE_UNDEFINED_OBJECT \"42704\"\n\n/* GUC configuration for shard cleaner */\nint NextOperationId = 0;\nint NextCleanupRecordId = 0;\n\n/* Data structure for cleanup operation */\n\n/*\n * CleanupRecord represents a record from pg_dist_cleanup.\n */\ntypedef struct CleanupRecord\n{\n\t/* unique identifier of the record */\n\tuint64 recordId;\n\n\t/* identifier of the operation that generated the record */\n\tOperationId operationId;\n\n\t/* type of the object (e.g. shard) */\n\tCleanupObject objectType;\n\n\t/* fully qualified name of the object */\n\tchar *objectName;\n\n\t/* node group ID on which the object is located */\n\tint nodeGroupId;\n\n\t/* cleanup policy that determines when object is cleaned */\n\tCleanupPolicy policy;\n} CleanupRecord;\n\n/* operation ID set by RegisterOperationNeedingCleanup */\nOperationId CurrentOperationId = INVALID_OPERATION_ID;\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(citus_cleanup_orphaned_shards);\nPG_FUNCTION_INFO_V1(citus_cleanup_orphaned_resources);\nPG_FUNCTION_INFO_V1(isolation_cleanup_orphaned_resources);\n\nstatic bool TryDropResourceByCleanupRecordOutsideTransaction(CleanupRecord *record,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t char *nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t int nodePort);\nstatic bool TryDropShardOutsideTransaction(char *qualifiedTableName,\n\t\t\t\t\t\t\t\t\t\t   char *nodeName,\n\t\t\t\t\t\t\t\t\t\t   int nodePort);\nstatic bool TryDropSubscriptionOutsideTransaction(char *subscriptionName,\n\t\t\t\t\t\t\t\t\t\t\t\t  char *nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t  int nodePort);\nstatic bool TryDropPublicationOutsideTransaction(char *publicationName,\n\t\t\t\t\t\t\t\t\t\t\t\t char *nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t int nodePort);\nstatic bool TryDropReplicationSlotOutsideTransaction(char *replicationSlotName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t char *nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t int nodePort);\nstatic bool TryDropUserOutsideTransaction(char *username, char *nodeName, int nodePort);\nstatic bool TryDropDatabaseOutsideTransaction(char *databaseName, char *nodeName,\n\t\t\t\t\t\t\t\t\t\t\t  int nodePort);\n\nstatic CleanupRecord * GetCleanupRecordByNameAndType(char *objectName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t CleanupObject type);\n\n/* Functions for cleanup infrastructure */\nstatic CleanupRecord * TupleToCleanupRecord(HeapTuple heapTuple,\n\t\t\t\t\t\t\t\t\t\t\tTupleDesc\n\t\t\t\t\t\t\t\t\t\t\ttupleDescriptor);\nstatic OperationId GetNextOperationId(void);\nstatic uint64 GetNextCleanupRecordId(void);\nstatic void LockOperationId(OperationId operationId);\nstatic bool TryLockOperationId(OperationId operationId);\nstatic void DeleteCleanupRecordByRecordId(uint64 recordId);\nstatic void DeleteCleanupRecordByRecordIdOutsideTransaction(uint64 recordId);\nstatic bool CleanupRecordExists(uint64 recordId);\nstatic List * ListCleanupRecords(void);\nstatic List * ListCleanupRecordsForCurrentOperation(void);\nstatic int DropOrphanedResourcesForCleanup(void);\nstatic int CompareCleanupRecordsByObjectType(const void *leftElement,\n\t\t\t\t\t\t\t\t\t\t\t const void *rightElement);\n\n/*\n * citus_cleanup_orphaned_shards is noop.\n * Use citus_cleanup_orphaned_resources instead.\n */\nDatum\ncitus_cleanup_orphaned_shards(PG_FUNCTION_ARGS)\n{\n\tereport(WARNING, (errmsg(\"citus_cleanup_orphaned_shards is deprecated. \"\n\t\t\t\t\t\t\t \"Use citus_cleanup_orphaned_resources instead\")));\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_cleanup_orphaned_resources implements a user-facing UDF to delete\n * orphaned resources that are present in the system. These resources are\n * orphaned by previous actions that either failed or marked the resources\n * for deferred cleanup.\n *\n * The function takes no arguments and runs on co-ordinator. It cannot be run in a\n * transaction, because holding the locks it takes for a long time is not good.\n * While the locks are held, it is impossible for the background daemon to\n * perform concurrent cleanup.\n */\nDatum\ncitus_cleanup_orphaned_resources(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tPreventInTransactionBlock(true, \"citus_cleanup_orphaned_resources\");\n\n\tint droppedCount = DropOrphanedResourcesForCleanup();\n\tif (droppedCount > 0)\n\t{\n\t\tereport(NOTICE, (errmsg(\"cleaned up %d orphaned resources\", droppedCount)));\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * isolation_cleanup_orphaned_resources implements a test UDF that's the same as\n * citus_cleanup_orphaned_resources. The only difference is that this command can\n * be run in transactions, this is needed to test this function in isolation tests\n * since commands are automatically run in transactions there.\n */\nDatum\nisolation_cleanup_orphaned_resources(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tDropOrphanedResourcesForCleanup();\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * DropOrphanedResourcesInSeparateTransaction cleans up orphaned resources by\n * connecting to localhost.\n */\nvoid\nDropOrphanedResourcesInSeparateTransaction(void)\n{\n\tint connectionFlag = FORCE_NEW_CONNECTION;\n\tMultiConnection *connection = GetNodeConnection(connectionFlag, LocalHostName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tPostPortNumber);\n\tExecuteCriticalRemoteCommand(connection, \"CALL citus_cleanup_orphaned_resources()\");\n\tCloseConnection(connection);\n}\n\n\n/*\n * TryDropOrphanedResources is a wrapper around DropOrphanedResourcesForCleanup\n * that catches any errors to make it safe to use in the maintenance daemon.\n *\n * If dropping any of the resources failed this function returns -1, otherwise it\n * returns the number of dropped resources.\n */\nint\nTryDropOrphanedResources()\n{\n\tint droppedResourceCount = 0;\n\tMemoryContext savedContext = CurrentMemoryContext;\n\n\t/*\n\t * Start a subtransaction so we can rollback database's state to it in case\n\t * of error.\n\t */\n\tBeginInternalSubTransaction(NULL);\n\n\tPG_TRY();\n\t{\n\t\tdroppedResourceCount = DropOrphanedResourcesForCleanup();\n\n\t\t/*\n\t\t * Releasing a subtransaction doesn't free its memory context, since the\n\t\t * data it contains will be needed at upper commit. See the comments for\n\t\t * AtSubCommit_Memory() at postgres/src/backend/access/transam/xact.c.\n\t\t */\n\t\tReleaseCurrentSubTransaction();\n\t}\n\tPG_CATCH();\n\t{\n\t\tMemoryContextSwitchTo(savedContext);\n\t\tErrorData *edata = CopyErrorData();\n\t\tFlushErrorState();\n\n\t\tRollbackAndReleaseCurrentSubTransaction();\n\n\t\t/* rethrow as WARNING */\n\t\tedata->elevel = WARNING;\n\t\tThrowErrorData(edata);\n\t}\n\tPG_END_TRY();\n\n\treturn droppedResourceCount;\n}\n\n\n/*\n * DropOrphanedResourcesForCleanup removes resources that were marked for cleanup by operation.\n * It does so by trying to take an exclusive lock on the resources. If the lock cannot be\n * obtained it skips the resource and continues with others.\n * The resource that has been skipped will be removed at a later iteration when there are no\n * locks held anymore.\n */\nstatic int\nDropOrphanedResourcesForCleanup()\n{\n\tList *cleanupRecordList = ListCleanupRecords();\n\n\t/*\n\t * We sort the records before cleaning up by their types, because of dependencies.\n\t * For example, a subscription might depend on a publication.\n\t */\n\tcleanupRecordList = SortList(cleanupRecordList,\n\t\t\t\t\t\t\t\t CompareCleanupRecordsByObjectType);\n\n\tint removedResourceCountForCleanup = 0;\n\tint failedResourceCountForCleanup = 0;\n\tCleanupRecord *record = NULL;\n\n\tforeach_declared_ptr(record, cleanupRecordList)\n\t{\n\t\tif (!PrimaryNodeForGroup(record->nodeGroupId, NULL))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* Advisory locks are reentrant */\n\t\tif (!TryLockOperationId(record->operationId))\n\t\t{\n\t\t\t/* operation that the cleanup record is part of is still running */\n\t\t\tcontinue;\n\t\t}\n\n\t\tchar *resourceName = record->objectName;\n\t\tWorkerNode *workerNode = LookupNodeForGroup(record->nodeGroupId);\n\n\t\t/*\n\t\t * Now that we have the lock, check if record exists.\n\t\t * The operation could have completed successfully just after we called\n\t\t * ListCleanupRecords in which case the record will be now gone.\n\t\t */\n\t\tif (!CleanupRecordExists(record->recordId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (TryDropResourceByCleanupRecordOutsideTransaction(record,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t workerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t workerNode->workerPort))\n\t\t{\n\t\t\tif (record->policy == CLEANUP_DEFERRED_ON_SUCCESS)\n\t\t\t{\n\t\t\t\tereport(LOG, (errmsg(\"deferred drop of orphaned resource %s on %s:%d \"\n\t\t\t\t\t\t\t\t\t \"completed\",\n\t\t\t\t\t\t\t\t\t resourceName,\n\t\t\t\t\t\t\t\t\t workerNode->workerName, workerNode->workerPort)));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tereport(LOG, (errmsg(\"cleaned up orphaned resource %s on %s:%d which \"\n\t\t\t\t\t\t\t\t\t \"was left behind after a failed operation\",\n\t\t\t\t\t\t\t\t\t resourceName,\n\t\t\t\t\t\t\t\t\t workerNode->workerName, workerNode->workerPort)));\n\t\t\t}\n\n\t\t\t/* delete the cleanup record */\n\t\t\tDeleteCleanupRecordByRecordId(record->recordId);\n\t\t\tremovedResourceCountForCleanup++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * We log failures at the end, since they occur repeatedly\n\t\t\t * for a large number of objects.\n\t\t\t */\n\t\t\tfailedResourceCountForCleanup++;\n\t\t}\n\t}\n\n\tif (failedResourceCountForCleanup > 0)\n\t{\n\t\tereport(WARNING, (errmsg(\"failed to clean up %d orphaned resources out of %d\",\n\t\t\t\t\t\t\t\t failedResourceCountForCleanup,\n\t\t\t\t\t\t\t\t list_length(cleanupRecordList))));\n\t}\n\n\treturn removedResourceCountForCleanup;\n}\n\n\n/*\n * RegisterOperationNeedingCleanup is be called by an operation to register\n * for cleanup.\n */\nOperationId\nRegisterOperationNeedingCleanup(void)\n{\n\tCurrentOperationId = GetNextOperationId();\n\n\tLockOperationId(CurrentOperationId);\n\n\treturn CurrentOperationId;\n}\n\n\n/*\n * FinalizeOperationNeedingCleanupOnSuccess is be called by an operation to signal\n * completion with success. This will trigger cleanup of appropriate resources.\n */\nvoid\nFinalizeOperationNeedingCleanupOnSuccess(const char *operationName)\n{\n\t/* We must have a valid OperationId. Any operation requring cleanup\n\t * will call RegisterOperationNeedingCleanup.\n\t */\n\tAssert(CurrentOperationId != INVALID_OPERATION_ID);\n\n\tList *currentOperationRecordList = ListCleanupRecordsForCurrentOperation();\n\n\t/*\n\t * We sort the records before cleaning up by their types, because of dependencies.\n\t * For example, a subscription might depend on a publication.\n\t */\n\tcurrentOperationRecordList = SortList(currentOperationRecordList,\n\t\t\t\t\t\t\t\t\t\t  CompareCleanupRecordsByObjectType);\n\n\tint failedShardCountOnComplete = 0;\n\n\tCleanupRecord *record = NULL;\n\tforeach_declared_ptr(record, currentOperationRecordList)\n\t{\n\t\tif (record->policy == CLEANUP_ALWAYS)\n\t\t{\n\t\t\tWorkerNode *workerNode = LookupNodeForGroup(record->nodeGroupId);\n\n\t\t\t/*\n\t\t\t * For all resources of CurrentOperationId that are marked as 'CLEANUP_ALWAYS'\n\t\t\t * drop resource and cleanup records.\n\t\t\t */\n\t\t\tif (TryDropResourceByCleanupRecordOutsideTransaction(record,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t workerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t workerNode->workerPort))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Delete cleanup records outside transaction as:\n\t\t\t\t * The resources are marked as 'CLEANUP_ALWAYS' and should be cleaned no matter\n\t\t\t\t * the operation succeeded or failed.\n\t\t\t\t */\n\t\t\t\tDeleteCleanupRecordByRecordIdOutsideTransaction(record->recordId);\n\t\t\t}\n\t\t\telse if (record->objectType == CLEANUP_OBJECT_SHARD_PLACEMENT)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We log failures at the end, since they occur repeatedly\n\t\t\t\t * for a large number of objects.\n\t\t\t\t */\n\t\t\t\tfailedShardCountOnComplete++;\n\t\t\t}\n\t\t}\n\t\telse if (record->policy == CLEANUP_ON_FAILURE)\n\t\t{\n\t\t\t/* Delete cleanup records (and not the actual resource) in same transaction as:\n\t\t\t * The resources are marked as 'CLEANUP_ON_FAILURE' and we are approaching a successful\n\t\t\t * completion of the operation. However, we cannot guarentee that operation will succeed\n\t\t\t * so we tie the Delete with parent transaction.\n\t\t\t */\n\t\t\tDeleteCleanupRecordByRecordId(record->recordId);\n\t\t}\n\t}\n\n\tif (failedShardCountOnComplete > 0)\n\t{\n\t\tereport(WARNING, (errmsg(\n\t\t\t\t\t\t\t  \"failed to clean up %d orphaned shards out of %d after \"\n\t\t\t\t\t\t\t  \"a %s operation completed\",\n\t\t\t\t\t\t\t  failedShardCountOnComplete,\n\t\t\t\t\t\t\t  list_length(currentOperationRecordList),\n\t\t\t\t\t\t\t  operationName)));\n\t}\n}\n\n\n/*\n * CompareRecordsByObjectType is a comparison function for sort\n * cleanup records by their object type.\n */\nstatic int\nCompareCleanupRecordsByObjectType(const void *leftElement, const void *rightElement)\n{\n\tCleanupRecord *leftRecord = *((CleanupRecord **) leftElement);\n\tCleanupRecord *rightRecord = *((CleanupRecord **) rightElement);\n\n\t/* we compare 64-bit integers, instead of casting their difference to int */\n\tif (leftRecord->objectType > rightRecord->objectType)\n\t{\n\t\treturn 1;\n\t}\n\telse if (leftRecord->objectType < rightRecord->objectType)\n\t{\n\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n\n\n/*\n * InsertCleanupOnSuccessRecordInCurrentTransaction inserts a new pg_dist_cleanup entry\n * as part of the current transaction. This is primarily useful for deferred drop scenarios,\n * since these records would roll back in case of operation failure. And for the same reason,\n * always sets the policy type to CLEANUP_DEFERRED_ON_SUCCESS.\n */\nvoid\nInsertCleanupOnSuccessRecordInCurrentTransaction(CleanupObject objectType,\n\t\t\t\t\t\t\t\t\t\t\t\t char *objectName,\n\t\t\t\t\t\t\t\t\t\t\t\t int nodeGroupId)\n{\n\t/* We must have a valid OperationId. Any operation requring cleanup\n\t * will call RegisterOperationNeedingCleanup.\n\t */\n\tAssert(CurrentOperationId != INVALID_OPERATION_ID);\n\n\tDatum values[Natts_pg_dist_cleanup];\n\tbool isNulls[Natts_pg_dist_cleanup];\n\n\t/* form new shard tuple */\n\tmemset(values, 0, sizeof(values));\n\tmemset(isNulls, false, sizeof(isNulls));\n\n\tuint64 recordId = GetNextCleanupRecordId();\n\tOperationId operationId = CurrentOperationId;\n\n\tvalues[Anum_pg_dist_cleanup_record_id - 1] = UInt64GetDatum(recordId);\n\tvalues[Anum_pg_dist_cleanup_operation_id - 1] = UInt64GetDatum(operationId);\n\tvalues[Anum_pg_dist_cleanup_object_type - 1] = Int32GetDatum(objectType);\n\tvalues[Anum_pg_dist_cleanup_object_name - 1] = CStringGetTextDatum(objectName);\n\tvalues[Anum_pg_dist_cleanup_node_group_id - 1] = Int32GetDatum(nodeGroupId);\n\tvalues[Anum_pg_dist_cleanup_policy_type - 1] =\n\t\tInt32GetDatum(CLEANUP_DEFERRED_ON_SUCCESS);\n\n\t/* open cleanup relation and insert new tuple */\n\tOid relationId = DistCleanupRelationId();\n\tRelation pgDistCleanup = table_open(relationId, RowExclusiveLock);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistCleanup);\n\tHeapTuple heapTuple = heap_form_tuple(tupleDescriptor, values, isNulls);\n\n\tCatalogTupleInsert(pgDistCleanup, heapTuple);\n\n\tCommandCounterIncrement();\n\ttable_close(pgDistCleanup, NoLock);\n}\n\n\n/*\n * InsertCleanupRecordOutsideTransaction inserts a new pg_dist_cleanup entry in a\n * separate transaction to ensure the record persists after rollback. We should\n * delete these records if the operation completes successfully.\n *\n * This is used in scenarios where we need to cleanup resources on operation\n * completion (CLEANUP_ALWAYS) or on failure (CLEANUP_ON_FAILURE).\n */\nvoid\nInsertCleanupRecordOutsideTransaction(CleanupObject objectType,\n\t\t\t\t\t\t\t\t\t  char *objectName,\n\t\t\t\t\t\t\t\t\t  int nodeGroupId,\n\t\t\t\t\t\t\t\t\t  CleanupPolicy policy)\n{\n\t/* We must have a valid OperationId. Any operation requring cleanup\n\t * will call RegisterOperationNeedingCleanup.\n\t */\n\tAssert(CurrentOperationId != INVALID_OPERATION_ID);\n\n\t/* assert the circumstance noted in function comment */\n\tAssert(policy == CLEANUP_ALWAYS || policy == CLEANUP_ON_FAILURE);\n\n\tStringInfo sequenceName = makeStringInfo();\n\tappendStringInfo(sequenceName, \"%s.%s\",\n\t\t\t\t\t PG_CATALOG,\n\t\t\t\t\t CLEANUPRECORDID_SEQUENCE_NAME);\n\n\tStringInfo command = makeStringInfo();\n\tappendStringInfo(command,\n\t\t\t\t\t \"INSERT INTO %s.%s \"\n\t\t\t\t\t \" (record_id, operation_id, object_type, object_name, node_group_id, policy_type) \"\n\t\t\t\t\t \" VALUES ( nextval('%s'), \" UINT64_FORMAT \", %d, %s, %d, %d)\",\n\t\t\t\t\t PG_CATALOG,\n\t\t\t\t\t PG_DIST_CLEANUP,\n\t\t\t\t\t sequenceName->data,\n\t\t\t\t\t CurrentOperationId,\n\t\t\t\t\t objectType,\n\t\t\t\t\t quote_literal_cstr(objectName),\n\t\t\t\t\t nodeGroupId,\n\t\t\t\t\t policy);\n\n\tMultiConnection *connection =\n\t\tGetConnectionForLocalQueriesOutsideTransaction(CitusExtensionOwnerName());\n\tSendCommandListToWorkerOutsideTransactionWithConnection(connection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlist_make1(command->data));\n}\n\n\n/*\n * DeleteCleanupRecordByRecordIdOutsideTransaction deletes a cleanup record by record id.\n */\nstatic void\nDeleteCleanupRecordByRecordIdOutsideTransaction(uint64 recordId)\n{\n\tStringInfo command = makeStringInfo();\n\tappendStringInfo(command,\n\t\t\t\t\t \"DELETE FROM %s.%s \"\n\t\t\t\t\t \"WHERE record_id = %lu\",\n\t\t\t\t\t PG_CATALOG,\n\t\t\t\t\t PG_DIST_CLEANUP,\n\t\t\t\t\t recordId);\n\n\tMultiConnection *connection = GetConnectionForLocalQueriesOutsideTransaction(\n\t\tCitusExtensionOwnerName());\n\tSendCommandListToWorkerOutsideTransactionWithConnection(connection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlist_make1(command->data));\n}\n\n\n/*\n * TryDropResourceByCleanupRecordOutsideTransaction tries to drop the given resource\n * and returns true on success.\n */\nstatic bool\nTryDropResourceByCleanupRecordOutsideTransaction(CleanupRecord *record,\n\t\t\t\t\t\t\t\t\t\t\t\t char *nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t int nodePort)\n{\n\tswitch (record->objectType)\n\t{\n\t\tcase CLEANUP_OBJECT_SHARD_PLACEMENT:\n\t\t{\n\t\t\treturn TryDropShardOutsideTransaction(record->objectName,\n\t\t\t\t\t\t\t\t\t\t\t\t  nodeName, nodePort);\n\t\t}\n\n\t\tcase CLEANUP_OBJECT_SUBSCRIPTION:\n\t\t{\n\t\t\treturn TryDropSubscriptionOutsideTransaction(record->objectName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t nodeName, nodePort);\n\t\t}\n\n\t\tcase CLEANUP_OBJECT_PUBLICATION:\n\t\t{\n\t\t\treturn TryDropPublicationOutsideTransaction(record->objectName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeName, nodePort);\n\t\t}\n\n\t\tcase CLEANUP_OBJECT_REPLICATION_SLOT:\n\t\t{\n\t\t\treturn TryDropReplicationSlotOutsideTransaction(record->objectName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeName, nodePort);\n\t\t}\n\n\t\tcase CLEANUP_OBJECT_USER:\n\t\t{\n\t\t\treturn TryDropUserOutsideTransaction(record->objectName, nodeName, nodePort);\n\t\t}\n\n\t\tcase CLEANUP_OBJECT_DATABASE:\n\t\t{\n\t\t\treturn TryDropDatabaseOutsideTransaction(record->objectName, nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t nodePort);\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\n\t\t\t\t\t\t\t\t  \"Invalid object type %d on failed operation cleanup\",\n\t\t\t\t\t\t\t\t  record->objectType)));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * TryDropShardOutsideTransaction tries to drop the given shard placement and returns\n * true on success.\n */\nstatic bool\nTryDropShardOutsideTransaction(char *qualifiedTableName,\n\t\t\t\t\t\t\t   char *nodeName,\n\t\t\t\t\t\t\t   int nodePort)\n{\n\t/* prepare sql query to execute to drop the shard */\n\tStringInfo dropQuery = makeStringInfo();\n\tappendStringInfo(dropQuery, DROP_REGULAR_TABLE_COMMAND, qualifiedTableName);\n\n\t/*\n\t * We set a lock_timeout here so that if there are running queries on the\n\t * shards we won't get blocked more than 1s and fail.\n\t *\n\t * The lock timeout also avoids getting stuck in a distributed deadlock, which\n\t * can occur because we might be holding pg_dist_placement locks while also\n\t * taking locks on the shard placements, and this code interrupts the\n\t * distributed deadlock detector.\n\t */\n\tList *dropCommandList = list_make2(\"SET LOCAL lock_timeout TO '1s'\",\n\t\t\t\t\t\t\t\t\t   dropQuery->data);\n\n\t/* remove the shard from the node */\n\tint connectionFlags = OUTSIDE_TRANSACTION;\n\tMultiConnection *workerConnection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  CurrentUserName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL);\n\tbool success = SendOptionalCommandListToWorkerOutsideTransactionWithConnection(\n\t\tworkerConnection,\n\t\tdropCommandList);\n\n\treturn success;\n}\n\n\n/*\n * TryDropSubscriptionOutsideTransaction drops subscription with the given name on the\n * subscriber node if it exists. Note that this doesn't drop the replication slot on the\n * publisher node. The reason is that sometimes this is not possible. To known\n * cases where this is not possible are:\n * 1. Due to the node with the replication slot being down.\n * 2. Due to a deadlock when the replication is on the same node as the\n *    subscription, which is the case for shard splits to the local node.\n *\n * So instead of directly dropping the subscription, including the attached\n * replication slot, the subscription is first disconnected from the\n * replication slot before dropping it. The replication slot itself should be\n * dropped using DropReplicationSlot on the source connection.\n */\nstatic bool\nTryDropSubscriptionOutsideTransaction(char *subscriptionName,\n\t\t\t\t\t\t\t\t\t  char *nodeName,\n\t\t\t\t\t\t\t\t\t  int nodePort)\n{\n\tint connectionFlags = OUTSIDE_TRANSACTION;\n\tMultiConnection *connection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCitusExtensionOwnerName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL);\n\n\tRemoteTransactionBegin(connection);\n\n\tif (ExecuteOptionalRemoteCommand(connection,\n\t\t\t\t\t\t\t\t\t \"SET LOCAL lock_timeout TO '1s'\", NULL) != 0)\n\t{\n\t\tRemoteTransactionAbort(connection);\n\t\tResetRemoteTransaction(connection);\n\t\treturn false;\n\t}\n\n\tint querySent = SendRemoteCommand(\n\t\tconnection,\n\t\tpsprintf(\"ALTER SUBSCRIPTION %s DISABLE\", quote_identifier(subscriptionName)));\n\tif (querySent == 0)\n\t{\n\t\tReportConnectionError(connection, WARNING);\n\t\tRemoteTransactionAbort(connection);\n\t\tResetRemoteTransaction(connection);\n\t\treturn false;\n\t}\n\n\tbool raiseInterrupts = true;\n\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\n\tif (!IsResponseOK(result))\n\t{\n\t\tchar *errorcode = PQresultErrorField(result, PG_DIAG_SQLSTATE);\n\t\tif (errorcode != NULL && strcmp(errorcode, STR_ERRCODE_UNDEFINED_OBJECT) == 0)\n\t\t{\n\t\t\t/*\n\t\t\t * The subscription doesn't exist, so we can return right away.\n\t\t\t * This DropSubscription call is effectively a no-op.\n\t\t\t */\n\t\t\tPQclear(result);\n\t\t\tForgetResults(connection);\n\t\t\tRemoteTransactionAbort(connection);\n\t\t\tResetRemoteTransaction(connection);\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tReportResultError(connection, result, WARNING);\n\t\t\tPQclear(result);\n\t\t\tForgetResults(connection);\n\t\t\tRemoteTransactionAbort(connection);\n\t\t\tResetRemoteTransaction(connection);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tPQclear(result);\n\tForgetResults(connection);\n\tRemoteTransactionCommit(connection);\n\tResetRemoteTransaction(connection);\n\n\tStringInfo alterQuery = makeStringInfo();\n\tappendStringInfo(alterQuery,\n\t\t\t\t\t \"ALTER SUBSCRIPTION %s SET (slot_name = NONE)\",\n\t\t\t\t\t quote_identifier(subscriptionName));\n\n\tStringInfo dropQuery = makeStringInfo();\n\tappendStringInfo(dropQuery,\n\t\t\t\t\t \"DROP SUBSCRIPTION %s\",\n\t\t\t\t\t quote_identifier(subscriptionName));\n\n\tList *dropCommandList = list_make3(\"SET LOCAL lock_timeout TO '1s'\",\n\t\t\t\t\t\t\t\t\t   alterQuery->data, dropQuery->data);\n\tbool success = SendOptionalCommandListToWorkerOutsideTransactionWithConnection(\n\t\tconnection,\n\t\tdropCommandList);\n\n\treturn success;\n}\n\n\n/*\n * TryDropPublicationOutsideTransaction drops the publication with the given name if it\n * exists.\n */\nstatic bool\nTryDropPublicationOutsideTransaction(char *publicationName,\n\t\t\t\t\t\t\t\t\t char *nodeName,\n\t\t\t\t\t\t\t\t\t int nodePort)\n{\n\tint connectionFlags = OUTSIDE_TRANSACTION;\n\tMultiConnection *connection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCitusExtensionOwnerName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL);\n\tStringInfo dropQuery = makeStringInfo();\n\tappendStringInfo(dropQuery,\n\t\t\t\t\t \"DROP PUBLICATION IF EXISTS %s\",\n\t\t\t\t\t quote_identifier(publicationName));\n\n\tList *dropCommandList = list_make2(\"SET LOCAL lock_timeout TO '1s'\",\n\t\t\t\t\t\t\t\t\t   dropQuery->data);\n\tbool success = SendOptionalCommandListToWorkerOutsideTransactionWithConnection(\n\t\tconnection,\n\t\tdropCommandList);\n\n\treturn success;\n}\n\n\n/*\n * TryDropReplicationSlotOutsideTransaction drops the replication slot with the given\n * name if it exists.\n */\nstatic bool\nTryDropReplicationSlotOutsideTransaction(char *replicationSlotName,\n\t\t\t\t\t\t\t\t\t\t char *nodeName,\n\t\t\t\t\t\t\t\t\t\t int nodePort)\n{\n\tint connectionFlags = OUTSIDE_TRANSACTION;\n\tMultiConnection *connection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCitusExtensionOwnerName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL);\n\n\tRemoteTransactionBegin(connection);\n\n\tif (ExecuteOptionalRemoteCommand(connection,\n\t\t\t\t\t\t\t\t\t \"SET LOCAL lock_timeout TO '1s'\", NULL) != 0)\n\t{\n\t\tRemoteTransactionAbort(connection);\n\t\tResetRemoteTransaction(connection);\n\t\treturn false;\n\t}\n\n\tint querySent = SendRemoteCommand(\n\t\tconnection,\n\t\tpsprintf(\n\t\t\t\"select pg_drop_replication_slot(slot_name) from \"\n\t\t\tREPLICATION_SLOT_CATALOG_TABLE_NAME\n\t\t\t\" where slot_name = %s\",\n\t\t\tquote_literal_cstr(replicationSlotName))\n\t\t);\n\n\tif (querySent == 0)\n\t{\n\t\tReportConnectionError(connection, WARNING);\n\t\tRemoteTransactionAbort(connection);\n\t\tResetRemoteTransaction(connection);\n\t\treturn false;\n\t}\n\n\tbool raiseInterrupts = true;\n\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\n\tif (IsResponseOK(result))\n\t{\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\t\tRemoteTransactionCommit(connection);\n\t\tResetRemoteTransaction(connection);\n\t\treturn true;\n\t}\n\n\tchar *errorcode = PQresultErrorField(result, PG_DIAG_SQLSTATE);\n\tif (errorcode != NULL && strcmp(errorcode, STR_ERRCODE_OBJECT_IN_USE) != 0)\n\t{\n\t\t/* throw a warning unless object is in use */\n\t\tReportResultError(connection, result, WARNING);\n\t}\n\n\tPQclear(result);\n\tForgetResults(connection);\n\tRemoteTransactionAbort(connection);\n\tResetRemoteTransaction(connection);\n\n\treturn false;\n}\n\n\n/*\n * TryDropUserOutsideTransaction drops the user with the given name if it exists.\n */\nstatic bool\nTryDropUserOutsideTransaction(char *username,\n\t\t\t\t\t\t\t  char *nodeName, int nodePort)\n{\n\tint connectionFlags = OUTSIDE_TRANSACTION;\n\tMultiConnection *connection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCitusExtensionOwnerName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL);\n\n\t/*\n\t * The DROP USER command should not propagate, so we temporarily disable\n\t * DDL propagation.\n\t */\n\tbool success = SendOptionalCommandListToWorkerOutsideTransactionWithConnection(\n\t\tconnection,\n\t\tlist_make3(\n\t\t\t\"SET LOCAL lock_timeout TO '1s'\",\n\t\t\t\"SET LOCAL citus.enable_ddl_propagation TO OFF;\",\n\t\t\tpsprintf(\"DROP USER IF EXISTS %s;\",\n\t\t\t\t\t quote_identifier(username))));\n\n\treturn success;\n}\n\n\n/*\n * TryDropDatabaseOutsideTransaction drops the database with the given name\n * if it exists.\n */\nstatic bool\nTryDropDatabaseOutsideTransaction(char *databaseName, char *nodeName, int nodePort)\n{\n\tint connectionFlags = (OUTSIDE_TRANSACTION | FORCE_NEW_CONNECTION);\n\tMultiConnection *connection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCitusExtensionOwnerName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL);\n\n\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * We want to disable DDL propagation and set lock_timeout before issuing\n\t * the DROP DATABASE command but we cannot do so in a way that's scoped\n\t * to the DROP DATABASE command. This is because, we cannot use a\n\t * transaction block for the DROP DATABASE command.\n\t *\n\t * For this reason, to avoid leaking the lock_timeout and DDL propagation\n\t * settings to future commands, we force the connection to close at the end\n\t * of the transaction.\n\t */\n\tForceConnectionCloseAtTransactionEnd(connection);\n\n\t/*\n\t * The DROP DATABASE command should not propagate, so we disable DDL\n\t * propagation.\n\t */\n\tList *commandList = list_make3(\n\t\t\"SET lock_timeout TO '1s'\",\n\t\t\"SET citus.enable_ddl_propagation TO OFF;\",\n\t\tpsprintf(\"DROP DATABASE IF EXISTS %s;\", quote_identifier(databaseName))\n\t\t);\n\n\tbool executeCommand = true;\n\n\tconst char *commandString = NULL;\n\tforeach_declared_ptr(commandString, commandList)\n\t{\n\t\t/*\n\t\t * Cannot use SendOptionalCommandListToWorkerOutsideTransactionWithConnection()\n\t\t * because we don't want to open a transaction block on remote nodes as DROP\n\t\t * DATABASE commands cannot be run inside a transaction block.\n\t\t */\n\t\tif (ExecuteOptionalRemoteCommand(\n\t\t\t\tconnection, commandString, NULL) != RESPONSE_OKAY)\n\t\t{\n\t\t\texecuteCommand = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tCloseConnection(connection);\n\treturn executeCommand;\n}\n\n\n/*\n * ErrorIfCleanupRecordForShardExists errors out if a cleanup record for the given\n * shard name exists.\n */\nvoid\nErrorIfCleanupRecordForShardExists(char *shardName)\n{\n\tCleanupRecord *record =\n\t\tGetCleanupRecordByNameAndType(shardName, CLEANUP_OBJECT_SHARD_PLACEMENT);\n\n\tif (record == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tereport(ERROR, (errmsg(\"shard move failed as the orphaned shard %s leftover \"\n\t\t\t\t\t\t   \"from the previous move could not be cleaned up\",\n\t\t\t\t\t\t   record->objectName)));\n}\n\n\n/*\n * GetNextOperationId allocates and returns a unique operationId for an operation\n * requiring potential cleanup. This allocation occurs both in shared memory and\n * in write ahead logs; writing to logs avoids the risk of having operationId collisions.\n */\nstatic OperationId\nGetNextOperationId()\n{\n\tOperationId operationdId = INVALID_OPERATION_ID;\n\n\t/*\n\t * In regression tests, we would like to generate operation IDs consistently\n\t * even if the tests run in parallel. Instead of the sequence, we can use\n\t * the next_operation_id GUC to specify which operation ID the current session should\n\t * generate next. The GUC is automatically increased by 1 every time a new\n\t * operation ID is generated.\n\t */\n\tif (NextOperationId > 0)\n\t{\n\t\toperationdId = NextOperationId;\n\t\tNextOperationId += 1;\n\n\t\treturn operationdId;\n\t}\n\n\t/* Generate sequence using a subtransaction. else we can hold replication slot creation for operations */\n\tStringInfo sequenceName = makeStringInfo();\n\tappendStringInfo(sequenceName, \"%s.%s\",\n\t\t\t\t\t PG_CATALOG,\n\t\t\t\t\t OPERATIONID_SEQUENCE_NAME);\n\n\tStringInfo nextValueCommand = makeStringInfo();\n\tappendStringInfo(nextValueCommand, \"SELECT nextval(%s);\",\n\t\t\t\t\t quote_literal_cstr(sequenceName->data));\n\n\tMultiConnection *connection = GetConnectionForLocalQueriesOutsideTransaction(\n\t\tCitusExtensionOwnerName());\n\n\tPGresult *result = NULL;\n\tint queryResult = ExecuteOptionalRemoteCommand(connection, nextValueCommand->data,\n\t\t\t\t\t\t\t\t\t\t\t\t   &result);\n\tif (queryResult != RESPONSE_OKAY || !IsResponseOK(result) || PQntuples(result) != 1 ||\n\t\tPQnfields(result) != 1)\n\t{\n\t\tReportResultError(connection, result, ERROR);\n\t}\n\n\toperationdId = SafeStringToUint64(PQgetvalue(result, 0, 0 /* nodeId column*/));\n\n\tPQclear(result);\n\tForgetResults(connection);\n\n\treturn operationdId;\n}\n\n\n/*\n * ListCleanupRecords lists all the current cleanup records.\n */\nstatic List *\nListCleanupRecords(void)\n{\n\tRelation pgDistCleanup = table_open(DistCleanupRelationId(), AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistCleanup);\n\n\tList *recordList = NIL;\n\tint scanKeyCount = 0;\n\tbool indexOK = false;\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistCleanup, InvalidOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK, NULL, scanKeyCount, NULL);\n\n\tHeapTuple heapTuple = NULL;\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tCleanupRecord *record = TupleToCleanupRecord(heapTuple, tupleDescriptor);\n\t\trecordList = lappend(recordList, record);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistCleanup, NoLock);\n\n\treturn recordList;\n}\n\n\n/*\n * ListCleanupRecordsForCurrentOperation lists all the cleanup records for\n * current operation.\n */\nstatic List *\nListCleanupRecordsForCurrentOperation(void)\n{\n\t/* We must have a valid OperationId. Any operation requring cleanup\n\t * will call RegisterOperationNeedingCleanup.\n\t */\n\tAssert(CurrentOperationId != INVALID_OPERATION_ID);\n\n\tRelation pgDistCleanup = table_open(DistCleanupRelationId(), AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistCleanup);\n\n\tScanKeyData scanKey[1];\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_cleanup_operation_id, BTEqualStrategyNumber,\n\t\t\t\tF_INT8EQ, Int64GetDatum(CurrentOperationId));\n\n\tint scanKeyCount = 1;\n\tOid scanIndexId = InvalidOid;\n\tbool useIndex = false;\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistCleanup, scanIndexId, useIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\tscanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = NULL;\n\tList *recordList = NIL;\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tCleanupRecord *record = TupleToCleanupRecord(heapTuple, tupleDescriptor);\n\t\trecordList = lappend(recordList, record);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistCleanup, NoLock);\n\n\treturn recordList;\n}\n\n\n/*\n * GetCleanupRecordByNameAndType returns the cleanup record with given name and type,\n * if any, returns NULL otherwise.\n */\nstatic CleanupRecord *\nGetCleanupRecordByNameAndType(char *objectName, CleanupObject type)\n{\n\tCleanupRecord *objectFound = NULL;\n\n\tRelation pgDistCleanup = table_open(DistCleanupRelationId(), AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistCleanup);\n\n\tScanKeyData scanKey[1];\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_cleanup_object_type, BTEqualStrategyNumber,\n\t\t\t\tF_INT4EQ, Int32GetDatum(type));\n\n\tint scanKeyCount = 1;\n\tOid scanIndexId = InvalidOid;\n\tbool useIndex = false;\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistCleanup, scanIndexId, useIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\tscanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = NULL;\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tCleanupRecord *record = TupleToCleanupRecord(heapTuple, tupleDescriptor);\n\t\tif (strcmp(record->objectName, objectName) == 0)\n\t\t{\n\t\t\tobjectFound = record;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistCleanup, NoLock);\n\n\treturn objectFound;\n}\n\n\n/*\n * TupleToCleanupRecord converts a pg_dist_cleanup record tuple into a CleanupRecord struct.\n */\nstatic CleanupRecord *\nTupleToCleanupRecord(HeapTuple heapTuple, TupleDesc tupleDescriptor)\n{\n\tDatum datumArray[Natts_pg_dist_cleanup];\n\tbool isNullArray[Natts_pg_dist_cleanup];\n\theap_deform_tuple(heapTuple, tupleDescriptor, datumArray, isNullArray);\n\n\tCleanupRecord *record = palloc0(sizeof(CleanupRecord));\n\n\trecord->recordId =\n\t\tDatumGetUInt64(datumArray[Anum_pg_dist_cleanup_record_id - 1]);\n\n\trecord->operationId =\n\t\tDatumGetUInt64(datumArray[Anum_pg_dist_cleanup_operation_id - 1]);\n\n\trecord->objectType =\n\t\tDatumGetInt32(datumArray[Anum_pg_dist_cleanup_object_type - 1]);\n\n\trecord->objectName =\n\t\tTextDatumGetCString(datumArray[Anum_pg_dist_cleanup_object_name - 1]);\n\n\trecord->nodeGroupId =\n\t\tDatumGetInt32(datumArray[Anum_pg_dist_cleanup_node_group_id - 1]);\n\n\trecord->policy =\n\t\tDatumGetInt32(datumArray[Anum_pg_dist_cleanup_policy_type - 1]);\n\n\treturn record;\n}\n\n\n/*\n * CleanupRecordExists returns whether a cleanup record with the given\n * record ID exists in pg_dist_cleanup.\n */\nstatic bool\nCleanupRecordExists(uint64 recordId)\n{\n\tRelation pgDistCleanup = table_open(DistCleanupRelationId(),\n\t\t\t\t\t\t\t\t\t\tAccessShareLock);\n\n\tconst int scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\tbool indexOK = true;\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_cleanup_record_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(recordId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistCleanup,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistCleanupPrimaryKeyIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tbool recordExists = HeapTupleIsValid(heapTuple);\n\n\tsystable_endscan(scanDescriptor);\n\n\tCommandCounterIncrement();\n\ttable_close(pgDistCleanup, NoLock);\n\n\treturn recordExists;\n}\n\n\n/*\n * DeleteCleanupRecordByRecordId deletes a single pg_dist_cleanup entry.\n */\nstatic void\nDeleteCleanupRecordByRecordId(uint64 recordId)\n{\n\tRelation pgDistCleanup = table_open(DistCleanupRelationId(),\n\t\t\t\t\t\t\t\t\t\tRowExclusiveLock);\n\n\tconst int scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\tbool indexOK = true;\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_cleanup_record_id,\n\t\t\t\tBTEqualStrategyNumber, F_INT8EQ, Int64GetDatum(recordId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistCleanup,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistCleanupPrimaryKeyIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (heapTuple == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find cleanup record \" UINT64_FORMAT,\n\t\t\t\t\t\t\t   recordId)));\n\t}\n\n\tsimple_heap_delete(pgDistCleanup, &heapTuple->t_self);\n\n\tsystable_endscan(scanDescriptor);\n\n\tCommandCounterIncrement();\n\ttable_close(pgDistCleanup, NoLock);\n}\n\n\n/*\n * GetNextCleanupRecordId allocates and returns a unique recordid for a cleanup entry.\n * This allocation occurs both in shared memory and\n * in write ahead logs; writing to logs avoids the risk of having operationId collisions.\n */\nstatic uint64\nGetNextCleanupRecordId(void)\n{\n\tuint64 recordId = INVALID_CLEANUP_RECORD_ID;\n\n\t/*\n\t * In regression tests, we would like to generate record IDs consistently\n\t * even if the tests run in parallel. Instead of the sequence, we can use\n\t * the next_record_id GUC to specify which recordid ID the current session should\n\t * generate next. The GUC is automatically increased by 1 every time a new\n\t * record ID is generated.\n\t */\n\tif (NextCleanupRecordId > 0)\n\t{\n\t\trecordId = NextCleanupRecordId;\n\t\tNextCleanupRecordId += 1;\n\n\t\treturn recordId;\n\t}\n\n\tRangeVar *sequenceName = makeRangeVar(PG_CATALOG,\n\t\t\t\t\t\t\t\t\t\t  CLEANUPRECORDID_SEQUENCE_NAME,\n\t\t\t\t\t\t\t\t\t\t  -1);\n\n\tbool missingOK = false;\n\tOid sequenceId = RangeVarGetRelid(sequenceName, NoLock, missingOK);\n\tbool checkPermissions = false;\n\treturn nextval_internal(sequenceId, checkPermissions);\n}\n\n\n/*\n * LockOperationId takes an exclusive lock to ensure that only one process\n * can cleanup operationId resources at the same time.\n */\nstatic void\nLockOperationId(OperationId operationId)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = false;\n\tSET_LOCKTAG_CLEANUP_OPERATION_ID(tag, operationId);\n\t(void) LockAcquire(&tag, ExclusiveLock, sessionLock, dontWait);\n}\n\n\n/*\n * TryLockOperationId takes an exclusive lock (with dontWait = true) to ensure that\n * only one process can cleanup operationId resources at the same time.\n */\nstatic bool\nTryLockOperationId(OperationId operationId)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = true;\n\tSET_LOCKTAG_CLEANUP_OPERATION_ID(tag, operationId);\n\tLockAcquireResult lockResult = LockAcquire(&tag, ExclusiveLock, sessionLock,\n\t\t\t\t\t\t\t\t\t\t\t   dontWait);\n\treturn (lockResult != LOCKACQUIRE_NOT_AVAIL);\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/shard_rebalancer.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_rebalancer.c\n *\n * Function definitions for the shard rebalancer tool.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include <math.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/dbcommands.h\"\n#include \"commands/sequence.h\"\n#include \"common/hashfn.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/guc_tables.h\"\n#include \"utils/json.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/pg_lsn.h\"\n#include \"utils/syscache.h\"\n#include \"utils/varlena.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/argutils.h\"\n#include \"distributed/background_jobs.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/enterprise.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_logical_replication.h\"\n#include \"distributed/multi_progress.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/pg_dist_rebalance_strategy.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/shard_rebalancer.h\"\n#include \"distributed/shard_transfer.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/worker_protocol.h\"\n\n/* RebalanceOptions are the options used to control the rebalance algorithm */\ntypedef struct RebalanceOptions\n{\n\tList *relationIdList;\n\tfloat4 threshold;\n\tint32 maxShardMoves;\n\tArrayType *excludedShardArray;\n\tbool drainOnly;\n\tfloat4 improvementThreshold;\n\tForm_pg_dist_rebalance_strategy rebalanceStrategy;\n\tconst char *operationName;\n\tWorkerNode *workerNode;\n\tList *involvedWorkerNodeList;\n} RebalanceOptions;\n\ntypedef struct SplitPrimaryCloneShards\n{\n\t/*\n\t * primaryShardPlacementList contains the placements that\n\t * should stay on primary worker node.\n\t */\n\tList *primaryShardIdList;\n\n\t/*\n\t * cloneShardPlacementList contains the placements that should stay on\n\t * clone worker node.\n\t */\n\tList *cloneShardIdList;\n} SplitPrimaryCloneShards;\n\n\nstatic SplitPrimaryCloneShards * GetPrimaryCloneSplitRebalanceSteps(RebalanceOptions\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t*options,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWorkerNode\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t*cloneNode);\n\n/*\n * RebalanceState is used to keep the internal state of the rebalance\n * algorithm in one place.\n */\ntypedef struct RebalanceState\n{\n\t/*\n\t * placementsHash contains the current state of all shard placements, it\n\t * is initialized from pg_dist_placement and is then modified based on the\n\t * found shard moves.\n\t */\n\tHTAB *placementsHash;\n\n\t/*\n\t * placementUpdateList contains all of the updates that have been done to\n\t * reach the current state of placementsHash.\n\t */\n\tList *placementUpdateList;\n\tRebalancePlanFunctions *functions;\n\n\t/*\n\t * fillStateListDesc contains all NodeFillStates ordered from full nodes to\n\t * empty nodes.\n\t */\n\tList *fillStateListDesc;\n\n\t/*\n\t * fillStateListAsc contains all NodeFillStates ordered from empty nodes to\n\t * full nodes.\n\t */\n\tList *fillStateListAsc;\n\n\t/*\n\t * disallowedPlacementList contains all placements that currently exist,\n\t * but are not allowed according to the shardAllowedOnNode function.\n\t */\n\tList *disallowedPlacementList;\n\n\t/*\n\t * totalCost is the cost of all the shards in the cluster added together.\n\t */\n\tfloat4 totalCost;\n\n\t/*\n\t * totalCapacity is the capacity of all the nodes in the cluster added\n\t * together.\n\t */\n\tfloat4 totalCapacity;\n\n\t/*\n\t * ignoredMoves is the number of moves that were ignored. This is used to\n\t * limit the amount of loglines we send.\n\t */\n\tint64 ignoredMoves;\n} RebalanceState;\n\n\n/* RebalanceContext stores the context for the function callbacks */\ntypedef struct RebalanceContext\n{\n\tFmgrInfo shardCostUDF;\n\tFmgrInfo nodeCapacityUDF;\n\tFmgrInfo shardAllowedOnNodeUDF;\n} RebalanceContext;\n\n/* WorkerHashKey contains hostname and port to be used as a key in a hash */\ntypedef struct WorkerHashKey\n{\n\tchar hostname[MAX_NODE_LENGTH];\n\tint port;\n} WorkerHashKey;\n\n/* WorkerShardIds represents a set of shardIds grouped by worker */\ntypedef struct WorkerShardIds\n{\n\tWorkerHashKey worker;\n\n\t/* This is a uint64 hashset representing the shard ids for a specific worker */\n\tHTAB *shardIds;\n} WorkerShardIds;\n\n/* ShardStatistics contains statistics about a shard */\ntypedef struct ShardStatistics\n{\n\tuint64 shardId;\n\n\t/* The shard its size in bytes. */\n\tuint64 totalSize;\n\tXLogRecPtr shardLSN;\n} ShardStatistics;\n\n/*\n * WorkerShardStatistics represents a set of statistics about shards,\n * grouped by worker.\n */\ntypedef struct WorkerShardStatistics\n{\n\tWorkerHashKey worker;\n\tXLogRecPtr workerLSN;\n\n\t/*\n\t * Statistics for each shard on this worker:\n\t * key: shardId\n\t * value: ShardStatistics\n\t */\n\tHTAB *statistics;\n} WorkerShardStatistics;\n\n/*\n * ShardMoveDependencyHashEntry contains the taskId which any new shard\n * move task within the corresponding colocation group\n * must take a dependency on\n */\ntypedef struct ShardMoveDependencyInfo\n{\n\tint64 key;\n\tint64 taskId;\n} ShardMoveDependencyInfo;\n\n/*\n * ShardMoveSourceNodeHashEntry keeps track of the source nodes\n * of the moves.\n */\ntypedef struct ShardMoveSourceNodeHashEntry\n{\n\t/* this is the key */\n\tint32 node_id;\n\tList *taskIds;\n} ShardMoveSourceNodeHashEntry;\n\n/*\n * ShardMoveDependencies keeps track of all needed dependencies\n * between shard moves.\n */\ntypedef struct ShardMoveDependencies\n{\n\tHTAB *colocationDependencies;\n\tHTAB *nodeDependencies;\n\tbool parallelTransferColocatedShards;\n} ShardMoveDependencies;\n\nchar *VariablesToBePassedToNewConnections = NULL;\n\n/* static declarations for main logic */\nstatic int ShardActivePlacementCount(HTAB *activePlacementsHash, uint64 shardId,\n\t\t\t\t\t\t\t\t\t List *activeWorkerNodeList);\nstatic void UpdateShardPlacement(PlacementUpdateEvent *placementUpdateEvent,\n\t\t\t\t\t\t\t\t List *responsiveNodeList, Oid shardReplicationModeOid);\n\n/* static declarations for main logic's utility functions */\nstatic HTAB * ShardPlacementsListToHash(List *shardPlacementList);\nstatic bool PlacementsHashFind(HTAB *placementsHash, uint64 shardId,\n\t\t\t\t\t\t\t   WorkerNode *workerNode);\nstatic void PlacementsHashEnter(HTAB *placementsHash, uint64 shardId,\n\t\t\t\t\t\t\t\tWorkerNode *workerNode);\nstatic void PlacementsHashRemove(HTAB *placementsHash, uint64 shardId,\n\t\t\t\t\t\t\t\t WorkerNode *workerNode);\nstatic int PlacementsHashCompare(const void *lhsKey, const void *rhsKey, Size keySize);\nstatic uint32 PlacementsHashHashCode(const void *key, Size keySize);\nstatic bool WorkerNodeListContains(List *workerNodeList, const char *workerName,\n\t\t\t\t\t\t\t\t   uint32 workerPort);\nstatic void UpdateColocatedShardPlacementProgress(uint64 shardId, char *sourceName,\n\t\t\t\t\t\t\t\t\t\t\t\t  int sourcePort, uint64 progress);\nstatic NodeFillState * FindFillStateForPlacement(RebalanceState *state,\n\t\t\t\t\t\t\t\t\t\t\t\t ShardPlacement *placement);\nstatic RebalanceState * InitRebalanceState(List *workerNodeList, List *shardPlacementList,\n\t\t\t\t\t\t\t\t\t\t   RebalancePlanFunctions *functions);\nstatic void MoveShardsAwayFromDisallowedNodes(RebalanceState *state);\nstatic bool FindAndMoveShardCost(float4 utilizationLowerBound,\n\t\t\t\t\t\t\t\t float4 utilizationUpperBound,\n\t\t\t\t\t\t\t\t float4 improvementThreshold,\n\t\t\t\t\t\t\t\t RebalanceState *state);\nstatic NodeFillState * FindAllowedTargetFillState(RebalanceState *state, uint64 shardId);\nstatic void MoveShardCost(NodeFillState *sourceFillState, NodeFillState *targetFillState,\n\t\t\t\t\t\t  ShardCost *shardCost, RebalanceState *state);\nstatic int CompareNodeFillStateAsc(const void *void1, const void *void2);\nstatic int CompareNodeFillStateDesc(const void *void1, const void *void2);\nstatic int CompareShardCostAsc(const void *void1, const void *void2);\nstatic int CompareShardCostDesc(const void *void1, const void *void2);\nstatic int CompareDisallowedPlacementAsc(const void *void1, const void *void2);\nstatic int CompareDisallowedPlacementDesc(const void *void1, const void *void2);\nstatic bool ShardAllowedOnNode(uint64 shardId, WorkerNode *workerNode, void *context);\nstatic float4 NodeCapacity(WorkerNode *workerNode, void *context);\nstatic ShardCost GetShardCost(uint64 shardId, void *context);\nstatic List * NonColocatedDistRelationIdList(void);\nstatic void RebalanceTableShards(RebalanceOptions *options, Oid shardReplicationModeOid);\nstatic int64 RebalanceTableShardsBackground(RebalanceOptions *options, Oid\n\t\t\t\t\t\t\t\t\t\t\tshardReplicationModeOid,\n\t\t\t\t\t\t\t\t\t\t\tbool ParallelTransferReferenceTables,\n\t\t\t\t\t\t\t\t\t\t\tbool ParallelTransferColocatedShards);\nstatic void AcquireRebalanceColocationLock(Oid relationId, const char *operationName);\nstatic void ExecutePlacementUpdates(List *placementUpdateList, Oid\n\t\t\t\t\t\t\t\t\tshardReplicationModeOid, char *noticeOperation);\nstatic float4 CalculateUtilization(float4 totalCost, float4 capacity);\nstatic Form_pg_dist_rebalance_strategy GetRebalanceStrategy(Name name);\nstatic void EnsureShardCostUDF(Oid functionOid);\nstatic void EnsureNodeCapacityUDF(Oid functionOid);\nstatic void EnsureShardAllowedOnNodeUDF(Oid functionOid);\nstatic HTAB * BuildWorkerShardStatisticsHash(PlacementUpdateEventProgress *steps,\n\t\t\t\t\t\t\t\t\t\t\t int stepCount);\nstatic HTAB * GetShardStatistics(MultiConnection *connection, HTAB *shardIds);\nstatic HTAB * GetMovedShardIdsByWorker(PlacementUpdateEventProgress *steps,\n\t\t\t\t\t\t\t\t\t   int stepCount, bool fromSource);\nstatic uint64 WorkerShardSize(HTAB *workerShardStatistics,\n\t\t\t\t\t\t\t  char *workerName, int workerPort, uint64 shardId);\nstatic XLogRecPtr WorkerShardLSN(HTAB *workerShardStatisticsHash, char *workerName,\n\t\t\t\t\t\t\t\t int workerPort, uint64 shardId);\nstatic XLogRecPtr WorkerLSN(HTAB *workerShardStatisticsHash,\n\t\t\t\t\t\t\tchar *workerName, int workerPort);\nstatic void AddToWorkerShardIdSet(HTAB *shardsByWorker, char *workerName, int workerPort,\n\t\t\t\t\t\t\t\t  uint64 shardId);\nstatic HTAB * BuildShardSizesHash(ProgressMonitorData *monitor, HTAB *shardStatistics);\nstatic void ErrorOnConcurrentRebalance(RebalanceOptions *);\nstatic List * GetSetCommandListForNewConnections(void);\nstatic int64 GetColocationId(PlacementUpdateEvent *move);\nstatic ShardMoveDependencies InitializeShardMoveDependencies(bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ParallelTransferColocatedShards);\nstatic int64 * GenerateTaskMoveDependencyList(PlacementUpdateEvent *move,\n\t\t\t\t\t\t\t\t\t\t\t  int64 colocationId,\n\t\t\t\t\t\t\t\t\t\t\t  int64 *refTablesDepTaskIds,\n\t\t\t\t\t\t\t\t\t\t\t  int refTablesDepTaskIdsCount,\n\t\t\t\t\t\t\t\t\t\t\t  ShardMoveDependencies shardMoveDependencies,\n\t\t\t\t\t\t\t\t\t\t\t  int *nDepends);\nstatic void UpdateShardMoveDependencies(PlacementUpdateEvent *move, uint64 colocationId,\n\t\t\t\t\t\t\t\t\t\tint64 taskId,\n\t\t\t\t\t\t\t\t\t\tShardMoveDependencies shardMoveDependencies);\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(rebalance_table_shards);\nPG_FUNCTION_INFO_V1(replicate_table_shards);\nPG_FUNCTION_INFO_V1(get_rebalance_table_shards_plan);\nPG_FUNCTION_INFO_V1(get_rebalance_progress);\nPG_FUNCTION_INFO_V1(citus_drain_node);\nPG_FUNCTION_INFO_V1(master_drain_node);\nPG_FUNCTION_INFO_V1(citus_shard_cost_by_disk_size);\nPG_FUNCTION_INFO_V1(citus_validate_rebalance_strategy_functions);\nPG_FUNCTION_INFO_V1(pg_dist_rebalance_strategy_enterprise_check);\nPG_FUNCTION_INFO_V1(citus_rebalance_start);\nPG_FUNCTION_INFO_V1(citus_rebalance_stop);\nPG_FUNCTION_INFO_V1(citus_rebalance_wait);\nPG_FUNCTION_INFO_V1(get_snapshot_based_node_split_plan);\n\nbool RunningUnderCitusTestSuite = false;\nint MaxRebalancerLoggedIgnoredMoves = 5;\nint RebalancerByDiskSizeBaseCost = 100 * 1024 * 1024;\nbool PropagateSessionSettingsForLoopbackConnection = false;\n\nstatic const char *PlacementUpdateTypeNames[] = {\n\t[PLACEMENT_UPDATE_INVALID_FIRST] = \"unknown\",\n\t[PLACEMENT_UPDATE_MOVE] = \"move\",\n\t[PLACEMENT_UPDATE_COPY] = \"copy\",\n};\n\nstatic const char *PlacementUpdateStatusNames[] = {\n\t[PLACEMENT_UPDATE_STATUS_NOT_STARTED_YET] = \"Not Started Yet\",\n\t[PLACEMENT_UPDATE_STATUS_SETTING_UP] = \"Setting Up\",\n\t[PLACEMENT_UPDATE_STATUS_COPYING_DATA] = \"Copying Data\",\n\t[PLACEMENT_UPDATE_STATUS_CATCHING_UP] = \"Catching Up\",\n\t[PLACEMENT_UPDATE_STATUS_CREATING_CONSTRAINTS] = \"Creating Constraints\",\n\t[PLACEMENT_UPDATE_STATUS_FINAL_CATCH_UP] = \"Final Catchup\",\n\t[PLACEMENT_UPDATE_STATUS_CREATING_FOREIGN_KEYS] = \"Creating Foreign Keys\",\n\t[PLACEMENT_UPDATE_STATUS_COMPLETING] = \"Completing\",\n\t[PLACEMENT_UPDATE_STATUS_COMPLETED] = \"Completed\",\n};\n\n#ifdef USE_ASSERT_CHECKING\n\n/*\n * Check that all the invariants of the state hold.\n */\nstatic void\nCheckRebalanceStateInvariants(const RebalanceState *state)\n{\n\tNodeFillState *fillState = NULL;\n\tNodeFillState *prevFillState = NULL;\n\tint fillStateIndex = 0;\n\tint fillStateLength = list_length(state->fillStateListAsc);\n\n\tAssert(state != NULL);\n\tAssert(list_length(state->fillStateListAsc) == list_length(state->fillStateListDesc));\n\tforeach_declared_ptr(fillState, state->fillStateListAsc)\n\t{\n\t\tfloat4 totalCost = 0;\n\t\tShardCost *shardCost = NULL;\n\t\tShardCost *prevShardCost = NULL;\n\t\tif (prevFillState != NULL)\n\t\t{\n\t\t\t/* Check that the previous fill state is more empty than this one */\n\t\t\tbool higherUtilization = fillState->utilization > prevFillState->utilization;\n\t\t\tbool sameUtilization = fillState->utilization == prevFillState->utilization;\n\t\t\tbool lowerOrSameCapacity = fillState->capacity <= prevFillState->capacity;\n\t\t\tAssert(higherUtilization || (sameUtilization && lowerOrSameCapacity));\n\t\t}\n\n\t\t/* Check that fillStateListDesc is the reversed version of fillStateListAsc */\n\t\tAssert(list_nth(state->fillStateListDesc, fillStateLength - fillStateIndex - 1) ==\n\t\t\t   fillState);\n\n\n\t\tforeach_declared_ptr(shardCost, fillState->shardCostListDesc)\n\t\t{\n\t\t\tif (prevShardCost != NULL)\n\t\t\t{\n\t\t\t\t/* Check that shard costs are sorted in descending order */\n\t\t\t\tAssert(shardCost->cost <= prevShardCost->cost);\n\t\t\t}\n\t\t\ttotalCost += shardCost->cost;\n\t\t\tprevShardCost = shardCost;\n\t\t}\n\n\t\t/* Check that utilization field is up to date. */\n\t\tAssert(fillState->utilization == CalculateUtilization(fillState->totalCost,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  fillState->capacity)); /* lgtm[cpp/equality-on-floats] */\n\n\t\t/*\n\t\t * Check that fillState->totalCost is within 0.1% difference of\n\t\t * sum(fillState->shardCostListDesc->cost)\n\t\t * We cannot compare exactly, because these numbers are floats and\n\t\t * fillState->totalCost is modified by doing + and - on it. So instead\n\t\t * we check that the numbers are roughly the same.\n\t\t */\n\t\tfloat4 absoluteDifferenceBetweenTotalCosts =\n\t\t\tfabsf(fillState->totalCost - totalCost);\n\t\tfloat4 maximumAbsoluteValueOfTotalCosts =\n\t\t\tfmaxf(fabsf(fillState->totalCost), fabsf(totalCost));\n\t\tAssert(absoluteDifferenceBetweenTotalCosts <= maximumAbsoluteValueOfTotalCosts /\n\t\t\t   1000);\n\n\t\tprevFillState = fillState;\n\t\tfillStateIndex++;\n\t}\n}\n\n\n#else\n#define CheckRebalanceStateInvariants(l) ((void) 0)\n#endif                          /* USE_ASSERT_CHECKING */\n\n/*\n * BigIntArrayDatumContains checks if the array contains the given number.\n */\nstatic bool\nBigIntArrayDatumContains(Datum *array, int arrayLength, uint64 toFind)\n{\n\tfor (int i = 0; i < arrayLength; i++)\n\t{\n\t\tif (DatumGetInt64(array[i]) == toFind)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n\n/*\n * FullShardPlacementList returns a List containing all the shard placements of\n * a specific table (excluding the excludedShardArray)\n */\nstatic List *\nFullShardPlacementList(Oid relationId, ArrayType *excludedShardArray)\n{\n\tList *shardPlacementList = NIL;\n\tCitusTableCacheEntry *citusTableCacheEntry = GetCitusTableCacheEntry(relationId);\n\tint shardIntervalArrayLength = citusTableCacheEntry->shardIntervalArrayLength;\n\tint excludedShardIdCount = ArrayObjectCount(excludedShardArray);\n\tDatum *excludedShardArrayDatum = DeconstructArrayObject(excludedShardArray);\n\n\tfor (int shardIndex = 0; shardIndex < shardIntervalArrayLength; shardIndex++)\n\t{\n\t\tShardInterval *shardInterval =\n\t\t\tcitusTableCacheEntry->sortedShardIntervalArray[shardIndex];\n\t\tGroupShardPlacement *placementArray =\n\t\t\tcitusTableCacheEntry->arrayOfPlacementArrays[shardIndex];\n\t\tint numberOfPlacements =\n\t\t\tcitusTableCacheEntry->arrayOfPlacementArrayLengths[shardIndex];\n\n\t\tif (BigIntArrayDatumContains(excludedShardArrayDatum, excludedShardIdCount,\n\t\t\t\t\t\t\t\t\t shardInterval->shardId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (int placementIndex = 0; placementIndex < numberOfPlacements;\n\t\t\t placementIndex++)\n\t\t{\n\t\t\tGroupShardPlacement *groupPlacement = &placementArray[placementIndex];\n\t\t\tWorkerNode *worker = LookupNodeForGroup(groupPlacement->groupId);\n\t\t\tShardPlacement *placement = CitusMakeNode(ShardPlacement);\n\t\t\tplacement->shardId = groupPlacement->shardId;\n\t\t\tplacement->shardLength = groupPlacement->shardLength;\n\t\t\tplacement->nodeId = worker->nodeId;\n\t\t\tplacement->nodeName = pstrdup(worker->workerName);\n\t\t\tplacement->nodePort = worker->workerPort;\n\t\t\tplacement->placementId = groupPlacement->placementId;\n\n\t\t\tshardPlacementList = lappend(shardPlacementList, placement);\n\t\t}\n\t}\n\treturn SortList(shardPlacementList, CompareShardPlacements);\n}\n\n\n/*\n * SortedActiveWorkers returns all the active workers like\n * ActiveReadableNodeList, but sorted.\n */\nstatic List *\nSortedActiveWorkers()\n{\n\tList *activeWorkerList = ActiveReadableNodeList();\n\treturn SortList(activeWorkerList, CompareWorkerNodes);\n}\n\n\n/*\n * GetRebalanceSteps returns a List of PlacementUpdateEvents that are needed to\n * rebalance a list of tables.\n */\nstatic List *\nGetRebalanceSteps(RebalanceOptions *options)\n{\n\tEnsureShardCostUDF(options->rebalanceStrategy->shardCostFunction);\n\tEnsureNodeCapacityUDF(options->rebalanceStrategy->nodeCapacityFunction);\n\tEnsureShardAllowedOnNodeUDF(options->rebalanceStrategy->shardAllowedOnNodeFunction);\n\n\tRebalanceContext context;\n\tmemset(&context, 0, sizeof(RebalanceContext));\n\tfmgr_info(options->rebalanceStrategy->shardCostFunction, &context.shardCostUDF);\n\tfmgr_info(options->rebalanceStrategy->nodeCapacityFunction, &context.nodeCapacityUDF);\n\tfmgr_info(options->rebalanceStrategy->shardAllowedOnNodeFunction,\n\t\t\t  &context.shardAllowedOnNodeUDF);\n\n\tRebalancePlanFunctions rebalancePlanFunctions = {\n\t\t.shardAllowedOnNode = ShardAllowedOnNode,\n\t\t.nodeCapacity = NodeCapacity,\n\t\t.shardCost = GetShardCost,\n\t\t.context = &context,\n\t};\n\n\tif (options->involvedWorkerNodeList == NULL)\n\t{\n\t\t/*\n\t\t * If the user did not specify a list of worker nodes, we use all the\n\t\t * active worker nodes.\n\t\t */\n\t\toptions->involvedWorkerNodeList = SortedActiveWorkers();\n\t}\n\n\t/* sort the lists to make the function more deterministic */\n\tList *activeWorkerList = options->involvedWorkerNodeList; /*SortedActiveWorkers(); */\n\tint shardAllowedNodeCount = 0;\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, activeWorkerList)\n\t{\n\t\tif (workerNode->shouldHaveShards)\n\t\t{\n\t\t\tshardAllowedNodeCount++;\n\t\t}\n\t}\n\n\tif (shardAllowedNodeCount < ShardReplicationFactor)\n\t{\n\t\tereport(ERROR, (errmsg(\"Shard replication factor (%d) cannot be greater than \"\n\t\t\t\t\t\t\t   \"number of nodes with should_have_shards=true (%d).\",\n\t\t\t\t\t\t\t   ShardReplicationFactor, shardAllowedNodeCount)));\n\t}\n\n\tList *activeShardPlacementListList = NIL;\n\tList *unbalancedShards = NIL;\n\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, options->relationIdList)\n\t{\n\t\tList *shardPlacementList = FullShardPlacementList(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  options->excludedShardArray);\n\t\tList *activeShardPlacementListForRelation =\n\t\t\tFilterShardPlacementList(shardPlacementList, IsActiveShardPlacement);\n\n\t\tif (options->workerNode != NULL)\n\t\t{\n\t\t\tactiveShardPlacementListForRelation = FilterActiveShardPlacementListByNode(\n\t\t\t\tshardPlacementList, options->workerNode);\n\t\t}\n\n\t\tif (list_length(activeShardPlacementListForRelation) >= shardAllowedNodeCount)\n\t\t{\n\t\t\tactiveShardPlacementListList = lappend(activeShardPlacementListList,\n\t\t\t\t\t\t\t\t\t\t\t\t   activeShardPlacementListForRelation);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * If the number of shard groups are less than the number of worker nodes,\n\t\t\t * at least one of the worker nodes will remain empty. For such cases,\n\t\t\t * we consider those shard groups as a colocation group and try to\n\t\t\t * distribute them across the cluster.\n\t\t\t */\n\t\t\tunbalancedShards = list_concat(unbalancedShards,\n\t\t\t\t\t\t\t\t\t\t   activeShardPlacementListForRelation);\n\t\t}\n\t}\n\n\tif (list_length(unbalancedShards) > 0)\n\t{\n\t\tactiveShardPlacementListList = lappend(activeShardPlacementListList,\n\t\t\t\t\t\t\t\t\t\t\t   unbalancedShards);\n\t}\n\n\tif (options->threshold < options->rebalanceStrategy->minimumThreshold)\n\t{\n\t\tereport(WARNING, (errmsg(\n\t\t\t\t\t\t\t  \"the given threshold is lower than the minimum \"\n\t\t\t\t\t\t\t  \"threshold allowed by the rebalance strategy, \"\n\t\t\t\t\t\t\t  \"using the minimum allowed threshold instead\"\n\t\t\t\t\t\t\t  ),\n\t\t\t\t\t\t  errdetail(\"Using threshold of %.2f\",\n\t\t\t\t\t\t\t\t\toptions->rebalanceStrategy->minimumThreshold\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t  ));\n\t\toptions->threshold = options->rebalanceStrategy->minimumThreshold;\n\t}\n\n\treturn RebalancePlacementUpdates(activeWorkerList,\n\t\t\t\t\t\t\t\t\t activeShardPlacementListList,\n\t\t\t\t\t\t\t\t\t options->threshold,\n\t\t\t\t\t\t\t\t\t options->maxShardMoves,\n\t\t\t\t\t\t\t\t\t options->drainOnly,\n\t\t\t\t\t\t\t\t\t options->improvementThreshold,\n\t\t\t\t\t\t\t\t\t &rebalancePlanFunctions);\n}\n\n\n/*\n * ShardAllowedOnNode determines if shard is allowed on a specific worker node.\n */\nstatic bool\nShardAllowedOnNode(uint64 shardId, WorkerNode *workerNode, void *voidContext)\n{\n\tif (!workerNode->shouldHaveShards)\n\t{\n\t\treturn false;\n\t}\n\n\tRebalanceContext *context = voidContext;\n\tDatum allowed = FunctionCall2(&context->shardAllowedOnNodeUDF, shardId,\n\t\t\t\t\t\t\t\t  workerNode->nodeId);\n\treturn DatumGetBool(allowed);\n}\n\n\n/*\n * NodeCapacity returns the relative capacity of a node. A node with capacity 2\n * can contain twice as many shards as a node with capacity 1. The actual\n * capacity can be a number grounded in reality, like the disk size, number of\n * cores, but it doesn't have to be.\n */\nstatic float4\nNodeCapacity(WorkerNode *workerNode, void *voidContext)\n{\n\tif (!workerNode->shouldHaveShards)\n\t{\n\t\treturn 0;\n\t}\n\n\tRebalanceContext *context = voidContext;\n\tDatum capacity = FunctionCall1(&context->nodeCapacityUDF, workerNode->nodeId);\n\treturn DatumGetFloat4(capacity);\n}\n\n\n/*\n * GetShardCost returns the cost of the given shard. A shard with cost 2 will\n * be weighted as heavily as two shards with cost 1. This cost number can be a\n * number grounded in reality, like the shard size on disk, but it doesn't have\n * to be.\n */\nstatic ShardCost\nGetShardCost(uint64 shardId, void *voidContext)\n{\n\tShardCost shardCost = { 0 };\n\tshardCost.shardId = shardId;\n\tRebalanceContext *context = voidContext;\n\tDatum shardCostDatum = FunctionCall1(&context->shardCostUDF, UInt64GetDatum(shardId));\n\tshardCost.cost = DatumGetFloat4(shardCostDatum);\n\treturn shardCost;\n}\n\n\n/*\n * citus_shard_cost_by_disk_size gets the cost for a shard based on the disk\n * size of the shard on a worker. The worker to check the disk size is\n * determined by choosing the first active placement for the shard. The disk\n * size is calculated using pg_total_relation_size, so it includes indexes.\n *\n * SQL signature:\n * citus_shard_cost_by_disk_size(shardid bigint) returns float4\n */\nDatum\ncitus_shard_cost_by_disk_size(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tuint64 shardId = PG_GETARG_INT64(0);\n\tbool missingOk = false;\n\tShardPlacement *shardPlacement = ActiveShardPlacement(shardId, missingOk);\n\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"CostByDiscSizeContext\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\tList *colocatedShardList = ColocatedNonPartitionShardIntervalList(shardInterval);\n\n\tuint64 colocationSizeInBytes = ShardListSizeInBytes(colocatedShardList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardPlacement->nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardPlacement->nodePort);\n\n\tMemoryContextSwitchTo(oldContext);\n\tMemoryContextReset(localContext);\n\n\tcolocationSizeInBytes += RebalancerByDiskSizeBaseCost;\n\n\tif (colocationSizeInBytes <= 0)\n\t{\n\t\tPG_RETURN_FLOAT4(1);\n\t}\n\n\tPG_RETURN_FLOAT4(colocationSizeInBytes);\n}\n\n\n/*\n * GetColocatedRebalanceSteps takes a List of PlacementUpdateEvents and creates\n * a new List of containing those and all the updates for colocated shards.\n */\nstatic List *\nGetColocatedRebalanceSteps(List *placementUpdateList)\n{\n\tListCell *placementUpdateCell = NULL;\n\tList *colocatedUpdateList = NIL;\n\n\tforeach(placementUpdateCell, placementUpdateList)\n\t{\n\t\tPlacementUpdateEvent *placementUpdate = lfirst(placementUpdateCell);\n\t\tShardInterval *shardInterval = LoadShardInterval(placementUpdate->shardId);\n\t\tList *colocatedShardList = ColocatedShardIntervalList(shardInterval);\n\t\tListCell *colocatedShardCell = NULL;\n\n\t\tforeach(colocatedShardCell, colocatedShardList)\n\t\t{\n\t\t\tShardInterval *colocatedShard = lfirst(colocatedShardCell);\n\t\t\tPlacementUpdateEvent *colocatedUpdate = palloc0(sizeof(PlacementUpdateEvent));\n\n\t\t\tcolocatedUpdate->shardId = colocatedShard->shardId;\n\t\t\tcolocatedUpdate->sourceNode = placementUpdate->sourceNode;\n\t\t\tcolocatedUpdate->targetNode = placementUpdate->targetNode;\n\t\t\tcolocatedUpdate->updateType = placementUpdate->updateType;\n\n\t\t\tcolocatedUpdateList = lappend(colocatedUpdateList, colocatedUpdate);\n\t\t}\n\t}\n\n\treturn colocatedUpdateList;\n}\n\n\n/*\n * AcquireRelationColocationLock tries to acquire a lock for\n * rebalance/replication. If this is it not possible it fails\n * instantly because this means another rebalance/replication\n * is currently happening. This would really mess up planning.\n */\nstatic void\nAcquireRebalanceColocationLock(Oid relationId, const char *operationName)\n{\n\tuint32 lockId = relationId;\n\tLOCKTAG tag;\n\n\tCitusTableCacheEntry *citusTableCacheEntry = GetCitusTableCacheEntry(relationId);\n\tif (citusTableCacheEntry->colocationId != INVALID_COLOCATION_ID)\n\t{\n\t\tlockId = citusTableCacheEntry->colocationId;\n\t}\n\n\tSET_LOCKTAG_REBALANCE_COLOCATION(tag, (int64) lockId);\n\n\tLockAcquireResult lockAcquired = LockAcquire(&tag, ExclusiveLock, false, true);\n\tif (!lockAcquired)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not acquire the lock required to %s %s\",\n\t\t\t\t\t\t\t   operationName,\n\t\t\t\t\t\t\t   generate_qualified_relation_name(relationId)),\n\t\t\t\t\t\terrdetail(\"It means that either a concurrent shard move \"\n\t\t\t\t\t\t\t\t  \"or shard copy is happening.\"),\n\t\t\t\t\t\terrhint(\"Make sure that the concurrent operation has \"\n\t\t\t\t\t\t\t\t\"finished and re-run the command\")));\n\t}\n}\n\n\n/*\n * AcquirePlacementColocationLock tries to acquire a lock for\n * rebalance/replication while moving/copying the placement. If this\n * is it not possible it fails instantly because this means\n * another move/copy is currently happening. This would really mess up planning.\n */\nvoid\nAcquirePlacementColocationLock(Oid relationId, int lockMode,\n\t\t\t\t\t\t\t   const char *operationName)\n{\n\tuint32 lockId = relationId;\n\tLOCKTAG tag;\n\n\tCitusTableCacheEntry *citusTableCacheEntry = GetCitusTableCacheEntry(relationId);\n\tif (citusTableCacheEntry->colocationId != INVALID_COLOCATION_ID)\n\t{\n\t\tlockId = citusTableCacheEntry->colocationId;\n\t}\n\n\tSET_LOCKTAG_REBALANCE_PLACEMENT_COLOCATION(tag, (int64) lockId);\n\n\tLockAcquireResult lockAcquired = LockAcquire(&tag, lockMode, false, true);\n\tif (!lockAcquired)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not acquire the lock required to %s %s\",\n\t\t\t\t\t\t\t   operationName,\n\t\t\t\t\t\t\t   generate_qualified_relation_name(relationId)),\n\t\t\t\t\t\terrdetail(\"It means that either a concurrent shard move \"\n\t\t\t\t\t\t\t\t  \"or colocated distributed table creation is \"\n\t\t\t\t\t\t\t\t  \"happening.\"),\n\t\t\t\t\t\terrhint(\"Make sure that the concurrent operation has \"\n\t\t\t\t\t\t\t\t\"finished and re-run the command\")));\n\t}\n}\n\n\n/*\n * GetResponsiveWorkerList returns a List of workers that respond to new\n * connection requests.\n */\nstatic List *\nGetResponsiveWorkerList()\n{\n\tList *activeWorkerList = ActiveReadableNodeList();\n\tListCell *activeWorkerCell = NULL;\n\tList *responsiveWorkerList = NIL;\n\n\tforeach(activeWorkerCell, activeWorkerList)\n\t{\n\t\tWorkerNode *worker = lfirst(activeWorkerCell);\n\t\tint connectionFlag = FORCE_NEW_CONNECTION;\n\n\t\tMultiConnection *connection = GetNodeConnection(connectionFlag,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tworker->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tworker->workerPort);\n\n\t\tif (connection != NULL && connection->pgConn != NULL)\n\t\t{\n\t\t\tif (PQstatus(connection->pgConn) == CONNECTION_OK)\n\t\t\t{\n\t\t\t\tresponsiveWorkerList = lappend(responsiveWorkerList, worker);\n\t\t\t}\n\n\t\t\tCloseConnection(connection);\n\t\t}\n\t}\n\treturn responsiveWorkerList;\n}\n\n\n/*\n * ExecutePlacementUpdates copies or moves a shard placement by calling the\n * corresponding functions in Citus in a separate subtransaction for each\n * update.\n */\nstatic void\nExecutePlacementUpdates(List *placementUpdateList, Oid shardReplicationModeOid,\n\t\t\t\t\t\tchar *noticeOperation)\n{\n\tList *responsiveWorkerList = GetResponsiveWorkerList();\n\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"ExecutePlacementLoopContext\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\tListCell *placementUpdateCell = NULL;\n\n\tDropOrphanedResourcesInSeparateTransaction();\n\n\tforeach(placementUpdateCell, placementUpdateList)\n\t{\n\t\tPlacementUpdateEvent *placementUpdate = lfirst(placementUpdateCell);\n\t\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t\t \"%s shard %lu from %s:%u to %s:%u ...\",\n\t\t\t\t\t\t\t noticeOperation,\n\t\t\t\t\t\t\t placementUpdate->shardId,\n\t\t\t\t\t\t\t placementUpdate->sourceNode->workerName,\n\t\t\t\t\t\t\t placementUpdate->sourceNode->workerPort,\n\t\t\t\t\t\t\t placementUpdate->targetNode->workerName,\n\t\t\t\t\t\t\t placementUpdate->targetNode->workerPort\n\t\t\t\t\t\t\t )));\n\t\tUpdateShardPlacement(placementUpdate, responsiveWorkerList,\n\t\t\t\t\t\t\t shardReplicationModeOid);\n\t\tMemoryContextReset(localContext);\n\t}\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * SetupRebalanceMonitor initializes the dynamic shared memory required for storing the\n * progress information of a rebalance process. The function takes a List of\n * PlacementUpdateEvents for all shards that will be moved (including colocated\n * ones) and the relation id of the target table. The dynamic shared memory\n * portion consists of a RebalanceMonitorHeader and multiple\n * PlacementUpdateEventProgress, one for each planned shard placement move. The\n * dsm_handle of the created segment is saved in the progress of the current backend so\n * that it can be read by external agents such as get_rebalance_progress function by\n * calling pg_stat_get_progress_info UDF. Since currently only VACUUM commands are\n * officially allowed as the command type, we describe ourselves as a VACUUM command and\n * in order to distinguish a rebalancer progress from regular VACUUM progresses, we put\n * a magic number to the first progress field as an indicator. Finally we return the\n * dsm handle so that it can be used for updating the progress and cleaning things up.\n */\nvoid\nSetupRebalanceMonitor(List *placementUpdateList,\n\t\t\t\t\t  Oid relationId,\n\t\t\t\t\t  uint64 initialProgressState,\n\t\t\t\t\t  PlacementUpdateStatus initialStatus)\n{\n\tList *colocatedUpdateList = GetColocatedRebalanceSteps(placementUpdateList);\n\tListCell *colocatedUpdateCell = NULL;\n\n\tdsm_handle dsmHandle;\n\tProgressMonitorData *monitor = CreateProgressMonitor(\n\t\tlist_length(colocatedUpdateList),\n\t\tsizeof(PlacementUpdateEventProgress),\n\t\t&dsmHandle);\n\tPlacementUpdateEventProgress *rebalanceSteps = ProgressMonitorSteps(monitor);\n\n\tint32 eventIndex = 0;\n\tforeach(colocatedUpdateCell, colocatedUpdateList)\n\t{\n\t\tPlacementUpdateEvent *colocatedUpdate = lfirst(colocatedUpdateCell);\n\t\tPlacementUpdateEventProgress *event = rebalanceSteps + eventIndex;\n\n\t\tstrlcpy(event->sourceName, colocatedUpdate->sourceNode->workerName, 255);\n\t\tstrlcpy(event->targetName, colocatedUpdate->targetNode->workerName, 255);\n\n\t\tevent->shardId = colocatedUpdate->shardId;\n\t\tevent->sourcePort = colocatedUpdate->sourceNode->workerPort;\n\t\tevent->targetPort = colocatedUpdate->targetNode->workerPort;\n\t\tevent->updateType = colocatedUpdate->updateType;\n\t\tpg_atomic_init_u64(&event->updateStatus, initialStatus);\n\t\tpg_atomic_init_u64(&event->progress, initialProgressState);\n\n\t\teventIndex++;\n\t}\n\tRegisterProgressMonitor(REBALANCE_ACTIVITY_MAGIC_NUMBER, relationId, dsmHandle);\n}\n\n\n/*\n * rebalance_table_shards rebalances the shards across the workers.\n *\n * SQL signature:\n *\n * rebalance_table_shards(\n *     relation regclass,\n *     threshold float4,\n *     max_shard_moves int,\n *     excluded_shard_list bigint[],\n *     shard_transfer_mode citus.shard_transfer_mode,\n *     drain_only boolean,\n *     rebalance_strategy name\n * ) RETURNS VOID\n */\nDatum\nrebalance_table_shards(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tList *relationIdList = NIL;\n\tif (!PG_ARGISNULL(0))\n\t{\n\t\tOid relationId = PG_GETARG_OID(0);\n\t\tErrorIfMoveUnsupportedTableType(relationId);\n\t\trelationIdList = list_make1_oid(relationId);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * Note that we don't need to do any checks to error out for\n\t\t * citus local tables here as NonColocatedDistRelationIdList\n\t\t * already doesn't return non-distributed tables.\n\t\t */\n\t\trelationIdList = NonColocatedDistRelationIdList();\n\t}\n\n\tPG_ENSURE_ARGNOTNULL(2, \"max_shard_moves\");\n\tPG_ENSURE_ARGNOTNULL(3, \"excluded_shard_list\");\n\tPG_ENSURE_ARGNOTNULL(4, \"shard_transfer_mode\");\n\tPG_ENSURE_ARGNOTNULL(5, \"drain_only\");\n\n\tForm_pg_dist_rebalance_strategy strategy = GetRebalanceStrategy(\n\t\tPG_GETARG_NAME_OR_NULL(6));\n\tRebalanceOptions options = {\n\t\t.relationIdList = relationIdList,\n\t\t.threshold = PG_GETARG_FLOAT4_OR_DEFAULT(1, strategy->defaultThreshold),\n\t\t.maxShardMoves = PG_GETARG_INT32(2),\n\t\t.excludedShardArray = PG_GETARG_ARRAYTYPE_P(3),\n\t\t.drainOnly = PG_GETARG_BOOL(5),\n\t\t.rebalanceStrategy = strategy,\n\t\t.involvedWorkerNodeList = NULL,\n\t\t.improvementThreshold = strategy->improvementThreshold,\n\t};\n\tOid shardTransferModeOid = PG_GETARG_OID(4);\n\tRebalanceTableShards(&options, shardTransferModeOid);\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_rebalance_start rebalances the shards across the workers.\n *\n * SQL signature:\n *\n * citus_rebalance_start(\n *     rebalance_strategy name DEFAULT NULL,\n *     drain_only boolean DEFAULT false,\n *     shard_transfer_mode citus.shard_transfer_mode default 'auto'\n * ) RETURNS VOID\n */\nDatum\ncitus_rebalance_start(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tList *relationIdList = NonColocatedDistRelationIdList();\n\tForm_pg_dist_rebalance_strategy strategy =\n\t\tGetRebalanceStrategy(PG_GETARG_NAME_OR_NULL(0));\n\n\tPG_ENSURE_ARGNOTNULL(1, \"drain_only\");\n\tbool drainOnly = PG_GETARG_BOOL(1);\n\n\tPG_ENSURE_ARGNOTNULL(2, \"shard_transfer_mode\");\n\tOid shardTransferModeOid = PG_GETARG_OID(2);\n\n\tPG_ENSURE_ARGNOTNULL(3, \"parallel_transfer_reference_tables\");\n\tbool ParallelTransferReferenceTables = PG_GETARG_BOOL(3);\n\n\tPG_ENSURE_ARGNOTNULL(4, \"parallel_transfer_colocated_shards\");\n\tbool ParallelTransferColocatedShards = PG_GETARG_BOOL(4);\n\n\tRebalanceOptions options = {\n\t\t.relationIdList = relationIdList,\n\t\t.threshold = strategy->defaultThreshold,\n\t\t.maxShardMoves = 10000000,\n\t\t.excludedShardArray = construct_empty_array(INT4OID),\n\t\t.drainOnly = drainOnly,\n\t\t.rebalanceStrategy = strategy,\n\t\t.improvementThreshold = strategy->improvementThreshold,\n\t};\n\tint jobId = RebalanceTableShardsBackground(&options, shardTransferModeOid,\n\t\t\t\t\t\t\t\t\t\t\t   ParallelTransferReferenceTables,\n\t\t\t\t\t\t\t\t\t\t\t   ParallelTransferColocatedShards);\n\n\tif (jobId == 0)\n\t{\n\t\tPG_RETURN_NULL();\n\t}\n\tPG_RETURN_INT64(jobId);\n}\n\n\n/*\n * citus_rebalance_stop stops any ongoing background rebalance that is executing.\n * Raises an error when there is no backgound rebalance ongoing at the moment.\n */\nDatum\ncitus_rebalance_stop(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint64 jobId = 0;\n\tif (!HasNonTerminalJobOfType(\"rebalance\", &jobId))\n\t{\n\t\tereport(ERROR, (errmsg(\"no ongoing rebalance that can be stopped\")));\n\t}\n\n\tDirectFunctionCall1(citus_job_cancel, Int64GetDatum(jobId));\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_rebalance_wait waits till an ongoing background rebalance has finished execution.\n * A warning will be displayed if no rebalance is ongoing.\n */\nDatum\ncitus_rebalance_wait(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint64 jobId = 0;\n\tif (!HasNonTerminalJobOfType(\"rebalance\", &jobId))\n\t{\n\t\tereport(WARNING, (errmsg(\"no ongoing rebalance that can be waited on\")));\n\t\tPG_RETURN_VOID();\n\t}\n\n\tcitus_job_wait_internal(jobId, NULL);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * GetRebalanceStrategy returns the rebalance strategy from\n * pg_dist_rebalance_strategy matching the given name. If name is NULL it\n * returns the default rebalance strategy from pg_dist_rebalance_strategy.\n */\nstatic Form_pg_dist_rebalance_strategy\nGetRebalanceStrategy(Name name)\n{\n\tRelation pgDistRebalanceStrategy = table_open(DistRebalanceStrategyRelationId(),\n\t\t\t\t\t\t\t\t\t\t\t\t  AccessShareLock);\n\n\tconst int scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\tif (name == NULL)\n\t{\n\t\t/* WHERE default_strategy=true */\n\t\tScanKeyInit(&scanKey[0], Anum_pg_dist_rebalance_strategy_default_strategy,\n\t\t\t\t\tBTEqualStrategyNumber, F_BOOLEQ, BoolGetDatum(true));\n\t}\n\telse\n\t{\n\t\t/* WHERE name=$name */\n\t\tScanKeyInit(&scanKey[0], Anum_pg_dist_rebalance_strategy_name,\n\t\t\t\t\tBTEqualStrategyNumber, F_NAMEEQ, NameGetDatum(name));\n\t}\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistRebalanceStrategy,\n\t\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tif (name == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\"no rebalance_strategy was provided, but there is also no default strategy set\")));\n\t\t}\n\t\tereport(ERROR, (errmsg(\"could not find rebalance strategy with name %s\",\n\t\t\t\t\t\t\t   (char *) name)));\n\t}\n\n\tForm_pg_dist_rebalance_strategy strategy =\n\t\t(Form_pg_dist_rebalance_strategy) GETSTRUCT(heapTuple);\n\tForm_pg_dist_rebalance_strategy strategy_copy =\n\t\tpalloc0(sizeof(FormData_pg_dist_rebalance_strategy));\n\n\t/* Copy data over by dereferencing */\n\t*strategy_copy = *strategy;\n\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistRebalanceStrategy, NoLock);\n\n\treturn strategy_copy;\n}\n\n\n/*\n * citus_drain_node drains a node by setting shouldhaveshards to false and\n * running the rebalancer after in drain_only mode.\n */\nDatum\ncitus_drain_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tPG_ENSURE_ARGNOTNULL(0, \"nodename\");\n\tPG_ENSURE_ARGNOTNULL(1, \"nodeport\");\n\tPG_ENSURE_ARGNOTNULL(2, \"shard_transfer_mode\");\n\n\ttext *nodeNameText = PG_GETARG_TEXT_P(0);\n\tint32 nodePort = PG_GETARG_INT32(1);\n\tOid shardTransferModeOid = PG_GETARG_OID(2);\n\tForm_pg_dist_rebalance_strategy strategy = GetRebalanceStrategy(\n\t\tPG_GETARG_NAME_OR_NULL(3));\n\tRebalanceOptions options = {\n\t\t.relationIdList = NonColocatedDistRelationIdList(),\n\t\t.threshold = strategy->defaultThreshold,\n\t\t.maxShardMoves = 0,\n\t\t.excludedShardArray = construct_empty_array(INT4OID),\n\t\t.drainOnly = true,\n\t\t.rebalanceStrategy = strategy,\n\t};\n\n\tchar *nodeName = text_to_cstring(nodeNameText);\n\toptions.workerNode = FindWorkerNodeOrError(nodeName, nodePort);\n\n\t/*\n\t * This is done in a separate session. This way it's not undone if the\n\t * draining fails midway through.\n\t */\n\tExecuteRebalancerCommandInSeparateTransaction(psprintf(\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"SELECT master_set_node_property(%s, %i, 'shouldhaveshards', false)\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  quote_literal_cstr(nodeName),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  nodePort));\n\n\tRebalanceTableShards(&options, shardTransferModeOid);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * replicate_table_shards replicates under-replicated shards of the specified\n * table.\n */\nDatum\nreplicate_table_shards(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tOid relationId = PG_GETARG_OID(0);\n\tuint32 shardReplicationFactor = PG_GETARG_INT32(1);\n\tint32 maxShardCopies = PG_GETARG_INT32(2);\n\tArrayType *excludedShardArray = PG_GETARG_ARRAYTYPE_P(3);\n\tOid shardReplicationModeOid = PG_GETARG_OID(4);\n\n\tif (IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot replicate single shard tables' shards\")));\n\t}\n\n\tchar transferMode = LookupShardTransferMode(shardReplicationModeOid);\n\tEnsureReferenceTablesExistOnAllNodesExtended(transferMode);\n\n\tAcquireRebalanceColocationLock(relationId, \"replicate\");\n\n\tList *activeWorkerList = SortedActiveWorkers();\n\tList *shardPlacementList = FullShardPlacementList(relationId, excludedShardArray);\n\tList *activeShardPlacementList = FilterShardPlacementList(shardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  IsActiveShardPlacement);\n\n\tList *placementUpdateList = ReplicationPlacementUpdates(activeWorkerList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tactiveShardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardReplicationFactor);\n\tplacementUpdateList = list_truncate(placementUpdateList, maxShardCopies);\n\n\tExecutePlacementUpdates(placementUpdateList, shardReplicationModeOid, \"Copying\");\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * master_drain_node is a wrapper function for old UDF name.\n */\nDatum\nmaster_drain_node(PG_FUNCTION_ARGS)\n{\n\treturn citus_drain_node(fcinfo);\n}\n\n\n/*\n * get_rebalance_table_shards_plan function calculates the shard move steps\n * required for the rebalance operations including the ones for colocated\n * tables.\n *\n * SQL signature:\n *\n * get_rebalance_table_shards_plan(\n *     relation regclass,\n *     threshold float4,\n *     max_shard_moves int,\n *     excluded_shard_list bigint[],\n *     drain_only boolean,\n *     rebalance_strategy name\n * )\n */\nDatum\nget_rebalance_table_shards_plan(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tList *relationIdList = NIL;\n\tif (!PG_ARGISNULL(0))\n\t{\n\t\tOid relationId = PG_GETARG_OID(0);\n\t\tErrorIfMoveUnsupportedTableType(relationId);\n\n\t\trelationIdList = list_make1_oid(relationId);\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * Note that we don't need to do any checks to error out for\n\t\t * citus local tables here as NonColocatedDistRelationIdList\n\t\t * already doesn't return non-distributed tables.\n\t\t */\n\t\trelationIdList = NonColocatedDistRelationIdList();\n\t}\n\n\tPG_ENSURE_ARGNOTNULL(2, \"max_shard_moves\");\n\tPG_ENSURE_ARGNOTNULL(3, \"excluded_shard_list\");\n\tPG_ENSURE_ARGNOTNULL(4, \"drain_only\");\n\n\tForm_pg_dist_rebalance_strategy strategy = GetRebalanceStrategy(\n\t\tPG_GETARG_NAME_OR_NULL(5));\n\tRebalanceOptions options = {\n\t\t.relationIdList = relationIdList,\n\t\t.threshold = PG_GETARG_FLOAT4_OR_DEFAULT(1, strategy->defaultThreshold),\n\t\t.maxShardMoves = PG_GETARG_INT32(2),\n\t\t.excludedShardArray = PG_GETARG_ARRAYTYPE_P(3),\n\t\t.drainOnly = PG_GETARG_BOOL(4),\n\t\t.rebalanceStrategy = strategy,\n\t\t.improvementThreshold = PG_GETARG_FLOAT4_OR_DEFAULT(\n\t\t\t6, strategy->improvementThreshold),\n\t};\n\n\n\tList *placementUpdateList = GetRebalanceSteps(&options);\n\tList *colocatedUpdateList = GetColocatedRebalanceSteps(placementUpdateList);\n\tListCell *colocatedUpdateCell = NULL;\n\n\tTupleDesc tupdesc;\n\tTuplestorestate *tupstore = SetupTuplestore(fcinfo, &tupdesc);\n\n\tforeach(colocatedUpdateCell, colocatedUpdateList)\n\t{\n\t\tPlacementUpdateEvent *colocatedUpdate = lfirst(colocatedUpdateCell);\n\t\tDatum values[7];\n\t\tbool nulls[7];\n\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(nulls, 0, sizeof(nulls));\n\n\t\tvalues[0] = ObjectIdGetDatum(RelationIdForShard(colocatedUpdate->shardId));\n\t\tvalues[1] = UInt64GetDatum(colocatedUpdate->shardId);\n\t\tvalues[2] = UInt64GetDatum(ShardLength(colocatedUpdate->shardId));\n\t\tvalues[3] = PointerGetDatum(cstring_to_text(\n\t\t\t\t\t\t\t\t\t\tcolocatedUpdate->sourceNode->workerName));\n\t\tvalues[4] = UInt32GetDatum(colocatedUpdate->sourceNode->workerPort);\n\t\tvalues[5] = PointerGetDatum(cstring_to_text(\n\t\t\t\t\t\t\t\t\t\tcolocatedUpdate->targetNode->workerName));\n\t\tvalues[6] = UInt32GetDatum(colocatedUpdate->targetNode->workerPort);\n\n\t\ttuplestore_putvalues(tupstore, tupdesc, values, nulls);\n\t}\n\n\treturn (Datum) 0;\n}\n\n\n/*\n * get_rebalance_progress collects information about the ongoing rebalance operations and\n * returns the concatenated list of steps involved in the operations, along with their\n * progress information. Currently the progress field can take 4 integer values\n * (-1: error, 0: waiting, 1: moving, 2: moved). The progress field is of type bigint\n * because we may implement a more granular, byte-level progress as a future improvement.\n */\nDatum\nget_rebalance_progress(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tList *segmentList = NIL;\n\tTupleDesc tupdesc;\n\tTuplestorestate *tupstore = SetupTuplestore(fcinfo, &tupdesc);\n\n\t/* get the addresses of all current rebalance monitors */\n\tList *rebalanceMonitorList = ProgressMonitorList(REBALANCE_ACTIVITY_MAGIC_NUMBER,\n\t\t\t\t\t\t\t\t\t\t\t\t\t &segmentList);\n\n\tProgressMonitorData *monitor = NULL;\n\tforeach_declared_ptr(monitor, rebalanceMonitorList)\n\t{\n\t\tPlacementUpdateEventProgress *placementUpdateEvents = ProgressMonitorSteps(\n\t\t\tmonitor);\n\t\tHTAB *shardStatistics = BuildWorkerShardStatisticsHash(placementUpdateEvents,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   monitor->stepCount);\n\t\tHTAB *shardSizes = BuildShardSizesHash(monitor, shardStatistics);\n\t\tfor (int eventIndex = 0; eventIndex < monitor->stepCount; eventIndex++)\n\t\t{\n\t\t\tPlacementUpdateEventProgress *step = placementUpdateEvents + eventIndex;\n\t\t\tuint64 shardId = step->shardId;\n\t\t\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\n\t\t\tuint64 sourceSize = WorkerShardSize(shardStatistics, step->sourceName,\n\t\t\t\t\t\t\t\t\t\t\t\tstep->sourcePort, shardId);\n\t\t\tuint64 targetSize = WorkerShardSize(shardStatistics, step->targetName,\n\t\t\t\t\t\t\t\t\t\t\t\tstep->targetPort, shardId);\n\n\t\t\tXLogRecPtr sourceLSN = WorkerLSN(shardStatistics, step->sourceName,\n\t\t\t\t\t\t\t\t\t\t\t step->sourcePort);\n\t\t\tXLogRecPtr targetLSN = WorkerShardLSN(shardStatistics, step->targetName,\n\t\t\t\t\t\t\t\t\t\t\t\t  step->targetPort, shardId);\n\n\t\t\tuint64 shardSize = 0;\n\t\t\tShardStatistics *shardSizesStat =\n\t\t\t\thash_search(shardSizes, &shardId, HASH_FIND, NULL);\n\t\t\tif (shardSizesStat)\n\t\t\t{\n\t\t\t\tshardSize = shardSizesStat->totalSize;\n\t\t\t}\n\n\t\t\tDatum values[15];\n\t\t\tbool nulls[15];\n\n\t\t\tmemset(values, 0, sizeof(values));\n\t\t\tmemset(nulls, 0, sizeof(nulls));\n\n\t\t\tvalues[0] = monitor->processId;\n\t\t\tvalues[1] = ObjectIdGetDatum(shardInterval->relationId);\n\t\t\tvalues[2] = UInt64GetDatum(shardId);\n\t\t\tvalues[3] = UInt64GetDatum(shardSize);\n\t\t\tvalues[4] = PointerGetDatum(cstring_to_text(step->sourceName));\n\t\t\tvalues[5] = UInt32GetDatum(step->sourcePort);\n\t\t\tvalues[6] = PointerGetDatum(cstring_to_text(step->targetName));\n\t\t\tvalues[7] = UInt32GetDatum(step->targetPort);\n\t\t\tvalues[8] = UInt64GetDatum(pg_atomic_read_u64(&step->progress));\n\t\t\tvalues[9] = UInt64GetDatum(sourceSize);\n\t\t\tvalues[10] = UInt64GetDatum(targetSize);\n\t\t\tvalues[11] = PointerGetDatum(\n\t\t\t\tcstring_to_text(PlacementUpdateTypeNames[step->updateType]));\n\t\t\tvalues[12] = LSNGetDatum(sourceLSN);\n\t\t\tif (sourceLSN == InvalidXLogRecPtr)\n\t\t\t{\n\t\t\t\tnulls[12] = true;\n\t\t\t}\n\n\t\t\tvalues[13] = LSNGetDatum(targetLSN);\n\t\t\tif (targetLSN == InvalidXLogRecPtr)\n\t\t\t{\n\t\t\t\tnulls[13] = true;\n\t\t\t}\n\n\t\t\tvalues[14] = PointerGetDatum(cstring_to_text(\n\t\t\t\t\t\t\t\t\t\t\t PlacementUpdateStatusNames[\n\t\t\t\t\t\t\t\t\t\t\t\t pg_atomic_read_u64(\n\t\t\t\t\t\t\t\t\t\t\t\t\t &step->updateStatus)]));\n\n\t\t\ttuplestore_putvalues(tupstore, tupdesc, values, nulls);\n\t\t}\n\t}\n\n\tDetachFromDSMSegments(segmentList);\n\n\treturn (Datum) 0;\n}\n\n\n/*\n * BuildShardSizesHash creates a hash that maps a shardid to its full size\n * within the cluster. It does this by using the rebalance progress monitor\n * state to find the node the shard is currently on. It then looks up the shard\n * size in the shardStatistics hashmap for this node.\n */\nstatic HTAB *\nBuildShardSizesHash(ProgressMonitorData *monitor, HTAB *shardStatistics)\n{\n\tHASHCTL info = {\n\t\t.keysize = sizeof(uint64),\n\t\t.entrysize = sizeof(ShardStatistics),\n\t\t.hcxt = CurrentMemoryContext\n\t};\n\n\tHTAB *shardSizes = hash_create(\n\t\t\"ShardSizeHash\", 32, &info,\n\t\tHASH_ELEM | HASH_CONTEXT | HASH_BLOBS);\n\tPlacementUpdateEventProgress *placementUpdateEvents = ProgressMonitorSteps(monitor);\n\n\tfor (int eventIndex = 0; eventIndex < monitor->stepCount; eventIndex++)\n\t{\n\t\tPlacementUpdateEventProgress *step = placementUpdateEvents + eventIndex;\n\n\t\tuint64 shardId = step->shardId;\n\t\tuint64 shardSize = 0;\n\t\tuint64 backupShardSize = 0;\n\t\tuint64 progress = pg_atomic_read_u64(&step->progress);\n\n\t\tuint64 sourceSize = WorkerShardSize(shardStatistics, step->sourceName,\n\t\t\t\t\t\t\t\t\t\t\tstep->sourcePort, shardId);\n\t\tuint64 targetSize = WorkerShardSize(shardStatistics, step->targetName,\n\t\t\t\t\t\t\t\t\t\t\tstep->targetPort, shardId);\n\n\t\tif (progress == REBALANCE_PROGRESS_WAITING ||\n\t\t\tprogress == REBALANCE_PROGRESS_MOVING)\n\t\t{\n\t\t\t/*\n\t\t\t * If we are not done with the move, the correct shard size is the\n\t\t\t * size on the source.\n\t\t\t */\n\t\t\tshardSize = sourceSize;\n\t\t\tbackupShardSize = targetSize;\n\t\t}\n\t\telse if (progress == REBALANCE_PROGRESS_MOVED)\n\t\t{\n\t\t\t/*\n\t\t\t * If we are done with the move, the correct shard size is the size\n\t\t\t * on the target\n\t\t\t */\n\t\t\tshardSize = targetSize;\n\t\t\tbackupShardSize = sourceSize;\n\t\t}\n\n\t\tif (shardSize == 0)\n\t\t{\n\t\t\tif (backupShardSize == 0)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We don't have any useful shard size. This can happen when a\n\t\t\t\t * shard is moved multiple times and it is not present on\n\t\t\t\t * either of these nodes. Probably the shard is on a worker\n\t\t\t\t * related to another event. In the weird case that this shard\n\t\t\t\t * is on the nodes and actually is size 0, we will have no\n\t\t\t\t * entry in the hashmap. When fetching from it we always\n\t\t\t\t * default to 0 if no entry is found, so that's fine.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Because of the way we fetch shard sizes they are from a slightly\n\t\t\t * earlier moment than the progress state we just read from shared\n\t\t\t * memory. Usually this is no problem, but there exist some race\n\t\t\t * conditions where this matters. For example, for very quick moves\n\t\t\t * it is possible that even though a step is now reported as MOVED,\n\t\t\t * when we read the shard sizes the move had not even started yet.\n\t\t\t * This in turn can mean that the target size is 0 while the source\n\t\t\t * size is not. We try to handle such rare edge cases by falling\n\t\t\t * back on the other shard size if that one is not 0.\n\t\t\t */\n\t\t\tshardSize = backupShardSize;\n\t\t}\n\n\n\t\tShardStatistics *currentWorkerStatistics =\n\t\t\thash_search(shardSizes, &shardId, HASH_ENTER, NULL);\n\t\tcurrentWorkerStatistics->totalSize = shardSize;\n\t}\n\treturn shardSizes;\n}\n\n\n/*\n * WorkerShardSize returns the size of a shard in bytes on a worker, based on\n * the workerShardStatisticsHash.\n */\nstatic uint64\nWorkerShardSize(HTAB *workerShardStatisticsHash, char *workerName, int workerPort,\n\t\t\t\tuint64 shardId)\n{\n\tWorkerHashKey workerKey = { 0 };\n\tstrlcpy(workerKey.hostname, workerName, MAX_NODE_LENGTH);\n\tworkerKey.port = workerPort;\n\n\tWorkerShardStatistics *workerStats =\n\t\thash_search(workerShardStatisticsHash, &workerKey, HASH_FIND, NULL);\n\tif (!workerStats)\n\t{\n\t\treturn 0;\n\t}\n\n\tShardStatistics *shardStats =\n\t\thash_search(workerStats->statistics, &shardId, HASH_FIND, NULL);\n\tif (!shardStats)\n\t{\n\t\treturn 0;\n\t}\n\treturn shardStats->totalSize;\n}\n\n\n/*\n * WorkerShardLSN returns the LSN of a shard on a worker, based on\n * the workerShardStatisticsHash. If there is no LSN data in the\n * statistics object, returns InvalidXLogRecPtr.\n */\nstatic XLogRecPtr\nWorkerShardLSN(HTAB *workerShardStatisticsHash, char *workerName, int workerPort,\n\t\t\t   uint64 shardId)\n{\n\tWorkerHashKey workerKey = { 0 };\n\tstrlcpy(workerKey.hostname, workerName, MAX_NODE_LENGTH);\n\tworkerKey.port = workerPort;\n\n\tWorkerShardStatistics *workerStats =\n\t\thash_search(workerShardStatisticsHash, &workerKey, HASH_FIND, NULL);\n\tif (!workerStats)\n\t{\n\t\treturn InvalidXLogRecPtr;\n\t}\n\n\tShardStatistics *shardStats =\n\t\thash_search(workerStats->statistics, &shardId, HASH_FIND, NULL);\n\tif (!shardStats)\n\t{\n\t\treturn InvalidXLogRecPtr;\n\t}\n\n\treturn shardStats->shardLSN;\n}\n\n\n/*\n * WorkerLSN returns the LSN of a worker, based on the workerShardStatisticsHash.\n * If there is no LSN data in the statistics object, returns InvalidXLogRecPtr.\n */\nstatic XLogRecPtr\nWorkerLSN(HTAB *workerShardStatisticsHash, char *workerName, int workerPort)\n{\n\tWorkerHashKey workerKey = { 0 };\n\tstrlcpy(workerKey.hostname, workerName, MAX_NODE_LENGTH);\n\tworkerKey.port = workerPort;\n\n\tWorkerShardStatistics *workerStats =\n\t\thash_search(workerShardStatisticsHash, &workerKey, HASH_FIND, NULL);\n\tif (!workerStats)\n\t{\n\t\treturn InvalidXLogRecPtr;\n\t}\n\n\treturn workerStats->workerLSN;\n}\n\n\n/*\n * BuildWorkerShardStatisticsHash returns a shard id -> shard statistics hash containing\n * sizes of shards on the source node and destination node.\n */\nstatic HTAB *\nBuildWorkerShardStatisticsHash(PlacementUpdateEventProgress *steps, int stepCount)\n{\n\tHTAB *shardsByWorker = GetMovedShardIdsByWorker(steps, stepCount, true);\n\n\tHASHCTL info = {\n\t\t.keysize = sizeof(WorkerHashKey),\n\t\t.entrysize = sizeof(WorkerShardStatistics),\n\t\t.hcxt = CurrentMemoryContext\n\t};\n\n\tHTAB *workerShardStatistics = hash_create(\"WorkerShardStatistics\", 32, &info,\n\t\t\t\t\t\t\t\t\t\t\t  HASH_ELEM | HASH_CONTEXT | HASH_BLOBS);\n\tWorkerShardIds *entry = NULL;\n\n\tHASH_SEQ_STATUS status;\n\thash_seq_init(&status, shardsByWorker);\n\twhile ((entry = hash_seq_search(&status)) != NULL)\n\t{\n\t\tint connectionFlags = 0;\n\t\tMultiConnection *connection = GetNodeConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tentry->worker.hostname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tentry->worker.port);\n\n\t\tHTAB *statistics =\n\t\t\tGetShardStatistics(connection, entry->shardIds);\n\n\t\tWorkerHashKey workerKey = { 0 };\n\t\tstrlcpy(workerKey.hostname, entry->worker.hostname, MAX_NODE_LENGTH);\n\t\tworkerKey.port = entry->worker.port;\n\n\t\tWorkerShardStatistics *moveStat =\n\t\t\thash_search(workerShardStatistics, &entry->worker, HASH_ENTER, NULL);\n\t\tmoveStat->statistics = statistics;\n\t\tmoveStat->workerLSN = GetRemoteLogPosition(connection);\n\t}\n\n\treturn workerShardStatistics;\n}\n\n\n/*\n * GetShardStatistics fetches the statics for the given shard ids over the\n * given connection. It returns a hashmap where the keys are the shard ids and\n * the values are the statistics.\n */\nstatic HTAB *\nGetShardStatistics(MultiConnection *connection, HTAB *shardIds)\n{\n\tStringInfo query = makeStringInfo();\n\n\tappendStringInfoString(\n\t\tquery,\n\t\t\"WITH shard_names (shard_id, schema_name, table_name) AS ((VALUES \");\n\n\tbool isFirst = true;\n\tuint64 *shardIdPtr = NULL;\n\tHASH_SEQ_STATUS status;\n\thash_seq_init(&status, shardIds);\n\twhile ((shardIdPtr = hash_seq_search(&status)) != NULL)\n\t{\n\t\tuint64 shardId = *shardIdPtr;\n\t\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\t\tOid relationId = shardInterval->relationId;\n\t\tchar *shardName = get_rel_name(relationId);\n\n\t\tAppendShardIdToName(&shardName, shardId);\n\n\t\tOid schemaId = get_rel_namespace(relationId);\n\t\tchar *schemaName = get_namespace_name(schemaId);\n\t\tif (!isFirst)\n\t\t{\n\t\t\tappendStringInfo(query, \", \");\n\t\t}\n\n\t\tappendStringInfo(query, \"(\" UINT64_FORMAT \",%s,%s)\",\n\t\t\t\t\t\t shardId,\n\t\t\t\t\t\t quote_literal_cstr(schemaName),\n\t\t\t\t\t\t quote_literal_cstr(shardName));\n\n\t\tisFirst = false;\n\t}\n\n\tappendStringInfoString(query, \"))\");\n\tappendStringInfoString(\n\t\tquery,\n\t\t\" SELECT shard_id, coalesce(pg_total_relation_size(tables.relid),0), tables.lsn\"\n\n\t\t/* for each shard in shardIds */\n\t\t\" FROM shard_names\"\n\n\t\t/* check if its name can be found in pg_class, if so return size */\n\t\t\" LEFT JOIN\"\n\t\t\" (SELECT c.oid AS relid, c.relname, n.nspname, ss.latest_end_lsn AS lsn\"\n\t\t\" FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace \"\n\t\t\" LEFT JOIN pg_subscription_rel sr ON sr.srrelid = c.oid \"\n\t\t\" LEFT JOIN pg_stat_subscription ss ON sr.srsubid = ss.subid) tables\"\n\t\t\" ON tables.relname = shard_names.table_name AND\"\n\t\t\" tables.nspname = shard_names.schema_name \");\n\n\tPGresult *result = NULL;\n\tint queryResult = ExecuteOptionalRemoteCommand(connection, query->data, &result);\n\tif (queryResult != RESPONSE_OKAY)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\"cannot get the size because of a connection error\")));\n\t}\n\n\tint rowCount = PQntuples(result);\n\tint colCount = PQnfields(result);\n\n\t/* This is not expected to ever happen, but we check just to be sure */\n\tif (colCount < 2)\n\t{\n\t\tereport(ERROR, (errmsg(\"unexpected number of columns returned by: %s\",\n\t\t\t\t\t\t\t   query->data)));\n\t}\n\n\tHASHCTL info = {\n\t\t.keysize = sizeof(uint64),\n\t\t.entrysize = sizeof(ShardStatistics),\n\t\t.hcxt = CurrentMemoryContext\n\t};\n\n\tHTAB *shardStatistics = hash_create(\"ShardStatisticsHash\", 32, &info,\n\t\t\t\t\t\t\t\t\t\tHASH_ELEM | HASH_CONTEXT | HASH_BLOBS);\n\n\tfor (int rowIndex = 0; rowIndex < rowCount; rowIndex++)\n\t{\n\t\tchar *shardIdString = PQgetvalue(result, rowIndex, 0);\n\t\tuint64 shardId = strtou64(shardIdString, NULL, 10);\n\t\tchar *sizeString = PQgetvalue(result, rowIndex, 1);\n\t\tuint64 totalSize = strtou64(sizeString, NULL, 10);\n\n\t\tShardStatistics *statistics =\n\t\t\thash_search(shardStatistics, &shardId, HASH_ENTER, NULL);\n\t\tstatistics->totalSize = totalSize;\n\n\t\tif (PQgetisnull(result, rowIndex, 2))\n\t\t{\n\t\t\tstatistics->shardLSN = InvalidXLogRecPtr;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar *LSNString = PQgetvalue(result, rowIndex, 2);\n\t\t\tDatum LSNDatum = DirectFunctionCall1(pg_lsn_in, CStringGetDatum(LSNString));\n\t\t\tstatistics->shardLSN = DatumGetLSN(LSNDatum);\n\t\t}\n\t}\n\n\tPQclear(result);\n\n\tbool raiseErrors = true;\n\tClearResults(connection, raiseErrors);\n\n\treturn shardStatistics;\n}\n\n\n/*\n * GetMovedShardIdsByWorker groups the shard ids in the provided steps by\n * worker. It returns a hashmap that contains a set of these shard ids.\n */\nstatic HTAB *\nGetMovedShardIdsByWorker(PlacementUpdateEventProgress *steps, int stepCount,\n\t\t\t\t\t\t bool fromSource)\n{\n\tHASHCTL info = {\n\t\t.keysize = sizeof(WorkerHashKey),\n\t\t.entrysize = sizeof(WorkerShardIds),\n\t\t.hcxt = CurrentMemoryContext\n\t};\n\n\tHTAB *shardsByWorker = hash_create(\"GetRebalanceStepsByWorker\", 32, &info,\n\t\t\t\t\t\t\t\t\t   HASH_ELEM | HASH_CONTEXT | HASH_BLOBS);\n\n\tfor (int stepIndex = 0; stepIndex < stepCount; stepIndex++)\n\t{\n\t\tPlacementUpdateEventProgress *step = &(steps[stepIndex]);\n\n\t\tAddToWorkerShardIdSet(shardsByWorker, step->sourceName, step->sourcePort,\n\t\t\t\t\t\t\t  step->shardId);\n\n\t\tif (pg_atomic_read_u64(&step->progress) == REBALANCE_PROGRESS_WAITING)\n\t\t{\n\t\t\t/*\n\t\t\t * shard move has not started so we don't need target stats for\n\t\t\t * this shard\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tAddToWorkerShardIdSet(shardsByWorker, step->targetName, step->targetPort,\n\t\t\t\t\t\t\t  step->shardId);\n\t}\n\n\treturn shardsByWorker;\n}\n\n\n/*\n * AddToWorkerShardIdSet adds the shard id to the shard id set for the\n * specified worker in the shardsByWorker hashmap.\n */\nstatic void\nAddToWorkerShardIdSet(HTAB *shardsByWorker, char *workerName, int workerPort,\n\t\t\t\t\t  uint64 shardId)\n{\n\tWorkerHashKey workerKey = { 0 };\n\n\tstrlcpy(workerKey.hostname, workerName, MAX_NODE_LENGTH);\n\tworkerKey.port = workerPort;\n\n\tbool isFound = false;\n\tWorkerShardIds *workerShardIds =\n\t\thash_search(shardsByWorker, &workerKey, HASH_ENTER, &isFound);\n\tif (!isFound)\n\t{\n\t\tHASHCTL info = {\n\t\t\t.keysize = sizeof(uint64),\n\t\t\t.entrysize = sizeof(uint64),\n\t\t\t.hcxt = CurrentMemoryContext\n\t\t};\n\n\t\tworkerShardIds->shardIds = hash_create(\n\t\t\t\"WorkerShardIdsSet\", 32, &info,\n\t\t\tHASH_ELEM | HASH_CONTEXT | HASH_BLOBS);\n\t}\n\n\thash_search(workerShardIds->shardIds, &shardId, HASH_ENTER, NULL);\n}\n\n\n/*\n * NonColocatedDistRelationIdList returns a list of distributed table oids, one\n * for each existing colocation group.\n */\nstatic List *\nNonColocatedDistRelationIdList(void)\n{\n\tList *relationIdList = NIL;\n\tList *allCitusTablesList = CitusTableTypeIdList(ANY_CITUS_TABLE_TYPE);\n\tOid tableId = InvalidOid;\n\n\t/* allocate sufficient capacity for O(1) expected look-up time */\n\tint capacity = (int) (list_length(allCitusTablesList) / 0.75) + 1;\n\tint flags = HASH_ELEM | HASH_CONTEXT | HASH_BLOBS;\n\tHASHCTL info = {\n\t\t.keysize = sizeof(Oid),\n\t\t.entrysize = sizeof(Oid),\n\t\t.hcxt = CurrentMemoryContext\n\t};\n\n\tHTAB *alreadySelectedColocationIds = hash_create(\"RebalanceColocationIdSet\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t capacity, &info, flags);\n\tforeach_declared_oid(tableId, allCitusTablesList)\n\t{\n\t\tbool foundInSet = false;\n\t\tCitusTableCacheEntry *citusTableCacheEntry = GetCitusTableCacheEntry(\n\t\t\ttableId);\n\n\t\tif (!IsCitusTableTypeCacheEntry(citusTableCacheEntry, DISTRIBUTED_TABLE))\n\t\t{\n\t\t\t/*\n\t\t\t * We're only interested in distributed tables, should ignore\n\t\t\t * reference tables and citus local tables.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (citusTableCacheEntry->colocationId != INVALID_COLOCATION_ID)\n\t\t{\n\t\t\thash_search(alreadySelectedColocationIds,\n\t\t\t\t\t\t&citusTableCacheEntry->colocationId, HASH_ENTER,\n\t\t\t\t\t\t&foundInSet);\n\t\t\tif (foundInSet)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\trelationIdList = lappend_oid(relationIdList, tableId);\n\t}\n\treturn relationIdList;\n}\n\n\n/*\n * RebalanceTableShards rebalances the shards for the relations inside the\n * relationIdList across the different workers.\n */\nstatic void\nRebalanceTableShards(RebalanceOptions *options, Oid shardReplicationModeOid)\n{\n\tchar transferMode = LookupShardTransferMode(shardReplicationModeOid);\n\n\tif (list_length(options->relationIdList) == 0)\n\t{\n\t\tEnsureReferenceTablesExistOnAllNodesExtended(transferMode);\n\t\treturn;\n\t}\n\n\tchar *operationName = \"rebalance\";\n\tif (options->drainOnly)\n\t{\n\t\toperationName = \"move\";\n\t}\n\n\toptions->operationName = operationName;\n\tErrorOnConcurrentRebalance(options);\n\n\tList *placementUpdateList = GetRebalanceSteps(options);\n\n\tif (transferMode == TRANSFER_MODE_AUTOMATIC)\n\t{\n\t\t/*\n\t\t * If the shard transfer mode is set to auto, we should check beforehand\n\t\t * if we are able to use logical replication to transfer shards or not.\n\t\t * We throw an error if any of the tables do not have a replica identity, which\n\t\t * is required for logical replication to replicate UPDATE and DELETE commands.\n\t\t */\n\t\tPlacementUpdateEvent *placementUpdate = NULL;\n\t\tforeach_declared_ptr(placementUpdate, placementUpdateList)\n\t\t{\n\t\t\tOid relationId = RelationIdForShard(placementUpdate->shardId);\n\t\t\tList *colocatedTableList = ColocatedTableList(relationId);\n\t\t\tVerifyTablesHaveReplicaIdentity(colocatedTableList);\n\t\t}\n\t}\n\n\tEnsureReferenceTablesExistOnAllNodesExtended(transferMode);\n\n\tif (list_length(placementUpdateList) == 0)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * This uses the first relationId from the list, it's only used for display\n\t * purposes so it does not really matter which to show\n\t */\n\tSetupRebalanceMonitor(placementUpdateList, linitial_oid(options->relationIdList),\n\t\t\t\t\t\t  REBALANCE_PROGRESS_WAITING,\n\t\t\t\t\t\t  PLACEMENT_UPDATE_STATUS_NOT_STARTED_YET);\n\tExecutePlacementUpdates(placementUpdateList, shardReplicationModeOid, \"Moving\");\n\tFinalizeCurrentProgressMonitor();\n}\n\n\n/*\n * ErrorOnConcurrentRebalance raises an error with extra information when there is already\n * a rebalance running.\n */\nstatic void\nErrorOnConcurrentRebalance(RebalanceOptions *options)\n{\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, options->relationIdList)\n\t{\n\t\t/* this provides the legacy error when the lock can't be acquired */\n\t\tAcquireRebalanceColocationLock(relationId, options->operationName);\n\t}\n\n\tint64 jobId = 0;\n\tif (HasNonTerminalJobOfType(\"rebalance\", &jobId))\n\t{\n\t\tereport(ERROR, (\n\t\t\t\t\terrmsg(\"A rebalance is already running as job %ld\", jobId),\n\t\t\t\t\terrdetail(\"A rebalance was already scheduled as background job\"),\n\t\t\t\t\terrhint(\"To monitor progress, run: SELECT * FROM \"\n\t\t\t\t\t\t\t\"citus_rebalance_status();\")));\n\t}\n}\n\n\n/*\n * GetColocationId function returns the colocationId of the shard in a PlacementUpdateEvent.\n */\nstatic int64\nGetColocationId(PlacementUpdateEvent *move)\n{\n\tShardInterval *shardInterval = LoadShardInterval(move->shardId);\n\n\tCitusTableCacheEntry *citusTableCacheEntry = GetCitusTableCacheEntry(\n\t\tshardInterval->relationId);\n\n\treturn citusTableCacheEntry->colocationId;\n}\n\n\n/*\n * InitializeShardMoveDependencies function creates the hash maps that we use to track\n * the latest moves so that subsequent moves with the same properties must take a dependency\n * on them. There are two hash maps. One is for tracking the latest move scheduled in a\n * given colocation group and the other one is for tracking source nodes of all moves.\n */\nstatic ShardMoveDependencies\nInitializeShardMoveDependencies(bool ParallelTransferColocatedShards)\n{\n\tShardMoveDependencies shardMoveDependencies;\n\tshardMoveDependencies.colocationDependencies = CreateSimpleHashWithNameAndSize(int64,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ShardMoveDependencyInfo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"colocationDependencyHashMap\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   6);\n\n\tshardMoveDependencies.nodeDependencies = CreateSimpleHashWithNameAndSize(int32,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ShardMoveSourceNodeHashEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"nodeDependencyHashMap\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t 6);\n\tshardMoveDependencies.parallelTransferColocatedShards =\n\t\tParallelTransferColocatedShards;\n\treturn shardMoveDependencies;\n}\n\n\n/*\n * GenerateTaskMoveDependencyList creates and returns a List of taskIds that\n * the move must take a dependency on, given the shard move dependencies as input.\n */\nstatic int64 *\nGenerateTaskMoveDependencyList(PlacementUpdateEvent *move, int64 colocationId,\n\t\t\t\t\t\t\t   int64 *refTablesDepTaskIds, int refTablesDepTaskIdsCount,\n\t\t\t\t\t\t\t   ShardMoveDependencies shardMoveDependencies, int *nDepends)\n{\n\tHTAB *dependsList = CreateSimpleHashSetWithNameAndSize(int64,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"shardMoveDependencyList\", 0);\n\n\tbool found;\n\n\tif (!shardMoveDependencies.parallelTransferColocatedShards)\n\t{\n\t\t/* Check if there exists a move in the same colocation group scheduled earlier. */\n\t\tShardMoveDependencyInfo *shardMoveDependencyInfo = hash_search(\n\t\t\tshardMoveDependencies.colocationDependencies, &colocationId, HASH_ENTER, &\n\t\t\tfound);\n\n\t\tif (found)\n\t\t{\n\t\t\thash_search(dependsList, &shardMoveDependencyInfo->taskId, HASH_ENTER, NULL);\n\t\t}\n\t}\n\n\t/*\n\t * Check if there exists moves scheduled earlier whose source node\n\t * overlaps with the current move's target node.\n\t * The earlier/first move might make space for the later/second move.\n\t * So we could run out of disk space (or at least overload the node)\n\t * if we move the second shard to it before the first one is moved away.\n\t */\n\tShardMoveSourceNodeHashEntry *shardMoveSourceNodeHashEntry = hash_search(\n\t\tshardMoveDependencies.nodeDependencies, &move->targetNode->nodeId, HASH_FIND,\n\t\t&found);\n\n\tif (found)\n\t{\n\t\tint64 *taskId = NULL;\n\t\tforeach_declared_ptr(taskId, shardMoveSourceNodeHashEntry->taskIds)\n\t\t{\n\t\t\thash_search(dependsList, taskId, HASH_ENTER, NULL);\n\t\t}\n\t}\n\n\t*nDepends = hash_get_num_entries(dependsList);\n\tif (*nDepends == 0)\n\t{\n\t\t/*\n\t\t * shard copy can only start after finishing copy of reference table shards\n\t\t * so each shard task will have a dependency on the task that indicates the\n\t\t * copy complete of reference tables\n\t\t */\n\t\twhile (refTablesDepTaskIdsCount > 0)\n\t\t{\n\t\t\tint64 refTableTaskId = *refTablesDepTaskIds;\n\t\t\thash_search(dependsList, &refTableTaskId, HASH_ENTER, NULL);\n\t\t\trefTablesDepTaskIds++;\n\t\t\trefTablesDepTaskIdsCount--;\n\t\t}\n\t}\n\n\t*nDepends = hash_get_num_entries(dependsList);\n\n\tint64 *dependsArray = NULL;\n\n\tif (*nDepends > 0)\n\t{\n\t\tHASH_SEQ_STATUS seq;\n\n\t\tdependsArray = palloc((*nDepends) * sizeof(int64));\n\n\t\thash_seq_init(&seq, dependsList);\n\t\tint i = 0;\n\t\tint64 *dependsTaskId;\n\n\t\twhile ((dependsTaskId = (int64 *) hash_seq_search(&seq)) != NULL)\n\t\t{\n\t\t\tdependsArray[i++] = *dependsTaskId;\n\t\t}\n\t}\n\n\treturn dependsArray;\n}\n\n\n/*\n * UpdateShardMoveDependencies function updates the dependency maps with the latest move's taskId.\n */\nstatic void\nUpdateShardMoveDependencies(PlacementUpdateEvent *move, uint64 colocationId, int64 taskId,\n\t\t\t\t\t\t\tShardMoveDependencies shardMoveDependencies)\n{\n\tif (!shardMoveDependencies.parallelTransferColocatedShards)\n\t{\n\t\tShardMoveDependencyInfo *shardMoveDependencyInfo = hash_search(\n\t\t\tshardMoveDependencies.colocationDependencies, &colocationId,\n\t\t\tHASH_ENTER, NULL);\n\t\tshardMoveDependencyInfo->taskId = taskId;\n\t}\n\n\tbool found;\n\tShardMoveSourceNodeHashEntry *shardMoveSourceNodeHashEntry = hash_search(\n\t\tshardMoveDependencies.nodeDependencies, &move->sourceNode->nodeId, HASH_ENTER,\n\t\t&found);\n\n\tif (!found)\n\t{\n\t\tshardMoveSourceNodeHashEntry->taskIds = NIL;\n\t}\n\n\tint64 *newTaskId = palloc0(sizeof(int64));\n\t*newTaskId = taskId;\n\tshardMoveSourceNodeHashEntry->taskIds = lappend(\n\t\tshardMoveSourceNodeHashEntry->taskIds, newTaskId);\n}\n\n\n/*\n * RebalanceTableShardsBackground rebalances the shards for the relations\n * inside the relationIdList across the different workers. It does so using our\n * background job+task infrastructure.\n */\nstatic int64\nRebalanceTableShardsBackground(RebalanceOptions *options, Oid shardReplicationModeOid,\n\t\t\t\t\t\t\t   bool ParallelTransferReferenceTables,\n\t\t\t\t\t\t\t   bool ParallelTransferColocatedShards)\n{\n\tif (list_length(options->relationIdList) == 0)\n\t{\n\t\tereport(NOTICE, (errmsg(\"No tables to rebalance\")));\n\t\treturn 0;\n\t}\n\n\tchar *operationName = \"rebalance\";\n\tif (options->drainOnly)\n\t{\n\t\toperationName = \"move\";\n\t}\n\n\toptions->operationName = operationName;\n\tErrorOnConcurrentRebalance(options);\n\n\tconst char shardTransferMode = LookupShardTransferMode(shardReplicationModeOid);\n\tList *colocatedTableList = NIL;\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, options->relationIdList)\n\t{\n\t\tcolocatedTableList = list_concat(colocatedTableList,\n\t\t\t\t\t\t\t\t\t\t ColocatedTableList(relationId));\n\t}\n\tOid colocatedTableId = InvalidOid;\n\tforeach_declared_oid(colocatedTableId, colocatedTableList)\n\t{\n\t\tEnsureTableOwner(colocatedTableId);\n\t}\n\n\tList *placementUpdateList = GetRebalanceSteps(options);\n\n\tif (list_length(placementUpdateList) == 0)\n\t{\n\t\tereport(NOTICE, (errmsg(\"No moves available for rebalancing\")));\n\t\treturn 0;\n\t}\n\n\tif (shardTransferMode == TRANSFER_MODE_AUTOMATIC)\n\t{\n\t\t/*\n\t\t * If the shard transfer mode is set to auto, we should check beforehand\n\t\t * if we are able to use logical replication to transfer shards or not.\n\t\t * We throw an error if any of the tables do not have a replica identity, which\n\t\t * is required for logical replication to replicate UPDATE and DELETE commands.\n\t\t */\n\t\tPlacementUpdateEvent *placementUpdate = NULL;\n\t\tforeach_declared_ptr(placementUpdate, placementUpdateList)\n\t\t{\n\t\t\trelationId = RelationIdForShard(placementUpdate->shardId);\n\t\t\tList *colocatedTables = ColocatedTableList(relationId);\n\t\t\tVerifyTablesHaveReplicaIdentity(colocatedTables);\n\t\t}\n\t}\n\n\tDropOrphanedResourcesInSeparateTransaction();\n\n\t/* find the name of the shard transfer mode to interpolate in the scheduled command */\n\tDatum shardTranferModeLabelDatum =\n\t\tDirectFunctionCall1(enum_out, shardReplicationModeOid);\n\tchar *shardTranferModeLabel = DatumGetCString(shardTranferModeLabelDatum);\n\n\t/* schedule planned moves */\n\tint64 jobId = CreateBackgroundJob(\"rebalance\", \"Rebalance all colocation groups\");\n\n\t/* buffer used to construct the sql command for the tasks */\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\n\tList *referenceTableIdList = NIL;\n\tint64 *refTablesDepTaskIds = NULL;\n\tint refTablesDepTaskIdsCount = 0;\n\n\tif (HasNodesWithMissingReferenceTables(&referenceTableIdList))\n\t{\n\t\tif (shardTransferMode == TRANSFER_MODE_AUTOMATIC)\n\t\t{\n\t\t\tVerifyTablesHaveReplicaIdentity(referenceTableIdList);\n\t\t}\n\n\t\t/*\n\t\t * Reference tables need to be copied to (newly-added) nodes, this needs to be the\n\t\t * first task before we can move any other table.\n\t\t */\n\t\tif (ParallelTransferReferenceTables)\n\t\t{\n\t\t\trefTablesDepTaskIds =\n\t\t\t\tScheduleTasksToParallelCopyReferenceTablesOnAllMissingNodes(\n\t\t\t\t\tjobId, shardTransferMode, &refTablesDepTaskIdsCount);\n\t\t\tereport(DEBUG2,\n\t\t\t\t\t(errmsg(\"%d dependent copy reference table tasks for job %ld\",\n\t\t\t\t\t\t\trefTablesDepTaskIdsCount, jobId),\n\t\t\t\t\t errdetail(\"Rebalance scheduled as background job\"),\n\t\t\t\t\t errhint(\"To monitor progress, run: SELECT * FROM \"\n\t\t\t\t\t\t\t \"citus_rebalance_status();\")));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* Move all reference tables as single task. Classical way */\n\t\t\tappendStringInfo(&buf,\n\t\t\t\t\t\t\t \"SELECT pg_catalog.replicate_reference_tables(%s)\",\n\t\t\t\t\t\t\t quote_literal_cstr(shardTranferModeLabel));\n\n\t\t\tint32 nodesInvolved[] = { 0 };\n\n\t\t\t/* replicate_reference_tables permissions require superuser */\n\t\t\tOid superUserId = CitusExtensionOwner();\n\t\t\tBackgroundTask *task = ScheduleBackgroundTask(jobId, superUserId, buf.data, 0,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL, 0, nodesInvolved);\n\t\t\trefTablesDepTaskIds = palloc0(sizeof(int64));\n\t\t\trefTablesDepTaskIds[0] = task->taskid;\n\t\t\trefTablesDepTaskIdsCount = 1;\n\t\t}\n\t}\n\n\tPlacementUpdateEvent *move = NULL;\n\n\tShardMoveDependencies shardMoveDependencies =\n\t\tInitializeShardMoveDependencies(ParallelTransferColocatedShards);\n\n\tforeach_declared_ptr(move, placementUpdateList)\n\t{\n\t\tresetStringInfo(&buf);\n\n\t\tappendStringInfo(&buf,\n\t\t\t\t\t\t \"SELECT pg_catalog.citus_move_shard_placement(%ld,%u,%u,%s)\",\n\t\t\t\t\t\t move->shardId,\n\t\t\t\t\t\t move->sourceNode->nodeId,\n\t\t\t\t\t\t move->targetNode->nodeId,\n\t\t\t\t\t\t quote_literal_cstr(shardTranferModeLabel));\n\n\t\tint64 colocationId = GetColocationId(move);\n\n\t\tint nDepends = 0;\n\n\t\tint64 *dependsArray = GenerateTaskMoveDependencyList(move, colocationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t refTablesDepTaskIds,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t refTablesDepTaskIdsCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardMoveDependencies,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t &nDepends);\n\n\t\tint32 nodesInvolved[2] = { 0 };\n\t\tnodesInvolved[0] = move->sourceNode->nodeId;\n\t\tnodesInvolved[1] = move->targetNode->nodeId;\n\n\t\tBackgroundTask *task = ScheduleBackgroundTask(jobId, GetUserId(), buf.data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  nDepends,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  dependsArray, 2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  nodesInvolved);\n\n\t\tUpdateShardMoveDependencies(move, colocationId, task->taskid,\n\t\t\t\t\t\t\t\t\tshardMoveDependencies);\n\t}\n\n\tereport(NOTICE,\n\t\t\t(errmsg(\"Scheduled %d moves as job %ld\",\n\t\t\t\t\tlist_length(placementUpdateList), jobId),\n\t\t\t errdetail(\"Rebalance scheduled as background job\"),\n\t\t\t errhint(\"To monitor progress, run: SELECT * FROM \"\n\t\t\t\t\t \"citus_rebalance_status();\")));\n\n\treturn jobId;\n}\n\n\n/*\n * UpdateShardPlacement copies or moves a shard placement by calling\n * the corresponding functions in Citus in a subtransaction.\n */\nstatic void\nUpdateShardPlacement(PlacementUpdateEvent *placementUpdateEvent,\n\t\t\t\t\t List *responsiveNodeList, Oid shardReplicationModeOid)\n{\n\tPlacementUpdateType updateType = placementUpdateEvent->updateType;\n\tuint64 shardId = placementUpdateEvent->shardId;\n\tWorkerNode *sourceNode = placementUpdateEvent->sourceNode;\n\tWorkerNode *targetNode = placementUpdateEvent->targetNode;\n\n\tDatum shardTranferModeLabelDatum =\n\t\tDirectFunctionCall1(enum_out, shardReplicationModeOid);\n\tchar *shardTranferModeLabel = DatumGetCString(shardTranferModeLabelDatum);\n\n\tStringInfo placementUpdateCommand = makeStringInfo();\n\n\t/* if target node is not responsive, don't continue */\n\tbool targetResponsive = WorkerNodeListContains(responsiveNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t   targetNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t   targetNode->workerPort);\n\tif (!targetResponsive)\n\t{\n\t\tereport(ERROR, (errmsg(\"target node %s:%d is not responsive\",\n\t\t\t\t\t\t\t   targetNode->workerName,\n\t\t\t\t\t\t\t   targetNode->workerPort)));\n\t}\n\n\t/* if source node is not responsive, don't continue */\n\tbool sourceResponsive = WorkerNodeListContains(responsiveNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t   sourceNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t   sourceNode->workerPort);\n\tif (!sourceResponsive)\n\t{\n\t\tereport(ERROR, (errmsg(\"source node %s:%d is not responsive\",\n\t\t\t\t\t\t\t   sourceNode->workerName,\n\t\t\t\t\t\t\t   sourceNode->workerPort)));\n\t}\n\n\tif (updateType == PLACEMENT_UPDATE_MOVE)\n\t{\n\t\tappendStringInfo(placementUpdateCommand,\n\t\t\t\t\t\t \"SELECT pg_catalog.citus_move_shard_placement(%ld,%u,%u,%s)\",\n\t\t\t\t\t\t shardId,\n\t\t\t\t\t\t sourceNode->nodeId,\n\t\t\t\t\t\t targetNode->nodeId,\n\t\t\t\t\t\t quote_literal_cstr(shardTranferModeLabel));\n\t}\n\telse if (updateType == PLACEMENT_UPDATE_COPY)\n\t{\n\t\tappendStringInfo(placementUpdateCommand,\n\t\t\t\t\t\t \"SELECT pg_catalog.citus_copy_shard_placement(%ld,%u,%u,%s)\",\n\t\t\t\t\t\t shardId,\n\t\t\t\t\t\t sourceNode->nodeId,\n\t\t\t\t\t\t targetNode->nodeId,\n\t\t\t\t\t\t quote_literal_cstr(shardTranferModeLabel));\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"only moving or copying shards is supported\")));\n\t}\n\n\tUpdateColocatedShardPlacementProgress(shardId,\n\t\t\t\t\t\t\t\t\t\t  sourceNode->workerName,\n\t\t\t\t\t\t\t\t\t\t  sourceNode->workerPort,\n\t\t\t\t\t\t\t\t\t\t  REBALANCE_PROGRESS_MOVING);\n\n\t/*\n\t * In case of failure, we throw an error such that rebalance_table_shards\n\t * fails early.\n\t */\n\tExecuteRebalancerCommandInSeparateTransaction(placementUpdateCommand->data);\n\n\tUpdateColocatedShardPlacementProgress(shardId,\n\t\t\t\t\t\t\t\t\t\t  sourceNode->workerName,\n\t\t\t\t\t\t\t\t\t\t  sourceNode->workerPort,\n\t\t\t\t\t\t\t\t\t\t  REBALANCE_PROGRESS_MOVED);\n}\n\n\n/*\n * ExecuteRebalancerCommandInSeparateTransaction runs a command in a separate\n * transaction that is commited right away. This is useful for things that you\n * don't want to rollback when the current transaction is rolled back.\n * Set true to 'useExclusiveTransactionBlock' to initiate a BEGIN and COMMIT statements.\n */\nvoid\nExecuteRebalancerCommandInSeparateTransaction(char *command)\n{\n\tint connectionFlag = FORCE_NEW_CONNECTION;\n\tMultiConnection *connection = GetNodeConnection(connectionFlag, LocalHostName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tPostPortNumber);\n\tList *commandList = NIL;\n\n\tcommandList = lappend(commandList, psprintf(\n\t\t\t\t\t\t\t  \"SET LOCAL application_name TO '%s%ld'\",\n\t\t\t\t\t\t\t  CITUS_REBALANCER_APPLICATION_NAME_PREFIX,\n\t\t\t\t\t\t\t  GetGlobalPID()));\n\n\tif (PropagateSessionSettingsForLoopbackConnection)\n\t{\n\t\tList *setCommands = GetSetCommandListForNewConnections();\n\t\tchar *setCommand = NULL;\n\n\t\tforeach_declared_ptr(setCommand, setCommands)\n\t\t{\n\t\t\tcommandList = lappend(commandList, setCommand);\n\t\t}\n\t}\n\n\tcommandList = lappend(commandList, command);\n\n\tSendCommandListToWorkerOutsideTransactionWithConnection(connection, commandList);\n\tCloseConnection(connection);\n}\n\n\n/*\n * GetSetCommandListForNewConnections returns a list of SET statements to\n * be executed in new connections to worker nodes.\n */\nstatic List *\nGetSetCommandListForNewConnections(void)\n{\n\tList *commandList = NIL;\n\n\tint gucCount = 0;\n\tstruct config_generic **guc_vars = get_guc_variables(&gucCount);\n\n\tfor (int gucIndex = 0; gucIndex < gucCount; gucIndex++)\n\t{\n\t\tstruct config_generic *var = (struct config_generic *) guc_vars[gucIndex];\n\t\tif (var->source == PGC_S_SESSION && IsSettingSafeToPropagate(var->name))\n\t\t{\n\t\t\tconst char *variableValue = GetConfigOption(var->name, true, true);\n\t\t\tcommandList = lappend(commandList, psprintf(\"SET LOCAL %s TO '%s';\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvar->name, variableValue));\n\t\t}\n\t}\n\n\treturn commandList;\n}\n\n\n/*\n * RebalancePlacementUpdates returns a list of placement updates which makes the\n * cluster balanced. We move shards to these nodes until all nodes become utilized.\n * We consider a node under-utilized if it has less than floor((1.0 - threshold) *\n * placementCountAverage) shard placements. In each iteration we choose the node\n * with maximum number of shard placements as the source, and we choose the node\n * with minimum number of shard placements as the target. Then we choose a shard\n * which is placed in the source node but not in the target node as the shard to\n * move.\n *\n * The activeShardPlacementListList argument contains a list of lists of active shard\n * placements. Each of these lists are balanced independently. This is used to\n * make sure different colocation groups are balanced separately, so each list\n * contains the placements of a colocation group.\n */\nList *\nRebalancePlacementUpdates(List *workerNodeList, List *activeShardPlacementListList,\n\t\t\t\t\t\t  double threshold,\n\t\t\t\t\t\t  int32 maxShardMoves,\n\t\t\t\t\t\t  bool drainOnly,\n\t\t\t\t\t\t  float4 improvementThreshold,\n\t\t\t\t\t\t  RebalancePlanFunctions *functions)\n{\n\tList *rebalanceStates = NIL;\n\tRebalanceState *state = NULL;\n\tList *shardPlacementList = NIL;\n\tList *placementUpdateList = NIL;\n\n\tforeach_declared_ptr(shardPlacementList, activeShardPlacementListList)\n\t{\n\t\tstate = InitRebalanceState(workerNodeList, shardPlacementList,\n\t\t\t\t\t\t\t\t   functions);\n\t\trebalanceStates = lappend(rebalanceStates, state);\n\t}\n\n\tforeach_declared_ptr(state, rebalanceStates)\n\t{\n\t\tstate->placementUpdateList = placementUpdateList;\n\t\tMoveShardsAwayFromDisallowedNodes(state);\n\t\tplacementUpdateList = state->placementUpdateList;\n\t}\n\n\tif (!drainOnly)\n\t{\n\t\tforeach_declared_ptr(state, rebalanceStates)\n\t\t{\n\t\t\tstate->placementUpdateList = placementUpdateList;\n\n\t\t\t/* calculate lower bound for placement count */\n\t\t\tfloat4 averageUtilization = (state->totalCost / state->totalCapacity);\n\t\t\tfloat4 utilizationLowerBound = ((1.0 - threshold) * averageUtilization);\n\t\t\tfloat4 utilizationUpperBound = ((1.0 + threshold) * averageUtilization);\n\n\t\t\tbool moreMovesAvailable = true;\n\t\t\twhile (list_length(state->placementUpdateList) < maxShardMoves &&\n\t\t\t\t   moreMovesAvailable)\n\t\t\t{\n\t\t\t\tmoreMovesAvailable = FindAndMoveShardCost(\n\t\t\t\t\tutilizationLowerBound,\n\t\t\t\t\tutilizationUpperBound,\n\t\t\t\t\timprovementThreshold,\n\t\t\t\t\tstate);\n\t\t\t}\n\t\t\tplacementUpdateList = state->placementUpdateList;\n\n\t\t\tif (moreMovesAvailable)\n\t\t\t{\n\t\t\t\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t\t\t\t \"Stopped searching before we were out of moves. \"\n\t\t\t\t\t\t\t\t\t \"Please rerun the rebalancer after it's finished \"\n\t\t\t\t\t\t\t\t\t \"for a more optimal placement.\")));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tforeach_declared_ptr(state, rebalanceStates)\n\t{\n\t\thash_destroy(state->placementsHash);\n\t}\n\n\tint64 ignoredMoves = 0;\n\tforeach_declared_ptr(state, rebalanceStates)\n\t{\n\t\tignoredMoves += state->ignoredMoves;\n\t}\n\n\tif (ignoredMoves > 0)\n\t{\n\t\tif (MaxRebalancerLoggedIgnoredMoves == -1 ||\n\t\t\tignoredMoves <= MaxRebalancerLoggedIgnoredMoves)\n\t\t{\n\t\t\tereport(NOTICE, (\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Ignored %ld moves, all of which are shown in notices above\",\n\t\t\t\t\t\t\tignoredMoves\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\"If you do want these moves to happen, try changing improvement_threshold to a lower value than what it is now (%g).\",\n\t\t\t\t\t\t\timprovementThreshold)\n\t\t\t\t\t\t));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(NOTICE, (\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Ignored %ld moves, %d of which are shown in notices above\",\n\t\t\t\t\t\t\tignoredMoves,\n\t\t\t\t\t\t\tMaxRebalancerLoggedIgnoredMoves\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\"If you do want these moves to happen, try changing improvement_threshold to a lower value than what it is now (%g).\",\n\t\t\t\t\t\t\timprovementThreshold)\n\t\t\t\t\t\t));\n\t\t}\n\t}\n\treturn placementUpdateList;\n}\n\n\n/*\n * InitRebalanceState sets up a RebalanceState for it's arguments. The\n * RebalanceState contains the information needed to calculate shard moves.\n */\nstatic RebalanceState *\nInitRebalanceState(List *workerNodeList, List *shardPlacementList,\n\t\t\t\t   RebalancePlanFunctions *functions)\n{\n\tShardPlacement *placement = NULL;\n\tHASH_SEQ_STATUS status;\n\tWorkerNode *workerNode = NULL;\n\n\tRebalanceState *state = palloc0(sizeof(RebalanceState));\n\tstate->functions = functions;\n\tstate->placementsHash = ShardPlacementsListToHash(shardPlacementList);\n\n\t/* create empty fill state for all of the worker nodes */\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tNodeFillState *fillState = palloc0(sizeof(NodeFillState));\n\t\tfillState->node = workerNode;\n\t\tfillState->capacity = functions->nodeCapacity(workerNode, functions->context);\n\n\t\t/*\n\t\t * Set the utilization here although the totalCost is not set yet. This\n\t\t * is needed to set the utilization to INFINITY when the capacity is 0.\n\t\t */\n\t\tfillState->utilization = CalculateUtilization(fillState->totalCost,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  fillState->capacity);\n\t\tstate->fillStateListAsc = lappend(state->fillStateListAsc, fillState);\n\t\tstate->fillStateListDesc = lappend(state->fillStateListDesc, fillState);\n\t\tstate->totalCapacity += fillState->capacity;\n\t}\n\n\t/* Fill the fill states for all of the worker nodes based on the placements */\n\tforeach_htab(placement, &status, state->placementsHash)\n\t{\n\t\tShardCost *shardCost = palloc0(sizeof(ShardCost));\n\t\tNodeFillState *fillState = FindFillStateForPlacement(state, placement);\n\n\t\tAssert(fillState != NULL);\n\n\t\t*shardCost = functions->shardCost(placement->shardId, functions->context);\n\n\t\tfillState->totalCost += shardCost->cost;\n\t\tfillState->utilization = CalculateUtilization(fillState->totalCost,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  fillState->capacity);\n\t\tfillState->shardCostListDesc = lappend(fillState->shardCostListDesc,\n\t\t\t\t\t\t\t\t\t\t\t   shardCost);\n\t\tfillState->shardCostListDesc = SortList(fillState->shardCostListDesc,\n\t\t\t\t\t\t\t\t\t\t\t\tCompareShardCostDesc);\n\n\t\tstate->totalCost += shardCost->cost;\n\n\t\tif (!functions->shardAllowedOnNode(placement->shardId, fillState->node,\n\t\t\t\t\t\t\t\t\t\t   functions->context))\n\t\t{\n\t\t\tDisallowedPlacement *disallowed = palloc0(sizeof(DisallowedPlacement));\n\t\t\tdisallowed->shardCost = shardCost;\n\t\t\tdisallowed->fillState = fillState;\n\t\t\tstate->disallowedPlacementList = lappend(state->disallowedPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t disallowed);\n\t\t}\n\t}\n\tforeach_htab_cleanup(placement, &status);\n\n\tstate->fillStateListAsc = SortList(state->fillStateListAsc, CompareNodeFillStateAsc);\n\tstate->fillStateListDesc = SortList(state->fillStateListDesc,\n\t\t\t\t\t\t\t\t\t\tCompareNodeFillStateDesc);\n\tCheckRebalanceStateInvariants(state);\n\n\treturn state;\n}\n\n\n/*\n * CalculateUtilization returns INFINITY when capacity is 0 and\n * totalCost/capacity otherwise.\n */\nstatic float4\nCalculateUtilization(float4 totalCost, float4 capacity)\n{\n\tif (capacity <= 0)\n\t{\n\t\treturn INFINITY;\n\t}\n\treturn totalCost / capacity;\n}\n\n\n/*\n * FindFillStateForPlacement finds the fillState for the workernode that\n * matches the placement.\n */\nstatic NodeFillState *\nFindFillStateForPlacement(RebalanceState *state, ShardPlacement *placement)\n{\n\tNodeFillState *fillState = NULL;\n\n\t/* Find the correct fill state to add the placement to and do that */\n\tforeach_declared_ptr(fillState, state->fillStateListAsc)\n\t{\n\t\tif (IsPlacementOnWorkerNode(placement, fillState->node))\n\t\t{\n\t\t\treturn fillState;\n\t\t}\n\t}\n\treturn NULL;\n}\n\n\n/*\n * CompareNodeFillStateAsc can be used to sort fill states from empty to full.\n */\nstatic int\nCompareNodeFillStateAsc(const void *void1, const void *void2)\n{\n\tconst NodeFillState *a = *((const NodeFillState **) void1);\n\tconst NodeFillState *b = *((const NodeFillState **) void2);\n\tif (a->utilization < b->utilization)\n\t{\n\t\treturn -1;\n\t}\n\tif (a->utilization > b->utilization)\n\t{\n\t\treturn 1;\n\t}\n\n\t/*\n\t * If utilization prefer nodes with more capacity, since utilization will\n\t * grow slower on those\n\t */\n\tif (a->capacity > b->capacity)\n\t{\n\t\treturn -1;\n\t}\n\tif (a->capacity < b->capacity)\n\t{\n\t\treturn 1;\n\t}\n\n\t/* Finally differentiate by node id */\n\tif (a->node->nodeId < b->node->nodeId)\n\t{\n\t\treturn -1;\n\t}\n\treturn a->node->nodeId > b->node->nodeId;\n}\n\n\n/*\n * CompareNodeFillStateDesc can be used to sort fill states from full to empty.\n */\nstatic int\nCompareNodeFillStateDesc(const void *a, const void *b)\n{\n\treturn -CompareNodeFillStateAsc(a, b);\n}\n\n\n/*\n * CompareShardCostAsc can be used to sort shard costs from low cost to high\n * cost.\n */\nstatic int\nCompareShardCostAsc(const void *void1, const void *void2)\n{\n\tconst ShardCost *a = *((const ShardCost **) void1);\n\tconst ShardCost *b = *((const ShardCost **) void2);\n\tif (a->cost < b->cost)\n\t{\n\t\treturn -1;\n\t}\n\tif (a->cost > b->cost)\n\t{\n\t\treturn 1;\n\t}\n\n\t/* make compare function (more) stable for tests */\n\tif (a->shardId > b->shardId)\n\t{\n\t\treturn -1;\n\t}\n\treturn a->shardId < b->shardId;\n}\n\n\n/*\n * CompareShardCostDesc can be used to sort shard costs from high cost to low\n * cost.\n */\nstatic int\nCompareShardCostDesc(const void *a, const void *b)\n{\n\treturn -CompareShardCostAsc(a, b);\n}\n\n\n/*\n * MoveShardsAwayFromDisallowedNodes returns a list of placement updates that\n * move any shards that are not allowed on their current node to a node that\n * they are allowed on.\n */\nstatic void\nMoveShardsAwayFromDisallowedNodes(RebalanceState *state)\n{\n\tDisallowedPlacement *disallowedPlacement = NULL;\n\n\tstate->disallowedPlacementList = SortList(state->disallowedPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t  CompareDisallowedPlacementDesc);\n\n\t/* Move shards off of nodes they are not allowed on */\n\tforeach_declared_ptr(disallowedPlacement, state->disallowedPlacementList)\n\t{\n\t\tNodeFillState *targetFillState = FindAllowedTargetFillState(\n\t\t\tstate, disallowedPlacement->shardCost->shardId);\n\t\tif (targetFillState == NULL)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\n\t\t\t\t\t\t\t\t  \"Not allowed to move shard \" UINT64_FORMAT\n\t\t\t\t\t\t\t\t  \" anywhere from %s:%d\",\n\t\t\t\t\t\t\t\t  disallowedPlacement->shardCost->shardId,\n\t\t\t\t\t\t\t\t  disallowedPlacement->fillState->node->workerName,\n\t\t\t\t\t\t\t\t  disallowedPlacement->fillState->node->workerPort\n\t\t\t\t\t\t\t\t  )));\n\t\t\tcontinue;\n\t\t}\n\t\tMoveShardCost(disallowedPlacement->fillState,\n\t\t\t\t\t  targetFillState,\n\t\t\t\t\t  disallowedPlacement->shardCost,\n\t\t\t\t\t  state);\n\t}\n}\n\n\n/*\n * CompareDisallowedPlacementAsc can be used to sort disallowed placements from\n * low cost to high cost.\n */\nstatic int\nCompareDisallowedPlacementAsc(const void *void1, const void *void2)\n{\n\tconst DisallowedPlacement *a = *((const DisallowedPlacement **) void1);\n\tconst DisallowedPlacement *b = *((const DisallowedPlacement **) void2);\n\treturn CompareShardCostAsc(&(a->shardCost), &(b->shardCost));\n}\n\n\n/*\n * CompareDisallowedPlacementDesc can be used to sort disallowed placements from\n * high cost to low cost.\n */\nstatic int\nCompareDisallowedPlacementDesc(const void *a, const void *b)\n{\n\treturn -CompareDisallowedPlacementAsc(a, b);\n}\n\n\n/*\n * FindAllowedTargetFillState finds the first fill state in fillStateListAsc\n * where the shard can be moved to.\n */\nstatic NodeFillState *\nFindAllowedTargetFillState(RebalanceState *state, uint64 shardId)\n{\n\tNodeFillState *targetFillState = NULL;\n\tforeach_declared_ptr(targetFillState, state->fillStateListAsc)\n\t{\n\t\tbool hasShard = PlacementsHashFind(\n\t\t\tstate->placementsHash,\n\t\t\tshardId,\n\t\t\ttargetFillState->node);\n\t\tif (!hasShard && state->functions->shardAllowedOnNode(\n\t\t\t\tshardId,\n\t\t\t\ttargetFillState->node,\n\t\t\t\tstate->functions->context))\n\t\t{\n\t\t\tbool targetHasShard = PlacementsHashFind(state->placementsHash,\n\t\t\t\t\t\t\t\t\t\t\t\t\t shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t targetFillState->node);\n\n\t\t\t/* skip if the shard is already placed on the target node */\n\t\t\tif (!targetHasShard)\n\t\t\t{\n\t\t\t\treturn targetFillState;\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\n\n\n/*\n * MoveShardCost moves a shardcost from the source to the target fill states\n * and updates the RebalanceState accordingly. What it does in detail is:\n * 1. add a placement update to state->placementUpdateList\n * 2. update state->placementsHash\n * 3. update totalcost, utilization and shardCostListDesc in source and target\n * 4. resort state->fillStateListAsc/Desc\n */\nstatic void\nMoveShardCost(NodeFillState *sourceFillState,\n\t\t\t  NodeFillState *targetFillState,\n\t\t\t  ShardCost *shardCost,\n\t\t\t  RebalanceState *state)\n{\n\tuint64 shardIdToMove = shardCost->shardId;\n\n\t/* construct the placement update */\n\tPlacementUpdateEvent *placementUpdateEvent = palloc0(sizeof(PlacementUpdateEvent));\n\tplacementUpdateEvent->updateType = PLACEMENT_UPDATE_MOVE;\n\tplacementUpdateEvent->shardId = shardIdToMove;\n\tplacementUpdateEvent->sourceNode = sourceFillState->node;\n\tplacementUpdateEvent->targetNode = targetFillState->node;\n\n\t/* record the placement update */\n\tstate->placementUpdateList = lappend(state->placementUpdateList,\n\t\t\t\t\t\t\t\t\t\t placementUpdateEvent);\n\n\t/* update the placements hash and the node shard lists */\n\tPlacementsHashRemove(state->placementsHash, shardIdToMove, sourceFillState->node);\n\tPlacementsHashEnter(state->placementsHash, shardIdToMove, targetFillState->node);\n\n\tsourceFillState->totalCost -= shardCost->cost;\n\tsourceFillState->utilization = CalculateUtilization(sourceFillState->totalCost,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsourceFillState->capacity);\n\tsourceFillState->shardCostListDesc = list_delete_ptr(\n\t\tsourceFillState->shardCostListDesc,\n\t\tshardCost);\n\n\ttargetFillState->totalCost += shardCost->cost;\n\ttargetFillState->utilization = CalculateUtilization(targetFillState->totalCost,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetFillState->capacity);\n\ttargetFillState->shardCostListDesc = lappend(targetFillState->shardCostListDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t shardCost);\n\ttargetFillState->shardCostListDesc = SortList(targetFillState->shardCostListDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t  CompareShardCostDesc);\n\n\tstate->fillStateListAsc = SortList(state->fillStateListAsc, CompareNodeFillStateAsc);\n\tstate->fillStateListDesc = SortList(state->fillStateListDesc,\n\t\t\t\t\t\t\t\t\t\tCompareNodeFillStateDesc);\n\tCheckRebalanceStateInvariants(state);\n}\n\n\n/*\n * FindAndMoveShardCost is the main rebalancing algorithm. This takes the\n * current state and returns a list with a new move appended that improves the\n * balance of shards. The algorithm is greedy and will use the first new move\n * that improves the balance. It finds nodes by trying to move a shard from the\n * most utilized node (highest utilization) to the emptiest node (lowest\n * utilization). If no moves are possible it will try the second emptiest node\n * until it tried all of them. Then it wil try the second fullest node. If it\n * was able to find a move it will return true and false if it couldn't.\n *\n * This algorithm won't necessarily result in the best possible balance. Getting\n * the best balance is an NP problem, so it's not feasible to go for the best\n * balance. This algorithm was chosen because of the following reasons:\n * 1. Literature research showed that similar problems would get within 2X of\n *    the optimal balance with a greedy algoritm.\n * 2. Every move will always improve the balance. So if the user stops a\n *    rebalance midway through, they will never be in a worse situation than\n *    before.\n * 3. It's pretty easy to reason about.\n * 4. It's simple to implement.\n *\n * utilizationLowerBound and utilizationUpperBound are used to indicate what\n * the target utilization range of all nodes is. If they are within this range,\n * then balance is good enough. If all nodes are in this range then the cluster\n * is considered balanced and no more moves are done. This is mostly useful for\n * the by_disk_size rebalance strategy. If we wouldn't have this then the\n * rebalancer could become flappy in certain cases.\n *\n * improvementThreshold is a threshold that can be used to ignore moves when\n * they only improve the balance a little relative to the cost of the shard.\n * Again this is mostly useful for the by_disk_size rebalance strategy.\n * Without this threshold the rebalancer would move a shard of 1TB when this\n * move only improves the cluster by 10GB.\n */\nstatic bool\nFindAndMoveShardCost(float4 utilizationLowerBound,\n\t\t\t\t\t float4 utilizationUpperBound,\n\t\t\t\t\t float4 improvementThreshold,\n\t\t\t\t\t RebalanceState *state)\n{\n\tNodeFillState *sourceFillState = NULL;\n\tNodeFillState *targetFillState = NULL;\n\n\t/*\n\t * find a source node for the move, starting at the node with the highest\n\t * utilization\n\t */\n\tforeach_declared_ptr(sourceFillState, state->fillStateListDesc)\n\t{\n\t\t/* Don't move shards away from nodes that are already too empty, we're\n\t\t * done searching */\n\t\tif (sourceFillState->utilization <= utilizationLowerBound)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t/* find a target node for the move, starting at the node with the\n\t\t * lowest utilization */\n\t\tforeach_declared_ptr(targetFillState, state->fillStateListAsc)\n\t\t{\n\t\t\tShardCost *shardCost = NULL;\n\n\t\t\t/* Don't add more shards to nodes that are already at the upper\n\t\t\t * bound. We should try the next source node now because further\n\t\t\t * target nodes will also be above the upper bound */\n\t\t\tif (targetFillState->utilization >= utilizationUpperBound)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* Don't move a shard between nodes that both have decent\n\t\t\t * utilization. We should try the next source node now because\n\t\t\t * further target nodes will also have have decent utilization */\n\t\t\tif (targetFillState->utilization >= utilizationLowerBound &&\n\t\t\t\tsourceFillState->utilization <= utilizationUpperBound)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* find a shardcost that can be moved between between nodes that\n\t\t\t * makes the cost distribution more equal */\n\t\t\tforeach_declared_ptr(shardCost, sourceFillState->shardCostListDesc)\n\t\t\t{\n\t\t\t\tbool targetHasShard = PlacementsHashFind(state->placementsHash,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardCost->shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t targetFillState->node);\n\t\t\t\tfloat4 newTargetTotalCost = targetFillState->totalCost + shardCost->cost;\n\t\t\t\tfloat4 newTargetUtilization = CalculateUtilization(\n\t\t\t\t\tnewTargetTotalCost,\n\t\t\t\t\ttargetFillState->capacity);\n\t\t\t\tfloat4 newSourceTotalCost = sourceFillState->totalCost - shardCost->cost;\n\t\t\t\tfloat4 newSourceUtilization = CalculateUtilization(\n\t\t\t\t\tnewSourceTotalCost,\n\t\t\t\t\tsourceFillState->capacity);\n\n\t\t\t\t/* Skip shards that already are on the node */\n\t\t\t\tif (targetHasShard)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t/* Skip shards that already are not allowed on the node */\n\t\t\t\tif (!state->functions->shardAllowedOnNode(shardCost->shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  targetFillState->node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  state->functions->context))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * If the target is still less utilized than the source, then\n\t\t\t\t * this is clearly a good move. And if they are equally\n\t\t\t\t * utilized too.\n\t\t\t\t */\n\t\t\t\tif (newTargetUtilization <= newSourceUtilization)\n\t\t\t\t{\n\t\t\t\t\tMoveShardCost(sourceFillState, targetFillState,\n\t\t\t\t\t\t\t\t  shardCost, state);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * The target is now more utilized than the source. So we need\n\t\t\t\t * to determine if the move is a net positive for the overall\n\t\t\t\t * cost distribution. This means that the new highest\n\t\t\t\t * utilization of source and target is lower than the previous\n\t\t\t\t * highest, or the highest utilization is the same, but the\n\t\t\t\t * lowest increased.\n\t\t\t\t */\n\t\t\t\tif (newTargetUtilization > sourceFillState->utilization)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (newTargetUtilization == sourceFillState->utilization &&\n\t\t\t\t\tnewSourceUtilization <= targetFillState->utilization\n\t\t\t\t\t) /* lgtm[cpp/equality-on-floats] */\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * this can trigger when capacity of the nodes is not the\n\t\t\t\t\t * same. Example (also a test):\n\t\t\t\t\t * - node with capacity 3\n\t\t\t\t\t * - node with capacity 1\n\t\t\t\t\t * - 3 shards with cost 1\n\t\t\t\t\t * Best distribution would be 2 shards on node with\n\t\t\t\t\t * capacity 3 and one on node with capacity 1\n\t\t\t\t\t */\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * fmaxf and fminf here are only needed for cases when nodes\n\t\t\t\t * have different capacities. If they are the same, then both\n\t\t\t\t * arguments are equal.\n\t\t\t\t */\n\t\t\t\tfloat4 utilizationImprovement = fmaxf(\n\t\t\t\t\tsourceFillState->utilization - newTargetUtilization,\n\t\t\t\t\tnewSourceUtilization - targetFillState->utilization\n\t\t\t\t\t);\n\t\t\t\tfloat4 utilizationAddedByShard = fminf(\n\t\t\t\t\tnewTargetUtilization - targetFillState->utilization,\n\t\t\t\t\tsourceFillState->utilization - newSourceUtilization\n\t\t\t\t\t);\n\n\t\t\t\t/*\n\t\t\t\t * If the shard causes a lot of utilization, but the\n\t\t\t\t * improvement which is gained by moving it is small, then we\n\t\t\t\t * ignore the move. Probably there are other shards that are\n\t\t\t\t * better candidates, and in any case it's probably not worth\n\t\t\t\t * the effort to move the this shard.\n\t\t\t\t *\n\t\t\t\t * One of the main cases this tries to avoid is the rebalancer\n\t\t\t\t * moving a very large shard with the \"by_disk_size\" strategy\n\t\t\t\t * when that only gives a small benefit in data distribution.\n\t\t\t\t */\n\t\t\t\tfloat4 normalizedUtilizationImprovement = utilizationImprovement /\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  utilizationAddedByShard;\n\t\t\t\tif (normalizedUtilizationImprovement < improvementThreshold)\n\t\t\t\t{\n\t\t\t\t\tstate->ignoredMoves++;\n\t\t\t\t\tif (MaxRebalancerLoggedIgnoredMoves == -1 ||\n\t\t\t\t\t\tstate->ignoredMoves <= MaxRebalancerLoggedIgnoredMoves)\n\t\t\t\t\t{\n\t\t\t\t\t\tereport(NOTICE, (\n\t\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\t\"Ignoring move of shard %ld from %s:%d to %s:%d, because the move only brings a small improvement relative to the shard its size\",\n\t\t\t\t\t\t\t\t\t\tshardCost->shardId,\n\t\t\t\t\t\t\t\t\t\tsourceFillState->node->workerName,\n\t\t\t\t\t\t\t\t\t\tsourceFillState->node->workerPort,\n\t\t\t\t\t\t\t\t\t\ttargetFillState->node->workerName,\n\t\t\t\t\t\t\t\t\t\ttargetFillState->node->workerPort\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\t\t\t\"The balance improvement of %g is lower than the improvement_threshold of %g\",\n\t\t\t\t\t\t\t\t\t\tnormalizedUtilizationImprovement,\n\t\t\t\t\t\t\t\t\t\timprovementThreshold\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t));\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tMoveShardCost(sourceFillState, targetFillState,\n\t\t\t\t\t\t\t  shardCost, state);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n\n/*\n * ReplicationPlacementUpdates returns a list of placement updates which\n * replicates shard placements that need re-replication. To do this, the\n * function loops over the active shard placements, and for each shard placement\n * which needs to be re-replicated, it chooses an active worker node with\n * smallest number of shards as the target node.\n */\nList *\nReplicationPlacementUpdates(List *workerNodeList, List *activeShardPlacementList,\n\t\t\t\t\t\t\tint shardReplicationFactor)\n{\n\tList *placementUpdateList = NIL;\n\tListCell *shardPlacementCell = NULL;\n\tuint32 workerNodeIndex = 0;\n\tHTAB *placementsHash = ShardPlacementsListToHash(activeShardPlacementList);\n\tuint32 workerNodeCount = list_length(workerNodeList);\n\n\t/* get number of shards per node */\n\tuint32 *shardCountArray = palloc0(workerNodeCount * sizeof(uint32));\n\tforeach(shardPlacementCell, activeShardPlacementList)\n\t{\n\t\tShardPlacement *placement = lfirst(shardPlacementCell);\n\n\t\tfor (workerNodeIndex = 0; workerNodeIndex < workerNodeCount; workerNodeIndex++)\n\t\t{\n\t\t\tWorkerNode *node = list_nth(workerNodeList, workerNodeIndex);\n\t\t\tif (strncmp(node->workerName, placement->nodeName, WORKER_LENGTH) == 0 &&\n\t\t\t\tnode->workerPort == placement->nodePort)\n\t\t\t{\n\t\t\t\tshardCountArray[workerNodeIndex]++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tforeach(shardPlacementCell, activeShardPlacementList)\n\t{\n\t\tWorkerNode *sourceNode = NULL;\n\t\tWorkerNode *targetNode = NULL;\n\t\tuint32 targetNodeShardCount = UINT_MAX;\n\t\tuint32 targetNodeIndex = 0;\n\n\t\tShardPlacement *placement = (ShardPlacement *) lfirst(shardPlacementCell);\n\t\tuint64 shardId = placement->shardId;\n\n\t\t/* skip the shard placement if it has enough replications */\n\t\tint activePlacementCount = ShardActivePlacementCount(placementsHash, shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t workerNodeList);\n\t\tif (activePlacementCount >= shardReplicationFactor)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * We can copy the shard from any active worker node that contains the\n\t\t * shard.\n\t\t */\n\t\tfor (workerNodeIndex = 0; workerNodeIndex < workerNodeCount; workerNodeIndex++)\n\t\t{\n\t\t\tWorkerNode *workerNode = list_nth(workerNodeList, workerNodeIndex);\n\n\t\t\tbool placementExists = PlacementsHashFind(placementsHash, shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  workerNode);\n\t\t\tif (placementExists)\n\t\t\t{\n\t\t\t\tsourceNode = workerNode;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * If we couldn't find any worker node which contains the shard, then\n\t\t * all copies of the shard are list and we should error out.\n\t\t */\n\t\tif (sourceNode == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"could not find a source for shard \" UINT64_FORMAT,\n\t\t\t\t\t\t\t\t   shardId)));\n\t\t}\n\n\t\t/*\n\t\t * We can copy the shard to any worker node that doesn't contain the shard.\n\t\t * Among such worker nodes, we choose the worker node with minimum shard\n\t\t * count as the target.\n\t\t */\n\t\tfor (workerNodeIndex = 0; workerNodeIndex < workerNodeCount; workerNodeIndex++)\n\t\t{\n\t\t\tWorkerNode *workerNode = list_nth(workerNodeList, workerNodeIndex);\n\n\t\t\tif (!NodeCanHaveDistTablePlacements(workerNode))\n\t\t\t{\n\t\t\t\t/* never replicate placements to nodes that should not have placements */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* skip this node if it already contains the shard */\n\t\t\tbool placementExists = PlacementsHashFind(placementsHash, shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  workerNode);\n\t\t\tif (placementExists)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* compare and change the target node */\n\t\t\tif (shardCountArray[workerNodeIndex] < targetNodeShardCount)\n\t\t\t{\n\t\t\t\ttargetNode = workerNode;\n\t\t\t\ttargetNodeShardCount = shardCountArray[workerNodeIndex];\n\t\t\t\ttargetNodeIndex = workerNodeIndex;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * If there is no worker node which doesn't contain the shard, then the\n\t\t * shard replication factor is greater than number of worker nodes, and\n\t\t * we should error out.\n\t\t */\n\t\tif (targetNode == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"could not find a target for shard \" UINT64_FORMAT,\n\t\t\t\t\t\t\t\t   shardId)));\n\t\t}\n\n\t\t/* construct the placement update */\n\t\tPlacementUpdateEvent *placementUpdateEvent = palloc0(\n\t\t\tsizeof(PlacementUpdateEvent));\n\t\tplacementUpdateEvent->updateType = PLACEMENT_UPDATE_COPY;\n\t\tplacementUpdateEvent->shardId = shardId;\n\t\tplacementUpdateEvent->sourceNode = sourceNode;\n\t\tplacementUpdateEvent->targetNode = targetNode;\n\n\t\t/* record the placement update */\n\t\tplacementUpdateList = lappend(placementUpdateList, placementUpdateEvent);\n\n\t\t/* update the placements hash and the shard count array */\n\t\tPlacementsHashEnter(placementsHash, shardId, targetNode);\n\t\tshardCountArray[targetNodeIndex]++;\n\t}\n\n\thash_destroy(placementsHash);\n\n\treturn placementUpdateList;\n}\n\n\n/*\n * ShardActivePlacementCount returns the number of active placements for the\n * given shard which are placed at the active worker nodes.\n */\nstatic int\nShardActivePlacementCount(HTAB *activePlacementsHash, uint64 shardId,\n\t\t\t\t\t\t  List *activeWorkerNodeList)\n{\n\tint shardActivePlacementCount = 0;\n\tListCell *workerNodeCell = NULL;\n\n\tforeach(workerNodeCell, activeWorkerNodeList)\n\t{\n\t\tWorkerNode *workerNode = lfirst(workerNodeCell);\n\t\tbool placementExists = PlacementsHashFind(activePlacementsHash, shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t  workerNode);\n\t\tif (placementExists)\n\t\t{\n\t\t\tshardActivePlacementCount++;\n\t\t}\n\t}\n\n\treturn shardActivePlacementCount;\n}\n\n\n/*\n * ShardPlacementsListToHash creates and returns a hash set from a shard\n * placement list.\n */\nstatic HTAB *\nShardPlacementsListToHash(List *shardPlacementList)\n{\n\tListCell *shardPlacementCell = NULL;\n\tHASHCTL info;\n\tint shardPlacementCount = list_length(shardPlacementList);\n\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(ShardPlacement);\n\tinfo.entrysize = sizeof(ShardPlacement);\n\tinfo.hash = PlacementsHashHashCode;\n\tinfo.match = PlacementsHashCompare;\n\tinfo.hcxt = CurrentMemoryContext;\n\tint hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT);\n\n\tHTAB *shardPlacementsHash = hash_create(\"ActivePlacements Hash\",\n\t\t\t\t\t\t\t\t\t\t\tshardPlacementCount, &info, hashFlags);\n\n\tforeach(shardPlacementCell, shardPlacementList)\n\t{\n\t\tShardPlacement *shardPlacement = (ShardPlacement *) lfirst(shardPlacementCell);\n\t\tvoid *hashKey = (void *) shardPlacement;\n\t\thash_search(shardPlacementsHash, hashKey, HASH_ENTER, NULL);\n\t}\n\n\treturn shardPlacementsHash;\n}\n\n\n/*\n * PlacementsHashFind returns true if there exists a shard placement with the\n * given workerNode and shard id in the given placements hash, otherwise it\n * returns false.\n */\nstatic bool\nPlacementsHashFind(HTAB *placementsHash, uint64 shardId, WorkerNode *workerNode)\n{\n\tbool placementFound = false;\n\n\tShardPlacement shardPlacement;\n\tmemset(&shardPlacement, 0, sizeof(shardPlacement));\n\n\tshardPlacement.shardId = shardId;\n\tshardPlacement.nodeName = workerNode->workerName;\n\tshardPlacement.nodePort = workerNode->workerPort;\n\n\tvoid *hashKey = (void *) (&shardPlacement);\n\thash_search(placementsHash, hashKey, HASH_FIND, &placementFound);\n\n\treturn placementFound;\n}\n\n\n/*\n * PlacementsHashEnter enters a shard placement for the given worker node and\n * shard id to the given placements hash.\n */\nstatic void\nPlacementsHashEnter(HTAB *placementsHash, uint64 shardId, WorkerNode *workerNode)\n{\n\tShardPlacement shardPlacement;\n\tmemset(&shardPlacement, 0, sizeof(shardPlacement));\n\n\tshardPlacement.shardId = shardId;\n\tshardPlacement.nodeName = workerNode->workerName;\n\tshardPlacement.nodePort = workerNode->workerPort;\n\n\tvoid *hashKey = (void *) (&shardPlacement);\n\thash_search(placementsHash, hashKey, HASH_ENTER, NULL);\n}\n\n\n/*\n * PlacementsHashRemove removes the shard placement for the given worker node and\n * shard id from the given placements hash.\n */\nstatic void\nPlacementsHashRemove(HTAB *placementsHash, uint64 shardId, WorkerNode *workerNode)\n{\n\tShardPlacement shardPlacement;\n\tmemset(&shardPlacement, 0, sizeof(shardPlacement));\n\n\tshardPlacement.shardId = shardId;\n\tshardPlacement.nodeName = workerNode->workerName;\n\tshardPlacement.nodePort = workerNode->workerPort;\n\n\tvoid *hashKey = (void *) (&shardPlacement);\n\thash_search(placementsHash, hashKey, HASH_REMOVE, NULL);\n}\n\n\n/*\n * PlacementsHashCompare compares two shard placements using shard id, node name,\n * and node port number.\n */\nstatic int\nPlacementsHashCompare(const void *lhsKey, const void *rhsKey, Size keySize)\n{\n\tconst ShardPlacement *placementLhs = (const ShardPlacement *) lhsKey;\n\tconst ShardPlacement *placementRhs = (const ShardPlacement *) rhsKey;\n\n\tint shardIdCompare = 0;\n\n\t/* first, compare by shard id */\n\tif (placementLhs->shardId < placementRhs->shardId)\n\t{\n\t\tshardIdCompare = -1;\n\t}\n\telse if (placementLhs->shardId > placementRhs->shardId)\n\t{\n\t\tshardIdCompare = 1;\n\t}\n\n\tif (shardIdCompare != 0)\n\t{\n\t\treturn shardIdCompare;\n\t}\n\n\t/* then, compare by node name */\n\tint nodeNameCompare = strncmp(placementLhs->nodeName, placementRhs->nodeName,\n\t\t\t\t\t\t\t\t  WORKER_LENGTH);\n\tif (nodeNameCompare != 0)\n\t{\n\t\treturn nodeNameCompare;\n\t}\n\n\t/* finally, compare by node port */\n\tint nodePortCompare = placementLhs->nodePort - placementRhs->nodePort;\n\treturn nodePortCompare;\n}\n\n\n/*\n * PlacementsHashHashCode computes the hash code for a shard placement from the\n * placement's shard id, node name, and node port number.\n */\nstatic uint32\nPlacementsHashHashCode(const void *key, Size keySize)\n{\n\tconst ShardPlacement *placement = (const ShardPlacement *) key;\n\tconst uint64 *shardId = &(placement->shardId);\n\tconst char *nodeName = placement->nodeName;\n\tconst uint32 *nodePort = &(placement->nodePort);\n\n\t/* standard hash function outlined in Effective Java, Item 8 */\n\tuint32 result = 17;\n\tresult = 37 * result + tag_hash(shardId, sizeof(uint64));\n\tresult = 37 * result + string_hash(nodeName, WORKER_LENGTH);\n\tresult = 37 * result + tag_hash(nodePort, sizeof(uint32));\n\n\treturn result;\n}\n\n\n/* WorkerNodeListContains checks if the worker node exists in the given list. */\nstatic bool\nWorkerNodeListContains(List *workerNodeList, const char *workerName, uint32 workerPort)\n{\n\tbool workerNodeListContains = false;\n\tListCell *workerNodeCell = NULL;\n\n\tforeach(workerNodeCell, workerNodeList)\n\t{\n\t\tWorkerNode *workerNode = (WorkerNode *) lfirst(workerNodeCell);\n\n\t\tif ((strncmp(workerNode->workerName, workerName, WORKER_LENGTH) == 0) &&\n\t\t\t(workerNode->workerPort == workerPort))\n\t\t{\n\t\t\tworkerNodeListContains = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn workerNodeListContains;\n}\n\n\n/*\n * UpdateColocatedShardPlacementProgress updates the progress of the given placement,\n * along with its colocated placements, to the given state.\n */\nstatic void\nUpdateColocatedShardPlacementProgress(uint64 shardId, char *sourceName, int sourcePort,\n\t\t\t\t\t\t\t\t\t  uint64 progress)\n{\n\tProgressMonitorData *header = GetCurrentProgressMonitor();\n\n\tif (header != NULL)\n\t{\n\t\tPlacementUpdateEventProgress *steps = ProgressMonitorSteps(header);\n\t\tListCell *colocatedShardIntervalCell = NULL;\n\n\t\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\t\tList *colocatedShardIntervalList = ColocatedShardIntervalList(shardInterval);\n\n\t\tfor (int moveIndex = 0; moveIndex < header->stepCount; moveIndex++)\n\t\t{\n\t\t\tPlacementUpdateEventProgress *step = steps + moveIndex;\n\t\t\tuint64 currentShardId = step->shardId;\n\t\t\tbool colocatedShard = false;\n\n\t\t\tforeach(colocatedShardIntervalCell, colocatedShardIntervalList)\n\t\t\t{\n\t\t\t\tShardInterval *candidateShard = lfirst(colocatedShardIntervalCell);\n\t\t\t\tif (candidateShard->shardId == currentShardId)\n\t\t\t\t{\n\t\t\t\t\tcolocatedShard = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (colocatedShard &&\n\t\t\t\tstrcmp(step->sourceName, sourceName) == 0 &&\n\t\t\t\tstep->sourcePort == sourcePort)\n\t\t\t{\n\t\t\t\tpg_atomic_write_u64(&step->progress, progress);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * pg_dist_rebalance_strategy_enterprise_check is a now removed function, but\n * to avoid issues during upgrades a C stub is kept.\n */\nDatum\npg_dist_rebalance_strategy_enterprise_check(PG_FUNCTION_ARGS)\n{\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_validate_rebalance_strategy_functions checks all the functions for\n * their correct signature.\n *\n * SQL signature:\n *\n * citus_validate_rebalance_strategy_functions(\n *     shard_cost_function regproc,\n *     node_capacity_function regproc,\n *     shard_allowed_on_node_function regproc,\n * ) RETURNS VOID\n */\nDatum\ncitus_validate_rebalance_strategy_functions(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureShardCostUDF(PG_GETARG_OID(0));\n\tEnsureNodeCapacityUDF(PG_GETARG_OID(1));\n\tEnsureShardAllowedOnNodeUDF(PG_GETARG_OID(2));\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * EnsureShardCostUDF checks that the UDF matching the oid has the correct\n * signature to be used as a ShardCost function. The expected signature is:\n *\n * shard_cost(shardid bigint) returns float4\n */\nstatic void\nEnsureShardCostUDF(Oid functionOid)\n{\n\tHeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));\n\tif (!HeapTupleIsValid(proctup))\n\t{\n\t\tereport(ERROR, (errmsg(\"cache lookup failed for shard_cost_function with oid %u\",\n\t\t\t\t\t\t\t   functionOid)));\n\t}\n\tForm_pg_proc procForm = (Form_pg_proc) GETSTRUCT(proctup);\n\tchar *name = NameStr(procForm->proname);\n\tif (procForm->pronargs != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"signature for shard_cost_function is incorrect\"),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"number of arguments of %s should be 1, not %i\",\n\t\t\t\t\t\t\tname, procForm->pronargs)));\n\t}\n\tif (procForm->proargtypes.values[0] != INT8OID)\n\t{\n\t\tereport(ERROR, (errmsg(\"signature for shard_cost_function is incorrect\"),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"argument type of %s should be bigint\", name)));\n\t}\n\tif (procForm->prorettype != FLOAT4OID)\n\t{\n\t\tereport(ERROR, (errmsg(\"signature for shard_cost_function is incorrect\"),\n\t\t\t\t\t\terrdetail(\"return type of %s should be real\", name)));\n\t}\n\tReleaseSysCache(proctup);\n}\n\n\n/*\n * SplitShardsBetweenPrimaryAndClone splits the shards in shardPlacementList\n * between the primary and clone nodes, adding them to the respective lists.\n */\nvoid\nSplitShardsBetweenPrimaryAndClone(WorkerNode *primaryNode,\n\t\t\t\t\t\t\t\t  WorkerNode *cloneNode,\n\t\t\t\t\t\t\t\t  Name strategyName)\n{\n\tCheckCitusVersion(ERROR);\n\n\tList *relationIdList = NonColocatedDistRelationIdList();\n\n\tForm_pg_dist_rebalance_strategy strategy = GetRebalanceStrategy(strategyName);/* We use default strategy for now */\n\n\tRebalanceOptions options = {\n\t\t.relationIdList = relationIdList,\n\t\t.threshold = 0, /* Threshold is not strictly needed for two nodes */\n\t\t.maxShardMoves = -1, /* No limit on moves between these two nodes */\n\t\t.excludedShardArray = construct_empty_array(INT8OID),\n\t\t.drainOnly = false, /* Not a drain operation */\n\t\t.rebalanceStrategy = strategy,\n\t\t.improvementThreshold = 0, /* Consider all beneficial moves */\n\t\t.workerNode = primaryNode /* indicate Primary node as a source node */\n\t};\n\n\tSplitPrimaryCloneShards *splitShards = GetPrimaryCloneSplitRebalanceSteps(&options\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  cloneNode);\n\tAdjustShardsForPrimaryCloneNodeSplit(primaryNode, cloneNode,\n\t\t\t\t\t\t\t\t\t\t splitShards->primaryShardIdList, splitShards->\n\t\t\t\t\t\t\t\t\t\t cloneShardIdList);\n}\n\n\n/*\n * GetPrimaryCloneSplitRebalanceSteps returns a List of PlacementUpdateEvents that are needed to\n * rebalance a list of tables.\n */\nstatic SplitPrimaryCloneShards *\nGetPrimaryCloneSplitRebalanceSteps(RebalanceOptions *options, WorkerNode *cloneNode)\n{\n\tWorkerNode *sourceNode = options->workerNode;\n\tWorkerNode *targetNode = cloneNode;\n\n\t/* Initialize rebalance plan functions and context */\n\tEnsureShardCostUDF(options->rebalanceStrategy->shardCostFunction);\n\tEnsureNodeCapacityUDF(options->rebalanceStrategy->nodeCapacityFunction);\n\tEnsureShardAllowedOnNodeUDF(options->rebalanceStrategy->shardAllowedOnNodeFunction);\n\n\tRebalanceContext context;\n\tmemset(&context, 0, sizeof(RebalanceContext));\n\tfmgr_info(options->rebalanceStrategy->shardCostFunction, &context.shardCostUDF);\n\tfmgr_info(options->rebalanceStrategy->nodeCapacityFunction, &context.nodeCapacityUDF);\n\tfmgr_info(options->rebalanceStrategy->shardAllowedOnNodeFunction,\n\t\t\t  &context.shardAllowedOnNodeUDF);\n\n\tRebalancePlanFunctions rebalancePlanFunctions = {\n\t\t.shardAllowedOnNode = ShardAllowedOnNode,\n\t\t.nodeCapacity = NodeCapacity,\n\t\t.shardCost = GetShardCost,\n\t\t.context = &context,\n\t};\n\n\t/*\n\t * Collect all active shard placements on the source node for the given relations.\n\t * Unlike the main rebalancer, we build a single list of all relevant source placements\n\t * across all specified relations (or all relations if none specified).\n\t */\n\tList *allSourcePlacements = NIL;\n\tOid relationIdItr = InvalidOid;\n\tforeach_declared_oid(relationIdItr, options->relationIdList)\n\t{\n\t\tList *shardPlacementList = FullShardPlacementList(relationIdItr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  options->excludedShardArray);\n\t\tList *activeShardPlacementsForRelation =\n\t\t\tFilterShardPlacementList(shardPlacementList, IsActiveShardPlacement);\n\n\t\tShardPlacement *placement = NULL;\n\t\tforeach_declared_ptr(placement, activeShardPlacementsForRelation)\n\t\t{\n\t\t\tif (placement->nodeId == sourceNode->nodeId)\n\t\t\t{\n\t\t\t\t/* Ensure we don't add duplicate shardId if it's somehow listed under multiple relations */\n\t\t\t\tbool alreadyAdded = false;\n\t\t\t\tShardPlacement *existingPlacement = NULL;\n\t\t\t\tforeach_declared_ptr(existingPlacement, allSourcePlacements)\n\t\t\t\t{\n\t\t\t\t\tif (existingPlacement->shardId == placement->shardId)\n\t\t\t\t\t{\n\t\t\t\t\t\talreadyAdded = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!alreadyAdded)\n\t\t\t\t{\n\t\t\t\t\tallSourcePlacements = lappend(allSourcePlacements, placement);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tList *activeWorkerList = list_make2(options->workerNode, cloneNode);\n\tSplitPrimaryCloneShards *splitShards = palloc0(sizeof(SplitPrimaryCloneShards));\n\tsplitShards->primaryShardIdList = NIL;\n\tsplitShards->cloneShardIdList = NIL;\n\n\tif (list_length(allSourcePlacements) > 0)\n\t{\n\t\t/*\n\t\t * Initialize RebalanceState considering only the source node's shards\n\t\t * and the two active workers (source and target).\n\t\t */\n\t\tRebalanceState *state = InitRebalanceState(activeWorkerList, allSourcePlacements,\n\t\t\t\t\t\t\t\t\t\t\t\t   &rebalancePlanFunctions);\n\n\t\tNodeFillState *sourceFillState = NULL;\n\t\tNodeFillState *targetFillState = NULL;\n\t\tListCell *fsc = NULL;\n\n\t\t/* Identify the fill states for our specific source and target nodes */\n\t\tforeach(fsc, state->fillStateListAsc) /* Could be fillStateListDesc too, order doesn't matter here */\n\t\t{\n\t\t\tNodeFillState *fs = (NodeFillState *) lfirst(fsc);\n\t\t\tif (fs->node->nodeId == sourceNode->nodeId)\n\t\t\t{\n\t\t\t\tsourceFillState = fs;\n\t\t\t}\n\t\t\telse if (fs->node->nodeId == targetNode->nodeId)\n\t\t\t{\n\t\t\t\ttargetFillState = fs;\n\t\t\t}\n\t\t}\n\n\t\tif (sourceFillState != NULL && targetFillState != NULL)\n\t\t{\n\t\t\t/*\n\t\t\t * The goal is to move roughly half the total cost from source to target.\n\t\t\t * The target node is assumed to be empty or its existing load is not\n\t\t\t * considered for this specific two-node balancing plan's shard distribution.\n\t\t\t * We calculate costs based *only* on the shards currently on the source node.\n\t\t\t */\n\n\t\t\t/*\n\t\t\t * The core idea is to simulate the balancing process between these two nodes.\n\t\t\t * We have all shards on sourceFillState. TargetFillState is initially empty (in terms of these specific shards).\n\t\t\t * We want to move shards from source to target until their costs are as balanced as possible.\n\t\t\t */\n\t\t\tfloat4 sourceCurrentCost = sourceFillState->totalCost;\n\t\t\tfloat4 targetCurrentCost = 0; /* Representing cost on target from these source shards */\n\n\t\t\t/* Sort shards on source node by cost (descending). This is a common heuristic. */\n\t\t\tsourceFillState->shardCostListDesc = SortList(sourceFillState->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardCostListDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  CompareShardCostDesc);\n\n\t\t\tList *potentialMoves = NIL;\n\t\t\tListCell *lc_shardcost = NULL;\n\n\t\t\t/*\n\t\t\t * Iterate through each shard on the source node. For each shard, decide if moving it\n\t\t\t * to the target node would improve the balance (or is necessary to reach balance).\n\t\t\t * A simple greedy approach: move shard if target node's current cost is less than source's.\n\t\t\t */\n\t\t\tforeach(lc_shardcost, sourceFillState->shardCostListDesc)\n\t\t\t{\n\t\t\t\tShardCost *shardToConsider = (ShardCost *) lfirst(lc_shardcost);\n\n\t\t\t\t/*\n\t\t\t\t * If moving this shard makes the target less loaded than the source would become,\n\t\t\t\t * or if target is simply less loaded currently, consider the move.\n\t\t\t\t * More accurately, we move if target's cost + shard's cost < source's cost - shard's cost (approximately)\n\t\t\t\t * or if target is significantly emptier.\n\t\t\t\t * The condition (targetCurrentCost < sourceCurrentCost - shardToConsider->cost) is a greedy choice.\n\t\t\t\t * A better check: would moving this shard reduce the difference in costs?\n\t\t\t\t * Current difference: abs(sourceCurrentCost - targetCurrentCost)\n\t\t\t\t * Difference after move: abs((sourceCurrentCost - shardToConsider->cost) - (targetCurrentCost + shardToConsider->cost))\n\t\t\t\t * Move if new difference is smaller.\n\t\t\t\t */\n\t\t\t\tfloat4 costOfShard = shardToConsider->cost;\n\t\t\t\tfloat4 diffBefore = fabsf(sourceCurrentCost - targetCurrentCost);\n\t\t\t\tfloat4 diffAfter = fabsf((sourceCurrentCost - costOfShard) - (\n\t\t\t\t\t\t\t\t\t\t\t targetCurrentCost + costOfShard));\n\n\t\t\t\tif (diffAfter < diffBefore)\n\t\t\t\t{\n\t\t\t\t\tPlacementUpdateEvent *update = palloc0(sizeof(PlacementUpdateEvent));\n\t\t\t\t\tupdate->shardId = shardToConsider->shardId;\n\t\t\t\t\tupdate->sourceNode = sourceNode;\n\t\t\t\t\tupdate->targetNode = targetNode;\n\t\t\t\t\tupdate->updateType = PLACEMENT_UPDATE_MOVE;\n\t\t\t\t\tpotentialMoves = lappend(potentialMoves, update);\n\t\t\t\t\tsplitShards->cloneShardIdList = lappend_int(splitShards->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcloneShardIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardToConsider->shardId\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\n\n\t\t\t\t\t/* Update simulated costs for the next iteration */\n\t\t\t\t\tsourceCurrentCost -= costOfShard;\n\t\t\t\t\ttargetCurrentCost += costOfShard;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tsplitShards->primaryShardIdList = lappend_int(splitShards->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  primaryShardIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardToConsider->shardId\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/* RebalanceState is in memory context, will be cleaned up */\n\t}\n\treturn splitShards;\n}\n\n\n/*\n * Snapshot-based node split plan outputs the shard placement plan\n * for primary and replica based node split\n *\n * SQL signature:\n * get_snapshot_based_node_split_plan(\n *     primary_node_name text,\n *     primary_node_port integer,\n *     replica_node_name text,\n *     replica_node_port integer,\n *     rebalance_strategy name DEFAULT NULL\n *\n */\nDatum\nget_snapshot_based_node_split_plan(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *primaryNodeNameText = PG_GETARG_TEXT_P(0);\n\tint32 primaryNodePort = PG_GETARG_INT32(1);\n\ttext *cloneNodeNameText = PG_GETARG_TEXT_P(2);\n\tint32 cloneNodePort = PG_GETARG_INT32(3);\n\n\tchar *primaryNodeName = text_to_cstring(primaryNodeNameText);\n\tchar *cloneNodeName = text_to_cstring(cloneNodeNameText);\n\n\tWorkerNode *primaryNode = FindWorkerNodeOrError(primaryNodeName, primaryNodePort);\n\tWorkerNode *cloneNode = FindWorkerNodeOrError(cloneNodeName, cloneNodePort);\n\n\tif (!cloneNode->nodeisclone || cloneNode->nodeprimarynodeid == 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Node %s:%d (ID %d) is not a valid clone or its primary node ID is not set.\",\n\t\t\t\t\t\t\tcloneNode->workerName, cloneNode->workerPort,\n\t\t\t\t\t\t\tcloneNode->nodeId)));\n\t}\n\tif (primaryNode->nodeisclone)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"Primary node %s:%d (ID %d) is itself a replica.\",\n\t\t\t\t\t\t\t   primaryNode->workerName, primaryNode->workerPort,\n\t\t\t\t\t\t\t   primaryNode->nodeId)));\n\t}\n\n\t/* Ensure the primary node is related to the replica node */\n\tif (primaryNode->nodeId != cloneNode->nodeprimarynodeid)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Clone node %s:%d (ID %d) is not a clone of the primary node %s:%d (ID %d).\",\n\t\t\t\t\t\t\tcloneNode->workerName, cloneNode->workerPort,\n\t\t\t\t\t\t\tcloneNode->nodeId,\n\t\t\t\t\t\t\tprimaryNode->workerName, primaryNode->workerPort,\n\t\t\t\t\t\t\tprimaryNode->nodeId)));\n\t}\n\n\tList *relationIdList = NonColocatedDistRelationIdList();\n\n\tForm_pg_dist_rebalance_strategy strategy = GetRebalanceStrategy(\n\t\tPG_GETARG_NAME_OR_NULL(4));\n\n\tRebalanceOptions options = {\n\t\t.relationIdList = relationIdList,\n\t\t.threshold = 0, /* Threshold is not strictly needed for two nodes */\n\t\t.maxShardMoves = -1, /* No limit on moves between these two nodes */\n\t\t.excludedShardArray = construct_empty_array(INT8OID),\n\t\t.drainOnly = false, /* Not a drain operation */\n\t\t.rebalanceStrategy = strategy,\n\t\t.improvementThreshold = 0, /* Consider all beneficial moves */\n\t\t.workerNode = primaryNode /* indicate Primary node as a source node */\n\t};\n\n\tSplitPrimaryCloneShards *splitShards = GetPrimaryCloneSplitRebalanceSteps(\n\t\t&options,\n\t\tcloneNode);\n\n\tint shardId = 0;\n\tTupleDesc tupdesc;\n\tTuplestorestate *tupstore = SetupTuplestore(fcinfo, &tupdesc);\n\tDatum values[4];\n\tbool nulls[4];\n\n\n\tforeach_declared_int(shardId, splitShards->primaryShardIdList)\n\t{\n\t\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\t\tList *colocatedShardList = ColocatedShardIntervalList(shardInterval);\n\t\tListCell *colocatedShardCell = NULL;\n\t\tforeach(colocatedShardCell, colocatedShardList)\n\t\t{\n\t\t\tShardInterval *colocatedShard = lfirst(colocatedShardCell);\n\t\t\tint colocatedShardId = colocatedShard->shardId;\n\t\t\tmemset(values, 0, sizeof(values));\n\t\t\tmemset(nulls, 0, sizeof(nulls));\n\n\t\t\tvalues[0] = ObjectIdGetDatum(RelationIdForShard(colocatedShardId));\n\t\t\tvalues[1] = UInt64GetDatum(colocatedShardId);\n\t\t\tvalues[2] = UInt64GetDatum(ShardLength(colocatedShardId));\n\t\t\tvalues[3] = PointerGetDatum(cstring_to_text(\"Primary Node\"));\n\t\t\ttuplestore_putvalues(tupstore, tupdesc, values, nulls);\n\t\t}\n\t}\n\n\tforeach_declared_int(shardId, splitShards->cloneShardIdList)\n\t{\n\t\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\t\tList *colocatedShardList = ColocatedShardIntervalList(shardInterval);\n\t\tListCell *colocatedShardCell = NULL;\n\t\tforeach(colocatedShardCell, colocatedShardList)\n\t\t{\n\t\t\tShardInterval *colocatedShard = lfirst(colocatedShardCell);\n\t\t\tint colocatedShardId = colocatedShard->shardId;\n\t\t\tmemset(values, 0, sizeof(values));\n\t\t\tmemset(nulls, 0, sizeof(nulls));\n\n\t\t\tvalues[0] = ObjectIdGetDatum(RelationIdForShard(colocatedShardId));\n\t\t\tvalues[1] = UInt64GetDatum(colocatedShardId);\n\t\t\tvalues[2] = UInt64GetDatum(ShardLength(colocatedShardId));\n\t\t\tvalues[3] = PointerGetDatum(cstring_to_text(\"Clone Node\"));\n\t\t\ttuplestore_putvalues(tupstore, tupdesc, values, nulls);\n\t\t}\n\t}\n\n\treturn (Datum) 0;\n}\n\n\n/*\n * EnsureNodeCapacityUDF checks that the UDF matching the oid has the correct\n * signature to be used as a NodeCapacity function. The expected signature is:\n *\n * node_capacity(nodeid int) returns float4\n */\nstatic void\nEnsureNodeCapacityUDF(Oid functionOid)\n{\n\tHeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));\n\tif (!HeapTupleIsValid(proctup))\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"cache lookup failed for node_capacity_function with oid %u\",\n\t\t\t\t\t\t\tfunctionOid)));\n\t}\n\tForm_pg_proc procForm = (Form_pg_proc) GETSTRUCT(proctup);\n\tchar *name = NameStr(procForm->proname);\n\tif (procForm->pronargs != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"signature for node_capacity_function is incorrect\"),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"number of arguments of %s should be 1, not %i\",\n\t\t\t\t\t\t\tname, procForm->pronargs)));\n\t}\n\tif (procForm->proargtypes.values[0] != INT4OID)\n\t{\n\t\tereport(ERROR, (errmsg(\"signature for node_capacity_function is incorrect\"),\n\t\t\t\t\t\terrdetail(\"argument type of %s should be int\", name)));\n\t}\n\tif (procForm->prorettype != FLOAT4OID)\n\t{\n\t\tereport(ERROR, (errmsg(\"signature for node_capacity_function is incorrect\"),\n\t\t\t\t\t\terrdetail(\"return type of %s should be real\", name)));\n\t}\n\tReleaseSysCache(proctup);\n}\n\n\n/*\n * EnsureShardAllowedOnNodeUDF checks that the UDF matching the oid has the correct\n * signature to be used as a NodeCapacity function. The expected signature is:\n *\n * shard_allowed_on_node(shardid bigint, nodeid int) returns boolean\n */\nstatic void\nEnsureShardAllowedOnNodeUDF(Oid functionOid)\n{\n\tHeapTuple proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid));\n\tif (!HeapTupleIsValid(proctup))\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"cache lookup failed for shard_allowed_on_node_function with oid %u\",\n\t\t\t\t\t\t\tfunctionOid)));\n\t}\n\tForm_pg_proc procForm = (Form_pg_proc) GETSTRUCT(proctup);\n\tchar *name = NameStr(procForm->proname);\n\tif (procForm->pronargs != 2)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"signature for shard_allowed_on_node_function is incorrect\"),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"number of arguments of %s should be 2, not %i\",\n\t\t\t\t\t\t\tname, procForm->pronargs)));\n\t}\n\tif (procForm->proargtypes.values[0] != INT8OID)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"signature for shard_allowed_on_node_function is incorrect\"),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"type of first argument of %s should be bigint\", name)));\n\t}\n\tif (procForm->proargtypes.values[1] != INT4OID)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"signature for shard_allowed_on_node_function is incorrect\"),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"type of second argument of %s should be int\", name)));\n\t}\n\tif (procForm->prorettype != BOOLOID)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"signature for shard_allowed_on_node_function is incorrect\"),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"return type of %s should be boolean\", name)));\n\t}\n\tReleaseSysCache(proctup);\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/shard_split.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_split.c\n *\n * Function definitions for the shard split.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"commands/dbcommands.h\"\n#include \"common/hashfn.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/pg_list.h\"\n#include \"postmaster/postmaster.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/shard_rebalancer.h\"\n#include \"distributed/shard_split.h\"\n#include \"distributed/shard_transfer.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/shardsplit_logical_replication.h\"\n#include \"distributed/shared_library_init.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/utils/distribution_column_map.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_transaction.h\"\n\n/*\n * Entry for map that tracks ShardInterval -> Placement Node\n * created by split workflow.\n */\ntypedef struct ShardCreatedByWorkflowEntry\n{\n\tShardInterval *shardIntervalKey;\n\tWorkerNode *workerNodeValue;\n} ShardCreatedByWorkflowEntry;\n\n/*\n * Entry for map that trackes dummy shards.\n * Key: node + owner\n * Value: List of dummy shards for that node + owner\n */\ntypedef struct GroupedDummyShards\n{\n\tNodeAndOwner key;\n\tList *shardIntervals;\n} GroupedDummyShards;\n\n/* Function declarations */\nstatic void ErrorIfCannotSplitShard(SplitOperation splitOperation,\n\t\t\t\t\t\t\t\t\tShardInterval *sourceShard);\nstatic void ErrorIfCannotSplitShardExtended(SplitOperation splitOperation,\n\t\t\t\t\t\t\t\t\t\t\tShardInterval *shardIntervalToSplit,\n\t\t\t\t\t\t\t\t\t\t\tList *shardSplitPointsList,\n\t\t\t\t\t\t\t\t\t\t\tList *nodeIdsForPlacementList);\nstatic bool CheckIfRelationWithSameNameExists(ShardInterval *shardInterval,\n\t\t\t\t\t\t\t\t\t\t\t  WorkerNode *workerNode);\nstatic void ErrorIfModificationAndSplitInTheSameTransaction(SplitOperation\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsplitOperation);\nstatic void CreateSplitShardsForShardGroup(List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t   List *workersForPlacementList);\nstatic void CreateDummyShardsForShardGroup(HTAB *mapOfPlacementToDummyShardList,\n\t\t\t\t\t\t\t\t\t\t   List *sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t\t\t\t   List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t   WorkerNode *sourceWorkerNode,\n\t\t\t\t\t\t\t\t\t\t   List *workersForPlacementList);\nstatic HTAB * CreateWorkerForPlacementSet(List *workersForPlacementList);\nstatic void CreateAuxiliaryStructuresForShardGroup(List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t\t   List *workersForPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t   bool includeReplicaIdentity);\nstatic void CreateReplicaIdentitiesForDummyShards(HTAB *mapOfPlacementToDummyShardList);\nstatic void CreateObjectOnPlacement(List *objectCreationCommandList,\n\t\t\t\t\t\t\t\t\tWorkerNode *workerNode);\nstatic List *    CreateSplitIntervalsForShardGroup(List *sourceColocatedShardList,\n\t\t\t\t\t\t\t\t\t\t\t\t   List *splitPointsForShard);\nstatic void CreateSplitIntervalsForShard(ShardInterval *sourceShard,\n\t\t\t\t\t\t\t\t\t\t List *splitPointsForShard,\n\t\t\t\t\t\t\t\t\t\t List **shardSplitChildrenIntervalList);\nstatic void BlockingShardSplit(SplitOperation splitOperation,\n\t\t\t\t\t\t\t   uint64 splitWorkflowId,\n\t\t\t\t\t\t\t   List *sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t   List *shardSplitPointsList,\n\t\t\t\t\t\t\t   List *workersForPlacementList,\n\t\t\t\t\t\t\t   DistributionColumnMap *distributionColumnOverrides);\nstatic void NonBlockingShardSplit(SplitOperation splitOperation,\n\t\t\t\t\t\t\t\t  uint64 splitWorkflowId,\n\t\t\t\t\t\t\t\t  List *sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t\t  List *shardSplitPointsList,\n\t\t\t\t\t\t\t\t  List *workersForPlacementList,\n\t\t\t\t\t\t\t\t  DistributionColumnMap *distributionColumnOverrides,\n\t\t\t\t\t\t\t\t  uint32 targetColocationId);\nstatic void DoSplitCopy(WorkerNode *sourceShardNode,\n\t\t\t\t\t\tList *sourceColocatedShardIntervalList,\n\t\t\t\t\t\tList *shardGroupSplitIntervalListList,\n\t\t\t\t\t\tList *workersForPlacementList,\n\t\t\t\t\t\tchar *snapShotName,\n\t\t\t\t\t\tDistributionColumnMap *distributionColumnOverrides);\nstatic StringInfo CreateSplitCopyCommand(ShardInterval *sourceShardSplitInterval,\n\t\t\t\t\t\t\t\t\t\t char *distributionColumnName,\n\t\t\t\t\t\t\t\t\t\t List *splitChildrenShardIntervalList,\n\t\t\t\t\t\t\t\t\t\t List *workersForPlacementList);\nstatic Task * CreateSplitCopyTask(StringInfo splitCopyUdfCommand, char *snapshotName, int\n\t\t\t\t\t\t\t\t  taskId, uint64 jobId);\nstatic void UpdateDistributionColumnsForShardGroup(List *colocatedShardList,\n\t\t\t\t\t\t\t\t\t\t\t\t   DistributionColumnMap *distCols,\n\t\t\t\t\t\t\t\t\t\t\t\t   char distributionMethod,\n\t\t\t\t\t\t\t\t\t\t\t\t   int shardCount,\n\t\t\t\t\t\t\t\t\t\t\t\t   uint32 colocationId);\nstatic void InsertSplitChildrenShardMetadata(List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t List *workersForPlacementList);\nstatic void CreatePartitioningHierarchyForBlockingSplit(List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *workersForPlacementList);\nstatic void CreateForeignKeyConstraints(List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\tList *workersForPlacementList);\nstatic Task * CreateTaskForDDLCommandList(List *ddlCommandList, WorkerNode *workerNode);\nstatic StringInfo CreateSplitShardReplicationSetupUDF(List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *destinationWorkerNodesList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  DistributionColumnMap *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  distributionColumnOverrides);\nstatic List * ParseReplicationSlotInfoFromResult(PGresult *result);\n\nstatic List * ExecuteSplitShardReplicationSetupUDF(WorkerNode *sourceWorkerNode,\n\t\t\t\t\t\t\t\t\t\t\t\t   List *sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\t   List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t\t   List *destinationWorkerNodesList,\n\t\t\t\t\t\t\t\t\t\t\t\t   DistributionColumnMap *\n\t\t\t\t\t\t\t\t\t\t\t\t   distributionColumnOverrides);\nstatic void ExecuteSplitShardReleaseSharedMemory(MultiConnection *sourceConnection);\nstatic void AddDummyShardEntryInMap(HTAB *mapOfPlacementToDummyShardList, uint32\n\t\t\t\t\t\t\t\t\ttargetNodeId,\n\t\t\t\t\t\t\t\t\tShardInterval *shardInterval);\nstatic uint64 GetNextShardIdForSplitChild(void);\nstatic void AcquireNonblockingSplitLock(Oid relationId);\nstatic List * GetWorkerNodesFromWorkerIds(List *nodeIdsForPlacementList);\nstatic void DropShardListMetadata(List *shardIntervalList);\n\n/* Customize error message strings based on operation type */\nstatic const char *const SplitOperationName[] =\n{\n\t[SHARD_SPLIT_API] = \"split\",\n\t[ISOLATE_TENANT_TO_NEW_SHARD] = \"isolate\",\n\t[CREATE_DISTRIBUTED_TABLE] = \"create\"\n};\nstatic const char *const SplitOperationAPIName[] =\n{\n\t[SHARD_SPLIT_API] = \"citus_split_shard_by_split_points\",\n\t[ISOLATE_TENANT_TO_NEW_SHARD] = \"isolate_tenant_to_new_shard\",\n\t[CREATE_DISTRIBUTED_TABLE] = \"create_distributed_table_concurrently\"\n};\nstatic const char *const SplitTargetName[] =\n{\n\t[SHARD_SPLIT_API] = \"shard\",\n\t[ISOLATE_TENANT_TO_NEW_SHARD] = \"tenant\",\n\t[CREATE_DISTRIBUTED_TABLE] = \"distributed table\"\n};\n\n/* Function definitions */\n\n/*\n * ErrorIfCannotSplitShard checks relation kind and invalid shards. It errors\n * out if we are not able to split the given shard.\n */\nstatic void\nErrorIfCannotSplitShard(SplitOperation splitOperation, ShardInterval *sourceShard)\n{\n\tOid relationId = sourceShard->relationId;\n\tListCell *colocatedTableCell = NULL;\n\n\t/* checks for table ownership and foreign tables */\n\tList *colocatedTableList = ColocatedTableList(relationId);\n\tforeach(colocatedTableCell, colocatedTableList)\n\t{\n\t\tOid colocatedTableId = lfirst_oid(colocatedTableCell);\n\n\t\t/* check that user has owner rights in all co-located tables */\n\t\tEnsureTableOwner(colocatedTableId);\n\n\t\tchar relationKind = get_rel_relkind(colocatedTableId);\n\t\tif (relationKind == RELKIND_FOREIGN_TABLE)\n\t\t{\n\t\t\tchar *relationName = get_rel_name(colocatedTableId);\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot %s %s because \\\"%s\\\" is a \"\n\t\t\t\t\t\t\t\t   \"foreign table\",\n\t\t\t\t\t\t\t\t   SplitOperationName[splitOperation],\n\t\t\t\t\t\t\t\t   SplitTargetName[splitOperation],\n\t\t\t\t\t\t\t\t   relationName),\n\t\t\t\t\t\t\terrdetail(\"Splitting shards backed by foreign tables \"\n\t\t\t\t\t\t\t\t\t  \"is not supported.\")));\n\t\t}\n\t}\n}\n\n\n/*\n * Extended checks before we decide to split the shard.\n * When all consumers (Example : ISOLATE_TENANT_TO_NEW_SHARD) directly call 'SplitShard' API,\n * this method will be merged with 'ErrorIfCannotSplitShard' above.\n */\nstatic void\nErrorIfCannotSplitShardExtended(SplitOperation splitOperation,\n\t\t\t\t\t\t\t\tShardInterval *shardIntervalToSplit,\n\t\t\t\t\t\t\t\tList *shardSplitPointsList,\n\t\t\t\t\t\t\t\tList *nodeIdsForPlacementList)\n{\n\t/* we should not perform checks for create distributed table operation */\n\tif (splitOperation == CREATE_DISTRIBUTED_TABLE)\n\t{\n\t\treturn;\n\t}\n\n\tCitusTableCacheEntry *cachedTableEntry = GetCitusTableCacheEntry(\n\t\tshardIntervalToSplit->relationId);\n\n\t/* Perform checks common to both blocking and non-blocking Split API here. */\n\tif (!IsCitusTableTypeCacheEntry(cachedTableEntry, HASH_DISTRIBUTED))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"Cannot %s %s as operation \"\n\t\t\t\t\t\t\t   \"is only supported for hash distributed tables.\",\n\t\t\t\t\t\t\t   SplitOperationName[splitOperation],\n\t\t\t\t\t\t\t   SplitTargetName[splitOperation])));\n\t}\n\n\tuint32 relationReplicationFactor = TableShardReplicationFactor(\n\t\tshardIntervalToSplit->relationId);\n\tif (relationReplicationFactor > 1)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Operation %s not supported for %s as replication factor '%u' \"\n\t\t\t\t\t\t\t\"is greater than 1.\",\n\t\t\t\t\t\t\tSplitOperationName[splitOperation],\n\t\t\t\t\t\t\tSplitTargetName[splitOperation],\n\t\t\t\t\t\t\trelationReplicationFactor)));\n\t}\n\n\tint splitPointsCount = list_length(shardSplitPointsList);\n\tint nodeIdsCount = list_length(nodeIdsForPlacementList);\n\tint shardsCount = splitPointsCount + 1;\n\tif (nodeIdsCount != shardsCount)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t errmsg(\n\t\t\t\t\t \"Number of worker node ids should be one greater split points. \"\n\t\t\t\t\t \"NodeId count is '%d' and SplitPoint count is '%d'.\",\n\t\t\t\t\t nodeIdsCount,\n\t\t\t\t\t splitPointsCount)));\n\t}\n\n\tif (shardsCount > MAX_SHARD_COUNT)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"Resulting shard count '%d' with split is greater than max shard count '%d' limit.\",\n\t\t\t\t\t\t\tshardsCount, MAX_SHARD_COUNT)));\n\t}\n\n\tAssert(shardIntervalToSplit->minValueExists);\n\tAssert(shardIntervalToSplit->maxValueExists);\n\n\t/* We already verified table is Hash Distributed. We know (minValue, maxValue) are integers. */\n\tint32 minValue = DatumGetInt32(shardIntervalToSplit->minValue);\n\tint32 maxValue = DatumGetInt32(shardIntervalToSplit->maxValue);\n\n\t/* Fail if Shard Interval cannot be split anymore i.e (min, max) range overlap. */\n\tif (minValue == maxValue)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t errmsg(\n\t\t\t\t\t \"Cannot split shard id \\\"%lu\\\" as min/max range are equal: ('%d', '%d').\",\n\t\t\t\t\t shardIntervalToSplit->shardId,\n\t\t\t\t\t minValue,\n\t\t\t\t\t maxValue)));\n\t}\n\n\tNullableDatum lastShardSplitPoint = { 0, true /*isnull*/ };\n\tDatum shardSplitPoint;\n\tforeach_declared_int(shardSplitPoint, shardSplitPointsList)\n\t{\n\t\tint32 shardSplitPointValue = DatumGetInt32(shardSplitPoint);\n\n\t\t/*\n\t\t * 1) All Split points should lie within the shard interval range.\n\t\t * 2) Given our split points inclusive, you cannot specify the max value in a range as a split point.\n\t\t * Example: Shard 81060002 range is from (0,1073741823). '1073741823' as split point is invalid.\n\t\t * '1073741822' is correct and will split shard to: (0, 1073741822) and (1073741823, 1073741823).\n\t\t */\n\t\tif (shardSplitPointValue < minValue || shardSplitPointValue > maxValue)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t errmsg(\n\t\t\t\t\t\t \"Split point %d is outside the min/max range(%d, %d) for shard id %lu.\",\n\t\t\t\t\t\t shardSplitPointValue,\n\t\t\t\t\t\t DatumGetInt32(shardIntervalToSplit->minValue),\n\t\t\t\t\t\t DatumGetInt32(shardIntervalToSplit->maxValue),\n\t\t\t\t\t\t shardIntervalToSplit->shardId)));\n\t\t}\n\t\telse if (maxValue == shardSplitPointValue)\n\t\t{\n\t\t\tint32 validSplitPoint = shardIntervalToSplit->maxValue - 1;\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t errmsg(\n\t\t\t\t\t\t \"Invalid split point %d, as split points should be inclusive. Please use %d instead.\",\n\t\t\t\t\t\t maxValue,\n\t\t\t\t\t\t validSplitPoint)));\n\t\t}\n\n\t\t/* Split points should be in strictly increasing order */\n\t\tint32 lastShardSplitPointValue = DatumGetInt32(lastShardSplitPoint.value);\n\t\tif (!lastShardSplitPoint.isnull && shardSplitPointValue <=\n\t\t\tlastShardSplitPointValue)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t errmsg(\n\t\t\t\t\t\t \"Invalid Split Points '%d' followed by '%d'. \"\n\t\t\t\t\t\t \"All split points should be strictly increasing.\",\n\t\t\t\t\t\t lastShardSplitPointValue,\n\t\t\t\t\t\t shardSplitPointValue)));\n\t\t}\n\n\t\tlastShardSplitPoint = (NullableDatum) {\n\t\t\tshardSplitPoint, false\n\t\t};\n\t}\n}\n\n\n/*\n * ErrorIfModificationAndSplitInTheSameTransaction will error if we detect split operation\n * in the same transaction which has modification before.\n */\nstatic void\nErrorIfModificationAndSplitInTheSameTransaction(SplitOperation splitOperation)\n{\n\tif (XactModificationLevel > XACT_MODIFICATION_NONE)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),\n\t\t\t\t\t\terrmsg(\"cannot %s %s after other modifications \"\n\t\t\t\t\t\t\t   \"in the same transaction.\",\n\t\t\t\t\t\t\t   SplitOperationName[splitOperation],\n\t\t\t\t\t\t\t   SplitTargetName[splitOperation])));\n\t}\n}\n\n\n/*\n * ErrorIfMultipleNonblockingMoveSplitInTheSameTransaction will error if we detect multiple\n * nonblocking shard movements/splits in the same transaction.\n */\nvoid\nErrorIfMultipleNonblockingMoveSplitInTheSameTransaction(void)\n{\n\tif (PlacementMovedUsingLogicalReplicationInTX)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"multiple shard movements/splits via logical \"\n\t\t\t\t\t\t\t   \"replication in the same transaction is currently \"\n\t\t\t\t\t\t\t   \"not supported\")));\n\t}\n}\n\n\n/*\n * GetWorkerNodesFromWorkerIds returns list of worker nodes given a list\n * of worker ids. It will error if any node id is invalid.\n */\nstatic List *\nGetWorkerNodesFromWorkerIds(List *nodeIdsForPlacementList)\n{\n\tList *workersForPlacementList = NIL;\n\tint32 nodeId;\n\tforeach_declared_int(nodeId, nodeIdsForPlacementList)\n\t{\n\t\tuint32 nodeIdValue = (uint32) nodeId;\n\t\tWorkerNode *workerNode = LookupNodeByNodeId(nodeIdValue);\n\n\t\t/* NodeId in Citus are unsigned and range from [1, 4294967296]. */\n\t\tif (nodeIdValue < 1 || workerNode == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),\n\t\t\t\t\t\t\terrmsg(\"Invalid Node Id '%u'.\", nodeIdValue)));\n\t\t}\n\n\t\tworkersForPlacementList =\n\t\t\tlappend(workersForPlacementList, (void *) workerNode);\n\t}\n\n\treturn workersForPlacementList;\n}\n\n\n/*\n * SplitShard API to split a given shard (or shard group) based on specified split points\n * to a set of destination nodes.\n * 'splitMode'\t\t\t\t\t: Mode of split operation.\n * 'splitOperation'             : Customer operation that triggered split.\n * 'shardInterval'              : Source shard interval to be split.\n * 'shardSplitPointsList'\t\t: Split Points list for the source 'shardInterval'.\n * 'nodeIdsForPlacementList'\t: Placement list corresponding to split children.\n * 'distributionColumnOverrides': Maps relation IDs to distribution columns.\n *                                If not specified, the distribution column is read\n *                                from the metadata.\n * 'colocatedShardIntervalList' : Shard interval list for colocation group. (only used for\n *                                create_distributed_table_concurrently).\n * 'targetColocationId'         : Specifies the colocation ID (only used for\n *                                create_distributed_table_concurrently).\n */\nvoid\nSplitShard(SplitMode splitMode,\n\t\t   SplitOperation splitOperation,\n\t\t   uint64 shardIdToSplit,\n\t\t   List *shardSplitPointsList,\n\t\t   List *nodeIdsForPlacementList,\n\t\t   DistributionColumnMap *distributionColumnOverrides,\n\t\t   List *colocatedShardIntervalList,\n\t\t   uint32 targetColocationId)\n{\n\tconst char *operationName = SplitOperationAPIName[splitOperation];\n\n\tErrorIfModificationAndSplitInTheSameTransaction(splitOperation);\n\n\tShardInterval *shardIntervalToSplit = LoadShardInterval(shardIdToSplit);\n\tList *colocatedTableList = ColocatedTableList(shardIntervalToSplit->relationId);\n\n\tif (splitMode == AUTO_SPLIT)\n\t{\n\t\tVerifyTablesHaveReplicaIdentity(colocatedTableList);\n\t}\n\n\t/* Acquire global lock to prevent concurrent split on the same colocation group or relation */\n\tOid relationId = RelationIdForShard(shardIdToSplit);\n\tAcquirePlacementColocationLock(relationId, ExclusiveLock, \"split\");\n\n\t/* sort the tables to avoid deadlocks */\n\tcolocatedTableList = SortList(colocatedTableList, CompareOids);\n\tOid colocatedTableId = InvalidOid;\n\tforeach_declared_oid(colocatedTableId, colocatedTableList)\n\t{\n\t\t/*\n\t\t * Block concurrent DDL / TRUNCATE commands on the relation. Similarly,\n\t\t * block concurrent citus_move_shard_placement() / isolate_tenant_to_new_shard()\n\t\t * on any shard of the same relation.\n\t\t */\n\t\tLockRelationOid(colocatedTableId, ShareUpdateExclusiveLock);\n\t}\n\n\tErrorIfCannotSplitShard(splitOperation, shardIntervalToSplit);\n\tErrorIfCannotSplitShardExtended(\n\t\tsplitOperation,\n\t\tshardIntervalToSplit,\n\t\tshardSplitPointsList,\n\t\tnodeIdsForPlacementList);\n\n\tList *workersForPlacementList = GetWorkerNodesFromWorkerIds(nodeIdsForPlacementList);\n\n\tErrorIfNotAllNodesHaveReferenceTableReplicas(workersForPlacementList);\n\n\tList *sourceColocatedShardIntervalList = NIL;\n\tif (colocatedShardIntervalList == NIL)\n\t{\n\t\tsourceColocatedShardIntervalList = ColocatedShardIntervalList(\n\t\t\tshardIntervalToSplit);\n\t}\n\telse\n\t{\n\t\tsourceColocatedShardIntervalList = colocatedShardIntervalList;\n\t}\n\n\tDropOrphanedResourcesInSeparateTransaction();\n\n\t/* use the user-specified shard ID as the split workflow ID */\n\tuint64 splitWorkflowId = shardIntervalToSplit->shardId;\n\n\t/* Start operation to prepare for generating cleanup records */\n\tRegisterOperationNeedingCleanup();\n\n\tif (splitMode == BLOCKING_SPLIT)\n\t{\n\t\tereport(LOG, (errmsg(\"performing blocking %s \", operationName)));\n\n\t\tBlockingShardSplit(\n\t\t\tsplitOperation,\n\t\t\tsplitWorkflowId,\n\t\t\tsourceColocatedShardIntervalList,\n\t\t\tshardSplitPointsList,\n\t\t\tworkersForPlacementList,\n\t\t\tdistributionColumnOverrides);\n\t}\n\telse\n\t{\n\t\tereport(LOG, (errmsg(\"performing non-blocking %s \", operationName)));\n\n\t\tNonBlockingShardSplit(\n\t\t\tsplitOperation,\n\t\t\tsplitWorkflowId,\n\t\t\tsourceColocatedShardIntervalList,\n\t\t\tshardSplitPointsList,\n\t\t\tworkersForPlacementList,\n\t\t\tdistributionColumnOverrides,\n\t\t\ttargetColocationId);\n\n\t\tPlacementMovedUsingLogicalReplicationInTX = true;\n\t}\n\n\t/*\n\t * Drop temporary objects that were marked as CLEANUP_ALWAYS.\n\t */\n\tFinalizeOperationNeedingCleanupOnSuccess(operationName);\n}\n\n\n/*\n * SplitShard API to split a given shard (or shard group) in blocking fashion\n * based on specified split points to a set of destination nodes.\n * splitOperation                   : Customer operation that triggered split.\n * splitWorkflowId                  : Number used to identify split workflow in names.\n * sourceColocatedShardIntervalList : Source shard group to be split.\n * shardSplitPointsList             : Split Points list for the source 'shardInterval'.\n * workersForPlacementList          : Placement list corresponding to split children.\n */\nstatic void\nBlockingShardSplit(SplitOperation splitOperation,\n\t\t\t\t   uint64 splitWorkflowId,\n\t\t\t\t   List *sourceColocatedShardIntervalList,\n\t\t\t\t   List *shardSplitPointsList,\n\t\t\t\t   List *workersForPlacementList,\n\t\t\t\t   DistributionColumnMap *distributionColumnOverrides)\n{\n\tconst char *operationName = SplitOperationAPIName[splitOperation];\n\n\tBlockWritesToShardList(sourceColocatedShardIntervalList);\n\n\t/* First create shard interval metadata for split children */\n\tList *shardGroupSplitIntervalListList = CreateSplitIntervalsForShardGroup(\n\t\tsourceColocatedShardIntervalList,\n\t\tshardSplitPointsList);\n\n\t/* Only single placement allowed (already validated RelationReplicationFactor = 1) */\n\tShardInterval *firstShard = linitial(sourceColocatedShardIntervalList);\n\tWorkerNode *sourceShardNode =\n\t\tActiveShardPlacementWorkerNode(firstShard->shardId);\n\n\tereport(LOG, (errmsg(\"creating child shards for %s\", operationName)));\n\n\t/* Physically create split children. */\n\tCreateSplitShardsForShardGroup(shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t   workersForPlacementList);\n\n\tereport(LOG, (errmsg(\"performing copy for %s\", operationName)));\n\n\t/* For Blocking split, copy isn't snapshotted */\n\tchar *snapshotName = NULL;\n\tConflictWithIsolationTestingBeforeCopy();\n\tDoSplitCopy(sourceShardNode, sourceColocatedShardIntervalList,\n\t\t\t\tshardGroupSplitIntervalListList, workersForPlacementList,\n\t\t\t\tsnapshotName, distributionColumnOverrides);\n\tConflictWithIsolationTestingAfterCopy();\n\n\tereport(LOG, (errmsg(\n\t\t\t\t\t  \"creating auxillary structures (indexes, stats, replicaindentities, triggers) for %s\",\n\t\t\t\t\t  operationName)));\n\n\t/* Create auxiliary structures (indexes, stats, replicaindentities, triggers) */\n\tCreateAuxiliaryStructuresForShardGroup(shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t   workersForPlacementList,\n\t\t\t\t\t\t\t\t\t\t   true /* includeReplicaIdentity*/);\n\n\t/*\n\t * Up to this point, we performed various subtransactions that may\n\t * require additional clean-up in case of failure. The remaining operations\n\t * going forward are part of the same distributed transaction.\n\t */\n\n\n\t/*\n\t * Delete old shards metadata and mark the shards as to be deferred drop.\n\t * Have to do that before creating the new shard metadata,\n\t * because there's cross-checks preventing inconsistent metadata\n\t * (like overlapping shards).\n\t */\n\tereport(LOG, (errmsg(\"marking deferred cleanup of source shard(s) for %s\",\n\t\t\t\t\t\t operationName)));\n\n\tInsertDeferredDropCleanupRecordsForShards(sourceColocatedShardIntervalList);\n\n\tDropShardListMetadata(sourceColocatedShardIntervalList);\n\n\t/* Insert new shard and placement metdata */\n\tInsertSplitChildrenShardMetadata(shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t workersForPlacementList);\n\n\t/* create partitioning hierarchy, if any */\n\tCreatePartitioningHierarchyForBlockingSplit(\n\t\tshardGroupSplitIntervalListList,\n\t\tworkersForPlacementList);\n\n\tereport(LOG, (errmsg(\"creating foreign key constraints (if any) for %s\",\n\t\t\t\t\t\t operationName)));\n\n\t/*\n\t * Create foreign keys if exists after the metadata changes happening in\n\t * InsertSplitChildrenShardMetadata() because the foreign\n\t * key creation depends on the new metadata.\n\t */\n\tCreateForeignKeyConstraints(shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\tworkersForPlacementList);\n\n\tCitusInvalidateRelcacheByRelid(DistShardRelationId());\n}\n\n\n/* Check if a relation with given name already exists on the worker node */\nstatic bool\nCheckIfRelationWithSameNameExists(ShardInterval *shardInterval, WorkerNode *workerNode)\n{\n\tchar *schemaName = get_namespace_name(\n\t\tget_rel_namespace(shardInterval->relationId));\n\tchar *shardName = get_rel_name(shardInterval->relationId);\n\tAppendShardIdToName(&shardName, shardInterval->shardId);\n\n\tStringInfo checkShardExistsQuery = makeStringInfo();\n\n\t/*\n\t * We pass schemaName and shardName without quote_identifier, since\n\t * they are used as strings here.\n\t */\n\tappendStringInfo(checkShardExistsQuery,\n\t\t\t\t\t \"SELECT EXISTS (SELECT FROM pg_catalog.pg_tables WHERE schemaname = %s AND tablename = %s);\",\n\t\t\t\t\t quote_literal_cstr(schemaName),\n\t\t\t\t\t quote_literal_cstr(shardName));\n\n\tint connectionFlags = 0;\n\tMultiConnection *connection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerNode->workerPort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCitusExtensionOwnerName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tget_database_name(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tMyDatabaseId));\n\n\tPGresult *result = NULL;\n\tint queryResult = ExecuteOptionalRemoteCommand(connection,\n\t\t\t\t\t\t\t\t\t\t\t\t   checkShardExistsQuery->data, &result);\n\tif (queryResult != RESPONSE_OKAY || !IsResponseOK(result) || PQntuples(result) != 1)\n\t{\n\t\tReportResultError(connection, result, ERROR);\n\t}\n\n\tchar *existsString = PQgetvalue(result, 0, 0);\n\tbool tableExists = strcmp(existsString, \"t\") == 0;\n\n\tPQclear(result);\n\tForgetResults(connection);\n\n\treturn tableExists;\n}\n\n\n/* Create ShardGroup split children on a list of corresponding workers. */\nstatic void\nCreateSplitShardsForShardGroup(List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t   List *workersForPlacementList)\n{\n\t/*\n\t * Iterate over all the shards in the shard group.\n\t */\n\tList *shardIntervalList = NIL;\n\tforeach_declared_ptr(shardIntervalList, shardGroupSplitIntervalListList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tWorkerNode *workerPlacementNode = NULL;\n\n\t\t/*\n\t\t * Iterate on split shards DDL command list for a given shard\n\t\t * and create them on corresponding workerPlacementNode.\n\t\t */\n\t\tforboth_ptr(shardInterval, shardIntervalList, workerPlacementNode,\n\t\t\t\t\tworkersForPlacementList)\n\t\t{\n\t\t\t/* Populate list of commands necessary to create shard interval on destination */\n\t\t\tList *splitShardCreationCommandList = GetPreLoadTableCreationCommands(\n\t\t\t\tshardInterval->relationId,\n\t\t\t\tfalse, /* includeSequenceDefaults */\n\t\t\t\tfalse, /* includeIdentityDefaults */\n\t\t\t\tNULL /* auto add columnar options for cstore tables */);\n\t\t\tsplitShardCreationCommandList = WorkerApplyShardDDLCommandList(\n\t\t\t\tsplitShardCreationCommandList,\n\t\t\t\tshardInterval->shardId);\n\n\t\t\t/* Log resource for cleanup in case of failure only.\n\t\t\t * Before we log a record, do a best effort check to see if a shard with same name exists.\n\t\t\t * This is because, it will cause shard creation to fail and we will end up cleaning the\n\t\t\t * old shard. We don't want that.\n\t\t\t */\n\t\t\tbool relationExists = CheckIfRelationWithSameNameExists(shardInterval,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerPlacementNode);\n\n\t\t\tif (relationExists)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE),\n\t\t\t\t\t\t\t\terrmsg(\"relation %s already exists on worker %s:%d\",\n\t\t\t\t\t\t\t\t\t   ConstructQualifiedShardName(shardInterval),\n\t\t\t\t\t\t\t\t\t   workerPlacementNode->workerName,\n\t\t\t\t\t\t\t\t\t   workerPlacementNode->workerPort)));\n\t\t\t}\n\n\t\t\tInsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,\n\t\t\t\t\t\t\t\t\t\t\t\t  ConstructQualifiedShardName(\n\t\t\t\t\t\t\t\t\t\t\t\t\t  shardInterval),\n\t\t\t\t\t\t\t\t\t\t\t\t  workerPlacementNode->groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t  CLEANUP_ON_FAILURE);\n\n\t\t\t/* Create new split child shard on the specified placement list */\n\t\t\tCreateObjectOnPlacement(splitShardCreationCommandList,\n\t\t\t\t\t\t\t\t\tworkerPlacementNode);\n\t\t}\n\t}\n}\n\n\n/* Create a DDL task with corresponding task list on given worker node */\nstatic Task *\nCreateTaskForDDLCommandList(List *ddlCommandList, WorkerNode *workerNode)\n{\n\tTask *ddlTask = CitusMakeNode(Task);\n\tddlTask->taskType = DDL_TASK;\n\tddlTask->replicationModel = REPLICATION_MODEL_INVALID;\n\tSetTaskQueryStringList(ddlTask, ddlCommandList);\n\n\tShardPlacement *taskPlacement = CitusMakeNode(ShardPlacement);\n\tSetPlacementNodeMetadata(taskPlacement, workerNode);\n\tddlTask->taskPlacementList = list_make1(taskPlacement);\n\n\treturn ddlTask;\n}\n\n\n/* Create ShardGroup auxiliary structures (indexes, stats, replicaindentities, triggers)\n * on a list of corresponding workers.\n */\nstatic void\nCreateAuxiliaryStructuresForShardGroup(List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t   List *workersForPlacementList, bool\n\t\t\t\t\t\t\t\t\t   includeReplicaIdentity)\n{\n\tList *shardIntervalList = NIL;\n\tList *ddlTaskExecList = NIL;\n\n\t/*\n\t * Iterate over all the shards in the shard group.\n\t */\n\tforeach_declared_ptr(shardIntervalList, shardGroupSplitIntervalListList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tWorkerNode *workerPlacementNode = NULL;\n\n\t\t/*\n\t\t * Iterate on split shard interval list for given shard and create tasks\n\t\t * for every single split shard in a shard group.\n\t\t */\n\t\tforboth_ptr(shardInterval, shardIntervalList, workerPlacementNode,\n\t\t\t\t\tworkersForPlacementList)\n\t\t{\n\t\t\tList *ddlCommandList = GetPostLoadTableCreationCommands(\n\t\t\t\tshardInterval->relationId,\n\t\t\t\ttrue /* includeIndexes */,\n\t\t\t\tincludeReplicaIdentity);\n\t\t\tddlCommandList = WorkerApplyShardDDLCommandList(\n\t\t\t\tddlCommandList,\n\t\t\t\tshardInterval->shardId);\n\n\t\t\t/*\n\t\t\t * A task is expected to be instantiated with a non-null 'ddlCommandList'.\n\t\t\t * The list can be empty, if no auxiliary structures are present.\n\t\t\t */\n\t\t\tif (ddlCommandList != NULL)\n\t\t\t{\n\t\t\t\tTask *ddlTask = CreateTaskForDDLCommandList(ddlCommandList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerPlacementNode);\n\n\t\t\t\tddlTaskExecList = lappend(ddlTaskExecList, ddlTask);\n\t\t\t}\n\t\t}\n\t}\n\n\tExecuteTaskListOutsideTransaction(\n\t\tROW_MODIFY_NONE,\n\t\tddlTaskExecList,\n\t\tMaxAdaptiveExecutorPoolSize,\n\t\tNULL /* jobIdList (ignored by API impl.) */);\n}\n\n\n/*\n * Perform Split Copy from source shard(s) to split children.\n * 'sourceShardNode'\t\t\t\t\t: Source shard worker node.\n * 'sourceColocatedShardIntervalList'\t: List of source shard intervals from shard group.\n * 'shardGroupSplitIntervalListList'\t: List of shard intervals for split children.\n * 'workersForPlacementList'\t\t\t: List of workers for split children placement.\n */\nstatic void\nDoSplitCopy(WorkerNode *sourceShardNode, List *sourceColocatedShardIntervalList,\n\t\t\tList *shardGroupSplitIntervalListList, List *destinationWorkerNodesList,\n\t\t\tchar *snapShotName, DistributionColumnMap *distributionColumnOverrides)\n{\n\tShardInterval *sourceShardIntervalToCopy = NULL;\n\tList *splitShardIntervalList = NIL;\n\n\tint taskId = 0;\n\tList *splitCopyTaskList = NIL;\n\tforboth_ptr(sourceShardIntervalToCopy, sourceColocatedShardIntervalList,\n\t\t\t\tsplitShardIntervalList, shardGroupSplitIntervalListList)\n\t{\n\t\t/*\n\t\t * Skip copying data for partitioned tables, because they contain no\n\t\t * data themselves. Their partitions do contain data, but those are\n\t\t * different colocated shards that will be copied seperately.\n\t\t */\n\t\tif (PartitionedTable(sourceShardIntervalToCopy->relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tOid relationId = sourceShardIntervalToCopy->relationId;\n\n\t\tVar *distributionColumn =\n\t\t\tGetDistributionColumnWithOverrides(relationId,\n\t\t\t\t\t\t\t\t\t\t\t   distributionColumnOverrides);\n\t\tAssert(distributionColumn != NULL);\n\n\t\tbool missingOK = false;\n\t\tchar *distributionColumnName = get_attname(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t   distributionColumn->varattno,\n\t\t\t\t\t\t\t\t\t\t\t\t   missingOK);\n\n\t\tStringInfo splitCopyUdfCommand = CreateSplitCopyCommand(\n\t\t\tsourceShardIntervalToCopy,\n\t\t\tdistributionColumnName,\n\t\t\tsplitShardIntervalList,\n\t\t\tdestinationWorkerNodesList);\n\n\t\t/* Create copy task. Snapshot name is required for nonblocking splits */\n\t\tTask *splitCopyTask = CreateSplitCopyTask(splitCopyUdfCommand, snapShotName,\n\t\t\t\t\t\t\t\t\t\t\t\t  taskId,\n\t\t\t\t\t\t\t\t\t\t\t\t  sourceShardIntervalToCopy->shardId);\n\n\t\tShardPlacement *taskPlacement = CitusMakeNode(ShardPlacement);\n\t\tSetPlacementNodeMetadata(taskPlacement, sourceShardNode);\n\t\tsplitCopyTask->taskPlacementList = list_make1(taskPlacement);\n\n\t\tsplitCopyTaskList = lappend(splitCopyTaskList, splitCopyTask);\n\t\ttaskId++;\n\t}\n\n\tExecuteTaskListOutsideTransaction(ROW_MODIFY_NONE, splitCopyTaskList,\n\t\t\t\t\t\t\t\t\t  MaxAdaptiveExecutorPoolSize,\n\t\t\t\t\t\t\t\t\t  NULL /* jobIdList (ignored by API impl.) */);\n}\n\n\n/*\n * Create Copy command for a given shard source shard to be copied to corresponding split children.\n * 'sourceShardSplitInterval' : Source shard interval to be copied.\n * 'splitChildrenShardINnerIntervalList' : List of shard intervals for split children.\n * 'destinationWorkerNodesList' : List of workers for split children placement.\n * Here is an example of a 2 way split copy :\n * SELECT * from worker_split_copy(\n *  81060000, -- source shard id to split copy\n *  ARRAY[\n *       -- split copy info for split children 1\n *      ROW(81060015, -- destination shard id\n *           -2147483648, -- split range begin\n *          1073741823, --split range end\n *          10 -- worker node id)::pg_catalog.split_copy_info,\n *      -- split copy info for split children 2\n *      ROW(81060016,  --destination shard id\n *          1073741824, --split range begin\n *          2147483647, --split range end\n *          11 -- workef node id)::pg_catalog.split_copy_info\n *      ]\n *  );\n */\nstatic StringInfo\nCreateSplitCopyCommand(ShardInterval *sourceShardSplitInterval,\n\t\t\t\t\t   char *distributionColumnName,\n\t\t\t\t\t   List *splitChildrenShardIntervalList,\n\t\t\t\t\t   List *destinationWorkerNodesList)\n{\n\tStringInfo splitCopyInfoArray = makeStringInfo();\n\tappendStringInfo(splitCopyInfoArray, \"ARRAY[\");\n\n\tShardInterval *splitChildShardInterval = NULL;\n\tbool addComma = false;\n\tWorkerNode *destinationWorkerNode = NULL;\n\tforboth_ptr(splitChildShardInterval, splitChildrenShardIntervalList,\n\t\t\t\tdestinationWorkerNode, destinationWorkerNodesList)\n\t{\n\t\tif (addComma)\n\t\t{\n\t\t\tappendStringInfo(splitCopyInfoArray, \",\");\n\t\t}\n\n\t\tStringInfo splitCopyInfoRow = makeStringInfo();\n\t\tappendStringInfo(splitCopyInfoRow,\n\t\t\t\t\t\t \"ROW(%lu, %d, %d, %u)::pg_catalog.split_copy_info\",\n\t\t\t\t\t\t splitChildShardInterval->shardId,\n\t\t\t\t\t\t DatumGetInt32(splitChildShardInterval->minValue),\n\t\t\t\t\t\t DatumGetInt32(splitChildShardInterval->maxValue),\n\t\t\t\t\t\t destinationWorkerNode->nodeId);\n\t\tappendStringInfo(splitCopyInfoArray, \"%s\", splitCopyInfoRow->data);\n\n\t\taddComma = true;\n\t}\n\tappendStringInfo(splitCopyInfoArray, \"]\");\n\n\tStringInfo splitCopyUdf = makeStringInfo();\n\tappendStringInfo(splitCopyUdf, \"SELECT pg_catalog.worker_split_copy(%lu, %s, %s);\",\n\t\t\t\t\t sourceShardSplitInterval->shardId,\n\t\t\t\t\t quote_literal_cstr(distributionColumnName),\n\t\t\t\t\t splitCopyInfoArray->data);\n\n\treturn splitCopyUdf;\n}\n\n\n/*\n * CreateSplitCopyTask creates a task for copying data.\n * In the case of Non-blocking split, snapshotted copy task is created with given 'snapshotName'.\n * 'snapshotName' is NULL for Blocking split.\n */\nstatic Task *\nCreateSplitCopyTask(StringInfo splitCopyUdfCommand, char *snapshotName, int taskId, uint64\n\t\t\t\t\tjobId)\n{\n\tList *ddlCommandList = NIL;\n\tStringInfo beginTransaction = makeStringInfo();\n\tappendStringInfo(beginTransaction,\n\t\t\t\t\t \"BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;\");\n\tddlCommandList = lappend(ddlCommandList, beginTransaction->data);\n\n\t/* Set snapshot for non-blocking shard split. */\n\tif (snapshotName != NULL)\n\t{\n\t\tStringInfo snapShotString = makeStringInfo();\n\t\tappendStringInfo(snapShotString, \"SET TRANSACTION SNAPSHOT %s;\",\n\t\t\t\t\t\t quote_literal_cstr(\n\t\t\t\t\t\t\t snapshotName));\n\t\tddlCommandList = lappend(ddlCommandList, snapShotString->data);\n\t}\n\n\tddlCommandList = lappend(ddlCommandList, splitCopyUdfCommand->data);\n\n\tStringInfo commitCommand = makeStringInfo();\n\tappendStringInfo(commitCommand, \"COMMIT;\");\n\tddlCommandList = lappend(ddlCommandList, commitCommand->data);\n\n\tTask *splitCopyTask = CitusMakeNode(Task);\n\tsplitCopyTask->jobId = jobId;\n\tsplitCopyTask->taskId = taskId;\n\tsplitCopyTask->taskType = READ_TASK;\n\tsplitCopyTask->replicationModel = REPLICATION_MODEL_INVALID;\n\tSetTaskQueryStringList(splitCopyTask, ddlCommandList);\n\n\treturn splitCopyTask;\n}\n\n\n/*\n * Create an object on a worker node.\n */\nstatic void\nCreateObjectOnPlacement(List *objectCreationCommandList,\n\t\t\t\t\t\tWorkerNode *workerPlacementNode)\n{\n\tMultiConnection *connection =\n\t\tGetNodeUserDatabaseConnection(OUTSIDE_TRANSACTION,\n\t\t\t\t\t\t\t\t\t  workerPlacementNode->workerName,\n\t\t\t\t\t\t\t\t\t  workerPlacementNode->workerPort,\n\t\t\t\t\t\t\t\t\t  NULL, NULL);\n\tSendCommandListToWorkerOutsideTransactionWithConnection(connection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tobjectCreationCommandList);\n}\n\n\n/*\n * Create split children intervals for a shardgroup given list of split points.\n * Example:\n * 'sourceColocatedShardIntervalList': Colocated shard S1[-2147483648, 2147483647] & S2[-2147483648, 2147483647]\n * 'splitPointsForShard': [0] (2 way split)\n * 'shardGroupSplitIntervalListList':\n *  [\n *      [ S1_1(-2147483648, 0), S1_2(1, 2147483647) ], // Split Interval List for S1.\n *      [ S2_1(-2147483648, 0), S2_2(1, 2147483647) ]  // Split Interval List for S2.\n *  ]\n */\nstatic List *\nCreateSplitIntervalsForShardGroup(List *sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t\t  List *splitPointsForShard)\n{\n\tList *shardGroupSplitIntervalListList = NIL;\n\n\tShardInterval *shardToSplitInterval = NULL;\n\tforeach_declared_ptr(shardToSplitInterval, sourceColocatedShardIntervalList)\n\t{\n\t\tList *shardSplitIntervalList = NIL;\n\t\tCreateSplitIntervalsForShard(shardToSplitInterval, splitPointsForShard,\n\t\t\t\t\t\t\t\t\t &shardSplitIntervalList);\n\n\t\tshardGroupSplitIntervalListList = lappend(shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t\t  shardSplitIntervalList);\n\t}\n\n\treturn shardGroupSplitIntervalListList;\n}\n\n\n/*\n * Create split children intervals given a sourceshard and a list of split points.\n * Example: SourceShard is range [0, 100] and SplitPoints are (15, 30) will give us:\n *  [(0, 15) (16, 30) (31, 100)]\n */\nstatic void\nCreateSplitIntervalsForShard(ShardInterval *sourceShard,\n\t\t\t\t\t\t\t List *splitPointsForShard,\n\t\t\t\t\t\t\t List **shardSplitChildrenIntervalList)\n{\n\t/* For 'N' split points, we will have N+1 shard intervals created. */\n\tint shardIntervalCount = list_length(splitPointsForShard) + 1;\n\tListCell *splitPointCell = list_head(splitPointsForShard);\n\tint32 splitParentMaxValue = DatumGetInt32(sourceShard->maxValue);\n\tint32 currentSplitChildMinValue = DatumGetInt32(sourceShard->minValue);\n\n\t/* if we are splitting a Citus local table, assume whole shard range */\n\tif (!sourceShard->maxValueExists)\n\t{\n\t\tsplitParentMaxValue = PG_INT32_MAX;\n\t}\n\n\tif (!sourceShard->minValueExists)\n\t{\n\t\tcurrentSplitChildMinValue = PG_INT32_MIN;\n\t}\n\n\tfor (int index = 0; index < shardIntervalCount; index++)\n\t{\n\t\tShardInterval *splitChildShardInterval = CopyShardInterval(sourceShard);\n\t\tsplitChildShardInterval->shardIndex = -1;\n\t\tsplitChildShardInterval->shardId = GetNextShardIdForSplitChild();\n\n\t\tsplitChildShardInterval->minValueExists = true;\n\t\tsplitChildShardInterval->minValue = currentSplitChildMinValue;\n\t\tsplitChildShardInterval->maxValueExists = true;\n\n\t\t/* Length of splitPointsForShard is one less than 'shardIntervalCount' and we need to account */\n\t\t/* for 'splitPointCell' being NULL for last iteration. */\n\t\tif (splitPointCell)\n\t\t{\n\t\t\tsplitChildShardInterval->maxValue = DatumGetInt32((Datum) lfirst(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  splitPointCell));\n\t\t\tsplitPointCell = lnext(splitPointsForShard, splitPointCell);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsplitChildShardInterval->maxValue = splitParentMaxValue;\n\t\t}\n\n\t\tcurrentSplitChildMinValue = splitChildShardInterval->maxValue + 1;\n\t\t*shardSplitChildrenIntervalList = lappend(*shardSplitChildrenIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\t  splitChildShardInterval);\n\t}\n}\n\n\n/*\n * UpdateDistributionColumnsForShardGroup globally updates the pg_dist_partition metadata\n * for each relation that has a shard in colocatedShardList.\n *\n * This is used primarily for Citus local -> distributed table conversion\n * in create_distributed_table_concurrently.\n *\n * It would be nicer to keep this separate from shard split, but we need to do the\n * update at exactly the right point in the shard split process, namely after\n * replication slot creation and before inserting shard metadata, which itself\n * needs to happen before foreign key creation (mainly because the foreign key\n * functions depend on metadata).\n */\nstatic void\nUpdateDistributionColumnsForShardGroup(List *colocatedShardList,\n\t\t\t\t\t\t\t\t\t   DistributionColumnMap *distributionColumnMap,\n\t\t\t\t\t\t\t\t\t   char distributionMethod,\n\t\t\t\t\t\t\t\t\t   int shardCount,\n\t\t\t\t\t\t\t\t\t   uint32 colocationId)\n{\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, colocatedShardList)\n\t{\n\t\tOid relationId = shardInterval->relationId;\n\t\tVar *distributionColumn = GetDistributionColumnFromMap(distributionColumnMap,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   relationId);\n\n\t\t/* we should have an entry for every relation ID in the colocation group */\n\t\tAssert(distributionColumn != NULL);\n\n\t\tif (colocationId == INVALID_COLOCATION_ID)\n\t\t{\n\t\t\t/*\n\t\t\t * Getting here with an invalid co-location ID means that no\n\t\t\t * appropriate co-location group exists yet.\n\t\t\t */\n\t\t\tcolocationId = CreateColocationGroup(shardCount,\n\t\t\t\t\t\t\t\t\t\t\t\t ShardReplicationFactor,\n\t\t\t\t\t\t\t\t\t\t\t\t distributionColumn->vartype,\n\t\t\t\t\t\t\t\t\t\t\t\t distributionColumn->varcollid);\n\t\t}\n\n\t\tUpdateDistributionColumnGlobally(relationId, distributionMethod,\n\t\t\t\t\t\t\t\t\t\t distributionColumn, colocationId);\n\t}\n}\n\n\n/*\n * Insert new shard and placement metadata.\n * Sync the Metadata with all nodes if enabled.\n */\nstatic void\nInsertSplitChildrenShardMetadata(List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t List *workersForPlacementList)\n{\n\tList *shardIntervalList = NIL;\n\tList *syncedShardList = NIL;\n\n\t/*\n\t * Iterate over all the shards in the shard group.\n\t */\n\tforeach_declared_ptr(shardIntervalList, shardGroupSplitIntervalListList)\n\t{\n\t\t/*\n\t\t * Iterate on split shards list for a given shard and insert metadata.\n\t\t */\n\t\tShardInterval *shardInterval = NULL;\n\t\tWorkerNode *workerPlacementNode = NULL;\n\t\tforboth_ptr(shardInterval, shardIntervalList, workerPlacementNode,\n\t\t\t\t\tworkersForPlacementList)\n\t\t{\n\t\t\tInsertShardRow(\n\t\t\t\tshardInterval->relationId,\n\t\t\t\tshardInterval->shardId,\n\t\t\t\tshardInterval->storageType,\n\t\t\t\tIntegerToText(DatumGetInt32(shardInterval->minValue)),\n\t\t\t\tIntegerToText(DatumGetInt32(shardInterval->maxValue)));\n\n\t\t\tInsertShardPlacementRow(\n\t\t\t\tshardInterval->shardId,\n\t\t\t\tINVALID_PLACEMENT_ID, /* triggers generation of new id */\n\t\t\t\t0, /* shard length (zero for HashDistributed Table) */\n\t\t\t\tworkerPlacementNode->groupId);\n\n\t\t\tif (ShouldSyncTableMetadata(shardInterval->relationId))\n\t\t\t{\n\t\t\t\tsyncedShardList = lappend(syncedShardList, shardInterval);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* send commands to synced nodes one by one */\n\tList *splitOffShardMetadataCommandList = ShardListInsertCommand(syncedShardList);\n\tchar *command = NULL;\n\tforeach_declared_ptr(command, splitOffShardMetadataCommandList)\n\t{\n\t\tSendCommandToWorkersWithMetadata(command);\n\t}\n}\n\n\n/*\n * CreatePartitioningHierarchy creates the partitioning\n * hierarchy between the shardList, if any.\n */\nstatic void\nCreatePartitioningHierarchyForBlockingSplit(List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\tList *workersForPlacementList)\n{\n\t/* Create partition heirarchy between shards */\n\tList *shardIntervalList = NIL;\n\n\t/*\n\t * Iterate over all the shards in the shard group.\n\t */\n\tforeach_declared_ptr(shardIntervalList, shardGroupSplitIntervalListList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tWorkerNode *workerPlacementNode = NULL;\n\n\t\t/*\n\t\t * Iterate on split shards list for a given shard and create constraints.\n\t\t */\n\t\tforboth_ptr(shardInterval, shardIntervalList, workerPlacementNode,\n\t\t\t\t\tworkersForPlacementList)\n\t\t{\n\t\t\tif (PartitionTable(shardInterval->relationId))\n\t\t\t{\n\t\t\t\tchar *attachPartitionCommand =\n\t\t\t\t\tGenerateAttachShardPartitionCommand(shardInterval);\n\n\t\t\t\tSendCommandToWorker(\n\t\t\t\t\tworkerPlacementNode->workerName,\n\t\t\t\t\tworkerPlacementNode->workerPort,\n\t\t\t\t\tattachPartitionCommand);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * Create foreign key constraints on the split children shards.\n */\nstatic void\nCreateForeignKeyConstraints(List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\tList *workersForPlacementList)\n{\n\t/* Create constraints between shards */\n\tList *shardIntervalList = NIL;\n\n\t/*\n\t * Iterate over all the shards in the shard group.\n\t */\n\tforeach_declared_ptr(shardIntervalList, shardGroupSplitIntervalListList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tWorkerNode *workerPlacementNode = NULL;\n\n\t\t/*\n\t\t * Iterate on split shards list for a given shard and create constraints.\n\t\t */\n\t\tforboth_ptr(shardInterval, shardIntervalList,\n\t\t\t\t\tworkerPlacementNode, workersForPlacementList)\n\t\t{\n\t\t\tList *shardForeignConstraintCommandList = NIL;\n\t\t\tList *referenceTableForeignConstraintList = NIL;\n\n\t\t\tCopyShardForeignConstraintCommandListGrouped(\n\t\t\t\tshardInterval,\n\t\t\t\t&shardForeignConstraintCommandList,\n\t\t\t\t&referenceTableForeignConstraintList);\n\n\t\t\tList *constraintCommandList = NIL;\n\t\t\tconstraintCommandList = list_concat(constraintCommandList,\n\t\t\t\t\t\t\t\t\t\t\t\tshardForeignConstraintCommandList);\n\t\t\tconstraintCommandList = list_concat(constraintCommandList,\n\t\t\t\t\t\t\t\t\t\t\t\treferenceTableForeignConstraintList);\n\n\t\t\tchar *constraintCommand = NULL;\n\t\t\tforeach_declared_ptr(constraintCommand, constraintCommandList)\n\t\t\t{\n\t\t\t\tSendCommandToWorker(\n\t\t\t\t\tworkerPlacementNode->workerName,\n\t\t\t\t\tworkerPlacementNode->workerPort,\n\t\t\t\t\tconstraintCommand);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * DropShardListMetadata drops shard metadata from both the coordinator and\n * mx nodes.\n */\nstatic void\nDropShardListMetadata(List *shardIntervalList)\n{\n\tListCell *shardIntervalCell = NULL;\n\n\tforeach(shardIntervalCell, shardIntervalList)\n\t{\n\t\tShardInterval *shardInterval = (ShardInterval *) lfirst(shardIntervalCell);\n\t\tListCell *shardPlacementCell = NULL;\n\t\tOid relationId = shardInterval->relationId;\n\t\tuint64 oldShardId = shardInterval->shardId;\n\n\t\t/* delete metadata from synced nodes */\n\t\tif (ShouldSyncTableMetadata(relationId))\n\t\t{\n\t\t\tListCell *commandCell = NULL;\n\n\t\t\t/* send the commands one by one (calls citus_internal.delete_shard_metadata internally) */\n\t\t\tList *shardMetadataDeleteCommandList = ShardDeleteCommandList(shardInterval);\n\t\t\tforeach(commandCell, shardMetadataDeleteCommandList)\n\t\t\t{\n\t\t\t\tchar *command = (char *) lfirst(commandCell);\n\t\t\t\tSendCommandToWorkersWithMetadata(command);\n\t\t\t}\n\t\t}\n\n\t\t/* delete shard placements */\n\t\tList *shardPlacementList = ActiveShardPlacementList(oldShardId);\n\t\tforeach(shardPlacementCell, shardPlacementList)\n\t\t{\n\t\t\tShardPlacement *placement = (ShardPlacement *) lfirst(shardPlacementCell);\n\t\t\tDeleteShardPlacementRow(placement->placementId);\n\t\t}\n\n\t\t/* delete shard row */\n\t\tDeleteShardRow(oldShardId);\n\t}\n}\n\n\n/*\n * AcquireNonblockingSplitLock does not allow concurrent nonblocking splits, because we share memory and\n * replication slots.\n */\nstatic void\nAcquireNonblockingSplitLock(Oid relationId)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = true;\n\n\tSET_LOCKTAG_CITUS_OPERATION(tag, CITUS_NONBLOCKING_SPLIT);\n\n\tLockAcquireResult lockAcquired = LockAcquire(&tag, ExclusiveLock, sessionLock,\n\t\t\t\t\t\t\t\t\t\t\t\t dontWait);\n\tif (!lockAcquired)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not acquire the lock required to split \"\n\t\t\t\t\t\t\t   \"concurrently %s.\", generate_qualified_relation_name(\n\t\t\t\t\t\t\t\t   relationId)),\n\t\t\t\t\t\terrdetail(\"It means that either a concurrent shard move \"\n\t\t\t\t\t\t\t\t  \"or distributed table creation is happening.\"),\n\t\t\t\t\t\terrhint(\"Make sure that the concurrent operation has \"\n\t\t\t\t\t\t\t\t\"finished and re-run the command\")));\n\t}\n}\n\n\n/*\n * SplitShard API to split a given shard (or shard group) in non-blocking fashion\n * based on specified split points to a set of destination nodes.\n * splitOperation                   : Customer operation that triggered split.\n * splitWorkflowId                  : Number used to identify split workflow in names.\n * sourceColocatedShardIntervalList : Source shard group to be split.\n * shardSplitPointsList             : Split Points list for the source 'shardInterval'.\n * workersForPlacementList          : Placement list corresponding to split children.\n * distributionColumnList           : Maps relation IDs to distribution columns.\n *                                    If not specified, the distribution column is read\n *                                    from the metadata.\n * targetColocationId               : Specifies the colocation ID (only used for\n *                                    create_distributed_table_concurrently).\n */\nvoid\nNonBlockingShardSplit(SplitOperation splitOperation,\n\t\t\t\t\t  uint64 splitWorkflowId,\n\t\t\t\t\t  List *sourceColocatedShardIntervalList,\n\t\t\t\t\t  List *shardSplitPointsList,\n\t\t\t\t\t  List *workersForPlacementList,\n\t\t\t\t\t  DistributionColumnMap *distributionColumnOverrides,\n\t\t\t\t\t  uint32 targetColocationId)\n{\n\tconst char *operationName = SplitOperationAPIName[splitOperation];\n\n\tErrorIfMultipleNonblockingMoveSplitInTheSameTransaction();\n\n\tchar *superUser = CitusExtensionOwnerName();\n\tchar *databaseName = get_database_name(MyDatabaseId);\n\n\t/* First create shard interval metadata for split children */\n\tList *shardGroupSplitIntervalListList = CreateSplitIntervalsForShardGroup(\n\t\tsourceColocatedShardIntervalList,\n\t\tshardSplitPointsList);\n\n\tShardInterval *firstShard = linitial(sourceColocatedShardIntervalList);\n\n\t/* Acquire global lock to prevent concurrent nonblocking splits */\n\tAcquireNonblockingSplitLock(firstShard->relationId);\n\n\tWorkerNode *sourceShardToCopyNode =\n\t\tActiveShardPlacementWorkerNode(firstShard->shardId);\n\n\t/* Create hashmap to group shards for publication-subscription management */\n\tHTAB *publicationInfoHash = CreateShardSplitInfoMapForPublication(\n\t\tsourceColocatedShardIntervalList,\n\t\tshardGroupSplitIntervalListList,\n\t\tworkersForPlacementList);\n\n\tint connectionFlags = FORCE_NEW_CONNECTION;\n\tMultiConnection *sourceConnection = GetNodeUserDatabaseConnection(\n\t\tconnectionFlags,\n\t\tsourceShardToCopyNode->workerName,\n\t\tsourceShardToCopyNode->workerPort,\n\t\tsuperUser,\n\t\tdatabaseName);\n\tClaimConnectionExclusively(sourceConnection);\n\n\tMultiConnection *sourceReplicationConnection =\n\t\tGetReplicationConnection(sourceShardToCopyNode->workerName,\n\t\t\t\t\t\t\t\t sourceShardToCopyNode->workerPort);\n\n\t/* Non-Blocking shard split workflow starts here */\n\n\tereport(LOG, (errmsg(\"creating child shards for %s\",\n\t\t\t\t\t\t operationName)));\n\n\t/* 1) Physically create split children. */\n\tCreateSplitShardsForShardGroup(shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t   workersForPlacementList);\n\n\t/*\n\t * 2) Create dummy shards due to PG logical replication constraints.\n\t *    Refer to the comment section of 'CreateDummyShardsForShardGroup' for indepth\n\t *    information.\n\t */\n\tHTAB *mapOfPlacementToDummyShardList = CreateSimpleHash(NodeAndOwner,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tGroupedShardSplitInfos);\n\tCreateDummyShardsForShardGroup(\n\t\tmapOfPlacementToDummyShardList,\n\t\tsourceColocatedShardIntervalList,\n\t\tshardGroupSplitIntervalListList,\n\t\tsourceShardToCopyNode,\n\t\tworkersForPlacementList);\n\n\t/*\n\t * 3) Create replica identities on dummy shards. This needs to be done\n\t * before the subscriptions are created. Otherwise the subscription\n\t * creation will get stuck waiting for the publication to send a\n\t * replica identity. Since we never actually write data into these\n\t * dummy shards there's no point in creating these indexes after the\n\t * initial COPY phase, like we do for the replica identities on the\n\t * target shards.\n\t */\n\tCreateReplicaIdentitiesForDummyShards(mapOfPlacementToDummyShardList);\n\n\tereport(LOG, (errmsg(\n\t\t\t\t\t  \"creating replication artifacts (publications, replication slots, subscriptions for %s\",\n\t\t\t\t\t  operationName)));\n\n\t/* 4) Create Publications. */\n\tCreatePublications(sourceConnection, publicationInfoHash);\n\n\t/* 5) Execute 'worker_split_shard_replication_setup UDF */\n\tList *replicationSlotInfoList = ExecuteSplitShardReplicationSetupUDF(\n\t\tsourceShardToCopyNode,\n\t\tsourceColocatedShardIntervalList,\n\t\tshardGroupSplitIntervalListList,\n\t\tworkersForPlacementList,\n\t\tdistributionColumnOverrides);\n\n\t/*\n\t * Subscriber flow starts from here.\n\t * Populate 'ShardSplitSubscriberMetadata' for subscription management.\n\t */\n\tList *logicalRepTargetList =\n\t\tPopulateShardSplitSubscriptionsMetadataList(\n\t\t\tpublicationInfoHash, replicationSlotInfoList,\n\t\t\tshardGroupSplitIntervalListList, workersForPlacementList);\n\n\tHTAB *groupedLogicalRepTargetsHash = CreateGroupedLogicalRepTargetsHash(\n\t\tlogicalRepTargetList);\n\n\t/* Create connections to the target nodes */\n\tCreateGroupedLogicalRepTargetsConnections(\n\t\tgroupedLogicalRepTargetsHash,\n\t\tsuperUser, databaseName);\n\n\tchar *logicalRepDecoderPlugin = \"citus\";\n\n\t/*\n\t * 6) Create replication slots and keep track of their snapshot.\n\t */\n\tchar *snapshot = CreateReplicationSlots(\n\t\tsourceConnection,\n\t\tsourceReplicationConnection,\n\t\tlogicalRepTargetList,\n\t\tlogicalRepDecoderPlugin);\n\n\t/*\n\t * 7) Create subscriptions. This isn't strictly needed yet at this\n\t * stage, but this way we error out quickly if it fails.\n\t */\n\tCreateSubscriptions(\n\t\tsourceConnection,\n\t\tdatabaseName,\n\t\tlogicalRepTargetList);\n\n\t/*\n\t * We have to create the primary key (or any other replica identity)\n\t * before the update/delete operations that are queued will be\n\t * replicated. Because if the replica identity does not exist on the\n\t * target, the replication would fail.\n\t *\n\t * So the latest possible moment we could do this is right after the\n\t * initial data COPY, but before enabling the susbcriptions. It might\n\t * seem like a good idea to it after the initial data COPY, since\n\t * it's generally the rule that it's cheaper to build an index at once\n\t * than to create it incrementally. This general rule, is why we create\n\t * all the regular indexes as late during the move as possible.\n\t *\n\t * But as it turns out in practice it's not as clear cut, and we saw a\n\t * speed degradation in the time it takes to move shards when doing the\n\t * replica identity creation after the initial COPY. So, instead we\n\t * keep it before the COPY.\n\t */\n\tCreateReplicaIdentities(logicalRepTargetList);\n\n\tereport(LOG, (errmsg(\"performing copy for %s\", operationName)));\n\n\t/* 8) Do snapshotted Copy */\n\tDoSplitCopy(sourceShardToCopyNode, sourceColocatedShardIntervalList,\n\t\t\t\tshardGroupSplitIntervalListList, workersForPlacementList,\n\t\t\t\tsnapshot, distributionColumnOverrides);\n\n\tereport(LOG, (errmsg(\"replicating changes for %s\", operationName)));\n\n\t/*\n\t * 9) Logically replicate all the changes and do most of the table DDL,\n\t * like index and foreign key creation.\n\t */\n\tbool skipInterShardRelationshipCreation = false;\n\n\tCompleteNonBlockingShardTransfer(sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t\t\t sourceConnection,\n\t\t\t\t\t\t\t\t\t publicationInfoHash,\n\t\t\t\t\t\t\t\t\t logicalRepTargetList,\n\t\t\t\t\t\t\t\t\t groupedLogicalRepTargetsHash,\n\t\t\t\t\t\t\t\t\t SHARD_SPLIT,\n\t\t\t\t\t\t\t\t\t skipInterShardRelationshipCreation);\n\n\t/*\n\t * 10) Delete old shards metadata and mark the shards as to be deferred drop.\n\t * Have to do that before creating the new shard metadata,\n\t * because there's cross-checks preventing inconsistent metadata\n\t * (like overlapping shards).\n\t */\n\tereport(LOG, (errmsg(\"marking deferred cleanup of source shard(s) for %s\",\n\t\t\t\t\t\t operationName)));\n\n\tInsertDeferredDropCleanupRecordsForShards(sourceColocatedShardIntervalList);\n\n\tDropShardListMetadata(sourceColocatedShardIntervalList);\n\n\t/*\n\t * 11) In case of create_distributed_table_concurrently, which converts\n\t * a Citus local table to a distributed table, update the distributed\n\t * table metadata now.\n\t *\n\t * We would rather have this be outside of the scope of NonBlockingShardSplit,\n\t * but we cannot make metadata changes before replication slot creation, and\n\t * we cannot create the replication slot before creating new shards and\n\t * corresponding publications, because the decoder uses a catalog snapshot\n\t * from the time of the slot creation, which means it would not be able to see\n\t * the shards or publications when replication starts if it was created before.\n\t *\n\t * We also cannot easily move metadata changes to be after this function,\n\t * because CreateForeignKeyConstraints relies on accurate metadata and\n\t * we also want to perform the clean-up logic in PG_CATCH in case of\n\t * failure.\n\t *\n\t * Hence, this appears to be the only suitable spot for updating\n\t * pg_dist_partition and pg_dist_colocation.\n\t */\n\tif (splitOperation == CREATE_DISTRIBUTED_TABLE)\n\t{\n\t\t/* we currently only use split for hash-distributed tables */\n\t\tchar distributionMethod = DISTRIBUTE_BY_HASH;\n\t\tint shardCount = list_length(shardSplitPointsList) + 1;\n\n\t\tUpdateDistributionColumnsForShardGroup(sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t   distributionColumnOverrides,\n\t\t\t\t\t\t\t\t\t\t\t   distributionMethod,\n\t\t\t\t\t\t\t\t\t\t\t   shardCount,\n\t\t\t\t\t\t\t\t\t\t\t   targetColocationId);\n\t}\n\n\t/* 12) Insert new shard and placement metdata */\n\tInsertSplitChildrenShardMetadata(shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t workersForPlacementList);\n\n\t/* 13) create partitioning hierarchy, if any, this needs to be done\n\t * after the metadata is correct, because it fails for some\n\t * uninvestigated reason otherwise.\n\t */\n\tCreatePartitioningHierarchy(logicalRepTargetList);\n\n\tereport(LOG, (errmsg(\"creating foreign key constraints (if any) for %s\",\n\t\t\t\t\t\t operationName)));\n\n\t/*\n\t * 14) Create foreign keys if exists after the metadata changes happening in\n\t * InsertSplitChildrenShardMetadata() because the foreign\n\t * key creation depends on the new metadata.\n\t */\n\tCreateUncheckedForeignKeyConstraints(logicalRepTargetList);\n\n\t/*\n\t * 15) Release shared memory allocated by worker_split_shard_replication_setup udf\n\t * at source node.\n\t */\n\tExecuteSplitShardReleaseSharedMemory(sourceConnection);\n\n\t/* 16) Close source connection */\n\tCloseConnection(sourceConnection);\n\n\t/* 17) Close all subscriber connections */\n\tCloseGroupedLogicalRepTargetsConnections(groupedLogicalRepTargetsHash);\n\n\t/* 18) Close connection of template replication slot */\n\tCloseConnection(sourceReplicationConnection);\n}\n\n\n/*\n * Given we are using PG logical replication infrastructure there are some constraints\n * that need to met around matching table names in source and target nodes:\n * The restrictions in context of split are:\n * Constraint 1: Dummy source shard(s) from shard group must exist on all destination nodes.\n * Constraint 2: Dummy target shards from shard group must exist on source node.\n * Example :\n * Shard1[1-200] is co-located with Shard2[1-200] in Worker0.\n * We are splitting 2-way to worker0 (same node) and worker1 (different node).\n *\n * Non-Dummy shards (expected from Split):\n * In Worker0 --> Shard1_1 and Shard2_1.\n * In Worker1 --> Shard1_2 and Shard2_2.\n *\n * Dummy shards:\n * From constraint 1, we need to create: Dummy Shard1 and Shard2 in Worker0. Dummy Shard1 and Shard2 in Worker1\n * Note 1 : Given there is an overlap of source and destination in Worker0, Shard1 and Shard2 need not be created.\n * Be very careful here, dropping Shard1, Shard2 with customer data to create dummy Shard1, Shard2 on worker0 is catastrophic.\n *\n * From constraint 2, we need to create: Dummy Shard1_1, Shard2_1, Shard1_2 and Shard2_2 in Worker0.\n * Note 2 : Given there is an overlap of source and destination in Worker0, Shard1_1 and Shard2_1 need not be created.\n */\nstatic void\nCreateDummyShardsForShardGroup(HTAB *mapOfPlacementToDummyShardList,\n\t\t\t\t\t\t\t   List *sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t   List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t   WorkerNode *sourceWorkerNode,\n\t\t\t\t\t\t\t   List *workersForPlacementList)\n{\n\t/*\n\t * Statisfy Constraint 1: Create dummy source shard(s) on all destination nodes.\n\t * If source node is also in desintation, skip dummy shard creation(see Note 1 from function description).\n\t * We are guarenteed to have a single active placement for source shard. This is enforced earlier by ErrorIfCannotSplitShardExtended.\n\t */\n\n\t/* List 'workersForPlacementList' can have duplicates. We need all unique destination nodes. */\n\tHTAB *workersForPlacementSet = CreateWorkerForPlacementSet(workersForPlacementList);\n\n\tHASH_SEQ_STATUS status;\n\thash_seq_init(&status, workersForPlacementSet);\n\tWorkerNode *workerPlacementNode = NULL;\n\twhile ((workerPlacementNode = (WorkerNode *) hash_seq_search(&status)) != NULL)\n\t{\n\t\tif (workerPlacementNode->nodeId == sourceWorkerNode->nodeId)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tShardInterval *shardInterval = NULL;\n\t\tforeach_declared_ptr(shardInterval, sourceColocatedShardIntervalList)\n\t\t{\n\t\t\t/* Populate list of commands necessary to create shard interval on destination */\n\t\t\tList *splitShardCreationCommandList = GetPreLoadTableCreationCommands(\n\t\t\t\tshardInterval->relationId,\n\t\t\t\tfalse, /* includeSequenceDefaults */\n\t\t\t\tfalse, /* includeIdentityDefaults */\n\t\t\t\tNULL /* auto add columnar options for cstore tables */);\n\t\t\tsplitShardCreationCommandList = WorkerApplyShardDDLCommandList(\n\t\t\t\tsplitShardCreationCommandList,\n\t\t\t\tshardInterval->shardId);\n\n\t\t\t/* Log resource for cleanup in case of failure only.\n\t\t\t * Before we log a record, do a best effort check to see if a shard with same name exists.\n\t\t\t * This is because, it will cause shard creation to fail and we will end up cleaning the\n\t\t\t * old shard. We don't want that.\n\t\t\t */\n\t\t\tbool relationExists = CheckIfRelationWithSameNameExists(shardInterval,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerPlacementNode);\n\n\t\t\tif (relationExists)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE),\n\t\t\t\t\t\t\t\terrmsg(\"relation %s already exists on worker %s:%d\",\n\t\t\t\t\t\t\t\t\t   ConstructQualifiedShardName(shardInterval),\n\t\t\t\t\t\t\t\t\t   workerPlacementNode->workerName,\n\t\t\t\t\t\t\t\t\t   workerPlacementNode->workerPort)));\n\t\t\t}\n\n\t\t\t/* Log shard in pg_dist_cleanup. Given dummy shards are transient resources,\n\t\t\t * we want to cleanup irrespective of operation success or failure.\n\t\t\t */\n\t\t\tInsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,\n\t\t\t\t\t\t\t\t\t\t\t\t  ConstructQualifiedShardName(\n\t\t\t\t\t\t\t\t\t\t\t\t\t  shardInterval),\n\t\t\t\t\t\t\t\t\t\t\t\t  workerPlacementNode->groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t  CLEANUP_ALWAYS);\n\n\t\t\t/* Create dummy source shard on the specified placement list */\n\t\t\tCreateObjectOnPlacement(splitShardCreationCommandList,\n\t\t\t\t\t\t\t\t\tworkerPlacementNode);\n\n\t\t\t/* Add dummy source shard entry created for placement node in map */\n\t\t\tAddDummyShardEntryInMap(mapOfPlacementToDummyShardList,\n\t\t\t\t\t\t\t\t\tworkerPlacementNode->nodeId,\n\t\t\t\t\t\t\t\t\tshardInterval);\n\t\t}\n\t}\n\n\t/*\n\t * Statisfy Constraint 2: Create dummy target shards from shard group on source node.\n\t * If the target shard was created on source node as placement, skip it (See Note 2 from function description).\n\t */\n\tList *shardIntervalList = NULL;\n\tforeach_declared_ptr(shardIntervalList, shardGroupSplitIntervalListList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tworkerPlacementNode = NULL;\n\t\tforboth_ptr(shardInterval, shardIntervalList, workerPlacementNode,\n\t\t\t\t\tworkersForPlacementList)\n\t\t{\n\t\t\tif (workerPlacementNode->nodeId == sourceWorkerNode->nodeId)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tList *splitShardCreationCommandList = GetPreLoadTableCreationCommands(\n\t\t\t\tshardInterval->relationId,\n\t\t\t\tfalse, /* includeSequenceDefaults */\n\t\t\t\tfalse, /* includeIdentityDefaults */\n\t\t\t\tNULL /* auto add columnar options for cstore tables */);\n\t\t\tsplitShardCreationCommandList = WorkerApplyShardDDLCommandList(\n\t\t\t\tsplitShardCreationCommandList,\n\t\t\t\tshardInterval->shardId);\n\n\t\t\t/* Log resource for cleanup in case of failure only.\n\t\t\t * Before we log a record, do a best effort check to see if a shard with same name exists.\n\t\t\t * This is because, it will cause shard creation to fail and we will end up cleaning the\n\t\t\t * old shard. We don't want that.\n\t\t\t */\n\t\t\tbool relationExists = CheckIfRelationWithSameNameExists(shardInterval,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsourceWorkerNode);\n\n\t\t\tif (relationExists)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE),\n\t\t\t\t\t\t\t\terrmsg(\"relation %s already exists on worker %s:%d\",\n\t\t\t\t\t\t\t\t\t   ConstructQualifiedShardName(shardInterval),\n\t\t\t\t\t\t\t\t\t   sourceWorkerNode->workerName,\n\t\t\t\t\t\t\t\t\t   sourceWorkerNode->workerPort)));\n\t\t\t}\n\n\t\t\t/* Log shard in pg_dist_cleanup. Given dummy shards are transient resources,\n\t\t\t * we want to cleanup irrespective of operation success or failure.\n\t\t\t */\n\t\t\tInsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,\n\t\t\t\t\t\t\t\t\t\t\t\t  ConstructQualifiedShardName(\n\t\t\t\t\t\t\t\t\t\t\t\t\t  shardInterval),\n\t\t\t\t\t\t\t\t\t\t\t\t  sourceWorkerNode->groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t  CLEANUP_ALWAYS);\n\n\t\t\t/* Create dummy split child shard on source worker node */\n\t\t\tCreateObjectOnPlacement(splitShardCreationCommandList, sourceWorkerNode);\n\n\t\t\t/* Add dummy split child shard entry created on source node */\n\t\t\tAddDummyShardEntryInMap(mapOfPlacementToDummyShardList,\n\t\t\t\t\t\t\t\t\tsourceWorkerNode->nodeId,\n\t\t\t\t\t\t\t\t\tshardInterval);\n\t\t}\n\t}\n}\n\n\n/*\n * CreateWorkerForPlacementSet returns a set with unique worker nodes.\n */\nstatic HTAB *\nCreateWorkerForPlacementSet(List *workersForPlacementList)\n{\n\tHASHCTL info = { 0 };\n\tinfo.keysize = sizeof(WorkerNode);\n\tinfo.hash = WorkerNodeHashCode;\n\tinfo.match = WorkerNodeCompare;\n\n\t/* we don't have value field as it's a set */\n\tinfo.entrysize = info.keysize;\n\n\tuint32 hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT | HASH_COMPARE);\n\n\tHTAB *workerForPlacementSet = hash_create(\"worker placement set\", 32, &info,\n\t\t\t\t\t\t\t\t\t\t\t  hashFlags);\n\n\tWorkerNode *workerForPlacement = NULL;\n\tforeach_declared_ptr(workerForPlacement, workersForPlacementList)\n\t{\n\t\tvoid *hashKey = (void *) workerForPlacement;\n\t\thash_search(workerForPlacementSet, hashKey, HASH_ENTER, NULL);\n\t}\n\n\treturn workerForPlacementSet;\n}\n\n\n/*\n * ExecuteSplitShardReplicationSetupUDF executes\n * 'worker_split_shard_replication_setup' UDF on source shard node\n * and returns list of ReplicationSlotInfo.\n */\nstatic List *\nExecuteSplitShardReplicationSetupUDF(WorkerNode *sourceWorkerNode,\n\t\t\t\t\t\t\t\t\t List *sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t\t\t List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t List *destinationWorkerNodesList,\n\t\t\t\t\t\t\t\t\t DistributionColumnMap *distributionColumnOverrides)\n{\n\tStringInfo splitShardReplicationUDF = CreateSplitShardReplicationSetupUDF(\n\t\tsourceColocatedShardIntervalList,\n\t\tshardGroupSplitIntervalListList,\n\t\tdestinationWorkerNodesList,\n\t\tdistributionColumnOverrides);\n\n\t/* Force a new connection to execute the UDF */\n\tint connectionFlags = 0;\n\tMultiConnection *sourceConnection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  sourceWorkerNode->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  sourceWorkerNode->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  workerPort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  CitusExtensionOwnerName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  get_database_name(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  MyDatabaseId));\n\tClaimConnectionExclusively(sourceConnection);\n\n\tPGresult *result = NULL;\n\tint queryResult = ExecuteOptionalRemoteCommand(sourceConnection,\n\t\t\t\t\t\t\t\t\t\t\t\t   splitShardReplicationUDF->data,\n\t\t\t\t\t\t\t\t\t\t\t\t   &result);\n\n\t/*\n\t * Result should contain atleast one tuple. The information returned is\n\t * set of tuples where each tuple is formatted as:\n\t * <targetNodeId, tableOwnerName, replication_slot_name>.\n\t */\n\tif (queryResult != RESPONSE_OKAY || !IsResponseOK(result) || PQntuples(result) < 1 ||\n\t\tPQnfields(result) != 3)\n\t{\n\t\tPQclear(result);\n\t\tForgetResults(sourceConnection);\n\t\tCloseConnection(sourceConnection);\n\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Failed to run worker_split_shard_replication_setup UDF. \"\n\t\t\t\t\t\t\t\"It should successfully execute for splitting a shard in \"\n\t\t\t\t\t\t\t\"a non-blocking way. Please retry.\")));\n\t}\n\n\t/* Get replication slot information */\n\tList *replicationSlotInfoList = ParseReplicationSlotInfoFromResult(result);\n\n\tPQclear(result);\n\tForgetResults(sourceConnection);\n\n\tCloseConnection(sourceConnection);\n\treturn replicationSlotInfoList;\n}\n\n\n/*\n * ExecuteSplitShardReleaseSharedMemory releases dynamic shared memory\n * at source node.\n * As a part of non-blocking split workflow, worker_split_shard_replication_setup allocates\n * shared memory to store split information. This has to be released after split completes(or fails).\n */\nstatic void\nExecuteSplitShardReleaseSharedMemory(MultiConnection *sourceConnection)\n{\n\tStringInfo splitShardReleaseMemoryUDF = makeStringInfo();\n\tappendStringInfo(splitShardReleaseMemoryUDF,\n\t\t\t\t\t \"SELECT pg_catalog.worker_split_shard_release_dsm();\");\n\tExecuteCriticalRemoteCommand(sourceConnection, splitShardReleaseMemoryUDF->data);\n}\n\n\n/*\n * CreateSplitShardReplicationSetupUDF creates and returns\n * parameterized 'worker_split_shard_replication_setup' UDF command.\n *\n * 'sourceShardSplitIntervalList'    : Source shard interval to split.\n * 'shardGroupSplitIntervalListList' : List of shard intervals for split children..\n * 'destinationWorkerNodesList'      : List of workers for split children placement.\n *\n * For example consider below input values:\n * sourceColocatedShardIntervalList : [sourceShardInterval]\n * shardGroupSplitIntervalListList  : [<childFirstShardInterval, childSecondShardInterval>]\n * destinationWorkerNodesList       : [worker1, worker2]\n *\n * SELECT * FROM worker_split_shard_replication_setup(\n *  Array[\n *      ROW(sourceShardId, childFirstShardId, childFirstMinRange, childFirstMaxRange, worker1)::citus.split_shard_info,\n *      ROW(sourceShardId, childSecondShardId, childSecondMinRange, childSecondMaxRange, worker2)::citus.split_shard_info\n *  ], CurrentOperationId);\n */\nStringInfo\nCreateSplitShardReplicationSetupUDF(List *sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t\t\tList *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\tList *destinationWorkerNodesList,\n\t\t\t\t\t\t\t\t\tDistributionColumnMap *distributionColumnOverrides)\n{\n\tStringInfo splitChildrenRows = makeStringInfo();\n\n\tShardInterval *sourceShardIntervalToCopy = NULL;\n\tList *splitChildShardIntervalList = NULL;\n\tbool addComma = false;\n\tforboth_ptr(sourceShardIntervalToCopy, sourceColocatedShardIntervalList,\n\t\t\t\tsplitChildShardIntervalList, shardGroupSplitIntervalListList)\n\t{\n\t\tint64 sourceShardId = sourceShardIntervalToCopy->shardId;\n\t\tOid relationId = sourceShardIntervalToCopy->relationId;\n\n\t\tVar *distributionColumn =\n\t\t\tGetDistributionColumnWithOverrides(relationId,\n\t\t\t\t\t\t\t\t\t\t\t   distributionColumnOverrides);\n\n\t\tbool missingOK = false;\n\t\tchar *distributionColumnName =\n\t\t\tget_attname(relationId, distributionColumn->varattno,\n\t\t\t\t\t\tmissingOK);\n\n\t\tShardInterval *splitChildShardInterval = NULL;\n\t\tWorkerNode *destinationWorkerNode = NULL;\n\t\tforboth_ptr(splitChildShardInterval, splitChildShardIntervalList,\n\t\t\t\t\tdestinationWorkerNode, destinationWorkerNodesList)\n\t\t{\n\t\t\tif (addComma)\n\t\t\t{\n\t\t\t\tappendStringInfo(splitChildrenRows, \",\");\n\t\t\t}\n\n\t\t\tStringInfo minValueString = makeStringInfo();\n\t\t\tappendStringInfo(minValueString, \"%d\", DatumGetInt32(\n\t\t\t\t\t\t\t\t splitChildShardInterval->minValue));\n\n\t\t\tStringInfo maxValueString = makeStringInfo();\n\t\t\tappendStringInfo(maxValueString, \"%d\", DatumGetInt32(\n\t\t\t\t\t\t\t\t splitChildShardInterval->maxValue));\n\n\t\t\tappendStringInfo(splitChildrenRows,\n\t\t\t\t\t\t\t \"ROW(%lu, %s, %lu, %s, %s, %u)::pg_catalog.split_shard_info\",\n\t\t\t\t\t\t\t sourceShardId,\n\t\t\t\t\t\t\t quote_literal_cstr(distributionColumnName),\n\t\t\t\t\t\t\t splitChildShardInterval->shardId,\n\t\t\t\t\t\t\t quote_literal_cstr(minValueString->data),\n\t\t\t\t\t\t\t quote_literal_cstr(maxValueString->data),\n\t\t\t\t\t\t\t destinationWorkerNode->nodeId);\n\n\t\t\taddComma = true;\n\t\t}\n\t}\n\n\tStringInfo splitShardReplicationUDF = makeStringInfo();\n\tappendStringInfo(splitShardReplicationUDF,\n\t\t\t\t\t \"SELECT * FROM pg_catalog.worker_split_shard_replication_setup(\"\n\t\t\t\t\t \"ARRAY[%s], %lu);\",\n\t\t\t\t\t splitChildrenRows->data,\n\t\t\t\t\t CurrentOperationId);\n\n\treturn splitShardReplicationUDF;\n}\n\n\n/*\n * ParseReplicationSlotInfoFromResult parses custom datatype 'replication_slot_info'.\n * 'replication_slot_info' is a tuple with below format:\n * <targetNodeId, tableOwnerName, replicationSlotName>\n */\nstatic List *\nParseReplicationSlotInfoFromResult(PGresult *result)\n{\n\tint64 rowCount = PQntuples(result);\n\n\tList *replicationSlotInfoList = NIL;\n\tfor (int64 rowIndex = 0; rowIndex < rowCount; rowIndex++)\n\t{\n\t\tReplicationSlotInfo *replicationSlot = (ReplicationSlotInfo *) palloc0(\n\t\t\tsizeof(ReplicationSlotInfo));\n\n\t\tchar *targeNodeIdString = PQgetvalue(result, rowIndex, 0 /* nodeId column*/);\n\n\t\treplicationSlot->targetNodeId = strtoul(targeNodeIdString, NULL, 10);\n\n\t\tbool missingOk = false;\n\t\treplicationSlot->tableOwnerId = get_role_oid(\n\t\t\tPQgetvalue(result, rowIndex, 1 /* table owner name column */),\n\t\t\tmissingOk);\n\n\t\t/* Replication slot name */\n\t\treplicationSlot->name = pstrdup(PQgetvalue(result, rowIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t   2 /* slot name column */));\n\n\t\treplicationSlotInfoList = lappend(replicationSlotInfoList, replicationSlot);\n\t}\n\n\treturn replicationSlotInfoList;\n}\n\n\n/*\n * AddDummyShardEntryInMap adds shard entry into hash map to keep track\n * of dummy shards that are created. These shards are cleanedup after split completes.\n *\n * This is a cautious measure to keep track of dummy shards created for constraints\n * of logical replication. We cautiously delete only the dummy shards added in the DummyShardHashMap.\n */\nstatic void\nAddDummyShardEntryInMap(HTAB *mapOfPlacementToDummyShardList, uint32 targetNodeId,\n\t\t\t\t\t\tShardInterval *shardInterval)\n{\n\tNodeAndOwner key;\n\tkey.nodeId = targetNodeId;\n\tkey.tableOwnerId = TableOwnerOid(shardInterval->relationId);\n\n\tbool found = false;\n\tGroupedDummyShards *nodeMappingEntry =\n\t\t(GroupedDummyShards *) hash_search(mapOfPlacementToDummyShardList, &key,\n\t\t\t\t\t\t\t\t\t\t   HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t   &found);\n\tif (!found)\n\t{\n\t\tnodeMappingEntry->shardIntervals = NIL;\n\t}\n\n\tnodeMappingEntry->shardIntervals =\n\t\tlappend(nodeMappingEntry->shardIntervals, shardInterval);\n}\n\n\n/*\n * CreateReplicaIdentitiesForDummyShards creates replica indentities for split\n * dummy shards.\n */\nstatic void\nCreateReplicaIdentitiesForDummyShards(HTAB *mapOfDummyShardToPlacement)\n{\n\t/* Create Replica Identities for dummy shards */\n\tHASH_SEQ_STATUS status;\n\thash_seq_init(&status, mapOfDummyShardToPlacement);\n\n\tGroupedDummyShards *entry = NULL;\n\twhile ((entry = (GroupedDummyShards *) hash_seq_search(&status)) != NULL)\n\t{\n\t\tuint32 nodeId = entry->key.nodeId;\n\t\tWorkerNode *shardToBeDroppedNode = FindNodeWithNodeId(nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  false /* missingOk */);\n\n\t\tList *dummyShardIntervalList = entry->shardIntervals;\n\t\tCreateReplicaIdentitiesOnNode(dummyShardIntervalList,\n\t\t\t\t\t\t\t\t\t  shardToBeDroppedNode->workerName,\n\t\t\t\t\t\t\t\t\t  shardToBeDroppedNode->workerPort);\n\t}\n}\n\n\n/*\n * GetNextShardIdForSplitChild returns shard id to be used for split child.\n * The function connects to the local node through a new connection and gets the next\n * sequence. This prevents self deadlock when 'CREATE_REPLICATION_SLOT' is executed\n * as a part of nonblocking split workflow.\n */\nstatic uint64\nGetNextShardIdForSplitChild()\n{\n\tuint64 shardId = 0;\n\n\t/*\n\t * In regression tests, we would like to generate shard IDs consistently\n\t * even if the tests run in parallel. Instead of the sequence, we can use\n\t * the next_shard_id GUC to specify which shard ID the current session should\n\t * generate next. The GUC is automatically increased by 1 every time a new\n\t * shard ID is generated.\n\t */\n\tif (NextShardId > 0)\n\t{\n\t\tshardId = NextShardId;\n\t\tNextShardId += 1;\n\n\t\treturn shardId;\n\t}\n\n\tStringInfo nextValueCommand = makeStringInfo();\n\tappendStringInfo(nextValueCommand, \"SELECT nextval(%s);\", quote_literal_cstr(\n\t\t\t\t\t\t \"pg_catalog.pg_dist_shardid_seq\"));\n\n\tMultiConnection *connection = GetConnectionForLocalQueriesOutsideTransaction(\n\t\tCitusExtensionOwnerName());\n\tPGresult *result = NULL;\n\tint queryResult = ExecuteOptionalRemoteCommand(connection, nextValueCommand->data,\n\t\t\t\t\t\t\t\t\t\t\t\t   &result);\n\tif (queryResult != RESPONSE_OKAY || !IsResponseOK(result) || PQntuples(result) != 1 ||\n\t\tPQnfields(result) != 1)\n\t{\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\t\tCloseConnection(connection);\n\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"Could not generate next shard id while executing shard splits.\")));\n\t}\n\n\tshardId = SafeStringToUint64(PQgetvalue(result, 0, 0 /* nodeId column*/));\n\tPQclear(result);\n\tForgetResults(connection);\n\n\treturn shardId;\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/shard_transfer.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_transfer.c\n *\n * This file contains functions to transfer shards between nodes.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <string.h>\n#include <sys/statvfs.h>\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"miscadmin.h\"\n\n#include \"access/htup_details.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_enum.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/pg_list.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/lock.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/errcodes.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/palloc.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/backend_data.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_logical_replication.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_progress.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/shard_rebalancer.h\"\n#include \"distributed/shard_split.h\"\n#include \"distributed/shard_transfer.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_transaction.h\"\n\n/* local type declarations */\n\n/*\n * ShardInterval along with to be executed\n * DDL command list.\n */\ntypedef struct ShardCommandList\n{\n\tShardInterval *shardInterval;\n\tList *ddlCommandList;\n} ShardCommandList;\n\nstatic const char *ShardTransferTypeNames[] = {\n\t[SHARD_TRANSFER_INVALID_FIRST] = \"unknown\",\n\t[SHARD_TRANSFER_MOVE] = \"move\",\n\t[SHARD_TRANSFER_COPY] = \"copy\",\n};\n\nstatic const char *ShardTransferTypeNamesCapitalized[] = {\n\t[SHARD_TRANSFER_INVALID_FIRST] = \"unknown\",\n\t[SHARD_TRANSFER_MOVE] = \"Move\",\n\t[SHARD_TRANSFER_COPY] = \"Copy\",\n};\n\nstatic const char *ShardTransferTypeNamesContinuous[] = {\n\t[SHARD_TRANSFER_INVALID_FIRST] = \"unknown\",\n\t[SHARD_TRANSFER_MOVE] = \"Moving\",\n\t[SHARD_TRANSFER_COPY] = \"Copying\",\n};\n\nstatic const char *ShardTransferTypeFunctionNames[] = {\n\t[SHARD_TRANSFER_INVALID_FIRST] = \"unknown\",\n\t[SHARD_TRANSFER_MOVE] = \"citus_move_shard_placement\",\n\t[SHARD_TRANSFER_COPY] = \"citus_copy_shard_placement\",\n};\n\n/* local function forward declarations */\nstatic bool CanUseLogicalReplication(Oid relationId, char shardReplicationMode);\nstatic void ErrorIfTableCannotBeReplicated(Oid relationId);\nstatic void ErrorIfTargetNodeIsNotSafeForTransfer(const char *targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t  int targetNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t  ShardTransferType transferType);\nstatic void ErrorIfSameNode(char *sourceNodeName, int sourceNodePort,\n\t\t\t\t\t\t\tchar *targetNodeName, int targetNodePort,\n\t\t\t\t\t\t\tconst char *operationName);\nstatic void CopyShardTables(List *shardIntervalList, char *sourceNodeName,\n\t\t\t\t\t\t\tint32 sourceNodePort, char *targetNodeName,\n\t\t\t\t\t\t\tint32 targetNodePort, bool useLogicalReplication,\n\t\t\t\t\t\t\tconst char *operationName, uint32 optionFlags);\nstatic void CopyShardTablesViaLogicalReplication(List *shardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\t char *sourceNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t int32 sourceNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t char *targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t int32 targetNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t uint32 optionFlags);\n\nstatic void CopyShardTablesViaBlockWrites(List *shardIntervalList, char *sourceNodeName,\n\t\t\t\t\t\t\t\t\t\t  int32 sourceNodePort,\n\t\t\t\t\t\t\t\t\t\t  char *targetNodeName, int32 targetNodePort,\n\t\t\t\t\t\t\t\t\t\t  uint32 optionFlags);\nstatic void EnsureShardCanBeCopied(int64 shardId, const char *sourceNodeName,\n\t\t\t\t\t\t\t\t   int32 sourceNodePort, const char *targetNodeName,\n\t\t\t\t\t\t\t\t   int32 targetNodePort);\nstatic List * RecreateTableDDLCommandList(Oid relationId);\nstatic void EnsureTableListOwner(List *tableIdList);\nstatic void ErrorIfReplicatingDistributedTableWithFKeys(List *tableIdList);\n\nstatic void DropShardPlacementsFromMetadata(List *shardList,\n\t\t\t\t\t\t\t\t\t\t\tchar *nodeName,\n\t\t\t\t\t\t\t\t\t\t\tint32 nodePort);\nstatic void UpdateColocatedShardPlacementMetadataOnWorkers(int64 shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   char *sourceNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   int32 sourceNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   char *targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   int32 targetNodePort);\nstatic bool IsShardListOnNode(List *colocatedShardList, char *targetNodeName,\n\t\t\t\t\t\t\t  uint32 targetPort);\nstatic void SetupRebalanceMonitorForShardTransfer(uint64 shardId, Oid distributedTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t  char *sourceNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t  uint32 sourceNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t  char *targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t  uint32 targetNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t  ShardTransferType transferType);\nstatic void CheckSpaceConstraints(MultiConnection *connection,\n\t\t\t\t\t\t\t\t  uint64 colocationSizeInBytes);\nstatic void EnsureAllShardsCanBeCopied(List *colocatedShardList,\n\t\t\t\t\t\t\t\t\t   char *sourceNodeName, uint32 sourceNodePort,\n\t\t\t\t\t\t\t\t\t   char *targetNodeName, uint32 targetNodePort);\nstatic void EnsureEnoughDiskSpaceForShardMove(List *colocatedShardList,\n\t\t\t\t\t\t\t\t\t\t\t  char *sourceNodeName, uint32 sourceNodePort,\n\t\t\t\t\t\t\t\t\t\t\t  char *targetNodeName, uint32 targetNodePort,\n\t\t\t\t\t\t\t\t\t\t\t  ShardTransferType transferType);\nstatic bool TransferAlreadyCompleted(List *colocatedShardList,\n\t\t\t\t\t\t\t\t\t char *sourceNodeName, uint32 sourceNodePort,\n\t\t\t\t\t\t\t\t\t char *targetNodeName, uint32 targetNodePort,\n\t\t\t\t\t\t\t\t\t ShardTransferType transferType);\nstatic void LockColocatedRelationsForMove(List *colocatedTableList);\nstatic void ErrorIfForeignTableForShardTransfer(List *colocatedTableList,\n\t\t\t\t\t\t\t\t\t\t\t\tShardTransferType transferType);\nstatic List * RecreateShardDDLCommandList(ShardInterval *shardInterval,\n\t\t\t\t\t\t\t\t\t\t  const char *sourceNodeName,\n\t\t\t\t\t\t\t\t\t\t  int32 sourceNodePort);\nstatic List * PostLoadShardCreationCommandList(ShardInterval *shardInterval,\n\t\t\t\t\t\t\t\t\t\t\t   const char *sourceNodeName,\n\t\t\t\t\t\t\t\t\t\t\t   int32 sourceNodePort);\nstatic ShardCommandList * CreateShardCommandList(ShardInterval *shardInterval,\n\t\t\t\t\t\t\t\t\t\t\t\t List *ddlCommandList);\nstatic char * CreateShardCopyCommand(ShardInterval *shard, WorkerNode *targetNode);\nstatic void AcquireShardPlacementLock(uint64_t shardId, int lockMode, Oid relationId,\n\t\t\t\t\t\t\t\t\t  const char *operationName);\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(citus_copy_shard_placement);\nPG_FUNCTION_INFO_V1(citus_copy_shard_placement_with_nodeid);\nPG_FUNCTION_INFO_V1(master_copy_shard_placement);\nPG_FUNCTION_INFO_V1(citus_move_shard_placement);\nPG_FUNCTION_INFO_V1(citus_move_shard_placement_with_nodeid);\nPG_FUNCTION_INFO_V1(master_move_shard_placement);\nPG_FUNCTION_INFO_V1(citus_internal_copy_single_shard_placement);\ndouble DesiredPercentFreeAfterMove = 10;\nbool CheckAvailableSpaceBeforeMove = true;\n\n\n/*\n * citus_copy_shard_placement implements a user-facing UDF to copy a placement\n * from a source node to a target node, including all co-located placements.\n */\nDatum\ncitus_copy_shard_placement(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tint64 shardId = PG_GETARG_INT64(0);\n\ttext *sourceNodeNameText = PG_GETARG_TEXT_P(1);\n\tint32 sourceNodePort = PG_GETARG_INT32(2);\n\ttext *targetNodeNameText = PG_GETARG_TEXT_P(3);\n\tint32 targetNodePort = PG_GETARG_INT32(4);\n\tOid shardReplicationModeOid = PG_GETARG_OID(5);\n\n\tchar *sourceNodeName = text_to_cstring(sourceNodeNameText);\n\tchar *targetNodeName = text_to_cstring(targetNodeNameText);\n\n\tchar shardReplicationMode = LookupShardTransferMode(shardReplicationModeOid);\n\n\tTransferShards(shardId, sourceNodeName, sourceNodePort,\n\t\t\t\t   targetNodeName, targetNodePort,\n\t\t\t\t   shardReplicationMode, SHARD_TRANSFER_COPY, 0);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_copy_shard_placement_with_nodeid implements a user-facing UDF to copy a placement\n * from a source node to a target node, including all co-located placements.\n */\nDatum\ncitus_copy_shard_placement_with_nodeid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tint64 shardId = PG_GETARG_INT64(0);\n\tuint32 sourceNodeId = PG_GETARG_INT32(1);\n\tuint32 targetNodeId = PG_GETARG_INT32(2);\n\tOid shardReplicationModeOid = PG_GETARG_OID(3);\n\n\tbool missingOk = false;\n\tWorkerNode *sourceNode = FindNodeWithNodeId(sourceNodeId, missingOk);\n\tWorkerNode *targetNode = FindNodeWithNodeId(targetNodeId, missingOk);\n\n\tchar shardReplicationMode = LookupShardTransferMode(shardReplicationModeOid);\n\n\tTransferShards(shardId, sourceNode->workerName, sourceNode->workerPort,\n\t\t\t\t   targetNode->workerName, targetNode->workerPort,\n\t\t\t\t   shardReplicationMode, SHARD_TRANSFER_COPY, 0);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * master_copy_shard_placement is a wrapper function for old UDF name.\n */\nDatum\nmaster_copy_shard_placement(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tint64 shardId = PG_GETARG_INT64(0);\n\ttext *sourceNodeNameText = PG_GETARG_TEXT_P(1);\n\tint32 sourceNodePort = PG_GETARG_INT32(2);\n\ttext *targetNodeNameText = PG_GETARG_TEXT_P(3);\n\tint32 targetNodePort = PG_GETARG_INT32(4);\n\tbool doRepair = PG_GETARG_BOOL(5);\n\tOid shardReplicationModeOid = PG_GETARG_OID(6);\n\n\tchar *sourceNodeName = text_to_cstring(sourceNodeNameText);\n\tchar *targetNodeName = text_to_cstring(targetNodeNameText);\n\n\tchar shardReplicationMode = LookupShardTransferMode(shardReplicationModeOid);\n\n\tif (doRepair)\n\t{\n\t\tereport(WARNING, (errmsg(\"do_repair argument is deprecated\")));\n\t}\n\n\tTransferShards(shardId, sourceNodeName, sourceNodePort,\n\t\t\t\t   targetNodeName, targetNodePort,\n\t\t\t\t   shardReplicationMode, SHARD_TRANSFER_COPY, 0);\n\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_internal_copy_single_shard_placement is an internal function that\n * copies a single shard placement from a source node to a target node.\n * It has two main differences from citus_copy_shard_placement:\n * 1. it copies only a single shard placement, not all colocated shards\n * 2. It allows to defer the constraints creation and this same function\n *   can be used to create the constraints later.\n *\n * The primary use case for this function is to transfer the shards of\n * reference tables. Since all reference tables are colocated together,\n * and each reference table has only one shard, this function can be used\n * to transfer the shards of reference tables in parallel.\n * Furthermore, the reference tables could have relations with\n * other reference tables, so we need to ensure that their constraints\n * are also transferred after copying the shards to the target node.\n * For this reason, we allow the caller to defer the constraints creation.\n *\n * This function is not supposed to be called by the user directly.\n */\nDatum\ncitus_internal_copy_single_shard_placement(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tint64 shardId = PG_GETARG_INT64(0);\n\tuint32 sourceNodeId = PG_GETARG_INT32(1);\n\tuint32 targetNodeId = PG_GETARG_INT32(2);\n\tuint32 flags = PG_GETARG_INT32(3);\n\tOid shardReplicationModeOid = PG_GETARG_OID(4);\n\n\tbool missingOk = false;\n\tWorkerNode *sourceNode = FindNodeWithNodeId(sourceNodeId, missingOk);\n\tWorkerNode *targetNode = FindNodeWithNodeId(targetNodeId, missingOk);\n\n\tchar shardReplicationMode = LookupShardTransferMode(shardReplicationModeOid);\n\n\t/*\n\t * This is an internal function that is used by the rebalancer.\n\t * It is not supposed to be called by the user directly.\n\t */\n\tif (!IsRebalancerInternalBackend())\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"This is an internal Citus function that can only\"\n\t\t\t\t\t\t\t   \" be used by a rebalancer task\")));\n\t}\n\n\tTransferShards(shardId, sourceNode->workerName, sourceNode->workerPort,\n\t\t\t\t   targetNode->workerName, targetNode->workerPort,\n\t\t\t\t   shardReplicationMode, SHARD_TRANSFER_COPY, flags);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_move_shard_placement moves given shard (and its co-located shards) from one\n * node to the other node. To accomplish this it entirely recreates the table structure\n * before copying all data.\n *\n * After that, there are two different paths. First one is blocking shard move in the\n * sense that during shard move all modifications are paused to the shard. The second\n * one relies on logical replication meaning that the writes blocked only for a very\n * short duration almost only when the metadata is actually being updated.\n *\n * After successful move operation, shards in the source node gets deleted. If the move\n * fails at any point, this function throws an error, leaving the cluster without doing\n * any changes in source node or target node.\n */\nDatum\ncitus_move_shard_placement(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tList *referenceTableIdList = NIL;\n\n\tif (HasNodesWithMissingReferenceTables(&referenceTableIdList))\n\t{\n\t\tereport(ERROR, (errmsg(\"there are missing reference tables on some nodes\"),\n\t\t\t\t\t\terrhint(\"Copy reference tables first with \"\n\t\t\t\t\t\t\t\t\"replicate_reference_tables() or use \"\n\t\t\t\t\t\t\t\t\"citus_rebalance_start() that will do it automatically.\"\n\t\t\t\t\t\t\t\t)));\n\t}\n\n\tint64 shardId = PG_GETARG_INT64(0);\n\tchar *sourceNodeName = text_to_cstring(PG_GETARG_TEXT_P(1));\n\tint32 sourceNodePort = PG_GETARG_INT32(2);\n\tchar *targetNodeName = text_to_cstring(PG_GETARG_TEXT_P(3));\n\tint32 targetNodePort = PG_GETARG_INT32(4);\n\tOid shardReplicationModeOid = PG_GETARG_OID(5);\n\n\tchar shardReplicationMode = LookupShardTransferMode(shardReplicationModeOid);\n\tTransferShards(shardId, sourceNodeName, sourceNodePort,\n\t\t\t\t   targetNodeName, targetNodePort,\n\t\t\t\t   shardReplicationMode, SHARD_TRANSFER_MOVE, 0);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_move_shard_placement_with_nodeid does the same as citus_move_shard_placement,\n * but accepts node ids as parameters, instead of hostname and port.\n */\nDatum\ncitus_move_shard_placement_with_nodeid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tint64 shardId = PG_GETARG_INT64(0);\n\tuint32 sourceNodeId = PG_GETARG_INT32(1);\n\tuint32 targetNodeId = PG_GETARG_INT32(2);\n\tOid shardReplicationModeOid = PG_GETARG_OID(3);\n\n\tbool missingOk = false;\n\tWorkerNode *sourceNode = FindNodeWithNodeId(sourceNodeId, missingOk);\n\tWorkerNode *targetNode = FindNodeWithNodeId(targetNodeId, missingOk);\n\n\tchar shardReplicationMode = LookupShardTransferMode(shardReplicationModeOid);\n\tTransferShards(shardId, sourceNode->workerName,\n\t\t\t\t   sourceNode->workerPort, targetNode->workerName,\n\t\t\t\t   targetNode->workerPort, shardReplicationMode, SHARD_TRANSFER_MOVE, 0);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * AcquireShardPlacementLock tries to acquire a lock on the shardid\n * while moving/copying the shard placement. If this\n * is it not possible it fails instantly because this means\n * another move/copy on same shard is currently happening. */\nstatic void\nAcquireShardPlacementLock(uint64_t shardId, int lockMode, Oid relationId,\n\t\t\t\t\t\t  const char *operationName)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = true;\n\n\tSET_LOCKTAG_SHARD_MOVE(tag, shardId);\n\n\tLockAcquireResult lockAcquired = LockAcquire(&tag, lockMode, sessionLock, dontWait);\n\tif (!lockAcquired)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not acquire the lock required to %s %s\",\n\t\t\t\t\t\t\t   operationName,\n\t\t\t\t\t\t\t   generate_qualified_relation_name(relationId)),\n\t\t\t\t\t\terrdetail(\"It means that either a concurrent shard move \"\n\t\t\t\t\t\t\t\t  \"or colocated distributed table creation is \"\n\t\t\t\t\t\t\t\t  \"happening.\"),\n\t\t\t\t\t\terrhint(\"Make sure that the concurrent operation has \"\n\t\t\t\t\t\t\t\t\"finished and re-run the command\")));\n\t}\n}\n\n\n/*\n * TransferShards is responsible for handling shard transfers.\n *\n * The optionFlags parameter controls the transfer behavior:\n *\n * - By default, shard colocation groups are treated as a single unit. This works\n *   well for distributed tables, since they can contain multiple colocated shards\n *   on the same node, and shard transfers can still be parallelized at the group level.\n *\n * - Reference tables are different: every reference table belongs to the same\n *   colocation group but has only a single shard. To parallelize reference table\n *   transfers, we must bypass the colocation group. The\n *   SHARD_TRANSFER_SINGLE_SHARD_ONLY flag enables this behavior by transferring\n *   only the specific shardId passed into the function, ignoring colocated shards.\n *\n * - Reference tables may also define foreign key relationships with each other.\n *   Since we cannot create those relationships until all shards have been moved,\n *   the SHARD_TRANSFER_SKIP_CREATE_RELATIONSHIPS flag is used to defer their\n *   creation until shard transfer completes.\n *\n * - After shards are transferred, the SHARD_TRANSFER_CREATE_RELATIONSHIPS_ONLY\n *   flag is used to create the foreign key relationships for already-transferred\n *   reference tables.\n *\n * Currently, optionFlags are only used to customize reference table transfers.\n * For distributed tables, optionFlags should always be set to 0.\n * passing 0 as optionFlags means that the default behavior will be used for\n * all aspects of the shard transfer. That is to consider all colocated shards\n * as a single unit and return after creating the necessary relationships.\n */\nvoid\nTransferShards(int64 shardId, char *sourceNodeName,\n\t\t\t   int32 sourceNodePort, char *targetNodeName,\n\t\t\t   int32 targetNodePort, char shardReplicationMode,\n\t\t\t   ShardTransferType transferType, uint32 optionFlags)\n{\n\t/* strings to be used in log messages */\n\tconst char *operationName = ShardTransferTypeNames[transferType];\n\tconst char *operationNameCapitalized =\n\t\tShardTransferTypeNamesCapitalized[transferType];\n\tconst char *operationFunctionName = ShardTransferTypeFunctionNames[transferType];\n\n\t/* cannot transfer shard to the same node */\n\tErrorIfSameNode(sourceNodeName, sourceNodePort,\n\t\t\t\t\ttargetNodeName, targetNodePort,\n\t\t\t\t\toperationName);\n\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\tOid distributedTableId = shardInterval->relationId;\n\n\t/* error if unsupported shard transfer */\n\tif (transferType == SHARD_TRANSFER_MOVE)\n\t{\n\t\tErrorIfMoveUnsupportedTableType(distributedTableId);\n\t}\n\telse if (transferType == SHARD_TRANSFER_COPY)\n\t{\n\t\tErrorIfTableCannotBeReplicated(distributedTableId);\n\t\tEnsureNoModificationsHaveBeenDone();\n\t}\n\n\tErrorIfTargetNodeIsNotSafeForTransfer(targetNodeName, targetNodePort, transferType);\n\n\tAcquirePlacementColocationLock(distributedTableId, RowExclusiveLock, operationName);\n\n\tList *colocatedTableList;\n\tList *colocatedShardList;\n\n\t/*\n\t * If SHARD_TRANSFER_SINGLE_SHARD_ONLY is set, we only transfer a single shard\n\t * specified by shardId. Otherwise, we transfer all colocated shards.\n\t */\n\tbool isSingleShardOnly = optionFlags & SHARD_TRANSFER_SINGLE_SHARD_ONLY;\n\n\tif (isSingleShardOnly)\n\t{\n\t\tcolocatedTableList = list_make1_oid(distributedTableId);\n\t\tcolocatedShardList = list_make1(shardInterval);\n\t}\n\telse\n\t{\n\t\tcolocatedTableList = ColocatedTableList(distributedTableId);\n\t\tcolocatedShardList = ColocatedShardIntervalList(shardInterval);\n\t}\n\n\tEnsureTableListOwner(colocatedTableList);\n\n\tif (transferType == SHARD_TRANSFER_MOVE)\n\t{\n\t\t/*\n\t\t * Block concurrent DDL / TRUNCATE commands on the relation. while,\n\t\t * allow concurrent citus_move_shard_placement() on the shards of\n\t\t * the same relation.\n\t\t */\n\t\tLockColocatedRelationsForMove(colocatedTableList);\n\t}\n\n\tErrorIfForeignTableForShardTransfer(colocatedTableList, transferType);\n\n\tif (transferType == SHARD_TRANSFER_COPY)\n\t{\n\t\tErrorIfReplicatingDistributedTableWithFKeys(colocatedTableList);\n\t}\n\n\t/*\n\t * We sort shardIntervalList so that lock operations will not cause any\n\t * deadlocks. But we do not need to do that if the list contain only one\n\t * shard.\n\t */\n\tif (!isSingleShardOnly)\n\t{\n\t\tcolocatedShardList = SortList(colocatedShardList, CompareShardIntervalsById);\n\t}\n\n\t/* We have pretty much covered the concurrent rebalance operations\n\t * and we want to allow concurrent moves within the same colocation group.\n\t * but at the same time we want to block the concurrent moves on the same shard\n\t * placement. So we lock the shard moves before starting the transfer.\n\t */\n\tforeach_declared_ptr(shardInterval, colocatedShardList)\n\t{\n\t\tint64 shardIdToLock = shardInterval->shardId;\n\t\tAcquireShardPlacementLock(shardIdToLock, ExclusiveLock, distributedTableId,\n\t\t\t\t\t\t\t\t  operationName);\n\t}\n\n\tbool transferAlreadyCompleted = TransferAlreadyCompleted(colocatedShardList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t sourceNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t sourceNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t targetNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t transferType);\n\n\t/*\n\t * If we just need to create the shard relationships,We don't need to do anything\n\t * else other than calling CopyShardTables with SHARD_TRANSFER_CREATE_RELATIONSHIPS_ONLY\n\t * flag.\n\t */\n\tbool createRelationshipsOnly = optionFlags & SHARD_TRANSFER_CREATE_RELATIONSHIPS_ONLY;\n\n\tif (createRelationshipsOnly)\n\t{\n\t\tif (!transferAlreadyCompleted)\n\t\t{\n\t\t\t/*\n\t\t\t * if the transfer is not completed, and we are here just to create\n\t\t\t * the relationships, we can return right away\n\t\t\t */\n\t\t\tereport(WARNING, (errmsg(\"shard is not present on node %s:%d\",\n\t\t\t\t\t\t\t\t\t targetNodeName, targetNodePort),\n\t\t\t\t\t\t\t  errdetail(\"%s may have not completed.\",\n\t\t\t\t\t\t\t\t\t\toperationNameCapitalized)));\n\t\t\treturn;\n\t\t}\n\n\t\tCopyShardTables(colocatedShardList, sourceNodeName, sourceNodePort, targetNodeName\n\t\t\t\t\t\t,\n\t\t\t\t\t\ttargetNodePort, (shardReplicationMode ==\n\t\t\t\t\t\t\t\t\t\t TRANSFER_MODE_FORCE_LOGICAL),\n\t\t\t\t\t\toperationFunctionName, optionFlags);\n\n\t\t/* We don't need to do anything else, just return */\n\t\treturn;\n\t}\n\n\tif (transferAlreadyCompleted)\n\t{\n\t\t/* if the transfer is already completed, we can return right away */\n\t\tereport(WARNING, (errmsg(\"shard is already present on node %s:%d\",\n\t\t\t\t\t\t\t\t targetNodeName, targetNodePort),\n\t\t\t\t\t\t  errdetail(\"%s may have already completed.\",\n\t\t\t\t\t\t\t\t\toperationNameCapitalized)));\n\t\treturn;\n\t}\n\n\tEnsureAllShardsCanBeCopied(colocatedShardList, sourceNodeName, sourceNodePort,\n\t\t\t\t\t\t\t   targetNodeName, targetNodePort);\n\n\tif (shardReplicationMode == TRANSFER_MODE_AUTOMATIC)\n\t{\n\t\tVerifyTablesHaveReplicaIdentity(colocatedTableList);\n\t}\n\n\tEnsureEnoughDiskSpaceForShardMove(colocatedShardList,\n\t\t\t\t\t\t\t\t\t  sourceNodeName, sourceNodePort,\n\t\t\t\t\t\t\t\t\t  targetNodeName, targetNodePort, transferType);\n\n\tSetupRebalanceMonitorForShardTransfer(shardId, distributedTableId,\n\t\t\t\t\t\t\t\t\t\t  sourceNodeName, sourceNodePort,\n\t\t\t\t\t\t\t\t\t\t  targetNodeName, targetNodePort,\n\t\t\t\t\t\t\t\t\t\t  transferType);\n\n\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\tcolocatedShardList,\n\t\tsourceNodeName,\n\t\tsourceNodePort,\n\t\tPLACEMENT_UPDATE_STATUS_SETTING_UP);\n\n\t/*\n\t * At this point of the shard moves, we don't need to block the writes to\n\t * shards when logical replication is used.\n\t */\n\tbool useLogicalReplication = CanUseLogicalReplication(distributedTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardReplicationMode);\n\tif (!useLogicalReplication)\n\t{\n\t\tBlockWritesToShardList(colocatedShardList);\n\t}\n\telse if (transferType == SHARD_TRANSFER_MOVE)\n\t{\n\t\t/*\n\t\t * We prevent multiple shard moves in a transaction that use logical\n\t\t * replication. That's because the first call opens a transaction block\n\t\t * on the worker to drop the old shard placement and replication slot\n\t\t * creation waits for pending transactions to finish, which will not\n\t\t * happen ever. In other words, we prevent a self-deadlock if both\n\t\t * source shard placements are on the same node.\n\t\t */\n\t\tif (PlacementMovedUsingLogicalReplicationInTX)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"moving multiple shard placements via logical \"\n\t\t\t\t\t\t\t\t   \"replication in the same transaction is currently \"\n\t\t\t\t\t\t\t\t   \"not supported\"),\n\t\t\t\t\t\t\terrhint(\"If you wish to move multiple shard placements \"\n\t\t\t\t\t\t\t\t\t\"in a single transaction set the shard_transfer_mode \"\n\t\t\t\t\t\t\t\t\t\"to 'block_writes'.\")));\n\t\t}\n\n\t\tPlacementMovedUsingLogicalReplicationInTX = true;\n\t}\n\n\tif (transferType == SHARD_TRANSFER_COPY &&\n\t\t!IsCitusTableType(distributedTableId, REFERENCE_TABLE))\n\t{\n\t\t/*\n\t\t * When copying a shard to a new node, we should first ensure that reference\n\t\t * tables are present such that joins work immediately after copying the shard.\n\t\t * When copying a reference table, we are probably trying to achieve just that.\n\t\t *\n\t\t * Since this a long-running operation we do this after the error checks, but\n\t\t * before taking metadata locks.\n\t\t */\n\t\tEnsureReferenceTablesExistOnAllNodesExtended(shardReplicationMode);\n\t}\n\n\tDropOrphanedResourcesInSeparateTransaction();\n\n\tShardInterval *colocatedShard = NULL;\n\tforeach_declared_ptr(colocatedShard, colocatedShardList)\n\t{\n\t\t/*\n\t\t * This is to prevent any race condition possibility among the shard moves.\n\t\t * We don't allow the move to happen if the shard we are going to move has an\n\t\t * orphaned placement somewhere that is not cleanup up yet.\n\t\t */\n\t\tchar *qualifiedShardName = ConstructQualifiedShardName(colocatedShard);\n\t\tErrorIfCleanupRecordForShardExists(qualifiedShardName);\n\t}\n\n\tCopyShardTables(colocatedShardList, sourceNodeName, sourceNodePort, targetNodeName,\n\t\t\t\t\ttargetNodePort, useLogicalReplication, operationFunctionName,\n\t\t\t\t\toptionFlags);\n\n\tif (transferType == SHARD_TRANSFER_MOVE)\n\t{\n\t\t/* delete old shards metadata and mark the shards as to be deferred drop */\n\t\tint32 sourceGroupId = GroupForNode(sourceNodeName, sourceNodePort);\n\t\tInsertCleanupRecordsForShardPlacementsOnNode(colocatedShardList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t sourceGroupId);\n\t}\n\n\t/*\n\t * Finally insert the placements to pg_dist_placement and sync it to the\n\t * metadata workers.\n\t */\n\tcolocatedShard = NULL;\n\tforeach_declared_ptr(colocatedShard, colocatedShardList)\n\t{\n\t\tuint64 colocatedShardId = colocatedShard->shardId;\n\t\tuint32 groupId = GroupForNode(targetNodeName, targetNodePort);\n\t\tuint64 placementId = GetNextPlacementId();\n\n\t\tInsertShardPlacementRow(colocatedShardId, placementId,\n\t\t\t\t\t\t\t\tShardLength(colocatedShardId),\n\t\t\t\t\t\t\t\tgroupId);\n\n\t\tif (transferType == SHARD_TRANSFER_COPY &&\n\t\t\tShouldSyncTableMetadata(colocatedShard->relationId))\n\t\t{\n\t\t\tchar *placementCommand = PlacementUpsertCommand(colocatedShardId, placementId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t0, groupId);\n\n\t\t\tSendCommandToWorkersWithMetadata(placementCommand);\n\t\t}\n\t}\n\n\tif (transferType == SHARD_TRANSFER_MOVE)\n\t{\n\t\t/*\n\t\t * Since this is move operation, we remove the placements from the metadata\n\t\t * for the source node after copy.\n\t\t */\n\t\tDropShardPlacementsFromMetadata(colocatedShardList,\n\t\t\t\t\t\t\t\t\t\tsourceNodeName, sourceNodePort);\n\n\t\tUpdateColocatedShardPlacementMetadataOnWorkers(shardId, sourceNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   sourceNodePort, targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   targetNodePort);\n\t}\n\n\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\tcolocatedShardList,\n\t\tsourceNodeName,\n\t\tsourceNodePort,\n\t\tPLACEMENT_UPDATE_STATUS_COMPLETED);\n\n\tFinalizeCurrentProgressMonitor();\n}\n\n\n/*\n * AdjustShardsForPrimaryCloneNodeSplit is called when a primary-clone node split\n * occurs. It adjusts the shard placements between the primary and clone nodes based\n * on the provided shard lists. Since the clone is an exact replica of the primary\n * but the metadata is not aware of this replication, this function updates the\n * metadata to reflect the new shard distribution.\n *\n * The function handles three types of shards:\n *\n * 1. Shards moving to clone node (cloneShardList):\n *    - Updates shard placement metadata to move placements from primary to clone\n *    - No data movement is needed since the clone already has the data\n *    - Adds cleanup records to remove the shard data from primary at transaction commit\n *\n * 2. Shards staying on primary node (primaryShardList):\n *    - Metadata already correctly reflects these shards on primary\n *    - Adds cleanup records to remove the shard data from clone node\n *\n * 3. Reference tables:\n *    - Inserts new placement records on the clone node\n *    - Data is already present on clone, so only metadata update is needed\n *\n * This function does not perform any actual data movement; it only updates the\n * shard placement metadata and schedules cleanup operations for later execution.\n */\nvoid\nAdjustShardsForPrimaryCloneNodeSplit(WorkerNode *primaryNode,\n\t\t\t\t\t\t\t\t\t WorkerNode *cloneNode,\n\t\t\t\t\t\t\t\t\t List *primaryShardList,\n\t\t\t\t\t\t\t\t\t List *cloneShardList)\n{\n\t/* Input validation */\n\tif (primaryNode == NULL || cloneNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"primary or clone worker node is NULL\")));\n\t}\n\n\tif (primaryNode->nodeId == cloneNode->nodeId)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"primary and clone nodes must be different\")));\n\t}\n\n\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t \"adjusting shard placements for primary %s:%d and clone %s:%d\",\n\t\t\t\t\t\t primaryNode->workerName, primaryNode->workerPort,\n\t\t\t\t\t\t cloneNode->workerName, cloneNode->workerPort)));\n\n\tRegisterOperationNeedingCleanup();\n\n\t/*\n\t * Process shards that will stay on the primary node.\n\t * For these shards, we need to remove their data from the clone node\n\t * since the metadata already correctly reflects them on primary.\n\t */\n\tuint64 shardId = 0;\n\tuint32 primaryGroupId = GroupForNode(primaryNode->workerName, primaryNode->workerPort)\n\t;\n\tuint32 cloneGroupId = GroupForNode(cloneNode->workerName, cloneNode->workerPort);\n\n\tereport(NOTICE, (errmsg(\"processing %d shards for primary node GroupID %d\",\n\t\t\t\t\t\t\tlist_length(primaryShardList), primaryGroupId)));\n\n\n\t/*\n\t * For each shard staying on primary, insert cleanup records to remove\n\t * the shard data from the clone node. The metadata already correctly\n\t * reflects these shards on primary, so no metadata changes are needed.\n\t */\n\tforeach_declared_int(shardId, primaryShardList)\n\t{\n\t\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\t\tList *colocatedShardList = ColocatedShardIntervalList(shardInterval);\n\n\t\tchar *qualifiedShardName = ConstructQualifiedShardName(shardInterval);\n\t\tereport(LOG, (errmsg(\n\t\t\t\t\t\t  \"inserting DELETE shard record for shard %s from clone node GroupID %d\",\n\t\t\t\t\t\t  qualifiedShardName, cloneGroupId)));\n\n\t\tInsertCleanupRecordsForShardPlacementsOnNode(colocatedShardList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t cloneGroupId);\n\t}\n\n\n\t/*\n\t * Process shards that will move to the clone node.\n\t * For these shards, we need to:\n\t * 1. Update metadata to move placements from primary to clone\n\t * 2. Remove the shard data from primary (via cleanup records)\n\t * 3. No data movement needed since clone already has the data\n\t */\n\tereport(NOTICE, (errmsg(\"processing %d shards for clone node GroupID %d\", list_length(\n\t\t\t\t\t\t\t\tcloneShardList), cloneGroupId)));\n\n\tforeach_declared_int(shardId, cloneShardList)\n\t{\n\t\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\t\tList *colocatedShardList = ColocatedShardIntervalList(shardInterval);\n\n\t\t/*\n\t\t * Create new shard placement records on the clone node for all\n\t\t * colocated shards. This moves the shard placements from primary\n\t\t * to clone in the metadata.\n\t\t */\n\t\tforeach_declared_ptr(shardInterval, colocatedShardList)\n\t\t{\n\t\t\tuint64 colocatedShardId = shardInterval->shardId;\n\n\t\t\tuint64 placementId = GetNextPlacementId();\n\t\t\tInsertShardPlacementRow(colocatedShardId, placementId,\n\t\t\t\t\t\t\t\t\tShardLength(colocatedShardId),\n\t\t\t\t\t\t\t\t\tcloneGroupId);\n\t\t}\n\n\t\t/*\n\t\t * Update the metadata on worker nodes to reflect the new shard\n\t\t * placement distribution between primary and clone nodes.\n\t\t */\n\t\tUpdateColocatedShardPlacementMetadataOnWorkers(shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   primaryNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   primaryNode->workerPort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   cloneNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   cloneNode->workerPort);\n\n\t\t/*\n\t\t * Remove the shard placement records from primary node metadata\n\t\t * since these shards are now served from the clone node.\n\t\t */\n\t\tDropShardPlacementsFromMetadata(colocatedShardList,\n\t\t\t\t\t\t\t\t\t\tprimaryNode->workerName, primaryNode->workerPort);\n\n\t\tchar *qualifiedShardName = ConstructQualifiedShardName(shardInterval);\n\t\tereport(LOG, (errmsg(\n\t\t\t\t\t\t  \"inserting DELETE shard record for shard %s from primary node GroupID %d\",\n\t\t\t\t\t\t  qualifiedShardName, primaryGroupId)));\n\n\t\t/*\n\t\t * Insert cleanup records to remove the shard data from primary node\n\t\t * at transaction commit. This frees up space on the primary node\n\t\t * since the data is now served from the clone node.\n\t\t */\n\t\tInsertCleanupRecordsForShardPlacementsOnNode(colocatedShardList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t primaryGroupId);\n\t}\n\n\t/*\n\t * Handle reference tables - these need to be available on both\n\t * primary and clone nodes. Since the clone already has the data,\n\t * we just need to insert placement records for the clone node.\n\t */\n\tint colocationId = GetReferenceTableColocationId();\n\n\tif (colocationId == INVALID_COLOCATION_ID)\n\t{\n\t\t/* we have no reference table yet. */\n\t\treturn;\n\t}\n\tShardInterval *shardInterval = NULL;\n\tList *referenceTableIdList = CitusTableTypeIdList(REFERENCE_TABLE);\n\tOid referenceTableId = linitial_oid(referenceTableIdList);\n\tList *shardIntervalList = LoadShardIntervalList(referenceTableId);\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tList *colocatedShardList = ColocatedShardIntervalList(shardInterval);\n\t\tShardInterval *colocatedShardInterval = NULL;\n\n\t\t/*\n\t\t * For each reference table shard, create placement records on the\n\t\t * clone node. The data is already present on the clone, so we only\n\t\t * need to update the metadata to make the clone aware of these shards.\n\t\t */\n\t\tforeach_declared_ptr(colocatedShardInterval, colocatedShardList)\n\t\t{\n\t\t\tuint64 colocatedShardId = colocatedShardInterval->shardId;\n\n\t\t\t/*\n\t\t\t * Insert shard placement record for the clone node and\n\t\t\t * propagate the metadata change to worker nodes.\n\t\t\t */\n\t\t\tuint64 placementId = GetNextPlacementId();\n\t\t\tInsertShardPlacementRow(colocatedShardId, placementId,\n\t\t\t\t\t\t\t\t\tShardLength(colocatedShardId),\n\t\t\t\t\t\t\t\t\tcloneGroupId);\n\n\t\t\tchar *placementCommand = PlacementUpsertCommand(colocatedShardId, placementId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t0, cloneGroupId);\n\n\t\t\tSendCommandToWorkersWithMetadata(placementCommand);\n\t\t}\n\t}\n\n\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t \"shard placement adjustment complete for primary %s:%d and clone %s:%d\",\n\t\t\t\t\t\t primaryNode->workerName, primaryNode->workerPort,\n\t\t\t\t\t\t cloneNode->workerName, cloneNode->workerPort)));\n}\n\n\n/*\n * Insert deferred cleanup records.\n * The shards will be dropped by background cleaner later.\n */\nvoid\nInsertDeferredDropCleanupRecordsForShards(List *shardIntervalList)\n{\n\tListCell *shardIntervalCell = NULL;\n\n\tforeach(shardIntervalCell, shardIntervalList)\n\t{\n\t\tShardInterval *shardInterval = (ShardInterval *) lfirst(shardIntervalCell);\n\t\tListCell *shardPlacementCell = NULL;\n\t\tuint64 oldShardId = shardInterval->shardId;\n\n\t\t/* mark for deferred drop */\n\t\tList *shardPlacementList = ActiveShardPlacementList(oldShardId);\n\t\tforeach(shardPlacementCell, shardPlacementList)\n\t\t{\n\t\t\tShardPlacement *placement = (ShardPlacement *) lfirst(shardPlacementCell);\n\n\t\t\t/* get shard name */\n\t\t\tchar *qualifiedShardName = ConstructQualifiedShardName(shardInterval);\n\n\t\t\t/* Log shard in pg_dist_cleanup.\n\t\t\t * Parent shards are to be dropped only on sucess after split workflow is complete,\n\t\t\t * so mark the policy as 'CLEANUP_DEFERRED_ON_SUCCESS'.\n\t\t\t * We also log cleanup record in the current transaction. If the current transaction rolls back,\n\t\t\t * we do not generate a record at all.\n\t\t\t */\n\t\t\tInsertCleanupOnSuccessRecordInCurrentTransaction(\n\t\t\t\tCLEANUP_OBJECT_SHARD_PLACEMENT,\n\t\t\t\tqualifiedShardName,\n\t\t\t\tplacement->groupId);\n\t\t}\n\t}\n}\n\n\n/*\n * InsertCleanupRecordsForShardPlacementsOnNode inserts deferred cleanup records.\n * The shards will be dropped by background cleaner later.\n * This function does this only for the placements on the given node.\n */\nvoid\nInsertCleanupRecordsForShardPlacementsOnNode(List *shardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t int32 groupId)\n{\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\t/* get shard name */\n\t\tchar *qualifiedShardName = ConstructQualifiedShardName(shardInterval);\n\n\t\t/* Log shard in pg_dist_cleanup.\n\t\t * Parent shards are to be dropped only on sucess after split workflow is complete,\n\t\t * so mark the policy as 'CLEANUP_DEFERRED_ON_SUCCESS'.\n\t\t * We also log cleanup record in the current transaction. If the current transaction rolls back,\n\t\t * we do not generate a record at all.\n\t\t */\n\t\tInsertCleanupOnSuccessRecordInCurrentTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t qualifiedShardName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t groupId);\n\t}\n}\n\n\n/*\n * IsShardListOnNode determines whether a co-located shard list has\n * active placements on a given node.\n */\nstatic bool\nIsShardListOnNode(List *colocatedShardList, char *targetNodeName, uint32 targetNodePort)\n{\n\tWorkerNode *workerNode = FindWorkerNode(targetNodeName, targetNodePort);\n\tif (workerNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"Moving shards to a non-existing node is not supported\")));\n\t}\n\n\t/*\n\t * We exhaustively search all co-located shards\n\t */\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, colocatedShardList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\t\tList *placementList = ActiveShardPlacementListOnGroup(shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  workerNode->groupId);\n\t\tif (placementList == NIL)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * LockColocatedRelationsForMove takes a list of relations, locks all of them\n * using ShareLock\n */\nstatic void\nLockColocatedRelationsForMove(List *colocatedTableList)\n{\n\tOid colocatedTableId = InvalidOid;\n\tforeach_declared_oid(colocatedTableId, colocatedTableList)\n\t{\n\t\tLockRelationOid(colocatedTableId, RowExclusiveLock);\n\t}\n}\n\n\n/*\n * ErrorIfForeignTableForShardTransfer takes a list of relations, errors out if\n * there's a foreign table in the list.\n */\nstatic void\nErrorIfForeignTableForShardTransfer(List *colocatedTableList,\n\t\t\t\t\t\t\t\t\tShardTransferType transferType)\n{\n\tOid colocatedTableId = InvalidOid;\n\tforeach_declared_oid(colocatedTableId, colocatedTableList)\n\t{\n\t\tif (IsForeignTable(colocatedTableId))\n\t\t{\n\t\t\tchar *relationName = get_rel_name(colocatedTableId);\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot %s shard\",\n\t\t\t\t\t\t\t\t   ShardTransferTypeNames[transferType]),\n\t\t\t\t\t\t\terrdetail(\"Table %s is a foreign table. \"\n\t\t\t\t\t\t\t\t\t  \"%s shards backed by foreign tables is \"\n\t\t\t\t\t\t\t\t\t  \"not supported.\", relationName,\n\t\t\t\t\t\t\t\t\t  ShardTransferTypeNamesContinuous[transferType])));\n\t\t}\n\t}\n}\n\n\n/*\n * EnsureAllShardsCanBeCopied is a wrapper around EnsureShardCanBeCopied.\n */\nstatic void\nEnsureAllShardsCanBeCopied(List *colocatedShardList,\n\t\t\t\t\t\t   char *sourceNodeName, uint32 sourceNodePort,\n\t\t\t\t\t\t   char *targetNodeName, uint32 targetNodePort)\n{\n\tShardInterval *colocatedShard = NULL;\n\tforeach_declared_ptr(colocatedShard, colocatedShardList)\n\t{\n\t\tuint64 colocatedShardId = colocatedShard->shardId;\n\n\t\t/*\n\t\t * To transfer shard, there should be healthy placement in source node and no\n\t\t * placement in the target node.\n\t\t */\n\t\tEnsureShardCanBeCopied(colocatedShardId, sourceNodeName, sourceNodePort,\n\t\t\t\t\t\t\t   targetNodeName, targetNodePort);\n\t}\n}\n\n\n/*\n * EnsureEnoughDiskSpaceForShardMove checks that there is enough space for\n * shard moves of the given colocated shard list from source node to target node.\n * It tries to clean up old shard placements to ensure there is enough space.\n */\nstatic void\nEnsureEnoughDiskSpaceForShardMove(List *colocatedShardList,\n\t\t\t\t\t\t\t\t  char *sourceNodeName, uint32 sourceNodePort,\n\t\t\t\t\t\t\t\t  char *targetNodeName, uint32 targetNodePort,\n\t\t\t\t\t\t\t\t  ShardTransferType transferType)\n{\n\tif (!CheckAvailableSpaceBeforeMove || transferType != SHARD_TRANSFER_MOVE)\n\t{\n\t\treturn;\n\t}\n\tuint64 colocationSizeInBytes = ShardListSizeInBytes(colocatedShardList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsourceNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsourceNodePort);\n\n\tuint32 connectionFlag = 0;\n\tMultiConnection *connection = GetNodeConnection(connectionFlag, targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttargetNodePort);\n\tCheckSpaceConstraints(connection, colocationSizeInBytes);\n}\n\n\n/*\n * TransferAlreadyCompleted returns true if the given shard transfer is already done.\n * Returns false otherwise.\n */\nstatic bool\nTransferAlreadyCompleted(List *colocatedShardList,\n\t\t\t\t\t\t char *sourceNodeName, uint32 sourceNodePort,\n\t\t\t\t\t\t char *targetNodeName, uint32 targetNodePort,\n\t\t\t\t\t\t ShardTransferType transferType)\n{\n\tif (transferType == SHARD_TRANSFER_MOVE &&\n\t\tIsShardListOnNode(colocatedShardList, targetNodeName, targetNodePort) &&\n\t\t!IsShardListOnNode(colocatedShardList, sourceNodeName, sourceNodePort))\n\t{\n\t\treturn true;\n\t}\n\n\tif (transferType == SHARD_TRANSFER_COPY &&\n\t\tIsShardListOnNode(colocatedShardList, targetNodeName, targetNodePort) &&\n\t\tIsShardListOnNode(colocatedShardList, sourceNodeName, sourceNodePort))\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ShardListSizeInBytes returns the size in bytes of a set of shard tables.\n */\nuint64\nShardListSizeInBytes(List *shardList, char *workerNodeName, uint32\n\t\t\t\t\t workerNodePort)\n{\n\tuint32 connectionFlag = 0;\n\n\t/* we skip child tables of a partitioned table if this boolean variable is true */\n\tbool optimizePartitionCalculations = true;\n\n\t/* we're interested in whole table, not a particular index */\n\tOid indexId = InvalidOid;\n\n\tStringInfo tableSizeQuery = GenerateSizeQueryOnMultiplePlacements(shardList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  indexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  TOTAL_RELATION_SIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  optimizePartitionCalculations);\n\n\tMultiConnection *connection = GetNodeConnection(connectionFlag, workerNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tworkerNodePort);\n\tPGresult *result = NULL;\n\tint queryResult = ExecuteOptionalRemoteCommand(connection, tableSizeQuery->data,\n\t\t\t\t\t\t\t\t\t\t\t\t   &result);\n\n\tif (queryResult != RESPONSE_OKAY)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\"cannot get the size because of a connection error\")));\n\t}\n\n\tList *sizeList = ReadFirstColumnAsText(result);\n\tif (list_length(sizeList) != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"received wrong number of rows from worker, expected 1 received %d\",\n\t\t\t\t\t\t\tlist_length(sizeList))));\n\t}\n\n\tStringInfo totalSizeStringInfo = (StringInfo) linitial(sizeList);\n\tchar *totalSizeString = totalSizeStringInfo->data;\n\tuint64 totalSize = SafeStringToUint64(totalSizeString);\n\n\tPQclear(result);\n\tForgetResults(connection);\n\n\treturn totalSize;\n}\n\n\n/*\n * SetupRebalanceMonitorForShardTransfer prepares the parameters and\n * calls SetupRebalanceMonitor, unless the current transfer is a move\n * initiated by the rebalancer.\n * See comments on SetupRebalanceMonitor\n */\nstatic void\nSetupRebalanceMonitorForShardTransfer(uint64 shardId, Oid distributedTableId,\n\t\t\t\t\t\t\t\t\t  char *sourceNodeName, uint32 sourceNodePort,\n\t\t\t\t\t\t\t\t\t  char *targetNodeName, uint32 targetNodePort,\n\t\t\t\t\t\t\t\t\t  ShardTransferType transferType)\n{\n\tif (transferType == SHARD_TRANSFER_MOVE && IsRebalancerInternalBackend())\n\t{\n\t\t/*\n\t\t * We want to be able to track progress of shard moves using\n\t\t * get_rebalancer_progress. If this move is initiated by the rebalancer,\n\t\t * then the rebalancer call has already set up the shared memory that is\n\t\t * used to do that, so we should return here.\n\t\t * But if citus_move_shard_placement is called directly by the user\n\t\t * (or through any other mechanism), then the shared memory is not\n\t\t * set up yet. In that case we do it here.\n\t\t */\n\t\treturn;\n\t}\n\n\tWorkerNode *sourceNode = FindWorkerNode(sourceNodeName, sourceNodePort);\n\tWorkerNode *targetNode = FindWorkerNode(targetNodeName, targetNodePort);\n\n\tPlacementUpdateEvent *placementUpdateEvent = palloc0(\n\t\tsizeof(PlacementUpdateEvent));\n\tplacementUpdateEvent->updateType =\n\t\ttransferType == SHARD_TRANSFER_COPY ? PLACEMENT_UPDATE_COPY :\n\t\tPLACEMENT_UPDATE_MOVE;\n\tplacementUpdateEvent->shardId = shardId;\n\tplacementUpdateEvent->sourceNode = sourceNode;\n\tplacementUpdateEvent->targetNode = targetNode;\n\tSetupRebalanceMonitor(list_make1(placementUpdateEvent), distributedTableId,\n\t\t\t\t\t\t  REBALANCE_PROGRESS_MOVING,\n\t\t\t\t\t\t  PLACEMENT_UPDATE_STATUS_SETTING_UP);\n}\n\n\n/*\n * CheckSpaceConstraints checks there is enough space to place the colocation\n * on the node that the connection is connected to.\n */\nstatic void\nCheckSpaceConstraints(MultiConnection *connection, uint64 colocationSizeInBytes)\n{\n\tuint64 diskAvailableInBytes = 0;\n\tuint64 diskSizeInBytes = 0;\n\tbool success =\n\t\tGetNodeDiskSpaceStatsForConnection(connection, &diskAvailableInBytes,\n\t\t\t\t\t\t\t\t\t\t   &diskSizeInBytes);\n\tif (!success)\n\t{\n\t\tereport(ERROR, (errmsg(\"Could not fetch disk stats for node: %s-%d\",\n\t\t\t\t\t\t\t   connection->hostname, connection->port)));\n\t}\n\n\tuint64 diskAvailableInBytesAfterShardMove = 0;\n\tif (diskAvailableInBytes < colocationSizeInBytes)\n\t{\n\t\t/*\n\t\t * even though the space will be less than \"0\", we set it to 0 for convenience.\n\t\t */\n\t\tdiskAvailableInBytes = 0;\n\t}\n\telse\n\t{\n\t\tdiskAvailableInBytesAfterShardMove = diskAvailableInBytes - colocationSizeInBytes;\n\t}\n\tuint64 desiredNewDiskAvailableInBytes = diskSizeInBytes *\n\t\t\t\t\t\t\t\t\t\t\t(DesiredPercentFreeAfterMove / 100);\n\tif (diskAvailableInBytesAfterShardMove < desiredNewDiskAvailableInBytes)\n\t{\n\t\tereport(ERROR, (errmsg(\"not enough empty space on node if the shard is moved, \"\n\t\t\t\t\t\t\t   \"actual available space after move will be %ld bytes, \"\n\t\t\t\t\t\t\t   \"desired available space after move is %ld bytes, \"\n\t\t\t\t\t\t\t   \"estimated size increase on node after move is %ld bytes.\",\n\t\t\t\t\t\t\t   diskAvailableInBytesAfterShardMove,\n\t\t\t\t\t\t\t   desiredNewDiskAvailableInBytes, colocationSizeInBytes),\n\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\"consider lowering citus.desired_percent_disk_available_after_move.\")));\n\t}\n}\n\n\n/*\n * ErrorIfTargetNodeIsNotSafeForTransfer throws error if the target node is not\n * eligible for shard transfers.\n */\nstatic void\nErrorIfTargetNodeIsNotSafeForTransfer(const char *targetNodeName, int targetNodePort,\n\t\t\t\t\t\t\t\t\t  ShardTransferType transferType)\n{\n\tWorkerNode *workerNode = FindWorkerNode(targetNodeName, targetNodePort);\n\tif (workerNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"%s shards to a non-existing node is not supported\",\n\t\t\t\t\t\t\t   ShardTransferTypeNamesContinuous[transferType]),\n\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\"Add the target node via SELECT citus_add_node('%s', %d);\",\n\t\t\t\t\t\t\ttargetNodeName, targetNodePort)));\n\t}\n\n\tif (!workerNode->isActive)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"%s shards to a non-active node is not supported\",\n\t\t\t\t\t\t\t   ShardTransferTypeNamesContinuous[transferType]),\n\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\"Activate the target node via SELECT citus_activate_node('%s', %d);\",\n\t\t\t\t\t\t\ttargetNodeName, targetNodePort)));\n\t}\n\n\tif (transferType == SHARD_TRANSFER_MOVE && !workerNode->shouldHaveShards)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"Moving shards to a node that shouldn't have a shard is \"\n\t\t\t\t\t\t\t   \"not supported\"),\n\t\t\t\t\t\terrhint(\"Allow shards on the target node via \"\n\t\t\t\t\t\t\t\t\"SELECT * FROM citus_set_node_property('%s', %d, 'shouldhaveshards', true);\",\n\t\t\t\t\t\t\t\ttargetNodeName, targetNodePort)));\n\t}\n\n\tif (!NodeIsPrimary(workerNode))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"%s shards to a secondary (e.g., replica) node is \"\n\t\t\t\t\t\t\t   \"not supported\",\n\t\t\t\t\t\t\t   ShardTransferTypeNamesContinuous[transferType])));\n\t}\n}\n\n\n/*\n * ErrorIfSameNode throws an error if the two host:port combinations\n * are the same.\n */\nstatic void\nErrorIfSameNode(char *sourceNodeName, int sourceNodePort,\n\t\t\t\tchar *targetNodeName, int targetNodePort,\n\t\t\t\tconst char *operationName)\n{\n\tif (strncmp(sourceNodeName, targetNodeName, MAX_NODE_LENGTH) == 0 &&\n\t\tsourceNodePort == targetNodePort)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"cannot %s shard to the same node\",\n\t\t\t\t\t\t\t   operationName)));\n\t}\n}\n\n\n/*\n * master_move_shard_placement is a wrapper around citus_move_shard_placement.\n */\nDatum\nmaster_move_shard_placement(PG_FUNCTION_ARGS)\n{\n\treturn citus_move_shard_placement(fcinfo);\n}\n\n\n/*\n * ErrorIfMoveUnsupportedTableType is a helper function for rebalance_table_shards\n * and citus_move_shard_placement udf's to error out if relation with relationId\n * is not a distributed table.\n */\nvoid\nErrorIfMoveUnsupportedTableType(Oid relationId)\n{\n\tif (IsCitusTableType(relationId, DISTRIBUTED_TABLE))\n\t{\n\t\treturn;\n\t}\n\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\tif (!IsCitusTable(relationId))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"table %s is a regular postgres table, you can \"\n\t\t\t\t\t\t\t   \"only move shards of a citus table\",\n\t\t\t\t\t\t\t   qualifiedRelationName)));\n\t}\n\telse if (IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"table %s is a local table, moving shard of \"\n\t\t\t\t\t\t\t   \"a local table added to metadata is currently \"\n\t\t\t\t\t\t\t   \"not supported\", qualifiedRelationName)));\n\t}\n\telse if (IsCitusTableType(relationId, REFERENCE_TABLE))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"table %s is a reference table, moving shard of \"\n\t\t\t\t\t\t\t   \"a reference table is not supported\",\n\t\t\t\t\t\t\t   qualifiedRelationName)));\n\t}\n}\n\n\n/*\n * VerifyTablesHaveReplicaIdentity throws an error if any of the tables\n * do not have a replica identity, which is required for logical replication\n * to replicate UPDATE and DELETE commands.\n */\nvoid\nVerifyTablesHaveReplicaIdentity(List *colocatedTableList)\n{\n\tListCell *colocatedTableCell = NULL;\n\n\tforeach(colocatedTableCell, colocatedTableList)\n\t{\n\t\tOid colocatedTableId = lfirst_oid(colocatedTableCell);\n\n\t\tif (!RelationCanPublishAllModifications(colocatedTableId))\n\t\t{\n\t\t\tchar *colocatedRelationName = get_rel_name(colocatedTableId);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot use logical replication to transfer shards of \"\n\t\t\t\t\t\t\t\t   \"the relation %s since it doesn't have a REPLICA \"\n\t\t\t\t\t\t\t\t   \"IDENTITY or PRIMARY KEY\", colocatedRelationName),\n\t\t\t\t\t\t\terrdetail(\"UPDATE and DELETE commands on the shard will \"\n\t\t\t\t\t\t\t\t\t  \"error out during logical replication unless \"\n\t\t\t\t\t\t\t\t\t  \"there is a REPLICA IDENTITY or PRIMARY KEY.\"),\n\t\t\t\t\t\t\terrhint(\"If you wish to continue without a replica \"\n\t\t\t\t\t\t\t\t\t\"identity set the shard_transfer_mode to \"\n\t\t\t\t\t\t\t\t\t\"'force_logical' or 'block_writes'.\")));\n\t\t}\n\t}\n}\n\n\n/*\n * RelationCanPublishAllModifications returns true if the relation is safe to publish\n * all modification while being replicated via logical replication.\n */\nbool\nRelationCanPublishAllModifications(Oid relationId)\n{\n\tRelation relation = RelationIdGetRelation(relationId);\n\tbool canPublish = false;\n\n\tif (relation == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"could not open relation with OID %u\", relationId)));\n\t}\n\n\t/* if relation has replica identity we are always good */\n\tif (relation->rd_rel->relreplident == REPLICA_IDENTITY_FULL ||\n\t\tOidIsValid(RelationGetReplicaIndex(relation)))\n\t{\n\t\tcanPublish = true;\n\t}\n\n\t/* partitioned tables do not contain any data themselves, can always replicate */\n\tif (PartitionedTable(relationId))\n\t{\n\t\tcanPublish = true;\n\t}\n\n\tRelationClose(relation);\n\n\treturn canPublish;\n}\n\n\n/*\n * BlockWritesToShardList blocks writes to all shards in the given shard\n * list. The function assumes that all the shards in the list are colocated.\n */\nvoid\nBlockWritesToShardList(List *shardList)\n{\n\tShardInterval *shard = NULL;\n\tforeach_declared_ptr(shard, shardList)\n\t{\n\t\t/*\n\t\t * We need to lock the referenced reference table metadata to avoid\n\t\t * asynchronous shard copy in case of cascading DML operations.\n\t\t */\n\t\tLockReferencedReferenceShardDistributionMetadata(shard->shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t ExclusiveLock);\n\n\t\tLockShardDistributionMetadata(shard->shardId, ExclusiveLock);\n\t}\n\n\t/* following code relies on the list to have at least one shard */\n\tif (list_length(shardList) == 0)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * Since the function assumes that the input shards are colocated,\n\t * calculating shouldSyncMetadata for a single table is sufficient.\n\t */\n\tShardInterval *firstShardInterval = (ShardInterval *) linitial(shardList);\n\tOid firstDistributedTableId = firstShardInterval->relationId;\n\n\tbool shouldSyncMetadata = ShouldSyncTableMetadata(firstDistributedTableId);\n\tif (shouldSyncMetadata || !IsCoordinator())\n\t{\n\t\t/*\n\t\t * Even if users disable metadata sync, we cannot allow them not to\n\t\t * acquire the remote locks. Hence, we have !IsCoordinator() check.\n\t\t */\n\t\tLockShardListMetadataOnWorkers(ExclusiveLock, shardList);\n\t}\n}\n\n\n/*\n * CanUseLogicalReplication returns true if the given table can be logically replicated.\n */\nstatic bool\nCanUseLogicalReplication(Oid relationId, char shardReplicationMode)\n{\n\tif (shardReplicationMode == TRANSFER_MODE_BLOCK_WRITES)\n\t{\n\t\t/* user explicitly chose not to use logical replication */\n\t\treturn false;\n\t}\n\n\t/*\n\t * Logical replication doesn't support replicating foreign tables and views.\n\t */\n\tif (!RegularTable(relationId))\n\t{\n\t\tereport(LOG, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t  errmsg(\"Cannot use logical replication for \"\n\t\t\t\t\t\t\t \"shard move since the relation %s is not \"\n\t\t\t\t\t\t\t \"a regular relation\",\n\t\t\t\t\t\t\t get_rel_name(relationId))));\n\n\n\t\treturn false;\n\t}\n\n\t/* Logical replication doesn't support inherited tables */\n\tif (IsParentTable(relationId))\n\t{\n\t\tereport(LOG, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t  errmsg(\"Cannot use logical replication for \"\n\t\t\t\t\t\t\t \"shard move since the relation %s is an \"\n\t\t\t\t\t\t\t \"inherited relation\",\n\t\t\t\t\t\t\t get_rel_name(relationId))));\n\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ErrorIfTableCannotBeReplicated function errors out if the given table is not suitable\n * for its shard being replicated. There are 2 cases in which shard replication is not\n * allowed:\n *\n * 1) MX tables, since RF=1 is a must MX tables\n * 2) Reference tables, since the shard should already exist in all workers\n */\nstatic void\nErrorIfTableCannotBeReplicated(Oid relationId)\n{\n\t/*\n\t * Note that ShouldSyncTableMetadata() returns true for both MX tables\n\t * and reference tables.\n\t */\n\tbool shouldSyncMetadata = ShouldSyncTableMetadata(relationId);\n\tif (!shouldSyncMetadata)\n\t{\n\t\treturn;\n\t}\n\n\tCitusTableCacheEntry *tableEntry = GetCitusTableCacheEntry(relationId);\n\tchar *relationName = get_rel_name(relationId);\n\n\tif (IsCitusTableTypeCacheEntry(tableEntry, CITUS_LOCAL_TABLE))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t(errmsg(\"Table %s is a local table. Replicating \"\n\t\t\t\t\t\t\t\t\"shard of a local table added to metadata \"\n\t\t\t\t\t\t\t\t\"currently is not supported\",\n\t\t\t\t\t\t\t\tquote_literal_cstr(relationName)))));\n\t}\n\n\t/*\n\t * ShouldSyncTableMetadata() returns true also for reference table,\n\t * we don't want to error in that case since reference tables aren't\n\t * automatically replicated to active nodes with no shards, and\n\t * master_copy_shard_placement() can be used to create placements in\n\t * such nodes.\n\t */\n\tif (tableEntry->replicationModel == REPLICATION_MODEL_STREAMING)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t(errmsg(\"Table %s is streaming replicated. Shards \"\n\t\t\t\t\t\t\t\t\"of streaming replicated tables cannot \"\n\t\t\t\t\t\t\t\t\"be copied\", quote_literal_cstr(relationName)))));\n\t}\n}\n\n\n/*\n * LookupShardTransferMode maps the oids of citus.shard_transfer_mode enum\n * values to a char.\n */\nchar\nLookupShardTransferMode(Oid shardReplicationModeOid)\n{\n\tchar shardReplicationMode = 0;\n\n\tDatum enumLabelDatum = DirectFunctionCall1(enum_out, shardReplicationModeOid);\n\tchar *enumLabel = DatumGetCString(enumLabelDatum);\n\n\tif (strncmp(enumLabel, \"auto\", NAMEDATALEN) == 0)\n\t{\n\t\tshardReplicationMode = TRANSFER_MODE_AUTOMATIC;\n\t}\n\telse if (strncmp(enumLabel, \"force_logical\", NAMEDATALEN) == 0)\n\t{\n\t\tshardReplicationMode = TRANSFER_MODE_FORCE_LOGICAL;\n\t}\n\telse if (strncmp(enumLabel, \"block_writes\", NAMEDATALEN) == 0)\n\t{\n\t\tshardReplicationMode = TRANSFER_MODE_BLOCK_WRITES;\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"invalid label for enum: %s\", enumLabel)));\n\t}\n\n\treturn shardReplicationMode;\n}\n\n\n/*\n * EnsureTableListOwner ensures current user owns given tables. Superusers\n * are regarded as owners.\n */\nstatic void\nEnsureTableListOwner(List *tableIdList)\n{\n\tOid tableId = InvalidOid;\n\tforeach_declared_oid(tableId, tableIdList)\n\t{\n\t\tEnsureTableOwner(tableId);\n\t}\n}\n\n\n/*\n * ErrorIfReplicatingDistributedTableWithFKeys errors out if given tables are not\n * suitable for replication.\n */\nstatic void\nErrorIfReplicatingDistributedTableWithFKeys(List *tableIdList)\n{\n\tOid tableId = InvalidOid;\n\tforeach_declared_oid(tableId, tableIdList)\n\t{\n\t\tList *foreignConstraintCommandList =\n\t\t\tGetReferencingForeignConstaintCommands(tableId);\n\n\t\tif (foreignConstraintCommandList != NIL &&\n\t\t\tIsCitusTableType(tableId, DISTRIBUTED_TABLE))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot replicate shards with foreign keys\")));\n\t\t}\n\t}\n}\n\n\n/*\n * CopyShardTables copies a shard along with its co-located shards from a source\n * node to target node. It does not make any checks about state of the shards.\n * It is caller's responsibility to make those checks if they are necessary.\n */\nstatic void\nCopyShardTables(List *shardIntervalList, char *sourceNodeName, int32 sourceNodePort,\n\t\t\t\tchar *targetNodeName, int32 targetNodePort, bool useLogicalReplication,\n\t\t\t\tconst char *operationName, uint32 optionFlags)\n{\n\tif (list_length(shardIntervalList) < 1)\n\t{\n\t\treturn;\n\t}\n\n\t/* Start operation to prepare for generating cleanup records */\n\tRegisterOperationNeedingCleanup();\n\n\tbool createRelationshipsOnly = optionFlags & SHARD_TRANSFER_CREATE_RELATIONSHIPS_ONLY;\n\n\t/*\n\t * If we're just going to create relationships only always use\n\t * CopyShardTablesViaBlockWrites.\n\t */\n\tif (useLogicalReplication && !createRelationshipsOnly)\n\t{\n\t\tCopyShardTablesViaLogicalReplication(shardIntervalList, sourceNodeName,\n\t\t\t\t\t\t\t\t\t\t\t sourceNodePort, targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t targetNodePort, optionFlags);\n\t}\n\telse\n\t{\n\t\tCopyShardTablesViaBlockWrites(shardIntervalList, sourceNodeName, sourceNodePort,\n\t\t\t\t\t\t\t\t\t  targetNodeName, targetNodePort, optionFlags);\n\t}\n\n\t/*\n\t * Drop temporary objects that were marked as CLEANUP_ALWAYS.\n\t */\n\tFinalizeOperationNeedingCleanupOnSuccess(operationName);\n}\n\n\n/*\n * CopyShardTablesViaLogicalReplication copies a shard along with its co-located shards\n * from a source node to target node via logical replication.\n */\nstatic void\nCopyShardTablesViaLogicalReplication(List *shardIntervalList, char *sourceNodeName,\n\t\t\t\t\t\t\t\t\t int32 sourceNodePort, char *targetNodeName,\n\t\t\t\t\t\t\t\t\t int32 targetNodePort, uint32 optionFlags)\n{\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"CopyShardTablesViaLogicalReplication\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\t/*\n\t * Iterate through the colocated shards and create them on the\n\t * target node. We do not create the indexes yet.\n\t */\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tOid relationId = shardInterval->relationId;\n\t\tuint64 shardId = shardInterval->shardId;\n\t\tList *tableRecreationCommandList = RecreateTableDDLCommandList(relationId);\n\t\ttableRecreationCommandList =\n\t\t\tWorkerApplyShardDDLCommandList(tableRecreationCommandList, shardId);\n\n\t\tchar *tableOwner = TableOwner(shardInterval->relationId);\n\n\t\t/* drop the shard we created on the target, in case of failure */\n\t\tInsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,\n\t\t\t\t\t\t\t\t\t\t\t  ConstructQualifiedShardName(shardInterval),\n\t\t\t\t\t\t\t\t\t\t\t  GroupForNode(targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   targetNodePort),\n\t\t\t\t\t\t\t\t\t\t\t  CLEANUP_ON_FAILURE);\n\n\t\tSendCommandListToWorkerOutsideTransaction(targetNodeName, targetNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t  tableOwner,\n\t\t\t\t\t\t\t\t\t\t\t\t  tableRecreationCommandList);\n\n\t\tMemoryContextReset(localContext);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\n\tbool skipRelationshipCreation = (optionFlags &\n\t\t\t\t\t\t\t\t\t SHARD_TRANSFER_SKIP_CREATE_RELATIONSHIPS);\n\n\t/* data copy is done seperately when logical replication is used */\n\tLogicallyReplicateShards(shardIntervalList, sourceNodeName,\n\t\t\t\t\t\t\t sourceNodePort, targetNodeName, targetNodePort,\n\t\t\t\t\t\t\t skipRelationshipCreation);\n}\n\n\n/*\n * CreateShardCommandList creates a struct for shard interval\n * along with DDL commands to be executed.\n */\nstatic ShardCommandList *\nCreateShardCommandList(ShardInterval *shardInterval, List *ddlCommandList)\n{\n\tShardCommandList *shardCommandList = palloc0(\n\t\tsizeof(ShardCommandList));\n\tshardCommandList->shardInterval = shardInterval;\n\tshardCommandList->ddlCommandList = ddlCommandList;\n\n\treturn shardCommandList;\n}\n\n\n/*\n * CopyShardTablesViaBlockWrites copies a shard along with its co-located shards\n * from a source node to target node via COPY command. While the command is in\n * progress, the modifications on the source node is blocked.\n */\nstatic void\nCopyShardTablesViaBlockWrites(List *shardIntervalList, char *sourceNodeName,\n\t\t\t\t\t\t\t  int32 sourceNodePort, char *targetNodeName,\n\t\t\t\t\t\t\t  int32 targetNodePort, uint32 optionFlags)\n{\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"CopyShardTablesViaBlockWrites\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\tWorkerNode *sourceNode = FindWorkerNode(sourceNodeName, sourceNodePort);\n\tWorkerNode *targetNode = FindWorkerNode(targetNodeName, targetNodePort);\n\tShardInterval *shardInterval = NULL;\n\n\tbool createRelationshipsOnly = optionFlags & SHARD_TRANSFER_CREATE_RELATIONSHIPS_ONLY;\n\n\t/*\n\t * If we’re only asked to create the relationships, the shards are already\n\t * present and populated on the node. Skip the table‑setup and data‑loading\n\t * steps and proceed straight to creating the relationships.\n\t */\n\tif (!createRelationshipsOnly)\n\t{\n\t\t/* iterate through the colocated shards and copy each */\n\t\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t\t{\n\t\t\t/*\n\t\t\t * For each shard we first create the shard table in a separate\n\t\t\t * transaction and then we copy the data and create the indexes in a\n\t\t\t * second separate transaction. The reason we don't do both in a single\n\t\t\t * transaction is so we can see the size of the new shard growing\n\t\t\t * during the copy when we run get_rebalance_progress in another\n\t\t\t * session. If we wouldn't split these two phases up, then the table\n\t\t\t * wouldn't be visible in the session that get_rebalance_progress uses.\n\t\t\t * So get_rebalance_progress would always report its size as 0.\n\t\t\t */\n\t\t\tList *ddlCommandList = RecreateShardDDLCommandList(shardInterval,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   sourceNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   sourceNodePort);\n\t\t\tchar *tableOwner = TableOwner(shardInterval->relationId);\n\n\t\t\t/* drop the shard we created on the target, in case of failure */\n\t\t\tInsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,\n\t\t\t\t\t\t\t\t\t\t\t\t  ConstructQualifiedShardName(\n\t\t\t\t\t\t\t\t\t\t\t\t\t  shardInterval),\n\t\t\t\t\t\t\t\t\t\t\t\t  GroupForNode(targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   targetNodePort),\n\t\t\t\t\t\t\t\t\t\t\t\t  CLEANUP_ON_FAILURE);\n\n\t\t\tSendCommandListToWorkerOutsideTransaction(targetNodeName, targetNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  tableOwner, ddlCommandList);\n\t\t}\n\n\t\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\t\tshardIntervalList,\n\t\t\tsourceNodeName,\n\t\t\tsourceNodePort,\n\t\t\tPLACEMENT_UPDATE_STATUS_COPYING_DATA);\n\n\t\tConflictWithIsolationTestingBeforeCopy();\n\t\tCopyShardsToNode(sourceNode, targetNode, shardIntervalList, NULL);\n\t\tConflictWithIsolationTestingAfterCopy();\n\n\t\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\t\tshardIntervalList,\n\t\t\tsourceNodeName,\n\t\t\tsourceNodePort,\n\t\t\tPLACEMENT_UPDATE_STATUS_CREATING_CONSTRAINTS);\n\n\t\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t\t{\n\t\t\tList *ddlCommandList =\n\t\t\t\tPostLoadShardCreationCommandList(shardInterval, sourceNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t sourceNodePort);\n\t\t\tchar *tableOwner = TableOwner(shardInterval->relationId);\n\t\t\tSendCommandListToWorkerOutsideTransaction(targetNodeName, targetNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  tableOwner, ddlCommandList);\n\n\t\t\tMemoryContextReset(localContext);\n\t\t}\n\t}\n\n\t/*\n\t * Skip creating shard relationships if the caller has requested that they\n\t * not be created.\n\t */\n\tbool skipRelationshipCreation = (optionFlags &\n\t\t\t\t\t\t\t\t\t SHARD_TRANSFER_SKIP_CREATE_RELATIONSHIPS);\n\n\tif (!skipRelationshipCreation)\n\t{\n\t\t/*\n\t\t * Once all shards are copied, we can recreate relationships between shards.\n\t\t * Create DDL commands to Attach child tables to their parents in a partitioning hierarchy.\n\t\t */\n\t\tList *shardIntervalWithDDCommandsList = NIL;\n\t\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t\t{\n\t\t\tif (PartitionTable(shardInterval->relationId))\n\t\t\t{\n\t\t\t\tchar *attachPartitionCommand =\n\t\t\t\t\tGenerateAttachShardPartitionCommand(shardInterval);\n\n\t\t\t\tShardCommandList *shardCommandList = CreateShardCommandList(\n\t\t\t\t\tshardInterval,\n\t\t\t\t\tlist_make1(attachPartitionCommand));\n\t\t\t\tshardIntervalWithDDCommandsList = lappend(shardIntervalWithDDCommandsList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardCommandList);\n\t\t\t}\n\t\t}\n\n\t\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\t\tshardIntervalList,\n\t\t\tsourceNodeName,\n\t\t\tsourceNodePort,\n\t\t\tPLACEMENT_UPDATE_STATUS_CREATING_FOREIGN_KEYS);\n\n\t\t/*\n\t\t * Iterate through the colocated shards and create DDL commamnds\n\t\t * to create the foreign constraints.\n\t\t */\n\t\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t\t{\n\t\t\tList *shardForeignConstraintCommandList = NIL;\n\t\t\tList *referenceTableForeignConstraintList = NIL;\n\n\t\t\tCopyShardForeignConstraintCommandListGrouped(shardInterval,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t &\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardForeignConstraintCommandList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t &\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t referenceTableForeignConstraintList);\n\n\t\t\tShardCommandList *shardCommandList = CreateShardCommandList(\n\t\t\t\tshardInterval,\n\t\t\t\tlist_concat(shardForeignConstraintCommandList,\n\t\t\t\t\t\t\treferenceTableForeignConstraintList));\n\t\t\tshardIntervalWithDDCommandsList = lappend(shardIntervalWithDDCommandsList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  shardCommandList);\n\t\t}\n\n\t\t/* Now execute the Partitioning & Foreign constraints creation commads. */\n\t\tShardCommandList *shardCommandList = NULL;\n\t\tforeach_declared_ptr(shardCommandList, shardIntervalWithDDCommandsList)\n\t\t{\n\t\t\tchar *tableOwner = TableOwner(shardCommandList->shardInterval->relationId);\n\t\t\tSendCommandListToWorkerOutsideTransaction(targetNodeName, targetNodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  tableOwner,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  shardCommandList->ddlCommandList);\n\t\t}\n\n\t\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\t\tshardIntervalList,\n\t\t\tsourceNodeName,\n\t\t\tsourceNodePort,\n\t\t\tPLACEMENT_UPDATE_STATUS_COMPLETING);\n\t}\n\tMemoryContextReset(localContext);\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * CopyShardsToNode copies the list of shards from the source to the target.\n * When snapshotName is not NULL it will do the COPY using this snapshot name.\n */\nvoid\nCopyShardsToNode(WorkerNode *sourceNode, WorkerNode *targetNode, List *shardIntervalList,\n\t\t\t\t char *snapshotName)\n{\n\tint taskId = 0;\n\tList *copyTaskList = NIL;\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\t/*\n\t\t * Skip copying data for partitioned tables, because they contain no\n\t\t * data themselves. Their partitions do contain data, but those are\n\t\t * different colocated shards that will be copied seperately.\n\t\t */\n\t\tif (PartitionedTable(shardInterval->relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tList *ddlCommandList = NIL;\n\n\t\t/*\n\t\t * This uses repeatable read because we want to read the table in\n\t\t * the state exactly as it was when the snapshot was created. This\n\t\t * is needed when using this code for the initial data copy when\n\t\t * using logical replication. The logical replication catchup might\n\t\t * fail otherwise, because some of the updates that it needs to do\n\t\t * have already been applied on the target.\n\t\t */\n\t\tStringInfo beginTransaction = makeStringInfo();\n\t\tappendStringInfo(beginTransaction,\n\t\t\t\t\t\t \"BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;\");\n\t\tddlCommandList = lappend(ddlCommandList, beginTransaction->data);\n\n\t\t/* Set snapshot for non-blocking shard split. */\n\t\tif (snapshotName != NULL)\n\t\t{\n\t\t\tStringInfo snapShotString = makeStringInfo();\n\t\t\tappendStringInfo(snapShotString, \"SET TRANSACTION SNAPSHOT %s;\",\n\t\t\t\t\t\t\t quote_literal_cstr(\n\t\t\t\t\t\t\t\t snapshotName));\n\t\t\tddlCommandList = lappend(ddlCommandList, snapShotString->data);\n\t\t}\n\n\t\tchar *copyCommand = CreateShardCopyCommand(\n\t\t\tshardInterval, targetNode);\n\n\t\tddlCommandList = lappend(ddlCommandList, copyCommand);\n\n\t\tStringInfo commitCommand = makeStringInfo();\n\t\tappendStringInfo(commitCommand, \"COMMIT;\");\n\t\tddlCommandList = lappend(ddlCommandList, commitCommand->data);\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->jobId = shardInterval->shardId;\n\t\ttask->taskId = taskId;\n\t\ttask->taskType = READ_TASK;\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\tSetTaskQueryStringList(task, ddlCommandList);\n\n\t\tShardPlacement *taskPlacement = CitusMakeNode(ShardPlacement);\n\t\tSetPlacementNodeMetadata(taskPlacement, sourceNode);\n\n\t\ttask->taskPlacementList = list_make1(taskPlacement);\n\n\t\tcopyTaskList = lappend(copyTaskList, task);\n\t\ttaskId++;\n\t}\n\n\tExecuteTaskListOutsideTransaction(ROW_MODIFY_NONE, copyTaskList,\n\t\t\t\t\t\t\t\t\t  MaxAdaptiveExecutorPoolSize,\n\t\t\t\t\t\t\t\t\t  NULL /* jobIdList (ignored by API impl.) */);\n}\n\n\n/*\n * CreateShardCopyCommand constructs the command to copy a shard to another\n * worker node. This command needs to be run on the node wher you want to copy\n * the shard from.\n */\nstatic char *\nCreateShardCopyCommand(ShardInterval *shard,\n\t\t\t\t\t   WorkerNode *targetNode)\n{\n\tchar *shardName = ConstructQualifiedShardName(shard);\n\tStringInfo query = makeStringInfo();\n\tappendStringInfo(query,\n\t\t\t\t\t \"SELECT pg_catalog.worker_copy_table_to_node(%s::regclass, %u);\",\n\t\t\t\t\t quote_literal_cstr(shardName),\n\t\t\t\t\t targetNode->nodeId);\n\treturn query->data;\n}\n\n\n/*\n * EnsureShardCanBeCopied checks if the given shard has a healthy placement in the source\n * node and no placements in the target node.\n */\nstatic void\nEnsureShardCanBeCopied(int64 shardId, const char *sourceNodeName, int32 sourceNodePort,\n\t\t\t\t\t   const char *targetNodeName, int32 targetNodePort)\n{\n\tList *shardPlacementList = ShardPlacementList(shardId);\n\n\t/* error if the source shard placement does not exist */\n\tSearchShardPlacementInListOrError(shardPlacementList, sourceNodeName, sourceNodePort);\n\n\tShardPlacement *targetPlacement = SearchShardPlacementInList(shardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t targetNodePort);\n\n\tif (targetPlacement != NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"shard \" INT64_FORMAT \" already exists in the target node\",\n\t\t\t\t\t\t\tshardId)));\n\t}\n\n\t/*\n\t * Make sure the relation exists. In some cases the relation is actually dropped but\n\t * the metadata remains, such as dropping table while citus.enable_ddl_propagation\n\t * is set to off.\n\t */\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\tOid distributedTableId = shardInterval->relationId;\n\tEnsureRelationExists(distributedTableId);\n}\n\n\n/*\n * SearchShardPlacementInList searches a provided list for a shard placement with the\n * specified node name and port. This function returns NULL if no such\n * placement exists in the provided list.\n */\nShardPlacement *\nSearchShardPlacementInList(List *shardPlacementList, const char *nodeName,\n\t\t\t\t\t\t   uint32 nodePort)\n{\n\tShardPlacement *shardPlacement = NULL;\n\tforeach_declared_ptr(shardPlacement, shardPlacementList)\n\t{\n\t\tif (strncmp(nodeName, shardPlacement->nodeName, MAX_NODE_LENGTH) == 0 &&\n\t\t\tnodePort == shardPlacement->nodePort)\n\t\t{\n\t\t\treturn shardPlacement;\n\t\t}\n\t}\n\treturn NULL;\n}\n\n\n/*\n * SearchShardPlacementInListOrError searches a provided list for a shard\n * placement with the specified node name and port. This function throws an\n * error if no such placement exists in the provided list.\n *\n * This is a separate function (instead of using missingOk), so static analysis\n * reasons about NULL returns correctly.\n */\nShardPlacement *\nSearchShardPlacementInListOrError(List *shardPlacementList, const char *nodeName,\n\t\t\t\t\t\t\t\t  uint32 nodePort)\n{\n\tShardPlacement *placement = SearchShardPlacementInList(shardPlacementList, nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   nodePort);\n\tif (placement == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),\n\t\t\t\t\t\terrmsg(\"could not find placement matching \\\"%s:%d\\\"\",\n\t\t\t\t\t\t\t   nodeName, nodePort),\n\t\t\t\t\t\terrhint(\"Confirm the placement still exists and try again.\")));\n\t}\n\treturn placement;\n}\n\n\n/*\n * RecreateShardDDLCommandList generates a command list to recreate a shard,\n * but without any data init and without the post-load table creation commands.\n */\nstatic List *\nRecreateShardDDLCommandList(ShardInterval *shardInterval, const char *sourceNodeName,\n\t\t\t\t\t\t\tint32 sourceNodePort)\n{\n\tint64 shardId = shardInterval->shardId;\n\tOid relationId = shardInterval->relationId;\n\n\tList *tableRecreationCommandList = RecreateTableDDLCommandList(relationId);\n\treturn WorkerApplyShardDDLCommandList(tableRecreationCommandList, shardId);\n}\n\n\n/*\n * PostLoadShardCreationCommandList generates a command list to finalize the\n * creation of a shard after the data has been loaded. This creates stuff like\n * the indexes on the table.\n */\nstatic List *\nPostLoadShardCreationCommandList(ShardInterval *shardInterval, const char *sourceNodeName,\n\t\t\t\t\t\t\t\t int32 sourceNodePort)\n{\n\tint64 shardId = shardInterval->shardId;\n\tOid relationId = shardInterval->relationId;\n\tbool includeReplicaIdentity = true;\n\tList *indexCommandList =\n\t\tGetPostLoadTableCreationCommands(relationId, true, includeReplicaIdentity);\n\treturn WorkerApplyShardDDLCommandList(indexCommandList, shardId);\n}\n\n\n/*\n * CopyShardForeignConstraintCommandList generates command list to create foreign\n * constraints existing in source shard after copying it to the other node.\n */\nList *\nCopyShardForeignConstraintCommandList(ShardInterval *shardInterval)\n{\n\tList *colocatedShardForeignConstraintCommandList = NIL;\n\tList *referenceTableForeignConstraintList = NIL;\n\n\tCopyShardForeignConstraintCommandListGrouped(\n\t\tshardInterval,\n\t\t&colocatedShardForeignConstraintCommandList,\n\t\t&referenceTableForeignConstraintList);\n\n\treturn list_concat(colocatedShardForeignConstraintCommandList,\n\t\t\t\t\t   referenceTableForeignConstraintList);\n}\n\n\n/*\n * CopyShardForeignConstraintCommandListGrouped generates command lists\n * to create foreign constraints existing in source shard after copying it to other\n * node in separate groups for foreign constraints in between hash distributed tables\n * and from a hash distributed to reference tables.\n */\nvoid\nCopyShardForeignConstraintCommandListGrouped(ShardInterval *shardInterval,\n\t\t\t\t\t\t\t\t\t\t\t List **\n\t\t\t\t\t\t\t\t\t\t\t colocatedShardForeignConstraintCommandList,\n\t\t\t\t\t\t\t\t\t\t\t List **referenceTableForeignConstraintList)\n{\n\tOid relationId = shardInterval->relationId;\n\tOid schemaId = get_rel_namespace(relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\tchar *escapedSchemaName = quote_literal_cstr(schemaName);\n\tint shardIndex = 0;\n\n\tList *commandList = GetReferencingForeignConstaintCommands(relationId);\n\n\t/* we will only use shardIndex if there is a foreign constraint */\n\tif (commandList != NIL)\n\t{\n\t\tshardIndex = ShardIndex(shardInterval);\n\t}\n\n\t*colocatedShardForeignConstraintCommandList = NIL;\n\t*referenceTableForeignConstraintList = NIL;\n\n\tconst char *command = NULL;\n\tforeach_declared_ptr(command, commandList)\n\t{\n\t\tchar *escapedCommand = quote_literal_cstr(command);\n\n\t\tuint64 referencedShardId = INVALID_SHARD_ID;\n\t\tbool colocatedForeignKey = false;\n\n\t\tStringInfo applyForeignConstraintCommand = makeStringInfo();\n\n\t\t/* we need to parse the foreign constraint command to get referenced table id */\n\t\tOid referencedRelationId = ForeignConstraintGetReferencedTableId(command);\n\t\tif (referencedRelationId == InvalidOid)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t\terrmsg(\"cannot create foreign key constraint\"),\n\t\t\t\t\t\t\terrdetail(\"Referenced relation cannot be found.\")));\n\t\t}\n\n\t\tOid referencedSchemaId = get_rel_namespace(referencedRelationId);\n\t\tchar *referencedSchemaName = get_namespace_name(referencedSchemaId);\n\t\tchar *escapedReferencedSchemaName = quote_literal_cstr(referencedSchemaName);\n\n\t\tif (relationId == referencedRelationId)\n\t\t{\n\t\t\treferencedShardId = shardInterval->shardId;\n\t\t}\n\t\telse if (IsCitusTableType(referencedRelationId, REFERENCE_TABLE))\n\t\t{\n\t\t\treferencedShardId = GetFirstShardId(referencedRelationId);\n\t\t}\n\t\telse if (IsCitusTableType(referencedRelationId, CITUS_LOCAL_TABLE))\n\t\t{\n\t\t\t/*\n\t\t\t * Only reference tables and citus local tables can have foreign\n\t\t\t * keys to citus local tables but we already do not allow copying\n\t\t\t * citus local table shards and we don't try to replicate citus\n\t\t\t * local table shards. So, the referencing table must be a reference\n\t\t\t * table in this context.\n\t\t\t */\n\t\t\tAssert(IsCitusTableType(relationId, REFERENCE_TABLE));\n\n\t\t\t/*\n\t\t\t * We don't set foreign keys from reference tables to citus local\n\t\t\t * tables in worker shard placements of reference tables because\n\t\t\t * we don't have the shard placement for citus local table in worker\n\t\t\t * nodes.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treferencedShardId = ColocatedShardIdInRelation(referencedRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   shardIndex);\n\n\t\t\tcolocatedForeignKey = true;\n\t\t}\n\n\t\tappendStringInfo(applyForeignConstraintCommand,\n\t\t\t\t\t\t WORKER_APPLY_INTER_SHARD_DDL_COMMAND, shardInterval->shardId,\n\t\t\t\t\t\t escapedSchemaName, referencedShardId,\n\t\t\t\t\t\t escapedReferencedSchemaName, escapedCommand);\n\n\t\tif (colocatedForeignKey)\n\t\t{\n\t\t\t*colocatedShardForeignConstraintCommandList = lappend(\n\t\t\t\t*colocatedShardForeignConstraintCommandList,\n\t\t\t\tapplyForeignConstraintCommand->data);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t*referenceTableForeignConstraintList = lappend(\n\t\t\t\t*referenceTableForeignConstraintList,\n\t\t\t\tapplyForeignConstraintCommand->data);\n\t\t}\n\t}\n}\n\n\n/*\n * GetFirstShardId is a helper function which returns the first\n * shardId of the given distributed relation. The function doesn't\n * sort the shardIds, so it is mostly useful for reference tables.\n */\nuint64\nGetFirstShardId(Oid relationId)\n{\n\tList *shardList = LoadShardList(relationId);\n\tuint64 *shardIdPointer = (uint64 *) linitial(shardList);\n\n\treturn (*shardIdPointer);\n}\n\n\n/*\n * ConstuctQualifiedShardName creates the fully qualified name string of the\n * given shard in <schema>.<table_name>_<shard_id> format.\n */\nchar *\nConstructQualifiedShardName(ShardInterval *shardInterval)\n{\n\tOid schemaId = get_rel_namespace(shardInterval->relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\tchar *tableName = get_rel_name(shardInterval->relationId);\n\n\tchar *shardName = pstrdup(tableName);\n\tAppendShardIdToName(&shardName, shardInterval->shardId);\n\tshardName = quote_qualified_identifier(schemaName, shardName);\n\n\treturn shardName;\n}\n\n\n/*\n * RecreateTableDDLCommandList returns a list of DDL statements similar to that\n * returned by GetTableCreationCommands except that the list begins with a \"DROP TABLE\"\n * or \"DROP FOREIGN TABLE\" statement to facilitate idempotent recreation of a placement.\n */\nstatic List *\nRecreateTableDDLCommandList(Oid relationId)\n{\n\tconst char *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\n\tStringInfo dropCommand = makeStringInfo();\n\n\tIncludeSequenceDefaults includeSequenceDefaults = NO_SEQUENCE_DEFAULTS;\n\tIncludeIdentities includeIdentityDefaults = NO_IDENTITY;\n\n\t/* build appropriate DROP command based on relation kind */\n\tif (RegularTable(relationId))\n\t{\n\t\tappendStringInfo(dropCommand, DROP_REGULAR_TABLE_COMMAND,\n\t\t\t\t\t\t qualifiedRelationName);\n\t}\n\telse if (IsForeignTable(relationId))\n\t{\n\t\tappendStringInfo(dropCommand, DROP_FOREIGN_TABLE_COMMAND,\n\t\t\t\t\t\t qualifiedRelationName);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),\n\t\t\t\t\t\terrmsg(\"target is not a regular, foreign or partitioned \"\n\t\t\t\t\t\t\t   \"table\")));\n\t}\n\n\tList *dropCommandList = list_make1(makeTableDDLCommandString(dropCommand->data));\n\tList *createCommandList = GetPreLoadTableCreationCommands(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  includeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  includeIdentityDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL);\n\tList *recreateCommandList = list_concat(dropCommandList, createCommandList);\n\n\treturn recreateCommandList;\n}\n\n\n/*\n * DropShardPlacementsFromMetadata drops the shard placement metadata for\n * the shard placements of given shard interval list from pg_dist_placement.\n */\nstatic void\nDropShardPlacementsFromMetadata(List *shardList,\n\t\t\t\t\t\t\t\tchar *nodeName, int32 nodePort)\n{\n\tShardInterval *shardInverval = NULL;\n\tforeach_declared_ptr(shardInverval, shardList)\n\t{\n\t\tuint64 shardId = shardInverval->shardId;\n\t\tList *shardPlacementList = ShardPlacementList(shardId);\n\t\tShardPlacement *placement =\n\t\t\tSearchShardPlacementInListOrError(shardPlacementList, nodeName, nodePort);\n\n\t\tDeleteShardPlacementRow(placement->placementId);\n\t}\n}\n\n\n/*\n * UpdateColocatedShardPlacementMetadataOnWorkers updates the metadata about the\n * placements of the given shard and its colocated shards by changing the nodename and\n * nodeport of the shards from the source nodename/port to target nodename/port.\n *\n * Note that the function does nothing if the given shard belongs to a non-mx table.\n */\nstatic void\nUpdateColocatedShardPlacementMetadataOnWorkers(int64 shardId,\n\t\t\t\t\t\t\t\t\t\t\t   char *sourceNodeName, int32 sourceNodePort,\n\t\t\t\t\t\t\t\t\t\t\t   char *targetNodeName, int32 targetNodePort)\n{\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\tListCell *colocatedShardCell = NULL;\n\tbool shouldSyncMetadata = ShouldSyncTableMetadata(shardInterval->relationId);\n\n\tif (!shouldSyncMetadata)\n\t{\n\t\treturn;\n\t}\n\n\tuint32 sourceGroupId = GroupForNode(sourceNodeName, sourceNodePort);\n\tuint32 targetGroupId = GroupForNode(targetNodeName, targetNodePort);\n\n\tList *colocatedShardList = ColocatedShardIntervalList(shardInterval);\n\n\t/* iterate through the colocated shards and copy each */\n\tforeach(colocatedShardCell, colocatedShardList)\n\t{\n\t\tShardInterval *colocatedShard = (ShardInterval *) lfirst(colocatedShardCell);\n\t\tStringInfo updateCommand = makeStringInfo();\n\n\t\tappendStringInfo(updateCommand,\n\t\t\t\t\t\t \"SELECT citus_internal.update_placement_metadata(%ld, %d, %d)\",\n\t\t\t\t\t\t colocatedShard->shardId,\n\t\t\t\t\t\t sourceGroupId, targetGroupId);\n\n\t\tSendCommandToWorkersWithMetadata(updateCommand->data);\n\t}\n}\n\n\n/*\n * WorkerApplyShardDDLCommandList wraps all DDL commands in ddlCommandList\n * in a call to worker_apply_shard_ddl_command to apply the DDL command to\n * the shard specified by shardId.\n */\nList *\nWorkerApplyShardDDLCommandList(List *ddlCommandList, int64 shardId)\n{\n\tList *applyDDLCommandList = NIL;\n\n\tTableDDLCommand *ddlCommand = NULL;\n\tforeach_declared_ptr(ddlCommand, ddlCommandList)\n\t{\n\t\tAssert(CitusIsA(ddlCommand, TableDDLCommand));\n\t\tchar *applyDDLCommand = GetShardedTableDDLCommand(ddlCommand, shardId, NULL);\n\t\tapplyDDLCommandList = lappend(applyDDLCommandList, applyDDLCommand);\n\t}\n\n\treturn applyDDLCommandList;\n}\n\n\n/*\n * UpdatePlacementUpdateStatusForShardIntervalList updates the status field for shards\n * in the given shardInterval list.\n */\nvoid\nUpdatePlacementUpdateStatusForShardIntervalList(List *shardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\tchar *sourceName, int sourcePort,\n\t\t\t\t\t\t\t\t\t\t\t\tPlacementUpdateStatus status)\n{\n\tList *segmentList = NIL;\n\tList *rebalanceMonitorList = NULL;\n\n\tif (!HasProgressMonitor())\n\t{\n\t\trebalanceMonitorList = ProgressMonitorList(REBALANCE_ACTIVITY_MAGIC_NUMBER,\n\t\t\t\t\t\t\t\t\t\t\t\t   &segmentList);\n\t}\n\telse\n\t{\n\t\trebalanceMonitorList = list_make1(GetCurrentProgressMonitor());\n\t}\n\n\tProgressMonitorData *monitor = NULL;\n\tforeach_declared_ptr(monitor, rebalanceMonitorList)\n\t{\n\t\tPlacementUpdateEventProgress *steps = ProgressMonitorSteps(monitor);\n\n\t\tfor (int moveIndex = 0; moveIndex < monitor->stepCount; moveIndex++)\n\t\t{\n\t\t\tPlacementUpdateEventProgress *step = steps + moveIndex;\n\t\t\tuint64 currentShardId = step->shardId;\n\t\t\tbool foundInList = false;\n\n\t\t\tShardInterval *candidateShard = NULL;\n\t\t\tforeach_declared_ptr(candidateShard, shardIntervalList)\n\t\t\t{\n\t\t\t\tif (candidateShard->shardId == currentShardId)\n\t\t\t\t{\n\t\t\t\t\tfoundInList = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (foundInList &&\n\t\t\t\tstrcmp(step->sourceName, sourceName) == 0 &&\n\t\t\t\tstep->sourcePort == sourcePort)\n\t\t\t{\n\t\t\t\tpg_atomic_write_u64(&step->updateStatus, status);\n\t\t\t}\n\t\t}\n\t}\n\n\tDetachFromDSMSegments(segmentList);\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/stage_protocol.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * stage_protocol.c\n *\n * Routines for staging PostgreSQL table data as shards into the distributed\n * cluster. These user-defined functions are similar to the psql-side \\stage\n * command, but also differ from them in that users stage data from tables and\n * not files, and that they can also append to existing shards.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/htup_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/partition.h\"\n#include \"commands/tablecmds.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/inval.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/foreign_key_relationship.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/* Local functions forward declarations */\nstatic List * RelationShardListForShardCreate(ShardInterval *shardInterval);\nstatic bool WorkerShardStats(ShardPlacement *placement, Oid relationId,\n\t\t\t\t\t\t\t const char *shardName, uint64 *shardSize);\nstatic void UpdateTableStatistics(Oid relationId);\nstatic void ReceiveAndUpdateShardsSizes(List *connectionList);\nstatic void UpdateShardSize(uint64 shardId, ShardInterval *shardInterval,\n\t\t\t\t\t\t\tOid relationId, List *shardPlacementList,\n\t\t\t\t\t\t\tuint64 shardSize);\nstatic bool ProcessShardStatisticsRow(PGresult *result, int64 rowIndex, uint64 *shardId,\n\t\t\t\t\t\t\t\t\t  uint64 *shardSize);\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(master_create_empty_shard);\nPG_FUNCTION_INFO_V1(master_append_table_to_shard);\nPG_FUNCTION_INFO_V1(citus_update_shard_statistics);\nPG_FUNCTION_INFO_V1(master_update_shard_statistics);\nPG_FUNCTION_INFO_V1(citus_update_table_statistics);\n\n\n/*\n * master_create_empty_shard creates an empty shard for the given distributed\n * table. The function first updates metadata on the coordinator node to make\n * this shard visible. Then it creates empty shard on worker node and added\n * shard placement row to metadata table.\n */\nDatum\nmaster_create_empty_shard(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *relationNameText = PG_GETARG_TEXT_P(0);\n\tchar *relationName = text_to_cstring(relationNameText);\n\tuint32 attemptableNodeCount = 0;\n\tObjectAddress *tableAddress = palloc0(sizeof(ObjectAddress));\n\n\tuint32 candidateNodeIndex = 0;\n\tList *candidateNodeList = NIL;\n\ttext *nullMinValue = NULL;\n\ttext *nullMaxValue = NULL;\n\tchar storageType = SHARD_STORAGE_TABLE;\n\n\tOid relationId = ResolveRelationId(relationNameText, false);\n\n\tEnsureTablePermissions(relationId, ACL_INSERT, ACLMASK_ALL);\n\tCheckDistributedTable(relationId);\n\n\t/*\n\t * distributed tables might have dependencies on different objects, since we create\n\t * shards for a distributed table via multiple sessions these objects will be created\n\t * via their own connection and committed immediately so they become visible to all\n\t * sessions creating shards.\n\t */\n\tObjectAddressSet(*tableAddress, RelationRelationId, relationId);\n\tEnsureAllObjectDependenciesExistOnAllNodes(list_make1(tableAddress));\n\tEnsureReferenceTablesExistOnAllNodes();\n\n\t/* don't allow the table to be dropped */\n\tLockRelationOid(relationId, AccessShareLock);\n\n\t/* don't allow concurrent node list changes that require an exclusive lock */\n\tLockRelationOid(DistNodeRelationId(), RowShareLock);\n\n\t/* set the storage type of foreign tables to 'f' */\n\tif (IsForeignTable(relationId))\n\t{\n\t\tstorageType = SHARD_STORAGE_FOREIGN;\n\t}\n\n\tif (IsCitusTableType(relationId, HASH_DISTRIBUTED))\n\t{\n\t\tereport(ERROR, (errmsg(\"relation \\\"%s\\\" is a hash partitioned table\",\n\t\t\t\t\t\t\t   relationName),\n\t\t\t\t\t\terrdetail(\"We currently don't support creating shards \"\n\t\t\t\t\t\t\t\t  \"on hash-partitioned tables\")));\n\t}\n\telse if (IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED))\n\t{\n\t\tereport(ERROR, (errmsg(\"relation \\\"%s\\\" is a single shard table\",\n\t\t\t\t\t\t\t   relationName),\n\t\t\t\t\t\terrdetail(\"We currently don't support creating shards \"\n\t\t\t\t\t\t\t\t  \"on single shard tables\")));\n\t}\n\telse if (IsCitusTableType(relationId, REFERENCE_TABLE))\n\t{\n\t\tereport(ERROR, (errmsg(\"relation \\\"%s\\\" is a reference table\",\n\t\t\t\t\t\t\t   relationName),\n\t\t\t\t\t\terrdetail(\"We currently don't support creating shards \"\n\t\t\t\t\t\t\t\t  \"on reference tables\")));\n\t}\n\telse if (IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\tereport(ERROR, (errmsg(\"relation \\\"%s\\\" is a local table\",\n\t\t\t\t\t\t\t   relationName),\n\t\t\t\t\t\terrdetail(\"We currently don't support creating shards \"\n\t\t\t\t\t\t\t\t  \"on local tables\")));\n\t}\n\n\t/* generate new and unique shardId from sequence */\n\tuint64 shardId = GetNextShardId();\n\n\t/* if enough live groups, add an extra candidate node as backup */\n\tList *workerNodeList = DistributedTablePlacementNodeList(NoLock);\n\n\tif (list_length(workerNodeList) > ShardReplicationFactor)\n\t{\n\t\tattemptableNodeCount = ShardReplicationFactor + 1;\n\t}\n\telse\n\t{\n\t\tattemptableNodeCount = ShardReplicationFactor;\n\t}\n\n\t/* first retrieve a list of random nodes for shard placements */\n\twhile (candidateNodeIndex < attemptableNodeCount)\n\t{\n\t\tWorkerNode *candidateNode = WorkerGetRoundRobinCandidateNode(workerNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t candidateNodeIndex);\n\t\tif (candidateNode == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"could only find %u of %u possible nodes\",\n\t\t\t\t\t\t\t\t   candidateNodeIndex, attemptableNodeCount)));\n\t\t}\n\n\t\tcandidateNodeList = lappend(candidateNodeList, candidateNode);\n\t\tcandidateNodeIndex++;\n\t}\n\n\tInsertShardRow(relationId, shardId, storageType, nullMinValue, nullMaxValue);\n\n\tCreateAppendDistributedShardPlacements(relationId, shardId, candidateNodeList,\n\t\t\t\t\t\t\t\t\t\t   ShardReplicationFactor);\n\n\tPG_RETURN_INT64(shardId);\n}\n\n\n/*\n * master_append_table_to_shard is a deprecated function for appending data\n * to a shard in an append-distributed table.\n */\nDatum\nmaster_append_table_to_shard(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"master_append_table_to_shard has been deprecated\")));\n}\n\n\n/*\n * citus_update_shard_statistics updates metadata (shard size and shard min/max\n * values) of the given shard and returns the updated shard size.\n */\nDatum\ncitus_update_shard_statistics(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint64 shardId = PG_GETARG_INT64(0);\n\n\tuint64 shardSize = UpdateShardStatistics(shardId);\n\n\tPG_RETURN_INT64(shardSize);\n}\n\n\n/*\n * citus_update_table_statistics updates metadata (shard size and shard min/max\n * values) of the shards of the given table\n */\nDatum\ncitus_update_table_statistics(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid distributedTableId = PG_GETARG_OID(0);\n\n\t/*\n\t * Ensure the table still exists by trying to acquire a lock on it\n\t * If function returns NULL, it means the table doesn't exist\n\t * hence we should skip\n\t */\n\tRelation relation = try_relation_open(distributedTableId, AccessShareLock);\n\n\tif (relation != NULL)\n\t{\n\t\tUpdateTableStatistics(distributedTableId);\n\n\t\t/*\n\t\t * We release the lock here since citus_update_table_statistics\n\t\t * is usually used in the following command:\n\t\t * SELECT citus_update_table_statistics(logicalrelid) FROM pg_dist_partition;\n\t\t * In this way we avoid holding the locks on distributed tables for a long time:\n\t\t * If we close the relation with NoLock, the locks on the distributed tables will\n\t\t * be held until the above command is finished (all distributed tables are updated).\n\t\t */\n\t\trelation_close(relation, AccessShareLock);\n\t}\n\telse\n\t{\n\t\tereport(NOTICE, (errmsg(\"relation with OID %u does not exist, skipping\",\n\t\t\t\t\t\t\t\tdistributedTableId)));\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * master_update_shard_statistics is a wrapper function for old UDF name.\n */\nDatum\nmaster_update_shard_statistics(PG_FUNCTION_ARGS)\n{\n\treturn citus_update_shard_statistics(fcinfo);\n}\n\n\n/*\n * CheckDistributedTable checks if the given relationId corresponds to a\n * distributed table. If it does not, the function errors out.\n */\nvoid\nCheckDistributedTable(Oid relationId)\n{\n\tchar *relationName = get_rel_name(relationId);\n\n\t/* check that the relationId belongs to a table */\n\tEnsureRelationKindSupported(relationId);\n\n\tif (!IsCitusTable(relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"relation \\\"%s\\\" is not a distributed table\",\n\t\t\t\t\t\t\t   relationName)));\n\t}\n}\n\n\n/*\n * CreateAppendDistributedShardPlacements creates shards for append distributed\n * tables on worker nodes. After successfully creating shard on the worker,\n * shard placement rows are added to the metadata.\n */\nvoid\nCreateAppendDistributedShardPlacements(Oid relationId, int64 shardId,\n\t\t\t\t\t\t\t\t\t   List *workerNodeList, int replicationFactor)\n{\n\tint attemptCount = replicationFactor;\n\tint workerNodeCount = list_length(workerNodeList);\n\tint placementsCreated = 0;\n\tIncludeSequenceDefaults includeSequenceDefaults = NO_SEQUENCE_DEFAULTS;\n\tIncludeIdentities includeIdentityDefaults = NO_IDENTITY;\n\n\tbool creatingShellTableOnRemoteNode = false;\n\tList *ddlCommandList = GetFullTableCreationCommands(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tincludeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tincludeIdentityDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcreatingShellTableOnRemoteNode);\n\tuint32 connectionFlag = FOR_DDL;\n\tchar *relationOwner = TableOwner(relationId);\n\n\t/* if we have enough nodes, add an extra placement attempt for backup */\n\tif (workerNodeCount > replicationFactor)\n\t{\n\t\tattemptCount++;\n\t}\n\n\tfor (int attemptNumber = 0; attemptNumber < attemptCount; attemptNumber++)\n\t{\n\t\tint workerNodeIndex = attemptNumber % workerNodeCount;\n\t\tWorkerNode *workerNode = (WorkerNode *) list_nth(workerNodeList, workerNodeIndex);\n\n\t\tif (NodeIsCoordinator(workerNode))\n\t\t{\n\t\t\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t\t\t \"Creating placements for the append partitioned tables on the coordinator is not supported, skipping coordinator ...\")));\n\t\t\tcontinue;\n\t\t}\n\n\t\tuint32 nodeGroupId = workerNode->groupId;\n\t\tchar *nodeName = workerNode->workerName;\n\t\tuint32 nodePort = workerNode->workerPort;\n\t\tconst uint64 shardSize = 0;\n\t\tMultiConnection *connection =\n\t\t\tGetNodeUserDatabaseConnection(connectionFlag, nodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t  relationOwner, NULL);\n\n\t\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"could not connect to node \\\"%s:%u\\\"\", nodeName,\n\t\t\t\t\t\t\t\t\t nodePort)));\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tList *commandList = WorkerCreateShardCommandList(relationId, shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t ddlCommandList);\n\n\t\tExecuteCriticalRemoteCommandList(connection, commandList);\n\n\t\tInsertShardPlacementRow(shardId, INVALID_PLACEMENT_ID, shardSize,\n\t\t\t\t\t\t\t\tnodeGroupId);\n\t\tplacementsCreated++;\n\n\t\tif (placementsCreated >= replicationFactor)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* check if we created enough shard replicas */\n\tif (placementsCreated < replicationFactor)\n\t{\n\t\tereport(ERROR, (errmsg(\"could only create %u of %u of required shard replicas\",\n\t\t\t\t\t\t\t   placementsCreated, replicationFactor)));\n\t}\n}\n\n\n/*\n * InsertShardPlacementRows inserts shard placements to the metadata table on\n * the coordinator node.\n */\nvoid\nInsertShardPlacementRows(Oid relationId, int64 shardId, List *workerNodeList,\n\t\t\t\t\t\t int workerStartIndex, int replicationFactor)\n{\n\tint workerNodeCount = list_length(workerNodeList);\n\n\tfor (int placementIndex = 0; placementIndex < replicationFactor; placementIndex++)\n\t{\n\t\tint workerNodeIndex = (workerStartIndex + placementIndex) % workerNodeCount;\n\t\tWorkerNode *workerNode = (WorkerNode *) list_nth(workerNodeList, workerNodeIndex);\n\t\tuint32 nodeGroupId = workerNode->groupId;\n\t\tconst uint64 shardSize = 0;\n\n\t\tInsertShardPlacementRow(shardId,\n\t\t\t\t\t\t\t\tINVALID_PLACEMENT_ID,\n\t\t\t\t\t\t\t\tshardSize,\n\t\t\t\t\t\t\t\tnodeGroupId);\n\t}\n}\n\n\n/*\n * CreateShardsOnWorkers creates shards on worker nodes given the shard placements\n * as a parameter. The function creates the shards via the executor. This means\n * that it can adopt the number of connections required to create the shards.\n */\nvoid\nCreateShardsOnWorkers(Oid distributedRelationId, List *shardPlacements,\n\t\t\t\t\t  bool useExclusiveConnection)\n{\n\tIncludeSequenceDefaults includeSequenceDefaults = NO_SEQUENCE_DEFAULTS;\n\tIncludeIdentities includeIdentityDefaults = NO_IDENTITY;\n\n\tbool creatingShellTableOnRemoteNode = false;\n\tList *ddlCommandList = GetFullTableCreationCommands(distributedRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tincludeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tincludeIdentityDefaults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcreatingShellTableOnRemoteNode);\n\n\tint taskId = 1;\n\tList *taskList = NIL;\n\tint poolSize = 1;\n\n\tShardPlacement *shardPlacement = NULL;\n\tforeach_declared_ptr(shardPlacement, shardPlacements)\n\t{\n\t\tuint64 shardId = shardPlacement->shardId;\n\t\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\t\tList *relationShardList = RelationShardListForShardCreate(shardInterval);\n\n\t\tList *commandList = WorkerCreateShardCommandList(distributedRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardId, ddlCommandList);\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->jobId = INVALID_JOB_ID;\n\t\ttask->taskId = taskId++;\n\t\ttask->taskType = DDL_TASK;\n\t\tSetTaskQueryStringList(task, commandList);\n\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\ttask->dependentTaskList = NIL;\n\t\ttask->anchorShardId = shardId;\n\t\ttask->relationShardList = relationShardList;\n\t\ttask->taskPlacementList = list_make1(shardPlacement);\n\n\t\ttaskList = lappend(taskList, task);\n\t}\n\n\tif (useExclusiveConnection)\n\t{\n\t\t/*\n\t\t * When the table has local data, we force max parallelization so data\n\t\t * copy is done efficiently. We also prefer to use max parallelization\n\t\t * when we're inside a transaction block because the user might execute\n\t\t * compute heavy commands (e.g., load data or create index) later in the\n\t\t * transaction block.\n\t\t */\n\t\tSetLocalForceMaxQueryParallelization();\n\n\t\t/*\n\t\t * TODO: After we fix adaptive executor to record parallel access for\n\t\t * ForceMaxQueryParallelization, we should remove this. This is just\n\t\t * to force adaptive executor to record parallel access to relations.\n\t\t *\n\t\t * Adaptive executor uses poolSize to decide if it should record parallel\n\t\t * access to relations or not, and it ignores ForceMaxQueryParallelization\n\t\t * because of some complications in TRUNCATE.\n\t\t */\n\t\tpoolSize = MaxAdaptiveExecutorPoolSize;\n\t}\n\tbool localExecutionSupported = true;\n\tExecuteUtilityTaskListExtended(taskList, poolSize, localExecutionSupported);\n}\n\n\n/*\n * RelationShardListForShardCreate gets a shard interval and returns the placement\n * accesses that would happen when a placement of the shard interval is created.\n */\nstatic List *\nRelationShardListForShardCreate(ShardInterval *shardInterval)\n{\n\tOid relationId = shardInterval->relationId;\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\tList *referencedRelationList = cacheEntry->referencedRelationsViaForeignKey;\n\tList *referencingRelationList = cacheEntry->referencingRelationsViaForeignKey;\n\tint shardIndex = -1;\n\n\t/* list_concat_*() modifies the first arg, so make a copy first */\n\tList *allForeignKeyRelations = list_copy(referencedRelationList);\n\tallForeignKeyRelations = list_concat_unique_oid(allForeignKeyRelations,\n\t\t\t\t\t\t\t\t\t\t\t\t\treferencingRelationList);\n\n\t/* record the placement access of the shard itself */\n\tRelationShard *relationShard = CitusMakeNode(RelationShard);\n\trelationShard->relationId = relationId;\n\trelationShard->shardId = shardInterval->shardId;\n\tList *relationShardList = list_make1(relationShard);\n\n\tif ((IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED) ||\n\t\t IsCitusTableTypeCacheEntry(cacheEntry, SINGLE_SHARD_DISTRIBUTED)) &&\n\t\tcacheEntry->colocationId != INVALID_COLOCATION_ID)\n\t{\n\t\tshardIndex = ShardIndex(shardInterval);\n\t}\n\n\n\t/* all foregin key constraint relations */\n\tOid fkeyRelationid = InvalidOid;\n\tforeach_declared_oid(fkeyRelationid, allForeignKeyRelations)\n\t{\n\t\tuint64 fkeyShardId = INVALID_SHARD_ID;\n\n\t\tif (!IsCitusTable(fkeyRelationid))\n\t\t{\n\t\t\t/* we're not interested in local tables */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (IsCitusTableType(fkeyRelationid, REFERENCE_TABLE))\n\t\t{\n\t\t\tfkeyShardId = GetFirstShardId(fkeyRelationid);\n\t\t}\n\t\telse if (IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED) &&\n\t\t\t\t IsCitusTableType(fkeyRelationid, HASH_DISTRIBUTED))\n\t\t{\n\t\t\t/* hash distributed tables should be colocated to have fkey */\n\t\t\tAssert(TableColocationId(fkeyRelationid) == cacheEntry->colocationId);\n\n\t\t\tfkeyShardId =\n\t\t\t\tColocatedShardIdInRelation(fkeyRelationid, shardIndex);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * We currently do not support foreign keys from/to local tables or\n\t\t\t * non-colocated tables when creating shards. Also note that shard\n\t\t\t * creation via shard moves doesn't happen in a transaction block,\n\t\t\t * so not relevant here.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tRelationShard *fkeyRelationShard = CitusMakeNode(RelationShard);\n\t\tfkeyRelationShard->relationId = fkeyRelationid;\n\t\tfkeyRelationShard->shardId = fkeyShardId;\n\n\t\trelationShardList = lappend(relationShardList, fkeyRelationShard);\n\t}\n\n\n\t/* if partitioned table, make sure to record the parent table */\n\tif (PartitionTable(relationId))\n\t{\n\t\tRelationShard *parentRelationShard = CitusMakeNode(RelationShard);\n\n\t\t/* partitioned tables are always co-located */\n\t\tAssert(shardIndex != -1);\n\n\t\tparentRelationShard->relationId = PartitionParentOid(relationId);\n\t\tparentRelationShard->shardId =\n\t\t\tColocatedShardIdInRelation(parentRelationShard->relationId, shardIndex);\n\n\t\trelationShardList = lappend(relationShardList, parentRelationShard);\n\t}\n\n\treturn relationShardList;\n}\n\n\n/*\n * WorkerCreateShardCommandList returns a list of DDL commands for the given\n * shardId to create the shard on the worker node.\n */\nList *\nWorkerCreateShardCommandList(Oid relationId, uint64 shardId,\n\t\t\t\t\t\t\t List *ddlCommandList)\n{\n\tList *commandList = NIL;\n\tOid schemaId = get_rel_namespace(relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\n\tTableDDLCommand *ddlCommand = NULL;\n\tforeach_declared_ptr(ddlCommand, ddlCommandList)\n\t{\n\t\tAssert(CitusIsA(ddlCommand, TableDDLCommand));\n\t\tchar *applyDDLCommand = GetShardedTableDDLCommand(ddlCommand, shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  schemaName);\n\t\tcommandList = lappend(commandList, applyDDLCommand);\n\t}\n\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\n\tcommandList = list_concat(\n\t\tcommandList,\n\t\tCopyShardForeignConstraintCommandList(shardInterval)\n\t\t);\n\n\t/*\n\t * If the shard is created for a partition, send the command to create the\n\t * partitioning hierarcy on the shard.\n\t */\n\tif (PartitionTable(relationId))\n\t{\n\t\tchar *attachPartitionCommand = GenerateAttachShardPartitionCommand(shardInterval);\n\n\t\tcommandList = lappend(commandList, attachPartitionCommand);\n\t}\n\n\treturn commandList;\n}\n\n\n/*\n * UpdateShardStatistics updates metadata (shard size and shard min/max values)\n * of the given shard and returns the updated shard size.\n */\nuint64\nUpdateShardStatistics(int64 shardId)\n{\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\tOid relationId = shardInterval->relationId;\n\tbool statsOK = false;\n\tuint64 shardSize = 0;\n\n\t/* Build shard qualified name. */\n\tchar *shardName = get_rel_name(relationId);\n\tOid schemaId = get_rel_namespace(relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\n\tAppendShardIdToName(&shardName, shardId);\n\n\tchar *shardQualifiedName = quote_qualified_identifier(schemaName, shardName);\n\n\tList *shardPlacementList = ActiveShardPlacementList(shardId);\n\n\t/* get shard's statistics from a shard placement */\n\tShardPlacement *placement = NULL;\n\tforeach_declared_ptr(placement, shardPlacementList)\n\t{\n\t\tstatsOK = WorkerShardStats(placement, relationId, shardQualifiedName,\n\t\t\t\t\t\t\t\t   &shardSize);\n\t\tif (statsOK)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/*\n\t * If for some reason we appended data to a shard, but failed to retrieve\n\t * statistics we just WARN here to avoid losing shard-state updates. Note\n\t * that this means we will return 0 as the shard fill-factor, and this shard\n\t * also won't be pruned as the statistics will be empty. If the failure was\n\t * transient, a subsequent append call will fetch the correct statistics.\n\t */\n\tif (!statsOK)\n\t{\n\t\tereport(WARNING, (errmsg(\"could not get statistics for shard %s\",\n\t\t\t\t\t\t\t\t shardQualifiedName),\n\t\t\t\t\t\t  errdetail(\"Setting shard statistics to NULL\")));\n\t}\n\n\tUpdateShardSize(shardId, shardInterval, relationId, shardPlacementList,\n\t\t\t\t\tshardSize);\n\n\treturn shardSize;\n}\n\n\n/*\n * UpdateTableStatistics updates metadata (shard size and shard min/max values)\n * of the shards of the given table. Follows a similar logic to citus_shard_sizes function.\n */\nstatic void\nUpdateTableStatistics(Oid relationId)\n{\n\tList *citusTableIds = NIL;\n\tcitusTableIds = lappend_oid(citusTableIds, relationId);\n\n\t/* we want to use a distributed transaction here to detect distributed deadlocks */\n\tbool useDistributedTransaction = true;\n\n\tList *connectionList =\n\t\tSendShardStatisticsQueriesInParallel(citusTableIds, useDistributedTransaction);\n\n\tReceiveAndUpdateShardsSizes(connectionList);\n}\n\n\n/*\n * ReceiveAndUpdateShardsSizes receives shard id and size\n * results from the given connection list, and updates\n * respective entries in pg_dist_placement.\n */\nstatic void\nReceiveAndUpdateShardsSizes(List *connectionList)\n{\n\t/*\n\t * From the connection list, we will not get all the shards, but\n\t * all the placements. We use a hash table to remember already visited shard ids\n\t * since we update all the different placements of a shard id at once.\n\t */\n\tHTAB *alreadyVisitedShardPlacements = CreateSimpleHashSetWithName(Oid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"oid visited hash set\");\n\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tbool raiseInterrupts = true;\n\t\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\t\tif (!IsResponseOK(result))\n\t\t{\n\t\t\tReportResultError(connection, result, WARNING);\n\t\t\tcontinue;\n\t\t}\n\n\t\tint64 rowCount = PQntuples(result);\n\t\tint64 colCount = PQnfields(result);\n\n\t\t/* Although it is not expected */\n\t\tif (colCount != SHARD_SIZES_COLUMN_COUNT)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"unexpected number of columns from \"\n\t\t\t\t\t\t\t\t\t \"citus_update_table_statistics\")));\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (int64 rowIndex = 0; rowIndex < rowCount; rowIndex++)\n\t\t{\n\t\t\tuint64 shardId = 0;\n\t\t\tuint64 shardSize = 0;\n\n\t\t\tif (!ProcessShardStatisticsRow(result, rowIndex, &shardId, &shardSize))\n\t\t\t{\n\t\t\t\t/* this row has no valid shard statistics */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (OidVisited(alreadyVisitedShardPlacements, shardId))\n\t\t\t{\n\t\t\t\t/* We have already updated this placement list */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tVisitOid(alreadyVisitedShardPlacements, shardId);\n\n\t\t\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\t\t\tOid relationId = shardInterval->relationId;\n\t\t\tList *shardPlacementList = ActiveShardPlacementList(shardId);\n\n\t\t\tUpdateShardSize(shardId, shardInterval, relationId, shardPlacementList,\n\t\t\t\t\t\t\tshardSize);\n\t\t}\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\t}\n\thash_destroy(alreadyVisitedShardPlacements);\n}\n\n\n/*\n * ProcessShardStatisticsRow processes a row of shard statistics of the input PGresult\n * - it returns true if this row belongs to a valid shard\n * - it returns false if this row has no valid shard statistics (shardId = INVALID_SHARD_ID)\n *\n * Input tuples are assumed to be of the form:\n * (shard_id bigint, shard_name text, shard_size bigint)\n */\nstatic bool\nProcessShardStatisticsRow(PGresult *result, int64 rowIndex, uint64 *shardId,\n\t\t\t\t\t\t  uint64 *shardSize)\n{\n\t*shardId = ParseIntField(result, rowIndex, 0);\n\n\t/* check for the dummy entries we put so that UNION ALL wouldn't complain */\n\tif (*shardId == INVALID_SHARD_ID)\n\t{\n\t\t/* this row has no valid shard statistics */\n\t\treturn false;\n\t}\n\n\t*shardSize = ParseIntField(result, rowIndex, 1);\n\treturn true;\n}\n\n\n/*\n * UpdateShardSize updates the shardlength (shard size) of the given\n * shard and its placements in pg_dist_placement.\n */\nstatic void\nUpdateShardSize(uint64 shardId, ShardInterval *shardInterval, Oid relationId,\n\t\t\t\tList *shardPlacementList, uint64 shardSize)\n{\n\tShardPlacement *placement = NULL;\n\n\t/* update metadata for each shard placement */\n\tforeach_declared_ptr(placement, shardPlacementList)\n\t{\n\t\tuint64 placementId = placement->placementId;\n\t\tint32 groupId = placement->groupId;\n\n\t\tDeleteShardPlacementRow(placementId);\n\t\tInsertShardPlacementRow(shardId, placementId, shardSize, groupId);\n\t}\n}\n\n\n/*\n * WorkerShardStats queries the worker node, and retrieves shard statistics that\n * we assume have changed after new table data have been appended to the shard.\n */\nstatic bool\nWorkerShardStats(ShardPlacement *placement, Oid relationId, const char *shardName,\n\t\t\t\t uint64 *shardSize)\n{\n\tStringInfo tableSizeQuery = makeStringInfo();\n\tPGresult *queryResult = NULL;\n\tchar *tableSizeStringEnd = NULL;\n\n\tint connectionFlags = 0;\n\n\tMultiConnection *connection = GetPlacementConnection(connectionFlags, placement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t NULL);\n\n\t/*\n\t * This code-path doesn't support optional connections, so we don't expect\n\t * NULL connections.\n\t */\n\tAssert(connection != NULL);\n\n\t*shardSize = 0;\n\n\tchar *quotedShardName = quote_literal_cstr(shardName);\n\tappendStringInfo(tableSizeQuery, SHARD_TABLE_SIZE_QUERY, quotedShardName);\n\n\tint executeCommand = ExecuteOptionalRemoteCommand(connection, tableSizeQuery->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  &queryResult);\n\tif (executeCommand != 0)\n\t{\n\t\treturn false;\n\t}\n\n\tchar *tableSizeString = PQgetvalue(queryResult, 0, 0);\n\tif (tableSizeString == NULL)\n\t{\n\t\tPQclear(queryResult);\n\t\tForgetResults(connection);\n\t\treturn false;\n\t}\n\n\terrno = 0;\n\tuint64 tableSize = strtou64(tableSizeString, &tableSizeStringEnd, 0);\n\tif (errno != 0 || (*tableSizeStringEnd) != '\\0')\n\t{\n\t\tPQclear(queryResult);\n\t\tForgetResults(connection);\n\t\treturn false;\n\t}\n\n\t*shardSize = tableSize;\n\n\tPQclear(queryResult);\n\tForgetResults(connection);\n\n\treturn true;\n}\n\n\n/*\n * ForeignConstraintGetReferencedTableId parses given foreign constraint query and\n * extracts referenced table id from it.\n */\nOid\nForeignConstraintGetReferencedTableId(const char *queryString)\n{\n\tNode *queryNode = ParseTreeNode(queryString);\n\tAlterTableStmt *foreignConstraintStmt = (AlterTableStmt *) queryNode;\n\tAlterTableCmd *command = (AlterTableCmd *) linitial(foreignConstraintStmt->cmds);\n\n\tif (command->subtype == AT_AddConstraint)\n\t{\n\t\tConstraint *constraint = (Constraint *) command->def;\n\t\tif (constraint->contype == CONSTR_FOREIGN)\n\t\t{\n\t\t\tRangeVar *referencedTable = constraint->pktable;\n\n\t\t\treturn RangeVarGetRelid(referencedTable, NoLock,\n\t\t\t\t\t\t\t\t\tforeignConstraintStmt->missing_ok);\n\t\t}\n\t}\n\n\treturn InvalidOid;\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/worker_copy_table_to_node_udf.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_copy_table_to_node_udf.c\n *\n * This file implements the worker_copy_table_to_node UDF. This UDF can be\n * used to copy the data in a shard (or other table) from one worker node to\n * another.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"executor/executor.h\"    /* for CreateExecutorState(), FreeExecutorState(), CreateExprContext(), etc. */\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/priority.h\"\n#include \"distributed/worker_shard_copy.h\"\n\nPG_FUNCTION_INFO_V1(worker_copy_table_to_node);\n\n/*\n * worker_copy_table_to_node copies a shard from this worker to another worker\n *\n * SQL signature:\n *\n * worker_copy_table_to_node(\n *     source_table regclass,\n *     target_node_id integer\n *  ) RETURNS VOID\n */\nDatum\nworker_copy_table_to_node(PG_FUNCTION_ARGS)\n{\n\tOid relationId = PG_GETARG_OID(0);\n\tuint32_t targetNodeId = PG_GETARG_INT32(1);\n\n\tif (IsCitusTable(relationId))\n\t{\n\t\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"table %s is a Citus table, only copies of \"\n\t\t\t\t\t\t\t   \"shards or regular postgres tables are supported\",\n\t\t\t\t\t\t\t   qualifiedRelationName)));\n\t}\n\n\tOid schemaOid = get_rel_namespace(relationId);\n\tchar *relationSchemaName = get_namespace_name(schemaOid);\n\tchar *relationName = get_rel_name(relationId);\n\tchar *relationQualifiedName = quote_qualified_identifier(\n\t\trelationSchemaName,\n\t\trelationName);\n\n\tEState *executor = CreateExecutorState();\n\tDestReceiver *destReceiver = CreateShardCopyDestReceiver(\n\t\texecutor,\n\t\tlist_make2(relationSchemaName, relationName),\n\t\ttargetNodeId);\n\n\tStringInfo selectShardQueryForCopy = makeStringInfo();\n\n\t/*\n\t * Even though we do COPY(SELECT ...) all the columns, we can't just do SELECT * because we need to not COPY generated colums.\n\t */\n\tconst char *columnList = CopyableColumnNamesFromRelationName(relationSchemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t relationName);\n\tappendStringInfo(selectShardQueryForCopy,\n\t\t\t\t\t \"SELECT %s FROM %s;\", columnList, relationQualifiedName);\n\n\tParamListInfo params = NULL;\n\tExecuteQueryStringIntoDestReceiver(selectShardQueryForCopy->data, params,\n\t\t\t\t\t\t\t\t\t   destReceiver);\n\n\tFreeExecutorState(executor);\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/worker_node_manager.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_node_manager.c\n *\t  Routines for reading worker nodes from membership file, and allocating\n *\t  candidate nodes for shard placement.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"commands/dbcommands.h\"\n#include \"common/hashfn.h\"\n#include \"common/ip.h\"\n#include \"libpq/hba.h\"\n#include \"libpq/libpq-be.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/fd.h\"\n#include \"storage/ipc.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/shmem.h\"\n#include \"utils/guc.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/memutils.h\"\n\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/worker_manager.h\"\n\n\n/* Config variables managed via guc.c */\nchar *WorkerListFileName;\nint MaxWorkerNodesTracked = 2048;    /* determines worker node hash table size */\n\n\n/* Local functions forward declarations */\nstatic bool NodeIsPrimaryWorker(WorkerNode *node);\nstatic bool NodeIsReadableWorker(WorkerNode *node);\n\n\n/* ------------------------------------------------------------\n * Worker node selection functions follow\n * ------------------------------------------------------------\n */\n\n\n/*\n * WorkerGetRoundRobinCandidateNode takes in a list of worker nodes and returns\n * a candidate worker node from that list. To select this node, this function\n * uses the round-robin policy. An ideal round-robin implementation requires\n * keeping shared state for shard placements; and we instead approximate our\n * implementation by relying on the ever-increasing shardId. So, the first\n * worker node selected will be the node at the (shardId MOD worker node count)\n * index and the remaining candidate nodes will be the next nodes in the list.\n *\n * Note that the function returns null if the worker membership list does not\n * contain enough nodes to place all replicas.\n */\nWorkerNode *\nWorkerGetRoundRobinCandidateNode(List *workerNodeList, uint64 shardId,\n\t\t\t\t\t\t\t\t uint32 placementIndex)\n{\n\tuint32 workerNodeCount = list_length(workerNodeList);\n\tWorkerNode *candidateNode = NULL;\n\n\tif (placementIndex < workerNodeCount)\n\t{\n\t\tuint32 candidateNodeIndex = (shardId + placementIndex) % workerNodeCount;\n\t\tcandidateNode = (WorkerNode *) list_nth(workerNodeList, candidateNodeIndex);\n\t}\n\n\treturn candidateNode;\n}\n\n\n/*\n * ActivePrimaryNonCoordinatorNodeCount returns the number of groups with a primary in the cluster.\n * This method excludes coordinator even if it is added as a worker to cluster.\n */\nuint32\nActivePrimaryNonCoordinatorNodeCount(void)\n{\n\tList *workerNodeList = ActivePrimaryNonCoordinatorNodeList(NoLock);\n\tuint32 liveWorkerCount = list_length(workerNodeList);\n\n\treturn liveWorkerCount;\n}\n\n\n/*\n * ActiveReadableNodeCount returns the number of nodes in the cluster.\n */\nuint32\nActiveReadableNodeCount(void)\n{\n\tList *nodeList = ActiveReadableNodeList();\n\treturn list_length(nodeList);\n}\n\n\n/*\n * NodeIsCoordinator returns true if the given node represents the coordinator.\n */\nbool\nNodeIsCoordinator(WorkerNode *node)\n{\n\treturn node->groupId == COORDINATOR_GROUP_ID;\n}\n\n\n/*\n * ActiveNodeListFilterFunc returns a list of all active nodes that checkFunction\n * returns true for.\n * lockMode specifies which lock to use on pg_dist_node, this is necessary when\n * the caller wouldn't want nodes to be added concurrent to their use of this list\n */\nstatic List *\nFilterActiveNodeListFunc(LOCKMODE lockMode, bool (*checkFunction)(WorkerNode *))\n{\n\tList *workerNodeList = NIL;\n\tWorkerNode *workerNode = NULL;\n\tHASH_SEQ_STATUS status;\n\n\tAssert(checkFunction != NULL);\n\n\tif (lockMode != NoLock)\n\t{\n\t\tLockRelationOid(DistNodeRelationId(), lockMode);\n\t}\n\n\tHTAB *workerNodeHash = GetWorkerNodeHash();\n\thash_seq_init(&status, workerNodeHash);\n\n\twhile ((workerNode = hash_seq_search(&status)) != NULL)\n\t{\n\t\tif (workerNode->isActive && checkFunction(workerNode))\n\t\t{\n\t\t\tWorkerNode *workerNodeCopy = palloc0(sizeof(WorkerNode));\n\t\t\t*workerNodeCopy = *workerNode;\n\t\t\tworkerNodeList = lappend(workerNodeList, workerNodeCopy);\n\t\t}\n\t}\n\n\treturn workerNodeList;\n}\n\n\n/*\n * ActivePrimaryNonCoordinatorNodeList returns a list of all active primary worker nodes\n * in workerNodeHash. lockMode specifies which lock to use on pg_dist_node,\n * this is necessary when the caller wouldn't want nodes to be added concurrent\n * to their use of this list.\n * This method excludes coordinator even if it is added as a worker to cluster.\n */\nList *\nActivePrimaryNonCoordinatorNodeList(LOCKMODE lockMode)\n{\n\tEnsureModificationsCanRun();\n\treturn FilterActiveNodeListFunc(lockMode, NodeIsPrimaryWorker);\n}\n\n\n/*\n * ActivePrimaryNodeList returns a list of all active primary nodes in\n * workerNodeHash.\n */\nList *\nActivePrimaryNodeList(LOCKMODE lockMode)\n{\n\tEnsureModificationsCanRun();\n\treturn FilterActiveNodeListFunc(lockMode, NodeIsPrimary);\n}\n\n\n/*\n * ActivePrimaryRemoteNodeList returns a list of all active primary nodes in\n * workerNodeHash except the local one.\n */\nList *\nActivePrimaryRemoteNodeList(LOCKMODE lockMode)\n{\n\tEnsureModificationsCanRun();\n\treturn FilterActiveNodeListFunc(lockMode, NodeIsPrimaryAndRemote);\n}\n\n\n/*\n * NodeIsPrimaryWorker returns true if the node is a primary worker node.\n */\nstatic bool\nNodeIsPrimaryWorker(WorkerNode *node)\n{\n\treturn !NodeIsCoordinator(node) && NodeIsPrimary(node);\n}\n\n\n/*\n * CoordinatorAddedAsWorkerNode returns true if coordinator is added to the\n * pg_dist_node.\n */\nbool\nCoordinatorAddedAsWorkerNode()\n{\n\tbool groupContainsNodes = false;\n\n\tPrimaryNodeForGroup(COORDINATOR_GROUP_ID, &groupContainsNodes);\n\n\treturn groupContainsNodes;\n}\n\n\n/*\n * ReferenceTablePlacementNodeList returns the set of nodes that should have\n * reference table placements. This includes all primaries, including the\n * coordinator if known.\n */\nList *\nReferenceTablePlacementNodeList(LOCKMODE lockMode)\n{\n\tEnsureModificationsCanRun();\n\treturn FilterActiveNodeListFunc(lockMode, NodeIsPrimary);\n}\n\n\n/*\n * CoordinatorNodeIfAddedAsWorkerOrError returns the WorkerNode object for\n * coordinator node if it is added to pg_dist_node, otherwise errors out.\n * Also, as CoordinatorAddedAsWorkerNode acquires AccessShareLock on pg_dist_node\n * and doesn't release it, callers can safely assume coordinator won't be\n * removed from metadata until the end of transaction when this function\n * returns coordinator node.\n */\nWorkerNode *\nCoordinatorNodeIfAddedAsWorkerOrError()\n{\n\tErrorIfCoordinatorNotAddedAsWorkerNode();\n\n\tWorkerNode *coordinatorNode = LookupNodeForGroup(COORDINATOR_GROUP_ID);\n\n\tWorkerNode *coordinatorNodeCopy = palloc0(sizeof(WorkerNode));\n\t*coordinatorNodeCopy = *coordinatorNode;\n\n\treturn coordinatorNodeCopy;\n}\n\n\n/*\n * ErrorIfCoordinatorNotAddedAsWorkerNode errors out if coordinator is not added\n * to metadata.\n */\nvoid\nErrorIfCoordinatorNotAddedAsWorkerNode()\n{\n\tif (CoordinatorAddedAsWorkerNode())\n\t{\n\t\treturn;\n\t}\n\n\tereport(ERROR, (errmsg(\"operation is not allowed when coordinator \"\n\t\t\t\t\t\t   \"is not added into metadata\"),\n\t\t\t\t\terrhint(\"Use \\\"SELECT citus_set_coordinator_host('\"\n\t\t\t\t\t\t\t\"<hostname>', '<port>')\\\" to configure the \"\n\t\t\t\t\t\t\t\"coordinator hostname and port\")));\n}\n\n\n/*\n * DistributedTablePlacementNodeList returns a list of all active, primary\n * worker nodes that can store new data, i.e shouldstoreshards is 'true'\n */\nList *\nDistributedTablePlacementNodeList(LOCKMODE lockMode)\n{\n\tEnsureModificationsCanRun();\n\treturn FilterActiveNodeListFunc(lockMode, NodeCanHaveDistTablePlacements);\n}\n\n\n/*\n * NodeCanHaveDistTablePlacements returns true if the given node can have\n * shards of a distributed table.\n */\nbool\nNodeCanHaveDistTablePlacements(WorkerNode *node)\n{\n\tif (!NodeIsPrimary(node))\n\t{\n\t\treturn false;\n\t}\n\n\treturn node->shouldHaveShards;\n}\n\n\n/*\n * ActiveReadableNonCoordinatorNodeList returns a list of all nodes in workerNodeHash\n * that are readable nodes This method excludes coordinator.\n */\nList *\nActiveReadableNonCoordinatorNodeList(void)\n{\n\treturn FilterActiveNodeListFunc(NoLock, NodeIsReadableWorker);\n}\n\n\n/*\n * ActiveReadableNodeList returns a list of all nodes in workerNodeHash\n * that are readable workers.\n * This method includes coordinator if it is added as a worker to the cluster.\n */\nList *\nActiveReadableNodeList(void)\n{\n\treturn FilterActiveNodeListFunc(NoLock, NodeIsReadable);\n}\n\n\n/*\n * NodeIsReadableWorker returns true if the given node is a readable worker node.\n */\nstatic bool\nNodeIsReadableWorker(WorkerNode *node)\n{\n\treturn !NodeIsCoordinator(node) && NodeIsReadable(node);\n}\n\n\n/*\n * CompareWorkerNodes compares two pointers to worker nodes using the exact\n * same logic employed by WorkerNodeCompare.\n */\nint\nCompareWorkerNodes(const void *leftElement, const void *rightElement)\n{\n\tconst void *leftWorker = *((const void **) leftElement);\n\tconst void *rightWorker = *((const void **) rightElement);\n\tSize ignoredKeySize = 0;\n\n\tint compare = WorkerNodeCompare(leftWorker, rightWorker, ignoredKeySize);\n\n\treturn compare;\n}\n\n\n/*\n * WorkerNodeCompare compares two worker nodes by their host name and port\n * number. Two nodes that only differ by their rack locations are considered to\n * be equal to each other.\n */\nint\nWorkerNodeCompare(const void *lhsKey, const void *rhsKey, Size keySize)\n{\n\tconst WorkerNode *workerLhs = (const WorkerNode *) lhsKey;\n\tconst WorkerNode *workerRhs = (const WorkerNode *) rhsKey;\n\n\treturn NodeNamePortCompare(workerLhs->workerName, workerRhs->workerName,\n\t\t\t\t\t\t\t   workerLhs->workerPort, workerRhs->workerPort);\n}\n\n\n/*\n * WorkerNodeHashCode computes the hash code for a worker node from the node's\n * host name and port number. Nodes that only differ by their rack locations\n * hash to the same value.\n */\nuint32\nWorkerNodeHashCode(const void *key, Size keySize)\n{\n\tconst WorkerNode *worker = (const WorkerNode *) key;\n\tconst char *workerName = worker->workerName;\n\tconst uint32 *workerPort = &(worker->workerPort);\n\n\t/* standard hash function outlined in Effective Java, Item 8 */\n\tuint32 result = 17;\n\tresult = 37 * result + string_hash(workerName, WORKER_LENGTH);\n\tresult = 37 * result + tag_hash(workerPort, sizeof(uint32));\n\treturn result;\n}\n\n\n/*\n * NodeNamePortCompare implements the common logic for comparing two nodes\n * with their given nodeNames and ports.\n *\n * This function is useful for ensuring consistency of sort operations between\n * different representations of nodes in the cluster such as WorkerNode and\n * WorkerPool.\n */\nint\nNodeNamePortCompare(const char *workerLhsName, const char *workerRhsName,\n\t\t\t\t\tint workerLhsPort, int workerRhsPort)\n{\n\tint nameCompare = strncmp(workerLhsName, workerRhsName, WORKER_LENGTH);\n\tif (nameCompare != 0)\n\t{\n\t\treturn nameCompare;\n\t}\n\n\tint portCompare = workerLhsPort - workerRhsPort;\n\treturn portCompare;\n}\n\n\n/*\n * GetFirstPrimaryWorkerNode returns the primary worker node with the\n * lowest rank based on CompareWorkerNodes.\n *\n * The ranking is arbitrary, but needs to be kept consistent with IsFirstWorkerNode.\n */\nWorkerNode *\nGetFirstPrimaryWorkerNode(void)\n{\n\tList *workerNodeList = ActivePrimaryNonCoordinatorNodeList(RowShareLock);\n\tWorkerNode *firstWorkerNode = NULL;\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tif (firstWorkerNode == NULL ||\n\t\t\tCompareWorkerNodes(&workerNode, &firstWorkerNode) < 0)\n\t\t{\n\t\t\tfirstWorkerNode = workerNode;\n\t\t}\n\t}\n\n\treturn firstWorkerNode;\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/worker_shard_copy.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_shard_copy.c\n *   Functions for copying a shard to destination.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n\n#include \"commands/copy.h\"\n#include \"nodes/makefuncs.h\"\n#include \"parser/parse_relation.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/local_multi_copy.h\"\n#include \"distributed/relation_utils.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/replication_origin_session_utils.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_shard_copy.h\"\n\n/*\n * LocalCopyBuffer is used in copy callback to return the copied rows.\n * The reason this is a global variable is that we cannot pass an additional\n * argument to the copy callback.\n */\nstatic StringInfo LocalCopyBuffer;\n\ntypedef struct ShardCopyDestReceiver\n{\n\t/* public DestReceiver interface */\n\tDestReceiver pub;\n\n\t/* Destination Relation Name */\n\tList *destinationShardFullyQualifiedName;\n\n\t/* descriptor of the tuples that are sent to the worker */\n\tTupleDesc tupleDescriptor;\n\n\t/* state on how to copy out data types */\n\tCopyOutState copyOutState;\n\tFmgrInfo *columnOutputFunctions;\n\n\t/* number of tuples sent */\n\tint64 tuplesSent;\n\n\t/* destination node id */\n\tuint32_t destinationNodeId;\n\n\t/* local copy if destination shard in same node */\n\tbool useLocalCopy;\n\n\t/* EState for per-tuple memory allocation */\n\tEState *executorState;\n\n\t/*\n\t * Connection for destination shard (NULL if useLocalCopy is true)\n\t */\n\tMultiConnection *connection;\n} ShardCopyDestReceiver;\n\nstatic bool ShardCopyDestReceiverReceive(TupleTableSlot *slot, DestReceiver *dest);\nstatic void ShardCopyDestReceiverStartup(DestReceiver *dest, int operation,\n\t\t\t\t\t\t\t\t\t\t TupleDesc inputTupleDescriptor);\nstatic void ShardCopyDestReceiverShutdown(DestReceiver *destReceiver);\nstatic void ShardCopyDestReceiverDestroy(DestReceiver *destReceiver);\nstatic bool CanUseLocalCopy(uint32_t destinationNodeId);\nstatic StringInfo ConstructShardCopyStatement(List *destinationShardFullyQualifiedName,\n\t\t\t\t\t\t\t\t\t\t\t  bool\n\t\t\t\t\t\t\t\t\t\t\t  useBinaryFormat, TupleDesc tupleDesc);\nstatic void WriteLocalTuple(TupleTableSlot *slot, ShardCopyDestReceiver *copyDest);\nstatic int ReadFromLocalBufferCallback(void *outBuf, int minRead, int maxRead);\nstatic void LocalCopyToShard(ShardCopyDestReceiver *copyDest, CopyOutState\n\t\t\t\t\t\t\t localCopyOutState);\nstatic void ConnectToRemoteAndStartCopy(ShardCopyDestReceiver *copyDest);\n\n\nstatic bool\nCanUseLocalCopy(uint32_t destinationNodeId)\n{\n\t/* If destination node is same as source, use local copy */\n\treturn GetLocalNodeId() == (int32) destinationNodeId;\n}\n\n\n/* Connect to node with source shard and trigger copy start.  */\nstatic void\nConnectToRemoteAndStartCopy(ShardCopyDestReceiver *copyDest)\n{\n\tint connectionFlags = OUTSIDE_TRANSACTION;\n\tchar *currentUser = CurrentUserName();\n\tWorkerNode *workerNode = FindNodeWithNodeId(copyDest->destinationNodeId,\n\t\t\t\t\t\t\t\t\t\t\t\tfalse /* missingOk */);\n\tcopyDest->connection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t workerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t workerNode->workerPort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t currentUser,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t NULL /* database (current) */);\n\tClaimConnectionExclusively(copyDest->connection);\n\n\n\tRemoteTransactionBeginIfNecessary(copyDest->connection);\n\n\tSetupReplicationOriginRemoteSession(copyDest->connection);\n\n\n\tStringInfo copyStatement = ConstructShardCopyStatement(\n\t\tcopyDest->destinationShardFullyQualifiedName,\n\t\tcopyDest->copyOutState->binary,\n\t\tcopyDest->tupleDescriptor);\n\n\tif (!SendRemoteCommand(copyDest->connection, copyStatement->data))\n\t{\n\t\tReportConnectionError(copyDest->connection, ERROR);\n\t}\n\n\tPGresult *result = GetRemoteCommandResult(copyDest->connection,\n\t\t\t\t\t\t\t\t\t\t\t  true /* raiseInterrupts */);\n\tif (PQresultStatus(result) != PGRES_COPY_IN)\n\t{\n\t\tReportResultError(copyDest->connection, result, ERROR);\n\t}\n\n\tPQclear(result);\n}\n\n\n/*\n * CreateShardCopyDestReceiver creates a DestReceiver that copies into\n * a destinationShardFullyQualifiedName on destinationNodeId.\n */\nDestReceiver *\nCreateShardCopyDestReceiver(EState *executorState,\n\t\t\t\t\t\t\tList *destinationShardFullyQualifiedName,\n\t\t\t\t\t\t\tuint32_t destinationNodeId)\n{\n\tShardCopyDestReceiver *copyDest = (ShardCopyDestReceiver *) palloc0(\n\t\tsizeof(ShardCopyDestReceiver));\n\n\t/* set up the DestReceiver function pointers */\n\tcopyDest->pub.receiveSlot = ShardCopyDestReceiverReceive;\n\tcopyDest->pub.rStartup = ShardCopyDestReceiverStartup;\n\tcopyDest->pub.rShutdown = ShardCopyDestReceiverShutdown;\n\tcopyDest->pub.rDestroy = ShardCopyDestReceiverDestroy;\n\tcopyDest->pub.mydest = DestCopyOut;\n\tcopyDest->executorState = executorState;\n\n\tcopyDest->destinationNodeId = destinationNodeId;\n\tcopyDest->destinationShardFullyQualifiedName = destinationShardFullyQualifiedName;\n\tcopyDest->tuplesSent = 0;\n\tcopyDest->connection = NULL;\n\tcopyDest->useLocalCopy = CanUseLocalCopy(destinationNodeId);\n\n\treturn (DestReceiver *) copyDest;\n}\n\n\n/*\n * ShardCopyDestReceiverReceive implements the receiveSlot function of\n * ShardCopyDestReceiver. It takes a TupleTableSlot and sends the contents to\n * the appropriate destination node.\n */\nstatic bool\nShardCopyDestReceiverReceive(TupleTableSlot *slot, DestReceiver *dest)\n{\n\tShardCopyDestReceiver *copyDest = (ShardCopyDestReceiver *) dest;\n\n\t/*\n\t * Switch to a per-tuple memory memory context. When used in\n\t * context of Split Copy, this is a no-op as switch is already done.\n\t */\n\tEState *executorState = copyDest->executorState;\n\tMemoryContext executorTupleContext = GetPerTupleMemoryContext(executorState);\n\tMemoryContext oldContext = MemoryContextSwitchTo(executorTupleContext);\n\n\t/* If remote copy, connect lazily and initiate copy */\n\tif (copyDest->tuplesSent == 0 && (!copyDest->useLocalCopy))\n\t{\n\t\tConnectToRemoteAndStartCopy(copyDest);\n\t}\n\n\tslot_getallattrs(slot);\n\tDatum *columnValues = slot->tts_values;\n\tbool *columnNulls = slot->tts_isnull;\n\n\tCopyOutState copyOutState = copyDest->copyOutState;\n\tif (copyDest->useLocalCopy)\n\t{\n\t\t/* Setup replication origin session for local copy*/\n\n\t\tWriteLocalTuple(slot, copyDest);\n\t\tif (copyOutState->fe_msgbuf->len > LocalCopyFlushThresholdByte)\n\t\t{\n\t\t\tLocalCopyToShard(copyDest, copyOutState);\n\t\t}\n\t}\n\telse\n\t{\n\t\tresetStringInfo(copyOutState->fe_msgbuf);\n\t\tif (copyDest->copyOutState->binary && copyDest->tuplesSent == 0)\n\t\t{\n\t\t\tAppendCopyBinaryHeaders(copyDest->copyOutState);\n\t\t}\n\n\t\tAppendCopyRowData(columnValues,\n\t\t\t\t\t\t  columnNulls,\n\t\t\t\t\t\t  copyDest->tupleDescriptor,\n\t\t\t\t\t\t  copyOutState,\n\t\t\t\t\t\t  copyDest->columnOutputFunctions,\n\t\t\t\t\t\t  NULL /* columnCoercionPaths */);\n\t\tif (!PutRemoteCopyData(copyDest->connection, copyOutState->fe_msgbuf->data,\n\t\t\t\t\t\t\t   copyOutState->fe_msgbuf->len))\n\t\t{\n\t\t\tchar *destinationShardSchemaName = linitial(\n\t\t\t\tcopyDest->destinationShardFullyQualifiedName);\n\t\t\tchar *destinationShardRelationName = lsecond(\n\t\t\t\tcopyDest->destinationShardFullyQualifiedName);\n\n\t\t\tchar *errorMessage = PQerrorMessage(copyDest->connection->pgConn);\n\t\t\tereport(ERROR, (errcode(ERRCODE_IO_ERROR),\n\t\t\t\t\t\t\terrmsg(\"Failed to COPY to shard %s.%s : %s,\",\n\t\t\t\t\t\t\t\t   destinationShardSchemaName,\n\t\t\t\t\t\t\t\t   destinationShardRelationName,\n\t\t\t\t\t\t\t\t   errorMessage),\n\t\t\t\t\t\t\terrdetail(\"failed to send %d bytes %s on node %u\",\n\t\t\t\t\t\t\t\t\t  copyOutState->fe_msgbuf->len,\n\t\t\t\t\t\t\t\t\t  copyOutState->fe_msgbuf->data,\n\t\t\t\t\t\t\t\t\t  copyDest->destinationNodeId)));\n\t\t}\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\tResetPerTupleExprContext(executorState);\n\n\tcopyDest->tuplesSent++;\n\treturn true;\n}\n\n\n/*\n * ShardCopyDestReceiverStartup implements the rStartup interface of ShardCopyDestReceiver.\n */\nstatic void\nShardCopyDestReceiverStartup(DestReceiver *dest, int operation, TupleDesc\n\t\t\t\t\t\t\t inputTupleDescriptor)\n{\n\tShardCopyDestReceiver *copyDest = (ShardCopyDestReceiver *) dest;\n\tcopyDest->tupleDescriptor = inputTupleDescriptor;\n\tcopyDest->tuplesSent = 0;\n\n\tconst char *delimiterCharacter = \"\\t\";\n\tconst char *nullPrintCharacter = \"\\\\N\";\n\n\t/* define how tuples will be serialised */\n\tCopyOutState copyOutState = (CopyOutState) palloc0(sizeof(CopyOutStateData));\n\tcopyOutState->binary = EnableBinaryProtocol && CanUseBinaryCopyFormat(\n\t\tinputTupleDescriptor);\n\tcopyOutState->null_print = (char *) nullPrintCharacter;\n\tcopyOutState->null_print_client = (char *) nullPrintCharacter;\n\tcopyOutState->fe_msgbuf = makeStringInfo();\n\tcopyOutState->delim = (char *) delimiterCharacter;\n\tcopyOutState->rowcontext = GetPerTupleMemoryContext(copyDest->executorState);\n\tcopyDest->columnOutputFunctions = ColumnOutputFunctions(inputTupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcopyOutState->binary);\n\tcopyDest->copyOutState = copyOutState;\n\tif (copyDest->useLocalCopy)\n\t{\n\t\t/* Setup replication origin session for local copy*/\n\t\tSetupReplicationOriginLocalSession();\n\t}\n}\n\n\n/*\n * ShardCopyDestReceiverShutdown implements the rShutdown interface of\n * ShardCopyDestReceiver. It ends all open COPY operations, copying any pending\n * data in buffer.\n */\nstatic void\nShardCopyDestReceiverShutdown(DestReceiver *dest)\n{\n\tShardCopyDestReceiver *copyDest = (ShardCopyDestReceiver *) dest;\n\n\tif (copyDest->useLocalCopy)\n\t{\n\t\tif (copyDest->copyOutState != NULL &&\n\t\t\tcopyDest->copyOutState->fe_msgbuf->len > 0)\n\t\t{\n\t\t\t/* end the COPY input */\n\t\t\tLocalCopyToShard(copyDest, copyDest->copyOutState);\n\t\t}\n\t}\n\telse if (copyDest->connection != NULL)\n\t{\n\t\tresetStringInfo(copyDest->copyOutState->fe_msgbuf);\n\t\tif (copyDest->copyOutState->binary)\n\t\t{\n\t\t\tAppendCopyBinaryFooters(copyDest->copyOutState);\n\t\t}\n\n\t\t/* end the COPY input */\n\t\tif (!PutRemoteCopyEnd(copyDest->connection, NULL /* errormsg */))\n\t\t{\n\t\t\tchar *destinationShardSchemaName = linitial(\n\t\t\t\tcopyDest->destinationShardFullyQualifiedName);\n\t\t\tchar *destinationShardRelationName = lsecond(\n\t\t\t\tcopyDest->destinationShardFullyQualifiedName);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_IO_ERROR),\n\t\t\t\t\t\t\terrmsg(\"Failed to COPY to destination shard %s.%s\",\n\t\t\t\t\t\t\t\t   destinationShardSchemaName,\n\t\t\t\t\t\t\t\t   destinationShardRelationName),\n\t\t\t\t\t\t\terrdetail(\"failed to send %d bytes %s on node %u\",\n\t\t\t\t\t\t\t\t\t  copyDest->copyOutState->fe_msgbuf->len,\n\t\t\t\t\t\t\t\t\t  copyDest->copyOutState->fe_msgbuf->data,\n\t\t\t\t\t\t\t\t\t  copyDest->destinationNodeId)));\n\t\t}\n\n\t\t/* check whether there were any COPY errors */\n\t\tPGresult *result = GetRemoteCommandResult(copyDest->connection,\n\t\t\t\t\t\t\t\t\t\t\t\t  true /* raiseInterrupts */);\n\t\tif (PQresultStatus(result) != PGRES_COMMAND_OK)\n\t\t{\n\t\t\tReportCopyError(copyDest->connection, result);\n\t\t}\n\n\t\tPQclear(result);\n\t\tForgetResults(copyDest->connection);\n\n\t\tResetReplicationOriginRemoteSession(copyDest->connection);\n\n\t\tCloseConnection(copyDest->connection);\n\t}\n}\n\n\n/*\n * ShardCopyDestReceiverDestroy frees the DestReceiver.\n */\nstatic void\nShardCopyDestReceiverDestroy(DestReceiver *dest)\n{\n\tShardCopyDestReceiver *copyDest = (ShardCopyDestReceiver *) dest;\n\tif (copyDest->useLocalCopy)\n\t{\n\t\tResetReplicationOriginLocalSession();\n\t}\n\n\tif (copyDest->copyOutState)\n\t{\n\t\tpfree(copyDest->copyOutState);\n\t}\n\n\tif (copyDest->columnOutputFunctions)\n\t{\n\t\tpfree(copyDest->columnOutputFunctions);\n\t}\n\n\tpfree(copyDest);\n}\n\n\n/*\n *  CopyableColumnNamesFromTupleDesc function creates and returns a comma seperated column names string  to be used in COPY\n *  and SELECT statements when copying a table. The COPY and SELECT statements should filter out the GENERATED columns since COPY\n *  statement fails to handle them. Iterating over the attributes of the table we also need to skip the dropped columns.\n */\nconst char *\nCopyableColumnNamesFromTupleDesc(TupleDesc tupDesc)\n{\n\tStringInfo columnList = makeStringInfo();\n\tbool firstInList = true;\n\n\tfor (int i = 0; i < tupDesc->natts; i++)\n\t{\n\t\tForm_pg_attribute att = TupleDescAttr(tupDesc, i);\n\t\tif (att->attgenerated || att->attisdropped)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tif (!firstInList)\n\t\t{\n\t\t\tappendStringInfo(columnList, \",\");\n\t\t}\n\n\t\tfirstInList = false;\n\n\t\tappendStringInfo(columnList, \"%s\", quote_identifier(NameStr(att->attname)));\n\t}\n\n\treturn columnList->data;\n}\n\n\n/*\n *  CopyableColumnNamesFromRelationName function is a wrapper for CopyableColumnNamesFromTupleDesc.\n */\nconst char *\nCopyableColumnNamesFromRelationName(const char *schemaName, const char *relationName)\n{\n\tOid namespaceOid = get_namespace_oid(schemaName, true);\n\n\tOid relationId = get_relname_relid(relationName, namespaceOid);\n\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\n\tTupleDesc tupleDesc = RelationGetDescr(relation);\n\n\tconst char *columnList = CopyableColumnNamesFromTupleDesc(tupleDesc);\n\n\trelation_close(relation, NoLock);\n\n\treturn columnList;\n}\n\n\n/*\n * ConstructShardCopyStatement constructs the text of a COPY statement\n * for copying into a result table\n */\nstatic StringInfo\nConstructShardCopyStatement(List *destinationShardFullyQualifiedName, bool\n\t\t\t\t\t\t\tuseBinaryFormat,\n\t\t\t\t\t\t\tTupleDesc tupleDesc)\n{\n\tchar *destinationShardSchemaName = linitial(destinationShardFullyQualifiedName);\n\tchar *destinationShardRelationName = lsecond(destinationShardFullyQualifiedName);\n\n\n\tStringInfo command = makeStringInfo();\n\n\tconst char *columnList = CopyableColumnNamesFromTupleDesc(tupleDesc);\n\n\tappendStringInfo(command, \"COPY %s.%s (%s) FROM STDIN\",\n\t\t\t\t\t quote_identifier(destinationShardSchemaName), quote_identifier(\n\t\t\t\t\t\t destinationShardRelationName), columnList);\n\n\tif (useBinaryFormat)\n\t{\n\t\tappendStringInfo(command, \" WITH (format binary);\");\n\t}\n\telse\n\t{\n\t\tappendStringInfo(command, \";\");\n\t}\n\n\treturn command;\n}\n\n\n/* Write Tuple to Local Shard. */\nstatic void\nWriteLocalTuple(TupleTableSlot *slot, ShardCopyDestReceiver *copyDest)\n{\n\tCopyOutState localCopyOutState = copyDest->copyOutState;\n\n\t/*\n\t * Since we are doing a local copy, the following statements should\n\t * use local execution to see the changes\n\t */\n\tSetLocalExecutionStatus(LOCAL_EXECUTION_REQUIRED);\n\n\tbool isBinaryCopy = localCopyOutState->binary;\n\tbool shouldAddBinaryHeaders = (isBinaryCopy &&\n\t\t\t\t\t\t\t\t   localCopyOutState->fe_msgbuf->len == 0);\n\tif (shouldAddBinaryHeaders)\n\t{\n\t\tAppendCopyBinaryHeaders(localCopyOutState);\n\t}\n\n\tDatum *columnValues = slot->tts_values;\n\tbool *columnNulls = slot->tts_isnull;\n\tFmgrInfo *columnOutputFunctions = copyDest->columnOutputFunctions;\n\n\tAppendCopyRowData(columnValues, columnNulls, copyDest->tupleDescriptor,\n\t\t\t\t\t  localCopyOutState, columnOutputFunctions,\n\t\t\t\t\t  NULL /* columnCoercionPaths */);\n}\n\n\n/*\n * LocalCopyToShard performs local copy for the given destination shard.\n */\nstatic void\nLocalCopyToShard(ShardCopyDestReceiver *copyDest, CopyOutState localCopyOutState)\n{\n\tbool isBinaryCopy = localCopyOutState->binary;\n\tif (isBinaryCopy)\n\t{\n\t\tAppendCopyBinaryFooters(localCopyOutState);\n\t}\n\n\t/*\n\t * Set the buffer as a global variable to allow ReadFromLocalBufferCallback\n\t * to read from it. We cannot pass additional arguments to\n\t * ReadFromLocalBufferCallback.\n\t */\n\tLocalCopyBuffer = localCopyOutState->fe_msgbuf;\n\n\tchar *destinationShardSchemaName = linitial(\n\t\tcopyDest->destinationShardFullyQualifiedName);\n\tchar *destinationShardRelationName = lsecond(\n\t\tcopyDest->destinationShardFullyQualifiedName);\n\n\tOid destinationSchemaOid = get_namespace_oid(destinationShardSchemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t false /* missing_ok */);\n\tOid destinationShardOid = get_relname_relid(destinationShardRelationName,\n\t\t\t\t\t\t\t\t\t\t\t\tdestinationSchemaOid);\n\n\tDefElem *binaryFormatOption = NULL;\n\tif (isBinaryCopy)\n\t{\n\t\tbinaryFormatOption = makeDefElem(\"format\", (Node *) makeString(\"binary\"), -1);\n\t}\n\n\tRelation shard = table_open(destinationShardOid, RowExclusiveLock);\n\tParseState *pState = make_parsestate(NULL /* parentParseState */);\n\t(void) addRangeTableEntryForRelation(pState, shard, AccessShareLock,\n\t\t\t\t\t\t\t\t\t\t NULL /* alias */, false /* inh */,\n\t\t\t\t\t\t\t\t\t\t false /* inFromCl */);\n\n\tList *options = (isBinaryCopy) ? list_make1(binaryFormatOption) : NULL;\n\tCopyFromState cstate = BeginCopyFrom(pState, shard,\n\t\t\t\t\t\t\t\t\t\t NULL /* whereClause */,\n\t\t\t\t\t\t\t\t\t\t NULL /* fileName */,\n\t\t\t\t\t\t\t\t\t\t false /* is_program */,\n\t\t\t\t\t\t\t\t\t\t ReadFromLocalBufferCallback,\n\t\t\t\t\t\t\t\t\t\t NULL /* attlist (NULL is all columns) */,\n\t\t\t\t\t\t\t\t\t\t options);\n\tCopyFrom(cstate);\n\tEndCopyFrom(cstate);\n\tresetStringInfo(localCopyOutState->fe_msgbuf);\n\n\ttable_close(shard, NoLock);\n\tfree_parsestate(pState);\n}\n\n\n/*\n * ReadFromLocalBufferCallback is the copy callback.\n * It always tries to copy maxRead bytes.\n */\nstatic int\nReadFromLocalBufferCallback(void *outBuf, int minRead, int maxRead)\n{\n\tint bytesRead = 0;\n\tint avail = LocalCopyBuffer->len - LocalCopyBuffer->cursor;\n\tint bytesToRead = Min(avail, maxRead);\n\tif (bytesToRead > 0)\n\t{\n\t\tmemcpy_s(outBuf, bytesToRead,\n\t\t\t\t &LocalCopyBuffer->data[LocalCopyBuffer->cursor], bytesToRead);\n\t}\n\tbytesRead += bytesToRead;\n\tLocalCopyBuffer->cursor += bytesToRead;\n\n\treturn bytesRead;\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/worker_split_copy_udf.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_split_copy_udf.c\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/distribution_column.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/worker_shard_copy.h\"\n\nPG_FUNCTION_INFO_V1(worker_split_copy);\n\ntypedef struct SplitCopyInfo\n{\n\tuint64 destinationShardId;              /* destination shard id */\n\tDatum destinationShardMinHashValue;     /* min hash value of destination shard */\n\tDatum destinationShardMaxHashValue;     /* max hash value of destination shard */\n\tuint32_t destinationShardNodeId;        /* node where split child shard is to be placed */\n} SplitCopyInfo;\n\nstatic void ParseSplitCopyInfoDatum(Datum splitCopyInfoDatum,\n\t\t\t\t\t\t\t\t\tSplitCopyInfo **splitCopyInfo);\nstatic DestReceiver ** CreateShardCopyDestReceivers(EState *estate,\n\t\t\t\t\t\t\t\t\t\t\t\t\tShardInterval *\n\t\t\t\t\t\t\t\t\t\t\t\t\tshardIntervalToSplitCopy,\n\t\t\t\t\t\t\t\t\t\t\t\t\tList *splitCopyInfoList);\nstatic DestReceiver *  CreatePartitionedSplitCopyDestReceiver(EState *executor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ShardInterval *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardIntervalToSplitCopy,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  char *partitionColumnName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  List *splitCopyInfoList);\nstatic void BuildMinMaxRangeArrays(List *splitCopyInfoList, ArrayType **minValueArray,\n\t\t\t\t\t\t\t\t   ArrayType **maxValueArray);\nstatic char * TraceWorkerSplitCopyUdf(char *sourceShardToCopySchemaName,\n\t\t\t\t\t\t\t\t\t  char *sourceShardToCopyPrefix,\n\t\t\t\t\t\t\t\t\t  char *sourceShardToCopyQualifiedName,\n\t\t\t\t\t\t\t\t\t  List *splitCopyInfoList);\n\n/*\n * worker_split_copy(source_shard_id bigint, splitCopyInfo pg_catalog.split_copy_info[])\n * UDF to split copy shard to list of destination shards.\n * 'source_shard_id' : Source ShardId to split copy.\n * 'splitCopyInfos'  : Array of Split Copy Info (destination_shard's id, min/max ranges and node_id)\n */\nDatum\nworker_split_copy(PG_FUNCTION_ARGS)\n{\n\tuint64 shardIdToSplitCopy = DatumGetUInt64(PG_GETARG_DATUM(0));\n\tShardInterval *shardIntervalToSplitCopy = LoadShardInterval(shardIdToSplitCopy);\n\n\ttext *partitionColumnText = PG_GETARG_TEXT_P(1);\n\tchar *partitionColumnName = text_to_cstring(partitionColumnText);\n\n\tArrayType *splitCopyInfoArrayObject = PG_GETARG_ARRAYTYPE_P(2);\n\tbool arrayHasNull = ARR_HASNULL(splitCopyInfoArrayObject);\n\tif (arrayHasNull)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),\n\t\t\t\t\t\terrmsg(\"pg_catalog.split_copy_info array \"\n\t\t\t\t\t\t\t   \"cannot contain null values\")));\n\t}\n\n\tconst int slice_ndim = 0;\n\tArrayMetaState *mState = NULL;\n\tArrayIterator copyInfo_iterator = array_create_iterator(splitCopyInfoArrayObject,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tslice_ndim,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmState);\n\tDatum copyInfoDatum = 0;\n\tbool isnull = false;\n\tList *splitCopyInfoList = NIL;\n\twhile (array_iterate(copyInfo_iterator, &copyInfoDatum, &isnull))\n\t{\n\t\tSplitCopyInfo *splitCopyInfo = NULL;\n\t\tParseSplitCopyInfoDatum(copyInfoDatum, &splitCopyInfo);\n\n\t\tsplitCopyInfoList = lappend(splitCopyInfoList, splitCopyInfo);\n\t}\n\n\tEState *executor = CreateExecutorState();\n\tDestReceiver *splitCopyDestReceiver = CreatePartitionedSplitCopyDestReceiver(executor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardIntervalToSplitCopy,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t partitionColumnName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t splitCopyInfoList);\n\n\tOid sourceShardToCopySchemaOId = get_rel_namespace(\n\t\tshardIntervalToSplitCopy->relationId);\n\tchar *sourceShardToCopySchemaName = get_namespace_name(sourceShardToCopySchemaOId);\n\tchar *sourceShardPrefix = get_rel_name(shardIntervalToSplitCopy->relationId);\n\tchar *sourceShardToCopyName = pstrdup(sourceShardPrefix);\n\tAppendShardIdToName(&sourceShardToCopyName, shardIdToSplitCopy);\n\tchar *sourceShardToCopyQualifiedName = quote_qualified_identifier(\n\t\tsourceShardToCopySchemaName,\n\t\tsourceShardToCopyName);\n\n\tereport(LOG, (errmsg(\"%s\", TraceWorkerSplitCopyUdf(sourceShardToCopySchemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   sourceShardPrefix,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   sourceShardToCopyQualifiedName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   splitCopyInfoList))));\n\n\tStringInfo selectShardQueryForCopy = makeStringInfo();\n\tconst char *columnList = CopyableColumnNamesFromRelationName(\n\t\tsourceShardToCopySchemaName,\n\t\tsourceShardToCopyName);\n\n\tappendStringInfo(selectShardQueryForCopy,\n\t\t\t\t\t \"SELECT %s FROM %s;\", columnList,\n\t\t\t\t\t sourceShardToCopyQualifiedName);\n\n\tParamListInfo params = NULL;\n\tExecuteQueryStringIntoDestReceiver(selectShardQueryForCopy->data, params,\n\t\t\t\t\t\t\t\t\t   (DestReceiver *) splitCopyDestReceiver);\n\n\tFreeExecutorState(executor);\n\n\tPG_RETURN_VOID();\n}\n\n\n/* Trace split copy udf */\nstatic char *\nTraceWorkerSplitCopyUdf(char *sourceShardToCopySchemaName,\n\t\t\t\t\t\tchar *sourceShardToCopyPrefix,\n\t\t\t\t\t\tchar *sourceShardToCopyQualifiedName,\n\t\t\t\t\t\tList *splitCopyInfoList)\n{\n\tStringInfo splitCopyTrace = makeStringInfo();\n\tappendStringInfo(splitCopyTrace, \"performing copy from shard %s to [\",\n\t\t\t\t\t sourceShardToCopyQualifiedName);\n\n\t/* split copy always has at least two destinations */\n\tint index = 1;\n\tint splitWayCount = list_length(splitCopyInfoList);\n\tSplitCopyInfo *splitCopyInfo = NULL;\n\tforeach_declared_ptr(splitCopyInfo, splitCopyInfoList)\n\t{\n\t\tchar *shardNameCopy = pstrdup(sourceShardToCopyPrefix);\n\t\tAppendShardIdToName(&shardNameCopy, splitCopyInfo->destinationShardId);\n\n\t\tchar *shardNameCopyQualifiedName = quote_qualified_identifier(\n\t\t\tsourceShardToCopySchemaName,\n\t\t\tshardNameCopy);\n\n\t\tappendStringInfo(splitCopyTrace, \"%s (nodeId: %u)\", shardNameCopyQualifiedName,\n\t\t\t\t\t\t splitCopyInfo->destinationShardNodeId);\n\t\tpfree(shardNameCopy);\n\n\t\tif (index < splitWayCount)\n\t\t{\n\t\t\tappendStringInfo(splitCopyTrace, \", \");\n\t\t}\n\n\t\tindex++;\n\t}\n\n\tappendStringInfo(splitCopyTrace, \"]\");\n\n\treturn splitCopyTrace->data;\n}\n\n\n/* Parse a single SplitCopyInfo Tuple */\nstatic void\nParseSplitCopyInfoDatum(Datum splitCopyInfoDatum, SplitCopyInfo **splitCopyInfo)\n{\n\tHeapTupleHeader dataTuple = DatumGetHeapTupleHeader(splitCopyInfoDatum);\n\n\tSplitCopyInfo *copyInfo = palloc0(sizeof(SplitCopyInfo));\n\n\tbool isnull = false;\n\tDatum destinationShardIdDatum = GetAttributeByName(dataTuple, \"destination_shard_id\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   &isnull);\n\tif (isnull)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"destination_shard_id for pg_catalog.split_copy_info cannot be null.\")));\n\t}\n\tcopyInfo->destinationShardId = DatumGetUInt64(destinationShardIdDatum);\n\n\tDatum minValueDatum = GetAttributeByName(dataTuple, \"destination_shard_min_value\",\n\t\t\t\t\t\t\t\t\t\t\t &isnull);\n\tif (isnull)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"destination_shard_min_value for pg_catalog.split_copy_info cannot be null.\")));\n\t}\n\tcopyInfo->destinationShardMinHashValue = minValueDatum;\n\n\tDatum maxValueDatum = GetAttributeByName(dataTuple, \"destination_shard_max_value\",\n\t\t\t\t\t\t\t\t\t\t\t &isnull);\n\tif (isnull)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"destination_shard_max_value for pg_catalog.split_copy_info cannot be null.\")));\n\t}\n\tcopyInfo->destinationShardMaxHashValue = maxValueDatum;\n\n\tDatum nodeIdDatum = GetAttributeByName(dataTuple, \"destination_shard_node_id\",\n\t\t\t\t\t\t\t\t\t\t   &isnull);\n\tif (isnull)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"destination_shard_node_id for pg_catalog.split_copy_info cannot be null.\")));\n\t}\n\tcopyInfo->destinationShardNodeId = DatumGetInt32(nodeIdDatum);\n\n\t*splitCopyInfo = copyInfo;\n}\n\n\n/* Build 'min/max' hash range arrays for PartitionedResultDestReceiver */\nstatic void\nBuildMinMaxRangeArrays(List *splitCopyInfoList, ArrayType **minValueArray,\n\t\t\t\t\t   ArrayType **maxValueArray)\n{\n\tint partitionCount = list_length(splitCopyInfoList);\n\n\tDatum *minValues = palloc0(partitionCount * sizeof(Datum));\n\tbool *minValueNulls = palloc0(partitionCount * sizeof(bool));\n\tDatum *maxValues = palloc0(partitionCount * sizeof(Datum));\n\tbool *maxValueNulls = palloc0(partitionCount * sizeof(bool));\n\n\tSplitCopyInfo *splitCopyInfo = NULL;\n\tint index = 0;\n\tforeach_declared_ptr(splitCopyInfo, splitCopyInfoList)\n\t{\n\t\tminValues[index] = splitCopyInfo->destinationShardMinHashValue;\n\t\tmaxValues[index] = splitCopyInfo->destinationShardMaxHashValue;\n\n\t\t/* Caller enforces that min/max values will be not-null */\n\t\tminValueNulls[index] = false;\n\t\tmaxValueNulls[index] = false;\n\t\tindex++;\n\t}\n\n\t*minValueArray = CreateArrayFromDatums(minValues, minValueNulls, partitionCount,\n\t\t\t\t\t\t\t\t\t\t   TEXTOID);\n\t*maxValueArray = CreateArrayFromDatums(maxValues, maxValueNulls, partitionCount,\n\t\t\t\t\t\t\t\t\t\t   TEXTOID);\n}\n\n\n/*\n * Create underlying ShardCopyDestReceivers for PartitionedResultDestReceiver\n * Each ShardCopyDestReceivers will be responsible for copying tuples from source shard,\n * that fall under its min/max range, to specified destination shard.\n */\nstatic DestReceiver **\nCreateShardCopyDestReceivers(EState *estate, ShardInterval *shardIntervalToSplitCopy,\n\t\t\t\t\t\t\t List *splitCopyInfoList)\n{\n\tDestReceiver **shardCopyDests = palloc0(splitCopyInfoList->length *\n\t\t\t\t\t\t\t\t\t\t\tsizeof(DestReceiver *));\n\n\tSplitCopyInfo *splitCopyInfo = NULL;\n\tint index = 0;\n\tchar *sourceShardNamePrefix = get_rel_name(shardIntervalToSplitCopy->relationId);\n\tforeach_declared_ptr(splitCopyInfo, splitCopyInfoList)\n\t{\n\t\tOid destinationShardSchemaOid = get_rel_namespace(\n\t\t\tshardIntervalToSplitCopy->relationId);\n\t\tchar *destinationShardSchemaName = get_namespace_name(destinationShardSchemaOid);\n\t\tchar *destinationShardNameCopy = pstrdup(sourceShardNamePrefix);\n\t\tAppendShardIdToName(&destinationShardNameCopy, splitCopyInfo->destinationShardId);\n\n\t\tDestReceiver *shardCopyDest = CreateShardCopyDestReceiver(\n\t\t\testate,\n\t\t\tlist_make2(destinationShardSchemaName, destinationShardNameCopy),\n\t\t\tsplitCopyInfo->destinationShardNodeId);\n\n\t\tshardCopyDests[index] = shardCopyDest;\n\t\tindex++;\n\t}\n\n\treturn shardCopyDests;\n}\n\n\n/* Create PartitionedSplitCopyDestReceiver along with underlying ShardCopyDestReceivers */\nstatic DestReceiver *\nCreatePartitionedSplitCopyDestReceiver(EState *estate,\n\t\t\t\t\t\t\t\t\t   ShardInterval *shardIntervalToSplitCopy,\n\t\t\t\t\t\t\t\t\t   char *partitionColumnName,\n\t\t\t\t\t\t\t\t\t   List *splitCopyInfoList)\n{\n\t/* Create underlying ShardCopyDestReceivers */\n\tDestReceiver **shardCopyDestReceivers = CreateShardCopyDestReceivers(\n\t\testate,\n\t\tshardIntervalToSplitCopy,\n\t\tsplitCopyInfoList);\n\n\t/* construct an artificial CitusTableCacheEntry for routing tuples to appropriate ShardCopyReceiver */\n\tArrayType *minValuesArray = NULL;\n\tArrayType *maxValuesArray = NULL;\n\tBuildMinMaxRangeArrays(splitCopyInfoList, &minValuesArray, &maxValuesArray);\n\n\t/* we currently only support hash-distribution */\n\tchar partitionMethod = DISTRIBUTE_BY_HASH;\n\n\t/* synthetically build the partition column by looking at shard columns */\n\tuint64 shardId = shardIntervalToSplitCopy->shardId;\n\tbool missingOK = false;\n\tOid shardRelationId = LookupShardRelationFromCatalog(shardId, missingOK);\n\tVar *partitionColumn = BuildDistributionKeyFromColumnName(shardRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionColumnName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  AccessShareLock);\n\n\tCitusTableCacheEntry *shardSearchInfo =\n\t\tQueryTupleShardSearchInfo(minValuesArray, maxValuesArray,\n\t\t\t\t\t\t\t\t  partitionMethod, partitionColumn);\n\n\t/* Construct PartitionedResultDestReceiver from cache and underlying ShardCopyDestReceivers */\n\tint partitionColumnIndex = partitionColumn->varattno - 1;\n\tint partitionCount = splitCopyInfoList->length;\n\tDestReceiver *splitCopyDestReceiver = CreatePartitionedResultDestReceiver(\n\t\tpartitionColumnIndex,\n\t\tpartitionCount,\n\t\tshardSearchInfo,\n\t\tshardCopyDestReceivers,\n\t\ttrue /* lazyStartup */,\n\t\tfalse /* allowNullPartitionColumnValues */);\n\n\treturn splitCopyDestReceiver;\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/worker_split_shard_release_dsm_udf.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_split_shard_release_dsm.c\n *    This file contains functions to release dynamic shared memory segment\n *    allocated during split workflow.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/shardsplit_shared_memory.h\"\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(worker_split_shard_release_dsm);\n\nDatum\nworker_split_shard_release_dsm(PG_FUNCTION_ARGS)\n{\n\tReleaseSharedMemoryOfShardSplitInfo();\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/operations/worker_split_shard_replication_setup_udf.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_split_shard_replication_setup_udf.c\n *    This file contains functions to setup information about list of shards\n *    that are being split.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"commands/dbcommands.h\"\n#include \"common/hashfn.h\"\n#include \"postmaster/postmaster.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/distribution_column.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/shard_utils.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/shardsplit_logical_replication.h\"\n#include \"distributed/shardsplit_shared_memory.h\"\n#include \"distributed/tuplestore.h\"\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(worker_split_shard_replication_setup);\n\nstatic HTAB *ShardInfoHashMap = NULL;\n\n/* Function declarations */\nstatic void ParseShardSplitInfoFromDatum(Datum shardSplitInfoDatum,\n\t\t\t\t\t\t\t\t\t\t uint64 *sourceShardId,\n\t\t\t\t\t\t\t\t\t\t char **partitionColumnName,\n\t\t\t\t\t\t\t\t\t\t uint64 *childShardId,\n\t\t\t\t\t\t\t\t\t\t int32 *minValue,\n\t\t\t\t\t\t\t\t\t\t int32 *maxValue,\n\t\t\t\t\t\t\t\t\t\t int32 *nodeId);\n\nstatic ShardSplitInfo * CreateShardSplitInfo(uint64 sourceShardIdToSplit,\n\t\t\t\t\t\t\t\t\t\t\t char *partitionColumnName,\n\t\t\t\t\t\t\t\t\t\t\t uint64 desSplitChildShardId,\n\t\t\t\t\t\t\t\t\t\t\t int32 minValue,\n\t\t\t\t\t\t\t\t\t\t\t int32 maxValue,\n\t\t\t\t\t\t\t\t\t\t\t int32 nodeId);\nstatic void AddShardSplitInfoEntryForNodeInMap(ShardSplitInfo *shardSplitInfo);\nstatic void PopulateShardSplitInfoInSM(ShardSplitInfoSMHeader *shardSplitInfoSMHeader,\n\t\t\t\t\t\t\t\t\t   OperationId operationId);\n\nstatic void ReturnReplicationSlotInfo(Tuplestorestate *tupleStore,\n\t\t\t\t\t\t\t\t\t  TupleDesc tupleDescriptor,\n\t\t\t\t\t\t\t\t\t  OperationId operationId);\n\n/*\n * worker_split_shard_replication_setup UDF creates in-memory data structures\n * to store the meta information about the shard undergoing split and new split\n * children along with their placements. This info is required during the catch up\n * phase of logical replication.\n * This meta information is stored in a shared memory segment and accessed\n * by logical decoding plugin.\n *\n * Split information is given by user as an Array of custom data type 'pg_catalog.split_shard_info'.\n * (worker_split_shard_replication_setup(pg_catalog.split_shard_info[]))\n *\n * Fields of custom data type 'pg_catalog.split_shard_info':\n * source_shard_id - id of the shard that is undergoing a split\n *\n * distribution_column - Distribution column name\n *\n * child_shard_id  - id of shard that stores a specific range of values\n *                   belonging to sourceShardId(parent)\n *\n * shard_min_value - Lower bound(inclusive) of hash value which childShard stores\n *\n * shard_max_value - Upper bound(inclusive) of hash value which childShard stores\n *\n * node_id         - Node where the childShardId is located\n *\n * The function parses the data and builds routing map with key for each distinct\n * <nodeId, tableOwner> pair. Multiple shards can be placed on the same destination node.\n * Source and destination nodes can be same too.\n *\n * There is a 1-1 mapping between a (table owner, node) and replication slot. One replication\n * slot takes care of replicating changes for all shards belonging to the same owner on a particular node.\n *\n * During the replication phase, WAL senders will attach to the shared memory\n * populated by current UDF. It routes the tuple from the source shard to the appropriate destination\n * shard for which the respective slot is responsible.\n */\nDatum\nworker_split_shard_replication_setup(PG_FUNCTION_ARGS)\n{\n\tif (PG_ARGISNULL(0))\n\t{\n\t\tereport(ERROR, (errmsg(\"split_shard_info array cannot be NULL\")));\n\t}\n\n\tArrayType *shardInfoArrayObject = PG_GETARG_ARRAYTYPE_P(0);\n\tif (array_contains_nulls(shardInfoArrayObject))\n\t{\n\t\tereport(ERROR, (errmsg(\"Unexpectedly shard info array contains a null value\")));\n\t}\n\n\tOperationId operationId = DatumGetUInt64(PG_GETARG_DATUM(1));\n\n\t/* SetupMap */\n\tShardInfoHashMap = CreateSimpleHash(NodeAndOwner, GroupedShardSplitInfos);\n\n\tint shardSplitInfoCount = 0;\n\n\tArrayIterator shardInfo_iterator = array_create_iterator(shardInfoArrayObject, 0,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t NULL);\n\tDatum shardInfoDatum = 0;\n\tbool isnull = false;\n\twhile (array_iterate(shardInfo_iterator, &shardInfoDatum, &isnull))\n\t{\n\t\tuint64 sourceShardId = 0;\n\t\tchar *partitionColumnName = NULL;\n\t\tuint64 childShardId = 0;\n\t\tint32 minValue = 0;\n\t\tint32 maxValue = 0;\n\t\tint32 nodeId = 0;\n\n\t\tParseShardSplitInfoFromDatum(shardInfoDatum, &sourceShardId,\n\t\t\t\t\t\t\t\t\t &partitionColumnName, &childShardId,\n\t\t\t\t\t\t\t\t\t &minValue, &maxValue, &nodeId);\n\n\t\tShardSplitInfo *shardSplitInfo = CreateShardSplitInfo(\n\t\t\tsourceShardId,\n\t\t\tpartitionColumnName,\n\t\t\tchildShardId,\n\t\t\tminValue,\n\t\t\tmaxValue,\n\t\t\tnodeId);\n\n\t\tAddShardSplitInfoEntryForNodeInMap(shardSplitInfo);\n\t\tshardSplitInfoCount++;\n\t}\n\n\tdsm_handle dsmHandle;\n\tShardSplitInfoSMHeader *splitShardInfoSMHeader =\n\t\tCreateSharedMemoryForShardSplitInfo(shardSplitInfoCount, &dsmHandle);\n\n\tPopulateShardSplitInfoInSM(splitShardInfoSMHeader, operationId);\n\n\t/* store handle in statically allocated shared memory*/\n\tStoreShardSplitSharedMemoryHandle(dsmHandle);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\tReturnReplicationSlotInfo(tupleStore, tupleDescriptor, operationId);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * CreateShardSplitInfo function constructs ShardSplitInfo data structure\n * with appropriate OIs' for source and destination relation.\n *\n * sourceShardIdToSplit - Existing shardId which has a valid entry in cache and catalogue\n * partitionColumnName  - Name of column to use for partitioning\n * desSplitChildShardId - New split child shard which doesn't have an entry in metacache yet\n * minValue\t\t\t\t- Minimum hash value for desSplitChildShardId\n * maxValue\t\t\t\t- Maximum hash value for desSplitChildShardId\n * nodeId\t\t\t\t- NodeId where\n * However we can use shard ID and construct qualified shardName.\n */\nShardSplitInfo *\nCreateShardSplitInfo(uint64 sourceShardIdToSplit,\n\t\t\t\t\t char *partitionColumnName,\n\t\t\t\t\t uint64 desSplitChildShardId,\n\t\t\t\t\t int32 minValue,\n\t\t\t\t\t int32 maxValue,\n\t\t\t\t\t int32 nodeId)\n{\n\tShardInterval *shardIntervalToSplit = LoadShardInterval(sourceShardIdToSplit);\n\n\t/* If metadata is not synced, we cannot proceed further as split work flow assumes\n\t * metadata to be synced on worker node hosting source shard to split.\n\t */\n\tif (shardIntervalToSplit == NULL)\n\t{\n\t\tereport(ERROR,\n\t\t\t\terrmsg(\n\t\t\t\t\t\"Could not find metadata corresponding to source shard id: %ld. \"\n\t\t\t\t\t\"Split workflow assumes metadata to be synced across \"\n\t\t\t\t\t\"worker nodes hosting source shards.\", sourceShardIdToSplit));\n\t}\n\n\t/* Oid of distributed table */\n\tOid citusTableOid = shardIntervalToSplit->relationId;\n\tOid sourceShardToSplitOid = GetTableLocalShardOid(citusTableOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  sourceShardIdToSplit);\n\n\t/* Oid of dummy table at the source */\n\tOid destSplitChildShardOid = GetTableLocalShardOid(citusTableOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   desSplitChildShardId);\n\n\tif (citusTableOid == InvalidOid ||\n\t\tsourceShardToSplitOid == InvalidOid ||\n\t\tdestSplitChildShardOid == InvalidOid)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t\terrmsg(\"Invalid citusTableOid:%u, \"\n\t\t\t\t\t\t\t   \"sourceShardToSplitOid:%u, \"\n\t\t\t\t\t\t\t   \"destSplitChildShardOid:%u \",\n\t\t\t\t\t\t\t   citusTableOid,\n\t\t\t\t\t\t\t   sourceShardToSplitOid,\n\t\t\t\t\t\t\t   destSplitChildShardOid)));\n\t}\n\n\t/* determine the partition column in the tuple descriptor */\n\tVar *partitionColumn = BuildDistributionKeyFromColumnName(sourceShardToSplitOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionColumnName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  AccessShareLock);\n\tif (partitionColumn == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t\terrmsg(\"Invalid Partition Column\")));\n\t}\n\tint partitionColumnIndex = partitionColumn->varattno - 1;\n\n\tShardSplitInfo *shardSplitInfo = palloc0(sizeof(ShardSplitInfo));\n\tshardSplitInfo->distributedTableOid = citusTableOid;\n\tshardSplitInfo->partitionColumnIndex = partitionColumnIndex;\n\tshardSplitInfo->sourceShardOid = sourceShardToSplitOid;\n\tshardSplitInfo->splitChildShardOid = destSplitChildShardOid;\n\tshardSplitInfo->shardMinValue = minValue;\n\tshardSplitInfo->shardMaxValue = maxValue;\n\tshardSplitInfo->nodeId = nodeId;\n\tshardSplitInfo->sourceShardId = sourceShardIdToSplit;\n\tshardSplitInfo->splitChildShardId = desSplitChildShardId;\n\n\treturn shardSplitInfo;\n}\n\n\n/*\n * AddShardSplitInfoEntryForNodeInMap function adds ShardSplitInfo entry\n * to the hash map. The key is nodeId on which the new shard is to be placed.\n */\nstatic void\nAddShardSplitInfoEntryForNodeInMap(ShardSplitInfo *shardSplitInfo)\n{\n\tNodeAndOwner key;\n\tkey.nodeId = shardSplitInfo->nodeId;\n\tkey.tableOwnerId = TableOwnerOid(shardSplitInfo->distributedTableOid);\n\n\tbool found = false;\n\tGroupedShardSplitInfos *groupedInfos =\n\t\t(GroupedShardSplitInfos *) hash_search(ShardInfoHashMap, &key, HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t   &found);\n\tif (!found)\n\t{\n\t\tgroupedInfos->shardSplitInfoList = NIL;\n\t}\n\n\tgroupedInfos->shardSplitInfoList =\n\t\tlappend(groupedInfos->shardSplitInfoList, (ShardSplitInfo *) shardSplitInfo);\n}\n\n\n/*\n * PopulateShardSplitInfoInSM function copies information from the hash map\n * into shared memory segment. This information is consumed by the WAL sender\n * process during logical replication.\n *\n * shardSplitInfoSMHeader - Shared memory header\n */\nstatic void\nPopulateShardSplitInfoInSM(ShardSplitInfoSMHeader *shardSplitInfoSMHeader,\n\t\t\t\t\t\t   OperationId operationId)\n{\n\tHASH_SEQ_STATUS status;\n\thash_seq_init(&status, ShardInfoHashMap);\n\n\tGroupedShardSplitInfos *entry = NULL;\n\tint splitInfoIndex = 0;\n\twhile ((entry = (GroupedShardSplitInfos *) hash_seq_search(&status)) != NULL)\n\t{\n\t\tuint32_t nodeId = entry->key.nodeId;\n\t\tuint32_t tableOwnerId = entry->key.tableOwnerId;\n\t\tchar *derivedSlotName =\n\t\t\tReplicationSlotNameForNodeAndOwnerForOperation(SHARD_SPLIT,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   tableOwnerId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   operationId);\n\n\t\tList *shardSplitInfoList = entry->shardSplitInfoList;\n\t\tShardSplitInfo *splitShardInfo = NULL;\n\t\tforeach_declared_ptr(splitShardInfo, shardSplitInfoList)\n\t\t{\n\t\t\tshardSplitInfoSMHeader->splitInfoArray[splitInfoIndex] = *splitShardInfo;\n\t\t\tstrcpy_s(shardSplitInfoSMHeader->splitInfoArray[splitInfoIndex].slotName,\n\t\t\t\t\t NAMEDATALEN,\n\t\t\t\t\t derivedSlotName);\n\t\t\tsplitInfoIndex++;\n\t\t}\n\t}\n}\n\n\n/*\n * ParseShardSplitInfoFromDatum deserializes individual fields of 'pg_catalog.split_shard_info'\n * datatype.\n */\nstatic void\nParseShardSplitInfoFromDatum(Datum shardSplitInfoDatum,\n\t\t\t\t\t\t\t uint64 *sourceShardId,\n\t\t\t\t\t\t\t char **partitionColumnName,\n\t\t\t\t\t\t\t uint64 *childShardId,\n\t\t\t\t\t\t\t int32 *minValue,\n\t\t\t\t\t\t\t int32 *maxValue,\n\t\t\t\t\t\t\t int32 *nodeId)\n{\n\tHeapTupleHeader dataTuple = DatumGetHeapTupleHeader(shardSplitInfoDatum);\n\tbool isnull = false;\n\n\tDatum sourceShardIdDatum = GetAttributeByName(dataTuple, \"source_shard_id\", &isnull);\n\tif (isnull)\n\t{\n\t\tereport(ERROR, (errmsg(\"source_shard_id for split_shard_info can't be null\")));\n\t}\n\t*sourceShardId = DatumGetUInt64(sourceShardIdDatum);\n\n\tDatum partitionColumnDatum = GetAttributeByName(dataTuple, \"distribution_column\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t&isnull);\n\tif (isnull)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"distribution_column for split_shard_info can't be null\")));\n\t}\n\t*partitionColumnName = TextDatumGetCString(partitionColumnDatum);\n\n\tDatum childShardIdDatum = GetAttributeByName(dataTuple, \"child_shard_id\", &isnull);\n\tif (isnull)\n\t{\n\t\tereport(ERROR, (errmsg(\"child_shard_id for split_shard_info can't be null\")));\n\t}\n\t*childShardId = DatumGetUInt64(childShardIdDatum);\n\n\tDatum minValueDatum = GetAttributeByName(dataTuple, \"shard_min_value\", &isnull);\n\tif (isnull)\n\t{\n\t\tereport(ERROR, (errmsg(\"shard_min_value for split_shard_info can't be null\")));\n\t}\n\tchar *shardMinValueString = text_to_cstring(DatumGetTextP(minValueDatum));\n\t*minValue = SafeStringToInt32(shardMinValueString);\n\n\tDatum maxValueDatum = GetAttributeByName(dataTuple, \"shard_max_value\", &isnull);\n\tif (isnull)\n\t{\n\t\tereport(ERROR, (errmsg(\"shard_max_value for split_shard_info can't be null\")));\n\t}\n\tchar *shardMaxValueString = text_to_cstring(DatumGetTextP(maxValueDatum));\n\t*maxValue = SafeStringToInt32(shardMaxValueString);\n\n\tDatum nodeIdDatum = GetAttributeByName(dataTuple, \"node_id\", &isnull);\n\tif (isnull)\n\t{\n\t\tereport(ERROR, (errmsg(\"node_id for split_shard_info can't be null\")));\n\t}\n\n\t*nodeId = DatumGetInt32(nodeIdDatum);\n}\n\n\n/*\n * ReturnReplicationSlotInfo writes 'pg_catalog.replication_slot_info'\n * records to tuplestore.\n * This information is used by the coordinator to create replication slots as a\n * part of non-blocking split workflow.\n */\nstatic void\nReturnReplicationSlotInfo(Tuplestorestate *tupleStore,\n\t\t\t\t\t\t  TupleDesc tupleDescriptor,\n\t\t\t\t\t\t  OperationId operationId)\n{\n\tHASH_SEQ_STATUS status;\n\thash_seq_init(&status, ShardInfoHashMap);\n\n\tGroupedShardSplitInfos *entry = NULL;\n\twhile ((entry = (GroupedShardSplitInfos *) hash_seq_search(&status)) != NULL)\n\t{\n\t\tDatum values[3];\n\t\tbool nulls[3];\n\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(nulls, false, sizeof(nulls));\n\n\t\tvalues[0] = Int32GetDatum(entry->key.nodeId);\n\n\t\tchar *tableOwnerName = GetUserNameFromId(entry->key.tableOwnerId, false);\n\t\tvalues[1] = CStringGetTextDatum(tableOwnerName);\n\n\t\tchar *slotName =\n\t\t\tReplicationSlotNameForNodeAndOwnerForOperation(SHARD_SPLIT,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   entry->key.nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   entry->key.tableOwnerId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   operationId);\n\t\tvalues[2] = CStringGetTextDatum(slotName);\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, nulls);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/README.md",
    "content": "# Distributed Query Planner\n\nThe distributed query planner is entered through the `distributed_planner` function in `distributed_planner.c`. This is the hook that Postgres calls instead of `standard_planner`.\n\nIf the input query is trivial (e.g., no joins, no subqueries/ctes, single table and single shard), we create a very simple `PlannedStmt`. If the query is not trivial, call `standard_planner` to build a `PlannedStmt`. For queries containing a distributed table or reference table, we then proceed with distributed planning, which overwrites the `planTree` in the `PlannedStmt`.\n\nDistributed planning (`CreateDistributedPlan`) tries several different methods to plan the query:\n\n\n 1. Fast-path router planner, proceed if the query prunes down to a single shard of a single table\n 2. Router planner, proceed if the query prunes down to a single set of co-located shards\n 3. Modification planning, proceed if the query is a DML command and all joins are co-located\n 4. Recursive planning, find CTEs and subqueries that cannot be pushed down and go back to 1\n 5. Logical planner, constructs a multi-relational algebra tree to find a distributed execution plan\n\n## Fast-path router planner\n\nBy examining the query tree, if we can decide that the query hits only a single shard of a single table, we can skip calling `standard_planner()`. Later on the execution, we simply fetch the filter on the distribution key and do the pruning.\n\nAs the name reveals, this can be considered as a sub-item of Router planner described below. The only difference is that fast-path planner doesn't rely on `standard_planner()` for collecting restriction information.\n\n\n## Router planner\n\nDuring the call to `standard_planner`, Postgres calls a hook named `multi_relation_restriction_hook`. We use this hook to determine explicit and implicit filters on (occurrences of) distributed tables. We apply shard pruning to all tables using the filters in `PlanRouterQuery`. If all tables prune down to a single shard and all those shards are on the same node, then the query is router plannable meaning it can be fully executed by one of the worker nodes.\n\nThe router planner preserves the original query tree, but the query does need to be rewritten to have shard names instead of table names. We cannot simply put the shard names into the query tree, because it actually contains relation OIDs. If the deparsing logic resolves those OIDs, it would throw an error since the shards do not exist on the coordinator. Instead, we replace the table entries with a fake SQL function call to `citus_extradata_container` and encode the original table ID and the shard ID into the parameter of the function. The deparsing functions recognise the fake function call and convert it into a shard name.\n\n## Recursive planning\n\nCTEs and subqueries that cannot be pushed down (checked using `DeferErrorIfCannotPushdownSubquery`) and do not contain references to the outer query are planned by recursively calling the `planner` function with the subquery as the parse tree. Because the planner is called from the top, any type of query that is supported by Postgres or Citus can be a valid subquery. The resulting plan is added to the `subPlanList` in the `DistributedPlan` and the subquery is replaced by a call to `read_intermediate_result` with a particular subplan name. At execution time, all of the plans in the `subPlanList` are executed and their output is sent to all the workers and written into an intermediate result with the subplan name (see `subplan_execution.c`). In the remainder of the planner, calls to `read_intermediate_result` are treated in the same way as reference tables, which means they can be joined by any column.\n\n## Logical planner\n\nThe logical planner constructs a multi-relational algebra tree from the query with operators such as `MultiTable`, `MultiProject`, `MultiJoin` and `MultiCollect`. It first picks a strategy for handling joins in `MultiLogicalPlanCreate` (pushdown planning, or join order planning) and then builds a `MultiNode` tree based on the original query tree. In the initial `MultiNode` tree, each `MultiTable` is wrapped in `MultiCollect`, which effectively means collect the entire table in one place. The `MultiNode` tree is passed to the logical optimizer which transforms the tree into one that requires less network traffic by pushing down operators. Finally, the physical planner transforms the `MultiNode` tree into a `DistributedPlan` which contains the queries to execute on shards and can be passed to the executor.\n\n###  Pushdown planning\n\nDuring the call to `standard_planner`, Postgres calls a hook named `multi_relation_restriction_hook`. We use this hook to determine whether all (occurrences of) distributed tables are joined on their respective distribution columns. When this is the case, we can be somewhat agnostic to the structure of subqueries and other joins. In that case, we treat the whole join tree as a single `MultiTable` and deparse this part of the query as is during physical planning. Pushing down a subquery is only possible when the subquery can be answered without a merge step (checked using `DeferErrorIfCannotPushdownSubquery`). However, you may notice that these subqueries are already replaced by `read_intermediate_result` calls during recursive planning. Only subqueries that have references to the outer query remain at this stage would pass through recursive planning and fail the check.\n\n### Join order planning\n\nThe join order planner is applied to the join tree in the original query and generates all possible pair-wise join orders. If a pair-wise join is not on the distribution column, it requires re-partitioning. The join order that requires the fewest re-partition steps is converted into a tree of `MultiJoin` nodes, prior to running the logical optimizer. The optimizer leaves the `MultiJoin` tree in tact, but pushes down select and collect operators below the `MultiJoin` nodes.\n\n### Logical optimizer\n\nThe logical optimizer uses commutativity rules to push project and select operators down below the `MultiCollect` nodes. Everything above the `MultiCollect` operator will be is executed on the coordinator and everything below on the workers. Additionally, the optimizer uses distributivity rules to push down operators below the `MultiJoin` nodes, such that filters and projections are applied prior to joins. This is primarily relevant for re-partition joins which first try to reduce the data by applying selections and projections, and then re-partitioning the result.\n\nA number of SQL clauses like aggregates, GROUP BY, ORDER BY, LIMIT can only be pushed down below the `MultiCollect` under certain conditions. All these clauses are bundled together in a `MultiExtendedOpNode`. After the basic transformation, the `MultiExtendedOpNode`s are directly above the `MultiCollect` nodes. They are then split into a coordinator and a worker part and the worker part is pushed down below the `MultiCollect`.\n\n### Physical planner\n\nThis section needs to be expanded.\n\n## Modification planning\n\nIn terms of modification planning, we distinguish between several cases:\n\n 1. DML planning (`CreateModifyPlan`)\n 1.a. UPDATE/DELETE planning\n 1.b. INSERT planning\n 2. INSERT...SELECT planning (`CreateInsertSelectPlan`)\n\n### UPDATE/DELETE planning\n\nUPDATE and DELETE commands are handled by the router planner (`PlanRouterQuery`), but when tables prune to multiple shards we do not fall back to other planners, but instead proceed to generate a task for each shard, as long as all subqueries can be pushed down. We can do this because UPDATE and DELETE never have a meaningful merge step on the coordinator, other than concatening RETURNING rows.\n\nWhen there are CTEs or subqueries that cannot be pushed down in the UPDATE/DELETE, we continue with recursive planning and try again. If recursive planning cannot resolve the issue (e.g. due to a correlated subquery), then the command will error out.\n\n### INSERT planning\n\nDistributed planning for INSERT commands is relatively complicated because of multi-row INSERT. Each row in a multi-row INSERT may go to different shard and therefore we need to construct a different query for each shard. The logic for this is primarily in `BuildRoutesForInsert`, which builds the set of rows for each shard. Rather than construct a full query tree for each shard, we put each set of rows in the `rowValuesLists` of the `Task` and replace the VALUES section of the INSERT just before deparsing in `UpdateTaskQueryString`.\n\nOne additional complication for INSERTs is that it is very common to have a function call (such as `now()` or `nextval(..)`) in the position of the distribution column. In that case building the task list is deferred until the functions have been evaluated in the executor.\n\n### INSERT ... SELECT query planning\n\nCitus supports `INSERT ... SELECT` queries either by pushing down the whole query to the worker nodes or pulling the `SELECT` part to the coordinator.\n\n#### INSERT ... SELECT - by pushing down\n\nIf `INSERT ... SELECT` query can be planned by pushing down it to the worker nodes, Citus selects to choose that logic first. Query is planned separately for each shard in the target table. Do so by replacing the partitioning qual parameter using the shard's actual boundary values to create modify task for each shard. Then, shard pruning is performed to decide on to which shards query will be pushed down. Finally, checks if the target shardInterval has exactly same placements with the select task's available anchor placements.\n\n#### INSERT...SELECT - via the coordinator\n\nIf the query can not be pushed down to the worker nodes, two different approaches can be followed depending on whether ON CONFLICT or RETURNING clauses are used.\n\n* If `ON CONFLICT` or `RETURNING` are not used, Citus uses `COPY` command to handle such queries. After planning the `SELECT` part of the `INSERT ... SELECT` query, including subqueries and CTEs, it executes the plan and send results back to the DestReceiver which is created using the target table info.\n\n* Since `COPY` command supports neither `ON CONFLICT` nor `RETURNING` clauses, Citus perform `INSERT ... SELECT` queries with `ON CONFLICT` or `RETURNING` clause in two phases. First, Citus plans the `SELECT` part of the query, executes the plan and saves results to the intermediate table which is colocated with target table of the `INSERT ... SELECT` query. Then, `INSERT ... SELECT` query is directly run on the worker node using the intermediate table as the source table.\n"
  },
  {
    "path": "src/backend/distributed/planner/combine_query_planner.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * combine_query_planner.c\n *\t  Routines for planning the combine query that runs on the coordinator\n *    to combine results from the workers.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/planner.h\"\n#include \"rewrite/rewriteManip.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/combine_query_planner.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_physical_planner.h\"\n\nstatic List * RemoteScanTargetList(List *workerTargetList);\nstatic PlannedStmt * BuildSelectStatementViaStdPlanner(Query *combineQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   List *remoteScanTargetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   CustomScan *remoteScan);\n\nstatic Plan * CitusCustomScanPathPlan(PlannerInfo *root, RelOptInfo *rel,\n\t\t\t\t\t\t\t\t\t  struct CustomPath *best_path, List *tlist,\n\t\t\t\t\t\t\t\t\t  List *clauses, List *custom_plans);\n\nbool ReplaceCitusExtraDataContainer = false;\nCustomScan *ReplaceCitusExtraDataContainerWithCustomScan = NULL;\n\n/*\n * CitusCustomScanPathMethods defines the methods for a custom path we insert into the\n * planner during the planning of the query part that will be executed on the node\n * coordinating the query.\n */\nstatic CustomPathMethods CitusCustomScanPathMethods = {\n\t.CustomName = \"CitusCustomScanPath\",\n\t.PlanCustomPath = CitusCustomScanPathPlan,\n};\n\n/*\n * PlanCombineQuery takes in a distributed plan and a custom scan node which\n * wraps remote part of the plan. This function finds the combine query structure\n * in the multi plan, and builds the final select plan to execute on the tuples\n * returned by remote scan on the coordinator node. Note that this select\n * plan is executed after result files are retrieved from worker nodes and\n * filled into the tuple store inside provided custom scan.\n */\nPlannedStmt *\nPlanCombineQuery(DistributedPlan *distributedPlan, CustomScan *remoteScan)\n{\n\tQuery *combineQuery = distributedPlan->combineQuery;\n\n\tJob *workerJob = distributedPlan->workerJob;\n\tList *workerTargetList = workerJob->jobQuery->targetList;\n\tList *remoteScanTargetList = RemoteScanTargetList(workerTargetList);\n\treturn BuildSelectStatementViaStdPlanner(combineQuery, remoteScanTargetList,\n\t\t\t\t\t\t\t\t\t\t\t remoteScan);\n}\n\n\n/*\n * RemoteScanTargetList uses the given worker target list's expressions, and creates\n * a target list for the remote scan on the coordinator node.\n */\nstatic List *\nRemoteScanTargetList(List *workerTargetList)\n{\n\tList *remoteScanTargetList = NIL;\n\tconst Index tableId = 1;\n\tAttrNumber columnId = 1;\n\n\tListCell *workerTargetCell = NULL;\n\tforeach(workerTargetCell, workerTargetList)\n\t{\n\t\tTargetEntry *workerTargetEntry = (TargetEntry *) lfirst(workerTargetCell);\n\n\t\tif (workerTargetEntry->resjunk)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tVar *remoteScanColumn = makeVarFromTargetEntry(tableId, workerTargetEntry);\n\t\tremoteScanColumn->varattno = columnId;\n\t\tremoteScanColumn->varattnosyn = columnId;\n\t\tcolumnId++;\n\n\t\tif (remoteScanColumn->vartype == RECORDOID || remoteScanColumn->vartype ==\n\t\t\tRECORDARRAYOID)\n\t\t{\n\t\t\tremoteScanColumn->vartypmod = BlessRecordExpression(workerTargetEntry->expr);\n\t\t}\n\n\t\t/*\n\t\t * The remote scan target entry has two pieces to it. The first piece is the\n\t\t * target entry's expression, which we set to the newly created column.\n\t\t * The second piece is sort and group clauses that we implicitly copy\n\t\t * from the worker target entry. Note that any changes to worker target\n\t\t * entry's sort and group clauses will *break* us here.\n\t\t */\n\t\tTargetEntry *remoteScanTargetEntry = flatCopyTargetEntry(workerTargetEntry);\n\t\tremoteScanTargetEntry->expr = (Expr *) remoteScanColumn;\n\t\tremoteScanTargetList = lappend(remoteScanTargetList, remoteScanTargetEntry);\n\t}\n\n\treturn remoteScanTargetList;\n}\n\n\n/*\n * CreateCitusCustomScanPath creates a custom path node that will return the CustomScan if\n * the path ends up in the best_path during postgres planning. We use this function during\n * the set relation hook of postgres during the planning of the query part that will be\n * executed on the query coordinating node.\n */\nPath *\nCreateCitusCustomScanPath(PlannerInfo *root, RelOptInfo *relOptInfo,\n\t\t\t\t\t\t  Index restrictionIndex, RangeTblEntry *rte,\n\t\t\t\t\t\t  CustomScan *remoteScan)\n{\n\tCitusCustomScanPath *path = (CitusCustomScanPath *) newNode(\n\t\tsizeof(CitusCustomScanPath), T_CustomPath);\n\tpath->custom_path.methods = &CitusCustomScanPathMethods;\n\tpath->custom_path.path.pathtype = T_CustomScan;\n\tpath->custom_path.path.pathtarget = relOptInfo->reltarget;\n\tpath->custom_path.path.parent = relOptInfo;\n\n\t/* necessary to avoid extra Result node in PG15 */\n\tpath->custom_path.flags = CUSTOMPATH_SUPPORT_PROJECTION;\n\n\t/*\n\t * The 100k rows we put on the cost of the path is kind of arbitrary and could be\n\t * improved in accuracy to produce better plans.\n\t *\n\t * 100k on the row estimate causes the postgres planner to behave very much like the\n\t * old citus planner in the plans it produces. Namely the old planner had hardcoded\n\t * the use of Hash Aggregates for most of the operations, unless a postgres guc was\n\t * set that would disallow hash aggregates to be used.\n\t *\n\t * Ideally we would be able to provide estimates close to postgres' estimates on the\n\t * workers to let the standard planner choose an optimal solution for the combineQuery.\n\t */\n\tpath->custom_path.path.rows = 100000;\n\tpath->remoteScan = remoteScan;\n\n\treturn (Path *) path;\n}\n\n\n/*\n * CitusCustomScanPathPlan is called for the CitusCustomScanPath node in the best_path\n * after the postgres planner has evaluated all possible paths.\n *\n * This function returns a Plan node, more specifically the CustomScan Plan node that has\n * the ability to execute the distributed part of the query.\n *\n * When this function is called there is an extra list of clauses passed in that might not\n * already have been applied to the plan. We add these clauses to the quals this node will\n * execute. The quals are evaluated before returning the tuples scanned from the workers\n * to the plan above ours to make sure they do not end up in the final result.\n */\nstatic Plan *\nCitusCustomScanPathPlan(PlannerInfo *root,\n\t\t\t\t\t\tRelOptInfo *rel,\n\t\t\t\t\t\tstruct CustomPath *best_path,\n\t\t\t\t\t\tList *tlist,\n\t\t\t\t\t\tList *clauses,\n\t\t\t\t\t\tList *custom_plans)\n{\n\tCitusCustomScanPath *citusPath = (CitusCustomScanPath *) best_path;\n\n\t/*\n\t * Columns could have been pruned from the target list by the standard planner.\n\t * A situation in which this might happen is a CASE that is proven to be always the\n\t * same causing the other column to become useless;\n\t *   CASE WHEN ... <> NULL\n\t *     THEN ...\n\t *     ELSE ...\n\t *   END\n\t * Since nothing is equal to NULL it will always end up in the else branch. The final\n\t * target list the planenr needs from our node is passed in as tlist. By placing that\n\t * as the target list on our scan the internal rows will be projected to this one.\n\t */\n\tcitusPath->remoteScan->scan.plan.targetlist = tlist;\n\n\t/*\n\t * The custom_scan_tlist contains target entries for to the \"output\" of the call\n\t * to citus_extradata_container, which is actually replaced by a CustomScan.\n\t * The target entries are initialized with varno 1 (see RemoteScanTargetList), since\n\t * it's currently the only relation in the join tree of the combineQuery.\n\t *\n\t * If the citus_extradata_container function call is not the first relation to\n\t * appear in the flattened rtable for the entire plan, then varno is now pointing\n\t * to the wrong relation and needs to be updated.\n\t *\n\t * Example:\n\t * When the combineQuery field of the DistributedPlan is\n\t * INSERT INTO local SELECT .. FROM citus_extradata_container.\n\t * In that case the varno of citusdata_extradata_container should be 3, because\n\t * it is preceded range table entries for \"local\" and the subquery.\n\t */\n\tif (rel->relid != 1)\n\t{\n\t\tTargetEntry *targetEntry = NULL;\n\n\t\tforeach_declared_ptr(targetEntry, citusPath->remoteScan->custom_scan_tlist)\n\t\t{\n\t\t\t/* we created this list, so we know it only contains Var */\n\t\t\tAssert(IsA(targetEntry->expr, Var));\n\n\t\t\tVar *var = (Var *) targetEntry->expr;\n\n\t\t\tvar->varno = rel->relid;\n\t\t}\n\t}\n\n\t/* clauses might have been added by the planner, need to add them to our scan */\n\tRestrictInfo *restrictInfo = NULL;\n\tList **quals = &citusPath->remoteScan->scan.plan.qual;\n\tforeach_declared_ptr(restrictInfo, clauses)\n\t{\n\t\t*quals = lappend(*quals, restrictInfo->clause);\n\t}\n\treturn (Plan *) citusPath->remoteScan;\n}\n\n\n/*\n * BuildSelectStatementViaStdPlanner creates a PlannedStmt where it combines the\n * combineQuery and the remoteScan. It utilizes the standard_planner from postgres to\n * create a plan based on the combineQuery.\n */\nstatic PlannedStmt *\nBuildSelectStatementViaStdPlanner(Query *combineQuery, List *remoteScanTargetList,\n\t\t\t\t\t\t\t\t  CustomScan *remoteScan)\n{\n\t/*\n\t * the standard planner will scribble on the target list. Since it is essential to not\n\t * change the custom_scan_tlist we copy the target list before adding them to any.\n\t * The remoteScanTargetList is used in the end to extract the column names to be added to\n\t * the alias we will create for the CustomScan, (expressed as the\n\t * citus_extradata_container function call in the combineQuery).\n\t */\n\tremoteScan->custom_scan_tlist = copyObject(remoteScanTargetList);\n\tremoteScan->scan.plan.targetlist = copyObject(remoteScanTargetList);\n\n\t/*\n\t * We will overwrite the alias of the rangetable which describes the custom scan.\n\t * Ideally we would have set the correct column names and alias on the range table in\n\t * the combine query already when we inserted the extra data container. This could be\n\t * improved in the future.\n\t */\n\n\t/* find the rangetable entry for the extradata container and overwrite its alias */\n\tRangeTblEntry *extradataContainerRTE = NULL;\n\tFindCitusExtradataContainerRTE((Node *) combineQuery, &extradataContainerRTE);\n\tif (extradataContainerRTE != NULL)\n\t{\n\t\t/* extract column names from the remoteScanTargetList */\n\t\tList *columnNameList = NIL;\n\t\tTargetEntry *targetEntry = NULL;\n\t\tforeach_declared_ptr(targetEntry, remoteScanTargetList)\n\t\t{\n\t\t\tcolumnNameList = lappend(columnNameList, makeString(targetEntry->resname));\n\t\t}\n\t\textradataContainerRTE->eref = makeAlias(\"remote_scan\", columnNameList);\n\t}\n\n\t/*\n\t * Print the combine query at debug level 4. Since serializing the query is relatively\n\t * cpu intensive we only perform that if we are actually logging DEBUG4.\n\t */\n\tconst int logCombineQueryLevel = DEBUG4;\n\tif (IsLoggableLevel(logCombineQueryLevel))\n\t{\n\t\tStringInfo queryString = makeStringInfo();\n\t\tpg_get_query_def(combineQuery, queryString);\n\t\telog(logCombineQueryLevel, \"combine query: %s\", queryString->data);\n\t}\n\n\tPlannedStmt *standardStmt = NULL;\n\tPG_TRY();\n\t{\n\t\t/* This code should not be re-entrant, we check via asserts below */\n\t\tAssert(ReplaceCitusExtraDataContainer == false);\n\t\tAssert(ReplaceCitusExtraDataContainerWithCustomScan == NULL);\n\t\tReplaceCitusExtraDataContainer = true;\n\t\tReplaceCitusExtraDataContainerWithCustomScan = remoteScan;\n\n\t\tstandardStmt = standard_planner(combineQuery, NULL, 0, NULL);\n\n\t\tReplaceCitusExtraDataContainer = false;\n\t\tReplaceCitusExtraDataContainerWithCustomScan = NULL;\n\t}\n\tPG_CATCH();\n\t{\n\t\tReplaceCitusExtraDataContainer = false;\n\t\tReplaceCitusExtraDataContainerWithCustomScan = NULL;\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n\n\tAssert(standardStmt != NULL);\n\treturn standardStmt;\n}\n\n\n/*\n * Finds the rangetable entry in the query that refers to the citus_extradata_container\n * and stores the pointer in result.\n */\nbool\nFindCitusExtradataContainerRTE(Node *node, RangeTblEntry **result)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, RangeTblEntry))\n\t{\n\t\tRangeTblEntry *rangeTblEntry = castNode(RangeTblEntry, node);\n\t\tif (rangeTblEntry->rtekind == RTE_FUNCTION &&\n\t\t\tlist_length(rangeTblEntry->functions) == 1)\n\t\t{\n\t\t\tRangeTblFunction *rangeTblFunction = (RangeTblFunction *) linitial(\n\t\t\t\trangeTblEntry->functions);\n\t\t\tif (!IsA(rangeTblFunction->funcexpr, FuncExpr))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tFuncExpr *funcExpr = castNode(FuncExpr, rangeTblFunction->funcexpr);\n\t\t\tif (funcExpr->funcid == CitusExtraDataContainerFuncId())\n\t\t\t{\n\t\t\t\t*result = rangeTblEntry;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t/* query_tree_walker descends into RTEs */\n\t\treturn false;\n\t}\n\telse if (IsA(node, Query))\n\t{\n\t\tconst int flags = QTW_EXAMINE_RTES_BEFORE;\n\t\treturn query_tree_walker((Query *) node, FindCitusExtradataContainerRTE, result,\n\t\t\t\t\t\t\t\t flags);\n\t}\n\n\treturn expression_tree_walker(node, FindCitusExtradataContainerRTE, result);\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/cte_inline.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * cte_inline.c\n *\t  For multi-shard queries, Citus can only recursively plan CTEs. Instead,\n *\t  with the functions defined in this file, the certain CTEs can be inlined\n *\t  as subqueries in the query tree. In that case, more optimal distributed\n *\t  planning, the query pushdown planning, kicks in and the CTEs can actually\n *\t  be pushed down as long as it is safe to pushdown as a subquery.\n *\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"nodes/nodeFuncs.h\"\n#include \"optimizer/optimizer.h\"\n#include \"rewrite/rewriteManip.h\"\n\n#include \"pg_version_compat.h\"\n#include \"pg_version_constants.h\"\n\n#include \"distributed/cte_inline.h\"\n\ntypedef struct inline_cte_walker_context\n{\n\tconst char *ctename;       /* name and relative level of target CTE */\n\tint levelsup;\n\tint refcount;              /* number of remaining references */\n\tQuery *ctequery;           /* query to substitute */\n\n\tList *aliascolnames;  /* citus addition to Postgres' inline_cte_walker_context */\n} inline_cte_walker_context;\n\n/* copy & paste from Postgres source, moved into a function for readability */\nstatic bool PostgreSQLCTEInlineCondition(CommonTableExpr *cte, CmdType cmdType);\n\n/* the following utility functions are copy & paste from PostgreSQL code */\nstatic void inline_cte(Query *mainQuery, CommonTableExpr *cte);\nstatic bool inline_cte_walker(Node *node, inline_cte_walker_context *context);\nstatic bool contain_dml(Node *node);\nstatic bool contain_dml_walker(Node *node, void *context);\n\n\n/* the following utility functions are related to Citus' logic */\nstatic bool RecursivelyInlineCteWalker(Node *node, void *context);\nstatic void InlineCTEsInQueryTree(Query *query);\nstatic bool QueryTreeContainsInlinableCteWalker(Node *node, void *context);\n\n\n/*\n * RecursivelyInlineCtesInQueryTree gets a query and recursively traverses the\n * tree from top to bottom. On each level, the CTEs that are eligable for\n * inlining are inlined as subqueries. This is useful in distributed planning\n * because Citus' sub(query) planning logic superior to CTE planning, where CTEs\n * are always recursively planned, which might produce very slow executions.\n */\nvoid\nRecursivelyInlineCtesInQueryTree(Query *query)\n{\n\tInlineCTEsInQueryTree(query);\n\n\tquery_tree_walker(query, RecursivelyInlineCteWalker, NULL, 0);\n}\n\n\n/*\n * RecursivelyInlineCteWalker recursively finds all the Query nodes and\n * recursively inline eligable ctes.\n */\nstatic bool\nRecursivelyInlineCteWalker(Node *node, void *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\n\t\tInlineCTEsInQueryTree(query);\n\n\t\tquery_tree_walker(query, RecursivelyInlineCteWalker, NULL, 0);\n\n\t\t/* we're done, no need to recurse anymore for this query */\n\t\treturn false;\n\t}\n\n\treturn expression_tree_walker(node, RecursivelyInlineCteWalker, context);\n}\n\n\n/*\n * InlineCTEsInQueryTree gets a query tree and tries to inline CTEs as subqueries\n * in the query tree.\n *\n * Most of the code is coming from PostgreSQL's CTE inlining logic, there are very\n * few additions that Citus added, which are already commented in the code.\n */\nvoid\nInlineCTEsInQueryTree(Query *query)\n{\n\tListCell *cteCell = NULL;\n\n\t/* iterate on the copy of the list because we'll be modifying query->cteList */\n\tList *copyOfCteList = list_copy(query->cteList);\n\tforeach(cteCell, copyOfCteList)\n\t{\n\t\tCommonTableExpr *cte = (CommonTableExpr *) lfirst(cteCell);\n\n\t\t/*\n\t\t * First, make sure that Postgres is OK to inline the CTE. Later, check for\n\t\t * distributed query planning constraints that might prevent inlining.\n\t\t */\n\t\tif (PostgreSQLCTEInlineCondition(cte, query->commandType))\n\t\t{\n\t\t\telog(DEBUG1, \"CTE %s is going to be inlined via \"\n\t\t\t\t\t\t \"distributed planning\", cte->ctename);\n\n\t\t\t/* do the hard work of cte inlining */\n\t\t\tinline_cte(query, cte);\n\n\t\t\t/* clean-up the necessary fields for distributed planning */\n\t\t\tcte->cterefcount = 0;\n\t\t\tquery->cteList = list_delete_ptr(query->cteList, cte);\n\t\t}\n\t}\n}\n\n\n/*\n * QueryTreeContainsInlinableCTE recursively traverses the queryTree, and returns true\n * if any of the (sub)queries in the queryTree contains at least one CTE.\n */\nbool\nQueryTreeContainsInlinableCTE(Query *queryTree)\n{\n\treturn QueryTreeContainsInlinableCteWalker((Node *) queryTree, NULL);\n}\n\n\n/*\n * QueryTreeContainsInlinableCteWalker walks over the node, and returns true if any of\n * the (sub)queries in the node contains at least one CTE.\n */\nstatic bool\nQueryTreeContainsInlinableCteWalker(Node *node, void *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\n\t\tListCell *cteCell = NULL;\n\t\tforeach(cteCell, query->cteList)\n\t\t{\n\t\t\tCommonTableExpr *cte = (CommonTableExpr *) lfirst(cteCell);\n\n\t\t\tif (PostgreSQLCTEInlineCondition(cte, query->commandType))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Return true even if we can find a single CTE that is\n\t\t\t\t * eligable for inlining.\n\t\t\t\t */\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn query_tree_walker(query, QueryTreeContainsInlinableCteWalker, NULL, 0);\n\t}\n\n\treturn expression_tree_walker(node, QueryTreeContainsInlinableCteWalker, NULL);\n}\n\n\n/*\n * PostgreSQLCTEInlineCondition returns true if the CTE is considered\n * safe to inline by Postgres.\n */\nstatic bool\nPostgreSQLCTEInlineCondition(CommonTableExpr *cte, CmdType cmdType)\n{\n\t/*\n\t * Consider inlining the CTE (creating RTE_SUBQUERY RTE(s)) instead of\n\t * implementing it as a separately-planned CTE.\n\t *\n\t * We cannot inline if any of these conditions hold:\n\t *\n\t * 1. The user said not to (the CTEMaterializeAlways option).\n\t *\n\t * 2. The CTE is recursive.\n\t *\n\t * 3. The CTE has side-effects; this includes either not being a plain\n\t * SELECT, or containing volatile functions.  Inlining might change\n\t * the side-effects, which would be bad.\n\t *\n\t * Otherwise, we have an option whether to inline or not.  That should\n\t * always be a win if there's just a single reference, but if the CTE\n\t * is multiply-referenced then it's unclear: inlining adds duplicate\n\t * computations, but the ability to absorb restrictions from the outer\n\t * query level could outweigh that.  We do not have nearly enough\n\t * information at this point to tell whether that's true, so we let\n\t * the user express a preference.  Our default behavior is to inline\n\t * only singly-referenced CTEs, but a CTE marked CTEMaterializeNever\n\t * will be inlined even if multiply referenced.\n\t */\n\tif (\n\t\t(cte->ctematerialized == CTEMaterializeNever ||\n\t\t (cte->ctematerialized == CTEMaterializeDefault &&\n\t\t  cte->cterefcount == 1)) &&\n\t\t!cte->cterecursive &&\n\t\tcmdType == CMD_SELECT &&\n\t\t!contain_dml(cte->ctequery) &&\n\t\t!contain_volatile_functions(cte->ctequery))\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/* *INDENT-OFF* */\n/*\n * inline_cte: convert RTE_CTE references to given CTE into RTE_SUBQUERYs\n */\nstatic void\ninline_cte(Query *mainQuery, CommonTableExpr *cte)\n{\n\tstruct inline_cte_walker_context context;\n\n\tcontext.ctename = cte->ctename;\n\t/* Start at levelsup = -1 because we'll immediately increment it */\n\tcontext.levelsup = -1;\n\tcontext.refcount = cte->cterefcount;\n\tcontext.ctequery = castNode(Query, cte->ctequery);\n\tcontext.aliascolnames = cte->aliascolnames;\n\n\t(void) inline_cte_walker((Node *) mainQuery, &context);\n\n\t/* Assert we replaced all references */\n\tAssert(context.refcount == 0);\n}\n\n\n/*\n * See PostgreSQL's source code at src/backend/optimizer/plan/subselect.c.\n */\nstatic bool\ninline_cte_walker(Node *node, inline_cte_walker_context *context)\n{\n\tif (node == NULL)\n\t\treturn false;\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\n\t\tcontext->levelsup++;\n\n\t\t(void) query_tree_walker(query, inline_cte_walker, context,\n\t\t\t\t\t\t\t\t QTW_EXAMINE_RTES_AFTER);\n\t\tcontext->levelsup--;\n\n\t\treturn false;\n\t}\n\telse if (IsA(node, RangeTblEntry))\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) node;\n\n\t\tif (rte->rtekind == RTE_CTE &&\n\t\t\tstrcmp(rte->ctename, context->ctename) == 0 &&\n\t\t\trte->ctelevelsup == context->levelsup)\n\t\t{\n\t\t\t/*\n\t\t\t * Found a reference to replace.  Generate a copy of the CTE query\n\t\t\t * with appropriate level adjustment for outer references (e.g.,\n\t\t\t * to other CTEs).\n\t\t\t */\n\t\t\tQuery *newquery = copyObject(context->ctequery);\n\n\t\t\tif (context->levelsup > 0)\n\t\t\t\tIncrementVarSublevelsUp((Node *) newquery, context->levelsup, 1);\n\n\t\t\t/*\n\t\t\t * Convert the RTE_CTE RTE into a RTE_SUBQUERY.\n\t\t\t *\n\t\t\t * Historically, a FOR UPDATE clause has been treated as extending\n\t\t\t * into views and subqueries, but not into CTEs.  We preserve this\n\t\t\t * distinction by not trying to push rowmarks into the new\n\t\t\t * subquery.\n\t\t\t */\n\t\t\trte->rtekind = RTE_SUBQUERY;\n\t\t\trte->subquery = newquery;\n\t\t\trte->security_barrier = false;\n\n\t\t\tList *columnAliasList = context->aliascolnames;\n\t\t\tint columnAliasCount = list_length(columnAliasList);\n\t\t\tint columnIndex = 1;\n\t\t\tfor (; columnIndex < list_length(rte->subquery->targetList) + 1; ++columnIndex)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Rename the column only if a column alias is defined.\n\t\t\t\t * Notice that column alias count could be less than actual\n\t\t\t\t * column count. We only use provided aliases and keep the\n\t\t\t\t * original column names if no alias is defined.\n\t\t\t\t */\n\t\t\t\tif (columnAliasCount >= columnIndex)\n\t\t\t\t{\n\t\t\t\t\tString *columnAlias = (String *) list_nth(columnAliasList, columnIndex - 1);\n\t\t\t\t\tAssert(IsA(columnAlias, String));\n\t\t\t\t\tTargetEntry *targetEntry =\n\t\t\t\t\t\tlist_nth(rte->subquery->targetList, columnIndex - 1);\n\t\t\t\t\tAssert(IsA(columnAlias, String));\n\t\t\t\t\ttargetEntry->resname = strVal(columnAlias);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Zero out CTE-specific fields */\n\t\t\trte->ctename = NULL;\n\t\t\trte->ctelevelsup = 0;\n\t\t\trte->self_reference = false;\n\t\t\trte->coltypes = NIL;\n\t\t\trte->coltypmods = NIL;\n\t\t\trte->colcollations = NIL;\n\n\t\t\t/* Count the number of replacements we've done */\n\t\t\tcontext->refcount--;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\treturn expression_tree_walker(node, inline_cte_walker, context);\n}\n\n\n/*\n * contain_dml: is any subquery not a plain SELECT?\n *\n * We reject SELECT FOR UPDATE/SHARE as well as INSERT etc.\n */\nstatic bool\ncontain_dml(Node *node)\n{\n\treturn contain_dml_walker(node, NULL);\n}\n\n\nstatic bool\ncontain_dml_walker(Node *node, void *context)\n{\n\tif (node == NULL)\n\t\treturn false;\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\n\t\tif (query->commandType != CMD_SELECT ||\n\t\t\tquery->rowMarks != NIL)\n\t\t\treturn true;\n\n\t\treturn query_tree_walker(query, contain_dml_walker, context, 0);\n\t}\n\treturn expression_tree_walker(node, contain_dml_walker, context);\n}\n\n/* *INDENT-ON* */\n"
  },
  {
    "path": "src/backend/distributed/planner/deparse_shard_query.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_shard_query.c\n *\n * This file contains functions for deparsing shard queries.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_operator.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"parser/parsetree.h\"\n#include \"storage/lock.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/combine_query_planner.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/shard_utils.h\"\n#include \"distributed/stats/stat_tenants.h\"\n#include \"distributed/version_compat.h\"\n\n\nstatic void UpdateTaskQueryString(Query *query, Task *task);\nstatic RelationShard * FindRelationShard(Oid inputRelationId, List *relationShardList);\nstatic void ConvertRteToSubqueryWithEmptyResult(RangeTblEntry *rte);\nstatic bool ShouldLazyDeparseQuery(Task *task);\nstatic char * DeparseTaskQuery(Task *task, Query *query);\n\n\n/*\n * RebuildQueryStrings deparses the job query for each task to\n * include execution-time changes such as function evaluation.\n */\nvoid\nRebuildQueryStrings(Job *workerJob)\n{\n\tQuery *originalQuery = workerJob->jobQuery;\n\tList *taskList = workerJob->taskList;\n\tTask *task = NULL;\n\tbool isSingleTask = list_length(taskList) == 1;\n\n\tif (originalQuery->commandType == CMD_INSERT)\n\t{\n\t\tAddInsertAliasIfNeeded(originalQuery);\n\t}\n\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tQuery *query = originalQuery;\n\n\t\t/*\n\t\t * Copy the query if there are multiple tasks. If there is a single\n\t\t * task, we scribble on the original query to avoid the copying\n\t\t * overhead.\n\t\t */\n\t\tif (!isSingleTask)\n\t\t{\n\t\t\tquery = copyObject(originalQuery);\n\t\t}\n\n\t\tif (UpdateOrDeleteOrMergeQuery(query))\n\t\t{\n\t\t\tList *relationShardList = task->relationShardList;\n\n\t\t\t/*\n\t\t\t * For UPDATE and DELETE queries, we may have subqueries and joins, so\n\t\t\t * we use relation shard list to update shard names and call\n\t\t\t * pg_get_query_def() directly.\n\t\t\t */\n\t\t\tUpdateRelationToShardNames((Node *) query, relationShardList);\n\t\t}\n\t\telse if (query->commandType == CMD_INSERT && task->modifyWithSubquery)\n\t\t{\n\t\t\t/* for INSERT..SELECT, adjust shard names in SELECT part */\n\t\t\tList *relationShardList = task->relationShardList;\n\t\t\tShardInterval *shardInterval = LoadShardInterval(task->anchorShardId);\n\n\t\t\tRangeTblEntry *copiedInsertRte = ExtractResultRelationRTEOrError(query);\n\t\t\tRangeTblEntry *copiedSubqueryRte = ExtractSelectRangeTableEntry(query);\n\t\t\tQuery *copiedSubquery = copiedSubqueryRte->subquery;\n\n\t\t\t/* there are no restrictions to add for reference and citus local tables */\n\t\t\tif (IsCitusTableType(shardInterval->relationId, DISTRIBUTED_TABLE))\n\t\t\t{\n\t\t\t\tAddPartitionKeyNotNullFilterToSelect(copiedSubquery);\n\t\t\t}\n\n\t\t\tReorderInsertSelectTargetLists(query, copiedInsertRte, copiedSubqueryRte);\n\n\t\t\tUpdateRelationToShardNames((Node *) copiedSubquery, relationShardList);\n\t\t}\n\n\t\tif (query->commandType == CMD_INSERT)\n\t\t{\n\t\t\tRangeTblEntry *modifiedRelationRTE = linitial(originalQuery->rtable);\n\n\t\t\t/*\n\t\t\t * We store the modified relaiton ID in the task so we can lazily call\n\t\t\t * deparse_shard_query when the string is needed\n\t\t\t */\n\t\t\ttask->anchorDistributedTableId = modifiedRelationRTE->relid;\n\n\t\t\t/*\n\t\t\t * For multi-row inserts, we modify the VALUES before storing the\n\t\t\t * query in the task.\n\t\t\t */\n\t\t\tRangeTblEntry *valuesRTE = ExtractDistributedInsertValuesRTE(query);\n\t\t\tif (valuesRTE != NULL)\n\t\t\t{\n\t\t\t\tAssert(valuesRTE->rtekind == RTE_VALUES);\n\t\t\t\tAssert(task->rowValuesLists != NULL);\n\n\t\t\t\tvaluesRTE->values_lists = task->rowValuesLists;\n\t\t\t}\n\t\t}\n\n\t\tbool isQueryObjectOrText = GetTaskQueryType(task) == TASK_QUERY_TEXT ||\n\t\t\t\t\t\t\t\t   GetTaskQueryType(task) == TASK_QUERY_OBJECT;\n\t\tereport(DEBUG4, (errmsg(\"query before rebuilding: %s\",\n\t\t\t\t\t\t\t\t!isQueryObjectOrText\n\t\t\t\t\t\t\t\t? \"(null)\"\n\t\t\t\t\t\t\t\t: TaskQueryString(task))));\n\n\t\ttask->partitionKeyValue = workerJob->partitionKeyValue;\n\t\tSetJobColocationId(workerJob);\n\t\ttask->colocationId = workerJob->colocationId;\n\n\t\tUpdateTaskQueryString(query, task);\n\n\t\t/*\n\t\t * If parameters were resolved in the job query, then they are now also\n\t\t * resolved in the query string.\n\t\t */\n\t\ttask->parametersInQueryStringResolved = workerJob->parametersInJobQueryResolved;\n\n\t\tereport(DEBUG4, (errmsg(\"query after rebuilding:  %s\",\n\t\t\t\t\t\t\t\tTaskQueryString(task))));\n\t}\n}\n\n\n/*\n * AddInsertAliasIfNeeded adds an alias in UPSERTs and multi-row INSERTs to avoid\n * deparsing issues (e.g. RETURNING might reference the original table name,\n * which has been replaced by a shard name).\n */\nvoid\nAddInsertAliasIfNeeded(Query *query)\n{\n\tAssert(query->commandType == CMD_INSERT);\n\n\tif (query->onConflict == NULL &&\n\t\tExtractDistributedInsertValuesRTE(query) == NULL)\n\t{\n\t\t/* simple single-row insert does not need an alias */\n\t\treturn;\n\t}\n\n\tRangeTblEntry *rangeTableEntry = linitial(query->rtable);\n\tif (rangeTableEntry->alias != NULL)\n\t{\n\t\t/* INSERT already has an alias */\n\t\treturn;\n\t}\n\n\tAlias *alias = makeAlias(CITUS_TABLE_ALIAS, NIL);\n\trangeTableEntry->alias = alias;\n}\n\n\n/*\n * UpdateTaskQueryString updates the query string stored within the provided\n * Task. If the Task has row values from a multi-row INSERT, those are injected\n * into the provided query before deparse occurs (the query's full VALUES list\n * will be restored before this function returns).\n */\nstatic void\nUpdateTaskQueryString(Query *query, Task *task)\n{\n\tSetTaskQueryIfShouldLazyDeparse(task, query);\n}\n\n\n/*\n * CreateQualsForShardInterval creates the necessary qual conditions over the\n * given attnum and rtindex for the given shard interval.\n */\nNode *\nCreateQualsForShardInterval(RelationShard *relationShard, int attnum, int rtindex)\n{\n\tuint64 shardId = relationShard->shardId;\n\tOid relationId = relationShard->relationId;\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\tVar *partitionColumnVar = cacheEntry->partitionColumn;\n\n\t/*\n\t * Add constraints for the relation identified by rtindex, specifically on its column at attnum.\n\t * Create a Var node representing this column, which will be used to compare against the bounds\n\t * from the partition column of shard interval.\n\t */\n\n\tVar *outerTablePartitionColumnVar = makeVar(\n\t\trtindex, attnum, partitionColumnVar->vartype,\n\t\tpartitionColumnVar->vartypmod,\n\t\tpartitionColumnVar->varcollid,\n\t\t0);\n\n\tbool isFirstShard = IsFirstShard(cacheEntry, shardId);\n\n\t/* load the interval for the shard and create constant nodes for the upper/lower bounds */\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\tConst *constNodeLowerBound = makeConst(INT4OID, -1, InvalidOid, sizeof(int32),\n\t\t\t\t\t\t\t\t\t\t   shardInterval->minValue, false, true);\n\tConst *constNodeUpperBound = makeConst(INT4OID, -1, InvalidOid, sizeof(int32),\n\t\t\t\t\t\t\t\t\t\t   shardInterval->maxValue, false, true);\n\tConst *constNodeZero = makeConst(INT4OID, -1, InvalidOid, sizeof(int32),\n\t\t\t\t\t\t\t\t\t Int32GetDatum(0), false, true);\n\n\t/* create a function expression node for the hash partition column */\n\tFuncExpr *hashFunction = makeNode(FuncExpr);\n\thashFunction->funcid = cacheEntry->hashFunction->fn_oid;\n\thashFunction->args = list_make1(outerTablePartitionColumnVar);\n\thashFunction->funcresulttype = get_func_rettype(cacheEntry->hashFunction->fn_oid);\n\thashFunction->funcretset = false;\n\n\t/* create a function expression for the lower bound of the shard interval */\n\tOid resultTypeOid = get_func_rettype(\n\t\tcacheEntry->shardIntervalCompareFunction->fn_oid);\n\tFuncExpr *lowerBoundFuncExpr = makeNode(FuncExpr);\n\tlowerBoundFuncExpr->funcid = cacheEntry->shardIntervalCompareFunction->fn_oid;\n\tlowerBoundFuncExpr->args = list_make2((Node *) constNodeLowerBound,\n\t\t\t\t\t\t\t\t\t\t  (Node *) hashFunction);\n\tlowerBoundFuncExpr->funcresulttype = resultTypeOid;\n\tlowerBoundFuncExpr->funcretset = false;\n\n\tOid lessThan = GetSysCacheOid(OPERNAMENSP, Anum_pg_operator_oid, CStringGetDatum(\"<\"),\n\t\t\t\t\t\t\t\t  resultTypeOid, resultTypeOid, ObjectIdGetDatum(\n\t\t\t\t\t\t\t\t\t  PG_CATALOG_NAMESPACE));\n\n\t/*\n\t * Finally, check if the comparison result is less than 0, i.e.,\n\t * shardInterval->minValue < hash(partitionColumn)\n\t * See SearchCachedShardInterval for the behavior at the boundaries.\n\t */\n\tExpr *lowerBoundExpr = make_opclause(lessThan, BOOLOID, false,\n\t\t\t\t\t\t\t\t\t\t (Expr *) lowerBoundFuncExpr,\n\t\t\t\t\t\t\t\t\t\t (Expr *) constNodeZero, InvalidOid, InvalidOid);\n\n\t/* create a function expression for the upper bound of the shard interval */\n\tFuncExpr *upperBoundFuncExpr = makeNode(FuncExpr);\n\tupperBoundFuncExpr->funcid = cacheEntry->shardIntervalCompareFunction->fn_oid;\n\tupperBoundFuncExpr->args = list_make2((Node *) hashFunction,\n\t\t\t\t\t\t\t\t\t\t  (Expr *) constNodeUpperBound);\n\tupperBoundFuncExpr->funcresulttype = resultTypeOid;\n\tupperBoundFuncExpr->funcretset = false;\n\n\tOid lessThanOrEqualTo = GetSysCacheOid(OPERNAMENSP, Anum_pg_operator_oid,\n\t\t\t\t\t\t\t\t\t\t   CStringGetDatum(\"<=\"),\n\t\t\t\t\t\t\t\t\t\t   resultTypeOid, resultTypeOid,\n\t\t\t\t\t\t\t\t\t\t   ObjectIdGetDatum(PG_CATALOG_NAMESPACE));\n\n\n\t/*\n\t * Finally, check if the comparison result is less than or equal to 0, i.e.,\n\t * hash(partitionColumn) <= shardInterval->maxValue\n\t * See SearchCachedShardInterval for the behavior at the boundaries.\n\t */\n\tExpr *upperBoundExpr = make_opclause(lessThanOrEqualTo, BOOLOID, false,\n\t\t\t\t\t\t\t\t\t\t (Expr *) upperBoundFuncExpr,\n\t\t\t\t\t\t\t\t\t\t (Expr *) constNodeZero, InvalidOid, InvalidOid);\n\n\n\t/* create a node for both upper and lower bound */\n\tNode *shardIntervalBoundQuals = make_and_qual((Node *) lowerBoundExpr,\n\t\t\t\t\t\t\t\t\t\t\t\t  (Node *) upperBoundExpr);\n\n\t/*\n\t * Add a null test for the partition column for the first shard.\n\t * This is because we need to include the null values in exactly one of the shard queries.\n\t * The null test is added as an OR clause to the existing AND clause.\n\t */\n\tif (isFirstShard)\n\t{\n\t\t/* null test for the first shard */\n\t\tNullTest *nullTest = makeNode(NullTest);\n\t\tnullTest->nulltesttype = IS_NULL;  /* Check for IS NULL */\n\t\tnullTest->arg = (Expr *) outerTablePartitionColumnVar;  /* The variable to check */\n\t\tnullTest->argisrow = false;\n\t\tshardIntervalBoundQuals = (Node *) make_orclause(list_make2(nullTest,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardIntervalBoundQuals));\n\t}\n\treturn shardIntervalBoundQuals;\n}\n\n\n/*\n * UpdateWhereClauseToPushdownRecurringOuterJoinWalker walks over the query tree and\n * updates the WHERE clause for outer joins satisfying feasibility conditions.\n */\nbool\nUpdateWhereClauseToPushdownRecurringOuterJoinWalker(Node *node, List *relationShardList)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Query))\n\t{\n\t\tUpdateWhereClauseToPushdownRecurringOuterJoin((Query *) node, relationShardList);\n\t\treturn query_tree_walker((Query *) node,\n\t\t\t\t\t\t\t\t UpdateWhereClauseToPushdownRecurringOuterJoinWalker,\n\t\t\t\t\t\t\t\t relationShardList, QTW_EXAMINE_RTES_BEFORE);\n\t}\n\n\tif (!IsA(node, RangeTblEntry))\n\t{\n\t\treturn expression_tree_walker(node,\n\t\t\t\t\t\t\t\t\t  UpdateWhereClauseToPushdownRecurringOuterJoinWalker,\n\t\t\t\t\t\t\t\t\t  relationShardList);\n\t}\n\n\treturn false;\n}\n\n\n/*\n * UpdateWhereClauseToPushdownRecurringOuterJoin\n *\n * Inject shard interval predicates into the query WHERE clause for certain\n * outer joins to make the join semantically correct when distributed.\n *\n * Why this is needed:\n *   When an inner side of an OUTER JOIN is a distributed table that has been\n *   routed to a single shard, we cannot simply replace the RTE with the shard\n *   name and rely on implicit pruning: the preserved (outer) side could still\n *   produce rows whose join keys would hash to other shards. To keep results\n *   consistent with the global execution semantics we restrict the preserved\n *   (outer) side to only those partition key values that would route to the\n *   chosen shard (plus NULLs, which are assigned to exactly one shard).\n *\n * What the function does:\n *   1. Iterate over the top-level jointree->fromlist.\n *   2. For each JoinExpr call CanPushdownRecurringOuterJoinExtended() which:\n *        - Verifies shape / join type is eligible.\n *        - Returns:\n *            outerRtIndex : RT index whose column we will constrain,\n *            outerRte / innerRte,\n *            attnum       : attribute number (partition column) on outer side.\n *                           This is compared to partition column of innerRte.\n *   3. Find the RelationShard for the inner distributed table (innerRte->relid)\n *      in relationShardList; skip if absent (no fixed shard chosen).\n *   4. Build the shard qualification with CreateQualsForShardInterval():\n *        (minValue < hash(partcol) AND hash(partcol) <= maxValue)\n *      and, for the first shard only, OR (partcol IS NULL).\n *      The Var refers to (outerRtIndex, attnum) so the restriction applies to\n *      the preserved outer input.\n *   5. AND the new quals into jointree->quals (creating it if NULL).\n *\n * The function does not return anything, it modifies the query in place.\n */\nvoid\nUpdateWhereClauseToPushdownRecurringOuterJoin(Query *query, List *relationShardList)\n{\n\tif (query == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tFromExpr *fromExpr = query->jointree;\n\tif (fromExpr == NULL || fromExpr->fromlist == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tListCell *fromExprCell;\n\tforeach(fromExprCell, fromExpr->fromlist)\n\t{\n\t\tNode *fromItem = (Node *) lfirst(fromExprCell);\n\t\tif (!IsA(fromItem, JoinExpr))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tJoinExpr *joinExpr = (JoinExpr *) fromItem;\n\n\t\t/*\n\t\t * We will check if we need to add constraints to the WHERE clause.\n\t\t */\n\t\tRangeTblEntry *innerRte = NULL;\n\t\tRangeTblEntry *outerRte = NULL;\n\t\tint outerRtIndex = -1;\n\t\tint attnum;\n\t\tif (!CanPushdownRecurringOuterJoinExtended(joinExpr, query, &outerRtIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t   &outerRte, &innerRte, &attnum))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (attnum == InvalidAttrNumber)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tereport(DEBUG5, (errmsg(\n\t\t\t\t\t\t\t \"Distributed table from the inner part of the outer join: %s.\",\n\t\t\t\t\t\t\t innerRte->eref->aliasname)));\n\n\t\tRelationShard *relationShard = FindRelationShard(innerRte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t relationShardList);\n\n\t\tif (relationShard == NULL || relationShard->shardId == INVALID_SHARD_ID)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tNode *shardIntervalBoundQuals = CreateQualsForShardInterval(relationShard, attnum,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\touterRtIndex);\n\t\tif (fromExpr->quals == NULL)\n\t\t{\n\t\t\tfromExpr->quals = (Node *) shardIntervalBoundQuals;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfromExpr->quals = make_and_qual(fromExpr->quals, shardIntervalBoundQuals);\n\t\t}\n\t}\n}\n\n\n/*\n * UpdateRelationToShardNames walks over the query tree and appends shard ids to\n * relations. It uses unique identity value to establish connection between a\n * shard and the range table entry. If the range table id is not given a\n * identity, than the relation is not referenced from the query, no connection\n * could be found between a shard and this relation. Therefore relation is replaced\n * by set of NULL values so that the query would work at worker without any problems.\n *\n */\nbool\nUpdateRelationToShardNames(Node *node, List *relationShardList)\n{\n\tuint64 shardId = INVALID_SHARD_ID;\n\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\t/* want to look at all RTEs, even in subqueries, CTEs and such */\n\tif (IsA(node, Query))\n\t{\n\t\treturn query_tree_walker((Query *) node, UpdateRelationToShardNames,\n\t\t\t\t\t\t\t\t relationShardList, QTW_EXAMINE_RTES_BEFORE);\n\t}\n\n\tif (!IsA(node, RangeTblEntry))\n\t{\n\t\treturn expression_tree_walker(node, UpdateRelationToShardNames,\n\t\t\t\t\t\t\t\t\t  relationShardList);\n\t}\n\n\tRangeTblEntry *newRte = (RangeTblEntry *) node;\n\n\tif (newRte->rtekind == RTE_FUNCTION)\n\t{\n\t\tnewRte = NULL;\n\t\tif (!FindCitusExtradataContainerRTE(node, &newRte))\n\t\t{\n\t\t\t/* only update function rtes containing citus_extradata_container */\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (newRte->rtekind != RTE_RELATION)\n\t{\n\t\treturn false;\n\t}\n\n\tif (!IsCitusTable(newRte->relid))\n\t{\n\t\t/* leave local tables as is */\n\t\treturn false;\n\t}\n\n\tRelationShard *relationShard = FindRelationShard(newRte->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t relationShardList);\n\n\tbool replaceRteWithNullValues = relationShard == NULL ||\n\t\t\t\t\t\t\t\t\trelationShard->shardId == INVALID_SHARD_ID;\n\tif (replaceRteWithNullValues)\n\t{\n\t\tConvertRteToSubqueryWithEmptyResult(newRte);\n\t\treturn false;\n\t}\n\n\tshardId = relationShard->shardId;\n\tOid relationId = relationShard->relationId;\n\n\tchar *relationName = get_rel_name(relationId);\n\tAppendShardIdToName(&relationName, shardId);\n\n\tOid schemaId = get_rel_namespace(relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\n\tModifyRangeTblExtraData(newRte, CITUS_RTE_SHARD, schemaName, relationName, NIL);\n\n\treturn false;\n}\n\n\n/*\n * FindRelationShard finds the RelationShard for shard relation with\n * given Oid if exists in given relationShardList. Otherwise, returns NULL.\n */\nstatic RelationShard *\nFindRelationShard(Oid inputRelationId, List *relationShardList)\n{\n\tRelationShard *relationShard = NULL;\n\n\t/*\n\t * Search for the restrictions associated with the RTE. There better be\n\t * some, otherwise this query wouldn't be eligible as a router query.\n\t * FIXME: We should probably use a hashtable here, to do efficient lookup.\n\t */\n\tforeach_declared_ptr(relationShard, relationShardList)\n\t{\n\t\tif (inputRelationId == relationShard->relationId)\n\t\t{\n\t\t\treturn relationShard;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * ConvertRteToSubqueryWithEmptyResult converts given relation RTE into\n * subquery RTE that returns no results.\n */\nstatic void\nConvertRteToSubqueryWithEmptyResult(RangeTblEntry *rte)\n{\n\tRelation relation = table_open(rte->relid, NoLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\tint columnCount = tupleDescriptor->natts;\n\tList *targetList = NIL;\n\n\tfor (int columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tFormData_pg_attribute *attributeForm = TupleDescAttr(tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t columnIndex);\n\n\t\tif (attributeForm->attisdropped)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tStringInfo resname = makeStringInfo();\n\t\tConst *constValue = makeNullConst(attributeForm->atttypid,\n\t\t\t\t\t\t\t\t\t\t  attributeForm->atttypmod,\n\t\t\t\t\t\t\t\t\t\t  attributeForm->attcollation);\n\n\t\tappendStringInfo(resname, \"%s\", attributeForm->attname.data);\n\n\t\tTargetEntry *targetEntry = makeNode(TargetEntry);\n\t\ttargetEntry->expr = (Expr *) constValue;\n\t\ttargetEntry->resno = columnIndex;\n\t\ttargetEntry->resname = resname->data;\n\n\t\ttargetList = lappend(targetList, targetEntry);\n\t}\n\n\ttable_close(relation, NoLock);\n\n\tFromExpr *joinTree = makeNode(FromExpr);\n\tjoinTree->quals = makeBoolConst(false, false);\n\n\tQuery *subquery = makeNode(Query);\n\tsubquery->commandType = CMD_SELECT;\n\tsubquery->querySource = QSRC_ORIGINAL;\n\tsubquery->canSetTag = true;\n\tsubquery->targetList = targetList;\n\tsubquery->jointree = joinTree;\n\n\trte->rtekind = RTE_SUBQUERY;\n\n\t/* no permission checking for this RTE */\n\trte->perminfoindex = 0;\n\n\trte->subquery = subquery;\n\trte->alias = copyObject(rte->eref);\n}\n\n\n/*\n * ShouldLazyDeparseQuery returns true if we should lazily deparse the query\n * when adding it to the task. Right now it simply checks if any shards on the\n * local node can be used for the task.\n */\nstatic bool\nShouldLazyDeparseQuery(Task *task)\n{\n\treturn TaskAccessesLocalNode(task);\n}\n\n\n/*\n * SetTaskQueryIfShouldLazyDeparse attaches the query to the task so that it can be used during\n * execution. If local execution can possibly take place it sets task->jobQueryReferenceForLazyDeparsing.\n * If not it deparses the query and sets queryStringLazy, to avoid blowing the\n * size of the task unnecesarily.\n */\nvoid\nSetTaskQueryIfShouldLazyDeparse(Task *task, Query *query)\n{\n\tif (ShouldLazyDeparseQuery(task))\n\t{\n\t\ttask->taskQuery.queryType = TASK_QUERY_OBJECT;\n\t\ttask->taskQuery.data.jobQueryReferenceForLazyDeparsing = query;\n\t\ttask->queryCount = 1;\n\t\treturn;\n\t}\n\n\tSetTaskQueryString(task, AnnotateQuery(DeparseTaskQuery(task, query),\n\t\t\t\t\t\t\t\t\t\t   task->partitionKeyValue, task->colocationId));\n}\n\n\n/*\n * SetTaskQueryString attaches the query string to the task so that it can be\n * used during execution. It also unsets jobQueryReferenceForLazyDeparsing to be sure\n * these are kept in sync.\n */\nvoid\nSetTaskQueryString(Task *task, char *queryString)\n{\n\tif (queryString == NULL)\n\t{\n\t\ttask->taskQuery.queryType = TASK_QUERY_NULL;\n\t\ttask->queryCount = 0;\n\t}\n\telse\n\t{\n\t\ttask->taskQuery.queryType = TASK_QUERY_TEXT;\n\t\ttask->taskQuery.data.queryStringLazy = queryString;\n\t\ttask->queryCount = 1;\n\t}\n}\n\n\n/*\n * SetTaskQueryStringList sets the queryStringList of the given task.\n */\nvoid\nSetTaskQueryStringList(Task *task, List *queryStringList)\n{\n\tAssert(queryStringList != NIL);\n\ttask->taskQuery.queryType = TASK_QUERY_TEXT_LIST;\n\ttask->taskQuery.data.queryStringList = queryStringList;\n\ttask->queryCount = list_length(queryStringList);\n}\n\n\nvoid\nSetTaskQueryPlan(Task *task, Query *query, PlannedStmt *localPlan)\n{\n\tAssert(localPlan != NULL);\n\ttask->taskQuery.queryType = TASK_QUERY_LOCAL_PLAN;\n\ttask->taskQuery.data.localCompiled = (LocalCompilation *) palloc0(\n\t\tsizeof(LocalCompilation));\n\ttask->taskQuery.data.localCompiled->query = query;\n\ttask->taskQuery.data.localCompiled->plan = localPlan;\n\ttask->queryCount = 1;\n}\n\n\nPlannedStmt *\nTaskQueryLocalPlan(Task *task)\n{\n\tAssert(task->taskQuery.queryType == TASK_QUERY_LOCAL_PLAN);\n\treturn task->taskQuery.data.localCompiled->plan;\n}\n\n\n/*\n * DeparseTaskQuery is a general way of deparsing a query based on a task.\n */\nstatic char *\nDeparseTaskQuery(Task *task, Query *query)\n{\n\tStringInfo queryString = makeStringInfo();\n\n\tif (query->commandType == CMD_INSERT)\n\t{\n\t\t/*\n\t\t * For INSERT queries we cannot use pg_get_query_def. Mainly because we\n\t\t * cannot run UpdateRelationToShardNames on an INSERT query. This is\n\t\t * because the PG deparsing logic fails when trying to insert into a\n\t\t * RTE_FUNCTION (which is what will happen if you call\n\t\t * UpdateRelationToShardNames).\n\t\t */\n\t\tdeparse_shard_query(query, task->anchorDistributedTableId, task->anchorShardId,\n\t\t\t\t\t\t\tqueryString);\n\t}\n\telse\n\t{\n\t\tpg_get_query_def(query, queryString);\n\t}\n\n\treturn queryString->data;\n}\n\n\n/*\n * GetTaskQueryType returns the type of the task query.\n */\nint\nGetTaskQueryType(Task *task)\n{\n\treturn task->taskQuery.queryType;\n}\n\n\n/*\n * TaskQueryStringAtIndex returns query at given index among the possibly\n * multiple queries that a task can have.\n */\nchar *\nTaskQueryStringAtIndex(Task *task, int index)\n{\n\tAssert(index < task->queryCount);\n\n\tint taskQueryType = GetTaskQueryType(task);\n\tif (taskQueryType == TASK_QUERY_TEXT_LIST)\n\t{\n\t\treturn list_nth(task->taskQuery.data.queryStringList, index);\n\t}\n\n\treturn TaskQueryString(task);\n}\n\n\n/*\n * TaskQueryString generates task query string text if missing.\n *\n * For performance reasons, the queryString is generated lazily. For example\n * for local queries it is usually not needed to generate it, so this way we\n * can skip the expensive deparsing+parsing.\n */\nchar *\nTaskQueryString(Task *task)\n{\n\tint taskQueryType = GetTaskQueryType(task);\n\tif (taskQueryType == TASK_QUERY_NULL)\n\t{\n\t\t/* if task query type is TASK_QUERY_NULL then the data will be NULL,\n\t\t * this is unexpected state */\n\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t\t\terrmsg(\"unexpected task query state: task query type is null\"),\n\t\t\t\t\t\terrdetail(\"Please report this to the Citus core team.\")));\n\t}\n\telse if (taskQueryType == TASK_QUERY_TEXT_LIST)\n\t{\n\t\treturn StringJoin(task->taskQuery.data.queryStringList, ';');\n\t}\n\telse if (taskQueryType == TASK_QUERY_TEXT)\n\t{\n\t\treturn task->taskQuery.data.queryStringLazy;\n\t}\n\telse if (taskQueryType == TASK_QUERY_LOCAL_PLAN)\n\t{\n\t\tQuery *query = task->taskQuery.data.localCompiled->query;\n\t\tAssert(query != NULL);\n\n\t\t/*\n\t\t * Use the query of the local compilation to generate the\n\t\t * query string. For local compiled tasks, the query is retained\n\t\t * for this purpose, which may be EXPLAIN ANALYZing the task, or\n\t\t * command logging. Generating the query string on the fly is\n\t\t * acceptable because the plan of the local compilation is used\n\t\t * for query execution.\n\t\t */\n\t\tMemoryContext previousContext = MemoryContextSwitchTo(GetMemoryChunkContext(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  query));\n\t\tUpdateRelationToShardNames((Node *) query, task->relationShardList);\n\t\tMemoryContextSwitchTo(previousContext);\n\t\treturn AnnotateQuery(DeparseTaskQuery(task, query),\n\t\t\t\t\t\t\t task->partitionKeyValue, task->colocationId);\n\t}\n\n\tQuery *jobQueryReferenceForLazyDeparsing =\n\t\ttask->taskQuery.data.jobQueryReferenceForLazyDeparsing;\n\n\t/*\n\t *\tAt this point task query type should be TASK_QUERY_OBJECT.\n\t */\n\tAssert(task->taskQuery.queryType == TASK_QUERY_OBJECT &&\n\t\t   jobQueryReferenceForLazyDeparsing != NULL);\n\n\n\t/*\n\t * Switch to the memory context of task->jobQueryReferenceForLazyDeparsing before generating the query\n\t * string. This way the query string is not freed in between multiple\n\t * executions of a prepared statement. Except when UpdateTaskQueryString is\n\t * used to set task->jobQueryReferenceForLazyDeparsing, in that case it is freed but it will be set to\n\t * NULL on the next execution of the query because UpdateTaskQueryString\n\t * does that.\n\t */\n\tMemoryContext previousContext = MemoryContextSwitchTo(GetMemoryChunkContext(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  jobQueryReferenceForLazyDeparsing));\n\tchar *queryString = DeparseTaskQuery(task, jobQueryReferenceForLazyDeparsing);\n\tMemoryContextSwitchTo(previousContext);\n\tSetTaskQueryString(task, queryString);\n\treturn task->taskQuery.data.queryStringLazy;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/distributed_planner.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * distributed_planner.c\n *\t  General Citus planner code.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include <float.h>\n#include <limits.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"access/htup_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_type.h\"\n#include \"executor/executor.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/pg_list.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/pathnode.h\"\n#include \"optimizer/plancat.h\"\n#include \"optimizer/planmain.h\"\n#include \"optimizer/planner.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parse_type.h\"\n#include \"parser/parsetree.h\"\n#include \"utils/builtins.h\"\n#include \"utils/datum.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/syscache.h\"\n#include \"utils/typcache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_depended_object.h\"\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/combine_query_planner.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/cte_inline.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/function_call_delegation.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/intermediate_result_pruning.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/merge_planner.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/query_pushdown_planning.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/shard_utils.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/stats/stat_tenants.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_shard_visibility.h\"\n\n\nstatic List *plannerRestrictionContextList = NIL;\nint MultiTaskQueryLogLevel = CITUS_LOG_LEVEL_OFF; /* multi-task query log level */\nstatic uint64 NextPlanId = 1;\n\n/* keep track of planner call stack levels */\nint PlannerLevel = 0;\n\nstatic bool ListContainsDistributedTableRTE(List *rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\tbool *maybeHasForeignDistributedTable);\nstatic bool PlanContainsDistributedSubPlanRTE(List *subPlanList);\nstatic PlannedStmt * CreateDistributedPlannedStmt(DistributedPlanningContext *\n\t\t\t\t\t\t\t\t\t\t\t\t  planContext);\nstatic PlannedStmt * InlineCtesAndCreateDistributedPlannedStmt(uint64 planId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   DistributedPlanningContext\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   *planContext);\nstatic PlannedStmt * TryCreateDistributedPlannedStmt(PlannedStmt *localPlan,\n\t\t\t\t\t\t\t\t\t\t\t\t\t Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t Query *query, ParamListInfo\n\t\t\t\t\t\t\t\t\t\t\t\t\t boundParams,\n\t\t\t\t\t\t\t\t\t\t\t\t\t PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\nstatic DeferredErrorMessage * DeferErrorIfPartitionTableNotSingleReplicated(Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trelationId);\n\nstatic int AssignRTEIdentities(List *rangeTableList, int rteIdCounter);\nstatic void AssignRTEIdentity(RangeTblEntry *rangeTableEntry, int rteIdentifier);\nstatic void AdjustPartitioningForDistributedPlanning(List *rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t bool setPartitionedTablesInherited);\nstatic bool RTEWentThroughAdjustPartitioning(RangeTblEntry *rangeTableEntry);\nstatic PlannedStmt * FinalizeNonRouterPlan(PlannedStmt *localPlan,\n\t\t\t\t\t\t\t\t\t\t   DistributedPlan *distributedPlan,\n\t\t\t\t\t\t\t\t\t\t   CustomScan *customScan);\nstatic PlannedStmt * FinalizeRouterPlan(PlannedStmt *localPlan, CustomScan *customScan);\nstatic AppendRelInfo * FindTargetAppendRelInfo(PlannerInfo *root, int relationRteIndex);\nstatic List * makeTargetListFromCustomScanList(List *custom_scan_tlist);\nstatic List * makeCustomScanTargetlistFromExistingTargetList(List *existingTargetlist);\nstatic int32 BlessRecordExpressionList(List *exprs);\nstatic void CheckNodeIsDumpable(Node *node);\nstatic Node * CheckNodeCopyAndSerialization(Node *node);\nstatic void AdjustReadIntermediateResultCost(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t\t RelOptInfo *relOptInfo);\nstatic void AdjustReadIntermediateResultArrayCost(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t  RelOptInfo *relOptInfo);\nstatic void AdjustReadIntermediateResultsCostInternal(RelOptInfo *relOptInfo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *columnTypes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  int resultIdCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  Datum *resultIds,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  Const *resultFormatConst);\nstatic List * OuterPlanParamsList(PlannerInfo *root);\nstatic List * CopyPlanParamList(List *originalPlanParamList);\nstatic void CreateAndPushPlannerRestrictionContext(DistributedPlanningContext *\n\t\t\t\t\t\t\t\t\t\t\t\t   planContext,\n\t\t\t\t\t\t\t\t\t\t\t\t   FastPathRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t   fastPathContext);\nstatic PlannerRestrictionContext * CurrentPlannerRestrictionContext(void);\nstatic void PopPlannerRestrictionContext(void);\nstatic void ResetPlannerRestrictionContext(PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nstatic PlannedStmt * PlanFastPathDistributedStmt(DistributedPlanningContext *planContext);\nstatic PlannedStmt * PlanDistributedStmt(DistributedPlanningContext *planContext,\n\t\t\t\t\t\t\t\t\t\t int rteIdCounter);\nstatic RTEListProperties * GetRTEListProperties(List *rangeTableList);\nstatic List * TranslatedVars(PlannerInfo *root, int relationIndex);\nstatic void WarnIfListHasForeignDistributedTable(List *rangeTableList);\nstatic RouterPlanType GetRouterPlanType(Query *query,\n\t\t\t\t\t\t\t\t\t\tQuery *originalQuery,\n\t\t\t\t\t\t\t\t\t\tbool hasUnresolvedParams);\nstatic void ConcatenateRTablesAndPerminfos(PlannedStmt *mainPlan,\n\t\t\t\t\t\t\t\t\t\t   PlannedStmt *concatPlan);\nstatic bool CheckPostPlanDistribution(DistributedPlanningContext *planContext,\n\t\t\t\t\t\t\t\t\t  bool isDistributedQuery,\n\t\t\t\t\t\t\t\t\t  List *rangeTableList);\n#if PG_VERSION_NUM >= PG_VERSION_18\nstatic int DisableSelfJoinElimination(void);\n#endif\n\n/* Distributed planner hook */\nPlannedStmt *\ndistributed_planner(Query *parse,\n\t\t\t\t\tconst char *query_string,\n\t\t\t\t\tint cursorOptions,\n\t\t\t\t\tParamListInfo boundParams)\n{\n\tbool needsDistributedPlanning = false;\n\tbool fastPathRouterQuery = false;\n\tFastPathRestrictionContext fastPathContext = { 0 };\n#if PG_VERSION_NUM >= PG_VERSION_18\n\tint saveNestLevel = -1;\n#endif\n\n\tList *rangeTableList = ExtractRangeTableEntryList(parse);\n\n\tif (cursorOptions & CURSOR_OPT_FORCE_DISTRIBUTED)\n\t{\n\t\t/* this cursor flag could only be set when Citus has been loaded */\n\t\tAssert(CitusHasBeenLoaded());\n\n\t\t/*\n\t\t * We cannot have merge command for this path as well because\n\t\t * there cannot be recursively planned merge command.\n\t\t */\n\t\tAssert(!IsMergeQuery(parse));\n\n\t\tneedsDistributedPlanning = true;\n\t}\n\telse if (CitusHasBeenLoaded())\n\t{\n\t\tbool maybeHasForeignDistributedTable = false;\n\t\tneedsDistributedPlanning =\n\t\t\tListContainsDistributedTableRTE(rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t&maybeHasForeignDistributedTable);\n\t\tif (needsDistributedPlanning)\n\t\t{\n\t\t\tfastPathRouterQuery = FastPathRouterQuery(parse, &fastPathContext);\n\t\t\tif (maybeHasForeignDistributedTable)\n\t\t\t{\n\t\t\t\tWarnIfListHasForeignDistributedTable(rangeTableList);\n\t\t\t}\n\t\t}\n\t}\n\n\tint rteIdCounter = 1;\n\n\tDistributedPlanningContext planContext = {\n\t\t.query = parse,\n\t\t.cursorOptions = cursorOptions,\n\t\t.boundParams = boundParams,\n\t};\n\n\tif (needsDistributedPlanning)\n\t{\n\t\t/*\n\t\t * standard_planner scribbles on its input, but for deparsing we need the\n\t\t * unmodified form. Before copying we call AssignRTEIdentities to be able\n\t\t * to match RTEs in the rewritten query tree with those in the original\n\t\t * tree.\n\t\t */\n\t\trteIdCounter = AssignRTEIdentities(rangeTableList, rteIdCounter);\n\n\t\tplanContext.originalQuery = copyObject(parse);\n\n\n\t\tif (!fastPathRouterQuery)\n\t\t{\n\t\t\t/*\n\t\t\t * When there are partitioned tables (not applicable to fast path),\n\t\t\t * pretend that they are regular tables to avoid unnecessary work\n\t\t\t * in standard_planner.\n\t\t\t */\n\t\t\tbool setPartitionedTablesInherited = false;\n\t\t\tAdjustPartitioningForDistributedPlanning(rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t setPartitionedTablesInherited);\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\t\t\tsaveNestLevel = DisableSelfJoinElimination();\n#endif\n\t\t}\n\t}\n\n\t/*\n\t * Make sure that we hide shard names on the Citus MX worker nodes. See comments in\n\t * HideShardsFromSomeApplications() for the details.\n\t */\n\tHideShardsFromSomeApplications(parse);\n\n\t/*\n\t * If GUC is set, we prevent queries, which contain pg meta relations, from\n\t * showing any citus dependent object. The flag is expected to be set only before\n\t * postgres vanilla tests.\n\t */\n\tHideCitusDependentObjectsOnQueriesOfPgMetaTables((Node *) parse, NULL);\n\n\t/* create a restriction context and put it at the end of our plan context's context list */\n\tCreateAndPushPlannerRestrictionContext(&planContext,\n\t\t\t\t\t\t\t\t\t\t   &fastPathContext);\n\n\t/*\n\t * We keep track of how many times we've recursed into the planner, primarily\n\t * to detect whether we are in a function call. We need to make sure that the\n\t * PlannerLevel is decremented exactly once at the end of the next PG_TRY\n\t * block, both in the happy case and when an error occurs.\n\t */\n\tPlannerLevel++;\n\n\tPlannedStmt *result = NULL;\n\n\tPG_TRY();\n\t{\n\t\tif (fastPathRouterQuery)\n\t\t{\n\t\t\tresult = PlanFastPathDistributedStmt(&planContext);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Call into standard_planner because the Citus planner relies on both the\n\t\t\t * restriction information per table and parse tree transformations made by\n\t\t\t * postgres' planner.\n\t\t\t */\n\t\t\tplanContext.plan = standard_planner(planContext.query, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\tplanContext.cursorOptions,\n\t\t\t\t\t\t\t\t\t\t\t\tplanContext.boundParams);\n#if PG_VERSION_NUM >= PG_VERSION_18\n\t\t\tif (needsDistributedPlanning)\n\t\t\t{\n\t\t\t\tAssert(saveNestLevel > 0);\n\t\t\t\tAtEOXact_GUC(true, saveNestLevel);\n\t\t\t}\n\n\t\t\t/* Pop the plan context from the current restriction context */\n\t\t\tplanContext.plannerRestrictionContext->planContext = NULL;\n#endif\n\t\t\tneedsDistributedPlanning = CheckPostPlanDistribution(&planContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t needsDistributedPlanning,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t rangeTableList);\n\n\t\t\tif (needsDistributedPlanning)\n\t\t\t{\n\t\t\t\tresult = PlanDistributedStmt(&planContext, rteIdCounter);\n\t\t\t}\n\t\t\telse if ((result = TryToDelegateFunctionCall(&planContext)) == NULL)\n\t\t\t{\n\t\t\t\tresult = planContext.plan;\n\t\t\t}\n\t\t}\n\t}\n\tPG_CATCH();\n\t{\n\t\tPopPlannerRestrictionContext();\n\n\t\tPlannerLevel--;\n\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n\n\tPlannerLevel--;\n\n\t/* remove the context from the context list */\n\tPopPlannerRestrictionContext();\n\n\t/*\n\t * In some cases, for example; parameterized SQL functions, we may miss that\n\t * there is a need for distributed planning. Such cases only become clear after\n\t * standard_planner performs some modifications on parse tree. In such cases\n\t * we will simply error out.\n\t */\n\tif (!needsDistributedPlanning && NeedsDistributedPlanning(parse))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot perform distributed planning on this \"\n\t\t\t\t\t\t\t   \"query because parameterized queries for SQL \"\n\t\t\t\t\t\t\t   \"functions referencing distributed tables are \"\n\t\t\t\t\t\t\t   \"not supported\"),\n\t\t\t\t\t\terrhint(\"Consider using PL/pgSQL functions instead.\")));\n\t}\n\n\t/*\n\t * We annotate the query for tenant statisisics.\n\t */\n\tAttributeQueryIfAnnotated(query_string, parse->commandType);\n\n\treturn result;\n}\n\n\n/*\n * ExtractRangeTableEntryList is a wrapper around ExtractRangeTableEntryWalker.\n * The function traverses the input query and returns all the range table\n * entries that are in the query tree.\n */\nList *\nExtractRangeTableEntryList(Query *query)\n{\n\tList *rteList = NIL;\n\n\tExtractRangeTableEntryWalker((Node *) query, &rteList);\n\n\treturn rteList;\n}\n\n\n/*\n * NeedsDistributedPlanning returns true if the Citus extension is loaded and\n * the query contains a distributed table.\n *\n * This function allows queries containing local tables to pass through the\n * distributed planner. How to handle local tables is a decision that should\n * be made within the planner\n */\nbool\nNeedsDistributedPlanning(Query *query)\n{\n\tif (!CitusHasBeenLoaded())\n\t{\n\t\treturn false;\n\t}\n\n\tCmdType commandType = query->commandType;\n\n\tif (commandType != CMD_SELECT && commandType != CMD_INSERT &&\n\t\tcommandType != CMD_UPDATE && commandType != CMD_DELETE)\n\t{\n\t\treturn false;\n\t}\n\n\tList *allRTEs = ExtractRangeTableEntryList(query);\n\n\treturn ListContainsDistributedTableRTE(allRTEs, NULL);\n}\n\n\n/*\n * ListContainsDistributedTableRTE gets a list of range table entries\n * and returns true if there is at least one distributed relation range\n * table entry in the list. The boolean maybeHasForeignDistributedTable\n * variable is set to true if the list contains a foreign table.\n */\nstatic bool\nListContainsDistributedTableRTE(List *rangeTableList,\n\t\t\t\t\t\t\t\tbool *maybeHasForeignDistributedTable)\n{\n\tListCell *rangeTableCell = NULL;\n\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\n\t\tif (rangeTableEntry->rtekind != RTE_RELATION)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (HideCitusDependentObjects && IsolationIsSerializable() && IsPgLocksTable(\n\t\t\t\trangeTableEntry))\n\t\t{\n\t\t\t/*\n\t\t\t * Postgres tidscan.sql test fails if we do not filter pg_locks table because\n\t\t\t * test results, which show taken locks in serializable isolation mode,\n\t\t\t * fails by showing extra lock taken by IsCitusTable below.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (IsCitusTable(rangeTableEntry->relid))\n\t\t{\n\t\t\tif (maybeHasForeignDistributedTable != NULL &&\n\t\t\t\tIsForeignTable(rangeTableEntry->relid))\n\t\t\t{\n\t\t\t\t*maybeHasForeignDistributedTable = true;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * PlanContainsDistributedSubPlanRTE checks whether any of the subplans in the given\n * subPlanList is a Read Intermediate Result function scan.\n *\n * It is used by the check after standard_planner() to determine whether the plan\n * still requires distributed planning; in addition to checking the range table for\n * distributed tables, we also need to check whether there are any subplans that\n * read intermediate results, which indicates a distributed subplan and therefore\n * that distributed planning is required.\n */\nstatic bool\nPlanContainsDistributedSubPlanRTE(List *subPlanList)\n{\n\tListCell *subPlanCell = NULL;\n\n\tforeach(subPlanCell, subPlanList)\n\t{\n\t\tNode *planRoot = (Node *) lfirst(subPlanCell);\n\n\t\tif (!IsA(planRoot, FunctionScan))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tList *functionList = ((FunctionScan *) planRoot)->functions;\n\n\t\tif (functionList == NIL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tRangeTblFunction *rangeTblfunction = (RangeTblFunction *) linitial(functionList);\n\n\t\tif (IsReadIntermediateResultFunction(rangeTblfunction->funcexpr))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * AssignRTEIdentities function modifies query tree by adding RTE identities to the\n * RTE_RELATIONs.\n *\n * Please note that, we want to avoid modifying query tree as much as possible\n * because if PostgreSQL changes the way it uses modified fields, that may break\n * our logic.\n *\n * Returns the next id. This can be used to call on a rangeTableList that may've\n * been partially assigned. Should be set to 1 initially.\n */\nstatic int\nAssignRTEIdentities(List *rangeTableList, int rteIdCounter)\n{\n\tListCell *rangeTableCell = NULL;\n\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\n\t\t/*\n\t\t * To be able to track individual RTEs through PostgreSQL's query\n\t\t * planning, we need to be able to figure out whether an RTE is\n\t\t * actually a copy of another, rather than a different one. We\n\t\t * simply number the RTEs starting from 1.\n\t\t *\n\t\t * Note that we're only interested in RTE_RELATIONs and thus assigning\n\t\t * identifiers to those RTEs only.\n\t\t */\n\t\tif (rangeTableEntry->rtekind == RTE_RELATION &&\n\t\t\trangeTableEntry->values_lists == NIL)\n\t\t{\n\t\t\tAssignRTEIdentity(rangeTableEntry, rteIdCounter++);\n\t\t}\n\t}\n\n\treturn rteIdCounter;\n}\n\n\n/*\n * AdjustPartitioningForDistributedPlanning function modifies query tree by\n * changing inh flag and relkind of partitioned tables. We want Postgres to\n * treat partitioned tables as regular relations (i.e. we do not want to\n * expand them to their partitions) since it breaks Citus planning in different\n * ways. We let anything related to partitioning happen on the shards.\n *\n * Please note that, we want to avoid modifying query tree as much as possible\n * because if PostgreSQL changes the way it uses modified fields, that may break\n * our logic.\n */\nstatic void\nAdjustPartitioningForDistributedPlanning(List *rangeTableList,\n\t\t\t\t\t\t\t\t\t\t bool setPartitionedTablesInherited)\n{\n\tListCell *rangeTableCell = NULL;\n\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\n\t\t/*\n\t\t * We want Postgres to behave partitioned tables as regular relations\n\t\t * (i.e. we do not want to expand them to their partitions). To do this\n\t\t * we set each partitioned table's inh flag to appropriate\n\t\t * value before and after dropping to the standart_planner.\n\t\t */\n\t\tif (rangeTableEntry->rtekind == RTE_RELATION &&\n\t\t\tPartitionedTable(rangeTableEntry->relid))\n\t\t{\n\t\t\trangeTableEntry->inh = setPartitionedTablesInherited;\n\n\t\t\tif (setPartitionedTablesInherited)\n\t\t\t{\n\t\t\t\trangeTableEntry->relkind = RELKIND_PARTITIONED_TABLE;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trangeTableEntry->relkind = RELKIND_RELATION;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * RTEWentThroughAdjustPartitioning returns true if the given rangetableentry\n * has been modified through AdjustPartitioningForDistributedPlanning\n * function, false otherwise.\n */\nstatic bool\nRTEWentThroughAdjustPartitioning(RangeTblEntry *rangeTableEntry)\n{\n\treturn (rangeTableEntry->rtekind == RTE_RELATION &&\n\t\t\tPartitionedTable(rangeTableEntry->relid) &&\n\t\t\trangeTableEntry->inh == false);\n}\n\n\n/*\n * AssignRTEIdentity assigns the given rteIdentifier to the given range table\n * entry.\n *\n * To be able to track RTEs through postgres' query planning, which copies and\n * duplicate, and modifies them, we sometimes need to figure out whether two\n * RTEs are copies of the same original RTE. For that we, hackishly, use a\n * field normally unused in RTE_RELATION RTEs.\n *\n * The assigned identifier better be unique within a plantree.\n */\nstatic void\nAssignRTEIdentity(RangeTblEntry *rangeTableEntry, int rteIdentifier)\n{\n\tAssert(rangeTableEntry->rtekind == RTE_RELATION);\n\n\trangeTableEntry->values_lists = list_make2_int(rteIdentifier, rangeTableEntry->inh);\n}\n\n\n/* GetRTEIdentity returns the identity assigned with AssignRTEIdentity. */\nint\nGetRTEIdentity(RangeTblEntry *rte)\n{\n\tAssert(rte->rtekind == RTE_RELATION);\n\n\t/*\n\t * Since SQL functions might be in-lined by standard_planner,\n\t * we might miss assigning an RTE identity for RangeTblEntries\n\t * related to SQL functions. We already have checks in other\n\t * places to throw an error for SQL functions but they are not\n\t * sufficient due to function in-lining; so here we capture such\n\t * cases and throw an error here.\n\t */\n\tif (list_length(rte->values_lists) != 2)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot perform distributed planning on this \"\n\t\t\t\t\t\t\t   \"query because parameterized queries for SQL \"\n\t\t\t\t\t\t\t   \"functions referencing distributed tables are \"\n\t\t\t\t\t\t\t   \"not supported\"),\n\t\t\t\t\t\terrhint(\"Consider using PL/pgSQL functions instead.\")));\n\t}\n\n\tAssert(IsA(rte->values_lists, IntList));\n\n\treturn linitial_int(rte->values_lists);\n}\n\n\n/*\n * GetOriginalInh gets the original value of the inheritance flag set by\n * AssignRTEIdentity. The planner resets this flag in the rewritten query,\n * but we need it during deparsing.\n */\nbool\nGetOriginalInh(RangeTblEntry *rte)\n{\n\treturn lsecond_int(rte->values_lists);\n}\n\n\n/*\n * GetQueryLockMode returns the necessary lock mode to be acquired for the\n * given query. (See comment written in RangeTblEntry->rellockmode)\n */\nLOCKMODE\nGetQueryLockMode(Query *query)\n{\n\tif (IsModifyCommand(query))\n\t{\n\t\treturn RowExclusiveLock;\n\t}\n\telse if (query->hasForUpdate)\n\t{\n\t\treturn RowShareLock;\n\t}\n\telse\n\t{\n\t\treturn AccessShareLock;\n\t}\n}\n\n\n/*\n * IsModifyCommand returns true if the query performs modifications, false\n * otherwise.\n */\nbool\nIsModifyCommand(Query *query)\n{\n\tCmdType commandType = query->commandType;\n\n\tif (commandType == CMD_INSERT || commandType == CMD_UPDATE ||\n\t\tcommandType == CMD_DELETE || commandType == CMD_MERGE)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * IsMultiTaskPlan returns true if job contains multiple tasks.\n */\nbool\nIsMultiTaskPlan(DistributedPlan *distributedPlan)\n{\n\tJob *workerJob = distributedPlan->workerJob;\n\n\tif (workerJob != NULL && list_length(workerJob->taskList) > 1)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * PlanFastPathDistributedStmt creates a distributed planned statement using\n * the FastPathPlanner.\n */\nstatic PlannedStmt *\nPlanFastPathDistributedStmt(DistributedPlanningContext *planContext)\n{\n\tFastPathRestrictionContext *fastPathContext =\n\t\tplanContext->plannerRestrictionContext->fastPathRestrictionContext;\n\tAssert(fastPathContext != NULL);\n\tAssert(fastPathContext->fastPathRouterQuery);\n\n\tFastPathPreprocessParseTree(planContext->query);\n\n\tif (!fastPathContext->delayFastPathPlanning)\n\t{\n\t\tplanContext->plan = FastPathPlanner(planContext->originalQuery,\n\t\t\t\t\t\t\t\t\t\t\tplanContext->query,\n\t\t\t\t\t\t\t\t\t\t\tplanContext->boundParams);\n\t}\n\n\treturn CreateDistributedPlannedStmt(planContext);\n}\n\n\n/*\n * PlanDistributedStmt creates a distributed planned statement using the PG\n * planner.\n */\nstatic PlannedStmt *\nPlanDistributedStmt(DistributedPlanningContext *planContext,\n\t\t\t\t\tint rteIdCounter)\n{\n\t/* may've inlined new relation rtes */\n\tList *rangeTableList = ExtractRangeTableEntryList(planContext->query);\n\trteIdCounter = AssignRTEIdentities(rangeTableList, rteIdCounter);\n\n\n\tPlannedStmt *result = CreateDistributedPlannedStmt(planContext);\n\n\tbool setPartitionedTablesInherited = true;\n\tAdjustPartitioningForDistributedPlanning(rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t setPartitionedTablesInherited);\n\n\treturn result;\n}\n\n\n/*\n * DissuadePlannerFromUsingPlan try dissuade planner when planning a plan that\n * potentially failed due to unresolved prepared statement parameters.\n */\nvoid\nDissuadePlannerFromUsingPlan(PlannedStmt *plan)\n{\n\t/*\n\t * Arbitrarily high cost, but low enough that it can be added up\n\t * without overflowing by choose_custom_plan().\n\t */\n\tAssert(plan != NULL);\n\tplan->planTree->total_cost = FLT_MAX / 100000000;\n}\n\n\n/*\n * CreateDistributedPlannedStmt encapsulates the logic needed to transform a particular\n * query into a distributed plan that is encapsulated by a PlannedStmt.\n */\nstatic PlannedStmt *\nCreateDistributedPlannedStmt(DistributedPlanningContext *planContext)\n{\n\tuint64 planId = NextPlanId++;\n\tbool hasUnresolvedParams = false;\n\n\tPlannedStmt *resultPlan = NULL;\n\n\tif (QueryTreeContainsInlinableCTE(planContext->originalQuery))\n\t{\n\t\t/*\n\t\t * Inlining CTEs as subqueries in the query can avoid recursively\n\t\t * planning some (or all) of the CTEs. In other words, the inlined\n\t\t * CTEs could become part of query pushdown planning, which is much\n\t\t * more efficient than recursively planning. So, first try distributed\n\t\t * planning on the inlined CTEs in the query tree.\n\t\t *\n\t\t * We also should fallback to distributed planning with non-inlined CTEs\n\t\t * if the distributed planning fails with inlined CTEs, because recursively\n\t\t * planning CTEs can provide full SQL coverage, although it might be slow.\n\t\t */\n\t\tresultPlan = InlineCtesAndCreateDistributedPlannedStmt(planId, planContext);\n\t\tif (resultPlan != NULL)\n\t\t{\n\t\t\treturn resultPlan;\n\t\t}\n\t}\n\n\tif (HasUnresolvedExternParamsWalker((Node *) planContext->originalQuery,\n\t\t\t\t\t\t\t\t\t\tplanContext->boundParams))\n\t{\n\t\thasUnresolvedParams = true;\n\t}\n\n\tbool allowRecursivePlanning = true;\n\tDistributedPlan *distributedPlan =\n\t\tCreateDistributedPlan(planId, allowRecursivePlanning,\n\t\t\t\t\t\t\t  planContext->originalQuery,\n\t\t\t\t\t\t\t  planContext->query,\n\t\t\t\t\t\t\t  planContext->boundParams,\n\t\t\t\t\t\t\t  hasUnresolvedParams,\n\t\t\t\t\t\t\t  planContext->plannerRestrictionContext);\n\n\t/*\n\t * If no plan was generated, prepare a generic error to be emitted.\n\t * Normally this error message will never returned to the user, as it's\n\t * usually due to unresolved prepared statement parameters - in that case\n\t * the logic below will force a custom plan (i.e. with parameters bound to\n\t * specific values) to be generated.  But sql (not plpgsql) functions\n\t * unfortunately don't go through a codepath supporting custom plans - so\n\t * we still need to have an error prepared.\n\t */\n\tif (!distributedPlan)\n\t{\n\t\t/* currently always should have a more specific error otherwise */\n\t\tAssert(hasUnresolvedParams);\n\t\tdistributedPlan = CitusMakeNode(DistributedPlan);\n\t\tdistributedPlan->planningError =\n\t\t\tDeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t  \"could not create distributed plan\",\n\t\t\t\t\t\t  \"Possibly this is caused by the use of parameters in SQL \"\n\t\t\t\t\t\t  \"functions, which is not supported in Citus.\",\n\t\t\t\t\t\t  \"Consider using PL/pgSQL functions instead.\");\n\t}\n\n\t/*\n\t * Error out if none of the planners resulted in a usable plan, unless the\n\t * error was possibly triggered by missing parameters.  In that case we'll\n\t * not error out here, but instead rely on postgres' custom plan logic.\n\t * Postgres re-plans prepared statements the first five executions\n\t * (i.e. it produces custom plans), after that the cost of a generic plan\n\t * is compared with the average custom plan cost.  We support otherwise\n\t * unsupported prepared statement parameters by assigning an exorbitant\n\t * cost to the unsupported query.  That'll lead to the custom plan being\n\t * chosen.  But for that to be possible we can't error out here, as\n\t * otherwise that logic is never reached.\n\t */\n\tif (distributedPlan->planningError && !hasUnresolvedParams)\n\t{\n\t\tRaiseDeferredError(distributedPlan->planningError, ERROR);\n\t}\n\n\tCheckAndBuildDelayedFastPathPlan(planContext, distributedPlan);\n\n\t/* remember the plan's identifier for identifying subplans */\n\tdistributedPlan->planId = planId;\n\n\t/* create final plan by combining local plan with distributed plan */\n\tresultPlan = FinalizePlan(planContext->plan, distributedPlan);\n\n\t/*\n\t * As explained above, force planning costs to be unrealistically high if\n\t * query planning failed (possibly) due to prepared statement parameters or\n\t * if it is planned as a multi shard modify query.\n\t */\n\tif ((distributedPlan->planningError ||\n\t\t (UpdateOrDeleteOrMergeQuery(planContext->originalQuery) && IsMultiTaskPlan(\n\t\t\t  distributedPlan))) &&\n\t\thasUnresolvedParams)\n\t{\n\t\tDissuadePlannerFromUsingPlan(resultPlan);\n\t}\n\n\treturn resultPlan;\n}\n\n\n/*\n * InlineCtesAndCreateDistributedPlannedStmt gets all the parameters required\n * for creating a distributed planned statement. The function is primarily a\n * wrapper on top of CreateDistributedPlannedStmt(), by first inlining the\n * CTEs and calling CreateDistributedPlannedStmt() in PG_TRY() block. The\n * function returns NULL if the planning fails on the query where eligable\n * CTEs are inlined.\n */\nstatic PlannedStmt *\nInlineCtesAndCreateDistributedPlannedStmt(uint64 planId,\n\t\t\t\t\t\t\t\t\t\t  DistributedPlanningContext *planContext)\n{\n\t/*\n\t * We'll inline the CTEs and try distributed planning, preserve the original\n\t * query in case the planning fails and we fallback to recursive planning of\n\t * CTEs.\n\t */\n\tQuery *copyOfOriginalQuery = copyObject(planContext->originalQuery);\n\n\tRecursivelyInlineCtesInQueryTree(copyOfOriginalQuery);\n\n\t/* after inlining, we shouldn't have any inlinable CTEs */\n\tAssert(!QueryTreeContainsInlinableCTE(copyOfOriginalQuery));\n\n\t/* simply recurse into CreateDistributedPlannedStmt() in a PG_TRY() block */\n\tPlannedStmt *result = TryCreateDistributedPlannedStmt(planContext->plan,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  copyOfOriginalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  planContext->query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  planContext->boundParams,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  planContext->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext);\n\n\treturn result;\n}\n\n\n/*\n * TryCreateDistributedPlannedStmt is a wrapper around CreateDistributedPlannedStmt, simply\n * calling it in PG_TRY()/PG_CATCH() block. The function returns a PlannedStmt if the input\n * query can be planned by Citus. If not, the function returns NULL and generates a DEBUG4\n * message with the reason for the failure.\n */\nstatic PlannedStmt *\nTryCreateDistributedPlannedStmt(PlannedStmt *localPlan,\n\t\t\t\t\t\t\t\tQuery *originalQuery,\n\t\t\t\t\t\t\t\tQuery *query, ParamListInfo boundParams,\n\t\t\t\t\t\t\t\tPlannerRestrictionContext *plannerRestrictionContext)\n{\n\tMemoryContext savedContext = CurrentMemoryContext;\n\tPlannedStmt *result = NULL;\n\n\tDistributedPlanningContext *planContext = palloc0(sizeof(DistributedPlanningContext));\n\n\tplanContext->plan = localPlan;\n\tplanContext->boundParams = boundParams;\n\tplanContext->originalQuery = originalQuery;\n\tplanContext->query = query;\n\tplanContext->plannerRestrictionContext = plannerRestrictionContext;\n\n\n\tPG_TRY();\n\t{\n\t\tresult = CreateDistributedPlannedStmt(planContext);\n\t}\n\tPG_CATCH();\n\t{\n\t\tMemoryContextSwitchTo(savedContext);\n\t\tErrorData *edata = CopyErrorData();\n\t\tFlushErrorState();\n\n\t\t/* don't try to intercept PANIC or FATAL, let those breeze past us */\n\t\tif (edata->elevel != ERROR)\n\t\t{\n\t\t\tPG_RE_THROW();\n\t\t}\n\n\t\tereport(DEBUG4, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t errmsg(\"Planning after CTEs inlined failed with \"\n\t\t\t\t\t\t\t\t\"\\nmessage: %s\\ndetail: %s\\nhint: %s\",\n\t\t\t\t\t\t\t\tedata->message ? edata->message : \"\",\n\t\t\t\t\t\t\t\tedata->detail ? edata->detail : \"\",\n\t\t\t\t\t\t\t\tedata->hint ? edata->hint : \"\")));\n\n\t\t/* leave the error handling system */\n\t\tFreeErrorData(edata);\n\n\t\tresult = NULL;\n\t}\n\tPG_END_TRY();\n\n\treturn result;\n}\n\n\n/*\n * GetRouterPlanType checks the parse tree to return appropriate plan type.\n */\nstatic RouterPlanType\nGetRouterPlanType(Query *query, Query *originalQuery, bool hasUnresolvedParams)\n{\n\tif (!IsModifyCommand(originalQuery))\n\t{\n\t\treturn SELECT_QUERY;\n\t}\n\n\tOid targetRelationId = ModifyQueryResultRelationId(query);\n\n\tEnsureModificationsCanRunOnRelation(targetRelationId);\n\tEnsurePartitionTableNotReplicated(targetRelationId);\n\n\t/* Check the type of modification being done */\n\n\tif (InsertSelectIntoCitusTable(originalQuery))\n\t{\n\t\tif (hasUnresolvedParams)\n\t\t{\n\t\t\treturn REPLAN_WITH_BOUND_PARAMETERS;\n\t\t}\n\t\treturn INSERT_SELECT_INTO_CITUS_TABLE;\n\t}\n\telse if (InsertSelectIntoLocalTable(originalQuery))\n\t{\n\t\tif (hasUnresolvedParams)\n\t\t{\n\t\t\treturn REPLAN_WITH_BOUND_PARAMETERS;\n\t\t}\n\t\treturn INSERT_SELECT_INTO_LOCAL_TABLE;\n\t}\n\telse if (IsMergeQuery(originalQuery))\n\t{\n\t\tif (hasUnresolvedParams)\n\t\t{\n\t\t\treturn REPLAN_WITH_BOUND_PARAMETERS;\n\t\t}\n\t\treturn MERGE_QUERY;\n\t}\n\telse\n\t{\n\t\treturn DML_QUERY;\n\t}\n}\n\n\n/*\n * CreateDistributedPlan generates a distributed plan for a query.\n * It goes through 3 steps:\n *\n * 1. Try router planner\n * 2. Generate subplans for CTEs and complex subqueries\n *    - If any, go back to step 1 by calling itself recursively\n * 3. Logical planner\n */\nDistributedPlan *\nCreateDistributedPlan(uint64 planId, bool allowRecursivePlanning, Query *originalQuery,\n\t\t\t\t\t  Query *query, ParamListInfo boundParams, bool hasUnresolvedParams,\n\t\t\t\t\t  PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tDistributedPlan *distributedPlan = NULL;\n\tbool hasCtes = originalQuery->cteList != NIL;\n\n\t/* Step 1: Try router planner */\n\n\tRouterPlanType routerPlan = GetRouterPlanType(query, originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t  hasUnresolvedParams);\n\n\tswitch (routerPlan)\n\t{\n\t\tcase INSERT_SELECT_INTO_CITUS_TABLE:\n\t\t{\n\t\t\tdistributedPlan =\n\t\t\t\tCreateInsertSelectPlan(planId,\n\t\t\t\t\t\t\t\t\t   originalQuery,\n\t\t\t\t\t\t\t\t\t   plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t   boundParams);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase INSERT_SELECT_INTO_LOCAL_TABLE:\n\t\t{\n\t\t\tdistributedPlan =\n\t\t\t\tCreateInsertSelectIntoLocalTablePlan(planId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t boundParams,\n\t\t\t\t\t\t\t\t\t\t\t\t\t hasUnresolvedParams,\n\t\t\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase DML_QUERY:\n\t\t{\n\t\t\t/* modifications are always routed through the same planner/executor */\n\t\t\tdistributedPlan =\n\t\t\t\tCreateModifyPlan(originalQuery, query, plannerRestrictionContext);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MERGE_QUERY:\n\t\t{\n\t\t\tdistributedPlan =\n\t\t\t\tCreateMergePlan(planId, originalQuery, query, plannerRestrictionContext,\n\t\t\t\t\t\t\t\tboundParams);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase REPLAN_WITH_BOUND_PARAMETERS:\n\t\t{\n\t\t\t/*\n\t\t\t * Unresolved parameters can cause performance regressions in\n\t\t\t * INSERT...SELECT when the partition column is a parameter\n\t\t\t * because we don't perform any additional pruning in the executor.\n\t\t\t */\n\t\t\treturn NULL;\n\t\t}\n\n\t\tcase SELECT_QUERY:\n\t\t{\n\t\t\t/*\n\t\t\t * For select queries we, if router executor is enabled, first try to\n\t\t\t * plan the query as a router query. If not supported, otherwise try\n\t\t\t * the full blown plan/optimize/physical planning process needed to\n\t\t\t * produce distributed query plans.\n\t\t\t */\n\t\t\tdistributedPlan =\n\t\t\t\tCreateRouterPlan(originalQuery, query, plannerRestrictionContext);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* the functions above always return a plan, possibly with an error */\n\tAssert(distributedPlan);\n\n\tif (distributedPlan->planningError == NULL)\n\t{\n\t\treturn distributedPlan;\n\t}\n\telse\n\t{\n\t\tRaiseDeferredError(distributedPlan->planningError, DEBUG2);\n\t}\n\n\tif (hasUnresolvedParams)\n\t{\n\t\t/*\n\t\t * There are parameters that don't have a value in boundParams.\n\t\t *\n\t\t * The remainder of the planning logic cannot handle unbound\n\t\t * parameters. We return a NULL plan, which will have an\n\t\t * extremely high cost, such that postgres will replan with\n\t\t * bound parameters.\n\t\t */\n\t\treturn NULL;\n\t}\n\n\t/* force evaluation of bound params */\n\tboundParams = copyParamList(boundParams);\n\n\t/*\n\t * If there are parameters that do have a value in boundParams, replace\n\t * them in the original query. This allows us to more easily cut the\n\t * query into pieces (during recursive planning) or deparse parts of\n\t * the query (during subquery pushdown planning).\n\t */\n\toriginalQuery = (Query *) ResolveExternalParams((Node *) originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\tboundParams);\n\tAssert(originalQuery != NULL);\n\n\t/* Step 2: Generate subplans for CTEs and complex subqueries */\n\n\t/*\n\t * Plan subqueries and CTEs that cannot be pushed down by recursively\n\t * calling the planner and return the resulting plans to subPlanList.\n\t * Note that GenerateSubplansForSubqueriesAndCTEs will reset perminfoindexes\n\t * for some RTEs in originalQuery->rtable list, while not changing\n\t * originalQuery->rteperminfos. That's fine because we will go through\n\t * standard_planner again, which will adjust things accordingly in\n\t * set_plan_references>add_rtes_to_flat_rtable>add_rte_to_flat_rtable.\n\t */\n\tList *subPlanList = GenerateSubplansForSubqueriesAndCTEs(planId, originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t routerPlan);\n\n\t/*\n\t * If subqueries were recursively planned then we need to replan the query\n\t * to get the new planner restriction context and apply planner transformations.\n\t *\n\t * We could simplify this code if the logical planner was capable of dealing\n\t * with an original query. In that case, we would only have to filter the\n\t * planner restriction context.\n\t *\n\t * Note that we check both for subplans and whether the query had CTEs\n\t * prior to calling GenerateSubplansForSubqueriesAndCTEs. If none of\n\t * the CTEs are referenced then there are no subplans, but we still want\n\t * to retry the router planner.\n\t */\n\tif (list_length(subPlanList) > 0 || hasCtes)\n\t{\n\t\t/*\n\t\t * recursive planner should handle all the tree from bottom to\n\t\t * top at single pass. i.e. It should have already recursively planned all\n\t\t * required parts in its first pass. Hence, we expect allowRecursivePlanning\n\t\t * to be true. Otherwise, this means we have bug at recursive planner,\n\t\t * which needs to be handled. We add a check here and return error.\n\t\t */\n\t\tif (!allowRecursivePlanning)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"recursive complex joins are only supported \"\n\t\t\t\t\t\t\t\t   \"when all distributed tables are co-located and \"\n\t\t\t\t\t\t\t\t   \"joined on their distribution columns\")));\n\t\t}\n\n\t\tQuery *newQuery = copyObject(originalQuery);\n\t\tbool setPartitionedTablesInherited = false;\n\t\tPlannerRestrictionContext *currentPlannerRestrictionContext =\n\t\t\tCurrentPlannerRestrictionContext();\n\n\t\t/* reset the current planner restrictions context */\n\t\tResetPlannerRestrictionContext(currentPlannerRestrictionContext);\n\n\t\t/*\n\t\t * We force standard_planner to treat partitioned tables as regular tables\n\t\t * by clearing the inh flag on RTEs. We already did this at the start of\n\t\t * distributed_planner, but on a copy of the original query, so we need\n\t\t * to do it again here.\n\t\t */\n\t\tAdjustPartitioningForDistributedPlanning(ExtractRangeTableEntryList(newQuery),\n\t\t\t\t\t\t\t\t\t\t\t\t setPartitionedTablesInherited);\n\n\t\t/*\n\t\t * Some relations may have been removed from the query, but we can skip\n\t\t * AssignRTEIdentities since we currently do not rely on RTE identities\n\t\t * being contiguous.\n\t\t */\n\n\t\tstandard_planner(newQuery, NULL, 0, boundParams);\n\n\t\t/* overwrite the old transformed query with the new transformed query */\n\t\t*query = *newQuery;\n\n\t\t/*\n\t\t * recurse into CreateDistributedPlan with subqueries/CTEs replaced.\n\t\t * We only allow recursive planning once, which should have already done all\n\t\t * the necessary transformations. So, we do not allow recursive planning once again.\n\t\t */\n\t\tallowRecursivePlanning = false;\n\t\tdistributedPlan = CreateDistributedPlan(planId, allowRecursivePlanning,\n\t\t\t\t\t\t\t\t\t\t\t\toriginalQuery, query, NULL, false,\n\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext);\n\n\t\t/* distributedPlan cannot be null since hasUnresolvedParams argument was false */\n\t\tAssert(distributedPlan != NULL);\n\t\tdistributedPlan->subPlanList = subPlanList;\n\n\t\treturn distributedPlan;\n\t}\n\n\t/*\n\t * DML command returns a planning error, even after recursive planning. The\n\t * logical planner cannot handle DML commands so return the plan with the\n\t * error.\n\t */\n\tif (IsModifyCommand(originalQuery))\n\t{\n\t\treturn distributedPlan;\n\t}\n\n\t/*\n\t * CTEs are stripped from the original query by RecursivelyPlanSubqueriesAndCTEs.\n\t * If we get here and there are still CTEs that means that none of the CTEs are\n\t * referenced. We therefore also strip the CTEs from the rewritten query.\n\t */\n\tquery->cteList = NIL;\n\tAssert(originalQuery->cteList == NIL);\n\n\t/* Step 3: Try Logical planner */\n\n\tMultiTreeRoot *logicalPlan = MultiLogicalPlanCreate(originalQuery, query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext);\n\tMultiLogicalPlanOptimize(logicalPlan);\n\n\t/*\n\t * This check is here to make it likely that all node types used in\n\t * Citus are dumpable. Explain can dump logical and physical plans\n\t * using the extended outfuncs infrastructure, but it's infeasible to\n\t * test most plans. MultiQueryContainerNode always serializes the\n\t * physical plan, so there's no need to check that separately\n\t */\n\tCheckNodeIsDumpable((Node *) logicalPlan);\n\n\t/* Create the physical plan */\n\tdistributedPlan = CreatePhysicalDistributedPlan(logicalPlan,\n\t\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext);\n\n\t/* distributed plan currently should always succeed or error out */\n\tAssert(distributedPlan && distributedPlan->planningError == NULL);\n\n\treturn distributedPlan;\n}\n\n\n/*\n * EnsurePartitionTableNotReplicated errors out if the input relation is\n * a partition table and the table has a replication factor greater than\n * one.\n *\n * If the table is not a partition or replication factor is 1, the function\n * becomes a no-op.\n */\nvoid\nEnsurePartitionTableNotReplicated(Oid relationId)\n{\n\tDeferredErrorMessage *deferredError =\n\t\tDeferErrorIfPartitionTableNotSingleReplicated(relationId);\n\tif (deferredError != NULL)\n\t{\n\t\tRaiseDeferredError(deferredError, ERROR);\n\t}\n}\n\n\n/*\n * DeferErrorIfPartitionTableNotSingleReplicated defers error if the input relation\n * is a partition table with replication factor > 1. Otherwise, the function returns\n * NULL.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfPartitionTableNotSingleReplicated(Oid relationId)\n{\n\tif (PartitionTableNoLock(relationId) && !SingleReplicatedTable(relationId))\n\t{\n\t\tOid parentOid = PartitionParentOid(relationId);\n\t\tchar *parentRelationTest = get_rel_name(parentOid);\n\t\tStringInfo errorHint = makeStringInfo();\n\n\t\tappendStringInfo(errorHint, \"Run the query on the parent table \"\n\t\t\t\t\t\t\t\t\t\"\\\"%s\\\" instead.\", parentRelationTest);\n\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"modifications on partitions when replication \"\n\t\t\t\t\t\t\t \"factor is greater than 1 is not supported\",\n\t\t\t\t\t\t\t NULL, errorHint->data);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * ResolveExternalParams replaces the external parameters that appears\n * in the query with the corresponding entries in the boundParams.\n *\n * Note that this function is inspired by eval_const_expr() on Postgres.\n * We cannot use that function because it requires access to PlannerInfo.\n */\nNode *\nResolveExternalParams(Node *inputNode, ParamListInfo boundParams)\n{\n\t/* consider resolving external parameters only when boundParams exists */\n\tif (!boundParams)\n\t{\n\t\treturn inputNode;\n\t}\n\n\tif (inputNode == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (IsA(inputNode, Param))\n\t{\n\t\tParam *paramToProcess = (Param *) inputNode;\n\t\tint numberOfParameters = boundParams->numParams;\n\t\tint parameterId = paramToProcess->paramid;\n\t\tint16 typeLength = 0;\n\t\tbool typeByValue = false;\n\t\tDatum constValue = 0;\n\n\t\tif (paramToProcess->paramkind != PARAM_EXTERN)\n\t\t{\n\t\t\treturn inputNode;\n\t\t}\n\n\t\tif (parameterId < 0)\n\t\t{\n\t\t\treturn inputNode;\n\t\t}\n\n\t\t/* parameterId starts from 1 */\n\t\tint parameterIndex = parameterId - 1;\n\t\tif (parameterIndex >= numberOfParameters)\n\t\t{\n\t\t\treturn inputNode;\n\t\t}\n\n\t\tParamExternData *correspondingParameterData =\n\t\t\t&boundParams->params[parameterIndex];\n\n\t\tif (!(correspondingParameterData->pflags & PARAM_FLAG_CONST))\n\t\t{\n\t\t\treturn inputNode;\n\t\t}\n\n\t\tget_typlenbyval(paramToProcess->paramtype, &typeLength, &typeByValue);\n\n\t\tbool paramIsNull = correspondingParameterData->isnull;\n\t\tif (paramIsNull)\n\t\t{\n\t\t\tconstValue = 0;\n\t\t}\n\t\telse if (typeByValue)\n\t\t{\n\t\t\tconstValue = correspondingParameterData->value;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Out of paranoia ensure that datum lives long enough,\n\t\t\t * although bind params currently should always live\n\t\t\t * long enough.\n\t\t\t */\n\t\t\tconstValue = datumCopy(correspondingParameterData->value, typeByValue,\n\t\t\t\t\t\t\t\t   typeLength);\n\t\t}\n\n\t\treturn (Node *) makeConst(paramToProcess->paramtype, paramToProcess->paramtypmod,\n\t\t\t\t\t\t\t\t  paramToProcess->paramcollid, typeLength, constValue,\n\t\t\t\t\t\t\t\t  paramIsNull, typeByValue);\n\t}\n\telse if (IsA(inputNode, Query))\n\t{\n\t\treturn (Node *) query_tree_mutator((Query *) inputNode, ResolveExternalParams,\n\t\t\t\t\t\t\t\t\t\t   boundParams, 0);\n\t}\n\n\treturn expression_tree_mutator(inputNode, ResolveExternalParams, boundParams);\n}\n\n\n/*\n * GetDistributedPlan returns the associated DistributedPlan for a CustomScan.\n *\n * Callers should only read from the returned data structure, since it may be\n * the plan of a prepared statement and may therefore be reused.\n */\nDistributedPlan *\nGetDistributedPlan(CustomScan *customScan)\n{\n\tAssert(list_length(customScan->custom_private) == 1);\n\n\tNode *node = (Node *) linitial(customScan->custom_private);\n\tAssert(CitusIsA(node, DistributedPlan));\n\n\tCheckNodeCopyAndSerialization(node);\n\n\tDistributedPlan *distributedPlan = (DistributedPlan *) node;\n\n\treturn distributedPlan;\n}\n\n\n/*\n * FinalizePlan combines local plan with distributed plan and creates a plan\n * which can be run by the PostgreSQL executor.\n */\nPlannedStmt *\nFinalizePlan(PlannedStmt *localPlan, DistributedPlan *distributedPlan)\n{\n\tPlannedStmt *finalPlan = NULL;\n\tCustomScan *customScan = makeNode(CustomScan);\n\tMultiExecutorType executorType = MULTI_EXECUTOR_INVALID_FIRST;\n\n\t/* this field is used in JobExecutorType */\n\tdistributedPlan->relationIdList = localPlan->relationOids;\n\n\tif (!distributedPlan->planningError)\n\t{\n\t\texecutorType = JobExecutorType(distributedPlan);\n\t}\n\n\tswitch (executorType)\n\t{\n\t\tcase MULTI_EXECUTOR_ADAPTIVE:\n\t\t{\n\t\t\tcustomScan->methods = &AdaptiveExecutorCustomScanMethods;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MULTI_EXECUTOR_NON_PUSHABLE_INSERT_SELECT:\n\t\t{\n\t\t\tcustomScan->methods = &NonPushableInsertSelectCustomScanMethods;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MULTI_EXECUTOR_NON_PUSHABLE_MERGE_QUERY:\n\t\t{\n\t\t\tcustomScan->methods = &NonPushableMergeCommandCustomScanMethods;\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tcustomScan->methods = &DelayedErrorCustomScanMethods;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (IsMultiTaskPlan(distributedPlan))\n\t{\n\t\t/* if it is not a single task executable plan, inform user according to the log level */\n\t\tif (MultiTaskQueryLogLevel != CITUS_LOG_LEVEL_OFF)\n\t\t{\n\t\t\tereport(MultiTaskQueryLogLevel, (errmsg(\n\t\t\t\t\t\t\t\t\t\t\t\t \"multi-task query about to be executed\"),\n\t\t\t\t\t\t\t\t\t\t\t errhint(\n\t\t\t\t\t\t\t\t\t\t\t\t \"Queries are split to multiple tasks \"\n\t\t\t\t\t\t\t\t\t\t\t\t \"if they have to be split into several\"\n\t\t\t\t\t\t\t\t\t\t\t\t \" queries on the workers.\")));\n\t\t}\n\t}\n\n\tdistributedPlan->queryId = localPlan->queryId;\n\n\tNode *distributedPlanData = (Node *) distributedPlan;\n\n\tcustomScan->custom_private = list_make1(distributedPlanData);\n\n\t/* necessary to avoid extra Result node in PG15 */\n\tcustomScan->flags = CUSTOMPATH_SUPPORT_BACKWARD_SCAN | CUSTOMPATH_SUPPORT_PROJECTION;\n\n\t/*\n\t * Fast path queries cannot have any subplans by definition, so skip\n\t * expensive traversals.\n\t */\n\tif (!distributedPlan->fastPathRouterPlan)\n\t{\n\t\t/*\n\t\t * Record subplans used by distributed plan to make intermediate result\n\t\t * pruning easier.\n\t\t *\n\t\t * We do this before finalizing the plan, because the combineQuery is\n\t\t * rewritten by standard_planner in FinalizeNonRouterPlan.\n\t\t */\n\t\tdistributedPlan->usedSubPlanNodeList = FindSubPlanUsages(distributedPlan);\n\t}\n\n\tif (distributedPlan->combineQuery)\n\t{\n\t\tfinalPlan = FinalizeNonRouterPlan(localPlan, distributedPlan, customScan);\n\t}\n\telse\n\t{\n\t\tfinalPlan = FinalizeRouterPlan(localPlan, customScan);\n\t}\n\n\treturn finalPlan;\n}\n\n\n/*\n * FinalizeNonRouterPlan gets the distributed custom scan plan, and creates the\n * final master select plan on the top of this distributed plan for adaptive executor.\n */\nstatic PlannedStmt *\nFinalizeNonRouterPlan(PlannedStmt *localPlan, DistributedPlan *distributedPlan,\n\t\t\t\t\t  CustomScan *customScan)\n{\n\tPlannedStmt *finalPlan = PlanCombineQuery(distributedPlan, customScan);\n\tfinalPlan->queryId = localPlan->queryId;\n\tfinalPlan->utilityStmt = localPlan->utilityStmt;\n\n\t/* add original range table list for access permission checks */\n\tConcatenateRTablesAndPerminfos(finalPlan, localPlan);\n\n\treturn finalPlan;\n}\n\n\nstatic void\nConcatenateRTablesAndPerminfos(PlannedStmt *mainPlan, PlannedStmt *concatPlan)\n{\n\tmainPlan->rtable = list_concat(mainPlan->rtable, concatPlan->rtable);\n\n\t/*\n\t * concatPlan's range table list is concatenated to mainPlan's range table list\n\t * therefore all the perminfoindexes should be updated to their value\n\t * PLUS the highest perminfoindex in mainPlan's perminfos, which is exactly\n\t * the list length.\n\t */\n\tint mainPlan_highest_perminfoindex = list_length(mainPlan->permInfos);\n\n\tListCell *lc;\n\tforeach(lc, concatPlan->rtable)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n\t\tif (rte->perminfoindex != 0)\n\t\t{\n\t\t\trte->perminfoindex = rte->perminfoindex + mainPlan_highest_perminfoindex;\n\t\t}\n\t}\n\n\t/* finally, concatenate perminfos as well */\n\tmainPlan->permInfos = list_concat(mainPlan->permInfos, concatPlan->permInfos);\n}\n\n\n/*\n * FinalizeRouterPlan gets a CustomScan node which already wrapped distributed\n * part of a router plan and sets it as the direct child of the router plan\n * because we don't run any query on master node for router executable queries.\n * Here, we also rebuild the column list to read from the remote scan.\n */\nstatic PlannedStmt *\nFinalizeRouterPlan(PlannedStmt *localPlan, CustomScan *customScan)\n{\n\tList *columnNameList = NIL;\n\n\tcustomScan->custom_scan_tlist =\n\t\tmakeCustomScanTargetlistFromExistingTargetList(localPlan->planTree->targetlist);\n\tcustomScan->scan.plan.targetlist =\n\t\tmakeTargetListFromCustomScanList(customScan->custom_scan_tlist);\n\n\t/* extract the column names from the final targetlist*/\n\tTargetEntry *targetEntry = NULL;\n\tforeach_declared_ptr(targetEntry, customScan->scan.plan.targetlist)\n\t{\n\t\tString *columnName = makeString(targetEntry->resname);\n\t\tcolumnNameList = lappend(columnNameList, columnName);\n\t}\n\n\tPlannedStmt *routerPlan = makeNode(PlannedStmt);\n\trouterPlan->planTree = (Plan *) customScan;\n\n\tRangeTblEntry *remoteScanRangeTableEntry = RemoteScanRangeTableEntry(columnNameList);\n\trouterPlan->rtable = list_make1(remoteScanRangeTableEntry);\n\n\t/* add original range table list for access permission checks */\n\tConcatenateRTablesAndPerminfos(routerPlan, localPlan);\n\n\trouterPlan->canSetTag = true;\n\trouterPlan->relationOids = NIL;\n\n\trouterPlan->queryId = localPlan->queryId;\n\trouterPlan->utilityStmt = localPlan->utilityStmt;\n\trouterPlan->commandType = localPlan->commandType;\n\trouterPlan->hasReturning = localPlan->hasReturning;\n\n\treturn routerPlan;\n}\n\n\n/*\n * makeCustomScanTargetlistFromExistingTargetList rebuilds the targetlist from the remote\n * query into a list that can be used as the custom_scan_tlist for our Citus Custom Scan.\n */\nstatic List *\nmakeCustomScanTargetlistFromExistingTargetList(List *existingTargetlist)\n{\n\tList *custom_scan_tlist = NIL;\n\n\t/* we will have custom scan range table entry as the first one in the list */\n\tconst int customScanRangeTableIndex = 1;\n\n\t/* build a targetlist to read from the custom scan output */\n\tTargetEntry *targetEntry = NULL;\n\tforeach_declared_ptr(targetEntry, existingTargetlist)\n\t{\n\t\tAssert(IsA(targetEntry, TargetEntry));\n\n\t\t/*\n\t\t * This is unlikely to be hit because we would not need resjunk stuff\n\t\t * at the toplevel of a router query - all things needing it have been\n\t\t * pushed down.\n\t\t */\n\t\tif (targetEntry->resjunk)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* build target entry pointing to remote scan range table entry */\n\t\tVar *newVar = makeVarFromTargetEntry(customScanRangeTableIndex, targetEntry);\n\n\t\tif (newVar->vartype == RECORDOID || newVar->vartype == RECORDARRAYOID)\n\t\t{\n\t\t\t/*\n\t\t\t * Add the anonymous composite type to the type cache and store\n\t\t\t * the key in vartypmod. Eventually this makes its way into the\n\t\t\t * TupleDesc used by the executor, which uses it to parse the\n\t\t\t * query results from the workers in BuildTupleFromCStrings.\n\t\t\t */\n\t\t\tnewVar->vartypmod = BlessRecordExpression(targetEntry->expr);\n\t\t}\n\n\t\tTargetEntry *newTargetEntry = flatCopyTargetEntry(targetEntry);\n\t\tnewTargetEntry->expr = (Expr *) newVar;\n\t\tcustom_scan_tlist = lappend(custom_scan_tlist, newTargetEntry);\n\t}\n\n\treturn custom_scan_tlist;\n}\n\n\n/*\n * makeTargetListFromCustomScanList based on a custom_scan_tlist create the target list to\n * use on the Citus Custom Scan Node. The targetlist differs from the custom_scan_tlist in\n * a way that the expressions in the targetlist all are references to the index (resno) in\n * the custom_scan_tlist in their varattno while the varno is replaced with INDEX_VAR\n * instead of the range table entry index.\n */\nstatic List *\nmakeTargetListFromCustomScanList(List *custom_scan_tlist)\n{\n\tList *targetList = NIL;\n\tTargetEntry *targetEntry = NULL;\n\tint resno = 1;\n\tforeach_declared_ptr(targetEntry, custom_scan_tlist)\n\t{\n\t\t/*\n\t\t * INDEX_VAR is used to reference back to the TargetEntry in custom_scan_tlist by\n\t\t * its resno (index)\n\t\t */\n\t\tVar *newVar = makeVarFromTargetEntry(INDEX_VAR, targetEntry);\n\t\tTargetEntry *newTargetEntry = makeTargetEntry((Expr *) newVar, resno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  targetEntry->resname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  targetEntry->resjunk);\n\t\ttargetList = lappend(targetList, newTargetEntry);\n\t\tresno++;\n\t}\n\treturn targetList;\n}\n\n\n/*\n * BlessRecordExpression ensures we can parse an anonymous composite type on the\n * target list of a query that is sent to the worker.\n *\n * We cannot normally parse record types coming from the workers unless we\n * \"bless\" the tuple descriptor, which adds a transient type to the type cache\n * and assigns it a type mod value, which is the key in the type cache.\n */\nint32\nBlessRecordExpression(Expr *expr)\n{\n\tint32 typeMod = -1;\n\n\tif (IsA(expr, FuncExpr) || IsA(expr, OpExpr))\n\t{\n\t\t/*\n\t\t * Handle functions that return records on the target\n\t\t * list, e.g. SELECT function_call(1,2);\n\t\t */\n\t\tOid resultTypeId = InvalidOid;\n\t\tTupleDesc resultTupleDesc = NULL;\n\n\t\t/* get_expr_result_type blesses the tuple descriptor */\n\t\tTypeFuncClass typeClass = get_expr_result_type((Node *) expr, &resultTypeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   &resultTupleDesc);\n\n\t\tif (typeClass == TYPEFUNC_COMPOSITE)\n\t\t{\n\t\t\ttypeMod = resultTupleDesc->tdtypmod;\n\t\t}\n\t}\n\telse if (IsA(expr, RowExpr))\n\t{\n\t\t/*\n\t\t * Handle row expressions, e.g. SELECT (1,2);\n\t\t */\n\t\tRowExpr *rowExpr = (RowExpr *) expr;\n\t\tListCell *argCell = NULL;\n\t\tint currentResno = 1;\n\n\t\tTupleDesc rowTupleDesc = CreateTemplateTupleDesc(list_length(rowExpr->args));\n\n\t\tforeach(argCell, rowExpr->args)\n\t\t{\n\t\t\tNode *rowArg = (Node *) lfirst(argCell);\n\t\t\tOid rowArgTypeId = exprType(rowArg);\n\t\t\tint rowArgTypeMod = exprTypmod(rowArg);\n\n\t\t\tif (rowArgTypeId == RECORDOID || rowArgTypeId == RECORDARRAYOID)\n\t\t\t{\n\t\t\t\t/* ensure nested rows are blessed as well */\n\t\t\t\trowArgTypeMod = BlessRecordExpression((Expr *) rowArg);\n\t\t\t}\n\n\t\t\tTupleDescInitEntry(rowTupleDesc, currentResno, NULL,\n\t\t\t\t\t\t\t   rowArgTypeId, rowArgTypeMod, 0);\n\t\t\tTupleDescInitEntryCollation(rowTupleDesc, currentResno,\n\t\t\t\t\t\t\t\t\t\texprCollation(rowArg));\n\n\t\t\tcurrentResno++;\n\t\t}\n\n\t\tBlessTupleDesc(rowTupleDesc);\n\n\t\ttypeMod = rowTupleDesc->tdtypmod;\n\t}\n\n\t/*\n\t * Record aggregates need blessed typmods to parse worker results.\n\t * For PG18, AGG_MATCH_RECORD allows MIN/MAX on composite types.\n\t *\n\t * Limitation: For multi-argument aggregates returning RECORD, we only\n\t * bless the first argument's type. This works for MIN/MAX (single-argument)\n\t * but may not handle custom multi-argument aggregates correctly.\n\t */\n\telse if (IsA(expr, Aggref))\n\t{\n\t\tAggref *aggref = (Aggref *) expr;\n\n\t\tif (aggref->aggtype == RECORDOID && list_length(aggref->args) > 0)\n\t\t{\n\t\t\tif (list_length(aggref->args) > 1)\n\t\t\t{\n\t\t\t\tereport(DEBUG2,\n\t\t\t\t\t\t(errmsg(\n\t\t\t\t\t\t\t \"blessing record aggregate with %d arguments, using first\",\n\t\t\t\t\t\t\t list_length(aggref->args))));\n\t\t\t}\n\n\t\t\tTargetEntry *argTle = (TargetEntry *) linitial(aggref->args);\n\t\t\tOid argTypeId = exprType((Node *) argTle->expr);\n\t\t\tint32 argTypeMod = exprTypmod((Node *) argTle->expr);\n\n\t\t\tif (argTypeId == RECORDOID)\n\t\t\t{\n\t\t\t\targTypeMod = BlessRecordExpression((Expr *) argTle->expr);\n\t\t\t}\n\n\t\t\t/* Use a RECORD TupleDesc derived from a named rowtype argument. */\n\t\t\tif (type_is_rowtype(argTypeId))\n\t\t\t{\n\t\t\t\tTupleDesc argTupleDesc =\n\t\t\t\t\tlookup_rowtype_tupdesc_copy(argTypeId, argTypeMod);\n\n\t\t\t\targTupleDesc->tdtypeid = RECORDOID;\n\t\t\t\targTupleDesc->tdtypmod = -1;\n\t\t\t\tBlessTupleDesc(argTupleDesc);\n\t\t\t\ttypeMod = argTupleDesc->tdtypmod;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If argTypeId is not a rowtype, we leave typeMod as -1.\n\t\t\t * This should not happen in practice since AGG_MATCH_RECORD\n\t\t\t * only matches rowtypes, but it's safe to fall through.\n\t\t\t */\n\t\t}\n\t}\n\telse if (IsA(expr, ArrayExpr))\n\t{\n\t\t/*\n\t\t * Handle row array expressions, e.g. SELECT ARRAY[(1,2)];\n\t\t * Postgres allows ARRAY[(1,2),(1,2,3)]. We do not.\n\t\t */\n\t\tArrayExpr *arrayExpr = (ArrayExpr *) expr;\n\n\t\ttypeMod = BlessRecordExpressionList(arrayExpr->elements);\n\t}\n\telse if (IsA(expr, NullIfExpr))\n\t{\n\t\tNullIfExpr *nullIfExpr = (NullIfExpr *) expr;\n\n\t\ttypeMod = BlessRecordExpressionList(nullIfExpr->args);\n\t}\n\telse if (IsA(expr, MinMaxExpr))\n\t{\n\t\tMinMaxExpr *minMaxExpr = (MinMaxExpr *) expr;\n\n\t\ttypeMod = BlessRecordExpressionList(minMaxExpr->args);\n\t}\n\telse if (IsA(expr, CoalesceExpr))\n\t{\n\t\tCoalesceExpr *coalesceExpr = (CoalesceExpr *) expr;\n\n\t\ttypeMod = BlessRecordExpressionList(coalesceExpr->args);\n\t}\n\telse if (IsA(expr, CaseExpr))\n\t{\n\t\tCaseExpr *caseExpr = (CaseExpr *) expr;\n\t\tList *results = NIL;\n\t\tListCell *whenCell = NULL;\n\n\t\tforeach(whenCell, caseExpr->args)\n\t\t{\n\t\t\tCaseWhen *whenArg = (CaseWhen *) lfirst(whenCell);\n\n\t\t\tresults = lappend(results, whenArg->result);\n\t\t}\n\n\t\tif (caseExpr->defresult != NULL)\n\t\t{\n\t\t\tresults = lappend(results, caseExpr->defresult);\n\t\t}\n\n\t\ttypeMod = BlessRecordExpressionList(results);\n\t}\n\n\treturn typeMod;\n}\n\n\n/*\n * BlessRecordExpressionList maps BlessRecordExpression over a list.\n * Returns typmod of all expressions, or -1 if they are not all the same.\n * Ignores expressions with a typmod of -1.\n */\nstatic int32\nBlessRecordExpressionList(List *exprs)\n{\n\tint32 finalTypeMod = -1;\n\tListCell *exprCell = NULL;\n\tforeach(exprCell, exprs)\n\t{\n\t\tNode *exprArg = (Node *) lfirst(exprCell);\n\t\tint32 exprTypeMod = BlessRecordExpression((Expr *) exprArg);\n\n\t\tif (exprTypeMod == -1)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\telse if (finalTypeMod == -1)\n\t\t{\n\t\t\tfinalTypeMod = exprTypeMod;\n\t\t}\n\t\telse if (finalTypeMod != exprTypeMod)\n\t\t{\n\t\t\treturn -1;\n\t\t}\n\t}\n\treturn finalTypeMod;\n}\n\n\n/*\n * RemoteScanRangeTableEntry creates a range table entry from given column name\n * list to represent a remote scan.\n */\nRangeTblEntry *\nRemoteScanRangeTableEntry(List *columnNameList)\n{\n\tRangeTblEntry *remoteScanRangeTableEntry = makeNode(RangeTblEntry);\n\n\t/* we use RTE_VALUES for custom scan because we can't look up relation */\n\tremoteScanRangeTableEntry->rtekind = RTE_VALUES;\n\tremoteScanRangeTableEntry->eref = makeAlias(\"remote_scan\", columnNameList);\n\tremoteScanRangeTableEntry->inh = false;\n\tremoteScanRangeTableEntry->inFromCl = true;\n\n\treturn remoteScanRangeTableEntry;\n}\n\n\n/*\n * CheckNodeIsDumpable checks that the passed node can be dumped using\n * nodeToString(). As this checks is expensive, it's only active when\n * assertions are enabled.\n */\nstatic void\nCheckNodeIsDumpable(Node *node)\n{\n#ifdef USE_ASSERT_CHECKING\n\tchar *out = nodeToString(node);\n\tpfree(out);\n#endif\n}\n\n\n/*\n * CheckNodeCopyAndSerialization checks copy/dump/read functions\n * for nodes and returns copy of the input.\n *\n * It is only active when assertions are enabled, otherwise it returns\n * the input directly. We use this to confirm that our serialization\n * and copy logic produces the correct plan during regression tests.\n *\n * It does not check string equality on node dumps due to differences\n * in some Postgres types.\n */\nstatic Node *\nCheckNodeCopyAndSerialization(Node *node)\n{\n#ifdef USE_ASSERT_CHECKING\n\tchar *out = nodeToString(node);\n\tNode *nodeCopy = copyObject(node);\n\tchar *outCopy = nodeToString(nodeCopy);\n\n\tpfree(out);\n\tpfree(outCopy);\n\n\treturn nodeCopy;\n#else\n\treturn node;\n#endif\n}\n\n\n/*\n * multi_join_restriction_hook is a hook called by postgresql standard planner\n * to notify us about various planning information regarding joins. We use\n * it to learn about the joining column.\n */\nvoid\nmulti_join_restriction_hook(PlannerInfo *root,\n\t\t\t\t\t\t\tRelOptInfo *joinrel,\n\t\t\t\t\t\t\tRelOptInfo *outerrel,\n\t\t\t\t\t\t\tRelOptInfo *innerrel,\n\t\t\t\t\t\t\tJoinType jointype,\n\t\t\t\t\t\t\tJoinPathExtraData *extra)\n{\n\tif (bms_is_empty(innerrel->relids) || bms_is_empty(outerrel->relids))\n\t{\n\t\t/*\n\t\t * We do not expect empty relids. Still, ignoring such JoinRestriction is\n\t\t * preferable for two reasons:\n\t\t * 1. This might be a query that doesn't rely on JoinRestrictions at all (e.g.,\n\t\t * local query).\n\t\t * 2. We cannot process them when they are empty (and likely to segfault if\n\t\t * we allow as-is).\n\t\t */\n\t\tereport(DEBUG1, (errmsg(\"Join restriction information is NULL\")));\n\t}\n\n\t/*\n\t * Use a memory context that's guaranteed to live long enough, could be\n\t * called in a more shortly lived one (e.g. with GEQO).\n\t */\n\tPlannerRestrictionContext *plannerRestrictionContext =\n\t\tCurrentPlannerRestrictionContext();\n\tMemoryContext restrictionsMemoryContext = plannerRestrictionContext->memoryContext;\n\tMemoryContext oldMemoryContext = MemoryContextSwitchTo(restrictionsMemoryContext);\n\n\tJoinRestrictionContext *joinRestrictionContext =\n\t\tplannerRestrictionContext->joinRestrictionContext;\n\tAssert(joinRestrictionContext != NULL);\n\n\tJoinRestriction *joinRestriction = palloc0(sizeof(JoinRestriction));\n\tjoinRestriction->joinType = jointype;\n\tjoinRestriction->plannerInfo = root;\n\n\t/*\n\t * We create a copy of restrictInfoList and relids because with geqo they may\n\t * be created in a memory context which will be deleted when we still need it,\n\t * thus we create a copy of it in our memory context.\n\t */\n\tjoinRestriction->joinRestrictInfoList = copyObject(extra->restrictlist);\n\tjoinRestriction->innerrelRelids = bms_copy(innerrel->relids);\n\tjoinRestriction->outerrelRelids = bms_copy(outerrel->relids);\n\n\tjoinRestrictionContext->joinRestrictionList =\n\t\tlappend(joinRestrictionContext->joinRestrictionList, joinRestriction);\n\n\t/*\n\t * Keep track if we received any semi joins here. If we didn't we can\n\t * later safely convert any semi joins in the rewritten query to inner\n\t * joins.\n\t */\n\tjoinRestrictionContext->hasSemiJoin = joinRestrictionContext->hasSemiJoin ||\n\t\t\t\t\t\t\t\t\t\t  extra->sjinfo->jointype == JOIN_SEMI;\n\tjoinRestrictionContext->hasOuterJoin = joinRestrictionContext->hasOuterJoin ||\n\t\t\t\t\t\t\t\t\t\t   IS_OUTER_JOIN(extra->sjinfo->jointype);\n\n\tMemoryContextSwitchTo(oldMemoryContext);\n}\n\n\n/*\n * multi_relation_restriction_hook is a hook called by postgresql standard planner\n * to notify us about various planning information regarding a relation. We use\n * it to retrieve restrictions on relations.\n */\nvoid\nmulti_relation_restriction_hook(PlannerInfo *root, RelOptInfo *relOptInfo,\n\t\t\t\t\t\t\t\tIndex restrictionIndex, RangeTblEntry *rte)\n{\n\tCitusTableCacheEntry *cacheEntry = NULL;\n\n\tif (ReplaceCitusExtraDataContainer && IsCitusExtraDataContainerRelation(rte))\n\t{\n\t\t/*\n\t\t * We got here by planning the query part that needs to be executed on the query\n\t\t * coordinator node.\n\t\t * We have verified the occurrence of the citus_extra_datacontainer function\n\t\t * encoding the remote scan we plan to execute here. We will replace all paths\n\t\t * with a path describing our custom scan.\n\t\t */\n\t\tPath *path = CreateCitusCustomScanPath(root, relOptInfo, restrictionIndex, rte,\n\t\t\t\t\t\t\t\t\t\t\t   ReplaceCitusExtraDataContainerWithCustomScan);\n\n\t\t/* replace all paths with our custom scan and recalculate cheapest */\n\t\trelOptInfo->pathlist = list_make1(path);\n\t\tset_cheapest(relOptInfo);\n\n\t\treturn;\n\t}\n\n\tAdjustReadIntermediateResultCost(rte, relOptInfo);\n\tAdjustReadIntermediateResultArrayCost(rte, relOptInfo);\n\n\tif (rte->rtekind != RTE_RELATION)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * Use a memory context that's guaranteed to live long enough, could be\n\t * called in a more shortly lived one (e.g. with GEQO).\n\t */\n\tPlannerRestrictionContext *plannerRestrictionContext =\n\t\tCurrentPlannerRestrictionContext();\n\tMemoryContext restrictionsMemoryContext = plannerRestrictionContext->memoryContext;\n\tMemoryContext oldMemoryContext = MemoryContextSwitchTo(restrictionsMemoryContext);\n\n\tbool isCitusTable = IsCitusTable(rte->relid);\n\n\tRelationRestriction *relationRestriction = palloc0(sizeof(RelationRestriction));\n\trelationRestriction->index = restrictionIndex;\n\trelationRestriction->relationId = rte->relid;\n\trelationRestriction->rte = rte;\n\trelationRestriction->relOptInfo = relOptInfo;\n\trelationRestriction->citusTable = isCitusTable;\n\trelationRestriction->plannerInfo = root;\n\n\t/* see comments on GetVarFromAssignedParam() */\n\trelationRestriction->outerPlanParamsList = OuterPlanParamsList(root);\n\trelationRestriction->translatedVars = TranslatedVars(root,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t relationRestriction->index);\n\n\tRelationRestrictionContext *relationRestrictionContext =\n\t\tplannerRestrictionContext->relationRestrictionContext;\n\n\t/*\n\t * We're also keeping track of whether all participant\n\t * tables are reference tables.\n\t */\n\tif (isCitusTable)\n\t{\n\t\tcacheEntry = GetCitusTableCacheEntry(rte->relid);\n\n\t\trelationRestrictionContext->allReferenceTables &=\n\t\t\tIsCitusTableTypeCacheEntry(cacheEntry, REFERENCE_TABLE);\n\t}\n\n\trelationRestrictionContext->relationRestrictionList =\n\t\tlappend(relationRestrictionContext->relationRestrictionList, relationRestriction);\n\n\tMemoryContextSwitchTo(oldMemoryContext);\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\tif (root->query_level == 1 && plannerRestrictionContext->planContext != NULL)\n\t{\n\t\t/* We're at the top query with a distributed context; see if Postgres\n\t\t * has changed the query tree we passed to it in distributed_planner().\n\t\t * This check was necessitated by PG commit 1e4351a, becuase in it the\n\t\t * planner modfies a copy of the passed in query tree with the consequence\n\t\t * that changes are not reflected back to the caller of standard_planner().\n\t\t */\n\t\tQuery *query = plannerRestrictionContext->planContext->query;\n\t\tif (root->parse != query)\n\t\t{\n\t\t\t/*\n\t\t\t * The Postgres planner has reconstructed the query tree, so the query\n\t\t\t * tree our distributed context passed in (to standard_planner() is\n\t\t\t * updated to track the new query tree.\n\t\t\t */\n\t\t\tereport(DEBUG4, (errmsg(\n\t\t\t\t\t\t\t\t \"Detected query reconstruction by Postgres planner, updating \"\n\t\t\t\t\t\t\t\t \"planContext to track it\")));\n\n\t\t\tplannerRestrictionContext->planContext->query = root->parse;\n\t\t}\n\t}\n#endif\n}\n\n\n/*\n * multi_get_relation_info_hook modifies the relation's indexlist\n * if necessary, to avoid a crash in PG16 caused by our\n * Citus function AdjustPartitioningForDistributedPlanning().\n *\n * AdjustPartitioningForDistributedPlanning() is a hack that we use\n * to prevent Postgres' standard_planner() to expand all the partitions\n * for the distributed planning when a distributed partitioned table\n * is queried. It is required for both correctness and performance\n * reasons. Although we can eliminate the use of the function for\n * the correctness (e.g., make sure that rest of the planner can handle\n * partitions), it's performance implication is hard to avoid. Certain\n * planning logic of Citus (such as router or query pushdown) relies\n * heavily on the relationRestrictionList. If\n * AdjustPartitioningForDistributedPlanning() is removed, all the\n * partitions show up in the relationRestrictionList, causing high\n * planning times for such queries.\n */\nvoid\nmulti_get_relation_info_hook(PlannerInfo *root, Oid relationObjectId, bool inhparent,\n\t\t\t\t\t\t\t RelOptInfo *rel)\n{\n\tif (!CitusHasBeenLoaded())\n\t{\n\t\treturn;\n\t}\n\n\tIndex varno = rel->relid;\n\tRangeTblEntry *rangeTableEntry = planner_rt_fetch(varno, root);\n\n\tif (RTEWentThroughAdjustPartitioning(rangeTableEntry))\n\t{\n\t\tListCell *lc = NULL;\n\t\tforeach(lc, rel->indexlist)\n\t\t{\n\t\t\tIndexOptInfo *indexOptInfo = (IndexOptInfo *) lfirst(lc);\n\t\t\tif (get_rel_relkind(indexOptInfo->indexoid) == RELKIND_PARTITIONED_INDEX)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Normally, we should not need this. However, the combination of\n\t\t\t\t * Postgres commit 3c569049b7b502bb4952483d19ce622ff0af5fd6 and\n\t\t\t\t * Citus function AdjustPartitioningForDistributedPlanning()\n\t\t\t\t * forces us to do this. The commit expects partitioned indexes\n\t\t\t\t * to belong to relations with \"inh\" flag set properly. Whereas, the\n\t\t\t\t * function overrides \"inh\" flag. To avoid a crash,\n\t\t\t\t * we go over the list of indexinfos and remove all partitioned indexes.\n\t\t\t\t * Partitioned indexes were ignored pre PG16 anyway, we are essentially\n\t\t\t\t * not breaking any logic.\n\t\t\t\t */\n\t\t\t\trel->indexlist = foreach_delete_current(rel->indexlist, lc);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * TranslatedVars deep copies the translated vars for the given relation index\n * if there is any append rel list.\n */\nstatic List *\nTranslatedVars(PlannerInfo *root, int relationIndex)\n{\n\tList *translatedVars = NIL;\n\n\tif (root->append_rel_list != NIL)\n\t{\n\t\tAppendRelInfo *targetAppendRelInfo =\n\t\t\tFindTargetAppendRelInfo(root, relationIndex);\n\t\tif (targetAppendRelInfo != NULL)\n\t\t{\n\t\t\t/* postgres deletes translated_vars, hence we deep copy them here */\n\t\t\tNode *targetNode = NULL;\n\t\t\tforeach_declared_ptr(targetNode, targetAppendRelInfo->translated_vars)\n\t\t\t{\n\t\t\t\ttranslatedVars =\n\t\t\t\t\tlappend(translatedVars, copyObject(targetNode));\n\t\t\t}\n\t\t}\n\t}\n\treturn translatedVars;\n}\n\n\n/*\n * FindTargetAppendRelInfo finds the target append rel info for the given\n * relation rte index.\n */\nstatic AppendRelInfo *\nFindTargetAppendRelInfo(PlannerInfo *root, int relationRteIndex)\n{\n\tAppendRelInfo *appendRelInfo = NULL;\n\n\t/* iterate on the queries that are part of UNION ALL subselects */\n\tforeach_declared_ptr(appendRelInfo, root->append_rel_list)\n\t{\n\t\t/*\n\t\t * We're only interested in the child rel that is equal to the\n\t\t * relation we're investigating. Here we don't need to find the offset\n\t\t * because postgres adds an offset to child_relid and parent_relid after\n\t\t * calling multi_relation_restriction_hook.\n\t\t */\n\t\tif (appendRelInfo->child_relid == relationRteIndex)\n\t\t{\n\t\t\treturn appendRelInfo;\n\t\t}\n\t}\n\treturn NULL;\n}\n\n\n/*\n * AdjustReadIntermediateResultCost adjusts the row count and total cost\n * of a read_intermediate_result call based on the file size.\n */\nstatic void\nAdjustReadIntermediateResultCost(RangeTblEntry *rangeTableEntry, RelOptInfo *relOptInfo)\n{\n\tif (rangeTableEntry->rtekind != RTE_FUNCTION ||\n\t\tlist_length(rangeTableEntry->functions) != 1)\n\t{\n\t\t/* avoid more expensive checks below for non-functions */\n\t\treturn;\n\t}\n\n\tif (!CitusHasBeenLoaded() || !CheckCitusVersion(DEBUG5))\n\t{\n\t\t/* read_intermediate_result may not exist */\n\t\treturn;\n\t}\n\n\tif (!ContainsReadIntermediateResultFunction((Node *) rangeTableEntry->functions))\n\t{\n\t\treturn;\n\t}\n\n\tRangeTblFunction *rangeTableFunction = (RangeTblFunction *) linitial(\n\t\trangeTableEntry->functions);\n\tFuncExpr *funcExpression = (FuncExpr *) rangeTableFunction->funcexpr;\n\tConst *resultIdConst = (Const *) linitial(funcExpression->args);\n\tif (!IsA(resultIdConst, Const))\n\t{\n\t\t/* not sure how to interpret non-const */\n\t\treturn;\n\t}\n\n\tDatum resultIdDatum = resultIdConst->constvalue;\n\n\tConst *resultFormatConst = (Const *) lsecond(funcExpression->args);\n\tif (!IsA(resultFormatConst, Const))\n\t{\n\t\t/* not sure how to interpret non-const */\n\t\treturn;\n\t}\n\n\tAdjustReadIntermediateResultsCostInternal(relOptInfo,\n\t\t\t\t\t\t\t\t\t\t\t  rangeTableFunction->funccoltypes,\n\t\t\t\t\t\t\t\t\t\t\t  1, &resultIdDatum, resultFormatConst);\n}\n\n\n/*\n * AdjustReadIntermediateResultArrayCost adjusts the row count and total cost\n * of a read_intermediate_results(resultIds, format) call based on the file size.\n */\nstatic void\nAdjustReadIntermediateResultArrayCost(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t\t  RelOptInfo *relOptInfo)\n{\n\tDatum *resultIdArray = NULL;\n\tint resultIdCount = 0;\n\n\tif (rangeTableEntry->rtekind != RTE_FUNCTION ||\n\t\tlist_length(rangeTableEntry->functions) != 1)\n\t{\n\t\t/* avoid more expensive checks below for non-functions */\n\t\treturn;\n\t}\n\n\tif (!CitusHasBeenLoaded() || !CheckCitusVersion(DEBUG5))\n\t{\n\t\t/* read_intermediate_result may not exist */\n\t\treturn;\n\t}\n\n\tif (!ContainsReadIntermediateResultArrayFunction((Node *) rangeTableEntry->functions))\n\t{\n\t\treturn;\n\t}\n\n\tRangeTblFunction *rangeTableFunction =\n\t\t(RangeTblFunction *) linitial(rangeTableEntry->functions);\n\tFuncExpr *funcExpression = (FuncExpr *) rangeTableFunction->funcexpr;\n\tConst *resultIdConst = (Const *) linitial(funcExpression->args);\n\tif (!IsA(resultIdConst, Const))\n\t{\n\t\t/* not sure how to interpret non-const */\n\t\treturn;\n\t}\n\n\tDatum resultIdArrayDatum = resultIdConst->constvalue;\n\tdeconstruct_array(DatumGetArrayTypeP(resultIdArrayDatum), TEXTOID, -1, false,\n\t\t\t\t\t  'i', &resultIdArray, NULL, &resultIdCount);\n\n\tConst *resultFormatConst = (Const *) lsecond(funcExpression->args);\n\tif (!IsA(resultFormatConst, Const))\n\t{\n\t\t/* not sure how to interpret non-const */\n\t\treturn;\n\t}\n\n\tAdjustReadIntermediateResultsCostInternal(relOptInfo,\n\t\t\t\t\t\t\t\t\t\t\t  rangeTableFunction->funccoltypes,\n\t\t\t\t\t\t\t\t\t\t\t  resultIdCount, resultIdArray,\n\t\t\t\t\t\t\t\t\t\t\t  resultFormatConst);\n}\n\n\n/*\n * AdjustReadIntermediateResultsCostInternal adjusts the row count and total cost\n * of reading intermediate results based on file sizes.\n */\nstatic void\nAdjustReadIntermediateResultsCostInternal(RelOptInfo *relOptInfo, List *columnTypes,\n\t\t\t\t\t\t\t\t\t\t  int resultIdCount, Datum *resultIds,\n\t\t\t\t\t\t\t\t\t\t  Const *resultFormatConst)\n{\n\tPathTarget *reltarget = relOptInfo->reltarget;\n\tList *pathList = relOptInfo->pathlist;\n\tdouble rowCost = 0.;\n\tdouble rowSizeEstimate = 0;\n\tdouble rowCountEstimate = 0.;\n\tdouble ioCost = 0.;\n\tQualCost funcCost = { 0., 0. };\n\tint64 totalResultSize = 0;\n\tListCell *typeCell = NULL;\n\n\tDatum resultFormatDatum = resultFormatConst->constvalue;\n\tOid resultFormatId = DatumGetObjectId(resultFormatDatum);\n\tbool binaryFormat = (resultFormatId == BinaryCopyFormatId());\n\n\tfor (int index = 0; index < resultIdCount; index++)\n\t{\n\t\tchar *resultId = TextDatumGetCString(resultIds[index]);\n\t\tint64 resultSize = IntermediateResultSize(resultId);\n\t\tif (resultSize < 0)\n\t\t{\n\t\t\t/* result does not exist, will probably error out later on */\n\t\t\treturn;\n\t\t}\n\n\t\tif (binaryFormat)\n\t\t{\n\t\t\t/* subtract 11-byte signature + 8 byte header + 2-byte footer */\n\t\t\ttotalResultSize -= 21;\n\t\t}\n\n\t\ttotalResultSize += resultSize;\n\t}\n\n\t/* start with the cost of evaluating quals */\n\trowCost += relOptInfo->baserestrictcost.per_tuple;\n\n\t/* postgres' estimate for the width of the rows */\n\trowSizeEstimate += reltarget->width;\n\n\t/* add 2 bytes for column count (binary) or line separator (text) */\n\trowSizeEstimate += 2;\n\n\tforeach(typeCell, columnTypes)\n\t{\n\t\tOid columnTypeId = lfirst_oid(typeCell);\n\t\tOid inputFunctionId = InvalidOid;\n\t\tOid typeIOParam = InvalidOid;\n\n\t\tif (binaryFormat)\n\t\t{\n\t\t\tgetTypeBinaryInputInfo(columnTypeId, &inputFunctionId, &typeIOParam);\n\n\t\t\t/* binary format: 4 bytes for field size */\n\t\t\trowSizeEstimate += 4;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tgetTypeInputInfo(columnTypeId, &inputFunctionId, &typeIOParam);\n\n\t\t\t/* text format: 1 byte for tab separator */\n\t\t\trowSizeEstimate += 1;\n\t\t}\n\n\n\t\t/* add the cost of parsing a column */\n\t\tadd_function_cost(NULL, inputFunctionId, NULL, &funcCost);\n\t}\n\trowCost += funcCost.per_tuple;\n\n\t/* estimate the number of rows based on the file size and estimated row size */\n\trowCountEstimate = Max(1, (double) totalResultSize / rowSizeEstimate);\n\n\t/* cost of reading the data */\n\tioCost = seq_page_cost * totalResultSize / BLCKSZ;\n\n\tAssert(pathList != NIL);\n\n\t/* tell the planner about the cost and row count of the function */\n\tPath *path = (Path *) linitial(pathList);\n\tpath->rows = rowCountEstimate;\n\tpath->total_cost = rowCountEstimate * rowCost + ioCost;\n\n\tpath->startup_cost = funcCost.startup + relOptInfo->baserestrictcost.startup;\n}\n\n\n/*\n * OuterPlanParamsList creates a list of RootPlanParams for outer nodes of the\n * given root. The first item in the list corresponds to parent_root, and the\n * last item corresponds to the outer most node.\n */\nstatic List *\nOuterPlanParamsList(PlannerInfo *root)\n{\n\tList *planParamsList = NIL;\n\n\tfor (PlannerInfo *outerNodeRoot = root->parent_root; outerNodeRoot != NULL;\n\t\t outerNodeRoot = outerNodeRoot->parent_root)\n\t{\n\t\tRootPlanParams *rootPlanParams = palloc0(sizeof(RootPlanParams));\n\t\trootPlanParams->root = outerNodeRoot;\n\n\t\t/*\n\t\t * TODO: In SearchPlannerParamList() we are only interested in Var plan\n\t\t * params, consider copying just them here.\n\t\t */\n\t\trootPlanParams->plan_params = CopyPlanParamList(outerNodeRoot->plan_params);\n\n\t\tplanParamsList = lappend(planParamsList, rootPlanParams);\n\t}\n\n\treturn planParamsList;\n}\n\n\n/*\n * CopyPlanParamList deep copies the input PlannerParamItem list and returns the newly\n * allocated list.\n * Note that we cannot use copyObject() function directly since there is no support for\n * copying PlannerParamItem structs.\n */\nstatic List *\nCopyPlanParamList(List *originalPlanParamList)\n{\n\tListCell *planParamCell = NULL;\n\tList *copiedPlanParamList = NIL;\n\n\tforeach(planParamCell, originalPlanParamList)\n\t{\n\t\tPlannerParamItem *originalParamItem = lfirst(planParamCell);\n\t\tPlannerParamItem *copiedParamItem = makeNode(PlannerParamItem);\n\n\t\tcopiedParamItem->paramId = originalParamItem->paramId;\n\t\tcopiedParamItem->item = copyObject(originalParamItem->item);\n\n\t\tcopiedPlanParamList = lappend(copiedPlanParamList, copiedParamItem);\n\t}\n\n\treturn copiedPlanParamList;\n}\n\n\n/*\n * CreateAndPushPlannerRestrictionContext creates a new planner restriction\n * context with an empty relation restriction context and an empty join and\n * a copy of the given fast path restriction context (if present). Finally,\n * the planner restriction context is inserted to the beginning of the\n * global plannerRestrictionContextList and, in PG18+, given a reference to\n * its distributed plan context.\n */\nstatic void\nCreateAndPushPlannerRestrictionContext(DistributedPlanningContext *planContext,\n\t\t\t\t\t\t\t\t\t   FastPathRestrictionContext *\n\t\t\t\t\t\t\t\t\t   fastPathRestrictionContext)\n{\n\tPlannerRestrictionContext *plannerRestrictionContext =\n\t\tpalloc0(sizeof(PlannerRestrictionContext));\n\n\tplannerRestrictionContext->relationRestrictionContext =\n\t\tpalloc0(sizeof(RelationRestrictionContext));\n\n\tplannerRestrictionContext->joinRestrictionContext =\n\t\tpalloc0(sizeof(JoinRestrictionContext));\n\n\tplannerRestrictionContext->fastPathRestrictionContext =\n\t\tpalloc0(sizeof(FastPathRestrictionContext));\n\n\tif (fastPathRestrictionContext != NULL)\n\t{\n\t\t/* copy the given fast path restriction context */\n\t\tFastPathRestrictionContext *plannersFastPathCtx =\n\t\t\tplannerRestrictionContext->fastPathRestrictionContext;\n\t\tplannersFastPathCtx->fastPathRouterQuery =\n\t\t\tfastPathRestrictionContext->fastPathRouterQuery;\n\t\tplannersFastPathCtx->distributionKeyValue =\n\t\t\tfastPathRestrictionContext->distributionKeyValue;\n\t\tplannersFastPathCtx->distributionKeyHasParam =\n\t\t\tfastPathRestrictionContext->distributionKeyHasParam;\n\t\tplannersFastPathCtx->delayFastPathPlanning =\n\t\t\tfastPathRestrictionContext->delayFastPathPlanning;\n\t}\n\n\tplannerRestrictionContext->memoryContext = CurrentMemoryContext;\n\n\t/* we'll apply logical AND as we add tables */\n\tplannerRestrictionContext->relationRestrictionContext->allReferenceTables = true;\n\n\tplannerRestrictionContextList = lcons(plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContextList);\n\n\tplanContext->plannerRestrictionContext = plannerRestrictionContext;\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\tplannerRestrictionContext->planContext = planContext;\n#endif\n}\n\n\n/*\n * TranslatedVarsForRteIdentity gets an rteIdentity and returns the\n * translatedVars that belong to the range table relation. If no\n * translatedVars found, the function returns NIL;\n */\nList *\nTranslatedVarsForRteIdentity(int rteIdentity)\n{\n\tPlannerRestrictionContext *currentPlannerRestrictionContext =\n\t\tCurrentPlannerRestrictionContext();\n\n\tList *relationRestrictionList =\n\t\tcurrentPlannerRestrictionContext->relationRestrictionContext->\n\t\trelationRestrictionList;\n\tRelationRestriction *relationRestriction = NULL;\n\tforeach_declared_ptr(relationRestriction, relationRestrictionList)\n\t{\n\t\tif (GetRTEIdentity(relationRestriction->rte) == rteIdentity)\n\t\t{\n\t\t\treturn relationRestriction->translatedVars;\n\t\t}\n\t}\n\n\treturn NIL;\n}\n\n\n/*\n * CurrentRestrictionContext returns the most recently added\n * PlannerRestrictionContext from the plannerRestrictionContextList list.\n */\nstatic PlannerRestrictionContext *\nCurrentPlannerRestrictionContext(void)\n{\n\tAssert(plannerRestrictionContextList != NIL);\n\n\tPlannerRestrictionContext *plannerRestrictionContext =\n\t\t(PlannerRestrictionContext *) linitial(plannerRestrictionContextList);\n\n\tif (plannerRestrictionContext == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t\t\terrmsg(\"planner restriction context stack was empty\"),\n\t\t\t\t\t\terrdetail(\"Please report this to the Citus core team.\")));\n\t}\n\n\treturn plannerRestrictionContext;\n}\n\n\n/*\n * PopPlannerRestrictionContext removes the most recently added restriction contexts from\n * the planner restriction context list. The function assumes the list is not empty.\n */\nstatic void\nPopPlannerRestrictionContext(void)\n{\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t/*\n\t * PG18+: Clear the restriction context's planContext pointer; this is done\n\t * by distributed_planner() when popping the context, but in case of error\n\t * during standard_planner() we want to clean up here also.\n\t */\n\tPlannerRestrictionContext *plannerRestrictionContext =\n\t\t(PlannerRestrictionContext *) linitial(plannerRestrictionContextList);\n\tplannerRestrictionContext->planContext = NULL;\n#endif\n\n\tplannerRestrictionContextList = list_delete_first(plannerRestrictionContextList);\n}\n\n\n/*\n * ResetPlannerRestrictionContext resets the element of the given planner\n * restriction context.\n */\nstatic void\nResetPlannerRestrictionContext(PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tplannerRestrictionContext->relationRestrictionContext =\n\t\tpalloc0(sizeof(RelationRestrictionContext));\n\n\tplannerRestrictionContext->joinRestrictionContext =\n\t\tpalloc0(sizeof(JoinRestrictionContext));\n\n\tplannerRestrictionContext->fastPathRestrictionContext =\n\t\tpalloc0(sizeof(FastPathRestrictionContext));\n\n\n\t/* we'll apply logical AND as we add tables */\n\tplannerRestrictionContext->relationRestrictionContext->allReferenceTables = true;\n}\n\n\n/*\n * HasUnresolvedExternParamsWalker returns true if the passed in expression\n * has external parameters that are not contained in boundParams, false\n * otherwise.\n */\nbool\nHasUnresolvedExternParamsWalker(Node *expression, ParamListInfo boundParams)\n{\n\tif (expression == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(expression, Param))\n\t{\n\t\tParam *param = (Param *) expression;\n\t\tint paramId = param->paramid;\n\n\t\t/* only care about user supplied parameters */\n\t\tif (param->paramkind != PARAM_EXTERN)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t/* check whether parameter is available (and valid) */\n\t\tif (boundParams && paramId > 0 && paramId <= boundParams->numParams)\n\t\t{\n\t\t\tOid paramType = InvalidOid;\n\n\t\t\t/* give hook a chance in case parameter is dynamic */\n\t\t\tif (boundParams->paramFetch != NULL)\n\t\t\t{\n\t\t\t\tParamExternData externParamPlaceholder;\n\t\t\t\tparamType = (*boundParams->paramFetch)(boundParams, paramId, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   &externParamPlaceholder)->ptype;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tparamType = boundParams->params[paramId - 1].ptype;\n\t\t\t}\n\n\t\t\tif (OidIsValid(paramType))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/* keep traversing */\n\tif (IsA(expression, Query))\n\t{\n\t\treturn query_tree_walker((Query *) expression,\n\t\t\t\t\t\t\t\t HasUnresolvedExternParamsWalker,\n\t\t\t\t\t\t\t\t boundParams,\n\t\t\t\t\t\t\t\t 0);\n\t}\n\telse\n\t{\n\t\treturn expression_tree_walker(expression,\n\t\t\t\t\t\t\t\t\t  HasUnresolvedExternParamsWalker,\n\t\t\t\t\t\t\t\t\t  boundParams);\n\t}\n}\n\n\n/*\n * ContainsSingleShardTable returns true if given query contains reference\n * to a single-shard table.\n */\nbool\nContainsSingleShardTable(Query *query)\n{\n\tRTEListProperties *rteListProperties = GetRTEListPropertiesForQuery(query);\n\treturn rteListProperties->hasSingleShardDistTable;\n}\n\n\n/*\n * GetRTEListPropertiesForQuery is a wrapper around GetRTEListProperties that\n * returns RTEListProperties for the rte list retrieved from query.\n */\nRTEListProperties *\nGetRTEListPropertiesForQuery(Query *query)\n{\n\tList *rteList = ExtractRangeTableEntryList(query);\n\treturn GetRTEListProperties(rteList);\n}\n\n\n/*\n * GetRTEListProperties returns RTEListProperties struct processing the given\n * rangeTableList.\n */\nstatic RTEListProperties *\nGetRTEListProperties(List *rangeTableList)\n{\n\tRTEListProperties *rteListProperties = palloc0(sizeof(RTEListProperties));\n\n\tRangeTblEntry *rangeTableEntry = NULL;\n\tforeach_declared_ptr(rangeTableEntry, rangeTableList)\n\t{\n\t\tif (rangeTableEntry->rtekind != RTE_RELATION)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\telse if (rangeTableEntry->relkind == RELKIND_VIEW)\n\t\t{\n\t\t\t/*\n\t\t\t * Skip over views, distributed tables within (regular) views are\n\t\t\t * already in rangeTableList.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\n\t\tif (rangeTableEntry->relkind == RELKIND_MATVIEW)\n\t\t{\n\t\t\t/*\n\t\t\t * Record materialized views as they are similar to postgres local tables\n\t\t\t * but it is nice to record them separately.\n\t\t\t *\n\t\t\t * Regular tables, partitioned tables or foreign tables can be a local or\n\t\t\t * distributed tables and we can qualify them accurately.\n\t\t\t *\n\t\t\t * For regular views, we don't care because their definitions are already\n\t\t\t * in the same query tree and we can detect what is inside the view definition.\n\t\t\t *\n\t\t\t * For materialized views, they are just local tables in the queries. But, when\n\t\t\t * REFRESH MATERIALIZED VIEW is used, they behave similar to regular views, adds\n\t\t\t * the view definition to the query. Hence, it is useful to record it seperately\n\t\t\t * and let the callers decide on what to do.\n\t\t\t */\n\t\t\trteListProperties->hasMaterializedView = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tOid relationId = rangeTableEntry->relid;\n\t\tCitusTableCacheEntry *cacheEntry = LookupCitusTableCacheEntry(relationId);\n\t\tif (!cacheEntry)\n\t\t{\n\t\t\trteListProperties->hasPostgresLocalTable = true;\n\t\t}\n\t\telse if (IsCitusTableTypeCacheEntry(cacheEntry, REFERENCE_TABLE))\n\t\t{\n\t\t\trteListProperties->hasReferenceTable = true;\n\t\t}\n\t\telse if (IsCitusTableTypeCacheEntry(cacheEntry, CITUS_LOCAL_TABLE))\n\t\t{\n\t\t\trteListProperties->hasCitusLocalTable = true;\n\t\t}\n\t\telse if (IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE))\n\t\t{\n\t\t\trteListProperties->hasDistributedTable = true;\n\n\t\t\tif (!HasDistributionKeyCacheEntry(cacheEntry))\n\t\t\t{\n\t\t\t\trteListProperties->hasSingleShardDistTable = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trteListProperties->hasDistTableWithShardKey = true;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* it's not expected, but let's do a bug catch here */\n\t\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t\t\t\terrmsg(\"encountered with an unexpected citus \"\n\t\t\t\t\t\t\t\t   \"table type while processing range table \"\n\t\t\t\t\t\t\t\t   \"entries of query\")));\n\t\t}\n\t}\n\n\trteListProperties->hasCitusTable = (rteListProperties->hasDistributedTable ||\n\t\t\t\t\t\t\t\t\t\trteListProperties->hasReferenceTable ||\n\t\t\t\t\t\t\t\t\t\trteListProperties->hasCitusLocalTable);\n\n\treturn rteListProperties;\n}\n\n\n/*\n * WarnIfListHasForeignDistributedTable iterates the given list and logs a WARNING\n * if the given relation is a distributed foreign table.\n * We do that because now we only support Citus Local Tables for foreign tables.\n */\nstatic void\nWarnIfListHasForeignDistributedTable(List *rangeTableList)\n{\n\tstatic bool DistributedForeignTableWarningPrompted = false;\n\n\tRangeTblEntry *rangeTableEntry = NULL;\n\tforeach_declared_ptr(rangeTableEntry, rangeTableList)\n\t{\n\t\tif (DistributedForeignTableWarningPrompted)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tOid relationId = rangeTableEntry->relid;\n\t\tif (IsForeignTable(relationId) && IsCitusTable(relationId) &&\n\t\t\t!IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t\t{\n\t\t\tDistributedForeignTableWarningPrompted = true;\n\t\t\tereport(WARNING, (errmsg(\n\t\t\t\t\t\t\t\t  \"support for distributed foreign tables are deprecated, \"\n\t\t\t\t\t\t\t\t  \"please use Citus managed local tables\"),\n\t\t\t\t\t\t\t  (errdetail(\n\t\t\t\t\t\t\t\t   \"Foreign tables can be added to metadata using UDF: \"\n\t\t\t\t\t\t\t\t   \"citus_add_local_table_to_metadata()\"))));\n\t\t}\n\t}\n}\n\n\nstatic bool\nCheckPostPlanDistribution(DistributedPlanningContext *planContext, bool\n\t\t\t\t\t\t  isDistributedQuery, List *rangeTableList)\n{\n\tif (isDistributedQuery)\n\t{\n\t\tQuery *origQuery = planContext->originalQuery;\n\t\tQuery *plannedQuery = planContext->query;\n\t\tNode *origQuals = origQuery->jointree->quals;\n\t\tNode *plannedQuals = plannedQuery->jointree->quals;\n\n\t\t#if PG_VERSION_NUM >= PG_VERSION_17\n\t\tif (IsMergeQuery(origQuery))\n\t\t{\n\t\t\torigQuals = origQuery->mergeJoinCondition;\n\t\t\tplannedQuals = plannedQuery->mergeJoinCondition;\n\t\t}\n\t\t#endif\n\n\t\t/*\n\t\t * If the WHERE quals have been eliminated by the Postgres planner, possibly\n\t\t * by an OR clause that was simplified to TRUE, we need to check if the\n\t\t * planned query still requires distributed planning.\n\t\t */\n\t\tif (origQuals != NULL && plannedQuals == NULL)\n\t\t{\n\t\t\t/* First check if the plan has a distributed table */\n\t\t\tbool planHasDistribution = ListContainsDistributedTableRTE(\n\t\t\t\tplanContext->plan->rtable, NULL);\n\n\t\t\t/* ..or a distributed subplan */\n\t\t\tplanHasDistribution = planHasDistribution ||\n\t\t\t\t\t\t\t\t  PlanContainsDistributedSubPlanRTE(\n\t\t\t\tplanContext->plan->subplans);\n\n\t\t\t/*\n\t\t\t * The plan has a distributed relation, so we know for sure that\n\t\t\t * the query requires distributed planning.\n\t\t\t */\n\t\t\tif (planHasDistribution)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Otherwise, if the query has less range table entries after Postgres,\n\t\t\t * planning, we should re-evaluate the distribution of the query. Postgres\n\t\t\t * may have optimized away all citus tables, per issues 7782, 7783.\n\t\t\t */\n\t\t\tList *rtesPostPlan = ExtractRangeTableEntryList(plannedQuery);\n\t\t\tif (list_length(rtesPostPlan) < list_length(rangeTableList))\n\t\t\t{\n\t\t\t\tbool hasDistTable = ListContainsDistributedTableRTE(\n\t\t\t\t\trtesPostPlan, NULL);\n\t\t\t\tif (hasDistTable != isDistributedQuery)\n\t\t\t\t{\n\t\t\t\t\tereport(DEBUG4, (errmsg(\n\t\t\t\t\t\t\t\t\t\t \"Plan has flipped from distributed to local \"\n\t\t\t\t\t\t\t\t\t\t \"after Postgres planning, updating distributed to %u\",\n\t\t\t\t\t\t\t\t\t\t hasDistTable)));\n\t\t\t\t\tisDistributedQuery = hasDistTable;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn isDistributedQuery;\n}\n\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n/*\n * DisableSelfJoinElimination is used to prevent self join elimination\n * during distributed query planning to ensure shard queries are correctly\n * generated. PG18's self join elimination (fc069a3a6) changes the Query\n * in a way that can cause problems for queries with a mix of Citus and\n * Postgres tables. Self join elimination is allowed on Postgres tables\n * only so queries involving shards get the benefit of it.\n */\nstatic int\nDisableSelfJoinElimination(void)\n{\n\tint NestLevel = NewGUCNestLevel();\n\tset_config_option(\"enable_self_join_elimination\", \"off\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n\treturn NestLevel;\n}\n\n\n#endif\n"
  },
  {
    "path": "src/backend/distributed/planner/extended_op_node_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * extended_op_node_utils.c implements the logic for building the necessary\n * information that is shared among both the worker and master extended\n * op nodes.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/pg_list.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/restrictinfo.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/extended_op_node_utils.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/pg_dist_partition.h\"\n\n\nstatic bool GroupedByPartitionColumn(MultiNode *node, MultiExtendedOp *opNode);\nstatic bool ExtendedOpNodeContainsRepartitionSubquery(MultiExtendedOp *originalOpNode);\n\nstatic bool HasNonPartitionColumnDistinctAgg(List *targetEntryList, Node *havingQual,\n\t\t\t\t\t\t\t\t\t\t\t List *tableNodeList);\nstatic bool PartitionColumnInTableList(Var *column, List *tableNodeList);\nstatic bool ShouldPullDistinctColumn(bool repartitionSubquery,\n\t\t\t\t\t\t\t\t\t bool groupedByDisjointPartitionColumn,\n\t\t\t\t\t\t\t\t\t bool hasNonPartitionColumnDistinctAgg,\n\t\t\t\t\t\t\t\t\t bool onlyPushableWindowFunctions);\nstatic bool CanPushDownGroupingAndHaving(bool pullUpIntermediateRows,\n\t\t\t\t\t\t\t\t\t\t bool groupedByDisjointPartitionColumn,\n\t\t\t\t\t\t\t\t\t\t bool hasWindowFuncs,\n\t\t\t\t\t\t\t\t\t\t bool onlyPushableWindowFunctions);\n\n\n/*\n * BuildExtendedOpNodeProperties is a helper function that simply builds\n * the necessary information for processing the extended op node. The return\n * value should be used in a read-only manner.\n */\nExtendedOpNodeProperties\nBuildExtendedOpNodeProperties(MultiExtendedOp *extendedOpNode,\n\t\t\t\t\t\t\t  bool hasNonDistributableAggregates)\n{\n\tExtendedOpNodeProperties extendedOpNodeProperties;\n\n\tList *tableNodeList = FindNodesOfType((MultiNode *) extendedOpNode, T_MultiTable);\n\tbool groupedByDisjointPartitionColumn =\n\t\tGroupedByPartitionColumn((MultiNode *) extendedOpNode, extendedOpNode);\n\n\tbool pullUpIntermediateRows = !groupedByDisjointPartitionColumn &&\n\t\t\t\t\t\t\t\t  hasNonDistributableAggregates;\n\n\tbool repartitionSubquery = ExtendedOpNodeContainsRepartitionSubquery(extendedOpNode);\n\n\tList *targetList = extendedOpNode->targetList;\n\tNode *havingQual = extendedOpNode->havingQual;\n\tbool hasNonPartitionColumnDistinctAgg =\n\t\tHasNonPartitionColumnDistinctAgg(targetList, havingQual, tableNodeList);\n\n\tbool pushDownGroupingAndHaving =\n\t\tCanPushDownGroupingAndHaving(pullUpIntermediateRows,\n\t\t\t\t\t\t\t\t\t groupedByDisjointPartitionColumn,\n\t\t\t\t\t\t\t\t\t extendedOpNode->hasWindowFuncs,\n\t\t\t\t\t\t\t\t\t extendedOpNode->onlyPushableWindowFunctions);\n\n\tbool pullDistinctColumns =\n\t\tShouldPullDistinctColumn(repartitionSubquery,\n\t\t\t\t\t\t\t\t groupedByDisjointPartitionColumn,\n\t\t\t\t\t\t\t\t hasNonPartitionColumnDistinctAgg,\n\t\t\t\t\t\t\t\t extendedOpNode->onlyPushableWindowFunctions);\n\n\textendedOpNodeProperties.hasGroupBy = extendedOpNode->groupClauseList != NIL;\n\textendedOpNodeProperties.hasAggregate = TargetListHasAggregates(targetList);\n\n\textendedOpNodeProperties.groupedByDisjointPartitionColumn =\n\t\tgroupedByDisjointPartitionColumn;\n\textendedOpNodeProperties.repartitionSubquery = repartitionSubquery;\n\textendedOpNodeProperties.hasNonPartitionColumnDistinctAgg =\n\t\thasNonPartitionColumnDistinctAgg;\n\textendedOpNodeProperties.pullDistinctColumns = pullDistinctColumns;\n\textendedOpNodeProperties.pullUpIntermediateRows = pullUpIntermediateRows;\n\textendedOpNodeProperties.hasWindowFuncs = extendedOpNode->hasWindowFuncs;\n\textendedOpNodeProperties.onlyPushableWindowFunctions =\n\t\textendedOpNode->onlyPushableWindowFunctions;\n\textendedOpNodeProperties.pushDownGroupingAndHaving = pushDownGroupingAndHaving;\n\n\treturn extendedOpNodeProperties;\n}\n\n\n/*\n * GroupedByPartitionColumn returns true if a GROUP BY in the opNode contains\n * the partition column of the underlying relation, which is determined by\n * searching the MultiNode tree for a MultiTable and MultiPartition with\n * a matching column.\n *\n * When there is a re-partition join, the search terminates at the\n * MultiPartition node. Hence we can push down the GROUP BY if the join\n * column is in the GROUP BY.\n */\nstatic bool\nGroupedByPartitionColumn(MultiNode *node, MultiExtendedOp *opNode)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (CitusIsA(node, MultiTable))\n\t{\n\t\tMultiTable *tableNode = (MultiTable *) node;\n\n\t\tOid relationId = tableNode->relationId;\n\n\t\tif (relationId == SUBQUERY_RELATION_ID)\n\t\t{\n\t\t\t/* ignore subqueries for now */\n\t\t\treturn false;\n\t\t}\n\t\telse if (relationId != SUBQUERY_PUSHDOWN_RELATION_ID)\n\t\t{\n\t\t\tif (!IsCitusTableType(relationId, STRICTLY_PARTITIONED_DISTRIBUTED_TABLE))\n\t\t\t{\n\t\t\t\t/* only range- and hash-distributed tables are strictly partitioned */\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif (GroupedByColumn(opNode->groupClauseList, opNode->targetList,\n\t\t\t\t\t\t\ttableNode->partitionColumn))\n\t\t{\n\t\t\t/* this node is partitioned by a column in the GROUP BY */\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if (CitusIsA(node, MultiPartition))\n\t{\n\t\tMultiPartition *partitionNode = (MultiPartition *) node;\n\n\t\tif (GroupedByColumn(opNode->groupClauseList, opNode->targetList,\n\t\t\t\t\t\t\tpartitionNode->partitionColumn))\n\t\t{\n\t\t\t/* this node is partitioned by a column in the GROUP BY */\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if (UnaryOperator(node))\n\t{\n\t\tMultiNode *childNode = ((MultiUnaryNode *) node)->childNode;\n\n\t\tif (GroupedByPartitionColumn(childNode, opNode))\n\t\t{\n\t\t\t/* a child node is partitioned by a column in the GROUP BY */\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if (BinaryOperator(node))\n\t{\n\t\tMultiNode *leftChildNode = ((MultiBinaryNode *) node)->leftChildNode;\n\t\tMultiNode *rightChildNode = ((MultiBinaryNode *) node)->rightChildNode;\n\n\t\tif (GroupedByPartitionColumn(leftChildNode, opNode) ||\n\t\t\tGroupedByPartitionColumn(rightChildNode, opNode))\n\t\t{\n\t\t\t/* a child node is partitioned by a column in the GROUP BY */\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ExtendedOpNodeContainsRepartitionSubquery is a utility function that\n * returns true if the extended op node contains a re-partition subquery.\n */\nstatic bool\nExtendedOpNodeContainsRepartitionSubquery(MultiExtendedOp *originalOpNode)\n{\n\tMultiNode *parentNode = ParentNode((MultiNode *) originalOpNode);\n\tMultiNode *childNode = ChildNode((MultiUnaryNode *) originalOpNode);\n\n\tif (CitusIsA(parentNode, MultiTable) && CitusIsA(childNode, MultiCollect))\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * HasNonPartitionColumnDistinctAgg returns true if target entry or having qualifier\n * has non-partition column reference in aggregate (distinct) definition. Note that,\n * it only checks aggs subfield of Aggref, it does not check FILTER or SORT clauses.\n * Having any non-column reference like operator expression, function call, or const\n * is considered as a non-partition column. Even if the expression contains partition column\n * like (column + 1), it needs to be evaluated at coordinator, since we can't reliably verify\n * the distinctness of the expression result like (column % 5) or (column + column).\n */\nstatic bool\nHasNonPartitionColumnDistinctAgg(List *targetEntryList, Node *havingQual,\n\t\t\t\t\t\t\t\t List *tableNodeList)\n{\n\tList *targetVarList = pull_var_clause((Node *) targetEntryList,\n\t\t\t\t\t\t\t\t\t\t  PVC_INCLUDE_AGGREGATES |\n\t\t\t\t\t\t\t\t\t\t  PVC_RECURSE_WINDOWFUNCS);\n\n\t/* having clause can't have window functions, no need to recurse for that */\n\tList *havingVarList = pull_var_clause((Node *) havingQual, PVC_INCLUDE_AGGREGATES);\n\tList *aggregateCheckList = list_concat(targetVarList, havingVarList);\n\n\tListCell *aggregateCheckCell = NULL;\n\tforeach(aggregateCheckCell, aggregateCheckList)\n\t{\n\t\tNode *targetNode = lfirst(aggregateCheckCell);\n\t\tListCell *varCell = NULL;\n\t\tbool isPartitionColumn = false;\n\n\t\tif (!IsA(targetNode, Aggref))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tAggref *targetAgg = castNode(Aggref, targetNode);\n\t\tif (targetAgg->aggdistinct == NIL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * We are dealing with a more complex count distinct, it needs to be\n\t\t * evaluated at coordinator level.\n\t\t */\n\t\tif (list_length(targetAgg->args) > 1 || list_length(targetAgg->aggdistinct) > 1)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tTargetEntry *firstTargetEntry = linitial_node(TargetEntry, targetAgg->args);\n\t\tNode *firstTargetExprNode = strip_implicit_coercions(\n\t\t\t(Node *) firstTargetEntry->expr);\n\t\tif (!IsA(firstTargetExprNode, Var))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tList *varList = pull_var_clause_default((Node *) targetAgg->args);\n\t\tforeach(varCell, varList)\n\t\t{\n\t\t\tNode *targetVar = (Node *) lfirst(varCell);\n\n\t\t\tAssert(IsA(targetVar, Var));\n\n\t\t\tisPartitionColumn =\n\t\t\t\tPartitionColumnInTableList((Var *) targetVar, tableNodeList);\n\n\t\t\tif (!isPartitionColumn)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * PartitionColumnInTableList returns true if provided column is a partition\n * column from provided table node list. It also returns false if a column is\n * partition column of an append distributed table.\n */\nstatic bool\nPartitionColumnInTableList(Var *column, List *tableNodeList)\n{\n\tListCell *tableNodeCell = NULL;\n\tforeach(tableNodeCell, tableNodeList)\n\t{\n\t\tMultiTable *tableNode = lfirst(tableNodeCell);\n\t\tVar *partitionColumn = tableNode->partitionColumn;\n\n\t\tif (partitionColumn != NULL &&\n\t\t\tpartitionColumn->varno == column->varno &&\n\t\t\tpartitionColumn->varattno == column->varattno)\n\t\t{\n\t\t\tif (!IsCitusTableType(tableNode->relationId, APPEND_DISTRIBUTED))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ShouldPullDistinctColumn returns true if distinct aggregate should pull\n * individual columns from worker to coordinator and evaluate aggregate operation\n * on the coordinator.\n *\n * Pull cases are:\n * - repartition subqueries\n * - query has count distinct on a non-partition column on at least one target\n * - count distinct is on a non-partition column and query is not\n *   grouped on partition column\n */\nstatic bool\nShouldPullDistinctColumn(bool repartitionSubquery,\n\t\t\t\t\t\t bool groupedByDisjointPartitionColumn,\n\t\t\t\t\t\t bool hasNonPartitionColumnDistinctAgg,\n\t\t\t\t\t\t bool onlyPushableWindowFunctions)\n{\n\tif (repartitionSubquery)\n\t{\n\t\treturn true;\n\t}\n\n\t/* don't pull distinct columns when it can be pushed down */\n\tif (onlyPushableWindowFunctions && groupedByDisjointPartitionColumn)\n\t{\n\t\treturn false;\n\t}\n\telse if (hasNonPartitionColumnDistinctAgg)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CanPushDownGroupingAndHaving returns whether GROUP BY & HAVING should be\n * pushed down to worker.\n */\nstatic bool\nCanPushDownGroupingAndHaving(bool pullUpIntermediateRows,\n\t\t\t\t\t\t\t bool groupedByDisjointPartitionColumn,\n\t\t\t\t\t\t\t bool hasWindowFuncs, bool onlyPushableWindowFunctions)\n{\n\t/* don't push down if we're pulling up */\n\tif (pullUpIntermediateRows)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * If grouped by a partition column we can push down the having qualifier.\n\t *\n\t * When a query with subquery is provided, we can't determine if\n\t * groupedByDisjointPartitionColumn, therefore we also check if there is a\n\t * window function too. If there is a window function we would know that it\n\t * is safe to push down (i.e. it is partitioned on distribution column, and\n\t * if there is a group by, it contains distribution column).\n\t */\n\treturn groupedByDisjointPartitionColumn ||\n\t\t   (hasWindowFuncs && onlyPushableWindowFunctions);\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/fast_path_router_planner.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * fast_path_router_planner.c\n *\n * Planning logic for fast path router planner queries. In this context,\n * we define \"Fast Path Planning\" as trivial queries where Citus\n * can skip relying on the standard_planner() and handle all the planning.\n *\n * For router planner, standard_planner() is mostly important to generate\n * the necessary restriction information. Later, the restriction information\n * generated by the standard_planner is used to decide whether all the shards\n * that a distributed query touches reside on a single worker node. However,\n * standard_planner() does a lot of extra things such as cost estimation and\n * execution path generations which are completely unnecessary in the context\n * of distributed planning.\n *\n * There are certain types of queries where Citus could skip relying on\n * standard_planner() to generate the restriction information. For queries\n * in the following format, Citus does not need any information that the\n * standard_planner() generates:\n *   SELECT ... FROM single_table WHERE distribution_key = X;  or\n *   DELETE FROM single_table WHERE distribution_key = X; or\n *   UPDATE single_table SET value_1 = value_2 + 1 WHERE distribution_key = X;\n *\n * Note that the queries might not be as simple as the above such that\n * GROUP BY, WINDOW FUNCIONS, ORDER BY or HAVING etc. are all acceptable. The\n * only rule is that the query is on a single distributed (or reference) table\n * and there is a \"distribution_key = X;\" in the WHERE clause. With that, we\n * could use to decide the shard that a distributed query touches reside on\n * a worker node.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"optimizer/optimizer.h\"\n#include \"tcop/pquery.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_clauses.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_physical_planner.h\" /* only to use some utility functions */\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/shardinterval_utils.h\"\n\nbool EnableFastPathRouterPlanner = true;\nbool EnableLocalFastPathQueryOptimization = true;\n\nstatic bool ColumnAppearsMultipleTimes(Node *quals, Var *distributionKey);\nstatic bool DistKeyInSimpleOpExpression(Expr *clause, Var *distColumn,\n\t\t\t\t\t\t\t\t\t\tNode **distributionKeyValue);\nstatic bool ConjunctionContainsColumnFilter(Node *node,\n\t\t\t\t\t\t\t\t\t\t\tVar *column,\n\t\t\t\t\t\t\t\t\t\t\tNode **distributionKeyValue);\n\n/*\n * FastPathPreprocessParseTree is used to apply transformations on the parse tree\n * that are expected by the Postgres planner. This is called on both delayed FastPath\n * and non-delayed FastPath queries.\n */\nvoid\nFastPathPreprocessParseTree(Query *parse)\n{\n\t/*\n\t * Citus planner relies on some of the transformations on constant\n\t * evaluation on the parse tree.\n\t */\n\tparse->targetList =\n\t\t(List *) eval_const_expressions(NULL, (Node *) parse->targetList);\n\tparse->jointree->quals =\n\t\t(Node *) eval_const_expressions(NULL, (Node *) parse->jointree->quals);\n}\n\n\n/*\n * FastPathPlanner is intended to be used instead of standard_planner() for trivial\n * queries defined by FastPathRouterQuery().\n *\n * The basic idea is that we need a very little of what standard_planner() does for\n * the trivial queries. So skip calling standard_planner() to save CPU cycles.\n *\n */\nPlannedStmt *\nFastPathPlanner(Query *originalQuery, Query *parse, ParamListInfo boundParams)\n{\n\tPlannedStmt *result = GeneratePlaceHolderPlannedStmt(originalQuery);\n\n\treturn result;\n}\n\n\n/*\n * GeneratePlaceHolderPlannedStmt creates a planned statement which contains\n * a sequential scan on the relation that is accessed by the input query.\n * The returned PlannedStmt is not proper (e.g., set_plan_references() is\n * not called on the plan or the quals are not set), so should not be\n * passed to the executor directly. This is only useful to have a\n * placeholder PlannedStmt where target list is properly set. Note that\n * this is what router executor relies on.\n *\n * This function makes the assumption (and the assertion) that\n * the input query is in the form defined by FastPathRouterQuery().\n */\nPlannedStmt *\nGeneratePlaceHolderPlannedStmt(Query *parse)\n{\n\tPlannedStmt *result = makeNode(PlannedStmt);\n\tSeqScan *scanNode = makeNode(SeqScan);\n\tPlan *plan = &(scanNode->scan.plan);\n\n\tFastPathRestrictionContext fprCtxt PG_USED_FOR_ASSERTS_ONLY = { 0 };\n\n\tAssert(FastPathRouterQuery(parse, &fprCtxt));\n\n\t/* there is only a single relation rte */\n\tscanNode->scan.scanrelid = 1;\n\n\tplan->targetlist =\n\t\tcopyObject(FetchStatementTargetList((Node *) parse));\n\n\tplan->qual = NULL;\n\tplan->lefttree = NULL;\n\tplan->righttree = NULL;\n\tplan->plan_node_id = 1;\n\n\t/*  rtable is used for access permission checks */\n\tresult->commandType = parse->commandType;\n\tresult->queryId = parse->queryId;\n\tresult->stmt_len = parse->stmt_len;\n\n\tresult->rtable = copyObject(parse->rtable);\n\tresult->permInfos = copyObject(parse->rteperminfos);\n\tresult->planTree = (Plan *) plan;\n\tresult->hasReturning = (parse->returningList != NIL);\n\n\tOid relationId = ExtractFirstCitusTableId(parse);\n\tresult->relationOids = list_make1_oid(relationId);\n\n\treturn result;\n}\n\n\n/*\n * InitializeFastPathContext - helper function to initialize a FastPath\n * restriction context with the details that the FastPath code path needs.\n */\nstatic void\nInitializeFastPathContext(FastPathRestrictionContext *fastPathContext,\n\t\t\t\t\t\t  Node *distributionKeyValue,\n\t\t\t\t\t\t  bool canAvoidDeparse,\n\t\t\t\t\t\t  Query *query)\n{\n\tAssert(fastPathContext != NULL);\n\tAssert(!fastPathContext->fastPathRouterQuery);\n\tAssert(!fastPathContext->delayFastPathPlanning);\n\n\t/*\n\t * We're looking at a fast path query, so we can fill the\n\t * fastPathContext with relevant details.\n\t */\n\tfastPathContext->fastPathRouterQuery = true;\n\tif (distributionKeyValue == NULL)\n\t{\n\t\t/* nothing to record */\n\t}\n\telse if (IsA(distributionKeyValue, Const))\n\t{\n\t\tfastPathContext->distributionKeyValue = (Const *) distributionKeyValue;\n\t}\n\telse if (IsA(distributionKeyValue, Param))\n\t{\n\t\tfastPathContext->distributionKeyHasParam = true;\n\t}\n\n\t/*\n\t * If local execution and the fast path optimization to\n\t * avoid deparse are enabled, and it is safe to do local\n\t * execution..\n\t */\n\tif (EnableLocalFastPathQueryOptimization &&\n\t\tEnableLocalExecution &&\n\t\tGetCurrentLocalExecutionStatus() != LOCAL_EXECUTION_DISABLED)\n\t{\n\t\t/*\n\t\t * .. we can delay fast path planning until we know whether\n\t\t * or not the shard is local. Make a final check for volatile\n\t\t * functions in the query tree to determine if we should delay\n\t\t * the fast path planning.\n\t\t */\n\t\tfastPathContext->delayFastPathPlanning = canAvoidDeparse &&\n\t\t\t\t\t\t\t\t\t\t\t\t !FindNodeMatchingCheckFunction(\n\t\t\t(Node *) query,\n\t\t\tCitusIsVolatileFunction);\n\t}\n}\n\n\n/*\n * FastPathRouterQuery gets a query and returns true if the query is eligible for\n * being a fast path router query. It also fills the given fastPathContext with\n * details about the query such as the distribution key value (if available),\n * whether the distribution key is a parameter, and the range table entry for the\n * table being queried.\n * The requirements for the fast path query can be listed below:\n *\n *   - SELECT/UPDATE/DELETE query without CTES, sublinks-subqueries, set operations\n *   - The query should touch only a single hash distributed or reference table\n *   - The distribution with equality operator should be in the WHERE clause\n *      and it should be ANDed with any other filters. Also, the distribution\n *      key should only exist once in the WHERE clause. So basically,\n *          SELECT ... FROM dist_table WHERE dist_key = X\n *      If the filter is a const, distributionKeyValue is set\n *   - All INSERT statements (including multi-row INSERTs) as long as the commands\n *     don't have any sublinks/CTEs etc\n *   -\n */\nbool\nFastPathRouterQuery(Query *query, FastPathRestrictionContext *fastPathContext)\n{\n\tif (!EnableFastPathRouterPlanner)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsMergeQuery(query))\n\t{\n\t\t/* MERGE command is not a fast path query */\n\t\treturn false;\n\t}\n\n\t/*\n\t * We want to deal with only very simple queries. Some of the\n\t * checks might be too restrictive, still we prefer this way.\n\t */\n\tif (query->cteList != NIL || query->hasSubLinks ||\n\t\tquery->setOperations != NULL || query->hasTargetSRFs ||\n\t\tquery->hasModifyingCTE)\n\t{\n\t\treturn false;\n\t}\n\n\tif (CheckInsertSelectQuery(query))\n\t{\n\t\t/* we don't support INSERT..SELECT in the fast-path */\n\t\treturn false;\n\t}\n\telse if (query->commandType == CMD_INSERT)\n\t{\n\t\t/* we don't need to do any further checks, all INSERTs are fast-path */\n\t\tInitializeFastPathContext(fastPathContext, NULL, true, query);\n\t\treturn true;\n\t}\n\n\tint numFromRels = list_length(query->rtable);\n\n\t/* make sure that there is only one range table in FROM clause */\n\tif ((numFromRels != 1)\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t    /* with a PG18+ twist for GROUP rte - if present make sure there's two range tables */\n\t\t&& (!query->hasGroupRTE || numFromRels != 2)\n#endif\n\t\t)\n\t{\n\t\treturn false;\n\t}\n\n\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) linitial(query->rtable);\n\tif (rangeTableEntry->rtekind != RTE_RELATION)\n\t{\n\t\treturn false;\n\t}\n\n\t/* we don't want to deal with append/range distributed tables */\n\tOid distributedTableId = rangeTableEntry->relid;\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(distributedTableId);\n\tif (IsCitusTableTypeCacheEntry(cacheEntry, RANGE_DISTRIBUTED) ||\n\t\tIsCitusTableTypeCacheEntry(cacheEntry, APPEND_DISTRIBUTED))\n\t{\n\t\treturn false;\n\t}\n\n\tbool isFastPath = false;\n\tbool canAvoidDeparse = false;\n\tNode *distributionKeyValue = NULL;\n\n\t/*\n\t * If the table doesn't have a distribution column, we don't need to\n\t * check anything further.\n\t */\n\tVar *distributionKey = PartitionColumn(distributedTableId, 1);\n\tif (!distributionKey)\n\t{\n\t\t/*\n\t\t * Local execution may avoid a deparse on single shard distributed tables or\n\t\t * citus local tables. We don't yet support reference tables in this code-path\n\t\t * because modifications on reference tables are complicated to support here.\n\t\t */\n\t\tcanAvoidDeparse = IsCitusTableTypeCacheEntry(cacheEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t\t SINGLE_SHARD_DISTRIBUTED) ||\n\t\t\t\t\t\t  IsCitusTableTypeCacheEntry(cacheEntry, CITUS_LOCAL_TABLE);\n\t\tisFastPath = true;\n\t}\n\telse\n\t{\n\t\tFromExpr *joinTree = query->jointree;\n\t\tNode *quals = NULL;\n\n\t\tcanAvoidDeparse = IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE);\n\n\t\tif (joinTree == NULL ||\n\t\t\t(joinTree->quals == NULL && canAvoidDeparse))\n\t\t{\n\t\t\t/* no quals, not a fast path query */\n\t\t\treturn false;\n\t\t}\n\n\t\tquals = joinTree->quals;\n\t\tif (quals != NULL && IsA(quals, List))\n\t\t{\n\t\t\tquals = (Node *) make_ands_explicit((List *) quals);\n\t\t}\n\n\t\t/*\n\t\t * Distribution column must be used in a simple equality match check and it must be\n\t\t * place at top level conjunction operator. In simple words, we should have\n\t\t *\t    WHERE dist_key = VALUE [AND  ....];\n\t\t *\n\t\t *\tWe're also not allowing any other appearances of the distribution key in the quals.\n\t\t *\n\t\t *\tOverall the logic might sound fuzzy since it involves two individual checks:\n\t\t *\t    (a) Check for top level AND operator with one side being \"dist_key = const\"\n\t\t *\t    (b) Only allow single appearance of \"dist_key\" in the quals\n\t\t *\n\t\t *\tThis is to simplify both of the individual checks and omit various edge cases\n\t\t *\tthat might arise with multiple distribution keys in the quals.\n\t\t */\n\t\tisFastPath = (ConjunctionContainsColumnFilter(quals, distributionKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  &distributionKeyValue) &&\n\t\t\t\t\t  !ColumnAppearsMultipleTimes(quals, distributionKey));\n\t}\n\n\tif (isFastPath)\n\t{\n\t\tInitializeFastPathContext(fastPathContext, distributionKeyValue, canAvoidDeparse,\n\t\t\t\t\t\t\t\t  query);\n\t}\n\n\treturn isFastPath;\n}\n\n\n/*\n * ColumnAppearsMultipleTimes returns true if the given input\n * appears more than once in the quals.\n */\nstatic bool\nColumnAppearsMultipleTimes(Node *quals, Var *distributionKey)\n{\n\tListCell *varClauseCell = NULL;\n\tint partitionColumnReferenceCount = 0;\n\n\t/* make sure partition column is used only once in the quals */\n\tList *varClauseList = pull_var_clause_default(quals);\n\tforeach(varClauseCell, varClauseList)\n\t{\n\t\tVar *column = (Var *) lfirst(varClauseCell);\n\t\tif (equal(column, distributionKey))\n\t\t{\n\t\t\tpartitionColumnReferenceCount++;\n\n\t\t\tif (partitionColumnReferenceCount > 1)\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ConjunctionContainsColumnFilter returns true if the query contains an exact\n * match (equal) expression on the provided column. The function returns true only\n * if the match expression has an AND relation with the rest of the expression tree.\n *\n * If the conjuction contains column filter which is const, distributionKeyValue is set.\n */\nstatic bool\nConjunctionContainsColumnFilter(Node *node, Var *column, Node **distributionKeyValue)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, OpExpr))\n\t{\n\t\tOpExpr *opExpr = (OpExpr *) node;\n\t\tbool distKeyInSimpleOpExpression =\n\t\t\tDistKeyInSimpleOpExpression((Expr *) opExpr, column, distributionKeyValue);\n\n\t\tif (!distKeyInSimpleOpExpression)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\treturn OperatorImplementsEquality(opExpr->opno);\n\t}\n\telse if (IsA(node, BoolExpr))\n\t{\n\t\tBoolExpr *boolExpr = (BoolExpr *) node;\n\t\tList *argumentList = boolExpr->args;\n\t\tListCell *argumentCell = NULL;\n\n\n\t\t/*\n\t\t * We do not descend into boolean expressions other than AND.\n\t\t * If the column filter appears in an OR clause, we do not\n\t\t * consider it even if it is logically the same as a single value\n\t\t * comparison (e.g. `<column> = <Const> OR false`)\n\t\t */\n\t\tif (boolExpr->boolop != AND_EXPR)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tforeach(argumentCell, argumentList)\n\t\t{\n\t\t\tNode *argumentNode = (Node *) lfirst(argumentCell);\n\n\t\t\tif (ConjunctionContainsColumnFilter(argumentNode, column,\n\t\t\t\t\t\t\t\t\t\t\t\tdistributionKeyValue))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * DistKeyInSimpleOpExpression checks whether given expression is a simple operator\n * expression with either (dist_key = param) or (dist_key = const). Note that the\n * operands could be in the reverse order as well.\n *\n * When a const is found, distributionKeyValue is set.\n */\nstatic bool\nDistKeyInSimpleOpExpression(Expr *clause, Var *distColumn, Node **distributionKeyValue)\n{\n\tParam *paramClause = NULL;\n\tConst *constantClause = NULL;\n\n\tVar *columnInExpr = NULL;\n\n\tNode *leftOperand;\n\tNode *rightOperand;\n\tif (!BinaryOpExpression(clause, &leftOperand, &rightOperand))\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(rightOperand, Param) && IsA(leftOperand, Var))\n\t{\n\t\tparamClause = (Param *) rightOperand;\n\t\tcolumnInExpr = (Var *) leftOperand;\n\t}\n\telse if (IsA(leftOperand, Param) && IsA(rightOperand, Var))\n\t{\n\t\tparamClause = (Param *) leftOperand;\n\t\tcolumnInExpr = (Var *) rightOperand;\n\t}\n\telse if (IsA(rightOperand, Const) && IsA(leftOperand, Var))\n\t{\n\t\tconstantClause = (Const *) rightOperand;\n\t\tcolumnInExpr = (Var *) leftOperand;\n\t}\n\telse if (IsA(leftOperand, Const) && IsA(rightOperand, Var))\n\t{\n\t\tconstantClause = (Const *) leftOperand;\n\t\tcolumnInExpr = (Var *) rightOperand;\n\t}\n\telse\n\t{\n\t\treturn false;\n\t}\n\n\tif (paramClause && paramClause->paramkind != PARAM_EXTERN)\n\t{\n\t\t/* we can only handle param_externs */\n\t\treturn false;\n\t}\n\telse if (constantClause && constantClause->constisnull)\n\t{\n\t\t/* we can only handle non-null constants */\n\t\treturn false;\n\t}\n\n\t/* at this point we should have the columnInExpr */\n\tAssert(columnInExpr);\n\tbool distColumnExists = equal(distColumn, columnInExpr);\n\tif (distColumnExists && constantClause != NULL &&\n\t\tdistColumn->vartype == constantClause->consttype &&\n\t\t*distributionKeyValue == NULL)\n\t{\n\t\t/* if the vartypes do not match, let shard pruning handle it later */\n\t\t*distributionKeyValue = (Node *) copyObject(constantClause);\n\t}\n\telse if (paramClause != NULL)\n\t{\n\t\t*distributionKeyValue = (Node *) copyObject(paramClause);\n\t}\n\n\treturn distColumnExists;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/function_call_delegation.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * function_call_delegation.c\n *    Planning logic for delegating a function call to a worker when the\n *    function was distributed with a distribution argument and the worker\n *    has metadata.\n *\n * Copyright (c), Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/defrem.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/primnodes.h\"\n#include \"nodes/print.h\"\n#include \"optimizer/clauses.h\"\n#include \"parser/parse_coerce.h\"\n#include \"parser/parsetree.h\"\n#include \"tcop/dest.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/function_call_delegation.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n\nstruct ParamWalkerContext\n{\n\tbool hasParam;\n\tParamKind paramKind;\n};\n\nextern AllowedDistributionColumn AllowedDistributionColumnValue;\n\nstatic bool contain_param_walker(Node *node, void *context);\nstatic void CheckDelegatedFunctionExecution(DistObjectCacheEntry *procedure,\n\t\t\t\t\t\t\t\t\t\t\tFuncExpr *funcExpr);\nstatic bool IsQuerySimple(Query *query);\nstatic FuncExpr * FunctionInFromClause(List *fromlist, Query *query);\nstatic void EnableInForceDelegatedFuncExecution(Const *distArgument, uint32 colocationId);\n\n\n/* global variable keeping track of whether we are in a delegated function call */\nbool InTopLevelDelegatedFunctionCall = false;\n\n\n/* global variable keeping track of whether we are in a delegated function call */\nbool InDelegatedFunctionCall = false;\n\n\n/*\n * contain_param_walker scans node for Param nodes.\n * Ignore the return value, instead check context afterwards.\n *\n * context is a struct ParamWalkerContext*.\n * hasParam is set to true if we find a Param node.\n * paramKind is set to the paramkind of the Param node if any found.\n * paramKind is set to PARAM_EXEC if both PARAM_EXEC & PARAM_EXTERN are found.\n *\n * By time we walk, Param nodes are either PARAM_EXTERN or PARAM_EXEC.\n */\nstatic bool\ncontain_param_walker(Node *node, void *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\tif (IsA(node, Param))\n\t{\n\t\tParam *paramNode = (Param *) node;\n\t\tstruct ParamWalkerContext *pwcontext =\n\t\t\t(struct ParamWalkerContext *) context;\n\n\t\tpwcontext->hasParam = true;\n\t\tpwcontext->paramKind = paramNode->paramkind;\n\t\treturn paramNode->paramkind == PARAM_EXEC;\n\t}\n\telse\n\t{\n\t\treturn expression_tree_walker((Node *) node, contain_param_walker, context);\n\t}\n}\n\n\n/*\n * TryToDelegateFunctionCall calls a function on the worker if possible.\n * We only support delegating the SELECT func(...) form for distributed\n * functions colocated by distributed tables, and not more complicated\n * forms involving multiple function calls, FROM clauses, WHERE clauses,\n * ... Those complex forms are handled in the coordinator.\n */\nPlannedStmt *\nTryToDelegateFunctionCall(DistributedPlanningContext *planContext)\n{\n\tShardPlacement *placement = NULL;\n\tstruct ParamWalkerContext walkerParamContext = { 0 };\n\tbool inTransactionBlock = false;\n\n\tif (!CitusHasBeenLoaded() || !CheckCitusVersion(DEBUG4))\n\t{\n\t\t/* Citus is not ready to determine whether function is distributed */\n\t\treturn NULL;\n\t}\n\n\tint32 localGroupId = GetLocalGroupId();\n\tif (localGroupId == GROUP_ID_UPGRADING)\n\t{\n\t\t/* do not delegate while upgrading */\n\t\treturn NULL;\n\t}\n\n\tif (planContext->query == NULL)\n\t{\n\t\t/* no query (mostly here to be defensive) */\n\t\treturn NULL;\n\t}\n\n\tif (planContext->query->commandType != CMD_SELECT)\n\t{\n\t\t/* not a SELECT */\n\t\treturn NULL;\n\t}\n\n\tFromExpr *joinTree = planContext->query->jointree;\n\tif (joinTree == NULL)\n\t{\n\t\t/* no join tree (mostly here to be defensive) */\n\t\treturn NULL;\n\t}\n\n\tif (joinTree->quals != NULL)\n\t{\n\t\t/* query has a WHERE section */\n\t\treturn NULL;\n\t}\n\n\tFuncExpr *fromFuncExpr = NULL;\n\tif (joinTree->fromlist != NIL)\n\t{\n\t\tif (list_length(joinTree->fromlist) != 1)\n\t\t{\n\t\t\t/* e.g. SELECT ... FROM rel1, rel2. */\n\t\t\tAssert(list_length(joinTree->fromlist) > 1);\n\t\t\treturn NULL;\n\t\t}\n\n\t\t/*\n\t\t * In the planning phase empty FROMs are represented with an RTE_RESULT.\n\t\t * When we arrive here, standard_planner has already been called which calls\n\t\t * replace_empty_jointree() which replaces empty fromlist with a list of\n\t\t * single RTE_RESULT RangleTableRef node.\n\t\t */\n\t\tRangeTblRef *reference = linitial(joinTree->fromlist);\n\n\t\tif (IsA(reference, RangeTblRef))\n\t\t{\n\t\t\tRangeTblEntry *rtentry = rt_fetch(reference->rtindex,\n\t\t\t\t\t\t\t\t\t\t\t  planContext->query->rtable);\n\n\t\t\tif (rtentry->rtekind == RTE_FUNCTION)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Look for a function in the FROM clause.\n\t\t\t\t */\n\t\t\t\tfromFuncExpr = FunctionInFromClause(joinTree->fromlist,\n\t\t\t\t\t\t\t\t\t\t\t\t\tplanContext->query);\n\t\t\t}\n\t\t\telse if (rtentry->rtekind != RTE_RESULT)\n\t\t\t{\n\t\t\t\t/* e.g. SELECT f() FROM rel */\n\t\t\t\tereport(DEBUG4, (errmsg(\"FromList item is not empty\")));\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * e.g. IsA(reference, JoinExpr). This is explicit join expressions\n\t\t\t * like INNER JOIN, NATURAL JOIN, ...\n\t\t\t */\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tFuncExpr *targetFuncExpr = NULL;\n\tList *targetList = planContext->query->targetList;\n\tint targetListLen = list_length(targetList);\n\n\tif (targetListLen == 1)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) linitial(targetList);\n\t\tif (IsA(targetEntry->expr, FuncExpr))\n\t\t{\n\t\t\t/* function from the SELECT clause e.g. SELECT fn() FROM  */\n\t\t\ttargetFuncExpr = (FuncExpr *) targetEntry->expr;\n\t\t}\n\t}\n\n\t/*\n\t * Look for one of:\n\t * SELECT fn(...);\n\t * SELECT ... FROM fn(...);\n\t */\n\tFuncExpr *funcExpr = NULL;\n\tif (targetFuncExpr != NULL)\n\t{\n\t\tif (fromFuncExpr != NULL)\n\t\t{\n\t\t\t/* query is of the form: SELECT fn() FROM fn() */\n\t\t\treturn NULL;\n\t\t}\n\n\t\t/* query is of the form: SELECT fn(); */\n\t\tfuncExpr = targetFuncExpr;\n\t}\n\telse if (fromFuncExpr != NULL)\n\t{\n\t\t/* query is of the form: SELECT ... FROM fn(); */\n\t\tfuncExpr = fromFuncExpr;\n\t}\n\telse\n\t{\n\t\t/* query does not have a function call in SELECT or FROM */\n\t\treturn NULL;\n\t}\n\n\tDistObjectCacheEntry *procedure = LookupDistObjectCacheEntry(ProcedureRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t funcExpr->funcid, 0);\n\tif (procedure == NULL || !procedure->isDistributed)\n\t{\n\t\t/* not a distributed function call */\n\t\treturn NULL;\n\t}\n\telse\n\t{\n\t\tereport(DEBUG4, (errmsg(\"function is distributed\")));\n\t}\n\n\tif (IsCitusInternalBackend())\n\t{\n\t\tbool isFunctionForceDelegated = procedure->forceDelegation;\n\n\t\t/*\n\t\t * We are planning a call to a distributed function within a Citus backend,\n\t\t * that means that this is the delegated call. If the function is forcefully\n\t\t * delegated, capture the distribution argument.\n\t\t */\n\t\tif (isFunctionForceDelegated)\n\t\t{\n\t\t\tCheckDelegatedFunctionExecution(procedure, funcExpr);\n\t\t}\n\n\t\t/* Are we planning the top function call? */\n\t\tif (ExecutorLevel == 0 && PlannerLevel == 1)\n\t\t{\n\t\t\t/*\n\t\t\t * InTopLevelDelegatedFunctionCall flag grants the levy\n\t\t\t * to do remote tasks from a delegated function.\n\t\t\t */\n\t\t\tif (!isFunctionForceDelegated)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * we are planning a regular delegated call, we\n\t\t\t\t * are allowed to do remote execution.\n\t\t\t\t */\n\t\t\t\tInTopLevelDelegatedFunctionCall = true;\n\t\t\t}\n\t\t\telse if (!IsMultiStatementTransaction())\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * we are planning a force-delegated call, we\n\t\t\t\t * are allowed to do remote execution if there\n\t\t\t\t * is no explicit BEGIN-END transaction.\n\t\t\t\t */\n\t\t\t\tInTopLevelDelegatedFunctionCall = true;\n\t\t\t}\n\t\t}\n\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * Cannot delegate functions for INSERT ... SELECT func(), since they require\n\t * coordinated transactions.\n\t */\n\tif (PlanningInsertSelect())\n\t{\n\t\tereport(DEBUG1, (errmsg(\"not pushing down function calls in INSERT ... SELECT\")));\n\t\treturn NULL;\n\t}\n\n\t/* dissuade the planner from trying a generic plan with parameters */\n\t(void) expression_tree_walker((Node *) funcExpr->args, contain_param_walker,\n\t\t\t\t\t\t\t\t  &walkerParamContext);\n\tif (walkerParamContext.hasParam)\n\t{\n\t\tif (walkerParamContext.paramKind == PARAM_EXTERN)\n\t\t{\n\t\t\t/* Don't log a message, we should end up here again without a parameter */\n\t\t\tDissuadePlannerFromUsingPlan(planContext->plan);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"arguments in a distributed function must \"\n\t\t\t\t\t\t\t\t\t\"not contain subqueries\")));\n\t\t}\n\t\treturn NULL;\n\t}\n\n\tif (IsMultiStatementTransaction())\n\t{\n\t\tif (!procedure->forceDelegation)\n\t\t{\n\t\t\t/* cannot delegate function calls in a multi-statement transaction */\n\t\t\tereport(DEBUG4, (errmsg(\"not pushing down function calls in \"\n\t\t\t\t\t\t\t\t\t\"a multi-statement transaction\")));\n\t\t\treturn NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tNode *partitionValueNode = (Node *) list_nth(funcExpr->args,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t procedure->distributionArgIndex);\n\n\t\t\tif (!IsA(partitionValueNode, Const))\n\t\t\t{\n\t\t\t\tereport(DEBUG1, (errmsg(\"distribution argument value must be a \"\n\t\t\t\t\t\t\t\t\t\t\"constant when using force_delegation flag\")));\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If the expression is simple, such as, SELECT function() or PEFORM function()\n\t\t\t * in PL/PgSQL code, PL engine does a simple expression evaluation which can't\n\t\t\t * interpret the Citus CustomScan Node.\n\t\t\t * Note: Function from FROM clause is not simple, so it's ok to pushdown.\n\t\t\t */\n\t\t\tif ((MaybeExecutingUDF() || DoBlockLevel > 0) &&\n\t\t\t\tIsQuerySimple(planContext->query) &&\n\t\t\t\t!fromFuncExpr)\n\t\t\t{\n\t\t\t\tereport(DEBUG1, (errmsg(\"Skipping pushdown of function \"\n\t\t\t\t\t\t\t\t\t\t\"from a PL/PgSQL simple expression\")));\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * When is flag is on, delegate the function call in a multi-statement\n\t\t\t * transaction but with restrictions.\n\t\t\t */\n\t\t\tereport(DEBUG1, (errmsg(\"pushing down function call in \"\n\t\t\t\t\t\t\t\t\t\"a multi-statement transaction\")));\n\t\t\tinTransactionBlock = true;\n\t\t}\n\t}\n\n\tif (contain_volatile_functions((Node *) funcExpr->args))\n\t{\n\t\tereport(DEBUG1, (errmsg(\"arguments in a distributed function must \"\n\t\t\t\t\t\t\t\t\"be constant expressions\")));\n\t\treturn NULL;\n\t}\n\n\tOid colocatedRelationId = ColocatedTableId(procedure->colocationId);\n\tif (colocatedRelationId == InvalidOid)\n\t{\n\t\tereport(DEBUG4, (errmsg(\"function does not have co-located tables\")));\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * This can be called in queries like SELECT ... WHERE EXISTS(SELECT func()), or other\n\t * forms of CTEs or subqueries. We don't push-down in those cases.\n\t */\n\tif (GeneratingSubplans())\n\t{\n\t\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t\t \"not pushing down function calls in CTEs or Subqueries\")));\n\t\treturn NULL;\n\t}\n\n\tCitusTableCacheEntry *distTable = GetCitusTableCacheEntry(colocatedRelationId);\n\tif (IsCitusTableType(colocatedRelationId, REFERENCE_TABLE))\n\t{\n\t\tplacement = ShardPlacementForFunctionColocatedWithReferenceTable(distTable);\n\t}\n\telse if (IsCitusTableType(colocatedRelationId, SINGLE_SHARD_DISTRIBUTED))\n\t{\n\t\tplacement = ShardPlacementForFunctionColocatedWithSingleShardTable(distTable);\n\t}\n\telse\n\t{\n\t\tplacement = ShardPlacementForFunctionColocatedWithDistTable(procedure,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfuncExpr->args,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdistTable->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdistTable,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplanContext->plan);\n\t}\n\n\t/* return if we could not find a placement */\n\tif (placement == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tWorkerNode *workerNode = FindWorkerNode(placement->nodeName, placement->nodePort);\n\n\tif (workerNode == NULL || !workerNode->hasMetadata || !workerNode->metadataSynced)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"the worker node does not have metadata\")));\n\t\treturn NULL;\n\t}\n\telse if (workerNode->groupId == GetLocalGroupId())\n\t{\n\t\t/* If the force_pushdown flag is set, capture the distribution argument */\n\t\tif (procedure->forceDelegation)\n\t\t{\n\t\t\tCheckDelegatedFunctionExecution(procedure, funcExpr);\n\t\t}\n\n\t\t/*\n\t\t * Two reasons for this:\n\t\t *  (a) It would lead to infinite recursion as the node would\n\t\t *      keep pushing down the procedure as it gets\n\t\t *  (b) It doesn't have any value to pushdown as we are already\n\t\t *      on the node itself\n\t\t */\n\t\tereport(DEBUG1, (errmsg(\"not pushing down function to the same node\")));\n\t\treturn NULL;\n\t}\n\n\tereport(DEBUG1, (errmsg(\"pushing down the function call\")));\n\n\tTask *task = CitusMakeNode(Task);\n\n\t/*\n\t * In a multi-statement block the function should be part of the sorrounding\n\t * transaction, at this time, not knowing the operations in the function, it\n\t * is safe to assume that it's a write task.\n\t *\n\t * TODO: We should compile the function to see the internals of the function\n\t * and find if this has read-only tasks, does it involve doing a remote task\n\t * or queries involving non-distribution column, etc.\n\t */\n\tif (inTransactionBlock)\n\t{\n\t\ttask->taskType = MODIFY_TASK;\n\t}\n\telse\n\t{\n\t\ttask->taskType = READ_TASK;\n\t}\n\n\ttask->taskPlacementList = list_make1(placement);\n\tSetTaskQueryIfShouldLazyDeparse(task, planContext->query);\n\ttask->anchorShardId = placement->shardId;\n\ttask->replicationModel = distTable->replicationModel;\n\n\tJob *job = CitusMakeNode(Job);\n\tjob->jobId = UniqueJobId();\n\tjob->jobQuery = planContext->query;\n\tjob->taskList = list_make1(task);\n\n\tDistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan);\n\tdistributedPlan->workerJob = job;\n\tdistributedPlan->combineQuery = NULL;\n\tdistributedPlan->expectResults = true;\n\n\t/* worker will take care of any necessary locking, treat query as read-only */\n\tdistributedPlan->modLevel = ROW_MODIFY_READONLY;\n\n\treturn FinalizePlan(planContext->plan, distributedPlan);\n}\n\n\n/*\n * ShardPlacementForFunctionColocatedWithDistTable decides on a placement\n * for delegating a procedure call that accesses a distributed table.\n */\nShardPlacement *\nShardPlacementForFunctionColocatedWithDistTable(DistObjectCacheEntry *procedure,\n\t\t\t\t\t\t\t\t\t\t\t\tList *argumentList,\n\t\t\t\t\t\t\t\t\t\t\t\tVar *partitionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\tCitusTableCacheEntry *cacheEntry,\n\t\t\t\t\t\t\t\t\t\t\t\tPlannedStmt *plan)\n{\n\tif (procedure->distributionArgIndex < 0 ||\n\t\tprocedure->distributionArgIndex >= list_length(argumentList))\n\t{\n\t\tereport(DEBUG1, (errmsg(\"cannot push down invalid distribution_argument_index\")));\n\t\treturn NULL;\n\t}\n\n\tNode *partitionValueNode = (Node *) list_nth(argumentList,\n\t\t\t\t\t\t\t\t\t\t\t\t procedure->distributionArgIndex);\n\tpartitionValueNode = strip_implicit_coercions(partitionValueNode);\n\n\tif (IsA(partitionValueNode, Param))\n\t{\n\t\tParam *partitionParam = (Param *) partitionValueNode;\n\n\t\tif (partitionParam->paramkind == PARAM_EXTERN)\n\t\t{\n\t\t\t/*\n\t\t\t * Don't log a message, we should end up here again without a\n\t\t\t * parameter.\n\t\t\t * Note that \"plan\" can be null, for example when a CALL statement\n\t\t\t * is prepared.\n\t\t\t */\n\t\t\tif (plan)\n\t\t\t{\n\t\t\t\tDissuadePlannerFromUsingPlan(plan);\n\t\t\t}\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tif (!IsA(partitionValueNode, Const))\n\t{\n\t\tereport(DEBUG1, (errmsg(\"distribution argument value must be a constant\")));\n\t\treturn NULL;\n\t}\n\n\tConst *partitionValue = (Const *) partitionValueNode;\n\n\tif (partitionValue->consttype != partitionColumn->vartype)\n\t{\n\t\tbool missingOk = false;\n\t\tpartitionValue =\n\t\t\tTransformPartitionRestrictionValue(partitionColumn, partitionValue,\n\t\t\t\t\t\t\t\t\t\t\t   missingOk);\n\t}\n\n\tDatum partitionValueDatum = partitionValue->constvalue;\n\tShardInterval *shardInterval = FindShardInterval(partitionValueDatum, cacheEntry);\n\tif (shardInterval == NULL)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"cannot push down call, failed to find shard interval\")));\n\t\treturn NULL;\n\t}\n\n\tList *placementList = ActiveShardPlacementList(shardInterval->shardId);\n\tif (list_length(placementList) != 1)\n\t{\n\t\t/* punt on this for now */\n\t\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t\t \"cannot push down function call for replicated distributed tables\")));\n\t\treturn NULL;\n\t}\n\n\treturn linitial(placementList);\n}\n\n\n/*\n * ShardPlacementForFunctionColocatedWithSingleShardTable decides on a placement\n * for delegating a function call that reads from a single shard table.\n */\nShardPlacement *\nShardPlacementForFunctionColocatedWithSingleShardTable(CitusTableCacheEntry *cacheEntry)\n{\n\tconst ShardInterval *shardInterval = cacheEntry->sortedShardIntervalArray[0];\n\n\tif (shardInterval == NULL)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"cannot push down call, failed to find shard interval\")));\n\t\treturn NULL;\n\t}\n\n\tList *placementList = ActiveShardPlacementList(shardInterval->shardId);\n\tif (list_length(placementList) != 1)\n\t{\n\t\t/* punt on this for now */\n\t\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t\t \"cannot push down function call for replicated distributed tables\")));\n\t\treturn NULL;\n\t}\n\n\treturn (ShardPlacement *) linitial(placementList);\n}\n\n\n/*\n * ShardPlacementForFunctionColocatedWithReferenceTable decides on a placement for delegating\n * a function call that reads from a reference table.\n *\n * If citus.task_assignment_policy is set to round-robin, we assign a different placement\n * on consecutive runs. Otherwise the function returns the first placement available.\n */\nShardPlacement *\nShardPlacementForFunctionColocatedWithReferenceTable(CitusTableCacheEntry *cacheEntry)\n{\n\tconst ShardInterval *shardInterval = cacheEntry->sortedShardIntervalArray[0];\n\tconst uint64 referenceTableShardId = shardInterval->shardId;\n\n\t/* Get the list of active shard placements ordered by the groupid */\n\tList *placementList = ActiveShardPlacementList(referenceTableShardId);\n\tplacementList = SortList(placementList, CompareShardPlacementsByGroupId);\n\n\t/* do not try to delegate to coordinator even if it is in metadata */\n\tplacementList = RemoveCoordinatorPlacementIfNotSingleNode(placementList);\n\n\tif (TaskAssignmentPolicy == TASK_ASSIGNMENT_ROUND_ROBIN)\n\t{\n\t\t/* reorder the placement list */\n\t\tplacementList = RoundRobinReorder(placementList);\n\t}\n\n\treturn (ShardPlacement *) linitial(placementList);\n}\n\n\n/*\n * Checks to see if the procedure is being executed on a worker after delegated\n * by the coordinator. If the flag forceDelegation is set, capture the distribution\n * argument value, to be used by the planner to make sure that function uses only\n * the colocated shards of the distribution argument.\n */\nvoid\nCheckDelegatedFunctionExecution(DistObjectCacheEntry *procedure, FuncExpr *funcExpr)\n{\n\tAssert(procedure->forceDelegation);\n\n\t/*\n\t * On the coordinator PartiallyEvaluateExpression() descends into an\n\t * expression tree to evaluate expressions that can be resolved to a\n\t * constant. Expressions containing a Var are skipped, since the value\n\t * of the Var is not known on the coordinator.\n\t */\n\tNode *partitionValueNode = (Node *) list_nth(funcExpr->args,\n\t\t\t\t\t\t\t\t\t\t\t\t procedure->distributionArgIndex);\n\tAssert(partitionValueNode);\n\tpartitionValueNode = strip_implicit_coercions(partitionValueNode);\n\n\tif (IsA(partitionValueNode, Param))\n\t{\n\t\tParam *partitionParam = (Param *) partitionValueNode;\n\n\t\tif (partitionParam->paramkind == PARAM_EXTERN)\n\t\t{\n\t\t\t/* we should end up here again without a parameter */\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (IsA(partitionValueNode, Const))\n\t{\n\t\tConst *partitionValueConst = (Const *) partitionValueNode;\n\n\t\tereport(DEBUG1, (errmsg(\"Pushdown argument: %s\", pretty_format_node_dump(\n\t\t\t\t\t\t\t\t\tnodeToString(partitionValueNode)))));\n\t\tEnableInForceDelegatedFuncExecution(partitionValueConst, procedure->colocationId);\n\t}\n}\n\n\n/*\n * Function returns true if the query is simple enough to skip the full executor\n * It checks only for expressions in the query clauses, and not WHERE and FROM\n * lists.\n */\nstatic bool\nIsQuerySimple(Query *query)\n{\n\tif (query->hasAggs ||\n\t\tquery->hasWindowFuncs ||\n\t\tquery->hasTargetSRFs ||\n\t\tquery->hasSubLinks ||\n\t\tquery->cteList ||\n\t\tquery->groupClause ||\n\t\tquery->groupingSets ||\n\t\tquery->havingQual ||\n\t\tquery->windowClause ||\n\t\tquery->distinctClause ||\n\t\tquery->sortClause ||\n\t\tquery->limitOffset ||\n\t\tquery->limitCount ||\n\t\tquery->setOperations)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * Look for a function in the FROM clause.\n */\nstatic FuncExpr *\nFunctionInFromClause(List *fromlist, Query *query)\n{\n\tif (list_length(fromlist) != 1)\n\t{\n\t\t/* We are looking for a single function */\n\t\treturn NULL;\n\t}\n\n\tRangeTblRef *reference = linitial(fromlist);\n\tif (!IsA(reference, RangeTblRef))\n\t{\n\t\t/* Skip if there is no RTE */\n\t\treturn NULL;\n\t}\n\n\tRangeTblEntry *rtentry = rt_fetch(reference->rtindex, query->rtable);\n\tif (rtentry->rtekind != RTE_FUNCTION)\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (list_length(rtentry->functions) != 1)\n\t{\n\t\t/* Skip if RTE isn't a single FuncExpr */\n\t\treturn NULL;\n\t}\n\n\tRangeTblFunction *rtfunc = (RangeTblFunction *) linitial(rtentry->functions);\n\tif (!IsA(rtfunc->funcexpr, FuncExpr))\n\t{\n\t\t/* Skip if RTE isn't a simple FuncExpr */\n\t\treturn NULL;\n\t}\n\n\treturn (FuncExpr *) rtfunc->funcexpr;\n}\n\n\n/*\n * Sets a flag to true indicating that the current node is executing a delegated\n * function call, using forceDelegation, within a distributed transaction issued\n * by the coordinator. Also, saves the distribution argument.\n */\nstatic void\nEnableInForceDelegatedFuncExecution(Const *distArgument, uint32 colocationId)\n{\n\t/*\n\t * If the distribution key is already set, the key is fixed until\n\t * the force-delegation function returns. All nested force-delegation\n\t * functions must use the same key.\n\t */\n\tif (AllowedDistributionColumnValue.isActive)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * The saved distribution argument need to persist through the life\n\t * of the query, both during the planning (where we save) and execution\n\t * (where we compare)\n\t */\n\tMemoryContext oldcontext = MemoryContextSwitchTo(TopTransactionContext);\n\tereport(DEBUG1, errmsg(\"Saving Distribution Argument: %s:%d\",\n\t\t\t\t\t\t   pretty_format_node_dump(nodeToString(distArgument)),\n\t\t\t\t\t\t   colocationId));\n\tAllowedDistributionColumnValue.distributionColumnValue = copyObject(distArgument);\n\tAllowedDistributionColumnValue.colocationId = colocationId;\n\tAllowedDistributionColumnValue.executorLevel = ExecutorLevel;\n\tAllowedDistributionColumnValue.isActive = true;\n\tMemoryContextSwitchTo(oldcontext);\n}\n\n\n/*\n * Within a 2PC, when a function is delegated to a remote node, we pin\n * the distribution argument as the shard key for all the SQL in the\n * function's block. The restriction is imposed to not to access other\n * nodes from the current node and violate the transactional integrity of\n * the 2PC. Reset the distribution argument value once the function ends.\n */\nvoid\nCheckAndResetAllowedShardKeyValueIfNeeded(void)\n{\n\t/*\n\t * If no distribution argument is pinned or the pinned argument was\n\t * set by a nested-executor from upper level, nothing to reset.\n\t */\n\tif (!AllowedDistributionColumnValue.isActive ||\n\t\tExecutorLevel > AllowedDistributionColumnValue.executorLevel)\n\t{\n\t\treturn;\n\t}\n\n\tAssert(ExecutorLevel == AllowedDistributionColumnValue.executorLevel);\n\tpfree(AllowedDistributionColumnValue.distributionColumnValue);\n\tAllowedDistributionColumnValue.isActive = false;\n\tAllowedDistributionColumnValue.executorLevel = 0;\n}\n\n\n/*\n * Function returns true if the current shard key in the adaptive executor\n * matches the saved distribution argument of a force_delegation function.\n */\nbool\nIsShardKeyValueAllowed(Const *shardKey, uint32 colocationId)\n{\n\tAssert(AllowedDistributionColumnValue.isActive);\n\tAssert(ExecutorLevel > AllowedDistributionColumnValue.executorLevel);\n\n\tereport(DEBUG4, errmsg(\n\t\t\t\t\"Comparing saved:%s with Shard key: %s colocationid:%d:%d\",\n\t\t\t\tpretty_format_node_dump(\n\t\t\t\t\tnodeToString(AllowedDistributionColumnValue.\n\t\t\t\t\t\t\t\t distributionColumnValue)),\n\t\t\t\tpretty_format_node_dump(nodeToString(shardKey)),\n\t\t\t\tAllowedDistributionColumnValue.colocationId, colocationId));\n\n\treturn (equal(AllowedDistributionColumnValue.distributionColumnValue, shardKey) &&\n\t\t\t(AllowedDistributionColumnValue.colocationId == colocationId));\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/insert_select_planner.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * insert_select_planner.c\n *\n * Planning logic for INSERT..SELECT.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/print.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/planner.h\"\n#include \"optimizer/restrictinfo.h\"\n#include \"optimizer/tlist.h\"\n#include \"parser/parse_coerce.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parsetree.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_clauses.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/insert_select_executor.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/query_pushdown_planning.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/repartition_executor.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/version_compat.h\"\n\n\nstatic void PrepareInsertSelectForCitusPlanner(Query *insertSelectQuery);\nstatic DistributedPlan * CreateInsertSelectPlanInternal(uint64 planId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tQuery *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tPlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tParamListInfo boundParams);\nstatic DistributedPlan * CreateDistributedInsertSelectPlan(Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nstatic bool InsertSelectHasRouterSelect(Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\tPlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext);\nstatic Task * RouterModifyTaskForShardInterval(Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t   CitusTableCacheEntry *\n\t\t\t\t\t\t\t\t\t\t\t   targetTableCacheEntry,\n\t\t\t\t\t\t\t\t\t\t\t   ShardInterval *shardInterval,\n\t\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t   uint32 taskIdIndex,\n\t\t\t\t\t\t\t\t\t\t\t   bool allRelationsJoinedOnPartitionKey,\n\t\t\t\t\t\t\t\t\t\t\t   DeferredErrorMessage **routerPlannerError);\nstatic Query * CreateCombineQueryForRouterPlan(DistributedPlan *distPlan);\nstatic List * CreateTargetListForCombineQuery(List *targetList);\nstatic DeferredErrorMessage * DistributedInsertSelectSupported(Query *queryTree,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   RangeTblEntry *insertRte,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   RangeTblEntry *subqueryRte,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   bool allReferenceTables,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   bool routerSelect,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nstatic DeferredErrorMessage * InsertPartitionColumnMatchesSelect(Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t RangeTblEntry *insertRte,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t RangeTblEntry *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t subqueryRte,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Oid *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t selectPartitionColumnTableId);\nstatic DistributedPlan * CreateNonPushableInsertSelectPlan(uint64 planId, Query *parse,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ParamListInfo boundParams);\nstatic DeferredErrorMessage * NonPushableInsertSelectSupported(Query *insertSelectQuery);\nstatic void RelabelTargetEntryList(List *selectTargetList, List *insertTargetList);\nstatic List * AddInsertSelectCasts(List *insertTargetList, List *selectTargetList,\n\t\t\t\t\t\t\t\t   Oid targetRelationId);\nstatic Expr * CastExpr(Expr *expr, Oid sourceType, Oid targetType, Oid targetCollation,\n\t\t\t\t\t   int targetTypeMod);\nstatic Oid GetNextvalReturnTypeCatalog(void);\nstatic void AppendCastedEntry(TargetEntry *insertEntry, TargetEntry *selectEntry,\n\t\t\t\t\t\t\t  Oid castFromType, Oid targetType, Oid collation, int32\n\t\t\t\t\t\t\t  typmod,\n\t\t\t\t\t\t\t  int targetEntryIndex,\n\t\t\t\t\t\t\t  List **projectedEntries, List **nonProjectedEntries);\nstatic void SetTargetEntryName(TargetEntry *tle, const char *format, int index);\nstatic void ResetTargetEntryResno(List *targetList);\nstatic void ProcessEntryPair(TargetEntry *insertEntry, TargetEntry *selectEntry,\n\t\t\t\t\t\t\t Form_pg_attribute attr, int targetEntryIndex,\n\t\t\t\t\t\t\t List **projectedEntries, List **nonProjectedEntries);\n\n\n/* depth of current insert/select planner. */\nstatic int insertSelectPlannerLevel = 0;\n\n\n/*\n * InsertSelectIntoCitusTable returns true when the input query is an\n * INSERT INTO ... SELECT kind of query and the target is a citus\n * table.\n *\n * Note that the input query should be the original parsetree of\n * the query (i.e., not passed trough the standard planner).\n */\nbool\nInsertSelectIntoCitusTable(Query *query)\n{\n\tbool insertSelectQuery = CheckInsertSelectQuery(query);\n\n\tif (insertSelectQuery)\n\t{\n\t\tRangeTblEntry *insertRte = ExtractResultRelationRTE(query);\n\t\tif (IsCitusTable(insertRte->relid))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * InsertSelectIntoLocalTable checks whether INSERT INTO ... SELECT inserts\n * into local table. Note that query must be a sample of INSERT INTO ... SELECT\n * type of query.\n */\nbool\nInsertSelectIntoLocalTable(Query *query)\n{\n\tbool insertSelectQuery = CheckInsertSelectQuery(query);\n\n\tif (insertSelectQuery)\n\t{\n\t\tRangeTblEntry *insertRte = ExtractResultRelationRTE(query);\n\t\tif (!IsCitusTable(insertRte->relid))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CheckInsertSelectQuery returns true when the input query is an INSERT INTO\n * ... SELECT kind of query.\n *\n * This function is inspired from getInsertSelectQuery() on\n * rewrite/rewriteManip.c.\n */\nbool\nCheckInsertSelectQuery(Query *query)\n{\n\tCmdType commandType = query->commandType;\n\n\tif (commandType != CMD_INSERT)\n\t{\n\t\treturn false;\n\t}\n\n\tif (query->jointree == NULL || !IsA(query->jointree, FromExpr))\n\t{\n\t\treturn false;\n\t}\n\n\tList *fromList = query->jointree->fromlist;\n\tif (list_length(fromList) != 1)\n\t{\n\t\treturn false;\n\t}\n\n\tRangeTblRef *rangeTableReference = linitial(fromList);\n\tif (!IsA(rangeTableReference, RangeTblRef))\n\t{\n\t\treturn false;\n\t}\n\n\tRangeTblEntry *subqueryRte = rt_fetch(rangeTableReference->rtindex, query->rtable);\n\tif (subqueryRte->rtekind != RTE_SUBQUERY)\n\t{\n\t\treturn false;\n\t}\n\n\t/* ensure that there is a query */\n\tAssert(IsA(subqueryRte->subquery, Query));\n\n\treturn true;\n}\n\n\n/*\n * CoordinatorInsertSelectExecScan is a wrapper around\n * CoordinatorInsertSelectExecScanInternal which also properly increments\n * or decrements insertSelectExecutorLevel.\n */\nDistributedPlan *\nCreateInsertSelectPlan(uint64 planId, Query *originalQuery,\n\t\t\t\t\t   PlannerRestrictionContext *plannerRestrictionContext,\n\t\t\t\t\t   ParamListInfo boundParams)\n{\n\tDistributedPlan *result = NULL;\n\tinsertSelectPlannerLevel++;\n\n\tPG_TRY();\n\t{\n\t\tresult = CreateInsertSelectPlanInternal(planId, originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext, boundParams);\n\t}\n\tPG_CATCH();\n\t{\n\t\tinsertSelectPlannerLevel--;\n\t\tPG_RE_THROW();\n\t}\n\tPG_END_TRY();\n\n\tinsertSelectPlannerLevel--;\n\treturn result;\n}\n\n\n/*\n * CreateInsertSelectPlan tries to create a distributed plan for an\n * INSERT INTO distributed_table SELECT ... query by push down the\n * command to the workers and if that is not possible it creates a\n * plan for evaluating the SELECT on the coordinator.\n */\nstatic DistributedPlan *\nCreateInsertSelectPlanInternal(uint64 planId, Query *originalQuery,\n\t\t\t\t\t\t\t   PlannerRestrictionContext *plannerRestrictionContext,\n\t\t\t\t\t\t\t   ParamListInfo boundParams)\n{\n\tDeferredErrorMessage *deferredError = ErrorIfOnConflictNotSupported(originalQuery);\n\tif (deferredError != NULL)\n\t{\n\t\t/* raising the error as there is no possible solution for the unsupported on conflict statements */\n\t\tRaiseDeferredError(deferredError, ERROR);\n\t}\n\n\tDistributedPlan *distributedPlan = CreateDistributedInsertSelectPlan(originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\n\n\tif (distributedPlan->planningError != NULL)\n\t{\n\t\tRaiseDeferredError(distributedPlan->planningError, DEBUG1);\n\n\t\t/*\n\t\t * If INSERT..SELECT cannot be distributed, pull to coordinator or use\n\t\t * repartitioning.\n\t\t */\n\t\tdistributedPlan = CreateNonPushableInsertSelectPlan(planId, originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tboundParams);\n\t}\n\n\treturn distributedPlan;\n}\n\n\n/*\n * CreateDistributedInsertSelectPlan creates a DistributedPlan for distributed\n * INSERT ... SELECT queries which could consist of multiple tasks.\n *\n * The function never returns NULL, it errors out if cannot create the DistributedPlan.\n */\nstatic DistributedPlan *\nCreateDistributedInsertSelectPlan(Query *originalQuery,\n\t\t\t\t\t\t\t\t  PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tList *sqlTaskList = NIL;\n\tuint32 taskIdIndex = 1;     /* 0 is reserved for invalid taskId */\n\tuint64 jobId = INVALID_JOB_ID;\n\tDistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan);\n\tRangeTblEntry *insertRte = ExtractResultRelationRTEOrError(originalQuery);\n\tRangeTblEntry *subqueryRte = ExtractSelectRangeTableEntry(originalQuery);\n\tOid targetRelationId = insertRte->relid;\n\tCitusTableCacheEntry *targetCacheEntry = GetCitusTableCacheEntry(targetRelationId);\n\tint shardCount = targetCacheEntry->shardIntervalArrayLength;\n\tRelationRestrictionContext *relationRestrictionContext =\n\t\tplannerRestrictionContext->relationRestrictionContext;\n\tbool allReferenceTables = relationRestrictionContext->allReferenceTables;\n\tbool routerSelect =\n\t\tInsertSelectHasRouterSelect(copyObject(originalQuery),\n\t\t\t\t\t\t\t\t\tplannerRestrictionContext);\n\n\tdistributedPlan->modLevel = RowModifyLevelForQuery(originalQuery);\n\n\t/*\n\t * Error semantics for INSERT ... SELECT queries are different than regular\n\t * modify queries. Thus, handle separately.\n\t */\n\tdistributedPlan->planningError = DistributedInsertSelectSupported(originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  insertRte,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  subqueryRte,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  allReferenceTables,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  routerSelect,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext);\n\tif (distributedPlan->planningError)\n\t{\n\t\treturn distributedPlan;\n\t}\n\n\n\t/*\n\t * if the query goes to a single node (\"router\" in Citus' parlance),\n\t * we don't need to go through AllDistributionKeysInQueryAreEqual checks.\n\t *\n\t * For PG16+, this is required as some of the outer JOINs are converted to\n\t * \"ON(true)\" and filters are pushed down to the table scans. As\n\t * AllDistributionKeysInQueryAreEqual rely on JOIN filters, it will fail to\n\t * detect the router case. However, we can still detect it by checking if\n\t * the query is a router query as the router query checks the filters on\n\t * the tables.\n\t */\n\tbool allDistributionKeysInQueryAreEqual =\n\t\trouterSelect ||\n\t\tAllDistributionKeysInQueryAreEqual(originalQuery, plannerRestrictionContext);\n\n\t/*\n\t * Plan select query for each shard in the target table. Do so by replacing the\n\t * partitioning qual parameter added in distributed_planner() using the current shard's\n\t * actual boundary values. Also, add the current shard's boundary values to the\n\t * top level subquery to ensure that even if the partitioning qual is not distributed\n\t * to all the tables, we never run the queries on the shards that don't match with\n\t * the current shard boundaries. Finally, perform the normal shard pruning to\n\t * decide on whether to push the query to the current shard or not.\n\t */\n\tfor (int shardOffset = 0; shardOffset < shardCount; shardOffset++)\n\t{\n\t\tShardInterval *targetShardInterval =\n\t\t\ttargetCacheEntry->sortedShardIntervalArray[shardOffset];\n\n\t\tTask *modifyTask = RouterModifyTaskForShardInterval(originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetCacheEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetShardInterval,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttaskIdIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tallDistributionKeysInQueryAreEqual,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t&distributedPlan->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplanningError);\n\n\t\tif (distributedPlan->planningError != NULL)\n\t\t{\n\t\t\treturn distributedPlan;\n\t\t}\n\n\t\t/* add the task if it could be created */\n\t\tif (modifyTask != NULL)\n\t\t{\n\t\t\tmodifyTask->modifyWithSubquery = true;\n\n\t\t\tsqlTaskList = lappend(sqlTaskList, modifyTask);\n\t\t}\n\n\t\ttaskIdIndex++;\n\t}\n\n\t/* Create the worker job */\n\tJob *workerJob = CitusMakeNode(Job);\n\tworkerJob->taskList = sqlTaskList;\n\tworkerJob->subqueryPushdown = false;\n\tworkerJob->dependentJobList = NIL;\n\tworkerJob->jobId = jobId;\n\tworkerJob->jobQuery = originalQuery;\n\tworkerJob->requiresCoordinatorEvaluation =\n\t\tRequiresCoordinatorEvaluation(originalQuery);\n\n\t/* and finally the multi plan */\n\tdistributedPlan->workerJob = workerJob;\n\tdistributedPlan->combineQuery = NULL;\n\tdistributedPlan->expectResults = originalQuery->returningList != NIL;\n\tdistributedPlan->targetRelationId = targetRelationId;\n\n\treturn distributedPlan;\n}\n\n\n/*\n * InsertSelectHasRouterSelect is a helper function that returns true of the SELECT\n * part of the INSERT .. SELECT query is a router query.\n */\nstatic bool\nInsertSelectHasRouterSelect(Query *originalQuery,\n\t\t\t\t\t\t\tPlannerRestrictionContext *plannerRestrictionContext)\n{\n\tRangeTblEntry *subqueryRte = ExtractSelectRangeTableEntry(originalQuery);\n\tDistributedPlan *distributedPlan = CreateRouterPlan(subqueryRte->subquery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsubqueryRte->subquery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext);\n\n\treturn distributedPlan->planningError == NULL;\n}\n\n\n/*\n * CreateInsertSelectIntoLocalTablePlan creates the plan for INSERT .. SELECT queries\n * where the selected table is distributed and the inserted table is not.\n *\n * To create the plan, this function first creates a distributed plan for the SELECT\n * part. Then puts it as a subquery to the original (non-distributed) INSERT query as\n * a subquery. Finally, it puts this INSERT query, which now has a distributed SELECT\n * subquery, in the combineQuery.\n *\n * If the SELECT query is a router query, whose distributed plan does not have a\n * combineQuery, this function also creates a dummy combineQuery for that.\n */\nDistributedPlan *\nCreateInsertSelectIntoLocalTablePlan(uint64 planId, Query *insertSelectQuery,\n\t\t\t\t\t\t\t\t\t ParamListInfo boundParams, bool hasUnresolvedParams,\n\t\t\t\t\t\t\t\t\t PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tPrepareInsertSelectForCitusPlanner(insertSelectQuery);\n\n\t/* get the SELECT query (may have changed after PrepareInsertSelectForCitusPlanner) */\n\tRangeTblEntry *selectRte = ExtractSelectRangeTableEntry(insertSelectQuery);\n\tQuery *selectQuery = selectRte->subquery;\n\n\tbool allowRecursivePlanning = true;\n\tDistributedPlan *distPlan = CreateDistributedPlan(planId, allowRecursivePlanning,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  selectQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  copyObject(selectQuery),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  boundParams, hasUnresolvedParams,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext);\n\n\t/*\n\t * We don't expect distPlan to be NULL here because hasUnresolvedParams is\n\t * already checked before this function and CreateDistributedPlan only returns\n\t * NULL when there are unresolved parameters.\n\t */\n\tAssert(distPlan != NULL);\n\n\tif (distPlan->planningError)\n\t{\n\t\treturn distPlan;\n\t}\n\n\tif (distPlan->combineQuery == NULL)\n\t{\n\t\t/*\n\t\t * For router queries, we construct a synthetic master query that simply passes\n\t\t * on the results of the remote tasks, which we can then use as the select in\n\t\t * the INSERT .. SELECT.\n\t\t */\n\t\tdistPlan->combineQuery = CreateCombineQueryForRouterPlan(\n\t\t\tdistPlan);\n\t}\n\n\t/*\n\t * combineQuery of a distributed select is for combining the results from\n\t * worker nodes on the coordinator node. Putting it as a subquery to the\n\t * INSERT query, causes the INSERT query to insert the combined select value\n\t * from the workers. And making the resulting insert query the combineQuery\n\t * let's us execute this insert command.\n\t *\n\t * So this operation makes the master query insert the result of the\n\t * distributed select instead of returning it.\n\t */\n\tselectRte->subquery = distPlan->combineQuery;\n\tdistPlan->combineQuery = insertSelectQuery;\n\n\treturn distPlan;\n}\n\n\n/*\n * PrepareInsertSelectForCitusPlanner prepares an INSERT..SELECT query tree\n * that was passed to the planner for use by Citus.\n *\n * First, it rebuilds the target lists of the INSERT and the SELECT\n * to be in the same order, which is not guaranteed in the parse tree.\n *\n * Second, some of the constants in the target list will have type\n * \"unknown\", which would confuse the Citus planner. To address that,\n * we add casts to SELECT target list entries whose type does not correspond\n * to the destination. This also helps us feed the output directly into\n * a COPY stream for INSERT..SELECT via coordinator.\n *\n * In case of UNION or other set operations, the SELECT does not have a\n * clearly defined target list, so we first wrap the UNION in a subquery.\n * UNION queries do not have the \"unknown\" type problem.\n *\n * Finally, if the INSERT has CTEs, we move those CTEs into the SELECT,\n * such that we can plan the SELECT as an independent query. To ensure\n * the ctelevelsup for CTE RTE's remain the same, we wrap the SELECT into\n * a subquery, unless we already did so in case of a UNION.\n */\nstatic void\nPrepareInsertSelectForCitusPlanner(Query *insertSelectQuery)\n{\n\tRangeTblEntry *insertRte = ExtractResultRelationRTEOrError(insertSelectQuery);\n\tRangeTblEntry *selectRte = ExtractSelectRangeTableEntry(insertSelectQuery);\n\tOid targetRelationId = insertRte->relid;\n\n\tbool isWrapped = false;\n\n\t/*\n\t * PG18 is stricter about GroupRTE/GroupVar. For INSERT … SELECT with a GROUP BY,\n\t * flatten the SELECT’s targetList and havingQual so Vars point to base RTEs and\n\t * avoid Unrecognized range table id.\n\t */\n\tFlattenGroupExprs(selectRte->subquery);\n\n\tif (selectRte->subquery->setOperations != NULL)\n\t{\n\t\t/*\n\t\t * Prepare UNION query for reordering and adding casts by\n\t\t * wrapping it in a subquery to have a single target list.\n\t\t */\n\t\tselectRte->subquery = WrapSubquery(selectRte->subquery);\n\t\tisWrapped = true;\n\t}\n\n\t/* this is required for correct deparsing of the query */\n\tReorderInsertSelectTargetLists(insertSelectQuery, insertRte, selectRte);\n\n\t/*\n\t * Cast types of insert target list and select projection list to\n\t * match the column types of the target relation.\n\t */\n\tselectRte->subquery->targetList =\n\t\tAddInsertSelectCasts(insertSelectQuery->targetList,\n\t\t\t\t\t\t\t copyObject(selectRte->subquery->targetList),\n\t\t\t\t\t\t\t targetRelationId);\n\n\tif (list_length(insertSelectQuery->cteList) > 0)\n\t{\n\t\tif (!isWrapped)\n\t\t{\n\t\t\t/*\n\t\t\t * By wrapping the SELECT in a subquery, we can avoid adjusting\n\t\t\t * ctelevelsup in RTE's that point to the CTEs.\n\t\t\t */\n\t\t\tselectRte->subquery = WrapSubquery(selectRte->subquery);\n\t\t}\n\n\t\t/* copy CTEs from the INSERT ... SELECT statement into outer SELECT */\n\t\tselectRte->subquery->cteList = copyObject(insertSelectQuery->cteList);\n\t\tselectRte->subquery->hasModifyingCTE = insertSelectQuery->hasModifyingCTE;\n\t\tinsertSelectQuery->cteList = NIL;\n\t}\n}\n\n\n/*\n * CreateCombineQueryForRouterPlan is used for creating a dummy combineQuery\n * for a router plan, since router plans normally don't have one.\n */\nstatic Query *\nCreateCombineQueryForRouterPlan(DistributedPlan *distPlan)\n{\n\tconst Index insertTableId = 1;\n\tList *tableIdList = list_make1(makeInteger(insertTableId));\n\tJob *dependentJob = distPlan->workerJob;\n\tList *dependentTargetList = dependentJob->jobQuery->targetList;\n\n\t/* compute column names for the derived table */\n\tuint32 columnCount = (uint32) list_length(dependentTargetList);\n\tList *columnNameList = DerivedColumnNameList(columnCount,\n\t\t\t\t\t\t\t\t\t\t\t\t dependentJob->jobId);\n\n\tList *funcColumnNames = NIL;\n\tList *funcColumnTypes = NIL;\n\tList *funcColumnTypeMods = NIL;\n\tList *funcCollations = NIL;\n\n\tTargetEntry *targetEntry = NULL;\n\tforeach_declared_ptr(targetEntry, dependentTargetList)\n\t{\n\t\tNode *expr = (Node *) targetEntry->expr;\n\n\t\tchar *name = targetEntry->resname;\n\t\tif (name == NULL)\n\t\t{\n\t\t\tname = pstrdup(\"unnamed\");\n\t\t}\n\n\t\tfuncColumnNames = lappend(funcColumnNames, makeString(name));\n\n\t\tfuncColumnTypes = lappend_oid(funcColumnTypes, exprType(expr));\n\t\tfuncColumnTypeMods = lappend_int(funcColumnTypeMods, exprTypmod(expr));\n\t\tfuncCollations = lappend_oid(funcCollations, exprCollation(expr));\n\t}\n\n\tRangeTblEntry *rangeTableEntry = DerivedRangeTableEntry(NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolumnNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttableIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfuncColumnNames,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfuncColumnTypes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfuncColumnTypeMods,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfuncCollations);\n\n\tList *targetList = CreateTargetListForCombineQuery(dependentTargetList);\n\n\tRangeTblRef *rangeTableRef = makeNode(RangeTblRef);\n\trangeTableRef->rtindex = 1;\n\n\tFromExpr *joinTree = makeNode(FromExpr);\n\tjoinTree->quals = NULL;\n\tjoinTree->fromlist = list_make1(rangeTableRef);\n\n\tQuery *combineQuery = makeNode(Query);\n\tcombineQuery->commandType = CMD_SELECT;\n\tcombineQuery->querySource = QSRC_ORIGINAL;\n\tcombineQuery->canSetTag = true;\n\tcombineQuery->rtable = list_make1(rangeTableEntry);\n\n\t/*\n\t * This part of the code is more of a sanity check for readability,\n\t * it doesn't really do anything.\n\t * We know that Only relation RTEs and subquery RTEs that were once relation\n\t * RTEs (views) have their perminfoindex set. (see ExecCheckPermissions function)\n\t * DerivedRangeTableEntry sets the rtekind to RTE_FUNCTION\n\t * Hence we should have no perminfos here.\n\t */\n\tAssert(rangeTableEntry->rtekind == RTE_FUNCTION &&\n\t\t   rangeTableEntry->perminfoindex == 0);\n\tcombineQuery->rteperminfos = NIL;\n\n\tcombineQuery->targetList = targetList;\n\tcombineQuery->jointree = joinTree;\n\treturn combineQuery;\n}\n\n\n/*\n * CreateTargetListForCombineQuery is used for creating a target list for\n * master query.\n */\nstatic List *\nCreateTargetListForCombineQuery(List *targetList)\n{\n\tList *newTargetEntryList = NIL;\n\tconst uint32 masterTableId = 1;\n\tint columnId = 1;\n\n\t/* iterate over original target entries */\n\tTargetEntry *originalTargetEntry = NULL;\n\tforeach_declared_ptr(originalTargetEntry, targetList)\n\t{\n\t\tTargetEntry *newTargetEntry = flatCopyTargetEntry(originalTargetEntry);\n\n\t\tVar *column = makeVarFromTargetEntry(masterTableId, originalTargetEntry);\n\t\tcolumn->varattno = columnId;\n\t\tcolumn->varattnosyn = columnId;\n\t\tcolumnId++;\n\n\t\tif (column->vartype == RECORDOID || column->vartype == RECORDARRAYOID)\n\t\t{\n\t\t\tcolumn->vartypmod = BlessRecordExpression(originalTargetEntry->expr);\n\t\t}\n\n\t\tExpr *newExpression = (Expr *) column;\n\n\t\tnewTargetEntry->expr = newExpression;\n\t\tnewTargetEntryList = lappend(newTargetEntryList, newTargetEntry);\n\t}\n\treturn newTargetEntryList;\n}\n\n\n/*\n * DistributedInsertSelectSupported returns NULL if the INSERT ... SELECT query\n * is supported, or a description why not.\n */\nstatic DeferredErrorMessage *\nDistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte,\n\t\t\t\t\t\t\t\t RangeTblEntry *subqueryRte, bool allReferenceTables,\n\t\t\t\t\t\t\t\t bool routerSelect,\n\t\t\t\t\t\t\t\t PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tOid selectPartitionColumnTableId = InvalidOid;\n\tOid targetRelationId = insertRte->relid;\n\tListCell *rangeTableCell = NULL;\n\n\t/* we only do this check for INSERT ... SELECT queries */\n\tAssert(InsertSelectIntoCitusTable(queryTree));\n\n\tQuery *subquery = subqueryRte->subquery;\n\n\tif (!NeedsDistributedPlanning(subquery))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"distributed INSERT ... SELECT can only select from \"\n\t\t\t\t\t\t\t \"distributed tables\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tRTEListProperties *subqueryRteListProperties = GetRTEListPropertiesForQuery(subquery);\n\tif (subqueryRteListProperties->hasDistributedTable &&\n\t\t(subqueryRteListProperties->hasCitusLocalTable ||\n\t\t subqueryRteListProperties->hasPostgresLocalTable))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"distributed INSERT ... SELECT cannot select from \"\n\t\t\t\t\t\t\t \"distributed tables and local tables at the same time\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tif (subqueryRteListProperties->hasDistributedTable &&\n\t\tIsCitusTableType(targetRelationId, CITUS_LOCAL_TABLE))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"distributed INSERT ... SELECT cannot insert into a \"\n\t\t\t\t\t\t\t \"local table that is added to metadata\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\t/*\n\t * In some cases, it might be possible to allow postgres local tables\n\t * in distributed insert select. However, we want to behave consistent\n\t * on all cases including Citus MX, and let insert select via coordinator\n\t * to kick-in.\n\t */\n\tif (subqueryRteListProperties->hasPostgresLocalTable)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"distributed INSERT ... SELECT cannot select from \"\n\t\t\t\t\t\t\t \"a local table\", NULL, NULL);\n\t\treturn NULL;\n\t}\n\n\t/* we do not expect to see a view in modify target */\n\tforeach(rangeTableCell, queryTree->rtable)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\t\tif (rangeTableEntry->rtekind == RTE_RELATION &&\n\t\t\trangeTableEntry->relkind == RELKIND_VIEW)\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"cannot insert into view over distributed table\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\t}\n\n\tif (FindNodeMatchingCheckFunction((Node *) queryTree, CitusIsVolatileFunction))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"volatile functions are not allowed in distributed \"\n\t\t\t\t\t\t\t \"INSERT ... SELECT queries\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tDeferredErrorMessage *error = NULL;\n\n\t/*\n\t * We can skip SQL support related checks for router queries as\n\t * they are safe to route with any SQL.\n\t */\n\tif (!routerSelect)\n\t{\n\t\t/* first apply toplevel pushdown checks to SELECT query */\n\t\terror =\n\t\t\tDeferErrorIfUnsupportedSubqueryPushdown(subquery, plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttrue);\n\t\tif (error)\n\t\t{\n\t\t\treturn error;\n\t\t}\n\n\t\t/* then apply subquery pushdown checks to SELECT query */\n\t\terror = DeferErrorIfCannotPushdownSubquery(subquery, false);\n\t\tif (error)\n\t\t{\n\t\t\treturn error;\n\t\t}\n\t}\n\n\tif (IsCitusTableType(targetRelationId, CITUS_LOCAL_TABLE))\n\t{\n\t\t/*\n\t\t * If we're inserting into a citus local table, it is ok because we've\n\t\t * checked the non-existence of distributed tables in the subquery.\n\t\t */\n\t}\n\telse if (IsCitusTableType(targetRelationId, REFERENCE_TABLE))\n\t{\n\t\t/*\n\t\t * If we're inserting into a reference table, all participating tables\n\t\t * should be reference tables as well.\n\t\t */\n\t\tif (!allReferenceTables)\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"only reference tables may be queried when targeting \"\n\t\t\t\t\t\t\t\t \"a reference table with distributed INSERT ... SELECT\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * Note that we've already checked the non-existence of Postgres\n\t\t * tables in the subquery.\n\t\t */\n\t\tif (subqueryRteListProperties->hasCitusLocalTable ||\n\t\t\tsubqueryRteListProperties->hasMaterializedView)\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"distributed INSERT ... SELECT cannot select from \"\n\t\t\t\t\t\t\t\t \"a local relation when inserting into a distributed \"\n\t\t\t\t\t\t\t\t \"table\", NULL, NULL);\n\t\t}\n\n\t\tif (HasDistributionKey(targetRelationId))\n\t\t{\n\t\t\t/* ensure that INSERT's partition column comes from SELECT's partition column */\n\t\t\terror = InsertPartitionColumnMatchesSelect(queryTree, insertRte, subqueryRte,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   &selectPartitionColumnTableId);\n\t\t\tif (error)\n\t\t\t{\n\t\t\t\treturn error;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* All tables in source list and target table should be colocated. */\n\tList *distributedRelationIdList = DistributedRelationIdList(subquery);\n\tdistributedRelationIdList = lappend_oid(distributedRelationIdList,\n\t\t\t\t\t\t\t\t\t\t\ttargetRelationId);\n\n\tif (!AllDistributedRelationsInListColocated(distributedRelationIdList))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"INSERT target relation and all source relations of the \"\n\t\t\t\t\t\t\t \"SELECT must be colocated in distributed INSERT ... SELECT\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * RouterModifyTaskForShardInterval creates a modify task by\n * replacing the partitioning qual parameter added in distributed_planner()\n * with the shardInterval's boundary value. Then perform the normal\n * shard pruning on the subquery. Finally, checks if the target shardInterval\n * has exactly same placements with the select task's available anchor\n * placements.\n *\n * The function errors out if the subquery is not router select query (i.e.,\n * subqueries with non equi-joins.).\n */\nstatic Task *\nRouterModifyTaskForShardInterval(Query *originalQuery,\n\t\t\t\t\t\t\t\t CitusTableCacheEntry *targetTableCacheEntry,\n\t\t\t\t\t\t\t\t ShardInterval *shardInterval,\n\t\t\t\t\t\t\t\t PlannerRestrictionContext *plannerRestrictionContext,\n\t\t\t\t\t\t\t\t uint32 taskIdIndex,\n\t\t\t\t\t\t\t\t bool safeToPushdownSubquery,\n\t\t\t\t\t\t\t\t DeferredErrorMessage **routerPlannerError)\n{\n\tQuery *copiedQuery = copyObject(originalQuery);\n\tRangeTblEntry *copiedInsertRte = ExtractResultRelationRTEOrError(copiedQuery);\n\tRangeTblEntry *copiedSubqueryRte = ExtractSelectRangeTableEntry(copiedQuery);\n\tQuery *copiedSubquery = (Query *) copiedSubqueryRte->subquery;\n\n\tuint64 shardId = shardInterval->shardId;\n\tOid distributedTableId = shardInterval->relationId;\n\n\tPlannerRestrictionContext *copyOfPlannerRestrictionContext = palloc0(\n\t\tsizeof(PlannerRestrictionContext));\n\n\tStringInfo queryString = makeStringInfo();\n\tListCell *restrictionCell = NULL;\n\tList *selectPlacementList = NIL;\n\tuint64 selectAnchorShardId = INVALID_SHARD_ID;\n\tList *relationShardList = NIL;\n\tList *prunedShardIntervalListList = NIL;\n\tuint64 jobId = INVALID_JOB_ID;\n\tbool allReferenceTables =\n\t\tplannerRestrictionContext->relationRestrictionContext->allReferenceTables;\n\tList *shardOpExpressions = NIL;\n\tRestrictInfo *shardRestrictionList = NULL;\n\tbool multiShardModifyQuery = false;\n\tList *relationRestrictionList = NIL;\n\n\tcopyOfPlannerRestrictionContext->relationRestrictionContext =\n\t\tCopyRelationRestrictionContext(\n\t\t\tplannerRestrictionContext->relationRestrictionContext);\n\tcopyOfPlannerRestrictionContext->joinRestrictionContext =\n\t\tplannerRestrictionContext->joinRestrictionContext;\n\tcopyOfPlannerRestrictionContext->fastPathRestrictionContext =\n\t\tplannerRestrictionContext->fastPathRestrictionContext;\n\n\trelationRestrictionList =\n\t\tcopyOfPlannerRestrictionContext->relationRestrictionContext->\n\t\trelationRestrictionList;\n\n\t/* grab shared metadata lock to stop concurrent placement additions */\n\tLockShardDistributionMetadata(shardId, ShareLock);\n\n\t/*\n\t * Replace the partitioning qual parameter value in all baserestrictinfos.\n\t * Note that this has to be done on a copy, as the walker modifies in place.\n\t */\n\tforeach(restrictionCell, relationRestrictionList)\n\t{\n\t\tRelationRestriction *restriction = lfirst(restrictionCell);\n\t\tList *originalBaseRestrictInfo = restriction->relOptInfo->baserestrictinfo;\n\t\tList *extendedBaseRestrictInfo = originalBaseRestrictInfo;\n\t\tIndex rteIndex = restriction->index;\n\n\t\tif (!safeToPushdownSubquery || allReferenceTables)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tshardOpExpressions = ShardIntervalOpExpressions(shardInterval, rteIndex);\n\n\t\t/* means it is a reference table and do not add any shard interval information  */\n\t\tif (shardOpExpressions == NIL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tshardRestrictionList = make_simple_restrictinfo(restriction->plannerInfo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(Expr *) shardOpExpressions);\n\t\textendedBaseRestrictInfo = lappend(extendedBaseRestrictInfo,\n\t\t\t\t\t\t\t\t\t\t   shardRestrictionList);\n\n\t\trestriction->relOptInfo->baserestrictinfo = extendedBaseRestrictInfo;\n\t}\n\n\t/*\n\t * We also need to add shard interval range to the subquery in case\n\t * the partition qual not distributed all tables such as some\n\t * subqueries in WHERE clause.\n\t *\n\t * Note that we need to add the ranges before the shard pruning to\n\t * prevent shard pruning logic (i.e, namely UpdateRelationNames())\n\t * modifies range table entries, which makes hard to add the quals.\n\t */\n\tRTEListProperties *subqueryRteListProperties = GetRTEListPropertiesForQuery(\n\t\tcopiedSubquery);\n\tif (subqueryRteListProperties->hasDistTableWithShardKey)\n\t{\n\t\tAddPartitionKeyNotNullFilterToSelect(copiedSubquery);\n\t}\n\n\t/* mark that we don't want the router planner to generate dummy hosts/queries */\n\tbool replacePrunedQueryWithDummy = false;\n\n\t/*\n\t * Use router planner to decide on whether we can push down the query or not.\n\t * If we can, we also rely on the side-effects that all RTEs have been updated\n\t * to point to the relevant nodes and selectPlacementList is determined.\n\t */\n\tDeferredErrorMessage *planningError = PlanRouterQuery(copiedSubquery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  copyOfPlannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &selectPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &selectAnchorShardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &relationShardList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &prunedShardIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  replacePrunedQueryWithDummy,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &multiShardModifyQuery, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL);\n\n\tAssert(!multiShardModifyQuery);\n\n\tif (planningError)\n\t{\n\t\t*routerPlannerError = planningError;\n\t\treturn NULL;\n\t}\n\n\n\t/* ensure that we do not send queries where select is pruned away completely */\n\tif (list_length(selectPlacementList) == 0)\n\t{\n\t\tereport(DEBUG2, (errmsg(\"Skipping target shard interval \" UINT64_FORMAT\n\t\t\t\t\t\t\t\t\" since SELECT query for it pruned away\",\n\t\t\t\t\t\t\t\tshardId)));\n\n\t\treturn NULL;\n\t}\n\n\t/* get the placements for insert target shard and its intersection with select */\n\tList *insertShardPlacementList = ActiveShardPlacementList(shardId);\n\tList *intersectedPlacementList = IntersectPlacementList(insertShardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tselectPlacementList);\n\n\t/*\n\t * If insert target does not have exactly the same placements with the select,\n\t * we sholdn't run the query.\n\t */\n\tif (list_length(insertShardPlacementList) != list_length(intersectedPlacementList))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot perform distributed planning for the given \"\n\t\t\t\t\t\t\t   \"modification\"),\n\t\t\t\t\t\terrdetail(\"Insert query cannot be executed on all placements \"\n\t\t\t\t\t\t\t\t  \"for shard \" UINT64_FORMAT \"\", shardId)));\n\t}\n\n\n\t/* this is required for correct deparsing of the query */\n\tReorderInsertSelectTargetLists(copiedQuery, copiedInsertRte, copiedSubqueryRte);\n\n\t/* setting an alias simplifies deparsing of RETURNING */\n\tif (copiedInsertRte->alias == NULL)\n\t{\n\t\tAlias *alias = makeAlias(CITUS_TABLE_ALIAS, NIL);\n\t\tcopiedInsertRte->alias = alias;\n\t}\n\n\t/* and generate the full query string */\n\tdeparse_shard_query(copiedQuery, distributedTableId, shardInterval->shardId,\n\t\t\t\t\t\tqueryString);\n\tereport(DEBUG2, (errmsg(\"distributed statement: %s\",\n\t\t\t\t\t\t\tqueryString->data)));\n\n\tTask *modifyTask = CreateBasicTask(jobId, taskIdIndex, MODIFY_TASK,\n\t\t\t\t\t\t\t\t\t   queryString->data);\n\tmodifyTask->dependentTaskList = NULL;\n\tmodifyTask->anchorShardId = shardId;\n\tmodifyTask->taskPlacementList = insertShardPlacementList;\n\tmodifyTask->relationShardList = relationShardList;\n\tmodifyTask->replicationModel = targetTableCacheEntry->replicationModel;\n\tmodifyTask->isLocalTableModification = false;\n\n\treturn modifyTask;\n}\n\n\n/*\n * ReorderInsertSelectTargetLists reorders the target lists of INSERT/SELECT\n * query which is required for deparsing purposes. The reordered query is returned.\n *\n * The necessity for this function comes from the fact that ruleutils.c is not supposed\n * to be used on \"rewritten\" queries (i.e. ones that have been passed through\n * QueryRewrite()). Query rewriting is the process in which views and such are expanded,\n * and, INSERT/UPDATE targetlists are reordered to match the physical order,\n * defaults etc. For the details of reordeing, see transformInsertRow() and\n * rewriteTargetListIU().\n */\nQuery *\nReorderInsertSelectTargetLists(Query *originalQuery, RangeTblEntry *insertRte,\n\t\t\t\t\t\t\t   RangeTblEntry *subqueryRte)\n{\n\tListCell *insertTargetEntryCell;\n\tList *newSubqueryTargetlist = NIL;\n\tList *newInsertTargetlist = NIL;\n\tList *columnNameList = NIL;\n\tint resno = 1;\n\tIndex selectTableId = 2;\n\tint targetEntryIndex = 0;\n\n\tQuery *subquery = subqueryRte->subquery;\n\n\tOid insertRelationId = insertRte->relid;\n\n\t/*\n\t * We implement the following algorithm for the reoderding:\n\t *  - Iterate over the INSERT target list entries\n\t *    - If the target entry includes a Var, find the corresponding\n\t *      SELECT target entry on the original query and update resno\n\t *    - If the target entry does not include a Var (i.e., defaults\n\t *      or constants), create new target entry and add that to\n\t *      SELECT target list\n\t *    - Create a new INSERT target entry with respect to the new\n\t *      SELECT target entry created.\n\t */\n\tforeach(insertTargetEntryCell, originalQuery->targetList)\n\t{\n\t\tTargetEntry *oldInsertTargetEntry = lfirst(insertTargetEntryCell);\n\t\tTargetEntry *newSubqueryTargetEntry = NULL;\n\t\tAttrNumber originalAttrNo = get_attnum(insertRelationId,\n\t\t\t\t\t\t\t\t\t\t\t   oldInsertTargetEntry->resname);\n\n\t\t/* we need to explore the underlying expression */\n\t\tNode *expr = strip_implicit_coercions((Node *) oldInsertTargetEntry->expr);\n\n\t\t/* see transformInsertRow() for the details */\n\t\tif (IsA(expr, SubscriptingRef) || IsA(expr, FieldStore))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),\n\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\"cannot plan distributed INSERT INTO ... SELECT query\"),\n\t\t\t\t\t\t\terrhint(\"Do not use array references and field stores \"\n\t\t\t\t\t\t\t\t\t\"on the INSERT target list.\")));\n\t\t}\n\n\t\t/*\n\t\t * It is safe to pull Var clause and ignore the coercions since that\n\t\t * are already going to be added on the workers implicitly.\n\t\t */\n\t\tList *targetVarList = pull_var_clause((Node *) oldInsertTargetEntry->expr,\n\t\t\t\t\t\t\t\t\t\t\t  PVC_RECURSE_AGGREGATES);\n\n\t\tint targetVarCount = list_length(targetVarList);\n\n\t\t/* a single INSERT target entry cannot have more than one Var */\n\t\tAssert(targetVarCount <= 1);\n\n\t\tif (targetVarCount == 1)\n\t\t{\n\t\t\tVar *oldInsertVar = (Var *) linitial(targetVarList);\n\t\t\tTargetEntry *oldSubqueryTle = list_nth(subquery->targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t   oldInsertVar->varattno - 1);\n\n\t\t\tnewSubqueryTargetEntry = copyObject(oldSubqueryTle);\n\n\t\t\tnewSubqueryTargetEntry->resno = resno;\n\t\t\tnewSubqueryTargetlist = lappend(newSubqueryTargetlist,\n\t\t\t\t\t\t\t\t\t\t\tnewSubqueryTargetEntry);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnewSubqueryTargetEntry = makeTargetEntry(oldInsertTargetEntry->expr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t resno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t oldInsertTargetEntry->resname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t oldInsertTargetEntry->resjunk);\n\t\t\tnewSubqueryTargetlist = lappend(newSubqueryTargetlist,\n\t\t\t\t\t\t\t\t\t\t\tnewSubqueryTargetEntry);\n\t\t}\n\n\t\tString *columnName = makeString(newSubqueryTargetEntry->resname);\n\t\tcolumnNameList = lappend(columnNameList, columnName);\n\n\t\t/*\n\t\t * The newly created select target entry cannot be a junk entry since junk\n\t\t * entries are not in the final target list and we're processing the\n\t\t * final target list entries.\n\t\t */\n\t\tAssert(!newSubqueryTargetEntry->resjunk);\n\n\t\tVar *newInsertVar = makeVar(selectTableId, resno,\n\t\t\t\t\t\t\t\t\texprType((Node *) newSubqueryTargetEntry->expr),\n\t\t\t\t\t\t\t\t\texprTypmod((Node *) newSubqueryTargetEntry->expr),\n\t\t\t\t\t\t\t\t\texprCollation((Node *) newSubqueryTargetEntry->expr),\n\t\t\t\t\t\t\t\t\t0);\n\t\tTargetEntry *newInsertTargetEntry = makeTargetEntry(\n\t\t\t(Expr *) newInsertVar,\n\t\t\toriginalAttrNo,\n\t\t\toldInsertTargetEntry->resname,\n\t\t\toldInsertTargetEntry->resjunk);\n\n\t\tnewInsertTargetlist = lappend(newInsertTargetlist, newInsertTargetEntry);\n\t\tresno++;\n\t}\n\n\t/*\n\t * if there are any remaining target list entries (i.e., GROUP BY column not on the\n\t * target list of subquery), update the remaining resnos.\n\t */\n\tint subqueryTargetLength = list_length(subquery->targetList);\n\tfor (; targetEntryIndex < subqueryTargetLength; ++targetEntryIndex)\n\t{\n\t\tTargetEntry *oldSubqueryTle = list_nth(subquery->targetList,\n\t\t\t\t\t\t\t\t\t\t\t   targetEntryIndex);\n\n\t\t/*\n\t\t * Skip non-junk entries since we've already processed them above and this\n\t\t * loop only is intended for junk entries.\n\t\t */\n\t\tif (!oldSubqueryTle->resjunk)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tTargetEntry *newSubqueryTargetEntry = copyObject(oldSubqueryTle);\n\n\t\tnewSubqueryTargetEntry->resno = resno;\n\t\tnewSubqueryTargetlist = lappend(newSubqueryTargetlist,\n\t\t\t\t\t\t\t\t\t\tnewSubqueryTargetEntry);\n\n\t\tresno++;\n\t}\n\n\toriginalQuery->targetList = newInsertTargetlist;\n\tsubquery->targetList = newSubqueryTargetlist;\n\tsubqueryRte->eref->colnames = columnNameList;\n\n\treturn NULL;\n}\n\n\n/*\n * InsertPartitionColumnMatchesSelect returns NULL the partition column in the\n * table targeted by INSERTed matches with the any of the SELECTed table's\n * partition column.  Returns the error description if there's no match.\n *\n * On return without error (i.e., if partition columns match), the function\n * also sets selectPartitionColumnTableId.\n */\nstatic DeferredErrorMessage *\nInsertPartitionColumnMatchesSelect(Query *query, RangeTblEntry *insertRte,\n\t\t\t\t\t\t\t\t   RangeTblEntry *subqueryRte,\n\t\t\t\t\t\t\t\t   Oid *selectPartitionColumnTableId)\n{\n\tListCell *targetEntryCell = NULL;\n\tuint32 rangeTableId = 1;\n\tOid insertRelationId = insertRte->relid;\n\tVar *insertPartitionColumn = PartitionColumn(insertRelationId, rangeTableId);\n\tQuery *subquery = subqueryRte->subquery;\n\tbool targetTableHasPartitionColumn = false;\n\n\tforeach(targetEntryCell, query->targetList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell);\n\t\tList *insertTargetEntryColumnList = pull_var_clause_default((Node *) targetEntry);\n\t\tVar *subqueryPartitionColumn = NULL;\n\n\t\t/*\n\t\t * We only consider target entries that include a single column. Note that this\n\t\t * is slightly different than directly checking the whether the targetEntry->expr\n\t\t * is a var since the var could be wrapped into an implicit/explicit casting.\n\t\t *\n\t\t * Also note that we skip the target entry if it does not contain a Var, which\n\t\t * corresponds to columns with DEFAULT values on the target list.\n\t\t */\n\t\tif (list_length(insertTargetEntryColumnList) != 1)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tVar *insertVar = (Var *) linitial(insertTargetEntryColumnList);\n\t\tAttrNumber originalAttrNo = targetEntry->resno;\n\n\t\t/* skip processing of target table non-partition columns */\n\t\tif (originalAttrNo != insertPartitionColumn->varattno)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* INSERT query includes the partition column */\n\t\ttargetTableHasPartitionColumn = true;\n\n\t\tTargetEntry *subqueryTargetEntry = list_nth(subquery->targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tinsertVar->varattno - 1);\n\t\tExpr *selectTargetExpr = subqueryTargetEntry->expr;\n\n\t\tRangeTblEntry *subqueryPartitionColumnRelationIdRTE = NULL;\n\t\tList *parentQueryList = list_make2(query, subquery);\n\t\tbool skipOuterVars = false;\n\t\tFindReferencedTableColumn(selectTargetExpr,\n\t\t\t\t\t\t\t\t  parentQueryList, subquery,\n\t\t\t\t\t\t\t\t  &subqueryPartitionColumn,\n\t\t\t\t\t\t\t\t  &subqueryPartitionColumnRelationIdRTE,\n\t\t\t\t\t\t\t\t  skipOuterVars);\n\t\tOid subqueryPartitionColumnRelationId = subqueryPartitionColumnRelationIdRTE ?\n\t\t\t\t\t\t\t\t\t\t\t\tsubqueryPartitionColumnRelationIdRTE->\n\t\t\t\t\t\t\t\t\t\t\t\trelid :\n\t\t\t\t\t\t\t\t\t\t\t\tInvalidOid;\n\n\t\t/*\n\t\t * Corresponding (i.e., in the same ordinal position as the target table's\n\t\t * partition column) select target entry does not directly belong a table.\n\t\t * Evaluate its expression type and error out properly.\n\t\t */\n\t\tif (subqueryPartitionColumnRelationId == InvalidOid)\n\t\t{\n\t\t\tchar *errorDetailTemplate = \"Subquery contains %s in the \"\n\t\t\t\t\t\t\t\t\t\t\"same position as the target table's \"\n\t\t\t\t\t\t\t\t\t\t\"partition column.\";\n\n\t\t\tchar *exprDescription = \"\";\n\n\t\t\tswitch (selectTargetExpr->type)\n\t\t\t{\n\t\t\t\tcase T_Const:\n\t\t\t\t{\n\t\t\t\t\texprDescription = \"a constant value\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase T_OpExpr:\n\t\t\t\t{\n\t\t\t\t\texprDescription = \"an operator\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase T_FuncExpr:\n\t\t\t\t{\n\t\t\t\t\tFuncExpr *subqueryFunctionExpr = (FuncExpr *) selectTargetExpr;\n\n\t\t\t\t\tswitch (subqueryFunctionExpr->funcformat)\n\t\t\t\t\t{\n\t\t\t\t\t\tcase COERCE_EXPLICIT_CALL:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\texprDescription = \"a function call\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcase COERCE_EXPLICIT_CAST:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\texprDescription = \"an explicit cast\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcase COERCE_IMPLICIT_CAST:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\texprDescription = \"an implicit cast\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\texprDescription = \"a function call\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase T_Aggref:\n\t\t\t\t{\n\t\t\t\t\texprDescription = \"an aggregation\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase T_CaseExpr:\n\t\t\t\t{\n\t\t\t\t\texprDescription = \"a case expression\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase T_CoalesceExpr:\n\t\t\t\t{\n\t\t\t\t\texprDescription = \"a coalesce expression\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase T_RowExpr:\n\t\t\t\t{\n\t\t\t\t\texprDescription = \"a row expression\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase T_MinMaxExpr:\n\t\t\t\t{\n\t\t\t\t\texprDescription = \"a min/max expression\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase T_CoerceViaIO:\n\t\t\t\t{\n\t\t\t\t\texprDescription = \"an explicit coercion\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t{\n\t\t\t\t\texprDescription =\n\t\t\t\t\t\t\"an expression that is not a simple column reference\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"cannot perform distributed INSERT INTO ... SELECT \"\n\t\t\t\t\t\t\t\t \"because the partition columns in the source table \"\n\t\t\t\t\t\t\t\t \"and subquery do not match\",\n\t\t\t\t\t\t\t\t psprintf(errorDetailTemplate, exprDescription),\n\t\t\t\t\t\t\t\t \"Ensure the target table's partition column has a \"\n\t\t\t\t\t\t\t\t \"corresponding simple column reference to a distributed \"\n\t\t\t\t\t\t\t\t \"table's partition column in the subquery.\");\n\t\t}\n\n\t\t/*\n\t\t * Insert target expression could only be non-var if the select target\n\t\t * entry does not have the same type (i.e., target column requires casting).\n\t\t */\n\t\tif (!IsA(targetEntry->expr, Var))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"cannot perform distributed INSERT INTO ... SELECT \"\n\t\t\t\t\t\t\t\t \"because the partition columns in the source table \"\n\t\t\t\t\t\t\t\t \"and subquery do not match\",\n\t\t\t\t\t\t\t\t \"The data type of the target table's partition column \"\n\t\t\t\t\t\t\t\t \"should exactly match the data type of the \"\n\t\t\t\t\t\t\t\t \"corresponding simple column reference in the subquery.\",\n\t\t\t\t\t\t\t\t NULL);\n\t\t}\n\n\t\t/* finally, check that the select target column is a partition column */\n\t\tif (!IsPartitionColumn(selectTargetExpr, subquery, skipOuterVars))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"cannot perform distributed INSERT INTO ... SELECT \"\n\t\t\t\t\t\t\t\t \"because the partition columns in the source table \"\n\t\t\t\t\t\t\t\t \"and subquery do not match\",\n\t\t\t\t\t\t\t\t \"The target table's partition column should correspond \"\n\t\t\t\t\t\t\t\t \"to a partition column in the subquery.\",\n\t\t\t\t\t\t\t\t NULL);\n\t\t}\n\n\t\t/* finally, check that the select target column is a partition column */\n\t\t/* we can set the select relation id */\n\t\t*selectPartitionColumnTableId = subqueryPartitionColumnRelationId;\n\n\t\tbreak;\n\t}\n\n\tif (!targetTableHasPartitionColumn)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot perform distributed INSERT INTO ... SELECT \"\n\t\t\t\t\t\t\t \"because the partition columns in the source table \"\n\t\t\t\t\t\t\t \"and subquery do not match\",\n\t\t\t\t\t\t\t \"the query doesn't include the target table's \"\n\t\t\t\t\t\t\t \"partition column\",\n\t\t\t\t\t\t\t NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * CreateNonPushableInsertSelectPlan creates a query plan for a SELECT into a\n * distributed table. The query plan can also be executed on a worker in MX.\n */\nstatic DistributedPlan *\nCreateNonPushableInsertSelectPlan(uint64 planId, Query *parse, ParamListInfo boundParams)\n{\n\tQuery *insertSelectQuery = copyObject(parse);\n\tDistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan);\n\tdistributedPlan->modLevel = RowModifyLevelForQuery(insertSelectQuery);\n\n\tdistributedPlan->planningError =\n\t\tNonPushableInsertSelectSupported(insertSelectQuery);\n\n\tif (distributedPlan->planningError != NULL)\n\t{\n\t\treturn distributedPlan;\n\t}\n\n\tPrepareInsertSelectForCitusPlanner(insertSelectQuery);\n\n\t/* get the SELECT query (may have changed after PrepareInsertSelectForCitusPlanner) */\n\tRangeTblEntry *selectRte = ExtractSelectRangeTableEntry(insertSelectQuery);\n\tQuery *selectQuery = selectRte->subquery;\n\n\t/*\n\t * Later we might need to call WrapTaskListForProjection(), which requires\n\t * that select target list has unique names, otherwise the outer query\n\t * cannot select columns unambiguously. So we relabel select columns to\n\t * match target columns.\n\t */\n\tList *insertTargetList = insertSelectQuery->targetList;\n\tRelabelTargetEntryList(selectQuery->targetList, insertTargetList);\n\n\t/*\n\t * Make a copy of the select query, since following code scribbles it\n\t * but we need to keep the original for EXPLAIN.\n\t */\n\tQuery *selectQueryCopy = copyObject(selectQuery);\n\n\t/* plan the subquery, this may be another distributed query */\n\tint cursorOptions = CURSOR_OPT_PARALLEL_OK;\n\tPlannedStmt *selectPlan = pg_plan_query(selectQueryCopy, NULL, cursorOptions,\n\t\t\t\t\t\t\t\t\t\t\tboundParams);\n\n\t/* decide whether we can repartition the results */\n\tRangeTblEntry *insertRte = ExtractResultRelationRTEOrError(insertSelectQuery);\n\tOid targetRelationId = insertRte->relid;\n\tbool repartitioned = IsRedistributablePlan(selectPlan->planTree) &&\n\t\t\t\t\t\t IsSupportedRedistributionTarget(targetRelationId);\n\n\t/*\n\t * It's not possible to generate a distributed plan for a SELECT\n\t * having more than one tasks if it references a single-shard table.\n\t *\n\t * For this reason, right now we don't expect an INSERT .. SELECT\n\t * query to go through the repartitioned INSERT .. SELECT logic if the\n\t * SELECT query references a single-shard table.\n\t */\n\tAssert(!repartitioned ||\n\t\t   !ContainsSingleShardTable(selectQueryCopy));\n\n\tdistributedPlan->modifyQueryViaCoordinatorOrRepartition = insertSelectQuery;\n\tdistributedPlan->selectPlanForModifyViaCoordinatorOrRepartition = selectPlan;\n\tdistributedPlan->modifyWithSelectMethod = repartitioned ?\n\t\t\t\t\t\t\t\t\t\t\t  MODIFY_WITH_SELECT_REPARTITION :\n\t\t\t\t\t\t\t\t\t\t\t  MODIFY_WITH_SELECT_VIA_COORDINATOR;\n\tdistributedPlan->expectResults = insertSelectQuery->returningList != NIL;\n\tdistributedPlan->intermediateResultIdPrefix = InsertSelectResultIdPrefix(planId);\n\tdistributedPlan->targetRelationId = targetRelationId;\n\n\treturn distributedPlan;\n}\n\n\n/*\n * NonPushableInsertSelectSupported returns an error if executing an\n * INSERT ... SELECT command by pulling results of the SELECT to the coordinator\n * or with repartitioning is unsupported because it needs to generate sequence\n * values or insert into an append-distributed table.\n */\nstatic DeferredErrorMessage *\nNonPushableInsertSelectSupported(Query *insertSelectQuery)\n{\n\tDeferredErrorMessage *deferredError = ErrorIfOnConflictNotSupported(\n\t\tinsertSelectQuery);\n\tif (deferredError)\n\t{\n\t\treturn deferredError;\n\t}\n\n\tRangeTblEntry *insertRte = ExtractResultRelationRTE(insertSelectQuery);\n\tif (IsCitusTableType(insertRte->relid, APPEND_DISTRIBUTED))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"INSERT ... SELECT into an append-distributed table is \"\n\t\t\t\t\t\t\t \"not supported\", NULL, NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * InsertSelectResultPrefix returns the prefix to use for intermediate\n * results of an INSERT ... SELECT via the coordinator that runs in two\n * phases in order to do RETURNING or ON CONFLICT.\n */\nchar *\nInsertSelectResultIdPrefix(uint64 planId)\n{\n\tStringInfo resultIdPrefix = makeStringInfo();\n\n\tappendStringInfo(resultIdPrefix, \"insert_select_\" UINT64_FORMAT, planId);\n\n\treturn resultIdPrefix->data;\n}\n\n\n/*\n * Return true if the expression tree can change value within a single scan\n * (i.e. the planner must treat it as VOLATILE).\n * We just delegate to PostgreSQL’s helper.\n */\nstatic inline bool\nexpr_is_volatile(Node *node)\n{\n\t/* contain_volatile_functions() also returns true for set-returning\n\t * volatile functions and for nextval()/currval(). */\n\treturn contain_volatile_functions(node);\n}\n\n\n/*\n * WrapSubquery\n *\n * Build a wrapper query:\n *\n *     SELECT <outer-TL>\n *       FROM ( <subquery with any volatile items stripped> )\n *            citus_insert_select_subquery\n *\n * Purpose:\n *   - Preserve column numbering while lifting volatile expressions to the coordinator.\n *   - Volatile (non-deterministic) expressions not used in GROUP BY / ORDER BY\n *     are lifted to the outer SELECT to ensure they are evaluated only once.\n *   - Stable/immutable expressions or volatile ones required by GROUP BY / ORDER BY\n *     stay in the subquery and are accessed via Vars in the outer SELECT.\n */\nQuery *\nWrapSubquery(Query *subquery)\n{\n\t/*\n\t * 1. Build the wrapper skeleton: SELECT ... FROM (subquery) alias\n\t */\n\tParseState *pstate = make_parsestate(NULL);\n\tQuery *outerQuery = makeNode(Query);\n\touterQuery->commandType = CMD_SELECT;\n\n\tAlias *alias = makeAlias(\"citus_insert_select_subquery\", NIL);\n\tRangeTblEntry *rte_subq =\n\t\tRangeTableEntryFromNSItem(\n\t\t\taddRangeTableEntryForSubquery(pstate,\n\t\t\t\t\t\t\t\t\t\t  subquery,    /* still points to original subquery */\n\t\t\t\t\t\t\t\t\t\t  alias,\n\t\t\t\t\t\t\t\t\t\t  false,       /* not LATERAL */\n\t\t\t\t\t\t\t\t\t\t  true));      /* in FROM clause */\n\n\touterQuery->rtable = list_make1(rte_subq);\n\n\t/* Ensure RTE_SUBQUERY has proper permission handling */\n\tAssert(rte_subq->rtekind == RTE_SUBQUERY &&\n\t\t   rte_subq->perminfoindex == 0);\n\touterQuery->rteperminfos = NIL;\n\n\tRangeTblRef *rtref = makeNode(RangeTblRef);\n\trtref->rtindex = 1;  /* Only one RTE, so index is 1 */\n\touterQuery->jointree = makeFromExpr(list_make1(rtref), NULL);\n\n\t/*\n\t * 2. Create new target lists for inner (worker) and outer (coordinator)\n\t */\n\tList *newInnerTL = NIL;\n\tList *newOuterTL = NIL;\n\tint nextResno = 1;\n\n\tTargetEntry *te = NULL;\n\tforeach_declared_ptr(te, subquery->targetList)\n\t{\n\t\tif (te->resjunk)\n\t\t{\n\t\t\t/* Keep resjunk entries only in subquery (not in outer query) */\n\t\t\tnewInnerTL = lappend(newInnerTL, te);\n\t\t\tcontinue;\n\t\t}\n\n\t\tbool isVolatile = expr_is_volatile((Node *) te->expr);\n\t\tbool usedInSort = (te->ressortgroupref != 0);\n\n\t\tif (isVolatile && !usedInSort)\n\t\t{\n\t\t\t/*\n\t\t\t * Lift volatile expression to outer query so it's evaluated once.\n\t\t\t * In inner query, place a NULL of the same type to preserve column position.\n\t\t\t */\n\t\t\tTargetEntry *outerTE =\n\t\t\t\tmakeTargetEntry(copyObject(te->expr),\n\t\t\t\t\t\t\t\tlist_length(newOuterTL) + 1,\n\t\t\t\t\t\t\t\tte->resname,\n\t\t\t\t\t\t\t\tfalse);\n\t\t\tnewOuterTL = lappend(newOuterTL, outerTE);\n\n\t\t\tConst *nullConst = makeNullConst(exprType((Node *) te->expr),\n\t\t\t\t\t\t\t\t\t\t\t exprTypmod((Node *) te->expr),\n\t\t\t\t\t\t\t\t\t\t\t exprCollation((Node *) te->expr));\n\n\t\t\tTargetEntry *placeholder =\n\t\t\t\tmakeTargetEntry((Expr *) nullConst,\n\t\t\t\t\t\t\t\tnextResno++,          /* preserve column position */\n\t\t\t\t\t\t\t\tte->resname,\n\t\t\t\t\t\t\t\tfalse);               /* visible, not resjunk */\n\t\t\tnewInnerTL = lappend(newInnerTL, placeholder);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Either:\n\t\t\t *   - expression is stable or immutable, or\n\t\t\t *   - volatile but needed for sorting or grouping\n\t\t\t *\n\t\t\t * In both cases, keep it in subquery and reference it using a Var.\n\t\t\t */\n\t\t\tTargetEntry *innerTE = te;          /* reuse original node */\n\t\t\tinnerTE->resno = nextResno++;\n\t\t\tnewInnerTL = lappend(newInnerTL, innerTE);\n\n\t\t\tVar *v = makeVar(/* subquery reference index is 1 */\n\t\t\t\trtref->rtindex,     /* same as 1, but self‑documenting */\n\t\t\t\tinnerTE->resno,\n\t\t\t\texprType((Node *) innerTE->expr),\n\t\t\t\texprTypmod((Node *) innerTE->expr),\n\t\t\t\texprCollation((Node *) innerTE->expr),\n\t\t\t\t0);\n\n\t\t\tTargetEntry *outerTE =\n\t\t\t\tmakeTargetEntry((Expr *) v,\n\t\t\t\t\t\t\t\tlist_length(newOuterTL) + 1,\n\t\t\t\t\t\t\t\tinnerTE->resname,\n\t\t\t\t\t\t\t\tfalse);\n\t\t\tnewOuterTL = lappend(newOuterTL, outerTE);\n\t\t}\n\t}\n\n\t/*\n\t * 3. Assign target lists and return the wrapper query\n\t */\n\tsubquery->targetList = newInnerTL;\n\touterQuery->targetList = newOuterTL;\n\n\treturn outerQuery;\n}\n\n\n/*\n * RelabelTargetEntryList relabels select target list to have matching names with\n * insert target list.\n */\nstatic void\nRelabelTargetEntryList(List *selectTargetList, List *insertTargetList)\n{\n\tTargetEntry *selectTargetEntry = NULL;\n\tTargetEntry *insertTargetEntry = NULL;\n\tforboth_ptr(selectTargetEntry, selectTargetList, insertTargetEntry, insertTargetList)\n\t{\n\t\tselectTargetEntry->resname = insertTargetEntry->resname;\n\t}\n}\n\n\n/*\n * AddInsertSelectCasts ensures that the columns in the given target lists\n * have the same type as the corresponding columns of the target relation.\n * It adds casts when necessary.\n *\n * Returns the updated selectTargetList.\n */\nstatic List *\nAddInsertSelectCasts(List *insertTargetList, List *selectTargetList,\n\t\t\t\t\t Oid targetRelationId)\n{\n\tList *projectedEntries = NIL;\n\tList *nonProjectedEntries = NIL;\n\n\t/*\n\t * ReorderInsertSelectTargetLists() ensures that the first few columns of the\n\t * SELECT query match the insert targets. It might also include additional\n\t * items (for GROUP BY, etc.), so the insertTargetList is shorter.\n\t */\n\tAssert(list_length(insertTargetList) <= list_length(selectTargetList));\n\n\tRelation distributedRelation = table_open(targetRelationId, RowExclusiveLock);\n\tTupleDesc destTupleDescriptor = RelationGetDescr(distributedRelation);\n\n\tint targetEntryIndex = 0;\n\tTargetEntry *insertEntry = NULL;\n\tTargetEntry *selectEntry = NULL;\n\n\tforboth_ptr(insertEntry, insertTargetList, selectEntry, selectTargetList)\n\t{\n\t\t/*\n\t\t * Retrieve the target attribute corresponding to the insert entry.\n\t\t * The attribute is located at (resno - 1) in the tuple descriptor.\n\t\t */\n\t\tForm_pg_attribute attr = TupleDescAttr(destTupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t   insertEntry->resno - 1);\n\n\t\tProcessEntryPair(insertEntry, selectEntry, attr, targetEntryIndex,\n\t\t\t\t\t\t &projectedEntries, &nonProjectedEntries);\n\n\t\ttargetEntryIndex++;\n\t}\n\n\t/* Append any additional non-projected entries from selectTargetList */\n\tfor (int entryIndex = list_length(insertTargetList);\n\t\t entryIndex < list_length(selectTargetList);\n\t\t entryIndex++)\n\t{\n\t\tnonProjectedEntries = lappend(nonProjectedEntries, list_nth(selectTargetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tentryIndex));\n\t}\n\n\t/* Concatenate projected and non-projected entries and reset resno numbering */\n\tselectTargetList = list_concat(projectedEntries, nonProjectedEntries);\n\tResetTargetEntryResno(selectTargetList);\n\n\ttable_close(distributedRelation, NoLock);\n\n\treturn selectTargetList;\n}\n\n\n/*\n * Processes a single pair of insert and select target entries.\n * It compares the source and target types and appends either the\n * original select entry or a casted version to the appropriate list.\n */\nstatic void\nProcessEntryPair(TargetEntry *insertEntry, TargetEntry *selectEntry,\n\t\t\t\t Form_pg_attribute attr, int targetEntryIndex,\n\t\t\t\t List **projectedEntries, List **nonProjectedEntries)\n{\n\tOid effectiveSourceType = exprType((Node *) selectEntry->expr);\n\tOid targetType = attr->atttypid;\n\n\t/*\n\t * If the select expression is a NextValueExpr, use its actual return type.\n\t *\n\t * NextValueExpr represents a call to the nextval() function, which is used to\n\t * obtain the next value from a sequence—commonly for populating auto-increment\n\t * columns. In many cases, nextval() returns an INT8 (bigint), but the actual\n\t * return type may differ depending on database configuration or custom implementations.\n\t *\n\t * Since the target column might have a different type (e.g., INT4), we need to\n\t * obtain the real return type of nextval() to ensure that any type coercion is applied\n\t * correctly. This is done by calling GetNextvalReturnTypeCatalog(), which looks up the\n\t * function in the catalog and returns its return type. The effectiveSourceType is then\n\t * set to this value, ensuring that subsequent comparisons and casts use the correct type.\n\t */\n\tif (IsA(selectEntry->expr, NextValueExpr))\n\t{\n\t\teffectiveSourceType = GetNextvalReturnTypeCatalog();\n\t}\n\n\tif (effectiveSourceType != targetType)\n\t{\n\t\tAppendCastedEntry(insertEntry, selectEntry,\n\t\t\t\t\t\t  effectiveSourceType, targetType,\n\t\t\t\t\t\t  attr->attcollation, attr->atttypmod,\n\t\t\t\t\t\t  targetEntryIndex,\n\t\t\t\t\t\t  projectedEntries, nonProjectedEntries);\n\t}\n\telse\n\t{\n\t\t/* Types match, no cast needed */\n\t\t*projectedEntries = lappend(*projectedEntries, selectEntry);\n\t}\n}\n\n\n/*\n * Resets the resno field for each target entry in the list so that\n * they are numbered sequentially.\n */\nstatic void\nResetTargetEntryResno(List *targetList)\n{\n\tint entryResNo = 1;\n\tListCell *lc = NULL;\n\tforeach(lc, targetList)\n\t{\n\t\tTargetEntry *tle = (TargetEntry *) lfirst(lc);\n\t\ttle->resno = entryResNo++;\n\t}\n}\n\n\n/*\n * Looks up the nextval(regclass) function in pg_proc, returning its actual\n * rettype. In a standard build, that will be INT8OID, but this is more robust.\n */\nstatic Oid\nGetNextvalReturnTypeCatalog(void)\n{\n\tOid argTypes[1] = { REGCLASSOID };\n\tList *nameList = list_make1(makeString(\"nextval\"));\n\n\t/* Look up the nextval(regclass) function */\n\tOid nextvalFuncOid = LookupFuncName(nameList, 1, argTypes, false);\n\tif (!OidIsValid(nextvalFuncOid))\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_UNDEFINED_FUNCTION),\n\t\t\t\t errmsg(\"could not find function nextval(regclass)\")));\n\t}\n\n\t/* Retrieve and validate the return type of the nextval function */\n\tOid nextvalReturnType = get_func_rettype(nextvalFuncOid);\n\tif (!OidIsValid(nextvalReturnType))\n\t{\n\t\telog(ERROR, \"could not determine return type of nextval(regclass)\");\n\t}\n\n\treturn nextvalReturnType;\n}\n\n\n/**\n * Modifies the given insert entry to match the target column's type and typmod,\n * then creates and appends a new target entry containing a casted expression\n * to the projected list. If the original select entry is used by ORDER BY or GROUP BY,\n * it is marked as junk to avoid ambiguity.\n */\nstatic void\nAppendCastedEntry(TargetEntry *insertEntry, TargetEntry *selectEntry,\n\t\t\t\t  Oid castFromType, Oid targetType, Oid collation, int32 typmod,\n\t\t\t\t  int targetEntryIndex,\n\t\t\t\t  List **projectedEntries, List **nonProjectedEntries)\n{\n\t/* Update the insert entry's Var to match the target column's type, typmod, and collation */\n\tAssert(IsA(insertEntry->expr, Var));\n\t{\n\t\tVar *insertVar = (Var *) insertEntry->expr;\n\t\tinsertVar->vartype = targetType;\n\t\tinsertVar->vartypmod = typmod;\n\t\tinsertVar->varcollid = collation;\n\t}\n\n\t/* Create a new TargetEntry with the casted expression */\n\tTargetEntry *coercedEntry = copyObject(selectEntry);\n\tcoercedEntry->expr = CastExpr((Expr *) selectEntry->expr,\n\t\t\t\t\t\t\t\t  castFromType,\n\t\t\t\t\t\t\t\t  targetType,\n\t\t\t\t\t\t\t\t  collation,\n\t\t\t\t\t\t\t\t  typmod);\n\tcoercedEntry->ressortgroupref = 0;\n\n\t/* Assign a unique name to the coerced entry */\n\tSetTargetEntryName(coercedEntry, \"auto_coerced_by_citus_%d\", targetEntryIndex);\n\t*projectedEntries = lappend(*projectedEntries, coercedEntry);\n\n\t/* If the original select entry is referenced in ORDER BY or GROUP BY,\n\t * mark it as junk and rename it to avoid ambiguity.\n\t */\n\tif (selectEntry->ressortgroupref != 0)\n\t{\n\t\tselectEntry->resjunk = true;\n\t\tSetTargetEntryName(selectEntry, \"discarded_target_item_%d\", targetEntryIndex);\n\t\t*nonProjectedEntries = lappend(*nonProjectedEntries, selectEntry);\n\t}\n}\n\n\n/*\n * CastExpr returns an expression which casts the given expr from sourceType to\n * the given targetType.\n */\nstatic Expr *\nCastExpr(Expr *expr, Oid sourceType, Oid targetType, Oid targetCollation,\n\t\t int targetTypeMod)\n{\n\tOid coercionFuncId = InvalidOid;\n\tCoercionPathType coercionType = find_coercion_pathway(targetType, sourceType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  COERCION_EXPLICIT,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &coercionFuncId);\n\n\tif (coercionType == COERCION_PATH_FUNC)\n\t{\n\t\tFuncExpr *coerceExpr = makeNode(FuncExpr);\n\t\tcoerceExpr->funcid = coercionFuncId;\n\t\tcoerceExpr->args = list_make1(copyObject(expr));\n\t\tcoerceExpr->funccollid = targetCollation;\n\t\tcoerceExpr->funcresulttype = targetType;\n\n\t\treturn (Expr *) coerceExpr;\n\t}\n\telse if (coercionType == COERCION_PATH_RELABELTYPE)\n\t{\n\t\tRelabelType *coerceExpr = makeNode(RelabelType);\n\t\tcoerceExpr->arg = copyObject(expr);\n\t\tcoerceExpr->resulttype = targetType;\n\t\tcoerceExpr->resulttypmod = targetTypeMod;\n\t\tcoerceExpr->resultcollid = targetCollation;\n\t\tcoerceExpr->relabelformat = COERCE_IMPLICIT_CAST;\n\t\tcoerceExpr->location = -1;\n\n\t\treturn (Expr *) coerceExpr;\n\t}\n\telse if (coercionType == COERCION_PATH_ARRAYCOERCE)\n\t{\n\t\tOid sourceBaseType = get_base_element_type(sourceType);\n\t\tOid targetBaseType = get_base_element_type(targetType);\n\n\t\tCaseTestExpr *elemExpr = makeNode(CaseTestExpr);\n\t\telemExpr->collation = targetCollation;\n\t\telemExpr->typeId = sourceBaseType;\n\t\telemExpr->typeMod = -1;\n\n\t\tExpr *elemCastExpr = CastExpr((Expr *) elemExpr, sourceBaseType,\n\t\t\t\t\t\t\t\t\t  targetBaseType, targetCollation,\n\t\t\t\t\t\t\t\t\t  targetTypeMod);\n\n\t\tArrayCoerceExpr *coerceExpr = makeNode(ArrayCoerceExpr);\n\t\tcoerceExpr->arg = copyObject(expr);\n\t\tcoerceExpr->elemexpr = elemCastExpr;\n\t\tcoerceExpr->resultcollid = targetCollation;\n\t\tcoerceExpr->resulttype = targetType;\n\t\tcoerceExpr->resulttypmod = targetTypeMod;\n\t\tcoerceExpr->location = -1;\n\t\tcoerceExpr->coerceformat = COERCE_IMPLICIT_CAST;\n\n\t\treturn (Expr *) coerceExpr;\n\t}\n\telse if (coercionType == COERCION_PATH_COERCEVIAIO)\n\t{\n\t\tCoerceViaIO *coerceExpr = makeNode(CoerceViaIO);\n\t\tcoerceExpr->arg = (Expr *) copyObject(expr);\n\t\tcoerceExpr->resulttype = targetType;\n\t\tcoerceExpr->resultcollid = targetCollation;\n\t\tcoerceExpr->coerceformat = COERCE_IMPLICIT_CAST;\n\t\tcoerceExpr->location = -1;\n\n\t\treturn (Expr *) coerceExpr;\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find a conversion path from type %d to %d\",\n\t\t\t\t\t\t\t   sourceType, targetType)));\n\t}\n\n\treturn NULL; /* keep compiler happy */\n}\n\n\n/* Helper function to set the target entry name using a formatted string */\nstatic void\nSetTargetEntryName(TargetEntry *tle, const char *format, int index)\n{\n\tStringInfo resnameString = makeStringInfo();\n\tappendStringInfo(resnameString, format, index);\n\ttle->resname = resnameString->data;\n}\n\n\n/* PlanningInsertSelect returns true if we are planning an INSERT ...SELECT query */\nbool\nPlanningInsertSelect(void)\n{\n\treturn insertSelectPlannerLevel > 0;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/intermediate_result_pruning.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * intermediate_result_pruning.c\n *   Functions for pruning intermediate result broadcasting.\n *\n * We only send intermediate results of subqueries and CTEs to worker nodes\n * that use them in the remainder of the distributed plan to avoid unnecessary\n * network traffic.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"common/hashfn.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/intermediate_result_pruning.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/worker_manager.h\"\n\n/* controlled via GUC, used mostly for testing */\nbool LogIntermediateResults = false;\n\n\nstatic List * FindSubPlansUsedInNode(Node *node, SubPlanAccessType accessType);\nstatic void AppendAllAccessedWorkerNodes(IntermediateResultsHashEntry *entry,\n\t\t\t\t\t\t\t\t\t\t DistributedPlan *distributedPlan,\n\t\t\t\t\t\t\t\t\t\t int nodeCount);\nstatic void AppendAllWorkerNodes(IntermediateResultsHashEntry *entry);\nstatic List * FindAllRemoteWorkerNodesUsingSubplan(IntermediateResultsHashEntry *entry);\nstatic List * RemoveLocalNodeFromWorkerList(List *workerNodeList);\nstatic void LogIntermediateResultMulticastSummary(IntermediateResultsHashEntry *entry,\n\t\t\t\t\t\t\t\t\t\t\t\t  List *workerNodeList);\n\n\n/*\n * FindSubPlanUsages finds the subplans used in the master query and the\n * job query and returns them as a combined list of UsedDistributedSubPlan\n * structs.\n *\n * The list may contain duplicates if the subplan is referenced multiple\n * times.\n */\nList *\nFindSubPlanUsages(DistributedPlan *plan)\n{\n\tList *localSubPlans = NIL;\n\tList *remoteSubPlans = NIL;\n\n\tif (plan->combineQuery != NULL)\n\t{\n\t\tlocalSubPlans = FindSubPlansUsedInNode((Node *) plan->combineQuery,\n\t\t\t\t\t\t\t\t\t\t\t   SUBPLAN_ACCESS_LOCAL);\n\t}\n\n\tif (plan->workerJob != NULL)\n\t{\n\t\t/*\n\t\t * Mark the subplans as needed on remote side. Note that this decision is\n\t\t * revisited on execution, when the query only consists of intermediate\n\t\t * results.\n\t\t */\n\t\tremoteSubPlans = FindSubPlansUsedInNode((Node *) plan->workerJob->jobQuery,\n\t\t\t\t\t\t\t\t\t\t\t\tSUBPLAN_ACCESS_REMOTE);\n\t}\n\n\tif (plan->modifyQueryViaCoordinatorOrRepartition != NULL)\n\t{\n\t\t/* INSERT..SELECT plans currently do not have a workerJob */\n\t\tAssert(plan->workerJob == NULL);\n\n\t\t/*\n\t\t * The SELECT in an INSERT..SELECT is not fully planned yet and we cannot\n\t\t * perform pruning. We therefore require all subplans used in the\n\t\t * INSERT..SELECT to be available all nodes.\n\t\t */\n\t\tremoteSubPlans =\n\t\t\tFindSubPlansUsedInNode((Node *) plan->modifyQueryViaCoordinatorOrRepartition,\n\t\t\t\t\t\t\t\t   SUBPLAN_ACCESS_ANYWHERE);\n\t}\n\n\t/* merge the used subplans */\n\treturn list_concat(localSubPlans, remoteSubPlans);\n}\n\n\n/*\n * FindSubPlansUsedInPlan finds all the subplans used by the plan by traversing\n * the input node.\n */\nstatic List *\nFindSubPlansUsedInNode(Node *node, SubPlanAccessType accessType)\n{\n\tList *rangeTableList = NIL;\n\tListCell *rangeTableCell = NULL;\n\tList *usedSubPlanList = NIL;\n\n\tExtractRangeTableEntryWalker(node, &rangeTableList);\n\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = lfirst(rangeTableCell);\n\n\t\tif (rangeTableEntry->rtekind == RTE_FUNCTION)\n\t\t{\n\t\t\tchar *resultId =\n\t\t\t\tFindIntermediateResultIdIfExists(rangeTableEntry);\n\n\t\t\tif (resultId == NULL)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Use a Value to be able to use list_append_unique and store\n\t\t\t * the result ID in the DistributedPlan.\n\t\t\t */\n\t\t\tUsedDistributedSubPlan *usedPlan = CitusMakeNode(UsedDistributedSubPlan);\n\n\t\t\tusedPlan->subPlanId = pstrdup(resultId);\n\t\t\tusedPlan->accessType = accessType;\n\n\t\t\tusedSubPlanList = lappend(usedSubPlanList, usedPlan);\n\t\t}\n\t}\n\n\treturn usedSubPlanList;\n}\n\n\n/*\n * RecordSubplanExecutionsOnNodes iterates over the usedSubPlanNodeList,\n * and for each entry, record the workerNodes that are accessed by\n * the distributed plan.\n *\n * Later, we'll use this information while we broadcast the intermediate\n * results to the worker nodes. The idea is that the intermediate result\n * should only be broadcasted to the worker nodes that are accessed by\n * the distributedPlan(s) that the subPlan is used in.\n *\n * Finally, the function recursively descends into the actual subplans\n * of the input distributedPlan as well.\n */\nvoid\nRecordSubplanExecutionsOnNodes(HTAB *intermediateResultsHash,\n\t\t\t\t\t\t\t   DistributedPlan *distributedPlan)\n{\n\tList *usedSubPlanNodeList = distributedPlan->usedSubPlanNodeList;\n\tList *subPlanList = distributedPlan->subPlanList;\n\tListCell *subPlanCell = NULL;\n\tint nodeCount = list_length(ActiveReadableNodeList());\n\n\tforeach(subPlanCell, usedSubPlanNodeList)\n\t{\n\t\tUsedDistributedSubPlan *usedPlan = lfirst(subPlanCell);\n\n\t\tchar *resultId = usedPlan->subPlanId;\n\n\t\tIntermediateResultsHashEntry *entry = SearchIntermediateResult(\n\t\t\tintermediateResultsHash, resultId);\n\n\t\t/*\n\t\t * There is no need to traverse the subplan if the intermediate result\n\t\t * will be written to a local file and sent to all nodes. Note that the\n\t\t * remaining subplans in the distributed plan should still be traversed.\n\t\t */\n\t\tif (list_length(entry->nodeIdList) == nodeCount && entry->writeLocalFile)\n\t\t{\n\t\t\telog(DEBUG4, \"Subplan %s is used in all workers\", resultId);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (usedPlan->accessType == SUBPLAN_ACCESS_LOCAL)\n\t\t{\n\t\t\t/* subPlan needs to be written locally as the planner decided */\n\t\t\tentry->writeLocalFile = true;\n\t\t}\n\t\telse if (usedPlan->accessType == SUBPLAN_ACCESS_REMOTE)\n\t\t{\n\t\t\t/*\n\t\t\t * traverse the plan and add find all worker nodes\n\t\t\t *\n\t\t\t * If we have reference tables in the distributed plan, all the\n\t\t\t * workers will be in the node list. We can improve intermediate result\n\t\t\t * pruning by deciding which reference table shard will be accessed earlier.\n\t\t\t */\n\t\t\tAppendAllAccessedWorkerNodes(entry, distributedPlan, nodeCount);\n\n\t\t\telog(DEBUG4, \"Subplan %s is used in %lu\", resultId, distributedPlan->planId);\n\t\t}\n\t\telse if (usedPlan->accessType == SUBPLAN_ACCESS_ANYWHERE)\n\t\t{\n\t\t\t/* subplan is needed on all nodes */\n\t\t\tentry->writeLocalFile = true;\n\t\t\tAppendAllWorkerNodes(entry);\n\t\t}\n\t}\n\n\t/* descend into the subPlans */\n\tforeach(subPlanCell, subPlanList)\n\t{\n\t\tDistributedSubPlan *subPlan = (DistributedSubPlan *) lfirst(subPlanCell);\n\t\tCustomScan *customScan = FetchCitusCustomScanIfExists(subPlan->plan->planTree);\n\t\tif (customScan)\n\t\t{\n\t\t\tDistributedPlan *distributedPlanOfSubPlan = GetDistributedPlan(customScan);\n\t\t\tRecordSubplanExecutionsOnNodes(intermediateResultsHash,\n\t\t\t\t\t\t\t\t\t\t   distributedPlanOfSubPlan);\n\t\t}\n\t}\n}\n\n\n/*\n * AppendAllAccessedWorkerNodes iterates over all the tasks in a distributed plan\n * to updates the list of worker nodes that can be accessed when this plan is\n * executed in entry. Depending on the plan, the function may give the decision for\n * writing the results locally.\n *\n * If there are multiple placements of a Shard, all of them are considered and\n * all the workers with placements are appended to the list. This effectively\n * means that if there is a reference table access in the distributed plan, all\n * the workers will be in the resulting list.\n */\nstatic void\nAppendAllAccessedWorkerNodes(IntermediateResultsHashEntry *entry,\n\t\t\t\t\t\t\t DistributedPlan *distributedPlan,\n\t\t\t\t\t\t\t int nodeCount)\n{\n\tList *taskList = distributedPlan->workerJob->taskList;\n\tListCell *taskCell = NULL;\n\n\tforeach(taskCell, taskList)\n\t{\n\t\tTask *task = lfirst(taskCell);\n\t\tListCell *placementCell = NULL;\n\t\tforeach(placementCell, task->taskPlacementList)\n\t\t{\n\t\t\tShardPlacement *placement = lfirst(placementCell);\n\n\t\t\tif (placement->nodeId == LOCAL_NODE_ID)\n\t\t\t{\n\t\t\t\tentry->writeLocalFile = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tentry->nodeIdList =\n\t\t\t\tlist_append_unique_int(entry->nodeIdList, placement->nodeId);\n\n\t\t\t/* early return if all the workers are accessed */\n\t\t\tif (list_length(entry->nodeIdList) == nodeCount &&\n\t\t\t\tentry->writeLocalFile)\n\t\t\t{\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * AppendAllWorkerNodes appends all node IDs of readable worker nodes to the\n * nodeIdList, meaning the corresponding intermediate result should be sent\n * to all readable nodes.\n */\nstatic void\nAppendAllWorkerNodes(IntermediateResultsHashEntry *entry)\n{\n\tList *workerNodeList = ActiveReadableNodeList();\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tentry->nodeIdList =\n\t\t\tlist_append_unique_int(entry->nodeIdList, workerNode->nodeId);\n\t}\n}\n\n\n/*\n * MakeIntermediateResultHTAB is a helper method that creates a Hash Table that\n * stores information on the intermediate result.\n */\nHTAB *\nMakeIntermediateResultHTAB()\n{\n\tHASHCTL info = { 0 };\n\tint initialNumberOfElements = 16;\n\n\tinfo.keysize = NAMEDATALEN;\n\tinfo.entrysize = sizeof(IntermediateResultsHashEntry);\n\tinfo.hash = string_hash;\n\tinfo.hcxt = CurrentMemoryContext;\n\tuint32 hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);\n\n\tHTAB *intermediateResultsHash = hash_create(\"Intermediate results hash\",\n\t\t\t\t\t\t\t\t\t\t\t\tinitialNumberOfElements, &info,\n\t\t\t\t\t\t\t\t\t\t\t\thashFlags);\n\n\treturn intermediateResultsHash;\n}\n\n\n/*\n * FindAllWorkerNodesUsingSubplan creates a list of worker nodes that\n * may need to access subplan results. The function also sets writeToLocalFile\n * flag if the result should also need be written locally.\n */\nList *\nFindAllWorkerNodesUsingSubplan(HTAB *intermediateResultsHash,\n\t\t\t\t\t\t\t   char *resultId)\n{\n\tIntermediateResultsHashEntry *entry =\n\t\tSearchIntermediateResult(intermediateResultsHash, resultId);\n\n\tList *remoteWorkerNodes = FindAllRemoteWorkerNodesUsingSubplan(entry);\n\n\t/*\n\t * Don't include the current worker if the result will be written to local\n\t * file as this would be very inefficient and potentially leading race\n\t * conditions while tring to write the same file twice.\n\t */\n\tif (entry->writeLocalFile)\n\t{\n\t\tremoteWorkerNodes = RemoveLocalNodeFromWorkerList(remoteWorkerNodes);\n\t}\n\n\tLogIntermediateResultMulticastSummary(entry, remoteWorkerNodes);\n\n\treturn remoteWorkerNodes;\n}\n\n\n/*\n * FindAllRemoteWorkerNodesUsingSubplan goes over the nodeIdList of the\n * intermediate result entry, and returns a list of workerNodes that the\n * entry should be multi-casted to. The aim of the function is to filter\n * out nodes with LOCAL_NODE_ID.\n */\nstatic List *\nFindAllRemoteWorkerNodesUsingSubplan(IntermediateResultsHashEntry *entry)\n{\n\tList *workerNodeList = NIL;\n\n\tListCell *nodeIdCell = NULL;\n\tforeach(nodeIdCell, entry->nodeIdList)\n\t{\n\t\tuint32 nodeId = lfirst_int(nodeIdCell);\n\t\tWorkerNode *workerNode = LookupNodeByNodeId(nodeId);\n\t\tif (workerNode != NULL)\n\t\t{\n\t\t\tworkerNodeList = lappend(workerNodeList, workerNode);\n\t\t}\n\t}\n\n\treturn workerNodeList;\n}\n\n\n/*\n * RemoveLocalNodeFromWorkerList goes over the input workerNode list and\n * removes the worker node with the local group id, and returns a new list.\n */\nstatic List *\nRemoveLocalNodeFromWorkerList(List *workerNodeList)\n{\n\tint32 localGroupId = GetLocalGroupId();\n\n\tListCell *workerNodeCell = NULL;\n\tforeach(workerNodeCell, workerNodeList)\n\t{\n\t\tWorkerNode *workerNode = (WorkerNode *) lfirst(workerNodeCell);\n\t\tif (workerNode->groupId == localGroupId)\n\t\t{\n\t\t\treturn list_delete_cell(workerNodeList, workerNodeCell);\n\t\t}\n\t}\n\n\treturn workerNodeList;\n}\n\n\n/*\n * LogIntermediateResultMulticastSummary is a utility function to DEBUG output\n * the decisions given on which intermediate result should be sent to which node.\n *\n * For details, see the function comments.\n */\nstatic void\nLogIntermediateResultMulticastSummary(IntermediateResultsHashEntry *entry,\n\t\t\t\t\t\t\t\t\t  List *workerNodeList)\n{\n\tchar *resultId = entry->key;\n\n\t/*\n\t * Log a summary of decisions made for intermediate result multicast. By default\n\t * we log at level DEBUG4. When the user has set citus.log_intermediate_results\n\t * we change the log level to DEBUG1. This is mostly useful in regression tests\n\t * where we specifically want to debug this decisions, but not all DEBUG4 messages.\n\t */\n\tint logLevel = DEBUG4;\n\n\tif (LogIntermediateResults)\n\t{\n\t\tlogLevel = DEBUG1;\n\t}\n\n\tif (IsLoggableLevel(logLevel))\n\t{\n\t\tif (entry->writeLocalFile)\n\t\t{\n\t\t\telog(logLevel, \"Subplan %s will be written to local file\", resultId);\n\t\t}\n\n\t\tWorkerNode *workerNode = NULL;\n\t\tforeach_declared_ptr(workerNode, workerNodeList)\n\t\t{\n\t\t\telog(logLevel, \"Subplan %s will be sent to %s:%d\", resultId,\n\t\t\t\t workerNode->workerName, workerNode->workerPort);\n\t\t}\n\t}\n}\n\n\n/*\n * SearchIntermediateResult searches through intermediateResultsHash for a given\n * intermediate result id.\n *\n * If an entry is not found, creates a new entry with sane defaults.\n */\nIntermediateResultsHashEntry *\nSearchIntermediateResult(HTAB *intermediateResultsHash, char *resultId)\n{\n\tbool found = false;\n\n\tIntermediateResultsHashEntry *entry = hash_search(intermediateResultsHash, resultId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  HASH_ENTER, &found);\n\n\t/* use sane defaults */\n\tif (!found)\n\t{\n\t\tentry->nodeIdList = NIL;\n\t\tentry->writeLocalFile = false;\n\t}\n\n\treturn entry;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/local_distributed_join_planner.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * local_distributed_join_planner.c\n *\n * This file contains functions to convert convert local-distributed\n * tables to subqueries so that they can be planned by the router planner.\n *\n *\n * The current algorithm checks if there is any table in the `jointree` that\n * should be converted, if so it creates conversion candidates.\n * With conversion candidates, it will convert either a distributed table or a local table to a\n * subquery until it is plannable by router planner. It will choose a distributed table if we\n * expect it to return few rows, such as a constant equality filter on a unique column.\n *\n * ```sql\n * -- assuming dist.a is a unique column, this will convert distributed table\n * SELECT * FROM dist join local ON(a) where dist.a = 5;\n * ```\n *\n * If the uniqueness is defined on multiple columns such as `dist.a, dist.b`\n * then distributed table will only be chosen if there is a constant equality in all of the columns such as:\n *\n * ```sql\n * SELECT * FROM dist join local ON(a) where dist.a = 5 AND dist.b =10; -- this will choose distributed table\n * SELECT * FROM dist join local ON(a) where dist.a = 5 AND dist.b >10; -- this won't since no equality on dist.b\n * SELECT * FROM dist join local ON(a) where dist.a = 5; -- this won't since no equality on dist.b\n * ```\n *\n * The algorithm will also not favor distributed tables if there exists a\n * distributed table which is expected to return many rows, because in that\n * case we will already plan local tables hence there is no point in converting some distributed tables.\n *\n * ```sql\n * -- here only the local table will be chosen\n * SELECT * FROM dist_without_unique JOIN dist_with_unique USING(a) join local USING (a);\n * ```\n *\n * this also makes the algorithm consistent.\n *\n * The algorithm can understand `OR` and `AND` expressions in the filters.\n *\n * There is a GUC called `local_table_join_policy` consisting of 4 modes:\n * `none`: don't do any conversion\n * `prefer-local`: prefer converting local tables if there is\n * `prefer-distributed`: prefer converting distributed tables if there is\n * `auto`: use the above mechanism to decide (constant equality on unique column)\n *\n * `auto` mode is the default.\n *\n * While converting to a subquery, we use a trick to avoid unnecessary network bandwidth,\n * if there are columns that are not required in a table that will be converted to a subquery, We do:\n *\n * ```sql\n * SELECT t.a, NULL, NULL (SELECT a FROM table) t\n * ```\n *\n * instead of\n *\n * ```sql\n * SELECT a, NULL, NULL FROM table\n * ```\n *\n * There are NULLs in the query because we currently don't have an easy way to update the Vars\n * that reference the non-required ones and we don't want to break the postgres query.\n *\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_index.h\"\n#include \"catalog/pg_type.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/pathnodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/planner.h\"\n#include \"optimizer/prep.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parsetree.h\"\n#include \"utils/builtins.h\"\n#include \"utils/guc.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_distributed_join_planner.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/query_colocation_checker.h\"\n#include \"distributed/query_pushdown_planning.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/relation_restriction_equivalence.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/version_compat.h\"\n\n#define INVALID_RTE_IDENTITY -1\n\n/*\n * Managed via a GUC\n */\nint LocalTableJoinPolicy = LOCAL_JOIN_POLICY_AUTO;\n\n/*\n * RangeTableEntryDetails contains some information about\n * a range table entry so that we don't need to calculate\n * them over and over.\n */\ntypedef struct RangeTableEntryDetails\n{\n\tRangeTblEntry *rangeTableEntry;\n\tList *requiredAttributeNumbers;\n\tbool hasConstantFilterOnUniqueColumn;\n\tRTEPermissionInfo *perminfo;\n} RangeTableEntryDetails;\n\n/*\n * ConversionCandidates contains candidates that could\n * be converted to a subquery. This is used as a convenience to\n * first generate all the candidates and then choose which ones to convert.\n */\ntypedef struct ConversionCandidates\n{\n\tList *distributedTableList; /* reference or distributed table */\n\tList *localTableList; /* local or citus local table */\n}ConversionCandidates;\n\n\n/*\n * IndexColumns contains the column numbers for an index.\n * For example if there is an index on (a, b) then it will contain\n * their column numbers (1,2).\n */\ntypedef struct IndexColumns\n{\n\tList *indexColumnNos;\n}IndexColumns;\n\n/*\n * ConversionChoice represents which conversion group\n * to convert to a subquery. Currently we either convert all\n * local tables, or distributed tables.\n */\ntypedef enum ConversionChoice\n{\n\tCONVERT_LOCAL_TABLES = 1,\n\tCONVERT_DISTRIBUTED_TABLES = 2\n}ConversionChoice;\n\nstatic bool HasConstantFilterOnUniqueColumn(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t\tRelationRestriction *relationRestriction);\nstatic ConversionCandidates * CreateConversionCandidates(PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t List *rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t int resultRTEIdentity,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t List *rteperminfos);\nstatic void AppendUniqueIndexColumnsToList(Form_pg_index indexForm, List **uniqueIndexes,\n\t\t\t\t\t\t\t\t\t\t   int flags);\nstatic ConversionChoice GetConversionChoice(ConversionCandidates *\n\t\t\t\t\t\t\t\t\t\t\tconversionCandidates,\n\t\t\t\t\t\t\t\t\t\t\tPlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext);\nstatic bool AllRangeTableEntriesHaveUniqueIndex(List *rangeTableEntryDetailsList);\nstatic bool FirstIsSuperSetOfSecond(List *firstIntList, List *secondIntList);\nstatic void ConvertRTEsToSubquery(List *rangeTableEntryDetailsList,\n\t\t\t\t\t\t\t\t  RecursivePlanningContext *context);\nstatic int ResultRTEIdentity(Query *query);\nstatic List * RTEListToConvert(ConversionCandidates *conversionCandidates,\n\t\t\t\t\t\t\t   ConversionChoice conversionChoice);\n\n\n/*\n * RecursivelyPlanLocalTableJoins gets a query and the planner\n * restrictions. As long as the query is not plannable by router planner,\n * it converts either a local or distributed table to a subquery.\n */\nvoid\nRecursivelyPlanLocalTableJoins(Query *query,\n\t\t\t\t\t\t\t   RecursivePlanningContext *context)\n{\n\tPlannerRestrictionContext *plannerRestrictionContext =\n\t\tGetPlannerRestrictionContext(context);\n\n\tList *rangeTableList = query->rtable;\n\tList *rteperminfos = query->rteperminfos;\n\tint resultRTEIdentity = ResultRTEIdentity(query);\n\tConversionCandidates *conversionCandidates =\n\t\tCreateConversionCandidates(plannerRestrictionContext,\n\t\t\t\t\t\t\t\t   rangeTableList, resultRTEIdentity, rteperminfos);\n\n\tConversionChoice conversionChoise =\n\t\tGetConversionChoice(conversionCandidates, plannerRestrictionContext);\n\n\n\tList *rteListToConvert = RTEListToConvert(conversionCandidates, conversionChoise);\n\tConvertRTEsToSubquery(rteListToConvert, context);\n}\n\n\n/*\n * ResultRTEIdentity returns the result RTE's identity if it exists,\n * otherwise it returns INVALID_RTE_INDENTITY\n */\nstatic int\nResultRTEIdentity(Query *query)\n{\n\tint resultRTEIdentity = INVALID_RTE_IDENTITY;\n\tif (IsModifyCommand(query))\n\t{\n\t\tRangeTblEntry *resultRTE = ExtractResultRelationRTEOrError(query);\n\t\tresultRTEIdentity = GetRTEIdentity(resultRTE);\n\t}\n\treturn resultRTEIdentity;\n}\n\n\n/*\n * RTEListToConvert to converts returns a list of RTEs that should\n * be converted to a subquery.\n */\nstatic List *\nRTEListToConvert(ConversionCandidates *conversionCandidates, ConversionChoice\n\t\t\t\t conversionChoice)\n{\n\tList *rtesToConvert = NIL;\n\tif (conversionChoice == CONVERT_LOCAL_TABLES)\n\t{\n\t\trtesToConvert = list_concat(rtesToConvert, conversionCandidates->localTableList);\n\t}\n\telse\n\t{\n\t\trtesToConvert = list_concat(rtesToConvert,\n\t\t\t\t\t\t\t\t\tconversionCandidates->distributedTableList);\n\t}\n\treturn rtesToConvert;\n}\n\n\n/*\n * GetConversionChoice returns the conversion choice considering the local table\n * join policy.\n */\nstatic ConversionChoice\nGetConversionChoice(ConversionCandidates *conversionCandidates,\n\t\t\t\t\tPlannerRestrictionContext *plannerRestrictionContext)\n{\n\tRangeTableEntryDetails *localRTECandidate = NULL;\n\tRangeTableEntryDetails *distributedRTECandidate = NULL;\n\n\tif (list_length(conversionCandidates->localTableList) > 0)\n\t{\n\t\tlocalRTECandidate = linitial(conversionCandidates->localTableList);\n\t}\n\tif (list_length(conversionCandidates->distributedTableList) > 0)\n\t{\n\t\tdistributedRTECandidate = linitial(conversionCandidates->distributedTableList);\n\t}\n\n\tif (LocalTableJoinPolicy == LOCAL_JOIN_POLICY_PREFER_LOCAL)\n\t{\n\t\treturn localRTECandidate ? CONVERT_LOCAL_TABLES : CONVERT_DISTRIBUTED_TABLES;\n\t}\n\telse if (LocalTableJoinPolicy == LOCAL_JOIN_POLICY_PREFER_DISTRIBUTED)\n\t{\n\t\treturn distributedRTECandidate ? CONVERT_DISTRIBUTED_TABLES :\n\t\t\t   CONVERT_LOCAL_TABLES;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * We want to convert distributed tables only if all the distributed tables\n\t\t * have a constant filter on a unique index, otherwise we would be redundantly\n\t\t * converting a distributed table as we will convert all the other local tables.\n\t\t */\n\t\tbool allRangeTableEntriesHaveUniqueIndex = AllRangeTableEntriesHaveUniqueIndex(\n\t\t\tconversionCandidates->distributedTableList);\n\n\t\tif (allRangeTableEntriesHaveUniqueIndex)\n\t\t{\n\t\t\treturn distributedRTECandidate ? CONVERT_DISTRIBUTED_TABLES :\n\t\t\t\t   CONVERT_LOCAL_TABLES;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn localRTECandidate ? CONVERT_LOCAL_TABLES : CONVERT_DISTRIBUTED_TABLES;\n\t\t}\n\t}\n}\n\n\n/*\n * ConvertRTEsToSubquery converts all the given range table entries\n * to a subquery.\n */\nstatic void\nConvertRTEsToSubquery(List *rangeTableEntryDetailsList, RecursivePlanningContext *context)\n{\n\tRangeTableEntryDetails *rangeTableEntryDetails = NULL;\n\tforeach_declared_ptr(rangeTableEntryDetails, rangeTableEntryDetailsList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = rangeTableEntryDetails->rangeTableEntry;\n\t\tList *requiredAttributeNumbers = rangeTableEntryDetails->requiredAttributeNumbers;\n\t\tReplaceRTERelationWithRteSubquery(rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t  requiredAttributeNumbers, context,\n\t\t\t\t\t\t\t\t\t\t  rangeTableEntryDetails->perminfo);\n\t}\n}\n\n\n/*\n * AllRangeTableEntriesHaveUniqueIndex returns true if all of the RTE's in the given\n * list have a unique index.\n */\nstatic bool\nAllRangeTableEntriesHaveUniqueIndex(List *rangeTableEntryDetailsList)\n{\n\tRangeTableEntryDetails *rangeTableEntryDetails = NULL;\n\tforeach_declared_ptr(rangeTableEntryDetails, rangeTableEntryDetailsList)\n\t{\n\t\tif (!rangeTableEntryDetails->hasConstantFilterOnUniqueColumn)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n\n/*\n * ShouldConvertLocalTableJoinsToSubqueries returns true if we should\n * convert local-dist table joins to subqueries.\n */\nbool\nShouldConvertLocalTableJoinsToSubqueries(List *rangeTableList)\n{\n\tif (LocalTableJoinPolicy == LOCAL_JOIN_POLICY_NEVER)\n\t{\n\t\t/* user doesn't want Citus to enable local table joins */\n\t\treturn false;\n\t}\n\n\tif (!ContainsLocalTableDistributedTableJoin(rangeTableList))\n\t{\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n\n/*\n * HasConstantFilterOnUniqueColumn returns true if the given rangeTableEntry has a constant\n * filter on a unique column.\n */\nstatic bool\nHasConstantFilterOnUniqueColumn(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\tRelationRestriction *relationRestriction)\n{\n\tif (rangeTableEntry == NULL || relationRestriction == NULL)\n\t{\n\t\t/*\n\t\t * Postgres might not pass relationRestriction info with hooks if\n\t\t * the table doesn't contribute to the result, and in that case\n\t\t * relationRestriction will be NULL. Ideally it doesn't make sense\n\t\t * to recursively plan such tables but for the time being we don't\n\t\t * add any special logic for these tables as it might introduce bugs.\n\t\t */\n\t\treturn false;\n\t}\n\n\tbool joinOnFalse = JoinConditionIsOnFalse(relationRestriction->relOptInfo->joininfo);\n\tif (joinOnFalse)\n\t{\n\t\t/* If there is a WHERE FALSE, we consider it as a constant filter. */\n\t\treturn true;\n\t}\n\n\tList *baseRestrictionList = relationRestriction->relOptInfo->baserestrictinfo;\n\tList *restrictClauseList = get_all_actual_clauses(baseRestrictionList);\n\n\tList *rteEqualityColumnsNos =\n\t\tFetchEqualityAttrNumsForRTE((Node *) restrictClauseList);\n\n\tList *uniqueIndexColumnsList = ExecuteFunctionOnEachTableIndex(rangeTableEntry->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   AppendUniqueIndexColumnsToList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   INCLUDE_INDEX_ALL_STATEMENTS);\n\tIndexColumns *indexColumns = NULL;\n\tforeach_declared_ptr(indexColumns, uniqueIndexColumnsList)\n\t{\n\t\tList *uniqueIndexColumnNos = indexColumns->indexColumnNos;\n\t\tif (FirstIsSuperSetOfSecond(rteEqualityColumnsNos,\n\t\t\t\t\t\t\t\t\tuniqueIndexColumnNos))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n\n/*\n * FirstIsSuperSetOfSecond returns true if the first int List\n * contains every element of the second int List.\n */\nstatic bool\nFirstIsSuperSetOfSecond(List *firstIntList, List *secondIntList)\n{\n\tint curInt = 0;\n\tforeach_declared_int(curInt, secondIntList)\n\t{\n\t\tif (!list_member_int(firstIntList, curInt))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n\n/*\n * AppendUniqueIndexColumnsToList adds the given index's column numbers if it is a\n * unique index.\n */\nstatic void\nAppendUniqueIndexColumnsToList(Form_pg_index indexForm, List **uniqueIndexGroups,\n\t\t\t\t\t\t\t   int flags)\n{\n\tif (indexForm->indisunique || indexForm->indisprimary)\n\t{\n\t\tIndexColumns *indexColumns = palloc0(sizeof(IndexColumns));\n\t\tList *uniqueIndexes = NIL;\n\t\tfor (int i = 0; i < indexForm->indkey.dim1; i++)\n\t\t{\n\t\t\tuniqueIndexes = list_append_unique_int(uniqueIndexes,\n\t\t\t\t\t\t\t\t\t\t\t\t   indexForm->indkey.values[i]);\n\t\t}\n\t\tif (list_length(uniqueIndexes) == 0)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t\tindexColumns->indexColumnNos = uniqueIndexes;\n\t\t*uniqueIndexGroups = lappend(*uniqueIndexGroups, indexColumns);\n\t}\n}\n\n\n/*\n * RequiredAttrNumbersForRelation returns the required attribute numbers for\n * the input RTE relation in order for the planning to succeed.\n *\n * The function could be optimized by not adding the columns that only appear\n * WHERE clause as a filter (e.g., not a join clause).\n */\nList *\nRequiredAttrNumbersForRelation(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t   PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tRelationRestriction *relationRestriction =\n\t\tRelationRestrictionForRelation(rangeTableEntry, plannerRestrictionContext);\n\n\tif (relationRestriction == NULL)\n\t{\n\t\treturn NIL;\n\t}\n\n\tPlannerInfo *plannerInfo = relationRestriction->plannerInfo;\n\n\tint rteIndex = relationRestriction->index;\n\n\t/*\n\t * Here we used the query from plannerInfo because it has the optimizations\n\t * so that it doesn't have unnecessary columns. The original query doesn't have\n\t * some of these optimizations hence if we use it here, we don't get the\n\t * 'required' attributes.\n\t */\n\tQuery *queryToProcess = plannerInfo->parse;\n\n\treturn RequiredAttrNumbersForRelationInternal(queryToProcess, rteIndex);\n}\n\n\n/*\n * RequiredAttrNumbersForRelationInternal returns the required attribute numbers\n * for the input range-table-index in the query parameter.\n */\nList *\nRequiredAttrNumbersForRelationInternal(Query *queryToProcess, int rteIndex)\n{\n\tList *allVarsInQuery = pull_vars_of_level((Node *) queryToProcess, 0);\n\n\tList *requiredAttrNumbers = NIL;\n\n\tVar *var = NULL;\n\tforeach_declared_ptr(var, allVarsInQuery)\n\t{\n\t\tif (var->varno == rteIndex)\n\t\t{\n\t\t\trequiredAttrNumbers = list_append_unique_int(requiredAttrNumbers,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t var->varattno);\n\t\t}\n\t}\n\n\treturn requiredAttrNumbers;\n}\n\n\n/*\n * CreateConversionCandidates creates the conversion candidates that might\n * be converted to a subquery so that citus planners can work.\n */\nstatic ConversionCandidates *\nCreateConversionCandidates(PlannerRestrictionContext *plannerRestrictionContext,\n\t\t\t\t\t\t   List *rangeTableList,\n\t\t\t\t\t\t   int resultRTEIdentity,\n\t\t\t\t\t\t   List *rteperminfos)\n{\n\tConversionCandidates *conversionCandidates =\n\t\tpalloc0(sizeof(ConversionCandidates));\n\n\n\tRangeTblEntry *rangeTableEntry = NULL;\n\tforeach_declared_ptr(rangeTableEntry, rangeTableList)\n\t{\n\t\t/* we're only interested in tables */\n\t\tif (!IsRecursivelyPlannableRelation(rangeTableEntry))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tint rteIdentity = GetRTEIdentity(rangeTableEntry);\n\n\t\t/* result relation cannot converted to a subquery */\n\t\tif (resultRTEIdentity == rteIdentity)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tRelationRestriction *relationRestriction =\n\t\t\tRelationRestrictionForRelation(rangeTableEntry, plannerRestrictionContext);\n\n\t\tRangeTableEntryDetails *rangeTableEntryDetails =\n\t\t\tpalloc0(sizeof(RangeTableEntryDetails));\n\n\t\trangeTableEntryDetails->rangeTableEntry = rangeTableEntry;\n\t\trangeTableEntryDetails->requiredAttributeNumbers =\n\t\t\tRequiredAttrNumbersForRelation(rangeTableEntry, plannerRestrictionContext);\n\t\trangeTableEntryDetails->hasConstantFilterOnUniqueColumn =\n\t\t\tHasConstantFilterOnUniqueColumn(rangeTableEntry, relationRestriction);\n\t\trangeTableEntryDetails->perminfo = NULL;\n\t\tif (rangeTableEntry->perminfoindex)\n\t\t{\n\t\t\trangeTableEntryDetails->perminfo = getRTEPermissionInfo(rteperminfos,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trangeTableEntry);\n\t\t}\n\n\t\tbool referenceOrDistributedTable =\n\t\t\tIsCitusTableType(rangeTableEntry->relid, REFERENCE_TABLE) ||\n\t\t\tIsCitusTableType(rangeTableEntry->relid, DISTRIBUTED_TABLE);\n\t\tif (referenceOrDistributedTable)\n\t\t{\n\t\t\tconversionCandidates->distributedTableList =\n\t\t\t\tlappend(conversionCandidates->distributedTableList,\n\t\t\t\t\t\trangeTableEntryDetails);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconversionCandidates->localTableList =\n\t\t\t\tlappend(conversionCandidates->localTableList,\n\t\t\t\t\t\trangeTableEntryDetails);\n\t\t}\n\t}\n\treturn conversionCandidates;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/local_plan_cache.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * local_plan_cache.c\n *\n * Local plan cache related functions\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/local_plan_cache.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/version_compat.h\"\n\n\nstatic Query * GetLocalShardQueryForCache(Query *jobQuery, Task *task,\n\t\t\t\t\t\t\t\t\t\t  ParamListInfo paramListInfo);\nstatic char * DeparseLocalShardQuery(Query *jobQuery, List *relationShardList,\n\t\t\t\t\t\t\t\t\t Oid anchorDistributedTableId, int64 anchorShardId);\nstatic int ExtractParameterTypesForParamListInfo(ParamListInfo originalParamListInfo,\n\t\t\t\t\t\t\t\t\t\t\t\t Oid **parameterTypes);\n\n/*\n * CacheLocalPlanForShardQuery replaces the relation OIDs in the job query\n * with shard relation OIDs and then plans the query and caches the result\n * in the originalDistributedPlan (which may be preserved across executions).\n */\nvoid\nCacheLocalPlanForShardQuery(Task *task, DistributedPlan *originalDistributedPlan,\n\t\t\t\t\t\t\tParamListInfo paramListInfo)\n{\n\tPlannedStmt *localPlan = GetCachedLocalPlan(task, originalDistributedPlan);\n\tif (localPlan != NULL)\n\t{\n\t\t/* we already have a local plan */\n\t\treturn;\n\t}\n\n\tif (list_length(task->relationShardList) == 0)\n\t{\n\t\t/* zero shard plan, no need to cache */\n\t\treturn;\n\t}\n\n\t/*\n\t * All memory allocations should happen in the plan's context\n\t * since we'll cache the local plan there.\n\t */\n\tMemoryContext oldContext =\n\t\tMemoryContextSwitchTo(GetMemoryChunkContext(originalDistributedPlan));\n\n\t/*\n\t * We prefer to use jobQuery (over task->query) because we don't want any\n\t * functions/params to have been evaluated in the cached plan.\n\t */\n\tQuery *jobQuery = copyObject(originalDistributedPlan->workerJob->jobQuery);\n\n\tQuery *localShardQuery = GetLocalShardQueryForCache(jobQuery, task, paramListInfo);\n\n\tLOCKMODE lockMode = GetQueryLockMode(localShardQuery);\n\n\t/* fast path queries can only have a single RTE by definition */\n\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) linitial(localShardQuery->rtable);\n\n\t/*\n\t * If the shard has been created in this transction, we wouldn't see the relationId\n\t * for it, so do not cache.\n\t */\n\tif (rangeTableEntry->relid == InvalidOid)\n\t{\n\t\tpfree(jobQuery);\n\t\tpfree(localShardQuery);\n\t\tMemoryContextSwitchTo(oldContext);\n\t\treturn;\n\t}\n\n\tLockRelationOid(rangeTableEntry->relid, lockMode);\n\n\tLocalPlannedStatement *localPlannedStatement = CitusMakeNode(LocalPlannedStatement);\n\tlocalPlan = planner(localShardQuery, NULL, 0, NULL);\n\tlocalPlannedStatement->localPlan = localPlan;\n\tlocalPlannedStatement->shardId = task->anchorShardId;\n\tlocalPlannedStatement->localGroupId = GetLocalGroupId();\n\n\toriginalDistributedPlan->workerJob->localPlannedStatements =\n\t\tlappend(originalDistributedPlan->workerJob->localPlannedStatements,\n\t\t\t\tlocalPlannedStatement);\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * GetLocalShardQueryForCache is a helper function which generates\n * the local shard query based on the jobQuery. The function should\n * not be used for generic purposes, it is specialized for local cached\n * queries.\n *\n * It is not guaranteed to have consistent attribute numbers on the shards\n * and on the shell (e.g., distributed/reference tables) due to DROP COLUMN\n * commands.\n *\n * To avoid any edge cases due to such discrepancies, we first deparse the\n * jobQuery with the tables replaced to shards, and parse the query string\n * back. This is normally a very expensive operation, however we only do it\n * once per cached local plan, which is acceptable.\n */\nstatic Query *\nGetLocalShardQueryForCache(Query *jobQuery, Task *task, ParamListInfo orig_paramListInfo)\n{\n\tchar *shardQueryString =\n\t\tDeparseLocalShardQuery(jobQuery, task->relationShardList,\n\t\t\t\t\t\t\t   task->anchorDistributedTableId,\n\t\t\t\t\t\t\t   task->anchorShardId);\n\tereport(DEBUG5, (errmsg(\"Local shard query that is going to be cached: %s\",\n\t\t\t\t\t\t\tshardQueryString)));\n\n\tOid *parameterTypes = NULL;\n\tint numberOfParameters =\n\t\tExtractParameterTypesForParamListInfo(orig_paramListInfo, &parameterTypes);\n\n\tQuery *localShardQuery =\n\t\tParseQueryString(shardQueryString, parameterTypes, numberOfParameters);\n\n\treturn localShardQuery;\n}\n\n\n/*\n * DeparseLocalShardQuery is a helper function to deparse given jobQuery for the shard(s)\n * identified by the relationShardList, anchorDistributedTableId and anchorShardId.\n *\n * For the details and comparison with TaskQueryString(), see the comments in the function.\n */\nstatic char *\nDeparseLocalShardQuery(Query *jobQuery, List *relationShardList, Oid\n\t\t\t\t\t   anchorDistributedTableId, int64 anchorShardId)\n{\n\tStringInfo queryString = makeStringInfo();\n\n\t/*\n\t * We imitate what TaskQueryString() does, but we cannot rely on that function\n\t * as the parameters might have been already resolved on the QueryTree in the\n\t * task. Instead, we operate on the jobQuery where are sure that the\n\t * coordination evaluation has not happened.\n\t *\n\t * Local shard queries are only applicable for local cached query execution.\n\t * In the local cached query execution mode, we can use a query structure\n\t * (or query string) with unevaluated expressions as we allow function calls\n\t * to be evaluated when the query on the shard is executed (e.g., do no have\n\t * coordinator evaluation, instead let Postgres executor evaluate values).\n\t *\n\t * Additionally, we can allow them to be evaluated again because they are stable,\n\t * and we do not cache plans / use unevaluated query strings for queries containing\n\t * volatile functions.\n\t */\n\tif (jobQuery->commandType == CMD_INSERT)\n\t{\n\t\t/*\n\t\t * We currently do not support INSERT .. SELECT here. To support INSERT..SELECT\n\t\t * queries, we should update the relation names to shard names in the SELECT\n\t\t * clause (e.g., UpdateRelationToShardNames()).\n\t\t */\n\t\tAssert(!CheckInsertSelectQuery(jobQuery));\n\n\t\tAddInsertAliasIfNeeded(jobQuery);\n\n\t\t/*\n\t\t * For INSERT queries we cannot use pg_get_query_def. Mainly because we\n\t\t * cannot run UpdateRelationToShardNames on an INSERT query. This is\n\t\t * because the PG deparsing logic fails when trying to insert into a\n\t\t * RTE_FUNCTION (which is what will happen if you call\n\t\t * UpdateRelationToShardNames).\n\t\t */\n\t\tdeparse_shard_query(jobQuery, anchorDistributedTableId, anchorShardId,\n\t\t\t\t\t\t\tqueryString);\n\t}\n\telse\n\t{\n\t\tUpdateRelationToShardNames((Node *) jobQuery, relationShardList);\n\n\t\tpg_get_query_def(jobQuery, queryString);\n\t}\n\n\treturn queryString->data;\n}\n\n\n/*\n * ExtractParameterTypesForParamListInfo is a helper function which helps to\n * extract the parameter types of the given ParamListInfo via the second\n * parameter of the function.\n *\n * The function also returns the number of parameters. If no parameter exists,\n * the function returns 0.\n */\nstatic int\nExtractParameterTypesForParamListInfo(ParamListInfo originalParamListInfo,\n\t\t\t\t\t\t\t\t\t  Oid **parameterTypes)\n{\n\t*parameterTypes = NULL;\n\n\tint numberOfParameters = 0;\n\tif (originalParamListInfo != NULL)\n\t{\n\t\tconst char **parameterValues = NULL;\n\t\tParamListInfo paramListInfo = copyParamList(originalParamListInfo);\n\t\tExtractParametersForLocalExecution(paramListInfo, parameterTypes,\n\t\t\t\t\t\t\t\t\t\t   &parameterValues);\n\t\tnumberOfParameters = paramListInfo->numParams;\n\t}\n\n\treturn numberOfParameters;\n}\n\n\n/*\n * GetCachedLocalPlan is a helper function which return the cached\n * plan in the distributedPlan for the given task if exists.\n *\n * Otherwise, the function returns NULL.\n */\nPlannedStmt *\nGetCachedLocalPlan(Task *task, DistributedPlan *distributedPlan)\n{\n\tif (distributedPlan == NULL || distributedPlan->workerJob == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (list_length(distributedPlan->workerJob->taskList) != 1)\n\t{\n\t\t/* we only support plan caching for single shard queries */\n\t\treturn NULL;\n\t}\n\n\tList *cachedPlanList = distributedPlan->workerJob->localPlannedStatements;\n\tLocalPlannedStatement *localPlannedStatement = NULL;\n\n\tint32 localGroupId = GetLocalGroupId();\n\n\tforeach_declared_ptr(localPlannedStatement, cachedPlanList)\n\t{\n\t\tif (localPlannedStatement->shardId == task->anchorShardId &&\n\t\t\tlocalPlannedStatement->localGroupId == localGroupId)\n\t\t{\n\t\t\t/* already have a cached plan, no need to continue */\n\t\t\treturn localPlannedStatement->localPlan;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * IsLocalPlanCachingSupported returns whether (part of) the task can be planned\n * and executed locally and whether caching is supported (single shard, no volatile\n * functions).\n */\nbool\nIsLocalPlanCachingSupported(Job *currentJob, DistributedPlan *originalDistributedPlan)\n{\n\tif (originalDistributedPlan->numberOfTimesExecuted < 1)\n\t{\n\t\t/*\n\t\t * Only cache if a plan is being reused (via a prepared statement).\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (!currentJob->deferredPruning)\n\t{\n\t\t/*\n\t\t * When not using deferred pruning we may have already replaced distributed\n\t\t * table RTEs with citus_extradata_container RTEs to pass the shard ID to the\n\t\t * deparser. In that case, we cannot pass the query tree directly to the\n\t\t * planner.\n\t\t *\n\t\t * If desired, we can relax this check by improving the implementation of\n\t\t * CacheLocalPlanForShardQuery to translate citus_extradata_container\n\t\t * to a shard relation OID.\n\t\t */\n\t\treturn false;\n\t}\n\n\tList *taskList = currentJob->taskList;\n\tif (list_length(taskList) != 1)\n\t{\n\t\t/* we only support plan caching for single shard queries */\n\t\treturn false;\n\t}\n\n\tTask *task = linitial(taskList);\n\tif (!TaskAccessesLocalNode(task))\n\t{\n\t\t/* not a local task */\n\t\treturn false;\n\t}\n\n\tif (!EnableLocalExecution)\n\t{\n\t\t/* user requested not to use local execution */\n\t\treturn false;\n\t}\n\n\tif (GetCurrentLocalExecutionStatus() == LOCAL_EXECUTION_DISABLED)\n\t{\n\t\t/* transaction already connected to localhost */\n\t\treturn false;\n\t}\n\n\tQuery *originalJobQuery = originalDistributedPlan->workerJob->jobQuery;\n\tif (contain_volatile_functions((Node *) originalJobQuery))\n\t{\n\t\t/*\n\t\t * We do not cache plans with volatile functions in the query.\n\t\t *\n\t\t * The reason we care about volatile functions is primarily that we\n\t\t * already executed them in ExecuteCoordinatorEvaluableExpressions\n\t\t * and since we're falling back to the original query tree here we would\n\t\t * execute them again if we execute the plan.\n\t\t */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/merge_planner.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * merge_planner.c\n *\n * This file contains functions to help plan MERGE queries.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <stddef.h>\n\n#include \"postgres.h\"\n\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"optimizer/optimizer.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parsetree.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_clauses.h\"\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_distributed_join_planner.h\"\n#include \"distributed/merge_planner.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/pg_dist_node_metadata.h\"\n#include \"distributed/query_colocation_checker.h\"\n#include \"distributed/query_pushdown_planning.h\"\n#include \"distributed/repartition_executor.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/shared_library_init.h\"\n\nstatic int SourceResultPartitionColumnIndex(Query *mergeQuery,\n\t\t\t\t\t\t\t\t\t\t\tList *sourceTargetList,\n\t\t\t\t\t\t\t\t\t\t\tCitusTableCacheEntry *targetRelation);\nstatic int FindTargetListEntryWithVarExprAttno(List *targetList, AttrNumber varattno);\nstatic Var * ValidateAndReturnVarIfSupported(Node *entryExpr);\nstatic DeferredErrorMessage * DeferErrorIfTargetHasFalseClause(Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nstatic void ErrorIfMergeQueryQualAndTargetListNotSupported(Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   Query *originalQuery);\nstatic void ErrorIfMergeNotSupported(Query *query, Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t List *rangeTableList);\nstatic void ErrorIfMergeHasUnsupportedTables(Oid targetRelationId, List *rangeTableList);\nstatic bool IsDistributionColumnInMergeSource(Expr *columnExpression, Query *query, bool\n\t\t\t\t\t\t\t\t\t\t\t  skipOuterVars);\nstatic DeferredErrorMessage * DeferErrorIfRoutableMergeNotSupported(Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPlannerRestrictionContext\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t*\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOid targetRelationId);\nstatic bool MergeSourceHasRouterSelect(Query *query,\n\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nstatic DeferredErrorMessage * MergeQualAndTargetListFunctionsSupported(Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   resultRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   Node *quals,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   List *targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   CmdType\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   commandType);\n\nstatic DistributedPlan * CreateRouterMergePlan(Oid targetRelationId, Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t   Query *query,\n\t\t\t\t\t\t\t\t\t\t\t   List *rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nstatic void ErrorIfRepartitionMergeNotSupported(Oid targetRelationId, Query *mergeQuery,\n\t\t\t\t\t\t\t\t\t\t\t\tQuery *sourceQuery);\nstatic void ConvertSourceRTEIntoSubquery(Query *mergeQuery,\n\t\t\t\t\t\t\t\t\t\t RangeTblEntry *sourceRte,\n\t\t\t\t\t\t\t\t\t\t PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\nstatic void ConvertSubqueryRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte);\nstatic void ConvertCteRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte);\nstatic void ConvertRelationRTEIntoSubquery(Query *mergeQuery,\n\t\t\t\t\t\t\t\t\t\t   RangeTblEntry *sourceRte,\n\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nstatic void ErrorIfUnsupportedCTEs(Query *query);\nstatic void ContainsUnsupportedCTEs(Query *query);\nstatic bool MergeQueryCTEWalker(Node *node, void *context);\nstatic DistributedPlan * CreateNonPushableMergePlan(Oid targetRelationId, uint64 planId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tQuery *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\tQuery *query,\n\t\t\t\t\t\t\t\t\t\t\t\t\tPlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\tParamListInfo boundParams);\nstatic char * MergeCommandResultIdPrefix(uint64 planId);\nstatic void ErrorIfMergeHasReturningList(Query *query);\nstatic Node * GetMergeJoinCondition(Query *mergeQuery);\n\n\n/*\n * CreateMergePlan\n * 1) Check for conditions that are not supported in MERGE command.\n * 2) Try to create a pushable plan\n *    - Check for conditions suitable for a routable plan, if not found,\n *      raise deferred error\n * 3) Try to create repartition and redistribution plan\n *    - Check for conditions that prevent repartition strategy, if found,\n *      raise an exception and quit.\n */\nDistributedPlan *\nCreateMergePlan(uint64 planId, Query *originalQuery, Query *query,\n\t\t\t\tPlannerRestrictionContext *plannerRestrictionContext,\n\t\t\t\tParamListInfo boundParams)\n{\n\tOid targetRelationId = ModifyQueryResultRelationId(originalQuery);\n\n\t/*\n\t * Step 1: Look for definitive error conditions applicable to both Routable\n\t * and Repartition strategies.\n\t */\n\tList *rangeTableList = ExtractRangeTableEntryList(originalQuery);\n\tErrorIfMergeNotSupported(originalQuery, targetRelationId, rangeTableList);\n\n\t/* Step 2: Try pushable merge plan */\n\tDistributedPlan *distributedPlan =\n\t\tCreateRouterMergePlan(targetRelationId, originalQuery, query,\n\t\t\t\t\t\t\t  rangeTableList, plannerRestrictionContext);\n\n\t/* Step 3: If the routing plan failed, try for repartition strategy */\n\tif (distributedPlan->planningError != NULL)\n\t{\n\t\tRaiseDeferredError(distributedPlan->planningError, DEBUG1);\n\n\t\t/* If MERGE is not routable, try repartitioning  */\n\t\tdistributedPlan =\n\t\t\tCreateNonPushableMergePlan(targetRelationId, planId,\n\t\t\t\t\t\t\t\t\t   originalQuery, query,\n\t\t\t\t\t\t\t\t\t   plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t   boundParams);\n\t}\n\n\treturn distributedPlan;\n}\n\n\n/*\n * GetMergeJoinTree constructs and returns the jointree for a MERGE query.\n */\nFromExpr *\nGetMergeJoinTree(Query *mergeQuery)\n{\n\tFromExpr *mergeJointree = NULL;\n#if PG_VERSION_NUM >= PG_VERSION_17\n\n\t/*\n\t * In Postgres 17, the query tree has a specific field for the merge condition.\n\t * For deriving the WhereClauseList from the merge condition, we construct a dummy\n\t * jointree with an empty fromlist. This works because the fromlist of a merge query\n\t * join tree consists of range table references only, and range table references are\n\t * disregarded by the WhereClauseList() walker.\n\t * Relevant PG17 commit: 0294df2f1\n\t */\n\tmergeJointree = makeFromExpr(NIL, mergeQuery->mergeJoinCondition);\n#else\n\tmergeJointree = mergeQuery->jointree;\n#endif\n\n\treturn mergeJointree;\n}\n\n\n/*\n * GetMergeJoinCondition returns the quals of the ON condition\n */\nstatic Node *\nGetMergeJoinCondition(Query *mergeQuery)\n{\n\tNode *joinCondition = NULL;\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tjoinCondition = (Node *) mergeQuery->mergeJoinCondition;\n#else\n\tjoinCondition = (Node *) mergeQuery->jointree->quals;\n#endif\n\treturn joinCondition;\n}\n\n\n/*\n * CreateRouterMergePlan attempts to create a pushable plan for the given MERGE\n * SQL statement. If the planning fails, the ->planningError is set to a description\n * of the failure.\n */\nstatic DistributedPlan *\nCreateRouterMergePlan(Oid targetRelationId, Query *originalQuery, Query *query,\n\t\t\t\t\t  List *rangeTableList,\n\t\t\t\t\t  PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tDistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan);\n\n\tAssert(originalQuery->commandType == CMD_MERGE);\n\tAssert(OidIsValid(targetRelationId));\n\n\tdistributedPlan->planningError = DeferErrorIfRoutableMergeNotSupported(originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   targetRelationId);\n\tif (distributedPlan->planningError != NULL)\n\t{\n\t\treturn distributedPlan;\n\t}\n\n\n\tJob *job = RouterJob(originalQuery, plannerRestrictionContext,\n\t\t\t\t\t\t &distributedPlan->planningError);\n\n\tif (distributedPlan->planningError != NULL)\n\t{\n\t\treturn distributedPlan;\n\t}\n\n\tereport(DEBUG1, (errmsg(\"Creating MERGE router plan\")));\n\n\tdistributedPlan->workerJob = job;\n\tdistributedPlan->targetRelationId = targetRelationId;\n\tdistributedPlan->modLevel = RowModifyLevelForQuery(query);\n\n\t/* There is no coordinator query for MERGE */\n\tdistributedPlan->combineQuery = NULL;\n\n\t/* MERGE doesn't support RETURNING clause */\n\tdistributedPlan->expectResults = false;\n\tdistributedPlan->fastPathRouterPlan =\n\t\tplannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery;\n\n\treturn distributedPlan;\n}\n\n\n/*\n * CreateNonPushableMergePlan comes into effect if the router planning fails\n * and incorporates two planning strategies.\n *\n * ExecuteSourceAtWorkerAndRepartition(): Plan the source query independently,\n * execute the results into intermediate files, and repartition the files to\n * co-locate them with the merge-target table. Subsequently, compile a final\n * merge query on the target table using the intermediate results as the data\n * source.\n *\n * ExecuteSourceAtCoordAndRedistribution(): Execute the plan that requires\n * evaluation at the coordinator, run the query on the coordinator, and\n * redistribute the resulting rows to ensure colocation with the target shards.\n * Direct the MERGE SQL operation to the worker nodes' target shards, using the\n * intermediate files colocated with the data as the data source.\n */\nstatic DistributedPlan *\nCreateNonPushableMergePlan(Oid targetRelationId, uint64 planId, Query *originalQuery,\n\t\t\t\t\t\t   Query *query,\n\t\t\t\t\t\t   PlannerRestrictionContext *plannerRestrictionContext,\n\t\t\t\t\t\t   ParamListInfo boundParams)\n{\n\tQuery *mergeQuery = copyObject(originalQuery);\n\tRangeTblEntry *sourceRte = ExtractMergeSourceRangeTableEntry(mergeQuery, false);\n\tDistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan);\n\n\tereport(DEBUG1, (errmsg(\"Creating MERGE repartition plan\")));\n\tConvertSourceRTEIntoSubquery(mergeQuery, sourceRte, plannerRestrictionContext);\n\tQuery *sourceQuery = sourceRte->subquery;\n\n\tErrorIfRepartitionMergeNotSupported(targetRelationId, mergeQuery, sourceQuery);\n\n\tCitusTableCacheEntry *targetRelation = GetCitusTableCacheEntry(targetRelationId);\n\n\n\tif (IsCitusTableType(targetRelation->relationId, SINGLE_SHARD_DISTRIBUTED))\n\t{\n\t\t/*\n\t\t * if target table is SINGLE_SHARD_DISTRIBUTED let's set this to invalid -1\n\t\t * so later in execution phase we don't rely on this value and try to find single shard of target instead.\n\t\t */\n\t\tdistributedPlan->sourceResultRepartitionColumnIndex = -1;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * Get the index of the column in the source query that will be utilized\n\t\t * to repartition the source rows, ensuring colocation with the target\n\t\t */\n\n\t\tdistributedPlan->sourceResultRepartitionColumnIndex =\n\t\t\tSourceResultPartitionColumnIndex(mergeQuery,\n\t\t\t\t\t\t\t\t\t\t\t sourceQuery->targetList,\n\t\t\t\t\t\t\t\t\t\t\t targetRelation);\n\t}\n\n\t/*\n\t * Make a copy of the source query, since following code scribbles it\n\t * but we need to keep the original for EXPLAIN.\n\t */\n\tQuery *sourceQueryCopy = copyObject(sourceQuery);\n\n\t/* plan the subquery, this may be another distributed query */\n\tint cursorOptions = CURSOR_OPT_PARALLEL_OK;\n\tPlannedStmt *sourceRowsPlan = pg_plan_query(sourceQueryCopy, NULL, cursorOptions,\n\t\t\t\t\t\t\t\t\t\t\t\tboundParams);\n\tbool isRepartitionAllowed = IsRedistributablePlan(sourceRowsPlan->planTree) &&\n\t\t\t\t\t\t\t\tIsSupportedRedistributionTarget(targetRelationId);\n\n\t/* If plan is distributed, no work at the coordinator */\n\tif (isRepartitionAllowed)\n\t{\n\t\tdistributedPlan->modifyWithSelectMethod = MODIFY_WITH_SELECT_REPARTITION;\n\t}\n\telse\n\t{\n\t\tdistributedPlan->modifyWithSelectMethod = MODIFY_WITH_SELECT_VIA_COORDINATOR;\n\t}\n\n\t/* There is no coordinator query for MERGE */\n\tdistributedPlan->combineQuery = NULL;\n\n\t/* MERGE doesn't support RETURNING clause */\n\tdistributedPlan->expectResults = false;\n\n\tdistributedPlan->modLevel = RowModifyLevelForQuery(mergeQuery);\n\tdistributedPlan->targetRelationId = targetRelationId;\n\tdistributedPlan->intermediateResultIdPrefix = MergeCommandResultIdPrefix(planId);\n\tdistributedPlan->modifyQueryViaCoordinatorOrRepartition = mergeQuery;\n\tdistributedPlan->selectPlanForModifyViaCoordinatorOrRepartition = sourceRowsPlan;\n\tdistributedPlan->fastPathRouterPlan =\n\t\tplannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery;\n\n\treturn distributedPlan;\n}\n\n\n/*\n * ContainsUnsupportedCTEs checks the CTE if it's modifying or recursive CTE, if true,\n * raises an exception.\n */\nstatic void\nContainsUnsupportedCTEs(Query *query)\n{\n\tif (query->hasModifyingCTE)\n\t{\n\t\tereport(ERROR, (errmsg(\"CTEs with modifying actions are not yet \"\n\t\t\t\t\t\t\t   \"supported in MERGE\")));\n\t}\n\n\tif (query->hasRecursive)\n\t{\n\t\tereport(ERROR, (errmsg(\"Recursive CTEs are not yet \"\n\t\t\t\t\t\t\t   \"supported in MERGE\")));\n\t}\n}\n\n\n/*\n * MergeQueryCTEWalker descends into the MERGE query to check for any subqueries\n */\nstatic bool\nMergeQueryCTEWalker(Node *node, void *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\n\t\tContainsUnsupportedCTEs(query);\n\n\t\tquery_tree_walker(query, MergeQueryCTEWalker, NULL, 0);\n\n\t\t/* we're done, no need to recurse anymore for this query */\n\t\treturn false;\n\t}\n\n\treturn expression_tree_walker(node, MergeQueryCTEWalker, context);\n}\n\n\n/*\n * ErrorIfUnsupportedCTEs checks for unsupported CTEs, such as, modifying and recursive\n */\nstatic void\nErrorIfUnsupportedCTEs(Query *query)\n{\n\tContainsUnsupportedCTEs(query);\n\tquery_tree_walker(query, MergeQueryCTEWalker, NULL, 0);\n}\n\n\n/*\n * ErrorIfMergeHasUnsupportedTables checks if all the tables(target, source or any CTE\n * present) in the MERGE command are local i.e. a combination of Citus local and Non-Citus\n * tables (regular Postgres tables), or distributed tables with some restrictions\n * raises an exception for all other combinations.\n */\nstatic void\nErrorIfMergeHasUnsupportedTables(Oid targetRelationId, List *rangeTableList)\n{\n\tRangeTblEntry *rangeTableEntry = NULL;\n\tforeach_declared_ptr(rangeTableEntry, rangeTableList)\n\t{\n\t\tOid relationId = rangeTableEntry->relid;\n\n\t\tswitch (rangeTableEntry->rtekind)\n\t\t{\n\t\t\tcase RTE_RELATION:\n\t\t\t{\n\t\t\t\t/* Check the relation type */\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase RTE_SUBQUERY:\n\t\t\tcase RTE_FUNCTION:\n\t\t\tcase RTE_TABLEFUNC:\n\t\t\tcase RTE_VALUES:\n\t\t\tcase RTE_JOIN:\n\t\t\tcase RTE_CTE:\n#if PG_VERSION_NUM >= PG_VERSION_18\n\t\t\tcase RTE_GROUP:\n#endif\n\t\t\t{\n\t\t\t\t/* Skip them as base table(s) will be checked */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * RTE_NAMEDTUPLESTORE is typically used in ephmeral named relations,\n\t\t\t * such as, trigger data; until we find a genuine use case, raise an\n\t\t\t * exception.\n\t\t\t * RTE_RESULT is a node added by the planner and we shouldn't\n\t\t\t * encounter it in the parse tree.\n\t\t\t */\n\t\t\tcase RTE_NAMEDTUPLESTORE:\n\t\t\tcase RTE_RESULT:\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\terrmsg(\"MERGE command is not supported with \"\n\t\t\t\t\t\t\t\t\t   \"Tuplestores and results\")));\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\t\"MERGE command: Unrecognized range table entry(%d) \",\n\t\t\t\t\t\t\t\t\trangeTableEntry->rtekind)));\n\t\t\t}\n\t\t}\n\n\t\t/* RTE Relation can be of various types, check them now */\n\t\tswitch (rangeTableEntry->relkind)\n\t\t{\n\t\t\t/* skip the regular views as they are replaced with subqueries */\n\t\t\tcase RELKIND_VIEW:\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcase RELKIND_MATVIEW:\n\t\t\tcase RELKIND_FOREIGN_TABLE:\n\t\t\t{\n\t\t\t\t/* These two cases as a target is not allowed */\n\t\t\t\tif (relationId == targetRelationId)\n\t\t\t\t{\n\t\t\t\t\t/* Usually we don't reach this exception as the Postgres parser catches it */\n\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\t\terrmsg(\"MERGE command is not allowed on \"\n\t\t\t\t\t\t\t\t\t\t   \"relation type(relkind:%c)\",\n\t\t\t\t\t\t\t\t\t\t   rangeTableEntry->relkind)));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase RELKIND_RELATION:\n\t\t\tcase RELKIND_PARTITIONED_TABLE:\n\t\t\t{\n\t\t\t\t/* Check for citus/postgres table types */\n\t\t\t\tAssert(OidIsValid(relationId));\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\terrmsg(\"Unexpected table type(relkind:%c) \"\n\t\t\t\t\t\t\t\t\t   \"in MERGE command\", rangeTableEntry->relkind)));\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Check for unsupported distributed tables\n\t\t */\n\t\tif (extern_IsColumnarTableAmTable(relationId) &&\n\t\t\trelationId == targetRelationId)\n\t\t{\n\t\t\t/* Columnar tables are not supported */\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"Columnar table as target is \"\n\t\t\t\t\t\t\t\t   \"not allowed in MERGE command\")));\n\t\t}\n\t\telse if (IsCitusTableType(relationId, DISTRIBUTED_TABLE))\n\t\t{\n\t\t\t/* Append/Range distributed tables are not supported */\n\t\t\tif (IsCitusTableType(relationId, APPEND_DISTRIBUTED) ||\n\t\t\t\tIsCitusTableType(relationId, RANGE_DISTRIBUTED))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\terrmsg(\"For MERGE command, append/range distribution \"\n\t\t\t\t\t\t\t\t\t   \"table is not supported yet\")));\n\t\t\t}\n\t\t}\n\t\telse if (IsCitusTableType(relationId, REFERENCE_TABLE) &&\n\t\t\t\t relationId == targetRelationId)\n\t\t{\n\t\t\t/* Reference table as a target is not allowed */\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"Reference table as target is \"\n\t\t\t\t\t\t\t\t   \"not allowed in MERGE command\")));\n\t\t}\n\t\telse if (IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t\t{\n\t\t\t/*\n\t\t\t * All the tables are local/reference, supported as long as\n\t\t\t * coordinator is in the metadata.\n\t\t\t */\n\t\t\tif (FindCoordinatorNodeId() == -1)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"Coordinator node is not in \"\n\t\t\t\t\t\t\t\t\t   \"the metadata\"),\n\t\t\t\t\t\t\t\terrhint(\"To ensure that the distributed planner \"\n\t\t\t\t\t\t\t\t\t\t\"planner the Citus table, please consider \"\n\t\t\t\t\t\t\t\t\t\t\"configuring a coordinator node\")));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * IsPartitionColumnInMerge returns true if the given column is a partition column.\n * The function uses FindReferencedTableColumn to find the original relation\n * id and column that the column expression refers to. It then checks whether\n * that column is a partition column of the relation.\n *\n * Also, the function returns always false for reference tables given that\n * reference tables do not have partition column.\n *\n * If skipOuterVars is true, then it doesn't process the outervars.\n */\nbool\nIsDistributionColumnInMergeSource(Expr *columnExpression, Query *query, bool\n\t\t\t\t\t\t\t\t  skipOuterVars)\n{\n\tbool isDistributionColumn = false;\n\tVar *column = NULL;\n\tRangeTblEntry *relationRTE = NULL;\n\n\t/* ParentQueryList is same as the original query for MERGE */\n\tFindReferencedTableColumn(columnExpression, list_make1(query), query, &column,\n\t\t\t\t\t\t\t  &relationRTE,\n\t\t\t\t\t\t\t  skipOuterVars);\n\tOid relationId = relationRTE ? relationRTE->relid : InvalidOid;\n\tif (relationId != InvalidOid && column != NULL)\n\t{\n\t\tVar *distributionColumn = DistPartitionKey(relationId);\n\n\t\t/* not all distributed tables have partition column */\n\t\tif (distributionColumn != NULL &&\n\t\t\tcolumn->varattno == distributionColumn->varattno)\n\t\t{\n\t\t\tisDistributionColumn = true;\n\t\t}\n\t}\n\n\treturn isDistributionColumn;\n}\n\n\n/*\n * MergeQualAndTargetListFunctionsSupported Checks WHEN/ON clause actions to see what functions\n * are allowed, if we are updating distribution column, etc.\n */\nstatic DeferredErrorMessage *\nMergeQualAndTargetListFunctionsSupported(Oid resultRelationId, Query *query,\n\t\t\t\t\t\t\t\t\t\t Node *quals,\n\t\t\t\t\t\t\t\t\t\t List *targetList, CmdType commandType)\n{\n\tuint32 targetRangeTableIndex = query->resultRelation;\n\tFromExpr *joinTree = GetMergeJoinTree(query);\n\tVar *distributionColumn = NULL;\n\tif (IsCitusTable(resultRelationId) && HasDistributionKey(resultRelationId))\n\t{\n\t\tdistributionColumn = PartitionColumn(resultRelationId, targetRangeTableIndex);\n\t}\n\n\tListCell *targetEntryCell = NULL;\n\tbool hasVarArgument = false; /* A STABLE function is passed a Var argument */\n\tbool hasBadCoalesce = false; /* CASE/COALESCE passed a mutable function */\n\tforeach(targetEntryCell, targetList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell);\n\n\t\tbool targetEntryDistributionColumn = false;\n\t\tAttrNumber targetColumnAttrNumber = InvalidAttrNumber;\n\n\t\tif (distributionColumn)\n\t\t{\n\t\t\tif (commandType == CMD_UPDATE)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Note that it is not possible to give an alias to\n\t\t\t\t * UPDATE table SET ...\n\t\t\t\t */\n\t\t\t\tif (targetEntry->resname)\n\t\t\t\t{\n\t\t\t\t\ttargetColumnAttrNumber = get_attnum(resultRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetEntry->resname);\n\t\t\t\t\tif (targetColumnAttrNumber == distributionColumn->varattno)\n\t\t\t\t\t{\n\t\t\t\t\t\ttargetEntryDistributionColumn = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * joinTree->quals, retrieved by GetMergeJoinTree() - either from\n\t\t * mergeJoinCondition (PG >= 17) or jointree->quals (PG < 17),\n\t\t * only contains the quals that present in \"ON (..)\" clause. Action\n\t\t * quals that can be specified for each specific action, as in\n\t\t * \"WHEN <match condition> AND <action quals> THEN <action>\"\", are\n\t\t * saved into \"qual\" field of the corresponding action's entry in\n\t\t * mergeActionList, see\n\t\t * https://github.com/postgres/postgres/blob/e6da68a6e1d60a037b63a9c9ed36e5ef0a996769/src/backend/parser/parse_merge.c#L285-L293.\n\t\t *\n\t\t * For this reason, even if TargetEntryChangesValue() could prove that\n\t\t * an action's quals ensure that the action cannot change the distribution\n\t\t * key, this is not the case as we don't provide action quals to\n\t\t * TargetEntryChangesValue(), but just joinTree, which only contains\n\t\t * the \"ON (..)\" clause quals.\n\t\t */\n\t\tif (targetEntryDistributionColumn &&\n\t\t\tTargetEntryChangesValue(targetEntry, distributionColumn, joinTree))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"updating the distribution column is not \"\n\t\t\t\t\t\t\t\t \"allowed in MERGE actions\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\n\t\tif (FindNodeMatchingCheckFunction((Node *) targetEntry->expr,\n\t\t\t\t\t\t\t\t\t\t  CitusIsVolatileFunction))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"functions used in MERGE actions on distributed \"\n\t\t\t\t\t\t\t\t \"tables must not be VOLATILE\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\n\t\tif (MasterIrreducibleExpression((Node *) targetEntry->expr,\n\t\t\t\t\t\t\t\t\t\t&hasVarArgument, &hasBadCoalesce))\n\t\t{\n\t\t\tAssert(hasVarArgument || hasBadCoalesce);\n\t\t}\n\n\t\tif (FindNodeMatchingCheckFunction((Node *) targetEntry->expr,\n\t\t\t\t\t\t\t\t\t\t  NodeIsFieldStore))\n\t\t{\n\t\t\t/* DELETE cannot do field indirection already */\n\t\t\tAssert(commandType == CMD_UPDATE || commandType == CMD_INSERT);\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"inserting or modifying composite type fields is not \"\n\t\t\t\t\t\t\t\t \"supported\", NULL,\n\t\t\t\t\t\t\t\t \"Use the column name to insert or update the composite \"\n\t\t\t\t\t\t\t\t \"type as a single value\");\n\t\t}\n\t}\n\n\n\t/*\n\t * Check the condition, convert list of expressions into expression tree for further processing\n\t */\n\tif (quals)\n\t{\n\t\tif (IsA(quals, List))\n\t\t{\n\t\t\tquals = (Node *) make_ands_explicit((List *) quals);\n\t\t}\n\n\t\tif (FindNodeMatchingCheckFunction((Node *) quals, CitusIsVolatileFunction))\n\t\t{\n\t\t\tStringInfo errorMessage = makeStringInfo();\n\t\t\tappendStringInfo(errorMessage, \"functions used in the %s clause of MERGE \"\n\t\t\t\t\t\t\t\t\t\t   \"queries on distributed tables must not be VOLATILE\",\n\t\t\t\t\t\t\t (commandType == CMD_MERGE) ? \"ON\" : \"WHEN\");\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t errorMessage->data, NULL, NULL);\n\t\t}\n\t\telse if (MasterIrreducibleExpression(quals, &hasVarArgument, &hasBadCoalesce))\n\t\t{\n\t\t\tAssert(hasVarArgument || hasBadCoalesce);\n\t\t}\n\t}\n\n\tif (hasVarArgument)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"STABLE functions used in MERGE queries \"\n\t\t\t\t\t\t\t \"cannot be called with column references\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tif (hasBadCoalesce)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"non-IMMUTABLE functions are not allowed in CASE or \"\n\t\t\t\t\t\t\t \"COALESCE statements\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tif (quals != NULL && nodeTag(quals) == T_CurrentOfExpr)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot run MERGE actions with cursors\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * RepartitionMergeSupported checks if certain conditions cannot accommodate the\n * strategy of repartition and redistribution of source rows, the routine will verify\n * them and subsequently raises an exception.\n */\nstatic void\nErrorIfRepartitionMergeNotSupported(Oid targetRelationId, Query *mergeQuery,\n\t\t\t\t\t\t\t\t\tQuery *sourceQuery)\n{\n\tif (!IsCitusTableType(targetRelationId, DISTRIBUTED_TABLE))\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"MERGE involving repartition of rows \"\n\t\t\t\t\t\t\"is supported only if the target is distributed\")));\n\t}\n\n\tRTEListProperties *queryRteListProperties = GetRTEListPropertiesForQuery(mergeQuery);\n\tif (queryRteListProperties->hasPostgresLocalTable)\n\t{\n\t\tereport(ERROR, (errmsg(\"MERGE INTO an distributed table from \"\n\t\t\t\t\t\t\t   \"Postgres table is not yet supported\")));\n\t}\n\n\tqueryRteListProperties = GetRTEListPropertiesForQuery(sourceQuery);\n\tif (!queryRteListProperties->hasCitusTable)\n\t{\n\t\tereport(ERROR, (errmsg(\"To MERGE into a distributed table, source must \"\n\t\t\t\t\t\t\t   \"be Citus table(s)\")));\n\t}\n\n\t/*\n\t * Sub-queries and CTEs are not allowed in actions and ON clause\n\t */\n\tNode *joinCondition = GetMergeJoinCondition(mergeQuery);\n\n\tif (FindNodeMatchingCheckFunction(joinCondition, IsNodeSubquery))\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"Sub-queries and CTEs are not allowed in ON clause for MERGE \"\n\t\t\t\t\t\t\"with repartitioning\"),\n\t\t\t\t errhint(\"Consider making the source and target colocated \"\n\t\t\t\t\t\t \"and joined on the distribution column to make it a \"\n\t\t\t\t\t\t \"routable query\")));\n\t}\n\n\tMergeAction *action = NULL;\n\tforeach_declared_ptr(action, mergeQuery->mergeActionList)\n\t{\n\t\tif (FindNodeMatchingCheckFunction((Node *) action, IsNodeSubquery))\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errmsg(\"Sub-queries and CTEs are not allowed in actions for MERGE \"\n\t\t\t\t\t\t\t\"with repartitioning\"),\n\t\t\t\t\t errhint(\"Consider making the source and target colocated \"\n\t\t\t\t\t\t\t \"and joined on the distribution column to make it a \"\n\t\t\t\t\t\t\t \"routable query\")));\n\t\t}\n\t}\n}\n\n\n/*\n * ConvertCteRTEIntoSubquery takes a RTE_CTE and converts it into a RTE_SUBQUERY.\n */\nstatic void\nConvertCteRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte)\n{\n\tCommonTableExpr *sourceCte = NULL;\n\tCommonTableExpr *candidateCte = NULL;\n\tList *cteList = NIL;\n\n\t/*\n\t * Presently, CTEs are only permitted within the USING clause, and thus,\n\t * we search for the corresponding one\n\t */\n\tforeach_declared_ptr(candidateCte, mergeQuery->cteList)\n\t{\n\t\tif (strcmp(candidateCte->ctename, sourceRte->ctename) == 0)\n\t\t{\n\t\t\t/* The source CTE that will be converted to a subquery */\n\t\t\tsourceCte = candidateCte;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Save any other CTEs that are referenced, either directly\n\t\t\t * or indirectly, in the source CTE.\n\t\t\t */\n\t\t\tcteList = lappend(cteList, candidateCte);\n\t\t}\n\t}\n\n\tAssert(sourceCte);\n\n\tQuery *cteQuery = (Query *) copyObject(sourceCte->ctequery);\n\n\tsourceRte->rtekind = RTE_SUBQUERY;\n\n\t/* sanity check - sourceRte was RTE_CTE previously so it should have no perminfo */\n\tAssert(sourceRte->perminfoindex == 0);\n\n\t/*\n\t * As we are delinking the CTE from main query, we have to walk through the\n\t * tree and decrement the ctelevelsup, but by wrapping a subquery, we avoid\n\t * adjusting the ctelevelsup in RTE's\n\t */\n\tsourceRte->subquery = WrapSubquery(cteQuery);\n\n\t/* Copy the rest of the CTEs(if any) and remove them from main query */\n\tsourceRte->subquery->cteList = copyObject(cteList);\n\tmergeQuery->cteList = NIL;\n\n\t/* Zero out CTE-specific fields */\n\tsourceRte->security_barrier = false;\n\tsourceRte->ctename = NULL;\n\tsourceRte->ctelevelsup = 0;\n\tsourceRte->self_reference = false;\n\tsourceRte->coltypes = NIL;\n\tsourceRte->coltypmods = NIL;\n\tsourceRte->colcollations = NIL;\n}\n\n\n/*\n * ConvertRelationRTEIntoSubquery takes a RTE_RELATION and converts it into a RTE_SUBQUERY,\n * which is basically a SELECT * FROM the relation.\n */\nstatic void\nConvertRelationRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte,\n\t\t\t\t\t\t\t   PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tQuery *sourceResultsQuery = makeNode(Query);\n\tRangeTblRef *newRangeTableRef = makeNode(RangeTblRef);\n\tList *requiredAttributes = NIL;\n\n\tRelationRestriction *relationRestriction =\n\t\tRelationRestrictionForRelation(sourceRte, plannerRestrictionContext);\n\tif (relationRestriction)\n\t{\n\t\trequiredAttributes =\n\t\t\tRequiredAttrNumbersForRelationInternal(mergeQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t   relationRestriction->index);\n\t}\n\n\tsourceResultsQuery->commandType = CMD_SELECT;\n\n\t/* we copy the input rteRelation to preserve the rteIdentity */\n\tRangeTblEntry *newRangeTableEntry = copyObject(sourceRte);\n\tsourceResultsQuery->rtable = list_make1(newRangeTableEntry);\n\tsourceResultsQuery->rteperminfos = NIL;\n\tif (sourceRte->perminfoindex)\n\t{\n\t\t/* create permission info for newRangeTableEntry */\n\t\tRTEPermissionInfo *perminfo = getRTEPermissionInfo(mergeQuery->rteperminfos,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   sourceRte);\n\n\t\t/* update the sourceResultsQuery's rteperminfos accordingly */\n\t\tnewRangeTableEntry->perminfoindex = 1;\n\t\tsourceResultsQuery->rteperminfos = list_make1(perminfo);\n\t}\n\n\t/* set the FROM expression to the subquery */\n\tnewRangeTableRef->rtindex = SINGLE_RTE_INDEX;\n\tsourceResultsQuery->jointree = makeFromExpr(list_make1(newRangeTableRef), NULL);\n\tsourceResultsQuery->targetList =\n\t\tCreateFilteredTargetListForRelation(sourceRte->relid, requiredAttributes);\n\tList *restrictionList =\n\t\tGetRestrictInfoListForRelation(sourceRte, plannerRestrictionContext);\n\tList *copyRestrictionList = copyObject(restrictionList);\n\tExpr *andedBoundExpressions = make_ands_explicit(copyRestrictionList);\n\tsourceResultsQuery->jointree->quals = (Node *) andedBoundExpressions;\n\n\t/*\n\t * Originally the quals were pointing to the RTE and its varno\n\t * was pointing to its index in rtable. However now we converted the RTE\n\t * to a subquery and the quals should be pointing to that subquery, which\n\t * is the only RTE in its rtable, hence we update the varnos so that they\n\t * point to the subquery RTE.\n\t * Originally: rtable: [rte1, current_rte, rte3...]\n\t * Now: rtable: [rte1, subquery[current_rte], rte3...] --subquery[current_rte] refers to its rtable.\n\t */\n\tNode *quals = sourceResultsQuery->jointree->quals;\n\tUpdateVarNosInNode(quals, SINGLE_RTE_INDEX);\n\n\t/* replace the function with the constructed subquery */\n\tsourceRte->rtekind = RTE_SUBQUERY;\n\tsourceRte->perminfoindex = 0;\n\tsourceRte->subquery = sourceResultsQuery;\n\tsourceRte->inh = false;\n}\n\n\n/*\n * ConvertSubqueryRTEIntoSubquery takes a RTE_SUBQUERY and wraps it into a new\n * subquery, which eliminates any resjunk columns and adjusts the CTE levelsup.\n * In addition, if the subquery happens to be a SET operation, such as,\n * (SELECT * from a UNION SELECT * FROM b), it reorders, adds casts  and\n * prepares a single taget list\n */\nstatic void\nConvertSubqueryRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte)\n{\n\tsourceRte->subquery = WrapSubquery(sourceRte->subquery);\n\n\tif (list_length(mergeQuery->cteList) > 0)\n\t{\n\t\t/* copy CTEs from the MERGE ... INTO statement into source subquery */\n\t\tsourceRte->subquery->cteList = copyObject(mergeQuery->cteList);\n\t\tsourceRte->subquery->hasModifyingCTE = mergeQuery->hasModifyingCTE;\n\t\tmergeQuery->cteList = NIL;\n\t}\n}\n\n\n/*\n * ConvertSourceRTEIntoSubquery converts MERGE's source RTE into a subquery,\n * whose result rows are repartitioned during runtime.\n */\nstatic void\nConvertSourceRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte,\n\t\t\t\t\t\t\t PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tswitch (sourceRte->rtekind)\n\t{\n\t\tcase RTE_SUBQUERY:\n\t\t{\n\t\t\tConvertSubqueryRTEIntoSubquery(mergeQuery, sourceRte);\n\t\t\treturn;\n\t\t}\n\n\t\tcase RTE_RELATION:\n\t\t{\n\t\t\tConvertRelationRTEIntoSubquery(mergeQuery,\n\t\t\t\t\t\t\t\t\t\t   sourceRte, plannerRestrictionContext);\n\t\t\treturn;\n\t\t}\n\n\t\tcase RTE_CTE:\n\t\t{\n\t\t\tConvertCteRTEIntoSubquery(mergeQuery, sourceRte);\n\t\t\treturn;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"Currently, Citus only supports \"\n\t\t\t\t\t\t\t\t   \"table, subquery, and CTEs as \"\n\t\t\t\t\t\t\t\t   \"valid sources for the MERGE \"\n\t\t\t\t\t\t\t\t   \"operation\")));\n\t\t}\n\t}\n}\n\n\n/*\n * ErrorIfMergeHasReturningList raises an exception if the MERGE has\n * a RETURNING clause, as we don't support this yet for Citus tables\n * Relevant PG17 commit: c649fa24a\n */\nstatic void\nErrorIfMergeHasReturningList(Query *query)\n{\n\tif (query->returningList)\n\t{\n\t\tereport(ERROR, (errmsg(\"MERGE with RETURNING is not yet supported \"\n\t\t\t\t\t\t\t   \"for Citus tables\")));\n\t}\n}\n\n\n/*\n * ErrorIfMergeNotSupported Checks for conditions that are not supported in either\n * the routable or repartition strategies. It checks for\n * - MERGE with a RETURNING clause\n * - Supported table types and their combinations\n * - Check the target lists and quals of both the query and merge actions\n * - Supported CTEs\n */\nstatic void\nErrorIfMergeNotSupported(Query *query, Oid targetRelationId, List *rangeTableList)\n{\n\tErrorIfMergeHasReturningList(query);\n\tErrorIfMergeHasUnsupportedTables(targetRelationId, rangeTableList);\n\tErrorIfMergeQueryQualAndTargetListNotSupported(targetRelationId, query);\n\tErrorIfUnsupportedCTEs(query);\n}\n\n\n/*\n * DeferErrorIfTargetHasFalseClause checks for the presence of a false clause in the\n * target relation and throws an exception if found. Router planner prunes all the shards\n * for relations with such clauses, resulting in no task generation for the job. However,\n * in the case of a MERGE query, tasks still need to be generated for the shards of the\n * source relation.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfTargetHasFalseClause(Oid targetRelationId,\n\t\t\t\t\t\t\t\t PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tListCell *restrictionCell = NULL;\n\tforeach(\n\t\trestrictionCell,\n\t\tplannerRestrictionContext->relationRestrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction =\n\t\t\t(RelationRestriction *) lfirst(restrictionCell);\n\t\tOid relationId = relationRestriction->relationId;\n\n\t\t/* Check only for target relation */\n\t\tif (relationId != targetRelationId)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tList *baseRestrictionList = relationRestriction->relOptInfo->baserestrictinfo;\n\t\tList *restrictClauseList = get_all_actual_clauses(baseRestrictionList);\n\t\tif (ContainsFalseClause(restrictClauseList) ||\n\t\t\tJoinConditionIsOnFalse(relationRestriction->relOptInfo->joininfo))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"Routing query is not possible with \"\n\t\t\t\t\t\t\t\t \"no shards for target\", NULL, NULL);\n\t\t}\n\t}\n\treturn NULL;\n}\n\n\n/*\n * DeferErrorIfRoutableMergeNotSupported Checks for conditions that prevent pushable planning, if\n * found, raises a deferred error, which then continues to try repartitioning strategy.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfRoutableMergeNotSupported(Query *query, List *rangeTableList,\n\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t  plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t  Oid targetRelationId)\n{\n\tList *distTablesList = NIL;\n\tList *refTablesList = NIL;\n\tList *localTablesList = NIL;\n\tRangeTblEntry *rangeTableEntry = NULL;\n\n\tforeach_declared_ptr(rangeTableEntry, rangeTableList)\n\t{\n\t\tOid relationId = rangeTableEntry->relid;\n\n\t\tif (IsCitusTableType(relationId, DISTRIBUTED_TABLE))\n\t\t{\n\t\t\tdistTablesList = lappend(distTablesList, rangeTableEntry);\n\t\t}\n\t\telse if (IsCitusTableType(relationId, REFERENCE_TABLE))\n\t\t{\n\t\t\trefTablesList = lappend(refTablesList, rangeTableEntry);\n\t\t}\n\t\telse if (IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t\t{\n\t\t\tlocalTablesList = lappend(localTablesList, rangeTableEntry);\n\t\t}\n\t}\n\n\tif (list_length(distTablesList) > 0 && list_length(refTablesList) > 0)\n\t{\n\t\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t\t \"A mix of distributed and reference table, try repartitioning\")));\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"A mix of distributed and reference table, \"\n\t\t\t\t\t\t\t \"routable query is not possible\", NULL, NULL);\n\t}\n\n\tif (list_length(distTablesList) > 0 && list_length(localTablesList) > 0)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"A mix of distributed and local table, \"\n\t\t\t\t\t\t\t\t\"try repartitioning\")));\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"A mix of distributed and citus-local table, \"\n\t\t\t\t\t\t\t \"routable query is not possible\", NULL, NULL);\n\t}\n\n\t/*\n\t * If all tables are either local or reference tables, no need to proceed further down\n\t * as the below checks are applicable for distributed tables only\n\t */\n\tif (list_length(distTablesList) == 0)\n\t{\n\t\treturn NULL;\n\t}\n\n\t/* Only one distributed table is involved in the MERGE */\n\tif (list_length(distTablesList) == 1)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"There is only one distributed table, merge is not \"\n\t\t\t\t\t\t\t \"pushable, try repartitioning\", NULL, NULL);\n\t}\n\n\t/* Ensure all distributed tables are indeed co-located */\n\tif (!AllDistributedRelationsInRTEListColocated(distTablesList))\n\t{\n\t\tereport(DEBUG1, (errmsg(\"Distributed tables are not co-located, try \"\n\t\t\t\t\t\t\t\t\"repartitioning\")));\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"For MERGE command, all the distributed tables \"\n\t\t\t\t\t\t\t \"must be colocated\", NULL, NULL);\n\t}\n\n\tDeferredErrorMessage *deferredError = NULL;\n\n\n\t/*\n\t * if the query goes to a single node (\"router\" in Citus' parlance),\n\t * we don't need to go through certain SQL support and colocation checks.\n\t *\n\t * For PG16+, this is required as some of the outer JOINs are converted to\n\t * \"ON(true)\" and filters are pushed down to the table scans. As\n\t * DeferErrorIfUnsupportedSubqueryPushdown rely on JOIN filters, it will fail to\n\t * detect the router case. However, we can still detect it by checking if\n\t * the query is a router query as the router query checks the filters on\n\t * the tables.\n\t */\n\n\n\tif (!MergeSourceHasRouterSelect(query, plannerRestrictionContext))\n\t{\n\t\tdeferredError =\n\t\t\tDeferErrorIfUnsupportedSubqueryPushdown(query,\n\t\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttrue);\n\t\tif (deferredError)\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"Sub-query is not pushable, try repartitioning\")));\n\t\t\treturn deferredError;\n\t\t}\n\n\t\tif (HasDangerousJoinUsing(query->rtable, (Node *) query->jointree))\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t\t\t \"Query has ambigious joins, merge is not pushable, try repartitioning\")));\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"a join with USING causes an internal naming \"\n\t\t\t\t\t\t\t\t \"conflict, use ON instead\", NULL, NULL);\n\t\t}\n\t}\n\n\tdeferredError = DeferErrorIfTargetHasFalseClause(targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\n\tif (deferredError)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"Target relation has a filter of the \"\n\t\t\t\t\t\t\t\t\"form: false (AND ..), which results \"\n\t\t\t\t\t\t\t\t\"in empty shards, but we still need  \"\n\t\t\t\t\t\t\t\t\"to evaluate NOT-MATCHED clause, try \"\n\t\t\t\t\t\t\t\t\"repartitioning\")));\n\t\treturn deferredError;\n\t}\n\n\n\t/*\n\t * If execution has reached this point, it indicates that the query can be delegated to the worker.\n\t * However, before proceeding with this delegation, we need to confirm that the user is utilizing\n\t * the distribution column of the source table in the Insert variable.\n\t * If this is not the case, we should refrain from pushing down the query.\n\t * This is just a deffered error which will be handle by caller.\n\t */\n\n\tVar *insertVar =\n\t\tFetchAndValidateInsertVarIfExists(targetRelationId, query);\n\tif (insertVar &&\n\t\t!IsDistributionColumnInMergeSource((Expr *) insertVar, query, true))\n\t{\n\t\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t\t \"MERGE INSERT must use the source table distribution column value for push down to workers. Otherwise, repartitioning will be applied\")));\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"MERGE INSERT must use the source table distribution column value for push down to workers. Otherwise, repartitioning will be applied\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\treturn NULL;\n}\n\n\n/*\n * MergeSourceHasRouterSelect is a helper function that returns true of the source\n * part of the merge query is a router query.\n */\nstatic bool\nMergeSourceHasRouterSelect(Query *query,\n\t\t\t\t\t\t   PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tQuery *copiedQuery = copyObject(query);\n\tRangeTblEntry *mergeSourceRte = ExtractMergeSourceRangeTableEntry(copiedQuery, true);\n\n\tif (mergeSourceRte == NULL)\n\t{\n\t\t/*\n\t\t * We might potentially support this case in the future, but for now,\n\t\t * we don't support MERGE with JOIN in the source.\n\t\t */\n\t\treturn false;\n\t}\n\n\tConvertSourceRTEIntoSubquery(copiedQuery, mergeSourceRte, plannerRestrictionContext);\n\tQuery *sourceQuery = mergeSourceRte->subquery;\n\n\tDistributedPlan *distributedPlan = CreateRouterPlan(sourceQuery, sourceQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext);\n\n\treturn distributedPlan->planningError == NULL;\n}\n\n\n/*\n * ErrorIfMergeQueryQualAndTargetListNotSupported does check for a MERGE command in the query, if it finds\n * one, it will verify the below criteria\n * - Distributed tables co-location requirements\n * - Checks target-lists and functions-in-quals in TargetlistAndFunctionsSupported\n */\nstatic void\nErrorIfMergeQueryQualAndTargetListNotSupported(Oid targetRelationId, Query *originalQuery)\n{\n\t/*\n\t * TODO: For now, we are adding an exception where any volatile or stable\n\t * functions are not allowed in the MERGE query, but this will become too\n\t * restrictive as this will prevent many useful and simple cases, such as,\n\t * INSERT VALUES(ts::timestamp), bigserial column inserts etc. But without\n\t * this restriction, we have a potential danger of some of the function(s)\n\t * getting executed at the worker which will result in incorrect behavior.\n\t */\n\tif (contain_mutable_functions((Node *) originalQuery))\n\t{\n\t\tereport(ERROR, (errmsg(\"non-IMMUTABLE functions are not yet \"\n\t\t\t\t\t\t\t   \"supported in MERGE sql with distributed tables\")));\n\t}\n\n\tNode *joinCondition = GetMergeJoinCondition(originalQuery);\n\n\tDeferredErrorMessage *deferredError =\n\t\tMergeQualAndTargetListFunctionsSupported(\n\t\t\ttargetRelationId,\n\t\t\toriginalQuery,\n\t\t\tjoinCondition,\n\t\t\toriginalQuery->targetList,\n\t\t\toriginalQuery->commandType);\n\n\tif (deferredError)\n\t{\n\t\tRaiseDeferredError(deferredError, ERROR);\n\t}\n\n\t/*\n\t * MERGE is a special case where we have multiple modify statements\n\t * within itself. Check each INSERT/UPDATE/DELETE individually.\n\t */\n\tMergeAction *action = NULL;\n\tforeach_declared_ptr(action, originalQuery->mergeActionList)\n\t{\n\t\tAssert(originalQuery->returningList == NULL);\n\t\tdeferredError = MergeQualAndTargetListFunctionsSupported(targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t action->qual,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t action->targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t action->commandType);\n\t\tif (deferredError)\n\t\t{\n\t\t\t/* MERGE's unsupported scenario, raise the exception */\n\t\t\tRaiseDeferredError(deferredError, ERROR);\n\t\t}\n\t}\n}\n\n\n/*\n * MergeCommandResultIdPrefix returns the prefix to use for intermediate results of\n * an MERGE INTO ... USING source-query results via the coordinator.\n */\nstatic char *\nMergeCommandResultIdPrefix(uint64 planId)\n{\n\tStringInfo resultIdPrefix = makeStringInfo();\n\tappendStringInfo(resultIdPrefix, \"merge_into_\" UINT64_FORMAT, planId);\n\treturn resultIdPrefix->data;\n}\n\n\n/*\n * ValidateAndReturnVarIfSupported Checks for valid expressions of type Var, and\n * returns the Var if it finds one, for everything else, raises an exception.\n */\nstatic Var *\nValidateAndReturnVarIfSupported(Node *entryExpr)\n{\n\tif (!IsA(entryExpr, Var))\n\t{\n\t\tereport(ERROR, (errmsg(\"MERGE INSERT is using unsupported expression type \"\n\t\t\t\t\t\t\t   \"for distribution column\"),\n\t\t\t\t\t\terrdetail(\"Inserting arbitrary values that don't correspond \"\n\t\t\t\t\t\t\t\t  \"to the joined column values can lead to unpredictable \"\n\t\t\t\t\t\t\t\t  \"outcomes where rows are incorrectly distributed \"\n\t\t\t\t\t\t\t\t  \"among different shards\")));\n\t}\n\n\t/* Found a Var inserting into target's distribution column */\n\treturn (Var *) entryExpr;\n}\n\n\n/*\n * SourceResultPartitionColumnIndex collects all Join conditions from the\n * ON clause and verifies if there is a join, either left or right, with\n * the distribution column of the given target. Once a match is found, it\n * returns the index of that match in the source's target list.\n */\nstatic int\nSourceResultPartitionColumnIndex(Query *mergeQuery, List *sourceTargetList,\n\t\t\t\t\t\t\t\t CitusTableCacheEntry *targetRelation)\n{\n\tList *mergeJoinConditionList = WhereClauseList(GetMergeJoinTree(mergeQuery));\n\tVar *targetColumn = targetRelation->partitionColumn;\n\tVar *sourceRepartitionVar = NULL;\n\tbool foundTypeMismatch = false;\n\n\tOpExpr *validJoinClause =\n\t\tSinglePartitionJoinClause(list_make1(targetColumn), mergeJoinConditionList,\n\t\t\t\t\t\t\t\t  &foundTypeMismatch);\n\tif (!validJoinClause)\n\t{\n\t\tif (foundTypeMismatch)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"In the MERGE ON clause, there is a datatype mismatch \"\n\t\t\t\t\t\t\t\t   \"between target's distribution \"\n\t\t\t\t\t\t\t\t   \"column and the expression originating from the source.\"),\n\t\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\t\"If the types are different, Citus uses different hash \"\n\t\t\t\t\t\t\t\t\"functions for the two column types, which might \"\n\t\t\t\t\t\t\t\t\"lead to incorrect repartitioning of the result data\")));\n\t\t}\n\n\t\tereport(ERROR, (errmsg(\"The required join operation is missing between \"\n\t\t\t\t\t\t\t   \"the target's distribution column and any \"\n\t\t\t\t\t\t\t   \"expression originating from the source. The \"\n\t\t\t\t\t\t\t   \"issue may arise from a non-equi-join.\"),\n\t\t\t\t\t\terrdetail(\"Without a equi-join condition on the target's \"\n\t\t\t\t\t\t\t\t  \"distribution column, the source rows \"\n\t\t\t\t\t\t\t\t  \"cannot be efficiently redistributed, and \"\n\t\t\t\t\t\t\t\t  \"the NOT-MATCHED condition cannot be evaluated \"\n\t\t\t\t\t\t\t\t  \"unambiguously. This can result in incorrect or \"\n\t\t\t\t\t\t\t\t  \"unexpected results when attempting to merge \"\n\t\t\t\t\t\t\t\t  \"tables in a distributed setting\")));\n\t}\n\n\t/* both are verified in SinglePartitionJoinClause to not be NULL, assert is to guard */\n\tVar *leftColumn = LeftColumnOrNULL(validJoinClause);\n\tVar *rightColumn = RightColumnOrNULL(validJoinClause);\n\n\tAssert(leftColumn != NULL);\n\tAssert(rightColumn != NULL);\n\n\tif (equal(targetColumn, leftColumn))\n\t{\n\t\tsourceRepartitionVar = rightColumn;\n\t}\n\telse if (equal(targetColumn, rightColumn))\n\t{\n\t\tsourceRepartitionVar = leftColumn;\n\t}\n\n\t/* Either we find an insert-action or it's not relevant for certain class of tables */\n\tVar *insertVar =\n\t\tFetchAndValidateInsertVarIfExists(targetRelation->relationId, mergeQuery);\n\tif (insertVar)\n\t{\n\t\t/* INSERT action, must choose joining column for inserted value */\n\t\tbool joinedOnInsertColumn =\n\t\t\tJoinOnColumns(list_make1(targetColumn), insertVar, mergeJoinConditionList);\n\t\tif (joinedOnInsertColumn)\n\t\t{\n\t\t\tsourceRepartitionVar = insertVar;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"MERGE INSERT must use the \"\n\t\t\t\t\t\t\t\t   \"source's joining column for \"\n\t\t\t\t\t\t\t\t   \"target's distribution column\")));\n\t\t}\n\t}\n\n\tAssert(sourceRepartitionVar);\n\n\tint sourceResultRepartitionColumnIndex =\n\t\tFindTargetListEntryWithVarExprAttno(sourceTargetList,\n\t\t\t\t\t\t\t\t\t\t\tsourceRepartitionVar->varattno);\n\n\tif (sourceResultRepartitionColumnIndex == -1)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"Unexpected column index of the source list\")));\n\t}\n\telse\n\t{\n\t\tereport(DEBUG1, (errmsg(\"Using column - index:%d from the source list \"\n\t\t\t\t\t\t\t\t\"to redistribute\", sourceResultRepartitionColumnIndex)));\n\t}\n\n\treturn sourceResultRepartitionColumnIndex;\n}\n\n\n/*\n * ExtractMergeSourceRangeTableEntry returns the range table entry of source\n * table or source query in USING clause.\n */\nRangeTblEntry *\nExtractMergeSourceRangeTableEntry(Query *query, bool joinSourceOk)\n{\n\tAssert(IsMergeQuery(query));\n\n\tList *fromList = query->jointree->fromlist;\n\n\t/*\n\t * We should have only one RTE(MergeStmt->sourceRelation) in the from-list\n\t * unless Postgres community changes the representation of merge.\n\t */\n\tif (list_length(fromList) != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"Unexpected source list in MERGE sql USING clause\")));\n\t}\n\n\tRangeTblRef *reference = linitial(fromList);\n\n\t/*\n\t * The planner sometimes generates JoinExprs internally; these can\n\t * have rtindex = 0 if there are no join alias variables referencing\n\t * such joins.\n\t */\n\tif (reference->rtindex == 0)\n\t{\n\t\tif (!joinSourceOk)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"Source is not an explicit query\"),\n\t\t\t\t\t\t\terrhint(\"Source query is a Join expression, \"\n\t\t\t\t\t\t\t\t\t\"try converting into a query as SELECT * \"\n\t\t\t\t\t\t\t\t\t\"FROM (..Join..)\")));\n\t\t}\n\n\t\treturn NULL;\n\t}\n\n\n\tAssert(reference->rtindex >= 1);\n\tRangeTblEntry *subqueryRte = rt_fetch(reference->rtindex, query->rtable);\n\n\treturn subqueryRte;\n}\n\n\n/*\n * FetchAndValidateInsertVarIfExists checks to see if MERGE is inserting a\n * value into the target which is not from the source table, if so, it\n * raises an exception. The return value is the Var that's being inserted\n * into the target's distribution column, If no INSERT action exist, it\n * simply returns a NULL.\n * Note: Inserting random values other than the joined column values will\n * result in unexpected behaviour of rows ending up in incorrect shards, to\n * prevent such mishaps, we disallow such inserts here.\n */\nVar *\nFetchAndValidateInsertVarIfExists(Oid targetRelationId, Query *query)\n{\n\tAssert(IsMergeQuery(query));\n\n\tif (!IsCitusTableType(targetRelationId, DISTRIBUTED_TABLE))\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (!HasDistributionKey(targetRelationId))\n\t{\n\t\treturn NULL;\n\t}\n\n\tbool foundDistributionColumn = false;\n\tMergeAction *action = NULL;\n\tuint32 targetRangeTableIndex = query->resultRelation;\n\tforeach_declared_ptr(action, query->mergeActionList)\n\t{\n\t\t/* Skip MATCHED clause as INSERTS are not allowed in it */\n\t\tif (matched_compat(action))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* NOT MATCHED can have either INSERT, DO NOTHING or UPDATE(PG17) */\n\t\tif (action->commandType == CMD_NOTHING || action->commandType == CMD_UPDATE)\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (action->targetList == NIL)\n\t\t{\n\t\t\t/* INSERT DEFAULT VALUES is not allowed */\n\t\t\tereport(ERROR, (errmsg(\"cannot perform MERGE INSERT with DEFAULTS\"),\n\t\t\t\t\t\t\terrdetail(\"Inserting arbitrary values that don't correspond \"\n\t\t\t\t\t\t\t\t\t  \"to the joined column values can lead to \"\n\t\t\t\t\t\t\t\t\t  \"unpredictable outcomes where rows are \"\n\t\t\t\t\t\t\t\t\t  \"incorrectly distributed among different \"\n\t\t\t\t\t\t\t\t\t  \"shards\")));\n\t\t}\n\n\t\tAssert(action->commandType == CMD_INSERT);\n\t\tVar *targetDistributionKey =\n\t\t\tPartitionColumn(targetRelationId, targetRangeTableIndex);\n\n\t\tTargetEntry *targetEntry = NULL;\n\t\tforeach_declared_ptr(targetEntry, action->targetList)\n\t\t{\n\t\t\tAttrNumber originalAttrNo = targetEntry->resno;\n\n\t\t\t/* skip processing of target table non-distribution columns */\n\t\t\tif (originalAttrNo != targetDistributionKey->varattno)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfoundDistributionColumn = true;\n\n\t\t\tNode *insertExpr =\n\t\t\t\tstrip_implicit_coercions((Node *) copyObject(targetEntry->expr));\n\t\t\treturn ValidateAndReturnVarIfSupported(insertExpr);\n\t\t}\n\n\t\tif (!foundDistributionColumn)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errmsg(\"MERGE INSERT must have distribution column as value\")));\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * FindTargetListEntryWithVarExprAttno finds the index of the target\n * entry whose expr is a Var that points to input varattno.\n *\n * If no such target entry is found, it returns -1.\n */\nstatic int\nFindTargetListEntryWithVarExprAttno(List *targetList, AttrNumber varattno)\n{\n\tint targetEntryIndex = 0;\n\n\tTargetEntry *targetEntry = NULL;\n\tforeach_declared_ptr(targetEntry, targetList)\n\t{\n\t\tif (IsA(targetEntry->expr, Var) &&\n\t\t\t((Var *) targetEntry->expr)->varattno == varattno)\n\t\t{\n\t\t\treturn targetEntryIndex;\n\t\t}\n\n\t\ttargetEntryIndex++;\n\t}\n\n\treturn -1;\n}\n\n\n/*\n * IsLocalTableModification returns true if the table modified is a Postgres table.\n * We do not support recursive planning for MERGE yet, so we could have a join\n * between local and Citus tables. Only allow local tables when it is the target table.\n */\nbool\nIsLocalTableModification(Oid targetRelationId, Query *query, uint64 shardId,\n\t\t\t\t\t\t RTEListProperties *rteProperties)\n{\n\t/* No-op for SELECT command */\n\tif (!IsModifyCommand(query))\n\t{\n\t\treturn false;\n\t}\n\n\t/* For MERGE, we have to check only the target relation */\n\tif (IsMergeQuery(query) && !IsCitusTable(targetRelationId))\n\t{\n\t\t/* Postgres table */\n\t\treturn true;\n\t}\n\n\tif (shardId == INVALID_SHARD_ID && ContainsOnlyLocalOrReferenceTables(rteProperties))\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/multi_explain.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_explain.c\n *\t  Citus explain support.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/htup_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/copy.h\"\n#include \"commands/createas.h\"\n#include \"commands/dbcommands.h\"\n#include \"commands/explain.h\"\n#include \"commands/tablecmds.h\"\n#include \"executor/tstoreReceiver.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/plannodes.h\"\n#include \"nodes/primnodes.h\"\n#include \"nodes/print.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/cost.h\"\n#include \"optimizer/planner.h\"\n#include \"parser/analyze.h\"\n#include \"portability/instr_time.h\"\n#include \"rewrite/rewriteHandler.h\"\n#include \"tcop/dest.h\"\n#include \"tcop/tcopprot.h\"\n#include \"tcop/utility.h\"\n#include \"utils/builtins.h\"\n#include \"utils/json.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/snapmgr.h\"\n\n#include \"pg_version_constants.h\"\n#if PG_VERSION_NUM >= PG_VERSION_18\n#include \"commands/explain_dr.h\"   /* CreateExplainSerializeDestReceiver() */\n#include \"commands/explain_format.h\"\n#endif\n\n\n#include \"distributed/citus_depended_object.h\"\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/combine_query_planner.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/executor_util.h\"\n#include \"distributed/insert_select_executor.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/jsonbutils.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/merge_planner.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_explain.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/subplan_execution.h\"\n#include \"distributed/tuple_destination.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/* Config variables that enable printing distributed query plans */\nbool ExplainDistributedQueries = true;\nbool ExplainAllTasks = false;\nint ExplainAnalyzeSortMethod = EXPLAIN_ANALYZE_SORT_BY_TIME;\nextern MemoryContext SubPlanExplainAnalyzeContext;\n\n/*\n * If enabled, EXPLAIN ANALYZE output & other statistics of last worker task\n * are saved in following variables.\n */\nstatic char *SavedExplainPlan = NULL;\nstatic double SavedExecutionDurationMillisec = 0.0;\nstatic double SavedExplainPlanNtuples = 0;\nstatic double SavedExplainPlanNloops = 0;\nextern SubPlanExplainOutputData *SubPlanExplainOutput;\nuint8 TotalExplainOutputCapacity = 0;\nuint8 NumTasksOutput = 0;\n\n/* struct to save explain flags */\ntypedef struct\n{\n\tbool verbose;\n\tbool costs;\n\tbool buffers;\n\tbool wal;\n\tbool timing;\n\tbool summary;\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tbool memory;\n\tExplainSerializeOption serialize;\n#endif\n\tExplainFormat format;\n} ExplainOptions;\n\n\n/* EXPLAIN flags of current distributed explain */\n#if PG_VERSION_NUM >= PG_VERSION_17\nstatic ExplainOptions CurrentDistributedQueryExplainOptions = {\n\t0, 0, 0, 0, 0, 0, 0, EXPLAIN_SERIALIZE_NONE, EXPLAIN_FORMAT_TEXT\n};\n#else\nstatic ExplainOptions CurrentDistributedQueryExplainOptions = {\n\t0, 0, 0, 0, 0, 0, EXPLAIN_FORMAT_TEXT\n};\n#endif\n\n/* Result for a single remote EXPLAIN command */\ntypedef struct RemoteExplainPlan\n{\n\tint placementIndex;\n\tList *explainOutputList;\n} RemoteExplainPlan;\n\n\n/*\n * ExplainAnalyzeDestination is internal representation of a TupleDestination\n * which collects EXPLAIN ANALYZE output after the main query is run.\n */\ntypedef struct ExplainAnalyzeDestination\n{\n\tTupleDestination pub;\n\tTask *originalTask;\n\tTupleDestination *originalTaskDestination;\n\tTupleDesc lastSavedExplainAnalyzeTupDesc;\n} ExplainAnalyzeDestination;\n\n#if PG_VERSION_NUM >= PG_VERSION_17 && PG_VERSION_NUM < PG_VERSION_18\n\n/* copied from explain.c */\n/* Instrumentation data for SERIALIZE option */\ntypedef struct SerializeMetrics\n{\n\tuint64 bytesSent;           /* # of bytes serialized */\n\tinstr_time timeSpent;       /* time spent serializing */\n\tBufferUsage bufferUsage;    /* buffers accessed during serialization */\n} SerializeMetrics;\n\n/* copied from explain.c */\nstatic void ExplainIndentText(ExplainState *es);\nstatic SerializeMetrics GetSerializationMetrics(DestReceiver *dest);\n\n/*\n * DestReceiver functions for SERIALIZE option\n *\n * A DestReceiver for query tuples, that serializes passed rows into RowData\n * messages while measuring the resources expended and total serialized size,\n * while never sending the data to the client.  This allows measuring the\n * overhead of deTOASTing and datatype out/sendfuncs, which are not otherwise\n * exercisable without actually hitting the network.\n *\n * copied from explain.c\n */\ntypedef struct SerializeDestReceiver\n{\n\tDestReceiver pub;\n\tExplainState *es;           /* this EXPLAIN statement's ExplainState */\n\tint8 format;                /* text or binary, like pq wire protocol */\n\tTupleDesc attrinfo;         /* the output tuple desc */\n\tint nattrs;                 /* current number of columns */\n\tFmgrInfo *finfos;           /* precomputed call info for output fns */\n\tMemoryContext tmpcontext;   /* per-row temporary memory context */\n\tStringInfoData buf;         /* buffer to hold the constructed message */\n\tSerializeMetrics metrics;   /* collected metrics */\n} SerializeDestReceiver;\n#endif\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\n/*\n * Various places within need to convert bytes to kilobytes.  Round these up\n * to the next whole kilobyte.\n * copied from explain.c\n */\n#define BYTES_TO_KILOBYTES(b) (((b) + 1023) / 1024)\n\n/* copied from explain.c */\nstatic bool peek_buffer_usage(ExplainState *es, const BufferUsage *usage);\nstatic void show_buffer_usage(ExplainState *es, const BufferUsage *usage);\nstatic void show_memory_counters(ExplainState *es,\n\t\t\t\t\t\t\t\t const MemoryContextCounters *mem_counters);\nstatic void ExplainPrintSerialize(ExplainState *es,\n\t\t\t\t\t\t\t\t  SerializeMetrics *metrics);\n#endif\n\n/* Explain functions for distributed queries */\nstatic void ExplainSubPlans(DistributedPlan *distributedPlan, ExplainState *es);\nstatic void ExplainJob(CitusScanState *scanState, Job *job, ExplainState *es,\n\t\t\t\t\t   ParamListInfo params);\nstatic void ExplainMapMergeJob(MapMergeJob *mapMergeJob, ExplainState *es);\nstatic void ExplainTaskList(CitusScanState *scanState, List *taskList, ExplainState *es,\n\t\t\t\t\t\t\tParamListInfo params);\nstatic RemoteExplainPlan * RemoteExplain(Task *task, ExplainState *es, ParamListInfo\n\t\t\t\t\t\t\t\t\t\t params);\nstatic RemoteExplainPlan * GetSavedRemoteExplain(Task *task, ExplainState *es);\nstatic RemoteExplainPlan * FetchRemoteExplainFromWorkers(Task *task, ExplainState *es,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t ParamListInfo params);\nstatic void ExplainTask(CitusScanState *scanState, Task *task, int placementIndex,\n\t\t\t\t\t\tList *explainOutputList,\n\t\t\t\t\t\tExplainState *es);\nstatic void ExplainTaskPlacement(ShardPlacement *taskPlacement, List *explainOutputList,\n\t\t\t\t\t\t\t\t ExplainState *es);\nstatic StringInfo BuildRemoteExplainQuery(char *queryString, ExplainState *es);\nstatic const char * ExplainFormatStr(ExplainFormat format);\n#if PG_VERSION_NUM >= PG_VERSION_17\nstatic const char * ExplainSerializeStr(ExplainSerializeOption serializeOption);\n#endif\nstatic void ExplainWorkerPlan(PlannedStmt *plannedStmt, DistributedSubPlan *subPlan,\n\t\t\t\t\t\t\t  DestReceiver *dest,\n\t\t\t\t\t\t\t  ExplainState *es,\n\t\t\t\t\t\t\t  const char *queryString, ParamListInfo params,\n\t\t\t\t\t\t\t  QueryEnvironment *queryEnv,\n\t\t\t\t\t\t\t  const instr_time *planduration,\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\t\t\t\t\t\t  const BufferUsage *bufusage,\n\t\t\t\t\t\t\t  const MemoryContextCounters *mem_counters,\n#endif\n\t\t\t\t\t\t\t  double *executionDurationMillisec,\n\t\t\t\t\t\t\t  double *executionTuples,\n\t\t\t\t\t\t\t  double *executionLoops);\nstatic ExplainFormat ExtractFieldExplainFormat(Datum jsonbDoc, const char *fieldName,\n\t\t\t\t\t\t\t\t\t\t\t   ExplainFormat defaultValue);\n#if PG_VERSION_NUM >= PG_VERSION_17\nstatic ExplainSerializeOption ExtractFieldExplainSerialize(Datum jsonbDoc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   const char *fieldName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ExplainSerializeOption\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   defaultValue);\n#endif\nstatic TupleDestination * CreateExplainAnlyzeDestination(Task *task,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t TupleDestination *taskDest);\nstatic void ExplainAnalyzeDestPutTuple(TupleDestination *self, Task *task,\n\t\t\t\t\t\t\t\t\t   int placementIndex, int queryNumber,\n\t\t\t\t\t\t\t\t\t   HeapTuple heapTuple, uint64 tupleLibpqSize);\nstatic TupleDesc ExplainAnalyzeDestTupleDescForQuery(TupleDestination *self, int\n\t\t\t\t\t\t\t\t\t\t\t\t\t queryNumber);\nstatic char * WrapQueryForExplainAnalyze(const char *queryString, TupleDesc tupleDesc,\n\t\t\t\t\t\t\t\t\t\t ParamListInfo params);\nstatic char * FetchPlanQueryForExplainAnalyze(const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t  ParamListInfo params);\nstatic char * ParameterResolutionSubquery(ParamListInfo params);\nstatic List * SplitString(const char *str, char delimiter, int maxLength);\n\n/* Static Explain functions copied from explain.c */\nstatic void ExplainOneQuery(Query *query, int cursorOptions,\n\t\t\t\t\t\t\tIntoClause *into, ExplainState *es,\n\t\t\t\t\t\t\tconst char *queryString, ParamListInfo params,\n\t\t\t\t\t\t\tQueryEnvironment *queryEnv);\nstatic double elapsed_time(instr_time *starttime);\nstatic void ExplainPropertyBytes(const char *qlabel, int64 bytes, ExplainState *es);\nstatic uint64 TaskReceivedTupleData(Task *task);\nstatic bool ShowReceivedTupleData(CitusScanState *scanState, ExplainState *es);\nstatic bool PlanStateAnalyzeWalker(PlanState *planState, void *ctx);\nstatic void ExtractAnalyzeStats(DistributedSubPlan *subPlan, PlanState *planState);\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(worker_last_saved_explain_analyze);\nPG_FUNCTION_INFO_V1(worker_save_query_explain_analyze);\n\n\n/*\n * CitusExplainScan is a custom scan explain callback function which is used to\n * print explain information of a Citus plan which includes both combine query and\n * distributed plan.\n */\nvoid\nCitusExplainScan(CustomScanState *node, List *ancestors, struct ExplainState *es)\n{\n\tif (es->generic)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"EXPLAIN GENERIC_PLAN is currently not supported for Citus tables\")));\n\t}\n\n\tCitusScanState *scanState = (CitusScanState *) node;\n\tDistributedPlan *distributedPlan = scanState->distributedPlan;\n\tEState *executorState = ScanStateGetExecutorState(scanState);\n\tParamListInfo params = executorState->es_param_list_info;\n\n\tif (!ExplainDistributedQueries)\n\t{\n\t\tExplainPropertyBool(\"citus.explain_distributed_queries\", false, es);\n\t\treturn;\n\t}\n\n\tExplainOpenGroup(\"Distributed Query\", \"Distributed Query\", true, es);\n\n\t/*\n\t * ExplainOnePlan function of postgres might be called in this codepath.\n\t * It requires an ActiveSnapshot being set. Make sure to make ActiveSnapshot available before calling into\n\t * Citus Explain functions.\n\t */\n\tPushActiveSnapshot(executorState->es_snapshot);\n\n\tif (distributedPlan->subPlanList != NIL)\n\t{\n\t\tExplainSubPlans(distributedPlan, es);\n\t}\n\n\tExplainJob(scanState, distributedPlan->workerJob, es, params);\n\n\tPopActiveSnapshot();\n\n\tExplainCloseGroup(\"Distributed Query\", \"Distributed Query\", true, es);\n}\n\n\n/*\n * NonPushableInsertSelectExplainScan is a custom scan explain callback function\n * which is used to print explain information of a Citus plan for an INSERT INTO\n * distributed_table SELECT ... query that is evaluated on the coordinator or\n * uses repartitioning.\n */\nvoid\nNonPushableInsertSelectExplainScan(CustomScanState *node, List *ancestors,\n\t\t\t\t\t\t\t\t   struct ExplainState *es)\n{\n\tCitusScanState *scanState = (CitusScanState *) node;\n\tDistributedPlan *distributedPlan = scanState->distributedPlan;\n\tQuery *insertSelectQuery = distributedPlan->modifyQueryViaCoordinatorOrRepartition;\n\tRangeTblEntry *selectRte = ExtractSelectRangeTableEntry(insertSelectQuery);\n\n\t/*\n\t * Create a copy because ExplainOneQuery can modify the query, and later\n\t * executions of prepared statements might require it. See\n\t * https://github.com/citusdata/citus/issues/3947 for what can happen.\n\t */\n\tQuery *queryCopy = copyObject(selectRte->subquery);\n\n\tbool repartition =\n\t\tdistributedPlan->modifyWithSelectMethod == MODIFY_WITH_SELECT_REPARTITION;\n\n\tif (es->analyze)\n\t{\n\t\tereport(ERROR, (errmsg(\"EXPLAIN ANALYZE is currently not supported for INSERT \"\n\t\t\t\t\t\t\t   \"... SELECT commands %s\",\n\t\t\t\t\t\t\t   repartition ? \"with repartitioning\" : \"via coordinator\")));\n\t}\n\n\tif (repartition)\n\t{\n\t\tExplainPropertyText(\"INSERT/SELECT method\", \"repartition\", es);\n\t}\n\telse\n\t{\n\t\tExplainPropertyText(\"INSERT/SELECT method\", \"pull to coordinator\", es);\n\t}\n\n\tExplainOpenGroup(\"Select Query\", \"Select Query\", false, es);\n\n\t/* explain the inner SELECT query */\n\tIntoClause *into = NULL;\n\tParamListInfo params = NULL;\n\n\t/*\n\t * With PG14, we need to provide a string here,\n\t * for now we put an empty string, which is valid according to postgres.\n\t */\n\tchar *queryString = pstrdup(\"\");\n\n\tExplainOneQuery(queryCopy, 0, into, es, queryString, params, NULL);\n\n\tExplainCloseGroup(\"Select Query\", \"Select Query\", false, es);\n}\n\n\n/*\n * NonPushableMergeSqlExplainScan is a custom scan explain callback function\n * which is used to print explain information of a Citus plan for MERGE INTO\n * distributed_table USING (source query/table), where source can be any query\n * whose results are repartitioned to colocated with the target table.\n */\nvoid\nNonPushableMergeCommandExplainScan(CustomScanState *node, List *ancestors,\n\t\t\t\t\t\t\t\t   struct ExplainState *es)\n{\n\tCitusScanState *scanState = (CitusScanState *) node;\n\tDistributedPlan *distributedPlan = scanState->distributedPlan;\n\tQuery *mergeQuery = distributedPlan->modifyQueryViaCoordinatorOrRepartition;\n\tRangeTblEntry *sourceRte = ExtractMergeSourceRangeTableEntry(mergeQuery, false);\n\n\t/*\n\t * Create a copy because ExplainOneQuery can modify the query, and later\n\t * executions of prepared statements might require it. See\n\t * https://github.com/citusdata/citus/issues/3947 for what can happen.\n\t */\n\tQuery *sourceQueryCopy = copyObject(sourceRte->subquery);\n\tbool repartition =\n\t\tdistributedPlan->modifyWithSelectMethod == MODIFY_WITH_SELECT_REPARTITION;\n\n\tif (es->analyze)\n\t{\n\t\tereport(ERROR, (errmsg(\"EXPLAIN ANALYZE is currently not supported for \"\n\t\t\t\t\t\t\t   \"MERGE INTO ... commands with repartitioning\")));\n\t}\n\n\tOid targetRelationId = ModifyQueryResultRelationId(mergeQuery);\n\tStringInfo mergeMethodMessage = makeStringInfo();\n\tappendStringInfo(mergeMethodMessage,\n\t\t\t\t\t \"MERGE INTO %s method\", get_rel_name(targetRelationId));\n\n\tif (repartition)\n\t{\n\t\tExplainPropertyText(mergeMethodMessage->data, \"repartition\", es);\n\t}\n\telse\n\t{\n\t\tExplainPropertyText(mergeMethodMessage->data, \"pull to coordinator\", es);\n\t}\n\n\tExplainOpenGroup(\"Source Query\", \"Source Query\", false, es);\n\n\t/* explain the MERGE source query */\n\tIntoClause *into = NULL;\n\tParamListInfo params = NULL;\n\n\t/*\n\t * With PG14, we need to provide a string here, for now we put an empty\n\t * string, which is valid according to postgres.\n\t */\n\tchar *queryString = pstrdup(\"\");\n\tExplainOneQuery(sourceQueryCopy, 0, into, es, queryString, params, NULL);\n\n\tExplainCloseGroup(\"Source Query\", \"Source Query\", false, es);\n}\n\n\n/*\n * ExtractAnalyzeStats parses the EXPLAIN ANALYZE output of the pre-executed\n * subplans and injects the parsed statistics into queryDesc->planstate->instrument.\n */\nstatic void\nExtractAnalyzeStats(DistributedSubPlan *subPlan, PlanState *planState)\n{\n\tif (!planState)\n\t{\n\t\treturn;\n\t}\n\n\tInstrumentation *instr = planState->instrument;\n\tif (!IsA(planState, CustomScanState))\n\t{\n\t\tinstr->ntuples = subPlan->ntuples;\n\t\tinstr->nloops = 1; /* subplan nodes are executed only once */\n\t\treturn;\n\t}\n\n\tAssert(IsA(planState, CustomScanState));\n\n\tif (subPlan->numTasksOutput <= 0)\n\t{\n\t\treturn;\n\t}\n\n\tListCell *lc;\n\tint tasksOutput = 0;\n\tdouble tasksNtuples = 0;\n\tdouble tasksNloops = 0;\n\tmemset(instr, 0, sizeof(Instrumentation));\n\tDistributedPlan *newdistributedPlan =\n\t\t((CitusScanState *) planState)->distributedPlan;\n\n\t/*\n\t * Inject the earlier executed results—extracted from the workers' EXPLAIN output—\n\t * into the newly created tasks.\n\t */\n\tforeach(lc, newdistributedPlan->workerJob->taskList)\n\t{\n\t\tTask *task = (Task *) lfirst(lc);\n\t\tuint32 taskId = task->taskId;\n\n\t\tif (tasksOutput > subPlan->numTasksOutput)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!subPlan->totalExplainOutput[taskId].explainOutput)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Now feed the earlier saved output, which will be used\n\t\t * by RemoteExplain() when printing tasks\n\t\t */\n\t\tMemoryContext taskContext = GetMemoryChunkContext(task);\n\t\ttask->totalReceivedTupleData =\n\t\t\tsubPlan->totalExplainOutput[taskId].totalReceivedTupleData;\n\t\ttask->fetchedExplainAnalyzeExecutionDuration =\n\t\t\tsubPlan->totalExplainOutput[taskId].executionDuration;\n\t\ttask->fetchedExplainAnalyzePlan =\n\t\t\tMemoryContextStrdup(taskContext,\n\t\t\t\t\t\t\t\tsubPlan->totalExplainOutput[taskId].explainOutput);\n\t\ttasksNtuples += subPlan->totalExplainOutput[taskId].executionNtuples;\n\t\ttasksNloops = subPlan->totalExplainOutput[taskId].executionNloops;\n\n\t\tsubPlan->totalExplainOutput[taskId].explainOutput = NULL;\n\t\ttasksOutput++;\n\t}\n\n\tinstr->ntuples = tasksNtuples;\n\tinstr->nloops = tasksNloops;\n}\n\n\n/*\n * ExplainSubPlans generates EXPLAIN output for subplans for CTEs\n * and complex subqueries. Because the planning for these queries\n * is done along with the top-level plan, we cannot determine the\n * planning time and set it to 0.\n */\nstatic void\nExplainSubPlans(DistributedPlan *distributedPlan, ExplainState *es)\n{\n\tListCell *subPlanCell = NULL;\n\tuint64 planId = distributedPlan->planId;\n\n\tExplainOpenGroup(\"Subplans\", \"Subplans\", false, es);\n\n\tforeach(subPlanCell, distributedPlan->subPlanList)\n\t{\n\t\tDistributedSubPlan *subPlan = (DistributedSubPlan *) lfirst(subPlanCell);\n\t\tPlannedStmt *plan = subPlan->plan;\n\t\tParamListInfo params = NULL;\n\n\t\t/*\n\t\t * With PG14, we need to provide a string here,\n\t\t * for now we put an empty string, which is valid according to postgres.\n\t\t */\n\t\tchar *queryString = pstrdup(\"\");\n\t\tinstr_time planduration;\n\t\tBufferUsage bufusage_start,\n\t\t\t\t\tbufusage;\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\tMemoryContextCounters mem_counters;\n\t\tMemoryContext planner_ctx = NULL;\n\t\tMemoryContext saved_ctx = NULL;\n\n\t\tif (es->memory)\n\t\t{\n\t\t\t/* copy paste from postgres code */\n\t\t\tplanner_ctx = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\"explain analyze planner context\",\n\t\t\t\t\t\t\t\t\t\t\t\tALLOCSET_DEFAULT_SIZES);\n\t\t\tsaved_ctx = MemoryContextSwitchTo(planner_ctx);\n\t\t}\n#endif\n\n\t\tif (es->buffers)\n\t\t{\n\t\t\tbufusage_start = pgBufferUsage;\n\t\t}\n\n\t\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t\t{\n\t\t\tchar *resultId = GenerateResultId(planId, subPlan->subPlanId);\n\n\t\t\tappendStringInfoSpaces(es->str, es->indent * 2);\n\t\t\tappendStringInfo(es->str, \"->  Distributed Subplan %s\\n\", resultId);\n\t\t\tes->indent += 3;\n\t\t}\n\n\t\tExplainOpenGroup(\"Subplan\", NULL, true, es);\n\n\t\tif (es->analyze)\n\t\t{\n\t\t\tif (es->timing)\n\t\t\t{\n\t\t\t\tExplainPropertyFloat(\"Subplan Duration\", \"ms\", subPlan->durationMillisecs,\n\t\t\t\t\t\t\t\t\t 2, es);\n\t\t\t}\n\n\t\t\tExplainPropertyBytes(\"Intermediate Data Size\",\n\t\t\t\t\t\t\t\t subPlan->bytesSentPerWorker, es);\n\n\t\t\tStringInfo destination = makeStringInfo();\n\t\t\tif (subPlan->remoteWorkerCount && subPlan->writeLocalFile)\n\t\t\t{\n\t\t\t\tappendStringInfo(destination, \"Send to %d nodes, write locally\",\n\t\t\t\t\t\t\t\t subPlan->remoteWorkerCount);\n\t\t\t}\n\t\t\telse if (subPlan->writeLocalFile)\n\t\t\t{\n\t\t\t\tappendStringInfoString(destination, \"Write locally\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(destination, \"Send to %d nodes\",\n\t\t\t\t\t\t\t\t subPlan->remoteWorkerCount);\n\t\t\t}\n\n\t\t\tExplainPropertyText(\"Result destination\", destination->data, es);\n\t\t}\n\n\t\tINSTR_TIME_SET_ZERO(planduration);\n\n\t\t/* calc differences of buffer counters. */\n\t\tif (es->buffers)\n\t\t{\n\t\t\tmemset(&bufusage, 0, sizeof(BufferUsage));\n\t\t\tBufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);\n\t\t}\n\n\t\tExplainOpenGroup(\"PlannedStmt\", \"PlannedStmt\", false, es);\n\n\t\tDestReceiver *dest = None_Receiver; /* No query execution */\n\t\tdouble executionDurationMillisec = 0.0;\n\t\tdouble executionTuples = 0;\n\t\tdouble executionLoops = 0;\n\n/* Capture memory stats on PG17+ */\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\tif (es->memory)\n\t\t{\n\t\t\tMemoryContextSwitchTo(saved_ctx);\n\t\t\tMemoryContextMemConsumed(planner_ctx, &mem_counters);\n\t\t}\n\n\t\t/* Execute EXPLAIN without ANALYZE */\n\t\tExplainWorkerPlan(plan, subPlan, dest, es, queryString, params, NULL,\n\t\t\t\t\t\t  &planduration,\n\t\t\t\t\t\t  (es->buffers ? &bufusage : NULL),\n\t\t\t\t\t\t  (es->memory ? &mem_counters : NULL),\n\t\t\t\t\t\t  &executionDurationMillisec,\n\t\t\t\t\t\t  &executionTuples,\n\t\t\t\t\t\t  &executionLoops);\n#else\n\n\t\t/* Execute EXPLAIN without ANALYZE */\n\t\tExplainWorkerPlan(plan, subPlan, dest, es, queryString, params, NULL,\n\t\t\t\t\t\t  &planduration, &executionDurationMillisec,\n\t\t\t\t\t\t  &executionTuples, &executionLoops);\n#endif\n\n\t\tExplainCloseGroup(\"PlannedStmt\", \"PlannedStmt\", false, es);\n\t\tExplainCloseGroup(\"Subplan\", NULL, true, es);\n\n\t\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t\t{\n\t\t\tes->indent -= 3;\n\t\t}\n\t}\n\n\tExplainCloseGroup(\"Subplans\", \"Subplans\", false, es);\n}\n\n\n/*\n * ExplainPropertyBytes formats bytes in a human readable way by using\n * pg_size_pretty.\n */\nstatic void\nExplainPropertyBytes(const char *qlabel, int64 bytes, ExplainState *es)\n{\n\tDatum textDatum = DirectFunctionCall1(pg_size_pretty, Int64GetDatum(bytes));\n\tExplainPropertyText(qlabel, TextDatumGetCString(textDatum), es);\n}\n\n\n/*\n * ShowReceivedTupleData returns true if explain should show received data.\n * This is only the case when using EXPLAIN ANALYZE on queries that return\n * rows.\n */\nstatic bool\nShowReceivedTupleData(CitusScanState *scanState, ExplainState *es)\n{\n\tTupleDesc tupDesc = ScanStateGetTupleDescriptor(scanState);\n\treturn es->analyze && tupDesc != NULL && tupDesc->natts > 0;\n}\n\n\n/*\n * ExplainJob shows the EXPLAIN output for a Job in the physical plan of\n * a distributed query by showing the remote EXPLAIN for the first task,\n * or all tasks if citus.explain_all_tasks is on.\n */\nstatic void\nExplainJob(CitusScanState *scanState, Job *job, ExplainState *es,\n\t\t   ParamListInfo params)\n{\n\tList *dependentJobList = job->dependentJobList;\n\tint dependentJobCount = list_length(dependentJobList);\n\tListCell *dependentJobCell = NULL;\n\tList *taskList = job->taskList;\n\tint taskCount = list_length(taskList);\n\n\tExplainOpenGroup(\"Job\", \"Job\", true, es);\n\n\tExplainPropertyInteger(\"Task Count\", NULL, taskCount, es);\n\tif (ShowReceivedTupleData(scanState, es))\n\t{\n\t\tTask *task = NULL;\n\t\tuint64 totalReceivedTupleDataForAllTasks = 0;\n\t\tforeach_declared_ptr(task, taskList)\n\t\t{\n\t\t\ttotalReceivedTupleDataForAllTasks += TaskReceivedTupleData(task);\n\t\t}\n\t\tExplainPropertyBytes(\"Tuple data received from nodes\",\n\t\t\t\t\t\t\t totalReceivedTupleDataForAllTasks,\n\t\t\t\t\t\t\t es);\n\t}\n\n\tif (dependentJobCount > 0)\n\t{\n\t\tExplainPropertyText(\"Tasks Shown\", \"None, not supported for re-partition \"\n\t\t\t\t\t\t\t\t\t\t   \"queries\", es);\n\t}\n\telse if (ExplainAllTasks || taskCount <= 1)\n\t{\n\t\tExplainPropertyText(\"Tasks Shown\", \"All\", es);\n\t}\n\telse\n\t{\n\t\tStringInfo tasksShownText = makeStringInfo();\n\t\tappendStringInfo(tasksShownText, \"One of %d\", taskCount);\n\n\t\tExplainPropertyText(\"Tasks Shown\", tasksShownText->data, es);\n\t}\n\n\t/*\n\t * We cannot fetch EXPLAIN plans for jobs that have dependencies, since the\n\t * intermediate tables have not been created.\n\t */\n\tif (dependentJobCount == 0)\n\t{\n\t\tExplainOpenGroup(\"Tasks\", \"Tasks\", false, es);\n\n\t\tExplainTaskList(scanState, taskList, es, params);\n\n\t\tExplainCloseGroup(\"Tasks\", \"Tasks\", false, es);\n\t}\n\telse\n\t{\n\t\tExplainOpenGroup(\"Dependent Jobs\", \"Dependent Jobs\", false, es);\n\n\t\t/* show explain output for dependent jobs, if any */\n\t\tforeach(dependentJobCell, dependentJobList)\n\t\t{\n\t\t\tJob *dependentJob = (Job *) lfirst(dependentJobCell);\n\n\t\t\tif (CitusIsA(dependentJob, MapMergeJob))\n\t\t\t{\n\t\t\t\tExplainMapMergeJob((MapMergeJob *) dependentJob, es);\n\t\t\t}\n\t\t}\n\n\t\tExplainCloseGroup(\"Dependent Jobs\", \"Dependent Jobs\", false, es);\n\t}\n\n\tExplainCloseGroup(\"Job\", \"Job\", true, es);\n}\n\n\n/*\n * TaskReceivedTupleData returns the amount of data that was received by the\n * coordinator for the task. If it's a RETURNING DML task the value stored in\n * totalReceivedTupleData is not correct yet because it only counts the bytes for\n * one placement.\n */\nstatic uint64\nTaskReceivedTupleData(Task *task)\n{\n\tif (task->taskType == MODIFY_TASK)\n\t{\n\t\treturn task->totalReceivedTupleData * list_length(task->taskPlacementList);\n\t}\n\treturn task->totalReceivedTupleData;\n}\n\n\n/*\n * ExplainMapMergeJob shows a very basic EXPLAIN plan for a MapMergeJob. It does\n * not yet show the EXPLAIN plan for the individual tasks, because this requires\n * specific logic for getting the query (which is wrapped in a UDF), and the\n * queries may use intermediate tables that have not been created.\n */\nstatic void\nExplainMapMergeJob(MapMergeJob *mapMergeJob, ExplainState *es)\n{\n\tList *dependentJobList = mapMergeJob->job.dependentJobList;\n\tint dependentJobCount = list_length(dependentJobList);\n\tListCell *dependentJobCell = NULL;\n\tint mapTaskCount = list_length(mapMergeJob->mapTaskList);\n\tint mergeTaskCount = list_length(mapMergeJob->mergeTaskList);\n\n\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t{\n\t\tappendStringInfoSpaces(es->str, es->indent * 2);\n\t\tappendStringInfo(es->str, \"->  MapMergeJob\\n\");\n\t\tes->indent += 3;\n\t}\n\n\tExplainOpenGroup(\"MapMergeJob\", NULL, true, es);\n\tExplainPropertyInteger(\"Map Task Count\", NULL, mapTaskCount, es);\n\tExplainPropertyInteger(\"Merge Task Count\", NULL, mergeTaskCount, es);\n\n\tif (dependentJobCount > 0)\n\t{\n\t\tExplainOpenGroup(\"Dependent Jobs\", \"Dependent Jobs\", false, es);\n\n\t\tforeach(dependentJobCell, dependentJobList)\n\t\t{\n\t\t\tJob *dependentJob = (Job *) lfirst(dependentJobCell);\n\n\t\t\tif (CitusIsA(dependentJob, MapMergeJob))\n\t\t\t{\n\t\t\t\tExplainMapMergeJob((MapMergeJob *) dependentJob, es);\n\t\t\t}\n\t\t}\n\n\t\tExplainCloseGroup(\"Dependent Jobs\", \"Dependent Jobs\", false, es);\n\t}\n\n\tExplainCloseGroup(\"MapMergeJob\", NULL, true, es);\n\n\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t{\n\t\tes->indent -= 3;\n\t}\n}\n\n\n/*\n * CompareTasksByFetchedExplainAnalyzeDuration is a helper function to compare two tasks by their execution duration.\n */\nstatic int\nCompareTasksByFetchedExplainAnalyzeDuration(const void *leftElement, const\n\t\t\t\t\t\t\t\t\t\t\tvoid *rightElement)\n{\n\tconst Task *leftTask = *((const Task **) leftElement);\n\tconst Task *rightTask = *((const Task **) rightElement);\n\n\tdouble leftTaskExecutionDuration = leftTask->fetchedExplainAnalyzeExecutionDuration;\n\tdouble rightTaskExecutionDuration = rightTask->fetchedExplainAnalyzeExecutionDuration;\n\n\tdouble diff = leftTaskExecutionDuration - rightTaskExecutionDuration;\n\tif (diff > 0)\n\t{\n\t\treturn -1;\n\t}\n\telse if (diff < 0)\n\t{\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\n\n/*\n * ExplainTaskList shows the remote EXPLAIN and execution time for the first task\n * in taskList, or all tasks if citus.explain_all_tasks is on.\n */\nstatic void\nExplainTaskList(CitusScanState *scanState, List *taskList, ExplainState *es,\n\t\t\t\tParamListInfo params)\n{\n\tList *remoteExplainList = NIL;\n\n\t/* if tasks are executed, we sort them by time; unless we are on a test env */\n\tif (es->analyze && ExplainAnalyzeSortMethod == EXPLAIN_ANALYZE_SORT_BY_TIME)\n\t{\n\t\t/* sort by execution duration only in case of ANALYZE */\n\t\ttaskList = SortList(taskList, CompareTasksByFetchedExplainAnalyzeDuration);\n\t}\n\telse\n\t{\n\t\t/* make sure that the output is consistent */\n\t\ttaskList = SortList(taskList, CompareTasksByTaskId);\n\t}\n\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tRemoteExplainPlan *remoteExplain = RemoteExplain(task, es, params);\n\t\tremoteExplainList = lappend(remoteExplainList, remoteExplain);\n\n\t\tif (!ExplainAllTasks)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tRemoteExplainPlan *remoteExplain = NULL;\n\tforboth_ptr(task, taskList, remoteExplain, remoteExplainList)\n\t{\n\t\tExplainTask(scanState, task, remoteExplain->placementIndex,\n\t\t\t\t\tremoteExplain->explainOutputList, es);\n\t}\n}\n\n\n/*\n * RemoteExplain fetches the remote EXPLAIN output for a single task.\n */\nstatic RemoteExplainPlan *\nRemoteExplain(Task *task, ExplainState *es, ParamListInfo params)\n{\n\t/*\n\t * For EXPLAIN EXECUTE we still use the old method, so task->fetchedExplainAnalyzePlan\n\t * can be NULL for some cases of es->analyze == true.\n\t */\n\tif (es->analyze && task->fetchedExplainAnalyzePlan)\n\t{\n\t\treturn GetSavedRemoteExplain(task, es);\n\t}\n\telse\n\t{\n\t\treturn FetchRemoteExplainFromWorkers(task, es, params);\n\t}\n}\n\n\n/*\n * GetSavedRemoteExplain creates a remote EXPLAIN output from information saved\n * in task.\n */\nstatic RemoteExplainPlan *\nGetSavedRemoteExplain(Task *task, ExplainState *es)\n{\n\tRemoteExplainPlan *remotePlan = (RemoteExplainPlan *) palloc0(\n\t\tsizeof(RemoteExplainPlan));\n\n\t/*\n\t * Similar to postgres' ExplainQuery(), we split by newline only for\n\t * text format.\n\t */\n\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t{\n\t\t/*\n\t\t * We limit the size of EXPLAIN plans to RSIZE_MAX_MEM (256MB).\n\t\t */\n\t\tremotePlan->explainOutputList = SplitString(task->fetchedExplainAnalyzePlan,\n\t\t\t\t\t\t\t\t\t\t\t\t\t'\\n', RSIZE_MAX_MEM);\n\t}\n\telse\n\t{\n\t\tStringInfo explainAnalyzeString = makeStringInfo();\n\t\tappendStringInfoString(explainAnalyzeString, task->fetchedExplainAnalyzePlan);\n\t\tremotePlan->explainOutputList = list_make1(explainAnalyzeString);\n\t}\n\n\tremotePlan->placementIndex = task->fetchedExplainAnalyzePlacementIndex;\n\n\treturn remotePlan;\n}\n\n\n/*\n * FetchRemoteExplainFromWorkers fetches the remote EXPLAIN output for a single\n * task by querying it from worker nodes. It tries each shard placement until\n * one succeeds or all failed.\n */\nstatic RemoteExplainPlan *\nFetchRemoteExplainFromWorkers(Task *task, ExplainState *es, ParamListInfo params)\n{\n\tList *taskPlacementList = task->taskPlacementList;\n\tint placementCount = list_length(taskPlacementList);\n\n\tRemoteExplainPlan *remotePlan = (RemoteExplainPlan *) palloc0(\n\t\tsizeof(RemoteExplainPlan));\n\n\tStringInfo explainQuery = BuildRemoteExplainQuery(TaskQueryString(task), es);\n\n\t/*\n\t * Use a coordinated transaction to ensure that we open a transaction block\n\t * such that we can set a savepoint.\n\t */\n\tUseCoordinatedTransaction();\n\n\tfor (int placementIndex = 0; placementIndex < placementCount; placementIndex++)\n\t{\n\t\tShardPlacement *taskPlacement = list_nth(taskPlacementList, placementIndex);\n\t\tint connectionFlags = 0;\n\n\t\tremotePlan->placementIndex = placementIndex;\n\n\t\tMultiConnection *connection = GetPlacementConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t taskPlacement, NULL);\n\n\t\t/*\n\t\t * This code-path doesn't support optional connections, so we don't expect\n\t\t * NULL connections.\n\t\t */\n\t\tAssert(connection != NULL);\n\n\t\t/* try other placements if we fail to connect this one */\n\t\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tRemoteTransactionBeginIfNecessary(connection);\n\n\t\t/*\n\t\t * Start a savepoint for the explain query. After running the explain\n\t\t * query, we will rollback to this savepoint. This saves us from side\n\t\t * effects of EXPLAIN ANALYZE on DML queries.\n\t\t */\n\t\tExecuteCriticalRemoteCommand(connection, \"SAVEPOINT citus_explain_savepoint\");\n\n\t\t/* run explain query */\n\t\tint numParams = params ? params->numParams : 0;\n\t\tOid *paramTypes = NULL;\n\t\tconst char **paramValues = NULL;\n\t\tPGresult *queryResult = NULL;\n\n\t\tif (params)\n\t\t{\n\t\t\tExtractParametersFromParamList(params, &paramTypes, &paramValues, false);\n\t\t}\n\n\t\tint sendStatus = SendRemoteCommandParams(connection, explainQuery->data,\n\t\t\t\t\t\t\t\t\t\t\t\t numParams, paramTypes, paramValues,\n\t\t\t\t\t\t\t\t\t\t\t\t false);\n\t\tif (sendStatus != 0)\n\t\t{\n\t\t\tqueryResult = GetRemoteCommandResult(connection, false);\n\t\t\tif (!IsResponseOK(queryResult))\n\t\t\t{\n\t\t\t\tPQclear(queryResult);\n\t\t\t\tForgetResults(connection);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\t/* read explain query results */\n\t\tremotePlan->explainOutputList = ReadFirstColumnAsText(queryResult);\n\n\t\tPQclear(queryResult);\n\t\tForgetResults(connection);\n\n\t\t/* rollback to the savepoint */\n\t\tExecuteCriticalRemoteCommand(connection,\n\t\t\t\t\t\t\t\t\t \"ROLLBACK TO SAVEPOINT citus_explain_savepoint\");\n\n\t\tif (remotePlan->explainOutputList != NIL)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn remotePlan;\n}\n\n\n/*\n * ExplainTask shows the EXPLAIN output for an single task. The output has been\n * fetched from the placement at index placementIndex. If explainOutputList is NIL,\n * then the EXPLAIN output could not be fetched from any placement.\n */\nstatic void\nExplainTask(CitusScanState *scanState, Task *task, int placementIndex,\n\t\t\tList *explainOutputList,\n\t\t\tExplainState *es)\n{\n\tExplainOpenGroup(\"Task\", NULL, true, es);\n\n\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t{\n\t\tappendStringInfoSpaces(es->str, es->indent * 2);\n\t\tappendStringInfo(es->str, \"->  Task\\n\");\n\t\tes->indent += 3;\n\t}\n\n\tif (es->verbose)\n\t{\n\t\tconst char *queryText = TaskQueryString(task);\n\t\tExplainPropertyText(\"Query\", queryText, es);\n\t}\n\n\tif (ShowReceivedTupleData(scanState, es))\n\t{\n\t\tExplainPropertyBytes(\"Tuple data received from node\",\n\t\t\t\t\t\t\t TaskReceivedTupleData(task),\n\t\t\t\t\t\t\t es);\n\t}\n\n\tif (explainOutputList != NIL)\n\t{\n\t\tList *taskPlacementList = task->taskPlacementList;\n\t\tShardPlacement *taskPlacement = list_nth(taskPlacementList, placementIndex);\n\n\t\tExplainTaskPlacement(taskPlacement, explainOutputList, es);\n\t}\n\telse\n\t{\n\t\tExplainPropertyText(\"Error\", \"Could not get remote plan.\", es);\n\t}\n\n\tExplainCloseGroup(\"Task\", NULL, true, es);\n\n\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t{\n\t\tes->indent -= 3;\n\t}\n}\n\n\n/*\n * ExplainTaskPlacement shows the EXPLAIN output for an individual task placement.\n * It corrects the indentation of the remote explain output to match the local\n * output.\n */\nstatic void\nExplainTaskPlacement(ShardPlacement *taskPlacement, List *explainOutputList,\n\t\t\t\t\t ExplainState *es)\n{\n\tint savedIndentation = es->indent;\n\tStringInfo nodeAddress = makeStringInfo();\n\tchar *nodeName = taskPlacement->nodeName;\n\tuint32 nodePort = taskPlacement->nodePort;\n\tconst char *nodeDatabase = CurrentDatabaseName();\n\tListCell *explainOutputCell = NULL;\n\tint rowIndex = 0;\n\n\tappendStringInfo(nodeAddress, \"host=%s port=%d dbname=%s\", nodeName, nodePort,\n\t\t\t\t\t nodeDatabase);\n\tExplainPropertyText(\"Node\", nodeAddress->data, es);\n\n\tExplainOpenGroup(\"Remote Plan\", \"Remote Plan\", false, es);\n\n\tif (es->format == EXPLAIN_FORMAT_JSON || es->format == EXPLAIN_FORMAT_YAML)\n\t{\n\t\t/* prevent appending the remote EXPLAIN on the same line */\n\t\tappendStringInfoChar(es->str, '\\n');\n\t}\n\n\tforeach(explainOutputCell, explainOutputList)\n\t{\n\t\tStringInfo rowString = (StringInfo) lfirst(explainOutputCell);\n\n\t\tint rowLength = strlen(rowString->data);\n\t\tchar *lineStart = rowString->data;\n\n\t\t/* parse the lines in the remote EXPLAIN for proper indentation */\n\t\twhile (lineStart < rowString->data + rowLength)\n\t\t{\n\t\t\t/* find the end-of-line */\n\t\t\tchar *lineEnd = strchr(lineStart, '\\n');\n\n\t\t\tif (lineEnd == NULL)\n\t\t\t{\n\t\t\t\t/* no end-of-line, use end of row string instead */\n\t\t\t\tlineEnd = rowString->data + rowLength;\n\t\t\t}\n\n\t\t\t/* convert line to a separate string */\n\t\t\t*lineEnd = '\\0';\n\n\t\t\t/* indentation that is applied to all lines */\n\t\t\tappendStringInfoSpaces(es->str, es->indent * 2);\n\n\t\t\tif (es->format == EXPLAIN_FORMAT_TEXT && rowIndex == 0)\n\t\t\t{\n\t\t\t\t/* indent the first line of the remote plan with an arrow */\n\t\t\t\tappendStringInfoString(es->str, \"->  \");\n\t\t\t\tes->indent += 2;\n\t\t\t}\n\n\t\t\t/* show line in the output */\n\t\t\tappendStringInfo(es->str, \"%s\\n\", lineStart);\n\n\t\t\t/* continue at the start of the next line */\n\t\t\tlineStart = lineEnd + 1;\n\t\t}\n\n\t\trowIndex++;\n\t}\n\n\tExplainCloseGroup(\"Remote Plan\", \"Remote Plan\", false, es);\n\n\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t{\n\t\tes->indent = savedIndentation;\n\t}\n}\n\n\n/*\n * BuildRemoteExplainQuery returns an EXPLAIN query string\n * to run on a worker node which explicitly contains all\n * the options in the explain state.\n */\nstatic StringInfo\nBuildRemoteExplainQuery(char *queryString, ExplainState *es)\n{\n\tStringInfo explainQuery = makeStringInfo();\n\tconst char *formatStr = ExplainFormatStr(es->format);\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tconst char *serializeStr = ExplainSerializeStr(es->serialize);\n#endif\n\n\n\tappendStringInfo(explainQuery,\n\t\t\t\t\t \"EXPLAIN (ANALYZE %s, VERBOSE %s, \"\n\t\t\t\t\t \"COSTS %s, BUFFERS %s, WAL %s, \"\n\t\t\t\t\t \"TIMING %s, SUMMARY %s, \"\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\t\t\t\t \"MEMORY %s, SERIALIZE %s, \"\n#endif\n\t\t\t\t\t \"FORMAT %s) %s\",\n\t\t\t\t\t es->analyze ? \"TRUE\" : \"FALSE\",\n\t\t\t\t\t es->verbose ? \"TRUE\" : \"FALSE\",\n\t\t\t\t\t es->costs ? \"TRUE\" : \"FALSE\",\n\t\t\t\t\t es->buffers ? \"TRUE\" : \"FALSE\",\n\t\t\t\t\t es->wal ? \"TRUE\" : \"FALSE\",\n\t\t\t\t\t es->timing ? \"TRUE\" : \"FALSE\",\n\t\t\t\t\t es->summary ? \"TRUE\" : \"FALSE\",\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\t\t\t\t es->memory ? \"TRUE\" : \"FALSE\",\n\t\t\t\t\t serializeStr,\n#endif\n\t\t\t\t\t formatStr,\n\t\t\t\t\t queryString);\n\n\treturn explainQuery;\n}\n\n\n/*\n * ExplainFormatStr converts the given explain format to string.\n */\nstatic const char *\nExplainFormatStr(ExplainFormat format)\n{\n\tswitch (format)\n\t{\n\t\tcase EXPLAIN_FORMAT_XML:\n\t\t{\n\t\t\treturn \"XML\";\n\t\t}\n\n\t\tcase EXPLAIN_FORMAT_JSON:\n\t\t{\n\t\t\treturn \"JSON\";\n\t\t}\n\n\t\tcase EXPLAIN_FORMAT_YAML:\n\t\t{\n\t\t\treturn \"YAML\";\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn \"TEXT\";\n\t\t}\n\t}\n}\n\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\n/*\n * ExplainSerializeStr converts the given explain serialize option to string.\n */\nstatic const char *\nExplainSerializeStr(ExplainSerializeOption serializeOption)\n{\n\tswitch (serializeOption)\n\t{\n\t\tcase EXPLAIN_SERIALIZE_NONE:\n\t\t{\n\t\t\treturn \"none\";\n\t\t}\n\n\t\tcase EXPLAIN_SERIALIZE_TEXT:\n\t\t{\n\t\t\treturn \"text\";\n\t\t}\n\n\t\tcase EXPLAIN_SERIALIZE_BINARY:\n\t\t{\n\t\t\treturn \"binary\";\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn \"none\";\n\t\t}\n\t}\n}\n\n\n#endif\n\n\n/*\n * worker_last_saved_explain_analyze returns the last saved EXPLAIN ANALYZE output of\n * a worker task query. It returns NULL if nothing has been saved yet.\n */\nDatum\nworker_last_saved_explain_analyze(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tif (SavedExplainPlan != NULL)\n\t{\n\t\tint columnCount = tupleDescriptor->natts;\n\t\tif (columnCount != 4)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"expected 4 output columns in definition of \"\n\t\t\t\t\t\t\t\t   \"worker_last_saved_explain_analyze, but got %d\",\n\t\t\t\t\t\t\t\t   columnCount)));\n\t\t}\n\n\t\tbool columnNulls[4] = { false };\n\t\tDatum columnValues[4] = {\n\t\t\tCStringGetTextDatum(SavedExplainPlan),\n\t\t\tFloat8GetDatum(SavedExecutionDurationMillisec),\n\t\t\tFloat8GetDatum(SavedExplainPlanNtuples),\n\t\t\tFloat8GetDatum(SavedExplainPlanNloops)\n\t\t};\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, columnValues, columnNulls);\n\t}\n\tPG_RETURN_DATUM(0);\n}\n\n\n/*\n * worker_save_query_explain_analyze executes and returns results of query while\n * saving its EXPLAIN ANALYZE to be fetched later.\n */\nDatum\nworker_save_query_explain_analyze(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *queryText = PG_GETARG_TEXT_P(0);\n\tchar *queryString = text_to_cstring(queryText);\n\tdouble executionDurationMillisec = 0.0;\n\tdouble executionTuples = 0;\n\tdouble executionLoops = 0;\n\n\tDatum explainOptions = PG_GETARG_DATUM(1);\n\tExplainState *es = NewExplainState();\n\tes->analyze = true;\n\n\t/* use the same defaults as NewExplainState() for following options */\n\tes->buffers = ExtractFieldBoolean(explainOptions, \"buffers\", es->buffers);\n\tes->wal = ExtractFieldBoolean(explainOptions, \"wal\", es->wal);\n\tes->costs = ExtractFieldBoolean(explainOptions, \"costs\", es->costs);\n\tes->summary = ExtractFieldBoolean(explainOptions, \"summary\", es->summary);\n\tes->verbose = ExtractFieldBoolean(explainOptions, \"verbose\", es->verbose);\n\tes->timing = ExtractFieldBoolean(explainOptions, \"timing\", es->timing);\n\tes->format = ExtractFieldExplainFormat(explainOptions, \"format\", es->format);\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tes->memory = ExtractFieldBoolean(explainOptions, \"memory\", es->memory);\n\tes->serialize = ExtractFieldExplainSerialize(explainOptions, \"serialize\",\n\t\t\t\t\t\t\t\t\t\t\t\t es->serialize);\n#endif\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\tDestReceiver *tupleStoreDest = CreateTuplestoreDestReceiver();\n\tSetTuplestoreDestReceiverParams(tupleStoreDest, tupleStore,\n\t\t\t\t\t\t\t\t\tCurrentMemoryContext, false, NULL, NULL);\n\n\tList *parseTreeList = pg_parse_query(queryString);\n\tif (list_length(parseTreeList) != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot EXPLAIN ANALYZE multiple queries\")));\n\t}\n\n\tRawStmt *parseTree = linitial(parseTreeList);\n\n\tParamListInfo boundParams = ExecutorBoundParams();\n\tint numParams = boundParams ? boundParams->numParams : 0;\n\tOid *paramTypes = NULL;\n\tconst char **paramValues = NULL;\n\n\tif (boundParams != NULL)\n\t{\n\t\tExtractParametersFromParamList(boundParams, &paramTypes, &paramValues, false);\n\t}\n\n\t/* resolve OIDs of unknown (user-defined) types */\n\tQuery *analyzedQuery = parse_analyze_varparams(parseTree, queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t   &paramTypes, &numParams, NULL);\n\n\t/* pg_rewrite_query is a wrapper around QueryRewrite with some debugging logic */\n\tList *queryList = pg_rewrite_query(analyzedQuery);\n\n\tif (list_length(queryList) != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot EXPLAIN ANALYZE a query rewritten \"\n\t\t\t\t\t\t\t   \"into multiple queries\")));\n\t}\n\n\tQuery *query = linitial(queryList);\n\n\tExplainBeginOutput(es);\n\n\t/* plan query and record planning stats */\n\tinstr_time planStart;\n\tinstr_time planDuration;\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tBufferUsage bufusage_start,\n\t\t\t\tbufusage;\n\tMemoryContextCounters mem_counters;\n\tMemoryContext planner_ctx = NULL;\n\tMemoryContext saved_ctx = NULL;\n\n\tif (es->memory)\n\t{\n\t\t/*\n\t\t * Create a new memory context to measure planner's memory consumption\n\t\t * accurately.  Note that if the planner were to be modified to use a\n\t\t * different memory context type, here we would be changing that to\n\t\t * AllocSet, which might be undesirable.  However, we don't have a way\n\t\t * to create a context of the same type as another, so we pray and\n\t\t * hope that this is OK.\n\t\t *\n\t\t * copied from explain.c\n\t\t */\n\t\tplanner_ctx = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\"explain analyze planner context\",\n\t\t\t\t\t\t\t\t\t\t\tALLOCSET_DEFAULT_SIZES);\n\t\tsaved_ctx = MemoryContextSwitchTo(planner_ctx);\n\t}\n\n\tif (es->buffers)\n\t{\n\t\tbufusage_start = pgBufferUsage;\n\t}\n#endif\n\n\tINSTR_TIME_SET_CURRENT(planStart);\n\n\tPlannedStmt *plan = pg_plan_query(query, NULL, CURSOR_OPT_PARALLEL_OK, NULL);\n\n\tINSTR_TIME_SET_CURRENT(planDuration);\n\tINSTR_TIME_SUBTRACT(planDuration, planStart);\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tif (es->memory)\n\t{\n\t\tMemoryContextSwitchTo(saved_ctx);\n\t\tMemoryContextMemConsumed(planner_ctx, &mem_counters);\n\t}\n\n\t/* calc differences of buffer counters. */\n\tif (es->buffers)\n\t{\n\t\tmemset(&bufusage, 0, sizeof(BufferUsage));\n\t\tBufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);\n\t}\n\n\t/* do the actual EXPLAIN ANALYZE */\n\tExplainWorkerPlan(plan, NULL, tupleStoreDest, es, queryString, boundParams, NULL,\n\t\t\t\t\t  &planDuration,\n\t\t\t\t\t  (es->buffers ? &bufusage : NULL),\n\t\t\t\t\t  (es->memory ? &mem_counters : NULL),\n\t\t\t\t\t  &executionDurationMillisec,\n\t\t\t\t\t  &executionTuples,\n\t\t\t\t\t  &executionLoops);\n#else\n\n\t/* do the actual EXPLAIN ANALYZE */\n\tExplainWorkerPlan(plan, NULL, tupleStoreDest, es, queryString, boundParams, NULL,\n\t\t\t\t\t  &planDuration, &executionDurationMillisec,\n\t\t\t\t\t  &executionTuples, &executionLoops);\n#endif\n\n\tExplainEndOutput(es);\n\n\t/* save EXPLAIN ANALYZE result to be fetched later */\n\tMemoryContext oldContext = MemoryContextSwitchTo(TopTransactionContext);\n\tFreeSavedExplainPlan();\n\n\tSavedExplainPlan = pstrdup(es->str->data);\n\tSavedExecutionDurationMillisec = executionDurationMillisec;\n\tSavedExplainPlanNtuples = executionTuples;\n\tSavedExplainPlanNloops = executionLoops;\n\n\tMemoryContextSwitchTo(oldContext);\n\n\tPG_RETURN_DATUM(0);\n}\n\n\n/*\n * FreeSavedExplainPlan frees allocated saved explain plan if any.\n */\nvoid\nFreeSavedExplainPlan(void)\n{\n\tif (SavedExplainPlan)\n\t{\n\t\tpfree(SavedExplainPlan);\n\t\tSavedExplainPlan = NULL;\n\t}\n}\n\n\n/*\n * ExtractFieldExplainFormat gets value of fieldName from jsonbDoc, or returns\n * defaultValue if it doesn't exist.\n */\nstatic ExplainFormat\nExtractFieldExplainFormat(Datum jsonbDoc, const char *fieldName, ExplainFormat\n\t\t\t\t\t\t  defaultValue)\n{\n\tDatum jsonbDatum = 0;\n\tbool found = ExtractFieldJsonbDatum(jsonbDoc, fieldName, &jsonbDatum);\n\tif (!found)\n\t{\n\t\treturn defaultValue;\n\t}\n\n\tconst char *formatStr = DatumGetCString(DirectFunctionCall1(jsonb_out, jsonbDatum));\n\tif (pg_strcasecmp(formatStr, \"\\\"text\\\"\") == 0)\n\t{\n\t\treturn EXPLAIN_FORMAT_TEXT;\n\t}\n\telse if (pg_strcasecmp(formatStr, \"\\\"xml\\\"\") == 0)\n\t{\n\t\treturn EXPLAIN_FORMAT_XML;\n\t}\n\telse if (pg_strcasecmp(formatStr, \"\\\"yaml\\\"\") == 0)\n\t{\n\t\treturn EXPLAIN_FORMAT_YAML;\n\t}\n\telse if (pg_strcasecmp(formatStr, \"\\\"json\\\"\") == 0)\n\t{\n\t\treturn EXPLAIN_FORMAT_JSON;\n\t}\n\n\tereport(ERROR, (errmsg(\"Invalid explain analyze format: %s\", formatStr)));\n\treturn 0;\n}\n\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\n/*\n * ExtractFieldExplainSerialize gets value of fieldName from jsonbDoc, or returns\n * defaultValue if it doesn't exist.\n */\nstatic ExplainSerializeOption\nExtractFieldExplainSerialize(Datum jsonbDoc, const char *fieldName, ExplainSerializeOption\n\t\t\t\t\t\t\t defaultValue)\n{\n\tDatum jsonbDatum = 0;\n\tbool found = ExtractFieldJsonbDatum(jsonbDoc, fieldName, &jsonbDatum);\n\tif (!found)\n\t{\n\t\treturn defaultValue;\n\t}\n\n\tconst char *serializeStr = DatumGetCString(DirectFunctionCall1(jsonb_out,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   jsonbDatum));\n\tif (pg_strcasecmp(serializeStr, \"\\\"none\\\"\") == 0)\n\t{\n\t\treturn EXPLAIN_SERIALIZE_NONE;\n\t}\n\telse if (pg_strcasecmp(serializeStr, \"\\\"off\\\"\") == 0)\n\t{\n\t\treturn EXPLAIN_SERIALIZE_NONE;\n\t}\n\telse if (pg_strcasecmp(serializeStr, \"\\\"text\\\"\") == 0)\n\t{\n\t\treturn EXPLAIN_SERIALIZE_TEXT;\n\t}\n\telse if (pg_strcasecmp(serializeStr, \"\\\"binary\\\"\") == 0)\n\t{\n\t\treturn EXPLAIN_SERIALIZE_BINARY;\n\t}\n\n\tereport(ERROR, (errmsg(\"Invalid explain analyze serialize: %s\", serializeStr)));\n\treturn 0;\n}\n\n\n#endif\n\n\n/*\n * CitusExplainOneQuery is the executor hook that is called when\n * postgres wants to explain a query.\n */\nvoid\nCitusExplainOneQuery(Query *query, int cursorOptions, IntoClause *into,\n\t\t\t\t\t ExplainState *es, const char *queryString, ParamListInfo params,\n\t\t\t\t\t QueryEnvironment *queryEnv)\n{\n\t/* save the flags of current EXPLAIN command */\n\tCurrentDistributedQueryExplainOptions.costs = es->costs;\n\tCurrentDistributedQueryExplainOptions.buffers = es->buffers;\n\tCurrentDistributedQueryExplainOptions.wal = es->wal;\n\tCurrentDistributedQueryExplainOptions.verbose = es->verbose;\n\tCurrentDistributedQueryExplainOptions.summary = es->summary;\n\tCurrentDistributedQueryExplainOptions.timing = es->timing;\n\tCurrentDistributedQueryExplainOptions.format = es->format;\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tCurrentDistributedQueryExplainOptions.memory = es->memory;\n\tCurrentDistributedQueryExplainOptions.serialize = es->serialize;\n#endif\n\n\t/* rest is copied from ExplainOneQuery() */\n\tinstr_time planstart,\n\t\t\t   planduration;\n\tBufferUsage bufusage_start,\n\t\t\t\tbufusage;\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tMemoryContextCounters mem_counters;\n\tMemoryContext planner_ctx = NULL;\n\tMemoryContext saved_ctx = NULL;\n\n\tif (es->memory)\n\t{\n\t\t/* copy paste from postgres code */\n\t\tplanner_ctx = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\"explain analyze planner context\",\n\t\t\t\t\t\t\t\t\t\t\tALLOCSET_DEFAULT_SIZES);\n\t\tsaved_ctx = MemoryContextSwitchTo(planner_ctx);\n\t}\n#endif\n\n\tif (es->buffers)\n\t{\n\t\tbufusage_start = pgBufferUsage;\n\t}\n\n\tINSTR_TIME_SET_CURRENT(planstart);\n\n\t/*\n\t * We should not hide any objects while explaining some query to not break\n\t * postgres vanilla tests.\n\t *\n\t * The filter 'is_citus_depended_object' is added to explain result\n\t * and causes some tests to fail if HideCitusDependentObjects is true.\n\t * Therefore, we disable HideCitusDependentObjects until the current transaction\n\t * ends.\n\t *\n\t * We do not use security quals because a postgres vanilla test fails\n\t * with a change of order for its result.\n\t */\n\tSetLocalHideCitusDependentObjectsDisabledWhenAlreadyEnabled();\n\n\t/* plan the query */\n\tPlannedStmt *plan = pg_plan_query(query, NULL, cursorOptions, params);\n\tINSTR_TIME_SET_CURRENT(planduration);\n\tINSTR_TIME_SUBTRACT(planduration, planstart);\n\n\t/* calc differences of buffer counters. */\n\tif (es->buffers)\n\t{\n\t\tmemset(&bufusage, 0, sizeof(BufferUsage));\n\t\tBufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);\n\t}\n\n/* capture memory stats on PG17+ */\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tif (es->memory)\n\t{\n\t\tMemoryContextSwitchTo(saved_ctx);\n\t\tMemoryContextMemConsumed(planner_ctx, &mem_counters);\n\t}\n#endif\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\n\t/* PostgreSQL 17 signature (9 args: includes mem_counters) */\n\tExplainOnePlan(\n\t\tplan,\n\t\tinto,\n\t\tes,\n\t\tqueryString,\n\t\tparams,\n\t\tqueryEnv,\n\t\t&planduration,\n\t\t(es->buffers ? &bufusage : NULL),\n\t\t(es->memory ? &mem_counters : NULL)\n\t\t);\n#else\n\tExplainOnePlan(\n\t\tplan,\n\t\tinto,\n\t\tes,\n\t\tqueryString,\n\t\tparams,\n\t\tqueryEnv,\n\t\t&planduration,\n\t\t(es->buffers ? &bufusage : NULL)\n\t\t);\n#endif\n}\n\n\n/*\n * CreateExplainAnlyzeDestination creates a destination suitable for collecting\n * explain analyze output from workers.\n */\nstatic TupleDestination *\nCreateExplainAnlyzeDestination(Task *task, TupleDestination *taskDest)\n{\n\tExplainAnalyzeDestination *tupleDestination = palloc0(\n\t\tsizeof(ExplainAnalyzeDestination));\n\ttupleDestination->originalTask = task;\n\ttupleDestination->originalTaskDestination = taskDest;\n\n\tTupleDesc lastSavedExplainAnalyzeTupDesc = CreateTemplateTupleDesc(4);\n\n\tTupleDescInitEntry(lastSavedExplainAnalyzeTupDesc, 1, \"explain analyze\", TEXTOID, 0,\n\t\t\t\t\t   0);\n\tTupleDescInitEntry(lastSavedExplainAnalyzeTupDesc, 2, \"duration\", FLOAT8OID, 0, 0);\n\tTupleDescInitEntry(lastSavedExplainAnalyzeTupDesc, 3, \"ntuples\", FLOAT8OID, 0, 0);\n\tTupleDescInitEntry(lastSavedExplainAnalyzeTupDesc, 4, \"nloops\", FLOAT8OID, 0, 0);\n\n\ttupleDestination->lastSavedExplainAnalyzeTupDesc = lastSavedExplainAnalyzeTupDesc;\n\n\ttupleDestination->pub.putTuple = ExplainAnalyzeDestPutTuple;\n\ttupleDestination->pub.tupleDescForQuery = ExplainAnalyzeDestTupleDescForQuery;\n\n\treturn (TupleDestination *) tupleDestination;\n}\n\n\n/*\n * EnsureExplainOutputCapacity is to ensure capacity for new entries. Input\n * parameter requiredSize is minimum number of elements needed.\n */\nstatic void\nEnsureExplainOutputCapacity(int requiredSize)\n{\n\tif (requiredSize < TotalExplainOutputCapacity)\n\t{\n\t\treturn;\n\t}\n\n\tint newCapacity =\n\t\t(TotalExplainOutputCapacity == 0) ? 32 : TotalExplainOutputCapacity * 2;\n\n\twhile (newCapacity <= requiredSize)\n\t{\n\t\tnewCapacity *= 2;\n\t}\n\n\tif (SubPlanExplainOutput == NULL)\n\t{\n\t\tSubPlanExplainOutput =\n\t\t\t(SubPlanExplainOutputData *) MemoryContextAllocZero(\n\t\t\t\tSubPlanExplainAnalyzeContext,\n\t\t\t\tnewCapacity *\n\t\t\t\tsizeof(SubPlanExplainOutputData));\n\t}\n\telse\n\t{\n\t\t/* Use repalloc and manually zero the new memory */\n\t\tint oldSize = TotalExplainOutputCapacity * sizeof(SubPlanExplainOutputData);\n\t\tint newSize = newCapacity * sizeof(SubPlanExplainOutputData);\n\n\t\tSubPlanExplainOutput =\n\t\t\t(SubPlanExplainOutputData *) repalloc(SubPlanExplainOutput, newSize);\n\n\t\t/* Zero out the newly allocated memory */\n\t\tMemSet((char *) SubPlanExplainOutput + oldSize, 0, newSize - oldSize);\n\t}\n\n\tTotalExplainOutputCapacity = newCapacity;\n}\n\n\n/*\n * ExplainAnalyzeDestPutTuple implements TupleDestination->putTuple\n * for ExplainAnalyzeDestination.\n */\nstatic void\nExplainAnalyzeDestPutTuple(TupleDestination *self, Task *task,\n\t\t\t\t\t\t   int placementIndex, int queryNumber,\n\t\t\t\t\t\t   HeapTuple heapTuple, uint64 tupleLibpqSize)\n{\n\tuint32 taskId = task->taskId;\n\n\tExplainAnalyzeDestination *tupleDestination = (ExplainAnalyzeDestination *) self;\n\tif (queryNumber == 0)\n\t{\n\t\tTupleDestination *originalTupDest = tupleDestination->originalTaskDestination;\n\t\toriginalTupDest->putTuple(originalTupDest, task, placementIndex, 0, heapTuple,\n\t\t\t\t\t\t\t\t  tupleLibpqSize);\n\t\ttupleDestination->originalTask->totalReceivedTupleData += tupleLibpqSize;\n\n\t\tif (SubPlanExplainAnalyzeContext)\n\t\t{\n\t\t\tEnsureExplainOutputCapacity(taskId + 1);\n\t\t\tSubPlanExplainOutput[taskId].totalReceivedTupleData =\n\t\t\t\ttupleDestination->originalTask->totalReceivedTupleData;\n\t\t}\n\t}\n\telse if (queryNumber == 1)\n\t{\n\t\tbool isNull = false;\n\t\tTupleDesc tupDesc = tupleDestination->lastSavedExplainAnalyzeTupDesc;\n\t\tDatum explainAnalyze = heap_getattr(heapTuple, 1, tupDesc, &isNull);\n\n\t\tif (isNull)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\n\t\t\t\t\t\t\t\t  \"received null explain analyze output from worker\")));\n\t\t\treturn;\n\t\t}\n\n\t\tDatum executionDuration = heap_getattr(heapTuple, 2, tupDesc, &isNull);\n\t\tDatum executionTuples = heap_getattr(heapTuple, 3, tupDesc, &isNull);\n\t\tDatum executionLoops = heap_getattr(heapTuple, 4, tupDesc, &isNull);\n\n\t\tif (isNull)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"received null execution time from worker\")));\n\t\t\treturn;\n\t\t}\n\n\t\tchar *fetchedExplainAnalyzePlan = TextDatumGetCString(explainAnalyze);\n\t\tdouble fetchedExplainAnalyzeExecutionDuration = DatumGetFloat8(executionDuration);\n\t\tdouble fetchedExplainAnalyzeTuples = DatumGetFloat8(executionTuples);\n\t\tdouble fetchedExplainAnalyzeLoops = DatumGetFloat8(executionLoops);\n\n\t\t/*\n\t\t * Allocate fetchedExplainAnalyzePlan in the same context as the Task, since we are\n\t\t * currently in execution context and a Task can span multiple executions.\n\t\t *\n\t\t * Although we won't reuse the same value in a future execution, but we have\n\t\t * calls to CheckNodeCopyAndSerialization() which asserts copy functions of the task\n\t\t * work as expected, which will try to copy this value in a future execution.\n\t\t *\n\t\t * Why don't we just allocate this field in executor context and reset it before\n\t\t * the next execution? Because when an error is raised we can skip pretty much most\n\t\t * of the meaningful places that we can insert the reset.\n\t\t *\n\t\t * TODO: Take all EXPLAIN ANALYZE related fields out of Task and store them in a\n\t\t * Task to ExplainAnalyzePrivate mapping in multi_explain.c, so we don't need to\n\t\t * do these hacky memory context management tricks.\n\t\t */\n\t\tMemoryContext taskContext = GetMemoryChunkContext(tupleDestination->originalTask);\n\n\t\ttupleDestination->originalTask->fetchedExplainAnalyzePlan =\n\t\t\tMemoryContextStrdup(taskContext, fetchedExplainAnalyzePlan);\n\t\ttupleDestination->originalTask->fetchedExplainAnalyzePlacementIndex =\n\t\t\tplacementIndex;\n\t\ttupleDestination->originalTask->fetchedExplainAnalyzeExecutionDuration =\n\t\t\tfetchedExplainAnalyzeExecutionDuration;\n\n\t\t/* We should build tupleDestination in subPlan similar to the above */\n\t\tif (SubPlanExplainAnalyzeContext)\n\t\t{\n\t\t\tEnsureExplainOutputCapacity(taskId + 1);\n\t\t\tSubPlanExplainOutput[taskId].explainOutput =\n\t\t\t\tMemoryContextStrdup(SubPlanExplainAnalyzeContext,\n\t\t\t\t\t\t\t\t\tfetchedExplainAnalyzePlan);\n\t\t\tSubPlanExplainOutput[taskId].executionDuration =\n\t\t\t\tfetchedExplainAnalyzeExecutionDuration;\n\t\t\tSubPlanExplainOutput[taskId].executionNtuples = fetchedExplainAnalyzeTuples;\n\t\t\tSubPlanExplainOutput[taskId].executionNloops = fetchedExplainAnalyzeLoops;\n\t\t\tNumTasksOutput++;\n\t\t}\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot get EXPLAIN ANALYZE of multiple queries\"),\n\t\t\t\t\t\terrdetail(\"while receiving tuples for query %d\", queryNumber)));\n\t}\n}\n\n\n/*\n * ResetExplainAnalyzeData reset fields in Task that are used by multi_explain.c\n */\nvoid\nResetExplainAnalyzeData(List *taskList)\n{\n\tTask *task = NULL;\n\tforeach_declared_ptr(task, taskList)\n\t{\n\t\tif (task->fetchedExplainAnalyzePlan != NULL)\n\t\t{\n\t\t\tpfree(task->fetchedExplainAnalyzePlan);\n\t\t}\n\n\t\ttask->totalReceivedTupleData = 0;\n\t\ttask->fetchedExplainAnalyzePlacementIndex = 0;\n\t\ttask->fetchedExplainAnalyzePlan = NULL;\n\t}\n}\n\n\n/*\n * ExplainAnalyzeDestTupleDescForQuery implements TupleDestination->tupleDescForQuery\n * for ExplainAnalyzeDestination.\n */\nstatic TupleDesc\nExplainAnalyzeDestTupleDescForQuery(TupleDestination *self, int queryNumber)\n{\n\tExplainAnalyzeDestination *tupleDestination = (ExplainAnalyzeDestination *) self;\n\tif (queryNumber == 0)\n\t{\n\t\tTupleDestination *originalTupDest = tupleDestination->originalTaskDestination;\n\t\treturn originalTupDest->tupleDescForQuery(originalTupDest, 0);\n\t}\n\telse if (queryNumber == 1)\n\t{\n\t\treturn tupleDestination->lastSavedExplainAnalyzeTupDesc;\n\t}\n\n\tereport(ERROR, (errmsg(\"cannot get EXPLAIN ANALYZE of multiple queries\"),\n\t\t\t\t\terrdetail(\"while requesting for tuple descriptor of query %d\",\n\t\t\t\t\t\t\t  queryNumber)));\n\treturn NULL;\n}\n\n\n/*\n * RequestedForExplainAnalyze returns true if we should get the EXPLAIN ANALYZE\n * output for the given custom scan node.\n */\nbool\nRequestedForExplainAnalyze(CitusScanState *node)\n{\n\t/*\n\t * When running a distributed plan—either the root plan or a subplan’s\n\t * distributed fragment—we need to know if we’re under EXPLAIN ANALYZE.\n\t * Subplans can’t receive the EXPLAIN ANALYZE flag directly, so we use\n\t * SubPlanExplainAnalyzeContext as a flag to indicate that context.\n\t */\n\treturn (node->customScanState.ss.ps.state->es_instrument != 0) ||\n\t\t   (SubPlanLevel > 0 && SubPlanExplainAnalyzeContext);\n}\n\n\n/*\n * ExplainAnalyzeTaskList returns a task list suitable for explain analyze. After executing\n * these tasks, fetchedExplainAnalyzePlan of originalTaskList should be populated.\n */\nList *\nExplainAnalyzeTaskList(List *originalTaskList,\n\t\t\t\t\t   TupleDestination *defaultTupleDest,\n\t\t\t\t\t   TupleDesc tupleDesc,\n\t\t\t\t\t   ParamListInfo params)\n{\n\tList *explainAnalyzeTaskList = NIL;\n\tTask *originalTask = NULL;\n\n\tforeach_declared_ptr(originalTask, originalTaskList)\n\t{\n\t\tif (originalTask->queryCount != 1)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot get EXPLAIN ANALYZE of multiple queries\")));\n\t\t}\n\n\t\tTask *explainAnalyzeTask = copyObject(originalTask);\n\t\tconst char *queryString = TaskQueryString(explainAnalyzeTask);\n\t\tParamListInfo taskParams = params;\n\n\t\t/*\n\t\t * We will not send parameters if they have already been resolved in the query\n\t\t * string.\n\t\t */\n\t\tif (explainAnalyzeTask->parametersInQueryStringResolved)\n\t\t{\n\t\t\ttaskParams = NULL;\n\t\t}\n\n\t\tchar *wrappedQuery = WrapQueryForExplainAnalyze(queryString, tupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttaskParams);\n\t\tchar *fetchQuery = FetchPlanQueryForExplainAnalyze(queryString, taskParams);\n\n\t\tSetTaskQueryStringList(explainAnalyzeTask, list_make2(wrappedQuery, fetchQuery));\n\n\t\tTupleDestination *originalTaskDest = originalTask->tupleDest ?\n\t\t\t\t\t\t\t\t\t\t\t originalTask->tupleDest :\n\t\t\t\t\t\t\t\t\t\t\t defaultTupleDest;\n\n\t\texplainAnalyzeTask->tupleDest =\n\t\t\tCreateExplainAnlyzeDestination(originalTask, originalTaskDest);\n\n\t\texplainAnalyzeTaskList = lappend(explainAnalyzeTaskList, explainAnalyzeTask);\n\t}\n\n\treturn explainAnalyzeTaskList;\n}\n\n\n/*\n * WrapQueryForExplainAnalyze wraps a query into a worker_save_query_explain_analyze()\n * call so we can fetch its explain analyze after its execution.\n */\nstatic char *\nWrapQueryForExplainAnalyze(const char *queryString, TupleDesc tupleDesc,\n\t\t\t\t\t\t   ParamListInfo params)\n{\n\tStringInfo columnDef = makeStringInfo();\n\tfor (int columnIndex = 0; columnIndex < tupleDesc->natts; columnIndex++)\n\t{\n\t\tif (columnIndex != 0)\n\t\t{\n\t\t\tappendStringInfoString(columnDef, \", \");\n\t\t}\n\n\t\tForm_pg_attribute attr = TupleDescAttr(tupleDesc, columnIndex);\n\t\tchar *attrType = format_type_extended(attr->atttypid, attr->atttypmod,\n\t\t\t\t\t\t\t\t\t\t\t  FORMAT_TYPE_TYPEMOD_GIVEN |\n\t\t\t\t\t\t\t\t\t\t\t  FORMAT_TYPE_FORCE_QUALIFY);\n\n\t\tappendStringInfo(columnDef, \"field_%d %s\", columnIndex, attrType);\n\t}\n\n\t/*\n\t * column definition cannot be empty, so create a dummy column definition for\n\t * queries with no results.\n\t */\n\tif (tupleDesc->natts == 0)\n\t{\n\t\tappendStringInfo(columnDef, \"dummy_field int\");\n\t}\n\n\tStringInfo explainOptions = makeStringInfo();\n\tappendStringInfo(explainOptions,\n\t\t\t\t\t \"{\\\"verbose\\\": %s, \\\"costs\\\": %s, \\\"buffers\\\": %s, \\\"wal\\\": %s, \"\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\t\t\t\t \"\\\"memory\\\": %s, \\\"serialize\\\": \\\"%s\\\", \"\n#endif\n\t\t\t\t\t \"\\\"timing\\\": %s, \\\"summary\\\": %s, \\\"format\\\": \\\"%s\\\"}\",\n\t\t\t\t\t CurrentDistributedQueryExplainOptions.verbose ? \"true\" : \"false\",\n\t\t\t\t\t CurrentDistributedQueryExplainOptions.costs ? \"true\" : \"false\",\n\t\t\t\t\t CurrentDistributedQueryExplainOptions.buffers ? \"true\" : \"false\",\n\t\t\t\t\t CurrentDistributedQueryExplainOptions.wal ? \"true\" : \"false\",\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\t\t\t\t CurrentDistributedQueryExplainOptions.memory ? \"true\" : \"false\",\n\t\t\t\t\t ExplainSerializeStr(CurrentDistributedQueryExplainOptions.serialize),\n#endif\n\t\t\t\t\t CurrentDistributedQueryExplainOptions.timing ? \"true\" : \"false\",\n\t\t\t\t\t CurrentDistributedQueryExplainOptions.summary ? \"true\" : \"false\",\n\t\t\t\t\t ExplainFormatStr(CurrentDistributedQueryExplainOptions.format));\n\n\tStringInfo wrappedQuery = makeStringInfo();\n\n\t/*\n\t * We do not include dummy column if original query didn't return any columns.\n\t * Otherwise, number of columns that original query returned wouldn't match\n\t * number of columns returned by worker_save_query_explain_analyze.\n\t */\n\tchar *workerSaveQueryFetchCols = (tupleDesc->natts == 0) ? \"\" : \"*\";\n\n\tif (params != NULL)\n\t{\n\t\t/*\n\t\t * Add a dummy CTE to ensure all parameters are referenced, such that their\n\t\t * types can be resolved.\n\t\t */\n\t\tappendStringInfo(wrappedQuery, \"WITH unused AS (%s) \",\n\t\t\t\t\t\t ParameterResolutionSubquery(params));\n\t}\n\n\tappendStringInfo(wrappedQuery,\n\t\t\t\t\t \"SELECT %s FROM worker_save_query_explain_analyze(%s, %s) AS (%s)\",\n\t\t\t\t\t workerSaveQueryFetchCols,\n\t\t\t\t\t quote_literal_cstr(queryString),\n\t\t\t\t\t quote_literal_cstr(explainOptions->data),\n\t\t\t\t\t columnDef->data);\n\n\treturn wrappedQuery->data;\n}\n\n\n/*\n * FetchPlanQueryForExplainAnalyze generates a query to fetch the plan saved\n * by worker_save_query_explain_analyze from the worker.\n */\nstatic char *\nFetchPlanQueryForExplainAnalyze(const char *queryString, ParamListInfo params)\n{\n\tStringInfo fetchQuery = makeStringInfo();\n\n\tif (params != NULL)\n\t{\n\t\t/*\n\t\t * Add a dummy CTE to ensure all parameters are referenced, such that their\n\t\t * types can be resolved.\n\t\t */\n\t\tappendStringInfo(fetchQuery, \"WITH unused AS (%s) \",\n\t\t\t\t\t\t ParameterResolutionSubquery(params));\n\t}\n\n\tappendStringInfoString(fetchQuery,\n\t\t\t\t\t\t   \"SELECT explain_analyze_output, execution_duration, \"\n\t\t\t\t\t\t   \"execution_ntuples, execution_nloops \"\n\t\t\t\t\t\t   \"FROM worker_last_saved_explain_analyze()\");\n\n\treturn fetchQuery->data;\n}\n\n\n/*\n * ParameterResolutionSubquery generates a subquery that returns all parameters\n * in params with explicit casts to their type names. This can be used in cases\n * where we use custom type parameters that are not directly referenced.\n */\nstatic char *\nParameterResolutionSubquery(ParamListInfo params)\n{\n\tStringInfo paramsQuery = makeStringInfo();\n\n\tappendStringInfo(paramsQuery, \"SELECT\");\n\n\tfor (int paramIndex = 0; paramIndex < params->numParams; paramIndex++)\n\t{\n\t\tParamExternData *param = &params->params[paramIndex];\n\t\tchar *typeName = format_type_extended(param->ptype, -1,\n\t\t\t\t\t\t\t\t\t\t\t  FORMAT_TYPE_FORCE_QUALIFY);\n\n\t\tappendStringInfo(paramsQuery, \"%s $%d::%s\",\n\t\t\t\t\t\t paramIndex > 0 ? \",\" : \"\",\n\t\t\t\t\t\t paramIndex + 1, typeName);\n\t}\n\n\treturn paramsQuery->data;\n}\n\n\n/*\n * SplitString splits the given string by the given delimiter.\n *\n * Why not use strtok_s()? Its signature and semantics are difficult to understand.\n *\n * Why not use strchr() (similar to do_text_output_multiline)? Although not banned,\n * it isn't safe if by any chance str is not null-terminated.\n */\nstatic List *\nSplitString(const char *str, char delimiter, int maxLength)\n{\n\tsize_t len = strnlen(str, maxLength);\n\tif (len == 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *tokenList = NIL;\n\tStringInfo token = makeStringInfo();\n\n\tfor (size_t index = 0; index < len; index++)\n\t{\n\t\tif (str[index] == delimiter)\n\t\t{\n\t\t\ttokenList = lappend(tokenList, token);\n\t\t\ttoken = makeStringInfo();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfoChar(token, str[index]);\n\t\t}\n\t}\n\n\t/* append last token */\n\ttokenList = lappend(tokenList, token);\n\n\treturn tokenList;\n}\n\n\n/* below are private functions copied from explain.c */\n\n\n/* *INDENT-OFF* */\n/*\n * ExplainOneQuery -\n *\t  print out the execution plan for one Query\n *\n * \"into\" is NULL unless we are explaining the contents of a CreateTableAsStmt.\n */\nstatic void\nExplainOneQuery(Query *query, int cursorOptions,\n\t\t\t\tIntoClause *into, ExplainState *es,\n\t\t\t\tconst char *queryString, ParamListInfo params,\n\t\t\t\tQueryEnvironment *queryEnv)\n{\n\t/* if an advisor plugin is present, let it manage things */\n\tif (ExplainOneQuery_hook)\n\t{\n\t\t(*ExplainOneQuery_hook) (query, cursorOptions, into, es,\n\t\t\t\t\t\t\t\t queryString, params, queryEnv);\n\t}\n\telse\n\t{\n\t\tinstr_time\tplanstart,\n\t\t\t\t\tplanduration;\n\t\tBufferUsage bufusage_start,\n\t\t\t    bufusage;\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\tMemoryContextCounters mem_counters;\n\t\tMemoryContext planner_ctx = NULL;\n\t\tMemoryContext saved_ctx = NULL;\n\n\t\tif (es->memory)\n\t\t{\n\t\t\t/* copy paste from postgres code */\n\t\t\tplanner_ctx = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\"explain analyze planner context\",\n\t\t\t\t\t\t\t\t\t\t\t\tALLOCSET_DEFAULT_SIZES);\n\t\t\tsaved_ctx = MemoryContextSwitchTo(planner_ctx);\n\t\t}\n#endif\n\n\t\tif (es->buffers)\n\t\t\tbufusage_start = pgBufferUsage;\n\t\tINSTR_TIME_SET_CURRENT(planstart);\n\n\t\t/* plan the query */\n\t\tPlannedStmt *plan = pg_plan_query(query, NULL, cursorOptions, params);\n\n\t\tINSTR_TIME_SET_CURRENT(planduration);\n\t\tINSTR_TIME_SUBTRACT(planduration, planstart);\n\n\t\t/* calc differences of buffer counters. */\n\t\tif (es->buffers)\n\t\t{\n\t\t\tmemset(&bufusage, 0, sizeof(BufferUsage));\n\t\t\tBufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);\n\t\t}\n\n/* 1) Capture memory counters on PG17+ only once: */\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\tif (es->memory)\n\t\t{\n\t\t\tMemoryContextSwitchTo(saved_ctx);\n\t\t\tMemoryContextMemConsumed(planner_ctx, &mem_counters);\n\t\t}\n#endif\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\tExplainOnePlan(\n\t\t\tplan,\n\t\t\tinto,\n\t\t\tes,\n\t\t\tqueryString,\n\t\t\tparams,\n\t\t\tqueryEnv,\n\t\t\t&planduration,\n\t\t\t(es->buffers  ? &bufusage    : NULL),\n\t\t\t(es->memory   ? &mem_counters: NULL)\n\t\t);\n#else\n\t\tExplainOnePlan(\n\t\t\tplan,\n\t\t\tinto,\n\t\t\tes,\n\t\t\tqueryString,\n\t\t\tparams,\n\t\t\tqueryEnv,\n\t\t\t&planduration,\n\t\t\t(es->buffers ? &bufusage : NULL)\n\t\t);\n#endif\n\t}\n}\n\n\n/*\n * PlanStateAnalyzeWalker Tree walker callback that visits each PlanState node in the\n * plan tree and extracts analyze statistics from CustomScanState tasks using\n * ExtractAnalyzeStats. Always returns false to recurse into all children.\n */\nstatic bool\nPlanStateAnalyzeWalker(PlanState *planState, void *ctx)\n{\n\tDistributedSubPlan *subplan = (DistributedSubPlan *) ctx;\n\tExtractAnalyzeStats(subplan, planState);\n\treturn false;\n}\n\n\n/*\n * ExplainWorkerPlan produces explain output into es. If es->analyze, it also executes\n * the given plannedStmt and sends the results to dest. It puts total time to execute in\n * executionDurationMillisec.\n *\n * This is based on postgres' ExplainOnePlan(). We couldn't use an IntoClause to store results\n * into tupleStore, so we had to copy the same functionality with some minor changes.\n *\n * Keeping the formatting to make comparing with the ExplainOnePlan() easier.\n *\n * TODO: Send a PR to postgres to change ExplainOnePlan's API to use a more generic result\n * destination.\n */\nstatic void\nExplainWorkerPlan(PlannedStmt *plannedstmt, DistributedSubPlan *subPlan, DestReceiver *dest, ExplainState *es,\n\t\t\t\t  const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv,\n\t\t\t\t  const instr_time *planduration,\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\t\t\t  const BufferUsage *bufusage,\n\t\t\t      const MemoryContextCounters *mem_counters,\n#endif\n\t\t\t\t  double *executionDurationMillisec,\n\t\t\t\t  double *executionTuples,\n\t\t\t\t  double *executionLoops)\n{\n\tQueryDesc  *queryDesc;\n\tinstr_time\tstarttime;\n\tdouble\t\ttotaltime = 0;\n\tint\t\t\teflags;\n\tint\t\t\tinstrument_option = 0;\n\t/* Sub-plan already executed; skipping execution */\n\tbool executeQuery = (es->analyze && !subPlan);\n\tbool executeSubplan = (es->analyze && subPlan);\n\n\tAssert(plannedstmt->commandType != CMD_UTILITY);\n\n\tif (es->analyze && es->timing)\n\t\tinstrument_option |= INSTRUMENT_TIMER;\n\telse if (es->analyze)\n\t\tinstrument_option |= INSTRUMENT_ROWS;\n\n\tif (es->buffers)\n\t\tinstrument_option |= INSTRUMENT_BUFFERS;\n\n\tif (es->wal)\n\t\tinstrument_option |= INSTRUMENT_WAL;\n\n\t/*\n\t * We always collect timing for the entire statement, even when node-level\n\t * timing is off, so we don't look at es->timing here.  (We could skip\n\t * this if !es->summary, but it's hardly worth the complication.)\n\t */\n\tINSTR_TIME_SET_CURRENT(starttime);\n\n\t/*\n\t * Use a snapshot with an updated command ID to ensure this query sees\n\t * results of any previously executed queries.\n\t */\n\tPushCopiedSnapshot(GetActiveSnapshot());\n\tUpdateActiveSnapshotCommandId();\n\n\t/* Create a QueryDesc for the query */\n\tqueryDesc = CreateQueryDesc(\n\t\tplannedstmt,    /* PlannedStmt *plannedstmt */\n\t\tqueryString,    /* const char *sourceText */\n\t\tGetActiveSnapshot(),   /* Snapshot snapshot */\n\t\tInvalidSnapshot,       /* Snapshot crosscheck_snapshot */\n\t\tdest,           /* DestReceiver *dest */\n\t\tparams,         /* ParamListInfo params */\n\t\tqueryEnv,       /* QueryEnvironment *queryEnv */\n\t\tinstrument_option /* int instrument_options */\n\t);\n\n\t/* Select execution options */\n\tif (executeQuery)\n\t\teflags = 0;\t\t\t\t/* default run-to-completion flags */\n\telse\n\t\teflags = EXEC_FLAG_EXPLAIN_ONLY;\n\n\t/* call ExecutorStart to prepare the plan for execution */\n\tExecutorStart(queryDesc, eflags);\n\n\t/* Execute the plan for statistics if asked for */\n\tif (executeQuery)\n\t{\n\t\tScanDirection dir = ForwardScanDirection;\n\n\t\t/* run the plan */\n/* run the plan: count = 0 (all rows) */\n#if PG_VERSION_NUM >= PG_VERSION_18\n    /* PG 18+ dropped the “execute_once” boolean */\n    ExecutorRun(queryDesc, dir, 0L);\n#else\n    /* PG 17- still expect the 4th ‘once’ argument */\n    ExecutorRun(queryDesc, dir, 0L, true);\n#endif\n\n\t\t/* run cleanup too */\n\t\tExecutorFinish(queryDesc);\n\n\t\t/* We can't run ExecutorEnd 'till we're done printing the stats... */\n\t\ttotaltime += elapsed_time(&starttime);\n\t}\n\n\tExplainOpenGroup(\"Query\", NULL, true, es);\n\n\tif (executeSubplan)\n\t{\n\t\tExtractAnalyzeStats(subPlan, queryDesc->planstate);\n\t\tplanstate_tree_walker(queryDesc->planstate, PlanStateAnalyzeWalker, (void *) subPlan);\n\t}\n\n\t/* Create textual dump of plan tree */\n\tExplainPrintPlan(es, queryDesc);\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t/* Show buffer and/or memory usage in planning */\n\tif (peek_buffer_usage(es, bufusage) || mem_counters)\n\t{\n\t\tExplainOpenGroup(\"Planning\", \"Planning\", true, es);\n\n\t\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t\t{\n\t\t\tExplainIndentText(es);\n\t\t\tappendStringInfoString(es->str, \"Planning:\\n\");\n\t\t\tes->indent++;\n\t\t}\n\n\t\tif (bufusage)\n\t\t\tshow_buffer_usage(es, bufusage);\n\n\t\tif (mem_counters)\n\t\t\tshow_memory_counters(es, mem_counters);\n\n\t\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t\t\tes->indent--;\n\n\t\tExplainCloseGroup(\"Planning\", \"Planning\", true, es);\n\t}\n#endif\n\n\tif (es->summary && planduration)\n\t{\n\t\tdouble\t\tplantime = INSTR_TIME_GET_DOUBLE(*planduration);\n\n\t\tExplainPropertyFloat(\"Planning Time\", \"ms\", 1000.0 * plantime, 3, es);\n\t}\n\n\t/* Print info about runtime of triggers */\n\tif (es->analyze)\n\t\tExplainPrintTriggers(es, queryDesc);\n\n\t/*\n\t * Print info about JITing. Tied to es->costs because we don't want to\n\t * display this in regression tests, as it'd cause output differences\n\t * depending on build options.  Might want to separate that out from COSTS\n\t * at a later stage.\n\t */\n\tif (es->costs)\n\t\tExplainPrintJITSummary(es, queryDesc);\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tif (es->serialize != EXPLAIN_SERIALIZE_NONE)\n\t{\n\t\t/* the SERIALIZE option requires its own tuple receiver */\n\t\tDestReceiver *dest_serialize = CreateExplainSerializeDestReceiver(es);\n\n\t\t/* grab serialization metrics before we destroy the DestReceiver */\n\t\tSerializeMetrics serializeMetrics = GetSerializationMetrics(dest_serialize);\n\n\t\t/* call the DestReceiver's destroy method even during explain */\n\t\tdest_serialize->rDestroy(dest_serialize);\n\n\t\t/* Print info about serialization of output */\n\t\tExplainPrintSerialize(es, &serializeMetrics);\n\t}\n#endif\n\n\t/*\n\t * Close down the query and free resources.  Include time for this in the\n\t * total execution time (although it should be pretty minimal).\n\t */\n\tINSTR_TIME_SET_CURRENT(starttime);\n\n\tif (executeQuery)\n\t{\n\t\tInstrumentation *instr = queryDesc->planstate->instrument;\n\t\t*executionTuples = instr->ntuples;\n\t\t*executionLoops = instr->nloops;\n\t}\n\n\tExecutorEnd(queryDesc);\n\n\tFreeQueryDesc(queryDesc);\n\n\tPopActiveSnapshot();\n\n\t/* We need a CCI just in case query expanded to multiple plans */\n\tif (executeQuery)\n\t\tCommandCounterIncrement();\n\n\ttotaltime += elapsed_time(&starttime);\n\n\t/*\n\t * We only report execution time if we actually ran the query (that is,\n\t * the user specified ANALYZE), and if summary reporting is enabled (the\n\t * user can set SUMMARY OFF to not have the timing information included in\n\t * the output).  By default, ANALYZE sets SUMMARY to true.\n\t */\n\tif (es->summary && es->analyze)\n\t\tExplainPropertyFloat(\"Execution Time\", \"ms\", 1000.0 * totaltime, 3,\n\t\t\t\t\t\t\t es);\n\n\t*executionDurationMillisec = totaltime * 1000;\n\n\tExplainCloseGroup(\"Query\", NULL, true, es);\n}\n\n\n/*\n * Compute elapsed time in seconds since given timestamp.\n *\n * Copied from explain.c.\n */\nstatic double\nelapsed_time(instr_time *starttime)\n{\n\tinstr_time\tendtime;\n\n\tINSTR_TIME_SET_CURRENT(endtime);\n\tINSTR_TIME_SUBTRACT(endtime, *starttime);\n\treturn INSTR_TIME_GET_DOUBLE(endtime);\n}\n\n\n#if PG_VERSION_NUM >= PG_VERSION_17 && PG_VERSION_NUM < PG_VERSION_18\n/*\n * Indent a text-format line.\n *\n * We indent by two spaces per indentation level.  However, when emitting\n * data for a parallel worker there might already be data on the current line\n * (cf. ExplainOpenWorker); in that case, don't indent any more.\n *\n * Copied from explain.c.\n */\nstatic void\nExplainIndentText(ExplainState *es)\n{\n\tAssert(es->format == EXPLAIN_FORMAT_TEXT);\n\tif (es->str->len == 0 || es->str->data[es->str->len - 1] == '\\n')\n\t\tappendStringInfoSpaces(es->str, es->indent * 2);\n}\n\n\n/*\n * GetSerializationMetrics - collect metrics\n *\n * We have to be careful here since the receiver could be an IntoRel\n * receiver if the subject statement is CREATE TABLE AS.  In that\n * case, return all-zeroes stats.\n *\n * Copied from explain.c.\n */\nstatic SerializeMetrics\nGetSerializationMetrics(DestReceiver *dest)\n{\n\tSerializeMetrics empty;\n\n\tif (dest->mydest == DestExplainSerialize)\n\t\treturn ((SerializeDestReceiver *) dest)->metrics;\n\n\tmemset(&empty, 0, sizeof(SerializeMetrics));\n\tINSTR_TIME_SET_ZERO(empty.timeSpent);\n\n\treturn empty;\n}\n#endif\n\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n/*\n * Return whether show_buffer_usage would have anything to print, if given\n * the same 'usage' data.  Note that when the format is anything other than\n * text, we print even if the counters are all zeroes.\n *\n * Copied from explain.c.\n */\nstatic bool\npeek_buffer_usage(ExplainState *es, const BufferUsage *usage)\n{\n\tbool\t\thas_shared;\n\tbool\t\thas_local;\n\tbool\t\thas_temp;\n\tbool\t\thas_shared_timing;\n\tbool\t\thas_local_timing;\n\tbool\t\thas_temp_timing;\n\n\tif (usage == NULL)\n\t\treturn false;\n\n\tif (es->format != EXPLAIN_FORMAT_TEXT)\n\t\treturn true;\n\n\thas_shared = (usage->shared_blks_hit > 0 ||\n\t\t\t\t  usage->shared_blks_read > 0 ||\n\t\t\t\t  usage->shared_blks_dirtied > 0 ||\n\t\t\t\t  usage->shared_blks_written > 0);\n\thas_local = (usage->local_blks_hit > 0 ||\n\t\t\t\t usage->local_blks_read > 0 ||\n\t\t\t\t usage->local_blks_dirtied > 0 ||\n\t\t\t\t usage->local_blks_written > 0);\n\thas_temp = (usage->temp_blks_read > 0 ||\n\t\t\t\tusage->temp_blks_written > 0);\n\thas_shared_timing = (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time) ||\n\t\t\t\t\t\t !INSTR_TIME_IS_ZERO(usage->shared_blk_write_time));\n\thas_local_timing = (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time) ||\n\t\t\t\t\t\t!INSTR_TIME_IS_ZERO(usage->local_blk_write_time));\n\thas_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||\n\t\t\t\t\t   !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));\n\n\treturn has_shared || has_local || has_temp || has_shared_timing ||\n\t\thas_local_timing || has_temp_timing;\n}\n\n\n/*\n * Show buffer usage details.  This better be sync with peek_buffer_usage.\n *\n * Copied from explain.c.\n */\nstatic void\nshow_buffer_usage(ExplainState *es, const BufferUsage *usage)\n{\n\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t{\n\t\tbool\t\thas_shared = (usage->shared_blks_hit > 0 ||\n\t\t\t\t\t\t\t\t  usage->shared_blks_read > 0 ||\n\t\t\t\t\t\t\t\t  usage->shared_blks_dirtied > 0 ||\n\t\t\t\t\t\t\t\t  usage->shared_blks_written > 0);\n\t\tbool\t\thas_local = (usage->local_blks_hit > 0 ||\n\t\t\t\t\t\t\t\t usage->local_blks_read > 0 ||\n\t\t\t\t\t\t\t\t usage->local_blks_dirtied > 0 ||\n\t\t\t\t\t\t\t\t usage->local_blks_written > 0);\n\t\tbool\t\thas_temp = (usage->temp_blks_read > 0 ||\n\t\t\t\t\t\t\t\tusage->temp_blks_written > 0);\n\t\tbool\t\thas_shared_timing = (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time) ||\n\t\t\t\t\t\t\t\t\t\t !INSTR_TIME_IS_ZERO(usage->shared_blk_write_time));\n\t\tbool\t\thas_local_timing = (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time) ||\n\t\t\t\t\t\t\t\t\t\t!INSTR_TIME_IS_ZERO(usage->local_blk_write_time));\n\t\tbool\t\thas_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||\n\t\t\t\t\t\t\t\t\t   !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));\n\n\t\t/* Show only positive counter values. */\n\t\tif (has_shared || has_local || has_temp)\n\t\t{\n\t\t\tExplainIndentText(es);\n\t\t\tappendStringInfoString(es->str, \"Buffers:\");\n\n\t\t\tif (has_shared)\n\t\t\t{\n\t\t\t\tappendStringInfoString(es->str, \" shared\");\n\t\t\t\tif (usage->shared_blks_hit > 0)\n\t\t\t\t\tappendStringInfo(es->str, \" hit=%lld\",\n\t\t\t\t\t\t\t\t\t (long long) usage->shared_blks_hit);\n\t\t\t\tif (usage->shared_blks_read > 0)\n\t\t\t\t\tappendStringInfo(es->str, \" read=%lld\",\n\t\t\t\t\t\t\t\t\t (long long) usage->shared_blks_read);\n\t\t\t\tif (usage->shared_blks_dirtied > 0)\n\t\t\t\t\tappendStringInfo(es->str, \" dirtied=%lld\",\n\t\t\t\t\t\t\t\t\t (long long) usage->shared_blks_dirtied);\n\t\t\t\tif (usage->shared_blks_written > 0)\n\t\t\t\t\tappendStringInfo(es->str, \" written=%lld\",\n\t\t\t\t\t\t\t\t\t (long long) usage->shared_blks_written);\n\t\t\t\tif (has_local || has_temp)\n\t\t\t\t\tappendStringInfoChar(es->str, ',');\n\t\t\t}\n\t\t\tif (has_local)\n\t\t\t{\n\t\t\t\tappendStringInfoString(es->str, \" local\");\n\t\t\t\tif (usage->local_blks_hit > 0)\n\t\t\t\t\tappendStringInfo(es->str, \" hit=%lld\",\n\t\t\t\t\t\t\t\t\t (long long) usage->local_blks_hit);\n\t\t\t\tif (usage->local_blks_read > 0)\n\t\t\t\t\tappendStringInfo(es->str, \" read=%lld\",\n\t\t\t\t\t\t\t\t\t (long long) usage->local_blks_read);\n\t\t\t\tif (usage->local_blks_dirtied > 0)\n\t\t\t\t\tappendStringInfo(es->str, \" dirtied=%lld\",\n\t\t\t\t\t\t\t\t\t (long long) usage->local_blks_dirtied);\n\t\t\t\tif (usage->local_blks_written > 0)\n\t\t\t\t\tappendStringInfo(es->str, \" written=%lld\",\n\t\t\t\t\t\t\t\t\t (long long) usage->local_blks_written);\n\t\t\t\tif (has_temp)\n\t\t\t\t\tappendStringInfoChar(es->str, ',');\n\t\t\t}\n\t\t\tif (has_temp)\n\t\t\t{\n\t\t\t\tappendStringInfoString(es->str, \" temp\");\n\t\t\t\tif (usage->temp_blks_read > 0)\n\t\t\t\t\tappendStringInfo(es->str, \" read=%lld\",\n\t\t\t\t\t\t\t\t\t (long long) usage->temp_blks_read);\n\t\t\t\tif (usage->temp_blks_written > 0)\n\t\t\t\t\tappendStringInfo(es->str, \" written=%lld\",\n\t\t\t\t\t\t\t\t\t (long long) usage->temp_blks_written);\n\t\t\t}\n\t\t\tappendStringInfoChar(es->str, '\\n');\n\t\t}\n\n\t\t/* As above, show only positive counter values. */\n\t\tif (has_shared_timing || has_local_timing || has_temp_timing)\n\t\t{\n\t\t\tExplainIndentText(es);\n\t\t\tappendStringInfoString(es->str, \"I/O Timings:\");\n\n\t\t\tif (has_shared_timing)\n\t\t\t{\n\t\t\t\tappendStringInfoString(es->str, \" shared\");\n\t\t\t\tif (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time))\n\t\t\t\t\tappendStringInfo(es->str, \" read=%0.3f\",\n\t\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->shared_blk_read_time));\n\t\t\t\tif (!INSTR_TIME_IS_ZERO(usage->shared_blk_write_time))\n\t\t\t\t\tappendStringInfo(es->str, \" write=%0.3f\",\n\t\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->shared_blk_write_time));\n\t\t\t\tif (has_local_timing || has_temp_timing)\n\t\t\t\t\tappendStringInfoChar(es->str, ',');\n\t\t\t}\n\t\t\tif (has_local_timing)\n\t\t\t{\n\t\t\t\tappendStringInfoString(es->str, \" local\");\n\t\t\t\tif (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time))\n\t\t\t\t\tappendStringInfo(es->str, \" read=%0.3f\",\n\t\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->local_blk_read_time));\n\t\t\t\tif (!INSTR_TIME_IS_ZERO(usage->local_blk_write_time))\n\t\t\t\t\tappendStringInfo(es->str, \" write=%0.3f\",\n\t\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->local_blk_write_time));\n\t\t\t\tif (has_temp_timing)\n\t\t\t\t\tappendStringInfoChar(es->str, ',');\n\t\t\t}\n\t\t\tif (has_temp_timing)\n\t\t\t{\n\t\t\t\tappendStringInfoString(es->str, \" temp\");\n\t\t\t\tif (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time))\n\t\t\t\t\tappendStringInfo(es->str, \" read=%0.3f\",\n\t\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time));\n\t\t\t\tif (!INSTR_TIME_IS_ZERO(usage->temp_blk_write_time))\n\t\t\t\t\tappendStringInfo(es->str, \" write=%0.3f\",\n\t\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time));\n\t\t\t}\n\t\t\tappendStringInfoChar(es->str, '\\n');\n\t\t}\n\t}\n\telse\n\t{\n\t\tExplainPropertyInteger(\"Shared Hit Blocks\", NULL,\n\t\t\t\t\t\t\t   usage->shared_blks_hit, es);\n\t\tExplainPropertyInteger(\"Shared Read Blocks\", NULL,\n\t\t\t\t\t\t\t   usage->shared_blks_read, es);\n\t\tExplainPropertyInteger(\"Shared Dirtied Blocks\", NULL,\n\t\t\t\t\t\t\t   usage->shared_blks_dirtied, es);\n\t\tExplainPropertyInteger(\"Shared Written Blocks\", NULL,\n\t\t\t\t\t\t\t   usage->shared_blks_written, es);\n\t\tExplainPropertyInteger(\"Local Hit Blocks\", NULL,\n\t\t\t\t\t\t\t   usage->local_blks_hit, es);\n\t\tExplainPropertyInteger(\"Local Read Blocks\", NULL,\n\t\t\t\t\t\t\t   usage->local_blks_read, es);\n\t\tExplainPropertyInteger(\"Local Dirtied Blocks\", NULL,\n\t\t\t\t\t\t\t   usage->local_blks_dirtied, es);\n\t\tExplainPropertyInteger(\"Local Written Blocks\", NULL,\n\t\t\t\t\t\t\t   usage->local_blks_written, es);\n\t\tExplainPropertyInteger(\"Temp Read Blocks\", NULL,\n\t\t\t\t\t\t\t   usage->temp_blks_read, es);\n\t\tExplainPropertyInteger(\"Temp Written Blocks\", NULL,\n\t\t\t\t\t\t\t   usage->temp_blks_written, es);\n\t\tif (track_io_timing)\n\t\t{\n\t\t\tExplainPropertyFloat(\"Shared I/O Read Time\", \"ms\",\n\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->shared_blk_read_time),\n\t\t\t\t\t\t\t\t 3, es);\n\t\t\tExplainPropertyFloat(\"Shared I/O Write Time\", \"ms\",\n\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->shared_blk_write_time),\n\t\t\t\t\t\t\t\t 3, es);\n\t\t\tExplainPropertyFloat(\"Local I/O Read Time\", \"ms\",\n\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->local_blk_read_time),\n\t\t\t\t\t\t\t\t 3, es);\n\t\t\tExplainPropertyFloat(\"Local I/O Write Time\", \"ms\",\n\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->local_blk_write_time),\n\t\t\t\t\t\t\t\t 3, es);\n\t\t\tExplainPropertyFloat(\"Temp I/O Read Time\", \"ms\",\n\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time),\n\t\t\t\t\t\t\t\t 3, es);\n\t\t\tExplainPropertyFloat(\"Temp I/O Write Time\", \"ms\",\n\t\t\t\t\t\t\t\t INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time),\n\t\t\t\t\t\t\t\t 3, es);\n\t\t}\n\t}\n}\n\n\n/*\n * Show memory usage details.\n *\n * Copied from explain.c.\n */\nstatic void\nshow_memory_counters(ExplainState *es, const MemoryContextCounters *mem_counters)\n{\n\tint64\t\tmemUsedkB = BYTES_TO_KILOBYTES(mem_counters->totalspace -\n\t\t\t\t\t\t\t\t\t\t\t   mem_counters->freespace);\n\tint64\t\tmemAllocatedkB = BYTES_TO_KILOBYTES(mem_counters->totalspace);\n\n\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t{\n\t\tExplainIndentText(es);\n\t\tappendStringInfo(es->str,\n\t\t\t\t\t\t \"Memory: used=\" INT64_FORMAT \"kB  allocated=\" INT64_FORMAT \"kB\",\n\t\t\t\t\t\t memUsedkB, memAllocatedkB);\n\t\tappendStringInfoChar(es->str, '\\n');\n\t}\n\telse\n\t{\n\t\tExplainPropertyInteger(\"Memory Used\", \"kB\", memUsedkB, es);\n\t\tExplainPropertyInteger(\"Memory Allocated\", \"kB\", memAllocatedkB, es);\n\t}\n}\n\n\n/*\n * ExplainPrintSerialize -\n *\t  Append information about query output volume to es->str.\n *\n * Copied from explain.c.\n */\nstatic void\nExplainPrintSerialize(ExplainState *es, SerializeMetrics *metrics)\n{\n\tconst char *format;\n\n\t/* We shouldn't get called for EXPLAIN_SERIALIZE_NONE */\n\tif (es->serialize == EXPLAIN_SERIALIZE_TEXT)\n\t\tformat = \"text\";\n\telse\n\t{\n\t\tAssert(es->serialize == EXPLAIN_SERIALIZE_BINARY);\n\t\tformat = \"binary\";\n\t}\n\n\tExplainOpenGroup(\"Serialization\", \"Serialization\", true, es);\n\n\tif (es->format == EXPLAIN_FORMAT_TEXT)\n\t{\n\t\tExplainIndentText(es);\n\t\tif (es->timing)\n\t\t\tappendStringInfo(es->str, \"Serialization: time=%.3f ms  output=\" UINT64_FORMAT \"kB  format=%s\\n\",\n\t\t\t\t\t\t\t 1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),\n\t\t\t\t\t\t\t BYTES_TO_KILOBYTES(metrics->bytesSent),\n\t\t\t\t\t\t\t format);\n\t\telse\n\t\t\tappendStringInfo(es->str, \"Serialization: output=\" UINT64_FORMAT \"kB  format=%s\\n\",\n\t\t\t\t\t\t\t BYTES_TO_KILOBYTES(metrics->bytesSent),\n\t\t\t\t\t\t\t format);\n\n\t\tif (es->buffers && peek_buffer_usage(es, &metrics->bufferUsage))\n\t\t{\n\t\t\tes->indent++;\n\t\t\tshow_buffer_usage(es, &metrics->bufferUsage);\n\t\t\tes->indent--;\n\t\t}\n\t}\n\telse\n\t{\n\t\tif (es->timing)\n\t\t\tExplainPropertyFloat(\"Time\", \"ms\",\n\t\t\t\t\t\t\t\t 1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),\n\t\t\t\t\t\t\t\t 3, es);\n\t\tExplainPropertyInteger(\"Output Volume\", \"kB\",\n\t\t\t\t\t\t\t\tBYTES_TO_KILOBYTES(metrics->bytesSent), es);\n\t\tExplainPropertyText(\"Format\", format, es);\n\t\tif (es->buffers)\n\t\t\tshow_buffer_usage(es, &metrics->bufferUsage);\n\t}\n\n\tExplainCloseGroup(\"Serialization\", \"Serialization\", true, es);\n}\n#endif\n"
  },
  {
    "path": "src/backend/distributed/planner/multi_join_order.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_join_order.c\n *\n * Routines for constructing the join order list using a rule-based approach.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <limits.h>\n\n#include \"postgres.h\"\n\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/nbtree.h\"\n#include \"catalog/pg_am.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"optimizer/optimizer.h\"\n#include \"utils/builtins.h\"\n#include \"utils/datum.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/* Config variables managed via guc.c */\nbool LogMultiJoinOrder = false; /* print join order as a debugging aid */\nbool EnableSingleHashRepartitioning = false;\n\n/* Function pointer type definition for join rule evaluation functions */\ntypedef JoinOrderNode *(*RuleEvalFunction) (JoinOrderNode *currentJoinNode,\n\t\t\t\t\t\t\t\t\t\t\tTableEntry *candidateTable,\n\t\t\t\t\t\t\t\t\t\t\tList *applicableJoinClauses,\n\t\t\t\t\t\t\t\t\t\t\tJoinType joinType);\n\nstatic char *RuleNameArray[JOIN_RULE_LAST] = { 0 }; /* ordered join rule names */\nstatic RuleEvalFunction RuleEvalFunctionArray[JOIN_RULE_LAST] = { 0 }; /* join rules */\n\n\n/* Local functions forward declarations */\nstatic bool JoinExprListWalker(Node *node, List **joinList);\nstatic bool ExtractLeftMostRangeTableIndex(Node *node, int *rangeTableIndex);\nstatic List * JoinOrderForTable(TableEntry *firstTable, List *tableEntryList,\n\t\t\t\t\t\t\t\tList *joinClauseList);\nstatic List * BestJoinOrder(List *candidateJoinOrders);\nstatic List * FewestOfJoinRuleType(List *candidateJoinOrders, JoinRuleType ruleType);\nstatic uint32 JoinRuleTypeCount(List *joinOrder, JoinRuleType ruleTypeToCount);\nstatic List * LatestLargeDataTransfer(List *candidateJoinOrders);\nstatic void PrintJoinOrderList(List *joinOrder);\nstatic uint32 LargeDataTransferLocation(List *joinOrder);\nstatic List * TableEntryListDifference(List *lhsTableList, List *rhsTableList);\n\n/* Local functions forward declarations for join evaluations */\nstatic JoinOrderNode * EvaluateJoinRules(List *joinedTableList,\n\t\t\t\t\t\t\t\t\t\t JoinOrderNode *currentJoinNode,\n\t\t\t\t\t\t\t\t\t\t TableEntry *candidateTable,\n\t\t\t\t\t\t\t\t\t\t List *joinClauseList, JoinType joinType);\nstatic List * RangeTableIdList(List *tableList);\nstatic RuleEvalFunction JoinRuleEvalFunction(JoinRuleType ruleType);\nstatic char * JoinRuleName(JoinRuleType ruleType);\nstatic JoinOrderNode * ReferenceJoin(JoinOrderNode *joinNode, TableEntry *candidateTable,\n\t\t\t\t\t\t\t\t\t List *applicableJoinClauses, JoinType joinType);\nstatic JoinOrderNode * CartesianProductReferenceJoin(JoinOrderNode *joinNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t TableEntry *candidateTable,\n\t\t\t\t\t\t\t\t\t\t\t\t\t List *applicableJoinClauses,\n\t\t\t\t\t\t\t\t\t\t\t\t\t JoinType joinType);\nstatic JoinOrderNode * LocalJoin(JoinOrderNode *joinNode, TableEntry *candidateTable,\n\t\t\t\t\t\t\t\t List *applicableJoinClauses, JoinType joinType);\nstatic JoinOrderNode * SinglePartitionJoin(JoinOrderNode *joinNode,\n\t\t\t\t\t\t\t\t\t\t   TableEntry *candidateTable,\n\t\t\t\t\t\t\t\t\t\t   List *applicableJoinClauses,\n\t\t\t\t\t\t\t\t\t\t   JoinType joinType);\nstatic JoinOrderNode * DualPartitionJoin(JoinOrderNode *joinNode,\n\t\t\t\t\t\t\t\t\t\t TableEntry *candidateTable,\n\t\t\t\t\t\t\t\t\t\t List *applicableJoinClauses,\n\t\t\t\t\t\t\t\t\t\t JoinType joinType);\nstatic JoinOrderNode * CartesianProduct(JoinOrderNode *joinNode,\n\t\t\t\t\t\t\t\t\t\tTableEntry *candidateTable,\n\t\t\t\t\t\t\t\t\t\tList *applicableJoinClauses,\n\t\t\t\t\t\t\t\t\t\tJoinType joinType);\nstatic JoinOrderNode * MakeJoinOrderNode(TableEntry *tableEntry,\n\t\t\t\t\t\t\t\t\t\t JoinRuleType joinRuleType,\n\t\t\t\t\t\t\t\t\t\t List *partitionColumnList, char partitionMethod,\n\t\t\t\t\t\t\t\t\t\t TableEntry *anchorTable);\n\n\n/*\n * JoinExprList flattens the JoinExpr nodes in the FROM expression and translate implicit\n * joins to inner joins. This function does not consider (right-)nested joins.\n */\nList *\nJoinExprList(FromExpr *fromExpr)\n{\n\tList *joinList = NIL;\n\tList *fromList = fromExpr->fromlist;\n\tListCell *fromCell = NULL;\n\n\tforeach(fromCell, fromList)\n\t{\n\t\tNode *nextNode = (Node *) lfirst(fromCell);\n\n\t\tif (joinList != NIL)\n\t\t{\n\t\t\t/* multiple nodes in from clause, add an explicit join between them */\n\t\t\tint nextRangeTableIndex = 0;\n\n\t\t\t/* find the left most range table in this node */\n\t\t\tExtractLeftMostRangeTableIndex((Node *) fromExpr, &nextRangeTableIndex);\n\n\t\t\tRangeTblRef *nextRangeTableRef = makeNode(RangeTblRef);\n\t\t\tnextRangeTableRef->rtindex = nextRangeTableIndex;\n\n\t\t\t/* join the previous node with nextRangeTableRef */\n\t\t\tJoinExpr *newJoinExpr = makeNode(JoinExpr);\n\t\t\tnewJoinExpr->jointype = JOIN_INNER;\n\t\t\tnewJoinExpr->rarg = (Node *) nextRangeTableRef;\n\t\t\tnewJoinExpr->quals = NULL;\n\n\t\t\tjoinList = lappend(joinList, newJoinExpr);\n\t\t}\n\n\t\tJoinExprListWalker(nextNode, &joinList);\n\t}\n\n\treturn joinList;\n}\n\n\n/*\n * JoinExprListWalker the JoinExpr nodes in a join tree in the order in which joins are\n * to be executed. If there are no joins then no elements are added to joinList.\n */\nstatic bool\nJoinExprListWalker(Node *node, List **joinList)\n{\n\tbool walkerResult = false;\n\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, JoinExpr))\n\t{\n\t\tJoinExpr *joinExpr = (JoinExpr *) node;\n\n\t\twalkerResult = JoinExprListWalker(joinExpr->larg, joinList);\n\n\t\t(*joinList) = lappend(*joinList, joinExpr);\n\t}\n\telse\n\t{\n\t\twalkerResult = expression_tree_walker(node, JoinExprListWalker,\n\t\t\t\t\t\t\t\t\t\t\t  joinList);\n\t}\n\n\treturn walkerResult;\n}\n\n\n/*\n * ExtractLeftMostRangeTableIndex extracts the range table index of the left-most\n * leaf in a join tree.\n */\nstatic bool\nExtractLeftMostRangeTableIndex(Node *node, int *rangeTableIndex)\n{\n\tbool walkerResult = false;\n\n\tAssert(node != NULL);\n\n\tif (IsA(node, JoinExpr))\n\t{\n\t\tJoinExpr *joinExpr = (JoinExpr *) node;\n\n\t\twalkerResult = ExtractLeftMostRangeTableIndex(joinExpr->larg, rangeTableIndex);\n\t}\n\telse if (IsA(node, RangeTblRef))\n\t{\n\t\tRangeTblRef *rangeTableRef = (RangeTblRef *) node;\n\n\t\t*rangeTableIndex = rangeTableRef->rtindex;\n\t\twalkerResult = true;\n\t}\n\telse\n\t{\n\t\twalkerResult = expression_tree_walker(node, ExtractLeftMostRangeTableIndex,\n\t\t\t\t\t\t\t\t\t\t\t  rangeTableIndex);\n\t}\n\n\treturn walkerResult;\n}\n\n\n/*\n * JoinOnColumns determines whether two columns are joined by a given join clause list.\n */\nbool\nJoinOnColumns(List *currentPartitionColumnList, Var *candidateColumn,\n\t\t\t  List *joinClauseList)\n{\n\tif (candidateColumn == NULL || list_length(currentPartitionColumnList) == 0)\n\t{\n\t\t/*\n\t\t * LocalJoin can only be happening if we have both a current column and a target\n\t\t * column, otherwise we are not joining two local tables\n\t\t */\n\t\treturn false;\n\t}\n\n\tVar *currentColumn = NULL;\n\tforeach_declared_ptr(currentColumn, currentPartitionColumnList)\n\t{\n\t\tNode *joinClause = NULL;\n\t\tforeach_declared_ptr(joinClause, joinClauseList)\n\t\t{\n\t\t\tif (!NodeIsEqualsOpExpr(joinClause))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tOpExpr *joinClauseOpExpr = castNode(OpExpr, joinClause);\n\t\t\tVar *leftColumn = LeftColumnOrNULL(joinClauseOpExpr);\n\t\t\tVar *rightColumn = RightColumnOrNULL(joinClauseOpExpr);\n\n\t\t\t/*\n\t\t\t * Check if both join columns and both partition key columns match, since the\n\t\t\t * current and candidate column's can't be NULL we know they won't match if either\n\t\t\t * of the columns resolved to NULL above.\n\t\t\t */\n\t\t\tif (equal(leftColumn, currentColumn) &&\n\t\t\t\tequal(rightColumn, candidateColumn))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (equal(leftColumn, candidateColumn) &&\n\t\t\t\tequal(rightColumn, currentColumn))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * NodeIsEqualsOpExpr checks if the node is an OpExpr, where the operator\n * matches OperatorImplementsEquality.\n */\nbool\nNodeIsEqualsOpExpr(Node *node)\n{\n\tif (!IsA(node, OpExpr))\n\t{\n\t\treturn false;\n\t}\n\tOpExpr *opExpr = castNode(OpExpr, node);\n\treturn OperatorImplementsEquality(opExpr->opno);\n}\n\n\n/*\n * JoinOrderList calculates the best join order and join rules that apply given\n * the list of tables and join clauses. First, the function generates a set of\n * candidate join orders, each with a different table as its first table. Then,\n * the function chooses among these candidates the join order that transfers the\n * least amount of data across the network, and returns this join order.\n */\nList *\nJoinOrderList(List *tableEntryList, List *joinClauseList)\n{\n\tList *candidateJoinOrderList = NIL;\n\tListCell *tableEntryCell = NULL;\n\n\tforeach(tableEntryCell, tableEntryList)\n\t{\n\t\tTableEntry *startingTable = (TableEntry *) lfirst(tableEntryCell);\n\n\t\t/* each candidate join order starts with a different table */\n\t\tList *candidateJoinOrder = JoinOrderForTable(startingTable, tableEntryList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t joinClauseList);\n\n\t\tif (candidateJoinOrder != NULL)\n\t\t{\n\t\t\tcandidateJoinOrderList = lappend(candidateJoinOrderList, candidateJoinOrder);\n\t\t}\n\t}\n\n\tif (list_length(candidateJoinOrderList) == 0)\n\t{\n\t\t/* there are no plans that we can create, time to error */\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"complex joins are only supported when all distributed \"\n\t\t\t\t\t\t\t   \"tables are joined on their distribution columns with \"\n\t\t\t\t\t\t\t   \"equal operator\")));\n\t}\n\n\tList *bestJoinOrder = BestJoinOrder(candidateJoinOrderList);\n\n\t/* if logging is enabled, print join order */\n\tif (LogMultiJoinOrder)\n\t{\n\t\tPrintJoinOrderList(bestJoinOrder);\n\t}\n\n\treturn bestJoinOrder;\n}\n\n\n/*\n * JoinOrderForTable creates a join order whose first element is the given first\n * table. To determine each subsequent element in the join order, the function\n * then chooses the table that has the lowest ranking join rule, and with which\n * it can join the table to the previous table in the join order. The function\n * repeats this until it determines all elements in the join order list, and\n * returns this list.\n */\nstatic List *\nJoinOrderForTable(TableEntry *firstTable, List *tableEntryList, List *joinClauseList)\n{\n\tJoinRuleType firstJoinRule = JOIN_RULE_INVALID_FIRST;\n\tint joinedTableCount = 1;\n\tint totalTableCount = list_length(tableEntryList);\n\n\t/* create join node for the first table */\n\tOid firstRelationId = firstTable->relationId;\n\tuint32 firstTableId = firstTable->rangeTableId;\n\tVar *firstPartitionColumn = PartitionColumn(firstRelationId, firstTableId);\n\tchar firstPartitionMethod = PartitionMethod(firstRelationId);\n\n\tJoinOrderNode *firstJoinNode = MakeJoinOrderNode(firstTable, firstJoinRule,\n\t\t\t\t\t\t\t\t\t\t\t\t\t list_make1(firstPartitionColumn),\n\t\t\t\t\t\t\t\t\t\t\t\t\t firstPartitionMethod,\n\t\t\t\t\t\t\t\t\t\t\t\t\t firstTable);\n\n\t/* add first node to the join order */\n\tList *joinOrderList = list_make1(firstJoinNode);\n\tList *joinedTableList = list_make1(firstTable);\n\tJoinOrderNode *currentJoinNode = firstJoinNode;\n\n\t/* loop until we join all remaining tables */\n\twhile (joinedTableCount < totalTableCount)\n\t{\n\t\tListCell *pendingTableCell = NULL;\n\t\tJoinOrderNode *nextJoinNode = NULL;\n\t\tJoinRuleType nextJoinRuleType = JOIN_RULE_LAST;\n\n\t\tList *pendingTableList = TableEntryListDifference(tableEntryList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  joinedTableList);\n\n\t\t/*\n\t\t * Iterate over all pending tables, and find the next best table to\n\t\t * join. The best table is the one whose join rule requires the least\n\t\t * amount of data transfer.\n\t\t */\n\t\tforeach(pendingTableCell, pendingTableList)\n\t\t{\n\t\t\tTableEntry *pendingTable = (TableEntry *) lfirst(pendingTableCell);\n\t\t\tJoinType joinType = JOIN_INNER;\n\n\t\t\t/* evaluate all join rules for this pending table */\n\t\t\tJoinOrderNode *pendingJoinNode = EvaluateJoinRules(joinedTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   currentJoinNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   pendingTable,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   joinClauseList, joinType);\n\n\t\t\tif (pendingJoinNode == NULL)\n\t\t\t{\n\t\t\t\t/* no join order could be generated, we try our next pending table */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* if this rule is better than previous ones, keep it */\n\t\t\tJoinRuleType pendingJoinRuleType = pendingJoinNode->joinRuleType;\n\t\t\tif (pendingJoinRuleType < nextJoinRuleType)\n\t\t\t{\n\t\t\t\tnextJoinNode = pendingJoinNode;\n\t\t\t\tnextJoinRuleType = pendingJoinRuleType;\n\t\t\t}\n\t\t}\n\n\t\tif (nextJoinNode == NULL)\n\t\t{\n\t\t\t/*\n\t\t\t * There is no next join node found, this will repeat indefinitely hence we\n\t\t\t * bail and let JoinOrderList try a new initial table\n\t\t\t */\n\t\t\treturn NULL;\n\t\t}\n\n\t\tAssert(nextJoinNode != NULL);\n\t\tTableEntry *nextJoinedTable = nextJoinNode->tableEntry;\n\n\t\t/* add next node to the join order */\n\t\tjoinOrderList = lappend(joinOrderList, nextJoinNode);\n\t\tjoinedTableList = lappend(joinedTableList, nextJoinedTable);\n\t\tcurrentJoinNode = nextJoinNode;\n\n\t\tjoinedTableCount++;\n\t}\n\n\treturn joinOrderList;\n}\n\n\n/*\n * BestJoinOrder takes in a list of candidate join orders, and determines the\n * best join order among these candidates. The function uses two heuristics for\n * this. First, the function chooses join orders that have the fewest number of\n * join operators that cause large data transfers. Second, the function chooses\n * join orders where large data transfers occur later in the execution.\n */\nstatic List *\nBestJoinOrder(List *candidateJoinOrders)\n{\n\tuint32 highestValidIndex = JOIN_RULE_LAST - 1;\n\tuint32 candidateCount PG_USED_FOR_ASSERTS_ONLY = 0;\n\n\t/*\n\t * We start with the highest ranking rule type (cartesian product), and walk\n\t * over these rules in reverse order. For each rule type, we then keep join\n\t * orders that only contain the fewest number of join rules of that type.\n\t *\n\t * For example, the algorithm chooses join orders like the following:\n\t * (a) The algorithm prefers join orders with 2 cartesian products (CP) to\n\t * those that have 3 or more, if there isn't a join order with fewer CPs.\n\t * (b) Assuming that all join orders have the same number of CPs, the\n\t * algorithm prefers join orders with 2 dual partitions (DP) to those that\n\t * have 3 or more, if there isn't a join order with fewer DPs; and so\n\t * forth.\n\t */\n\tfor (uint32 ruleTypeIndex = highestValidIndex; ruleTypeIndex > 0; ruleTypeIndex--)\n\t{\n\t\tJoinRuleType ruleType = (JoinRuleType) ruleTypeIndex;\n\n\t\tcandidateJoinOrders = FewestOfJoinRuleType(candidateJoinOrders, ruleType);\n\t}\n\n\t/*\n\t * If there is a tie, we pick candidate join orders where large data\n\t * transfers happen at later stages of query execution. This results in more\n\t * data being filtered via joins, selections, and projections earlier on.\n\t */\n\tcandidateJoinOrders = LatestLargeDataTransfer(candidateJoinOrders);\n\n\t/* we should have at least one join order left after optimizations */\n\tcandidateCount = list_length(candidateJoinOrders);\n\tAssert(candidateCount > 0);\n\n\t/*\n\t * If there still is a tie, we pick the join order whose relation appeared\n\t * earliest in the query's range table entry list.\n\t */\n\tList *bestJoinOrder = (List *) linitial(candidateJoinOrders);\n\n\treturn bestJoinOrder;\n}\n\n\n/*\n * FewestOfJoinRuleType finds join orders that have the fewest number of times\n * the given join rule occurs in the candidate join orders, and filters all\n * other join orders. For example, if four candidate join orders have a join\n * rule appearing 3, 5, 3, and 6 times, only two join orders that have the join\n * rule appearing 3 times will be returned.\n */\nstatic List *\nFewestOfJoinRuleType(List *candidateJoinOrders, JoinRuleType ruleType)\n{\n\tList *fewestJoinOrders = NULL;\n\tuint32 fewestRuleCount = INT_MAX;\n\tListCell *joinOrderCell = NULL;\n\n\tforeach(joinOrderCell, candidateJoinOrders)\n\t{\n\t\tList *joinOrder = (List *) lfirst(joinOrderCell);\n\t\tuint32 ruleTypeCount = JoinRuleTypeCount(joinOrder, ruleType);\n\n\t\tif (ruleTypeCount == fewestRuleCount)\n\t\t{\n\t\t\tfewestJoinOrders = lappend(fewestJoinOrders, joinOrder);\n\t\t}\n\t\telse if (ruleTypeCount < fewestRuleCount)\n\t\t{\n\t\t\tfewestJoinOrders = list_make1(joinOrder);\n\t\t\tfewestRuleCount = ruleTypeCount;\n\t\t}\n\t}\n\n\treturn fewestJoinOrders;\n}\n\n\n/* Counts the number of times the given join rule occurs in the join order. */\nstatic uint32\nJoinRuleTypeCount(List *joinOrder, JoinRuleType ruleTypeToCount)\n{\n\tuint32 ruleTypeCount = 0;\n\tListCell *joinOrderNodeCell = NULL;\n\n\tforeach(joinOrderNodeCell, joinOrder)\n\t{\n\t\tJoinOrderNode *joinOrderNode = (JoinOrderNode *) lfirst(joinOrderNodeCell);\n\n\t\tJoinRuleType ruleType = joinOrderNode->joinRuleType;\n\t\tif (ruleType == ruleTypeToCount)\n\t\t{\n\t\t\truleTypeCount++;\n\t\t}\n\t}\n\n\treturn ruleTypeCount;\n}\n\n\n/*\n * LatestLargeDataTransfer finds and returns join orders where a large data\n * transfer join rule occurs as late as possible in the join order. Late large\n * data transfers result in more data being filtered before data gets shuffled\n * in the network.\n */\nstatic List *\nLatestLargeDataTransfer(List *candidateJoinOrders)\n{\n\tList *latestJoinOrders = NIL;\n\tuint32 latestJoinLocation = 0;\n\tListCell *joinOrderCell = NULL;\n\n\tforeach(joinOrderCell, candidateJoinOrders)\n\t{\n\t\tList *joinOrder = (List *) lfirst(joinOrderCell);\n\t\tuint32 joinRuleLocation = LargeDataTransferLocation(joinOrder);\n\n\t\tif (joinRuleLocation == latestJoinLocation)\n\t\t{\n\t\t\tlatestJoinOrders = lappend(latestJoinOrders, joinOrder);\n\t\t}\n\t\telse if (joinRuleLocation > latestJoinLocation)\n\t\t{\n\t\t\tlatestJoinOrders = list_make1(joinOrder);\n\t\t\tlatestJoinLocation = joinRuleLocation;\n\t\t}\n\t}\n\n\treturn latestJoinOrders;\n}\n\n\n/*\n * LargeDataTransferLocation finds the first location of a large data transfer\n * join rule, and returns that location. If the join order does not have any\n * large data transfer rules, the function returns one location past the end of\n * the join order list.\n */\nstatic uint32\nLargeDataTransferLocation(List *joinOrder)\n{\n\tuint32 joinRuleLocation = 0;\n\tListCell *joinOrderNodeCell = NULL;\n\n\tforeach(joinOrderNodeCell, joinOrder)\n\t{\n\t\tJoinOrderNode *joinOrderNode = (JoinOrderNode *) lfirst(joinOrderNodeCell);\n\t\tJoinRuleType joinRuleType = joinOrderNode->joinRuleType;\n\n\t\t/* we consider the following join rules to cause large data transfers */\n\t\tif (joinRuleType == SINGLE_HASH_PARTITION_JOIN ||\n\t\t\tjoinRuleType == SINGLE_RANGE_PARTITION_JOIN ||\n\t\t\tjoinRuleType == DUAL_PARTITION_JOIN ||\n\t\t\tjoinRuleType == CARTESIAN_PRODUCT)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tjoinRuleLocation++;\n\t}\n\n\treturn joinRuleLocation;\n}\n\n\n/* Prints the join order list and join rules for debugging purposes. */\nstatic void\nPrintJoinOrderList(List *joinOrder)\n{\n\tStringInfo printBuffer = makeStringInfo();\n\tListCell *joinOrderNodeCell = NULL;\n\tbool firstJoinNode = true;\n\n\tforeach(joinOrderNodeCell, joinOrder)\n\t{\n\t\tJoinOrderNode *joinOrderNode = (JoinOrderNode *) lfirst(joinOrderNodeCell);\n\t\tOid relationId = joinOrderNode->tableEntry->relationId;\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tif (firstJoinNode)\n\t\t{\n\t\t\tappendStringInfo(printBuffer, \"[ \\\"%s\\\" ]\", relationName);\n\t\t\tfirstJoinNode = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tJoinRuleType ruleType = (JoinRuleType) joinOrderNode->joinRuleType;\n\t\t\tchar *ruleName = JoinRuleName(ruleType);\n\n\t\t\tappendStringInfo(printBuffer, \"[ %s \", ruleName);\n\t\t\tappendStringInfo(printBuffer, \"\\\"%s\\\" ]\", relationName);\n\t\t}\n\t}\n\n\tereport(LOG, (errmsg(\"join order: %s\",\n\t\t\t\t\t\t printBuffer->data)));\n}\n\n\n/*\n * TableEntryListDifference returns a list containing table entries that are in\n * the left-hand side table list, but not in the right-hand side table list.\n */\nstatic List *\nTableEntryListDifference(List *lhsTableList, List *rhsTableList)\n{\n\tList *tableListDifference = NIL;\n\tListCell *lhsTableCell = NULL;\n\n\tforeach(lhsTableCell, lhsTableList)\n\t{\n\t\tTableEntry *lhsTableEntry = (TableEntry *) lfirst(lhsTableCell);\n\t\tListCell *rhsTableCell = NULL;\n\t\tbool lhsTableEntryExists = false;\n\n\t\tforeach(rhsTableCell, rhsTableList)\n\t\t{\n\t\t\tTableEntry *rhsTableEntry = (TableEntry *) lfirst(rhsTableCell);\n\n\t\t\tif ((lhsTableEntry->relationId == rhsTableEntry->relationId) &&\n\t\t\t\t(lhsTableEntry->rangeTableId == rhsTableEntry->rangeTableId))\n\t\t\t{\n\t\t\t\tlhsTableEntryExists = true;\n\t\t\t}\n\t\t}\n\n\t\tif (!lhsTableEntryExists)\n\t\t{\n\t\t\ttableListDifference = lappend(tableListDifference, lhsTableEntry);\n\t\t}\n\t}\n\n\treturn tableListDifference;\n}\n\n\n/*\n * EvaluateJoinRules takes in a list of already joined tables and a candidate\n * next table, evaluates different join rules between the two tables, and finds\n * the best join rule that applies. The function returns the applicable join\n * order node which includes the join rule and the partition information.\n */\nstatic JoinOrderNode *\nEvaluateJoinRules(List *joinedTableList, JoinOrderNode *currentJoinNode,\n\t\t\t\t  TableEntry *candidateTable, List *joinClauseList,\n\t\t\t\t  JoinType joinType)\n{\n\tJoinOrderNode *nextJoinNode = NULL;\n\tuint32 lowestValidIndex = JOIN_RULE_INVALID_FIRST + 1;\n\tuint32 highestValidIndex = JOIN_RULE_LAST - 1;\n\n\t/*\n\t * We first find all applicable join clauses between already joined tables\n\t * and the candidate table.\n\t */\n\tList *joinedTableIdList = RangeTableIdList(joinedTableList);\n\tuint32 candidateTableId = candidateTable->rangeTableId;\n\tList *applicableJoinClauses = ApplicableJoinClauses(joinedTableIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcandidateTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tjoinClauseList);\n\n\t/* we then evaluate all join rules in order */\n\tfor (uint32 ruleIndex = lowestValidIndex; ruleIndex <= highestValidIndex; ruleIndex++)\n\t{\n\t\tJoinRuleType ruleType = (JoinRuleType) ruleIndex;\n\t\tRuleEvalFunction ruleEvalFunction = JoinRuleEvalFunction(ruleType);\n\n\t\tnextJoinNode = (*ruleEvalFunction)(currentJoinNode,\n\t\t\t\t\t\t\t\t\t\t   candidateTable,\n\t\t\t\t\t\t\t\t\t\t   applicableJoinClauses,\n\t\t\t\t\t\t\t\t\t\t   joinType);\n\n\t\t/* break after finding the first join rule that applies */\n\t\tif (nextJoinNode != NULL)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (nextJoinNode == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tAssert(nextJoinNode != NULL);\n\tnextJoinNode->joinType = joinType;\n\tnextJoinNode->joinClauseList = applicableJoinClauses;\n\treturn nextJoinNode;\n}\n\n\n/* Extracts range table identifiers from the given table list, and returns them. */\nstatic List *\nRangeTableIdList(List *tableList)\n{\n\tList *rangeTableIdList = NIL;\n\tListCell *tableCell = NULL;\n\n\tforeach(tableCell, tableList)\n\t{\n\t\tTableEntry *tableEntry = (TableEntry *) lfirst(tableCell);\n\n\t\tuint32 rangeTableId = tableEntry->rangeTableId;\n\t\trangeTableIdList = lappend_int(rangeTableIdList, rangeTableId);\n\t}\n\n\treturn rangeTableIdList;\n}\n\n\n/*\n * JoinRuleEvalFunction returns a function pointer for the rule evaluation\n * function; this rule evaluation function corresponds to the given rule type.\n * The function also initializes the rule evaluation function array in a static\n * code block, if the array has not been initialized.\n */\nstatic RuleEvalFunction\nJoinRuleEvalFunction(JoinRuleType ruleType)\n{\n\tstatic bool ruleEvalFunctionsInitialized = false;\n\n\tif (!ruleEvalFunctionsInitialized)\n\t{\n\t\tRuleEvalFunctionArray[REFERENCE_JOIN] = &ReferenceJoin;\n\t\tRuleEvalFunctionArray[LOCAL_PARTITION_JOIN] = &LocalJoin;\n\t\tRuleEvalFunctionArray[SINGLE_RANGE_PARTITION_JOIN] = &SinglePartitionJoin;\n\t\tRuleEvalFunctionArray[SINGLE_HASH_PARTITION_JOIN] = &SinglePartitionJoin;\n\t\tRuleEvalFunctionArray[DUAL_PARTITION_JOIN] = &DualPartitionJoin;\n\t\tRuleEvalFunctionArray[CARTESIAN_PRODUCT_REFERENCE_JOIN] =\n\t\t\t&CartesianProductReferenceJoin;\n\t\tRuleEvalFunctionArray[CARTESIAN_PRODUCT] = &CartesianProduct;\n\n\t\truleEvalFunctionsInitialized = true;\n\t}\n\n\tRuleEvalFunction ruleEvalFunction = RuleEvalFunctionArray[ruleType];\n\tAssert(ruleEvalFunction != NULL);\n\n\treturn ruleEvalFunction;\n}\n\n\n/* Returns a string name for the given join rule type. */\nstatic char *\nJoinRuleName(JoinRuleType ruleType)\n{\n\tstatic bool ruleNamesInitialized = false;\n\n\tif (!ruleNamesInitialized)\n\t{\n\t\t/* use strdup() to be independent of memory contexts */\n\t\tRuleNameArray[REFERENCE_JOIN] = strdup(\"reference join\");\n\t\tRuleNameArray[LOCAL_PARTITION_JOIN] = strdup(\"local partition join\");\n\t\tRuleNameArray[SINGLE_HASH_PARTITION_JOIN] =\n\t\t\tstrdup(\"single hash partition join\");\n\t\tRuleNameArray[SINGLE_RANGE_PARTITION_JOIN] =\n\t\t\tstrdup(\"single range partition join\");\n\t\tRuleNameArray[DUAL_PARTITION_JOIN] = strdup(\"dual partition join\");\n\t\tRuleNameArray[CARTESIAN_PRODUCT_REFERENCE_JOIN] = strdup(\n\t\t\t\"cartesian product reference join\");\n\t\tRuleNameArray[CARTESIAN_PRODUCT] = strdup(\"cartesian product\");\n\n\t\truleNamesInitialized = true;\n\t}\n\n\tchar *ruleName = RuleNameArray[ruleType];\n\tAssert(ruleName != NULL);\n\n\treturn ruleName;\n}\n\n\n/*\n * ReferenceJoin evaluates if the candidate table is a reference table for inner,\n * left and anti join. For right join, current join node must be represented by\n * a reference table. For full join, both of them must be a reference table.\n */\nstatic JoinOrderNode *\nReferenceJoin(JoinOrderNode *currentJoinNode, TableEntry *candidateTable,\n\t\t\t  List *applicableJoinClauses, JoinType joinType)\n{\n\tint applicableJoinCount = list_length(applicableJoinClauses);\n\tif (applicableJoinCount <= 0)\n\t{\n\t\treturn NULL;\n\t}\n\n\tbool leftIsReferenceTable = IsCitusTableType(\n\t\tcurrentJoinNode->tableEntry->relationId,\n\t\tREFERENCE_TABLE);\n\tbool rightIsReferenceTable = IsCitusTableType(candidateTable->relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t  REFERENCE_TABLE);\n\tif (!IsSupportedReferenceJoin(joinType, leftIsReferenceTable, rightIsReferenceTable))\n\t{\n\t\treturn NULL;\n\t}\n\treturn MakeJoinOrderNode(candidateTable, REFERENCE_JOIN,\n\t\t\t\t\t\t\t currentJoinNode->partitionColumnList,\n\t\t\t\t\t\t\t currentJoinNode->partitionMethod,\n\t\t\t\t\t\t\t currentJoinNode->anchorTable);\n}\n\n\n/*\n * IsSupportedReferenceJoin checks if with this join type we can safely do a simple join\n * on the reference table on all the workers.\n */\nbool\nIsSupportedReferenceJoin(JoinType joinType, bool leftIsReferenceTable,\n\t\t\t\t\t\t bool rightIsReferenceTable)\n{\n\tif ((joinType == JOIN_INNER || joinType == JOIN_LEFT || joinType == JOIN_ANTI) &&\n\t\trightIsReferenceTable)\n\t{\n\t\treturn true;\n\t}\n\telse if ((joinType == JOIN_RIGHT) &&\n\t\t\t leftIsReferenceTable)\n\t{\n\t\treturn true;\n\t}\n\telse if (joinType == JOIN_FULL && leftIsReferenceTable && rightIsReferenceTable)\n\t{\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n\n/*\n * ReferenceJoin evaluates if the candidate table is a reference table for inner,\n * left and anti join. For right join, current join node must be represented by\n * a reference table. For full join, both of them must be a reference table.\n */\nstatic JoinOrderNode *\nCartesianProductReferenceJoin(JoinOrderNode *currentJoinNode, TableEntry *candidateTable,\n\t\t\t\t\t\t\t  List *applicableJoinClauses, JoinType joinType)\n{\n\tbool leftIsReferenceTable = IsCitusTableType(\n\t\tcurrentJoinNode->tableEntry->relationId,\n\t\tREFERENCE_TABLE);\n\tbool rightIsReferenceTable = IsCitusTableType(candidateTable->relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t  REFERENCE_TABLE);\n\n\tif (!IsSupportedReferenceJoin(joinType, leftIsReferenceTable, rightIsReferenceTable))\n\t{\n\t\treturn NULL;\n\t}\n\treturn MakeJoinOrderNode(candidateTable, CARTESIAN_PRODUCT_REFERENCE_JOIN,\n\t\t\t\t\t\t\t currentJoinNode->partitionColumnList,\n\t\t\t\t\t\t\t currentJoinNode->partitionMethod,\n\t\t\t\t\t\t\t currentJoinNode->anchorTable);\n}\n\n\n/*\n * LocalJoin takes the current partition key column and the candidate table's\n * partition key column and the partition method for each table. The function\n * then evaluates if tables in the join order and the candidate table can be\n * joined locally, without any data transfers. If they can, the function returns\n * a join order node for a local join. Otherwise, the function returns null.\n *\n * Anchor table is used to decide whether the JoinOrderNode can be joined\n * locally with the candidate table. That table is updated by each join type\n * applied over JoinOrderNode. Note that, we lost the anchor table after\n * dual partitioning and cartesian product.\n */\nstatic JoinOrderNode *\nLocalJoin(JoinOrderNode *currentJoinNode, TableEntry *candidateTable,\n\t\t  List *applicableJoinClauses, JoinType joinType)\n{\n\tOid relationId = candidateTable->relationId;\n\tuint32 tableId = candidateTable->rangeTableId;\n\tVar *candidatePartitionColumn = PartitionColumn(relationId, tableId);\n\tList *currentPartitionColumnList = currentJoinNode->partitionColumnList;\n\tchar candidatePartitionMethod = PartitionMethod(relationId);\n\tchar currentPartitionMethod = currentJoinNode->partitionMethod;\n\tTableEntry *currentAnchorTable = currentJoinNode->anchorTable;\n\n\t/*\n\t * If we previously dual-hash re-partitioned the tables for a join or made cartesian\n\t * product, there is no anchor table anymore. In that case we don't allow local join.\n\t */\n\tif (currentAnchorTable == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\t/* the partition method should be the same for a local join */\n\tif (currentPartitionMethod != candidatePartitionMethod)\n\t{\n\t\treturn NULL;\n\t}\n\n\tbool joinOnPartitionColumns = JoinOnColumns(currentPartitionColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\tcandidatePartitionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\tapplicableJoinClauses);\n\tif (!joinOnPartitionColumns)\n\t{\n\t\treturn NULL;\n\t}\n\n\t/* shard interval lists must have 1-1 matching for local joins */\n\tbool coPartitionedTables = CoPartitionedTables(currentAnchorTable->relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t   relationId);\n\n\tif (!coPartitionedTables)\n\t{\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * Since we are applying a local join to the candidate table we need to keep track of\n\t * the partition column of the candidate table on the MultiJoinNode. This will allow\n\t * subsequent joins colocated with this candidate table to correctly be recognized as\n\t * a local join as well.\n\t */\n\tcurrentPartitionColumnList = list_append_unique(currentPartitionColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcandidatePartitionColumn);\n\n\tJoinOrderNode *nextJoinNode = MakeJoinOrderNode(candidateTable, LOCAL_PARTITION_JOIN,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcurrentPartitionColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcurrentPartitionMethod,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcurrentAnchorTable);\n\n\n\treturn nextJoinNode;\n}\n\n\n/*\n * SinglePartitionJoin takes the current and the candidate table's partition keys\n * and methods. The function then evaluates if either \"tables in the join order\"\n * or the candidate table is already partitioned on a join column. If they are,\n * the function returns a join order node with the already partitioned column as\n * the next partition key. Otherwise, the function returns null.\n */\nstatic JoinOrderNode *\nSinglePartitionJoin(JoinOrderNode *currentJoinNode, TableEntry *candidateTable,\n\t\t\t\t\tList *applicableJoinClauses, JoinType joinType)\n{\n\tList *currentPartitionColumnList = currentJoinNode->partitionColumnList;\n\tchar currentPartitionMethod = currentJoinNode->partitionMethod;\n\tTableEntry *currentAnchorTable = currentJoinNode->anchorTable;\n\tJoinRuleType currentJoinRuleType = currentJoinNode->joinRuleType;\n\n\n\tOid relationId = candidateTable->relationId;\n\tuint32 tableId = candidateTable->rangeTableId;\n\tVar *candidatePartitionColumn = PartitionColumn(relationId, tableId);\n\tchar candidatePartitionMethod = PartitionMethod(relationId);\n\n\t/* outer joins are not supported yet */\n\tif (IS_OUTER_JOIN(joinType))\n\t{\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * If we previously dual-hash re-partitioned the tables for a join or made\n\t * cartesian product, we currently don't allow a single-repartition join.\n\t */\n\tif (currentJoinRuleType == DUAL_PARTITION_JOIN ||\n\t\tcurrentJoinRuleType == CARTESIAN_PRODUCT)\n\t{\n\t\treturn NULL;\n\t}\n\n\tOpExpr *joinClause =\n\t\tSinglePartitionJoinClause(currentPartitionColumnList, applicableJoinClauses,\n\t\t\t\t\t\t\t\t  NULL);\n\tif (joinClause != NULL)\n\t{\n\t\tif (currentPartitionMethod == DISTRIBUTE_BY_HASH)\n\t\t{\n\t\t\t/*\n\t\t\t * Single hash repartitioning may perform worse than dual hash\n\t\t\t * repartitioning. Thus, we control it via a guc.\n\t\t\t */\n\t\t\tif (!EnableSingleHashRepartitioning)\n\t\t\t{\n\t\t\t\treturn NULL;\n\t\t\t}\n\n\t\t\treturn MakeJoinOrderNode(candidateTable, SINGLE_HASH_PARTITION_JOIN,\n\t\t\t\t\t\t\t\t\t currentPartitionColumnList,\n\t\t\t\t\t\t\t\t\t currentPartitionMethod,\n\t\t\t\t\t\t\t\t\t currentAnchorTable);\n\t\t}\n\t\telse if (candidatePartitionMethod == DISTRIBUTE_BY_RANGE)\n\t\t{\n\t\t\treturn MakeJoinOrderNode(candidateTable, SINGLE_RANGE_PARTITION_JOIN,\n\t\t\t\t\t\t\t\t\t currentPartitionColumnList,\n\t\t\t\t\t\t\t\t\t currentPartitionMethod,\n\t\t\t\t\t\t\t\t\t currentAnchorTable);\n\t\t}\n\t}\n\n\t/* evaluate re-partitioning the current table only if the rule didn't apply above */\n\tif (candidatePartitionMethod != DISTRIBUTE_BY_NONE)\n\t{\n\t\t/*\n\t\t * Create a new unique list (set) with the partition column of the candidate table\n\t\t * to check if a single repartition join will work for this table. When it works\n\t\t * the set is retained on the MultiJoinNode for later local join verification.\n\t\t */\n\t\tList *candidatePartitionColumnList = list_make1(candidatePartitionColumn);\n\t\tjoinClause = SinglePartitionJoinClause(candidatePartitionColumnList,\n\t\t\t\t\t\t\t\t\t\t\t   applicableJoinClauses,\n\t\t\t\t\t\t\t\t\t\t\t   NULL);\n\t\tif (joinClause != NULL)\n\t\t{\n\t\t\tif (candidatePartitionMethod == DISTRIBUTE_BY_HASH)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Single hash repartitioning may perform worse than dual hash\n\t\t\t\t * repartitioning. Thus, we control it via a guc.\n\t\t\t\t */\n\t\t\t\tif (!EnableSingleHashRepartitioning)\n\t\t\t\t{\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\n\t\t\t\treturn MakeJoinOrderNode(candidateTable,\n\t\t\t\t\t\t\t\t\t\t SINGLE_HASH_PARTITION_JOIN,\n\t\t\t\t\t\t\t\t\t\t candidatePartitionColumnList,\n\t\t\t\t\t\t\t\t\t\t candidatePartitionMethod,\n\t\t\t\t\t\t\t\t\t\t candidateTable);\n\t\t\t}\n\t\t\telse if (currentPartitionMethod == DISTRIBUTE_BY_RANGE)\n\t\t\t{\n\t\t\t\treturn MakeJoinOrderNode(candidateTable,\n\t\t\t\t\t\t\t\t\t\t SINGLE_RANGE_PARTITION_JOIN,\n\t\t\t\t\t\t\t\t\t\t candidatePartitionColumnList,\n\t\t\t\t\t\t\t\t\t\t candidatePartitionMethod,\n\t\t\t\t\t\t\t\t\t\t candidateTable);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * SinglePartitionJoinClause walks over the applicable join clause list, and\n * finds an applicable join clause for the given partition column. If no such\n * clause exists, the function returns NULL.\n */\nOpExpr *\nSinglePartitionJoinClause(List *partitionColumnList, List *applicableJoinClauses, bool\n\t\t\t\t\t\t  *foundTypeMismatch)\n{\n\tif (foundTypeMismatch)\n\t{\n\t\t*foundTypeMismatch = false;\n\t}\n\n\tif (list_length(partitionColumnList) == 0)\n\t{\n\t\treturn NULL;\n\t}\n\n\tVar *partitionColumn = NULL;\n\tforeach_declared_ptr(partitionColumn, partitionColumnList)\n\t{\n\t\tNode *applicableJoinClause = NULL;\n\t\tforeach_declared_ptr(applicableJoinClause, applicableJoinClauses)\n\t\t{\n\t\t\tif (!NodeIsEqualsOpExpr(applicableJoinClause))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tOpExpr *applicableJoinOpExpr = castNode(OpExpr, applicableJoinClause);\n\t\t\tVar *leftColumn = LeftColumnOrNULL(applicableJoinOpExpr);\n\t\t\tVar *rightColumn = RightColumnOrNULL(applicableJoinOpExpr);\n\t\t\tif (leftColumn == NULL || rightColumn == NULL)\n\t\t\t{\n\t\t\t\t/* not a simple partition column join */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t * We first check if partition column matches either of the join columns\n\t\t\t * and if it does, we then check if the join column types match. If the\n\t\t\t * types are different, we will use different hash functions for the two\n\t\t\t * column types, and will incorrectly repartition the data.\n\t\t\t */\n\t\t\tif (equal(leftColumn, partitionColumn) || equal(rightColumn, partitionColumn))\n\t\t\t{\n\t\t\t\tif (leftColumn->vartype == rightColumn->vartype)\n\t\t\t\t{\n\t\t\t\t\treturn applicableJoinOpExpr;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tereport(DEBUG1, (errmsg(\"single partition column types do not \"\n\t\t\t\t\t\t\t\t\t\t\t\"match\")));\n\t\t\t\t\tif (foundTypeMismatch)\n\t\t\t\t\t{\n\t\t\t\t\t\t*foundTypeMismatch = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * DualPartitionJoin evaluates if a join clause exists between \"tables in the\n * join order\" and the candidate table. If such a clause exists, both tables can\n * be repartitioned on the join column; and the function returns a join order\n * node with the join column as the next partition key. Otherwise, the function\n * returns null.\n */\nstatic JoinOrderNode *\nDualPartitionJoin(JoinOrderNode *currentJoinNode, TableEntry *candidateTable,\n\t\t\t\t  List *applicableJoinClauses, JoinType joinType)\n{\n\tOpExpr *joinClause = DualPartitionJoinClause(applicableJoinClauses);\n\tif (joinClause)\n\t{\n\t\t/* because of the dual partition, anchor table and partition column get lost */\n\t\treturn MakeJoinOrderNode(candidateTable,\n\t\t\t\t\t\t\t\t DUAL_PARTITION_JOIN,\n\t\t\t\t\t\t\t\t NIL,\n\t\t\t\t\t\t\t\t REDISTRIBUTE_BY_HASH,\n\t\t\t\t\t\t\t\t NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * DualPartitionJoinClause walks over the applicable join clause list, and finds\n * an applicable join clause for dual re-partitioning. If no such clause exists,\n * the function returns NULL.\n */\nOpExpr *\nDualPartitionJoinClause(List *applicableJoinClauses)\n{\n\tNode *applicableJoinClause = NULL;\n\tforeach_declared_ptr(applicableJoinClause, applicableJoinClauses)\n\t{\n\t\tif (!NodeIsEqualsOpExpr(applicableJoinClause))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tOpExpr *applicableJoinOpExpr = castNode(OpExpr, applicableJoinClause);\n\t\tVar *leftColumn = LeftColumnOrNULL(applicableJoinOpExpr);\n\t\tVar *rightColumn = RightColumnOrNULL(applicableJoinOpExpr);\n\n\t\tif (leftColumn == NULL || rightColumn == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* we only need to check that the join column types match */\n\t\tif (leftColumn->vartype == rightColumn->vartype)\n\t\t{\n\t\t\treturn applicableJoinOpExpr;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"dual partition column types do not match\")));\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * CartesianProduct always evaluates to true since all tables can be combined\n * using a cartesian product operator. This function acts as a catch-all rule,\n * in case none of the join rules apply.\n */\nstatic JoinOrderNode *\nCartesianProduct(JoinOrderNode *currentJoinNode, TableEntry *candidateTable,\n\t\t\t\t List *applicableJoinClauses, JoinType joinType)\n{\n\tif (list_length(applicableJoinClauses) == 0)\n\t{\n\t\t/* Because of the cartesian product, anchor table information got lost */\n\t\treturn MakeJoinOrderNode(candidateTable, CARTESIAN_PRODUCT,\n\t\t\t\t\t\t\t\t currentJoinNode->partitionColumnList,\n\t\t\t\t\t\t\t\t currentJoinNode->partitionMethod,\n\t\t\t\t\t\t\t\t NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/* Constructs and returns a join-order node with the given arguments */\nJoinOrderNode *\nMakeJoinOrderNode(TableEntry *tableEntry, JoinRuleType joinRuleType,\n\t\t\t\t  List *partitionColumnList, char partitionMethod,\n\t\t\t\t  TableEntry *anchorTable)\n{\n\tJoinOrderNode *joinOrderNode = palloc0(sizeof(JoinOrderNode));\n\tjoinOrderNode->tableEntry = tableEntry;\n\tjoinOrderNode->joinRuleType = joinRuleType;\n\tjoinOrderNode->joinType = JOIN_INNER;\n\tjoinOrderNode->partitionColumnList = partitionColumnList;\n\tjoinOrderNode->partitionMethod = partitionMethod;\n\tjoinOrderNode->joinClauseList = NIL;\n\tjoinOrderNode->anchorTable = anchorTable;\n\n\treturn joinOrderNode;\n}\n\n\n/*\n * IsApplicableJoinClause tests if the current joinClause is applicable to the join at\n * hand.\n *\n * Given a list of left hand tables and a candidate right hand table the join clause is\n * valid if atleast 1 column is from the right hand table AND all columns can be found\n * in either the list of tables on the left *or* in the right hand table.\n */\nbool\nIsApplicableJoinClause(List *leftTableIdList, uint32 rightTableId, Node *joinClause)\n{\n\tList *varList = pull_var_clause_default(joinClause);\n\tVar *var = NULL;\n\tbool joinContainsRightTable = false;\n\tforeach_declared_ptr(var, varList)\n\t{\n\t\tuint32 columnTableId = var->varno;\n\t\tif (rightTableId == columnTableId)\n\t\t{\n\t\t\tjoinContainsRightTable = true;\n\t\t}\n\t\telse if (!list_member_int(leftTableIdList, columnTableId))\n\t\t{\n\t\t\t/*\n\t\t\t * We couldn't find this column either on the right hand side (first if\n\t\t\t * statement), nor in the list on the left. This join clause involves a table\n\t\t\t * not yet available during the candidate join.\n\t\t\t */\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/*\n\t * All columns referenced in this clause are available during this join, now the join\n\t * is applicable if we found our candidate table as well\n\t */\n\treturn joinContainsRightTable;\n}\n\n\n/*\n * ApplicableJoinClauses finds all join clauses that apply between the given\n * left table list and the right table, and returns these found join clauses.\n */\nList *\nApplicableJoinClauses(List *leftTableIdList, uint32 rightTableId, List *joinClauseList)\n{\n\tList *applicableJoinClauses = NIL;\n\n\t/* make sure joinClauseList contains only join clauses */\n\tjoinClauseList = JoinClauseList(joinClauseList);\n\n\tNode *joinClause = NULL;\n\tforeach_declared_ptr(joinClause, joinClauseList)\n\t{\n\t\tif (IsApplicableJoinClause(leftTableIdList, rightTableId, joinClause))\n\t\t{\n\t\t\tapplicableJoinClauses = lappend(applicableJoinClauses, joinClause);\n\t\t}\n\t}\n\n\treturn applicableJoinClauses;\n}\n\n\n/*\n * Returns the left column only when directly referenced in the given join clause,\n * otherwise NULL is returned.\n */\nVar *\nLeftColumnOrNULL(OpExpr *joinClause)\n{\n\tList *argumentList = joinClause->args;\n\tNode *leftArgument = (Node *) linitial(argumentList);\n\n\tleftArgument = strip_implicit_coercions(leftArgument);\n\tif (!IsA(leftArgument, Var))\n\t{\n\t\treturn NULL;\n\t}\n\treturn castNode(Var, leftArgument);\n}\n\n\n/*\n * Returns the right column only when directly referenced in the given join clause,\n * otherwise NULL is returned.\n * */\nVar *\nRightColumnOrNULL(OpExpr *joinClause)\n{\n\tList *argumentList = joinClause->args;\n\tNode *rightArgument = (Node *) lsecond(argumentList);\n\n\trightArgument = strip_implicit_coercions(rightArgument);\n\tif (!IsA(rightArgument, Var))\n\t{\n\t\treturn NULL;\n\t}\n\treturn castNode(Var, rightArgument);\n}\n\n\n/*\n * PartitionColumn builds the partition column for the given relation, and sets\n * the partition column's range table references to the given table identifier.\n *\n * Note that reference tables do not have partition column. Thus, this function\n * returns NULL when called for reference tables.\n */\nVar *\nPartitionColumn(Oid relationId, uint32 rangeTableId)\n{\n\tVar *partitionKey = DistPartitionKey(relationId);\n\tVar *partitionColumn = NULL;\n\n\t/* short circuit for reference tables */\n\tif (partitionKey == NULL)\n\t{\n\t\treturn partitionColumn;\n\t}\n\n\tpartitionColumn = partitionKey;\n\tpartitionColumn->varno = rangeTableId;\n\tpartitionColumn->varnosyn = rangeTableId;\n\n\treturn partitionColumn;\n}\n\n\n/*\n * DistPartitionKey returns the partition key column for the given relation. Note\n * that in the context of distributed join and query planning, the callers of\n * this function *must* set the partition key column's range table reference\n * (varno) to match the table's location in the query range table list.\n *\n * Note that reference tables do not have partition column. Thus, this function\n * returns NULL when called for reference tables.\n */\nVar *\nDistPartitionKey(Oid relationId)\n{\n\tCitusTableCacheEntry *partitionEntry = GetCitusTableCacheEntry(relationId);\n\n\t/* non-distributed tables do not have partition column */\n\tif (!HasDistributionKeyCacheEntry(partitionEntry))\n\t{\n\t\treturn NULL;\n\t}\n\n\treturn copyObject(partitionEntry->partitionColumn);\n}\n\n\n/*\n * DistPartitionKeyOrError is the same as DistPartitionKey but errors out instead\n * of returning NULL if this is called with a relationId of a reference table.\n */\nVar *\nDistPartitionKeyOrError(Oid relationId)\n{\n\tVar *partitionKey = DistPartitionKey(relationId);\n\n\tif (partitionKey == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"no distribution column found for relation %d\",\n\t\t\t\t\t\t\trelationId)));\n\t}\n\n\treturn partitionKey;\n}\n\n\n/* Returns the partition method for the given relation. */\nchar\nPartitionMethod(Oid relationId)\n{\n\t/* errors out if not a distributed table */\n\tCitusTableCacheEntry *partitionEntry = GetCitusTableCacheEntry(relationId);\n\n\tchar partitionMethod = partitionEntry->partitionMethod;\n\n\treturn partitionMethod;\n}\n\n\n/* Returns the replication model for the given relation. */\nchar\nTableReplicationModel(Oid relationId)\n{\n\t/* errors out if not a distributed table */\n\tCitusTableCacheEntry *partitionEntry = GetCitusTableCacheEntry(relationId);\n\n\tchar replicationModel = partitionEntry->replicationModel;\n\n\treturn replicationModel;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/multi_logical_optimizer.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_logical_optimizer.c\n *\t  Routines for optimizing logical plan trees based on multi-relational\n *\t  algebra.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <math.h>\n\n#include \"postgres.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/nbtree.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/pg_aggregate.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/extension.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/tlist.h\"\n#include \"parser/parse_agg.h\"\n#include \"parser/parse_coerce.h\"\n#include \"parser/parse_oper.h\"\n#include \"parser/parsetree.h\"\n#include \"rewrite/rewriteManip.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/extended_op_node_utils.h\"\n#include \"distributed/function_utils.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/query_pushdown_planning.h\"\n#include \"distributed/string_utils.h\"\n#include \"distributed/tdigest_extension.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n/* Config variable managed via guc.c */\nint LimitClauseRowFetchCount = -1; /* number of rows to fetch from each task */\ndouble CountDistinctErrorRate = 0.0; /* precision of count(distinct) approximate */\nint CoordinatorAggregationStrategy = COORDINATOR_AGGREGATION_ROW_GATHER;\nbool AllowAggregateWorkerCombineOnInternalTypes = true;\n\n/* Constant used throughout file */\nstatic const uint32 masterTableId = 1; /* first range table reference on the master node */\n\ntypedef struct MasterAggregateWalkerContext\n{\n\tconst ExtendedOpNodeProperties *extendedOpNodeProperties;\n\tAttrNumber columnId;\n} MasterAggregateWalkerContext;\n\ntypedef struct WorkerAggregateWalkerContext\n{\n\tconst ExtendedOpNodeProperties *extendedOpNodeProperties;\n\tList *expressionList;\n\tbool createGroupByClause;\n} WorkerAggregateWalkerContext;\n\n\n/*\n * QueryTargetList encapsulates the necessary fields to form\n * worker query's target list.\n */\ntypedef struct QueryTargetList\n{\n\tList *targetEntryList; /* the list of target entries */\n\tAttrNumber targetProjectionNumber; /* the index of the last entry */\n} QueryTargetList;\n\n\n/*\n * QueryGroupClause encapsulates the necessary fields to form\n * worker query's group by clause.\n */\ntypedef struct QueryGroupClause\n{\n\tList *groupClauseList; /* the list of group clause entries */\n\tIndex *nextSortGroupRefIndex; /* pointer to the index of the largest sort group reference index */\n} QueryGroupClause;\n\n\n/*\n * QueryDistinctClause encapsulates the necessary fields to form\n * worker query's DISTINCT/DISTINCT ON parts.\n */\ntypedef struct QueryDistinctClause\n{\n\tList *workerDistinctClause; /* the list of distinct clause entries */\n\tbool workerHasDistinctOn;\n} QueryDistinctClause;\n\n\n/*\n * QueryWindowClause encapsulates the necessary fields to form\n * worker query's window clause.\n */\ntypedef struct QueryWindowClause\n{\n\tList *workerWindowClauseList; /* the list of window clause entries */\n\tbool hasWindowFunctions;\n\tIndex *nextSortGroupRefIndex; /* see QueryGroupClause */\n} QueryWindowClause;\n\n\n/*\n * QueryOrderByLimit encapsulates the necessary fields to form\n * worker query's order by and limit clauses. Note that we don't\n * keep track of limit offset clause since it is incorporated\n * into the limit clause during the processing.\n */\ntypedef struct QueryOrderByLimit\n{\n\tNode *workerLimitCount;\n\tList *workerSortClauseList;\n\tIndex *nextSortGroupRefIndex; /* see QueryGroupClause */\n} QueryOrderByLimit;\n\n\n/*\n * LimitPushdownable tells us how a limit can be pushed down.\n * See WorkerLimitCount for details.\n */\ntypedef enum LimitPushdownable\n{\n\tLIMIT_CANNOT_PUSHDOWN,\n\tLIMIT_CAN_PUSHDOWN,\n\tLIMIT_CAN_APPROXIMATE,\n} LimitPushdownable;\n\n\n/*\n * OrderByLimitReference a structure that is used commonly while\n * processing sort and limit clauses.\n */\ntypedef struct OrderByLimitReference\n{\n\tbool groupedByDisjointPartitionColumn;\n\tbool onlyPushableWindowFunctions;\n\tbool groupClauseIsEmpty;\n\tbool sortClauseIsEmpty;\n\tbool hasOrderByAggregate;\n\tbool canApproximate;\n\tbool hasDistinctOn;\n} OrderByLimitReference;\n\n\n/* Local functions forward declarations */\nstatic MultiSelect * AndSelectNode(MultiSelect *selectNode);\nstatic MultiSelect * OrSelectNode(MultiSelect *selectNode);\nstatic List * OrSelectClauseList(List *selectClauseList);\nstatic void PushDownNodeLoop(MultiUnaryNode *currentNode);\nstatic void PullUpCollectLoop(MultiCollect *collectNode);\nstatic void AddressProjectSpecialConditions(MultiProject *projectNode);\nstatic PushDownStatus CanPushDown(MultiUnaryNode *parentNode);\nstatic PullUpStatus CanPullUp(MultiUnaryNode *childNode);\nstatic PushDownStatus Commutative(MultiUnaryNode *parentNode,\n\t\t\t\t\t\t\t\t  MultiUnaryNode *childNode);\nstatic PushDownStatus Distributive(MultiUnaryNode *parentNode,\n\t\t\t\t\t\t\t\t   MultiBinaryNode *childNode);\nstatic PullUpStatus Factorizable(MultiBinaryNode *parentNode,\n\t\t\t\t\t\t\t\t MultiUnaryNode *childNode);\nstatic List * SelectClauseTableIdList(List *selectClauseList);\nstatic MultiUnaryNode * GenerateLeftNode(MultiUnaryNode *currentNode,\n\t\t\t\t\t\t\t\t\t\t MultiBinaryNode *binaryNode);\nstatic MultiUnaryNode * GenerateRightNode(MultiUnaryNode *currentNode,\n\t\t\t\t\t\t\t\t\t\t  MultiBinaryNode *binaryNode);\nstatic MultiUnaryNode * GenerateNode(MultiUnaryNode *currentNode, MultiNode *childNode);\nstatic List * TableIdListColumns(List *tableIdList, List *columnList);\nstatic List * TableIdListSelectClauses(List *tableIdList, List *selectClauseList);\nstatic void PushDownBelowUnaryChild(MultiUnaryNode *currentNode,\n\t\t\t\t\t\t\t\t\tMultiUnaryNode *childNode);\nstatic void PlaceUnaryNodeChild(MultiUnaryNode *unaryNode, MultiUnaryNode *childNode);\nstatic void PlaceBinaryNodeLeftChild(MultiBinaryNode *binaryNode,\n\t\t\t\t\t\t\t\t\t MultiUnaryNode *newLeftChildNode);\nstatic void PlaceBinaryNodeRightChild(MultiBinaryNode *binaryNode,\n\t\t\t\t\t\t\t\t\t  MultiUnaryNode *newRightChildNode);\nstatic void RemoveUnaryNode(MultiUnaryNode *unaryNode);\nstatic void PullUpUnaryNode(MultiUnaryNode *unaryNode);\nstatic void ParentSetNewChild(MultiNode *parentNode, MultiNode *oldChildNode,\n\t\t\t\t\t\t\t  MultiNode *newChildNode);\n\n/* Local functions forward declarations for aggregate expressions */\nstatic void ApplyExtendedOpNodes(MultiExtendedOp *originalNode,\n\t\t\t\t\t\t\t\t MultiExtendedOp *masterNode,\n\t\t\t\t\t\t\t\t MultiExtendedOp *workerNode);\nstatic void TransformSubqueryNode(MultiTable *subqueryNode,\n\t\t\t\t\t\t\t\t  bool subqueryHasNonDistributableAggregates);\nstatic MultiExtendedOp * MasterExtendedOpNode(MultiExtendedOp *originalOpNode,\n\t\t\t\t\t\t\t\t\t\t\t  ExtendedOpNodeProperties *\n\t\t\t\t\t\t\t\t\t\t\t  extendedOpNodeProperties);\nstatic Node * MasterAggregateMutator(Node *originalNode,\n\t\t\t\t\t\t\t\t\t MasterAggregateWalkerContext *walkerContext);\nstatic Expr * MasterAggregateExpression(Aggref *originalAggregate,\n\t\t\t\t\t\t\t\t\t\tMasterAggregateWalkerContext *walkerContext);\nstatic Expr * MasterAverageExpression(Oid sumAggregateType, Oid countAggregateType,\n\t\t\t\t\t\t\t\t\t  AttrNumber *columnId);\nstatic Expr * AddTypeConversion(Node *originalAggregate, Node *newExpression);\nstatic MultiExtendedOp * WorkerExtendedOpNode(MultiExtendedOp *originalOpNode,\n\t\t\t\t\t\t\t\t\t\t\t  ExtendedOpNodeProperties *\n\t\t\t\t\t\t\t\t\t\t\t  extendedOpNodeProperties);\nstatic void ProcessTargetListForWorkerQuery(List *targetEntryList,\n\t\t\t\t\t\t\t\t\t\t\tExtendedOpNodeProperties *\n\t\t\t\t\t\t\t\t\t\t\textendedOpNodeProperties,\n\t\t\t\t\t\t\t\t\t\t\tQueryTargetList *queryTargetList,\n\t\t\t\t\t\t\t\t\t\t\tQueryGroupClause *queryGroupClause);\nstatic void ProcessHavingClauseForWorkerQuery(Node *havingQual,\n\t\t\t\t\t\t\t\t\t\t\t  ExtendedOpNodeProperties *\n\t\t\t\t\t\t\t\t\t\t\t  extendedOpNodeProperties,\n\t\t\t\t\t\t\t\t\t\t\t  Node **workerHavingQual,\n\t\t\t\t\t\t\t\t\t\t\t  QueryTargetList *queryTargetList,\n\t\t\t\t\t\t\t\t\t\t\t  QueryGroupClause *queryGroupClause);\nstatic void ProcessDistinctClauseForWorkerQuery(List *distinctClause, bool hasDistinctOn,\n\t\t\t\t\t\t\t\t\t\t\t\tList *groupClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\tbool queryHasAggregates,\n\t\t\t\t\t\t\t\t\t\t\t\tQueryDistinctClause *queryDistinctClause,\n\t\t\t\t\t\t\t\t\t\t\t\tbool *distinctPreventsLimitPushdown);\nstatic void ProcessWindowFunctionsForWorkerQuery(List *windowClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t List *originalTargetEntryList,\n\t\t\t\t\t\t\t\t\t\t\t\t QueryWindowClause *queryWindowClause,\n\t\t\t\t\t\t\t\t\t\t\t\t QueryTargetList *queryTargetList);\nstatic void ProcessWindowFunctionPullUpForWorkerQuery(List *windowClause,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  QueryTargetList *queryTargetList);\nstatic void ProcessLimitOrderByForWorkerQuery(OrderByLimitReference orderByLimitReference,\n\t\t\t\t\t\t\t\t\t\t\t  Node *originalLimitCount, Node *limitOffset,\n\t\t\t\t\t\t\t\t\t\t\t  List *sortClauseList, List *groupClauseList,\n\t\t\t\t\t\t\t\t\t\t\t  List *originalTargetList,\n\t\t\t\t\t\t\t\t\t\t\t  QueryOrderByLimit *queryOrderByLimit,\n\t\t\t\t\t\t\t\t\t\t\t  QueryTargetList *queryTargetList);\nstatic OrderByLimitReference BuildOrderByLimitReference(bool hasDistinctOn, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tgroupedByDisjointPartitionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbool onlyPushableWindowFunctions,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *groupClause,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *sortClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *targetList);\nstatic void ExpandWorkerTargetEntry(List *expressionList,\n\t\t\t\t\t\t\t\t\tTargetEntry *originalTargetEntry,\n\t\t\t\t\t\t\t\t\tbool addToGroupByClause,\n\t\t\t\t\t\t\t\t\tQueryTargetList *queryTargetList,\n\t\t\t\t\t\t\t\t\tQueryGroupClause *queryGroupClause);\nstatic Index GetNextSortGroupRef(List *targetEntryList);\nstatic TargetEntry * GenerateWorkerTargetEntry(TargetEntry *targetEntry,\n\t\t\t\t\t\t\t\t\t\t\t   Expr *workerExpression,\n\t\t\t\t\t\t\t\t\t\t\t   AttrNumber targetProjectionNumber);\nstatic void AppendTargetEntryToGroupClause(TargetEntry *targetEntry,\n\t\t\t\t\t\t\t\t\t\t   QueryGroupClause *queryGroupClause);\nstatic bool WorkerAggregateWalker(Node *node,\n\t\t\t\t\t\t\t\t  WorkerAggregateWalkerContext *walkerContext);\nstatic List * WorkerAggregateExpressionList(Aggref *originalAggregate,\n\t\t\t\t\t\t\t\t\t\t\tWorkerAggregateWalkerContext *\n\t\t\t\t\t\t\t\t\t\t\twalkerContextry);\nstatic AggregateType GetAggregateType(Aggref *aggregatExpression);\nstatic Oid AggregateArgumentType(Aggref *aggregate);\nstatic Expr * FirstAggregateArgument(Aggref *aggregate);\nstatic bool AggregateEnabledCustom(Aggref *aggregateExpression);\nstatic Oid CitusFunctionOidWithSignature(char *functionName, int numargs, Oid *argtypes);\nstatic Oid WorkerPartialAggOid(void);\nstatic Oid WorkerBinaryPartialAggOid(void);\nstatic Oid CoordBinaryCombineAggOid(void);\nstatic bool IsAggTransTypeBinarySerializable(Form_pg_aggregate aggForm);\nstatic Oid CoordCombineAggOid(void);\nstatic Oid AggregateFunctionOid(const char *functionName, Oid inputType);\nstatic Oid TypeOid(Oid schemaId, const char *typeName);\nstatic SortGroupClause * CreateSortGroupClause(Var *column);\n\n/* Local functions forward declarations for count(distinct) approximations */\nstatic const char * CountDistinctHashFunctionName(Oid argumentType);\nstatic int CountDistinctStorageSize(double approximationErrorRate);\nstatic Const * MakeIntegerConstInt64(int64 integerValue);\nstatic Const * MakeIntegerConst(int32 integerValue);\n\n\n/* Local functions forward declarations for aggregate expression checks */\nstatic bool HasNonDistributableAggregates(MultiNode *logicalPlanNode);\nstatic bool CanPushDownExpression(Node *expression,\n\t\t\t\t\t\t\t\t  const ExtendedOpNodeProperties *\n\t\t\t\t\t\t\t\t  extendedOpNodeProperties);\nstatic DeferredErrorMessage * DeferErrorIfHasNonDistributableAggregates(MultiNode *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlogicalPlanNode);\nstatic DeferredErrorMessage * DeferErrorIfUnsupportedArrayAggregate(Aggref *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tarrayAggregateExpression);\nstatic DeferredErrorMessage * DeferErrorIfUnsupportedJsonAggregate(AggregateType type,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   Aggref *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   aggregateExpression);\nstatic DeferredErrorMessage * DeferErrorIfUnsupportedAggregateDistinct(Aggref *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   aggregateExpression,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   MultiNode *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   logicalPlanNode);\nstatic Var * AggregateDistinctColumn(Aggref *aggregateExpression);\nstatic bool TablePartitioningSupportsDistinct(List *tableNodeList,\n\t\t\t\t\t\t\t\t\t\t\t  MultiExtendedOp *opNode,\n\t\t\t\t\t\t\t\t\t\t\t  Var *distinctColumn,\n\t\t\t\t\t\t\t\t\t\t\t  AggregateType aggregateType);\n\n/* Local functions forward declarations for limit clauses */\nstatic Node * WorkerLimitCount(Node *limitCount, Node *limitOffset, OrderByLimitReference\n\t\t\t\t\t\t\t   orderByLimitReference);\nstatic List * WorkerSortClauseList(Node *limitCount,\n\t\t\t\t\t\t\t\t   List *groupClauseList, List *sortClauseList,\n\t\t\t\t\t\t\t\t   OrderByLimitReference orderByLimitReference);\nstatic bool CanPushDownLimitApproximate(List *sortClauseList, List *targetList);\nstatic bool HasOrderByAggregate(List *sortClauseList, List *targetList);\nstatic bool HasOrderByNonCommutativeAggregate(List *sortClauseList, List *targetList);\nstatic bool HasOrderByComplexExpression(List *sortClauseList, List *targetList);\nstatic bool HasOrderByHllType(List *sortClauseList, List *targetList);\nstatic bool ShouldProcessDistinctOrderAndLimitForWorker(ExtendedOpNodeProperties *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\textendedOpNodeProperties,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbool pushingDownOriginalGrouping,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tNode *havingQual);\nstatic bool IsIndexInRange(const List *list, int index);\n\n/*\n * MultiLogicalPlanOptimize applies multi-relational algebra optimizations on\n * the given logical plan tree. Specifically, the function applies four set of\n * optimizations in a particular order.\n *\n * First, the function splits the search node into two nodes that contain And\n * and Or clauses, and pushes down the node that contains And clauses. Second,\n * the function pushes down the project node; this node either contains columns\n * to return to the user, or aggregate expressions used by the aggregate node.\n * Third, the function pulls up the collect operators in the tree. Fourth, the\n * function finds the extended operator node, and splits this node into master\n * and worker extended operator nodes.\n */\nvoid\nMultiLogicalPlanOptimize(MultiTreeRoot *multiLogicalPlan)\n{\n\tMultiNode *logicalPlanNode = (MultiNode *) multiLogicalPlan;\n\tbool hasNonDistributableAggregates = HasNonDistributableAggregates(\n\t\tlogicalPlanNode);\n\tList *extendedOpNodeList = FindNodesOfType(logicalPlanNode, T_MultiExtendedOp);\n\tMultiExtendedOp *extendedOpNode = (MultiExtendedOp *) linitial(extendedOpNodeList);\n\tExtendedOpNodeProperties extendedOpNodeProperties = BuildExtendedOpNodeProperties(\n\t\textendedOpNode, hasNonDistributableAggregates);\n\n\tif (!extendedOpNodeProperties.groupedByDisjointPartitionColumn &&\n\t\t!extendedOpNodeProperties.pullUpIntermediateRows)\n\t{\n\t\tDeferredErrorMessage *aggregatePushdownError =\n\t\t\tDeferErrorIfHasNonDistributableAggregates(logicalPlanNode);\n\n\t\tif (aggregatePushdownError != NULL)\n\t\t{\n\t\t\tif (CoordinatorAggregationStrategy == COORDINATOR_AGGREGATION_DISABLED)\n\t\t\t{\n\t\t\t\tRaiseDeferredError(aggregatePushdownError, ERROR);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\textendedOpNodeProperties.pullUpIntermediateRows = true;\n\t\t\t\textendedOpNodeProperties.pushDownGroupingAndHaving = false;\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * If a select node exists, we use the idempower property to split the node\n\t * into two nodes that contain And and Or clauses. If both And and Or nodes\n\t * exist, we modify the tree in place to swap the original select node with\n\t * And and Or nodes. We then push down the And select node if it exists.\n\t */\n\tList *selectNodeList = FindNodesOfType(logicalPlanNode, T_MultiSelect);\n\tif (selectNodeList != NIL)\n\t{\n\t\tMultiSelect *selectNode = (MultiSelect *) linitial(selectNodeList);\n\t\tMultiSelect *andSelectNode = AndSelectNode(selectNode);\n\t\tMultiSelect *orSelectNode = OrSelectNode(selectNode);\n\n\t\tif (andSelectNode != NULL && orSelectNode != NULL)\n\t\t{\n\t\t\tMultiNode *parentNode = ParentNode((MultiNode *) selectNode);\n\t\t\tMultiNode *childNode = ChildNode((MultiUnaryNode *) selectNode);\n\t\t\tAssert(UnaryOperator(parentNode));\n\n\t\t\tSetChild((MultiUnaryNode *) parentNode, (MultiNode *) orSelectNode);\n\t\t\tSetChild((MultiUnaryNode *) orSelectNode, (MultiNode *) andSelectNode);\n\t\t\tSetChild((MultiUnaryNode *) andSelectNode, (MultiNode *) childNode);\n\t\t}\n\t\telse if (andSelectNode != NULL && orSelectNode == NULL)\n\t\t{\n\t\t\tandSelectNode = selectNode; /* no need to modify the tree */\n\t\t}\n\n\t\tif (andSelectNode != NULL)\n\t\t{\n\t\t\tPushDownNodeLoop((MultiUnaryNode *) andSelectNode);\n\t\t}\n\t}\n\n\t/* push down the multi project node */\n\tList *projectNodeList = FindNodesOfType(logicalPlanNode, T_MultiProject);\n\tMultiProject *projectNode = (MultiProject *) linitial(projectNodeList);\n\tPushDownNodeLoop((MultiUnaryNode *) projectNode);\n\n\t/* pull up collect nodes and merge duplicate collects */\n\tList *collectNodeList = FindNodesOfType(logicalPlanNode, T_MultiCollect);\n\tMultiCollect *collectNode = NULL;\n\tforeach_declared_ptr(collectNode, collectNodeList)\n\t{\n\t\tPullUpCollectLoop(collectNode);\n\t}\n\n\t/*\n\t * We split the extended operator node into its equivalent master and worker\n\t * operator nodes; and if the extended operator has aggregates, we transform\n\t * aggregate functions accordingly for the master and worker operator nodes.\n\t * If we can push down the limit clause, we also add limit count and sort\n\t * clause list to the worker operator node. We then push the worker operator\n\t * node below the collect node.\n\t */\n\tMultiExtendedOp *masterExtendedOpNode =\n\t\tMasterExtendedOpNode(extendedOpNode, &extendedOpNodeProperties);\n\tMultiExtendedOp *workerExtendedOpNode =\n\t\tWorkerExtendedOpNode(extendedOpNode, &extendedOpNodeProperties);\n\n\tApplyExtendedOpNodes(extendedOpNode, masterExtendedOpNode, workerExtendedOpNode);\n\n\tList *tableNodeList = FindNodesOfType(logicalPlanNode, T_MultiTable);\n\tMultiTable *tableNode = NULL;\n\tforeach_declared_ptr(tableNode, tableNodeList)\n\t{\n\t\tif (tableNode->relationId == SUBQUERY_RELATION_ID)\n\t\t{\n\t\t\tDeferredErrorMessage *error =\n\t\t\t\tDeferErrorIfHasNonDistributableAggregates((MultiNode *) tableNode);\n\t\t\tbool subqueryHasNonDistributableAggregates = false;\n\n\t\t\tif (error != NULL)\n\t\t\t{\n\t\t\t\tif (CoordinatorAggregationStrategy == COORDINATOR_AGGREGATION_DISABLED)\n\t\t\t\t{\n\t\t\t\t\tRaiseDeferredError(error, ERROR);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tsubqueryHasNonDistributableAggregates = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tTransformSubqueryNode(tableNode, subqueryHasNonDistributableAggregates);\n\t\t}\n\t}\n\n\t/*\n\t * When enabled, count(distinct) approximation uses hll as the intermediate\n\t * data type. We currently have a mismatch between hll target entry and sort\n\t * clause's sortop oid, so we can't push an order by on the hll data type to\n\t * the worker node. We check that here and error out if necessary.\n\t */\n\tbool hasOrderByHllType = HasOrderByHllType(workerExtendedOpNode->sortClauseList,\n\t\t\t\t\t\t\t\t\t\t\t   workerExtendedOpNode->targetList);\n\tif (hasOrderByHllType)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot approximate count(distinct) and order by it\"),\n\t\t\t\t\t\terrhint(\"You might need to disable approximations for either \"\n\t\t\t\t\t\t\t\t\"count(distinct) or limit through configuration.\")));\n\t}\n\n\tif (TargetListContainsSubquery(masterExtendedOpNode->targetList))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot push down subquery on the target list\"),\n\t\t\t\t\t\terrdetail(\"Subqueries in the SELECT part of the query can only \"\n\t\t\t\t\t\t\t\t  \"be pushed down if they happen before aggregates and \"\n\t\t\t\t\t\t\t\t  \"window functions\")));\n\t}\n}\n\n\n/*\n * AndSelectNode looks for AND clauses in the given select node. If they exist,\n * the function returns these clauses in a new node. Otherwise, the function\n * returns null.\n */\nstatic MultiSelect *\nAndSelectNode(MultiSelect *selectNode)\n{\n\tMultiSelect *andSelectNode = NULL;\n\tList *selectClauseList = selectNode->selectClauseList;\n\tList *orSelectClauseList = OrSelectClauseList(selectClauseList);\n\n\t/* AND clauses are select clauses that are not OR clauses */\n\tList *andSelectClauseList = list_difference(selectClauseList, orSelectClauseList);\n\tif (andSelectClauseList != NIL)\n\t{\n\t\tandSelectNode = CitusMakeNode(MultiSelect);\n\t\tandSelectNode->selectClauseList = andSelectClauseList;\n\t}\n\n\treturn andSelectNode;\n}\n\n\n/*\n * OrSelectNode looks for OR clauses in the given select node. If they exist,\n * the function returns these clauses in a new node. Otherwise, the function\n * returns null.\n */\nstatic MultiSelect *\nOrSelectNode(MultiSelect *selectNode)\n{\n\tMultiSelect *orSelectNode = NULL;\n\tList *selectClauseList = selectNode->selectClauseList;\n\tList *orSelectClauseList = OrSelectClauseList(selectClauseList);\n\n\tif (orSelectClauseList != NIL)\n\t{\n\t\torSelectNode = CitusMakeNode(MultiSelect);\n\t\torSelectNode->selectClauseList = orSelectClauseList;\n\t}\n\n\treturn orSelectNode;\n}\n\n\n/*\n * OrSelectClauseList walks over the select clause list, and returns all clauses\n * that have OR expressions in them.\n */\nstatic List *\nOrSelectClauseList(List *selectClauseList)\n{\n\tList *orSelectClauseList = NIL;\n\n\tNode *selectClause = NULL;\n\tforeach_declared_ptr(selectClause, selectClauseList)\n\t{\n\t\tbool orClause = is_orclause(selectClause);\n\t\tif (orClause)\n\t\t{\n\t\t\torSelectClauseList = lappend(orSelectClauseList, selectClause);\n\t\t}\n\t}\n\n\treturn orSelectClauseList;\n}\n\n\n/*\n * PushDownNodeLoop pushes down the current node as far down the plan tree as\n * possible. For this, the function first addresses any special conditions that\n * may apply on the current node. Then, the function pushes down the current\n * node if its child node is unary. If the child is binary, the function splits\n * the current node into two nodes by applying generation rules, and recurses\n * into itself to push down these two nodes.\n */\nstatic void\nPushDownNodeLoop(MultiUnaryNode *currentNode)\n{\n\tMultiUnaryNode *projectNodeGenerated = NULL;\n\tMultiUnaryNode *leftNodeGenerated = NULL;\n\tMultiUnaryNode *rightNodeGenerated = NULL;\n\n\tPushDownStatus pushDownStatus = CanPushDown(currentNode);\n\twhile (pushDownStatus == PUSH_DOWN_VALID ||\n\t\t   pushDownStatus == PUSH_DOWN_SPECIAL_CONDITIONS)\n\t{\n\t\tMultiNode *childNode = currentNode->childNode;\n\t\tbool unaryChild = UnaryOperator(childNode);\n\t\tbool binaryChild = BinaryOperator(childNode);\n\n\t\t/*\n\t\t * We first check if we can use the idempower property to split the\n\t\t * project node. We split at a partition node as it captures the\n\t\t * minimal set of columns needed from a partition job. After the split\n\t\t * we break from the loop and recursively call pushdown for the\n\t\t * generated project node.\n\t\t */\n\t\tMultiNode *parentNode = ParentNode((MultiNode *) currentNode);\n\t\tCitusNodeTag currentNodeType = CitusNodeTag(currentNode);\n\t\tCitusNodeTag parentNodeType = CitusNodeTag(parentNode);\n\n\t\tif (currentNodeType == T_MultiProject && parentNodeType == T_MultiPartition)\n\t\t{\n\t\t\tprojectNodeGenerated = GenerateNode(currentNode, childNode);\n\t\t\tPlaceUnaryNodeChild(currentNode, projectNodeGenerated);\n\n\t\t\tbreak;\n\t\t}\n\n\t\t/* address any special conditions before we can perform the pushdown */\n\t\tif (pushDownStatus == PUSH_DOWN_SPECIAL_CONDITIONS)\n\t\t{\n\t\t\tMultiProject *projectNode = (MultiProject *) currentNode;\n\t\t\tAssert(currentNodeType == T_MultiProject);\n\n\t\t\tAddressProjectSpecialConditions(projectNode);\n\t\t}\n\n\t\tif (unaryChild)\n\t\t{\n\t\t\tMultiUnaryNode *unaryChildNode = (MultiUnaryNode *) childNode;\n\t\t\tPushDownBelowUnaryChild(currentNode, unaryChildNode);\n\t\t}\n\t\telse if (binaryChild)\n\t\t{\n\t\t\tMultiBinaryNode *binaryChildNode = (MultiBinaryNode *) childNode;\n\t\t\tleftNodeGenerated = GenerateLeftNode(currentNode, binaryChildNode);\n\t\t\trightNodeGenerated = GenerateRightNode(currentNode, binaryChildNode);\n\n\t\t\t/* push down the generated nodes below the binary child node */\n\t\t\tPlaceBinaryNodeLeftChild(binaryChildNode, leftNodeGenerated);\n\t\t\tPlaceBinaryNodeRightChild(binaryChildNode, rightNodeGenerated);\n\n\t\t\t/*\n\t\t\t * Remove the current node, and break out of the push down loop for\n\t\t\t * the current node. Then, recurse into the push down function for\n\t\t\t * the newly generated nodes.\n\t\t\t */\n\t\t\tRemoveUnaryNode(currentNode);\n\t\t\tbreak;\n\t\t}\n\n\t\tpushDownStatus = CanPushDown(currentNode);\n\t}\n\n\t/* recursively perform pushdown of any nodes generated in the loop */\n\tif (projectNodeGenerated != NULL)\n\t{\n\t\tPushDownNodeLoop(projectNodeGenerated);\n\t}\n\tif (leftNodeGenerated != NULL)\n\t{\n\t\tPushDownNodeLoop(leftNodeGenerated);\n\t}\n\tif (rightNodeGenerated != NULL)\n\t{\n\t\tPushDownNodeLoop(rightNodeGenerated);\n\t}\n}\n\n\n/*\n * PullUpCollectLoop pulls up the collect node as far up as possible in the plan\n * tree. The function also merges two collect nodes that are direct descendants\n * of each other by removing the given collect node from the tree.\n */\nstatic void\nPullUpCollectLoop(MultiCollect *collectNode)\n{\n\tMultiUnaryNode *currentNode = (MultiUnaryNode *) collectNode;\n\n\tPullUpStatus pullUpStatus = CanPullUp(currentNode);\n\twhile (pullUpStatus == PULL_UP_VALID)\n\t{\n\t\tPullUpUnaryNode(currentNode);\n\t\tpullUpStatus = CanPullUp(currentNode);\n\t}\n\n\t/*\n\t * After pulling up the collect node, if we find that our child node is also\n\t * a collect, we merge the two collect nodes together by removing this node.\n\t */\n\tMultiNode *childNode = currentNode->childNode;\n\tif (CitusIsA(childNode, MultiCollect))\n\t{\n\t\tRemoveUnaryNode(currentNode);\n\t}\n}\n\n\n/*\n * AddressProjectSpecialConditions adds columns to the project node if necessary\n * to make the node commutative and distributive with its child node. For this,\n * the function checks for any special conditions between the project and child\n * node, and determines the child node columns to add for the special conditions\n * to apply. The function then adds these columns to the project node.\n */\nstatic void\nAddressProjectSpecialConditions(MultiProject *projectNode)\n{\n\tMultiNode *childNode = ChildNode((MultiUnaryNode *) projectNode);\n\tCitusNodeTag childNodeTag = CitusNodeTag(childNode);\n\tList *childColumnList = NIL;\n\n\t/*\n\t * We check if we need to include any child columns in the project node to\n\t * address the following special conditions.\n\t *\n\t * SNC1: project node must include child node's projected columns, or\n\t * SNC2: project node must include child node's partition column,  or\n\t * SNC3: project node must include child node's selection columns, or\n\t * NSC1: project node must include child node's join columns.\n\t */\n\tif (childNodeTag == T_MultiProject)\n\t{\n\t\tMultiProject *projectChildNode = (MultiProject *) childNode;\n\t\tList *projectColumnList = projectChildNode->columnList;\n\n\t\tchildColumnList = copyObject(projectColumnList);\n\t}\n\telse if (childNodeTag == T_MultiPartition)\n\t{\n\t\tMultiPartition *partitionNode = (MultiPartition *) childNode;\n\t\tVar *partitionColumn = partitionNode->partitionColumn;\n\t\tList *partitionColumnList = list_make1(partitionColumn);\n\n\t\tchildColumnList = copyObject(partitionColumnList);\n\t}\n\telse if (childNodeTag == T_MultiSelect)\n\t{\n\t\tMultiSelect *selectNode = (MultiSelect *) childNode;\n\t\tNode *selectClauseList = (Node *) selectNode->selectClauseList;\n\t\tList *selectList = pull_var_clause_default(selectClauseList);\n\n\t\tchildColumnList = copyObject(selectList);\n\t}\n\telse if (childNodeTag == T_MultiJoin)\n\t{\n\t\tMultiJoin *joinNode = (MultiJoin *) childNode;\n\t\tNode *joinClauseList = (Node *) joinNode->joinClauseList;\n\t\tList *joinList = pull_var_clause_default(joinClauseList);\n\n\t\tchildColumnList = copyObject(joinList);\n\t}\n\n\t/*\n\t * If we need to include any child columns, then find the columns that are\n\t * not already in the project column list, and add them.\n\t */\n\tif (childColumnList != NIL)\n\t{\n\t\tList *projectColumnList = projectNode->columnList;\n\t\tList *newColumnList = list_concat_unique(projectColumnList, childColumnList);\n\n\t\tprojectNode->columnList = newColumnList;\n\t}\n}\n\n\n/*\n * CanPushDown determines if a particular node can be moved below its child. The\n * criteria for pushing down a node is determined by multi-relational algebra's\n * rules for commutativity and distributivity.\n */\nstatic PushDownStatus\nCanPushDown(MultiUnaryNode *parentNode)\n{\n\tPushDownStatus pushDownStatus = PUSH_DOWN_INVALID_FIRST;\n\tMultiNode *childNode = parentNode->childNode;\n\tbool unaryChild = UnaryOperator(childNode);\n\tbool binaryChild = BinaryOperator(childNode);\n\n\tif (unaryChild)\n\t{\n\t\tpushDownStatus = Commutative(parentNode, (MultiUnaryNode *) childNode);\n\t}\n\telse if (binaryChild)\n\t{\n\t\tpushDownStatus = Distributive(parentNode, (MultiBinaryNode *) childNode);\n\t}\n\n\tAssert(pushDownStatus != PUSH_DOWN_INVALID_FIRST);\n\treturn pushDownStatus;\n}\n\n\n/*\n * CanPullUp determines if a particular node can be moved above its parent. The\n * criteria for pulling up a node is determined by multi-relational algebra's\n * rules for commutativity and factorizability.\n */\nstatic PullUpStatus\nCanPullUp(MultiUnaryNode *childNode)\n{\n\tPullUpStatus pullUpStatus = PULL_UP_INVALID_FIRST;\n\tMultiNode *parentNode = ParentNode((MultiNode *) childNode);\n\tbool unaryParent = UnaryOperator(parentNode);\n\tbool binaryParent = BinaryOperator(parentNode);\n\n\tif (unaryParent)\n\t{\n\t\t/*\n\t\t * Evaluate if parent can be pushed down below the child node, since it\n\t\t * is equivalent to pulling up the child above its parent.\n\t\t */\n\t\tPushDownStatus parentPushDownStatus = Commutative((MultiUnaryNode *) parentNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  childNode);\n\n\t\tif (parentPushDownStatus == PUSH_DOWN_VALID)\n\t\t{\n\t\t\tpullUpStatus = PULL_UP_VALID;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tpullUpStatus = PULL_UP_NOT_VALID;\n\t\t}\n\t}\n\telse if (binaryParent)\n\t{\n\t\tpullUpStatus = Factorizable((MultiBinaryNode *) parentNode, childNode);\n\t}\n\n\tAssert(pullUpStatus != PULL_UP_INVALID_FIRST);\n\treturn pullUpStatus;\n}\n\n\n/*\n * Commutative returns a status which denotes whether the given parent node can\n * be pushed down below its child node using the commutative property.\n */\nstatic PushDownStatus\nCommutative(MultiUnaryNode *parentNode, MultiUnaryNode *childNode)\n{\n\tPushDownStatus pushDownStatus = PUSH_DOWN_NOT_VALID;\n\tCitusNodeTag parentNodeTag = CitusNodeTag(parentNode);\n\tCitusNodeTag childNodeTag = CitusNodeTag(childNode);\n\n\t/* we cannot be commutative with non-query operators */\n\tif (childNodeTag == T_MultiTreeRoot || childNodeTag == T_MultiTable)\n\t{\n\t\treturn PUSH_DOWN_NOT_VALID;\n\t}\n\n\t/* first check for commutative operators and no special conditions */\n\tif ((parentNodeTag == T_MultiPartition && childNodeTag == T_MultiProject) ||\n\t\t(parentNodeTag == T_MultiPartition && childNodeTag == T_MultiPartition) ||\n\t\t(parentNodeTag == T_MultiPartition && childNodeTag == T_MultiSelect))\n\t{\n\t\tpushDownStatus = PUSH_DOWN_VALID;\n\t}\n\tif ((parentNodeTag == T_MultiCollect && childNodeTag == T_MultiProject) ||\n\t\t(parentNodeTag == T_MultiCollect && childNodeTag == T_MultiCollect) ||\n\t\t(parentNodeTag == T_MultiCollect && childNodeTag == T_MultiSelect))\n\t{\n\t\tpushDownStatus = PUSH_DOWN_VALID;\n\t}\n\tif (parentNodeTag == T_MultiSelect)\n\t{\n\t\tpushDownStatus = PUSH_DOWN_VALID;\n\t}\n\tif (parentNodeTag == T_MultiProject && childNodeTag == T_MultiCollect)\n\t{\n\t\tpushDownStatus = PUSH_DOWN_VALID;\n\t}\n\n\t/*\n\t * The project node is commutative with the below operators given that\n\t * its special conditions apply.\n\t */\n\tif ((parentNodeTag == T_MultiProject && childNodeTag == T_MultiProject) ||\n\t\t(parentNodeTag == T_MultiProject && childNodeTag == T_MultiPartition) ||\n\t\t(parentNodeTag == T_MultiProject && childNodeTag == T_MultiSelect) ||\n\t\t(parentNodeTag == T_MultiProject && childNodeTag == T_MultiJoin))\n\t{\n\t\tpushDownStatus = PUSH_DOWN_SPECIAL_CONDITIONS;\n\t}\n\n\treturn pushDownStatus;\n}\n\n\n/*\n * Distributive returns a status which denotes whether the given parent node can\n * be pushed down below its binary child node using the distributive property.\n */\nstatic PushDownStatus\nDistributive(MultiUnaryNode *parentNode, MultiBinaryNode *childNode)\n{\n\tPushDownStatus pushDownStatus = PUSH_DOWN_NOT_VALID;\n\tCitusNodeTag parentNodeTag = CitusNodeTag(parentNode);\n\tCitusNodeTag childNodeTag = CitusNodeTag(childNode);\n\n\t/* special condition checks for partition operator are not implemented */\n\tAssert(parentNodeTag != T_MultiPartition);\n\n\t/*\n\t * The project node is distributive with the join operator given that its\n\t * special conditions apply.\n\t */\n\tif (parentNodeTag == T_MultiProject)\n\t{\n\t\tpushDownStatus = PUSH_DOWN_SPECIAL_CONDITIONS;\n\t}\n\n\t/* collect node is distributive without special conditions */\n\tif ((parentNodeTag == T_MultiCollect && childNodeTag == T_MultiJoin) ||\n\t\t(parentNodeTag == T_MultiCollect && childNodeTag == T_MultiCartesianProduct))\n\t{\n\t\tpushDownStatus = PUSH_DOWN_VALID;\n\t}\n\n\t/*\n\t * The select node is distributive with a binary operator if all tables in\n\t * the select clauses are output by the binary child. The select clauses are\n\t * individually AND'd; and therefore this check is sufficient to implement\n\t * the NSC3 special condition in multi-relational algebra.\n\t */\n\tif ((parentNodeTag == T_MultiSelect && childNodeTag == T_MultiJoin) ||\n\t\t(parentNodeTag == T_MultiSelect && childNodeTag == T_MultiCartesianProduct))\n\t{\n\t\tMultiSelect *selectNode = (MultiSelect *) parentNode;\n\t\tList *selectClauseList = selectNode->selectClauseList;\n\n\t\tList *selectTableIdList = SelectClauseTableIdList(selectClauseList);\n\t\tList *childTableIdList = OutputTableIdList((MultiNode *) childNode);\n\n\t\t/* find tables that are in select clause list, but not in child list */\n\t\tList *diffList = list_difference_int(selectTableIdList, childTableIdList);\n\t\tif (diffList == NIL)\n\t\t{\n\t\t\tpushDownStatus = PUSH_DOWN_VALID;\n\t\t}\n\t}\n\n\treturn pushDownStatus;\n}\n\n\n/*\n * Factorizable returns a status which denotes whether the given unary child\n * node can be pulled up above its binary parent node using the factorizability\n * property. The function currently performs this check only for collect node\n * types; other node types have generation rules that are not yet implemented.\n */\nstatic PullUpStatus\nFactorizable(MultiBinaryNode *parentNode, MultiUnaryNode *childNode)\n{\n\tPullUpStatus pullUpStatus = PULL_UP_NOT_VALID;\n\tCitusNodeTag parentNodeTag = CitusNodeTag(parentNode);\n\tCitusNodeTag childNodeTag = CitusNodeTag(childNode);\n\n\t/*\n\t * The following nodes are factorizable with their parents, but we don't\n\t * have their generation rules implemented. We therefore assert here.\n\t */\n\tAssert(childNodeTag != T_MultiProject);\n\tAssert(childNodeTag != T_MultiPartition);\n\tAssert(childNodeTag != T_MultiSelect);\n\n\tif ((childNodeTag == T_MultiCollect && parentNodeTag == T_MultiJoin) ||\n\t\t(childNodeTag == T_MultiCollect && parentNodeTag == T_MultiCartesianProduct))\n\t{\n\t\tpullUpStatus = PULL_UP_VALID;\n\t}\n\n\treturn pullUpStatus;\n}\n\n\n/*\n * SelectClauseTableIdList finds the (range) table identifier for each select\n * clause in the given list, and returns these identifiers in a new list.\n */\nstatic List *\nSelectClauseTableIdList(List *selectClauseList)\n{\n\tList *tableIdList = NIL;\n\n\tNode *selectClause = NULL;\n\tforeach_declared_ptr(selectClause, selectClauseList)\n\t{\n\t\tList *selectColumnList = pull_var_clause_default(selectClause);\n\n\t\tif (list_length(selectColumnList) == 0)\n\t\t{\n\t\t\t/* filter is a constant, e.g. false or 1=0 */\n\t\t\tcontinue;\n\t\t}\n\n\t\tVar *selectColumn = (Var *) linitial(selectColumnList);\n\t\tint selectColumnTableId = (int) selectColumn->varno;\n\n\t\ttableIdList = lappend_int(tableIdList, selectColumnTableId);\n\t}\n\n\treturn tableIdList;\n}\n\n\n/*\n * GenerateLeftNode splits the current node over the binary node by applying the\n * generation rule for distributivity in multi-relational algebra. After the\n * split, the function returns the left node.\n */\nstatic MultiUnaryNode *\nGenerateLeftNode(MultiUnaryNode *currentNode, MultiBinaryNode *binaryNode)\n{\n\tMultiNode *leftChildNode = binaryNode->leftChildNode;\n\tMultiUnaryNode *leftNodeGenerated = GenerateNode(currentNode, leftChildNode);\n\n\treturn leftNodeGenerated;\n}\n\n\n/*\n * GenerateRightNode splits the current node over the binary node by applying\n * the generation rule for distributivity in multi-relational algebra. After the\n * split, the function returns the right node.\n */\nstatic MultiUnaryNode *\nGenerateRightNode(MultiUnaryNode *currentNode, MultiBinaryNode *binaryNode)\n{\n\tMultiNode *rightChildNode = binaryNode->rightChildNode;\n\tMultiUnaryNode *rightNodeGenerated = GenerateNode(currentNode, rightChildNode);\n\n\treturn rightNodeGenerated;\n}\n\n\n/*\n * GenerateNode determines the current node's type, and applies the relevant\n * generation node for that node type. If the current node is a project node,\n * the function creates a new project node with attributes that only have the\n * child subtree's tables. Else if the current node is a select node, the\n * function creates a new select node with select clauses that only belong to\n * the tables output by the child node's subtree.\n */\nstatic MultiUnaryNode *\nGenerateNode(MultiUnaryNode *currentNode, MultiNode *childNode)\n{\n\tMultiUnaryNode *generatedNode = NULL;\n\tCitusNodeTag currentNodeType = CitusNodeTag(currentNode);\n\tList *tableIdList = OutputTableIdList(childNode);\n\n\tif (currentNodeType == T_MultiProject)\n\t{\n\t\tMultiProject *projectNode = (MultiProject *) currentNode;\n\t\tList *columnList = copyObject(projectNode->columnList);\n\n\t\tList *newColumnList = TableIdListColumns(tableIdList, columnList);\n\t\tif (newColumnList != NIL)\n\t\t{\n\t\t\tMultiProject *newProjectNode = CitusMakeNode(MultiProject);\n\t\t\tnewProjectNode->columnList = newColumnList;\n\n\t\t\tgeneratedNode = (MultiUnaryNode *) newProjectNode;\n\t\t}\n\t}\n\telse if (currentNodeType == T_MultiSelect)\n\t{\n\t\tMultiSelect *selectNode = (MultiSelect *) currentNode;\n\t\tList *selectClauseList = copyObject(selectNode->selectClauseList);\n\n\t\tList *newSelectClauseList = TableIdListSelectClauses(tableIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t selectClauseList);\n\t\tif (newSelectClauseList != NIL)\n\t\t{\n\t\t\tMultiSelect *newSelectNode = CitusMakeNode(MultiSelect);\n\t\t\tnewSelectNode->selectClauseList = newSelectClauseList;\n\n\t\t\tgeneratedNode = (MultiUnaryNode *) newSelectNode;\n\t\t}\n\t}\n\n\treturn generatedNode;\n}\n\n\n/*\n * TableIdListColumns walks over the given column list, finds columns belonging\n * to the given table id list, and returns the found columns in a new list.\n */\nstatic List *\nTableIdListColumns(List *tableIdList, List *columnList)\n{\n\tList *tableColumnList = NIL;\n\n\tVar *column = NULL;\n\tforeach_declared_ptr(column, columnList)\n\t{\n\t\tint columnTableId = (int) column->varno;\n\n\t\tbool tableListMember = list_member_int(tableIdList, columnTableId);\n\t\tif (tableListMember)\n\t\t{\n\t\t\ttableColumnList = lappend(tableColumnList, column);\n\t\t}\n\t}\n\n\treturn tableColumnList;\n}\n\n\n/*\n * TableIdListSelectClauses walks over the given select clause list, finds the\n * select clauses whose column references belong to the given table list, and\n * returns the found clauses in a new list.\n */\nstatic List *\nTableIdListSelectClauses(List *tableIdList, List *selectClauseList)\n{\n\tList *tableSelectClauseList = NIL;\n\n\tNode *selectClause = NULL;\n\tforeach_declared_ptr(selectClause, selectClauseList)\n\t{\n\t\tList *selectColumnList = pull_var_clause_default(selectClause);\n\t\tif (list_length(selectColumnList) == 0)\n\t\t{\n\t\t\t/* filter is a constant, e.g. false or 1=0, always include it */\n\t\t\ttableSelectClauseList = lappend(tableSelectClauseList, selectClause);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tVar *selectColumn = (Var *) linitial(selectColumnList);\n\t\t\tint selectClauseTableId = (int) selectColumn->varno;\n\n\t\t\tbool tableIdListMember = list_member_int(tableIdList, selectClauseTableId);\n\t\t\tif (tableIdListMember)\n\t\t\t{\n\t\t\t\ttableSelectClauseList = lappend(tableSelectClauseList, selectClause);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn tableSelectClauseList;\n}\n\n\n/* Pushes down the current node below its unary child node. */\nstatic void\nPushDownBelowUnaryChild(MultiUnaryNode *currentNode, MultiUnaryNode *childNode)\n{\n\tMultiNode *parentNode = ParentNode((MultiNode *) currentNode);\n\tMultiNode *childChildNode = ChildNode(childNode);\n\n\t/* current node's parent now points to the child node */\n\tParentSetNewChild(parentNode, (MultiNode *) currentNode, (MultiNode *) childNode);\n\n\t/* current node's child becomes its parent */\n\tSetChild(childNode, (MultiNode *) currentNode);\n\n\t/* current node points to the child node's child */\n\tSetChild(currentNode, childChildNode);\n}\n\n\n/*\n * PlaceUnaryNodeChild inserts the new node as a child node under the given\n * unary node. The function also places the previous child node under the new\n * child node.\n */\nstatic void\nPlaceUnaryNodeChild(MultiUnaryNode *unaryNode, MultiUnaryNode *newChildNode)\n{\n\tMultiNode *oldChildNode = ChildNode(unaryNode);\n\n\tSetChild(unaryNode, (MultiNode *) newChildNode);\n\tSetChild(newChildNode, oldChildNode);\n}\n\n\n/*\n * PlaceBinaryNodeLeftChild inserts the new left child as the binary node's left\n * child. The function also places the previous left child below the new child\n * node.\n */\nstatic void\nPlaceBinaryNodeLeftChild(MultiBinaryNode *binaryNode, MultiUnaryNode *newLeftChildNode)\n{\n\tif (newLeftChildNode == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tSetChild(newLeftChildNode, binaryNode->leftChildNode);\n\tSetLeftChild(binaryNode, (MultiNode *) newLeftChildNode);\n}\n\n\n/*\n * PlaceBinaryNodeRightChild inserts the new right child as the binary node's\n * right child. The function also places the previous right child below the new\n * child node.\n */\nstatic void\nPlaceBinaryNodeRightChild(MultiBinaryNode *binaryNode, MultiUnaryNode *newRightChildNode)\n{\n\tif (newRightChildNode == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tSetChild(newRightChildNode, binaryNode->rightChildNode);\n\tSetRightChild(binaryNode, (MultiNode *) newRightChildNode);\n}\n\n\n/* Removes the given unary node from the logical plan, and frees the node. */\nstatic void\nRemoveUnaryNode(MultiUnaryNode *unaryNode)\n{\n\tMultiNode *parentNode = ParentNode((MultiNode *) unaryNode);\n\tMultiNode *childNode = ChildNode(unaryNode);\n\n\t/* set parent to directly point to unary node's child */\n\tParentSetNewChild(parentNode, (MultiNode *) unaryNode, childNode);\n\n\tpfree(unaryNode);\n}\n\n\n/* Pulls up the given current node above its parent node. */\nstatic void\nPullUpUnaryNode(MultiUnaryNode *unaryNode)\n{\n\tMultiNode *parentNode = ParentNode((MultiNode *) unaryNode);\n\tbool unaryParent = UnaryOperator(parentNode);\n\tbool binaryParent = BinaryOperator(parentNode);\n\n\tif (unaryParent)\n\t{\n\t\t/* pulling up a node is the same as pushing down the node's unary parent */\n\t\tMultiUnaryNode *unaryParentNode = (MultiUnaryNode *) parentNode;\n\t\tPushDownBelowUnaryChild(unaryParentNode, unaryNode);\n\t}\n\telse if (binaryParent)\n\t{\n\t\tMultiBinaryNode *binaryParentNode = (MultiBinaryNode *) parentNode;\n\t\tMultiNode *parentParentNode = ParentNode((MultiNode *) binaryParentNode);\n\t\tMultiNode *childNode = unaryNode->childNode;\n\n\t\t/* make the parent node point to the unary node's child node */\n\t\tif (binaryParentNode->leftChildNode == ((MultiNode *) unaryNode))\n\t\t{\n\t\t\tSetLeftChild(binaryParentNode, childNode);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSetRightChild(binaryParentNode, childNode);\n\t\t}\n\n\t\t/* make the parent parent node point to the unary node */\n\t\tParentSetNewChild(parentParentNode, parentNode, (MultiNode *) unaryNode);\n\n\t\t/* make the unary node point to the (old) parent node */\n\t\tSetChild(unaryNode, parentNode);\n\t}\n}\n\n\n/*\n * ParentSetNewChild takes in the given parent node, and replaces the parent's\n * old child node with the new child node. The function needs the old child node\n * in case the parent is a binary node and the function needs to determine which\n * side of the parent node the new child node needs to go to.\n */\nstatic void\nParentSetNewChild(MultiNode *parentNode, MultiNode *oldChildNode,\n\t\t\t\t  MultiNode *newChildNode)\n{\n\tbool unaryParent = UnaryOperator(parentNode);\n\tbool binaryParent = BinaryOperator(parentNode);\n\n\tif (unaryParent)\n\t{\n\t\tMultiUnaryNode *unaryParentNode = (MultiUnaryNode *) parentNode;\n\t\tSetChild(unaryParentNode, newChildNode);\n\t}\n\telse if (binaryParent)\n\t{\n\t\tMultiBinaryNode *binaryParentNode = (MultiBinaryNode *) parentNode;\n\n\t\t/* determine which side of the parent the old child is on */\n\t\tif (binaryParentNode->leftChildNode == oldChildNode)\n\t\t{\n\t\t\tSetLeftChild(binaryParentNode, newChildNode);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tSetRightChild(binaryParentNode, newChildNode);\n\t\t}\n\t}\n}\n\n\n/*\n * ApplyExtendedOpNodes replaces the original extended operator node with the\n * master and worker extended operator nodes. The function then pushes down the\n * worker node below the original node's child node. Note that for the push down\n * to apply, the original node's child must be a collect node.\n */\nstatic void\nApplyExtendedOpNodes(MultiExtendedOp *originalNode, MultiExtendedOp *masterNode,\n\t\t\t\t\t MultiExtendedOp *workerNode)\n{\n\tMultiNode *parentNode = ParentNode((MultiNode *) originalNode);\n\tMultiNode *collectNode = ChildNode((MultiUnaryNode *) originalNode);\n\tMultiNode *collectChildNode = ChildNode((MultiUnaryNode *) collectNode);\n\n\t/* original node's child must be a collect node */\n\tAssert(CitusIsA(collectNode, MultiCollect));\n\tAssert(UnaryOperator(parentNode));\n\n\t/* swap the original aggregate node with the master extended node */\n\tSetChild((MultiUnaryNode *) parentNode, (MultiNode *) masterNode);\n\tSetChild((MultiUnaryNode *) masterNode, (MultiNode *) collectNode);\n\n\t/* add the worker extended node below the collect node */\n\tSetChild((MultiUnaryNode *) collectNode, (MultiNode *) workerNode);\n\tSetChild((MultiUnaryNode *) workerNode, (MultiNode *) collectChildNode);\n\n\t/* clean up the original extended operator node */\n\tpfree(originalNode);\n}\n\n\n/*\n * TransformSubqueryNode splits the extended operator node under subquery\n * multi table node into its equivalent master and worker operator nodes, and\n * we transform aggregate functions accordingly for the master and worker\n * operator nodes. We create a partition node based on the first group by\n * column of the extended operator node and set it as the child of the master\n * operator node.\n */\nstatic void\nTransformSubqueryNode(MultiTable *subqueryNode,\n\t\t\t\t\t  bool subqueryHasNonDistributableAggregates)\n{\n\tif (CoordinatorAggregationStrategy != COORDINATOR_AGGREGATION_DISABLED &&\n\t\tHasNonDistributableAggregates((MultiNode *) subqueryNode))\n\t{\n\t\tsubqueryHasNonDistributableAggregates = true;\n\t}\n\n\tMultiExtendedOp *extendedOpNode =\n\t\t(MultiExtendedOp *) ChildNode((MultiUnaryNode *) subqueryNode);\n\tMultiNode *collectNode = ChildNode((MultiUnaryNode *) extendedOpNode);\n\tMultiNode *collectChildNode = ChildNode((MultiUnaryNode *) collectNode);\n\n\tExtendedOpNodeProperties extendedOpNodeProperties =\n\t\tBuildExtendedOpNodeProperties(extendedOpNode,\n\t\t\t\t\t\t\t\t\t  subqueryHasNonDistributableAggregates);\n\n\tMultiExtendedOp *masterExtendedOpNode =\n\t\tMasterExtendedOpNode(extendedOpNode, &extendedOpNodeProperties);\n\tMultiExtendedOp *workerExtendedOpNode =\n\t\tWorkerExtendedOpNode(extendedOpNode, &extendedOpNodeProperties);\n\n\tList *groupClauseList = extendedOpNode->groupClauseList;\n\tList *targetEntryList = extendedOpNode->targetList;\n\tList *groupTargetEntryList = GroupTargetEntryList(groupClauseList, targetEntryList);\n\tTargetEntry *groupByTargetEntry = (TargetEntry *) linitial(groupTargetEntryList);\n\tExpr *groupByExpression = groupByTargetEntry->expr;\n\n\tMultiPartition *partitionNode = CitusMakeNode(MultiPartition);\n\n\t/*\n\t * If group by is on a function expression, then we create a new column from\n\t * function expression result type. Because later while creating partition\n\t * tasks, we expect a column type to partition intermediate results.\n\t * Note that we will only need partition type. So we set column type to\n\t * result type of the function expression, and set other fields of column to\n\t * default values.\n\t */\n\tif (IsA(groupByExpression, Var))\n\t{\n\t\tpartitionNode->partitionColumn = (Var *) groupByExpression;\n\t}\n\telse if (IsA(groupByExpression, FuncExpr))\n\t{\n\t\tFuncExpr *functionExpression = (FuncExpr *) groupByExpression;\n\t\tIndex tableId = 0;\n\t\tAttrNumber columnAttributeNumber = InvalidAttrNumber;\n\t\tOid columnType = functionExpression->funcresulttype;\n\t\tint32 columnTypeMod = -1;\n\t\tOid columnCollationOid = InvalidOid;\n\t\tIndex columnLevelSup = 0;\n\n\t\tVar *partitionColumn = makeVar(tableId, columnAttributeNumber, columnType,\n\t\t\t\t\t\t\t\t\t   columnTypeMod, columnCollationOid, columnLevelSup);\n\t\tpartitionNode->partitionColumn = partitionColumn;\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot run this subquery\"),\n\t\t\t\t\t\terrdetail(\"Currently only columns and function expressions \"\n\t\t\t\t\t\t\t\t  \"are allowed in group by expression of subqueries\")));\n\t}\n\n\tSetChild((MultiUnaryNode *) subqueryNode, (MultiNode *) masterExtendedOpNode);\n\tSetChild((MultiUnaryNode *) masterExtendedOpNode, (MultiNode *) partitionNode);\n\tSetChild((MultiUnaryNode *) partitionNode, (MultiNode *) collectNode);\n\tSetChild((MultiUnaryNode *) collectNode, (MultiNode *) workerExtendedOpNode);\n\tSetChild((MultiUnaryNode *) workerExtendedOpNode, (MultiNode *) collectChildNode);\n}\n\n\n/*\n * MasterExtendedOpNode creates the master extended operator node from the given\n * target entries. The function walks over these target entries; and for entries\n * with aggregates in them, this function calls the aggregate expression mutator\n * function.\n *\n * Note that the function logically depends on the worker extended operator node\n * function. If the target entry does not contain aggregate functions, we assume\n * all work is done on the worker side, and create a column that references the\n * worker nodes' results.\n */\nstatic MultiExtendedOp *\nMasterExtendedOpNode(MultiExtendedOp *originalOpNode,\n\t\t\t\t\t ExtendedOpNodeProperties *extendedOpNodeProperties)\n{\n\tList *targetEntryList = originalOpNode->targetList;\n\tList *newTargetEntryList = NIL;\n\tList *newGroupClauseList = NIL;\n\tNode *originalHavingQual = originalOpNode->havingQual;\n\tNode *newHavingQual = NULL;\n\tMasterAggregateWalkerContext walkerContext = {\n\t\t.extendedOpNodeProperties = extendedOpNodeProperties,\n\t\t.columnId = 1,\n\t};\n\n\t/* iterate over original target entries */\n\tTargetEntry *originalTargetEntry = NULL;\n\tforeach_declared_ptr(originalTargetEntry, targetEntryList)\n\t{\n\t\tTargetEntry *newTargetEntry = flatCopyTargetEntry(originalTargetEntry);\n\t\tExpr *originalExpression = originalTargetEntry->expr;\n\t\tExpr *newExpression = NULL;\n\n\t\tif (CanPushDownExpression((Node *) originalExpression, extendedOpNodeProperties))\n\t\t{\n\t\t\t/*\n\t\t\t * The expression was entirely pushed down to worker.\n\t\t\t * We simply make it reference the output generated by worker nodes.\n\t\t\t */\n\t\t\tVar *column = makeVarFromTargetEntry(masterTableId, originalTargetEntry);\n\t\t\tcolumn->varattno = walkerContext.columnId;\n\t\t\tcolumn->varattnosyn = walkerContext.columnId;\n\t\t\twalkerContext.columnId++;\n\n\t\t\tif (column->vartype == RECORDOID || column->vartype == RECORDARRAYOID)\n\t\t\t{\n\t\t\t\tcolumn->vartypmod = BlessRecordExpression(originalTargetEntry->expr);\n\t\t\t}\n\n\t\t\tnewExpression = (Expr *) column;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tNode *newNode = MasterAggregateMutator((Node *) originalExpression,\n\t\t\t\t\t\t\t\t\t\t\t\t   &walkerContext);\n\t\t\tnewExpression = (Expr *) newNode;\n\t\t}\n\n\t\tnewTargetEntry->expr = newExpression;\n\t\tnewTargetEntryList = lappend(newTargetEntryList, newTargetEntry);\n\t}\n\n\tif (!extendedOpNodeProperties->pushDownGroupingAndHaving)\n\t{\n\t\t/*\n\t\t * Not pushing down GROUP BY, need to regroup on coordinator\n\t\t * and apply having on the coordinator.\n\t\t */\n\t\tnewGroupClauseList = originalOpNode->groupClauseList;\n\n\t\tif (originalHavingQual != NULL)\n\t\t{\n\t\t\tnewHavingQual = MasterAggregateMutator(originalHavingQual, &walkerContext);\n\t\t\tif (IsA(newHavingQual, List))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * unflatten having qual to allow standard planner to work when transforming\n\t\t\t\t * the master query to a plan\n\t\t\t\t */\n\t\t\t\tnewHavingQual = (Node *) make_ands_explicit(\n\t\t\t\t\tcastNode(List, newHavingQual));\n\t\t\t}\n\t\t}\n\t}\n\n\tMultiExtendedOp *masterExtendedOpNode = CitusMakeNode(MultiExtendedOp);\n\tmasterExtendedOpNode->targetList = newTargetEntryList;\n\tmasterExtendedOpNode->groupClauseList = newGroupClauseList;\n\tmasterExtendedOpNode->sortClauseList = originalOpNode->sortClauseList;\n\tmasterExtendedOpNode->distinctClause = originalOpNode->distinctClause;\n\tmasterExtendedOpNode->hasDistinctOn = originalOpNode->hasDistinctOn;\n\tmasterExtendedOpNode->limitCount = originalOpNode->limitCount;\n\tmasterExtendedOpNode->limitOffset = originalOpNode->limitOffset;\n\tmasterExtendedOpNode->limitOption = originalOpNode->limitOption;\n\tmasterExtendedOpNode->havingQual = newHavingQual;\n\n\tif (!extendedOpNodeProperties->onlyPushableWindowFunctions)\n\t{\n\t\tmasterExtendedOpNode->hasWindowFuncs = originalOpNode->hasWindowFuncs;\n\t\tmasterExtendedOpNode->windowClause = originalOpNode->windowClause;\n\t\tmasterExtendedOpNode->onlyPushableWindowFunctions = false;\n\t}\n\n\treturn masterExtendedOpNode;\n}\n\n\n/*\n * MasterAggregateMutator walks over the original target entry expression, and\n * creates the new expression tree to execute on the master node. The function\n * transforms aggregates, and copies columns; and recurses into the expression\n * mutator function for all other expression types.\n *\n * Please note that the recursive mutator function traverses the expression tree\n * in depth first order. For this function to set attribute numbers correctly,\n * WorkerAggregateWalker() *must* walk over the expression tree in the same\n * depth first order.\n */\nstatic Node *\nMasterAggregateMutator(Node *originalNode, MasterAggregateWalkerContext *walkerContext)\n{\n\tNode *newNode = NULL;\n\tif (originalNode == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (IsA(originalNode, Aggref))\n\t{\n\t\tAggref *originalAggregate = (Aggref *) originalNode;\n\t\tif (CanPushDownExpression(originalNode,\n\t\t\t\t\t\t\t\t  walkerContext->extendedOpNodeProperties))\n\t\t{\n\t\t\t/*\n\t\t\t * The expression was entirely pushed down to worker.\n\t\t\t * We simply make it reference the output generated by worker nodes.\n\t\t\t */\n\t\t\tVar *column = makeVar(masterTableId, walkerContext->columnId,\n\t\t\t\t\t\t\t\t  originalAggregate->aggtype,\n\t\t\t\t\t\t\t\t  -1, originalAggregate->aggcollid, 0);\n\t\t\twalkerContext->columnId++;\n\n\t\t\tif (column->vartype == RECORDOID || column->vartype == RECORDARRAYOID)\n\t\t\t{\n\t\t\t\tcolumn->vartypmod = BlessRecordExpression((Expr *) originalNode);\n\t\t\t}\n\n\t\t\tnewNode = (Node *) column;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tExpr *newExpression = MasterAggregateExpression(originalAggregate,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twalkerContext);\n\n\t\t\tnewNode = (Node *) newExpression;\n\t\t}\n\t}\n\telse if (IsA(originalNode, Var))\n\t{\n\t\tVar *origColumn = (Var *) originalNode;\n\t\tVar *newColumn = makeVar(masterTableId, walkerContext->columnId,\n\t\t\t\t\t\t\t\t origColumn->vartype, origColumn->vartypmod,\n\t\t\t\t\t\t\t\t origColumn->varcollid, origColumn->varlevelsup);\n\t\twalkerContext->columnId++;\n\n\t\tnewNode = (Node *) newColumn;\n\t}\n\telse\n\t{\n\t\tnewNode = expression_tree_mutator(originalNode, MasterAggregateMutator,\n\t\t\t\t\t\t\t\t\t\t  (void *) walkerContext);\n\t}\n\n\treturn newNode;\n}\n\n\n/*\n * MasterAggregateExpression creates the master aggregate expression using the\n * original aggregate and aggregate's type information. This function handles\n * the average, count, array_agg, hll and topn aggregates separately due to\n * differences in these aggregate functions' transformations.\n *\n * Note that this function has implicit knowledge of the transformations applied\n * for worker nodes on the original aggregate. The function uses this implicit\n * knowledge to create the appropriate master function with correct data types.\n */\nstatic Expr *\nMasterAggregateExpression(Aggref *originalAggregate,\n\t\t\t\t\t\t  MasterAggregateWalkerContext *walkerContext)\n{\n\tconst Index columnLevelsUp = 0;  /* normal column */\n\tconst AttrNumber argumentId = 1; /* our aggregates have single arguments */\n\tAggregateType aggregateType = GetAggregateType(originalAggregate);\n\tExpr *newMasterExpression = NULL;\n\n\tif (walkerContext->extendedOpNodeProperties->pullUpIntermediateRows)\n\t{\n\t\tAggref *aggregate = (Aggref *) copyObject(originalAggregate);\n\n\t\tTargetEntry *targetEntry;\n\t\tforeach_declared_ptr(targetEntry, aggregate->args)\n\t\t{\n\t\t\ttargetEntry->expr = (Expr *)\n\t\t\t\t\t\t\t\tmakeVar(masterTableId, walkerContext->columnId,\n\t\t\t\t\t\t\t\t\t\texprType((Node *) targetEntry->expr),\n\t\t\t\t\t\t\t\t\t\texprTypmod((Node *) targetEntry->expr),\n\t\t\t\t\t\t\t\t\t\texprCollation((Node *) targetEntry->expr),\n\t\t\t\t\t\t\t\t\t\tcolumnLevelsUp);\n\t\t\twalkerContext->columnId++;\n\t\t}\n\n\t\taggregate->aggdirectargs = NIL;\n\t\tExpr *directarg;\n\t\tforeach_declared_ptr(directarg, originalAggregate->aggdirectargs)\n\t\t{\n\t\t\t/*\n\t\t\t * Need to replace nodes that contain any Vars with Vars referring\n\t\t\t * to the related column of the result set returned for the worker\n\t\t\t * aggregation.\n\t\t\t *\n\t\t\t * When there are no Vars, then the expression can be fully evaluated\n\t\t\t * on the coordinator, so we skip it here. This is not just an\n\t\t\t * optimization, but the result of the expression might require\n\t\t\t * calling the final function of the aggregate, and doing so when\n\t\t\t * there are no input rows (i.e.: with an empty tuple slot) is not\n\t\t\t * desirable for the node-executor methods.\n\t\t\t */\n\t\t\tif (pull_var_clause_default((Node *) directarg) != NIL)\n\t\t\t{\n\t\t\t\tVar *var = makeVar(masterTableId, walkerContext->columnId,\n\t\t\t\t\t\t\t\t   exprType((Node *) directarg),\n\t\t\t\t\t\t\t\t   exprTypmod((Node *) directarg),\n\t\t\t\t\t\t\t\t   exprCollation((Node *) directarg),\n\t\t\t\t\t\t\t\t   columnLevelsUp);\n\t\t\t\taggregate->aggdirectargs = lappend(aggregate->aggdirectargs, var);\n\t\t\t\twalkerContext->columnId++;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\taggregate->aggdirectargs = lappend(aggregate->aggdirectargs, directarg);\n\t\t\t}\n\t\t}\n\n\t\tif (aggregate->aggfilter)\n\t\t{\n\t\t\taggregate->aggfilter = (Expr *)\n\t\t\t\t\t\t\t\t   makeVar(masterTableId, walkerContext->columnId,\n\t\t\t\t\t\t\t\t\t\t   BOOLOID, -1, InvalidOid, columnLevelsUp);\n\t\t\twalkerContext->columnId++;\n\t\t}\n\n\t\tnewMasterExpression = (Expr *) aggregate;\n\t}\n\telse if (aggregateType == AGGREGATE_COUNT && originalAggregate->aggdistinct &&\n\t\t\t CountDistinctErrorRate == DISABLE_DISTINCT_APPROXIMATION &&\n\t\t\t walkerContext->extendedOpNodeProperties->pullDistinctColumns)\n\t{\n\t\tAggref *aggregate = (Aggref *) copyObject(originalAggregate);\n\t\tList *varList = pull_var_clause_default((Node *) aggregate);\n\t\tList *uniqueVarList = NIL;\n\t\tint startColumnCount = walkerContext->columnId;\n\n\t\t/* determine unique vars that were placed in target list by worker */\n\t\tVar *column = NULL;\n\t\tforeach_declared_ptr(column, varList)\n\t\t{\n\t\t\tuniqueVarList = list_append_unique(uniqueVarList, copyObject(column));\n\t\t}\n\n\t\t/*\n\t\t * Go over each var inside aggregate and update their varattno's according to\n\t\t * worker query target entry column index.\n\t\t */\n\t\tVar *columnToUpdate = NULL;\n\t\tforeach_declared_ptr(columnToUpdate, varList)\n\t\t{\n\t\t\tint columnIndex = 0;\n\n\t\t\tVar *currentVar = NULL;\n\t\t\tforeach_declared_ptr(currentVar, uniqueVarList)\n\t\t\t{\n\t\t\t\tif (equal(columnToUpdate, currentVar))\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcolumnIndex++;\n\t\t\t}\n\n\t\t\tcolumnToUpdate->varno = masterTableId;\n\t\t\tcolumnToUpdate->varnosyn = masterTableId;\n\t\t\tcolumnToUpdate->varattno = startColumnCount + columnIndex;\n\t\t\tcolumnToUpdate->varattnosyn = startColumnCount + columnIndex;\n\t\t}\n\n\t\t/* we added that many columns */\n\t\twalkerContext->columnId += list_length(uniqueVarList);\n\n\t\tnewMasterExpression = (Expr *) aggregate;\n\t}\n\telse if (aggregateType == AGGREGATE_COUNT && originalAggregate->aggdistinct &&\n\t\t\t CountDistinctErrorRate != DISABLE_DISTINCT_APPROXIMATION)\n\t{\n\t\t/*\n\t\t * If enabled, we check for count(distinct) approximations before count\n\t\t * distincts. For this, we first compute hll_add_agg(hll_hash(column)) on\n\t\t * worker nodes, and get hll values. We then gather hlls on the master\n\t\t * node, and compute hll_cardinality(hll_union_agg(hll)).\n\t\t */\n\t\tconst int argCount = 1;\n\t\tconst int defaultTypeMod = -1;\n\n\n\t\t/* extract schema name of hll */\n\t\tOid hllId = get_extension_oid(HLL_EXTENSION_NAME, false);\n\t\tOid hllSchemaOid = get_extension_schema(hllId);\n\t\tconst char *hllSchemaName = get_namespace_name(hllSchemaOid);\n\n\t\tOid unionFunctionId = FunctionOid(hllSchemaName, HLL_UNION_AGGREGATE_NAME,\n\t\t\t\t\t\t\t\t\t\t  argCount);\n\t\tOid cardinalityFunctionId = FunctionOid(hllSchemaName, HLL_CARDINALITY_FUNC_NAME,\n\t\t\t\t\t\t\t\t\t\t\t\targCount);\n\t\tOid cardinalityReturnType = get_func_rettype(cardinalityFunctionId);\n\n\t\tOid hllType = TypeOid(hllSchemaOid, HLL_TYPE_NAME);\n\t\tOid hllTypeCollationId = get_typcollation(hllType);\n\t\tVar *hllColumn = makeVar(masterTableId, walkerContext->columnId, hllType,\n\t\t\t\t\t\t\t\t defaultTypeMod,\n\t\t\t\t\t\t\t\t hllTypeCollationId, columnLevelsUp);\n\t\twalkerContext->columnId++;\n\n\t\tTargetEntry *hllTargetEntry = makeTargetEntry((Expr *) hllColumn, argumentId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL, false);\n\n\t\tAggref *unionAggregate = makeNode(Aggref);\n\t\tunionAggregate->aggfnoid = unionFunctionId;\n\t\tunionAggregate->aggtype = hllType;\n\t\tunionAggregate->args = list_make1(hllTargetEntry);\n\t\tunionAggregate->aggkind = AGGKIND_NORMAL;\n\t\tunionAggregate->aggfilter = NULL;\n\t\tunionAggregate->aggtranstype = InvalidOid;\n\t\tunionAggregate->aggargtypes = list_make1_oid(unionAggregate->aggtype);\n\t\tunionAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\tFuncExpr *cardinalityExpression = makeNode(FuncExpr);\n\t\tcardinalityExpression->funcid = cardinalityFunctionId;\n\t\tcardinalityExpression->funcresulttype = cardinalityReturnType;\n\t\tcardinalityExpression->args = list_make1(unionAggregate);\n\n\t\tnewMasterExpression = (Expr *) cardinalityExpression;\n\t}\n\telse if (aggregateType == AGGREGATE_AVERAGE)\n\t{\n\t\t/*\n\t\t * If the original aggregate is an average, we first compute sum(colum)\n\t\t * and count(column) on worker nodes. Then, we compute (sum(sum(column))\n\t\t * / sum(count(column))) on the master node.\n\t\t */\n\t\tconst char *sumAggregateName = AggregateNames[AGGREGATE_SUM];\n\t\tconst char *countAggregateName = AggregateNames[AGGREGATE_COUNT];\n\n\t\tOid argumentType = AggregateArgumentType(originalAggregate);\n\n\t\tOid sumFunctionId = AggregateFunctionOid(sumAggregateName, argumentType);\n\t\tOid countFunctionId = AggregateFunctionOid(countAggregateName, ANYOID);\n\n\t\t/* calculate the aggregate types that worker nodes are going to return */\n\t\tOid workerSumReturnType = get_func_rettype(sumFunctionId);\n\t\tOid workerCountReturnType = get_func_rettype(countFunctionId);\n\n\t\t/* create the expression sum(sum(column) / sum(count(column))) */\n\t\tnewMasterExpression = MasterAverageExpression(workerSumReturnType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  workerCountReturnType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  &(walkerContext->columnId));\n\t}\n\telse if (aggregateType == AGGREGATE_COUNT)\n\t{\n\t\t/*\n\t\t * Count aggregates are handled in two steps. First, worker nodes report\n\t\t * their count results. Then, the master node sums up these results.\n\t\t */\n\n\t\t/* worker aggregate and original aggregate have the same return type */\n\t\tOid workerReturnType = exprType((Node *) originalAggregate);\n\t\tint32 workerReturnTypeMod = exprTypmod((Node *) originalAggregate);\n\t\tOid workerCollationId = exprCollation((Node *) originalAggregate);\n\n\t\tconst char *sumAggregateName = AggregateNames[AGGREGATE_SUM];\n\t\tOid sumFunctionId = AggregateFunctionOid(sumAggregateName, workerReturnType);\n\t\tOid masterReturnType = get_func_rettype(sumFunctionId);\n\n\t\tAggref *newMasterAggregate = copyObject(originalAggregate);\n\t\tnewMasterAggregate->aggstar = false;\n\t\tnewMasterAggregate->aggdistinct = NULL;\n\t\tnewMasterAggregate->aggfnoid = sumFunctionId;\n\t\tnewMasterAggregate->aggtype = masterReturnType;\n\t\tnewMasterAggregate->aggfilter = NULL;\n\t\tnewMasterAggregate->aggtranstype = InvalidOid;\n\t\tnewMasterAggregate->aggargtypes = list_make1_oid(newMasterAggregate->aggtype);\n\t\tnewMasterAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\tVar *column = makeVar(masterTableId, walkerContext->columnId, workerReturnType,\n\t\t\t\t\t\t\t  workerReturnTypeMod, workerCollationId, columnLevelsUp);\n\t\twalkerContext->columnId++;\n\n\t\t/* aggref expects its arguments to be wrapped in target entries */\n\t\tTargetEntry *columnTargetEntry = makeTargetEntry((Expr *) column, argumentId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t NULL, false);\n\t\tnewMasterAggregate->args = list_make1(columnTargetEntry);\n\n\t\t/* cast numeric sum result to bigint (count's return type) */\n\t\tCoerceViaIO *coerceExpr = makeNode(CoerceViaIO);\n\t\tcoerceExpr->arg = (Expr *) newMasterAggregate;\n\t\tcoerceExpr->resulttype = INT8OID;\n\t\tcoerceExpr->resultcollid = InvalidOid;\n\t\tcoerceExpr->coerceformat = COERCE_IMPLICIT_CAST;\n\t\tcoerceExpr->location = -1;\n\n\t\t/* convert NULL to 0 in case of no rows */\n\t\tConst *zeroConst = MakeIntegerConstInt64(0);\n\t\tList *coalesceArgs = list_make2(coerceExpr, zeroConst);\n\n\t\tCoalesceExpr *coalesceExpr = makeNode(CoalesceExpr);\n\t\tcoalesceExpr->coalescetype = INT8OID;\n\t\tcoalesceExpr->coalescecollid = InvalidOid;\n\t\tcoalesceExpr->args = coalesceArgs;\n\t\tcoalesceExpr->location = -1;\n\n\t\tnewMasterExpression = (Expr *) coalesceExpr;\n\t}\n\telse if (aggregateType == AGGREGATE_ARRAY_AGG ||\n\t\t\t aggregateType == AGGREGATE_JSONB_AGG ||\n\t\t\t aggregateType == AGGREGATE_JSONB_OBJECT_AGG ||\n\t\t\t aggregateType == AGGREGATE_JSON_AGG ||\n\t\t\t aggregateType == AGGREGATE_JSON_OBJECT_AGG)\n\t{\n\t\t/*\n\t\t * Array and json aggregates are handled in two steps. First, we compute\n\t\t * array_agg() or json aggregate on the worker nodes. Then, we gather\n\t\t * the arrays or jsons on the master and compute the array_cat_agg()\n\t\t * or jsonb_cat_agg() aggregate on them to get the final array or json.\n\t\t */\n\t\tconst char *catAggregateName = NULL;\n\t\tOid catInputType = InvalidOid;\n\n\t\t/* worker aggregate and original aggregate have same return type */\n\t\tOid workerReturnType = exprType((Node *) originalAggregate);\n\t\tint32 workerReturnTypeMod = exprTypmod((Node *) originalAggregate);\n\t\tOid workerCollationId = exprCollation((Node *) originalAggregate);\n\n\t\t/* assert that we do not support array or json aggregation with\n\t\t * distinct or order by */\n\t\tAssert(!originalAggregate->aggorder);\n\t\tAssert(!originalAggregate->aggdistinct);\n\n\t\tif (aggregateType == AGGREGATE_ARRAY_AGG)\n\t\t{\n\t\t\t/* array_cat_agg() takes anyarray as input */\n\t\t\tcatAggregateName = ARRAY_CAT_AGGREGATE_NAME;\n\t\t\tcatInputType = ANYCOMPATIBLEARRAYOID;\n\t\t}\n\t\telse if (aggregateType == AGGREGATE_JSONB_AGG ||\n\t\t\t\t aggregateType == AGGREGATE_JSONB_OBJECT_AGG)\n\t\t{\n\t\t\t/* jsonb_cat_agg() takes jsonb as input */\n\t\t\tcatAggregateName = JSONB_CAT_AGGREGATE_NAME;\n\t\t\tcatInputType = JSONBOID;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* json_cat_agg() takes json as input */\n\t\t\tcatAggregateName = JSON_CAT_AGGREGATE_NAME;\n\t\t\tcatInputType = JSONOID;\n\t\t}\n\n\t\tAssert(catAggregateName != NULL);\n\t\tAssert(catInputType != InvalidOid);\n\n\t\tOid aggregateFunctionId = AggregateFunctionOid(catAggregateName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   catInputType);\n\n\t\t/* create argument for the array_cat_agg() or jsonb_cat_agg() aggregate */\n\t\tVar *column = makeVar(masterTableId, walkerContext->columnId, workerReturnType,\n\t\t\t\t\t\t\t  workerReturnTypeMod, workerCollationId, columnLevelsUp);\n\t\tTargetEntry *catAggArgument = makeTargetEntry((Expr *) column, argumentId, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  false);\n\t\twalkerContext->columnId++;\n\n\t\t/* construct the master array_cat_agg() or jsonb_cat_agg() expression */\n\t\tAggref *newMasterAggregate = copyObject(originalAggregate);\n\t\tnewMasterAggregate->aggfnoid = aggregateFunctionId;\n\t\tnewMasterAggregate->args = list_make1(catAggArgument);\n\t\tnewMasterAggregate->aggfilter = NULL;\n\t\tnewMasterAggregate->aggtranstype = InvalidOid;\n\n\t\tif (aggregateType == AGGREGATE_ARRAY_AGG)\n\t\t{\n\t\t\t/*\n\t\t\t * Postgres expects the type of the array here such as INT4ARRAYOID.\n\t\t\t * Hence we set it to workerReturnType. If we set this to\n\t\t\t * ANYCOMPATIBLEARRAYOID then we will get the following error:\n\t\t\t * \"argument declared anycompatiblearray is not an array but type anycompatiblearray\"\n\t\t\t */\n\t\t\tnewMasterAggregate->aggargtypes = list_make1_oid(workerReturnType);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnewMasterAggregate->aggargtypes = list_make1_oid(ANYARRAYOID);\n\t\t}\n\t\tnewMasterAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\tnewMasterExpression = (Expr *) newMasterAggregate;\n\t}\n\telse if (aggregateType == AGGREGATE_HLL_ADD ||\n\t\t\t aggregateType == AGGREGATE_HLL_UNION)\n\t{\n\t\t/*\n\t\t * If hll aggregates are called, we simply create the hll_union_aggregate\n\t\t * to apply in the master after running the original aggregate in\n\t\t * workers.\n\t\t */\n\n\t\tOid hllType = exprType((Node *) originalAggregate);\n\t\tOid unionFunctionId = AggregateFunctionOid(HLL_UNION_AGGREGATE_NAME, hllType);\n\t\tint32 hllReturnTypeMod = exprTypmod((Node *) originalAggregate);\n\t\tOid hllTypeCollationId = exprCollation((Node *) originalAggregate);\n\n\t\tVar *hllColumn = makeVar(masterTableId, walkerContext->columnId, hllType,\n\t\t\t\t\t\t\t\t hllReturnTypeMod, hllTypeCollationId, columnLevelsUp);\n\t\twalkerContext->columnId++;\n\n\t\tTargetEntry *hllTargetEntry = makeTargetEntry((Expr *) hllColumn, argumentId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL, false);\n\n\t\tAggref *unionAggregate = makeNode(Aggref);\n\t\tunionAggregate->aggfnoid = unionFunctionId;\n\t\tunionAggregate->aggtype = hllType;\n\t\tunionAggregate->args = list_make1(hllTargetEntry);\n\t\tunionAggregate->aggkind = AGGKIND_NORMAL;\n\t\tunionAggregate->aggfilter = NULL;\n\t\tunionAggregate->aggtranstype = InvalidOid;\n\t\tunionAggregate->aggargtypes = list_make1_oid(hllType);\n\t\tunionAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\tnewMasterExpression = (Expr *) unionAggregate;\n\t}\n\telse if (aggregateType == AGGREGATE_TOPN_UNION_AGG ||\n\t\t\t aggregateType == AGGREGATE_TOPN_ADD_AGG)\n\t{\n\t\t/*\n\t\t * Top-N aggregates are handled in two steps. First, we compute\n\t\t * topn_add_agg() or topn_union_agg() aggregates on the worker nodes.\n\t\t * Then, we gather the Top-Ns on the master and take the union of all\n\t\t * to get the final topn.\n\t\t */\n\n\t\t/* worker aggregate and original aggregate have same return type */\n\t\tOid topnType = exprType((Node *) originalAggregate);\n\t\tOid unionFunctionId = AggregateFunctionOid(TOPN_UNION_AGGREGATE_NAME,\n\t\t\t\t\t\t\t\t\t\t\t\t   topnType);\n\t\tint32 topnReturnTypeMod = exprTypmod((Node *) originalAggregate);\n\t\tOid topnTypeCollationId = exprCollation((Node *) originalAggregate);\n\n\t\t/* create argument for the topn_union_agg() aggregate */\n\t\tVar *topnColumn = makeVar(masterTableId, walkerContext->columnId, topnType,\n\t\t\t\t\t\t\t\t  topnReturnTypeMod, topnTypeCollationId, columnLevelsUp);\n\t\twalkerContext->columnId++;\n\n\t\tTargetEntry *topNTargetEntry = makeTargetEntry((Expr *) topnColumn, argumentId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   NULL, false);\n\n\t\t/* construct the master topn_union_agg() expression */\n\t\tAggref *unionAggregate = makeNode(Aggref);\n\t\tunionAggregate->aggfnoid = unionFunctionId;\n\t\tunionAggregate->aggtype = topnType;\n\t\tunionAggregate->args = list_make1(topNTargetEntry);\n\t\tunionAggregate->aggkind = AGGKIND_NORMAL;\n\t\tunionAggregate->aggfilter = NULL;\n\t\tunionAggregate->aggtranstype = InvalidOid;\n\t\tunionAggregate->aggargtypes = list_make1_oid(topnType);\n\t\tunionAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\tnewMasterExpression = (Expr *) unionAggregate;\n\t}\n\telse if (aggregateType == AGGREGATE_TDIGEST_COMBINE ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_ADD_DOUBLE)\n\t{\n\t\t/* tdigest of column */\n\t\tOid tdigestType = TDigestExtensionTypeOid(); /* tdigest type */\n\t\tOid unionFunctionId = TDigestExtensionAggTDigest1();\n\n\t\tint32 tdigestReturnTypeMod = exprTypmod((Node *) originalAggregate);\n\t\tOid tdigestTypeCollationId = exprCollation((Node *) originalAggregate);\n\n\t\t/* create first argument for tdigest_precentile(tdigest, double) */\n\t\tVar *tdigestColumn = makeVar(masterTableId, walkerContext->columnId, tdigestType,\n\t\t\t\t\t\t\t\t\t tdigestReturnTypeMod, tdigestTypeCollationId,\n\t\t\t\t\t\t\t\t\t columnLevelsUp);\n\t\tTargetEntry *tdigestTargetEntry = makeTargetEntry((Expr *) tdigestColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  argumentId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL, false);\n\t\twalkerContext->columnId++;\n\n\t\t/* construct the master tdigest(tdigest) expression */\n\t\tAggref *unionAggregate = makeNode(Aggref);\n\t\tunionAggregate->aggfnoid = unionFunctionId;\n\t\tunionAggregate->aggtype = originalAggregate->aggtype;\n\t\tunionAggregate->args = list_make1(tdigestTargetEntry);\n\t\tunionAggregate->aggkind = AGGKIND_NORMAL;\n\t\tunionAggregate->aggfilter = NULL;\n\t\tunionAggregate->aggtranstype = InvalidOid;\n\t\tunionAggregate->aggargtypes = list_make1_oid(tdigestType);\n\t\tunionAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\tnewMasterExpression = (Expr *) unionAggregate;\n\t}\n\telse if (aggregateType == AGGREGATE_TDIGEST_PERCENTILE_ADD_DOUBLE ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_ADD_DOUBLEARRAY ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_OF_ADD_DOUBLE ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_OF_ADD_DOUBLEARRAY)\n\t{\n\t\t/* tdigest of column */\n\t\tOid tdigestType = TDigestExtensionTypeOid();\n\t\tOid unionFunctionId = InvalidOid;\n\t\tif (aggregateType == AGGREGATE_TDIGEST_PERCENTILE_ADD_DOUBLE)\n\t\t{\n\t\t\tunionFunctionId = TDigestExtensionAggTDigestPercentile2();\n\t\t}\n\t\telse if (aggregateType == AGGREGATE_TDIGEST_PERCENTILE_ADD_DOUBLEARRAY)\n\t\t{\n\t\t\tunionFunctionId = TDigestExtensionAggTDigestPercentile2a();\n\t\t}\n\t\telse if (aggregateType == AGGREGATE_TDIGEST_PERCENTILE_OF_ADD_DOUBLE)\n\t\t{\n\t\t\tunionFunctionId = TDigestExtensionAggTDigestPercentileOf2();\n\t\t}\n\t\telse if (aggregateType == AGGREGATE_TDIGEST_PERCENTILE_OF_ADD_DOUBLEARRAY)\n\t\t{\n\t\t\tunionFunctionId = TDigestExtensionAggTDigestPercentileOf2a();\n\t\t}\n\t\tAssert(OidIsValid(unionFunctionId));\n\n\t\tint32 tdigestReturnTypeMod = exprTypmod((Node *) originalAggregate);\n\t\tOid tdigestTypeCollationId = exprCollation((Node *) originalAggregate);\n\n\t\t/* create first argument for tdigest_precentile(tdigest, double) */\n\t\tVar *tdigestColumn = makeVar(masterTableId, walkerContext->columnId, tdigestType,\n\t\t\t\t\t\t\t\t\t tdigestReturnTypeMod, tdigestTypeCollationId,\n\t\t\t\t\t\t\t\t\t columnLevelsUp);\n\t\tTargetEntry *tdigestTargetEntry = makeTargetEntry((Expr *) tdigestColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  argumentId, NULL, false);\n\t\twalkerContext->columnId++;\n\n\t\t/* construct the master tdigest_precentile(tdigest, double) expression */\n\t\tAggref *unionAggregate = makeNode(Aggref);\n\t\tunionAggregate->aggfnoid = unionFunctionId;\n\t\tunionAggregate->aggtype = originalAggregate->aggtype;\n\t\tunionAggregate->args = list_make2(\n\t\t\ttdigestTargetEntry,\n\t\t\tlist_nth(originalAggregate->args, 2));\n\t\tunionAggregate->aggkind = AGGKIND_NORMAL;\n\t\tunionAggregate->aggfilter = NULL;\n\t\tunionAggregate->aggtranstype = InvalidOid;\n\t\tunionAggregate->aggargtypes = list_make2_oid(\n\t\t\ttdigestType,\n\t\t\tlist_nth_oid(originalAggregate->aggargtypes, 2));\n\t\tunionAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\tnewMasterExpression = (Expr *) unionAggregate;\n\t}\n\telse if (aggregateType == AGGREGATE_TDIGEST_PERCENTILE_TDIGEST_DOUBLE ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_TDIGEST_DOUBLEARRAY ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_OF_TDIGEST_DOUBLE ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_OF_TDIGEST_DOUBLEARRAY)\n\t{\n\t\t/* tdigest of column */\n\t\tOid tdigestType = TDigestExtensionTypeOid();\n\n\t\t/* These functions already will combine the tdigest arguments returned */\n\t\tOid unionFunctionId = originalAggregate->aggfnoid;\n\n\t\tint32 tdigestReturnTypeMod = exprTypmod((Node *) originalAggregate);\n\t\tOid tdigestTypeCollationId = exprCollation((Node *) originalAggregate);\n\n\t\t/* create first argument for tdigest_precentile(tdigest, double) */\n\t\tVar *tdigestColumn = makeVar(masterTableId, walkerContext->columnId, tdigestType,\n\t\t\t\t\t\t\t\t\t tdigestReturnTypeMod, tdigestTypeCollationId,\n\t\t\t\t\t\t\t\t\t columnLevelsUp);\n\t\tTargetEntry *tdigestTargetEntry = makeTargetEntry((Expr *) tdigestColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  argumentId, NULL, false);\n\t\twalkerContext->columnId++;\n\n\t\t/* construct the master tdigest_precentile(tdigest, double) expression */\n\t\tAggref *unionAggregate = makeNode(Aggref);\n\t\tunionAggregate->aggfnoid = unionFunctionId;\n\t\tunionAggregate->aggtype = originalAggregate->aggtype;\n\t\tunionAggregate->args = list_make2(\n\t\t\ttdigestTargetEntry,\n\t\t\tlist_nth(originalAggregate->args, 1));\n\t\tunionAggregate->aggkind = AGGKIND_NORMAL;\n\t\tunionAggregate->aggfilter = NULL;\n\t\tunionAggregate->aggtranstype = InvalidOid;\n\t\tunionAggregate->aggargtypes = list_make2_oid(\n\t\t\ttdigestType,\n\t\t\tlist_nth_oid(originalAggregate->aggargtypes, 1));\n\t\tunionAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\tnewMasterExpression = (Expr *) unionAggregate;\n\t}\n\telse if (aggregateType == AGGREGATE_CUSTOM_COMBINE)\n\t{\n\t\tHeapTuple aggTuple =\n\t\t\tSearchSysCache1(AGGFNOID, ObjectIdGetDatum(originalAggregate->aggfnoid));\n\t\tForm_pg_aggregate aggform;\n\t\tOid combine;\n\t\tbool useBinaryCoordinatorCombine = false;\n\n\t\tif (!HeapTupleIsValid(aggTuple))\n\t\t{\n\t\t\telog(ERROR, \"citus cache lookup failed for aggregate %u\",\n\t\t\t\t originalAggregate->aggfnoid);\n\t\t\treturn NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\taggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);\n\t\t\tcombine = aggform->aggcombinefn;\n\t\t\tuseBinaryCoordinatorCombine = aggform->aggtranstype != InvalidOid &&\n\t\t\t\t\t\t\t\t\t\t  IsAggTransTypeBinarySerializable(aggform);\n\t\t\tReleaseSysCache(aggTuple);\n\t\t}\n\n\t\tif (combine != InvalidOid)\n\t\t{\n\t\t\tOid coordCombineId =\n\t\t\t\tuseBinaryCoordinatorCombine ? CoordBinaryCombineAggOid()\n\t\t\t\t\t\t\t\t\t\t\t: CoordCombineAggOid();\n\t\t\tOid workerReturnType = useBinaryCoordinatorCombine ? BYTEAOID : CSTRINGOID;\n\t\t\tint32 workerReturnTypeMod = -1;\n\t\t\tOid workerCollationId = InvalidOid;\n\t\t\tOid resultType = exprType((Node *) originalAggregate);\n\n\t\t\tConst *aggOidParam = makeConst(OIDOID, -1, InvalidOid, sizeof(Oid),\n\t\t\t\t\t\t\t\t\t\t   ObjectIdGetDatum(originalAggregate->aggfnoid),\n\t\t\t\t\t\t\t\t\t\t   false, true);\n\t\t\tVar *column = makeVar(masterTableId, walkerContext->columnId,\n\t\t\t\t\t\t\t\t  workerReturnType,\n\t\t\t\t\t\t\t\t  workerReturnTypeMod, workerCollationId, columnLevelsUp);\n\t\t\twalkerContext->columnId++;\n\t\t\tConst *nullTag = makeNullConst(resultType, -1, InvalidOid);\n\n\t\t\tList *aggArguments =\n\t\t\t\tlist_make3(makeTargetEntry((Expr *) aggOidParam, 1, NULL, false),\n\t\t\t\t\t\t   makeTargetEntry((Expr *) column, 2, NULL, false),\n\t\t\t\t\t\t   makeTargetEntry((Expr *) nullTag, 3, NULL, false));\n\n\t\t\t/* coord_combine_agg(agg, workercol) */\n\t\t\tAggref *newMasterAggregate = makeNode(Aggref);\n\t\t\tnewMasterAggregate->aggfnoid = coordCombineId;\n\t\t\tnewMasterAggregate->aggtype = originalAggregate->aggtype;\n\t\t\tnewMasterAggregate->args = aggArguments;\n\t\t\tnewMasterAggregate->aggkind = AGGKIND_NORMAL;\n\t\t\tnewMasterAggregate->aggfilter = NULL;\n\t\t\tnewMasterAggregate->aggtranstype = INTERNALOID;\n\t\t\tnewMasterAggregate->aggargtypes = list_make3_oid(OIDOID, workerReturnType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t resultType);\n\t\t\tnewMasterAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\t\tnewMasterExpression = (Expr *) newMasterAggregate;\n\t\t}\n\t\telse\n\t\t{\n\t\t\telog(ERROR, \"Aggregate lacks COMBINEFUNC\");\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * All other aggregates are handled as they are. These include sum, min,\n\t\t * and max.\n\t\t */\n\n\t\t/* worker aggregate and original aggregate have the same return type */\n\t\tOid workerReturnType = exprType((Node *) originalAggregate);\n\t\tint32 workerReturnTypeMod = exprTypmod((Node *) originalAggregate);\n\t\tOid workerCollationId = exprCollation((Node *) originalAggregate);\n\n\t\tconst char *aggregateName = AggregateNames[aggregateType];\n\t\tOid aggregateFunctionId = AggregateFunctionOid(aggregateName, workerReturnType);\n\t\tOid masterReturnType = get_func_rettype(aggregateFunctionId);\n\n\t\tAggref *newMasterAggregate = copyObject(originalAggregate);\n\t\tnewMasterAggregate->aggdistinct = NULL;\n\t\tnewMasterAggregate->aggfnoid = aggregateFunctionId;\n\t\tnewMasterAggregate->aggtype = masterReturnType;\n\t\tnewMasterAggregate->aggfilter = NULL;\n\n\t\t/*\n\t\t * Polymorphic aggregates determine their actual return type based on\n\t\t * their argument type, so replace it with the worker return type.\n\t\t */\n\t\tif (IsPolymorphicTypeFamily1(masterReturnType))\n\t\t{\n\t\t\tnewMasterAggregate->aggtype = workerReturnType;\n\n\t\t\tExpr *firstArg = FirstAggregateArgument(originalAggregate);\n\t\t\tnewMasterAggregate->aggcollid = exprCollation((Node *) firstArg);\n\t\t}\n\n\t\tVar *column = makeVar(masterTableId, walkerContext->columnId, workerReturnType,\n\t\t\t\t\t\t\t  workerReturnTypeMod, workerCollationId, columnLevelsUp);\n\t\twalkerContext->columnId++;\n\n\t\t/* aggref expects its arguments to be wrapped in target entries */\n\t\tTargetEntry *columnTargetEntry = makeTargetEntry((Expr *) column, argumentId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t NULL, false);\n\t\tnewMasterAggregate->args = list_make1(columnTargetEntry);\n\n\t\tnewMasterExpression = (Expr *) newMasterAggregate;\n\t}\n\n\n\t/*\n\t * Aggregate functions could have changed the return type. If so, we wrap\n\t * the new expression with a conversion function to make it have the same\n\t * type as the original aggregate. We need this since functions like sorting\n\t * and grouping have already been chosen based on the original type.\n\t */\n\tExpr *typeConvertedExpression = AddTypeConversion((Node *) originalAggregate,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  (Node *) newMasterExpression);\n\tif (typeConvertedExpression != NULL)\n\t{\n\t\tnewMasterExpression = typeConvertedExpression;\n\t}\n\n\treturn newMasterExpression;\n}\n\n\n/*\n * MasterAverageExpression creates an expression of the form (sum(column1) /\n * sum(column2)), where column1 is the sum of the original value, and column2 is\n * the count of that value. This expression allows us to evaluate the average\n * function over distributed data.\n */\nstatic Expr *\nMasterAverageExpression(Oid sumAggregateType, Oid countAggregateType,\n\t\t\t\t\t\tAttrNumber *columnId)\n{\n\tconst char *sumAggregateName = AggregateNames[AGGREGATE_SUM];\n\tconst int32 defaultTypeMod = -1;\n\tconst Index defaultLevelsUp = 0;\n\tconst AttrNumber argumentId = 1;\n\n\tOid sumTypeCollationId = get_typcollation(sumAggregateType);\n\tOid countTypeCollationId = get_typcollation(countAggregateType);\n\n\t/* create the first argument for sum(column1) */\n\tVar *firstColumn = makeVar(masterTableId, (*columnId), sumAggregateType,\n\t\t\t\t\t\t\t   defaultTypeMod, sumTypeCollationId, defaultLevelsUp);\n\tTargetEntry *firstTargetEntry = makeTargetEntry((Expr *) firstColumn, argumentId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, false);\n\t(*columnId)++;\n\n\tAggref *firstSum = makeNode(Aggref);\n\tfirstSum->aggfnoid = AggregateFunctionOid(sumAggregateName, sumAggregateType);\n\tfirstSum->aggtype = get_func_rettype(firstSum->aggfnoid);\n\tfirstSum->args = list_make1(firstTargetEntry);\n\tfirstSum->aggkind = AGGKIND_NORMAL;\n\tfirstSum->aggtranstype = InvalidOid;\n\tfirstSum->aggargtypes = list_make1_oid(firstSum->aggtype);\n\tfirstSum->aggsplit = AGGSPLIT_SIMPLE;\n\n\t/* create the second argument for sum(column2) */\n\tVar *secondColumn = makeVar(masterTableId, (*columnId), countAggregateType,\n\t\t\t\t\t\t\t\tdefaultTypeMod, countTypeCollationId, defaultLevelsUp);\n\tTargetEntry *secondTargetEntry = makeTargetEntry((Expr *) secondColumn, argumentId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t NULL, false);\n\t(*columnId)++;\n\n\tAggref *secondSum = makeNode(Aggref);\n\tsecondSum->aggfnoid = AggregateFunctionOid(sumAggregateName, countAggregateType);\n\tsecondSum->aggtype = get_func_rettype(secondSum->aggfnoid);\n\tsecondSum->args = list_make1(secondTargetEntry);\n\tsecondSum->aggkind = AGGKIND_NORMAL;\n\tsecondSum->aggtranstype = InvalidOid;\n\tsecondSum->aggargtypes = list_make1_oid(firstSum->aggtype);\n\tsecondSum->aggsplit = AGGSPLIT_SIMPLE;\n\n\t/*\n\t * Build the division operator between these two aggregates. This function\n\t * will convert the types of the aggregates if necessary.\n\t */\n\tList *operatorNameList = list_make1(makeString(DIVISION_OPER_NAME));\n\tExpr *opExpr = make_op(NULL, operatorNameList, (Node *) firstSum, (Node *) secondSum,\n\t\t\t\t\t\t   NULL,\n\t\t\t\t\t\t   -1);\n\n\treturn opExpr;\n}\n\n\n/*\n * AddTypeConversion checks if the given expressions generate the same types. If\n * they don't, the function adds a type conversion function on top of the new\n * expression to have it generate the same type as the original aggregate.\n */\nstatic Expr *\nAddTypeConversion(Node *originalAggregate, Node *newExpression)\n{\n\tOid newTypeId = exprType(newExpression);\n\tOid originalTypeId = exprType(originalAggregate);\n\tint32 originalTypeMod = exprTypmod(originalAggregate);\n\n\t/* nothing to do if the two types are the same */\n\tif (originalTypeId == newTypeId)\n\t{\n\t\treturn NULL;\n\t}\n\n\t/* otherwise, add a type conversion function */\n\tNode *typeConvertedExpression = coerce_to_target_type(NULL, newExpression, newTypeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  originalTypeId, originalTypeMod,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  COERCION_EXPLICIT,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  COERCE_EXPLICIT_CAST, -1);\n\tAssert(typeConvertedExpression != NULL);\n\treturn (Expr *) typeConvertedExpression;\n}\n\n\n/*\n * WorkerExtendedOpNode creates the worker extended operator node from the given\n * originalOpNode and extendedOpNodeProperties.\n *\n * For the details of the processing see the comments of the functions that\n * are called from this function.\n */\nstatic MultiExtendedOp *\nWorkerExtendedOpNode(MultiExtendedOp *originalOpNode,\n\t\t\t\t\t ExtendedOpNodeProperties *extendedOpNodeProperties)\n{\n\tbool distinctPreventsLimitPushdown = false;\n\n\tQueryTargetList queryTargetList;\n\tQueryGroupClause queryGroupClause;\n\tQueryDistinctClause queryDistinctClause;\n\tQueryWindowClause queryWindowClause;\n\tQueryOrderByLimit queryOrderByLimit;\n\tNode *queryHavingQual = NULL;\n\n\tList *originalTargetEntryList = originalOpNode->targetList;\n\tList *originalGroupClauseList = originalOpNode->groupClauseList;\n\tList *originalSortClauseList = originalOpNode->sortClauseList;\n\tNode *originalHavingQual = originalOpNode->havingQual;\n\tNode *originalLimitCount = originalOpNode->limitCount;\n\tNode *originalLimitOffset = originalOpNode->limitOffset;\n\tList *originalWindowClause = originalOpNode->windowClause;\n\tList *originalDistinctClause = originalOpNode->distinctClause;\n\tbool hasDistinctOn = originalOpNode->hasDistinctOn;\n\n\tint originalGroupClauseLength = list_length(originalGroupClauseList);\n\n\t/* initialize to default values */\n\tmemset(&queryTargetList, 0, sizeof(queryTargetList));\n\tmemset(&queryGroupClause, 0, sizeof(queryGroupClause));\n\tmemset(&queryDistinctClause, 0, sizeof(queryDistinctClause));\n\tmemset(&queryWindowClause, 0, sizeof(queryWindowClause));\n\tmemset(&queryOrderByLimit, 0, sizeof(queryOrderByLimit));\n\n\t/* calculate the next sort group index based on the original target list */\n\tIndex nextSortGroupRefIndex = GetNextSortGroupRef(originalTargetEntryList);\n\n\t/* targetProjectionNumber starts from 1 */\n\tqueryTargetList.targetProjectionNumber = 1;\n\n\tif (!extendedOpNodeProperties->pullUpIntermediateRows)\n\t{\n\t\tqueryGroupClause.groupClauseList = copyObject(originalGroupClauseList);\n\t}\n\telse\n\t{\n\t\tqueryGroupClause.groupClauseList = NIL;\n\t}\n\n\t/*\n\t * For the purpose of this variable, not pushing down when there are no groups\n\t * is pushing down the original grouping, ie the worker's GROUP BY matches\n\t * the master's GROUP BY.\n\t */\n\tbool pushingDownOriginalGrouping =\n\t\tlist_length(queryGroupClause.groupClauseList) == originalGroupClauseLength;\n\n\t/*\n\t * nextSortGroupRefIndex is used by group by, window and order by clauses.\n\t * Thus, we pass a reference to a single nextSortGroupRefIndex and expect\n\t * it modified separately while processing those parts of the query.\n\t */\n\tqueryGroupClause.nextSortGroupRefIndex = &nextSortGroupRefIndex;\n\tqueryWindowClause.nextSortGroupRefIndex = &nextSortGroupRefIndex;\n\tqueryOrderByLimit.nextSortGroupRefIndex = &nextSortGroupRefIndex;\n\n\t/* process each part of the query in order to generate the worker query's parts */\n\tProcessTargetListForWorkerQuery(originalTargetEntryList, extendedOpNodeProperties,\n\t\t\t\t\t\t\t\t\t&queryTargetList, &queryGroupClause);\n\n\tProcessHavingClauseForWorkerQuery(originalHavingQual, extendedOpNodeProperties,\n\t\t\t\t\t\t\t\t\t  &queryHavingQual, &queryTargetList,\n\t\t\t\t\t\t\t\t\t  &queryGroupClause);\n\n\t/*\n\t * Planner optimizations may leave window clauses with hasWindowFuncs as false.\n\t * Ignore window clauses in that case.\n\t */\n\tif (extendedOpNodeProperties->hasWindowFuncs)\n\t{\n\t\tif (extendedOpNodeProperties->onlyPushableWindowFunctions)\n\t\t{\n\t\t\tProcessWindowFunctionsForWorkerQuery(originalWindowClause,\n\t\t\t\t\t\t\t\t\t\t\t\t originalTargetEntryList,\n\t\t\t\t\t\t\t\t\t\t\t\t &queryWindowClause, &queryTargetList);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tProcessWindowFunctionPullUpForWorkerQuery(originalWindowClause,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  &queryTargetList);\n\t\t}\n\t}\n\n\tif (ShouldProcessDistinctOrderAndLimitForWorker(extendedOpNodeProperties,\n\t\t\t\t\t\t\t\t\t\t\t\t\tpushingDownOriginalGrouping,\n\t\t\t\t\t\t\t\t\t\t\t\t\toriginalHavingQual))\n\t{\n\t\tbool queryHasAggregates = TargetListHasAggregates(originalTargetEntryList);\n\n\t\tProcessDistinctClauseForWorkerQuery(originalDistinctClause, hasDistinctOn,\n\t\t\t\t\t\t\t\t\t\t\tqueryGroupClause.groupClauseList,\n\t\t\t\t\t\t\t\t\t\t\tqueryHasAggregates, &queryDistinctClause,\n\t\t\t\t\t\t\t\t\t\t\t&distinctPreventsLimitPushdown);\n\n\t\t/*\n\t\t * Order by and limit clauses are relevant to each other, and processing\n\t\t * them together makes it handy for us.\n\t\t *\n\t\t * The other parts of the query might have already prohibited pushing down\n\t\t * LIMIT and ORDER BY clauses as described below:\n\t\t *      (1) Creating a new group by clause during aggregate mutation, or\n\t\t *      (2) Distinct clause is not pushed down\n\t\t */\n\t\tbool groupByExtended =\n\t\t\tlist_length(queryGroupClause.groupClauseList) > originalGroupClauseLength;\n\t\tif (pushingDownOriginalGrouping && !groupByExtended &&\n\t\t\t!distinctPreventsLimitPushdown)\n\t\t{\n\t\t\t/* both sort and limit clauses rely on similar information */\n\t\t\tOrderByLimitReference limitOrderByReference =\n\t\t\t\tBuildOrderByLimitReference(hasDistinctOn,\n\t\t\t\t\t\t\t\t\t\t   extendedOpNodeProperties->\n\t\t\t\t\t\t\t\t\t\t   groupedByDisjointPartitionColumn,\n\t\t\t\t\t\t\t\t\t\t   extendedOpNodeProperties->\n\t\t\t\t\t\t\t\t\t\t   onlyPushableWindowFunctions,\n\t\t\t\t\t\t\t\t\t\t   originalGroupClauseList,\n\t\t\t\t\t\t\t\t\t\t   originalSortClauseList,\n\t\t\t\t\t\t\t\t\t\t   originalTargetEntryList);\n\n\t\t\tProcessLimitOrderByForWorkerQuery(limitOrderByReference, originalLimitCount,\n\t\t\t\t\t\t\t\t\t\t\t  originalLimitOffset,\n\t\t\t\t\t\t\t\t\t\t\t  originalSortClauseList,\n\t\t\t\t\t\t\t\t\t\t\t  originalGroupClauseList,\n\t\t\t\t\t\t\t\t\t\t\t  originalTargetEntryList,\n\t\t\t\t\t\t\t\t\t\t\t  &queryOrderByLimit,\n\t\t\t\t\t\t\t\t\t\t\t  &queryTargetList);\n\t\t}\n\t}\n\n\t/* finally, fill the extended op node with the data we gathered */\n\tMultiExtendedOp *workerExtendedOpNode = CitusMakeNode(MultiExtendedOp);\n\n\tworkerExtendedOpNode->targetList = queryTargetList.targetEntryList;\n\tworkerExtendedOpNode->groupClauseList = queryGroupClause.groupClauseList;\n\tworkerExtendedOpNode->havingQual = queryHavingQual;\n\tworkerExtendedOpNode->hasDistinctOn = queryDistinctClause.workerHasDistinctOn;\n\tworkerExtendedOpNode->distinctClause = queryDistinctClause.workerDistinctClause;\n\tworkerExtendedOpNode->hasWindowFuncs = queryWindowClause.hasWindowFunctions;\n\tworkerExtendedOpNode->windowClause = queryWindowClause.workerWindowClauseList;\n\tworkerExtendedOpNode->sortClauseList = queryOrderByLimit.workerSortClauseList;\n\tworkerExtendedOpNode->limitCount = queryOrderByLimit.workerLimitCount;\n\n\t/*\n\t * If the limitCount cannot be pushed down it will be NULL, so the deparser will\n\t * ignore the limitOption.\n\t */\n\tworkerExtendedOpNode->limitOption = originalOpNode->limitOption;\n\n\treturn workerExtendedOpNode;\n}\n\n\n/*\n * ProcessTargetListForWorkerQuery gets the inputs and modifies the outputs\n * such that the worker query's target list and group by clauses are extended\n * for the given inputs.\n *\n * The function walks over the input targetEntryList. For the entries\n * with aggregates in them, it calls the recursive aggregate walker function to\n * create aggregates for the worker nodes. For example, the avg() is sent to\n * the worker with two expressions count() and sum(). Thus, a single target entry\n * might end up with multiple expressions in the worker query.\n *\n * The function doesn't change the aggregates in the window functions and sends them\n * as-is. The reason is that Citus only supports pushing down window functions when\n * this is safe to do.\n *\n * The function also handles count distinct operator if it is used in repartition\n * subqueries or on non-partition columns (e.g., cannot be pushed down). Each\n * column in count distinct aggregate is added to target list, and group by\n * list of worker extended operator. This approach guarantees the distinctness\n * in the worker queries.\n *\n *     inputs: targetEntryList, extendedOpNodeProperties\n *     outputs: queryTargetList, queryGroupClause\n */\nstatic void\nProcessTargetListForWorkerQuery(List *targetEntryList,\n\t\t\t\t\t\t\t\tExtendedOpNodeProperties *extendedOpNodeProperties,\n\t\t\t\t\t\t\t\tQueryTargetList *queryTargetList,\n\t\t\t\t\t\t\t\tQueryGroupClause *queryGroupClause)\n{\n\tWorkerAggregateWalkerContext workerAggContext = {\n\t\t.extendedOpNodeProperties = extendedOpNodeProperties,\n\t};\n\n\t/* iterate over original target entries */\n\tTargetEntry *originalTargetEntry = NULL;\n\tforeach_declared_ptr(originalTargetEntry, targetEntryList)\n\t{\n\t\tExpr *originalExpression = originalTargetEntry->expr;\n\t\tList *newExpressionList = NIL;\n\n\t\t/* reset walker context */\n\t\tworkerAggContext.expressionList = NIL;\n\t\tworkerAggContext.createGroupByClause = false;\n\n\t\t/*\n\t\t * If we can push down the expression we copy the expression to the targetlist of the worker query.\n\t\t * Otherwise the expression is processed to be combined on the coordinator.\n\t\t */\n\t\tif (CanPushDownExpression((Node *) originalExpression, extendedOpNodeProperties))\n\t\t{\n\t\t\tnewExpressionList = list_make1(originalExpression);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tWorkerAggregateWalker((Node *) originalExpression, &workerAggContext);\n\n\t\t\tnewExpressionList = workerAggContext.expressionList;\n\t\t}\n\n\t\tExpandWorkerTargetEntry(newExpressionList, originalTargetEntry,\n\t\t\t\t\t\t\t\tworkerAggContext.createGroupByClause,\n\t\t\t\t\t\t\t\tqueryTargetList, queryGroupClause);\n\t}\n}\n\n\n/*\n * ProcessHavingClauseForWorkerQuery gets the inputs and modifies the outputs\n * such that the worker query's target list and group by clauses are extended\n * based on the inputs.\n *\n * The rule is that Citus always applies the HAVING clause on the\n * coordinator. Thus, it pulls the necessary data from the workers. Also, when the\n * having clause is safe to pushdown to the workers, workerHavingQual is set to\n * be the original having clause.\n *\n *     inputs: originalHavingQual, extendedOpNodeProperties\n *     outputs: workerHavingQual, queryTargetList, queryGroupClause\n */\nstatic void\nProcessHavingClauseForWorkerQuery(Node *originalHavingQual,\n\t\t\t\t\t\t\t\t  ExtendedOpNodeProperties *extendedOpNodeProperties,\n\t\t\t\t\t\t\t\t  Node **workerHavingQual,\n\t\t\t\t\t\t\t\t  QueryTargetList *queryTargetList,\n\t\t\t\t\t\t\t\t  QueryGroupClause *queryGroupClause)\n{\n\t*workerHavingQual = NULL;\n\n\tif (originalHavingQual == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tif (extendedOpNodeProperties->pushDownGroupingAndHaving)\n\t{\n\t\t/*\n\t\t * We converted the having expression to a list in subquery pushdown\n\t\t * planner. However, this query cannot be parsed as it is in the worker.\n\t\t * We should convert this back to being explicit for worker query\n\t\t * so that it can be parsed when it hits the standard planner in worker.\n\t\t */\n\t\tif (IsA(originalHavingQual, List))\n\t\t{\n\t\t\t*workerHavingQual =\n\t\t\t\t(Node *) make_ands_explicit((List *) originalHavingQual);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t*workerHavingQual = originalHavingQual;\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * If the GROUP BY or PARTITION BY is not on the distribution column\n\t\t * then we need to combine the aggregates in the HAVING across shards.\n\t\t */\n\t\tWorkerAggregateWalkerContext workerAggContext = {\n\t\t\t.extendedOpNodeProperties = extendedOpNodeProperties,\n\t\t};\n\n\t\tWorkerAggregateWalker(originalHavingQual, &workerAggContext);\n\t\tList *newExpressionList = workerAggContext.expressionList;\n\t\tTargetEntry *targetEntry = NULL;\n\n\t\tExpandWorkerTargetEntry(newExpressionList, targetEntry,\n\t\t\t\t\t\t\t\tworkerAggContext.createGroupByClause,\n\t\t\t\t\t\t\t\tqueryTargetList, queryGroupClause);\n\t}\n}\n\n\n/*\n * ProcessDistinctClauseForWorkerQuery gets the inputs and modifies the outputs\n * such that worker query's DISTINCT and DISTINCT ON clauses are set accordingly.\n * Note the function may or may not decide to pushdown the DISTINCT and DISTINCT\n * on clauses based on the inputs.\n *\n * See the detailed comments in the function for the rules of pushing down DISTINCT\n * and DISTINCT ON clauses to the worker queries.\n *\n * The function also sets distinctPreventsLimitPushdown. As the name reveals,\n * distinct could prevent pushing down LIMIT clauses later in the planning.\n * For the details, see the comments in the function.\n *\n *     inputs: distinctClause, hasDistinctOn, groupClauseList, queryHasAggregates\n *     outputs: queryDistinctClause, distinctPreventsLimitPushdown\n *\n */\nstatic void\nProcessDistinctClauseForWorkerQuery(List *distinctClause, bool hasDistinctOn,\n\t\t\t\t\t\t\t\t\tList *groupClauseList,\n\t\t\t\t\t\t\t\t\tbool queryHasAggregates,\n\t\t\t\t\t\t\t\t\tQueryDistinctClause *queryDistinctClause,\n\t\t\t\t\t\t\t\t\tbool *distinctPreventsLimitPushdown)\n{\n\t*distinctPreventsLimitPushdown = false;\n\n\tif (distinctClause == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tbool distinctClauseSupersetofGroupClause = false;\n\n\tif (groupClauseList == NIL ||\n\t\tIsGroupBySubsetOfDistinct(groupClauseList, distinctClause))\n\t{\n\t\tdistinctClauseSupersetofGroupClause = true;\n\t}\n\telse\n\t{\n\t\tdistinctClauseSupersetofGroupClause = false;\n\n\t\t/*\n\t\t * GROUP BY being a subset of DISTINCT guarantees the\n\t\t * distinctness on the workers. Otherwise, pushing down\n\t\t * LIMIT might cause missing the necessary data from\n\t\t * the worker query\n\t\t */\n\t\t*distinctPreventsLimitPushdown = true;\n\t}\n\n\t/*\n\t * Distinct is pushed down to worker query only if the query does not\n\t * contain an aggregate in which master processing might be required to\n\t * complete the final result before distinct operation. We also prevent\n\t * distinct pushdown if distinct clause is missing some entries that\n\t * group by clause has.\n\t */\n\tbool shouldPushdownDistinct = !queryHasAggregates &&\n\t\t\t\t\t\t\t\t  distinctClauseSupersetofGroupClause;\n\tif (shouldPushdownDistinct)\n\t{\n\t\tqueryDistinctClause->workerDistinctClause = distinctClause;\n\t\tqueryDistinctClause->workerHasDistinctOn = hasDistinctOn;\n\t}\n}\n\n\n/*\n * ProcessWindowFunctionsForWorkerQuery gets the inputs and modifies the outputs such\n * that worker query's workerWindowClauseList is set when the window clauses are safe to\n * pushdown.\n *\n * Note that even though Citus only pushes down the window functions, it may need to\n * modify the target list of the worker query when the window function refers to\n * an avg(). The reason is that any aggregate which is also referred by other\n * target entries would be mutated by Citus. Thus, we add a copy of the same aggregate\n * to the worker target list to make sure that the window function refers to the\n * non-mutated aggregate.\n *\n *     inputs: windowClauseList, originalTargetEntryList\n *     outputs: queryWindowClause, queryTargetList\n *\n */\nstatic void\nProcessWindowFunctionsForWorkerQuery(List *windowClauseList,\n\t\t\t\t\t\t\t\t\t List *originalTargetEntryList,\n\t\t\t\t\t\t\t\t\t QueryWindowClause *queryWindowClause,\n\t\t\t\t\t\t\t\t\t QueryTargetList *queryTargetList)\n{\n\tif (windowClauseList == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tqueryWindowClause->workerWindowClauseList = windowClauseList;\n\tqueryWindowClause->hasWindowFunctions = true;\n}\n\n\n/* ProcessWindowFunctionPullUpForWorkerQuery pulls up inputs for window functions */\nstatic void\nProcessWindowFunctionPullUpForWorkerQuery(List *windowClause,\n\t\t\t\t\t\t\t\t\t\t  QueryTargetList *queryTargetList)\n{\n\tif (windowClause != NIL)\n\t{\n\t\tList *columnList = pull_var_clause_default((Node *) windowClause);\n\n\t\tExpr *newExpression = NULL;\n\t\tforeach_declared_ptr(newExpression, columnList)\n\t\t{\n\t\t\tTargetEntry *newTargetEntry = makeNode(TargetEntry);\n\n\t\t\tnewTargetEntry->expr = newExpression;\n\n\t\t\tnewTargetEntry->resname =\n\t\t\t\tWorkerColumnName(queryTargetList->targetProjectionNumber);\n\n\t\t\t/* force resjunk to false as we may need this on the master */\n\t\t\tnewTargetEntry->resjunk = false;\n\t\t\tnewTargetEntry->resno = queryTargetList->targetProjectionNumber;\n\n\t\t\tqueryTargetList->targetEntryList =\n\t\t\t\tlappend(queryTargetList->targetEntryList, newTargetEntry);\n\t\t\tqueryTargetList->targetProjectionNumber++;\n\t\t}\n\t}\n}\n\n\n/*\n * ProcessLimitOrderByForWorkerQuery gets the inputs and modifies the outputs\n * such that worker query's LIMIT and ORDER BY clauses are set accordingly.\n * Adding entries to ORDER BY might trigger adding new entries to newTargetEntryList.\n * See GenerateNewTargetEntriesForSortClauses() for the details.\n *\n * For the decisions on whether and how to pushdown LIMIT and ORDER BY are documented\n * in the functions that are called from this function.\n *\n *     inputs: sortLimitReference, originalLimitCount, limitOffset,\n *             sortClauseList, groupClauseList, originalTargetList\n *     outputs: queryOrderByLimit, queryTargetList\n */\nstatic void\nProcessLimitOrderByForWorkerQuery(OrderByLimitReference orderByLimitReference,\n\t\t\t\t\t\t\t\t  Node *originalLimitCount, Node *limitOffset,\n\t\t\t\t\t\t\t\t  List *sortClauseList, List *groupClauseList,\n\t\t\t\t\t\t\t\t  List *originalTargetList,\n\t\t\t\t\t\t\t\t  QueryOrderByLimit *queryOrderByLimit,\n\t\t\t\t\t\t\t\t  QueryTargetList *queryTargetList)\n{\n\tqueryOrderByLimit->workerLimitCount =\n\t\tWorkerLimitCount(originalLimitCount, limitOffset, orderByLimitReference);\n\n\tqueryOrderByLimit->workerSortClauseList =\n\t\tWorkerSortClauseList(originalLimitCount,\n\t\t\t\t\t\t\t groupClauseList,\n\t\t\t\t\t\t\t sortClauseList,\n\t\t\t\t\t\t\t orderByLimitReference);\n}\n\n\n/*\n * BuildOrderByLimitReference is a helper function that simply builds\n * the necessary information for processing the limit and order by.\n * The return value should be used in a read-only manner.\n */\nstatic OrderByLimitReference\nBuildOrderByLimitReference(bool hasDistinctOn, bool groupedByDisjointPartitionColumn,\n\t\t\t\t\t\t   bool onlyPushableWindowFunctions,\n\t\t\t\t\t\t   List *groupClause, List *sortClauseList, List *targetList)\n{\n\tOrderByLimitReference limitOrderByReference;\n\n\tlimitOrderByReference.groupedByDisjointPartitionColumn =\n\t\tgroupedByDisjointPartitionColumn;\n\tlimitOrderByReference.onlyPushableWindowFunctions =\n\t\tonlyPushableWindowFunctions;\n\tlimitOrderByReference.hasDistinctOn = hasDistinctOn;\n\tlimitOrderByReference.groupClauseIsEmpty = (groupClause == NIL);\n\tlimitOrderByReference.sortClauseIsEmpty = (sortClauseList == NIL);\n\tlimitOrderByReference.canApproximate =\n\t\tCanPushDownLimitApproximate(sortClauseList, targetList);\n\tlimitOrderByReference.hasOrderByAggregate =\n\t\tHasOrderByAggregate(sortClauseList, targetList);\n\n\treturn limitOrderByReference;\n}\n\n\n/*\n * TargetListHasAggregates returns true if any of the elements in the\n * target list contain aggregates that are not inside the window functions.\n * This function should not be called if window functions are being pulled up.\n */\nbool\nTargetListHasAggregates(List *targetEntryList)\n{\n\tTargetEntry *targetEntry = NULL;\n\tforeach_declared_ptr(targetEntry, targetEntryList)\n\t{\n\t\tExpr *targetExpr = targetEntry->expr;\n\t\tbool hasAggregates = contain_aggs_of_level((Node *) targetExpr, 0);\n\t\tbool hasWindowFunction = contain_window_function((Node *) targetExpr);\n\n\t\t/*\n\t\t * If the expression uses aggregates inside window function contain agg\n\t\t * clause still returns true. We want to make sure it is not a part of\n\t\t * window function before we proceed.\n\t\t */\n\t\tif (hasAggregates && !hasWindowFunction)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ExpandWorkerTargetEntry is a utility function which processes the\n * expressions that are intended to be added to the worker target list.\n *\n * In summary, the function gets a list of expressions, converts them to target\n * entries and updates all the necessary fields such that the expression is correctly\n * added to the worker query's target list.\n *\n * Inputs:\n *  - expressionList: The list of expressions that should be added to the worker query's\n *                    target list.\n *  - originalTargetEntry: Target entry that the expressionList generated for. NULL\n *                         if the expressionList is not generated from any target entry.\n *  - addToGroupByClause: True if the expressionList should also be added to the\n *                        worker query's GROUP BY clause.\n */\nstatic void\nExpandWorkerTargetEntry(List *expressionList, TargetEntry *originalTargetEntry,\n\t\t\t\t\t\tbool addToGroupByClause, QueryTargetList *queryTargetList,\n\t\t\t\t\t\tQueryGroupClause *queryGroupClause)\n{\n\t/* now create target entries for each new expression */\n\tExpr *newExpression = NULL;\n\tforeach_declared_ptr(newExpression, expressionList)\n\t{\n\t\t/* generate and add the new target entry to the target list */\n\t\tTargetEntry *newTargetEntry =\n\t\t\tGenerateWorkerTargetEntry(originalTargetEntry, newExpression,\n\t\t\t\t\t\t\t\t\t  queryTargetList->targetProjectionNumber);\n\t\tqueryTargetList->targetProjectionNumber++;\n\t\tqueryTargetList->targetEntryList =\n\t\t\tlappend(queryTargetList->targetEntryList, newTargetEntry);\n\n\t\t/*\n\t\t * Detect new targets of type Var and add it to group clause list.\n\t\t * This case is expected only if the target entry has aggregates and\n\t\t * it is inside a repartitioned subquery. We create group by entry\n\t\t * for each Var in target list. This code does not check if this\n\t\t * Var was already in the target list or in group by clauses.\n\t\t */\n\t\tif (IsA(newExpression, Var) && addToGroupByClause)\n\t\t{\n\t\t\tAppendTargetEntryToGroupClause(newTargetEntry, queryGroupClause);\n\t\t}\n\t}\n}\n\n\n/*\n * GetNextSortGroupRef gets a target list entry and returns\n * the next ressortgroupref that should be used based on the\n * input target list.\n */\nstatic Index\nGetNextSortGroupRef(List *targetEntryList)\n{\n\tIndex nextSortGroupRefIndex = 0;\n\n\t/* find max of sort group ref index */\n\tTargetEntry *targetEntry = NULL;\n\tforeach_declared_ptr(targetEntry, targetEntryList)\n\t{\n\t\tif (targetEntry->ressortgroupref > nextSortGroupRefIndex)\n\t\t{\n\t\t\tnextSortGroupRefIndex = targetEntry->ressortgroupref;\n\t\t}\n\t}\n\n\t/* next group ref index starts from max group ref index + 1 */\n\tnextSortGroupRefIndex++;\n\n\treturn nextSortGroupRefIndex;\n}\n\n\n/*\n * GenerateWorkerTargetEntry is a simple utility function which gets a\n * target entry, an expression and a targetProjectionNumber.\n *\n * The function returns a newly allocated target entry which can be added\n * to the worker's target list.\n */\nstatic TargetEntry *\nGenerateWorkerTargetEntry(TargetEntry *targetEntry, Expr *workerExpression,\n\t\t\t\t\t\t  AttrNumber targetProjectionNumber)\n{\n\tTargetEntry *newTargetEntry = NULL;\n\n\t/*\n\t * If a target entry is already provided, use a copy of\n\t * it because some of the callers rely on resorigtbl and\n\t * resorigcol.\n\t */\n\tif (targetEntry)\n\t{\n\t\tnewTargetEntry = flatCopyTargetEntry(targetEntry);\n\t}\n\telse\n\t{\n\t\tnewTargetEntry = makeNode(TargetEntry);\n\t}\n\n\tif (newTargetEntry->resname == NULL)\n\t{\n\t\tnewTargetEntry->resname = WorkerColumnName(targetProjectionNumber);\n\t}\n\n\t/* we can't generate a target entry without an expression */\n\tAssert(workerExpression != NULL);\n\n\t/* force resjunk to false as we may need this on the master */\n\tnewTargetEntry->expr = workerExpression;\n\tnewTargetEntry->resjunk = false;\n\tnewTargetEntry->resno = targetProjectionNumber;\n\n\treturn newTargetEntry;\n}\n\n\n/*\n * AppendTargetEntryToGroupClause gets a target entry, pointer to group list\n * and the ressortgroupref index.\n *\n * The function modifies all of the three input such that the target entry is\n * appended to the group clause and the index is incremented by one.\n */\nstatic void\nAppendTargetEntryToGroupClause(TargetEntry *targetEntry,\n\t\t\t\t\t\t\t   QueryGroupClause *queryGroupClause)\n{\n\tExpr *targetExpr PG_USED_FOR_ASSERTS_ONLY = targetEntry->expr;\n\n\t/* we currently only support appending Var target entries */\n\tAssert(IsA(targetExpr, Var));\n\n\tVar *targetColumn = (Var *) targetEntry->expr;\n\tSortGroupClause *groupByClause = CreateSortGroupClause(targetColumn);\n\n\t/* the target entry should have an index */\n\ttargetEntry->ressortgroupref = *queryGroupClause->nextSortGroupRefIndex;\n\n\t/* the group by clause entry should point to the correct index in the target list */\n\tgroupByClause->tleSortGroupRef = *queryGroupClause->nextSortGroupRefIndex;\n\n\t/* update the group by list and the index's value */\n\tqueryGroupClause->groupClauseList =\n\t\tlappend(queryGroupClause->groupClauseList, groupByClause);\n\t(*queryGroupClause->nextSortGroupRefIndex)++;\n}\n\n\n/*\n * WorkerAggregateWalker walks over the original target entry expression, and\n * creates the list of expression trees (potentially more than one) to execute\n * on the worker nodes. The function creates new expressions for aggregates and\n * columns; and recurses into expression_tree_walker() for all other expression\n * types.\n */\nstatic bool\nWorkerAggregateWalker(Node *node, WorkerAggregateWalkerContext *walkerContext)\n{\n\tbool walkerResult = false;\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Aggref))\n\t{\n\t\tif (CanPushDownExpression(node, walkerContext->extendedOpNodeProperties))\n\t\t{\n\t\t\twalkerContext->expressionList = lappend(walkerContext->expressionList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tnode);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tAggref *originalAggregate = (Aggref *) node;\n\t\t\tList *workerAggregateList = WorkerAggregateExpressionList(originalAggregate,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  walkerContext);\n\n\t\t\twalkerContext->expressionList = list_concat(walkerContext->expressionList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerAggregateList);\n\t\t}\n\t}\n\telse if (IsA(node, Var))\n\t{\n\t\tVar *originalColumn = (Var *) node;\n\t\twalkerContext->expressionList = lappend(walkerContext->expressionList,\n\t\t\t\t\t\t\t\t\t\t\t\toriginalColumn);\n\t}\n\telse\n\t{\n\t\twalkerResult = expression_tree_walker(node, WorkerAggregateWalker,\n\t\t\t\t\t\t\t\t\t\t\t  (void *) walkerContext);\n\t}\n\n\treturn walkerResult;\n}\n\n\n/*\n * WorkerAggregateExpressionList takes in the original aggregate function, and\n * determines the transformed aggregate functions to execute on worker nodes.\n * The function then returns these aggregates in a list. It also creates\n * group by clauses for newly added targets to be placed in the extended operator\n * node.\n */\nstatic List *\nWorkerAggregateExpressionList(Aggref *originalAggregate,\n\t\t\t\t\t\t\t  WorkerAggregateWalkerContext *walkerContext)\n{\n\tList *workerAggregateList = NIL;\n\n\tif (walkerContext->extendedOpNodeProperties->pullUpIntermediateRows)\n\t{\n\t\tTargetEntry *targetEntry;\n\t\tforeach_declared_ptr(targetEntry, originalAggregate->args)\n\t\t{\n\t\t\tworkerAggregateList = lappend(workerAggregateList, targetEntry->expr);\n\t\t}\n\n\t\tExpr *directarg;\n\t\tforeach_declared_ptr(directarg, originalAggregate->aggdirectargs)\n\t\t{\n\t\t\t/*\n\t\t\t * The worker aggregation should execute any node that contains any\n\t\t\t * Var nodes and return the result in the targetlist, so that the\n\t\t\t * combine query can then fetch the result via remote scan; see\n\t\t\t * MasterAggregateExpression.\n\t\t\t */\n\t\t\tif (pull_var_clause_default((Node *) directarg) != NIL)\n\t\t\t{\n\t\t\t\tworkerAggregateList = lappend(workerAggregateList, directarg);\n\t\t\t}\n\t\t}\n\n\t\tif (originalAggregate->aggfilter)\n\t\t{\n\t\t\tworkerAggregateList = lappend(workerAggregateList,\n\t\t\t\t\t\t\t\t\t\t  originalAggregate->aggfilter);\n\t\t}\n\n\t\treturn workerAggregateList;\n\t}\n\n\tAggregateType aggregateType = GetAggregateType(originalAggregate);\n\n\tif (aggregateType == AGGREGATE_COUNT && originalAggregate->aggdistinct &&\n\t\tCountDistinctErrorRate == DISABLE_DISTINCT_APPROXIMATION &&\n\t\twalkerContext->extendedOpNodeProperties->pullDistinctColumns)\n\t{\n\t\tAggref *aggregate = (Aggref *) copyObject(originalAggregate);\n\t\tList *columnList = pull_var_clause_default((Node *) aggregate);\n\n\t\tVar *column = NULL;\n\t\tforeach_declared_ptr(column, columnList)\n\t\t{\n\t\t\tworkerAggregateList = list_append_unique(workerAggregateList, column);\n\t\t}\n\n\t\twalkerContext->createGroupByClause = true;\n\t}\n\telse if (aggregateType == AGGREGATE_COUNT && originalAggregate->aggdistinct &&\n\t\t\t CountDistinctErrorRate != DISABLE_DISTINCT_APPROXIMATION)\n\t{\n\t\t/*\n\t\t * If the original aggregate is a count(distinct) approximation, we want\n\t\t * to compute hll_add_agg(hll_hash(var), storageSize) on worker nodes.\n\t\t */\n\t\tconst AttrNumber firstArgumentId = 1;\n\t\tconst AttrNumber secondArgumentId = 2;\n\t\tconst int hashArgumentCount = 2;\n\t\tconst int addArgumentCount = 2;\n\n\n\t\t/* init hll_hash() related variables */\n\t\tOid argumentType = AggregateArgumentType(originalAggregate);\n\t\tTargetEntry *argument = (TargetEntry *) linitial(originalAggregate->args);\n\t\tExpr *argumentExpression = copyObject(argument->expr);\n\n\t\t/* extract schema name of hll */\n\t\tOid hllId = get_extension_oid(HLL_EXTENSION_NAME, false);\n\t\tOid hllSchemaOid = get_extension_schema(hllId);\n\t\tconst char *hllSchemaName = get_namespace_name(hllSchemaOid);\n\n\t\tconst char *hashFunctionName = CountDistinctHashFunctionName(argumentType);\n\t\tOid hashFunctionId = FunctionOid(hllSchemaName, hashFunctionName,\n\t\t\t\t\t\t\t\t\t\t hashArgumentCount);\n\t\tOid hashFunctionReturnType = get_func_rettype(hashFunctionId);\n\n\t\t/* init hll_add_agg() related variables */\n\t\tOid addFunctionId = FunctionOid(hllSchemaName, HLL_ADD_AGGREGATE_NAME,\n\t\t\t\t\t\t\t\t\t\taddArgumentCount);\n\t\tOid hllType = TypeOid(hllSchemaOid, HLL_TYPE_NAME);\n\t\tint logOfStorageSize = CountDistinctStorageSize(CountDistinctErrorRate);\n\t\tConst *logOfStorageSizeConst = MakeIntegerConst(logOfStorageSize);\n\n\t\t/* construct hll_hash() expression */\n\t\tFuncExpr *hashFunction = makeNode(FuncExpr);\n\t\thashFunction->funcid = hashFunctionId;\n\t\thashFunction->funcresulttype = hashFunctionReturnType;\n\t\thashFunction->args = list_make1(argumentExpression);\n\n\t\t/* construct hll_add_agg() expression */\n\t\tTargetEntry *hashedColumnArgument = makeTargetEntry((Expr *) hashFunction,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfirstArgumentId, NULL, false);\n\t\tTargetEntry *storageSizeArgument = makeTargetEntry((Expr *) logOfStorageSizeConst,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   secondArgumentId, NULL, false);\n\t\tList *addAggregateArgumentList = list_make2(hashedColumnArgument,\n\t\t\t\t\t\t\t\t\t\t\t\t\tstorageSizeArgument);\n\n\t\tAggref *addAggregateFunction = makeNode(Aggref);\n\t\taddAggregateFunction->aggfnoid = addFunctionId;\n\t\taddAggregateFunction->aggtype = hllType;\n\t\taddAggregateFunction->args = addAggregateArgumentList;\n\t\taddAggregateFunction->aggkind = AGGKIND_NORMAL;\n\t\taddAggregateFunction->aggfilter = (Expr *) copyObject(\n\t\t\toriginalAggregate->aggfilter);\n\n\t\tworkerAggregateList = lappend(workerAggregateList, addAggregateFunction);\n\t}\n\telse if (aggregateType == AGGREGATE_AVERAGE)\n\t{\n\t\t/*\n\t\t * If the original aggregate is an average, we want to compute sum(var)\n\t\t * and count(var) on worker nodes.\n\t\t */\n\t\tAggref *sumAggregate = copyObject(originalAggregate);\n\t\tAggref *countAggregate = copyObject(originalAggregate);\n\n\t\t/* extract function names for sum and count */\n\t\tconst char *sumAggregateName = AggregateNames[AGGREGATE_SUM];\n\t\tconst char *countAggregateName = AggregateNames[AGGREGATE_COUNT];\n\n\t\t/*\n\t\t * Find the type of the expression over which we execute the aggregate.\n\t\t * We then need to find the right sum function for that type.\n\t\t */\n\t\tOid argumentType = AggregateArgumentType(originalAggregate);\n\n\t\t/* find function implementing sum over the original type */\n\t\tsumAggregate->aggfnoid = AggregateFunctionOid(sumAggregateName, argumentType);\n\t\tsumAggregate->aggtype = get_func_rettype(sumAggregate->aggfnoid);\n\n\t\tsumAggregate->aggtranstype = InvalidOid;\n\t\tsumAggregate->aggargtypes = list_make1_oid(argumentType);\n\t\tsumAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\t/* count has any input type */\n\t\tcountAggregate->aggfnoid = AggregateFunctionOid(countAggregateName, ANYOID);\n\t\tcountAggregate->aggtype = get_func_rettype(countAggregate->aggfnoid);\n\t\tcountAggregate->aggtranstype = InvalidOid;\n\t\tcountAggregate->aggargtypes = list_make1_oid(argumentType);\n\t\tcountAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\tworkerAggregateList = lappend(workerAggregateList, sumAggregate);\n\t\tworkerAggregateList = lappend(workerAggregateList, countAggregate);\n\t}\n\telse if (aggregateType == AGGREGATE_TDIGEST_PERCENTILE_ADD_DOUBLE ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_ADD_DOUBLEARRAY ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_OF_ADD_DOUBLE ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_OF_ADD_DOUBLEARRAY)\n\t{\n\t\t/*\n\t\t * The original query has an aggregate in the form of either\n\t\t *  - tdigest_percentile(column, compression, quantile)\n\t\t *  - tdigest_percentile(column, compression, quantile[])\n\t\t *  - tdigest_percentile_of(column, compression, value)\n\t\t *  - tdigest_percentile_of(column, compression, value[])\n\t\t *\n\t\t * We are creating the worker part of this query by creating a\n\t\t *  - tdigest(column, compression)\n\t\t *\n\t\t * One could see we are passing argument 0 and argument 1 from the original query\n\t\t * in here. This corresponds with the list_nth calls in the args and aggargstypes\n\t\t * list construction. The tdigest function and type are read from the catalog.\n\t\t */\n\t\tAggref *newWorkerAggregate = copyObject(originalAggregate);\n\t\tnewWorkerAggregate->aggfnoid = TDigestExtensionAggTDigest2();\n\t\tnewWorkerAggregate->aggtype = TDigestExtensionTypeOid();\n\t\tnewWorkerAggregate->args = list_make2(\n\t\t\tlist_nth(newWorkerAggregate->args, 0),\n\t\t\tlist_nth(newWorkerAggregate->args, 1));\n\t\tnewWorkerAggregate->aggkind = AGGKIND_NORMAL;\n\t\tnewWorkerAggregate->aggtranstype = InvalidOid;\n\t\tnewWorkerAggregate->aggargtypes = list_make2_oid(\n\t\t\tlist_nth_oid(newWorkerAggregate->aggargtypes, 0),\n\t\t\tlist_nth_oid(newWorkerAggregate->aggargtypes, 1));\n\t\tnewWorkerAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\tworkerAggregateList = lappend(workerAggregateList, newWorkerAggregate);\n\t}\n\telse if (aggregateType == AGGREGATE_TDIGEST_PERCENTILE_TDIGEST_DOUBLE ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_TDIGEST_DOUBLEARRAY ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_OF_TDIGEST_DOUBLE ||\n\t\t\t aggregateType == AGGREGATE_TDIGEST_PERCENTILE_OF_TDIGEST_DOUBLEARRAY)\n\t{\n\t\t/*\n\t\t * The original query has an aggregate in the form of either\n\t\t *  - tdigest_percentile(tdigest, quantile)\n\t\t *  - tdigest_percentile(tdigest, quantile[])\n\t\t *  - tdigest_percentile_of(tdigest, value)\n\t\t *  - tdigest_percentile_of(tdigest, value[])\n\t\t *\n\t\t * We are creating the worker part of this query by creating a\n\t\t *  - tdigest(tdigest)\n\t\t *\n\t\t * One could see we are passing argument 0 from the original query in here. This\n\t\t * corresponds with the list_nth calls in the args and aggargstypes list\n\t\t * construction. The tdigest function and type are read from the catalog.\n\t\t */\n\t\tAggref *newWorkerAggregate = copyObject(originalAggregate);\n\t\tnewWorkerAggregate->aggfnoid = TDigestExtensionAggTDigest1();\n\t\tnewWorkerAggregate->aggtype = TDigestExtensionTypeOid();\n\t\tnewWorkerAggregate->args = list_make1(list_nth(newWorkerAggregate->args, 0));\n\t\tnewWorkerAggregate->aggkind = AGGKIND_NORMAL;\n\t\tnewWorkerAggregate->aggtranstype = InvalidOid;\n\t\tnewWorkerAggregate->aggargtypes = list_make1_oid(\n\t\t\tlist_nth_oid(newWorkerAggregate->aggargtypes, 0));\n\t\tnewWorkerAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\tworkerAggregateList = lappend(workerAggregateList, newWorkerAggregate);\n\t}\n\telse if (aggregateType == AGGREGATE_CUSTOM_COMBINE)\n\t{\n\t\tHeapTuple aggTuple =\n\t\t\tSearchSysCache1(AGGFNOID, ObjectIdGetDatum(originalAggregate->aggfnoid));\n\t\tForm_pg_aggregate aggform;\n\t\tOid combine;\n\t\tbool useBinaryWorkerAggregate = false;\n\n\t\tif (!HeapTupleIsValid(aggTuple))\n\t\t{\n\t\t\telog(ERROR, \"citus cache lookup failed for aggregate %u\",\n\t\t\t\t originalAggregate->aggfnoid);\n\t\t\treturn NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\taggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);\n\t\t\tcombine = aggform->aggcombinefn;\n\t\t\tuseBinaryWorkerAggregate = (OidIsValid(aggform->aggtranstype) &&\n\t\t\t\t\t\t\t\t\t\tIsAggTransTypeBinarySerializable(aggform));\n\n\t\t\tReleaseSysCache(aggTuple);\n\t\t}\n\n\t\tif (combine != InvalidOid)\n\t\t{\n\t\t\tConst *aggOidParam = makeConst(REGPROCEDUREOID, -1, InvalidOid, sizeof(Oid),\n\t\t\t\t\t\t\t\t\t\t   ObjectIdGetDatum(originalAggregate->aggfnoid),\n\t\t\t\t\t\t\t\t\t\t   false, true);\n\n\t\t\tList *newWorkerAggregateArgs =\n\t\t\t\tlist_make1(makeTargetEntry((Expr *) aggOidParam, 1, NULL, false));\n\n\t\t\tif (list_length(originalAggregate->args) == 1)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Single argument case, append 'arg' to worker_partial_agg(agg, arg).\n\t\t\t\t * We don't wrap single argument in a row expression because\n\t\t\t\t * it has performance implications to unwrap arguments on each\n\t\t\t\t * SFUNC invocation.\n\t\t\t\t */\n\t\t\t\tTargetEntry *newArg =\n\t\t\t\t\tcopyObject((TargetEntry *) linitial(originalAggregate->args));\n\t\t\t\tnewArg->resno++;\n\t\t\t\tnewWorkerAggregateArgs = lappend(newWorkerAggregateArgs, newArg);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Aggregation on workers assumes a single aggregation parameter.\n\t\t\t\t * To still be able to handle multiple parameters, we combine\n\t\t\t\t * parameters into a single row expression, i.e., append 'ROW(...args)'\n\t\t\t\t * to worker_partial_agg(agg, ROW(...args)).\n\t\t\t\t */\n\t\t\t\tRowExpr *rowExpr = makeNode(RowExpr);\n\t\t\t\trowExpr->row_typeid = RECORDOID;\n\t\t\t\trowExpr->row_format = COERCE_EXPLICIT_CALL;\n\t\t\t\trowExpr->location = -1;\n\t\t\t\trowExpr->colnames = NIL;\n\n\t\t\t\tTargetEntry *arg = NULL;\n\t\t\t\tforeach_declared_ptr(arg, originalAggregate->args)\n\t\t\t\t{\n\t\t\t\t\trowExpr->args = lappend(rowExpr->args, copyObject(arg->expr));\n\t\t\t\t}\n\n\t\t\t\tnewWorkerAggregateArgs =\n\t\t\t\t\tlappend(newWorkerAggregateArgs,\n\t\t\t\t\t\t\tmakeTargetEntry((Expr *) rowExpr, 2, NULL, false));\n\t\t\t}\n\n\t\t\t/* worker_partial_agg(agg, arg) or worker_partial_agg(agg, ROW(...args)) */\n\t\t\tAggref *newWorkerAggregate = copyObject(originalAggregate);\n\n\t\t\tif (useBinaryWorkerAggregate)\n\t\t\t{\n\t\t\t\tnewWorkerAggregate->aggfnoid = WorkerBinaryPartialAggOid();\n\t\t\t\tnewWorkerAggregate->aggtype = BYTEAOID;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnewWorkerAggregate->aggfnoid = WorkerPartialAggOid();\n\t\t\t\tnewWorkerAggregate->aggtype = CSTRINGOID;\n\t\t\t}\n\n\t\t\tnewWorkerAggregate->args = newWorkerAggregateArgs;\n\t\t\tnewWorkerAggregate->aggkind = AGGKIND_NORMAL;\n\t\t\tnewWorkerAggregate->aggtranstype = INTERNALOID;\n\t\t\tnewWorkerAggregate->aggargtypes = lcons_oid(OIDOID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewWorkerAggregate->aggargtypes);\n\t\t\tnewWorkerAggregate->aggsplit = AGGSPLIT_SIMPLE;\n\n\t\t\tworkerAggregateList = list_make1(newWorkerAggregate);\n\t\t}\n\t\telse\n\t\t{\n\t\t\telog(ERROR, \"Aggregate lacks COMBINEFUNC\");\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * All other aggregates are sent as they are to the worker nodes.\n\t\t */\n\t\tAggref *workerAggregate = copyObject(originalAggregate);\n\t\tworkerAggregateList = lappend(workerAggregateList, workerAggregate);\n\t}\n\n\treturn workerAggregateList;\n}\n\n\n/*\n * GetAggregateType scans pg_catalog.pg_proc for the given aggregate oid, and\n * finds the aggregate's name. The function then matches the aggregate's name to\n * previously stored strings, and returns the appropriate aggregate type.\n */\nstatic AggregateType\nGetAggregateType(Aggref *aggregateExpression)\n{\n\tOid aggFunctionId = aggregateExpression->aggfnoid;\n\n\t/* custom aggregates with combine func take precedence over name-based logic */\n\tif (aggFunctionId >= FirstNormalObjectId &&\n\t\tAggregateEnabledCustom(aggregateExpression))\n\t{\n\t\treturn AGGREGATE_CUSTOM_COMBINE;\n\t}\n\n\t/* look up the function name */\n\tchar *aggregateProcName = get_func_name(aggFunctionId);\n\tif (aggregateProcName == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"citus cache lookup failed for function %u\",\n\t\t\t\t\t\t\t   aggFunctionId)));\n\t}\n\n\tuint32 aggregateCount = lengthof(AggregateNames);\n\n\tfor (uint32 aggregateIndex = 1; aggregateIndex < aggregateCount; aggregateIndex++)\n\t{\n\t\tconst char *aggregateName = AggregateNames[aggregateIndex];\n\t\tif (strncmp(aggregateName, aggregateProcName, NAMEDATALEN) == 0)\n\t\t{\n\t\t\treturn aggregateIndex;\n\t\t}\n\t}\n\n\t/*\n\t * All functions from github.com/tvondra/tdigest start with the \"tdigest\" prefix.\n\t * Since it requires lookups of function names in a schema we would like to only\n\t * perform these checks if there is some chance it will actually result in a positive\n\t * hit.\n\t */\n\tif (StringStartsWith(aggregateProcName, \"tdigest\"))\n\t{\n\t\tif (aggFunctionId == TDigestExtensionAggTDigest1())\n\t\t{\n\t\t\treturn AGGREGATE_TDIGEST_COMBINE;\n\t\t}\n\n\t\tif (aggFunctionId == TDigestExtensionAggTDigest2())\n\t\t{\n\t\t\treturn AGGREGATE_TDIGEST_ADD_DOUBLE;\n\t\t}\n\n\t\tif (aggFunctionId == TDigestExtensionAggTDigestPercentile3())\n\t\t{\n\t\t\treturn AGGREGATE_TDIGEST_PERCENTILE_ADD_DOUBLE;\n\t\t}\n\n\t\tif (aggFunctionId == TDigestExtensionAggTDigestPercentile3a())\n\t\t{\n\t\t\treturn AGGREGATE_TDIGEST_PERCENTILE_ADD_DOUBLEARRAY;\n\t\t}\n\n\t\tif (aggFunctionId == TDigestExtensionAggTDigestPercentile2())\n\t\t{\n\t\t\treturn AGGREGATE_TDIGEST_PERCENTILE_TDIGEST_DOUBLE;\n\t\t}\n\n\t\tif (aggFunctionId == TDigestExtensionAggTDigestPercentile2a())\n\t\t{\n\t\t\treturn AGGREGATE_TDIGEST_PERCENTILE_TDIGEST_DOUBLEARRAY;\n\t\t}\n\n\t\tif (aggFunctionId == TDigestExtensionAggTDigestPercentileOf3())\n\t\t{\n\t\t\treturn AGGREGATE_TDIGEST_PERCENTILE_OF_ADD_DOUBLE;\n\t\t}\n\n\t\tif (aggFunctionId == TDigestExtensionAggTDigestPercentileOf3a())\n\t\t{\n\t\t\treturn AGGREGATE_TDIGEST_PERCENTILE_OF_ADD_DOUBLEARRAY;\n\t\t}\n\n\t\tif (aggFunctionId == TDigestExtensionAggTDigestPercentileOf2())\n\t\t{\n\t\t\treturn AGGREGATE_TDIGEST_PERCENTILE_OF_TDIGEST_DOUBLE;\n\t\t}\n\n\t\tif (aggFunctionId == TDigestExtensionAggTDigestPercentileOf2a())\n\t\t{\n\t\t\treturn AGGREGATE_TDIGEST_PERCENTILE_OF_TDIGEST_DOUBLEARRAY;\n\t\t}\n\t}\n\n\t/* handle any remaining built-in aggregates with a suitable combinefn */\n\tif (AggregateEnabledCustom(aggregateExpression))\n\t{\n\t\treturn AGGREGATE_CUSTOM_COMBINE;\n\t}\n\n\tif (CoordinatorAggregationStrategy == COORDINATOR_AGGREGATION_DISABLED)\n\t{\n\t\tereport(ERROR, (errmsg(\"unsupported aggregate function %s\", aggregateProcName)));\n\t}\n\telse\n\t{\n\t\treturn AGGREGATE_CUSTOM_ROW_GATHER;\n\t}\n}\n\n\n/* Extracts the type of the argument over which the aggregate is operating. */\nstatic Oid\nAggregateArgumentType(Aggref *aggregate)\n{\n\tList *argumentList = aggregate->args;\n\tTargetEntry *argument = (TargetEntry *) linitial(argumentList);\n\tOid returnTypeId = exprType((Node *) argument->expr);\n\n\t/* Here we currently support aggregates with only one argument; assert that. */\n\tAssert(list_length(argumentList) == 1);\n\n\treturn returnTypeId;\n}\n\n\n/*\n * FirstAggregateArgument returns the first argument of the aggregate.\n */\nstatic Expr *\nFirstAggregateArgument(Aggref *aggregate)\n{\n\tList *argumentList = aggregate->args;\n\n\tAssert(list_length(argumentList) >= 1);\n\n\tTargetEntry *argument = (TargetEntry *) linitial(argumentList);\n\n\treturn argument->expr;\n}\n\n\n/*\n * AggregateEnabledCustom returns whether given aggregate can be\n * distributed across workers using worker_partial_agg & coord_combine_agg.\n */\nstatic bool\nAggregateEnabledCustom(Aggref *aggregateExpression)\n{\n\tif (aggregateExpression->aggorder != NIL ||\n\t\tlist_length(aggregateExpression->args) == 0)\n\t{\n\t\treturn false;\n\t}\n\n\tOid aggregateOid = aggregateExpression->aggfnoid;\n\tHeapTuple aggTuple = SearchSysCache1(AGGFNOID, aggregateOid);\n\tif (!HeapTupleIsValid(aggTuple))\n\t{\n\t\telog(ERROR, \"citus cache lookup failed.\");\n\t}\n\tForm_pg_aggregate aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);\n\n\tif (aggform->aggcombinefn == InvalidOid)\n\t{\n\t\tReleaseSysCache(aggTuple);\n\t\treturn false;\n\t}\n\n\tHeapTuple typeTuple = SearchSysCache1(TYPEOID, aggform->aggtranstype);\n\tif (!HeapTupleIsValid(typeTuple))\n\t{\n\t\telog(ERROR, \"citus cache lookup failed.\");\n\t}\n\tForm_pg_type typeform = (Form_pg_type) GETSTRUCT(typeTuple);\n\n\tbool supportsSafeCombine = typeform->typtype != TYPTYPE_PSEUDO;\n\n\tif (AllowAggregateWorkerCombineOnInternalTypes &&\n\t\ttypeform->oid == INTERNALOID && !supportsSafeCombine)\n\t{\n\t\t/* check if the type supports a SERIALFUNC/DESERIALFUNC - if it does\n\t\t * then we can leverage that for safe transfer of the state across the wire.\n\t\t */\n\t\tif (aggform->aggserialfn != InvalidOid && aggform->aggdeserialfn != InvalidOid)\n\t\t{\n\t\t\tsupportsSafeCombine = true;\n\t\t}\n\t}\n\n\tReleaseSysCache(aggTuple);\n\tReleaseSysCache(typeTuple);\n\n\treturn supportsSafeCombine;\n}\n\n\n/*\n * AggregateArgMatchLevel and AggregateArgumentMatchLevel()\n *\n * Citus needs to resolve an aggregate function OID by (name, argument type)\n * when planning distributed aggregates. In the multi-shard path we run the\n * aggregate on each shard (worker tasks) and then build a coordinator-side\n * “combine” aggregate over the per-shard results. To construct that master\n * aggregate expression, we must find the correct underlying Postgres aggregate\n * implementation (OID).\n *\n * Postgres defines many aggregates using polymorphic pseudo-types rather than\n * concrete types. For example, min/max are defined for:\n *   - anyarray       \t               (e.g., int[], text[])\n *   - anyenum                         (e.g., a user-defined enum type)\n *   - anyelement                      (e.g., int4, text, numeric)\n *   - record                          (e.g., a named composite/row type)\n * so an “exact type only” lookup can miss the right candidate and fail with\n * \"no matching oid for function\".\n *\n * AggregateArgMatchLevel is a ranking of how well a candidate aggregate\n * declaration matches the input type. AggregateArgumentMatchLevel() computes\n * that rank for a pair of types:\n *\n *   declaredArgType: the aggregate's declared argument type taken from a\n *     pg_proc candidate (e.g., ANYARRAYOID, ANYELEMENTOID, RECORDOID, or a\n *     concrete type OID).\n *\n *   inputType: the actual argument type of the user query expression for which\n *     we are resolving the aggregate (e.g., INT4OID for int, the array type OID\n *     for int[], or a rowtype OID for a composite type column).\n *\n * The OID resolution logic scans candidate aggregates and selects the best\n * match (highest rank), preferring:\n *   1) AGG_MATCH_EXACT:\n *        declaredArgType == inputType\n *        Example: min(int4) with inputType = INT4OID.\n *\n *   2) AGG_MATCH_ARRAY_POLY:\n *        declaredArgType is ANYARRAY and inputType is an\n *        array type.\n *        Example: min(int[]) matches min(anyarray).\n *\n *   3) AGG_MATCH_GENERAL_POLY:\n *        declaredArgType is ANYELEMENT/ANYENUM and is compatible\n *        with inputType.\n *        Example: min(mood_enum) matches min(anyenum), or min(text) matches a\n *        polymorphic min(anyelement).\n *\n *   4) AGG_MATCH_RECORD:\n *        declaredArgType is RECORD and inputType is a rowtype/composite.\n *        Example: min(product_rating) matches min(record).\n *\n * This makes aggregate OID resolution robust across PG versions and additional\n * polymorphic signatures introduced in PG18 (notably for min/max).\n */\ntypedef enum AggregateArgMatchLevel\n{\n\tAGG_MATCH_NONE = 0,\n\tAGG_MATCH_RECORD = 1,\n\tAGG_MATCH_GENERAL_POLY = 2,\n\tAGG_MATCH_ARRAY_POLY = 3,\n\tAGG_MATCH_EXACT = 4\n} AggregateArgMatchLevel;\n\nstatic AggregateArgMatchLevel\nAggregateArgumentMatchLevel(Oid declaredArgType, Oid inputType)\n{\n\tif (declaredArgType == inputType)\n\t{\n\t\treturn AGG_MATCH_EXACT;\n\t}\n\n\tbool inputIsArray = (inputType == ANYARRAYOID) || type_is_array(inputType);\n\tbool inputIsEnum = (inputType == ANYENUMOID) || type_is_enum(inputType);\n\n\tswitch (declaredArgType)\n\t{\n\t\tcase ANYARRAYOID:\n\t\t{\n\t\t\treturn inputIsArray ? AGG_MATCH_ARRAY_POLY : AGG_MATCH_NONE;\n\t\t}\n\n\t\tcase ANYELEMENTOID:\n\t\t{\n\t\t\treturn AGG_MATCH_GENERAL_POLY;\n\t\t}\n\n\t\tcase ANYENUMOID:\n\t\t{\n\t\t\treturn inputIsEnum ? AGG_MATCH_GENERAL_POLY : AGG_MATCH_NONE;\n\t\t}\n\n\t\tcase RECORDOID:\n\t\t{\n\t\t\treturn type_is_rowtype(inputType) ? AGG_MATCH_RECORD : AGG_MATCH_NONE;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn AGG_MATCH_NONE;\n\t\t}\n\t}\n}\n\n\n/*\n * AggregateFunctionOid performs a reverse lookup on aggregate function name,\n * and returns the corresponding aggregate function oid for the given function\n * name and input type.\n */\nstatic Oid\nAggregateFunctionOid(const char *functionName, Oid inputType)\n{\n\tOid functionOid = InvalidOid;\n\tAggregateArgMatchLevel bestMatch = AGG_MATCH_NONE;\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\n\tRelation procRelation = table_open(ProcedureRelationId, AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_proc_proname,\n\t\t\t\tBTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(functionName));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(procRelation,\n\t\t\t\t\t\t\t\t\t\t\t\t\tProcedureNameArgsNspIndexId, true,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\t/* loop until we find the right function */\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tForm_pg_proc procForm = (Form_pg_proc) GETSTRUCT(heapTuple);\n\t\tint argumentCount = procForm->pronargs;\n\n\t\tif (argumentCount == 1)\n\t\t{\n\t\t\tOid declaredArgType = procForm->proargtypes.values[0];\n\t\t\tAggregateArgMatchLevel matchLevel =\n\t\t\t\tAggregateArgumentMatchLevel(declaredArgType, inputType);\n\n\t\t\tif (matchLevel > bestMatch)\n\t\t\t{\n\t\t\t\tbestMatch = matchLevel;\n\t\t\t\tfunctionOid = procForm->oid;\n\n\t\t\t\tif (bestMatch == AGG_MATCH_EXACT)\n\t\t\t\t{\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tAssert(argumentCount <= 1);\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tif (functionOid == InvalidOid)\n\t{\n\t\tereport(ERROR, (errmsg(\"no matching oid for function: %s\", functionName)));\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(procRelation, AccessShareLock);\n\n\treturn functionOid;\n}\n\n\n/*\n * CitusFunctionOidWithSignature looks up a function with given input types.\n * Looks in pg_catalog schema, as this function's sole purpose is\n * support aggregate lookup.\n */\nstatic Oid\nCitusFunctionOidWithSignature(char *functionName, int numargs, Oid *argtypes)\n{\n\tList *aggregateName = list_make2(makeString(\"pg_catalog\"), makeString(functionName));\n\tFuncCandidateList clist = FuncnameGetCandidates(aggregateName, numargs, NIL,\n\t\t\t\t\t\t\t\t\t\t\t\t\tfalse, false, false, true);\n\n\tfor (; clist; clist = clist->next)\n\t{\n\t\tif (memcmp(clist->args, argtypes, numargs * sizeof(Oid)) == 0)\n\t\t{\n\t\t\treturn clist->oid;\n\t\t}\n\t}\n\n\tereport(ERROR, (errmsg(\"no matching oid for function: %s\", functionName)));\n\treturn InvalidOid;\n}\n\n\n/*\n * WorkerPartialAggOid looks up oid of pg_catalog.worker_partial_agg\n */\nstatic Oid\nWorkerPartialAggOid()\n{\n\tOid argtypes[] = {\n\t\tOIDOID,\n\t\tANYELEMENTOID,\n\t};\n\n\treturn CitusFunctionOidWithSignature(WORKER_PARTIAL_AGGREGATE_NAME, 2, argtypes);\n}\n\n\n/*\n * CoordCombineAggOid looks up oid of pg_catalog.coord_combine_agg\n */\nstatic Oid\nCoordCombineAggOid()\n{\n\tOid argtypes[] = {\n\t\tOIDOID,\n\t\tCSTRINGOID,\n\t\tANYELEMENTOID,\n\t};\n\n\treturn CitusFunctionOidWithSignature(COORD_COMBINE_AGGREGATE_NAME, 3, argtypes);\n}\n\n\n/*\n * WorkerBinaryPartialAggOid looks up oid of pg_catalog.worker_binary_partial_agg\n */\nstatic Oid\nWorkerBinaryPartialAggOid()\n{\n\tOid argtypes[] = {\n\t\tOIDOID,\n\t\tANYELEMENTOID,\n\t};\n\n\treturn CitusFunctionOidWithSignature(WORKER_BINARY_PARTIAL_AGGREGATE_NAME, 2, argtypes\n\t\t\t\t\t\t\t\t\t\t );\n}\n\n\n/*\n * CoordBinaryCombineAggOid looks up oid of pg_catalog.coord_binary_combine_agg\n */\nstatic Oid\nCoordBinaryCombineAggOid()\n{\n\tOid argtypes[] = {\n\t\tOIDOID,\n\t\tBYTEAOID,\n\t\tANYELEMENTOID,\n\t};\n\n\treturn CitusFunctionOidWithSignature(COORD_BINARY_COMBINE_AGGREGATE_NAME, 3, argtypes)\n\t;\n}\n\n\n/*\n * TypeOid looks for a type that has the given name and schema, and returns the\n * corresponding type's oid.\n */\nstatic Oid\nTypeOid(Oid schemaId, const char *typeName)\n{\n\tOid typeOid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,\n\t\t\t\t\t\t\t\t  PointerGetDatum(typeName),\n\t\t\t\t\t\t\t\t  ObjectIdGetDatum(schemaId));\n\n\treturn typeOid;\n}\n\n\nstatic bool\nIsAggTransTypeBinarySerializable(Form_pg_aggregate aggForm)\n{\n\tOid transitionType = aggForm->aggtranstype;\n\n\tif (AllowAggregateWorkerCombineOnInternalTypes &&\n\t\ttransitionType == INTERNALOID)\n\t{\n\t\t/* For aggregates with internal transition types, we apply the binary serialization\n\t\t * check on the output value of the SERIALFUNC. If a serialfunc exists, Postgres\n\t\t * requires that the serialfunc return a bytea - which will be binary serializable\n\t\t */\n\t\treturn (aggForm->aggserialfn != InvalidOid);\n\t}\n\n\tHeapTuple typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(transitionType));\n\tif (!HeapTupleIsValid(typeTuple))\n\t{\n\t\telog(ERROR, \"citus cache lookup failed for transition type %u\", transitionType);\n\t}\n\n\tForm_pg_type typeForm = (Form_pg_type) GETSTRUCT(typeTuple);\n\tbool isBinaryCoercible = typeForm->typsend != InvalidOid &&\n\t\t\t\t\t\t\t typeForm->typreceive != InvalidOid;\n\n\tReleaseSysCache(typeTuple);\n\n\treturn isBinaryCoercible;\n}\n\n\n/*\n * CreateSortGroupClause creates SortGroupClause for a given column Var.\n * The caller should set tleSortGroupRef field and respective\n * TargetEntry->ressortgroupref fields to appropriate SortGroupRefIndex.\n */\nstatic SortGroupClause *\nCreateSortGroupClause(Var *column)\n{\n\tOid lessThanOperator = InvalidOid;\n\tOid equalsOperator = InvalidOid;\n\tbool hashable = false;\n\tSortGroupClause *groupByClause = makeNode(SortGroupClause);\n\n\tget_sort_group_operators(column->vartype, true, true, true,\n\t\t\t\t\t\t\t &lessThanOperator, &equalsOperator, NULL,\n\t\t\t\t\t\t\t &hashable);\n\tgroupByClause->eqop = equalsOperator;\n\tgroupByClause->hashable = hashable;\n\tgroupByClause->nulls_first = false;\n\tgroupByClause->sortop = lessThanOperator;\n\n\treturn groupByClause;\n}\n\n\n/*\n * CountDistinctHashFunctionName resolves the hll_hash function name to use for\n * the given input type, and returns this function name.\n */\nstatic const char *\nCountDistinctHashFunctionName(Oid argumentType)\n{\n\t/* resolve hash function name based on input argument type */\n\tswitch (argumentType)\n\t{\n\t\tcase INT4OID:\n\t\t{\n\t\t\treturn HLL_HASH_INTEGER_FUNC_NAME;\n\t\t}\n\n\t\tcase INT8OID:\n\t\t{\n\t\t\treturn HLL_HASH_BIGINT_FUNC_NAME;\n\t\t}\n\n\t\tcase TEXTOID:\n\t\tcase BPCHAROID:\n\t\tcase VARCHAROID:\n\t\t{\n\t\t\treturn HLL_HASH_TEXT_FUNC_NAME;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn HLL_HASH_ANY_FUNC_NAME;\n\t\t}\n\t}\n}\n\n\n/*\n * CountDistinctStorageSize takes in the desired precision for count distinct\n * approximations, and returns the log-base-2 of storage space needed for the\n * HyperLogLog algorithm.\n */\nstatic int\nCountDistinctStorageSize(double approximationErrorRate)\n{\n\tdouble desiredStorageSize = pow((1.04 / approximationErrorRate), 2);\n\tdouble logOfDesiredStorageSize = log(desiredStorageSize) / log(2);\n\n\t/* keep log2(storage size) inside allowed range */\n\tint logOfStorageSize = (int) rint(logOfDesiredStorageSize);\n\tif (logOfStorageSize < 4)\n\t{\n\t\tlogOfStorageSize = 4;\n\t}\n\telse if (logOfStorageSize > 17)\n\t{\n\t\tlogOfStorageSize = 17;\n\t}\n\n\treturn logOfStorageSize;\n}\n\n\n/* Makes an integer constant node from the given value, and returns that node. */\nstatic Const *\nMakeIntegerConst(int32 integerValue)\n{\n\tconst int typeCollationId = get_typcollation(INT4OID);\n\tconst int16 typeLength = get_typlen(INT4OID);\n\tconst int32 typeModifier = -1;\n\tconst bool typeIsNull = false;\n\tconst bool typePassByValue = true;\n\n\tDatum integerDatum = Int32GetDatum(integerValue);\n\tConst *integerConst = makeConst(INT4OID, typeModifier, typeCollationId, typeLength,\n\t\t\t\t\t\t\t\t\tintegerDatum, typeIsNull, typePassByValue);\n\n\treturn integerConst;\n}\n\n\n/* Makes a 64-bit integer constant node from the given value, and returns that node. */\nstatic Const *\nMakeIntegerConstInt64(int64 integerValue)\n{\n\tconst int typeCollationId = get_typcollation(INT8OID);\n\tconst int16 typeLength = get_typlen(INT8OID);\n\tconst int32 typeModifier = -1;\n\tconst bool typeIsNull = false;\n\tconst bool typePassByValue = true;\n\n\tDatum integer64Datum = Int64GetDatum(integerValue);\n\tConst *integer64Const = makeConst(INT8OID, typeModifier, typeCollationId, typeLength,\n\t\t\t\t\t\t\t\t\t  integer64Datum, typeIsNull, typePassByValue);\n\n\treturn integer64Const;\n}\n\n\n/*\n * HasNonDistributableAggregates checks for if any aggregates cannot be pushed down.\n * This only checks with GetAggregateType. DeferErrorIfHasNonDistributableAggregates\n * performs further checks which should be done if aggregates are not being pushed down.\n */\nstatic bool\nHasNonDistributableAggregates(MultiNode *logicalPlanNode)\n{\n\tif (CoordinatorAggregationStrategy == COORDINATOR_AGGREGATION_DISABLED)\n\t{\n\t\treturn false;\n\t}\n\n\tList *opNodeList = FindNodesOfType(logicalPlanNode, T_MultiExtendedOp);\n\tMultiExtendedOp *extendedOpNode = (MultiExtendedOp *) linitial(opNodeList);\n\n\tList *targetList = extendedOpNode->targetList;\n\tNode *havingQual = extendedOpNode->havingQual;\n\n\t/*\n\t * PVC_REJECT_PLACEHOLDERS is implicit if PVC_INCLUDE_PLACEHOLDERS isn't\n\t * specified.\n\t */\n\tList *expressionList = pull_var_clause((Node *) targetList, PVC_INCLUDE_AGGREGATES |\n\t\t\t\t\t\t\t\t\t\t   PVC_INCLUDE_WINDOWFUNCS);\n\texpressionList = list_concat(expressionList,\n\t\t\t\t\t\t\t\t pull_var_clause(havingQual, PVC_INCLUDE_AGGREGATES));\n\n\tNode *expression = NULL;\n\tforeach_declared_ptr(expression, expressionList)\n\t{\n\t\t/* only consider aggregate expressions */\n\t\tif (!IsA(expression, Aggref))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tAggregateType aggregateType = GetAggregateType((Aggref *) expression);\n\t\tAssert(aggregateType != AGGREGATE_INVALID_FIRST);\n\n\t\tif (aggregateType == AGGREGATE_CUSTOM_ROW_GATHER)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CanPushDownExpression returns whether the expression can be pushed down to workers.\n */\nstatic bool\nCanPushDownExpression(Node *expression,\n\t\t\t\t\t  const ExtendedOpNodeProperties *extendedOpNodeProperties)\n{\n\tif (contain_nextval_expression_walker(expression, NULL))\n\t{\n\t\t/* nextval can only be evaluated on the coordinator */\n\t\treturn false;\n\t}\n\n\tbool hasAggregate = contain_aggs_of_level(expression, 0);\n\tbool hasWindowFunction = contain_window_function(expression);\n\tif (!hasAggregate && !hasWindowFunction)\n\t{\n\t\t/*\n\t\t * If the query has the form SELECT expression, agg(..) FROM table;\n\t\t * then expression should be evaluated on the coordinator.\n\t\t *\n\t\t * Other than the efficiency part of this, we could also crash if\n\t\t * we pushed down the expression to the workers. When pushing down\n\t\t * expressions to workers we create a Var reference to the worker\n\t\t * tuples. If the result from worker is empty, but we need to have\n\t\t * at least a row in coordinator result, postgres will crash when\n\t\t * trying to evaluate the Var.\n\t\t *\n\t\t * For details, see https://github.com/citusdata/citus/pull/3961\n\t\t */\n\t\tif (!extendedOpNodeProperties->hasAggregate ||\n\t\t\textendedOpNodeProperties->hasGroupBy)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/* aggregates inside pushed down window functions can be pushed down */\n\tbool hasPushableWindowFunction =\n\t\thasWindowFunction && extendedOpNodeProperties->onlyPushableWindowFunctions;\n\tif (hasPushableWindowFunction)\n\t{\n\t\treturn true;\n\t}\n\n\tif (extendedOpNodeProperties->pushDownGroupingAndHaving && !hasWindowFunction)\n\t{\n\t\treturn true;\n\t}\n\n\tif (hasAggregate && !hasWindowFunction &&\n\t\textendedOpNodeProperties->groupedByDisjointPartitionColumn)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * DeferErrorIfHasNonDistributableAggregates extracts aggregate expressions from\n * the logical plan, walks over them and uses helper functions to check if we\n * can transform these aggregate expressions and push them down to worker nodes.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfHasNonDistributableAggregates(MultiNode *logicalPlanNode)\n{\n\tDeferredErrorMessage *error = NULL;\n\tList *opNodeList = FindNodesOfType(logicalPlanNode, T_MultiExtendedOp);\n\tMultiExtendedOp *extendedOpNode = (MultiExtendedOp *) linitial(opNodeList);\n\n\tList *targetList = extendedOpNode->targetList;\n\tNode *havingQual = extendedOpNode->havingQual;\n\n\t/*\n\t * PVC_REJECT_PLACEHOLDERS is implicit if PVC_INCLUDE_PLACEHOLDERS isn't\n\t * specified.\n\t */\n\tList *expressionList = pull_var_clause((Node *) targetList, PVC_INCLUDE_AGGREGATES |\n\t\t\t\t\t\t\t\t\t\t   PVC_INCLUDE_WINDOWFUNCS);\n\texpressionList = list_concat(expressionList,\n\t\t\t\t\t\t\t\t pull_var_clause(havingQual, PVC_INCLUDE_AGGREGATES));\n\n\tNode *expression = NULL;\n\tforeach_declared_ptr(expression, expressionList)\n\t{\n\t\t/* only consider aggregate expressions */\n\t\tif (!IsA(expression, Aggref))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* GetAggregateType errors out on unsupported aggregate types */\n\t\tAggref *aggregateExpression = (Aggref *) expression;\n\t\tAggregateType aggregateType = GetAggregateType(aggregateExpression);\n\t\tAssert(aggregateType != AGGREGATE_INVALID_FIRST);\n\n\t\t/*\n\t\t * Check that we can transform the current aggregate expression. These\n\t\t * functions error out on unsupported array_agg and aggregate (distinct)\n\t\t * clauses.\n\t\t */\n\t\tif (aggregateType == AGGREGATE_ARRAY_AGG)\n\t\t{\n\t\t\terror = DeferErrorIfUnsupportedArrayAggregate(aggregateExpression);\n\t\t}\n\t\telse if (aggregateType == AGGREGATE_JSONB_AGG ||\n\t\t\t\t aggregateType == AGGREGATE_JSON_AGG)\n\t\t{\n\t\t\terror = DeferErrorIfUnsupportedJsonAggregate(aggregateType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t aggregateExpression);\n\t\t}\n\t\telse if (aggregateType == AGGREGATE_JSONB_OBJECT_AGG ||\n\t\t\t\t aggregateType == AGGREGATE_JSON_OBJECT_AGG)\n\t\t{\n\t\t\terror = DeferErrorIfUnsupportedJsonAggregate(aggregateType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t aggregateExpression);\n\t\t}\n\t\telse if (aggregateExpression->aggdistinct)\n\t\t{\n\t\t\terror = DeferErrorIfUnsupportedAggregateDistinct(aggregateExpression,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t logicalPlanNode);\n\t\t}\n\n\t\tif (error != NULL)\n\t\t{\n\t\t\treturn error;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * DeferErrorIfUnsupportedArrayAggregate checks if we can transform the array aggregate\n * expression and push it down to the worker node. If we cannot transform the\n * aggregate, this function errors.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfUnsupportedArrayAggregate(Aggref *arrayAggregateExpression)\n{\n\t/* if array_agg has order by, we error out */\n\tif (arrayAggregateExpression->aggorder)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"array_agg with order by is unsupported\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\t/* if array_agg has distinct, we error out */\n\tif (arrayAggregateExpression->aggdistinct)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"array_agg (distinct) is unsupported\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * DeferErrorIfUnsupportedJsonAggregate checks if we can transform the json\n * aggregate expression and push it down to the worker node. If we cannot\n * transform the aggregate, this function errors.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfUnsupportedJsonAggregate(AggregateType type,\n\t\t\t\t\t\t\t\t\t Aggref *aggregateExpression)\n{\n\t/* if json aggregate has order by, we error out */\n\tif (aggregateExpression->aggdistinct || aggregateExpression->aggorder)\n\t{\n\t\tStringInfoData errorDetail;\n\t\tinitStringInfo(&errorDetail);\n\t\tconst char *name = AggregateNames[type];\n\n\t\tappendStringInfoString(&errorDetail, name);\n\t\tif (aggregateExpression->aggorder)\n\t\t{\n\t\t\tappendStringInfoString(&errorDetail, \" with order by is unsupported\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfoString(&errorDetail, \" (distinct) is unsupported\");\n\t\t}\n\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, errorDetail.data,\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * DeferErrorIfUnsupportedAggregateDistinct checks if we can transform the aggregate\n * (distinct expression) and push it down to the worker node. It handles count\n * (distinct) separately to check if we can use distinct approximations. If we\n * cannot transform the aggregate, this function errors.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfUnsupportedAggregateDistinct(Aggref *aggregateExpression,\n\t\t\t\t\t\t\t\t\t\t MultiNode *logicalPlanNode)\n{\n\tconst char *errorDetail = NULL;\n\tbool distinctSupported = true;\n\n\tAggregateType aggregateType = GetAggregateType(aggregateExpression);\n\n\t/* If we're aggregating on coordinator, this becomes simple. */\n\tif (aggregateType == AGGREGATE_CUSTOM_ROW_GATHER)\n\t{\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * We partially support count(distinct) in subqueries, other distinct aggregates in\n\t * subqueries are not supported yet.\n\t */\n\tif (aggregateType == AGGREGATE_COUNT)\n\t{\n\t\tNode *aggregateArgument = (Node *) linitial(aggregateExpression->args);\n\t\tList *columnList = pull_var_clause_default(aggregateArgument);\n\n\t\tVar *column = NULL;\n\t\tforeach_declared_ptr(column, columnList)\n\t\t{\n\t\t\tif (column->varattno <= 0)\n\t\t\t{\n\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"cannot compute count (distinct)\",\n\t\t\t\t\t\t\t\t\t \"Non-column references are not supported yet\",\n\t\t\t\t\t\t\t\t\t NULL);\n\t\t\t}\n\t\t}\n\t}\n\telse\n\t{\n\t\tList *multiTableNodeList = FindNodesOfType(logicalPlanNode, T_MultiTable);\n\n\t\tMultiTable *multiTable = NULL;\n\t\tforeach_declared_ptr(multiTable, multiTableNodeList)\n\t\t{\n\t\t\tif (multiTable->relationId == SUBQUERY_RELATION_ID ||\n\t\t\t\tmultiTable->relationId == SUBQUERY_PUSHDOWN_RELATION_ID)\n\t\t\t{\n\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"cannot compute aggregate (distinct)\",\n\t\t\t\t\t\t\t\t\t \"Only count(distinct) aggregate is \"\n\t\t\t\t\t\t\t\t\t \"supported in subqueries\", NULL);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* if we have a count(distinct), and distinct approximation is enabled */\n\tif (aggregateType == AGGREGATE_COUNT &&\n\t\tCountDistinctErrorRate != DISABLE_DISTINCT_APPROXIMATION)\n\t{\n\t\tbool missingOK = true;\n\t\tOid distinctExtensionId = get_extension_oid(HLL_EXTENSION_NAME, missingOK);\n\n\t\t/* if extension for distinct approximation is loaded, we are good */\n\t\tif (distinctExtensionId != InvalidOid)\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"cannot compute count (distinct) approximation\",\n\t\t\t\t\t\t\t\t NULL,\n\t\t\t\t\t\t\t\t \"You need to have the hll extension loaded.\");\n\t\t}\n\t}\n\n\tif (aggregateType == AGGREGATE_COUNT)\n\t{\n\t\tList *aggregateVarList = pull_var_clause_default((Node *) aggregateExpression);\n\t\tif (aggregateVarList == NIL)\n\t\t{\n\t\t\tdistinctSupported = false;\n\t\t\terrorDetail = \"aggregate (distinct) with no columns is unsupported\";\n\t\t}\n\t}\n\n\tList *repartitionNodeList = FindNodesOfType(logicalPlanNode, T_MultiPartition);\n\tif (repartitionNodeList != NIL)\n\t{\n\t\tdistinctSupported = false;\n\t\terrorDetail = \"aggregate (distinct) with table repartitioning is unsupported\";\n\t}\n\n\tList *tableNodeList = FindNodesOfType(logicalPlanNode, T_MultiTable);\n\tList *extendedOpNodeList = FindNodesOfType(logicalPlanNode, T_MultiExtendedOp);\n\tMultiExtendedOp *extendedOpNode = (MultiExtendedOp *) linitial(extendedOpNodeList);\n\n\tVar *distinctColumn = AggregateDistinctColumn(aggregateExpression);\n\tif (distinctSupported)\n\t{\n\t\tif (distinctColumn == NULL)\n\t\t{\n\t\t\t/*\n\t\t\t * If the query has a single table, and table is grouped by partition\n\t\t\t * column, then we support count distincts even distinct column can\n\t\t\t * not be identified.\n\t\t\t */\n\t\t\tdistinctSupported = TablePartitioningSupportsDistinct(tableNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  extendedOpNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  distinctColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  aggregateType);\n\t\t\tif (!distinctSupported)\n\t\t\t{\n\t\t\t\terrorDetail = \"aggregate (distinct) on complex expressions is\"\n\t\t\t\t\t\t\t  \" unsupported\";\n\t\t\t}\n\t\t}\n\t\telse if (aggregateType != AGGREGATE_COUNT)\n\t\t{\n\t\t\tbool supports = TablePartitioningSupportsDistinct(tableNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  extendedOpNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  distinctColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  aggregateType);\n\t\t\tif (!supports)\n\t\t\t{\n\t\t\t\tdistinctSupported = false;\n\t\t\t\terrorDetail = \"table partitioning is unsuitable for aggregate (distinct)\";\n\t\t\t}\n\t\t}\n\t}\n\n\t/* if current aggregate expression isn't supported, error out */\n\tif (!distinctSupported)\n\t{\n\t\tconst char *errorHint = NULL;\n\t\tif (aggregateType == AGGREGATE_COUNT)\n\t\t{\n\t\t\terrorHint = \"You can load the hll extension from contrib \"\n\t\t\t\t\t\t\"packages and enable distinct approximations.\";\n\t\t}\n\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot compute aggregate (distinct)\",\n\t\t\t\t\t\t\t errorDetail, errorHint);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * AggregateDistinctColumn checks if the given aggregate expression's distinct\n * clause is on a single column. If it is, the function finds and returns that\n * column. Otherwise, the function returns null.\n * The function expects to find a single column here, no FieldSelect or other\n * expressions are accepted as a column.\n */\nstatic Var *\nAggregateDistinctColumn(Aggref *aggregateExpression)\n{\n\t/* only consider aggregates with distincts */\n\tif (!aggregateExpression->aggdistinct)\n\t{\n\t\treturn NULL;\n\t}\n\n\tint aggregateArgumentCount = list_length(aggregateExpression->args);\n\tif (aggregateArgumentCount != 1)\n\t{\n\t\treturn NULL;\n\t}\n\n\tTargetEntry *aggregateTargetEntry = (TargetEntry *) linitial(\n\t\taggregateExpression->args);\n\tif (!IsA(aggregateTargetEntry->expr, Var))\n\t{\n\t\treturn NULL;\n\t}\n\n\tVar *aggregateColumn = (Var *) aggregateTargetEntry->expr;\n\treturn aggregateColumn;\n}\n\n\n/*\n * TablePartitioningSupportsDistinct walks over all tables in the given list and\n * checks that each table's partitioning method is suitable for pushing down an\n * aggregate (distinct) expression to worker nodes. For this, the function needs\n * to check that task results do not overlap with one another on the distinct\n * column.\n */\nstatic bool\nTablePartitioningSupportsDistinct(List *tableNodeList, MultiExtendedOp *opNode,\n\t\t\t\t\t\t\t\t  Var *distinctColumn, AggregateType aggregateType)\n{\n\tbool distinctSupported = true;\n\n\tMultiTable *tableNode = NULL;\n\tforeach_declared_ptr(tableNode, tableNodeList)\n\t{\n\t\tOid relationId = tableNode->relationId;\n\t\tbool tableDistinctSupported = false;\n\n\t\tif (relationId == SUBQUERY_RELATION_ID ||\n\t\t\trelationId == SUBQUERY_PUSHDOWN_RELATION_ID)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\t/* if table has one shard, task results don't overlap */\n\t\tList *shardList = LoadShardList(relationId);\n\t\tif (list_length(shardList) == 1)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * We need to check that task results don't overlap. We can only do this\n\t\t * if table is range partitioned.\n\t\t */\n\t\tif (IsCitusTableType(relationId, RANGE_DISTRIBUTED) ||\n\t\t\tIsCitusTableType(relationId, HASH_DISTRIBUTED))\n\t\t{\n\t\t\tVar *tablePartitionColumn = tableNode->partitionColumn;\n\n\t\t\tif (aggregateType == AGGREGATE_COUNT)\n\t\t\t{\n\t\t\t\ttableDistinctSupported = true;\n\t\t\t}\n\n\t\t\t/* if distinct is on table partition column, we can push it down */\n\t\t\tif (distinctColumn != NULL &&\n\t\t\t\ttablePartitionColumn->varno == distinctColumn->varno &&\n\t\t\t\ttablePartitionColumn->varattno == distinctColumn->varattno)\n\t\t\t{\n\t\t\t\ttableDistinctSupported = true;\n\t\t\t}\n\n\t\t\t/* if results are grouped by partition column, we can push down */\n\t\t\tbool groupedByPartitionColumn = GroupedByColumn(opNode->groupClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\topNode->targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttablePartitionColumn);\n\t\t\tif (groupedByPartitionColumn)\n\t\t\t{\n\t\t\t\ttableDistinctSupported = true;\n\t\t\t}\n\t\t}\n\n\t\tif (!tableDistinctSupported)\n\t\t{\n\t\t\tdistinctSupported = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn distinctSupported;\n}\n\n\n/*\n * GroupedByColumn walks over group clauses in the given list, and checks if any\n * of the group clauses is on the given column.\n */\nbool\nGroupedByColumn(List *groupClauseList, List *targetList, Var *column)\n{\n\tbool groupedByColumn = false;\n\n\tif (column == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tSortGroupClause *groupClause = NULL;\n\tforeach_declared_ptr(groupClause, groupClauseList)\n\t{\n\t\tTargetEntry *groupTargetEntry = get_sortgroupclause_tle(groupClause, targetList);\n\n\t\tExpr *groupExpression = (Expr *) groupTargetEntry->expr;\n\t\tif (IsA(groupExpression, Var))\n\t\t{\n\t\t\tVar *groupColumn = (Var *) groupExpression;\n\t\t\tif (groupColumn->varno == column->varno &&\n\t\t\t\tgroupColumn->varattno == column->varattno)\n\t\t\t{\n\t\t\t\tgroupedByColumn = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn groupedByColumn;\n}\n\n\n/*\n * SubqueryMultiTableList extracts multi tables in the given logical plan tree\n * and returns subquery multi tables in a new list.\n */\nList *\nSubqueryMultiTableList(MultiNode *multiNode)\n{\n\tList *subqueryMultiTableList = NIL;\n\tList *multiTableNodeList = FindNodesOfType(multiNode, T_MultiTable);\n\n\tMultiTable *multiTable = NULL;\n\tforeach_declared_ptr(multiTable, multiTableNodeList)\n\t{\n\t\tQuery *subquery = multiTable->subquery;\n\n\t\tif (subquery != NULL)\n\t\t{\n\t\t\tsubqueryMultiTableList = lappend(subqueryMultiTableList, multiTable);\n\t\t}\n\t}\n\n\treturn subqueryMultiTableList;\n}\n\n\n/*\n * GroupTargetEntryList walks over group clauses in the given list, finds\n * matching target entries and return them in a new list.\n */\nList *\nGroupTargetEntryList(List *groupClauseList, List *targetEntryList)\n{\n\tList *groupTargetEntryList = NIL;\n\n\tSortGroupClause *groupClause = NULL;\n\tforeach_declared_ptr(groupClause, groupClauseList)\n\t{\n\t\tTargetEntry *groupTargetEntry =\n\t\t\tget_sortgroupclause_tle(groupClause, targetEntryList);\n\t\tgroupTargetEntryList = lappend(groupTargetEntryList, groupTargetEntry);\n\t}\n\n\treturn groupTargetEntryList;\n}\n\n\n/*\n * IsPartitionColumn returns true if the given column is a partition column.\n * The function uses FindReferencedTableColumn to find the original relation\n * id and column that the column expression refers to. It then checks whether\n * that column is a partition column of the relation.\n *\n * Also, the function returns always false for reference tables given that\n * reference tables do not have partition column. The function does not\n * support queries with CTEs, it would return false if columnExpression\n * refers to a column returned by a CTE.\n *\n * If skipOuterVars is true, then it doesn't process the outervars.\n */\nbool\nIsPartitionColumn(Expr *columnExpression, Query *query, bool skipOuterVars)\n{\n\tbool isPartitionColumn = false;\n\tVar *column = NULL;\n\tRangeTblEntry *relationRTE = NULL;\n\n\tFindReferencedTableColumn(columnExpression, NIL, query, &column, &relationRTE,\n\t\t\t\t\t\t\t  skipOuterVars);\n\tOid relationId = relationRTE ? relationRTE->relid : InvalidOid;\n\tif (relationId != InvalidOid && column != NULL)\n\t{\n\t\tVar *partitionColumn = DistPartitionKey(relationId);\n\n\t\t/* not all distributed tables have partition column */\n\t\tif (partitionColumn != NULL && column->varattno == partitionColumn->varattno)\n\t\t{\n\t\t\tisPartitionColumn = true;\n\t\t}\n\t}\n\n\treturn isPartitionColumn;\n}\n\n\n/*\n * FindReferencedTableColumn recursively traverses query tree to find actual relation\n * id, and column that columnExpression refers to. If columnExpression is a\n * non-relational or computed/derived expression, the function returns NULL for\n * rte and NULL for column. The caller should provide parent query list from\n * top of the tree to this particular Query's parent. This argument is used to look\n * into CTEs that may be present in the query.\n *\n * If skipOuterVars is true, then it doesn't check vars coming from outer queries.\n * We probably don't need this skipOuterVars check but we wanted to be on the safe side\n * and used it only in UNION path, we can separately work on verifying that it doesn't break\n * anything existing.\n */\nvoid\nFindReferencedTableColumn(Expr *columnExpression, List *parentQueryList, Query *query,\n\t\t\t\t\t\t  Var **column, RangeTblEntry **rteContainingReferencedColumn,\n\t\t\t\t\t\t  bool skipOuterVars)\n{\n\tVar *candidateColumn = NULL;\n\tExpr *strippedColumnExpression = (Expr *) strip_implicit_coercions(\n\t\t(Node *) columnExpression);\n\n\t*rteContainingReferencedColumn = NULL;\n\t*column = NULL;\n\n\tif (IsA(strippedColumnExpression, Var))\n\t{\n\t\tcandidateColumn = (Var *) strippedColumnExpression;\n\t}\n\telse if (IsA(strippedColumnExpression, FieldSelect))\n\t{\n\t\tFieldSelect *compositeField = (FieldSelect *) strippedColumnExpression;\n\t\tExpr *fieldExpression = compositeField->arg;\n\n\t\tif (IsA(fieldExpression, Var))\n\t\t{\n\t\t\tcandidateColumn = (Var *) fieldExpression;\n\t\t}\n\t}\n\n\tif (candidateColumn == NULL)\n\t{\n\t\treturn;\n\t}\n\n\t/* Walk up varlevelsup as many times as needed */\n\twhile (candidateColumn->varlevelsup > 0)\n\t{\n\t\t/* Caller asked us to ignore any outer Vars → just bail out */\n\t\tif (skipOuterVars)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t/* Locate the parent query that owns this Var */\n\t\tint parentIdx =\n\t\t\tlist_length(parentQueryList) - candidateColumn->varlevelsup;\n\t\tif (!IsIndexInRange(parentQueryList, parentIdx))\n\t\t{\n\t\t\treturn;                             /* malformed tree */\n\t\t}\n\n\t\t/* Work on a fresh copy of the Var with varlevelsup reset */\n\t\tcandidateColumn = copyObject(candidateColumn);\n\t\tcandidateColumn->varlevelsup = 0;\n\n\t\t/*\n\t\t * Make a *completely private* copy of parentQueryList for the\n\t\t * next recursion step.  We copy the whole list and then truncate\n\t\t * so every recursive branch owns its own list cells.\n\t\t */\n\t\tList *newParent =\n\t\t\tlist_copy(parentQueryList);         /* duplicates every cell  */\n\t\tnewParent = list_truncate(newParent, parentIdx);\n\n\t\tquery = list_nth(parentQueryList, parentIdx);\n\t\tparentQueryList = newParent;            /* hand private copy down */\n\n\t\t/* Loop again if still pointing to an outer level */\n\t}\n\n\n\tif (candidateColumn->varattno == InvalidAttrNumber)\n\t{\n\t\t/*\n\t\t * varattno can be 0 in case of SELECT table FROM table, but that Var\n\t\t * definitely does not correspond to a specific column.\n\t\t */\n\t\treturn;\n\t}\n\n\tList *rangetableList = query->rtable;\n\tint rangeTableEntryIndex = candidateColumn->varno - 1;\n\tRangeTblEntry *rangeTableEntry = list_nth(rangetableList, rangeTableEntryIndex);\n\n\tif (rangeTableEntry->rtekind == RTE_RELATION)\n\t{\n\t\t*rteContainingReferencedColumn = rangeTableEntry;\n\t\t*column = candidateColumn;\n\t}\n\telse if (rangeTableEntry->rtekind == RTE_SUBQUERY)\n\t{\n\t\tQuery *subquery = rangeTableEntry->subquery;\n\t\tList *targetEntryList = subquery->targetList;\n\t\tAttrNumber targetEntryIndex = candidateColumn->varattno - 1;\n\t\tTargetEntry *subqueryTargetEntry = list_nth(targetEntryList, targetEntryIndex);\n\t\tExpr *subColumnExpression = subqueryTargetEntry->expr;\n\n\t\t/* append current query to parent query list */\n\t\tparentQueryList = lappend(parentQueryList, query);\n\t\tFindReferencedTableColumn(subColumnExpression, parentQueryList,\n\t\t\t\t\t\t\t\t  subquery, column, rteContainingReferencedColumn,\n\t\t\t\t\t\t\t\t  skipOuterVars);\n\t}\n\telse if (rangeTableEntry->rtekind == RTE_JOIN)\n\t{\n\t\tList *joinColumnList = rangeTableEntry->joinaliasvars;\n\t\tAttrNumber joinColumnIndex = candidateColumn->varattno - 1;\n\t\tExpr *joinColumn = list_nth(joinColumnList, joinColumnIndex);\n\n\t\t/* parent query list stays the same since still in the same query boundary */\n\t\tFindReferencedTableColumn(joinColumn, parentQueryList, query, column,\n\t\t\t\t\t\t\t\t  rteContainingReferencedColumn, skipOuterVars);\n\t}\n#if PG_VERSION_NUM >= PG_VERSION_18\n\telse if (rangeTableEntry->rtekind == RTE_GROUP)\n\t{\n\t\t/*\n\t\t * PG 18: synthetic GROUP RTE.  Each groupexprs item corresponds to the\n\t\t * columns produced by the grouping step, in the *same ordinal order* as\n\t\t * the Vars that reference them.\n\t\t */\n\t\tList *groupexprs = rangeTableEntry->groupexprs;\n\t\tAttrNumber groupIndex = candidateColumn->varattno - 1;\n\n\t\t/* this must always hold unless upstream Postgres mis-constructed the RTE_GROUP */\n\t\tAssert(groupIndex >= 0 && groupIndex < list_length(groupexprs));\n\n\t\tExpr *groupExpr = (Expr *) list_nth(groupexprs, groupIndex);\n\n\t\t/* Recurse on the underlying expression (stay in the same query) */\n\t\tFindReferencedTableColumn(groupExpr, parentQueryList, query,\n\t\t\t\t\t\t\t\t  column, rteContainingReferencedColumn,\n\t\t\t\t\t\t\t\t  skipOuterVars);\n\t}\n#endif   /* PG_VERSION_NUM >= 180000 */\n\n\telse if (rangeTableEntry->rtekind == RTE_CTE)\n\t{\n\t\t/*\n\t\t * Resolve through a CTE even when skipOuterVars == false.\n\t\t * Maintain the invariant that each recursion level owns a private,\n\t\t * correctly-bounded copy of parentQueryList.\n\t\t */\n\t\tint cteParentListIndex = list_length(parentQueryList) -\n\t\t\t\t\t\t\t\t rangeTableEntry->ctelevelsup - 1;\n\t\tQuery *cteParentQuery = NULL;\n\t\tList *cteList = NIL;\n\t\tCommonTableExpr *cte = NULL;\n\n\t\t/*\n\t\t * This should have been an error case, not marking it as error at the\n\t\t * moment due to usage from IsPartitionColumn. Callers of that function\n\t\t * do not have access to parent query list.\n\t\t */\n\t\tif (IsIndexInRange(parentQueryList, cteParentListIndex))\n\t\t{\n\t\t\tcteParentQuery = list_nth(parentQueryList, cteParentListIndex);\n\t\t\tcteList = cteParentQuery->cteList;\n\t\t}\n\n\t\tCommonTableExpr *candidateCte = NULL;\n\t\tforeach_declared_ptr(candidateCte, cteList)\n\t\t{\n\t\t\tif (strcmp(candidateCte->ctename, rangeTableEntry->ctename) == 0)\n\t\t\t{\n\t\t\t\tcte = candidateCte;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (cte != NULL)\n\t\t{\n\t\t\tQuery *cteQuery = (Query *) cte->ctequery;\n\t\t\tAttrNumber targetEntryIndex = candidateColumn->varattno - 1;\n\n\t\t\tif (targetEntryIndex >= 0 &&\n\t\t\t\ttargetEntryIndex < list_length(cteQuery->targetList))\n\t\t\t{\n\t\t\t\tTargetEntry *targetEntry =\n\t\t\t\t\tlist_nth(cteQuery->targetList, targetEntryIndex);\n\n\t\t\t\t/* Build a private, bounded parentQueryList before recursing into the CTE.\n\t\t\t\t * Invariant: list is [top … current], owned by this call (no aliasing).\n\t\t\t\t * For RTE_CTE:\n\t\t\t\t *   owner_idx = list_length(parentQueryList) - rangeTableEntry->ctelevelsup - 1;\n\t\t\t\t *   newParent = lappend(list_truncate(list_copy(parentQueryList), owner_idx + 1), query);\n\t\t\t\t * Example (Q0 owns CTE; we’re in Q2 via nested subquery):\n\t\t\t\t *   parent=[Q0,Q1,Q2], ctelevelsup=2 ⇒ owner_idx=0 ⇒ newParent=[Q0,Q2].\n\t\t\t\t * Keeps outer-Var level math correct without mutating the caller’s list.\n\t\t\t\t */\n\t\t\t\tList *newParent = list_copy(parentQueryList);\n\t\t\t\tnewParent = list_truncate(newParent, cteParentListIndex + 1);\n\t\t\t\tnewParent = lappend(newParent, query);\n\n\t\t\t\tFindReferencedTableColumn(targetEntry->expr,\n\t\t\t\t\t\t\t\t\t\t  newParent,\n\t\t\t\t\t\t\t\t\t\t  cteQuery,\n\t\t\t\t\t\t\t\t\t\t  column,\n\t\t\t\t\t\t\t\t\t\t  rteContainingReferencedColumn,\n\t\t\t\t\t\t\t\t\t\t  skipOuterVars);\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * IsIndexInRange returns true if the given index is within the\n * range of the given list.\n */\nstatic bool\nIsIndexInRange(const List *list, int index)\n{\n\treturn index >= 0 && index < list_length(list);\n}\n\n\n/*\n * ExtractQueryWalker walks over a query, and finds all queries in the query\n * tree and returns these queries. Note that the function also recurses into\n * the subqueries in WHERE clause.\n */\nbool\nExtractQueryWalker(Node *node, List **queryList)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\n\t\t(*queryList) = lappend(*queryList, query);\n\t\treturn query_tree_walker(query, ExtractQueryWalker, queryList, 0);\n\t}\n\n\treturn expression_tree_walker(node, ExtractQueryWalker, queryList);\n}\n\n\n/*\n * WorkerLimitCount checks if the given input contains a valid limit node, and\n * if that node can be pushed down. For this, the function checks if this limit\n * count or a meaningful approximation of it can be pushed down to worker nodes.\n * If they can, the function returns the limit count.\n *\n * The limit push-down decision tree is as follows:\n *                                         group by?\n *                                       1/         \\0\n *                       group by partition column?   (exact pd)\n *                              0/         \\1\n *                          order by?        (exact pd)\n *                       1/           \\0\n *           has order by agg?          (no pd)\n *            1/           \\0\n *     can approximate?    (exact pd)\n *      1/       \\0\n * (approx pd)   (no pd)\n *\n * When an offset is present, the offset value is added to limit because for a query\n * with LIMIT x OFFSET y, (x+y) records should be pulled from the workers.\n *\n * If no limit is present or can be pushed down, then WorkerLimitCount\n * returns null.\n */\nstatic Node *\nWorkerLimitCount(Node *limitCount, Node *limitOffset, OrderByLimitReference\n\t\t\t\t orderByLimitReference)\n{\n\tNode *workerLimitNode = NULL;\n\tLimitPushdownable canPushDownLimit = LIMIT_CANNOT_PUSHDOWN;\n\n\tif (limitCount == NULL)\n\t{\n\t\t/* no limit node to push down */\n\t\treturn NULL;\n\t}\n\n\tif (!IsA(limitCount, Const))\n\t{\n\t\t/*\n\t\t * We only push down constant LIMIT clauses to make sure we get back\n\t\t * the minimum number of rows.\n\t\t */\n\t\treturn NULL;\n\t}\n\n\tif (limitOffset != NULL && !IsA(limitOffset, Const))\n\t{\n\t\t/*\n\t\t * If OFFSET is not a constant then we cannot calculate the LIMIT to\n\t\t * push down.\n\t\t */\n\t\treturn NULL;\n\t}\n\n\n\t/*\n\t * If window functions are computed on coordinator, we cannot push down LIMIT.\n\t * If we don't have group by clauses, or we have group by partition column,\n\t * or if we have order by clauses without aggregates, we can push down the\n\t * original limit. Else if we have order by clauses with commutative aggregates,\n\t * we can push down approximate limits.\n\t */\n\tif (!orderByLimitReference.onlyPushableWindowFunctions)\n\t{\n\t\tcanPushDownLimit = LIMIT_CANNOT_PUSHDOWN;\n\t}\n\telse if (orderByLimitReference.groupClauseIsEmpty ||\n\t\t\t orderByLimitReference.groupedByDisjointPartitionColumn)\n\t{\n\t\tcanPushDownLimit = LIMIT_CAN_PUSHDOWN;\n\t}\n\telse if (orderByLimitReference.sortClauseIsEmpty)\n\t{\n\t\tcanPushDownLimit = LIMIT_CANNOT_PUSHDOWN;\n\t}\n\telse if (!orderByLimitReference.hasOrderByAggregate)\n\t{\n\t\tcanPushDownLimit = LIMIT_CAN_PUSHDOWN;\n\t}\n\telse if (orderByLimitReference.canApproximate)\n\t{\n\t\tcanPushDownLimit = LIMIT_CAN_APPROXIMATE;\n\t}\n\n\t/* create the workerLimitNode according to the decisions above */\n\tif (canPushDownLimit == LIMIT_CAN_PUSHDOWN)\n\t{\n\t\tworkerLimitNode = (Node *) copyObject(limitCount);\n\t}\n\telse if (canPushDownLimit == LIMIT_CAN_APPROXIMATE)\n\t{\n\t\tConst *workerLimitConst = (Const *) copyObject(limitCount);\n\t\tint64 workerLimitCount = (int64) LimitClauseRowFetchCount;\n\t\tworkerLimitConst->constvalue = Int64GetDatum(workerLimitCount);\n\n\t\tworkerLimitNode = (Node *) workerLimitConst;\n\t}\n\n\t/*\n\t * If offset clause is present and limit can be pushed down (whether exactly or\n\t * approximately), add the offset value to limit on workers\n\t */\n\tif (workerLimitNode != NULL && limitOffset != NULL)\n\t{\n\t\tConst *workerLimitConst = (Const *) workerLimitNode;\n\n\t\t/* Only update the worker limit if the const is not null.*/\n\t\tif (!workerLimitConst->constisnull)\n\t\t{\n\t\t\tConst *workerOffsetConst = (Const *) limitOffset;\n\t\t\tint64 workerLimitCount = DatumGetInt64(workerLimitConst->constvalue);\n\n\t\t\t/* If the offset is null, it defaults to 0 when cast to int64. */\n\t\t\tint64 workerOffsetCount = DatumGetInt64(workerOffsetConst->constvalue);\n\t\t\tworkerLimitCount = workerLimitCount + workerOffsetCount;\n\t\t\tworkerLimitNode = (Node *) MakeIntegerConstInt64(workerLimitCount);\n\t\t}\n\t}\n\n\t/* display debug message on limit push down */\n\tif (workerLimitNode != NULL)\n\t{\n\t\tConst *workerLimitConst = (Const *) workerLimitNode;\n\t\tif (!workerLimitConst->constisnull)\n\t\t{\n\t\t\tint64 workerLimitCount = DatumGetInt64(workerLimitConst->constvalue);\n\n\t\t\tereport(DEBUG1, (errmsg(\"push down of limit count: \" INT64_FORMAT,\n\t\t\t\t\t\t\t\t\tworkerLimitCount)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"push down of limit count: ALL\")));\n\t\t}\n\t}\n\n\treturn workerLimitNode;\n}\n\n\n/*\n * WorkerSortClauseList first checks if the given input contains a limit\n * or hasDistinctOn that can be pushed down. If it does, the function then\n * checks if we need to add any sorting and grouping clauses to the sort list we\n * push down for the limit. If we do, the function adds these clauses and\n * returns them. Otherwise, the function returns null.\n */\nstatic List *\nWorkerSortClauseList(Node *limitCount, List *groupClauseList, List *sortClauseList,\n\t\t\t\t\t OrderByLimitReference orderByLimitReference)\n{\n\tList *workerSortClauseList = NIL;\n\n\t/* if no limit node and no hasDistinctOn, no need to push down sort clauses */\n\tif (limitCount == NULL && !orderByLimitReference.hasDistinctOn)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* If window functions are computed on coordinator, we cannot push down sorting. */\n\tif (!orderByLimitReference.onlyPushableWindowFunctions)\n\t{\n\t\treturn NIL;\n\t}\n\n\tsortClauseList = copyObject(sortClauseList);\n\n\t/*\n\t * If we are pushing down the limit, push down any order by clauses. Also if\n\t * we are pushing down the limit because the order by clauses don't have any\n\t * aggregates, add group by clauses to the order by list. We do this because\n\t * rows that belong to the same grouping may appear in different \"offsets\"\n\t * in different task results. By ordering on the group by clause, we ensure\n\t * that query results are consistent.\n\t */\n\tif (orderByLimitReference.groupClauseIsEmpty ||\n\t\torderByLimitReference.groupedByDisjointPartitionColumn)\n\t{\n\t\tworkerSortClauseList = sortClauseList;\n\t}\n\telse if (sortClauseList != NIL)\n\t{\n\t\tbool orderByNonAggregates = !orderByLimitReference.hasOrderByAggregate;\n\t\tbool canApproximate = orderByLimitReference.canApproximate;\n\n\t\tif (orderByNonAggregates)\n\t\t{\n\t\t\tworkerSortClauseList = sortClauseList;\n\t\t\tworkerSortClauseList = list_concat(workerSortClauseList, groupClauseList);\n\t\t}\n\t\telse if (canApproximate)\n\t\t{\n\t\t\tworkerSortClauseList = sortClauseList;\n\t\t}\n\t}\n\n\treturn workerSortClauseList;\n}\n\n\n/*\n * CanPushDownLimitApproximate checks if we can push down the limit clause to\n * the worker nodes, and get approximate and meaningful results. We can do this\n * only when: (1) the user has enabled the limit approximation and (2) the query\n * has order by clauses that are commutative.\n */\nstatic bool\nCanPushDownLimitApproximate(List *sortClauseList, List *targetList)\n{\n\tbool canApproximate = false;\n\n\t/* user hasn't enabled the limit approximation */\n\tif (LimitClauseRowFetchCount == DISABLE_LIMIT_APPROXIMATION)\n\t{\n\t\treturn false;\n\t}\n\n\tif (sortClauseList != NIL)\n\t{\n\t\tbool orderByNonCommutativeAggregate =\n\t\t\tHasOrderByNonCommutativeAggregate(sortClauseList, targetList);\n\t\tbool orderByComplex = HasOrderByComplexExpression(sortClauseList, targetList);\n\n\t\tif (!orderByNonCommutativeAggregate && !orderByComplex)\n\t\t{\n\t\t\tcanApproximate = true;\n\t\t}\n\t}\n\n\treturn canApproximate;\n}\n\n\n/*\n * HasOrderByAggregate walks over the given order by clauses, and checks if we\n * have an order by an aggregate function. If we do, the function returns true.\n */\nstatic bool\nHasOrderByAggregate(List *sortClauseList, List *targetList)\n{\n\tbool hasOrderByAggregate = false;\n\n\tSortGroupClause *sortClause = NULL;\n\tforeach_declared_ptr(sortClause, sortClauseList)\n\t{\n\t\tNode *sortExpression = get_sortgroupclause_expr(sortClause, targetList);\n\n\t\tbool containsAggregate = contain_aggs_of_level(sortExpression, 0);\n\t\tif (containsAggregate)\n\t\t{\n\t\t\thasOrderByAggregate = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn hasOrderByAggregate;\n}\n\n\n/*\n * HasOrderByNonCommutativeAggregate walks over the given order by clauses,\n * and checks if we have an order by an aggregate which is not commutative.\n */\nstatic bool\nHasOrderByNonCommutativeAggregate(List *sortClauseList, List *targetList)\n{\n\tbool hasOrderByNonCommutativeAggregate = false;\n\n\tSortGroupClause *sortClause = NULL;\n\tforeach_declared_ptr(sortClause, sortClauseList)\n\t{\n\t\tNode *sortExpression = get_sortgroupclause_expr(sortClause, targetList);\n\n\t\t/* if sort expression is an aggregate, check its type */\n\t\tif (IsA(sortExpression, Aggref))\n\t\t{\n\t\t\tAggref *aggregate = (Aggref *) sortExpression;\n\n\t\t\tAggregateType aggregateType = GetAggregateType(aggregate);\n\t\t\tif (aggregateType != AGGREGATE_MIN &&\n\t\t\t\taggregateType != AGGREGATE_MAX &&\n\t\t\t\taggregateType != AGGREGATE_SUM &&\n\t\t\t\taggregateType != AGGREGATE_COUNT &&\n\t\t\t\taggregateType != AGGREGATE_BIT_AND &&\n\t\t\t\taggregateType != AGGREGATE_BIT_OR &&\n\t\t\t\taggregateType != AGGREGATE_EVERY &&\n\t\t\t\taggregateType != AGGREGATE_ANY_VALUE)\n\t\t\t{\n\t\t\t\thasOrderByNonCommutativeAggregate = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn hasOrderByNonCommutativeAggregate;\n}\n\n\n/*\n * HasOrderByComplexExpression walks over the given order by clauses, and checks\n * if we have a nested expression that contains an aggregate function within it.\n * If we do, the function returns true.\n */\nstatic bool\nHasOrderByComplexExpression(List *sortClauseList, List *targetList)\n{\n\tbool hasOrderByComplexExpression = false;\n\n\tSortGroupClause *sortClause = NULL;\n\tforeach_declared_ptr(sortClause, sortClauseList)\n\t{\n\t\tNode *sortExpression = get_sortgroupclause_expr(sortClause, targetList);\n\n\t\t/* simple aggregate functions are ok */\n\t\tif (IsA(sortExpression, Aggref))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tbool nestedAggregate = contain_aggs_of_level(sortExpression, 0);\n\t\tif (nestedAggregate)\n\t\t{\n\t\t\thasOrderByComplexExpression = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn hasOrderByComplexExpression;\n}\n\n\n/*\n * HasOrderByHllType walks over the given order by clauses, and checks if any of\n * those clauses operate on hll data type. If they do, the function returns true.\n */\nstatic bool\nHasOrderByHllType(List *sortClauseList, List *targetList)\n{\n\tbool hasOrderByHllType = false;\n\n\t/* check whether HLL is loaded */\n\tOid hllId = get_extension_oid(HLL_EXTENSION_NAME, true);\n\tif (!OidIsValid(hllId))\n\t{\n\t\treturn hasOrderByHllType;\n\t}\n\n\tOid hllSchemaOid = get_extension_schema(hllId);\n\tOid hllTypeId = TypeOid(hllSchemaOid, HLL_TYPE_NAME);\n\n\tSortGroupClause *sortClause = NULL;\n\tforeach_declared_ptr(sortClause, sortClauseList)\n\t{\n\t\tNode *sortExpression = get_sortgroupclause_expr(sortClause, targetList);\n\n\t\tOid sortColumnTypeId = exprType(sortExpression);\n\t\tif (sortColumnTypeId == hllTypeId)\n\t\t{\n\t\t\thasOrderByHllType = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn hasOrderByHllType;\n}\n\n\n/*\n * ShouldProcessDistinctOrderAndLimitForWorker returns whether\n * ProcessDistinctClauseForWorkerQuery should be called. If not,\n * neither should ProcessLimitOrderByForWorkerQuery.\n */\nstatic bool\nShouldProcessDistinctOrderAndLimitForWorker(ExtendedOpNodeProperties *\n\t\t\t\t\t\t\t\t\t\t\textendedOpNodeProperties,\n\t\t\t\t\t\t\t\t\t\t\tbool pushingDownOriginalGrouping,\n\t\t\t\t\t\t\t\t\t\t\tNode *havingQual)\n{\n\tif (extendedOpNodeProperties->pullUpIntermediateRows)\n\t{\n\t\treturn false;\n\t}\n\n\t/* window functions must be evaluated beforehand */\n\tif (!extendedOpNodeProperties->onlyPushableWindowFunctions)\n\t{\n\t\treturn false;\n\t}\n\n\tif (extendedOpNodeProperties->pushDownGroupingAndHaving)\n\t{\n\t\treturn true;\n\t}\n\n\t/* If the same GROUP BY is being pushed down and there's no HAVING,\n\t * then the push down logic will be able to handle this scenario.\n\t */\n\tif (pushingDownOriginalGrouping && havingQual == NULL)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * WorkerColumnName returns a palloc'd string for being the resname of a TargetEntry.\n */\nchar *\nWorkerColumnName(AttrNumber resno)\n{\n\tStringInfoData name = { 0 };\n\tinitStringInfo(&name);\n\tappendStringInfo(&name, WORKER_COLUMN_FORMAT, resno);\n\n\treturn name.data;\n}\n\n\n/*\n * IsGroupBySubsetOfDistinct checks whether each clause in group clauses also\n * exists in the distinct clauses. Note that, empty group clause is not a subset\n * of distinct clause.\n */\nbool\nIsGroupBySubsetOfDistinct(List *groupClauses, List *distinctClauses)\n{\n\t/* There must be a group clause */\n\tif (list_length(groupClauses) == 0)\n\t{\n\t\treturn false;\n\t}\n\n\tSortGroupClause *groupClause = NULL;\n\tforeach_declared_ptr(groupClause, groupClauses)\n\t{\n\t\tbool isFound = false;\n\n\t\tSortGroupClause *distinctClause = NULL;\n\t\tforeach_declared_ptr(distinctClause, distinctClauses)\n\t\t{\n\t\t\tif (groupClause->tleSortGroupRef == distinctClause->tleSortGroupRef)\n\t\t\t{\n\t\t\t\tisFound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * If we can't find any member of group clause in the distinct clause,\n\t\t * that means group clause is not a subset of distinct clause.\n\t\t */\n\t\tif (!isFound)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/multi_logical_planner.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_logical_planner.c\n *\n * Routines for constructing a logical plan tree from the given Query tree\n * structure. This new logical plan is based on multi-relational algebra rules.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/heapam.h\"\n#include \"access/nbtree.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_class.h\"\n#include \"commands/defrem.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/pathnodes.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/prep.h\"\n#include \"optimizer/tlist.h\"\n#include \"parser/parsetree.h\"\n#include \"utils/builtins.h\"\n#include \"utils/datum.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/relcache.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n#if PG_VERSION_NUM >= PG_VERSION_18\ntypedef OpIndexInterpretation OpBtreeInterpretation;\n#endif\n\n\n#include \"distributed/citus_clauses.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/query_pushdown_planning.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relation_restriction_equivalence.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/* Struct to differentiate different qualifier types in an expression tree walker */\ntypedef struct QualifierWalkerContext\n{\n\tList *baseQualifierList;\n\tList *outerJoinQualifierList;\n} QualifierWalkerContext;\n\n\n/* Function pointer type definition for apply join rule functions */\ntypedef MultiNode *(*RuleApplyFunction) (MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t\t\t\t\t\t List *partitionColumnList, JoinType joinType,\n\t\t\t\t\t\t\t\t\t\t List *joinClauses);\n\ntypedef bool (*CheckNodeFunc)(Node *);\n\nstatic RuleApplyFunction RuleApplyFunctionArray[JOIN_RULE_LAST] = { 0 }; /* join rules */\n\n/* Local functions forward declarations */\nstatic FieldSelect * CompositeFieldRecursive(Expr *expression, Query *query);\nstatic Oid NodeTryGetRteRelid(Node *node);\nstatic bool FullCompositeFieldList(List *compositeFieldList);\nstatic bool HasUnsupportedJoinWalker(Node *node, void *context);\nstatic bool ErrorHintRequired(const char *errorHint, Query *queryTree);\nstatic bool HasComplexRangeTableType(Query *queryTree);\nstatic bool IsReadIntermediateResultArrayFunction(Node *node);\nstatic bool IsCitusExtraDataContainerFunc(Node *node);\nstatic bool IsFunctionWithOid(Node *node, Oid funcOid);\nstatic bool IsGroupingFunc(Node *node);\nstatic bool ExtractFromExpressionWalker(Node *node,\n\t\t\t\t\t\t\t\t\t\tQualifierWalkerContext *walkerContext);\nstatic List * MultiTableNodeList(List *tableEntryList, List *rangeTableList);\nstatic List * AddMultiCollectNodes(List *tableNodeList);\nstatic MultiNode * MultiJoinTree(List *joinOrderList, List *collectTableList,\n\t\t\t\t\t\t\t\t List *joinClauseList);\nstatic MultiCollect * CollectNodeForTable(List *collectTableList, uint32 rangeTableId);\nstatic MultiSelect * MultiSelectNode(List *whereClauseList);\nstatic bool IsSelectClause(Node *clause);\n\n/* Local functions forward declarations for applying joins */\nstatic MultiNode * ApplyJoinRule(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t\t\t\t JoinRuleType ruleType, List *partitionColumnList,\n\t\t\t\t\t\t\t\t JoinType joinType, List *joinClauseList);\nstatic RuleApplyFunction JoinRuleApplyFunction(JoinRuleType ruleType);\nstatic MultiNode * ApplyReferenceJoin(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t\t\t\t\t  List *partitionColumnList, JoinType joinType,\n\t\t\t\t\t\t\t\t\t  List *joinClauses);\nstatic MultiNode * ApplyLocalJoin(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t\t\t\t  List *partitionColumnList, JoinType joinType,\n\t\t\t\t\t\t\t\t  List *joinClauses);\nstatic MultiNode * ApplySingleRangePartitionJoin(MultiNode *leftNode,\n\t\t\t\t\t\t\t\t\t\t\t\t MultiNode *rightNode,\n\t\t\t\t\t\t\t\t\t\t\t\t List *partitionColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t JoinType joinType,\n\t\t\t\t\t\t\t\t\t\t\t\t List *applicableJoinClauses);\nstatic MultiNode * ApplySingleHashPartitionJoin(MultiNode *leftNode,\n\t\t\t\t\t\t\t\t\t\t\t\tMultiNode *rightNode,\n\t\t\t\t\t\t\t\t\t\t\t\tList *partitionColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\tJoinType joinType,\n\t\t\t\t\t\t\t\t\t\t\t\tList *applicableJoinClauses);\nstatic MultiJoin * ApplySinglePartitionJoin(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t\t\t\t\t\t\tList *partitionColumnList, JoinType joinType,\n\t\t\t\t\t\t\t\t\t\t\tList *joinClauses);\nstatic MultiNode * ApplyDualPartitionJoin(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t\t\t\t\t\t  List *partitionColumnList, JoinType joinType,\n\t\t\t\t\t\t\t\t\t\t  List *joinClauses);\nstatic MultiNode * ApplyCartesianProductReferenceJoin(MultiNode *leftNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  MultiNode *rightNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *partitionColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  JoinType joinType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *joinClauses);\nstatic MultiNode * ApplyCartesianProduct(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t\t\t\t\t\t List *partitionColumnList, JoinType joinType,\n\t\t\t\t\t\t\t\t\t\t List *joinClauses);\n\n\n/*\n * MultiLogicalPlanCreate takes in both the original query and its corresponding modified\n * query tree yield by the standard planner. It uses helper functions to create logical\n * plan and adds a root node to top of it. The original query is only used for subquery\n * pushdown planning.\n *\n * We also pass queryTree and plannerRestrictionContext to the planner. They\n * are primarily used to decide whether the subquery is safe to pushdown.\n * If not, it helps to produce meaningful error messages for subquery\n * pushdown planning.\n */\nMultiTreeRoot *\nMultiLogicalPlanCreate(Query *originalQuery, Query *queryTree,\n\t\t\t\t\t   PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tMultiNode *multiQueryNode = NULL;\n\n\n\tif (ShouldUseSubqueryPushDown(originalQuery, queryTree, plannerRestrictionContext))\n\t{\n\t\tmultiQueryNode = SubqueryMultiNodeTree(originalQuery, queryTree,\n\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\n\t}\n\telse\n\t{\n\t\tmultiQueryNode = MultiNodeTree(queryTree);\n\t}\n\n\t/* add a root node to serve as the permanent handle to the tree */\n\tMultiTreeRoot *rootNode = CitusMakeNode(MultiTreeRoot);\n\tSetChild((MultiUnaryNode *) rootNode, multiQueryNode);\n\n\treturn rootNode;\n}\n\n\n/*\n * FindNodeMatchingCheckFunction finds a node for which the checker function returns true.\n *\n * To call this function directly with an RTE, use:\n * range_table_walker(rte, FindNodeMatchingCheckFunction, checker, QTW_EXAMINE_RTES_BEFORE)\n */\nbool\nFindNodeMatchingCheckFunction(Node *node, CheckNodeFunc checker)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (checker(node))\n\t{\n\t\treturn true;\n\t}\n\n\tif (IsA(node, RangeTblEntry))\n\t{\n\t\t/* query_tree_walker descends into RTEs */\n\t\treturn false;\n\t}\n\telse if (IsA(node, Query))\n\t{\n\t\treturn query_tree_walker((Query *) node, FindNodeMatchingCheckFunction, checker,\n\t\t\t\t\t\t\t\t QTW_EXAMINE_RTES_BEFORE);\n\t}\n\n\treturn expression_tree_walker(node, FindNodeMatchingCheckFunction, checker);\n}\n\n\n/*\n * TargetListOnPartitionColumn checks if at least one target list entry is on\n * partition column.\n */\nbool\nTargetListOnPartitionColumn(Query *query, List *targetEntryList)\n{\n\tbool targetListOnPartitionColumn = false;\n\tList *compositeFieldList = NIL;\n\n\tListCell *targetEntryCell = NULL;\n\tforeach(targetEntryCell, targetEntryList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell);\n\t\tExpr *targetExpression = targetEntry->expr;\n\n\t\tbool skipOuterVars = true;\n\t\tbool isPartitionColumn = IsPartitionColumn(targetExpression, query,\n\t\t\t\t\t\t\t\t\t\t\t\t   skipOuterVars);\n\t\tVar *column = NULL;\n\t\tRangeTblEntry *rte = NULL;\n\n\t\tFindReferencedTableColumn(targetExpression, NIL, query, &column, &rte,\n\t\t\t\t\t\t\t\t  skipOuterVars);\n\t\tOid relationId = rte ? rte->relid : InvalidOid;\n\n\t\t/*\n\t\t * If the expression belongs to a non-distributed table continue searching for\n\t\t * other partition keys.\n\t\t */\n\t\tif (IsCitusTable(relationId) && !HasDistributionKey(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* append-distributed tables do not have a strict partition column */\n\t\tif (IsCitusTableType(relationId, APPEND_DISTRIBUTED))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (isPartitionColumn)\n\t\t{\n\t\t\tFieldSelect *compositeField = CompositeFieldRecursive(targetExpression,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  query);\n\t\t\tif (compositeField)\n\t\t\t{\n\t\t\t\tcompositeFieldList = lappend(compositeFieldList, compositeField);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttargetListOnPartitionColumn = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* check composite fields */\n\tif (!targetListOnPartitionColumn)\n\t{\n\t\tbool fullCompositeFieldList = FullCompositeFieldList(compositeFieldList);\n\t\tif (fullCompositeFieldList)\n\t\t{\n\t\t\ttargetListOnPartitionColumn = true;\n\t\t}\n\t}\n\n\t/*\n\t * We could still behave as if the target list is on partition column if\n\t * range table entries don't contain a distributed table.\n\t */\n\tif (!targetListOnPartitionColumn)\n\t{\n\t\tif (!FindNodeMatchingCheckFunctionInRangeTableList(query->rtable,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   IsTableWithDistKeyRTE))\n\t\t{\n\t\t\ttargetListOnPartitionColumn = true;\n\t\t}\n\t}\n\n\treturn targetListOnPartitionColumn;\n}\n\n\n/*\n * FindNodeMatchingCheckFunctionInRangeTableList finds a node for which the checker\n * function returns true.\n *\n * FindNodeMatchingCheckFunctionInRangeTableList relies on\n * FindNodeMatchingCheckFunction() but only considers the range table entries.\n */\nbool\nFindNodeMatchingCheckFunctionInRangeTableList(List *rtable, CheckNodeFunc checker)\n{\n\tint rtWalkFlags = QTW_EXAMINE_RTES_BEFORE;\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t/*\n\t * PG18+: Do not descend into GROUP BY expressions subqueries, they\n\t * have already been visited as recursive planning is depth-first.\n\t */\n\trtWalkFlags |= QTW_IGNORE_GROUPEXPRS;\n#endif\n\n\treturn range_table_walker(rtable, FindNodeMatchingCheckFunction, checker,\n\t\t\t\t\t\t\t  rtWalkFlags);\n}\n\n\n/*\n * NodeTryGetRteRelid returns the relid of the given RTE_RELATION RangeTableEntry.\n * Returns InvalidOid if any of these assumptions fail for given node.\n */\nstatic Oid\nNodeTryGetRteRelid(Node *node)\n{\n\tif (node == NULL)\n\t{\n\t\treturn InvalidOid;\n\t}\n\n\tif (!IsA(node, RangeTblEntry))\n\t{\n\t\treturn InvalidOid;\n\t}\n\n\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) node;\n\n\tif (rangeTableEntry->rtekind != RTE_RELATION)\n\t{\n\t\treturn InvalidOid;\n\t}\n\n\treturn rangeTableEntry->relid;\n}\n\n\n/*\n * IsCitusTableRTE gets a node and returns true if the node is a\n * range table relation entry that points to a distributed relation.\n */\nbool\nIsCitusTableRTE(Node *node)\n{\n\tOid relationId = NodeTryGetRteRelid(node);\n\treturn relationId != InvalidOid && IsCitusTable(relationId);\n}\n\n\n/*\n * IsDistributedOrReferenceTableRTE returns true if the given node\n * is eeither a distributed(hash/range/append) or reference table.\n */\nbool\nIsDistributedOrReferenceTableRTE(Node *node)\n{\n\tOid relationId = NodeTryGetRteRelid(node);\n\tif (!OidIsValid(relationId))\n\t{\n\t\treturn false;\n\t}\n\treturn IsCitusTableType(relationId, DISTRIBUTED_TABLE) ||\n\t\t   IsCitusTableType(relationId, REFERENCE_TABLE);\n}\n\n\n/*\n * IsDistributedTableRTE gets a node and returns true if the node\n * is a range table relation entry that points to a distributed relation,\n * returning false still if the relation is a reference table.\n */\nbool\nIsDistributedTableRTE(Node *node)\n{\n\tOid relationId = NodeTryGetRteRelid(node);\n\treturn relationId != InvalidOid && IsCitusTableType(relationId, DISTRIBUTED_TABLE);\n}\n\n\n/*\n * IsReferenceTableRTE gets a node and returns true if the node\n * is a range table relation entry that points to a reference table.\n */\nbool\nIsReferenceTableRTE(Node *node)\n{\n\tOid relationId = NodeTryGetRteRelid(node);\n\treturn relationId != InvalidOid && IsCitusTableType(relationId, REFERENCE_TABLE);\n}\n\n\n/*\n * IsTableWithDistKeyRTE gets a node and returns true if the node\n * is a range table relation entry that points to a distributed table\n * that has a distribution column.\n */\nbool\nIsTableWithDistKeyRTE(Node *node)\n{\n\tOid relationId = NodeTryGetRteRelid(node);\n\treturn relationId != InvalidOid && IsCitusTable(relationId) &&\n\t\t   HasDistributionKey(relationId);\n}\n\n\n/*\n * FullCompositeFieldList gets a composite field list, and checks if all fields\n * of composite type are used in the list.\n */\nstatic bool\nFullCompositeFieldList(List *compositeFieldList)\n{\n\tbool fullCompositeFieldList = true;\n\tbool *compositeFieldArray = NULL;\n\tuint32 compositeFieldCount = 0;\n\n\tListCell *fieldSelectCell = NULL;\n\tforeach(fieldSelectCell, compositeFieldList)\n\t{\n\t\tFieldSelect *fieldSelect = (FieldSelect *) lfirst(fieldSelectCell);\n\n\t\tExpr *fieldExpression = fieldSelect->arg;\n\t\tif (!IsA(fieldExpression, Var))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (compositeFieldArray == NULL)\n\t\t{\n\t\t\tVar *compositeColumn = (Var *) fieldExpression;\n\t\t\tOid compositeTypeId = compositeColumn->vartype;\n\t\t\tOid compositeRelationId = get_typ_typrelid(compositeTypeId);\n\n\t\t\t/* get composite type attribute count */\n\t\t\tRelation relation = relation_open(compositeRelationId, AccessShareLock);\n\t\t\tcompositeFieldCount = relation->rd_att->natts;\n\t\t\tcompositeFieldArray = palloc0(compositeFieldCount * sizeof(bool));\n\t\t\trelation_close(relation, AccessShareLock);\n\n\t\t\tfor (uint32 compositeFieldIndex = 0;\n\t\t\t\t compositeFieldIndex < compositeFieldCount;\n\t\t\t\t compositeFieldIndex++)\n\t\t\t{\n\t\t\t\tcompositeFieldArray[compositeFieldIndex] = false;\n\t\t\t}\n\t\t}\n\n\t\tuint32 compositeFieldIndex = fieldSelect->fieldnum - 1;\n\t\tcompositeFieldArray[compositeFieldIndex] = true;\n\t}\n\n\tfor (uint32 fieldIndex = 0; fieldIndex < compositeFieldCount; fieldIndex++)\n\t{\n\t\tif (!compositeFieldArray[fieldIndex])\n\t\t{\n\t\t\tfullCompositeFieldList = false;\n\t\t}\n\t}\n\n\tif (compositeFieldCount == 0)\n\t{\n\t\tfullCompositeFieldList = false;\n\t}\n\n\treturn fullCompositeFieldList;\n}\n\n\n/*\n * CompositeFieldRecursive recursively finds composite field in the query tree\n * referred by given expression. If expression does not refer to a composite\n * field, then it returns NULL.\n *\n * If expression is a field select we directly return composite field. If it is\n * a column is referenced from a subquery, then we recursively check that subquery\n * until we reach the source of that column, and find composite field. If this\n * column is referenced from join range table entry, then we resolve which join\n * column it refers and recursively use this column with the same query.\n */\nstatic FieldSelect *\nCompositeFieldRecursive(Expr *expression, Query *query)\n{\n\tFieldSelect *compositeField = NULL;\n\tList *rangetableList = query->rtable;\n\tVar *candidateColumn = NULL;\n\n\tif (IsA(expression, FieldSelect))\n\t{\n\t\tcompositeField = (FieldSelect *) expression;\n\t\treturn compositeField;\n\t}\n\n\tif (IsA(expression, Var))\n\t{\n\t\tcandidateColumn = (Var *) expression;\n\t}\n\telse\n\t{\n\t\treturn NULL;\n\t}\n\n\tIndex rangeTableEntryIndex = candidateColumn->varno - 1;\n\tRangeTblEntry *rangeTableEntry = list_nth(rangetableList, rangeTableEntryIndex);\n\n\tif (rangeTableEntry->rtekind == RTE_SUBQUERY)\n\t{\n\t\tQuery *subquery = rangeTableEntry->subquery;\n\t\tList *targetEntryList = subquery->targetList;\n\t\tAttrNumber targetEntryIndex = candidateColumn->varattno - 1;\n\t\tTargetEntry *subqueryTargetEntry = list_nth(targetEntryList, targetEntryIndex);\n\n\t\tExpr *subqueryExpression = subqueryTargetEntry->expr;\n\t\tcompositeField = CompositeFieldRecursive(subqueryExpression, subquery);\n\t}\n\telse if (rangeTableEntry->rtekind == RTE_JOIN)\n\t{\n\t\tList *joinColumnList = rangeTableEntry->joinaliasvars;\n\t\tAttrNumber joinColumnIndex = candidateColumn->varattno - 1;\n\t\tExpr *joinColumn = list_nth(joinColumnList, joinColumnIndex);\n\n\t\tcompositeField = CompositeFieldRecursive(joinColumn, query);\n\t}\n\n\treturn compositeField;\n}\n\n\n/*\n * SubqueryEntryList finds the subquery nodes in the range table entry list, and\n * builds a list of subquery range table entries from these subquery nodes. Range\n * table entry list also includes subqueries which are pulled up. We don't want\n * to add pulled up subqueries to list, so we walk over join tree indexes and\n * check range table entries referenced in the join tree.\n */\nList *\nSubqueryEntryList(Query *queryTree)\n{\n\tList *rangeTableList = queryTree->rtable;\n\tList *subqueryEntryList = NIL;\n\tList *joinTreeTableIndexList = NIL;\n\tListCell *joinTreeTableIndexCell = NULL;\n\n\t/*\n\t * Extract all range table indexes from the join tree. Note that here we\n\t * only walk over range table entries at this level and do not recurse into\n\t * subqueries.\n\t */\n\tExtractRangeTableIndexWalker((Node *) queryTree->jointree, &joinTreeTableIndexList);\n\tforeach(joinTreeTableIndexCell, joinTreeTableIndexList)\n\t{\n\t\t/*\n\t\t * Join tree's range table index starts from 1 in the query tree. But,\n\t\t * list indexes start from 0.\n\t\t */\n\t\tint joinTreeTableIndex = lfirst_int(joinTreeTableIndexCell);\n\t\tint rangeTableListIndex = joinTreeTableIndex - 1;\n\t\tRangeTblEntry *rangeTableEntry =\n\t\t\t(RangeTblEntry *) list_nth(rangeTableList, rangeTableListIndex);\n\n\t\tif (rangeTableEntry->rtekind == RTE_SUBQUERY)\n\t\t{\n\t\t\tsubqueryEntryList = lappend(subqueryEntryList, rangeTableEntry);\n\t\t}\n\t}\n\n\treturn subqueryEntryList;\n}\n\n\n/*\n * MultiNodeTree takes in a parsed query tree and uses that tree to construct a\n * logical plan. This plan is based on multi-relational algebra. This function\n * creates the logical plan in several steps.\n *\n * First, the function checks if there is a subquery. If there is a subquery\n * it recursively creates nested multi trees. If this query has a subquery, the\n * function does not create any join trees and jumps to last step.\n *\n * If there is no subquery, the function calculates the join order using tables\n * in the query and join clauses between the tables. Second, the function\n * starts building the logical plan from the bottom-up, and begins with the table\n * and collect nodes. Third, the function builds the join tree using the join\n * order information and table nodes.\n *\n * In the last step, the function adds the select, project, aggregate, sort,\n * group, and limit nodes if they appear in the original query tree.\n */\nMultiNode *\nMultiNodeTree(Query *queryTree)\n{\n\tList *rangeTableList = queryTree->rtable;\n\tList *targetEntryList = queryTree->targetList;\n\tList *joinClauseList = NIL;\n\tList *joinOrderList = NIL;\n\tList *tableEntryList = NIL;\n\tList *tableNodeList = NIL;\n\tList *collectTableList = NIL;\n\tMultiNode *joinTreeNode = NULL;\n\tMultiNode *currentTopNode = NULL;\n\n\t/* verify we can perform distributed planning on this query */\n\tDeferredErrorMessage *unsupportedQueryError = DeferErrorIfQueryNotSupported(\n\t\tqueryTree);\n\tif (unsupportedQueryError != NULL)\n\t{\n\t\tRaiseDeferredError(unsupportedQueryError, ERROR);\n\t}\n\n\t/* extract where clause qualifiers and verify we can plan for them */\n\tList *whereClauseList = WhereClauseList(queryTree->jointree);\n\tunsupportedQueryError = DeferErrorIfUnsupportedClause(whereClauseList);\n\tif (unsupportedQueryError)\n\t{\n\t\tRaiseDeferredErrorInternal(unsupportedQueryError, ERROR);\n\t}\n\n\t/*\n\t * If we have a subquery, build a multi table node for the subquery and\n\t * add a collect node on top of the multi table node.\n\t */\n\tList *subqueryEntryList = SubqueryEntryList(queryTree);\n\tif (subqueryEntryList != NIL)\n\t{\n\t\tMultiCollect *subqueryCollectNode = CitusMakeNode(MultiCollect);\n\t\tListCell *columnCell = NULL;\n\n\t\t/* we only support single subquery in the entry list */\n\t\tAssert(list_length(subqueryEntryList) == 1);\n\n\t\tRangeTblEntry *subqueryRangeTableEntry = (RangeTblEntry *) linitial(\n\t\t\tsubqueryEntryList);\n\t\tQuery *subqueryTree = subqueryRangeTableEntry->subquery;\n\n\t\t/* ensure if subquery satisfies preconditions */\n\t\tAssert(DeferErrorIfUnsupportedSubqueryRepartition(subqueryTree) == NULL);\n\n\t\tMultiTable *subqueryNode = CitusMakeNode(MultiTable);\n\t\tsubqueryNode->relationId = SUBQUERY_RELATION_ID;\n\t\tsubqueryNode->rangeTableId = SUBQUERY_RANGE_TABLE_ID;\n\t\tsubqueryNode->partitionColumn = NULL;\n\t\tsubqueryNode->alias = NULL;\n\t\tsubqueryNode->referenceNames = NULL;\n\n\t\t/*\n\t\t * We disregard pulled subqueries. This changes order of range table list.\n\t\t * We do not allow subquery joins, so we will have only one range table\n\t\t * entry in range table list after dropping pulled subquery. For this\n\t\t * reason, here we are updating columns in the most outer query for where\n\t\t * clause list and target list accordingly.\n\t\t */\n\t\tAssert(list_length(subqueryEntryList) == 1);\n\n\t\tList *whereClauseColumnList = pull_var_clause_default((Node *) whereClauseList);\n\t\tList *targetListColumnList = pull_var_clause_default((Node *) targetEntryList);\n\n\t\tList *columnList = list_concat(whereClauseColumnList, targetListColumnList);\n\t\tforeach(columnCell, columnList)\n\t\t{\n\t\t\tVar *column = (Var *) lfirst(columnCell);\n\t\t\tcolumn->varno = 1;\n\t\t}\n\n\t\t/* recursively create child nested multitree */\n\t\tMultiNode *subqueryExtendedNode = MultiNodeTree(subqueryTree);\n\n\t\tSetChild((MultiUnaryNode *) subqueryCollectNode, (MultiNode *) subqueryNode);\n\t\tSetChild((MultiUnaryNode *) subqueryNode, subqueryExtendedNode);\n\n\t\tcurrentTopNode = (MultiNode *) subqueryCollectNode;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * We calculate the join order using the list of tables in the query and\n\t\t * the join clauses between them. Note that this function owns the table\n\t\t * entry list's memory, and JoinOrderList() shallow copies the list's\n\t\t * elements.\n\t\t */\n\t\tjoinClauseList = JoinClauseList(whereClauseList);\n\t\ttableEntryList = UsedTableEntryList(queryTree);\n\n\t\t/* build the list of multi table nodes */\n\t\ttableNodeList = MultiTableNodeList(tableEntryList, rangeTableList);\n\n\t\t/* add collect nodes on top of the multi table nodes */\n\t\tcollectTableList = AddMultiCollectNodes(tableNodeList);\n\n\t\t/* find best join order for commutative inner joins */\n\t\tjoinOrderList = JoinOrderList(tableEntryList, joinClauseList);\n\n\t\t/* build join tree using the join order and collected tables */\n\t\tjoinTreeNode = MultiJoinTree(joinOrderList, collectTableList, joinClauseList);\n\n\t\tcurrentTopNode = joinTreeNode;\n\t}\n\n\tAssert(currentTopNode != NULL);\n\n\t/* build select node if the query has selection criteria */\n\tMultiSelect *selectNode = MultiSelectNode(whereClauseList);\n\tif (selectNode != NULL)\n\t{\n\t\tSetChild((MultiUnaryNode *) selectNode, currentTopNode);\n\t\tcurrentTopNode = (MultiNode *) selectNode;\n\t}\n\n\t/* build project node for the columns to project */\n\tMultiProject *projectNode = MultiProjectNode(targetEntryList);\n\tSetChild((MultiUnaryNode *) projectNode, currentTopNode);\n\tcurrentTopNode = (MultiNode *) projectNode;\n\n\t/*\n\t * We build the extended operator node to capture aggregate functions, group\n\t * clauses, sort clauses, limit/offset clauses, and expressions. We need to\n\t * distinguish between aggregates and expressions; and we address this later\n\t * in the logical optimizer.\n\t */\n\tMultiExtendedOp *extendedOpNode = MultiExtendedOpNode(queryTree, queryTree);\n\tSetChild((MultiUnaryNode *) extendedOpNode, currentTopNode);\n\tcurrentTopNode = (MultiNode *) extendedOpNode;\n\n\treturn currentTopNode;\n}\n\n\n/*\n * ContainsReadIntermediateResultFunction determines whether an expression tree\n * contains a call to the read_intermediate_result function.\n */\nbool\nContainsReadIntermediateResultFunction(Node *node)\n{\n\treturn FindNodeMatchingCheckFunction(node, IsReadIntermediateResultFunction);\n}\n\n\n/*\n * ContainsReadIntermediateResultArrayFunction determines whether an expression\n * tree contains a call to the read_intermediate_results(result_ids, format)\n * function.\n */\nbool\nContainsReadIntermediateResultArrayFunction(Node *node)\n{\n\treturn FindNodeMatchingCheckFunction(node, IsReadIntermediateResultArrayFunction);\n}\n\n\n/*\n * IsReadIntermediateResultFunction determines whether a given node is a function call\n * to the read_intermediate_result function.\n */\nbool\nIsReadIntermediateResultFunction(Node *node)\n{\n\treturn IsFunctionWithOid(node, CitusReadIntermediateResultFuncId());\n}\n\n\n/*\n * IsReadIntermediateResultArrayFunction determines whether a given node is a\n * function call to the read_intermediate_results(result_ids, format) function.\n */\nstatic bool\nIsReadIntermediateResultArrayFunction(Node *node)\n{\n\treturn IsFunctionWithOid(node, CitusReadIntermediateResultArrayFuncId());\n}\n\n\n/*\n * IsCitusExtraDataContainerRelation determines whether a range table entry contains a\n * call to the citus_extradata_container function.\n */\nbool\nIsCitusExtraDataContainerRelation(RangeTblEntry *rte)\n{\n\tif (rte->rtekind != RTE_FUNCTION || list_length(rte->functions) != 1)\n\t{\n\t\t/* avoid more expensive checks below for non-functions */\n\t\treturn false;\n\t}\n\n\tif (!CitusHasBeenLoaded() || !CheckCitusVersion(DEBUG5))\n\t{\n\t\treturn false;\n\t}\n\n\treturn FindNodeMatchingCheckFunction((Node *) rte->functions,\n\t\t\t\t\t\t\t\t\t\t IsCitusExtraDataContainerFunc);\n}\n\n\n/*\n * IsCitusExtraDataContainerFunc determines whether a given node is a function call\n * to the citus_extradata_container function.\n */\nstatic bool\nIsCitusExtraDataContainerFunc(Node *node)\n{\n\treturn IsFunctionWithOid(node, CitusExtraDataContainerFuncId());\n}\n\n\n/*\n * IsFunctionWithOid determines whether a given node is a function call\n * to the read_intermediate_result function.\n */\nstatic bool\nIsFunctionWithOid(Node *node, Oid funcOid)\n{\n\tif (IsA(node, FuncExpr))\n\t{\n\t\tFuncExpr *funcExpr = (FuncExpr *) node;\n\n\t\tif (funcExpr->funcid == funcOid)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * IsGroupingFunc returns whether node is a GroupingFunc.\n */\nstatic bool\nIsGroupingFunc(Node *node)\n{\n\treturn IsA(node, GroupingFunc);\n}\n\n\n/*\n * FindIntermediateResultIdIfExists extracts the id of the intermediate result\n * if the given RTE contains a read_intermediate_results function, NULL otherwise\n */\nchar *\nFindIntermediateResultIdIfExists(RangeTblEntry *rte)\n{\n\tchar *resultId = NULL;\n\n\tAssert(rte->rtekind == RTE_FUNCTION);\n\n\tList *functionList = rte->functions;\n\tRangeTblFunction *rangeTblfunction = (RangeTblFunction *) linitial(functionList);\n\tFuncExpr *funcExpr = (FuncExpr *) rangeTblfunction->funcexpr;\n\n\tif (IsReadIntermediateResultFunction((Node *) funcExpr))\n\t{\n\t\tConst *resultIdConst = linitial(funcExpr->args);\n\n\t\tif (!resultIdConst->constisnull)\n\t\t{\n\t\t\tresultId = TextDatumGetCString(resultIdConst->constvalue);\n\t\t}\n\t}\n\n\treturn resultId;\n}\n\n\n/*\n * ErrorIfQueryNotSupported checks that we can perform distributed planning for\n * the given query. The checks in this function will be removed as we support\n * more functionality in our distributed planning.\n */\nDeferredErrorMessage *\nDeferErrorIfQueryNotSupported(Query *queryTree)\n{\n\tchar *errorMessage = NULL;\n\tbool preconditionsSatisfied = true;\n\tconst char *errorHint = NULL;\n\tconst char *joinHint = \"Consider joining tables on partition column and have \"\n\t\t\t\t\t\t   \"equal filter on joining columns.\";\n\tconst char *filterHint = \"Consider using an equality filter on the distributed \"\n\t\t\t\t\t\t\t \"table's partition column.\";\n\n\tif (queryTree->setOperations)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorMessage = \"could not run distributed query with UNION, INTERSECT, or \"\n\t\t\t\t\t   \"EXCEPT\";\n\t\terrorHint = filterHint;\n\t}\n\n\tif (queryTree->hasRecursive)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorMessage = \"could not run distributed query with RECURSIVE\";\n\t\terrorHint = filterHint;\n\t}\n\n\tif (queryTree->cteList)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorMessage = \"could not run distributed query with common table expressions\";\n\t\terrorHint = filterHint;\n\t}\n\n\tif (queryTree->hasForUpdate)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorMessage = \"could not run distributed query with FOR UPDATE/SHARE commands\";\n\t\terrorHint = filterHint;\n\t}\n\n\tif (queryTree->groupingSets)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorMessage = \"could not run distributed query with GROUPING SETS, CUBE, \"\n\t\t\t\t\t   \"or ROLLUP\";\n\t\terrorHint = filterHint;\n\t}\n\n\tif (FindNodeMatchingCheckFunction((Node *) queryTree, IsGroupingFunc))\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorMessage = \"could not run distributed query with GROUPING\";\n\t\terrorHint = filterHint;\n\t}\n\n\tbool hasUnsupportedJoin = HasUnsupportedJoinWalker((Node *) queryTree->jointree,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   NULL);\n\tif (hasUnsupportedJoin)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorMessage = \"could not run distributed query with join types other than \"\n\t\t\t\t\t   \"INNER or OUTER JOINS\";\n\t\terrorHint = joinHint;\n\t}\n\n\tbool hasComplexRangeTableType = HasComplexRangeTableType(queryTree);\n\tif (hasComplexRangeTableType)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorMessage = \"could not run distributed query with complex table expressions\";\n\t\terrorHint = filterHint;\n\t}\n\n\tif (FindNodeMatchingCheckFunction((Node *) queryTree->limitCount, IsNodeSubquery))\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorMessage = \"subquery in LIMIT is not supported in multi-shard queries\";\n\t}\n\n\tif (FindNodeMatchingCheckFunction((Node *) queryTree->limitOffset, IsNodeSubquery))\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorMessage = \"subquery in OFFSET is not supported in multi-shard queries\";\n\t}\n\n\tRTEListProperties *queryRteListProperties = GetRTEListPropertiesForQuery(queryTree);\n\tif (queryRteListProperties->hasCitusLocalTable ||\n\t\tqueryRteListProperties->hasPostgresLocalTable)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorMessage = \"direct joins between distributed and local tables are \"\n\t\t\t\t\t   \"not supported\";\n\t\terrorHint = LOCAL_TABLE_SUBQUERY_CTE_HINT;\n\t}\n\n\t/* finally check and error out if not satisfied */\n\tif (!preconditionsSatisfied)\n\t{\n\t\tbool showHint = ErrorHintRequired(errorHint, queryTree);\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t errorMessage, NULL,\n\t\t\t\t\t\t\t showHint ? errorHint : NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * HasUnsupportedJoinWalker returns tree if the query contains an unsupported\n * join type. We currently support inner, left, right, full and anti joins.\n * Semi joins are not supported. A full description of these join types is\n * included in nodes/nodes.h.\n */\nstatic bool\nHasUnsupportedJoinWalker(Node *node, void *context)\n{\n\tbool hasUnsupportedJoin = false;\n\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, JoinExpr))\n\t{\n\t\tJoinExpr *joinExpr = (JoinExpr *) node;\n\t\tJoinType joinType = joinExpr->jointype;\n\t\tbool outerJoin = IS_OUTER_JOIN(joinType);\n\t\tif (!outerJoin && joinType != JOIN_INNER && joinType != JOIN_SEMI)\n\t\t{\n\t\t\thasUnsupportedJoin = true;\n\t\t}\n\t}\n\n\tif (!hasUnsupportedJoin)\n\t{\n\t\thasUnsupportedJoin = expression_tree_walker(node, HasUnsupportedJoinWalker,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL);\n\t}\n\n\treturn hasUnsupportedJoin;\n}\n\n\n/*\n * ErrorHintRequired returns true if error hint shold be displayed with the\n * query error message. Error hint is valid only for queries involving reference\n * and hash partitioned tables. If more than one hash distributed table is\n * present we display the hint only if the tables are colocated. If the query\n * only has reference table(s), then it is handled by router planner.\n */\nstatic bool\nErrorHintRequired(const char *errorHint, Query *queryTree)\n{\n\tList *distributedRelationIdList = DistributedRelationIdList(queryTree);\n\tListCell *relationIdCell = NULL;\n\tList *colocationIdList = NIL;\n\n\tif (errorHint == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tforeach(relationIdCell, distributedRelationIdList)\n\t{\n\t\tOid relationId = lfirst_oid(relationIdCell);\n\t\tif (IsCitusTableType(relationId, REFERENCE_TABLE))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\telse if (IsCitusTableType(relationId, HASH_DISTRIBUTED) ||\n\t\t\t\t IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED))\n\t\t{\n\t\t\tint colocationId = TableColocationId(relationId);\n\t\t\tcolocationIdList = list_append_unique_int(colocationIdList, colocationId);\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/* do not display the hint if there are more than one colocation group */\n\tif (list_length(colocationIdList) > 1)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * DeferErrorIfUnsupportedSubqueryRepartition checks that we can perform distributed planning for\n * the given subquery. If not, a deferred error is returned. The function recursively\n * does this check to all lower levels of the subquery.\n */\nDeferredErrorMessage *\nDeferErrorIfUnsupportedSubqueryRepartition(Query *subqueryTree)\n{\n\tchar *errorDetail = NULL;\n\tbool preconditionsSatisfied = true;\n\tList *joinTreeTableIndexList = NIL;\n\n\tif (!subqueryTree->hasAggs)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"Subqueries without aggregates are not supported yet\";\n\t}\n\n\tif (subqueryTree->groupClause == NIL)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"Subqueries without group by clause are not supported yet\";\n\t}\n\n\tif (subqueryTree->sortClause != NULL)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"Subqueries with order by clause are not supported yet\";\n\t}\n\n\tif (subqueryTree->limitCount != NULL)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"Subqueries with limit are not supported yet\";\n\t}\n\n\tif (subqueryTree->limitOffset != NULL)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"Subqueries with offset are not supported yet\";\n\t}\n\n\tif (subqueryTree->hasSubLinks)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"Subqueries other than from-clause subqueries are unsupported\";\n\t}\n\n\t/* finally check and return error if conditions are not satisfied */\n\tif (!preconditionsSatisfied)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot perform distributed planning on this query\",\n\t\t\t\t\t\t\t errorDetail, NULL);\n\t}\n\n\t/*\n\t * Extract all range table indexes from the join tree. Note that sub-queries\n\t * that get pulled up by PostgreSQL don't appear in this join tree.\n\t */\n\tExtractRangeTableIndexWalker((Node *) subqueryTree->jointree,\n\t\t\t\t\t\t\t\t &joinTreeTableIndexList);\n\tAssert(list_length(joinTreeTableIndexList) == 1);\n\n\t/* continue with the inner subquery */\n\tint rangeTableIndex = linitial_int(joinTreeTableIndexList);\n\tRangeTblEntry *rangeTableEntry = rt_fetch(rangeTableIndex, subqueryTree->rtable);\n\tif (rangeTableEntry->rtekind == RTE_RELATION)\n\t{\n\t\treturn NULL;\n\t}\n\n\tAssert(rangeTableEntry->rtekind == RTE_SUBQUERY);\n\tQuery *innerSubquery = rangeTableEntry->subquery;\n\n\t/* recursively continue to the inner subqueries */\n\treturn DeferErrorIfUnsupportedSubqueryRepartition(innerSubquery);\n}\n\n\n/*\n * HasComplexRangeTableType checks if the given query tree contains any complex\n * range table types. For this, the function walks over all range tables in the\n * join tree, and checks if they correspond to simple relations or subqueries.\n * If they don't, the function assumes the query has complex range tables.\n */\nstatic bool\nHasComplexRangeTableType(Query *queryTree)\n{\n\tList *rangeTableList = queryTree->rtable;\n\tList *joinTreeTableIndexList = NIL;\n\tListCell *joinTreeTableIndexCell = NULL;\n\tbool hasComplexRangeTableType = false;\n\n\t/*\n\t * Extract all range table indexes from the join tree. Note that sub-queries\n\t * that get pulled up by PostgreSQL don't appear in this join tree.\n\t */\n\tExtractRangeTableIndexWalker((Node *) queryTree->jointree, &joinTreeTableIndexList);\n\tforeach(joinTreeTableIndexCell, joinTreeTableIndexList)\n\t{\n\t\t/*\n\t\t * Join tree's range table index starts from 1 in the query tree. But,\n\t\t * list indexes start from 0.\n\t\t */\n\t\tint joinTreeTableIndex = lfirst_int(joinTreeTableIndexCell);\n\t\tint rangeTableListIndex = joinTreeTableIndex - 1;\n\n\t\tRangeTblEntry *rangeTableEntry =\n\t\t\t(RangeTblEntry *) list_nth(rangeTableList, rangeTableListIndex);\n\n\t\t/*\n\t\t * Check if the range table in the join tree is a simple relation or a\n\t\t * subquery or a function. Note that RTE_FUNCTIONs are handled via (sub)query\n\t\t * pushdown.\n\t\t */\n\t\tif (rangeTableEntry->rtekind != RTE_RELATION &&\n\t\t\trangeTableEntry->rtekind != RTE_SUBQUERY &&\n\t\t\trangeTableEntry->rtekind != RTE_FUNCTION &&\n\t\t\trangeTableEntry->rtekind != RTE_VALUES &&\n\t\t\t!IsJsonTableRTE(rangeTableEntry))\n\t\t{\n\t\t\thasComplexRangeTableType = true;\n\t\t}\n\n\t\t/*\n\t\t * Check if the subquery range table entry includes children inheritance.\n\t\t *\n\t\t * Note that PostgreSQL flattens out simple union all queries into an\n\t\t * append relation, sets \"inh\" field of RangeTblEntry to true and deletes\n\t\t * set operations. Here we check this for subqueries.\n\t\t */\n\t\tif (rangeTableEntry->rtekind == RTE_SUBQUERY && rangeTableEntry->inh)\n\t\t{\n\t\t\thasComplexRangeTableType = true;\n\t\t}\n\t}\n\n\treturn hasComplexRangeTableType;\n}\n\n\n/*\n * WhereClauseList walks over the FROM expression in the query tree, and builds\n * a list of all clauses from the expression tree. The function checks for both\n * implicitly and explicitly defined clauses, but only selects INNER join\n * explicit clauses, and skips any outer-join clauses. Explicit clauses are\n * expressed as \"SELECT ... FROM R1 INNER JOIN R2 ON R1.A = R2.A\". Implicit\n * joins differ in that they live in the WHERE clause, and are expressed as\n * \"SELECT ... FROM ... WHERE R1.a = R2.a\".\n */\nList *\nWhereClauseList(FromExpr *fromExpr)\n{\n\tFromExpr *fromExprCopy = copyObject(fromExpr);\n\tQualifierWalkerContext *walkerContext = palloc0(sizeof(QualifierWalkerContext));\n\n\tExtractFromExpressionWalker((Node *) fromExprCopy, walkerContext);\n\tList *whereClauseList = walkerContext->baseQualifierList;\n\n\treturn whereClauseList;\n}\n\n\n/*\n * QualifierList walks over the FROM expression in the query tree, and builds\n * a list of all qualifiers from the expression tree. The function checks for\n * both implicitly and explicitly defined qualifiers. Note that this function\n * is very similar to WhereClauseList(), but QualifierList() also includes\n * outer-join clauses.\n */\nList *\nQualifierList(FromExpr *fromExpr)\n{\n\tFromExpr *fromExprCopy = copyObject(fromExpr);\n\tQualifierWalkerContext *walkerContext = palloc0(sizeof(QualifierWalkerContext));\n\tList *qualifierList = NIL;\n\n\tExtractFromExpressionWalker((Node *) fromExprCopy, walkerContext);\n\tqualifierList = list_concat(qualifierList, walkerContext->baseQualifierList);\n\tqualifierList = list_concat(qualifierList, walkerContext->outerJoinQualifierList);\n\n\treturn qualifierList;\n}\n\n\n/*\n * DeferErrorIfUnsupportedClause walks over the given list of clauses, and\n * checks that we can recognize all the clauses. This function ensures that\n * we do not drop an unsupported clause type on the floor, and thus prevents\n * erroneous results.\n *\n * Returns a deferred error, caller is responsible for raising the error.\n */\nDeferredErrorMessage *\nDeferErrorIfUnsupportedClause(List *clauseList)\n{\n\tListCell *clauseCell = NULL;\n\tforeach(clauseCell, clauseList)\n\t{\n\t\tNode *clause = (Node *) lfirst(clauseCell);\n\n\t\tif (!(IsSelectClause(clause) || IsJoinClause(clause) || is_orclause(clause)))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"unsupported clause type\", NULL, NULL);\n\t\t}\n\t}\n\treturn NULL;\n}\n\n\n/*\n * JoinClauseList finds the join clauses from the given where clause expression\n * list, and returns them. The function does not iterate into nested OR clauses\n * and relies on find_duplicate_ors() in the optimizer to pull up factorizable\n * OR clauses.\n */\nList *\nJoinClauseList(List *whereClauseList)\n{\n\tList *joinClauseList = NIL;\n\tListCell *whereClauseCell = NULL;\n\n\tforeach(whereClauseCell, whereClauseList)\n\t{\n\t\tNode *whereClause = (Node *) lfirst(whereClauseCell);\n\t\tif (IsJoinClause(whereClause))\n\t\t{\n\t\t\tjoinClauseList = lappend(joinClauseList, whereClause);\n\t\t}\n\t}\n\n\treturn joinClauseList;\n}\n\n\n/*\n * ExtractFromExpressionWalker walks over a FROM expression, and finds all\n * implicit and explicit qualifiers in the expression. The function looks at\n * join and from expression nodes to find qualifiers, and returns these\n * qualifiers.\n *\n * Note that we don't want outer join clauses in regular outer join planning,\n * but we need outer join clauses in subquery pushdown prerequisite checks.\n * Therefore, outer join qualifiers are returned in a different list than other\n * qualifiers inside the given walker context. For this reason, we return two\n * qualifier lists.\n *\n * Note that we check if the qualifier node in join and from expression nodes\n * is a list node. If it is not a list node which is the case for subqueries,\n * then we run eval_const_expressions(), canonicalize_qual() and make_ands_implicit()\n * on the qualifier node and get a list of flattened implicitly AND'ed qualifier\n * list. Actually in the planer phase of PostgreSQL these functions also run on\n * subqueries but differently from the outermost query, they are run on a copy\n * of parse tree and changes do not get persisted as modifications to the original\n * query tree.\n *\n * Also this function adds SubLinks to the baseQualifierList when they appear on\n * the query's WHERE clause. The callers of the function should consider processing\n * Sublinks as well.\n */\nstatic bool\nExtractFromExpressionWalker(Node *node, QualifierWalkerContext *walkerContext)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * Get qualifier lists of join and from expression nodes. Note that in the\n\t * case of subqueries, PostgreSQL can skip simplifying, flattening and\n\t * making ANDs implicit. If qualifiers node is not a list, then we run these\n\t * preprocess routines on qualifiers node.\n\t */\n\tif (IsA(node, JoinExpr))\n\t{\n\t\tList *joinQualifierList = NIL;\n\t\tJoinExpr *joinExpression = (JoinExpr *) node;\n\t\tNode *joinQualifiersNode = joinExpression->quals;\n\t\tJoinType joinType = joinExpression->jointype;\n\n\t\tif (joinQualifiersNode != NULL)\n\t\t{\n\t\t\tif (IsA(joinQualifiersNode, List))\n\t\t\t{\n\t\t\t\tjoinQualifierList = (List *) joinQualifiersNode;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* this part of code only run for subqueries */\n\t\t\t\tNode *joinClause = eval_const_expressions(NULL, joinQualifiersNode);\n\t\t\t\tjoinClause = (Node *) canonicalize_qual((Expr *) joinClause, false);\n\t\t\t\tjoinQualifierList = make_ands_implicit((Expr *) joinClause);\n\t\t\t}\n\t\t}\n\n\t\t/* return outer join clauses in a separate list */\n\t\tif (joinType == JOIN_INNER || joinType == JOIN_SEMI)\n\t\t{\n\t\t\twalkerContext->baseQualifierList =\n\t\t\t\tlist_concat(walkerContext->baseQualifierList, joinQualifierList);\n\t\t}\n\t\telse if (IS_OUTER_JOIN(joinType))\n\t\t{\n\t\t\twalkerContext->outerJoinQualifierList =\n\t\t\t\tlist_concat(walkerContext->outerJoinQualifierList, joinQualifierList);\n\t\t}\n\t}\n\telse if (IsA(node, FromExpr))\n\t{\n\t\tList *fromQualifierList = NIL;\n\t\tFromExpr *fromExpression = (FromExpr *) node;\n\t\tNode *fromQualifiersNode = fromExpression->quals;\n\n\t\tif (fromQualifiersNode != NULL)\n\t\t{\n\t\t\tif (IsA(fromQualifiersNode, List))\n\t\t\t{\n\t\t\t\tfromQualifierList = (List *) fromQualifiersNode;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* this part of code only run for subqueries */\n\t\t\t\tNode *fromClause = eval_const_expressions(NULL, fromQualifiersNode);\n\t\t\t\tfromClause = (Node *) canonicalize_qual((Expr *) fromClause, false);\n\t\t\t\tfromQualifierList = make_ands_implicit((Expr *) fromClause);\n\t\t\t}\n\n\t\t\twalkerContext->baseQualifierList =\n\t\t\t\tlist_concat(walkerContext->baseQualifierList, fromQualifierList);\n\t\t}\n\t}\n\n\tbool walkerResult = expression_tree_walker(node, ExtractFromExpressionWalker,\n\t\t\t\t\t\t\t\t\t\t\t   (void *) walkerContext);\n\n\treturn walkerResult;\n}\n\n\n/*\n * IsJoinClause determines if the given node is a join clause according to our\n * criteria. Our criteria defines a join clause as an equi join operator between\n * two columns that belong to two different tables.\n */\nbool\nIsJoinClause(Node *clause)\n{\n\tVar *var = NULL;\n\n\t/*\n\t * take all column references from the clause, if we find 2 column references from a\n\t * different relation we assume this is a join clause\n\t */\n\tList *varList = pull_var_clause_default(clause);\n\tif (list_length(varList) <= 0)\n\t{\n\t\t/* no column references in query, not describing a join */\n\t\treturn false;\n\t}\n\tVar *initialVar = castNode(Var, linitial(varList));\n\n\tforeach_declared_ptr(var, varList)\n\t{\n\t\tif (var->varno != initialVar->varno)\n\t\t{\n\t\t\t/*\n\t\t\t * this column reference comes from a different relation, hence describing a\n\t\t\t * join\n\t\t\t */\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/* all column references were to the same relation, no join */\n\treturn false;\n}\n\n\n/*\n * TableEntryList finds the regular relation nodes in the range table entry\n * list, and builds a list of table entries from these regular relation nodes.\n */\nList *\nTableEntryList(List *rangeTableList)\n{\n\tList *tableEntryList = NIL;\n\tListCell *rangeTableCell = NULL;\n\tuint32 tableId = 1; /* range table indices start at 1 */\n\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\n\t\tif (rangeTableEntry->rtekind == RTE_RELATION)\n\t\t{\n\t\t\tTableEntry *tableEntry = (TableEntry *) palloc0(sizeof(TableEntry));\n\t\t\ttableEntry->relationId = rangeTableEntry->relid;\n\t\t\ttableEntry->rangeTableId = tableId;\n\n\t\t\ttableEntryList = lappend(tableEntryList, tableEntry);\n\t\t}\n\n\t\t/*\n\t\t * Increment tableId regardless so that table entry's tableId remains\n\t\t * congruent with column's range table reference (varno).\n\t\t */\n\t\ttableId++;\n\t}\n\n\treturn tableEntryList;\n}\n\n\n/*\n * UsedTableEntryList returns list of relation range table entries\n * that are referenced within the query. Unused entries due to query\n * flattening or re-rewriting are ignored.\n */\nList *\nUsedTableEntryList(Query *query)\n{\n\tList *tableEntryList = NIL;\n\tList *rangeTableList = query->rtable;\n\tList *joinTreeTableIndexList = NIL;\n\tListCell *joinTreeTableIndexCell = NULL;\n\n\tExtractRangeTableIndexWalker((Node *) query->jointree, &joinTreeTableIndexList);\n\tforeach(joinTreeTableIndexCell, joinTreeTableIndexList)\n\t{\n\t\tint joinTreeTableIndex = lfirst_int(joinTreeTableIndexCell);\n\t\tRangeTblEntry *rangeTableEntry = rt_fetch(joinTreeTableIndex, rangeTableList);\n\t\tif (rangeTableEntry->rtekind == RTE_RELATION)\n\t\t{\n\t\t\tTableEntry *tableEntry = (TableEntry *) palloc0(sizeof(TableEntry));\n\t\t\ttableEntry->relationId = rangeTableEntry->relid;\n\t\t\ttableEntry->rangeTableId = joinTreeTableIndex;\n\n\t\t\ttableEntryList = lappend(tableEntryList, tableEntry);\n\t\t}\n\t}\n\n\treturn tableEntryList;\n}\n\n\n/*\n * MultiTableNodeList builds a list of MultiTable nodes from the given table\n * entry list. A multi table node represents one entry from the range table\n * list. These entries may belong to the same physical relation in the case of\n * self-joins.\n */\nstatic List *\nMultiTableNodeList(List *tableEntryList, List *rangeTableList)\n{\n\tList *tableNodeList = NIL;\n\tListCell *tableEntryCell = NULL;\n\n\tforeach(tableEntryCell, tableEntryList)\n\t{\n\t\tTableEntry *tableEntry = (TableEntry *) lfirst(tableEntryCell);\n\t\tOid relationId = tableEntry->relationId;\n\t\tuint32 rangeTableId = tableEntry->rangeTableId;\n\t\tVar *partitionColumn = PartitionColumn(relationId, rangeTableId);\n\t\tRangeTblEntry *rangeTableEntry = rt_fetch(rangeTableId, rangeTableList);\n\n\t\tMultiTable *tableNode = CitusMakeNode(MultiTable);\n\t\ttableNode->subquery = NULL;\n\t\ttableNode->relationId = relationId;\n\t\ttableNode->rangeTableId = rangeTableId;\n\t\ttableNode->partitionColumn = partitionColumn;\n\t\ttableNode->alias = rangeTableEntry->alias;\n\t\ttableNode->referenceNames = rangeTableEntry->eref;\n\t\ttableNode->includePartitions = GetOriginalInh(rangeTableEntry);\n\t\ttableNode->tablesample = rangeTableEntry->tablesample;\n\n\t\ttableNodeList = lappend(tableNodeList, tableNode);\n\t}\n\n\treturn tableNodeList;\n}\n\n\n/* Adds a MultiCollect node on top of each MultiTable node in the given list. */\nstatic List *\nAddMultiCollectNodes(List *tableNodeList)\n{\n\tList *collectTableList = NIL;\n\tListCell *tableNodeCell = NULL;\n\n\tforeach(tableNodeCell, tableNodeList)\n\t{\n\t\tMultiTable *tableNode = (MultiTable *) lfirst(tableNodeCell);\n\n\t\tMultiCollect *collectNode = CitusMakeNode(MultiCollect);\n\t\tSetChild((MultiUnaryNode *) collectNode, (MultiNode *) tableNode);\n\n\t\tcollectTableList = lappend(collectTableList, collectNode);\n\t}\n\n\treturn collectTableList;\n}\n\n\n/*\n * MultiJoinTree takes in the join order information and the list of tables, and\n * builds a join tree by applying the corresponding join rules. The function\n * builds a left deep tree, as expressed by the join order list.\n *\n * The function starts by setting the first table as the top node in the join\n * tree. Then, the function iterates over the list of tables, and builds a new\n * join node between the top of the join tree and the next table in the list.\n * At each iteration, the function sets the top of the join tree to the newly\n * built list. This results in a left deep join tree, and the function returns\n * this tree after every table in the list has been joined.\n */\nstatic MultiNode *\nMultiJoinTree(List *joinOrderList, List *collectTableList, List *joinWhereClauseList)\n{\n\tMultiNode *currentTopNode = NULL;\n\tListCell *joinOrderCell = NULL;\n\tbool firstJoinNode = true;\n\n\tforeach(joinOrderCell, joinOrderList)\n\t{\n\t\tJoinOrderNode *joinOrderNode = (JoinOrderNode *) lfirst(joinOrderCell);\n\t\tuint32 joinTableId = joinOrderNode->tableEntry->rangeTableId;\n\t\tMultiCollect *collectNode = CollectNodeForTable(collectTableList, joinTableId);\n\n\t\tif (firstJoinNode)\n\t\t{\n\t\t\tcurrentTopNode = (MultiNode *) collectNode;\n\t\t\tfirstJoinNode = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tJoinRuleType joinRuleType = joinOrderNode->joinRuleType;\n\t\t\tJoinType joinType = joinOrderNode->joinType;\n\t\t\tList *partitionColumnList = joinOrderNode->partitionColumnList;\n\t\t\tList *joinClauseList = joinOrderNode->joinClauseList;\n\n\t\t\t/*\n\t\t\t * Build a join node between the top of our join tree and the next\n\t\t\t * table in the join order.\n\t\t\t */\n\t\t\tMultiNode *newJoinNode = ApplyJoinRule(currentTopNode,\n\t\t\t\t\t\t\t\t\t\t\t\t   (MultiNode *) collectNode,\n\t\t\t\t\t\t\t\t\t\t\t\t   joinRuleType, partitionColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t   joinType,\n\t\t\t\t\t\t\t\t\t\t\t\t   joinClauseList);\n\n\t\t\t/* the new join node becomes the top of our join tree */\n\t\t\tcurrentTopNode = newJoinNode;\n\t\t}\n\t}\n\n\t/* current top node points to the entire left deep join tree */\n\treturn currentTopNode;\n}\n\n\n/*\n * CollectNodeForTable finds the MultiCollect node whose MultiTable node has the\n * given range table identifier. Note that this function expects each collect\n * node in the given list to have one table node as its child.\n */\nstatic MultiCollect *\nCollectNodeForTable(List *collectTableList, uint32 rangeTableId)\n{\n\tMultiCollect *collectNodeForTable = NULL;\n\tListCell *collectTableCell = NULL;\n\n\tforeach(collectTableCell, collectTableList)\n\t{\n\t\tMultiCollect *collectNode = (MultiCollect *) lfirst(collectTableCell);\n\n\t\tList *tableIdList = OutputTableIdList((MultiNode *) collectNode);\n\t\tuint32 tableId = (uint32) linitial_int(tableIdList);\n\t\tAssert(list_length(tableIdList) == 1);\n\n\t\tif (tableId == rangeTableId)\n\t\t{\n\t\t\tcollectNodeForTable = collectNode;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tAssert(collectNodeForTable != NULL);\n\treturn collectNodeForTable;\n}\n\n\n/*\n * MultiSelectNode extracts the select clauses from the given where clause list,\n * and builds a MultiSelect node from these clauses. If the expression tree does\n * not have any select clauses, the function return null.\n */\nstatic MultiSelect *\nMultiSelectNode(List *whereClauseList)\n{\n\tList *selectClauseList = NIL;\n\tMultiSelect *selectNode = NULL;\n\n\tListCell *whereClauseCell = NULL;\n\tforeach(whereClauseCell, whereClauseList)\n\t{\n\t\tNode *whereClause = (Node *) lfirst(whereClauseCell);\n\t\tif (IsSelectClause(whereClause))\n\t\t{\n\t\t\tselectClauseList = lappend(selectClauseList, whereClause);\n\t\t}\n\t}\n\n\tif (list_length(selectClauseList) > 0)\n\t{\n\t\tselectNode = CitusMakeNode(MultiSelect);\n\t\tselectNode->selectClauseList = selectClauseList;\n\t}\n\n\treturn selectNode;\n}\n\n\n/*\n * IsSelectClause determines if the given node is a select clause according to\n * our criteria. Our criteria defines a select clause as an expression that has\n * zero or more columns belonging to only one table. The function assumes that\n * no sublinks exists in the clause.\n */\nstatic bool\nIsSelectClause(Node *clause)\n{\n\tListCell *columnCell = NULL;\n\tbool isSelectClause = true;\n\n\t/* extract columns from the clause */\n\tList *columnList = pull_var_clause_default(clause);\n\tif (list_length(columnList) == 0)\n\t{\n\t\treturn true;\n\t}\n\n\t/* get first column's tableId */\n\tVar *firstColumn = (Var *) linitial(columnList);\n\tIndex firstColumnTableId = firstColumn->varno;\n\n\t/* check if all columns are from the same table */\n\tforeach(columnCell, columnList)\n\t{\n\t\tVar *column = (Var *) lfirst(columnCell);\n\t\tif (column->varno != firstColumnTableId)\n\t\t{\n\t\t\tisSelectClause = false;\n\t\t}\n\t}\n\n\treturn isSelectClause;\n}\n\n\n/*\n * MultiProjectNode builds the project node using the target entry information\n * from the query tree. The project node only encapsulates projected columns,\n * and does not include aggregates, group clauses, or project expressions.\n */\nMultiProject *\nMultiProjectNode(List *targetEntryList)\n{\n\tList *uniqueColumnList = NIL;\n\tListCell *columnCell = NULL;\n\n\t/* extract the list of columns and remove any duplicates */\n\tList *columnList = pull_var_clause_default((Node *) targetEntryList);\n\tforeach(columnCell, columnList)\n\t{\n\t\tVar *column = (Var *) lfirst(columnCell);\n\n\t\tuniqueColumnList = list_append_unique(uniqueColumnList, column);\n\t}\n\n\t/* create project node with list of columns to project */\n\tMultiProject *projectNode = CitusMakeNode(MultiProject);\n\tprojectNode->columnList = uniqueColumnList;\n\n\treturn projectNode;\n}\n\n\n/* Builds the extended operator node using fields from the given query tree. */\nMultiExtendedOp *\nMultiExtendedOpNode(Query *queryTree, Query *originalQuery)\n{\n\tMultiExtendedOp *extendedOpNode = CitusMakeNode(MultiExtendedOp);\n\textendedOpNode->targetList = queryTree->targetList;\n\textendedOpNode->groupClauseList = queryTree->groupClause;\n\textendedOpNode->sortClauseList = queryTree->sortClause;\n\textendedOpNode->limitCount = queryTree->limitCount;\n\textendedOpNode->limitOffset = queryTree->limitOffset;\n\textendedOpNode->limitOption = queryTree->limitOption;\n\textendedOpNode->havingQual = queryTree->havingQual;\n\textendedOpNode->distinctClause = queryTree->distinctClause;\n\textendedOpNode->hasDistinctOn = queryTree->hasDistinctOn;\n\textendedOpNode->hasWindowFuncs = queryTree->hasWindowFuncs;\n\textendedOpNode->windowClause = queryTree->windowClause;\n\textendedOpNode->onlyPushableWindowFunctions =\n\t\t!queryTree->hasWindowFuncs ||\n\t\tSafeToPushdownWindowFunction(originalQuery, NULL);\n\n\treturn extendedOpNode;\n}\n\n\n/* Helper function to return the parent node of the given node. */\nMultiNode *\nParentNode(MultiNode *multiNode)\n{\n\tMultiNode *parentNode = multiNode->parentNode;\n\treturn parentNode;\n}\n\n\n/* Helper function to return the child of the given unary node. */\nMultiNode *\nChildNode(MultiUnaryNode *multiNode)\n{\n\tMultiNode *childNode = multiNode->childNode;\n\treturn childNode;\n}\n\n\n/* Helper function to return the grand child of the given unary node. */\nMultiNode *\nGrandChildNode(MultiUnaryNode *multiNode)\n{\n\tMultiNode *childNode = ChildNode(multiNode);\n\tMultiNode *grandChildNode = ChildNode((MultiUnaryNode *) childNode);\n\n\treturn grandChildNode;\n}\n\n\n/* Sets the given child node as a child of the given unary parent node. */\nvoid\nSetChild(MultiUnaryNode *parent, MultiNode *child)\n{\n\tparent->childNode = child;\n\tchild->parentNode = (MultiNode *) parent;\n}\n\n\n/* Sets the given child node as a left child of the given parent node. */\nvoid\nSetLeftChild(MultiBinaryNode *parent, MultiNode *leftChild)\n{\n\tparent->leftChildNode = leftChild;\n\tleftChild->parentNode = (MultiNode *) parent;\n}\n\n\n/* Sets the given child node as a right child of the given parent node. */\nvoid\nSetRightChild(MultiBinaryNode *parent, MultiNode *rightChild)\n{\n\tparent->rightChildNode = rightChild;\n\trightChild->parentNode = (MultiNode *) parent;\n}\n\n\n/* Returns true if the given node is a unary operator. */\nbool\nUnaryOperator(MultiNode *node)\n{\n\tbool unaryOperator = false;\n\n\tif (CitusIsA(node, MultiTreeRoot) || CitusIsA(node, MultiTable) ||\n\t\tCitusIsA(node, MultiCollect) || CitusIsA(node, MultiSelect) ||\n\t\tCitusIsA(node, MultiProject) || CitusIsA(node, MultiPartition) ||\n\t\tCitusIsA(node, MultiExtendedOp))\n\t{\n\t\tunaryOperator = true;\n\t}\n\n\treturn unaryOperator;\n}\n\n\n/* Returns true if the given node is a binary operator. */\nbool\nBinaryOperator(MultiNode *node)\n{\n\tbool binaryOperator = false;\n\n\tif (CitusIsA(node, MultiJoin) || CitusIsA(node, MultiCartesianProduct))\n\t{\n\t\tbinaryOperator = true;\n\t}\n\n\treturn binaryOperator;\n}\n\n\n/*\n * OutputTableIdList finds all table identifiers that are output by the given\n * multi node, and returns these identifiers in a new list.\n */\nList *\nOutputTableIdList(MultiNode *multiNode)\n{\n\tList *tableIdList = NIL;\n\tList *tableNodeList = FindNodesOfType(multiNode, T_MultiTable);\n\tListCell *tableNodeCell = NULL;\n\n\tforeach(tableNodeCell, tableNodeList)\n\t{\n\t\tMultiTable *tableNode = (MultiTable *) lfirst(tableNodeCell);\n\t\tint tableId = (int) tableNode->rangeTableId;\n\n\t\tif (tableId != SUBQUERY_RANGE_TABLE_ID)\n\t\t{\n\t\t\ttableIdList = lappend_int(tableIdList, tableId);\n\t\t}\n\t}\n\n\treturn tableIdList;\n}\n\n\n/*\n * FindNodesOfType takes in a given logical plan tree, and recursively traverses\n * the tree in preorder. The function finds all nodes of requested type during\n * the traversal, and returns them in a list.\n */\nList *\nFindNodesOfType(MultiNode *node, int type)\n{\n\tList *nodeList = NIL;\n\n\t/* terminal condition for recursion */\n\tif (node == NULL)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* current node has expected node type */\n\tint nodeType = CitusNodeTag(node);\n\tif (nodeType == type)\n\t{\n\t\tnodeList = lappend(nodeList, node);\n\t}\n\n\tif (UnaryOperator(node))\n\t{\n\t\tMultiNode *childNode = ((MultiUnaryNode *) node)->childNode;\n\t\tList *childNodeList = FindNodesOfType(childNode, type);\n\n\t\tnodeList = list_concat(nodeList, childNodeList);\n\t}\n\telse if (BinaryOperator(node))\n\t{\n\t\tMultiNode *leftChildNode = ((MultiBinaryNode *) node)->leftChildNode;\n\t\tMultiNode *rightChildNode = ((MultiBinaryNode *) node)->rightChildNode;\n\n\t\tList *leftChildNodeList = FindNodesOfType(leftChildNode, type);\n\t\tList *rightChildNodeList = FindNodesOfType(rightChildNode, type);\n\n\t\tnodeList = list_concat(nodeList, leftChildNodeList);\n\t\tnodeList = list_concat(nodeList, rightChildNodeList);\n\t}\n\n\treturn nodeList;\n}\n\n\n/*\n * pull_var_clause_default calls pull_var_clause with the most commonly used\n * arguments for distributed planning.\n */\nList *\npull_var_clause_default(Node *node)\n{\n\t/*\n\t * PVC_REJECT_PLACEHOLDERS is implicit if PVC_INCLUDE_PLACEHOLDERS\n\t * isn't specified.\n\t */\n\tList *columnList = pull_var_clause(node, PVC_RECURSE_AGGREGATES |\n\t\t\t\t\t\t\t\t\t   PVC_RECURSE_WINDOWFUNCS);\n\n\treturn columnList;\n}\n\n\n/*\n * ApplyJoinRule finds the join rule application function that corresponds to\n * the given join rule, and calls this function to create a new join node that\n * joins the left and right nodes together.\n */\nstatic MultiNode *\nApplyJoinRule(MultiNode *leftNode, MultiNode *rightNode, JoinRuleType ruleType,\n\t\t\t  List *partitionColumnList, JoinType joinType, List *joinClauseList)\n{\n\tList *leftTableIdList = OutputTableIdList(leftNode);\n\tList *rightTableIdList = OutputTableIdList(rightNode);\n\tint rightTableIdCount PG_USED_FOR_ASSERTS_ONLY = 0;\n\n\trightTableIdCount = list_length(rightTableIdList);\n\tAssert(rightTableIdCount == 1);\n\n\t/* find applicable join clauses between the left and right data sources */\n\tuint32 rightTableId = (uint32) linitial_int(rightTableIdList);\n\tList *applicableJoinClauses = ApplicableJoinClauses(leftTableIdList, rightTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tjoinClauseList);\n\n\t/* call the join rule application function to create the new join node */\n\tRuleApplyFunction ruleApplyFunction = JoinRuleApplyFunction(ruleType);\n\tMultiNode *multiNode = (*ruleApplyFunction)(leftNode, rightNode, partitionColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\tjoinType, applicableJoinClauses);\n\n\tif (joinType != JOIN_INNER && CitusIsA(multiNode, MultiJoin))\n\t{\n\t\tMultiJoin *joinNode = (MultiJoin *) multiNode;\n\n\t\t/* preserve non-join clauses for OUTER joins */\n\t\tjoinNode->joinClauseList = list_copy(joinClauseList);\n\t}\n\n\treturn multiNode;\n}\n\n\n/*\n * JoinRuleApplyFunction returns a function pointer for the rule application\n * function; this rule application function corresponds to the given rule type.\n * This function also initializes the rule application function array in a\n * static code block, if the array has not been initialized.\n */\nstatic RuleApplyFunction\nJoinRuleApplyFunction(JoinRuleType ruleType)\n{\n\tstatic bool ruleApplyFunctionInitialized = false;\n\n\tif (!ruleApplyFunctionInitialized)\n\t{\n\t\tRuleApplyFunctionArray[REFERENCE_JOIN] = &ApplyReferenceJoin;\n\t\tRuleApplyFunctionArray[LOCAL_PARTITION_JOIN] = &ApplyLocalJoin;\n\t\tRuleApplyFunctionArray[SINGLE_HASH_PARTITION_JOIN] =\n\t\t\t&ApplySingleHashPartitionJoin;\n\t\tRuleApplyFunctionArray[SINGLE_RANGE_PARTITION_JOIN] =\n\t\t\t&ApplySingleRangePartitionJoin;\n\t\tRuleApplyFunctionArray[DUAL_PARTITION_JOIN] = &ApplyDualPartitionJoin;\n\t\tRuleApplyFunctionArray[CARTESIAN_PRODUCT_REFERENCE_JOIN] =\n\t\t\t&ApplyCartesianProductReferenceJoin;\n\t\tRuleApplyFunctionArray[CARTESIAN_PRODUCT] = &ApplyCartesianProduct;\n\n\t\truleApplyFunctionInitialized = true;\n\t}\n\n\tRuleApplyFunction ruleApplyFunction = RuleApplyFunctionArray[ruleType];\n\tAssert(ruleApplyFunction != NULL);\n\n\treturn ruleApplyFunction;\n}\n\n\n/*\n * ApplyBroadcastJoin creates a new MultiJoin node that joins the left and the\n * right node. The new node uses the broadcast join rule to perform the join.\n */\nstatic MultiNode *\nApplyReferenceJoin(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t   List *partitionColumnList, JoinType joinType,\n\t\t\t\t   List *applicableJoinClauses)\n{\n\tMultiJoin *joinNode = CitusMakeNode(MultiJoin);\n\tjoinNode->joinRuleType = REFERENCE_JOIN;\n\tjoinNode->joinType = joinType;\n\tjoinNode->joinClauseList = applicableJoinClauses;\n\n\tSetLeftChild((MultiBinaryNode *) joinNode, leftNode);\n\tSetRightChild((MultiBinaryNode *) joinNode, rightNode);\n\n\treturn (MultiNode *) joinNode;\n}\n\n\n/*\n * ApplyCartesianProductReferenceJoin creates a new MultiJoin node that joins\n * the left and the right node. The new node uses the broadcast join rule to\n * perform the join.\n */\nstatic MultiNode *\nApplyCartesianProductReferenceJoin(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t\t\t\t   List *partitionColumnList, JoinType joinType,\n\t\t\t\t\t\t\t\t   List *applicableJoinClauses)\n{\n\tMultiJoin *joinNode = CitusMakeNode(MultiJoin);\n\tjoinNode->joinRuleType = CARTESIAN_PRODUCT_REFERENCE_JOIN;\n\tjoinNode->joinType = joinType;\n\tjoinNode->joinClauseList = applicableJoinClauses;\n\n\tSetLeftChild((MultiBinaryNode *) joinNode, leftNode);\n\tSetRightChild((MultiBinaryNode *) joinNode, rightNode);\n\n\treturn (MultiNode *) joinNode;\n}\n\n\n/*\n * ApplyLocalJoin creates a new MultiJoin node that joins the left and the right\n * node. The new node uses the local join rule to perform the join.\n */\nstatic MultiNode *\nApplyLocalJoin(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t   List *partitionColumnList, JoinType joinType,\n\t\t\t   List *applicableJoinClauses)\n{\n\tMultiJoin *joinNode = CitusMakeNode(MultiJoin);\n\tjoinNode->joinRuleType = LOCAL_PARTITION_JOIN;\n\tjoinNode->joinType = joinType;\n\tjoinNode->joinClauseList = applicableJoinClauses;\n\n\tSetLeftChild((MultiBinaryNode *) joinNode, leftNode);\n\tSetRightChild((MultiBinaryNode *) joinNode, rightNode);\n\n\treturn (MultiNode *) joinNode;\n}\n\n\n/*\n * ApplySingleRangePartitionJoin is a wrapper around ApplySinglePartitionJoin()\n * which sets the joinRuleType properly.\n */\nstatic MultiNode *\nApplySingleRangePartitionJoin(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t\t\t  List *partitionColumnList, JoinType joinType,\n\t\t\t\t\t\t\t  List *applicableJoinClauses)\n{\n\tMultiJoin *joinNode =\n\t\tApplySinglePartitionJoin(leftNode, rightNode, partitionColumnList, joinType,\n\t\t\t\t\t\t\t\t applicableJoinClauses);\n\n\tjoinNode->joinRuleType = SINGLE_RANGE_PARTITION_JOIN;\n\n\treturn (MultiNode *) joinNode;\n}\n\n\n/*\n * ApplySingleHashPartitionJoin is a wrapper around ApplySinglePartitionJoin()\n * which sets the joinRuleType properly.\n */\nstatic MultiNode *\nApplySingleHashPartitionJoin(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t\t\t List *partitionColumnList, JoinType joinType,\n\t\t\t\t\t\t\t List *applicableJoinClauses)\n{\n\tMultiJoin *joinNode =\n\t\tApplySinglePartitionJoin(leftNode, rightNode, partitionColumnList, joinType,\n\t\t\t\t\t\t\t\t applicableJoinClauses);\n\n\tjoinNode->joinRuleType = SINGLE_HASH_PARTITION_JOIN;\n\n\treturn (MultiNode *) joinNode;\n}\n\n\n/*\n * ApplySinglePartitionJoin creates a new MultiJoin node that joins the left and\n * right node. The function also adds a MultiPartition node on top of the node\n * (left or right) that is not partitioned on the join column.\n */\nstatic MultiJoin *\nApplySinglePartitionJoin(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t\t List *partitionColumnList, JoinType joinType,\n\t\t\t\t\t\t List *applicableJoinClauses)\n{\n\tVar *partitionColumn = linitial(partitionColumnList);\n\tuint32 partitionTableId = partitionColumn->varno;\n\n\t/* create all operator structures up front */\n\tMultiJoin *joinNode = CitusMakeNode(MultiJoin);\n\tMultiCollect *collectNode = CitusMakeNode(MultiCollect);\n\tMultiPartition *partitionNode = CitusMakeNode(MultiPartition);\n\n\t/*\n\t * We first find the appropriate join clause. Then, we compare the partition\n\t * column against the join clause's columns. If one of the columns matches,\n\t * we introduce a (re-)partition operator for the other column.\n\t */\n\tOpExpr *joinClause = SinglePartitionJoinClause(partitionColumnList,\n\t\t\t\t\t\t\t\t\t\t\t\t   applicableJoinClauses,\n\t\t\t\t\t\t\t\t\t\t\t\t   NULL);\n\tAssert(joinClause != NULL);\n\n\t/* both are verified in SinglePartitionJoinClause to not be NULL, assert is to guard */\n\tVar *leftColumn = LeftColumnOrNULL(joinClause);\n\tVar *rightColumn = RightColumnOrNULL(joinClause);\n\n\tAssert(leftColumn != NULL);\n\tAssert(rightColumn != NULL);\n\n\tif (equal(partitionColumn, leftColumn))\n\t{\n\t\tpartitionNode->partitionColumn = rightColumn;\n\t\tpartitionNode->splitPointTableId = partitionTableId;\n\t}\n\telse if (equal(partitionColumn, rightColumn))\n\t{\n\t\tpartitionNode->partitionColumn = leftColumn;\n\t\tpartitionNode->splitPointTableId = partitionTableId;\n\t}\n\n\t/* determine the node the partition operator goes on top of */\n\tList *rightTableIdList = OutputTableIdList(rightNode);\n\tuint32 rightTableId = (uint32) linitial_int(rightTableIdList);\n\tAssert(list_length(rightTableIdList) == 1);\n\n\t/*\n\t * If the right child node is partitioned on the partition key column, we\n\t * add the partition operator on the left child node; and vice versa. Then,\n\t * we add a collect operator on top of the partition operator, and always\n\t * make sure that we have at most one relation on the right-hand side.\n\t */\n\tif (partitionTableId == rightTableId)\n\t{\n\t\tSetChild((MultiUnaryNode *) partitionNode, leftNode);\n\t\tSetChild((MultiUnaryNode *) collectNode, (MultiNode *) partitionNode);\n\n\t\tSetLeftChild((MultiBinaryNode *) joinNode, (MultiNode *) collectNode);\n\t\tSetRightChild((MultiBinaryNode *) joinNode, rightNode);\n\t}\n\telse\n\t{\n\t\tSetChild((MultiUnaryNode *) partitionNode, rightNode);\n\t\tSetChild((MultiUnaryNode *) collectNode, (MultiNode *) partitionNode);\n\n\t\tSetLeftChild((MultiBinaryNode *) joinNode, leftNode);\n\t\tSetRightChild((MultiBinaryNode *) joinNode, (MultiNode *) collectNode);\n\t}\n\n\t/* finally set join operator fields */\n\tjoinNode->joinType = joinType;\n\tjoinNode->joinClauseList = applicableJoinClauses;\n\n\treturn joinNode;\n}\n\n\n/*\n * ApplyDualPartitionJoin creates a new MultiJoin node that joins the left and\n * right node. The function also adds two MultiPartition operators on top of\n * both nodes to repartition these nodes' data on the join clause columns.\n */\nstatic MultiNode *\nApplyDualPartitionJoin(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t   List *partitionColumnList, JoinType joinType,\n\t\t\t\t\t   List *applicableJoinClauses)\n{\n\t/* find the appropriate join clause */\n\tOpExpr *joinClause = DualPartitionJoinClause(applicableJoinClauses);\n\tAssert(joinClause != NULL);\n\n\t/* both are verified in DualPartitionJoinClause to not be NULL, assert is to guard */\n\tVar *leftColumn = LeftColumnOrNULL(joinClause);\n\tVar *rightColumn = RightColumnOrNULL(joinClause);\n\tAssert(leftColumn != NULL);\n\tAssert(rightColumn != NULL);\n\n\tList *rightTableIdList = OutputTableIdList(rightNode);\n\tuint32 rightTableId = (uint32) linitial_int(rightTableIdList);\n\tAssert(list_length(rightTableIdList) == 1);\n\n\tMultiPartition *leftPartitionNode = CitusMakeNode(MultiPartition);\n\tMultiPartition *rightPartitionNode = CitusMakeNode(MultiPartition);\n\n\t/* find the partition node each join clause column belongs to */\n\tif (leftColumn->varno == rightTableId)\n\t{\n\t\tleftPartitionNode->partitionColumn = rightColumn;\n\t\trightPartitionNode->partitionColumn = leftColumn;\n\t}\n\telse\n\t{\n\t\tleftPartitionNode->partitionColumn = leftColumn;\n\t\trightPartitionNode->partitionColumn = rightColumn;\n\t}\n\n\t/* add partition operators on top of left and right nodes */\n\tSetChild((MultiUnaryNode *) leftPartitionNode, leftNode);\n\tSetChild((MultiUnaryNode *) rightPartitionNode, rightNode);\n\n\t/* add collect operators on top of the two partition operators */\n\tMultiCollect *leftCollectNode = CitusMakeNode(MultiCollect);\n\tMultiCollect *rightCollectNode = CitusMakeNode(MultiCollect);\n\n\tSetChild((MultiUnaryNode *) leftCollectNode, (MultiNode *) leftPartitionNode);\n\tSetChild((MultiUnaryNode *) rightCollectNode, (MultiNode *) rightPartitionNode);\n\n\t/* add join operator on top of the two collect operators */\n\tMultiJoin *joinNode = CitusMakeNode(MultiJoin);\n\tjoinNode->joinRuleType = DUAL_PARTITION_JOIN;\n\tjoinNode->joinType = joinType;\n\tjoinNode->joinClauseList = applicableJoinClauses;\n\n\tSetLeftChild((MultiBinaryNode *) joinNode, (MultiNode *) leftCollectNode);\n\tSetRightChild((MultiBinaryNode *) joinNode, (MultiNode *) rightCollectNode);\n\n\treturn (MultiNode *) joinNode;\n}\n\n\n/* Creates a cartesian product node that joins the left and the right node. */\nstatic MultiNode *\nApplyCartesianProduct(MultiNode *leftNode, MultiNode *rightNode,\n\t\t\t\t\t  List *partitionColumnList, JoinType joinType,\n\t\t\t\t\t  List *applicableJoinClauses)\n{\n\tMultiCartesianProduct *cartesianNode = CitusMakeNode(MultiCartesianProduct);\n\n\tSetLeftChild((MultiBinaryNode *) cartesianNode, leftNode);\n\tSetRightChild((MultiBinaryNode *) cartesianNode, rightNode);\n\n\treturn (MultiNode *) cartesianNode;\n}\n\n\n/*\n * OperatorImplementsEquality returns true if the given opno represents an\n * equality operator. The function retrieves btree interpretation list for this\n * opno and check if BTEqualStrategyNumber strategy is present.\n */\nbool\nOperatorImplementsEquality(Oid opno)\n{\n\tbool equalityOperator = false;\n\tList *btreeIntepretationList = get_op_btree_interpretation(opno);\n\tListCell *btreeInterpretationCell = NULL;\n\tforeach(btreeInterpretationCell, btreeIntepretationList)\n\t{\n\t\tOpBtreeInterpretation *btreeIntepretation = (OpBtreeInterpretation *)\n\t\t\t\t\t\t\t\t\t\t\t\t\tlfirst(btreeInterpretationCell);\n\n\t#if PG_VERSION_NUM >= PG_VERSION_18\n\t\tif (btreeIntepretation->cmptype == BTEqualStrategyNumber)\n\t#else\n\t\tif (btreeIntepretation->strategy == BTEqualStrategyNumber)\n\t#endif\n\t\t{\n\t\t\tequalityOperator = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn equalityOperator;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/multi_physical_planner.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_physical_planner.c\n *\t  Routines for creating physical plans from given multi-relational algebra\n *\t  trees.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <math.h>\n#include <stdint.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/hash.h\"\n#include \"access/heapam.h\"\n#include \"access/nbtree.h\"\n#include \"access/skey.h\"\n#include \"access/xlog.h\"\n#include \"catalog/pg_aggregate.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_operator.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/defrem.h\"\n#include \"commands/sequence.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/pathnodes.h\"\n#include \"nodes/print.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/restrictinfo.h\"\n#include \"optimizer/tlist.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parse_type.h\"\n#include \"parser/parsetree.h\"\n#include \"rewrite/rewriteManip.h\"\n#include \"utils/builtins.h\"\n#include \"utils/catcache.h\"\n#include \"utils/datum.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/guc.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n#include \"utils/typcache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/query_pushdown_planning.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/string_utils.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_protocol.h\"\n\n/* RepartitionJoinBucketCountPerNode determines bucket amount during repartitions */\nint RepartitionJoinBucketCountPerNode = 4;\n\n/* Policy to use when assigning tasks to worker nodes */\nint TaskAssignmentPolicy = TASK_ASSIGNMENT_GREEDY;\nbool EnableUniqueJobIds = true;\n\n\n/*\n * OperatorCache is used for caching operator identifiers for given typeId,\n * accessMethodId and strategyNumber. It is initialized to empty list as\n * there are no items in the cache.\n */\nstatic List *OperatorCache = NIL;\n\n\n/* context passed down in AddAnyValueAggregates mutator */\ntypedef struct AddAnyValueAggregatesContext\n{\n\t/* SortGroupClauses corresponding to the GROUP BY clause */\n\tList *groupClauseList;\n\n\t/* TargetEntry's to which the GROUP BY clauses refer */\n\tList *groupByTargetEntryList;\n\n\t/*\n\t * haveNonVarGrouping is true if there are expressions in the\n\t * GROUP BY target entries. We use this as an optimisation to\n\t * skip expensive checks when possible.\n\t */\n\tbool haveNonVarGrouping;\n} AddAnyValueAggregatesContext;\n\n\n/* Local functions forward declarations for job creation */\nstatic Job * BuildJobTree(MultiTreeRoot *multiTree);\nstatic MultiNode * LeftMostNode(MultiTreeRoot *multiTree);\nstatic Oid RangePartitionJoinBaseRelationId(MultiJoin *joinNode);\nstatic MultiTable * FindTableNode(MultiNode *multiNode, int rangeTableId);\nstatic Query * BuildJobQuery(MultiNode *multiNode, List *dependentJobList);\nstatic List * BaseRangeTableList(MultiNode *multiNode);\nstatic List * QueryTargetList(MultiNode *multiNode);\nstatic List * TargetEntryList(List *expressionList);\nstatic Node * AddAnyValueAggregates(Node *node, AddAnyValueAggregatesContext *context);\nstatic List * QueryGroupClauseList(MultiNode *multiNode);\nstatic List * QuerySelectClauseList(MultiNode *multiNode);\nstatic List * QueryFromList(List *rangeTableList);\nstatic Node * QueryJoinTree(MultiNode *multiNode, List *dependentJobList,\n\t\t\t\t\t\t\tList **rangeTableList);\nstatic void SetJoinRelatedColumnsCompat(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\tOid leftRelId,\n\t\t\t\t\t\t\t\t\t\tOid rightRelId,\n\t\t\t\t\t\t\t\t\t\tList *leftColumnVars,\n\t\t\t\t\t\t\t\t\t\tList *rightColumnVars);\nstatic RangeTblEntry * JoinRangeTableEntry(JoinExpr *joinExpr, List *dependentJobList,\n\t\t\t\t\t\t\t\t\t\t   List *rangeTableList);\nstatic int ExtractRangeTableId(Node *node);\nstatic void ExtractColumns(RangeTblEntry *callingRTE, int rangeTableId,\n\t\t\t\t\t\t   List **columnNames, List **columnVars);\nstatic RangeTblEntry * ConstructCallingRTE(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t   List *dependentJobList);\nstatic Query * BuildSubqueryJobQuery(MultiNode *multiNode);\nstatic void UpdateAllColumnAttributes(Node *columnContainer, List *rangeTableList,\n\t\t\t\t\t\t\t\t\t  List *dependentJobList);\nstatic void UpdateColumnAttributes(Var *column, List *rangeTableList,\n\t\t\t\t\t\t\t\t   List *dependentJobList);\nstatic Index NewTableId(Index originalTableId, List *rangeTableList);\nstatic AttrNumber NewColumnId(Index originalTableId, AttrNumber originalColumnId,\n\t\t\t\t\t\t\t  RangeTblEntry *newRangeTableEntry, List *dependentJobList);\nstatic Job * JobForRangeTable(List *jobList, RangeTblEntry *rangeTableEntry);\nstatic Job * JobForTableIdList(List *jobList, List *searchedTableIdList);\nstatic List * ChildNodeList(MultiNode *multiNode);\nstatic Job * BuildJob(Query *jobQuery, List *dependentJobList);\nstatic MapMergeJob * BuildMapMergeJob(Query *jobQuery, List *dependentJobList,\n\t\t\t\t\t\t\t\t\t  Var *partitionKey, PartitionType partitionType,\n\t\t\t\t\t\t\t\t\t  Oid baseRelationId,\n\t\t\t\t\t\t\t\t\t  BoundaryNodeJobType boundaryNodeJobType);\nstatic uint32 HashPartitionCount(void);\n\n/* Local functions forward declarations for task list creation and helper functions */\nstatic Job * BuildJobTreeTaskList(Job *jobTree,\n\t\t\t\t\t\t\t\t  PlannerRestrictionContext *plannerRestrictionContext);\nstatic bool IsInnerTableOfOuterJoin(RelationRestriction *relationRestriction,\n\t\t\t\t\t\t\t\t\tBitmapset *distributedTables,\n\t\t\t\t\t\t\t\t\tbool *outerPartHasDistributedTable);\nstatic void ErrorIfUnsupportedShardDistribution(Query *query);\nstatic Task * QueryPushdownTaskCreate(Query *originalQuery, int shardIndex,\n\t\t\t\t\t\t\t\t\t  RelationRestrictionContext *restrictionContext,\n\t\t\t\t\t\t\t\t\t  uint32 taskId,\n\t\t\t\t\t\t\t\t\t  TaskType taskType,\n\t\t\t\t\t\t\t\t\t  bool modifyRequiresCoordinatorEvaluation,\n\t\t\t\t\t\t\t\t\t  bool updateQualsForOuterJoin,\n\t\t\t\t\t\t\t\t\t  DeferredErrorMessage **planningError);\nstatic List * SqlTaskList(Job *job);\nstatic bool DependsOnHashPartitionJob(Job *job);\nstatic uint32 AnchorRangeTableId(List *rangeTableList);\nstatic List * BaseRangeTableIdList(List *rangeTableList);\nstatic List * AnchorRangeTableIdList(List *rangeTableList, List *baseRangeTableIdList);\nstatic void AdjustColumnOldAttributes(List *expressionList);\nstatic List * RangeTableFragmentsList(List *rangeTableList, List *whereClauseList,\n\t\t\t\t\t\t\t\t\t  List *dependentJobList);\nstatic OperatorCacheEntry * LookupOperatorByType(Oid typeId, Oid accessMethodId,\n\t\t\t\t\t\t\t\t\t\t\t\t int16 strategyNumber);\nstatic Oid GetOperatorByType(Oid typeId, Oid accessMethodId, int16 strategyNumber);\nstatic List * FragmentCombinationList(List *rangeTableFragmentsList, Query *jobQuery,\n\t\t\t\t\t\t\t\t\t  List *dependentJobList);\nstatic JoinSequenceNode * JoinSequenceArray(List *rangeTableFragmentsList,\n\t\t\t\t\t\t\t\t\t\t\tQuery *jobQuery, List *dependentJobList);\nstatic bool PartitionedOnColumn(Var *column, List *rangeTableList,\n\t\t\t\t\t\t\t\tList *dependentJobList);\nstatic void CheckJoinBetweenColumns(OpExpr *joinClause);\nstatic List * FindRangeTableFragmentsList(List *rangeTableFragmentsList, int taskId);\nstatic bool JoinPrunable(RangeTableFragment *leftFragment,\n\t\t\t\t\t\t RangeTableFragment *rightFragment);\nstatic ShardInterval * FragmentInterval(RangeTableFragment *fragment);\nstatic StringInfo FragmentIntervalString(ShardInterval *fragmentInterval);\nstatic List * DataFetchTaskList(uint64 jobId, uint32 taskIdIndex, List *fragmentList);\nstatic List * BuildRelationShardList(List *rangeTableList, List *fragmentList);\nstatic void UpdateRangeTableAlias(List *rangeTableList, List *fragmentList);\nstatic Alias * FragmentAlias(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t RangeTableFragment *fragment);\nstatic List * FetchTaskResultNameList(List *mapOutputFetchTaskList);\nstatic uint64 AnchorShardId(List *fragmentList, uint32 anchorRangeTableId);\nstatic List * PruneSqlTaskDependencies(List *sqlTaskList);\nstatic List * AssignTaskList(List *sqlTaskList);\nstatic bool HasMergeTaskDependencies(List *sqlTaskList);\nstatic List * GreedyAssignTaskList(List *taskList);\nstatic Task * GreedyAssignTask(WorkerNode *workerNode, List *taskList,\n\t\t\t\t\t\t\t   List *activeShardPlacementLists);\nstatic List * ReorderAndAssignTaskList(List *taskList,\n\t\t\t\t\t\t\t\t\t   ReorderFunction reorderFunction);\nstatic int CompareTasksByShardId(const void *leftElement, const void *rightElement);\nstatic List * ActiveShardPlacementLists(List *taskList);\nstatic List * LeftRotateList(List *list, uint32 rotateCount);\nstatic List * FindDependentMergeTaskList(Task *sqlTask);\nstatic List * AssignDualHashTaskList(List *taskList);\nstatic void AssignDataFetchDependencies(List *taskList);\nstatic uint32 TaskListHighestTaskId(List *taskList);\nstatic List * MapTaskList(MapMergeJob *mapMergeJob, List *filterTaskList);\nstatic StringInfo CreateMapQueryString(MapMergeJob *mapMergeJob, Task *filterTask,\n\t\t\t\t\t\t\t\t\t   uint32 partitionColumnIndex, bool useBinaryFormat);\nstatic char * PartitionResultNamePrefix(uint64 jobId, int32 taskId);\nstatic char * PartitionResultName(uint64 jobId, uint32 taskId, uint32 partitionId);\nstatic ShardInterval ** RangeIntervalArrayWithNullBucket(ShardInterval **intervalArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t int intervalCount);\nstatic List * MergeTaskList(MapMergeJob *mapMergeJob, List *mapTaskList,\n\t\t\t\t\t\t\tuint32 taskIdIndex);\n\nstatic List * FetchEqualityAttrNumsForRTEOpExpr(OpExpr *opExpr);\nstatic List * FetchEqualityAttrNumsForRTEBoolExpr(BoolExpr *boolExpr);\nstatic List * FetchEqualityAttrNumsForList(List *nodeList);\nstatic int PartitionColumnIndex(Var *targetVar, List *targetList);\nstatic List * GetColumnOriginalIndexes(Oid relationId);\nstatic bool QueryTreeHasImproperForDeparseNodes(Node *inputNode, void *context);\nstatic Node * AdjustImproperForDeparseNodes(Node *inputNode, void *context);\nstatic bool IsImproperForDeparseRelabelTypeNode(Node *inputNode);\nstatic bool IsImproperForDeparseCoerceViaIONode(Node *inputNode);\nstatic CollateExpr * RelabelTypeToCollateExpr(RelabelType *relabelType);\n\n\n/*\n * CreatePhysicalDistributedPlan is the entry point for physical plan generation. The\n * function builds the physical plan; this plan includes the list of tasks to be\n * executed on worker nodes, and the final query to run on the master node.\n */\nDistributedPlan *\nCreatePhysicalDistributedPlan(MultiTreeRoot *multiTree,\n\t\t\t\t\t\t\t  PlannerRestrictionContext *plannerRestrictionContext)\n{\n\t/* build the worker job tree and check that we only have one job in the tree */\n\tJob *workerJob = BuildJobTree(multiTree);\n\n\t/* create the tree of executable tasks for the worker job */\n\tworkerJob = BuildJobTreeTaskList(workerJob, plannerRestrictionContext);\n\n\t/* build the final merge query to execute on the master */\n\tList *masterDependentJobList = list_make1(workerJob);\n\tQuery *combineQuery = BuildJobQuery((MultiNode *) multiTree, masterDependentJobList);\n\n\tDistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan);\n\tdistributedPlan->workerJob = workerJob;\n\tdistributedPlan->combineQuery = combineQuery;\n\tdistributedPlan->modLevel = ROW_MODIFY_READONLY;\n\tdistributedPlan->expectResults = true;\n\n\treturn distributedPlan;\n}\n\n\n/*\n * ModifyLocalTableJob returns true if the given task contains\n * a modification of local table.\n */\nbool\nModifyLocalTableJob(Job *job)\n{\n\tif (job == NULL)\n\t{\n\t\treturn false;\n\t}\n\tList *taskList = job->taskList;\n\tif (list_length(taskList) != 1)\n\t{\n\t\treturn false;\n\t}\n\tTask *singleTask = (Task *) linitial(taskList);\n\treturn singleTask->isLocalTableModification;\n}\n\n\n/*\n * BuildJobTree builds the physical job tree from the given logical plan tree.\n * The function walks over the logical plan from the bottom up, finds boundaries\n * for jobs, and creates the query structure for each job. The function also\n * sets dependencies between jobs, and then returns the top level worker job.\n */\nstatic Job *\nBuildJobTree(MultiTreeRoot *multiTree)\n{\n\t/* start building the tree from the deepest left node */\n\tMultiNode *leftMostNode = LeftMostNode(multiTree);\n\tMultiNode *currentNode = leftMostNode;\n\tMultiNode *parentNode = ParentNode(currentNode);\n\tList *loopDependentJobList = NIL;\n\tJob *topLevelJob = NULL;\n\n\twhile (parentNode != NULL)\n\t{\n\t\tCitusNodeTag currentNodeType = CitusNodeTag(currentNode);\n\t\tCitusNodeTag parentNodeType = CitusNodeTag(parentNode);\n\t\tBoundaryNodeJobType boundaryNodeJobType = JOB_INVALID_FIRST;\n\n\t\t/* we first check if this node forms the boundary for a remote job */\n\t\tif (currentNodeType == T_MultiJoin)\n\t\t{\n\t\t\tMultiJoin *joinNode = (MultiJoin *) currentNode;\n\t\t\tif (joinNode->joinRuleType == SINGLE_HASH_PARTITION_JOIN ||\n\t\t\t\tjoinNode->joinRuleType == SINGLE_RANGE_PARTITION_JOIN ||\n\t\t\t\tjoinNode->joinRuleType == DUAL_PARTITION_JOIN)\n\t\t\t{\n\t\t\t\tboundaryNodeJobType = JOIN_MAP_MERGE_JOB;\n\t\t\t}\n\t\t}\n\t\telse if (currentNodeType == T_MultiCollect &&\n\t\t\t\t parentNodeType != T_MultiPartition)\n\t\t{\n\t\t\tboundaryNodeJobType = TOP_LEVEL_WORKER_JOB;\n\t\t}\n\n\t\t/*\n\t\t * If this node is at the boundary for a repartition or top level worker\n\t\t * job, we build the corresponding job(s) and set their dependencies.\n\t\t */\n\t\tif (boundaryNodeJobType == JOIN_MAP_MERGE_JOB)\n\t\t{\n\t\t\tMultiJoin *joinNode = (MultiJoin *) currentNode;\n\t\t\tMultiNode *leftChildNode = joinNode->binaryNode.leftChildNode;\n\t\t\tMultiNode *rightChildNode = joinNode->binaryNode.rightChildNode;\n\n\t\t\tPartitionType partitionType = PARTITION_INVALID_FIRST;\n\t\t\tOid baseRelationId = InvalidOid;\n\n\t\t\tif (joinNode->joinRuleType == SINGLE_RANGE_PARTITION_JOIN)\n\t\t\t{\n\t\t\t\tpartitionType = RANGE_PARTITION_TYPE;\n\t\t\t\tbaseRelationId = RangePartitionJoinBaseRelationId(joinNode);\n\t\t\t}\n\t\t\telse if (joinNode->joinRuleType == SINGLE_HASH_PARTITION_JOIN)\n\t\t\t{\n\t\t\t\tpartitionType = SINGLE_HASH_PARTITION_TYPE;\n\t\t\t\tbaseRelationId = RangePartitionJoinBaseRelationId(joinNode);\n\t\t\t}\n\t\t\telse if (joinNode->joinRuleType == DUAL_PARTITION_JOIN)\n\t\t\t{\n\t\t\t\tpartitionType = DUAL_HASH_PARTITION_TYPE;\n\t\t\t}\n\n\t\t\tif (CitusIsA(leftChildNode, MultiPartition))\n\t\t\t{\n\t\t\t\tMultiPartition *partitionNode = (MultiPartition *) leftChildNode;\n\t\t\t\tMultiNode *queryNode = GrandChildNode((MultiUnaryNode *) partitionNode);\n\t\t\t\tVar *partitionKey = partitionNode->partitionColumn;\n\n\t\t\t\t/* build query and partition job */\n\t\t\t\tList *dependentJobList = list_copy(loopDependentJobList);\n\t\t\t\tQuery *jobQuery = BuildJobQuery(queryNode, dependentJobList);\n\n\t\t\t\tMapMergeJob *mapMergeJob = BuildMapMergeJob(jobQuery, dependentJobList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionKey, partitionType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbaseRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tJOIN_MAP_MERGE_JOB);\n\n\t\t\t\t/* reset dependent job list */\n\t\t\t\tloopDependentJobList = NIL;\n\t\t\t\tloopDependentJobList = list_make1(mapMergeJob);\n\t\t\t}\n\n\t\t\tif (CitusIsA(rightChildNode, MultiPartition))\n\t\t\t{\n\t\t\t\tMultiPartition *partitionNode = (MultiPartition *) rightChildNode;\n\t\t\t\tMultiNode *queryNode = GrandChildNode((MultiUnaryNode *) partitionNode);\n\t\t\t\tVar *partitionKey = partitionNode->partitionColumn;\n\n\t\t\t\t/*\n\t\t\t\t * The right query and right partition job do not depend on any\n\t\t\t\t * jobs since our logical plan tree is left deep.\n\t\t\t\t */\n\t\t\t\tQuery *jobQuery = BuildJobQuery(queryNode, NIL);\n\t\t\t\tMapMergeJob *mapMergeJob = BuildMapMergeJob(jobQuery, NIL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionKey, partitionType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbaseRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tJOIN_MAP_MERGE_JOB);\n\n\t\t\t\t/* append to the dependent job list for on-going dependencies */\n\t\t\t\tloopDependentJobList = lappend(loopDependentJobList, mapMergeJob);\n\t\t\t}\n\t\t}\n\t\telse if (boundaryNodeJobType == TOP_LEVEL_WORKER_JOB)\n\t\t{\n\t\t\tMultiNode *childNode = ChildNode((MultiUnaryNode *) currentNode);\n\t\t\tList *dependentJobList = list_copy(loopDependentJobList);\n\t\t\tbool subqueryPushdown = false;\n\n\t\t\tList *subqueryMultiTableList = SubqueryMultiTableList(childNode);\n\t\t\tint subqueryCount = list_length(subqueryMultiTableList);\n\n\t\t\tif (subqueryCount > 0)\n\t\t\t{\n\t\t\t\tsubqueryPushdown = true;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Build top level query. If subquery pushdown is set, we use\n\t\t\t * sligthly different version of BuildJobQuery(). They are similar\n\t\t\t * but we don't need some parts of BuildJobQuery() for subquery\n\t\t\t * pushdown such as updating column attributes etc.\n\t\t\t */\n\t\t\tif (subqueryPushdown)\n\t\t\t{\n\t\t\t\tQuery *topLevelQuery = BuildSubqueryJobQuery(childNode);\n\n\t\t\t\ttopLevelJob = BuildJob(topLevelQuery, dependentJobList);\n\t\t\t\ttopLevelJob->subqueryPushdown = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tQuery *topLevelQuery = BuildJobQuery(childNode, dependentJobList);\n\n\t\t\t\ttopLevelJob = BuildJob(topLevelQuery, dependentJobList);\n\t\t\t}\n\t\t}\n\n\t\t/* walk up the tree */\n\t\tcurrentNode = parentNode;\n\t\tparentNode = ParentNode(currentNode);\n\t}\n\n\treturn topLevelJob;\n}\n\n\n/*\n * LeftMostNode finds the deepest left node in the left-deep logical plan tree.\n * We build the physical plan by traversing the logical plan from the bottom up;\n * and this function helps us find the bottom of the logical tree.\n */\nstatic MultiNode *\nLeftMostNode(MultiTreeRoot *multiTree)\n{\n\tMultiNode *currentNode = (MultiNode *) multiTree;\n\tMultiNode *leftChildNode = ChildNode((MultiUnaryNode *) multiTree);\n\n\twhile (leftChildNode != NULL)\n\t{\n\t\tcurrentNode = leftChildNode;\n\n\t\tif (UnaryOperator(currentNode))\n\t\t{\n\t\t\tleftChildNode = ChildNode((MultiUnaryNode *) currentNode);\n\t\t}\n\t\telse if (BinaryOperator(currentNode))\n\t\t{\n\t\t\tMultiBinaryNode *binaryNode = (MultiBinaryNode *) currentNode;\n\t\t\tleftChildNode = binaryNode->leftChildNode;\n\t\t}\n\t}\n\n\treturn currentNode;\n}\n\n\n/*\n * RangePartitionJoinBaseRelationId finds partition node from join node, and\n * returns base relation id of this node. Note that this function assumes that\n * given join node is range partition join type.\n */\nstatic Oid\nRangePartitionJoinBaseRelationId(MultiJoin *joinNode)\n{\n\tMultiPartition *partitionNode = NULL;\n\n\tMultiNode *leftChildNode = joinNode->binaryNode.leftChildNode;\n\tMultiNode *rightChildNode = joinNode->binaryNode.rightChildNode;\n\n\tif (CitusIsA(leftChildNode, MultiPartition))\n\t{\n\t\tpartitionNode = (MultiPartition *) leftChildNode;\n\t}\n\telse if (CitusIsA(rightChildNode, MultiPartition))\n\t{\n\t\tpartitionNode = (MultiPartition *) rightChildNode;\n\t}\n\telse\n\t{\n\t\tAssert(false);\n\t}\n\n\tIndex baseTableId = partitionNode->splitPointTableId;\n\tMultiTable *baseTable = FindTableNode((MultiNode *) joinNode, baseTableId);\n\tOid baseRelationId = baseTable->relationId;\n\n\treturn baseRelationId;\n}\n\n\n/*\n * FindTableNode walks over the given logical plan tree, and returns the table\n * node that corresponds to the given range tableId.\n */\nstatic MultiTable *\nFindTableNode(MultiNode *multiNode, int rangeTableId)\n{\n\tMultiTable *foundTableNode = NULL;\n\tList *tableNodeList = FindNodesOfType(multiNode, T_MultiTable);\n\tListCell *tableNodeCell = NULL;\n\n\tforeach(tableNodeCell, tableNodeList)\n\t{\n\t\tMultiTable *tableNode = (MultiTable *) lfirst(tableNodeCell);\n\t\tif (tableNode->rangeTableId == rangeTableId)\n\t\t{\n\t\t\tfoundTableNode = tableNode;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tAssert(foundTableNode != NULL);\n\treturn foundTableNode;\n}\n\n\n/*\n * BuildJobQuery traverses the given logical plan tree, determines the job that\n * corresponds to this part of the tree, and builds the query structure for that\n * particular job. The function assumes that jobs this particular job depends on\n * have already been built, as their output is needed to build the query.\n */\nstatic Query *\nBuildJobQuery(MultiNode *multiNode, List *dependentJobList)\n{\n\tbool updateColumnAttributes = false;\n\tList *targetList = NIL;\n\tList *sortClauseList = NIL;\n\tNode *limitCount = NULL;\n\tNode *limitOffset = NULL;\n\tLimitOption limitOption = LIMIT_OPTION_COUNT;\n\tNode *havingQual = NULL;\n\tbool hasDistinctOn = false;\n\tList *distinctClause = NIL;\n\tbool isRepartitionJoin = false;\n\tbool hasWindowFuncs = false;\n\tList *windowClause = NIL;\n\n\t/* we start building jobs from below the collect node */\n\tAssert(!CitusIsA(multiNode, MultiCollect));\n\n\t/*\n\t * First check if we are building a master/worker query. If we are building\n\t * a worker query, we update the column attributes for target entries, select\n\t * and join columns. Because if underlying query includes repartition joins,\n\t * then we create multiple queries from a join. In this case, range table lists\n\t * and column lists are subject to change.\n\t *\n\t * Note that we don't do this for master queries, as column attributes for\n\t * master target entries are already set during the master/worker split.\n\t */\n\tMultiNode *parentNode = ParentNode(multiNode);\n\tif (parentNode != NULL)\n\t{\n\t\tupdateColumnAttributes = true;\n\t}\n\n\t/*\n\t * If we are building this query on a repartitioned subquery job then we\n\t * don't need to update column attributes.\n\t */\n\tif (dependentJobList != NIL)\n\t{\n\t\tJob *job = (Job *) linitial(dependentJobList);\n\t\tif (CitusIsA(job, MapMergeJob))\n\t\t{\n\t\t\tisRepartitionJoin = true;\n\t\t}\n\t}\n\n\t/*\n\t * If we have an extended operator, then we copy the operator's target list.\n\t * Otherwise, we use the target list based on the MultiProject node at this\n\t * level in the query tree.\n\t */\n\tList *extendedOpNodeList = FindNodesOfType(multiNode, T_MultiExtendedOp);\n\tif (extendedOpNodeList != NIL)\n\t{\n\t\tMultiExtendedOp *extendedOp = (MultiExtendedOp *) linitial(extendedOpNodeList);\n\t\ttargetList = copyObject(extendedOp->targetList);\n\t\tdistinctClause = extendedOp->distinctClause;\n\t\thasDistinctOn = extendedOp->hasDistinctOn;\n\t\thasWindowFuncs = extendedOp->hasWindowFuncs;\n\t\twindowClause = extendedOp->windowClause;\n\t}\n\telse\n\t{\n\t\ttargetList = QueryTargetList(multiNode);\n\t}\n\n\t/* build the join tree and the range table list */\n\tList *rangeTableList = BaseRangeTableList(multiNode);\n\tNode *joinRoot = QueryJoinTree(multiNode, dependentJobList, &rangeTableList);\n\n\t/* update the column attributes for target entries */\n\tif (updateColumnAttributes)\n\t{\n\t\tUpdateAllColumnAttributes((Node *) targetList, rangeTableList, dependentJobList);\n\t}\n\n\t/* extract limit count/offset and sort clauses */\n\tif (extendedOpNodeList != NIL)\n\t{\n\t\tMultiExtendedOp *extendedOp = (MultiExtendedOp *) linitial(extendedOpNodeList);\n\n\t\tlimitCount = extendedOp->limitCount;\n\t\tlimitOffset = extendedOp->limitOffset;\n\t\tlimitOption = extendedOp->limitOption;\n\t\tsortClauseList = extendedOp->sortClauseList;\n\t\thavingQual = extendedOp->havingQual;\n\t}\n\n\t/* build group clauses */\n\tList *groupClauseList = QueryGroupClauseList(multiNode);\n\n\n\t/* build the where clause list using select predicates */\n\tList *selectClauseList = QuerySelectClauseList(multiNode);\n\n\t/* set correct column attributes for select and having clauses */\n\tif (updateColumnAttributes)\n\t{\n\t\tUpdateAllColumnAttributes((Node *) selectClauseList, rangeTableList,\n\t\t\t\t\t\t\t\t  dependentJobList);\n\t\tUpdateAllColumnAttributes(havingQual, rangeTableList, dependentJobList);\n\t}\n\n\t/*\n\t * Group by on primary key allows all columns to appear in the target\n\t * list, but after re-partitioning we will be querying an intermediate\n\t * table that does not have the primary key. We therefore wrap all the\n\t * columns that do not appear in the GROUP BY in an any_value aggregate.\n\t */\n\tif (groupClauseList != NIL && isRepartitionJoin)\n\t{\n\t\ttargetList = (List *) WrapUngroupedVarsInAnyValueAggregate(\n\t\t\t(Node *) targetList, groupClauseList, targetList, true);\n\n\t\thavingQual = WrapUngroupedVarsInAnyValueAggregate(\n\t\t\t(Node *) havingQual, groupClauseList, targetList, false);\n\t}\n\n\t/*\n\t * Build the From/Where construct. We keep the where-clause list implicitly\n\t * AND'd, since both partition and join pruning depends on the clauses being\n\t * expressed as a list.\n\t */\n\tFromExpr *joinTree = makeNode(FromExpr);\n\tjoinTree->quals = (Node *) list_copy(selectClauseList);\n\tjoinTree->fromlist = list_make1(joinRoot);\n\n\t/* build the query structure for this job */\n\tQuery *jobQuery = makeNode(Query);\n\tjobQuery->commandType = CMD_SELECT;\n\tjobQuery->querySource = QSRC_ORIGINAL;\n\tjobQuery->canSetTag = true;\n\tjobQuery->rtable = rangeTableList;\n\tjobQuery->targetList = targetList;\n\tjobQuery->jointree = joinTree;\n\tjobQuery->sortClause = sortClauseList;\n\tjobQuery->groupClause = groupClauseList;\n\tjobQuery->limitOffset = limitOffset;\n\tjobQuery->limitCount = limitCount;\n\tjobQuery->limitOption = limitOption;\n\tjobQuery->havingQual = havingQual;\n\tjobQuery->hasAggs = contain_aggs_of_level((Node *) targetList, 0) ||\n\t\t\t\t\t\tcontain_aggs_of_level((Node *) havingQual, 0);\n\tjobQuery->distinctClause = distinctClause;\n\tjobQuery->hasDistinctOn = hasDistinctOn;\n\tjobQuery->windowClause = windowClause;\n\tjobQuery->hasWindowFuncs = hasWindowFuncs;\n\tjobQuery->hasSubLinks = checkExprHasSubLink((Node *) jobQuery);\n\n\tAssert(jobQuery->hasWindowFuncs == contain_window_function((Node *) jobQuery));\n\n\treturn jobQuery;\n}\n\n\n/*\n * BaseRangeTableList returns the list of range table entries for base tables in\n * the query. These base tables stand in contrast to derived tables generated by\n * repartition jobs. Note that this function only considers base tables relevant\n * to the current query, and does not visit nodes under the collect node.\n */\nstatic List *\nBaseRangeTableList(MultiNode *multiNode)\n{\n\tList *baseRangeTableList = NIL;\n\tList *pendingNodeList = list_make1(multiNode);\n\n\twhile (pendingNodeList != NIL)\n\t{\n\t\tMultiNode *currMultiNode = (MultiNode *) linitial(pendingNodeList);\n\t\tCitusNodeTag nodeType = CitusNodeTag(currMultiNode);\n\t\tpendingNodeList = list_delete_first(pendingNodeList);\n\n\t\tif (nodeType == T_MultiTable)\n\t\t{\n\t\t\t/*\n\t\t\t * We represent subqueries as MultiTables, and so for base table\n\t\t\t * entries we skip the subquery ones.\n\t\t\t */\n\t\t\tMultiTable *multiTable = (MultiTable *) currMultiNode;\n\t\t\tif (multiTable->relationId != SUBQUERY_RELATION_ID &&\n\t\t\t\tmultiTable->relationId != SUBQUERY_PUSHDOWN_RELATION_ID)\n\t\t\t{\n\t\t\t\tRangeTblEntry *rangeTableEntry = makeNode(RangeTblEntry);\n\t\t\t\trangeTableEntry->inFromCl = true;\n\t\t\t\trangeTableEntry->eref = multiTable->referenceNames;\n\t\t\t\trangeTableEntry->alias = multiTable->alias;\n\t\t\t\trangeTableEntry->relid = multiTable->relationId;\n\t\t\t\trangeTableEntry->inh = multiTable->includePartitions;\n\t\t\t\trangeTableEntry->tablesample = multiTable->tablesample;\n\n\t\t\t\tSetRangeTblExtraData(rangeTableEntry, CITUS_RTE_RELATION, NULL, NULL,\n\t\t\t\t\t\t\t\t\t list_make1_int(multiTable->rangeTableId),\n\t\t\t\t\t\t\t\t\t NIL, NIL, NIL, NIL);\n\n\t\t\t\tbaseRangeTableList = lappend(baseRangeTableList, rangeTableEntry);\n\t\t\t}\n\t\t}\n\n\t\t/* do not visit nodes that belong to remote queries */\n\t\tif (nodeType != T_MultiCollect)\n\t\t{\n\t\t\tList *childNodeList = ChildNodeList(currMultiNode);\n\t\t\tpendingNodeList = list_concat(pendingNodeList, childNodeList);\n\t\t}\n\t}\n\n\treturn baseRangeTableList;\n}\n\n\n/*\n * DerivedRangeTableEntry builds a range table entry for the derived table. This\n * derived table either represents the output of a repartition job; or the data\n * on worker nodes in case of the master node query.\n */\nRangeTblEntry *\nDerivedRangeTableEntry(MultiNode *multiNode, List *columnList, List *tableIdList,\n\t\t\t\t\t   List *funcColumnNames, List *funcColumnTypes,\n\t\t\t\t\t   List *funcColumnTypeMods, List *funcCollations)\n{\n\tRangeTblEntry *rangeTableEntry = makeNode(RangeTblEntry);\n\trangeTableEntry->inFromCl = true;\n\trangeTableEntry->eref = makeNode(Alias);\n\trangeTableEntry->eref->colnames = columnList;\n\n\tSetRangeTblExtraData(rangeTableEntry, CITUS_RTE_REMOTE_QUERY, NULL, NULL, tableIdList,\n\t\t\t\t\t\t funcColumnNames, funcColumnTypes, funcColumnTypeMods,\n\t\t\t\t\t\t funcCollations);\n\n\treturn rangeTableEntry;\n}\n\n\n/*\n * DerivedColumnNameList builds a column name list for derived (intermediate)\n * tables. These column names are then used when building the create stament\n * query string for derived tables.\n */\nList *\nDerivedColumnNameList(uint32 columnCount, uint64 generatingJobId)\n{\n\tList *columnNameList = NIL;\n\n\tfor (uint32 columnIndex = 0; columnIndex < columnCount; columnIndex++)\n\t{\n\t\tStringInfo columnName = makeStringInfo();\n\n\t\tappendStringInfo(columnName, \"intermediate_column_\");\n\t\tappendStringInfo(columnName, UINT64_FORMAT \"_\", generatingJobId);\n\t\tappendStringInfo(columnName, \"%u\", columnIndex);\n\n\t\tString *columnValue = makeString(columnName->data);\n\t\tcolumnNameList = lappend(columnNameList, columnValue);\n\t}\n\n\treturn columnNameList;\n}\n\n\n/*\n * QueryTargetList returns the target entry list for the projected columns\n * needed to evaluate the operators above the given multiNode. To do this,\n * the function retrieves a list of all MultiProject nodes below the given\n * node and picks the columns from the top-most MultiProject node, as this\n * will be the minimal list of columns needed. Note that this function relies\n * on a pre-order traversal of the operator tree by the function FindNodesOfType.\n */\nstatic List *\nQueryTargetList(MultiNode *multiNode)\n{\n\tList *projectNodeList = FindNodesOfType(multiNode, T_MultiProject);\n\tif (list_length(projectNodeList) == 0)\n\t{\n\t\t/*\n\t\t * The physical planner assumes that all worker queries would have\n\t\t * target list entries based on the fact that at least the column\n\t\t * on the JOINs have to be on the target list. However, there is\n\t\t * an exception to that if there is a cartesian product join and\n\t\t * there is no additional target list entries belong to one side\n\t\t * of the JOIN. Once we support cartesian product join, we should\n\t\t * remove this error.\n\t\t */\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot perform distributed planning on this query\"),\n\t\t\t\t\t\terrdetail(\"Cartesian products are currently unsupported\")));\n\t}\n\n\tMultiProject *topProjectNode = (MultiProject *) linitial(projectNodeList);\n\tList *columnList = topProjectNode->columnList;\n\tList *queryTargetList = TargetEntryList(columnList);\n\n\tAssert(queryTargetList != NIL);\n\treturn queryTargetList;\n}\n\n\n/*\n * TargetEntryList creates a target entry for each expression in the given list,\n * and returns the newly created target entries in a list.\n */\nstatic List *\nTargetEntryList(List *expressionList)\n{\n\tList *targetEntryList = NIL;\n\tListCell *expressionCell = NULL;\n\n\tforeach(expressionCell, expressionList)\n\t{\n\t\tExpr *expression = (Expr *) lfirst(expressionCell);\n\t\tint columnNumber = list_length(targetEntryList) + 1;\n\n\t\tStringInfo columnName = makeStringInfo();\n\t\tappendStringInfo(columnName, \"column%d\", columnNumber);\n\n\t\tTargetEntry *targetEntry = makeTargetEntry(expression, columnNumber,\n\t\t\t\t\t\t\t\t\t\t\t\t   columnName->data, false);\n\n\t\ttargetEntryList = lappend(targetEntryList, targetEntry);\n\t}\n\n\treturn targetEntryList;\n}\n\n\n/*\n * WrapUngroupedVarsInAnyValueAggregate finds Var nodes in the expression\n * that do not refer to any GROUP BY column and wraps them in an any_value\n * aggregate. These columns are allowed when the GROUP BY is on a primary\n * key of a relation, but not if we wrap the relation in a subquery.\n * However, since we still know the value is unique, any_value gives the\n * right result.\n */\nNode *\nWrapUngroupedVarsInAnyValueAggregate(Node *expression, List *groupClauseList,\n\t\t\t\t\t\t\t\t\t List *targetList, bool checkExpressionEquality)\n{\n\tif (expression == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tAddAnyValueAggregatesContext context;\n\tcontext.groupClauseList = groupClauseList;\n\tcontext.groupByTargetEntryList = GroupTargetEntryList(groupClauseList, targetList);\n\tcontext.haveNonVarGrouping = false;\n\n\tif (checkExpressionEquality)\n\t{\n\t\t/*\n\t\t * If the GROUP BY contains non-Var expressions, we need to do an expensive\n\t\t * subexpression equality check.\n\t\t */\n\t\tTargetEntry *targetEntry = NULL;\n\t\tforeach_declared_ptr(targetEntry, context.groupByTargetEntryList)\n\t\t{\n\t\t\tif (!IsA(targetEntry->expr, Var))\n\t\t\t{\n\t\t\t\tcontext.haveNonVarGrouping = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* put the result in the same memory context */\n\tMemoryContext nodeContext = GetMemoryChunkContext(expression);\n\tMemoryContext oldContext = MemoryContextSwitchTo(nodeContext);\n\n\tNode *result = expression_tree_mutator(expression, AddAnyValueAggregates,\n\t\t\t\t\t\t\t\t\t\t   &context);\n\n\tMemoryContextSwitchTo(oldContext);\n\n\treturn result;\n}\n\n\n/*\n * AddAnyValueAggregates wraps all vars that do not appear in the GROUP BY\n * clause or are inside an aggregate function in an any_value aggregate\n * function. This is needed because postgres allows columns that are not\n * in the GROUP BY to appear on the target list as long as the primary key\n * of the table is in the GROUP BY, but we sometimes wrap the join tree\n * in a subquery in which case the primary key information is lost.\n *\n * This function copies parts of the node tree, but may contain references\n * to the original node tree.\n *\n * The implementation is derived from / inspired by\n * check_ungrouped_columns_walker.\n */\nstatic Node *\nAddAnyValueAggregates(Node *node, AddAnyValueAggregatesContext *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn node;\n\t}\n\n\tif (IsA(node, Aggref) || IsA(node, GroupingFunc))\n\t{\n\t\t/* any column is allowed to appear in an aggregate or grouping */\n\t\treturn node;\n\t}\n\telse if (IsA(node, Var))\n\t{\n\t\tVar *var = (Var *) node;\n\n\t\t/*\n\t\t * Check whether this Var appears in the GROUP BY.\n\t\t */\n\t\tTargetEntry *groupByTargetEntry = NULL;\n\t\tforeach_declared_ptr(groupByTargetEntry, context->groupByTargetEntryList)\n\t\t{\n\t\t\tif (!IsA(groupByTargetEntry->expr, Var))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tVar *groupByVar = (Var *) groupByTargetEntry->expr;\n\n\t\t\t/* we should only be doing this at the top level of the query */\n\t\t\tAssert(groupByVar->varlevelsup == 0);\n\n\t\t\tif (var->varno == groupByVar->varno &&\n\t\t\t\tvar->varattno == groupByVar->varattno)\n\t\t\t{\n\t\t\t\t/* this Var is in the GROUP BY, do not wrap it */\n\t\t\t\treturn node;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * We have found a Var that does not appear in the GROUP BY.\n\t\t * Wrap it in an any_value aggregate.\n\t\t */\n\t\tAggref *agg = makeNode(Aggref);\n\t\tagg->aggfnoid = CitusAnyValueFunctionId();\n\t\tagg->aggtype = var->vartype;\n\t\tagg->args = list_make1(makeTargetEntry((Expr *) var, 1, NULL, false));\n\t\tagg->aggkind = AGGKIND_NORMAL;\n\t\tagg->aggtranstype = InvalidOid;\n\t\tagg->aggargtypes = list_make1_oid(var->vartype);\n\t\tagg->aggsplit = AGGSPLIT_SIMPLE;\n\t\tagg->aggcollid = exprCollation((Node *) var);\n\t\treturn (Node *) agg;\n\t}\n\telse if (context->haveNonVarGrouping)\n\t{\n\t\t/*\n\t\t * The GROUP BY contains at least one expression. Check whether the\n\t\t * current expression is equal to one of the GROUP BY expressions.\n\t\t * Otherwise, continue to descend into subexpressions.\n\t\t */\n\t\tTargetEntry *groupByTargetEntry = NULL;\n\t\tforeach_declared_ptr(groupByTargetEntry, context->groupByTargetEntryList)\n\t\t{\n\t\t\tif (equal(node, groupByTargetEntry->expr))\n\t\t\t{\n\t\t\t\t/* do not descend into mutator, all Vars are safe */\n\t\t\t\treturn node;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn expression_tree_mutator(node, AddAnyValueAggregates, context);\n}\n\n\n/*\n * QueryGroupClauseList extracts the group clause list from the logical plan. If\n * no grouping clauses exist, the function returns an empty list.\n */\nstatic List *\nQueryGroupClauseList(MultiNode *multiNode)\n{\n\tList *groupClauseList = NIL;\n\tList *pendingNodeList = list_make1(multiNode);\n\n\twhile (pendingNodeList != NIL)\n\t{\n\t\tMultiNode *currMultiNode = (MultiNode *) linitial(pendingNodeList);\n\t\tCitusNodeTag nodeType = CitusNodeTag(currMultiNode);\n\t\tpendingNodeList = list_delete_first(pendingNodeList);\n\n\t\t/* extract the group clause list from the extended operator */\n\t\tif (nodeType == T_MultiExtendedOp)\n\t\t{\n\t\t\tMultiExtendedOp *extendedOpNode = (MultiExtendedOp *) currMultiNode;\n\t\t\tgroupClauseList = extendedOpNode->groupClauseList;\n\t\t}\n\n\t\t/* add children only if this node isn't a multi collect and multi table */\n\t\tif (nodeType != T_MultiCollect && nodeType != T_MultiTable)\n\t\t{\n\t\t\tList *childNodeList = ChildNodeList(currMultiNode);\n\t\t\tpendingNodeList = list_concat(pendingNodeList, childNodeList);\n\t\t}\n\t}\n\n\treturn groupClauseList;\n}\n\n\n/*\n * QuerySelectClauseList traverses the given logical plan tree, and extracts all\n * select clauses from the select nodes. Note that this function does not walk\n * below a collect node; the clauses below the collect node apply to a remote\n * query, and they would have been captured by the remote job we depend upon.\n */\nstatic List *\nQuerySelectClauseList(MultiNode *multiNode)\n{\n\tList *selectClauseList = NIL;\n\tList *pendingNodeList = list_make1(multiNode);\n\n\twhile (pendingNodeList != NIL)\n\t{\n\t\tMultiNode *currMultiNode = (MultiNode *) linitial(pendingNodeList);\n\t\tCitusNodeTag nodeType = CitusNodeTag(currMultiNode);\n\t\tpendingNodeList = list_delete_first(pendingNodeList);\n\n\t\t/* extract select clauses from the multi select node */\n\t\tif (nodeType == T_MultiSelect)\n\t\t{\n\t\t\tMultiSelect *selectNode = (MultiSelect *) currMultiNode;\n\t\t\tList *clauseList = copyObject(selectNode->selectClauseList);\n\t\t\tselectClauseList = list_concat(selectClauseList, clauseList);\n\t\t}\n\n\t\t/* add children only if this node isn't a multi collect */\n\t\tif (nodeType != T_MultiCollect)\n\t\t{\n\t\t\tList *childNodeList = ChildNodeList(currMultiNode);\n\t\t\tpendingNodeList = list_concat(pendingNodeList, childNodeList);\n\t\t}\n\t}\n\n\treturn selectClauseList;\n}\n\n\n/*\n * Create a tree of JoinExpr and RangeTblRef nodes for the job query from\n * a given multiNode. If the tree contains MultiCollect or MultiJoin nodes,\n * add corresponding entries to the range table list. We need to construct\n * the entries at the same time as the tree to know the appropriate rtindex.\n */\nstatic Node *\nQueryJoinTree(MultiNode *multiNode, List *dependentJobList, List **rangeTableList)\n{\n\tCitusNodeTag nodeType = CitusNodeTag(multiNode);\n\n\tswitch (nodeType)\n\t{\n\t\tcase T_MultiJoin:\n\t\t{\n\t\t\tMultiJoin *joinNode = (MultiJoin *) multiNode;\n\t\t\tMultiBinaryNode *binaryNode = (MultiBinaryNode *) multiNode;\n\t\t\tListCell *columnCell = NULL;\n\t\t\tJoinExpr *joinExpr = makeNode(JoinExpr);\n\t\t\tjoinExpr->jointype = joinNode->joinType;\n\t\t\tjoinExpr->isNatural = false;\n\t\t\tjoinExpr->larg = QueryJoinTree(binaryNode->leftChildNode, dependentJobList,\n\t\t\t\t\t\t\t\t\t\t   rangeTableList);\n\t\t\tjoinExpr->rarg = QueryJoinTree(binaryNode->rightChildNode, dependentJobList,\n\t\t\t\t\t\t\t\t\t\t   rangeTableList);\n\t\t\tjoinExpr->usingClause = NIL;\n\t\t\tjoinExpr->alias = NULL;\n\t\t\tjoinExpr->rtindex = list_length(*rangeTableList) + 1;\n\n\t\t\t/*\n\t\t\t * PostgreSQL's optimizer may mark left joins as anti-joins, when there\n\t\t\t * is a right-hand-join-key-is-null restriction, but there is no logic\n\t\t\t * in ruleutils to deparse anti-joins, so we cannot construct a task\n\t\t\t * query containing anti-joins. We therefore translate anti-joins back\n\t\t\t * into left-joins. At some point, we may also want to use different\n\t\t\t * join pruning logic for anti-joins.\n\t\t\t *\n\t\t\t * This approach would not work for anti-joins introduced via NOT EXISTS\n\t\t\t * sublinks, but currently such queries are prevented by error checks in\n\t\t\t * the logical planner.\n\t\t\t */\n\t\t\tif (joinExpr->jointype == JOIN_ANTI)\n\t\t\t{\n\t\t\t\tjoinExpr->jointype = JOIN_LEFT;\n\t\t\t}\n\n\t\t\t/* fix the column attributes in ON (...) clauses */\n\t\t\tList *columnList = pull_var_clause_default((Node *) joinNode->joinClauseList);\n\t\t\tforeach(columnCell, columnList)\n\t\t\t{\n\t\t\t\tVar *column = (Var *) lfirst(columnCell);\n\t\t\t\tUpdateColumnAttributes(column, *rangeTableList, dependentJobList);\n\n\t\t\t\t/* adjust our column old attributes for partition pruning to work */\n\t\t\t\tcolumn->varnosyn = column->varno;\n\t\t\t\tcolumn->varattnosyn = column->varattno;\n\t\t\t}\n\n\t\t\t/* make AND clauses explicit after fixing them */\n\t\t\tjoinExpr->quals = (Node *) make_ands_explicit(joinNode->joinClauseList);\n\n\t\t\tRangeTblEntry *rangeTableEntry = JoinRangeTableEntry(joinExpr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t dependentJobList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t *rangeTableList);\n\t\t\t*rangeTableList = lappend(*rangeTableList, rangeTableEntry);\n\n\t\t\treturn (Node *) joinExpr;\n\t\t}\n\n\t\tcase T_MultiTable:\n\t\t{\n\t\t\tMultiTable *rangeTableNode = (MultiTable *) multiNode;\n\t\t\tMultiUnaryNode *unaryNode = (MultiUnaryNode *) multiNode;\n\n\t\t\tif (unaryNode->childNode != NULL)\n\t\t\t{\n\t\t\t\t/* MultiTable is actually a subquery, return the query tree below */\n\t\t\t\tNode *childNode = QueryJoinTree(unaryNode->childNode, dependentJobList,\n\t\t\t\t\t\t\t\t\t\t\t\trangeTableList);\n\n\t\t\t\treturn childNode;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tRangeTblRef *rangeTableRef = makeNode(RangeTblRef);\n\t\t\t\tuint32 rangeTableId = rangeTableNode->rangeTableId;\n\t\t\t\trangeTableRef->rtindex = NewTableId(rangeTableId, *rangeTableList);\n\n\t\t\t\treturn (Node *) rangeTableRef;\n\t\t\t}\n\t\t}\n\n\t\tcase T_MultiCollect:\n\t\t{\n\t\t\tList *tableIdList = OutputTableIdList(multiNode);\n\t\t\tJob *dependentJob = JobForTableIdList(dependentJobList, tableIdList);\n\t\t\tList *dependentTargetList = dependentJob->jobQuery->targetList;\n\n\t\t\t/* compute column names for the derived table */\n\t\t\tuint32 columnCount = (uint32) list_length(dependentTargetList);\n\t\t\tList *columnNameList = DerivedColumnNameList(columnCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t dependentJob->jobId);\n\n\t\t\tList *funcColumnNames = NIL;\n\t\t\tList *funcColumnTypes = NIL;\n\t\t\tList *funcColumnTypeMods = NIL;\n\t\t\tList *funcCollations = NIL;\n\n\t\t\tTargetEntry *targetEntry = NULL;\n\t\t\tforeach_declared_ptr(targetEntry, dependentTargetList)\n\t\t\t{\n\t\t\t\tNode *expr = (Node *) targetEntry->expr;\n\n\t\t\t\tchar *name = targetEntry->resname;\n\t\t\t\tif (name == NULL)\n\t\t\t\t{\n\t\t\t\t\tname = pstrdup(\"unnamed\");\n\t\t\t\t}\n\n\t\t\t\tfuncColumnNames = lappend(funcColumnNames, makeString(name));\n\n\t\t\t\tfuncColumnTypes = lappend_oid(funcColumnTypes, exprType(expr));\n\t\t\t\tfuncColumnTypeMods = lappend_int(funcColumnTypeMods, exprTypmod(expr));\n\t\t\t\tfuncCollations = lappend_oid(funcCollations, exprCollation(expr));\n\t\t\t}\n\n\t\t\tRangeTblEntry *rangeTableEntry = DerivedRangeTableEntry(multiNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolumnNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttableIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfuncColumnNames,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfuncColumnTypes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfuncColumnTypeMods,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfuncCollations);\n\n\t\t\tRangeTblRef *rangeTableRef = makeNode(RangeTblRef);\n\n\t\t\trangeTableRef->rtindex = list_length(*rangeTableList) + 1;\n\t\t\t*rangeTableList = lappend(*rangeTableList, rangeTableEntry);\n\n\t\t\treturn (Node *) rangeTableRef;\n\t\t}\n\n\t\tcase T_MultiCartesianProduct:\n\t\t{\n\t\t\tMultiBinaryNode *binaryNode = (MultiBinaryNode *) multiNode;\n\n\t\t\tJoinExpr *joinExpr = makeNode(JoinExpr);\n\t\t\tjoinExpr->jointype = JOIN_INNER;\n\t\t\tjoinExpr->isNatural = false;\n\t\t\tjoinExpr->larg = QueryJoinTree(binaryNode->leftChildNode, dependentJobList,\n\t\t\t\t\t\t\t\t\t\t   rangeTableList);\n\t\t\tjoinExpr->rarg = QueryJoinTree(binaryNode->rightChildNode, dependentJobList,\n\t\t\t\t\t\t\t\t\t\t   rangeTableList);\n\t\t\tjoinExpr->usingClause = NIL;\n\t\t\tjoinExpr->alias = NULL;\n\t\t\tjoinExpr->quals = NULL;\n\t\t\tjoinExpr->rtindex = list_length(*rangeTableList) + 1;\n\n\t\t\tRangeTblEntry *rangeTableEntry = JoinRangeTableEntry(joinExpr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t dependentJobList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t *rangeTableList);\n\t\t\t*rangeTableList = lappend(*rangeTableList, rangeTableEntry);\n\n\t\t\treturn (Node *) joinExpr;\n\t\t}\n\n\t\tcase T_MultiTreeRoot:\n\t\tcase T_MultiSelect:\n\t\tcase T_MultiProject:\n\t\tcase T_MultiExtendedOp:\n\t\tcase T_MultiPartition:\n\t\t{\n\t\t\tMultiUnaryNode *unaryNode = (MultiUnaryNode *) multiNode;\n\n\t\t\tAssert(UnaryOperator(multiNode));\n\n\t\t\tNode *childNode = QueryJoinTree(unaryNode->childNode, dependentJobList,\n\t\t\t\t\t\t\t\t\t\t\trangeTableList);\n\n\t\t\treturn childNode;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"unrecognized multi-node type: %d\", nodeType)));\n\t\t}\n\t}\n}\n\n\n/*\n * JoinRangeTableEntry builds a range table entry for a fully initialized JoinExpr node.\n * The column names and vars are determined using expandRTE, analogous to\n * transformFromClauseItem.\n */\nstatic RangeTblEntry *\nJoinRangeTableEntry(JoinExpr *joinExpr, List *dependentJobList, List *rangeTableList)\n{\n\tRangeTblEntry *rangeTableEntry = makeNode(RangeTblEntry);\n\tList *leftColumnNames = NIL;\n\tList *leftColumnVars = NIL;\n\tList *joinedColumnNames = NIL;\n\tList *joinedColumnVars = NIL;\n\tint leftRangeTableId = ExtractRangeTableId(joinExpr->larg);\n\tRangeTblEntry *leftRTE = rt_fetch(leftRangeTableId, rangeTableList);\n\tList *rightColumnNames = NIL;\n\tList *rightColumnVars = NIL;\n\tint rightRangeTableId = ExtractRangeTableId(joinExpr->rarg);\n\tRangeTblEntry *rightRTE = rt_fetch(rightRangeTableId, rangeTableList);\n\n\trangeTableEntry->rtekind = RTE_JOIN;\n\trangeTableEntry->relid = InvalidOid;\n\trangeTableEntry->inFromCl = true;\n\trangeTableEntry->alias = joinExpr->alias;\n\trangeTableEntry->jointype = joinExpr->jointype;\n\trangeTableEntry->subquery = NULL;\n\trangeTableEntry->eref = makeAlias(\"unnamed_join\", NIL);\n\n\tRangeTblEntry *leftCallingRTE = ConstructCallingRTE(leftRTE, dependentJobList);\n\tRangeTblEntry *rightCallingRte = ConstructCallingRTE(rightRTE, dependentJobList);\n\tExtractColumns(leftCallingRTE, leftRangeTableId,\n\t\t\t\t   &leftColumnNames, &leftColumnVars);\n\tExtractColumns(rightCallingRte, rightRangeTableId,\n\t\t\t\t   &rightColumnNames, &rightColumnVars);\n\tOid leftRelId = leftCallingRTE->relid;\n\tOid rightRelId = rightCallingRte->relid;\n\tjoinedColumnNames = list_concat(joinedColumnNames, leftColumnNames);\n\tjoinedColumnNames = list_concat(joinedColumnNames, rightColumnNames);\n\tjoinedColumnVars = list_concat(joinedColumnVars, leftColumnVars);\n\tjoinedColumnVars = list_concat(joinedColumnVars, rightColumnVars);\n\n\trangeTableEntry->eref->colnames = joinedColumnNames;\n\trangeTableEntry->joinaliasvars = joinedColumnVars;\n\n\tSetJoinRelatedColumnsCompat(rangeTableEntry, leftRelId, rightRelId, leftColumnVars,\n\t\t\t\t\t\t\t\trightColumnVars);\n\n\treturn rangeTableEntry;\n}\n\n\n/*\n * SetJoinRelatedColumnsCompat sets join related fields on the given range table entry.\n * Currently it sets joinleftcols/joinrightcols which are introduced with postgres 13.\n * For more info see postgres commit: 9ce77d75c5ab094637cc4a446296dc3be6e3c221\n */\nstatic void\nSetJoinRelatedColumnsCompat(RangeTblEntry *rangeTableEntry, Oid leftRelId, Oid rightRelId,\n\t\t\t\t\t\t\tList *leftColumnVars, List *rightColumnVars)\n{\n\t/* We don't have any merged columns so set it to 0 */\n\trangeTableEntry->joinmergedcols = 0;\n\n\tif (OidIsValid(leftRelId))\n\t{\n\t\trangeTableEntry->joinleftcols = GetColumnOriginalIndexes(leftRelId);\n\t}\n\telse\n\t{\n\t\tint leftColsSize = list_length(leftColumnVars);\n\t\trangeTableEntry->joinleftcols = GeneratePositiveIntSequenceList(leftColsSize);\n\t}\n\n\tif (OidIsValid(rightRelId))\n\t{\n\t\trangeTableEntry->joinrightcols = GetColumnOriginalIndexes(rightRelId);\n\t}\n\telse\n\t{\n\t\tint rightColsSize = list_length(rightColumnVars);\n\t\trangeTableEntry->joinrightcols = GeneratePositiveIntSequenceList(rightColsSize);\n\t}\n}\n\n\n/*\n * GetColumnOriginalIndexes gets the original indexes of columns by taking column drops into account.\n */\nstatic List *\nGetColumnOriginalIndexes(Oid relationId)\n{\n\tList *originalIndexes = NIL;\n\tRelation relation = table_open(relationId, AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(relation);\n\tfor (int columnIndex = 0; columnIndex < tupleDescriptor->natts; columnIndex++)\n\t{\n\t\tForm_pg_attribute currentColumn = TupleDescAttr(tupleDescriptor, columnIndex);\n\t\tif (currentColumn->attisdropped)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\toriginalIndexes = lappend_int(originalIndexes, columnIndex + 1);\n\t}\n\ttable_close(relation, NoLock);\n\treturn originalIndexes;\n}\n\n\n/*\n * ExtractRangeTableId gets the range table id from a node that could\n * either be a JoinExpr or RangeTblRef.\n */\nstatic int\nExtractRangeTableId(Node *node)\n{\n\tint rangeTableId = 0;\n\n\tif (IsA(node, JoinExpr))\n\t{\n\t\tJoinExpr *joinExpr = (JoinExpr *) node;\n\t\trangeTableId = joinExpr->rtindex;\n\t}\n\telse if (IsA(node, RangeTblRef))\n\t{\n\t\tRangeTblRef *rangeTableRef = (RangeTblRef *) node;\n\t\trangeTableId = rangeTableRef->rtindex;\n\t}\n\n\tAssert(rangeTableId > 0);\n\n\treturn rangeTableId;\n}\n\n\n/*\n * ExtractColumns gets a list of column names and vars for a given range\n * table entry using expandRTE.\n */\nstatic void\nExtractColumns(RangeTblEntry *callingRTE, int rangeTableId,\n\t\t\t   List **columnNames, List **columnVars)\n{\n\tint subLevelsUp = 0;\n\tint location = -1;\n\tbool includeDroppedColumns = false;\n#if PG_VERSION_NUM >= PG_VERSION_18\n\texpandRTE(callingRTE,\n\t\t\t  rangeTableId,\n\t\t\t  subLevelsUp,\n\t\t\t  VAR_RETURNING_DEFAULT,    /* new argument on PG 18+ */\n\t\t\t  location,\n\t\t\t  includeDroppedColumns,\n\t\t\t  columnNames,\n\t\t\t  columnVars);\n#else\n\texpandRTE(callingRTE,\n\t\t\t  rangeTableId,\n\t\t\t  subLevelsUp,\n\t\t\t  location,\n\t\t\t  includeDroppedColumns,\n\t\t\t  columnNames,\n\t\t\t  columnVars);\n#endif\n}\n\n\n/*\n * ConstructCallingRTE constructs a calling RTE from the given range table entry and\n * dependentJobList in case of repartition joins. Since the range table entries in a job\n * query are mocked RTE_FUNCTION entries, this construction is needed to form an RTE\n * that expandRTE can handle.\n */\nstatic RangeTblEntry *\nConstructCallingRTE(RangeTblEntry *rangeTableEntry, List *dependentJobList)\n{\n\tRangeTblEntry *callingRTE = NULL;\n\n\tCitusRTEKind rangeTableKind = GetRangeTblKind(rangeTableEntry);\n\tif (rangeTableKind == CITUS_RTE_JOIN)\n\t{\n\t\t/*\n\t\t * For joins, we can call expandRTE directly.\n\t\t */\n\t\tcallingRTE = rangeTableEntry;\n\t}\n\telse if (rangeTableKind == CITUS_RTE_RELATION)\n\t{\n\t\t/*\n\t\t * For distributed tables, we construct a regular table RTE to call\n\t\t * expandRTE, which will extract columns from the distributed table\n\t\t * schema.\n\t\t */\n\t\tcallingRTE = makeNode(RangeTblEntry);\n\t\tcallingRTE->rtekind = RTE_RELATION;\n\t\tcallingRTE->eref = rangeTableEntry->eref;\n\t\tcallingRTE->relid = rangeTableEntry->relid;\n\t\tcallingRTE->inh = rangeTableEntry->inh;\n\t}\n\telse if (rangeTableKind == CITUS_RTE_REMOTE_QUERY)\n\t{\n\t\tJob *dependentJob = JobForRangeTable(dependentJobList, rangeTableEntry);\n\t\tQuery *jobQuery = dependentJob->jobQuery;\n\n\t\t/*\n\t\t * For re-partition jobs, we construct a subquery RTE to call expandRTE,\n\t\t * which will extract the columns from the target list of the job query.\n\t\t */\n\t\tcallingRTE = makeNode(RangeTblEntry);\n\t\tcallingRTE->rtekind = RTE_SUBQUERY;\n\t\tcallingRTE->eref = rangeTableEntry->eref;\n\t\tcallingRTE->subquery = jobQuery;\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"unsupported Citus RTE kind: %d\", rangeTableKind)));\n\t}\n\treturn callingRTE;\n}\n\n\n/*\n * QueryFromList creates the from list construct that is used for building the\n * query's join tree. The function creates the from list by making a range table\n * reference for each entry in the given range table list.\n */\nstatic List *\nQueryFromList(List *rangeTableList)\n{\n\tList *fromList = NIL;\n\tint rangeTableCount = list_length(rangeTableList);\n\n\tfor (Index rangeTableIndex = 1; rangeTableIndex <= rangeTableCount; rangeTableIndex++)\n\t{\n\t\tRangeTblRef *rangeTableReference = makeNode(RangeTblRef);\n\t\trangeTableReference->rtindex = rangeTableIndex;\n\n\t\tfromList = lappend(fromList, rangeTableReference);\n\t}\n\n\treturn fromList;\n}\n\n\n/*\n * BuildSubqueryJobQuery traverses the given logical plan tree, finds MultiTable\n * which represents the subquery. It builds the query structure by adding this\n * subquery as it is to range table list of the query.\n *\n * Such as if user runs a query like this;\n *\n * SELECT avg(id) FROM (\n *     SELECT ... FROM ()\n * )\n *\n * then this function will build this worker query as keeping subquery as it is;\n *\n * SELECT sum(id), count(id) FROM (\n *     SELECT ... FROM ()\n * )\n */\nstatic Query *\nBuildSubqueryJobQuery(MultiNode *multiNode)\n{\n\tList *targetList = NIL;\n\tList *sortClauseList = NIL;\n\tNode *havingQual = NULL;\n\tNode *limitCount = NULL;\n\tNode *limitOffset = NULL;\n\tbool hasAggregates = false;\n\tList *distinctClause = NIL;\n\tbool hasDistinctOn = false;\n\tbool hasWindowFuncs = false;\n\tList *windowClause = NIL;\n\n\t/* we start building jobs from below the collect node */\n\tAssert(!CitusIsA(multiNode, MultiCollect));\n\n\tList *subqueryMultiTableList = SubqueryMultiTableList(multiNode);\n\tAssert(list_length(subqueryMultiTableList) == 1);\n\n\tMultiTable *multiTable = (MultiTable *) linitial(subqueryMultiTableList);\n\tQuery *subquery = multiTable->subquery;\n\n\t/*  build subquery range table list */\n\tRangeTblEntry *rangeTableEntry = makeNode(RangeTblEntry);\n\trangeTableEntry->rtekind = RTE_SUBQUERY;\n\trangeTableEntry->inFromCl = true;\n\trangeTableEntry->eref = multiTable->referenceNames;\n\trangeTableEntry->alias = multiTable->alias;\n\trangeTableEntry->subquery = subquery;\n\n\tList *rangeTableList = list_make1(rangeTableEntry);\n\n\t/*\n\t * If we have an extended operator, then we copy the operator's target list.\n\t * Otherwise, we use the target list based on the MultiProject node at this\n\t * level in the query tree.\n\t */\n\tList *extendedOpNodeList = FindNodesOfType(multiNode, T_MultiExtendedOp);\n\tif (extendedOpNodeList != NIL)\n\t{\n\t\tMultiExtendedOp *extendedOp = (MultiExtendedOp *) linitial(extendedOpNodeList);\n\t\ttargetList = copyObject(extendedOp->targetList);\n\t}\n\telse\n\t{\n\t\ttargetList = QueryTargetList(multiNode);\n\t}\n\n\t/* extract limit count/offset, sort and having clauses */\n\tif (extendedOpNodeList != NIL)\n\t{\n\t\tMultiExtendedOp *extendedOp = (MultiExtendedOp *) linitial(extendedOpNodeList);\n\n\t\tlimitCount = extendedOp->limitCount;\n\t\tlimitOffset = extendedOp->limitOffset;\n\t\tsortClauseList = extendedOp->sortClauseList;\n\t\thavingQual = extendedOp->havingQual;\n\t\tdistinctClause = extendedOp->distinctClause;\n\t\thasDistinctOn = extendedOp->hasDistinctOn;\n\t\thasWindowFuncs = extendedOp->hasWindowFuncs;\n\t\twindowClause = extendedOp->windowClause;\n\t}\n\n\t/* build group clauses */\n\tList *groupClauseList = QueryGroupClauseList(multiNode);\n\n\t/* build the where clause list using select predicates */\n\tList *whereClauseList = QuerySelectClauseList(multiNode);\n\n\tif (contain_aggs_of_level((Node *) targetList, 0) ||\n\t\tcontain_aggs_of_level((Node *) havingQual, 0))\n\t{\n\t\thasAggregates = true;\n\t}\n\n\t/* distinct is not sent to worker query if there are top level aggregates */\n\tif (hasAggregates)\n\t{\n\t\thasDistinctOn = false;\n\t\tdistinctClause = NIL;\n\t}\n\n\n\t/*\n\t * Build the From/Where construct. We keep the where-clause list implicitly\n\t * AND'd, since both partition and join pruning depends on the clauses being\n\t * expressed as a list.\n\t */\n\tFromExpr *joinTree = makeNode(FromExpr);\n\tjoinTree->quals = (Node *) whereClauseList;\n\tjoinTree->fromlist = QueryFromList(rangeTableList);\n\n\t/* build the query structure for this job */\n\tQuery *jobQuery = makeNode(Query);\n\tjobQuery->commandType = CMD_SELECT;\n\tjobQuery->querySource = QSRC_ORIGINAL;\n\tjobQuery->canSetTag = true;\n\tjobQuery->rtable = rangeTableList;\n\tjobQuery->targetList = targetList;\n\tjobQuery->jointree = joinTree;\n\tjobQuery->sortClause = sortClauseList;\n\tjobQuery->groupClause = groupClauseList;\n\tjobQuery->limitOffset = limitOffset;\n\tjobQuery->limitCount = limitCount;\n\tjobQuery->havingQual = havingQual;\n\tjobQuery->hasAggs = hasAggregates;\n\tjobQuery->hasDistinctOn = hasDistinctOn;\n\tjobQuery->distinctClause = distinctClause;\n\tjobQuery->hasWindowFuncs = hasWindowFuncs;\n\tjobQuery->windowClause = windowClause;\n\tjobQuery->hasSubLinks = checkExprHasSubLink((Node *) jobQuery);\n\n\tAssert(jobQuery->hasWindowFuncs == contain_window_function((Node *) jobQuery));\n\n\treturn jobQuery;\n}\n\n\n/*\n * UpdateAllColumnAttributes extracts column references from provided columnContainer\n * and calls UpdateColumnAttributes to updates the column's range table reference (varno) and\n * column attribute number for the range table (varattno).\n */\nstatic void\nUpdateAllColumnAttributes(Node *columnContainer, List *rangeTableList,\n\t\t\t\t\t\t  List *dependentJobList)\n{\n\tListCell *columnCell = NULL;\n\tList *columnList = pull_var_clause_default(columnContainer);\n\tforeach(columnCell, columnList)\n\t{\n\t\tVar *column = (Var *) lfirst(columnCell);\n\t\tUpdateColumnAttributes(column, rangeTableList, dependentJobList);\n\t}\n}\n\n\n/*\n * UpdateColumnAttributes updates the column's range table reference (varno) and\n * column attribute number for the range table (varattno). The function uses the\n * newly built range table list to update the given column's attributes.\n */\nstatic void\nUpdateColumnAttributes(Var *column, List *rangeTableList, List *dependentJobList)\n{\n\tIndex originalTableId = column->varnosyn;\n\tAttrNumber originalColumnId = column->varattnosyn;\n\n\t/* find the new table identifier */\n\tIndex newTableId = NewTableId(originalTableId, rangeTableList);\n\tAttrNumber newColumnId = originalColumnId;\n\n\t/* if this is a derived table, find the new column identifier */\n\tRangeTblEntry *newRangeTableEntry = rt_fetch(newTableId, rangeTableList);\n\tif (GetRangeTblKind(newRangeTableEntry) == CITUS_RTE_REMOTE_QUERY)\n\t{\n\t\tnewColumnId = NewColumnId(originalTableId, originalColumnId,\n\t\t\t\t\t\t\t\t  newRangeTableEntry, dependentJobList);\n\t}\n\n\tcolumn->varno = newTableId;\n\tcolumn->varattno = newColumnId;\n}\n\n\n/*\n * NewTableId determines the new tableId for the query that is currently being\n * built. In this query, the original tableId represents the order of the table\n * in the initial parse tree. When queries involve repartitioning, we re-order\n * tables; and the new tableId corresponds to this new table order.\n */\nstatic Index\nNewTableId(Index originalTableId, List *rangeTableList)\n{\n\tIndex rangeTableIndex = 1;\n\tListCell *rangeTableCell = NULL;\n\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\t\tList *originalTableIdList = NIL;\n\n\t\tExtractRangeTblExtraData(rangeTableEntry, NULL, NULL, NULL, &originalTableIdList);\n\n\t\tbool listMember = list_member_int(originalTableIdList, originalTableId);\n\t\tif (listMember)\n\t\t{\n\t\t\treturn rangeTableIndex;\n\t\t}\n\n\t\trangeTableIndex++;\n\t}\n\n\tereport(ERROR, (errmsg(\"Unrecognized range table id %d\", (int) originalTableId)));\n\n\treturn 0;\n}\n\n\n/*\n * NewColumnId determines the new columnId for the query that is currently being\n * built. In this query, the original columnId corresponds to the column in base\n * tables. When the current query is a partition job and generates intermediate\n * tables, the columns have a different order and the new columnId corresponds\n * to this order. Please note that this function assumes columnIds for dependent\n * jobs have already been updated.\n */\nstatic AttrNumber\nNewColumnId(Index originalTableId, AttrNumber originalColumnId,\n\t\t\tRangeTblEntry *newRangeTableEntry, List *dependentJobList)\n{\n\tAttrNumber newColumnId = 1;\n\tAttrNumber columnIndex = 1;\n\n\tJob *dependentJob = JobForRangeTable(dependentJobList, newRangeTableEntry);\n\tList *targetEntryList = dependentJob->jobQuery->targetList;\n\n\tListCell *targetEntryCell = NULL;\n\tforeach(targetEntryCell, targetEntryList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell);\n\t\tExpr *expression = targetEntry->expr;\n\n\t\tVar *column = (Var *) expression;\n\t\tAssert(IsA(expression, Var));\n\n\t\t/*\n\t\t * Check against the *old* values for this column, as the new values\n\t\t * would have been updated already.\n\t\t */\n\t\tif (column->varnosyn == originalTableId &&\n\t\t\tcolumn->varattnosyn == originalColumnId)\n\t\t{\n\t\t\tnewColumnId = columnIndex;\n\t\t\tbreak;\n\t\t}\n\n\t\tcolumnIndex++;\n\t}\n\n\treturn newColumnId;\n}\n\n\n/*\n * JobForRangeTable returns the job that corresponds to the given range table\n * entry. The function walks over jobs in the given job list, and compares each\n * job's table list against the given range table entry's table list. When two\n * table lists match, the function returns the matching job. Note that we call\n * this function in practice when we need to determine which one of the jobs we\n * depend upon corresponds to given range table entry.\n */\nstatic Job *\nJobForRangeTable(List *jobList, RangeTblEntry *rangeTableEntry)\n{\n\tList *searchedTableIdList = NIL;\n\tCitusRTEKind rangeTableKind;\n\n\tExtractRangeTblExtraData(rangeTableEntry, &rangeTableKind, NULL, NULL,\n\t\t\t\t\t\t\t &searchedTableIdList);\n\n\tAssert(rangeTableKind == CITUS_RTE_REMOTE_QUERY);\n\n\tJob *searchedJob = JobForTableIdList(jobList, searchedTableIdList);\n\n\treturn searchedJob;\n}\n\n\n/*\n * JobForTableIdList returns the job that corresponds to the given\n * tableIdList. The function walks over jobs in the given job list, and\n * compares each job's table list against the given table list. When the\n * two table lists match, the function returns the matching job.\n */\nstatic Job *\nJobForTableIdList(List *jobList, List *searchedTableIdList)\n{\n\tJob *searchedJob = NULL;\n\tListCell *jobCell = NULL;\n\n\tforeach(jobCell, jobList)\n\t{\n\t\tJob *job = (Job *) lfirst(jobCell);\n\t\tList *jobRangeTableList = job->jobQuery->rtable;\n\t\tList *jobTableIdList = NIL;\n\t\tListCell *jobRangeTableCell = NULL;\n\n\t\tforeach(jobRangeTableCell, jobRangeTableList)\n\t\t{\n\t\t\tRangeTblEntry *jobRangeTable = (RangeTblEntry *) lfirst(jobRangeTableCell);\n\t\t\tList *tableIdList = NIL;\n\n\t\t\tExtractRangeTblExtraData(jobRangeTable, NULL, NULL, NULL, &tableIdList);\n\n\t\t\t/* copy the list since list_concat is destructive */\n\t\t\ttableIdList = list_copy(tableIdList);\n\t\t\tjobTableIdList = list_concat(jobTableIdList, tableIdList);\n\t\t}\n\n\t\t/*\n\t\t * Check if the searched range table's tableIds and the current job's\n\t\t * tableIds are the same.\n\t\t */\n\t\tList *lhsDiff = list_difference_int(jobTableIdList, searchedTableIdList);\n\t\tList *rhsDiff = list_difference_int(searchedTableIdList, jobTableIdList);\n\t\tif (lhsDiff == NIL && rhsDiff == NIL)\n\t\t{\n\t\t\tsearchedJob = job;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tAssert(searchedJob != NULL);\n\treturn searchedJob;\n}\n\n\n/* Returns the list of children for the given multi node. */\nstatic List *\nChildNodeList(MultiNode *multiNode)\n{\n\tList *childNodeList = NIL;\n\tbool isUnaryNode = UnaryOperator(multiNode);\n\tbool isBinaryNode = BinaryOperator(multiNode);\n\n\t/* relation table nodes don't have any children */\n\tif (CitusIsA(multiNode, MultiTable))\n\t{\n\t\tMultiTable *multiTable = (MultiTable *) multiNode;\n\t\tif (multiTable->relationId != SUBQUERY_RELATION_ID)\n\t\t{\n\t\t\treturn NIL;\n\t\t}\n\t}\n\n\tif (isUnaryNode)\n\t{\n\t\tMultiUnaryNode *unaryNode = (MultiUnaryNode *) multiNode;\n\t\tchildNodeList = list_make1(unaryNode->childNode);\n\t}\n\telse if (isBinaryNode)\n\t{\n\t\tMultiBinaryNode *binaryNode = (MultiBinaryNode *) multiNode;\n\t\tchildNodeList = list_make2(binaryNode->leftChildNode,\n\t\t\t\t\t\t\t\t   binaryNode->rightChildNode);\n\t}\n\n\treturn childNodeList;\n}\n\n\n/*\n * UniqueJobId allocates and returns a unique jobId for the job to be executed.\n *\n * The resulting job ID is built up as:\n * <16-bit group ID><24-bit process ID><1-bit secondary flag><23-bit local counter>\n *\n * When citus.enable_unique_job_ids is off then only the local counter is\n * included to get repeatable results.\n */\nuint64\nUniqueJobId(void)\n{\n\tstatic uint32 jobIdCounter = 0;\n\n\tuint64 jobId = 0;\n\tuint64 processId = 0;\n\tuint64 localGroupId = 0;\n\n\tjobIdCounter++;\n\n\tif (EnableUniqueJobIds)\n\t{\n\t\t/*\n\t\t * Add the local group id information to the jobId to\n\t\t * prevent concurrent jobs on different groups to conflict.\n\t\t */\n\t\tlocalGroupId = GetLocalGroupId() & 0xFF;\n\t\tjobId = jobId | (localGroupId << 48);\n\n\t\t/*\n\t\t * Add the current process ID to distinguish jobs by this\n\t\t * backends from jobs started by other backends. Process\n\t\t * IDs can have at most 24-bits on platforms supported by\n\t\t * Citus.\n\t\t */\n\t\tprocessId = MyProcPid & 0xFFFFFF;\n\t\tjobId = jobId | (processId << 24);\n\n\t\t/*\n\t\t * Add an extra bit for secondaries to distinguish their\n\t\t * jobs from primaries.\n\t\t */\n\t\tif (RecoveryInProgress())\n\t\t{\n\t\t\tjobId = jobId | (1 << 23);\n\t\t}\n\t}\n\n\t/*\n\t * Use the remaining 23 bits to distinguish jobs by the\n\t * same backend.\n\t */\n\tuint64 jobIdNumber = jobIdCounter & 0x1FFFFFF;\n\tjobId = jobId | jobIdNumber;\n\n\treturn jobId;\n}\n\n\n/* Builds a job from the given job query and dependent job list. */\nstatic Job *\nBuildJob(Query *jobQuery, List *dependentJobList)\n{\n\tJob *job = CitusMakeNode(Job);\n\tjob->jobId = UniqueJobId();\n\tjob->jobQuery = jobQuery;\n\tjob->dependentJobList = dependentJobList;\n\tjob->requiresCoordinatorEvaluation = false;\n\n\treturn job;\n}\n\n\n/*\n * BuildMapMergeJob builds a MapMerge job from the given query and dependent job\n * list. The function then copies and updates the logical plan's partition\n * column, and uses the join rule type to determine the physical repartitioning\n * method to apply.\n */\nstatic MapMergeJob *\nBuildMapMergeJob(Query *jobQuery, List *dependentJobList, Var *partitionKey,\n\t\t\t\t PartitionType partitionType, Oid baseRelationId,\n\t\t\t\t BoundaryNodeJobType boundaryNodeJobType)\n{\n\tList *rangeTableList = jobQuery->rtable;\n\tVar *partitionColumn = copyObject(partitionKey);\n\n\t/* update the logical partition key's table and column identifiers */\n\tUpdateColumnAttributes(partitionColumn, rangeTableList, dependentJobList);\n\n\tMapMergeJob *mapMergeJob = CitusMakeNode(MapMergeJob);\n\tmapMergeJob->job.jobId = UniqueJobId();\n\tmapMergeJob->job.jobQuery = jobQuery;\n\tmapMergeJob->job.dependentJobList = dependentJobList;\n\tmapMergeJob->partitionColumn = partitionColumn;\n\tmapMergeJob->sortedShardIntervalArrayLength = 0;\n\n\t/*\n\t * We assume dual partition join defaults to hash partitioning, and single\n\t * partition join defaults to range partitioning. In practice, the join type\n\t * should have no impact on the physical repartitioning (hash/range) method.\n\t * If join type is not set, this means this job represents a subquery, and\n\t * uses hash partitioning.\n\t */\n\tif (partitionType == DUAL_HASH_PARTITION_TYPE)\n\t{\n\t\tuint32 partitionCount = HashPartitionCount();\n\n\t\tmapMergeJob->partitionType = DUAL_HASH_PARTITION_TYPE;\n\t\tmapMergeJob->partitionCount = partitionCount;\n\t}\n\telse if (partitionType == SINGLE_HASH_PARTITION_TYPE || partitionType ==\n\t\t\t RANGE_PARTITION_TYPE)\n\t{\n\t\tCitusTableCacheEntry *cache = GetCitusTableCacheEntry(baseRelationId);\n\t\tint shardCount = cache->shardIntervalArrayLength;\n\t\tShardInterval **cachedSortedShardIntervalArray =\n\t\t\tcache->sortedShardIntervalArray;\n\t\tbool hasUninitializedShardInterval =\n\t\t\tcache->hasUninitializedShardInterval;\n\n\t\tShardInterval **sortedShardIntervalArray =\n\t\t\tpalloc0(sizeof(ShardInterval) * shardCount);\n\n\t\tfor (int shardIndex = 0; shardIndex < shardCount; shardIndex++)\n\t\t{\n\t\t\tsortedShardIntervalArray[shardIndex] =\n\t\t\t\tCopyShardInterval(cachedSortedShardIntervalArray[shardIndex]);\n\t\t}\n\n\t\tif (hasUninitializedShardInterval)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot range repartition shard with \"\n\t\t\t\t\t\t\t\t   \"missing min/max values\")));\n\t\t}\n\n\t\tmapMergeJob->partitionType = partitionType;\n\t\tmapMergeJob->partitionCount = (uint32) shardCount;\n\t\tmapMergeJob->sortedShardIntervalArray = sortedShardIntervalArray;\n\t\tmapMergeJob->sortedShardIntervalArrayLength = shardCount;\n\t}\n\n\treturn mapMergeJob;\n}\n\n\n/*\n * HashPartitionCount returns the number of partition files we create for a hash\n * partition task. The function follows Hadoop's method for picking the number\n * of reduce tasks: 0.95 or 1.75 * node count * max reduces per node. We choose\n * the lower constant 0.95 so that all tasks can start immediately, but round it\n * to 1.0 so that we have a smooth number of partition tasks.\n */\nstatic uint32\nHashPartitionCount(void)\n{\n\tuint32 groupCount = list_length(ActiveReadableNodeList());\n\tdouble maxReduceTasksPerNode = RepartitionJoinBucketCountPerNode;\n\n\tuint32 partitionCount = (uint32) rint(groupCount * maxReduceTasksPerNode);\n\treturn partitionCount;\n}\n\n\n/* ------------------------------------------------------------\n * Functions that relate to building and assigning tasks follow\n * ------------------------------------------------------------\n */\n\n/*\n * BuildJobTreeTaskList takes in the given job tree and walks over jobs in this\n * tree bottom up. The function then creates tasks for each job in the tree,\n * sets dependencies between tasks and their downstream dependencies and assigns\n * tasks to worker nodes.\n */\nstatic Job *\nBuildJobTreeTaskList(Job *jobTree, PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tList *flattenedJobList = NIL;\n\n\t/*\n\t * We traverse the job tree in preorder, and append each visited job to our\n\t * flattened list. This way, each job in our list appears before the jobs it\n\t * depends on.\n\t */\n\tList *jobStack = list_make1(jobTree);\n\twhile (jobStack != NIL)\n\t{\n\t\tJob *job = (Job *) llast(jobStack);\n\t\tflattenedJobList = lappend(flattenedJobList, job);\n\n\t\t/* pop top element and push its children to the stack */\n\t\tjobStack = list_delete_ptr(jobStack, job);\n\t\tjobStack = list_union_ptr(jobStack, job->dependentJobList);\n\t}\n\n\t/*\n\t * We walk the job list in reverse order to visit jobs bottom up. This way,\n\t * we can create dependencies between tasks bottom up, and assign them to\n\t * worker nodes accordingly.\n\t */\n\tuint32 flattenedJobCount = (int32) list_length(flattenedJobList);\n\tfor (int32 jobIndex = (flattenedJobCount - 1); jobIndex >= 0; jobIndex--)\n\t{\n\t\tJob *job = (Job *) list_nth(flattenedJobList, jobIndex);\n\t\tList *sqlTaskList = NIL;\n\t\tListCell *assignedSqlTaskCell = NULL;\n\n\t\t/* create sql tasks for the job, and prune redundant data fetch tasks */\n\t\tif (job->subqueryPushdown)\n\t\t{\n\t\t\tbool isMultiShardQuery = false;\n\t\t\tList *prunedRelationShardList =\n\t\t\t\tTargetShardIntervalsForRestrictInfo(plannerRestrictionContext->\n\t\t\t\t\t\t\t\t\t\t\t\t\trelationRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t&isMultiShardQuery, NULL);\n\n\t\t\tDeferredErrorMessage *deferredErrorMessage = NULL;\n\t\t\tsqlTaskList = QueryPushdownSqlTaskList(job->jobQuery, job->jobId,\n\t\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext->\n\t\t\t\t\t\t\t\t\t\t\t\t   relationRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t   prunedRelationShardList, READ_TASK,\n\t\t\t\t\t\t\t\t\t\t\t\t   false,\n\t\t\t\t\t\t\t\t\t\t\t\t   &deferredErrorMessage);\n\t\t\tif (deferredErrorMessage != NULL)\n\t\t\t{\n\t\t\t\tRaiseDeferredErrorInternal(deferredErrorMessage, ERROR);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsqlTaskList = SqlTaskList(job);\n\t\t}\n\n\t\tsqlTaskList = PruneSqlTaskDependencies(sqlTaskList);\n\n\t\t/*\n\t\t * We first assign sql and merge tasks to worker nodes. Next, we assign\n\t\t * sql tasks' data fetch dependencies.\n\t\t */\n\t\tList *assignedSqlTaskList = AssignTaskList(sqlTaskList);\n\t\tAssignDataFetchDependencies(assignedSqlTaskList);\n\n\t\t/* if the parameters has not been resolved, record it */\n\t\tjob->parametersInJobQueryResolved =\n\t\t\t!HasUnresolvedExternParamsWalker((Node *) job->jobQuery, NULL);\n\n\t\t/*\n\t\t * Make final adjustments for the assigned tasks.\n\t\t *\n\t\t * First, update SELECT tasks' parameters resolved field.\n\t\t *\n\t\t * Second, assign merge task's data fetch dependencies.\n\t\t */\n\t\tforeach(assignedSqlTaskCell, assignedSqlTaskList)\n\t\t{\n\t\t\tTask *assignedSqlTask = (Task *) lfirst(assignedSqlTaskCell);\n\n\t\t\t/* we don't support parameters in the physical planner */\n\t\t\tif (assignedSqlTask->taskType == READ_TASK)\n\t\t\t{\n\t\t\t\tassignedSqlTask->parametersInQueryStringResolved =\n\t\t\t\t\tjob->parametersInJobQueryResolved;\n\t\t\t}\n\n\t\t\tList *assignedMergeTaskList = FindDependentMergeTaskList(assignedSqlTask);\n\t\t\tAssignDataFetchDependencies(assignedMergeTaskList);\n\t\t}\n\n\t\t/*\n\t\t * If we have a MapMerge job, the map tasks in this job wrap around the\n\t\t * SQL tasks and their assignments.\n\t\t */\n\t\tif (CitusIsA(job, MapMergeJob))\n\t\t{\n\t\t\tMapMergeJob *mapMergeJob = (MapMergeJob *) job;\n\t\t\tuint32 taskIdIndex = TaskListHighestTaskId(assignedSqlTaskList) + 1;\n\n\t\t\tList *mapTaskList = MapTaskList(mapMergeJob, assignedSqlTaskList);\n\t\t\tList *mergeTaskList = MergeTaskList(mapMergeJob, mapTaskList, taskIdIndex);\n\n\t\t\tmapMergeJob->mapTaskList = mapTaskList;\n\t\t\tmapMergeJob->mergeTaskList = mergeTaskList;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tjob->taskList = assignedSqlTaskList;\n\t\t}\n\t}\n\n\treturn jobTree;\n}\n\n\n/*\n * QueryPushdownSqlTaskList creates a list of SQL tasks to execute the given subquery\n * pushdown job. For this, it is being checked whether the query is router\n * plannable per target shard interval. For those router plannable worker\n * queries, we create a SQL task and append the task to the task list that is going\n * to be executed.\n */\nList *\nQueryPushdownSqlTaskList(Query *query, uint64 jobId,\n\t\t\t\t\t\t RelationRestrictionContext *relationRestrictionContext,\n\t\t\t\t\t\t List *prunedRelationShardList, TaskType taskType, bool\n\t\t\t\t\t\t modifyRequiresCoordinatorEvaluation,\n\t\t\t\t\t\t DeferredErrorMessage **planningError)\n{\n\tList *sqlTaskList = NIL;\n\tuint32 taskIdIndex = 1; /* 0 is reserved for invalid taskId */\n\tint minShardOffset = INT_MAX;\n\tint prevShardCount = 0;\n\tBitmapset *taskRequiredForShardIndex = NULL;\n\tBitmapset *distributedTableIndex = NULL;\n\n\t/* error if shards are not co-partitioned */\n\tErrorIfUnsupportedShardDistribution(query);\n\n\tif (list_length(relationRestrictionContext->relationRestrictionList) == 0)\n\t{\n\t\t*planningError = DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t   \"cannot handle complex subqueries when the \"\n\t\t\t\t\t\t\t\t\t   \"router executor is disabled\",\n\t\t\t\t\t\t\t\t\t   NULL, NULL);\n\t\treturn NIL;\n\t}\n\n\tRelationRestriction *relationRestriction = NULL;\n\tList *prunedShardList = NULL;\n\n\t/* First loop, gather the indexes of distributed tables\n\t *  this is required to decide whether we can skip shards\n\t *  from inner tables of outer joins\n\t */\n\tforeach_declared_ptr(relationRestriction,\n\t\t\t\t\t\t relationRestrictionContext->relationRestrictionList)\n\t{\n\t\tOid relationId = relationRestriction->relationId;\n\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\t\tif (!HasDistributionKeyCacheEntry(cacheEntry))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* we expect distributed tables to have the same shard count */\n\t\tif (prevShardCount > 0 && prevShardCount != cacheEntry->shardIntervalArrayLength)\n\t\t{\n\t\t\t*planningError = DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t\t   \"shard counts of co-located tables do not \"\n\t\t\t\t\t\t\t\t\t\t   \"match\",\n\t\t\t\t\t\t\t\t\t\t   NULL, NULL);\n\t\t\treturn NIL;\n\t\t}\n\t\tprevShardCount = cacheEntry->shardIntervalArrayLength;\n\n\t\tdistributedTableIndex = bms_add_member(distributedTableIndex,\n\t\t\t\t\t\t\t\t\t\t\t   relationRestriction->index);\n\t}\n\n\t/* In the second loop, populate taskRequiredForShardIndex */\n\tbool updateQualsForOuterJoin = false;\n\tbool outerPartHasDistributedTable = false;\n\tbool noDistTables = bms_is_empty(distributedTableIndex);\n\tbool hasRefOrSchemaShardedTable = false;\n\tforboth_ptr(prunedShardList, prunedRelationShardList,\n\t\t\t\trelationRestriction, relationRestrictionContext->relationRestrictionList)\n\t{\n\t\tOid relationId = relationRestriction->relationId;\n\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\t\tif (!HasDistributionKeyCacheEntry(cacheEntry))\n\t\t{\n\t\t\tif (noDistTables && !hasRefOrSchemaShardedTable)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Before continuing, check if we're looking at a reference or schema-\n\t\t\t\t * sharded table. If so, and it is the first such table we've seen, we\n\t\t\t\t * add a task for shard index 0; all reference and schema sharded tables\n\t\t\t\t * have shard index 0 so we can hard-code the value rather than looking at\n\t\t\t\t * the shardIndex in pruned shard list, as is done further on down for\n\t\t\t\t * distributed tables.\n\t\t\t\t *\n\t\t\t\t * Note that this only needs to be done once, regardless of how many\n\t\t\t\t * reference or schema sharded tables there are; they all have the\n\t\t\t\t * same shard index (0), and will require just one task.\n\t\t\t\t *\n\t\t\t\t * Also note that this is only done if there are no distributed tables\n\t\t\t\t * involved; the relevant shard indexes will get added, and furthermore\n\t\t\t\t * we don't want to incorrectly add shard index 0 if for example a left\n\t\t\t\t * outer join between a reference table and a distributed table also has\n\t\t\t\t * a restriction that prunes out shard index 0 of the distributed table.\n\t\t\t\t */\n\t\t\t\tCitusTableType currentTableType = GetCitusTableType(cacheEntry);\n\t\t\t\thasRefOrSchemaShardedTable = currentTableType == REFERENCE_TABLE ||\n\t\t\t\t\t\t\t\t\t\t\t currentTableType == SINGLE_SHARD_DISTRIBUTED;\n\t\t\t\tif (hasRefOrSchemaShardedTable)\n\t\t\t\t{\n\t\t\t\t\ttaskRequiredForShardIndex = bms_add_member(taskRequiredForShardIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   0);\n\t\t\t\t\tminShardOffset = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * For left joins we don't care about the shards pruned for the right hand side.\n\t\t * If the right hand side would prune to a smaller set we should still send it to\n\t\t * all tables of the left hand side. However if the right hand side is bigger than\n\t\t * the left hand side we don't have to send the query to any shard that is not\n\t\t * matching anything on the left hand side.\n\t\t *\n\t\t * Instead we will simply skip any RelationRestriction if it is an OUTER join,\n\t\t * the table is part of the non-outer side of the join and the outer side has a\n\t\t * distributed table.\n\t\t */\n\t\tif (IsInnerTableOfOuterJoin(relationRestriction, distributedTableIndex,\n\t\t\t\t\t\t\t\t\t&outerPartHasDistributedTable))\n\t\t{\n\t\t\tif (outerPartHasDistributedTable)\n\t\t\t{\n\t\t\t\t/* we can skip the shards from this relation restriction */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* The outer part does not include distributed tables, we can not skip shards.\n\t\t\t\t * Also, we will possibly update the quals of the outer relation for recurring join push down, mark here.\n\t\t\t\t */\n\t\t\t\tupdateQualsForOuterJoin = true;\n\t\t\t}\n\t\t}\n\n\t\tShardInterval *shardInterval = NULL;\n\t\tforeach_declared_ptr(shardInterval, prunedShardList)\n\t\t{\n\t\t\tint shardIndex = shardInterval->shardIndex;\n\n\t\t\ttaskRequiredForShardIndex =\n\t\t\t\tbms_add_member(taskRequiredForShardIndex, shardIndex);\n\t\t\tminShardOffset = Min(minShardOffset, shardIndex);\n\t\t}\n\t}\n\n\t/*\n\t * We might fail to find outer joins from the relationRestrictionContext\n\t * when the original query has CTEs. In order to ensure that we always mark\n\t * the outer joins correctly and compute additional quals when necessary,\n\t * check the task query as well.\n\t */\n\tif (!updateQualsForOuterJoin && FindNodeMatchingCheckFunction((Node *) query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  IsOuterJoinExpr))\n\t{\n\t\t/*\n\t\t * We have an outer join, so assume \"might\" need to update quals.\n\t\t * See the usage of this flag in QueryPushdownTaskCreate().\n\t\t */\n\t\tupdateQualsForOuterJoin = true;\n\t}\n\n\t/*\n\t * We keep track of minShardOffset to skip over a potentially big amount of pruned\n\t * shards. However, we need to start at minShardOffset - 1 to make sure we don't\n\t * miss to first/min shard recorder as bms_next_member will return the first member\n\t * added after shardOffset. Meaning minShardOffset would be the first member we\n\t * expect.\n\t *\n\t * We don't have to keep track of maxShardOffset as the bitmapset will only have been\n\t * allocated till the last shard we have added. Therefore, the iterator will quickly\n\t * identify the end of the bitmapset.\n\t */\n\tint shardOffset = minShardOffset - 1;\n\twhile ((shardOffset = bms_next_member(taskRequiredForShardIndex, shardOffset)) >= 0)\n\t{\n\t\tTask *subqueryTask = QueryPushdownTaskCreate(query, shardOffset,\n\t\t\t\t\t\t\t\t\t\t\t\t\t relationRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t taskIdIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t taskType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t modifyRequiresCoordinatorEvaluation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t updateQualsForOuterJoin,\n\t\t\t\t\t\t\t\t\t\t\t\t\t planningError);\n\t\tif (*planningError != NULL)\n\t\t{\n\t\t\treturn NIL;\n\t\t}\n\t\tsubqueryTask->jobId = jobId;\n\t\tsqlTaskList = lappend(sqlTaskList, subqueryTask);\n\n\t\t++taskIdIndex;\n\t}\n\n\t/* If we detected a reference or schema sharded table then there\n\t * should be no distributed tables involved and exactly one task.\n\t */\n\tAssert(!hasRefOrSchemaShardedTable || (noDistTables &&\n\t\t\t\t\t\t\t\t\t\t   list_length(sqlTaskList) == 1));\n\n\t/* If it is a modify task with multiple tables */\n\tif (taskType == MODIFY_TASK && list_length(\n\t\t\trelationRestrictionContext->relationRestrictionList) > 1)\n\t{\n\t\tListCell *taskCell = NULL;\n\t\tforeach(taskCell, sqlTaskList)\n\t\t{\n\t\t\tTask *task = (Task *) lfirst(taskCell);\n\t\t\ttask->modifyWithSubquery = true;\n\t\t}\n\t}\n\n\treturn sqlTaskList;\n}\n\n\n/*\n * IsInnerTableOfOuterJoin tests based on the join information envoded in a\n * RelationRestriction if the table accessed for this relation is\n *   a) in an outer join\n *   b) on the inner part of said join\n *\n *  The function also sets outerPartHasDistributedTable if the outer part\n *  of the corresponding join has a distributed table.\n */\nstatic bool\nIsInnerTableOfOuterJoin(RelationRestriction *relationRestriction,\n\t\t\t\t\t\tBitmapset *distributedTables,\n\t\t\t\t\t\tbool *outerPartHasDistributedTable)\n{\n\tRestrictInfo *joinInfo = NULL;\n\tforeach_declared_ptr(joinInfo, relationRestriction->relOptInfo->joininfo)\n\t{\n\t\tif (joinInfo->outer_relids == NULL)\n\t\t{\n\t\t\t/* not an outer join */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * This join restriction info describes an outer join, we need to figure out if\n\t\t * our table is in the non outer part of this join. If that is the case this is a\n\t\t * non outer table of an outer join.\n\t\t */\n\t\tbool isInOuter = bms_is_member(relationRestriction->relOptInfo->relid,\n\t\t\t\t\t\t\t\t\t   joinInfo->outer_relids);\n\t\tif (!isInOuter)\n\t\t{\n\t\t\t/* this table is joined in the inner part of an outer join */\n\t\t\t/* set if the outer part has a distributed relation */\n\t\t\t*outerPartHasDistributedTable = bms_overlap(joinInfo->outer_relids,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdistributedTables);\n\n\t\t\t/* this is an inner table of an outer join  */\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t/* we have not found any join clause that satisfies both requirements */\n\treturn false;\n}\n\n\n/*\n * ErrorIfUnsupportedShardDistribution gets list of relations in the given query\n * and checks if two conditions below hold for them, otherwise it errors out.\n * a. Every relation is distributed by range or hash. This means shards are\n * disjoint based on the partition column.\n * b. All relations have 1-to-1 shard partitioning between them. This means\n * shard count for every relation is same and for every shard in a relation\n * there is exactly one shard in other relations with same min/max values.\n */\nstatic void\nErrorIfUnsupportedShardDistribution(Query *query)\n{\n\tOid firstTableRelationId = InvalidOid;\n\tList *relationIdList = DistributedRelationIdList(query);\n\tList *nonReferenceRelations = NIL;\n\tListCell *relationIdCell = NULL;\n\tuint32 relationIndex = 0;\n\tuint32 rangeDistributedRelationCount = 0;\n\tuint32 hashDistOrSingleShardRelCount = 0;\n\tuint32 appendDistributedRelationCount = 0;\n\n\tforeach(relationIdCell, relationIdList)\n\t{\n\t\tOid relationId = lfirst_oid(relationIdCell);\n\t\tif (IsCitusTableType(relationId, RANGE_DISTRIBUTED))\n\t\t{\n\t\t\trangeDistributedRelationCount++;\n\t\t\tnonReferenceRelations = lappend_oid(nonReferenceRelations,\n\t\t\t\t\t\t\t\t\t\t\t\trelationId);\n\t\t}\n\t\telse if (IsCitusTableType(relationId, HASH_DISTRIBUTED) ||\n\t\t\t\t IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED))\n\t\t{\n\t\t\thashDistOrSingleShardRelCount++;\n\t\t\tnonReferenceRelations = lappend_oid(nonReferenceRelations,\n\t\t\t\t\t\t\t\t\t\t\t\trelationId);\n\t\t}\n\t\telse if (IsCitusTable(relationId) && !HasDistributionKey(relationId))\n\t\t{\n\t\t\t/* do not need to handle non-distributed tables */\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendDistributedRelationCount++;\n\t\t}\n\t}\n\n\tif ((rangeDistributedRelationCount > 0) && (hashDistOrSingleShardRelCount > 0))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot push down this subquery\"),\n\t\t\t\t\t\terrdetail(\"A query including both range and hash \"\n\t\t\t\t\t\t\t\t  \"partitioned relations are unsupported\")));\n\t}\n\telse if ((rangeDistributedRelationCount > 0) && (appendDistributedRelationCount > 0))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot push down this subquery\"),\n\t\t\t\t\t\terrdetail(\"A query including both range and append \"\n\t\t\t\t\t\t\t\t  \"partitioned relations are unsupported\")));\n\t}\n\telse if ((appendDistributedRelationCount > 0) && (hashDistOrSingleShardRelCount > 0))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot push down this subquery\"),\n\t\t\t\t\t\terrdetail(\"A query including both append and hash \"\n\t\t\t\t\t\t\t\t  \"partitioned relations are unsupported\")));\n\t}\n\n\tforeach(relationIdCell, nonReferenceRelations)\n\t{\n\t\tOid relationId = lfirst_oid(relationIdCell);\n\t\tOid currentRelationId = relationId;\n\n\t\t/* get shard list of first relation and continue for the next relation */\n\t\tif (relationIndex == 0)\n\t\t{\n\t\t\tfirstTableRelationId = relationId;\n\t\t\trelationIndex++;\n\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* check if this table has 1-1 shard partitioning with first table */\n\t\tbool coPartitionedTables = CoPartitionedTables(firstTableRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   currentRelationId);\n\t\tif (!coPartitionedTables)\n\t\t{\n\t\t\tchar *firstRelName = get_rel_name(firstTableRelationId);\n\t\t\tchar *currentRelName = get_rel_name(currentRelationId);\n\t\t\tint compareResult = strcmp(firstRelName, currentRelName);\n\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot push down this subquery\"),\n\t\t\t\t\t\t\terrdetail(\"%s and %s are not colocated\",\n\t\t\t\t\t\t\t\t\t  (compareResult > 0 ? currentRelName : firstRelName),\n\t\t\t\t\t\t\t\t\t  (compareResult > 0 ? firstRelName :\n\t\t\t\t\t\t\t\t\t   currentRelName))));\n\t\t}\n\t}\n}\n\n\n/*\n * SubqueryTaskCreate creates a sql task by replacing the target\n * shardInterval's boundary value.\n */\nstatic Task *\nQueryPushdownTaskCreate(Query *originalQuery, int shardIndex,\n\t\t\t\t\t\tRelationRestrictionContext *restrictionContext, uint32 taskId,\n\t\t\t\t\t\tTaskType taskType, bool modifyRequiresCoordinatorEvaluation,\n\t\t\t\t\t\tbool updateQualsForOuterJoin,\n\t\t\t\t\t\tDeferredErrorMessage **planningError)\n{\n\tQuery *taskQuery = copyObject(originalQuery);\n\n\tStringInfo queryString = makeStringInfo();\n\tListCell *restrictionCell = NULL;\n\tList *taskShardList = NIL;\n\tList *relationShardList = NIL;\n\tuint64 jobId = INVALID_JOB_ID;\n\tuint64 anchorShardId = INVALID_SHARD_ID;\n\tbool modifyWithSubselect = false;\n\tRangeTblEntry *resultRangeTable = NULL;\n\tOid resultRelationOid = InvalidOid;\n\n\t/*\n\t * If it is a modify query with sub-select, we need to set result relation shard's id\n\t * as anchor shard id.\n\t */\n\tif (UpdateOrDeleteOrMergeQuery(originalQuery))\n\t{\n\t\tresultRangeTable = rt_fetch(originalQuery->resultRelation, originalQuery->rtable);\n\t\tresultRelationOid = resultRangeTable->relid;\n\t\tmodifyWithSubselect = true;\n\t}\n\n\t/*\n\t * Find the relevant shard out of each relation for this task.\n\t */\n\tforeach(restrictionCell, restrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction =\n\t\t\t(RelationRestriction *) lfirst(restrictionCell);\n\t\tOid relationId = relationRestriction->relationId;\n\t\tShardInterval *shardInterval = NULL;\n\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\t\tif (!HasDistributionKeyCacheEntry(cacheEntry))\n\t\t{\n\t\t\t/* non-distributed tables have only one shard */\n\t\t\tshardInterval = cacheEntry->sortedShardIntervalArray[0];\n\n\t\t\t/* use as anchor shard only if we couldn't find any yet */\n\t\t\tif (anchorShardId == INVALID_SHARD_ID)\n\t\t\t{\n\t\t\t\tanchorShardId = shardInterval->shardId;\n\t\t\t}\n\t\t}\n\t\telse if (UpdateOrDeleteOrMergeQuery(originalQuery))\n\t\t{\n\t\t\tshardInterval = cacheEntry->sortedShardIntervalArray[shardIndex];\n\t\t\tif (!modifyWithSubselect || relationId == resultRelationOid)\n\t\t\t{\n\t\t\t\t/* for UPDATE/DELETE the shard in the result relation becomes the anchor shard */\n\t\t\t\tanchorShardId = shardInterval->shardId;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* for SELECT we pick an arbitrary shard as the anchor shard */\n\t\t\tshardInterval = cacheEntry->sortedShardIntervalArray[shardIndex];\n\t\t\tanchorShardId = shardInterval->shardId;\n\t\t}\n\n\t\tShardInterval *copiedShardInterval = CopyShardInterval(shardInterval);\n\n\t\ttaskShardList = lappend(taskShardList, list_make1(copiedShardInterval));\n\n\t\tRelationShard *relationShard = CitusMakeNode(RelationShard);\n\t\trelationShard->relationId = copiedShardInterval->relationId;\n\t\trelationShard->shardId = copiedShardInterval->shardId;\n\n\t\trelationShardList = lappend(relationShardList, relationShard);\n\t}\n\n\tAssert(anchorShardId != INVALID_SHARD_ID);\n\n\tList *taskPlacementList = PlacementsForWorkersContainingAllShards(taskShardList);\n\tif (list_length(taskPlacementList) == 0)\n\t{\n\t\t*planningError = DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t   \"cannot find a worker that has active placements for all \"\n\t\t\t\t\t\t\t\t\t   \"shards in the query\",\n\t\t\t\t\t\t\t\t\t   NULL, NULL);\n\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * Augment the relations in the query with the shard IDs.\n\t */\n\tUpdateRelationToShardNames((Node *) taskQuery, relationShardList);\n\n\t/*\n\t * Ands are made implicit during shard pruning, as predicate comparison and\n\t * refutation depend on it being so. We need to make them explicit again so\n\t * that the query string is generated as (...) AND (...) as opposed to\n\t * (...), (...).\n\t */\n\tif (taskQuery->jointree->quals != NULL && IsA(taskQuery->jointree->quals, List))\n\t{\n\t\ttaskQuery->jointree->quals = (Node *) make_ands_explicit(\n\t\t\t(List *) taskQuery->jointree->quals);\n\t}\n\n\tif (updateQualsForOuterJoin)\n\t{\n\t\t/*\n\t\t * QueryPushdownSqlTaskList() might set this when it detects an outer join,\n\t\t * even if the outer join is not surely known to be happening between a\n\t\t * recurring and a distributed rel. However, it's still safe to call\n\t\t * UpdateWhereClauseToPushdownRecurringOuterJoinWalker() here as it only\n\t\t * acts on the where clause if the join is happening between a\n\t\t * recurring and a distributed rel.\n\t\t */\n\t\tUpdateWhereClauseToPushdownRecurringOuterJoinWalker((Node *) taskQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trelationShardList);\n\t}\n\n\n\tTask *subqueryTask = CreateBasicTask(jobId, taskId, taskType, NULL);\n\n\tif ((taskType == MODIFY_TASK && !modifyRequiresCoordinatorEvaluation) ||\n\t\ttaskType == READ_TASK)\n\t{\n\t\tpg_get_query_def(taskQuery, queryString);\n\t\tereport(DEBUG4, (errmsg(\"distributed statement: %s\",\n\t\t\t\t\t\t\t\tqueryString->data)));\n\t\tSetTaskQueryString(subqueryTask, queryString->data);\n\t}\n\n\tsubqueryTask->dependentTaskList = NULL;\n\tsubqueryTask->anchorShardId = anchorShardId;\n\tsubqueryTask->taskPlacementList = taskPlacementList;\n\tsubqueryTask->relationShardList = relationShardList;\n\n\treturn subqueryTask;\n}\n\n\n/*\n * CoPartitionedTables checks if given two distributed tables are co-located.\n */\nbool\nCoPartitionedTables(Oid firstRelationId, Oid secondRelationId)\n{\n\tCitusTableCacheEntry *firstTableCache = GetCitusTableCacheEntry(firstRelationId);\n\tCitusTableCacheEntry *secondTableCache = GetCitusTableCacheEntry(secondRelationId);\n\n\tif (firstTableCache->partitionMethod == DISTRIBUTE_BY_APPEND ||\n\t\tsecondTableCache->partitionMethod == DISTRIBUTE_BY_APPEND)\n\t{\n\t\t/*\n\t\t * Append-distributed tables can have overlapping shards. Therefore they are\n\t\t * never co-partitioned, not even with themselves.\n\t\t */\n\t\treturn false;\n\t}\n\n\t/*\n\t * Check if the tables have the same colocation ID - if so, we know\n\t * they're colocated.\n\t */\n\tif (firstTableCache->colocationId != INVALID_COLOCATION_ID &&\n\t\tfirstTableCache->colocationId == secondTableCache->colocationId)\n\t{\n\t\treturn true;\n\t}\n\n\tif (firstRelationId == secondRelationId)\n\t{\n\t\t/*\n\t\t * Even without an explicit co-location ID, non-append tables can be considered\n\t\t * co-located with themselves.\n\t\t */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * SqlTaskList creates a list of SQL tasks to execute the given job. For this,\n * the function walks over each range table in the job's range table list, gets\n * each range table's table fragments, and prunes unneeded table fragments. The\n * function then joins table fragments from different range tables, and creates\n * all fragment combinations. For each created combination, the function builds\n * a SQL task, and appends this task to a task list.\n */\nstatic List *\nSqlTaskList(Job *job)\n{\n\tList *sqlTaskList = NIL;\n\tuint32 taskIdIndex = 1; /* 0 is reserved for invalid taskId */\n\tuint64 jobId = job->jobId;\n\tbool anchorRangeTableBasedAssignment = false;\n\tuint32 anchorRangeTableId = 0;\n\n\tQuery *jobQuery = job->jobQuery;\n\tList *rangeTableList = jobQuery->rtable;\n\tList *whereClauseList = (List *) jobQuery->jointree->quals;\n\tList *dependentJobList = job->dependentJobList;\n\n\t/*\n\t * If we don't depend on a hash partition, then we determine the largest\n\t * table around which we build our queries. This reduces data fetching.\n\t */\n\tbool dependsOnHashPartitionJob = DependsOnHashPartitionJob(job);\n\tif (!dependsOnHashPartitionJob)\n\t{\n\t\tanchorRangeTableBasedAssignment = true;\n\t\tanchorRangeTableId = AnchorRangeTableId(rangeTableList);\n\n\t\tAssert(anchorRangeTableId != 0);\n\t\tAssert(anchorRangeTableId <= list_length(rangeTableList));\n\t}\n\n\t/* adjust our column old attributes for partition pruning to work */\n\tAdjustColumnOldAttributes(whereClauseList);\n\tAdjustColumnOldAttributes(jobQuery->targetList);\n\n\t/*\n\t * Ands are made implicit during shard pruning, as predicate comparison and\n\t * refutation depend on it being so. We need to make them explicit again so\n\t * that the query string is generated as (...) AND (...) as opposed to\n\t * (...), (...).\n\t */\n\tNode *whereClauseTree = (Node *) make_ands_explicit(\n\t\t(List *) jobQuery->jointree->quals);\n\tjobQuery->jointree->quals = whereClauseTree;\n\n\t/*\n\t * For each range table, we first get a list of their shards or merge tasks.\n\t * We also apply partition pruning based on the selection criteria. If all\n\t * range table fragments are pruned away, we return an empty task list.\n\t */\n\tList *rangeTableFragmentsList = RangeTableFragmentsList(rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twhereClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdependentJobList);\n\tif (rangeTableFragmentsList == NIL)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * We then generate fragment combinations according to how range tables join\n\t * with each other (and apply join pruning). Each fragment combination then\n\t * represents one SQL task's dependencies.\n\t */\n\tList *fragmentCombinationList = FragmentCombinationList(rangeTableFragmentsList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tjobQuery, dependentJobList);\n\n\t/*\n\t * Adjust RelabelType and CoerceViaIO nodes that are improper for deparsing.\n\t * We first check if there are any such nodes by using a query tree walker.\n\t * The reason is that a query tree mutator will create a deep copy of all\n\t * the query sublinks, and we don't want to do that unless necessary, as it\n\t * would be inefficient.\n\t */\n\tif (QueryTreeHasImproperForDeparseNodes((Node *) jobQuery, NULL))\n\t{\n\t\tjobQuery = (Query *) AdjustImproperForDeparseNodes((Node *) jobQuery, NULL);\n\t}\n\n\tListCell *fragmentCombinationCell = NULL;\n\tforeach(fragmentCombinationCell, fragmentCombinationList)\n\t{\n\t\tList *fragmentCombination = (List *) lfirst(fragmentCombinationCell);\n\n\t\t/* create tasks to fetch fragments required for the sql task */\n\t\tList *dataFetchTaskList = DataFetchTaskList(jobId, taskIdIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\tfragmentCombination);\n\t\tint32 dataFetchTaskCount = list_length(dataFetchTaskList);\n\t\ttaskIdIndex += dataFetchTaskCount;\n\n\t\t/* update range table entries with fragment aliases (in place) */\n\t\tQuery *taskQuery = copyObject(jobQuery);\n\t\tList *fragmentRangeTableList = taskQuery->rtable;\n\t\tUpdateRangeTableAlias(fragmentRangeTableList, fragmentCombination);\n\n\t\t/* transform the updated task query to a SQL query string */\n\t\tStringInfo sqlQueryString = makeStringInfo();\n\t\tpg_get_query_def(taskQuery, sqlQueryString);\n\n\t\tTask *sqlTask = CreateBasicTask(jobId, taskIdIndex, READ_TASK,\n\t\t\t\t\t\t\t\t\t\tsqlQueryString->data);\n\t\tsqlTask->dependentTaskList = dataFetchTaskList;\n\t\tsqlTask->relationShardList = BuildRelationShardList(fragmentRangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfragmentCombination);\n\n\t\t/* log the query string we generated */\n\t\tereport(DEBUG4, (errmsg(\"generated sql query for task %d\", sqlTask->taskId),\n\t\t\t\t\t\t errdetail(\"query string: \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t   sqlQueryString->data)));\n\n\t\tsqlTask->anchorShardId = INVALID_SHARD_ID;\n\t\tif (anchorRangeTableBasedAssignment)\n\t\t{\n\t\t\tsqlTask->anchorShardId = AnchorShardId(fragmentCombination,\n\t\t\t\t\t\t\t\t\t\t\t\t   anchorRangeTableId);\n\t\t}\n\n\t\ttaskIdIndex++;\n\t\tsqlTaskList = lappend(sqlTaskList, sqlTask);\n\t}\n\n\treturn sqlTaskList;\n}\n\n\n/*\n * RelabelTypeToCollateExpr converts RelabelType's into CollationExpr's.\n * With that, we will be able to pushdown COLLATE's.\n */\nstatic CollateExpr *\nRelabelTypeToCollateExpr(RelabelType *relabelType)\n{\n\tAssert(OidIsValid(relabelType->resultcollid));\n\n\tCollateExpr *collateExpr = makeNode(CollateExpr);\n\tcollateExpr->arg = relabelType->arg;\n\tcollateExpr->collOid = relabelType->resultcollid;\n\tcollateExpr->location = relabelType->location;\n\n\treturn collateExpr;\n}\n\n\n/*\n * DependsOnHashPartitionJob checks if the given job depends on a hash\n * partitioning job.\n */\nstatic bool\nDependsOnHashPartitionJob(Job *job)\n{\n\tbool dependsOnHashPartitionJob = false;\n\tList *dependentJobList = job->dependentJobList;\n\n\tuint32 dependentJobCount = (uint32) list_length(dependentJobList);\n\tif (dependentJobCount > 0)\n\t{\n\t\tJob *dependentJob = (Job *) linitial(dependentJobList);\n\t\tif (CitusIsA(dependentJob, MapMergeJob))\n\t\t{\n\t\t\tMapMergeJob *mapMergeJob = (MapMergeJob *) dependentJob;\n\t\t\tif (mapMergeJob->partitionType == DUAL_HASH_PARTITION_TYPE)\n\t\t\t{\n\t\t\t\tdependsOnHashPartitionJob = true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn dependsOnHashPartitionJob;\n}\n\n\n/*\n * AnchorRangeTableId determines the table around which we build our queries,\n * and returns this table's range table id. We refer to this table as the anchor\n * table, and make sure that the anchor table's shards are moved or cached only\n * when absolutely necessary.\n */\nstatic uint32\nAnchorRangeTableId(List *rangeTableList)\n{\n\tuint32 anchorRangeTableId = 0;\n\tuint64 maxTableSize = 0;\n\n\t/*\n\t * We first filter anything but ordinary tables. Then, we pick the table(s)\n\t * with the most number of shards as our anchor table. If multiple tables\n\t * have the most number of shards, we have a draw.\n\t */\n\tList *baseTableIdList = BaseRangeTableIdList(rangeTableList);\n\tList *anchorTableRTIList = AnchorRangeTableIdList(rangeTableList, baseTableIdList);\n\tListCell *anchorTableIdCell = NULL;\n\n\tint anchorTableIdCount = list_length(anchorTableRTIList);\n\tAssert(anchorTableIdCount > 0);\n\n\tif (anchorTableIdCount == 1)\n\t{\n\t\tanchorRangeTableId = (uint32) linitial_int(anchorTableRTIList);\n\t\treturn anchorRangeTableId;\n\t}\n\n\t/*\n\t * If more than one table has the most number of shards, we break the draw\n\t * by comparing table sizes and picking the table with the largest size.\n\t */\n\tforeach(anchorTableIdCell, anchorTableRTIList)\n\t{\n\t\tuint32 anchorTableId = (uint32) lfirst_int(anchorTableIdCell);\n\t\tRangeTblEntry *tableEntry = rt_fetch(anchorTableId, rangeTableList);\n\t\tuint64 tableSize = 0;\n\n\t\tList *shardList = LoadShardList(tableEntry->relid);\n\t\tListCell *shardCell = NULL;\n\n\t\tforeach(shardCell, shardList)\n\t\t{\n\t\t\tuint64 *shardIdPointer = (uint64 *) lfirst(shardCell);\n\t\t\tuint64 shardId = (*shardIdPointer);\n\t\t\tuint64 shardSize = ShardLength(shardId);\n\n\t\t\ttableSize += shardSize;\n\t\t}\n\n\t\tif (tableSize > maxTableSize)\n\t\t{\n\t\t\tmaxTableSize = tableSize;\n\t\t\tanchorRangeTableId = anchorTableId;\n\t\t}\n\t}\n\n\tif (anchorRangeTableId == 0)\n\t{\n\t\t/* all tables have the same shard count and size 0, pick the first */\n\t\tanchorRangeTableId = (uint32) linitial_int(anchorTableRTIList);\n\t}\n\n\treturn anchorRangeTableId;\n}\n\n\n/*\n * BaseRangeTableIdList walks over range tables in the given range table list,\n * finds range tables that correspond to base (non-repartitioned) tables, and\n * returns these range tables' identifiers in a new list.\n */\nstatic List *\nBaseRangeTableIdList(List *rangeTableList)\n{\n\tList *baseRangeTableIdList = NIL;\n\tuint32 rangeTableId = 1;\n\n\tListCell *rangeTableCell = NULL;\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\t\tif (GetRangeTblKind(rangeTableEntry) == CITUS_RTE_RELATION)\n\t\t{\n\t\t\tbaseRangeTableIdList = lappend_int(baseRangeTableIdList, rangeTableId);\n\t\t}\n\n\t\trangeTableId++;\n\t}\n\n\treturn baseRangeTableIdList;\n}\n\n\n/*\n * AnchorRangeTableIdList finds ordinary table(s) with the most number of shards\n * and returns the corresponding range table id(s) in a list.\n */\nstatic List *\nAnchorRangeTableIdList(List *rangeTableList, List *baseRangeTableIdList)\n{\n\tList *anchorTableRTIList = NIL;\n\tuint32 maxShardCount = 0;\n\tListCell *baseRangeTableIdCell = NULL;\n\n\tuint32 baseRangeTableCount = list_length(baseRangeTableIdList);\n\tif (baseRangeTableCount == 1)\n\t{\n\t\treturn baseRangeTableIdList;\n\t}\n\n\tuint32 referenceTableRTI = 0;\n\n\tforeach(baseRangeTableIdCell, baseRangeTableIdList)\n\t{\n\t\tuint32 baseRangeTableId = (uint32) lfirst_int(baseRangeTableIdCell);\n\t\tRangeTblEntry *tableEntry = rt_fetch(baseRangeTableId, rangeTableList);\n\n\t\tOid citusTableId = tableEntry->relid;\n\t\tif (IsCitusTableType(citusTableId, REFERENCE_TABLE))\n\t\t{\n\t\t\treferenceTableRTI = baseRangeTableId;\n\t\t\tcontinue;\n\t\t}\n\n\t\tList *shardList = LoadShardList(citusTableId);\n\n\t\tuint32 shardCount = (uint32) list_length(shardList);\n\t\tif (shardCount > maxShardCount)\n\t\t{\n\t\t\tanchorTableRTIList = list_make1_int(baseRangeTableId);\n\t\t\tmaxShardCount = shardCount;\n\t\t}\n\t\telse if (shardCount == maxShardCount)\n\t\t{\n\t\t\tanchorTableRTIList = lappend_int(anchorTableRTIList, baseRangeTableId);\n\t\t}\n\t}\n\n\t/*\n\t * We favor distributed tables over reference tables as anchor tables. But\n\t * in case we cannot find any distributed tables, we let reference table to be\n\t * anchor table. For now, we cannot see a query that might require this, but we\n\t * want to be backward compatiable.\n\t */\n\tif (list_length(anchorTableRTIList) == 0)\n\t{\n\t\treturn referenceTableRTI > 0 ? list_make1_int(referenceTableRTI) : NIL;\n\t}\n\n\treturn anchorTableRTIList;\n}\n\n\n/*\n * AdjustColumnOldAttributes adjust the old tableId (varnosyn) and old columnId\n * (varattnosyn), and sets them equal to the new values. We need this adjustment\n * for partition pruning where we compare these columns with partition columns\n * loaded from system catalogs. Since columns loaded from system catalogs always\n * have the same old and new values, we also need to adjust column values here.\n */\nstatic void\nAdjustColumnOldAttributes(List *expressionList)\n{\n\tList *columnList = pull_var_clause_default((Node *) expressionList);\n\tListCell *columnCell = NULL;\n\n\tforeach(columnCell, columnList)\n\t{\n\t\tVar *column = (Var *) lfirst(columnCell);\n\t\tcolumn->varnosyn = column->varno;\n\t\tcolumn->varattnosyn = column->varattno;\n\t}\n}\n\n\n/*\n * RangeTableFragmentsList walks over range tables in the given range table list\n * and for each table, the function creates a list of its fragments. A fragment\n * in this list represents either a regular shard or a merge task. Once a list\n * for each range table is constructed, the function applies partition pruning\n * using the given where clause list. Then, the function appends the fragment\n * list for each range table to a list of lists, and returns this list of lists.\n */\nstatic List *\nRangeTableFragmentsList(List *rangeTableList, List *whereClauseList,\n\t\t\t\t\t\tList *dependentJobList)\n{\n\tList *rangeTableFragmentsList = NIL;\n\tuint32 rangeTableIndex = 0;\n\tconst uint32 fragmentSize = sizeof(RangeTableFragment);\n\n\tListCell *rangeTableCell = NULL;\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tuint32 tableId = rangeTableIndex + 1; /* tableId starts from 1 */\n\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\t\tCitusRTEKind rangeTableKind = GetRangeTblKind(rangeTableEntry);\n\n\t\tif (rangeTableKind == CITUS_RTE_RELATION)\n\t\t{\n\t\t\tOid relationId = rangeTableEntry->relid;\n\t\t\tListCell *shardIntervalCell = NULL;\n\t\t\tList *shardFragmentList = NIL;\n\t\t\tList *prunedShardIntervalList = PruneShards(relationId, tableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twhereClauseList, NULL);\n\n\t\t\t/*\n\t\t\t * If we prune all shards for one table, query results will be empty.\n\t\t\t * We can therefore return NIL for the task list here.\n\t\t\t */\n\t\t\tif (prunedShardIntervalList == NIL)\n\t\t\t{\n\t\t\t\treturn NIL;\n\t\t\t}\n\n\t\t\tforeach(shardIntervalCell, prunedShardIntervalList)\n\t\t\t{\n\t\t\t\tShardInterval *shardInterval =\n\t\t\t\t\t(ShardInterval *) lfirst(shardIntervalCell);\n\n\t\t\t\tRangeTableFragment *shardFragment = palloc0(fragmentSize);\n\t\t\t\tshardFragment->fragmentReference = shardInterval;\n\t\t\t\tshardFragment->fragmentType = CITUS_RTE_RELATION;\n\t\t\t\tshardFragment->rangeTableId = tableId;\n\n\t\t\t\tshardFragmentList = lappend(shardFragmentList, shardFragment);\n\t\t\t}\n\n\t\t\trangeTableFragmentsList = lappend(rangeTableFragmentsList,\n\t\t\t\t\t\t\t\t\t\t\t  shardFragmentList);\n\t\t}\n\t\telse if (rangeTableKind == CITUS_RTE_REMOTE_QUERY)\n\t\t{\n\t\t\tList *mergeTaskFragmentList = NIL;\n\t\t\tListCell *mergeTaskCell = NULL;\n\n\t\t\tJob *dependentJob = JobForRangeTable(dependentJobList, rangeTableEntry);\n\t\t\tAssert(CitusIsA(dependentJob, MapMergeJob));\n\n\t\t\tMapMergeJob *dependentMapMergeJob = (MapMergeJob *) dependentJob;\n\t\t\tList *mergeTaskList = dependentMapMergeJob->mergeTaskList;\n\n\t\t\t/* if there are no tasks for the dependent job, just return NIL */\n\t\t\tif (mergeTaskList == NIL)\n\t\t\t{\n\t\t\t\treturn NIL;\n\t\t\t}\n\n\t\t\tforeach(mergeTaskCell, mergeTaskList)\n\t\t\t{\n\t\t\t\tTask *mergeTask = (Task *) lfirst(mergeTaskCell);\n\n\t\t\t\tRangeTableFragment *mergeTaskFragment = palloc0(fragmentSize);\n\t\t\t\tmergeTaskFragment->fragmentReference = mergeTask;\n\t\t\t\tmergeTaskFragment->fragmentType = CITUS_RTE_REMOTE_QUERY;\n\t\t\t\tmergeTaskFragment->rangeTableId = tableId;\n\n\t\t\t\tmergeTaskFragmentList = lappend(mergeTaskFragmentList, mergeTaskFragment);\n\t\t\t}\n\n\t\t\trangeTableFragmentsList = lappend(rangeTableFragmentsList,\n\t\t\t\t\t\t\t\t\t\t\t  mergeTaskFragmentList);\n\t\t}\n\n\t\trangeTableIndex++;\n\t}\n\n\treturn rangeTableFragmentsList;\n}\n\n\n/*\n * BuildBaseConstraint builds and returns a base constraint. This constraint\n * implements an expression in the form of (column <= max && column >= min),\n * where column is the partition key, and min and max values represent a shard's\n * min and max values. These shard values are filled in after the constraint is\n * built.\n */\nNode *\nBuildBaseConstraint(Var *column)\n{\n\t/* Build these expressions with only one argument for now */\n\tOpExpr *lessThanExpr = MakeOpExpression(column, BTLessEqualStrategyNumber);\n\tOpExpr *greaterThanExpr = MakeOpExpression(column, BTGreaterEqualStrategyNumber);\n\n\t/* Build base constaint as an and of two qual conditions */\n\tNode *baseConstraint = make_and_qual((Node *) lessThanExpr, (Node *) greaterThanExpr);\n\n\treturn baseConstraint;\n}\n\n\n/*\n * MakeOpExpressionExtended builds an operator expression node that's of\n * the form \"Var <op> Expr\", where, Expr must either be a Const or a Var\n * (*1).\n *\n * This operator expression implements the operator clause as defined by\n * the variable and the strategy number.\n */\nOpExpr *\nMakeOpExpressionExtended(Var *leftVar, Expr *rightArg, int16 strategyNumber)\n{\n\t/*\n\t * Other types of expressions are probably also fine to be used, but\n\t * none of the callers need support for them for now, so we haven't\n\t * tested them (*1).\n\t */\n\tAssert(IsA(rightArg, Const) || IsA(rightArg, Var));\n\n\tOid typeId = leftVar->vartype;\n\tOid collationId = leftVar->varcollid;\n\n\tOid accessMethodId = BTREE_AM_OID;\n\n\tOperatorCacheEntry *operatorCacheEntry = LookupOperatorByType(typeId, accessMethodId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  strategyNumber);\n\n\tOid operatorId = operatorCacheEntry->operatorId;\n\tOid operatorClassInputType = operatorCacheEntry->operatorClassInputType;\n\tchar typeType = operatorCacheEntry->typeType;\n\n\t/*\n\t * Relabel variable if input type of default operator class is not equal to\n\t * the variable type. Note that we don't relabel the variable if the default\n\t * operator class variable type is a pseudo-type.\n\t */\n\tif (operatorClassInputType != typeId && typeType != TYPTYPE_PSEUDO)\n\t{\n\t\tleftVar = (Var *) makeRelabelType((Expr *) leftVar, operatorClassInputType,\n\t\t\t\t\t\t\t\t\t\t  -1, collationId, COERCE_IMPLICIT_CAST);\n\t}\n\n\t/* Now make the expression with the given variable and a null constant */\n\tOpExpr *expression = (OpExpr *) make_opclause(operatorId,\n\t\t\t\t\t\t\t\t\t\t\t\t  InvalidOid, /* no result type yet */\n\t\t\t\t\t\t\t\t\t\t\t\t  false, /* no return set */\n\t\t\t\t\t\t\t\t\t\t\t\t  (Expr *) leftVar,\n\t\t\t\t\t\t\t\t\t\t\t\t  rightArg,\n\t\t\t\t\t\t\t\t\t\t\t\t  InvalidOid, collationId);\n\n\t/* Set implementing function id and result type */\n\texpression->opfuncid = get_opcode(operatorId);\n\texpression->opresulttype = get_func_rettype(expression->opfuncid);\n\n\treturn expression;\n}\n\n\n/*\n * MakeOpExpression is a wrapper around MakeOpExpressionExtended\n * that creates a null constant of the appropriate type for right\n * hand side operator class input type. As a result, it builds an\n * operator expression node that's of the form \"Var <op> NULL\".\n */\nOpExpr *\nMakeOpExpression(Var *leftVar, int16 strategyNumber)\n{\n\tOid typeId = leftVar->vartype;\n\tOid typeModId = leftVar->vartypmod;\n\tOid collationId = leftVar->varcollid;\n\n\tOid accessMethodId = BTREE_AM_OID;\n\n\tOperatorCacheEntry *operatorCacheEntry = LookupOperatorByType(typeId, accessMethodId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  strategyNumber);\n\tOid operatorClassInputType = operatorCacheEntry->operatorClassInputType;\n\n\tConst *constantValue = makeNullConst(operatorClassInputType, typeModId, collationId);\n\n\treturn MakeOpExpressionExtended(leftVar, (Expr *) constantValue, strategyNumber);\n}\n\n\n/*\n * LookupOperatorByType is a wrapper around GetOperatorByType(),\n * operatorClassInputType() and get_typtype() functions that uses a cache to avoid\n * multiple lookups of operators and its related fields within a single session by\n * their types, access methods and strategy numbers.\n * LookupOperatorByType function errors out if it cannot find corresponding\n * default operator class with the given parameters on the system catalogs.\n */\nstatic OperatorCacheEntry *\nLookupOperatorByType(Oid typeId, Oid accessMethodId, int16 strategyNumber)\n{\n\tOperatorCacheEntry *matchingCacheEntry = NULL;\n\tListCell *cacheEntryCell = NULL;\n\n\t/* search the cache */\n\tforeach(cacheEntryCell, OperatorCache)\n\t{\n\t\tOperatorCacheEntry *cacheEntry = lfirst(cacheEntryCell);\n\n\t\tif ((cacheEntry->typeId == typeId) &&\n\t\t\t(cacheEntry->accessMethodId == accessMethodId) &&\n\t\t\t(cacheEntry->strategyNumber == strategyNumber))\n\t\t{\n\t\t\tmatchingCacheEntry = cacheEntry;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* if not found in the cache, call GetOperatorByType and put the result in cache */\n\tif (matchingCacheEntry == NULL)\n\t{\n\t\tOid operatorClassId = GetDefaultOpClass(typeId, accessMethodId);\n\n\t\tif (operatorClassId == InvalidOid)\n\t\t{\n\t\t\t/* if operatorId is invalid, error out */\n\t\t\tereport(ERROR, (errmsg(\"cannot find default operator class for type:%d,\"\n\t\t\t\t\t\t\t\t   \" access method: %d\", typeId, accessMethodId)));\n\t\t}\n\n\t\t/* fill the other fields to the cache */\n\t\tOid operatorId = GetOperatorByType(typeId, accessMethodId, strategyNumber);\n\t\tOid operatorClassInputType = get_opclass_input_type(operatorClassId);\n\t\tchar typeType = get_typtype(operatorClassInputType);\n\n\t\t/* make sure we've initialized CacheMemoryContext */\n\t\tif (CacheMemoryContext == NULL)\n\t\t{\n\t\t\tCreateCacheMemoryContext();\n\t\t}\n\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(CacheMemoryContext);\n\n\t\tmatchingCacheEntry = palloc0(sizeof(OperatorCacheEntry));\n\t\tmatchingCacheEntry->typeId = typeId;\n\t\tmatchingCacheEntry->accessMethodId = accessMethodId;\n\t\tmatchingCacheEntry->strategyNumber = strategyNumber;\n\t\tmatchingCacheEntry->operatorId = operatorId;\n\t\tmatchingCacheEntry->operatorClassInputType = operatorClassInputType;\n\t\tmatchingCacheEntry->typeType = typeType;\n\n\t\tOperatorCache = lappend(OperatorCache, matchingCacheEntry);\n\n\t\tMemoryContextSwitchTo(oldContext);\n\t}\n\n\treturn matchingCacheEntry;\n}\n\n\n/*\n * GetOperatorByType returns the operator oid for the given type, access method,\n * and strategy number.\n */\nstatic Oid\nGetOperatorByType(Oid typeId, Oid accessMethodId, int16 strategyNumber)\n{\n\t/* Get default operator class from pg_opclass */\n\tOid operatorClassId = GetDefaultOpClass(typeId, accessMethodId);\n\n\tOid operatorFamily = get_opclass_family(operatorClassId);\n\tOid operatorClassInputType = get_opclass_input_type(operatorClassId);\n\n\t/* Lookup for the operator with the desired input type in the family */\n\tOid operatorId = get_opfamily_member(operatorFamily, operatorClassInputType,\n\t\t\t\t\t\t\t\t\t\t operatorClassInputType, strategyNumber);\n\treturn operatorId;\n}\n\n\n/*\n * BinaryOpExpression checks that a given expression is a binary operator. If\n * this is the case it returns true and sets leftOperand and rightOperand to\n * the left and right hand side of the operator. left/rightOperand will be\n * stripped of implicit coercions by strip_implicit_coercions.\n */\nbool\nBinaryOpExpression(Expr *clause, Node **leftOperand, Node **rightOperand)\n{\n\tif (!is_opclause(clause) || list_length(((OpExpr *) clause)->args) != 2)\n\t{\n\t\tif (leftOperand != NULL)\n\t\t{\n\t\t\t*leftOperand = NULL;\n\t\t}\n\t\tif (rightOperand != NULL)\n\t\t{\n\t\t\t*rightOperand = NULL;\n\t\t}\n\t\treturn false;\n\t}\n\tif (leftOperand != NULL)\n\t{\n\t\t*leftOperand = get_leftop(clause);\n\t\tAssert(*leftOperand != NULL);\n\t\t*leftOperand = strip_implicit_coercions(*leftOperand);\n\t}\n\tif (rightOperand != NULL)\n\t{\n\t\t*rightOperand = get_rightop(clause);\n\t\tAssert(*rightOperand != NULL);\n\t\t*rightOperand = strip_implicit_coercions(*rightOperand);\n\t}\n\treturn true;\n}\n\n\n/*\n * MakeInt4Column creates a column of int4 type with invalid table id and max\n * attribute number.\n */\nVar *\nMakeInt4Column()\n{\n\tIndex tableId = 0;\n\tAttrNumber columnAttributeNumber = RESERVED_HASHED_COLUMN_ID;\n\tOid columnType = INT4OID;\n\tint32 columnTypeMod = -1;\n\tOid columnCollationOid = InvalidOid;\n\tIndex columnLevelSup = 0;\n\n\tVar *int4Column = makeVar(tableId, columnAttributeNumber, columnType,\n\t\t\t\t\t\t\t  columnTypeMod, columnCollationOid, columnLevelSup);\n\treturn int4Column;\n}\n\n\n/* Updates the base constraint with the given min/max values. */\nvoid\nUpdateConstraint(Node *baseConstraint, ShardInterval *shardInterval)\n{\n\tBoolExpr *andExpr = (BoolExpr *) baseConstraint;\n\tNode *lessThanExpr = (Node *) linitial(andExpr->args);\n\tNode *greaterThanExpr = (Node *) lsecond(andExpr->args);\n\n\tNode *minNode = get_rightop((Expr *) greaterThanExpr); /* right op */\n\tNode *maxNode = get_rightop((Expr *) lessThanExpr);    /* right op */\n\n\tAssert(shardInterval != NULL);\n\tAssert(shardInterval->minValueExists);\n\tAssert(shardInterval->maxValueExists);\n\tAssert(minNode != NULL);\n\tAssert(maxNode != NULL);\n\tAssert(IsA(minNode, Const));\n\tAssert(IsA(maxNode, Const));\n\n\tConst *minConstant = (Const *) minNode;\n\tConst *maxConstant = (Const *) maxNode;\n\n\tminConstant->constvalue = datumCopy(shardInterval->minValue,\n\t\t\t\t\t\t\t\t\t\tshardInterval->valueByVal,\n\t\t\t\t\t\t\t\t\t\tshardInterval->valueTypeLen);\n\tmaxConstant->constvalue = datumCopy(shardInterval->maxValue,\n\t\t\t\t\t\t\t\t\t\tshardInterval->valueByVal,\n\t\t\t\t\t\t\t\t\t\tshardInterval->valueTypeLen);\n\n\tminConstant->constisnull = false;\n\tmaxConstant->constisnull = false;\n}\n\n\n/*\n * FragmentCombinationList first builds an ordered sequence of range tables that\n * join together. The function then iteratively adds fragments from each joined\n * range table, and forms fragment combinations (lists) that cover all tables.\n * While doing so, the function also performs join pruning to remove unnecessary\n * fragment pairs. Last, the function adds each fragment combination (list) to a\n * list, and returns this list.\n */\nstatic List *\nFragmentCombinationList(List *rangeTableFragmentsList, Query *jobQuery,\n\t\t\t\t\t\tList *dependentJobList)\n{\n\tList *fragmentCombinationList = NIL;\n\tList *fragmentCombinationQueue = NIL;\n\tList *emptyList = NIL;\n\n\t/* find a sequence that joins the range tables in the list */\n\tJoinSequenceNode *joinSequenceArray = JoinSequenceArray(rangeTableFragmentsList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tjobQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdependentJobList);\n\n\t/*\n\t * We use breadth-first search with pruning to create fragment combinations.\n\t * For this, we first queue the root node (an empty combination), and then\n\t * start traversing our search space.\n\t */\n\tfragmentCombinationQueue = lappend(fragmentCombinationQueue, emptyList);\n\twhile (fragmentCombinationQueue != NIL)\n\t{\n\t\tListCell *tableFragmentCell = NULL;\n\t\tint32 joiningTableSequenceIndex = -1;\n\n\t\t/* pop first element from the fragment queue */\n\t\tList *fragmentCombination = linitial(fragmentCombinationQueue);\n\t\tfragmentCombinationQueue = list_delete_first(fragmentCombinationQueue);\n\n\t\t/*\n\t\t * If this combination covered all range tables in a join sequence, add\n\t\t * this combination to our result set.\n\t\t */\n\t\tint32 joinSequenceIndex = list_length(fragmentCombination);\n\t\tint32 rangeTableCount = list_length(rangeTableFragmentsList);\n\t\tif (joinSequenceIndex == rangeTableCount)\n\t\t{\n\t\t\tfragmentCombinationList = lappend(fragmentCombinationList,\n\t\t\t\t\t\t\t\t\t\t\t  fragmentCombination);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* find the next range table to add to our search space */\n\t\tuint32 tableId = joinSequenceArray[joinSequenceIndex].rangeTableId;\n\t\tList *tableFragments = FindRangeTableFragmentsList(rangeTableFragmentsList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   tableId);\n\n\t\t/* resolve sequence index for the previous range table we join against */\n\t\tint32 joiningTableId = joinSequenceArray[joinSequenceIndex].joiningRangeTableId;\n\t\tif (joiningTableId != NON_PRUNABLE_JOIN)\n\t\t{\n\t\t\tfor (int32 sequenceIndex = 0; sequenceIndex < rangeTableCount;\n\t\t\t\t sequenceIndex++)\n\t\t\t{\n\t\t\t\tJoinSequenceNode *joinSequenceNode = &joinSequenceArray[sequenceIndex];\n\t\t\t\tif (joinSequenceNode->rangeTableId == joiningTableId)\n\t\t\t\t{\n\t\t\t\t\tjoiningTableSequenceIndex = sequenceIndex;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tAssert(joiningTableSequenceIndex != -1);\n\t\t}\n\n\t\t/*\n\t\t * We walk over each range table fragment, and check if we can prune out\n\t\t * this fragment joining with the existing fragment combination. If we\n\t\t * can't prune away, we create a new fragment combination and add it to\n\t\t * our search space.\n\t\t */\n\t\tforeach(tableFragmentCell, tableFragments)\n\t\t{\n\t\t\tRangeTableFragment *tableFragment = lfirst(tableFragmentCell);\n\t\t\tbool joinPrunable = false;\n\n\t\t\tif (joiningTableId != NON_PRUNABLE_JOIN)\n\t\t\t{\n\t\t\t\tRangeTableFragment *joiningTableFragment =\n\t\t\t\t\tlist_nth(fragmentCombination, joiningTableSequenceIndex);\n\n\t\t\t\tjoinPrunable = JoinPrunable(joiningTableFragment, tableFragment);\n\t\t\t}\n\n\t\t\t/* if join can't be pruned, extend fragment combination and search */\n\t\t\tif (!joinPrunable)\n\t\t\t{\n\t\t\t\tList *newFragmentCombination = list_copy(fragmentCombination);\n\t\t\t\tnewFragmentCombination = lappend(newFragmentCombination, tableFragment);\n\n\t\t\t\tfragmentCombinationQueue = lappend(fragmentCombinationQueue,\n\t\t\t\t\t\t\t\t\t\t\t\t   newFragmentCombination);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn fragmentCombinationList;\n}\n\n\n/*\n * NodeIsRangeTblRefReferenceTable checks if the node is a RangeTblRef that\n * points to a reference table in the rangeTableList.\n */\nstatic bool\nNodeIsRangeTblRefReferenceTable(Node *node, List *rangeTableList)\n{\n\tif (!IsA(node, RangeTblRef))\n\t{\n\t\treturn false;\n\t}\n\tRangeTblRef *tableRef = castNode(RangeTblRef, node);\n\tRangeTblEntry *rangeTableEntry = rt_fetch(tableRef->rtindex, rangeTableList);\n\tCitusRTEKind rangeTableType = GetRangeTblKind(rangeTableEntry);\n\tif (rangeTableType != CITUS_RTE_RELATION)\n\t{\n\t\treturn false;\n\t}\n\treturn IsCitusTableType(rangeTableEntry->relid, REFERENCE_TABLE);\n}\n\n\n/*\n * FetchEqualityAttrNumsForRTE fetches the attribute numbers from quals\n * which have an equality operator\n */\nList *\nFetchEqualityAttrNumsForRTE(Node *node)\n{\n\tif (node == NULL)\n\t{\n\t\treturn NIL;\n\t}\n\tif (IsA(node, List))\n\t{\n\t\treturn FetchEqualityAttrNumsForList((List *) node);\n\t}\n\telse if (IsA(node, OpExpr))\n\t{\n\t\treturn FetchEqualityAttrNumsForRTEOpExpr((OpExpr *) node);\n\t}\n\telse if (IsA(node, BoolExpr))\n\t{\n\t\treturn FetchEqualityAttrNumsForRTEBoolExpr((BoolExpr *) node);\n\t}\n\treturn NIL;\n}\n\n\n/*\n * FetchEqualityAttrNumsForList fetches the attribute numbers of expression\n * of the form \"= constant\" from the given node list.\n */\nstatic List *\nFetchEqualityAttrNumsForList(List *nodeList)\n{\n\tList *attributeNums = NIL;\n\tNode *node = NULL;\n\tbool hasAtLeastOneEquality = false;\n\tforeach_declared_ptr(node, nodeList)\n\t{\n\t\tList *fetchedEqualityAttrNums =\n\t\t\tFetchEqualityAttrNumsForRTE(node);\n\t\thasAtLeastOneEquality |= list_length(fetchedEqualityAttrNums) > 0;\n\t\tattributeNums = list_concat(attributeNums, fetchedEqualityAttrNums);\n\t}\n\n\t/*\n\t * the given list is in the form of AND'ed expressions\n\t * hence if we have one equality then it is enough.\n\t * E.g: dist.a = 5 AND dist.a > 10\n\t */\n\tif (hasAtLeastOneEquality)\n\t{\n\t\treturn attributeNums;\n\t}\n\treturn NIL;\n}\n\n\n/*\n * FetchEqualityAttrNumsForRTEOpExpr fetches the attribute numbers of expression\n * of the form \"= constant\" from the given opExpr.\n */\nstatic List *\nFetchEqualityAttrNumsForRTEOpExpr(OpExpr *opExpr)\n{\n\tif (!OperatorImplementsEquality(opExpr->opno))\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *attributeNums = NIL;\n\tVar *var = NULL;\n\tif (VarConstOpExprClause(opExpr, &var, NULL))\n\t{\n\t\tattributeNums = lappend_int(attributeNums, var->varattno);\n\t}\n\treturn attributeNums;\n}\n\n\n/*\n * FetchEqualityAttrNumsForRTEBoolExpr fetches the attribute numbers of expression\n * of the form \"= constant\" from the given boolExpr.\n */\nstatic List *\nFetchEqualityAttrNumsForRTEBoolExpr(BoolExpr *boolExpr)\n{\n\tif (boolExpr->boolop != AND_EXPR && boolExpr->boolop != OR_EXPR)\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *attributeNums = NIL;\n\tbool hasEquality = true;\n\tNode *arg = NULL;\n\tforeach_declared_ptr(arg, boolExpr->args)\n\t{\n\t\tList *attributeNumsInSubExpression = FetchEqualityAttrNumsForRTE(arg);\n\t\tif (boolExpr->boolop == AND_EXPR)\n\t\t{\n\t\t\thasEquality |= list_length(attributeNumsInSubExpression) > 0;\n\t\t}\n\t\telse if (boolExpr->boolop == OR_EXPR)\n\t\t{\n\t\t\thasEquality &= list_length(attributeNumsInSubExpression) > 0;\n\t\t}\n\t\tattributeNums = list_concat(attributeNums, attributeNumsInSubExpression);\n\t}\n\tif (hasEquality)\n\t{\n\t\treturn attributeNums;\n\t}\n\treturn NIL;\n}\n\n\n/*\n * JoinSequenceArray walks over the join nodes in the job query and constructs a join\n * sequence containing an entry for each joined table. The function then returns an\n * array of join sequence nodes, in which each node contains the id of a table in the\n * range table list and the id of a preceding table with which it is joined, if any.\n */\nstatic JoinSequenceNode *\nJoinSequenceArray(List *rangeTableFragmentsList, Query *jobQuery, List *dependentJobList)\n{\n\tList *rangeTableList = jobQuery->rtable;\n\tuint32 rangeTableCount = (uint32) list_length(rangeTableList);\n\tuint32 sequenceNodeSize = sizeof(JoinSequenceNode);\n\tuint32 joinedTableCount = 0;\n\tListCell *joinExprCell = NULL;\n\tuint32 firstRangeTableId = 1;\n\tJoinSequenceNode *joinSequenceArray = palloc0(rangeTableCount * sequenceNodeSize);\n\n\tList *joinExprList = JoinExprList(jobQuery->jointree);\n\n\t/* pick first range table as starting table for the join sequence */\n\tif (list_length(joinExprList) > 0)\n\t{\n\t\tJoinExpr *firstExpr = (JoinExpr *) linitial(joinExprList);\n\t\tRangeTblRef *leftTableRef = (RangeTblRef *) firstExpr->larg;\n\t\tfirstRangeTableId = leftTableRef->rtindex;\n\t}\n\telse\n\t{\n\t\t/* when there are no joins, the join sequence contains a node for the table */\n\t\tfirstRangeTableId = 1;\n\t}\n\n\tjoinSequenceArray[joinedTableCount].rangeTableId = firstRangeTableId;\n\tjoinSequenceArray[joinedTableCount].joiningRangeTableId = NON_PRUNABLE_JOIN;\n\tjoinedTableCount++;\n\n\tforeach(joinExprCell, joinExprList)\n\t{\n\t\tJoinExpr *joinExpr = (JoinExpr *) lfirst(joinExprCell);\n\t\tRangeTblRef *rightTableRef = castNode(RangeTblRef, joinExpr->rarg);\n\t\tuint32 nextRangeTableId = rightTableRef->rtindex;\n\t\tIndex existingRangeTableId = 0;\n\t\tbool applyJoinPruning = false;\n\n\t\tList *nextJoinClauseList = make_ands_implicit((Expr *) joinExpr->quals);\n\t\tbool leftIsReferenceTable = NodeIsRangeTblRefReferenceTable(joinExpr->larg,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trangeTableList);\n\t\tbool rightIsReferenceTable = NodeIsRangeTblRefReferenceTable(joinExpr->rarg,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t rangeTableList);\n\t\tbool isReferenceJoin = IsSupportedReferenceJoin(joinExpr->jointype,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tleftIsReferenceTable,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\trightIsReferenceTable);\n\n\t\t/*\n\t\t * If next join clause list is empty, the user tried a cartesian product\n\t\t * between tables. We don't support this functionality for non\n\t\t * reference joins, and error out.\n\t\t */\n\t\tif (nextJoinClauseList == NIL && !isReferenceJoin)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot perform distributed planning on this query\"),\n\t\t\t\t\t\t\terrdetail(\"Cartesian products are currently unsupported\")));\n\t\t}\n\n\t\t/*\n\t\t * We now determine if we can apply join pruning between existing range\n\t\t * tables and this new one.\n\t\t */\n\t\tNode *nextJoinClause = NULL;\n\t\tforeach_declared_ptr(nextJoinClause, nextJoinClauseList)\n\t\t{\n\t\t\tif (!NodeIsEqualsOpExpr(nextJoinClause))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tOpExpr *nextJoinClauseOpExpr = castNode(OpExpr, nextJoinClause);\n\n\t\t\tif (!IsJoinClause((Node *) nextJoinClauseOpExpr))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tVar *leftColumn = LeftColumnOrNULL(nextJoinClauseOpExpr);\n\t\t\tVar *rightColumn = RightColumnOrNULL(nextJoinClauseOpExpr);\n\t\t\tif (leftColumn == NULL || rightColumn == NULL)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tIndex leftRangeTableId = leftColumn->varno;\n\t\t\tIndex rightRangeTableId = rightColumn->varno;\n\n\t\t\t/*\n\t\t\t * We have a table from the existing join list joining with the next\n\t\t\t * table. First resolve the existing table's range table id.\n\t\t\t */\n\t\t\tif (leftRangeTableId == nextRangeTableId)\n\t\t\t{\n\t\t\t\texistingRangeTableId = rightRangeTableId;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\texistingRangeTableId = leftRangeTableId;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Then, we check if we can apply join pruning between the existing\n\t\t\t * range table and this new one. For this, columns need to have the\n\t\t\t * same type and be the partition column for their respective tables.\n\t\t\t */\n\t\t\tif (leftColumn->vartype != rightColumn->vartype)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tbool leftPartitioned = PartitionedOnColumn(leftColumn, rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   dependentJobList);\n\t\t\tbool rightPartitioned = PartitionedOnColumn(rightColumn, rangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdependentJobList);\n\t\t\tif (leftPartitioned && rightPartitioned)\n\t\t\t{\n\t\t\t\t/* make sure this join clause references only simple columns */\n\t\t\t\tCheckJoinBetweenColumns(nextJoinClauseOpExpr);\n\n\t\t\t\tapplyJoinPruning = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/* set next joining range table's info in the join sequence */\n\t\tJoinSequenceNode *nextJoinSequenceNode = &joinSequenceArray[joinedTableCount];\n\t\tif (applyJoinPruning)\n\t\t{\n\t\t\tnextJoinSequenceNode->rangeTableId = nextRangeTableId;\n\t\t\tnextJoinSequenceNode->joiningRangeTableId = (int32) existingRangeTableId;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnextJoinSequenceNode->rangeTableId = nextRangeTableId;\n\t\t\tnextJoinSequenceNode->joiningRangeTableId = NON_PRUNABLE_JOIN;\n\t\t}\n\n\t\tjoinedTableCount++;\n\t}\n\n\treturn joinSequenceArray;\n}\n\n\n/*\n * PartitionedOnColumn finds the given column's range table entry, and checks if\n * that range table is partitioned on the given column. Note that since reference\n * tables do not have partition columns, the function returns false when the distributed\n * relation is a reference table.\n */\nstatic bool\nPartitionedOnColumn(Var *column, List *rangeTableList, List *dependentJobList)\n{\n\tbool partitionedOnColumn = false;\n\tIndex rangeTableId = column->varno;\n\tRangeTblEntry *rangeTableEntry = rt_fetch(rangeTableId, rangeTableList);\n\n\tCitusRTEKind rangeTableType = GetRangeTblKind(rangeTableEntry);\n\tif (rangeTableType == CITUS_RTE_RELATION)\n\t{\n\t\tOid relationId = rangeTableEntry->relid;\n\t\tVar *partitionColumn = PartitionColumn(relationId, rangeTableId);\n\n\t\t/* non-distributed tables do not have partition columns */\n\t\tif (IsCitusTable(relationId) && !HasDistributionKey(relationId))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tif (partitionColumn->varattno == column->varattno)\n\t\t{\n\t\t\tpartitionedOnColumn = true;\n\t\t}\n\t}\n\telse if (rangeTableType == CITUS_RTE_REMOTE_QUERY)\n\t{\n\t\tJob *job = JobForRangeTable(dependentJobList, rangeTableEntry);\n\t\tMapMergeJob *mapMergeJob = (MapMergeJob *) job;\n\n\t\t/*\n\t\t * The column's current attribute number is it's location in the target\n\t\t * list for the table represented by the remote query. We retrieve this\n\t\t * value from the target list to compare against the partition column\n\t\t * as stored in the job.\n\t\t */\n\t\tList *targetEntryList = job->jobQuery->targetList;\n\t\tint32 columnIndex = column->varattno - 1;\n\t\tAssert(columnIndex >= 0);\n\t\tAssert(columnIndex < list_length(targetEntryList));\n\n\t\tTargetEntry *targetEntry = (TargetEntry *) list_nth(targetEntryList, columnIndex);\n\t\tVar *remoteRelationColumn = (Var *) targetEntry->expr;\n\t\tAssert(IsA(remoteRelationColumn, Var));\n\n\t\t/* retrieve the partition column for the job */\n\t\tVar *partitionColumn = mapMergeJob->partitionColumn;\n\t\tif (partitionColumn->varattno == remoteRelationColumn->varattno)\n\t\t{\n\t\t\tpartitionedOnColumn = true;\n\t\t}\n\t}\n\n\treturn partitionedOnColumn;\n}\n\n\n/* Checks that the join clause references only simple columns. */\nstatic void\nCheckJoinBetweenColumns(OpExpr *joinClause)\n{\n\tList *argumentList = joinClause->args;\n\tNode *leftArgument = (Node *) linitial(argumentList);\n\tNode *rightArgument = (Node *) lsecond(argumentList);\n\tNode *strippedLeftArgument = strip_implicit_coercions(leftArgument);\n\tNode *strippedRightArgument = strip_implicit_coercions(rightArgument);\n\n\tNodeTag leftArgumentType = nodeTag(strippedLeftArgument);\n\tNodeTag rightArgumentType = nodeTag(strippedRightArgument);\n\n\tif (leftArgumentType != T_Var || rightArgumentType != T_Var)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot perform local joins that involve expressions\"),\n\t\t\t\t\t\terrdetail(\"local joins can be performed between columns only\")));\n\t}\n}\n\n\n/*\n * FindRangeTableFragmentsList walks over the given list of range table fragments\n * and, returns the one with the given table id.\n */\nstatic List *\nFindRangeTableFragmentsList(List *rangeTableFragmentsList, int tableId)\n{\n\tList *foundTableFragments = NIL;\n\tListCell *rangeTableFragmentsCell = NULL;\n\n\tforeach(rangeTableFragmentsCell, rangeTableFragmentsList)\n\t{\n\t\tList *tableFragments = (List *) lfirst(rangeTableFragmentsCell);\n\t\tif (tableFragments != NIL)\n\t\t{\n\t\t\tRangeTableFragment *tableFragment =\n\t\t\t\t(RangeTableFragment *) linitial(tableFragments);\n\t\t\tif (tableFragment->rangeTableId == tableId)\n\t\t\t{\n\t\t\t\tfoundTableFragments = tableFragments;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn foundTableFragments;\n}\n\n\n/*\n * JoinPrunable checks if a join between the given left and right fragments can\n * be pruned away, without performing the actual join. To do this, the function\n * checks if we have a hash repartition join. If we do, the function determines\n * pruning based on partitionIds. Else if we have a merge repartition join, the\n * function checks if the two fragments have disjoint intervals.\n */\nstatic bool\nJoinPrunable(RangeTableFragment *leftFragment, RangeTableFragment *rightFragment)\n{\n\t/*\n\t * If both range tables are remote queries, we then have a hash repartition\n\t * join. In that case, we can just prune away this join if left and right\n\t * hand side fragments have the same partitionId.\n\t */\n\tif (leftFragment->fragmentType == CITUS_RTE_REMOTE_QUERY &&\n\t\trightFragment->fragmentType == CITUS_RTE_REMOTE_QUERY)\n\t{\n\t\tTask *leftMergeTask = (Task *) leftFragment->fragmentReference;\n\t\tTask *rightMergeTask = (Task *) rightFragment->fragmentReference;\n\n\n\t\tif (leftMergeTask->partitionId != rightMergeTask->partitionId)\n\t\t{\n\t\t\tereport(DEBUG2, (errmsg(\"join prunable for task partitionId %u and %u\",\n\t\t\t\t\t\t\t\t\tleftMergeTask->partitionId,\n\t\t\t\t\t\t\t\t\trightMergeTask->partitionId)));\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\n\t/*\n\t * We have a single (re)partition join. We now get shard intervals for both\n\t * fragments, and then check if these intervals overlap.\n\t */\n\tShardInterval *leftFragmentInterval = FragmentInterval(leftFragment);\n\tShardInterval *rightFragmentInterval = FragmentInterval(rightFragment);\n\n\tbool overlap = ShardIntervalsOverlap(leftFragmentInterval, rightFragmentInterval);\n\tif (!overlap)\n\t{\n\t\tif (IsLoggableLevel(DEBUG2))\n\t\t{\n\t\t\tStringInfo leftString = FragmentIntervalString(leftFragmentInterval);\n\t\t\tStringInfo rightString = FragmentIntervalString(rightFragmentInterval);\n\n\t\t\tereport(DEBUG2, (errmsg(\"join prunable for intervals %s and %s\",\n\t\t\t\t\t\t\t\t\tleftString->data, rightString->data)));\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * FragmentInterval takes the given fragment, and determines the range of data\n * covered by this fragment. The function then returns this range (interval).\n */\nstatic ShardInterval *\nFragmentInterval(RangeTableFragment *fragment)\n{\n\tShardInterval *fragmentInterval = NULL;\n\tif (fragment->fragmentType == CITUS_RTE_RELATION)\n\t{\n\t\tAssert(CitusIsA(fragment->fragmentReference, ShardInterval));\n\t\tfragmentInterval = (ShardInterval *) fragment->fragmentReference;\n\t}\n\telse if (fragment->fragmentType == CITUS_RTE_REMOTE_QUERY)\n\t{\n\t\tAssert(CitusIsA(fragment->fragmentReference, Task));\n\n\t\tTask *mergeTask = (Task *) fragment->fragmentReference;\n\t\tfragmentInterval = mergeTask->shardInterval;\n\t}\n\n\treturn fragmentInterval;\n}\n\n\n/* Checks if the given shard intervals have overlapping ranges. */\nbool\nShardIntervalsOverlap(ShardInterval *firstInterval, ShardInterval *secondInterval)\n{\n\tCitusTableCacheEntry *intervalRelation =\n\t\tGetCitusTableCacheEntry(firstInterval->relationId);\n\n\tAssert(IsCitusTableTypeCacheEntry(intervalRelation, DISTRIBUTED_TABLE));\n\n\tif (!(firstInterval->minValueExists && firstInterval->maxValueExists &&\n\t\t  secondInterval->minValueExists && secondInterval->maxValueExists))\n\t{\n\t\treturn true;\n\t}\n\n\tDatum firstMin = firstInterval->minValue;\n\tDatum firstMax = firstInterval->maxValue;\n\tDatum secondMin = secondInterval->minValue;\n\tDatum secondMax = secondInterval->maxValue;\n\n\tFmgrInfo *comparisonFunction = intervalRelation->shardIntervalCompareFunction;\n\tOid collation = intervalRelation->partitionColumn->varcollid;\n\n\treturn ShardIntervalsOverlapWithParams(firstMin, firstMax, secondMin, secondMax,\n\t\t\t\t\t\t\t\t\t\t   comparisonFunction, collation);\n}\n\n\n/*\n * ShardIntervalsOverlapWithParams is a helper function which compares the input\n * shard min/max values, and returns true if the shards overlap.\n * The caller is responsible to ensure the input shard min/max values are not NULL.\n */\nbool\nShardIntervalsOverlapWithParams(Datum firstMin, Datum firstMax, Datum secondMin,\n\t\t\t\t\t\t\t\tDatum secondMax, FmgrInfo *comparisonFunction,\n\t\t\t\t\t\t\t\tOid collation)\n{\n\t/*\n\t * We need to have min/max values for both intervals first. Then, we assume\n\t * two intervals i1 = [min1, max1] and i2 = [min2, max2] do not overlap if\n\t * (max1 < min2) or (max2 < min1). For details, please see the explanation\n\t * on overlapping intervals at http://www.rgrjr.com/emacs/overlap.html.\n\t */\n\tDatum firstDatum = FunctionCall2Coll(comparisonFunction, collation, firstMax,\n\t\t\t\t\t\t\t\t\t\t secondMin);\n\tDatum secondDatum = FunctionCall2Coll(comparisonFunction, collation, secondMax,\n\t\t\t\t\t\t\t\t\t\t  firstMin);\n\tint firstComparison = DatumGetInt32(firstDatum);\n\tint secondComparison = DatumGetInt32(secondDatum);\n\n\tif (firstComparison < 0 || secondComparison < 0)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * FragmentIntervalString takes the given fragment interval, and converts this\n * interval into its string representation for use in debug messages.\n */\nstatic StringInfo\nFragmentIntervalString(ShardInterval *fragmentInterval)\n{\n\tOid typeId = fragmentInterval->valueTypeId;\n\tOid outputFunctionId = InvalidOid;\n\tbool typeVariableLength = false;\n\n\tAssert(fragmentInterval->minValueExists);\n\tAssert(fragmentInterval->maxValueExists);\n\n\tFmgrInfo *outputFunction = (FmgrInfo *) palloc0(sizeof(FmgrInfo));\n\tgetTypeOutputInfo(typeId, &outputFunctionId, &typeVariableLength);\n\tfmgr_info(outputFunctionId, outputFunction);\n\n\tchar *minValueString = OutputFunctionCall(outputFunction, fragmentInterval->minValue);\n\tchar *maxValueString = OutputFunctionCall(outputFunction, fragmentInterval->maxValue);\n\n\tStringInfo fragmentIntervalString = makeStringInfo();\n\tappendStringInfo(fragmentIntervalString, \"[%s,%s]\", minValueString, maxValueString);\n\n\treturn fragmentIntervalString;\n}\n\n\n/*\n * DataFetchTaskList builds a merge fetch task for every remote query result\n * in the given fragment list, appends these merge fetch tasks into a list,\n * and returns this list.\n */\nstatic List *\nDataFetchTaskList(uint64 jobId, uint32 taskIdIndex, List *fragmentList)\n{\n\tList *dataFetchTaskList = NIL;\n\tListCell *fragmentCell = NULL;\n\n\tforeach(fragmentCell, fragmentList)\n\t{\n\t\tRangeTableFragment *fragment = (RangeTableFragment *) lfirst(fragmentCell);\n\t\tif (fragment->fragmentType == CITUS_RTE_REMOTE_QUERY)\n\t\t{\n\t\t\tTask *mergeTask = (Task *) fragment->fragmentReference;\n\t\t\tchar *undefinedQueryString = NULL;\n\n\t\t\t/* create merge fetch task and have it depend on the merge task */\n\t\t\tTask *mergeFetchTask = CreateBasicTask(jobId, taskIdIndex, MERGE_FETCH_TASK,\n\t\t\t\t\t\t\t\t\t\t\t\t   undefinedQueryString);\n\t\t\tmergeFetchTask->dependentTaskList = list_make1(mergeTask);\n\n\t\t\tdataFetchTaskList = lappend(dataFetchTaskList, mergeFetchTask);\n\t\t\ttaskIdIndex++;\n\t\t}\n\t}\n\n\treturn dataFetchTaskList;\n}\n\n\n/*\n * CreateBasicTask creates a task, initializes fields that are common to each task,\n * and returns the created task.\n */\nTask *\nCreateBasicTask(uint64 jobId, uint32 taskId, TaskType taskType, char *queryString)\n{\n\tTask *task = CitusMakeNode(Task);\n\ttask->jobId = jobId;\n\ttask->taskId = taskId;\n\ttask->taskType = taskType;\n\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\tSetTaskQueryString(task, queryString);\n\n\treturn task;\n}\n\n\n/*\n * BuildRelationShardList builds a list of RelationShard pairs for a task.\n * This represents the mapping of range table entries to shard IDs for a\n * task for the purposes of locking, deparsing, and connection management.\n */\nstatic List *\nBuildRelationShardList(List *rangeTableList, List *fragmentList)\n{\n\tList *relationShardList = NIL;\n\tListCell *fragmentCell = NULL;\n\n\tforeach(fragmentCell, fragmentList)\n\t{\n\t\tRangeTableFragment *fragment = (RangeTableFragment *) lfirst(fragmentCell);\n\t\tIndex rangeTableId = fragment->rangeTableId;\n\t\tRangeTblEntry *rangeTableEntry = rt_fetch(rangeTableId, rangeTableList);\n\n\t\tCitusRTEKind fragmentType = fragment->fragmentType;\n\t\tif (fragmentType == CITUS_RTE_RELATION)\n\t\t{\n\t\t\tShardInterval *shardInterval = (ShardInterval *) fragment->fragmentReference;\n\t\t\tRelationShard *relationShard = CitusMakeNode(RelationShard);\n\n\t\t\trelationShard->relationId = rangeTableEntry->relid;\n\t\t\trelationShard->shardId = shardInterval->shardId;\n\n\t\t\trelationShardList = lappend(relationShardList, relationShard);\n\t\t}\n\t}\n\n\treturn relationShardList;\n}\n\n\n/*\n * UpdateRangeTableAlias walks over each fragment in the given fragment list,\n * and creates an alias that represents the fragment name to be used in the\n * query. The function then updates the corresponding range table entry with\n * this alias.\n */\nstatic void\nUpdateRangeTableAlias(List *rangeTableList, List *fragmentList)\n{\n\tListCell *fragmentCell = NULL;\n\tforeach(fragmentCell, fragmentList)\n\t{\n\t\tRangeTableFragment *fragment = (RangeTableFragment *) lfirst(fragmentCell);\n\t\tIndex rangeTableId = fragment->rangeTableId;\n\t\tRangeTblEntry *rangeTableEntry = rt_fetch(rangeTableId, rangeTableList);\n\n\t\tAlias *fragmentAlias = FragmentAlias(rangeTableEntry, fragment);\n\t\trangeTableEntry->alias = fragmentAlias;\n\t}\n}\n\n\n/*\n * FragmentAlias creates an alias structure that captures the table fragment's\n * name on the worker node. Each fragment represents either a regular shard, or\n * a merge task.\n */\nstatic Alias *\nFragmentAlias(RangeTblEntry *rangeTableEntry, RangeTableFragment *fragment)\n{\n\tchar *aliasName = NULL;\n\tchar *schemaName = NULL;\n\tchar *fragmentName = NULL;\n\n\tCitusRTEKind fragmentType = fragment->fragmentType;\n\tif (fragmentType == CITUS_RTE_RELATION)\n\t{\n\t\tShardInterval *shardInterval = (ShardInterval *) fragment->fragmentReference;\n\t\tuint64 shardId = shardInterval->shardId;\n\n\t\tOid relationId = rangeTableEntry->relid;\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tOid schemaId = get_rel_namespace(relationId);\n\t\tschemaName = get_namespace_name(schemaId);\n\n\t\taliasName = relationName;\n\n\t\t/*\n\t\t * Set shard name in alias to <relation_name>_<shard_id>.\n\t\t */\n\t\tfragmentName = pstrdup(relationName);\n\t\tAppendShardIdToName(&fragmentName, shardId);\n\t}\n\telse if (fragmentType == CITUS_RTE_REMOTE_QUERY)\n\t{\n\t\tTask *mergeTask = (Task *) fragment->fragmentReference;\n\t\tList *mapOutputFetchTaskList = mergeTask->dependentTaskList;\n\t\tList *resultNameList = FetchTaskResultNameList(mapOutputFetchTaskList);\n\t\tList *mapJobTargetList = mergeTask->mapJobTargetList;\n\n\t\t/* determine whether all types have binary input/output functions */\n\t\tbool useBinaryFormat = CanUseBinaryCopyFormatForTargetList(mapJobTargetList);\n\n\t\t/* generate the query on the intermediate result */\n\t\tQuery *fragmentSetQuery = BuildReadIntermediateResultsArrayQuery(mapJobTargetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t NIL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t resultNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t useBinaryFormat);\n\n\t\t/* we only really care about the function RTE */\n\t\tRangeTblEntry *readIntermediateResultsRTE = linitial(fragmentSetQuery->rtable);\n\n\t\t/* crudely override the fragment RTE */\n\t\t*rangeTableEntry = *readIntermediateResultsRTE;\n\n\t\treturn rangeTableEntry->alias;\n\t}\n\n\t/*\n\t * We need to set the aliasname to relation name, as pg_get_query_def() uses\n\t * the relation name to disambiguate column names from different tables.\n\t */\n\tAlias *alias = rangeTableEntry->alias;\n\tif (alias == NULL)\n\t{\n\t\talias = makeNode(Alias);\n\t\talias->aliasname = aliasName;\n\t}\n\n\tModifyRangeTblExtraData(rangeTableEntry, CITUS_RTE_SHARD,\n\t\t\t\t\t\t\tschemaName, fragmentName, NIL);\n\n\treturn alias;\n}\n\n\n/*\n * FetchTaskResultNameList builds a list of result names that reflect\n * the output of map-fetch tasks.\n */\nstatic List *\nFetchTaskResultNameList(List *mapOutputFetchTaskList)\n{\n\tList *resultNameList = NIL;\n\tTask *mapOutputFetchTask = NULL;\n\n\tforeach_declared_ptr(mapOutputFetchTask, mapOutputFetchTaskList)\n\t{\n\t\tTask *mapTask = linitial(mapOutputFetchTask->dependentTaskList);\n\t\tint partitionId = mapOutputFetchTask->partitionId;\n\t\tchar *resultName =\n\t\t\tPartitionResultName(mapTask->jobId, mapTask->taskId, partitionId);\n\n\t\tresultNameList = lappend(resultNameList, resultName);\n\t}\n\n\treturn resultNameList;\n}\n\n\n/*\n * AnchorShardId walks over each fragment in the given fragment list, finds the\n * fragment that corresponds to the given anchor range tableId, and returns this\n * fragment's shard identifier. Note that the given tableId must correspond to a\n * base relation.\n */\nstatic uint64\nAnchorShardId(List *fragmentList, uint32 anchorRangeTableId)\n{\n\tuint64 anchorShardId = INVALID_SHARD_ID;\n\tListCell *fragmentCell = NULL;\n\n\tforeach(fragmentCell, fragmentList)\n\t{\n\t\tRangeTableFragment *fragment = (RangeTableFragment *) lfirst(fragmentCell);\n\t\tif (fragment->rangeTableId == anchorRangeTableId)\n\t\t{\n\t\t\tAssert(fragment->fragmentType == CITUS_RTE_RELATION);\n\t\t\tAssert(CitusIsA(fragment->fragmentReference, ShardInterval));\n\n\t\t\tShardInterval *shardInterval = (ShardInterval *) fragment->fragmentReference;\n\t\t\tanchorShardId = shardInterval->shardId;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tAssert(anchorShardId != INVALID_SHARD_ID);\n\treturn anchorShardId;\n}\n\n\n/*\n * PruneSqlTaskDependencies iterates over each sql task from the given sql task\n * list, and prunes away merge-fetch tasks, as the task assignment algorithm\n * ensures co-location of these tasks.\n */\nstatic List *\nPruneSqlTaskDependencies(List *sqlTaskList)\n{\n\tListCell *sqlTaskCell = NULL;\n\tforeach(sqlTaskCell, sqlTaskList)\n\t{\n\t\tTask *sqlTask = (Task *) lfirst(sqlTaskCell);\n\t\tList *dependentTaskList = sqlTask->dependentTaskList;\n\t\tList *prunedDependendTaskList = NIL;\n\n\t\tListCell *dependentTaskCell = NULL;\n\t\tforeach(dependentTaskCell, dependentTaskList)\n\t\t{\n\t\t\tTask *dataFetchTask = (Task *) lfirst(dependentTaskCell);\n\n\t\t\t/*\n\t\t\t * If we have a merge fetch task, our task assignment algorithm makes\n\t\t\t * sure that the sql task is colocated with the anchor shard / merge\n\t\t\t * task. We can therefore prune out this data fetch task.\n\t\t\t */\n\t\t\tif (dataFetchTask->taskType == MERGE_FETCH_TASK)\n\t\t\t{\n\t\t\t\tList *mergeFetchDependencyList = dataFetchTask->dependentTaskList;\n\t\t\t\tAssert(list_length(mergeFetchDependencyList) == 1);\n\n\t\t\t\tTask *mergeTaskReference = (Task *) linitial(mergeFetchDependencyList);\n\t\t\t\tprunedDependendTaskList = lappend(prunedDependendTaskList,\n\t\t\t\t\t\t\t\t\t\t\t\t  mergeTaskReference);\n\n\t\t\t\tereport(DEBUG2, (errmsg(\"pruning merge fetch taskId %d\",\n\t\t\t\t\t\t\t\t\t\tdataFetchTask->taskId),\n\t\t\t\t\t\t\t\t errdetail(\"Creating dependency on merge taskId %d\",\n\t\t\t\t\t\t\t\t\t\t   mergeTaskReference->taskId)));\n\t\t\t}\n\t\t}\n\n\t\tsqlTask->dependentTaskList = prunedDependendTaskList;\n\t}\n\n\treturn sqlTaskList;\n}\n\n\n/*\n * MapTaskList creates a list of map tasks for the given MapMerge job. For this,\n * the function walks over each filter task (sql task) in the given filter task\n * list, and wraps this task with a map function call. The map function call\n * repartitions the filter task's output according to MapMerge job's parameters.\n */\nstatic List *\nMapTaskList(MapMergeJob *mapMergeJob, List *filterTaskList)\n{\n\tList *mapTaskList = NIL;\n\tQuery *filterQuery = mapMergeJob->job.jobQuery;\n\tListCell *filterTaskCell = NULL;\n\tVar *partitionColumn = mapMergeJob->partitionColumn;\n\n\tuint32 partitionColumnResNo = 0;\n\tList *groupClauseList = filterQuery->groupClause;\n\tif (groupClauseList != NIL)\n\t{\n\t\tList *targetEntryList = filterQuery->targetList;\n\t\tList *groupTargetEntryList = GroupTargetEntryList(groupClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  targetEntryList);\n\t\tTargetEntry *groupByTargetEntry = (TargetEntry *) linitial(groupTargetEntryList);\n\n\t\tpartitionColumnResNo = groupByTargetEntry->resno;\n\t}\n\telse\n\t{\n\t\tpartitionColumnResNo = PartitionColumnIndex(partitionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\tfilterQuery->targetList);\n\t}\n\n\t/* determine whether all types have binary input/output functions */\n\tbool useBinaryFormat = CanUseBinaryCopyFormatForTargetList(filterQuery->targetList);\n\n\tforeach(filterTaskCell, filterTaskList)\n\t{\n\t\tTask *filterTask = (Task *) lfirst(filterTaskCell);\n\t\tStringInfo mapQueryString = CreateMapQueryString(mapMergeJob, filterTask,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t partitionColumnResNo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t useBinaryFormat);\n\n\t\t/* convert filter query task into map task */\n\t\tTask *mapTask = filterTask;\n\t\tSetTaskQueryString(mapTask, mapQueryString->data);\n\t\tmapTask->taskType = MAP_TASK;\n\n\t\t/*\n\t\t * We do not support fail-over in case of map tasks, since we would also\n\t\t * have to fail over the corresponding merge tasks. We therefore truncate\n\t\t * the list down to the first element.\n\t\t */\n\t\tmapTask->taskPlacementList = list_truncate(mapTask->taskPlacementList, 1);\n\n\t\tmapTaskList = lappend(mapTaskList, mapTask);\n\t}\n\n\treturn mapTaskList;\n}\n\n\n/*\n * PartitionColumnIndex finds the index of the given target var.\n */\nstatic int\nPartitionColumnIndex(Var *targetVar, List *targetList)\n{\n\tTargetEntry *targetEntry = NULL;\n\tint resNo = 1;\n\tforeach_declared_ptr(targetEntry, targetList)\n\t{\n\t\tif (IsA(targetEntry->expr, Var))\n\t\t{\n\t\t\tVar *candidateVar = (Var *) targetEntry->expr;\n\t\t\tif (candidateVar->varattno == targetVar->varattno &&\n\t\t\t\tcandidateVar->varno == targetVar->varno)\n\t\t\t{\n\t\t\t\treturn resNo;\n\t\t\t}\n\t\t\tresNo++;\n\t\t}\n\t}\n\n\tereport(ERROR, (errmsg(\"unexpected state: %d varno %d varattno couldn't be found\",\n\t\t\t\t\t\t   targetVar->varno, targetVar->varattno)));\n\treturn resNo;\n}\n\n\n/*\n * CreateMapQueryString creates and returns the map query string for the given filterTask.\n */\nstatic StringInfo\nCreateMapQueryString(MapMergeJob *mapMergeJob, Task *filterTask,\n\t\t\t\t\t uint32 partitionColumnIndex, bool useBinaryFormat)\n{\n\tuint64 jobId = filterTask->jobId;\n\tuint32 taskId = filterTask->taskId;\n\tchar *resultNamePrefix = PartitionResultNamePrefix(jobId, taskId);\n\n\t/* wrap repartition query string around filter query string */\n\tStringInfo mapQueryString = makeStringInfo();\n\tchar *filterQueryString = TaskQueryString(filterTask);\n\tPartitionType partitionType = mapMergeJob->partitionType;\n\n\tVar *partitionColumn = mapMergeJob->partitionColumn;\n\tOid partitionColumnType = partitionColumn->vartype;\n\n\tShardInterval **intervalArray = mapMergeJob->sortedShardIntervalArray;\n\tuint32 intervalCount = mapMergeJob->partitionCount;\n\n\tif (partitionType == DUAL_HASH_PARTITION_TYPE)\n\t{\n\t\tpartitionColumnType = INT4OID;\n\t\tintervalArray = GenerateSyntheticShardIntervalArray(intervalCount);\n\t}\n\telse if (partitionType == SINGLE_HASH_PARTITION_TYPE)\n\t{\n\t\tpartitionColumnType = INT4OID;\n\t}\n\telse if (partitionType == RANGE_PARTITION_TYPE)\n\t{\n\t\t/* add a partition for NULL values at index 0 */\n\t\tintervalArray = RangeIntervalArrayWithNullBucket(intervalArray, intervalCount);\n\t\tintervalCount++;\n\t}\n\n\tOid intervalTypeOutFunc = InvalidOid;\n\tbool intervalTypeVarlena = false;\n\tArrayType *minValueArray = NULL;\n\tArrayType *maxValueArray = NULL;\n\n\tgetTypeOutputInfo(partitionColumnType, &intervalTypeOutFunc, &intervalTypeVarlena);\n\n\tShardMinMaxValueArrays(intervalArray, intervalCount, intervalTypeOutFunc,\n\t\t\t\t\t\t   &minValueArray, &maxValueArray);\n\n\tStringInfo minValuesString = ArrayObjectToString(minValueArray, TEXTOID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t InvalidOid);\n\tStringInfo maxValuesString = ArrayObjectToString(maxValueArray, TEXTOID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t InvalidOid);\n\n\tchar *partitionMethodString = partitionType == RANGE_PARTITION_TYPE ?\n\t\t\t\t\t\t\t\t  \"range\" : \"hash\";\n\n\t/*\n\t * Non-partition columns can easily contain NULL values, so we allow NULL\n\t * values in the column by which we re-partition. They will end up in the\n\t * first partition.\n\t */\n\tbool allowNullPartitionColumnValue = true;\n\n\t/*\n\t * We currently generate empty results for each partition and fetch all of them.\n\t */\n\tbool generateEmptyResults = true;\n\n\tappendStringInfo(mapQueryString,\n\t\t\t\t\t \"SELECT partition_index\"\n\t\t\t\t\t \", %s || '_' || partition_index::text \"\n\t\t\t\t\t \", rows_written \"\n\t\t\t\t\t \"FROM pg_catalog.worker_partition_query_result\"\n\t\t\t\t\t \"(%s,%s,%d,%s,%s,%s,%s,%s,%s) WHERE rows_written > 0\",\n\t\t\t\t\t quote_literal_cstr(resultNamePrefix),\n\t\t\t\t\t quote_literal_cstr(resultNamePrefix),\n\t\t\t\t\t quote_literal_cstr(filterQueryString),\n\t\t\t\t\t partitionColumnIndex - 1,\n\t\t\t\t\t quote_literal_cstr(partitionMethodString),\n\t\t\t\t\t minValuesString->data,\n\t\t\t\t\t maxValuesString->data,\n\t\t\t\t\t useBinaryFormat ? \"true\" : \"false\",\n\t\t\t\t\t allowNullPartitionColumnValue ? \"true\" : \"false\",\n\t\t\t\t\t generateEmptyResults ? \"true\" : \"false\");\n\n\treturn mapQueryString;\n}\n\n\n/*\n * PartitionResultNamePrefix returns the prefix we use for worker_partition_query_result\n * results. Each result will have a _<partition index> suffix.\n */\nstatic char *\nPartitionResultNamePrefix(uint64 jobId, int32 taskId)\n{\n\tStringInfo resultNamePrefix = makeStringInfo();\n\n\tappendStringInfo(resultNamePrefix, \"repartition_\" UINT64_FORMAT \"_%u\", jobId, taskId);\n\n\treturn resultNamePrefix->data;\n}\n\n\n/*\n * PartitionResultName returns the name of a worker_partition_query_result result for\n * a specific partition.\n */\nstatic char *\nPartitionResultName(uint64 jobId, uint32 taskId, uint32 partitionId)\n{\n\tStringInfo resultName = makeStringInfo();\n\tchar *resultNamePrefix = PartitionResultNamePrefix(jobId, taskId);\n\n\tappendStringInfo(resultName, \"%s_%d\", resultNamePrefix, partitionId);\n\n\treturn resultName->data;\n}\n\n\n/*\n * GenerateSyntheticShardIntervalArray returns a shard interval pointer array\n * which has a uniform hash distribution for the given input partitionCount.\n *\n * The function only fills the min/max values of shard the intervals. Thus, should\n * not be used for general purpose operations.\n */\nShardInterval **\nGenerateSyntheticShardIntervalArray(int partitionCount)\n{\n\tShardInterval **shardIntervalArray = palloc0(partitionCount *\n\t\t\t\t\t\t\t\t\t\t\t\t sizeof(ShardInterval *));\n\tuint64 hashTokenIncrement = HASH_TOKEN_COUNT / partitionCount;\n\n\tfor (int shardIndex = 0; shardIndex < partitionCount; ++shardIndex)\n\t{\n\t\tShardInterval *shardInterval = CitusMakeNode(ShardInterval);\n\n\t\t/* calculate the split of the hash space */\n\t\tint32 shardMinHashToken = PG_INT32_MIN + (shardIndex * hashTokenIncrement);\n\t\tint32 shardMaxHashToken = shardMinHashToken + (hashTokenIncrement - 1);\n\n\t\t/* extend the last range to cover the full range of integers */\n\t\tif (shardIndex == (partitionCount - 1))\n\t\t{\n\t\t\tshardMaxHashToken = PG_INT32_MAX;\n\t\t}\n\n\t\tshardInterval->relationId = InvalidOid;\n\t\tshardInterval->minValueExists = true;\n\t\tshardInterval->minValue = Int32GetDatum(shardMinHashToken);\n\n\t\tshardInterval->maxValueExists = true;\n\t\tshardInterval->maxValue = Int32GetDatum(shardMaxHashToken);\n\n\t\tshardInterval->shardId = INVALID_SHARD_ID;\n\t\tshardInterval->valueTypeId = INT4OID;\n\n\t\tshardIntervalArray[shardIndex] = shardInterval;\n\t}\n\n\treturn shardIntervalArray;\n}\n\n\n/*\n * RangeIntervalArrayWithNullBucket prepends an additional bucket for NULL values\n * to intervalArray and returns the result.\n *\n * When we support NULL values in (range-partitioned) shards, we will need to revise\n * this logic, since there may already be an interval for NULL values.\n */\nstatic ShardInterval **\nRangeIntervalArrayWithNullBucket(ShardInterval **intervalArray, int intervalCount)\n{\n\tint fullIntervalCount = intervalCount + 1;\n\tShardInterval **fullIntervalArray =\n\t\tpalloc0(fullIntervalCount * sizeof(ShardInterval *));\n\n\tfullIntervalArray[0] = CitusMakeNode(ShardInterval);\n\tfullIntervalArray[0]->minValueExists = true;\n\tfullIntervalArray[0]->maxValueExists = true;\n\tfullIntervalArray[0]->valueTypeId = intervalArray[0]->valueTypeId;\n\n\tfor (int intervalIndex = 1; intervalIndex < fullIntervalCount; intervalIndex++)\n\t{\n\t\tfullIntervalArray[intervalIndex] = intervalArray[intervalIndex - 1];\n\t}\n\n\treturn fullIntervalArray;\n}\n\n\n/*\n * Determine RowModifyLevel required for given query\n */\nRowModifyLevel\nRowModifyLevelForQuery(Query *query)\n{\n\tCmdType commandType = query->commandType;\n\n\tif (commandType == CMD_SELECT)\n\t{\n\t\tif (query->hasModifyingCTE)\n\t\t{\n\t\t\t/* skip checking for INSERT as those CTEs are recursively planned */\n\t\t\tCommonTableExpr *cte = NULL;\n\t\t\tforeach_declared_ptr(cte, query->cteList)\n\t\t\t{\n\t\t\t\tQuery *cteQuery = (Query *) cte->ctequery;\n\n\t\t\t\tif (cteQuery->commandType == CMD_UPDATE ||\n\t\t\t\t\tcteQuery->commandType == CMD_DELETE)\n\t\t\t\t{\n\t\t\t\t\treturn ROW_MODIFY_NONCOMMUTATIVE;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ROW_MODIFY_READONLY;\n\t}\n\n\tif (commandType == CMD_INSERT)\n\t{\n\t\tif (query->onConflict == NULL)\n\t\t{\n\t\t\treturn ROW_MODIFY_COMMUTATIVE;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn ROW_MODIFY_NONCOMMUTATIVE;\n\t\t}\n\t}\n\n\tif (commandType == CMD_UPDATE ||\n\t\tcommandType == CMD_DELETE ||\n\t\tcommandType == CMD_MERGE)\n\t{\n\t\treturn ROW_MODIFY_NONCOMMUTATIVE;\n\t}\n\n\treturn ROW_MODIFY_NONE;\n}\n\n\n/*\n * ArrayObjectToString converts an SQL object to its string representation.\n */\nStringInfo\nArrayObjectToString(ArrayType *arrayObject, Oid columnType, int32 columnTypeMod)\n{\n\tDatum arrayDatum = PointerGetDatum(arrayObject);\n\tOid outputFunctionId = InvalidOid;\n\tbool typeVariableLength = false;\n\n\tOid arrayOutType = get_array_type(columnType);\n\tif (arrayOutType == InvalidOid)\n\t{\n\t\tchar *columnTypeName = format_type_be(columnType);\n\t\tereport(ERROR, (errmsg(\"cannot range repartition table on column type %s\",\n\t\t\t\t\t\t\t   columnTypeName)));\n\t}\n\n\tFmgrInfo *arrayOutFunction = (FmgrInfo *) palloc0(sizeof(FmgrInfo));\n\tgetTypeOutputInfo(arrayOutType, &outputFunctionId, &typeVariableLength);\n\tfmgr_info(outputFunctionId, arrayOutFunction);\n\n\tchar *arrayOutputText = OutputFunctionCall(arrayOutFunction, arrayDatum);\n\tchar *arrayOutputEscapedText = quote_literal_cstr(arrayOutputText);\n\n\t/* add an explicit cast to array's string representation */\n\tchar *arrayOutTypeName = format_type_be(arrayOutType);\n\n\tStringInfo arrayString = makeStringInfo();\n\tappendStringInfo(arrayString, \"%s::%s\",\n\t\t\t\t\t arrayOutputEscapedText, arrayOutTypeName);\n\n\treturn arrayString;\n}\n\n\n/*\n * MergeTaskList creates a list of merge tasks for the given MapMerge job. While\n * doing this, the function also establishes dependencies between each merge\n * task and its downstream map task dependencies by creating \"map fetch\" tasks.\n */\nstatic List *\nMergeTaskList(MapMergeJob *mapMergeJob, List *mapTaskList, uint32 taskIdIndex)\n{\n\tList *mergeTaskList = NIL;\n\tuint64 jobId = mapMergeJob->job.jobId;\n\tuint32 partitionCount = mapMergeJob->partitionCount;\n\n\t/* build column name and column type arrays (table schema) */\n\tQuery *filterQuery = mapMergeJob->job.jobQuery;\n\tList *targetEntryList = filterQuery->targetList;\n\n\t/* if all map tasks were pruned away, return NIL for merge tasks */\n\tif (mapTaskList == NIL)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * XXX: We currently ignore the 0th partition bucket that range partitioning\n\t * generates. This bucket holds all values less than the minimum value or\n\t * NULLs, both of which we can currently ignore. However, when we support\n\t * range re-partitioned OUTER joins, we will need these rows for the\n\t * relation whose rows are retained in the OUTER join.\n\t */\n\tuint32 initialPartitionId = 0;\n\tif (mapMergeJob->partitionType == RANGE_PARTITION_TYPE)\n\t{\n\t\tinitialPartitionId = 1;\n\t\tpartitionCount = partitionCount + 1;\n\t}\n\telse if (mapMergeJob->partitionType == SINGLE_HASH_PARTITION_TYPE)\n\t{\n\t\tinitialPartitionId = 0;\n\t}\n\n\t/* build merge tasks and their associated \"map output fetch\" tasks */\n\tfor (uint32 partitionId = initialPartitionId; partitionId < partitionCount;\n\t\t partitionId++)\n\t{\n\t\tList *mapOutputFetchTaskList = NIL;\n\t\tListCell *mapTaskCell = NULL;\n\t\tuint32 mergeTaskId = taskIdIndex;\n\n\t\t/* create logical merge task (not executed, but useful for bookkeeping) */\n\t\tTask *mergeTask = CreateBasicTask(jobId, mergeTaskId, MERGE_TASK,\n\t\t\t\t\t\t\t\t\t\t  \"<merge>\");\n\t\tmergeTask->partitionId = partitionId;\n\t\ttaskIdIndex++;\n\n\t\t/* create tasks to fetch map outputs to this merge task */\n\t\tforeach(mapTaskCell, mapTaskList)\n\t\t{\n\t\t\tTask *mapTask = (Task *) lfirst(mapTaskCell);\n\n\t\t\t/* find the node name/port for map task's execution */\n\t\t\tList *mapTaskPlacementList = mapTask->taskPlacementList;\n\t\t\tShardPlacement *mapTaskPlacement = linitial(mapTaskPlacementList);\n\n\t\t\tchar *partitionResultName =\n\t\t\t\tPartitionResultName(jobId, mapTask->taskId, partitionId);\n\n\t\t\t/* we currently only fetch a single fragment at a time */\n\t\t\tDistributedResultFragment singleFragmentTransfer;\n\t\t\tsingleFragmentTransfer.resultId = partitionResultName;\n\t\t\tsingleFragmentTransfer.nodeId = mapTaskPlacement->nodeId;\n\t\t\tsingleFragmentTransfer.rowCount = 0;\n\t\t\tsingleFragmentTransfer.targetShardId = INVALID_SHARD_ID;\n\t\t\tsingleFragmentTransfer.targetShardIndex = partitionId;\n\n\t\t\tNodeToNodeFragmentsTransfer fragmentsTransfer;\n\t\t\tfragmentsTransfer.nodes.sourceNodeId = mapTaskPlacement->nodeId;\n\n\t\t\t/*\n\t\t\t * Target node is not yet decided, and not necessary for\n\t\t\t * QueryStringForFragmentsTransfer.\n\t\t\t */\n\t\t\tfragmentsTransfer.nodes.targetNodeId = -1;\n\n\t\t\tfragmentsTransfer.fragmentList = list_make1(&singleFragmentTransfer);\n\n\t\t\tchar *fetchQueryString = QueryStringForFragmentsTransfer(&fragmentsTransfer);\n\n\t\t\tTask *mapOutputFetchTask = CreateBasicTask(jobId, taskIdIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   MAP_OUTPUT_FETCH_TASK,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   fetchQueryString);\n\t\t\tmapOutputFetchTask->partitionId = partitionId;\n\t\t\tmapOutputFetchTask->upstreamTaskId = mergeTaskId;\n\t\t\tmapOutputFetchTask->dependentTaskList = list_make1(mapTask);\n\t\t\ttaskIdIndex++;\n\n\t\t\tmapOutputFetchTaskList = lappend(mapOutputFetchTaskList, mapOutputFetchTask);\n\t\t}\n\n\t\t/* merge task depends on completion of fetch tasks */\n\t\tmergeTask->dependentTaskList = mapOutputFetchTaskList;\n\t\tmergeTask->mapJobTargetList = targetEntryList;\n\n\t\t/* if single repartitioned, each merge task represents an interval */\n\t\tif (mapMergeJob->partitionType == RANGE_PARTITION_TYPE)\n\t\t{\n\t\t\tint32 mergeTaskIntervalId = partitionId - 1;\n\t\t\tShardInterval **mergeTaskIntervals = mapMergeJob->sortedShardIntervalArray;\n\t\t\tAssert(mergeTaskIntervalId >= 0);\n\n\t\t\tmergeTask->shardInterval = mergeTaskIntervals[mergeTaskIntervalId];\n\t\t}\n\t\telse if (mapMergeJob->partitionType == SINGLE_HASH_PARTITION_TYPE)\n\t\t{\n\t\t\tint32 mergeTaskIntervalId = partitionId;\n\t\t\tShardInterval **mergeTaskIntervals = mapMergeJob->sortedShardIntervalArray;\n\t\t\tAssert(mergeTaskIntervalId >= 0);\n\n\t\t\tmergeTask->shardInterval = mergeTaskIntervals[mergeTaskIntervalId];\n\t\t}\n\n\t\tmergeTaskList = lappend(mergeTaskList, mergeTask);\n\t}\n\n\treturn mergeTaskList;\n}\n\n\n/*\n * AssignTaskList assigns locations to given tasks based on dependencies between\n * tasks and configured task assignment policies. The function also handles the\n * case where multiple SQL tasks depend on the same merge task, and makes sure\n * that this group of multiple SQL tasks and the merge task are assigned to the\n * same location.\n */\nstatic List *\nAssignTaskList(List *sqlTaskList)\n{\n\tList *assignedSqlTaskList = NIL;\n\tbool hasAnchorShardId = false;\n\tListCell *sqlTaskCell = NULL;\n\tList *primarySqlTaskList = NIL;\n\tListCell *primarySqlTaskCell = NULL;\n\tListCell *constrainedSqlTaskCell = NULL;\n\n\t/* no tasks to assign */\n\tif (sqlTaskList == NIL)\n\t{\n\t\treturn NIL;\n\t}\n\n\tTask *firstSqlTask = (Task *) linitial(sqlTaskList);\n\tif (firstSqlTask->anchorShardId != INVALID_SHARD_ID)\n\t{\n\t\thasAnchorShardId = true;\n\t}\n\n\t/*\n\t * If these SQL tasks don't depend on any merge tasks, we can assign each\n\t * one independently of the other. We therefore go ahead and assign these\n\t * SQL tasks using the \"anchor shard based\" assignment algorithms.\n\t */\n\tbool hasMergeTaskDependencies = HasMergeTaskDependencies(sqlTaskList);\n\tif (!hasMergeTaskDependencies)\n\t{\n\t\tAssert(hasAnchorShardId);\n\n\t\tassignedSqlTaskList = AssignAnchorShardTaskList(sqlTaskList);\n\n\t\treturn assignedSqlTaskList;\n\t}\n\n\t/*\n\t * SQL tasks can depend on merge tasks in one of two ways: (1) each SQL task\n\t * depends on merge task(s) that no other SQL task depends upon, (2) several\n\t * SQL tasks depend on the same merge task(s) and all need to be assigned to\n\t * the same worker node. To handle the second case, we first pick a primary\n\t * SQL task among those that depend on the same merge task, and assign it.\n\t */\n\tforeach(sqlTaskCell, sqlTaskList)\n\t{\n\t\tTask *sqlTask = (Task *) lfirst(sqlTaskCell);\n\t\tList *mergeTaskList = FindDependentMergeTaskList(sqlTask);\n\n\t\tTask *firstMergeTask = (Task *) linitial(mergeTaskList);\n\t\tif (!firstMergeTask->assignmentConstrained)\n\t\t{\n\t\t\tfirstMergeTask->assignmentConstrained = true;\n\n\t\t\tprimarySqlTaskList = lappend(primarySqlTaskList, sqlTask);\n\t\t}\n\t}\n\n\tif (hasAnchorShardId)\n\t{\n\t\tprimarySqlTaskList = AssignAnchorShardTaskList(primarySqlTaskList);\n\t}\n\telse\n\t{\n\t\tprimarySqlTaskList = AssignDualHashTaskList(primarySqlTaskList);\n\t}\n\n\t/* propagate SQL task assignments to the merge tasks we depend upon */\n\tforeach(primarySqlTaskCell, primarySqlTaskList)\n\t{\n\t\tTask *sqlTask = (Task *) lfirst(primarySqlTaskCell);\n\t\tList *mergeTaskList = FindDependentMergeTaskList(sqlTask);\n\n\t\tListCell *mergeTaskCell = NULL;\n\t\tforeach(mergeTaskCell, mergeTaskList)\n\t\t{\n\t\t\tTask *mergeTask = (Task *) lfirst(mergeTaskCell);\n\t\t\tAssert(mergeTask->taskPlacementList == NIL);\n\n\t\t\tmergeTask->taskPlacementList = list_copy(sqlTask->taskPlacementList);\n\t\t}\n\n\t\tassignedSqlTaskList = lappend(assignedSqlTaskList, sqlTask);\n\t}\n\n\t/*\n\t * If we had a set of SQL tasks depending on the same merge task, we only\n\t * assigned one SQL task from that set. We call the assigned SQL task the\n\t * primary, and note that the remaining SQL tasks are constrained by the\n\t * primary's task assignment. We propagate the primary's task assignment in\n\t * each set to the remaining (constrained) tasks.\n\t */\n\tList *constrainedSqlTaskList = TaskListDifference(sqlTaskList, primarySqlTaskList);\n\n\tforeach(constrainedSqlTaskCell, constrainedSqlTaskList)\n\t{\n\t\tTask *sqlTask = (Task *) lfirst(constrainedSqlTaskCell);\n\t\tList *mergeTaskList = FindDependentMergeTaskList(sqlTask);\n\t\tList *mergeTaskPlacementList = NIL;\n\n\t\tListCell *mergeTaskCell = NULL;\n\t\tforeach(mergeTaskCell, mergeTaskList)\n\t\t{\n\t\t\tTask *mergeTask = (Task *) lfirst(mergeTaskCell);\n\n\t\t\t/*\n\t\t\t * If we have more than one merge task, both of them should have the\n\t\t\t * same task placement list.\n\t\t\t */\n\t\t\tmergeTaskPlacementList = mergeTask->taskPlacementList;\n\t\t\tAssert(mergeTaskPlacementList != NIL);\n\n\t\t\tereport(DEBUG3, (errmsg(\"propagating assignment from merge task %d \"\n\t\t\t\t\t\t\t\t\t\"to constrained sql task %d\",\n\t\t\t\t\t\t\t\t\tmergeTask->taskId, sqlTask->taskId)));\n\t\t}\n\n\t\tsqlTask->taskPlacementList = list_copy(mergeTaskPlacementList);\n\n\t\tassignedSqlTaskList = lappend(assignedSqlTaskList, sqlTask);\n\t}\n\n\treturn assignedSqlTaskList;\n}\n\n\n/*\n * HasMergeTaskDependencies checks if sql tasks in the given sql task list have\n * any dependencies on merge tasks. If they do, the function returns true.\n */\nstatic bool\nHasMergeTaskDependencies(List *sqlTaskList)\n{\n\tbool hasMergeTaskDependencies = false;\n\tTask *sqlTask = (Task *) linitial(sqlTaskList);\n\tList *dependentTaskList = sqlTask->dependentTaskList;\n\n\tListCell *dependentTaskCell = NULL;\n\tforeach(dependentTaskCell, dependentTaskList)\n\t{\n\t\tTask *dependentTask = (Task *) lfirst(dependentTaskCell);\n\t\tif (dependentTask->taskType == MERGE_TASK)\n\t\t{\n\t\t\thasMergeTaskDependencies = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn hasMergeTaskDependencies;\n}\n\n\n/* Return true if two tasks are equal, false otherwise. */\nbool\nTasksEqual(const Task *a, const Task *b)\n{\n\tAssert(CitusIsA(a, Task));\n\tAssert(CitusIsA(b, Task));\n\n\tif (a->taskType != b->taskType)\n\t{\n\t\treturn false;\n\t}\n\tif (a->jobId != b->jobId)\n\t{\n\t\treturn false;\n\t}\n\tif (a->taskId != b->taskId)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/* Is the passed in Task a member of the list. */\nbool\nTaskListMember(const List *taskList, const Task *task)\n{\n\tconst ListCell *taskCell = NULL;\n\n\tforeach(taskCell, taskList)\n\t{\n\t\tif (TasksEqual((Task *) lfirst(taskCell), task))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * TaskListDifference returns a list that contains all the tasks in taskList1\n * that are not in taskList2. The returned list is freshly allocated via\n * palloc(), but the cells themselves point to the same objects as the cells\n * of the input lists.\n */\nList *\nTaskListDifference(const List *list1, const List *list2)\n{\n\tconst ListCell *taskCell = NULL;\n\tList *resultList = NIL;\n\n\tif (list2 == NIL)\n\t{\n\t\treturn list_copy(list1);\n\t}\n\n\tforeach(taskCell, list1)\n\t{\n\t\tif (!TaskListMember(list2, lfirst(taskCell)))\n\t\t{\n\t\t\tresultList = lappend(resultList, lfirst(taskCell));\n\t\t}\n\t}\n\n\treturn resultList;\n}\n\n\n/*\n * AssignAnchorShardTaskList assigns locations to the given tasks based on the\n * configured task assignment policy. The distributed executor later sends these\n * tasks to their assigned locations for remote execution.\n */\nList *\nAssignAnchorShardTaskList(List *taskList)\n{\n\tList *assignedTaskList = NIL;\n\n\t/* choose task assignment policy based on config value */\n\tif (TaskAssignmentPolicy == TASK_ASSIGNMENT_GREEDY)\n\t{\n\t\tassignedTaskList = GreedyAssignTaskList(taskList);\n\t}\n\telse if (TaskAssignmentPolicy == TASK_ASSIGNMENT_FIRST_REPLICA)\n\t{\n\t\tassignedTaskList = FirstReplicaAssignTaskList(taskList);\n\t}\n\telse if (TaskAssignmentPolicy == TASK_ASSIGNMENT_ROUND_ROBIN)\n\t{\n\t\tassignedTaskList = RoundRobinAssignTaskList(taskList);\n\t}\n\n\tAssert(assignedTaskList != NIL);\n\treturn assignedTaskList;\n}\n\n\n/*\n * GreedyAssignTaskList uses a greedy algorithm similar to Hadoop's, and assigns\n * locations to the given tasks. The ideal assignment algorithm balances three\n * properties: (a) determinism, (b) even load distribution, and (c) consistency\n * across similar task lists. To maintain these properties, the algorithm sorts\n * all its input lists.\n */\nstatic List *\nGreedyAssignTaskList(List *taskList)\n{\n\tList *assignedTaskList = NIL;\n\tuint32 assignedTaskCount = 0;\n\tuint32 taskCount = list_length(taskList);\n\n\t/* get the worker node list and sort the list */\n\tList *workerNodeList = ActiveReadableNodeList();\n\tworkerNodeList = SortList(workerNodeList, CompareWorkerNodes);\n\n\t/*\n\t * We first sort tasks by their anchor shard id. We then walk over each task\n\t * in the sorted list, get the task's anchor shard id, and look up the shard\n\t * placements (locations) for this shard id. Next, we sort the placements by\n\t * their insertion time, and append them to a new list.\n\t */\n\ttaskList = SortList(taskList, CompareTasksByShardId);\n\tList *activeShardPlacementLists = ActiveShardPlacementLists(taskList);\n\n\twhile (assignedTaskCount < taskCount)\n\t{\n\t\tListCell *workerNodeCell = NULL;\n\t\tuint32 loopStartTaskCount = assignedTaskCount;\n\n\t\t/* walk over each node and check if we can assign a task to it */\n\t\tforeach(workerNodeCell, workerNodeList)\n\t\t{\n\t\t\tWorkerNode *workerNode = (WorkerNode *) lfirst(workerNodeCell);\n\n\t\t\tTask *assignedTask = GreedyAssignTask(workerNode, taskList,\n\t\t\t\t\t\t\t\t\t\t\t\t  activeShardPlacementLists);\n\t\t\tif (assignedTask != NULL)\n\t\t\t{\n\t\t\t\tassignedTaskList = lappend(assignedTaskList, assignedTask);\n\t\t\t\tassignedTaskCount++;\n\t\t\t}\n\t\t}\n\n\t\t/* if we could not assign any new tasks, avoid looping forever */\n\t\tif (assignedTaskCount == loopStartTaskCount)\n\t\t{\n\t\t\tuint32 remainingTaskCount = taskCount - assignedTaskCount;\n\t\t\tereport(ERROR, (errmsg(\"failed to assign %u task(s) to worker nodes\",\n\t\t\t\t\t\t\t\t   remainingTaskCount)));\n\t\t}\n\t}\n\n\treturn assignedTaskList;\n}\n\n\n/*\n * GreedyAssignTask tries to assign a task to the given worker node. To do this,\n * the function walks over tasks' anchor shard ids, and finds the first set of\n * nodes the shards were replicated to. If any of these replica nodes and the\n * given worker node match, the corresponding task is assigned to that node. If\n * not, the function goes on to search the second set of replicas and so forth.\n *\n * Note that this function has side-effects; when the function assigns a new\n * task, it overwrites the corresponding task list pointer.\n */\nstatic Task *\nGreedyAssignTask(WorkerNode *workerNode, List *taskList, List *activeShardPlacementLists)\n{\n\tTask *assignedTask = NULL;\n\tList *taskPlacementList = NIL;\n\tShardPlacement *primaryPlacement = NULL;\n\tuint32 rotatePlacementListBy = 0;\n\tuint32 replicaIndex = 0;\n\tuint32 replicaCount = ShardReplicationFactor;\n\tconst char *workerName = workerNode->workerName;\n\tconst uint32 workerPort = workerNode->workerPort;\n\n\twhile ((assignedTask == NULL) && (replicaIndex < replicaCount))\n\t{\n\t\t/* walk over all tasks and try to assign one */\n\t\tListCell *taskCell = NULL;\n\t\tListCell *placementListCell = NULL;\n\n\t\tforboth(taskCell, taskList, placementListCell, activeShardPlacementLists)\n\t\t{\n\t\t\tTask *task = (Task *) lfirst(taskCell);\n\t\t\tList *placementList = (List *) lfirst(placementListCell);\n\n\t\t\t/* check if we already assigned this task */\n\t\t\tif (task == NULL)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* check if we have enough replicas */\n\t\t\tuint32 placementCount = list_length(placementList);\n\t\t\tif (placementCount <= replicaIndex)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tShardPlacement *placement = (ShardPlacement *) list_nth(placementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treplicaIndex);\n\t\t\tif ((strncmp(placement->nodeName, workerName, WORKER_LENGTH) == 0) &&\n\t\t\t\t(placement->nodePort == workerPort))\n\t\t\t{\n\t\t\t\t/* we found a task to assign to the given worker node */\n\t\t\t\tassignedTask = task;\n\t\t\t\ttaskPlacementList = placementList;\n\t\t\t\trotatePlacementListBy = replicaIndex;\n\n\t\t\t\t/* overwrite task list to signal that this task is assigned */\n\t\t\t\tSetListCellPtr(taskCell, NULL);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/* go over the next set of shard replica placements */\n\t\treplicaIndex++;\n\t}\n\n\t/* if we found a task placement list, rotate and assign task placements */\n\tif (assignedTask != NULL)\n\t{\n\t\ttaskPlacementList = LeftRotateList(taskPlacementList, rotatePlacementListBy);\n\t\tassignedTask->taskPlacementList = taskPlacementList;\n\n\t\tprimaryPlacement = (ShardPlacement *) linitial(assignedTask->taskPlacementList);\n\t\tereport(DEBUG3, (errmsg(\"assigned task %u to node %s:%u\", assignedTask->taskId,\n\t\t\t\t\t\t\t\tprimaryPlacement->nodeName,\n\t\t\t\t\t\t\t\tprimaryPlacement->nodePort)));\n\t}\n\n\treturn assignedTask;\n}\n\n\n/*\n * FirstReplicaAssignTaskList assigns locations to the given tasks simply by\n * looking at placements for a given shard. A particular task's assignments are\n * then ordered by the insertion order of the relevant placements rows. In other\n * words, a task for a specific shard is simply assigned to the first replica\n * for that shard. This algorithm is extremely simple and intended for use when\n * a customer has placed shards carefully and wants strong guarantees about\n * which shards will be used by what nodes (i.e. for stronger memory residency\n * guarantees).\n */\nList *\nFirstReplicaAssignTaskList(List *taskList)\n{\n\t/* No additional reordering need take place for this algorithm */\n\tReorderFunction reorderFunction = NULL;\n\n\ttaskList = ReorderAndAssignTaskList(taskList, reorderFunction);\n\n\treturn taskList;\n}\n\n\n/*\n * RoundRobinAssignTaskList uses a round-robin algorithm to assign locations to\n * the given tasks. An ideal round-robin implementation requires keeping shared\n * state for task assignments; and we instead approximate our implementation by\n * relying on the sequentially increasing jobId. For each task, we mod its jobId\n * by the number of active shard placements, and ensure that we rotate between\n * these placements across subsequent queries.\n */\nList *\nRoundRobinAssignTaskList(List *taskList)\n{\n\ttaskList = ReorderAndAssignTaskList(taskList, RoundRobinReorder);\n\n\treturn taskList;\n}\n\n\n/*\n * RoundRobinReorder implements the core of the round-robin assignment policy.\n * It takes a placement list and rotates a copy of it based on the latest stable\n * transaction id provided by PostgreSQL.\n *\n * We prefer to use transactionId as the seed for the rotation to use the replicas\n * in the same worker node within the same transaction. This becomes more important\n * when we're reading from (the same or multiple) reference tables within a\n * transaction. With this approach, we can prevent reads to expand the worker nodes\n * that participate in a distributed transaction.\n *\n * Note that we prefer PostgreSQL's transactionId over distributed transactionId that\n * Citus generates since the distributed transactionId is generated during the execution\n * where as task-assignment happens duing the planning.\n */\nList *\nRoundRobinReorder(List *placementList)\n{\n\tTransactionId transactionId = GetMyProcLocalTransactionId();\n\tuint32 activePlacementCount = list_length(placementList);\n\tuint32 roundRobinIndex = (transactionId % activePlacementCount);\n\n\tplacementList = LeftRotateList(placementList, roundRobinIndex);\n\n\treturn placementList;\n}\n\n\n/*\n * ReorderAndAssignTaskList finds the placements for a task based on its anchor\n * shard id and then sorts them by insertion time. If reorderFunction is given,\n * it is used to reorder the placements list in a custom fashion (for instance,\n * by rotation or shuffling). Returns the task list with placements assigned.\n */\nstatic List *\nReorderAndAssignTaskList(List *taskList, ReorderFunction reorderFunction)\n{\n\tList *assignedTaskList = NIL;\n\tListCell *taskCell = NULL;\n\tListCell *placementListCell = NULL;\n\tuint32 unAssignedTaskCount = 0;\n\n\tif (taskList == NIL)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * We first sort tasks by their anchor shard id. We then sort placements for\n\t * each anchor shard by the placement's insertion time. Note that we sort\n\t * these lists just to make our policy more deterministic.\n\t */\n\ttaskList = SortList(taskList, CompareTasksByShardId);\n\tList *activeShardPlacementLists = ActiveShardPlacementLists(taskList);\n\n\tforboth(taskCell, taskList, placementListCell, activeShardPlacementLists)\n\t{\n\t\tTask *task = (Task *) lfirst(taskCell);\n\t\tList *placementList = (List *) lfirst(placementListCell);\n\n\t\t/* inactive placements are already filtered out */\n\t\tuint32 activePlacementCount = list_length(placementList);\n\t\tif (activePlacementCount > 0)\n\t\t{\n\t\t\tif (reorderFunction != NULL)\n\t\t\t{\n\t\t\t\tplacementList = reorderFunction(placementList);\n\t\t\t}\n\t\t\ttask->taskPlacementList = placementList;\n\n\t\t\tShardPlacement *primaryPlacement = (ShardPlacement *) linitial(\n\t\t\t\ttask->taskPlacementList);\n\t\t\tereport(DEBUG3, (errmsg(\"assigned task %u to node %s:%u\", task->taskId,\n\t\t\t\t\t\t\t\t\tprimaryPlacement->nodeName,\n\t\t\t\t\t\t\t\t\tprimaryPlacement->nodePort)));\n\n\t\t\tassignedTaskList = lappend(assignedTaskList, task);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tunAssignedTaskCount++;\n\t\t}\n\t}\n\n\t/* if we have unassigned tasks, error out */\n\tif (unAssignedTaskCount > 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"failed to assign %u task(s) to worker nodes\",\n\t\t\t\t\t\t\t   unAssignedTaskCount)));\n\t}\n\n\treturn assignedTaskList;\n}\n\n\n/* Helper function to compare two tasks by their anchor shardId. */\nstatic int\nCompareTasksByShardId(const void *leftElement, const void *rightElement)\n{\n\tconst Task *leftTask = *((const Task **) leftElement);\n\tconst Task *rightTask = *((const Task **) rightElement);\n\n\tuint64 leftShardId = leftTask->anchorShardId;\n\tuint64 rightShardId = rightTask->anchorShardId;\n\n\t/* we compare 64-bit integers, instead of casting their difference to int */\n\tif (leftShardId > rightShardId)\n\t{\n\t\treturn 1;\n\t}\n\telse if (leftShardId < rightShardId)\n\t{\n\t\treturn -1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * ActiveShardPlacementLists finds the active shard placement list for each task in\n * the given task list, sorts each shard placement list by shard creation time,\n * and adds the sorted placement list into a new list of lists. The function also\n * ensures a one-to-one mapping between each placement list in the new list of\n * lists and each task in the given task list.\n */\nstatic List *\nActiveShardPlacementLists(List *taskList)\n{\n\tList *shardPlacementLists = NIL;\n\tListCell *taskCell = NULL;\n\n\tforeach(taskCell, taskList)\n\t{\n\t\tTask *task = (Task *) lfirst(taskCell);\n\t\tuint64 anchorShardId = task->anchorShardId;\n\t\tList *activeShardPlacementList = ActiveShardPlacementList(anchorShardId);\n\n\t\tif (activeShardPlacementList == NIL)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errmsg(\"no active placements were found for shard \" UINT64_FORMAT,\n\t\t\t\t\t\t\tanchorShardId)));\n\t\t}\n\n\t\t/* sort shard placements by their creation time */\n\t\tactiveShardPlacementList = SortList(activeShardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\tCompareShardPlacements);\n\n\t\tshardPlacementLists = lappend(shardPlacementLists, activeShardPlacementList);\n\t}\n\n\treturn shardPlacementLists;\n}\n\n\n/*\n * CompareShardPlacements compares two shard placements by placement id.\n */\nint\nCompareShardPlacements(const void *leftElement, const void *rightElement)\n{\n\tconst ShardPlacement *leftPlacement = *((const ShardPlacement **) leftElement);\n\tconst ShardPlacement *rightPlacement = *((const ShardPlacement **) rightElement);\n\n\tuint64 leftPlacementId = leftPlacement->placementId;\n\tuint64 rightPlacementId = rightPlacement->placementId;\n\n\tif (leftPlacementId < rightPlacementId)\n\t{\n\t\treturn -1;\n\t}\n\telse if (leftPlacementId > rightPlacementId)\n\t{\n\t\treturn 1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * CompareGroupShardPlacements compares two group shard placements by placement id.\n */\nint\nCompareGroupShardPlacements(const void *leftElement, const void *rightElement)\n{\n\tconst GroupShardPlacement *leftPlacement =\n\t\t*((const GroupShardPlacement **) leftElement);\n\tconst GroupShardPlacement *rightPlacement =\n\t\t*((const GroupShardPlacement **) rightElement);\n\n\tuint64 leftPlacementId = leftPlacement->placementId;\n\tuint64 rightPlacementId = rightPlacement->placementId;\n\n\tif (leftPlacementId < rightPlacementId)\n\t{\n\t\treturn -1;\n\t}\n\telse if (leftPlacementId > rightPlacementId)\n\t{\n\t\treturn 1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * LeftRotateList returns a copy of the given list that has been cyclically\n * shifted to the left by the given rotation count. For this, the function\n * repeatedly moves the list's first element to the end of the list, and\n * then returns the newly rotated list.\n */\nstatic List *\nLeftRotateList(List *list, uint32 rotateCount)\n{\n\tList *rotatedList = list_copy(list);\n\n\tfor (uint32 rotateIndex = 0; rotateIndex < rotateCount; rotateIndex++)\n\t{\n\t\tvoid *firstElement = linitial(rotatedList);\n\n\t\trotatedList = list_delete_first(rotatedList);\n\t\trotatedList = lappend(rotatedList, firstElement);\n\t}\n\n\treturn rotatedList;\n}\n\n\n/*\n * FindDependentMergeTaskList walks over the given task's dependent task list,\n * finds the merge tasks in the list, and returns those found tasks in a new\n * list.\n */\nstatic List *\nFindDependentMergeTaskList(Task *sqlTask)\n{\n\tList *dependentMergeTaskList = NIL;\n\tList *dependentTaskList = sqlTask->dependentTaskList;\n\n\tListCell *dependentTaskCell = NULL;\n\tforeach(dependentTaskCell, dependentTaskList)\n\t{\n\t\tTask *dependentTask = (Task *) lfirst(dependentTaskCell);\n\t\tif (dependentTask->taskType == MERGE_TASK)\n\t\t{\n\t\t\tdependentMergeTaskList = lappend(dependentMergeTaskList, dependentTask);\n\t\t}\n\t}\n\n\treturn dependentMergeTaskList;\n}\n\n\n/*\n * AssignDualHashTaskList uses a round-robin algorithm to assign locations to\n * tasks; these tasks don't have any anchor shards and instead operate on (hash\n * repartitioned) merged tables.\n */\nstatic List *\nAssignDualHashTaskList(List *taskList)\n{\n\tList *assignedTaskList = NIL;\n\tListCell *taskCell = NULL;\n\tTask *firstTask = (Task *) linitial(taskList);\n\tuint64 jobId = firstTask->jobId;\n\tuint32 assignedTaskIndex = 0;\n\n\t/*\n\t * We start assigning tasks at an index determined by the jobId. This way,\n\t * if subsequent jobs have a small number of tasks, we won't allocate the\n\t * tasks to the same worker repeatedly.\n\t */\n\tList *workerNodeList = ActiveReadableNodeList();\n\tuint32 workerNodeCount = (uint32) list_length(workerNodeList);\n\tuint32 beginningNodeIndex = jobId % workerNodeCount;\n\n\t/* sort worker node list and task list for deterministic results */\n\tworkerNodeList = SortList(workerNodeList, CompareWorkerNodes);\n\ttaskList = SortList(taskList, CompareTasksByTaskId);\n\n\tforeach(taskCell, taskList)\n\t{\n\t\tTask *task = (Task *) lfirst(taskCell);\n\t\tList *taskPlacementList = NIL;\n\n\t\tfor (uint32 replicaIndex = 0; replicaIndex < ShardReplicationFactor;\n\t\t\t replicaIndex++)\n\t\t{\n\t\t\tuint32 assignmentOffset = beginningNodeIndex + assignedTaskIndex +\n\t\t\t\t\t\t\t\t\t  replicaIndex;\n\t\t\tuint32 assignmentIndex = assignmentOffset % workerNodeCount;\n\t\t\tWorkerNode *workerNode = list_nth(workerNodeList, assignmentIndex);\n\n\t\t\tShardPlacement *taskPlacement = CitusMakeNode(ShardPlacement);\n\t\t\tSetPlacementNodeMetadata(taskPlacement, workerNode);\n\n\t\t\ttaskPlacementList = lappend(taskPlacementList, taskPlacement);\n\t\t}\n\n\t\ttask->taskPlacementList = taskPlacementList;\n\n\t\tShardPlacement *primaryPlacement = (ShardPlacement *) linitial(\n\t\t\ttask->taskPlacementList);\n\t\tereport(DEBUG3, (errmsg(\"assigned task %u to node %s:%u\", task->taskId,\n\t\t\t\t\t\t\t\tprimaryPlacement->nodeName,\n\t\t\t\t\t\t\t\tprimaryPlacement->nodePort)));\n\n\t\tassignedTaskList = lappend(assignedTaskList, task);\n\t\tassignedTaskIndex++;\n\t}\n\n\treturn assignedTaskList;\n}\n\n\n/*\n * SetPlacementNodeMetadata sets nodename, nodeport, nodeid and groupid for the placement.\n */\nvoid\nSetPlacementNodeMetadata(ShardPlacement *placement, WorkerNode *workerNode)\n{\n\tplacement->nodeName = pstrdup(workerNode->workerName);\n\tplacement->nodePort = workerNode->workerPort;\n\tplacement->nodeId = workerNode->nodeId;\n\tplacement->groupId = workerNode->groupId;\n}\n\n\n/*\n * CompareTasksByTaskId is a helper function to compare two tasks by their taskId.\n */\nint\nCompareTasksByTaskId(const void *leftElement, const void *rightElement)\n{\n\tconst Task *leftTask = *((const Task **) leftElement);\n\tconst Task *rightTask = *((const Task **) rightElement);\n\n\tuint32 leftTaskId = leftTask->taskId;\n\tuint32 rightTaskId = rightTask->taskId;\n\n\tint taskIdDiff = leftTaskId - rightTaskId;\n\treturn taskIdDiff;\n}\n\n\n/*\n * AssignDataFetchDependencies walks over tasks in the given sql or merge task\n * list. The function then propagates worker node assignments from each sql or\n * merge task to the task's data fetch dependencies.\n */\nstatic void\nAssignDataFetchDependencies(List *taskList)\n{\n\tListCell *taskCell = NULL;\n\tforeach(taskCell, taskList)\n\t{\n\t\tTask *task = (Task *) lfirst(taskCell);\n\t\tList *dependentTaskList = task->dependentTaskList;\n\t\tListCell *dependentTaskCell = NULL;\n\n\t\tAssert(task->taskPlacementList != NIL);\n\t\tAssert(task->taskType == READ_TASK || task->taskType == MERGE_TASK);\n\n\t\tforeach(dependentTaskCell, dependentTaskList)\n\t\t{\n\t\t\tTask *dependentTask = (Task *) lfirst(dependentTaskCell);\n\t\t\tif (dependentTask->taskType == MAP_OUTPUT_FETCH_TASK)\n\t\t\t{\n\t\t\t\tdependentTask->taskPlacementList = task->taskPlacementList;\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * TaskListHighestTaskId walks over tasks in the given task list, finds the task\n * that has the largest taskId, and returns that taskId.\n *\n * Note: This function assumes that the dependent taskId's are set before the\n * taskId's for the given task list.\n */\nstatic uint32\nTaskListHighestTaskId(List *taskList)\n{\n\tuint32 highestTaskId = 0;\n\tListCell *taskCell = NULL;\n\n\tforeach(taskCell, taskList)\n\t{\n\t\tTask *task = (Task *) lfirst(taskCell);\n\t\tif (task->taskId > highestTaskId)\n\t\t{\n\t\t\thighestTaskId = task->taskId;\n\t\t}\n\t}\n\n\treturn highestTaskId;\n}\n\n\n/*\n * QueryTreeHasImproperForDeparseNodes walks over the node,\n * and returns true if there are RelabelType or\n * CoerceViaIONodes which are improper for deparse\n */\nstatic bool\nQueryTreeHasImproperForDeparseNodes(Node *inputNode, void *context)\n{\n\tif (inputNode == NULL)\n\t{\n\t\treturn false;\n\t}\n\telse if (IsImproperForDeparseRelabelTypeNode(inputNode) ||\n\t\t\t IsImproperForDeparseCoerceViaIONode(inputNode))\n\t{\n\t\treturn true;\n\t}\n\telse if (IsA(inputNode, Query))\n\t{\n\t\treturn query_tree_walker((Query *) inputNode,\n\t\t\t\t\t\t\t\t QueryTreeHasImproperForDeparseNodes,\n\t\t\t\t\t\t\t\t NULL, 0);\n\t}\n\n\treturn expression_tree_walker(inputNode,\n\t\t\t\t\t\t\t\t  QueryTreeHasImproperForDeparseNodes,\n\t\t\t\t\t\t\t\t  NULL);\n}\n\n\n/*\n * AdjustImproperForDeparseNodes takes an input rewritten query and modifies\n * nodes which, after going through our planner, pose a problem when\n * deparsing. So far we have two such type of Nodes that may pose problems:\n * RelabelType and CoerceIO nodes.\n * Details will be written in comments in the corresponding if conditions.\n */\nstatic Node *\nAdjustImproperForDeparseNodes(Node *inputNode, void *context)\n{\n\tif (inputNode == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tif (IsImproperForDeparseRelabelTypeNode(inputNode))\n\t{\n\t\t/*\n\t\t * The planner converts CollateExpr to RelabelType\n\t\t * and here we convert back.\n\t\t */\n\t\treturn (Node *) RelabelTypeToCollateExpr((RelabelType *) inputNode);\n\t}\n\telse if (IsImproperForDeparseCoerceViaIONode(inputNode))\n\t{\n\t\t/*\n\t\t * The planner converts some ::text/::varchar casts to ::cstring\n\t\t * and here we convert back to text because cstring is a pseudotype\n\t\t * and it cannot be casted to most resulttypes\n\t\t */\n\n\t\tCoerceViaIO *iocoerce = (CoerceViaIO *) inputNode;\n\t\tNode *arg = (Node *) iocoerce->arg;\n\t\tConst *cstringToText = (Const *) arg;\n\n\t\tcstringToText->consttype = TEXTOID;\n\t\tcstringToText->constlen = -1;\n\n\t\tType textType = typeidType(TEXTOID);\n\t\tchar *constvalue = NULL;\n\n\t\tif (!cstringToText->constisnull)\n\t\t{\n\t\t\tconstvalue = DatumGetCString(cstringToText->constvalue);\n\t\t}\n\n\t\tcstringToText->constvalue = stringTypeDatum(textType,\n\t\t\t\t\t\t\t\t\t\t\t\t\tconstvalue,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcstringToText->consttypmod);\n\t\tReleaseSysCache(textType);\n\t\treturn inputNode;\n\t}\n\telse if (IsA(inputNode, Query))\n\t{\n\t\treturn (Node *) query_tree_mutator((Query *) inputNode,\n\t\t\t\t\t\t\t\t\t\t   AdjustImproperForDeparseNodes,\n\t\t\t\t\t\t\t\t\t\t   NULL, QTW_DONT_COPY_QUERY);\n\t}\n\n\treturn expression_tree_mutator(inputNode, AdjustImproperForDeparseNodes, NULL);\n}\n\n\n/*\n * Checks if the given node is of Relabel type which is improper for deparsing\n * The planner converts some CollateExpr to RelabelType nodes, and we need\n * to find these nodes. They would be improperly deparsed without the\n * \"COLLATE\" expression.\n */\nstatic bool\nIsImproperForDeparseRelabelTypeNode(Node *inputNode)\n{\n\treturn (IsA(inputNode, RelabelType) &&\n\t\t\tOidIsValid(((RelabelType *) inputNode)->resultcollid) &&\n\t\t\t((RelabelType *) inputNode)->resultcollid != DEFAULT_COLLATION_OID);\n}\n\n\n/*\n * Checks if the given node is of CoerceViaIO type which is improper for deparsing\n * The planner converts some ::text/::varchar casts to ::cstring, and we need\n * to find these nodes. They would be improperly deparsed with \"cstring\" which cannot\n * be casted to most resulttypes.\n */\nstatic bool\nIsImproperForDeparseCoerceViaIONode(Node *inputNode)\n{\n\treturn (IsA(inputNode, CoerceViaIO) &&\n\t\t\tIsA(((CoerceViaIO *) inputNode)->arg, Const) &&\n\t\t\t((Const *) ((CoerceViaIO *) inputNode)->arg)->consttype == CSTRINGOID);\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/multi_router_planner.c",
    "content": "\n/*-------------------------------------------------------------------------\n *\n * multi_router_planner.c\n *\n * This file contains functions to plan multiple shard queries without any\n * aggregation step including distributed table modifications.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <stddef.h>\n\n#include \"postgres.h\"\n\n#include \"access/stratnum.h\"\n#include \"access/tupdesc.h\"\n#include \"access/tupdesc_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/pg_opfamily.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_type.h\"\n#include \"executor/execdesc.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/joininfo.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/pathnode.h\"\n#include \"optimizer/paths.h\"\n#include \"optimizer/planmain.h\"\n#include \"optimizer/planner.h\"\n#include \"optimizer/restrictinfo.h\"\n#include \"parser/parse_oper.h\"\n#include \"parser/parsetree.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/lock.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/errcodes.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/typcache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_clauses.h\"\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/distribution_column.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/executor_util.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/intermediate_result_pruning.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/merge_planner.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/query_pushdown_planning.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relation_restriction_equivalence.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/shard_utils.h\"\n#include \"distributed/shardinterval_utils.h\"\n\n/* intermediate value for INSERT processing */\ntypedef struct InsertValues\n{\n\tExpr *partitionValueExpr; /* partition value provided in INSERT row */\n\tList *rowValues;          /* full values list of INSERT row, possibly NIL */\n\tint64 shardId;            /* target shard for this row, possibly invalid */\n\tIndex listIndex;          /* index to make our sorting stable */\n} InsertValues;\n\n\n/*\n * A ModifyRoute encapsulates the information needed to route modifications\n * to the appropriate shard. For a single-shard modification, only one route\n * is needed, but in the case of e.g. a multi-row INSERT, lists of these values\n * will help divide the rows by their destination shards, permitting later\n * shard-and-row-specific extension of the original SQL.\n */\ntypedef struct ModifyRoute\n{\n\tint64 shardId;        /* identifier of target shard */\n\tList *rowValuesLists; /* for multi-row INSERTs, list of rows to be inserted */\n} ModifyRoute;\n\n\ntypedef struct WalkerState\n{\n\tbool containsVar;\n\tbool varArgument;\n\tbool badCoalesce;\n} WalkerState;\n\nbool EnableRouterExecution = true;\nbool EnableNonColocatedRouterQueryPushdown = false;\n\n\n/* planner functions forward declarations */\nstatic void CreateSingleTaskRouterSelectPlan(DistributedPlan *distributedPlan,\n\t\t\t\t\t\t\t\t\t\t\t Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t Query *query,\n\t\t\t\t\t\t\t\t\t\t\t PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\nstatic bool IsTidColumn(Node *node);\nstatic DeferredErrorMessage * ModifyPartialQuerySupported(Query *queryTree, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  multiShardQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Oid *distributedTableId);\nstatic DeferredErrorMessage * MultiShardUpdateDeleteSupported(Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext);\nstatic DeferredErrorMessage * SingleShardUpdateDeleteSupported(Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nstatic bool MasterIrreducibleExpressionWalker(Node *expression, WalkerState *state);\nstatic bool MasterIrreducibleExpressionFunctionChecker(Oid func_id, void *context);\nstatic Job * RouterInsertJob(Query *originalQuery);\nstatic void ErrorIfNoShardsExist(CitusTableCacheEntry *cacheEntry);\nstatic DeferredErrorMessage * DeferErrorIfModifyView(Query *queryTree);\nstatic Job * CreateJob(Query *query);\nstatic Task * CreateTask(TaskType taskType);\nstatic bool RelationPrunesToMultipleShards(List *relationShardList);\nstatic void NormalizeMultiRowInsertTargetList(Query *query);\nstatic void AppendNextDummyColReference(Alias *expendedReferenceNames);\nstatic String * MakeDummyColumnString(int dummyColumnId);\nstatic List * BuildRoutesForInsert(Query *query, DeferredErrorMessage **planningError);\nstatic List * GroupInsertValuesByShardId(List *insertValuesList);\nstatic List * ExtractInsertValuesList(Query *query, Var *partitionColumn);\nstatic DeferredErrorMessage * DeferErrorIfUnsupportedRouterPlannableSelectQuery(Query *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tquery);\nstatic DeferredErrorMessage * ErrorIfQueryHasUnroutableModifyingCTE(Query *queryTree);\nstatic DeferredErrorMessage * ErrorIfQueryHasCTEWithSearchClause(Query *queryTree);\nstatic bool ContainsSearchClauseWalker(Node *node, void *context);\nstatic bool SelectsFromDistributedTable(List *rangeTableList, Query *query);\nstatic bool AllShardsColocated(List *relationShardList);\nstatic ShardPlacement * CreateDummyPlacement(bool hasLocalRelation);\nstatic ShardPlacement * CreateLocalDummyPlacement();\nstatic int CompareInsertValuesByShardId(const void *leftElement,\n\t\t\t\t\t\t\t\t\t\tconst void *rightElement);\nstatic List * SingleShardTaskList(Query *query, uint64 jobId,\n\t\t\t\t\t\t\t\t  List *relationShardList, List *placementList,\n\t\t\t\t\t\t\t\t  uint64 shardId, bool parametersInQueryResolved,\n\t\t\t\t\t\t\t\t  bool isLocalTableModification, Const *partitionKeyValue,\n\t\t\t\t\t\t\t\t  int colocationId, bool delayedFastPath);\nstatic bool RowLocksOnRelations(Node *node, List **rtiLockList);\nstatic void ReorderTaskPlacementsByTaskAssignmentPolicy(Job *job,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tTaskAssignmentPolicyType\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttaskAssignmentPolicy,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *placementList);\nstatic bool ModifiesLocalTableWithRemoteCitusLocalTable(List *rangeTableList);\nstatic DeferredErrorMessage * DeferErrorIfUnsupportedLocalTableJoin(List *rangeTableList);\nstatic bool IsLocallyAccessibleCitusLocalTable(Oid relationId);\nstatic bool ConvertToQueryOnShard(Query *query, Oid relationID, Oid shardRelationId);\n\n/*\n * CreateRouterPlan attempts to create a router executor plan for the given\n * SELECT statement. ->planningError is set if planning fails.\n */\nDistributedPlan *\nCreateRouterPlan(Query *originalQuery, Query *query,\n\t\t\t\t PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tDistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan);\n\n\tdistributedPlan->planningError = DeferErrorIfUnsupportedRouterPlannableSelectQuery(\n\t\tquery);\n\n\tif (distributedPlan->planningError == NULL)\n\t{\n\t\tCreateSingleTaskRouterSelectPlan(distributedPlan, originalQuery, query,\n\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\n\t}\n\n\tdistributedPlan->fastPathRouterPlan =\n\t\tplannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery;\n\n\treturn distributedPlan;\n}\n\n\n/*\n * CreateModifyPlan attempts to create a plan for the given modification\n * statement. If planning fails ->planningError is set to a description of\n * the failure.\n */\nDistributedPlan *\nCreateModifyPlan(Query *originalQuery, Query *query,\n\t\t\t\t PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tJob *job = NULL;\n\tDistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan);\n\tbool multiShardQuery = false;\n\n\tAssert(originalQuery->commandType != CMD_SELECT);\n\n\tdistributedPlan->modLevel = RowModifyLevelForQuery(query);\n\n\tdistributedPlan->planningError = ModifyQuerySupported(query, originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  multiShardQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext);\n\n\tif (distributedPlan->planningError != NULL)\n\t{\n\t\treturn distributedPlan;\n\t}\n\n\tif (UpdateOrDeleteOrMergeQuery(query))\n\t{\n\t\tjob = RouterJob(originalQuery, plannerRestrictionContext,\n\t\t\t\t\t\t&distributedPlan->planningError);\n\t}\n\telse\n\t{\n\t\tjob = RouterInsertJob(originalQuery);\n\t}\n\n\tif (distributedPlan->planningError != NULL)\n\t{\n\t\treturn distributedPlan;\n\t}\n\n\tereport(DEBUG2, (errmsg(\"Creating router plan\")));\n\n\tdistributedPlan->workerJob = job;\n\tdistributedPlan->combineQuery = NULL;\n\tdistributedPlan->expectResults = originalQuery->returningList != NIL;\n\tdistributedPlan->targetRelationId = ResultRelationOidForQuery(query);\n\n\tdistributedPlan->fastPathRouterPlan =\n\t\tplannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery;\n\n\n\treturn distributedPlan;\n}\n\n\n/*\n * CreateSingleTaskRouterSelectPlan creates a physical plan for given SELECT query.\n * The returned plan is a router task that returns query results from a single worker.\n * If not router plannable, the returned plan's planningError describes the problem.\n */\nstatic void\nCreateSingleTaskRouterSelectPlan(DistributedPlan *distributedPlan, Query *originalQuery,\n\t\t\t\t\t\t\t\t Query *query,\n\t\t\t\t\t\t\t\t PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tAssert(query->commandType == CMD_SELECT);\n\n\tdistributedPlan->modLevel = RowModifyLevelForQuery(query);\n\n\tJob *job = RouterJob(originalQuery, plannerRestrictionContext,\n\t\t\t\t\t\t &distributedPlan->planningError);\n\n\tif (distributedPlan->planningError != NULL)\n\t{\n\t\t/* query cannot be handled by this planner */\n\t\treturn;\n\t}\n\n\tereport(DEBUG2, (errmsg(\"Creating router plan\")));\n\n\tdistributedPlan->workerJob = job;\n\tdistributedPlan->combineQuery = NULL;\n\tdistributedPlan->expectResults = true;\n}\n\n\n/*\n * ShardIntervalOpExpressions returns a list of OpExprs with exactly two\n * items in it. The list consists of shard interval ranges with partition columns\n * such as (partitionColumn >= shardMinValue) and (partitionColumn <= shardMaxValue).\n *\n * The function returns hashed columns generated by MakeInt4Column() for the hash\n * partitioned tables in place of partition columns.\n *\n * The function returns NIL if shard interval does not belong to a hash,\n * range and append distributed tables.\n *\n * NB: If you update this, also look at PrunableExpressionsWalker().\n */\nList *\nShardIntervalOpExpressions(ShardInterval *shardInterval, Index rteIndex)\n{\n\tOid relationId = shardInterval->relationId;\n\tVar *partitionColumn = NULL;\n\n\tif (IsCitusTableType(relationId, HASH_DISTRIBUTED))\n\t{\n\t\tpartitionColumn = MakeInt4Column();\n\t}\n\telse if (IsCitusTableType(relationId, RANGE_DISTRIBUTED) || IsCitusTableType(\n\t\t\t\t relationId, APPEND_DISTRIBUTED))\n\t{\n\t\tAssert(rteIndex > 0);\n\t\tpartitionColumn = PartitionColumn(relationId, rteIndex);\n\t}\n\telse\n\t{\n\t\t/* do not add any shard range interval for reference tables */\n\t\treturn NIL;\n\t}\n\n\t/* build the base expression for constraint */\n\tNode *baseConstraint = BuildBaseConstraint(partitionColumn);\n\n\t/* walk over shard list and check if shards can be pruned */\n\tif (shardInterval->minValueExists && shardInterval->maxValueExists)\n\t{\n\t\tUpdateConstraint(baseConstraint, shardInterval);\n\t}\n\n\treturn list_make1(baseConstraint);\n}\n\n\n/*\n * AddPartitionKeyNotNullFilterToSelect adds the following filters to a subquery:\n *\n *    partitionColumn IS NOT NULL\n *\n * The function expects and asserts that subquery's target list contains a partition\n * column value. Thus, this function should never be called with reference tables.\n */\nvoid\nAddPartitionKeyNotNullFilterToSelect(Query *subqery)\n{\n\tList *targetList = subqery->targetList;\n\tListCell *targetEntryCell = NULL;\n\tVar *targetPartitionColumnVar = NULL;\n\n\t/* iterate through the target entries */\n\tforeach(targetEntryCell, targetList)\n\t{\n\t\tTargetEntry *targetEntry = lfirst(targetEntryCell);\n\n\t\tbool skipOuterVars = true;\n\t\tif (IsPartitionColumn(targetEntry->expr, subqery, skipOuterVars) &&\n\t\t\tIsA(targetEntry->expr, Var))\n\t\t{\n\t\t\ttargetPartitionColumnVar = (Var *) targetEntry->expr;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* we should have found target partition column */\n\tAssert(targetPartitionColumnVar != NULL);\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\tif (subqery->hasGroupRTE)\n\t{\n\t\t/* if the partition column is a grouped column, we need to flatten it\n\t\t * to ensure query deparsing works correctly. We choose to do this here\n\t\t * instead of in ruletils.c because we want to keep the flattening logic\n\t\t * close to the NOT NULL filter injection.\n\t\t */\n\t\tRangeTblEntry *partitionRTE = rt_fetch(targetPartitionColumnVar->varno,\n\t\t\t\t\t\t\t\t\t\t\t   subqery->rtable);\n\t\tif (partitionRTE->rtekind == RTE_GROUP)\n\t\t{\n\t\t\ttargetPartitionColumnVar = (Var *) flatten_group_exprs(NULL, subqery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   (Node *)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   targetPartitionColumnVar);\n\t\t}\n\t}\n#endif\n\n\t/* create expression for partition_column IS NOT NULL */\n\tNullTest *nullTest = makeNode(NullTest);\n\tnullTest->nulltesttype = IS_NOT_NULL;\n\tnullTest->arg = (Expr *) targetPartitionColumnVar;\n\tnullTest->argisrow = false;\n\n\t/* finally add the quals */\n\tif (subqery->jointree->quals == NULL)\n\t{\n\t\tsubqery->jointree->quals = (Node *) nullTest;\n\t}\n\telse\n\t{\n\t\tsubqery->jointree->quals = make_and_qual(subqery->jointree->quals,\n\t\t\t\t\t\t\t\t\t\t\t\t (Node *) nullTest);\n\t}\n}\n\n\n/*\n * ExtractSourceResultRangeTableEntry Generic wrapper for modification commands that\n * utilizes results as input, based on an source query.\n */\nRangeTblEntry *\nExtractSourceResultRangeTableEntry(Query *query)\n{\n\tif (IsMergeQuery(query))\n\t{\n\t\treturn ExtractMergeSourceRangeTableEntry(query, false);\n\t}\n\telse if (CheckInsertSelectQuery(query))\n\t{\n\t\treturn ExtractSelectRangeTableEntry(query);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * ExtractSelectRangeTableEntry returns the range table entry of the subquery.\n * Note that the function expects and asserts that the input query be\n * an INSERT...SELECT query.\n */\nRangeTblEntry *\nExtractSelectRangeTableEntry(Query *query)\n{\n\tAssert(InsertSelectIntoCitusTable(query) || InsertSelectIntoLocalTable(query));\n\n\t/*\n\t * Since we already asserted InsertSelectIntoCitusTable() it is safe to access\n\t * both lists\n\t */\n\tList *fromList = query->jointree->fromlist;\n\tRangeTblRef *reference = linitial(fromList);\n\tRangeTblEntry *subqueryRte = rt_fetch(reference->rtindex, query->rtable);\n\n\treturn subqueryRte;\n}\n\n\n/*\n * ModifyQueryResultRelationId returns the result relation's Oid\n * for the given modification query.\n *\n * The function errors out if the input query is not a\n * modify query (e.g., INSERT, UPDATE, DELETE or MERGE). So, this\n * function is not expected to be called on SELECT queries.\n */\nOid\nModifyQueryResultRelationId(Query *query)\n{\n\t/* only modify queries have result relations */\n\tif (!IsModifyCommand(query))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"input query is not a modification query\")));\n\t}\n\n\tRangeTblEntry *resultRte = ExtractResultRelationRTE(query);\n\tAssert(OidIsValid(resultRte->relid));\n\n\treturn resultRte->relid;\n}\n\n\n/*\n * ResultRelationOidForQuery returns the OID of the relation this is modified\n * by a given query.\n */\nOid\nResultRelationOidForQuery(Query *query)\n{\n\tRangeTblEntry *resultRTE = rt_fetch(query->resultRelation, query->rtable);\n\n\treturn resultRTE->relid;\n}\n\n\n/*\n * ExtractResultRelationRTE returns the table's resultRelation range table\n * entry. This returns NULL when there's no resultRelation, such as in a SELECT\n * query.\n */\nRangeTblEntry *\nExtractResultRelationRTE(Query *query)\n{\n\tif (query->resultRelation > 0)\n\t{\n\t\treturn rt_fetch(query->resultRelation, query->rtable);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * ExtractResultRelationRTEOrError returns the table's resultRelation range table\n * entry and errors out if there's no result relation at all, e.g. like in a\n * SELECT query.\n *\n * This is a separate function (instead of using missingOk), so static analysis\n * reasons about NULL returns correctly.\n */\nRangeTblEntry *\nExtractResultRelationRTEOrError(Query *query)\n{\n\tRangeTblEntry *relation = ExtractResultRelationRTE(query);\n\tif (relation == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"no result relation could be found for the query\"),\n\t\t\t\t\t\terrhint(\"is this a SELECT query?\")));\n\t}\n\n\treturn relation;\n}\n\n\n/*\n * IsTidColumn gets a node and returns true if the node is a Var type of TID.\n */\nstatic bool\nIsTidColumn(Node *node)\n{\n\tif (IsA(node, Var))\n\t{\n\t\tVar *column = (Var *) node;\n\t\tif (column->vartype == TIDOID)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * TargetlistAndFunctionsSupported implements a subset of what ModifyPartialQuerySupported\n * checks, that subset being checking what functions are allowed, if we are\n * updating distribution column, etc.\n * Note: This subset of checks are repeated for each MERGE modify action.\n */\nDeferredErrorMessage *\nTargetlistAndFunctionsSupported(Oid resultRelationId, FromExpr *joinTree, Node *quals,\n\t\t\t\t\t\t\t\tList *targetList,\n\t\t\t\t\t\t\t\tCmdType commandType, List *returningList)\n{\n\tuint32 rangeTableId = 1;\n\tVar *partitionColumn = NULL;\n\n\tif (IsCitusTable(resultRelationId))\n\t{\n\t\tpartitionColumn = PartitionColumn(resultRelationId, rangeTableId);\n\t}\n\n\tbool hasVarArgument = false; /* A STABLE function is passed a Var argument */\n\tbool hasBadCoalesce = false; /* CASE/COALESCE passed a mutable function */\n\tListCell *targetEntryCell = NULL;\n\n\tforeach(targetEntryCell, targetList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell);\n\n\t\t/* skip resjunk entries: UPDATE adds some for ctid, etc. */\n\t\tif (targetEntry->resjunk)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tbool targetEntryPartitionColumn = false;\n\t\tAttrNumber targetColumnAttrNumber = InvalidAttrNumber;\n\n\t\t/* reference tables do not have partition column */\n\t\tif (partitionColumn == NULL)\n\t\t{\n\t\t\ttargetEntryPartitionColumn = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (commandType == CMD_UPDATE)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Note that it is not possible to give an alias to\n\t\t\t\t * UPDATE table SET ...\n\t\t\t\t */\n\t\t\t\tif (targetEntry->resname)\n\t\t\t\t{\n\t\t\t\t\ttargetColumnAttrNumber = get_attnum(resultRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetEntry->resname);\n\t\t\t\t\tif (targetColumnAttrNumber == partitionColumn->varattno)\n\t\t\t\t\t{\n\t\t\t\t\t\ttargetEntryPartitionColumn = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\t\tif (commandType == CMD_UPDATE &&\n\t\t\tFindNodeMatchingCheckFunction((Node *) targetEntry->expr,\n\t\t\t\t\t\t\t\t\t\t  CitusIsVolatileFunction))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"functions used in UPDATE queries on distributed \"\n\t\t\t\t\t\t\t\t \"tables must not be VOLATILE\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\n\t\tif (commandType == CMD_UPDATE && targetEntryPartitionColumn &&\n\t\t\tTargetEntryChangesValue(targetEntry, partitionColumn, joinTree))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"modifying the partition value of rows is not \"\n\t\t\t\t\t\t\t\t \"allowed\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\n\t\tif (commandType == CMD_UPDATE &&\n\t\t\tMasterIrreducibleExpression((Node *) targetEntry->expr,\n\t\t\t\t\t\t\t\t\t\t&hasVarArgument, &hasBadCoalesce))\n\t\t{\n\t\t\tAssert(hasVarArgument || hasBadCoalesce);\n\t\t}\n\n\t\tif (FindNodeMatchingCheckFunction((Node *) targetEntry->expr,\n\t\t\t\t\t\t\t\t\t\t  NodeIsFieldStore))\n\t\t{\n\t\t\t/* DELETE cannot do field indirection already */\n\t\t\tAssert(commandType == CMD_UPDATE || commandType == CMD_INSERT);\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"inserting or modifying composite type fields is not \"\n\t\t\t\t\t\t\t\t \"supported\", NULL,\n\t\t\t\t\t\t\t\t \"Use the column name to insert or update the composite \"\n\t\t\t\t\t\t\t\t \"type as a single value\");\n\t\t}\n\t}\n\n\tif (joinTree != NULL)\n\t{\n\t\tif (FindNodeMatchingCheckFunction((Node *) quals,\n\t\t\t\t\t\t\t\t\t\t  CitusIsVolatileFunction))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"functions used in the WHERE/ON/WHEN clause of modification \"\n\t\t\t\t\t\t\t\t \"queries on distributed tables must not be VOLATILE\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\t\telse if (MasterIrreducibleExpression(quals, &hasVarArgument,\n\t\t\t\t\t\t\t\t\t\t\t &hasBadCoalesce))\n\t\t{\n\t\t\tAssert(hasVarArgument || hasBadCoalesce);\n\t\t}\n\t}\n\n\tif (hasVarArgument)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"STABLE functions used in UPDATE queries \"\n\t\t\t\t\t\t\t \"cannot be called with column references\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tif (hasBadCoalesce)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"non-IMMUTABLE functions are not allowed in CASE or \"\n\t\t\t\t\t\t\t \"COALESCE statements\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tif (contain_mutable_functions((Node *) returningList))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"non-IMMUTABLE functions are not allowed in the \"\n\t\t\t\t\t\t\t \"RETURNING clause\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tif (quals != NULL &&\n\t\tnodeTag(quals) == T_CurrentOfExpr)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot run DML queries with cursors\", NULL,\n\t\t\t\t\t\t\t NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * ModifyPartialQuerySupported implements a subset of what ModifyQuerySupported checks,\n * that subset being what's necessary to check modifying CTEs for.\n */\nstatic DeferredErrorMessage *\nModifyPartialQuerySupported(Query *queryTree, bool multiShardQuery,\n\t\t\t\t\t\t\tOid *distributedTableIdOutput)\n{\n\tDeferredErrorMessage *deferredError = DeferErrorIfModifyView(queryTree);\n\tif (deferredError != NULL)\n\t{\n\t\treturn deferredError;\n\t}\n\tCmdType commandType = queryTree->commandType;\n\n\tdeferredError = DeferErrorIfUnsupportedLocalTableJoin(queryTree->rtable);\n\tif (deferredError != NULL)\n\t{\n\t\treturn deferredError;\n\t}\n\n\t/*\n\t * Reject subqueries which are in SELECT or WHERE clause.\n\t * Queries which include subqueries in FROM clauses are rejected below.\n\t */\n\tif (queryTree->hasSubLinks == true)\n\t{\n\t\t/* we support subqueries for INSERTs only via INSERT INTO ... SELECT */\n\t\tif (!UpdateOrDeleteOrMergeQuery(queryTree))\n\t\t{\n\t\t\tAssert(queryTree->commandType == CMD_INSERT);\n\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"subqueries are not supported within INSERT queries\",\n\t\t\t\t\t\t\t\t NULL, \"Try rewriting your queries with 'INSERT \"\n\t\t\t\t\t\t\t\t\t   \"INTO ... SELECT' syntax.\");\n\t\t}\n\t}\n\n\t/* reject queries which include CommonTableExpr which aren't routable */\n\tif (queryTree->cteList != NIL)\n\t{\n\t\tListCell *cteCell = NULL;\n\n\t\t/* CTEs still not supported for INSERTs. */\n\t\tif (queryTree->commandType == CMD_INSERT)\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"Router planner doesn't support common table expressions with INSERT queries.\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\n\t\tforeach(cteCell, queryTree->cteList)\n\t\t{\n\t\t\tCommonTableExpr *cte = (CommonTableExpr *) lfirst(cteCell);\n\t\t\tQuery *cteQuery = (Query *) cte->ctequery;\n\n\t\t\tif (cteQuery->commandType != CMD_SELECT)\n\t\t\t{\n\t\t\t\t/* Modifying CTEs still not supported for multi shard queries. */\n\t\t\t\tif (multiShardQuery)\n\t\t\t\t{\n\t\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t\t \"Router planner doesn't support non-select common table expressions with multi shard queries.\",\n\t\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t\t\t}\n\t\t\t\t/* Modifying CTEs exclude both INSERT CTEs & INSERT queries. */\n\t\t\t\telse if (cteQuery->commandType == CMD_INSERT)\n\t\t\t\t{\n\t\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t\t \"Router planner doesn't support INSERT common table expressions.\",\n\t\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (cteQuery->hasForUpdate &&\n\t\t\t\tFindNodeMatchingCheckFunctionInRangeTableList(cteQuery->rtable,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  IsReferenceTableRTE))\n\t\t\t{\n\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"Router planner doesn't support SELECT FOR UPDATE\"\n\t\t\t\t\t\t\t\t\t \" in common table expressions involving reference tables.\",\n\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t\t}\n\n\t\t\tif (FindNodeMatchingCheckFunction((Node *) cteQuery, CitusIsVolatileFunction))\n\t\t\t{\n\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"Router planner doesn't support VOLATILE functions\"\n\t\t\t\t\t\t\t\t\t \" in common table expressions.\",\n\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t\t}\n\n\t\t\tif (cteQuery->commandType == CMD_SELECT)\n\t\t\t{\n\t\t\t\tDeferredErrorMessage *cteError =\n\t\t\t\t\tDeferErrorIfUnsupportedRouterPlannableSelectQuery(cteQuery);\n\t\t\t\tif (cteError)\n\t\t\t\t{\n\t\t\t\t\treturn cteError;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\tOid resultRelationId = ModifyQueryResultRelationId(queryTree);\n\t*distributedTableIdOutput = resultRelationId;\n\n\tcommandType = queryTree->commandType;\n\tif (commandType == CMD_INSERT || commandType == CMD_UPDATE ||\n\t\tcommandType == CMD_DELETE)\n\t{\n\t\tdeferredError =\n\t\t\tTargetlistAndFunctionsSupported(resultRelationId,\n\t\t\t\t\t\t\t\t\t\t\tqueryTree->jointree,\n\t\t\t\t\t\t\t\t\t\t\tqueryTree->jointree->quals,\n\t\t\t\t\t\t\t\t\t\t\tqueryTree->targetList,\n\t\t\t\t\t\t\t\t\t\t\tcommandType,\n\t\t\t\t\t\t\t\t\t\t\tqueryTree->returningList);\n\t\tif (deferredError)\n\t\t{\n\t\t\treturn deferredError;\n\t\t}\n\t}\n\n\tdeferredError = ErrorIfOnConflictNotSupported(queryTree);\n\tif (deferredError != NULL)\n\t{\n\t\treturn deferredError;\n\t}\n\n\n\t/* set it for caller to use when we don't return any errors */\n\t*distributedTableIdOutput = resultRelationId;\n\n\treturn NULL;\n}\n\n\n/*\n * DeferErrorIfUnsupportedLocalTableJoin returns an error message\n * if there is an unsupported join in the given range table list.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfUnsupportedLocalTableJoin(List *rangeTableList)\n{\n\tif (ModifiesLocalTableWithRemoteCitusLocalTable(rangeTableList))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"Modifying local tables with remote local tables is \"\n\t\t\t\t\t\t\t \"not supported.\",\n\t\t\t\t\t\t\t NULL,\n\t\t\t\t\t\t\t \"Consider wrapping remote local table to a CTE, \"\n\t\t\t\t\t\t\t \"or subquery\");\n\t}\n\treturn NULL;\n}\n\n\n/*\n * ModifiesLocalTableWithRemoteCitusLocalTable returns true if a local\n * table is modified with a remote citus local table. This could be a case with\n * MX structure.\n */\nstatic bool\nModifiesLocalTableWithRemoteCitusLocalTable(List *rangeTableList)\n{\n\tbool containsLocalResultRelation = false;\n\tbool containsRemoteCitusLocalTable = false;\n\n\tRangeTblEntry *rangeTableEntry = NULL;\n\tforeach_declared_ptr(rangeTableEntry, rangeTableList)\n\t{\n\t\tif (!IsRecursivelyPlannableRelation(rangeTableEntry))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tif (IsCitusTableType(rangeTableEntry->relid, CITUS_LOCAL_TABLE))\n\t\t{\n\t\t\tif (!IsLocallyAccessibleCitusLocalTable(rangeTableEntry->relid))\n\t\t\t{\n\t\t\t\tcontainsRemoteCitusLocalTable = true;\n\t\t\t}\n\t\t}\n\t\telse if (!IsCitusTable(rangeTableEntry->relid))\n\t\t{\n\t\t\tcontainsLocalResultRelation = true;\n\t\t}\n\t}\n\treturn containsLocalResultRelation && containsRemoteCitusLocalTable;\n}\n\n\n/*\n * IsLocallyAccessibleCitusLocalTable returns true if the given table\n * is a citus local table that can be accessed using local execution.\n */\nstatic bool\nIsLocallyAccessibleCitusLocalTable(Oid relationId)\n{\n\tif (!IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\treturn false;\n\t}\n\n\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\n\t/*\n\t * Citus local tables should always have exactly one shard, but we have\n\t * this check for safety.\n\t */\n\tif (list_length(shardIntervalList) != 1)\n\t{\n\t\treturn false;\n\t}\n\n\tShardInterval *shardInterval = linitial(shardIntervalList);\n\tuint64 shardId = shardInterval->shardId;\n\tShardPlacement *localShardPlacement =\n\t\tActiveShardPlacementOnGroup(GetLocalGroupId(), shardId);\n\treturn localShardPlacement != NULL;\n}\n\n\n/*\n * NodeIsFieldStore returns true if given Node is a FieldStore object.\n */\nbool\nNodeIsFieldStore(Node *node)\n{\n\treturn node && IsA(node, FieldStore);\n}\n\n\n/*\n * ModifyQuerySupported returns NULL if the query only contains supported\n * features, otherwise it returns an error description.\n * Note that we need both the original query and the modified one because\n * different checks need different versions. In particular, we cannot\n * perform the ContainsReadIntermediateResultFunction check on the\n * rewritten query because it may have been replaced by a subplan,\n * while some of the checks for setting the partition column value rely\n * on the rewritten query.\n */\nDeferredErrorMessage *\nModifyQuerySupported(Query *queryTree, Query *originalQuery, bool multiShardQuery,\n\t\t\t\t\t PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tOid distributedTableId = InvalidOid;\n\n\tDeferredErrorMessage *error =\n\t\tModifyPartialQuerySupported(queryTree, multiShardQuery,\n\t\t\t\t\t\t\t\t\t&distributedTableId);\n\tif (error)\n\t{\n\t\treturn error;\n\t}\n\n\tList *rangeTableList = NIL;\n\tCmdType commandType = queryTree->commandType;\n\tbool fastPathRouterQuery =\n\t\tplannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery;\n\n\t/*\n\t * Here, we check if a recursively planned query tries to modify\n\t * rows based on the ctid column. This is a bad idea because ctid of\n\t * the rows could be changed before the modification part of\n\t * the query is executed.\n\t *\n\t * We can exclude fast path queries since they cannot have intermediate\n\t * results by definition.\n\t */\n\tif (!fastPathRouterQuery &&\n\t\tContainsReadIntermediateResultFunction((Node *) originalQuery))\n\t{\n\t\tbool hasTidColumn = FindNodeMatchingCheckFunction(\n\t\t\t(Node *) originalQuery->jointree, IsTidColumn);\n\n\t\tif (hasTidColumn)\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"cannot perform distributed planning for the given \"\n\t\t\t\t\t\t\t\t \"modification\",\n\t\t\t\t\t\t\t\t \"Recursively planned distributed modifications \"\n\t\t\t\t\t\t\t\t \"with ctid on where clause are not supported.\",\n\t\t\t\t\t\t\t\t NULL);\n\t\t}\n\t}\n\n\t/*\n\t * Extract range table entries for queries that are not fast path. We can skip fast\n\t * path queries because their definition is a single RTE entry, which is a relation,\n\t * so the following check doesn't apply for fast-path queries.\n\t */\n\tif (!fastPathRouterQuery)\n\t{\n\t\tExtractRangeTableEntryWalker((Node *) originalQuery, &rangeTableList);\n\t}\n\tbool containsLocalTableDistributedTableJoin =\n\t\tContainsLocalTableDistributedTableJoin(queryTree->rtable);\n\n\tRangeTblEntry *rangeTableEntry = NULL;\n\tforeach_declared_ptr(rangeTableEntry, rangeTableList)\n\t{\n\t\tif (rangeTableEntry->rtekind == RTE_RELATION)\n\t\t{\n\t\t\t/* we do not expect to see a view in modify query */\n\t\t\tif (rangeTableEntry->relkind == RELKIND_VIEW)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * we already check if modify is run on a view in DeferErrorIfModifyView\n\t\t\t\t * function call. In addition, since Postgres replaced views in FROM\n\t\t\t\t * clause with subqueries, encountering with a view should not be a problem here.\n\t\t\t\t */\n\t\t\t}\n\t\t\telse if (rangeTableEntry->relkind == RELKIND_MATVIEW)\n\t\t\t{\n\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"materialized views in \"\n\t\t\t\t\t\t\t\t\t \"modify queries are not supported\",\n\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t\t}\n\t\t\t/* for other kinds of relations, check if it's distributed */\n\t\t\telse\n\t\t\t{\n\t\t\t\tif (IsRelationLocalTableOrMatView(rangeTableEntry->relid) &&\n\t\t\t\t\tcontainsLocalTableDistributedTableJoin)\n\t\t\t\t{\n\t\t\t\t\tStringInfo errorMessage = makeStringInfo();\n\t\t\t\t\tchar *relationName = get_rel_name(rangeTableEntry->relid);\n\t\t\t\t\tif (IsCitusTable(rangeTableEntry->relid))\n\t\t\t\t\t{\n\t\t\t\t\t\tappendStringInfo(errorMessage,\n\t\t\t\t\t\t\t\t\t\t \"local table %s cannot be joined with these distributed tables\",\n\t\t\t\t\t\t\t\t\t\t relationName);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tappendStringInfo(errorMessage, \"relation %s is not distributed\",\n\t\t\t\t\t\t\t\t\t\t relationName);\n\t\t\t\t\t}\n\t\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t\t errorMessage->data, NULL, NULL);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (rangeTableEntry->rtekind == RTE_VALUES ||\n\t\t\t\t rangeTableEntry->rtekind == RTE_RESULT\n\t\t\t\t )\n\t\t{\n\t\t\t/* do nothing, this type is supported */\n\t\t}\n\t\telse\n\t\t{\n\t\t\tchar *rangeTableEntryErrorDetail = NULL;\n\n\t\t\t/*\n\t\t\t * We support UPDATE, DELETE and MERGE with subqueries and joins unless\n\t\t\t * they are multi shard queries.\n\t\t\t */\n\t\t\tif (UpdateOrDeleteOrMergeQuery(queryTree))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Error out for rangeTableEntries that we do not support.\n\t\t\t * We do not explicitly specify \"in FROM clause\" in the error detail\n\t\t\t * for the features that we do not support at all (SUBQUERY, JOIN).\n\t\t\t */\n\t\t\tif (rangeTableEntry->rtekind == RTE_SUBQUERY)\n\t\t\t{\n\t\t\t\tStringInfo errorHint = makeStringInfo();\n\t\t\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(\n\t\t\t\t\tdistributedTableId);\n\t\t\t\tchar *partitionColumnName =\n\t\t\t\t\tColumnToColumnName(distributedTableId,\n\t\t\t\t\t\t\t\t\t   (Node *) cacheEntry->partitionColumn);\n\n\t\t\t\tappendStringInfo(errorHint, \"Consider using an equality filter on \"\n\t\t\t\t\t\t\t\t\t\t\t\"partition column \\\"%s\\\" to target a single shard.\",\n\t\t\t\t\t\t\t\t partitionColumnName);\n\n\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, \"subqueries are not \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"supported in modifications across multiple shards\",\n\t\t\t\t\t\t\t\t\t errorHint->data, NULL);\n\t\t\t}\n\t\t\telse if (rangeTableEntry->rtekind == RTE_JOIN)\n\t\t\t{\n\t\t\t\trangeTableEntryErrorDetail = \"Joins are not supported in distributed\"\n\t\t\t\t\t\t\t\t\t\t\t \" modifications.\";\n\t\t\t}\n\t\t\telse if (rangeTableEntry->rtekind == RTE_FUNCTION)\n\t\t\t{\n\t\t\t\trangeTableEntryErrorDetail = \"Functions must not appear in the FROM\"\n\t\t\t\t\t\t\t\t\t\t\t \" clause of a distributed modifications.\";\n\t\t\t}\n\t\t\telse if (rangeTableEntry->rtekind == RTE_CTE)\n\t\t\t{\n\t\t\t\trangeTableEntryErrorDetail = \"Common table expressions are not supported\"\n\t\t\t\t\t\t\t\t\t\t\t \" in distributed modifications.\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\trangeTableEntryErrorDetail = \"Unrecognized range table entry.\";\n\t\t\t}\n\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"cannot perform distributed planning for the given \"\n\t\t\t\t\t\t\t\t \"modifications\",\n\t\t\t\t\t\t\t\t rangeTableEntryErrorDetail,\n\t\t\t\t\t\t\t\t NULL);\n\t\t}\n\t}\n\n\tif (commandType != CMD_INSERT)\n\t{\n\t\tDeferredErrorMessage *errorMessage = NULL;\n\n\t\tif (multiShardQuery)\n\t\t{\n\t\t\terrorMessage = MultiShardUpdateDeleteSupported(\n\t\t\t\toriginalQuery,\n\t\t\t\tplannerRestrictionContext);\n\t\t}\n\t\telse\n\t\t{\n\t\t\terrorMessage = SingleShardUpdateDeleteSupported(originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext);\n\t\t}\n\n\t\tif (errorMessage != NULL)\n\t\t{\n\t\t\treturn errorMessage;\n\t\t}\n\t}\n\n\tDeferredErrorMessage *CTEWithSearchClauseError =\n\t\tErrorIfQueryHasCTEWithSearchClause(originalQuery);\n\tif (CTEWithSearchClauseError != NULL)\n\t{\n\t\treturn CTEWithSearchClauseError;\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * Modify statements on simple updetable views are not supported yet.\n * Actually, we need the original query (the query before postgres\n * pg_rewrite_query) to detect if the view sitting in rtable is to\n * be updated or just to be used in FROM clause.\n * Hence, tracing the postgres source code, we deduced that postgres\n * puts the relation to be modified to the first entry of rtable.\n * If first element of the range table list is a simple updatable\n * view and this view is not coming from FROM clause (inFromCl = False),\n * then update is run \"on\" that view.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfModifyView(Query *queryTree)\n{\n\tif (queryTree->rtable != NIL)\n\t{\n\t\tRangeTblEntry *firstRangeTableElement = (RangeTblEntry *) linitial(\n\t\t\tqueryTree->rtable);\n\n\t\tif (firstRangeTableElement->rtekind == RTE_RELATION &&\n\t\t\tfirstRangeTableElement->relkind == RELKIND_VIEW &&\n\t\t\tfirstRangeTableElement->inFromCl == false)\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"cannot modify views when the query contains citus tables\",\n\t\t\t\t\t\t\t\t NULL,\n\t\t\t\t\t\t\t\t NULL);\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * ErrorIfOnConflictNotSupprted returns an error if an INSERT query has an\n * unsupported ON CONFLICT clause. In particular, changing the partition\n * column value or using volatile functions is not allowed.\n */\nDeferredErrorMessage *\nErrorIfOnConflictNotSupported(Query *queryTree)\n{\n\tuint32 rangeTableId = 1;\n\tListCell *setTargetCell = NULL;\n\tbool specifiesPartitionValue = false;\n\n\tCmdType commandType = queryTree->commandType;\n\tif (commandType != CMD_INSERT || queryTree->onConflict == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tOid distributedTableId = ExtractFirstCitusTableId(queryTree);\n\tVar *partitionColumn = PartitionColumn(distributedTableId, rangeTableId);\n\n\tList *onConflictSet = queryTree->onConflict->onConflictSet;\n\tNode *arbiterWhere = queryTree->onConflict->arbiterWhere;\n\tNode *onConflictWhere = queryTree->onConflict->onConflictWhere;\n\n\t/*\n\t * onConflictSet is expanded via expand_targetlist() on the standard planner.\n\t * This ends up adding all the columns to the onConflictSet even if the user\n\t * does not explicitly state the columns in the query.\n\t *\n\t * The following loop simply allows \"DO UPDATE SET part_col = table.part_col\"\n\t * types of elements in the target list, which are added by expand_targetlist().\n\t * Any other attempt to update partition column value is forbidden.\n\t */\n\tforeach(setTargetCell, onConflictSet)\n\t{\n\t\tTargetEntry *setTargetEntry = (TargetEntry *) lfirst(setTargetCell);\n\t\tbool setTargetEntryPartitionColumn = false;\n\n\t\t/* reference tables do not have partition column */\n\t\tif (partitionColumn == NULL)\n\t\t{\n\t\t\tsetTargetEntryPartitionColumn = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tOid resultRelationId = ModifyQueryResultRelationId(queryTree);\n\n\t\t\tAttrNumber targetColumnAttrNumber = InvalidAttrNumber;\n\t\t\tif (setTargetEntry->resname)\n\t\t\t{\n\t\t\t\ttargetColumnAttrNumber = get_attnum(resultRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tsetTargetEntry->resname);\n\t\t\t\tif (targetColumnAttrNumber == partitionColumn->varattno)\n\t\t\t\t{\n\t\t\t\t\tsetTargetEntryPartitionColumn = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (setTargetEntryPartitionColumn)\n\t\t{\n\t\t\tExpr *setExpr = setTargetEntry->expr;\n\t\t\tif (IsA(setExpr, Var) &&\n\t\t\t\t((Var *) setExpr)->varattno == partitionColumn->varattno)\n\t\t\t{\n\t\t\t\tspecifiesPartitionValue = false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tspecifiesPartitionValue = true;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Similarly, allow  \"DO UPDATE SET col_1 = table.col_1\" types of\n\t\t\t * target list elements. Note that, the following check allows\n\t\t\t * \"DO UPDATE SET col_1 = table.col_2\", which is not harmful.\n\t\t\t */\n\t\t\tif (IsA(setTargetEntry->expr, Var))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (contain_mutable_functions((Node *) setTargetEntry->expr))\n\t\t\t{\n\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"functions used in the DO UPDATE SET clause of \"\n\t\t\t\t\t\t\t\t\t \"INSERTs on distributed tables must be marked \"\n\t\t\t\t\t\t\t\t\t \"IMMUTABLE\",\n\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* error if either arbiter or on conflict WHERE contains a mutable function */\n\tif (contain_mutable_functions((Node *) arbiterWhere) ||\n\t\tcontain_mutable_functions((Node *) onConflictWhere))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"functions used in the WHERE clause of the \"\n\t\t\t\t\t\t\t \"ON CONFLICT clause of INSERTs on distributed \"\n\t\t\t\t\t\t\t \"tables must be marked IMMUTABLE\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tif (specifiesPartitionValue)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"modifying the partition value of rows is not \"\n\t\t\t\t\t\t\t \"allowed\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * MultiShardUpdateDeleteSupported returns the error message if the update/delete is\n * not pushdownable, otherwise it returns NULL.\n */\nstatic DeferredErrorMessage *\nMultiShardUpdateDeleteSupported(Query *originalQuery,\n\t\t\t\t\t\t\t\tPlannerRestrictionContext *plannerRestrictionContext)\n{\n\tDeferredErrorMessage *errorMessage = NULL;\n\tRangeTblEntry *resultRangeTable = ExtractResultRelationRTE(originalQuery);\n\tOid resultRelationOid = resultRangeTable->relid;\n\n\tif (HasDangerousJoinUsing(originalQuery->rtable, (Node *) originalQuery->jointree))\n\t{\n\t\terrorMessage = DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"a join with USING causes an internal naming conflict, use \"\n\t\t\t\t\t\t\t\t\t \"ON instead\",\n\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\telse if (FindNodeMatchingCheckFunction((Node *) originalQuery,\n\t\t\t\t\t\t\t\t\t\t   CitusIsVolatileFunction))\n\t{\n\t\terrorMessage = DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"functions used in UPDATE queries on distributed \"\n\t\t\t\t\t\t\t\t\t \"tables must not be VOLATILE\",\n\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\telse if (IsCitusTableType(resultRelationOid, REFERENCE_TABLE))\n\t{\n\t\terrorMessage = DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"only reference tables may be queried when targeting \"\n\t\t\t\t\t\t\t\t\t \"a reference table with multi shard UPDATE/DELETE queries \"\n\t\t\t\t\t\t\t\t\t \"with multiple tables \",\n\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\telse\n\t{\n\t\terrorMessage = DeferErrorIfUnsupportedSubqueryPushdown(\n\t\t\toriginalQuery,\n\t\t\tplannerRestrictionContext,\n\t\t\ttrue);\n\t}\n\n\treturn errorMessage;\n}\n\n\n/*\n * SingleShardUpdateDeleteSupported returns the error message if the update/delete query is\n * not routable, otherwise it returns NULL.\n */\nstatic DeferredErrorMessage *\nSingleShardUpdateDeleteSupported(Query *originalQuery,\n\t\t\t\t\t\t\t\t PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tDeferredErrorMessage *errorMessage = NULL;\n\n\t/*\n\t * We currently do not support volatile functions in update/delete statements because\n\t * the function evaluation logic does not know how to distinguish volatile functions\n\t * (that need to be evaluated per row) from stable functions (that need to be evaluated per query),\n\t * and it is also not safe to push the volatile functions down on replicated tables.\n\t */\n\tif (FindNodeMatchingCheckFunction((Node *) originalQuery,\n\t\t\t\t\t\t\t\t\t  CitusIsVolatileFunction))\n\t{\n\t\terrorMessage = DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"functions used in UPDATE queries on distributed \"\n\t\t\t\t\t\t\t\t\t \"tables must not be VOLATILE\",\n\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\treturn errorMessage;\n}\n\n\n/*\n * HasDangerousJoinUsing search jointree for unnamed JOIN USING. Check the\n * implementation of has_dangerous_join_using in ruleutils.\n */\nbool\nHasDangerousJoinUsing(List *rtableList, Node *joinTreeNode)\n{\n\tif (IsA(joinTreeNode, RangeTblRef))\n\t{\n\t\t/* nothing to do here */\n\t}\n\telse if (IsA(joinTreeNode, FromExpr))\n\t{\n\t\tFromExpr *fromExpr = (FromExpr *) joinTreeNode;\n\t\tListCell *listCell;\n\n\t\tforeach(listCell, fromExpr->fromlist)\n\t\t{\n\t\t\tif (HasDangerousJoinUsing(rtableList, (Node *) lfirst(listCell)))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\telse if (IsA(joinTreeNode, JoinExpr))\n\t{\n\t\tJoinExpr *joinExpr = (JoinExpr *) joinTreeNode;\n\n\t\t/* Is it an unnamed JOIN with USING? */\n\t\tif (joinExpr->alias == NULL && joinExpr->usingClause)\n\t\t{\n\t\t\t/*\n\t\t\t * Yes, so check each join alias var to see if any of them are not\n\t\t\t * simple references to underlying columns. If so, we have a\n\t\t\t * dangerous situation and must pick unique aliases.\n\t\t\t */\n\t\t\tRangeTblEntry *joinRTE = rt_fetch(joinExpr->rtindex, rtableList);\n\t\t\tListCell *listCell;\n\n\t\t\tforeach(listCell, joinRTE->joinaliasvars)\n\t\t\t{\n\t\t\t\tVar *aliasVar = (Var *) lfirst(listCell);\n\n\t\t\t\tif (aliasVar != NULL && !IsA(aliasVar, Var))\n\t\t\t\t{\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/* Nope, but inspect children */\n\t\tif (HasDangerousJoinUsing(rtableList, joinExpr->larg))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\tif (HasDangerousJoinUsing(rtableList, joinExpr->rarg))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\telse\n\t{\n\t\telog(ERROR, \"unrecognized node type: %d\",\n\t\t\t (int) nodeTag(joinTreeNode));\n\t}\n\treturn false;\n}\n\n\n/*\n * UpdateOrDeleteOrMergeQuery checks if the given query is an UPDATE or DELETE or\n * MERGE command. If it is, it returns true otherwise it returns false.\n */\nbool\nUpdateOrDeleteOrMergeQuery(Query *query)\n{\n\treturn (query->commandType == CMD_UPDATE ||\n\t\t\tquery->commandType == CMD_DELETE ||\n\t\t\tquery->commandType == CMD_MERGE);\n}\n\n\n/*\n * IsMergeQuery checks if the given query is a MERGE SQL command.\n */\nbool\nIsMergeQuery(Query *query)\n{\n\treturn (query->commandType == CMD_MERGE);\n}\n\n\n/*\n * If the expression contains STABLE functions which accept any parameters derived from a\n * Var returns true and sets varArgument.\n *\n * If the expression contains a CASE or COALESCE which invoke non-IMMUTABLE functions\n * returns true and sets badCoalesce.\n *\n * Assumes the expression contains no VOLATILE functions.\n *\n * Var's are allowed, but only if they are passed solely to IMMUTABLE functions\n *\n * We special-case CASE/COALESCE because those are evaluated lazily. We could evaluate\n * CASE/COALESCE expressions which don't reference Vars, or partially evaluate some\n * which do, but for now we just error out. That makes both the code and user-education\n * easier.\n */\nbool\nMasterIrreducibleExpression(Node *expression, bool *varArgument, bool *badCoalesce)\n{\n\tWalkerState data;\n\tdata.containsVar = data.varArgument = data.badCoalesce = false;\n\n\tbool result = MasterIrreducibleExpressionWalker(expression, &data);\n\n\t*varArgument |= data.varArgument;\n\t*badCoalesce |= data.badCoalesce;\n\treturn result;\n}\n\n\nstatic bool\nMasterIrreducibleExpressionWalker(Node *expression, WalkerState *state)\n{\n\tchar volatileFlag = 0;\n\tWalkerState childState = { false, false, false };\n\tbool containsDisallowedFunction = false;\n\tbool hasVolatileFunction PG_USED_FOR_ASSERTS_ONLY = false;\n\n\tif (expression == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(expression, CoalesceExpr))\n\t{\n\t\tCoalesceExpr *expr = (CoalesceExpr *) expression;\n\n\t\tif (contain_mutable_functions((Node *) (expr->args)))\n\t\t{\n\t\t\tstate->badCoalesce = true;\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * There's no need to recurse. Since there are no STABLE functions\n\t\t\t * varArgument will never be set.\n\t\t\t */\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (IsA(expression, CaseExpr))\n\t{\n\t\tif (contain_mutable_functions(expression))\n\t\t{\n\t\t\tstate->badCoalesce = true;\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tif (IsA(expression, Var))\n\t{\n\t\tstate->containsVar = true;\n\t\treturn false;\n\t}\n\n\t/*\n\t * In order for statement replication to give us consistent results it's important\n\t * that we either disallow or evaluate on the coordinator anything which has a\n\t * volatility category above IMMUTABLE. Newer versions of postgres might add node\n\t * types which should be checked in this function.\n\t *\n\t * Look through contain_mutable_functions_walker or future PG's equivalent for new\n\t * node types before bumping this version number to fix compilation; e.g. for any\n\t * PostgreSQL after 9.5, see check_functions_in_node. Review\n\t * MasterIrreducibleExpressionFunctionChecker for any changes in volatility\n\t * permissibility ordering.\n\t *\n\t * Once you've added them to this check, make sure you also evaluate them in the\n\t * executor!\n\t */\n\n\thasVolatileFunction =\n\t\tcheck_functions_in_node(expression, MasterIrreducibleExpressionFunctionChecker,\n\t\t\t\t\t\t\t\t&volatileFlag);\n\n\t/* the caller should have already checked for this */\n\tAssert(!hasVolatileFunction);\n\tAssert(volatileFlag != PROVOLATILE_VOLATILE);\n\n\tif (volatileFlag == PROVOLATILE_STABLE)\n\t{\n\t\tcontainsDisallowedFunction =\n\t\t\texpression_tree_walker(expression,\n\t\t\t\t\t\t\t\t   MasterIrreducibleExpressionWalker,\n\t\t\t\t\t\t\t\t   &childState);\n\n\t\tif (childState.containsVar)\n\t\t{\n\t\t\tstate->varArgument = true;\n\t\t}\n\n\t\tstate->badCoalesce |= childState.badCoalesce;\n\t\tstate->varArgument |= childState.varArgument;\n\n\t\treturn (containsDisallowedFunction || childState.containsVar);\n\t}\n\n\t/* keep traversing */\n\treturn expression_tree_walker(expression,\n\t\t\t\t\t\t\t\t  MasterIrreducibleExpressionWalker,\n\t\t\t\t\t\t\t\t  state);\n}\n\n\n/*\n * MasterIrreducibleExpressionFunctionChecker returns true if a provided function\n * oid corresponds to a volatile function. It also updates provided context if\n * the current volatility flag is more permissive than the provided one. It is\n * only called from check_functions_in_node as checker function.\n */\nstatic bool\nMasterIrreducibleExpressionFunctionChecker(Oid func_id, void *context)\n{\n\tchar volatileFlag = func_volatile(func_id);\n\tchar *volatileContext = (char *) context;\n\n\tif (volatileFlag == PROVOLATILE_VOLATILE || *volatileContext == PROVOLATILE_VOLATILE)\n\t{\n\t\t*volatileContext = PROVOLATILE_VOLATILE;\n\t}\n\telse if (volatileFlag == PROVOLATILE_STABLE || *volatileContext == PROVOLATILE_STABLE)\n\t{\n\t\t*volatileContext = PROVOLATILE_STABLE;\n\t}\n\telse\n\t{\n\t\t*volatileContext = PROVOLATILE_IMMUTABLE;\n\t}\n\n\treturn (volatileFlag == PROVOLATILE_VOLATILE);\n}\n\n\n/*\n * TargetEntryChangesValue determines whether the given target entry may\n * change the value given a column and a join tree.\n *\n * The function assumes that the \"targetEntry\" references given \"column\"\n * Var via its \"resname\" and is used as part of a modify query. This means\n * that, for example, for an update query, the input \"targetEntry\" constructs\n * the following assignment operation as part of the SET clause:\n *   \"col_a = expr_a \", where, \"col_a\" refers to input \"column\" Var (via\n * \"resname\") as per the assumption written above. And we want to understand\n * if \"expr_a\" (which is pointed to by targetEntry->expr) refers directly to\n * the \"column\" Var, or \"expr_a\" is a value that is implied to be equal\n * to \"column\" Var by the qualifiers of the join tree. If so, we know that\n * the value of \"col_a\" effectively cannot be changed by this assignment\n * operation.\n */\nbool\nTargetEntryChangesValue(TargetEntry *targetEntry, Var *column, FromExpr *joinTree)\n{\n\tbool isColumnValueChanged = true;\n\tExpr *setExpr = targetEntry->expr;\n\n\tif (IsA(setExpr, Var))\n\t{\n\t\tVar *newValue = (Var *) setExpr;\n\t\tif (column->varno == newValue->varno &&\n\t\t\tcolumn->varattno == newValue->varattno)\n\t\t{\n\t\t\t/*\n\t\t\t * Target entry is of the form \"SET col_a = foo.col_b\",\n\t\t\t * where foo also points to the same range table entry\n\t\t\t * and col_a and col_b are the same. So, effectively\n\t\t\t * they're literally referring to the same column.\n\t\t\t */\n\t\t\tisColumnValueChanged = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tList *restrictClauseList = WhereClauseList(joinTree);\n\t\t\tOpExpr *equalityExpr = MakeOpExpressionExtended(column, (Expr *) newValue,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tBTEqualStrategyNumber);\n\n\t\t\tbool predicateIsImplied = predicate_implied_by(list_make1(equalityExpr),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   restrictClauseList, false);\n\t\t\tif (predicateIsImplied)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Target entry is of the form\n\t\t\t\t * \"SET col_a = foo.col_b WHERE col_a = foo.col_b (AND (...))\",\n\t\t\t\t * where foo points to a different relation or it points\n\t\t\t\t * to the same relation but col_a is not the same column as col_b.\n\t\t\t\t */\n\t\t\t\tisColumnValueChanged = false;\n\t\t\t}\n\t\t}\n\t}\n\telse if (IsA(setExpr, Const))\n\t{\n\t\tConst *newValue = (Const *) setExpr;\n\t\tList *restrictClauseList = WhereClauseList(joinTree);\n\t\tOpExpr *equalityExpr = MakeOpExpression(column, BTEqualStrategyNumber);\n\t\tNode *rightOp = get_rightop((Expr *) equalityExpr);\n\n\t\tAssert(rightOp != NULL);\n\t\tAssert(IsA(rightOp, Const));\n\t\tConst *rightConst = (Const *) rightOp;\n\n\t\trightConst->constvalue = newValue->constvalue;\n\t\trightConst->constisnull = newValue->constisnull;\n\t\trightConst->constbyval = newValue->constbyval;\n\n\t\tbool predicateIsImplied = predicate_implied_by(list_make1(equalityExpr),\n\t\t\t\t\t\t\t\t\t\t\t\t\t   restrictClauseList, false);\n\t\tif (predicateIsImplied)\n\t\t{\n\t\t\t/*\n\t\t\t * Target entry is of the form\n\t\t\t * \"SET col_a = const_a WHERE col_a = const_a (AND (...))\".\n\t\t\t */\n\t\t\tisColumnValueChanged = false;\n\t\t}\n\t}\n\n\treturn isColumnValueChanged;\n}\n\n\n/*\n * RouterInsertJob builds a Job to represent an insertion performed by the provided\n * query. For inserts we always defer shard pruning and generating the task list to\n * the executor.\n */\nstatic Job *\nRouterInsertJob(Query *originalQuery)\n{\n\tAssert(originalQuery->commandType == CMD_INSERT);\n\n\tbool isMultiRowInsert = IsMultiRowInsert(originalQuery);\n\tif (isMultiRowInsert)\n\t{\n\t\t/* add default expressions to RTE_VALUES in multi-row INSERTs */\n\t\tNormalizeMultiRowInsertTargetList(originalQuery);\n\t}\n\n\tJob *job = CreateJob(originalQuery);\n\tjob->requiresCoordinatorEvaluation = RequiresCoordinatorEvaluation(originalQuery);\n\tjob->deferredPruning = true;\n\tjob->partitionKeyValue = ExtractInsertPartitionKeyValue(originalQuery);\n\n\treturn job;\n}\n\n\n/*\n * CreateJob returns a new Job for the given query.\n */\nstatic Job *\nCreateJob(Query *query)\n{\n\tJob *job = CitusMakeNode(Job);\n\tjob->jobId = UniqueJobId();\n\tjob->jobQuery = query;\n\tjob->taskList = NIL;\n\tjob->dependentJobList = NIL;\n\tjob->subqueryPushdown = false;\n\tjob->requiresCoordinatorEvaluation = false;\n\tjob->deferredPruning = false;\n\n\treturn job;\n}\n\n\n/*\n * ErrorIfNoShardsExist throws an error if the given table has no shards.\n */\nstatic void\nErrorIfNoShardsExist(CitusTableCacheEntry *cacheEntry)\n{\n\tint shardCount = cacheEntry->shardIntervalArrayLength;\n\tif (shardCount == 0)\n\t{\n\t\tOid distributedTableId = cacheEntry->relationId;\n\t\tchar *relationName = get_rel_name(distributedTableId);\n\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"could not find any shards\"),\n\t\t\t\t\t\terrdetail(\"No shards exist for distributed table \\\"%s\\\".\",\n\t\t\t\t\t\t\t\t  relationName),\n\t\t\t\t\t\terrhint(\"Run master_create_worker_shards to create shards \"\n\t\t\t\t\t\t\t\t\"and try again.\")));\n\t}\n}\n\n\n/*\n * RouterInsertTaskList generates a list of tasks for performing an INSERT on\n * a distributed table via the router executor.\n */\nList *\nRouterInsertTaskList(Query *query, bool parametersInQueryResolved,\n\t\t\t\t\t DeferredErrorMessage **planningError)\n{\n\tList *insertTaskList = NIL;\n\n\tOid distributedTableId = ExtractFirstCitusTableId(query);\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(distributedTableId);\n\n\tErrorIfNoShardsExist(cacheEntry);\n\n\tAssert(query->commandType == CMD_INSERT);\n\n\tList *modifyRouteList = BuildRoutesForInsert(query, planningError);\n\tif (*planningError != NULL)\n\t{\n\t\treturn NIL;\n\t}\n\n\tModifyRoute *modifyRoute = NULL;\n\tforeach_declared_ptr(modifyRoute, modifyRouteList)\n\t{\n\t\tTask *modifyTask = CreateTask(MODIFY_TASK);\n\t\tmodifyTask->anchorShardId = modifyRoute->shardId;\n\t\tmodifyTask->replicationModel = cacheEntry->replicationModel;\n\t\tmodifyTask->rowValuesLists = modifyRoute->rowValuesLists;\n\n\t\tRelationShard *relationShard = CitusMakeNode(RelationShard);\n\t\trelationShard->shardId = modifyRoute->shardId;\n\t\trelationShard->relationId = distributedTableId;\n\n\t\tmodifyTask->relationShardList = list_make1(relationShard);\n\t\tmodifyTask->taskPlacementList = ActiveShardPlacementList(\n\t\t\tmodifyRoute->shardId);\n\t\tmodifyTask->parametersInQueryStringResolved = parametersInQueryResolved;\n\n\t\tinsertTaskList = lappend(insertTaskList, modifyTask);\n\t}\n\n\treturn insertTaskList;\n}\n\n\n/*\n * CreateTask returns a new Task with the given type.\n */\nstatic Task *\nCreateTask(TaskType taskType)\n{\n\tTask *task = CitusMakeNode(Task);\n\ttask->taskType = taskType;\n\ttask->jobId = INVALID_JOB_ID;\n\ttask->taskId = INVALID_TASK_ID;\n\tSetTaskQueryString(task, NULL);\n\ttask->anchorShardId = INVALID_SHARD_ID;\n\ttask->taskPlacementList = NIL;\n\ttask->dependentTaskList = NIL;\n\n\ttask->partitionId = 0;\n\ttask->upstreamTaskId = INVALID_TASK_ID;\n\ttask->shardInterval = NULL;\n\ttask->assignmentConstrained = false;\n\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\ttask->relationRowLockList = NIL;\n\n\ttask->modifyWithSubquery = false;\n\ttask->partiallyLocalOrRemote = false;\n\ttask->relationShardList = NIL;\n\n\treturn task;\n}\n\n\n/*\n * ExtractFirstCitusTableId takes a given query, and finds the relationId\n * for the first distributed table in that query. If the function cannot find a\n * distributed table, it returns InvalidOid.\n *\n * We only use this function for modifications and fast path queries, which\n * should have the first distributed table in the top-level rtable.\n */\nOid\nExtractFirstCitusTableId(Query *query)\n{\n\tList *rangeTableList = query->rtable;\n\tListCell *rangeTableCell = NULL;\n\tOid distributedTableId = InvalidOid;\n\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\n\t\tif (IsCitusTable(rangeTableEntry->relid))\n\t\t{\n\t\t\tdistributedTableId = rangeTableEntry->relid;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn distributedTableId;\n}\n\n\n/*\n * RouterJob builds a Job to represent a single shard select/update/delete and\n * multiple shard update/delete queries.\n */\nJob *\nRouterJob(Query *originalQuery, PlannerRestrictionContext *plannerRestrictionContext,\n\t\t  DeferredErrorMessage **planningError)\n{\n\tuint64 shardId = INVALID_SHARD_ID;\n\tList *placementList = NIL;\n\tList *relationShardList = NIL;\n\tList *prunedShardIntervalListList = NIL;\n\tbool isMultiShardModifyQuery = false;\n\tConst *partitionKeyValue = NULL;\n\n\t/* router planner should create task even if it doesn't hit a shard at all */\n\tbool replacePrunedQueryWithDummy = true;\n\n\tbool isLocalTableModification = false;\n\n\t/* check if this query requires coordinator evaluation */\n\tbool requiresCoordinatorEvaluation = RequiresCoordinatorEvaluation(originalQuery);\n\tFastPathRestrictionContext *fastPathRestrictionContext =\n\t\tplannerRestrictionContext->fastPathRestrictionContext;\n\n\t/*\n\t * We prefer to defer shard pruning/task generation to the\n\t * execution when the parameter on the distribution key\n\t * cannot be resolved.\n\t */\n\tif (fastPathRestrictionContext->fastPathRouterQuery &&\n\t\tfastPathRestrictionContext->distributionKeyHasParam)\n\t{\n\t\tJob *job = CreateJob(originalQuery);\n\t\tjob->deferredPruning = true;\n\n\t\tereport(DEBUG2, (errmsg(\"Deferred pruning for a fast-path router \"\n\t\t\t\t\t\t\t\t\"query\")));\n\t\treturn job;\n\t}\n\telse\n\t{\n\t\t(*planningError) = PlanRouterQuery(originalQuery, plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t   &placementList, &shardId, &relationShardList,\n\t\t\t\t\t\t\t\t\t\t   &prunedShardIntervalListList,\n\t\t\t\t\t\t\t\t\t\t   replacePrunedQueryWithDummy,\n\t\t\t\t\t\t\t\t\t\t   &isMultiShardModifyQuery,\n\t\t\t\t\t\t\t\t\t\t   &partitionKeyValue,\n\t\t\t\t\t\t\t\t\t\t   &isLocalTableModification);\n\t}\n\n\tif (*planningError)\n\t{\n\t\treturn NULL;\n\t}\n\n\tJob *job = CreateJob(originalQuery);\n\tjob->partitionKeyValue = partitionKeyValue;\n\n\tif (originalQuery->resultRelation > 0)\n\t{\n\t\tRangeTblEntry *updateOrDeleteOrMergeRTE = ExtractResultRelationRTE(originalQuery);\n\n\t\tif (updateOrDeleteOrMergeRTE->rtekind == RTE_SUBQUERY)\n\t\t{\n\t\t\t/*\n\t\t\t * Not generating tasks for MERGE target relation might\n\t\t\t * result in incorrect behavior as source rows with NOT\n\t\t\t * MATCHED clause might qualify for insertion.\n\t\t\t */\n\t\t\tif (IsMergeQuery(originalQuery))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\t\terrmsg(\"Merge command is currently \"\n\t\t\t\t\t\t\t\t\t   \"unsupported with filters that \"\n\t\t\t\t\t\t\t\t\t   \"prunes down to zero shards\"),\n\t\t\t\t\t\t\t\terrhint(\"Avoid `WHERE false` clause or \"\n\t\t\t\t\t\t\t\t\t\t\"any equivalent filters that \"\n\t\t\t\t\t\t\t\t\t\t\"could prune down to zero shards\")));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * If all of the shards are pruned, we replace the\n\t\t\t\t * relation RTE into subquery RTE that returns no\n\t\t\t\t * results. However, this is not useful for UPDATE\n\t\t\t\t * and DELETE queries. Therefore, if we detect a\n\t\t\t\t * UPDATE or DELETE RTE with subquery type, we just\n\t\t\t\t * set task list to empty and return the job.\n\t\t\t\t */\n\t\t\t\tjob->taskList = NIL;\n\t\t\t\treturn job;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (isMultiShardModifyQuery)\n\t{\n\t\tjob->taskList = QueryPushdownSqlTaskList(originalQuery, job->jobId,\n\t\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext->\n\t\t\t\t\t\t\t\t\t\t\t\t relationRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t prunedShardIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t\t MODIFY_TASK,\n\t\t\t\t\t\t\t\t\t\t\t\t requiresCoordinatorEvaluation,\n\t\t\t\t\t\t\t\t\t\t\t\t planningError);\n\t\tif (*planningError)\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\t}\n\telse\n\t{\n\t\tGenerateSingleShardRouterTaskList(job, relationShardList,\n\t\t\t\t\t\t\t\t\t\t  placementList, shardId,\n\t\t\t\t\t\t\t\t\t\t  isLocalTableModification,\n\t\t\t\t\t\t\t\t\t\t  fastPathRestrictionContext->\n\t\t\t\t\t\t\t\t\t\t  delayFastPathPlanning);\n\t}\n\n\tjob->requiresCoordinatorEvaluation = requiresCoordinatorEvaluation;\n\treturn job;\n}\n\n\n/*\n * CheckAttributesMatch checks if the attributes of the Citus table and the shard\n * table match.\n *\n * It is used to ensure that the shard table has the same schema as the Citus\n * table before replacing the Citus table OID with the shard table OID in the\n * parse tree we (Citus planner) recieved from Postgres.\n */\nstatic\nbool\nCheckAttributesMatch(Oid citusTableId, Oid shardTableId)\n{\n\tbool same_schema = false;\n\tRelation citusRelation = RelationIdGetRelation(citusTableId);\n\tRelation shardRelation = RelationIdGetRelation(shardTableId);\n\n\tif (RelationIsValid(citusRelation) && RelationIsValid(shardRelation))\n\t{\n\t\tTupleDesc citusTupDesc = citusRelation->rd_att;\n\t\tTupleDesc shardTupDesc = shardRelation->rd_att;\n\n\t\tif (citusTupDesc->natts == shardTupDesc->natts)\n\t\t{\n\t\t\t/*\n\t\t\t * Do an attribute-by-attribute comparison. This is borrowed from\n\t\t\t * the Postgres function equalTupleDescs(), which we cannot use\n\t\t\t * because the citus table and shard table have different composite\n\t\t\t * types.\n\t\t\t */\n\t\t\tsame_schema = true;\n\t\t\tfor (int i = 0; i < citusTupDesc->natts && same_schema; i++)\n\t\t\t{\n\t\t\t\tForm_pg_attribute attr1 = TupleDescAttr(citusTupDesc, i);\n\t\t\t\tForm_pg_attribute attr2 = TupleDescAttr(shardTupDesc, i);\n\n\t\t\t\tif (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0)\n\t\t\t\t{\n\t\t\t\t\tsame_schema = false;\n\t\t\t\t}\n\t\t\t\tif (attr1->atttypid != attr2->atttypid)\n\t\t\t\t{\n\t\t\t\t\tsame_schema = false;\n\t\t\t\t}\n\t\t\t\tif (attr1->atttypmod != attr2->atttypmod)\n\t\t\t\t{\n\t\t\t\t\tsame_schema = false;\n\t\t\t\t}\n\t\t\t\tif (attr1->attcollation != attr2->attcollation)\n\t\t\t\t{\n\t\t\t\t\tsame_schema = false;\n\t\t\t\t}\n\n\t\t\t\t/* Record types derived from tables could have dropped fields. */\n\t\t\t\tif (attr1->attisdropped != attr2->attisdropped)\n\t\t\t\t{\n\t\t\t\t\tsame_schema = false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tRelationClose(citusRelation);\n\tRelationClose(shardRelation);\n\treturn same_schema;\n}\n\n\n/*\n * CheckAndBuildDelayedFastPathPlan() - if the query being planned is a fast\n * path query, not marked for deferred pruning and the placement for the task\n * is not a dummy placement then if the placement is local to this node we can\n * take a shortcut of replacing the OID of the citus table with the OID of the\n * shard in the query tree and plan that directly, instead of deparsing the\n * parse tree to a SQL query on the shard and parsing and planning that in\n * the local executor. Instead, the local executor can use the plan created\n * here.\n */\nvoid\nCheckAndBuildDelayedFastPathPlan(DistributedPlanningContext *planContext,\n\t\t\t\t\t\t\t\t DistributedPlan *plan)\n{\n\tFastPathRestrictionContext *fastPathContext =\n\t\tplanContext->plannerRestrictionContext->fastPathRestrictionContext;\n\n\tif (!fastPathContext->delayFastPathPlanning)\n\t{\n\t\treturn;\n\t}\n\n\tJob *job = plan->workerJob;\n\tAssert(job != NULL);\n\n\tif (job->deferredPruning)\n\t{\n\t\t/* Execution time pruning => don't know which shard at this point */\n\t\tplanContext->plan = FastPathPlanner(planContext->originalQuery,\n\t\t\t\t\t\t\t\t\t\t\tplanContext->query,\n\t\t\t\t\t\t\t\t\t\t\tplanContext->boundParams);\n\t\treturn;\n\t}\n\n\tList *tasks = job->taskList;\n\tAssert(list_length(tasks) == 1);\n\tTask *task = (Task *) linitial(tasks);\n\tList *placements = task->taskPlacementList;\n\tint32 localGroupId = GetLocalGroupId();\n\tShardPlacement *primaryPlacement = (ShardPlacement *) linitial(placements);\n\n\tbool isLocalExecution = !IsDummyPlacement(primaryPlacement) &&\n\t\t\t\t\t\t\t(primaryPlacement->groupId == localGroupId);\n\tbool canBuildLocalPlan = true;\n\n\tif (isLocalExecution)\n\t{\n\t\tList *relationShards = task->relationShardList;\n\t\tAssert(list_length(relationShards) == 1);\n\t\tRelationShard *relationShard = (RelationShard *) linitial(relationShards);\n\t\tAssert(relationShard->shardId == primaryPlacement->shardId);\n\n\t\t/*\n\t\t * Today FastPathRouterQuery() doesn't set delayFastPathPlanning to true for\n\t\t * reference tables. We should be looking at 1 placement, or their replication\n\t\t * factor.\n\t\t */\n\t\tAssert(list_length(placements) == 1 || list_length(placements) ==\n\t\t\t   TableShardReplicationFactor(relationShard->relationId));\n\n\t\tcanBuildLocalPlan = ConvertToQueryOnShard(planContext->query,\n\t\t\t\t\t\t\t\t\t\t\t\t  relationShard->relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t  relationShard->shardId);\n\t\tif (canBuildLocalPlan)\n\t\t{\n\t\t\t/* Plan the query with the new shard relation id */\n\t\t\tplanContext->plan = standard_planner(planContext->query, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t planContext->cursorOptions,\n\t\t\t\t\t\t\t\t\t\t\t\t planContext->boundParams);\n\t\t\tSetTaskQueryPlan(task, job->jobQuery, planContext->plan);\n\n\t\t\tereport(DEBUG2, (errmsg(\n\t\t\t\t\t\t\t\t \"Fast-path router query: created local execution plan \"\n\t\t\t\t\t\t\t\t \"to avoid deparse and compile of shard query\")));\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/*\n\t * Either the shard is not local to this node, or it was not safe to replace\n\t * the OIDs in the parse tree; in any case we fall back to generating the shard\n\t * query and compiling that.\n\t */\n\tAssert(!isLocalExecution || (isLocalExecution && !canBuildLocalPlan));\n\n\t/* Fall back to fast path planner and generating SQL query on the shard */\n\tplanContext->plan = FastPathPlanner(planContext->originalQuery,\n\t\t\t\t\t\t\t\t\t\tplanContext->query,\n\t\t\t\t\t\t\t\t\t\tplanContext->boundParams);\n\tUpdateRelationToShardNames((Node *) job->jobQuery, task->relationShardList);\n\tSetTaskQueryIfShouldLazyDeparse(task, job->jobQuery);\n}\n\n\n/*\n * ConvertToQueryOnShard() converts the given query on a citus table (identified by\n * citusTableOid) to a query on a shard (identified by shardId).\n *\n * The function assumes that the query is a \"fast path\" query - it has only one\n * RangeTblEntry and one RTEPermissionInfo.\n *\n * It acquires the same lock on the shard that was acquired on the citus table\n * by the Postgres parser. It checks that the attribute numbers and metadata of\n * the shard table and citus table are identical - otherwise it is not safe\n * to proceed with this shortcut. Assuming the attributes do match, the actual\n * conversion involves changing the target list entries that reference the\n * citus table's oid to reference the shard's relation id instead. Finally,\n * it changes the RangeTblEntry's relid to the shard's relation id and (PG16+)\n * changes the RTEPermissionInfo's relid to the shard's relation id also.\n * At this point the Query is ready for the postgres planner.\n */\nstatic bool\nConvertToQueryOnShard(Query *query, Oid citusTableOid, Oid shardId)\n{\n\tAssert(list_length(query->rtable) == 1\n\t#if PG_VERSION_NUM >= PG_VERSION_18\n\t\t   || (list_length(query->rtable) == 2 && query->hasGroupRTE)\n\t#endif\n\t\t   );\n\tRangeTblEntry *citusTableRte = (RangeTblEntry *) linitial(query->rtable);\n\tAssert(citusTableRte->relid == citusTableOid);\n\n\tconst char *citusTableName = get_rel_name(citusTableOid);\n\tAssert(citusTableName != NULL);\n\n\t/* construct shard relation name */\n\tchar *shardRelationName = pstrdup(citusTableName);\n\tAppendShardIdToName(&shardRelationName, shardId);\n\n\t/* construct the schema name */\n\tchar *schemaName = get_namespace_name(get_rel_namespace(citusTableOid));\n\n\t/* now construct a range variable for the shard */\n\tRangeVar shardRangeVar = {\n\t\t.relname = shardRelationName,\n\t\t.schemaname = schemaName,\n\t\t.inh = citusTableRte->inh,\n\t\t.relpersistence = RELPERSISTENCE_PERMANENT,\n\t};\n\n\t/* Must apply the same lock to the shard that was applied to the citus table */\n\tOid shardRelationId = RangeVarGetRelidExtended(&shardRangeVar,\n\t\t\t\t\t\t\t\t\t\t\t\t   citusTableRte->rellockmode,\n\t\t\t\t\t\t\t\t\t\t\t\t   0, NULL, NULL);\n\n\t/* Verify that the attributes of citus table and shard table match */\n\tif (!CheckAttributesMatch(citusTableOid, shardRelationId))\n\t{\n\t\t/* There is a difference between the attributes of the citus\n\t\t * table and the shard table. This can happen if there is a DROP\n\t\t * COLUMN on the citus table. In this case, we cannot\n\t\t * convert the query to a shard query, so clean up and return.\n\t\t */\n\t\tUnlockRelationOid(shardRelationId, citusTableRte->rellockmode);\n\t\tereport(DEBUG2, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t errmsg(\n\t\t\t\t\t\t\t \"Router planner fast path cannot modify parse tree for local execution: shard table \\\"%s.%s\\\" does not match the \"\n\t\t\t\t\t\t\t \"distributed table \\\"%s.%s\\\"\",\n\t\t\t\t\t\t\t schemaName, shardRelationName, schemaName,\n\t\t\t\t\t\t\t citusTableName)));\n\t\tpfree(shardRelationName);\n\t\tpfree(schemaName);\n\n\t\treturn false;\n\t}\n\n\t/* Change the target list entries that reference the original citus table's relation id */\n\tListCell *lc = NULL;\n\tforeach(lc, query->targetList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(lc);\n\t\tif (targetEntry->resorigtbl == citusTableOid)\n\t\t{\n\t\t\ttargetEntry->resorigtbl = shardRelationId;\n\t\t}\n\t}\n\n\t/* Change the range table entry's oid to that of the shard's */\n\tAssert(shardRelationId != InvalidOid);\n\tcitusTableRte->relid = shardRelationId;\n\n\t/* Change the range table permission oid to that of the shard's (PG16+) */\n\tAssert(list_length(query->rteperminfos) == 1);\n\tRTEPermissionInfo *rtePermInfo = (RTEPermissionInfo *) linitial(query->rteperminfos);\n\trtePermInfo->relid = shardRelationId;\n\n\treturn true;\n}\n\n\n/*\n * GenerateSingleShardRouterTaskList is a wrapper around other corresponding task\n * list generation functions specific to single shard selects and modifications.\n *\n * The function updates the input job's taskList in-place.\n */\nvoid\nGenerateSingleShardRouterTaskList(Job *job, List *relationShardList,\n\t\t\t\t\t\t\t\t  List *placementList, uint64 shardId, bool\n\t\t\t\t\t\t\t\t  isLocalTableModification, bool delayedFastPath)\n{\n\tQuery *originalQuery = job->jobQuery;\n\n\tif (originalQuery->commandType == CMD_SELECT)\n\t{\n\t\tSetJobColocationId(job);\n\n\t\tjob->taskList = SingleShardTaskList(originalQuery, job->jobId,\n\t\t\t\t\t\t\t\t\t\t\trelationShardList, placementList,\n\t\t\t\t\t\t\t\t\t\t\tshardId,\n\t\t\t\t\t\t\t\t\t\t\tjob->parametersInJobQueryResolved,\n\t\t\t\t\t\t\t\t\t\t\tisLocalTableModification,\n\t\t\t\t\t\t\t\t\t\t\tjob->partitionKeyValue, job->colocationId,\n\t\t\t\t\t\t\t\t\t\t\tdelayedFastPath);\n\n\t\t/*\n\t\t * Queries to reference tables, or distributed tables with multiple replica's have\n\t\t * their task placements reordered according to the configured\n\t\t * task_assignment_policy. This is only applicable to select queries as the modify\n\t\t * queries will _always_ be executed on all placements.\n\t\t *\n\t\t * We also ignore queries that are targeting only intermediate results (e.g., no\n\t\t * valid anchorShardId).\n\t\t */\n\t\tif (shardId != INVALID_SHARD_ID)\n\t\t{\n\t\t\tReorderTaskPlacementsByTaskAssignmentPolicy(job, TaskAssignmentPolicy,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tplacementList);\n\t\t}\n\t}\n\telse if (shardId == INVALID_SHARD_ID && !isLocalTableModification)\n\t{\n\t\t/* modification that prunes to 0 shards */\n\t\tjob->taskList = NIL;\n\t}\n\telse\n\t{\n\t\tSetJobColocationId(job);\n\n\t\tjob->taskList = SingleShardTaskList(originalQuery, job->jobId,\n\t\t\t\t\t\t\t\t\t\t\trelationShardList, placementList,\n\t\t\t\t\t\t\t\t\t\t\tshardId,\n\t\t\t\t\t\t\t\t\t\t\tjob->parametersInJobQueryResolved,\n\t\t\t\t\t\t\t\t\t\t\tisLocalTableModification,\n\t\t\t\t\t\t\t\t\t\t\tjob->partitionKeyValue, job->colocationId,\n\t\t\t\t\t\t\t\t\t\t\tdelayedFastPath);\n\t}\n}\n\n\n/*\n * ReorderTaskPlacementsByTaskAssignmentPolicy applies selective reordering for supported\n * TaskAssignmentPolicyTypes.\n *\n * Supported Types\n * - TASK_ASSIGNMENT_ROUND_ROBIN round robin schedule queries among placements\n *\n * By default it does not reorder the task list, implying a first-replica strategy.\n */\nstatic void\nReorderTaskPlacementsByTaskAssignmentPolicy(Job *job,\n\t\t\t\t\t\t\t\t\t\t\tTaskAssignmentPolicyType taskAssignmentPolicy,\n\t\t\t\t\t\t\t\t\t\t\tList *placementList)\n{\n\tif (taskAssignmentPolicy == TASK_ASSIGNMENT_ROUND_ROBIN)\n\t{\n\t\t/*\n\t\t * We hit a single shard on router plans, and there should be only\n\t\t * one task in the task list\n\t\t */\n\t\tAssert(list_length(job->taskList) == 1);\n\t\tTask *task = (Task *) linitial(job->taskList);\n\n\t\t/*\n\t\t * For round-robin SELECT queries, we don't want to include the coordinator\n\t\t * because the user is trying to distributed the load across nodes via\n\t\t * round-robin policy. Otherwise, the local execution would prioritize\n\t\t * executing the local tasks and especially for reference tables on the\n\t\t * coordinator this would prevent load balancing across nodes.\n\t\t *\n\t\t * For other worker nodes in Citus MX, we let the local execution to kick-in\n\t\t * even for round-robin policy, that's because we expect the clients to evenly\n\t\t * connect to the worker nodes.\n\t\t */\n\t\tAssert(ReadOnlyTask(task->taskType));\n\t\tplacementList = RemoveCoordinatorPlacementIfNotSingleNode(placementList);\n\n\t\t/* reorder the placement list */\n\t\tList *reorderedPlacementList = RoundRobinReorder(placementList);\n\t\ttask->taskPlacementList = reorderedPlacementList;\n\n\t\tShardPlacement *primaryPlacement = (ShardPlacement *) linitial(\n\t\t\treorderedPlacementList);\n\t\tereport(DEBUG3, (errmsg(\"assigned task %u to node %s:%u\", task->taskId,\n\t\t\t\t\t\t\t\tprimaryPlacement->nodeName,\n\t\t\t\t\t\t\t\tprimaryPlacement->nodePort)));\n\t}\n}\n\n\n/*\n * RemoveCoordinatorPlacementIfNotSingleNode gets a task placement list and returns the list\n * by removing the placement belonging to the coordinator (if any).\n *\n * If the list has a single element or no placements on the coordinator, the list\n * returned is unmodified.\n */\nList *\nRemoveCoordinatorPlacementIfNotSingleNode(List *placementList)\n{\n\tListCell *placementCell = NULL;\n\n\tif (list_length(placementList) < 2)\n\t{\n\t\treturn placementList;\n\t}\n\n\tforeach(placementCell, placementList)\n\t{\n\t\tShardPlacement *placement = (ShardPlacement *) lfirst(placementCell);\n\n\t\tif (placement->groupId == COORDINATOR_GROUP_ID)\n\t\t{\n\t\t\treturn list_delete_ptr(placementList, placement);\n\t\t}\n\t}\n\n\treturn placementList;\n}\n\n\n/*\n * SingleShardTaskList generates a task for single shard query\n * and returns it as a list.\n */\nstatic List *\nSingleShardTaskList(Query *query, uint64 jobId, List *relationShardList,\n\t\t\t\t\tList *placementList, uint64 shardId,\n\t\t\t\t\tbool parametersInQueryResolved,\n\t\t\t\t\tbool isLocalTableModification, Const *partitionKeyValue,\n\t\t\t\t\tint colocationId, bool delayedFastPath)\n{\n\tTaskType taskType = READ_TASK;\n\tchar replicationModel = 0;\n\n\tif (query->commandType != CMD_SELECT)\n\t{\n\t\tList *rangeTableList = NIL;\n\t\tExtractRangeTableEntryWalker((Node *) query, &rangeTableList);\n\n\t\tRangeTblEntry *updateOrDeleteRTE = ExtractResultRelationRTE(query);\n\t\tAssert(updateOrDeleteRTE != NULL);\n\n\t\tCitusTableCacheEntry *modificationTableCacheEntry = NULL;\n\t\tif (IsCitusTable(updateOrDeleteRTE->relid))\n\t\t{\n\t\t\tmodificationTableCacheEntry = GetCitusTableCacheEntry(\n\t\t\t\tupdateOrDeleteRTE->relid);\n\t\t}\n\n\t\tif (IsCitusTableType(updateOrDeleteRTE->relid, REFERENCE_TABLE) &&\n\t\t\tSelectsFromDistributedTable(rangeTableList, query))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"cannot perform select on a distributed table \"\n\t\t\t\t\t\t\t\t   \"and modify a reference table\")));\n\t\t}\n\n\t\ttaskType = MODIFY_TASK;\n\t\tif (modificationTableCacheEntry)\n\t\t{\n\t\t\treplicationModel = modificationTableCacheEntry->replicationModel;\n\t\t}\n\t}\n\n\tif (taskType == READ_TASK && query->hasModifyingCTE)\n\t{\n\t\t/* assume ErrorIfQueryHasUnroutableModifyingCTE checked query already */\n\n\t\tCommonTableExpr *cte = NULL;\n\t\tforeach_declared_ptr(cte, query->cteList)\n\t\t{\n\t\t\tQuery *cteQuery = (Query *) cte->ctequery;\n\n\t\t\tif (cteQuery->commandType != CMD_SELECT)\n\t\t\t{\n\t\t\t\tRangeTblEntry *updateOrDeleteRTE = ExtractResultRelationRTE(cteQuery);\n\t\t\t\tCitusTableCacheEntry *modificationTableCacheEntry =\n\t\t\t\t\tGetCitusTableCacheEntry(\n\t\t\t\t\t\tupdateOrDeleteRTE->relid);\n\n\t\t\t\ttaskType = MODIFY_TASK;\n\t\t\t\treplicationModel = modificationTableCacheEntry->replicationModel;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tTask *task = CreateTask(taskType);\n\ttask->isLocalTableModification = isLocalTableModification;\n\tList *relationRowLockList = NIL;\n\n\tRowLocksOnRelations((Node *) query, &relationRowLockList);\n\n\t/*\n\t * For performance reasons, we skip generating the queryString. For local\n\t * execution this is not needed, so we wait until the executor determines\n\t * that the query cannot be executed locally.\n\t */\n\ttask->taskPlacementList = placementList;\n\ttask->partitionKeyValue = partitionKeyValue;\n\ttask->colocationId = colocationId;\n\tif (!delayedFastPath)\n\t{\n\t\tSetTaskQueryIfShouldLazyDeparse(task, query);\n\t}\n\ttask->anchorShardId = shardId;\n\ttask->jobId = jobId;\n\ttask->relationShardList = relationShardList;\n\ttask->relationRowLockList = relationRowLockList;\n\ttask->replicationModel = replicationModel;\n\ttask->parametersInQueryStringResolved = parametersInQueryResolved;\n\n\treturn list_make1(task);\n}\n\n\n/*\n * RowLocksOnRelations forms the list for range table IDs and corresponding\n * row lock modes.\n */\nstatic bool\nRowLocksOnRelations(Node *node, List **relationRowLockList)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\t\tListCell *rowMarkCell = NULL;\n\n\t\tforeach(rowMarkCell, query->rowMarks)\n\t\t{\n\t\t\tRowMarkClause *rowMarkClause = (RowMarkClause *) lfirst(rowMarkCell);\n\t\t\tRangeTblEntry *rangeTable = rt_fetch(rowMarkClause->rti, query->rtable);\n\t\t\tOid relationId = rangeTable->relid;\n\n\t\t\tif (IsCitusTable(relationId))\n\t\t\t{\n\t\t\t\tRelationRowLock *relationRowLock = CitusMakeNode(RelationRowLock);\n\t\t\t\trelationRowLock->relationId = relationId;\n\t\t\t\trelationRowLock->rowLockStrength = rowMarkClause->strength;\n\t\t\t\t*relationRowLockList = lappend(*relationRowLockList, relationRowLock);\n\t\t\t}\n\t\t}\n\n\t\treturn query_tree_walker(query, RowLocksOnRelations, relationRowLockList, 0);\n\t}\n\telse\n\t{\n\t\treturn expression_tree_walker(node, RowLocksOnRelations, relationRowLockList);\n\t}\n}\n\n\n/*\n * SelectsFromDistributedTable checks if there is a select on a distributed\n * table by looking into range table entries.\n */\nstatic bool\nSelectsFromDistributedTable(List *rangeTableList, Query *query)\n{\n\tListCell *rangeTableCell = NULL;\n\tRangeTblEntry *resultRangeTableEntry = NULL;\n\n\tif (query->resultRelation > 0)\n\t{\n\t\tresultRangeTableEntry = ExtractResultRelationRTE(query);\n\t}\n\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\n\t\tif (rangeTableEntry->relid == InvalidOid)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (rangeTableEntry->relkind == RELKIND_VIEW ||\n\t\t\trangeTableEntry->relkind == RELKIND_MATVIEW)\n\t\t{\n\t\t\t/*\n\t\t\t * Skip over views, which would error out in GetCitusTableCacheEntry.\n\t\t\t * Distributed tables within (regular) views are already in rangeTableList.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(\n\t\t\trangeTableEntry->relid);\n\t\tif (IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE) &&\n\t\t\t(resultRangeTableEntry == NULL || resultRangeTableEntry->relid !=\n\t\t\t rangeTableEntry->relid))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * PlanRouterQuery runs router pruning logic for SELECT, UPDATE, DELETE, and\n * MERGE queries. If there are shards present and query is routable, all RTEs\n * have been updated to point to the relevant shards in the originalQuery. Also,\n * placementList is filled with the list of worker nodes that has all the\n * required shard placements for the query execution. anchorShardId is set to\n * the first pruned shardId of the given query. Finally, relationShardList is\n * filled with the list of relation-to-shard mappings for the query.\n *\n * If the given query is not routable, it fills planningError with the related\n * DeferredErrorMessage. The caller can check this error message to see if query\n * is routable or not.\n *\n * Note: If the query prunes down to 0 shards due to filters (e.g. WHERE false),\n * or the query has only read_intermediate_result calls (no relations left after\n * recursively planning CTEs and subqueries), then it will be assigned to an\n * arbitrary worker node in a round-robin fashion.\n *\n * Relations that prune down to 0 shards are replaced by subqueries returning\n * 0 values in UpdateRelationToShardNames.\n */\nDeferredErrorMessage *\nPlanRouterQuery(Query *originalQuery,\n\t\t\t\tPlannerRestrictionContext *plannerRestrictionContext,\n\t\t\t\tList **placementList, uint64 *anchorShardId, List **relationShardList,\n\t\t\t\tList **prunedShardIntervalListList,\n\t\t\t\tbool replacePrunedQueryWithDummy, bool *multiShardModifyQuery,\n\t\t\t\tConst **partitionValueConst,\n\t\t\t\tbool *isLocalTableModification)\n{\n\tbool isMultiShardQuery = false;\n\tDeferredErrorMessage *planningError = NULL;\n\tbool shardsPresent = false;\n\tCmdType commandType = originalQuery->commandType;\n\tOid targetRelationId = InvalidOid;\n\tbool fastPathRouterQuery =\n\t\tplannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery;\n\n\t*placementList = NIL;\n\n\t/*\n\t * When FastPathRouterQuery() returns true, we know that standard_planner() has\n\t * not been called. Thus, restriction information is not avaliable and we do the\n\t * shard pruning based on the distribution column in the quals of the query.\n\t */\n\tif (fastPathRouterQuery)\n\t{\n\t\tConst *distributionKeyValue =\n\t\t\tplannerRestrictionContext->fastPathRestrictionContext->distributionKeyValue;\n\n\t\tList *shardIntervalList =\n\t\t\tTargetShardIntervalForFastPathQuery(originalQuery, &isMultiShardQuery,\n\t\t\t\t\t\t\t\t\t\t\t\tdistributionKeyValue,\n\t\t\t\t\t\t\t\t\t\t\t\tpartitionValueConst);\n\t\tAssert(!isMultiShardQuery);\n\n\t\t*prunedShardIntervalListList = shardIntervalList;\n\t\tereport(DEBUG2, (errmsg(\"Distributed planning for a fast-path router \"\n\t\t\t\t\t\t\t\t\"query\")));\n\t}\n\telse\n\t{\n\t\t*prunedShardIntervalListList =\n\t\t\tTargetShardIntervalsForRestrictInfo(plannerRestrictionContext->\n\t\t\t\t\t\t\t\t\t\t\t\trelationRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t&isMultiShardQuery,\n\t\t\t\t\t\t\t\t\t\t\t\tpartitionValueConst);\n\t}\n\n\tif (isMultiShardQuery)\n\t{\n\t\t/*\n\t\t * If multiShardQuery is true and it is a type of SELECT query, then\n\t\t * return deferred error. We do not support multi-shard SELECT queries\n\t\t * with this code path.\n\t\t */\n\t\tif (commandType == CMD_SELECT)\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"Router planner cannot handle multi-shard select queries\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\n\t\tAssert(UpdateOrDeleteOrMergeQuery(originalQuery));\n\n\t\tif (!IsMergeQuery(originalQuery))\n\t\t{\n\t\t\tplanningError = ModifyQuerySupported(originalQuery, originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t isMultiShardQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\n\t\t}\n\n\t\tif (planningError != NULL)\n\t\t{\n\t\t\treturn planningError;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t*multiShardModifyQuery = true;\n\t\t\treturn planningError;\n\t\t}\n\t}\n\n\t*relationShardList =\n\t\tRelationShardListForShardIntervalList(*prunedShardIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t  &shardsPresent);\n\n\tif (!EnableNonColocatedRouterQueryPushdown &&\n\t\t!AllShardsColocated(*relationShardList))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"router planner does not support queries that \"\n\t\t\t\t\t\t\t \"reference non-colocated distributed tables\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tif (!shardsPresent && !replacePrunedQueryWithDummy)\n\t{\n\t\t/*\n\t\t * For INSERT ... SELECT, this query could be still a valid for some other target\n\t\t * shard intervals. Thus, we should return empty list if there aren't any matching\n\t\t * workers, so that the caller can decide what to do with this task.\n\t\t */\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * We bail out if there are RTEs that prune multiple shards above, but\n\t * there can also be multiple RTEs that reference the same relation.\n\t */\n\tif (RelationPrunesToMultipleShards(*relationShardList))\n\t{\n\t\tplanningError = DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t  \"cannot run command which targets \"\n\t\t\t\t\t\t\t\t\t  \"multiple shards\", NULL, NULL);\n\t\treturn planningError;\n\t}\n\n\t/* we need anchor shard id for select queries with router planner */\n\tuint64 shardId = GetAnchorShardId(*prunedShardIntervalListList);\n\n\t/* both Postgres tables and materialized tables are locally avaliable */\n\tRTEListProperties *rteProperties = GetRTEListPropertiesForQuery(originalQuery);\n\n\tif (isLocalTableModification)\n\t{\n\t\t*isLocalTableModification =\n\t\t\tIsLocalTableModification(targetRelationId, originalQuery, shardId,\n\t\t\t\t\t\t\t\t\t rteProperties);\n\t}\n\n\tbool hasPostgresLocalRelation =\n\t\trteProperties->hasPostgresLocalTable || rteProperties->hasMaterializedView;\n\tList *taskPlacementList =\n\t\tCreateTaskPlacementListForShardIntervals(*prunedShardIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t\t shardsPresent,\n\t\t\t\t\t\t\t\t\t\t\t\t replacePrunedQueryWithDummy,\n\t\t\t\t\t\t\t\t\t\t\t\t hasPostgresLocalRelation);\n\tif (taskPlacementList == NIL)\n\t{\n\t\tplanningError = DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t  \"found no worker with all shard placements\",\n\t\t\t\t\t\t\t\t\t  NULL, NULL);\n\t\treturn planningError;\n\t}\n\n\t/*\n\t * If this is an UPDATE or DELETE query which requires coordinator evaluation,\n\t * don't try update shard names, and postpone that to execution phase. Also, if\n\t * this is a delayed fast path query, we don't update the shard names\n\t * either, as the shard names will be updated in the fast path query planner.\n\t */\n\tbool isUpdateOrDelete = UpdateOrDeleteOrMergeQuery(originalQuery);\n\tbool delayedFastPath =\n\t\tplannerRestrictionContext->fastPathRestrictionContext->delayFastPathPlanning;\n\tif (!(isUpdateOrDelete && RequiresCoordinatorEvaluation(originalQuery)) &&\n\t\t!delayedFastPath)\n\t{\n\t\tUpdateRelationToShardNames((Node *) originalQuery, *relationShardList);\n\t}\n\n\t*multiShardModifyQuery = false;\n\t*placementList = taskPlacementList;\n\t*anchorShardId = shardId;\n\n\treturn planningError;\n}\n\n\n/*\n * AllShardsColocated returns true if all the shards in the given relationShardList\n * have colocated tables and are on the same shard index.\n */\nstatic bool\nAllShardsColocated(List *relationShardList)\n{\n\tRelationShard *relationShard = NULL;\n\tint shardIndex = -1;\n\tint colocationId = -1;\n\tCitusTableType tableType = ANY_CITUS_TABLE_TYPE;\n\n\tforeach_declared_ptr(relationShard, relationShardList)\n\t{\n\t\tOid relationId = relationShard->relationId;\n\t\tuint64 shardId = relationShard->shardId;\n\t\tif (shardId == INVALID_SHARD_ID)\n\t\t{\n\t\t\t/* intermediate results are always colocated, so ignore */\n\t\t\tcontinue;\n\t\t}\n\n\t\tCitusTableCacheEntry *tableEntry = LookupCitusTableCacheEntry(relationId);\n\t\tif (tableEntry == NULL)\n\t\t{\n\t\t\t/* local tables never colocated */\n\t\t\treturn false;\n\t\t}\n\n\t\tCitusTableType currentTableType = GetCitusTableType(tableEntry);\n\t\tif (currentTableType == REFERENCE_TABLE)\n\t\t{\n\t\t\t/*\n\t\t\t * Reference tables are always colocated so it is\n\t\t\t * safe to skip them.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\t\telse if (IsCitusTableTypeCacheEntry(tableEntry, DISTRIBUTED_TABLE))\n\t\t{\n\t\t\tif (tableType == ANY_CITUS_TABLE_TYPE)\n\t\t\t{\n\t\t\t\ttableType = currentTableType;\n\t\t\t}\n\t\t\telse if (tableType != currentTableType)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We cannot qualify different types of distributed tables\n\t\t\t\t * as colocated.\n\t\t\t\t */\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (currentTableType == RANGE_DISTRIBUTED ||\n\t\t\t\tcurrentTableType == APPEND_DISTRIBUTED)\n\t\t\t{\n\t\t\t\t/* we do not have further strict colocation checks */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tint currentColocationId = TableColocationId(relationId);\n\t\tif (colocationId == -1)\n\t\t{\n\t\t\tcolocationId = currentColocationId;\n\t\t}\n\t\telse if (colocationId != currentColocationId)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tint currentIndex = ShardIndex(LoadShardInterval(shardId));\n\t\tif (shardIndex == -1)\n\t\t{\n\t\t\tshardIndex = currentIndex;\n\t\t}\n\t\telse if (shardIndex != currentIndex)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ContainsOnlyLocalOrReferenceTables returns true if there are no distributed\n * tables in the query. In other words, the query might reference only local\n * tables and/or reference tables, but no fully distributed tables.\n */\nbool\nContainsOnlyLocalOrReferenceTables(RTEListProperties *rteProperties)\n{\n\t/* If hasDistributedTable is false, then all tables are either local or reference. */\n\treturn !rteProperties->hasDistributedTable;\n}\n\n\n/*\n * CreateTaskPlacementListForShardIntervals returns a list of shard placements\n * on which it can access all shards in shardIntervalListList, which contains\n * a list of shards for each relation in the query.\n *\n * If the query contains a local table then hasLocalRelation should be set to\n * true. In that case, CreateTaskPlacementListForShardIntervals only returns\n * a placement for the local node or an empty list if the shards cannot be\n * accessed locally.\n *\n * If generateDummyPlacement is true and there are no shards that need to be\n * accessed to answer the query (shardsPresent is false), then a single\n * placement is returned that is either local or follows a round-robin policy.\n * A typical example is a router query that only reads an intermediate result.\n * This will happen on the coordinator, unless the user wants to balance the\n * load by setting the citus.task_assignment_policy.\n */\nList *\nCreateTaskPlacementListForShardIntervals(List *shardIntervalListList, bool shardsPresent,\n\t\t\t\t\t\t\t\t\t\t bool generateDummyPlacement,\n\t\t\t\t\t\t\t\t\t\t bool hasLocalRelation)\n{\n\tList *placementList = NIL;\n\n\tif (shardsPresent)\n\t{\n\t\t/*\n\t\t * Determine the workers that have all shard placements, if any.\n\t\t */\n\t\tList *shardPlacementList =\n\t\t\tPlacementsForWorkersContainingAllShards(shardIntervalListList);\n\n\t\tif (hasLocalRelation)\n\t\t{\n\t\t\tShardPlacement *taskPlacement = NULL;\n\n\t\t\t/*\n\t\t\t * If there is a local table, we only allow the local placement to\n\t\t\t * be used. If there is none, we disallow the query.\n\t\t\t */\n\t\t\tforeach_declared_ptr(taskPlacement, shardPlacementList)\n\t\t\t{\n\t\t\t\tif (taskPlacement->groupId == GetLocalGroupId())\n\t\t\t\t{\n\t\t\t\t\tplacementList = lappend(placementList, taskPlacement);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tplacementList = shardPlacementList;\n\t\t}\n\t}\n\telse if (generateDummyPlacement)\n\t{\n\t\tShardPlacement *dummyPlacement = CreateDummyPlacement(hasLocalRelation);\n\n\t\tplacementList = list_make1(dummyPlacement);\n\t}\n\n\treturn placementList;\n}\n\n\n/*\n * CreateLocalDummyPlacement creates a dummy placement for the local node that\n * can be used for queries that don't involve any shards. The typical examples\n * are:\n *       (a) queries that consist of only intermediate results\n *       (b) queries that hit zero shards (... WHERE false;)\n */\nstatic ShardPlacement *\nCreateLocalDummyPlacement()\n{\n\tShardPlacement *dummyPlacement = CitusMakeNode(ShardPlacement);\n\tdummyPlacement->nodeId = LOCAL_NODE_ID;\n\tdummyPlacement->nodeName = LocalHostName;\n\tdummyPlacement->nodePort = PostPortNumber;\n\tdummyPlacement->groupId = GetLocalGroupId();\n\treturn dummyPlacement;\n}\n\n\n/*\n * CreateDummyPlacement creates a dummy placement that can be used for queries\n * that don't involve any shards. The typical examples are:\n *       (a) queries that consist of only intermediate results\n *       (b) queries that hit zero shards (... WHERE false;)\n *\n * If round robin policy is set, the placement could be on any node in pg_dist_node.\n * Else, the local node is set for the placement.\n *\n * Queries can also involve local tables. In that case we always use the local\n * node.\n */\nstatic ShardPlacement *\nCreateDummyPlacement(bool hasLocalRelation)\n{\n\tstatic uint32 zeroShardQueryRoundRobin = 0;\n\n\tif (TaskAssignmentPolicy != TASK_ASSIGNMENT_ROUND_ROBIN || hasLocalRelation)\n\t{\n\t\treturn CreateLocalDummyPlacement();\n\t}\n\n\tList *workerNodeList = ActiveReadableNonCoordinatorNodeList();\n\tif (workerNodeList == NIL)\n\t{\n\t\t/*\n\t\t * We want to round-robin over the workers, but there are no workers.\n\t\t * To make sure the query can still succeed we fall back to returning\n\t\t * a local dummy placement.\n\t\t */\n\t\treturn CreateLocalDummyPlacement();\n\t}\n\n\tint workerNodeCount = list_length(workerNodeList);\n\tint workerNodeIndex = zeroShardQueryRoundRobin % workerNodeCount;\n\tWorkerNode *workerNode = (WorkerNode *) list_nth(workerNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t workerNodeIndex);\n\n\tShardPlacement *dummyPlacement = CitusMakeNode(ShardPlacement);\n\tSetPlacementNodeMetadata(dummyPlacement, workerNode);\n\n\tzeroShardQueryRoundRobin++;\n\n\treturn dummyPlacement;\n}\n\n\n/*\n * RelationShardListForShardIntervalList is a utility function which gets a list of\n * shardInterval, and returns a list of RelationShard.\n */\nList *\nRelationShardListForShardIntervalList(List *shardIntervalList, bool *shardsPresent)\n{\n\tList *relationShardList = NIL;\n\tListCell *shardIntervalListCell = NULL;\n\n\tforeach(shardIntervalListCell, shardIntervalList)\n\t{\n\t\tList *prunedShardIntervalList = (List *) lfirst(shardIntervalListCell);\n\n\t\t/* no shard is present or all shards are pruned out case will be handled later */\n\t\tif (prunedShardIntervalList == NIL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t*shardsPresent = true;\n\n\t\tListCell *shardIntervalCell = NULL;\n\t\tforeach(shardIntervalCell, prunedShardIntervalList)\n\t\t{\n\t\t\tShardInterval *shardInterval = (ShardInterval *) lfirst(shardIntervalCell);\n\t\t\tRelationShard *relationShard = CitusMakeNode(RelationShard);\n\n\t\t\trelationShard->relationId = shardInterval->relationId;\n\t\t\trelationShard->shardId = shardInterval->shardId;\n\n\t\t\trelationShardList = lappend(relationShardList, relationShard);\n\t\t}\n\t}\n\n\treturn relationShardList;\n}\n\n\n/*\n * GetAnchorShardId returns the anchor shard id given relation shard list.\n * The desired anchor shard is found as follows:\n *\n * - Return the first distributed table shard id in the relationShardList if\n * there is any.\n * - Return a random reference table shard id if all the shards belong to\n * reference tables\n * - Return INVALID_SHARD_ID on empty lists\n */\nuint64\nGetAnchorShardId(List *prunedShardIntervalListList)\n{\n\tListCell *prunedShardIntervalListCell = NULL;\n\tuint64 referenceShardId = INVALID_SHARD_ID;\n\n\tforeach(prunedShardIntervalListCell, prunedShardIntervalListList)\n\t{\n\t\tList *prunedShardIntervalList = (List *) lfirst(prunedShardIntervalListCell);\n\n\t\t/* no shard is present or all shards are pruned out case will be handled later */\n\t\tif (prunedShardIntervalList == NIL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tShardInterval *shardInterval = linitial(prunedShardIntervalList);\n\n\t\tif (ReferenceTableShardId(shardInterval->shardId))\n\t\t{\n\t\t\treferenceShardId = shardInterval->shardId;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn shardInterval->shardId;\n\t\t}\n\t}\n\n\treturn referenceShardId;\n}\n\n\n/*\n * TargetShardIntervalForFastPathQuery gets a query which is in\n * the form defined by FastPathRouterQuery() and returns exactly\n * one list of one shard interval (see FastPathRouterQuery()\n * for the detail).\n *\n * If the caller requested the distributionKey value that this function\n * yields, set outputPartitionValueConst.\n */\nList *\nTargetShardIntervalForFastPathQuery(Query *query, bool *isMultiShardQuery,\n\t\t\t\t\t\t\t\t\tConst *inputDistributionKeyValue,\n\t\t\t\t\t\t\t\t\tConst **outputPartitionValueConst)\n{\n\tOid relationId = ExtractFirstCitusTableId(query);\n\n\tif (!HasDistributionKey(relationId))\n\t{\n\t\t/* we don't need to do shard pruning for single shard tables */\n\t\treturn list_make1(LoadShardIntervalList(relationId));\n\t}\n\n\tif (inputDistributionKeyValue && !inputDistributionKeyValue->constisnull)\n\t{\n\t\tCitusTableCacheEntry *cache = GetCitusTableCacheEntry(relationId);\n\t\tVar *distributionKey = cache->partitionColumn;\n\n\t\t/*\n\t\t * We currently don't allow implicitly coerced values to be handled by fast-\n\t\t * path planner. Still, let's be defensive for any  future changes.\n\t\t */\n\t\tif (inputDistributionKeyValue->consttype != distributionKey->vartype)\n\t\t{\n\t\t\tbool missingOk = false;\n\t\t\tinputDistributionKeyValue =\n\t\t\t\tTransformPartitionRestrictionValue(distributionKey,\n\t\t\t\t\t\t\t\t\t\t\t\t   inputDistributionKeyValue, missingOk);\n\t\t}\n\n\t\tShardInterval *cachedShardInterval =\n\t\t\tFindShardInterval(inputDistributionKeyValue->constvalue, cache);\n\t\tif (cachedShardInterval == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"could not find shardinterval to which to send \"\n\t\t\t\t\t\t\t\t   \"the query\")));\n\t\t}\n\n\t\tif (outputPartitionValueConst != NULL)\n\t\t{\n\t\t\t/* set the outgoing partition column value if requested */\n\t\t\t*outputPartitionValueConst = inputDistributionKeyValue;\n\t\t}\n\t\tShardInterval *shardInterval = CopyShardInterval(cachedShardInterval);\n\t\tList *shardIntervalList = list_make1(shardInterval);\n\n\t\treturn list_make1(shardIntervalList);\n\t}\n\n\tNode *quals = query->jointree->quals;\n\tint relationIndex = 1;\n\n\t/*\n\t * We couldn't do the shard pruning based on inputDistributionKeyValue as it might\n\t * be passed as NULL. Still, we can search the quals for distribution key.\n\t */\n\tConst *distributionKeyValueInQuals = NULL;\n\tList *prunedShardIntervalList =\n\t\tPruneShards(relationId, relationIndex, make_ands_implicit((Expr *) quals),\n\t\t\t\t\t&distributionKeyValueInQuals);\n\n\tif (!distributionKeyValueInQuals || distributionKeyValueInQuals->constisnull)\n\t{\n\t\t/*\n\t\t * If the distribution key equals to NULL, we prefer to treat it as a zero shard\n\t\t * query as it cannot return any rows.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\t/* we're only expecting single shard from a single table */\n\tif (list_length(prunedShardIntervalList) > 1)\n\t{\n\t\t*isMultiShardQuery = true;\n\t}\n\telse if (list_length(prunedShardIntervalList) == 1 &&\n\t\t\t outputPartitionValueConst != NULL)\n\t{\n\t\t/* set the outgoing partition column value if requested */\n\t\t*outputPartitionValueConst = distributionKeyValueInQuals;\n\t}\n\n\treturn list_make1(prunedShardIntervalList);\n}\n\n\n/*\n * TargetShardIntervalsForRestrictInfo performs shard pruning for all referenced\n * relations in the relation restriction context and returns list of shards per\n * relation. Shard pruning is done based on provided restriction context per relation.\n * The function sets multiShardQuery to true if any of the relations pruned down to\n * more than one active shard. It also records pruned shard intervals in relation\n * restriction context to be used later on. Some queries may have contradiction\n * clauses like 'and false' or 'and 1=0', such queries are treated as if all of\n * the shards of joining relations are pruned out.\n */\nList *\nTargetShardIntervalsForRestrictInfo(RelationRestrictionContext *restrictionContext,\n\t\t\t\t\t\t\t\t\tbool *multiShardQuery, Const **partitionValueConst)\n{\n\tList *prunedShardIntervalListList = NIL;\n\tListCell *restrictionCell = NULL;\n\tbool multiplePartitionValuesExist = false;\n\tConst *queryPartitionValueConst = NULL;\n\n\tAssert(restrictionContext != NULL);\n\n\tforeach(restrictionCell, restrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction =\n\t\t\t(RelationRestriction *) lfirst(restrictionCell);\n\t\tOid relationId = relationRestriction->relationId;\n\n\t\tif (!IsCitusTable(relationId))\n\t\t{\n\t\t\t/* ignore local tables for shard pruning purposes */\n\t\t\tcontinue;\n\t\t}\n\n\t\tIndex tableId = relationRestriction->index;\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\t\tint shardCount = cacheEntry->shardIntervalArrayLength;\n\t\tList *baseRestrictionList = relationRestriction->relOptInfo->baserestrictinfo;\n\t\tList *restrictClauseList = get_all_actual_clauses(baseRestrictionList);\n\t\tList *prunedShardIntervalList = NIL;\n\n\t\t/*\n\t\t * Queries may have contradiction clauses like 'false', or '1=0' in\n\t\t * their filters. Such queries would have pseudo constant 'false'\n\t\t * inside relOptInfo->joininfo list. We treat such cases as if all\n\t\t * shards of the table are pruned out.\n\t\t */\n\t\tbool joinFalseQuery = JoinConditionIsOnFalse(\n\t\t\trelationRestriction->relOptInfo->joininfo);\n\t\tif (!joinFalseQuery && shardCount > 0)\n\t\t{\n\t\t\tConst *restrictionPartitionValueConst = NULL;\n\t\t\tprunedShardIntervalList = PruneShards(relationId, tableId, restrictClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t  &restrictionPartitionValueConst);\n\n\t\t\tif (list_length(prunedShardIntervalList) > 1)\n\t\t\t{\n\t\t\t\t(*multiShardQuery) = true;\n\t\t\t}\n\t\t\tif (restrictionPartitionValueConst != NULL &&\n\t\t\t\tqueryPartitionValueConst == NULL)\n\t\t\t{\n\t\t\t\tqueryPartitionValueConst = restrictionPartitionValueConst;\n\t\t\t}\n\t\t\telse if (restrictionPartitionValueConst != NULL &&\n\t\t\t\t\t !equal(queryPartitionValueConst, restrictionPartitionValueConst))\n\t\t\t{\n\t\t\t\tmultiplePartitionValuesExist = true;\n\t\t\t}\n\t\t}\n\n\t\tprunedShardIntervalListList = lappend(prunedShardIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t  prunedShardIntervalList);\n\t}\n\n\t/*\n\t * Different restrictions might have different partition columns.\n\t * We report partition column value if there is only one.\n\t */\n\tif (multiplePartitionValuesExist)\n\t{\n\t\tqueryPartitionValueConst = NULL;\n\t}\n\n\t/* set the outgoing partition column value if requested */\n\tif (partitionValueConst != NULL)\n\t{\n\t\t*partitionValueConst = queryPartitionValueConst;\n\t}\n\n\treturn prunedShardIntervalListList;\n}\n\n\n/*\n * JoinConditionIsOnFalse returns true for queries that\n * have contradiction clauses like 'false', or '1=0' in\n * their filters. Such queries would have pseudo constant 'false'\n * inside joininfo list.\n */\nbool\nJoinConditionIsOnFalse(List *joinInfoList)\n{\n\tList *pseudoJoinRestrictionList = extract_actual_clauses(joinInfoList, true);\n\n\tbool joinFalseQuery = ContainsFalseClause(pseudoJoinRestrictionList);\n\treturn joinFalseQuery;\n}\n\n\n/*\n * RelationPrunesToMultipleShards returns true if the given list of\n * relation-to-shard mappings contains at least two mappings with\n * the same relation, but different shards.\n */\nstatic bool\nRelationPrunesToMultipleShards(List *relationShardList)\n{\n\tListCell *relationShardCell = NULL;\n\tRelationShard *previousRelationShard = NULL;\n\n\trelationShardList = SortList(relationShardList, CompareRelationShards);\n\n\tforeach(relationShardCell, relationShardList)\n\t{\n\t\tRelationShard *relationShard = (RelationShard *) lfirst(relationShardCell);\n\n\t\tif (previousRelationShard != NULL &&\n\t\t\trelationShard->relationId == previousRelationShard->relationId &&\n\t\t\trelationShard->shardId != previousRelationShard->shardId)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tpreviousRelationShard = relationShard;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * PlacementsForWorkersContainingAllShards returns list of shard placements for workers\n * that contain all shard intervals in the given list of shard interval lists.\n */\nList *\nPlacementsForWorkersContainingAllShards(List *shardIntervalListList)\n{\n\tbool firstShard = true;\n\tList *currentPlacementList = NIL;\n\tList *shardIntervalList = NIL;\n\n\tforeach_declared_ptr(shardIntervalList, shardIntervalListList)\n\t{\n\t\tif (shardIntervalList == NIL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tAssert(list_length(shardIntervalList) == 1);\n\n\t\tShardInterval *shardInterval = (ShardInterval *) linitial(shardIntervalList);\n\t\tuint64 shardId = shardInterval->shardId;\n\n\t\t/* retrieve all active shard placements for this shard */\n\t\tList *newPlacementList = ActiveShardPlacementList(shardId);\n\n\t\tif (firstShard)\n\t\t{\n\t\t\tfirstShard = false;\n\t\t\tcurrentPlacementList = newPlacementList;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* keep placements that still exists for this shard */\n\t\t\tcurrentPlacementList = IntersectPlacementList(currentPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  newPlacementList);\n\t\t}\n\n\t\t/*\n\t\t * Bail out if placement list becomes empty. This means there is no worker\n\t\t * containing all shards referenced by the query, hence we can not forward\n\t\t * this query directly to any worker.\n\t\t */\n\t\tif (currentPlacementList == NIL)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn currentPlacementList;\n}\n\n\n/*\n * BuildRoutesForInsert returns a list of ModifyRoute objects for an INSERT\n * query or an empty list if the partition column value is defined as an ex-\n * pression that still needs to be evaluated. If any partition column value\n * falls within 0 or multiple (overlapping) shards, the planning error is set.\n *\n * Multi-row INSERTs are handled by grouping their rows by target shard. These\n * groups are returned in ascending order by shard id, ready for later deparse\n * to shard-specific SQL.\n */\nstatic List *\nBuildRoutesForInsert(Query *query, DeferredErrorMessage **planningError)\n{\n\tOid distributedTableId = ExtractFirstCitusTableId(query);\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(distributedTableId);\n\tList *modifyRouteList = NIL;\n\tListCell *insertValuesCell = NULL;\n\n\tAssert(query->commandType == CMD_INSERT);\n\n\t/* tables that don't have distribution column can only have one shard */\n\tif (!HasDistributionKeyCacheEntry(cacheEntry))\n\t{\n\t\tList *shardIntervalList = LoadShardIntervalList(distributedTableId);\n\n\t\tint shardCount = list_length(shardIntervalList);\n\t\tif (shardCount != 1)\n\t\t{\n\t\t\tif (IsCitusTableTypeCacheEntry(cacheEntry, REFERENCE_TABLE))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"reference table cannot have %d shards\",\n\t\t\t\t\t\t\t\t\t   shardCount)));\n\t\t\t}\n\t\t\telse if (IsCitusTableTypeCacheEntry(cacheEntry, CITUS_LOCAL_TABLE))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"local table cannot have %d shards\",\n\t\t\t\t\t\t\t\t\t   shardCount)));\n\t\t\t}\n\t\t\telse if (IsCitusTableTypeCacheEntry(cacheEntry, SINGLE_SHARD_DISTRIBUTED))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"distributed tables having a null shard key \"\n\t\t\t\t\t\t\t\t\t   \"cannot have %d shards\",\n\t\t\t\t\t\t\t\t\t   shardCount)));\n\t\t\t}\n\t\t}\n\n\t\tShardInterval *shardInterval = linitial(shardIntervalList);\n\t\tModifyRoute *modifyRoute = palloc(sizeof(ModifyRoute));\n\n\t\tmodifyRoute->shardId = shardInterval->shardId;\n\n\t\tRangeTblEntry *valuesRTE = ExtractDistributedInsertValuesRTE(query);\n\t\tif (valuesRTE != NULL)\n\t\t{\n\t\t\t/* add the values list for a multi-row INSERT */\n\t\t\tmodifyRoute->rowValuesLists = valuesRTE->values_lists;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmodifyRoute->rowValuesLists = NIL;\n\t\t}\n\n\t\tmodifyRouteList = lappend(modifyRouteList, modifyRoute);\n\n\t\treturn modifyRouteList;\n\t}\n\n\tVar *partitionColumn = cacheEntry->partitionColumn;\n\n\t/* get full list of insert values and iterate over them to prune */\n\tList *insertValuesList = ExtractInsertValuesList(query, partitionColumn);\n\n\tforeach(insertValuesCell, insertValuesList)\n\t{\n\t\tInsertValues *insertValues = (InsertValues *) lfirst(insertValuesCell);\n\t\tList *prunedShardIntervalList = NIL;\n\t\tNode *partitionValueExpr = (Node *) insertValues->partitionValueExpr;\n\n\t\t/*\n\t\t * We only support constant partition values at this point. Sometimes\n\t\t * they are wrappend in an implicit coercion though. Most notably\n\t\t * FuncExpr coercions for casts created with CREATE CAST ... WITH\n\t\t * FUNCTION .. AS IMPLICIT. To support this first we strip them here.\n\t\t * Then we do the coercion manually below using\n\t\t * TransformPartitionRestrictionValue, if the types are not the same.\n\t\t *\n\t\t * NOTE: eval_const_expressions below would do some of these removals\n\t\t * too, but it's unclear if it would do all of them. It is possible\n\t\t * that there are no cases where this strip_implicit_coercions call is\n\t\t * really necessary at all, but currently that's hard to rule out.\n\t\t * So to be on the safe side we call strip_implicit_coercions too, to\n\t\t * be sure we support as much as possible.\n\t\t */\n\t\tpartitionValueExpr = strip_implicit_coercions(partitionValueExpr);\n\n\t\t/*\n\t\t * By evaluating constant expressions an expression such as 2 + 4\n\t\t * will become const 6. That way we can use them as a partition column\n\t\t * value. Normally the planner evaluates constant expressions, but we\n\t\t * may be working on the original query tree here. So we do it here\n\t\t * explicitely before checking that the partition value is a const.\n\t\t *\n\t\t * NOTE: We do not use expression_planner here, since all it does\n\t\t * apart from calling eval_const_expressions is call fix_opfuncids.\n\t\t * This is not needed here, since it's a no-op for T_Const nodes and we\n\t\t * error out below in all other cases.\n\t\t */\n\t\tpartitionValueExpr = eval_const_expressions(NULL, partitionValueExpr);\n\n\t\tif (!IsA(partitionValueExpr, Const))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\"failed to evaluate partition key in insert\"),\n\t\t\t\t\t\t\terrhint(\"try using constant values for partition column\")));\n\t\t}\n\n\t\tConst *partitionValueConst = (Const *) partitionValueExpr;\n\t\tif (partitionValueConst->constisnull)\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),\n\t\t\t\t\t\t\terrmsg(\"cannot perform an INSERT with NULL in the partition \"\n\t\t\t\t\t\t\t\t   \"column\")));\n\t\t}\n\n\t\t/* actually do the coercions that we skipped before, if fails throw an\n\t\t * error */\n\t\tif (partitionValueConst->consttype != partitionColumn->vartype)\n\t\t{\n\t\t\tbool missingOk = false;\n\t\t\tpartitionValueConst =\n\t\t\t\tTransformPartitionRestrictionValue(partitionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t   partitionValueConst,\n\t\t\t\t\t\t\t\t\t\t\t\t   missingOk);\n\t\t}\n\n\t\tif (IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED) ||\n\t\t\tIsCitusTableTypeCacheEntry(cacheEntry, RANGE_DISTRIBUTED))\n\t\t{\n\t\t\tDatum partitionValue = partitionValueConst->constvalue;\n\n\t\t\tShardInterval *shardInterval = FindShardInterval(partitionValue, cacheEntry);\n\t\t\tif (shardInterval != NULL)\n\t\t\t{\n\t\t\t\tprunedShardIntervalList = list_make1(shardInterval);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tIndex tableId = 1;\n\t\t\tOpExpr *equalityExpr = MakeOpExpression(partitionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\tBTEqualStrategyNumber);\n\t\t\tNode *rightOp = get_rightop((Expr *) equalityExpr);\n\n\t\t\tAssert(rightOp != NULL);\n\t\t\tAssert(IsA(rightOp, Const));\n\t\t\tConst *rightConst = (Const *) rightOp;\n\n\t\t\trightConst->constvalue = partitionValueConst->constvalue;\n\t\t\trightConst->constisnull = partitionValueConst->constisnull;\n\t\t\trightConst->constbyval = partitionValueConst->constbyval;\n\n\t\t\tList *restrictClauseList = list_make1(equalityExpr);\n\n\t\t\tprunedShardIntervalList = PruneShards(distributedTableId, tableId,\n\t\t\t\t\t\t\t\t\t\t\t\t  restrictClauseList, NULL);\n\t\t}\n\n\t\tint prunedShardIntervalCount = list_length(prunedShardIntervalList);\n\t\tif (prunedShardIntervalCount != 1)\n\t\t{\n\t\t\tchar *partitionKeyString = cacheEntry->partitionKeyString;\n\t\t\tchar *partitionColumnName =\n\t\t\t\tColumnToColumnName(distributedTableId, stringToNode(partitionKeyString));\n\t\t\tStringInfo errorMessage = makeStringInfo();\n\t\t\tStringInfo errorHint = makeStringInfo();\n\t\t\tconst char *targetCountType = NULL;\n\n\t\t\tif (prunedShardIntervalCount == 0)\n\t\t\t{\n\t\t\t\ttargetCountType = \"no\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\ttargetCountType = \"multiple\";\n\t\t\t}\n\n\t\t\tif (prunedShardIntervalCount == 0)\n\t\t\t{\n\t\t\t\tappendStringInfo(errorHint, \"Make sure you have created a shard which \"\n\t\t\t\t\t\t\t\t\t\t\t\"can receive this partition column value.\");\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tappendStringInfo(errorHint, \"Make sure the value for partition column \"\n\t\t\t\t\t\t\t\t\t\t\t\"\\\"%s\\\" falls into a single shard.\",\n\t\t\t\t\t\t\t\t partitionColumnName);\n\t\t\t}\n\n\t\t\tappendStringInfo(errorMessage, \"cannot run INSERT command which targets %s \"\n\t\t\t\t\t\t\t\t\t\t   \"shards\", targetCountType);\n\n\t\t\t(*planningError) = DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t\t\t errorMessage->data, NULL,\n\t\t\t\t\t\t\t\t\t\t\t errorHint->data);\n\n\t\t\treturn NIL;\n\t\t}\n\n\t\tShardInterval *targetShard = (ShardInterval *) linitial(prunedShardIntervalList);\n\t\tinsertValues->shardId = targetShard->shardId;\n\t}\n\n\tmodifyRouteList = GroupInsertValuesByShardId(insertValuesList);\n\n\treturn modifyRouteList;\n}\n\n\n/*\n * IsMultiRowInsert returns whether the given query is a multi-row INSERT.\n *\n * It does this by determining whether the query is an INSERT that has an\n * RTE_VALUES. Single-row INSERTs will have their RTE_VALUES optimised away\n * in transformInsertStmt, and instead use the target list.\n */\nbool\nIsMultiRowInsert(Query *query)\n{\n\treturn ExtractDistributedInsertValuesRTE(query) != NULL;\n}\n\n\n/*\n * ExtractDistributedInsertValuesRTE does precisely that. If the provided\n * query is not an INSERT, or if the INSERT does not have a VALUES RTE\n * (i.e. it is not a multi-row INSERT), this function returns NULL.\n * If all those conditions are met, an RTE representing the multiple values\n * of a multi-row INSERT is returned.\n */\nRangeTblEntry *\nExtractDistributedInsertValuesRTE(Query *query)\n{\n\tListCell *rteCell = NULL;\n\n\tif (query->commandType != CMD_INSERT)\n\t{\n\t\treturn NULL;\n\t}\n\n\tforeach(rteCell, query->rtable)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(rteCell);\n\n\t\tif (rte->rtekind == RTE_VALUES)\n\t\t{\n\t\t\treturn rte;\n\t\t}\n\t}\n\treturn NULL;\n}\n\n\n/*\n * NormalizeMultiRowInsertTargetList ensures all elements of multi-row INSERT target\n * lists are Vars. In multi-row INSERTs, most target list entries contain a Var\n * expression pointing to a position within the values_lists field of a VALUES\n * RTE, but non-NULL default columns are handled differently. Instead of adding\n * the default expression to each row, a single expression encoding the DEFAULT\n * appears in the target list. For consistency, we move these expressions into\n * values lists and replace them with an appropriately constructed Var.\n */\nstatic void\nNormalizeMultiRowInsertTargetList(Query *query)\n{\n\tListCell *valuesListCell = NULL;\n\tListCell *targetEntryCell = NULL;\n\tint targetEntryNo = 0;\n\n\tRangeTblEntry *valuesRTE = ExtractDistributedInsertValuesRTE(query);\n\tif (valuesRTE == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tforeach(valuesListCell, valuesRTE->values_lists)\n\t{\n\t\tList *valuesList = (List *) lfirst(valuesListCell);\n\t\tExpr **valuesArray = (Expr **) PointerArrayFromList(valuesList);\n\t\tList *expandedValuesList = NIL;\n\n\t\tforeach(targetEntryCell, query->targetList)\n\t\t{\n\t\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell);\n\t\t\tExpr *targetExpr = targetEntry->expr;\n\n\t\t\tif (IsA(targetExpr, Var))\n\t\t\t{\n\t\t\t\t/* expression from the VALUES section */\n\t\t\t\tVar *targetListVar = (Var *) targetExpr;\n\t\t\t\ttargetExpr = valuesArray[targetListVar->varattno - 1];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* copy the column's default expression */\n\t\t\t\ttargetExpr = copyObject(targetExpr);\n\t\t\t}\n\n\t\t\texpandedValuesList = lappend(expandedValuesList, targetExpr);\n\t\t}\n\t\tSetListCellPtr(valuesListCell, (void *) expandedValuesList);\n\t}\n\n\t/* reset coltypes, coltypmods, colcollations and rebuild them below */\n\tvaluesRTE->coltypes = NIL;\n\tvaluesRTE->coltypmods = NIL;\n\tvaluesRTE->colcollations = NIL;\n\n\tforeach(targetEntryCell, query->targetList)\n\t{\n\t\tTargetEntry *targetEntry = lfirst(targetEntryCell);\n\t\tNode *targetExprNode = (Node *) targetEntry->expr;\n\n\t\t/* RTE_VALUES comes 2nd, after destination table */\n\t\tIndex valuesVarno = 2;\n\n\t\ttargetEntryNo++;\n\n\t\tOid targetType = exprType(targetExprNode);\n\t\tint32 targetTypmod = exprTypmod(targetExprNode);\n\t\tOid targetColl = exprCollation(targetExprNode);\n\n\t\tvaluesRTE->coltypes = lappend_oid(valuesRTE->coltypes, targetType);\n\t\tvaluesRTE->coltypmods = lappend_int(valuesRTE->coltypmods, targetTypmod);\n\t\tvaluesRTE->colcollations = lappend_oid(valuesRTE->colcollations, targetColl);\n\n\t\tif (IsA(targetExprNode, Var))\n\t\t{\n\t\t\tVar *targetVar = (Var *) targetExprNode;\n\t\t\ttargetVar->varattno = targetEntryNo;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* replace the original expression with a Var referencing values_lists */\n\t\tVar *syntheticVar = makeVar(valuesVarno, targetEntryNo, targetType, targetTypmod,\n\t\t\t\t\t\t\t\t\ttargetColl, 0);\n\t\ttargetEntry->expr = (Expr *) syntheticVar;\n\n\t\t/*\n\t\t * Postgres appends a dummy column reference into valuesRTE->eref->colnames\n\t\t * list in addRangeTableEntryForValues for each column specified in VALUES\n\t\t * clause. Now that we replaced DEFAULT column with a synthetic Var, we also\n\t\t * need to add a dummy column reference for that column.\n\t\t */\n\t\tAppendNextDummyColReference(valuesRTE->eref);\n\t}\n}\n\n\n/*\n * AppendNextDummyColReference appends a new dummy column reference to colnames\n * list of given Alias object.\n */\nstatic void\nAppendNextDummyColReference(Alias *expendedReferenceNames)\n{\n\tint existingColReferences = list_length(expendedReferenceNames->colnames);\n\tint nextColReferenceId = existingColReferences + 1;\n\tString *missingColumnString = MakeDummyColumnString(nextColReferenceId);\n\texpendedReferenceNames->colnames = lappend(expendedReferenceNames->colnames,\n\t\t\t\t\t\t\t\t\t\t\t   missingColumnString);\n}\n\n\n/*\n * MakeDummyColumnString returns a String (Value) object by appending given\n * integer to end of the \"column\" string.\n */\nstatic String *\nMakeDummyColumnString(int dummyColumnId)\n{\n\tStringInfo dummyColumnStringInfo = makeStringInfo();\n\tappendStringInfo(dummyColumnStringInfo, \"column%d\", dummyColumnId);\n\tString *dummyColumnString = makeString(dummyColumnStringInfo->data);\n\n\treturn dummyColumnString;\n}\n\n\n/*\n * IntersectPlacementList performs placement pruning based on matching on\n * nodeName:nodePort fields of shard placement data. We start pruning from all\n * placements of the first relation's shard. Then for each relation's shard, we\n * compute intersection of the new shards placement with existing placement list.\n * This operation could have been done using other methods, but since we do not\n * expect very high replication factor, iterating over a list and making string\n * comparisons should be sufficient.\n */\nList *\nIntersectPlacementList(List *lhsPlacementList, List *rhsPlacementList)\n{\n\tListCell *lhsPlacementCell = NULL;\n\tList *placementList = NIL;\n\n\t/* Keep existing placement in the list if it is also present in new placement list */\n\tforeach(lhsPlacementCell, lhsPlacementList)\n\t{\n\t\tShardPlacement *lhsPlacement = (ShardPlacement *) lfirst(lhsPlacementCell);\n\t\tListCell *rhsPlacementCell = NULL;\n\t\tforeach(rhsPlacementCell, rhsPlacementList)\n\t\t{\n\t\t\tShardPlacement *rhsPlacement = (ShardPlacement *) lfirst(rhsPlacementCell);\n\t\t\tif (rhsPlacement->nodePort == lhsPlacement->nodePort &&\n\t\t\t\tstrncmp(rhsPlacement->nodeName, lhsPlacement->nodeName,\n\t\t\t\t\t\tWORKER_LENGTH) == 0)\n\t\t\t{\n\t\t\t\tplacementList = lappend(placementList, rhsPlacement);\n\n\t\t\t\t/*\n\t\t\t\t * We don't need to add the same placement over and over again. This\n\t\t\t\t * could happen if both placements of a shard appear on the same node.\n\t\t\t\t */\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn placementList;\n}\n\n\n/*\n * GroupInsertValuesByShardId takes care of grouping the rows from a multi-row\n * INSERT by target shard. At this point, all pruning has taken place and we\n * need only to build sets of rows for each destination. This is done by a\n * simple sort (by shard identifier) and gather step. The sort has the side-\n * effect of getting things in ascending order to avoid unnecessary deadlocks\n * during Task execution.\n */\nstatic List *\nGroupInsertValuesByShardId(List *insertValuesList)\n{\n\tModifyRoute *route = NULL;\n\tListCell *insertValuesCell = NULL;\n\tList *modifyRouteList = NIL;\n\n\tinsertValuesList = SortList(insertValuesList, CompareInsertValuesByShardId);\n\tforeach(insertValuesCell, insertValuesList)\n\t{\n\t\tInsertValues *insertValues = (InsertValues *) lfirst(insertValuesCell);\n\t\tint64 shardId = insertValues->shardId;\n\t\tbool foundSameShardId = false;\n\n\t\tif (route != NULL)\n\t\t{\n\t\t\tif (route->shardId == shardId)\n\t\t\t{\n\t\t\t\tfoundSameShardId = true;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* new shard id seen; current aggregation done; add to list */\n\t\t\t\tmodifyRouteList = lappend(modifyRouteList, route);\n\t\t\t}\n\t\t}\n\n\t\tif (foundSameShardId)\n\t\t{\n\t\t\t/*\n\t\t\t * Our current value has the same shard id as our aggregate object,\n\t\t\t * so append the rowValues.\n\t\t\t */\n\t\t\troute->rowValuesLists = lappend(route->rowValuesLists,\n\t\t\t\t\t\t\t\t\t\t\tinsertValues->rowValues);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* we encountered a new shard id; build a new aggregate object */\n\t\t\troute = (ModifyRoute *) palloc(sizeof(ModifyRoute));\n\t\t\troute->shardId = insertValues->shardId;\n\t\t\troute->rowValuesLists = list_make1(insertValues->rowValues);\n\t\t}\n\t}\n\n\t/* left holding one final aggregate object; add to list */\n\tmodifyRouteList = lappend(modifyRouteList, route);\n\n\treturn modifyRouteList;\n}\n\n\n/*\n * ExtractInsertValuesList extracts the partition column value for an INSERT\n * command and returns it within an InsertValues struct. For single-row INSERTs\n * this is simply a value extracted from the target list, but multi-row INSERTs\n * will generate a List of InsertValues, each with full row values in addition\n * to the partition value. If a partition value is NULL or missing altogether,\n * this function errors.\n */\nstatic List *\nExtractInsertValuesList(Query *query, Var *partitionColumn)\n{\n\tList *insertValuesList = NIL;\n\tTargetEntry *targetEntry = get_tle_by_resno(query->targetList,\n\t\t\t\t\t\t\t\t\t\t\t\tpartitionColumn->varattno);\n\n\tif (targetEntry == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),\n\t\t\t\t\t\terrmsg(\"cannot perform an INSERT without a partition column \"\n\t\t\t\t\t\t\t   \"value\")));\n\t}\n\n\t/*\n\t * We've got a multi-row INSERT. PostgreSQL internally represents such\n\t * commands by linking Vars in the target list to lists of values within\n\t * a special VALUES range table entry. By extracting the right positional\n\t * expression from each list within that RTE, we will extract the partition\n\t * values for each row within the multi-row INSERT.\n\t */\n\tif (IsA(targetEntry->expr, Var))\n\t{\n\t\tVar *partitionVar = (Var *) targetEntry->expr;\n\t\tListCell *valuesListCell = NULL;\n\t\tIndex ivIndex = 0;\n\n\t\tRangeTblEntry *referencedRTE = rt_fetch(partitionVar->varno, query->rtable);\n\t\tforeach(valuesListCell, referencedRTE->values_lists)\n\t\t{\n\t\t\tInsertValues *insertValues = (InsertValues *) palloc(sizeof(InsertValues));\n\t\t\tinsertValues->rowValues = (List *) lfirst(valuesListCell);\n\t\t\tinsertValues->partitionValueExpr = list_nth(insertValues->rowValues,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(partitionVar->varattno - 1));\n\t\t\tinsertValues->shardId = INVALID_SHARD_ID;\n\t\t\tinsertValues->listIndex = ivIndex;\n\n\t\t\tinsertValuesList = lappend(insertValuesList, insertValues);\n\t\t\tivIndex++;\n\t\t}\n\t}\n\n\t/* nothing's been found yet; this is a simple single-row INSERT */\n\tif (insertValuesList == NIL)\n\t{\n\t\tInsertValues *insertValues = (InsertValues *) palloc(sizeof(InsertValues));\n\t\tinsertValues->rowValues = NIL;\n\t\tinsertValues->partitionValueExpr = targetEntry->expr;\n\t\tinsertValues->shardId = INVALID_SHARD_ID;\n\n\t\tinsertValuesList = lappend(insertValuesList, insertValues);\n\t}\n\n\treturn insertValuesList;\n}\n\n\n/*\n * ExtractInsertPartitionKeyValue extracts the partition column value\n * from an INSERT query. If the expression in the partition column is\n * non-constant or it is a multi-row INSERT with multiple different partition\n * column values, the function returns NULL.\n */\nConst *\nExtractInsertPartitionKeyValue(Query *query)\n{\n\tOid distributedTableId = ExtractFirstCitusTableId(query);\n\tuint32 rangeTableId = 1;\n\tConst *singlePartitionValueConst = NULL;\n\n\tif (!HasDistributionKey(distributedTableId))\n\t{\n\t\treturn NULL;\n\t}\n\n\tVar *partitionColumn = PartitionColumn(distributedTableId, rangeTableId);\n\tTargetEntry *targetEntry = get_tle_by_resno(query->targetList,\n\t\t\t\t\t\t\t\t\t\t\t\tpartitionColumn->varattno);\n\tif (targetEntry == NULL)\n\t{\n\t\t/* partition column value not specified */\n\t\treturn NULL;\n\t}\n\n\tNode *targetExpression = strip_implicit_coercions((Node *) targetEntry->expr);\n\n\t/*\n\t * Multi-row INSERTs have a Var in the target list that points to\n\t * an RTE_VALUES.\n\t */\n\tif (IsA(targetExpression, Var))\n\t{\n\t\tVar *partitionVar = (Var *) targetExpression;\n\t\tListCell *valuesListCell = NULL;\n\n\t\tRangeTblEntry *referencedRTE = rt_fetch(partitionVar->varno, query->rtable);\n\n\t\tforeach(valuesListCell, referencedRTE->values_lists)\n\t\t{\n\t\t\tList *rowValues = (List *) lfirst(valuesListCell);\n\t\t\tNode *partitionValueNode = list_nth(rowValues, partitionVar->varattno - 1);\n\t\t\tExpr *partitionValueExpr = (Expr *) strip_implicit_coercions(\n\t\t\t\tpartitionValueNode);\n\n\t\t\tif (!IsA(partitionValueExpr, Const))\n\t\t\t{\n\t\t\t\t/* non-constant value in the partition column */\n\t\t\t\tsinglePartitionValueConst = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tConst *partitionValueConst = (Const *) partitionValueExpr;\n\n\t\t\tif (singlePartitionValueConst == NULL)\n\t\t\t{\n\t\t\t\t/* first row has a constant in the partition column, looks promising! */\n\t\t\t\tsinglePartitionValueConst = partitionValueConst;\n\t\t\t}\n\t\t\telse if (!equal(partitionValueConst, singlePartitionValueConst))\n\t\t\t{\n\t\t\t\t/* multiple different values in the partition column, too bad */\n\t\t\t\tsinglePartitionValueConst = NULL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* another row with the same partition column value! */\n\t\t\t}\n\t\t}\n\t}\n\telse if (IsA(targetExpression, Const))\n\t{\n\t\t/* single-row INSERT with a constant partition column value */\n\t\tsinglePartitionValueConst = (Const *) targetExpression;\n\t}\n\telse\n\t{\n\t\t/* single-row INSERT with a non-constant partition column value */\n\t\tsinglePartitionValueConst = NULL;\n\t}\n\n\tif (singlePartitionValueConst != NULL)\n\t{\n\t\tsinglePartitionValueConst = copyObject(singlePartitionValueConst);\n\t}\n\n\treturn singlePartitionValueConst;\n}\n\n\n/*\n * DeferErrorIfUnsupportedRouterPlannableSelectQuery checks if given query is router plannable,\n * SELECT query, setting distributedPlan->planningError if not.\n * The query is router plannable if it is a modify query, or if it is a select\n * query issued on a hash partitioned distributed table. Router plannable checks\n * for select queries can be turned off by setting citus.enable_router_execution\n * flag to false.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfUnsupportedRouterPlannableSelectQuery(Query *query)\n{\n\tList *rangeTableRelationList = NIL;\n\tListCell *rangeTableRelationCell = NULL;\n\n\tif (query->commandType != CMD_SELECT)\n\t{\n\t\treturn DeferredError(ERRCODE_ASSERT_FAILURE,\n\t\t\t\t\t\t\t \"Only SELECT query types are supported in this path\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tif (!EnableRouterExecution)\n\t{\n\t\treturn DeferredError(ERRCODE_SUCCESSFUL_COMPLETION,\n\t\t\t\t\t\t\t \"Router planner not enabled.\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tbool hasPostgresOrCitusLocalTable = false;\n\tbool hasDistributedTable = false;\n\tbool hasReferenceTable = false;\n\n\tList *distributedRelationList = NIL;\n\n\tExtractRangeTableRelationWalker((Node *) query, &rangeTableRelationList);\n\tforeach(rangeTableRelationCell, rangeTableRelationList)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(rangeTableRelationCell);\n\t\tif (rte->rtekind == RTE_RELATION)\n\t\t{\n\t\t\tOid distributedTableId = rte->relid;\n\n\t\t\t/* local tables are allowed if there are no distributed tables */\n\t\t\tif (!IsCitusTable(distributedTableId))\n\t\t\t{\n\t\t\t\thasPostgresOrCitusLocalTable = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (IsCitusTableType(distributedTableId, REFERENCE_TABLE))\n\t\t\t{\n\t\t\t\thasReferenceTable = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (IsCitusTableType(distributedTableId, CITUS_LOCAL_TABLE))\n\t\t\t{\n\t\t\t\thasPostgresOrCitusLocalTable = true;\n\t\t\t\telog(DEBUG4, \"Router planner finds a local table added to metadata\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (IsCitusTableType(distributedTableId, APPEND_DISTRIBUTED))\n\t\t\t{\n\t\t\t\treturn DeferredError(\n\t\t\t\t\tERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\"Router planner does not support append-partitioned tables.\",\n\t\t\t\t\tNULL, NULL);\n\t\t\t}\n\n\t\t\tif (IsCitusTableType(distributedTableId, DISTRIBUTED_TABLE))\n\t\t\t{\n\t\t\t\thasDistributedTable = true;\n\t\t\t\tdistributedRelationList = lappend_oid(distributedRelationList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  distributedTableId);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Currently, we don't support tables with replication factor > 1,\n\t\t\t * except reference tables with SELECT ... FOR UPDATE queries. It is\n\t\t\t * also not supported from MX nodes.\n\t\t\t */\n\t\t\tif (query->hasForUpdate)\n\t\t\t{\n\t\t\t\tuint32 tableReplicationFactor = TableShardReplicationFactor(\n\t\t\t\t\tdistributedTableId);\n\n\t\t\t\tif (tableReplicationFactor > 1 && IsCitusTableType(distributedTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   DISTRIBUTED_TABLE))\n\t\t\t\t{\n\t\t\t\t\treturn DeferredError(\n\t\t\t\t\t\tERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\"SELECT FOR UPDATE with table replication factor > 1 not supported for non-reference tables.\",\n\t\t\t\t\t\tNULL, NULL);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * We want to make sure nextval happens on the coordinator / the current\n\t * node, since the user may have certain expectations around the values\n\t * produced by the sequence. We therefore cannot push down the nextval\n\t * call as part of a router query.\n\t *\n\t * We let queries with nextval in the target list fall through to\n\t * the logical planner, which will ensure that the nextval is called\n\t * in the combine query on the coordinator.\n\t *\n\t * If there are no distributed or reference tables in the query,\n\t * then the query will anyway happen on the coordinator, so we can\n\t * allow nextval.\n\t */\n\tif (contain_nextval_expression_walker((Node *) query->targetList, NULL) &&\n\t\t(hasDistributedTable || hasReferenceTable))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"Sequences cannot be used in router queries\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\t/* local tables are not allowed if there are distributed tables */\n\tif (hasPostgresOrCitusLocalTable && hasDistributedTable)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"Local tables cannot be used in distributed queries.\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\tDeferredErrorMessage *CTEWithSearchClauseError =\n\t\tErrorIfQueryHasCTEWithSearchClause(query);\n\tif (CTEWithSearchClauseError != NULL)\n\t{\n\t\treturn CTEWithSearchClauseError;\n\t}\n\n\treturn ErrorIfQueryHasUnroutableModifyingCTE(query);\n}\n\n\n/*\n * Copy a RelationRestrictionContext. Note that several subfields are copied\n * shallowly, for lack of copyObject support.\n *\n * Note that CopyRelationRestrictionContext copies the following fields per relation\n * context: index, relationId, distributedRelation, rte, relOptInfo->baserestrictinfo\n * and relOptInfo->joininfo. Also, the function shallowly copies plannerInfo and\n * prunedShardIntervalList which are read-only. All other parts of the relOptInfo\n * is also shallowly copied.\n */\nRelationRestrictionContext *\nCopyRelationRestrictionContext(RelationRestrictionContext *oldContext)\n{\n\tRelationRestrictionContext *newContext =\n\t\t(RelationRestrictionContext *) palloc(sizeof(RelationRestrictionContext));\n\tListCell *relationRestrictionCell = NULL;\n\n\tnewContext->allReferenceTables = oldContext->allReferenceTables;\n\tnewContext->relationRestrictionList = NIL;\n\n\tforeach(relationRestrictionCell, oldContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *oldRestriction =\n\t\t\t(RelationRestriction *) lfirst(relationRestrictionCell);\n\t\tRelationRestriction *newRestriction = (RelationRestriction *)\n\t\t\t\t\t\t\t\t\t\t\t  palloc0(sizeof(RelationRestriction));\n\n\t\tnewRestriction->index = oldRestriction->index;\n\t\tnewRestriction->relationId = oldRestriction->relationId;\n\t\tnewRestriction->citusTable = oldRestriction->citusTable;\n\t\tnewRestriction->rte = copyObject(oldRestriction->rte);\n\n\t\t/* can't be copied, we copy (flatly) a RelOptInfo, and then decouple baserestrictinfo */\n\t\tnewRestriction->relOptInfo = palloc(sizeof(RelOptInfo));\n\t\t*newRestriction->relOptInfo = *oldRestriction->relOptInfo;\n\n\t\tnewRestriction->relOptInfo->baserestrictinfo =\n\t\t\tcopyObject(oldRestriction->relOptInfo->baserestrictinfo);\n\n\t\tnewRestriction->relOptInfo->joininfo =\n\t\t\tcopyObject(oldRestriction->relOptInfo->joininfo);\n\n\t\t/* not copyable, but readonly */\n\t\tnewRestriction->plannerInfo = oldRestriction->plannerInfo;\n\n\t\tnewContext->relationRestrictionList =\n\t\t\tlappend(newContext->relationRestrictionList, newRestriction);\n\t}\n\n\treturn newContext;\n}\n\n\n/*\n * ErrorIfQueryHasUnroutableModifyingCTE checks if the query contains modifying common table\n * expressions and errors out if it does.\n */\nstatic DeferredErrorMessage *\nErrorIfQueryHasUnroutableModifyingCTE(Query *queryTree)\n{\n\tAssert(queryTree->commandType == CMD_SELECT);\n\n\tif (!queryTree->hasModifyingCTE)\n\t{\n\t\treturn NULL;\n\t}\n\n\t/* we can't route conflicting replication models */\n\tchar replicationModel = 0;\n\n\tCommonTableExpr *cte = NULL;\n\tforeach_declared_ptr(cte, queryTree->cteList)\n\t{\n\t\tQuery *cteQuery = (Query *) cte->ctequery;\n\n\t\t/*\n\t\t * Here we only check for command type of top level query. Normally there can be\n\t\t * nested CTE, however PostgreSQL dictates that data-modifying statements must\n\t\t * be at top level of CTE. Therefore it is OK to just check for top level.\n\t\t * Similarly, we do not need to check for subqueries.\n\t\t */\n\t\tif (cteQuery->commandType != CMD_SELECT &&\n\t\t\tcteQuery->commandType != CMD_UPDATE &&\n\t\t\tcteQuery->commandType != CMD_DELETE)\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"only SELECT, UPDATE, or DELETE common table expressions \"\n\t\t\t\t\t\t\t\t \"may be router planned\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\n\t\tif (cteQuery->commandType != CMD_SELECT)\n\t\t{\n\t\t\tOid distributedTableId = InvalidOid;\n\t\t\tDeferredErrorMessage *cteError =\n\t\t\t\tModifyPartialQuerySupported(cteQuery, false, &distributedTableId);\n\t\t\tif (cteError)\n\t\t\t{\n\t\t\t\treturn cteError;\n\t\t\t}\n\n\t\t\tCitusTableCacheEntry *modificationTableCacheEntry =\n\t\t\t\tGetCitusTableCacheEntry(distributedTableId);\n\n\t\t\tif (!IsCitusTableTypeCacheEntry(modificationTableCacheEntry,\n\t\t\t\t\t\t\t\t\t\t\tDISTRIBUTED_TABLE))\n\t\t\t{\n\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"cannot router plan modification of a non-distributed table\",\n\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t\t}\n\n\t\t\tif (replicationModel &&\n\t\t\t\tmodificationTableCacheEntry->replicationModel != replicationModel)\n\t\t\t{\n\t\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t\t \"cannot route mixed replication models\",\n\t\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t\t}\n\n\t\t\treplicationModel = modificationTableCacheEntry->replicationModel;\n\t\t}\n\t}\n\n\t/* everything OK */\n\treturn NULL;\n}\n\n\n/*\n * ErrorIfQueryHasCTEWithSearchClause checks if the query contains any common table\n * expressions with search clause and errors out if it does.\n */\nstatic DeferredErrorMessage *\nErrorIfQueryHasCTEWithSearchClause(Query *queryTree)\n{\n\tif (ContainsSearchClauseWalker((Node *) queryTree, NULL))\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"CTEs with search clauses are not supported\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\treturn NULL;\n}\n\n\n/*\n * ContainsSearchClauseWalker walks over the node and finds if there are any\n * CommonTableExprs with search clause\n */\nstatic bool\nContainsSearchClauseWalker(Node *node, void *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, CommonTableExpr))\n\t{\n\t\tif (((CommonTableExpr *) node)->search_clause != NULL)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (IsA(node, Query))\n\t{\n\t\treturn query_tree_walker((Query *) node, ContainsSearchClauseWalker, NULL, 0);\n\t}\n\n\treturn expression_tree_walker(node, ContainsSearchClauseWalker, NULL);\n}\n\n\n/*\n * get_all_actual_clauses\n *\n * Returns a list containing the bare clauses from 'restrictinfo_list'.\n *\n * This loses the distinction between regular and pseudoconstant clauses,\n * so be careful what you use it for.\n */\nList *\nget_all_actual_clauses(List *restrictinfo_list)\n{\n\tList *result = NIL;\n\tListCell *l;\n\n\tforeach(l, restrictinfo_list)\n\t{\n\t\tRestrictInfo *rinfo = (RestrictInfo *) lfirst(l);\n\n\t\tAssert(IsA(rinfo, RestrictInfo));\n\n\t\tresult = lappend(result, rinfo->clause);\n\t}\n\treturn result;\n}\n\n\n/*\n * CompareInsertValuesByShardId does what it says in the name. Used for sorting\n * InsertValues objects by their shard.\n */\nstatic int\nCompareInsertValuesByShardId(const void *leftElement, const void *rightElement)\n{\n\tInsertValues *leftValue = *((InsertValues **) leftElement);\n\tInsertValues *rightValue = *((InsertValues **) rightElement);\n\tint64 leftShardId = leftValue->shardId;\n\tint64 rightShardId = rightValue->shardId;\n\tIndex leftIndex = leftValue->listIndex;\n\tIndex rightIndex = rightValue->listIndex;\n\n\tif (leftShardId > rightShardId)\n\t{\n\t\treturn 1;\n\t}\n\telse if (leftShardId < rightShardId)\n\t{\n\t\treturn -1;\n\t}\n\telse\n\t{\n\t\t/* shard identifiers are the same, list index is secondary sort key */\n\t\tif (leftIndex > rightIndex)\n\t\t{\n\t\t\treturn 1;\n\t\t}\n\t\telse if (leftIndex < rightIndex)\n\t\t{\n\t\t\treturn -1;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/query_colocation_checker.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * query_colocation_checker.c implements the logic for determining\n * whether any subqueries in a given query are co-located (e.g.,\n * distribution keys of the relations inside subqueries are equal).\n *\n * The main logic behind non colocated subquery joins is that we pick\n * an anchor range table entry and check for distribution key equality\n * of any other subqueries in the given query. If for a given subquery,\n * we cannot find distribution key equality with the anchor rte, we\n * recursively plan that subquery.\n *\n * We also used a hacky solution for picking relations as the anchor range\n * table entries. The hack is that we wrap them into a subquery. This is only\n * necessary since some of the attribute equivalence checks are based on\n * queries rather than range table entries.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/relation.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"optimizer/planner.h\"\n#include \"optimizer/prep.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parsetree.h\"\n#include \"utils/rel.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_logical_planner.h\" /* only to access utility functions */\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/query_colocation_checker.h\"\n#include \"distributed/relation_restriction_equivalence.h\"\n\n\nstatic RangeTblEntry * AnchorRte(Query *subquery);\nstatic List * UnionRelationRestrictionLists(List *firstRelationList,\n\t\t\t\t\t\t\t\t\t\t\tList *secondRelationList);\nstatic List * CreateDummyTargetList(Oid relationId, List *requiredAttributes);\nstatic TargetEntry * CreateTargetEntryForColumn(Form_pg_attribute attributeTuple, Index\n\t\t\t\t\t\t\t\t\t\t\t\trteIndex,\n\t\t\t\t\t\t\t\t\t\t\t\tint attributeNumber, int resno);\nstatic TargetEntry * CreateTargetEntryForNullCol(Form_pg_attribute attributeTuple, int\n\t\t\t\t\t\t\t\t\t\t\t\t resno);\nstatic TargetEntry * CreateUnusedTargetEntry(int resno);\n\n/*\n * CreateColocatedJoinChecker is a helper function that simply calculates\n * a ColocatedJoinChecker with the given input and returns it.\n */\nColocatedJoinChecker\nCreateColocatedJoinChecker(Query *subquery, PlannerRestrictionContext *restrictionContext)\n{\n\tColocatedJoinChecker colocatedJoinChecker = { 0 };\n\n\tQuery *anchorSubquery = NULL;\n\n\t/* we couldn't pick an anchor subquery, no need to continue */\n\tRangeTblEntry *anchorRangeTblEntry = AnchorRte(subquery);\n\tif (anchorRangeTblEntry == NULL)\n\t{\n\t\tcolocatedJoinChecker.anchorRelationRestrictionList = NIL;\n\n\t\treturn colocatedJoinChecker;\n\t}\n\n\tif (anchorRangeTblEntry->rtekind == RTE_RELATION)\n\t{\n\t\t/*\n\t\t * If we get a relation as our anchor, wrap into a subquery. The only\n\t\t * reason that we wrap the relation into a subquery is that some of the utility\n\t\t * functions (i.e., FilterPlannerRestrictionForQuery()) rely on queries\n\t\t * not relations.\n\t\t */\n\t\tRTEPermissionInfo *perminfo = NULL;\n\t\tif (anchorRangeTblEntry->perminfoindex)\n\t\t{\n\t\t\tperminfo = getRTEPermissionInfo(subquery->rteperminfos, anchorRangeTblEntry);\n\t\t}\n\t\tanchorSubquery = WrapRteRelationIntoSubquery(anchorRangeTblEntry, NIL, perminfo);\n\t}\n\telse if (anchorRangeTblEntry->rtekind == RTE_SUBQUERY)\n\t{\n\t\tanchorSubquery = anchorRangeTblEntry->subquery;\n\t}\n\telse\n\t{\n\t\t/* we don't expect any other RTE type here */\n\t\tpg_unreachable();\n\t}\n\n\tPlannerRestrictionContext *anchorPlannerRestrictionContext =\n\t\tFilterPlannerRestrictionForQuery(restrictionContext, anchorSubquery);\n\tRelationRestrictionContext *anchorRelationRestrictionContext =\n\t\tanchorPlannerRestrictionContext->relationRestrictionContext;\n\tList *anchorRestrictionEquivalences =\n\t\tGenerateAllAttributeEquivalences(anchorPlannerRestrictionContext);\n\n\t/* fill the non colocated planning context */\n\tcolocatedJoinChecker.subquery = subquery;\n\tcolocatedJoinChecker.subqueryPlannerRestriction = restrictionContext;\n\n\tcolocatedJoinChecker.anchorRelationRestrictionList =\n\t\tanchorRelationRestrictionContext->relationRestrictionList;\n\tcolocatedJoinChecker.anchorAttributeEquivalences = anchorRestrictionEquivalences;\n\n\treturn colocatedJoinChecker;\n}\n\n\n/*\n * AnchorRte gets a query and searches for a relation or a subquery within\n * the join tree of the query such that we can use it as our anchor range\n * table entry during our non colocated subquery planning.\n *\n * The function returns NULL if it cannot find a proper range table entry for our\n * purposes. See the function for the details.\n */\nstatic RangeTblEntry *\nAnchorRte(Query *subquery)\n{\n\tFromExpr *joinTree = subquery->jointree;\n\tRelids joinRelIds = get_relids_in_jointree((Node *) joinTree, false, false);\n\tint currentRTEIndex = -1;\n\tRangeTblEntry *anchorRangeTblEntry = NULL;\n\n\t/*\n\t * Pick a random anchor relation or subquery (i.e., the first) for now. We\n\t * might consider picking a better rte as the anchor. For example, we could\n\t * iterate on the joinRelIds, and check which rteIndex has more distribution\n\t * key equiality with rteIndexes. For the time being, the current primitive\n\t * approach helps us in many cases.\n\t */\n\twhile ((currentRTEIndex = bms_next_member(joinRelIds, currentRTEIndex)) >= 0)\n\t{\n\t\tRangeTblEntry *currentRte = rt_fetch(currentRTEIndex, subquery->rtable);\n\n\t\t/*\n\t\t * We always prefer distributed relations if we can find any. The\n\t\t * reason is that Citus is currently able to recursively plan\n\t\t * subqueries, but not relations.\n\t\t *\n\t\t * For the subqueries, make sure that the subquery contains at least one\n\t\t * distributed table and doesn't have a set operation.\n\t\t *\n\t\t * TODO: The set operation restriction might sound weird, but, the restriction\n\t\t * equivalence generation functions ignore set operations. We should\n\t\t * integrate the logic in SafeToPushdownUnionSubquery() to\n\t\t * GenerateAllAttributeEquivalences() such that the latter becomes aware of\n\t\t * the set operations.\n\t\t */\n\t\tif (anchorRangeTblEntry == NULL && currentRte->rtekind == RTE_SUBQUERY &&\n\t\t\tFindNodeMatchingCheckFunction((Node *) currentRte->subquery,\n\t\t\t\t\t\t\t\t\t\t  IsDistributedTableRTE) &&\n\t\t\tcurrentRte->subquery->setOperations == NULL &&\n\t\t\t!ContainsUnionSubquery(currentRte->subquery))\n\t\t{\n\t\t\t/* found a subquery, keep it if we cannot find a relation */\n\t\t\tanchorRangeTblEntry = currentRte;\n\t\t}\n\t\telse if (currentRte->rtekind == RTE_RELATION)\n\t\t{\n\t\t\tOid relationId = currentRte->relid;\n\n\t\t\tif (!IsCitusTableType(relationId, DISTRIBUTED_TABLE))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We're not interested in non distributed relations.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tanchorRangeTblEntry = currentRte;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn anchorRangeTblEntry;\n}\n\n\n/*\n * SubqueryColocated returns true if the input subquery has a distribution\n * key equality with the anchor subquery. In other words, we refer the\n * distribution key equality of relations as \"colocation\" in this context.\n */\nbool\nSubqueryColocated(Query *subquery, ColocatedJoinChecker *checker)\n{\n\tList *anchorRelationRestrictionList = checker->anchorRelationRestrictionList;\n\tList *anchorAttributeEquivalences = checker->anchorAttributeEquivalences;\n\n\tPlannerRestrictionContext *restrictionContext = checker->subqueryPlannerRestriction;\n\tPlannerRestrictionContext *filteredPlannerContext =\n\t\tFilterPlannerRestrictionForQuery(restrictionContext, subquery);\n\tList *filteredRestrictionList =\n\t\tfilteredPlannerContext->relationRestrictionContext->relationRestrictionList;\n\n\n\t/*\n\t * There are no relations in the input subquery, such as a subquery\n\t * that consist of only intermediate results or without FROM\n\t * clause or subquery in WHERE clause anded with FALSE.\n\t *\n\t * Note that for the subquery in WHERE clause, the input original\n\t * subquery (a.k.a., which didn't go through standard_planner()) may\n\t * contain distributed relations, but postgres is smart enough to\n\t * not generate the restriction information. That's the reason for\n\t * not asserting non-existence of distributed relations.\n\t */\n\tif (list_length(filteredRestrictionList) == 0)\n\t{\n\t\treturn true;\n\t}\n\n\t/*\n\t * We merge the relation restrictions of the input subquery and the anchor\n\t * restrictions to form a temporary relation restriction context. The aim of\n\t * forming this temporary context is to check whether the context contains\n\t * distribution key equality or not.\n\t */\n\tList *unionedRelationRestrictionList =\n\t\tUnionRelationRestrictionLists(anchorRelationRestrictionList,\n\t\t\t\t\t\t\t\t\t  filteredRestrictionList);\n\n\t/*\n\t * We already have the attributeEquivalences, thus, only need to prepare\n\t * the planner restrictions with unioned relations for our purpose of\n\t * distribution key equality. Note that we don't need to calculate the\n\t * join restrictions, we're already relying on the attributeEquivalences\n\t * provided by the context.\n\t */\n\tRelationRestrictionContext *unionedRelationRestrictionContext = palloc0(\n\t\tsizeof(RelationRestrictionContext));\n\tunionedRelationRestrictionContext->relationRestrictionList =\n\t\tunionedRelationRestrictionList;\n\n\tPlannerRestrictionContext *unionedPlannerRestrictionContext = palloc0(\n\t\tsizeof(PlannerRestrictionContext));\n\tunionedPlannerRestrictionContext->relationRestrictionContext =\n\t\tunionedRelationRestrictionContext;\n\n\tif (!RestrictionEquivalenceForPartitionKeysViaEquivalences(\n\t\t\tunionedPlannerRestrictionContext,\n\t\t\tanchorAttributeEquivalences))\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * WrapRteRelationIntoSubquery wraps the given relation range table entry\n * in a newly constructed \"(SELECT * FROM table_name as anchor_relation)\" query.\n *\n * Note that the query returned by this function does not contain any filters or\n * projections. The returned query should be used cautiosly and it is mostly\n * designed for generating a stub query.\n */\nQuery *\nWrapRteRelationIntoSubquery(RangeTblEntry *rteRelation,\n\t\t\t\t\t\t\tList *requiredAttributes,\n\t\t\t\t\t\t\tRTEPermissionInfo *perminfo)\n{\n\tQuery *subquery = makeNode(Query);\n\tRangeTblRef *newRangeTableRef = makeNode(RangeTblRef);\n\n\tsubquery->commandType = CMD_SELECT;\n\n\t/* we copy the input rteRelation to preserve the rteIdentity */\n\tRangeTblEntry *newRangeTableEntry = copyObject(rteRelation);\n\tsubquery->rtable = list_make1(newRangeTableEntry);\n\n\tif (perminfo)\n\t{\n\t\tnewRangeTableEntry->perminfoindex = 1;\n\t\tsubquery->rteperminfos = list_make1(perminfo);\n\t}\n\n\t/* set the FROM expression to the subquery */\n\tnewRangeTableRef = makeNode(RangeTblRef);\n\tnewRangeTableRef->rtindex = SINGLE_RTE_INDEX;\n\tsubquery->jointree = makeFromExpr(list_make1(newRangeTableRef), NULL);\n\n\tsubquery->targetList =\n\t\tCreateFilteredTargetListForRelation(rteRelation->relid, requiredAttributes);\n\n\tif (list_length(subquery->targetList) == 0)\n\t{\n\t\t/*\n\t\t * in case there is no required column, we assign one dummy NULL target entry\n\t\t * to the subquery targetList so that it has at least one target.\n\t\t * (targetlist should have at least one element)\n\t\t */\n\t\tsubquery->targetList = CreateDummyTargetList(rteRelation->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t requiredAttributes);\n\t}\n\n\treturn subquery;\n}\n\n\n/*\n * CreateAllTargetListForRelation creates a target list which contains all the columns\n * of the given relation. If the column is not in required columns, then it is added\n * as a NULL column.\n */\nList *\nCreateAllTargetListForRelation(Oid relationId, List *requiredAttributes)\n{\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\tint numberOfAttributes = RelationGetNumberOfAttributes(relation);\n\n\tList *targetList = NIL;\n\tint varAttrNo = 1;\n\n\tfor (int attrNum = 1; attrNum <= numberOfAttributes; attrNum++)\n\t{\n\t\tForm_pg_attribute attributeTuple =\n\t\t\tTupleDescAttr(relation->rd_att, attrNum - 1);\n\n\t\tint resNo = attrNum;\n\n\t\tif (attributeTuple->attisdropped)\n\t\t{\n\t\t\t/*\n\t\t\t * For dropped columns, we generate a dummy null column because\n\t\t\t * varattno in relation and subquery are different things, however if\n\t\t\t * we put the NULL columns to the subquery for the dropped columns,\n\t\t\t * they will point to the same variable.\n\t\t\t */\n\t\t\tTargetEntry *nullTargetEntry = CreateUnusedTargetEntry(resNo);\n\t\t\ttargetList = lappend(targetList, nullTargetEntry);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!list_member_int(requiredAttributes, attrNum))\n\t\t{\n\t\t\tTargetEntry *nullTargetEntry =\n\t\t\t\tCreateTargetEntryForNullCol(attributeTuple, resNo);\n\t\t\ttargetList = lappend(targetList, nullTargetEntry);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tTargetEntry *targetEntry =\n\t\t\t\tCreateTargetEntryForColumn(attributeTuple, SINGLE_RTE_INDEX, varAttrNo++,\n\t\t\t\t\t\t\t\t\t\t   resNo);\n\t\t\ttargetList = lappend(targetList, targetEntry);\n\t\t}\n\t}\n\n\trelation_close(relation, NoLock);\n\treturn targetList;\n}\n\n\n/*\n * CreateFilteredTargetListForRelation creates a target list which contains\n * only the required columns of the given relation. If there is not required\n * columns then a dummy NULL column is put as the only entry.\n */\nList *\nCreateFilteredTargetListForRelation(Oid relationId, List *requiredAttributes)\n{\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\tint numberOfAttributes = RelationGetNumberOfAttributes(relation);\n\n\tList *targetList = NIL;\n\tint resultNo = 1;\n\tfor (int attrNum = 1; attrNum <= numberOfAttributes; attrNum++)\n\t{\n\t\tForm_pg_attribute attributeTuple =\n\t\t\tTupleDescAttr(relation->rd_att, attrNum - 1);\n\n\t\tif (list_member_int(requiredAttributes, attrNum))\n\t\t{\n\t\t\t/* In the subquery with only required attribute numbers, the result no\n\t\t\t * corresponds to the ordinal index of it in targetList.\n\t\t\t */\n\t\t\tTargetEntry *targetEntry =\n\t\t\t\tCreateTargetEntryForColumn(attributeTuple, SINGLE_RTE_INDEX, attrNum,\n\t\t\t\t\t\t\t\t\t\t   resultNo++);\n\t\t\ttargetList = lappend(targetList, targetEntry);\n\t\t}\n\t}\n\trelation_close(relation, NoLock);\n\treturn targetList;\n}\n\n\n/*\n * CreateDummyTargetList creates a target list which contains only a\n * NULL entry.\n */\nstatic List *\nCreateDummyTargetList(Oid relationId, List *requiredAttributes)\n{\n\tint resno = 1;\n\tTargetEntry *dummyTargetEntry = CreateUnusedTargetEntry(resno);\n\treturn list_make1(dummyTargetEntry);\n}\n\n\n/*\n * CreateTargetEntryForColumn creates a target entry for the given\n * column.\n */\nstatic TargetEntry *\nCreateTargetEntryForColumn(Form_pg_attribute attributeTuple, Index rteIndex,\n\t\t\t\t\t\t   int attributeNumber, int resno)\n{\n\tVar *targetColumn =\n\t\tmakeVar(rteIndex, attributeNumber, attributeTuple->atttypid,\n\t\t\t\tattributeTuple->atttypmod, attributeTuple->attcollation, 0);\n\tTargetEntry *targetEntry =\n\t\tmakeTargetEntry((Expr *) targetColumn, resno,\n\t\t\t\t\t\tpstrdup(attributeTuple->attname.data), false);\n\treturn targetEntry;\n}\n\n\n/*\n * CreateTargetEntryForNullCol creates a target entry that has a NULL expression.\n */\nstatic TargetEntry *\nCreateTargetEntryForNullCol(Form_pg_attribute attributeTuple, int resno)\n{\n\tExpr *nullExpr = (Expr *) makeNullConst(attributeTuple->atttypid,\n\t\t\t\t\t\t\t\t\t\t\tattributeTuple->atttypmod,\n\t\t\t\t\t\t\t\t\t\t\tattributeTuple->attcollation);\n\tchar *resName = attributeTuple->attname.data;\n\tTargetEntry *targetEntry =\n\t\tmakeTargetEntry(nullExpr, resno, pstrdup(resName), false);\n\treturn targetEntry;\n}\n\n\n/*\n * CreateUnusedTargetEntry creates a dummy target entry which is not used\n * in postgres query.\n */\nstatic TargetEntry *\nCreateUnusedTargetEntry(int resno)\n{\n\tStringInfo colname = makeStringInfo();\n\tappendStringInfo(colname, \"dummy-%d\", resno);\n\tExpr *nullExpr = (Expr *) makeNullConst(INT4OID,\n\t\t\t\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\t\t\t\tInvalidOid);\n\tTargetEntry *targetEntry =\n\t\tmakeTargetEntry(nullExpr, resno, colname->data, false);\n\treturn targetEntry;\n}\n\n\n/*\n * UnionRelationRestrictionLists merges two relation restriction lists\n * and returns a newly allocated list. The merged relation restriction\n * list doesn't contain any duplicate elements.\n */\nstatic List *\nUnionRelationRestrictionLists(List *firstRelationList, List *secondRelationList)\n{\n\tList *unionedRelationRestrictionList = NULL;\n\tListCell *relationRestrictionCell = NULL;\n\tRelids rteIdentities = NULL;\n\n\t/* list_concat destructively modifies the first list, thus copy it */\n\tfirstRelationList = list_copy(firstRelationList);\n\tList *allRestrictionList = list_concat(firstRelationList, secondRelationList);\n\n\tforeach(relationRestrictionCell, allRestrictionList)\n\t{\n\t\tRelationRestriction *restriction =\n\t\t\t(RelationRestriction *) lfirst(relationRestrictionCell);\n\t\tint rteIdentity = GetRTEIdentity(restriction->rte);\n\n\t\t/* already have the same rte, skip */\n\t\tif (bms_is_member(rteIdentity, rteIdentities))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tunionedRelationRestrictionList =\n\t\t\tlappend(unionedRelationRestrictionList, restriction);\n\n\t\trteIdentities = bms_add_member(rteIdentities, rteIdentity);\n\t}\n\n\tRelationRestrictionContext *unionedRestrictionContext = palloc0(\n\t\tsizeof(RelationRestrictionContext));\n\tunionedRestrictionContext->relationRestrictionList = unionedRelationRestrictionList;\n\n\treturn unionedRelationRestrictionList;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/query_pushdown_planning.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * query_pushdown_planning.c\n *\n * Routines for creating pushdown plans for queries. Both select and modify\n * queries can be planned using query pushdown logic passing the checks given\n * in this file.\n *\n * Checks are controlled to understand whether the query can be sent to worker\n * nodes by simply adding shard_id to table names and getting the correct result\n * from them. That means, all the required data is present on the workers.\n *\n * For select queries, Citus try to use query pushdown planner if it has a\n * subquery or function RTEs. For modify queries, Citus try to use query pushdown\n * planner if the query accesses multiple tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/pg_list.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"parser/parsetree.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_clauses.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/query_pushdown_planning.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/relation_restriction_equivalence.h\"\n#include \"distributed/version_compat.h\"\n\n\n#define INVALID_RELID -1\n\n/*\n * RecurringTuplesType is used to distinguish different types of expressions\n * that always produce the same set of tuples when a shard is queried. We make\n * this distinction to produce relevant error messages when recurring tuples\n * are used in a way that would give incorrect results.\n */\ntypedef enum RecurringTuplesType\n{\n\tRECURRING_TUPLES_INVALID = 0,\n\tRECURRING_TUPLES_REFERENCE_TABLE,\n\tRECURRING_TUPLES_FUNCTION,\n\tRECURRING_TUPLES_EMPTY_JOIN_TREE,\n\tRECURRING_TUPLES_RESULT_FUNCTION,\n\tRECURRING_TUPLES_VALUES,\n\tRECURRING_TUPLES_JSON_TABLE\n} RecurringTuplesType;\n\n/*\n * RelidsReferenceWalkerContext is used to find Vars in a (sub)query that\n * refer to certain relids from the upper query.\n */\ntypedef struct RelidsReferenceWalkerContext\n{\n\tint level;\n\tRelids relids;\n\tint foundRelid;\n} RelidsReferenceWalkerContext;\n\n\n/* Config variable managed via guc.c */\nbool SubqueryPushdown = false; /* is subquery pushdown enabled */\nint ValuesMaterializationThreshold = 100;\n\n/* Local functions forward declarations */\nstatic bool JoinTreeContainsSubqueryWalker(Node *joinTreeNode, void *context);\nstatic bool IsFunctionOrValuesRTE(Node *node);\nstatic bool WindowPartitionOnDistributionColumn(Query *query);\nstatic DeferredErrorMessage * DeferErrorIfFromClauseRecurs(Query *queryTree);\nstatic RecurringTuplesType FromClauseRecurringTupleType(Query *queryTree);\nstatic DeferredErrorMessage * DeferredErrorIfUnsupportedRecurringTuplesJoin(\n\tPlannerRestrictionContext *plannerRestrictionContext, bool plannerPhase);\nstatic DeferredErrorMessage * DeferErrorIfUnsupportedTableCombination(Query *queryTree);\nstatic DeferredErrorMessage * DeferErrorIfSubqueryRequiresMerge(Query *subqueryTree, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlateral,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar *referencedThing);\nstatic bool ExtractSetOperationStatementWalker(Node *node, List **setOperationList);\nstatic RecurringTuplesType FetchFirstRecurType(PlannerInfo *plannerInfo,\n\t\t\t\t\t\t\t\t\t\t\t   Relids relids);\nstatic bool ContainsRecurringRTE(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t RecurringTuplesType *recurType);\nstatic bool ContainsRecurringRangeTable(List *rangeTable, RecurringTuplesType *recurType);\nstatic bool HasRecurringTuples(Node *node, RecurringTuplesType *recurType);\nstatic MultiNode * SubqueryPushdownMultiNodeTree(Query *queryTree);\nstatic MultiTable * MultiSubqueryPushdownTable(Query *subquery);\nstatic List * CreateSubqueryTargetListAndAdjustVars(List *columnList);\nstatic AttrNumber FindResnoForVarInTargetList(List *targetList, int varno, int varattno);\nstatic bool RelationInfoContainsOnlyRecurringTuples(PlannerInfo *plannerInfo,\n\t\t\t\t\t\t\t\t\t\t\t\t\tRelids relids);\nstatic char * RecurringTypeDescription(RecurringTuplesType recurType);\nstatic DeferredErrorMessage * DeferredErrorIfUnsupportedLateralSubquery(PlannerInfo *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplannerInfo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRelids\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trecurringRelIds,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRelids\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnonRecurringRelIds);\nstatic bool ContainsLateralSubquery(PlannerInfo *plannerInfo);\nstatic Var * PartitionColumnForPushedDownSubquery(Query *query);\nstatic bool ContainsReferencesToRelids(Query *query, Relids relids, int *foundRelid);\nstatic bool ContainsReferencesToRelidsWalker(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t RelidsReferenceWalkerContext *context);\n\n\n/*\n * ShouldUseSubqueryPushDown determines whether it's desirable to use\n * subquery pushdown to plan the query based on the original and\n * rewritten query.\n */\nbool\nShouldUseSubqueryPushDown(Query *originalQuery, Query *rewrittenQuery,\n\t\t\t\t\t\t  PlannerRestrictionContext *plannerRestrictionContext)\n{\n\t/*\n\t * We check the existence of subqueries in FROM clause on the modified query\n\t * given that if postgres already flattened the subqueries, MultiNodeTree()\n\t * can plan corresponding distributed plan.\n\t */\n\tif (JoinTreeContainsSubquery(rewrittenQuery))\n\t{\n\t\treturn true;\n\t}\n\n\t/*\n\t * We check the existence of subqueries in WHERE and HAVING clause on the\n\t * modified query. In some cases subqueries in the original query are\n\t * converted into inner joins and in those cases MultiNodeTree() can plan\n\t * the rewritten plan.\n\t */\n\tif (WhereOrHavingClauseContainsSubquery(rewrittenQuery))\n\t{\n\t\treturn true;\n\t}\n\n\t/*\n\t * We check the existence of subqueries in the SELECT clause on the modified\n\t * query.\n\t */\n\tif (TargetListContainsSubquery(rewrittenQuery->targetList))\n\t{\n\t\treturn true;\n\t}\n\n\n\t/*\n\t * We check if postgres planned any semi joins, MultiNodeTree doesn't\n\t * support these so we fail. Postgres is able to replace some IN/ANY\n\t * subqueries with semi joins and then replace those with inner joins (ones\n\t * where the subquery returns unique results). This allows MultiNodeTree to\n\t * execute these subqueries (because they are converted to inner joins).\n\t * However, even in that case the rewrittenQuery still contains join nodes\n\t * with jointype JOIN_SEMI because Postgres doesn't actually update these.\n\t * The way we find out instead if it actually planned semi joins, is by\n\t * checking the joins that were sent to multi_join_restriction_hook. If no\n\t * joins of type JOIN_SEMI are sent it is safe to convert all JOIN_SEMI\n\t * nodes to JOIN_INNER nodes (which is what is done in MultiNodeTree).\n\t */\n\tJoinRestrictionContext *joinRestrictionContext =\n\t\tplannerRestrictionContext->joinRestrictionContext;\n\tif (joinRestrictionContext->hasSemiJoin)\n\t{\n\t\treturn true;\n\t}\n\n\n\t/*\n\t * We process function and VALUES RTEs as subqueries, since the join order planner\n\t * does not know how to handle them.\n\t */\n\tif (FindNodeMatchingCheckFunction((Node *) originalQuery, IsFunctionOrValuesRTE))\n\t{\n\t\treturn true;\n\t}\n\n\t/*\n\t * We handle outer joins as subqueries, since the join order planner\n\t * does not know how to handle them.\n\t */\n\tif (FindNodeMatchingCheckFunction((Node *) originalQuery->jointree, IsOuterJoinExpr))\n\t{\n\t\treturn true;\n\t}\n\n\t/*\n\t * Original query may not have an outer join while rewritten query does.\n\t * We should push down in this case.\n\t * An example of this is https://github.com/citusdata/citus/issues/2739\n\t * where postgres pulls-up the outer-join in the subquery.\n\t */\n\tif (FindNodeMatchingCheckFunction((Node *) rewrittenQuery->jointree, IsOuterJoinExpr))\n\t{\n\t\treturn true;\n\t}\n\n\t/*\n\t * Some unsupported join clauses in logical planner\n\t * may be supported by subquery pushdown planner.\n\t */\n\tList *qualifierList = QualifierList(rewrittenQuery->jointree);\n\tif (DeferErrorIfUnsupportedClause(qualifierList) != NULL)\n\t{\n\t\treturn true;\n\t}\n\n\t/* check if the query has a window function and it is safe to pushdown */\n\tif (originalQuery->hasWindowFuncs &&\n\t\tSafeToPushdownWindowFunction(originalQuery, NULL))\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * JoinTreeContainsSubquery returns true if the input query contains any subqueries\n * in the join tree (e.g., FROM clause).\n */\nbool\nJoinTreeContainsSubquery(Query *query)\n{\n\tFromExpr *joinTree = query->jointree;\n\n\tif (!joinTree)\n\t{\n\t\treturn false;\n\t}\n\n\treturn JoinTreeContainsSubqueryWalker((Node *) joinTree, query);\n}\n\n\n/*\n * HasEmptyJoinTree returns whether the query selects from anything.\n */\nbool\nHasEmptyJoinTree(Query *query)\n{\n\tif (query->rtable == NIL)\n\t{\n\t\treturn true;\n\t}\n\telse if (list_length(query->rtable) == 1)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) linitial(query->rtable);\n\t\tif (rte->rtekind == RTE_RESULT)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * JoinTreeContainsSubqueryWalker returns true if the input joinTreeNode\n * references to a subquery. Otherwise, recurses into the expression.\n */\nstatic bool\nJoinTreeContainsSubqueryWalker(Node *joinTreeNode, void *context)\n{\n\tif (joinTreeNode == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(joinTreeNode, RangeTblRef))\n\t{\n\t\tQuery *query = (Query *) context;\n\n\t\tRangeTblRef *rangeTableRef = (RangeTblRef *) joinTreeNode;\n\t\tRangeTblEntry *rangeTableEntry = rt_fetch(rangeTableRef->rtindex, query->rtable);\n\n\t\tif (rangeTableEntry->rtekind == RTE_SUBQUERY)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\treturn expression_tree_walker(joinTreeNode, JoinTreeContainsSubqueryWalker, context);\n}\n\n\n/*\n * WhereOrHavingClauseContainsSubquery returns true if the input query contains\n * any subqueries in the WHERE or HAVING clause.\n */\nbool\nWhereOrHavingClauseContainsSubquery(Query *query)\n{\n\tif (FindNodeMatchingCheckFunction(query->havingQual, IsNodeSubquery))\n\t{\n\t\treturn true;\n\t}\n\n\tif (!query->jointree)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * We search the whole jointree here, not just the quals. The reason for\n\t * this is that the fromlist can contain other FromExpr nodes again or\n\t * JoinExpr nodes that also have quals. If that's the case we need to check\n\t * those as well if they contain andy subqueries.\n\t */\n\treturn FindNodeMatchingCheckFunction((Node *) query->jointree, IsNodeSubquery);\n}\n\n\n/*\n * TargetList returns true if the input query contains\n * any subqueries in the WHERE clause.\n */\nbool\nTargetListContainsSubquery(List *targetList)\n{\n\tbool hasSubquery = FindNodeMatchingCheckFunction((Node *) targetList, IsNodeSubquery);\n\n\treturn hasSubquery;\n}\n\n\n/*\n * IsFunctionRTE determines whether the given node is a function RTE.\n */\nstatic bool\nIsFunctionOrValuesRTE(Node *node)\n{\n\tif (IsA(node, RangeTblEntry))\n\t{\n\t\tRangeTblEntry *rangeTblEntry = (RangeTblEntry *) node;\n\n\t\tif (rangeTblEntry->rtekind == RTE_FUNCTION ||\n\t\t\trangeTblEntry->rtekind == RTE_VALUES ||\n\t\t\tIsJsonTableRTE(rangeTblEntry))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * IsNodeSubquery returns true if the given node is a Query or SubPlan or a\n * Param node with paramkind PARAM_EXEC.\n *\n * The check for SubPlan is needed when this is used on a already rewritten\n * query. Such a query has SubPlan nodes instead of SubLink nodes (which\n * contain a Query node).\n * The check for PARAM_EXEC is needed because some very simple subqueries like\n * (select 1) are converted to init plans in the rewritten query. In this case\n * the only thing left in the query tree is a Param node with type PARAM_EXEC.\n */\nbool\nIsNodeSubquery(Node *node)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Query) || IsA(node, SubPlan))\n\t{\n\t\treturn true;\n\t}\n\n\tif (!IsA(node, Param))\n\t{\n\t\treturn false;\n\t}\n\treturn ((Param *) node)->paramkind == PARAM_EXEC;\n}\n\n\n/*\n * IsOuterJoinExpr returns whether the given node is an outer join expression.\n */\nbool\nIsOuterJoinExpr(Node *node)\n{\n\tbool isOuterJoin = false;\n\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, JoinExpr))\n\t{\n\t\tJoinExpr *joinExpr = (JoinExpr *) node;\n\t\tJoinType joinType = joinExpr->jointype;\n\t\tif (IS_OUTER_JOIN(joinType))\n\t\t{\n\t\t\tisOuterJoin = true;\n\t\t}\n\t}\n\n\treturn isOuterJoin;\n}\n\n\n/*\n * SafeToPushdownWindowFunction checks if the query with window function is supported.\n * Returns the result accordingly and modifies errorDetail if non null.\n */\nbool\nSafeToPushdownWindowFunction(Query *query, StringInfo *errorDetail)\n{\n\tListCell *windowClauseCell = NULL;\n\tList *windowClauseList = query->windowClause;\n\n\t/*\n\t * We need to check each window clause separately if there is a partition by clause\n\t * and if it is partitioned on the distribution column.\n\t */\n\tforeach(windowClauseCell, windowClauseList)\n\t{\n\t\tWindowClause *windowClause = lfirst(windowClauseCell);\n\n\t\tif (!windowClause->partitionClause)\n\t\t{\n\t\t\tif (errorDetail)\n\t\t\t{\n\t\t\t\t*errorDetail = makeStringInfo();\n\t\t\t\tappendStringInfoString(*errorDetail,\n\t\t\t\t\t\t\t\t\t   \"Window functions without PARTITION BY on distribution \"\n\t\t\t\t\t\t\t\t\t   \"column is currently unsupported\");\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (!WindowPartitionOnDistributionColumn(query))\n\t{\n\t\tif (errorDetail)\n\t\t{\n\t\t\t*errorDetail = makeStringInfo();\n\t\t\tappendStringInfoString(*errorDetail,\n\t\t\t\t\t\t\t\t   \"Window functions with PARTITION BY list missing distribution \"\n\t\t\t\t\t\t\t\t   \"column is currently unsupported\");\n\t\t}\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * WindowPartitionOnDistributionColumn checks if the given subquery has one\n * or more window functions and at least one of them is not partitioned by\n * distribution column. The function returns false if your window function does not\n * have a partition by clause or it does not include the distribution column.\n *\n * Please note that if the query does not have a window function, the function\n * returns true.\n */\nstatic bool\nWindowPartitionOnDistributionColumn(Query *query)\n{\n\tList *windowClauseList = query->windowClause;\n\tListCell *windowClauseCell = NULL;\n\n\tforeach(windowClauseCell, windowClauseList)\n\t{\n\t\tWindowClause *windowClause = lfirst(windowClauseCell);\n\t\tList *partitionClauseList = windowClause->partitionClause;\n\t\tList *targetEntryList = query->targetList;\n\n\t\tList *groupTargetEntryList =\n\t\t\tGroupTargetEntryList(partitionClauseList, targetEntryList);\n\n\t\tbool partitionOnDistributionColumn =\n\t\t\tTargetListOnPartitionColumn(query, groupTargetEntryList);\n\n\t\tif (!partitionOnDistributionColumn)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * SubqueryMultiNodeTree gets the query objects and returns logical plan\n * for subqueries.\n *\n * We currently have two different code paths for creating logic plan for subqueries:\n *   (i) subquery pushdown\n *   (ii) single relation repartition subquery\n *\n * In order to create the logical plan, we follow the algorithm below:\n *    -  If subquery pushdown planner can plan the query\n *        -  We're done, we create the multi plan tree and return\n *    -  Else\n *       - If the query is not eligible for single table repartition subquery planning\n *            - Throw the error that the subquery pushdown planner generated\n *       - If it is eligible for single table repartition subquery planning\n *            - Check for the errors for single table repartition subquery planning\n *                - If no errors found, we're done. Create the multi plan and return\n *                - If found errors, throw it\n */\nMultiNode *\nSubqueryMultiNodeTree(Query *originalQuery, Query *queryTree,\n\t\t\t\t\t  PlannerRestrictionContext *plannerRestrictionContext)\n{\n\t/*\n\t * This is a generic error check that applies to both subquery pushdown\n\t * and single table repartition subquery.\n\t */\n\tDeferredErrorMessage *unsupportedQueryError = DeferErrorIfQueryNotSupported(\n\t\toriginalQuery);\n\tif (unsupportedQueryError != NULL)\n\t{\n\t\tRaiseDeferredError(unsupportedQueryError, ERROR);\n\t}\n\n\t/*\n\t * We reach here at the third step of the planning, thus we already checked for pushed down\n\t * feasibility of recurring outer joins, at this step the unsupported outer join check should\n\t * only generate an error when there is a lateral subquery.\n\t */\n\tDeferredErrorMessage *subqueryPushdownError = DeferErrorIfUnsupportedSubqueryPushdown(\n\t\toriginalQuery,\n\t\tplannerRestrictionContext,\n\t\tfalse);\n\n\tif (subqueryPushdownError != NULL)\n\t{\n\t\tRaiseDeferredError(subqueryPushdownError, ERROR);\n\t}\n\n\tMultiNode *multiQueryNode = SubqueryPushdownMultiNodeTree(originalQuery);\n\n\tAssert(multiQueryNode != NULL);\n\n\treturn multiQueryNode;\n}\n\n\n/*\n * DeferErrorIfContainsUnsupportedSubqueryPushdown iterates on the query's subquery\n * entry list and uses helper functions to check if we can push down subquery\n * to worker nodes. These helper functions returns a deferred error if we\n * cannot push down the subquery.\n */\nDeferredErrorMessage *\nDeferErrorIfUnsupportedSubqueryPushdown(Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\tPlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\tbool plannerPhase)\n{\n\tbool outerMostQueryHasLimit = false;\n\tListCell *subqueryCell = NULL;\n\tList *subqueryList = NIL;\n\n\tif (originalQuery->limitCount != NULL)\n\t{\n\t\touterMostQueryHasLimit = true;\n\t}\n\n\t/*\n\t * We're checking two things here:\n\t *    (i)   If the query contains a top level union, ensure that all leaves\n\t *          return the partition key at the same position\n\t *    (ii)  Else, check whether all relations joined on the partition key or not\n\t */\n\tif (ContainsUnionSubquery(originalQuery))\n\t{\n\t\tif (!SafeToPushdownUnionSubquery(originalQuery, plannerRestrictionContext))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"cannot pushdown the subquery since not all subqueries \"\n\t\t\t\t\t\t\t\t \"in the UNION have the partition column in the same \"\n\t\t\t\t\t\t\t\t \"position\",\n\t\t\t\t\t\t\t\t \"Each leaf query of the UNION should return the \"\n\t\t\t\t\t\t\t\t \"partition column in the same position and all joins \"\n\t\t\t\t\t\t\t\t \"must be on the partition column\",\n\t\t\t\t\t\t\t\t NULL);\n\t\t}\n\t}\n\telse if (!RestrictionEquivalenceForPartitionKeys(plannerRestrictionContext))\n\t{\n\t\tStringInfo errorMessage = makeStringInfo();\n\t\tbool isMergeCmd = IsMergeQuery(originalQuery);\n\t\tappendStringInfo(errorMessage,\n\t\t\t\t\t\t \"%s\"\n\t\t\t\t\t\t \"only supported when all distributed tables are \"\n\t\t\t\t\t\t \"co-located and joined on their distribution columns\",\n\t\t\t\t\t\t isMergeCmd ? \"MERGE command is \" : \"complex joins are \");\n\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t errorMessage->data, NULL, NULL);\n\t}\n\n\t/* we shouldn't allow reference tables in the FROM clause when the query has sublinks */\n\tDeferredErrorMessage *error = DeferErrorIfFromClauseRecurs(originalQuery);\n\tif (error)\n\t{\n\t\treturn error;\n\t}\n\n\terror = DeferredErrorIfUnsupportedRecurringTuplesJoin(plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  plannerPhase);\n\tif (error)\n\t{\n\t\treturn error;\n\t}\n\n\t/*\n\t * We first extract all the queries that appear in the original query. Later,\n\t * we delete the original query given that error rules does not apply to the\n\t * top level query. For instance, we could support any LIMIT/ORDER BY on the\n\t * top level query.\n\t */\n\tExtractQueryWalker((Node *) originalQuery, &subqueryList);\n\tsubqueryList = list_delete(subqueryList, originalQuery);\n\n\t/* iterate on the subquery list and error out accordingly */\n\tforeach(subqueryCell, subqueryList)\n\t{\n\t\tQuery *subquery = lfirst(subqueryCell);\n\t\terror = DeferErrorIfCannotPushdownSubquery(subquery,\n\t\t\t\t\t\t\t\t\t\t\t\t   outerMostQueryHasLimit);\n\t\tif (error)\n\t\t{\n\t\t\treturn error;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * DeferErrorIfFromClauseRecurs returns a deferred error if the\n * given query is not suitable for subquery pushdown.\n *\n * While planning sublinks, we rely on Postgres in the sense that it converts some of\n * sublinks into joins.\n *\n * In some cases, sublinks are pulled up and converted into outer joins. Those cases\n * are already handled with RecursivelyPlanRecurringTupleOuterJoinWalker() or thrown\n * an error for in DeferredErrorIfUnsupportedRecurringTuplesJoin().\n *\n * If the sublinks are not pulled up, we should still error out in if the expression\n * in the FROM clause would recur for every shard in a subquery on the WHERE clause.\n *\n * Otherwise, the result would include duplicate rows.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfFromClauseRecurs(Query *queryTree)\n{\n\tif (!queryTree->hasSubLinks)\n\t{\n\t\treturn NULL;\n\t}\n\n\tRecurringTuplesType recurType = FromClauseRecurringTupleType(queryTree);\n\tif (recurType == RECURRING_TUPLES_REFERENCE_TABLE)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"correlated subqueries are not supported when \"\n\t\t\t\t\t\t\t \"the FROM clause contains a reference table\", NULL, NULL);\n\t}\n\telse if (recurType == RECURRING_TUPLES_FUNCTION)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"correlated subqueries are not supported when \"\n\t\t\t\t\t\t\t \"the FROM clause contains a set returning function\", NULL,\n\t\t\t\t\t\t\t NULL);\n\t}\n\telse if (recurType == RECURRING_TUPLES_RESULT_FUNCTION)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"correlated subqueries are not supported when \"\n\t\t\t\t\t\t\t \"the FROM clause contains a CTE or subquery\", NULL, NULL);\n\t}\n\telse if (recurType == RECURRING_TUPLES_EMPTY_JOIN_TREE)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"correlated subqueries are not supported when \"\n\t\t\t\t\t\t\t \"the FROM clause contains a subquery without FROM\", NULL,\n\t\t\t\t\t\t\t NULL);\n\t}\n\telse if (recurType == RECURRING_TUPLES_VALUES)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"correlated subqueries are not supported when \"\n\t\t\t\t\t\t\t \"the FROM clause contains VALUES\", NULL,\n\t\t\t\t\t\t\t NULL);\n\t}\n\telse if (recurType == RECURRING_TUPLES_JSON_TABLE)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"correlated subqueries are not supported when \"\n\t\t\t\t\t\t\t \"the FROM clause contains JSON_TABLE\", NULL,\n\t\t\t\t\t\t\t NULL);\n\t}\n\n\n\t/*\n\t * We get here when there is neither a distributed table, nor recurring tuples.\n\t * That usually means that there isn't a FROM at all (only sublinks), this\n\t * implies that queryTree is recurring, but whether this is a problem depends\n\t * on outer queries, not on queryTree itself.\n\t */\n\n\treturn NULL;\n}\n\n\n/*\n * FromClauseRecurringTupleType returns tuple recurrence information\n * in query result based on range table entries in from clause.\n *\n * Returned information is used to prepare appropriate deferred error\n * message for subquery pushdown checks.\n */\nstatic RecurringTuplesType\nFromClauseRecurringTupleType(Query *queryTree)\n{\n\tRecurringTuplesType recurType = RECURRING_TUPLES_INVALID;\n\n\tif (HasEmptyJoinTree(queryTree))\n\t{\n\t\treturn RECURRING_TUPLES_EMPTY_JOIN_TREE;\n\t}\n\n\tif (FindNodeMatchingCheckFunctionInRangeTableList(queryTree->rtable,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  IsDistributedTableRTE))\n\t{\n\t\t/*\n\t\t * There is a distributed table somewhere in the FROM clause.\n\t\t *\n\t\t * In the typical case this means that the query does not recur,\n\t\t * but there are two exceptions:\n\t\t *\n\t\t * - outer joins such as reference_table LEFT JOIN distributed_table\n\t\t * - FROM reference_table WHERE .. (SELECT .. FROM distributed_table) ..\n\t\t *\n\t\t * However, we check all subqueries and joins separately, so we would\n\t\t * find such conditions in other calls.\n\t\t */\n\t\treturn RECURRING_TUPLES_INVALID;\n\t}\n\n\t/*\n\t * Try to figure out which type of recurring tuples we have to produce a\n\t * relevant error message. If there are several we'll pick the first one.\n\t */\n\tContainsRecurringRangeTable(queryTree->rtable, &recurType);\n\n\treturn recurType;\n}\n\n\n/*\n * DeferredErrorIfUnsupportedRecurringTuplesJoin returns a DeferredError if\n * there exists a join between a recurring rel (such as reference tables\n * and intermediate_results) and a non-recurring rel (such as distributed tables\n * and subqueries that we can push-down to worker nodes) when plannerPhase is\n * true, so that we try to recursively plan these joins.\n * During recursive planning phase, we either replace those with recursive plans\n * or leave them if it is safe to push-down.\n * During the logical planning phase (plannerPhase is false), we only check if\n * such queries have lateral subqueries.\n */\nstatic DeferredErrorMessage *\nDeferredErrorIfUnsupportedRecurringTuplesJoin(PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t  bool plannerPhase)\n{\n\tList *joinRestrictionList =\n\t\tplannerRestrictionContext->joinRestrictionContext->joinRestrictionList;\n\tListCell *joinRestrictionCell = NULL;\n\tRecurringTuplesType recurType = RECURRING_TUPLES_INVALID;\n\tforeach(joinRestrictionCell, joinRestrictionList)\n\t{\n\t\tJoinRestriction *joinRestriction = (JoinRestriction *) lfirst(\n\t\t\tjoinRestrictionCell);\n\t\tJoinType joinType = joinRestriction->joinType;\n\t\tPlannerInfo *plannerInfo = joinRestriction->plannerInfo;\n\t\tRelids innerrelRelids = joinRestriction->innerrelRelids;\n\t\tRelids outerrelRelids = joinRestriction->outerrelRelids;\n\n\t\t/*\n\t\t * This loop aims to determine whether this join is between a recurring\n\t\t * rel and a non-recurring rel, and if so, whether it can yield an incorrect\n\t\t * result set due to recurring tuples.\n\t\t *\n\t\t * For outer joins, this can only happen if it's a lateral outer join\n\t\t * where the inner distributed subquery references the recurring outer\n\t\t * rel. This because, such outer joins should not appear here because\n\t\t * the recursive planner (RecursivelyPlanRecurringTupleOuterJoinWalker)\n\t\t * should have already planned the non-recurring side if it wasn't a\n\t\t * lateral join. For this reason, if the outer join is between a recurring\n\t\t * rel --on the outer side-- and a non-recurring rel --on the other side--,\n\t\t * we throw an error assuming that it's a lateral outer join.\n\t\t * Also note that; in the context of outer joins, we only check left outer\n\t\t * and full outer joins because PostgreSQL converts right joins to left\n\t\t * joins before passing them through \"set_join_pathlist_hook\"s.\n\t\t *\n\t\t * For semi / anti joins, we anyway throw an error when the inner\n\t\t * side is a distributed subquery that references a recurring outer rel\n\t\t * (in the FROM clause) thanks to DeferErrorIfFromClauseRecurs. And when\n\t\t * the inner side is a recurring rel and the outer side a non-recurring\n\t\t * one, then the non-recurring side can't reference the recurring side\n\t\t * anyway.\n\t\t *\n\t\t * For those reasons, here we perform below lateral join checks only for\n\t\t * outer (except anti) / inner joins but not for anti / semi joins.\n\t\t */\n\n\t\tif (joinType == JOIN_LEFT)\n\t\t{\n\t\t\tif (RelationInfoContainsOnlyRecurringTuples(plannerInfo, innerrelRelids))\n\t\t\t{\n\t\t\t\t/* inner side only contains recurring rels */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (RelationInfoContainsOnlyRecurringTuples(plannerInfo, outerrelRelids))\n\t\t\t{\n\t\t\t\tif (plannerPhase)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * We have not yet tried to recursively plan this join, we should\n\t\t\t\t\t * defer an error.\n\t\t\t\t\t */\n\t\t\t\t\trecurType = FetchFirstRecurType(plannerInfo, outerrelRelids);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * Inner side contains distributed rels but the outer side only\n\t\t\t\t * contains recurring rels, might be an unsupported lateral outer\n\t\t\t\t * join.\n\t\t\t\t * Note that plannerInfo->hasLateralRTEs is not always set to\n\t\t\t\t * true, so here we check rtes, see ContainsLateralSubquery for details.\n\t\t\t\t */\n\n\t\t\t\tif (ContainsLateralSubquery(plannerInfo))\n\t\t\t\t{\n\t\t\t\t\trecurType = FetchFirstRecurType(plannerInfo, outerrelRelids);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (joinType == JOIN_FULL)\n\t\t{\n\t\t\tbool innerContainOnlyRecurring =\n\t\t\t\tRelationInfoContainsOnlyRecurringTuples(plannerInfo, innerrelRelids);\n\t\t\tbool outerContainOnlyRecurring =\n\t\t\t\tRelationInfoContainsOnlyRecurringTuples(plannerInfo, outerrelRelids);\n\n\t\t\tif (innerContainOnlyRecurring && !outerContainOnlyRecurring)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Right side contains distributed rels but the left side only\n\t\t\t\t * contains recurring rels, must be an unsupported lateral outer\n\t\t\t\t * join.\n\t\t\t\t */\n\t\t\t\trecurType = FetchFirstRecurType(plannerInfo, innerrelRelids);\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!innerContainOnlyRecurring && outerContainOnlyRecurring)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Left side contains distributed rels but the right side only\n\t\t\t\t * contains recurring rels, must be an unsupported lateral outer\n\t\t\t\t * join.\n\t\t\t\t */\n\t\t\t\trecurType = FetchFirstRecurType(plannerInfo, outerrelRelids);\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\telse if (joinType == JOIN_INNER && plannerInfo->hasLateralRTEs)\n\t\t{\n\t\t\t/*\n\t\t\t * Sometimes we cannot push down INNER JOINS when they have only\n\t\t\t * recurring tuples on one side and a lateral on the other side.\n\t\t\t * See comment on DeferredErrorIfUnsupportedLateralSubquery for\n\t\t\t * details.\n\t\t\t *\n\t\t\t * When planning inner joins, postgres can move RTEs from left to\n\t\t\t * right and from right to left. So we don't know on which side the\n\t\t\t * lateral join wil appear. Thus we try to find a side of the join\n\t\t\t * that only contains recurring tuples. And then we check the other\n\t\t\t * side to see if it contains an unsupported lateral join.\n\t\t\t *\n\t\t\t */\n\t\t\tif (RelationInfoContainsOnlyRecurringTuples(plannerInfo, innerrelRelids))\n\t\t\t{\n\t\t\t\tDeferredErrorMessage *deferredError =\n\t\t\t\t\tDeferredErrorIfUnsupportedLateralSubquery(plannerInfo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  innerrelRelids,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  outerrelRelids);\n\t\t\t\tif (deferredError)\n\t\t\t\t{\n\t\t\t\t\treturn deferredError;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (RelationInfoContainsOnlyRecurringTuples(plannerInfo, outerrelRelids))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * This branch uses \"else if\" instead of \"if\", because if both\n\t\t\t\t * sides contain only recurring tuples there will never be an\n\t\t\t\t * unsupported lateral subquery.\n\t\t\t\t */\n\t\t\t\tDeferredErrorMessage *deferredError =\n\t\t\t\t\tDeferredErrorIfUnsupportedLateralSubquery(plannerInfo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  outerrelRelids,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  innerrelRelids);\n\t\t\t\tif (deferredError)\n\t\t\t\t{\n\t\t\t\t\treturn deferredError;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (recurType != RECURRING_TUPLES_INVALID)\n\t{\n\t\tchar *errmsg = psprintf(\"cannot perform a lateral outer join when \"\n\t\t\t\t\t\t\t\t\"a distributed subquery references %s\",\n\t\t\t\t\t\t\t\tRecurringTypeDescription(recurType));\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t errmsg, NULL, NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * CanPushdownSubquery checks if we can push down the given\n * subquery to worker nodes.\n */\nbool\nCanPushdownSubquery(Query *subqueryTree, bool outerMostQueryHasLimit)\n{\n\treturn DeferErrorIfCannotPushdownSubquery(subqueryTree, outerMostQueryHasLimit) ==\n\t\t   NULL;\n}\n\n\n/*\n * DeferErrorIfCannotPushdownSubquery checks if we can push down the given\n * subquery to worker nodes. If we cannot push down the subquery, this function\n * returns a deferred error.\n *\n * We can push down a subquery if it follows rules below:\n * a. If there is an aggregate, it must be grouped on partition column.\n * b. If there is a join, it must be between two regular tables or two subqueries.\n * We don't support join between a regular table and a subquery. And columns on\n * the join condition must be partition columns.\n * c. If there is a distinct clause, it must be on the partition column.\n *\n * This function is very similar to DeferErrorIfQueryNotSupported() in logical\n * planner, but we don't reuse it, because differently for subqueries we support\n * a subset of distinct, union and left joins.\n *\n * Note that this list of checks is not exhaustive, there can be some cases\n * which we let subquery to run but returned results would be wrong. Such as if\n * a subquery has a group by on another subquery which includes order by with\n * limit, we let this query to run, but results could be wrong depending on the\n * features of underlying tables.\n */\nDeferredErrorMessage *\nDeferErrorIfCannotPushdownSubquery(Query *subqueryTree, bool outerMostQueryHasLimit)\n{\n\tbool preconditionsSatisfied = true;\n\tchar *errorDetail = NULL;\n\n\tDeferredErrorMessage *deferredError = DeferErrorIfUnsupportedTableCombination(\n\t\tsubqueryTree);\n\tif (deferredError)\n\t{\n\t\treturn deferredError;\n\t}\n\n\tif (HasEmptyJoinTree(subqueryTree) &&\n\t\tcontain_mutable_functions((Node *) subqueryTree->targetList))\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"Subqueries without a FROM clause can only contain immutable \"\n\t\t\t\t\t  \"functions\";\n\t}\n\n\t/*\n\t * Correlated subqueries are effectively functions that are repeatedly called\n\t * for the values of the vars that point to the outer query. We can liberally\n\t * push down SQL features within such a function, as long as co-located join\n\t * checks are applied.\n\t */\n\tif (!ContainsReferencesToOuterQuery(subqueryTree))\n\t{\n\t\tdeferredError = DeferErrorIfSubqueryRequiresMerge(subqueryTree, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"another query\");\n\t\tif (deferredError)\n\t\t{\n\t\t\treturn deferredError;\n\t\t}\n\t}\n\n\t/*\n\t * Limit is partially supported when SubqueryPushdown is set.\n\t * The outermost query must have a limit clause.\n\t */\n\tif (subqueryTree->limitCount && SubqueryPushdown && !outerMostQueryHasLimit)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"Limit in subquery without limit in the outermost query is \"\n\t\t\t\t\t  \"unsupported\";\n\t}\n\n\tif (subqueryTree->setOperations)\n\t{\n\t\tdeferredError = DeferErrorIfUnsupportedUnionQuery(subqueryTree);\n\t\tif (deferredError)\n\t\t{\n\t\t\treturn deferredError;\n\t\t}\n\t}\n\n\tif (subqueryTree->hasRecursive)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"Recursive queries are currently unsupported\";\n\t}\n\n\tif (subqueryTree->cteList)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"Common Table Expressions are currently unsupported\";\n\t}\n\n\tif (subqueryTree->hasForUpdate)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"For Update/Share commands are currently unsupported\";\n\t}\n\n\t/* grouping sets are not allowed in subqueries*/\n\tif (subqueryTree->groupingSets)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = \"could not run distributed query with GROUPING SETS, CUBE, \"\n\t\t\t\t\t  \"or ROLLUP\";\n\t}\n\n\tdeferredError = DeferErrorIfFromClauseRecurs(subqueryTree);\n\tif (deferredError)\n\t{\n\t\treturn deferredError;\n\t}\n\n\n\t/* finally check and return deferred if not satisfied */\n\tif (!preconditionsSatisfied)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot push down this subquery\",\n\t\t\t\t\t\t\t errorDetail, NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * FlattenGroupExprs flattens the GROUP BY expressions in the query tree\n * by replacing VAR nodes referencing the GROUP range table with the actual\n * GROUP BY expression. This is used by Citus planning to ensure correctness\n * when analysing and building the distributed plan.\n */\nvoid\nFlattenGroupExprs(Query *queryTree)\n{\n#if PG_VERSION_NUM >= PG_VERSION_18\n\tif (queryTree->hasGroupRTE)\n\t{\n\t\tqueryTree->targetList = (List *)\n\t\t\t\t\t\t\t\tflatten_group_exprs(NULL, queryTree,\n\t\t\t\t\t\t\t\t\t\t\t\t\t(Node *) queryTree->targetList);\n\t\tqueryTree->havingQual =\n\t\t\tflatten_group_exprs(NULL, queryTree, queryTree->havingQual);\n\t}\n#endif\n}\n\n\n/*\n * DeferErrorIfSubqueryRequiresMerge returns a deferred error if the subquery\n * requires a merge step on the coordinator (e.g. limit, group by non-distribution\n * column, etc.).\n */\nstatic DeferredErrorMessage *\nDeferErrorIfSubqueryRequiresMerge(Query *subqueryTree, bool lateral,\n\t\t\t\t\t\t\t\t  char *referencedThing)\n{\n\tbool preconditionsSatisfied = true;\n\tchar *errorDetail = NULL;\n\n\tchar *lateralString = lateral ? \"lateral \" : \"\";\n\n\tif (subqueryTree->limitOffset)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = psprintf(\"Offset clause is currently unsupported when a %ssubquery \"\n\t\t\t\t\t\t\t   \"references a column from %s\", lateralString,\n\t\t\t\t\t\t\t   referencedThing);\n\t}\n\n\t/* limit is not supported when SubqueryPushdown is not set */\n\tif (subqueryTree->limitCount && !SubqueryPushdown)\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = psprintf(\"Limit clause is currently unsupported when a \"\n\t\t\t\t\t\t\t   \"%ssubquery references a column from %s\", lateralString,\n\t\t\t\t\t\t\t   referencedThing);\n\t}\n\n\t/* group clause list must include partition column */\n\tif (subqueryTree->groupClause)\n\t{\n\t\tList *groupClauseList = subqueryTree->groupClause;\n\t\tList *targetEntryList = subqueryTree->targetList;\n\t\tList *groupTargetEntryList = GroupTargetEntryList(groupClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  targetEntryList);\n\t\tbool groupOnPartitionColumn =\n\t\t\tTargetListOnPartitionColumn(subqueryTree, groupTargetEntryList);\n\t\tif (!groupOnPartitionColumn)\n\t\t{\n\t\t\tpreconditionsSatisfied = false;\n\t\t\terrorDetail = psprintf(\"Group by list without partition column is currently \"\n\t\t\t\t\t\t\t\t   \"unsupported when a %ssubquery references a column \"\n\t\t\t\t\t\t\t\t   \"from %s\", lateralString, referencedThing);\n\t\t}\n\t}\n\n\t/* we don't support aggregates without group by */\n\tif (subqueryTree->hasAggs && (subqueryTree->groupClause == NULL))\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = psprintf(\"Aggregates without group by are currently unsupported \"\n\t\t\t\t\t\t\t   \"when a %ssubquery references a column from %s\",\n\t\t\t\t\t\t\t   lateralString, referencedThing);\n\t}\n\n\t/* having clause without group by on partition column is not supported */\n\tif (subqueryTree->havingQual && (subqueryTree->groupClause == NULL))\n\t{\n\t\tpreconditionsSatisfied = false;\n\t\terrorDetail = psprintf(\"Having qual without group by on partition column is \"\n\t\t\t\t\t\t\t   \"currently unsupported when a %ssubquery references \"\n\t\t\t\t\t\t\t   \"a column from %s\", lateralString, referencedThing);\n\t}\n\n\t/*\n\t * We support window functions when the window function\n\t * is partitioned on distribution column.\n\t */\n\tStringInfo errorInfo = NULL;\n\tif (subqueryTree->hasWindowFuncs && !SafeToPushdownWindowFunction(subqueryTree,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &errorInfo))\n\t{\n\t\terrorDetail = (char *) errorInfo->data;\n\t\tpreconditionsSatisfied = false;\n\t}\n\n\t/* distinct clause list must include partition column */\n\tif (subqueryTree->distinctClause)\n\t{\n\t\tList *distinctClauseList = subqueryTree->distinctClause;\n\t\tList *targetEntryList = subqueryTree->targetList;\n\t\tList *distinctTargetEntryList = GroupTargetEntryList(distinctClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t targetEntryList);\n\t\tbool distinctOnPartitionColumn =\n\t\t\tTargetListOnPartitionColumn(subqueryTree, distinctTargetEntryList);\n\t\tif (!distinctOnPartitionColumn)\n\t\t{\n\t\t\tpreconditionsSatisfied = false;\n\t\t\terrorDetail = \"Distinct on columns without partition column is \"\n\t\t\t\t\t\t  \"currently unsupported\";\n\t\t}\n\t}\n\n\t/* finally check and return deferred if not satisfied */\n\tif (!preconditionsSatisfied)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot push down this subquery\",\n\t\t\t\t\t\t\t errorDetail, NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * DeferErrorIfUnsupportedTableCombination checks if the given query tree contains any\n * unsupported range table combinations. For this, the function walks over all\n * range tables in the join tree, and checks if they correspond to simple relations\n * or subqueries. It also checks if there is a join between a regular table and\n * a subquery and if join is on more than two range table entries. If any error is found,\n * a deferred error is returned. Else, NULL is returned.\n */\nstatic DeferredErrorMessage *\nDeferErrorIfUnsupportedTableCombination(Query *queryTree)\n{\n\tList *rangeTableList = queryTree->rtable;\n\tList *joinTreeTableIndexList = NIL;\n\tint joinTreeTableIndex = 0;\n\tbool unsupportedTableCombination = false;\n\tchar *errorDetail = NULL;\n\n\t/*\n\t * Extract all range table indexes from the join tree. Note that sub-queries\n\t * that get pulled up by PostgreSQL don't appear in this join tree.\n\t */\n\tExtractRangeTableIndexWalker((Node *) queryTree->jointree,\n\t\t\t\t\t\t\t\t &joinTreeTableIndexList);\n\n\tforeach_declared_int(joinTreeTableIndex, joinTreeTableIndexList)\n\t{\n\t\t/*\n\t\t * Join tree's range table index starts from 1 in the query tree. But,\n\t\t * list indexes start from 0.\n\t\t */\n\t\tint rangeTableListIndex = joinTreeTableIndex - 1;\n\n\t\tRangeTblEntry *rangeTableEntry =\n\t\t\t(RangeTblEntry *) list_nth(rangeTableList, rangeTableListIndex);\n\n\t\t/*\n\t\t * Check if the range table in the join tree is a simple relation, a\n\t\t * subquery, or immutable function.\n\t\t */\n\t\tif (rangeTableEntry->rtekind == RTE_RELATION ||\n\t\t\trangeTableEntry->rtekind == RTE_SUBQUERY ||\n\t\t\trangeTableEntry->rtekind == RTE_RESULT ||\n\t\t\tIsJsonTableRTE(rangeTableEntry))\n\t\t{\n\t\t\t/* accepted */\n\t\t}\n\t\telse if (rangeTableEntry->rtekind == RTE_VALUES)\n\t\t{\n\t\t\t/*\n\t\t\t * When GUC is set to -1, we disable materialization, when set to 0,\n\t\t\t * we materialize everything. Other values are compared against the\n\t\t\t * length of the values_lists.\n\t\t\t */\n\t\t\tint valuesRowCount = list_length(rangeTableEntry->values_lists);\n\t\t\tif (ValuesMaterializationThreshold >= 0 &&\n\t\t\t\tvaluesRowCount > ValuesMaterializationThreshold)\n\t\t\t{\n\t\t\t\tunsupportedTableCombination = true;\n\t\t\t\terrorDetail = \"VALUES has more than \"\n\t\t\t\t\t\t\t  \"\\\"citus.values_materialization_threshold\\\" \"\n\t\t\t\t\t\t\t  \"entries, so it is materialized\";\n\t\t\t}\n\t\t\telse if (contain_mutable_functions((Node *) rangeTableEntry->values_lists))\n\t\t\t{\n\t\t\t\t/* VALUES should not contain mutable functions */\n\t\t\t\tunsupportedTableCombination = true;\n\t\t\t\terrorDetail = \"Only immutable functions can be used in VALUES\";\n\t\t\t}\n\t\t}\n\t\telse if (rangeTableEntry->rtekind == RTE_FUNCTION)\n\t\t{\n\t\t\tList *functionList = rangeTableEntry->functions;\n\n\t\t\tif (list_length(functionList) == 1 &&\n\t\t\t\tContainsReadIntermediateResultFunction(linitial(functionList)))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * The read_intermediate_result function is volatile, but we know\n\t\t\t\t * it has the same result across all nodes and can therefore treat\n\t\t\t\t * it as a reference table.\n\t\t\t\t */\n\t\t\t}\n\t\t\telse if (contain_mutable_functions((Node *) functionList))\n\t\t\t{\n\t\t\t\tunsupportedTableCombination = true;\n\t\t\t\terrorDetail = \"Only immutable functions can be used as a table \"\n\t\t\t\t\t\t\t  \"expressions in a multi-shard query\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* immutable function RTEs are treated as reference tables */\n\t\t\t}\n\t\t}\n\t\telse if (rangeTableEntry->rtekind == RTE_CTE)\n\t\t{\n\t\t\tunsupportedTableCombination = true;\n\t\t\terrorDetail = \"CTEs in subqueries are currently unsupported\";\n\t\t\tbreak;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tunsupportedTableCombination = true;\n\t\t\terrorDetail = \"Table expressions other than relations, subqueries, \"\n\t\t\t\t\t\t  \"and immutable functions are currently unsupported\";\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* finally check and error out if not satisfied */\n\tif (unsupportedTableCombination)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot push down this subquery\",\n\t\t\t\t\t\t\t errorDetail, NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * DeferErrorIfUnsupportedUnionQuery is a helper function for ErrorIfCannotPushdownSubquery().\n * The function also errors out for set operations INTERSECT and EXCEPT.\n */\nDeferredErrorMessage *\nDeferErrorIfUnsupportedUnionQuery(Query *subqueryTree)\n{\n\tList *setOperationStatementList = NIL;\n\tListCell *setOperationStatmentCell = NULL;\n\tRecurringTuplesType recurType = RECURRING_TUPLES_INVALID;\n\n\tExtractSetOperationStatementWalker((Node *) subqueryTree->setOperations,\n\t\t\t\t\t\t\t\t\t   &setOperationStatementList);\n\tforeach(setOperationStatmentCell, setOperationStatementList)\n\t{\n\t\tSetOperationStmt *setOperation =\n\t\t\t(SetOperationStmt *) lfirst(setOperationStatmentCell);\n\t\tNode *leftArg = setOperation->larg;\n\t\tNode *rightArg = setOperation->rarg;\n\t\tint leftArgRTI = 0;\n\t\tint rightArgRTI = 0;\n\n\t\tif (setOperation->op != SETOP_UNION)\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"cannot push down this subquery\",\n\t\t\t\t\t\t\t\t \"Intersect and Except are currently unsupported\",\n\t\t\t\t\t\t\t\t NULL);\n\t\t}\n\n\t\tif (IsA(leftArg, RangeTblRef))\n\t\t{\n\t\t\tleftArgRTI = ((RangeTblRef *) leftArg)->rtindex;\n\t\t\tQuery *leftArgSubquery = rt_fetch(leftArgRTI,\n\t\t\t\t\t\t\t\t\t\t\t  subqueryTree->rtable)->subquery;\n\t\t\trecurType = FromClauseRecurringTupleType(leftArgSubquery);\n\t\t\tif (recurType != RECURRING_TUPLES_INVALID)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (IsA(rightArg, RangeTblRef))\n\t\t{\n\t\t\trightArgRTI = ((RangeTblRef *) rightArg)->rtindex;\n\t\t\tQuery *rightArgSubquery = rt_fetch(rightArgRTI,\n\t\t\t\t\t\t\t\t\t\t\t   subqueryTree->rtable)->subquery;\n\t\t\trecurType = FromClauseRecurringTupleType(rightArgSubquery);\n\t\t\tif (recurType != RECURRING_TUPLES_INVALID)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (recurType == RECURRING_TUPLES_REFERENCE_TABLE)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot push down this subquery\",\n\t\t\t\t\t\t\t \"Reference tables are not supported with union operator\",\n\t\t\t\t\t\t\t NULL);\n\t}\n\telse if (recurType == RECURRING_TUPLES_FUNCTION)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot push down this subquery\",\n\t\t\t\t\t\t\t \"Table functions are not supported with union operator\",\n\t\t\t\t\t\t\t NULL);\n\t}\n\telse if (recurType == RECURRING_TUPLES_EMPTY_JOIN_TREE)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot push down this subquery\",\n\t\t\t\t\t\t\t \"Subqueries without a FROM clause are not supported with \"\n\t\t\t\t\t\t\t \"union operator\", NULL);\n\t}\n\telse if (recurType == RECURRING_TUPLES_RESULT_FUNCTION)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot push down this subquery\",\n\t\t\t\t\t\t\t \"Complex subqueries and CTEs are not supported within a \"\n\t\t\t\t\t\t\t \"UNION\", NULL);\n\t}\n\telse if (recurType == RECURRING_TUPLES_VALUES)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot push down this subquery\",\n\t\t\t\t\t\t\t \"VALUES is not supported within a \"\n\t\t\t\t\t\t\t \"UNION\", NULL);\n\t}\n\telse if (recurType == RECURRING_TUPLES_JSON_TABLE)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"cannot push down this subquery\",\n\t\t\t\t\t\t\t \"JSON_TABLE is not supported within a \"\n\t\t\t\t\t\t\t \"UNION\", NULL);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * ExtractSetOperationStatementWalker walks over a set operations statment,\n * and finds all set operations in the tree.\n */\nstatic bool\nExtractSetOperationStatementWalker(Node *node, List **setOperationList)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, SetOperationStmt))\n\t{\n\t\tSetOperationStmt *setOperation = (SetOperationStmt *) node;\n\n\t\t(*setOperationList) = lappend(*setOperationList, setOperation);\n\t}\n\n\tbool walkerResult = expression_tree_walker(node,\n\t\t\t\t\t\t\t\t\t\t\t   ExtractSetOperationStatementWalker,\n\t\t\t\t\t\t\t\t\t\t\t   setOperationList);\n\n\treturn walkerResult;\n}\n\n\n/*\n * RelationInfoContainsOnlyRecurringTuples returns false if any of the relations in\n * a RelOptInfo is not recurring.\n */\nstatic bool\nRelationInfoContainsOnlyRecurringTuples(PlannerInfo *plannerInfo, Relids relids)\n{\n\tint relationId = -1;\n\n\twhile ((relationId = bms_next_member(relids, relationId)) >= 0)\n\t{\n\t\t/* outer join RTE check in PG16 */\n\t\tif (IsRelOptOuterJoin(plannerInfo, relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tRangeTblEntry *rangeTableEntry = plannerInfo->simple_rte_array[relationId];\n\n\t\tif (FindNodeMatchingCheckFunctionInRangeTableList(list_make1(rangeTableEntry),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  IsDistributedTableRTE))\n\t\t{\n\t\t\t/* we already found a distributed table, no need to check further */\n\t\t\treturn false;\n\t\t}\n\n\t\t/*\n\t\t * If there are no distributed tables, there should be at least\n\t\t * one recurring rte.\n\t\t */\n\t\tRecurringTuplesType recurType PG_USED_FOR_ASSERTS_ONLY;\n\t\tAssert(ContainsRecurringRTE(rangeTableEntry, &recurType));\n\t}\n\n\treturn true;\n}\n\n\n/*\n * RecurringTypeDescription returns a discriptive string for the given\n * recurType. This string can be used in error messages to help the users\n * understand why a query cannot be planned.\n */\nstatic char *\nRecurringTypeDescription(RecurringTuplesType recurType)\n{\n\tswitch (recurType)\n\t{\n\t\tcase RECURRING_TUPLES_REFERENCE_TABLE:\n\t\t{\n\t\t\treturn \"a reference table\";\n\t\t}\n\n\t\tcase RECURRING_TUPLES_FUNCTION:\n\t\t{\n\t\t\treturn \"a table function\";\n\t\t}\n\n\t\tcase RECURRING_TUPLES_EMPTY_JOIN_TREE:\n\t\t{\n\t\t\treturn \"a subquery without FROM\";\n\t\t}\n\n\t\tcase RECURRING_TUPLES_RESULT_FUNCTION:\n\t\t{\n\t\t\treturn \"complex subqueries, CTEs or local tables\";\n\t\t}\n\n\t\tcase RECURRING_TUPLES_VALUES:\n\t\t{\n\t\t\treturn \"a VALUES clause\";\n\t\t}\n\n\t\tcase RECURRING_TUPLES_JSON_TABLE:\n\t\t{\n\t\t\treturn \"a JSON_TABLE\";\n\t\t}\n\n\t\tcase RECURRING_TUPLES_INVALID:\n\t\t{\n\t\t\t/*\n\t\t\t * This branch should never be hit, but it's here just in case it\n\t\t\t * happens.\n\t\t\t */\n\t\t\treturn \"an unknown recurring tuple\";\n\t\t}\n\t}\n\n\t/*\n\t * This should never be hit, but is needed to fix compiler warnings.\n\t */\n\treturn \"an unknown recurring tuple\";\n}\n\n\n/*\n * ContainsReferencesToRelids determines whether the given query contains\n * any references that point to columns of the given relids. The given relids\n * should be from exactly one query level above the given query.\n *\n * If the function returns true, then foundRelid is set to the first relid that\n * was referenced.\n *\n * There are some queries where it cannot easily be determined if the relids\n * are used, e.g because the query contains placeholder vars. In those cases\n * this function returns true, because it's better to error out than to return\n * wrong results. But in these cases foundRelid is set to INVALID_RELID.\n */\nstatic bool\nContainsReferencesToRelids(Query *query, Relids relids, int *foundRelid)\n{\n\tRelidsReferenceWalkerContext context = { 0 };\n\tcontext.level = 1;\n\tcontext.relids = relids;\n\tcontext.foundRelid = INVALID_RELID;\n\tint flags = 0;\n\n\tif (query_tree_walker(query, ContainsReferencesToRelidsWalker,\n\t\t\t\t\t\t  &context, flags))\n\t{\n\t\t*foundRelid = context.foundRelid;\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n\n/*\n * ContainsReferencesToRelidsWalker determines whether the given query\n * contains any Vars that reference the relids in the context.\n *\n * ContainsReferencesToRelidsWalker recursively descends into subqueries\n * and increases the level by 1 before recursing.\n */\nstatic bool\nContainsReferencesToRelidsWalker(Node *node, RelidsReferenceWalkerContext *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Var))\n\t{\n\t\tVar *var = (Var *) node;\n\t\tif (var->varlevelsup == context->level && bms_is_member(var->varno,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontext->relids))\n\t\t{\n\t\t\tcontext->foundRelid = var->varno;\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\telse if (IsA(node, Aggref))\n\t{\n\t\tif (((Aggref *) node)->agglevelsup > context->level)\n\t\t{\n\t\t\t/*\n\t\t\t * TODO: Only return true when aggref points to an aggregate that\n\t\t\t * uses vars from a recurring tuple.\n\t\t\t */\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if (IsA(node, GroupingFunc))\n\t{\n\t\tif (((GroupingFunc *) node)->agglevelsup > context->level)\n\t\t{\n\t\t\t/*\n\t\t\t * TODO: Only return true when groupingfunc points to a grouping\n\t\t\t * func that uses vars from a recurring tuple.\n\t\t\t */\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\telse if (IsA(node, PlaceHolderVar))\n\t{\n\t\tif (((PlaceHolderVar *) node)->phlevelsup > context->level)\n\t\t{\n\t\t\t/*\n\t\t\t * TODO: Only return true when aggref points to a placeholdervar\n\t\t\t * that uses vars from a recurring tuple.\n\t\t\t */\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\t\tint flags = 0;\n\n\t\tcontext->level += 1;\n\t\tbool found = query_tree_walker(query, ContainsReferencesToRelidsWalker,\n\t\t\t\t\t\t\t\t\t   context, flags);\n\t\tcontext->level -= 1;\n\n\t\treturn found;\n\t}\n\n\treturn expression_tree_walker(node, ContainsReferencesToRelidsWalker,\n\t\t\t\t\t\t\t\t  context);\n}\n\n\n/*\n * DeferredErrorIfUnsupportedLateralSubquery returns true if\n * notFullyRecurringRelids contains a lateral subquery that we do not support.\n *\n * If there is an inner join with a lateral subquery we cannot\n * push it down when the following properties all hold:\n * 1. The lateral subquery contains some non recurring tuples\n * 2. The lateral subquery references a recurring tuple from\n *    outside of the subquery (recurringRelids)\n * 3. The lateral subquery requires a merge step (e.g. a LIMIT)\n * 4. The reference to the recurring tuple should be something else than an\n *    equality check on the distribution column, e.g. equality on a non\n *    distribution column.\n *\n * Property number four is considered both hard to detect and\n * probably not used very often, so we only check for 1, 2 and 3.\n */\nstatic DeferredErrorMessage *\nDeferredErrorIfUnsupportedLateralSubquery(PlannerInfo *plannerInfo,\n\t\t\t\t\t\t\t\t\t\t  Relids recurringRelids,\n\t\t\t\t\t\t\t\t\t\t  Relids notFullyRecurringRelids)\n{\n\tint relationId = -1;\n\twhile ((relationId = bms_next_member(notFullyRecurringRelids, relationId)) >= 0)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = plannerInfo->simple_rte_array[relationId];\n\n\t\tif (!rangeTableEntry->lateral)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* TODO: What about others kinds? */\n\t\tif (rangeTableEntry->rtekind == RTE_SUBQUERY)\n\t\t{\n\t\t\t/* property number 1, contains non-recurring tuples */\n\t\t\tif (!FindNodeMatchingCheckFunctionInRangeTableList(\n\t\t\t\t\tlist_make1(rangeTableEntry), IsDistributedTableRTE))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* property number 2, references recurring tuple */\n\t\t\tint recurringRelid = INVALID_RELID;\n\t\t\tif (!ContainsReferencesToRelids(rangeTableEntry->subquery, recurringRelids,\n\t\t\t\t\t\t\t\t\t\t\t&recurringRelid))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tchar *recurTypeDescription =\n\t\t\t\t\"an aggregate, grouping func or placeholder var coming from the outer query\";\n\t\t\tif (recurringRelid != INVALID_RELID)\n\t\t\t{\n\t\t\t\tRangeTblEntry *recurringRangeTableEntry =\n\t\t\t\t\tplannerInfo->simple_rte_array[recurringRelid];\n\t\t\t\tRecurringTuplesType recurType = RECURRING_TUPLES_INVALID;\n\t\t\t\tContainsRecurringRTE(recurringRangeTableEntry, &recurType);\n\t\t\t\trecurTypeDescription = RecurringTypeDescription(recurType);\n\n\t\t\t\t/*\n\t\t\t\t * Add the alias for all recuring tuples where it is useful to\n\t\t\t\t * see them. We don't add it for VALUES and intermediate\n\t\t\t\t * results, because there the aliases are currently hardcoded\n\t\t\t\t * strings anyway.\n\t\t\t\t */\n\t\t\t\tif (recurType != RECURRING_TUPLES_VALUES &&\n\t\t\t\t\trecurType != RECURRING_TUPLES_RESULT_FUNCTION &&\n\t\t\t\t\trecurType != RECURRING_TUPLES_JSON_TABLE)\n\t\t\t\t{\n\t\t\t\t\trecurTypeDescription = psprintf(\"%s (%s)\", recurTypeDescription,\n\t\t\t\t\t\t\t\t\t\t\t\t\trecurringRangeTableEntry->eref->\n\t\t\t\t\t\t\t\t\t\t\t\t\taliasname);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* property number 3, has a merge step */\n\t\t\tDeferredErrorMessage *deferredError = DeferErrorIfSubqueryRequiresMerge(\n\t\t\t\trangeTableEntry->subquery, true, recurTypeDescription);\n\t\t\tif (deferredError)\n\t\t\t{\n\t\t\t\treturn deferredError;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * ContainsLateralSubquery checks if the given plannerInfo contains any\n * lateral subqueries in its rtable. If it does, it returns true, otherwise false.\n */\nstatic bool\nContainsLateralSubquery(PlannerInfo *plannerInfo)\n{\n\tListCell *lc;\n\n\tforeach(lc, plannerInfo->parse->rtable)\n\t{\n\t\tRangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);\n\n\t\t/* We are only interested in subqueries that are lateral */\n\t\tif (rte->lateral && rte->rtekind == RTE_SUBQUERY)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * FetchFirstRecurType checks whether the relationInfo\n * contains any recurring table expression, namely a reference table,\n * or immutable function. If found, FetchFirstRecurType\n * returns true.\n *\n * Note that since relation ids of relationInfo indexes to the range\n * table entry list of planner info, planner info is also passed.\n */\nstatic RecurringTuplesType\nFetchFirstRecurType(PlannerInfo *plannerInfo, Relids relids)\n{\n\tRecurringTuplesType recurType = RECURRING_TUPLES_INVALID;\n\tint relationId = -1;\n\n\twhile ((relationId = bms_next_member(relids, relationId)) >= 0)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = plannerInfo->simple_rte_array[relationId];\n\n\t\t/* relationInfo has this range table entry */\n\t\tif (ContainsRecurringRTE(rangeTableEntry, &recurType))\n\t\t{\n\t\t\treturn recurType;\n\t\t}\n\t}\n\n\treturn recurType;\n}\n\n\n/*\n * ContainsRecurringRTE returns whether the range table entry contains\n * any entry that generates the same set of tuples when repeating it in\n * a query on different shards.\n */\nstatic bool\nContainsRecurringRTE(RangeTblEntry *rangeTableEntry, RecurringTuplesType *recurType)\n{\n\treturn ContainsRecurringRangeTable(list_make1(rangeTableEntry), recurType);\n}\n\n\n/*\n * ContainsRecurringRangeTable returns whether the range table list contains\n * any entry that generates the same set of tuples when repeating it in\n * a query on different shards.\n */\nstatic bool\nContainsRecurringRangeTable(List *rangeTable, RecurringTuplesType *recurType)\n{\n\treturn range_table_walker(rangeTable, HasRecurringTuples, recurType,\n\t\t\t\t\t\t\t  QTW_EXAMINE_RTES_BEFORE);\n}\n\n\n/*\n * IsJsonTableRTE checks whether the RTE refers to a JSON_TABLE\n * table function, which was introduced in PostgreSQL 17.\n */\nbool\nIsJsonTableRTE(RangeTblEntry *rte)\n{\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tif (rte == NULL)\n\t{\n\t\treturn false;\n\t}\n\treturn (rte->rtekind == RTE_TABLEFUNC &&\n\t\t\trte->tablefunc->functype == TFT_JSON_TABLE);\n#endif\n\n\treturn false;\n}\n\n\n/*\n * HasRecurringTuples returns whether any part of the expression will generate\n * the same set of tuples in every query on shards when executing a distributed\n * query.\n */\nstatic bool\nHasRecurringTuples(Node *node, RecurringTuplesType *recurType)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, RangeTblEntry))\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) node;\n\n\t\tif (rangeTableEntry->rtekind == RTE_RELATION)\n\t\t{\n\t\t\tOid relationId = rangeTableEntry->relid;\n\t\t\tif (IsCitusTableType(relationId, REFERENCE_TABLE))\n\t\t\t{\n\t\t\t\t*recurType = RECURRING_TUPLES_REFERENCE_TABLE;\n\n\t\t\t\t/*\n\t\t\t\t * Tuples from reference tables will recur in every query on shards\n\t\t\t\t * that includes it.\n\t\t\t\t */\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\telse if (rangeTableEntry->rtekind == RTE_FUNCTION)\n\t\t{\n\t\t\tList *functionList = rangeTableEntry->functions;\n\n\t\t\tif (list_length(functionList) == 1 &&\n\t\t\t\tContainsReadIntermediateResultFunction((Node *) functionList))\n\t\t\t{\n\t\t\t\t*recurType = RECURRING_TUPLES_RESULT_FUNCTION;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t*recurType = RECURRING_TUPLES_FUNCTION;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Tuples from functions will recur in every query on shards that includes\n\t\t\t * it.\n\t\t\t */\n\t\t\treturn true;\n\t\t}\n\t\telse if (rangeTableEntry->rtekind == RTE_RESULT)\n\t\t{\n\t\t\t*recurType = RECURRING_TUPLES_EMPTY_JOIN_TREE;\n\t\t\treturn true;\n\t\t}\n\t\telse if (rangeTableEntry->rtekind == RTE_VALUES)\n\t\t{\n\t\t\t*recurType = RECURRING_TUPLES_VALUES;\n\t\t\treturn true;\n\t\t}\n\t\telse if (IsJsonTableRTE(rangeTableEntry))\n\t\t{\n\t\t\t*recurType = RECURRING_TUPLES_JSON_TABLE;\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\telse if (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\n\t\tif (HasEmptyJoinTree(query))\n\t\t{\n\t\t\t*recurType = RECURRING_TUPLES_EMPTY_JOIN_TREE;\n\n\t\t\t/*\n\t\t\t * Queries with empty join trees will recur in every query on shards\n\t\t\t * that includes it.\n\t\t\t */\n\t\t\treturn true;\n\t\t}\n\n\t\treturn query_tree_walker((Query *) node, HasRecurringTuples,\n\t\t\t\t\t\t\t\t recurType, QTW_EXAMINE_RTES_BEFORE);\n\t}\n\n\treturn expression_tree_walker(node, HasRecurringTuples, recurType);\n}\n\n\n/*\n * SubqueryPushdownMultiNodeTree creates logical plan for subquery pushdown logic.\n * Note that this logic will be changed in next iterations, so we decoupled it\n * from other parts of code although it causes some code duplication.\n *\n * Current subquery pushdown support in MultiTree logic requires a single range\n * table entry in the top most from clause. Therefore we inject a synthetic\n * query derived from the top level query and make it the only range table\n * entry for the top level query. This way we can push down any subquery joins\n * down to workers without invoking join order planner.\n */\nstatic MultiNode *\nSubqueryPushdownMultiNodeTree(Query *originalQuery)\n{\n\tQuery *queryTree = copyObject(originalQuery);\n\n\t/*\n\t * PG18+ need to flatten GROUP BY expressions to ensure correct processing\n\t * later on, such as identification of partition columns in GROUP BY.\n\t */\n\tFlattenGroupExprs(queryTree);\n\n\tList *targetEntryList = queryTree->targetList;\n\tMultiCollect *subqueryCollectNode = CitusMakeNode(MultiCollect);\n\n\t/* verify we can perform distributed planning on this query */\n\tDeferredErrorMessage *unsupportedQueryError = DeferErrorIfQueryNotSupported(\n\t\tqueryTree);\n\tif (unsupportedQueryError != NULL)\n\t{\n\t\tRaiseDeferredError(unsupportedQueryError, ERROR);\n\t}\n\n\t/*\n\t * We would be creating a new Query and pushing down top level query's\n\t * contents down to it. Join and filter clauses in higher level query would\n\t * be transferred to lower query. Therefore after this function we would\n\t * only have a single range table entry in the top level query. We need to\n\t * create a target list entry in lower query for each column reference in\n\t * upper level query's target list and having clauses. Any column reference\n\t * in the upper query will be updated to have varno=1, and varattno=<resno>\n\t * of matching target entry in pushed down query.\n\t * Consider query\n\t *      SELECT s1.a, sum(s2.c)\n\t *      FROM (some subquery) s1, (some subquery) s2\n\t *      WHERE s1.a = s2.a\n\t *      GROUP BY s1.a\n\t *      HAVING avg(s2.b);\n\t *\n\t * We want to prepare a multi tree to avoid subquery joins at top level,\n\t * therefore above query is converted to an equivalent\n\t *      SELECT worker_column_0, sum(worker_column_1)\n\t *      FROM (\n\t *              SELECT\n\t *                  s1.a AS worker_column_0,\n\t *                  s2.c AS worker_column_1,\n\t *                  s2.b AS worker_column_2\n\t *              FROM (some subquery) s1, (some subquery) s2\n\t *              WHERE s1.a = s2.a) worker_subquery\n\t *      GROUP BY worker_column_0\n\t *      HAVING avg(worker_column_2);\n\t *  After this conversion MultiTree is created as follows\n\t *\n\t *  MultiExtendedOpNode(\n\t *      targetList : worker_column_0, sum(worker_column_1)\n\t *      groupBy : worker_column_0\n\t *      having :  avg(worker_column_2))\n\t * --->MultiProject (worker_column_0, worker_column_1, worker_column_2)\n\t * --->--->\tMultiTable (subquery : worker_subquery)\n\t *\n\t * Master and worker queries will be created out of this MultiTree at later stages.\n\t */\n\n\t/*\n\t * columnList contains all columns returned by subquery. Subquery target\n\t * entry list, subquery range table entry's column name list are derived from\n\t * columnList. Columns mentioned in multiProject node and multiExtendedOp\n\t * node are indexed with their respective position in columnList.\n\t */\n\tList *targetColumnList = pull_vars_of_level((Node *) targetEntryList, 0);\n\tList *havingClauseColumnList = pull_var_clause_default(queryTree->havingQual);\n\tList *columnList = list_concat(targetColumnList, havingClauseColumnList);\n\n\t/* create a target entry for each unique column */\n\tList *subqueryTargetEntryList = CreateSubqueryTargetListAndAdjustVars(columnList);\n\n\t/* new query only has target entries, join tree, and rtable*/\n\tQuery *pushedDownQuery = makeNode(Query);\n\tpushedDownQuery->commandType = queryTree->commandType;\n\tpushedDownQuery->targetList = subqueryTargetEntryList;\n\tpushedDownQuery->jointree = copyObject(queryTree->jointree);\n\tpushedDownQuery->rtable = copyObject(queryTree->rtable);\n\tpushedDownQuery->rteperminfos = copyObject(queryTree->rteperminfos);\n\tpushedDownQuery->setOperations = copyObject(queryTree->setOperations);\n\tpushedDownQuery->querySource = queryTree->querySource;\n\tpushedDownQuery->hasSubLinks = queryTree->hasSubLinks;\n#if PG_VERSION_NUM >= PG_VERSION_18\n\tpushedDownQuery->hasGroupRTE = queryTree->hasGroupRTE;\n#endif\n\tMultiTable *subqueryNode = MultiSubqueryPushdownTable(pushedDownQuery);\n\n\tSetChild((MultiUnaryNode *) subqueryCollectNode, (MultiNode *) subqueryNode);\n\tMultiNode *currentTopNode = (MultiNode *) subqueryCollectNode;\n\n\t/* build project node for the columns to project */\n\tMultiProject *projectNode = MultiProjectNode(targetEntryList);\n\tSetChild((MultiUnaryNode *) projectNode, currentTopNode);\n\tcurrentTopNode = (MultiNode *) projectNode;\n\n\t/*\n\t * We build the extended operator node to capture aggregate functions, group\n\t * clauses, sort clauses, limit/offset clauses, and expressions. We need to\n\t * distinguish between aggregates and expressions; and we address this later\n\t * in the logical optimizer.\n\t */\n\tMultiExtendedOp *extendedOpNode = MultiExtendedOpNode(queryTree, originalQuery);\n\n\t/*\n\t * Postgres standard planner converts having qual node to a list of and\n\t * clauses and expects havingQual to be of type List when executing the\n\t * query later. This function is called on an original query, therefore\n\t * havingQual has not been converted yet. Perform conversion here.\n\t */\n\tif (extendedOpNode->havingQual != NULL &&\n\t\t!IsA(extendedOpNode->havingQual, List))\n\t{\n\t\textendedOpNode->havingQual =\n\t\t\t(Node *) make_ands_implicit((Expr *) extendedOpNode->havingQual);\n\t}\n\n\t/*\n\t * Group by on primary key allows all columns to appear in the target\n\t * list, but once we wrap the join tree into a subquery the GROUP BY\n\t * will no longer directly refer to the primary key and referencing\n\t * columns that are not in the GROUP BY would result in an error. To\n\t * prevent that we wrap all the columns that do not appear in the\n\t * GROUP BY in an any_value aggregate.\n\t */\n\tif (extendedOpNode->groupClauseList != NIL)\n\t{\n\t\textendedOpNode->targetList = (List *) WrapUngroupedVarsInAnyValueAggregate(\n\t\t\t(Node *) extendedOpNode->targetList,\n\t\t\textendedOpNode->groupClauseList,\n\t\t\textendedOpNode->targetList, true);\n\n\t\textendedOpNode->havingQual = WrapUngroupedVarsInAnyValueAggregate(\n\t\t\t(Node *) extendedOpNode->havingQual,\n\t\t\textendedOpNode->groupClauseList,\n\t\t\textendedOpNode->targetList, false);\n\t}\n\n\t/*\n\t * Postgres standard planner evaluates expressions in the LIMIT/OFFSET clauses.\n\t * Since we're using original query here, we should manually evaluate the\n\t * expression on the LIMIT and OFFSET clauses. Note that logical optimizer\n\t * expects those clauses to be already evaluated.\n\t */\n\textendedOpNode->limitCount =\n\t\tPartiallyEvaluateExpression(extendedOpNode->limitCount, NULL);\n\textendedOpNode->limitOffset =\n\t\tPartiallyEvaluateExpression(extendedOpNode->limitOffset, NULL);\n\n\tSetChild((MultiUnaryNode *) extendedOpNode, currentTopNode);\n\tcurrentTopNode = (MultiNode *) extendedOpNode;\n\n\treturn currentTopNode;\n}\n\n\n/*\n * CreateSubqueryTargetListAndAdjustVars creates a target entry for each unique\n * column in the column list, adjusts the columns to point into the subquery target\n * list and returns the new subquery target list.\n */\nstatic List *\nCreateSubqueryTargetListAndAdjustVars(List *columnList)\n{\n\tVar *column = NULL;\n\tList *subqueryTargetEntryList = NIL;\n\n\tforeach_declared_ptr(column, columnList)\n\t{\n\t\t/*\n\t\t * To avoid adding the same column multiple times, we first check whether there\n\t\t * is already a target entry containing a Var with the given varno and varattno.\n\t\t */\n\t\tAttrNumber resNo = FindResnoForVarInTargetList(subqueryTargetEntryList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   column->varno, column->varattno);\n\t\tif (resNo == InvalidAttrNumber)\n\t\t{\n\t\t\t/* Var is not yet on the target list, create a new entry */\n\t\t\tresNo = list_length(subqueryTargetEntryList) + 1;\n\n\t\t\t/*\n\t\t\t * The join tree in the subquery is an exact duplicate of the original\n\t\t\t * query. Hence, we can make a copy of the original Var. However, if the\n\t\t\t * original Var was in a sublink it would be pointing up whereas now it\n\t\t\t * will be placed directly on the target list. Hence we reset the\n\t\t\t * varlevelsup.\n\t\t\t */\n\t\t\tVar *subqueryTargetListVar = (Var *) copyObject(column);\n\n\t\t\tsubqueryTargetListVar->varlevelsup = 0;\n\n\t\t\tTargetEntry *newTargetEntry = makeNode(TargetEntry);\n\t\t\tnewTargetEntry->expr = (Expr *) subqueryTargetListVar;\n\t\t\tnewTargetEntry->resname = WorkerColumnName(resNo);\n\t\t\tnewTargetEntry->resjunk = false;\n\t\t\tnewTargetEntry->resno = resNo;\n\n\t\t\tsubqueryTargetEntryList = lappend(subqueryTargetEntryList, newTargetEntry);\n\t\t}\n\n\t\t/*\n\t\t * Change the original column reference to point to the target list\n\t\t * entry in the subquery. There is only 1 subquery, so the varno is 1.\n\t\t */\n\t\tcolumn->varno = 1;\n\t\tcolumn->varattno = resNo;\n\n\t\t/*\n\t\t * 1 subquery means there is one range table entry so with Postgres 16+ we need\n\t\t * to ensure that column's varnullingrels - the set of join rels that can null\n\t\t * the var - is empty. Otherwise, when given the query, the Postgres planner\n\t\t * may attempt to access a non-existent range table and segfault, as in #7787.\n\t\t */\n\t\tcolumn->varnullingrels = NULL;\n\t}\n\n\treturn subqueryTargetEntryList;\n}\n\n\n/*\n * FindResnoForVarInTargetList finds a Var on a target list that has the given varno\n * (range table entry number) and varattno (column number) and returns the resno\n * of the target list entry.\n */\nstatic AttrNumber\nFindResnoForVarInTargetList(List *targetList, int varno, int varattno)\n{\n\tTargetEntry *targetEntry = NULL;\n\tforeach_declared_ptr(targetEntry, targetList)\n\t{\n\t\tif (!IsA(targetEntry->expr, Var))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tVar *targetEntryVar = (Var *) targetEntry->expr;\n\n\t\tif (targetEntryVar->varno == varno && targetEntryVar->varattno == varattno)\n\t\t{\n\t\t\treturn targetEntry->resno;\n\t\t}\n\t}\n\n\treturn InvalidAttrNumber;\n}\n\n\n/*\n * MultiSubqueryPushdownTable creates a MultiTable from the given subquery,\n * populates column list and returns the multitable.\n */\nstatic MultiTable *\nMultiSubqueryPushdownTable(Query *subquery)\n{\n\tStringInfo rteName = makeStringInfo();\n\tList *columnNamesList = NIL;\n\tListCell *targetEntryCell = NULL;\n\n\tappendStringInfo(rteName, \"worker_subquery\");\n\n\tforeach(targetEntryCell, subquery->targetList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell);\n\t\tcolumnNamesList = lappend(columnNamesList, makeString(targetEntry->resname));\n\t}\n\n\tMultiTable *subqueryTableNode = CitusMakeNode(MultiTable);\n\tsubqueryTableNode->subquery = subquery;\n\tsubqueryTableNode->relationId = SUBQUERY_PUSHDOWN_RELATION_ID;\n\tsubqueryTableNode->rangeTableId = SUBQUERY_RANGE_TABLE_ID;\n\tsubqueryTableNode->partitionColumn = PartitionColumnForPushedDownSubquery(subquery);\n\tsubqueryTableNode->alias = makeNode(Alias);\n\tsubqueryTableNode->alias->aliasname = rteName->data;\n\tsubqueryTableNode->referenceNames = makeNode(Alias);\n\tsubqueryTableNode->referenceNames->aliasname = rteName->data;\n\tsubqueryTableNode->referenceNames->colnames = columnNamesList;\n\n\treturn subqueryTableNode;\n}\n\n\n/*\n * PartitionColumnForPushedDownSubquery finds the partition column on the target\n * list of a pushed down subquery.\n */\nstatic Var *\nPartitionColumnForPushedDownSubquery(Query *query)\n{\n\tList *targetEntryList = query->targetList;\n\n\tTargetEntry *targetEntry = NULL;\n\tforeach_declared_ptr(targetEntry, targetEntryList)\n\t{\n\t\tif (targetEntry->resjunk)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tExpr *targetExpression = targetEntry->expr;\n\t\tif (IsA(targetExpression, Var))\n\t\t{\n\t\t\tbool skipOuterVars = true;\n\t\t\tbool isPartitionColumn = IsPartitionColumn(targetExpression, query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   skipOuterVars);\n\t\t\tif (isPartitionColumn)\n\t\t\t{\n\t\t\t\tVar *partitionColumn = copyObject((Var *) targetExpression);\n\n\t\t\t\t/* the pushed down subquery is the only range table entry */\n\t\t\t\tpartitionColumn->varno = 1;\n\n\t\t\t\t/* point the var to the position in the subquery target list */\n\t\t\t\tpartitionColumn->varattno = targetEntry->resno;\n\n\t\t\t\treturn partitionColumn;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn NULL;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/recursive_planning.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * recursive_planning.c\n *\n * Logic for calling the postgres planner recursively for CTEs and\n * non-pushdownable subqueries in distributed queries.\n *\n * PostgreSQL with Citus can execute 4 types of queries:\n *\n * - Postgres queries on local tables and functions.\n *\n *   These queries can use all SQL features, but they may not reference\n *   distributed tables.\n *\n * - Router queries that can be executed on a single by node by replacing\n *   table names with shard names.\n *\n *   These queries can use nearly all SQL features, but only if they have\n *   a single-valued filter on the distribution column.\n *\n * - Multi-shard queries that can be executed by performing a task for each\n *   shard in a distributed table and performing a merge step.\n *\n *   These queries have limited SQL support. They may only include\n *   subqueries if the subquery can be executed on each shard by replacing\n *   table names with shard names and concatenating the result.\n *\n *   These queries have very limited SQL support and only support basic\n *   inner joins and subqueries without joins.\n *\n * To work around the limitations of these planners, we recursively call\n * the planner for CTEs and unsupported subqueries to obtain a list of\n * subplans.\n *\n * During execution, each subplan is executed separately through the method\n * that is appropriate for that query. The results are written to temporary\n * files on the workers. In the original query, the CTEs and subqueries are\n * replaced by mini-subqueries that read from the temporary files.\n *\n * This allows almost all SQL to be directly or indirectly supported,\n * because if all subqueries that contain distributed tables have been\n * replaced then what remains is a router query which can use nearly all\n * SQL features.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_type.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/pathnodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/planner.h\"\n#include \"optimizer/prep.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/parsetree.h\"\n#include \"utils/builtins.h\"\n#include \"utils/guc.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/combine_query_planner.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/distribution_column.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_distributed_join_planner.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/query_colocation_checker.h\"\n#include \"distributed/query_pushdown_planning.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/relation_restriction_equivalence.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/version_compat.h\"\n\nbool EnableRecurringOuterJoinPushdown = true;\nbool EnableOuterJoinsWithPseudoconstantQualsPrePG17 = false;\n\n/*\n * RecursivePlanningContext is used to recursively plan subqueries\n * and CTEs, pull results to the coordinator, and push it back into\n * the workers.\n */\nstruct RecursivePlanningContextInternal\n{\n\tint level;\n\tuint64 planId;\n\tbool allDistributionKeysInQueryAreEqual; /* used for some optimizations */\n\tList *subPlanList;\n\tPlannerRestrictionContext *plannerRestrictionContext;\n\tbool restrictionEquivalenceCheck;\n\tbool forceRecursivelyPlanRecurringOuterJoins;\n};\n\n/* track depth of current recursive planner query */\nstatic int recursivePlanningDepth = 0;\n\n/*\n * CteReferenceWalkerContext is used to collect CTE references in\n * CteReferenceListWalker.\n */\ntypedef struct CteReferenceWalkerContext\n{\n\tint level;\n\tList *cteReferenceList;\n} CteReferenceWalkerContext;\n\n/*\n * VarLevelsUpWalkerContext is used to find Vars in a (sub)query that\n * refer to upper levels and therefore cannot be planned separately.\n */\ntypedef struct VarLevelsUpWalkerContext\n{\n\tint level;\n} VarLevelsUpWalkerContext;\n\n\n/* local function forward declarations */\nstatic DeferredErrorMessage * RecursivelyPlanSubqueriesAndCTEs(Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   RecursivePlanningContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   context);\nstatic bool ShouldRecursivelyPlanNonColocatedSubqueries(Query *subquery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tRecursivePlanningContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontext);\nstatic bool ContainsSubquery(Query *query);\nstatic bool ShouldRecursivelyPlanOuterJoins(Query *query,\n\t\t\t\t\t\t\t\t\t\t\tRecursivePlanningContext *context);\nstatic void RecursivelyPlanNonColocatedSubqueries(Query *subquery,\n\t\t\t\t\t\t\t\t\t\t\t\t  RecursivePlanningContext *context);\nstatic void RecursivelyPlanNonColocatedJoinWalker(Node *joinNode,\n\t\t\t\t\t\t\t\t\t\t\t\t  ColocatedJoinChecker *\n\t\t\t\t\t\t\t\t\t\t\t\t  colocatedJoinChecker,\n\t\t\t\t\t\t\t\t\t\t\t\t  RecursivePlanningContext *\n\t\t\t\t\t\t\t\t\t\t\t\t  recursivePlanningContext);\nstatic void RecursivelyPlanNonColocatedSubqueriesInWhere(Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t ColocatedJoinChecker *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t colocatedJoinChecker,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t RecursivePlanningContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t recursivePlanningContext);\nstatic bool RecursivelyPlanRecurringTupleOuterJoinWalker(Node *node, Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t RecursivePlanningContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t context,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t bool chainedJoin);\nstatic void RecursivelyPlanDistributedJoinNode(Node *node, Query *query,\n\t\t\t\t\t\t\t\t\t\t\t   RecursivePlanningContext *context);\nstatic bool IsRTERefRecurring(RangeTblRef *rangeTableRef, Query *query);\nstatic List * SublinkListFromWhere(Query *originalQuery);\nstatic bool ExtractSublinkWalker(Node *node, List **sublinkList);\nstatic bool ShouldRecursivelyPlanSublinks(Query *query);\nstatic bool RecursivelyPlanAllSubqueries(Node *node,\n\t\t\t\t\t\t\t\t\t\t RecursivePlanningContext *planningContext);\nstatic DeferredErrorMessage * RecursivelyPlanCTEs(Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\t  RecursivePlanningContext *context);\nstatic bool RecursivelyPlanSubqueryWalker(Node *node, RecursivePlanningContext *context);\nstatic bool ShouldRecursivelyPlanSubquery(Query *subquery,\n\t\t\t\t\t\t\t\t\t\t  RecursivePlanningContext *context);\nstatic bool AllDistributionKeysInSubqueryAreEqual(Query *subquery,\n\t\t\t\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t  restrictionContext);\nstatic bool ShouldRecursivelyPlanSetOperation(Query *query,\n\t\t\t\t\t\t\t\t\t\t\t  RecursivePlanningContext *context);\nstatic bool RecursivelyPlanSubquery(Query *subquery,\n\t\t\t\t\t\t\t\t\tRecursivePlanningContext *planningContext);\nstatic void RecursivelyPlanSetOperations(Query *query, Node *node,\n\t\t\t\t\t\t\t\t\t\t RecursivePlanningContext *context);\nstatic bool IsLocalTableRteOrMatView(Node *node);\nstatic DistributedSubPlan * CreateDistributedSubPlan(uint32 subPlanId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t Query *subPlanQuery);\nstatic bool CteReferenceListWalker(Node *node, CteReferenceWalkerContext *context);\nstatic bool ContainsReferencesToOuterQueryWalker(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t VarLevelsUpWalkerContext *context);\nstatic bool NodeContainsSubqueryReferencingOuterQuery(Node *node);\nstatic void WrapFunctionsInSubqueries(Query *query);\nstatic void TransformFunctionRTE(RangeTblEntry *rangeTblEntry);\nstatic bool ShouldTransformRTE(RangeTblEntry *rangeTableEntry);\nstatic Query * BuildReadIntermediateResultsQuery(List *targetEntryList,\n\t\t\t\t\t\t\t\t\t\t\t\t List *columnAliasList,\n\t\t\t\t\t\t\t\t\t\t\t\t Const *resultIdConst, Oid functionOid,\n\t\t\t\t\t\t\t\t\t\t\t\t bool useBinaryCopyFormat);\nstatic Query * CreateOuterSubquery(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t   List *outerSubqueryTargetList);\nstatic List * GenerateRequiredColNamesFromTargetList(List *targetList);\nstatic char * GetRelationNameAndAliasName(RangeTblEntry *rangeTablentry);\nstatic bool CanPushdownRecurringOuterJoinOnOuterRTE(RangeTblEntry *rte);\nstatic bool CanPushdownRecurringOuterJoinOnInnerVar(Var *innervar, RangeTblEntry *rte);\nstatic bool CanPushdownRecurringOuterJoin(JoinExpr *joinExpr, Query *query);\n#if PG_VERSION_NUM < PG_VERSION_17\nstatic bool hasPseudoconstantQuals(RelationRestrictionContext *\n\t\t\t\t\t\t\t\t   relationRestrictionContext);\n#endif\n\n/*\n * GenerateSubplansForSubqueriesAndCTEs is a wrapper around RecursivelyPlanSubqueriesAndCTEs.\n * The function returns the subplans if necessary. For the details of when/how subplans are\n * generated, see RecursivelyPlanSubqueriesAndCTEs().\n *\n * Note that the input originalQuery query is modified if any subplans are generated.\n */\nList *\nGenerateSubplansForSubqueriesAndCTEs(uint64 planId, Query *originalQuery,\n\t\t\t\t\t\t\t\t\t PlannerRestrictionContext *plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t RouterPlanType routerPlan)\n{\n\tRecursivePlanningContext context;\n\n\trecursivePlanningDepth++;\n\n\t/*\n\t * Plan subqueries and CTEs that cannot be pushed down by recursively\n\t * calling the planner and add the resulting plans to subPlanList.\n\t */\n\tcontext.level = 0;\n\tcontext.planId = planId;\n\tcontext.subPlanList = NIL;\n\tcontext.plannerRestrictionContext = plannerRestrictionContext;\n\tcontext.forceRecursivelyPlanRecurringOuterJoins = false;\n\n\t/*\n\t * Force recursive planning of recurring outer joins for these queries\n\t * since the planning error from the previous step is generated prior to\n\t * the actual planning attempt.\n\t */\n\tif (routerPlan == DML_QUERY)\n\t{\n\t\tcontext.forceRecursivelyPlanRecurringOuterJoins = true;\n\t}\n\n\t/*\n\t * Calculating the distribution key equality upfront is a trade-off for us.\n\t *\n\t * When the originalQuery contains the distribution key equality, we'd be\n\t * able to skip further checks for each lower level subqueries (i.e., if the\n\t * all query contains distribution key equality, each subquery also contains\n\t * distribution key equality.)\n\t *\n\t * When the originalQuery doesn't contain the distribution key equality,\n\t * calculating this wouldn't help us at all, we should individually check\n\t * each each subquery and subquery joins among subqueries.\n\t */\n\tcontext.allDistributionKeysInQueryAreEqual =\n\t\tAllDistributionKeysInQueryAreEqual(originalQuery, plannerRestrictionContext);\n\tDeferredErrorMessage *error = RecursivelyPlanSubqueriesAndCTEs(originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &context);\n\tif (error != NULL)\n\t{\n\t\trecursivePlanningDepth--;\n\t\tRaiseDeferredError(error, ERROR);\n\t}\n\n\tif (context.subPlanList && IsLoggableLevel(DEBUG1))\n\t{\n\t\tStringInfo subPlanString = makeStringInfo();\n\t\tpg_get_query_def(originalQuery, subPlanString);\n\t\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t\t \"Plan \" UINT64_FORMAT\n\t\t\t\t\t\t\t \" query after replacing subqueries and CTEs: %s\", planId,\n\t\t\t\t\t\t\t subPlanString->data)));\n\t}\n\n\trecursivePlanningDepth--;\n\n\treturn context.subPlanList;\n}\n\n\n/*\n * RecursivelyPlanSubqueriesAndCTEs finds subqueries and CTEs that cannot be pushed down to\n * workers directly and instead plans them by recursively calling the planner and\n * adding the subplan to subPlanList.\n *\n * Subplans are executed prior to the distributed plan and the results are written\n * to temporary files on workers.\n *\n * CTE references are replaced by a subquery on the read_intermediate_result\n * function, which reads from the temporary file.\n *\n * If recursive planning results in an error then the error is returned. Otherwise, the\n * subplans will be added to subPlanList.\n */\nstatic DeferredErrorMessage *\nRecursivelyPlanSubqueriesAndCTEs(Query *query, RecursivePlanningContext *context)\n{\n\tDeferredErrorMessage *error = RecursivelyPlanCTEs(query, context);\n\tif (error != NULL)\n\t{\n\t\treturn error;\n\t}\n\n\tif (SubqueryPushdown)\n\t{\n\t\t/*\n\t\t * When the subquery_pushdown flag is enabled we make some hacks\n\t\t * to push down subqueries with LIMIT. Recursive planning would\n\t\t * valiantly do the right thing and try to recursively plan the\n\t\t * inner subqueries, but we don't really want it to because those\n\t\t * subqueries might not be supported and would be much slower.\n\t\t *\n\t\t * Instead, we skip recursive planning altogether when\n\t\t * subquery_pushdown is enabled.\n\t\t */\n\t\treturn NULL;\n\t}\n\n\t/* make sure function calls in joins are executed in the coordinator */\n\tWrapFunctionsInSubqueries(query);\n\n\t/* descend into subqueries */\n\tquery_tree_walker(query, RecursivelyPlanSubqueryWalker, context, 0);\n\n\t/*\n\t * At this point, all CTEs, leaf subqueries containing local tables and\n\t * non-pushdownable subqueries have been replaced. We now check for\n\t * combinations of subqueries that cannot be pushed down (e.g.\n\t * <subquery on reference table> UNION <subquery on distributed table>).\n\t *\n\t * This code also runs for the top-level query, which allows us to support\n\t * top-level set operations.\n\t */\n\n\tif (ShouldRecursivelyPlanSetOperation(query, context))\n\t{\n\t\tRecursivelyPlanSetOperations(query, (Node *) query->setOperations, context);\n\t}\n\n\tif (query->havingQual != NULL)\n\t{\n\t\tif (NodeContainsSubqueryReferencingOuterQuery(query->havingQual))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"Subqueries in HAVING cannot refer to outer query\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\n\t\tRecursivelyPlanAllSubqueries(query->havingQual, context);\n\t}\n\n\t/*\n\t * If the query doesn't have distribution key equality,\n\t * recursively plan some of its subqueries.\n\t */\n\tif (ShouldRecursivelyPlanNonColocatedSubqueries(query, context))\n\t{\n\t\tRecursivelyPlanNonColocatedSubqueries(query, context);\n\t}\n\n\n\tif (ShouldConvertLocalTableJoinsToSubqueries(query->rtable))\n\t{\n\t\t/*\n\t\t * Logical planner cannot handle \"local_table\" [OUTER] JOIN \"dist_table\", or\n\t\t * a query with local table/citus local table and subquery. We convert local/citus local\n\t\t * tables to a subquery until they can be planned.\n\t\t */\n\t\tRecursivelyPlanLocalTableJoins(query, context);\n\t}\n\n\t/*\n\t * Similarly, logical planner cannot handle outer joins when the outer rel\n\t * is recurring, such as \"<recurring> LEFT JOIN <distributed>\". In that case,\n\t * we convert distributed table into a subquery and recursively plan inner\n\t * side of the outer join. That way, inner rel gets converted into an intermediate\n\t * result and logical planner can handle the new query since it's of the from\n\t * \"<recurring> LEFT JOIN <recurring>\".\n\t */\n\tif (ShouldRecursivelyPlanOuterJoins(query, context))\n\t{\n\t\tRecursivelyPlanRecurringTupleOuterJoinWalker((Node *) query->jointree,\n\t\t\t\t\t\t\t\t\t\t\t\t\t query, context, false);\n\t}\n\n\t/*\n\t * If the FROM clause is recurring (does not contain a distributed table),\n\t * then we cannot have any distributed tables appearing in subqueries in\n\t * the SELECT and WHERE clauses.\n\t *\n\t * We do the sublink conversations at the end of the recursive planning\n\t * because earlier steps might have transformed the query into a\n\t * shape that needs recursively planning the sublinks.\n\t */\n\tif (ShouldRecursivelyPlanSublinks(query))\n\t{\n\t\t/* replace all subqueries in the WHERE clause */\n\t\tif (query->jointree && query->jointree->quals)\n\t\t{\n\t\t\tRecursivelyPlanAllSubqueries((Node *) query->jointree->quals, context);\n\t\t}\n\n\t\t/* replace all subqueries in the SELECT clause */\n\t\tRecursivelyPlanAllSubqueries((Node *) query->targetList, context);\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * GetPlannerRestrictionContext returns the planner restriction context\n * from the given context.\n */\nPlannerRestrictionContext *\nGetPlannerRestrictionContext(RecursivePlanningContext *recursivePlanningContext)\n{\n\treturn recursivePlanningContext->plannerRestrictionContext;\n}\n\n\n/*\n * ShouldRecursivelyPlanNonColocatedSubqueries returns true if the input query contains joins\n * that are not on the distribution key.\n * *\n * Note that at the point that this function is called, we've already recursively planned all\n * the leaf subqueries. Thus, we're actually checking whether the joins among the subqueries\n * on the distribution key or not.\n */\nstatic bool\nShouldRecursivelyPlanNonColocatedSubqueries(Query *subquery,\n\t\t\t\t\t\t\t\t\t\t\tRecursivePlanningContext *context)\n{\n\t/*\n\t * If the input query already contains the equality, simply return since it is not\n\t * possible to find any non colocated subqueries.\n\t */\n\tif (context->allDistributionKeysInQueryAreEqual)\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * This check helps us in two ways:\n\t *   (i) We're not targeting queries that don't include subqueries at all,\n\t *       they should go through regular planning.\n\t *  (ii) Lower level subqueries are already recursively planned, so we should\n\t *       only bother non-colocated subquery joins, which only happens when\n\t *       there are subqueries.\n\t */\n\tif (!ContainsSubquery(subquery))\n\t{\n\t\treturn false;\n\t}\n\n\t/* direct joins with local tables are not supported by any of Citus planners */\n\tif (FindNodeMatchingCheckFunctionInRangeTableList(subquery->rtable,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  IsLocalTableRteOrMatView))\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * Finally, check whether this subquery contains distribution key equality or not.\n\t */\n\tif (!AllDistributionKeysInSubqueryAreEqual(subquery,\n\t\t\t\t\t\t\t\t\t\t\t   context->plannerRestrictionContext))\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ContainsSubquery returns true if the input query contains any subqueries\n * in the FROM or WHERE clauses.\n */\nstatic bool\nContainsSubquery(Query *query)\n{\n\treturn JoinTreeContainsSubquery(query) || WhereOrHavingClauseContainsSubquery(query);\n}\n\n\n/*\n * ShouldRecursivelyPlanOuterJoins returns true if the JoinRestrictionContext\n * that given RecursivePlanningContext holds implies that the query has outer\n * join(s) that might need to be recursively planned.\n */\nstatic bool\nShouldRecursivelyPlanOuterJoins(Query *query, RecursivePlanningContext *context)\n{\n\tif (!context || !context->plannerRestrictionContext ||\n\t\t!context->plannerRestrictionContext->joinRestrictionContext)\n\t{\n\t\tereport(ERROR, (errmsg(\"unexpectedly got NULL pointer in recursive \"\n\t\t\t\t\t\t\t   \"planning context\")));\n\t}\n\n\tbool hasOuterJoin =\n\t\tcontext->plannerRestrictionContext->joinRestrictionContext->hasOuterJoin;\n#if PG_VERSION_NUM < PG_VERSION_17\n\tif (!EnableOuterJoinsWithPseudoconstantQualsPrePG17 && !hasOuterJoin)\n\t{\n\t\t/*\n\t\t * PG16 commit 695f5deb7902865901eb2d50a70523af655c3a00\n\t\t * disallows replacing joins with scans in queries with pseudoconstant quals.\n\t\t * This commit prevents the set_join_pathlist_hook from being called\n\t\t * if any of the join restrictions is a pseudo-constant.\n\t\t * So in these cases, citus has no info on the join, never sees that the query\n\t\t * has an outer join, and ends up producing an incorrect plan.\n\t\t * PG17 fixes this by commit 9e9931d2bf40e2fea447d779c2e133c2c1256ef3\n\t\t * Therefore, we take this extra measure here for PG versions less than 17.\n\t\t * hasOuterJoin can never be true when set_join_pathlist_hook is absent.\n\t\t */\n\t\tif (hasPseudoconstantQuals(\n\t\t\t\tcontext->plannerRestrictionContext->relationRestrictionContext) &&\n\t\t\tFindNodeMatchingCheckFunction((Node *) query->jointree, IsOuterJoinExpr))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"Distributed queries with outer joins and \"\n\t\t\t\t\t\t\t\t   \"pseudoconstant quals are not supported in PG16.\"),\n\t\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\t\"PG16 disallows replacing joins with scans when the\"\n\t\t\t\t\t\t\t\t\" query has pseudoconstant quals\"),\n\t\t\t\t\t\t\terrhint(\"Consider upgrading your PG version to PG17+\")));\n\t\t}\n\t}\n#endif\n\treturn hasOuterJoin;\n}\n\n\n/*\n * RecursivelyPlanNonColocatedSubqueries gets a query which includes one or more\n * other subqueries that are not joined on their distribution keys. The function\n * tries to recursively plan some of the subqueries to make the input query\n * executable by Citus.\n *\n * The function picks an anchor subquery and iterates on the remaining subqueries.\n * Whenever it finds a non colocated subquery with the anchor subquery, the function\n * decides to recursively plan the non colocated subquery.\n *\n * The function first handles subqueries in FROM clause (i.e., jointree->fromlist) and then\n * subqueries in WHERE clause (i.e., jointree->quals).\n *\n * The function does not treat outer joins seperately. Thus, we might end up with\n * a query where the function decides to recursively plan an outer side of an outer\n * join (i.e., LEFT side of LEFT JOIN). For simplicity, we chose to do so and handle\n * outer joins with a seperate pass on the join tree.\n */\nstatic void\nRecursivelyPlanNonColocatedSubqueries(Query *subquery, RecursivePlanningContext *context)\n{\n\tFromExpr *joinTree = subquery->jointree;\n\n\t/* create the context for the non colocated subquery planning */\n\tPlannerRestrictionContext *restrictionContext = context->plannerRestrictionContext;\n\tColocatedJoinChecker colocatedJoinChecker = CreateColocatedJoinChecker(subquery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   restrictionContext);\n\n\t/*\n\t * Although this is a rare case, we weren't able to pick an anchor\n\t * range table entry, so we cannot continue.\n\t */\n\tif (colocatedJoinChecker.anchorRelationRestrictionList == NIL)\n\t{\n\t\treturn;\n\t}\n\n\t/* handle from clause subqueries first */\n\tRecursivelyPlanNonColocatedJoinWalker((Node *) joinTree, &colocatedJoinChecker,\n\t\t\t\t\t\t\t\t\t\t  context);\n\n\t/* handle subqueries in WHERE clause */\n\tRecursivelyPlanNonColocatedSubqueriesInWhere(subquery, &colocatedJoinChecker,\n\t\t\t\t\t\t\t\t\t\t\t\t context);\n}\n\n\n/*\n * RecursivelyPlanNonColocatedJoinWalker gets a join node and walks over it to find\n * subqueries that live under the node.\n *\n * When a subquery found, it's checked whether the subquery is colocated with the\n * anchor subquery specified in the nonColocatedJoinContext. If not,\n * the subquery is recursively planned.\n */\nstatic void\nRecursivelyPlanNonColocatedJoinWalker(Node *joinNode,\n\t\t\t\t\t\t\t\t\t  ColocatedJoinChecker *colocatedJoinChecker,\n\t\t\t\t\t\t\t\t\t  RecursivePlanningContext *recursivePlanningContext)\n{\n\tif (joinNode == NULL)\n\t{\n\t\treturn;\n\t}\n\telse if (IsA(joinNode, FromExpr))\n\t{\n\t\tFromExpr *fromExpr = (FromExpr *) joinNode;\n\t\tListCell *fromExprCell;\n\n\t\t/*\n\t\t * For each element of the from list, check whether the element is\n\t\t * colocated with the anchor subquery by recursing until we\n\t\t * find the subqueries.\n\t\t */\n\t\tforeach(fromExprCell, fromExpr->fromlist)\n\t\t{\n\t\t\tNode *fromElement = (Node *) lfirst(fromExprCell);\n\n\t\t\tRecursivelyPlanNonColocatedJoinWalker(fromElement, colocatedJoinChecker,\n\t\t\t\t\t\t\t\t\t\t\t\t  recursivePlanningContext);\n\t\t}\n\t}\n\telse if (IsA(joinNode, JoinExpr))\n\t{\n\t\tJoinExpr *joinExpr = (JoinExpr *) joinNode;\n\n\t\t/* recurse into the left subtree */\n\t\tRecursivelyPlanNonColocatedJoinWalker(joinExpr->larg, colocatedJoinChecker,\n\t\t\t\t\t\t\t\t\t\t\t  recursivePlanningContext);\n\n\t\t/* recurse into the right subtree */\n\t\tRecursivelyPlanNonColocatedJoinWalker(joinExpr->rarg, colocatedJoinChecker,\n\t\t\t\t\t\t\t\t\t\t\t  recursivePlanningContext);\n\t}\n\telse if (IsA(joinNode, RangeTblRef))\n\t{\n\t\tint rangeTableIndex = ((RangeTblRef *) joinNode)->rtindex;\n\t\tList *rangeTableList = colocatedJoinChecker->subquery->rtable;\n\t\tRangeTblEntry *rte = rt_fetch(rangeTableIndex, rangeTableList);\n\n\t\t/* we're only interested in subqueries for now */\n\t\tif (rte->rtekind != RTE_SUBQUERY)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\t/*\n\t\t * If the subquery is not colocated with the anchor subquery,\n\t\t * recursively plan it.\n\t\t */\n\t\tQuery *subquery = rte->subquery;\n\t\tif (!SubqueryColocated(subquery, colocatedJoinChecker))\n\t\t{\n\t\t\tRecursivelyPlanSubquery(subquery, recursivePlanningContext);\n\t\t}\n\t}\n\telse\n\t{\n\t\tpg_unreachable();\n\t}\n}\n\n\n/*\n * RecursivelyPlanNonColocatedSubqueriesInWhere gets a query and walks over its\n * sublinks to find subqueries that live in WHERE clause.\n *\n * When a subquery found, it's checked whether the subquery is colocated with the\n * anchor subquery specified in the nonColocatedJoinContext. If not,\n * the subquery is recursively planned.\n */\nstatic void\nRecursivelyPlanNonColocatedSubqueriesInWhere(Query *query,\n\t\t\t\t\t\t\t\t\t\t\t ColocatedJoinChecker *colocatedJoinChecker,\n\t\t\t\t\t\t\t\t\t\t\t RecursivePlanningContext *\n\t\t\t\t\t\t\t\t\t\t\t recursivePlanningContext)\n{\n\tList *sublinkList = SublinkListFromWhere(query);\n\tListCell *sublinkCell = NULL;\n\n\tforeach(sublinkCell, sublinkList)\n\t{\n\t\tSubLink *sublink = (SubLink *) lfirst(sublinkCell);\n\t\tQuery *subselect = (Query *) sublink->subselect;\n\n\t\t/* subselect is probably never NULL, but anyway lets keep the check */\n\t\tif (subselect == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!SubqueryColocated(subselect, colocatedJoinChecker))\n\t\t{\n\t\t\tRecursivelyPlanSubquery(subselect, recursivePlanningContext);\n\t\t}\n\t}\n}\n\n\n/*\n * RecursivelyPlanRecurringTupleOuterJoinWalker descends into a join tree and\n * recursively plans all non-recurring (i.e., distributed) rels that that\n * participate in an outer join expression together with a recurring rel,\n * such as <distributed> in \"<recurring> LEFT JOIN <distributed>\", i.e.,\n * where the recurring rel causes returning recurring tuples from the worker\n * nodes.\n *\n * Returns true if given node is recurring.\n *\n * See RecursivelyPlanDistributedJoinNode() function for the explanation on\n * what does it mean for a node to be \"recurring\" or \"distributed\".\n */\nstatic bool\nRecursivelyPlanRecurringTupleOuterJoinWalker(Node *node, Query *query,\n\t\t\t\t\t\t\t\t\t\t\t RecursivePlanningContext *\n\t\t\t\t\t\t\t\t\t\t\t recursivePlanningContext,\n\t\t\t\t\t\t\t\t\t\t\t bool chainedJoin)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\telse if (IsA(node, FromExpr))\n\t{\n\t\tFromExpr *fromExpr = (FromExpr *) node;\n\t\tListCell *fromExprCell;\n\n\t\t/* search for join trees in each FROM element */\n\t\tforeach(fromExprCell, fromExpr->fromlist)\n\t\t{\n\t\t\tNode *fromElement = (Node *) lfirst(fromExprCell);\n\n\t\t\tRecursivelyPlanRecurringTupleOuterJoinWalker(fromElement, query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t recursivePlanningContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t false);\n\t\t}\n\n\t\t/*\n\t\t * Can only appear during the top-level call and top-level callers\n\t\t * are not interested in the return value. Even more, we can't tell\n\t\t * whether a FromExpr is recurring or not.\n\t\t */\n\t\treturn false;\n\t}\n\telse if (IsA(node, JoinExpr))\n\t{\n\t\tJoinExpr *joinExpr = (JoinExpr *) node;\n\n\t\tNode *leftNode = joinExpr->larg;\n\t\tNode *rightNode = joinExpr->rarg;\n\n\t\t/*\n\t\t * There may be recursively plannable outer joins deeper in the join tree.\n\t\t *\n\t\t * We first handle the sub join trees and then the top level one since the\n\t\t * top level join expression might not require recursive planning after\n\t\t * handling the sub join trees.\n\t\t */\n\t\tbool leftNodeRecurs =\n\t\t\tRecursivelyPlanRecurringTupleOuterJoinWalker(leftNode, query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t recursivePlanningContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t true);\n\t\tbool rightNodeRecurs =\n\t\t\tRecursivelyPlanRecurringTupleOuterJoinWalker(rightNode, query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t recursivePlanningContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t true);\n\t\tswitch (joinExpr->jointype)\n\t\t{\n\t\t\tcase JOIN_LEFT:\n\t\t\t{\n\t\t\t\t/* <recurring> left join <distributed> */\n\t\t\t\tif (leftNodeRecurs && !rightNodeRecurs)\n\t\t\t\t{\n\t\t\t\t\tif (recursivePlanningContext->forceRecursivelyPlanRecurringOuterJoins\n\t\t\t\t\t\t||\n\t\t\t\t\t\tchainedJoin || !CanPushdownRecurringOuterJoin(joinExpr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  query))\n\t\t\t\t\t{\n\t\t\t\t\t\tereport(DEBUG1, (errmsg(\"recursively planning right side of \"\n\t\t\t\t\t\t\t\t\t\t\t\t\"the left join since the outer side \"\n\t\t\t\t\t\t\t\t\t\t\t\t\"is a recurring rel\")));\n\t\t\t\t\t\tRecursivelyPlanDistributedJoinNode(rightNode, query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   recursivePlanningContext);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tereport(DEBUG3, (errmsg(\n\t\t\t\t\t\t\t\t\t\t\t \"a push down safe left join with recurring left side\")));\n\t\t\t\t\t\tleftNodeRecurs = false; /* left node will be pushed down */\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * A LEFT JOIN is recurring if the lhs is recurring.\n\t\t\t\t * Note that we might have converted the rhs into a recurring\n\t\t\t\t * one too if the lhs is recurring, but this anyway has no\n\t\t\t\t * effects when deciding whether a LEFT JOIN is recurring.\n\t\t\t\t */\n\t\t\t\treturn leftNodeRecurs;\n\t\t\t}\n\n\t\t\tcase JOIN_RIGHT:\n\t\t\t{\n\t\t\t\t/* <distributed> right join <recurring> */\n\t\t\t\tif (!leftNodeRecurs && rightNodeRecurs)\n\t\t\t\t{\n\t\t\t\t\tif (recursivePlanningContext->forceRecursivelyPlanRecurringOuterJoins\n\t\t\t\t\t\t||\n\t\t\t\t\t\tchainedJoin || !CanPushdownRecurringOuterJoin(joinExpr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  query))\n\t\t\t\t\t{\n\t\t\t\t\t\tereport(DEBUG1, (errmsg(\"recursively planning left side of \"\n\t\t\t\t\t\t\t\t\t\t\t\t\"the right join since the outer side \"\n\t\t\t\t\t\t\t\t\t\t\t\t\"is a recurring rel\")));\n\t\t\t\t\t\tRecursivelyPlanDistributedJoinNode(leftNode, query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   recursivePlanningContext);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tereport(DEBUG3, (errmsg(\n\t\t\t\t\t\t\t\t\t\t\t \"a push down safe right join with recurring left side\")));\n\t\t\t\t\t\trightNodeRecurs = false; /* right node will be pushed down */\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * Similar to LEFT JOINs, a RIGHT JOIN is recurring if the rhs\n\t\t\t\t * is recurring.\n\t\t\t\t */\n\t\t\t\treturn rightNodeRecurs;\n\t\t\t}\n\n\t\t\tcase JOIN_FULL:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * <recurring> full join <distributed>\n\t\t\t\t * <distributed> full join <recurring>\n\t\t\t\t */\n\t\t\t\tif (leftNodeRecurs && !rightNodeRecurs)\n\t\t\t\t{\n\t\t\t\t\tereport(DEBUG1, (errmsg(\"recursively planning right side of \"\n\t\t\t\t\t\t\t\t\t\t\t\"the full join since the other side \"\n\t\t\t\t\t\t\t\t\t\t\t\"is a recurring rel\")));\n\t\t\t\t\tRecursivelyPlanDistributedJoinNode(rightNode, query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   recursivePlanningContext);\n\t\t\t\t}\n\t\t\t\telse if (!leftNodeRecurs && rightNodeRecurs)\n\t\t\t\t{\n\t\t\t\t\tereport(DEBUG1, (errmsg(\"recursively planning left side of \"\n\t\t\t\t\t\t\t\t\t\t\t\"the full join since the other side \"\n\t\t\t\t\t\t\t\t\t\t\t\"is a recurring rel\")));\n\t\t\t\t\tRecursivelyPlanDistributedJoinNode(leftNode, query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   recursivePlanningContext);\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * An OUTER JOIN is recurring if any sides of the join is\n\t\t\t\t * recurring. As in other outer join types, it doesn't matter\n\t\t\t\t * whether the other side was / became recurring or not.\n\t\t\t\t */\n\t\t\t\treturn leftNodeRecurs || rightNodeRecurs;\n\t\t\t}\n\n\t\t\tcase JOIN_INNER:\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We don't need to recursively plan non-outer joins and we\n\t\t\t\t * already descended into sub join trees to handle outer joins\n\t\t\t\t * buried in them.\n\t\t\t\t */\n\t\t\t\treturn leftNodeRecurs && rightNodeRecurs;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"got unexpected join type (%d) when recursively \"\n\t\t\t\t\t\t\t\t\t   \"planning a join\",\n\t\t\t\t\t\t\t\t\t   joinExpr->jointype)));\n\t\t\t}\n\t\t}\n\t}\n\telse if (IsA(node, RangeTblRef))\n\t{\n\t\treturn IsRTERefRecurring((RangeTblRef *) node, query);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, errmsg(\"got unexpected node type (%d) when recursively \"\n\t\t\t\t\t\t\t  \"planning a join\",\n\t\t\t\t\t\t\t  nodeTag(node)));\n\t}\n}\n\n\n/*\n * RecursivelyPlanDistributedJoinNode is a helper function for\n * RecursivelyPlanRecurringTupleOuterJoinWalker that recursively plans given\n * distributed node that is known to be inner side of an outer join.\n *\n * Fails to do so if the distributed join node references the recurring one.\n * In that case, we don't throw an error here but instead we let\n * DeferredErrorIfUnsupportedRecurringTuplesJoin to so for a better error\n * message.\n *\n * We call a node \"distributed\" if it points to a distributed table or a\n * more complex object (i.e., a join tree or a subquery) that can be pushed\n * down to the worker nodes directly. For a join, this means that it's either\n * an INNER join where any side of it is a distributed table / a distributed\n * sub join tree, or an OUTER join where the outer side is a distributed table\n * / a distributed sub join tree.\n */\nstatic void\nRecursivelyPlanDistributedJoinNode(Node *node, Query *query,\n\t\t\t\t\t\t\t\t   RecursivePlanningContext *recursivePlanningContext)\n{\n\tif (IsA(node, JoinExpr))\n\t{\n\t\t/*\n\t\t * This, for example, means that RecursivelyPlanRecurringTupleOuterJoinWalker\n\t\t * needs to plan inner side, i.e., \"<distributed> INNER JOIN <distributed>\",\n\t\t * of the following join:\n\t\t *   <recurring> LEFT JOIN (<distributed> JOIN <distributed>)\n\t\t *\n\t\t * XXX: Ideally, we should handle such a sub join tree by moving\n\t\t *      it into a subquery \"as a whole\" but this implies that we need to\n\t\t *      rebuild the rtable and re-point all the Vars to the new rtable\n\t\t *      indexes, so we've not implemented that yet.\n\t\t *\n\t\t *      Instead, we recursively plan all the distributed tables in that\n\t\t *      sub join tree. This is much more inefficient than the other\n\t\t *      approach (since we lose the opportunity to push-down the whole\n\t\t *      sub join tree into the workers) but is easier to implement.\n\t\t */\n\n\t\tRecursivelyPlanDistributedJoinNode(((JoinExpr *) node)->larg,\n\t\t\t\t\t\t\t\t\t\t   query, recursivePlanningContext);\n\n\t\tRecursivelyPlanDistributedJoinNode(((JoinExpr *) node)->rarg,\n\t\t\t\t\t\t\t\t\t\t   query, recursivePlanningContext);\n\n\t\treturn;\n\t}\n\n\tif (!IsA(node, RangeTblRef))\n\t{\n\t\tereport(ERROR, (errmsg(\"unexpected join node type (%d)\",\n\t\t\t\t\t\t\t   nodeTag(node))));\n\t}\n\n\tRangeTblRef *rangeTableRef = (RangeTblRef *) node;\n\tif (IsRTERefRecurring(rangeTableRef, query))\n\t{\n\t\t/*\n\t\t * Not the top-level callers but RecursivelyPlanDistributedJoinNode\n\t\t * might call itself for recurring nodes and need to skip them.\n\t\t */\n\t\treturn;\n\t}\n\n\tRangeTblEntry *distributedRte = rt_fetch(rangeTableRef->rtindex,\n\t\t\t\t\t\t\t\t\t\t\t query->rtable);\n\tif (distributedRte->rtekind == RTE_RELATION)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"recursively planning distributed relation %s \"\n\t\t\t\t\t\t\t\t\"since it is part of a distributed join node \"\n\t\t\t\t\t\t\t\t\"that is outer joined with a recurring rel\",\n\t\t\t\t\t\t\t\tGetRelationNameAndAliasName(distributedRte))));\n\n\t\tPlannerRestrictionContext *restrictionContext =\n\t\t\tGetPlannerRestrictionContext(recursivePlanningContext);\n\t\tList *requiredAttributes =\n\t\t\tRequiredAttrNumbersForRelation(distributedRte, restrictionContext);\n\n\t\tRTEPermissionInfo *perminfo = NULL;\n\t\tif (distributedRte->perminfoindex)\n\t\t{\n\t\t\tperminfo = getRTEPermissionInfo(query->rteperminfos, distributedRte);\n\t\t}\n\n\t\tReplaceRTERelationWithRteSubquery(distributedRte, requiredAttributes,\n\t\t\t\t\t\t\t\t\t\t  recursivePlanningContext, perminfo);\n\t}\n\telse if (distributedRte->rtekind == RTE_SUBQUERY)\n\t{\n\t\t/*\n\t\t * We don't try logging the subquery here because RecursivelyPlanSubquery\n\t\t * will anyway do so if the query doesn't reference the outer query.\n\t\t */\n\t\tereport(DEBUG1, (errmsg(\"recursively planning the distributed subquery \"\n\t\t\t\t\t\t\t\t\"since it is part of a distributed join node \"\n\t\t\t\t\t\t\t\t\"that is outer joined with a recurring rel\")));\n\n\t\tbool recursivelyPlanned = RecursivelyPlanSubquery(distributedRte->subquery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  recursivePlanningContext);\n\t\tif (!recursivelyPlanned)\n\t\t{\n\t\t\t/*\n\t\t\t * RecursivelyPlanSubquery fails to plan a subquery only if it\n\t\t\t * contains references to the outer query. This means that, we can't\n\t\t\t * plan such outer joins (like <recurring LEFT OUTER distributed>)\n\t\t\t * if it's a LATERAL join where the distributed side is a subquery that\n\t\t\t * references the outer side, as in,\n\t\t\t *\n\t\t\t * SELECT * FROM reference\n\t\t\t * LEFT JOIN LATERAL\n\t\t\t * (SELECT * FROM distributed WHERE reference.b > distributed.b) q\n\t\t\t * USING (a);\n\t\t\t */\n\t\t\tAssert(ContainsReferencesToOuterQuery(distributedRte->subquery));\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * We don't expect RecursivelyPlanRecurringTupleOuterJoinWalker to try recursively\n\t\t * plan such an RTE.\n\t\t */\n\t\tereport(ERROR, errmsg(\"got unexpected RTE type (%d) when recursively \"\n\t\t\t\t\t\t\t  \"planning a join\",\n\t\t\t\t\t\t\t  distributedRte->rtekind));\n\t}\n}\n\n\n/*\n * IsRTERefRecurring returns true if given rte reference points to a recurring\n * rte.\n *\n * If an rte points to a table, then we call it recurring if the table is not\n * a distributed table. Otherwise, e.g., if it points a query, then we call it\n * recurring if none of the rtes that belongs to the query point to a distributed\n * table.\n *\n * Note that it's safe to assume a subquery is not recurring if we have a rte reference\n * to a distributed table somewhere in the query tree. For example, considering\n * the subquery (q) of the the following query:\n *   SELECT * FROM ref LEFT JOIN (SELECT * FROM ref LEFT dist) q,\n * one might think that it's not appropriate to call IsRTERefRecurring for subquery\n * (q). However, this is already not the case because this function is called\n * in the context of recursive planning and hence any query that contains\n * rtes pointing to distributed tables and that cannot be pushed down to worker\n * nodes should've been recursively planned already. This is because, the recursive\n * planner processes the queries in bottom-up fashion. For this reason, the subquery\n * in the example should've already be converted to the following before we check\n * the rte reference that points to the subquery (q):\n *   SELECT * FROM ref LEFT JOIN (SELECT * FROM ref LEFT (SELECT * FROM read_intermediate_result()) dist_1)\n * That way, we wouldn't incorrectly say that (SELECT * FROM ref LEFT dist) is a\n * distributed subquery (due to having a reference to a distributed table).\n */\nstatic bool\nIsRTERefRecurring(RangeTblRef *rangeTableRef, Query *query)\n{\n\tint rangeTableIndex = rangeTableRef->rtindex;\n\tList *rangeTableList = query->rtable;\n\tRangeTblEntry *rangeTableEntry = rt_fetch(rangeTableIndex, rangeTableList);\n\treturn !FindNodeMatchingCheckFunctionInRangeTableList(list_make1(rangeTableEntry),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  IsDistributedTableRTE);\n}\n\n\n/*\n * SublinkListFromWhere finds the subquery nodes in the where clause of the given query. Note\n * that the function should be called on the original query given that postgres\n * standard_planner() may convert the subqueries in WHERE clause to joins.\n */\nstatic List *\nSublinkListFromWhere(Query *originalQuery)\n{\n\tFromExpr *joinTree = originalQuery->jointree;\n\tList *sublinkList = NIL;\n\n\tif (!joinTree)\n\t{\n\t\treturn NIL;\n\t}\n\n\tNode *queryQuals = joinTree->quals;\n\tExtractSublinkWalker(queryQuals, &sublinkList);\n\n\treturn sublinkList;\n}\n\n\n/*\n * ExtractSublinkWalker walks over a quals node, and finds all sublinks\n * in that node.\n */\nstatic bool\nExtractSublinkWalker(Node *node, List **sublinkList)\n{\n\tbool walkerResult = false;\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, SubLink))\n\t{\n\t\t(*sublinkList) = lappend(*sublinkList, node);\n\t}\n\telse\n\t{\n\t\twalkerResult = expression_tree_walker(node, ExtractSublinkWalker,\n\t\t\t\t\t\t\t\t\t\t\t  sublinkList);\n\t}\n\n\treturn walkerResult;\n}\n\n\n/*\n * ShouldRecursivelyPlanSublinks returns true if the query has a recurring\n * FROM clause.\n */\nstatic bool\nShouldRecursivelyPlanSublinks(Query *query)\n{\n\tbool hasDistributedTable = (FindNodeMatchingCheckFunctionInRangeTableList(\n\t\t\t\t\t\t\t\t\tquery->rtable,\n\t\t\t\t\t\t\t\t\tIsDistributedTableRTE));\n\treturn !hasDistributedTable;\n}\n\n\n/*\n * RecursivelyPlanAllSubqueries descends into an expression tree and recursively\n * plans all subqueries that contain at least one distributed table. The recursive\n * planning starts from the top of the input query.\n */\nstatic bool\nRecursivelyPlanAllSubqueries(Node *node, RecursivePlanningContext *planningContext)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\t\tif (FindNodeMatchingCheckFunctionInRangeTableList(query->rtable, IsCitusTableRTE))\n\t\t{\n\t\t\tRecursivelyPlanSubquery(query, planningContext);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\treturn expression_tree_walker(node, RecursivelyPlanAllSubqueries, planningContext);\n}\n\n\n/*\n * RecursivelyPlanCTEs plans all CTEs in the query by recursively calling the planner\n * The resulting plan is added to planningContext->subPlanList and CTE references\n * are replaced by subqueries that call read_intermediate_result, which reads the\n * intermediate result of the CTE after it is executed.\n *\n * Recursive and modifying CTEs are not yet supported and return an error.\n */\nstatic DeferredErrorMessage *\nRecursivelyPlanCTEs(Query *query, RecursivePlanningContext *planningContext)\n{\n\tListCell *cteCell = NULL;\n\tCteReferenceWalkerContext context = { -1, NIL };\n\n\tif (query->cteList == NIL)\n\t{\n\t\t/* no CTEs, nothing to do */\n\t\treturn NULL;\n\t}\n\n\tif (query->hasRecursive)\n\t{\n\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t \"recursive CTEs are only supported when they \"\n\t\t\t\t\t\t\t \"contain a filter on the distribution column\",\n\t\t\t\t\t\t\t NULL, NULL);\n\t}\n\n\t/* get all RTE_CTEs that point to CTEs from cteList */\n\tCteReferenceListWalker((Node *) query, &context);\n\n\tforeach(cteCell, query->cteList)\n\t{\n\t\tCommonTableExpr *cte = (CommonTableExpr *) lfirst(cteCell);\n\t\tchar *cteName = cte->ctename;\n\t\tQuery *subquery = (Query *) cte->ctequery;\n\t\tuint64 planId = planningContext->planId;\n\t\tList *cteTargetList = NIL;\n\t\tListCell *rteCell = NULL;\n\t\tint replacedCtesCount = 0;\n\n\t\tif (ContainsReferencesToOuterQuery(subquery))\n\t\t{\n\t\t\treturn DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED,\n\t\t\t\t\t\t\t\t \"CTEs that refer to other subqueries are not \"\n\t\t\t\t\t\t\t\t \"supported in multi-shard queries\",\n\t\t\t\t\t\t\t\t NULL, NULL);\n\t\t}\n\n\t\tif (cte->cterefcount == 0 && subquery->commandType == CMD_SELECT)\n\t\t{\n\t\t\t/*\n\t\t\t * SELECT CTEs that aren't referenced aren't executed in postgres.\n\t\t\t * We don't need to generate a subplan for it and can take the rest\n\t\t\t * of this iteration off.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tuint32 subPlanId = list_length(planningContext->subPlanList) + 1;\n\n\t\tif (IsLoggableLevel(DEBUG1))\n\t\t{\n\t\t\tStringInfo subPlanString = makeStringInfo();\n\t\t\tpg_get_query_def(subquery, subPlanString);\n\t\t\tereport(DEBUG1, (errmsg(\"generating subplan \" UINT64_FORMAT\n\t\t\t\t\t\t\t\t\t\"_%u for CTE %s: %s\", planId, subPlanId,\n\t\t\t\t\t\t\t\t\tcteName,\n\t\t\t\t\t\t\t\t\tsubPlanString->data)));\n\t\t}\n\n\t\t/* build a sub plan for the CTE */\n\t\tDistributedSubPlan *subPlan = CreateDistributedSubPlan(subPlanId, subquery);\n\t\tplanningContext->subPlanList = lappend(planningContext->subPlanList, subPlan);\n\n\t\t/* build the result_id parameter for the call to read_intermediate_result */\n\t\tchar *resultId = GenerateResultId(planId, subPlanId);\n\n\t\tif (subquery->returningList)\n\t\t{\n\t\t\t/* modifying CTE with returning */\n\t\t\tcteTargetList = subquery->returningList;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* regular SELECT CTE */\n\t\t\tcteTargetList = subquery->targetList;\n\t\t}\n\n\t\t/* replace references to the CTE with a subquery that reads results */\n\t\tQuery *resultQuery = BuildSubPlanResultQuery(cteTargetList, cte->aliascolnames,\n\t\t\t\t\t\t\t\t\t\t\t\t\t resultId);\n\n\t\tforeach(rteCell, context.cteReferenceList)\n\t\t{\n\t\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rteCell);\n\n\t\t\tif (rangeTableEntry->rtekind != RTE_CTE)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * This RTE pointed to a preceding CTE that was already replaced\n\t\t\t\t * by a subplan.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (strncmp(rangeTableEntry->ctename, cteName, NAMEDATALEN) == 0)\n\t\t\t{\n\t\t\t\t/* change the RTE_CTE into an RTE_SUBQUERY */\n\t\t\t\trangeTableEntry->rtekind = RTE_SUBQUERY;\n\t\t\t\trangeTableEntry->ctename = NULL;\n\t\t\t\trangeTableEntry->ctelevelsup = 0;\n\n\t\t\t\tif (replacedCtesCount == 0)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Replace the first CTE reference with the result query directly.\n\t\t\t\t\t */\n\t\t\t\t\trangeTableEntry->subquery = resultQuery;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Replace subsequent CTE references with a copy of the result\n\t\t\t\t\t * query.\n\t\t\t\t\t */\n\t\t\t\t\trangeTableEntry->subquery = copyObject(resultQuery);\n\t\t\t\t}\n\n\t\t\t\treplacedCtesCount++;\n\t\t\t}\n\t\t}\n\n\t\tAssert(cte->cterefcount == replacedCtesCount);\n\t}\n\n\t/*\n\t * All CTEs are now executed through subplans and RTE_CTEs pointing\n\t * to the CTE list have been replaced with subqueries. We can now\n\t * clear the cteList.\n\t */\n\tquery->cteList = NIL;\n\n\treturn NULL;\n}\n\n\n/*\n * RecursivelyPlanSubqueryWalker recursively finds all the Query nodes and\n * recursively plans if necessary.\n */\nstatic bool\nRecursivelyPlanSubqueryWalker(Node *node, RecursivePlanningContext *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\n\t\tcontext->level += 1;\n\n\t\t/*\n\t\t * First, make sure any subqueries and CTEs within this subquery\n\t\t * are recursively planned if necessary.\n\t\t */\n\t\tDeferredErrorMessage *error = RecursivelyPlanSubqueriesAndCTEs(query, context);\n\t\tif (error != NULL)\n\t\t{\n\t\t\tRaiseDeferredError(error, ERROR);\n\t\t}\n\t\tcontext->level -= 1;\n\n\t\t/*\n\t\t * Recursively plan this subquery if it cannot be pushed down and is\n\t\t * eligible for recursive planning.\n\t\t */\n\t\tif (ShouldRecursivelyPlanSubquery(query, context))\n\t\t{\n\t\t\tRecursivelyPlanSubquery(query, context);\n\t\t}\n\n\t\t/* we're done, no need to recurse anymore for this query */\n\t\treturn false;\n\t}\n\n\treturn expression_tree_walker(node, RecursivelyPlanSubqueryWalker, context);\n}\n\n\n/*\n * ShouldRecursivelyPlanSubquery decides whether the input subquery should be recursively\n * planned or not.\n *\n * For the details, see the cases in the function.\n */\nstatic bool\nShouldRecursivelyPlanSubquery(Query *subquery, RecursivePlanningContext *context)\n{\n\tif (FindNodeMatchingCheckFunctionInRangeTableList(subquery->rtable,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  IsLocalTableRteOrMatView))\n\t{\n\t\t/*\n\t\t * Postgres can always plan queries that don't require distributed planning.\n\t\t * Note that we need to check this first, otherwise the calls to the many other\n\t\t * Citus planner functions would error our due to local relations.\n\t\t *\n\t\t * TODO: We could only successfully create distributed plans with local tables\n\t\t * when the local tables are on the leaf queries and the upper level queries\n\t\t * do not contain any other local tables.\n\t\t */\n\t}\n\telse if (CanPushdownSubquery(subquery, false))\n\t{\n\t\t/*\n\t\t * We should do one more check for the distribution key equality.\n\t\t *\n\t\t * If the input query to the planner doesn't contain distribution key equality,\n\t\t * we should further check whether this individual subquery contains or not.\n\t\t *\n\t\t * If all relations are not joined on their distribution keys for the given\n\t\t * subquery, we cannot push push it down and therefore we should try to\n\t\t * recursively plan it.\n\t\t */\n\t\tif (!context->allDistributionKeysInQueryAreEqual &&\n\t\t\t!AllDistributionKeysInSubqueryAreEqual(subquery,\n\t\t\t\t\t\t\t\t\t\t\t\t   context->plannerRestrictionContext))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\t/*\n\t\t * Citus can pushdown this subquery, no need to recursively\n\t\t * plan which is much more expensive than pushdown.\n\t\t */\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * AllDistributionKeysInSubqueryAreEqual is a wrapper function\n * for AllDistributionKeysInQueryAreEqual(). Here, we filter the\n * planner restrictions for the given subquery and do the restriction\n * equality checks on the filtered restriction.\n */\nstatic bool\nAllDistributionKeysInSubqueryAreEqual(Query *subquery,\n\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext *restrictionContext)\n{\n\t/* we don't support distribution eq. checks for CTEs yet */\n\tif (subquery->cteList != NIL)\n\t{\n\t\treturn false;\n\t}\n\n\tPlannerRestrictionContext *filteredRestrictionContext =\n\t\tFilterPlannerRestrictionForQuery(restrictionContext, subquery);\n\n\tbool allDistributionKeysInSubqueryAreEqual =\n\t\tAllDistributionKeysInQueryAreEqual(subquery, filteredRestrictionContext);\n\tif (!allDistributionKeysInSubqueryAreEqual)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ShouldRecursivelyPlanSetOperation determines whether the leaf queries of a\n * set operations tree need to be recursively planned in order to support the\n * query as a whole.\n */\nstatic bool\nShouldRecursivelyPlanSetOperation(Query *query, RecursivePlanningContext *context)\n{\n\tSetOperationStmt *setOperations = (SetOperationStmt *) query->setOperations;\n\tif (setOperations == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (context->level == 0)\n\t{\n\t\t/*\n\t\t * We cannot push down top-level set operation. Recursively plan the\n\t\t * leaf nodes such that it becomes a router query.\n\t\t */\n\t\treturn true;\n\t}\n\n\tif (setOperations->op != SETOP_UNION)\n\t{\n\t\t/*\n\t\t * We can only push down UNION operaionts, plan other set operations\n\t\t * recursively.\n\t\t */\n\t\treturn true;\n\t}\n\n\tif (DeferErrorIfUnsupportedUnionQuery(query) != NULL)\n\t{\n\t\t/*\n\t\t * If at least one leaf query in the union is recurring, then all\n\t\t * leaf nodes need to be recurring.\n\t\t */\n\t\treturn true;\n\t}\n\n\tPlannerRestrictionContext *filteredRestrictionContext =\n\t\tFilterPlannerRestrictionForQuery(context->plannerRestrictionContext, query);\n\tif (!SafeToPushdownUnionSubquery(query, filteredRestrictionContext))\n\t{\n\t\t/*\n\t\t * The distribution column is not in the same place in all sides\n\t\t * of the union, meaning we cannot determine distribution column\n\t\t * equivalence. Recursive planning is necessary.\n\t\t */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * RecursivelyPlanSetOperations descends into a tree of set operations\n * (e.g. UNION, INTERSECTS) and recursively plans all leaf nodes that\n * contain distributed tables.\n */\nstatic void\nRecursivelyPlanSetOperations(Query *query, Node *node,\n\t\t\t\t\t\t\t RecursivePlanningContext *context)\n{\n\tif (IsA(node, SetOperationStmt))\n\t{\n\t\tSetOperationStmt *setOperations = (SetOperationStmt *) node;\n\n\t\tRecursivelyPlanSetOperations(query, setOperations->larg, context);\n\t\tRecursivelyPlanSetOperations(query, setOperations->rarg, context);\n\t}\n\telse if (IsA(node, RangeTblRef))\n\t{\n\t\tRangeTblRef *rangeTableRef = (RangeTblRef *) node;\n\t\tRangeTblEntry *rangeTableEntry = rt_fetch(rangeTableRef->rtindex,\n\t\t\t\t\t\t\t\t\t\t\t\t  query->rtable);\n\t\tQuery *subquery = rangeTableEntry->subquery;\n\n\t\tif (rangeTableEntry->rtekind == RTE_SUBQUERY &&\n\t\t\tFindNodeMatchingCheckFunction((Node *) subquery, IsDistributedTableRTE))\n\t\t{\n\t\t\tRecursivelyPlanSubquery(subquery, context);\n\t\t}\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"unexpected node type (%d) while \"\n\t\t\t\t\t\t\t   \"expecting set operations or \"\n\t\t\t\t\t\t\t   \"range table references\", nodeTag(node))));\n\t}\n}\n\n\n/*\n * IsLocalTableRteOrMatView gets a node and returns true if the node is a range\n * table entry that points to a postgres local or citus local table or to a\n * materialized view.\n */\nstatic bool\nIsLocalTableRteOrMatView(Node *node)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (!IsA(node, RangeTblEntry))\n\t{\n\t\treturn false;\n\t}\n\n\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) node;\n\tif (rangeTableEntry->rtekind != RTE_RELATION)\n\t{\n\t\treturn false;\n\t}\n\n\tif (rangeTableEntry->relkind == RELKIND_VIEW)\n\t{\n\t\treturn false;\n\t}\n\n\tOid relationId = rangeTableEntry->relid;\n\treturn IsRelationLocalTableOrMatView(relationId);\n}\n\n\n/*\n * IsRelationLocalTableOrMatView returns true if the given relation\n * is a citus local, local, or materialized view.\n */\nbool\nIsRelationLocalTableOrMatView(Oid relationId)\n{\n\tif (!IsCitusTable(relationId))\n\t{\n\t\t/* postgres local table or a materialized view */\n\t\treturn true;\n\t}\n\telse if (IsCitusTableType(relationId, CITUS_LOCAL_TABLE))\n\t{\n\t\treturn true;\n\t}\n\n\t/* no local table found */\n\treturn false;\n}\n\n\n/*\n * RecursivelyPlanSubquery recursively plans a query, replaces it with a\n * result query and returns the subplan.\n *\n * Before we recursively plan the given subquery, we should ensure\n * that the subquery doesn't contain any references to the outer\n * queries (i.e., such queries cannot be separately planned). In\n * that case, the function doesn't recursively plan the input query\n * and immediately returns. Later, the planner decides on what to do\n * with the query.\n */\nstatic bool\nRecursivelyPlanSubquery(Query *subquery, RecursivePlanningContext *planningContext)\n{\n\tuint64 planId = planningContext->planId;\n\tQuery *debugQuery = NULL;\n\n\tif (ContainsReferencesToOuterQuery(subquery))\n\t{\n\t\telog(DEBUG2, \"skipping recursive planning for the subquery since it \"\n\t\t\t\t\t \"contains references to outer queries\");\n\n\t\treturn false;\n\t}\n\n\t/*\n\t * Subquery will go through the standard planner, thus to properly deparse it\n\t * we keep its copy: debugQuery.\n\t */\n\tif (IsLoggableLevel(DEBUG1))\n\t{\n\t\tdebugQuery = copyObject(subquery);\n\t}\n\n\n\t/*\n\t * Create the subplan and append it to the list in the planning context.\n\t */\n\tint subPlanId = list_length(planningContext->subPlanList) + 1;\n\n\tDistributedSubPlan *subPlan = CreateDistributedSubPlan(subPlanId, subquery);\n\tplanningContext->subPlanList = lappend(planningContext->subPlanList, subPlan);\n\n\t/* build the result_id parameter for the call to read_intermediate_result */\n\tchar *resultId = GenerateResultId(planId, subPlanId);\n\n\t/*\n\t * BuildSubPlanResultQuery() can optionally use provided column aliases.\n\t * We do not need to send additional alias list for subqueries.\n\t */\n\tQuery *resultQuery = BuildSubPlanResultQuery(subquery->targetList, NIL, resultId);\n\n\tif (IsLoggableLevel(DEBUG1))\n\t{\n\t\tStringInfo subqueryString = makeStringInfo();\n\n\t\tpg_get_query_def(debugQuery, subqueryString);\n\n\t\tereport(DEBUG1, (errmsg(\"generating subplan \" UINT64_FORMAT\n\t\t\t\t\t\t\t\t\"_%u for subquery %s\", planId, subPlanId,\n\t\t\t\t\t\t\t\tsubqueryString->data)));\n\t}\n\n\t/* finally update the input subquery to point the result query */\n\t*subquery = *resultQuery;\n\treturn true;\n}\n\n\n/*\n * CreateDistributedSubPlan creates a distributed subplan by recursively calling\n * the planner from the top, which may either generate a local plan or another\n * distributed plan, which can itself contain subplans.\n */\nstatic DistributedSubPlan *\nCreateDistributedSubPlan(uint32 subPlanId, Query *subPlanQuery)\n{\n\tint cursorOptions = 0;\n\n\tif (ContainsReadIntermediateResultFunction((Node *) subPlanQuery))\n\t{\n\t\t/*\n\t\t * Make sure we go through distributed planning if there are\n\t\t * read_intermediate_result calls, even if there are no distributed\n\t\t * tables in the query anymore.\n\t\t *\n\t\t * We cannot perform this check in the planner itself, since that\n\t\t * would also cause the workers to attempt distributed planning.\n\t\t */\n\t\tcursorOptions |= CURSOR_OPT_FORCE_DISTRIBUTED;\n\t}\n\n\tDistributedSubPlan *subPlan = CitusMakeNode(DistributedSubPlan);\n\tsubPlan->plan = planner(subPlanQuery, NULL, cursorOptions, NULL);\n\tsubPlan->subPlanId = subPlanId;\n\n\treturn subPlan;\n}\n\n\n/*\n * CteReferenceListWalker finds all references to CTEs in the top level of a query\n * and adds them to context->cteReferenceList.\n */\nstatic bool\nCteReferenceListWalker(Node *node, CteReferenceWalkerContext *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, RangeTblEntry))\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) node;\n\n\t\tif (rangeTableEntry->rtekind == RTE_CTE &&\n\t\t\trangeTableEntry->ctelevelsup == context->level)\n\t\t{\n\t\t\tcontext->cteReferenceList = lappend(context->cteReferenceList,\n\t\t\t\t\t\t\t\t\t\t\t\trangeTableEntry);\n\t\t}\n\n\t\t/* caller will descend into range table entry */\n\t\treturn false;\n\t}\n\telse if (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\n\t\tcontext->level += 1;\n\t\tquery_tree_walker(query, CteReferenceListWalker, context,\n\t\t\t\t\t\t  QTW_EXAMINE_RTES_BEFORE);\n\t\tcontext->level -= 1;\n\n\t\treturn false;\n\t}\n\telse\n\t{\n\t\treturn expression_tree_walker(node, CteReferenceListWalker, context);\n\t}\n}\n\n\n/*\n * ContainsReferencesToOuterQuery determines whether the given query contains\n * anything that points outside of the query itself. Such queries cannot be\n * planned recursively.\n */\nbool\nContainsReferencesToOuterQuery(Query *query)\n{\n\tVarLevelsUpWalkerContext context = { 0 };\n\tint flags = 0;\n\n\treturn query_tree_walker(query, ContainsReferencesToOuterQueryWalker,\n\t\t\t\t\t\t\t &context, flags);\n}\n\n\n/*\n * ContainsReferencesToOuterQueryWalker determines whether the given query\n * contains any Vars that point more than context->level levels up.\n *\n * ContainsReferencesToOuterQueryWalker recursively descends into subqueries\n * and increases the level by 1 before recursing.\n */\nstatic bool\nContainsReferencesToOuterQueryWalker(Node *node, VarLevelsUpWalkerContext *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Var))\n\t{\n\t\tif (((Var *) node)->varlevelsup > context->level)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\telse if (IsA(node, Aggref))\n\t{\n\t\tif (((Aggref *) node)->agglevelsup > context->level)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if (IsA(node, GroupingFunc))\n\t{\n\t\tif (((GroupingFunc *) node)->agglevelsup > context->level)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\telse if (IsA(node, PlaceHolderVar))\n\t{\n\t\tif (((PlaceHolderVar *) node)->phlevelsup > context->level)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\telse if (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\t\tint flags = 0;\n\n\t\tcontext->level += 1;\n\t\tbool found = query_tree_walker(query, ContainsReferencesToOuterQueryWalker,\n\t\t\t\t\t\t\t\t\t   context, flags);\n\t\tcontext->level -= 1;\n\n\t\treturn found;\n\t}\n\n\treturn expression_tree_walker(node, ContainsReferencesToOuterQueryWalker,\n\t\t\t\t\t\t\t\t  context);\n}\n\n\n/*\n * NodeContainsSubqueryReferencingOuterQuery determines whether the given node\n * contains anything that points outside of the query itself.\n */\nstatic bool\nNodeContainsSubqueryReferencingOuterQuery(Node *node)\n{\n\tList *sublinks = NIL;\n\tExtractSublinkWalker(node, &sublinks);\n\n\tSubLink *sublink;\n\tforeach_declared_ptr(sublink, sublinks)\n\t{\n\t\tif (ContainsReferencesToOuterQuery(castNode(Query, sublink->subselect)))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ReplaceRTERelationWithRteSubquery replaces the input rte relation target entry\n * with a subquery. The function also pushes down the filters to the subquery.\n *\n * It then recursively plans the subquery. This subquery is wrapped with another subquery\n * as a trick to reduce network cost, because we currently don't have an easy way to\n * skip generating NULL's for non-required columns, and if we create (SELECT a, NULL, NULL FROM table)\n * then this will be sent over network and NULL's also occupy some space. Instead of this we generate:\n * (SELECT t.a, NULL, NULL FROM (SELECT a FROM table) t). The inner subquery will be recursively planned\n * but the outer part will not be yet it will still have the NULL columns so that the query is correct.\n */\nvoid\nReplaceRTERelationWithRteSubquery(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t  List *requiredAttrNumbers,\n\t\t\t\t\t\t\t\t  RecursivePlanningContext *context,\n\t\t\t\t\t\t\t\t  RTEPermissionInfo *perminfo)\n{\n\tQuery *subquery = WrapRteRelationIntoSubquery(rangeTableEntry, requiredAttrNumbers,\n\t\t\t\t\t\t\t\t\t\t\t\t  perminfo);\n\tList *outerQueryTargetList = CreateAllTargetListForRelation(rangeTableEntry->relid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trequiredAttrNumbers);\n\n\tList *restrictionList =\n\t\tGetRestrictInfoListForRelation(rangeTableEntry,\n\t\t\t\t\t\t\t\t\t   context->plannerRestrictionContext);\n\tList *copyRestrictionList = copyObject(restrictionList);\n\tExpr *andedBoundExpressions = make_ands_explicit(copyRestrictionList);\n\tsubquery->jointree->quals = (Node *) andedBoundExpressions;\n\n\t/*\n\t * Originally the quals were pointing to the RTE and its varno\n\t * was pointing to its index in rtable. However now we converted the RTE\n\t * to a subquery and the quals should be pointing to that subquery, which\n\t * is the only RTE in its rtable, hence we update the varnos so that they\n\t * point to the subquery RTE.\n\t * Originally: rtable: [rte1, current_rte, rte3...]\n\t * Now: rtable: [rte1, subquery[current_rte], rte3...] --subquery[current_rte] refers to its rtable.\n\t */\n\tNode *quals = subquery->jointree->quals;\n\tUpdateVarNosInNode(quals, SINGLE_RTE_INDEX);\n\n\t/* replace the function with the constructed subquery */\n\trangeTableEntry->rtekind = RTE_SUBQUERY;\n\trangeTableEntry->perminfoindex = 0;\n\trangeTableEntry->subquery = subquery;\n\n\t/*\n\t * If the relation is inherited, it'll still be inherited as\n\t * we've copied it earlier. This is to prevent the newly created\n\t * subquery being treated as inherited.\n\t */\n\trangeTableEntry->inh = false;\n\n\tif (IsLoggableLevel(DEBUG1))\n\t{\n\t\tchar *relationAndAliasName = GetRelationNameAndAliasName(rangeTableEntry);\n\t\tereport(DEBUG1, (errmsg(\"Wrapping relation %s to a subquery\",\n\t\t\t\t\t\t\t\trelationAndAliasName)));\n\t}\n\n\t/* as we created the subquery, now forcefully recursively plan it */\n\tbool recursivelyPlanned = RecursivelyPlanSubquery(subquery, context);\n\tif (!recursivelyPlanned)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"unexpected state: query should have been recursively planned\")));\n\t}\n\n\tQuery *outerSubquery = CreateOuterSubquery(rangeTableEntry, outerQueryTargetList);\n\trangeTableEntry->subquery = outerSubquery;\n}\n\n\n/*\n * GetRelationNameAndAliasName returns the relname + alias name if\n * alias name exists otherwise only the relname is returned.\n */\nstatic char *\nGetRelationNameAndAliasName(RangeTblEntry *rangeTableEntry)\n{\n\tStringInfo str = makeStringInfo();\n\tappendStringInfo(str, \"\\\"%s\\\"\", get_rel_name(rangeTableEntry->relid));\n\n\tchar *aliasName = NULL;\n\tif (rangeTableEntry->alias)\n\t{\n\t\taliasName = rangeTableEntry->alias->aliasname;\n\t}\n\n\tif (aliasName)\n\t{\n\t\tappendStringInfo(str, \" \\\"%s\\\"\", aliasName);\n\t}\n\treturn str->data;\n}\n\n\n/*\n * CreateOuterSubquery creates outer subquery which contains\n * the given range table entry in its rtable.\n */\nstatic Query *\nCreateOuterSubquery(RangeTblEntry *rangeTableEntry, List *outerSubqueryTargetList)\n{\n\tList *innerSubqueryColNames = GenerateRequiredColNamesFromTargetList(\n\t\touterSubqueryTargetList);\n\n\tQuery *outerSubquery = makeNode(Query);\n\touterSubquery->commandType = CMD_SELECT;\n\n\t/* we copy the input rteRelation to preserve the rteIdentity */\n\tRangeTblEntry *innerSubqueryRTE = copyObject(rangeTableEntry);\n\n\tinnerSubqueryRTE->eref->colnames = innerSubqueryColNames;\n\touterSubquery->rtable = list_make1(innerSubqueryRTE);\n\n\t/* sanity check */\n\tAssert(innerSubqueryRTE->rtekind == RTE_SUBQUERY &&\n\t\t   innerSubqueryRTE->perminfoindex == 0);\n\touterSubquery->rteperminfos = NIL;\n\n\n\t/* set the FROM expression to the subquery */\n\tRangeTblRef *newRangeTableRef = makeNode(RangeTblRef);\n\tnewRangeTableRef->rtindex = 1;\n\touterSubquery->jointree = makeFromExpr(list_make1(newRangeTableRef), NULL);\n\n\touterSubquery->targetList = outerSubqueryTargetList;\n\treturn outerSubquery;\n}\n\n\n/*\n * GenerateRequiredColNamesFromTargetList generates the required colnames\n * from the given target list.\n */\nstatic List *\nGenerateRequiredColNamesFromTargetList(List *targetList)\n{\n\tTargetEntry *entry = NULL;\n\tList *innerSubqueryColNames = NIL;\n\tforeach_declared_ptr(entry, targetList)\n\t{\n\t\tif (IsA(entry->expr, Var))\n\t\t{\n\t\t\t/*\n\t\t\t * column names of the inner subquery should only contain the\n\t\t\t * required columns, as in if we choose 'b' from ('a','b') colnames\n\t\t\t * should be 'a' not ('a','b')\n\t\t\t */\n\t\t\tinnerSubqueryColNames = lappend(innerSubqueryColNames, makeString(\n\t\t\t\t\t\t\t\t\t\t\t\tentry->resname));\n\t\t}\n\t}\n\treturn innerSubqueryColNames;\n}\n\n\n/*\n * UpdateVarNosInNode iterates the Vars in the\n * given node and updates the varno's as the newVarNo.\n */\nvoid\nUpdateVarNosInNode(Node *node, Index newVarNo)\n{\n\tList *varList = pull_var_clause(node, PVC_RECURSE_AGGREGATES |\n\t\t\t\t\t\t\t\t\tPVC_RECURSE_PLACEHOLDERS);\n\tVar *var = NULL;\n\tforeach_declared_ptr(var, varList)\n\t{\n\t\tvar->varno = newVarNo;\n\t}\n}\n\n\n/*\n * IsRecursivelyPlannableRelation returns true if the given range table entry\n * is a relation type that can be converted to a subquery.\n */\nbool\nIsRecursivelyPlannableRelation(RangeTblEntry *rangeTableEntry)\n{\n\tif (rangeTableEntry->rtekind != RTE_RELATION)\n\t{\n\t\treturn false;\n\t}\n\treturn rangeTableEntry->relkind == RELKIND_PARTITIONED_TABLE ||\n\t\t   rangeTableEntry->relkind == RELKIND_RELATION ||\n\t\t   rangeTableEntry->relkind == RELKIND_MATVIEW ||\n\t\t   rangeTableEntry->relkind == RELKIND_FOREIGN_TABLE;\n}\n\n\n/*\n * ContainsLocalTableDistributedTableJoin returns true if the input range table list\n * contains a direct join between local RTE and an RTE that contains a distributed\n * or reference table.\n */\nbool\nContainsLocalTableDistributedTableJoin(List *rangeTableList)\n{\n\tbool containsLocalTable = false;\n\tbool containsDistributedTable = false;\n\n\tRangeTblEntry *rangeTableEntry = NULL;\n\tforeach_declared_ptr(rangeTableEntry, rangeTableList)\n\t{\n\t\tif (FindNodeMatchingCheckFunctionInRangeTableList(list_make1(rangeTableEntry),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  IsDistributedOrReferenceTableRTE))\n\t\t{\n\t\t\tcontainsDistributedTable = true;\n\t\t}\n\t\telse if (IsRecursivelyPlannableRelation(rangeTableEntry) &&\n\t\t\t\t IsLocalTableRteOrMatView((Node *) rangeTableEntry))\n\t\t{\n\t\t\t/* we consider citus local tables as local table */\n\t\t\tcontainsLocalTable = true;\n\t\t}\n\t}\n\n\treturn containsLocalTable && containsDistributedTable;\n}\n\n\n/*\n * WrapFunctionsInSubqueries iterates over all the immediate Range Table Entries\n * of a query and wraps the functions inside (SELECT * FROM fnc() f)\n * subqueries, so that those functions will be executed on the coordinator if\n * necessary.\n *\n * We wrap all the functions that are used in joins except the ones that are\n * laterally joined or have WITH ORDINALITY clauses.\n * */\nstatic void\nWrapFunctionsInSubqueries(Query *query)\n{\n\tList *rangeTableList = query->rtable;\n\tListCell *rangeTableCell = NULL;\n\n\t/*\n\t * If we have only one function call in a query without any joins, we can\n\t * easily decide where to execute it.\n\t *\n\t * If there are some subqueries and/or functions that are joined with a\n\t * function, it is not trivial to decide whether we should run this\n\t * function in the coordinator or in workers and therefore we may need to\n\t * wrap some of those functions in subqueries.\n\t *\n\t * If we have only one RTE, we leave the parsed query tree as it is. This\n\t * also makes sure we do not wrap an already wrapped function call\n\t * because we know that there will always be 1 RTE in a wrapped function.\n\t * */\n\tif (list_length(rangeTableList) < 2)\n\t{\n\t\treturn;\n\t}\n\n\t/* iterate over all RTEs and wrap them if necessary */\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\n\t\tif (ShouldTransformRTE(rangeTableEntry))\n\t\t{\n\t\t\tTransformFunctionRTE(rangeTableEntry);\n\t\t}\n\t}\n}\n\n\n/*\n * TransformFunctionRTE wraps a given function RangeTableEntry\n * inside a (SELECT * from function() f) subquery.\n *\n * The said RangeTableEntry is modified and now points to the new subquery.\n * */\nstatic void\nTransformFunctionRTE(RangeTblEntry *rangeTblEntry)\n{\n\tQuery *subquery = makeNode(Query);\n\tRangeTblRef *newRangeTableRef = makeNode(RangeTblRef);\n\tVar *targetColumn = NULL;\n\tTargetEntry *targetEntry = NULL;\n\tAttrNumber targetColumnIndex = 0;\n\n\tRangeTblFunction *rangeTblFunction = linitial(rangeTblEntry->functions);\n\n\tsubquery->commandType = CMD_SELECT;\n\n\t/* copy the input rangeTblEntry to prevent cycles */\n\tRangeTblEntry *newRangeTableEntry = copyObject(rangeTblEntry);\n\n\t/* set the FROM expression to the subquery */\n\tsubquery->rtable = list_make1(newRangeTableEntry);\n\n\t/* sanity check */\n\tAssert(newRangeTableEntry->rtekind == RTE_FUNCTION &&\n\t\t   newRangeTableEntry->perminfoindex == 0);\n\tsubquery->rteperminfos = NIL;\n\n\tnewRangeTableRef->rtindex = 1;\n\tsubquery->jointree = makeFromExpr(list_make1(newRangeTableRef), NULL);\n\n\t/* Determine the result type of the function.\n\t *\n\t * If function return type is not composite or rowtype can't be determined,\n\t * tupleDesc is set to null here\n\t */\n\tTupleDesc tupleDesc = (TupleDesc) get_expr_result_tupdesc(rangeTblFunction->funcexpr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true);\n\n\t/*\n\t * If tupleDesc is not null, we iterate over all the attributes and\n\t * create targetEntries\n\t * */\n\tif (tupleDesc)\n\t{\n\t\t/*\n\t\t * A sample function join that end up here:\n\t\t *\n\t\t * CREATE FUNCTION f(..) RETURNS TABLE(c1 int, c2 text) AS .. ;\n\t\t * SELECT .. FROM table JOIN f(..) ON ( .. ) ;\n\t\t *\n\t\t * We will iterate over Tuple Description attributes. i.e (c1 int, c2 text)\n\t\t */\n\t\tif (tupleDesc->natts > MaxAttrNumber)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"bad number of tuple descriptor attributes\")));\n\t\t}\n\t\tAttrNumber natts = tupleDesc->natts;\n\t\tfor (targetColumnIndex = 0; targetColumnIndex < natts;\n\t\t\t targetColumnIndex++)\n\t\t{\n\t\t\tFormData_pg_attribute *attribute = TupleDescAttr(tupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t targetColumnIndex);\n\t\t\tOid columnType = attribute->atttypid;\n\t\t\tchar *columnName = attribute->attname.data;\n\n\t\t\t/*\n\t\t\t * The indexing of attributes and TupleDesc and varattno differ\n\t\t\t *\n\t\t\t * varattno=0 corresponds to whole row\n\t\t\t * varattno=1 corresponds to first column that is stored in tupDesc->attrs[0]\n\t\t\t *\n\t\t\t * That's why we need to add one to the targetColumnIndex\n\t\t\t * */\n\t\t\ttargetColumn = makeVar(1, targetColumnIndex + 1, columnType, -1, InvalidOid,\n\t\t\t\t\t\t\t\t   0);\n\t\t\ttargetEntry = makeTargetEntry((Expr *) targetColumn, targetColumnIndex + 1,\n\t\t\t\t\t\t\t\t\t\t  columnName, false);\n\t\t\tsubquery->targetList = lappend(subquery->targetList, targetEntry);\n\t\t}\n\t}\n\n\t/*\n\t * If tupleDesc is NULL we have 2 different cases:\n\t *\n\t * 1. The function returns a record but the attributes can not be\n\t * determined just by looking at the function definition. In this case the\n\t * column names and types must be defined explicitly in the query\n\t *\n\t * 2. The function returns a non-composite type (e.g. int, text, jsonb ..)\n\t * */\n\telse\n\t{\n\t\t/* create target entries for all columns returned by the function */\n\t\tListCell *functionColumnName = NULL;\n\n\t\tList *functionColumnNames = rangeTblEntry->eref->colnames;\n\t\tforeach(functionColumnName, functionColumnNames)\n\t\t{\n\t\t\tchar *columnName = strVal(lfirst(functionColumnName));\n\t\t\tOid columnType = InvalidOid;\n\n\t\t\t/*\n\t\t\t * If the function returns a set of records, the query needs\n\t\t\t * to explicitly name column names and types\n\t\t\t *\n\t\t\t * Use explicitly defined types in the query if they are\n\t\t\t * available\n\t\t\t * */\n\t\t\tif (list_length(rangeTblFunction->funccoltypes) > 0)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * A sample function join that end up here:\n\t\t\t\t *\n\t\t\t\t * CREATE FUNCTION get_set_of_records() RETURNS SETOF RECORD AS\n\t\t\t\t * $cmd$\n\t\t\t\t * SELECT x, x+1 FROM generate_series(0,4) f(x)\n\t\t\t\t * $cmd$\n\t\t\t\t * LANGUAGE SQL;\n\t\t\t\t *\n\t\t\t\t * SELECT *\n\t\t\t\t * FROM table1 JOIN get_set_of_records() AS t2(x int, y int)\n\t\t\t\t * ON (id = x);\n\t\t\t\t *\n\t\t\t\t * Note that the function definition does not have column\n\t\t\t\t * names and types. Therefore the user needs to explicitly\n\t\t\t\t * state them in the query\n\t\t\t\t * */\n\t\t\t\tcolumnType = list_nth_oid(rangeTblFunction->funccoltypes,\n\t\t\t\t\t\t\t\t\t\t  targetColumnIndex);\n\t\t\t}\n\n\t\t\t/* use the types in the function definition otherwise */\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Only functions returning simple types end up here.\n\t\t\t\t * A sample function:\n\t\t\t\t *\n\t\t\t\t * CREATE FUNCTION add(integer, integer) RETURNS integer AS\n\t\t\t\t * 'SELECT $1 + $2;'\n\t\t\t\t * LANGUAGE SQL;\n\t\t\t\t * SELECT * FROM table JOIN add(3,5) sum ON ( .. ) ;\n\t\t\t\t * */\n\t\t\t\tFuncExpr *funcExpr = (FuncExpr *) rangeTblFunction->funcexpr;\n\t\t\t\tcolumnType = funcExpr->funcresulttype;\n\t\t\t}\n\n\t\t\t/* Note that the column k is associated with varattno/resno of k+1 */\n\t\t\ttargetColumn = makeVar(1, targetColumnIndex + 1, columnType, -1,\n\t\t\t\t\t\t\t\t   InvalidOid, 0);\n\t\t\ttargetEntry = makeTargetEntry((Expr *) targetColumn,\n\t\t\t\t\t\t\t\t\t\t  targetColumnIndex + 1, columnName, false);\n\t\t\tsubquery->targetList = lappend(subquery->targetList, targetEntry);\n\n\t\t\ttargetColumnIndex++;\n\t\t}\n\t}\n\n\t/* replace the function with the constructed subquery */\n\trangeTblEntry->rtekind = RTE_SUBQUERY;\n\trangeTblEntry->subquery = subquery;\n}\n\n\n/*\n * ShouldTransformRTE determines whether a given RTE should bne wrapped in a\n * subquery.\n *\n * Not all functions should be wrapped in a subquery for now. As we support more\n * functions to be used in joins, the constraints here will be relaxed.\n * */\nstatic bool\nShouldTransformRTE(RangeTblEntry *rangeTableEntry)\n{\n\t/*\n\t * We should wrap only function rtes that are not LATERAL and\n\t * without WITH ORDINALITY clause\n\t */\n\tif (rangeTableEntry->rtekind != RTE_FUNCTION ||\n\t\trangeTableEntry->lateral ||\n\t\trangeTableEntry->funcordinality)\n\t{\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n\n/*\n * BuildSubPlanResultQuery returns a query of the form:\n *\n * SELECT\n *   <target list>\n * FROM\n *   read_intermediate_result('<resultId>', '<copy format'>)\n *   AS res (<column definition list>);\n *\n * The caller can optionally supply a columnAliasList, which is useful for\n * CTEs that have column aliases.\n *\n * If any of the types in the target list cannot be used in the binary copy format,\n * then the copy format 'text' is used, otherwise 'binary' is used.\n */\nQuery *\nBuildSubPlanResultQuery(List *targetEntryList, List *columnAliasList, char *resultId)\n{\n\tOid functionOid = CitusReadIntermediateResultFuncId();\n\tbool useBinaryCopyFormat = CanUseBinaryCopyFormatForTargetList(targetEntryList);\n\n\tConst *resultIdConst = makeNode(Const);\n\tresultIdConst->consttype = TEXTOID;\n\tresultIdConst->consttypmod = -1;\n\tresultIdConst->constlen = -1;\n\tresultIdConst->constvalue = CStringGetTextDatum(resultId);\n\tresultIdConst->constbyval = false;\n\tresultIdConst->constisnull = false;\n\tresultIdConst->location = -1;\n\n\treturn BuildReadIntermediateResultsQuery(targetEntryList, columnAliasList,\n\t\t\t\t\t\t\t\t\t\t\t resultIdConst, functionOid,\n\t\t\t\t\t\t\t\t\t\t\t useBinaryCopyFormat);\n}\n\n\n/*\n * BuildReadIntermediateResultsArrayQuery returns a query of the form:\n *\n * SELECT\n *   <target list>\n * FROM\n *   read_intermediate_results(ARRAY['<resultId>', ...]::text[], '<copy format'>)\n *   AS res (<column definition list>);\n *\n * The caller can optionally supply a columnAliasList, which is useful for\n * CTEs that have column aliases.\n *\n * If useBinaryCopyFormat is true, then 'binary' format is used. Otherwise,\n * 'text' format is used.\n */\nQuery *\nBuildReadIntermediateResultsArrayQuery(List *targetEntryList,\n\t\t\t\t\t\t\t\t\t   List *columnAliasList,\n\t\t\t\t\t\t\t\t\t   List *resultIdList,\n\t\t\t\t\t\t\t\t\t   bool useBinaryCopyFormat)\n{\n\tOid functionOid = CitusReadIntermediateResultArrayFuncId();\n\n\tConst *resultIdConst = makeNode(Const);\n\tresultIdConst->consttype = TEXTARRAYOID;\n\tresultIdConst->consttypmod = -1;\n\tresultIdConst->constlen = -1;\n\tresultIdConst->constvalue = PointerGetDatum(strlist_to_textarray(resultIdList));\n\tresultIdConst->constbyval = false;\n\tresultIdConst->constisnull = false;\n\tresultIdConst->location = -1;\n\n\treturn BuildReadIntermediateResultsQuery(targetEntryList, columnAliasList,\n\t\t\t\t\t\t\t\t\t\t\t resultIdConst, functionOid,\n\t\t\t\t\t\t\t\t\t\t\t useBinaryCopyFormat);\n}\n\n\n/*\n * For the given target list, build an empty relation with the same target list.\n * For example, if the target list is (a, b, c), and resultId is \"empty\", then\n * it returns a Query object for this SQL:\n *      SELECT a, b, c FROM (VALUES (NULL, NULL, NULL)) AS empty(a, b, c) WHERE false;\n */\nQuery *\nBuildEmptyResultQuery(List *targetEntryList, char *resultId)\n{\n\tList *targetList = NIL;\n\tListCell *targetEntryCell = NULL;\n\n\tList *colTypes = NIL;\n\tList *colTypMods = NIL;\n\tList *colCollations = NIL;\n\tList *colNames = NIL;\n\n\tList *valueConsts = NIL;\n\tList *valueTargetList = NIL;\n\tList *valueColNames = NIL;\n\n\tint targetIndex = 1;\n\n\t/* build the target list and column lists needed */\n\tforeach(targetEntryCell, targetEntryList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell);\n\t\tNode *targetExpr = (Node *) targetEntry->expr;\n\t\tchar *columnName = targetEntry->resname;\n\t\tOid columnType = exprType(targetExpr);\n\t\tOid columnTypMod = exprTypmod(targetExpr);\n\t\tOid columnCollation = exprCollation(targetExpr);\n\n\t\tif (targetEntry->resjunk)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tVar *tgtVar = makeVar(1, targetIndex, columnType, columnTypMod, columnCollation,\n\t\t\t\t\t\t\t  0);\n\t\tTargetEntry *tgtEntry = makeTargetEntry((Expr *) tgtVar, targetIndex, columnName,\n\t\t\t\t\t\t\t\t\t\t\t\tfalse);\n\t\tConst *valueConst = makeConst(columnType, columnTypMod, columnCollation, 0,\n\t\t\t\t\t\t\t\t\t  (Datum) 0, true, false);\n\n\t\tStringInfoData *columnString = makeStringInfo();\n\t\tappendStringInfo(columnString, \"column%d\", targetIndex);\n\n\t\tTargetEntry *valueTgtEntry = makeTargetEntry((Expr *) tgtVar, targetIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t columnString->data, false);\n\n\t\tvalueConsts = lappend(valueConsts, valueConst);\n\t\tvalueTargetList = lappend(valueTargetList, valueTgtEntry);\n\t\tvalueColNames = lappend(valueColNames, makeString(columnString->data));\n\n\t\tcolNames = lappend(colNames, makeString(columnName));\n\t\tcolTypes = lappend_oid(colTypes, columnType);\n\t\tcolTypMods = lappend_oid(colTypMods, columnTypMod);\n\t\tcolCollations = lappend_oid(colCollations, columnCollation);\n\n\t\ttargetList = lappend(targetList, tgtEntry);\n\n\t\ttargetIndex++;\n\t}\n\n\t/* Build a RangeTable Entry for the VALUES relation */\n\tRangeTblEntry *valuesRangeTable = makeNode(RangeTblEntry);\n\tvaluesRangeTable->rtekind = RTE_VALUES;\n\tvaluesRangeTable->values_lists = list_make1(valueConsts);\n\tvaluesRangeTable->colcollations = colCollations;\n\tvaluesRangeTable->coltypes = colTypes;\n\tvaluesRangeTable->coltypmods = colTypMods;\n\tvaluesRangeTable->alias = NULL;\n\tvaluesRangeTable->eref = makeAlias(\"*VALUES*\", valueColNames);\n\tvaluesRangeTable->inFromCl = true;\n\n\tRangeTblRef *valuesRTRef = makeNode(RangeTblRef);\n\tvaluesRTRef->rtindex = 1;\n\n\tFromExpr *valuesJoinTree = makeNode(FromExpr);\n\tvaluesJoinTree->fromlist = list_make1(valuesRTRef);\n\n\t/* build the VALUES query */\n\tQuery *valuesQuery = makeNode(Query);\n\tvaluesQuery->canSetTag = true;\n\tvaluesQuery->commandType = CMD_SELECT;\n\tvaluesQuery->rtable = list_make1(valuesRangeTable);\n\tvaluesQuery->rteperminfos = NIL;\n\tvaluesQuery->jointree = valuesJoinTree;\n\tvaluesQuery->targetList = valueTargetList;\n\n\t/* build the relation selecting from the VALUES */\n\tRangeTblEntry *emptyRangeTable = makeNode(RangeTblEntry);\n\temptyRangeTable->rtekind = RTE_SUBQUERY;\n\temptyRangeTable->subquery = valuesQuery;\n\temptyRangeTable->alias = makeAlias(resultId, colNames);\n\temptyRangeTable->eref = emptyRangeTable->alias;\n\temptyRangeTable->inFromCl = true;\n\n\t/* build the SELECT query */\n\tQuery *resultQuery = makeNode(Query);\n\tresultQuery->commandType = CMD_SELECT;\n\tresultQuery->canSetTag = true;\n\tresultQuery->rtable = list_make1(emptyRangeTable);\n\tresultQuery->rteperminfos = NIL;\n\tRangeTblRef *rangeTableRef = makeNode(RangeTblRef);\n\trangeTableRef->rtindex = 1;\n\n\t/* insert a FALSE qual to ensure 0 rows returned */\n\tFromExpr *joinTree = makeNode(FromExpr);\n\tjoinTree->fromlist = list_make1(rangeTableRef);\n\tjoinTree->quals = makeBoolConst(false, false);\n\tresultQuery->jointree = joinTree;\n\tresultQuery->targetList = targetList;\n\n\treturn resultQuery;\n}\n\n\n/*\n * BuildReadIntermediateResultsQuery is the common code for generating\n * queries to read from result files. It is used by\n * BuildReadIntermediateResultsArrayQuery and BuildSubPlanResultQuery.\n */\nstatic Query *\nBuildReadIntermediateResultsQuery(List *targetEntryList, List *columnAliasList,\n\t\t\t\t\t\t\t\t  Const *resultIdConst, Oid functionOid,\n\t\t\t\t\t\t\t\t  bool useBinaryCopyFormat)\n{\n\tList *funcColNames = NIL;\n\tList *funcColTypes = NIL;\n\tList *funcColTypMods = NIL;\n\tList *funcColCollations = NIL;\n\tListCell *targetEntryCell = NULL;\n\tList *targetList = NIL;\n\tint columnNumber = 1;\n\tOid copyFormatId = BinaryCopyFormatId();\n\tint columnAliasCount = list_length(columnAliasList);\n\n\t/* build the target list and column definition list */\n\tforeach(targetEntryCell, targetEntryList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell);\n\t\tNode *targetExpr = (Node *) targetEntry->expr;\n\t\tchar *columnName = targetEntry->resname;\n\t\tOid columnType = exprType(targetExpr);\n\t\tOid columnTypMod = exprTypmod(targetExpr);\n\t\tOid columnCollation = exprCollation(targetExpr);\n\n\t\tif (targetEntry->resjunk)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tfuncColNames = lappend(funcColNames, makeString(columnName));\n\t\tfuncColTypes = lappend_int(funcColTypes, columnType);\n\t\tfuncColTypMods = lappend_int(funcColTypMods, columnTypMod);\n\t\tfuncColCollations = lappend_int(funcColCollations, columnCollation);\n\n\t\tVar *functionColumnVar = makeNode(Var);\n\t\tfunctionColumnVar->varno = 1;\n\t\tfunctionColumnVar->varattno = columnNumber;\n\t\tfunctionColumnVar->vartype = columnType;\n\t\tfunctionColumnVar->vartypmod = columnTypMod;\n\t\tfunctionColumnVar->varcollid = columnCollation;\n\t\tfunctionColumnVar->varlevelsup = 0;\n\t\tfunctionColumnVar->varnosyn = 1;\n\t\tfunctionColumnVar->varattnosyn = columnNumber;\n\t\tfunctionColumnVar->location = -1;\n\n\t\tTargetEntry *newTargetEntry = makeNode(TargetEntry);\n\t\tnewTargetEntry->expr = (Expr *) functionColumnVar;\n\t\tnewTargetEntry->resno = columnNumber;\n\n\t\t/*\n\t\t * Rename the column only if a column alias is defined.\n\t\t * Notice that column alias count could be less than actual\n\t\t * column count. We only use provided aliases and keep the\n\t\t * original column names if no alias is defined.\n\t\t */\n\t\tif (columnAliasCount >= columnNumber)\n\t\t{\n\t\t\tString *columnAlias = (String *) list_nth(columnAliasList, columnNumber - 1);\n\t\t\tAssert(IsA(columnAlias, String));\n\t\t\tnewTargetEntry->resname = strVal(columnAlias);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnewTargetEntry->resname = columnName;\n\t\t}\n\t\tnewTargetEntry->resjunk = false;\n\n\t\ttargetList = lappend(targetList, newTargetEntry);\n\n\t\tcolumnNumber++;\n\t}\n\n\t/* build the citus_copy_format parameter for the call to read_intermediate_result */\n\tif (!useBinaryCopyFormat)\n\t{\n\t\tcopyFormatId = TextCopyFormatId();\n\t}\n\n\tConst *resultFormatConst = makeNode(Const);\n\tresultFormatConst->consttype = CitusCopyFormatTypeId();\n\tresultFormatConst->consttypmod = -1;\n\tresultFormatConst->constlen = 4;\n\tresultFormatConst->constvalue = ObjectIdGetDatum(copyFormatId);\n\tresultFormatConst->constbyval = true;\n\tresultFormatConst->constisnull = false;\n\tresultFormatConst->location = -1;\n\n\t/* build the call to read_intermediate_result */\n\tFuncExpr *funcExpr = makeNode(FuncExpr);\n\tfuncExpr->funcid = functionOid;\n\tfuncExpr->funcretset = true;\n\tfuncExpr->funcvariadic = false;\n\tfuncExpr->funcformat = 0;\n\tfuncExpr->funccollid = 0;\n\tfuncExpr->inputcollid = 0;\n\tfuncExpr->location = -1;\n\tfuncExpr->args = list_make2(resultIdConst, resultFormatConst);\n\n\t/* build the RTE for the call to read_intermediate_result */\n\tRangeTblFunction *rangeTableFunction = makeNode(RangeTblFunction);\n\trangeTableFunction->funccolcount = list_length(funcColNames);\n\trangeTableFunction->funccolnames = funcColNames;\n\trangeTableFunction->funccoltypes = funcColTypes;\n\trangeTableFunction->funccoltypmods = funcColTypMods;\n\trangeTableFunction->funccolcollations = funcColCollations;\n\trangeTableFunction->funcparams = NULL;\n\trangeTableFunction->funcexpr = (Node *) funcExpr;\n\n\tAlias *funcAlias = makeNode(Alias);\n\tfuncAlias->aliasname = \"intermediate_result\";\n\tfuncAlias->colnames = funcColNames;\n\n\tRangeTblEntry *rangeTableEntry = makeNode(RangeTblEntry);\n\trangeTableEntry->rtekind = RTE_FUNCTION;\n\trangeTableEntry->functions = list_make1(rangeTableFunction);\n\trangeTableEntry->inFromCl = true;\n\trangeTableEntry->eref = funcAlias;\n\n\t/* build the join tree using the read_intermediate_result RTE */\n\tRangeTblRef *rangeTableRef = makeNode(RangeTblRef);\n\trangeTableRef->rtindex = 1;\n\n\tFromExpr *joinTree = makeNode(FromExpr);\n\tjoinTree->fromlist = list_make1(rangeTableRef);\n\n\t/* build the SELECT query */\n\tQuery *resultQuery = makeNode(Query);\n\tresultQuery->commandType = CMD_SELECT;\n\tresultQuery->rtable = list_make1(rangeTableEntry);\n\tresultQuery->rteperminfos = NIL;\n\tresultQuery->jointree = joinTree;\n\tresultQuery->targetList = targetList;\n\n\treturn resultQuery;\n}\n\n\n/*\n * GenerateResultId generates the result ID that is used to identify an intermediate\n * result of the subplan with the given plan ID and subplan ID.\n */\nchar *\nGenerateResultId(uint64 planId, uint32 subPlanId)\n{\n\tStringInfo resultId = makeStringInfo();\n\n\tappendStringInfo(resultId, UINT64_FORMAT \"_%u\", planId, subPlanId);\n\n\treturn resultId->data;\n}\n\n\n/*\n * GeneratingSubplans returns true if we are currently in the process of\n * generating subplans.\n */\nbool\nGeneratingSubplans(void)\n{\n\treturn recursivePlanningDepth > 0;\n}\n\n\n#if PG_VERSION_NUM < PG_VERSION_17\n\n/*\n * hasPseudoconstantQuals returns true if any of the planner infos in the\n * relation restriction list of the input relation restriction context\n * has a pseudoconstant qual\n */\nstatic bool\nhasPseudoconstantQuals(RelationRestrictionContext *relationRestrictionContext)\n{\n\tListCell *objectCell = NULL;\n\tforeach(objectCell, relationRestrictionContext->relationRestrictionList)\n\t{\n\t\tif (((RelationRestriction *) lfirst(\n\t\t\t\t objectCell))->plannerInfo->hasPseudoConstantQuals)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n\n#endif\n\n\n/*\n * CanPushdownRecurringOuterJoinOnOuterRTE returns true if the given range table entry\n * is safe for pushdown when it is the outer relation of a outer join when the\n * inner relation is not recurring.\n * Currently, we only allow reference tables.\n */\nstatic bool\nCanPushdownRecurringOuterJoinOnOuterRTE(RangeTblEntry *rte)\n{\n\tif (IsCitusTable(rte->relid) && IsCitusTableType(rte->relid, REFERENCE_TABLE))\n\t{\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\tereport(DEBUG5, (errmsg(\"RTE type %d is not safe for pushdown\",\n\t\t\t\t\t\t\t\trte->rtekind)));\n\t\treturn false;\n\t}\n}\n\n\n/*\n * ResolveBaseVarFromSubquery recursively resolves a Var from a subquery target list to\n * the base Var and RTE\n */\nbool\nResolveBaseVarFromSubquery(Var *var, Query *query,\n\t\t\t\t\t\t   Var **baseVar, RangeTblEntry **baseRte)\n{\n\tTargetEntry *tle = get_tle_by_resno(query->targetList, var->varattno);\n\tif (!tle || !IsA(tle->expr, Var))\n\t{\n\t\treturn false;\n\t}\n\n\tVar *tleVar = (Var *) tle->expr;\n\tRangeTblEntry *rte = rt_fetch(tleVar->varno, query->rtable);\n\n\tif (rte == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (rte->rtekind == RTE_RELATION || rte->rtekind == RTE_FUNCTION)\n\t{\n\t\t*baseVar = tleVar;\n\t\t*baseRte = rte;\n\t\treturn true;\n\t}\n\telse if (rte->rtekind == RTE_SUBQUERY)\n\t{\n\t\t/* Prevent overflow, and allow query cancellation */\n\t\tcheck_stack_depth();\n\t\tCHECK_FOR_INTERRUPTS();\n\t\treturn ResolveBaseVarFromSubquery(tleVar, rte->subquery, baseVar, baseRte);\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CanPushdownRecurringOuterJoinOnInnerVar checks if the inner variable\n * from a join qual for a join pushdown. It returns true if it is valid,\n * it is the partition column and hash distributed, otherwise it returns false.\n */\nstatic bool\nCanPushdownRecurringOuterJoinOnInnerVar(Var *innerVar, RangeTblEntry *rte)\n{\n\tif (!innerVar || !rte)\n\t{\n\t\treturn false;\n\t}\n\n\tif (innerVar->varattno == InvalidAttrNumber)\n\t{\n\t\treturn false;\n\t}\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(rte->relid);\n\n\tif (!cacheEntry || GetCitusTableType(cacheEntry) != HASH_DISTRIBUTED)\n\t{\n\t\treturn false;\n\t}\n\n\t/* Check if the inner variable is part of the distribution column */\n\tif (cacheEntry->partitionColumn &&\n\t\tinnerVar->varattno == cacheEntry->partitionColumn->varattno)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * JoinTreeContainsLateral checks if the given node contains a lateral\n * join. It returns true if it does, otherwise false.\n *\n * It recursively traverses the join tree and checks each RangeTblRef and JoinExpr\n * for lateral joins.\n */\nstatic bool\nJoinTreeContainsLateral(Node *node, List *rtable)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\t/* Prevent overflow, and allow query cancellation */\n\tcheck_stack_depth();\n\tCHECK_FOR_INTERRUPTS();\n\n\tif (IsA(node, RangeTblRef))\n\t{\n\t\tRangeTblEntry *rte = rt_fetch(((RangeTblRef *) node)->rtindex, rtable);\n\t\tif (rte == NULL)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tif (rte->lateral)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tif (rte->rtekind == RTE_SUBQUERY)\n\t\t{\n\t\t\tif (rte->subquery)\n\t\t\t{\n\t\t\t\treturn JoinTreeContainsLateral((Node *) rte->subquery->jointree,\n\t\t\t\t\t\t\t\t\t\t\t   rte->subquery->rtable);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\telse if (IsA(node, JoinExpr))\n\t{\n\t\tJoinExpr *join = (JoinExpr *) node;\n\t\treturn JoinTreeContainsLateral(join->larg, rtable) ||\n\t\t\t   JoinTreeContainsLateral(join->rarg, rtable);\n\t}\n\telse if (IsA(node, FromExpr))\n\t{\n\t\tFromExpr *fromExpr = (FromExpr *) node;\n\t\tListCell *lc = NULL;\n\t\tforeach(lc, fromExpr->fromlist)\n\t\t{\n\t\t\tif (JoinTreeContainsLateral((Node *) lfirst(lc), rtable))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\n\n/*\n * CanPushdownRecurringOuterJoinExtended checks if the given join expression\n * is an outer join between recurring rel -on outer part- and a distributed\n * rel -on the inner side- and if it is feasible to push down the join. If feasible,\n * it computes the outer relation's range table index, the outer relation's\n * range table entry, the inner (distributed) relation's range table entry, and the\n * attribute number of the partition column in the outer relation.\n */\nbool\nCanPushdownRecurringOuterJoinExtended(JoinExpr *joinExpr, Query *query,\n\t\t\t\t\t\t\t\t\t  int *outerRtIndex, RangeTblEntry **outerRte,\n\t\t\t\t\t\t\t\t\t  RangeTblEntry **distRte, int *attnum)\n{\n\tif (!EnableRecurringOuterJoinPushdown)\n\t{\n\t\treturn false;\n\t}\n\n\tif (!IS_OUTER_JOIN(joinExpr->jointype))\n\t{\n\t\treturn false;\n\t}\n\n\tif (joinExpr->jointype != JOIN_LEFT && joinExpr->jointype != JOIN_RIGHT)\n\t{\n\t\treturn false;\n\t}\n\n\t/* Push down for chained joins is not supported in this path. */\n\tif (IsA(joinExpr->rarg, JoinExpr) || IsA(joinExpr->larg, JoinExpr))\n\t{\n\t\tereport(DEBUG5, (errmsg(\n\t\t\t\t\t\t\t \"One side is a join expression, pushdown is not supported in this path.\")));\n\t\treturn false;\n\t}\n\n\t/* Push down for joins with fromExpr on one side is not supported in this path. */\n\tif (!IsA(joinExpr->larg, RangeTblRef) || !IsA(joinExpr->rarg, RangeTblRef))\n\t{\n\t\tereport(DEBUG5, (errmsg(\n\t\t\t\t\t\t\t \"One side is not a RangeTblRef, pushdown is not supported in this path.\")));\n\t\treturn false;\n\t}\n\n\tif (joinExpr->jointype == JOIN_LEFT)\n\t{\n\t\t*outerRtIndex = (((RangeTblRef *) joinExpr->larg)->rtindex);\n\t}\n\telse /* JOIN_RIGHT */\n\t{\n\t\t*outerRtIndex = (((RangeTblRef *) joinExpr->rarg)->rtindex);\n\t}\n\n\t*outerRte = rt_fetch(*outerRtIndex, query->rtable);\n\n\tif (!CanPushdownRecurringOuterJoinOnOuterRTE(*outerRte))\n\t{\n\t\treturn false;\n\t}\n\n\t/* For now if we see any lateral join in the join tree, we return false.\n\t * This check can be improved to support the cases where the lateral reference\n\t * does not cause an error in the final planner checks.\n\t */\n\tif (JoinTreeContainsLateral(joinExpr->rarg, query->rtable) || JoinTreeContainsLateral(\n\t\t\tjoinExpr->larg, query->rtable))\n\t{\n\t\tereport(DEBUG5, (errmsg(\"Lateral join is not supported for pushdown \"\n\t\t\t\t\t\t\t\t\"in this path.\")));\n\t\treturn false;\n\t}\n\n\t/* Check if the join is performed on the distribution column */\n\tList *joinClauseList = make_ands_implicit((Expr *) joinExpr->quals);\n\tif (joinClauseList == NIL)\n\t{\n\t\treturn false;\n\t}\n\n\tNode *joinClause = NULL;\n\tforeach_declared_ptr(joinClause, joinClauseList)\n\t{\n\t\tif (!NodeIsEqualsOpExpr(joinClause))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tOpExpr *joinClauseExpr = castNode(OpExpr, joinClause);\n\n\t\tVar *leftColumn = LeftColumnOrNULL(joinClauseExpr);\n\t\tVar *rightColumn = RightColumnOrNULL(joinClauseExpr);\n\t\tif (leftColumn == NULL || rightColumn == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tRangeTblEntry *rte;\n\t\tVar *innerVar;\n\t\tif (leftColumn->varno == *outerRtIndex)\n\t\t{\n\t\t\t/* left column is the outer table of the comparison, get right */\n\t\t\trte = rt_fetch(rightColumn->varno, query->rtable);\n\t\t\tinnerVar = rightColumn;\n\n\t\t\t/* additional constraints will be introduced on outer relation variable */\n\t\t\t*attnum = leftColumn->varattno;\n\t\t}\n\t\telse if (rightColumn->varno == *outerRtIndex)\n\t\t{\n\t\t\t/* right column is the outer table of the comparison, get left*/\n\t\t\trte = rt_fetch(leftColumn->varno, query->rtable);\n\t\t\tinnerVar = leftColumn;\n\n\t\t\t/* additional constraints will be introduced on outer relation variable */\n\t\t\t*attnum = rightColumn->varattno;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* the simple case, the inner table itself a Citus table */\n\t\tif (rte && IsCitusTable(rte->relid))\n\t\t{\n\t\t\tif (CanPushdownRecurringOuterJoinOnInnerVar(innerVar, rte))\n\t\t\t{\n\t\t\t\t*distRte = rte;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\t/* the inner table is a subquery, extract the base relation referred in the qual */\n\t\telse if (rte && rte->rtekind == RTE_SUBQUERY)\n\t\t{\n\t\t\tVar *baseVar = NULL;\n\t\t\tRangeTblEntry *baseRte = NULL;\n\n\t\t\tif (ResolveBaseVarFromSubquery(innerVar, rte->subquery, &baseVar, &baseRte))\n\t\t\t{\n\t\t\t\tif (baseRte && IsCitusTable(baseRte->relid))\n\t\t\t\t{\n\t\t\t\t\tif (CanPushdownRecurringOuterJoinOnInnerVar(baseVar, baseRte))\n\t\t\t\t\t{\n\t\t\t\t\t\t*distRte = baseRte;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CanPushdownRecurringOuterJoin initializes input variables to call\n * CanPushdownRecurringOuterJoinExtended.\n * See CanPushdownRecurringOuterJoinExtended for more details.\n */\nbool\nCanPushdownRecurringOuterJoin(JoinExpr *joinExpr, Query *query)\n{\n\tint outerRtIndex;\n\tRangeTblEntry *outerRte = NULL;\n\tRangeTblEntry *innerRte = NULL;\n\tint attnum;\n\treturn CanPushdownRecurringOuterJoinExtended(joinExpr, query, &outerRtIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t &outerRte, &innerRte, &attnum);\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/relation_restriction_equivalence.c",
    "content": "/*\n * relation_restriction_equivalence.c\n *\n * This file contains functions helper functions for planning\n * queries with colocated tables and subqueries.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/pathnodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/pathnode.h\"\n#include \"optimizer/paths.h\"\n#include \"parser/parsetree.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/relation_restriction_equivalence.h\"\n#include \"distributed/shard_pruning.h\"\n\n\nstatic uint32 AttributeEquivalenceId = 1;\n\n\n/*\n * AttributeEquivalenceClass\n *\n * Whenever we find an equality clause A = B, where both A and B originates from\n * relation attributes (i.e., not random expressions), we create an\n * AttributeEquivalenceClass to record this knowledge. If we later find another\n * equivalence B = C, we create another AttributeEquivalenceClass. Finally, we can\n * apply transitivity rules and generate a new AttributeEquivalenceClass which includes\n * A, B and C.\n *\n * Note that equality among the members are identified by the varattno and rteIdentity.\n */\ntypedef struct AttributeEquivalenceClass\n{\n\tuint32 equivalenceId;\n\tList *equivalentAttributes;\n\n\tIndex unionQueryPartitionKeyIndex;\n} AttributeEquivalenceClass;\n\ntypedef struct FindQueryContainingRteIdentityContext\n{\n\tint targetRTEIdentity;\n\tQuery *query;\n}FindQueryContainingRteIdentityContext;\n\n/*\n *  AttributeEquivalenceClassMember - one member expression of an\n *  AttributeEquivalenceClass. The important thing to consider is that\n *  the class member contains \"rteIndentity\" field. Note that each RTE_RELATION\n *  is assigned a unique rteIdentity in AssignRTEIdentities() function.\n *\n *  \"varno\" and \"varattno\" is directly used from a Var clause that is being added\n *  to the attribute equivalence. Since we only use this class for relations, the member\n *  also includes the relation id field.\n */\ntypedef struct AttributeEquivalenceClassMember\n{\n\tOid relationId;\n\tint rteIdentity;\n\tIndex varno;\n\tAttrNumber varattno;\n} AttributeEquivalenceClassMember;\n\n/*\n * ECGroupByExpr\n * Helper structure to group EquivalenceClasses by their non-Var expressions.\n */\ntypedef struct ECGroupByExpr\n{\n\tNode *strippedExpr;           /* The canonical non-Var expression (stripped) */\n\tList *ecsWithThisExpr;        /* List of EquivalenceClass* sharing this expr */\n\tList *varsInTheseECs;         /* Cached list of Var* from all these ECs */\n} ECGroupByExpr;\n\nstatic bool ContextContainsLocalRelation(RelationRestrictionContext *restrictionContext);\nstatic bool ContextContainsAppendRelation(RelationRestrictionContext *restrictionContext);\nstatic int RangeTableOffsetCompat(PlannerInfo *root, AppendRelInfo *appendRelInfo);\nstatic Var * FindUnionAllVar(PlannerInfo *root, List *translatedVars, Oid relationOid,\n\t\t\t\t\t\t\t Index relationRteIndex, Index *partitionKeyIndex);\nstatic bool ContainsMultipleDistributedRelations(PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\nstatic List * GenerateAttributeEquivalencesForRelationRestrictions(\n\tRelationRestrictionContext *restrictionContext);\nstatic List * MergeEquivalenceClassesWithSameFunctions(RelationRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t   restrictionContext);\nstatic AttributeEquivalenceClass * AttributeEquivalenceClassForEquivalenceClass(\n\tEquivalenceClass *plannerEqClass, RelationRestriction *relationRestriction);\nstatic void AddToAttributeEquivalenceClass(AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t   attributeEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t   PlannerInfo *root, Var *varToBeAdded);\nstatic void AddRteSubqueryToAttributeEquivalenceClass(AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  attributeEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  RangeTblEntry *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  PlannerInfo *root,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  Var *varToBeAdded);\nstatic Query * GetTargetSubquery(PlannerInfo *root, RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t Var *varToBeAdded);\nstatic void AddUnionAllSetOperationsToAttributeEquivalenceClass(\n\tAttributeEquivalenceClass *\n\tattributeEquivalenceClass,\n\tPlannerInfo *root,\n\tVar *varToBeAdded);\nstatic void AddUnionSetOperationsToAttributeEquivalenceClass(AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t attributeEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t PlannerInfo *root,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t SetOperationStmt *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t setOperation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Var *varToBeAdded);\nstatic void AddRteRelationToAttributeEquivalenceClass(AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  attrEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  Var *varToBeAdded);\nstatic Var * GetVarFromAssignedParam(List *outerPlanParamsList, Param *plannerParam,\n\t\t\t\t\t\t\t\t\t PlannerInfo **rootContainingVar);\nstatic Var * SearchPlannerParamList(List *plannerParamList, Param *plannerParam);\nstatic List * GenerateAttributeEquivalencesForJoinRestrictions(JoinRestrictionContext\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   *joinRestrictionContext);\nstatic bool AttributeClassContainsAttributeClassMember(AttributeEquivalenceClassMember *\n\t\t\t\t\t\t\t\t\t\t\t\t\t   inputMember,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t\t\t\t   attributeEquivalenceClass);\nstatic List * AddAttributeClassToAttributeClassList(List *attributeEquivalenceList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tAttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t\t\t\tattributeEquivalence);\nstatic bool AttributeEquivalencesAreEqual(AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t  firstAttributeEquivalence,\n\t\t\t\t\t\t\t\t\t\t  AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t  secondAttributeEquivalence);\nstatic AttributeEquivalenceClass * GenerateCommonEquivalence(List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t attributeEquivalenceList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t RelationRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t relationRestrictionContext);\nstatic AttributeEquivalenceClass * GenerateEquivalenceClassForRelationRestriction(\n\tRelationRestrictionContext\n\t*\n\trelationRestrictionContext);\nstatic void ListConcatUniqueAttributeClassMemberLists(AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  firstClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  secondClass);\nstatic Var * PartitionKeyForRTEIdentityInQuery(Query *query, int targetRTEIndex,\n\t\t\t\t\t\t\t\t\t\t\t   Index *partitionKeyIndex);\nstatic bool AllDistributedRelationsInRestrictionContextColocated(\n\tRelationRestrictionContext *\n\trestrictionContext);\nstatic bool IsNotSafeRestrictionToRecursivelyPlan(Node *node);\nstatic bool HasPlaceHolderVar(Node *node);\nstatic JoinRestrictionContext * FilterJoinRestrictionContext(JoinRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t joinRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Relids\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t queryRteIdentities);\nstatic bool RangeTableArrayContainsAnyRTEIdentities(RangeTblEntry **rangeTableEntries, int\n\t\t\t\t\t\t\t\t\t\t\t\t\trangeTableArrayLength, Relids\n\t\t\t\t\t\t\t\t\t\t\t\t\tqueryRteIdentities);\nstatic Relids QueryRteIdentities(Query *queryTree);\n\nstatic Query * FindQueryContainingRTEIdentity(Query *mainQuery, int rteIndex);\nstatic bool FindQueryContainingRTEIdentityInternal(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t   FindQueryContainingRteIdentityContext *\n\t\t\t\t\t\t\t\t\t\t\t\t   context);\n\nstatic int ParentCountPriorToAppendRel(List *appendRelList, AppendRelInfo *appendRelInfo);\n\nstatic bool PartitionColumnSelectedForOuterJoin(Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\tRelationRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\trestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\tJoinRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\tjoinRestrictionContext);\n\nstatic bool PartitionColumnIsInTargetList(Query *query, JoinRestriction *joinRestriction,\n\t\t\t\t\t\t\t\t\t\t  RelationRestrictionContext *restrictionContext);\n\n/*\n * AllDistributionKeysInQueryAreEqual returns true if either\n *    (i)  there exists join in the query and all relations joined on their\n *         partition keys\n *    (ii) there exists only union set operations and all relations has\n *         partition keys in the same ordinal position in the query\n */\nbool\nAllDistributionKeysInQueryAreEqual(Query *originalQuery,\n\t\t\t\t\t\t\t\t   PlannerRestrictionContext *plannerRestrictionContext)\n{\n\t/* we don't support distribution key equality checks for CTEs yet */\n\tif (originalQuery->cteList != NIL)\n\t{\n\t\treturn false;\n\t}\n\n\t/* we don't support distribution key equality checks for local tables */\n\tRelationRestrictionContext *restrictionContext =\n\t\tplannerRestrictionContext->relationRestrictionContext;\n\tif (ContextContainsLocalRelation(restrictionContext))\n\t{\n\t\treturn false;\n\t}\n\n\tbool restrictionEquivalenceForPartitionKeys =\n\t\tRestrictionEquivalenceForPartitionKeys(plannerRestrictionContext);\n\tif (restrictionEquivalenceForPartitionKeys)\n\t{\n\t\treturn true;\n\t}\n\n\tif (originalQuery->setOperations || ContainsUnionSubquery(originalQuery))\n\t{\n\t\treturn SafeToPushdownUnionSubquery(originalQuery, plannerRestrictionContext);\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ContextContainsLocalRelation determines whether the given\n * RelationRestrictionContext contains any local tables.\n */\nstatic bool\nContextContainsLocalRelation(RelationRestrictionContext *restrictionContext)\n{\n\tListCell *relationRestrictionCell = NULL;\n\n\tforeach(relationRestrictionCell, restrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction = lfirst(relationRestrictionCell);\n\n\t\tif (!relationRestriction->citusTable)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ContextContainsAppendRelation determines whether the given\n * RelationRestrictionContext contains any append-distributed tables.\n */\nstatic bool\nContextContainsAppendRelation(RelationRestrictionContext *restrictionContext)\n{\n\tListCell *relationRestrictionCell = NULL;\n\n\tforeach(relationRestrictionCell, restrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction = lfirst(relationRestrictionCell);\n\n\t\tif (IsCitusTableType(relationRestriction->relationId, APPEND_DISTRIBUTED))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * SafeToPushdownUnionSubquery returns true if all the relations are returns\n * partition keys in the same ordinal position and there is no reference table\n * exists.\n *\n * Note that the function expects (and asserts) the input query to be a top\n * level union query defined by TopLevelUnionQuery().\n *\n * Lastly, the function fails to produce correct output if the target lists contains\n * multiple partition keys on the target list such as the following:\n *\n *   select count(*) from (\n *       select user_id, user_id from users_table\n *   union\n *       select 2, user_id from users_table) u;\n *\n * For the above query, although the second item in the target list make this query\n * safe to push down, the function would fail to return true.\n */\nbool\nSafeToPushdownUnionSubquery(Query *originalQuery,\n\t\t\t\t\t\t\tPlannerRestrictionContext *plannerRestrictionContext)\n{\n\tRelationRestrictionContext *restrictionContext =\n\t\tplannerRestrictionContext->relationRestrictionContext;\n\tJoinRestrictionContext *joinRestrictionContext =\n\t\tplannerRestrictionContext->joinRestrictionContext;\n\n\tAttributeEquivalenceClass *attributeEquivalence =\n\t\tpalloc0(sizeof(AttributeEquivalenceClass));\n\tListCell *relationRestrictionCell = NULL;\n\n\tattributeEquivalence->equivalenceId = AttributeEquivalenceId++;\n\n\t/*\n\t * Ensure that the partition column is in the same place across all\n\t * leaf queries in the UNION and construct an equivalence class for\n\t * these columns.\n\t */\n\tforeach(relationRestrictionCell, restrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction = lfirst(relationRestrictionCell);\n\t\tIndex partitionKeyIndex = InvalidAttrNumber;\n\t\tPlannerInfo *relationPlannerRoot = relationRestriction->plannerInfo;\n\n\t\tint targetRTEIndex = GetRTEIdentity(relationRestriction->rte);\n\t\tVar *varToBeAdded =\n\t\t\tPartitionKeyForRTEIdentityInQuery(originalQuery, targetRTEIndex,\n\t\t\t\t\t\t\t\t\t\t\t  &partitionKeyIndex);\n\n\t\t/* union does not have partition key in the target list */\n\t\tif (partitionKeyIndex == 0)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * This should never happen but to be on the safe side, we have this\n\t\t */\n\t\tif (relationPlannerRoot->simple_rel_array_size < relationRestriction->index)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * We update the varno because we use the original parse tree for finding the\n\t\t * var. However the rest of the code relies on a query tree that might be different\n\t\t * than the original parse tree because of postgres optimizations.\n\t\t * That's why we update the varno to reflect the rteIndex in the modified query tree.\n\t\t */\n\t\tvarToBeAdded->varno = relationRestriction->index;\n\n\n\t\t/*\n\t\t * The current relation does not have its partition key in the target list.\n\t\t */\n\t\tif (partitionKeyIndex == InvalidAttrNumber)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * We find the first relations partition key index in the target list. Later,\n\t\t * we check whether all the relations have partition keys in the\n\t\t * same position.\n\t\t */\n\t\tif (attributeEquivalence->unionQueryPartitionKeyIndex == InvalidAttrNumber)\n\t\t{\n\t\t\tattributeEquivalence->unionQueryPartitionKeyIndex = partitionKeyIndex;\n\t\t}\n\t\telse if (attributeEquivalence->unionQueryPartitionKeyIndex != partitionKeyIndex)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tAssert(varToBeAdded != NULL);\n\t\tAddToAttributeEquivalenceClass(attributeEquivalence, relationPlannerRoot,\n\t\t\t\t\t\t\t\t\t   varToBeAdded);\n\t}\n\n\t/*\n\t * For queries of the form:\n\t * (SELECT ... FROM a JOIN b ...) UNION (SELECT .. FROM c JOIN d ... )\n\t *\n\t * we determine whether all relations are joined on the partition column\n\t * by adding the equivalence classes that can be inferred from joins.\n\t */\n\tList *relationRestrictionAttributeEquivalenceList =\n\t\tGenerateAttributeEquivalencesForRelationRestrictions(restrictionContext);\n\tList *joinRestrictionAttributeEquivalenceList =\n\t\tGenerateAttributeEquivalencesForJoinRestrictions(joinRestrictionContext);\n\n\tList *allAttributeEquivalenceList =\n\t\tlist_concat(relationRestrictionAttributeEquivalenceList,\n\t\t\t\t\tjoinRestrictionAttributeEquivalenceList);\n\n\tallAttributeEquivalenceList = lappend(allAttributeEquivalenceList,\n\t\t\t\t\t\t\t\t\t\t  attributeEquivalence);\n\n\tif (!EquivalenceListContainsRelationsEquality(allAttributeEquivalenceList,\n\t\t\t\t\t\t\t\t\t\t\t\t  restrictionContext))\n\t{\n\t\t/* cannot confirm equality for all distribution colums */\n\t\treturn false;\n\t}\n\n\tif (!AllDistributedRelationsInRestrictionContextColocated(restrictionContext))\n\t{\n\t\t/* distribution columns are equal, but tables are not co-located */\n\t\treturn false;\n\t}\n\n\tif (!PartitionColumnSelectedForOuterJoin(originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t restrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t joinRestrictionContext))\n\t{\n\t\t/* outer join does not select partition column of outer relation */\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n\n/*\n * PartitionColumnSelectedForOuterJoin checks whether the partition column of\n * the outer relation is selected in the target list of the query.\n *\n * If there is no outer join, it returns true.\n */\nstatic bool\nPartitionColumnSelectedForOuterJoin(Query *query,\n\t\t\t\t\t\t\t\t\tRelationRestrictionContext *restrictionContext,\n\t\t\t\t\t\t\t\t\tJoinRestrictionContext *joinRestrictionContext)\n{\n\tListCell *joinRestrictionCell;\n\tforeach(joinRestrictionCell, joinRestrictionContext->joinRestrictionList)\n\t{\n\t\tJoinRestriction *joinRestriction = (JoinRestriction *) lfirst(\n\t\t\tjoinRestrictionCell);\n\n\t\t/* Restriction context includes alternative plans, sufficient to check for left joins.*/\n\t\tif (joinRestriction->joinType != JOIN_LEFT)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!PartitionColumnIsInTargetList(query, joinRestriction, restrictionContext))\n\t\t{\n\t\t\t/* outer join does not select partition column of outer relation */\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * PartitionColumnIsInTargetList checks whether the partition column of\n * the given relation is included in the target list of the query.\n */\nstatic bool\nPartitionColumnIsInTargetList(Query *query, JoinRestriction *joinRestriction,\n\t\t\t\t\t\t\t  RelationRestrictionContext *restrictionContext)\n{\n\tRelids relids = joinRestriction->outerrelRelids;\n\tint relationId = -1;\n\tIndex partitionKeyIndex = InvalidAttrNumber;\n\twhile ((relationId = bms_next_member(relids, relationId)) >= 0)\n\t{\n\t\tRangeTblEntry *rte = joinRestriction->plannerInfo->simple_rte_array[relationId];\n\t\tif (rte->rtekind != RTE_RELATION)\n\t\t{\n\t\t\t/* skip if it is not a relation */\n\t\t\tcontinue;\n\t\t}\n\t\tint targetRTEIndex = GetRTEIdentity(rte);\n\t\tPartitionKeyForRTEIdentityInQuery(query, targetRTEIndex,\n\t\t\t\t\t\t\t\t\t\t  &partitionKeyIndex);\n\t\tif (partitionKeyIndex == 0)\n\t\t{\n\t\t\t/* partition key is not in the target list */\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * RangeTableOffsetCompat returns the range table offset(in glob->finalrtable) for the appendRelInfo.\n */\nstatic int\nRangeTableOffsetCompat(PlannerInfo *root, AppendRelInfo *appendRelInfo)\n{\n\tint parentCount = ParentCountPriorToAppendRel(root->append_rel_list, appendRelInfo);\n\tint skipParentCount = parentCount - 1;\n\n\tint i = 1;\n\tfor (; i < root->simple_rel_array_size; i++)\n\t{\n\t\tRangeTblEntry *rte = root->simple_rte_array[i];\n\t\tif (rte->inh)\n\t\t{\n\t\t\t/*\n\t\t\t * We skip the previous parents because we want to find the offset\n\t\t\t * for the given append rel info.\n\t\t\t */\n\t\t\tif (skipParentCount > 0)\n\t\t\t{\n\t\t\t\tskipParentCount--;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\tint indexInRtable = (i - 1);\n\n\t/*\n\t * Postgres adds the global rte array size to parent_relid as an offset.\n\t * Here we do the reverse operation: Commit on postgres side:\n\t * 6ef77cf46e81f45716ec981cb08781d426181378\n\t */\n\tint parentRelIndex = appendRelInfo->parent_relid - 1;\n\treturn parentRelIndex - indexInRtable;\n}\n\n\n/*\n * FindUnionAllVar finds the variable used in union all for the side that has\n * relationRteIndex as its index and the same varattno as the partition key of\n * the given relation with relationOid.\n */\nstatic Var *\nFindUnionAllVar(PlannerInfo *root, List *translatedVars, Oid relationOid,\n\t\t\t\tIndex relationRteIndex, Index *partitionKeyIndex)\n{\n\tif (!IsCitusTableType(relationOid, STRICTLY_PARTITIONED_DISTRIBUTED_TABLE))\n\t{\n\t\t/* we only care about hash and range partitioned tables */\n\t\t*partitionKeyIndex = 0;\n\t\treturn NULL;\n\t}\n\n\tVar *relationPartitionKey = DistPartitionKeyOrError(relationOid);\n\n\tAttrNumber childAttrNumber = 0;\n\t*partitionKeyIndex = 0;\n\tListCell *translatedVarCell;\n\tforeach(translatedVarCell, translatedVars)\n\t{\n\t\tNode *targetNode = (Node *) lfirst(translatedVarCell);\n\t\tchildAttrNumber++;\n\n\t\tif (!IsA(targetNode, Var))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tVar *targetVar = (Var *) lfirst(translatedVarCell);\n\t\tif (targetVar->varno == relationRteIndex &&\n\t\t\ttargetVar->varattno == relationPartitionKey->varattno)\n\t\t{\n\t\t\t*partitionKeyIndex = childAttrNumber;\n\n\t\t\treturn targetVar;\n\t\t}\n\t}\n\treturn NULL;\n}\n\n\n/*\n * RestrictionEquivalenceForPartitionKeys aims to deduce whether each of the RTE_RELATION\n * is joined with at least one another RTE_RELATION on their partition keys. If each\n * RTE_RELATION follows the above rule, we can conclude that all RTE_RELATIONs are\n * joined on their partition keys.\n *\n * Before doing the expensive equality checks, we do a cheaper check to understand\n * whether there are more than one distributed relations. Otherwise, we exit early.\n *\n * The function returns true if all relations are joined on their partition keys.\n * Otherwise, the function returns false. We ignore reference tables at all since\n * they don't have partition keys.\n *\n * In order to do that, we invented a new equivalence class namely:\n * AttributeEquivalenceClass. In very simple words, a AttributeEquivalenceClass is\n * identified by an unique id and consists of a list of AttributeEquivalenceMembers.\n *\n * Each AttributeEquivalenceMember is designed to identify attributes uniquely within the\n * whole query. The necessity of this arise since varno attributes are defined within\n * a single level of a query. Instead, here we want to identify each RTE_RELATION uniquely\n * and try to find equality among each RTE_RELATION's partition key.\n *\n * Each equality among RTE_RELATION is saved using an AttributeEquivalenceClass where\n * each member attribute is identified by a AttributeEquivalenceMember. In the final\n * step, we try generate a common attribute equivalence class that holds as much as\n * AttributeEquivalenceMembers whose attributes are a partition keys.\n *\n * RestrictionEquivalenceForPartitionKeys uses both relation restrictions and join restrictions\n * to find as much as information that Postgres planner provides to extensions. For the\n * details of the usage, please see GenerateAttributeEquivalencesForRelationRestrictions()\n * and GenerateAttributeEquivalencesForJoinRestrictions().\n */\nbool\nRestrictionEquivalenceForPartitionKeys(PlannerRestrictionContext *restrictionContext)\n{\n\tif (ContextContainsLocalRelation(restrictionContext->relationRestrictionContext))\n\t{\n\t\treturn false;\n\t}\n\telse if (!ContainsMultipleDistributedRelations(restrictionContext))\n\t{\n\t\t/* there is a single distributed relation, no need to continue */\n\t\treturn true;\n\t}\n\telse if (ContextContainsAppendRelation(\n\t\t\t\t restrictionContext->relationRestrictionContext))\n\t{\n\t\t/* we never consider append-distributed tables co-located */\n\t\treturn false;\n\t}\n\n\tList *attributeEquivalenceList = GenerateAllAttributeEquivalences(restrictionContext);\n\n\treturn RestrictionEquivalenceForPartitionKeysViaEquivalences(\n\t\trestrictionContext,\n\t\tattributeEquivalenceList);\n}\n\n\n/*\n * RestrictionEquivalenceForPartitionKeysViaEquivalences follows the same rules\n * with RestrictionEquivalenceForPartitionKeys(). The only difference is that\n * this function allows passing pre-computed attribute equivalences along with\n * the planner restriction context.\n */\nbool\nRestrictionEquivalenceForPartitionKeysViaEquivalences(PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *allAttributeEquivalenceList)\n{\n\tRelationRestrictionContext *restrictionContext =\n\t\tplannerRestrictionContext->relationRestrictionContext;\n\n\t/* there is a single distributed relation, no need to continue */\n\tif (!ContainsMultipleDistributedRelations(plannerRestrictionContext))\n\t{\n\t\treturn true;\n\t}\n\n\treturn EquivalenceListContainsRelationsEquality(allAttributeEquivalenceList,\n\t\t\t\t\t\t\t\t\t\t\t\t\trestrictionContext);\n}\n\n\n/*\n * ContainsMultipleDistributedRelations returns true if the input planner\n * restriction context contains more than one distributed relation.\n */\nstatic bool\nContainsMultipleDistributedRelations(PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t plannerRestrictionContext)\n{\n\tRelationRestrictionContext *restrictionContext =\n\t\tplannerRestrictionContext->relationRestrictionContext;\n\n\tuint32 distributedRelationCount =\n\t\tUniqueRelationCount(restrictionContext, DISTRIBUTED_TABLE);\n\n\t/*\n\t * If the query includes a single relation which is not a reference table,\n\t * we should not check the partition column equality.\n\t * Consider two example cases:\n\t *   (i)   The query includes only a single colocated relation\n\t *   (ii)  A colocated relation is joined with a (or multiple) reference\n\t *         table(s) where colocated relation is not joined on the partition key\n\t *\n\t * For the above two cases, we don't need to execute the partition column equality\n\t * algorithm. The reason is that the essence of this function is to ensure that the\n\t * tasks that are going to be created should not need data from other tasks. In both\n\t * cases mentioned above, the necessary data per task would be on available.\n\t */\n\tif (distributedRelationCount <= 1)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * GenerateAllAttributeEquivalences gets the planner restriction context and returns\n * the list of all attribute equivalences based on both join restrictions and relation\n * restrictions.\n */\nList *\nGenerateAllAttributeEquivalences(PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tRelationRestrictionContext *relationRestrictionContext =\n\t\tplannerRestrictionContext->relationRestrictionContext;\n\tJoinRestrictionContext *joinRestrictionContext =\n\t\tplannerRestrictionContext->joinRestrictionContext;\n\n\t/* reset the equivalence id counter per call to prevent overflows */\n\tAttributeEquivalenceId = 1;\n\n\tList *relationRestrictionAttributeEquivalenceList =\n\t\tGenerateAttributeEquivalencesForRelationRestrictions(relationRestrictionContext);\n\tList *joinRestrictionAttributeEquivalenceList =\n\t\tGenerateAttributeEquivalencesForJoinRestrictions(joinRestrictionContext);\n\n\tList *allAttributeEquivalenceList = list_concat(\n\t\trelationRestrictionAttributeEquivalenceList,\n\t\tjoinRestrictionAttributeEquivalenceList);\n\n\treturn allAttributeEquivalenceList;\n}\n\n\n/*\n * UniqueRelationCount iterates over the relations and returns the\n * unique relation count. We use RTEIdentity as the identifiers, so if\n * the same relation appears twice in the restrictionContext, we count\n * it as a single item.\n */\nuint32\nUniqueRelationCount(RelationRestrictionContext *restrictionContext, CitusTableType\n\t\t\t\t\ttableType)\n{\n\tListCell *relationRestrictionCell = NULL;\n\tList *rteIdentityList = NIL;\n\n\tforeach(relationRestrictionCell, restrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction =\n\t\t\t(RelationRestriction *) lfirst(relationRestrictionCell);\n\t\tOid relationId = relationRestriction->relationId;\n\n\t\tCitusTableCacheEntry *cacheEntry = LookupCitusTableCacheEntry(relationId);\n\t\tif (cacheEntry == NULL)\n\t\t{\n\t\t\t/* we  don't expect non-distributed tables, still be no harm to skip */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (IsCitusTableTypeCacheEntry(cacheEntry, tableType))\n\t\t{\n\t\t\tint rteIdentity = GetRTEIdentity(relationRestriction->rte);\n\t\t\trteIdentityList = list_append_unique_int(rteIdentityList, rteIdentity);\n\t\t}\n\t}\n\n\treturn list_length(rteIdentityList);\n}\n\n\n/*\n * EquivalenceListContainsRelationsEquality gets a list of attributed equivalence\n * list and a relation restriction context. The function first generates a common\n * equivalence class out of the attributeEquivalenceList. Later, the function checks\n * whether all the relations exists in the common equivalence class.\n *\n */\nbool\nEquivalenceListContainsRelationsEquality(List *attributeEquivalenceList,\n\t\t\t\t\t\t\t\t\t\t RelationRestrictionContext *restrictionContext)\n{\n\tListCell *commonEqClassCell = NULL;\n\tListCell *relationRestrictionCell = NULL;\n\tRelids commonRteIdentities = NULL;\n\n\t/*\n\t * In general we're trying to expand existing the equivalence classes to find a\n\t * common equivalence class. The main goal is to test whether this main class\n\t * contains all partition keys of the existing relations.\n\t */\n\tAttributeEquivalenceClass *commonEquivalenceClass = GenerateCommonEquivalence(\n\t\tattributeEquivalenceList,\n\t\trestrictionContext);\n\n\t/* add the rte indexes of relations to a bitmap */\n\tforeach(commonEqClassCell, commonEquivalenceClass->equivalentAttributes)\n\t{\n\t\tAttributeEquivalenceClassMember *classMember =\n\t\t\t(AttributeEquivalenceClassMember *) lfirst(commonEqClassCell);\n\t\tint rteIdentity = classMember->rteIdentity;\n\n\t\tcommonRteIdentities = bms_add_member(commonRteIdentities, rteIdentity);\n\t}\n\n\t/* check whether all relations exists in the main restriction list */\n\tforeach(relationRestrictionCell, restrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction =\n\t\t\t(RelationRestriction *) lfirst(relationRestrictionCell);\n\t\tint rteIdentity = GetRTEIdentity(relationRestriction->rte);\n\n\t\t/* we shouldn't check for the equality of non-distributed tables */\n\t\tif (IsCitusTable(relationRestriction->relationId) &&\n\t\t\t!HasDistributionKey(relationRestriction->relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!bms_is_member(rteIdentity, commonRteIdentities))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * GenerateAttributeEquivalencesForRelationRestrictions gets a relation restriction\n * context and returns a list of AttributeEquivalenceClass.\n *\n * The algorithm followed can be summarized as below:\n *\n * - Per relation restriction\n *     - Per plannerInfo's eq_class\n *         - Create an AttributeEquivalenceClass\n *         - Add all Vars that appear in the plannerInfo's\n *           eq_class to the AttributeEquivalenceClass\n *               - While doing that, consider LATERAL vars as well.\n *                 See GetVarFromAssignedParam() for the details. Note\n *                 that we're using parentPlannerInfo while adding the\n *                 LATERAL vars given that we rely on that plannerInfo.\n *\n */\nstatic List *\nGenerateAttributeEquivalencesForRelationRestrictions(RelationRestrictionContext\n\t\t\t\t\t\t\t\t\t\t\t\t\t *restrictionContext)\n{\n\tList *attributeEquivalenceList = NIL;\n\tListCell *relationRestrictionCell = NULL;\n\tbool foundRLSPattern = false;\n\n\tif (restrictionContext == NULL)\n\t{\n\t\treturn attributeEquivalenceList;\n\t}\n\n\t/*\n\t * First pass: Process equivalence classes using the original algorithm.\n\t * This builds the standard attribute equivalence list.\n\t *\n\t * Skip RLS pattern detection entirely if the query doesn't\n\t * use Row Level Security. The hasRowSecurity flag is checked from the query's\n\t * parse tree when any table has RLS policies active. This allows us to skip\n\t * both the pattern detection loop AND the expensive merge pass for non-RLS\n\t * queries (common case).\n\t *\n\t * For RLS queries, detect patterns efficiently. We only need\n\t * to find one EC with both Var + non-Var members to justify the merge pass.\n\t * Once found, skip further pattern checks and focus on building equivalences.\n\t */\n\tbool skipRLSProcessing = true;\n\tforeach(relationRestrictionCell, restrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction =\n\t\t\t(RelationRestriction *) lfirst(relationRestrictionCell);\n\n\t\tskipRLSProcessing = !relationRestriction->plannerInfo->parse->hasRowSecurity;\n\t\tList *equivalenceClasses = relationRestriction->plannerInfo->eq_classes;\n\t\tListCell *equivalenceClassCell = NULL;\n\n\t\tforeach(equivalenceClassCell, equivalenceClasses)\n\t\t{\n\t\t\tEquivalenceClass *plannerEqClass =\n\t\t\t\t(EquivalenceClass *) lfirst(equivalenceClassCell);\n\n\t\t\t/*\n\t\t\t * RLS pattern = EC with both Var and non-Var (function) members.\n\t\t\t * Finding even one such pattern means we need the merge pass.\n\t\t\t */\n\t\t\tif (!skipRLSProcessing && !foundRLSPattern)\n\t\t\t{\n\t\t\t\tbool hasVar = false;\n\t\t\t\tbool hasNonVar = false;\n\t\t\t\tListCell *memberCell = NULL;\n\n\t\t\t\tforeach(memberCell, plannerEqClass->ec_members)\n\t\t\t\t{\n\t\t\t\t\tEquivalenceMember *member = (EquivalenceMember *) lfirst(memberCell);\n\t\t\t\t\tNode *expr = strip_implicit_coercions((Node *) member->em_expr);\n\n\t\t\t\t\tif (IsA(expr, Var))\n\t\t\t\t\t{\n\t\t\t\t\t\thasVar = true;\n\t\t\t\t\t}\n\t\t\t\t\telse if (member->em_is_const &&\n\t\t\t\t\t\t\t !IsA(expr, Param) && !IsA(expr, Const))\n\t\t\t\t\t{\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Found a pseudoconstant expression (no Vars) that's not a\n\t\t\t\t\t\t * Param or Const - this is the RLS function pattern.\n\t\t\t\t\t\t */\n\t\t\t\t\t\thasNonVar = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t/* Early exit: If we've found both, we have the pattern */\n\t\t\t\t\tif (hasVar && hasNonVar)\n\t\t\t\t\t{\n\t\t\t\t\t\tfoundRLSPattern = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tAttributeEquivalenceClass *attributeEquivalence =\n\t\t\t\tAttributeEquivalenceClassForEquivalenceClass(plannerEqClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t relationRestriction);\n\n\t\t\tattributeEquivalenceList =\n\t\t\t\tAddAttributeClassToAttributeClassList(attributeEquivalenceList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  attributeEquivalence);\n\t\t}\n\t}\n\n\t/*\n\t * Second pass: Handle RLS-specific case where PostgreSQL splits join conditions\n\t * across multiple EquivalenceClasses due to volatile functions in RLS policies.\n\t *\n\t * When RLS policies use volatile functions (e.g., current_setting()), PostgreSQL\n\t * creates separate EquivalenceClasses that both contain the same volatile function:\n\t *   EC1: [table_a.tenant_id, current_setting(...)]\n\t *   EC2: [table_b.tenant_id, current_setting(...)]\n\t *\n\t * We need to recognize that these should be merged to detect that tables are\n\t * joined on their distribution columns: [table_a.tenant_id, table_b.tenant_id]\n\t */\n\tif (foundRLSPattern)\n\t{\n\t\tList *rlsMergedList = MergeEquivalenceClassesWithSameFunctions(\n\t\t\trestrictionContext);\n\n\t\t/* Append any newly created merged classes to the original list */\n\t\tattributeEquivalenceList = list_concat(attributeEquivalenceList, rlsMergedList);\n\t}\n\n\treturn attributeEquivalenceList;\n}\n\n\n/*\n * MergeEquivalenceClassesWithSameFunctions scans equivalence classes\n * looking for RLS-specific patterns where volatile functions cause PostgreSQL to\n * split what should be a single join condition across multiple EquivalenceClasses.\n *\n * This function specifically targets the pattern:\n *   EC1: [table_a.col, COERCEVIAIO(func(...))]\n *   EC2: [table_b.col, COERCEVIAIO(func(...))]\n *\n * Where the underlying function calls are identical after stripping implicit coercions\n * (e.g., both resolve to current_setting('session.current_tenant_id')).\n *\n * PostgreSQL wraps RLS policy expressions in COERCEVIAIO nodes to handle type\n * conversions (e.g., text → UUID). We strip these to compare the actual function calls.\n *\n * Returns a list of newly created merged AttributeEquivalenceClasses. Each merged\n * class contains the Var members from pairs of EquivalenceClasses that share identical\n * non-Var expressions. For example, if EC1 contains [table_a.tenant_id, func()] and\n * EC2 contains [table_b.tenant_id, func()], the returned list will include a new\n * AttributeEquivalenceClass with [table_a.tenant_id, table_b.tenant_id]. Only classes\n * with 2+ members are returned (indicating an actual join between tables).\n */\nstatic List *\nMergeEquivalenceClassesWithSameFunctions(RelationRestrictionContext *restrictionContext)\n{\n\tList *newlyMergedClasses = NIL;\n\tList *ecGroupList = NIL;  /* List of ECGroupByExpr* */\n\tListCell *relationRestrictionCell = NULL;\n\n\tif (restrictionContext == NULL)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * Phase 1: Collect candidate ECs and group them by their non-Var expressions.\n\t *\n\t * Strategy: For each EC, extract and strip all non-Var expressions, then\n\t * find or create a group for each unique expression. This gives us direct\n\t * access to all ECs sharing the same expression.\n\t */\n\tforeach(relationRestrictionCell, restrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction =\n\t\t\t(RelationRestriction *) lfirst(relationRestrictionCell);\n\t\tList *equivalenceClasses = relationRestriction->plannerInfo->eq_classes;\n\t\tListCell *equivalenceClassCell = NULL;\n\n\t\tforeach(equivalenceClassCell, equivalenceClasses)\n\t\t{\n\t\t\tEquivalenceClass *ec = (EquivalenceClass *) lfirst(equivalenceClassCell);\n\t\t\tbool hasVar = false;\n\t\t\tList *nonVarExprs = NIL;\n\t\t\tListCell *memberCell = NULL;\n\n\t\t\t/*\n\t\t\t * Single pass through EC members: collect Vars and non-Var expressions.\n\t\t\t * Strip coercions once and cache the results.\n\t\t\t */\n\t\t\tforeach(memberCell, ec->ec_members)\n\t\t\t{\n\t\t\t\tEquivalenceMember *member = (EquivalenceMember *) lfirst(memberCell);\n\t\t\t\tNode *expr = strip_implicit_coercions((Node *) member->em_expr);\n\n\t\t\t\tif (IsA(expr, Var))\n\t\t\t\t{\n\t\t\t\t\thasVar = true;\n\t\t\t\t}\n\t\t\t\telse if (member->em_is_const && !IsA(expr, Param) && !IsA(expr, Const))\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Found a pseudoconstant expression (no Vars) - potential RLS function.\n\t\t\t\t\t * After stripping, this is typically a FUNCEXPR like\n\t\t\t\t\t * current_setting('session.current_tenant_id').\n\t\t\t\t\t */\n\t\t\t\t\tnonVarExprs = lappend(nonVarExprs, expr);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* Only process ECs with both Var and non-Var members (RLS pattern) */\n\t\t\tif (!hasVar || nonVarExprs == NIL)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * For each non-Var expression in this EC, find or create a group.\n\t\t\t * Multiple ECs with the same expression will be grouped together.\n\t\t\t */\n\t\t\tListCell *exprCell = NULL;\n\t\t\tforeach(exprCell, nonVarExprs)\n\t\t\t{\n\t\t\t\tNode *strippedExpr = (Node *) lfirst(exprCell);\n\t\t\t\tECGroupByExpr *matchingGroup = NULL;\n\t\t\t\tListCell *groupCell = NULL;\n\n\t\t\t\t/* Search for existing group with this expression */\n\t\t\t\tforeach(groupCell, ecGroupList)\n\t\t\t\t{\n\t\t\t\t\tECGroupByExpr *group = (ECGroupByExpr *) lfirst(groupCell);\n\n\t\t\t\t\tif (equal(group->strippedExpr, strippedExpr))\n\t\t\t\t\t{\n\t\t\t\t\t\tmatchingGroup = group;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* Create new group if this is the first EC with this expression */\n\t\t\t\tif (matchingGroup == NULL)\n\t\t\t\t{\n\t\t\t\t\tmatchingGroup = palloc0(sizeof(ECGroupByExpr));\n\t\t\t\t\tmatchingGroup->strippedExpr = strippedExpr;\n\t\t\t\t\tmatchingGroup->ecsWithThisExpr = NIL;\n\t\t\t\t\tmatchingGroup->varsInTheseECs = NIL;\n\t\t\t\t\tecGroupList = lappend(ecGroupList, matchingGroup);\n\t\t\t\t}\n\n\t\t\t\t/* Add this EC to the group (avoid duplicates) */\n\t\t\t\tif (!list_member_ptr(matchingGroup->ecsWithThisExpr, ec))\n\t\t\t\t{\n\t\t\t\t\tmatchingGroup->ecsWithThisExpr =\n\t\t\t\t\t\tlappend(matchingGroup->ecsWithThisExpr, ec);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * Phase 2: For each group with 2+ ECs, extract all Vars and create a merged\n\t * AttributeEquivalenceClass. This is where we detect the join pattern.\n\t *\n\t * Idea here is that if multiple ECs share the same non-Var expression (e.g., RLS\n\t * function), then all Vars in those ECs are implicitly equal to each other.\n\t */\n\tListCell *groupCell = NULL;\n\tforeach(groupCell, ecGroupList)\n\t{\n\t\tECGroupByExpr *group = (ECGroupByExpr *) lfirst(groupCell);\n\n\t\t/* Skip groups with only one EC - no join to detect */\n\t\tif (list_length(group->ecsWithThisExpr) < 2)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Extract all Vars from all ECs in this group.\n\t\t * These Vars are implicitly equal via the shared expression.\n\t\t */\n\t\tListCell *ecCell = NULL;\n\t\tforeach(ecCell, group->ecsWithThisExpr)\n\t\t{\n\t\t\tEquivalenceClass *ec = (EquivalenceClass *) lfirst(ecCell);\n\t\t\tListCell *memberCell = NULL;\n\n\t\t\tforeach(memberCell, ec->ec_members)\n\t\t\t{\n\t\t\t\tEquivalenceMember *member = (EquivalenceMember *) lfirst(memberCell);\n\t\t\t\tNode *expr = strip_implicit_coercions((Node *) member->em_expr);\n\n\t\t\t\tif (IsA(expr, Var))\n\t\t\t\t{\n\t\t\t\t\t/* Cache this Var for later processing */\n\t\t\t\t\tgroup->varsInTheseECs = lappend(group->varsInTheseECs, expr);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/* Need at least 2 Vars from different tables to represent a join */\n\t\tif (list_length(group->varsInTheseECs) < 2)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Create the merged AttributeEquivalenceClass.\n\t\t */\n\t\tAttributeEquivalenceClass *mergedClass =\n\t\t\tpalloc0(sizeof(AttributeEquivalenceClass));\n\t\tmergedClass->equivalenceId = AttributeEquivalenceId++;\n\t\tmergedClass->equivalentAttributes = NIL;\n\n\t\t/*\n\t\t * Match each Var to its RelationRestriction by comparing varno to\n\t\t * the restriction's index field (which is the RTE index).\n\t\t */\n\t\tListCell *varCell = NULL;\n\t\tforeach(varCell, group->varsInTheseECs)\n\t\t{\n\t\t\tVar *var = (Var *) lfirst(varCell);\n\t\t\tListCell *relResCell = NULL;\n\t\t\tbool foundMatch = false;\n\n\t\t\t/*\n\t\t\t * Find the RelationRestriction that corresponds to this Var.\n\t\t\t * The index field contains the RTE index (varno) of the relation.\n\t\t\t */\n\t\t\tforeach(relResCell, restrictionContext->relationRestrictionList)\n\t\t\t{\n\t\t\t\tRelationRestriction *relRestriction =\n\t\t\t\t\t(RelationRestriction *) lfirst(relResCell);\n\n\t\t\t\t/* Direct match: varno equals the restriction's index */\n\t\t\t\tif (var->varno == relRestriction->index)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Process this Var through AddToAttributeEquivalenceClass.\n\t\t\t\t\t * This handles subqueries, UNION ALL, LATERAL joins, etc.\n\t\t\t\t\t */\n\t\t\t\t\tAddToAttributeEquivalenceClass(mergedClass,\n\t\t\t\t\t\t\t\t\t\t\t\t   relRestriction->plannerInfo, var);\n\t\t\t\t\tfoundMatch = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If we didn't find a matching restriction, this Var might be from\n\t\t\t * a context not tracked in our restriction list (e.g., subquery).\n\t\t\t * We skip it as we only care about Vars from distributed tables.\n\t\t\t */\n\t\t\tif (!foundMatch)\n\t\t\t{\n\t\t\t\telog(DEBUG2, \"Skipping Var with varno=%d in RLS merge - \"\n\t\t\t\t\t\t\t \"no matching RelationRestriction found\", var->varno);\n\t\t\t}\n\t\t}\n\n\t\t/* Only emit if we successfully merged attributes from multiple sources */\n\t\tif (list_length(mergedClass->equivalentAttributes) >= 2)\n\t\t{\n\t\t\tnewlyMergedClasses = lappend(newlyMergedClasses, mergedClass);\n\t\t}\n\t}\n\n\treturn newlyMergedClasses;\n}\n\n\n/*\n * AttributeEquivalenceClassForEquivalenceClass is a helper function for\n * GenerateAttributeEquivalencesForRelationRestrictions. The function takes an\n * EquivalenceClass and the relation restriction that the equivalence class\n * belongs to. The function returns an AttributeEquivalenceClass that is composed\n * of ec_members that are simple Var references.\n *\n * The function also takes case of LATERAL joins by simply replacing the PARAM_EXEC\n * with the corresponding expression.\n */\nstatic AttributeEquivalenceClass *\nAttributeEquivalenceClassForEquivalenceClass(EquivalenceClass *plannerEqClass,\n\t\t\t\t\t\t\t\t\t\t\t RelationRestriction *relationRestriction)\n{\n\tAttributeEquivalenceClass *attributeEquivalence =\n\t\tpalloc0(sizeof(AttributeEquivalenceClass));\n\tListCell *equivilanceMemberCell = NULL;\n\tPlannerInfo *plannerInfo = relationRestriction->plannerInfo;\n\n\tattributeEquivalence->equivalenceId = AttributeEquivalenceId++;\n\n\tforeach(equivilanceMemberCell, plannerEqClass->ec_members)\n\t{\n\t\tEquivalenceMember *equivalenceMember =\n\t\t\t(EquivalenceMember *) lfirst(equivilanceMemberCell);\n\t\tNode *equivalenceNode = strip_implicit_coercions(\n\t\t\t(Node *) equivalenceMember->em_expr);\n\t\tExpr *strippedEquivalenceExpr = (Expr *) equivalenceNode;\n\n\t\tVar *expressionVar = NULL;\n\n\t\tif (IsA(strippedEquivalenceExpr, Param))\n\t\t{\n\t\t\tPlannerInfo *outerNodeRoot = NULL;\n\t\t\tParam *equivalenceParam = (Param *) strippedEquivalenceExpr;\n\n\t\t\texpressionVar =\n\t\t\t\tGetVarFromAssignedParam(relationRestriction->outerPlanParamsList,\n\t\t\t\t\t\t\t\t\t\tequivalenceParam, &outerNodeRoot);\n\t\t\tif (expressionVar)\n\t\t\t{\n\t\t\t\tAddToAttributeEquivalenceClass(attributeEquivalence, outerNodeRoot,\n\t\t\t\t\t\t\t\t\t\t\t   expressionVar);\n\t\t\t}\n\t\t}\n\t\telse if (IsA(strippedEquivalenceExpr, Var))\n\t\t{\n\t\t\texpressionVar = (Var *) strippedEquivalenceExpr;\n\t\t\tAddToAttributeEquivalenceClass(attributeEquivalence, plannerInfo,\n\t\t\t\t\t\t\t\t\t\t   expressionVar);\n\t\t}\n\t}\n\n\treturn attributeEquivalence;\n}\n\n\n/*\n * GetVarFromAssignedParam returns the Var that is assigned to the given\n * plannerParam if its kind is PARAM_EXEC.\n *\n * If the paramkind is not equal to PARAM_EXEC the function returns NULL. Similarly,\n * if there is no Var corresponding to the given param is, the function returns NULL.\n *\n * Rationale behind this function:\n *\n *   While iterating through the equivalence classes of RTE_RELATIONs, we\n *   observe that there are PARAM type of equivalence member expressions for\n *   the RTE_RELATIONs which actually belong to lateral vars from the other query\n *   levels.\n *\n *   We're also keeping track of the RTE_RELATION's outer nodes'\n *   plan_params lists which is expected to hold the parameters that are required\n *   for its lower level queries as it is documented:\n *\n *        plan_params contains the expressions that this query level needs to\n *        make available to a lower query level that is currently being planned.\n *\n *   This function is a helper function to iterate through the outer node's query's\n *   plan_params and looks for the param that the equivalence member has. The\n *   comparison is done via the \"paramid\" field. Finally, if the found parameter's\n *   item is a Var, we conclude that Postgres standard_planner replaced the Var\n *   with the Param on assign_param_for_var() function\n *   @src/backend/optimizer/plan/subselect.c.\n */\nstatic Var *\nGetVarFromAssignedParam(List *outerPlanParamsList, Param *plannerParam,\n\t\t\t\t\t\tPlannerInfo **rootContainingVar)\n{\n\tVar *assignedVar = NULL;\n\tListCell *rootPlanParamsCell = NULL;\n\n\tAssert(plannerParam != NULL);\n\n\t/* we're only interested in parameters that Postgres added for execution */\n\tif (plannerParam->paramkind != PARAM_EXEC)\n\t{\n\t\treturn NULL;\n\t}\n\n\tforeach(rootPlanParamsCell, outerPlanParamsList)\n\t{\n\t\tRootPlanParams *outerPlanParams = lfirst(rootPlanParamsCell);\n\n\t\tassignedVar = SearchPlannerParamList(outerPlanParams->plan_params,\n\t\t\t\t\t\t\t\t\t\t\t plannerParam);\n\t\tif (assignedVar != NULL)\n\t\t{\n\t\t\t*rootContainingVar = outerPlanParams->root;\n\t\t\tbreak;\n\t\t}\n\t}\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t/*\n\t * In PG18+, the dereferenced PARAM node could be a GroupVar if the\n\t * query has a GROUP BY. In that case, we need to make an extra\n\t * hop to get the underlying Var from the grouping expressions.\n\t */\n\tif (assignedVar != NULL)\n\t{\n\t\tQuery *parse = (*rootContainingVar)->parse;\n\t\tif (parse->hasGroupRTE)\n\t\t{\n\t\t\tRangeTblEntry *rte = rt_fetch(assignedVar->varno, parse->rtable);\n\t\t\tif (rte->rtekind == RTE_GROUP)\n\t\t\t{\n\t\t\t\tAssert(assignedVar->varattno >= 1 &&\n\t\t\t\t\t   assignedVar->varattno <= list_length(rte->groupexprs));\n\t\t\t\tNode *groupVar = list_nth(rte->groupexprs, assignedVar->varattno - 1);\n\t\t\t\tif (IsA(groupVar, Var))\n\t\t\t\t{\n\t\t\t\t\tassignedVar = (Var *) groupVar;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* todo: handle PlaceHolderVar case if needed */\n\t\t\t\t\tereport(DEBUG2, (errmsg(\n\t\t\t\t\t\t\t\t\t\t \"GroupVar maps to non-Var group expr; bailing out\")));\n\t\t\t\t\tassignedVar = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\treturn assignedVar;\n}\n\n\n/*\n * SearchPlannerParamList searches in plannerParamList and returns the Var that\n * corresponds to the given plannerParam. If there is no Var corresponding to the\n * given param is, the function returns NULL.\n */\nstatic Var *\nSearchPlannerParamList(List *plannerParamList, Param *plannerParam)\n{\n\tVar *assignedVar = NULL;\n\tListCell *plannerParameterCell = NULL;\n\n\tforeach(plannerParameterCell, plannerParamList)\n\t{\n\t\tPlannerParamItem *plannerParamItem =\n\t\t\t(PlannerParamItem *) lfirst(plannerParameterCell);\n\n\t\tif (plannerParamItem->paramId != plannerParam->paramid)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* TODO: Should we consider PlaceHolderVar? */\n\t\tif (!IsA(plannerParamItem->item, Var))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tassignedVar = (Var *) plannerParamItem->item;\n\n\t\tbreak;\n\t}\n\n\treturn assignedVar;\n}\n\n\n/*\n * GenerateCommonEquivalence gets a list of unrelated AttributeEquiavalenceClass\n * whose all members are partition keys.\n *\n * With the equivalence classes, the function follows the algorithm\n * outlined below:\n *\n *     - Add the first equivalence class to the common equivalence class\n *     - Then, iterate on the remaining equivalence classes\n *          - If any of the members equal to the common equivalence class\n *            add all the members of the equivalence class to the common\n *            class\n *          - Start the iteration from the beginning. The reason is that\n *            in case any of the classes we've passed is equivalent to the\n *            newly added one. To optimize the algorithm, we utilze the\n *            equivalence class ids and skip the ones that are already added.\n *      - Finally, return the common equivalence class.\n */\nstatic AttributeEquivalenceClass *\nGenerateCommonEquivalence(List *attributeEquivalenceList,\n\t\t\t\t\t\t  RelationRestrictionContext *relationRestrictionContext)\n{\n\tBitmapset *addedEquivalenceIds = NULL;\n\tuint32 equivalenceListSize = list_length(attributeEquivalenceList);\n\tuint32 equivalenceClassIndex = 0;\n\n\tAttributeEquivalenceClass *commonEquivalenceClass = palloc0(\n\t\tsizeof(AttributeEquivalenceClass));\n\tcommonEquivalenceClass->equivalenceId = 0;\n\n\t/*\n\t * We seed the common equivalence class with a the first distributed\n\t * table since we always want the input distributed relations to be\n\t * on the common class.\n\t */\n\tAttributeEquivalenceClass *firstEquivalenceClass =\n\t\tGenerateEquivalenceClassForRelationRestriction(relationRestrictionContext);\n\n\t/* we skip the calculation if there are not enough information */\n\tif (equivalenceListSize < 1 || firstEquivalenceClass == NULL)\n\t{\n\t\treturn commonEquivalenceClass;\n\t}\n\n\tcommonEquivalenceClass->equivalentAttributes =\n\t\tfirstEquivalenceClass->equivalentAttributes;\n\taddedEquivalenceIds = bms_add_member(addedEquivalenceIds,\n\t\t\t\t\t\t\t\t\t\t firstEquivalenceClass->equivalenceId);\n\n\twhile (equivalenceClassIndex < equivalenceListSize)\n\t{\n\t\tListCell *equivalenceMemberCell = NULL;\n\t\tbool restartLoop = false;\n\n\t\tAttributeEquivalenceClass *currentEquivalenceClass = list_nth(\n\t\t\tattributeEquivalenceList,\n\t\t\tequivalenceClassIndex);\n\n\t\t/*\n\t\t * This is an optimization. If we already added the same equivalence class,\n\t\t * we could skip it since we've already added all the relevant equivalence\n\t\t * members.\n\t\t */\n\t\tif (bms_is_member(currentEquivalenceClass->equivalenceId, addedEquivalenceIds))\n\t\t{\n\t\t\tequivalenceClassIndex++;\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tforeach(equivalenceMemberCell, currentEquivalenceClass->equivalentAttributes)\n\t\t{\n\t\t\tAttributeEquivalenceClassMember *attributeEquialanceMember =\n\t\t\t\t(AttributeEquivalenceClassMember *) lfirst(equivalenceMemberCell);\n\n\t\t\tif (AttributeClassContainsAttributeClassMember(attributeEquialanceMember,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   commonEquivalenceClass))\n\t\t\t{\n\t\t\t\tListConcatUniqueAttributeClassMemberLists(commonEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  currentEquivalenceClass);\n\n\t\t\t\taddedEquivalenceIds = bms_add_member(addedEquivalenceIds,\n\t\t\t\t\t\t\t\t\t\t\t\t\t currentEquivalenceClass->\n\t\t\t\t\t\t\t\t\t\t\t\t\t equivalenceId);\n\n\t\t\t\t/*\n\t\t\t\t * It seems inefficient to start from the beginning.\n\t\t\t\t * But, we should somehow restart from the beginning to test that\n\t\t\t\t * whether the already skipped ones are equal or not.\n\t\t\t\t */\n\t\t\t\trestartLoop = true;\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (restartLoop)\n\t\t{\n\t\t\tequivalenceClassIndex = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t++equivalenceClassIndex;\n\t\t}\n\t}\n\n\treturn commonEquivalenceClass;\n}\n\n\n/*\n * GenerateEquivalenceClassForRelationRestriction generates an AttributeEquivalenceClass\n * with a single AttributeEquivalenceClassMember.\n */\nstatic AttributeEquivalenceClass *\nGenerateEquivalenceClassForRelationRestriction(RelationRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t   relationRestrictionContext)\n{\n\tListCell *relationRestrictionCell = NULL;\n\tAttributeEquivalenceClassMember *eqMember = NULL;\n\tAttributeEquivalenceClass *eqClassForRelation = NULL;\n\n\tforeach(relationRestrictionCell, relationRestrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction =\n\t\t\t(RelationRestriction *) lfirst(relationRestrictionCell);\n\t\tVar *relationPartitionKey = DistPartitionKey(relationRestriction->relationId);\n\n\t\tif (relationPartitionKey)\n\t\t{\n\t\t\teqClassForRelation = palloc0(sizeof(AttributeEquivalenceClass));\n\t\t\teqMember = palloc0(sizeof(AttributeEquivalenceClassMember));\n\t\t\teqMember->relationId = relationRestriction->relationId;\n\t\t\teqMember->rteIdentity = GetRTEIdentity(relationRestriction->rte);\n\t\t\teqMember->varno = relationRestriction->index;\n\t\t\teqMember->varattno = relationPartitionKey->varattno;\n\n\t\t\teqClassForRelation->equivalentAttributes =\n\t\t\t\tlappend(eqClassForRelation->equivalentAttributes, eqMember);\n\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn eqClassForRelation;\n}\n\n\n/*\n * ListConcatUniqueAttributeClassMemberLists gets two attribute equivalence classes. It\n * basically concatenates attribute equivalence member lists uniquely and updates the\n * firstClass' member list with the list.\n *\n * Basically, the function iterates over the secondClass' member list and checks whether\n * it already exists in the firstClass' member list. If not, the member is added to the\n * firstClass.\n */\nstatic void\nListConcatUniqueAttributeClassMemberLists(AttributeEquivalenceClass *firstClass,\n\t\t\t\t\t\t\t\t\t\t  AttributeEquivalenceClass *secondClass)\n{\n\tListCell *equivalenceClassMemberCell = NULL;\n\tList *equivalenceMemberList = secondClass->equivalentAttributes;\n\n\tforeach(equivalenceClassMemberCell, equivalenceMemberList)\n\t{\n\t\tAttributeEquivalenceClassMember *newEqMember =\n\t\t\t(AttributeEquivalenceClassMember *) lfirst(equivalenceClassMemberCell);\n\n\t\tif (AttributeClassContainsAttributeClassMember(newEqMember, firstClass))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tfirstClass->equivalentAttributes = lappend(firstClass->equivalentAttributes,\n\t\t\t\t\t\t\t\t\t\t\t\t   newEqMember);\n\t}\n}\n\n\n/*\n * GenerateAttributeEquivalencesForJoinRestrictions gets a join restriction\n * context and returns a list of AttrributeEquivalenceClass.\n *\n * The algorithm followed can be summarized as below:\n *\n * - Per join restriction\n *     - Per RestrictInfo of the join restriction\n *     - Check whether the join restriction is in the form of (Var1 = Var2)\n *         - Create an AttributeEquivalenceClass\n *         - Add both Var1 and Var2 to the AttributeEquivalenceClass\n */\nstatic List *\nGenerateAttributeEquivalencesForJoinRestrictions(JoinRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t joinRestrictionContext)\n{\n\tList *attributeEquivalenceList = NIL;\n\tListCell *joinRestrictionCell = NULL;\n\n\tif (joinRestrictionContext == NULL)\n\t{\n\t\treturn attributeEquivalenceList;\n\t}\n\n\tforeach(joinRestrictionCell, joinRestrictionContext->joinRestrictionList)\n\t{\n\t\tJoinRestriction *joinRestriction =\n\t\t\t(JoinRestriction *) lfirst(joinRestrictionCell);\n\t\tListCell *restrictionInfoList = NULL;\n\n\t\tforeach(restrictionInfoList, joinRestriction->joinRestrictInfoList)\n\t\t{\n\t\t\tRestrictInfo *rinfo = (RestrictInfo *) lfirst(restrictionInfoList);\n\t\t\tExpr *restrictionClause = rinfo->clause;\n\n\t\t\tif (!IsA(restrictionClause, OpExpr))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tOpExpr *restrictionOpExpr = (OpExpr *) restrictionClause;\n\t\t\tif (list_length(restrictionOpExpr->args) != 2)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!OperatorImplementsEquality(restrictionOpExpr->opno))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tNode *leftNode = linitial(restrictionOpExpr->args);\n\t\t\tNode *rightNode = lsecond(restrictionOpExpr->args);\n\n\t\t\t/* we also don't want implicit coercions */\n\t\t\tExpr *strippedLeftExpr = (Expr *) strip_implicit_coercions((Node *) leftNode);\n\t\t\tExpr *strippedRightExpr = (Expr *) strip_implicit_coercions(\n\t\t\t\t(Node *) rightNode);\n\n\t\t\tif (!(IsA(strippedLeftExpr, Var) && IsA(strippedRightExpr, Var)))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tVar *leftVar = (Var *) strippedLeftExpr;\n\t\t\tVar *rightVar = (Var *) strippedRightExpr;\n\n\t\t\tAttributeEquivalenceClass *attributeEquivalence = palloc0(\n\t\t\t\tsizeof(AttributeEquivalenceClass));\n\t\t\tattributeEquivalence->equivalenceId = AttributeEquivalenceId++;\n\n\t\t\tAddToAttributeEquivalenceClass(attributeEquivalence,\n\t\t\t\t\t\t\t\t\t\t   joinRestriction->plannerInfo, leftVar);\n\n\t\t\tAddToAttributeEquivalenceClass(attributeEquivalence,\n\t\t\t\t\t\t\t\t\t\t   joinRestriction->plannerInfo, rightVar);\n\n\t\t\tattributeEquivalenceList =\n\t\t\t\tAddAttributeClassToAttributeClassList(attributeEquivalenceList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  attributeEquivalence);\n\t\t}\n\t}\n\n\treturn attributeEquivalenceList;\n}\n\n\n/*\n * AddToAttributeEquivalenceClass is a key function for building the attribute\n * equivalences. The function gets a plannerInfo, var and attribute equivalence\n * class. It searches for the RTE_RELATION(s) that the input var belongs to and\n * adds the found Var(s) to the input attribute equivalence class.\n *\n * Note that the input var could come from a subquery (i.e., not directly from an\n * RTE_RELATION). That's the reason we recursively call the function until the\n * RTE_RELATION found.\n *\n * The algorithm could be summarized as follows:\n *\n *    - If the RTE that corresponds to a relation\n *        - Generate an AttributeEquivalenceMember and add to the input\n *          AttributeEquivalenceClass\n *    - If the RTE that corresponds to a subquery\n *        - If the RTE that corresponds to a UNION ALL subquery\n *            - Iterate on each of the appendRels (i.e., each of the UNION ALL query)\n *            - Recursively add all children of the set operation's\n *              corresponding target entries\n *        - If the corresponding subquery entry is a UNION set operation\n *             - Recursively add all children of the set operation's\n *               corresponding target entries\n *        - If the corresponding subquery is a regular subquery (i.e., No set operations)\n *             - Recursively try to add the corresponding target entry to the\n *               equivalence class\n */\nstatic void\nAddToAttributeEquivalenceClass(AttributeEquivalenceClass *attributeEquivalenceClass,\n\t\t\t\t\t\t\t   PlannerInfo *root, Var *varToBeAdded)\n{\n\t/* punt if it's a whole-row var rather than a plain column reference */\n\tif (varToBeAdded->varattno == InvalidAttrNumber)\n\t{\n\t\treturn;\n\t}\n\n\t/* we also don't want to process ctid, tableoid etc */\n\tif (varToBeAdded->varattno < InvalidAttrNumber)\n\t{\n\t\treturn;\n\t}\n\n\t/* outer join checks in PG16 */\n\tif (IsRelOptOuterJoin(root, varToBeAdded->varno))\n\t{\n\t\treturn;\n\t}\n\n\tRangeTblEntry *rangeTableEntry = root->simple_rte_array[varToBeAdded->varno];\n\tif (rangeTableEntry->rtekind == RTE_RELATION)\n\t{\n\t\tAddRteRelationToAttributeEquivalenceClass(attributeEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t\t\t  rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t  varToBeAdded);\n\t}\n\telse if (rangeTableEntry->rtekind == RTE_SUBQUERY)\n\t{\n\t\tAddRteSubqueryToAttributeEquivalenceClass(attributeEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t\t\t  rangeTableEntry, root,\n\t\t\t\t\t\t\t\t\t\t\t\t  varToBeAdded);\n\t}\n}\n\n\n/*\n * AddRteSubqueryToAttributeEquivalenceClass adds the given var to the given\n * attribute equivalence class.\n *\n * The main algorithm is outlined in AddToAttributeEquivalenceClass().\n */\nstatic void\nAddRteSubqueryToAttributeEquivalenceClass(AttributeEquivalenceClass\n\t\t\t\t\t\t\t\t\t\t  *attributeEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t  RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t  PlannerInfo *root,\n\t\t\t\t\t\t\t\t\t\t  Var *varToBeAdded)\n{\n\tRelOptInfo *baseRelOptInfo = find_base_rel(root, varToBeAdded->varno);\n\tQuery *targetSubquery = GetTargetSubquery(root, rangeTableEntry, varToBeAdded);\n\n\t/*\n\t * We might not always get the subquery because the subquery might be a\n\t * referencing to RELOPT_DEADREL such that the corresponding join is\n\t * removed via join_is_removable().\n\t *\n\t * Returning here implies that PostgreSQL doesn't need to plan the\n\t * subquery because it doesn't contribute to the query result at all.\n\t * Since the relations in the subquery does not appear in the query\n\t * plan as well, Citus would simply ignore the subquery and treat that\n\t * as a safe-to-pushdown subquery.\n\t */\n\tif (targetSubquery == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tTargetEntry *subqueryTargetEntry = get_tle_by_resno(targetSubquery->targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvarToBeAdded->varattno);\n\n\t/* if we fail to find corresponding target entry, do not proceed */\n\tif (subqueryTargetEntry == NULL || subqueryTargetEntry->resjunk)\n\t{\n\t\treturn;\n\t}\n\n\t/* we're only interested in Vars */\n\tif (!IsA(subqueryTargetEntry->expr, Var))\n\t{\n\t\treturn;\n\t}\n\n\tvarToBeAdded = (Var *) subqueryTargetEntry->expr;\n\n\t/*\n\t *  \"inh\" flag is set either when inheritance or \"UNION ALL\" exists in the\n\t *  subquery. Here we're only interested in the \"UNION ALL\" case.\n\t *\n\t *  Else, we check one more thing: Does the subquery contain a \"UNION\" query.\n\t *  If so, we recursively traverse all \"UNION\" tree and add the corresponding\n\t *  target list elements to the attribute equivalence.\n\t *\n\t *  Finally, if it is a regular subquery (i.e., does not contain UNION or UNION ALL),\n\t *  we simply recurse to find the corresponding RTE_RELATION to add to the\n\t *  equivalence class.\n\t *\n\t *  Note that we're treating \"UNION\" and \"UNION ALL\" clauses differently given\n\t *  that postgres planner process/plans them separately.\n\t */\n\tif (rangeTableEntry->inh)\n\t{\n\t\tAddUnionAllSetOperationsToAttributeEquivalenceClass(attributeEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\troot, varToBeAdded);\n\t}\n\telse if (targetSubquery->setOperations)\n\t{\n\t\tAddUnionSetOperationsToAttributeEquivalenceClass(attributeEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t baseRelOptInfo->subroot,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t (SetOperationStmt *)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t targetSubquery->setOperations,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t varToBeAdded);\n\t}\n\telse if (varToBeAdded && IsA(varToBeAdded, Var) && varToBeAdded->varlevelsup == 0)\n\t{\n\t\tAddToAttributeEquivalenceClass(attributeEquivalenceClass,\n\t\t\t\t\t\t\t\t\t   baseRelOptInfo->subroot, varToBeAdded);\n\t}\n}\n\n\n/*\n * GetTargetSubquery returns the corresponding subquery for the given planner root,\n * range table entry and the var.\n *\n * The aim of this function is to simplify extracting the subquery in case of \"UNION ALL\"\n * queries.\n */\nstatic Query *\nGetTargetSubquery(PlannerInfo *root, RangeTblEntry *rangeTableEntry, Var *varToBeAdded)\n{\n\tQuery *targetSubquery = NULL;\n\n\t/*\n\t * For subqueries other than \"UNION ALL\", find the corresponding targetSubquery. See\n\t * the details of how we process subqueries in the below comments.\n\t */\n\tif (!rangeTableEntry->inh)\n\t{\n\t\tRelOptInfo *baseRelOptInfo = find_base_rel(root, varToBeAdded->varno);\n\n\t\t/* If the targetSubquery was not planned, we have to punt */\n\t\tif (baseRelOptInfo->subroot == NULL)\n\t\t{\n\t\t\treturn NULL;\n\t\t}\n\n\t\tAssert(IsA(baseRelOptInfo->subroot, PlannerInfo));\n\n\t\ttargetSubquery = baseRelOptInfo->subroot->parse;\n\t\tAssert(IsA(targetSubquery, Query));\n\t}\n\telse\n\t{\n\t\ttargetSubquery = rangeTableEntry->subquery;\n\t}\n\n\treturn targetSubquery;\n}\n\n\n/*\n * IsRelOptOuterJoin returns true if the RelOpt referenced\n * by varNo is an outer join, false otherwise.\n */\nbool\nIsRelOptOuterJoin(PlannerInfo *root, int varNo)\n{\n\tif (root->simple_rel_array_size <= varNo)\n\t{\n\t\treturn true;\n\t}\n\n\tRelOptInfo *rel = root->simple_rel_array[varNo];\n\tif (rel == NULL)\n\t{\n\t\t/* must be an outer join */\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n\n/*\n * AddUnionAllSetOperationsToAttributeEquivalenceClass recursively iterates on all the\n * append rels, sets the varno's accordingly and adds the\n * var the given equivalence class.\n */\nstatic void\nAddUnionAllSetOperationsToAttributeEquivalenceClass(AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t\t\t\tattributeEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\tPlannerInfo *root,\n\t\t\t\t\t\t\t\t\t\t\t\t\tVar *varToBeAdded)\n{\n\tList *appendRelList = root->append_rel_list;\n\tListCell *appendRelCell = NULL;\n\n\t/* iterate on the queries that are part of UNION ALL subqueries */\n\tforeach(appendRelCell, appendRelList)\n\t{\n\t\tAppendRelInfo *appendRelInfo = (AppendRelInfo *) lfirst(appendRelCell);\n\n\t\t/*\n\t\t * We're only interested in UNION ALL clauses and parent_reloid is invalid\n\t\t * only for UNION ALL (i.e., equals to a legitimate Oid for inheritance)\n\t\t */\n\t\tif (appendRelInfo->parent_reloid != InvalidOid)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tint rtoffset = RangeTableOffsetCompat(root, appendRelInfo);\n\t\tint childRelId = appendRelInfo->child_relid - rtoffset;\n\n\t\tif (root->simple_rel_array_size <= childRelId)\n\t\t{\n\t\t\t/* we prefer to return over an Assert or error to be defensive */\n\t\t\treturn;\n\t\t}\n\n\t\tRangeTblEntry *rte = root->simple_rte_array[childRelId];\n\t\tif (rte->inh)\n\t\t{\n\t\t\t/*\n\t\t\t * This code-path may require improvements. If a leaf of a UNION ALL\n\t\t\t * (e.g., an entry in appendRelList) itself is another UNION ALL\n\t\t\t * (e.g., rte->inh = true), the logic here might get into an infinite\n\t\t\t * recursion.\n\t\t\t *\n\t\t\t * The downside of \"continue\" here is that certain UNION ALL queries\n\t\t\t * that are safe to pushdown may not be pushed down.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\t\telse if (rte->rtekind == RTE_RELATION)\n\t\t{\n\t\t\tIndex partitionKeyIndex = 0;\n\t\t\tList *translatedVars = TranslatedVarsForRteIdentity(GetRTEIdentity(rte));\n\t\t\tVar *varToBeAddedOnUnionAllSubquery =\n\t\t\t\tFindUnionAllVar(root, translatedVars, rte->relid, childRelId,\n\t\t\t\t\t\t\t\t&partitionKeyIndex);\n\t\t\tif (partitionKeyIndex == 0)\n\t\t\t{\n\t\t\t\t/* no partition key on the target list */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (attributeEquivalenceClass->unionQueryPartitionKeyIndex == 0)\n\t\t\t{\n\t\t\t\t/* the first partition key index we found */\n\t\t\t\tattributeEquivalenceClass->unionQueryPartitionKeyIndex =\n\t\t\t\t\tpartitionKeyIndex;\n\t\t\t}\n\t\t\telse if (attributeEquivalenceClass->unionQueryPartitionKeyIndex !=\n\t\t\t\t\t partitionKeyIndex)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Partition keys on the leaves of the UNION ALL queries on\n\t\t\t\t * different ordinal positions. We cannot pushdown, so skip.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (varToBeAddedOnUnionAllSubquery != NULL)\n\t\t\t{\n\t\t\t\tAddToAttributeEquivalenceClass(attributeEquivalenceClass, root,\n\t\t\t\t\t\t\t\t\t\t\t   varToBeAddedOnUnionAllSubquery);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* set the varno accordingly for this specific child */\n\t\t\tvarToBeAdded->varno = childRelId;\n\n\t\t\tAddToAttributeEquivalenceClass(attributeEquivalenceClass, root,\n\t\t\t\t\t\t\t\t\t\t   varToBeAdded);\n\t\t}\n\t}\n}\n\n\n/*\n * ParentCountPriorToAppendRel returns the number of parents that come before\n * the given append rel info.\n */\nstatic int\nParentCountPriorToAppendRel(List *appendRelList, AppendRelInfo *targetAppendRelInfo)\n{\n\tint targetParentIndex = targetAppendRelInfo->parent_relid;\n\tBitmapset *parent_ids = NULL;\n\tAppendRelInfo *appendRelInfo = NULL;\n\tforeach_declared_ptr(appendRelInfo, appendRelList)\n\t{\n\t\tint curParentIndex = appendRelInfo->parent_relid;\n\t\tif (curParentIndex <= targetParentIndex)\n\t\t{\n\t\t\tparent_ids = bms_add_member(parent_ids, curParentIndex);\n\t\t}\n\t}\n\treturn bms_num_members(parent_ids);\n}\n\n\n/*\n * AddUnionSetOperationsToAttributeEquivalenceClass recursively iterates on all the\n * setOperations and adds each corresponding target entry to the given equivalence\n * class.\n *\n * Although the function silently accepts INTERSECT and EXPECT set operations, they are\n * rejected later in the planning. We prefer this behavior to provide better error\n * messages.\n */\nstatic void\nAddUnionSetOperationsToAttributeEquivalenceClass(AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t\t\t attributeEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t\t\t PlannerInfo *root,\n\t\t\t\t\t\t\t\t\t\t\t\t SetOperationStmt *setOperation,\n\t\t\t\t\t\t\t\t\t\t\t\t Var *varToBeAdded)\n{\n\tList *rangeTableIndexList = NIL;\n\tListCell *rangeTableIndexCell = NULL;\n\n\tExtractRangeTableIndexWalker((Node *) setOperation, &rangeTableIndexList);\n\n\tforeach(rangeTableIndexCell, rangeTableIndexList)\n\t{\n\t\tint rangeTableIndex = lfirst_int(rangeTableIndexCell);\n\n\t\tvarToBeAdded->varno = rangeTableIndex;\n\t\tAddToAttributeEquivalenceClass(attributeEquivalenceClass, root, varToBeAdded);\n\t}\n}\n\n\n/*\n * AddRteRelationToAttributeEquivalenceClass adds the given var to the given equivalence\n * class using the rteIdentity provided by the rangeTableEntry. Note that\n * rteIdentities are only assigned to RTE_RELATIONs and this function asserts\n * the input rte to be an RTE_RELATION.\n */\nstatic void\nAddRteRelationToAttributeEquivalenceClass(AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t  attrEquivalenceClass,\n\t\t\t\t\t\t\t\t\t\t  RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t  Var *varToBeAdded)\n{\n\tOid relationId = rangeTableEntry->relid;\n\n\t/* we don't consider local tables in the equality on columns */\n\tif (!IsCitusTable(relationId))\n\t{\n\t\treturn;\n\t}\n\n\tVar *relationPartitionKey = DistPartitionKey(relationId);\n\n\tAssert(rangeTableEntry->rtekind == RTE_RELATION);\n\n\t/*\n\t * we only calculate the equivalence of distributed tables.\n\t * This leads to certain shortcomings in the query planning when reference\n\t * tables and/or intermediate results are involved in the query. For example,\n\t * the following query patterns could actually be pushed-down in a single iteration\n\t *    \"(intermediate_res INNER JOIN dist dist1) INNER JOIN dist dist2 \" or\n\t *    \"(ref INNER JOIN dist dist1) JOIN dist dist2\"\n\t *\n\t * However, if there are no explicit join conditions between distributed tables,\n\t * the planner cannot deduce the equivalence between the distributed tables.\n\t *\n\t * Instead, we should be able to track all the equivalences between range table\n\t * entries, and expand distributed table equivalences that happens via\n\t * reference table/intermediate results\n\t */\n\tif (relationPartitionKey == NULL)\n\t{\n\t\treturn;\n\t}\n\n\t/* we're only interested in distribution columns */\n\tif (relationPartitionKey->varattno != varToBeAdded->varattno)\n\t{\n\t\treturn;\n\t}\n\n\tAttributeEquivalenceClassMember *attributeEqMember = palloc0(\n\t\tsizeof(AttributeEquivalenceClassMember));\n\n\tattributeEqMember->varattno = varToBeAdded->varattno;\n\tattributeEqMember->varno = varToBeAdded->varno;\n\tattributeEqMember->rteIdentity = GetRTEIdentity(rangeTableEntry);\n\tattributeEqMember->relationId = rangeTableEntry->relid;\n\n\tattrEquivalenceClass->equivalentAttributes =\n\t\tlappend(attrEquivalenceClass->equivalentAttributes,\n\t\t\t\tattributeEqMember);\n}\n\n\n/*\n * AttributeClassContainsAttributeClassMember returns true if it the input class member\n * is already exists in the attributeEquivalenceClass. An equality is identified by the\n * varattno and rteIdentity.\n */\nstatic bool\nAttributeClassContainsAttributeClassMember(AttributeEquivalenceClassMember *inputMember,\n\t\t\t\t\t\t\t\t\t\t   AttributeEquivalenceClass *\n\t\t\t\t\t\t\t\t\t\t   attributeEquivalenceClass)\n{\n\tListCell *classCell = NULL;\n\tforeach(classCell, attributeEquivalenceClass->equivalentAttributes)\n\t{\n\t\tAttributeEquivalenceClassMember *memberOfClass =\n\t\t\t(AttributeEquivalenceClassMember *) lfirst(classCell);\n\t\tif (memberOfClass->rteIdentity == inputMember->rteIdentity &&\n\t\t\tmemberOfClass->varattno == inputMember->varattno)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * AddAttributeClassToAttributeClassList checks for certain properties of the\n * input attributeEquivalence before adding it to the attributeEquivalenceList.\n *\n * Firstly, the function skips adding NULL attributeEquivalence to the list.\n * Secondly, since an attribute equivalence class with a single member does\n * not contribute to our purposes, we skip such classed adding to the list.\n * Finally, we don't want to add an equivalence class whose exact equivalent\n * already exists in the list.\n */\nstatic List *\nAddAttributeClassToAttributeClassList(List *attributeEquivalenceList,\n\t\t\t\t\t\t\t\t\t  AttributeEquivalenceClass *attributeEquivalence)\n{\n\tListCell *attributeEquivalenceCell = NULL;\n\n\tif (attributeEquivalence == NULL)\n\t{\n\t\treturn attributeEquivalenceList;\n\t}\n\n\t/*\n\t * Note that in some cases we allow having equivalentAttributes with zero or\n\t * one elements. For the details, see AddToAttributeEquivalenceClass().\n\t */\n\tList *equivalentAttributes = attributeEquivalence->equivalentAttributes;\n\tif (list_length(equivalentAttributes) < 2)\n\t{\n\t\treturn attributeEquivalenceList;\n\t}\n\n\t/* we don't want to add an attributeEquivalence which already exists */\n\tforeach(attributeEquivalenceCell, attributeEquivalenceList)\n\t{\n\t\tAttributeEquivalenceClass *currentAttributeEquivalence =\n\t\t\t(AttributeEquivalenceClass *) lfirst(attributeEquivalenceCell);\n\n\t\tif (AttributeEquivalencesAreEqual(currentAttributeEquivalence,\n\t\t\t\t\t\t\t\t\t\t  attributeEquivalence))\n\t\t{\n\t\t\treturn attributeEquivalenceList;\n\t\t}\n\t}\n\n\tattributeEquivalenceList = lappend(attributeEquivalenceList,\n\t\t\t\t\t\t\t\t\t   attributeEquivalence);\n\n\treturn attributeEquivalenceList;\n}\n\n\n/*\n *  AttributeEquivalencesAreEqual returns true if both input attribute equivalence\n *  classes contains exactly the same members.\n */\nstatic bool\nAttributeEquivalencesAreEqual(AttributeEquivalenceClass *firstAttributeEquivalence,\n\t\t\t\t\t\t\t  AttributeEquivalenceClass *secondAttributeEquivalence)\n{\n\tList *firstEquivalenceMemberList =\n\t\tfirstAttributeEquivalence->equivalentAttributes;\n\tList *secondEquivalenceMemberList =\n\t\tsecondAttributeEquivalence->equivalentAttributes;\n\tListCell *firstAttributeEquivalenceCell = NULL;\n\tListCell *secondAttributeEquivalenceCell = NULL;\n\n\tif (list_length(firstEquivalenceMemberList) != list_length(\n\t\t\tsecondEquivalenceMemberList))\n\t{\n\t\treturn false;\n\t}\n\n\tforeach(firstAttributeEquivalenceCell, firstEquivalenceMemberList)\n\t{\n\t\tAttributeEquivalenceClassMember *firstEqMember =\n\t\t\t(AttributeEquivalenceClassMember *) lfirst(firstAttributeEquivalenceCell);\n\t\tbool foundAnEquivalentMember = false;\n\n\t\tforeach(secondAttributeEquivalenceCell, secondEquivalenceMemberList)\n\t\t{\n\t\t\tAttributeEquivalenceClassMember *secondEqMember =\n\t\t\t\t(AttributeEquivalenceClassMember *) lfirst(\n\t\t\t\t\tsecondAttributeEquivalenceCell);\n\n\t\t\tif (firstEqMember->rteIdentity == secondEqMember->rteIdentity &&\n\t\t\t\tfirstEqMember->varattno == secondEqMember->varattno)\n\t\t\t{\n\t\t\t\tfoundAnEquivalentMember = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/* we couldn't find an equivalent member */\n\t\tif (!foundAnEquivalentMember)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ContainsUnionSubquery gets a queryTree and returns true if the query\n * contains\n *      - a subquery with UNION set operation\n *      - no joins above the UNION set operation in the query tree\n *\n * Note that the function allows top level unions being wrapped into aggregations\n * queries and/or simple projection queries that only selects some fields from\n * the lower level queries.\n *\n * If there exists joins before the set operations, the function returns false.\n * Similarly, if the query does not contain any union set operations, the\n * function returns false.\n */\nbool\nContainsUnionSubquery(Query *queryTree)\n{\n\tList *rangeTableList = queryTree->rtable;\n\tList *joinTreeTableIndexList = NIL;\n\n\tExtractRangeTableIndexWalker((Node *) queryTree->jointree, &joinTreeTableIndexList);\n\tuint32 joiningRangeTableCount = list_length(joinTreeTableIndexList);\n\n\t/* don't allow joins on top of unions */\n\tif (joiningRangeTableCount > 1)\n\t{\n\t\treturn false;\n\t}\n\n\t/* subquery without FROM */\n\tif (joiningRangeTableCount == 0)\n\t{\n\t\treturn false;\n\t}\n\n\tIndex subqueryRteIndex = linitial_int(joinTreeTableIndexList);\n\tRangeTblEntry *rangeTableEntry = rt_fetch(subqueryRteIndex, rangeTableList);\n\tif (rangeTableEntry->rtekind != RTE_SUBQUERY)\n\t{\n\t\treturn false;\n\t}\n\n\tQuery *subqueryTree = rangeTableEntry->subquery;\n\tNode *setOperations = subqueryTree->setOperations;\n\tif (setOperations != NULL)\n\t{\n\t\tSetOperationStmt *setOperationStatement = (SetOperationStmt *) setOperations;\n\n\t\t/*\n\t\t * Note that the set operation tree is traversed elsewhere for ensuring\n\t\t * that we only support UNIONs.\n\t\t */\n\t\tif (setOperationStatement->op != SETOP_UNION)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn ContainsUnionSubquery(subqueryTree);\n}\n\n\n/*\n * PartitionKeyForRTEIdentityInQuery finds the partition key var(if exists),\n * in the given original query for the rte that has targetRTEIndex.\n */\nstatic Var *\nPartitionKeyForRTEIdentityInQuery(Query *originalQuery, int targetRTEIndex,\n\t\t\t\t\t\t\t\t  Index *partitionKeyIndex)\n{\n\tQuery *originalQueryContainingRTEIdentity =\n\t\tFindQueryContainingRTEIdentity(originalQuery, targetRTEIndex);\n\tif (!originalQueryContainingRTEIdentity)\n\t{\n\t\t/*\n\t\t * We should always find the query but we have this check for sanity.\n\t\t * This check makes sure that if there is a bug while finding the query,\n\t\t * we don't get a crash etc. and the only downside will be we might be recursively\n\t\t * planning a query that could be pushed down.\n\t\t */\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * This approach fails to detect when\n\t * the top level query might have the column indexes in different order:\n\t * explain\n\t * SELECT count(*) FROM\n\t * (\n\t * SELECT user_id,value_2 FROM events_table\n\t * UNION\n\t * SELECT value_2, user_id FROM (SELECT user_id, value_2, random() FROM events_table) as foo\n\t * ) foobar;\n\t * So we hit https://github.com/citusdata/citus/issues/5093.\n\t */\n\tList *relationTargetList = originalQueryContainingRTEIdentity->targetList;\n\n\tListCell *targetEntryCell = NULL;\n\tIndex partitionKeyTargetAttrIndex = 0;\n\tforeach(targetEntryCell, relationTargetList)\n\t{\n\t\tTargetEntry *targetEntry = (TargetEntry *) lfirst(targetEntryCell);\n\t\tExpr *targetExpression = targetEntry->expr;\n\n\t\tpartitionKeyTargetAttrIndex++;\n\n\t\tbool skipOuterVars = false;\n\t\tif (!targetEntry->resjunk &&\n\t\t\tIsA(targetExpression, Var) &&\n\t\t\tIsPartitionColumn(targetExpression, originalQueryContainingRTEIdentity,\n\t\t\t\t\t\t\t  skipOuterVars))\n\t\t{\n\t\t\tVar *targetColumn = (Var *) targetExpression;\n\n\t\t\t/*\n\t\t\t * We find the referenced table column to support distribution\n\t\t\t * columns that are correlated.\n\t\t\t */\n\t\t\tRangeTblEntry *rteContainingPartitionKey = NULL;\n\t\t\tFindReferencedTableColumn(targetExpression, NIL,\n\t\t\t\t\t\t\t\t\t  originalQueryContainingRTEIdentity,\n\t\t\t\t\t\t\t\t\t  &targetColumn,\n\t\t\t\t\t\t\t\t\t  &rteContainingPartitionKey,\n\t\t\t\t\t\t\t\t\t  skipOuterVars);\n\n\t\t\tif (rteContainingPartitionKey->rtekind == RTE_RELATION &&\n\t\t\t\tGetRTEIdentity(rteContainingPartitionKey) == targetRTEIndex)\n\t\t\t{\n\t\t\t\t*partitionKeyIndex = partitionKeyTargetAttrIndex;\n\t\t\t\treturn (Var *) copyObject(targetColumn);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n\n/*\n * FindQueryContainingRTEIdentity finds the query/subquery that has an RTE\n * with rteIndex in its rtable.\n */\nstatic Query *\nFindQueryContainingRTEIdentity(Query *query, int rteIndex)\n{\n\tFindQueryContainingRteIdentityContext *findRteIdentityContext =\n\t\tpalloc0(sizeof(FindQueryContainingRteIdentityContext));\n\tfindRteIdentityContext->targetRTEIdentity = rteIndex;\n\tFindQueryContainingRTEIdentityInternal((Node *) query, findRteIdentityContext);\n\treturn findRteIdentityContext->query;\n}\n\n\n/*\n * FindQueryContainingRTEIdentityInternal walks on the given node to find a query\n * which has an RTE that has a given rteIdentity.\n */\nstatic bool\nFindQueryContainingRTEIdentityInternal(Node *node,\n\t\t\t\t\t\t\t\t\t   FindQueryContainingRteIdentityContext *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\t\tQuery *parentQuery = context->query;\n\t\tcontext->query = query;\n\t\tif (query_tree_walker(query, FindQueryContainingRTEIdentityInternal, context,\n\t\t\t\t\t\t\t  QTW_EXAMINE_RTES_BEFORE))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t\tcontext->query = parentQuery;\n\t\treturn false;\n\t}\n\n\tif (!IsA(node, RangeTblEntry))\n\t{\n\t\treturn expression_tree_walker(node, FindQueryContainingRTEIdentityInternal,\n\t\t\t\t\t\t\t\t\t  context);\n\t}\n\tRangeTblEntry *rte = (RangeTblEntry *) node;\n\tif (rte->rtekind == RTE_RELATION)\n\t{\n\t\tif (GetRTEIdentity(rte) == context->targetRTEIdentity)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n\n/*\n * AllDistributedRelationsInRestrictionContextColocated determines whether all of the\n * distributed  relations in the given relation restrictions list are co-located.\n */\nstatic bool\nAllDistributedRelationsInRestrictionContextColocated(RelationRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t restrictionContext)\n{\n\tRelationRestriction *relationRestriction = NULL;\n\tList *relationIdList = NIL;\n\n\t/* check whether all relations exists in the main restriction list */\n\tforeach_declared_ptr(relationRestriction, restrictionContext->relationRestrictionList)\n\t{\n\t\trelationIdList = lappend_oid(relationIdList, relationRestriction->relationId);\n\t}\n\n\treturn AllDistributedRelationsInListColocated(relationIdList);\n}\n\n\n/*\n * AllDistributedRelationsInRTEListColocated determines whether all of the\n * distributed relations in the given RangeTableEntry list are co-located.\n */\nbool\nAllDistributedRelationsInRTEListColocated(List *rangeTableEntryList)\n{\n\tRangeTblEntry *rangeTableEntry = NULL;\n\tList *relationIdList = NIL;\n\n\tforeach_declared_ptr(rangeTableEntry, rangeTableEntryList)\n\t{\n\t\trelationIdList = lappend_oid(relationIdList, rangeTableEntry->relid);\n\t}\n\n\treturn AllDistributedRelationsInListColocated(relationIdList);\n}\n\n\n/*\n * AllDistributedRelationsInListColocated determines whether all of the\n * distributed relations in the given list are co-located.\n */\nbool\nAllDistributedRelationsInListColocated(List *relationList)\n{\n\tint initialColocationId = INVALID_COLOCATION_ID;\n\tOid relationId = InvalidOid;\n\n\tforeach_declared_oid(relationId, relationList)\n\t{\n\t\tif (!IsCitusTable(relationId))\n\t\t{\n\t\t\t/* not interested in Postgres tables */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!IsCitusTableType(relationId, DISTRIBUTED_TABLE))\n\t\t{\n\t\t\t/* not interested in non-distributed tables */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (IsCitusTableType(relationId, APPEND_DISTRIBUTED))\n\t\t{\n\t\t\t/*\n\t\t\t * If we got to this point, it means there are multiple distributed\n\t\t\t * relations and at least one of them is append-distributed. Since\n\t\t\t * we do not consider append-distributed tables to be co-located,\n\t\t\t * we can immediately return false.\n\t\t\t */\n\t\t\treturn false;\n\t\t}\n\n\t\tint colocationId = TableColocationId(relationId);\n\n\t\tif (initialColocationId == INVALID_COLOCATION_ID)\n\t\t{\n\t\t\tinitialColocationId = colocationId;\n\t\t}\n\t\telse if (colocationId != initialColocationId)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * RelationIdList returns list of unique relation ids in query tree.\n */\nList *\nDistributedRelationIdList(Query *query)\n{\n\tList *rangeTableList = NIL;\n\tList *relationIdList = NIL;\n\tListCell *tableEntryCell = NULL;\n\n\tExtractRangeTableRelationWalker((Node *) query, &rangeTableList);\n\tList *tableEntryList = TableEntryList(rangeTableList);\n\n\tforeach(tableEntryCell, tableEntryList)\n\t{\n\t\tTableEntry *tableEntry = (TableEntry *) lfirst(tableEntryCell);\n\t\tOid relationId = tableEntry->relationId;\n\n\t\tif (!IsCitusTable(relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\trelationIdList = list_append_unique_oid(relationIdList, relationId);\n\t}\n\n\treturn relationIdList;\n}\n\n\n/*\n * FilterPlannerRestrictionForQuery gets a planner restriction context and\n * set of rte identities. It returns the restrictions that that appear\n * in the queryRteIdentities and returns a newly allocated\n * PlannerRestrictionContext. The function also sets all the other fields of\n * the PlannerRestrictionContext with respect to the filtered restrictions.\n */\nPlannerRestrictionContext *\nFilterPlannerRestrictionForQuery(PlannerRestrictionContext *plannerRestrictionContext,\n\t\t\t\t\t\t\t\t Query *query)\n{\n\tRelids queryRteIdentities = QueryRteIdentities(query);\n\n\tRelationRestrictionContext *relationRestrictionContext =\n\t\tplannerRestrictionContext->relationRestrictionContext;\n\tJoinRestrictionContext *joinRestrictionContext =\n\t\tplannerRestrictionContext->joinRestrictionContext;\n\n\tRelationRestrictionContext *filteredRelationRestrictionContext =\n\t\tFilterRelationRestrictionContext(relationRestrictionContext, queryRteIdentities);\n\n\tJoinRestrictionContext *filtererdJoinRestrictionContext =\n\t\tFilterJoinRestrictionContext(joinRestrictionContext, queryRteIdentities);\n\n\t/* allocate the filtered planner restriction context and set all the fields */\n\tPlannerRestrictionContext *filteredPlannerRestrictionContext = palloc0(\n\t\tsizeof(PlannerRestrictionContext));\n\tfilteredPlannerRestrictionContext->fastPathRestrictionContext = palloc0(\n\t\tsizeof(FastPathRestrictionContext));\n\n\tfilteredPlannerRestrictionContext->memoryContext =\n\t\tplannerRestrictionContext->memoryContext;\n\n\tint totalRelationCount = UniqueRelationCount(\n\t\tfilteredRelationRestrictionContext, ANY_CITUS_TABLE_TYPE);\n\tint referenceRelationCount = UniqueRelationCount(\n\t\tfilteredRelationRestrictionContext, REFERENCE_TABLE);\n\n\tfilteredRelationRestrictionContext->allReferenceTables =\n\t\t(totalRelationCount == referenceRelationCount);\n\n\t/* finally set the relation and join restriction contexts */\n\tfilteredPlannerRestrictionContext->relationRestrictionContext =\n\t\tfilteredRelationRestrictionContext;\n\tfilteredPlannerRestrictionContext->joinRestrictionContext =\n\t\tfiltererdJoinRestrictionContext;\n\n\treturn filteredPlannerRestrictionContext;\n}\n\n\n/*\n * GetRestrictInfoListForRelation gets a range table entry and planner\n * restriction context. The function returns a list of expressions that\n * appear in the restriction context for only the given relation. And,\n * all the varnos are set to 1.\n */\nList *\nGetRestrictInfoListForRelation(RangeTblEntry *rangeTblEntry,\n\t\t\t\t\t\t\t   PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tRelationRestriction *relationRestriction =\n\t\tRelationRestrictionForRelation(rangeTblEntry, plannerRestrictionContext);\n\tif (relationRestriction == NULL)\n\t{\n\t\treturn NIL;\n\t}\n\n\tRelOptInfo *relOptInfo = relationRestriction->relOptInfo;\n\tList *baseRestrictInfo = relOptInfo->baserestrictinfo;\n\n\tbool joinConditionIsOnFalse = JoinConditionIsOnFalse(relOptInfo->joininfo);\n\tif (joinConditionIsOnFalse)\n\t{\n\t\t/* found WHERE false, no need  to continue, we just return a false clause */\n\t\tbool value = false;\n\t\tbool isNull = false;\n\t\tNode *falseClause = makeBoolConst(value, isNull);\n\t\treturn list_make1(falseClause);\n\t}\n\n\n\tList *restrictExprList = NIL;\n\tRestrictInfo *restrictInfo = NULL;\n\tforeach_declared_ptr(restrictInfo, baseRestrictInfo)\n\t{\n\t\tExpr *restrictionClause = restrictInfo->clause;\n\n\t\t/*\n\t\t * we cannot process some restriction clauses because they are not\n\t\t * safe to recursively plan.\n\t\t */\n\t\tif (FindNodeMatchingCheckFunction((Node *) restrictionClause,\n\t\t\t\t\t\t\t\t\t\t  IsNotSafeRestrictionToRecursivelyPlan))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * If the restriction involves multiple tables, we cannot add it to\n\t\t * input relation's expression list.\n\t\t */\n\t\tRelids varnos = pull_varnos(relationRestriction->plannerInfo,\n\t\t\t\t\t\t\t\t\t(Node *) restrictionClause);\n\t\tif (bms_num_members(varnos) != 1)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * PlaceHolderVar is not relevant to be processed inside a restriction clause.\n\t\t * Otherwise, pull_var_clause_default would throw error. PG would create\n\t\t * the restriction to physical Var that PlaceHolderVar points anyway, so it is\n\t\t * safe to skip this restriction.\n\t\t */\n\t\tif (FindNodeMatchingCheckFunction((Node *) restrictionClause, HasPlaceHolderVar))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * We're going to add this restriction expression to a subquery\n\t\t * which consists of only one relation in its jointree. Thus,\n\t\t * simply set the varnos accordingly.\n\t\t */\n\t\tExpr *copyOfRestrictClause = (Expr *) copyObject((Node *) restrictionClause);\n\t\tList *varClauses = pull_var_clause_default((Node *) copyOfRestrictClause);\n\t\tVar *column = NULL;\n\t\tforeach_declared_ptr(column, varClauses)\n\t\t{\n\t\t\tcolumn->varno = SINGLE_RTE_INDEX;\n\t\t\tcolumn->varnosyn = SINGLE_RTE_INDEX;\n\t\t}\n\n\t\trestrictExprList = lappend(restrictExprList, copyOfRestrictClause);\n\t}\n\n\treturn restrictExprList;\n}\n\n\n/*\n * RelationRestrictionForRelation gets the relation restriction for the given\n * range table entry.\n */\nRelationRestriction *\nRelationRestrictionForRelation(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t   PlannerRestrictionContext *plannerRestrictionContext)\n{\n\tint rteIdentity = GetRTEIdentity(rangeTableEntry);\n\tRelationRestrictionContext *relationRestrictionContext =\n\t\tplannerRestrictionContext->relationRestrictionContext;\n\tRelids queryRteIdentities = bms_make_singleton(rteIdentity);\n\tRelationRestrictionContext *filteredRelationRestrictionContext =\n\t\tFilterRelationRestrictionContext(relationRestrictionContext, queryRteIdentities);\n\tList *filteredRelationRestrictionList =\n\t\tfilteredRelationRestrictionContext->relationRestrictionList;\n\n\tif (list_length(filteredRelationRestrictionList) < 1)\n\t{\n\t\treturn NULL;\n\t}\n\n\tRelationRestriction *relationRestriction =\n\t\t(RelationRestriction *) linitial(filteredRelationRestrictionList);\n\treturn relationRestriction;\n}\n\n\n/*\n * IsNotSafeRestrictionToRecursivelyPlan returns true if the given node\n * is not a safe restriction to be recursivelly planned.\n */\nstatic bool\nIsNotSafeRestrictionToRecursivelyPlan(Node *node)\n{\n\tif (IsA(node, Param) || IsA(node, SubLink) || IsA(node, SubPlan) || IsA(node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAlternativeSubPlan))\n\t{\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n\n/*\n * HasPlaceHolderVar returns true if given node contains any PlaceHolderVar.\n */\nstatic bool\nHasPlaceHolderVar(Node *node)\n{\n\treturn IsA(node, PlaceHolderVar);\n}\n\n\n/*\n * FilterRelationRestrictionContext gets a relation restriction context and\n * set of rte identities. It returns the relation restrictions that that appear\n * in the queryRteIdentities and returns a newly allocated\n * RelationRestrictionContext.\n */\nRelationRestrictionContext *\nFilterRelationRestrictionContext(RelationRestrictionContext *relationRestrictionContext,\n\t\t\t\t\t\t\t\t Relids queryRteIdentities)\n{\n\tRelationRestrictionContext *filteredRestrictionContext =\n\t\tpalloc0(sizeof(RelationRestrictionContext));\n\n\tListCell *relationRestrictionCell = NULL;\n\n\tforeach(relationRestrictionCell, relationRestrictionContext->relationRestrictionList)\n\t{\n\t\tRelationRestriction *relationRestriction =\n\t\t\t(RelationRestriction *) lfirst(relationRestrictionCell);\n\n\t\tint rteIdentity = GetRTEIdentity(relationRestriction->rte);\n\n\t\tif (bms_is_member(rteIdentity, queryRteIdentities))\n\t\t{\n\t\t\tfilteredRestrictionContext->relationRestrictionList =\n\t\t\t\tlappend(filteredRestrictionContext->relationRestrictionList,\n\t\t\t\t\t\trelationRestriction);\n\t\t}\n\t}\n\n\treturn filteredRestrictionContext;\n}\n\n\n/*\n * FilterJoinRestrictionContext gets a join restriction context and\n * set of rte identities. It returns the join restrictions that that appear\n * in the queryRteIdentities and returns a newly allocated\n * JoinRestrictionContext.\n *\n * Note that the join restriction is added to the return context as soon as\n * any range table entry that appear in the join belongs to queryRteIdentities.\n */\nstatic JoinRestrictionContext *\nFilterJoinRestrictionContext(JoinRestrictionContext *joinRestrictionContext, Relids\n\t\t\t\t\t\t\t queryRteIdentities)\n{\n\tJoinRestrictionContext *filtererdJoinRestrictionContext =\n\t\tpalloc0(sizeof(JoinRestrictionContext));\n\n\tListCell *joinRestrictionCell = NULL;\n\n\tforeach(joinRestrictionCell, joinRestrictionContext->joinRestrictionList)\n\t{\n\t\tJoinRestriction *joinRestriction =\n\t\t\t(JoinRestriction *) lfirst(joinRestrictionCell);\n\t\tRangeTblEntry **rangeTableEntries =\n\t\t\tjoinRestriction->plannerInfo->simple_rte_array;\n\t\tint rangeTableArrayLength = joinRestriction->plannerInfo->simple_rel_array_size;\n\n\t\tif (RangeTableArrayContainsAnyRTEIdentities(rangeTableEntries,\n\t\t\t\t\t\t\t\t\t\t\t\t\trangeTableArrayLength,\n\t\t\t\t\t\t\t\t\t\t\t\t\tqueryRteIdentities))\n\t\t{\n\t\t\tfiltererdJoinRestrictionContext->joinRestrictionList = lappend(\n\t\t\t\tfiltererdJoinRestrictionContext->joinRestrictionList,\n\t\t\t\tjoinRestriction);\n\t\t}\n\t}\n\n\t/*\n\t * No need to re calculate has join fields as we are still operating on\n\t * the same query and as these values are calculated per-query basis.\n\t */\n\tfiltererdJoinRestrictionContext->hasSemiJoin = joinRestrictionContext->hasSemiJoin;\n\tfiltererdJoinRestrictionContext->hasOuterJoin = joinRestrictionContext->hasOuterJoin;\n\n\treturn filtererdJoinRestrictionContext;\n}\n\n\n/*\n * RangeTableArrayContainsAnyRTEIdentities returns true if any of the range table entries\n * in rangeTableEntries array is a range table relation specified in queryRteIdentities.\n */\nstatic bool\nRangeTableArrayContainsAnyRTEIdentities(RangeTblEntry **rangeTableEntries, int\n\t\t\t\t\t\t\t\t\t\trangeTableArrayLength, Relids queryRteIdentities)\n{\n\t/* simple_rte_array starts from 1, see plannerInfo struct */\n\tfor (int rteIndex = 1; rteIndex < rangeTableArrayLength; ++rteIndex)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = rangeTableEntries[rteIndex];\n\t\tList *rangeTableRelationList = NULL;\n\t\tListCell *rteRelationCell = NULL;\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t\t/*\n\t\t * In PG18+, planner array simple_rte_array may contain NULL entries\n\t\t * for \"dead relations\". See PG commits 5f6f951 and e9a20e4 for details.\n\t\t */\n\t\tif (rangeTableEntry == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n#endif\n\n\t\t/*\n\t\t * Get list of all RTE_RELATIONs in the given range table entry\n\t\t * (i.e.,rangeTableEntry could be a subquery where we're interested\n\t\t * in relations).\n\t\t */\n\t\tif (rangeTableEntry->rtekind == RTE_SUBQUERY)\n\t\t{\n\t\t\tExtractRangeTableRelationWalker((Node *) rangeTableEntry->subquery,\n\t\t\t\t\t\t\t\t\t\t\t&rangeTableRelationList);\n\t\t}\n\t\telse if (rangeTableEntry->rtekind == RTE_RELATION)\n\t\t{\n\t\t\tExtractRangeTableRelationWalker((Node *) rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t\t&rangeTableRelationList);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* we currently do not accept any other RTE types here */\n\t\t\tcontinue;\n\t\t}\n\n\t\tforeach(rteRelationCell, rangeTableRelationList)\n\t\t{\n\t\t\tRangeTblEntry *rteRelation = (RangeTblEntry *) lfirst(rteRelationCell);\n\n\t\t\tAssert(rteRelation->rtekind == RTE_RELATION);\n\n\t\t\tint rteIdentity = GetRTEIdentity(rteRelation);\n\t\t\tif (bms_is_member(rteIdentity, queryRteIdentities))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * QueryRteIdentities gets a queryTree, find get all the rte identities assigned by\n * us.\n */\nstatic Relids\nQueryRteIdentities(Query *queryTree)\n{\n\tList *rangeTableList = NULL;\n\tListCell *rangeTableCell = NULL;\n\tRelids queryRteIdentities = NULL;\n\n\t/* extract range table entries for simple relations only */\n\tExtractRangeTableRelationWalker((Node *) queryTree, &rangeTableList);\n\n\tforeach(rangeTableCell, rangeTableList)\n\t{\n\t\tRangeTblEntry *rangeTableEntry = (RangeTblEntry *) lfirst(rangeTableCell);\n\n\t\t/* we're only interested in relations */\n\t\tAssert(rangeTableEntry->rtekind == RTE_RELATION);\n\n\t\tint rteIdentity = GetRTEIdentity(rangeTableEntry);\n\n\t\tqueryRteIdentities = bms_add_member(queryRteIdentities, rteIdentity);\n\t}\n\n\treturn queryRteIdentities;\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/shard_pruning.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_pruning.c\n *   Shard pruning related code.\n *\n * The goal of shard pruning is to find a minimal (super)set of shards that\n * need to be queried to find rows matching the expression in a query.\n *\n * In PruneShards we first make a compact representation of the given\n * query logical tree. This tree represents boolean operators and its\n * associated valid constraints (expression nodes) and whether boolean\n * operator has associated unknown constraints. This allows essentially\n * unknown constraints to be replaced by a simple placeholder flag.\n *\n * For example query: WHERE (hash_col IN (1,2)) AND (other_col=1 OR other_col=2)\n * Gets transformed by steps:\n * 1. AND(hash_col IN (1,2), OR(X, X))\n * 2. AND(hash_col IN (1,2), OR(X))\n * 3. AND(hash_col IN (1,2), X)\n * Where X represents any set of unrecognized unprunable constraint(s).\n *\n * Above allows the following pruning machinery to understand that\n * the target shard is determined solely by constraint: hash_col IN (1,2).\n * Here it does not matter what X is as its ANDed by a valid constraint.\n * Pruning machinery will fail, returning all shards, if it encounters\n * eg. OR(hash_col=1, X) as this condition does not limit the target shards.\n *\n * PruneShards secondly computes a simplified disjunctive normal form (DNF)\n * of the logical tree as a list of pruning instances. Each pruning instance\n * contains all AND-ed constraints on the partition column. An OR expression\n * will result in two or more new pruning instances being added for the\n * subexpressions. The \"parent\" instance is marked isPartial and ignored\n * during pruning.\n *\n * We use the distributive property for constraints of the form P AND (Q OR R)\n * to rewrite it to (P AND Q) OR (P AND R) by copying constraints from parent\n * to \"child\" pruning instances. However, we do not distribute nested\n * expressions. While (P OR Q) AND (R OR S) is logically equivalent to (P AND\n * R) OR (P AND S) OR (Q AND R) OR (Q AND S), in our implementation it becomes\n * P OR Q OR R OR S. This is acceptable since this will always result in a\n * superset of shards. If this proves to be a issue in practice, a more\n * complete algorithm could be implemented.\n *\n * We then evaluate each non-partial pruning instance in the disjunction\n * through the following, increasingly expensive, steps:\n *\n * 1) If there is a constant equality constraint on the partition column, and\n *    no overlapping shards exist, find the shard interval in which the\n *    constant falls\n *\n * 2) If there is a hash range constraint on the partition column, find the\n *    shard interval matching the range\n *\n * 3) If there are range constraints (e.g. (a > 0 AND a < 10)) on the\n *    partition column, find the shard intervals that overlap with the range\n *\n * 4) If there are overlapping shards, exhaustively search all shards that are\n *    not excluded by constraints\n *\n * Finally, the union of the shards found by each pruning instance is\n * returned.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"access/nbtree.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/planner.h\"\n#include \"parser/parse_coerce.h\"\n#include \"utils/arrayaccess.h\"\n#include \"utils/catcache.h\"\n#include \"utils/fmgrprotos.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/ruleutils.h\"\n\n#include \"pg_version_constants.h\"\n#if PG_VERSION_NUM >= PG_VERSION_18\ntypedef OpIndexInterpretation OpBtreeInterpretation;\n#endif\n\n\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/*\n * Tree node for compact representation of the given query logical tree.\n * Represent a single boolean operator node and its associated\n * valid constraints (expression nodes) and invalid constraint flag.\n */\ntypedef struct PruningTreeNode\n{\n\t/* Indicates is this AND/OR boolean operator */\n\tBoolExprType boolop;\n\n\t/* Does this boolean operator have unknown/unprunable constraint(s) */\n\tbool hasInvalidConstraints;\n\n\t/* List of recognized valid prunable constraints of this boolean opearator */\n\tList *validConstraints;\n\n\t/* Child boolean producing operators. Parents are always different from their children */\n\tList *childBooleanNodes;\n} PruningTreeNode;\n\n/*\n * Context used for expression_tree_walker\n */\ntypedef struct PruningTreeBuildContext\n{\n\tVar *partitionColumn;\n\tPruningTreeNode *current;\n} PruningTreeBuildContext;\n\n/*\n * A pruning instance is a set of ANDed constraints on a partition key.\n */\ntypedef struct PruningInstance\n{\n\t/* Does this instance contain any prunable expressions? */\n\tbool hasValidConstraint;\n\n\t/*\n\t * This constraint never evaluates to true, i.e. pruning does not have to\n\t * be performed.\n\t */\n\tbool evaluatesToFalse;\n\n\t/*\n\t * Constraints on the partition column value. If multiple values are\n\t * found the more restrictive one should be stored here. Even for\n\t * a hash-partitioned table, actual column-values are stored here, *not*\n\t * hashed values.\n\t */\n\tConst *lessConsts;\n\tConst *lessEqualConsts;\n\tConst *equalConsts;\n\tConst *greaterEqualConsts;\n\tConst *greaterConsts;\n\n\t/*\n\t * Constraint using a pre-hashed column value. The constant will store the\n\t * hashed value, not the original value of the restriction.\n\t */\n\tConst *hashedEqualConsts;\n\n\t/*\n\t * Has this PruningInstance been added to\n\t * ClauseWalkerContext->pruningInstances? This is not done immediately,\n\t * but the first time a constraint (independent of us being able to handle\n\t * that constraint) is found.\n\t */\n\tbool addedToPruningInstances;\n\n\t/*\n\t * When OR clauses are found, the non-ORed part (think of a < 3 AND (a > 5\n\t * OR a > 7)) of the expression is stored in one PruningInstance which is\n\t * then copied for the ORed expressions. The original is marked as\n\t * isPartial, to avoid being used for pruning.\n\t */\n\tbool isPartial;\n} PruningInstance;\n\n\n/*\n * Partial instances that need to be finished building. This is used to\n * collect all ANDed restrictions, before looking into ORed expressions.\n */\ntypedef struct PendingPruningInstance\n{\n\tPruningInstance *instance;\n\tPruningTreeNode *continueAt;\n} PendingPruningInstance;\n\ntypedef union \\\n{ \\\n\tFunctionCallInfoBaseData fcinfo; \\\n\t/* ensure enough space for nargs args is available */ \\\n\tchar fcinfo_data[SizeForFunctionCallInfo(2)]; \\\n} FunctionCall2InfoData;\n\n/*\n * We also ignore this warning in ./configure, but that's not always enough.\n * The flags that are used during compilation by ./configure are determined by\n * the compiler support it detects. This is usually GCC. This warning is only\n * present in clang. So it would normally be fine to not use it with GCC. The\n * problem is that clang is used to compile the JIT bitcode when postgres is\n * compiled with -with-llvm. So in the end both clang and GCC are used to\n * compile the project.\n *\n * So the flag is not provided on the command line, because ./configure notices\n * that GCC doesn't support it. But this warning persists when compiling the\n * bitcode. So that's why we ignore it here explicitly.\n */\n#ifdef __clang__\n#pragma clang diagnostic ignored \"-Wgnu-variable-sized-type-not-at-end\"\n#endif /* __clang__ */\n\n/*\n * Data necessary to perform a single PruneShards().\n */\ntypedef struct ClauseWalkerContext\n{\n\tVar *partitionColumn;\n\tchar partitionMethod;\n\n\t/* ORed list of pruning targets */\n\tList *pruningInstances;\n\n\t/*\n\t * Partially built PruningInstances, that need to be completed by doing a\n\t * separate PrunableExpressionsWalker() pass.\n\t */\n\tList *pendingInstances;\n\n\t/* PruningInstance currently being built, all eligible constraints are added here */\n\tPruningInstance *currentPruningInstance;\n\n\t/*\n\t * Information about function calls we need to perform. Re-using the same\n\t * FunctionCall2InfoData, instead of using FunctionCall2Coll, is often\n\t * cheaper.\n\t */\n\tFunctionCall2InfoData compareValueFunctionCall;\n\tFunctionCall2InfoData compareIntervalFunctionCall;\n} ClauseWalkerContext;\n\nstatic bool BuildPruningTree(Node *node, PruningTreeBuildContext *context);\nstatic void SimplifyPruningTree(PruningTreeNode *node, PruningTreeNode *parent);\nstatic void PrunableExpressions(PruningTreeNode *node, ClauseWalkerContext *context);\nstatic void PrunableExpressionsWalker(PruningTreeNode *node,\n\t\t\t\t\t\t\t\t\t  ClauseWalkerContext *context);\nstatic bool IsValidPartitionKeyRestriction(OpExpr *opClause);\nstatic void AddPartitionKeyRestrictionToInstance(ClauseWalkerContext *context,\n\t\t\t\t\t\t\t\t\t\t\t\t OpExpr *opClause, Var *varClause,\n\t\t\t\t\t\t\t\t\t\t\t\t Const *constantClause);\nstatic void AddSAOPartitionKeyRestrictionToInstance(ClauseWalkerContext *context,\n\t\t\t\t\t\t\t\t\t\t\t\t\tScalarArrayOpExpr *\n\t\t\t\t\t\t\t\t\t\t\t\t\tarrayOperatorExpression);\nstatic bool SAORestrictions(ScalarArrayOpExpr *arrayOperatorExpression,\n\t\t\t\t\t\t\tVar *partitionColumn,\n\t\t\t\t\t\t\tList **requestedRestrictions);\nstatic void ErrorTypesDontMatch(Oid firstType, Oid firstCollId, Oid secondType,\n\t\t\t\t\t\t\t\tOid secondCollId);\nstatic bool IsValidHashRestriction(OpExpr *opClause);\nstatic void AddHashRestrictionToInstance(ClauseWalkerContext *context, OpExpr *opClause,\n\t\t\t\t\t\t\t\t\t\t Var *varClause, Const *constantClause);\nstatic void AddNewConjuction(ClauseWalkerContext *context, PruningTreeNode *node);\nstatic PruningInstance * CopyPartialPruningInstance(PruningInstance *sourceInstance);\nstatic List * ShardArrayToList(ShardInterval **shardArray, int length);\nstatic List * DeepCopyShardIntervalList(List *originalShardIntervalList);\nstatic int PerformValueCompare(FunctionCallInfo compareFunctionCall, Datum a,\n\t\t\t\t\t\t\t   Datum b);\nstatic int PerformCompare(FunctionCallInfo compareFunctionCall);\n\nstatic List * PruneOne(CitusTableCacheEntry *cacheEntry, ClauseWalkerContext *context,\n\t\t\t\t\t   PruningInstance *prune);\nstatic List * PruneWithBoundaries(CitusTableCacheEntry *cacheEntry,\n\t\t\t\t\t\t\t\t  ClauseWalkerContext *context,\n\t\t\t\t\t\t\t\t  PruningInstance *prune);\nstatic List * ExhaustivePrune(CitusTableCacheEntry *cacheEntry,\n\t\t\t\t\t\t\t  ClauseWalkerContext *context,\n\t\t\t\t\t\t\t  PruningInstance *prune);\nstatic bool ExhaustivePruneOne(ShardInterval *curInterval,\n\t\t\t\t\t\t\t   ClauseWalkerContext *context,\n\t\t\t\t\t\t\t   PruningInstance *prune);\nstatic int UpperShardBoundary(Datum partitionColumnValue,\n\t\t\t\t\t\t\t  ShardInterval **shardIntervalCache,\n\t\t\t\t\t\t\t  int shardCount, FunctionCallInfo compareFunction,\n\t\t\t\t\t\t\t  bool includeMin);\nstatic int LowerShardBoundary(Datum partitionColumnValue,\n\t\t\t\t\t\t\t  ShardInterval **shardIntervalCache,\n\t\t\t\t\t\t\t  int shardCount, FunctionCallInfo compareFunction,\n\t\t\t\t\t\t\t  bool includeMax);\nstatic PruningTreeNode * CreatePruningNode(BoolExprType boolop);\nstatic OpExpr * SAORestrictionArrayEqualityOp(ScalarArrayOpExpr *arrayOperatorExpression,\n\t\t\t\t\t\t\t\t\t\t\t  Var *partitionColumn);\nstatic void DebugLogNode(char *fmt, Node *node, List *deparseCtx);\nstatic void DebugLogPruningInstance(PruningInstance *pruning, List *deparseCtx);\nstatic int ConstraintCount(PruningTreeNode *node);\n\n\n/*\n * PruneShards returns all shards from a distributed table that cannot be\n * proven to be eliminated by whereClauseList.\n *\n * For non-distributed tables such as reference table, the function\n * simply returns the single shard that the table has.\n *\n * When there is a single <partition column> = <constant> filter in the where\n * clause list, the constant is written to the partitionValueConst pointer.\n */\nList *\nPruneShards(Oid relationId, Index rangeTableId, List *whereClauseList,\n\t\t\tConst **partitionValueConst)\n{\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\tint shardCount = cacheEntry->shardIntervalArrayLength;\n\tchar partitionMethod = cacheEntry->partitionMethod;\n\tClauseWalkerContext context = { 0 };\n\tListCell *pruneCell;\n\tList *prunedList = NIL;\n\tbool foundRestriction = false;\n\tbool foundPartitionColumnValue = false;\n\tConst *singlePartitionValueConst = NULL;\n\n\t/* there are no shards to return */\n\tif (shardCount == 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* always return empty result if WHERE clause is of the form: false (AND ..) */\n\tif (ContainsFalseClause(whereClauseList))\n\t{\n\t\treturn NIL;\n\t}\n\n\t/* short circuit for non-distributed tables such as reference table */\n\tif (!HasDistributionKeyCacheEntry(cacheEntry))\n\t{\n\t\tprunedList = ShardArrayToList(cacheEntry->sortedShardIntervalArray,\n\t\t\t\t\t\t\t\t\t  cacheEntry->shardIntervalArrayLength);\n\t\treturn DeepCopyShardIntervalList(prunedList);\n\t}\n\n\n\tcontext.partitionMethod = partitionMethod;\n\tcontext.partitionColumn = PartitionColumn(relationId, rangeTableId);\n\tcontext.currentPruningInstance = palloc0(sizeof(PruningInstance));\n\n\tif (cacheEntry->shardIntervalCompareFunction)\n\t{\n\t\t/* initiate function call info once (allows comparators to cache metadata) */\n\t\tInitFunctionCallInfoData(*(FunctionCallInfo) &\n\t\t\t\t\t\t\t\t context.compareIntervalFunctionCall,\n\t\t\t\t\t\t\t\t cacheEntry->shardIntervalCompareFunction,\n\t\t\t\t\t\t\t\t 2, cacheEntry->partitionColumn->varcollid, NULL, NULL);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"shard pruning not possible without \"\n\t\t\t\t\t\t\t   \"a shard interval comparator\")));\n\t}\n\n\tif (cacheEntry->shardColumnCompareFunction)\n\t{\n\t\t/* initiate function call info once (allows comparators to cache metadata) */\n\t\tInitFunctionCallInfoData(*(FunctionCallInfo) &\n\t\t\t\t\t\t\t\t context.compareValueFunctionCall,\n\t\t\t\t\t\t\t\t cacheEntry->shardColumnCompareFunction,\n\t\t\t\t\t\t\t\t 2, cacheEntry->partitionColumn->varcollid, NULL, NULL);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errmsg(\"shard pruning not possible without \"\n\t\t\t\t\t\t\t   \"a partition column comparator\")));\n\t}\n\n\tPruningTreeNode *tree = CreatePruningNode(AND_EXPR);\n\n\tPruningTreeBuildContext treeBuildContext = { 0 };\n\ttreeBuildContext.current = tree;\n\ttreeBuildContext.partitionColumn = PartitionColumn(relationId, rangeTableId);\n\n\t/* Build logical tree of prunable restrictions and invalid restrictions */\n\tBuildPruningTree((Node *) whereClauseList, &treeBuildContext);\n\n\t/* Simplify logic tree of prunable restrictions */\n\tSimplifyPruningTree(tree, NULL);\n\n\t/* Figure out what we can prune on */\n\tPrunableExpressions(tree, &context);\n\n\tList *debugLoggedPruningInstances = NIL;\n\n\t/*\n\t * Prune using each of the PrunableInstances we found, and OR results\n\t * together.\n\t */\n\tforeach(pruneCell, context.pruningInstances)\n\t{\n\t\tPruningInstance *prune = (PruningInstance *) lfirst(pruneCell);\n\n\t\t/*\n\t\t * If this is a partial instance, a fully built one has also been\n\t\t * added. Skip.\n\t\t */\n\t\tif (prune->isPartial)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * If the current instance has no prunable expressions, we'll have to\n\t\t * return all shards. No point in continuing pruning in that case.\n\t\t */\n\t\tif (!prune->hasValidConstraint)\n\t\t{\n\t\t\tfoundRestriction = false;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (context.partitionMethod == DISTRIBUTE_BY_HASH)\n\t\t{\n\t\t\tif (!prune->evaluatesToFalse && !prune->equalConsts &&\n\t\t\t\t!prune->hashedEqualConsts)\n\t\t\t{\n\t\t\t\t/* if hash-partitioned and no equals constraints, return all shards */\n\t\t\t\tfoundRestriction = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (partitionValueConst != NULL && prune->equalConsts != NULL)\n\t\t\t{\n\t\t\t\tif (!foundPartitionColumnValue)\n\t\t\t\t{\n\t\t\t\t\t/* remember the partition column value */\n\t\t\t\t\tsinglePartitionValueConst = prune->equalConsts;\n\t\t\t\t\tfoundPartitionColumnValue = true;\n\t\t\t\t}\n\t\t\t\telse if (singlePartitionValueConst == NULL)\n\t\t\t\t{\n\t\t\t\t\t/* already found multiple partition column values */\n\t\t\t\t}\n\t\t\t\telse if (!equal(prune->equalConsts, singlePartitionValueConst))\n\t\t\t\t{\n\t\t\t\t\t/* found multiple partition column values */\n\t\t\t\t\tsinglePartitionValueConst = NULL;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tList *pruneOneList = PruneOne(cacheEntry, &context, prune);\n\n\t\tif (prunedList)\n\t\t{\n\t\t\t/*\n\t\t\t * We can use list_union_ptr, which is a lot faster than doing\n\t\t\t * comparing shards by value, because all the ShardIntervals are\n\t\t\t * guaranteed to be from\n\t\t\t * CitusTableCacheEntry->sortedShardIntervalArray (thus having the\n\t\t\t * same pointer values).\n\t\t\t */\n\t\t\tprunedList = list_union_ptr(prunedList, pruneOneList);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tprunedList = pruneOneList;\n\t\t}\n\t\tfoundRestriction = true;\n\n\t\tif (IsLoggableLevel(DEBUG3) && pruneOneList)\n\t\t{\n\t\t\tdebugLoggedPruningInstances = lappend(debugLoggedPruningInstances, prune);\n\t\t}\n\t}\n\n\t/* found no valid restriction, build list of all shards */\n\tif (!foundRestriction)\n\t{\n\t\tprunedList = ShardArrayToList(cacheEntry->sortedShardIntervalArray,\n\t\t\t\t\t\t\t\t\t  cacheEntry->shardIntervalArrayLength);\n\t}\n\n\tif (IsLoggableLevel(DEBUG3))\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\t\tif (foundRestriction && debugLoggedPruningInstances != NIL)\n\t\t{\n\t\t\tList *deparseCtx = deparse_context_for(\"unknown\", relationId);\n\t\t\tforeach(pruneCell, debugLoggedPruningInstances)\n\t\t\t{\n\t\t\t\tPruningInstance *prune = (PruningInstance *) lfirst(pruneCell);\n\t\t\t\tDebugLogPruningInstance(prune, deparseCtx);\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(DEBUG3, (errmsg(\"no shard pruning constraints on %s found\",\n\t\t\t\t\t\t\t\t\trelationName)));\n\t\t}\n\n\t\tereport(DEBUG3, (errmsg(\"shard count after pruning for %s: %d\", relationName,\n\t\t\t\t\t\t\t\tlist_length(prunedList))));\n\t}\n\n\t/* if requested, copy the partition value constant */\n\tif (partitionValueConst != NULL)\n\t{\n\t\tif (singlePartitionValueConst != NULL)\n\t\t{\n\t\t\t*partitionValueConst = copyObject(singlePartitionValueConst);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t*partitionValueConst = NULL;\n\t\t}\n\t}\n\n\t/*\n\t * Deep copy list, so it's independent of the CitusTableCacheEntry\n\t * contents.\n\t */\n\treturn DeepCopyShardIntervalList(prunedList);\n}\n\n\n/*\n * IsValidConditionNode checks whether node is a valid constraint for pruning.\n */\nstatic bool\nIsValidConditionNode(Node *node, Var *partitionColumn)\n{\n\tif (IsA(node, OpExpr))\n\t{\n\t\tOpExpr *opClause = (OpExpr *) node;\n\t\tVar *varClause = NULL;\n\t\tif (VarConstOpExprClause(opClause, &varClause, NULL))\n\t\t{\n\t\t\tif (equal(varClause, partitionColumn))\n\t\t\t{\n\t\t\t\treturn IsValidPartitionKeyRestriction(opClause);\n\t\t\t}\n\t\t\telse if (varClause->varattno == RESERVED_HASHED_COLUMN_ID)\n\t\t\t{\n\t\t\t\treturn IsValidHashRestriction(opClause);\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\telse if (IsA(node, ScalarArrayOpExpr))\n\t{\n\t\tScalarArrayOpExpr *arrayOperatorExpression = (ScalarArrayOpExpr *) node;\n\t\treturn SAORestrictions(arrayOperatorExpression, partitionColumn, NULL);\n\t}\n\telse\n\t{\n\t\treturn false;\n\t}\n}\n\n\n/*\n * BuildPruningTree builds a logical tree of constraints for pruning.\n */\nstatic bool\nBuildPruningTree(Node *node, PruningTreeBuildContext *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, List))\n\t{\n\t\treturn expression_tree_walker(node, BuildPruningTree, context);\n\t}\n\telse if (IsA(node, BoolExpr))\n\t{\n\t\tBoolExpr *boolExpr = (BoolExpr *) node;\n\n\t\tif (boolExpr->boolop == NOT_EXPR)\n\t\t{\n\t\t\t/*\n\t\t\t * With Var-Const conditions we should not encounter NOT_EXPR nodes.\n\t\t\t * Postgres standard planner applies De Morgan's laws to remove them.\n\t\t\t * We still encounter them with subqueries inside NOT, for example with:\n\t\t\t * WHERE id NOT IN (SELECT id FROM something).\n\t\t\t * We treat these as invalid constraints for pruning when we encounter them.\n\t\t\t */\n\t\t\tcontext->current->hasInvalidConstraints = true;\n\n\t\t\treturn false;\n\t\t}\n\t\telse if (context->current->boolop != boolExpr->boolop)\n\t\t{\n\t\t\tPruningTreeNode *child = CreatePruningNode(boolExpr->boolop);\n\n\t\t\tcontext->current->childBooleanNodes = lappend(\n\t\t\t\tcontext->current->childBooleanNodes, child);\n\n\t\t\tPruningTreeBuildContext newContext = { 0 };\n\t\t\tnewContext.partitionColumn = context->partitionColumn;\n\t\t\tnewContext.current = child;\n\n\t\t\treturn expression_tree_walker((Node *) boolExpr->args,\n\t\t\t\t\t\t\t\t\t\t  BuildPruningTree, &newContext);\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn expression_tree_walker(node, BuildPruningTree, context);\n\t\t}\n\t}\n\telse if (IsValidConditionNode(node, context->partitionColumn))\n\t{\n\t\tcontext->current->validConstraints = lappend(context->current->validConstraints,\n\t\t\t\t\t\t\t\t\t\t\t\t\t node);\n\n\t\treturn false;\n\t}\n\telse\n\t{\n\t\tcontext->current->hasInvalidConstraints = true;\n\n\t\treturn false;\n\t}\n}\n\n\n/*\n * SimplifyPruningTree reduces logical tree of valid and invalid constraints for pruning.\n * The goal is to remove any node having just a single constraint associated with it.\n * This constraint is assigned to the parent logical node.\n *\n * For example 'AND(hash_col = 1, OR(X))' gets simplified to 'AND(hash_col = 1, X)',\n * where X is any unknown condition.\n */\nstatic void\nSimplifyPruningTree(PruningTreeNode *node, PruningTreeNode *parent)\n{\n\t/* Copy list of children as its mutated inside the loop */\n\tList *childBooleanNodes = list_copy(node->childBooleanNodes);\n\n\tListCell *cell;\n\tforeach(cell, childBooleanNodes)\n\t{\n\t\tPruningTreeNode *child = (PruningTreeNode *) lfirst(cell);\n\t\tSimplifyPruningTree(child, node);\n\t}\n\n\tif (!parent)\n\t{\n\t\t/* Root is always ANDed expressions */\n\t\tAssert(node->boolop == AND_EXPR);\n\t\treturn;\n\t}\n\n\t/* Boolean operator with single (recognized/unknown) constraint gets simplified */\n\tif (ConstraintCount(node) <= 1)\n\t{\n\t\tAssert(node->childBooleanNodes == NIL);\n\t\tparent->validConstraints = list_concat(parent->validConstraints,\n\t\t\t\t\t\t\t\t\t\t\t   node->validConstraints);\n\t\tparent->hasInvalidConstraints = parent->hasInvalidConstraints ||\n\t\t\t\t\t\t\t\t\t\tnode->hasInvalidConstraints;\n\n\t\t/* Remove current node from parent. Its constraint was assigned to the parent above */\n\t\tparent->childBooleanNodes = list_delete_ptr(parent->childBooleanNodes, node);\n\t}\n}\n\n\n/*\n * ContainsFalseClause returns whether the flattened where clause list\n * contains false as a clause.\n */\nbool\nContainsFalseClause(List *whereClauseList)\n{\n\tbool containsFalseClause = false;\n\tListCell *clauseCell = NULL;\n\n\tforeach(clauseCell, whereClauseList)\n\t{\n\t\tNode *clause = (Node *) lfirst(clauseCell);\n\n\t\tif (IsA(clause, Const))\n\t\t{\n\t\t\tConst *constant = (Const *) clause;\n\t\t\tif (constant->consttype == BOOLOID && !DatumGetBool(constant->constvalue))\n\t\t\t{\n\t\t\t\tcontainsFalseClause = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn containsFalseClause;\n}\n\n\n/*\n * PrunableExpressions builds a list of all prunable expressions in node,\n * storing them in context->pruningInstances.\n */\nstatic void\nPrunableExpressions(PruningTreeNode *tree, ClauseWalkerContext *context)\n{\n\t/*\n\t * Build initial list of prunable expressions. As long as only,\n\t * implicitly or explicitly, ANDed expressions are found, this perform a\n\t * depth-first search. When an ORed expression is found, the current\n\t * PruningInstance is added to context->pruningInstances (once for each\n\t * ORed expression), then the tree-traversal is continued without\n\t * recursing. Once at the top-level again, we'll process all pending\n\t * expressions - that allows us to find all ANDed expressions, before\n\t * recursing into an ORed expression.\n\t */\n\tPrunableExpressionsWalker(tree, context);\n\n\t/*\n\t * Process all pending instances. While processing, new ones might be\n\t * added to the list, so don't use foreach().\n\t *\n\t * Check the places in PruningInstanceWalker that push onto\n\t * context->pendingInstances why construction of the PruningInstance might\n\t * be pending.\n\t *\n\t * We copy the partial PruningInstance, and continue adding information by\n\t * calling PrunableExpressionsWalker() on the copy, continuing at the\n\t * node stored in PendingPruningInstance->continueAt.\n\t */\n\twhile (context->pendingInstances != NIL)\n\t{\n\t\tPendingPruningInstance *instance =\n\t\t\t(PendingPruningInstance *) linitial(context->pendingInstances);\n\t\tPruningInstance *newPrune = CopyPartialPruningInstance(instance->instance);\n\n\t\tcontext->pendingInstances = list_delete_first(context->pendingInstances);\n\n\t\tcontext->currentPruningInstance = newPrune;\n\t\tPrunableExpressionsWalker(instance->continueAt, context);\n\t\tcontext->currentPruningInstance = NULL;\n\t}\n}\n\n\n/*\n * PrunableExpressionsWalker() is the main work horse for\n * PrunableExpressions().\n */\nstatic void\nPrunableExpressionsWalker(PruningTreeNode *node, ClauseWalkerContext *context)\n{\n\tListCell *cell = NULL;\n\n\tif (node == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tif (node->boolop == OR_EXPR)\n\t{\n\t\t/*\n\t\t * \"Queue\" partial pruning instances. This is used to convert\n\t\t * expressions like (A AND (B OR C) AND D) into (A AND B AND D),\n\t\t * (A AND C AND D), with A, B, C, D being restrictions. When the\n\t\t * OR is encountered, a reference to the partially built\n\t\t * PruningInstance (containing A at this point), is added to\n\t\t * context->pendingInstances once for B and once for C. Once a\n\t\t * full tree-walk completed, PrunableExpressions() will complete\n\t\t * the pending instances, which'll now also know about restriction\n\t\t * D, by calling PrunableExpressionsWalker() once for B and once\n\t\t * for C.\n\t\t */\n\n\t\tif (node->hasInvalidConstraints)\n\t\t{\n\t\t\tPruningTreeNode *child = CreatePruningNode(AND_EXPR);\n\t\t\tchild->hasInvalidConstraints = true;\n\n\t\t\tAddNewConjuction(context, child);\n\t\t}\n\n\t\tforeach(cell, node->validConstraints)\n\t\t{\n\t\t\tNode *constraint = (Node *) lfirst(cell);\n\n\t\t\tPruningTreeNode *child = CreatePruningNode(AND_EXPR);\n\t\t\tchild->validConstraints = list_make1(constraint);\n\n\t\t\tAddNewConjuction(context, child);\n\t\t}\n\n\t\tforeach(cell, node->childBooleanNodes)\n\t\t{\n\t\t\tPruningTreeNode *child = (PruningTreeNode *) lfirst(cell);\n\t\t\tAssert(child->boolop == AND_EXPR);\n\t\t\tAddNewConjuction(context, child);\n\t\t}\n\n\t\treturn;\n\t}\n\n\tAssert(node->boolop == AND_EXPR);\n\n\tforeach(cell, node->validConstraints)\n\t{\n\t\tNode *constraint = (Node *) lfirst(cell);\n\n\t\tif (IsA(constraint, OpExpr))\n\t\t{\n\t\t\tOpExpr *opClause = (OpExpr *) constraint;\n\t\t\tPruningInstance *prune = context->currentPruningInstance;\n\t\t\tVar *varClause = NULL;\n\t\t\tConst *constantClause = NULL;\n\n\t\t\tif (!prune->addedToPruningInstances)\n\t\t\t{\n\t\t\t\tcontext->pruningInstances = lappend(context->pruningInstances, prune);\n\t\t\t\tprune->addedToPruningInstances = true;\n\t\t\t}\n\n\t\t\tif (VarConstOpExprClause(opClause, &varClause, &constantClause))\n\t\t\t{\n\t\t\t\tif (equal(varClause, context->partitionColumn))\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Found a restriction on the partition column itself. Update the\n\t\t\t\t\t * current constraint with the new information.\n\t\t\t\t\t */\n\t\t\t\t\tAddPartitionKeyRestrictionToInstance(context, opClause, varClause,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t constantClause);\n\t\t\t\t}\n\t\t\t\telse if (varClause->varattno == RESERVED_HASHED_COLUMN_ID)\n\t\t\t\t{\n\t\t\t\t\t/*\n\t\t\t\t\t * Found restriction that directly specifies the boundaries of a\n\t\t\t\t\t * hashed column.\n\t\t\t\t\t */\n\t\t\t\t\tAddHashRestrictionToInstance(context, opClause, varClause,\n\t\t\t\t\t\t\t\t\t\t\t\t constantClause);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t/* We encounter here only valid constraints */\n\t\t\t\t\tAssert(false);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/* We encounter here only valid constraints */\n\t\t\t\tAssert(false);\n\t\t\t}\n\t\t}\n\t\telse if (IsA(constraint, ScalarArrayOpExpr))\n\t\t{\n\t\t\tScalarArrayOpExpr *arrayOperatorExpression = (ScalarArrayOpExpr *) constraint;\n\t\t\tAddSAOPartitionKeyRestrictionToInstance(context, arrayOperatorExpression);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* We encounter here only valid constraints */\n\t\t\tAssert(false);\n\t\t}\n\t}\n\n\tif (node->hasInvalidConstraints)\n\t{\n\t\tPruningInstance *prune = context->currentPruningInstance;\n\n\t\t/*\n\t\t * Mark unknown expression as added, so we'll fail pruning if there's no ANDed\n\t\t * restrictions that we know how to deal with.\n\t\t */\n\t\tif (!prune->addedToPruningInstances)\n\t\t{\n\t\t\tcontext->pruningInstances = lappend(context->pruningInstances, prune);\n\t\t\tprune->addedToPruningInstances = true;\n\t\t}\n\t}\n\n\tforeach(cell, node->childBooleanNodes)\n\t{\n\t\tPruningTreeNode *child = (PruningTreeNode *) lfirst(cell);\n\t\tAssert(child->boolop == OR_EXPR);\n\t\tPrunableExpressionsWalker(child, context);\n\t}\n}\n\n\n/*\n * VarConstOpExprClause check whether an expression is a valid comparison of a Var to a Const.\n * Also obtaining the var with constant when valid.\n */\nbool\nVarConstOpExprClause(OpExpr *opClause, Var **varClause, Const **constantClause)\n{\n\tVar *foundVarClause = NULL;\n\tConst *foundConstantClause = NULL;\n\n\tNode *leftOperand;\n\tNode *rightOperand;\n\tif (!BinaryOpExpression((Expr *) opClause, &leftOperand, &rightOperand))\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(rightOperand, Const) && IsA(leftOperand, Var))\n\t{\n\t\tfoundVarClause = (Var *) leftOperand;\n\t\tfoundConstantClause = (Const *) rightOperand;\n\t}\n\telse if (IsA(leftOperand, Const) && IsA(rightOperand, Var))\n\t{\n\t\tfoundVarClause = (Var *) rightOperand;\n\t\tfoundConstantClause = (Const *) leftOperand;\n\t}\n\telse\n\t{\n\t\treturn false;\n\t}\n\n\tif (varClause)\n\t{\n\t\t*varClause = foundVarClause;\n\t}\n\tif (constantClause)\n\t{\n\t\t*constantClause = foundConstantClause;\n\t}\n\treturn true;\n}\n\n\n/*\n * AddSAOPartitionKeyRestrictionToInstance adds partcol = arrayelem operator\n * restriction to the current pruning instance for each element of the array. These\n * restrictions are added to pruning instance to prune shards based on IN/=ANY\n * constraints.\n */\nstatic void\nAddSAOPartitionKeyRestrictionToInstance(ClauseWalkerContext *context,\n\t\t\t\t\t\t\t\t\t\tScalarArrayOpExpr *arrayOperatorExpression)\n{\n\tList *restrictions = NULL;\n\tbool validSAORestriction PG_USED_FOR_ASSERTS_ONLY =\n\t\tSAORestrictions(arrayOperatorExpression, context->partitionColumn, &restrictions);\n\n\tAssert(validSAORestriction);\n\n\tPruningTreeNode *node = CreatePruningNode(OR_EXPR);\n\tnode->validConstraints = restrictions;\n\tAddNewConjuction(context, node);\n}\n\n\n/*\n * SAORestrictions checks whether an SAO constraint is valid.\n * Also obtains equality restrictions.\n */\nstatic bool\nSAORestrictions(ScalarArrayOpExpr *arrayOperatorExpression, Var *partitionColumn,\n\t\t\t\tList **requestedRestrictions)\n{\n\tNode *leftOpExpression = linitial(arrayOperatorExpression->args);\n\tNode *strippedLeftOpExpression = strip_implicit_coercions(leftOpExpression);\n\tbool usingEqualityOperator = OperatorImplementsEquality(\n\t\tarrayOperatorExpression->opno);\n\tExpr *arrayArgument = (Expr *) lsecond(arrayOperatorExpression->args);\n\n\t/* checking for partcol = ANY(const, value, s); or partcol IN (const,b,c); */\n\tif (usingEqualityOperator && strippedLeftOpExpression != NULL &&\n\t\tequal(strippedLeftOpExpression, partitionColumn) &&\n\t\tIsA(arrayArgument, Const))\n\t{\n\t\tConst *arrayConst = (Const *) arrayArgument;\n\t\tint16 typlen = 0;\n\t\tbool typbyval = false;\n\t\tchar typalign = '\\0';\n\t\tDatum arrayElement = 0;\n\t\tDatum inArray = arrayConst->constvalue;\n\t\tbool isNull = false;\n\t\tbool foundValid = false;\n\n\t\t/* check for the NULL right-hand expression*/\n\t\tif (inArray == 0)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tArrayType *array = DatumGetArrayTypeP(arrayConst->constvalue);\n\n\t\t/* get the necessary information from array type to iterate over it */\n\t\tOid elementType = ARR_ELEMTYPE(array);\n\t\tget_typlenbyvalalign(elementType,\n\t\t\t\t\t\t\t &typlen,\n\t\t\t\t\t\t\t &typbyval,\n\t\t\t\t\t\t\t &typalign);\n\n\t\t/* Iterate over the righthand array of expression */\n\t\tArrayIterator arrayIterator = array_create_iterator(array, 0, NULL);\n\t\twhile (array_iterate(arrayIterator, &arrayElement, &isNull))\n\t\t{\n\t\t\tif (isNull)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We can ignore IN (NULL) clauses because a value is never\n\t\t\t\t * equal to NULL.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfoundValid = true;\n\n\t\t\tif (requestedRestrictions)\n\t\t\t{\n\t\t\t\tConst *constElement = makeConst(elementType, -1,\n\t\t\t\t\t\t\t\t\t\t\t\tarrayConst->constcollid,\n\t\t\t\t\t\t\t\t\t\t\t\ttyplen, arrayElement,\n\t\t\t\t\t\t\t\t\t\t\t\tisNull, typbyval);\n\n\t\t\t\t/* build partcol = arrayelem operator */\n\t\t\t\tOpExpr *arrayEqualityOp = SAORestrictionArrayEqualityOp(\n\t\t\t\t\tarrayOperatorExpression,\n\t\t\t\t\tpartitionColumn);\n\t\t\t\tarrayEqualityOp->args = list_make2(strippedLeftOpExpression,\n\t\t\t\t\t\t\t\t\t\t\t\t   constElement);\n\n\t\t\t\t*requestedRestrictions = lappend(*requestedRestrictions, arrayEqualityOp);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn foundValid;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * AddNewConjuction adds the OpExpr to pending instance list of context\n * as conjunction as partial instance.\n */\nstatic void\nAddNewConjuction(ClauseWalkerContext *context, PruningTreeNode *node)\n{\n\tPendingPruningInstance *instance = palloc0(sizeof(PendingPruningInstance));\n\n\tinstance->instance = context->currentPruningInstance;\n\tinstance->continueAt = node;\n\n\t/*\n\t * Signal that this instance is not to be used for pruning on\n\t * its own. Once the pending instance is processed, it'll be\n\t * used.\n\t */\n\tinstance->instance->isPartial = true;\n\tcontext->pendingInstances = lappend(context->pendingInstances, instance);\n}\n\n\n/*\n * IsValidPartitionKeyRestriction check whether an operator clause is\n * a valid restriction for comparing to a partition column.\n */\nstatic bool\nIsValidPartitionKeyRestriction(OpExpr *opClause)\n{\n\tListCell *btreeInterpretationCell = NULL;\n\tbool matchedOp = false;\n\n\tList *btreeInterpretationList =\n\t\tget_op_btree_interpretation(opClause->opno);\n\tforeach(btreeInterpretationCell, btreeInterpretationList)\n\t{\n\t\tOpBtreeInterpretation *btreeInterpretation =\n\t\t\t(OpBtreeInterpretation *) lfirst(btreeInterpretationCell);\n\n\t#if PG_VERSION_NUM >= PG_VERSION_18\n\t\tif (btreeInterpretation->cmptype == ROWCOMPARE_NE)\n\t#else\n\t\tif (btreeInterpretation->strategy == ROWCOMPARE_NE)\n\t#endif\n\t\t{\n\t\t\t/* TODO: could add support for this, if we feel like it */\n\t\t\treturn false;\n\t\t}\n\n\t\tmatchedOp = true;\n\t}\n\n\treturn matchedOp;\n}\n\n\n/*\n * AddPartitionKeyRestrictionToInstance adds information about a PartitionKey\n * $op Const restriction to the current pruning instance.\n */\nstatic void\nAddPartitionKeyRestrictionToInstance(ClauseWalkerContext *context, OpExpr *opClause,\n\t\t\t\t\t\t\t\t\t Var *partitionColumn, Const *constantClause)\n{\n\tPruningInstance *prune = context->currentPruningInstance;\n\tListCell *btreeInterpretationCell = NULL;\n\n\t/* only have extra work to do if const isn't same type as partition column */\n\tif (constantClause->consttype != partitionColumn->vartype)\n\t{\n\t\t/* we want our restriction value in terms of the type of the partition column */\n\t\tconstantClause = TransformPartitionRestrictionValue(partitionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconstantClause, true);\n\t\tif (constantClause == NULL)\n\t\t{\n\t\t\t/* couldn't coerce value, its invalid restriction */\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (constantClause->constisnull)\n\t{\n\t\t/* we cannot do pruning on NULL values */\n\t\treturn;\n\t}\n\n\t/* at this point, we'd better be able to pass binary Datums to comparison functions */\n\tAssert(IsBinaryCoercible(constantClause->consttype, partitionColumn->vartype));\n\n\tList *btreeInterpretationList = get_op_btree_interpretation(opClause->opno);\n\tforeach(btreeInterpretationCell, btreeInterpretationList)\n\t{\n\t\tOpBtreeInterpretation *btreeInterpretation =\n\t\t\t(OpBtreeInterpretation *) lfirst(btreeInterpretationCell);\n\n\t#if PG_VERSION_NUM >= PG_VERSION_18\n\t\tswitch (btreeInterpretation->cmptype)\n\t#else\n\t\tswitch (btreeInterpretation->strategy)\n\t#endif\n\t\t{\n\t\t\tcase BTLessStrategyNumber:\n\t\t\t{\n\t\t\t\tif (!prune->lessConsts ||\n\t\t\t\t\tPerformValueCompare((FunctionCallInfo) &\n\t\t\t\t\t\t\t\t\t\tcontext->compareValueFunctionCall,\n\t\t\t\t\t\t\t\t\t\tconstantClause->constvalue,\n\t\t\t\t\t\t\t\t\t\tprune->lessConsts->constvalue) < 0)\n\t\t\t\t{\n\t\t\t\t\tprune->lessConsts = constantClause;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BTLessEqualStrategyNumber:\n\t\t\t{\n\t\t\t\tif (!prune->lessEqualConsts ||\n\t\t\t\t\tPerformValueCompare((FunctionCallInfo) &\n\t\t\t\t\t\t\t\t\t\tcontext->compareValueFunctionCall,\n\t\t\t\t\t\t\t\t\t\tconstantClause->constvalue,\n\t\t\t\t\t\t\t\t\t\tprune->lessEqualConsts->constvalue) < 0)\n\t\t\t\t{\n\t\t\t\t\tprune->lessEqualConsts = constantClause;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BTEqualStrategyNumber:\n\t\t\t{\n\t\t\t\tif (!prune->equalConsts)\n\t\t\t\t{\n\t\t\t\t\tprune->equalConsts = constantClause;\n\t\t\t\t}\n\t\t\t\telse if (PerformValueCompare((FunctionCallInfo) &\n\t\t\t\t\t\t\t\t\t\t\t context->compareValueFunctionCall,\n\t\t\t\t\t\t\t\t\t\t\t constantClause->constvalue,\n\t\t\t\t\t\t\t\t\t\t\t prune->equalConsts->constvalue) != 0)\n\t\t\t\t{\n\t\t\t\t\t/* key can't be equal to two values */\n\t\t\t\t\tprune->evaluatesToFalse = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BTGreaterEqualStrategyNumber:\n\t\t\t{\n\t\t\t\tif (!prune->greaterEqualConsts ||\n\t\t\t\t\tPerformValueCompare((FunctionCallInfo) &\n\t\t\t\t\t\t\t\t\t\tcontext->compareValueFunctionCall,\n\t\t\t\t\t\t\t\t\t\tconstantClause->constvalue,\n\t\t\t\t\t\t\t\t\t\tprune->greaterEqualConsts->constvalue) > 0\n\t\t\t\t\t)\n\t\t\t\t{\n\t\t\t\t\tprune->greaterEqualConsts = constantClause;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase BTGreaterStrategyNumber:\n\t\t\t{\n\t\t\t\tif (!prune->greaterConsts ||\n\t\t\t\t\tPerformValueCompare((FunctionCallInfo) &\n\t\t\t\t\t\t\t\t\t\tcontext->compareValueFunctionCall,\n\t\t\t\t\t\t\t\t\t\tconstantClause->constvalue,\n\t\t\t\t\t\t\t\t\t\tprune->greaterConsts->constvalue) > 0)\n\t\t\t\t{\n\t\t\t\t\tprune->greaterConsts = constantClause;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tAssert(false);\n\t\t\t}\n\t\t}\n\t}\n\n\tprune->hasValidConstraint = true;\n}\n\n\n/*\n * TransformPartitionRestrictionValue works around how PostgreSQL sometimes\n * chooses to try to wrap our Var in a coercion rather than the Const.\n * To deal with this, we strip coercions from both and manually coerce\n * the Const into the type of our partition column.\n * It is conceivable that in some instances this may not be possible,\n * in those cases we will simply fail to prune partitions based on this clause.\n */\nConst *\nTransformPartitionRestrictionValue(Var *partitionColumn, Const *restrictionValue,\n\t\t\t\t\t\t\t\t   bool missingOk)\n{\n\tNode *transformedValue = coerce_to_target_type(NULL, (Node *) restrictionValue,\n\t\t\t\t\t\t\t\t\t\t\t\t   restrictionValue->consttype,\n\t\t\t\t\t\t\t\t\t\t\t\t   partitionColumn->vartype,\n\t\t\t\t\t\t\t\t\t\t\t\t   partitionColumn->vartypmod,\n\t\t\t\t\t\t\t\t\t\t\t\t   COERCION_ASSIGNMENT,\n\t\t\t\t\t\t\t\t\t\t\t\t   COERCE_IMPLICIT_CAST, -1);\n\n\t/* if NULL, no implicit coercion is possible between the types */\n\tif (transformedValue == NULL)\n\t{\n\t\tif (!missingOk)\n\t\t{\n\t\t\tErrorTypesDontMatch(partitionColumn->vartype, partitionColumn->varcollid,\n\t\t\t\t\t\t\t\trestrictionValue->consttype,\n\t\t\t\t\t\t\t\trestrictionValue->constcollid);\n\t\t}\n\n\t\treturn NULL;\n\t}\n\n\t/* if still not a constant, evaluate coercion */\n\tif (!IsA(transformedValue, Const))\n\t{\n\t\ttransformedValue = (Node *) expression_planner((Expr *) transformedValue);\n\t}\n\n\t/* if still not a constant, no immutable coercion matched */\n\tif (!IsA(transformedValue, Const))\n\t{\n\t\tif (!missingOk)\n\t\t{\n\t\t\tErrorTypesDontMatch(partitionColumn->vartype, partitionColumn->varcollid,\n\t\t\t\t\t\t\t\trestrictionValue->consttype,\n\t\t\t\t\t\t\t\trestrictionValue->constcollid);\n\t\t}\n\n\t\treturn NULL;\n\t}\n\n\treturn (Const *) transformedValue;\n}\n\n\n/*\n * ErrorTypesDontMatch throws an error explicitly printing the type names.\n */\nstatic void\nErrorTypesDontMatch(Oid firstType, Oid firstCollId, Oid secondType, Oid secondCollId)\n{\n\tDatum firstTypename =\n\t\tDirectFunctionCall1Coll(regtypeout, firstCollId, ObjectIdGetDatum(firstType));\n\n\tDatum secondTypename =\n\t\tDirectFunctionCall1Coll(regtypeout, secondCollId, ObjectIdGetDatum(secondType));\n\n\tereport(ERROR, (errmsg(\"Cannot coerce %s to %s\",\n\t\t\t\t\t\t   DatumGetCString(secondTypename),\n\t\t\t\t\t\t   DatumGetCString(firstTypename))));\n}\n\n\n/*\n * IsValidHashRestriction checks whether an operator clause is a valid restriction for hashed column.\n */\nstatic bool\nIsValidHashRestriction(OpExpr *opClause)\n{\n\tListCell *btreeInterpretationCell = NULL;\n\n\tList *btreeInterpretationList =\n\t\tget_op_btree_interpretation(opClause->opno);\n\tforeach(btreeInterpretationCell, btreeInterpretationList)\n\t{\n\t\tOpBtreeInterpretation *btreeInterpretation =\n\t\t\t(OpBtreeInterpretation *) lfirst(btreeInterpretationCell);\n\n\t\t#if PG_VERSION_NUM >= PG_VERSION_18\n\t\tif (btreeInterpretation->cmptype == BTGreaterEqualStrategyNumber)\n\t\t#else\n\t\tif (btreeInterpretation->strategy == BTGreaterEqualStrategyNumber)\n\t\t#endif\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * AddHashRestrictionToInstance adds information about a\n * RESERVED_HASHED_COLUMN_ID = Const restriction to the current pruning\n * instance.\n */\nstatic void\nAddHashRestrictionToInstance(ClauseWalkerContext *context, OpExpr *opClause,\n\t\t\t\t\t\t\t Var *varClause, Const *constantClause)\n{\n\t/* be paranoid */\n\tAssert(IsBinaryCoercible(constantClause->consttype, INT4OID));\n\tAssert(IsValidHashRestriction(opClause));\n\n\t/*\n\t * Ladidadida, dirty hackety hack. We only add such\n\t * constraints (in ShardIntervalOpExpressions()) to select a\n\t * shard based on its exact boundaries. For efficient binary\n\t * search it's better to simply use one representative value\n\t * to look up the shard. In practice, this is sufficient for\n\t * now.\n\t */\n\tPruningInstance *prune = context->currentPruningInstance;\n\tAssert(!prune->hashedEqualConsts);\n\tprune->hashedEqualConsts = constantClause;\n\tprune->hasValidConstraint = true;\n}\n\n\n/*\n * CopyPartialPruningInstance copies a partial PruningInstance, so it can be\n * completed.\n */\nstatic PruningInstance *\nCopyPartialPruningInstance(PruningInstance *sourceInstance)\n{\n\tPruningInstance *newInstance = palloc(sizeof(PruningInstance));\n\n\tAssert(sourceInstance->isPartial);\n\n\t/*\n\t * To make the new PruningInstance useful for pruning, we have to reset it\n\t * being partial - if necessary it'll be marked so again by\n\t * PrunableExpressionsWalker().\n\t */\n\t*newInstance = *sourceInstance;\n\tnewInstance->addedToPruningInstances = false;\n\tnewInstance->isPartial = false;\n\n\treturn newInstance;\n}\n\n\n/*\n * ShardArrayToList builds a list of out the array of ShardInterval*.\n */\nstatic List *\nShardArrayToList(ShardInterval **shardArray, int length)\n{\n\tList *shardIntervalList = NIL;\n\n\tfor (int shardIndex = 0; shardIndex < length; shardIndex++)\n\t{\n\t\tShardInterval *shardInterval =\n\t\t\tshardArray[shardIndex];\n\t\tshardIntervalList = lappend(shardIntervalList, shardInterval);\n\t}\n\n\treturn shardIntervalList;\n}\n\n\n/*\n * DeepCopyShardIntervalList copies originalShardIntervalList and the\n * contained ShardIntervals, into a new list.\n */\nstatic List *\nDeepCopyShardIntervalList(List *originalShardIntervalList)\n{\n\tList *copiedShardIntervalList = NIL;\n\n\tShardInterval *originalShardInterval = NULL;\n\tforeach_declared_ptr(originalShardInterval, originalShardIntervalList)\n\t{\n\t\tShardInterval *copiedShardInterval = CopyShardInterval(originalShardInterval);\n\n\t\tcopiedShardIntervalList = lappend(copiedShardIntervalList, copiedShardInterval);\n\t}\n\n\treturn copiedShardIntervalList;\n}\n\n\n/*\n * PruneOne returns all shards in the table that match a single\n * PruningInstance.\n */\nstatic List *\nPruneOne(CitusTableCacheEntry *cacheEntry, ClauseWalkerContext *context,\n\t\t PruningInstance *prune)\n{\n\tShardInterval *shardInterval = NULL;\n\n\t/* Well, if life always were this easy... */\n\tif (prune->evaluatesToFalse)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * For an equal constraints, if there's no overlapping shards (always the\n\t * case for hash and range partitioning, sometimes for append), can\n\t * perform binary search for the right interval. That's usually the\n\t * fastest, so try that first.\n\t */\n\tif (prune->equalConsts &&\n\t\t!cacheEntry->hasOverlappingShardInterval)\n\t{\n\t\tshardInterval = FindShardInterval(prune->equalConsts->constvalue, cacheEntry);\n\n\t\t/*\n\t\t * If pruned down to nothing, we're done. Otherwise see if other\n\t\t * methods prune down further / to nothing.\n\t\t */\n\t\tif (!shardInterval)\n\t\t{\n\t\t\treturn NIL;\n\t\t}\n\t}\n\n\t/*\n\t * If the hash value we're looking for is known, we can search for the\n\t * interval directly. That's fast and should only ever be the case for a\n\t * hash-partitioned table.\n\t */\n\tif (prune->hashedEqualConsts)\n\t{\n\t\tShardInterval **sortedShardIntervalArray = cacheEntry->sortedShardIntervalArray;\n\n\t\tAssert(context->partitionMethod == DISTRIBUTE_BY_HASH);\n\n\t\tint shardIndex = FindShardIntervalIndex(prune->hashedEqualConsts->constvalue,\n\t\t\t\t\t\t\t\t\t\t\t\tcacheEntry);\n\n\t\tif (shardIndex == INVALID_SHARD_INDEX)\n\t\t{\n\t\t\treturn NIL;\n\t\t}\n\t\telse if (shardInterval &&\n\t\t\t\t sortedShardIntervalArray[shardIndex]->shardId != shardInterval->shardId)\n\t\t{\n\t\t\t/*\n\t\t\t * equalConst based pruning above yielded a different shard than\n\t\t\t * pruning based on pre-hashed equality. This is useful in case\n\t\t\t * of INSERT ... SELECT, where both can occur together (one via\n\t\t\t * join/colocation, the other via a plain equality restriction).\n\t\t\t */\n\t\t\treturn NIL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn list_make1(sortedShardIntervalArray[shardIndex]);\n\t\t}\n\t}\n\n\t/*\n\t * If previous pruning method yielded a single shard, and the table is not\n\t * hash partitioned, attempt range based pruning to exclude it further.\n\t *\n\t * That's particularly important in particular for subquery pushdown,\n\t * where it's very common to have a user specified equality restriction,\n\t * and a range based restriction for shard boundaries, added by the\n\t * subquery machinery.\n\t */\n\tif (shardInterval)\n\t{\n\t\tif (context->partitionMethod != DISTRIBUTE_BY_HASH &&\n\t\t\tExhaustivePruneOne(shardInterval, context, prune))\n\t\t{\n\t\t\treturn NIL;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* no chance to prune further, return */\n\t\t\treturn list_make1(shardInterval);\n\t\t}\n\t}\n\n\t/*\n\t * Should never get here for hashing, we've filtered down to either zero\n\t * or one shard, and returned.\n\t */\n\tAssert(context->partitionMethod != DISTRIBUTE_BY_HASH);\n\n\t/*\n\t * Next method: binary search with fuzzy boundaries. Can't trivially do so\n\t * if shards have overlapping boundaries.\n\t *\n\t * TODO: If we kept shard intervals separately sorted by both upper and\n\t * lower boundaries, this should be possible?\n\t */\n\tif (!cacheEntry->hasOverlappingShardInterval && (\n\t\t\tprune->greaterConsts || prune->greaterEqualConsts ||\n\t\t\tprune->lessConsts || prune->lessEqualConsts))\n\t{\n\t\treturn PruneWithBoundaries(cacheEntry, context, prune);\n\t}\n\n\t/*\n\t * Brute force: Check each shard.\n\t */\n\treturn ExhaustivePrune(cacheEntry, context, prune);\n}\n\n\n/*\n * PerformCompare invokes comparator with prepared values, check for\n * unexpected NULL returns.\n */\nstatic int\nPerformCompare(FunctionCallInfo compareFunctionCall)\n{\n\tDatum result = FunctionCallInvoke(compareFunctionCall);\n\n\tif (compareFunctionCall->isnull)\n\t{\n\t\telog(ERROR, \"function %u returned NULL\", compareFunctionCall->flinfo->fn_oid);\n\t}\n\n\treturn DatumGetInt32(result);\n}\n\n\n/*\n * PerformValueCompare invokes comparator with a/b, and checks for unexpected\n * NULL returns.\n */\nstatic int\nPerformValueCompare(FunctionCallInfo compareFunctionCall, Datum a, Datum b)\n{\n\tfcSetArg(compareFunctionCall, 0, a);\n\tfcSetArg(compareFunctionCall, 1, b);\n\n\treturn PerformCompare(compareFunctionCall);\n}\n\n\n/*\n * LowerShardBoundary returns the index of the first ShardInterval that's >=\n * (if includeMax) or > partitionColumnValue.\n */\nstatic int\nLowerShardBoundary(Datum partitionColumnValue, ShardInterval **shardIntervalCache,\n\t\t\t\t   int shardCount, FunctionCallInfo compareFunction, bool includeMax)\n{\n\tint lowerBoundIndex = 0;\n\tint upperBoundIndex = shardCount;\n\n\tAssert(shardCount != 0);\n\n\t/* setup partitionColumnValue argument once */\n\tfcSetArg(compareFunction, 0, partitionColumnValue);\n\n\t/*\n\t * Now we test partitionColumnValue used in where clause such as\n\t * partCol > partitionColumnValue (or partCol >= partitionColumnValue)\n\t * against four possibilities, these are:\n\t * 1) partitionColumnValue falls into a specific shard, such that:\n\t *    partitionColumnValue >= shard[x].min, and\n\t *    partitionColumnValue < shard[x].max (or partitionColumnValue <= shard[x].max).\n\t * 2) partitionColumnValue < shard[x].min for all the shards\n\t * 3) partitionColumnValue > shard[x].max for all the shards\n\t * 4) partitionColumnValue falls in between two shards, such that:\n\t *    partitionColumnValue > shard[x].max and\n\t *    partitionColumnValue < shard[x+1].min\n\t *\n\t * For 1), we find that shard in below loop using binary search and\n\t * return the index of it. For the others, see the end of this function.\n\t */\n\twhile (lowerBoundIndex < upperBoundIndex)\n\t{\n\t\tint middleIndex = lowerBoundIndex + ((upperBoundIndex - lowerBoundIndex) / 2);\n\n\t\t/* setup minValue as argument */\n\t\tfcSetArg(compareFunction, 1, shardIntervalCache[middleIndex]->minValue);\n\n\t\t/* execute cmp(partitionValue, lowerBound) */\n\t\tint minValueComparison = PerformCompare(compareFunction);\n\n\t\t/* and evaluate results */\n\t\tif (minValueComparison < 0)\n\t\t{\n\t\t\t/* value smaller than entire range */\n\t\t\tupperBoundIndex = middleIndex;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* setup maxValue as argument */\n\t\tfcSetArg(compareFunction, 1, shardIntervalCache[middleIndex]->maxValue);\n\n\t\t/* execute cmp(partitionValue, upperBound) */\n\t\tint maxValueComparison = PerformCompare(compareFunction);\n\n\t\tif ((maxValueComparison == 0 && !includeMax) ||\n\t\t\tmaxValueComparison > 0)\n\t\t{\n\t\t\t/* value bigger than entire range */\n\t\t\tlowerBoundIndex = middleIndex + 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* partitionColumnValue falls into a specific shard, possibility 1) */\n\t\treturn middleIndex;\n\t}\n\n\tAssert(lowerBoundIndex == upperBoundIndex);\n\n\t/*\n\t * If we get here, none of the ShardIntervals exactly contain the value\n\t * (we'd have hit the return middleIndex; case otherwise). Figure out\n\t * whether there's possibly any interval containing a value that's bigger\n\t * than the partition key one.\n\t *\n\t * Also note that we initialized lowerBoundIndex with 0. Similarly,\n\t * we always set it to the index of the  shard that we consider as our\n\t * lower boundary during binary search.\n\t */\n\tif (lowerBoundIndex == shardCount)\n\t{\n\t\t/*\n\t\t * Since lowerBoundIndex is an inclusive index, being equal to shardCount\n\t\t * means all the shards have smaller values than partitionColumnValue,\n\t\t * which corresponds to possibility 3).\n\t\t * In that case, since we can't have a lower bound shard, we return\n\t\t * INVALID_SHARD_INDEX here.\n\t\t */\n\t\treturn INVALID_SHARD_INDEX;\n\t}\n\n\t/*\n\t * partitionColumnValue is either smaller than all the shards or falls in\n\t * between two shards, which corresponds to possibility 2) or 4).\n\t * Knowing that lowerBoundIndex is an inclusive index, we directly return\n\t * it as the index for the lower bound shard here.\n\t */\n\treturn lowerBoundIndex;\n}\n\n\n/*\n * UpperShardBoundary returns the index of the last ShardInterval that's <=\n * (if includeMin) or < partitionColumnValue.\n */\nstatic int\nUpperShardBoundary(Datum partitionColumnValue, ShardInterval **shardIntervalCache,\n\t\t\t\t   int shardCount, FunctionCallInfo compareFunction, bool includeMin)\n{\n\tint lowerBoundIndex = 0;\n\tint upperBoundIndex = shardCount;\n\n\tAssert(shardCount != 0);\n\n\t/* setup partitionColumnValue argument once */\n\tfcSetArg(compareFunction, 0, partitionColumnValue);\n\n\t/*\n\t * Now we test partitionColumnValue used in where clause such as\n\t * partCol < partitionColumnValue (or partCol <= partitionColumnValue)\n\t * against four possibilities, these are:\n\t * 1) partitionColumnValue falls into a specific shard, such that:\n\t *    partitionColumnValue <= shard[x].max, and\n\t *    partitionColumnValue > shard[x].min (or partitionColumnValue >= shard[x].min).\n\t * 2) partitionColumnValue > shard[x].max for all the shards\n\t * 3) partitionColumnValue < shard[x].min for all the shards\n\t * 4) partitionColumnValue falls in between two shards, such that:\n\t *    partitionColumnValue > shard[x].max and\n\t *    partitionColumnValue < shard[x+1].min\n\t *\n\t * For 1), we find that shard in below loop using binary search and\n\t * return the index of it. For the others, see the end of this function.\n\t */\n\n\twhile (lowerBoundIndex < upperBoundIndex)\n\t{\n\t\tint middleIndex = lowerBoundIndex + ((upperBoundIndex - lowerBoundIndex) / 2);\n\n\t\t/* setup minValue as argument */\n\t\tfcSetArg(compareFunction, 1, shardIntervalCache[middleIndex]->minValue);\n\n\t\t/* execute cmp(partitionValue, lowerBound) */\n\t\tint minValueComparison = PerformCompare(compareFunction);\n\n\t\t/* and evaluate results */\n\t\tif ((minValueComparison == 0 && !includeMin) ||\n\t\t\tminValueComparison < 0)\n\t\t{\n\t\t\t/* value smaller than entire range */\n\t\t\tupperBoundIndex = middleIndex;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* setup maxValue as argument */\n\t\tfcSetArg(compareFunction, 1, shardIntervalCache[middleIndex]->maxValue);\n\n\t\t/* execute cmp(partitionValue, upperBound) */\n\t\tint maxValueComparison = PerformCompare(compareFunction);\n\n\t\tif (maxValueComparison > 0)\n\t\t{\n\t\t\t/* value bigger than entire range */\n\t\t\tlowerBoundIndex = middleIndex + 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* partitionColumnValue falls into a specific shard, possibility 1) */\n\t\treturn middleIndex;\n\t}\n\n\tAssert(lowerBoundIndex == upperBoundIndex);\n\n\t/*\n\t * If we get here, none of the ShardIntervals exactly contain the value\n\t * (we'd have hit the return middleIndex; case otherwise). Figure out\n\t * whether there's possibly any interval containing a value that's smaller\n\t * than the partition key one.\n\t *\n\t * Also note that we initialized upperBoundIndex with shardCount. Similarly,\n\t * we always set it to the index of the next shard that we consider as our\n\t * upper boundary during binary search.\n\t */\n\tif (upperBoundIndex == 0)\n\t{\n\t\t/*\n\t\t * Since upperBoundIndex is an exclusive index, being equal to 0 means\n\t\t * all the shards have greater values than partitionColumnValue, which\n\t\t * corresponds to possibility 3).\n\t\t * In that case, since we can't have an upper bound shard, we return\n\t\t * INVALID_SHARD_INDEX here.\n\t\t */\n\t\treturn INVALID_SHARD_INDEX;\n\t}\n\n\t/*\n\t * partitionColumnValue is either greater than all the shards or falls in\n\t * between two shards, which corresponds to possibility 2) or 4).\n\t * Knowing that upperBoundIndex is an exclusive index, we return the index\n\t * for the previous shard here.\n\t */\n\treturn upperBoundIndex - 1;\n}\n\n\n/*\n * PruneWithBoundaries searches for shards that match inequality constraints,\n * using binary search on both the upper and lower boundary, and returns a\n * list of surviving shards.\n */\nstatic List *\nPruneWithBoundaries(CitusTableCacheEntry *cacheEntry, ClauseWalkerContext *context,\n\t\t\t\t\tPruningInstance *prune)\n{\n\tList *remainingShardList = NIL;\n\tint shardCount = cacheEntry->shardIntervalArrayLength;\n\tShardInterval **sortedShardIntervalArray = cacheEntry->sortedShardIntervalArray;\n\tbool hasLowerBound = false;\n\tbool hasUpperBound = false;\n\tDatum lowerBound = 0;\n\tDatum upperBound = 0;\n\tbool lowerBoundInclusive = false;\n\tbool upperBoundInclusive = false;\n\tint lowerBoundIdx = -1;\n\tint upperBoundIdx = -1;\n\tFunctionCallInfo compareFunctionCall = (FunctionCallInfo) &\n\t\t\t\t\t\t\t\t\t\t   context->compareIntervalFunctionCall;\n\n\tif (prune->greaterEqualConsts)\n\t{\n\t\tlowerBound = prune->greaterEqualConsts->constvalue;\n\t\tlowerBoundInclusive = true;\n\t\thasLowerBound = true;\n\t}\n\tif (prune->greaterConsts)\n\t{\n\t\t/*\n\t\t * Use the more restrictive one, if both greater and greaterEqual\n\t\t * constraints are specified.\n\t\t */\n\t\tif (!hasLowerBound ||\n\t\t\tPerformValueCompare(compareFunctionCall,\n\t\t\t\t\t\t\t\tprune->greaterConsts->constvalue,\n\t\t\t\t\t\t\t\tlowerBound) >= 0)\n\t\t{\n\t\t\tlowerBound = prune->greaterConsts->constvalue;\n\t\t\tlowerBoundInclusive = false;\n\t\t\thasLowerBound = true;\n\t\t}\n\t}\n\tif (prune->lessEqualConsts)\n\t{\n\t\tupperBound = prune->lessEqualConsts->constvalue;\n\t\tupperBoundInclusive = true;\n\t\thasUpperBound = true;\n\t}\n\tif (prune->lessConsts)\n\t{\n\t\t/*\n\t\t * Use the more restrictive one, if both less and lessEqual\n\t\t * constraints are specified.\n\t\t */\n\t\tif (!hasUpperBound ||\n\t\t\tPerformValueCompare(compareFunctionCall,\n\t\t\t\t\t\t\t\tprune->lessConsts->constvalue,\n\t\t\t\t\t\t\t\tupperBound) <= 0)\n\t\t{\n\t\t\tupperBound = prune->lessConsts->constvalue;\n\t\t\tupperBoundInclusive = false;\n\t\t\thasUpperBound = true;\n\t\t}\n\t}\n\n\tAssert(hasLowerBound || hasUpperBound);\n\n\t/* find lower bound */\n\tif (hasLowerBound)\n\t{\n\t\tlowerBoundIdx = LowerShardBoundary(lowerBound, sortedShardIntervalArray,\n\t\t\t\t\t\t\t\t\t\t   shardCount, compareFunctionCall,\n\t\t\t\t\t\t\t\t\t\t   lowerBoundInclusive);\n\t}\n\telse\n\t{\n\t\tlowerBoundIdx = 0;\n\t}\n\n\t/* find upper bound */\n\tif (hasUpperBound)\n\t{\n\t\tupperBoundIdx = UpperShardBoundary(upperBound, sortedShardIntervalArray,\n\t\t\t\t\t\t\t\t\t\t   shardCount, compareFunctionCall,\n\t\t\t\t\t\t\t\t\t\t   upperBoundInclusive);\n\t}\n\telse\n\t{\n\t\tupperBoundIdx = shardCount - 1;\n\t}\n\n\tif (lowerBoundIdx == INVALID_SHARD_INDEX)\n\t{\n\t\treturn NIL;\n\t}\n\telse if (upperBoundIdx == INVALID_SHARD_INDEX)\n\t{\n\t\treturn NIL;\n\t}\n\n\t/*\n\t * Build list of all shards that are in the range of shards (possibly 0).\n\t */\n\tfor (int curIdx = lowerBoundIdx; curIdx <= upperBoundIdx; curIdx++)\n\t{\n\t\tremainingShardList = lappend(remainingShardList,\n\t\t\t\t\t\t\t\t\t sortedShardIntervalArray[curIdx]);\n\t}\n\n\treturn remainingShardList;\n}\n\n\n/*\n * ExhaustivePrune returns a list of shards matching PruningInstances\n * constraints, by simply checking them for each individual shard.\n */\nstatic List *\nExhaustivePrune(CitusTableCacheEntry *cacheEntry, ClauseWalkerContext *context,\n\t\t\t\tPruningInstance *prune)\n{\n\tList *remainingShardList = NIL;\n\tint shardCount = cacheEntry->shardIntervalArrayLength;\n\tShardInterval **sortedShardIntervalArray = cacheEntry->sortedShardIntervalArray;\n\n\tfor (int curIdx = 0; curIdx < shardCount; curIdx++)\n\t{\n\t\tShardInterval *curInterval = sortedShardIntervalArray[curIdx];\n\n\t\tif (!ExhaustivePruneOne(curInterval, context, prune))\n\t\t{\n\t\t\tremainingShardList = lappend(remainingShardList, curInterval);\n\t\t}\n\t}\n\n\treturn remainingShardList;\n}\n\n\n/*\n * ExhaustivePruneOne returns whether curInterval is pruned away.\n */\nstatic bool\nExhaustivePruneOne(ShardInterval *curInterval,\n\t\t\t\t   ClauseWalkerContext *context,\n\t\t\t\t   PruningInstance *prune)\n{\n\tFunctionCallInfo compareFunctionCall = (FunctionCallInfo) &\n\t\t\t\t\t\t\t\t\t\t   context->compareIntervalFunctionCall;\n\tDatum compareWith = 0;\n\n\t/* NULL boundaries can't be compared to */\n\tif (!curInterval->minValueExists || !curInterval->maxValueExists)\n\t{\n\t\treturn false;\n\t}\n\n\tif (prune->equalConsts)\n\t{\n\t\tcompareWith = prune->equalConsts->constvalue;\n\n\t\tif (PerformValueCompare(compareFunctionCall,\n\t\t\t\t\t\t\t\tcompareWith,\n\t\t\t\t\t\t\t\tcurInterval->minValue) < 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tif (PerformValueCompare(compareFunctionCall,\n\t\t\t\t\t\t\t\tcompareWith,\n\t\t\t\t\t\t\t\tcurInterval->maxValue) > 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\tif (prune->greaterEqualConsts)\n\t{\n\t\tcompareWith = prune->greaterEqualConsts->constvalue;\n\n\t\tif (PerformValueCompare(compareFunctionCall,\n\t\t\t\t\t\t\t\tcurInterval->maxValue,\n\t\t\t\t\t\t\t\tcompareWith) < 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\tif (prune->greaterConsts)\n\t{\n\t\tcompareWith = prune->greaterConsts->constvalue;\n\n\t\tif (PerformValueCompare(compareFunctionCall,\n\t\t\t\t\t\t\t\tcurInterval->maxValue,\n\t\t\t\t\t\t\t\tcompareWith) <= 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\tif (prune->lessEqualConsts)\n\t{\n\t\tcompareWith = prune->lessEqualConsts->constvalue;\n\n\t\tif (PerformValueCompare(compareFunctionCall,\n\t\t\t\t\t\t\t\tcurInterval->minValue,\n\t\t\t\t\t\t\t\tcompareWith) > 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\tif (prune->lessConsts)\n\t{\n\t\tcompareWith = prune->lessConsts->constvalue;\n\n\t\tif (PerformValueCompare(compareFunctionCall,\n\t\t\t\t\t\t\t\tcurInterval->minValue,\n\t\t\t\t\t\t\t\tcompareWith) >= 0)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * Helper for creating a node for pruning tree\n */\nstatic PruningTreeNode *\nCreatePruningNode(BoolExprType boolop)\n{\n\tPruningTreeNode *node = palloc0(sizeof(PruningTreeNode));\n\tnode->boolop = boolop;\n\tnode->childBooleanNodes = NIL;\n\tnode->validConstraints = NIL;\n\tnode->hasInvalidConstraints = false;\n\treturn node;\n}\n\n\n/*\n * SAORestrictionArrayEqualityOp creates an equality operator\n * for a single element of a scalar array constraint.\n */\nstatic OpExpr *\nSAORestrictionArrayEqualityOp(ScalarArrayOpExpr *arrayOperatorExpression,\n\t\t\t\t\t\t\t  Var *partitionColumn)\n{\n\tOpExpr *arrayEqualityOp = makeNode(OpExpr);\n\tarrayEqualityOp->opno = arrayOperatorExpression->opno;\n\tarrayEqualityOp->opfuncid = arrayOperatorExpression->opfuncid;\n\tarrayEqualityOp->inputcollid = arrayOperatorExpression->inputcollid;\n\tarrayEqualityOp->opresulttype = get_func_rettype(\n\t\tarrayOperatorExpression->opfuncid);\n\tarrayEqualityOp->opcollid = partitionColumn->varcollid;\n\tarrayEqualityOp->location = -1;\n\treturn arrayEqualityOp;\n}\n\n\n/*\n * DebugLogNode is a helper for logging expression nodes.\n */\nstatic void\nDebugLogNode(char *fmt, Node *node, List *deparseCtx)\n{\n\tif (node != NULL)\n\t{\n\t\tchar *deparsed = deparse_expression(node, deparseCtx, false, false);\n\t\tereport(DEBUG3, (errmsg(fmt, deparsed)));\n\t}\n}\n\n\n/*\n * DebugLogPruningInstance is a helper for logging purning constraints.\n */\nstatic void\nDebugLogPruningInstance(PruningInstance *pruning, List *deparseCtx)\n{\n\tDebugLogNode(\"constraint value: %s\",\n\t\t\t\t (Node *) pruning->equalConsts, deparseCtx);\n\tDebugLogNode(\"constraint (lt) value: %s\", \\\n\t\t\t\t (Node *) pruning->lessConsts, deparseCtx);\n\tDebugLogNode(\"constraint (lteq) value: %s\", \\\n\t\t\t\t (Node *) pruning->lessEqualConsts, deparseCtx);\n\tDebugLogNode(\"constraint (gt) value: %s\", \\\n\t\t\t\t (Node *) pruning->greaterConsts, deparseCtx);\n\tDebugLogNode(\"constraint (gteq) value: %s\",\n\t\t\t\t (Node *) pruning->greaterEqualConsts, deparseCtx);\n}\n\n\n/*\n * ConstraintCount returns how many arguments this node is taking.\n */\nstatic int\nConstraintCount(PruningTreeNode *node)\n{\n\treturn list_length(node->childBooleanNodes) +\n\t\t   list_length(node->validConstraints) +\n\t\t   (node->hasInvalidConstraints ? 1 : 0);\n}\n"
  },
  {
    "path": "src/backend/distributed/planner/tdigest_extension.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * tdigest_extension.c\n *    Helper functions to get access to tdigest specific data.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"catalog/pg_extension.h\"\n#include \"catalog/pg_type.h\"\n#include \"parser/parse_func.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/tdigest_extension.h\"\n#include \"distributed/version_compat.h\"\n\n\nstatic Oid LookupTDigestFunction(const char *functionName, int argcount, Oid *argtypes);\n\n/*\n * TDigestExtensionSchema finds the schema the tdigest extension is installed in. The\n * function will return InvalidOid if the extension is not installed.\n */\nOid\nTDigestExtensionSchema()\n{\n\tScanKeyData entry[1];\n\tForm_pg_extension extensionForm = NULL;\n\tOid tdigestExtensionSchema = InvalidOid;\n\n\tRelation relation = table_open(ExtensionRelationId, AccessShareLock);\n\n\tScanKeyInit(&entry[0],\n\t\t\t\tAnum_pg_extension_extname,\n\t\t\t\tBTEqualStrategyNumber, F_NAMEEQ,\n\t\t\t\tCStringGetDatum(\"tdigest\"));\n\n\tSysScanDesc scandesc = systable_beginscan(relation, ExtensionNameIndexId, true,\n\t\t\t\t\t\t\t\t\t\t\t  NULL, 1, entry);\n\n\tHeapTuple extensionTuple = systable_getnext(scandesc);\n\n\t/*\n\t * We assume that there can be at most one matching tuple, if no tuple found the\n\t * extension is not installed. The value of InvalidOid will not be changed.\n\t */\n\tif (HeapTupleIsValid(extensionTuple))\n\t{\n\t\textensionForm = (Form_pg_extension) GETSTRUCT(extensionTuple);\n\t\ttdigestExtensionSchema = extensionForm->extnamespace;\n\t\tAssert(OidIsValid(tdigestExtensionSchema));\n\t}\n\n\tsystable_endscan(scandesc);\n\n\ttable_close(relation, AccessShareLock);\n\n\treturn tdigestExtensionSchema;\n}\n\n\n/*\n * TDigestExtensionTypeOid performs a lookup for the Oid of the type representing the\n * tdigest as installed by the tdigest extension returns InvalidOid if the type cannot be\n * found.\n */\nOid\nTDigestExtensionTypeOid()\n{\n\tOid tdigestSchemaOid = TDigestExtensionSchema();\n\tif (!OidIsValid(tdigestSchemaOid))\n\t{\n\t\treturn InvalidOid;\n\t}\n\tchar *namespaceName = get_namespace_name(tdigestSchemaOid);\n\treturn LookupTypeOid(namespaceName, \"tdigest\");\n}\n\n\n/*\n * LookupTDigestFunction is a helper function specifically to lookup functions in the\n * namespace/schema where the tdigest extension is installed. This makes the lookup of\n * following aggregate functions easier and less repetitive.\n */\nstatic Oid\nLookupTDigestFunction(const char *functionName, int argcount, Oid *argtypes)\n{\n\tOid tdigestSchemaOid = TDigestExtensionSchema();\n\tif (!OidIsValid(tdigestSchemaOid))\n\t{\n\t\treturn InvalidOid;\n\t}\n\n\tchar *namespaceName = get_namespace_name(tdigestSchemaOid);\n\treturn LookupFuncName(\n\t\tlist_make2(makeString(namespaceName), makeString(pstrdup(functionName))),\n\t\targcount, argtypes, true);\n}\n\n\n/*\n * TDigestExtensionAggTDigest1 performs a lookup for the Oid of the tdigest aggregate;\n *   tdigest(tdigest)\n *\n * If the aggregate is not found InvalidOid is returned.\n */\nOid\nTDigestExtensionAggTDigest1()\n{\n\treturn LookupTDigestFunction(\"tdigest\", 1, (Oid[]) { TDigestExtensionTypeOid() });\n}\n\n\n/*\n * TDigestExtensionAggTDigest2 performs a lookup for the Oid of the tdigest aggregate;\n *   tdigest(value double precision, compression int)\n *\n * If the aggregate is not found InvalidOid is returned.\n */\nOid\nTDigestExtensionAggTDigest2()\n{\n\treturn LookupTDigestFunction(\"tdigest\", 2, (Oid[]) { FLOAT8OID, INT4OID });\n}\n\n\n/*\n * TDigestExtensionAggTDigestPercentile2 performs a lookup for the Oid of the tdigest\n * aggregate;\n *   tdigest_percentile(tdigest, double precision)\n *\n * If the aggregate is not found InvalidOid is returned.\n */\nOid\nTDigestExtensionAggTDigestPercentile2()\n{\n\treturn LookupTDigestFunction(\"tdigest_percentile\", 2,\n\t\t\t\t\t\t\t\t (Oid[]) { TDigestExtensionTypeOid(), FLOAT8OID });\n}\n\n\n/*\n * TDigestExtensionAggTDigestPercentile2a performs a lookup for the Oid of the tdigest\n * aggregate;\n *   tdigest_percentile(tdigest, double precision[])\n *\n * If the aggregate is not found InvalidOid is returned.\n */\nOid\nTDigestExtensionAggTDigestPercentile2a(void)\n{\n\treturn LookupTDigestFunction(\"tdigest_percentile\", 2,\n\t\t\t\t\t\t\t\t (Oid[]) { TDigestExtensionTypeOid(), FLOAT8ARRAYOID });\n}\n\n\n/*\n * TDigestExtensionAggTDigestPercentile3 performs a lookup for the Oid of the tdigest\n * aggregate;\n *   tdigest_percentile(double precision, int, double precision)\n *\n * If the aggregate is not found InvalidOid is returned.\n */\nOid\nTDigestExtensionAggTDigestPercentile3()\n{\n\treturn LookupTDigestFunction(\"tdigest_percentile\", 3,\n\t\t\t\t\t\t\t\t (Oid[]) { FLOAT8OID, INT4OID, FLOAT8OID });\n}\n\n\n/*\n * TDigestExtensionAggTDigestPercentile3a performs a lookup for the Oid of the tdigest\n * aggregate;\n *   tdigest_percentile(double precision, int, double precision[])\n *\n * If the aggregate is not found InvalidOid is returned.\n */\nOid\nTDigestExtensionAggTDigestPercentile3a(void)\n{\n\treturn LookupTDigestFunction(\"tdigest_percentile\", 3,\n\t\t\t\t\t\t\t\t (Oid[]) { FLOAT8OID, INT4OID, FLOAT8ARRAYOID });\n}\n\n\n/*\n * TDigestExtensionAggTDigestPercentileOf2 performs a lookup for the Oid of the tdigest\n * aggregate;\n *   tdigest_percentile_of(tdigest, double precision)\n *\n * If the aggregate is not found InvalidOid is returned.\n */\nOid\nTDigestExtensionAggTDigestPercentileOf2()\n{\n\treturn LookupTDigestFunction(\"tdigest_percentile_of\", 2,\n\t\t\t\t\t\t\t\t (Oid[]) { TDigestExtensionTypeOid(), FLOAT8OID });\n}\n\n\n/*\n * TDigestExtensionAggTDigestPercentileOf2a performs a lookup for the Oid of the tdigest\n * aggregate;\n *   tdigest_percentile_of(tdigest, double precision[])\n *\n * If the aggregate is not found InvalidOid is returned.\n */\nOid\nTDigestExtensionAggTDigestPercentileOf2a(void)\n{\n\treturn LookupTDigestFunction(\"tdigest_percentile_of\", 2,\n\t\t\t\t\t\t\t\t (Oid[]) { TDigestExtensionTypeOid(), FLOAT8ARRAYOID });\n}\n\n\n/*\n * TDigestExtensionAggTDigestPercentileOf3 performs a lookup for the Oid of the tdigest\n * aggregate;\n *   tdigest_percentile_of(double precision, int, double precision)\n *\n * If the aggregate is not found InvalidOid is returned.\n */\nOid\nTDigestExtensionAggTDigestPercentileOf3()\n{\n\treturn LookupTDigestFunction(\"tdigest_percentile_of\", 3,\n\t\t\t\t\t\t\t\t (Oid[]) { FLOAT8OID, INT4OID, FLOAT8OID });\n}\n\n\n/*\n * TDigestExtensionAggTDigestPercentileOf3a performs a lookup for the Oid of the tdigest\n * aggregate;\n *   tdigest_percentile_of(double precision, int, double precision[])\n *\n * If the aggregate is not found InvalidOid is returned.\n */\nOid\nTDigestExtensionAggTDigestPercentileOf3a(void)\n{\n\treturn LookupTDigestFunction(\"tdigest_percentile_of\", 3,\n\t\t\t\t\t\t\t\t (Oid[]) { FLOAT8OID, INT4OID, FLOAT8ARRAYOID });\n}\n"
  },
  {
    "path": "src/backend/distributed/progress/multi_progress.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_progress.c\n *\t  Routines for tracking long-running jobs and seeing their progress.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"storage/dsm.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/function_utils.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/multi_progress.h\"\n#include \"distributed/version_compat.h\"\n\n\n/* dynamic shared memory handle of the current progress */\nstatic uint64 currentProgressDSMHandle = DSM_HANDLE_INVALID;\n\nstatic ProgressMonitorData * MonitorDataFromDSMHandle(dsm_handle dsmHandle,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  dsm_segment **attachedSegment);\n\n\n/*\n * CreateProgressMonitor is used to create a place to store progress\n * information related to long running processes. The function creates a\n * dynamic shared memory segment consisting of a header regarding to the\n * process and an array of \"steps\" that the long running \"operations\" consists\n * of. After initializing the data in the array of steps, the shared memory\n * segment can be shared with other processes using RegisterProgressMonitor, by\n * giving it the value that's written to the dsmHandle argument.\n */\nProgressMonitorData *\nCreateProgressMonitor(int stepCount, Size stepSize, dsm_handle *dsmHandle)\n{\n\tif (stepSize <= 0 || stepCount <= 0)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"number of steps and size of each step should be \"\n\t\t\t\t\t\t\"positive values\")));\n\t}\n\n\tSize monitorSize = sizeof(ProgressMonitorData) + stepSize * stepCount;\n\tdsm_segment *dsmSegment = dsm_create(monitorSize, DSM_CREATE_NULL_IF_MAXSEGMENTS);\n\n\tif (dsmSegment == NULL)\n\t{\n\t\tereport(WARNING,\n\t\t\t\t(errmsg(\"could not create a dynamic shared memory segment to \"\n\t\t\t\t\t\t\"keep track of progress of the current command\")));\n\t\treturn NULL;\n\t}\n\n\t*dsmHandle = dsm_segment_handle(dsmSegment);\n\n\tProgressMonitorData *monitor = MonitorDataFromDSMHandle(*dsmHandle, &dsmSegment);\n\n\tmonitor->stepCount = stepCount;\n\tmonitor->processId = MyProcPid;\n\treturn monitor;\n}\n\n\n/*\n * RegisterProgressMonitor shares dsmHandle with other postgres process by\n * storing it in pg_stat_get_progress_info output, to be parsed by a\n * progress retrieval command later on. This behavior may cause unrelated (but\n * hopefully harmless) rows in pg_stat_progress_vacuum output. The caller of\n * this function should provide a magic number, a unique 64 bit unsigned\n * integer, to distinguish different types of commands.\n *\n * IMPORTANT: After registering the progress monitor, all modification to the\n * data should be done using concurrency safe operations (i.e. locks and\n * atomics)\n */\nvoid\nRegisterProgressMonitor(uint64 progressTypeMagicNumber, Oid relationId,\n\t\t\t\t\t\tdsm_handle dsmHandle)\n{\n\tpgstat_progress_start_command(PROGRESS_COMMAND_VACUUM, relationId);\n\tpgstat_progress_update_param(1, dsmHandle);\n\tpgstat_progress_update_param(0, progressTypeMagicNumber);\n\n\tcurrentProgressDSMHandle = dsmHandle;\n}\n\n\n/*\n * GetCurrentProgressMonitor function returns the header and steps array related to the\n * current progress. A progress monitor should be created by calling\n * CreateProgressMonitor, before calling this function.\n */\nProgressMonitorData *\nGetCurrentProgressMonitor(void)\n{\n\tdsm_segment *dsmSegment = NULL;\n\tProgressMonitorData *monitor = MonitorDataFromDSMHandle(currentProgressDSMHandle,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t&dsmSegment);\n\n\treturn monitor;\n}\n\n\n/*\n * FinalizeCurrentProgressMonitor releases the dynamic memory segment of the current\n * progress monitoring data structure and removes the process from\n * pg_stat_get_progress_info() output. If there's no such dynamic memory\n * segment this is a no-op.\n */\nvoid\nFinalizeCurrentProgressMonitor(void)\n{\n\tif (currentProgressDSMHandle == DSM_HANDLE_INVALID)\n\t{\n\t\treturn;\n\t}\n\n\tdsm_segment *dsmSegment = dsm_find_mapping(currentProgressDSMHandle);\n\n\tif (dsmSegment != NULL)\n\t{\n\t\tdsm_detach(dsmSegment);\n\t}\n\n\tpgstat_progress_end_command();\n\n\tcurrentProgressDSMHandle = DSM_HANDLE_INVALID;\n}\n\n\n/*\n * HasProgressMonitor returns true if there is a current progress monitor,\n * by checking the variable currentProgressDSMHandle. Returns false otherwise.\n */\nbool\nHasProgressMonitor(void)\n{\n\treturn currentProgressDSMHandle != DSM_HANDLE_INVALID;\n}\n\n\n/*\n * ProgressMonitorList returns the addresses of monitors of ongoing commands, associated\n * with the given identifier magic number. The function takes a pass in\n * pg_stat_get_progress_info output, filters the rows according to the given magic number,\n * and returns the list of addresses of dynamic shared memory segments. Notice that the\n * caller detach from the attached segments with a call to DetachFromDSMSegments function.\n */\nList *\nProgressMonitorList(uint64 commandTypeMagicNumber, List **attachedDSMSegments)\n{\n\t/*\n\t * The expected magic number should reside in the first progress field and the\n\t * actual segment handle in the second but the slot ordering is 1-indexed in the\n\t * tuple table slot and there are 3 other fields before the progress fields in the\n\t * pg_stat_get_progress_info output.\n\t */\n\tconst int magicNumberIndex = 0 + 1 + 3;\n\tconst int dsmHandleIndex = 1 + 1 + 3;\n\n\t/*\n\t * Currently, Postgres' progress logging mechanism supports only the VACUUM\n\t * operations. Therefore, we identify ourselves as a VACUUM command but only fill\n\t * a couple of the available fields. Therefore the commands that use Citus' progress\n\t * monitoring API will appear in pg_stat_progress_vacuum output.\n\t */\n\ttext *commandTypeText = cstring_to_text(\"VACUUM\");\n\tDatum commandTypeDatum = PointerGetDatum(commandTypeText);\n\tList *monitorList = NIL;\n\n\tOid getProgressInfoFunctionOid = FunctionOid(\"pg_catalog\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"pg_stat_get_progress_info\",\n\t\t\t\t\t\t\t\t\t\t\t\t 1);\n\n\tReturnSetInfo *progressResultSet = FunctionCallGetTupleStore1(\n\t\tpg_stat_get_progress_info,\n\t\tgetProgressInfoFunctionOid,\n\t\tcommandTypeDatum);\n\n\tTupleTableSlot *tupleTableSlot = MakeSingleTupleTableSlot(\n\t\tprogressResultSet->setDesc,\n\t\t&TTSOpsMinimalTuple);\n\n\t/* iterate over tuples in tuple store, and send them to destination */\n\tfor (;;)\n\t{\n\t\tbool isNull = false;\n\n\t\tbool nextTuple = tuplestore_gettupleslot(progressResultSet->setResult,\n\t\t\t\t\t\t\t\t\t\t\t\t true,\n\t\t\t\t\t\t\t\t\t\t\t\t false,\n\t\t\t\t\t\t\t\t\t\t\t\t tupleTableSlot);\n\n\t\tif (!nextTuple)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tDatum magicNumberDatum = slot_getattr(tupleTableSlot, magicNumberIndex, &isNull);\n\t\tuint64 magicNumber = DatumGetUInt64(magicNumberDatum);\n\n\t\tif (!isNull && magicNumber == commandTypeMagicNumber)\n\t\t{\n\t\t\tDatum dsmHandleDatum = slot_getattr(tupleTableSlot, dsmHandleIndex, &isNull);\n\t\t\tdsm_handle dsmHandle = DatumGetUInt64(dsmHandleDatum);\n\t\t\tdsm_segment *attachedSegment = NULL;\n\t\t\tProgressMonitorData *monitor = MonitorDataFromDSMHandle(dsmHandle,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t&attachedSegment);\n\n\t\t\tif (monitor != NULL)\n\t\t\t{\n\t\t\t\t*attachedDSMSegments = lappend(*attachedDSMSegments, attachedSegment);\n\t\t\t\tmonitorList = lappend(monitorList, monitor);\n\t\t\t}\n\t\t}\n\n\t\tExecClearTuple(tupleTableSlot);\n\t}\n\n\tExecDropSingleTupleTableSlot(tupleTableSlot);\n\n\treturn monitorList;\n}\n\n\n/*\n * MonitorDataFromDSMHandle returns the progress monitoring data structure at the\n * given segment\n */\nProgressMonitorData *\nMonitorDataFromDSMHandle(dsm_handle dsmHandle, dsm_segment **attachedSegment)\n{\n\tdsm_segment *dsmSegment = dsm_find_mapping(dsmHandle);\n\n\tif (dsmSegment == NULL)\n\t{\n\t\tdsmSegment = dsm_attach(dsmHandle);\n\t}\n\n\tif (dsmSegment == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tProgressMonitorData *monitor = (ProgressMonitorData *) dsm_segment_address(\n\t\tdsmSegment);\n\n\t*attachedSegment = dsmSegment;\n\n\treturn monitor;\n}\n\n\n/*\n * ProgressMonitorSteps returns a pointer to the array of steps that are stored\n * in a progress monitor. This is simply the data right after the header, so\n * this function is trivial. The main purpose of this function is to make the\n * intent clear to readers of the code.\n *\n * NOTE: The pointer this function returns is explicitly not stored in the\n * header, because the header is shared between processes. The absolute pointer\n * to the steps can have a different value between processes though, because\n * the same piece of shared memory often has a different address in different\n * processes. So we calculate this pointer over and over to make sure we use\n * the right value for each process.\n */\nvoid *\nProgressMonitorSteps(ProgressMonitorData *monitor)\n{\n\treturn monitor + 1;\n}\n\n\n/*\n * DetachFromDSMSegments ensures that the process is detached from all of the segments in\n * the given list.\n */\nvoid\nDetachFromDSMSegments(List *dsmSegmentList)\n{\n\tdsm_segment *dsmSegment = NULL;\n\tforeach_declared_ptr(dsmSegment, dsmSegmentList)\n\t{\n\t\tdsm_detach(dsmSegment);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/relay/relay_event_utility.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * relay_event_utility.c\n *\n * Routines for handling DDL statements that relate to relay files. These\n * routines extend relation, index and constraint names in utility commands.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <stdio.h>\n#include <string.h>\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n\n#include \"access/genam.h\"\n#include \"access/hash.h\"\n#include \"access/heapam.h\"\n#include \"access/htup.h\"\n#include \"access/htup_details.h\"\n#include \"access/skey.h\"\n#include \"access/stratnum.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"lib/stringinfo.h\"\n#include \"mb/pg_wchar.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n#include \"nodes/value.h\"\n#include \"storage/lock.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/errcodes.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/palloc.h\"\n#include \"utils/relcache.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/version_compat.h\"\n\n/* Local functions forward declarations */\nstatic void RelayEventExtendConstraintAndIndexNames(AlterTableStmt *alterTableStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\tConstraint *constraint,\n\t\t\t\t\t\t\t\t\t\t\t\t\tuint64 shardId);\nstatic bool UpdateWholeRowColumnReferencesWalker(Node *node, uint64 *shardId);\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(shard_name);\n\n/*\n * RelayEventExtendNames extends relation names in the given parse tree for\n * certain utility commands. The function more specifically extends table and\n * index names in the parse tree by appending the given shardId; thereby\n * avoiding name collisions in the database among sharded tables. This function\n * has the side effect of extending relation names in the parse tree.\n */\nvoid\nRelayEventExtendNames(Node *parseTree, char *schemaName, uint64 shardId)\n{\n\t/* we don't extend names in extension or schema commands */\n\tNodeTag nodeType = nodeTag(parseTree);\n\tif (nodeType == T_CreateExtensionStmt || nodeType == T_CreateSchemaStmt ||\n\t\tnodeType == T_CreateSeqStmt || nodeType == T_AlterSeqStmt ||\n\t\tnodeType == T_CreateForeignServerStmt)\n\t{\n\t\treturn;\n\t}\n\n\tswitch (nodeType)\n\t{\n\t\tcase T_AlterObjectSchemaStmt:\n\t\t{\n\t\t\tAlterObjectSchemaStmt *alterObjectSchemaStmt =\n\t\t\t\t(AlterObjectSchemaStmt *) parseTree;\n\t\t\tObjectType objectType = alterObjectSchemaStmt->objectType;\n\n\t\t\tif (objectType == OBJECT_STATISTIC_EXT)\n\t\t\t{\n\t\t\t\tRangeVar *stat = makeRangeVarFromNameList(\n\t\t\t\t\t(List *) alterObjectSchemaStmt->object);\n\n\t\t\t\t/* append shard id */\n\t\t\t\tAppendShardIdToName(&stat->relname, shardId);\n\n\t\t\t\talterObjectSchemaStmt->object = (Node *) MakeNameListFromRangeVar(stat);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tchar **relationName = &(alterObjectSchemaStmt->relation->relname);\n\t\t\t\tchar **relationSchemaName =\n\t\t\t\t\t&(alterObjectSchemaStmt->relation->schemaname);\n\n\t\t\t\t/* prefix with schema name if it is not added already */\n\t\t\t\tSetSchemaNameIfNotExist(relationSchemaName, schemaName);\n\n\t\t\t\t/* append shardId to base relation name */\n\t\t\t\tAppendShardIdToName(relationName, shardId);\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_AlterStatsStmt:\n\t\t{\n\t\t\tAlterStatsStmt *alterStatsStmt = (AlterStatsStmt *) parseTree;\n\t\t\tRangeVar *stat = makeRangeVarFromNameList(alterStatsStmt->defnames);\n\n\t\t\tAppendShardIdToName(&stat->relname, shardId);\n\n\t\t\talterStatsStmt->defnames = MakeNameListFromRangeVar(stat);\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_AlterTableStmt:\n\t\t{\n\t\t\t/*\n\t\t\t * We append shardId to the very end of table and index, constraint\n\t\t\t * and trigger names to avoid name collisions.\n\t\t\t */\n\n\t\t\tAlterTableStmt *alterTableStmt = (AlterTableStmt *) parseTree;\n\t\t\tOid relationId = InvalidOid;\n\t\t\tchar **relationName = &(alterTableStmt->relation->relname);\n\t\t\tchar **relationSchemaName = &(alterTableStmt->relation->schemaname);\n\n\t\t\tList *commandList = alterTableStmt->cmds;\n\n\t\t\t/* prefix with schema name if it is not added already */\n\t\t\tSetSchemaNameIfNotExist(relationSchemaName, schemaName);\n\n\t\t\t/* append shardId to base relation name */\n\t\t\tAppendShardIdToName(relationName, shardId);\n\n\t\t\tAlterTableCmd *command = NULL;\n\t\t\tforeach_declared_ptr(command, commandList)\n\t\t\t{\n\t\t\t\tif (command->subtype == AT_AddConstraint)\n\t\t\t\t{\n\t\t\t\t\tConstraint *constraint = (Constraint *) command->def;\n\t\t\t\t\tRelayEventExtendConstraintAndIndexNames(alterTableStmt, constraint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardId);\n\t\t\t\t}\n\t\t\t\telse if (command->subtype == AT_AddColumn)\n\t\t\t\t{\n\t\t\t\t\tColumnDef *columnDefinition = (ColumnDef *) command->def;\n\t\t\t\t\tConstraint *constraint = NULL;\n\t\t\t\t\tforeach_declared_ptr(constraint, columnDefinition->constraints)\n\t\t\t\t\t{\n\t\t\t\t\t\tRelayEventExtendConstraintAndIndexNames(alterTableStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconstraint, shardId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (command->subtype == AT_DropConstraint ||\n\t\t\t\t\t\t command->subtype == AT_ValidateConstraint)\n\t\t\t\t{\n\t\t\t\t\tchar **constraintName = &(command->name);\n\t\t\t\t\tconst bool constraintMissingOk = true;\n\n\t\t\t\t\tif (!OidIsValid(relationId))\n\t\t\t\t\t{\n\t\t\t\t\t\tconst bool rvMissingOk = false;\n\t\t\t\t\t\trelationId = RangeVarGetRelid(alterTableStmt->relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  AccessShareLock,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  rvMissingOk);\n\t\t\t\t\t}\n\n\t\t\t\t\tOid constraintOid = get_relation_constraint_oid(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommand->name,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconstraintMissingOk);\n\t\t\t\t\tif (!OidIsValid(constraintOid))\n\t\t\t\t\t{\n\t\t\t\t\t\tAppendShardIdToName(constraintName, shardId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (command->subtype == AT_ClusterOn)\n\t\t\t\t{\n\t\t\t\t\tchar **indexName = &(command->name);\n\t\t\t\t\tAppendShardIdToName(indexName, shardId);\n\t\t\t\t}\n\t\t\t\telse if (command->subtype == AT_ReplicaIdentity)\n\t\t\t\t{\n\t\t\t\t\tReplicaIdentityStmt *replicaIdentity =\n\t\t\t\t\t\t(ReplicaIdentityStmt *) command->def;\n\n\t\t\t\t\tif (replicaIdentity->identity_type == REPLICA_IDENTITY_INDEX)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar **indexName = &(replicaIdentity->name);\n\t\t\t\t\t\tAppendShardIdToName(indexName, shardId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (command->subtype == AT_EnableTrig ||\n\t\t\t\t\t\t command->subtype == AT_DisableTrig ||\n\t\t\t\t\t\t command->subtype == AT_EnableAlwaysTrig ||\n\t\t\t\t\t\t command->subtype == AT_EnableReplicaTrig)\n\t\t\t\t{\n\t\t\t\t\tchar **triggerName = &(command->name);\n\t\t\t\t\tAppendShardIdToName(triggerName, shardId);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_AlterOwnerStmt:\n\t\t{\n\t\t\tAlterOwnerStmt *alterOwnerStmt = castNode(AlterOwnerStmt, parseTree);\n\n\t\t\t/* we currently extend names in alter owner statements only for statistics */\n\t\t\tAssert(alterOwnerStmt->objectType == OBJECT_STATISTIC_EXT);\n\n\t\t\tRangeVar *stat = makeRangeVarFromNameList((List *) alterOwnerStmt->object);\n\n\t\t\tAppendShardIdToName(&stat->relname, shardId);\n\n\t\t\talterOwnerStmt->object = (Node *) MakeNameListFromRangeVar(stat);\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_ClusterStmt:\n\t\t{\n\t\t\tClusterStmt *clusterStmt = (ClusterStmt *) parseTree;\n\n\t\t\t/* we do not support clustering the entire database */\n\t\t\tif (clusterStmt->relation == NULL)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"cannot extend name for multi-relation cluster\")));\n\t\t\t}\n\n\t\t\tchar **relationName = &(clusterStmt->relation->relname);\n\t\t\tchar **relationSchemaName = &(clusterStmt->relation->schemaname);\n\n\t\t\t/* prefix with schema name if it is not added already */\n\t\t\tSetSchemaNameIfNotExist(relationSchemaName, schemaName);\n\n\t\t\tAppendShardIdToName(relationName, shardId);\n\n\t\t\tif (clusterStmt->indexname != NULL)\n\t\t\t{\n\t\t\t\tchar **indexName = &(clusterStmt->indexname);\n\t\t\t\tAppendShardIdToName(indexName, shardId);\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_CreateForeignTableStmt:\n\t\tcase T_CreateStmt:\n\t\t{\n\t\t\tCreateStmt *createStmt = (CreateStmt *) parseTree;\n\t\t\tchar **relationName = &(createStmt->relation->relname);\n\t\t\tchar **relationSchemaName = &(createStmt->relation->schemaname);\n\n\t\t\t/* prefix with schema name if it is not added already */\n\t\t\tSetSchemaNameIfNotExist(relationSchemaName, schemaName);\n\n\t\t\tAppendShardIdToName(relationName, shardId);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_CreateTrigStmt:\n\t\t{\n\t\t\tCreateTrigStmt *createTriggerStmt = (CreateTrigStmt *) parseTree;\n\t\t\tCreateTriggerEventExtendNames(createTriggerStmt, schemaName, shardId);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_AlterObjectDependsStmt:\n\t\t{\n\t\t\tAlterObjectDependsStmt *alterTriggerDependsStmt =\n\t\t\t\t(AlterObjectDependsStmt *) parseTree;\n\t\t\tObjectType objectType = alterTriggerDependsStmt->objectType;\n\n\t\t\tif (objectType == OBJECT_TRIGGER)\n\t\t\t{\n\t\t\t\tAlterTriggerDependsEventExtendNames(alterTriggerDependsStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\tschemaName, shardId);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tereport(WARNING, (errmsg(\"unsafe object type in alter object \"\n\t\t\t\t\t\t\t\t\t\t \"depends statement\"),\n\t\t\t\t\t\t\t\t  errdetail(\"Object type: %u\", (uint32) objectType)));\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_DropStmt:\n\t\t{\n\t\t\tDropStmt *dropStmt = (DropStmt *) parseTree;\n\t\t\tObjectType objectType = dropStmt->removeType;\n\n\t\t\tif (objectType == OBJECT_TABLE || objectType == OBJECT_INDEX ||\n\t\t\t\tobjectType == OBJECT_FOREIGN_TABLE || objectType == OBJECT_FOREIGN_SERVER)\n\t\t\t{\n\t\t\t\tString *relationSchemaNameValue = NULL;\n\t\t\t\tString *relationNameValue = NULL;\n\n\t\t\t\tuint32 dropCount = list_length(dropStmt->objects);\n\t\t\t\tif (dropCount > 1)\n\t\t\t\t{\n\t\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t\t(errmsg(\"cannot extend name for multiple drop objects\")));\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t * We now need to extend a single relation or index name. To be\n\t\t\t\t * able to do this extension, we need to extract the names'\n\t\t\t\t * addresses from the value objects they are stored in. Other-\n\t\t\t\t * wise, the repalloc called in AppendShardIdToName() will not\n\t\t\t\t * have the correct memory address for the name.\n\t\t\t\t */\n\n\t\t\t\tList *relationNameList = (List *) linitial(dropStmt->objects);\n\t\t\t\tint relationNameListLength = list_length(relationNameList);\n\n\t\t\t\tswitch (relationNameListLength)\n\t\t\t\t{\n\t\t\t\t\tcase 1:\n\t\t\t\t\t{\n\t\t\t\t\t\trelationNameValue = linitial(relationNameList);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase 2:\n\t\t\t\t\t{\n\t\t\t\t\t\trelationSchemaNameValue = linitial(relationNameList);\n\t\t\t\t\t\trelationNameValue = lsecond(relationNameList);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase 3:\n\t\t\t\t\t{\n\t\t\t\t\t\trelationSchemaNameValue = lsecond(relationNameList);\n\t\t\t\t\t\trelationNameValue = lthird(relationNameList);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t{\n\t\t\t\t\t\tereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),\n\t\t\t\t\t\t\t\t\t\terrmsg(\"improper relation name: \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t\t\t\t   NameListToString(relationNameList))));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* prefix with schema name if it is not added already */\n\t\t\t\tif (relationSchemaNameValue == NULL)\n\t\t\t\t{\n\t\t\t\t\tString *schemaNameValue = makeString(pstrdup(schemaName));\n\t\t\t\t\trelationNameList = lcons(schemaNameValue, relationNameList);\n\t\t\t\t}\n\n\t\t\t\tchar **relationName = &(strVal(relationNameValue));\n\t\t\t\tAppendShardIdToName(relationName, shardId);\n\t\t\t}\n\t\t\telse if (objectType == OBJECT_POLICY)\n\t\t\t{\n\t\t\t\tDropPolicyEventExtendNames(dropStmt, schemaName, shardId);\n\t\t\t}\n\t\t\telse if (objectType == OBJECT_TRIGGER)\n\t\t\t{\n\t\t\t\tDropTriggerEventExtendNames(dropStmt, schemaName, shardId);\n\t\t\t}\n\t\t\telse if (objectType == OBJECT_STATISTIC_EXT)\n\t\t\t{\n\t\t\t\tList *shardStatisticsList = NIL;\n\t\t\t\tList *objectNameList = NULL;\n\t\t\t\tforeach_declared_ptr(objectNameList, dropStmt->objects)\n\t\t\t\t{\n\t\t\t\t\tRangeVar *stat = makeRangeVarFromNameList(objectNameList);\n\n\t\t\t\t\tSetSchemaNameIfNotExist(&stat->schemaname, schemaName);\n\n\t\t\t\t\tAppendShardIdToName(&stat->relname, shardId);\n\t\t\t\t\tshardStatisticsList = lappend(shardStatisticsList,\n\t\t\t\t\t\t\t\t\t\t\t\t  MakeNameListFromRangeVar(stat));\n\t\t\t\t}\n\n\t\t\t\tdropStmt->objects = shardStatisticsList;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tereport(WARNING, (errmsg(\"unsafe object type in drop statement\"),\n\t\t\t\t\t\t\t\t  errdetail(\"Object type: %u\", (uint32) objectType)));\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\n\t\tcase T_GrantStmt:\n\t\t{\n\t\t\tGrantStmt *grantStmt = (GrantStmt *) parseTree;\n\t\t\tif (grantStmt->targtype == ACL_TARGET_OBJECT &&\n\t\t\t\tgrantStmt->objtype == OBJECT_TABLE)\n\t\t\t{\n\t\t\t\tRangeVar *relation = NULL;\n\t\t\t\tforeach_declared_ptr(relation, grantStmt->objects)\n\t\t\t\t{\n\t\t\t\t\tchar **relationName = &(relation->relname);\n\t\t\t\t\tchar **relationSchemaName = &(relation->schemaname);\n\n\t\t\t\t\t/* prefix with schema name if it is not added already */\n\t\t\t\t\tSetSchemaNameIfNotExist(relationSchemaName, schemaName);\n\n\t\t\t\t\tAppendShardIdToName(relationName, shardId);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_CreatePolicyStmt:\n\t\t{\n\t\t\tCreatePolicyEventExtendNames((CreatePolicyStmt *) parseTree, schemaName,\n\t\t\t\t\t\t\t\t\t\t shardId);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_AlterPolicyStmt:\n\t\t{\n\t\t\tAlterPolicyEventExtendNames((AlterPolicyStmt *) parseTree, schemaName,\n\t\t\t\t\t\t\t\t\t\tshardId);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_IndexStmt:\n\t\t{\n\t\t\tIndexStmt *indexStmt = (IndexStmt *) parseTree;\n\t\t\tchar **relationName = &(indexStmt->relation->relname);\n\t\t\tchar **indexName = &(indexStmt->idxname);\n\t\t\tchar **relationSchemaName = &(indexStmt->relation->schemaname);\n\n\t\t\t/*\n\t\t\t * Concurrent index statements cannot run within a transaction block.\n\t\t\t * Therefore, we do not support them.\n\t\t\t */\n\t\t\tif (indexStmt->concurrent)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"cannot extend name for concurrent index\")));\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * In the regular DDL execution code path (for non-sharded tables),\n\t\t\t * if the index statement results from a table creation command, the\n\t\t\t * indexName may be null. For sharded tables however, we intercept\n\t\t\t * that code path and explicitly set the index name. Therefore, the\n\t\t\t * index name in here cannot be null.\n\t\t\t */\n\t\t\tif ((*indexName) == NULL)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"cannot extend name for null index name\")));\n\t\t\t}\n\n\t\t\t/* extend ColumnRef nodes in the IndexStmt with the shardId */\n\t\t\tUpdateWholeRowColumnReferencesWalker((Node *) indexStmt->indexParams,\n\t\t\t\t\t\t\t\t\t\t\t\t &shardId);\n\n\t\t\t/* prefix with schema name if it is not added already */\n\t\t\tSetSchemaNameIfNotExist(relationSchemaName, schemaName);\n\n\t\t\tAppendShardIdToName(relationName, shardId);\n\t\t\tAppendShardIdToName(indexName, shardId);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_ReindexStmt:\n\t\t{\n\t\t\tReindexStmt *reindexStmt = (ReindexStmt *) parseTree;\n\n\t\t\tReindexObjectType objectType = reindexStmt->kind;\n\t\t\tif (objectType == REINDEX_OBJECT_TABLE || objectType == REINDEX_OBJECT_INDEX)\n\t\t\t{\n\t\t\t\tchar **objectName = &(reindexStmt->relation->relname);\n\t\t\t\tchar **objectSchemaName = &(reindexStmt->relation->schemaname);\n\n\t\t\t\t/* prefix with schema name if it is not added already */\n\t\t\t\tSetSchemaNameIfNotExist(objectSchemaName, schemaName);\n\n\t\t\t\tAppendShardIdToName(objectName, shardId);\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_RenameStmt:\n\t\t{\n\t\t\tRenameStmt *renameStmt = (RenameStmt *) parseTree;\n\t\t\tObjectType objectType = renameStmt->renameType;\n\n\t\t\tif (objectType == OBJECT_TABLE || objectType == OBJECT_INDEX ||\n\t\t\t\tobjectType == OBJECT_FOREIGN_TABLE)\n\t\t\t{\n\t\t\t\tchar **oldRelationName = &(renameStmt->relation->relname);\n\t\t\t\tchar **newRelationName = &(renameStmt->newname);\n\t\t\t\tchar **objectSchemaName = &(renameStmt->relation->schemaname);\n\n\t\t\t\t/* prefix with schema name if it is not added already */\n\t\t\t\tSetSchemaNameIfNotExist(objectSchemaName, schemaName);\n\n\t\t\t\tAppendShardIdToName(oldRelationName, shardId);\n\t\t\t\tAppendShardIdToName(newRelationName, shardId);\n\t\t\t}\n\t\t\telse if (objectType == OBJECT_COLUMN)\n\t\t\t{\n\t\t\t\tchar **relationName = &(renameStmt->relation->relname);\n\t\t\t\tchar **objectSchemaName = &(renameStmt->relation->schemaname);\n\n\t\t\t\t/* prefix with schema name if it is not added already */\n\t\t\t\tSetSchemaNameIfNotExist(objectSchemaName, schemaName);\n\n\t\t\t\tAppendShardIdToName(relationName, shardId);\n\t\t\t}\n\t\t\telse if (objectType == OBJECT_TRIGGER)\n\t\t\t{\n\t\t\t\tAlterTriggerRenameEventExtendNames(renameStmt, schemaName, shardId);\n\t\t\t}\n\t\t\telse if (objectType == OBJECT_POLICY)\n\t\t\t{\n\t\t\t\tRenamePolicyEventExtendNames(renameStmt, schemaName, shardId);\n\t\t\t}\n\t\t\telse if (objectType == OBJECT_STATISTIC_EXT)\n\t\t\t{\n\t\t\t\tRangeVar *stat = makeRangeVarFromNameList((List *) renameStmt->object);\n\n\t\t\t\tAppendShardIdToName(&stat->relname, shardId);\n\t\t\t\tAppendShardIdToName(&renameStmt->newname, shardId);\n\n\t\t\t\tSetSchemaNameIfNotExist(&stat->schemaname, schemaName);\n\n\t\t\t\trenameStmt->object = (Node *) MakeNameListFromRangeVar(stat);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tereport(WARNING, (errmsg(\"unsafe object type in rename statement\"),\n\t\t\t\t\t\t\t\t  errdetail(\"Object type: %u\", (uint32) objectType)));\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_CreateStatsStmt:\n\t\t{\n\t\t\tCreateStatsStmt *createStatsStmt = (CreateStatsStmt *) parseTree;\n\n\t\t\t/* because CREATE STATISTICS statements can only have one relation */\n\t\t\tRangeVar *relation = linitial(createStatsStmt->relations);\n\n\t\t\tchar **relationName = &(relation->relname);\n\t\t\tchar **objectSchemaName = &(relation->schemaname);\n\n\t\t\tSetSchemaNameIfNotExist(objectSchemaName, schemaName);\n\t\t\tAppendShardIdToName(relationName, shardId);\n\n\t\t\tRangeVar *stat = makeRangeVarFromNameList(createStatsStmt->defnames);\n\t\t\tAppendShardIdToName(&stat->relname, shardId);\n\n\t\t\tcreateStatsStmt->defnames = MakeNameListFromRangeVar(stat);\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_TruncateStmt:\n\t\t{\n\t\t\t/*\n\t\t\t * We currently do not support truncate statements. This is\n\t\t\t * primarily because truncates allow implicit modifications to\n\t\t\t * sequences through table column dependencies. As we have not\n\t\t\t * determined our dependency model for sequences, we error here.\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\"cannot extend name for truncate statement\")));\n\t\t\tbreak;\n\t\t}\n\n\t\tcase T_SecLabelStmt:\n\t\t{\n\t\t\tSecLabelStmt *secLabelStmt = (SecLabelStmt *) parseTree;\n\n\t\t\t/* Should be looking at a security label for a table or column */\n\t\t\tif (secLabelStmt->objtype == OBJECT_TABLE || secLabelStmt->objtype ==\n\t\t\t\tOBJECT_COLUMN)\n\t\t\t{\n\t\t\t\tList *qualified_name = (List *) secLabelStmt->object;\n\t\t\t\tString *table_name = NULL;\n\n\t\t\t\tswitch (list_length(qualified_name))\n\t\t\t\t{\n\t\t\t\t\tcase 1:\n\t\t\t\t\t{\n\t\t\t\t\t\ttable_name = castNode(String, linitial(qualified_name));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase 2:\n\t\t\t\t\tcase 3:\n\t\t\t\t\t{\n\t\t\t\t\t\ttable_name = castNode(String, lsecond(qualified_name));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t{\n\t\t\t\t\t\t/* Unlikely, but just in case */\n\t\t\t\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\t\t\t\"unhandled name type in security label; name is: \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t\t\t\tNameListToString(qualified_name))));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t/* Now change the table name: <dist table> -> <shard table> */\n\t\t\t\tchar *relationName = strVal(table_name);\n\t\t\t\tAppendShardIdToName(&relationName, shardId);\n\t\t\t\tstrVal(table_name) = relationName;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tereport(WARNING, (errmsg(\n\t\t\t\t\t\t\t\t\t  \"unsafe object type in security label statement\"),\n\t\t\t\t\t\t\t\t  errdetail(\"Object type: %u\",\n\t\t\t\t\t\t\t\t\t\t\t(uint32) secLabelStmt->objtype)));\n\t\t\t}\n\n\t\t\tbreak; /* End of handling Security Label */\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"unsafe statement type in name extension\"),\n\t\t\t\t\t\t\t  errdetail(\"Statement type: %u\", (uint32) nodeType)));\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n/*\n * RelayEventExtendConstraintAndIndexNames extends the names of constraints\n * and indexes in given constraint with the shardId.\n */\nstatic void\nRelayEventExtendConstraintAndIndexNames(AlterTableStmt *alterTableStmt,\n\t\t\t\t\t\t\t\t\t\tConstraint *constraint,\n\t\t\t\t\t\t\t\t\t\tuint64 shardId)\n{\n\tchar **constraintName = &(constraint->conname);\n\tconst bool missingOk = false;\n\tOid relationId = RangeVarGetRelid(alterTableStmt->relation,\n\t\t\t\t\t\t\t\t\t  AccessShareLock,\n\t\t\t\t\t\t\t\t\t  missingOk);\n\n\tif (constraint->indexname)\n\t{\n\t\tchar **indexName = &(constraint->indexname);\n\t\tAppendShardIdToName(indexName, shardId);\n\t}\n\n\t/*\n\t * Append shardId to constraint names if\n\t *  - table is not partitioned or\n\t *  - constraint is not a CHECK constraint\n\t *\n\t * We do not want to append shardId to partitioned table shards because\n\t * the names of constraints will be inherited, and the shardId will no\n\t * longer be valid for the child table.\n\t *\n\t * See MergeConstraintsIntoExisting function in Postgres that requires\n\t * inherited check constraints in child tables to have the same name\n\t * with those in parent tables.\n\t */\n\tif (!PartitionedTable(relationId) ||\n\t\tconstraint->contype != CONSTR_CHECK)\n\t{\n\t\t/*\n\t\t * constraint->conname could be empty in the case of\n\t\t * ADD {PRIMARY KEY, UNIQUE} USING INDEX.\n\t\t * In this case, already extended index name will be used by postgres.\n\t\t */\n\t\tif (constraint->conname != NULL)\n\t\t{\n\t\t\tAppendShardIdToName(constraintName, shardId);\n\t\t}\n\t}\n}\n\n\n/*\n * RelayEventExtendNamesForInterShardCommands extends relation names in the given parse\n * tree for certain utility commands. The function more specifically extends table, index\n * and constraint names in the parse tree by appending the given shardId; thereby\n * avoiding name collisions in the database among sharded tables. This function\n * has the side effect of extending relation names in the parse tree.\n */\nvoid\nRelayEventExtendNamesForInterShardCommands(Node *parseTree, uint64 leftShardId,\n\t\t\t\t\t\t\t\t\t\t   char *leftShardSchemaName, uint64 rightShardId,\n\t\t\t\t\t\t\t\t\t\t   char *rightShardSchemaName)\n{\n\tNodeTag nodeType = nodeTag(parseTree);\n\n\tswitch (nodeType)\n\t{\n\t\tcase T_AlterTableStmt:\n\t\t{\n\t\t\tAlterTableStmt *alterTableStmt = (AlterTableStmt *) parseTree;\n\t\t\tList *commandList = alterTableStmt->cmds;\n\n\t\t\tAlterTableCmd *command = NULL;\n\t\t\tforeach_declared_ptr(command, commandList)\n\t\t\t{\n\t\t\t\tchar **referencedTableName = NULL;\n\t\t\t\tchar **relationSchemaName = NULL;\n\n\t\t\t\tif (command->subtype == AT_AddConstraint)\n\t\t\t\t{\n\t\t\t\t\tConstraint *constraint = (Constraint *) command->def;\n\t\t\t\t\tif (constraint->contype == CONSTR_FOREIGN)\n\t\t\t\t\t{\n\t\t\t\t\t\treferencedTableName = &(constraint->pktable->relname);\n\t\t\t\t\t\trelationSchemaName = &(constraint->pktable->schemaname);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (command->subtype == AT_AddColumn)\n\t\t\t\t{\n\t\t\t\t\tColumnDef *columnDefinition = (ColumnDef *) command->def;\n\t\t\t\t\tList *columnConstraints = columnDefinition->constraints;\n\n\t\t\t\t\tConstraint *constraint = NULL;\n\t\t\t\t\tforeach_declared_ptr(constraint, columnConstraints)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (constraint->contype == CONSTR_FOREIGN)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treferencedTableName = &(constraint->pktable->relname);\n\t\t\t\t\t\t\trelationSchemaName = &(constraint->pktable->schemaname);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (command->subtype == AT_AttachPartition ||\n\t\t\t\t\t\t command->subtype == AT_DetachPartition)\n\t\t\t\t{\n\t\t\t\t\tPartitionCmd *partitionCommand = (PartitionCmd *) command->def;\n\n\t\t\t\t\treferencedTableName = &(partitionCommand->name->relname);\n\t\t\t\t\trelationSchemaName = &(partitionCommand->name->schemaname);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t/* prefix with schema name if it is not added already */\n\t\t\t\tSetSchemaNameIfNotExist(relationSchemaName, rightShardSchemaName);\n\n\t\t\t\t/*\n\t\t\t\t * We will not append shard id to left shard name. This will be\n\t\t\t\t * handled when we drop into RelayEventExtendNames.\n\t\t\t\t */\n\t\t\t\tAppendShardIdToName(referencedTableName, rightShardId);\n\t\t\t}\n\n\t\t\t/* drop into RelayEventExtendNames for non-inter table commands */\n\t\t\tRelayEventExtendNames(parseTree, leftShardSchemaName, leftShardId);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"unsafe statement type in name extension\"),\n\t\t\t\t\t\t\t  errdetail(\"Statement type: %u\", (uint32) nodeType)));\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n/*\n * UpdateWholeRowColumnReferencesWalker extends ColumnRef nodes that end with A_Star\n * with the given shardId.\n *\n * ColumnRefs that don't reference A_Star are not extended as catalog access isn't\n * allowed here and we don't otherwise have enough context to disambiguate a\n * field name that is identical to the table name.\n */\nstatic bool\nUpdateWholeRowColumnReferencesWalker(Node *node, uint64 *shardId)\n{\n\tbool walkIsComplete = false;\n\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, IndexElem))\n\t{\n\t\tIndexElem *indexElem = (IndexElem *) node;\n\n\t\twalkIsComplete = raw_expression_tree_walker(indexElem->expr,\n\t\t\t\t\t\t\t\t\t\t\t\t\tUpdateWholeRowColumnReferencesWalker,\n\t\t\t\t\t\t\t\t\t\t\t\t\tshardId);\n\t}\n\telse if (IsA(node, ColumnRef))\n\t{\n\t\tColumnRef *columnRef = (ColumnRef *) node;\n\t\tNode *lastField = llast(columnRef->fields);\n\n\t\tif (IsA(lastField, A_Star))\n\t\t{\n\t\t\t/*\n\t\t\t * ColumnRef fields list ends with an A_Star, so we can blindly\n\t\t\t * extend the penultimate element with the shardId.\n\t\t\t */\n\t\t\tint colrefFieldCount = list_length(columnRef->fields);\n\t\t\tString *relnameValue = list_nth(columnRef->fields, colrefFieldCount - 2);\n\t\t\tAssert(IsA(relnameValue, String));\n\n\t\t\tAppendShardIdToName(&strVal(relnameValue), *shardId);\n\t\t}\n\n\t\t/* might be more than one ColumnRef to visit */\n\t\twalkIsComplete = false;\n\t}\n\telse\n\t{\n\t\twalkIsComplete = raw_expression_tree_walker(node,\n\t\t\t\t\t\t\t\t\t\t\t\t\tUpdateWholeRowColumnReferencesWalker,\n\t\t\t\t\t\t\t\t\t\t\t\t\tshardId);\n\t}\n\n\treturn walkIsComplete;\n}\n\n\n/*\n * SetSchemaNameIfNotExist function checks whether schemaName is set and if it is not set\n * it sets its value to given newSchemaName.\n */\nvoid\nSetSchemaNameIfNotExist(char **schemaName, const char *newSchemaName)\n{\n\tif ((*schemaName) == NULL)\n\t{\n\t\t*schemaName = pstrdup(newSchemaName);\n\t}\n}\n\n\n/*\n * AppendShardIdToName appends shardId to the given name. The function takes in\n * the name's address in order to reallocate memory for the name in the same\n * memory context the name was originally created in.\n */\nvoid\nAppendShardIdToName(char **name, uint64 shardId)\n{\n\tchar extendedName[NAMEDATALEN];\n\tint nameLength = strlen(*name);\n\tchar shardIdAndSeparator[NAMEDATALEN];\n\tuint32 longNameHash = 0;\n\tint multiByteClipLength = 0;\n\n\tif (nameLength >= NAMEDATALEN)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_NAME_TOO_LONG),\n\t\t\t\t\t\terrmsg(\"identifier must be less than %d characters\",\n\t\t\t\t\t\t\t   NAMEDATALEN)));\n\t}\n\n\tSafeSnprintf(shardIdAndSeparator, NAMEDATALEN, \"%c\" UINT64_FORMAT,\n\t\t\t\t SHARD_NAME_SEPARATOR, shardId);\n\tint shardIdAndSeparatorLength = strlen(shardIdAndSeparator);\n\n\t/*\n\t * If *name strlen is < (NAMEDATALEN - shardIdAndSeparatorLength),\n\t * it is safe merely to append the separator and shardId.\n\t */\n\n\tif (nameLength < (NAMEDATALEN - shardIdAndSeparatorLength))\n\t{\n\t\tSafeSnprintf(extendedName, NAMEDATALEN, \"%s%s\", (*name), shardIdAndSeparator);\n\t}\n\t/*\n\t * Otherwise, we need to truncate the name further to accommodate\n\t * a sufficient hash value. The resulting name will avoid collision\n\t * with other hashed names such that for any given schema with\n\t * 90 distinct object names that are long enough to require hashing\n\t * (typically 57-63 characters), the chance of a collision existing is:\n\t *\n\t * If randomly generated UTF8 names:\n\t *     (1e-6) * (9.39323783788e-114) ~= (9.39e-120)\n\t * If random case-insensitive ASCII names (letter first, 37 useful characters):\n\t *     (1e-6) * (2.80380202421e-74) ~= (2.8e-80)\n\t * If names sharing only N distinct 45- to 47-character prefixes:\n\t *     (1e-6) * (1/N) = (1e-6/N)\n\t *     1e-7 for 10 distinct prefixes\n\t *     5e-8 for 20 distinct prefixes\n\t *\n\t * In practice, since shard IDs are globally unique, the risk of name collision\n\t * exists only amongst objects that pertain to a single distributed table\n\t * and are created for each shard: the table name and the names of any indexes\n\t * or index-backed constraints. Since there are typically less than five such\n\t * names, and almost never more than ten, the expected collision rate even in\n\t * the worst case (ten names share same 45- to 47-character prefix) is roughly\n\t * 1e-8: one in 100 million schemas will experience a name collision only if ALL\n\t * 100 million schemas present the worst-case scenario.\n\t */\n\telse\n\t{\n\t\tlongNameHash = hash_any((unsigned char *) (*name), nameLength);\n\t\tmultiByteClipLength = pg_mbcliplen(*name, nameLength, (NAMEDATALEN -\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   shardIdAndSeparatorLength -\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   10));\n\t\tSafeSnprintf(extendedName, NAMEDATALEN, \"%.*s%c%.8x%s\",\n\t\t\t\t\t multiByteClipLength, (*name),\n\t\t\t\t\t SHARD_NAME_SEPARATOR, longNameHash,\n\t\t\t\t\t shardIdAndSeparator);\n\t}\n\n\t(*name) = (char *) repalloc((*name), NAMEDATALEN);\n\tint neededBytes = SafeSnprintf((*name), NAMEDATALEN, \"%s\", extendedName);\n\tif (neededBytes < 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY),\n\t\t\t\t\t\terrmsg(\"out of memory: %m\")));\n\t}\n\telse if (neededBytes >= NAMEDATALEN)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),\n\t\t\t\t\t\terrmsg(\"new name %s would be truncated at %d characters\",\n\t\t\t\t\t\t\t   extendedName, NAMEDATALEN)));\n\t}\n}\n\n\n/*\n * shard_name() provides a PG function interface to AppendShardNameToId above.\n * Returns the name of a shard as a quoted schema-qualified identifier.\n */\nDatum\nshard_name(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\tint64 shardId = PG_GETARG_INT64(1);\n\tbool skipQualifyPublic = PG_GETARG_BOOL(2);\n\n\tchar *qualifiedName = NULL;\n\n\tif (shardId <= 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"shard_id cannot be zero or negative value\")));\n\t}\n\n\n\tif (!OidIsValid(relationId))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"object_name does not reference a valid relation\")));\n\t}\n\n\tchar *relationName = get_rel_name(relationId);\n\n\tif (relationName == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"object_name does not reference a valid relation\")));\n\t}\n\n\tAppendShardIdToName(&relationName, shardId);\n\n\tOid schemaId = get_rel_namespace(relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\n\tif (skipQualifyPublic && strncmp(schemaName, \"public\", NAMEDATALEN) == 0)\n\t{\n\t\tqualifiedName = (char *) quote_identifier(relationName);\n\t}\n\telse\n\t{\n\t\tqualifiedName = quote_qualified_identifier(schemaName, relationName);\n\t}\n\n\tPG_RETURN_TEXT_P(cstring_to_text(qualifiedName));\n}\n"
  },
  {
    "path": "src/backend/distributed/replication/multi_logical_replication.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_logical_replication.c\n *\n * This file contains functions to use logical replication on the distributed\n * tables for moving/replicating shards.\n *\n * Copyright (c) 2017, Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"access/sysattr.h\"\n#include \"access/xact.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_subscription_rel.h\"\n#include \"commands/dbcommands.h\"\n#include \"common/hashfn.h\"\n#include \"nodes/bitmapset.h\"\n#include \"parser/scansup.h\"\n#include \"postmaster/interrupt.h\"\n#include \"storage/ipc.h\"\n#include \"storage/latch.h\"\n#include \"storage/lock.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/fmgrprotos.h\"\n#include \"utils/formatting.h\"\n#include \"utils/guc.h\"\n#include \"utils/inval.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/pg_lsn.h\"\n#include \"utils/rel.h\"\n#include \"utils/ruleutils.h\"\n#include \"utils/syscache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_logical_replication.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/priority.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/shard_rebalancer.h\"\n#include \"distributed/shard_transfer.h\"\n#include \"distributed/version_compat.h\"\n\n#define CURRENT_LOG_POSITION_COMMAND \"SELECT pg_current_wal_lsn()\"\n\n/* decimal representation of Adler-16 hash value of citus_shard_move_publication */\n#define SHARD_MOVE_ADVISORY_LOCK_FIRST_KEY 44000\n\n/* decimal representation of Adler-16 hash value of citus_shard_move_subscription */\n#define SHARD_MOVE_ADVISORY_LOCK_SECOND_KEY 55152\n\nstatic const char *publicationPrefix[] = {\n\t[SHARD_MOVE] = \"citus_shard_move_publication_\",\n\t[SHARD_SPLIT] = \"citus_shard_split_publication_\",\n};\n\nstatic const char *replicationSlotPrefix[] = {\n\t[SHARD_MOVE] = \"citus_shard_move_slot_\",\n\t[SHARD_SPLIT] = \"citus_shard_split_slot_\",\n};\n\n/*\n * IMPORTANT: All the subscription names should start with \"citus_\". Otherwise\n * our utility hook does not defend against non-superusers altering or dropping\n * them, which is important for security purposes.\n *\n * We should also keep these in sync with IsCitusShardTransferBackend().\n */\nstatic const char *subscriptionPrefix[] = {\n\t[SHARD_MOVE] = \"citus_shard_move_subscription_\",\n\t[SHARD_SPLIT] = \"citus_shard_split_subscription_\",\n};\n\nstatic const char *subscriptionRolePrefix[] = {\n\t[SHARD_MOVE] = \"citus_shard_move_subscription_role_\",\n\t[SHARD_SPLIT] = \"citus_shard_split_subscription_role_\",\n};\n\n\n/* GUC variable, defaults to 2 hours */\nint LogicalReplicationTimeout = 2 * 60 * 60 * 1000;\n\n\n/* see the comment in master_move_shard_placement */\nbool PlacementMovedUsingLogicalReplicationInTX = false;\n\n/* report in every 10 seconds */\nstatic int logicalReplicationProgressReportTimeout = 10 * 1000;\n\n\nstatic List * PrepareReplicationSubscriptionList(List *shardList);\nstatic List * GetReplicaIdentityCommandListForShard(Oid relationId, uint64 shardId);\nstatic List * GetIndexCommandListForShardBackingReplicaIdentity(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tuint64 shardId);\nstatic void CreatePostLogicalReplicationDataLoadObjects(List *logicalRepTargetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tLogicalRepType type,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbool skipInterShardRelationships);\nstatic void ExecuteCreateIndexCommands(List *logicalRepTargetList);\nstatic void ExecuteCreateConstraintsBackedByIndexCommands(List *logicalRepTargetList);\nstatic List * ConvertNonExistingPlacementDDLCommandsToTasks(List *shardCommandList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar *targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tint targetNodePort);\nstatic void ExecuteClusterOnCommands(List *logicalRepTargetList);\nstatic void ExecuteCreateIndexStatisticsCommands(List *logicalRepTargetList);\nstatic void ExecuteRemainingPostLoadTableCommands(List *logicalRepTargetList);\nstatic char * escape_param_str(const char *str);\nstatic XLogRecPtr GetRemoteLSN(MultiConnection *connection, char *command);\nstatic void WaitForMiliseconds(long timeout);\nstatic XLogRecPtr GetSubscriptionPosition(GroupedLogicalRepTargets *\n\t\t\t\t\t\t\t\t\t\t  groupedLogicalRepTargets);\n\nstatic HTAB * CreateShardMovePublicationInfoHash(WorkerNode *targetNode,\n\t\t\t\t\t\t\t\t\t\t\t\t List *shardIntervals);\nstatic List * CreateShardMoveLogicalRepTargetList(HTAB *publicationInfoHash,\n\t\t\t\t\t\t\t\t\t\t\t\t  List *shardList);\nstatic void WaitForGroupedLogicalRepTargetsToCatchUp(XLogRecPtr sourcePosition,\n\t\t\t\t\t\t\t\t\t\t\t\t\t GroupedLogicalRepTargets *\n\t\t\t\t\t\t\t\t\t\t\t\t\t groupedLogicalRepTargets);\n\n/*\n * LogicallyReplicateShards replicates a list of shards from one node to another\n * using logical replication. Once replication is reasonably caught up, writes\n * are blocked and then the publication and subscription are dropped.\n *\n * The caller of the function should ensure that logical replication is applicable\n * for the given shards, source and target nodes. Also, the caller is responsible\n * for ensuring that the input shard list consists of co-located distributed tables\n * or a single shard.\n */\nvoid\nLogicallyReplicateShards(List *shardList, char *sourceNodeName, int sourceNodePort,\n\t\t\t\t\t\t char *targetNodeName, int targetNodePort,\n\t\t\t\t\t\t bool skipInterShardRelationshipCreation)\n{\n\tchar *superUser = CitusExtensionOwnerName();\n\tchar *databaseName = get_database_name(MyDatabaseId);\n\tint connectionFlags = FORCE_NEW_CONNECTION;\n\tList *replicationSubscriptionList = PrepareReplicationSubscriptionList(shardList);\n\n\t/* no shards to move */\n\tif (list_length(replicationSubscriptionList) == 0)\n\t{\n\t\treturn;\n\t}\n\n\tMultiConnection *sourceConnection =\n\t\tGetNodeUserDatabaseConnection(connectionFlags, sourceNodeName, sourceNodePort,\n\t\t\t\t\t\t\t\t\t  superUser, databaseName);\n\n\t/*\n\t * Operations on publications and replication slots cannot run in a\n\t * transaction block. We claim the connections exclusively to ensure they\n\t * do not get used for metadata syncing, which does open a transaction\n\t * block.\n\t */\n\tClaimConnectionExclusively(sourceConnection);\n\n\tWorkerNode *sourceNode = FindWorkerNode(sourceNodeName, sourceNodePort);\n\tWorkerNode *targetNode = FindWorkerNode(targetNodeName, targetNodePort);\n\n\tHTAB *publicationInfoHash = CreateShardMovePublicationInfoHash(\n\t\ttargetNode, replicationSubscriptionList);\n\n\tList *logicalRepTargetList = CreateShardMoveLogicalRepTargetList(publicationInfoHash,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t shardList);\n\n\tHTAB *groupedLogicalRepTargetsHash = CreateGroupedLogicalRepTargetsHash(\n\t\tlogicalRepTargetList);\n\n\tCreateGroupedLogicalRepTargetsConnections(groupedLogicalRepTargetsHash, superUser,\n\t\t\t\t\t\t\t\t\t\t\t  databaseName);\n\n\tMultiConnection *sourceReplicationConnection =\n\t\tGetReplicationConnection(sourceConnection->hostname, sourceConnection->port);\n\n\t/* set up the publication on the source and subscription on the target */\n\tCreatePublications(sourceConnection, publicationInfoHash);\n\tchar *snapshot = CreateReplicationSlots(\n\t\tsourceConnection,\n\t\tsourceReplicationConnection,\n\t\tlogicalRepTargetList,\n\t\t\"pgoutput\");\n\n\tCreateSubscriptions(\n\t\tsourceConnection,\n\t\tsourceConnection->database,\n\t\tlogicalRepTargetList);\n\n\t/* only useful for isolation testing, see the function comment for the details */\n\tConflictWithIsolationTestingBeforeCopy();\n\n\t/*\n\t * We have to create the primary key (or any other replica identity)\n\t * before the update/delete operations that are queued will be\n\t * replicated. Because if the replica identity does not exist on the\n\t * target, the replication would fail.\n\t *\n\t * So the latest possible moment we could do this is right after the\n\t * initial data COPY, but before enabling the susbcriptions. It might\n\t * seem like a good idea to it after the initial data COPY, since\n\t * it's generally the rule that it's cheaper to build an index at once\n\t * than to create it incrementally. This general rule, is why we create\n\t * all the regular indexes as late during the move as possible.\n\t *\n\t * But as it turns out in practice it's not as clear cut, and we saw a\n\t * speed degradation in the time it takes to move shards when doing the\n\t * replica identity creation after the initial COPY. So, instead we\n\t * keep it before the COPY.\n\t */\n\tCreateReplicaIdentities(logicalRepTargetList);\n\n\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\tshardList,\n\t\tsourceNodeName,\n\t\tsourceNodePort,\n\t\tPLACEMENT_UPDATE_STATUS_COPYING_DATA);\n\n\tCopyShardsToNode(sourceNode, targetNode, shardList, snapshot);\n\n\t/*\n\t * We can close this connection now, because we're done copying the\n\t * data and thus don't need access to the snapshot anymore. The\n\t * replication slot will still be at the same LSN, because the\n\t * subscriptions have not been enabled yet.\n\t */\n\tCloseConnection(sourceReplicationConnection);\n\n\t/*\n\t * Start the replication and copy all data\n\t */\n\tCompleteNonBlockingShardTransfer(shardList,\n\t\t\t\t\t\t\t\t\t sourceConnection,\n\t\t\t\t\t\t\t\t\t publicationInfoHash,\n\t\t\t\t\t\t\t\t\t logicalRepTargetList,\n\t\t\t\t\t\t\t\t\t groupedLogicalRepTargetsHash,\n\t\t\t\t\t\t\t\t\t SHARD_MOVE,\n\t\t\t\t\t\t\t\t\t skipInterShardRelationshipCreation);\n\n\t/*\n\t * We use these connections exclusively for subscription management,\n\t * because otherwise subsequent metadata changes may inadvertedly use\n\t * these connections instead of the connections that were used to\n\t * grab locks in BlockWritesToShardList.\n\t */\n\tCloseGroupedLogicalRepTargetsConnections(groupedLogicalRepTargetsHash);\n\tCloseConnection(sourceConnection);\n}\n\n\n/*\n * CreateGroupedLogicalRepTargetsHash creates a hashmap that groups the subscriptions\n * logicalRepTargetList by node. This is useful for cases where we want to\n * iterate the subscriptions by node, so we can batch certain operations, such\n * as checking subscription readiness.\n */\nHTAB *\nCreateGroupedLogicalRepTargetsHash(List *logicalRepTargetList)\n{\n\tHTAB *logicalRepTargetsHash = CreateSimpleHash(uint32, GroupedLogicalRepTargets);\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tbool found = false;\n\t\tGroupedLogicalRepTargets *groupedLogicalRepTargets =\n\t\t\t(GroupedLogicalRepTargets *) hash_search(\n\t\t\t\tlogicalRepTargetsHash,\n\t\t\t\t&target->replicationSlot->targetNodeId,\n\t\t\t\tHASH_ENTER,\n\t\t\t\t&found);\n\t\tif (!found)\n\t\t{\n\t\t\tgroupedLogicalRepTargets->logicalRepTargetList = NIL;\n\t\t\tgroupedLogicalRepTargets->superuserConnection = NULL;\n\t\t}\n\t\tgroupedLogicalRepTargets->logicalRepTargetList =\n\t\t\tlappend(groupedLogicalRepTargets->logicalRepTargetList, target);\n\t}\n\treturn logicalRepTargetsHash;\n}\n\n\n/*\n * CompleteNonBlockingShardTransfer uses logical replication to apply the changes\n * made on the source to the target. It also runs all DDL on the target shards\n * that need to be run after the data copy.\n *\n * For shard splits it skips the partition hierarchy and foreign key creation\n * though, since those need to happen after the metadata is updated.\n */\nvoid\nCompleteNonBlockingShardTransfer(List *shardList,\n\t\t\t\t\t\t\t\t MultiConnection *sourceConnection,\n\t\t\t\t\t\t\t\t HTAB *publicationInfoHash,\n\t\t\t\t\t\t\t\t List *logicalRepTargetList,\n\t\t\t\t\t\t\t\t HTAB *groupedLogicalRepTargetsHash,\n\t\t\t\t\t\t\t\t LogicalRepType type,\n\t\t\t\t\t\t\t\t bool skipInterShardRelationshipCreation)\n{\n\t/* Start applying the changes from the replication slots to catch up. */\n\tEnableSubscriptions(logicalRepTargetList);\n\n\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\tshardList,\n\t\tsourceConnection->hostname,\n\t\tsourceConnection->port,\n\t\tPLACEMENT_UPDATE_STATUS_CATCHING_UP);\n\n\t/*\n\t * Wait until all the subscriptions are caught up to changes that\n\t * happened after the initial COPY on the shards.\n\t */\n\tWaitForAllSubscriptionsToCatchUp(sourceConnection, groupedLogicalRepTargetsHash);\n\n\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\tshardList,\n\t\tsourceConnection->hostname,\n\t\tsourceConnection->port,\n\t\tPLACEMENT_UPDATE_STATUS_CREATING_CONSTRAINTS);\n\n\t/*\n\t * Now lets create the post-load objects, such as the indexes, constraints\n\t * and partitioning hierarchy. Once they are done, wait until the replication\n\t * catches up again. So we don't block writes too long.\n\t */\n\tCreatePostLogicalReplicationDataLoadObjects(logicalRepTargetList, type,\n\t\t\t\t\t\t\t\t\t\t\t\tskipInterShardRelationshipCreation);\n\n\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\tshardList,\n\t\tsourceConnection->hostname,\n\t\tsourceConnection->port,\n\t\tPLACEMENT_UPDATE_STATUS_FINAL_CATCH_UP);\n\n\tWaitForAllSubscriptionsToCatchUp(sourceConnection, groupedLogicalRepTargetsHash);\n\n\n\t/* only useful for isolation testing, see the function comment for the details */\n\tConflictWithIsolationTestingAfterCopy();\n\n\t/*\n\t * We're almost done, we'll block the writes to the shards that we're\n\t * replicating and expect all the subscription to catch up quickly\n\t * afterwards.\n\t *\n\t * Notice that although shards in partitioned relation are excluded from\n\t * logical replication, they are still locked against modification, and\n\t * foreign constraints are created on them too.\n\t */\n\tBlockWritesToShardList(shardList);\n\n\tWaitForAllSubscriptionsToCatchUp(sourceConnection, groupedLogicalRepTargetsHash);\n\n\tif (type != SHARD_SPLIT && !skipInterShardRelationshipCreation)\n\t{\n\t\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\t\tshardList,\n\t\t\tsourceConnection->hostname,\n\t\t\tsourceConnection->port,\n\t\t\tPLACEMENT_UPDATE_STATUS_CREATING_FOREIGN_KEYS);\n\n\t\t/*\n\t\t * We're creating the foreign constraints to reference tables after the\n\t\t * data is already replicated and all the necessary locks are acquired.\n\t\t *\n\t\t * We prefer to do it here because the placements of reference tables\n\t\t * are always valid, and any modification during the shard move would\n\t\t * cascade to the hash distributed tables' shards if we had created\n\t\t * the constraints earlier. The same is true for foreign keys between\n\t\t * tables owned by different users.\n\t\t */\n\t\tCreateUncheckedForeignKeyConstraints(logicalRepTargetList);\n\t}\n\n\tUpdatePlacementUpdateStatusForShardIntervalList(\n\t\tshardList,\n\t\tsourceConnection->hostname,\n\t\tsourceConnection->port,\n\t\tPLACEMENT_UPDATE_STATUS_COMPLETING);\n}\n\n\n/*\n * CreateShardMovePublicationInfoHash creates hashmap of PublicationInfos for a\n * shard move. Even though we only support moving a shard to a single target\n * node, the resulting hashmap can have multiple PublicationInfos in it.\n * The reason for that is that we need a separate publication for each\n * distributed table owning user in the shard group.\n */\nstatic HTAB *\nCreateShardMovePublicationInfoHash(WorkerNode *targetNode, List *shardIntervals)\n{\n\tHTAB *publicationInfoHash = CreateSimpleHash(NodeAndOwner, PublicationInfo);\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervals)\n\t{\n\t\tNodeAndOwner key;\n\t\tkey.nodeId = targetNode->nodeId;\n\t\tkey.tableOwnerId = TableOwnerOid(shardInterval->relationId);\n\t\tbool found = false;\n\t\tPublicationInfo *publicationInfo =\n\t\t\t(PublicationInfo *) hash_search(publicationInfoHash, &key,\n\t\t\t\t\t\t\t\t\t\t\tHASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t&found);\n\t\tif (!found)\n\t\t{\n\t\t\tpublicationInfo->name = PublicationName(SHARD_MOVE, key.nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey.tableOwnerId);\n\t\t\tpublicationInfo->shardIntervals = NIL;\n\t\t}\n\t\tpublicationInfo->shardIntervals =\n\t\t\tlappend(publicationInfo->shardIntervals, shardInterval);\n\t}\n\treturn publicationInfoHash;\n}\n\n\n/*\n * CreateShardMoveLogicalRepTargetList creates the list containing all the\n * subscriptions that should be connected to the publications in the given\n * publicationHash.\n */\nstatic List *\nCreateShardMoveLogicalRepTargetList(HTAB *publicationInfoHash, List *shardList)\n{\n\tList *logicalRepTargetList = NIL;\n\n\tHASH_SEQ_STATUS status;\n\thash_seq_init(&status, publicationInfoHash);\n\tOid nodeId = InvalidOid;\n\n\tPublicationInfo *publication = NULL;\n\twhile ((publication = (PublicationInfo *) hash_seq_search(&status)) != NULL)\n\t{\n\t\tOid ownerId = publication->key.tableOwnerId;\n\t\tnodeId = publication->key.nodeId;\n\t\tLogicalRepTarget *target = palloc0(sizeof(LogicalRepTarget));\n\t\ttarget->subscriptionName = SubscriptionName(SHARD_MOVE, ownerId);\n\t\ttarget->tableOwnerId = ownerId;\n\t\ttarget->publication = publication;\n\t\tpublication->target = target;\n\t\ttarget->newShards = NIL;\n\t\ttarget->subscriptionOwnerName = SubscriptionRoleName(SHARD_MOVE, ownerId);\n\t\ttarget->replicationSlot = palloc0(sizeof(ReplicationSlotInfo));\n\t\ttarget->replicationSlot->name =\n\t\t\tReplicationSlotNameForNodeAndOwnerForOperation(SHARD_MOVE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ownerId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   CurrentOperationId);\n\t\ttarget->replicationSlot->targetNodeId = nodeId;\n\t\ttarget->replicationSlot->tableOwnerId = ownerId;\n\t\tlogicalRepTargetList = lappend(logicalRepTargetList, target);\n\t}\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardList)\n\t{\n\t\tNodeAndOwner key;\n\t\tkey.nodeId = nodeId;\n\t\tkey.tableOwnerId = TableOwnerOid(shardInterval->relationId);\n\n\t\tbool found = false;\n\t\tpublication = (PublicationInfo *) hash_search(\n\t\t\tpublicationInfoHash,\n\t\t\t&key,\n\t\t\tHASH_FIND,\n\t\t\t&found);\n\t\tif (!found)\n\t\t{\n\t\t\tereport(ERROR, errmsg(\"Could not find publication matching a split\"));\n\t\t}\n\t\tpublication->target->newShards = lappend(\n\t\t\tpublication->target->newShards, shardInterval);\n\t}\n\treturn logicalRepTargetList;\n}\n\n\n/*\n * PrepareReplicationSubscriptionList returns list of shards to be logically\n * replicated from given shard list. This is needed because Postgres does not\n * allow logical replication on partitioned tables, therefore shards belonging\n * to a partitioned tables should be exluded from logical replication\n * subscription list.\n */\nstatic List *\nPrepareReplicationSubscriptionList(List *shardList)\n{\n\tList *replicationSubscriptionList = NIL;\n\tListCell *shardCell = NULL;\n\n\tforeach(shardCell, shardList)\n\t{\n\t\tShardInterval *shardInterval = (ShardInterval *) lfirst(shardCell);\n\t\tif (!PartitionedTable(shardInterval->relationId))\n\t\t{\n\t\t\t/* only add regular and child tables to subscription */\n\t\t\treplicationSubscriptionList = lappend(replicationSubscriptionList,\n\t\t\t\t\t\t\t\t\t\t\t\t  shardInterval);\n\t\t}\n\t}\n\n\treturn replicationSubscriptionList;\n}\n\n\n/*\n * CreateReplicaIdentities creates replica identities for all the shards that\n * are part of the given subscriptions.\n */\nvoid\nCreateReplicaIdentities(List *logicalRepTargetList)\n{\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tMultiConnection *superuserConnection = target->superuserConnection;\n\t\tCreateReplicaIdentitiesOnNode(\n\t\t\ttarget->newShards,\n\t\t\tsuperuserConnection->hostname,\n\t\t\tsuperuserConnection->port);\n\t}\n}\n\n\n/*\n * CreateReplicaIdentitiesOnNode gets a shardList and creates all the replica\n * identities on the shards in the given node.\n */\nvoid\nCreateReplicaIdentitiesOnNode(List *shardList, char *nodeName, int32 nodePort)\n{\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"CreateReplicaIdentitiesOnNode\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\tShardInterval *shardInterval;\n\tforeach_declared_ptr(shardInterval, shardList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\t\tOid relationId = shardInterval->relationId;\n\n\t\tList *backingIndexCommandList =\n\t\t\tGetIndexCommandListForShardBackingReplicaIdentity(relationId, shardId);\n\n\t\tList *replicaIdentityShardCommandList =\n\t\t\tGetReplicaIdentityCommandListForShard(relationId, shardId);\n\n\t\tList *commandList =\n\t\t\tlist_concat(backingIndexCommandList, replicaIdentityShardCommandList);\n\n\t\tif (commandList != NIL)\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"Creating replica identity for shard %ld on \"\n\t\t\t\t\t\t\t\t\t\"target node %s:%d\", shardId, nodeName, nodePort)));\n\n\t\t\tSendCommandListToWorkerOutsideTransaction(nodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  TableOwner(relationId),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  commandList);\n\t\t}\n\n\t\tMemoryContextReset(localContext);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * GetIndexCommandListForShardBackingReplicaIdentity returns all the create index\n * commands that are needed to create replica identity. If the table doesn't have\n * a replica identity, the function returns NIL.\n */\nstatic List *\nGetIndexCommandListForShardBackingReplicaIdentity(Oid relationId, uint64 shardId)\n{\n\tList *commandList = NIL;\n\tRelation relation = table_open(relationId, AccessShareLock);\n\tOid replicaIdentityIndex = GetRelationIdentityOrPK(relation);\n\ttable_close(relation, NoLock);\n\n\tif (OidIsValid(replicaIdentityIndex))\n\t{\n\t\t/*\n\t\t * The replica identity is backed by an index or primary key,\n\t\t * so get the index/pkey definition first.\n\t\t */\n\t\tHeapTuple indexTuple =\n\t\t\tSearchSysCache1(INDEXRELID, ObjectIdGetDatum(replicaIdentityIndex));\n\t\tif (!HeapTupleIsValid(indexTuple))\n\t\t{\n\t\t\t/* should not happen */\n\t\t\telog(ERROR, \"cache lookup failed for index %u\", replicaIdentityIndex);\n\t\t}\n\n\t\tForm_pg_index indexForm = ((Form_pg_index) GETSTRUCT(indexTuple));\n\t\tList *indexCommandTableDDLList = NIL;\n\t\tint indexFlags = INCLUDE_INDEX_ALL_STATEMENTS;\n\t\tGatherIndexAndConstraintDefinitionList(indexForm, &indexCommandTableDDLList,\n\t\t\t\t\t\t\t\t\t\t\t   indexFlags);\n\n\t\tList *indexCommandShardDDLList =\n\t\t\tWorkerApplyShardDDLCommandList(indexCommandTableDDLList, shardId);\n\n\t\tcommandList = list_concat(commandList, indexCommandShardDDLList);\n\n\t\tReleaseSysCache(indexTuple);\n\t}\n\n\treturn commandList;\n}\n\n\n/*\n * GetReplicaIdentityCommandListForShard returns the create replica identity\n * command that are needed to create replica identity. If the table doesn't have\n * a replica identity, the function returns NIL.\n */\nstatic List *\nGetReplicaIdentityCommandListForShard(Oid relationId, uint64 shardId)\n{\n\tList *replicaIdentityTableDDLCommand =\n\t\tGetTableReplicaIdentityCommand(relationId);\n\tList *replicaIdentityShardCommandList =\n\t\tWorkerApplyShardDDLCommandList(replicaIdentityTableDDLCommand, shardId);\n\n\treturn replicaIdentityShardCommandList;\n}\n\n\n/*\n * CreatePostLogicalReplicationDataLoadObjects gets a shardList and creates all\n * the objects that can be created after the data is moved with logical replication.\n */\nstatic void\nCreatePostLogicalReplicationDataLoadObjects(List *logicalRepTargetList,\n\t\t\t\t\t\t\t\t\t\t\tLogicalRepType type,\n\t\t\t\t\t\t\t\t\t\t\tbool skipInterShardRelationships)\n{\n\t/*\n\t * We create indexes in 4 steps.\n\t *  - CREATE INDEX statements\n\t *  - CREATE CONSTRAINT statements that are backed by\n\t *    indexes (unique and exclude constraints)\n\t *  - ALTER TABLE %s CLUSTER ON %s\n\t *  - ALTER INDEX %s ALTER COLUMN %d SET STATISTICS %d\n\t *\n\t *  On each step, we execute can execute commands in parallel. For example,\n\t *  multiple indexes on the shard table or indexes for the colocated shards\n\t *  can be created in parallel. However, the latter two steps, clustering the\n\t *  table and setting the statistics of indexes, depends on the indexes being\n\t *  created. That's why the execution is divided into four distinct stages.\n\t */\n\tExecuteCreateIndexCommands(logicalRepTargetList);\n\tExecuteCreateConstraintsBackedByIndexCommands(logicalRepTargetList);\n\tExecuteClusterOnCommands(logicalRepTargetList);\n\tExecuteCreateIndexStatisticsCommands(logicalRepTargetList);\n\n\t/*\n\t * Once the indexes are created, there are few more objects like triggers and table\n\t * statistics that should be created after the data move.\n\t */\n\tExecuteRemainingPostLoadTableCommands(logicalRepTargetList);\n\n\t/*\n\t * Creating the partitioning hierarchy errors out in shard splits when\n\t */\n\tif (type != SHARD_SPLIT && !skipInterShardRelationships)\n\t{\n\t\t/* create partitioning hierarchy, if any */\n\t\tCreatePartitioningHierarchy(logicalRepTargetList);\n\t}\n}\n\n\n/*\n * ExecuteCreateIndexCommands gets a shardList and creates all the indexes\n * for the given shardList in the given target node.\n *\n * The execution is done in parallel, and throws an error if any of the\n * commands fail.\n */\nstatic void\nExecuteCreateIndexCommands(List *logicalRepTargetList)\n{\n\tList *taskList = NIL;\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tforeach_declared_ptr(shardInterval, target->newShards)\n\t\t{\n\t\t\tOid relationId = shardInterval->relationId;\n\n\t\t\tList *tableCreateIndexCommandList =\n\t\t\t\tGetTableIndexAndConstraintCommandsExcludingReplicaIdentity(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   INCLUDE_CREATE_INDEX_STATEMENTS);\n\n\t\t\tList *shardCreateIndexCommandList =\n\t\t\t\tWorkerApplyShardDDLCommandList(tableCreateIndexCommandList,\n\t\t\t\t\t\t\t\t\t\t\t   shardInterval->shardId);\n\t\t\tList *taskListForShard =\n\t\t\t\tConvertNonExistingPlacementDDLCommandsToTasks(\n\t\t\t\t\tshardCreateIndexCommandList,\n\t\t\t\t\ttarget->superuserConnection->hostname,\n\t\t\t\t\ttarget->superuserConnection->port);\n\t\t\ttaskList = list_concat(taskList, taskListForShard);\n\t\t}\n\t}\n\n\t/*\n\t * We are going to create indexes and constraints using the current user. That is\n\t * alright because an index/constraint always belongs to the owner of the table,\n\t * and Citus already ensures that the current user owns all the tables that are\n\t * moved.\n\t *\n\t * CREATE INDEX commands acquire ShareLock on a relation. So, it is\n\t * allowed to run multiple CREATE INDEX commands concurrently on a table\n\t * and across different tables (e.g., shards).\n\t */\n\n\tereport(DEBUG1, (errmsg(\"Creating post logical replication objects \"\n\t\t\t\t\t\t\t\"(indexes)\")));\n\n\tExecuteTaskListOutsideTransaction(ROW_MODIFY_NONE, taskList,\n\t\t\t\t\t\t\t\t\t  MaxAdaptiveExecutorPoolSize,\n\t\t\t\t\t\t\t\t\t  NIL);\n}\n\n\n/*\n * ExecuteCreateConstraintsBackedByIndexCommands gets a shardList and creates all the constraints\n * that are backed by indexes for the given shardList in the given target node.\n *\n * The execution is done in sequential mode, and throws an error if any of the\n * commands fail.\n */\nstatic void\nExecuteCreateConstraintsBackedByIndexCommands(List *logicalRepTargetList)\n{\n\tereport(DEBUG1, (errmsg(\"Creating post logical replication objects \"\n\t\t\t\t\t\t\t\"(constraints backed by indexes)\")));\n\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"CreateConstraintsBackedByIndexContext\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tforeach_declared_ptr(shardInterval, target->newShards)\n\t\t{\n\t\t\tOid relationId = shardInterval->relationId;\n\n\t\t\tList *tableCreateConstraintCommandList =\n\t\t\t\tGetTableIndexAndConstraintCommandsExcludingReplicaIdentity(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   INCLUDE_CREATE_CONSTRAINT_STATEMENTS);\n\n\t\t\tif (tableCreateConstraintCommandList == NIL)\n\t\t\t{\n\t\t\t\t/* no constraints backed by indexes, skip */\n\t\t\t\tMemoryContextReset(localContext);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tList *shardCreateConstraintCommandList =\n\t\t\t\tWorkerApplyShardDDLCommandList(tableCreateConstraintCommandList,\n\t\t\t\t\t\t\t\t\t\t\t   shardInterval->shardId);\n\n\t\t\tchar *tableOwner = TableOwner(shardInterval->relationId);\n\t\t\tSendCommandListToWorkerOutsideTransaction(\n\t\t\t\ttarget->superuserConnection->hostname,\n\t\t\t\ttarget->superuserConnection->port,\n\t\t\t\ttableOwner,\n\t\t\t\tshardCreateConstraintCommandList);\n\t\t\tMemoryContextReset(localContext);\n\t\t}\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * ConvertNonExistingShardDDLCommandsToTasks generates one task per input\n * element in shardCommandList.\n *\n * The generated tasks' placements do not exist (yet). We are generating\n * fake placements for the tasks.\n */\nstatic List *\nConvertNonExistingPlacementDDLCommandsToTasks(List *shardCommandList,\n\t\t\t\t\t\t\t\t\t\t\t  char *targetNodeName,\n\t\t\t\t\t\t\t\t\t\t\t  int targetNodePort)\n{\n\tWorkerNode *workerNode = FindWorkerNodeOrError(targetNodeName, targetNodePort);\n\n\tList *taskList = NIL;\n\tuint64 jobId = INVALID_JOB_ID;\n\n\tListCell *commandCell = NULL;\n\tint taskId = 1;\n\tforeach(commandCell, shardCommandList)\n\t{\n\t\tchar *command = (char *) lfirst(commandCell);\n\t\tTask *task = CreateBasicTask(jobId, taskId, DDL_TASK, command);\n\n\t\t/* this placement currently does not exist */\n\t\tShardPlacement *taskPlacement = CitusMakeNode(ShardPlacement);\n\t\tSetPlacementNodeMetadata(taskPlacement, workerNode);\n\n\t\ttask->taskPlacementList = list_make1(taskPlacement);\n\n\t\ttaskList = lappend(taskList, task);\n\t\ttaskId++;\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * ExecuteClusterOnCommands gets a shardList and creates all the CLUSTER ON commands\n * for the given shardList in the given target node.\n *\n * The execution is done in parallel, and in case of any failure, the transaction\n * is aborted.\n */\nstatic void\nExecuteClusterOnCommands(List *logicalRepTargetList)\n{\n\tList *taskList = NIL;\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tforeach_declared_ptr(shardInterval, target->newShards)\n\t\t{\n\t\t\tOid relationId = shardInterval->relationId;\n\n\t\t\tList *tableAlterTableClusterOnCommandList =\n\t\t\t\tGetTableIndexAndConstraintCommandsExcludingReplicaIdentity(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   INCLUDE_INDEX_CLUSTERED_STATEMENTS);\n\n\t\t\tList *shardAlterTableClusterOnCommandList =\n\t\t\t\tWorkerApplyShardDDLCommandList(tableAlterTableClusterOnCommandList,\n\t\t\t\t\t\t\t\t\t\t\t   shardInterval->shardId);\n\n\t\t\tList *taskListForShard =\n\t\t\t\tConvertNonExistingPlacementDDLCommandsToTasks(\n\t\t\t\t\tshardAlterTableClusterOnCommandList,\n\t\t\t\t\ttarget->superuserConnection->hostname,\n\t\t\t\t\ttarget->superuserConnection->port);\n\t\t\ttaskList = list_concat(taskList, taskListForShard);\n\t\t}\n\t}\n\n\tereport(DEBUG1, (errmsg(\"Creating post logical replication objects \"\n\t\t\t\t\t\t\t\"(CLUSTER ON)\")));\n\n\tExecuteTaskListOutsideTransaction(ROW_MODIFY_NONE, taskList,\n\t\t\t\t\t\t\t\t\t  MaxAdaptiveExecutorPoolSize,\n\t\t\t\t\t\t\t\t\t  NIL);\n}\n\n\n/*\n * ExecuteCreateIndexStatisticsCommands gets a shardList and creates\n * all the statistics objects for the indexes in the given target node.\n *\n * The execution is done in sequentially, and in case of any failure, the transaction\n * is aborted.\n */\nstatic void\nExecuteCreateIndexStatisticsCommands(List *logicalRepTargetList)\n{\n\tereport(DEBUG1, (errmsg(\"Creating post logical replication objects \"\n\t\t\t\t\t\t\t\"(index statistics)\")));\n\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"CreateIndexStatisticsContext\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tforeach_declared_ptr(shardInterval, target->newShards)\n\t\t{\n\t\t\tOid relationId = shardInterval->relationId;\n\n\t\t\tList *tableAlterIndexSetStatisticsCommandList =\n\t\t\t\tGetTableIndexAndConstraintCommandsExcludingReplicaIdentity(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   INCLUDE_INDEX_STATISTICS_STATEMENTTS);\n\t\t\tList *shardAlterIndexSetStatisticsCommandList =\n\t\t\t\tWorkerApplyShardDDLCommandList(tableAlterIndexSetStatisticsCommandList,\n\t\t\t\t\t\t\t\t\t\t\t   shardInterval->shardId);\n\n\t\t\tif (shardAlterIndexSetStatisticsCommandList == NIL)\n\t\t\t{\n\t\t\t\t/* no index statistics exists, skip */\n\t\t\t\tMemoryContextReset(localContext);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * These remaining operations do not require significant resources, so no\n\t\t\t * need to create them in parallel.\n\t\t\t */\n\t\t\tchar *tableOwner = TableOwner(shardInterval->relationId);\n\t\t\tSendCommandListToWorkerOutsideTransaction(\n\t\t\t\ttarget->superuserConnection->hostname,\n\t\t\t\ttarget->superuserConnection->port,\n\t\t\t\ttableOwner,\n\t\t\t\tshardAlterIndexSetStatisticsCommandList);\n\n\t\t\tMemoryContextReset(localContext);\n\t\t}\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * ExecuteRemainingPostLoadTableCommands gets a shardList and creates\n * all the remaining post load objects other than the indexes\n * in the given target node.\n */\nstatic void\nExecuteRemainingPostLoadTableCommands(List *logicalRepTargetList)\n{\n\tereport(DEBUG1, (errmsg(\"Creating post logical replication objects \"\n\t\t\t\t\t\t\t\"(triggers and table statistics)\"\n\t\t\t\t\t\t\t)));\n\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"CreateTableStatisticsContext\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tforeach_declared_ptr(shardInterval, target->newShards)\n\t\t{\n\t\t\tOid relationId = shardInterval->relationId;\n\n\t\t\tbool includeIndexes = false;\n\t\t\tbool includeReplicaIdentity = false;\n\n\t\t\tList *tablePostLoadTableCommandList =\n\t\t\t\tGetPostLoadTableCreationCommands(relationId, includeIndexes,\n\t\t\t\t\t\t\t\t\t\t\t\t includeReplicaIdentity);\n\n\t\t\tList *shardPostLoadTableCommandList =\n\t\t\t\tWorkerApplyShardDDLCommandList(tablePostLoadTableCommandList,\n\t\t\t\t\t\t\t\t\t\t\t   shardInterval->shardId);\n\n\t\t\tif (shardPostLoadTableCommandList == NIL)\n\t\t\t{\n\t\t\t\t/* no index statistics exists, skip */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * These remaining operations do not require significant resources, so no\n\t\t\t * need to create them in parallel.\n\t\t\t */\n\t\t\tchar *tableOwner = TableOwner(shardInterval->relationId);\n\t\t\tSendCommandListToWorkerOutsideTransaction(\n\t\t\t\ttarget->superuserConnection->hostname,\n\t\t\t\ttarget->superuserConnection->port,\n\t\t\t\ttableOwner,\n\t\t\t\tshardPostLoadTableCommandList);\n\n\t\t\tMemoryContextReset(localContext);\n\t\t}\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * CreatePartitioningHierarchy gets a shardList and creates the partitioning\n * hierarchy between the shardList, if any,\n */\nvoid\nCreatePartitioningHierarchy(List *logicalRepTargetList)\n{\n\tereport(DEBUG1, (errmsg(\"Creating post logical replication objects \"\n\t\t\t\t\t\t\t\"(partitioning hierarchy)\")));\n\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"CreatePartitioningHierarchy\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tforeach_declared_ptr(shardInterval, target->newShards)\n\t\t{\n\t\t\tif (PartitionTable(shardInterval->relationId))\n\t\t\t{\n\t\t\t\tchar *attachPartitionCommand =\n\t\t\t\t\tGenerateAttachShardPartitionCommand(shardInterval);\n\n\t\t\t\tchar *tableOwner = TableOwner(shardInterval->relationId);\n\n\t\t\t\t/*\n\t\t\t\t * Attaching partition may acquire conflicting locks when created in\n\t\t\t\t * parallel, so create them sequentially. Also attaching partition\n\t\t\t\t * is a quick operation, so it is fine to execute sequentially.\n\t\t\t\t */\n\n\t\t\t\tMultiConnection *connection =\n\t\t\t\t\tGetNodeUserDatabaseConnection(OUTSIDE_TRANSACTION,\n\t\t\t\t\t\t\t\t\t\t\t\t  target->superuserConnection->hostname,\n\t\t\t\t\t\t\t\t\t\t\t\t  target->superuserConnection->port,\n\t\t\t\t\t\t\t\t\t\t\t\t  tableOwner, NULL);\n\t\t\t\tExecuteCriticalRemoteCommand(connection, attachPartitionCommand);\n\n\t\t\t\tMemoryContextReset(localContext);\n\t\t\t}\n\t\t}\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * CreateUncheckedForeignKeyConstraints is used to create the foreign\n * constraints on the logical replication target without checking that they are\n * actually valid.\n *\n * We skip the validation phase of foreign keys to after a shard\n * move/copy/split because the validation is pretty costly and given that the\n * source placements are already valid, the validation in the target nodes is\n * useless.\n */\nvoid\nCreateUncheckedForeignKeyConstraints(List *logicalRepTargetList)\n{\n\tMemoryContext localContext =\n\t\tAllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t  \"CreateKeyForeignConstraints\",\n\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\n\t/*\n\t * Iterate over all the shards in the shard group.\n\t */\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\n\t\t/*\n\t\t * Iterate on split shards list for a given shard and create constraints.\n\t\t */\n\t\tforeach_declared_ptr(shardInterval, target->newShards)\n\t\t{\n\t\t\tList *commandList = CopyShardForeignConstraintCommandList(\n\t\t\t\tshardInterval);\n\t\t\tcommandList = list_concat(\n\t\t\t\tlist_make1(\"SET LOCAL citus.skip_constraint_validation TO ON;\"),\n\t\t\t\tcommandList);\n\n\t\t\tSendCommandListToWorkerOutsideTransactionWithConnection(\n\t\t\t\ttarget->superuserConnection,\n\t\t\t\tcommandList);\n\n\t\t\tMemoryContextReset(localContext);\n\t\t}\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * ConflictWithIsolationTestingBeforeCopy is only useful to test\n * get_rebalance_progress by pausing before doing the actual copy. This way we\n * can see the state of the tables at that point. This should not be called by\n * any code-path except for code paths to move and split shards().\n *\n * Note that since the cost of calling this function is pretty low, we prefer\n * to use it in non-assert builds as well not to diverge in the behaviour.\n */\nextern void\nConflictWithIsolationTestingBeforeCopy(void)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = false;\n\n\tif (RunningUnderCitusTestSuite)\n\t{\n\t\tSET_LOCKTAG_ADVISORY(tag, MyDatabaseId,\n\t\t\t\t\t\t\t SHARD_MOVE_ADVISORY_LOCK_SECOND_KEY,\n\t\t\t\t\t\t\t SHARD_MOVE_ADVISORY_LOCK_FIRST_KEY, 2);\n\n\t\t/* uses sharelock so concurrent moves don't conflict with eachother */\n\t\t(void) LockAcquire(&tag, ShareLock, sessionLock, dontWait);\n\t}\n}\n\n\n/*\n * ConflictWithIsolationTestingAfterCopy is only useful for two types of tests.\n * 1. Testing the output of get_rebalance_progress after the copy is completed,\n *    but before the move is completely finished. Because finishing the move\n *    will clear the contents of get_rebalance_progress.\n * 2. To test that our non-blocking shard moves/splits actually don't block\n *    writes. Since logically replicating shards does eventually block\n *    modifications, it becomes tricky to use isolation tester to show\n *    concurrent behaviour of online shard rebalancing and modification\n *    queries. So, during logical replication we call this function at\n *    the end of the catchup, right before blocking writes.\n *\n * Note that since the cost of calling this function is pretty low, we prefer\n * to use it in non-assert builds as well not to diverge in the behaviour.\n */\nextern void\nConflictWithIsolationTestingAfterCopy(void)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = false;\n\n\tif (RunningUnderCitusTestSuite)\n\t{\n\t\tSET_LOCKTAG_ADVISORY(tag, MyDatabaseId,\n\t\t\t\t\t\t\t SHARD_MOVE_ADVISORY_LOCK_FIRST_KEY,\n\t\t\t\t\t\t\t SHARD_MOVE_ADVISORY_LOCK_SECOND_KEY, 2);\n\n\t\t/* uses sharelock so concurrent moves don't conflict with eachother */\n\t\t(void) LockAcquire(&tag, ShareLock, sessionLock, dontWait);\n\t}\n}\n\n\n/*\n * PublicationName returns the name of the publication for the given node and\n * table owner.\n */\nchar *\nPublicationName(LogicalRepType type, uint32_t nodeId, Oid ownerId)\n{\n\treturn psprintf(\"%s%u_%u_%lu\", publicationPrefix[type],\n\t\t\t\t\tnodeId, ownerId, CurrentOperationId);\n}\n\n\n/*\n * ReplicationSlotNameForNodeAndOwnerForOperation returns the name of the\n * replication slot for the given node, table owner and operation id.\n */\nchar *\nReplicationSlotNameForNodeAndOwnerForOperation(LogicalRepType type, uint32_t nodeId,\n\t\t\t\t\t\t\t\t\t\t\t   Oid ownerId, OperationId operationId)\n{\n\tStringInfo slotName = makeStringInfo();\n\tappendStringInfo(slotName, \"%s%u_%u_%lu\", replicationSlotPrefix[type], nodeId,\n\t\t\t\t\t ownerId, operationId);\n\n\tif (slotName->len > NAMEDATALEN)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\n\t\t\t\t\t \"Replication Slot name:%s having length:%d is greater than maximum allowed length:%d\",\n\t\t\t\t\t slotName->data, slotName->len, NAMEDATALEN)));\n\t}\n\treturn slotName->data;\n}\n\n\n/*\n * SubscriptionName returns the name of the subscription for the given owner.\n */\nchar *\nSubscriptionName(LogicalRepType type, Oid ownerId)\n{\n\treturn psprintf(\"%s%u_%lu\", subscriptionPrefix[type],\n\t\t\t\t\townerId, CurrentOperationId);\n}\n\n\n/*\n * SubscriptionRoleName returns the name of the role used by the\n * subscription that subscribes to the tables of the given owner.\n */\nchar *\nSubscriptionRoleName(LogicalRepType type, Oid ownerId)\n{\n\treturn psprintf(\"%s%u_%lu\", subscriptionRolePrefix[type], ownerId,\n\t\t\t\t\tCurrentOperationId);\n}\n\n\n/*\n * GetQueryResultStringList expects a query that returns a single column of\n * strings. This query is executed on the connection and the function then\n * returns the results of the query in a List.\n */\nList *\nGetQueryResultStringList(MultiConnection *connection, char *query)\n{\n\tbool raiseInterrupts = true;\n\n\tint querySent = SendRemoteCommand(connection, query);\n\tif (querySent == 0)\n\t{\n\t\tReportConnectionError(connection, ERROR);\n\t}\n\n\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\tif (!IsResponseOK(result))\n\t{\n\t\tReportResultError(connection, result, ERROR);\n\t}\n\n\tint rowCount = PQntuples(result);\n\tint columnCount = PQnfields(result);\n\n\tif (columnCount != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"unexpected number of columns returned while reading \")));\n\t}\n\n\tList *resultList = NIL;\n\tfor (int rowIndex = 0; rowIndex < rowCount; rowIndex++)\n\t{\n\t\tint columnIndex = 0;\n\t\tStringInfo resultStringInfo = makeStringInfo();\n\n\t\tchar *resultString = PQgetvalue(result, rowIndex, columnIndex);\n\n\t\t/* we're using the stringinfo to copy the data into the current memory context */\n\t\tappendStringInfoString(resultStringInfo, resultString);\n\n\t\tresultList = lappend(resultList, resultStringInfo->data);\n\t}\n\n\tPQclear(result);\n\tForgetResults(connection);\n\treturn resultList;\n}\n\n\n/*\n * CreatePublications creates a the publications defined in the\n * publicationInfoHash over the given connection.\n */\nvoid\nCreatePublications(MultiConnection *connection,\n\t\t\t\t   HTAB *publicationInfoHash)\n{\n\tHASH_SEQ_STATUS status;\n\thash_seq_init(&status, publicationInfoHash);\n\tPublicationInfo *entry = NULL;\n\twhile ((entry = (PublicationInfo *) hash_seq_search(&status)) != NULL)\n\t{\n\t\tStringInfo createPublicationCommand = makeStringInfo();\n\t\tbool prefixWithComma = false;\n\n\t\tappendStringInfo(createPublicationCommand, \"CREATE PUBLICATION %s FOR TABLE \",\n\t\t\t\t\t\t quote_identifier(entry->name));\n\n\t\tShardInterval *shard = NULL;\n\t\tforeach_declared_ptr(shard, entry->shardIntervals)\n\t\t{\n\t\t\tchar *shardName = ConstructQualifiedShardName(shard);\n\n\t\t\tif (prefixWithComma)\n\t\t\t{\n\t\t\t\tappendStringInfoString(createPublicationCommand, \",\");\n\t\t\t}\n\n\t\t\tappendStringInfoString(createPublicationCommand, shardName);\n\t\t\tprefixWithComma = true;\n\t\t}\n\n\t\tWorkerNode *worker = FindWorkerNode(connection->hostname,\n\t\t\t\t\t\t\t\t\t\t\tconnection->port);\n\t\tInsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_PUBLICATION,\n\t\t\t\t\t\t\t\t\t\t\t  entry->name,\n\t\t\t\t\t\t\t\t\t\t\t  worker->groupId,\n\t\t\t\t\t\t\t\t\t\t\t  CLEANUP_ALWAYS);\n\n\t\tExecuteCriticalRemoteCommand(connection, DISABLE_DDL_PROPAGATION);\n\t\tExecuteCriticalRemoteCommand(connection, createPublicationCommand->data);\n\t\tExecuteCriticalRemoteCommand(connection, ENABLE_DDL_PROPAGATION);\n\t\tpfree(createPublicationCommand->data);\n\t\tpfree(createPublicationCommand);\n\t}\n}\n\n\n/*\n * GetReplicationConnection opens a new replication connection to this node.\n * This connection can be used to send replication commands, such as\n * CREATE_REPLICATION_SLOT.\n */\nMultiConnection *\nGetReplicationConnection(char *nodeName, int nodePort)\n{\n\tint connectionFlags = FORCE_NEW_CONNECTION;\n\tconnectionFlags |= REQUIRE_REPLICATION_CONNECTION_PARAM;\n\n\tMultiConnection *connection = GetNodeUserDatabaseConnection(\n\t\tconnectionFlags,\n\t\tnodeName,\n\t\tnodePort,\n\t\tCitusExtensionOwnerName(),\n\t\tget_database_name(MyDatabaseId));\n\n\t/*\n\t * Replication connections are special and don't support all of SQL, so we\n\t * don't want it to be used for other purposes what we create it for.\n\t */\n\tClaimConnectionExclusively(connection);\n\treturn connection;\n}\n\n\n/*\n * CreateReplicationSlot creates a replication slot with the given slot name\n * over the given connection. The given connection should be a replication\n * connection. This function returns the name of the snapshot that is used for\n * this replication slot. When using this snapshot name for other transactions\n * you need to keep the given replication connection open until you have used\n * the snapshot name.\n */\nstatic char *\nCreateReplicationSlot(MultiConnection *connection, char *slotname, char *outputPlugin)\n{\n\tStringInfo createReplicationSlotCommand = makeStringInfo();\n\tappendStringInfo(createReplicationSlotCommand,\n\t\t\t\t\t \"CREATE_REPLICATION_SLOT %s LOGICAL %s EXPORT_SNAPSHOT;\",\n\t\t\t\t\t quote_identifier(slotname), quote_identifier(outputPlugin));\n\n\tPGresult *result = NULL;\n\tint response = ExecuteOptionalRemoteCommand(connection,\n\t\t\t\t\t\t\t\t\t\t\t\tcreateReplicationSlotCommand->data,\n\t\t\t\t\t\t\t\t\t\t\t\t&result);\n\n\tif (response != RESPONSE_OKAY || !IsResponseOK(result) || PQntuples(result) != 1)\n\t{\n\t\tReportResultError(connection, result, ERROR);\n\t}\n\n\t/*'snapshot_name' is second column where index starts from zero.\n\t * We're using the pstrdup to copy the data into the current memory context */\n\tchar *snapShotName = pstrdup(PQgetvalue(result, 0, 2 /* columIndex */));\n\tPQclear(result);\n\tForgetResults(connection);\n\treturn snapShotName;\n}\n\n\n/*\n * CreateReplicationSlots creates the replication slots that the subscriptions\n * in the logicalRepTargetList can use.\n *\n * This function returns the snapshot name of the replication slots that are\n * used by the subscription. When using this snapshot name for other\n * transactions you need to keep the given replication connection open until\n * you are finished using the snapshot.\n */\nchar *\nCreateReplicationSlots(MultiConnection *sourceConnection,\n\t\t\t\t\t   MultiConnection *sourceReplicationConnection,\n\t\t\t\t\t   List *logicalRepTargetList,\n\t\t\t\t\t   char *outputPlugin)\n{\n\tReplicationSlotInfo *firstReplicationSlot = NULL;\n\tchar *snapshot = NULL;\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tReplicationSlotInfo *replicationSlot = target->replicationSlot;\n\n\t\tWorkerNode *worker = FindWorkerNode(sourceConnection->hostname,\n\t\t\t\t\t\t\t\t\t\t\tsourceConnection->port);\n\t\tInsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_REPLICATION_SLOT,\n\t\t\t\t\t\t\t\t\t\t\t  replicationSlot->name,\n\t\t\t\t\t\t\t\t\t\t\t  worker->groupId,\n\t\t\t\t\t\t\t\t\t\t\t  CLEANUP_ALWAYS);\n\n\t\tif (!firstReplicationSlot)\n\t\t{\n\t\t\tfirstReplicationSlot = replicationSlot;\n\t\t\tsnapshot = CreateReplicationSlot(\n\t\t\t\tsourceReplicationConnection,\n\t\t\t\treplicationSlot->name,\n\t\t\t\toutputPlugin\n\t\t\t\t);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tExecuteCriticalRemoteCommand(\n\t\t\t\tsourceConnection,\n\t\t\t\tpsprintf(\"SELECT pg_catalog.pg_copy_logical_replication_slot(%s, %s)\",\n\t\t\t\t\t\t quote_literal_cstr(firstReplicationSlot->name),\n\t\t\t\t\t\t quote_literal_cstr(replicationSlot->name)));\n\t\t}\n\t}\n\treturn snapshot;\n}\n\n\n/*\n * CreateSubscriptions creates the subscriptions according to their definition\n * in the logicalRepTargetList. The remote node(s) needs to have appropriate\n * pg_dist_authinfo rows for the superuser such that the apply process can\n * connect. Because the generated CREATE SUBSCRIPTION statements use the host\n * and port names directly (rather than looking up any relevant\n * pg_dist_poolinfo rows), all such connections remain direct and will not\n * route through any configured poolers.\n *\n * The subscriptions created by this function are created in the disabled\n * state. This is done so a data copy can be done manually afterwards. To\n * enable the subscriptions you can use EnableSubscriptions().\n */\nvoid\nCreateSubscriptions(MultiConnection *sourceConnection,\n\t\t\t\t\tchar *databaseName,\n\t\t\t\t\tList *logicalRepTargetList)\n{\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tint ownerId = target->tableOwnerId;\n\n\t\tWorkerNode *worker = FindWorkerNode(target->superuserConnection->hostname,\n\t\t\t\t\t\t\t\t\t\t\ttarget->superuserConnection->port);\n\n\t\t/*\n\t\t * The CREATE USER command should not propagate, so we temporarily\n\t\t * disable DDL propagation.\n\t\t *\n\t\t * Subscription workers have SUPERUSER permissions. Hence we temporarily\n\t\t * create a user with SUPERUSER permissions and then alter it to NOSUPERUSER.\n\t\t * This prevents permission escalations.\n\t\t */\n\t\tSendCommandListToWorkerOutsideTransactionWithConnection(\n\t\t\ttarget->superuserConnection,\n\t\t\tlist_make2(\n\t\t\t\t\"SET LOCAL citus.enable_ddl_propagation TO OFF;\",\n\t\t\t\tpsprintf(\n\t\t\t\t\t\"CREATE USER %s SUPERUSER IN ROLE %s;\",\n\t\t\t\t\tquote_identifier(target->subscriptionOwnerName),\n\t\t\t\t\tquote_identifier(GetUserNameFromId(ownerId, false))\n\t\t\t\t\t)));\n\n\t\tInsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_USER,\n\t\t\t\t\t\t\t\t\t\t\t  target->subscriptionOwnerName,\n\t\t\t\t\t\t\t\t\t\t\t  worker->groupId,\n\t\t\t\t\t\t\t\t\t\t\t  CLEANUP_ALWAYS);\n\n\t\tStringInfo conninfo = makeStringInfo();\n\t\tappendStringInfo(conninfo, \"host='%s' port=%d user='%s' dbname='%s' \"\n\t\t\t\t\t\t\t\t   \"connect_timeout=20\",\n\t\t\t\t\t\t escape_param_str(sourceConnection->hostname),\n\t\t\t\t\t\t sourceConnection->port,\n\t\t\t\t\t\t escape_param_str(sourceConnection->user), escape_param_str(\n\t\t\t\t\t\t\t databaseName));\n\t\tif (CpuPriorityLogicalRepSender != CPU_PRIORITY_INHERIT &&\n\t\t\tlist_length(logicalRepTargetList) <= MaxHighPriorityBackgroundProcesess)\n\t\t{\n\t\t\tappendStringInfo(conninfo,\n\t\t\t\t\t\t\t \" options='-c citus.cpu_priority=%d'\",\n\t\t\t\t\t\t\t CpuPriorityLogicalRepSender);\n\t\t}\n\n\t\tStringInfo createSubscriptionCommand = makeStringInfo();\n\t\tappendStringInfo(createSubscriptionCommand,\n\t\t\t\t\t\t \"CREATE SUBSCRIPTION %s CONNECTION %s PUBLICATION %s \"\n\t\t\t\t\t\t \"WITH (citus_use_authinfo=true, create_slot=false, \"\n\n\t\t                 /*\n\t\t                  * password_required specifies whether connections to the publisher\n\t\t                  * made as a result of this subscription must use password authentication.\n\t\t                  * However, this setting is ignored when the subscription is owned\n\t\t                  * by a superuser.\n\t\t                  * Given that this command is executed below with superuser\n\t\t                  * ExecuteCriticalRemoteCommand(target->superuserConnection,\n\t\t                  *                              createSubscriptionCommand->data);\n\t\t                  * We are safe to pass password_required as false because\n\t\t                  * it will be ignored anyway\n\t\t                  */\n\t\t\t\t\t\t \"copy_data=false, enabled=false, slot_name=%s, password_required=false\",\n\t\t\t\t\t\t quote_identifier(target->subscriptionName),\n\t\t\t\t\t\t quote_literal_cstr(conninfo->data),\n\t\t\t\t\t\t quote_identifier(target->publication->name),\n\t\t\t\t\t\t quote_identifier(target->replicationSlot->name));\n\n\t\tif (EnableBinaryProtocol)\n\t\t{\n\t\t\tappendStringInfoString(createSubscriptionCommand, \", binary=true)\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfoString(createSubscriptionCommand, \")\");\n\t\t}\n\n\n\t\tExecuteCriticalRemoteCommand(target->superuserConnection,\n\t\t\t\t\t\t\t\t\t createSubscriptionCommand->data);\n\t\tpfree(createSubscriptionCommand->data);\n\t\tpfree(createSubscriptionCommand);\n\n\t\tInsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SUBSCRIPTION,\n\t\t\t\t\t\t\t\t\t\t\t  target->subscriptionName,\n\t\t\t\t\t\t\t\t\t\t\t  worker->groupId,\n\t\t\t\t\t\t\t\t\t\t\t  CLEANUP_ALWAYS);\n\n\t\tExecuteCriticalRemoteCommand(target->superuserConnection, psprintf(\n\t\t\t\t\t\t\t\t\t\t \"ALTER SUBSCRIPTION %s OWNER TO %s\",\n\t\t\t\t\t\t\t\t\t\t quote_identifier(target->subscriptionName),\n\t\t\t\t\t\t\t\t\t\t quote_identifier(target->subscriptionOwnerName)\n\t\t\t\t\t\t\t\t\t\t ));\n\n\t\t/*\n\t\t * The ALTER ROLE command should not propagate, so we temporarily\n\t\t * disable DDL propagation.\n\t\t */\n\t\tSendCommandListToWorkerOutsideTransactionWithConnection(\n\t\t\ttarget->superuserConnection,\n\t\t\tlist_make2(\n\t\t\t\t\"SET LOCAL citus.enable_ddl_propagation TO OFF;\",\n\t\t\t\tpsprintf(\n\t\t\t\t\t\"ALTER ROLE %s NOSUPERUSER;\",\n\t\t\t\t\tquote_identifier(target->subscriptionOwnerName)\n\t\t\t\t\t)));\n\t}\n}\n\n\n/*\n * EnableSubscriptions enables all the the subscriptions in the\n * logicalRepTargetList. This means the replication slot will start to be read\n * and the catchup phase begins.\n */\nvoid\nEnableSubscriptions(List *logicalRepTargetList)\n{\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tExecuteCriticalRemoteCommand(target->superuserConnection, psprintf(\n\t\t\t\t\t\t\t\t\t\t \"ALTER SUBSCRIPTION %s ENABLE\",\n\t\t\t\t\t\t\t\t\t\t target->subscriptionName\n\t\t\t\t\t\t\t\t\t\t ));\n\t}\n}\n\n\n/* *INDENT-OFF* */\n/*\n * Escaping libpq connect parameter strings.\n *\n * Replaces \"'\" with \"\\'\" and \"\\\" with \"\\\\\".\n *\n * Copied from dblink.c to escape libpq params\n */\nstatic char *\nescape_param_str(const char *str)\n{\n\tStringInfoData buf;\n\n\tinitStringInfo(&buf);\n\n\tfor (const char *cp = str; *cp; cp++)\n\t{\n\t\tif (*cp == '\\\\' || *cp == '\\'')\n\t\t\tappendStringInfoChar(&buf, '\\\\');\n\t\tappendStringInfoChar(&buf, *cp);\n\t}\n\n\treturn buf.data;\n}\n\n/* *INDENT-ON* */\n\n\n/*\n * GetRemoteLogPosition gets the current WAL log position over the given connection.\n */\nXLogRecPtr\nGetRemoteLogPosition(MultiConnection *connection)\n{\n\treturn GetRemoteLSN(connection, CURRENT_LOG_POSITION_COMMAND);\n}\n\n\n/*\n * GetRemoteLSN executes a command that returns a single LSN over the given connection\n * and returns it as an XLogRecPtr (uint64).\n */\nstatic XLogRecPtr\nGetRemoteLSN(MultiConnection *connection, char *command)\n{\n\tbool raiseInterrupts = false;\n\tXLogRecPtr remoteLogPosition = InvalidXLogRecPtr;\n\n\tint querySent = SendRemoteCommand(connection, command);\n\tif (querySent == 0)\n\t{\n\t\tReportConnectionError(connection, ERROR);\n\t}\n\n\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\tif (!IsResponseOK(result))\n\t{\n\t\tReportResultError(connection, result, ERROR);\n\t}\n\n\tint rowCount = PQntuples(result);\n\tif (rowCount != 1)\n\t{\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\t\treturn InvalidXLogRecPtr;\n\t}\n\n\tint colCount = PQnfields(result);\n\tif (colCount != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"unexpected number of columns returned by: %s\",\n\t\t\t\t\t\t\t   command)));\n\t}\n\n\tif (!PQgetisnull(result, 0, 0))\n\t{\n\t\tchar *resultString = PQgetvalue(result, 0, 0);\n\t\tDatum remoteLogPositionDatum = DirectFunctionCall1Coll(pg_lsn_in, InvalidOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   CStringGetDatum(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   resultString));\n\t\tremoteLogPosition = DatumGetLSN(remoteLogPositionDatum);\n\t}\n\n\tPQclear(result);\n\tForgetResults(connection);\n\n\treturn remoteLogPosition;\n}\n\n\n/*\n * CreateGroupedLogicalRepTargetsConnections creates connections for all of the nodes\n * in the groupedLogicalRepTargetsHash.\n */\nvoid\nCreateGroupedLogicalRepTargetsConnections(HTAB *groupedLogicalRepTargetsHash,\n\t\t\t\t\t\t\t\t\t\t  char *user,\n\t\t\t\t\t\t\t\t\t\t  char *databaseName)\n{\n\tint connectionFlags = FORCE_NEW_CONNECTION;\n\tHASH_SEQ_STATUS status;\n\tGroupedLogicalRepTargets *groupedLogicalRepTargets = NULL;\n\tforeach_htab(groupedLogicalRepTargets, &status, groupedLogicalRepTargetsHash)\n\t{\n\t\tWorkerNode *targetWorkerNode = FindNodeWithNodeId(\n\t\t\tgroupedLogicalRepTargets->nodeId,\n\t\t\tfalse);\n\t\tMultiConnection *superuserConnection =\n\t\t\tGetNodeUserDatabaseConnection(connectionFlags, targetWorkerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t  targetWorkerNode->workerPort,\n\t\t\t\t\t\t\t\t\t\t  user,\n\t\t\t\t\t\t\t\t\t\t  databaseName);\n\n\t\t/*\n\t\t * Operations on subscriptions cannot run in a transaction block. We\n\t\t * claim the connections exclusively to ensure they do not get used for\n\t\t * metadata syncing, which does open a transaction block.\n\t\t */\n\t\tClaimConnectionExclusively(superuserConnection);\n\n\t\tgroupedLogicalRepTargets->superuserConnection = superuserConnection;\n\n\t\tLogicalRepTarget *target = NULL;\n\t\tforeach_declared_ptr(target, groupedLogicalRepTargets->logicalRepTargetList)\n\t\t{\n\t\t\ttarget->superuserConnection = superuserConnection;\n\t\t}\n\t}\n}\n\n\n/*\n * CreateGroupedLogicalRepTargetsConnections closes the  connections for all of the\n * nodes in the groupedLogicalRepTargetsHash.\n */\nvoid\nCloseGroupedLogicalRepTargetsConnections(HTAB *groupedLogicalRepTargetsHash)\n{\n\tHASH_SEQ_STATUS status;\n\tGroupedLogicalRepTargets *groupedLogicalRepTargets = NULL;\n\tforeach_htab(groupedLogicalRepTargets, &status, groupedLogicalRepTargetsHash)\n\t{\n\t\tCloseConnection(groupedLogicalRepTargets->superuserConnection);\n\t}\n}\n\n\n/*\n * SubscriptionNamesValueList returns a SQL value list containing the\n * subscription names from the logicalRepTargetList. This value list can\n * be used in a query by using the IN operator.\n */\nstatic char *\nSubscriptionNamesValueList(List *logicalRepTargetList)\n{\n\tStringInfo subscriptionValueList = makeStringInfo();\n\tappendStringInfoString(subscriptionValueList, \"(\");\n\tbool first = true;\n\n\tLogicalRepTarget *target = NULL;\n\tforeach_declared_ptr(target, logicalRepTargetList)\n\t{\n\t\tif (!first)\n\t\t{\n\t\t\tappendStringInfoString(subscriptionValueList, \",\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tfirst = false;\n\t\t}\n\t\tappendStringInfoString(subscriptionValueList, quote_literal_cstr(\n\t\t\t\t\t\t\t\t   target->subscriptionName));\n\t}\n\tappendStringInfoString(subscriptionValueList, \")\");\n\treturn subscriptionValueList->data;\n}\n\n\n/*\n * WaitForAllSubscriptionToCatchUp waits until the last LSN reported by the\n * subscription.\n *\n * The function errors if the target LSN doesn't increase within\n * LogicalReplicationErrorTimeout. The function also reports its progress in\n * every logicalReplicationProgressReportTimeout.\n */\nvoid\nWaitForAllSubscriptionsToCatchUp(MultiConnection *sourceConnection,\n\t\t\t\t\t\t\t\t HTAB *groupedLogicalRepTargetsHash)\n{\n\tXLogRecPtr sourcePosition = GetRemoteLogPosition(sourceConnection);\n\tHASH_SEQ_STATUS status;\n\tGroupedLogicalRepTargets *groupedLogicalRepTargets = NULL;\n\tforeach_htab(groupedLogicalRepTargets, &status, groupedLogicalRepTargetsHash)\n\t{\n\t\tWaitForGroupedLogicalRepTargetsToCatchUp(sourcePosition,\n\t\t\t\t\t\t\t\t\t\t\t\t groupedLogicalRepTargets);\n\t}\n}\n\n\n/*\n * WaitForNodeSubscriptionToCatchUp waits until the last LSN reported by the\n * subscription.\n *\n * The function errors if the target LSN doesn't increase within\n * LogicalReplicationErrorTimeout. The function also reports its progress in\n * every logicalReplicationProgressReportTimeout.\n */\nstatic void\nWaitForGroupedLogicalRepTargetsToCatchUp(XLogRecPtr sourcePosition,\n\t\t\t\t\t\t\t\t\t\t GroupedLogicalRepTargets *\n\t\t\t\t\t\t\t\t\t\t groupedLogicalRepTargets)\n{\n\tXLogRecPtr previousTargetPosition = 0;\n\tTimestampTz previousLSNIncrementTime = GetCurrentTimestamp();\n\n\t/* report in the first iteration as well */\n\tTimestampTz previousReportTime = 0;\n\tMultiConnection *superuserConnection = groupedLogicalRepTargets->superuserConnection;\n\n\n\t/*\n\t * We might be in the loop for a while. Since we don't need to preserve\n\t * any memory beyond this function, we can simply switch to a child context\n\t * and reset it on every iteration to make sure we don't slowly build up\n\t * a lot of memory.\n\t */\n\tMemoryContext loopContext = AllocSetContextCreateInternal(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"WaitForShardSubscriptionToCatchUp\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MINSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_INITSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MAXSIZE);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(loopContext);\n\n\twhile (true)\n\t{\n\t\tXLogRecPtr targetPosition = GetSubscriptionPosition(groupedLogicalRepTargets);\n\t\tif (targetPosition >= sourcePosition)\n\t\t{\n\t\t\tereport(LOG, (errmsg(\n\t\t\t\t\t\t\t  \"The LSN of the target subscriptions on node %s:%d have \"\n\t\t\t\t\t\t\t  \"caught up with the source LSN \",\n\t\t\t\t\t\t\t  superuserConnection->hostname,\n\t\t\t\t\t\t\t  superuserConnection->port)));\n\n\t\t\tbreak;\n\t\t}\n\n\t\t/*\n\t\t * The following logic ensures that the subsription continues to grow withing\n\t\t * LogicalReplicationErrorTimeout duration. Otherwise, we error out since we\n\t\t * suspect that there is a problem on the target. It also handles the progess\n\t\t * reporting.\n\t\t */\n\t\tif (targetPosition > previousTargetPosition)\n\t\t{\n\t\t\t/* variable is only used for the log message */\n\t\t\tuint64 previousTargetBeforeThisLoop = previousTargetPosition;\n\n\t\t\tpreviousTargetPosition = targetPosition;\n\t\t\tpreviousLSNIncrementTime = GetCurrentTimestamp();\n\n\t\t\tif (TimestampDifferenceExceeds(previousReportTime,\n\t\t\t\t\t\t\t\t\t\t   GetCurrentTimestamp(),\n\t\t\t\t\t\t\t\t\t\t   logicalReplicationProgressReportTimeout))\n\t\t\t{\n\t\t\t\tereport(LOG, (errmsg(\"The LSN of the target subscriptions on node %s:%d \"\n\t\t\t\t\t\t\t\t\t \"has increased from %X/%X to %X/%X at %s where the \"\n\t\t\t\t\t\t\t\t\t \"source LSN is %X/%X \",\n\t\t\t\t\t\t\t\t\t superuserConnection->hostname,\n\t\t\t\t\t\t\t\t\t superuserConnection->port,\n\t\t\t\t\t\t\t\t\t LSN_FORMAT_ARGS(previousTargetBeforeThisLoop),\n\t\t\t\t\t\t\t\t\t LSN_FORMAT_ARGS(targetPosition),\n\t\t\t\t\t\t\t\t\t timestamptz_to_str(previousLSNIncrementTime),\n\t\t\t\t\t\t\t\t\t LSN_FORMAT_ARGS(sourcePosition))));\n\n\t\t\t\tpreviousReportTime = GetCurrentTimestamp();\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif (TimestampDifferenceExceeds(previousLSNIncrementTime,\n\t\t\t\t\t\t\t\t\t\t   GetCurrentTimestamp(),\n\t\t\t\t\t\t\t\t\t\t   LogicalReplicationTimeout))\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"The logical replication waiting timeout \"\n\t\t\t\t\t\t\t\t\t   \"of %d msec is exceeded\",\n\t\t\t\t\t\t\t\t\t   LogicalReplicationTimeout),\n\t\t\t\t\t\t\t\terrdetail(\"The LSN on the target subscription hasn't \"\n\t\t\t\t\t\t\t\t\t\t  \"caught up ready on the target node %s:%d\",\n\t\t\t\t\t\t\t\t\t\t  superuserConnection->hostname,\n\t\t\t\t\t\t\t\t\t\t  superuserConnection->port),\n\t\t\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\t\t\"There might have occurred problems on the target \"\n\t\t\t\t\t\t\t\t\t\"node. If not consider using higher values for \"\n\t\t\t\t\t\t\t\t\t\"citus.logical_replication_error_timeout\")));\n\t\t\t}\n\t\t}\n\n\t\t/* sleep for 1 seconds (1000 miliseconds) and try again */\n\t\tWaitForMiliseconds(1000);\n\n\t\tMemoryContextReset(loopContext);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * WaitForMiliseconds waits for given timeout and then checks for some\n * interrupts.\n */\nstatic void\nWaitForMiliseconds(long timeout)\n{\n\tint latchFlags = WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH;\n\n\t/* wait until timeout, or until somebody wakes us up */\n\tint rc = WaitLatch(MyLatch, latchFlags, timeout, PG_WAIT_EXTENSION);\n\n\t/* emergency bailout if postmaster has died */\n\tif (rc & WL_POSTMASTER_DEATH)\n\t{\n\t\tproc_exit(1);\n\t}\n\n\tif (rc & WL_LATCH_SET)\n\t{\n\t\tResetLatch(MyLatch);\n\t\tCHECK_FOR_INTERRUPTS();\n\t}\n\n\tif (ConfigReloadPending)\n\t{\n\t\tConfigReloadPending = false;\n\t\tProcessConfigFile(PGC_SIGHUP);\n\t}\n}\n\n\n/*\n * GetSubscriptionPosition gets the minimum WAL log position of the\n * subscription given subscriptions: That is the WAL log position on the source\n * node up to which the subscription completed replication.\n */\nstatic XLogRecPtr\nGetSubscriptionPosition(GroupedLogicalRepTargets *groupedLogicalRepTargets)\n{\n\tchar *subscriptionValueList = SubscriptionNamesValueList(\n\t\tgroupedLogicalRepTargets->logicalRepTargetList);\n\treturn GetRemoteLSN(groupedLogicalRepTargets->superuserConnection, psprintf(\n\t\t\t\t\t\t\t\"SELECT min(latest_end_lsn) FROM pg_stat_subscription \"\n\t\t\t\t\t\t\t\"WHERE subname IN %s\", subscriptionValueList));\n}\n"
  },
  {
    "path": "src/backend/distributed/shardsplit/shardsplit_decoder.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shardsplit_decoder.c\n *\t\tLogical Replication output plugin\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"catalog/pg_namespace.h\"\n#include \"replication/logical.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/typcache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/shardsplit_shared_memory.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_shard_visibility.h\"\n\nextern void _PG_output_plugin_init(OutputPluginCallbacks *cb);\nstatic LogicalDecodeChangeCB pgOutputPluginChangeCB;\n\n#define InvalidRepOriginId 0\n\nstatic HTAB *SourceToDestinationShardMap = NULL;\nstatic bool replication_origin_filter_cb(LogicalDecodingContext *ctx, RepOriginId\n\t\t\t\t\t\t\t\t\t\t origin_id);\n\n/* Plugin callback */\nstatic void shard_split_change_cb(LogicalDecodingContext *ctx,\n\t\t\t\t\t\t\t\t  ReorderBufferTXN *txn,\n\t\t\t\t\t\t\t\t  Relation relation, ReorderBufferChange *change);\n\n/* Helper methods */\nstatic int32_t GetHashValueForIncomingTuple(Relation sourceShardRelation,\n\t\t\t\t\t\t\t\t\t\t\tHeapTuple tuple,\n\t\t\t\t\t\t\t\t\t\t\tint partitionColumIndex,\n\t\t\t\t\t\t\t\t\t\t\tOid distributedTableOid);\n\nstatic Oid FindTargetRelationOid(Relation sourceShardRelation,\n\t\t\t\t\t\t\t\t HeapTuple tuple,\n\t\t\t\t\t\t\t\t char *currentSlotName);\n\nstatic HeapTuple GetTupleForTargetSchema(HeapTuple sourceRelationTuple,\n\t\t\t\t\t\t\t\t\t\t TupleDesc sourceTupleDesc,\n\t\t\t\t\t\t\t\t\t\t TupleDesc targetTupleDesc);\n\n/*\n * Postgres uses 'pgoutput' as default plugin for logical replication.\n * We want to reuse Postgres pgoutput's functionality as much as possible.\n * Hence we load all the functions of this plugin and override as required.\n */\nvoid\n_PG_output_plugin_init(OutputPluginCallbacks *cb)\n{\n\tLogicalOutputPluginInit plugin_init =\n\t\t(LogicalOutputPluginInit) (void *)\n\t\tload_external_function(\"pgoutput\",\n\t\t\t\t\t\t\t   \"_PG_output_plugin_init\",\n\t\t\t\t\t\t\t   false, NULL);\n\n\tif (plugin_init == NULL)\n\t{\n\t\telog(ERROR, \"output plugins have to declare the _PG_output_plugin_init symbol\");\n\t}\n\n\t/* ask the output plugin to fill the callback struct */\n\tplugin_init(cb);\n\n\t/* actual pgoutput callback will be called with the appropriate destination shard */\n\tpgOutputPluginChangeCB = cb->change_cb;\n\tcb->change_cb = shard_split_change_cb;\n\tcb->filter_by_origin_cb = replication_origin_filter_cb;\n}\n\n\n/*\n * replication_origin_filter_cb call back function filters out publication of changes\n * originated from any other node other than the current node. This is\n * identified by the \"origin_id\" of the changes. The origin_id is set to\n * a non-zero value in the origin node as part of WAL replication for internal\n * operations like shard split/moves/create_distributed_table etc.\n */\nstatic bool\nreplication_origin_filter_cb(LogicalDecodingContext *ctx, RepOriginId origin_id)\n{\n\treturn  (origin_id != InvalidRepOriginId);\n}\n\n\n/*\n * shard_split_change_cb function emits the incoming tuple change\n * to the appropriate destination shard.\n */\nstatic void\nshard_split_change_cb(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,\n\t\t\t\t\t  Relation relation, ReorderBufferChange *change)\n{\n\t/*\n\t * If Citus has not been loaded yet, pass the changes\n\t * through to the undrelying decoder plugin.\n\t */\n\tif (!CitusHasBeenLoaded())\n\t{\n\t\tpgOutputPluginChangeCB(ctx, txn, relation, change);\n\t\treturn;\n\t}\n\n\t/* check if the relation is publishable.*/\n\tif (!is_publishable_relation(relation))\n\t{\n\t\treturn;\n\t}\n\n\tchar *replicationSlotName = ctx->slot->data.name.data;\n\tif (replicationSlotName == NULL)\n\t{\n\t\telog(ERROR, \"Replication slot name is NULL!\");\n\t\treturn;\n\t}\n\n\t/*\n\t * Initialize SourceToDestinationShardMap if not already initialized.\n\t * This gets initialized during the replication of first message.\n\t */\n\tif (SourceToDestinationShardMap == NULL)\n\t{\n\t\tSourceToDestinationShardMap = PopulateSourceToDestinationShardMapForSlot(\n\t\t\treplicationSlotName, TopMemoryContext);\n\t}\n\n\tOid targetRelationOid = InvalidOid;\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\tswitch (change->action)\n\t{\n\t\tcase REORDER_BUFFER_CHANGE_INSERT:\n\t\t{\n\t\t\tHeapTuple newTuple = change->data.tp.newtuple;\n\t\t\ttargetRelationOid = FindTargetRelationOid(relation, newTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  replicationSlotName);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* updating non-partition column value */\n\t\tcase REORDER_BUFFER_CHANGE_UPDATE:\n\t\t{\n\t\t\tHeapTuple newTuple = change->data.tp.newtuple;\n\t\t\ttargetRelationOid = FindTargetRelationOid(relation, newTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  replicationSlotName);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase REORDER_BUFFER_CHANGE_DELETE:\n\t\t{\n\t\t\tHeapTuple oldTuple = change->data.tp.oldtuple;\n\t\t\ttargetRelationOid = FindTargetRelationOid(relation, oldTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  replicationSlotName);\n\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Only INSERT/DELETE/UPDATE actions are visible in the replication path of split shard */\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, errmsg(\n\t\t\t\t\t\t\"Unexpected Action :%d. Expected action is INSERT/DELETE/UPDATE\",\n\t\t\t\t\t\tchange->action));\n\t\t}\n\t}\n#else\n\tswitch (change->action)\n\t{\n\t\tcase REORDER_BUFFER_CHANGE_INSERT:\n\t\t{\n\t\t\tHeapTuple newTuple = &(change->data.tp.newtuple->tuple);\n\t\t\ttargetRelationOid = FindTargetRelationOid(relation, newTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  replicationSlotName);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* updating non-partition column value */\n\t\tcase REORDER_BUFFER_CHANGE_UPDATE:\n\t\t{\n\t\t\tHeapTuple newTuple = &(change->data.tp.newtuple->tuple);\n\t\t\ttargetRelationOid = FindTargetRelationOid(relation, newTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  replicationSlotName);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase REORDER_BUFFER_CHANGE_DELETE:\n\t\t{\n\t\t\tHeapTuple oldTuple = &(change->data.tp.oldtuple->tuple);\n\t\t\ttargetRelationOid = FindTargetRelationOid(relation, oldTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  replicationSlotName);\n\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Only INSERT/DELETE/UPDATE actions are visible in the replication path of split shard */\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, errmsg(\n\t\t\t\t\t\t\"Unexpected Action :%d. Expected action is INSERT/DELETE/UPDATE\",\n\t\t\t\t\t\tchange->action));\n\t\t}\n\t}\n#endif\n\n\t/* Current replication slot is not responsible for handling the change */\n\tif (targetRelationOid == InvalidOid)\n\t{\n\t\treturn;\n\t}\n\n\tRelation targetRelation = RelationIdGetRelation(targetRelationOid);\n\n\t/*\n\t * If any columns from source relation have been dropped, then the tuple needs to\n\t * be formatted according to the target relation.\n\t */\n\tTupleDesc sourceRelationDesc = RelationGetDescr(relation);\n\tTupleDesc targetRelationDesc = RelationGetDescr(targetRelation);\n\tif (sourceRelationDesc->natts > targetRelationDesc->natts)\n\t{\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\tswitch (change->action)\n\t\t{\n\t\t\tcase REORDER_BUFFER_CHANGE_INSERT:\n\t\t\t{\n\t\t\t\tHeapTuple sourceRelationNewTuple = change->data.tp.newtuple;\n\t\t\t\tHeapTuple targetRelationNewTuple = GetTupleForTargetSchema(\n\t\t\t\t\tsourceRelationNewTuple, sourceRelationDesc, targetRelationDesc);\n\n\t\t\t\tchange->data.tp.newtuple = targetRelationNewTuple;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase REORDER_BUFFER_CHANGE_UPDATE:\n\t\t\t{\n\t\t\t\tHeapTuple sourceRelationNewTuple = change->data.tp.newtuple;\n\t\t\t\tHeapTuple targetRelationNewTuple = GetTupleForTargetSchema(\n\t\t\t\t\tsourceRelationNewTuple, sourceRelationDesc, targetRelationDesc);\n\n\t\t\t\tchange->data.tp.newtuple = targetRelationNewTuple;\n\n\t\t\t\t/*\n\t\t\t\t * Format oldtuple according to the target relation. If the column values of replica\n\t\t\t\t * identiy change, then the old tuple is non-null and needs to be formatted according\n\t\t\t\t * to the target relation schema.\n\t\t\t\t */\n\t\t\t\tif (change->data.tp.oldtuple != NULL)\n\t\t\t\t{\n\t\t\t\t\tHeapTuple sourceRelationOldTuple = change->data.tp.oldtuple;\n\t\t\t\t\tHeapTuple targetRelationOldTuple = GetTupleForTargetSchema(\n\t\t\t\t\t\tsourceRelationOldTuple,\n\t\t\t\t\t\tsourceRelationDesc,\n\t\t\t\t\t\ttargetRelationDesc);\n\n\t\t\t\t\tchange->data.tp.oldtuple = targetRelationOldTuple;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase REORDER_BUFFER_CHANGE_DELETE:\n\t\t\t{\n\t\t\t\tHeapTuple sourceRelationOldTuple = change->data.tp.oldtuple;\n\t\t\t\tHeapTuple targetRelationOldTuple = GetTupleForTargetSchema(\n\t\t\t\t\tsourceRelationOldTuple, sourceRelationDesc, targetRelationDesc);\n\n\t\t\t\tchange->data.tp.oldtuple = targetRelationOldTuple;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* Only INSERT/DELETE/UPDATE actions are visible in the replication path of split shard */\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tereport(ERROR, errmsg(\n\t\t\t\t\t\t\t\"Unexpected Action :%d. Expected action is INSERT/DELETE/UPDATE\",\n\t\t\t\t\t\t\tchange->action));\n\t\t\t}\n\t\t}\n#else\n\t\tswitch (change->action)\n\t\t{\n\t\t\tcase REORDER_BUFFER_CHANGE_INSERT:\n\t\t\t{\n\t\t\t\tHeapTuple sourceRelationNewTuple = &(change->data.tp.newtuple->tuple);\n\t\t\t\tHeapTuple targetRelationNewTuple = GetTupleForTargetSchema(\n\t\t\t\t\tsourceRelationNewTuple, sourceRelationDesc, targetRelationDesc);\n\n\t\t\t\tchange->data.tp.newtuple->tuple = *targetRelationNewTuple;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase REORDER_BUFFER_CHANGE_UPDATE:\n\t\t\t{\n\t\t\t\tHeapTuple sourceRelationNewTuple = &(change->data.tp.newtuple->tuple);\n\t\t\t\tHeapTuple targetRelationNewTuple = GetTupleForTargetSchema(\n\t\t\t\t\tsourceRelationNewTuple, sourceRelationDesc, targetRelationDesc);\n\n\t\t\t\tchange->data.tp.newtuple->tuple = *targetRelationNewTuple;\n\n\t\t\t\t/*\n\t\t\t\t * Format oldtuple according to the target relation. If the column values of replica\n\t\t\t\t * identiy change, then the old tuple is non-null and needs to be formatted according\n\t\t\t\t * to the target relation schema.\n\t\t\t\t */\n\t\t\t\tif (change->data.tp.oldtuple != NULL)\n\t\t\t\t{\n\t\t\t\t\tHeapTuple sourceRelationOldTuple = &(change->data.tp.oldtuple->tuple);\n\t\t\t\t\tHeapTuple targetRelationOldTuple = GetTupleForTargetSchema(\n\t\t\t\t\t\tsourceRelationOldTuple,\n\t\t\t\t\t\tsourceRelationDesc,\n\t\t\t\t\t\ttargetRelationDesc);\n\n\t\t\t\t\tchange->data.tp.oldtuple->tuple = *targetRelationOldTuple;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase REORDER_BUFFER_CHANGE_DELETE:\n\t\t\t{\n\t\t\t\tHeapTuple sourceRelationOldTuple = &(change->data.tp.oldtuple->tuple);\n\t\t\t\tHeapTuple targetRelationOldTuple = GetTupleForTargetSchema(\n\t\t\t\t\tsourceRelationOldTuple, sourceRelationDesc, targetRelationDesc);\n\n\t\t\t\tchange->data.tp.oldtuple->tuple = *targetRelationOldTuple;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* Only INSERT/DELETE/UPDATE actions are visible in the replication path of split shard */\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\tereport(ERROR, errmsg(\n\t\t\t\t\t\t\t\"Unexpected Action :%d. Expected action is INSERT/DELETE/UPDATE\",\n\t\t\t\t\t\t\tchange->action));\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\tpgOutputPluginChangeCB(ctx, txn, targetRelation, change);\n\tRelationClose(targetRelation);\n}\n\n\n/*\n * FindTargetRelationOid returns the destination relation Oid for the incoming\n * tuple.\n * sourceShardRelation - Relation on which a commit has happened.\n * tuple               - changed tuple.\n * currentSlotName     - Name of replication slot that is processing this update.\n */\nstatic Oid\nFindTargetRelationOid(Relation sourceShardRelation,\n\t\t\t\t\t  HeapTuple tuple,\n\t\t\t\t\t  char *currentSlotName)\n{\n\tOid targetRelationOid = InvalidOid;\n\tOid sourceShardRelationOid = sourceShardRelation->rd_id;\n\n\t/* Get child shard list for source(parent) shard from hashmap*/\n\tbool found = false;\n\tSourceToDestinationShardMapEntry *entry =\n\t\t(SourceToDestinationShardMapEntry *) hash_search(\n\t\t\tSourceToDestinationShardMap, &sourceShardRelationOid, HASH_FIND, &found);\n\n\t/*\n\t * Source shard Oid might not exist in the hash map. This can happen\n\t * in below cases:\n\t * 1) The commit can belong to any other table that is not under going split.\n\t * 2) The commit can be recursive in nature. When the source shard\n\t * receives a commit(a), the WAL sender processes this commit message. This\n\t * commit is applied to a child shard which is placed on the same node as a\n\t * part of replication. This in turn creates one more commit(b) which is recursive in nature.\n\t * Commit 'b' should be skipped as the source shard and destination for commit 'b'\n\t * are same and the commit has already been applied.\n\t */\n\tif (!found)\n\t{\n\t\treturn InvalidOid;\n\t}\n\n\tShardSplitInfo *shardSplitInfo = (ShardSplitInfo *) lfirst(list_head(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   entry->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   shardSplitInfoList));\n\tint hashValue = GetHashValueForIncomingTuple(sourceShardRelation, tuple,\n\t\t\t\t\t\t\t\t\t\t\t\t shardSplitInfo->partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t shardSplitInfo->distributedTableOid);\n\n\tshardSplitInfo = NULL;\n\tforeach_declared_ptr(shardSplitInfo, entry->shardSplitInfoList)\n\t{\n\t\tif (shardSplitInfo->shardMinValue <= hashValue &&\n\t\t\tshardSplitInfo->shardMaxValue >= hashValue)\n\t\t{\n\t\t\ttargetRelationOid = shardSplitInfo->splitChildShardOid;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn targetRelationOid;\n}\n\n\n/*\n * GetHashValueForIncomingTuple returns the hash value of the partition\n * column for the incoming tuple.\n */\nstatic int32_t\nGetHashValueForIncomingTuple(Relation sourceShardRelation,\n\t\t\t\t\t\t\t HeapTuple tuple,\n\t\t\t\t\t\t\t int partitionColumnIndex,\n\t\t\t\t\t\t\t Oid distributedTableOid)\n{\n\tTupleDesc relationTupleDes = RelationGetDescr(sourceShardRelation);\n\tForm_pg_attribute partitionColumn = TupleDescAttr(relationTupleDes,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionColumnIndex);\n\n\tbool isNull = false;\n\tDatum partitionColumnValue = heap_getattr(tuple,\n\t\t\t\t\t\t\t\t\t\t\t  partitionColumnIndex + 1,\n\t\t\t\t\t\t\t\t\t\t\t  relationTupleDes,\n\t\t\t\t\t\t\t\t\t\t\t  &isNull);\n\n\tTypeCacheEntry *typeEntry = lookup_type_cache(partitionColumn->atttypid,\n\t\t\t\t\t\t\t\t\t\t\t\t  TYPECACHE_HASH_PROC_FINFO);\n\n\t/* get hashed value of the distribution value */\n\tDatum hashedValueDatum = FunctionCall1Coll(&(typeEntry->hash_proc_finfo),\n\t\t\t\t\t\t\t\t\t\t\t   typeEntry->typcollation,\n\t\t\t\t\t\t\t\t\t\t\t   partitionColumnValue);\n\n\treturn DatumGetInt32(hashedValueDatum);\n}\n\n\n/*\n * GetTupleForTargetSchema returns a tuple with the schema of the target relation.\n * If some columns within the source relations are dropped, we would have to reformat\n * the tuple to match the schema of the target relation.\n *\n * Consider the below scenario:\n * Session1 : Drop column followed by create_distributed_table_concurrently\n * Session2 : Concurrent insert workload\n *\n * The child shards created by create_distributed_table_concurrently will have less columns\n * than the source shard because some column were dropped.\n * The incoming tuple from session2 will have more columns as the writes\n * happened on source shard. But now the tuple needs to be applied on child shard. So we need to format\n * it according to child schema.\n */\nstatic HeapTuple\nGetTupleForTargetSchema(HeapTuple sourceRelationTuple,\n\t\t\t\t\t\tTupleDesc sourceRelDesc,\n\t\t\t\t\t\tTupleDesc targetRelDesc)\n{\n\t/* Deform the tuple */\n\tDatum *oldValues = (Datum *) palloc0(sourceRelDesc->natts * sizeof(Datum));\n\tbool *oldNulls = (bool *) palloc0(sourceRelDesc->natts * sizeof(bool));\n\theap_deform_tuple(sourceRelationTuple, sourceRelDesc, oldValues,\n\t\t\t\t\t  oldNulls);\n\n\n\t/* Create new tuple by skipping dropped columns */\n\tint nextAttributeIndex = 0;\n\tDatum *newValues = (Datum *) palloc0(targetRelDesc->natts * sizeof(Datum));\n\tbool *newNulls = (bool *) palloc0(targetRelDesc->natts * sizeof(bool));\n\tfor (int i = 0; i < sourceRelDesc->natts; i++)\n\t{\n\t\tif (TupleDescAttr(sourceRelDesc, i)->attisdropped)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tnewValues[nextAttributeIndex] = oldValues[i];\n\t\tnewNulls[nextAttributeIndex] = oldNulls[i];\n\t\tnextAttributeIndex++;\n\t}\n\n\tHeapTuple targetRelationTuple = heap_form_tuple(targetRelDesc, newValues, newNulls);\n\treturn targetRelationTuple;\n}\n"
  },
  {
    "path": "src/backend/distributed/shardsplit/shardsplit_logical_replication.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shardsplit_logical_replication.c\n *\n * Function definitions for logically replicating shard to split children.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"commands/dbcommands.h\"\n#include \"nodes/pg_list.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/priority.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_split.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/shardsplit_logical_replication.h\"\n#include \"distributed/shared_library_init.h\"\n\n\nstatic HTAB *ShardInfoHashMapForPublications = NULL;\n\n/* function declarations */\nstatic void AddPublishableShardEntryInMap(uint32 targetNodeId,\n\t\t\t\t\t\t\t\t\t\t  ShardInterval *shardInterval, bool\n\t\t\t\t\t\t\t\t\t\t  isChildShardInterval);\nstatic LogicalRepTarget * CreateLogicalRepTarget(Oid tableOwnerId,\n\t\t\t\t\t\t\t\t\t\t\t\t uint32 nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t List *replicationSlotInfoList);\n\n/*\n * CreateShardSplitInfoMapForPublication creates a hashmap that groups\n * shards for creating publications and subscriptions.\n *\n * While creating publications and subscriptions, apart from table owners,\n * placement of child shard matters too. To further understand this, please see\n * the following example:\n *\n * Shard1(on Worker1) is to be split in Shard2 and Shard3 on Worker2 and Worker3 respectively.\n * Lets assume the owner to be 'A'. The hashmap groups shard list in the following way.\n *\n * Map key\n * =======\t               ------     ------\n * <Worker2, 'A'> ------> |Shard2|-->|Shard1|\n *                         ------     ------\n *\n *                         ------     ------\n * <Worker3, 'A'> ------> |Shard3|-->|Shard1|\n *                         ------     ------\n * Shard1 is a dummy table that is to be created on Worker2 and Worker3.\n * Based on the above placement, we would need to create two publications on the source node.\n */\nHTAB *\nCreateShardSplitInfoMapForPublication(List *sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t\t\t  List *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t  List *destinationWorkerNodesList)\n{\n\tShardInfoHashMapForPublications = CreateSimpleHash(NodeAndOwner, PublicationInfo);\n\tShardInterval *sourceShardIntervalToCopy = NULL;\n\tList *splitChildShardIntervalList = NULL;\n\tforboth_ptr(sourceShardIntervalToCopy, sourceColocatedShardIntervalList,\n\t\t\t\tsplitChildShardIntervalList, shardGroupSplitIntervalListList)\n\t{\n\t\t/*\n\t\t * Skipping partitioned table for logical replication.\n\t\t * Since PG13, logical replication is supported for partitioned tables.\n\t\t * However, we want to keep the behaviour consistent with shard moves.\n\t\t */\n\t\tif (PartitionedTable(sourceShardIntervalToCopy->relationId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tShardInterval *splitChildShardInterval = NULL;\n\t\tWorkerNode *destinationWorkerNode = NULL;\n\t\tforboth_ptr(splitChildShardInterval, splitChildShardIntervalList,\n\t\t\t\t\tdestinationWorkerNode, destinationWorkerNodesList)\n\t\t{\n\t\t\tuint32 destinationWorkerNodeId = destinationWorkerNode->nodeId;\n\n\t\t\t/* Add child shard for publication.\n\t\t\t * If a columnar shard is a part of publications, then writes on the shard fail.\n\t\t\t * In the case of local split, adding child shards to the publication\n\t\t\t * would prevent copying the initial data done through 'DoSplitCopy'.\n\t\t\t * Hence we avoid adding columnar child shards to publication.\n\t\t\t */\n\t\t\tif (!extern_IsColumnarTableAmTable(splitChildShardInterval->relationId))\n\t\t\t{\n\t\t\t\tAddPublishableShardEntryInMap(destinationWorkerNodeId,\n\t\t\t\t\t\t\t\t\t\t\t  splitChildShardInterval,\n\t\t\t\t\t\t\t\t\t\t\t  true /*isChildShardInterval*/);\n\t\t\t}\n\n\t\t\t/* Add parent shard if not already added */\n\t\t\tAddPublishableShardEntryInMap(destinationWorkerNodeId,\n\t\t\t\t\t\t\t\t\t\t  sourceShardIntervalToCopy,\n\t\t\t\t\t\t\t\t\t\t  false /*isChildShardInterval*/);\n\t\t}\n\t}\n\n\treturn ShardInfoHashMapForPublications;\n}\n\n\n/*\n * AddPublishableShardEntryInMap adds a shard interval in the list\n * of shards to be published.\n */\nstatic void\nAddPublishableShardEntryInMap(uint32 targetNodeId, ShardInterval *shardInterval, bool\n\t\t\t\t\t\t\t  isChildShardInterval)\n{\n\tNodeAndOwner key;\n\tkey.nodeId = targetNodeId;\n\tkey.tableOwnerId = TableOwnerOid(shardInterval->relationId);\n\n\tbool found = false;\n\tPublicationInfo *publicationInfo =\n\t\t(PublicationInfo *) hash_search(ShardInfoHashMapForPublications, &key,\n\t\t\t\t\t\t\t\t\t\tHASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t&found);\n\n\t/* Create a new list for <nodeId, owner> pair */\n\tif (!found)\n\t{\n\t\tpublicationInfo->shardIntervals = NIL;\n\t\tpublicationInfo->name = PublicationName(SHARD_SPLIT, key.nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\tkey.tableOwnerId);\n\t}\n\n\t/* Add child shard interval */\n\tif (isChildShardInterval)\n\t{\n\t\tpublicationInfo->shardIntervals =\n\t\t\tlappend(publicationInfo->shardIntervals, shardInterval);\n\n\t\t/* We return from here as the child interval is only added once in the list */\n\t\treturn;\n\t}\n\n\t/* Check if parent is already added */\n\tShardInterval *existingShardInterval = NULL;\n\tforeach_declared_ptr(existingShardInterval, publicationInfo->shardIntervals)\n\t{\n\t\tif (existingShardInterval->shardId == shardInterval->shardId)\n\t\t{\n\t\t\t/* parent shard interval is already added hence return */\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/* Add parent shard Interval */\n\tpublicationInfo->shardIntervals =\n\t\tlappend(publicationInfo->shardIntervals, shardInterval);\n}\n\n\n/*\n * PopulateShardSplitSubscriptionsMetadataList returns a list of 'LogicalRepTarget'\n * structure.\n *\n * shardSplitInfoHashMap - Shards are grouped by <owner, node id> key.\n *                         For each key, we create a metadata structure. This facilitates easy\n *                         publication-subscription management.\n *\n * replicationSlotInfoList - List of replication slot info.\n */\nList *\nPopulateShardSplitSubscriptionsMetadataList(HTAB *shardSplitInfoHashMap,\n\t\t\t\t\t\t\t\t\t\t\tList *replicationSlotInfoList,\n\t\t\t\t\t\t\t\t\t\t\tList *shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\tList *workersForPlacementList)\n{\n\tHASH_SEQ_STATUS status;\n\thash_seq_init(&status, shardSplitInfoHashMap);\n\n\tPublicationInfo *publication = NULL;\n\tList *logicalRepTargetList = NIL;\n\twhile ((publication = (PublicationInfo *) hash_seq_search(&status)) != NULL)\n\t{\n\t\tuint32 nodeId = publication->key.nodeId;\n\t\tuint32 tableOwnerId = publication->key.tableOwnerId;\n\t\tLogicalRepTarget *target =\n\t\t\tCreateLogicalRepTarget(tableOwnerId, nodeId,\n\t\t\t\t\t\t\t\t   replicationSlotInfoList);\n\t\ttarget->publication = publication;\n\t\tpublication->target = target;\n\n\t\tlogicalRepTargetList = lappend(logicalRepTargetList, target);\n\t}\n\n\tList *shardIntervalList = NIL;\n\tforeach_declared_ptr(shardIntervalList, shardGroupSplitIntervalListList)\n\t{\n\t\tShardInterval *shardInterval = NULL;\n\t\tWorkerNode *workerPlacementNode = NULL;\n\t\tforboth_ptr(shardInterval, shardIntervalList, workerPlacementNode,\n\t\t\t\t\tworkersForPlacementList)\n\t\t{\n\t\t\tNodeAndOwner key;\n\t\t\tkey.nodeId = workerPlacementNode->nodeId;\n\t\t\tkey.tableOwnerId = TableOwnerOid(shardInterval->relationId);\n\n\t\t\tbool found = false;\n\t\t\tpublication = (PublicationInfo *) hash_search(\n\t\t\t\tShardInfoHashMapForPublications,\n\t\t\t\t&key,\n\t\t\t\tHASH_FIND,\n\t\t\t\t&found);\n\t\t\tif (!found)\n\t\t\t{\n\t\t\t\tereport(ERROR, errmsg(\"Could not find publication matching a split\"));\n\t\t\t}\n\t\t\tpublication->target->newShards = lappend(\n\t\t\t\tpublication->target->newShards, shardInterval);\n\t\t}\n\t}\n\n\treturn logicalRepTargetList;\n}\n\n\n/*\n * Creates a 'LogicalRepTarget' structure for given table owner, node id.\n * It scans the list of 'ReplicationSlotInfo' to identify the corresponding slot\n * to be used for given tableOwnerId and nodeId.\n */\nstatic LogicalRepTarget *\nCreateLogicalRepTarget(Oid tableOwnerId, uint32 nodeId,\n\t\t\t\t\t   List *replicationSlotInfoList)\n{\n\tLogicalRepTarget *target = palloc0(sizeof(LogicalRepTarget));\n\ttarget->subscriptionName = SubscriptionName(SHARD_SPLIT, tableOwnerId);\n\ttarget->tableOwnerId = tableOwnerId;\n\ttarget->subscriptionOwnerName =\n\t\tSubscriptionRoleName(SHARD_SPLIT, tableOwnerId);\n\ttarget->superuserConnection = NULL;\n\n\t/*\n\t * Each 'ReplicationSlotInfo' belongs to a unique combination of node id and owner.\n\t * Traverse the slot list to identify the corresponding slot for given\n\t * table owner and node.\n\t */\n\tReplicationSlotInfo *replicationSlot = NULL;\n\tforeach_declared_ptr(replicationSlot, replicationSlotInfoList)\n\t{\n\t\tif (nodeId == replicationSlot->targetNodeId &&\n\t\t\ttableOwnerId == replicationSlot->tableOwnerId)\n\t\t{\n\t\t\ttarget->replicationSlot = replicationSlot;\n\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!target->replicationSlot)\n\t{\n\t\tereport(ERROR, errmsg(\n\t\t\t\t\t\"Could not find replication slot matching a subscription %s\",\n\t\t\t\t\ttarget->subscriptionName));\n\t}\n\n\treturn target;\n}\n"
  },
  {
    "path": "src/backend/distributed/shardsplit/shardsplit_shared_memory.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shardsplit_shared_memory.c\n *    API's for creating and accessing shared memory segments to store\n *    shard split information. 'setup_shard_replication' UDF creates the\n *    shared memory, populates the contents and WAL sender processes are\n *    the consumers.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"common/hashfn.h\"\n#include \"storage/ipc.h\"\n#include \"utils/memutils.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/multi_logical_replication.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/shardsplit_shared_memory.h\"\n\nconst char *SharedMemoryNameForHandleManagement =\n\t\"Shared memory handle for shard split\";\n\nstatic shmem_startup_hook_type prev_shmem_startup_hook = NULL;\n\n/* Function declarations */\nstatic ShardSplitInfoSMHeader * AllocateSharedMemoryForShardSplitInfo(int\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardSplitInfoCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Size\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardSplitInfoSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  dsm_handle *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  dsmHandle);\nstatic ShardSplitInfoSMHeader * GetShardSplitInfoSMHeaderFromDSMHandle(dsm_handle\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   dsmHandle);\nstatic dsm_handle GetShardSplitSharedMemoryHandle(void);\nstatic void ShardSplitShmemInit(void);\n\n/*\n * GetShardSplitInfoSMHeaderFromDSMHandle returns the header of the shared memory\n * segment. It pins the mapping till lifetime of the backend process accessing it.\n */\nstatic ShardSplitInfoSMHeader *\nGetShardSplitInfoSMHeaderFromDSMHandle(dsm_handle dsmHandle)\n{\n\tdsm_segment *dsmSegment = dsm_find_mapping(dsmHandle);\n\n\tif (dsmSegment == NULL)\n\t{\n\t\tdsmSegment = dsm_attach(dsmHandle);\n\t}\n\n\tif (dsmSegment == NULL)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"could not attach to dynamic shared memory segment \"\n\t\t\t\t\t\t\"corresponding to handle:%u\", dsmHandle)));\n\t}\n\n\t/*\n\t * Detatching segment associated with resource owner with 'dsm_pin_mapping' call before the\n\t * resource owner releases, to avoid warning being logged and potential leaks.\n\t */\n\tdsm_pin_mapping(dsmSegment);\n\n\tShardSplitInfoSMHeader *header = (ShardSplitInfoSMHeader *) dsm_segment_address(\n\t\tdsmSegment);\n\n\tif (header == NULL)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"Could not get shared memory segment header \"\n\t\t\t\t\t\t\"corresponding to handle for split workflow:%u\", dsmHandle)));\n\t}\n\n\treturn header;\n}\n\n\n/*\n * GetShardSplitInfoSMHeader returns pointer to the header of shared memory segment.\n */\nShardSplitInfoSMHeader *\nGetShardSplitInfoSMHeader()\n{\n\tdsm_handle dsmHandle = GetShardSplitSharedMemoryHandle();\n\n\tShardSplitInfoSMHeader *shardSplitInfoSMHeader =\n\t\tGetShardSplitInfoSMHeaderFromDSMHandle(dsmHandle);\n\n\treturn shardSplitInfoSMHeader;\n}\n\n\n/*\n * AllocateSharedMemoryForShardSplitInfo is used to allocate and store\n * information about the shard undergoing a split. The function allocates dynamic\n * shared memory segment consisting of a header and an array of ShardSplitInfo structure.\n * The contents of this shared memory segment are consumed by WAL sender process\n * during catch up phase of replication through logical decoding plugin.\n *\n * The shared memory segment exists till the catch up phase completes or the\n * postmaster shutsdown.\n */\nstatic ShardSplitInfoSMHeader *\nAllocateSharedMemoryForShardSplitInfo(int shardSplitInfoCount, Size shardSplitInfoSize,\n\t\t\t\t\t\t\t\t\t  dsm_handle *dsmHandle)\n{\n\tif (shardSplitInfoCount <= 0 || shardSplitInfoSize <= 0)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"shardSplitInfoCount and size of each step should be \"\n\t\t\t\t\t\t\"positive values\")));\n\t}\n\n\tSize totalSize = offsetof(ShardSplitInfoSMHeader, splitInfoArray) +\n\t\t\t\t\t (shardSplitInfoCount * shardSplitInfoSize);\n\tdsm_segment *dsmSegment = dsm_create(totalSize, DSM_CREATE_NULL_IF_MAXSEGMENTS);\n\n\tif (dsmSegment == NULL)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"could not create a dynamic shared memory segment to \"\n\t\t\t\t\t\t\"store shard split info\")));\n\t}\n\n\t*dsmHandle = dsm_segment_handle(dsmSegment);\n\n\t/*\n\t * Pin the segment till Postmaster shutsdown since we need this\n\t * segment even after the session ends for replication catchup phase.\n\t */\n\tdsm_pin_segment(dsmSegment);\n\n\tShardSplitInfoSMHeader *shardSplitInfoSMHeader =\n\t\tGetShardSplitInfoSMHeaderFromDSMHandle(*dsmHandle);\n\n\tshardSplitInfoSMHeader->count = shardSplitInfoCount;\n\n\treturn shardSplitInfoSMHeader;\n}\n\n\n/*\n * CreateSharedMemoryForShardSplitInfo is a wrapper function which creates shared memory\n * for storing shard split infomation. The function returns pointer to the header of\n * shared memory segment.\n *\n * shardSplitInfoCount - number of 'ShardSplitInfo ' elements to be allocated\n * dsmHandle           - handle of the allocated shared memory segment\n */\nShardSplitInfoSMHeader *\nCreateSharedMemoryForShardSplitInfo(int shardSplitInfoCount, dsm_handle *dsmHandle)\n{\n\tShardSplitInfoSMHeader *shardSplitInfoSMHeader =\n\t\tAllocateSharedMemoryForShardSplitInfo(shardSplitInfoCount,\n\t\t\t\t\t\t\t\t\t\t\t  sizeof(ShardSplitInfo),\n\t\t\t\t\t\t\t\t\t\t\t  dsmHandle);\n\treturn shardSplitInfoSMHeader;\n}\n\n\n/*\n * ReleaseSharedMemoryOfShardSplitInfo releases(unpins) the dynamic shared memory segment\n * allocated by 'worker_split_shard_replication_setup'. This shared memory was pinned\n * to Postmaster process and is valid till Postmaster shutsdown or\n * explicitly unpinned by calling 'dsm_unpin_segment'.\n */\nvoid\nReleaseSharedMemoryOfShardSplitInfo()\n{\n\t/* Get handle of dynamic shared memory segment*/\n\tdsm_handle dsmHandle = GetShardSplitSharedMemoryHandle();\n\n\tif (dsmHandle == DSM_HANDLE_INVALID)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * Unpin the dynamic shared memory segment. 'dsm_pin_segment' was\n\t * called previously by 'AllocateSharedMemoryForShardSplitInfo'.\n\t */\n\tdsm_unpin_segment(dsmHandle);\n\n\t/*\n\t * As dynamic shared memory is unpinned, store an invalid handle in static\n\t * shared memory used for handle management.\n\t */\n\tStoreShardSplitSharedMemoryHandle(DSM_HANDLE_INVALID);\n}\n\n\n/*\n * InitializeShardSplitSMHandleManagement requests the necessary shared memory\n * from Postgres and sets up the shared memory startup hook.\n * This memory is used to store handle of other shared memories allocated during split workflow.\n */\nvoid\nInitializeShardSplitSMHandleManagement(void)\n{\n\tprev_shmem_startup_hook = shmem_startup_hook;\n\tshmem_startup_hook = ShardSplitShmemInit;\n}\n\n\nstatic void\nShardSplitShmemInit(void)\n{\n\tbool alreadyInitialized = false;\n\tShardSplitShmemData *smData = ShmemInitStruct(SharedMemoryNameForHandleManagement,\n\t\t\t\t\t\t\t\t\t\t\t\t  sizeof(ShardSplitShmemData),\n\t\t\t\t\t\t\t\t\t\t\t\t  &alreadyInitialized);\n\n\tif (!alreadyInitialized)\n\t{\n\t\tchar *trancheName = \"Split Shard Setup Tranche\";\n\n\t\tNamedLWLockTranche *namedLockTranche =\n\t\t\t&smData->namedLockTranche;\n\n\t\t/* start by zeroing out all the memory */\n\t\tmemset(smData, 0,\n\t\t\t   sizeof(ShardSplitShmemData));\n\n\t\tnamedLockTranche->trancheId = LWLockNewTrancheId();\n\n\t\tLWLockRegisterTranche(namedLockTranche->trancheId, trancheName);\n\t\tLWLockInitialize(&smData->lock,\n\t\t\t\t\t\t namedLockTranche->trancheId);\n\n\t\tsmData->dsmHandle = DSM_HANDLE_INVALID;\n\t}\n\n\tif (prev_shmem_startup_hook != NULL)\n\t{\n\t\tprev_shmem_startup_hook();\n\t}\n}\n\n\n/*\n * StoreShardSplitSharedMemoryHandle stores a handle of shared memory\n * allocated and populated by 'worker_split_shard_replication_setup' UDF.\n * This handle is stored in a different statically allocated shared memory\n * segment with name 'Shared memory handle for shard split'.\n */\nvoid\nStoreShardSplitSharedMemoryHandle(dsm_handle dsmHandle)\n{\n\tbool found = false;\n\tShardSplitShmemData *smData = ShmemInitStruct(SharedMemoryNameForHandleManagement,\n\t\t\t\t\t\t\t\t\t\t\t\t  sizeof(ShardSplitShmemData),\n\t\t\t\t\t\t\t\t\t\t\t\t  &found);\n\tif (!found)\n\t{\n\t\tereport(ERROR,\n\t\t\t\terrmsg(\n\t\t\t\t\t\"Shared memory for handle management should have been initialized during boot\"));\n\t}\n\n\t/*\n\t * We only support non concurrent split. However, it is fine to take a\n\t * lock and store the handle incase concurrent splits are introduced in future.\n\t */\n\tLWLockAcquire(&smData->lock, LW_EXCLUSIVE);\n\n\t/*\n\t * In a normal situation, previously stored handle should have been invalidated\n\t * before the current function is called.\n\t * If this handle is still valid, it means cleanup of previous split shard\n\t * workflow failed. Log a waring and continue the current shard split operation.\n\t * Skip warning if new handle to be stored is invalid. We store invalid handle\n\t * when shared memory is released by calling worker_split_shard_release_dsm.\n\t */\n\tif (smData->dsmHandle != DSM_HANDLE_INVALID && dsmHandle != DSM_HANDLE_INVALID)\n\t{\n\t\tereport(WARNING,\n\t\t\t\terrmsg(\n\t\t\t\t\t\"Previous split shard worflow was not successfully and could not complete the cleanup phase.\"\n\t\t\t\t\t\" Continuing with the current split shard workflow.\"));\n\t}\n\n\t/* Store the incoming handle */\n\tsmData->dsmHandle = dsmHandle;\n\n\tLWLockRelease(&smData->lock);\n}\n\n\n/*\n * GetShardSplitSharedMemoryHandle returns the handle of dynamic shared memory segment stored\n * by 'worker_split_shard_replication_setup' UDF. This handle is requested by WAL sender processes\n * during logical replication phase or during cleanup.\n */\ndsm_handle\nGetShardSplitSharedMemoryHandle(void)\n{\n\tbool found = false;\n\tShardSplitShmemData *smData = ShmemInitStruct(SharedMemoryNameForHandleManagement,\n\t\t\t\t\t\t\t\t\t\t\t\t  sizeof(ShardSplitShmemData),\n\t\t\t\t\t\t\t\t\t\t\t\t  &found);\n\tif (!found)\n\t{\n\t\tereport(ERROR,\n\t\t\t\terrmsg(\n\t\t\t\t\t\"Shared memory for handle management should have been initialized during boot\"));\n\t}\n\n\tLWLockAcquire(&smData->lock, LW_SHARED);\n\tdsm_handle dsmHandle = smData->dsmHandle;\n\tLWLockRelease(&smData->lock);\n\n\treturn dsmHandle;\n}\n\n\n/*\n * PopulateSourceToDestinationShardMapForSlot populates 'SourceToDestinationShard' hash map for a given slot.\n * Key of the map is Oid of source shard which is undergoing a split and value is a list of corresponding child shards.\n * To populate the map, the function traverses 'ShardSplitInfo' array stored within shared memory segment.\n */\nHTAB *\nPopulateSourceToDestinationShardMapForSlot(char *slotName, MemoryContext cxt)\n{\n\tHASHCTL info;\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(Oid);\n\tinfo.entrysize = sizeof(SourceToDestinationShardMapEntry);\n\tinfo.hash = uint32_hash;\n\tinfo.hcxt = cxt;\n\n\tint hashFlags = (HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION);\n\tHTAB *sourceShardToDesShardMap = hash_create(\"SourceToDestinationShardMap\", 128,\n\t\t\t\t\t\t\t\t\t\t\t\t &info, hashFlags);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(cxt);\n\n\tShardSplitInfoSMHeader *smHeader = GetShardSplitInfoSMHeader();\n\tfor (int index = 0; index < smHeader->count; index++)\n\t{\n\t\tif (strcmp(smHeader->splitInfoArray[index].slotName, slotName) == 0)\n\t\t{\n\t\t\tOid sourceShardOid = smHeader->splitInfoArray[index].sourceShardOid;\n\t\t\tbool found = false;\n\t\t\tSourceToDestinationShardMapEntry *entry =\n\t\t\t\t(SourceToDestinationShardMapEntry *) hash_search(\n\t\t\t\t\tsourceShardToDesShardMap, &sourceShardOid, HASH_ENTER, &found);\n\n\t\t\tif (!found)\n\t\t\t{\n\t\t\t\tentry->shardSplitInfoList = NIL;\n\t\t\t\tentry->sourceShardKey = sourceShardOid;\n\t\t\t}\n\n\t\t\tShardSplitInfo *shardSplitInfoForSlot = (ShardSplitInfo *) palloc0(\n\t\t\t\tsizeof(ShardSplitInfo));\n\t\t\t*shardSplitInfoForSlot = smHeader->splitInfoArray[index];\n\n\t\t\tentry->shardSplitInfoList = lappend(entry->shardSplitInfoList,\n\t\t\t\t\t\t\t\t\t\t\t\t(ShardSplitInfo *) shardSplitInfoForSlot);\n\t\t}\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\treturn sourceShardToDesShardMap;\n}\n"
  },
  {
    "path": "src/backend/distributed/shared_library_init.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shared_library_init.c\n *\t  Functionality related to the initialization of the Citus extension.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include <limits.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include \"postgres.h\"\n\n/* necessary to get alloca on illumos */\n#ifdef __sun\n#include <alloca.h>\n#endif\n\n#include \"fmgr.h\"\n#include \"miscadmin.h\"\n#include \"safe_lib.h\"\n\n#include \"catalog/objectaccess.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_extension.h\"\n#include \"commands/explain.h\"\n#include \"commands/extension.h\"\n#include \"commands/seclabel.h\"\n#include \"common/string.h\"\n#include \"executor/executor.h\"\n#include \"libpq/auth.h\"\n#include \"optimizer/paths.h\"\n#include \"optimizer/plancat.h\"\n#include \"optimizer/planner.h\"\n#include \"port/atomics.h\"\n#include \"postmaster/postmaster.h\"\n#include \"replication/walsender.h\"\n#include \"storage/ipc.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/guc.h\"\n#include \"utils/guc_tables.h\"\n#include \"utils/inval.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n#include \"utils/varlena.h\"\n\n#include \"citus_version.h\"\n\n#include \"columnar/columnar.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/backend_data.h\"\n#include \"distributed/background_jobs.h\"\n#include \"distributed/causal_clock.h\"\n#include \"distributed/citus_depended_object.h\"\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/combine_query_planner.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/cte_inline.h\"\n#include \"distributed/distributed_deadlock_detection.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/intermediate_result_pruning.h\"\n#include \"distributed/local_distributed_join_planner.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/local_multi_copy.h\"\n#include \"distributed/locally_reserved_shared_connections.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/maintenanced.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_explain.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_logical_replication.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/priority.h\"\n#include \"distributed/query_pushdown_planning.h\"\n#include \"distributed/recursive_planning.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/remote_transaction.h\"\n#include \"distributed/repartition_executor.h\"\n#include \"distributed/replication_origin_session_utils.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/run_from_same_connection.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/shard_rebalancer.h\"\n#include \"distributed/shard_transfer.h\"\n#include \"distributed/shardsplit_shared_memory.h\"\n#include \"distributed/shared_connection_stats.h\"\n#include \"distributed/shared_library_init.h\"\n#include \"distributed/stats/query_stats.h\"\n#include \"distributed/stats/stat_counters.h\"\n#include \"distributed/stats/stat_tenants.h\"\n#include \"distributed/subplan_execution.h\"\n#include \"distributed/time_constants.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/transaction_recovery.h\"\n#include \"distributed/utils/directory.h\"\n#include \"distributed/worker_log_messages.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_shard_visibility.h\"\n\n/* marks shared object as one loadable by the postgres version compiled against */\n#if PG_VERSION_NUM >= PG_VERSION_18\nPG_MODULE_MAGIC_EXT(.name = \"citus\", .version = \"15.0devel\");\n#else\nPG_MODULE_MAGIC;\n#endif\n\nColumnarSupportsIndexAM_type extern_ColumnarSupportsIndexAM = NULL;\nCompressionTypeStr_type extern_CompressionTypeStr = NULL;\nIsColumnarTableAmTable_type extern_IsColumnarTableAmTable = NULL;\nReadColumnarOptions_type extern_ReadColumnarOptions = NULL;\n\n/*\n * Define \"pass-through\" functions so that a SQL function defined as one of\n * these symbols in the citus module can use the definition in the columnar\n * module.\n */\n#define DEFINE_COLUMNAR_PASSTHROUGH_FUNC(funcname) \\\n\t\tstatic PGFunction CppConcat(extern_, funcname); \\\n\t\tPG_FUNCTION_INFO_V1(funcname); \\\n\t\tDatum funcname(PG_FUNCTION_ARGS) \\\n\t\t{ \\\n\t\t\treturn CppConcat(extern_, funcname)(fcinfo); \\\n\t\t}\n#define INIT_COLUMNAR_SYMBOL(typename, funcname) \\\n\t\tCppConcat(extern_, funcname) = \\\n\t\t\t(typename) (void *) lookup_external_function(handle, # funcname)\n\n#define CDC_DECODER_DYNAMIC_LIB_PATH \"$libdir/citus_decoders:$libdir\"\n\nDEFINE_COLUMNAR_PASSTHROUGH_FUNC(columnar_handler)\nDEFINE_COLUMNAR_PASSTHROUGH_FUNC(alter_columnar_table_set)\nDEFINE_COLUMNAR_PASSTHROUGH_FUNC(alter_columnar_table_reset)\nDEFINE_COLUMNAR_PASSTHROUGH_FUNC(upgrade_columnar_storage)\nDEFINE_COLUMNAR_PASSTHROUGH_FUNC(downgrade_columnar_storage)\nDEFINE_COLUMNAR_PASSTHROUGH_FUNC(columnar_relation_storageid)\nDEFINE_COLUMNAR_PASSTHROUGH_FUNC(columnar_storage_info)\nDEFINE_COLUMNAR_PASSTHROUGH_FUNC(columnar_store_memory_stats)\nDEFINE_COLUMNAR_PASSTHROUGH_FUNC(test_columnar_storage_write_new_page)\n\n#define DUMMY_REAL_TIME_EXECUTOR_ENUM_VALUE 9999999\nstatic char *CitusVersion = CITUS_VERSION;\nstatic char *DeprecatedEmptyString = \"\";\nstatic char *MitmfifoEmptyString = \"\";\nstatic bool DeprecatedDeferShardDeleteOnMove = true;\nstatic bool DeprecatedDeferShardDeleteOnSplit = true;\nstatic bool DeprecatedReplicateReferenceTablesOnActivate = false;\nstatic bool DeprecatedEnableStatisticsCollection = false;\n\n/* deprecated GUC value that should not be used anywhere outside this file */\nstatic int ReplicationModel = REPLICATION_MODEL_STREAMING;\n\n/* we override the application_name assign_hook and keep a pointer to the old one */\nstatic GucStringAssignHook OldApplicationNameAssignHook = NULL;\n\n/*\n * Flag to indicate when ApplicationNameAssignHook becomes responsible for\n * updating the global pid.\n */\nstatic bool FinishedStartupCitusBackend = false;\n\nstatic object_access_hook_type PrevObjectAccessHook = NULL;\n\nstatic shmem_request_hook_type prev_shmem_request_hook = NULL;\n\nvoid _PG_init(void);\n\nstatic void citus_shmem_request(void);\nstatic void CitusObjectAccessHook(ObjectAccessType access, Oid classId, Oid objectId, int\n\t\t\t\t\t\t\t\t  subId, void *arg);\nstatic void DoInitialCleanup(void);\nstatic void ResizeStackToMaximumDepth(void);\nstatic void multi_log_hook(ErrorData *edata);\nstatic bool IsSequenceOverflowError(ErrorData *edata);\nstatic void RegisterConnectionCleanup(void);\nstatic void RegisterSaveBackendStatsIntoSavedBackendStatsHash(void);\nstatic void RegisterExternalClientBackendCounterDecrement(void);\nstatic void CitusCleanupConnectionsAtExit(int code, Datum arg);\nstatic void SaveBackendStatsIntoSavedBackendStatsHashAtExit(int code, Datum arg);\nstatic void DecrementExternalClientBackendCounterAtExit(int code, Datum arg);\nstatic void CreateRequiredDirectories(void);\nstatic void RegisterCitusConfigVariables(void);\nstatic void OverridePostgresConfigProperties(void);\nstatic bool ErrorIfNotASuitableDeadlockFactor(double *newval, void **extra,\n\t\t\t\t\t\t\t\t\t\t\t  GucSource source);\nstatic bool WarnIfDeprecatedExecutorUsed(int *newval, void **extra, GucSource source);\nstatic bool WarnIfReplicationModelIsSet(int *newval, void **extra, GucSource source);\nstatic bool NoticeIfSubqueryPushdownEnabled(bool *newval, void **extra, GucSource source);\nstatic bool ShowShardsForAppNamePrefixesCheckHook(char **newval, void **extra,\n\t\t\t\t\t\t\t\t\t\t\t\t  GucSource source);\nstatic void ShowShardsForAppNamePrefixesAssignHook(const char *newval, void *extra);\nstatic void ApplicationNameAssignHook(const char *newval, void *extra);\nstatic void CpuPriorityAssignHook(int newval, void *extra);\nstatic bool NodeConninfoGucCheckHook(char **newval, void **extra, GucSource source);\nstatic void NodeConninfoGucAssignHook(const char *newval, void *extra);\nstatic const char * MaxSharedPoolSizeGucShowHook(void);\nstatic const char * LocalPoolSizeGucShowHook(void);\nstatic bool WarnIfLocalExecutionDisabled(bool *newval, void **extra, GucSource source);\nstatic void CitusAuthHook(Port *port, int status);\nstatic bool IsSuperuser(char *userName);\nstatic void AdjustDynamicLibraryPathForCdcDecoders(void);\nstatic void EnableChangeDataCaptureAssignHook(bool newval, void *extra);\n\nstatic ClientAuthentication_hook_type original_client_auth_hook = NULL;\nstatic emit_log_hook_type original_emit_log_hook = NULL;\n\n/* *INDENT-OFF* */\n/* GUC enum definitions */\nstatic const struct config_enum_entry propagate_set_commands_options[] = {\n\t{\"none\", PROPSETCMD_NONE, false},\n\t{\"local\", PROPSETCMD_LOCAL, false},\n\t{NULL, 0, false}\n};\n\n\nstatic const struct config_enum_entry stat_statements_track_options[] = {\n\t{ \"none\", STAT_STATEMENTS_TRACK_NONE, false },\n\t{ \"all\", STAT_STATEMENTS_TRACK_ALL, false },\n\t{ NULL, 0, false }\n};\n\nstatic const struct config_enum_entry stat_tenants_track_options[] = {\n\t{ \"none\", STAT_TENANTS_TRACK_NONE, false },\n\t{ \"all\", STAT_TENANTS_TRACK_ALL, false },\n\t{ NULL, 0, false }\n};\n\nstatic const struct config_enum_entry task_assignment_policy_options[] = {\n\t{ \"greedy\", TASK_ASSIGNMENT_GREEDY, false },\n\t{ \"first-replica\", TASK_ASSIGNMENT_FIRST_REPLICA, false },\n\t{ \"round-robin\", TASK_ASSIGNMENT_ROUND_ROBIN, false },\n\t{ NULL, 0, false }\n};\n\nstatic const struct config_enum_entry replication_model_options[] = {\n\t{ \"statement\", REPLICATION_MODEL_COORDINATOR, false },\n\t{ \"streaming\", REPLICATION_MODEL_STREAMING, false },\n\t{ NULL, 0, false }\n};\n\nstatic const struct config_enum_entry task_executor_type_options[] = {\n\t{ \"adaptive\", MULTI_EXECUTOR_ADAPTIVE, false },\n\t{ \"real-time\", DUMMY_REAL_TIME_EXECUTOR_ENUM_VALUE, false }, /* keep it for backward comp. */\n\t{ \"task-tracker\", MULTI_EXECUTOR_ADAPTIVE, false },\n\t{ NULL, 0, false }\n};\n\nstatic const struct config_enum_entry use_secondary_nodes_options[] = {\n\t{ \"never\", USE_SECONDARY_NODES_NEVER, false },\n\t{ \"always\", USE_SECONDARY_NODES_ALWAYS, false },\n\t{ NULL, 0, false }\n};\n\nstatic const struct config_enum_entry coordinator_aggregation_options[] = {\n\t{ \"disabled\", COORDINATOR_AGGREGATION_DISABLED, false },\n\t{ \"row-gather\", COORDINATOR_AGGREGATION_ROW_GATHER, false },\n\t{ NULL, 0, false }\n};\n\nstatic const struct config_enum_entry log_level_options[] = {\n\t{ \"off\", CITUS_LOG_LEVEL_OFF, false },\n\t{ \"debug5\", DEBUG5, false},\n\t{ \"debug4\", DEBUG4, false},\n\t{ \"debug3\", DEBUG3, false},\n\t{ \"debug2\", DEBUG2, false},\n\t{ \"debug1\", DEBUG1, false},\n\t{ \"debug\", DEBUG2, true},\n\t{ \"log\", LOG, false},\n\t{ \"info\", INFO, true},\n\t{ \"notice\", NOTICE, false},\n\t{ \"warning\", WARNING, false},\n\t{ \"error\", ERROR, false},\n\t{ NULL, 0, false}\n};\n\n\nstatic const struct config_enum_entry local_table_join_policies[] = {\n\t{ \"never\", LOCAL_JOIN_POLICY_NEVER, false},\n\t{ \"prefer-local\", LOCAL_JOIN_POLICY_PREFER_LOCAL, false},\n\t{ \"prefer-distributed\", LOCAL_JOIN_POLICY_PREFER_DISTRIBUTED, false},\n\t{ \"auto\", LOCAL_JOIN_POLICY_AUTO, false},\n\t{ NULL, 0, false}\n};\n\n\nstatic const struct config_enum_entry multi_shard_modify_connection_options[] = {\n\t{ \"parallel\", PARALLEL_CONNECTION, false },\n\t{ \"sequential\", SEQUENTIAL_CONNECTION, false },\n\t{ NULL, 0, false }\n};\n\nstatic const struct config_enum_entry explain_analyze_sort_method_options[] = {\n\t{ \"execution-time\", EXPLAIN_ANALYZE_SORT_BY_TIME, false },\n\t{ \"taskId\", EXPLAIN_ANALYZE_SORT_BY_TASK_ID, false },\n\t{ NULL, 0, false }\n};\n\nstatic const struct config_enum_entry create_object_propagation_options[] = {\n\t{\"deferred\",  CREATE_OBJECT_PROPAGATION_DEFERRED,  false},\n\t{\"automatic\", CREATE_OBJECT_PROPAGATION_AUTOMATIC, false},\n\t{\"immediate\", CREATE_OBJECT_PROPAGATION_IMMEDIATE, false},\n\t{NULL,        0,                                   false}\n};\n\n/*\n * This used to choose CPU priorities for GUCs. For most other integer options\n * we use the -1 value as inherit/default/unset. For CPU priorities this isn't\n * possible, because they can actually have negative values. So we need a value\n * outside of the range that's valid for priorities. But if this is only one\n * more or less than the valid values, this can also be quite confusing for\n * people that don't know the exact range of valid values.\n *\n * So, instead we opt for using an enum that contains all valid priority values\n * as strings, as well as the \"inherit\" string to indicate that the priority\n * value should not be changed.\n */\nstatic const struct config_enum_entry cpu_priority_options[] = {\n\t{ \"inherit\", CPU_PRIORITY_INHERIT, false },\n\t{ \"-20\", -20, false},\n\t{ \"-19\", -19, false},\n\t{ \"-18\", -18, false},\n\t{ \"-17\", -17, false},\n\t{ \"-16\", -16, false},\n\t{ \"-15\", -15, false},\n\t{ \"-14\", -14, false},\n\t{ \"-13\", -13, false},\n\t{ \"-12\", -12, false},\n\t{ \"-11\", -11, false},\n\t{ \"-10\", -10, false},\n\t{ \"-9\", -9, false},\n\t{ \"-8\", -8, false},\n\t{ \"-7\", -7, false},\n\t{ \"-6\", -6, false},\n\t{ \"-5\", -5, false},\n\t{ \"-4\", -4, false},\n\t{ \"-3\", -3, false},\n\t{ \"-2\", -2, false},\n\t{ \"-1\", -1, false},\n\t{ \"0\", 0, false},\n\t{ \"1\", 1, false},\n\t{ \"2\", 2, false},\n\t{ \"3\", 3, false},\n\t{ \"4\", 4, false},\n\t{ \"5\", 5, false},\n\t{ \"6\", 6, false},\n\t{ \"7\", 7, false},\n\t{ \"8\", 8, false},\n\t{ \"9\", 9, false},\n\t{ \"10\", 10, false},\n\t{ \"11\", 11, false},\n\t{ \"12\", 12, false},\n\t{ \"13\", 13, false},\n\t{ \"14\", 14, false},\n\t{ \"15\", 15, false},\n\t{ \"16\", 16, false},\n\t{ \"17\", 17, false},\n\t{ \"18\", 18, false},\n\t{ \"19\", 19, false},\n\t{ NULL, 0, false}\n};\n\nstatic const struct config_enum_entry metadata_sync_mode_options[] = {\n\t{ \"transactional\", METADATA_SYNC_TRANSACTIONAL, false },\n\t{ \"nontransactional\", METADATA_SYNC_NON_TRANSACTIONAL, false },\n\t{ NULL, 0, false }\n};\n\n/* *INDENT-ON* */\n\n\n/*----------------------------------------------------------------------*\n* On PG 18+ the hook signature changed; we wrap the old Citus handler\n* in a fresh function that matches the new typedef exactly.\n*----------------------------------------------------------------------*/\nstatic void\ncitus_executor_run_adapter(QueryDesc *queryDesc,\n\t\t\t\t\t\t   ScanDirection direction,\n\t\t\t\t\t\t   uint64 count\n#if PG_VERSION_NUM < PG_VERSION_18\n\t\t\t\t\t\t   , bool run_once\n#endif\n\t\t\t\t\t\t   )\n{\n\t/* PG18+ has no run_once flag */\n\tCitusExecutorRun(queryDesc,\n\t\t\t\t\t direction,\n\t\t\t\t\t count,\n#if PG_VERSION_NUM >= PG_VERSION_18\n\t\t\t\t\t true\n#else\n\t\t\t\t\t run_once\n#endif\n\t\t\t\t\t );\n}\n\n\n/* shared library initialization function */\nvoid\n_PG_init(void)\n{\n\tif (!process_shared_preload_libraries_in_progress)\n\t{\n\t\tereport(ERROR, (errmsg(\"Citus can only be loaded via shared_preload_libraries\"),\n\t\t\t\t\t\terrhint(\"Add citus to shared_preload_libraries configuration \"\n\t\t\t\t\t\t\t\t\"variable in postgresql.conf in master and workers. Note \"\n\t\t\t\t\t\t\t\t\"that citus should be at the beginning of \"\n\t\t\t\t\t\t\t\t\"shared_preload_libraries.\")));\n\t}\n\n\t/*\n\t * Register contstraint_handler hooks of safestringlib first. This way\n\t * loading the extension will error out if one of these constraints are hit\n\t * during load.\n\t */\n\tset_str_constraint_handler_s(ereport_constraint_handler);\n\tset_mem_constraint_handler_s(ereport_constraint_handler);\n\n\t/*\n\t * Perform checks before registering any hooks, to avoid erroring out in a\n\t * partial state.\n\t *\n\t * In many cases (e.g. planner and utility hook, to run inside\n\t * pg_stat_statements et. al.) we have to be loaded before other hooks\n\t * (thus as the innermost/last running hook) to be able to do our\n\t * duties. For simplicity insist that all hooks are previously unused.\n\t */\n\tif (planner_hook != NULL || ProcessUtility_hook != NULL ||\n\t\tExecutorStart_hook != NULL || ExecutorRun_hook != NULL ||\n\t\tExplainOneQuery_hook != NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"Citus has to be loaded first\"),\n\t\t\t\t\t\terrhint(\"Place citus at the beginning of \"\n\t\t\t\t\t\t\t\t\"shared_preload_libraries.\")));\n\t}\n\n\tResizeStackToMaximumDepth();\n\n\t/*\n\t * Extend the database directory structure before continuing with\n\t * initialization - one of the later steps might require them to exist.\n\t * If in a sub-process (windows / EXEC_BACKEND) this already has been\n\t * done.\n\t */\n\tif (!IsUnderPostmaster)\n\t{\n\t\tCreateRequiredDirectories();\n\t}\n\n\tInitConnParams();\n\n\t/*\n\t * Register Citus configuration variables. Do so before intercepting\n\t * hooks or calling initialization functions, in case we want to do the\n\t * latter in a configuration dependent manner.\n\t */\n\tRegisterCitusConfigVariables();\n\n\t/* make our additional node types known */\n\tRegisterNodes();\n\n\t/* make our custom scan nodes known */\n\tRegisterCitusCustomScanMethods();\n\n\t/* intercept planner */\n\tplanner_hook = distributed_planner;\n\n\t/* register for planner hook */\n\tset_rel_pathlist_hook = multi_relation_restriction_hook;\n\tget_relation_info_hook = multi_get_relation_info_hook;\n\tset_join_pathlist_hook = multi_join_restriction_hook;\n\tExecutorStart_hook = CitusExecutorStart;\n\tExecutorRun_hook = citus_executor_run_adapter;\n\tExplainOneQuery_hook = CitusExplainOneQuery;\n\tprev_ExecutorEnd = ExecutorEnd_hook;\n\tExecutorEnd_hook = CitusAttributeToEnd;\n\n\t/* register hook for error messages */\n\toriginal_emit_log_hook = emit_log_hook;\n\temit_log_hook = multi_log_hook;\n\n\n\t/*\n\t * Register hook for counting client backends that\n\t * are successfully authenticated.\n\t */\n\toriginal_client_auth_hook = ClientAuthentication_hook;\n\tClientAuthentication_hook = CitusAuthHook;\n\n\tprev_shmem_request_hook = shmem_request_hook;\n\tshmem_request_hook = citus_shmem_request;\n\n\tInitializeMaintenanceDaemon();\n\tInitializeMaintenanceDaemonForMainDb();\n\n\t/* initialize coordinated transaction management */\n\tInitializeTransactionManagement();\n\tInitializeBackendManagement();\n\tInitializeConnectionManagement();\n\tInitPlacementConnectionManagement();\n\tInitRelationAccessHash();\n\tInitializeCitusQueryStats();\n\tInitializeSharedConnectionStats();\n\tInitializeLocallyReservedSharedConnections();\n\tInitializeClusterClockMem();\n\n\t/*\n\t * Adjust the Dynamic Library Path to prepend citus_decodes to the dynamic\n\t * library path. This is needed to make sure that the citus decoders are\n\t * loaded before the default decoders for CDC.\n\t */\n\tif (EnableChangeDataCapture)\n\t{\n\t\tAdjustDynamicLibraryPathForCdcDecoders();\n\t}\n\n\n\t/* initialize shard split shared memory handle management */\n\tInitializeShardSplitSMHandleManagement();\n\n\tInitializeMultiTenantMonitorSMHandleManagement();\n\tInitializeStatCountersShmem();\n\n\n\t/* enable modification of pg_catalog tables during pg_upgrade */\n\tif (IsBinaryUpgrade)\n\t{\n\t\tSetConfigOption(\"allow_system_table_mods\", \"true\", PGC_POSTMASTER,\n\t\t\t\t\t\tPGC_S_OVERRIDE);\n\t}\n\n\t/*\n\t * In postmasters execution of _PG_init, IsUnderPostmaster will be false and\n\t * we want to do the cleanup at that time only, otherwise there is a chance that\n\t * there will be parallel queries and we might do a cleanup for things that are\n\t * already in use. This is only needed in Windows.\n\t */\n\tif (!IsUnderPostmaster)\n\t{\n\t\tDoInitialCleanup();\n\t}\n\n\tPrevObjectAccessHook = object_access_hook;\n\tobject_access_hook = CitusObjectAccessHook;\n\n\n\t/* ensure columnar module is loaded at the right time */\n\tload_file(COLUMNAR_MODULE_NAME, false);\n\n\t/*\n\t * Register utility hook. This must be done after loading columnar, so\n\t * that the citus hook is called first, followed by the columnar hook,\n\t * followed by standard_ProcessUtility. That allows citus to distribute\n\t * ALTER TABLE commands before columnar strips out the columnar-specific\n\t * options.\n\t */\n\tPrevProcessUtility = (ProcessUtility_hook != NULL) ?\n\t\t\t\t\t\t ProcessUtility_hook : standard_ProcessUtility;\n\tProcessUtility_hook = citus_ProcessUtility;\n\n\t/*\n\t * Acquire symbols for columnar functions that citus calls.\n\t */\n\tvoid *handle = NULL;\n\n\t/* use load_external_function() the first time to initialize the handle */\n\textern_ColumnarSupportsIndexAM = (ColumnarSupportsIndexAM_type) (void *)\n\t\t\t\t\t\t\t\t\t load_external_function(COLUMNAR_MODULE_NAME,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"ColumnarSupportsIndexAM\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttrue, &handle);\n\n\tCacheRegisterRelcacheCallback(InvalidateDistRelationCacheCallback,\n\t\t\t\t\t\t\t\t  (Datum) 0);\n\n\tINIT_COLUMNAR_SYMBOL(CompressionTypeStr_type, CompressionTypeStr);\n\tINIT_COLUMNAR_SYMBOL(IsColumnarTableAmTable_type, IsColumnarTableAmTable);\n\tINIT_COLUMNAR_SYMBOL(ReadColumnarOptions_type, ReadColumnarOptions);\n\n\t/* initialize symbols for \"pass-through\" functions */\n\tINIT_COLUMNAR_SYMBOL(PGFunction, columnar_handler);\n\tINIT_COLUMNAR_SYMBOL(PGFunction, alter_columnar_table_set);\n\tINIT_COLUMNAR_SYMBOL(PGFunction, alter_columnar_table_reset);\n\tINIT_COLUMNAR_SYMBOL(PGFunction, upgrade_columnar_storage);\n\tINIT_COLUMNAR_SYMBOL(PGFunction, downgrade_columnar_storage);\n\tINIT_COLUMNAR_SYMBOL(PGFunction, columnar_relation_storageid);\n\tINIT_COLUMNAR_SYMBOL(PGFunction, columnar_storage_info);\n\tINIT_COLUMNAR_SYMBOL(PGFunction, columnar_store_memory_stats);\n\tINIT_COLUMNAR_SYMBOL(PGFunction, test_columnar_storage_write_new_page);\n\n\t/*\n\t * This part is only for SECURITY LABEL tests\n\t * mimicking what an actual security label provider would do\n\t */\n\tif (RunningUnderCitusTestSuite)\n\t{\n\t\tregister_label_provider(\"citus '!tests_label_provider\",\n\t\t\t\t\t\t\t\tcitus_test_object_relabel);\n\t}\n}\n\n\n/*\n * PrependCitusDecodersToDynamicLibrayPath prepends the $libdir/citus_decoders\n * to the dynamic library path. This is needed to make sure that the citus\n * decoders are loaded before the default decoders for CDC.\n */\nstatic void\nAdjustDynamicLibraryPathForCdcDecoders(void)\n{\n\tif (strcmp(Dynamic_library_path, \"$libdir\") == 0)\n\t{\n\t\tSetConfigOption(\"dynamic_library_path\", CDC_DECODER_DYNAMIC_LIB_PATH,\n\t\t\t\t\t\tPGC_POSTMASTER, PGC_S_OVERRIDE);\n\t}\n}\n\n\n/*\n * Requests any additional shared memory required for citus.\n */\nstatic void\ncitus_shmem_request(void)\n{\n\tif (prev_shmem_request_hook)\n\t{\n\t\tprev_shmem_request_hook();\n\t}\n\n\tRequestAddinShmemSpace(BackendManagementShmemSize());\n\tRequestAddinShmemSpace(SharedConnectionStatsShmemSize());\n\tRequestAddinShmemSpace(MaintenanceDaemonShmemSize());\n\tRequestAddinShmemSpace(CitusQueryStatsSharedMemSize());\n\tRequestAddinShmemSpace(LogicalClockShmemSize());\n\tRequestNamedLWLockTranche(STATS_SHARED_MEM_NAME, 1);\n\tRequestAddinShmemSpace(StatCountersShmemSize());\n\tRequestNamedLWLockTranche(SAVED_BACKEND_STATS_HASH_LOCK_TRANCHE_NAME, 1);\n}\n\n\n/*\n * DoInitialCleanup does cleanup at start time.\n * Currently it:\n * - Removes intermediate result directories ( in case there are any leftovers)\n */\nstatic void\nDoInitialCleanup(void)\n{\n\tCleanupJobCacheDirectory();\n}\n\n\n/*\n * Stack size increase during high memory load may cause unexpected crashes.\n * With this alloca call, we are increasing stack size explicitly, so that if\n * it is not possible to increase stack size, we will get an OOM error instead\n * of a crash.\n *\n * This function is called on backend startup. The allocated memory will\n * automatically be released at the end of the function's scope. However, we'd\n * have already expanded the stack and it wouldn't shrink back. So, in a sense,\n * per backend we're securing max_stack_depth kB's of memory on the stack upfront.\n *\n * Not all the backends require max_stack_depth kB's on the stack, so we might end\n * up with unnecessary allocations. However, the default value is 2MB, which seems\n * an acceptable trade-off. Also, allocating memory upfront may perform better\n * under some circumstances.\n */\nstatic void\nResizeStackToMaximumDepth(void)\n{\n#ifndef WIN32\n\tlong max_stack_depth_bytes = max_stack_depth * 1024L;\n\n\t/*\n\t * Explanation of IGNORE-BANNED:\n\t * alloca is safe to use here since we limit the allocated size. We cannot\n\t * use malloc as a replacement, since we actually want to grow the stack\n\t * here.\n\t */\n\tvolatile char *stack_resizer = alloca(max_stack_depth_bytes); /* IGNORE-BANNED */\n\n\t/*\n\t * Different architectures might have different directions while\n\t * growing the stack. So, touch both ends.\n\t */\n\tstack_resizer[0] = 0;\n\tstack_resizer[max_stack_depth_bytes - 1] = 0;\n\n\t/*\n\t * Passing the address to external function also prevents the function\n\t * from being optimized away, and the debug elog can also help with\n\t * diagnosis if needed.\n\t */\n\telog(DEBUG5, \"entry stack is at %p, increased to %p, the top and bottom values of \"\n\t\t\t\t \"the stack is %d and %d\", &stack_resizer[0],\n\t\t &stack_resizer[max_stack_depth_bytes - 1],\n\t\t stack_resizer[max_stack_depth_bytes - 1], stack_resizer[0]);\n\n#endif\n}\n\n\n/*\n * multi_log_hook intercepts postgres log commands. We use this to override\n * postgres error messages when they're not specific enough for the users.\n */\nstatic void\nmulti_log_hook(ErrorData *edata)\n{\n\t/*\n\t * Show the user a meaningful error message when a backend is cancelled\n\t * by the distributed deadlock detection. Also reset the state for this,\n\t * since the next cancelation of the backend might have another reason.\n\t *\n\t * We also want to provide a useful hint for sequence overflow errors\n\t * because they're likely to be caused by the way Citus handles smallint/int\n\t * based sequences on worker nodes. Note that we add the hint without checking\n\t * whether we're on a worker node or the sequence was used on a distributed\n\t * table because catalog might not be available at this point. And given\n\t * that this hint might be shown for regular Postgres tables too, we inject\n\t * the hint only when EnableUnsupportedFeatureMessages is set to true.\n\t * Otherwise, vanilla tests would fail.\n\t */\n\tbool clearState = true;\n\tif (edata->elevel == ERROR && edata->sqlerrcode == ERRCODE_QUERY_CANCELED &&\n\t\tMyBackendGotCancelledDueToDeadlock(clearState))\n\t{\n\t\tedata->sqlerrcode = ERRCODE_T_R_DEADLOCK_DETECTED;\n\n\t\t/*\n\t\t * This hook is called by EmitErrorReport() when emitting the ereport\n\t\t * either to frontend or to the server logs. And some callers of\n\t\t * EmitErrorReport() (e.g.: errfinish()) seems to assume that string\n\t\t * fields of given ErrorData object needs to be freed. For this reason,\n\t\t * we copy the message into heap here.\n\t\t */\n\t\tedata->message = pstrdup(\"canceling the transaction since it was \"\n\t\t\t\t\t\t\t\t \"involved in a distributed deadlock\");\n\t}\n\telse if (EnableUnsupportedFeatureMessages &&\n\t\t\t IsSequenceOverflowError(edata))\n\t{\n\t\tedata->detail = pstrdup(\"nextval(sequence) calls in worker nodes \"\n\t\t\t\t\t\t\t\t\"are not supported for column defaults of \"\n\t\t\t\t\t\t\t\t\"type int or smallint\");\n\t\tedata->hint = pstrdup(\"If the command was issued from a worker node, \"\n\t\t\t\t\t\t\t  \"try issuing it from the coordinator node \"\n\t\t\t\t\t\t\t  \"instead.\");\n\t}\n\n\tif (original_emit_log_hook)\n\t{\n\t\toriginal_emit_log_hook(edata);\n\t}\n}\n\n\n/*\n * IsSequenceOverflowError returns true if the given error is a sequence\n * overflow error.\n */\nstatic bool\nIsSequenceOverflowError(ErrorData *edata)\n{\n\tstatic const char *sequenceOverflowedMsgPrefix =\n\t\t\"nextval: reached maximum value of sequence\";\n\tstatic const int sequenceOverflowedMsgPrefixLen = 42;\n\n\treturn edata->elevel == ERROR &&\n\t\t   edata->sqlerrcode == ERRCODE_SEQUENCE_GENERATOR_LIMIT_EXCEEDED &&\n\t\t   edata->message != NULL &&\n\t\t   strncmp(edata->message, sequenceOverflowedMsgPrefix,\n\t\t\t\t   sequenceOverflowedMsgPrefixLen) == 0;\n}\n\n\n/*\n * StartupCitusBackend initializes per-backend infrastructure, and is called\n * the first time citus is used in a database.\n *\n * NB: All code here has to be able to cope with this routine being called\n * multiple times in the same backend.  This will e.g. happen when the\n * extension is created, upgraded or dropped. Due to the way we detect the\n * extension being dropped this can also happen when autovacuum runs ANALYZE on\n * pg_dist_partition, see InvalidateDistRelationCacheCallback for details.\n */\nvoid\nStartupCitusBackend(void)\n{\n\tInitializeMaintenanceDaemonBackend();\n\n\t/*\n\t * For query backends this will be a no-op, because InitializeBackendData\n\t * is already called from the CitusAuthHook. But for background workers we\n\t * still need to initialize the backend data.\n\t */\n\tInitializeBackendData(application_name);\n\n\t/*\n\t * If this is an external connection or a background workers this will\n\t * generate the global PID for this connection. For internal connections\n\t * this is a no-op, since InitializeBackendData will already have extracted\n\t * the gpid from the application_name.\n\t */\n\tAssignGlobalPID(application_name);\n\n\tSetBackendDataDatabaseId();\n\tRegisterConnectionCleanup();\n\tRegisterSaveBackendStatsIntoSavedBackendStatsHash();\n\n\tFinishedStartupCitusBackend = true;\n}\n\n\n/*\n * GetCurrentClientMinMessageLevelName returns the name of the\n * the GUC client_min_messages for its specified value.\n */\nconst char *\nGetClientMinMessageLevelNameForValue(int minMessageLevel)\n{\n\tstruct config_enum record = { 0 };\n\trecord.options = log_level_options;\n\tconst char *clientMinMessageLevelName = config_enum_lookup_by_value(&record,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tminMessageLevel);\n\treturn clientMinMessageLevelName;\n}\n\n\n/*\n * RegisterConnectionCleanup cleans up any resources left at the end of the\n * session. We prefer to cleanup before shared memory exit to make sure that\n * this session properly releases anything hold in the shared memory.\n */\nstatic void\nRegisterConnectionCleanup(void)\n{\n\tstatic bool registeredCleanup = false;\n\tif (registeredCleanup == false)\n\t{\n\t\tbefore_shmem_exit(CitusCleanupConnectionsAtExit, 0);\n\n\t\tregisteredCleanup = true;\n\t}\n}\n\n\n/*\n * RegisterSaveBackendStatsIntoSavedBackendStatsHash registers the function\n * that saves the backend stats for the exited backends into the saved backend\n * stats hash.\n */\nstatic void\nRegisterSaveBackendStatsIntoSavedBackendStatsHash(void)\n{\n\tstatic bool registeredSaveBackendStats = false;\n\tif (registeredSaveBackendStats == false)\n\t{\n\t\tbefore_shmem_exit(SaveBackendStatsIntoSavedBackendStatsHashAtExit, 0);\n\n\t\tregisteredSaveBackendStats = true;\n\t}\n}\n\n\n/*\n * RegisterExternalClientBackendCounterDecrement is called when the backend terminates.\n * For all client backends, we register a callback that will undo\n */\nstatic void\nRegisterExternalClientBackendCounterDecrement(void)\n{\n\tstatic bool registeredCleanup = false;\n\tif (registeredCleanup == false)\n\t{\n\t\tbefore_shmem_exit(DecrementExternalClientBackendCounterAtExit, 0);\n\n\t\tregisteredCleanup = true;\n\t}\n}\n\n\n/*\n * CitusCleanupConnectionsAtExit is called before_shmem_exit() of the\n * backend for the purposes of any clean-up needed.\n */\nstatic void\nCitusCleanupConnectionsAtExit(int code, Datum arg)\n{\n\t/* properly close all the cached connections */\n\tShutdownAllConnections();\n\n\t/*\n\t * Make sure that we give the shared connections back to the shared\n\t * pool if any. This operation is a no-op if the reserved connections\n\t * are already given away.\n\t */\n\tDeallocateReservedConnections();\n\n\t/* we don't want any monitoring view/udf to show already exited backends */\n\tSetActiveMyBackend(false);\n\tUnSetGlobalPID();\n}\n\n\n/*\n * SaveBackendStatsIntoSavedBackendStatsHashAtExit is called before_shmem_exit()\n * of the backend for the purposes of saving the backend stats for the exited\n * backends into the saved backend stats hash.\n */\nstatic void\nSaveBackendStatsIntoSavedBackendStatsHashAtExit(int code, Datum arg)\n{\n\tif (code)\n\t{\n\t\t/* don't try to save the stats during a crash */\n\t\treturn;\n\t}\n\n\tSaveBackendStatsIntoSavedBackendStatsHash();\n}\n\n\n/*\n * DecrementExternalClientBackendCounterAtExit is called before_shmem_exit() of the\n * backend for the purposes decrementing\n */\nstatic void\nDecrementExternalClientBackendCounterAtExit(int code, Datum arg)\n{\n\tDecrementExternalClientBackendCounter();\n}\n\n\n/*\n * CreateRequiredDirectories - Create directories required for Citus to\n * function.\n *\n * These used to be created by initdb, but that's not possible anymore.\n */\nstatic void\nCreateRequiredDirectories(void)\n{\n\tconst char *subdir = (\"base/\" PG_JOB_CACHE_DIR);\n\n\tif (MakePGDirectory(subdir) != 0 && errno != EEXIST)\n\t{\n\t\tereport(ERROR, (errcode_for_file_access(),\n\t\t\t\t\t\terrmsg(\"could not create directory \\\"%s\\\": %m\",\n\t\t\t\t\t\t\t   subdir)));\n\t}\n}\n\n\n/* Register Citus configuration variables. */\nstatic void\nRegisterCitusConfigVariables(void)\n{\n\tDefineCustomBoolVariable(\n\t\t\"citus.all_modifications_commutative\",\n\t\tgettext_noop(\"Bypasses commutativity checks when enabled\"),\n\t\tNULL,\n\t\t&AllModificationsCommutative,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.allow_aggregate_worker_combine_on_internal_types\",\n\t\tgettext_noop(\"Enables aggregate worker partial aggregates on aggregates that \"\n\t\t\t\t\t \"have internal type for the aggregate partial state storage.\"),\n\t\tgettext_noop(\n\t\t\t\"This setting allows the use of pushdown of custom aggregates that have \"\n\t\t\t\"an STYPE that is internal. This is typically okay to do, but if a custom aggregate \"\n\t\t\t\"persists OID information or any node specific data into the state, this can cause \"\n\t\t\t\"weirdness when combining in the coordinator, so this is left as an option to turn off \"\n\t\t\t\"in those cases worker combine functions on internal types.\"),\n\t\t&AllowAggregateWorkerCombineOnInternalTypes,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.allow_modifications_from_workers_to_replicated_tables\",\n\t\tgettext_noop(\"Enables modifications from workers to replicated \"\n\t\t\t\t\t \"tables such as reference tables or hash \"\n\t\t\t\t\t \"distributed tables with replication factor \"\n\t\t\t\t\t \"greater than 1.\"),\n\t\tgettext_noop(\"Allowing modifications from the worker nodes \"\n\t\t\t\t\t \"requires extra locking which might decrease \"\n\t\t\t\t\t \"the throughput. Disabling this GUC skips the \"\n\t\t\t\t\t \"extra locking and prevents modifications from \"\n\t\t\t\t\t \"worker nodes.\"),\n\t\t&AllowModificationsFromWorkersToReplicatedTables,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.allow_nested_distributed_execution\",\n\t\tgettext_noop(\"Enables distributed execution within a task \"\n\t\t\t\t\t \"of another distributed execution.\"),\n\t\tgettext_noop(\"Nested distributed execution can happen when Citus \"\n\t\t\t\t\t \"pushes down a call to a user-defined function within \"\n\t\t\t\t\t \"a distributed query, and the function contains another \"\n\t\t\t\t\t \"distributed query. In this scenario, Citus makes no \"\n\t\t\t\t\t \"guarantess with regards to correctness and it is therefore \"\n\t\t\t\t\t \"disallowed by default. This setting can be used to allow \"\n\t\t\t\t\t \"nested distributed execution.\"),\n\t\t&AllowNestedDistributedExecution,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.allow_unsafe_constraints\",\n\t\tgettext_noop(\"Enables unique constraints and exclusion constraints \"\n\t\t\t\t\t \"that do not include a distribution column.\"),\n\t\tgettext_noop(\"To enforce global uniqueness, Citus normally requires \"\n\t\t\t\t\t \"that unique constraints and exclusion constraints contain \"\n\t\t\t\t\t \"the distribution column. If the tuple does not include the \"\n\t\t\t\t\t \"distribution column, Citus cannot ensure that the same value \"\n\t\t\t\t\t \"is not present in another shard. However, in some cases the \"\n\t\t\t\t\t \"index creator knows that uniqueness within the shard implies \"\n\t\t\t\t\t \"global uniqueness (e.g. when indexing an expression derived \"\n\t\t\t\t\t \"from the distribution column) and adding the distribution column \"\n\t\t\t\t\t \"separately may not be desirable. This setting can then be used \"\n\t\t\t\t\t \"to disable the check.\"),\n\t\t&AllowUnsafeConstraints,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.allow_unsafe_locks_from_workers\",\n\t\tgettext_noop(\"Enables acquiring a distributed lock from a worker \"\n\t\t\t\t\t \"when the coordinator is not in the metadata\"),\n\t\tgettext_noop(\"Set to false by default. If set to true, enables \"\n\t\t\t\t\t \"acquiring a distributed lock from a worker \"\n\t\t\t\t\t \"when the coordinator is not in the metadata. \"\n\t\t\t\t\t \"This type of lock is unsafe because the worker will not be \"\n\t\t\t\t\t \"able to lock the coordinator; the coordinator will be able to \"\n\t\t\t\t\t \"intialize distributed operations on the resources locked \"\n\t\t\t\t\t \"by the worker. This can lead to concurrent operations from the \"\n\t\t\t\t\t \"coordinator and distributed deadlocks since the coordinator \"\n\t\t\t\t\t \"and the workers would not acquire locks across the same nodes \"\n\t\t\t\t\t \"in the same order.\"),\n\t\t&EnableAcquiringUnsafeLockFromWorkers,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.background_task_queue_interval\",\n\t\tgettext_noop(\"Time to wait between checks for scheduled background tasks.\"),\n\t\tNULL,\n\t\t&BackgroundTaskQueueCheckInterval,\n\t\t5000, -1, 7 * 24 * 3600 * 1000,\n\t\tPGC_SIGHUP,\n\t\tGUC_UNIT_MS,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.check_available_space_before_move\",\n\t\tgettext_noop(\"When enabled will check free disk space before a shard move\"),\n\t\tgettext_noop(\n\t\t\t\"Free disk space will be checked when this setting is enabled before each shard move.\"),\n\t\t&CheckAvailableSpaceBeforeMove,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomStringVariable(\n\t\t\"citus.cluster_name\",\n\t\tgettext_noop(\"Which cluster this node is a part of\"),\n\t\tNULL,\n\t\t&CurrentCluster,\n\t\t\"default\",\n\t\tPGC_SU_BACKEND,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.coordinator_aggregation_strategy\",\n\t\tgettext_noop(\"Sets the strategy for when an aggregate cannot be pushed down. \"\n\t\t\t\t\t \"'row-gather' will pull up intermediate rows to the coordinator, \"\n\t\t\t\t\t \"while 'disabled' will error if coordinator aggregation is necessary\"),\n\t\tNULL,\n\t\t&CoordinatorAggregationStrategy,\n\t\tCOORDINATOR_AGGREGATION_ROW_GATHER,\n\t\tcoordinator_aggregation_options,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.copy_switchover_threshold\",\n\t\tgettext_noop(\"Sets the threshold for copy to be switched \"\n\t\t\t\t\t \"over per connection.\"),\n\t\tgettext_noop(\"Data size threshold to switch over the active placement for \"\n\t\t\t\t\t \"a connection. If this is too low, overhead of starting COPY \"\n\t\t\t\t\t \"commands will hurt the performance. If this is too high, \"\n\t\t\t\t\t \"buffered data will use lots of memory. 4MB is a good balance \"\n\t\t\t\t\t \"between memory usage and performance. Note that this is irrelevant \"\n\t\t\t\t\t \"in the common case where we open one connection per placement.\"),\n\t\t&CopySwitchOverThresholdBytes,\n\t\t4 * 1024 * 1024, 1, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_UNIT_BYTE | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomRealVariable(\n\t\t\"citus.count_distinct_error_rate\",\n\t\tgettext_noop(\"Desired error rate when calculating count(distinct) \"\n\t\t\t\t\t \"approximates using the postgresql-hll extension. \"\n\t\t\t\t\t \"0.0 disables approximations for count(distinct); 1.0 \"\n\t\t\t\t\t \"provides no guarantees about the accuracy of results.\"),\n\t\tNULL,\n\t\t&CountDistinctErrorRate,\n\t\t0.0, 0.0, 1.0,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\t/*\n\t * This doesn't use cpu_priority_options on purpose, because we always need\n\t * to know the actual priority value so that `RESET citus.cpu_priority`\n\t * actually changes the priority back.\n\t */\n\tDefineCustomIntVariable(\n\t\t\"citus.cpu_priority\",\n\t\tgettext_noop(\"Sets the CPU priority of the current backend.\"),\n\t\tgettext_noop(\"Lower numbers cause more favorable scheduling, so the \"\n\t\t\t\t\t \"queries that this backend runs will be able to use more \"\n\t\t\t\t\t \"CPU resources compared to queries from other backends. \"\n\t\t\t\t\t \"WARNING: Changing this setting can lead to a pnemomenom \"\n\t\t\t\t\t \"called 'priority inversion', due to locks being held \"\n\t\t\t\t\t \"between different backends. This means that processes \"\n\t\t\t\t\t \"might be scheduled in the exact oposite way of what you \"\n\t\t\t\t\t \"want, i.e. processes that you want scheduled a lot, are \"\n\t\t\t\t\t \"scheduled very little. So use this setting at your own \"\n\t\t\t\t\t \"risk.\"),\n\t\t&CpuPriority,\n\t\tGetOwnPriority(), -20, 19,\n\t\tPGC_SUSET,\n\t\tGUC_STANDARD,\n\t\tNULL, CpuPriorityAssignHook, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.cpu_priority_for_logical_replication_senders\",\n\t\tgettext_noop(\"Sets the CPU priority for backends that send logical \"\n\t\t\t\t\t \"replication changes to other nodes for online shard \"\n\t\t\t\t\t \"moves and splits.\"),\n\t\tgettext_noop(\"Lower numbers cause more favorable scheduling, so the \"\n\t\t\t\t\t \"backends used to do the shard move will get more CPU \"\n\t\t\t\t\t \"resources. 'inherit' is a special value and disables \"\n\t\t\t\t\t \"overriding the CPU priority for backends that send \"\n\t\t\t\t\t \"logical replication changes.\"),\n\t\t&CpuPriorityLogicalRepSender,\n\t\tCPU_PRIORITY_INHERIT, cpu_priority_options,\n\t\tPGC_SUSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.create_object_propagation\",\n\t\tgettext_noop(\"Controls the behavior of CREATE statements in transactions for \"\n\t\t\t\t\t \"supported objects\"),\n\t\tgettext_noop(\"When creating new objects in transactions this setting is used to \"\n\t\t\t\t\t \"determine the behavior for propagating. When objects are created \"\n\t\t\t\t\t \"in a multi-statement transaction block Citus needs to switch to \"\n\t\t\t\t\t \"sequential mode (if not already) to make sure the objects are \"\n\t\t\t\t\t \"visible to later statements on shards. The switch to sequential is \"\n\t\t\t\t\t \"not always desired. By changing this behavior the user can trade \"\n\t\t\t\t\t \"off performance for full transactional consistency on the creation \"\n\t\t\t\t\t \"of new objects.\"),\n\t\t&CreateObjectPropagationMode,\n\t\tCREATE_OBJECT_PROPAGATION_IMMEDIATE, create_object_propagation_options,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.defer_drop_after_shard_move\",\n\t\tgettext_noop(\"Deprecated, Citus always defers drop after shard move\"),\n\t\tNULL,\n\t\t&DeprecatedDeferShardDeleteOnMove,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\t0,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.defer_drop_after_shard_split\",\n\t\tgettext_noop(\"Deprecated, Citus always defers drop after shard split\"),\n\t\tNULL,\n\t\t&DeprecatedDeferShardDeleteOnSplit,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\t0,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.defer_shard_delete_interval\",\n\t\tgettext_noop(\"Sets the time to wait between background deletion for shards.\"),\n\t\tgettext_noop(\"Shards that are marked for deferred deletion need to be deleted in \"\n\t\t\t\t\t \"the background at a later time. This is done at a regular interval \"\n\t\t\t\t\t \"configured here. The deletion is executed optimistically, it tries \"\n\t\t\t\t\t \"to take a lock on a shard to clean, if the lock can't be acquired \"\n\t\t\t\t\t \"the background worker moves on. When set to -1 this background \"\n\t\t\t\t\t \"process is skipped.\"),\n\t\t&DeferShardDeleteInterval,\n\t\t15000, -1, 7 * 24 * 3600 * 1000,\n\t\tPGC_SIGHUP,\n\t\tGUC_UNIT_MS,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomRealVariable(\n\t\t\"citus.desired_percent_disk_available_after_move\",\n\t\tgettext_noop(\n\t\t\t\"Sets how many percentage of free disk space should be after a shard move\"),\n\t\tgettext_noop(\n\t\t\t\"This setting controls how much free space should be available after a shard move. \"\n\t\t\t\"If the free disk space will be lower than this parameter, then shard move will result in \"\n\t\t\t\"an error.\"),\n\t\t&DesiredPercentFreeAfterMove,\n\t\t10.0, 0.0, 100.0,\n\t\tPGC_SIGHUP,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomRealVariable(\n\t\t\"citus.distributed_deadlock_detection_factor\",\n\t\tgettext_noop(\"Sets the time to wait before checking for distributed \"\n\t\t\t\t\t \"deadlocks. Postgres' deadlock_timeout setting is \"\n\t\t\t\t\t \"multiplied with the value. If the value is set to -1, \"\n\t\t\t\t\t \"distributed deadlock detection is disabled.\"),\n\t\tNULL,\n\t\t&DistributedDeadlockDetectionTimeoutFactor,\n\t\t2.0, -1.0, 1000.0,\n\t\tPGC_SIGHUP,\n\t\tGUC_STANDARD,\n\t\tErrorIfNotASuitableDeadlockFactor, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_alter_database_owner\",\n\t\tgettext_noop(\"Enables propagating ALTER DATABASE ... OWNER TO ... statements to \"\n\t\t\t\t\t \"workers\"),\n\t\tNULL,\n\t\t&EnableAlterDatabaseOwner,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_alter_role_propagation\",\n\t\tgettext_noop(\"Enables propagating ALTER ROLE statements to workers (excluding \"\n\t\t\t\t\t \"ALTER ROLE SET)\"),\n\t\tNULL,\n\t\t&EnableAlterRolePropagation,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_alter_role_set_propagation\",\n\t\tgettext_noop(\"Enables propagating ALTER ROLE SET statements to workers\"),\n\t\tNULL,\n\t\t&EnableAlterRoleSetPropagation,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_binary_protocol\",\n\t\tgettext_noop(\n\t\t\t\"Enables communication between nodes using binary protocol when possible\"),\n\t\tNULL,\n\t\t&EnableBinaryProtocol,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_change_data_capture\",\n\t\tgettext_noop(\"Enables using replication origin tracking for change data capture\"),\n\t\tNULL,\n\t\t&EnableChangeDataCapture,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, EnableChangeDataCaptureAssignHook, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_cluster_clock\",\n\t\tgettext_noop(\"When users explicitly call UDF citus_get_transaction_clock() \"\n\t\t\t\t\t \"and the flag is true, it returns the maximum \"\n\t\t\t\t\t \"clock among all nodes. All nodes move to the \"\n\t\t\t\t\t \"new clock. If clocks go bad for any reason, \"\n\t\t\t\t\t \"this serves as a safety valve.\"),\n\t\tNULL,\n\t\t&EnableClusterClock,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_cost_based_connection_establishment\",\n\t\tgettext_noop(\"When enabled the connection establishment times \"\n\t\t\t\t\t \"and task execution times into account for deciding \"\n\t\t\t\t\t \"whether or not to establish new connections.\"),\n\t\tNULL,\n\t\t&EnableCostBasedConnectionEstablishment,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_create_database_propagation\",\n\t\tgettext_noop(\"Enables propagating CREATE DATABASE \"\n\t\t\t\t\t \"and DROP DATABASE statements to workers.\"),\n\t\tNULL,\n\t\t&EnableCreateDatabasePropagation,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_create_role_propagation\",\n\t\tgettext_noop(\"Enables propagating CREATE ROLE \"\n\t\t\t\t\t \"and DROP ROLE statements to workers\"),\n\t\tNULL,\n\t\t&EnableCreateRolePropagation,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_create_type_propagation\",\n\t\tgettext_noop(\"Enables propagating of CREATE TYPE statements to workers\"),\n\t\tNULL,\n\t\t&EnableCreateTypePropagation,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_ddl_propagation\",\n\t\tgettext_noop(\"Enables propagating DDL statements to worker shards\"),\n\t\tNULL,\n\t\t&EnableDDLPropagation,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_deadlock_prevention\",\n\t\tgettext_noop(\"Avoids deadlocks by preventing concurrent multi-shard commands\"),\n\t\tgettext_noop(\"Multi-shard modifications such as UPDATE, DELETE, and \"\n\t\t\t\t\t \"INSERT...SELECT are typically executed in parallel. If multiple \"\n\t\t\t\t\t \"such commands run concurrently and affect the same rows, then \"\n\t\t\t\t\t \"they are likely to deadlock. When enabled, this flag prevents \"\n\t\t\t\t\t \"multi-shard modifications from running concurrently when they \"\n\t\t\t\t\t \"affect the same shards in order to prevent deadlocks.\"),\n\t\t&EnableDeadlockPrevention,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_fast_path_router_planner\",\n\t\tgettext_noop(\"Enables fast path router planner\"),\n\t\tNULL,\n\t\t&EnableFastPathRouterPlanner,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_local_execution\",\n\t\tgettext_noop(\"Enables queries on shards that are local to the current node \"\n\t\t\t\t\t \"to be planned and executed locally.\"),\n\t\tNULL,\n\t\t&EnableLocalExecution,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_local_fast_path_query_optimization\",\n\t\tgettext_noop(\"Enables the planner to avoid a query deparse and planning if \"\n\t\t\t\t\t \"the shard is local to the current node.\"),\n\t\tNULL,\n\t\t&EnableLocalFastPathQueryOptimization,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tWarnIfLocalExecutionDisabled, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_local_reference_table_foreign_keys\",\n\t\tgettext_noop(\"Enables foreign keys from/to local tables\"),\n\t\tgettext_noop(\"When enabled, foreign keys between local tables and reference \"\n\t\t\t\t\t \"tables supported.\"),\n\t\t&EnableLocalReferenceForeignKeys,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_manual_changes_to_shards\",\n\t\tgettext_noop(\"Enables dropping and truncating known shards.\"),\n\t\tgettext_noop(\"Set to false by default. If set to true, enables \"\n\t\t\t\t\t \"dropping and truncating shards on the coordinator \"\n\t\t\t\t\t \"(or the workers with metadata)\"),\n\t\t&EnableManualChangesToShards,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomStringVariable(\n\t\t\"citus.enable_manual_metadata_changes_for_user\",\n\t\tgettext_noop(\"Enables some helper UDFs to modify metadata \"\n\t\t\t\t\t \"for the given user\"),\n\t\tNULL,\n\t\t&EnableManualMetadataChangesForUser,\n\t\t\"\",\n\t\tPGC_SIGHUP,\n\t\tGUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_metadata_sync\",\n\t\tgettext_noop(\"Enables object and metadata syncing.\"),\n\t\tNULL,\n\t\t&EnableMetadataSync,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_non_colocated_router_query_pushdown\",\n\t\tgettext_noop(\"Enables router planner for the queries that reference \"\n\t\t\t\t\t \"non-colocated distributed tables.\"),\n\t\tgettext_noop(\"Normally, router planner planner is only enabled for \"\n\t\t\t\t\t \"the queries that reference colocated distributed tables \"\n\t\t\t\t\t \"because it is not guaranteed to have the target shards \"\n\t\t\t\t\t \"always on the same node, e.g., after rebalancing the \"\n\t\t\t\t\t \"shards. For this reason, while enabling this flag allows \"\n\t\t\t\t\t \"some degree of optimization for the queries that reference \"\n\t\t\t\t\t \"non-colocated distributed tables, it is not guaranteed \"\n\t\t\t\t\t \"that the same query will work after rebalancing the shards \"\n\t\t\t\t\t \"or altering the shard count of one of those distributed \"\n\t\t\t\t\t \"tables.\"),\n\t\t&EnableNonColocatedRouterQueryPushdown,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_outer_joins_with_pseudoconstant_quals_pre_pg17\",\n\t\tgettext_noop(\"Enables running distributed queries with outer joins \"\n\t\t\t\t\t \"and pseudoconstant quals pre PG17.\"),\n\t\tgettext_noop(\"Set to false by default. If set to true, enables \"\n\t\t\t\t\t \"running distributed queries with outer joins and  \"\n\t\t\t\t\t \"pseudoconstant quals, at user's own risk, because \"\n\t\t\t\t\t \"pre PG17, Citus doesn't have access to \"\n\t\t\t\t\t \"set_join_pathlist_hook, which doesn't guarantee correct\"\n\t\t\t\t\t \"query results. Note that in PG17+, this GUC has no effect\"\n\t\t\t\t\t \"and the user can run such queries\"),\n\t\t&EnableOuterJoinsWithPseudoconstantQualsPrePG17,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_recurring_outer_join_pushdown\",\n\t\tgettext_noop(\"Enables outer join pushdown for recurring relations.\"),\n\t\tgettext_noop(\"When enabled, Citus will try to push down outer joins \"\n\t\t\t\t\t \"between recurring and non-recurring relations to workers \"\n\t\t\t\t\t \"whenever feasible by introducing correctness constraints \"\n\t\t\t\t\t \"to the where clause of the query. Note that if this is \"\n\t\t\t\t\t \"disabled, or push down is not feasible, the result will \"\n\t\t\t\t\t \"be computed via recursive planning.\"),\n\t\t&EnableRecurringOuterJoinPushdown,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_repartition_joins\",\n\t\tgettext_noop(\"Allows Citus to repartition data between nodes.\"),\n\t\tNULL,\n\t\t&EnableRepartitionJoins,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_repartitioned_insert_select\",\n\t\tgettext_noop(\"Enables repartitioned INSERT/SELECTs\"),\n\t\tNULL,\n\t\t&EnableRepartitionedInsertSelect,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_router_execution\",\n\t\tgettext_noop(\"Enables router execution\"),\n\t\tNULL,\n\t\t&EnableRouterExecution,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_schema_based_sharding\",\n\t\tgettext_noop(\"Enables schema based sharding.\"),\n\t\tgettext_noop(\"The schemas created while this is ON will be automatically \"\n\t\t\t\t\t \"associated with individual colocation groups such that the \"\n\t\t\t\t\t \"tables created in those schemas will be automatically \"\n\t\t\t\t\t \"converted to colocated distributed tables without a shard \"\n\t\t\t\t\t \"key.\"),\n\t\t&EnableSchemaBasedSharding,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_single_hash_repartition_joins\",\n\t\tgettext_noop(\"Enables single hash repartitioning between hash \"\n\t\t\t\t\t \"distributed tables\"),\n\t\tNULL,\n\t\t&EnableSingleHashRepartitioning,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_stat_counters\",\n\t\tgettext_noop(\"Enables the collection of statistic counters for Citus.\"),\n\t\tgettext_noop(\"When enabled, Citus maintains a set of statistic \"\n\t\t\t\t\t \"counters for the Citus extension. These statistics are \"\n\t\t\t\t\t \"available in the citus_stat_counters view and are \"\n\t\t\t\t\t \"lost on server shutdown and can be reset by executing \"\n\t\t\t\t\t \"the function citus_stat_counters_reset() on demand.\"),\n\t\t&EnableStatCounters,\n\t\tENABLE_STAT_COUNTERS_DEFAULT,\n\t\tPGC_SUSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_statistics_collection\",\n\t\tgettext_noop(\"Deprecated.\"),\n\t\tNULL,\n\t\t&DeprecatedEnableStatisticsCollection,\n\t\tfalse,\n\t\tPGC_SIGHUP,\n\t\tGUC_SUPERUSER_ONLY,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_unique_job_ids\",\n\t\tgettext_noop(\"Enables unique job IDs by prepending the local process ID and \"\n\t\t\t\t\t \"group ID. This should usually be enabled, but can be disabled \"\n\t\t\t\t\t \"for repeatable output in regression tests.\"),\n\t\tNULL,\n\t\t&EnableUniqueJobIds,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_unsafe_triggers\",\n\t\tgettext_noop(\"Enables arbitrary triggers on distributed tables which may cause \"\n\t\t\t\t\t \"visibility and deadlock issues. Use at your own risk.\"),\n\t\tNULL,\n\t\t&EnableUnsafeTriggers,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_unsupported_feature_messages\",\n\t\tgettext_noop(\"Controls showing of some citus related messages. It is intended to \"\n\t\t\t\t\t \"be used before vanilla tests to stop unwanted citus messages.\"),\n\t\tNULL,\n\t\t&EnableUnsupportedFeatureMessages,\n\t\ttrue,\n\t\tPGC_SUSET,\n\t\tGUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enable_version_checks\",\n\t\tgettext_noop(\"Enables version checks during CREATE/ALTER EXTENSION commands\"),\n\t\tNULL,\n\t\t&EnableVersionChecks,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enforce_foreign_key_restrictions\",\n\t\tgettext_noop(\"Enforce restrictions while querying distributed/reference \"\n\t\t\t\t\t \"tables with foreign keys\"),\n\t\tgettext_noop(\"When enabled, cascading modifications from reference tables \"\n\t\t\t\t\t \"to distributed tables are traced and acted accordingly \"\n\t\t\t\t\t \"to avoid creating distributed deadlocks and ensure correctness.\"),\n\t\t&EnforceForeignKeyRestrictions,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.enforce_object_restrictions_for_local_objects\",\n\t\tgettext_noop(\n\t\t\t\"Controls some restrictions for local objects.\"),\n\t\tNULL,\n\t\t&EnforceLocalObjectRestrictions,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.executor_slow_start_interval\",\n\t\tgettext_noop(\"Time to wait between opening connections to the same worker node\"),\n\t\tgettext_noop(\"When the individual tasks of a multi-shard query take very \"\n\t\t\t\t\t \"little time, they can often be finished over a single (often \"\n\t\t\t\t\t \"already cached) connection. To avoid redundantly opening \"\n\t\t\t\t\t \"additional connections, the executor waits between connection \"\n\t\t\t\t\t \"attempts for the configured number of milliseconds. At the end \"\n\t\t\t\t\t \"of the interval, it increases the number of connections it is \"\n\t\t\t\t\t \"allowed to open next time.\"),\n\t\t&ExecutorSlowStartInterval,\n\t\t10, 0, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_UNIT_MS | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.explain_all_tasks\",\n\t\tgettext_noop(\"Enables showing output for all tasks in Explain.\"),\n\t\tgettext_noop(\"The Explain command for distributed queries shows \"\n\t\t\t\t\t \"the remote plan for a single task by default. When \"\n\t\t\t\t\t \"this configuration entry is enabled, the plan for \"\n\t\t\t\t\t \"all tasks is shown, but the Explain takes longer.\"),\n\t\t&ExplainAllTasks,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.explain_analyze_sort_method\",\n\t\tgettext_noop(\"Sets the sorting method for EXPLAIN ANALYZE queries.\"),\n\t\tgettext_noop(\"This parameter is intended for testing. It is developed \"\n\t\t\t\t\t \"to get consistent regression test outputs. When it is set \"\n\t\t\t\t\t \"to 'time', EXPLAIN ANALYZE output is sorted by execution \"\n\t\t\t\t\t \"duration on workers. When it is set to 'taskId', it is \"\n\t\t\t\t\t \"sorted by task id. By default, it is set to 'time'; but \"\n\t\t\t\t\t \"in regression tests, it's set to 'taskId' for consistency.\"),\n\t\t&ExplainAnalyzeSortMethod,\n\t\tEXPLAIN_ANALYZE_SORT_BY_TIME, explain_analyze_sort_method_options,\n\t\tPGC_USERSET,\n\t\t0,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.explain_distributed_queries\",\n\t\tgettext_noop(\"Enables Explain for distributed queries.\"),\n\t\tgettext_noop(\"When enabled, the Explain command shows remote and local \"\n\t\t\t\t\t \"plans when used with a distributed query. It is enabled \"\n\t\t\t\t\t \"by default, but can be disabled for regression tests.\"),\n\t\t&ExplainDistributedQueries,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.force_max_query_parallelization\",\n\t\tgettext_noop(\"Open as many connections as possible to maximize query \"\n\t\t\t\t\t \"parallelization\"),\n\t\tgettext_noop(\"When enabled, Citus will force the executor to use \"\n\t\t\t\t\t \"as many connections as possible while executing a \"\n\t\t\t\t\t \"parallel distributed query. If not enabled, the executor \"\n\t\t\t\t\t \"might choose to use less connections to optimize overall \"\n\t\t\t\t\t \"query execution throughput. Internally, setting this true \"\n\t\t\t\t\t \"will end up with using one connection per task.\"),\n\t\t&ForceMaxQueryParallelization,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.function_opens_transaction_block\",\n\t\tgettext_noop(\"Open transaction blocks for function calls\"),\n\t\tgettext_noop(\"When enabled, Citus will always send a BEGIN to workers when \"\n\t\t\t\t\t \"running distributed queres in a function. When disabled, the \"\n\t\t\t\t\t \"queries may be committed immediately after the statemnent \"\n\t\t\t\t\t \"completes. Disabling this flag is dangerous, it is only provided \"\n\t\t\t\t\t \"for backwards compatibility with pre-8.2 behaviour.\"),\n\t\t&FunctionOpensTransactionBlock,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomStringVariable(\n\t\t\"citus.grep_remote_commands\",\n\t\tgettext_noop(\n\t\t\t\"Applies \\\"command\\\" like citus.grep_remote_commands, if returns \"\n\t\t\t\"true, the command is logged.\"),\n\t\tNULL,\n\t\t&GrepRemoteCommands,\n\t\t\"\",\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.hide_citus_dependent_objects\",\n\t\tgettext_noop(\n\t\t\t\"Hides some objects, which depends on citus extension, from pg meta class queries. \"\n\t\t\t\"It is intended to be used only before postgres vanilla tests to not break them.\"),\n\t\tNULL,\n\t\t&HideCitusDependentObjects,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\t/*\n\t * This was a GUC we added on Citus 11.0.1, and\n\t * replaced with another name on 11.0.2 via #5920.\n\t * However, as this GUC has been used in\n\t * citus_shard_indexes_on_worker-11.0.1\n\t * script. So, it is not easy to completely get rid\n\t * of the GUC. Especially with PG 15+, Postgres verifies\n\t * existence of the GUCs that are used. So, without this\n\t * CREATE EXTENSION fails.\n\t */\n\tDefineCustomStringVariable(\n\t\t\"citus.hide_shards_from_app_name_prefixes\",\n\t\tgettext_noop(\"Deprecated, use citus.show_shards_for_app_name_prefixes\"),\n\t\tNULL,\n\t\t&DeprecatedEmptyString,\n\t\t\"\",\n\t\tPGC_SUSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.isolation_test_session_process_id\",\n\t\tNULL,\n\t\tNULL,\n\t\t&IsolationTestSessionProcessID,\n\t\t-1, -1, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.isolation_test_session_remote_process_id\",\n\t\tNULL,\n\t\tNULL,\n\t\t&IsolationTestSessionRemoteProcessID,\n\t\t-1, -1, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.limit_clause_row_fetch_count\",\n\t\tgettext_noop(\"Number of rows to fetch per task for limit clause optimization.\"),\n\t\tgettext_noop(\"Select queries get partitioned and executed as smaller \"\n\t\t\t\t\t \"tasks. In some cases, select queries with limit clauses \"\n\t\t\t\t\t \"may need to fetch all rows from each task to generate \"\n\t\t\t\t\t \"results. In those cases, and where an approximation would \"\n\t\t\t\t\t \"produce meaningful results, this configuration value sets \"\n\t\t\t\t\t \"the number of rows to fetch from each task.\"),\n\t\t&LimitClauseRowFetchCount,\n\t\t-1, -1, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.local_copy_flush_threshold\",\n\t\tgettext_noop(\"Sets the threshold for local copy to be flushed.\"),\n\t\tNULL,\n\t\t&LocalCopyFlushThresholdByte,\n\t\t512 * 1024, 1, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_UNIT_BYTE | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomStringVariable(\n\t\t\"citus.local_hostname\",\n\t\tgettext_noop(\"Sets the hostname when connecting back to itself.\"),\n\t\tgettext_noop(\"For some operations nodes, mostly the coordinator, connect back to \"\n\t\t\t\t\t \"itself. When configuring SSL certificates it sometimes is required \"\n\t\t\t\t\t \"to use a specific hostname to match the CN of the certificate when \"\n\t\t\t\t\t \"verify-full is used.\"),\n\t\t&LocalHostName,\n\t\t\"localhost\",\n\t\tPGC_SUSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.local_shared_pool_size\",\n\t\tgettext_noop(\n\t\t\t\"Sets the maximum number of connections allowed for the shards on the \"\n\t\t\t\"local node across all the backends from this node. Setting to -1 disables \"\n\t\t\t\"connections throttling. Setting to 0 makes it auto-adjust, meaning \"\n\t\t\t\"equal to the half of max_connections on the coordinator.\"),\n\t\tgettext_noop(\"As a rule of thumb, the value should be at most equal to the \"\n\t\t\t\t\t \"max_connections on the local node.\"),\n\t\t&LocalSharedPoolSize,\n\t\t0, -1, INT_MAX,\n\t\tPGC_SIGHUP,\n\t\tGUC_SUPERUSER_ONLY,\n\t\tNULL, NULL, LocalPoolSizeGucShowHook);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.local_table_join_policy\",\n\t\tgettext_noop(\"defines the behaviour when a distributed table \"\n\t\t\t\t\t \"is joined with a local table\"),\n\t\tgettext_noop(\n\t\t\t\"There are 4 values available. The default, 'auto' will recursively plan \"\n\t\t\t\"distributed tables if there is a constant filter on a unique index. \"\n\t\t\t\"'prefer-local' will choose local tables if possible. \"\n\t\t\t\"'prefer-distributed' will choose distributed tables if possible. \"\n\t\t\t\"'never' will basically skip local table joins.\"\n\t\t\t),\n\t\t&LocalTableJoinPolicy,\n\t\tLOCAL_JOIN_POLICY_AUTO,\n\t\tlocal_table_join_policies,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.log_distributed_deadlock_detection\",\n\t\tgettext_noop(\"Log distributed deadlock detection related processing in \"\n\t\t\t\t\t \"the server log\"),\n\t\tNULL,\n\t\t&LogDistributedDeadlockDetection,\n\t\tfalse,\n\t\tPGC_SIGHUP,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.log_intermediate_results\",\n\t\tgettext_noop(\"Log intermediate results sent to other nodes\"),\n\t\tNULL,\n\t\t&LogIntermediateResults,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.log_local_commands\",\n\t\tgettext_noop(\"Log queries that are executed locally, can be overriden by \"\n\t\t\t\t\t \"citus.log_remote_commands\"),\n\t\tNULL,\n\t\t&LogLocalCommands,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.log_multi_join_order\",\n\t\tgettext_noop(\"Logs the distributed join order to the server log.\"),\n\t\tgettext_noop(\"We use this private configuration entry as a debugging aid. \"\n\t\t\t\t\t \"If enabled, we print the distributed join order.\"),\n\t\t&LogMultiJoinOrder,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.log_remote_commands\",\n\t\tgettext_noop(\"Log queries sent to other nodes in the server log\"),\n\t\tNULL,\n\t\t&LogRemoteCommands,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.logical_replication_timeout\",\n\t\tgettext_noop(\"Sets the timeout to error out when logical replication is used\"),\n\t\tgettext_noop(\"Citus uses logical replication when it moves/replicates shards. \"\n\t\t\t\t\t \"This setting determines when Citus gives up waiting for progress \"\n\t\t\t\t\t \"during logical replication and errors out.\"),\n\t\t&LogicalReplicationTimeout,\n\t\t2 * 60 * 60 * 1000, 0, 7 * 24 * 3600 * 1000,\n\t\tPGC_SIGHUP,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_UNIT_MS,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_adaptive_executor_pool_size\",\n\t\tgettext_noop(\"Sets the maximum number of connections per worker node used by \"\n\t\t\t\t\t \"the adaptive executor to execute a multi-shard command\"),\n\t\tgettext_noop(\"The adaptive executor may open multiple connections per worker \"\n\t\t\t\t\t \"node when running multi-shard commands to parallelize the command \"\n\t\t\t\t\t \"across multiple cores on the worker. This setting specifies the \"\n\t\t\t\t\t \"maximum number of connections it will open. The number of \"\n\t\t\t\t\t \"connections is also bounded by the number of shards on the node. \"\n\t\t\t\t\t \"This setting can be used to reduce the memory usage of a query \"\n\t\t\t\t\t \"and allow a higher degree of concurrency when concurrent \"\n\t\t\t\t\t \"multi-shard queries open too many connections to a worker.\"),\n\t\t&MaxAdaptiveExecutorPoolSize,\n\t\t16, 1, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_background_task_executors\",\n\t\tgettext_noop(\n\t\t\t\"Sets the maximum number of parallel task executor workers for scheduled \"\n\t\t\t\"background tasks\"),\n\t\tgettext_noop(\n\t\t\t\"Controls the maximum number of parallel task executors the task monitor \"\n\t\t\t\"can create for scheduled background tasks. Note that the value is not effective \"\n\t\t\t\"if it is set a value higher than 'max_worker_processes' postgres parameter . It is \"\n\t\t\t\"also not guaranteed to have exactly specified number of parallel task executors \"\n\t\t\t\"because total background worker count is shared by all background workers. The value \"\n\t\t\t\"represents the possible maximum number of task executors.\"),\n\t\t&MaxBackgroundTaskExecutors,\n\t\t1, 1, MAX_BG_TASK_EXECUTORS,\n\t\tPGC_SIGHUP,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_background_task_executors_per_node\",\n\t\tgettext_noop(\n\t\t\t\"Sets the maximum number of parallel background task executor workers \"\n\t\t\t\"for scheduled background tasks that involve a particular node\"),\n\t\tNULL,\n\t\t&MaxBackgroundTaskExecutorsPerNode,\n\t\t1, 1, 128,\n\t\tPGC_SIGHUP,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_cached_connection_lifetime\",\n\t\tgettext_noop(\"Sets the maximum lifetime of cached connections to other nodes.\"),\n\t\tNULL,\n\t\t&MaxCachedConnectionLifetime,\n\t\t10 * MS_PER_MINUTE, -1, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_UNIT_MS | GUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_cached_conns_per_worker\",\n\t\tgettext_noop(\"Sets the maximum number of connections to cache per worker.\"),\n\t\tgettext_noop(\"Each backend opens connections to the workers to query the \"\n\t\t\t\t\t \"shards. At the end of the transaction, the configurated number \"\n\t\t\t\t\t \"of connections is kept open to speed up subsequent commands. \"\n\t\t\t\t\t \"Increasing this value will reduce the latency of multi-shard \"\n\t\t\t\t\t \"queries, but increases overhead on the workers\"),\n\t\t&MaxCachedConnectionsPerWorker,\n\t\t1, 0, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_client_connections\",\n\t\tgettext_noop(\"Sets the maximum number of connections regular clients can make\"),\n\t\tgettext_noop(\"To ensure that a Citus cluster has a sufficient number of \"\n\t\t\t\t\t \"connection slots to serve queries internally, it can be \"\n\t\t\t\t\t \"useful to reserve connection slots for Citus internal \"\n\t\t\t\t\t \"connections. When max_client_connections is set to a value \"\n\t\t\t\t\t \"below max_connections, the remaining connections are reserved \"\n\t\t\t\t\t \"for connections between Citus nodes. This does not affect \"\n\t\t\t\t\t \"superuser_reserved_connections. If set to -1, no connections \"\n\t\t\t\t\t \"are reserved.\"),\n\t\t&MaxClientConnections,\n\t\t-1, -1, MaxConnections,\n\t\tPGC_SUSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_high_priority_background_processes\",\n\t\tgettext_noop(\"Sets the maximum number of background processes \"\n\t\t\t\t\t \"that can have their CPU priority increased at the same \"\n\t\t\t\t\t \"time on a specific node.\"),\n\t\tgettext_noop(\"This setting is useful to make sure logical replication \"\n\t\t\t\t\t \"senders don't take over the CPU of the entire machine.\"),\n\t\t&MaxHighPriorityBackgroundProcesess,\n\t\t2, 0, 10000,\n\t\tPGC_SUSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_intermediate_result_size\",\n\t\tgettext_noop(\"Sets the maximum size of the intermediate results in KB for \"\n\t\t\t\t\t \"CTEs and complex subqueries.\"),\n\t\tNULL,\n\t\t&MaxIntermediateResult,\n\t\t1048576, -1, MAX_KILOBYTES,\n\t\tPGC_USERSET,\n\t\tGUC_UNIT_KB | GUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_matview_size_to_auto_recreate\",\n\t\tgettext_noop(\"Sets the maximum size of materialized views in MB to \"\n\t\t\t\t\t \"automatically distribute them.\"),\n\t\tNULL,\n\t\t&MaxMatViewSizeToAutoRecreate,\n\t\t1024, -1, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_UNIT_MB | GUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_rebalancer_logged_ignored_moves\",\n\t\tgettext_noop(\"Sets the maximum number of ignored moves the rebalance logs\"),\n\t\tNULL,\n\t\t&MaxRebalancerLoggedIgnoredMoves,\n\t\t5, -1, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_shared_pool_size\",\n\t\tgettext_noop(\"Sets the maximum number of connections allowed per worker node \"\n\t\t\t\t\t \"across all the backends from this node. Setting to -1 disables \"\n\t\t\t\t\t \"connections throttling. Setting to 0 makes it auto-adjust, meaning \"\n\t\t\t\t\t \"equal to max_connections on the coordinator.\"),\n\t\tgettext_noop(\"As a rule of thumb, the value should be at most equal to the \"\n\t\t\t\t\t \"max_connections on the remote nodes.\"),\n\t\t&MaxSharedPoolSize,\n\t\t0, -1, INT_MAX,\n\t\tPGC_SIGHUP,\n\t\tGUC_SUPERUSER_ONLY,\n\t\tNULL, NULL, MaxSharedPoolSizeGucShowHook);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.max_worker_nodes_tracked\",\n\t\tgettext_noop(\"Sets the maximum number of worker nodes that are tracked.\"),\n\t\tgettext_noop(\"Worker nodes' network locations, their membership and \"\n\t\t\t\t\t \"health status are tracked in a shared hash table on \"\n\t\t\t\t\t \"the master node. This configuration value limits the \"\n\t\t\t\t\t \"size of the hash table, and consequently the maximum \"\n\t\t\t\t\t \"number of worker nodes that can be tracked. \"\n\t\t\t\t\t \"Citus keeps some information about the worker nodes \"\n\t\t\t\t\t \"in the shared memory for certain optimizations. The \"\n\t\t\t\t\t \"optimizations are enforced up to this number of worker \"\n\t\t\t\t\t \"nodes. Any additional worker nodes may not benefit from \"\n\t\t\t\t\t \"the optimizations.\"),\n\t\t&MaxWorkerNodesTracked,\n\t\t2048, 1024, INT_MAX,\n\t\tPGC_POSTMASTER,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.metadata_sync_interval\",\n\t\tgettext_noop(\"Sets the time to wait between metadata syncs.\"),\n\t\tgettext_noop(\"metadata sync needs to run every so often \"\n\t\t\t\t\t \"to synchronize metadata to metadata nodes \"\n\t\t\t\t\t \"that are out of sync.\"),\n\t\t&MetadataSyncInterval,\n\t\t60 * MS_PER_SECOND, 1, 7 * MS_PER_DAY,\n\t\tPGC_SIGHUP,\n\t\tGUC_UNIT_MS | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.metadata_sync_mode\",\n\t\tgettext_noop(\"Sets transaction mode for metadata syncs.\"),\n\t\tgettext_noop(\"metadata sync can be run inside a single coordinated \"\n\t\t\t\t\t \"transaction or with multiple small transactions in \"\n\t\t\t\t\t \"idempotent way. By default we sync metadata in single \"\n\t\t\t\t\t \"coordinated transaction. When we hit memory problems \"\n\t\t\t\t\t \"at workers, we have alternative nontransactional mode \"\n\t\t\t\t\t \"where we send each command with separate transaction.\"),\n\t\t&MetadataSyncTransMode,\n\t\tMETADATA_SYNC_TRANSACTIONAL, metadata_sync_mode_options,\n\t\tPGC_SUSET,\n\t\tGUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.metadata_sync_retry_interval\",\n\t\tgettext_noop(\"Sets the interval to retry failed metadata syncs.\"),\n\t\tgettext_noop(\"metadata sync needs to run every so often \"\n\t\t\t\t\t \"to synchronize metadata to metadata nodes \"\n\t\t\t\t\t \"that are out of sync.\"),\n\t\t&MetadataSyncRetryInterval,\n\t\t5 * MS_PER_SECOND, 1, 7 * MS_PER_DAY,\n\t\tPGC_SIGHUP,\n\t\tGUC_UNIT_MS | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\t/*\n\t * Previously we setting this configuration parameter\n\t * in the fly for failure tests schedule.\n\t * However, PG15 doesn't allow that anymore: reserved prefixes\n\t * like \"citus\" cannot be used to set non-existing GUCs.\n\t * Relevant PG commit: 88103567cb8fa5be46dc9fac3e3b8774951a2be7\n\t */\n\n\tDefineCustomStringVariable(\n\t\t\"citus.mitmfifo\",\n\t\tgettext_noop(\"Sets the citus mitm fifo path for failure tests\"),\n\t\tgettext_noop(\"This GUC is only used for testing.\"),\n\t\t&MitmfifoEmptyString,\n\t\t\"\",\n\t\tPGC_SUSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.multi_shard_modify_mode\",\n\t\tgettext_noop(\"Sets the connection type for multi shard modify queries\"),\n\t\tNULL,\n\t\t&MultiShardConnectionType,\n\t\tPARALLEL_CONNECTION, multi_shard_modify_connection_options,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.multi_task_query_log_level\",\n\t\tgettext_noop(\"Sets the level of multi task query execution log messages\"),\n\t\tNULL,\n\t\t&MultiTaskQueryLogLevel,\n\t\tCITUS_LOG_LEVEL_OFF, log_level_options,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.next_cleanup_record_id\",\n\t\tgettext_noop(\"Set the next cleanup record ID to use in operation creation.\"),\n\t\tgettext_noop(\"Cleanup record IDs are normally generated using a sequence. If \"\n\t\t\t\t\t \"next_cleanup_record_id is set to a non-zero value, cleanup record IDs will \"\n\t\t\t\t\t \"instead be generated by incrementing from the value of \"\n\t\t\t\t\t \"this GUC and this will be reflected in the GUC. This is \"\n\t\t\t\t\t \"mainly useful to ensure consistent cleanup record IDs when running \"\n\t\t\t\t\t \"tests in parallel.\"),\n\t\t&NextCleanupRecordId,\n\t\t0, 0, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.next_operation_id\",\n\t\tgettext_noop(\"Set the next operation ID to use in operation creation.\"),\n\t\tgettext_noop(\"Operation IDs are normally generated using a sequence. If \"\n\t\t\t\t\t \"next_operation_id is set to a non-zero value, operation IDs will \"\n\t\t\t\t\t \"instead be generated by incrementing from the value of \"\n\t\t\t\t\t \"this GUC and this will be reflected in the GUC. This is \"\n\t\t\t\t\t \"mainly useful to ensure consistent operation IDs when running \"\n\t\t\t\t\t \"tests in parallel.\"),\n\t\t&NextOperationId,\n\t\t0, 0, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.next_placement_id\",\n\t\tgettext_noop(\"Set the next placement ID to use in placement creation.\"),\n\t\tgettext_noop(\"Placement IDs are normally generated using a sequence. If \"\n\t\t\t\t\t \"next_placement_id is set to a non-zero value, placement IDs will \"\n\t\t\t\t\t \"instead be generated by incrementing from the value of \"\n\t\t\t\t\t \"this GUC and this will be reflected in the GUC. This is \"\n\t\t\t\t\t \"mainly useful to ensure consistent placement IDs when running \"\n\t\t\t\t\t \"tests in parallel.\"),\n\t\t&NextPlacementId,\n\t\t0, 0, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.next_shard_id\",\n\t\tgettext_noop(\"Set the next shard ID to use in shard creation.\"),\n\t\tgettext_noop(\"Shard IDs are normally generated using a sequence. If \"\n\t\t\t\t\t \"next_shard_id is set to a non-zero value, shard IDs will \"\n\t\t\t\t\t \"instead be generated by incrementing from the value of \"\n\t\t\t\t\t \"this GUC and this will be reflected in the GUC. This is \"\n\t\t\t\t\t \"mainly useful to ensure consistent shard IDs when running \"\n\t\t\t\t\t \"tests in parallel.\"),\n\t\t&NextShardId,\n\t\t0, 0, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.node_connection_timeout\",\n\t\tgettext_noop(\"Sets the maximum duration to connect to worker nodes.\"),\n\t\tNULL,\n\t\t&NodeConnectionTimeout,\n\t\t30 * MS_PER_SECOND, 10 * MS, MS_PER_HOUR,\n\t\tPGC_USERSET,\n\t\tGUC_UNIT_MS | GUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomStringVariable(\n\t\t\"citus.node_conninfo\",\n\t\tgettext_noop(\"Sets parameters used for outbound connections.\"),\n\t\tNULL,\n\t\t&NodeConninfo,\n#ifdef USE_SSL\n\t\t\"sslmode=require\",\n#else\n\t\t\"sslmode=prefer\",\n#endif\n\t\tPGC_SIGHUP,\n\t\tGUC_SUPERUSER_ONLY,\n\t\tNodeConninfoGucCheckHook,\n\t\tNodeConninfoGucAssignHook,\n\t\tNULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.override_table_visibility\",\n\t\tgettext_noop(\"Enables replacing occurrrences of pg_catalog.pg_table_visible() \"\n\t\t\t\t\t \"with pg_catalog.citus_table_visible()\"),\n\t\tgettext_noop(\"When enabled, shards on the Citus MX worker (data) nodes would be \"\n\t\t\t\t\t \"filtered out by many psql commands to provide better user \"\n\t\t\t\t\t \"experience.\"),\n\t\t&OverrideTableVisibility,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.prevent_incomplete_connection_establishment\",\n\t\tgettext_noop(\"When enabled, the executor waits until all the connections \"\n\t\t\t\t\t \"are successfully established.\"),\n\t\tgettext_noop(\"Under some load, the executor may decide to establish some \"\n\t\t\t\t\t \"extra connections to further parallelize the execution. However, \"\n\t\t\t\t\t \"before the connection establishment is done, the execution might \"\n\t\t\t\t\t \"have already finished. When this GUC is set to true, the execution \"\n\t\t\t\t\t \"waits for such connections to be established.\"),\n\t\t&PreventIncompleteConnectionEstablishment,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.propagate_session_settings_for_loopback_connection\",\n\t\tgettext_noop(\n\t\t\t\"When enabled, rebalancer propagates all the allowed GUC settings to new connections.\"),\n\t\tNULL,\n\t\t&PropagateSessionSettingsForLoopbackConnection,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.propagate_set_commands\",\n\t\tgettext_noop(\"Sets which SET commands are propagated to workers.\"),\n\t\tNULL,\n\t\t&PropagateSetCommands,\n\t\tPROPSETCMD_NONE,\n\t\tpropagate_set_commands_options,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.rebalancer_by_disk_size_base_cost\",\n\t\tgettext_noop(\n\t\t\t\"When using the by_disk_size rebalance strategy each shard group \"\n\t\t\t\"will get this cost in bytes added to its actual disk size. This \"\n\t\t\t\"is used to avoid creating a bad balance when there's very little \"\n\t\t\t\"data in some of the shards. The assumption is that even empty \"\n\t\t\t\"shards have some cost, because of parallelism and because empty \"\n\t\t\t\"shard groups will likely grow in the future.\"),\n\t\tgettext_noop(\n\t\t\t\"The main reason this is configurable, is so it can be lowered for Citus its regression tests.\"),\n\t\t&RebalancerByDiskSizeBaseCost,\n\t\t100 * 1024 * 1024, 0, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_UNIT_BYTE | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.recover_2pc_interval\",\n\t\tgettext_noop(\"Sets the time to wait between recovering 2PCs.\"),\n\t\tgettext_noop(\"2PC transaction recovery needs to run every so often \"\n\t\t\t\t\t \"to clean up records in pg_dist_transaction and \"\n\t\t\t\t\t \"potentially roll failed 2PCs forward. This setting \"\n\t\t\t\t\t \"determines how often recovery should run, \"\n\t\t\t\t\t \"use -1 to disable.\"),\n\t\t&Recover2PCInterval,\n\t\t60 * MS_PER_SECOND, -1, 7 * MS_PER_DAY,\n\t\tPGC_SIGHUP,\n\t\tGUC_UNIT_MS | GUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.remote_copy_flush_threshold\",\n\t\tgettext_noop(\"Sets the threshold for remote copy to be flushed.\"),\n\t\tgettext_noop(\"When sending data over remote connections via the COPY protocol, \"\n\t\t\t\t\t \"bytes are first buffered internally by libpq. If the number of \"\n\t\t\t\t\t \"bytes buffered exceeds the threshold, Citus waits for all the \"\n\t\t\t\t\t \"bytes to flush.\"),\n\t\t&RemoteCopyFlushThreshold,\n\t\t8 * 1024 * 1024, 0, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_UNIT_BYTE | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.remote_task_check_interval\",\n\t\tgettext_noop(\"Sets the frequency at which we check job statuses.\"),\n\t\tgettext_noop(\"The master node assigns tasks to workers nodes, and \"\n\t\t\t\t\t \"then regularly checks with them about each task's \"\n\t\t\t\t\t \"progress. This configuration value sets the time \"\n\t\t\t\t\t \"interval between two consequent checks.\"),\n\t\t&RemoteTaskCheckInterval,\n\t\t10, 1, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_UNIT_MS | GUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.repartition_join_bucket_count_per_node\",\n\t\tgettext_noop(\"Sets the bucket size for repartition joins per node\"),\n\t\tgettext_noop(\"Repartition joins create buckets in each node and \"\n\t\t\t\t\t \"uses those to shuffle data around nodes. \"),\n\t\t&RepartitionJoinBucketCountPerNode,\n\t\t4, 1, INT_MAX,\n\t\tPGC_SIGHUP,\n\t\tGUC_STANDARD | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\t/* deprecated setting */\n\tDefineCustomBoolVariable(\n\t\t\"citus.replicate_reference_tables_on_activate\",\n\t\tNULL,\n\t\tNULL,\n\t\t&DeprecatedReplicateReferenceTablesOnActivate,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.replication_model\",\n\t\tgettext_noop(\"Deprecated. Please use citus.shard_replication_factor instead\"),\n\t\tgettext_noop(\n\t\t\t\"Shard replication model is determined by the shard replication factor. \"\n\t\t\t\"'statement' replication is used only when the replication factor is one.\"),\n\t\t&ReplicationModel,\n\t\tREPLICATION_MODEL_STREAMING,\n\t\treplication_model_options,\n\t\tPGC_SUSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tWarnIfReplicationModelIsSet, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.running_under_citus_test_suite\",\n\t\tgettext_noop(\n\t\t\t\"Only useful for testing purposes, when set to true, Citus does some \"\n\t\t\t\"tricks to implement useful isolation tests with rebalancing. It also \"\n\t\t\t\"registers a dummy label provider for SECURITY LABEL tests. Should \"\n\t\t\t\"never be set to true on production systems \"),\n\t\tgettext_noop(\"for details of the tricks implemented, refer to the source code\"),\n\t\t&RunningUnderCitusTestSuite,\n\t\tfalse,\n\t\tPGC_SUSET,\n\t\tGUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.select_opens_transaction_block\",\n\t\tgettext_noop(\"Open transaction blocks for SELECT commands\"),\n\t\tgettext_noop(\"When enabled, Citus will always send a BEGIN to workers when \"\n\t\t\t\t\t \"running a distributed SELECT in a transaction block (the \"\n\t\t\t\t\t \"default). When disabled, Citus will only send BEGIN before \"\n\t\t\t\t\t \"the first write or other operation that requires a distributed \"\n\t\t\t\t\t \"transaction, meaning the SELECT on the worker commits \"\n\t\t\t\t\t \"immediately, releasing any locks and apply any changes made \"\n\t\t\t\t\t \"through function calls even if the distributed transaction \"\n\t\t\t\t\t \"aborts.\"),\n\t\t&SelectOpensTransactionBlock,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.shard_count\",\n\t\tgettext_noop(\"Sets the number of shards for a new hash-partitioned table \"\n\t\t\t\t\t \"created with create_distributed_table().\"),\n\t\tNULL,\n\t\t&ShardCount,\n\t\t32, 1, MAX_SHARD_COUNT,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.shard_replication_factor\",\n\t\tgettext_noop(\"Sets the replication factor for shards.\"),\n\t\tgettext_noop(\"Shards are replicated across nodes according to this \"\n\t\t\t\t\t \"replication factor. Note that shards read this \"\n\t\t\t\t\t \"configuration value at sharded table creation time, \"\n\t\t\t\t\t \"and later reuse the initially read value.\"),\n\t\t&ShardReplicationFactor,\n\t\t1, 1, MAX_SHARD_REPLICATION_FACTOR,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomStringVariable(\n\t\t\"citus.show_shards_for_app_name_prefixes\",\n\t\tgettext_noop(\"If application_name starts with one of these values, show shards\"),\n\t\tgettext_noop(\"Citus places distributed tables and shards in the same schema. \"\n\t\t\t\t\t \"That can cause confusion when inspecting the list of tables on \"\n\t\t\t\t\t \"a node with shards. By default the shards are hidden from \"\n\t\t\t\t\t \"pg_class. This GUC can be used to show the shards to certain \"\n\t\t\t\t\t \"applications based on the application_name of the connection. \"\n\t\t\t\t\t \"The default is empty string, which hides shards from all \"\n\t\t\t\t\t \"applications. This behaviour can be overridden using the \"\n\t\t\t\t\t \"citus.override_table_visibility setting\"),\n\t\t&ShowShardsForAppNamePrefixes,\n\t\t\"\",\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tShowShardsForAppNamePrefixesCheckHook,\n\t\tShowShardsForAppNamePrefixesAssignHook,\n\t\tNULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.skip_advisory_lock_permission_checks\",\n\t\tgettext_noop(\"Postgres would normally enforce some \"\n\t\t\t\t\t \"ownership checks while acquiring locks. \"\n\t\t\t\t\t \"When this setting is 'on', Citus skips \"\n\t\t\t\t\t \"ownership checks on internal advisory \"\n\t\t\t\t\t \"locks.\"),\n\t\tNULL,\n\t\t&SkipAdvisoryLockPermissionChecks,\n\t\tfalse,\n\t\tPGC_SUSET,\n\t\tGUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.skip_constraint_validation\",\n\t\tgettext_noop(\"Skip validation of constraints\"),\n\t\tgettext_noop(\"Validating constraints is a costly operation which effects Citus' \"\n\t\t\t\t\t \"performance negatively. With this GUC set to true, we skip \"\n\t\t\t\t\t \"validating them. Constraint validation can be redundant for some \"\n\t\t\t\t\t \"cases. For instance, when moving a shard, which has already \"\n\t\t\t\t\t \"validated constraints at the source; we don't need to validate \"\n\t\t\t\t\t \"the constraints again at the destination.\"),\n\t\t&SkipConstraintValidation,\n\t\tfalse,\n\t\tPGC_SUSET,\n\t\t0,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.skip_jsonb_validation_in_copy\",\n\t\tgettext_noop(\"Skip validation of JSONB columns on the coordinator during COPY \"\n\t\t\t\t\t \"into a distributed table\"),\n\t\tgettext_noop(\"Parsing large JSON objects may incur significant CPU overhead, \"\n\t\t\t\t\t \"which can lower COPY throughput. If this GUC is set (the default), \"\n\t\t\t\t\t \"JSON parsing is skipped on the coordinator, which means you cannot \"\n\t\t\t\t\t \"see the line number in case of malformed JSON, but throughput will \"\n\t\t\t\t\t \"be higher. This setting does not apply if the input format is \"\n\t\t\t\t\t \"binary.\"),\n\t\t&SkipJsonbValidationInCopy,\n\t\ttrue,\n\t\tPGC_USERSET,\n\t\t0,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.sort_returning\",\n\t\tgettext_noop(\"Sorts the RETURNING clause to get consistent test output\"),\n\t\tgettext_noop(\"This feature is not intended for users. It is developed \"\n\t\t\t\t\t \"to get consistent regression test outputs. When enabled, \"\n\t\t\t\t\t \"the RETURNING clause returns the tuples sorted. The sort \"\n\t\t\t\t\t \"is done for all the entries, starting from the first one. \"\n\t\t\t\t\t \"Finally, the sorting is done in ASC order.\"),\n\t\t&SortReturning,\n\t\tfalse,\n\t\tPGC_SUSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\t/*\n\t * It takes about 140 bytes of shared memory to store one row, therefore\n\t * this setting should be used responsibly. setting it to 10M will require\n\t * 1.4GB of shared memory.\n\t */\n\tDefineCustomIntVariable(\n\t\t\"citus.stat_statements_max\",\n\t\tgettext_noop(\"Determines maximum number of statements tracked by \"\n\t\t\t\t\t \"citus_stat_statements.\"),\n\t\tNULL,\n\t\t&StatStatementsMax,\n\t\t50000, 1000, 10000000,\n\t\tPGC_POSTMASTER,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.stat_statements_purge_interval\",\n\t\tgettext_noop(\"Determines time interval in seconds for \"\n\t\t\t\t\t \"citus_stat_statements to purge expired entries.\"),\n\t\tNULL,\n\t\t&StatStatementsPurgeInterval,\n\t\t10, -1, INT_MAX,\n\t\tPGC_SIGHUP,\n\t\tGUC_UNIT_MS | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.stat_statements_track\",\n\t\tgettext_noop(\n\t\t\t\"Enables/Disables the stats collection for citus_stat_statements.\"),\n\t\tgettext_noop(\"Enables the stats collection when set to 'all'. \"\n\t\t\t\t\t \"Disables when set to 'none'. Disabling can be useful for \"\n\t\t\t\t\t \"avoiding extra CPU cycles needed for the calculations.\"),\n\t\t&StatStatementsTrack,\n\t\tSTAT_STATEMENTS_TRACK_NONE,\n\t\tstat_statements_track_options,\n\t\tPGC_SUSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.stat_tenants_limit\",\n\t\tgettext_noop(\"Number of tenants to be shown in citus_stat_tenants.\"),\n\t\tNULL,\n\t\t&StatTenantsLimit,\n\t\t100, 1, 10000,\n\t\tPGC_POSTMASTER,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\tDefineCustomEnumVariable(\n\t\t\"citus.stat_tenants_log_level\",\n\t\tgettext_noop(\"Sets the level of citus_stat_tenants log messages\"),\n\t\tNULL,\n\t\t&StatTenantsLogLevel,\n\t\tCITUS_LOG_LEVEL_OFF, log_level_options,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.stat_tenants_period\",\n\t\tgettext_noop(\"Period in seconds to be used for calculating the tenant \"\n\t\t\t\t\t \"statistics in citus_stat_tenants.\"),\n\t\tNULL,\n\t\t&StatTenantsPeriod,\n\t\t60, 1, 60 * 60 * 24,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.stat_tenants_track\",\n\t\tgettext_noop(\"Enables/Disables the stats collection for citus_stat_tenants.\"),\n\t\tgettext_noop(\"Enables the stats collection when set to 'all'. \"\n\t\t\t\t\t \"Disables when set to 'none'. Disabling can be useful for \"\n\t\t\t\t\t \"avoiding extra CPU cycles needed for the calculations.\"),\n\t\t&StatTenantsTrack,\n\t\tSTAT_TENANTS_TRACK_NONE,\n\t\tstat_tenants_track_options,\n\t\tPGC_SUSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomRealVariable(\n\t\t\"citus.stat_tenants_untracked_sample_rate\",\n\t\tgettext_noop(\"Sampling rate for new tenants in citus_stat_tenants.\"),\n\t\tNULL,\n\t\t&StatTenantsSampleRateForNewTenants,\n\t\t1, 0, 1,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.subquery_pushdown\",\n\t\tgettext_noop(\"Usage of this GUC is highly discouraged, please read the long \"\n\t\t\t\t\t \"description\"),\n\t\tgettext_noop(\"When enabled, the planner skips many correctness checks \"\n\t\t\t\t\t \"for subqueries and pushes down the queries to shards as-is. \"\n\t\t\t\t\t \"It means that the queries are likely to return wrong results \"\n\t\t\t\t\t \"unless the user is absolutely sure that pushing down the \"\n\t\t\t\t\t \"subquery is safe. This GUC is maintained only for backward \"\n\t\t\t\t\t \"compatibility, no new users are supposed to use it. The planner \"\n\t\t\t\t\t \"is capable of pushing down as much computation as possible to the \"\n\t\t\t\t\t \"shards depending on the query.\"),\n\t\t&SubqueryPushdown,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE,\n\t\tNoticeIfSubqueryPushdownEnabled, NULL, NULL);\n\n\tDefineCustomStringVariable(\n\t\t\"citus.superuser\",\n\t\tgettext_noop(\"Name of a superuser role to be used in Citus main database \"\n\t\t\t\t\t \"connections\"),\n\t\tNULL,\n\t\t&SuperuserRole,\n\t\t\"\",\n\t\tPGC_SUSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.task_assignment_policy\",\n\t\tgettext_noop(\"Sets the policy to use when assigning tasks to worker nodes.\"),\n\t\tgettext_noop(\"The master node assigns tasks to worker nodes based on shard \"\n\t\t\t\t\t \"locations. This configuration value specifies the policy to \"\n\t\t\t\t\t \"use when making these assignments. The greedy policy aims to \"\n\t\t\t\t\t \"evenly distribute tasks across worker nodes, first-replica just \"\n\t\t\t\t\t \"assigns tasks in the order shard placements were created, \"\n\t\t\t\t\t \"and the round-robin policy assigns tasks to worker nodes in \"\n\t\t\t\t\t \"a round-robin fashion.\"),\n\t\t&TaskAssignmentPolicy,\n\t\tTASK_ASSIGNMENT_GREEDY,\n\t\ttask_assignment_policy_options,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.task_executor_type\",\n\t\tgettext_noop(\"Sets the executor type to be used for distributed queries.\"),\n\t\tgettext_noop(\"The master node chooses between two different executor types \"\n\t\t\t\t\t \"when executing a distributed query.The adaptive executor is \"\n\t\t\t\t\t \"optimal for simple key-value lookup queries and queries that \"\n\t\t\t\t\t \"involve aggregations and/or co-located joins on multiple shards. \"),\n\t\t&TaskExecutorType,\n\t\tMULTI_EXECUTOR_ADAPTIVE,\n\t\ttask_executor_type_options,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tWarnIfDeprecatedExecutorUsed, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.use_citus_managed_tables\",\n\t\tgettext_noop(\"Allows new local tables to be accessed on workers\"),\n\t\tgettext_noop(\"Adds all newly created tables to Citus metadata by default, \"\n\t\t\t\t\t \"when enabled. Set to false by default.\"),\n\t\t&AddAllLocalTablesToMetadata,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.use_secondary_nodes\",\n\t\tgettext_noop(\"Sets the policy to use when choosing nodes for SELECT queries.\"),\n\t\tNULL,\n\t\t&ReadFromSecondaries,\n\t\tUSE_SECONDARY_NODES_NEVER, use_secondary_nodes_options,\n\t\tPGC_SU_BACKEND,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomIntVariable(\n\t\t\"citus.values_materialization_threshold\",\n\t\tgettext_noop(\"Sets the maximum number of rows allowed for pushing down \"\n\t\t\t\t\t \"VALUES clause in multi-shard queries. If the number of \"\n\t\t\t\t\t \"rows exceeds the threshold, the VALUES is materialized \"\n\t\t\t\t\t \"via pull-push execution. When set to -1, materialization \"\n\t\t\t\t\t \"is disabled. When set to 0, all VALUES are materialized.\"),\n\t\tgettext_noop(\"When the VALUES is pushed down (i.e., not materialized), \"\n\t\t\t\t\t \"the VALUES clause needs to be deparsed for every shard on \"\n\t\t\t\t\t \"the coordinator - and parsed on the workers. As this \"\n\t\t\t\t\t \"setting increased, the associated overhead is multiplied \"\n\t\t\t\t\t \"by the shard count. When materialized, the VALUES is \"\n\t\t\t\t\t \"deparsed and parsed once. The downside of materialization \"\n\t\t\t\t\t \"is that Postgres may choose a poor plan when joining \"\n\t\t\t\t\t \"the materialized result with tables.\"),\n\t\t&ValuesMaterializationThreshold,\n\t\t100, -1, INT_MAX,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomStringVariable(\n\t\t\"citus.version\",\n\t\tgettext_noop(\"Shows the Citus library version\"),\n\t\tNULL,\n\t\t&CitusVersion,\n\t\tCITUS_VERSION,\n\t\tPGC_INTERNAL,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomEnumVariable(\n\t\t\"citus.worker_min_messages\",\n\t\tgettext_noop(\"Log messages from workers only if their log level is at or above \"\n\t\t\t\t\t \"the configured level\"),\n\t\tNULL,\n\t\t&WorkerMinMessages,\n\t\tNOTICE,\n\t\tlog_level_options,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\tDefineCustomBoolVariable(\n\t\t\"citus.writable_standby_coordinator\",\n\t\tgettext_noop(\"Enables simple DML via a streaming replica of the coordinator\"),\n\t\tNULL,\n\t\t&WritableStandbyCoordinator,\n\t\tfalse,\n\t\tPGC_USERSET,\n\t\tGUC_STANDARD,\n\t\tNULL, NULL, NULL);\n\n\t/* warn about config items in the citus namespace that are not registered above */\n\tEmitWarningsOnPlaceholders(\"citus\");\n\n\tOverridePostgresConfigProperties();\n}\n\n\n/*\n * OverridePostgresConfigProperties overrides GUC properties where we want\n * custom behaviour. We should consider using Postgres function find_option\n * in this function once it is exported by Postgres in a later release.\n */\nstatic void\nOverridePostgresConfigProperties(void)\n{\n\tint gucCount = 0;\n\tstruct config_generic **guc_vars = get_guc_variables(&gucCount);\n\n\tfor (int gucIndex = 0; gucIndex < gucCount; gucIndex++)\n\t{\n\t\tstruct config_generic *var = (struct config_generic *) guc_vars[gucIndex];\n\n\t\tif (strcmp(var->name, \"application_name\") == 0)\n\t\t{\n\t\t\tstruct config_string *stringVar = (struct config_string *) var;\n\n\t\t\tOldApplicationNameAssignHook = stringVar->assign_hook;\n\t\t\tstringVar->assign_hook = ApplicationNameAssignHook;\n\t\t}\n\n\t\t/*\n\t\t * Turn on GUC_REPORT for search_path. GUC_REPORT provides that an S (Parameter Status)\n\t\t * packet is appended after the C (Command Complete) packet sent from the server\n\t\t * for SET command. S packet contains the new value of the parameter\n\t\t * if its value has been changed.\n\t\t */\n\t\tif (strcmp(var->name, \"search_path\") == 0)\n\t\t{\n\t\t\tvar->flags |= GUC_REPORT;\n\t\t}\n\t}\n}\n\n\n/*\n * We don't want to allow values less than 1.0. However, we define -1 as the value to disable\n * distributed deadlock checking. Here we enforce our special constraint.\n */\nstatic bool\nErrorIfNotASuitableDeadlockFactor(double *newval, void **extra, GucSource source)\n{\n\tif (*newval <= 1.0 && *newval != -1.0)\n\t{\n\t\tereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t  errmsg(\n\t\t\t\t\t\t\t  \"citus.distributed_deadlock_detection_factor cannot be less than 1. \"\n\t\t\t\t\t\t\t  \"To disable distributed deadlock detection set the value to -1.\")));\n\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * WarnIfDeprecatedExecutorUsed prints a warning and sets the config value to\n * adaptive executor (a.k.a., ignores real-time executor).\n */\nstatic bool\nWarnIfDeprecatedExecutorUsed(int *newval, void **extra, GucSource source)\n{\n\tif (*newval == DUMMY_REAL_TIME_EXECUTOR_ENUM_VALUE)\n\t{\n\t\tereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t  errmsg(\"Ignoring the setting, real-time executor is \"\n\t\t\t\t\t\t\t\t \"deprecated\")));\n\n\t\t/* adaptive executor is superset of real-time, so switch to that */\n\t\t*newval = MULTI_EXECUTOR_ADAPTIVE;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * WarnIfLocalExecutionDisabled is used to emit a warning message when\n * enabling citus.enable_local_fast_path_query_optimization if\n * citus.enable_local_execution was disabled.\n */\nstatic bool\nWarnIfLocalExecutionDisabled(bool *newval, void **extra, GucSource source)\n{\n\tif (*newval == true && EnableLocalExecution == false)\n\t{\n\t\tereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\t  errmsg(\n\t\t\t\t\t\t\t  \"citus.enable_local_execution must be set in order for \"\n\t\t\t\t\t\t\t  \"citus.enable_local_fast_path_query_optimization to be effective.\")));\n\t}\n\n\treturn true;\n}\n\n\n/*\n * NoticeIfSubqueryPushdownEnabled prints a notice when a user sets\n * citus.subquery_pushdown to ON. It doesn't print the notice if the\n * value is already true.\n */\nstatic bool\nNoticeIfSubqueryPushdownEnabled(bool *newval, void **extra, GucSource source)\n{\n\t/* notice only when the value changes */\n\tif (*newval == true && SubqueryPushdown == false)\n\t{\n\t\tereport(NOTICE, (errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),\n\t\t\t\t\t\t errmsg(\"Setting citus.subquery_pushdown flag is \"\n\t\t\t\t\t\t\t\t\"discouraged becuase it forces the planner \"\n\t\t\t\t\t\t\t\t\"to pushdown certain queries, skipping \"\n\t\t\t\t\t\t\t\t\"relevant correctness checks.\"),\n\t\t\t\t\t\t errdetail(\n\t\t\t\t\t\t\t \"When enabled, the planner skips many correctness checks \"\n\t\t\t\t\t\t\t \"for subqueries and pushes down the queries to shards as-is. \"\n\t\t\t\t\t\t\t \"It means that the queries are likely to return wrong results \"\n\t\t\t\t\t\t\t \"unless the user is absolutely sure that pushing down the \"\n\t\t\t\t\t\t\t \"subquery is safe. This GUC is maintained only for backward \"\n\t\t\t\t\t\t\t \"compatibility, no new users are supposed to use it. The planner \"\n\t\t\t\t\t\t\t \"is capable of pushing down as much computation as possible to the \"\n\t\t\t\t\t\t\t \"shards depending on the query.\")));\n\t}\n\n\treturn true;\n}\n\n\n/*\n * WarnIfReplicationModelIsSet prints a warning when a user sets\n * citus.replication_model.\n */\nstatic bool\nWarnIfReplicationModelIsSet(int *newval, void **extra, GucSource source)\n{\n\t/* print a warning only when user sets the guc */\n\tif (source == PGC_S_SESSION)\n\t{\n\t\tereport(NOTICE, (errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),\n\t\t\t\t\t\t errmsg(\n\t\t\t\t\t\t\t \"Setting citus.replication_model has no effect. Please use \"\n\t\t\t\t\t\t\t \"citus.shard_replication_factor instead.\"),\n\t\t\t\t\t\t errdetail(\n\t\t\t\t\t\t\t \"Citus determines the replication model based on the \"\n\t\t\t\t\t\t\t \"replication factor and the replication models of the colocated \"\n\t\t\t\t\t\t\t \"shards. If a colocated table is present, the replication model \"\n\t\t\t\t\t\t\t \"is inherited. Otherwise 'streaming' replication is preferred if \"\n\t\t\t\t\t\t\t \"supported by the replication factor.\")));\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ShowShardsForAppNamePrefixesCheckHook ensures that the\n * citus.show_shards_for_app_name_prefixes holds a valid list of application_name\n * values.\n */\nstatic bool\nShowShardsForAppNamePrefixesCheckHook(char **newval, void **extra, GucSource source)\n{\n\tList *prefixList = NIL;\n\n\t/* SplitGUCList scribbles on the input */\n\tchar *splitCopy = pstrdup(*newval);\n\n\t/* check whether we can split into a list of identifiers */\n\tif (!SplitGUCList(splitCopy, ',', &prefixList))\n\t{\n\t\tGUC_check_errdetail(\"not a valid list of identifiers\");\n\t\treturn false;\n\t}\n\n\tchar *appNamePrefix = NULL;\n\tforeach_declared_ptr(appNamePrefix, prefixList)\n\t{\n\t\tint prefixLength = strlen(appNamePrefix);\n\t\tif (prefixLength >= NAMEDATALEN)\n\t\t{\n\t\t\tGUC_check_errdetail(\"prefix %s is more than %d characters\", appNamePrefix,\n\t\t\t\t\t\t\t\tNAMEDATALEN);\n\t\t\treturn false;\n\t\t}\n\n\t\tchar *prefixAscii = pstrdup(appNamePrefix);\n\t\tpg_clean_ascii(prefixAscii, 0);\n\n\t\tif (strcmp(prefixAscii, appNamePrefix) != 0)\n\t\t{\n\t\t\tGUC_check_errdetail(\"prefix %s in citus.show_shards_for_app_name_prefixes \"\n\t\t\t\t\t\t\t\t\"contains non-ascii characters\", appNamePrefix);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * ShowShardsForAppNamePrefixesAssignHook ensures changes to\n * citus.show_shards_for_app_name_prefixes are reflected in the decision\n * whether or not to show shards.\n */\nstatic void\nShowShardsForAppNamePrefixesAssignHook(const char *newval, void *extra)\n{\n\tResetHideShardsDecision();\n}\n\n\n/*\n * ApplicationNameAssignHook is called whenever application_name changes\n * to allow us to reset our hide shards decision.\n */\nstatic void\nApplicationNameAssignHook(const char *newval, void *extra)\n{\n\tResetHideShardsDecision();\n\tDetermineCitusBackendType(newval);\n\n\t/*\n\t * We use StartupCitusBackend to initialize the global pid after catalogs\n\t * are available. After that happens this hook becomes responsible to update\n\t * the global pid on later application_name changes. So we set the\n\t * FinishedStartupCitusBackend flag in StartupCitusBackend to indicate when\n\t * this responsibility handoff has happened.\n\t *\n\t * Also note that when application_name changes, we don't actually need to\n\t * try re-assigning the global pid for external client backends and\n\t * background workers because application_name doesn't affect the global\n\t * pid for such backends - note that !IsExternalClientBackend() check covers\n\t * both types of backends. Plus,\n\t * trying to re-assign the global pid for such backends would unnecessarily\n\t * cause performing a catalog access when the cached local node id is\n\t * invalidated. However, accessing to the catalog tables is dangerous in\n\t * certain situations like when we're not in a transaction block. And for\n\t * the other types of backends, i.e., the Citus internal backends, we need\n\t * to re-assign the global pid when the application_name changes because for\n\t * such backends we simply extract the global pid inherited from the\n\t * originating backend from the application_name -that's specified by\n\t * originating backend when openning that connection- and this doesn't require\n\t * catalog access.\n\t *\n\t * Another solution to the catalog table acccess problem would be to update\n\t * global pid lazily, like we do for HideShards. But that's not possible\n\t * for the global pid, since it is stored in shared memory instead of in a\n\t * process-local global variable. So other processes might want to read it\n\t * before this process has updated it. So instead we try to set it as early\n\t * as reasonably possible, which is also why we extract global pids in the\n\t * AuthHook already (extracting doesn't require catalog access).\n\t */\n\tif (FinishedStartupCitusBackend && !IsExternalClientBackend())\n\t{\n\t\tAssignGlobalPID(newval);\n\t}\n\tOldApplicationNameAssignHook(newval, extra);\n}\n\n\n/*\n * NodeConninfoGucCheckHook ensures conninfo settings are in the expected form\n * and that the keywords of all non-null settings are on a allowlist devised to\n * keep users from setting options that may result in confusion.\n */\nstatic bool\nNodeConninfoGucCheckHook(char **newval, void **extra, GucSource source)\n{\n\t/* this array _must_ be kept in an order usable by bsearch */\n\tconst char *allowedConninfoKeywords[] = {\n\t\t\"connect_timeout\",\n\t\t\t#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)\n\t\t\"gsslib\",\n\t\t\t#endif\n\t\t\"host\",\n\t\t\"keepalives\",\n\t\t\"keepalives_count\",\n\t\t\"keepalives_idle\",\n\t\t\"keepalives_interval\",\n\t\t\t#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)\n\t\t\"krbsrvname\",\n\t\t\t#endif\n\t\t\"sslcert\",\n\t\t\"sslcompression\",\n\t\t\"sslcrl\",\n\t\t\"sslkey\",\n#if PG_VERSION_NUM >= PG_VERSION_18\n\t\t\"sslkeylogfile\",\n#endif\n\t\t\"sslmode\",\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\t\"sslnegotiation\",\n#endif\n\t\t\"sslrootcert\",\n\t\t\"tcp_user_timeout\",\n\t};\n\tchar *errorMsg = NULL;\n\tbool conninfoValid = CheckConninfo(*newval, allowedConninfoKeywords,\n\t\t\t\t\t\t\t\t\t   lengthof(allowedConninfoKeywords), &errorMsg);\n\n\tif (!conninfoValid)\n\t{\n\t\tGUC_check_errdetail(\"%s\", errorMsg);\n\t}\n\n\treturn conninfoValid;\n}\n\n\n/*\n * CpuPriorityAssignHook changes the priority of the current backend to match\n * the chosen value.\n */\nstatic void\nCpuPriorityAssignHook(int newval, void *extra)\n{\n\tSetOwnPriority(newval);\n}\n\n\n/*\n * NodeConninfoGucAssignHook is the assignment hook for the node_conninfo GUC\n * variable. Though this GUC is a \"string\", we actually parse it as a non-URI\n * PQconninfo key/value setting, storing the resultant PQconninfoOption values\n * using the public functions in connection_configuration.c.\n */\nstatic void\nNodeConninfoGucAssignHook(const char *newval, void *extra)\n{\n\tif (newval == NULL)\n\t{\n\t\tnewval = \"\";\n\t}\n\n\tif (strcmp(newval, NodeConninfo) == 0 && checkAtBootPassed)\n\t{\n\t\t/* It did not change, no need to do anything */\n\t\treturn;\n\t}\n\n\tcheckAtBootPassed = true;\n\n\tPQconninfoOption *optionArray = PQconninfoParse(newval, NULL);\n\tif (optionArray == NULL)\n\t{\n\t\tereport(FATAL, (errmsg(\"cannot parse node_conninfo value\"),\n\t\t\t\t\t\terrdetail(\"The GUC check hook should prevent \"\n\t\t\t\t\t\t\t\t  \"all malformed values.\")));\n\t}\n\n\tResetConnParams();\n\n\tfor (PQconninfoOption *option = optionArray; option->keyword != NULL; option++)\n\t{\n\t\tif (option->val == NULL || option->val[0] == '\\0')\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tAddConnParam(option->keyword, option->val);\n\t}\n\n\tPQconninfoFree(optionArray);\n\n\t/*\n\t * Mark all connections for shutdown, since they have been opened using old\n\t * connection settings. This is mostly important when changing SSL\n\t * parameters, otherwise these would not be applied and connections could\n\t * be unencrypted when the user doesn't want that.\n\t */\n\tCloseAllConnectionsAfterTransaction();\n}\n\n\n/*\n * MaxSharedPoolSizeGucShowHook overrides the value that is shown to the\n * user when the default value has not been set.\n */\nstatic const char *\nMaxSharedPoolSizeGucShowHook(void)\n{\n\tStringInfo newvalue = makeStringInfo();\n\n\tif (MaxSharedPoolSize == 0)\n\t{\n\t\tappendStringInfo(newvalue, \"%d\", GetMaxSharedPoolSize());\n\t}\n\telse\n\t{\n\t\tappendStringInfo(newvalue, \"%d\", MaxSharedPoolSize);\n\t}\n\n\treturn (const char *) newvalue->data;\n}\n\n\n/*\n * LocalPoolSizeGucShowHook overrides the value that is shown to the\n * user when the default value has not been set.\n */\nstatic const char *\nLocalPoolSizeGucShowHook(void)\n{\n\tStringInfo newvalue = makeStringInfo();\n\n\tappendStringInfo(newvalue, \"%d\", GetLocalSharedPoolSize());\n\n\treturn (const char *) newvalue->data;\n}\n\n\n/*\n * CitusAuthHook is a callback for client authentication that Postgres provides.\n * Citus uses this hook to count the number of active backends.\n */\nstatic void\nCitusAuthHook(Port *port, int status)\n{\n\t/*\n\t * We determine the backend type here because other calls in this hook rely\n\t * on it, both IsExternalClientBackend and InitializeBackendData. These\n\t * calls would normally initialize its value based on the application_name\n\t * global, but this global is not set yet at this point in the connection\n\t * initialization. So here we determine it based on the value from Port.\n\t */\n\tDetermineCitusBackendType(port->application_name);\n\n\t/* external connections to not have a GPID immediately */\n\tif (IsExternalClientBackend())\n\t{\n\t\t/*\n\t\t * We raise the shared connection counter pre-emptively. As a result, we may\n\t\t * have scenarios in which a few simultaneous connection attempts prevent\n\t\t * each other from succeeding, but we avoid scenarios where we oversubscribe\n\t\t * the system.\n\t\t *\n\t\t * By also calling RegisterExternalClientBackendCounterDecrement here, we\n\t\t * immediately lower the counter if we throw a FATAL error below. The client\n\t\t * connection counter may temporarily exceed maxClientConnections in between.\n\t\t */\n\t\tRegisterExternalClientBackendCounterDecrement();\n\n\t\tuint32 externalClientCount = IncrementExternalClientBackendCounter();\n\n\t\t/*\n\t\t * Limit non-superuser client connections if citus.max_client_connections\n\t\t * is set.\n\t\t */\n\t\tif (MaxClientConnections >= 0 &&\n\t\t\t!IsSuperuser(port->user_name) &&\n\t\t\texternalClientCount > MaxClientConnections)\n\t\t{\n\t\t\tereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS),\n\t\t\t\t\t\t\terrmsg(\"remaining connection slots are reserved for \"\n\t\t\t\t\t\t\t\t   \"non-replication superuser connections\"),\n\t\t\t\t\t\t\terrdetail(\"the server is configured to accept up to %d \"\n\t\t\t\t\t\t\t\t\t  \"regular client connections\",\n\t\t\t\t\t\t\t\t\t  MaxClientConnections)));\n\t\t}\n\t}\n\n\t/*\n\t * Right after this, but before we assign global pid, this backend might\n\t * get blocked by a DDL as that happens during parsing.\n\t *\n\t * That's why, we now initialize its backend data, with the gpid.\n\t *\n\t * We do this so that this backend gets the chance to show up in\n\t * citus_lock_waits.\n\t *\n\t * We cannot assign a new global PID yet here, because that would require\n\t * reading from catalogs, but that's not allowed this early in the\n\t * connection startup (because no database has been assigned yet).\n\t *\n\t * A second reason is for backends that never call StartupCitusBackend. For\n\t * those we already set the global PID in the backend data here to be able\n\t * to do blocked process detection on connections that are opened over a\n\t * replication connection. A replication connection backend will never call\n\t * StartupCitusBackend, which normally sets up the global PID.\n\t */\n\tInitializeBackendData(port->application_name);\n\n\tIsMainDB = (strncmp(MainDb, \"\", NAMEDATALEN) == 0 ||\n\t\t\t\tstrncmp(MainDb, port->database_name, NAMEDATALEN) == 0);\n\n\t/* let other authentication hooks to kick in first */\n\tif (original_client_auth_hook)\n\t{\n\t\toriginal_client_auth_hook(port, status);\n\t}\n}\n\n\n/*\n * IsSuperuser returns whether the role with the given name is superuser. If\n * the user doesn't exist, this simply returns false instead of throwing an\n * error. This is done to not leak information about users existing or not, in\n * some cases postgres is vague about this on purpose. So, by returning false\n * we let postgres return this possibly vague error message.\n */\nstatic bool\nIsSuperuser(char *roleName)\n{\n\tif (roleName == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tHeapTuple roleTuple = SearchSysCache1(AUTHNAME, CStringGetDatum(roleName));\n\tif (!HeapTupleIsValid(roleTuple))\n\t{\n\t\treturn false;\n\t}\n\n\tForm_pg_authid rform = (Form_pg_authid) GETSTRUCT(roleTuple);\n\tbool isSuperuser = rform->rolsuper;\n\n\tReleaseSysCache(roleTuple);\n\n\treturn isSuperuser;\n}\n\n\n/*\n * CitusObjectAccessHook is called when an object is created.\n *\n * We currently use it to track CREATE EXTENSION citus; operations to make sure we\n * clear the metadata if the transaction is rolled back.\n */\nstatic void\nCitusObjectAccessHook(ObjectAccessType access, Oid classId, Oid objectId, int subId,\n\t\t\t\t\t  void *arg)\n{\n\tif (PrevObjectAccessHook)\n\t{\n\t\tPrevObjectAccessHook(access, classId, objectId, subId, arg);\n\t}\n\n\t/* Checks if the access is post_create and that it's an extension id */\n\tif (access == OAT_POST_CREATE && classId == ExtensionRelationId)\n\t{\n\t\t/* There's currently an engine bug that makes it difficult to check\n\t\t * the provided objectId with extension oid so we will set the value\n\t\t * regardless if it's citus being created */\n\t\tSetCreateCitusTransactionLevel(GetCurrentTransactionNestLevel());\n\t}\n}\n\n\n/*\n * EnableChangeDataCaptureAssignHook is called whenever the\n * citus.enable_change_data_capture setting is changed to dynamically\n * adjust the dynamic_library_path based on the new value.\n */\nstatic void\nEnableChangeDataCaptureAssignHook(bool newval, void *extra)\n{\n\tif (newval)\n\t{\n\t\t/* CDC enabled: add citus_decoders to the path */\n\t\tAdjustDynamicLibraryPathForCdcDecoders();\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/sql/cat_upgrades/add_clone_info_to_pg_dist_node.sql",
    "content": "-- Add replica information columns to pg_dist_node\nALTER TABLE pg_catalog.pg_dist_node ADD COLUMN nodeisclone BOOLEAN NOT NULL DEFAULT FALSE;\nALTER TABLE pg_catalog.pg_dist_node ADD COLUMN nodeprimarynodeid INT4 NOT NULL DEFAULT 0;\n\n-- Add a comment to the table and columns for clarity in \\d output\nCOMMENT ON COLUMN pg_catalog.pg_dist_node.nodeisclone IS 'Indicates if this node is a replica of another node.';\nCOMMENT ON COLUMN pg_catalog.pg_dist_node.nodeprimarynodeid IS 'If nodeisclone is true, this stores the nodeid of its primary node.';\n"
  },
  {
    "path": "src/backend/distributed/sql/cat_upgrades/remove_clone_info_to_pg_dist_node.sql",
    "content": "-- Remove clone information columns to pg_dist_node\nALTER TABLE pg_catalog.pg_dist_node DROP COLUMN IF EXISTS nodeisclone;\nALTER TABLE pg_catalog.pg_dist_node DROP COLUMN IF EXISTS nodeprimarynodeid;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--10.0-1--10.0-2.sql",
    "content": "-- citus--10.0-1--10.0-2\n\n--#include \"../../columnar/sql/columnar--10.0-1--10.0-2.sql\"\nDO $check_columnar$\nBEGIN\n  IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_extension AS e\n             INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid)\n             INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid)\n             WHERE e.extname='citus_columnar' and p.proname = 'columnar_handler'\n  ) THEN\n    #include \"../../columnar/sql/columnar--10.0-1--10.0-2.sql\"\n  END IF;\nEND;\n$check_columnar$;\n\nGRANT SELECT ON public.citus_tables TO public;\n\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--10.0-2--10.0-3.sql",
    "content": "-- citus--10.0-2--10.0-3\n\n#include \"udfs/citus_update_table_statistics/10.0-3.sql\"\n\nCREATE OR REPLACE FUNCTION master_update_table_statistics(relation regclass)\nRETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_update_table_statistics$$;\nCOMMENT ON FUNCTION pg_catalog.master_update_table_statistics(regclass)\n\tIS 'updates shard statistics of the given table';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_get_active_worker_nodes(OUT node_name text, OUT node_port bigint)\n    RETURNS SETOF record\n    LANGUAGE C STRICT ROWS 100\n    AS 'MODULE_PATHNAME', $$citus_get_active_worker_nodes$$;\nCOMMENT ON FUNCTION pg_catalog.citus_get_active_worker_nodes()\n    IS 'fetch set of active worker nodes';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--10.0-3--10.0-4.sql",
    "content": "-- citus--10.0-3--10.0-4\n\n-- This migration file aims to fix 2 issues with upgrades on clusters\n\n-- 1. a bug in public schema dependency for citus_tables view.\n--\n-- Users who do not have public schema in their clusters were unable to upgrade\n-- to Citus 10.x due to the citus_tables view that used to be created in public\n-- schema\n\n#include \"udfs/citus_tables/10.0-4.sql\"\n\n-- 2. a bug in our PG upgrade functions\n--\n-- Users who took the 9.5-2--10.0-1 upgrade path already have the fix, but users\n-- who took the 9.5-1--10.0-1 upgrade path do not. Hence, we repeat the CREATE OR\n-- REPLACE from the 9.5-2 definition for citus_prepare_pg_upgrade.\n\n#include \"udfs/citus_prepare_pg_upgrade/9.5-2.sql\"\n#include \"udfs/citus_finish_pg_upgrade/10.0-4.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--10.0-4--10.1-1.sql",
    "content": "-- citus--10.0-4--10.1-1\n\n-- add the current database to the distributed objects if not already in there.\n-- this is to reliably propagate some of the alter database commands that might be\n-- supported.\n\nINSERT INTO citus.pg_dist_object SELECT\n  'pg_catalog.pg_database'::regclass::oid AS oid,\n  (SELECT oid FROM pg_database WHERE datname = current_database()) as objid,\n  0 as objsubid\nON CONFLICT DO NOTHING;\n\n--#include \"../../columnar/sql/columnar--10.0-3--10.1-1.sql\"\nDO $check_columnar$\nBEGIN\n  IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_extension AS e\n             INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid)\n             INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid)\n             WHERE e.extname='citus_columnar' and p.proname = 'columnar_handler'\n  ) THEN\n      #include \"../../columnar/sql/columnar--10.0-3--10.1-1.sql\"\n  END IF;\nEND;\n$check_columnar$;\n#include \"udfs/create_distributed_table/10.1-1.sql\";\n#include \"udfs/worker_partitioned_relation_total_size/10.1-1.sql\"\n#include \"udfs/worker_partitioned_relation_size/10.1-1.sql\"\n#include \"udfs/worker_partitioned_table_size/10.1-1.sql\"\n#include \"udfs/citus_prepare_pg_upgrade/10.1-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/10.1-1.sql\"\n#include \"udfs/citus_local_disk_space_stats/10.1-1.sql\"\n#include \"udfs/get_rebalance_table_shards_plan/10.1-1.sql\"\n#include \"udfs/citus_add_rebalance_strategy/10.1-1.sql\"\n\nALTER TABLE pg_catalog.pg_dist_rebalance_strategy ADD COLUMN improvement_threshold float4 NOT NULL default 0;\nUPDATE pg_catalog.pg_dist_rebalance_strategy SET improvement_threshold = 0.5 WHERE name = 'by_disk_size';\n\n#include \"udfs/get_rebalance_progress/10.1-1.sql\"\n\n-- use streaming replication when replication factor = 1\nWITH replicated_shards AS (\n    SELECT shardid\n    FROM pg_dist_placement\n    WHERE shardstate = 1 OR shardstate = 3\n    GROUP BY shardid\n    HAVING count(*) <> 1 ),\nreplicated_relations AS (\n    SELECT DISTINCT logicalrelid\n    FROM pg_dist_shard\n    JOIN replicated_shards\n    USING (shardid)\n)\nUPDATE pg_dist_partition\nSET repmodel = 's'\nWHERE repmodel = 'c'\n    AND partmethod = 'h'\n    AND logicalrelid NOT IN (SELECT * FROM replicated_relations);\n#include \"udfs/citus_shards/10.1-1.sql\"\n\nDROP TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger ON pg_catalog.pg_dist_rebalance_strategy;\nDROP FUNCTION citus_internal.pg_dist_rebalance_strategy_enterprise_check();\n\n#include \"udfs/citus_cleanup_orphaned_shards/10.1-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--10.1-1--10.2-1.sql",
    "content": "-- citus--10.1-1--10.2-1\n\n-- bump version to 10.2-1\n\nDROP FUNCTION IF EXISTS pg_catalog.stop_metadata_sync_to_node(text, integer);\nGRANT ALL ON FUNCTION pg_catalog.worker_record_sequence_dependency(regclass,regclass,name) TO PUBLIC;\n\n-- the same shard cannot have placements on different nodes\nALTER TABLE pg_catalog.pg_dist_placement ADD CONSTRAINT placement_shardid_groupid_unique_index UNIQUE (shardid, groupid);\n\n#include \"udfs/stop_metadata_sync_to_node/10.2-1.sql\"\n--#include \"../../columnar/sql/columnar--10.1-1--10.2-1.sql\"\nDO $check_columnar$\nBEGIN\n  IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_extension AS e\n             INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid)\n             INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid)\n             WHERE e.extname='citus_columnar' and p.proname = 'columnar_handler'\n  ) THEN\n      #include \"../../columnar/sql/columnar--10.1-1--10.2-1.sql\"\n  END IF;\nEND;\n$check_columnar$;\n\n#include \"udfs/citus_internal_add_partition_metadata/10.2-1.sql\";\n#include \"udfs/citus_internal_add_shard_metadata/10.2-1.sql\";\n#include \"udfs/citus_internal_add_placement_metadata/10.2-1.sql\";\n#include \"udfs/citus_internal_update_placement_metadata/10.2-1.sql\";\n#include \"udfs/citus_internal_delete_shard_metadata/10.2-1.sql\";\n#include \"udfs/citus_internal_update_relation_colocation/10.2-1.sql\";\n#include \"udfs/create_time_partitions/10.2-1.sql\"\n#include \"udfs/drop_old_time_partitions/10.2-1.sql\"\n#include \"udfs/get_missing_time_partition_ranges/10.2-1.sql\"\n#include \"udfs/worker_nextval/10.2-1.sql\"\n\n\nDROP FUNCTION pg_catalog.citus_drop_all_shards(regclass, text, text);\nCREATE FUNCTION pg_catalog.citus_drop_all_shards(logicalrelid regclass,\n                                                 schema_name text,\n                                                 table_name text,\n                                                 drop_shards_metadata_only boolean default false)\n    RETURNS integer\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_drop_all_shards$$;\nCOMMENT ON FUNCTION pg_catalog.citus_drop_all_shards(regclass, text, text, boolean)\n    IS 'drop all shards in a relation and update metadata';\n#include \"udfs/citus_drop_trigger/10.2-1.sql\";\n#include \"udfs/citus_prepare_pg_upgrade/10.2-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/10.2-1.sql\"\n\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--10.2-1--10.2-2.sql",
    "content": "-- citus--10.2-1--10.2-2\n\n-- bump version to 10.2-2\n\n--#include \"../../columnar/sql/columnar--10.2-1--10.2-2.sql\"\nDO $check_columnar$\nBEGIN\n  IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_extension AS e\n             INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid)\n             INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid)\n             WHERE e.extname='citus_columnar' and p.proname = 'columnar_handler'\n  ) THEN\n      #include \"../../columnar/sql/columnar--10.2-1--10.2-2.sql\"\n  END IF;\nEND;\n$check_columnar$;\n\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--10.2-2--10.2-3.sql",
    "content": "-- citus--10.2-2--10.2-3\n\n-- bump version to 10.2-3\n\n--#include \"../../columnar/sql/columnar--10.2-2--10.2-3.sql\"\nDO $check_columnar$\nBEGIN\n  IF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_extension AS e\n             INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid)\n             INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid)\n             WHERE e.extname='citus_columnar' and p.proname = 'columnar_handler'\n  ) THEN\n      #include \"../../columnar/sql/columnar--10.2-2--10.2-3.sql\"\n  END IF;\nEND;\n$check_columnar$;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--10.2-3--10.2-4.sql",
    "content": "-- citus--10.2-3--10.2-4\n\n-- bump version to 10.2-4\n\nDO $check_columnar$\nBEGIN\nIF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_extension AS e\n             INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid)\n             INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid)\n             WHERE e.extname='citus_columnar' and p.proname = 'columnar_handler'\n  ) THEN\n    #include \"../../columnar/sql/columnar--10.2-3--10.2-4.sql\"\nEND IF;\nEND;\n$check_columnar$;\n\n#include \"udfs/fix_partition_shard_index_names/10.2-4.sql\"\n#include \"udfs/fix_all_partition_shard_index_names/10.2-4.sql\"\n#include \"udfs/worker_fix_partition_shard_index_names/10.2-4.sql\"\n#include \"udfs/citus_finish_pg_upgrade/10.2-4.sql\"\n\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--10.2-4--10.2-5.sql",
    "content": "#include \"udfs/citus_finish_pg_upgrade/10.2-5.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--10.2-5--10.2-4.sql",
    "content": "#include \"udfs/citus_finish_pg_upgrade/10.2-4.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--10.2-5--11.0-1.sql",
    "content": "-- citus--10.2-5--11.0-1\n\n-- bump version to 11.0-1\n#include \"udfs/citus_disable_node/11.0-1.sql\"\n#include \"udfs/create_distributed_function/11.0-1.sql\"\n\n#include \"udfs/citus_check_connection_to_node/11.0-1.sql\"\n#include \"udfs/citus_check_cluster_node_health/11.0-1.sql\"\n#include \"udfs/citus_shards_on_worker/11.0-1.sql\"\n#include \"udfs/citus_shard_indexes_on_worker/11.0-1.sql\"\n\n#include \"udfs/citus_internal_add_object_metadata/11.0-1.sql\"\n#include \"udfs/citus_internal_add_colocation_metadata/11.0-1.sql\"\n#include \"udfs/citus_internal_delete_colocation_metadata/11.0-1.sql\"\n#include \"udfs/citus_run_local_command/11.0-1.sql\"\n#include \"udfs/worker_drop_sequence_dependency/11.0-1.sql\"\n#include \"udfs/worker_drop_shell_table/11.0-1.sql\"\n\n#include \"udfs/get_all_active_transactions/11.0-1.sql\"\n#include \"udfs/get_global_active_transactions/11.0-1.sql\"\n\n#include \"udfs/citus_internal_local_blocked_processes/11.0-1.sql\"\n#include \"udfs/citus_internal_global_blocked_processes/11.0-1.sql\"\n\n#include \"udfs/run_command_on_all_nodes/11.0-1.sql\"\n#include \"udfs/citus_stat_activity/11.0-1.sql\"\n\n#include \"udfs/worker_create_or_replace_object/11.0-1.sql\"\n#include \"udfs/citus_isolation_test_session_is_blocked/11.0-1.sql\"\n#include \"udfs/citus_blocking_pids/11.0-1.sql\"\n#include \"udfs/citus_calculate_gpid/11.0-1.sql\"\n#include \"udfs/citus_backend_gpid/11.0-1.sql\"\n\nDROP VIEW IF EXISTS pg_catalog.citus_lock_waits;\nDROP VIEW IF EXISTS pg_catalog.citus_dist_stat_activity;\nDROP VIEW IF EXISTS pg_catalog.citus_worker_stat_activity;\nDROP FUNCTION IF EXISTS pg_catalog.citus_dist_stat_activity();\nDROP FUNCTION IF EXISTS pg_catalog.citus_worker_stat_activity();\n#include \"udfs/citus_dist_stat_activity/11.0-1.sql\"\n\n-- a very simple helper function defined for citus_lock_waits\nCREATE OR REPLACE FUNCTION get_nodeid_for_groupid(groupIdInput int) RETURNS int AS $$\nDECLARE\n\treturnNodeNodeId int := 0;\nbegin\n\tSELECT nodeId into returnNodeNodeId FROM pg_dist_node WHERE groupid = groupIdInput and nodecluster = current_setting('citus.cluster_name');\n\tRETURN returnNodeNodeId;\nend\n$$ LANGUAGE plpgsql;\n\n#include \"udfs/citus_lock_waits/11.0-1.sql\"\n\n#include \"udfs/pg_cancel_backend/11.0-1.sql\"\n#include \"udfs/pg_terminate_backend/11.0-1.sql\"\n#include \"udfs/worker_partition_query_result/11.0-1.sql\"\n\nDROP FUNCTION pg_catalog.master_apply_delete_command(text);\nDROP FUNCTION pg_catalog.master_get_table_metadata(text);\nDROP FUNCTION pg_catalog.master_append_table_to_shard(bigint, text, text, integer);\n\n-- all existing citus local tables are auto converted\n-- none of the other tables can have auto-converted as true\nALTER TABLE pg_catalog.pg_dist_partition ADD COLUMN autoconverted boolean DEFAULT false;\nALTER TABLE citus.pg_dist_object ADD COLUMN force_delegation bool DEFAULT NULL;\nUPDATE pg_catalog.pg_dist_partition SET autoconverted = TRUE WHERE partmethod = 'n' AND repmodel = 's';\n\nREVOKE ALL ON FUNCTION start_metadata_sync_to_node(text, integer) FROM PUBLIC;\nREVOKE ALL ON FUNCTION stop_metadata_sync_to_node(text, integer,bool) FROM PUBLIC;\n\nDO LANGUAGE plpgsql\n$$\nBEGIN\n    IF EXISTS (SELECT 1 FROM pg_dist_shard where shardstorage = 'c') THEN\n\t    RAISE EXCEPTION 'cstore_fdw tables are deprecated as of Citus 11.0'\n        USING HINT = 'Install Citus 10.2 and convert your cstore_fdw tables to the columnar access method before upgrading further';\n\tEND IF;\nEND;\n$$;\n\n-- Here we keep track of partitioned tables that exists before Citus 11\n-- where we need to call fix_all_partition_shard_index_names() before\n-- metadata is synced. Note that after citus-11, we automatically\n-- adjust the indexes so we only need to fix existing indexes\nDO LANGUAGE plpgsql\n$$\nDECLARE\n  partitioned_table_exists bool :=false;\nBEGIN\n      SELECT count(*) > 0 INTO partitioned_table_exists FROM pg_dist_partition p JOIN pg_class c ON p.logicalrelid = c.oid WHERE c.relkind = 'p';\n      UPDATE pg_dist_node_metadata SET metadata=jsonb_set(metadata, '{partitioned_citus_table_exists_pre_11}', to_jsonb(partitioned_table_exists), true);\nEND;\n$$;\n\n#include \"udfs/citus_finalize_upgrade_to_citus11/11.0-1.sql\"\n\nALTER TABLE citus.pg_dist_object SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.pg_dist_object TO public;\n#include \"udfs/citus_prepare_pg_upgrade/11.0-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/11.0-1.sql\"\n\n#include \"udfs/citus_nodename_for_nodeid/11.0-1.sql\"\n#include \"udfs/citus_nodeport_for_nodeid/11.0-1.sql\"\n\n#include \"udfs/citus_nodeid_for_gpid/11.0-1.sql\"\n#include \"udfs/citus_pid_for_gpid/11.0-1.sql\"\n\n#include \"udfs/citus_coordinator_nodeid/11.0-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--11.0-1--11.0-2.sql",
    "content": "#include \"udfs/citus_shards_on_worker/11.0-2.sql\"\n#include \"udfs/citus_shard_indexes_on_worker/11.0-2.sql\"\n#include \"udfs/citus_is_coordinator/11.0-2.sql\"\n#include \"udfs/citus_disable_node/11.0-2.sql\"\n#include \"udfs/run_command_on_coordinator/11.0-2.sql\"\n#include \"udfs/start_metadata_sync_to_all_nodes/11.0-2.sql\"\n#include \"udfs/citus_finalize_upgrade_to_citus11/11.0-2.sql\"\n#include \"udfs/citus_finish_citus_upgrade/11.0-2.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--11.0-2--11.0-3.sql",
    "content": "#include \"udfs/citus_finalize_upgrade_to_citus11/11.0-3.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--11.0-3--11.0-4.sql",
    "content": "#include \"udfs/citus_finish_pg_upgrade/11.0-4.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--11.0-4--11.0-3.sql",
    "content": "#include \"udfs/citus_finish_pg_upgrade/11.0-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--11.0-4--11.1-1.sql",
    "content": "#include \"udfs/citus_locks/11.1-1.sql\"\n#include \"udfs/citus_tables/11.1-1.sql\"\n#include \"udfs/citus_shards/11.1-1.sql\"\n#include \"udfs/create_distributed_table_concurrently/11.1-1.sql\"\n#include \"udfs/citus_internal_delete_partition_metadata/11.1-1.sql\"\n#include \"udfs/citus_copy_shard_placement/11.1-1.sql\"\n\n-- We should not introduce breaking sql changes to upgrade files after they are released.\n-- We did that for worker_fetch_foreign_file in v9.0.0 and worker_repartition_cleanup in v9.2.0.\n-- When we try to drop those udfs in that file, they were missing for some clients unexpectedly\n-- due to buggy changes in old upgrade scripts. For that case, the fix is to change DROP statements\n-- with DROP IF EXISTS for those 2 udfs in 11.0-4--11.1-1.\n-- Fixes an upgrade problem for worker_fetch_foreign_file when upgrade starts from 8.3 up to 11.1\n-- Fixes an upgrade problem for worker_repartition_cleanup when upgrade starts from 9.1 up to 11.1\n-- Refer the related PR https://github.com/citusdata/citus/pull/6441 for more information\nDROP FUNCTION IF EXISTS pg_catalog.worker_fetch_foreign_file(text, text, bigint, text[], integer[]);\nDROP FUNCTION IF EXISTS pg_catalog.worker_repartition_cleanup(bigint);\n\nDROP FUNCTION pg_catalog.worker_create_schema(bigint,text);\nDROP FUNCTION pg_catalog.worker_cleanup_job_schema_cache();\nDROP FUNCTION pg_catalog.worker_fetch_partition_file(bigint, integer, integer, integer, text, integer);\nDROP FUNCTION pg_catalog.worker_hash_partition_table(bigint, integer, text, text, oid, anyarray);\nDROP FUNCTION pg_catalog.worker_merge_files_into_table(bigint, integer, text[], text[]);\nDROP FUNCTION pg_catalog.worker_range_partition_table(bigint, integer, text, text, oid, anyarray);\n\nDO $check_columnar$\nBEGIN\nIF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_extension AS e\n             INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid)\n             INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid)\n             WHERE e.extname='citus_columnar' and p.proname = 'columnar_handler'\n  ) THEN\n    #include \"../../columnar/sql/columnar--11.0-3--11.1-1.sql\"\nEND IF;\nEND;\n$check_columnar$;\n\n-- If upgrading citus, the columnar objects are already being a part of the\n-- citus extension, and must be detached so that they can be attached\n-- to the citus_columnar extension.\nDO $check_citus$\nBEGIN\n  IF EXISTS (SELECT 1 FROM pg_catalog.pg_extension AS e\n             INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid)\n             INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid)\n             WHERE e.extname='citus' and p.proname = 'columnar_handler'\n  ) THEN\n    ALTER EXTENSION citus DROP SCHEMA columnar;\n    ALTER EXTENSION citus DROP SCHEMA columnar_internal;\n    ALTER EXTENSION citus DROP SEQUENCE columnar_internal.storageid_seq;\n\n    -- columnar tables\n    ALTER EXTENSION citus DROP TABLE columnar_internal.options;\n    ALTER EXTENSION citus DROP TABLE columnar_internal.stripe;\n    ALTER EXTENSION citus DROP TABLE columnar_internal.chunk_group;\n    ALTER EXTENSION citus DROP TABLE columnar_internal.chunk;\n\n    ALTER EXTENSION citus DROP FUNCTION columnar_internal.columnar_handler;\n    ALTER EXTENSION citus DROP ACCESS METHOD columnar;\n    ALTER EXTENSION citus DROP FUNCTION pg_catalog.alter_columnar_table_set;\n    ALTER EXTENSION citus DROP FUNCTION pg_catalog.alter_columnar_table_reset;\n    ALTER EXTENSION citus DROP FUNCTION columnar.get_storage_id;\n\n    -- columnar view\n    ALTER EXTENSION citus DROP VIEW columnar.storage;\n    ALTER EXTENSION citus DROP VIEW columnar.options;\n    ALTER EXTENSION citus DROP VIEW columnar.stripe;\n    ALTER EXTENSION citus DROP VIEW columnar.chunk_group;\n    ALTER EXTENSION citus DROP VIEW columnar.chunk;\n\n    -- functions under citus_internal for columnar\n    ALTER EXTENSION citus DROP FUNCTION citus_internal.upgrade_columnar_storage;\n    ALTER EXTENSION citus DROP FUNCTION citus_internal.downgrade_columnar_storage;\n    ALTER EXTENSION citus DROP FUNCTION citus_internal.columnar_ensure_am_depends_catalog;\n\n  END IF;\nEND $check_citus$;\n#include \"udfs/citus_prepare_pg_upgrade/11.1-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/11.1-1.sql\"\n\nDROP FUNCTION pg_catalog.get_all_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4,\n                                                     OUT worker_query BOOL, OUT transaction_number int8, OUT transaction_stamp timestamptz,\n                                                     OUT global_pid int8);\n#include \"udfs/get_all_active_transactions/11.1-1.sql\"\n#include \"udfs/citus_split_shard_by_split_points/11.1-1.sql\"\n#include \"udfs/worker_split_copy/11.1-1.sql\"\n#include \"udfs/worker_copy_table_to_node/11.1-1.sql\"\n#include \"udfs/worker_split_shard_replication_setup/11.1-1.sql\"\n#include \"udfs/citus_isolation_test_session_is_blocked/11.1-1.sql\"\n#include \"udfs/replicate_reference_tables/11.1-1.sql\"\n#include \"udfs/worker_split_shard_release_dsm/11.1-1.sql\"\n\nDROP FUNCTION pg_catalog.isolate_tenant_to_new_shard(table_name regclass, tenant_id \"any\", cascade_option text);\n#include \"udfs/isolate_tenant_to_new_shard/11.1-1.sql\"\n\n-- Table of records to:\n-- 1) Cleanup leftover resources after a failure\n-- 2) Deferred drop of old shard placements after a split.\n#include \"udfs/citus_cleanup_orphaned_resources/11.1-1.sql\"\n\nCREATE TABLE citus.pg_dist_cleanup (\n    record_id bigint primary key,\n    operation_id bigint not null,\n    object_type int not null,\n    object_name text not null,\n    node_group_id int not null,\n    policy_type int not null\n);\nALTER TABLE citus.pg_dist_cleanup SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.pg_dist_cleanup TO public;\n\n-- Sequence used to generate operation Ids and record Ids in pg_dist_cleanup_record.\nCREATE SEQUENCE citus.pg_dist_operationid_seq;\nALTER SEQUENCE citus.pg_dist_operationid_seq SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.pg_dist_operationid_seq TO public;\n\nCREATE SEQUENCE citus.pg_dist_cleanup_recordid_seq;\nALTER SEQUENCE citus.pg_dist_cleanup_recordid_seq SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.pg_dist_cleanup_recordid_seq TO public;\n\n-- We recreate these two UDF from 11.0-1 on purpose, because we changed their\n-- old definition. By recreating it here upgrades also pick up the new changes.\n#include \"udfs/pg_cancel_backend/11.0-1.sql\"\n#include \"udfs/pg_terminate_backend/11.0-1.sql\"\n\nCREATE TYPE citus.citus_job_status AS ENUM ('scheduled', 'running', 'finished', 'cancelling', 'cancelled', 'failing', 'failed');\nALTER TYPE citus.citus_job_status SET SCHEMA pg_catalog;\n\nCREATE TABLE citus.pg_dist_background_job (\n    job_id bigserial NOT NULL,\n    state pg_catalog.citus_job_status DEFAULT 'scheduled' NOT NULL,\n    job_type name NOT NULL,\n    description text NOT NULL,\n    started_at timestamptz,\n    finished_at timestamptz,\n\n    CONSTRAINT pg_dist_background_job_pkey PRIMARY KEY (job_id)\n);\nALTER TABLE citus.pg_dist_background_job SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.pg_dist_background_job TO PUBLIC;\nGRANT SELECT ON pg_catalog.pg_dist_background_job_job_id_seq TO PUBLIC;\n\nCREATE TYPE citus.citus_task_status AS ENUM ('blocked', 'runnable', 'running', 'done', 'cancelling', 'error', 'unscheduled', 'cancelled');\nALTER TYPE citus.citus_task_status SET SCHEMA pg_catalog;\n\nCREATE TABLE citus.pg_dist_background_task(\n    job_id bigint NOT NULL REFERENCES pg_catalog.pg_dist_background_job(job_id),\n    task_id bigserial NOT NULL,\n    owner regrole NOT NULL DEFAULT CURRENT_USER::regrole,\n    pid integer,\n    status pg_catalog.citus_task_status default 'runnable' NOT NULL,\n    command text NOT NULL,\n    retry_count integer,\n    not_before timestamptz, -- can be null to indicate no delay for start of the task, will be set on failure to delay retries\n    message text NOT NULL DEFAULT '',\n\n    CONSTRAINT pg_dist_background_task_pkey PRIMARY KEY (task_id),\n    CONSTRAINT pg_dist_background_task_job_id_task_id UNIQUE (job_id, task_id) -- required for FK's to enforce tasks only reference other tasks within the same job\n);\nALTER TABLE citus.pg_dist_background_task SET SCHEMA pg_catalog;\nCREATE INDEX pg_dist_background_task_status_task_id_index ON pg_catalog.pg_dist_background_task USING btree(status, task_id);\nGRANT SELECT ON pg_catalog.pg_dist_background_task TO PUBLIC;\nGRANT SELECT ON pg_catalog.pg_dist_background_task_task_id_seq TO PUBLIC;\n\nCREATE TABLE citus.pg_dist_background_task_depend(\n   job_id bigint NOT NULL REFERENCES pg_catalog.pg_dist_background_job(job_id) ON DELETE CASCADE,\n   task_id bigint NOT NULL,\n   depends_on bigint NOT NULL,\n\n   PRIMARY KEY (job_id, task_id, depends_on),\n   FOREIGN KEY (job_id, task_id) REFERENCES pg_catalog.pg_dist_background_task (job_id, task_id) ON DELETE CASCADE,\n   FOREIGN KEY (job_id, depends_on) REFERENCES pg_catalog.pg_dist_background_task (job_id, task_id) ON DELETE CASCADE\n);\n\nALTER TABLE citus.pg_dist_background_task_depend SET SCHEMA pg_catalog;\nCREATE INDEX pg_dist_background_task_depend_task_id ON pg_catalog.pg_dist_background_task_depend USING btree(job_id, task_id);\nCREATE INDEX pg_dist_background_task_depend_depends_on ON pg_catalog.pg_dist_background_task_depend USING btree(job_id, depends_on);\nGRANT SELECT ON pg_catalog.pg_dist_background_task_depend TO PUBLIC;\n\n#include \"udfs/citus_job_wait/11.1-1.sql\"\n#include \"udfs/citus_job_cancel/11.1-1.sql\"\n#include \"udfs/citus_rebalance_start/11.1-1.sql\"\n#include \"udfs/citus_rebalance_stop/11.1-1.sql\"\n#include \"udfs/citus_rebalance_wait/11.1-1.sql\"\n#include \"udfs/get_rebalance_progress/11.1-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--11.1-1--11.2-1.sql",
    "content": "-- citus--11.1-1--11.2-1\n\nDROP FUNCTION pg_catalog.worker_append_table_to_shard(text, text, text, integer);\n\n#include \"udfs/get_rebalance_progress/11.2-1.sql\"\n#include \"udfs/citus_isolation_test_session_is_blocked/11.2-1.sql\"\n#include \"datatypes/citus_cluster_clock/11.2-1.sql\"\n#include \"udfs/citus_get_node_clock/11.2-1.sql\"\n#include \"udfs/citus_get_transaction_clock/11.2-1.sql\"\n#include \"udfs/citus_is_clock_after/11.2-1.sql\"\n#include \"udfs/citus_internal_adjust_local_clock_to_remote/11.2-1.sql\"\n#include \"udfs/citus_job_list/11.2-1.sql\"\n#include \"udfs/citus_job_status/11.2-1.sql\"\n#include \"udfs/citus_rebalance_status/11.2-1.sql\"\n#include \"udfs/worker_split_shard_replication_setup/11.2-1.sql\"\n#include \"udfs/citus_task_wait/11.2-1.sql\"\n#include \"udfs/citus_prepare_pg_upgrade/11.2-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/11.2-1.sql\"\n#include \"udfs/citus_copy_shard_placement/11.2-1.sql\"\n#include \"udfs/citus_move_shard_placement/11.2-1.sql\"\n#include \"udfs/citus_internal_add_placement_metadata/11.2-1.sql\";\n\n-- drop orphaned shards after inserting records for them into pg_dist_cleanup\nINSERT INTO pg_dist_cleanup\n    SELECT nextval('pg_dist_cleanup_recordid_seq'), 0, 1, shard_name(sh.logicalrelid, sh.shardid) AS object_name, plc.groupid AS node_group_id, 0\n        FROM pg_dist_placement plc\n        JOIN pg_dist_shard sh ON sh.shardid = plc.shardid\n        WHERE plc.shardstate = 4;\n\nDELETE FROM pg_dist_placement WHERE shardstate = 4;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--11.2-1--11.2-2.sql",
    "content": "-- citus--11.2-1--11.2-2\n#include \"udfs/worker_adjust_identity_column_seq_ranges/11.2-2.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--11.2-2--11.3-1.sql",
    "content": "-- citus--11.2-1--11.3-1\n#include \"udfs/repl_origin_helper/11.3-1.sql\"\nALTER TABLE pg_catalog.pg_dist_authinfo REPLICA IDENTITY USING INDEX pg_dist_authinfo_identification_index;\nALTER TABLE pg_catalog.pg_dist_partition REPLICA IDENTITY USING INDEX pg_dist_partition_logical_relid_index;\nALTER TABLE pg_catalog.pg_dist_placement REPLICA IDENTITY USING INDEX pg_dist_placement_placementid_index;\nALTER TABLE pg_catalog.pg_dist_rebalance_strategy REPLICA IDENTITY USING INDEX pg_dist_rebalance_strategy_name_key;\nALTER TABLE pg_catalog.pg_dist_shard REPLICA IDENTITY USING INDEX pg_dist_shard_shardid_index;\nALTER TABLE pg_catalog.pg_dist_transaction REPLICA IDENTITY USING INDEX pg_dist_transaction_unique_constraint;\n\n#include \"udfs/worker_drop_all_shell_tables/11.3-1.sql\"\n#include \"udfs/citus_internal_mark_node_not_synced/11.3-1.sql\"\n#include \"udfs/citus_stat_tenants_local/11.3-1.sql\"\n#include \"udfs/citus_stat_tenants/11.3-1.sql\"\n\n#include \"udfs/citus_stat_tenants_local_reset/11.3-1.sql\"\n#include \"udfs/citus_stat_tenants_reset/11.3-1.sql\"\n\n-- we introduce nodes_involved, which will be used internally to\n-- limit the number of parallel tasks running per node\nALTER TABLE pg_catalog.pg_dist_background_task ADD COLUMN nodes_involved int[] DEFAULT NULL;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--11.3-1--11.3-2.sql",
    "content": "DROP VIEW citus_shards;\nDROP VIEW IF EXISTS pg_catalog.citus_tables;\nDROP VIEW IF EXISTS public.citus_tables;\nDROP FUNCTION citus_shard_sizes;\n\n#include \"udfs/citus_shard_sizes/11.3-2.sql\"\n\n#include \"udfs/citus_shards/11.3-2.sql\"\n#include \"udfs/citus_tables/11.3-2.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--11.3-2--12.0-1.sql",
    "content": "-- citus--11.3-1--12.0-1\n\n-- bump version to 12.0-1\n\nCREATE TABLE citus.pg_dist_schema (\n    schemaid oid NOT NULL,\n    colocationid int NOT NULL,\n    CONSTRAINT pg_dist_schema_pkey PRIMARY KEY (schemaid),\n    CONSTRAINT pg_dist_schema_unique_colocationid_index UNIQUE (colocationid)\n);\n\nALTER TABLE citus.pg_dist_schema SET SCHEMA pg_catalog;\n\nGRANT SELECT ON pg_catalog.pg_dist_schema TO public;\n\n-- udfs used to modify pg_dist_schema on workers, to sync metadata\n#include \"udfs/citus_internal_add_tenant_schema/12.0-1.sql\"\n#include \"udfs/citus_internal_delete_tenant_schema/12.0-1.sql\"\n\n#include \"udfs/citus_prepare_pg_upgrade/12.0-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/12.0-1.sql\"\n\n-- udfs used to modify pg_dist_schema globally via drop trigger\n#include \"udfs/citus_internal_unregister_tenant_schema_globally/12.0-1.sql\"\n#include \"udfs/citus_drop_trigger/12.0-1.sql\"\n\n#include \"udfs/citus_tables/12.0-1.sql\"\nDROP VIEW citus_shards;\n#include \"udfs/citus_shards/12.0-1.sql\"\n\n#include \"udfs/citus_schemas/12.0-1.sql\"\n\n-- udfs used to include schema-based tenants in tenant monitoring\n#include \"udfs/citus_stat_tenants_local/12.0-1.sql\"\n\n-- udfs to convert a regular/tenant schema to a tenant/regular schema\n#include \"udfs/citus_schema_distribute/12.0-1.sql\"\n#include \"udfs/citus_schema_undistribute/12.0-1.sql\"\n\n#include \"udfs/drop_old_time_partitions/12.0-1.sql\"\n#include \"udfs/get_missing_time_partition_ranges/12.0-1.sql\"\n\n-- Update the default rebalance strategy to 'by_disk_size', but only if the\n-- default is currently 'by_shard_count'\nSELECT citus_set_default_rebalance_strategy(name)\nFROM pg_dist_rebalance_strategy\nWHERE name = 'by_disk_size'\n    AND (SELECT default_strategy FROM pg_dist_rebalance_strategy WHERE name = 'by_shard_count');\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--12.0-1--12.1-1.sql",
    "content": "-- citus--12.0-1--12.1-1\n\n-- bump version to 12.1-1\n\n#include \"udfs/citus_pause_node_within_txn/12.1-1.sql\"\n#include \"udfs/citus_prepare_pg_upgrade/12.1-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/12.1-1.sql\"\n\n#include \"udfs/citus_internal_update_none_dist_table_metadata/12.1-1.sql\"\n#include \"udfs/citus_internal_delete_placement_metadata/12.1-1.sql\"\n\n#include \"udfs/citus_schema_move/12.1-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--12.1-1--13.0-1.sql",
    "content": "-- citus--12.1-1--13.0-1.sql\n\n-- bump version to 13.0-1\n#include \"udfs/citus_prepare_pg_upgrade/13.0-1.sql\"\n#include \"udfs/create_time_partitions/13.0-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--13.0-1--13.1-1.sql",
    "content": "-- citus--13.0-1--13.1-1\n-- bump version to 13.1-1\n\n#include \"udfs/citus_internal_database_command/13.1-1.sql\"\n#include \"udfs/citus_add_rebalance_strategy/13.1-1.sql\"\n\nDROP FUNCTION pg_catalog.citus_unmark_object_distributed(oid, oid, int);\n#include \"udfs/citus_unmark_object_distributed/13.1-1.sql\"\n\nALTER TABLE pg_catalog.pg_dist_transaction ADD COLUMN outer_xid xid8;\n\n#include \"udfs/citus_internal_acquire_citus_advisory_object_class_lock/13.1-1.sql\"\n\nGRANT USAGE ON SCHEMA citus_internal TO PUBLIC;\nREVOKE ALL ON FUNCTION citus_internal.find_groupid_for_node FROM PUBLIC;\nREVOKE ALL ON FUNCTION citus_internal.pg_dist_node_trigger_func FROM PUBLIC;\nREVOKE ALL ON FUNCTION citus_internal.pg_dist_rebalance_strategy_trigger_func FROM PUBLIC;\nREVOKE ALL ON FUNCTION citus_internal.pg_dist_shard_placement_trigger_func FROM PUBLIC;\nREVOKE ALL ON FUNCTION citus_internal.refresh_isolation_tester_prepared_statement FROM PUBLIC;\nREVOKE ALL ON FUNCTION citus_internal.replace_isolation_tester_func FROM PUBLIC;\nREVOKE ALL ON FUNCTION citus_internal.restore_isolation_tester_func FROM PUBLIC;\n\n#include \"udfs/citus_internal_add_colocation_metadata/13.1-1.sql\"\n#include \"udfs/citus_internal_add_object_metadata/13.1-1.sql\"\n#include \"udfs/citus_internal_add_partition_metadata/13.1-1.sql\"\n#include \"udfs/citus_internal_add_placement_metadata/13.1-1.sql\"\n#include \"udfs/citus_internal_add_shard_metadata/13.1-1.sql\"\n#include \"udfs/citus_internal_add_tenant_schema/13.1-1.sql\"\n#include \"udfs/citus_internal_adjust_local_clock_to_remote/13.1-1.sql\"\n#include \"udfs/citus_internal_delete_colocation_metadata/13.1-1.sql\"\n#include \"udfs/citus_internal_delete_partition_metadata/13.1-1.sql\"\n#include \"udfs/citus_internal_delete_placement_metadata/13.1-1.sql\"\n#include \"udfs/citus_internal_delete_shard_metadata/13.1-1.sql\"\n#include \"udfs/citus_internal_delete_tenant_schema/13.1-1.sql\"\n#include \"udfs/citus_internal_local_blocked_processes/13.1-1.sql\"\n#include \"udfs/citus_internal_global_blocked_processes/13.1-1.sql\"\n#include \"udfs/citus_blocking_pids/13.1-1.sql\"\n#include \"udfs/citus_isolation_test_session_is_blocked/13.1-1.sql\"\nDROP VIEW IF EXISTS pg_catalog.citus_lock_waits;\n#include \"udfs/citus_lock_waits/13.1-1.sql\"\n\n#include \"udfs/citus_internal_mark_node_not_synced/13.1-1.sql\"\n#include \"udfs/citus_internal_unregister_tenant_schema_globally/13.1-1.sql\"\n#include \"udfs/citus_drop_trigger/13.1-1.sql\"\n#include \"udfs/citus_internal_update_none_dist_table_metadata/13.1-1.sql\"\n#include \"udfs/citus_internal_update_placement_metadata/13.1-1.sql\"\n#include \"udfs/citus_internal_update_relation_colocation/13.1-1.sql\"\n#include \"udfs/repl_origin_helper/13.1-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/13.1-1.sql\"\n#include \"udfs/citus_is_primary_node/13.1-1.sql\"\n#include \"udfs/citus_stat_counters/13.1-1.sql\"\n#include \"udfs/citus_stat_counters_reset/13.1-1.sql\"\n#include \"udfs/citus_nodes/13.1-1.sql\"\n\n-- Since shard_name/13.1-1.sql first drops the function and then creates it, we first\n-- need to drop citus_shards view since that view depends on this function. And immediately\n-- after creating the function, we recreate citus_shards view again.\nDROP VIEW pg_catalog.citus_shards;\n#include \"udfs/shard_name/13.1-1.sql\"\n#include \"udfs/citus_shards/12.0-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--13.1-1--13.2-1.sql",
    "content": "-- citus--13.1-1--13.2-1\n\n-- bump version to 13.2-1\n#include \"udfs/worker_last_saved_explain_analyze/13.2-1.sql\"\n\n#include \"cat_upgrades/add_clone_info_to_pg_dist_node.sql\"\n#include \"udfs/citus_add_clone_node/13.2-1.sql\"\n#include \"udfs/citus_remove_clone_node/13.2-1.sql\"\n#include \"udfs/citus_promote_clone_and_rebalance/13.2-1.sql\"\n#include \"udfs/get_snapshot_based_node_split_plan/13.2-1.sql\"\n\n#include \"udfs/citus_rebalance_start/13.2-1.sql\"\n#include \"udfs/citus_internal_copy_single_shard_placement/13.2-1.sql\"\n\n#include \"udfs/citus_finish_pg_upgrade/13.2-1.sql\"\n\n#include \"udfs/citus_stats/13.2-1.sql\"\n\nDO $drop_leftover_old_columnar_objects$\nBEGIN\n  -- If old columnar exists, i.e., the columnar access method that we had before Citus 11.1,\n  -- and we don't have any relations using the old columnar, then we want to drop the columnar\n  -- objects. This is because, we don't want to automatically create the \"citus_columnar\"\n  -- extension together with the \"citus\" extension anymore. And for the cases where we don't\n  -- want to automatically create the \"citus_columnar\" extension, there is no point of keeping\n  -- the columnar objects that we had before Citus 11.1 around.\n  IF (\n    SELECT EXISTS (\n      SELECT 1 FROM pg_am\n      WHERE\n        -- looking for an access method whose name is \"columnar\" ..\n        pg_am.amname = 'columnar' AND\n        -- .. and there should *NOT* be such a dependency edge in pg_depend, where ..\n        NOT EXISTS (\n          SELECT 1 FROM pg_depend\n          WHERE\n            -- .. the depender is columnar access method (2601 = access method class) ..\n            pg_depend.classid = 2601 AND pg_depend.objid = pg_am.oid AND pg_depend.objsubid = 0 AND\n            -- .. and the dependee is an extension (3079 = extension class)\n            pg_depend.refclassid = 3079 AND pg_depend.refobjsubid = 0\n          LIMIT 1\n        ) AND\n        -- .. and there should *NOT* be any relations using it\n        NOT EXISTS (\n          SELECT 1\n          FROM pg_class\n          WHERE pg_class.relam = pg_am.oid\n          LIMIT 1\n        )\n    )\n  )\n  THEN\n    -- Below we drop the columnar objects in such an order that the objects that depend on\n    -- other objects are dropped first.\n\n    DROP VIEW IF EXISTS columnar.options;\n    DROP VIEW IF EXISTS columnar.stripe;\n    DROP VIEW IF EXISTS columnar.chunk_group;\n    DROP VIEW IF EXISTS columnar.chunk;\n    DROP VIEW IF EXISTS columnar.storage;\n\n    DROP ACCESS METHOD IF EXISTS columnar;\n\n    DROP SEQUENCE IF EXISTS columnar_internal.storageid_seq;\n\n    DROP TABLE IF EXISTS columnar_internal.options;\n    DROP TABLE IF EXISTS columnar_internal.stripe;\n    DROP TABLE IF EXISTS columnar_internal.chunk_group;\n    DROP TABLE IF EXISTS columnar_internal.chunk;\n\n    DROP FUNCTION IF EXISTS columnar_internal.columnar_handler;\n\n    DROP FUNCTION IF EXISTS pg_catalog.alter_columnar_table_set;\n    DROP FUNCTION IF EXISTS pg_catalog.alter_columnar_table_reset;\n    DROP FUNCTION IF EXISTS columnar.get_storage_id;\n\n    DROP FUNCTION IF EXISTS citus_internal.upgrade_columnar_storage;\n    DROP FUNCTION IF EXISTS citus_internal.downgrade_columnar_storage;\n    DROP FUNCTION IF EXISTS citus_internal.columnar_ensure_am_depends_catalog;\n\n    DROP SCHEMA IF EXISTS columnar;\n    DROP SCHEMA IF EXISTS columnar_internal;\n  END IF;\nEND $drop_leftover_old_columnar_objects$;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--13.2-1--14.0-1.sql",
    "content": "-- citus--13.2-1--14.0-1\n-- bump version to 14.0-1\n\n#include \"udfs/citus_prepare_pg_upgrade/14.0-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/14.0-1.sql\"\n\n#include \"udfs/worker_binary_partial_agg_ffunc/14.0-1.sql\"\n#include \"udfs/coord_binary_combine_agg_sfunc/14.0-1.sql\"\n#include \"udfs/coord_binary_combine_agg_ffunc/14.0-1.sql\"\n#include \"udfs/worker_binary_partial_agg/14.0-1.sql\"\n#include \"udfs/coord_binary_combine_agg/14.0-1.sql\"\n\n#include \"udfs/fix_pre_citus14_colocation_group_collation_mismatches/14.0-1.sql\"\n#include \"udfs/citus_finish_citus_upgrade/14.0-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--14.0-1--15.0-1.sql",
    "content": "-- citus--14.0-1--15.0-1\n-- bump version to 15.0-1\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-1--8.0-2.sql",
    "content": "--  citus--7.5-7--8.0-1\nSET search_path = 'pg_catalog';\n\nCREATE OR REPLACE FUNCTION pg_catalog.relation_is_a_known_shard(regclass)\n  RETURNS bool\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$relation_is_a_known_shard$$;\nCOMMENT ON FUNCTION relation_is_a_known_shard(regclass)\n    IS 'returns true if the given relation is a known shard';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_table_is_visible(oid)\n  RETURNS bool\nLANGUAGE C STRICT\nSTABLE\nPARALLEL SAFE\nAS 'MODULE_PATHNAME', $$citus_table_is_visible$$;\nCOMMENT ON FUNCTION citus_table_is_visible(oid)\n\tIS 'wrapper on pg_table_is_visible, filtering out tables (and indexes) that are known to be shards';\n\n-- this is the exact same query with what \\d\n-- command produces, except pg_table_is_visible\n-- is replaced with pg_catalog.relation_is_a_known_shard(c.oid)\nCREATE VIEW citus.citus_shards_on_worker AS\n\tSELECT n.nspname as \"Schema\",\n\t  c.relname as \"Name\",\n\t  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 'table' END as \"Type\",\n\t  pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\"\n\tFROM pg_catalog.pg_class c\n\t     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n\tWHERE c.relkind IN ('r','p','v','m','S','f','')\n\t      AND n.nspname <> 'pg_catalog'\n\t      AND n.nspname <> 'information_schema'\n\t      AND n.nspname !~ '^pg_toast'\n  \t\tAND pg_catalog.relation_is_a_known_shard(c.oid)\n\tORDER BY 1,2;\nALTER VIEW citus.citus_shards_on_worker SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_shards_on_worker TO public;\n\n-- this is the exact same query with what \\di\n-- command produces, except pg_table_is_visible\n-- is replaced with pg_catalog.relation_is_a_known_shard(c.oid)\nCREATE VIEW citus.citus_shard_indexes_on_worker AS\nSELECT 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 'table' END as \"Type\",\n  pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\",\n c2.relname as \"Table\"\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\nWHERE c.relkind IN ('i','')\n      AND n.nspname <> 'pg_catalog'\n      AND n.nspname <> 'information_schema'\n      AND n.nspname !~ '^pg_toast'\n  AND pg_catalog.relation_is_a_known_shard(c.oid)\nORDER BY 1,2;\n\nALTER VIEW citus.citus_shard_indexes_on_worker SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_shard_indexes_on_worker TO public;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-1.sql",
    "content": "-- complain if script is sourced in psql, rather than via CREATE EXTENSION\n\\echo Use \"CREATE EXTENSION citus\" to load this file. \\quit\n\nCREATE SCHEMA citus;\n\nSET search_path = 'pg_catalog';\n\n-- Enable SSL to encrypt all trafic by default\n\n-- create temporary UDF that has the power to change settings within postgres and drop it\n-- after ssl has been setup.\nCREATE FUNCTION citus_setup_ssl()\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_setup_ssl$$;\n\nDO LANGUAGE plpgsql\n$$\nBEGIN\n    -- setup ssl when postgres is OpenSSL-enabled\n    IF current_setting('ssl_ciphers') != 'none' THEN\n        PERFORM citus_setup_ssl();\n    END IF;\nEND;\n$$;\n\nDROP FUNCTION citus_setup_ssl();\n\n-- Citus data types\n\nCREATE TYPE citus.distribution_type AS ENUM (\n   'hash',\n   'range',\n   'append'\n);\n\n\n-- Citus tables & corresponding indexes\n\nCREATE TABLE citus.pg_dist_partition(\n    logicalrelid regclass NOT NULL,\n    partmethod \"char\" NOT NULL,\n    partkey text,\n\tcolocationid integer DEFAULT 0 NOT NULL,\n\trepmodel \"char\" DEFAULT 'c' NOT NULL\n);\n--  SELECT granted to PUBLIC in upgrade script\nCREATE UNIQUE INDEX pg_dist_partition_logical_relid_index\nON citus.pg_dist_partition using btree(logicalrelid);\nALTER TABLE citus.pg_dist_partition SET SCHEMA pg_catalog;\nCREATE INDEX pg_dist_partition_colocationid_index\nON pg_catalog.pg_dist_partition using btree(colocationid);\n\nCREATE TABLE citus.pg_dist_shard(\n    logicalrelid regclass NOT NULL,\n    shardid int8 NOT NULL,\n    shardstorage \"char\" NOT NULL,\n    shardalias text,\n    shardminvalue text,\n    shardmaxvalue text\n);\n-- ALTER-after-CREATE to keep table tuple layout consistent\n-- with earlier versions of Citus.\nALTER TABLE citus.pg_dist_shard DROP shardalias;\n--  SELECT granted to PUBLIC in upgrade script\nCREATE UNIQUE INDEX pg_dist_shard_shardid_index\nON citus.pg_dist_shard using btree(shardid);\nCREATE INDEX pg_dist_shard_logical_relid_index\nON citus.pg_dist_shard using btree(logicalrelid);\nALTER TABLE citus.pg_dist_shard SET SCHEMA pg_catalog;\n\nCREATE SEQUENCE citus.pg_dist_shard_placement_placementid_seq\n    NO CYCLE;\nALTER SEQUENCE citus.pg_dist_shard_placement_placementid_seq\n    SET SCHEMA pg_catalog;\n\nCREATE TABLE citus.pg_dist_shard_placement(\n    shardid int8 NOT NULL,\n    shardstate int4 NOT NULL,\n    shardlength int8 NOT NULL,\n    nodename text NOT NULL,\n    nodeport int8 NOT NULL,\n\tplacementid bigint NOT NULL DEFAULT nextval('pg_catalog.pg_dist_shard_placement_placementid_seq')\n);\n--  SELECT granted to PUBLIC in upgrade script\nCREATE UNIQUE INDEX pg_dist_shard_placement_placementid_index\nON citus.pg_dist_shard_placement using btree(placementid);\nCREATE INDEX pg_dist_shard_placement_shardid_index\nON citus.pg_dist_shard_placement using btree(shardid);\nCREATE INDEX pg_dist_shard_placement_nodeid_index\nON citus.pg_dist_shard_placement using btree(nodename, nodeport);\nALTER TABLE citus.pg_dist_shard_placement SET SCHEMA pg_catalog;\n\n--  Citus sequences\n\n-- Internal sequence to generate 64-bit shard ids. These identifiers are then\n-- used to identify shards in the distributed database.\n\nCREATE SEQUENCE citus.pg_dist_shardid_seq\n    MINVALUE 102008\n    NO CYCLE;\nALTER SEQUENCE  citus.pg_dist_shardid_seq SET SCHEMA pg_catalog;\n\n-- Internal sequence to generate 32-bit jobIds. These identifiers are then\n-- used to identify jobs in the distributed database; and they wrap at 32-bits\n-- to allow for worker nodes to independently execute their distributed jobs.\nCREATE SEQUENCE citus.pg_dist_jobid_seq\n    MINVALUE 2 --  first jobId reserved for clean up jobs\n    MAXVALUE 4294967296;\nALTER SEQUENCE  citus.pg_dist_jobid_seq SET SCHEMA pg_catalog;\n\n\n-- Citus functions\n\n--  master_* functions\n\nCREATE FUNCTION master_get_table_metadata(relation_name text, OUT logical_relid oid,\n                                          OUT part_storage_type \"char\",\n                                          OUT part_method \"char\", OUT part_key text,\n                                          OUT part_replica_count integer,\n                                          OUT part_max_size bigint,\n                                          OUT part_placement_policy integer)\n    RETURNS record\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$master_get_table_metadata$$;\nCOMMENT ON FUNCTION master_get_table_metadata(relation_name text)\n    IS 'fetch metadata values for the table';\n\nCREATE FUNCTION master_get_table_ddl_events(text)\n    RETURNS SETOF text\n    LANGUAGE C STRICT ROWS 100\n    AS 'MODULE_PATHNAME', $$master_get_table_ddl_events$$;\nCOMMENT ON FUNCTION master_get_table_ddl_events(text)\n    IS 'fetch set of ddl statements for the table';\n\nCREATE FUNCTION master_get_new_shardid()\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_get_new_shardid$$;\nCOMMENT ON FUNCTION master_get_new_shardid()\n    IS 'fetch unique shardId';\n\nCREATE FUNCTION master_create_empty_shard(text)\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_create_empty_shard$$;\nCOMMENT ON FUNCTION master_create_empty_shard(text)\n    IS 'create an empty shard and shard placements for the table';\n\nCREATE FUNCTION master_append_table_to_shard(bigint, text, text, integer)\n    RETURNS real\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_append_table_to_shard$$;\nCOMMENT ON FUNCTION master_append_table_to_shard(bigint, text, text, integer)\n    IS 'append given table to all shard placements and update metadata';\n\nCREATE FUNCTION master_drop_all_shards(logicalrelid regclass,\n                                       schema_name text,\n                                       table_name text)\n    RETURNS integer\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_drop_all_shards$$;\nCOMMENT ON FUNCTION master_drop_all_shards(regclass, text, text)\n    IS 'drop all shards in a relation and update metadata';\n\nCREATE FUNCTION master_apply_delete_command(text)\n    RETURNS integer\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_apply_delete_command$$;\nCOMMENT ON FUNCTION master_apply_delete_command(text)\n    IS 'drop shards matching delete criteria and update metadata';\n\nCREATE FUNCTION master_get_active_worker_nodes(OUT node_name text, OUT node_port bigint)\n    RETURNS SETOF record\n    LANGUAGE C STRICT ROWS 100\n    AS 'MODULE_PATHNAME', $$master_get_active_worker_nodes$$;\nCOMMENT ON FUNCTION master_get_active_worker_nodes()\n    IS 'fetch set of active worker nodes';\n\nCREATE FUNCTION master_create_distributed_table(table_name regclass,\n                                                distribution_column text,\n                                                distribution_method citus.distribution_type)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_create_distributed_table$$;\nCOMMENT ON FUNCTION master_create_distributed_table(table_name regclass,\n                                                    distribution_column text,\n                                                    distribution_method citus.distribution_type)\n    IS 'define the table distribution functions';\n\n-- define shard creation function for hash-partitioned tables\nCREATE FUNCTION master_create_worker_shards(table_name text, shard_count integer,\n                                            replication_factor integer DEFAULT 2)\nRETURNS void\nAS 'MODULE_PATHNAME'\nLANGUAGE C STRICT;\n\n--  task_tracker_* functions\n\nCREATE FUNCTION task_tracker_assign_task(bigint, integer, text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$task_tracker_assign_task$$;\nCOMMENT ON FUNCTION task_tracker_assign_task(bigint, integer, text)\n    IS 'assign a task to execute';\n\nCREATE FUNCTION task_tracker_task_status(bigint, integer)\n    RETURNS integer\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$task_tracker_task_status$$;\nCOMMENT ON FUNCTION task_tracker_task_status(bigint, integer)\n    IS 'check an assigned task''s execution status';\n\nCREATE FUNCTION task_tracker_cleanup_job(bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$task_tracker_cleanup_job$$;\nCOMMENT ON FUNCTION task_tracker_cleanup_job(bigint)\n    IS 'clean up all tasks associated with a job';\n\n\n--  worker_* functions\n\nCREATE FUNCTION worker_fetch_partition_file(bigint, integer, integer, integer, text,\n                                            integer)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_fetch_partition_file$$;\nCOMMENT ON FUNCTION worker_fetch_partition_file(bigint, integer, integer, integer, text,\n                                                integer)\n    IS 'fetch partition file from remote node';\n\nCREATE FUNCTION worker_range_partition_table(bigint, integer, text, text, oid, anyarray)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_range_partition_table$$;\nCOMMENT ON FUNCTION worker_range_partition_table(bigint, integer, text, text, oid,\n                                                 anyarray)\n    IS 'range partition query results';\n\nCREATE FUNCTION worker_hash_partition_table(bigint, integer, text, text, oid, anyarray)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_hash_partition_table$$;\nCOMMENT ON FUNCTION worker_hash_partition_table(bigint, integer, text, text, oid,\n                                                anyarray)\n    IS 'hash partition query results';\n\nCREATE FUNCTION worker_merge_files_into_table(bigint, integer, text[], text[])\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_merge_files_into_table$$;\nCOMMENT ON FUNCTION worker_merge_files_into_table(bigint, integer, text[], text[])\n    IS 'merge files into a table';\n\nCREATE FUNCTION worker_merge_files_and_run_query(bigint, integer, text, text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_merge_files_and_run_query$$;\nCOMMENT ON FUNCTION worker_merge_files_and_run_query(bigint, integer, text, text)\n    IS 'merge files and run a reduce query on merged files';\n\nCREATE FUNCTION worker_cleanup_job_schema_cache()\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_cleanup_job_schema_cache$$;\nCOMMENT ON FUNCTION worker_cleanup_job_schema_cache()\n    IS 'cleanup all job schemas in current database';\n\nCREATE FUNCTION worker_append_table_to_shard(text, text, text, integer)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_append_table_to_shard$$;\nCOMMENT ON FUNCTION worker_append_table_to_shard(text, text, text, integer)\n    IS 'append a regular table''s contents to the shard';\n\nCREATE FUNCTION master_drop_sequences(sequence_names text[])\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_drop_sequences$$;\nCOMMENT ON FUNCTION master_drop_sequences(text[])\n    IS 'drop specified sequences from the cluster';\n\nCREATE FUNCTION master_dist_partition_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$master_dist_partition_cache_invalidate$$;\nCOMMENT ON FUNCTION master_dist_partition_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n\nCREATE FUNCTION master_dist_shard_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$master_dist_shard_cache_invalidate$$;\nCOMMENT ON FUNCTION master_dist_shard_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n\n\n--  internal functions, not user accessible\n\nCREATE FUNCTION citus_extradata_container(INTERNAL)\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_extradata_container$$;\nCOMMENT ON FUNCTION pg_catalog.citus_extradata_container(INTERNAL)\n    IS 'placeholder function to store additional data in postgres node trees';\n\n\n-- Citus triggers\n\nCREATE TRIGGER dist_partition_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE\n    ON pg_catalog.pg_dist_partition\n    FOR EACH ROW EXECUTE PROCEDURE master_dist_partition_cache_invalidate();\n\nCREATE TRIGGER dist_shard_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE\n    ON pg_catalog.pg_dist_shard\n    FOR EACH ROW EXECUTE PROCEDURE master_dist_shard_cache_invalidate();\n\n\n-- Citus aggregates\n\nDO $proc$\nBEGIN\nIF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $$\nCREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\nCOMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n    IS 'concatenate input arrays into a single array';\n  $$;\nELSE\n    EXECUTE $$\nCREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\nCOMMENT ON AGGREGATE array_cat_agg(anyarray)\n    IS 'concatenate input arrays into a single array';\n    $$;\nEND IF;\nEND$proc$;\n\nGRANT SELECT ON pg_catalog.pg_dist_partition TO public;\nGRANT SELECT ON pg_catalog.pg_dist_shard TO public;\nGRANT SELECT ON pg_catalog.pg_dist_shard_placement TO public;\n\n--  empty, but required to update the extension version\nCREATE FUNCTION pg_catalog.master_modify_multiple_shards(text)\n    RETURNS integer\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_modify_multiple_shards$$;\nCOMMENT ON FUNCTION master_modify_multiple_shards(text)\n    IS 'push delete and update queries to shards';\n\nCREATE FUNCTION pg_catalog.master_update_shard_statistics(shard_id bigint)\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_update_shard_statistics$$;\nCOMMENT ON FUNCTION master_update_shard_statistics(bigint)\n    IS 'updates shard statistics and returns the updated shard size';\n\nCREATE FUNCTION pg_catalog.worker_apply_shard_ddl_command(bigint, text, text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_apply_shard_ddl_command$$;\nCOMMENT ON FUNCTION worker_apply_shard_ddl_command(bigint, text, text)\n    IS 'extend ddl command with shardId and apply on database';\n\nCREATE FUNCTION pg_catalog.worker_fetch_foreign_file(text, text, bigint, text[], integer[])\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_fetch_foreign_file$$;\nCOMMENT ON FUNCTION pg_catalog.worker_fetch_foreign_file(text, text, bigint, text[], integer[])\n    IS 'fetch foreign file from remote node and apply file';\n\nCREATE FUNCTION pg_catalog.worker_apply_shard_ddl_command(bigint, text)\n    RETURNS void\n    LANGUAGE sql\nAS $worker_apply_shard_ddl_command$\n    SELECT pg_catalog.worker_apply_shard_ddl_command($1, 'public', $2);\n$worker_apply_shard_ddl_command$;\nCOMMENT ON FUNCTION worker_apply_shard_ddl_command(bigint, text)\n    IS 'extend ddl command with shardId and apply on database';\n\nCREATE FUNCTION pg_catalog.shard_name(object_name regclass, shard_id bigint)\n    RETURNS text\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$shard_name$$;\nCOMMENT ON FUNCTION pg_catalog.shard_name(object_name regclass, shard_id bigint)\n    IS 'returns schema-qualified, shard-extended identifier of object name';\nCREATE SEQUENCE citus.pg_dist_groupid_seq\n\tMINVALUE 1\n\tMAXVALUE 4294967296;\n\nCREATE SEQUENCE citus.pg_dist_node_nodeid_seq\n\tMINVALUE 1\n\tMAXVALUE 4294967296;\n\nALTER SEQUENCE citus.pg_dist_groupid_seq SET SCHEMA pg_catalog;\nALTER SEQUENCE citus.pg_dist_node_nodeid_seq SET SCHEMA pg_catalog;\n\n--  add pg_dist_node\nCREATE TABLE citus.pg_dist_node(\n\tnodeid int NOT NULL DEFAULT nextval('pg_dist_groupid_seq') PRIMARY KEY,\n\tgroupid int NOT NULL DEFAULT nextval('pg_dist_node_nodeid_seq'),\n\tnodename text NOT NULL,\n\tnodeport int NOT NULL DEFAULT 5432,\n\tnoderack text NOT NULL DEFAULT 'default',\n\tUNIQUE (nodename, nodeport)\n);\n-- ALTER-after-CREATE to preserve table tuple layout\nALTER TABLE citus.pg_dist_node\n\tADD hasmetadata bool NOT NULL DEFAULT false,\n\tADD isactive bool NOT NULL DEFAULT true;\n\nALTER TABLE citus.pg_dist_node SET SCHEMA pg_catalog;\n\nCREATE FUNCTION master_dist_node_cache_invalidate()\n\tRETURNS trigger\n\tLANGUAGE C\n\tAS 'MODULE_PATHNAME', $$master_dist_node_cache_invalidate$$;\nCOMMENT ON FUNCTION master_dist_node_cache_invalidate()\n\tIS 'invalidate internal cache of nodes when pg_dist_nodes changes';\nCREATE TRIGGER dist_node_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE\n    ON pg_catalog.pg_dist_node\n    FOR EACH ROW EXECUTE PROCEDURE master_dist_node_cache_invalidate();\n\nCREATE FUNCTION master_remove_node(nodename text, nodeport integer)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$master_remove_node$$;\nCOMMENT ON FUNCTION master_remove_node(nodename text, nodeport integer)\n\tIS 'remove node from the cluster';\n\nCREATE FUNCTION pg_catalog.master_get_new_placementid()\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_get_new_placementid$$;\nCOMMENT ON FUNCTION pg_catalog.master_get_new_placementid()\n    IS 'fetch unique placementid';\n\nCREATE FUNCTION pg_catalog.worker_drop_distributed_table(logicalrelid Oid)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_drop_distributed_table$$;\n\nCOMMENT ON FUNCTION pg_catalog.worker_drop_distributed_table(logicalrelid Oid)\n    IS 'drop the clustered table and its reference from metadata tables';\n\nCREATE FUNCTION pg_catalog.column_name_to_column(table_name regclass, column_name text)\n    RETURNS text\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$column_name_to_column$$;\nCOMMENT ON FUNCTION pg_catalog.column_name_to_column(table_name regclass, column_name text)\n    IS 'convert a column name to its textual Var representation';\n\nCREATE FUNCTION pg_catalog.get_colocated_table_array(regclass)\n    RETURNS regclass[]\n    AS 'citus'\n    LANGUAGE C STRICT;\n\nCREATE TABLE citus.pg_dist_local_group(\n    groupid int NOT NULL PRIMARY KEY)\n;\n\n--  insert the default value for being the coordinator node\nINSERT INTO citus.pg_dist_local_group VALUES (0);\n\nALTER TABLE citus.pg_dist_local_group SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.pg_dist_local_group TO public;\n\nCREATE TABLE citus.pg_dist_transaction (\n    groupid int NOT NULL,\n    gid text NOT NULL\n);\n\nCREATE INDEX pg_dist_transaction_group_index\nON citus.pg_dist_transaction using btree(groupid);\n\nALTER TABLE citus.pg_dist_transaction SET SCHEMA pg_catalog;\nALTER TABLE pg_catalog.pg_dist_transaction\nADD CONSTRAINT pg_dist_transaction_unique_constraint UNIQUE (groupid, gid);\n\nGRANT SELECT ON pg_catalog.pg_dist_transaction TO public;\n\nCREATE FUNCTION pg_catalog.recover_prepared_transactions()\n    RETURNS int\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$recover_prepared_transactions$$;\n\nCOMMENT ON FUNCTION pg_catalog.recover_prepared_transactions()\n    IS 'recover prepared transactions started by this node';\n\nCREATE SEQUENCE citus.pg_dist_colocationid_seq\n\tMINVALUE 1\n\tMAXVALUE 4294967296;\n\nALTER SEQUENCE citus.pg_dist_colocationid_seq SET SCHEMA pg_catalog;\n\n--  add pg_dist_colocation\nCREATE TABLE citus.pg_dist_colocation(\n\tcolocationid int NOT NULL PRIMARY KEY,\n\tshardcount int NOT NULL,\n\treplicationfactor int NOT NULL,\n\tdistributioncolumntype oid NOT NULL\n);\n\nALTER TABLE citus.pg_dist_colocation SET SCHEMA pg_catalog;\n\nCREATE INDEX pg_dist_colocation_configuration_index\nON pg_dist_colocation USING btree(shardcount, replicationfactor, distributioncolumntype);\n\nCREATE FUNCTION create_reference_table(table_name regclass)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$create_reference_table$$;\nCOMMENT ON FUNCTION create_reference_table(table_name regclass)\n\tIS 'create a distributed reference table';\n\nCREATE FUNCTION pg_catalog.worker_apply_inter_shard_ddl_command(referencing_shard bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treferencing_schema_name text,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treferenced_shard bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treferenced_schema_name text,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommand text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_apply_inter_shard_ddl_command$$;\nCOMMENT ON FUNCTION pg_catalog.worker_apply_inter_shard_ddl_command(referencing_shard bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treferencing_schema_name text,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treferenced_shard bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treferenced_schema_name text,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommand text)\n    IS 'executes inter shard ddl command';\n\nCREATE FUNCTION pg_catalog.master_dist_placement_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$master_dist_placement_cache_invalidate$$;\nCOMMENT ON FUNCTION master_dist_placement_cache_invalidate()\n    IS 'register relcache invalidation for changed placements';\n\nCREATE TRIGGER dist_placement_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE\n    ON pg_catalog.pg_dist_shard_placement\n    FOR EACH ROW EXECUTE PROCEDURE master_dist_placement_cache_invalidate();\n\nCREATE FUNCTION mark_tables_colocated(source_table_name regclass, target_table_names regclass[])\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$mark_tables_colocated$$;\nCOMMENT ON FUNCTION mark_tables_colocated(source_table_name regclass, target_table_names regclass[])\n\tIS 'mark target distributed tables as colocated with the source table';\n\nCREATE FUNCTION start_metadata_sync_to_node(nodename text, nodeport integer)\n\tRETURNS VOID\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$start_metadata_sync_to_node$$;\nCOMMENT ON FUNCTION start_metadata_sync_to_node(nodename text, nodeport integer)\n    IS 'sync metadata to node';\n\nCREATE FUNCTION worker_create_truncate_trigger(table_name regclass)\n\tRETURNS VOID\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$worker_create_truncate_trigger$$;\nCOMMENT ON FUNCTION worker_create_truncate_trigger(tablename regclass)\n\tIS 'create truncate trigger for distributed table';\n\nCREATE FUNCTION stop_metadata_sync_to_node(nodename text, nodeport integer)\n\tRETURNS VOID\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$stop_metadata_sync_to_node$$;\nCOMMENT ON FUNCTION stop_metadata_sync_to_node(nodename text, nodeport integer)\n    IS 'stop metadata sync to node';\n\nCREATE FUNCTION column_to_column_name(table_name regclass, column_var_text text)\n    RETURNS text\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$column_to_column_name$$;\nCOMMENT ON FUNCTION column_to_column_name(table_name regclass, column_var_text text)\n    IS 'convert the textual Var representation to a column name';\n\nCREATE FUNCTION create_distributed_table(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t distribution_column text,\n\t\t\t\t\t\t\t\t\t\t distribution_type citus.distribution_type DEFAULT 'hash',\n\t\t\t\t\t\t\t\t\t\t colocate_with text DEFAULT 'default')\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$create_distributed_table$$;\nCOMMENT ON FUNCTION create_distributed_table(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t\t distribution_column text,\n\t\t\t\t\t\t\t\t\t\t\t distribution_type citus.distribution_type,\n\t\t\t\t\t\t\t\t\t\t\t colocate_with text)\n    IS 'creates a distributed table';\n\nCREATE FUNCTION get_shard_id_for_distribution_column(table_name regclass, distribution_value \"any\" DEFAULT NULL)\n\tRETURNS bigint\n\tLANGUAGE C\n\tAS 'MODULE_PATHNAME', $$get_shard_id_for_distribution_column$$;\nCOMMENT ON FUNCTION get_shard_id_for_distribution_column(table_name regclass, distribution_value \"any\")\n    IS 'return shard id which belongs to given table and contains given value';\n\nCREATE FUNCTION lock_shard_resources(lock_mode int, shard_id bigint[])\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$lock_shard_resources$$;\nCOMMENT ON FUNCTION lock_shard_resources(lock_mode int, shard_id bigint[])\n    IS 'lock shard resource to serialise non-commutative writes';\n\nCREATE FUNCTION lock_shard_metadata(lock_mode int, shard_id bigint[])\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$lock_shard_metadata$$;\nCOMMENT ON FUNCTION lock_shard_metadata(lock_mode int, shard_id bigint[])\n    IS 'lock shard metadata to prevent writes during metadata changes';\n\nCREATE FUNCTION master_drop_distributed_table_metadata(logicalrelid regclass,\n                                          \t\t\t   schema_name text,\n                                          \t\t\t   table_name text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_drop_distributed_table_metadata$$;\nCOMMENT ON FUNCTION master_drop_distributed_table_metadata(logicalrelid regclass,\n                                              \t\t\t   schema_name text,\n                                              \t\t\t   table_name text)\n    IS 'delete metadata of the distributed table';\n\n-- allow users to read catalog tables\nGRANT SELECT ON pg_catalog.pg_dist_node TO public;\nGRANT SELECT ON pg_catalog.pg_dist_colocation TO public;\nGRANT SELECT ON pg_catalog.pg_dist_colocationid_seq TO public;\nGRANT SELECT ON pg_catalog.pg_dist_groupid_seq TO public;\nGRANT SELECT ON pg_catalog.pg_dist_node_nodeid_seq TO public;\nGRANT SELECT ON pg_catalog.pg_dist_shard_placement_placementid_seq TO public;\nGRANT SELECT ON pg_catalog.pg_dist_shardid_seq TO public;\nGRANT SELECT ON pg_catalog.pg_dist_jobid_seq TO public;\n\nCREATE FUNCTION upgrade_to_reference_table(table_name regclass)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$upgrade_to_reference_table$$;\nCOMMENT ON FUNCTION upgrade_to_reference_table(table_name regclass)\n    IS 'upgrades an existing broadcast table to a reference table';\n\nCREATE FUNCTION master_disable_node(nodename text, nodeport integer)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$master_disable_node$$;\nCOMMENT ON FUNCTION master_disable_node(nodename text, nodeport integer)\n\tIS 'removes node from the cluster temporarily';\n\n-- functions for running commands on workers and shards\nCREATE FUNCTION pg_catalog.master_run_on_worker(worker_name text[],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   port integer[],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   command text[],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   parallel boolean,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   OUT node_name text,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   OUT node_port integer,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   OUT success boolean,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   OUT result text )\n\tRETURNS SETOF record\n\tLANGUAGE C STABLE STRICT\n\tAS 'MODULE_PATHNAME', $$master_run_on_worker$$;\n\n\nCREATE TYPE citus.colocation_placement_type AS (\n    shardid1 bigint,\n    shardid2 bigint,\n    nodename text,\n    nodeport bigint\n);\n\n--\n-- distributed_tables_colocated returns true if given tables are co-located, false otherwise.\n-- The function checks shard definitions, matches shard placements for given tables.\n--\nCREATE FUNCTION pg_catalog.distributed_tables_colocated(table1 regclass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   table2 regclass)\n    RETURNS bool\n    LANGUAGE plpgsql\n    AS $function$\nDECLARE\n\tcolocated_shard_count int;\n\ttable1_shard_count int;\n\ttable2_shard_count int;\n\ttable1_placement_count int;\n\ttable2_placement_count int;\n\ttable1_placements citus.colocation_placement_type[];\n\ttable2_placements citus.colocation_placement_type[];\nBEGIN\n\tSELECT count(*),\n\t    (SELECT count(*) FROM pg_dist_shard a WHERE a.logicalrelid = table1),\n\t    (SELECT count(*) FROM pg_dist_shard b WHERE b.logicalrelid = table2)\n\tINTO colocated_shard_count, table1_shard_count, table2_shard_count\n\tFROM pg_dist_shard tba JOIN pg_dist_shard tbb USING(shardminvalue, shardmaxvalue)\n\tWHERE tba.logicalrelid = table1 AND tbb.logicalrelid = table2;\n\n\tIF (table1_shard_count != table2_shard_count OR\n\t\ttable1_shard_count != colocated_shard_count)\n\tTHEN\n\t\tRETURN false;\n\tEND IF;\n\n\tWITH colocated_shards AS (\n\t\tSELECT tba.shardid as shardid1, tbb.shardid as shardid2\n\t\tFROM pg_dist_shard tba JOIN pg_dist_shard tbb USING(shardminvalue, shardmaxvalue)\n\t\tWHERE tba.logicalrelid = table1 AND tbb.logicalrelid = table2),\n\tleft_shard_placements AS (\n\t\tSELECT cs.shardid1, cs.shardid2, sp.nodename, sp.nodeport\n\t\tFROM colocated_shards cs JOIN pg_dist_shard_placement sp\n\t\tON (cs.shardid1 = sp.shardid)\n\t\tWHERE sp.shardstate = 1)\n\tSELECT\n\t\tarray_agg(\n\t\t\t(lsp.shardid1, lsp.shardid2, lsp.nodename, lsp.nodeport)::citus.colocation_placement_type\n\t\t\tORDER BY shardid1, shardid2, nodename, nodeport),\n\t\tcount(distinct lsp.shardid1)\n\tFROM left_shard_placements lsp\n\tINTO table1_placements, table1_placement_count;\n\n\tWITH colocated_shards AS (\n\t\tSELECT tba.shardid as shardid1, tbb.shardid as shardid2\n\t\tFROM pg_dist_shard tba JOIN pg_dist_shard tbb USING(shardminvalue, shardmaxvalue)\n\t\tWHERE tba.logicalrelid = table1 AND tbb.logicalrelid = table2),\n\tright_shard_placements AS (\n\t\tSELECT cs.shardid1, cs.shardid2, sp.nodename, sp.nodeport\n\t\tFROM colocated_shards cs LEFT JOIN pg_dist_shard_placement sp ON(cs.shardid2 = sp.shardid)\n\t\tWHERE sp.shardstate = 1)\n\tSELECT\n\t\tarray_agg(\n\t\t\t(rsp.shardid1, rsp.shardid2, rsp.nodename, rsp.nodeport)::citus.colocation_placement_type\n\t\t\tORDER BY shardid1, shardid2, nodename, nodeport),\n\t\tcount(distinct rsp.shardid2)\n\tFROM right_shard_placements rsp\n\tINTO table2_placements, table2_placement_count;\n\n\tIF (table1_shard_count != table1_placement_count\n\t\tOR table1_placement_count != table2_placement_count) THEN\n\t\tRETURN false;\n\tEND IF;\n\n\tIF (array_length(table1_placements, 1) != array_length(table2_placements, 1)) THEN\n\t\tRETURN false;\n\tEND IF;\n\n\tFOR i IN  1..array_length(table1_placements,1) LOOP\n\t\tIF (table1_placements[i].nodename != table2_placements[i].nodename OR\n\t\t\ttable1_placements[i].nodeport != table2_placements[i].nodeport) THEN\n\t\t\tRETURN false;\n\t\tEND IF;\n\tEND LOOP;\n\n\tRETURN true;\nEND;\n$function$;\n\n\nCREATE FUNCTION pg_catalog.run_command_on_workers(command text,\n\t\t\t\t\t\t\t\t\t\t\t\t\tparallel bool default true,\n\t\t\t\t\t\t\t\t\t\t\t\t\tOUT nodename text,\n\t\t\t\t\t\t\t\t\t\t\t\t\tOUT nodeport int,\n\t\t\t\t\t\t\t\t\t\t\t\t\tOUT success bool,\n\t\t\t\t\t\t\t\t\t\t\t\t\tOUT result text)\n\tRETURNS SETOF record\n\tLANGUAGE plpgsql\n\tAS $function$\nDECLARE\n\tworkers text[];\n\tports int[];\n\tcommands text[];\nBEGIN\n\tWITH citus_workers AS (\n\t\tSELECT * FROM master_get_active_worker_nodes() ORDER BY node_name, node_port)\n\tSELECT array_agg(node_name), array_agg(node_port), array_agg(command)\n\tINTO workers, ports, commands\n\tFROM citus_workers;\n\n\tRETURN QUERY SELECT * FROM master_run_on_worker(workers, ports, commands, parallel);\nEND;\n$function$;\n\n\nCREATE FUNCTION pg_catalog.run_command_on_placements(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommand text,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tparallel bool default true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOUT nodename text,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOUT nodeport int,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOUT shardid bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOUT success bool,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOUT result text)\n\tRETURNS SETOF record\n\tLANGUAGE plpgsql\n\tAS $function$\nDECLARE\n\tworkers text[];\n\tports int[];\n\tshards bigint[];\n\tcommands text[];\nBEGIN\n\tWITH citus_placements AS (\n\t\tSELECT\n\t\t\tds.logicalrelid::regclass AS tablename,\n\t\t\tds.shardid AS shardid,\n\t\t\tshard_name(ds.logicalrelid, ds.shardid) AS shardname,\n\t\t\tdsp.nodename AS nodename, dsp.nodeport::int AS nodeport\n\t\tFROM pg_dist_shard ds JOIN pg_dist_shard_placement dsp USING (shardid)\n\t\tWHERE dsp.shardstate = 1 and ds.logicalrelid::regclass = table_name\n\t\tORDER BY ds.logicalrelid, ds.shardid, dsp.nodename, dsp.nodeport)\n\tSELECT\n\t\tarray_agg(cp.nodename), array_agg(cp.nodeport), array_agg(cp.shardid),\n\t\tarray_agg(format(command, cp.shardname))\n\tINTO workers, ports, shards, commands\n\tFROM citus_placements cp;\n\n\tRETURN QUERY\n\t\tSELECT r.node_name, r.node_port, shards[ordinality],\n\t\t\tr.success, r.result\n\t\tFROM master_run_on_worker(workers, ports, commands, parallel) WITH ORDINALITY r;\nEND;\n$function$;\n\n\nCREATE FUNCTION pg_catalog.run_command_on_colocated_placements(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t table_name1 regclass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t table_name2 regclass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t command text,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t parallel bool default true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t OUT nodename text,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t OUT nodeport int,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t OUT shardid1 bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t OUT shardid2 bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t OUT success bool,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t OUT result text)\n\tRETURNS SETOF record\n\tLANGUAGE plpgsql\n\tAS $function$\nDECLARE\n\tworkers text[];\n\tports int[];\n\tshards1 bigint[];\n\tshards2 bigint[];\n\tcommands text[];\nBEGIN\n\tIF NOT (SELECT distributed_tables_colocated(table_name1, table_name2)) THEN\n\t\tRAISE EXCEPTION 'tables % and % are not co-located', table_name1, table_name2;\n\tEND IF;\n\n\tWITH active_shard_placements AS (\n\t\tSELECT\n\t\t\tds.logicalrelid,\n\t\t\tds.shardid AS shardid,\n\t\t\tshard_name(ds.logicalrelid, ds.shardid) AS shardname,\n\t\t\tds.shardminvalue AS shardminvalue,\n\t\t\tds.shardmaxvalue AS shardmaxvalue,\n\t\t\tdsp.nodename AS nodename,\n\t\t\tdsp.nodeport::int AS nodeport\n\t\tFROM pg_dist_shard ds JOIN pg_dist_shard_placement dsp USING (shardid)\n\t\tWHERE dsp.shardstate = 1 and (ds.logicalrelid::regclass = table_name1 or\n\t\t\tds.logicalrelid::regclass = table_name2)\n\t\tORDER BY ds.logicalrelid, ds.shardid, dsp.nodename, dsp.nodeport),\n\tcitus_colocated_placements AS (\n\t\tSELECT\n\t\t\ta.logicalrelid::regclass AS tablename1,\n\t\t\ta.shardid AS shardid1,\n\t\t\tshard_name(a.logicalrelid, a.shardid) AS shardname1,\n\t\t\tb.logicalrelid::regclass AS tablename2,\n\t\t\tb.shardid AS shardid2,\n\t\t\tshard_name(b.logicalrelid, b.shardid) AS shardname2,\n\t\t\ta.nodename AS nodename,\n\t\t\ta.nodeport::int AS nodeport\n\t\tFROM\n\t\t\tactive_shard_placements a, active_shard_placements b\n\t\tWHERE\n\t\t\ta.shardminvalue = b.shardminvalue AND\n\t\t\ta.shardmaxvalue = b.shardmaxvalue AND\n\t\t\ta.logicalrelid != b.logicalrelid AND\n\t\t\ta.nodename = b.nodename AND\n\t\t\ta.nodeport = b.nodeport AND\n\t\t\ta.logicalrelid::regclass = table_name1 AND\n\t\t\tb.logicalrelid::regclass = table_name2\n\t\tORDER BY a.logicalrelid, a.shardid, nodename, nodeport)\n\tSELECT\n\t\tarray_agg(cp.nodename), array_agg(cp.nodeport), array_agg(cp.shardid1),\n\t\tarray_agg(cp.shardid2), array_agg(format(command, cp.shardname1, cp.shardname2))\n\tINTO workers, ports, shards1, shards2, commands\n  \tFROM citus_colocated_placements cp;\n\n\tRETURN QUERY SELECT r.node_name, r.node_port, shards1[ordinality],\n\t\tshards2[ordinality], r.success, r.result\n\tFROM master_run_on_worker(workers, ports, commands, parallel) WITH ORDINALITY r;\nEND;\n$function$;\n\n\nCREATE FUNCTION pg_catalog.run_command_on_shards(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommand text,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tparallel bool default true,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOUT shardid bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOUT success bool,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOUT result text)\n\tRETURNS SETOF record\n\tLANGUAGE plpgsql\n\tAS $function$\nDECLARE\n\tworkers text[];\n\tports int[];\n\tshards bigint[];\n\tcommands text[];\n\tshard_count int;\nBEGIN\n\tSELECT COUNT(*) INTO shard_count FROM pg_dist_shard\n\tWHERE logicalrelid = table_name;\n\n\tWITH citus_shards AS (\n\t\tSELECT ds.logicalrelid::regclass AS tablename,\n\t\t\tds.shardid AS shardid,\n\t\t\tshard_name(ds.logicalrelid, ds.shardid) AS shardname,\n\t\t\tarray_agg(dsp.nodename) AS nodenames,\n\t\t\tarray_agg(dsp.nodeport) AS nodeports\n\t\tFROM pg_dist_shard ds LEFT JOIN pg_dist_shard_placement dsp USING (shardid)\n\t\tWHERE dsp.shardstate = 1 and ds.logicalrelid::regclass = table_name\n\t\tGROUP BY ds.logicalrelid, ds.shardid\n\t\tORDER BY ds.logicalrelid, ds.shardid)\n\tSELECT\n\t\tarray_agg(cs.nodenames[1]), array_agg(cs.nodeports[1]), array_agg(cs.shardid),\n\t\tarray_agg(format(command, cs.shardname))\n\tINTO workers, ports, shards, commands\n\tFROM citus_shards cs;\n\n\tIF (shard_count != array_length(workers, 1)) THEN\n\t\tRAISE NOTICE 'some shards do  not have active placements';\n\tEND IF;\n\n\tRETURN QUERY\n\t\tSELECT shards[ordinality], r.success, r.result\n\t\tFROM master_run_on_worker(workers, ports, commands, parallel) WITH ORDINALITY r;\nEND;\n$function$;\n\nCREATE FUNCTION master_dist_local_group_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$master_dist_local_group_cache_invalidate$$;\nCOMMENT ON FUNCTION master_dist_local_group_cache_invalidate()\n    IS 'register node cache invalidation for changed rows';\n\nCREATE TRIGGER dist_local_group_cache_invalidate\n    AFTER UPDATE\n    ON pg_catalog.pg_dist_local_group\n    FOR EACH ROW EXECUTE PROCEDURE master_dist_local_group_cache_invalidate();\n\nCREATE FUNCTION worker_apply_sequence_command(text)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_apply_sequence_command$$;\nCOMMENT ON FUNCTION worker_apply_sequence_command(text)\n    IS 'create a sequence which products globally unique values';\n\nCREATE FUNCTION isolate_tenant_to_new_shard(table_name regclass, tenant_id \"any\", cascade_option text DEFAULT '')\n\tRETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$isolate_tenant_to_new_shard$$;\nCOMMENT ON FUNCTION isolate_tenant_to_new_shard(table_name regclass, tenant_id \"any\", cascade_option text)\n    IS 'isolate a tenant to its own shard and return the new shard id';\n\nCREATE FUNCTION worker_hash(value \"any\")\n\tRETURNS integer\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_hash$$;\nCOMMENT ON FUNCTION worker_hash(value \"any\")\n    IS 'calculate hashed value and return it';\n\n-- table size functions\nCREATE FUNCTION citus_table_size(logicalrelid regclass)\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_table_size$$;\nCOMMENT ON FUNCTION citus_table_size(logicalrelid regclass)\n    IS 'get disk space used by the specified table, excluding indexes';\n\nCREATE FUNCTION citus_relation_size(logicalrelid regclass)\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_relation_size$$;\nCOMMENT ON FUNCTION citus_relation_size(logicalrelid regclass)\n    IS 'get disk space used by the ''main'' fork';\n\nCREATE FUNCTION citus_total_relation_size(logicalrelid regclass)\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_total_relation_size$$;\nCOMMENT ON FUNCTION citus_total_relation_size(logicalrelid regclass)\n    IS 'get total disk space used by the specified table';\n\nCREATE FUNCTION pg_catalog.citus_truncate_trigger()\n    RETURNS trigger\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_truncate_trigger$$;\nCOMMENT ON FUNCTION pg_catalog.citus_truncate_trigger()\n    IS 'trigger function called when truncating the distributed table';\n\n-- introduce replication-agnostic pg_dist_placement table\nALTER SEQUENCE pg_catalog.pg_dist_shard_placement_placementid_seq\n  RENAME TO pg_dist_placement_placementid_seq;\n\nALTER TABLE pg_catalog.pg_dist_shard_placement\n  ALTER COLUMN placementid SET DEFAULT nextval('pg_catalog.pg_dist_placement_placementid_seq');\n\nCREATE TABLE citus.pg_dist_placement (\n  placementid BIGINT NOT NULL default nextval('pg_dist_placement_placementid_seq'::regclass),\n  shardid BIGINT NOT NULL,\n  shardstate INT NOT NULL,\n  shardlength BIGINT NOT NULL,\n  groupid INT NOT NULL\n);\nALTER TABLE citus.pg_dist_placement SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.pg_dist_placement TO public;\n\nCREATE INDEX pg_dist_placement_groupid_index\n  ON pg_dist_placement USING btree(groupid);\n\nCREATE INDEX pg_dist_placement_shardid_index\n  ON pg_dist_placement USING btree(shardid);\n\nCREATE UNIQUE INDEX pg_dist_placement_placementid_index\n  ON pg_dist_placement USING btree(placementid);\n\nCREATE OR REPLACE FUNCTION citus.find_groupid_for_node(text, int)\nRETURNS int AS $$\nDECLARE\n  groupid int := (SELECT groupid FROM pg_dist_node WHERE nodename = $1 AND nodeport = $2);\nBEGIN\n  IF groupid IS NULL THEN\n    RAISE EXCEPTION 'There is no node at \"%:%\"', $1, $2;\n  ELSE\n    RETURN groupid;\n  END IF;\nEND;\n$$ LANGUAGE plpgsql;\n\nINSERT INTO pg_catalog.pg_dist_placement\nSELECT placementid, shardid, shardstate, shardlength,\n       citus.find_groupid_for_node(placement.nodename, placement.nodeport::int) AS groupid\nFROM pg_dist_shard_placement placement;\n\nDROP TRIGGER dist_placement_cache_invalidate ON pg_catalog.pg_dist_shard_placement;\nCREATE TRIGGER dist_placement_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE\n    ON pg_catalog.pg_dist_placement\n    FOR EACH ROW EXECUTE PROCEDURE master_dist_placement_cache_invalidate();\n\n-- this should be removed when noderole is added but for now it ensures the below view\n-- returns the correct results and that placements unambiguously belong to a view\nALTER TABLE pg_catalog.pg_dist_node ADD CONSTRAINT pg_dist_node_groupid_unique\n  UNIQUE (groupid);\n\nDROP TABLE pg_dist_shard_placement;\nCREATE VIEW citus.pg_dist_shard_placement AS\nSELECT shardid, shardstate, shardlength, nodename, nodeport, placementid\n-- assumes there's only one node per group\nFROM pg_dist_placement placement INNER JOIN pg_dist_node node ON (\n  placement.groupid = node.groupid\n);\nALTER VIEW citus.pg_dist_shard_placement SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.pg_dist_shard_placement TO public;\n\n-- add some triggers which make it look like pg_dist_shard_placement is still a table\n\nALTER VIEW pg_catalog.pg_dist_shard_placement\n  ALTER placementid SET DEFAULT nextval('pg_dist_placement_placementid_seq');\n\nCREATE OR REPLACE FUNCTION citus.pg_dist_shard_placement_trigger_func()\nRETURNS TRIGGER AS $$\n  BEGIN\n    IF (TG_OP = 'DELETE') THEN\n      DELETE FROM pg_dist_placement WHERE placementid = OLD.placementid;\n      RETURN OLD;\n    ELSIF (TG_OP = 'UPDATE') THEN\n      UPDATE pg_dist_placement\n        SET shardid = NEW.shardid, shardstate = NEW.shardstate,\n            shardlength = NEW.shardlength, placementid = NEW.placementid,\n            groupid = citus.find_groupid_for_node(NEW.nodename, NEW.nodeport)\n        WHERE placementid = OLD.placementid;\n      RETURN NEW;\n    ELSIF (TG_OP = 'INSERT') THEN\n      INSERT INTO pg_dist_placement\n        (placementid, shardid, shardstate, shardlength, groupid)\n      VALUES (NEW.placementid, NEW.shardid, NEW.shardstate, NEW.shardlength,\n        citus.find_groupid_for_node(NEW.nodename, NEW.nodeport));\n      RETURN NEW;\n    END IF;\n  END;\n$$ LANGUAGE plpgsql;\n\nCREATE TRIGGER pg_dist_shard_placement_trigger\n  INSTEAD OF INSERT OR UPDATE OR DELETE ON pg_dist_shard_placement\n  FOR EACH ROW EXECUTE PROCEDURE citus.pg_dist_shard_placement_trigger_func();\n\nCREATE TYPE pg_catalog.noderole AS ENUM (\n  'primary',     -- node is available and accepting writes\n  'secondary',   -- node is available but only accepts reads\n  'unavailable' -- node is in recovery or otherwise not usable\n-- adding new values to a type inside of a transaction (such as during an ALTER EXTENSION\n-- citus UPDATE) isn't allowed in PG 9.6, and only allowed in PG10 if you don't use the\n-- new values inside of the same transaction. You might need to replace this type with a\n-- new one and then change the column type in pg_dist_node. There's a list of\n-- alternatives here:\n-- https://stackoverflow.com/questions/1771543/postgresql-updating-an-enum-type/41696273\n);\n\nALTER TABLE pg_dist_node ADD COLUMN noderole noderole NOT NULL DEFAULT 'primary';\n\n-- we're now allowed to have more than one node per group\nALTER TABLE pg_catalog.pg_dist_node DROP CONSTRAINT pg_dist_node_groupid_unique;\n\n-- so make sure pg_dist_shard_placement only returns writable placements\nCREATE OR REPLACE VIEW pg_catalog.pg_dist_shard_placement AS\n  SELECT shardid, shardstate, shardlength, nodename, nodeport, placementid\n  FROM pg_dist_placement placement INNER JOIN pg_dist_node node ON (\n    placement.groupid = node.groupid AND node.noderole = 'primary'\n  );\n\nCREATE OR REPLACE FUNCTION citus.pg_dist_node_trigger_func()\nRETURNS TRIGGER AS $$\n  BEGIN\n    -- AddNodeMetadata also takes out a ShareRowExclusiveLock\n    LOCK TABLE pg_dist_node IN SHARE ROW EXCLUSIVE MODE;\n    IF (TG_OP = 'INSERT') THEN\n      IF NEW.noderole = 'primary'\n          AND EXISTS (SELECT 1 FROM pg_dist_node WHERE groupid = NEW.groupid AND\n                                                       noderole = 'primary' AND\n                                                       nodeid <> NEW.nodeid) THEN\n        RAISE EXCEPTION 'there cannot be two primary nodes in a group';\n      END IF;\n      RETURN NEW;\n    ELSIF (TG_OP = 'UPDATE') THEN\n      IF NEW.noderole = 'primary'\n           AND EXISTS (SELECT 1 FROM pg_dist_node WHERE groupid = NEW.groupid AND\n                                                        noderole = 'primary' AND\n                                                        nodeid <> NEW.nodeid) THEN\n         RAISE EXCEPTION 'there cannot be two primary nodes in a group';\n      END IF;\n      RETURN NEW;\n    END IF;\n  END;\n$$ LANGUAGE plpgsql;\n\nCREATE TRIGGER pg_dist_node_trigger\n  BEFORE INSERT OR UPDATE ON pg_dist_node\n  FOR EACH ROW EXECUTE PROCEDURE citus.pg_dist_node_trigger_func();\n\n-- distributed deadlocks\nCREATE FUNCTION assign_distributed_transaction_id(initiator_node_identifier int4, transaction_number int8, transaction_stamp timestamptz)\n     RETURNS void\n     LANGUAGE C STRICT\n     AS 'MODULE_PATHNAME',$$assign_distributed_transaction_id$$;\n COMMENT ON FUNCTION assign_distributed_transaction_id(initiator_node_identifier int4, transaction_number int8, transaction_stamp timestamptz)\n     IS 'Only intended for internal use, users should not call this. The function sets the distributed transaction id';\n\nCREATE OR REPLACE FUNCTION get_current_transaction_id(OUT database_id oid, OUT process_id int, OUT initiator_node_identifier int4, OUT transaction_number int8, OUT transaction_stamp timestamptz)\n     RETURNS RECORD\n     LANGUAGE C STRICT\n     AS 'MODULE_PATHNAME',$$get_current_transaction_id$$;\n COMMENT ON FUNCTION get_current_transaction_id(OUT database_id oid, OUT process_id int, OUT initiator_node_identifier int4, OUT transaction_number int8, OUT transaction_stamp timestamptz)\n     IS 'returns the current backend data including distributed transaction id';\n\nCREATE OR REPLACE FUNCTION get_all_active_transactions(OUT database_id oid, OUT process_id int, OUT initiator_node_identifier int4, OUT transaction_number int8, OUT transaction_stamp timestamptz)\n\tRETURNS SETOF RECORD\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$get_all_active_transactions$$;\n COMMENT ON FUNCTION get_all_active_transactions(OUT database_id oid, OUT process_id int, OUT initiator_node_identifier int4, OUT transaction_number int8, OUT transaction_stamp timestamptz)\n     IS 'returns distributed transaction ids of active distributed transactions';\n\nCREATE OR REPLACE FUNCTION check_distributed_deadlocks()\nRETURNS BOOL\nLANGUAGE 'c' STRICT\nAS $$MODULE_PATHNAME$$, $$check_distributed_deadlocks$$;\nCOMMENT ON FUNCTION check_distributed_deadlocks()\nIS 'does a distributed deadlock check, if a deadlock found cancels one of the participating backends and returns true ';\n\nCREATE FUNCTION pg_catalog.dump_local_wait_edges(\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$dump_local_wait_edges$$;\nCOMMENT ON FUNCTION pg_catalog.dump_local_wait_edges()\nIS 'returns all local lock wait chains, that start from distributed transactions';\n\nCREATE FUNCTION pg_catalog.dump_global_wait_edges(\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE 'c' STRICT\nAS $$MODULE_PATHNAME$$, $$dump_global_wait_edges$$;\nCOMMENT ON FUNCTION pg_catalog.dump_global_wait_edges()\nIS 'returns a global list of blocked transactions originating from this node';\n\nCREATE FUNCTION citus.replace_isolation_tester_func()\nRETURNS void AS $$\n  DECLARE\n    version integer := current_setting('server_version_num');\n  BEGIN\n    IF version >= 100000 THEN\n      ALTER FUNCTION pg_catalog.pg_isolation_test_session_is_blocked(integer, integer[])\n        RENAME TO old_pg_isolation_test_session_is_blocked;\n      ALTER FUNCTION pg_catalog.citus_isolation_test_session_is_blocked(integer, integer[])\n        RENAME TO pg_isolation_test_session_is_blocked;\n    ELSE\n      ALTER FUNCTION pg_catalog.pg_blocking_pids(integer)\n        RENAME TO old_pg_blocking_pids;\n      ALTER FUNCTION pg_catalog.citus_blocking_pids(integer)\n        RENAME TO pg_blocking_pids;\n    END IF;\n  END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION citus.restore_isolation_tester_func()\nRETURNS void AS $$\n  DECLARE\n    version integer := current_setting('server_version_num');\n  BEGIN\n    IF version >= 100000 THEN\n      ALTER FUNCTION pg_catalog.pg_isolation_test_session_is_blocked(integer, integer[])\n        RENAME TO citus_isolation_test_session_is_blocked;\n      ALTER FUNCTION pg_catalog.old_pg_isolation_test_session_is_blocked(integer, integer[])\n        RENAME TO pg_isolation_test_session_is_blocked;\n    ELSE\n      ALTER FUNCTION pg_catalog.pg_blocking_pids(integer)\n        RENAME TO citus_blocking_pids;\n      ALTER FUNCTION pg_catalog.old_pg_blocking_pids(integer)\n        RENAME TO pg_blocking_pids;\n    END IF;\n  END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION citus.refresh_isolation_tester_prepared_statement()\nRETURNS void AS $$\n  BEGIN\n    -- isolation creates a prepared statement using the old function before tests have a\n    -- chance to call replace_isolation_tester_func. By calling that prepared statement\n    -- with a different search_path we force a re-parse which picks up the new function\n    SET search_path TO 'citus';\n    EXECUTE 'EXECUTE isolationtester_waiting (0)';\n    RESET search_path;\n  END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION pg_catalog.citus_blocking_pids(pBlockedPid integer)\nRETURNS int4[] AS $$\n  DECLARE\n    mLocalBlockingPids int4[];\n    mRemoteBlockingPids int4[];\n    mLocalTransactionNum int8;\n  BEGIN\n    SELECT pg_catalog.old_pg_blocking_pids(pBlockedPid) INTO mLocalBlockingPids;\n\n    IF (array_length(mLocalBlockingPids, 1) > 0) THEN\n      RETURN mLocalBlockingPids;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    SELECT transaction_number INTO mLocalTransactionNum\n      FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n\n    SELECT array_agg(process_id) INTO mRemoteBlockingPids FROM (\n      WITH activeTransactions AS (\n        SELECT process_id, transaction_number FROM get_all_active_transactions()\n      ), blockingTransactions AS (\n        SELECT blocking_transaction_num AS txn_num FROM dump_global_wait_edges()\n        WHERE waiting_transaction_num = mLocalTransactionNum\n      )\n      SELECT activeTransactions.process_id FROM activeTransactions, blockingTransactions\n      WHERE activeTransactions.transaction_number = blockingTransactions.txn_num\n    ) AS sub;\n\n    RETURN mRemoteBlockingPids;\n  END;\n$$ LANGUAGE plpgsql;\n\nCREATE FUNCTION pg_catalog.citus_isolation_test_session_is_blocked(pBlockedPid integer, pInterestingPids integer[])\nRETURNS boolean AS $$\n  DECLARE\n    mBlockedTransactionNum int8;\n  BEGIN\n    IF pg_catalog.old_pg_isolation_test_session_is_blocked(pBlockedPid, pInterestingPids) THEN\n      RETURN true;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    SELECT transaction_number INTO mBlockedTransactionNum\n      FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n\n    RETURN EXISTS (\n      SELECT 1 FROM dump_global_wait_edges()\n        WHERE waiting_transaction_num = mBlockedTransactionNum\n    );\n  END;\n$$ LANGUAGE plpgsql;\n\nALTER TABLE pg_dist_node ADD COLUMN nodecluster name NOT NULL DEFAULT 'default';\nALTER TABLE pg_dist_node\n  ADD CONSTRAINT primaries_are_only_allowed_in_the_default_cluster\n  CHECK (NOT (nodecluster <> 'default' AND noderole = 'primary'));\n\nCREATE FUNCTION master_add_node(nodename text,\n                                nodeport integer,\n                                groupid integer default 0,\n                                noderole noderole default 'primary',\n                                nodecluster name default 'default',\n                                OUT nodeid integer,\n                                OUT groupid integer,\n                                OUT nodename text,\n                                OUT nodeport integer,\n                                OUT noderack text,\n                                OUT hasmetadata boolean,\n                                OUT isactive bool,\n                                OUT noderole noderole,\n                                OUT nodecluster name)\n  RETURNS record\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$master_add_node$$;\nCOMMENT ON FUNCTION master_add_node(nodename text, nodeport integer,\n                                    groupid integer, noderole noderole, nodecluster name)\n  IS 'add node to the cluster';\n\nCREATE FUNCTION master_add_inactive_node(nodename text,\n                                         nodeport integer,\n                                         groupid integer default 0,\n                                         noderole noderole default 'primary',\n                                         nodecluster name default 'default',\n                                         OUT nodeid integer,\n                                         OUT groupid integer,\n                                         OUT nodename text,\n                                         OUT nodeport integer,\n                                         OUT noderack text,\n                                         OUT hasmetadata boolean,\n                                         OUT isactive bool,\n                                         OUT noderole noderole,\n                                         OUT nodecluster name)\n  RETURNS record\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME',$$master_add_inactive_node$$;\nCOMMENT ON FUNCTION master_add_inactive_node(nodename text,nodeport integer,\n                                             groupid integer, noderole noderole,\n                                             nodecluster name)\n  IS 'prepare node by adding it to pg_dist_node';\n\nCREATE FUNCTION master_activate_node(nodename text,\n                                     nodeport integer,\n                                     OUT nodeid integer,\n                                     OUT groupid integer,\n                                     OUT nodename text,\n                                     OUT nodeport integer,\n                                     OUT noderack text,\n                                     OUT hasmetadata boolean,\n                                     OUT isactive bool,\n                                     OUT noderole noderole,\n                                     OUT nodecluster name)\n    RETURNS record\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME',$$master_activate_node$$;\nCOMMENT ON FUNCTION master_activate_node(nodename text, nodeport integer)\n    IS 'activate a node which is in the cluster';\n\nCREATE FUNCTION master_add_secondary_node(nodename text,\n                                          nodeport integer,\n                                          primaryname text,\n                                          primaryport integer,\n                                          nodecluster name default 'default',\n                                          OUT nodeid integer,\n                                          OUT groupid integer,\n                                          OUT nodename text,\n                                          OUT nodeport integer,\n                                          OUT noderack text,\n                                          OUT hasmetadata boolean,\n                                          OUT isactive bool,\n                                          OUT noderole noderole,\n                                          OUT nodecluster name)\n  RETURNS record\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$master_add_secondary_node$$;\nCOMMENT ON FUNCTION master_add_secondary_node(nodename text, nodeport integer,\n                                              primaryname text, primaryport integer,\n                                              nodecluster name)\n  IS 'add a secondary node to the cluster';\n\nCREATE FUNCTION master_update_node(node_id int,\n                                              new_node_name text,\n                                              new_node_port int)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$master_update_node$$;\nCOMMENT ON FUNCTION master_update_node(node_id int, new_node_name text, new_node_port int)\n  IS 'change the location of a node';\n\n-- shard statistics\nCREATE OR REPLACE FUNCTION master_update_table_statistics(relation regclass)\nRETURNS VOID AS $$\nDECLARE\n\tcolocated_tables regclass[];\nBEGIN\n\tSELECT get_colocated_table_array(relation) INTO colocated_tables;\n\n\tPERFORM\n\t\tmaster_update_shard_statistics(shardid)\n\tFROM\n\t\tpg_dist_shard\n\tWHERE\n\t\tlogicalrelid = ANY (colocated_tables);\nEND;\n$$ LANGUAGE 'plpgsql';\nCOMMENT ON FUNCTION master_update_table_statistics(regclass)\n\tIS 'updates shard statistics of the given table and its colocated tables';\n\nCREATE OR REPLACE FUNCTION get_colocated_shard_array(bigint)\n\tRETURNS BIGINT[]\n\tLANGUAGE C STRICT\n\tAS 'citus', $$get_colocated_shard_array$$;\nCOMMENT ON FUNCTION get_colocated_shard_array(bigint)\n\tIS 'returns the array of colocated shards of the given shard';\n\n-- distributed backups\nCREATE OR REPLACE FUNCTION pg_catalog.citus_create_restore_point(text)\nRETURNS pg_lsn\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_create_restore_point$$;\nCOMMENT ON FUNCTION pg_catalog.citus_create_restore_point(text)\nIS 'temporarily block writes and create a named restore point on all nodes';\n\n-- functions for giving node a unique identifier\nCREATE OR REPLACE FUNCTION pg_catalog.citus_version()\n    RETURNS text\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$citus_version$$;\nCOMMENT ON FUNCTION pg_catalog.citus_version()\n    IS 'Citus version string';\n\nCREATE TABLE citus.pg_dist_node_metadata(\n    metadata jsonb NOT NULL\n);\nALTER TABLE citus.pg_dist_node_metadata SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.pg_dist_node_metadata TO public;\n\nCREATE FUNCTION pg_catalog.citus_server_id()\n    RETURNS uuid\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_server_id$$;\nCOMMENT ON FUNCTION citus_server_id()\n    IS 'generates a random UUID to be used as server identifier';\n\n-- Insert the latest extension version into pg_dist_node_metadata\n-- for new installations.\n--\n-- While users could technically upgrade to an intermediate version\n-- everything in Citus fails until it is upgraded to the latest version,\n-- so it seems safe to use the latest.\nINSERT INTO pg_dist_node_metadata\nSELECT jsonb_build_object('server_id', citus_server_id()::text,\n                          'last_upgrade_version', default_version)\nFROM pg_available_extensions\nWHERE name = 'citus';\n\n-- rebalancer functions\nCREATE TYPE citus.shard_transfer_mode AS ENUM (\n   'auto',\n   'force_logical',\n   'block_writes'\n);\n\nCREATE OR REPLACE FUNCTION master_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$master_move_shard_placement$$;\n\nCOMMENT ON FUNCTION master_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'move a shard from a the source node to the destination node';\n\nCREATE FUNCTION master_copy_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tdo_repair bool DEFAULT true,\n\ttransfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$master_copy_shard_placement$$;\n\nCOMMENT ON FUNCTION master_copy_shard_placement(shard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tdo_repair bool,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'copy a shard from the source node to the destination node';\n\n-- intermediate result functions\nCREATE OR REPLACE FUNCTION pg_catalog.create_intermediate_result(result_id text, query text)\n    RETURNS bigint\n    LANGUAGE C STRICT VOLATILE\n    AS 'MODULE_PATHNAME', $$create_intermediate_result$$;\nCOMMENT ON FUNCTION pg_catalog.create_intermediate_result(text,text)\n    IS 'execute a query and write its results to local result file';\n\nCREATE OR REPLACE FUNCTION pg_catalog.broadcast_intermediate_result(result_id text, query text)\n    RETURNS bigint\n    LANGUAGE C STRICT VOLATILE\n    AS 'MODULE_PATHNAME', $$broadcast_intermediate_result$$;\nCOMMENT ON FUNCTION pg_catalog.broadcast_intermediate_result(text,text)\n    IS 'execute a query and write its results to an result file on all workers';\n\nCREATE TYPE pg_catalog.citus_copy_format AS ENUM ('csv', 'binary', 'text');\n\nCREATE OR REPLACE FUNCTION pg_catalog.read_intermediate_result(result_id text, format pg_catalog.citus_copy_format default 'csv')\n    RETURNS SETOF record\n    LANGUAGE C STRICT VOLATILE PARALLEL SAFE\n    AS 'MODULE_PATHNAME', $$read_intermediate_result$$;\nCOMMENT ON FUNCTION pg_catalog.read_intermediate_result(text,pg_catalog.citus_copy_format)\n    IS 'read a file and return it as a set of records';\n\nCREATE FUNCTION pg_catalog.citus_text_send_as_jsonb(text)\nRETURNS bytea\nLANGUAGE C IMMUTABLE PARALLEL SAFE STRICT\nAS 'MODULE_PATHNAME', $$citus_text_send_as_jsonb$$;\n\n-- Citus json aggregate helpers\n\nCREATE FUNCTION pg_catalog.citus_jsonb_concatenate(state jsonb, val jsonb)\n\tRETURNS jsonb\n\tLANGUAGE SQL\nAS $function$\n\tSELECT CASE\n\t\tWHEN val IS NULL THEN state\n\t\tWHEN jsonb_typeof(state) = 'null' THEN val\n\t\tELSE state || val\n\tEND;\n$function$;\n\nCREATE FUNCTION pg_catalog.citus_jsonb_concatenate_final(state jsonb)\n\tRETURNS jsonb\n\tLANGUAGE SQL\nAS $function$\n\tSELECT CASE WHEN jsonb_typeof(state) = 'null' THEN NULL ELSE state END;\n$function$;\n\nCREATE FUNCTION pg_catalog.citus_json_concatenate(state json, val json)\n\tRETURNS json\n\tLANGUAGE SQL\nAS $function$\n\tSELECT CASE\n\t\tWHEN val IS NULL THEN state\n\t\tWHEN json_typeof(state) = 'null' THEN val\n\t\tWHEN json_typeof(state) = 'object' THEN\n \t\t\t(SELECT json_object_agg(key, value) FROM (\n\t\t \t\tSELECT * FROM json_each(state)\n\t\t \t\tUNION ALL\n\t\t \t\tSELECT * FROM json_each(val)\n\t \t\t) t)\n\t\tELSE\n\t \t\t(SELECT json_agg(a) FROM (\n\t \t\t\tSELECT json_array_elements(state) AS a\n\t\t \t\tUNION ALL\n\t\t \t\tSELECT json_array_elements(val) AS a\n\t \t\t) t)\n\tEND;\n$function$;\n\nCREATE FUNCTION pg_catalog.citus_json_concatenate_final(state json)\n\tRETURNS json\n\tLANGUAGE SQL\nAS $function$\n\tSELECT CASE WHEN json_typeof(state) = 'null' THEN NULL ELSE state END;\n$function$;\n\n\n-- Citus json aggregates\n\nCREATE AGGREGATE pg_catalog.jsonb_cat_agg(jsonb) (\n    SFUNC = citus_jsonb_concatenate,\n    FINALFUNC = citus_jsonb_concatenate_final,\n    STYPE = jsonb,\n    INITCOND = 'null'\n);\nCOMMENT ON AGGREGATE pg_catalog.jsonb_cat_agg(jsonb)\n    IS 'concatenate input jsonbs into a single jsonb';\n\nCREATE AGGREGATE pg_catalog.json_cat_agg(json) (\n    SFUNC = citus_json_concatenate,\n    FINALFUNC = citus_json_concatenate_final,\n    STYPE = json,\n    INITCOND = 'null'\n);\nCOMMENT ON AGGREGATE pg_catalog.json_cat_agg(json)\n    IS 'concatenate input jsons into a single json';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_drop_trigger()\n    RETURNS event_trigger\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cdbdt$\nDECLARE\n    v_obj record;\n    sequence_names text[] := '{}';\n    table_colocation_id integer;\n    propagate_drop boolean := false;\nBEGIN\n    -- collect set of dropped sequences to drop on workers later\n    SELECT array_agg(object_identity) INTO sequence_names\n    FROM pg_event_trigger_dropped_objects()\n    WHERE object_type = 'sequence';\n\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n                 WHERE object_type IN ('table', 'foreign table')\n    LOOP\n        -- drop all shards and the metadata\n        PERFORM master_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n        PERFORM master_drop_distributed_table_metadata(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n    END LOOP;\n\n    IF cardinality(sequence_names) = 0 THEN\n        RETURN;\n    END IF;\n\n    PERFORM master_drop_sequences(sequence_names);\nEND;\n$cdbdt$;\nCOMMENT ON FUNCTION pg_catalog.citus_drop_trigger()\n    IS 'perform checks and actions at the end of DROP actions';\n\nCREATE EVENT TRIGGER citus_cascade_to_partition\n    ON SQL_DROP\n    EXECUTE PROCEDURE citus_drop_trigger();\n\n-- pg_dist_authinfo\nCREATE FUNCTION pg_catalog.role_exists(name)\n    RETURNS boolean\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$role_exists$$;\nCOMMENT ON FUNCTION role_exists(name) IS 'returns whether a role exists';\n\nCREATE FUNCTION pg_catalog.authinfo_valid(text)\n\tRETURNS boolean\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$authinfo_valid$$;\nCOMMENT ON FUNCTION authinfo_valid(text) IS 'returns whether an authinfo is valid';\n\nCREATE TABLE citus.pg_dist_authinfo (\n\tnodeid integer NOT NULL,\n\trolename name NOT NULL\n\t              CONSTRAINT role_exists\n\t\t\t\t\t\t\t\tCHECK (role_exists(rolename)),\n\tauthinfo text NOT NULL\n\t              CONSTRAINT authinfo_valid\n\t\t\t\t\t\t\t\tCHECK (authinfo_valid(authinfo))\n);\n\nCREATE UNIQUE INDEX pg_dist_authinfo_identification_index\nON citus.pg_dist_authinfo (rolename, nodeid DESC);\n\nALTER TABLE citus.pg_dist_authinfo SET SCHEMA pg_catalog;\n\nREVOKE ALL ON pg_catalog.pg_dist_authinfo FROM PUBLIC;\n\n\nCREATE FUNCTION master_dist_authinfo_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'citus', $$master_dist_authinfo_cache_invalidate$$;\nCOMMENT ON FUNCTION master_dist_authinfo_cache_invalidate()\n    IS 'register authinfo cache invalidation on any modifications';\n\nCREATE FUNCTION task_tracker_conninfo_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'citus', $$task_tracker_conninfo_cache_invalidate$$;\nCOMMENT ON FUNCTION task_tracker_conninfo_cache_invalidate()\n    IS 'invalidate task-tracker conninfo cache';\n\nCREATE TRIGGER dist_authinfo_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE\n    ON pg_catalog.pg_dist_authinfo\n    FOR EACH STATEMENT EXECUTE PROCEDURE master_dist_authinfo_cache_invalidate();\n\nCREATE TRIGGER dist_authinfo_task_tracker_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE\n    ON pg_catalog.pg_dist_authinfo\n    FOR EACH STATEMENT EXECUTE PROCEDURE task_tracker_conninfo_cache_invalidate();\n\n-- citus_stat_statements\nCREATE FUNCTION pg_catalog.citus_query_stats(OUT queryid bigint,\n\t\t\t\t\t\t\t\t\t\t\t OUT userid oid,\n\t\t\t\t\t\t\t\t\t\t\t OUT dbid oid,\n\t\t\t\t\t\t\t\t\t\t\t OUT executor bigint,\n\t\t\t\t\t\t\t\t\t\t\t OUT partition_key text,\n\t\t\t\t\t\t\t\t\t\t\t OUT calls bigint)\nRETURNS SETOF record\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_query_stats$$;\n\nCREATE FUNCTION pg_catalog.citus_stat_statements_reset()\nRETURNS VOID\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_stat_statements_reset$$;\n\nCREATE FUNCTION pg_catalog.citus_stat_statements(OUT queryid bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT userid oid,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT dbid oid,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT query text,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT executor bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT partition_key text,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT calls bigint)\nRETURNS SETOF record\nLANGUAGE plpgsql\nAS $citus_stat_statements$\nBEGIN\n IF EXISTS (\n \tSELECT extname FROM pg_extension\n \tWHERE extname = 'pg_stat_statements')\n THEN\n \tRETURN QUERY SELECT pss.queryid, pss.userid, pss.dbid, pss.query, cqs.executor,\n \t\t\t\t\t\tcqs.partition_key, cqs.calls\n \t\t\t\t FROM pg_stat_statements(true) pss\n \t\t\t\t \tJOIN citus_query_stats() cqs\n \t\t\t\t \tUSING (queryid);\n ELSE\n    RAISE EXCEPTION 'pg_stat_statements is not installed'\n    \tUSING HINT = 'install pg_stat_statements extension and try again';\n END IF;\nEND;\n$citus_stat_statements$;\n\nCREATE VIEW citus.citus_stat_statements as SELECT * FROM pg_catalog.citus_stat_statements();\nALTER VIEW citus.citus_stat_statements SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_stat_statements TO public;\n\nCREATE FUNCTION pg_catalog.citus_executor_name(executor_type int)\nRETURNS TEXT\nLANGUAGE plpgsql\nAS $function$\nBEGIN\n\tIF (executor_type = 1) THEN\n\t\tRETURN 'real-time';\n\tELSIF (executor_type = 2) THEN\n\t\tRETURN 'task-tracker';\n\tELSIF (executor_type = 3) THEN\n\t\tRETURN 'router';\n\tELSIF (executor_type = 4) THEN\n\t\tRETURN 'insert-select';\n\tELSE\n\t\tRETURN 'unknown';\n\tEND IF;\nEND;\n$function$;\n\nDROP VIEW pg_catalog.citus_stat_statements;\n\nCREATE VIEW citus.citus_stat_statements AS\nSELECT\n  queryid,\n  userid,\n  dbid,\n  query,\n  pg_catalog.citus_executor_name(executor::int) AS executor,\n  partition_key,\n  calls\nFROM pg_catalog.citus_stat_statements();\nALTER VIEW citus.citus_stat_statements SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_stat_statements TO public;\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_stat_statements_reset() FROM PUBLIC;\n\n-- pg_dist_poolinfo\nCREATE FUNCTION pg_catalog.poolinfo_valid(text)\n\tRETURNS boolean\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$poolinfo_valid$$;\nCOMMENT ON FUNCTION pg_catalog.poolinfo_valid(text) IS 'returns whether a poolinfo is valid';\n\nCREATE TABLE citus.pg_dist_poolinfo (\n    nodeid integer PRIMARY KEY\n                   REFERENCES pg_dist_node(nodeid)\n                              ON DELETE CASCADE,\n    poolinfo text NOT NULL\n\t              CONSTRAINT poolinfo_valid\n\t\t\t\t\t\t\t\tCHECK (poolinfo_valid(poolinfo))\n);\n\nALTER TABLE citus.pg_dist_poolinfo SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.pg_dist_poolinfo TO public;\n\nALTER FUNCTION master_dist_authinfo_cache_invalidate()\nRENAME TO master_conninfo_cache_invalidate;\n\nCREATE TRIGGER dist_poolinfo_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE\n    ON pg_catalog.pg_dist_poolinfo\n    FOR EACH STATEMENT EXECUTE PROCEDURE master_conninfo_cache_invalidate();\n\nCREATE TRIGGER dist_poolinfo_task_tracker_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE\n    ON pg_catalog.pg_dist_poolinfo\n    FOR EACH STATEMENT EXECUTE PROCEDURE task_tracker_conninfo_cache_invalidate();\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-10--8.0-11.sql",
    "content": "--  citus--8.0-10--8.0-11\nSET search_path = 'pg_catalog';\n\n-- Deprecated functions\nDROP FUNCTION IF EXISTS worker_hash_partition_table(bigint,integer,text,text,oid,integer);\nDROP FUNCTION IF EXISTS worker_foreign_file_path(text);\nDROP FUNCTION IF EXISTS worker_find_block_local_path(bigint,text[]);\nDROP FUNCTION IF EXISTS worker_fetch_query_results_file(bigint,integer,integer,text,integer);\nDROP FUNCTION IF EXISTS master_drop_distributed_table_metadata(regclass,text,text);\n\n-- Testing functions\nREVOKE ALL ON FUNCTION citus_blocking_pids(integer) FROM PUBLIC;\nREVOKE ALL ON FUNCTION citus_isolation_test_session_is_blocked(integer,integer[]) FROM PUBLIC;\n\n-- Maintenance function\nREVOKE ALL ON FUNCTION worker_cleanup_job_schema_cache() FROM PUBLIC;\nREVOKE ALL ON FUNCTION recover_prepared_transactions() FROM PUBLIC;\nREVOKE ALL ON FUNCTION check_distributed_deadlocks() FROM PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-11--8.0-12.sql",
    "content": "--  citus--8.0-11--8.0-12\nSET search_path = 'pg_catalog';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_stat_statements(OUT queryid bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT userid oid,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT dbid oid,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT query text,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT executor bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT partition_key text,\n\t\t\t\t\t\t\t\t\t\t\t\t OUT calls bigint)\nRETURNS SETOF record\nLANGUAGE plpgsql\nAS $citus_stat_statements$\nBEGIN\n IF EXISTS (\n \tSELECT extname FROM pg_extension\n \tWHERE extname = 'pg_stat_statements')\n THEN\n \tRETURN QUERY SELECT pss.queryid, pss.userid, pss.dbid, pss.query, cqs.executor,\n \t\t\t\t\t\tcqs.partition_key, cqs.calls\n \t\t\t\t FROM pg_stat_statements(true) pss\n \t\t\t\t \tJOIN citus_query_stats() cqs\n \t\t\t\t \tUSING (queryid, userid, dbid);\n ELSE\n    RAISE EXCEPTION 'pg_stat_statements is not installed'\n    \tUSING HINT = 'install pg_stat_statements extension and try again';\n END IF;\nEND;\n$citus_stat_statements$;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-12--8.0-13.sql",
    "content": "--  citus--8.0-12--8.0-13\nCREATE FUNCTION citus_check_defaults_for_sslmode()\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_check_defaults_for_sslmode$$;\n\nDO LANGUAGE plpgsql\n$$\nBEGIN\n    -- Citus 8.1 and higher default to requiring SSL for all outgoing connections\n    -- (specified by citus.node_conninfo).\n    -- If it looks like we are about to enforce ssl for outgoing connections on a postgres\n    -- installation that does not have ssl turned on we fall back to sslmode=prefer for\n    -- outgoing connections.\n    -- This will only be the case for upgrades from previous versions of Citus, on new\n    -- installations we will have turned on ssl in an earlier stage of the extension\n    -- creation.\n    IF\n        NOT current_setting('ssl')::boolean\n\tTHEN\n\t    PERFORM citus_check_defaults_for_sslmode();\n\tEND IF;\nEND;\n$$;\n\nDROP FUNCTION citus_check_defaults_for_sslmode();\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-13--8.1-1.sql",
    "content": "--  citus--8.0-13--8.1-1.sql\n\n--  bump version to 8.1-1\n\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-2--8.0-3.sql",
    "content": "--  citus--8.0-2--8.0-3\nSET search_path = 'pg_catalog';\n\nCREATE FUNCTION master_remove_partition_metadata(logicalrelid regclass,\n                                                   schema_name text,\n                                                   table_name text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_remove_partition_metadata$$;\nCOMMENT ON FUNCTION master_remove_partition_metadata(logicalrelid regclass,\n                                                       schema_name text,\n                                                       table_name text)\n    IS 'deletes the partition metadata of a distributed table';\n\nCREATE OR REPLACE FUNCTION master_remove_distributed_table_metadata_from_workers(logicalrelid regclass,\n                                                   schema_name text,\n                                                   table_name text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_remove_distributed_table_metadata_from_workers$$;\nCOMMENT ON FUNCTION master_remove_distributed_table_metadata_from_workers(logicalrelid regclass,\n                                                       schema_name text,\n                                                       table_name text)\n    IS 'drops the table and removes all the metadata belonging the distributed table in the worker nodes with metadata.';\n\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_drop_trigger()\n    RETURNS event_trigger\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cdbdt$\nDECLARE\n    v_obj record;\n    sequence_names text[] := '{}';\n    table_colocation_id integer;\n    propagate_drop boolean := false;\nBEGIN\n    -- collect set of dropped sequences to drop on workers later\n    SELECT array_agg(object_identity) INTO sequence_names\n    FROM pg_event_trigger_dropped_objects()\n    WHERE object_type = 'sequence';\n\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n                 WHERE object_type IN ('table', 'foreign table')\n    LOOP\n        -- first drop the table and metadata on the workers\n        -- then drop all the shards on the workers\n        -- finally remove the pg_dist_partition entry on the coordinator\n        PERFORM master_remove_distributed_table_metadata_from_workers(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n        PERFORM master_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n        PERFORM master_remove_partition_metadata(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n    END LOOP;\n\n    IF cardinality(sequence_names) = 0 THEN\n        RETURN;\n    END IF;\n\n    PERFORM master_drop_sequences(sequence_names);\nEND;\n$cdbdt$;\nCOMMENT ON FUNCTION pg_catalog.citus_drop_trigger()\n    IS 'perform checks and actions at the end of DROP actions';\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-3--8.0-4.sql",
    "content": "--  citus--8.0-3--8.0-4\nSET search_path = 'pg_catalog';\n\nCREATE OR REPLACE FUNCTION lock_relation_if_exists(table_name text, lock_mode text)\nRETURNS BOOL\nLANGUAGE C STRICT as 'MODULE_PATHNAME',\n$$lock_relation_if_exists$$;\nCOMMENT ON FUNCTION lock_relation_if_exists(table_name text, lock_mode text)\nIS 'used internally to locks relation in the lock_mode if the relation exists without throwing errors; consider using LOCK * IN * MODE instead';\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-4--8.0-5.sql",
    "content": "--  citus--8.0-4--8.0-5.sql\nSET search_path = 'pg_catalog';\n\n\nDROP FUNCTION IF EXISTS get_all_active_transactions();\n\n\nCREATE OR REPLACE FUNCTION get_all_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4, OUT worker_query BOOL,\n                                                       OUT transaction_number int8, OUT transaction_stamp timestamptz)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$get_all_active_transactions$$;\n\nCOMMENT ON FUNCTION get_all_active_transactions(OUT datid oid, OUT datname text, OUT process_id int, OUT initiator_node_identifier int4, OUT worker_query BOOL,\n                                                OUT transaction_number int8, OUT transaction_stamp timestamptz)\nIS 'returns distributed transaction ids of active distributed transactions';\n\n\nCREATE OR REPLACE FUNCTION citus_dist_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT master_query_host_name text, OUT master_query_host_port int,\n                                                    OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                                    OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                                    OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                                    OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                                    OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$citus_dist_stat_activity$$;\n\nCOMMENT ON FUNCTION citus_dist_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT master_query_host_name text, OUT master_query_host_port int,\n                                             OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                             OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                             OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                             OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                             OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nIS 'returns distributed transaction activity on distributed tables';\n\nCREATE VIEW citus.citus_dist_stat_activity AS\nSELECT * FROM pg_catalog.citus_dist_stat_activity();\nALTER VIEW citus.citus_dist_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_dist_stat_activity TO PUBLIC;\n\n\n\nCREATE OR REPLACE FUNCTION citus_worker_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT master_query_host_name text, OUT master_query_host_port int,\n                                                      OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                                      OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                                      OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                                      OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                                      OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$citus_worker_stat_activity$$;\n\nCOMMENT ON FUNCTION citus_worker_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT master_query_host_name text, OUT master_query_host_port int,\n                                               OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                               OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                               OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                               OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                               OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nIS 'returns distributed transaction activity on shards of distributed tables';\n\nCREATE VIEW citus.citus_worker_stat_activity AS\nSELECT * FROM pg_catalog.citus_worker_stat_activity();\nALTER VIEW citus.citus_worker_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_worker_stat_activity TO PUBLIC;\n\nRESET search_path;\n\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-5--8.0-6.sql",
    "content": "--  citus--8.0-5--8.0-6\nSET search_path = 'pg_catalog';\n\nCREATE FUNCTION get_global_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4, OUT worker_query BOOL, OUT transaction_number int8, OUT transaction_stamp timestamptz)\n  RETURNS SETOF RECORD\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$get_global_active_transactions$$;\n COMMENT ON FUNCTION get_global_active_transactions(OUT database_id oid, OUT process_id int, OUT initiator_node_identifier int4, OUT transaction_number int8, OUT transaction_stamp timestamptz)\n     IS 'returns distributed transaction ids of active distributed transactions from each node of the cluster';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_blocking_pids(pBlockedPid integer)\nRETURNS int4[] AS $$\n  DECLARE\n    mLocalBlockingPids int4[];\n    mRemoteBlockingPids int4[];\n    mLocalTransactionNum int8;\n    workerProcessId integer := current_setting('citus.isolation_test_session_remote_process_id');\n    coordinatorProcessId integer := current_setting('citus.isolation_test_session_process_id');\n  BEGIN\n    SELECT pg_catalog.old_pg_blocking_pids(pBlockedPid) INTO mLocalBlockingPids;\n\n    IF (array_length(mLocalBlockingPids, 1) > 0) THEN\n      RETURN mLocalBlockingPids;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    -- Note that worker process may be blocked or waiting for a lock. So we need to\n    -- get transaction number for both of them. Following IF provides the transaction\n    -- number when the worker process waiting for other session.\n    IF EXISTS (SELECT transaction_number FROM get_global_active_transactions()\n               WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId) THEN\n\n      SELECT transaction_number INTO mLocalTransactionNum\n        FROM get_global_active_transactions() WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId;\n    ELSE\n      -- Check whether transactions initiated from the coordinator get locked\n      SELECT transaction_number INTO mLocalTransactionNum\n        FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n    END IF;\n\n    IF EXISTS (SELECT waiting_transaction_num FROM dump_global_wait_edges()\n                 WHERE waiting_transaction_num = mLocalTransactionNum) THEN\n      SELECT array_agg(pBlockedPid) INTO mRemoteBlockingPids;\n    END IF;\n\n    RETURN mRemoteBlockingPids;\n  END;\n$$ LANGUAGE plpgsql;\n\n#include \"udfs/citus_isolation_test_session_is_blocked/8.0-6.sql\"\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-6--8.0-7.sql",
    "content": "--  citus--8.0-6--8.0-7\nSET search_path = 'pg_catalog';\n\nCREATE VIEW citus.citus_lock_waits AS\n\nWITH\ncitus_dist_stat_activity AS\n(\n  SELECT * FROM citus_dist_stat_activity\n),\nunique_global_wait_edges AS\n(\n\tSELECT DISTINCT ON(waiting_node_id, waiting_transaction_num, blocking_node_id, blocking_transaction_num) * FROM dump_global_wait_edges()\n),\ncitus_dist_stat_activity_with_node_id AS\n(\n  SELECT\n  citus_dist_stat_activity.*, (CASE citus_dist_stat_activity.master_query_host_name WHEN 'coordinator_host' THEN 0 ELSE pg_dist_node.nodeid END) as initiator_node_id\n  FROM\n  citus_dist_stat_activity LEFT JOIN pg_dist_node\n  ON\n  citus_dist_stat_activity.master_query_host_name = pg_dist_node.nodename AND\n  citus_dist_stat_activity.master_query_host_port = pg_dist_node.nodeport\n)\nSELECT\n waiting.pid AS waiting_pid,\n blocking.pid AS blocking_pid,\n waiting.query AS blocked_statement,\n blocking.query AS current_statement_in_blocking_process,\n waiting.initiator_node_id AS waiting_node_id,\n blocking.initiator_node_id AS blocking_node_id,\n waiting.master_query_host_name AS waiting_node_name,\n blocking.master_query_host_name AS blocking_node_name,\n waiting.master_query_host_port AS waiting_node_port,\n blocking.master_query_host_port AS blocking_node_port\nFROM\n unique_global_wait_edges\nJOIN\n citus_dist_stat_activity_with_node_id waiting ON (unique_global_wait_edges.waiting_transaction_num = waiting.transaction_number AND unique_global_wait_edges.waiting_node_id = waiting.initiator_node_id)\nJOIN\n citus_dist_stat_activity_with_node_id blocking ON (unique_global_wait_edges.blocking_transaction_num = blocking.transaction_number AND unique_global_wait_edges.blocking_node_id = blocking.initiator_node_id);\n\nALTER VIEW citus.citus_lock_waits SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_lock_waits TO PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-7--8.0-8.sql",
    "content": "--  citus--8.0-7--8.0-8\nSET search_path = 'pg_catalog';\n\nDROP FUNCTION IF EXISTS pg_catalog.worker_drop_distributed_table(logicalrelid Oid);\n\n\nCREATE FUNCTION worker_drop_distributed_table(table_name text)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_drop_distributed_table$$;\n\nCOMMENT ON FUNCTION worker_drop_distributed_table(table_name text)\n    IS 'drop the distributed table and its reference from metadata tables';\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-8--8.0-9.sql",
    "content": "--  citus--8.0-8--8.0-9\nSET search_path = 'pg_catalog';\n\nREVOKE ALL ON FUNCTION master_activate_node(text,int) FROM PUBLIC;\nREVOKE ALL ON FUNCTION master_add_inactive_node(text,int,int,noderole,name) FROM PUBLIC;\nREVOKE ALL ON FUNCTION master_add_node(text,int,int,noderole,name) FROM PUBLIC;\nREVOKE ALL ON FUNCTION master_add_secondary_node(text,int,text,int,name) FROM PUBLIC;\nREVOKE ALL ON FUNCTION master_disable_node(text,int) FROM PUBLIC;\nREVOKE ALL ON FUNCTION master_remove_node(text,int) FROM PUBLIC;\nREVOKE ALL ON FUNCTION master_update_node(int,text,int) FROM PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.0-9--8.0-10.sql",
    "content": "--  citus--8.0-9--8.0-10\nSET search_path = 'pg_catalog';\n\nCREATE FUNCTION worker_execute_sql_task(jobid bigint, taskid integer, query text, binary bool)\nRETURNS bigint\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_execute_sql_task$$;\nCOMMENT ON FUNCTION worker_execute_sql_task(bigint, integer, text, bool)\nIS 'execute a query and write the results to a task file';\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.1-1--8.2-1.sql",
    "content": "--  citus--8.1-1--8.2-1.sql\n\n--  bump version to 8.2-1\n\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.2-1--8.2-2.sql",
    "content": "--  citus--8.2-1--8.2-2.sql\n\nDROP FUNCTION IF EXISTS pg_catalog.create_insert_proxy_for_table(regclass,regclass);\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.2-2--8.2-3.sql",
    "content": "--  citus--8.2-2--8.2-3\n\nSET search_path = 'pg_catalog';\n\nDROP FUNCTION master_update_node(node_id int,\n                                 new_node_name text,\n                                 new_node_port int);\n\nCREATE OR REPLACE FUNCTION master_update_node(node_id int,\n                                              new_node_name text,\n                                              new_node_port int,\n                                              force bool DEFAULT false,\n                                              lock_cooldown int DEFAULT 10000)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$master_update_node$$;\n\nCOMMENT ON FUNCTION master_update_node(node_id int,\n                                       new_node_name text,\n                                       new_node_port int,\n                                       force bool,\n                                       lock_cooldown int)\n  IS 'change the location of a node. when force => true it will wait lock_cooldown ms before killing competing locks';\n\nREVOKE ALL ON FUNCTION master_update_node(int,text,int,bool,int) FROM PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.2-3--8.2-4.sql",
    "content": "--  citus--8.2-3--8.2-4\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_executor_name(executor_type int)\nRETURNS text\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_executor_name$$;\nCOMMENT ON FUNCTION pg_catalog.citus_executor_name(int)\nIS 'return the name of the external based for the value in citus_stat_statements() output';\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.2-4--8.3-1.sql",
    "content": "--  citus--8.2-4--8.3-1\n\n--  bump version to 8.3-1\n\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--8.3-1--9.0-1.sql",
    "content": "--  citus--8.3-1--9.0-1\n\nSET search_path = 'pg_catalog';\n\n-- We swapped the groupid and nodeid sequences when creating pg_dist_node\nALTER TABLE pg_dist_node ALTER COLUMN groupid SET DEFAULT nextval ('pg_dist_groupid_seq');\nALTER TABLE pg_dist_node ALTER COLUMN nodeid SET DEFAULT nextval('pg_dist_node_nodeid_seq');\n\nCREATE SCHEMA IF NOT EXISTS citus_internal;\n\n-- move citus internal functions to citus_internal to make space in the citus schema for\n-- our public interface\nALTER FUNCTION citus.find_groupid_for_node SET SCHEMA citus_internal;\nALTER FUNCTION citus.pg_dist_node_trigger_func SET SCHEMA citus_internal;\nALTER FUNCTION citus.pg_dist_shard_placement_trigger_func SET SCHEMA citus_internal;\nALTER FUNCTION citus.refresh_isolation_tester_prepared_statement SET SCHEMA citus_internal;\nALTER FUNCTION citus.replace_isolation_tester_func SET SCHEMA citus_internal;\nALTER FUNCTION citus.restore_isolation_tester_func SET SCHEMA citus_internal;\n\n-- we can now safely grant usage on the citus schema to use types\nGRANT USAGE ON SCHEMA citus TO public;\n\n#include \"udfs/pg_dist_shard_placement_trigger_func/9.0-1.sql\"\n#include \"udfs/worker_create_or_replace_object/9.0-1.sql\"\n\nCREATE OR REPLACE FUNCTION pg_catalog.master_unmark_object_distributed(classid oid, objid oid, objsubid int)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_unmark_object_distributed$$;\nCOMMENT ON FUNCTION pg_catalog.master_unmark_object_distributed(classid oid, objid oid, objsubid int)\n    IS 'remove an object address from citus.pg_dist_object once the object has been deleted';\n\nCREATE TABLE citus.pg_dist_object (\n\t-- fields used for composite primary key\n    classid oid NOT NULL,\n    objid oid NOT NULL,\n    objsubid integer NOT NULL,\n\n    -- fields used for upgrades\n    type text DEFAULT NULL,\n    object_names text[] DEFAULT NULL,\n    object_args text[] DEFAULT NULL,\n\n    -- fields that are only valid for distributed\n    -- functions/procedures\n    distribution_argument_index int,\n    colocationid int,\n\n    CONSTRAINT pg_dist_object_pkey PRIMARY KEY (classid, objid, objsubid)\n);\n\nCREATE FUNCTION master_dist_object_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$master_dist_object_cache_invalidate$$;\nCOMMENT ON FUNCTION master_dist_object_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\nCREATE TRIGGER dist_object_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE\n    ON citus.pg_dist_object\n    FOR EACH ROW EXECUTE PROCEDURE master_dist_object_cache_invalidate();\n\n#include \"udfs/create_distributed_function/9.0-1.sql\"\n\n#include \"udfs/citus_drop_trigger/9.0-1.sql\"\n#include \"udfs/citus_prepare_pg_upgrade/9.0-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/9.0-1.sql\"\n\n-- We truncate pg_dist_node during metadata syncing, but we do not want\n-- this to cascade to pg_dist_poolinfo, which is generally maintained\n-- by the operator.\nALTER TABLE pg_dist_poolinfo DROP CONSTRAINT pg_dist_poolinfo_nodeid_fkey;\n\n--  if the rebalancer extension is still around, drop it before creating Citus functions\nDROP EXTENSION IF EXISTS shard_rebalancer;\n\n#include \"udfs/get_rebalance_table_shards_plan/9.0-1.sql\"\n#include \"udfs/replicate_table_shards/9.0-1.sql\"\n#include \"udfs/rebalance_table_shards/9.0-1.sql\"\n#include \"udfs/get_rebalance_progress/9.0-1.sql\"\n\nDROP FUNCTION master_add_node(text, integer, integer, noderole, name);\nCREATE FUNCTION master_add_node(nodename text,\n                                nodeport integer,\n                                groupid integer default 0,\n                                noderole noderole default 'primary',\n                                nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$master_add_node$$;\nCOMMENT ON FUNCTION master_add_node(nodename text, nodeport integer,\n                                    groupid integer, noderole noderole, nodecluster name)\n  IS 'add node to the cluster';\n\nDROP FUNCTION master_add_inactive_node(text, integer, integer, noderole, name);\nCREATE FUNCTION master_add_inactive_node(nodename text,\n                                         nodeport integer,\n                                         groupid integer default 0,\n                                         noderole noderole default 'primary',\n                                         nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME',$$master_add_inactive_node$$;\nCOMMENT ON FUNCTION master_add_inactive_node(nodename text,nodeport integer,\n                                             groupid integer, noderole noderole,\n                                             nodecluster name)\n  IS 'prepare node by adding it to pg_dist_node';\n\nDROP FUNCTION master_activate_node(text, integer);\nCREATE FUNCTION master_activate_node(nodename text,\n                                     nodeport integer)\n    RETURNS INTEGER\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME',$$master_activate_node$$;\nCOMMENT ON FUNCTION master_activate_node(nodename text, nodeport integer)\n    IS 'activate a node which is in the cluster';\n\nDROP FUNCTION master_add_secondary_node(text, integer, text, integer, name);\nCREATE FUNCTION master_add_secondary_node(nodename text,\n                                          nodeport integer,\n                                          primaryname text,\n                                          primaryport integer,\n                                          nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$master_add_secondary_node$$;\nCOMMENT ON FUNCTION master_add_secondary_node(nodename text, nodeport integer,\n                                              primaryname text, primaryport integer,\n                                              nodecluster name)\n  IS 'add a secondary node to the cluster';\n\n\nREVOKE ALL ON FUNCTION master_activate_node(text,int) FROM PUBLIC;\nREVOKE ALL ON FUNCTION master_add_inactive_node(text,int,int,noderole,name) FROM PUBLIC;\nREVOKE ALL ON FUNCTION master_add_node(text,int,int,noderole,name) FROM PUBLIC;\nREVOKE ALL ON FUNCTION master_add_secondary_node(text,int,text,int,name) FROM PUBLIC;\n\nALTER TABLE pg_dist_node ADD COLUMN metadatasynced BOOLEAN DEFAULT FALSE;\nCOMMENT ON COLUMN pg_dist_node.metadatasynced IS\n    'indicates whether the node has the most recent metadata';\n\nCREATE FUNCTION worker_apply_sequence_command(create_sequence_command text,\n                                              sequence_type_id regtype DEFAULT 'bigint'::regtype)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_apply_sequence_command$$;\nCOMMENT ON FUNCTION worker_apply_sequence_command(text,regtype)\n    IS 'create a sequence which produces globally unique values';\n\n#include \"udfs/citus_isolation_test_session_is_blocked/9.0-1.sql\"\n\n\nCREATE FUNCTION ensure_truncate_trigger_is_after()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n    --\n    -- register triggers\n    --\n    FOR table_name, trigger_name IN SELECT tgrelid::regclass, tgname\n      FROM pg_dist_partition\n      JOIN pg_trigger ON tgrelid=logicalrelid\n      JOIN pg_class ON pg_class.oid=logicalrelid\n      WHERE\n        tgname LIKE 'truncate_trigger_%' AND tgfoid = 'citus_truncate_trigger'::regproc\n    LOOP\n        command := 'drop trigger ' || trigger_name || ' on ' || table_name;\n        EXECUTE command;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\nEND;\n$$;\n\nSELECT ensure_truncate_trigger_is_after();\nDROP FUNCTION ensure_truncate_trigger_is_after;\n\n-- This sequence is unused\nDROP SEQUENCE pg_catalog.pg_dist_jobid_seq;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.0-1--9.0-2.sql",
    "content": "-- Using the citus schema is a bad idea since many environments use \"citus\"\n-- as the main user and the \"citus\" schema then sits in front of the\n-- search_path.\nREVOKE USAGE ON SCHEMA citus FROM public;\n\n-- redefine distributed_tables_colocated to avoid using citus schema\n#include \"udfs/distributed_tables_colocated/9.0-2.sql\"\n\n-- type was used in old version of distributed_tables_colocated\nDROP TYPE citus.colocation_placement_type;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.0-2--9.1-1.sql",
    "content": "ALTER TABLE pg_catalog.pg_dist_node ADD shouldhaveshards bool NOT NULL DEFAULT true;\nCOMMENT ON COLUMN pg_catalog.pg_dist_node.shouldhaveshards IS\n    'indicates whether the node is eligible to contain data from distributed tables';\n\n#include \"udfs/master_set_node_property/9.1-1.sql\"\n#include \"udfs/master_drain_node/9.1-1.sql\"\n#include \"udfs/worker_create_schema/9.1-1.sql\"\n#include \"udfs/worker_repartition_cleanup/9.1-1.sql\"\n#include \"udfs/rebalance_table_shards/9.1-1.sql\"\n#include \"udfs/get_rebalance_table_shards_plan/9.1-1.sql\"\n#include \"udfs/master_add_node/9.1-1.sql\"\n#include \"udfs/master_add_inactive_node/9.1-1.sql\"\n#include \"udfs/alter_role_if_exists/9.1-1.sql\"\n\n-- we don't maintain replication factor of reference tables anymore and just\n-- use -1 instead.\nUPDATE pg_dist_colocation SET replicationfactor = -1 WHERE distributioncolumntype = 0;\n\n#include \"udfs/any_value/9.1-1.sql\"\n\n-- drop function which was used for upgrading from 6.0\n-- creation was removed from citus--7.0-1.sql\nDROP FUNCTION IF EXISTS pg_catalog.master_initialize_node_metadata;\n\n-- Support infrastructure for distributing aggregation\nCREATE FUNCTION pg_catalog.worker_partial_agg_sfunc(internal, oid, anyelement)\nRETURNS internal\nAS 'MODULE_PATHNAME'\nLANGUAGE C PARALLEL SAFE;\nCOMMENT ON FUNCTION pg_catalog.worker_partial_agg_sfunc(internal, oid, anyelement)\n    IS 'transition function for worker_partial_agg';\n\nCREATE FUNCTION pg_catalog.worker_partial_agg_ffunc(internal)\nRETURNS cstring\nAS 'MODULE_PATHNAME'\nLANGUAGE C PARALLEL SAFE;\nCOMMENT ON FUNCTION pg_catalog.worker_partial_agg_ffunc(internal)\n    IS 'finalizer for worker_partial_agg';\n\nCREATE FUNCTION pg_catalog.coord_combine_agg_sfunc(internal, oid, cstring, anyelement)\nRETURNS internal\nAS 'MODULE_PATHNAME'\nLANGUAGE C PARALLEL SAFE;\nCOMMENT ON FUNCTION pg_catalog.coord_combine_agg_sfunc(internal, oid, cstring, anyelement)\n    IS 'transition function for coord_combine_agg';\n\nCREATE FUNCTION pg_catalog.coord_combine_agg_ffunc(internal, oid, cstring, anyelement)\nRETURNS anyelement\nAS 'MODULE_PATHNAME'\nLANGUAGE C PARALLEL SAFE;\nCOMMENT ON FUNCTION pg_catalog.coord_combine_agg_ffunc(internal, oid, cstring, anyelement)\n    IS 'finalizer for coord_combine_agg';\n\n-- select worker_partial_agg(agg, ...)\n-- equivalent to\n-- select to_cstring(agg_without_ffunc(...))\nCREATE AGGREGATE pg_catalog.worker_partial_agg(oid, anyelement) (\n    STYPE = internal,\n    SFUNC = pg_catalog.worker_partial_agg_sfunc,\n    FINALFUNC = pg_catalog.worker_partial_agg_ffunc\n);\nCOMMENT ON AGGREGATE pg_catalog.worker_partial_agg(oid, anyelement)\n    IS 'support aggregate for implementing partial aggregation on workers';\n\n-- select coord_combine_agg(agg, col)\n-- equivalent to\n-- select agg_ffunc(agg_combine(from_cstring(col)))\nCREATE AGGREGATE pg_catalog.coord_combine_agg(oid, cstring, anyelement) (\n    STYPE = internal,\n    SFUNC = pg_catalog.coord_combine_agg_sfunc,\n    FINALFUNC = pg_catalog.coord_combine_agg_ffunc,\n    FINALFUNC_EXTRA\n);\nCOMMENT ON AGGREGATE pg_catalog.coord_combine_agg(oid, cstring, anyelement)\n    IS 'support aggregate for implementing combining partial aggregate results from workers';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_partial_agg_ffunc FROM PUBLIC;\nREVOKE ALL ON FUNCTION pg_catalog.worker_partial_agg_sfunc FROM PUBLIC;\nREVOKE ALL ON FUNCTION pg_catalog.coord_combine_agg_ffunc FROM PUBLIC;\nREVOKE ALL ON FUNCTION pg_catalog.coord_combine_agg_sfunc FROM PUBLIC;\nREVOKE ALL ON FUNCTION pg_catalog.worker_partial_agg FROM PUBLIC;\nREVOKE ALL ON FUNCTION pg_catalog.coord_combine_agg FROM PUBLIC;\n\nGRANT EXECUTE ON FUNCTION pg_catalog.worker_partial_agg_ffunc TO PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.worker_partial_agg_sfunc TO PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.coord_combine_agg_ffunc TO PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.coord_combine_agg_sfunc TO PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.worker_partial_agg TO PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.coord_combine_agg TO PUBLIC;\n\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.1-1--9.2-1.sql",
    "content": "#include \"udfs/read_intermediate_results/9.2-1.sql\"\n#include \"udfs/fetch_intermediate_results/9.2-1.sql\"\n#include \"udfs/worker_partition_query_result/9.2-1.sql\"\n\nALTER TABLE pg_catalog.pg_dist_colocation ADD distributioncolumncollation oid;\nUPDATE pg_catalog.pg_dist_colocation dc SET distributioncolumncollation = t.typcollation\n\tFROM pg_catalog.pg_type t WHERE t.oid = dc.distributioncolumntype;\nUPDATE pg_catalog.pg_dist_colocation dc SET distributioncolumncollation = 0 WHERE distributioncolumncollation IS NULL;\nALTER TABLE pg_catalog.pg_dist_colocation ALTER COLUMN distributioncolumncollation SET NOT NULL;\n\nDROP INDEX pg_dist_colocation_configuration_index;\n-- distributioncolumntype should be listed first so that this index can be used for looking up reference tables' colocation id\nCREATE INDEX pg_dist_colocation_configuration_index\nON pg_dist_colocation USING btree(distributioncolumntype, shardcount, replicationfactor, distributioncolumncollation);\n\nCREATE TABLE citus.pg_dist_rebalance_strategy(\n    name name NOT NULL,\n    default_strategy boolean NOT NULL DEFAULT false,\n    shard_cost_function regproc NOT NULL,\n    node_capacity_function regproc NOT NULL,\n    shard_allowed_on_node_function regproc NOT NULL,\n    default_threshold float4 NOT NULL,\n    minimum_threshold float4 NOT NULL DEFAULT 0,\n    UNIQUE(name)\n);\nALTER TABLE citus.pg_dist_rebalance_strategy SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.pg_dist_rebalance_strategy TO public;\n\n#include \"udfs/citus_validate_rebalance_strategy_functions/9.2-1.sql\"\n#include \"udfs/pg_dist_rebalance_strategy_trigger_func/9.2-1.sql\"\nCREATE TRIGGER pg_dist_rebalance_strategy_validation_trigger\n  BEFORE INSERT OR UPDATE ON pg_dist_rebalance_strategy\n  FOR EACH ROW EXECUTE PROCEDURE citus_internal.pg_dist_rebalance_strategy_trigger_func();\n\n#include \"udfs/citus_add_rebalance_strategy/9.2-1.sql\"\n#include \"udfs/citus_set_default_rebalance_strategy/9.2-1.sql\"\n\n#include \"udfs/citus_shard_cost_1/9.2-1.sql\"\n#include \"udfs/citus_shard_cost_by_disk_size/9.2-1.sql\"\n#include \"udfs/citus_node_capacity_1/9.2-1.sql\"\n#include \"udfs/citus_shard_allowed_on_node_true/9.2-1.sql\"\n\nINSERT INTO\n    pg_catalog.pg_dist_rebalance_strategy(\n        name,\n        default_strategy,\n        shard_cost_function,\n        node_capacity_function,\n        shard_allowed_on_node_function,\n        default_threshold,\n        minimum_threshold\n    ) VALUES (\n        'by_shard_count',\n        true,\n        'citus_shard_cost_1',\n        'citus_node_capacity_1',\n        'citus_shard_allowed_on_node_true',\n        0,\n        0\n    ), (\n        'by_disk_size',\n        false,\n        'citus_shard_cost_by_disk_size',\n        'citus_node_capacity_1',\n        'citus_shard_allowed_on_node_true',\n        0.1,\n        0.01\n    );\n\n\nCREATE FUNCTION citus_internal.pg_dist_rebalance_strategy_enterprise_check()\n  RETURNS TRIGGER\n  LANGUAGE C\n  AS 'MODULE_PATHNAME';\nCREATE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger\n  BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE ON pg_dist_rebalance_strategy\n  FOR EACH STATEMENT EXECUTE FUNCTION citus_internal.pg_dist_rebalance_strategy_enterprise_check();\n\n\n#include \"udfs/master_drain_node/9.2-1.sql\"\n#include \"udfs/rebalance_table_shards/9.2-1.sql\"\n#include \"udfs/get_rebalance_table_shards_plan/9.2-1.sql\"\n\n#include \"udfs/citus_prepare_pg_upgrade/9.2-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/9.2-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.2-1--9.2-2.sql",
    "content": "#include \"udfs/worker_create_schema/9.2-2.sql\"\n\n-- reserve UINT32_MAX (4294967295) for a special node\nALTER SEQUENCE pg_catalog.pg_dist_node_nodeid_seq MAXVALUE 4294967294;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.2-2--9.2-4.sql",
    "content": "-- we've some issues with versioning, and we're fixing it by bumping version\n-- from 9.2-2 to 9.2-4 see #3673 for details\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.2-4--9.3-2.sql",
    "content": "-- citus--9.2-4--9.3-2\n\n-- bump version to 9.3-2\n\n#include \"udfs/citus_extradata_container/9.3-2.sql\"\n#include \"udfs/update_distributed_table_colocation/9.3-2.sql\"\n#include \"udfs/replicate_reference_tables/9.3-2.sql\"\n#include \"udfs/citus_remote_connection_stats/9.3-2.sql\"\n#include \"udfs/worker_create_or_alter_role/9.3-2.sql\"\n#include \"udfs/truncate_local_data_after_distributing_table/9.3-2.sql\"\n-- add citus extension owner as a distributed object, if not already in there\nINSERT INTO citus.pg_dist_object SELECT\n  (SELECT oid FROM pg_class WHERE relname = 'pg_authid') AS oid,\n  (SELECT oid FROM pg_authid WHERE rolname = current_user) as objid,\n  0 as objsubid\nON CONFLICT DO NOTHING;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.3-1--9.2-4.sql",
    "content": "-- citus--9.3-1--9.2-4\n-- this is an unusual upgrade path, we are doing it because\n-- we have accidentally tagged master branch with v9.2-3\n-- however master branch was already bumped to v9.3-1\n-- with this file, we are undoing the catalog changes that\n-- have happened between 9.2-2 to 9.3-1, and making 9.2-4\n-- as the release that we can\n\n-- undo the changes for citus_extradata_container that happened on citus 9.3\nDROP FUNCTION IF EXISTS pg_catalog.citus_extradata_container(INTERNAL);\nCREATE FUNCTION pg_catalog.citus_extradata_container(INTERNAL)\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_extradata_container$$;\nCOMMENT ON FUNCTION pg_catalog.citus_extradata_container(INTERNAL)\n    IS 'placeholder function to store additional data in postgres node trees';\n\nDROP FUNCTION IF EXISTS pg_catalog.update_distributed_table_colocation(regclass, text);\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.3-2--9.4-1.sql",
    "content": "-- citus--9.3-2--9.4-1\n\n-- bump version to 9.4-1\n#include \"udfs/worker_last_saved_explain_analyze/9.4-1.sql\"\n#include \"udfs/worker_save_query_explain_analyze/9.4-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.4-1--9.4-2.sql",
    "content": "-- 9.4-1--9.4-2 was added later as a patch to fix a bug in our PG upgrade functions\n#include \"udfs/citus_prepare_pg_upgrade/9.4-2.sql\"\n#include \"udfs/citus_finish_pg_upgrade/9.4-2.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.4-1--9.5-1.sql",
    "content": "-- citus--9.4-1--9.5-1\n\n-- bump version to 9.5-1\n#include \"udfs/undistribute_table/9.5-1.sql\"\n#include \"udfs/create_citus_local_table/9.5-1.sql\"\n#include \"udfs/citus_drop_trigger/9.5-1.sql\"\n#include \"udfs/worker_record_sequence_dependency/9.5-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/9.5-1.sql\"\n#include \"udfs/citus_prepare_pg_upgrade/9.5-1.sql\"\n\nSET search_path = 'pg_catalog';\n\nDROP FUNCTION task_tracker_assign_task(bigint, integer, text);\nDROP FUNCTION task_tracker_task_status(bigint, integer);\nDROP FUNCTION task_tracker_cleanup_job(bigint);\nDROP FUNCTION worker_merge_files_and_run_query(bigint, integer, text, text);\nDROP FUNCTION worker_execute_sql_task(bigint, integer, text, bool);\nDROP TRIGGER dist_authinfo_task_tracker_cache_invalidate ON pg_catalog.pg_dist_authinfo;\nDROP TRIGGER dist_poolinfo_task_tracker_cache_invalidate ON pg_catalog.pg_dist_poolinfo;\nDROP FUNCTION task_tracker_conninfo_cache_invalidate();\nDROP FUNCTION master_drop_sequences(text[]);\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.4-2--9.4-1.sql",
    "content": "--\n-- 9.4-1--9.4-2 was added later as a patch to fix a bug in our PG upgrade functions\n--\n-- This script brings users who installed the patch released back to the 9.4-1\n-- upgrade path. We do this via a semantical downgrade since there has already been\n-- introduced new changes in the schema from 9.4-1 to 9.5-1. To make sure we include all\n-- changes made during that version change we decide to use the existing upgrade path from\n-- our later introduced 9.4-2 version.\n--\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.4-2--9.4-3.sql",
    "content": "-- 9.4-2--9.4-3 was added later as a patch to improve master_update_table_statistics\nCREATE OR REPLACE FUNCTION master_update_table_statistics(relation regclass)\nRETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_update_table_statistics$$;\nCOMMENT ON FUNCTION pg_catalog.master_update_table_statistics(regclass)\n\tIS 'updates shard statistics of the given table';\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.4-3--9.4-2.sql",
    "content": "-- citus--9.4-3--9.4-2\n-- This is a downgrade path that will revert the changes made in citus--9.4-2--9.4-3.sql\n-- 9.4-2--9.4-3 was added later as a patch to improve master_update_table_statistics.\n-- We have this downgrade script so that we can continue from the main upgrade path\n-- when upgrading to later versions.\nCREATE OR REPLACE FUNCTION master_update_table_statistics(relation regclass)\nRETURNS VOID AS $$\nDECLARE\n\tcolocated_tables regclass[];\nBEGIN\n\tSELECT get_colocated_table_array(relation) INTO colocated_tables;\n\n\tPERFORM\n\t\tmaster_update_shard_statistics(shardid)\n\tFROM\n\t\tpg_dist_shard\n\tWHERE\n\t\tlogicalrelid = ANY (colocated_tables);\nEND;\n$$ LANGUAGE 'plpgsql';\nCOMMENT ON FUNCTION master_update_table_statistics(regclass)\n\tIS 'updates shard statistics of the given table and its colocated tables';\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.5-1--10.0-4.sql",
    "content": "-- citus--9.5-1--10.0-4\n\n-- This migration file aims to fix the issues with upgrades on clusters without public schema.\n\n-- This file is created by the following command, and some more changes in a separate commit\n-- cat citus--9.5-1--10.0-1.sql citus--10.0-1--10.0-2.sql citus--10.0-2--10.0-3.sql > citus--9.5-1--10.0-4.sql\n\n-- copy of citus--9.5-1--10.0-1\nDROP FUNCTION pg_catalog.upgrade_to_reference_table(regclass);\nDROP FUNCTION IF EXISTS pg_catalog.citus_total_relation_size(regclass);\n\n#include \"udfs/citus_total_relation_size/10.0-1.sql\"\n#include \"udfs/citus_finish_pg_upgrade/10.0-1.sql\"\n#include \"udfs/alter_distributed_table/10.0-1.sql\"\n#include \"udfs/alter_table_set_access_method/10.0-1.sql\"\n#include \"udfs/undistribute_table/10.0-1.sql\"\n#include \"udfs/create_citus_local_table/10.0-1.sql\"\n#include \"udfs/citus_set_coordinator_host/10.0-1.sql\"\n#include \"udfs/citus_add_node/10.0-1.sql\"\n#include \"udfs/citus_activate_node/10.0-1.sql\"\n#include \"udfs/citus_add_inactive_node/10.0-1.sql\"\n#include \"udfs/citus_add_secondary_node/10.0-1.sql\"\n#include \"udfs/citus_disable_node/10.0-1.sql\"\n#include \"udfs/citus_drain_node/10.0-1.sql\"\n#include \"udfs/citus_remove_node/10.0-1.sql\"\n#include \"udfs/citus_set_node_property/10.0-1.sql\"\n#include \"udfs/citus_unmark_object_distributed/10.0-1.sql\"\n#include \"udfs/citus_update_node/10.0-1.sql\"\n#include \"udfs/citus_update_shard_statistics/10.0-1.sql\"\n#include \"udfs/citus_update_table_statistics/10.0-1.sql\"\n#include \"udfs/citus_copy_shard_placement/10.0-1.sql\"\n#include \"udfs/citus_move_shard_placement/10.0-1.sql\"\n#include \"udfs/citus_drop_trigger/10.0-1.sql\"\n#include \"udfs/worker_change_sequence_dependency/10.0-1.sql\"\n#include \"udfs/remove_local_tables_from_metadata/10.0-1.sql\"\n\n--#include \"../../columnar/sql/columnar--9.5-1--10.0-1.sql\"\nDO $check_columnar$\nBEGIN\nIF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_extension AS e\n             INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid)\n             INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid)\n             WHERE e.extname='citus_columnar' and p.proname = 'columnar_handler'\n  ) THEN\n#include \"../../columnar/sql/columnar--9.5-1--10.0-1.sql\"\nEND IF;\nEND;\n$check_columnar$;\n\n#include \"udfs/time_partition_range/10.0-1.sql\"\n#include \"udfs/time_partitions/10.0-1.sql\"\n#include \"udfs/alter_old_partitions_set_access_method/10.0-1.sql\"\n\nALTER FUNCTION pg_catalog.master_conninfo_cache_invalidate()\nRENAME TO citus_conninfo_cache_invalidate;\nALTER FUNCTION pg_catalog.master_dist_local_group_cache_invalidate()\nRENAME TO citus_dist_local_group_cache_invalidate;\nALTER FUNCTION pg_catalog.master_dist_node_cache_invalidate()\nRENAME TO citus_dist_node_cache_invalidate;\nALTER FUNCTION pg_catalog.master_dist_object_cache_invalidate()\nRENAME TO citus_dist_object_cache_invalidate;\nALTER FUNCTION pg_catalog.master_dist_partition_cache_invalidate()\nRENAME TO citus_dist_partition_cache_invalidate;\nALTER FUNCTION pg_catalog.master_dist_placement_cache_invalidate()\nRENAME TO citus_dist_placement_cache_invalidate;\nALTER FUNCTION pg_catalog.master_dist_shard_cache_invalidate()\nRENAME TO citus_dist_shard_cache_invalidate;\n\n#include \"udfs/citus_conninfo_cache_invalidate/10.0-1.sql\"\n#include \"udfs/citus_dist_local_group_cache_invalidate/10.0-1.sql\"\n#include \"udfs/citus_dist_node_cache_invalidate/10.0-1.sql\"\n#include \"udfs/citus_dist_object_cache_invalidate/10.0-1.sql\"\n#include \"udfs/citus_dist_partition_cache_invalidate/10.0-1.sql\"\n#include \"udfs/citus_dist_placement_cache_invalidate/10.0-1.sql\"\n#include \"udfs/citus_dist_shard_cache_invalidate/10.0-1.sql\"\n\nALTER FUNCTION pg_catalog.master_drop_all_shards(regclass, text, text)\nRENAME TO citus_drop_all_shards;\n\nDROP FUNCTION pg_catalog.master_modify_multiple_shards(text);\nDROP FUNCTION pg_catalog.master_create_distributed_table(regclass, text, citus.distribution_type);\nDROP FUNCTION pg_catalog.master_create_worker_shards(text, integer, integer);\nDROP FUNCTION pg_catalog.mark_tables_colocated(regclass, regclass[]);\n#include \"udfs/citus_shard_sizes/10.0-1.sql\"\n#include \"udfs/citus_shards/10.0-1.sql\"\n\n#include \"udfs/fix_pre_citus10_partitioned_table_constraint_names/10.0-1.sql\"\n#include \"udfs/worker_fix_pre_citus10_partitioned_table_constraint_names/10.0-1.sql\"\nDROP FUNCTION pg_catalog.citus_dist_stat_activity CASCADE;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_dist_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT distributed_query_host_name text, OUT distributed_query_host_port int,\n                                                    OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                                    OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                                    OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                                    OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                                    OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$citus_dist_stat_activity$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_dist_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT distributed_query_host_name text, OUT distributed_query_host_port int,\n                                             OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                             OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                             OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                             OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                             OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nIS 'returns distributed transaction activity on distributed tables';\n\nCREATE VIEW citus.citus_dist_stat_activity AS\nSELECT * FROM pg_catalog.citus_dist_stat_activity();\nALTER VIEW citus.citus_dist_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_dist_stat_activity TO PUBLIC;\n\nSET search_path = 'pg_catalog';\n\nCREATE VIEW citus.citus_lock_waits AS\n\nWITH\ncitus_dist_stat_activity AS\n(\n  SELECT * FROM citus_dist_stat_activity\n),\nunique_global_wait_edges AS\n(\n\tSELECT DISTINCT ON(waiting_node_id, waiting_transaction_num, blocking_node_id, blocking_transaction_num) * FROM dump_global_wait_edges()\n),\ncitus_dist_stat_activity_with_node_id AS\n(\n  SELECT\n  citus_dist_stat_activity.*, (CASE citus_dist_stat_activity.distributed_query_host_name WHEN 'coordinator_host' THEN 0 ELSE pg_dist_node.nodeid END) as initiator_node_id\n  FROM\n  citus_dist_stat_activity LEFT JOIN pg_dist_node\n  ON\n  citus_dist_stat_activity.distributed_query_host_name = pg_dist_node.nodename AND\n  citus_dist_stat_activity.distributed_query_host_port = pg_dist_node.nodeport\n)\nSELECT\n waiting.pid AS waiting_pid,\n blocking.pid AS blocking_pid,\n waiting.query AS blocked_statement,\n blocking.query AS current_statement_in_blocking_process,\n waiting.initiator_node_id AS waiting_node_id,\n blocking.initiator_node_id AS blocking_node_id,\n waiting.distributed_query_host_name AS waiting_node_name,\n blocking.distributed_query_host_name AS blocking_node_name,\n waiting.distributed_query_host_port AS waiting_node_port,\n blocking.distributed_query_host_port AS blocking_node_port\nFROM\n unique_global_wait_edges\nJOIN\n citus_dist_stat_activity_with_node_id waiting ON (unique_global_wait_edges.waiting_transaction_num = waiting.transaction_number AND unique_global_wait_edges.waiting_node_id = waiting.initiator_node_id)\nJOIN\n citus_dist_stat_activity_with_node_id blocking ON (unique_global_wait_edges.blocking_transaction_num = blocking.transaction_number AND unique_global_wait_edges.blocking_node_id = blocking.initiator_node_id);\n\nALTER VIEW citus.citus_lock_waits SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_lock_waits TO PUBLIC;\n\nDROP FUNCTION citus_worker_stat_activity CASCADE;\n\nCREATE OR REPLACE FUNCTION citus_worker_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT distributed_query_host_name text, OUT distributed_query_host_port int,\n                                                      OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                                      OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                                      OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                                      OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                                      OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$citus_worker_stat_activity$$;\n\nCOMMENT ON FUNCTION citus_worker_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT distributed_query_host_name text, OUT distributed_query_host_port int,\n                                               OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                               OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                               OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                               OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                               OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nIS 'returns distributed transaction activity on shards of distributed tables';\n\nCREATE VIEW citus.citus_worker_stat_activity AS\nSELECT * FROM pg_catalog.citus_worker_stat_activity();\nALTER VIEW citus.citus_worker_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_worker_stat_activity TO PUBLIC;\n\n-- copy of citus--10.0-1--10.0-2\n\n--#include \"../../columnar/sql/columnar--10.0-1--10.0-2.sql\"\nDO $check_columnar$\nBEGIN\nIF NOT EXISTS (SELECT 1 FROM pg_catalog.pg_extension AS e\n             INNER JOIN pg_catalog.pg_depend AS d ON (d.refobjid = e.oid)\n             INNER JOIN pg_catalog.pg_proc AS p ON (p.oid = d.objid)\n             WHERE e.extname='citus_columnar' and p.proname = 'columnar_handler'\n  ) THEN\n#include \"../../columnar/sql/columnar--10.0-1--10.0-2.sql\"\nEND IF;\nEND;\n$check_columnar$;\n\n\n-- copy of citus--10.0-2--10.0-3\n\n#include \"udfs/citus_update_table_statistics/10.0-3.sql\"\n\nCREATE OR REPLACE FUNCTION master_update_table_statistics(relation regclass)\nRETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_update_table_statistics$$;\nCOMMENT ON FUNCTION pg_catalog.master_update_table_statistics(regclass)\n\tIS 'updates shard statistics of the given table';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_get_active_worker_nodes(OUT node_name text, OUT node_port bigint)\n    RETURNS SETOF record\n    LANGUAGE C STRICT ROWS 100\n    AS 'MODULE_PATHNAME', $$citus_get_active_worker_nodes$$;\nCOMMENT ON FUNCTION pg_catalog.citus_get_active_worker_nodes()\n    IS 'fetch set of active worker nodes';\n\n-- copy of citus--10.0-3--10.0-4\n\n-- This migration file aims to fix 2 issues with upgrades on clusters\n\n-- 1. a bug in public schema dependency for citus_tables view.\n--\n-- Users who do not have public schema in their clusters were unable to upgrade\n-- to Citus 10.x due to the citus_tables view that used to be created in public\n-- schema\n\n#include \"udfs/citus_tables/10.0-4.sql\"\n\n-- 2. a bug in our PG upgrade functions\n--\n-- Users who took the 9.5-2--10.0-1 upgrade path already have the fix, but users\n-- who took the 9.5-1--10.0-1 upgrade path do not. Hence, we repeat the CREATE OR\n-- REPLACE from the 9.5-2 definition for citus_prepare_pg_upgrade.\n\n#include \"udfs/citus_prepare_pg_upgrade/9.5-2.sql\"\n#include \"udfs/citus_finish_pg_upgrade/10.0-4.sql\"\n\n\nRESET search_path;\n\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.5-1--9.5-2.sql",
    "content": "-- 9.5-1--9.5-2 was added later as a patch to fix a bug in our PG upgrade functions\n#include \"udfs/citus_prepare_pg_upgrade/9.5-2.sql\"\n#include \"udfs/citus_finish_pg_upgrade/9.5-2.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.5-2--9.5-1.sql",
    "content": "--\n-- 9.5-1--9.5-2 was added later as a patch to fix a bug in our PG upgrade functions\n--\n-- This script brings users who installed the patch released back to the 9.5-1\n-- upgrade path. We do this via a semantical downgrade since there has already been\n-- introduced new changes in the schema from 9.5-1 to 10.0-1. To make sure we include all\n-- changes made during that version change we decide to use the existing upgrade path from\n-- our later introduced 9.5-1 version.\n--\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.5-2--9.5-3.sql",
    "content": "-- 9.5-2--9.5-3 was added later as a patch to improve master_update_table_statistics\nCREATE OR REPLACE FUNCTION master_update_table_statistics(relation regclass)\nRETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_update_table_statistics$$;\nCOMMENT ON FUNCTION pg_catalog.master_update_table_statistics(regclass)\n\tIS 'updates shard statistics of the given table';\n"
  },
  {
    "path": "src/backend/distributed/sql/citus--9.5-3--9.5-2.sql",
    "content": "-- citus--9.5-3--9.5-2\n-- This is a downgrade path that will revert the changes made in citus--9.5-2--9.5-3.sql\n-- 9.5-2--9.5-3 was added later as a patch to improve master_update_table_statistics.\n-- We have this downgrade script so that we can continue from the main upgrade path\n-- when upgrading to later versions.\nCREATE OR REPLACE FUNCTION master_update_table_statistics(relation regclass)\nRETURNS VOID AS $$\nDECLARE\n\tcolocated_tables regclass[];\nBEGIN\n\tSELECT get_colocated_table_array(relation) INTO colocated_tables;\n\n\tPERFORM\n\t\tmaster_update_shard_statistics(shardid)\n\tFROM\n\t\tpg_dist_shard\n\tWHERE\n\t\tlogicalrelid = ANY (colocated_tables);\nEND;\n$$ LANGUAGE 'plpgsql';\nCOMMENT ON FUNCTION master_update_table_statistics(regclass)\n\tIS 'updates shard statistics of the given table and its colocated tables';\n"
  },
  {
    "path": "src/backend/distributed/sql/datatypes/citus_cluster_clock/11.2-1.sql",
    "content": "--\n-- cluster_clock base type is a combination of\n-- uint64 cluster clock logical timestamp at the commit\n-- uint32 cluster clock counter(ticks with in the logical clock)\n--\n\nCREATE TYPE citus.cluster_clock;\n\nCREATE FUNCTION pg_catalog.cluster_clock_in(cstring)\n    RETURNS citus.cluster_clock\n    AS 'MODULE_PATHNAME',$$cluster_clock_in$$\n    LANGUAGE C IMMUTABLE STRICT;\n\nCREATE FUNCTION pg_catalog.cluster_clock_out(citus.cluster_clock)\n    RETURNS cstring\n    AS 'MODULE_PATHNAME',$$cluster_clock_out$$\n    LANGUAGE C IMMUTABLE STRICT;\n\nCREATE FUNCTION pg_catalog.cluster_clock_recv(internal)\n   RETURNS citus.cluster_clock\n   AS 'MODULE_PATHNAME',$$cluster_clock_recv$$\n   LANGUAGE C IMMUTABLE STRICT;\n\nCREATE FUNCTION pg_catalog.cluster_clock_send(citus.cluster_clock)\n   RETURNS bytea\n   AS 'MODULE_PATHNAME',$$cluster_clock_send$$\n   LANGUAGE C IMMUTABLE STRICT;\n\nCREATE FUNCTION pg_catalog.cluster_clock_logical(citus.cluster_clock)\n    RETURNS bigint\n    AS 'MODULE_PATHNAME',$$cluster_clock_logical$$\n    LANGUAGE C IMMUTABLE STRICT;\n\nCREATE TYPE citus.cluster_clock (\n    internallength = 12, -- specifies the size of the memory block required to hold the type uint64 + uint32\n    input = cluster_clock_in,\n    output = cluster_clock_out,\n    receive = cluster_clock_recv,\n    send = cluster_clock_send\n);\n\nALTER TYPE citus.cluster_clock SET SCHEMA pg_catalog;\nCOMMENT ON TYPE cluster_clock IS 'combination of (logical, counter): 42 bits + 22 bits';\n\n--\n-- Define the required operators\n--\nCREATE FUNCTION pg_catalog.cluster_clock_lt(cluster_clock, cluster_clock) RETURNS bool\n    AS 'MODULE_PATHNAME',$$cluster_clock_lt$$\n    LANGUAGE C IMMUTABLE STRICT;\n\nCREATE FUNCTION pg_catalog.cluster_clock_le(cluster_clock, cluster_clock) RETURNS bool\n    AS 'MODULE_PATHNAME',$$cluster_clock_le$$\n    LANGUAGE C IMMUTABLE STRICT;\n\nCREATE FUNCTION pg_catalog.cluster_clock_eq(cluster_clock, cluster_clock) RETURNS bool\n    AS 'MODULE_PATHNAME',$$cluster_clock_eq$$\n    LANGUAGE C IMMUTABLE STRICT;\n\nCREATE FUNCTION pg_catalog.cluster_clock_ne(cluster_clock, cluster_clock) RETURNS bool\n    AS 'MODULE_PATHNAME',$$cluster_clock_ne$$\n    LANGUAGE C IMMUTABLE STRICT;\n\nCREATE FUNCTION pg_catalog.cluster_clock_ge(cluster_clock, cluster_clock) RETURNS bool\n    AS 'MODULE_PATHNAME',$$cluster_clock_ge$$\n    LANGUAGE C IMMUTABLE STRICT;\n\nCREATE FUNCTION pg_catalog.cluster_clock_gt(cluster_clock, cluster_clock) RETURNS bool\n    AS 'MODULE_PATHNAME',$$cluster_clock_gt$$\n    LANGUAGE C IMMUTABLE STRICT;\n\nCREATE OPERATOR < (\n   leftarg = cluster_clock, rightarg = cluster_clock, procedure = cluster_clock_lt,\n   commutator = > , negator = >= ,\n   restrict = scalarltsel, join = scalarltjoinsel\n);\n\nCREATE OPERATOR <= (\n   leftarg = cluster_clock, rightarg = cluster_clock, procedure = cluster_clock_le,\n   commutator = >= , negator = > ,\n   restrict = scalarlesel, join = scalarlejoinsel\n);\n\nCREATE OPERATOR = (\n   leftarg = cluster_clock, rightarg = cluster_clock, procedure = cluster_clock_eq,\n   commutator = = ,\n   negator = <> ,\n   restrict = eqsel, join = eqjoinsel\n);\n\nCREATE OPERATOR <> (\n   leftarg = cluster_clock, rightarg = cluster_clock, procedure = cluster_clock_ne,\n   commutator = <> ,\n   negator = = ,\n   restrict = neqsel, join = neqjoinsel\n);\n\nCREATE OPERATOR >= (\n   leftarg = cluster_clock, rightarg = cluster_clock, procedure = cluster_clock_ge,\n   commutator = <= , negator = < ,\n   restrict = scalargesel, join = scalargejoinsel\n);\n\nCREATE OPERATOR > (\n   leftarg = cluster_clock, rightarg = cluster_clock, procedure = cluster_clock_gt,\n   commutator = < , negator = <= ,\n   restrict = scalargtsel, join = scalargtjoinsel\n);\n\n-- Create the support function too\nCREATE FUNCTION pg_catalog.cluster_clock_cmp(cluster_clock, cluster_clock) RETURNS int4\n    AS 'MODULE_PATHNAME',$$cluster_clock_cmp$$\n    LANGUAGE C IMMUTABLE STRICT;\n\n-- Define operator class to be be used by an index for type cluster_clock.\nCREATE OPERATOR CLASS pg_catalog.cluster_clock_ops\n    DEFAULT FOR TYPE cluster_clock USING btree AS\n        OPERATOR        1       < ,\n        OPERATOR        2       <= ,\n        OPERATOR        3       = ,\n        OPERATOR        4       >= ,\n        OPERATOR        5       > ,\n        FUNCTION        1       cluster_clock_cmp(cluster_clock, cluster_clock);\n\n--\n-- Create sequences for logical and counter fields of the type cluster_clock, to\n-- be used as a storage.\n--\nCREATE SEQUENCE citus.pg_dist_clock_logical_seq START 1;\n\nALTER SEQUENCE citus.pg_dist_clock_logical_seq SET SCHEMA pg_catalog;\n\nREVOKE UPDATE ON SEQUENCE pg_catalog.pg_dist_clock_logical_seq FROM public;\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--10.0-4--9.5-1.sql",
    "content": "-- citus--10.0-4--9.5-1\n\n-- This migration file aims to fix the issues with upgrades on clusters without public schema.\n\n-- This file is created by the following command, and some more changes in a separate commit\n-- cat citus--10.0-3--10.0-2.sql citus--10.0-2--10.0-1.sql citus--10.0-1--9.5-1.sql > citus--10.0-4--9.5-1.sql\n\n-- copy of citus--10.0-4--10.0-3\n--\n-- 10.0-3--10.0-4 was added later as a patch to fix a bug in our PG upgrade functions\n--\n-- The upgrade fixes a bug in citus_(prepare|finish)_pg_upgrade. Given the old versions of\n-- these functions contain a bug it is better to _not_ restore the old version and keep\n-- the patched version of the function.\n--\n-- This is inline with the downgrade scripts for earlier versions of this patch\n--\n\n-- copy of citus--10.0-3--10.0-2\n-- this is a downgrade path that will revert the changes made in citus--10.0-2--10.0-3.sql\n\nDROP FUNCTION pg_catalog.citus_update_table_statistics(regclass);\n\n#include \"../udfs/citus_update_table_statistics/10.0-1.sql\"\n\nCREATE OR REPLACE FUNCTION master_update_table_statistics(relation regclass)\nRETURNS VOID AS $$\nDECLARE\n\tcolocated_tables regclass[];\nBEGIN\n\tSELECT get_colocated_table_array(relation) INTO colocated_tables;\n\n\tPERFORM\n\t\tmaster_update_shard_statistics(shardid)\n\tFROM\n\t\tpg_dist_shard\n\tWHERE\n\t\tlogicalrelid = ANY (colocated_tables);\nEND;\n$$ LANGUAGE 'plpgsql';\nCOMMENT ON FUNCTION master_update_table_statistics(regclass)\n\tIS 'updates shard statistics of the given table and its colocated tables';\n\nDROP FUNCTION pg_catalog.citus_get_active_worker_nodes(OUT text, OUT bigint);\n-- copy of citus--10.0-2--10.0-1.sql\n#include \"../../../columnar/sql/downgrades/columnar--10.0-2--10.0-1.sql\"\n\n-- copy of citus--10.0-1--9.5-1\n\n-- In Citus 10.0, we added another internal udf (notify_constraint_dropped)\n-- to be called by citus_drop_trigger. Since this script is executed when\n-- downgrading Citus, we don't have notify_constraint_dropped in citus.so.\n-- For this reason, we first need to downgrade citus_drop_trigger so it doesn't\n-- call notify_constraint_dropped.\n-- To downgrade citus_drop_trigger, we first need to have the old version of\n-- citus_drop_all_shards as we renamed it in Citus 10.0.\nALTER FUNCTION pg_catalog.citus_drop_all_shards(regclass, text, text)\nRENAME TO master_drop_all_shards;\n#include \"../udfs/citus_drop_trigger/9.5-1.sql\"\n\n-- Now we can safely drop notify_constraint_dropped as we downgraded citus_drop_trigger.\nDROP FUNCTION pg_catalog.notify_constraint_dropped();\n\n#include \"../udfs/citus_finish_pg_upgrade/9.5-1.sql\"\n\n#include \"../../../columnar/sql/downgrades/columnar--10.0-1--9.5-1.sql\"\n\nDROP VIEW IF EXISTS pg_catalog.citus_tables;\nDROP VIEW IF EXISTS public.citus_tables;\nDROP FUNCTION pg_catalog.alter_distributed_table(regclass, text, int, text, boolean);\nDROP FUNCTION pg_catalog.alter_table_set_access_method(regclass, text);\nDROP FUNCTION pg_catalog.citus_total_relation_size(regclass,boolean);\nDROP FUNCTION pg_catalog.undistribute_table(regclass,boolean);\nDROP FUNCTION pg_catalog.citus_add_local_table_to_metadata(regclass,boolean);\nDROP FUNCTION pg_catalog.citus_add_node(text, integer, integer, noderole, name);\nDROP FUNCTION pg_catalog.citus_activate_node(text, integer);\nDROP FUNCTION pg_catalog.citus_add_inactive_node(text, integer, integer, noderole, name);\nDROP FUNCTION pg_catalog.citus_add_secondary_node(text, integer, text, integer, name);\nDROP FUNCTION pg_catalog.citus_disable_node(text, integer);\nDROP FUNCTION pg_catalog.citus_drain_node(text, integer, citus.shard_transfer_mode, name);\nDROP FUNCTION pg_catalog.citus_remove_node(text, integer);\nDROP FUNCTION pg_catalog.citus_set_node_property(text, integer, text, boolean);\nDROP FUNCTION pg_catalog.citus_unmark_object_distributed(oid, oid, int);\nDROP FUNCTION pg_catalog.citus_update_node(int, text, int, bool, int);\nDROP FUNCTION pg_catalog.citus_update_shard_statistics(bigint);\nDROP FUNCTION pg_catalog.citus_update_table_statistics(regclass);\nDROP FUNCTION pg_catalog.citus_copy_shard_placement(bigint, text, integer, text, integer, bool, citus.shard_transfer_mode);\nDROP FUNCTION pg_catalog.citus_move_shard_placement(bigint, text, integer, text, integer, citus.shard_transfer_mode);\n\nALTER FUNCTION pg_catalog.citus_conninfo_cache_invalidate()\nRENAME TO master_conninfo_cache_invalidate;\nALTER FUNCTION pg_catalog.citus_dist_local_group_cache_invalidate()\nRENAME TO master_dist_local_group_cache_invalidate;\nALTER FUNCTION pg_catalog.citus_dist_node_cache_invalidate()\nRENAME TO master_dist_node_cache_invalidate;\nALTER FUNCTION pg_catalog.citus_dist_object_cache_invalidate()\nRENAME TO master_dist_object_cache_invalidate;\nALTER FUNCTION pg_catalog.citus_dist_partition_cache_invalidate()\nRENAME TO master_dist_partition_cache_invalidate;\nALTER FUNCTION pg_catalog.citus_dist_placement_cache_invalidate()\nRENAME TO master_dist_placement_cache_invalidate;\nALTER FUNCTION pg_catalog.citus_dist_shard_cache_invalidate()\nRENAME TO master_dist_shard_cache_invalidate;\n\n#include \"../udfs/citus_conninfo_cache_invalidate/9.5-1.sql\"\n#include \"../udfs/citus_dist_local_group_cache_invalidate/9.5-1.sql\"\n#include \"../udfs/citus_dist_node_cache_invalidate/9.5-1.sql\"\n#include \"../udfs/citus_dist_object_cache_invalidate/9.5-1.sql\"\n#include \"../udfs/citus_dist_partition_cache_invalidate/9.5-1.sql\"\n#include \"../udfs/citus_dist_placement_cache_invalidate/9.5-1.sql\"\n#include \"../udfs/citus_dist_shard_cache_invalidate/9.5-1.sql\"\n\nDROP VIEW pg_catalog.time_partitions;\nDROP FUNCTION pg_catalog.time_partition_range(regclass);\nDROP PROCEDURE pg_catalog.alter_old_partitions_set_access_method(regclass,timestamptz,name);\n\nDROP FUNCTION pg_catalog.citus_set_coordinator_host(text,int,noderole,name);\nDROP FUNCTION pg_catalog.worker_change_sequence_dependency(regclass, regclass, regclass);\n\nCREATE FUNCTION pg_catalog.mark_tables_colocated(source_table_name regclass, target_table_names regclass[])\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$mark_tables_colocated$$;\nCOMMENT ON FUNCTION pg_catalog.mark_tables_colocated(source_table_name regclass, target_table_names regclass[])\n\tIS 'mark target distributed tables as colocated with the source table';\n\nCREATE FUNCTION pg_catalog.master_modify_multiple_shards(text)\n    RETURNS integer\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_modify_multiple_shards$$;\nCOMMENT ON FUNCTION master_modify_multiple_shards(text)\n    IS 'push delete and update queries to shards';\n\nCREATE FUNCTION pg_catalog.master_create_distributed_table(table_name regclass,\n                                                           distribution_column text,\n                                                           distribution_method citus.distribution_type)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_create_distributed_table$$;\nCOMMENT ON FUNCTION pg_catalog.master_create_distributed_table(table_name regclass,\n                                                               distribution_column text,\n                                                               distribution_method citus.distribution_type)\n    IS 'define the table distribution functions';\n\nCREATE FUNCTION pg_catalog.master_create_worker_shards(table_name text, shard_count integer,\n                                                       replication_factor integer DEFAULT 2)\n    RETURNS void\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C STRICT;\n\nDROP FUNCTION pg_catalog.remove_local_tables_from_metadata();\n\n#include \"../udfs/citus_total_relation_size/7.0-1.sql\"\n#include \"../udfs/upgrade_to_reference_table/8.0-1.sql\"\n#include \"../udfs/undistribute_table/9.5-1.sql\"\n#include \"../udfs/create_citus_local_table/9.5-1.sql\"\nDROP VIEW pg_catalog.citus_shards CASCADE;\nDROP FUNCTION pg_catalog.citus_shard_sizes(OUT table_name text, OUT size bigint);\n\nDROP FUNCTION pg_catalog.fix_pre_citus10_partitioned_table_constraint_names();\nDROP FUNCTION pg_catalog.fix_pre_citus10_partitioned_table_constraint_names(regclass);\nDROP FUNCTION pg_catalog.worker_fix_pre_citus10_partitioned_table_constraint_names(regclass,bigint,text);\nDROP FUNCTION pg_catalog.citus_dist_stat_activity CASCADE;\nDROP FUNCTION pg_catalog.citus_worker_stat_activity CASCADE;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_dist_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT master_query_host_name text, OUT master_query_host_port int,\n                                                    OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                                    OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                                    OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                                    OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                                    OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$citus_dist_stat_activity$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_dist_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT master_query_host_name text, OUT master_query_host_port int,\n                                             OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                             OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                             OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                             OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                             OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nIS 'returns distributed transaction activity on distributed tables';\n\nCREATE VIEW citus.citus_dist_stat_activity AS\nSELECT * FROM pg_catalog.citus_dist_stat_activity();\nALTER VIEW citus.citus_dist_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_dist_stat_activity TO PUBLIC;\n\nSET search_path = 'pg_catalog';\n\nCREATE VIEW citus.citus_lock_waits AS\n\nWITH\ncitus_dist_stat_activity AS\n(\n  SELECT * FROM citus_dist_stat_activity\n),\nunique_global_wait_edges AS\n(\n\tSELECT DISTINCT ON(waiting_node_id, waiting_transaction_num, blocking_node_id, blocking_transaction_num) * FROM dump_global_wait_edges()\n),\ncitus_dist_stat_activity_with_node_id AS\n(\n  SELECT\n  citus_dist_stat_activity.*, (CASE citus_dist_stat_activity.master_query_host_name WHEN 'coordinator_host' THEN 0 ELSE pg_dist_node.nodeid END) as initiator_node_id\n  FROM\n  citus_dist_stat_activity LEFT JOIN pg_dist_node\n  ON\n  citus_dist_stat_activity.master_query_host_name = pg_dist_node.nodename AND\n  citus_dist_stat_activity.master_query_host_port = pg_dist_node.nodeport\n)\nSELECT\n waiting.pid AS waiting_pid,\n blocking.pid AS blocking_pid,\n waiting.query AS blocked_statement,\n blocking.query AS current_statement_in_blocking_process,\n waiting.initiator_node_id AS waiting_node_id,\n blocking.initiator_node_id AS blocking_node_id,\n waiting.master_query_host_name AS waiting_node_name,\n blocking.master_query_host_name AS blocking_node_name,\n waiting.master_query_host_port AS waiting_node_port,\n blocking.master_query_host_port AS blocking_node_port\nFROM\n unique_global_wait_edges\nJOIN\n citus_dist_stat_activity_with_node_id waiting ON (unique_global_wait_edges.waiting_transaction_num = waiting.transaction_number AND unique_global_wait_edges.waiting_node_id = waiting.initiator_node_id)\nJOIN\n citus_dist_stat_activity_with_node_id blocking ON (unique_global_wait_edges.blocking_transaction_num = blocking.transaction_number AND unique_global_wait_edges.blocking_node_id = blocking.initiator_node_id);\n\nALTER VIEW citus.citus_lock_waits SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_lock_waits TO PUBLIC;\n\nCREATE OR REPLACE FUNCTION citus_worker_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT master_query_host_name text, OUT master_query_host_port int,\n                                                      OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                                      OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                                      OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                                      OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                                      OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$citus_worker_stat_activity$$;\n\nCOMMENT ON FUNCTION citus_worker_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT master_query_host_name text, OUT master_query_host_port int,\n                                               OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                               OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                               OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                               OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                               OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nIS 'returns distributed transaction activity on shards of distributed tables';\n\nCREATE VIEW citus.citus_worker_stat_activity AS\nSELECT * FROM pg_catalog.citus_worker_stat_activity();\nALTER VIEW citus.citus_worker_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_worker_stat_activity TO PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--10.1-1--10.0-4.sql",
    "content": "-- citus--10.1-1--10.0-4\n\n-- This migration file aims to fix the issues with upgrades on clusters without public schema.\n\n-- copy of citus--10.1-1--10.0-3\n\n-- remove databases as distributed objects to prevent unknown object types being managed\n-- on older versions.\nDELETE FROM citus.pg_dist_object\n      WHERE classid = 'pg_catalog.pg_database'::regclass::oid;\n\n#include \"../../../columnar/sql/downgrades/columnar--10.1-1--10.0-3.sql\"\n\nDROP FUNCTION pg_catalog.create_distributed_table(regclass, text, citus.distribution_type, text, int);\nCREATE FUNCTION create_distributed_table(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t distribution_column text,\n\t\t\t\t\t\t\t\t\t\t distribution_type citus.distribution_type DEFAULT 'hash',\n\t\t\t\t\t\t\t\t\t\t colocate_with text DEFAULT 'default')\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$create_distributed_table$$;\nCOMMENT ON FUNCTION create_distributed_table(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t\t distribution_column text,\n\t\t\t\t\t\t\t\t\t\t\t distribution_type citus.distribution_type,\n\t\t\t\t\t\t\t\t\t\t\t colocate_with text)\n    IS 'creates a distributed table';\n\nDROP FUNCTION pg_catalog.worker_partitioned_relation_total_size(regclass);\nDROP FUNCTION pg_catalog.worker_partitioned_relation_size(regclass);\nDROP FUNCTION pg_catalog.worker_partitioned_table_size(regclass);\nDROP FUNCTION pg_catalog.citus_local_disk_space_stats();\n\n#include \"../udfs/citus_prepare_pg_upgrade/9.5-1.sql\"\n#include \"../udfs/citus_finish_pg_upgrade/10.0-1.sql\"\n#include \"../udfs/get_rebalance_table_shards_plan/9.2-1.sql\"\n\n-- the migration for citus_add_rebalance_strategy from 9.2-1 was the first one,\n-- so it doesn't have a DROP. This is why we DROP manually here.\nDROP FUNCTION pg_catalog.citus_add_rebalance_strategy;\n#include \"../udfs/citus_add_rebalance_strategy/9.2-1.sql\"\n\nALTER TABLE pg_catalog.pg_dist_rebalance_strategy DROP COLUMN improvement_threshold;\n\n-- the migration for get_rebalance_progress from 9.0-1 was the first one,\n-- so it doesn't have a DROP. This is why we DROP manually here.\nDROP FUNCTION pg_catalog.get_rebalance_progress;\n#include \"../udfs/get_rebalance_progress/9.0-1.sql\"\n\nCREATE OR REPLACE VIEW pg_catalog.citus_shards AS\nWITH shard_sizes AS (SELECT * FROM pg_catalog.citus_shard_sizes())\nSELECT\n     pg_dist_shard.logicalrelid AS table_name,\n     pg_dist_shard.shardid,\n     shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) as shard_name,\n     CASE WHEN partkey IS NOT NULL THEN 'distributed' WHEN repmodel = 't' THEN 'reference' ELSE 'local' END AS citus_table_type,\n     colocationid AS colocation_id,\n     pg_dist_node.nodename,\n     pg_dist_node.nodeport,\n     (SELECT size FROM shard_sizes WHERE\n       shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) = table_name\n       OR\n       'public.' || shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) = table_name\n      LIMIT 1) as shard_size\nFROM\n   pg_dist_shard\nJOIN\n   pg_dist_placement\nON\n   pg_dist_shard.shardid = pg_dist_placement.shardid\nJOIN\n   pg_dist_node\nON\n   pg_dist_placement.groupid = pg_dist_node.groupid\nJOIN\n   pg_dist_partition\nON\n   pg_dist_partition.logicalrelid = pg_dist_shard.logicalrelid\nORDER BY\n   pg_dist_shard.logicalrelid::text, shardid\n;\n\n#include \"../udfs/citus_finish_pg_upgrade/10.0-1.sql\"\nCREATE FUNCTION citus_internal.pg_dist_rebalance_strategy_enterprise_check()\n  RETURNS TRIGGER\n  LANGUAGE C\n  AS 'MODULE_PATHNAME';\nCREATE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger\n  BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE ON pg_dist_rebalance_strategy\n  FOR EACH STATEMENT EXECUTE FUNCTION citus_internal.pg_dist_rebalance_strategy_enterprise_check();\n\nDROP PROCEDURE pg_catalog.citus_cleanup_orphaned_shards();\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--10.2-1--10.1-1.sql",
    "content": "-- citus--10.2-1--10.1-1\n\n#include \"../../../columnar/sql/downgrades/columnar--10.2-1--10.1-1.sql\"\n\nDROP FUNCTION pg_catalog.stop_metadata_sync_to_node(text, integer, bool);\n\nCREATE FUNCTION pg_catalog.stop_metadata_sync_to_node(nodename text, nodeport integer)\n\tRETURNS VOID\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$stop_metadata_sync_to_node$$;\nCOMMENT ON FUNCTION pg_catalog.stop_metadata_sync_to_node(nodename text, nodeport integer)\n    IS 'stop metadata sync to node';\n\nDROP FUNCTION pg_catalog.citus_internal_add_partition_metadata(regclass, \"char\", text, integer, \"char\");\nDROP FUNCTION pg_catalog.citus_internal_add_shard_metadata(regclass, bigint, \"char\", text, text);\nDROP FUNCTION pg_catalog.citus_internal_add_placement_metadata(bigint, integer, bigint, integer, bigint);\nDROP FUNCTION pg_catalog.citus_internal_update_placement_metadata(bigint, integer, integer);\nDROP FUNCTION pg_catalog.citus_internal_delete_shard_metadata(bigint);\nDROP FUNCTION pg_catalog.citus_internal_update_relation_colocation(oid, integer);\nDROP FUNCTION pg_catalog.create_time_partitions(regclass, interval, timestamp with time zone, timestamp with time zone);\nDROP FUNCTION pg_catalog.get_missing_time_partition_ranges(regclass, interval, timestamp with time zone, timestamp with time zone);\nDROP FUNCTION pg_catalog.worker_nextval(regclass);\n\nDROP PROCEDURE pg_catalog.drop_old_time_partitions(regclass, timestamptz);\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_record_sequence_dependency(regclass,regclass,name) FROM PUBLIC;\nALTER TABLE pg_catalog.pg_dist_placement DROP CONSTRAINT placement_shardid_groupid_unique_index;\n\nDROP FUNCTION pg_catalog.citus_drop_all_shards(regclass, text, text, boolean);\nCREATE FUNCTION pg_catalog.citus_drop_all_shards(logicalrelid regclass,\n                                                 schema_name text,\n                                                 table_name text)\n    RETURNS integer\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_drop_all_shards$$;\nCOMMENT ON FUNCTION pg_catalog.citus_drop_all_shards(regclass, text, text)\n    IS 'drop all shards in a relation and update metadata';\n#include \"../udfs/citus_drop_trigger/10.0-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--10.2-2--10.2-1.sql",
    "content": "-- citus--10.2-2--10.2-1\n\n#include \"../../../columnar/sql/downgrades/columnar--10.2-2--10.2-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--10.2-3--10.2-2.sql",
    "content": "-- citus--10.2-3--10.2-2\n\n#include \"../../../columnar/sql/downgrades/columnar--10.2-3--10.2-2.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--10.2-4--10.2-3.sql",
    "content": "-- citus--10.2-4--10.2-3\n\nDROP FUNCTION pg_catalog.fix_all_partition_shard_index_names();\nDROP FUNCTION pg_catalog.fix_partition_shard_index_names(regclass);\nDROP FUNCTION pg_catalog.worker_fix_partition_shard_index_names(regclass, text, text);\n\n#include \"../udfs/citus_finish_pg_upgrade/10.2-1.sql\"\n\n-- This needs to be done after downgrading citus_finish_pg_upgrade. This is\n-- because citus_finish_pg_upgrade/10.2-4 depends on columnar_ensure_am_depends_catalog,\n-- which is dropped by columnar--10.2-4--10.2-3.sql\n#include \"../../../columnar/sql/downgrades/columnar--10.2-4--10.2-3.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--11.0-1--10.2-4.sql",
    "content": "-- citus--11.0-1--10.2-4\nREVOKE SELECT ON pg_catalog.pg_dist_object FROM public;\nALTER TABLE pg_catalog.pg_dist_object SET SCHEMA citus;\n\nDROP FUNCTION pg_catalog.create_distributed_function(regprocedure, text, text, bool);\n\nDROP FUNCTION pg_catalog.worker_partition_query_result(text, text, int, citus.distribution_type, text[], text[], boolean, boolean, boolean);\n#include \"../udfs/worker_partition_query_result/9.2-1.sql\"\n\nCREATE FUNCTION pg_catalog.master_apply_delete_command(text)\n    RETURNS integer\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_apply_delete_command$$;\nCOMMENT ON FUNCTION pg_catalog.master_apply_delete_command(text)\n    IS 'drop shards matching delete criteria and update metadata';\n\nCREATE FUNCTION pg_catalog.master_get_table_metadata(\n                                          relation_name text,\n                                          OUT logical_relid oid,\n                                          OUT part_storage_type \"char\",\n                                          OUT part_method \"char\", OUT part_key text,\n                                          OUT part_replica_count integer,\n                                          OUT part_max_size bigint,\n                                          OUT part_placement_policy integer)\n    RETURNS record\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$master_get_table_metadata$$;\nCOMMENT ON FUNCTION master_get_table_metadata(relation_name text)\n    IS 'fetch metadata values for the table';\n\nALTER TABLE pg_catalog.pg_dist_partition DROP COLUMN autoconverted;\n\nCREATE FUNCTION master_append_table_to_shard(bigint, text, text, integer)\n    RETURNS real\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_append_table_to_shard$$;\nCOMMENT ON FUNCTION master_append_table_to_shard(bigint, text, text, integer)\n    IS 'append given table to all shard placements and update metadata';\n\nGRANT ALL ON FUNCTION start_metadata_sync_to_node(text, integer) TO PUBLIC;\nGRANT ALL ON FUNCTION stop_metadata_sync_to_node(text, integer,bool) TO PUBLIC;\n\nDROP FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer, force bool);\nCREATE FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer)\n        RETURNS void\n        LANGUAGE C STRICT\n        AS 'MODULE_PATHNAME', $$citus_disable_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer)\n        IS 'removes node from the cluster temporarily';\n\nDROP FUNCTION pg_catalog.citus_check_connection_to_node (text, integer);\nDROP FUNCTION pg_catalog.citus_check_cluster_node_health ();\n\nDROP FUNCTION pg_catalog.citus_internal_add_object_metadata(text, text[], text[], integer, integer, boolean);\nDROP FUNCTION pg_catalog.citus_internal_add_colocation_metadata(int, int, int, regtype, oid);\nDROP FUNCTION pg_catalog.citus_internal_delete_colocation_metadata(int);\nDROP FUNCTION pg_catalog.citus_run_local_command(text);\nDROP FUNCTION pg_catalog.worker_drop_sequence_dependency(text);\nDROP FUNCTION pg_catalog.worker_drop_shell_table(table_name text);\n\nCREATE OR REPLACE VIEW pg_catalog.citus_shards_on_worker AS\n\tSELECT n.nspname as \"Schema\",\n\t  c.relname as \"Name\",\n\t  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 'table' END as \"Type\",\n\t  pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\"\n\tFROM pg_catalog.pg_class c\n\t     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n\tWHERE c.relkind IN ('r','p','v','m','S','f','')\n\t      AND n.nspname <> 'pg_catalog'\n\t      AND n.nspname <> 'information_schema'\n\t      AND n.nspname !~ '^pg_toast'\n          AND pg_catalog.relation_is_a_known_shard(c.oid)\n\tORDER BY 1,2;\n\nCREATE OR REPLACE VIEW pg_catalog.citus_shard_indexes_on_worker AS\nSELECT 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 'table' END as \"Type\",\n  pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\",\n c2.relname as \"Table\"\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\nWHERE c.relkind IN ('i','')\n      AND n.nspname <> 'pg_catalog'\n      AND n.nspname <> 'information_schema'\n      AND n.nspname !~ '^pg_toast'\n      AND pg_catalog.relation_is_a_known_shard(c.oid)\nORDER BY 1,2;\n\nDROP FUNCTION pg_catalog.citus_shards_on_worker();\nDROP FUNCTION pg_catalog.citus_shard_indexes_on_worker();\n#include \"../udfs/create_distributed_function/9.0-1.sql\"\nALTER TABLE citus.pg_dist_object DROP COLUMN force_delegation;\n\nSET search_path = 'pg_catalog';\n\n\nDROP FUNCTION IF EXISTS get_all_active_transactions();\n\n\nCREATE OR REPLACE FUNCTION get_all_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4, OUT worker_query BOOL,\n                                                       OUT transaction_number int8, OUT transaction_stamp timestamptz)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$get_all_active_transactions$$;\n\nCOMMENT ON FUNCTION get_all_active_transactions(OUT datid oid, OUT datname text, OUT process_id int, OUT initiator_node_identifier int4, OUT worker_query BOOL,\n                                                OUT transaction_number int8, OUT transaction_stamp timestamptz)\nIS 'returns distributed transaction ids of active distributed transactions';\n\nDROP FUNCTION IF EXISTS get_global_active_transactions();\n\nCREATE FUNCTION get_global_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4, OUT worker_query BOOL, OUT transaction_number int8, OUT transaction_stamp timestamptz)\n  RETURNS SETOF RECORD\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$get_global_active_transactions$$;\n COMMENT ON FUNCTION get_global_active_transactions(OUT database_id oid, OUT process_id int, OUT initiator_node_identifier int4, OUT transaction_number int8, OUT transaction_stamp timestamptz)\n     IS 'returns distributed transaction ids of active distributed transactions from each node of the cluster';\n\nRESET search_path;\n\nDROP VIEW pg_catalog.citus_lock_waits;\n\nDROP FUNCTION citus_internal_local_blocked_processes;\nDROP FUNCTION citus_internal_global_blocked_processes;\n\nDROP VIEW IF EXISTS pg_catalog.citus_dist_stat_activity;\nDROP FUNCTION IF EXISTS pg_catalog.citus_dist_stat_activity;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_dist_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT distributed_query_host_name text, OUT distributed_query_host_port int,\n                                                    OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                                    OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                                    OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                                    OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                                    OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$citus_dist_stat_activity$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_dist_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT distributed_query_host_name text, OUT distributed_query_host_port int,\n                                             OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                             OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                             OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                             OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                             OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nIS 'returns distributed transaction activity on distributed tables';\n\nCREATE VIEW citus.citus_dist_stat_activity AS\nSELECT * FROM pg_catalog.citus_dist_stat_activity();\nALTER VIEW citus.citus_dist_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_dist_stat_activity TO PUBLIC;\n\nSET search_path = 'pg_catalog';\nDROP VIEW IF EXISTS citus_worker_stat_activity;\nDROP FUNCTION IF EXISTS citus_worker_stat_activity;\n\nCREATE OR REPLACE FUNCTION citus_worker_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT distributed_query_host_name text, OUT distributed_query_host_port int,\n                                                      OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                                      OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                                      OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                                      OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                                      OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$citus_worker_stat_activity$$;\n\nCOMMENT ON FUNCTION citus_worker_stat_activity(OUT query_hostname text, OUT query_hostport int, OUT distributed_query_host_name text, OUT distributed_query_host_port int,\n                                               OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT datid oid, OUT datname name,\n                                               OUT pid int, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr INET,\n                                               OUT client_hostname TEXT, OUT client_port int, OUT backend_start timestamptz, OUT xact_start timestamptz,\n                                               OUT query_start timestamptz, OUT state_change timestamptz, OUT wait_event_type text, OUT wait_event text,\n                                               OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query text, OUT backend_type text)\nIS 'returns distributed transaction activity on shards of distributed tables';\n\nDROP FUNCTION pg_catalog.worker_create_or_replace_object(text[]);\n#include \"../udfs/worker_create_or_replace_object/9.0-1.sql\"\n\nDROP FUNCTION IF EXISTS pg_catalog.pg_cancel_backend(bigint);\nDROP FUNCTION IF EXISTS pg_catalog.pg_terminate_backend(bigint, bigint);\n\nDROP FUNCTION pg_catalog.dump_local_wait_edges;\nCREATE FUNCTION pg_catalog.dump_local_wait_edges(\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$dump_local_wait_edges$$;\nCOMMENT ON FUNCTION pg_catalog.dump_local_wait_edges()\nIS 'returns all local lock wait chains, that start from distributed transactions';\n\nDROP FUNCTION pg_catalog.dump_global_wait_edges;\nCREATE FUNCTION pg_catalog.dump_global_wait_edges(\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE 'c' STRICT\nAS $$MODULE_PATHNAME$$, $$dump_global_wait_edges$$;\nCOMMENT ON FUNCTION pg_catalog.dump_global_wait_edges()\nIS 'returns a global list of blocked transactions originating from this node';\n\nDROP FUNCTION pg_catalog.citus_isolation_test_session_is_blocked(pBlockedPid integer, pInterestingPids integer[]);\nCREATE FUNCTION pg_catalog.citus_isolation_test_session_is_blocked(pBlockedPid integer, pInterestingPids integer[])\nRETURNS boolean AS $$\n  DECLARE\n    mBlockedTransactionNum int8;\n    workerProcessId integer := current_setting('citus.isolation_test_session_remote_process_id');\n    coordinatorProcessId integer := current_setting('citus.isolation_test_session_process_id');\n  BEGIN\n    IF pg_catalog.old_pg_isolation_test_session_is_blocked(pBlockedPid, pInterestingPids) THEN\n      RETURN true;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    -- Note that worker process may be blocked or waiting for a lock. So we need to\n    -- get transaction number for both of them. Following IF provides the transaction\n    -- number when the worker process waiting for other session.\n    IF EXISTS (SELECT transaction_number FROM get_global_active_transactions()\n               WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId) THEN\n      SELECT transaction_number INTO mBlockedTransactionNum FROM get_global_active_transactions()\n      WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId;\n    ELSE\n      -- Check whether transactions initiated from the coordinator get locked\n      SELECT transaction_number INTO mBlockedTransactionNum\n        FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n    END IF;\n\n    RETURN EXISTS (\n      SELECT 1 FROM dump_global_wait_edges()\n        WHERE waiting_transaction_num = mBlockedTransactionNum\n    ) OR EXISTS (\n      -- Check on the workers if any logical replication job spawned by the\n      -- current PID is blocked, by checking it's application name\n      -- Query is heavily based on: https://wiki.postgresql.org/wiki/Lock_Monitoring\n      SELECT result FROM run_command_on_workers($two$\n        SELECT blocked_activity.application_name AS blocked_application\n           FROM  pg_catalog.pg_locks         blocked_locks\n            JOIN pg_catalog.pg_stat_activity blocked_activity  ON blocked_activity.pid = blocked_locks.pid\n            JOIN pg_catalog.pg_locks         blocking_locks\n                ON blocking_locks.locktype = blocked_locks.locktype\n                AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE\n                AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n                AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n                AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n                AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n                AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n                AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n                AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n                AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n                AND blocking_locks.pid != blocked_locks.pid\n            JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n           WHERE NOT blocked_locks.GRANTED AND blocked_activity.application_name LIKE 'citus_shard_move_subscription_%'\n        $two$) where result='citus_shard_move_subscription_' || pBlockedPid);\n\n  END;\n$$ LANGUAGE plpgsql;\n\nREVOKE ALL ON FUNCTION citus_isolation_test_session_is_blocked(integer,integer[]) FROM PUBLIC;\n\nDROP FUNCTION pg_catalog.citus_blocking_pids(pBlockedPid integer);\nCREATE FUNCTION pg_catalog.citus_blocking_pids(pBlockedPid integer)\nRETURNS int4[] AS $$\n  DECLARE\n    mLocalBlockingPids int4[];\n    mRemoteBlockingPids int4[];\n    mLocalTransactionNum int8;\n  BEGIN\n    SELECT pg_catalog.old_pg_blocking_pids(pBlockedPid) INTO mLocalBlockingPids;\n\n    IF (array_length(mLocalBlockingPids, 1) > 0) THEN\n      RETURN mLocalBlockingPids;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    SELECT transaction_number INTO mLocalTransactionNum\n      FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n\n    SELECT array_agg(process_id) INTO mRemoteBlockingPids FROM (\n      WITH activeTransactions AS (\n        SELECT process_id, transaction_number FROM get_all_active_transactions()\n      ), blockingTransactions AS (\n        SELECT blocking_transaction_num AS txn_num FROM dump_global_wait_edges()\n        WHERE waiting_transaction_num = mLocalTransactionNum\n      )\n      SELECT activeTransactions.process_id FROM activeTransactions, blockingTransactions\n      WHERE activeTransactions.transaction_number = blockingTransactions.txn_num\n    ) AS sub;\n\n    RETURN mRemoteBlockingPids;\n  END;\n$$ LANGUAGE plpgsql;\nREVOKE ALL ON FUNCTION citus_blocking_pids(integer) FROM PUBLIC;\n\nCREATE VIEW citus.citus_worker_stat_activity AS\nSELECT * FROM pg_catalog.citus_worker_stat_activity();\nALTER VIEW citus.citus_worker_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_worker_stat_activity TO PUBLIC;\n\n-- we have to recreate this view because we drop citus_dist_stat_activity that this view depends\nCREATE VIEW citus.citus_lock_waits AS\n\nWITH\ncitus_dist_stat_activity AS\n(\n  SELECT * FROM citus_dist_stat_activity\n),\nunique_global_wait_edges AS\n(\n  SELECT DISTINCT ON(waiting_node_id, waiting_transaction_num, blocking_node_id, blocking_transaction_num) * FROM dump_global_wait_edges()\n),\ncitus_dist_stat_activity_with_node_id AS\n(\n  SELECT\n  citus_dist_stat_activity.*, (CASE citus_dist_stat_activity.distributed_query_host_name WHEN 'coordinator_host' THEN 0 ELSE pg_dist_node.nodeid END) as initiator_node_id\n  FROM\n  citus_dist_stat_activity LEFT JOIN pg_dist_node\n  ON\n  citus_dist_stat_activity.distributed_query_host_name = pg_dist_node.nodename AND\n  citus_dist_stat_activity.distributed_query_host_port = pg_dist_node.nodeport\n)\nSELECT\n waiting.pid AS waiting_pid,\n blocking.pid AS blocking_pid,\n waiting.query AS blocked_statement,\n blocking.query AS current_statement_in_blocking_process,\n waiting.initiator_node_id AS waiting_node_id,\n blocking.initiator_node_id AS blocking_node_id,\n waiting.distributed_query_host_name AS waiting_node_name,\n blocking.distributed_query_host_name AS blocking_node_name,\n waiting.distributed_query_host_port AS waiting_node_port,\n blocking.distributed_query_host_port AS blocking_node_port\nFROM\n unique_global_wait_edges\nJOIN\n citus_dist_stat_activity_with_node_id waiting ON (unique_global_wait_edges.waiting_transaction_num = waiting.transaction_number AND unique_global_wait_edges.waiting_node_id = waiting.initiator_node_id)\nJOIN\n citus_dist_stat_activity_with_node_id blocking ON (unique_global_wait_edges.blocking_transaction_num = blocking.transaction_number AND unique_global_wait_edges.blocking_node_id = blocking.initiator_node_id);\n\nALTER VIEW citus.citus_lock_waits SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_lock_waits TO PUBLIC;\n\nDROP FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(bool);\nDROP FUNCTION pg_catalog.citus_calculate_gpid(integer,integer);\nDROP FUNCTION pg_catalog.citus_backend_gpid();\nDROP FUNCTION get_nodeid_for_groupid(integer);\n\nRESET search_path;\n\nDROP VIEW pg_catalog.citus_stat_activity;\nDROP FUNCTION pg_catalog.citus_stat_activity;\nDROP FUNCTION pg_catalog.run_command_on_all_nodes;\n\nDROP FUNCTION pg_catalog.citus_nodename_for_nodeid(integer);\nDROP FUNCTION pg_catalog.citus_nodeport_for_nodeid(integer);\n\nDROP FUNCTION pg_catalog.citus_nodeid_for_gpid(bigint);\nDROP FUNCTION pg_catalog.citus_pid_for_gpid(bigint);\n\nDROP FUNCTION pg_catalog.citus_coordinator_nodeid();\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--11.0-2--11.0-1.sql",
    "content": "#include \"../udfs/citus_shards_on_worker/11.0-1.sql\"\n#include \"../udfs/citus_shard_indexes_on_worker/11.0-1.sql\"\n#include \"../udfs/citus_finalize_upgrade_to_citus11/11.0-1.sql\"\n\nDROP FUNCTION  pg_catalog.citus_disable_node(text, integer, bool);\nCREATE FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer, force bool default false)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_disable_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer, force bool)\n    IS 'removes node from the cluster temporarily';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_disable_node(text,int, bool) FROM PUBLIC;\n\nDROP FUNCTION pg_catalog.citus_is_coordinator();\nDROP FUNCTION pg_catalog.run_command_on_coordinator(text,boolean);\n\nDROP FUNCTION pg_catalog.start_metadata_sync_to_all_nodes();\nDROP PROCEDURE pg_catalog.citus_finish_citus_upgrade();\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--11.0-3--11.0-2.sql",
    "content": "#include \"../udfs/citus_finalize_upgrade_to_citus11/11.0-2.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--11.1-1--11.0-4.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_create_schema(jobid bigint, username text)\n RETURNS void\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $function$worker_create_schema$function$;\n\nCREATE FUNCTION pg_catalog.worker_cleanup_job_schema_cache()\n RETURNS void\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $function$worker_cleanup_job_schema_cache$function$;\n\nCREATE FUNCTION pg_catalog.worker_fetch_foreign_file(text, text, bigint, text[], integer[])\n RETURNS void\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $function$worker_fetch_foreign_file$function$;\n\nCREATE FUNCTION pg_catalog.worker_fetch_partition_file(bigint, integer, integer, integer, text, integer)\n RETURNS void\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $function$worker_fetch_partition_file$function$;\n\nCREATE FUNCTION pg_catalog.worker_hash_partition_table(bigint, integer, text, text, oid, anyarray)\n RETURNS void\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $function$worker_hash_partition_table$function$;\n\nCREATE FUNCTION pg_catalog.worker_merge_files_into_table(bigint, integer, text[], text[])\n RETURNS void\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $function$worker_merge_files_into_table$function$;\n\nCREATE FUNCTION pg_catalog.worker_range_partition_table(bigint, integer, text, text, oid, anyarray)\n RETURNS void\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $function$worker_range_partition_table$function$;\n\nCREATE FUNCTION pg_catalog.worker_repartition_cleanup(bigint)\n RETURNS void\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $function$worker_repartition_cleanup$function$;\n\n-- add relations to citus\nALTER EXTENSION citus ADD SCHEMA columnar;\nALTER EXTENSION citus ADD SEQUENCE columnar.storageid_seq;\nALTER EXTENSION citus ADD TABLE columnar.options;\nALTER EXTENSION citus ADD TABLE columnar.stripe;\nALTER EXTENSION citus ADD TABLE columnar.chunk_group;\nALTER EXTENSION citus ADD TABLE columnar.chunk;\n\nALTER EXTENSION citus ADD FUNCTION columnar.columnar_handler;\nALTER EXTENSION citus ADD ACCESS METHOD columnar;\nALTER EXTENSION citus ADD FUNCTION pg_catalog.alter_columnar_table_set;\nALTER EXTENSION citus ADD FUNCTION pg_catalog.alter_columnar_table_reset;\n\nALTER EXTENSION citus ADD FUNCTION citus_internal.upgrade_columnar_storage;\nALTER EXTENSION citus ADD FUNCTION citus_internal.downgrade_columnar_storage;\nALTER EXTENSION citus ADD FUNCTION citus_internal.columnar_ensure_am_depends_catalog;\n\nDROP FUNCTION pg_catalog.citus_split_shard_by_split_points(\n    shard_id bigint,\n    split_points text[],\n    node_ids integer[],\n    shard_transfer_mode citus.shard_transfer_mode);\nDROP FUNCTION pg_catalog.worker_split_copy(\n    source_shard_id bigint,\n    distribution_column text,\n    splitCopyInfos pg_catalog.split_copy_info[]);\nDROP TYPE pg_catalog.split_copy_info;\n\nDROP FUNCTION pg_catalog.worker_copy_table_to_node(\n    source_table regclass,\n    target_node_id integer);\n\nDROP FUNCTION pg_catalog.worker_split_shard_replication_setup(\n    splitShardInfo pg_catalog.split_shard_info[]);\nDROP TYPE pg_catalog.split_shard_info;\nDROP TYPE pg_catalog.replication_slot_info;\n\nDROP FUNCTION pg_catalog.worker_split_shard_release_dsm();\n\nDROP FUNCTION pg_catalog.get_all_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4,\n                                                     OUT worker_query BOOL, OUT transaction_number int8, OUT transaction_stamp timestamptz,\n                                                     OUT global_pid int8);\n#include \"../udfs/get_all_active_transactions/11.0-1.sql\"\n\nDROP VIEW pg_catalog.citus_locks;\nDROP FUNCTION pg_catalog.citus_locks();\n\n#include \"../udfs/citus_tables/10.0-4.sql\"\n#include \"../udfs/citus_shards/10.1-1.sql\"\n\nDROP FUNCTION pg_catalog.replicate_reference_tables(citus.shard_transfer_mode);\n#include \"../udfs/replicate_reference_tables/9.3-2.sql\"\n\nDROP FUNCTION pg_catalog.isolate_tenant_to_new_shard(table_name regclass, tenant_id \"any\", cascade_option text, shard_transfer_mode citus.shard_transfer_mode);\n#include \"../udfs/isolate_tenant_to_new_shard/8.0-1.sql\"\nDROP FUNCTION pg_catalog.create_distributed_table_concurrently;\nDROP FUNCTION pg_catalog.citus_internal_delete_partition_metadata(regclass);\n\n-- Check if user has any cleanup records.\n-- If not, DROP pg_dist_cleanup and continue safely.\n-- Otherwise, raise an exception to stop the downgrade process.\nDO $$\nDECLARE\n    cleanup_record_count INTEGER;\nBEGIN\n    SELECT COUNT(*) INTO cleanup_record_count FROM pg_dist_cleanup;\n\n    IF cleanup_record_count = 0 THEN\n        -- no cleanup records exist, can safely downgrade\n        DROP TABLE pg_catalog.pg_dist_cleanup;\n    ELSE\n        RAISE EXCEPTION 'pg_dist_cleanup is introduced in Citus 11.1'\n        USING HINT = 'To downgrade Citus to an older version, you should '\n                     'first cleanup all the orphaned resources and make sure '\n                     'pg_dist_cleanup is empty, by executing '\n                     'CALL citus_cleanup_orphaned_resources();';\n    END IF;\nEND;\n$$ LANGUAGE plpgsql;\n\nDROP SEQUENCE pg_catalog.pg_dist_operationid_seq;\nDROP SEQUENCE pg_catalog.pg_dist_cleanup_recordid_seq;\nDROP PROCEDURE pg_catalog.citus_cleanup_orphaned_resources();\n\nDROP FUNCTION pg_catalog.citus_rebalance_start(name, bool, citus.shard_transfer_mode);\nDROP FUNCTION pg_catalog.citus_rebalance_stop();\nDROP FUNCTION pg_catalog.citus_rebalance_wait();\nDROP FUNCTION pg_catalog.citus_job_cancel(bigint);\nDROP FUNCTION pg_catalog.citus_job_wait(bigint, pg_catalog.citus_job_status);\nDROP TABLE pg_catalog.pg_dist_background_task_depend;\nDROP TABLE pg_catalog.pg_dist_background_task;\nDROP TYPE pg_catalog.citus_task_status;\nDROP TABLE pg_catalog.pg_dist_background_job;\nDROP TYPE pg_catalog.citus_job_status;\nDROP FUNCTION pg_catalog.citus_copy_shard_placement;\n#include \"../udfs/citus_copy_shard_placement/10.0-1.sql\"\n#include \"../udfs/get_rebalance_progress/10.1-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--11.2-1--11.1-1.sql",
    "content": "-- citus--11.2-1--11.1-1\n#include \"../udfs/get_rebalance_progress/11.1-1.sql\"\n#include \"../udfs/citus_isolation_test_session_is_blocked/11.1-1.sql\"\nDROP FUNCTION pg_catalog.citus_get_node_clock();\nDROP FUNCTION pg_catalog.citus_get_transaction_clock();\nDROP FUNCTION pg_catalog.citus_internal_adjust_local_clock_to_remote(cluster_clock);\nDROP FUNCTION pg_catalog.citus_is_clock_after(cluster_clock, cluster_clock);\nDROP FUNCTION pg_catalog.citus_job_list();\nDROP FUNCTION pg_catalog.citus_job_status(bigint,boolean);\nDROP FUNCTION pg_catalog.citus_rebalance_status(boolean);\nDROP FUNCTION pg_catalog.cluster_clock_logical(cluster_clock);\nDROP SEQUENCE pg_catalog.pg_dist_clock_logical_seq;\nDROP OPERATOR CLASS pg_catalog.cluster_clock_ops USING btree CASCADE;\nDROP OPERATOR FAMILY pg_catalog.cluster_clock_ops USING btree CASCADE;\nDROP TYPE pg_catalog.cluster_clock CASCADE;\nDROP FUNCTION pg_catalog.worker_split_shard_replication_setup(pg_catalog.split_shard_info[], bigint);\nDROP TYPE pg_catalog.replication_slot_info;\nDROP TYPE pg_catalog.split_shard_info;\n\nCREATE FUNCTION pg_catalog.worker_append_table_to_shard(text, text, text, integer)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_append_table_to_shard$$;\nCOMMENT ON FUNCTION pg_catalog.worker_append_table_to_shard(text, text, text, integer)\n    IS 'append a regular table''s contents to the shard';\n\n#include \"../udfs/worker_split_shard_replication_setup/11.1-1.sql\"\nDROP FUNCTION pg_catalog.citus_task_wait(bigint, pg_catalog.citus_task_status);\n#include \"../udfs/citus_prepare_pg_upgrade/11.1-1.sql\"\n#include \"../udfs/citus_finish_pg_upgrade/11.1-1.sql\"\n\nDROP FUNCTION pg_catalog.citus_copy_shard_placement(bigint, integer, integer, citus.shard_transfer_mode);\nDROP FUNCTION pg_catalog.citus_move_shard_placement(bigint, integer, integer, citus.shard_transfer_mode);\nDROP FUNCTION pg_catalog.citus_internal_add_placement_metadata(bigint, bigint, integer, bigint);\n#include \"../udfs/citus_internal_add_placement_metadata/10.2-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--11.2-2--11.2-1.sql",
    "content": "-- citus--11.2-2--11.2-1\nDROP FUNCTION IF EXISTS pg_catalog.worker_adjust_identity_column_seq_ranges(regclass);\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--11.3-1--11.2-2.sql",
    "content": "-- citus--11.3-1--11.2-1\n\nDROP FUNCTION pg_catalog.citus_internal_start_replication_origin_tracking();\nDROP FUNCTION pg_catalog.citus_internal_stop_replication_origin_tracking();\nDROP FUNCTION pg_catalog.citus_internal_is_replication_origin_tracking_active();\nALTER TABLE pg_catalog.pg_dist_authinfo REPLICA IDENTITY NOTHING;\nALTER TABLE pg_catalog.pg_dist_partition REPLICA IDENTITY NOTHING;\nALTER TABLE pg_catalog.pg_dist_placement REPLICA IDENTITY NOTHING;\nALTER TABLE pg_catalog.pg_dist_rebalance_strategy REPLICA IDENTITY NOTHING;\nALTER TABLE pg_catalog.pg_dist_shard REPLICA IDENTITY NOTHING;\nALTER TABLE pg_catalog.pg_dist_transaction REPLICA IDENTITY NOTHING;\n\nALTER TABLE pg_catalog.pg_dist_authinfo REPLICA IDENTITY NOTHING;\nALTER TABLE pg_catalog.pg_dist_partition REPLICA IDENTITY NOTHING;\nALTER TABLE pg_catalog.pg_dist_placement REPLICA IDENTITY NOTHING;\nALTER TABLE pg_catalog.pg_dist_rebalance_strategy REPLICA IDENTITY NOTHING;\nALTER TABLE pg_catalog.pg_dist_shard REPLICA IDENTITY NOTHING;\nALTER TABLE pg_catalog.pg_dist_transaction REPLICA IDENTITY NOTHING;\n\nDROP PROCEDURE pg_catalog.worker_drop_all_shell_tables(bool);\nDROP FUNCTION pg_catalog.citus_internal_mark_node_not_synced(int, int);\n\nDROP VIEW pg_catalog.citus_stat_tenants_local;\nDROP FUNCTION pg_catalog.citus_stat_tenants_local(boolean);\n\nDROP VIEW pg_catalog.citus_stat_tenants;\nDROP FUNCTION pg_catalog.citus_stat_tenants(boolean);\n\nDROP FUNCTION pg_catalog.citus_stat_tenants_local_reset();\nDROP FUNCTION pg_catalog.citus_stat_tenants_reset();\n\nALTER TABLE pg_catalog.pg_dist_background_task DROP COLUMN nodes_involved;\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--11.3-2--11.3-1.sql",
    "content": "DROP VIEW IF EXISTS public.citus_tables;\nDROP VIEW IF EXISTS pg_catalog.citus_tables;\n\nDROP VIEW pg_catalog.citus_shards;\nDROP FUNCTION pg_catalog.citus_shard_sizes;\n#include \"../udfs/citus_shard_sizes/10.0-1.sql\"\n-- citus_shards/11.1-1.sql tries to create citus_shards in pg_catalog but it is not allowed.\n-- Here we use citus_shards/10.0-1.sql to properly create the view in citus schema and\n-- then alter it to pg_catalog, so citus_shards/11.1-1.sql can REPLACE it without any errors.\n#include \"../udfs/citus_shards/10.0-1.sql\"\n\n#include \"../udfs/citus_tables/11.1-1.sql\"\n#include \"../udfs/citus_shards/11.1-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-2.sql",
    "content": "-- citus--12.0-1--11.3-1\n\nDO $$\nBEGIN\n    -- Throw an error if user has created any tenant schemas.\n    IF EXISTS (SELECT 1 FROM pg_catalog.pg_dist_schema)\n    THEN\n        RAISE EXCEPTION 'cannot downgrade Citus because there are '\n                        'tenant schemas created.'\n        USING HINT = 'To downgrade Citus to an older version, you should '\n                     'first issue SELECT citus.schema_tenant_unset(\"%s\") '\n                     'for each tenant schema.';\n    END IF;\n\n    -- Throw an error if user has any distributed tables without a shard key.\n    IF EXISTS (\n        SELECT 1 FROM pg_dist_partition\n        WHERE repmodel != 't' AND partmethod = 'n' AND colocationid != 0)\n    THEN\n        RAISE EXCEPTION 'cannot downgrade Citus because there are '\n                        'distributed tables without a shard key.'\n        USING HINT = 'You can find the distributed tables without a shard '\n                     'key in the cluster by using the following query: '\n                     '\"SELECT * FROM citus_tables WHERE distribution_column '\n                     '= ''<none>'' AND colocation_id > 0\".',\n        DETAIL = 'To downgrade Citus to an older version, you should '\n                 'first convert those tables to Postgres tables by '\n                 'executing SELECT undistribute_table(\"%s\").';\n    END IF;\nEND;\n$$ LANGUAGE plpgsql;\n\nDROP FUNCTION pg_catalog.citus_schema_distribute(regnamespace);\nDROP FUNCTION pg_catalog.citus_schema_undistribute(regnamespace);\n\nDROP FUNCTION pg_catalog.citus_internal_add_tenant_schema(Oid, int);\n\n#include \"../udfs/citus_prepare_pg_upgrade/11.2-1.sql\"\n#include \"../udfs/citus_finish_pg_upgrade/11.2-1.sql\"\n\nDROP FUNCTION pg_catalog.citus_internal_delete_tenant_schema(Oid);\nDROP FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(Oid, text);\n\n#include \"../udfs/citus_drop_trigger/10.2-1.sql\"\n\n-- citus_schemas might be created in either of the schemas\nDROP VIEW IF EXISTS public.citus_schemas;\nDROP VIEW IF EXISTS pg_catalog.citus_schemas;\n\nDROP VIEW IF EXISTS public.citus_tables;\nDROP VIEW IF EXISTS pg_catalog.citus_tables;\n\nDROP VIEW pg_catalog.citus_shards;\n\n#include \"../udfs/citus_tables/11.3-2.sql\"\n#include \"../udfs/citus_shards/11.3-2.sql\"\n\nDROP TABLE pg_catalog.pg_dist_schema;\n\nDROP VIEW pg_catalog.citus_stat_tenants_local;\nDROP FUNCTION pg_catalog.citus_stat_tenants_local_internal(\n    BOOLEAN,\n    OUT INT,\n    OUT TEXT,\n    OUT INT,\n    OUT INT,\n    OUT INT,\n    OUT INT,\n    OUT DOUBLE PRECISION,\n    OUT DOUBLE PRECISION,\n    OUT BIGINT);\n#include \"../udfs/citus_stat_tenants_local/11.3-1.sql\"\n\n#include \"../udfs/drop_old_time_partitions/10.2-1.sql\"\n#include \"../udfs/get_missing_time_partition_ranges/10.2-1.sql\"\n\n-- This explicitly does not reset the rebalance strategy to by_shard_count,\n-- because there's no way of knowing if the rebalance strategy before the\n-- upgrade was by_disk_size or by_shard_count. And even in previous versions\n-- by_disk_size is considered superior for quite some time.\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--12.1-1--12.0-1.sql",
    "content": "-- citus--12.1-1--12.0-1\nDROP FUNCTION pg_catalog.citus_pause_node_within_txn(int,bool,int);\n-- we have modified the relevant upgrade script to include any_value changes\n-- we don't need to upgrade this downgrade path for any_value changes\n-- since if we are doing a Citus downgrade, not PG downgrade, then it would be no-op.\n\nDROP FUNCTION pg_catalog.citus_internal_update_none_dist_table_metadata(\n    relation_id oid, replication_model \"char\", colocation_id bigint,\n    auto_converted boolean\n);\n\nDROP FUNCTION pg_catalog.citus_internal_delete_placement_metadata(\n    placement_id bigint\n);\n\nDROP FUNCTION pg_catalog.citus_schema_move(\n    schema_id regnamespace, target_node_name text, target_node_port integer,\n    shard_transfer_mode citus.shard_transfer_mode\n);\n\nDROP FUNCTION pg_catalog.citus_schema_move(\n    schema_id regnamespace, target_node_id integer,\n    shard_transfer_mode citus.shard_transfer_mode\n);\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--13.0-1--12.1-1.sql",
    "content": "-- citus--13.0-1--12.1-1\n-- this is an empty downgrade path since citus--12.1-1--13.0-1.sql is empty\n\n#include \"../udfs/create_time_partitions/10.2-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--13.1-1--13.0-1.sql",
    "content": "-- citus--13.1-1--13.0-1\n\nDROP FUNCTION citus_internal.database_command(text);\nDROP FUNCTION citus_internal.acquire_citus_advisory_object_class_lock(int, cstring);\n\n#include \"../udfs/citus_add_rebalance_strategy/10.1-1.sql\"\n\nDROP FUNCTION pg_catalog.citus_unmark_object_distributed(oid,oid,int,boolean);\n#include \"../udfs/citus_unmark_object_distributed/10.0-1.sql\"\n\nALTER TABLE pg_catalog.pg_dist_transaction DROP COLUMN outer_xid;\nREVOKE USAGE ON SCHEMA citus_internal FROM PUBLIC;\n\nDROP FUNCTION pg_catalog.citus_is_primary_node();\nDROP FUNCTION citus_internal.add_colocation_metadata(int, int, int, regtype, oid);\nDROP FUNCTION citus_internal.add_object_metadata(text, text[], text[], integer, integer, boolean);\nDROP FUNCTION citus_internal.add_partition_metadata(regclass, \"char\", text, integer, \"char\");\nDROP FUNCTION citus_internal.add_placement_metadata(bigint, bigint, integer, bigint);\nDROP FUNCTION citus_internal.add_shard_metadata(regclass, bigint, \"char\", text, text);\nDROP FUNCTION citus_internal.add_tenant_schema(oid, integer);\nDROP FUNCTION citus_internal.adjust_local_clock_to_remote(pg_catalog.cluster_clock);\nDROP FUNCTION citus_internal.delete_colocation_metadata(int);\nDROP FUNCTION citus_internal.delete_partition_metadata(regclass);\nDROP FUNCTION citus_internal.delete_placement_metadata(bigint);\nDROP FUNCTION citus_internal.delete_shard_metadata(bigint);\nDROP FUNCTION citus_internal.delete_tenant_schema(oid);\nDROP FUNCTION citus_internal.local_blocked_processes();\n#include \"../udfs/citus_blocking_pids/11.0-1.sql\"\n#include \"../udfs/citus_isolation_test_session_is_blocked/11.1-1.sql\"\nDROP VIEW IF EXISTS pg_catalog.citus_lock_waits;\n#include \"../udfs/citus_lock_waits/11.0-1.sql\"\nDROP FUNCTION citus_internal.global_blocked_processes();\n\nDROP FUNCTION citus_internal.mark_node_not_synced(int, int);\nDROP FUNCTION citus_internal.unregister_tenant_schema_globally(oid, text);\n#include \"../udfs/citus_drop_trigger/12.0-1.sql\"\nDROP FUNCTION citus_internal.update_none_dist_table_metadata(oid, \"char\", bigint, boolean);\nDROP FUNCTION citus_internal.update_placement_metadata(bigint, integer, integer);\nDROP FUNCTION citus_internal.update_relation_colocation(oid, int);\nDROP FUNCTION citus_internal.start_replication_origin_tracking();\nDROP FUNCTION citus_internal.stop_replication_origin_tracking();\nDROP FUNCTION citus_internal.is_replication_origin_tracking_active();\n#include \"../udfs/citus_finish_pg_upgrade/12.1-1.sql\"\n\nDROP VIEW pg_catalog.citus_stat_counters;\nDROP FUNCTION pg_catalog.citus_stat_counters(oid);\nDROP FUNCTION pg_catalog.citus_stat_counters_reset(oid);\nDROP VIEW IF EXISTS pg_catalog.citus_nodes;\n\n-- Definition of shard_name() prior to this release doesn't have a separate SQL file\n-- because it's quite an old UDF that its prior definition(s) was(were) squashed into\n-- citus--8.0-1.sql. For this reason, to downgrade it, here we directly execute its old\n-- definition instead of including it from such a separate file.\n--\n-- And before dropping and creating the function, we also need to drop citus_shards view\n-- since it depends on it. And immediately after creating the function, we recreate\n-- citus_shards view again.\n\nDROP VIEW pg_catalog.citus_shards;\n\nDROP FUNCTION pg_catalog.shard_name(object_name regclass, shard_id bigint, skip_qualify_public boolean);\nCREATE FUNCTION pg_catalog.shard_name(object_name regclass, shard_id bigint)\n    RETURNS text\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$shard_name$$;\nCOMMENT ON FUNCTION pg_catalog.shard_name(object_name regclass, shard_id bigint)\n    IS 'returns schema-qualified, shard-extended identifier of object name';\n\n#include \"../udfs/citus_shards/12.0-1.sql\"\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--13.2-1--13.1-1.sql",
    "content": "-- citus--13.2-1--13.1-1\n-- downgrade version to 13.1-1\nDROP FUNCTION IF EXISTS citus_internal.citus_internal_copy_single_shard_placement(bigint, integer, integer, integer, citus.shard_transfer_mode);\n\nDROP FUNCTION IF EXISTS pg_catalog.citus_rebalance_start(name, boolean, citus.shard_transfer_mode, boolean, boolean);\n#include \"../udfs/citus_rebalance_start/11.1-1.sql\"\n\nDROP FUNCTION IF EXISTS pg_catalog.worker_last_saved_explain_analyze();\n#include \"../udfs/worker_last_saved_explain_analyze/9.4-1.sql\"\n\nDROP FUNCTION IF EXISTS pg_catalog.citus_add_clone_node(text, integer, text, integer);\nDROP FUNCTION IF EXISTS pg_catalog.citus_add_clone_node_with_nodeid(text, integer, integer);\n\nDROP FUNCTION IF EXISTS pg_catalog.citus_remove_clone_node(text, integer);\nDROP FUNCTION IF EXISTS pg_catalog.citus_remove_clone_node_with_nodeid(integer);\n\nDROP FUNCTION IF EXISTS pg_catalog.citus_promote_clone_and_rebalance(integer, name, integer);\nDROP FUNCTION IF EXISTS pg_catalog.get_snapshot_based_node_split_plan(text, integer, text, integer, name);\n\n#include \"../cat_upgrades/remove_clone_info_to_pg_dist_node.sql\"\n#include \"../udfs/citus_finish_pg_upgrade/13.1-1.sql\"\n\n-- Note that we intentionally don't add the old columnar objects back to the \"citus\"\n-- extension in this downgrade script, even if they were present in the older version.\n--\n-- If the user wants to create \"citus_columnar\" extension later, \"citus_columnar\"\n-- will anyway properly create them at the scope of that extension.\n\nDROP VIEW IF EXISTS pg_catalog.citus_stats;\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--14.0-1--13.2-1.sql",
    "content": "-- citus--14.0-1--13.2-1\n-- downgrade version to 13.2-1\n\n#include \"../udfs/citus_prepare_pg_upgrade/13.0-1.sql\"\n#include \"../udfs/citus_finish_pg_upgrade/13.2-1.sql\"\n\nDROP AGGREGATE IF EXISTS pg_catalog.worker_binary_partial_agg(oid, anyelement);\nDROP AGGREGATE IF EXISTS pg_catalog.coord_binary_combine_agg(oid, bytea, anyelement);\nDROP FUNCTION IF EXISTS pg_catalog.worker_binary_partial_agg_ffunc(internal);\nDROP FUNCTION IF EXISTS pg_catalog.coord_binary_combine_agg_sfunc(internal, oid, bytea, anyelement);\nDROP FUNCTION IF EXISTS pg_catalog.coord_binary_combine_agg_ffunc(internal, oid, bytea, anyelement);\n\n#include \"../udfs/citus_finish_citus_upgrade/11.0-2.sql\"\nDROP FUNCTION IF EXISTS pg_catalog.fix_pre_citus14_colocation_group_collation_mismatches();\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--15.0-1--14.0-1.sql",
    "content": "-- citus--15.0-1--14.0-1\n-- downgrade version to 14.0-1\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--9.2-4--9.2-2.sql",
    "content": "-- citus--9.2-4--9.2-2\n-- this is an empty downgrade path since citus--9.2-2--9.2-4.sql is empty\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--9.3-2--9.2-4.sql",
    "content": "-- citus--9.3-2--9.2-4\n-- this is a downgrade path that will revert the changes made in citus--9.2-4--9.3-2.sql\n--\n-- 9.3-2 added citus extension owner as a distributed object, if not already in there.\n-- However we can not really know if it was a distributed owner prior to 9.3-2.\n-- That's why we leave the record in place.\n\n-- Revert the return type to void\nDROP FUNCTION pg_catalog.citus_extradata_container(INTERNAL);\nCREATE FUNCTION pg_catalog.citus_extradata_container(INTERNAL)\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_extradata_container$$;\nCOMMENT ON FUNCTION pg_catalog.citus_extradata_container(INTERNAL)\n    IS 'placeholder function to store additional data in postgres node trees';\n\n-- Remove newly introduced functions that are absent in earlier versions\nDROP FUNCTION pg_catalog.update_distributed_table_colocation(regclass, text);\nDROP FUNCTION pg_catalog.replicate_reference_tables();\nDROP FUNCTION pg_catalog.citus_remote_connection_stats(\n    OUT hostname text,\n    OUT port int,\n    OUT database_name text,\n    OUT connection_count_to_node int);\nDROP FUNCTION pg_catalog.worker_create_or_alter_role(\n    role_name text,\n    create_role_utility_query text,\n    alter_role_utility_query text);\nDROP FUNCTION pg_catalog.truncate_local_data_after_distributing_table(\n    function_name regclass);\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--9.4-1--9.3-2.sql",
    "content": "-- citus--9.4-1--9.3-2\n-- this is a downgrade path that will revert the changes made in citus--9.3-2--9.4-1.sql\n\nDROP FUNCTION pg_catalog.worker_last_saved_explain_analyze();\nDROP FUNCTION pg_catalog.worker_save_query_explain_analyze(query text,\n                                                           options jsonb);\n"
  },
  {
    "path": "src/backend/distributed/sql/downgrades/citus--9.5-1--9.4-1.sql",
    "content": "-- citus--9.5-1--9.4-1\n\nSET search_path = 'pg_catalog';\n\n#include \"../udfs/citus_drop_trigger/9.0-1.sql\"\n\n-- Check if user has any citus local tables.\n-- If not, DROP create_citus_local_table UDF and continue safely.\n-- Otherwise, raise an exception to stop the downgrade process.\nDO $$\nDECLARE\n    citus_local_table_count INTEGER;\nBEGIN\n    SELECT COUNT(*) INTO citus_local_table_count\n    FROM pg_dist_partition WHERE repmodel != 't' AND partmethod = 'n';\n\n    IF citus_local_table_count = 0 THEN\n        -- no citus local tables exist, can safely downgrade\n        DROP FUNCTION create_citus_local_table(table_name regclass);\n    ELSE\n        RAISE EXCEPTION 'citus local tables are introduced in Citus 9.5'\n        USING HINT = 'To downgrade Citus to an older version, you should '\n                     'first convert each citus local table to a postgres '\n                     'table by executing SELECT undistribute_table(\"%s\")';\n    END IF;\nEND;\n$$ LANGUAGE plpgsql;\n\nDROP FUNCTION worker_record_sequence_dependency(regclass, regclass, name);\n\n--  task_tracker_* functions\n\nCREATE FUNCTION task_tracker_assign_task(bigint, integer, text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$task_tracker_assign_task$$;\nCOMMENT ON FUNCTION task_tracker_assign_task(bigint, integer, text)\n    IS 'assign a task to execute';\n\nCREATE FUNCTION task_tracker_task_status(bigint, integer)\n    RETURNS integer\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$task_tracker_task_status$$;\nCOMMENT ON FUNCTION task_tracker_task_status(bigint, integer)\n    IS 'check an assigned task''s execution status';\n\nCREATE FUNCTION task_tracker_cleanup_job(bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$task_tracker_cleanup_job$$;\nCOMMENT ON FUNCTION task_tracker_cleanup_job(bigint)\n    IS 'clean up all tasks associated with a job';\n\nCREATE FUNCTION worker_merge_files_and_run_query(bigint, integer, text, text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_merge_files_and_run_query$$;\nCOMMENT ON FUNCTION worker_merge_files_and_run_query(bigint, integer, text, text)\n    IS 'merge files and run a reduce query on merged files';\n\nCREATE FUNCTION worker_execute_sql_task(jobid bigint, taskid integer, query text, binary bool)\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_execute_sql_task$$;\nCOMMENT ON FUNCTION worker_execute_sql_task(bigint, integer, text, bool)\n    IS 'execute a query and write the results to a task file';\n\nCREATE FUNCTION task_tracker_conninfo_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'citus', $$task_tracker_conninfo_cache_invalidate$$;\nCOMMENT ON FUNCTION task_tracker_conninfo_cache_invalidate()\n    IS 'invalidate task-tracker conninfo cache';\n\nCREATE TRIGGER dist_poolinfo_task_tracker_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE\n    ON pg_catalog.pg_dist_poolinfo\n    FOR EACH STATEMENT EXECUTE PROCEDURE task_tracker_conninfo_cache_invalidate();\n\nCREATE TRIGGER dist_authinfo_task_tracker_cache_invalidate\n    AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE\n    ON pg_catalog.pg_dist_authinfo\n    FOR EACH STATEMENT EXECUTE PROCEDURE task_tracker_conninfo_cache_invalidate();\n\nCREATE FUNCTION master_drop_sequences(sequence_names text[])\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$master_drop_sequences$$;\nCOMMENT ON FUNCTION master_drop_sequences(text[])\n    IS 'drop specified sequences from the cluster';\n\nRESET search_path;\n\nDROP FUNCTION pg_catalog.undistribute_table(table_name regclass);\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/alter_distributed_table/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_distributed_table(\n    table_name regclass, distribution_column text DEFAULT NULL, shard_count int DEFAULT NULL, colocate_with text DEFAULT NULL, cascade_to_colocated boolean DEFAULT NULL)\n    RETURNS VOID\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$alter_distributed_table$$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_distributed_table(\n    table_name regclass, distribution_column text, shard_count int, colocate_with text, cascade_to_colocated boolean)\n    IS 'alters a distributed table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/alter_distributed_table/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_distributed_table(\n    table_name regclass, distribution_column text DEFAULT NULL, shard_count int DEFAULT NULL, colocate_with text DEFAULT NULL, cascade_to_colocated boolean DEFAULT NULL)\n    RETURNS VOID\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$alter_distributed_table$$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_distributed_table(\n    table_name regclass, distribution_column text, shard_count int, colocate_with text, cascade_to_colocated boolean)\n    IS 'alters a distributed table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/alter_old_partitions_set_access_method/10.0-1.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.alter_old_partitions_set_access_method(\n\t\tparent_table_name regclass,\n\t\tolder_than timestamptz,\n\t\tnew_access_method name)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    r record;\nBEGIN\n\t-- first check whether we can convert all the to_value's to timestamptz\n\tBEGIN\n\t\tPERFORM\n\t\tFROM pg_catalog.time_partitions\n\t\tWHERE parent_table = parent_table_name\n\t\tAND to_value IS NOT NULL\n\t\tAND to_value::timestamptz <= older_than\n\t\tAND access_method <> new_access_method;\n\tEXCEPTION WHEN invalid_datetime_format THEN\n\t\tRAISE 'partition column of % cannot be cast to a timestamptz', parent_table_name;\n\tEND;\n\n\t-- now convert the partitions in separate transactions\n    FOR r IN\n\t\tSELECT partition, from_value, to_value\n\t\tFROM pg_catalog.time_partitions\n\t\tWHERE parent_table = parent_table_name\n\t\tAND to_value IS NOT NULL\n\t\tAND to_value::timestamptz <= older_than\n\t\tAND access_method <> new_access_method\n\t\tORDER BY to_value::timestamptz\n    LOOP\n        RAISE NOTICE 'converting % with start time % and end time %', r.partition, r.from_value, r.to_value;\n        PERFORM pg_catalog.alter_table_set_access_method(r.partition, new_access_method);\n        COMMIT;\n    END LOOP;\nEND;\n$$;\nCOMMENT ON PROCEDURE pg_catalog.alter_old_partitions_set_access_method(\n\t\tparent_table_name regclass,\n\t\tolder_than timestamptz,\n\t\tnew_access_method name)\nIS 'convert old partitions of a time-partitioned table to a new access method';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/alter_old_partitions_set_access_method/latest.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.alter_old_partitions_set_access_method(\n\t\tparent_table_name regclass,\n\t\tolder_than timestamptz,\n\t\tnew_access_method name)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    r record;\nBEGIN\n\t-- first check whether we can convert all the to_value's to timestamptz\n\tBEGIN\n\t\tPERFORM\n\t\tFROM pg_catalog.time_partitions\n\t\tWHERE parent_table = parent_table_name\n\t\tAND to_value IS NOT NULL\n\t\tAND to_value::timestamptz <= older_than\n\t\tAND access_method <> new_access_method;\n\tEXCEPTION WHEN invalid_datetime_format THEN\n\t\tRAISE 'partition column of % cannot be cast to a timestamptz', parent_table_name;\n\tEND;\n\n\t-- now convert the partitions in separate transactions\n    FOR r IN\n\t\tSELECT partition, from_value, to_value\n\t\tFROM pg_catalog.time_partitions\n\t\tWHERE parent_table = parent_table_name\n\t\tAND to_value IS NOT NULL\n\t\tAND to_value::timestamptz <= older_than\n\t\tAND access_method <> new_access_method\n\t\tORDER BY to_value::timestamptz\n    LOOP\n        RAISE NOTICE 'converting % with start time % and end time %', r.partition, r.from_value, r.to_value;\n        PERFORM pg_catalog.alter_table_set_access_method(r.partition, new_access_method);\n        COMMIT;\n    END LOOP;\nEND;\n$$;\nCOMMENT ON PROCEDURE pg_catalog.alter_old_partitions_set_access_method(\n\t\tparent_table_name regclass,\n\t\tolder_than timestamptz,\n\t\tnew_access_method name)\nIS 'convert old partitions of a time-partitioned table to a new access method';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/alter_role_if_exists/9.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_role_if_exists(\n    role_name text,\n    utility_query text)\n    RETURNS BOOL\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$alter_role_if_exists$$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_role_if_exists(\n    role_name text,\n    utility_query text)\n    IS 'runs the utility query, if the role exists';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/alter_role_if_exists/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_role_if_exists(\n    role_name text,\n    utility_query text)\n    RETURNS BOOL\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$alter_role_if_exists$$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_role_if_exists(\n    role_name text,\n    utility_query text)\n    IS 'runs the utility query, if the role exists';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/alter_table_set_access_method/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_table_set_access_method(\n    table_name regclass, access_method text)\n    RETURNS VOID\n    LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$alter_table_set_access_method$$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_table_set_access_method(\n    table_name regclass, access_method text)\n    IS 'alters a table''s access method';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/alter_table_set_access_method/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.alter_table_set_access_method(\n    table_name regclass, access_method text)\n    RETURNS VOID\n    LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$alter_table_set_access_method$$;\n\nCOMMENT ON FUNCTION pg_catalog.alter_table_set_access_method(\n    table_name regclass, access_method text)\n    IS 'alters a table''s access method';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/any_value/9.1-1.sql",
    "content": "DO $proc$\nBEGIN\n-- PG16 has its own any_value, so only create it pre PG16.\nIF substring(current_Setting('server_version'), '\\d+')::int < 16 THEN\n    EXECUTE $$\n\nCREATE OR REPLACE FUNCTION pg_catalog.any_value_agg ( anyelement, anyelement )\nRETURNS anyelement AS $agg$\n        SELECT CASE WHEN $1 IS NULL THEN $2 ELSE $1 END;\n$agg$ LANGUAGE SQL STABLE;\n\nCREATE AGGREGATE pg_catalog.any_value (\n        sfunc       = pg_catalog.any_value_agg,\n        combinefunc = pg_catalog.any_value_agg,\n        basetype    = anyelement,\n        stype       = anyelement\n);\nCOMMENT ON AGGREGATE pg_catalog.any_value(anyelement) IS\n    'Returns the value of any row in the group. It is mostly useful when you know there will be only 1 element.';\n\n  $$;\nEND IF;\nEND $proc$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/any_value/latest.sql",
    "content": "DO $proc$\nBEGIN\n-- PG16 has its own any_value, so only create it pre PG16.\nIF substring(current_Setting('server_version'), '\\d+')::int < 16 THEN\n    EXECUTE $$\n\nCREATE OR REPLACE FUNCTION pg_catalog.any_value_agg ( anyelement, anyelement )\nRETURNS anyelement AS $agg$\n        SELECT CASE WHEN $1 IS NULL THEN $2 ELSE $1 END;\n$agg$ LANGUAGE SQL STABLE;\n\nCREATE AGGREGATE pg_catalog.any_value (\n        sfunc       = pg_catalog.any_value_agg,\n        combinefunc = pg_catalog.any_value_agg,\n        basetype    = anyelement,\n        stype       = anyelement\n);\nCOMMENT ON AGGREGATE pg_catalog.any_value(anyelement) IS\n    'Returns the value of any row in the group. It is mostly useful when you know there will be only 1 element.';\n\n  $$;\nEND IF;\nEND $proc$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_activate_node/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_activate_node(nodename text,\n                                               nodeport integer)\n    RETURNS INTEGER\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME',$$citus_activate_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_activate_node(nodename text, nodeport integer)\n    IS 'activate a node which is in the cluster';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_activate_node(text, integer) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_activate_node/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_activate_node(nodename text,\n                                               nodeport integer)\n    RETURNS INTEGER\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME',$$citus_activate_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_activate_node(nodename text, nodeport integer)\n    IS 'activate a node which is in the cluster';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_activate_node(text, integer) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_clone_node/13.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_add_clone_node(\n    replica_hostname text,\n    replica_port integer,\n    primary_hostname text,\n    primary_port integer)\n RETURNS INTEGER\n LANGUAGE C VOLATILE STRICT\n AS 'MODULE_PATHNAME', $$citus_add_clone_node$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_add_clone_node(text, integer, text, integer) IS\n'Adds a new node as a clone of an existing primary node. The clone is initially inactive. Returns the nodeid of the new clone node.';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_add_clone_node(text, int, text, int) FROM PUBLIC;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_add_clone_node_with_nodeid(\n    replica_hostname text,\n    replica_port integer,\n    primary_nodeid integer)\n RETURNS INTEGER\n LANGUAGE C VOLATILE STRICT\n AS 'MODULE_PATHNAME', $$citus_add_clone_node_with_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_add_clone_node_with_nodeid(text, integer, integer) IS\n'Adds a new node as a clone of an existing primary node using the primary node''s ID. The clone is initially inactive. Returns the nodeid of the new clone node.';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_add_clone_node_with_nodeid(text, int, int) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_clone_node/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_add_clone_node(\n    replica_hostname text,\n    replica_port integer,\n    primary_hostname text,\n    primary_port integer)\n RETURNS INTEGER\n LANGUAGE C VOLATILE STRICT\n AS 'MODULE_PATHNAME', $$citus_add_clone_node$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_add_clone_node(text, integer, text, integer) IS\n'Adds a new node as a clone of an existing primary node. The clone is initially inactive. Returns the nodeid of the new clone node.';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_add_clone_node(text, int, text, int) FROM PUBLIC;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_add_clone_node_with_nodeid(\n    replica_hostname text,\n    replica_port integer,\n    primary_nodeid integer)\n RETURNS INTEGER\n LANGUAGE C VOLATILE STRICT\n AS 'MODULE_PATHNAME', $$citus_add_clone_node_with_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_add_clone_node_with_nodeid(text, integer, integer) IS\n'Adds a new node as a clone of an existing primary node using the primary node''s ID. The clone is initially inactive. Returns the nodeid of the new clone node.';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_add_clone_node_with_nodeid(text, int, int) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_inactive_node/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_add_inactive_node(nodename text,\n                                                   nodeport integer,\n                                        groupid integer default -1,\n                                        noderole noderole default 'primary',\n                                        nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME',$$citus_add_inactive_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_add_inactive_node(nodename text,nodeport integer,\n                                            groupid integer, noderole noderole,\n                                            nodecluster name)\n  IS 'prepare node by adding it to pg_dist_node';\nREVOKE ALL ON FUNCTION pg_catalog.citus_add_inactive_node(text,int,int,noderole,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_inactive_node/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_add_inactive_node(nodename text,\n                                                   nodeport integer,\n                                        groupid integer default -1,\n                                        noderole noderole default 'primary',\n                                        nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME',$$citus_add_inactive_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_add_inactive_node(nodename text,nodeport integer,\n                                            groupid integer, noderole noderole,\n                                            nodecluster name)\n  IS 'prepare node by adding it to pg_dist_node';\nREVOKE ALL ON FUNCTION pg_catalog.citus_add_inactive_node(text,int,int,noderole,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_node/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_add_node(nodename text,\n                                          nodeport integer,\n                                          groupid integer default -1,\n                                          noderole noderole default 'primary',\n                                          nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$citus_add_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_add_node(nodename text, nodeport integer,\n                                              groupid integer, noderole noderole, nodecluster name)\n  IS 'add node to the cluster';\nREVOKE ALL ON FUNCTION pg_catalog.citus_add_node(text,int,int,noderole,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_node/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_add_node(nodename text,\n                                          nodeport integer,\n                                          groupid integer default -1,\n                                          noderole noderole default 'primary',\n                                          nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$citus_add_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_add_node(nodename text, nodeport integer,\n                                              groupid integer, noderole noderole, nodecluster name)\n  IS 'add node to the cluster';\nREVOKE ALL ON FUNCTION pg_catalog.citus_add_node(text,int,int,noderole,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_rebalance_strategy/10.1-1.sql",
    "content": "DROP FUNCTION pg_catalog.citus_add_rebalance_strategy;\nCREATE OR REPLACE FUNCTION pg_catalog.citus_add_rebalance_strategy(\n    name name,\n    shard_cost_function regproc,\n    node_capacity_function regproc,\n    shard_allowed_on_node_function regproc,\n    default_threshold float4,\n    minimum_threshold float4 DEFAULT 0,\n    improvement_threshold float4 DEFAULT 0\n)\n    RETURNS VOID AS $$\n    INSERT INTO\n        pg_catalog.pg_dist_rebalance_strategy(\n            name,\n            shard_cost_function,\n            node_capacity_function,\n            shard_allowed_on_node_function,\n            default_threshold,\n            minimum_threshold\n        ) VALUES (\n            name,\n            shard_cost_function,\n            node_capacity_function,\n            shard_allowed_on_node_function,\n            default_threshold,\n            minimum_threshold\n        );\n    $$ LANGUAGE sql;\nCOMMENT ON FUNCTION pg_catalog.citus_add_rebalance_strategy(name,regproc,regproc,regproc,float4, float4, float4)\n  IS 'adds a new rebalance strategy which can be used when rebalancing shards or draining nodes';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_rebalance_strategy/13.1-1.sql",
    "content": "DROP FUNCTION pg_catalog.citus_add_rebalance_strategy;\nCREATE OR REPLACE FUNCTION pg_catalog.citus_add_rebalance_strategy(\n    name name,\n    shard_cost_function regproc,\n    node_capacity_function regproc,\n    shard_allowed_on_node_function regproc,\n    default_threshold float4,\n    minimum_threshold float4 DEFAULT 0,\n    improvement_threshold float4 DEFAULT 0\n)\n    RETURNS VOID AS $$\n    INSERT INTO\n        pg_catalog.pg_dist_rebalance_strategy(\n            name,\n            shard_cost_function,\n            node_capacity_function,\n            shard_allowed_on_node_function,\n            default_threshold,\n            minimum_threshold,\n            improvement_threshold\n        ) VALUES (\n            name,\n            shard_cost_function,\n            node_capacity_function,\n            shard_allowed_on_node_function,\n            default_threshold,\n            minimum_threshold,\n            improvement_threshold\n        );\n    $$ LANGUAGE sql;\nCOMMENT ON FUNCTION pg_catalog.citus_add_rebalance_strategy(name,regproc,regproc,regproc,float4, float4, float4)\n  IS 'adds a new rebalance strategy which can be used when rebalancing shards or draining nodes';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_rebalance_strategy/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_add_rebalance_strategy(\n    name name,\n    shard_cost_function regproc,\n    node_capacity_function regproc,\n    shard_allowed_on_node_function regproc,\n    default_threshold float4,\n    minimum_threshold float4 DEFAULT 0\n)\n    RETURNS VOID AS $$\n    INSERT INTO\n        pg_catalog.pg_dist_rebalance_strategy(\n            name,\n            shard_cost_function,\n            node_capacity_function,\n            shard_allowed_on_node_function,\n            default_threshold,\n            minimum_threshold\n        ) VALUES (\n            name,\n            shard_cost_function,\n            node_capacity_function,\n            shard_allowed_on_node_function,\n            default_threshold,\n            minimum_threshold\n        );\n    $$ LANGUAGE sql;\nCOMMENT ON FUNCTION pg_catalog.citus_add_rebalance_strategy(name,regproc,regproc,regproc,float4, float4)\n  IS 'adds a new rebalance strategy which can be used when rebalancing shards or draining nodes';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_rebalance_strategy/latest.sql",
    "content": "DROP FUNCTION pg_catalog.citus_add_rebalance_strategy;\nCREATE OR REPLACE FUNCTION pg_catalog.citus_add_rebalance_strategy(\n    name name,\n    shard_cost_function regproc,\n    node_capacity_function regproc,\n    shard_allowed_on_node_function regproc,\n    default_threshold float4,\n    minimum_threshold float4 DEFAULT 0,\n    improvement_threshold float4 DEFAULT 0\n)\n    RETURNS VOID AS $$\n    INSERT INTO\n        pg_catalog.pg_dist_rebalance_strategy(\n            name,\n            shard_cost_function,\n            node_capacity_function,\n            shard_allowed_on_node_function,\n            default_threshold,\n            minimum_threshold,\n            improvement_threshold\n        ) VALUES (\n            name,\n            shard_cost_function,\n            node_capacity_function,\n            shard_allowed_on_node_function,\n            default_threshold,\n            minimum_threshold,\n            improvement_threshold\n        );\n    $$ LANGUAGE sql;\nCOMMENT ON FUNCTION pg_catalog.citus_add_rebalance_strategy(name,regproc,regproc,regproc,float4, float4, float4)\n  IS 'adds a new rebalance strategy which can be used when rebalancing shards or draining nodes';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_secondary_node/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_add_secondary_node(nodename text,\n                                         nodeport integer,\n                                         primaryname text,\n                                         primaryport integer,\n                                         nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$citus_add_secondary_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_add_secondary_node(nodename text, nodeport integer,\n                                             primaryname text, primaryport integer,\n                                             nodecluster name)\n  IS 'add a secondary node to the cluster';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_add_secondary_node(text,int,text,int,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_add_secondary_node/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_add_secondary_node(nodename text,\n                                         nodeport integer,\n                                         primaryname text,\n                                         primaryport integer,\n                                         nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$citus_add_secondary_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_add_secondary_node(nodename text, nodeport integer,\n                                             primaryname text, primaryport integer,\n                                             nodecluster name)\n  IS 'add a secondary node to the cluster';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_add_secondary_node(text,int,text,int,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_backend_gpid/11.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_backend_gpid()\n    RETURNS BIGINT\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME',$$citus_backend_gpid$$;\nCOMMENT ON FUNCTION pg_catalog.citus_backend_gpid()\n    IS 'returns gpid of the current backend';\n\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_backend_gpid() TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_backend_gpid/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_backend_gpid()\n    RETURNS BIGINT\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME',$$citus_backend_gpid$$;\nCOMMENT ON FUNCTION pg_catalog.citus_backend_gpid()\n    IS 'returns gpid of the current backend';\n\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_backend_gpid() TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_blocking_pids/11.0-1.sql",
    "content": "DROP FUNCTION pg_catalog.citus_blocking_pids;\nCREATE FUNCTION pg_catalog.citus_blocking_pids(pBlockedPid integer)\nRETURNS int4[] AS $$\n  DECLARE\n    mLocalBlockingPids int4[];\n    mRemoteBlockingPids int4[];\n    mLocalGlobalPid int8;\n  BEGIN\n    SELECT pg_catalog.old_pg_blocking_pids(pBlockedPid) INTO mLocalBlockingPids;\n\n    IF (array_length(mLocalBlockingPids, 1) > 0) THEN\n      RETURN mLocalBlockingPids;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    SELECT global_pid INTO mLocalGlobalPid\n      FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n\n    SELECT array_agg(global_pid) INTO mRemoteBlockingPids FROM (\n      WITH activeTransactions AS (\n        SELECT global_pid FROM get_all_active_transactions()\n      ), blockingTransactions AS (\n        SELECT blocking_global_pid FROM citus_internal_global_blocked_processes()\n        WHERE waiting_global_pid = mLocalGlobalPid\n      )\n      SELECT activeTransactions.global_pid FROM activeTransactions, blockingTransactions\n      WHERE activeTransactions.global_pid = blockingTransactions.blocking_global_pid\n    ) AS sub;\n\n    RETURN mRemoteBlockingPids;\n  END;\n$$ LANGUAGE plpgsql;\n\nREVOKE ALL ON FUNCTION citus_blocking_pids(integer) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_blocking_pids/13.1-1.sql",
    "content": "DROP FUNCTION pg_catalog.citus_blocking_pids;\nCREATE FUNCTION pg_catalog.citus_blocking_pids(pBlockedPid integer)\nRETURNS int4[] AS $$\n  DECLARE\n    mLocalBlockingPids int4[];\n    mRemoteBlockingPids int4[];\n    mLocalGlobalPid int8;\n  BEGIN\n    SELECT pg_catalog.old_pg_blocking_pids(pBlockedPid) INTO mLocalBlockingPids;\n\n    IF (array_length(mLocalBlockingPids, 1) > 0) THEN\n      RETURN mLocalBlockingPids;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    SELECT global_pid INTO mLocalGlobalPid\n      FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n\n    SELECT array_agg(global_pid) INTO mRemoteBlockingPids FROM (\n      WITH activeTransactions AS (\n        SELECT global_pid FROM get_all_active_transactions()\n      ), blockingTransactions AS (\n        SELECT blocking_global_pid FROM citus_internal.global_blocked_processes()\n        WHERE waiting_global_pid = mLocalGlobalPid\n      )\n      SELECT activeTransactions.global_pid FROM activeTransactions, blockingTransactions\n      WHERE activeTransactions.global_pid = blockingTransactions.blocking_global_pid\n    ) AS sub;\n\n    RETURN mRemoteBlockingPids;\n  END;\n$$ LANGUAGE plpgsql;\n\nREVOKE ALL ON FUNCTION citus_blocking_pids(integer) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_blocking_pids/latest.sql",
    "content": "DROP FUNCTION pg_catalog.citus_blocking_pids;\nCREATE FUNCTION pg_catalog.citus_blocking_pids(pBlockedPid integer)\nRETURNS int4[] AS $$\n  DECLARE\n    mLocalBlockingPids int4[];\n    mRemoteBlockingPids int4[];\n    mLocalGlobalPid int8;\n  BEGIN\n    SELECT pg_catalog.old_pg_blocking_pids(pBlockedPid) INTO mLocalBlockingPids;\n\n    IF (array_length(mLocalBlockingPids, 1) > 0) THEN\n      RETURN mLocalBlockingPids;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    SELECT global_pid INTO mLocalGlobalPid\n      FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n\n    SELECT array_agg(global_pid) INTO mRemoteBlockingPids FROM (\n      WITH activeTransactions AS (\n        SELECT global_pid FROM get_all_active_transactions()\n      ), blockingTransactions AS (\n        SELECT blocking_global_pid FROM citus_internal.global_blocked_processes()\n        WHERE waiting_global_pid = mLocalGlobalPid\n      )\n      SELECT activeTransactions.global_pid FROM activeTransactions, blockingTransactions\n      WHERE activeTransactions.global_pid = blockingTransactions.blocking_global_pid\n    ) AS sub;\n\n    RETURN mRemoteBlockingPids;\n  END;\n$$ LANGUAGE plpgsql;\n\nREVOKE ALL ON FUNCTION citus_blocking_pids(integer) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_calculate_gpid/11.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_calculate_gpid(nodeid integer,\n                                                pid integer)\n    RETURNS BIGINT\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME',$$citus_calculate_gpid$$;\nCOMMENT ON FUNCTION pg_catalog.citus_calculate_gpid(nodeid integer, pid integer)\n    IS 'calculate gpid of a backend running on any node';\n\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_calculate_gpid(integer, integer) TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_calculate_gpid/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_calculate_gpid(nodeid integer,\n                                                pid integer)\n    RETURNS BIGINT\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME',$$citus_calculate_gpid$$;\nCOMMENT ON FUNCTION pg_catalog.citus_calculate_gpid(nodeid integer, pid integer)\n    IS 'calculate gpid of a backend running on any node';\n\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_calculate_gpid(integer, integer) TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_check_cluster_node_health/11.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_check_cluster_node_health (\n    OUT from_nodename text,\n    OUT from_nodeport int,\n    OUT to_nodename text,\n    OUT to_nodeport int,\n    OUT result bool )\n    RETURNS SETOF RECORD\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME', $$citus_check_cluster_node_health$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_check_cluster_node_health ()\n    IS 'checks connections between all nodes in the cluster';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_check_cluster_node_health/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_check_cluster_node_health (\n    OUT from_nodename text,\n    OUT from_nodeport int,\n    OUT to_nodename text,\n    OUT to_nodeport int,\n    OUT result bool )\n    RETURNS SETOF RECORD\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME', $$citus_check_cluster_node_health$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_check_cluster_node_health ()\n    IS 'checks connections between all nodes in the cluster';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_check_connection_to_node/11.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_check_connection_to_node (\n    nodename text,\n    nodeport integer DEFAULT 5432)\n    RETURNS bool\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME', $$citus_check_connection_to_node$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_check_connection_to_node (\n    nodename text, nodeport integer)\n    IS 'checks connection to another node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_check_connection_to_node/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_check_connection_to_node (\n    nodename text,\n    nodeport integer DEFAULT 5432)\n    RETURNS bool\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME', $$citus_check_connection_to_node$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_check_connection_to_node (\n    nodename text, nodeport integer)\n    IS 'checks connection to another node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_cleanup_orphaned_resources/11.1-1.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.citus_cleanup_orphaned_resources()\n    LANGUAGE C\n    AS 'citus', $$citus_cleanup_orphaned_resources$$;\nCOMMENT ON PROCEDURE pg_catalog.citus_cleanup_orphaned_resources()\n    IS 'cleanup orphaned resources';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_cleanup_orphaned_resources/latest.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.citus_cleanup_orphaned_resources()\n    LANGUAGE C\n    AS 'citus', $$citus_cleanup_orphaned_resources$$;\nCOMMENT ON PROCEDURE pg_catalog.citus_cleanup_orphaned_resources()\n    IS 'cleanup orphaned resources';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_cleanup_orphaned_shards/10.1-1.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.citus_cleanup_orphaned_shards()\n    LANGUAGE C\n    AS 'citus', $$citus_cleanup_orphaned_shards$$;\nCOMMENT ON PROCEDURE pg_catalog.citus_cleanup_orphaned_shards()\n    IS 'cleanup orphaned shards';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_cleanup_orphaned_shards/latest.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.citus_cleanup_orphaned_shards()\n    LANGUAGE C\n    AS 'citus', $$citus_cleanup_orphaned_shards$$;\nCOMMENT ON PROCEDURE pg_catalog.citus_cleanup_orphaned_shards()\n    IS 'cleanup orphaned shards';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_conninfo_cache_invalidate/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_conninfo_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_conninfo_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_conninfo_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_conninfo_cache_invalidate/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.master_conninfo_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$master_dist_authinfo_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.master_conninfo_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_conninfo_cache_invalidate/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_conninfo_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_conninfo_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_conninfo_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_coordinator_nodeid/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_coordinator_nodeid()\n    RETURNS integer\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$citus_coordinator_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_coordinator_nodeid()\n    IS 'returns node id of the coordinator node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_coordinator_nodeid/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_coordinator_nodeid()\n    RETURNS integer\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$citus_coordinator_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_coordinator_nodeid()\n    IS 'returns node id of the coordinator node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_copy_shard_placement/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_copy_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tdo_repair bool DEFAULT true,\n\ttransfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_copy_shard_placement$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_copy_shard_placement(shard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tdo_repair bool,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'copy a shard from the source node to the destination node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_copy_shard_placement/11.1-1.sql",
    "content": "DROP FUNCTION pg_catalog.citus_copy_shard_placement;\nCREATE FUNCTION pg_catalog.citus_copy_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\ttransfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_copy_shard_placement$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_copy_shard_placement(shard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'copy a shard from the source node to the destination node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_copy_shard_placement/11.2-1.sql",
    "content": "-- citus_copy_shard_placement, but with nodeid\nCREATE FUNCTION pg_catalog.citus_copy_shard_placement(\n\tshard_id bigint,\n\tsource_node_id integer,\n\ttarget_node_id integer,\n\ttransfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_copy_shard_placement_with_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_copy_shard_placement(\n\tshard_id bigint,\n\tsource_node_id integer,\n\ttarget_node_id integer,\n\ttransfer_mode citus.shard_transfer_mode)\nIS 'copy a shard from the source node to the destination node';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_copy_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\ttransfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_copy_shard_placement$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_copy_shard_placement(shard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'copy a shard from the source node to the destination node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_copy_shard_placement/latest.sql",
    "content": "-- citus_copy_shard_placement, but with nodeid\nCREATE FUNCTION pg_catalog.citus_copy_shard_placement(\n\tshard_id bigint,\n\tsource_node_id integer,\n\ttarget_node_id integer,\n\ttransfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_copy_shard_placement_with_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_copy_shard_placement(\n\tshard_id bigint,\n\tsource_node_id integer,\n\ttarget_node_id integer,\n\ttransfer_mode citus.shard_transfer_mode)\nIS 'copy a shard from the source node to the destination node';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_copy_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\ttransfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_copy_shard_placement$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_copy_shard_placement(shard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'copy a shard from the source node to the destination node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_disable_node/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_disable_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer)\n\tIS 'removes node from the cluster temporarily';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_disable_node(text,int) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_disable_node/11.0-1.sql",
    "content": "DROP FUNCTION  pg_catalog.citus_disable_node(nodename text, nodeport integer);\nCREATE FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer, force bool default false)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_disable_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer, force bool)\n\tIS 'removes node from the cluster temporarily';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_disable_node(text,int, bool) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_disable_node/11.0-2.sql",
    "content": "DROP FUNCTION  pg_catalog.citus_disable_node(nodename text, nodeport integer, force bool);\nCREATE FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer, synchronous bool default false)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_disable_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer, synchronous bool)\n\tIS 'removes node from the cluster temporarily';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_disable_node(text,int, bool) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_disable_node/latest.sql",
    "content": "DROP FUNCTION  pg_catalog.citus_disable_node(nodename text, nodeport integer, force bool);\nCREATE FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer, synchronous bool default false)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_disable_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_disable_node(nodename text, nodeport integer, synchronous bool)\n\tIS 'removes node from the cluster temporarily';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_disable_node(text,int, bool) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_local_group_cache_invalidate/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_local_group_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_dist_local_group_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_local_group_cache_invalidate()\n    IS 'register node cache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_local_group_cache_invalidate/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.master_dist_local_group_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$master_dist_local_group_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.master_dist_local_group_cache_invalidate()\n    IS 'register node cache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_local_group_cache_invalidate/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_local_group_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_dist_local_group_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_local_group_cache_invalidate()\n    IS 'register node cache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_node_cache_invalidate/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_node_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_dist_node_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_node_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_node_cache_invalidate/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.master_dist_node_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$master_dist_node_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.master_dist_node_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_node_cache_invalidate/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_node_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_dist_node_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_node_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_object_cache_invalidate/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_object_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_dist_object_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_object_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_object_cache_invalidate/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.master_dist_object_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$master_dist_object_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.master_dist_object_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_object_cache_invalidate/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_object_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_dist_object_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_object_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_partition_cache_invalidate/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_partition_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'citus', $$citus_dist_partition_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_partition_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_partition_cache_invalidate/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.master_dist_partition_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'citus', $$master_dist_partition_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.master_dist_partition_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_partition_cache_invalidate/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_partition_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'citus', $$citus_dist_partition_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_partition_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_placement_cache_invalidate/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_placement_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_dist_placement_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_placement_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_placement_cache_invalidate/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.master_dist_placement_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$master_dist_placement_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.master_dist_placement_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_placement_cache_invalidate/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_placement_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_dist_placement_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_placement_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_shard_cache_invalidate/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_shard_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_dist_shard_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_shard_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_shard_cache_invalidate/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.master_dist_shard_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$master_dist_shard_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.master_dist_shard_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_shard_cache_invalidate/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_dist_shard_cache_invalidate()\n    RETURNS trigger\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_dist_shard_cache_invalidate$$;\nCOMMENT ON FUNCTION pg_catalog.citus_dist_shard_cache_invalidate()\n    IS 'register relcache invalidation for changed rows';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_stat_activity/11.0-1.sql",
    "content": "DROP VIEW IF EXISTS pg_catalog.citus_dist_stat_activity;\n\nCREATE OR REPLACE VIEW citus.citus_dist_stat_activity AS\nSELECT * FROM citus_stat_activity\nWHERE is_worker_query = false;\n\nALTER VIEW citus.citus_dist_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_dist_stat_activity TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_dist_stat_activity/latest.sql",
    "content": "DROP VIEW IF EXISTS pg_catalog.citus_dist_stat_activity;\n\nCREATE OR REPLACE VIEW citus.citus_dist_stat_activity AS\nSELECT * FROM citus_stat_activity\nWHERE is_worker_query = false;\n\nALTER VIEW citus.citus_dist_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_dist_stat_activity TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_drain_node/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_drain_node(\n    nodename text,\n    nodeport integer,\n    shard_transfer_mode citus.shard_transfer_mode default 'auto',\n    rebalance_strategy name default NULL\n  )\n  RETURNS VOID\n  LANGUAGE C\n  AS 'MODULE_PATHNAME', $$citus_drain_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_drain_node(text,int,citus.shard_transfer_mode,name)\n  IS 'mark a node to be drained of data and actually drain it as well';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_drain_node(text,int,citus.shard_transfer_mode,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_drain_node/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_drain_node(\n    nodename text,\n    nodeport integer,\n    shard_transfer_mode citus.shard_transfer_mode default 'auto',\n    rebalance_strategy name default NULL\n  )\n  RETURNS VOID\n  LANGUAGE C\n  AS 'MODULE_PATHNAME', $$citus_drain_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_drain_node(text,int,citus.shard_transfer_mode,name)\n  IS 'mark a node to be drained of data and actually drain it as well';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_drain_node(text,int,citus.shard_transfer_mode,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_drop_trigger/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.notify_constraint_dropped()\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$notify_constraint_dropped$$;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_drop_trigger()\n    RETURNS event_trigger\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cdbdt$\nDECLARE\n    constraint_event_count INTEGER;\n    v_obj record;\n    sequence_names text[] := '{}';\n    table_colocation_id integer;\n    propagate_drop boolean := false;\nBEGIN\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n                 WHERE object_type IN ('table', 'foreign table')\n    LOOP\n        -- first drop the table and metadata on the workers\n        -- then drop all the shards on the workers\n        -- finally remove the pg_dist_partition entry on the coordinator\n        PERFORM master_remove_distributed_table_metadata_from_workers(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n        PERFORM citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n        PERFORM master_remove_partition_metadata(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n    END LOOP;\n\n    -- remove entries from citus.pg_dist_object for all dropped root (objsubid = 0) objects\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n    LOOP\n        PERFORM master_unmark_object_distributed(v_obj.classid, v_obj.objid, v_obj.objsubid);\n    END LOOP;\n\n    SELECT COUNT(*) INTO constraint_event_count\n    FROM pg_event_trigger_dropped_objects()\n    WHERE object_type IN ('table constraint');\n\n    IF constraint_event_count > 0\n    THEN\n        -- Tell utility hook that a table constraint is dropped so we might\n        -- need to undistribute some of the citus local tables that are not\n        -- connected to any reference tables.\n        PERFORM notify_constraint_dropped();\n    END IF;\nEND;\n$cdbdt$;\nCOMMENT ON FUNCTION pg_catalog.citus_drop_trigger()\n    IS 'perform checks and actions at the end of DROP actions';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_drop_trigger/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_drop_trigger()\n    RETURNS event_trigger\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cdbdt$\nDECLARE\n    constraint_event_count INTEGER;\n    v_obj record;\n    dropped_table_is_a_partition boolean := false;\nBEGIN\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n                 WHERE object_type IN ('table', 'foreign table')\n    LOOP\n        -- first drop the table and metadata on the workers\n        -- then drop all the shards on the workers\n        -- finally remove the pg_dist_partition entry on the coordinator\n        PERFORM master_remove_distributed_table_metadata_from_workers(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n\n        -- If both original and normal values are false, the dropped table was a partition\n        -- that was dropped as a result of its parent being dropped\n        -- NOTE: the other way around is not true:\n        -- the table being a partition doesn't imply both original and normal values are false\n        SELECT (v_obj.original = false AND v_obj.normal = false) INTO dropped_table_is_a_partition;\n\n        -- The partition's shards will be dropped when dropping the parent's shards, so we can skip:\n        -- i.e. we call citus_drop_all_shards with drop_shards_metadata_only parameter set to true\n        IF dropped_table_is_a_partition\n        THEN\n            PERFORM citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := true);\n        ELSE\n            PERFORM citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false);\n        END IF;\n\n        PERFORM master_remove_partition_metadata(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n    END LOOP;\n\n    -- remove entries from citus.pg_dist_object for all dropped root (objsubid = 0) objects\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n    LOOP\n        PERFORM master_unmark_object_distributed(v_obj.classid, v_obj.objid, v_obj.objsubid);\n    END LOOP;\n\n    SELECT COUNT(*) INTO constraint_event_count\n    FROM pg_event_trigger_dropped_objects()\n    WHERE object_type IN ('table constraint');\n\n    IF constraint_event_count > 0\n    THEN\n        -- Tell utility hook that a table constraint is dropped so we might\n        -- need to undistribute some of the citus local tables that are not\n        -- connected to any reference tables.\n        PERFORM notify_constraint_dropped();\n    END IF;\nEND;\n$cdbdt$;\nCOMMENT ON FUNCTION pg_catalog.citus_drop_trigger()\n    IS 'perform checks and actions at the end of DROP actions';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_drop_trigger/12.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_drop_trigger()\n    RETURNS event_trigger\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cdbdt$\nDECLARE\n    constraint_event_count INTEGER;\n    v_obj record;\n    dropped_table_is_a_partition boolean := false;\nBEGIN\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n                 WHERE object_type IN ('table', 'foreign table')\n    LOOP\n        -- first drop the table and metadata on the workers\n        -- then drop all the shards on the workers\n        -- finally remove the pg_dist_partition entry on the coordinator\n        PERFORM master_remove_distributed_table_metadata_from_workers(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n\n        -- If both original and normal values are false, the dropped table was a partition\n        -- that was dropped as a result of its parent being dropped\n        -- NOTE: the other way around is not true:\n        -- the table being a partition doesn't imply both original and normal values are false\n        SELECT (v_obj.original = false AND v_obj.normal = false) INTO dropped_table_is_a_partition;\n\n        -- The partition's shards will be dropped when dropping the parent's shards, so we can skip:\n        -- i.e. we call citus_drop_all_shards with drop_shards_metadata_only parameter set to true\n        IF dropped_table_is_a_partition\n        THEN\n            PERFORM citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := true);\n        ELSE\n            PERFORM citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false);\n        END IF;\n\n        PERFORM master_remove_partition_metadata(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n    END LOOP;\n\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n    LOOP\n        -- Remove entries from pg_catalog.pg_dist_schema for all dropped tenant schemas.\n        -- Also delete the corresponding colocation group from pg_catalog.pg_dist_colocation.\n        --\n        -- Although normally we automatically delete the colocation groups when they become empty,\n        -- we don't do so for the colocation groups that are created for tenant schemas. For this\n        -- reason, here we need to delete the colocation group when the tenant schema is dropped.\n        IF v_obj.object_type = 'schema' AND EXISTS (SELECT 1 FROM pg_catalog.pg_dist_schema WHERE schemaid = v_obj.objid)\n        THEN\n            PERFORM pg_catalog.citus_internal_unregister_tenant_schema_globally(v_obj.objid, v_obj.object_name);\n        END IF;\n\n        -- remove entries from citus.pg_dist_object for all dropped root (objsubid = 0) objects\n        PERFORM master_unmark_object_distributed(v_obj.classid, v_obj.objid, v_obj.objsubid);\n    END LOOP;\n\n    SELECT COUNT(*) INTO constraint_event_count\n    FROM pg_event_trigger_dropped_objects()\n    WHERE object_type IN ('table constraint');\n\n    IF constraint_event_count > 0\n    THEN\n        -- Tell utility hook that a table constraint is dropped so we might\n        -- need to undistribute some of the citus local tables that are not\n        -- connected to any reference tables.\n        PERFORM notify_constraint_dropped();\n    END IF;\nEND;\n$cdbdt$;\nCOMMENT ON FUNCTION pg_catalog.citus_drop_trigger()\n    IS 'perform checks and actions at the end of DROP actions';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_drop_trigger/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_drop_trigger()\n    RETURNS event_trigger\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cdbdt$\nDECLARE\n    constraint_event_count INTEGER;\n    v_obj record;\n    dropped_table_is_a_partition boolean := false;\nBEGIN\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n                 WHERE object_type IN ('table', 'foreign table')\n    LOOP\n        -- first drop the table and metadata on the workers\n        -- then drop all the shards on the workers\n        -- finally remove the pg_dist_partition entry on the coordinator\n        PERFORM master_remove_distributed_table_metadata_from_workers(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n\n        -- If both original and normal values are false, the dropped table was a partition\n        -- that was dropped as a result of its parent being dropped\n        -- NOTE: the other way around is not true:\n        -- the table being a partition doesn't imply both original and normal values are false\n        SELECT (v_obj.original = false AND v_obj.normal = false) INTO dropped_table_is_a_partition;\n\n        -- The partition's shards will be dropped when dropping the parent's shards, so we can skip:\n        -- i.e. we call citus_drop_all_shards with drop_shards_metadata_only parameter set to true\n        IF dropped_table_is_a_partition\n        THEN\n            PERFORM citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := true);\n        ELSE\n            PERFORM citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false);\n        END IF;\n\n        PERFORM master_remove_partition_metadata(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n    END LOOP;\n\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n    LOOP\n        -- Remove entries from pg_catalog.pg_dist_schema for all dropped tenant schemas.\n        -- Also delete the corresponding colocation group from pg_catalog.pg_dist_colocation.\n        --\n        -- Although normally we automatically delete the colocation groups when they become empty,\n        -- we don't do so for the colocation groups that are created for tenant schemas. For this\n        -- reason, here we need to delete the colocation group when the tenant schema is dropped.\n        IF v_obj.object_type = 'schema' AND EXISTS (SELECT 1 FROM pg_catalog.pg_dist_schema WHERE schemaid = v_obj.objid)\n        THEN\n            PERFORM citus_internal.unregister_tenant_schema_globally(v_obj.objid, v_obj.object_name);\n        END IF;\n\n        -- remove entries from citus.pg_dist_object for all dropped root (objsubid = 0) objects\n        PERFORM master_unmark_object_distributed(v_obj.classid, v_obj.objid, v_obj.objsubid);\n    END LOOP;\n\n    SELECT COUNT(*) INTO constraint_event_count\n    FROM pg_event_trigger_dropped_objects()\n    WHERE object_type IN ('table constraint');\n\n    IF constraint_event_count > 0\n    THEN\n        -- Tell utility hook that a table constraint is dropped so we might\n        -- need to undistribute some of the citus local tables that are not\n        -- connected to any reference tables.\n        PERFORM notify_constraint_dropped();\n    END IF;\nEND;\n$cdbdt$;\nCOMMENT ON FUNCTION pg_catalog.citus_drop_trigger()\n    IS 'perform checks and actions at the end of DROP actions';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_drop_trigger/9.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_drop_trigger()\n    RETURNS event_trigger\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cdbdt$\nDECLARE\n    v_obj record;\n    sequence_names text[] := '{}';\n    table_colocation_id integer;\n    propagate_drop boolean := false;\nBEGIN\n    -- collect set of dropped sequences to drop on workers later\n    SELECT array_agg(object_identity) INTO sequence_names\n    FROM pg_event_trigger_dropped_objects()\n    WHERE object_type = 'sequence';\n\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n                 WHERE object_type IN ('table', 'foreign table')\n    LOOP\n        -- first drop the table and metadata on the workers\n        -- then drop all the shards on the workers\n        -- finally remove the pg_dist_partition entry on the coordinator\n        PERFORM master_remove_distributed_table_metadata_from_workers(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n        PERFORM master_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n        PERFORM master_remove_partition_metadata(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n    END LOOP;\n\n    IF cardinality(sequence_names) > 0 THEN\n        PERFORM master_drop_sequences(sequence_names);\n    END IF;\n\n    -- remove entries from citus.pg_dist_object for all dropped root (objsubid = 0) objects\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n    LOOP\n        PERFORM master_unmark_object_distributed(v_obj.classid, v_obj.objid, v_obj.objsubid);\n    END LOOP;\nEND;\n$cdbdt$;\nCOMMENT ON FUNCTION pg_catalog.citus_drop_trigger()\n    IS 'perform checks and actions at the end of DROP actions';\n\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_drop_trigger/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_drop_trigger()\n    RETURNS event_trigger\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cdbdt$\nDECLARE\n    v_obj record;\n    sequence_names text[] := '{}';\n    table_colocation_id integer;\n    propagate_drop boolean := false;\nBEGIN\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n                 WHERE object_type IN ('table', 'foreign table')\n    LOOP\n        -- first drop the table and metadata on the workers\n        -- then drop all the shards on the workers\n        -- finally remove the pg_dist_partition entry on the coordinator\n        PERFORM master_remove_distributed_table_metadata_from_workers(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n        PERFORM master_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n        PERFORM master_remove_partition_metadata(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n    END LOOP;\n\n    -- remove entries from citus.pg_dist_object for all dropped root (objsubid = 0) objects\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n    LOOP\n        PERFORM master_unmark_object_distributed(v_obj.classid, v_obj.objid, v_obj.objsubid);\n    END LOOP;\nEND;\n$cdbdt$;\nCOMMENT ON FUNCTION pg_catalog.citus_drop_trigger()\n    IS 'perform checks and actions at the end of DROP actions';\n\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_drop_trigger/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_drop_trigger()\n    RETURNS event_trigger\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cdbdt$\nDECLARE\n    constraint_event_count INTEGER;\n    v_obj record;\n    dropped_table_is_a_partition boolean := false;\nBEGIN\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n                 WHERE object_type IN ('table', 'foreign table')\n    LOOP\n        -- first drop the table and metadata on the workers\n        -- then drop all the shards on the workers\n        -- finally remove the pg_dist_partition entry on the coordinator\n        PERFORM master_remove_distributed_table_metadata_from_workers(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n\n        -- If both original and normal values are false, the dropped table was a partition\n        -- that was dropped as a result of its parent being dropped\n        -- NOTE: the other way around is not true:\n        -- the table being a partition doesn't imply both original and normal values are false\n        SELECT (v_obj.original = false AND v_obj.normal = false) INTO dropped_table_is_a_partition;\n\n        -- The partition's shards will be dropped when dropping the parent's shards, so we can skip:\n        -- i.e. we call citus_drop_all_shards with drop_shards_metadata_only parameter set to true\n        IF dropped_table_is_a_partition\n        THEN\n            PERFORM citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := true);\n        ELSE\n            PERFORM citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false);\n        END IF;\n\n        PERFORM master_remove_partition_metadata(v_obj.objid, v_obj.schema_name, v_obj.object_name);\n    END LOOP;\n\n    FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects()\n    LOOP\n        -- Remove entries from pg_catalog.pg_dist_schema for all dropped tenant schemas.\n        -- Also delete the corresponding colocation group from pg_catalog.pg_dist_colocation.\n        --\n        -- Although normally we automatically delete the colocation groups when they become empty,\n        -- we don't do so for the colocation groups that are created for tenant schemas. For this\n        -- reason, here we need to delete the colocation group when the tenant schema is dropped.\n        IF v_obj.object_type = 'schema' AND EXISTS (SELECT 1 FROM pg_catalog.pg_dist_schema WHERE schemaid = v_obj.objid)\n        THEN\n            PERFORM citus_internal.unregister_tenant_schema_globally(v_obj.objid, v_obj.object_name);\n        END IF;\n\n        -- remove entries from citus.pg_dist_object for all dropped root (objsubid = 0) objects\n        PERFORM master_unmark_object_distributed(v_obj.classid, v_obj.objid, v_obj.objsubid);\n    END LOOP;\n\n    SELECT COUNT(*) INTO constraint_event_count\n    FROM pg_event_trigger_dropped_objects()\n    WHERE object_type IN ('table constraint');\n\n    IF constraint_event_count > 0\n    THEN\n        -- Tell utility hook that a table constraint is dropped so we might\n        -- need to undistribute some of the citus local tables that are not\n        -- connected to any reference tables.\n        PERFORM notify_constraint_dropped();\n    END IF;\nEND;\n$cdbdt$;\nCOMMENT ON FUNCTION pg_catalog.citus_drop_trigger()\n    IS 'perform checks and actions at the end of DROP actions';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_extradata_container/9.3-2.sql",
    "content": "-- we use the citus_extradata_container function as a range table entry in the query part\n-- executed on the coordinator. Now that we are letting this query be planned by the\n-- postgres planner we need to be able to pass column names and type information with this\n-- function. This requires the change of the prototype of the function and add a return\n-- type. Changing the return type of the function requires we drop the function first.\nDROP FUNCTION citus_extradata_container(INTERNAL);\nCREATE OR REPLACE FUNCTION citus_extradata_container(INTERNAL)\n    RETURNS SETOF record\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$citus_extradata_container$$;\nCOMMENT ON FUNCTION pg_catalog.citus_extradata_container(INTERNAL)\n    IS 'placeholder function to store additional data in postgres node trees';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_extradata_container/latest.sql",
    "content": "-- we use the citus_extradata_container function as a range table entry in the query part\n-- executed on the coordinator. Now that we are letting this query be planned by the\n-- postgres planner we need to be able to pass column names and type information with this\n-- function. This requires the change of the prototype of the function and add a return\n-- type. Changing the return type of the function requires we drop the function first.\nDROP FUNCTION citus_extradata_container(INTERNAL);\nCREATE OR REPLACE FUNCTION citus_extradata_container(INTERNAL)\n    RETURNS SETOF record\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$citus_extradata_container$$;\nCOMMENT ON FUNCTION pg_catalog.citus_extradata_container(INTERNAL)\n    IS 'placeholder function to store additional data in postgres node trees';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finalize_upgrade_to_citus11/11.0-1.sql",
    "content": "-- citus_finalize_upgrade_to_citus11() is a helper UDF ensures\n-- the upgrade to Citus 11 is finished successfully. Upgrade to\n-- Citus 11 requires all active primary worker nodes to get the\n-- metadata. And, this function's job is to sync the metadata to\n-- the nodes that does not already have\n-- once the function finishes without any errors and returns true\n-- the cluster is ready for running distributed queries from\n-- the worker nodes. When debug is enabled, the function provides\n-- more information to the user.\nCREATE OR REPLACE FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(enforce_version_check bool default true)\n  RETURNS bool\n  LANGUAGE plpgsql\n  AS $$\nBEGIN\n\n  ---------------------------------------------\n  -- This script consists of N stages\n  -- Each step is documented, and if log level\n  -- is reduced to DEBUG1, each step is logged\n  -- as well\n  ---------------------------------------------\n\n------------------------------------------------------------------------------------------\n  -- STAGE 0: Ensure no concurrent node metadata changing operation happens while this\n  -- script is running via acquiring a strong lock on the pg_dist_node\n------------------------------------------------------------------------------------------\nBEGIN\n  LOCK TABLE pg_dist_node IN EXCLUSIVE MODE NOWAIT;\n\n  EXCEPTION WHEN OTHERS THEN\n  RAISE 'Another node metadata changing operation is in progress, try again.';\nEND;\n\n------------------------------------------------------------------------------------------\n  -- STAGE 1: We want all the commands to run in the same transaction block. Without\n  -- sequential mode, metadata syncing cannot be done in a transaction block along with\n  -- other commands\n------------------------------------------------------------------------------------------\n  SET LOCAL citus.multi_shard_modify_mode TO 'sequential';\n\n------------------------------------------------------------------------------------------\n  -- STAGE 2: Ensure we have the prerequisites\n  -- (a) only superuser can run this script\n  -- (b) cannot be executed when enable_ddl_propagation is False\n  -- (c) can only be executed from the coordinator\n------------------------------------------------------------------------------------------\nDECLARE\n  is_superuser_running boolean := False;\n  enable_ddl_prop boolean:= False;\n  local_group_id int := 0;\nBEGIN\n      SELECT rolsuper INTO is_superuser_running FROM pg_roles WHERE rolname = current_user;\n      IF is_superuser_running IS NOT True THEN\n                RAISE EXCEPTION 'This operation can only be initiated by superuser';\n      END IF;\n\n      SELECT current_setting('citus.enable_ddl_propagation') INTO enable_ddl_prop;\n      IF enable_ddl_prop IS NOT True THEN\n                RAISE EXCEPTION 'This operation cannot be completed when citus.enable_ddl_propagation is False.';\n      END IF;\n\n      SELECT groupid INTO local_group_id FROM pg_dist_local_group;\n\n      IF local_group_id != 0 THEN\n                RAISE EXCEPTION 'Operation is not allowed on this node. Connect to the coordinator and run it again.';\n      ELSE\n                RAISE DEBUG 'We are on the coordinator, continue to sync metadata';\n      END IF;\nEND;\n\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 3: Ensure all primary nodes are active\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    primary_disabled_worker_node_count int := 0;\n  BEGIN\n        SELECT count(*) INTO primary_disabled_worker_node_count FROM pg_dist_node\n                WHERE groupid != 0 AND noderole = 'primary' AND NOT isactive;\n\n        IF primary_disabled_worker_node_count != 0 THEN\n                  RAISE EXCEPTION 'There are inactive primary worker nodes, you need to activate the nodes first.'\n                                  'Use SELECT citus_activate_node() to activate the disabled nodes';\n        ELSE\n                  RAISE DEBUG 'There are no disabled worker nodes, continue to sync metadata';\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 4: Ensure there is no connectivity issues in the cluster\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    all_nodes_can_connect_to_each_other boolean := False;\n  BEGIN\n       SELECT bool_and(coalesce(result, false)) INTO all_nodes_can_connect_to_each_other FROM citus_check_cluster_node_health();\n\n        IF all_nodes_can_connect_to_each_other != True THEN\n                  RAISE EXCEPTION 'There are unhealth primary nodes, you need to ensure all '\n                                  'nodes are up and running. Also, make sure that all nodes can connect '\n                                  'to each other. Use SELECT * FROM citus_check_cluster_node_health(); '\n                                  'to check the cluster health';\n        ELSE\n                  RAISE DEBUG 'Cluster is healthy, all nodes can connect to each other';\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 5: Ensure all nodes are on the same version\n  ------------------------------------------------------------------------------------------\n    DECLARE\n      coordinator_version text := '';\n      worker_node_version text := '';\n      worker_node_version_count int := 0;\n\n    BEGIN\n         SELECT extversion INTO coordinator_version from pg_extension WHERE extname = 'citus';\n\n         -- first, check if all nodes have the same versions\n          SELECT\n            count(distinct result) INTO worker_node_version_count\n          FROM\n            run_command_on_workers('SELECT extversion from pg_extension WHERE extname = ''citus''');\n          IF enforce_version_check AND worker_node_version_count != 1 THEN\n                    RAISE EXCEPTION 'All nodes should have the same Citus version installed. Currently '\n                                     'some of the workers have different versions.';\n          ELSE\n                    RAISE DEBUG 'All worker nodes have the same Citus version';\n          END IF;\n\n         -- second, check if all nodes have the same versions\n         SELECT\n            result INTO worker_node_version\n         FROM\n            run_command_on_workers('SELECT extversion from pg_extension WHERE extname = ''citus'';')\n          GROUP BY result;\n\n          IF enforce_version_check AND coordinator_version != worker_node_version THEN\n                    RAISE EXCEPTION 'All nodes should have the same Citus version installed. Currently '\n                                     'the coordinator has version % and the worker(s) has %',\n                                     coordinator_version, worker_node_version;\n          ELSE\n                    RAISE DEBUG 'All nodes have the same Citus version';\n          END IF;\n    END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 6: Ensure all the partitioned tables have the proper naming structure\n    -- As described on https://github.com/citusdata/citus/issues/4962\n    -- existing indexes on partitioned distributed tables can collide\n    -- with the index names exists on the shards\n    -- luckily, we know how to fix it.\n    -- And, note that we should do this even if the cluster is a basic plan\n    -- (e.g., single node Citus) such that when cluster scaled out, everything\n    -- works as intended\n    -- And, this should be done only ONCE for a cluster as it can be a pretty\n    -- time consuming operation. Thus, even if the function is called multiple time,\n    -- we keep track of it and do not re-execute this part if not needed.\n  ------------------------------------------------------------------------------------------\n  DECLARE\n      partitioned_table_exists_pre_11 boolean:=False;\n  BEGIN\n\n    -- we recorded if partitioned tables exists during upgrade to Citus 11\n    SELECT metadata->>'partitioned_citus_table_exists_pre_11' INTO partitioned_table_exists_pre_11\n    FROM pg_dist_node_metadata;\n\n    IF partitioned_table_exists_pre_11 IS NOT NULL AND partitioned_table_exists_pre_11 THEN\n\n      -- this might take long depending on the number of partitions and shards...\n      RAISE NOTICE 'Preparing all the existing partitioned table indexes';\n      PERFORM pg_catalog.fix_all_partition_shard_index_names();\n\n      -- great, we are done with fixing the existing wrong index names\n      -- so, lets remove this\n      UPDATE pg_dist_node_metadata\n      SET metadata=jsonb_delete(metadata, 'partitioned_citus_table_exists_pre_11');\n    ELSE\n        RAISE DEBUG 'There are no partitioned tables that should be fixed';\n    END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n  -- STAGE 7: Return early if there are no primary worker nodes\n  -- We don't strictly need this step, but it gives a nicer notice message\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    primary_worker_node_count bigint :=0;\n  BEGIN\n        SELECT count(*) INTO primary_worker_node_count FROM pg_dist_node WHERE groupid != 0 AND noderole = 'primary';\n\n        IF primary_worker_node_count = 0 THEN\n                  RAISE NOTICE 'There are no primary worker nodes, no need to sync metadata to any node';\n                  RETURN true;\n        ELSE\n                  RAISE DEBUG 'There are % primary worker nodes, continue to sync metadata', primary_worker_node_count;\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n  -- STAGE 8: Do the actual metadata & object syncing to the worker nodes\n  -- For the \"already synced\" metadata nodes, we do not strictly need to\n  -- sync the objects & metadata, but there is no harm to do it anyway\n  -- it'll only cost some execution time but makes sure that we have a\n  -- a consistent metadata & objects across all the nodes\n  ------------------------------------------------------------------------------------------\n  DECLARE\n  BEGIN\n\n    -- this might take long depending on the number of tables & objects ...\n    RAISE NOTICE 'Preparing to sync the metadata to all nodes';\n\n    PERFORM start_metadata_sync_to_node(nodename,nodeport)\n    FROM\n      pg_dist_node WHERE groupid != 0 AND noderole = 'primary';\n  END;\n\n  RETURN true;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(bool)\n  IS 'finalizes upgrade to Citus';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(bool) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finalize_upgrade_to_citus11/11.0-2.sql",
    "content": "-- citus_finalize_upgrade_to_citus11() is a helper UDF ensures\n-- the upgrade to Citus 11 is finished successfully. Upgrade to\n-- Citus 11 requires all active primary worker nodes to get the\n-- metadata. And, this function's job is to sync the metadata to\n-- the nodes that does not already have\n-- once the function finishes without any errors and returns true\n-- the cluster is ready for running distributed queries from\n-- the worker nodes. When debug is enabled, the function provides\n-- more information to the user.\nCREATE OR REPLACE FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(enforce_version_check bool default true)\n  RETURNS bool\n  LANGUAGE plpgsql\n  AS $$\nBEGIN\n\n  ---------------------------------------------\n  -- This script consists of N stages\n  -- Each step is documented, and if log level\n  -- is reduced to DEBUG1, each step is logged\n  -- as well\n  ---------------------------------------------\n\n------------------------------------------------------------------------------------------\n  -- STAGE 0: Ensure no concurrent node metadata changing operation happens while this\n  -- script is running via acquiring a strong lock on the pg_dist_node\n------------------------------------------------------------------------------------------\nBEGIN\n  LOCK TABLE pg_dist_node IN EXCLUSIVE MODE NOWAIT;\n\n  EXCEPTION WHEN OTHERS THEN\n  RAISE 'Another node metadata changing operation is in progress, try again.';\nEND;\n\n------------------------------------------------------------------------------------------\n  -- STAGE 1: We want all the commands to run in the same transaction block. Without\n  -- sequential mode, metadata syncing cannot be done in a transaction block along with\n  -- other commands\n------------------------------------------------------------------------------------------\n  SET LOCAL citus.multi_shard_modify_mode TO 'sequential';\n\n------------------------------------------------------------------------------------------\n  -- STAGE 2: Ensure we have the prerequisites\n  -- (a) only superuser can run this script\n  -- (b) cannot be executed when enable_ddl_propagation is False\n  -- (c) can only be executed from the coordinator\n------------------------------------------------------------------------------------------\nDECLARE\n  is_superuser_running boolean := False;\n  enable_ddl_prop boolean:= False;\n  local_group_id int := 0;\nBEGIN\n      SELECT rolsuper INTO is_superuser_running FROM pg_roles WHERE rolname = current_user;\n      IF is_superuser_running IS NOT True THEN\n                RAISE EXCEPTION 'This operation can only be initiated by superuser';\n      END IF;\n\n      SELECT current_setting('citus.enable_ddl_propagation') INTO enable_ddl_prop;\n      IF enable_ddl_prop IS NOT True THEN\n                RAISE EXCEPTION 'This operation cannot be completed when citus.enable_ddl_propagation is False.';\n      END IF;\n\n      SELECT groupid INTO local_group_id FROM pg_dist_local_group;\n\n      IF local_group_id != 0 THEN\n                RAISE EXCEPTION 'Operation is not allowed on this node. Connect to the coordinator and run it again.';\n      ELSE\n                RAISE DEBUG 'We are on the coordinator, continue to sync metadata';\n      END IF;\nEND;\n\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 3: Ensure all primary nodes are active\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    primary_disabled_worker_node_count int := 0;\n  BEGIN\n        SELECT count(*) INTO primary_disabled_worker_node_count FROM pg_dist_node\n                WHERE groupid != 0 AND noderole = 'primary' AND NOT isactive;\n\n        IF primary_disabled_worker_node_count != 0 THEN\n                  RAISE EXCEPTION 'There are inactive primary worker nodes, you need to activate the nodes first.'\n                                  'Use SELECT citus_activate_node() to activate the disabled nodes';\n        ELSE\n                  RAISE DEBUG 'There are no disabled worker nodes, continue to sync metadata';\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 4: Ensure there is no connectivity issues in the cluster\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    all_nodes_can_connect_to_each_other boolean := False;\n  BEGIN\n       SELECT bool_and(coalesce(result, false)) INTO all_nodes_can_connect_to_each_other FROM citus_check_cluster_node_health();\n\n        IF all_nodes_can_connect_to_each_other != True THEN\n                  RAISE EXCEPTION 'There are unhealth primary nodes, you need to ensure all '\n                                  'nodes are up and running. Also, make sure that all nodes can connect '\n                                  'to each other. Use SELECT * FROM citus_check_cluster_node_health(); '\n                                  'to check the cluster health';\n        ELSE\n                  RAISE DEBUG 'Cluster is healthy, all nodes can connect to each other';\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 5: Ensure all nodes are on the same version\n  ------------------------------------------------------------------------------------------\n    DECLARE\n      coordinator_version text := '';\n      worker_node_version text := '';\n      worker_node_version_count int := 0;\n\n    BEGIN\n         SELECT extversion INTO coordinator_version from pg_extension WHERE extname = 'citus';\n\n         -- first, check if all nodes have the same versions\n          SELECT\n            count(distinct result) INTO worker_node_version_count\n          FROM\n            run_command_on_workers('SELECT extversion from pg_extension WHERE extname = ''citus''');\n          IF enforce_version_check AND worker_node_version_count != 1 THEN\n                    RAISE EXCEPTION 'All nodes should have the same Citus version installed. Currently '\n                                     'some of the workers have different versions.';\n          ELSE\n                    RAISE DEBUG 'All worker nodes have the same Citus version';\n          END IF;\n\n         -- second, check if all nodes have the same versions\n         SELECT\n            result INTO worker_node_version\n         FROM\n            run_command_on_workers('SELECT extversion from pg_extension WHERE extname = ''citus'';')\n          GROUP BY result;\n\n          IF enforce_version_check AND coordinator_version != worker_node_version THEN\n                    RAISE EXCEPTION 'All nodes should have the same Citus version installed. Currently '\n                                     'the coordinator has version % and the worker(s) has %',\n                                     coordinator_version, worker_node_version;\n          ELSE\n                    RAISE DEBUG 'All nodes have the same Citus version';\n          END IF;\n    END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 6: Ensure all the partitioned tables have the proper naming structure\n    -- As described on https://github.com/citusdata/citus/issues/4962\n    -- existing indexes on partitioned distributed tables can collide\n    -- with the index names exists on the shards\n    -- luckily, we know how to fix it.\n    -- And, note that we should do this even if the cluster is a basic plan\n    -- (e.g., single node Citus) such that when cluster scaled out, everything\n    -- works as intended\n    -- And, this should be done only ONCE for a cluster as it can be a pretty\n    -- time consuming operation. Thus, even if the function is called multiple time,\n    -- we keep track of it and do not re-execute this part if not needed.\n  ------------------------------------------------------------------------------------------\n  DECLARE\n      partitioned_table_exists_pre_11 boolean:=False;\n  BEGIN\n\n    -- we recorded if partitioned tables exists during upgrade to Citus 11\n    SELECT metadata->>'partitioned_citus_table_exists_pre_11' INTO partitioned_table_exists_pre_11\n    FROM pg_dist_node_metadata;\n\n    IF partitioned_table_exists_pre_11 IS NOT NULL AND partitioned_table_exists_pre_11 THEN\n\n      -- this might take long depending on the number of partitions and shards...\n      RAISE NOTICE 'Preparing all the existing partitioned table indexes';\n      PERFORM pg_catalog.fix_all_partition_shard_index_names();\n\n      -- great, we are done with fixing the existing wrong index names\n      -- so, lets remove this\n      UPDATE pg_dist_node_metadata\n      SET metadata=jsonb_delete(metadata, 'partitioned_citus_table_exists_pre_11');\n    ELSE\n        RAISE DEBUG 'There are no partitioned tables that should be fixed';\n    END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n  -- STAGE 7: Return early if there are no primary worker nodes\n  -- We don't strictly need this step, but it gives a nicer notice message\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    primary_worker_node_count bigint :=0;\n  BEGIN\n        SELECT count(*) INTO primary_worker_node_count FROM pg_dist_node WHERE groupid != 0 AND noderole = 'primary';\n\n        IF primary_worker_node_count = 0 THEN\n                  RAISE NOTICE 'There are no primary worker nodes, no need to sync metadata to any node';\n                  RETURN true;\n        ELSE\n                  RAISE DEBUG 'There are % primary worker nodes, continue to sync metadata', primary_worker_node_count;\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n  -- STAGE 8: Do the actual metadata & object syncing to the worker nodes\n  -- For the \"already synced\" metadata nodes, we do not strictly need to\n  -- sync the objects & metadata, but there is no harm to do it anyway\n  -- it'll only cost some execution time but makes sure that we have a\n  -- a consistent metadata & objects across all the nodes\n  ------------------------------------------------------------------------------------------\n  DECLARE\n  BEGIN\n\n    -- this might take long depending on the number of tables & objects ...\n    RAISE NOTICE 'Preparing to sync the metadata to all nodes';\n\n    PERFORM start_metadata_sync_to_all_nodes();\n  END;\n\n  RETURN true;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(bool)\n  IS 'finalizes upgrade to Citus';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(bool) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finalize_upgrade_to_citus11/11.0-3.sql",
    "content": "-- citus_finalize_upgrade_to_citus11() is a helper UDF ensures\n-- the upgrade to Citus 11 is finished successfully. Upgrade to\n-- Citus 11 requires all active primary worker nodes to get the\n-- metadata. And, this function's job is to sync the metadata to\n-- the nodes that does not already have\n-- once the function finishes without any errors and returns true\n-- the cluster is ready for running distributed queries from\n-- the worker nodes. When debug is enabled, the function provides\n-- more information to the user.\nCREATE OR REPLACE FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(enforce_version_check bool default true)\n  RETURNS bool\n  LANGUAGE plpgsql\n  AS $$\nBEGIN\n\n  ---------------------------------------------\n  -- This script consists of N stages\n  -- Each step is documented, and if log level\n  -- is reduced to DEBUG1, each step is logged\n  -- as well\n  ---------------------------------------------\n\n------------------------------------------------------------------------------------------\n  -- STAGE 0: Ensure no concurrent node metadata changing operation happens while this\n  -- script is running via acquiring a strong lock on the pg_dist_node\n------------------------------------------------------------------------------------------\nBEGIN\n  LOCK TABLE pg_dist_node IN EXCLUSIVE MODE NOWAIT;\n\n  EXCEPTION WHEN OTHERS THEN\n  RAISE 'Another node metadata changing operation is in progress, try again.';\nEND;\n\n------------------------------------------------------------------------------------------\n  -- STAGE 1: We want all the commands to run in the same transaction block. Without\n  -- sequential mode, metadata syncing cannot be done in a transaction block along with\n  -- other commands\n------------------------------------------------------------------------------------------\n  SET LOCAL citus.multi_shard_modify_mode TO 'sequential';\n\n------------------------------------------------------------------------------------------\n  -- STAGE 2: Ensure we have the prerequisites\n  -- (a) only superuser can run this script\n  -- (b) cannot be executed when enable_ddl_propagation is False\n  -- (c) can only be executed from the coordinator\n------------------------------------------------------------------------------------------\nDECLARE\n  is_superuser_running boolean := False;\n  enable_ddl_prop boolean:= False;\n  local_group_id int := 0;\nBEGIN\n      SELECT rolsuper INTO is_superuser_running FROM pg_roles WHERE rolname = current_user;\n      IF is_superuser_running IS NOT True THEN\n                RAISE EXCEPTION 'This operation can only be initiated by superuser';\n      END IF;\n\n      SELECT current_setting('citus.enable_ddl_propagation') INTO enable_ddl_prop;\n      IF enable_ddl_prop IS NOT True THEN\n                RAISE EXCEPTION 'This operation cannot be completed when citus.enable_ddl_propagation is False.';\n      END IF;\n\n      SELECT groupid INTO local_group_id FROM pg_dist_local_group;\n\n      IF local_group_id != 0 THEN\n                RAISE EXCEPTION 'Operation is not allowed on this node. Connect to the coordinator and run it again.';\n      ELSE\n                RAISE DEBUG 'We are on the coordinator, continue to sync metadata';\n      END IF;\nEND;\n\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 3: Ensure all primary nodes are active\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    primary_disabled_worker_node_count int := 0;\n  BEGIN\n        SELECT count(*) INTO primary_disabled_worker_node_count FROM pg_dist_node\n                WHERE groupid != 0 AND noderole = 'primary' AND NOT isactive;\n\n        IF primary_disabled_worker_node_count != 0 THEN\n                  RAISE EXCEPTION 'There are inactive primary worker nodes, you need to activate the nodes first.'\n                                  'Use SELECT citus_activate_node() to activate the disabled nodes';\n        ELSE\n                  RAISE DEBUG 'There are no disabled worker nodes, continue to sync metadata';\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 4: Ensure there is no connectivity issues in the cluster\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    all_nodes_can_connect_to_each_other boolean := False;\n  BEGIN\n       SELECT bool_and(coalesce(result, false)) INTO all_nodes_can_connect_to_each_other FROM citus_check_cluster_node_health();\n\n        IF all_nodes_can_connect_to_each_other != True THEN\n                  RAISE EXCEPTION 'There are unhealth primary nodes, you need to ensure all '\n                                  'nodes are up and running. Also, make sure that all nodes can connect '\n                                  'to each other. Use SELECT * FROM citus_check_cluster_node_health(); '\n                                  'to check the cluster health';\n        ELSE\n                  RAISE DEBUG 'Cluster is healthy, all nodes can connect to each other';\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 5: Ensure all nodes are on the same version\n  ------------------------------------------------------------------------------------------\n    DECLARE\n      coordinator_version text := '';\n      worker_node_version text := '';\n      worker_node_version_count int := 0;\n\n    BEGIN\n         SELECT extversion INTO coordinator_version from pg_extension WHERE extname = 'citus';\n\n         -- first, check if all nodes have the same versions\n          SELECT\n            count(distinct result) INTO worker_node_version_count\n          FROM\n            run_command_on_workers('SELECT extversion from pg_extension WHERE extname = ''citus''');\n\n          IF enforce_version_check AND worker_node_version_count = 0 THEN\n      RAISE DEBUG 'There are no worker nodes';\n          ELSIF enforce_version_check AND worker_node_version_count != 1 THEN\n                    RAISE EXCEPTION 'All nodes should have the same Citus version installed. Currently '\n                                     'some of the workers have different versions.';\n          ELSE\n                    RAISE DEBUG 'All worker nodes have the same Citus version';\n          END IF;\n\n         -- second, check if all nodes have the same versions\n         SELECT\n            result INTO worker_node_version\n         FROM\n            run_command_on_workers('SELECT extversion from pg_extension WHERE extname = ''citus'';')\n          GROUP BY result;\n\n          IF enforce_version_check AND coordinator_version != worker_node_version THEN\n                    RAISE EXCEPTION 'All nodes should have the same Citus version installed. Currently '\n                                     'the coordinator has version % and the worker(s) has %',\n                                     coordinator_version, worker_node_version;\n          ELSE\n                    RAISE DEBUG 'All nodes have the same Citus version';\n          END IF;\n    END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 6: Ensure all the partitioned tables have the proper naming structure\n    -- As described on https://github.com/citusdata/citus/issues/4962\n    -- existing indexes on partitioned distributed tables can collide\n    -- with the index names exists on the shards\n    -- luckily, we know how to fix it.\n    -- And, note that we should do this even if the cluster is a basic plan\n    -- (e.g., single node Citus) such that when cluster scaled out, everything\n    -- works as intended\n    -- And, this should be done only ONCE for a cluster as it can be a pretty\n    -- time consuming operation. Thus, even if the function is called multiple time,\n    -- we keep track of it and do not re-execute this part if not needed.\n  ------------------------------------------------------------------------------------------\n  DECLARE\n      partitioned_table_exists_pre_11 boolean:=False;\n  BEGIN\n\n    -- we recorded if partitioned tables exists during upgrade to Citus 11\n    SELECT metadata->>'partitioned_citus_table_exists_pre_11' INTO partitioned_table_exists_pre_11\n    FROM pg_dist_node_metadata;\n\n    IF partitioned_table_exists_pre_11 IS NOT NULL AND partitioned_table_exists_pre_11 THEN\n\n      -- this might take long depending on the number of partitions and shards...\n      RAISE NOTICE 'Preparing all the existing partitioned table indexes';\n      PERFORM pg_catalog.fix_all_partition_shard_index_names();\n\n      -- great, we are done with fixing the existing wrong index names\n      -- so, lets remove this\n      UPDATE pg_dist_node_metadata\n      SET metadata=jsonb_delete(metadata, 'partitioned_citus_table_exists_pre_11');\n    ELSE\n        RAISE DEBUG 'There are no partitioned tables that should be fixed';\n    END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n  -- STAGE 7: Return early if there are no primary worker nodes\n  -- We don't strictly need this step, but it gives a nicer notice message\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    primary_worker_node_count bigint :=0;\n  BEGIN\n        SELECT count(*) INTO primary_worker_node_count FROM pg_dist_node WHERE groupid != 0 AND noderole = 'primary';\n\n        IF primary_worker_node_count = 0 THEN\n                  RAISE NOTICE 'There are no primary worker nodes, no need to sync metadata to any node';\n                  RETURN true;\n        ELSE\n                  RAISE DEBUG 'There are % primary worker nodes, continue to sync metadata', primary_worker_node_count;\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n  -- STAGE 8: Do the actual metadata & object syncing to the worker nodes\n  -- For the \"already synced\" metadata nodes, we do not strictly need to\n  -- sync the objects & metadata, but there is no harm to do it anyway\n  -- it'll only cost some execution time but makes sure that we have a\n  -- a consistent metadata & objects across all the nodes\n  ------------------------------------------------------------------------------------------\n  DECLARE\n  BEGIN\n\n    -- this might take long depending on the number of tables & objects ...\n    RAISE NOTICE 'Preparing to sync the metadata to all nodes';\n\n    PERFORM start_metadata_sync_to_all_nodes();\n  END;\n\n  RETURN true;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(bool)\n  IS 'finalizes upgrade to Citus';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(bool) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finalize_upgrade_to_citus11/latest.sql",
    "content": "-- citus_finalize_upgrade_to_citus11() is a helper UDF ensures\n-- the upgrade to Citus 11 is finished successfully. Upgrade to\n-- Citus 11 requires all active primary worker nodes to get the\n-- metadata. And, this function's job is to sync the metadata to\n-- the nodes that does not already have\n-- once the function finishes without any errors and returns true\n-- the cluster is ready for running distributed queries from\n-- the worker nodes. When debug is enabled, the function provides\n-- more information to the user.\nCREATE OR REPLACE FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(enforce_version_check bool default true)\n  RETURNS bool\n  LANGUAGE plpgsql\n  AS $$\nBEGIN\n\n  ---------------------------------------------\n  -- This script consists of N stages\n  -- Each step is documented, and if log level\n  -- is reduced to DEBUG1, each step is logged\n  -- as well\n  ---------------------------------------------\n\n------------------------------------------------------------------------------------------\n  -- STAGE 0: Ensure no concurrent node metadata changing operation happens while this\n  -- script is running via acquiring a strong lock on the pg_dist_node\n------------------------------------------------------------------------------------------\nBEGIN\n  LOCK TABLE pg_dist_node IN EXCLUSIVE MODE NOWAIT;\n\n  EXCEPTION WHEN OTHERS THEN\n  RAISE 'Another node metadata changing operation is in progress, try again.';\nEND;\n\n------------------------------------------------------------------------------------------\n  -- STAGE 1: We want all the commands to run in the same transaction block. Without\n  -- sequential mode, metadata syncing cannot be done in a transaction block along with\n  -- other commands\n------------------------------------------------------------------------------------------\n  SET LOCAL citus.multi_shard_modify_mode TO 'sequential';\n\n------------------------------------------------------------------------------------------\n  -- STAGE 2: Ensure we have the prerequisites\n  -- (a) only superuser can run this script\n  -- (b) cannot be executed when enable_ddl_propagation is False\n  -- (c) can only be executed from the coordinator\n------------------------------------------------------------------------------------------\nDECLARE\n  is_superuser_running boolean := False;\n  enable_ddl_prop boolean:= False;\n  local_group_id int := 0;\nBEGIN\n      SELECT rolsuper INTO is_superuser_running FROM pg_roles WHERE rolname = current_user;\n      IF is_superuser_running IS NOT True THEN\n                RAISE EXCEPTION 'This operation can only be initiated by superuser';\n      END IF;\n\n      SELECT current_setting('citus.enable_ddl_propagation') INTO enable_ddl_prop;\n      IF enable_ddl_prop IS NOT True THEN\n                RAISE EXCEPTION 'This operation cannot be completed when citus.enable_ddl_propagation is False.';\n      END IF;\n\n      SELECT groupid INTO local_group_id FROM pg_dist_local_group;\n\n      IF local_group_id != 0 THEN\n                RAISE EXCEPTION 'Operation is not allowed on this node. Connect to the coordinator and run it again.';\n      ELSE\n                RAISE DEBUG 'We are on the coordinator, continue to sync metadata';\n      END IF;\nEND;\n\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 3: Ensure all primary nodes are active\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    primary_disabled_worker_node_count int := 0;\n  BEGIN\n        SELECT count(*) INTO primary_disabled_worker_node_count FROM pg_dist_node\n                WHERE groupid != 0 AND noderole = 'primary' AND NOT isactive;\n\n        IF primary_disabled_worker_node_count != 0 THEN\n                  RAISE EXCEPTION 'There are inactive primary worker nodes, you need to activate the nodes first.'\n                                  'Use SELECT citus_activate_node() to activate the disabled nodes';\n        ELSE\n                  RAISE DEBUG 'There are no disabled worker nodes, continue to sync metadata';\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 4: Ensure there is no connectivity issues in the cluster\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    all_nodes_can_connect_to_each_other boolean := False;\n  BEGIN\n       SELECT bool_and(coalesce(result, false)) INTO all_nodes_can_connect_to_each_other FROM citus_check_cluster_node_health();\n\n        IF all_nodes_can_connect_to_each_other != True THEN\n                  RAISE EXCEPTION 'There are unhealth primary nodes, you need to ensure all '\n                                  'nodes are up and running. Also, make sure that all nodes can connect '\n                                  'to each other. Use SELECT * FROM citus_check_cluster_node_health(); '\n                                  'to check the cluster health';\n        ELSE\n                  RAISE DEBUG 'Cluster is healthy, all nodes can connect to each other';\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 5: Ensure all nodes are on the same version\n  ------------------------------------------------------------------------------------------\n    DECLARE\n      coordinator_version text := '';\n      worker_node_version text := '';\n      worker_node_version_count int := 0;\n\n    BEGIN\n         SELECT extversion INTO coordinator_version from pg_extension WHERE extname = 'citus';\n\n         -- first, check if all nodes have the same versions\n          SELECT\n            count(distinct result) INTO worker_node_version_count\n          FROM\n            run_command_on_workers('SELECT extversion from pg_extension WHERE extname = ''citus''');\n\n          IF enforce_version_check AND worker_node_version_count = 0 THEN\n      RAISE DEBUG 'There are no worker nodes';\n          ELSIF enforce_version_check AND worker_node_version_count != 1 THEN\n                    RAISE EXCEPTION 'All nodes should have the same Citus version installed. Currently '\n                                     'some of the workers have different versions.';\n          ELSE\n                    RAISE DEBUG 'All worker nodes have the same Citus version';\n          END IF;\n\n         -- second, check if all nodes have the same versions\n         SELECT\n            result INTO worker_node_version\n         FROM\n            run_command_on_workers('SELECT extversion from pg_extension WHERE extname = ''citus'';')\n          GROUP BY result;\n\n          IF enforce_version_check AND coordinator_version != worker_node_version THEN\n                    RAISE EXCEPTION 'All nodes should have the same Citus version installed. Currently '\n                                     'the coordinator has version % and the worker(s) has %',\n                                     coordinator_version, worker_node_version;\n          ELSE\n                    RAISE DEBUG 'All nodes have the same Citus version';\n          END IF;\n    END;\n\n  ------------------------------------------------------------------------------------------\n    -- STAGE 6: Ensure all the partitioned tables have the proper naming structure\n    -- As described on https://github.com/citusdata/citus/issues/4962\n    -- existing indexes on partitioned distributed tables can collide\n    -- with the index names exists on the shards\n    -- luckily, we know how to fix it.\n    -- And, note that we should do this even if the cluster is a basic plan\n    -- (e.g., single node Citus) such that when cluster scaled out, everything\n    -- works as intended\n    -- And, this should be done only ONCE for a cluster as it can be a pretty\n    -- time consuming operation. Thus, even if the function is called multiple time,\n    -- we keep track of it and do not re-execute this part if not needed.\n  ------------------------------------------------------------------------------------------\n  DECLARE\n      partitioned_table_exists_pre_11 boolean:=False;\n  BEGIN\n\n    -- we recorded if partitioned tables exists during upgrade to Citus 11\n    SELECT metadata->>'partitioned_citus_table_exists_pre_11' INTO partitioned_table_exists_pre_11\n    FROM pg_dist_node_metadata;\n\n    IF partitioned_table_exists_pre_11 IS NOT NULL AND partitioned_table_exists_pre_11 THEN\n\n      -- this might take long depending on the number of partitions and shards...\n      RAISE NOTICE 'Preparing all the existing partitioned table indexes';\n      PERFORM pg_catalog.fix_all_partition_shard_index_names();\n\n      -- great, we are done with fixing the existing wrong index names\n      -- so, lets remove this\n      UPDATE pg_dist_node_metadata\n      SET metadata=jsonb_delete(metadata, 'partitioned_citus_table_exists_pre_11');\n    ELSE\n        RAISE DEBUG 'There are no partitioned tables that should be fixed';\n    END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n  -- STAGE 7: Return early if there are no primary worker nodes\n  -- We don't strictly need this step, but it gives a nicer notice message\n  ------------------------------------------------------------------------------------------\n  DECLARE\n    primary_worker_node_count bigint :=0;\n  BEGIN\n        SELECT count(*) INTO primary_worker_node_count FROM pg_dist_node WHERE groupid != 0 AND noderole = 'primary';\n\n        IF primary_worker_node_count = 0 THEN\n                  RAISE NOTICE 'There are no primary worker nodes, no need to sync metadata to any node';\n                  RETURN true;\n        ELSE\n                  RAISE DEBUG 'There are % primary worker nodes, continue to sync metadata', primary_worker_node_count;\n        END IF;\n  END;\n\n  ------------------------------------------------------------------------------------------\n  -- STAGE 8: Do the actual metadata & object syncing to the worker nodes\n  -- For the \"already synced\" metadata nodes, we do not strictly need to\n  -- sync the objects & metadata, but there is no harm to do it anyway\n  -- it'll only cost some execution time but makes sure that we have a\n  -- a consistent metadata & objects across all the nodes\n  ------------------------------------------------------------------------------------------\n  DECLARE\n  BEGIN\n\n    -- this might take long depending on the number of tables & objects ...\n    RAISE NOTICE 'Preparing to sync the metadata to all nodes';\n\n    PERFORM start_metadata_sync_to_all_nodes();\n  END;\n\n  RETURN true;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(bool)\n  IS 'finalizes upgrade to Citus';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_finalize_upgrade_to_citus11(bool) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_citus_upgrade/11.0-2.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.citus_finish_citus_upgrade()\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    current_version_string text;\n    last_upgrade_version_string text;\n    last_upgrade_major_version int;\n    last_upgrade_minor_version int;\n    last_upgrade_sqlpatch_version int;\n    performed_upgrade bool := false;\nBEGIN\n\tSELECT extversion INTO current_version_string\n\tFROM pg_extension WHERE extname = 'citus';\n\n\t-- assume some arbitrarily old version when no last upgrade version is defined\n\tSELECT coalesce(metadata->>'last_upgrade_version', '8.0-1') INTO last_upgrade_version_string\n\tFROM pg_dist_node_metadata;\n\n\tSELECT r[1], r[2], r[3]\n\tFROM regexp_matches(last_upgrade_version_string,'([0-9]+)\\.([0-9]+)-([0-9]+)','') r\n\tINTO last_upgrade_major_version, last_upgrade_minor_version, last_upgrade_sqlpatch_version;\n\n\tIF last_upgrade_major_version IS NULL OR last_upgrade_minor_version IS NULL OR last_upgrade_sqlpatch_version IS NULL THEN\n\t\t-- version string is not valid, use an arbitrarily old version number\n\t\tlast_upgrade_major_version := 8;\n\t\tlast_upgrade_minor_version := 0;\n\t\tlast_upgrade_sqlpatch_version := 1;\n\tEND IF;\n\n\tIF last_upgrade_major_version < 11 THEN\n\t\tPERFORM citus_finalize_upgrade_to_citus11();\n\t\tperformed_upgrade := true;\n\tEND IF;\n\n\t-- add new upgrade steps here\n\n\tIF NOT performed_upgrade THEN\n\t\tRAISE NOTICE 'already at the latest distributed schema version (%)', last_upgrade_version_string;\n\t\tRETURN;\n\tEND IF;\n\n\tUPDATE pg_dist_node_metadata\n\tSET metadata = jsonb_set(metadata, array['last_upgrade_version'], to_jsonb(current_version_string));\nEND;\n$cppu$;\n\nCOMMENT ON PROCEDURE pg_catalog.citus_finish_citus_upgrade()\n    IS 'after upgrading Citus on all nodes call this function to upgrade the distributed schema';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_citus_upgrade/14.0-1.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.citus_finish_citus_upgrade()\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    current_version_string text;\n    last_upgrade_version_string text;\n    last_upgrade_major_version int;\n    last_upgrade_minor_version int;\n    last_upgrade_sqlpatch_version int;\n    performed_upgrade bool := false;\nBEGIN\n\tSELECT extversion INTO current_version_string\n\tFROM pg_extension WHERE extname = 'citus';\n\n\t-- assume some arbitrarily old version when no last upgrade version is defined\n\tSELECT coalesce(metadata->>'last_upgrade_version', '8.0-1') INTO last_upgrade_version_string\n\tFROM pg_dist_node_metadata;\n\n\tSELECT r[1], r[2], r[3]\n\tFROM regexp_matches(last_upgrade_version_string,'([0-9]+)\\.([0-9]+)-([0-9]+)','') r\n\tINTO last_upgrade_major_version, last_upgrade_minor_version, last_upgrade_sqlpatch_version;\n\n\tIF last_upgrade_major_version IS NULL OR last_upgrade_minor_version IS NULL OR last_upgrade_sqlpatch_version IS NULL THEN\n\t\t-- version string is not valid, use an arbitrarily old version number\n\t\tlast_upgrade_major_version := 8;\n\t\tlast_upgrade_minor_version := 0;\n\t\tlast_upgrade_sqlpatch_version := 1;\n\tEND IF;\n\n\tIF last_upgrade_major_version < 11 THEN\n\t\tPERFORM citus_finalize_upgrade_to_citus11();\n\t\tperformed_upgrade := true;\n\tEND IF;\n\n\tIF last_upgrade_major_version < 14 THEN\n\t\tPERFORM fix_pre_citus14_colocation_group_collation_mismatches();\n\t\tperformed_upgrade := true;\n\tEND IF;\n\n\t-- add new upgrade steps here\n\n\tIF NOT performed_upgrade THEN\n\t\tRAISE NOTICE 'already at the latest distributed schema version (%)', last_upgrade_version_string;\n\t\tRETURN;\n\tEND IF;\n\n\tUPDATE pg_dist_node_metadata\n\tSET metadata = jsonb_set(metadata, array['last_upgrade_version'], to_jsonb(current_version_string));\nEND;\n$cppu$;\n\nCOMMENT ON PROCEDURE pg_catalog.citus_finish_citus_upgrade()\n    IS 'after upgrading Citus on all nodes call this function to upgrade the distributed schema';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_citus_upgrade/latest.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.citus_finish_citus_upgrade()\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    current_version_string text;\n    last_upgrade_version_string text;\n    last_upgrade_major_version int;\n    last_upgrade_minor_version int;\n    last_upgrade_sqlpatch_version int;\n    performed_upgrade bool := false;\nBEGIN\n\tSELECT extversion INTO current_version_string\n\tFROM pg_extension WHERE extname = 'citus';\n\n\t-- assume some arbitrarily old version when no last upgrade version is defined\n\tSELECT coalesce(metadata->>'last_upgrade_version', '8.0-1') INTO last_upgrade_version_string\n\tFROM pg_dist_node_metadata;\n\n\tSELECT r[1], r[2], r[3]\n\tFROM regexp_matches(last_upgrade_version_string,'([0-9]+)\\.([0-9]+)-([0-9]+)','') r\n\tINTO last_upgrade_major_version, last_upgrade_minor_version, last_upgrade_sqlpatch_version;\n\n\tIF last_upgrade_major_version IS NULL OR last_upgrade_minor_version IS NULL OR last_upgrade_sqlpatch_version IS NULL THEN\n\t\t-- version string is not valid, use an arbitrarily old version number\n\t\tlast_upgrade_major_version := 8;\n\t\tlast_upgrade_minor_version := 0;\n\t\tlast_upgrade_sqlpatch_version := 1;\n\tEND IF;\n\n\tIF last_upgrade_major_version < 11 THEN\n\t\tPERFORM citus_finalize_upgrade_to_citus11();\n\t\tperformed_upgrade := true;\n\tEND IF;\n\n\tIF last_upgrade_major_version < 14 THEN\n\t\tPERFORM fix_pre_citus14_colocation_group_collation_mismatches();\n\t\tperformed_upgrade := true;\n\tEND IF;\n\n\t-- add new upgrade steps here\n\n\tIF NOT performed_upgrade THEN\n\t\tRAISE NOTICE 'already at the latest distributed schema version (%)', last_upgrade_version_string;\n\t\tRETURN;\n\tEND IF;\n\n\tUPDATE pg_dist_node_metadata\n\tSET metadata = jsonb_set(metadata, array['last_upgrade_version'], to_jsonb(current_version_string));\nEND;\n$cppu$;\n\nCOMMENT ON PROCEDURE pg_catalog.citus_finish_citus_upgrade()\n    IS 'after upgrading Citus on all nodes call this function to upgrade the distributed schema';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold\n    FROM public.pg_dist_rebalance_strategy;\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- restore pg_dist_object from the stable identifiers\n    -- DELETE/INSERT to avoid primary key violations\n    WITH old_records AS (\n        DELETE FROM\n            citus.pg_dist_object\n        RETURNING\n            type,\n            object_names,\n            object_args,\n            distribution_argument_index,\n            colocationid\n    )\n    INSERT INTO citus.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        old_records naming,\n        pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    PERFORM citus_internal.columnar_ensure_objects_exist();\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/10.0-4.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold\n    FROM public.pg_dist_rebalance_strategy;\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE citus.pg_dist_object;\n    INSERT INTO citus.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\n\n    PERFORM citus_internal.columnar_ensure_objects_exist();\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/10.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE citus.pg_dist_object;\n    INSERT INTO citus.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE citus.pg_dist_object;\n    INSERT INTO citus.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/10.2-4.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- set dependencies for columnar table access method\n    PERFORM citus_internal.columnar_ensure_am_depends_catalog();\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE citus.pg_dist_object;\n    INSERT INTO citus.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/10.2-5.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f'\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- set dependencies for columnar table access method\n    PERFORM citus_internal.columnar_ensure_am_depends_catalog();\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE citus.pg_dist_object;\n    INSERT INTO citus.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- set dependencies for columnar table access method\n    PERFORM citus_internal.columnar_ensure_am_depends_catalog();\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE pg_catalog.pg_dist_object;\n    INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/11.0-4.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f'\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- set dependencies for columnar table access method\n    PERFORM citus_internal.columnar_ensure_am_depends_catalog();\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE pg_catalog.pg_dist_object;\n    INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n    DROP TABLE public.pg_dist_cleanup;\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n    PERFORM setval('pg_catalog.pg_dist_operationid_seq', (SELECT MAX(operation_id)+1 AS max_operation_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_cleanup_recordid_seq', (SELECT MAX(record_id)+1 AS max_record_id FROM pg_dist_cleanup), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f'\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- set dependencies for columnar table access method\n    PERFORM columnar_internal.columnar_ensure_am_depends_catalog();\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE pg_catalog.pg_dist_object;\n    INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/11.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n    DROP TABLE public.pg_dist_cleanup;\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n    PERFORM setval('pg_catalog.pg_dist_operationid_seq', (SELECT MAX(operation_id)+1 AS max_operation_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_cleanup_recordid_seq', (SELECT MAX(record_id)+1 AS max_record_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_clock_logical_seq', (SELECT last_value FROM public.pg_dist_clock_logical_seq), false);\n    DROP TABLE public.pg_dist_clock_logical_seq;\n\n\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f'\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- set dependencies for columnar table access method\n    PERFORM columnar_internal.columnar_ensure_am_depends_catalog();\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE pg_catalog.pg_dist_object;\n    INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/12.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup;\n    INSERT INTO pg_catalog.pg_dist_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_schema;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n    DROP TABLE public.pg_dist_cleanup;\n    DROP TABLE public.pg_dist_schema;\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n    PERFORM setval('pg_catalog.pg_dist_operationid_seq', (SELECT MAX(operation_id)+1 AS max_operation_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_cleanup_recordid_seq', (SELECT MAX(record_id)+1 AS max_record_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_clock_logical_seq', (SELECT last_value FROM public.pg_dist_clock_logical_seq), false);\n    DROP TABLE public.pg_dist_clock_logical_seq;\n\n\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f'\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- set dependencies for columnar table access method\n    PERFORM columnar_internal.columnar_ensure_am_depends_catalog();\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE pg_catalog.pg_dist_object;\n    INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/12.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    -- PG16 has its own any_value, so only create it pre PG16.\n    -- We can remove this part when we drop support for PG16\n    IF substring(current_Setting('server_version'), '\\d+')::int < 16 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE OR REPLACE FUNCTION pg_catalog.any_value_agg ( anyelement, anyelement )\n        RETURNS anyelement AS $$\n                SELECT CASE WHEN $1 IS NULL THEN $2 ELSE $1 END;\n        $$ LANGUAGE SQL STABLE;\n\n        CREATE AGGREGATE pg_catalog.any_value (\n                sfunc       = pg_catalog.any_value_agg,\n                combinefunc = pg_catalog.any_value_agg,\n                basetype    = anyelement,\n                stype       = anyelement\n        );\n        COMMENT ON AGGREGATE pg_catalog.any_value(anyelement) IS\n            'Returns the value of any row in the group. It is mostly useful when you know there will be only 1 element.';\n        RESET citus.enable_ddl_propagation;\n        --\n        -- Citus creates the any_value aggregate but because of a compatibility\n        -- issue between pg15-pg16 -- any_value is created in PG16, we drop\n        -- and create it during upgrade IF upgraded version is less than 16.\n        -- And as Citus creates it, there needs to be a dependency to the\n        -- Citus extension, so we create that dependency here.\n\n        INSERT INTO pg_depend\n        SELECT\n            'pg_proc'::regclass::oid as classid,\n            (SELECT oid FROM pg_proc WHERE proname = 'any_value_agg') as objid,\n            0 as objsubid,\n            'pg_extension'::regclass::oid as refclassid,\n            (select oid from pg_extension where extname = 'citus') as refobjid,\n            0 as refobjsubid ,\n            'e' as deptype;\n\n        INSERT INTO pg_depend\n        SELECT\n            'pg_proc'::regclass::oid as classid,\n            (SELECT oid FROM pg_proc WHERE proname = 'any_value') as objid,\n            0 as objsubid,\n            'pg_extension'::regclass::oid as refclassid,\n            (select oid from pg_extension where extname = 'citus') as refobjid,\n            0 as refobjsubid ,\n            'e' as deptype;\n    $cmd$;\n    END IF;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n\n    -- if we are upgrading from PG14/PG15 to PG16+,\n    -- we need to regenerate the partkeys because they will include varnullingrels as well.\n    UPDATE pg_catalog.pg_dist_partition\n    SET partkey = column_name_to_column(pg_dist_partkeys_pre_16_upgrade.logicalrelid, col_name)\n    FROM public.pg_dist_partkeys_pre_16_upgrade\n    WHERE pg_dist_partkeys_pre_16_upgrade.logicalrelid = pg_dist_partition.logicalrelid;\n    DROP TABLE public.pg_dist_partkeys_pre_16_upgrade;\n\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup;\n    INSERT INTO pg_catalog.pg_dist_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_schema;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n    DROP TABLE public.pg_dist_cleanup;\n    DROP TABLE public.pg_dist_schema;\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n    PERFORM setval('pg_catalog.pg_dist_operationid_seq', (SELECT MAX(operation_id)+1 AS max_operation_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_cleanup_recordid_seq', (SELECT MAX(record_id)+1 AS max_record_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_clock_logical_seq', (SELECT last_value FROM public.pg_dist_clock_logical_seq), false);\n    DROP TABLE public.pg_dist_clock_logical_seq;\n\n\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f'\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- set dependencies for columnar table access method\n    PERFORM columnar_internal.columnar_ensure_am_depends_catalog();\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE pg_catalog.pg_dist_object;\n    INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    -- PG16 has its own any_value, so only create it pre PG16.\n    -- We can remove this part when we drop support for PG16\n    IF substring(current_Setting('server_version'), '\\d+')::int < 16 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE OR REPLACE FUNCTION pg_catalog.any_value_agg ( anyelement, anyelement )\n        RETURNS anyelement AS $$\n                SELECT CASE WHEN $1 IS NULL THEN $2 ELSE $1 END;\n        $$ LANGUAGE SQL STABLE;\n\n        CREATE AGGREGATE pg_catalog.any_value (\n                sfunc       = pg_catalog.any_value_agg,\n                combinefunc = pg_catalog.any_value_agg,\n                basetype    = anyelement,\n                stype       = anyelement\n        );\n        COMMENT ON AGGREGATE pg_catalog.any_value(anyelement) IS\n            'Returns the value of any row in the group. It is mostly useful when you know there will be only 1 element.';\n        RESET citus.enable_ddl_propagation;\n        --\n        -- Citus creates the any_value aggregate but because of a compatibility\n        -- issue between pg15-pg16 -- any_value is created in PG16, we drop\n        -- and create it during upgrade IF upgraded version is less than 16.\n        -- And as Citus creates it, there needs to be a dependency to the\n        -- Citus extension, so we create that dependency here.\n\n        INSERT INTO pg_depend\n        SELECT\n            'pg_proc'::regclass::oid as classid,\n            (SELECT oid FROM pg_proc WHERE proname = 'any_value_agg') as objid,\n            0 as objsubid,\n            'pg_extension'::regclass::oid as refclassid,\n            (select oid from pg_extension where extname = 'citus') as refobjid,\n            0 as refobjsubid ,\n            'e' as deptype;\n\n        INSERT INTO pg_depend\n        SELECT\n            'pg_proc'::regclass::oid as classid,\n            (SELECT oid FROM pg_proc WHERE proname = 'any_value') as objid,\n            0 as objsubid,\n            'pg_extension'::regclass::oid as refclassid,\n            (select oid from pg_extension where extname = 'citus') as refobjid,\n            0 as refobjsubid ,\n            'e' as deptype;\n    $cmd$;\n    END IF;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n\n    -- if we are upgrading from PG14/PG15 to PG16+,\n    -- we need to regenerate the partkeys because they will include varnullingrels as well.\n    UPDATE pg_catalog.pg_dist_partition\n    SET partkey = column_name_to_column(pg_dist_partkeys_pre_16_upgrade.logicalrelid, col_name)\n    FROM public.pg_dist_partkeys_pre_16_upgrade\n    WHERE pg_dist_partkeys_pre_16_upgrade.logicalrelid = pg_dist_partition.logicalrelid;\n    DROP TABLE public.pg_dist_partkeys_pre_16_upgrade;\n\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup;\n    INSERT INTO pg_catalog.pg_dist_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_schema;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    -- Temporarily disable trigger to check for validity of functions while\n    -- inserting. The current contents of the table might be invalid if one of\n    -- the functions was removed by the user without also removing the\n    -- rebalance strategy. Obviously that's not great, but it should be no\n    -- reason to fail the upgrade.\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n    DROP TABLE public.pg_dist_cleanup;\n    DROP TABLE public.pg_dist_schema;\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n    PERFORM setval('pg_catalog.pg_dist_operationid_seq', (SELECT MAX(operation_id)+1 AS max_operation_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_cleanup_recordid_seq', (SELECT MAX(record_id)+1 AS max_record_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_clock_logical_seq', (SELECT last_value FROM public.pg_dist_clock_logical_seq), false);\n    DROP TABLE public.pg_dist_clock_logical_seq;\n\n\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f'\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- set dependencies for columnar table access method\n    PERFORM columnar_internal.columnar_ensure_am_depends_catalog();\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE pg_catalog.pg_dist_object;\n    INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/13.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    -- PG16 has its own any_value, so only create it pre PG16.\n    -- We can remove this part when we drop support for PG16\n    IF substring(current_Setting('server_version'), '\\d+')::int < 16 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE OR REPLACE FUNCTION pg_catalog.any_value_agg ( anyelement, anyelement )\n        RETURNS anyelement AS $$\n                SELECT CASE WHEN $1 IS NULL THEN $2 ELSE $1 END;\n        $$ LANGUAGE SQL STABLE;\n\n        CREATE AGGREGATE pg_catalog.any_value (\n                sfunc       = pg_catalog.any_value_agg,\n                combinefunc = pg_catalog.any_value_agg,\n                basetype    = anyelement,\n                stype       = anyelement\n        );\n        COMMENT ON AGGREGATE pg_catalog.any_value(anyelement) IS\n            'Returns the value of any row in the group. It is mostly useful when you know there will be only 1 element.';\n        RESET citus.enable_ddl_propagation;\n        --\n        -- Citus creates the any_value aggregate but because of a compatibility\n        -- issue between pg15-pg16 -- any_value is created in PG16, we drop\n        -- and create it during upgrade IF upgraded version is less than 16.\n        -- And as Citus creates it, there needs to be a dependency to the\n        -- Citus extension, so we create that dependency here.\n\n        INSERT INTO pg_depend\n        SELECT\n            'pg_proc'::regclass::oid as classid,\n            (SELECT oid FROM pg_proc WHERE proname = 'any_value_agg') as objid,\n            0 as objsubid,\n            'pg_extension'::regclass::oid as refclassid,\n            (select oid from pg_extension where extname = 'citus') as refobjid,\n            0 as refobjsubid ,\n            'e' as deptype;\n\n        INSERT INTO pg_depend\n        SELECT\n            'pg_proc'::regclass::oid as classid,\n            (SELECT oid FROM pg_proc WHERE proname = 'any_value') as objid,\n            0 as objsubid,\n            'pg_extension'::regclass::oid as refclassid,\n            (select oid from pg_extension where extname = 'citus') as refobjid,\n            0 as refobjsubid ,\n            'e' as deptype;\n    $cmd$;\n    END IF;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n\n    -- if we are upgrading from PG14/PG15 to PG16+,\n    -- we need to regenerate the partkeys because they will include varnullingrels as well.\n    UPDATE pg_catalog.pg_dist_partition\n    SET partkey = column_name_to_column(pg_dist_partkeys_pre_16_upgrade.logicalrelid, col_name)\n    FROM public.pg_dist_partkeys_pre_16_upgrade\n    WHERE pg_dist_partkeys_pre_16_upgrade.logicalrelid = pg_dist_partition.logicalrelid;\n    DROP TABLE public.pg_dist_partkeys_pre_16_upgrade;\n\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup;\n    INSERT INTO pg_catalog.pg_dist_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_schema;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    -- Temporarily disable trigger to check for validity of functions while\n    -- inserting. The current contents of the table might be invalid if one of\n    -- the functions was removed by the user without also removing the\n    -- rebalance strategy. Obviously that's not great, but it should be no\n    -- reason to fail the upgrade.\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n    DROP TABLE public.pg_dist_cleanup;\n    DROP TABLE public.pg_dist_schema;\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n    PERFORM setval('pg_catalog.pg_dist_operationid_seq', (SELECT MAX(operation_id)+1 AS max_operation_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_cleanup_recordid_seq', (SELECT MAX(record_id)+1 AS max_record_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_clock_logical_seq', (SELECT last_value FROM public.pg_dist_clock_logical_seq), false);\n    DROP TABLE public.pg_dist_clock_logical_seq;\n\n\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f'\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- If citus_columnar extension exists, then perform the post PG-upgrade work for columnar as well.\n    --\n    -- First look if pg_catalog.columnar_finish_pg_upgrade function exists as part of the citus_columnar\n    -- extension. (We check whether it's part of the extension just for security reasons). If it does, then\n    -- call it. If not, then look for columnar_internal.columnar_ensure_am_depends_catalog function and as\n    -- part of the citus_columnar extension. If so, then call it. We alternatively check for the latter UDF\n    -- just because pg_catalog.columnar_finish_pg_upgrade function is introduced in citus_columnar 13.2-1\n    -- and as of today all it does is to call columnar_internal.columnar_ensure_am_depends_catalog function.\n    IF EXISTS (\n        SELECT 1 FROM pg_depend\n        JOIN pg_proc ON (pg_depend.objid = pg_proc.oid)\n        JOIN pg_namespace ON (pg_proc.pronamespace = pg_namespace.oid)\n        JOIN pg_extension ON (pg_depend.refobjid = pg_extension.oid)\n        WHERE\n            -- Looking if pg_catalog.columnar_finish_pg_upgrade function exists and\n            -- if there is a dependency record from it (proc class = 1255) ..\n            pg_depend.classid = 1255 AND pg_namespace.nspname = 'pg_catalog' AND pg_proc.proname = 'columnar_finish_pg_upgrade' AND\n            -- .. to citus_columnar extension (3079 = extension class), if it exists.\n            pg_depend.refclassid = 3079 AND pg_extension.extname = 'citus_columnar'\n    )\n    THEN PERFORM pg_catalog.columnar_finish_pg_upgrade();\n    ELSIF EXISTS (\n        SELECT 1 FROM pg_depend\n        JOIN pg_proc ON (pg_depend.objid = pg_proc.oid)\n        JOIN pg_namespace ON (pg_proc.pronamespace = pg_namespace.oid)\n        JOIN pg_extension ON (pg_depend.refobjid = pg_extension.oid)\n        WHERE\n            -- Looking if columnar_internal.columnar_ensure_am_depends_catalog function exists and\n            -- if there is a dependency record from it (proc class = 1255) ..\n            pg_depend.classid = 1255 AND pg_namespace.nspname = 'columnar_internal' AND pg_proc.proname = 'columnar_ensure_am_depends_catalog' AND\n            -- .. to citus_columnar extension (3079 = extension class), if it exists.\n            pg_depend.refclassid = 3079 AND pg_extension.extname = 'citus_columnar'\n    )\n    THEN PERFORM columnar_internal.columnar_ensure_am_depends_catalog();\n    END IF;\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE pg_catalog.pg_dist_object;\n    INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/14.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    -- PG16 has its own any_value, so only create it pre PG16.\n    -- We can remove this part when we drop support for PG16\n    IF substring(current_Setting('server_version'), '\\d+')::int < 16 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE OR REPLACE FUNCTION pg_catalog.any_value_agg ( anyelement, anyelement )\n        RETURNS anyelement AS $$\n                SELECT CASE WHEN $1 IS NULL THEN $2 ELSE $1 END;\n        $$ LANGUAGE SQL STABLE;\n\n        CREATE AGGREGATE pg_catalog.any_value (\n                sfunc       = pg_catalog.any_value_agg,\n                combinefunc = pg_catalog.any_value_agg,\n                basetype    = anyelement,\n                stype       = anyelement\n        );\n        COMMENT ON AGGREGATE pg_catalog.any_value(anyelement) IS\n            'Returns the value of any row in the group. It is mostly useful when you know there will be only 1 element.';\n        RESET citus.enable_ddl_propagation;\n        --\n        -- Citus creates the any_value aggregate but because of a compatibility\n        -- issue between pg15-pg16 -- any_value is created in PG16, we drop\n        -- and create it during upgrade IF upgraded version is less than 16.\n        -- And as Citus creates it, there needs to be a dependency to the\n        -- Citus extension, so we create that dependency here.\n\n        INSERT INTO pg_depend\n        SELECT\n            'pg_proc'::regclass::oid as classid,\n            (SELECT oid FROM pg_proc WHERE proname = 'any_value_agg') as objid,\n            0 as objsubid,\n            'pg_extension'::regclass::oid as refclassid,\n            (select oid from pg_extension where extname = 'citus') as refobjid,\n            0 as refobjsubid ,\n            'e' as deptype;\n\n        INSERT INTO pg_depend\n        SELECT\n            'pg_proc'::regclass::oid as classid,\n            (SELECT oid FROM pg_proc WHERE proname = 'any_value') as objid,\n            0 as objsubid,\n            'pg_extension'::regclass::oid as refclassid,\n            (select oid from pg_extension where extname = 'citus') as refobjid,\n            0 as refobjsubid ,\n            'e' as deptype;\n    $cmd$;\n    END IF;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n\n    -- if we are upgrading from PG14/PG15 to PG16+,\n    -- we need to regenerate the partkeys because they will include varnullingrels as well.\n    UPDATE pg_catalog.pg_dist_partition\n    SET partkey = column_name_to_column(pg_dist_partkeys_pre_16_upgrade.logicalrelid, col_name)\n    FROM public.pg_dist_partkeys_pre_16_upgrade\n    WHERE pg_dist_partkeys_pre_16_upgrade.logicalrelid = pg_dist_partition.logicalrelid;\n    DROP TABLE public.pg_dist_partkeys_pre_16_upgrade;\n\n    -- if we are upgrading to PG18+,\n    -- we need to regenerate the partkeys because they will include varreturningtype as well.\n    UPDATE pg_catalog.pg_dist_partition\n    SET partkey = column_name_to_column(pg_dist_partkeys_pre_18_upgrade.logicalrelid, col_name)\n    FROM public.pg_dist_partkeys_pre_18_upgrade\n    WHERE pg_dist_partkeys_pre_18_upgrade.logicalrelid = pg_dist_partition.logicalrelid;\n    DROP TABLE public.pg_dist_partkeys_pre_18_upgrade;\n\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup;\n    INSERT INTO pg_catalog.pg_dist_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_schema;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    -- Temporarily disable trigger to check for validity of functions while\n    -- inserting. The current contents of the table might be invalid if one of\n    -- the functions was removed by the user without also removing the\n    -- rebalance strategy. Obviously that's not great, but it should be no\n    -- reason to fail the upgrade.\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n    DROP TABLE public.pg_dist_cleanup;\n    DROP TABLE public.pg_dist_schema;\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n    PERFORM setval('pg_catalog.pg_dist_operationid_seq', (SELECT MAX(operation_id)+1 AS max_operation_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_cleanup_recordid_seq', (SELECT MAX(record_id)+1 AS max_record_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_clock_logical_seq', (SELECT last_value FROM public.pg_dist_clock_logical_seq), false);\n    DROP TABLE public.pg_dist_clock_logical_seq;\n\n\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f'\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- If citus_columnar extension exists, then perform the post PG-upgrade work for columnar as well.\n    --\n    -- First look if pg_catalog.columnar_finish_pg_upgrade function exists as part of the citus_columnar\n    -- extension. (We check whether it's part of the extension just for security reasons). If it does, then\n    -- call it. If not, then look for columnar_internal.columnar_ensure_am_depends_catalog function and as\n    -- part of the citus_columnar extension. If so, then call it. We alternatively check for the latter UDF\n    -- just because pg_catalog.columnar_finish_pg_upgrade function is introduced in citus_columnar 13.2-1\n    -- and as of today all it does is to call columnar_internal.columnar_ensure_am_depends_catalog function.\n    IF EXISTS (\n        SELECT 1 FROM pg_depend\n        JOIN pg_proc ON (pg_depend.objid = pg_proc.oid)\n        JOIN pg_namespace ON (pg_proc.pronamespace = pg_namespace.oid)\n        JOIN pg_extension ON (pg_depend.refobjid = pg_extension.oid)\n        WHERE\n            -- Looking if pg_catalog.columnar_finish_pg_upgrade function exists and\n            -- if there is a dependency record from it (proc class = 1255) ..\n            pg_depend.classid = 1255 AND pg_namespace.nspname = 'pg_catalog' AND pg_proc.proname = 'columnar_finish_pg_upgrade' AND\n            -- .. to citus_columnar extension (3079 = extension class), if it exists.\n            pg_depend.refclassid = 3079 AND pg_extension.extname = 'citus_columnar'\n    )\n    THEN PERFORM pg_catalog.columnar_finish_pg_upgrade();\n    ELSIF EXISTS (\n        SELECT 1 FROM pg_depend\n        JOIN pg_proc ON (pg_depend.objid = pg_proc.oid)\n        JOIN pg_namespace ON (pg_proc.pronamespace = pg_namespace.oid)\n        JOIN pg_extension ON (pg_depend.refobjid = pg_extension.oid)\n        WHERE\n            -- Looking if columnar_internal.columnar_ensure_am_depends_catalog function exists and\n            -- if there is a dependency record from it (proc class = 1255) ..\n            pg_depend.classid = 1255 AND pg_namespace.nspname = 'columnar_internal' AND pg_proc.proname = 'columnar_ensure_am_depends_catalog' AND\n            -- .. to citus_columnar extension (3079 = extension class), if it exists.\n            pg_depend.refclassid = 3079 AND pg_extension.extname = 'citus_columnar'\n    )\n    THEN PERFORM columnar_internal.columnar_ensure_am_depends_catalog();\n    END IF;\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE pg_catalog.pg_dist_object;\n    INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/9.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- restore pg_dist_object from the stable identifiers\n    -- DELETE/INSERT to avoid primary key violations\n    WITH old_records AS (\n        DELETE FROM\n            citus.pg_dist_object\n        RETURNING\n            type,\n            object_names,\n            object_args,\n            distribution_argument_index,\n            colocationid\n    )\n    INSERT INTO citus.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        old_records naming,\n        pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold\n    FROM public.pg_dist_rebalance_strategy;\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- restore pg_dist_object from the stable identifiers\n    -- DELETE/INSERT to avoid primary key violations\n    WITH old_records AS (\n        DELETE FROM\n            citus.pg_dist_object\n        RETURNING\n            type,\n            object_names,\n            object_args,\n            distribution_argument_index,\n            colocationid\n    )\n    INSERT INTO citus.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        old_records naming,\n        pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/9.4-2.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold\n    FROM public.pg_dist_rebalance_strategy;\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE citus.pg_dist_object;\n    INSERT INTO citus.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold\n    FROM public.pg_dist_rebalance_strategy;\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- restore pg_dist_object from the stable identifiers\n    -- DELETE/INSERT to avoid primary key violations\n    WITH old_records AS (\n        DELETE FROM\n            citus.pg_dist_object\n        RETURNING\n            type,\n            object_names,\n            object_args,\n            distribution_argument_index,\n            colocationid\n    )\n    INSERT INTO citus.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        old_records naming,\n        pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/9.5-2.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold\n    FROM public.pg_dist_rebalance_strategy;\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_enterprise_check_trigger;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE citus.pg_dist_object;\n    INSERT INTO citus.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nDECLARE\n    table_name regclass;\n    command text;\n    trigger_name text;\nBEGIN\n\n\n    IF substring(current_Setting('server_version'), '\\d+')::int >= 14 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray);\n        COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    ELSE\n    EXECUTE $cmd$\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray);\n        COMMENT ON AGGREGATE array_cat_agg(anyarray)\n        IS 'concatenate input arrays into a single array';\n        RESET citus.enable_ddl_propagation;\n    $cmd$;\n    END IF;\n\n    --\n    -- Citus creates the array_cat_agg but because of a compatibility\n    -- issue between pg13-pg14, we drop and create it during upgrade.\n    -- And as Citus creates it, there needs to be a dependency to the\n    -- Citus extension, so we create that dependency here.\n    -- We are not using:\n    --  ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg\n    -- because we don't have an easy way to check if the aggregate\n    -- exists with anyarray type or anycompatiblearray type.\n\n    INSERT INTO pg_depend\n    SELECT\n        'pg_proc'::regclass::oid as classid,\n        (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'e' as deptype;\n\n    -- PG16 has its own any_value, so only create it pre PG16.\n    -- We can remove this part when we drop support for PG16\n    IF substring(current_Setting('server_version'), '\\d+')::int < 16 THEN\n    EXECUTE $cmd$\n        -- disable propagation to prevent EnsureCoordinator errors\n        -- the aggregate created here does not depend on Citus extension (yet)\n        -- since we add the dependency with the next command\n        SET citus.enable_ddl_propagation TO OFF;\n        CREATE OR REPLACE FUNCTION pg_catalog.any_value_agg ( anyelement, anyelement )\n        RETURNS anyelement AS $$\n                SELECT CASE WHEN $1 IS NULL THEN $2 ELSE $1 END;\n        $$ LANGUAGE SQL STABLE;\n\n        CREATE AGGREGATE pg_catalog.any_value (\n                sfunc       = pg_catalog.any_value_agg,\n                combinefunc = pg_catalog.any_value_agg,\n                basetype    = anyelement,\n                stype       = anyelement\n        );\n        COMMENT ON AGGREGATE pg_catalog.any_value(anyelement) IS\n            'Returns the value of any row in the group. It is mostly useful when you know there will be only 1 element.';\n        RESET citus.enable_ddl_propagation;\n        --\n        -- Citus creates the any_value aggregate but because of a compatibility\n        -- issue between pg15-pg16 -- any_value is created in PG16, we drop\n        -- and create it during upgrade IF upgraded version is less than 16.\n        -- And as Citus creates it, there needs to be a dependency to the\n        -- Citus extension, so we create that dependency here.\n\n        INSERT INTO pg_depend\n        SELECT\n            'pg_proc'::regclass::oid as classid,\n            (SELECT oid FROM pg_proc WHERE proname = 'any_value_agg') as objid,\n            0 as objsubid,\n            'pg_extension'::regclass::oid as refclassid,\n            (select oid from pg_extension where extname = 'citus') as refobjid,\n            0 as refobjsubid ,\n            'e' as deptype;\n\n        INSERT INTO pg_depend\n        SELECT\n            'pg_proc'::regclass::oid as classid,\n            (SELECT oid FROM pg_proc WHERE proname = 'any_value') as objid,\n            0 as objsubid,\n            'pg_extension'::regclass::oid as refclassid,\n            (select oid from pg_extension where extname = 'citus') as refobjid,\n            0 as refobjsubid ,\n            'e' as deptype;\n    $cmd$;\n    END IF;\n\n    --\n    -- restore citus catalog tables\n    --\n    INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition;\n\n    -- if we are upgrading from PG14/PG15 to PG16+,\n    -- we need to regenerate the partkeys because they will include varnullingrels as well.\n    UPDATE pg_catalog.pg_dist_partition\n    SET partkey = column_name_to_column(pg_dist_partkeys_pre_16_upgrade.logicalrelid, col_name)\n    FROM public.pg_dist_partkeys_pre_16_upgrade\n    WHERE pg_dist_partkeys_pre_16_upgrade.logicalrelid = pg_dist_partition.logicalrelid;\n    DROP TABLE public.pg_dist_partkeys_pre_16_upgrade;\n\n    -- if we are upgrading to PG18+,\n    -- we need to regenerate the partkeys because they will include varreturningtype as well.\n    UPDATE pg_catalog.pg_dist_partition\n    SET partkey = column_name_to_column(pg_dist_partkeys_pre_18_upgrade.logicalrelid, col_name)\n    FROM public.pg_dist_partkeys_pre_18_upgrade\n    WHERE pg_dist_partkeys_pre_18_upgrade.logicalrelid = pg_dist_partition.logicalrelid;\n    DROP TABLE public.pg_dist_partkeys_pre_18_upgrade;\n\n    INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard;\n    INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement;\n    INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata;\n    INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node;\n    INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group;\n    INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction;\n    INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation;\n    INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup;\n    INSERT INTO pg_catalog.pg_dist_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_schema;\n    -- enterprise catalog tables\n    INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo;\n    INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo;\n\n    -- Temporarily disable trigger to check for validity of functions while\n    -- inserting. The current contents of the table might be invalid if one of\n    -- the functions was removed by the user without also removing the\n    -- rebalance strategy. Obviously that's not great, but it should be no\n    -- reason to fail the upgrade.\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy DISABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;\n    INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::regproc,\n        node_capacity_function::regprocedure::regproc,\n        shard_allowed_on_node_function::regprocedure::regproc,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM public.pg_dist_rebalance_strategy;\n    ALTER TABLE pg_catalog.pg_dist_rebalance_strategy ENABLE TRIGGER pg_dist_rebalance_strategy_validation_trigger;\n\n    --\n    -- drop backup tables\n    --\n    DROP TABLE public.pg_dist_authinfo;\n    DROP TABLE public.pg_dist_colocation;\n    DROP TABLE public.pg_dist_local_group;\n    DROP TABLE public.pg_dist_node;\n    DROP TABLE public.pg_dist_node_metadata;\n    DROP TABLE public.pg_dist_partition;\n    DROP TABLE public.pg_dist_placement;\n    DROP TABLE public.pg_dist_poolinfo;\n    DROP TABLE public.pg_dist_shard;\n    DROP TABLE public.pg_dist_transaction;\n    DROP TABLE public.pg_dist_rebalance_strategy;\n    DROP TABLE public.pg_dist_cleanup;\n    DROP TABLE public.pg_dist_schema;\n    --\n    -- reset sequences\n    --\n    PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false);\n    PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false);\n    PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false);\n    PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false);\n    PERFORM setval('pg_catalog.pg_dist_operationid_seq', (SELECT MAX(operation_id)+1 AS max_operation_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_cleanup_recordid_seq', (SELECT MAX(record_id)+1 AS max_record_id FROM pg_dist_cleanup), false);\n    PERFORM setval('pg_catalog.pg_dist_clock_logical_seq', (SELECT last_value FROM public.pg_dist_clock_logical_seq), false);\n    DROP TABLE public.pg_dist_clock_logical_seq;\n\n\n\n    --\n    -- register triggers\n    --\n    FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f'\n    LOOP\n        trigger_name := 'truncate_trigger_' || table_name::oid;\n        command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()';\n        EXECUTE command;\n        command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name);\n        EXECUTE command;\n    END LOOP;\n\n    --\n    -- set dependencies\n    --\n    INSERT INTO pg_depend\n    SELECT\n        'pg_class'::regclass::oid as classid,\n        p.logicalrelid::regclass::oid as objid,\n        0 as objsubid,\n        'pg_extension'::regclass::oid as refclassid,\n        (select oid from pg_extension where extname = 'citus') as refobjid,\n        0 as refobjsubid ,\n        'n' as deptype\n    FROM pg_catalog.pg_dist_partition p;\n\n    -- If citus_columnar extension exists, then perform the post PG-upgrade work for columnar as well.\n    --\n    -- First look if pg_catalog.columnar_finish_pg_upgrade function exists as part of the citus_columnar\n    -- extension. (We check whether it's part of the extension just for security reasons). If it does, then\n    -- call it. If not, then look for columnar_internal.columnar_ensure_am_depends_catalog function and as\n    -- part of the citus_columnar extension. If so, then call it. We alternatively check for the latter UDF\n    -- just because pg_catalog.columnar_finish_pg_upgrade function is introduced in citus_columnar 13.2-1\n    -- and as of today all it does is to call columnar_internal.columnar_ensure_am_depends_catalog function.\n    IF EXISTS (\n        SELECT 1 FROM pg_depend\n        JOIN pg_proc ON (pg_depend.objid = pg_proc.oid)\n        JOIN pg_namespace ON (pg_proc.pronamespace = pg_namespace.oid)\n        JOIN pg_extension ON (pg_depend.refobjid = pg_extension.oid)\n        WHERE\n            -- Looking if pg_catalog.columnar_finish_pg_upgrade function exists and\n            -- if there is a dependency record from it (proc class = 1255) ..\n            pg_depend.classid = 1255 AND pg_namespace.nspname = 'pg_catalog' AND pg_proc.proname = 'columnar_finish_pg_upgrade' AND\n            -- .. to citus_columnar extension (3079 = extension class), if it exists.\n            pg_depend.refclassid = 3079 AND pg_extension.extname = 'citus_columnar'\n    )\n    THEN PERFORM pg_catalog.columnar_finish_pg_upgrade();\n    ELSIF EXISTS (\n        SELECT 1 FROM pg_depend\n        JOIN pg_proc ON (pg_depend.objid = pg_proc.oid)\n        JOIN pg_namespace ON (pg_proc.pronamespace = pg_namespace.oid)\n        JOIN pg_extension ON (pg_depend.refobjid = pg_extension.oid)\n        WHERE\n            -- Looking if columnar_internal.columnar_ensure_am_depends_catalog function exists and\n            -- if there is a dependency record from it (proc class = 1255) ..\n            pg_depend.classid = 1255 AND pg_namespace.nspname = 'columnar_internal' AND pg_proc.proname = 'columnar_ensure_am_depends_catalog' AND\n            -- .. to citus_columnar extension (3079 = extension class), if it exists.\n            pg_depend.refclassid = 3079 AND pg_extension.extname = 'citus_columnar'\n    )\n    THEN PERFORM columnar_internal.columnar_ensure_am_depends_catalog();\n    END IF;\n\n    -- restore pg_dist_object from the stable identifiers\n    TRUNCATE pg_catalog.pg_dist_object;\n    INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid)\n    SELECT\n        address.classid,\n        address.objid,\n        address.objsubid,\n        naming.distribution_argument_index,\n        naming.colocationid\n    FROM\n        public.pg_dist_object naming,\n        pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address;\n\n    DROP TABLE public.pg_dist_object;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade()\n    IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_get_node_clock/11.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_get_node_clock()\n    RETURNS pg_catalog.cluster_clock\n    LANGUAGE C VOLATILE PARALLEL UNSAFE STRICT\n    AS 'MODULE_PATHNAME',$$citus_get_node_clock$$;\nCOMMENT ON FUNCTION pg_catalog.citus_get_node_clock()\n    IS 'Returns monotonically increasing timestamp with logical clock value as close to epoch value (in milli seconds) as possible, and a counter for ticks(maximum of 4 million) within the logical clock';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_get_node_clock/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_get_node_clock()\n    RETURNS pg_catalog.cluster_clock\n    LANGUAGE C VOLATILE PARALLEL UNSAFE STRICT\n    AS 'MODULE_PATHNAME',$$citus_get_node_clock$$;\nCOMMENT ON FUNCTION pg_catalog.citus_get_node_clock()\n    IS 'Returns monotonically increasing timestamp with logical clock value as close to epoch value (in milli seconds) as possible, and a counter for ticks(maximum of 4 million) within the logical clock';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_get_transaction_clock/11.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_get_transaction_clock()\n    RETURNS pg_catalog.cluster_clock\n    LANGUAGE C VOLATILE PARALLEL UNSAFE STRICT\n    AS 'MODULE_PATHNAME',$$citus_get_transaction_clock$$;\nCOMMENT ON FUNCTION pg_catalog.citus_get_transaction_clock()\n    IS 'Returns a transaction timestamp logical clock';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_get_transaction_clock/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_get_transaction_clock()\n    RETURNS pg_catalog.cluster_clock\n    LANGUAGE C VOLATILE PARALLEL UNSAFE STRICT\n    AS 'MODULE_PATHNAME',$$citus_get_transaction_clock$$;\nCOMMENT ON FUNCTION pg_catalog.citus_get_transaction_clock()\n    IS 'Returns a transaction timestamp logical clock';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_acquire_citus_advisory_object_class_lock/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.acquire_citus_advisory_object_class_lock(objectClass int, qualifiedObjectName cstring)\n RETURNS void\n LANGUAGE C\n VOLATILE\nAS 'MODULE_PATHNAME', $$citus_internal_acquire_citus_advisory_object_class_lock$$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_acquire_citus_advisory_object_class_lock/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.acquire_citus_advisory_object_class_lock(objectClass int, qualifiedObjectName cstring)\n RETURNS void\n LANGUAGE C\n VOLATILE\nAS 'MODULE_PATHNAME', $$citus_internal_acquire_citus_advisory_object_class_lock$$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_colocation_metadata/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_colocation_metadata(\n\t\t\t\t\t\t\tcolocation_id int,\n                            shard_count int,\n                            replication_factor int,\n\t\t\t\t\t\t\tdistribution_column_type regtype,\n                            distribution_column_collation oid)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_colocation_metadata(int,int,int,regtype,oid) IS\n    'Inserts a co-location group into pg_dist_colocation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_colocation_metadata/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.add_colocation_metadata(\n\t\t\t\t\t\t\tcolocation_id int,\n                            shard_count int,\n                            replication_factor int,\n\t\t\t\t\t\t\tdistribution_column_type regtype,\n                            distribution_column_collation oid)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_colocation_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.add_colocation_metadata(int,int,int,regtype,oid) IS\n    'Inserts a co-location group into pg_dist_colocation';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_colocation_metadata(\n\t\t\t\t\t\t\tcolocation_id int,\n                            shard_count int,\n                            replication_factor int,\n\t\t\t\t\t\t\tdistribution_column_type regtype,\n                            distribution_column_collation oid)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_colocation_metadata(int,int,int,regtype,oid) IS\n    'Inserts a co-location group into pg_dist_colocation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_colocation_metadata/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.add_colocation_metadata(\n\t\t\t\t\t\t\tcolocation_id int,\n                            shard_count int,\n                            replication_factor int,\n\t\t\t\t\t\t\tdistribution_column_type regtype,\n                            distribution_column_collation oid)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_colocation_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.add_colocation_metadata(int,int,int,regtype,oid) IS\n    'Inserts a co-location group into pg_dist_colocation';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_colocation_metadata(\n\t\t\t\t\t\t\tcolocation_id int,\n                            shard_count int,\n                            replication_factor int,\n\t\t\t\t\t\t\tdistribution_column_type regtype,\n                            distribution_column_collation oid)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_colocation_metadata(int,int,int,regtype,oid) IS\n    'Inserts a co-location group into pg_dist_colocation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_object_metadata/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_object_metadata(\n\t\t\t\t\t\t\ttypeText text,\n                            objNames text[],\n                            objArgs text[],\n\t\t\t\t\t\t\tdistribution_argument_index int,\n                            colocationid int,\n                            force_delegation bool)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_object_metadata(text,text[],text[],int,int,bool) IS\n    'Inserts distributed object into pg_dist_object';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_object_metadata/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.add_object_metadata(\n\t\t\t\t\t\t\ttypeText text,\n                            objNames text[],\n                            objArgs text[],\n\t\t\t\t\t\t\tdistribution_argument_index int,\n                            colocationid int,\n                            force_delegation bool)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_object_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.add_object_metadata(text,text[],text[],int,int,bool) IS\n    'Inserts distributed object into pg_dist_object';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_object_metadata(\n\t\t\t\t\t\t\ttypeText text,\n                            objNames text[],\n                            objArgs text[],\n\t\t\t\t\t\t\tdistribution_argument_index int,\n                            colocationid int,\n                            force_delegation bool)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_object_metadata(text,text[],text[],int,int,bool) IS\n    'Inserts distributed object into pg_dist_object';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_object_metadata/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.add_object_metadata(\n\t\t\t\t\t\t\ttypeText text,\n                            objNames text[],\n                            objArgs text[],\n\t\t\t\t\t\t\tdistribution_argument_index int,\n                            colocationid int,\n                            force_delegation bool)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_object_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.add_object_metadata(text,text[],text[],int,int,bool) IS\n    'Inserts distributed object into pg_dist_object';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_object_metadata(\n\t\t\t\t\t\t\ttypeText text,\n                            objNames text[],\n                            objArgs text[],\n\t\t\t\t\t\t\tdistribution_argument_index int,\n                            colocationid int,\n                            force_delegation bool)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_object_metadata(text,text[],text[],int,int,bool) IS\n    'Inserts distributed object into pg_dist_object';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_partition_metadata/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_partition_metadata(\n\t\t\t\t\t\t\trelation_id regclass, distribution_method \"char\",\n\t\t\t\t\t\t\tdistribution_column text, colocation_id integer,\n\t\t\t\t\t\t\treplication_model \"char\")\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_partition_metadata(regclass, \"char\", text, integer, \"char\") IS\n    'Inserts into pg_dist_partition with user checks';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_partition_metadata/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.add_partition_metadata(\n\t\t\t\t\t\t\trelation_id regclass, distribution_method \"char\",\n\t\t\t\t\t\t\tdistribution_column text, colocation_id integer,\n\t\t\t\t\t\t\treplication_model \"char\")\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_internal_add_partition_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.add_partition_metadata(regclass, \"char\", text, integer, \"char\") IS\n    'Inserts into pg_dist_partition with user checks';\n\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_partition_metadata(\n\t\t\t\t\t\t\trelation_id regclass, distribution_method \"char\",\n\t\t\t\t\t\t\tdistribution_column text, colocation_id integer,\n\t\t\t\t\t\t\treplication_model \"char\")\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_partition_metadata(regclass, \"char\", text, integer, \"char\") IS\n    'Inserts into pg_dist_partition with user checks';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_partition_metadata/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.add_partition_metadata(\n\t\t\t\t\t\t\trelation_id regclass, distribution_method \"char\",\n\t\t\t\t\t\t\tdistribution_column text, colocation_id integer,\n\t\t\t\t\t\t\treplication_model \"char\")\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_internal_add_partition_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.add_partition_metadata(regclass, \"char\", text, integer, \"char\") IS\n    'Inserts into pg_dist_partition with user checks';\n\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_partition_metadata(\n\t\t\t\t\t\t\trelation_id regclass, distribution_method \"char\",\n\t\t\t\t\t\t\tdistribution_column text, colocation_id integer,\n\t\t\t\t\t\t\treplication_model \"char\")\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_partition_metadata(regclass, \"char\", text, integer, \"char\") IS\n    'Inserts into pg_dist_partition with user checks';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_placement_metadata/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint, shard_state integer,\n\t\t\t\t\t\t\tshard_length bigint, group_id integer,\n\t\t\t\t\t\t\tplacement_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_placement_metadata(bigint, integer, bigint, integer, bigint) IS\n    'Inserts into pg_dist_shard_placement with user checks';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_placement_metadata/11.2-1.sql",
    "content": "-- create a new function, without shardstate\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint,\n\t\t\t\t\t\t\tshard_length bigint, group_id integer,\n\t\t\t\t\t\t\tplacement_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_placement_metadata$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_placement_metadata(bigint, bigint, integer, bigint) IS\n    'Inserts into pg_dist_shard_placement with user checks';\n\n-- replace the old one so it would call the old C function with shard_state\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint, shard_state integer,\n\t\t\t\t\t\t\tshard_length bigint, group_id integer,\n\t\t\t\t\t\t\tplacement_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_placement_metadata_legacy$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_placement_metadata(bigint, integer, bigint, integer, bigint) IS\n    'Inserts into pg_dist_shard_placement with user checks';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_placement_metadata/13.1-1.sql",
    "content": "-- create a new function, without shardstate\nCREATE OR REPLACE FUNCTION citus_internal.add_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint,\n\t\t\t\t\t\t\tshard_length bigint, group_id integer,\n\t\t\t\t\t\t\tplacement_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_placement_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.add_placement_metadata(bigint, bigint, integer, bigint) IS\n    'Inserts into pg_dist_shard_placement with user checks';\n\n-- create a new function, without shardstate\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint,\n\t\t\t\t\t\t\tshard_length bigint, group_id integer,\n\t\t\t\t\t\t\tplacement_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_placement_metadata$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_placement_metadata(bigint, bigint, integer, bigint) IS\n    'Inserts into pg_dist_shard_placement with user checks';\n\n-- replace the old one so it would call the old C function with shard_state\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint, shard_state integer,\n\t\t\t\t\t\t\tshard_length bigint, group_id integer,\n\t\t\t\t\t\t\tplacement_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_placement_metadata_legacy$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_placement_metadata(bigint, integer, bigint, integer, bigint) IS\n    'Inserts into pg_dist_shard_placement with user checks';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_placement_metadata/latest.sql",
    "content": "-- create a new function, without shardstate\nCREATE OR REPLACE FUNCTION citus_internal.add_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint,\n\t\t\t\t\t\t\tshard_length bigint, group_id integer,\n\t\t\t\t\t\t\tplacement_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_placement_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.add_placement_metadata(bigint, bigint, integer, bigint) IS\n    'Inserts into pg_dist_shard_placement with user checks';\n\n-- create a new function, without shardstate\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint,\n\t\t\t\t\t\t\tshard_length bigint, group_id integer,\n\t\t\t\t\t\t\tplacement_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_placement_metadata$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_placement_metadata(bigint, bigint, integer, bigint) IS\n    'Inserts into pg_dist_shard_placement with user checks';\n\n-- replace the old one so it would call the old C function with shard_state\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint, shard_state integer,\n\t\t\t\t\t\t\tshard_length bigint, group_id integer,\n\t\t\t\t\t\t\tplacement_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_add_placement_metadata_legacy$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_placement_metadata(bigint, integer, bigint, integer, bigint) IS\n    'Inserts into pg_dist_shard_placement with user checks';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_shard_metadata/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_shard_metadata(\n\t\t\t\t\t\t\trelation_id regclass, shard_id bigint,\n\t\t\t\t\t\t\tstorage_type \"char\", shard_min_value text,\n\t\t\t\t\t\t\tshard_max_value text\n\t\t\t\t\t\t\t)\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_shard_metadata(regclass, bigint, \"char\", text, text) IS\n    'Inserts into pg_dist_shard with user checks';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_shard_metadata/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.add_shard_metadata(\n\t\t\t\t\t\t\trelation_id regclass, shard_id bigint,\n\t\t\t\t\t\t\tstorage_type \"char\", shard_min_value text,\n\t\t\t\t\t\t\tshard_max_value text\n\t\t\t\t\t\t\t)\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_internal_add_shard_metadata$$;\nCOMMENT ON FUNCTION citus_internal.add_shard_metadata(regclass, bigint, \"char\", text, text) IS\n    'Inserts into pg_dist_shard with user checks';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_shard_metadata(\n\t\t\t\t\t\t\trelation_id regclass, shard_id bigint,\n\t\t\t\t\t\t\tstorage_type \"char\", shard_min_value text,\n\t\t\t\t\t\t\tshard_max_value text\n\t\t\t\t\t\t\t)\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_shard_metadata(regclass, bigint, \"char\", text, text) IS\n    'Inserts into pg_dist_shard with user checks';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_shard_metadata/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.add_shard_metadata(\n\t\t\t\t\t\t\trelation_id regclass, shard_id bigint,\n\t\t\t\t\t\t\tstorage_type \"char\", shard_min_value text,\n\t\t\t\t\t\t\tshard_max_value text\n\t\t\t\t\t\t\t)\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$citus_internal_add_shard_metadata$$;\nCOMMENT ON FUNCTION citus_internal.add_shard_metadata(regclass, bigint, \"char\", text, text) IS\n    'Inserts into pg_dist_shard with user checks';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_shard_metadata(\n\t\t\t\t\t\t\trelation_id regclass, shard_id bigint,\n\t\t\t\t\t\t\tstorage_type \"char\", shard_min_value text,\n\t\t\t\t\t\t\tshard_max_value text\n\t\t\t\t\t\t\t)\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_shard_metadata(regclass, bigint, \"char\", text, text) IS\n    'Inserts into pg_dist_shard with user checks';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/12.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_tenant_schema(schema_id Oid, colocation_id int)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_tenant_schema(Oid, int) IS\n    'insert given tenant schema into pg_dist_schema with given colocation id';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.add_tenant_schema(schema_id Oid, colocation_id int)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME', $$citus_internal_add_tenant_schema$$;\n\nCOMMENT ON FUNCTION citus_internal.add_tenant_schema(Oid, int) IS\n    'insert given tenant schema into pg_dist_schema with given colocation id';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_tenant_schema(schema_id Oid, colocation_id int)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_tenant_schema(Oid, int) IS\n    'insert given tenant schema into pg_dist_schema with given colocation id';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.add_tenant_schema(schema_id Oid, colocation_id int)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME', $$citus_internal_add_tenant_schema$$;\n\nCOMMENT ON FUNCTION citus_internal.add_tenant_schema(Oid, int) IS\n    'insert given tenant schema into pg_dist_schema with given colocation id';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_tenant_schema(schema_id Oid, colocation_id int)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_add_tenant_schema(Oid, int) IS\n    'insert given tenant schema into pg_dist_schema with given colocation id';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_adjust_local_clock_to_remote/11.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_adjust_local_clock_to_remote(pg_catalog.cluster_clock)\n    RETURNS void\n    LANGUAGE C STABLE PARALLEL SAFE STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_adjust_local_clock_to_remote$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_adjust_local_clock_to_remote(pg_catalog.cluster_clock)\n    IS 'Internal UDF used to adjust the local clock to the maximum of nodes in the cluster';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_internal_adjust_local_clock_to_remote(pg_catalog.cluster_clock) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_adjust_local_clock_to_remote/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.adjust_local_clock_to_remote(pg_catalog.cluster_clock)\n    RETURNS void\n    LANGUAGE C STABLE PARALLEL SAFE STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_adjust_local_clock_to_remote$$;\nCOMMENT ON FUNCTION citus_internal.adjust_local_clock_to_remote(pg_catalog.cluster_clock)\n    IS 'Internal UDF used to adjust the local clock to the maximum of nodes in the cluster';\n\nREVOKE ALL ON FUNCTION citus_internal.adjust_local_clock_to_remote(pg_catalog.cluster_clock) FROM PUBLIC;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_adjust_local_clock_to_remote(pg_catalog.cluster_clock)\n    RETURNS void\n    LANGUAGE C STABLE PARALLEL SAFE STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_adjust_local_clock_to_remote$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_adjust_local_clock_to_remote(pg_catalog.cluster_clock)\n    IS 'Internal UDF used to adjust the local clock to the maximum of nodes in the cluster';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_internal_adjust_local_clock_to_remote(pg_catalog.cluster_clock) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_adjust_local_clock_to_remote/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.adjust_local_clock_to_remote(pg_catalog.cluster_clock)\n    RETURNS void\n    LANGUAGE C STABLE PARALLEL SAFE STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_adjust_local_clock_to_remote$$;\nCOMMENT ON FUNCTION citus_internal.adjust_local_clock_to_remote(pg_catalog.cluster_clock)\n    IS 'Internal UDF used to adjust the local clock to the maximum of nodes in the cluster';\n\nREVOKE ALL ON FUNCTION citus_internal.adjust_local_clock_to_remote(pg_catalog.cluster_clock) FROM PUBLIC;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_adjust_local_clock_to_remote(pg_catalog.cluster_clock)\n    RETURNS void\n    LANGUAGE C STABLE PARALLEL SAFE STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_adjust_local_clock_to_remote$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_adjust_local_clock_to_remote(pg_catalog.cluster_clock)\n    IS 'Internal UDF used to adjust the local clock to the maximum of nodes in the cluster';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_internal_adjust_local_clock_to_remote(pg_catalog.cluster_clock) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_copy_single_shard_placement/13.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.citus_internal_copy_single_shard_placement(\n\tshard_id bigint,\n\tsource_node_id integer,\n\ttarget_node_id integer,\n\tflags integer,\n\ttransfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_copy_single_shard_placement$$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_copy_single_shard_placement/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.citus_internal_copy_single_shard_placement(\n\tshard_id bigint,\n\tsource_node_id integer,\n\ttarget_node_id integer,\n\tflags integer,\n\ttransfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_copy_single_shard_placement$$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_database_command/13.1-1.sql",
    "content": "--\n-- citus_internal.database_command run given database command without transaction block restriction.\n\nCREATE OR REPLACE FUNCTION citus_internal.database_command(command text)\n RETURNS void\n LANGUAGE C\n VOLATILE\nAS 'MODULE_PATHNAME', $$citus_internal_database_command$$;\nCOMMENT ON FUNCTION citus_internal.database_command(text) IS\n 'run a database command without transaction block restrictions';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_database_command/latest.sql",
    "content": "--\n-- citus_internal.database_command run given database command without transaction block restriction.\n\nCREATE OR REPLACE FUNCTION citus_internal.database_command(command text)\n RETURNS void\n LANGUAGE C\n VOLATILE\nAS 'MODULE_PATHNAME', $$citus_internal_database_command$$;\nCOMMENT ON FUNCTION citus_internal.database_command(text) IS\n 'run a database command without transaction block restrictions';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_colocation_metadata/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_colocation_metadata(\n\t\t\t\t\t\t\tcolocation_id int)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_colocation_metadata(int) IS\n    'deletes a co-location group from pg_dist_colocation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_colocation_metadata/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.delete_colocation_metadata(\n\t\t\t\t\t\t\tcolocation_id int)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_delete_colocation_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.delete_colocation_metadata(int) IS\n    'deletes a co-location group from pg_dist_colocation';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_colocation_metadata(\n\t\t\t\t\t\t\tcolocation_id int)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_colocation_metadata(int) IS\n    'deletes a co-location group from pg_dist_colocation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_colocation_metadata/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.delete_colocation_metadata(\n\t\t\t\t\t\t\tcolocation_id int)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_delete_colocation_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.delete_colocation_metadata(int) IS\n    'deletes a co-location group from pg_dist_colocation';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_colocation_metadata(\n\t\t\t\t\t\t\tcolocation_id int)\n    RETURNS void\n    LANGUAGE C\n    STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_colocation_metadata(int) IS\n    'deletes a co-location group from pg_dist_colocation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_partition_metadata/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_partition_metadata(table_name regclass)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_partition_metadata(regclass) IS\n    'Deletes a row from pg_dist_partition with table ownership checks';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_partition_metadata/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.delete_partition_metadata(table_name regclass)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_delete_partition_metadata$$;\nCOMMENT ON FUNCTION citus_internal.delete_partition_metadata(regclass) IS\n    'Deletes a row from pg_dist_partition with table ownership checks';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_partition_metadata(table_name regclass)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_partition_metadata(regclass) IS\n    'Deletes a row from pg_dist_partition with table ownership checks';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_partition_metadata/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.delete_partition_metadata(table_name regclass)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_delete_partition_metadata$$;\nCOMMENT ON FUNCTION citus_internal.delete_partition_metadata(regclass) IS\n    'Deletes a row from pg_dist_partition with table ownership checks';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_partition_metadata(table_name regclass)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_partition_metadata(regclass) IS\n    'Deletes a row from pg_dist_partition with table ownership checks';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_placement_metadata/12.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_placement_metadata(\n    placement_id bigint)\nRETURNS void\nLANGUAGE C\nVOLATILE\nAS 'MODULE_PATHNAME',\n$$citus_internal_delete_placement_metadata$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_placement_metadata(bigint)\n    IS 'Delete placement with given id from pg_dist_placement metadata table.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_placement_metadata/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.delete_placement_metadata(\n    placement_id bigint)\nRETURNS void\nLANGUAGE C\nVOLATILE\nAS 'MODULE_PATHNAME',\n$$citus_internal_delete_placement_metadata$$;\nCOMMENT ON FUNCTION citus_internal.delete_placement_metadata(bigint)\n    IS 'Delete placement with given id from pg_dist_placement metadata table.';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_placement_metadata(\n    placement_id bigint)\nRETURNS void\nLANGUAGE C\nVOLATILE\nAS 'MODULE_PATHNAME',\n$$citus_internal_delete_placement_metadata$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_placement_metadata(bigint)\n    IS 'Delete placement with given id from pg_dist_placement metadata table.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_placement_metadata/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.delete_placement_metadata(\n    placement_id bigint)\nRETURNS void\nLANGUAGE C\nVOLATILE\nAS 'MODULE_PATHNAME',\n$$citus_internal_delete_placement_metadata$$;\nCOMMENT ON FUNCTION citus_internal.delete_placement_metadata(bigint)\n    IS 'Delete placement with given id from pg_dist_placement metadata table.';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_placement_metadata(\n    placement_id bigint)\nRETURNS void\nLANGUAGE C\nVOLATILE\nAS 'MODULE_PATHNAME',\n$$citus_internal_delete_placement_metadata$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_placement_metadata(bigint)\n    IS 'Delete placement with given id from pg_dist_placement metadata table.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_shard_metadata/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_shard_metadata(shard_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_shard_metadata(bigint) IS\n    'Deletes rows from pg_dist_shard and pg_dist_shard_placement with user checks';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_shard_metadata/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.delete_shard_metadata(shard_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_delete_shard_metadata$$;\nCOMMENT ON FUNCTION citus_internal.delete_shard_metadata(bigint) IS\n    'Deletes rows from pg_dist_shard and pg_dist_shard_placement with user checks';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_shard_metadata(shard_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_shard_metadata(bigint) IS\n    'Deletes rows from pg_dist_shard and pg_dist_shard_placement with user checks';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_shard_metadata/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.delete_shard_metadata(shard_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_delete_shard_metadata$$;\nCOMMENT ON FUNCTION citus_internal.delete_shard_metadata(bigint) IS\n    'Deletes rows from pg_dist_shard and pg_dist_shard_placement with user checks';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_shard_metadata(shard_id bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_shard_metadata(bigint) IS\n    'Deletes rows from pg_dist_shard and pg_dist_shard_placement with user checks';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/12.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_tenant_schema(schema_id Oid)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_tenant_schema(Oid) IS\n    'delete given tenant schema from pg_dist_schema';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.delete_tenant_schema(schema_id Oid)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME', $$citus_internal_delete_tenant_schema$$;\n\nCOMMENT ON FUNCTION citus_internal.delete_tenant_schema(Oid) IS\n    'delete given tenant schema from pg_dist_schema';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_tenant_schema(schema_id Oid)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_tenant_schema(Oid) IS\n    'delete given tenant schema from pg_dist_schema';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.delete_tenant_schema(schema_id Oid)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME', $$citus_internal_delete_tenant_schema$$;\n\nCOMMENT ON FUNCTION citus_internal.delete_tenant_schema(Oid) IS\n    'delete given tenant schema from pg_dist_schema';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_tenant_schema(schema_id Oid)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_delete_tenant_schema(Oid) IS\n    'delete given tenant schema from pg_dist_schema';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_global_blocked_processes/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_global_blocked_processes(\n\t\t\t\tOUT waiting_global_pid int8,\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n\t\t\t\tOUT blocking_global_pid int8,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$citus_internal_global_blocked_processes$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_global_blocked_processes()\nIS 'returns a global list of blocked backends originating from this node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_global_blocked_processes/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.global_blocked_processes(\n\t\t\t\tOUT waiting_global_pid int8,\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n\t\t\t\tOUT blocking_global_pid int8,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$citus_internal_global_blocked_processes$$;\nCOMMENT ON FUNCTION citus_internal.global_blocked_processes()\nIS 'returns a global list of blocked backends originating from this node';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_global_blocked_processes(\n\t\t\t\tOUT waiting_global_pid int8,\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n\t\t\t\tOUT blocking_global_pid int8,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$citus_internal_global_blocked_processes$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_global_blocked_processes()\nIS 'returns a global list of blocked backends originating from this node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_global_blocked_processes/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.global_blocked_processes(\n\t\t\t\tOUT waiting_global_pid int8,\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n\t\t\t\tOUT blocking_global_pid int8,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$citus_internal_global_blocked_processes$$;\nCOMMENT ON FUNCTION citus_internal.global_blocked_processes()\nIS 'returns a global list of blocked backends originating from this node';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_global_blocked_processes(\n\t\t\t\tOUT waiting_global_pid int8,\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n\t\t\t\tOUT blocking_global_pid int8,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$citus_internal_global_blocked_processes$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_global_blocked_processes()\nIS 'returns a global list of blocked backends originating from this node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_local_blocked_processes/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_local_blocked_processes(\n\t\t    OUT waiting_global_pid int8,\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n                    OUT blocking_global_pid int8,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$citus_internal_local_blocked_processes$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_local_blocked_processes()\nIS 'returns all local lock wait chains, that start from any citus backend';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_local_blocked_processes/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.local_blocked_processes(\n\t\t    OUT waiting_global_pid int8,\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n                    OUT blocking_global_pid int8,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$citus_internal_local_blocked_processes$$;\nCOMMENT ON FUNCTION citus_internal.local_blocked_processes()\nIS 'returns all local lock wait chains, that start from any citus backend';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_local_blocked_processes(\n\t\t    OUT waiting_global_pid int8,\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n                    OUT blocking_global_pid int8,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$citus_internal_local_blocked_processes$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_local_blocked_processes()\nIS 'returns all local lock wait chains, that start from any citus backend';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_local_blocked_processes/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.local_blocked_processes(\n\t\t    OUT waiting_global_pid int8,\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n                    OUT blocking_global_pid int8,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$citus_internal_local_blocked_processes$$;\nCOMMENT ON FUNCTION citus_internal.local_blocked_processes()\nIS 'returns all local lock wait chains, that start from any citus backend';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_local_blocked_processes(\n\t\t    OUT waiting_global_pid int8,\n                    OUT waiting_pid int4,\n                    OUT waiting_node_id int4,\n                    OUT waiting_transaction_num int8,\n                    OUT waiting_transaction_stamp timestamptz,\n                    OUT blocking_global_pid int8,\n                    OUT blocking_pid int4,\n                    OUT blocking_node_id int4,\n                    OUT blocking_transaction_num int8,\n                    OUT blocking_transaction_stamp timestamptz,\n                    OUT blocking_transaction_waiting bool)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS $$MODULE_PATHNAME$$, $$citus_internal_local_blocked_processes$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_local_blocked_processes()\nIS 'returns all local lock wait chains, that start from any citus backend';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_mark_node_not_synced/11.3-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_mark_node_not_synced(parent_pid int, nodeid int)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_mark_node_not_synced$$;\nCOMMENT ON FUNCTION citus_internal_mark_node_not_synced(int, int)\n    IS 'marks given node not synced by unsetting metadatasynced column at the start of the nontransactional sync.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_mark_node_not_synced/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.mark_node_not_synced(parent_pid int, nodeid int)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_mark_node_not_synced$$;\nCOMMENT ON FUNCTION citus_internal.mark_node_not_synced(int, int)\n    IS 'marks given node not synced by unsetting metadatasynced column at the start of the nontransactional sync.';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_mark_node_not_synced(parent_pid int, nodeid int)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_mark_node_not_synced$$;\nCOMMENT ON FUNCTION citus_internal_mark_node_not_synced(int, int)\n    IS 'marks given node not synced by unsetting metadatasynced column at the start of the nontransactional sync.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_mark_node_not_synced/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.mark_node_not_synced(parent_pid int, nodeid int)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_mark_node_not_synced$$;\nCOMMENT ON FUNCTION citus_internal.mark_node_not_synced(int, int)\n    IS 'marks given node not synced by unsetting metadatasynced column at the start of the nontransactional sync.';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_mark_node_not_synced(parent_pid int, nodeid int)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_mark_node_not_synced$$;\nCOMMENT ON FUNCTION citus_internal_mark_node_not_synced(int, int)\n    IS 'marks given node not synced by unsetting metadatasynced column at the start of the nontransactional sync.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_unregister_tenant_schema_globally/12.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(schema_id Oid, schema_name text)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(schema_id Oid, schema_name text) IS\n    'Delete a tenant schema and the corresponding colocation group from metadata tables.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_unregister_tenant_schema_globally/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.unregister_tenant_schema_globally(schema_id Oid, schema_name text)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME', $$citus_internal_unregister_tenant_schema_globally$$;\nCOMMENT ON FUNCTION citus_internal.unregister_tenant_schema_globally(schema_id Oid, schema_name text) IS\n    'Delete a tenant schema and the corresponding colocation group from metadata tables.';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(schema_id Oid, schema_name text)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(schema_id Oid, schema_name text) IS\n    'Delete a tenant schema and the corresponding colocation group from metadata tables.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_unregister_tenant_schema_globally/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.unregister_tenant_schema_globally(schema_id Oid, schema_name text)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME', $$citus_internal_unregister_tenant_schema_globally$$;\nCOMMENT ON FUNCTION citus_internal.unregister_tenant_schema_globally(schema_id Oid, schema_name text) IS\n    'Delete a tenant schema and the corresponding colocation group from metadata tables.';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(schema_id Oid, schema_name text)\n    RETURNS void\n    LANGUAGE C\n    VOLATILE\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(schema_id Oid, schema_name text) IS\n    'Delete a tenant schema and the corresponding colocation group from metadata tables.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_update_none_dist_table_metadata/12.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_update_none_dist_table_metadata(\n    relation_id oid,\n    replication_model \"char\",\n    colocation_id bigint,\n    auto_converted boolean)\nRETURNS void\nLANGUAGE C\nVOLATILE\nAS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_update_none_dist_table_metadata(oid, \"char\", bigint, boolean)\n    IS 'Update pg_dist_partition metadata table for given none-distributed table, to convert it to another type of none-distributed table.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_update_none_dist_table_metadata/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.update_none_dist_table_metadata(\n    relation_id oid,\n    replication_model \"char\",\n    colocation_id bigint,\n    auto_converted boolean)\nRETURNS void\nLANGUAGE C\nVOLATILE\nAS 'MODULE_PATHNAME', $$citus_internal_update_none_dist_table_metadata$$;\nCOMMENT ON FUNCTION citus_internal.update_none_dist_table_metadata(oid, \"char\", bigint, boolean)\n    IS 'Update pg_dist_partition metadata table for given none-distributed table, to convert it to another type of none-distributed table.';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_update_none_dist_table_metadata(\n    relation_id oid,\n    replication_model \"char\",\n    colocation_id bigint,\n    auto_converted boolean)\nRETURNS void\nLANGUAGE C\nVOLATILE\nAS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_update_none_dist_table_metadata(oid, \"char\", bigint, boolean)\n    IS 'Update pg_dist_partition metadata table for given none-distributed table, to convert it to another type of none-distributed table.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_update_none_dist_table_metadata/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.update_none_dist_table_metadata(\n    relation_id oid,\n    replication_model \"char\",\n    colocation_id bigint,\n    auto_converted boolean)\nRETURNS void\nLANGUAGE C\nVOLATILE\nAS 'MODULE_PATHNAME', $$citus_internal_update_none_dist_table_metadata$$;\nCOMMENT ON FUNCTION citus_internal.update_none_dist_table_metadata(oid, \"char\", bigint, boolean)\n    IS 'Update pg_dist_partition metadata table for given none-distributed table, to convert it to another type of none-distributed table.';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_update_none_dist_table_metadata(\n    relation_id oid,\n    replication_model \"char\",\n    colocation_id bigint,\n    auto_converted boolean)\nRETURNS void\nLANGUAGE C\nVOLATILE\nAS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_update_none_dist_table_metadata(oid, \"char\", bigint, boolean)\n    IS 'Update pg_dist_partition metadata table for given none-distributed table, to convert it to another type of none-distributed table.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_update_placement_metadata/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_update_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint, source_group_id integer,\n\t\t\t\t\t\t\ttarget_group_id integer)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_update_placement_metadata(bigint, integer, integer) IS\n    'Updates into pg_dist_placement with user checks';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_update_placement_metadata/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.update_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint, source_group_id integer,\n\t\t\t\t\t\t\ttarget_group_id integer)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_update_placement_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.update_placement_metadata(bigint, integer, integer) IS\n    'Updates into pg_dist_placement with user checks';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_update_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint, source_group_id integer,\n\t\t\t\t\t\t\ttarget_group_id integer)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_update_placement_metadata(bigint, integer, integer) IS\n    'Updates into pg_dist_placement with user checks';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_update_placement_metadata/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.update_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint, source_group_id integer,\n\t\t\t\t\t\t\ttarget_group_id integer)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_update_placement_metadata$$;\n\nCOMMENT ON FUNCTION citus_internal.update_placement_metadata(bigint, integer, integer) IS\n    'Updates into pg_dist_placement with user checks';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_update_placement_metadata(\n\t\t\t\t\t\t\tshard_id bigint, source_group_id integer,\n\t\t\t\t\t\t\ttarget_group_id integer)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\n\nCOMMENT ON FUNCTION pg_catalog.citus_internal_update_placement_metadata(bigint, integer, integer) IS\n    'Updates into pg_dist_placement with user checks';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_update_relation_colocation/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_update_relation_colocation(relation_id Oid, target_colocation_id int)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_update_relation_colocation(oid, int) IS\n    'Updates colocationId field of pg_dist_partition for the relation_id';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_update_relation_colocation/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.update_relation_colocation(relation_id Oid, target_colocation_id int)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_update_relation_colocation$$;\nCOMMENT ON FUNCTION citus_internal.update_relation_colocation(oid, int) IS\n    'Updates colocationId field of pg_dist_partition for the relation_id';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_update_relation_colocation(relation_id Oid, target_colocation_id int)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_update_relation_colocation(oid, int) IS\n    'Updates colocationId field of pg_dist_partition for the relation_id';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_internal_update_relation_colocation/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.update_relation_colocation(relation_id Oid, target_colocation_id int)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_internal_update_relation_colocation$$;\nCOMMENT ON FUNCTION citus_internal.update_relation_colocation(oid, int) IS\n    'Updates colocationId field of pg_dist_partition for the relation_id';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_update_relation_colocation(relation_id Oid, target_colocation_id int)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME';\nCOMMENT ON FUNCTION pg_catalog.citus_internal_update_relation_colocation(oid, int) IS\n    'Updates colocationId field of pg_dist_partition for the relation_id';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_is_clock_after/11.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_is_clock_after(clock_one pg_catalog.cluster_clock, clock_two pg_catalog.cluster_clock)\n    RETURNS BOOL\n    LANGUAGE C STABLE PARALLEL SAFE STRICT\n    AS 'MODULE_PATHNAME',$$citus_is_clock_after$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_is_clock_after(pg_catalog.cluster_clock, pg_catalog.cluster_clock)\n    IS 'Accepts logical clock timestamps of two causally related events and returns true if the argument1 happened before argument2';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_is_clock_after/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_is_clock_after(clock_one pg_catalog.cluster_clock, clock_two pg_catalog.cluster_clock)\n    RETURNS BOOL\n    LANGUAGE C STABLE PARALLEL SAFE STRICT\n    AS 'MODULE_PATHNAME',$$citus_is_clock_after$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_is_clock_after(pg_catalog.cluster_clock, pg_catalog.cluster_clock)\n    IS 'Accepts logical clock timestamps of two causally related events and returns true if the argument1 happened before argument2';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_is_coordinator/11.0-2.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_is_coordinator()\n RETURNS bool\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $$citus_is_coordinator$$;\nCOMMENT ON FUNCTION pg_catalog.citus_is_coordinator()\n IS 'returns whether the current node is a coordinator';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_is_coordinator/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_is_coordinator()\n RETURNS bool\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $$citus_is_coordinator$$;\nCOMMENT ON FUNCTION pg_catalog.citus_is_coordinator()\n IS 'returns whether the current node is a coordinator';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_is_primary_node/13.1-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_is_primary_node()\n RETURNS bool\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $$citus_is_primary_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_is_primary_node()\n IS 'returns whether the current node is the primary node in the group';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_is_primary_node/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_is_primary_node()\n RETURNS bool\n LANGUAGE c\n STRICT\nAS 'MODULE_PATHNAME', $$citus_is_primary_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_is_primary_node()\n IS 'returns whether the current node is the primary node in the group';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_isolation_test_session_is_blocked/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_isolation_test_session_is_blocked(pBlockedPid integer, pInterestingPids integer[])\nRETURNS boolean AS $$\n  DECLARE\n    mBlockedGlobalPid int8;\n    workerProcessId integer := current_setting('citus.isolation_test_session_remote_process_id');\n    coordinatorProcessId integer := current_setting('citus.isolation_test_session_process_id');\n  BEGIN\n    IF pg_catalog.old_pg_isolation_test_session_is_blocked(pBlockedPid, pInterestingPids) THEN\n      RETURN true;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    -- Note that worker process may be blocked or waiting for a lock. So we need to\n    -- get transaction number for both of them. Following IF provides the transaction\n    -- number when the worker process waiting for other session.\n    IF EXISTS (SELECT 1 FROM get_global_active_transactions()\n               WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId) THEN\n      SELECT global_pid INTO mBlockedGlobalPid FROM get_global_active_transactions()\n      WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId;\n    ELSE\n      -- Check whether transactions initiated from the coordinator get locked\n      SELECT global_pid INTO mBlockedGlobalPid\n        FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n    END IF;\n\n    RETURN EXISTS (\n      SELECT 1 FROM citus_internal_global_blocked_processes()\n        WHERE waiting_global_pid = mBlockedGlobalPid\n    ) OR EXISTS (\n      -- Check on the workers if any logical replication job spawned by the\n      -- current PID is blocked, by checking it's application name\n      -- Query is heavily based on: https://wiki.postgresql.org/wiki/Lock_Monitoring\n      SELECT result FROM run_command_on_workers($two$\n        SELECT blocked_activity.application_name AS blocked_application\n           FROM  pg_catalog.pg_locks         blocked_locks\n            JOIN pg_catalog.pg_stat_activity blocked_activity  ON blocked_activity.pid = blocked_locks.pid\n            JOIN pg_catalog.pg_locks         blocking_locks\n                ON blocking_locks.locktype = blocked_locks.locktype\n                AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE\n                AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n                AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n                AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n                AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n                AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n                AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n                AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n                AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n                AND blocking_locks.pid != blocked_locks.pid\n            JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n           WHERE NOT blocked_locks.GRANTED AND blocked_activity.application_name LIKE 'citus_shard_move_subscription_%'\n        $two$) where result LIKE 'citus_shard_move_subscription_%_' || pBlockedPid);\n\n  END;\n$$ LANGUAGE plpgsql;\n\nREVOKE ALL ON FUNCTION citus_isolation_test_session_is_blocked(integer,integer[]) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_isolation_test_session_is_blocked/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_isolation_test_session_is_blocked(pBlockedPid integer, pInterestingPids integer[])\nRETURNS boolean AS $$\n  DECLARE\n    mBlockedGlobalPid int8;\n    workerProcessId integer := current_setting('citus.isolation_test_session_remote_process_id');\n    coordinatorProcessId integer := current_setting('citus.isolation_test_session_process_id');\n  BEGIN\n    IF pg_catalog.old_pg_isolation_test_session_is_blocked(pBlockedPid, pInterestingPids) THEN\n      RETURN true;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    -- Note that worker process may be blocked or waiting for a lock. So we need to\n    -- get transaction number for both of them. Following IF provides the transaction\n    -- number when the worker process waiting for other session.\n    IF EXISTS (SELECT 1 FROM get_global_active_transactions()\n               WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId) THEN\n      SELECT global_pid INTO mBlockedGlobalPid FROM get_global_active_transactions()\n      WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId;\n    ELSE\n      -- Check whether transactions initiated from the coordinator get locked\n      SELECT global_pid INTO mBlockedGlobalPid\n        FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n    END IF;\n\n    RETURN EXISTS (\n      SELECT 1 FROM citus_internal_global_blocked_processes()\n        WHERE waiting_global_pid = mBlockedGlobalPid\n    );\n  END;\n$$ LANGUAGE plpgsql;\n\nREVOKE ALL ON FUNCTION citus_isolation_test_session_is_blocked(integer,integer[]) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_isolation_test_session_is_blocked/11.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_isolation_test_session_is_blocked(pBlockedPid integer, pInterestingPids integer[])\nRETURNS boolean AS $$\n  DECLARE\n    mBlockedGlobalPid int8;\n    workerProcessId integer := current_setting('citus.isolation_test_session_remote_process_id');\n    coordinatorProcessId integer := current_setting('citus.isolation_test_session_process_id');\n  BEGIN\n    IF pg_catalog.old_pg_isolation_test_session_is_blocked(pBlockedPid, pInterestingPids) THEN\n      RETURN true;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    -- Note that worker process may be blocked or waiting for a lock. So we need to\n    -- get transaction number for both of them. Following IF provides the transaction\n    -- number when the worker process waiting for other session.\n    IF EXISTS (SELECT 1 FROM get_global_active_transactions()\n               WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId) THEN\n      SELECT global_pid INTO mBlockedGlobalPid FROM get_global_active_transactions()\n      WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId;\n    ELSE\n      -- Check whether transactions initiated from the coordinator get locked\n      SELECT global_pid INTO mBlockedGlobalPid\n        FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n    END IF;\n\n    -- We convert the blocking_global_pid to a regular pid and only look at\n    -- blocks caused by the interesting pids, or the workerProcessPid. If we\n    -- don't do that we might find unrelated blocks caused by some random\n    -- other processes that are not involved in this isolation test. Because we\n    -- run our isolation tests on a single physical machine, the PID part of\n    -- the GPID is known to be unique within the whole cluster.\n    RETURN EXISTS (\n      SELECT 1 FROM citus_internal_global_blocked_processes()\n        WHERE waiting_global_pid = mBlockedGlobalPid\n        AND (\n          citus_pid_for_gpid(blocking_global_pid) in (\n              select * from unnest(pInterestingPids)\n          )\n          OR citus_pid_for_gpid(blocking_global_pid) = workerProcessId\n        )\n    );\n  END;\n$$ LANGUAGE plpgsql;\n\nREVOKE ALL ON FUNCTION citus_isolation_test_session_is_blocked(integer,integer[]) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_isolation_test_session_is_blocked/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_isolation_test_session_is_blocked(pBlockedPid integer, pInterestingPids integer[])\nRETURNS boolean AS $$\n  DECLARE\n    mBlockedGlobalPid int8;\n    workerProcessId integer := current_setting('citus.isolation_test_session_remote_process_id');\n    coordinatorProcessId integer := current_setting('citus.isolation_test_session_process_id');\n  BEGIN\n    IF pg_catalog.old_pg_isolation_test_session_is_blocked(pBlockedPid, pInterestingPids) THEN\n      RETURN true;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    -- Note that worker process may be blocked or waiting for a lock. So we need to\n    -- get transaction number for both of them. Following IF provides the transaction\n    -- number when the worker process waiting for other session.\n    IF EXISTS (SELECT 1 FROM get_global_active_transactions()\n               WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId) THEN\n      SELECT global_pid INTO mBlockedGlobalPid FROM get_global_active_transactions()\n      WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId;\n    ELSE\n      -- Check whether transactions initiated from the coordinator get locked\n      SELECT global_pid INTO mBlockedGlobalPid\n        FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n    END IF;\n\n    -- We convert the blocking_global_pid to a regular pid and only look at\n    -- blocks caused by the interesting pids, or the workerProcessPid. If we\n    -- don't do that we might find unrelated blocks caused by some random\n    -- other processes that are not involved in this isolation test. Because we\n    -- run our isolation tests on a single physical machine, the PID part of\n    -- the GPID is known to be unique within the whole cluster.\n    RETURN EXISTS (\n      SELECT 1 FROM citus_internal.global_blocked_processes()\n        WHERE waiting_global_pid = mBlockedGlobalPid\n        AND (\n          citus_pid_for_gpid(blocking_global_pid) in (\n              select * from unnest(pInterestingPids)\n          )\n          OR citus_pid_for_gpid(blocking_global_pid) = workerProcessId\n        )\n    );\n  END;\n$$ LANGUAGE plpgsql;\n\nREVOKE ALL ON FUNCTION citus_isolation_test_session_is_blocked(integer,integer[]) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_isolation_test_session_is_blocked/8.0-6.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_isolation_test_session_is_blocked(pBlockedPid integer, pInterestingPids integer[])\nRETURNS boolean AS $$\n  DECLARE\n    mBlockedTransactionNum int8;\n    workerProcessId integer := current_setting('citus.isolation_test_session_remote_process_id');\n    coordinatorProcessId integer := current_setting('citus.isolation_test_session_process_id');\n  BEGIN\n    IF pg_catalog.old_pg_isolation_test_session_is_blocked(pBlockedPid, pInterestingPids) THEN\n      RETURN true;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    -- Note that worker process may be blocked or waiting for a lock. So we need to\n    -- get transaction number for both of them. Following IF provides the transaction\n    -- number when the worker process waiting for other session.\n    IF EXISTS (SELECT transaction_number FROM get_global_active_transactions()\n               WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId) THEN\n      SELECT transaction_number INTO mBlockedTransactionNum FROM get_global_active_transactions()\n      WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId;\n    ELSE\n      -- Check whether transactions initiated from the coordinator get locked\n      SELECT transaction_number INTO mBlockedTransactionNum\n        FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n    END IF;\n\n    RETURN EXISTS (\n      SELECT 1 FROM dump_global_wait_edges()\n        WHERE waiting_transaction_num = mBlockedTransactionNum\n    );\n\n  END;\n$$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_isolation_test_session_is_blocked/9.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_isolation_test_session_is_blocked(pBlockedPid integer, pInterestingPids integer[])\nRETURNS boolean AS $$\n  DECLARE\n    mBlockedTransactionNum int8;\n    workerProcessId integer := current_setting('citus.isolation_test_session_remote_process_id');\n    coordinatorProcessId integer := current_setting('citus.isolation_test_session_process_id');\n  BEGIN\n    IF pg_catalog.old_pg_isolation_test_session_is_blocked(pBlockedPid, pInterestingPids) THEN\n      RETURN true;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    -- Note that worker process may be blocked or waiting for a lock. So we need to\n    -- get transaction number for both of them. Following IF provides the transaction\n    -- number when the worker process waiting for other session.\n    IF EXISTS (SELECT transaction_number FROM get_global_active_transactions()\n               WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId) THEN\n      SELECT transaction_number INTO mBlockedTransactionNum FROM get_global_active_transactions()\n      WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId;\n    ELSE\n      -- Check whether transactions initiated from the coordinator get locked\n      SELECT transaction_number INTO mBlockedTransactionNum\n        FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n    END IF;\n\n    RETURN EXISTS (\n      SELECT 1 FROM dump_global_wait_edges()\n        WHERE waiting_transaction_num = mBlockedTransactionNum\n    ) OR EXISTS (\n      -- Check on the workers if any logical replication job spawned by the\n      -- current PID is blocked, by checking it's application name\n      -- Query is heavily based on: https://wiki.postgresql.org/wiki/Lock_Monitoring\n      SELECT result FROM run_command_on_workers($two$\n        SELECT blocked_activity.application_name AS blocked_application\n           FROM  pg_catalog.pg_locks         blocked_locks\n            JOIN pg_catalog.pg_stat_activity blocked_activity  ON blocked_activity.pid = blocked_locks.pid\n            JOIN pg_catalog.pg_locks         blocking_locks\n                ON blocking_locks.locktype = blocked_locks.locktype\n                AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE\n                AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation\n                AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page\n                AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple\n                AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid\n                AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid\n                AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid\n                AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid\n                AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid\n                AND blocking_locks.pid != blocked_locks.pid\n            JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid\n           WHERE NOT blocked_locks.GRANTED AND blocked_activity.application_name LIKE 'citus_shard_move_subscription_%'\n        $two$) where result LIKE 'citus_shard_move_subscription_%_' || pBlockedPid);\n\n  END;\n$$ LANGUAGE plpgsql;\n\nREVOKE ALL ON FUNCTION citus_isolation_test_session_is_blocked(integer,integer[]) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_isolation_test_session_is_blocked/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_isolation_test_session_is_blocked(pBlockedPid integer, pInterestingPids integer[])\nRETURNS boolean AS $$\n  DECLARE\n    mBlockedGlobalPid int8;\n    workerProcessId integer := current_setting('citus.isolation_test_session_remote_process_id');\n    coordinatorProcessId integer := current_setting('citus.isolation_test_session_process_id');\n  BEGIN\n    IF pg_catalog.old_pg_isolation_test_session_is_blocked(pBlockedPid, pInterestingPids) THEN\n      RETURN true;\n    END IF;\n\n    -- pg says we're not blocked locally; check whether we're blocked globally.\n    -- Note that worker process may be blocked or waiting for a lock. So we need to\n    -- get transaction number for both of them. Following IF provides the transaction\n    -- number when the worker process waiting for other session.\n    IF EXISTS (SELECT 1 FROM get_global_active_transactions()\n               WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId) THEN\n      SELECT global_pid INTO mBlockedGlobalPid FROM get_global_active_transactions()\n      WHERE process_id = workerProcessId AND pBlockedPid = coordinatorProcessId;\n    ELSE\n      -- Check whether transactions initiated from the coordinator get locked\n      SELECT global_pid INTO mBlockedGlobalPid\n        FROM get_all_active_transactions() WHERE process_id = pBlockedPid;\n    END IF;\n\n    -- We convert the blocking_global_pid to a regular pid and only look at\n    -- blocks caused by the interesting pids, or the workerProcessPid. If we\n    -- don't do that we might find unrelated blocks caused by some random\n    -- other processes that are not involved in this isolation test. Because we\n    -- run our isolation tests on a single physical machine, the PID part of\n    -- the GPID is known to be unique within the whole cluster.\n    RETURN EXISTS (\n      SELECT 1 FROM citus_internal.global_blocked_processes()\n        WHERE waiting_global_pid = mBlockedGlobalPid\n        AND (\n          citus_pid_for_gpid(blocking_global_pid) in (\n              select * from unnest(pInterestingPids)\n          )\n          OR citus_pid_for_gpid(blocking_global_pid) = workerProcessId\n        )\n    );\n  END;\n$$ LANGUAGE plpgsql;\n\nREVOKE ALL ON FUNCTION citus_isolation_test_session_is_blocked(integer,integer[]) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_job_cancel/11.1-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_job_cancel(jobid bigint)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME',$$citus_job_cancel$$;\nCOMMENT ON FUNCTION pg_catalog.citus_job_cancel(jobid bigint)\n    IS 'cancel a scheduled or running job and all of its tasks that didn''t finish yet';\n\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_job_cancel(jobid bigint) TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_job_cancel/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_job_cancel(jobid bigint)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME',$$citus_job_cancel$$;\nCOMMENT ON FUNCTION pg_catalog.citus_job_cancel(jobid bigint)\n    IS 'cancel a scheduled or running job and all of its tasks that didn''t finish yet';\n\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_job_cancel(jobid bigint) TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_job_list/11.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_job_list ()\n    RETURNS TABLE (\n            job_id bigint,\n            state pg_catalog.citus_job_status,\n            job_type name,\n            description text,\n            started_at timestamptz,\n            finished_at timestamptz\n)\n    LANGUAGE SQL\n    AS $fn$\n    SELECT\n        job_id,\n        state,\n        job_type,\n        description,\n        started_at,\n        finished_at\n    FROM\n        pg_dist_background_job\n    ORDER BY\n        job_id\n$fn$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_job_list/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_job_list ()\n    RETURNS TABLE (\n            job_id bigint,\n            state pg_catalog.citus_job_status,\n            job_type name,\n            description text,\n            started_at timestamptz,\n            finished_at timestamptz\n)\n    LANGUAGE SQL\n    AS $fn$\n    SELECT\n        job_id,\n        state,\n        job_type,\n        description,\n        started_at,\n        finished_at\n    FROM\n        pg_dist_background_job\n    ORDER BY\n        job_id\n$fn$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_job_status/11.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_job_status (\n    job_id bigint,\n    raw boolean DEFAULT FALSE\n)\n    RETURNS TABLE (\n            job_id bigint,\n            state pg_catalog.citus_job_status,\n            job_type name,\n            description text,\n            started_at timestamptz,\n            finished_at timestamptz,\n            details jsonb\n    )\n    LANGUAGE SQL\n    STRICT\n    AS $fn$\n    WITH rp AS MATERIALIZED (\n        SELECT\n            sessionid,\n            sum(source_shard_size) as source_shard_size,\n            sum(target_shard_size) as target_shard_size,\n            any_value(status) as status,\n            any_value(sourcename) as sourcename,\n            any_value(sourceport) as sourceport,\n            any_value(targetname) as targetname,\n            any_value(targetport) as targetport,\n            max(source_lsn) as source_lsn,\n            min(target_lsn) as target_lsn\n        FROM get_rebalance_progress()\n        GROUP BY sessionid\n    ),\n    task_state_occurence_counts AS (\n        SELECT t.status, count(task_id)\n        FROM pg_dist_background_job j\n            JOIN pg_dist_background_task t ON t.job_id = j.job_id\n        WHERE j.job_id = $1\n        GROUP BY t.status\n    ),\n    running_task_details AS (\n        SELECT jsonb_agg(jsonb_build_object(\n                'state', t.status,\n                'retried', coalesce(t.retry_count,0),\n                'phase', rp.status,\n                'size' , jsonb_build_object(\n                    'source', rp.source_shard_size,\n                    'target', rp.target_shard_size),\n                'hosts', jsonb_build_object(\n                    'source', rp.sourcename || ':' || rp.sourceport,\n                    'target', rp.targetname || ':' || rp.targetport),\n                'message', t.message,\n                'command', t.command,\n                'task_id', t.task_id ) ||\n            CASE\n                WHEN ($2) THEN jsonb_build_object(\n                    'size', jsonb_build_object(\n                        'source', rp.source_shard_size,\n                        'target', rp.target_shard_size),\n                    'LSN', jsonb_build_object(\n                        'source', rp.source_lsn,\n                        'target', rp.target_lsn,\n                        'lag', rp.source_lsn - rp.target_lsn))\n                ELSE jsonb_build_object(\n                    'size', jsonb_build_object(\n                        'source', pg_size_pretty(rp.source_shard_size),\n                        'target', pg_size_pretty(rp.target_shard_size)),\n                    'LSN', jsonb_build_object(\n                        'source', rp.source_lsn,\n                        'target', rp.target_lsn,\n                        'lag', pg_size_pretty(rp.source_lsn - rp.target_lsn)))\n            END) AS tasks\n        FROM\n            rp JOIN pg_dist_background_task t ON rp.sessionid = t.pid\n            JOIN pg_dist_background_job j ON t.job_id = j.job_id\n        WHERE j.job_id = $1\n            AND t.status = 'running'\n    ),\n    errored_or_retried_task_details AS (\n        SELECT jsonb_agg(jsonb_build_object(\n                'state', t.status,\n                'retried', coalesce(t.retry_count,0),\n                'message', t.message,\n                'command', t.command,\n                'task_id', t.task_id )) AS tasks\n        FROM\n            pg_dist_background_task t JOIN pg_dist_background_job j ON t.job_id = j.job_id\n        WHERE j.job_id = $1\n            AND NOT EXISTS (SELECT 1 FROM rp WHERE rp.sessionid = t.pid)\n            AND (t.status = 'error' OR (t.status = 'runnable' AND t.retry_count > 0))\n    )\n    SELECT\n        job_id,\n        state,\n        job_type,\n        description,\n        started_at,\n        finished_at,\n        jsonb_build_object(\n            'task_state_counts', (SELECT jsonb_object_agg(status, count) FROM task_state_occurence_counts),\n            'tasks', (COALESCE((SELECT tasks FROM running_task_details),'[]'::jsonb) ||\n                      COALESCE((SELECT tasks FROM errored_or_retried_task_details),'[]'::jsonb))) AS details\n    FROM pg_dist_background_job j\n    WHERE j.job_id = $1\n$fn$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_job_status/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_job_status (\n    job_id bigint,\n    raw boolean DEFAULT FALSE\n)\n    RETURNS TABLE (\n            job_id bigint,\n            state pg_catalog.citus_job_status,\n            job_type name,\n            description text,\n            started_at timestamptz,\n            finished_at timestamptz,\n            details jsonb\n    )\n    LANGUAGE SQL\n    STRICT\n    AS $fn$\n    WITH rp AS MATERIALIZED (\n        SELECT\n            sessionid,\n            sum(source_shard_size) as source_shard_size,\n            sum(target_shard_size) as target_shard_size,\n            any_value(status) as status,\n            any_value(sourcename) as sourcename,\n            any_value(sourceport) as sourceport,\n            any_value(targetname) as targetname,\n            any_value(targetport) as targetport,\n            max(source_lsn) as source_lsn,\n            min(target_lsn) as target_lsn\n        FROM get_rebalance_progress()\n        GROUP BY sessionid\n    ),\n    task_state_occurence_counts AS (\n        SELECT t.status, count(task_id)\n        FROM pg_dist_background_job j\n            JOIN pg_dist_background_task t ON t.job_id = j.job_id\n        WHERE j.job_id = $1\n        GROUP BY t.status\n    ),\n    running_task_details AS (\n        SELECT jsonb_agg(jsonb_build_object(\n                'state', t.status,\n                'retried', coalesce(t.retry_count,0),\n                'phase', rp.status,\n                'size' , jsonb_build_object(\n                    'source', rp.source_shard_size,\n                    'target', rp.target_shard_size),\n                'hosts', jsonb_build_object(\n                    'source', rp.sourcename || ':' || rp.sourceport,\n                    'target', rp.targetname || ':' || rp.targetport),\n                'message', t.message,\n                'command', t.command,\n                'task_id', t.task_id ) ||\n            CASE\n                WHEN ($2) THEN jsonb_build_object(\n                    'size', jsonb_build_object(\n                        'source', rp.source_shard_size,\n                        'target', rp.target_shard_size),\n                    'LSN', jsonb_build_object(\n                        'source', rp.source_lsn,\n                        'target', rp.target_lsn,\n                        'lag', rp.source_lsn - rp.target_lsn))\n                ELSE jsonb_build_object(\n                    'size', jsonb_build_object(\n                        'source', pg_size_pretty(rp.source_shard_size),\n                        'target', pg_size_pretty(rp.target_shard_size)),\n                    'LSN', jsonb_build_object(\n                        'source', rp.source_lsn,\n                        'target', rp.target_lsn,\n                        'lag', pg_size_pretty(rp.source_lsn - rp.target_lsn)))\n            END) AS tasks\n        FROM\n            rp JOIN pg_dist_background_task t ON rp.sessionid = t.pid\n            JOIN pg_dist_background_job j ON t.job_id = j.job_id\n        WHERE j.job_id = $1\n            AND t.status = 'running'\n    ),\n    errored_or_retried_task_details AS (\n        SELECT jsonb_agg(jsonb_build_object(\n                'state', t.status,\n                'retried', coalesce(t.retry_count,0),\n                'message', t.message,\n                'command', t.command,\n                'task_id', t.task_id )) AS tasks\n        FROM\n            pg_dist_background_task t JOIN pg_dist_background_job j ON t.job_id = j.job_id\n        WHERE j.job_id = $1\n            AND NOT EXISTS (SELECT 1 FROM rp WHERE rp.sessionid = t.pid)\n            AND (t.status = 'error' OR (t.status = 'runnable' AND t.retry_count > 0))\n    )\n    SELECT\n        job_id,\n        state,\n        job_type,\n        description,\n        started_at,\n        finished_at,\n        jsonb_build_object(\n            'task_state_counts', (SELECT jsonb_object_agg(status, count) FROM task_state_occurence_counts),\n            'tasks', (COALESCE((SELECT tasks FROM running_task_details),'[]'::jsonb) ||\n                      COALESCE((SELECT tasks FROM errored_or_retried_task_details),'[]'::jsonb))) AS details\n    FROM pg_dist_background_job j\n    WHERE j.job_id = $1\n$fn$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_job_wait/11.1-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_job_wait(jobid bigint, desired_status pg_catalog.citus_job_status DEFAULT NULL)\n    RETURNS VOID\n    LANGUAGE C\n    AS 'MODULE_PATHNAME',$$citus_job_wait$$;\nCOMMENT ON FUNCTION pg_catalog.citus_job_wait(jobid bigint, desired_status pg_catalog.citus_job_status)\n    IS 'blocks till the job identified by jobid is at the specified status, or reached a terminal status. Only waits for terminal status when no desired_status was specified. The return value indicates if the desired status was reached or not. When no desired status was specified it will assume any terminal status was desired';\n\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_job_wait(jobid bigint, desired_status pg_catalog.citus_job_status) TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_job_wait/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_job_wait(jobid bigint, desired_status pg_catalog.citus_job_status DEFAULT NULL)\n    RETURNS VOID\n    LANGUAGE C\n    AS 'MODULE_PATHNAME',$$citus_job_wait$$;\nCOMMENT ON FUNCTION pg_catalog.citus_job_wait(jobid bigint, desired_status pg_catalog.citus_job_status)\n    IS 'blocks till the job identified by jobid is at the specified status, or reached a terminal status. Only waits for terminal status when no desired_status was specified. The return value indicates if the desired status was reached or not. When no desired status was specified it will assume any terminal status was desired';\n\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_job_wait(jobid bigint, desired_status pg_catalog.citus_job_status) TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_local_disk_space_stats/10.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_local_disk_space_stats(\n  OUT available_disk_size bigint,\n  OUT total_disk_size bigint)\nRETURNS record\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_local_disk_space_stats$$;\nCOMMENT ON FUNCTION pg_catalog.citus_local_disk_space_stats()\nIS 'returns statistics on available disk space on the local node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_local_disk_space_stats/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_local_disk_space_stats(\n  OUT available_disk_size bigint,\n  OUT total_disk_size bigint)\nRETURNS record\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_local_disk_space_stats$$;\nCOMMENT ON FUNCTION pg_catalog.citus_local_disk_space_stats()\nIS 'returns statistics on available disk space on the local node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_lock_waits/11.0-1.sql",
    "content": "SET search_path = 'pg_catalog';\n\nCREATE VIEW citus.citus_lock_waits AS\nWITH\nunique_global_wait_edges_with_calculated_gpids AS (\nSELECT\n\t\t\t-- if global_pid is NULL, it is most likely that a backend is blocked on a DDL\n\t\t\t-- also for legacy reasons citus_internal_global_blocked_processes() returns groupId, we replace that with nodeIds\n\t\t\tcase WHEN waiting_global_pid  !=0 THEN waiting_global_pid   ELSE citus_calculate_gpid(get_nodeid_for_groupid(waiting_node_id),  waiting_pid)  END waiting_global_pid,\n\t\t\tcase WHEN blocking_global_pid !=0 THEN blocking_global_pid  ELSE citus_calculate_gpid(get_nodeid_for_groupid(blocking_node_id), blocking_pid) END blocking_global_pid,\n\n\t\t\t-- citus_internal_global_blocked_processes returns groupId, we replace it here with actual\n\t\t\t-- nodeId to be consisten with the other views\n\t\t\tget_nodeid_for_groupid(blocking_node_id) as blocking_node_id,\n\t\t\tget_nodeid_for_groupid(waiting_node_id) as waiting_node_id,\n\n\t\t\tblocking_transaction_waiting\n\n\t\t\tFROM citus_internal_global_blocked_processes()\n),\nunique_global_wait_edges AS\n(\n\tSELECT DISTINCT ON(waiting_global_pid, blocking_global_pid) * FROM unique_global_wait_edges_with_calculated_gpids\n),\ncitus_dist_stat_activity_with_calculated_gpids AS\n(\n\t-- if global_pid is NULL, it is most likely that a backend is blocked on a DDL\n\tSELECT CASE WHEN global_pid != 0 THEN global_pid ELSE citus_calculate_gpid(nodeid, pid) END global_pid, nodeid, pid, query FROM citus_dist_stat_activity\n)\nSELECT\n\twaiting.global_pid as waiting_gpid,\n\tblocking.global_pid as blocking_gpid,\n\twaiting.query AS blocked_statement,\n\tblocking.query AS current_statement_in_blocking_process,\n\twaiting.nodeid AS waiting_nodeid,\n\tblocking.nodeid AS blocking_nodeid\nFROM\n\tunique_global_wait_edges\n\t\tJOIN\n\tcitus_dist_stat_activity_with_calculated_gpids waiting ON (unique_global_wait_edges.waiting_global_pid = waiting.global_pid)\n\t\tJOIN\n\tcitus_dist_stat_activity_with_calculated_gpids blocking ON (unique_global_wait_edges.blocking_global_pid = blocking.global_pid);\n\nALTER VIEW citus.citus_lock_waits SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_lock_waits TO PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_lock_waits/13.1-1.sql",
    "content": "SET search_path = 'pg_catalog';\n\nCREATE VIEW citus.citus_lock_waits AS\nWITH\nunique_global_wait_edges_with_calculated_gpids AS (\nSELECT\n\t\t\t-- if global_pid is NULL, it is most likely that a backend is blocked on a DDL\n\t\t\t-- also for legacy reasons citus_internal.global_blocked_processes() returns groupId, we replace that with nodeIds\n\t\t\tcase WHEN waiting_global_pid  !=0 THEN waiting_global_pid   ELSE citus_calculate_gpid(get_nodeid_for_groupid(waiting_node_id),  waiting_pid)  END waiting_global_pid,\n\t\t\tcase WHEN blocking_global_pid !=0 THEN blocking_global_pid  ELSE citus_calculate_gpid(get_nodeid_for_groupid(blocking_node_id), blocking_pid) END blocking_global_pid,\n\n\t\t\t-- citus_internal.global_blocked_processes returns groupId, we replace it here with actual\n\t\t\t-- nodeId to be consisten with the other views\n\t\t\tget_nodeid_for_groupid(blocking_node_id) as blocking_node_id,\n\t\t\tget_nodeid_for_groupid(waiting_node_id) as waiting_node_id,\n\n\t\t\tblocking_transaction_waiting\n\n\t\t\tFROM citus_internal.global_blocked_processes()\n),\nunique_global_wait_edges AS\n(\n\tSELECT DISTINCT ON(waiting_global_pid, blocking_global_pid) * FROM unique_global_wait_edges_with_calculated_gpids\n),\ncitus_dist_stat_activity_with_calculated_gpids AS\n(\n\t-- if global_pid is NULL, it is most likely that a backend is blocked on a DDL\n\tSELECT CASE WHEN global_pid != 0 THEN global_pid ELSE citus_calculate_gpid(nodeid, pid) END global_pid, nodeid, pid, query FROM citus_dist_stat_activity\n)\nSELECT\n\twaiting.global_pid as waiting_gpid,\n\tblocking.global_pid as blocking_gpid,\n\twaiting.query AS blocked_statement,\n\tblocking.query AS current_statement_in_blocking_process,\n\twaiting.nodeid AS waiting_nodeid,\n\tblocking.nodeid AS blocking_nodeid\nFROM\n\tunique_global_wait_edges\n\t\tJOIN\n\tcitus_dist_stat_activity_with_calculated_gpids waiting ON (unique_global_wait_edges.waiting_global_pid = waiting.global_pid)\n\t\tJOIN\n\tcitus_dist_stat_activity_with_calculated_gpids blocking ON (unique_global_wait_edges.blocking_global_pid = blocking.global_pid);\n\nALTER VIEW citus.citus_lock_waits SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_lock_waits TO PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_lock_waits/latest.sql",
    "content": "SET search_path = 'pg_catalog';\n\nCREATE VIEW citus.citus_lock_waits AS\nWITH\nunique_global_wait_edges_with_calculated_gpids AS (\nSELECT\n\t\t\t-- if global_pid is NULL, it is most likely that a backend is blocked on a DDL\n\t\t\t-- also for legacy reasons citus_internal.global_blocked_processes() returns groupId, we replace that with nodeIds\n\t\t\tcase WHEN waiting_global_pid  !=0 THEN waiting_global_pid   ELSE citus_calculate_gpid(get_nodeid_for_groupid(waiting_node_id),  waiting_pid)  END waiting_global_pid,\n\t\t\tcase WHEN blocking_global_pid !=0 THEN blocking_global_pid  ELSE citus_calculate_gpid(get_nodeid_for_groupid(blocking_node_id), blocking_pid) END blocking_global_pid,\n\n\t\t\t-- citus_internal.global_blocked_processes returns groupId, we replace it here with actual\n\t\t\t-- nodeId to be consisten with the other views\n\t\t\tget_nodeid_for_groupid(blocking_node_id) as blocking_node_id,\n\t\t\tget_nodeid_for_groupid(waiting_node_id) as waiting_node_id,\n\n\t\t\tblocking_transaction_waiting\n\n\t\t\tFROM citus_internal.global_blocked_processes()\n),\nunique_global_wait_edges AS\n(\n\tSELECT DISTINCT ON(waiting_global_pid, blocking_global_pid) * FROM unique_global_wait_edges_with_calculated_gpids\n),\ncitus_dist_stat_activity_with_calculated_gpids AS\n(\n\t-- if global_pid is NULL, it is most likely that a backend is blocked on a DDL\n\tSELECT CASE WHEN global_pid != 0 THEN global_pid ELSE citus_calculate_gpid(nodeid, pid) END global_pid, nodeid, pid, query FROM citus_dist_stat_activity\n)\nSELECT\n\twaiting.global_pid as waiting_gpid,\n\tblocking.global_pid as blocking_gpid,\n\twaiting.query AS blocked_statement,\n\tblocking.query AS current_statement_in_blocking_process,\n\twaiting.nodeid AS waiting_nodeid,\n\tblocking.nodeid AS blocking_nodeid\nFROM\n\tunique_global_wait_edges\n\t\tJOIN\n\tcitus_dist_stat_activity_with_calculated_gpids waiting ON (unique_global_wait_edges.waiting_global_pid = waiting.global_pid)\n\t\tJOIN\n\tcitus_dist_stat_activity_with_calculated_gpids blocking ON (unique_global_wait_edges.blocking_global_pid = blocking.global_pid);\n\nALTER VIEW citus.citus_lock_waits SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_lock_waits TO PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_locks/11.1-1.sql",
    "content": "-- citus_locks combines the pg_locks views from all nodes and adds global_pid, nodeid, and\n-- relation_name. The columns of citus_locks don't change based on the Postgres version,\n-- however the pg_locks's columns do. Postgres 14 added one more column to pg_locks\n-- (waitstart timestamptz). citus_locks has the most expansive column set, including the\n-- newly added column. If citus_locks is queried in a Postgres version where pg_locks\n-- doesn't have some columns, the values for those columns in citus_locks will be NULL\nCREATE OR REPLACE FUNCTION pg_catalog.citus_locks (\n    OUT global_pid bigint,\n    OUT nodeid int,\n    OUT locktype text,\n    OUT database oid,\n    OUT relation oid,\n    OUT relation_name text,\n    OUT page integer,\n    OUT tuple smallint,\n    OUT virtualxid text,\n    OUT transactionid xid,\n    OUT classid oid,\n    OUT objid oid,\n    OUT objsubid smallint,\n    OUT virtualtransaction text,\n    OUT pid integer,\n    OUT mode text,\n    OUT granted boolean,\n    OUT fastpath boolean,\n    OUT waitstart timestamp with time zone\n)\n    RETURNS SETOF record\n    LANGUAGE plpgsql\n    AS $function$\nBEGIN\n    RETURN QUERY\n    SELECT *\n    FROM jsonb_to_recordset((\n        SELECT\n            jsonb_agg(all_citus_locks_rows_as_jsonb.citus_locks_row_as_jsonb)::jsonb\n        FROM (\n            SELECT\n                jsonb_array_elements(run_command_on_all_nodes.result::jsonb)::jsonb ||\n                    ('{\"nodeid\":' || run_command_on_all_nodes.nodeid || '}')::jsonb AS citus_locks_row_as_jsonb\n            FROM\n                run_command_on_all_nodes (\n                    $$\n                        SELECT\n                            coalesce(to_jsonb (array_agg(citus_locks_from_one_node.*)), '[{}]'::jsonb)\n                        FROM (\n                            SELECT\n                                global_pid, pg_locks.relation::regclass::text AS relation_name, pg_locks.*\n                            FROM pg_locks\n                        LEFT JOIN get_all_active_transactions () ON process_id = pid) AS citus_locks_from_one_node;\n                    $$,\n                    parallel:= TRUE,\n                    give_warning_for_connection_errors:= TRUE)\n            WHERE\n                success = 't')\n        AS all_citus_locks_rows_as_jsonb))\nAS (\n    global_pid bigint,\n    nodeid int,\n    locktype text,\n    database oid,\n    relation oid,\n    relation_name text,\n    page integer,\n    tuple smallint,\n    virtualxid text,\n    transactionid xid,\n    classid oid,\n    objid oid,\n    objsubid smallint,\n    virtualtransaction text,\n    pid integer,\n    mode text,\n    granted boolean,\n    fastpath boolean,\n    waitstart timestamp with time zone\n);\nEND;\n$function$;\n\nCREATE OR REPLACE VIEW citus.citus_locks AS\nSELECT * FROM pg_catalog.citus_locks();\n\nALTER VIEW citus.citus_locks SET SCHEMA pg_catalog;\n\nGRANT SELECT ON pg_catalog.citus_locks TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_locks/latest.sql",
    "content": "-- citus_locks combines the pg_locks views from all nodes and adds global_pid, nodeid, and\n-- relation_name. The columns of citus_locks don't change based on the Postgres version,\n-- however the pg_locks's columns do. Postgres 14 added one more column to pg_locks\n-- (waitstart timestamptz). citus_locks has the most expansive column set, including the\n-- newly added column. If citus_locks is queried in a Postgres version where pg_locks\n-- doesn't have some columns, the values for those columns in citus_locks will be NULL\nCREATE OR REPLACE FUNCTION pg_catalog.citus_locks (\n    OUT global_pid bigint,\n    OUT nodeid int,\n    OUT locktype text,\n    OUT database oid,\n    OUT relation oid,\n    OUT relation_name text,\n    OUT page integer,\n    OUT tuple smallint,\n    OUT virtualxid text,\n    OUT transactionid xid,\n    OUT classid oid,\n    OUT objid oid,\n    OUT objsubid smallint,\n    OUT virtualtransaction text,\n    OUT pid integer,\n    OUT mode text,\n    OUT granted boolean,\n    OUT fastpath boolean,\n    OUT waitstart timestamp with time zone\n)\n    RETURNS SETOF record\n    LANGUAGE plpgsql\n    AS $function$\nBEGIN\n    RETURN QUERY\n    SELECT *\n    FROM jsonb_to_recordset((\n        SELECT\n            jsonb_agg(all_citus_locks_rows_as_jsonb.citus_locks_row_as_jsonb)::jsonb\n        FROM (\n            SELECT\n                jsonb_array_elements(run_command_on_all_nodes.result::jsonb)::jsonb ||\n                    ('{\"nodeid\":' || run_command_on_all_nodes.nodeid || '}')::jsonb AS citus_locks_row_as_jsonb\n            FROM\n                run_command_on_all_nodes (\n                    $$\n                        SELECT\n                            coalesce(to_jsonb (array_agg(citus_locks_from_one_node.*)), '[{}]'::jsonb)\n                        FROM (\n                            SELECT\n                                global_pid, pg_locks.relation::regclass::text AS relation_name, pg_locks.*\n                            FROM pg_locks\n                        LEFT JOIN get_all_active_transactions () ON process_id = pid) AS citus_locks_from_one_node;\n                    $$,\n                    parallel:= TRUE,\n                    give_warning_for_connection_errors:= TRUE)\n            WHERE\n                success = 't')\n        AS all_citus_locks_rows_as_jsonb))\nAS (\n    global_pid bigint,\n    nodeid int,\n    locktype text,\n    database oid,\n    relation oid,\n    relation_name text,\n    page integer,\n    tuple smallint,\n    virtualxid text,\n    transactionid xid,\n    classid oid,\n    objid oid,\n    objsubid smallint,\n    virtualtransaction text,\n    pid integer,\n    mode text,\n    granted boolean,\n    fastpath boolean,\n    waitstart timestamp with time zone\n);\nEND;\n$function$;\n\nCREATE OR REPLACE VIEW citus.citus_locks AS\nSELECT * FROM pg_catalog.citus_locks();\n\nALTER VIEW citus.citus_locks SET SCHEMA pg_catalog;\n\nGRANT SELECT ON pg_catalog.citus_locks TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_move_shard_placement/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_move_shard_placement$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'move a shard from a the source node to the destination node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_move_shard_placement/11.2-1.sql",
    "content": "-- citus_move_shard_placement, but with nodeid\nCREATE FUNCTION pg_catalog.citus_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_id integer,\n\ttarget_node_id integer,\n\ttransfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_move_shard_placement_with_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_id integer,\n\ttarget_node_id integer,\n\ttransfer_mode citus.shard_transfer_mode)\nIS 'move a shard from the source node to the destination node';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_move_shard_placement$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'move a shard from a the source node to the destination node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_move_shard_placement/latest.sql",
    "content": "-- citus_move_shard_placement, but with nodeid\nCREATE FUNCTION pg_catalog.citus_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_id integer,\n\ttarget_node_id integer,\n\ttransfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_move_shard_placement_with_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_id integer,\n\ttarget_node_id integer,\n\ttransfer_mode citus.shard_transfer_mode)\nIS 'move a shard from the source node to the destination node';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_move_shard_placement$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_move_shard_placement(\n\tshard_id bigint,\n\tsource_node_name text,\n\tsource_node_port integer,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'move a shard from a the source node to the destination node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_node_capacity_1/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_node_capacity_1(int)\n    RETURNS float4 AS $$ SELECT 1.0::float4 $$ LANGUAGE sql;\nCOMMENT ON FUNCTION pg_catalog.citus_node_capacity_1(int)\n  IS 'a node capacity function for use by the rebalance algorithm that always returns 1';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_node_capacity_1/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_node_capacity_1(int)\n    RETURNS float4 AS $$ SELECT 1.0::float4 $$ LANGUAGE sql;\nCOMMENT ON FUNCTION pg_catalog.citus_node_capacity_1(int)\n  IS 'a node capacity function for use by the rebalance algorithm that always returns 1';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_nodeid_for_gpid/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_nodeid_for_gpid(global_pid bigint)\n    RETURNS integer\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$citus_nodeid_for_gpid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_nodeid_for_gpid(global_pid bigint)\n    IS 'returns node id for the global process with given global pid';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_nodeid_for_gpid/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_nodeid_for_gpid(global_pid bigint)\n    RETURNS integer\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$citus_nodeid_for_gpid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_nodeid_for_gpid(global_pid bigint)\n    IS 'returns node id for the global process with given global pid';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_nodename_for_nodeid/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_nodename_for_nodeid(nodeid integer)\n    RETURNS text\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$citus_nodename_for_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_nodename_for_nodeid(nodeid integer)\n    IS 'returns node name for the node with given node id';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_nodename_for_nodeid/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_nodename_for_nodeid(nodeid integer)\n    RETURNS text\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$citus_nodename_for_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_nodename_for_nodeid(nodeid integer)\n    IS 'returns node name for the node with given node id';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_nodeport_for_nodeid/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_nodeport_for_nodeid(nodeid integer)\n    RETURNS integer\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$citus_nodeport_for_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_nodeport_for_nodeid(nodeid integer)\n    IS 'returns node port for the node with given node id';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_nodeport_for_nodeid/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_nodeport_for_nodeid(nodeid integer)\n    RETURNS integer\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$citus_nodeport_for_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_nodeport_for_nodeid(nodeid integer)\n    IS 'returns node port for the node with given node id';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_nodes/13.1-1.sql",
    "content": "SET search_path = 'pg_catalog';\nDROP VIEW IF EXISTS pg_catalog.citus_nodes;\n\nCREATE OR REPLACE VIEW citus.citus_nodes AS\nSELECT\n    nodename,\n    nodeport,\n    CASE\n        WHEN groupid = 0 THEN 'coordinator'\n        ELSE 'worker'\n    END AS role,\n    isactive AS active\nFROM pg_dist_node;\n\nALTER VIEW citus.citus_nodes SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_nodes TO PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_nodes/latest.sql",
    "content": "SET search_path = 'pg_catalog';\nDROP VIEW IF EXISTS pg_catalog.citus_nodes;\n\nCREATE OR REPLACE VIEW citus.citus_nodes AS\nSELECT\n    nodename,\n    nodeport,\n    CASE\n        WHEN groupid = 0 THEN 'coordinator'\n        ELSE 'worker'\n    END AS role,\n    isactive AS active\nFROM pg_dist_node;\n\nALTER VIEW citus.citus_nodes SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_nodes TO PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_pause_node_within_txn/12.1-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_pause_node_within_txn(node_id int,\n                                              force bool DEFAULT false,\n                                              lock_cooldown int DEFAULT 10000)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$citus_pause_node_within_txn$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_pause_node_within_txn(node_id int,\n                                              force bool ,\n                                              lock_cooldown int )\n  IS 'pauses node with given id which leads to add lock in tables and prevent any queries to be executed on that node';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_pause_node_within_txn(int,bool,int) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_pause_node_within_txn/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_pause_node_within_txn(node_id int,\n                                              force bool DEFAULT false,\n                                              lock_cooldown int DEFAULT 10000)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$citus_pause_node_within_txn$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_pause_node_within_txn(node_id int,\n                                              force bool ,\n                                              lock_cooldown int )\n  IS 'pauses node with given id which leads to add lock in tables and prevent any queries to be executed on that node';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_pause_node_within_txn(int,bool,int) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_pid_for_gpid/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_pid_for_gpid(global_pid bigint)\n    RETURNS integer\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$citus_pid_for_gpid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_pid_for_gpid(global_pid bigint)\n    IS 'returns process id for the global process with given global pid';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_pid_for_gpid/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_pid_for_gpid(global_pid bigint)\n    RETURNS integer\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$citus_pid_for_gpid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_pid_for_gpid(global_pid bigint)\n    IS 'returns process id for the global process with given global pid';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/10.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n    DROP TABLE IF EXISTS public.pg_dist_object;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM citus.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n\n    DELETE FROM pg_depend WHERE\n        objid IN (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') AND\n        refobjid IN (select oid from pg_extension where extname = 'citus');\n    --\n    -- We are dropping the aggregates because postgres 14 changed\n    -- array_cat type from anyarray to anycompatiblearray. When\n    -- upgrading to pg14, specifically when running pg_restore on\n    -- array_cat_agg we would get an error. So we drop the aggregate\n    -- and create the right one on citus_finish_pg_upgrade.\n\n    DROP AGGREGATE IF EXISTS array_cat_agg(anyarray);\n    DROP AGGREGATE IF EXISTS array_cat_agg(anycompatiblearray);\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n    DROP TABLE IF EXISTS public.pg_dist_object;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM citus.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n\n    DELETE FROM pg_depend WHERE\n        objid IN (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') AND\n        refobjid IN (select oid from pg_extension where extname = 'citus');\n    --\n    -- We are dropping the aggregates because postgres 14 changed\n    -- array_cat type from anyarray to anycompatiblearray. When\n    -- upgrading to pg14, specifically when running pg_restore on\n    -- array_cat_agg we would get an error. So we drop the aggregate\n    -- and create the right one on citus_finish_pg_upgrade.\n\n    DROP AGGREGATE IF EXISTS array_cat_agg(anyarray);\n    DROP AGGREGATE IF EXISTS array_cat_agg(anycompatiblearray);\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n    DROP TABLE IF EXISTS public.pg_dist_object;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM pg_catalog.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n\n    DELETE FROM pg_depend WHERE\n        objid IN (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') AND\n        refobjid IN (select oid from pg_extension where extname = 'citus');\n    --\n    -- We are dropping the aggregates because postgres 14 changed\n    -- array_cat type from anyarray to anycompatiblearray. When\n    -- upgrading to pg14, specifically when running pg_restore on\n    -- array_cat_agg we would get an error. So we drop the aggregate\n    -- and create the right one on citus_finish_pg_upgrade.\n\n    DROP AGGREGATE IF EXISTS array_cat_agg(anyarray);\n    DROP AGGREGATE IF EXISTS array_cat_agg(anycompatiblearray);\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n    DROP TABLE IF EXISTS public.pg_dist_object;\n    DROP TABLE IF EXISTS public.pg_dist_cleanup;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    CREATE TABLE public.pg_dist_cleanup AS SELECT * FROM pg_catalog.pg_dist_cleanup;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM pg_catalog.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/11.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n\n    DELETE FROM pg_depend WHERE\n        objid IN (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') AND\n        refobjid IN (select oid from pg_extension where extname = 'citus');\n    --\n    -- We are dropping the aggregates because postgres 14 changed\n    -- array_cat type from anyarray to anycompatiblearray. When\n    -- upgrading to pg14, specifically when running pg_restore on\n    -- array_cat_agg we would get an error. So we drop the aggregate\n    -- and create the right one on citus_finish_pg_upgrade.\n\n    DROP AGGREGATE IF EXISTS array_cat_agg(anyarray);\n    DROP AGGREGATE IF EXISTS array_cat_agg(anycompatiblearray);\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n    DROP TABLE IF EXISTS public.pg_dist_object;\n    DROP TABLE IF EXISTS public.pg_dist_cleanup;\n    DROP TABLE IF EXISTS public.pg_dist_clock_logical_seq;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    CREATE TABLE public.pg_dist_cleanup AS SELECT * FROM pg_catalog.pg_dist_cleanup;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    -- sequences\n    CREATE TABLE public.pg_dist_clock_logical_seq AS SELECT last_value FROM pg_catalog.pg_dist_clock_logical_seq;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM pg_catalog.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/12.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n\n    DELETE FROM pg_depend WHERE\n        objid IN (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') AND\n        refobjid IN (select oid from pg_extension where extname = 'citus');\n    --\n    -- We are dropping the aggregates because postgres 14 changed\n    -- array_cat type from anyarray to anycompatiblearray. When\n    -- upgrading to pg14, specifically when running pg_restore on\n    -- array_cat_agg we would get an error. So we drop the aggregate\n    -- and create the right one on citus_finish_pg_upgrade.\n\n    DROP AGGREGATE IF EXISTS array_cat_agg(anyarray);\n    DROP AGGREGATE IF EXISTS array_cat_agg(anycompatiblearray);\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n    DROP TABLE IF EXISTS public.pg_dist_object;\n    DROP TABLE IF EXISTS public.pg_dist_cleanup;\n    DROP TABLE IF EXISTS public.pg_dist_schema;\n    DROP TABLE IF EXISTS public.pg_dist_clock_logical_seq;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    CREATE TABLE public.pg_dist_cleanup AS SELECT * FROM pg_catalog.pg_dist_cleanup;\n    -- save names of the tenant schemas instead of their oids because the oids might change after pg upgrade\n    CREATE TABLE public.pg_dist_schema AS SELECT schemaid::regnamespace::text AS schemaname, colocationid FROM pg_catalog.pg_dist_schema;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    -- sequences\n    CREATE TABLE public.pg_dist_clock_logical_seq AS SELECT last_value FROM pg_catalog.pg_dist_clock_logical_seq;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM pg_catalog.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/12.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n\n    DELETE FROM pg_depend WHERE\n        objid IN (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') AND\n        refobjid IN (select oid from pg_extension where extname = 'citus');\n    --\n    -- We are dropping the aggregates because postgres 14 changed\n    -- array_cat type from anyarray to anycompatiblearray. When\n    -- upgrading to pg14, specifically when running pg_restore on\n    -- array_cat_agg we would get an error. So we drop the aggregate\n    -- and create the right one on citus_finish_pg_upgrade.\n\n    DROP AGGREGATE IF EXISTS array_cat_agg(anyarray);\n    DROP AGGREGATE IF EXISTS array_cat_agg(anycompatiblearray);\n\n    -- We should drop any_value because PG16 has its own any_value function\n    -- We can remove this part when we drop support for PG16\n    DELETE FROM pg_depend WHERE\n        objid IN (SELECT oid FROM pg_proc WHERE proname = 'any_value' OR proname = 'any_value_agg') AND\n        refobjid IN (select oid from pg_extension where extname = 'citus');\n    DROP AGGREGATE IF EXISTS pg_catalog.any_value(anyelement);\n    DROP FUNCTION IF EXISTS pg_catalog.any_value_agg(anyelement, anyelement);\n\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n    DROP TABLE IF EXISTS public.pg_dist_object;\n    DROP TABLE IF EXISTS public.pg_dist_cleanup;\n    DROP TABLE IF EXISTS public.pg_dist_schema;\n    DROP TABLE IF EXISTS public.pg_dist_clock_logical_seq;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    CREATE TABLE public.pg_dist_cleanup AS SELECT * FROM pg_catalog.pg_dist_cleanup;\n    -- save names of the tenant schemas instead of their oids because the oids might change after pg upgrade\n    CREATE TABLE public.pg_dist_schema AS SELECT schemaid::regnamespace::text AS schemaname, colocationid FROM pg_catalog.pg_dist_schema;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    -- sequences\n    CREATE TABLE public.pg_dist_clock_logical_seq AS SELECT last_value FROM pg_catalog.pg_dist_clock_logical_seq;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM pg_catalog.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\n\n    -- if we are upgrading from PG14/PG15 to PG16+,\n    -- we will need to regenerate the partkeys because they will include varnullingrels as well.\n    -- so we save the partkeys as column names here\n    CREATE TABLE IF NOT EXISTS public.pg_dist_partkeys_pre_16_upgrade AS\n    SELECT logicalrelid, column_to_column_name(logicalrelid, partkey) as col_name\n    FROM pg_catalog.pg_dist_partition WHERE partkey IS NOT NULL AND partkey NOT ILIKE '%varnullingrels%';\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/13.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n\n    DELETE FROM pg_depend WHERE\n        objid IN (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') AND\n        refobjid IN (select oid from pg_extension where extname = 'citus');\n    --\n    -- We are dropping the aggregates because postgres 14 changed\n    -- array_cat type from anyarray to anycompatiblearray. When\n    -- upgrading to pg14, specifically when running pg_restore on\n    -- array_cat_agg we would get an error. So we drop the aggregate\n    -- and create the right one on citus_finish_pg_upgrade.\n\n    DROP AGGREGATE IF EXISTS array_cat_agg(anyarray);\n    DROP AGGREGATE IF EXISTS array_cat_agg(anycompatiblearray);\n\n    -- We should drop any_value because PG16+ has its own any_value function\n    -- We can remove this part when we drop support for PG16\n    IF substring(current_Setting('server_version'), '\\d+')::int < 16 THEN\n        DELETE FROM pg_depend WHERE\n            objid IN (SELECT oid FROM pg_proc WHERE proname = 'any_value' OR proname = 'any_value_agg') AND\n            refobjid IN (select oid from pg_extension where extname = 'citus');\n        DROP AGGREGATE IF EXISTS pg_catalog.any_value(anyelement);\n        DROP FUNCTION IF EXISTS pg_catalog.any_value_agg(anyelement, anyelement);\n    END IF;\n\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n    DROP TABLE IF EXISTS public.pg_dist_object;\n    DROP TABLE IF EXISTS public.pg_dist_cleanup;\n    DROP TABLE IF EXISTS public.pg_dist_schema;\n    DROP TABLE IF EXISTS public.pg_dist_clock_logical_seq;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    CREATE TABLE public.pg_dist_cleanup AS SELECT * FROM pg_catalog.pg_dist_cleanup;\n    -- save names of the tenant schemas instead of their oids because the oids might change after pg upgrade\n    CREATE TABLE public.pg_dist_schema AS SELECT schemaid::regnamespace::text AS schemaname, colocationid FROM pg_catalog.pg_dist_schema;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    -- sequences\n    CREATE TABLE public.pg_dist_clock_logical_seq AS SELECT last_value FROM pg_catalog.pg_dist_clock_logical_seq;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM pg_catalog.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\n\n    -- if we are upgrading from PG14/PG15 to PG16+,\n    -- we will need to regenerate the partkeys because they will include varnullingrels as well.\n    -- so we save the partkeys as column names here\n    CREATE TABLE IF NOT EXISTS public.pg_dist_partkeys_pre_16_upgrade AS\n    SELECT logicalrelid, column_to_column_name(logicalrelid, partkey) as col_name\n    FROM pg_catalog.pg_dist_partition WHERE partkey IS NOT NULL AND partkey NOT ILIKE '%varnullingrels%';\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/14.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n\n    DELETE FROM pg_depend WHERE\n        objid IN (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') AND\n        refobjid IN (select oid from pg_extension where extname = 'citus');\n    --\n    -- We are dropping the aggregates because postgres 14 changed\n    -- array_cat type from anyarray to anycompatiblearray. When\n    -- upgrading to pg14, specifically when running pg_restore on\n    -- array_cat_agg we would get an error. So we drop the aggregate\n    -- and create the right one on citus_finish_pg_upgrade.\n\n    DROP AGGREGATE IF EXISTS array_cat_agg(anyarray);\n    DROP AGGREGATE IF EXISTS array_cat_agg(anycompatiblearray);\n\n    -- We should drop any_value because PG16+ has its own any_value function\n    -- We can remove this part when we drop support for PG16\n    IF substring(current_Setting('server_version'), '\\d+')::int < 16 THEN\n        DELETE FROM pg_depend WHERE\n            objid IN (SELECT oid FROM pg_proc WHERE proname = 'any_value' OR proname = 'any_value_agg') AND\n            refobjid IN (select oid from pg_extension where extname = 'citus');\n        DROP AGGREGATE IF EXISTS pg_catalog.any_value(anyelement);\n        DROP FUNCTION IF EXISTS pg_catalog.any_value_agg(anyelement, anyelement);\n    END IF;\n\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n    DROP TABLE IF EXISTS public.pg_dist_object;\n    DROP TABLE IF EXISTS public.pg_dist_cleanup;\n    DROP TABLE IF EXISTS public.pg_dist_schema;\n    DROP TABLE IF EXISTS public.pg_dist_clock_logical_seq;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    CREATE TABLE public.pg_dist_cleanup AS SELECT * FROM pg_catalog.pg_dist_cleanup;\n    -- save names of the tenant schemas instead of their oids because the oids might change after pg upgrade\n    CREATE TABLE public.pg_dist_schema AS SELECT schemaid::regnamespace::text AS schemaname, colocationid FROM pg_catalog.pg_dist_schema;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    -- sequences\n    CREATE TABLE public.pg_dist_clock_logical_seq AS SELECT last_value FROM pg_catalog.pg_dist_clock_logical_seq;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM pg_catalog.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\n\n    -- if we are upgrading from PG14/PG15 to PG16+,\n    -- we will need to regenerate the partkeys because they will include varnullingrels as well.\n    -- so we save the partkeys as column names here\n    CREATE TABLE IF NOT EXISTS public.pg_dist_partkeys_pre_16_upgrade AS\n    SELECT logicalrelid, column_to_column_name(logicalrelid, partkey) as col_name\n    FROM pg_catalog.pg_dist_partition WHERE partkey IS NOT NULL AND partkey NOT ILIKE '%varnullingrels%';\n\n    -- similarly, if we are upgrading to PG18+,\n    -- we will need to regenerate the partkeys because they will include varreturningtype as well.\n    -- so we save the partkeys as column names here\n    CREATE TABLE IF NOT EXISTS public.pg_dist_partkeys_pre_18_upgrade AS\n    SELECT logicalrelid, column_to_column_name(logicalrelid, partkey) as col_name\n    FROM pg_catalog.pg_dist_partition WHERE partkey IS NOT NULL AND partkey NOT ILIKE '%varreturningtype%';\n    -- remove duplicates (we would only have duplicates if we are upgrading from pre-16 to PG18+)\n    DELETE FROM public.pg_dist_partkeys_pre_18_upgrade USING public.pg_dist_partkeys_pre_16_upgrade p16\n    WHERE public.pg_dist_partkeys_pre_18_upgrade.logicalrelid = p16.logicalrelid\n    AND public.pg_dist_partkeys_pre_18_upgrade.col_name = p16.col_name;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/9.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    UPDATE citus.pg_dist_object\n       SET (type, object_names, object_args) = (SELECT * FROM pg_identify_object_as_address(classid, objid, objsubid));\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    UPDATE citus.pg_dist_object\n       SET (type, object_names, object_args) = (SELECT * FROM pg_identify_object_as_address(classid, objid, objsubid));\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/9.4-2.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM citus.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    UPDATE citus.pg_dist_object\n       SET (type, object_names, object_args) = (SELECT * FROM pg_identify_object_as_address(classid, objid, objsubid));\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/9.5-2.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n    DROP TABLE IF EXISTS public.pg_dist_object;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM citus.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    RETURNS void\n    LANGUAGE plpgsql\n    SET search_path = pg_catalog\n    AS $cppu$\nBEGIN\n\n    DELETE FROM pg_depend WHERE\n        objid IN (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') AND\n        refobjid IN (select oid from pg_extension where extname = 'citus');\n    --\n    -- We are dropping the aggregates because postgres 14 changed\n    -- array_cat type from anyarray to anycompatiblearray. When\n    -- upgrading to pg14, specifically when running pg_restore on\n    -- array_cat_agg we would get an error. So we drop the aggregate\n    -- and create the right one on citus_finish_pg_upgrade.\n\n    DROP AGGREGATE IF EXISTS array_cat_agg(anyarray);\n    DROP AGGREGATE IF EXISTS array_cat_agg(anycompatiblearray);\n\n    -- We should drop any_value because PG16+ has its own any_value function\n    -- We can remove this part when we drop support for PG16\n    IF substring(current_Setting('server_version'), '\\d+')::int < 16 THEN\n        DELETE FROM pg_depend WHERE\n            objid IN (SELECT oid FROM pg_proc WHERE proname = 'any_value' OR proname = 'any_value_agg') AND\n            refobjid IN (select oid from pg_extension where extname = 'citus');\n        DROP AGGREGATE IF EXISTS pg_catalog.any_value(anyelement);\n        DROP FUNCTION IF EXISTS pg_catalog.any_value_agg(anyelement, anyelement);\n    END IF;\n\n    --\n    -- Drop existing backup tables\n    --\n    DROP TABLE IF EXISTS public.pg_dist_partition;\n    DROP TABLE IF EXISTS public.pg_dist_shard;\n    DROP TABLE IF EXISTS public.pg_dist_placement;\n    DROP TABLE IF EXISTS public.pg_dist_node_metadata;\n    DROP TABLE IF EXISTS public.pg_dist_node;\n    DROP TABLE IF EXISTS public.pg_dist_local_group;\n    DROP TABLE IF EXISTS public.pg_dist_transaction;\n    DROP TABLE IF EXISTS public.pg_dist_colocation;\n    DROP TABLE IF EXISTS public.pg_dist_authinfo;\n    DROP TABLE IF EXISTS public.pg_dist_poolinfo;\n    DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy;\n    DROP TABLE IF EXISTS public.pg_dist_object;\n    DROP TABLE IF EXISTS public.pg_dist_cleanup;\n    DROP TABLE IF EXISTS public.pg_dist_schema;\n    DROP TABLE IF EXISTS public.pg_dist_clock_logical_seq;\n\n    --\n    -- backup citus catalog tables\n    --\n    CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition;\n    CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard;\n    CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement;\n    CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata;\n    CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node;\n    CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group;\n    CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction;\n    CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation;\n    CREATE TABLE public.pg_dist_cleanup AS SELECT * FROM pg_catalog.pg_dist_cleanup;\n    -- save names of the tenant schemas instead of their oids because the oids might change after pg upgrade\n    CREATE TABLE public.pg_dist_schema AS SELECT schemaid::regnamespace::text AS schemaname, colocationid FROM pg_catalog.pg_dist_schema;\n    -- enterprise catalog tables\n    CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo;\n    CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo;\n    -- sequences\n    CREATE TABLE public.pg_dist_clock_logical_seq AS SELECT last_value FROM pg_catalog.pg_dist_clock_logical_seq;\n    CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT\n        name,\n        default_strategy,\n        shard_cost_function::regprocedure::text,\n        node_capacity_function::regprocedure::text,\n        shard_allowed_on_node_function::regprocedure::text,\n        default_threshold,\n        minimum_threshold,\n        improvement_threshold\n    FROM pg_catalog.pg_dist_rebalance_strategy;\n\n    -- store upgrade stable identifiers on pg_dist_object catalog\n    CREATE TABLE public.pg_dist_object AS SELECT\n       address.type,\n       address.object_names,\n       address.object_args,\n       objects.distribution_argument_index,\n       objects.colocationid\n    FROM pg_catalog.pg_dist_object objects,\n         pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address;\n\n    -- if we are upgrading from PG14/PG15 to PG16+,\n    -- we will need to regenerate the partkeys because they will include varnullingrels as well.\n    -- so we save the partkeys as column names here\n    CREATE TABLE IF NOT EXISTS public.pg_dist_partkeys_pre_16_upgrade AS\n    SELECT logicalrelid, column_to_column_name(logicalrelid, partkey) as col_name\n    FROM pg_catalog.pg_dist_partition WHERE partkey IS NOT NULL AND partkey NOT ILIKE '%varnullingrels%';\n\n    -- similarly, if we are upgrading to PG18+,\n    -- we will need to regenerate the partkeys because they will include varreturningtype as well.\n    -- so we save the partkeys as column names here\n    CREATE TABLE IF NOT EXISTS public.pg_dist_partkeys_pre_18_upgrade AS\n    SELECT logicalrelid, column_to_column_name(logicalrelid, partkey) as col_name\n    FROM pg_catalog.pg_dist_partition WHERE partkey IS NOT NULL AND partkey NOT ILIKE '%varreturningtype%';\n    -- remove duplicates (we would only have duplicates if we are upgrading from pre-16 to PG18+)\n    DELETE FROM public.pg_dist_partkeys_pre_18_upgrade USING public.pg_dist_partkeys_pre_16_upgrade p16\n    WHERE public.pg_dist_partkeys_pre_18_upgrade.logicalrelid = p16.logicalrelid\n    AND public.pg_dist_partkeys_pre_18_upgrade.col_name = p16.col_name;\nEND;\n$cppu$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade()\n    IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_promote_clone_and_rebalance/13.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_promote_clone_and_rebalance(\n    clone_nodeid integer,\n    rebalance_strategy name DEFAULT NULL,\n    catchup_timeout_seconds integer DEFAULT 300\n)\nRETURNS VOID\nAS 'MODULE_PATHNAME'\nLANGUAGE C VOLATILE;\n\nCOMMENT ON FUNCTION pg_catalog.citus_promote_clone_and_rebalance(integer, name, integer) IS\n'Promotes a registered clone node to a primary, performs necessary metadata updates, and rebalances a portion of shards from its original primary to the newly promoted node. The catchUpTimeoutSeconds parameter controls how long to wait for the clone to catch up with the primary (default: 300 seconds).';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_promote_clone_and_rebalance(integer, name, integer) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_promote_clone_and_rebalance/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_promote_clone_and_rebalance(\n    clone_nodeid integer,\n    rebalance_strategy name DEFAULT NULL,\n    catchup_timeout_seconds integer DEFAULT 300\n)\nRETURNS VOID\nAS 'MODULE_PATHNAME'\nLANGUAGE C VOLATILE;\n\nCOMMENT ON FUNCTION pg_catalog.citus_promote_clone_and_rebalance(integer, name, integer) IS\n'Promotes a registered clone node to a primary, performs necessary metadata updates, and rebalances a portion of shards from its original primary to the newly promoted node. The catchUpTimeoutSeconds parameter controls how long to wait for the clone to catch up with the primary (default: 300 seconds).';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_promote_clone_and_rebalance(integer, name, integer) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_rebalance_start/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_rebalance_start(\n        rebalance_strategy name DEFAULT NULL,\n        drain_only boolean DEFAULT false,\n        shard_transfer_mode citus.shard_transfer_mode default 'auto'\n    )\n    RETURNS bigint\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.citus_rebalance_start(name, boolean, citus.shard_transfer_mode)\n    IS 'rebalance the shards in the cluster in the background';\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_rebalance_start(name, boolean, citus.shard_transfer_mode) TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_rebalance_start/13.2-1.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.citus_rebalance_start(name, boolean, citus.shard_transfer_mode);\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_rebalance_start(\n        rebalance_strategy name DEFAULT NULL,\n        drain_only boolean DEFAULT false,\n        shard_transfer_mode citus.shard_transfer_mode default 'auto',\n        parallel_transfer_reference_tables boolean DEFAULT false,\n        parallel_transfer_colocated_shards boolean DEFAULT false\n    )\n    RETURNS bigint\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.citus_rebalance_start(name, boolean, citus.shard_transfer_mode, boolean, boolean)\n    IS 'rebalance the shards in the cluster in the background';\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_rebalance_start(name, boolean, citus.shard_transfer_mode, boolean, boolean) TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_rebalance_start/latest.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.citus_rebalance_start(name, boolean, citus.shard_transfer_mode);\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_rebalance_start(\n        rebalance_strategy name DEFAULT NULL,\n        drain_only boolean DEFAULT false,\n        shard_transfer_mode citus.shard_transfer_mode default 'auto',\n        parallel_transfer_reference_tables boolean DEFAULT false,\n        parallel_transfer_colocated_shards boolean DEFAULT false\n    )\n    RETURNS bigint\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.citus_rebalance_start(name, boolean, citus.shard_transfer_mode, boolean, boolean)\n    IS 'rebalance the shards in the cluster in the background';\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_rebalance_start(name, boolean, citus.shard_transfer_mode, boolean, boolean) TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_rebalance_status/11.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_rebalance_status (\n    raw boolean DEFAULT FALSE\n)\n    RETURNS TABLE (\n            job_id bigint,\n            state pg_catalog.citus_job_status,\n            job_type name,\n            description text,\n            started_at timestamptz,\n            finished_at timestamptz,\n            details jsonb\n)\n    LANGUAGE SQL\n    STRICT\n    AS $fn$\n    SELECT\n        job_status.*\n    FROM\n        pg_dist_background_job j,\n        citus_job_status (j.job_id, $1) job_status\n    WHERE\n        j.job_id IN (\n            SELECT job_id\n            FROM pg_dist_background_job\n            WHERE job_type = 'rebalance'\n            ORDER BY job_id DESC\n            LIMIT 1\n        );\n$fn$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_rebalance_status/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_rebalance_status (\n    raw boolean DEFAULT FALSE\n)\n    RETURNS TABLE (\n            job_id bigint,\n            state pg_catalog.citus_job_status,\n            job_type name,\n            description text,\n            started_at timestamptz,\n            finished_at timestamptz,\n            details jsonb\n)\n    LANGUAGE SQL\n    STRICT\n    AS $fn$\n    SELECT\n        job_status.*\n    FROM\n        pg_dist_background_job j,\n        citus_job_status (j.job_id, $1) job_status\n    WHERE\n        j.job_id IN (\n            SELECT job_id\n            FROM pg_dist_background_job\n            WHERE job_type = 'rebalance'\n            ORDER BY job_id DESC\n            LIMIT 1\n        );\n$fn$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_rebalance_stop/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_rebalance_stop()\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.citus_rebalance_stop()\n    IS 'stop a rebalance that is running in the background';\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_rebalance_stop() TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_rebalance_stop/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_rebalance_stop()\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.citus_rebalance_stop()\n    IS 'stop a rebalance that is running in the background';\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_rebalance_stop() TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_rebalance_wait/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_rebalance_wait()\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.citus_rebalance_wait()\n    IS 'wait on a running rebalance in the background';\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_rebalance_wait() TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_rebalance_wait/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_rebalance_wait()\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.citus_rebalance_wait()\n    IS 'wait on a running rebalance in the background';\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_rebalance_wait() TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_remote_connection_stats/9.3-2.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_remote_connection_stats(\n\tOUT hostname text,\n\tOUT port int,\n\tOUT database_name text,\n\tOUT connection_count_to_node int)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_remote_connection_stats$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_remote_connection_stats(\n\tOUT hostname text,\n\tOUT port int,\n\tOUT database_name text,\n\tOUT connection_count_to_node int)\n     IS 'returns statistics about remote connections';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_remote_connection_stats(\n\t\tOUT hostname text,\n\t\tOUT port int,\n\t\tOUT database_name text,\n\t\tOUT connection_count_to_node int)\nFROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_remote_connection_stats/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_remote_connection_stats(\n\tOUT hostname text,\n\tOUT port int,\n\tOUT database_name text,\n\tOUT connection_count_to_node int)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_remote_connection_stats$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_remote_connection_stats(\n\tOUT hostname text,\n\tOUT port int,\n\tOUT database_name text,\n\tOUT connection_count_to_node int)\n     IS 'returns statistics about remote connections';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_remote_connection_stats(\n\t\tOUT hostname text,\n\t\tOUT port int,\n\t\tOUT database_name text,\n\t\tOUT connection_count_to_node int)\nFROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_remove_clone_node/13.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_remove_clone_node(\n    nodename text,\n    nodeport integer\n)\nRETURNS VOID\nLANGUAGE C VOLATILE STRICT\nAS 'MODULE_PATHNAME', $$citus_remove_clone_node$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_remove_clone_node(text, integer)\nIS 'Removes an inactive streaming clone node from Citus metadata. Errors if the node is not found, not registered as a clone, or is currently marked active.';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_remove_clone_node(text, integer) FROM PUBLIC;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_remove_clone_node_with_nodeid(\n    nodeid integer\n)\nRETURNS VOID\nLANGUAGE C VOLATILE STRICT\nAS 'MODULE_PATHNAME', $$citus_remove_clone_node_with_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_remove_clone_node_with_nodeid(integer)\nIS 'Removes an inactive streaming clone node from Citus metadata using its node ID. Errors if the node is not found, not registered as a clone, or is currently marked active.';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_remove_clone_node_with_nodeid(integer) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_remove_clone_node/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_remove_clone_node(\n    nodename text,\n    nodeport integer\n)\nRETURNS VOID\nLANGUAGE C VOLATILE STRICT\nAS 'MODULE_PATHNAME', $$citus_remove_clone_node$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_remove_clone_node(text, integer)\nIS 'Removes an inactive streaming clone node from Citus metadata. Errors if the node is not found, not registered as a clone, or is currently marked active.';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_remove_clone_node(text, integer) FROM PUBLIC;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_remove_clone_node_with_nodeid(\n    nodeid integer\n)\nRETURNS VOID\nLANGUAGE C VOLATILE STRICT\nAS 'MODULE_PATHNAME', $$citus_remove_clone_node_with_nodeid$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_remove_clone_node_with_nodeid(integer)\nIS 'Removes an inactive streaming clone node from Citus metadata using its node ID. Errors if the node is not found, not registered as a clone, or is currently marked active.';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_remove_clone_node_with_nodeid(integer) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_remove_node/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_remove_node(nodename text, nodeport integer)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_remove_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_remove_node(nodename text, nodeport integer)\n\tIS 'remove node from the cluster';\nREVOKE ALL ON FUNCTION pg_catalog.citus_remove_node(text,int) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_remove_node/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_remove_node(nodename text, nodeport integer)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_remove_node$$;\nCOMMENT ON FUNCTION pg_catalog.citus_remove_node(nodename text, nodeport integer)\n\tIS 'remove node from the cluster';\nREVOKE ALL ON FUNCTION pg_catalog.citus_remove_node(text,int) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_run_local_command/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_run_local_command(command text)\nRETURNS void AS $$\nBEGIN\n    EXECUTE $1;\nEND;\n$$ LANGUAGE PLPGSQL;\nCOMMENT ON FUNCTION pg_catalog.citus_run_local_command(text)\n    IS 'citus_run_local_command executes the input command';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_run_local_command/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_run_local_command(command text)\nRETURNS void AS $$\nBEGIN\n    EXECUTE $1;\nEND;\n$$ LANGUAGE PLPGSQL;\nCOMMENT ON FUNCTION pg_catalog.citus_run_local_command(text)\n    IS 'citus_run_local_command executes the input command';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_schema_distribute/12.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_schema_distribute$$;\nCOMMENT ON FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace)\n\tIS 'distributes a schema, allowing it to move between nodes';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_schema_distribute/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_schema_distribute$$;\nCOMMENT ON FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace)\n\tIS 'distributes a schema, allowing it to move between nodes';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_schema_move/12.1-1.sql",
    "content": "-- citus_schema_move, using target node name and node port\nCREATE OR REPLACE FUNCTION pg_catalog.citus_schema_move(\n\tschema_id regnamespace,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_schema_move$$;\nCOMMENT ON FUNCTION pg_catalog.citus_schema_move(\n\tschema_id regnamespace,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'move a distributed schema to given node';\n\n-- citus_schema_move, using target node id\nCREATE OR REPLACE FUNCTION pg_catalog.citus_schema_move(\n\tschema_id regnamespace,\n\ttarget_node_id integer,\n\tshard_transfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_schema_move_with_nodeid$$;\nCOMMENT ON FUNCTION pg_catalog.citus_schema_move(\n\tschema_id regnamespace,\n\ttarget_node_id integer,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'move a distributed schema to given node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_schema_move/latest.sql",
    "content": "-- citus_schema_move, using target node name and node port\nCREATE OR REPLACE FUNCTION pg_catalog.citus_schema_move(\n\tschema_id regnamespace,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_schema_move$$;\nCOMMENT ON FUNCTION pg_catalog.citus_schema_move(\n\tschema_id regnamespace,\n\ttarget_node_name text,\n\ttarget_node_port integer,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'move a distributed schema to given node';\n\n-- citus_schema_move, using target node id\nCREATE OR REPLACE FUNCTION pg_catalog.citus_schema_move(\n\tschema_id regnamespace,\n\ttarget_node_id integer,\n\tshard_transfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_schema_move_with_nodeid$$;\nCOMMENT ON FUNCTION pg_catalog.citus_schema_move(\n\tschema_id regnamespace,\n\ttarget_node_id integer,\n\tshard_transfer_mode citus.shard_transfer_mode)\nIS 'move a distributed schema to given node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_schema_undistribute/12.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_schema_undistribute$$;\nCOMMENT ON FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace)\n\tIS 'reverts schema distribution, moving it back to the coordinator';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_schema_undistribute/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_schema_undistribute$$;\nCOMMENT ON FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace)\n\tIS 'reverts schema distribution, moving it back to the coordinator';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_schemas/12.0-1.sql",
    "content": "DO $$\ndeclare\ncitus_schemas_create_query text;\nBEGIN\ncitus_schemas_create_query=$CSCQ$\n    CREATE OR REPLACE VIEW %I.citus_schemas AS\n    SELECT\n        ts.schemaid::regnamespace AS schema_name,\n        ts.colocationid AS colocation_id,\n        CASE\n            WHEN pg_catalog.has_schema_privilege(CURRENT_USER, ts.schemaid::regnamespace, 'USAGE')\n            THEN pg_size_pretty(coalesce(schema_sizes.schema_size, 0))\n            ELSE NULL\n        END AS schema_size,\n        pg_get_userbyid(n.nspowner) AS schema_owner\n    FROM\n        pg_dist_schema ts\n    JOIN\n        pg_namespace n ON (ts.schemaid = n.oid)\n    LEFT JOIN (\n        SELECT c.relnamespace::regnamespace schema_id, SUM(size) AS schema_size\n        FROM citus_shard_sizes() css, pg_dist_shard ds, pg_class c\n        WHERE css.shard_id = ds.shardid AND ds.logicalrelid = c.oid\n        GROUP BY schema_id\n    ) schema_sizes ON schema_sizes.schema_id = ts.schemaid\n    ORDER BY\n        schema_name;\n$CSCQ$;\n\nIF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'public') THEN\n    EXECUTE format(citus_schemas_create_query, 'public');\n    REVOKE ALL ON public.citus_schemas FROM public;\n    GRANT SELECT ON public.citus_schemas TO public;\nELSE\n    EXECUTE format(citus_schemas_create_query, 'citus');\n    ALTER VIEW citus.citus_schemas SET SCHEMA pg_catalog;\n    REVOKE ALL ON pg_catalog.citus_schemas FROM public;\n    GRANT SELECT ON pg_catalog.citus_schemas TO public;\nEND IF;\n\nEND;\n$$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_schemas/latest.sql",
    "content": "DO $$\ndeclare\ncitus_schemas_create_query text;\nBEGIN\ncitus_schemas_create_query=$CSCQ$\n    CREATE OR REPLACE VIEW %I.citus_schemas AS\n    SELECT\n        ts.schemaid::regnamespace AS schema_name,\n        ts.colocationid AS colocation_id,\n        CASE\n            WHEN pg_catalog.has_schema_privilege(CURRENT_USER, ts.schemaid::regnamespace, 'USAGE')\n            THEN pg_size_pretty(coalesce(schema_sizes.schema_size, 0))\n            ELSE NULL\n        END AS schema_size,\n        pg_get_userbyid(n.nspowner) AS schema_owner\n    FROM\n        pg_dist_schema ts\n    JOIN\n        pg_namespace n ON (ts.schemaid = n.oid)\n    LEFT JOIN (\n        SELECT c.relnamespace::regnamespace schema_id, SUM(size) AS schema_size\n        FROM citus_shard_sizes() css, pg_dist_shard ds, pg_class c\n        WHERE css.shard_id = ds.shardid AND ds.logicalrelid = c.oid\n        GROUP BY schema_id\n    ) schema_sizes ON schema_sizes.schema_id = ts.schemaid\n    ORDER BY\n        schema_name;\n$CSCQ$;\n\nIF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'public') THEN\n    EXECUTE format(citus_schemas_create_query, 'public');\n    REVOKE ALL ON public.citus_schemas FROM public;\n    GRANT SELECT ON public.citus_schemas TO public;\nELSE\n    EXECUTE format(citus_schemas_create_query, 'citus');\n    ALTER VIEW citus.citus_schemas SET SCHEMA pg_catalog;\n    REVOKE ALL ON pg_catalog.citus_schemas FROM public;\n    GRANT SELECT ON pg_catalog.citus_schemas TO public;\nEND IF;\n\nEND;\n$$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_set_coordinator_host/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_set_coordinator_host(\n    host text,\n    port integer default current_setting('port')::int,\n    node_role noderole default 'primary',\n    node_cluster name default 'default')\nRETURNS VOID\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_set_coordinator_host$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_set_coordinator_host(text,integer,noderole,name)\nIS 'set the host and port of the coordinator';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_set_coordinator_host(text,int,noderole,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_set_coordinator_host/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_set_coordinator_host(\n    host text,\n    port integer default current_setting('port')::int,\n    node_role noderole default 'primary',\n    node_cluster name default 'default')\nRETURNS VOID\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_set_coordinator_host$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_set_coordinator_host(text,integer,noderole,name)\nIS 'set the host and port of the coordinator';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_set_coordinator_host(text,int,noderole,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_set_default_rebalance_strategy/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_set_default_rebalance_strategy(\n    name text\n)\n    RETURNS VOID\n    STRICT\nAS $$\n  BEGIN\n    LOCK TABLE pg_dist_rebalance_strategy IN SHARE ROW EXCLUSIVE MODE;\n    IF NOT EXISTS (SELECT 1 FROM pg_dist_rebalance_strategy t WHERE t.name = $1) THEN\n      RAISE EXCEPTION 'strategy with specified name does not exist';\n    END IF;\n    UPDATE pg_dist_rebalance_strategy SET default_strategy = false WHERE default_strategy = true;\n    UPDATE pg_dist_rebalance_strategy t SET default_strategy = true WHERE t.name = $1;\n  END;\n$$ LANGUAGE plpgsql;\n\nCOMMENT ON FUNCTION pg_catalog.citus_set_default_rebalance_strategy(text)\n  IS 'changes the default rebalance strategy to the one with the specified name';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_set_default_rebalance_strategy/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_set_default_rebalance_strategy(\n    name text\n)\n    RETURNS VOID\n    STRICT\nAS $$\n  BEGIN\n    LOCK TABLE pg_dist_rebalance_strategy IN SHARE ROW EXCLUSIVE MODE;\n    IF NOT EXISTS (SELECT 1 FROM pg_dist_rebalance_strategy t WHERE t.name = $1) THEN\n      RAISE EXCEPTION 'strategy with specified name does not exist';\n    END IF;\n    UPDATE pg_dist_rebalance_strategy SET default_strategy = false WHERE default_strategy = true;\n    UPDATE pg_dist_rebalance_strategy t SET default_strategy = true WHERE t.name = $1;\n  END;\n$$ LANGUAGE plpgsql;\n\nCOMMENT ON FUNCTION pg_catalog.citus_set_default_rebalance_strategy(text)\n  IS 'changes the default rebalance strategy to the one with the specified name';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_set_node_property/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  RETURNS VOID\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', 'citus_set_node_property';\nCOMMENT ON FUNCTION pg_catalog.citus_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  IS 'set a property of a node in pg_dist_node';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_set_node_property/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  RETURNS VOID\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', 'citus_set_node_property';\nCOMMENT ON FUNCTION pg_catalog.citus_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  IS 'set a property of a node in pg_dist_node';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_allowed_on_node_true/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_allowed_on_node_true(bigint, int)\n    RETURNS boolean AS $$ SELECT true $$ LANGUAGE sql;\nCOMMENT ON FUNCTION pg_catalog.citus_shard_allowed_on_node_true(bigint,int)\n  IS 'a shard_allowed_on_node_function for use by the rebalance algorithm that always returns true';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_allowed_on_node_true/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_allowed_on_node_true(bigint, int)\n    RETURNS boolean AS $$ SELECT true $$ LANGUAGE sql;\nCOMMENT ON FUNCTION pg_catalog.citus_shard_allowed_on_node_true(bigint,int)\n  IS 'a shard_allowed_on_node_function for use by the rebalance algorithm that always returns true';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_cost_1/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_cost_1(bigint)\n    RETURNS float4 AS $$ SELECT 1.0::float4 $$ LANGUAGE sql;\nCOMMENT ON FUNCTION pg_catalog.citus_shard_cost_1(bigint)\n  IS 'a shard cost function for use by the rebalance algorithm that always returns 1';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_cost_1/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_cost_1(bigint)\n    RETURNS float4 AS $$ SELECT 1.0::float4 $$ LANGUAGE sql;\nCOMMENT ON FUNCTION pg_catalog.citus_shard_cost_1(bigint)\n  IS 'a shard cost function for use by the rebalance algorithm that always returns 1';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_cost_by_disk_size/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_cost_by_disk_size(bigint)\n    RETURNS float4\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C STRICT VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.citus_shard_cost_by_disk_size(bigint)\n  IS 'a shard cost function for use by the rebalance algorithm that returns the disk size in bytes for the specified shard and the shards that are colocated with it';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_cost_by_disk_size/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_cost_by_disk_size(bigint)\n    RETURNS float4\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C STRICT VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.citus_shard_cost_by_disk_size(bigint)\n  IS 'a shard cost function for use by the rebalance algorithm that returns the disk size in bytes for the specified shard and the shards that are colocated with it';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_indexes_on_worker/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_indexes_on_worker(\n     OUT schema_name name,\n     OUT index_name name,\n     OUT table_type text,\n     OUT owner_name name,\n     OUT shard_name name)\n RETURNS SETOF record\n LANGUAGE plpgsql\n SET citus.hide_shards_from_app_name_prefixes = ''\n AS $$\nBEGIN\n  -- this is the query that \\di produces, except pg_table_is_visible\n  -- is replaced with pg_catalog.relation_is_a_known_shard(c.oid)\n  RETURN QUERY\n    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 'table' END as \"Type\",\n      pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\",\n      c2.relname as \"Table\"\n    FROM 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    WHERE c.relkind IN ('i','')\n      AND n.nspname <> 'pg_catalog'\n      AND n.nspname <> 'information_schema'\n      AND n.nspname !~ '^pg_toast'\n      AND pg_catalog.relation_is_a_known_shard(c.oid)\n    ORDER BY 1,2;\nEND;\n$$;\n\nCREATE OR REPLACE VIEW pg_catalog.citus_shard_indexes_on_worker AS\n\tSELECT schema_name as \"Schema\",\n\t  index_name as \"Name\",\n\t  table_type as \"Type\",\n\t  owner_name as \"Owner\",\n\t  shard_name as \"Table\"\n\tFROM pg_catalog.citus_shard_indexes_on_worker() s;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_indexes_on_worker/11.0-2.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_indexes_on_worker(\n     OUT schema_name name,\n     OUT index_name name,\n     OUT table_type text,\n     OUT owner_name name,\n     OUT shard_name name)\n RETURNS SETOF record\n LANGUAGE plpgsql\n SET citus.show_shards_for_app_name_prefixes = '*'\n AS $$\nBEGIN\n  -- this is the query that \\di produces, except pg_table_is_visible\n  -- is replaced with pg_catalog.relation_is_a_known_shard(c.oid)\n  RETURN QUERY\n    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 'table' END as \"Type\",\n      pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\",\n      c2.relname as \"Table\"\n    FROM 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    WHERE c.relkind IN ('i','')\n      AND n.nspname <> 'pg_catalog'\n      AND n.nspname <> 'information_schema'\n      AND n.nspname !~ '^pg_toast'\n      AND pg_catalog.relation_is_a_known_shard(c.oid)\n    ORDER BY 1,2;\nEND;\n$$;\n\nCREATE OR REPLACE VIEW pg_catalog.citus_shard_indexes_on_worker AS\n\tSELECT schema_name as \"Schema\",\n\t  index_name as \"Name\",\n\t  table_type as \"Type\",\n\t  owner_name as \"Owner\",\n\t  shard_name as \"Table\"\n\tFROM pg_catalog.citus_shard_indexes_on_worker() s;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_indexes_on_worker/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_indexes_on_worker(\n     OUT schema_name name,\n     OUT index_name name,\n     OUT table_type text,\n     OUT owner_name name,\n     OUT shard_name name)\n RETURNS SETOF record\n LANGUAGE plpgsql\n SET citus.show_shards_for_app_name_prefixes = '*'\n AS $$\nBEGIN\n  -- this is the query that \\di produces, except pg_table_is_visible\n  -- is replaced with pg_catalog.relation_is_a_known_shard(c.oid)\n  RETURN QUERY\n    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 'table' END as \"Type\",\n      pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\",\n      c2.relname as \"Table\"\n    FROM 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    WHERE c.relkind IN ('i','')\n      AND n.nspname <> 'pg_catalog'\n      AND n.nspname <> 'information_schema'\n      AND n.nspname !~ '^pg_toast'\n      AND pg_catalog.relation_is_a_known_shard(c.oid)\n    ORDER BY 1,2;\nEND;\n$$;\n\nCREATE OR REPLACE VIEW pg_catalog.citus_shard_indexes_on_worker AS\n\tSELECT schema_name as \"Schema\",\n\t  index_name as \"Name\",\n\t  table_type as \"Type\",\n\t  owner_name as \"Owner\",\n\t  shard_name as \"Table\"\n\tFROM pg_catalog.citus_shard_indexes_on_worker() s;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_sizes/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_shard_sizes(OUT table_name text, OUT size bigint)\n  RETURNS SETOF RECORD\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$citus_shard_sizes$$;\n COMMENT ON FUNCTION pg_catalog.citus_shard_sizes(OUT table_name text, OUT size bigint)\n     IS 'returns shards sizes across citus cluster';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_sizes/11.3-2.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_sizes(OUT shard_id int, OUT size bigint)\n  RETURNS SETOF RECORD\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$citus_shard_sizes$$;\n COMMENT ON FUNCTION pg_catalog.citus_shard_sizes(OUT shard_id int, OUT size bigint)\n     IS 'returns shards sizes across citus cluster';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shard_sizes/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_sizes(OUT shard_id int, OUT size bigint)\n  RETURNS SETOF RECORD\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$citus_shard_sizes$$;\n COMMENT ON FUNCTION pg_catalog.citus_shard_sizes(OUT shard_id int, OUT size bigint)\n     IS 'returns shards sizes across citus cluster';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shards/10.0-1.sql",
    "content": "CREATE OR REPLACE VIEW citus.citus_shards AS\nWITH shard_sizes AS (SELECT * FROM pg_catalog.citus_shard_sizes())\nSELECT\n     pg_dist_shard.logicalrelid AS table_name,\n     pg_dist_shard.shardid,\n     shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) as shard_name,\n     CASE WHEN partkey IS NOT NULL THEN 'distributed' WHEN repmodel = 't' THEN 'reference' ELSE 'local' END AS citus_table_type,\n     colocationid AS colocation_id,\n     pg_dist_node.nodename,\n     pg_dist_node.nodeport,\n     (SELECT size FROM shard_sizes WHERE\n       shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) = table_name\n       OR\n       'public.' || shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) = table_name\n      LIMIT 1) as shard_size\nFROM\n   pg_dist_shard\nJOIN\n   pg_dist_placement\nON\n   pg_dist_shard.shardid = pg_dist_placement.shardid\nJOIN\n   pg_dist_node\nON\n   pg_dist_placement.groupid = pg_dist_node.groupid\nJOIN\n   pg_dist_partition\nON\n   pg_dist_partition.logicalrelid = pg_dist_shard.logicalrelid\nORDER BY\n   pg_dist_shard.logicalrelid::text, shardid\n;\n\nALTER VIEW citus.citus_shards SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_shards TO public;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shards/10.1-1.sql",
    "content": "CREATE OR REPLACE VIEW pg_catalog.citus_shards AS\nSELECT\n     pg_dist_shard.logicalrelid AS table_name,\n     pg_dist_shard.shardid,\n     shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) as shard_name,\n     CASE WHEN partkey IS NOT NULL THEN 'distributed' WHEN repmodel = 't' THEN 'reference' ELSE 'local' END AS citus_table_type,\n     colocationid AS colocation_id,\n     pg_dist_node.nodename,\n     pg_dist_node.nodeport,\n     size as shard_size\nFROM\n   pg_dist_shard\nJOIN\n   pg_dist_placement\nON\n   pg_dist_shard.shardid = pg_dist_placement.shardid\nJOIN\n   pg_dist_node\nON\n   pg_dist_placement.groupid = pg_dist_node.groupid\nJOIN\n   pg_dist_partition\nON\n   pg_dist_partition.logicalrelid = pg_dist_shard.logicalrelid\nLEFT JOIN\n   (SELECT (regexp_matches(table_name,'_(\\d+)$'))[1]::int as shard_id, max(size) as size from citus_shard_sizes() GROUP BY shard_id) as shard_sizes\nON\n    pg_dist_shard.shardid = shard_sizes.shard_id\nWHERE\n   pg_dist_placement.shardstate = 1\nORDER BY\n   pg_dist_shard.logicalrelid::text, shardid\n;\n\nGRANT SELECT ON pg_catalog.citus_shards TO public;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shards/11.1-1.sql",
    "content": "CREATE OR REPLACE VIEW pg_catalog.citus_shards AS\nSELECT\n     pg_dist_shard.logicalrelid AS table_name,\n     pg_dist_shard.shardid,\n     shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) as shard_name,\n     CASE WHEN partkey IS NOT NULL THEN 'distributed' WHEN repmodel = 't' THEN 'reference' ELSE 'local' END AS citus_table_type,\n     colocationid AS colocation_id,\n     pg_dist_node.nodename,\n     pg_dist_node.nodeport,\n     size as shard_size\nFROM\n   pg_dist_shard\nJOIN\n   pg_dist_placement\nON\n   pg_dist_shard.shardid = pg_dist_placement.shardid\nJOIN\n   pg_dist_node\nON\n   pg_dist_placement.groupid = pg_dist_node.groupid\nJOIN\n   pg_dist_partition\nON\n   pg_dist_partition.logicalrelid = pg_dist_shard.logicalrelid\nLEFT JOIN\n   (SELECT (regexp_matches(table_name,'_(\\d+)$'))[1]::int as shard_id, max(size) as size from citus_shard_sizes() GROUP BY shard_id) as shard_sizes\nON\n    pg_dist_shard.shardid = shard_sizes.shard_id\nWHERE\n   pg_dist_placement.shardstate = 1\nAND\n   -- filter out tables owned by extensions\n   pg_dist_partition.logicalrelid NOT IN (\n      SELECT\n         objid\n      FROM\n         pg_depend\n      WHERE\n         classid = 'pg_class'::regclass AND refclassid = 'pg_extension'::regclass AND deptype = 'e'\n   )\nORDER BY\n   pg_dist_shard.logicalrelid::text, shardid\n;\n\nGRANT SELECT ON pg_catalog.citus_shards TO public;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shards/11.3-2.sql",
    "content": "CREATE OR REPLACE VIEW citus.citus_shards AS\nSELECT\n     pg_dist_shard.logicalrelid AS table_name,\n     pg_dist_shard.shardid,\n     shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) as shard_name,\n     CASE WHEN partkey IS NOT NULL THEN 'distributed' WHEN repmodel = 't' THEN 'reference' ELSE 'local' END AS citus_table_type,\n     colocationid AS colocation_id,\n     pg_dist_node.nodename,\n     pg_dist_node.nodeport,\n     size as shard_size\nFROM\n   pg_dist_shard\nJOIN\n   pg_dist_placement\nON\n   pg_dist_shard.shardid = pg_dist_placement.shardid\nJOIN\n   pg_dist_node\nON\n   pg_dist_placement.groupid = pg_dist_node.groupid\nJOIN\n   pg_dist_partition\nON\n   pg_dist_partition.logicalrelid = pg_dist_shard.logicalrelid\nLEFT JOIN\n   (SELECT shard_id, max(size) as size from citus_shard_sizes() GROUP BY shard_id) as shard_sizes\nON\n    pg_dist_shard.shardid = shard_sizes.shard_id\nWHERE\n   pg_dist_placement.shardstate = 1\nAND\n   -- filter out tables owned by extensions\n   pg_dist_partition.logicalrelid NOT IN (\n      SELECT\n         objid\n      FROM\n         pg_depend\n      WHERE\n         classid = 'pg_class'::regclass AND refclassid = 'pg_extension'::regclass AND deptype = 'e'\n   )\nORDER BY\n   pg_dist_shard.logicalrelid::text, shardid\n;\n\nALTER VIEW citus.citus_shards SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_shards TO public;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql",
    "content": "CREATE OR REPLACE VIEW citus.citus_shards AS\nSELECT\n     pg_dist_shard.logicalrelid AS table_name,\n     pg_dist_shard.shardid,\n     shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) as shard_name,\n     CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_schema) THEN 'schema'\n      WHEN partkey IS NOT NULL THEN 'distributed'\n      WHEN repmodel = 't' THEN 'reference'\n      WHEN colocationid = 0 THEN 'local'\n      ELSE 'distributed' END AS citus_table_type,\n     colocationid AS colocation_id,\n     pg_dist_node.nodename,\n     pg_dist_node.nodeport,\n     size as shard_size\nFROM\n   pg_dist_shard\nJOIN\n   pg_dist_placement\nON\n   pg_dist_shard.shardid = pg_dist_placement.shardid\nJOIN\n   pg_dist_node\nON\n   pg_dist_placement.groupid = pg_dist_node.groupid\nJOIN\n   pg_dist_partition\nON\n   pg_dist_partition.logicalrelid = pg_dist_shard.logicalrelid\nLEFT JOIN\n   (SELECT shard_id, max(size) as size from citus_shard_sizes() GROUP BY shard_id) as shard_sizes\nON\n    pg_dist_shard.shardid = shard_sizes.shard_id\nWHERE\n   pg_dist_placement.shardstate = 1\nAND\n   -- filter out tables owned by extensions\n   pg_dist_partition.logicalrelid NOT IN (\n      SELECT\n         objid\n      FROM\n         pg_depend\n      WHERE\n         classid = 'pg_class'::regclass AND refclassid = 'pg_extension'::regclass AND deptype = 'e'\n   )\nORDER BY\n   pg_dist_shard.logicalrelid::text, shardid\n;\n\nALTER VIEW citus.citus_shards SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_shards TO public;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shards/latest.sql",
    "content": "CREATE OR REPLACE VIEW citus.citus_shards AS\nSELECT\n     pg_dist_shard.logicalrelid AS table_name,\n     pg_dist_shard.shardid,\n     shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) as shard_name,\n     CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_schema) THEN 'schema'\n      WHEN partkey IS NOT NULL THEN 'distributed'\n      WHEN repmodel = 't' THEN 'reference'\n      WHEN colocationid = 0 THEN 'local'\n      ELSE 'distributed' END AS citus_table_type,\n     colocationid AS colocation_id,\n     pg_dist_node.nodename,\n     pg_dist_node.nodeport,\n     size as shard_size\nFROM\n   pg_dist_shard\nJOIN\n   pg_dist_placement\nON\n   pg_dist_shard.shardid = pg_dist_placement.shardid\nJOIN\n   pg_dist_node\nON\n   pg_dist_placement.groupid = pg_dist_node.groupid\nJOIN\n   pg_dist_partition\nON\n   pg_dist_partition.logicalrelid = pg_dist_shard.logicalrelid\nLEFT JOIN\n   (SELECT shard_id, max(size) as size from citus_shard_sizes() GROUP BY shard_id) as shard_sizes\nON\n    pg_dist_shard.shardid = shard_sizes.shard_id\nWHERE\n   pg_dist_placement.shardstate = 1\nAND\n   -- filter out tables owned by extensions\n   pg_dist_partition.logicalrelid NOT IN (\n      SELECT\n         objid\n      FROM\n         pg_depend\n      WHERE\n         classid = 'pg_class'::regclass AND refclassid = 'pg_extension'::regclass AND deptype = 'e'\n   )\nORDER BY\n   pg_dist_shard.logicalrelid::text, shardid\n;\n\nALTER VIEW citus.citus_shards SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_shards TO public;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shards_on_worker/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shards_on_worker(\n     OUT schema_name name,\n     OUT shard_name name,\n     OUT table_type text,\n     OUT owner_name name)\n RETURNS SETOF record\n LANGUAGE plpgsql\n SET citus.hide_shards_from_app_name_prefixes = ''\n AS $$\nBEGIN\n  -- this is the query that \\d produces, except pg_table_is_visible\n  -- is replaced with pg_catalog.relation_is_a_known_shard(c.oid)\n  RETURN QUERY\n\tSELECT n.nspname as \"Schema\",\n\t  c.relname as \"Name\",\n\t  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 'table' END as \"Type\",\n\t  pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\"\n\tFROM pg_catalog.pg_class c\n\t     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n\tWHERE c.relkind IN ('r','p','v','m','S','f','')\n\t      AND n.nspname <> 'pg_catalog'\n\t      AND n.nspname <> 'information_schema'\n\t      AND n.nspname !~ '^pg_toast'\n  \t\t  AND pg_catalog.relation_is_a_known_shard(c.oid)\n\tORDER BY 1,2;\nEND;\n$$;\n\nCREATE OR REPLACE VIEW pg_catalog.citus_shards_on_worker AS\n\tSELECT schema_name as \"Schema\",\n\t  shard_name as \"Name\",\n\t  table_type as \"Type\",\n\t  owner_name as \"Owner\"\n\tFROM pg_catalog.citus_shards_on_worker() s;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shards_on_worker/11.0-2.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shards_on_worker(\n     OUT schema_name name,\n     OUT shard_name name,\n     OUT table_type text,\n     OUT owner_name name)\n RETURNS SETOF record\n LANGUAGE plpgsql\n SET citus.show_shards_for_app_name_prefixes = '*'\n AS $$\nBEGIN\n  -- this is the query that \\d produces, except pg_table_is_visible\n  -- is replaced with pg_catalog.relation_is_a_known_shard(c.oid)\n  RETURN QUERY\n\tSELECT n.nspname as \"Schema\",\n\t  c.relname as \"Name\",\n\t  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 'table' END as \"Type\",\n\t  pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\"\n\tFROM pg_catalog.pg_class c\n\t     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n\tWHERE c.relkind IN ('r','p','v','m','S','f','')\n\t      AND n.nspname <> 'pg_catalog'\n\t      AND n.nspname <> 'information_schema'\n\t      AND n.nspname !~ '^pg_toast'\n  \t\t  AND pg_catalog.relation_is_a_known_shard(c.oid)\n\tORDER BY 1,2;\nEND;\n$$;\n\nCREATE OR REPLACE VIEW pg_catalog.citus_shards_on_worker AS\n\tSELECT schema_name as \"Schema\",\n\t  shard_name as \"Name\",\n\t  table_type as \"Type\",\n\t  owner_name as \"Owner\"\n\tFROM pg_catalog.citus_shards_on_worker() s;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_shards_on_worker/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_shards_on_worker(\n     OUT schema_name name,\n     OUT shard_name name,\n     OUT table_type text,\n     OUT owner_name name)\n RETURNS SETOF record\n LANGUAGE plpgsql\n SET citus.show_shards_for_app_name_prefixes = '*'\n AS $$\nBEGIN\n  -- this is the query that \\d produces, except pg_table_is_visible\n  -- is replaced with pg_catalog.relation_is_a_known_shard(c.oid)\n  RETURN QUERY\n\tSELECT n.nspname as \"Schema\",\n\t  c.relname as \"Name\",\n\t  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 'table' END as \"Type\",\n\t  pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\"\n\tFROM pg_catalog.pg_class c\n\t     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n\tWHERE c.relkind IN ('r','p','v','m','S','f','')\n\t      AND n.nspname <> 'pg_catalog'\n\t      AND n.nspname <> 'information_schema'\n\t      AND n.nspname !~ '^pg_toast'\n  \t\t  AND pg_catalog.relation_is_a_known_shard(c.oid)\n\tORDER BY 1,2;\nEND;\n$$;\n\nCREATE OR REPLACE VIEW pg_catalog.citus_shards_on_worker AS\n\tSELECT schema_name as \"Schema\",\n\t  shard_name as \"Name\",\n\t  table_type as \"Type\",\n\t  owner_name as \"Owner\"\n\tFROM pg_catalog.citus_shards_on_worker() s;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_split_shard_by_split_points/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_split_shard_by_split_points(\n    shard_id bigint,\n    split_points text[],\n    -- A 'nodeId' is a uint32 in CITUS [1, 4294967296] but postgres does not have unsigned type support.\n    -- Use integer (consistent with other previously defined UDFs that take nodeId as integer) as for all practical purposes it is big enough.\n    node_ids integer[],\n    -- Three modes to be implemented: block_writes, force_logical and auto.\n    -- The default mode is auto.\n    shard_transfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_split_shard_by_split_points$$;\nCOMMENT ON FUNCTION pg_catalog.citus_split_shard_by_split_points(shard_id bigint, split_points text[], nodeIds integer[], citus.shard_transfer_mode)\n    IS 'split a shard using split mode.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_split_shard_by_split_points/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_split_shard_by_split_points(\n    shard_id bigint,\n    split_points text[],\n    -- A 'nodeId' is a uint32 in CITUS [1, 4294967296] but postgres does not have unsigned type support.\n    -- Use integer (consistent with other previously defined UDFs that take nodeId as integer) as for all practical purposes it is big enough.\n    node_ids integer[],\n    -- Three modes to be implemented: block_writes, force_logical and auto.\n    -- The default mode is auto.\n    shard_transfer_mode citus.shard_transfer_mode default 'auto')\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_split_shard_by_split_points$$;\nCOMMENT ON FUNCTION pg_catalog.citus_split_shard_by_split_points(shard_id bigint, split_points text[], nodeIds integer[], citus.shard_transfer_mode)\n    IS 'split a shard using split mode.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_activity/11.0-1.sql",
    "content": "-- citus_stat_activity combines the pg_stat_activity views from all nodes and adds global_pid, nodeid and is_worker_query columns.\n-- The columns of citus_stat_activity don't change based on the Postgres version, however the pg_stat_activity's columns do.\n-- Both Postgres 13 and 14 added one more column to pg_stat_activity (leader_pid and query_id).\n-- citus_stat_activity has the most expansive column set, including the newly added columns.\n-- If citus_stat_activity is queried in a Postgres version where pg_stat_activity doesn't have some columns citus_stat_activity has\n-- the values for those columns will be NULL\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_stat_activity(OUT global_pid bigint, OUT nodeid int, OUT is_worker_query boolean, OUT datid oid, OUT datname name, OUT pid integer,\n                                                          OUT leader_pid integer, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr inet, OUT client_hostname text,\n                                                          OUT client_port integer, OUT backend_start timestamp with time zone, OUT xact_start timestamp with time zone,\n                                                          OUT query_start timestamp with time zone, OUT state_change timestamp with time zone, OUT wait_event_type text, OUT wait_event text,\n                                                          OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query_id bigint, OUT query text, OUT backend_type text)\n    RETURNS SETOF record\n    LANGUAGE plpgsql\n    AS $function$\nBEGIN\n    RETURN QUERY SELECT * FROM jsonb_to_recordset((\n        SELECT jsonb_agg(all_csa_rows_as_jsonb.csa_row_as_jsonb)::JSONB FROM (\n            SELECT jsonb_array_elements(run_command_on_all_nodes.result::JSONB)::JSONB || ('{\"nodeid\":' || run_command_on_all_nodes.nodeid || '}')::JSONB AS csa_row_as_jsonb\n            FROM run_command_on_all_nodes($$\n                SELECT coalesce(to_jsonb(array_agg(csa_from_one_node.*)), '[{}]'::JSONB)\n                FROM (\n                    SELECT global_pid, worker_query AS is_worker_query, pg_stat_activity.* FROM\n                    pg_stat_activity LEFT JOIN get_all_active_transactions() ON process_id = pid\n                ) AS csa_from_one_node;\n            $$, parallel:=true, give_warning_for_connection_errors:=true)\n            WHERE success = 't'\n        ) AS all_csa_rows_as_jsonb\n    ))\n    AS (global_pid bigint, nodeid int, is_worker_query boolean, datid oid, datname name, pid integer,\n        leader_pid integer, usesysid oid, usename name, application_name text, client_addr inet, client_hostname text,\n        client_port integer, backend_start timestamp with time zone, xact_start timestamp with time zone,\n        query_start timestamp with time zone, state_change timestamp with time zone, wait_event_type text, wait_event text,\n        state text, backend_xid xid, backend_xmin xid, query_id bigint, query text, backend_type text);\nEND;\n$function$;\n\nCREATE OR REPLACE VIEW citus.citus_stat_activity AS\nSELECT * FROM pg_catalog.citus_stat_activity();\n\nALTER VIEW citus.citus_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_stat_activity TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_activity/latest.sql",
    "content": "-- citus_stat_activity combines the pg_stat_activity views from all nodes and adds global_pid, nodeid and is_worker_query columns.\n-- The columns of citus_stat_activity don't change based on the Postgres version, however the pg_stat_activity's columns do.\n-- Both Postgres 13 and 14 added one more column to pg_stat_activity (leader_pid and query_id).\n-- citus_stat_activity has the most expansive column set, including the newly added columns.\n-- If citus_stat_activity is queried in a Postgres version where pg_stat_activity doesn't have some columns citus_stat_activity has\n-- the values for those columns will be NULL\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_stat_activity(OUT global_pid bigint, OUT nodeid int, OUT is_worker_query boolean, OUT datid oid, OUT datname name, OUT pid integer,\n                                                          OUT leader_pid integer, OUT usesysid oid, OUT usename name, OUT application_name text, OUT client_addr inet, OUT client_hostname text,\n                                                          OUT client_port integer, OUT backend_start timestamp with time zone, OUT xact_start timestamp with time zone,\n                                                          OUT query_start timestamp with time zone, OUT state_change timestamp with time zone, OUT wait_event_type text, OUT wait_event text,\n                                                          OUT state text, OUT backend_xid xid, OUT backend_xmin xid, OUT query_id bigint, OUT query text, OUT backend_type text)\n    RETURNS SETOF record\n    LANGUAGE plpgsql\n    AS $function$\nBEGIN\n    RETURN QUERY SELECT * FROM jsonb_to_recordset((\n        SELECT jsonb_agg(all_csa_rows_as_jsonb.csa_row_as_jsonb)::JSONB FROM (\n            SELECT jsonb_array_elements(run_command_on_all_nodes.result::JSONB)::JSONB || ('{\"nodeid\":' || run_command_on_all_nodes.nodeid || '}')::JSONB AS csa_row_as_jsonb\n            FROM run_command_on_all_nodes($$\n                SELECT coalesce(to_jsonb(array_agg(csa_from_one_node.*)), '[{}]'::JSONB)\n                FROM (\n                    SELECT global_pid, worker_query AS is_worker_query, pg_stat_activity.* FROM\n                    pg_stat_activity LEFT JOIN get_all_active_transactions() ON process_id = pid\n                ) AS csa_from_one_node;\n            $$, parallel:=true, give_warning_for_connection_errors:=true)\n            WHERE success = 't'\n        ) AS all_csa_rows_as_jsonb\n    ))\n    AS (global_pid bigint, nodeid int, is_worker_query boolean, datid oid, datname name, pid integer,\n        leader_pid integer, usesysid oid, usename name, application_name text, client_addr inet, client_hostname text,\n        client_port integer, backend_start timestamp with time zone, xact_start timestamp with time zone,\n        query_start timestamp with time zone, state_change timestamp with time zone, wait_event_type text, wait_event text,\n        state text, backend_xid xid, backend_xmin xid, query_id bigint, query text, backend_type text);\nEND;\n$function$;\n\nCREATE OR REPLACE VIEW citus.citus_stat_activity AS\nSELECT * FROM pg_catalog.citus_stat_activity();\n\nALTER VIEW citus.citus_stat_activity SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_stat_activity TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_counters/13.1-1.sql",
    "content": "-- See the comments for the function in\n-- src/backend/distributed/stats/stat_counters.c for more details.\nCREATE OR REPLACE FUNCTION pg_catalog.citus_stat_counters(\n\tdatabase_id oid DEFAULT 0,\n\n\t-- must always be the first column or you should accordingly update\n\t-- StoreDatabaseStatsIntoTupStore() function in src/backend/distributed/stats/stat_counters.c\n\tOUT database_id oid,\n\n\t-- Following stat counter columns must be in the same order as the\n\t-- StatType enum defined in src/include/distributed/stats/stat_counters.h\n\tOUT connection_establishment_succeeded bigint,\n\tOUT connection_establishment_failed bigint,\n\tOUT connection_reused bigint,\n\tOUT query_execution_single_shard bigint,\n\tOUT query_execution_multi_shard bigint,\n\n\t-- must always be the last column or you should accordingly update\n\t-- StoreDatabaseStatsIntoTupStore() function in src/backend/distributed/stats/stat_counters.c\n\tOUT stats_reset timestamp with time zone\n)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT VOLATILE PARALLEL SAFE\nAS 'MODULE_PATHNAME', $$citus_stat_counters$$;\nCOMMENT ON FUNCTION pg_catalog.citus_stat_counters(oid) IS 'Returns Citus stat counters for the given database OID, or for all databases if 0 is passed. Includes only databases with at least one connection since last restart, including dropped ones.';\n\n-- returns the stat counters for all the databases in local node\nCREATE VIEW citus.citus_stat_counters AS\nSELECT pg_database.oid,\n\t   pg_database.datname as name,\n\n\t   -- We always COALESCE the counters to 0 because the LEFT JOIN\n\t   -- will bring the databases that have never been connected to\n\t   -- since the last restart with NULL counters, but we want to\n\t   -- show them with 0 counters in the view.\n\t   COALESCE(citus_stat_counters.connection_establishment_succeeded, 0) as connection_establishment_succeeded,\n\t   COALESCE(citus_stat_counters.connection_establishment_failed, 0) as connection_establishment_failed,\n\t   COALESCE(citus_stat_counters.connection_reused, 0) as connection_reused,\n\t   COALESCE(citus_stat_counters.query_execution_single_shard, 0) as query_execution_single_shard,\n\t   COALESCE(citus_stat_counters.query_execution_multi_shard, 0) as query_execution_multi_shard,\n\n\t   citus_stat_counters.stats_reset\nFROM pg_catalog.pg_database\nLEFT JOIN (SELECT (pg_catalog.citus_stat_counters(0)).*) citus_stat_counters\nON (oid = database_id);\n\nALTER VIEW citus.citus_stat_counters SET SCHEMA pg_catalog;\n\nGRANT SELECT ON pg_catalog.citus_stat_counters TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_counters/latest.sql",
    "content": "-- See the comments for the function in\n-- src/backend/distributed/stats/stat_counters.c for more details.\nCREATE OR REPLACE FUNCTION pg_catalog.citus_stat_counters(\n\tdatabase_id oid DEFAULT 0,\n\n\t-- must always be the first column or you should accordingly update\n\t-- StoreDatabaseStatsIntoTupStore() function in src/backend/distributed/stats/stat_counters.c\n\tOUT database_id oid,\n\n\t-- Following stat counter columns must be in the same order as the\n\t-- StatType enum defined in src/include/distributed/stats/stat_counters.h\n\tOUT connection_establishment_succeeded bigint,\n\tOUT connection_establishment_failed bigint,\n\tOUT connection_reused bigint,\n\tOUT query_execution_single_shard bigint,\n\tOUT query_execution_multi_shard bigint,\n\n\t-- must always be the last column or you should accordingly update\n\t-- StoreDatabaseStatsIntoTupStore() function in src/backend/distributed/stats/stat_counters.c\n\tOUT stats_reset timestamp with time zone\n)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT VOLATILE PARALLEL SAFE\nAS 'MODULE_PATHNAME', $$citus_stat_counters$$;\nCOMMENT ON FUNCTION pg_catalog.citus_stat_counters(oid) IS 'Returns Citus stat counters for the given database OID, or for all databases if 0 is passed. Includes only databases with at least one connection since last restart, including dropped ones.';\n\n-- returns the stat counters for all the databases in local node\nCREATE VIEW citus.citus_stat_counters AS\nSELECT pg_database.oid,\n\t   pg_database.datname as name,\n\n\t   -- We always COALESCE the counters to 0 because the LEFT JOIN\n\t   -- will bring the databases that have never been connected to\n\t   -- since the last restart with NULL counters, but we want to\n\t   -- show them with 0 counters in the view.\n\t   COALESCE(citus_stat_counters.connection_establishment_succeeded, 0) as connection_establishment_succeeded,\n\t   COALESCE(citus_stat_counters.connection_establishment_failed, 0) as connection_establishment_failed,\n\t   COALESCE(citus_stat_counters.connection_reused, 0) as connection_reused,\n\t   COALESCE(citus_stat_counters.query_execution_single_shard, 0) as query_execution_single_shard,\n\t   COALESCE(citus_stat_counters.query_execution_multi_shard, 0) as query_execution_multi_shard,\n\n\t   citus_stat_counters.stats_reset\nFROM pg_catalog.pg_database\nLEFT JOIN (SELECT (pg_catalog.citus_stat_counters(0)).*) citus_stat_counters\nON (oid = database_id);\n\nALTER VIEW citus.citus_stat_counters SET SCHEMA pg_catalog;\n\nGRANT SELECT ON pg_catalog.citus_stat_counters TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_counters_reset/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_counters_reset(database_oid oid DEFAULT 0)\nRETURNS VOID\nLANGUAGE C STRICT PARALLEL SAFE\nAS 'MODULE_PATHNAME', $$citus_stat_counters_reset$$;\nCOMMENT ON FUNCTION pg_catalog.citus_stat_counters_reset(oid) IS 'Resets Citus stat counters for the given database OID or for the current database if nothing or 0 is provided.';\n\n-- Rather than using explicit superuser() check in the function, we use\n-- the GRANT system to REVOKE access to it when creating the extension.\n-- Administrators can later change who can access it, or leave them as\n-- only available to superuser / database cluster owner, if they choose.\nREVOKE ALL ON FUNCTION pg_catalog.citus_stat_counters_reset(oid) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_counters_reset/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_counters_reset(database_oid oid DEFAULT 0)\nRETURNS VOID\nLANGUAGE C STRICT PARALLEL SAFE\nAS 'MODULE_PATHNAME', $$citus_stat_counters_reset$$;\nCOMMENT ON FUNCTION pg_catalog.citus_stat_counters_reset(oid) IS 'Resets Citus stat counters for the given database OID or for the current database if nothing or 0 is provided.';\n\n-- Rather than using explicit superuser() check in the function, we use\n-- the GRANT system to REVOKE access to it when creating the extension.\n-- Administrators can later change who can access it, or leave them as\n-- only available to superuser / database cluster owner, if they choose.\nREVOKE ALL ON FUNCTION pg_catalog.citus_stat_counters_reset(oid) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_tenants/11.3-1.sql",
    "content": "-- cts in the query is an abbreviation for citus_stat_tenants\nCREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants (\n    return_all_tenants BOOLEAN DEFAULT FALSE,\n    OUT nodeid INT,\n    OUT colocation_id INT,\n    OUT tenant_attribute TEXT,\n    OUT read_count_in_this_period INT,\n    OUT read_count_in_last_period INT,\n    OUT query_count_in_this_period INT,\n    OUT query_count_in_last_period INT,\n    OUT cpu_usage_in_this_period DOUBLE PRECISION,\n    OUT cpu_usage_in_last_period DOUBLE PRECISION,\n    OUT score BIGINT\n)\n    RETURNS SETOF record\n    LANGUAGE plpgsql\n    AS $function$\nBEGIN\n    IF\n        array_position(enumvals, 'log') >= array_position(enumvals, setting)\n        AND setting != 'off'\n        FROM pg_settings\n        WHERE name = 'citus.stat_tenants_log_level'\n    THEN\n        RAISE LOG 'Generating citus_stat_tenants';\n    END IF;\n    RETURN QUERY\n    SELECT *\n    FROM jsonb_to_recordset((\n        SELECT\n            jsonb_agg(all_cst_rows_as_jsonb.cst_row_as_jsonb)::jsonb\n        FROM (\n            SELECT\n                jsonb_array_elements(run_command_on_all_nodes.result::jsonb)::jsonb ||\n                    ('{\"nodeid\":' || run_command_on_all_nodes.nodeid || '}')::jsonb AS cst_row_as_jsonb\n            FROM\n                run_command_on_all_nodes (\n                    $$\n                        SELECT\n                            coalesce(to_jsonb (array_agg(cstl.*)), '[]'::jsonb)\n                        FROM citus_stat_tenants_local($$||return_all_tenants||$$) cstl;\n                    $$,\n                    parallel:= TRUE,\n                    give_warning_for_connection_errors:= TRUE)\n            WHERE\n                success = 't')\n        AS all_cst_rows_as_jsonb))\nAS (\n    nodeid INT,\n    colocation_id INT,\n    tenant_attribute TEXT,\n    read_count_in_this_period INT,\n    read_count_in_last_period INT,\n    query_count_in_this_period INT,\n    query_count_in_last_period INT,\n    cpu_usage_in_this_period DOUBLE PRECISION,\n    cpu_usage_in_last_period DOUBLE PRECISION,\n    score BIGINT\n)\n    ORDER BY score DESC\n    LIMIT CASE WHEN NOT return_all_tenants THEN current_setting('citus.stat_tenants_limit')::BIGINT END;\nEND;\n$function$;\n\nCREATE OR REPLACE VIEW citus.citus_stat_tenants AS\nSELECT\n    nodeid,\n    colocation_id,\n    tenant_attribute,\n    read_count_in_this_period,\n    read_count_in_last_period,\n    query_count_in_this_period,\n    query_count_in_last_period,\n    cpu_usage_in_this_period,\n    cpu_usage_in_last_period\nFROM pg_catalog.citus_stat_tenants(FALSE);\n\nALTER VIEW citus.citus_stat_tenants SET SCHEMA pg_catalog;\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_stat_tenants(BOOLEAN) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_stat_tenants(BOOLEAN) TO pg_monitor;\n\nREVOKE ALL ON pg_catalog.citus_stat_tenants FROM PUBLIC;\nGRANT SELECT ON pg_catalog.citus_stat_tenants TO pg_monitor;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_tenants/latest.sql",
    "content": "-- cts in the query is an abbreviation for citus_stat_tenants\nCREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants (\n    return_all_tenants BOOLEAN DEFAULT FALSE,\n    OUT nodeid INT,\n    OUT colocation_id INT,\n    OUT tenant_attribute TEXT,\n    OUT read_count_in_this_period INT,\n    OUT read_count_in_last_period INT,\n    OUT query_count_in_this_period INT,\n    OUT query_count_in_last_period INT,\n    OUT cpu_usage_in_this_period DOUBLE PRECISION,\n    OUT cpu_usage_in_last_period DOUBLE PRECISION,\n    OUT score BIGINT\n)\n    RETURNS SETOF record\n    LANGUAGE plpgsql\n    AS $function$\nBEGIN\n    IF\n        array_position(enumvals, 'log') >= array_position(enumvals, setting)\n        AND setting != 'off'\n        FROM pg_settings\n        WHERE name = 'citus.stat_tenants_log_level'\n    THEN\n        RAISE LOG 'Generating citus_stat_tenants';\n    END IF;\n    RETURN QUERY\n    SELECT *\n    FROM jsonb_to_recordset((\n        SELECT\n            jsonb_agg(all_cst_rows_as_jsonb.cst_row_as_jsonb)::jsonb\n        FROM (\n            SELECT\n                jsonb_array_elements(run_command_on_all_nodes.result::jsonb)::jsonb ||\n                    ('{\"nodeid\":' || run_command_on_all_nodes.nodeid || '}')::jsonb AS cst_row_as_jsonb\n            FROM\n                run_command_on_all_nodes (\n                    $$\n                        SELECT\n                            coalesce(to_jsonb (array_agg(cstl.*)), '[]'::jsonb)\n                        FROM citus_stat_tenants_local($$||return_all_tenants||$$) cstl;\n                    $$,\n                    parallel:= TRUE,\n                    give_warning_for_connection_errors:= TRUE)\n            WHERE\n                success = 't')\n        AS all_cst_rows_as_jsonb))\nAS (\n    nodeid INT,\n    colocation_id INT,\n    tenant_attribute TEXT,\n    read_count_in_this_period INT,\n    read_count_in_last_period INT,\n    query_count_in_this_period INT,\n    query_count_in_last_period INT,\n    cpu_usage_in_this_period DOUBLE PRECISION,\n    cpu_usage_in_last_period DOUBLE PRECISION,\n    score BIGINT\n)\n    ORDER BY score DESC\n    LIMIT CASE WHEN NOT return_all_tenants THEN current_setting('citus.stat_tenants_limit')::BIGINT END;\nEND;\n$function$;\n\nCREATE OR REPLACE VIEW citus.citus_stat_tenants AS\nSELECT\n    nodeid,\n    colocation_id,\n    tenant_attribute,\n    read_count_in_this_period,\n    read_count_in_last_period,\n    query_count_in_this_period,\n    query_count_in_last_period,\n    cpu_usage_in_this_period,\n    cpu_usage_in_last_period\nFROM pg_catalog.citus_stat_tenants(FALSE);\n\nALTER VIEW citus.citus_stat_tenants SET SCHEMA pg_catalog;\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_stat_tenants(BOOLEAN) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_stat_tenants(BOOLEAN) TO pg_monitor;\n\nREVOKE ALL ON pg_catalog.citus_stat_tenants FROM PUBLIC;\nGRANT SELECT ON pg_catalog.citus_stat_tenants TO pg_monitor;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_tenants_local/11.3-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local(\n    return_all_tenants BOOLEAN DEFAULT FALSE,\n    OUT colocation_id INT,\n    OUT tenant_attribute TEXT,\n    OUT read_count_in_this_period INT,\n    OUT read_count_in_last_period INT,\n    OUT query_count_in_this_period INT,\n    OUT query_count_in_last_period INT,\n    OUT cpu_usage_in_this_period DOUBLE PRECISION,\n    OUT cpu_usage_in_last_period DOUBLE PRECISION,\n    OUT score BIGINT)\nRETURNS SETOF RECORD\nLANGUAGE C\nAS 'citus', $$citus_stat_tenants_local$$;\n\n\nCREATE OR REPLACE VIEW citus.citus_stat_tenants_local AS\nSELECT\n    colocation_id,\n    tenant_attribute,\n    read_count_in_this_period,\n    read_count_in_last_period,\n    query_count_in_this_period,\n    query_count_in_last_period,\n    cpu_usage_in_this_period,\n    cpu_usage_in_last_period\nFROM pg_catalog.citus_stat_tenants_local()\nORDER BY score DESC;\n\nALTER VIEW citus.citus_stat_tenants_local SET SCHEMA pg_catalog;\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_stat_tenants_local(BOOLEAN) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_stat_tenants_local(BOOLEAN) TO pg_monitor;\n\nREVOKE ALL ON pg_catalog.citus_stat_tenants_local FROM PUBLIC;\nGRANT SELECT ON pg_catalog.citus_stat_tenants_local TO pg_monitor;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_tenants_local/12.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local_internal(\n    return_all_tenants BOOLEAN DEFAULT FALSE,\n    OUT colocation_id INT,\n    OUT tenant_attribute TEXT,\n    OUT read_count_in_this_period INT,\n    OUT read_count_in_last_period INT,\n    OUT query_count_in_this_period INT,\n    OUT query_count_in_last_period INT,\n    OUT cpu_usage_in_this_period DOUBLE PRECISION,\n    OUT cpu_usage_in_last_period DOUBLE PRECISION,\n    OUT score BIGINT)\nRETURNS SETOF RECORD\nLANGUAGE C\nAS 'citus', $$citus_stat_tenants_local$$;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local(\n    return_all_tenants BOOLEAN DEFAULT FALSE,\n    OUT colocation_id INT,\n    OUT tenant_attribute TEXT,\n    OUT read_count_in_this_period INT,\n    OUT read_count_in_last_period INT,\n    OUT query_count_in_this_period INT,\n    OUT query_count_in_last_period INT,\n    OUT cpu_usage_in_this_period DOUBLE PRECISION,\n    OUT cpu_usage_in_last_period DOUBLE PRECISION,\n    OUT score BIGINT)\nRETURNS SETOF RECORD\nLANGUAGE plpgsql\nAS $function$\nBEGIN\n    RETURN QUERY\n    SELECT\n        L.colocation_id,\n        CASE WHEN L.tenant_attribute IS NULL THEN N.nspname ELSE L.tenant_attribute END COLLATE \"default\" as tenant_attribute,\n        L.read_count_in_this_period,\n        L.read_count_in_last_period,\n        L.query_count_in_this_period,\n        L.query_count_in_last_period,\n        L.cpu_usage_in_this_period,\n        L.cpu_usage_in_last_period,\n        L.score\n    FROM pg_catalog.citus_stat_tenants_local_internal(return_all_tenants) L\n    LEFT JOIN pg_dist_schema S ON L.tenant_attribute IS NULL AND L.colocation_id = S.colocationid\n    LEFT JOIN pg_namespace N ON N.oid = S.schemaid\n    ORDER BY L.score DESC;\nEND;\n$function$;\n\nCREATE OR REPLACE VIEW pg_catalog.citus_stat_tenants_local AS\nSELECT\n    colocation_id,\n    tenant_attribute,\n    read_count_in_this_period,\n    read_count_in_last_period,\n    query_count_in_this_period,\n    query_count_in_last_period,\n    cpu_usage_in_this_period,\n    cpu_usage_in_last_period\nFROM pg_catalog.citus_stat_tenants_local()\nORDER BY score DESC;\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_stat_tenants_local_internal(BOOLEAN) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_stat_tenants_local_internal(BOOLEAN) TO pg_monitor;\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_stat_tenants_local(BOOLEAN) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_stat_tenants_local(BOOLEAN) TO pg_monitor;\n\nREVOKE ALL ON pg_catalog.citus_stat_tenants_local FROM PUBLIC;\nGRANT SELECT ON pg_catalog.citus_stat_tenants_local TO pg_monitor;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local_internal(\n    return_all_tenants BOOLEAN DEFAULT FALSE,\n    OUT colocation_id INT,\n    OUT tenant_attribute TEXT,\n    OUT read_count_in_this_period INT,\n    OUT read_count_in_last_period INT,\n    OUT query_count_in_this_period INT,\n    OUT query_count_in_last_period INT,\n    OUT cpu_usage_in_this_period DOUBLE PRECISION,\n    OUT cpu_usage_in_last_period DOUBLE PRECISION,\n    OUT score BIGINT)\nRETURNS SETOF RECORD\nLANGUAGE C\nAS 'citus', $$citus_stat_tenants_local$$;\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local(\n    return_all_tenants BOOLEAN DEFAULT FALSE,\n    OUT colocation_id INT,\n    OUT tenant_attribute TEXT,\n    OUT read_count_in_this_period INT,\n    OUT read_count_in_last_period INT,\n    OUT query_count_in_this_period INT,\n    OUT query_count_in_last_period INT,\n    OUT cpu_usage_in_this_period DOUBLE PRECISION,\n    OUT cpu_usage_in_last_period DOUBLE PRECISION,\n    OUT score BIGINT)\nRETURNS SETOF RECORD\nLANGUAGE plpgsql\nAS $function$\nBEGIN\n    RETURN QUERY\n    SELECT\n        L.colocation_id,\n        CASE WHEN L.tenant_attribute IS NULL THEN N.nspname ELSE L.tenant_attribute END COLLATE \"default\" as tenant_attribute,\n        L.read_count_in_this_period,\n        L.read_count_in_last_period,\n        L.query_count_in_this_period,\n        L.query_count_in_last_period,\n        L.cpu_usage_in_this_period,\n        L.cpu_usage_in_last_period,\n        L.score\n    FROM pg_catalog.citus_stat_tenants_local_internal(return_all_tenants) L\n    LEFT JOIN pg_dist_schema S ON L.tenant_attribute IS NULL AND L.colocation_id = S.colocationid\n    LEFT JOIN pg_namespace N ON N.oid = S.schemaid\n    ORDER BY L.score DESC;\nEND;\n$function$;\n\nCREATE OR REPLACE VIEW pg_catalog.citus_stat_tenants_local AS\nSELECT\n    colocation_id,\n    tenant_attribute,\n    read_count_in_this_period,\n    read_count_in_last_period,\n    query_count_in_this_period,\n    query_count_in_last_period,\n    cpu_usage_in_this_period,\n    cpu_usage_in_last_period\nFROM pg_catalog.citus_stat_tenants_local()\nORDER BY score DESC;\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_stat_tenants_local_internal(BOOLEAN) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_stat_tenants_local_internal(BOOLEAN) TO pg_monitor;\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_stat_tenants_local(BOOLEAN) FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_stat_tenants_local(BOOLEAN) TO pg_monitor;\n\nREVOKE ALL ON pg_catalog.citus_stat_tenants_local FROM PUBLIC;\nGRANT SELECT ON pg_catalog.citus_stat_tenants_local TO pg_monitor;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_tenants_local_reset/11.3-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local_reset()\n    RETURNS VOID\n    LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_stat_tenants_local_reset$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_stat_tenants_local_reset()\n    IS 'resets the local tenant statistics';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_tenants_local_reset/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local_reset()\n    RETURNS VOID\n    LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_stat_tenants_local_reset$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_stat_tenants_local_reset()\n    IS 'resets the local tenant statistics';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_tenants_reset/11.3-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_reset()\n    RETURNS VOID\n    LANGUAGE plpgsql\nAS $function$\nBEGIN\n    PERFORM run_command_on_all_nodes($$SELECT citus_stat_tenants_local_reset()$$);\nEND;\n$function$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stat_tenants_reset/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_reset()\n    RETURNS VOID\n    LANGUAGE plpgsql\nAS $function$\nBEGIN\n    PERFORM run_command_on_all_nodes($$SELECT citus_stat_tenants_local_reset()$$);\nEND;\n$function$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stats/13.2-1.sql",
    "content": "SET search_path = 'pg_catalog';\nDROP VIEW IF EXISTS pg_catalog.citus_stats;\n\nCREATE OR REPLACE VIEW citus.citus_stats AS\n\nWITH most_common_vals_double_json AS (\n    SELECT ( SELECT json_agg(row_to_json(f)) FROM ( SELECT * FROM run_command_on_shards(logicalrelid,\n            $$ SELECT json_agg(row_to_json(shard_stats)) FROM (\n            SELECT '$$ || logicalrelid || $$' AS citus_table, attname, s.null_frac,\n                   most_common_vals, most_common_freqs, c.reltuples AS reltuples\n            -- join on tablename is enough here, no need to join with pg_namespace\n            -- since shards have unique ids in their names, hence two shard names\n            -- could never be the same\n            FROM pg_stats s RIGHT JOIN pg_class c ON (s.tablename = c.relname)\n            WHERE c.oid = '%s'::regclass) shard_stats $$ ))f)\n        FROM pg_dist_partition),\n\nmost_common_vals_json AS (\n    SELECT (json_array_elements(json_agg)->>'result') AS result,\n\t       (json_array_elements(json_agg)->>'shardid') AS shardid\n    FROM most_common_vals_double_json),\n\ntable_reltuples_json AS (\n    SELECT distinct(shardid),\n           CAST( CAST((json_array_elements(result::json)->>'reltuples') AS DOUBLE PRECISION) AS bigint) AS shard_reltuples,\n\t       (json_array_elements(result::json)->>'citus_table')::regclass AS citus_table\n    FROM most_common_vals_json),\n\ntable_reltuples AS (\n        SELECT citus_table, sum(shard_reltuples) AS table_reltuples\n        FROM table_reltuples_json GROUP BY 1 ORDER BY 1),\n\nnull_frac_json AS (\n    SELECT (json_array_elements(result::json)->>'citus_table')::regclass AS citus_table,\n           CAST( CAST((json_array_elements(result::json)->>'reltuples') AS DOUBLE PRECISION) AS bigint) AS shard_reltuples,\n           CAST((json_array_elements(result::json)->>'null_frac') AS float4) AS null_frac,\n           (json_array_elements(result::json)->>'attname')::text AS attname\n    FROM most_common_vals_json\n),\n\nnull_occurrences AS (\n    SELECT citus_table, attname, sum(null_frac * shard_reltuples)::bigint AS null_occurrences\n    FROM null_frac_json\n    GROUP BY 1, 2\n    ORDER BY 1, 2\n),\n\nmost_common_vals AS (\n    SELECT (json_array_elements(result::json)->>'citus_table')::regclass AS citus_table,\n           (json_array_elements(result::json)->>'attname')::text AS attname,\n           json_array_elements_text((json_array_elements(result::json)->>'most_common_vals')::json)::text AS common_val,\n           CAST(json_array_elements_text((json_array_elements(result::json)->>'most_common_freqs')::json) AS float4) AS common_freq,\n           CAST( CAST((json_array_elements(result::json)->>'reltuples') AS DOUBLE PRECISION) AS bigint) AS shard_reltuples\n    FROM most_common_vals_json),\n\ncommon_val_occurrence AS (\n    SELECT citus_table, m.attname, common_val,\n            sum(common_freq * shard_reltuples)::bigint AS occurrence\n    FROM most_common_vals m\n    GROUP BY citus_table, m.attname, common_val\n    ORDER BY 1, 2, occurrence DESC, 3)\n\nSELECT nsp.nspname AS schemaname, p.relname AS tablename, c.attname,\n\n       CASE WHEN max(t.table_reltuples::bigint) = 0 THEN 0\n       ELSE max(n.null_occurrences/t.table_reltuples)::float4 END AS null_frac,\n\n       ARRAY_agg(common_val) AS most_common_vals,\n\n       CASE WHEN max(t.table_reltuples::bigint) = 0 THEN NULL\n       ELSE ARRAY_agg((occurrence/t.table_reltuples)::float4) END AS most_common_freqs\n\nFROM common_val_occurrence c, table_reltuples t, null_occurrences n, pg_class p, pg_namespace nsp\nWHERE c.citus_table = t.citus_table\n      AND c.citus_table = n.citus_table AND c.attname = n.attname\n      AND c.citus_table::regclass::oid = p.oid AND p.relnamespace = nsp.oid\nGROUP BY nsp.nspname, c.citus_table, p.relname, c.attname;\n\nALTER VIEW citus.citus_stats SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_stats TO PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_stats/latest.sql",
    "content": "SET search_path = 'pg_catalog';\nDROP VIEW IF EXISTS pg_catalog.citus_stats;\n\nCREATE OR REPLACE VIEW citus.citus_stats AS\n\nWITH most_common_vals_double_json AS (\n    SELECT ( SELECT json_agg(row_to_json(f)) FROM ( SELECT * FROM run_command_on_shards(logicalrelid,\n            $$ SELECT json_agg(row_to_json(shard_stats)) FROM (\n            SELECT '$$ || logicalrelid || $$' AS citus_table, attname, s.null_frac,\n                   most_common_vals, most_common_freqs, c.reltuples AS reltuples\n            -- join on tablename is enough here, no need to join with pg_namespace\n            -- since shards have unique ids in their names, hence two shard names\n            -- could never be the same\n            FROM pg_stats s RIGHT JOIN pg_class c ON (s.tablename = c.relname)\n            WHERE c.oid = '%s'::regclass) shard_stats $$ ))f)\n        FROM pg_dist_partition),\n\nmost_common_vals_json AS (\n    SELECT (json_array_elements(json_agg)->>'result') AS result,\n\t       (json_array_elements(json_agg)->>'shardid') AS shardid\n    FROM most_common_vals_double_json),\n\ntable_reltuples_json AS (\n    SELECT distinct(shardid),\n           CAST( CAST((json_array_elements(result::json)->>'reltuples') AS DOUBLE PRECISION) AS bigint) AS shard_reltuples,\n\t       (json_array_elements(result::json)->>'citus_table')::regclass AS citus_table\n    FROM most_common_vals_json),\n\ntable_reltuples AS (\n        SELECT citus_table, sum(shard_reltuples) AS table_reltuples\n        FROM table_reltuples_json GROUP BY 1 ORDER BY 1),\n\nnull_frac_json AS (\n    SELECT (json_array_elements(result::json)->>'citus_table')::regclass AS citus_table,\n           CAST( CAST((json_array_elements(result::json)->>'reltuples') AS DOUBLE PRECISION) AS bigint) AS shard_reltuples,\n           CAST((json_array_elements(result::json)->>'null_frac') AS float4) AS null_frac,\n           (json_array_elements(result::json)->>'attname')::text AS attname\n    FROM most_common_vals_json\n),\n\nnull_occurrences AS (\n    SELECT citus_table, attname, sum(null_frac * shard_reltuples)::bigint AS null_occurrences\n    FROM null_frac_json\n    GROUP BY 1, 2\n    ORDER BY 1, 2\n),\n\nmost_common_vals AS (\n    SELECT (json_array_elements(result::json)->>'citus_table')::regclass AS citus_table,\n           (json_array_elements(result::json)->>'attname')::text AS attname,\n           json_array_elements_text((json_array_elements(result::json)->>'most_common_vals')::json)::text AS common_val,\n           CAST(json_array_elements_text((json_array_elements(result::json)->>'most_common_freqs')::json) AS float4) AS common_freq,\n           CAST( CAST((json_array_elements(result::json)->>'reltuples') AS DOUBLE PRECISION) AS bigint) AS shard_reltuples\n    FROM most_common_vals_json),\n\ncommon_val_occurrence AS (\n    SELECT citus_table, m.attname, common_val,\n            sum(common_freq * shard_reltuples)::bigint AS occurrence\n    FROM most_common_vals m\n    GROUP BY citus_table, m.attname, common_val\n    ORDER BY 1, 2, occurrence DESC, 3)\n\nSELECT nsp.nspname AS schemaname, p.relname AS tablename, c.attname,\n\n       CASE WHEN max(t.table_reltuples::bigint) = 0 THEN 0\n       ELSE max(n.null_occurrences/t.table_reltuples)::float4 END AS null_frac,\n\n       ARRAY_agg(common_val) AS most_common_vals,\n\n       CASE WHEN max(t.table_reltuples::bigint) = 0 THEN NULL\n       ELSE ARRAY_agg((occurrence/t.table_reltuples)::float4) END AS most_common_freqs\n\nFROM common_val_occurrence c, table_reltuples t, null_occurrences n, pg_class p, pg_namespace nsp\nWHERE c.citus_table = t.citus_table\n      AND c.citus_table = n.citus_table AND c.attname = n.attname\n      AND c.citus_table::regclass::oid = p.oid AND p.relnamespace = nsp.oid\nGROUP BY nsp.nspname, c.citus_table, p.relname, c.attname;\n\nALTER VIEW citus.citus_stats SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.citus_stats TO PUBLIC;\n\nRESET search_path;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_tables/10.0-1.sql",
    "content": "CREATE VIEW public.citus_tables AS\nSELECT\n  logicalrelid AS table_name,\n  CASE WHEN partkey IS NOT NULL THEN 'distributed' ELSE 'reference' END AS citus_table_type,\n  coalesce(column_to_column_name(logicalrelid, partkey), '<none>') AS distribution_column,\n  colocationid AS colocation_id,\n  pg_size_pretty(citus_total_relation_size(logicalrelid, fail_on_error := false)) AS table_size,\n  (select count(*) from pg_dist_shard where logicalrelid = p.logicalrelid) AS shard_count,\n  pg_get_userbyid(relowner) AS table_owner,\n  amname AS access_method\nFROM\n  pg_dist_partition p\nJOIN\n  pg_class c ON (p.logicalrelid = c.oid)\nLEFT JOIN\n  pg_am a ON (a.oid = c.relam)\nWHERE\n  partkey IS NOT NULL OR repmodel = 't'\nORDER BY\n  logicalrelid::text;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_tables/10.0-4.sql",
    "content": "DO $$\ndeclare\ncitus_tables_create_query text;\nBEGIN\ncitus_tables_create_query=$CTCQ$\n    CREATE OR REPLACE VIEW %I.citus_tables AS\n    SELECT\n        logicalrelid AS table_name,\n        CASE WHEN partkey IS NOT NULL THEN 'distributed' ELSE 'reference' END AS citus_table_type,\n        coalesce(column_to_column_name(logicalrelid, partkey), '<none>') AS distribution_column,\n        colocationid AS colocation_id,\n        pg_size_pretty(citus_total_relation_size(logicalrelid, fail_on_error := false)) AS table_size,\n        (select count(*) from pg_dist_shard where logicalrelid = p.logicalrelid) AS shard_count,\n        pg_get_userbyid(relowner) AS table_owner,\n        amname AS access_method\n    FROM\n        pg_dist_partition p\n    JOIN\n        pg_class c ON (p.logicalrelid = c.oid)\n    LEFT JOIN\n        pg_am a ON (a.oid = c.relam)\n    WHERE\n        partkey IS NOT NULL OR repmodel = 't'\n    ORDER BY\n        logicalrelid::text;\n$CTCQ$;\n\nIF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'public') THEN\n    EXECUTE format(citus_tables_create_query, 'public');\n    GRANT SELECT ON public.citus_tables TO public;\nELSE\n    EXECUTE format(citus_tables_create_query, 'citus');\n    ALTER VIEW citus.citus_tables SET SCHEMA pg_catalog;\n    GRANT SELECT ON pg_catalog.citus_tables TO public;\nEND IF;\n\nEND;\n$$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_tables/11.1-1.sql",
    "content": "DO $$\ndeclare\ncitus_tables_create_query text;\nBEGIN\ncitus_tables_create_query=$CTCQ$\n    CREATE OR REPLACE VIEW %I.citus_tables AS\n    SELECT\n        logicalrelid AS table_name,\n        CASE WHEN partkey IS NOT NULL THEN 'distributed' ELSE\n            CASE when repmodel = 't' THEN 'reference' ELSE 'local' END\n        END AS citus_table_type,\n        coalesce(column_to_column_name(logicalrelid, partkey), '<none>') AS distribution_column,\n        colocationid AS colocation_id,\n        pg_size_pretty(citus_total_relation_size(logicalrelid, fail_on_error := false)) AS table_size,\n        (select count(*) from pg_dist_shard where logicalrelid = p.logicalrelid) AS shard_count,\n        pg_get_userbyid(relowner) AS table_owner,\n        amname AS access_method\n    FROM\n        pg_dist_partition p\n    JOIN\n        pg_class c ON (p.logicalrelid = c.oid)\n    LEFT JOIN\n        pg_am a ON (a.oid = c.relam)\n    WHERE\n        -- filter out tables owned by extensions\n        logicalrelid NOT IN (\n            SELECT\n                objid\n            FROM\n                pg_depend\n            WHERE\n                classid = 'pg_class'::regclass AND refclassid = 'pg_extension'::regclass AND deptype = 'e'\n        )\n    ORDER BY\n        logicalrelid::text;\n$CTCQ$;\n\nIF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'public') THEN\n    EXECUTE format(citus_tables_create_query, 'public');\n    GRANT SELECT ON public.citus_tables TO public;\nELSE\n    EXECUTE format(citus_tables_create_query, 'citus');\n    ALTER VIEW citus.citus_tables SET SCHEMA pg_catalog;\n    GRANT SELECT ON pg_catalog.citus_tables TO public;\nEND IF;\n\nEND;\n$$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_tables/11.3-2.sql",
    "content": "DO $$\ndeclare\ncitus_tables_create_query text;\nBEGIN\ncitus_tables_create_query=$CTCQ$\n    CREATE OR REPLACE VIEW %I.citus_tables AS\n    SELECT\n        logicalrelid AS table_name,\n        CASE WHEN partkey IS NOT NULL THEN 'distributed' ELSE\n            CASE when repmodel = 't' THEN 'reference' ELSE 'local' END\n        END AS citus_table_type,\n        coalesce(column_to_column_name(logicalrelid, partkey), '<none>') AS distribution_column,\n        colocationid AS colocation_id,\n        pg_size_pretty(table_sizes.table_size) AS table_size,\n        (select count(*) from pg_dist_shard where logicalrelid = p.logicalrelid) AS shard_count,\n        pg_get_userbyid(relowner) AS table_owner,\n        amname AS access_method\n    FROM\n        pg_dist_partition p\n    JOIN\n        pg_class c ON (p.logicalrelid = c.oid)\n    LEFT JOIN\n        pg_am a ON (a.oid = c.relam)\n    JOIN\n        (\n            SELECT ds.logicalrelid AS table_id, SUM(css.size) AS table_size\n            FROM citus_shard_sizes() css, pg_dist_shard ds\n            WHERE css.shard_id = ds.shardid\n            GROUP BY ds.logicalrelid\n        ) table_sizes ON (table_sizes.table_id = p.logicalrelid)\n    WHERE\n        -- filter out tables owned by extensions\n        logicalrelid NOT IN (\n            SELECT\n                objid\n            FROM\n                pg_depend\n            WHERE\n                classid = 'pg_class'::regclass AND refclassid = 'pg_extension'::regclass AND deptype = 'e'\n        )\n    ORDER BY\n        logicalrelid::text;\n$CTCQ$;\n\nIF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'public') THEN\n    EXECUTE format(citus_tables_create_query, 'public');\n    GRANT SELECT ON public.citus_tables TO public;\nELSE\n    EXECUTE format(citus_tables_create_query, 'citus');\n    ALTER VIEW citus.citus_tables SET SCHEMA pg_catalog;\n    GRANT SELECT ON pg_catalog.citus_tables TO public;\nEND IF;\n\nEND;\n$$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_tables/12.0-1.sql",
    "content": "DO $$\ndeclare\ncitus_tables_create_query text;\nBEGIN\ncitus_tables_create_query=$CTCQ$\n    CREATE OR REPLACE VIEW %I.citus_tables AS\n    SELECT\n        logicalrelid AS table_name,\n        CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_schema) THEN 'schema'\n            WHEN partkey IS NOT NULL THEN 'distributed'\n            WHEN repmodel = 't' THEN 'reference'\n            WHEN colocationid = 0 THEN 'local'\n            ELSE 'distributed'\n        END AS citus_table_type,\n        coalesce(column_to_column_name(logicalrelid, partkey), '<none>') AS distribution_column,\n        colocationid AS colocation_id,\n        pg_size_pretty(table_sizes.table_size) AS table_size,\n        (select count(*) from pg_dist_shard where logicalrelid = p.logicalrelid) AS shard_count,\n        pg_get_userbyid(relowner) AS table_owner,\n        amname AS access_method\n    FROM\n        pg_dist_partition p\n    JOIN\n        pg_class c ON (p.logicalrelid = c.oid)\n    LEFT JOIN\n        pg_am a ON (a.oid = c.relam)\n    JOIN\n        (\n            SELECT ds.logicalrelid AS table_id, SUM(css.size) AS table_size\n            FROM citus_shard_sizes() css, pg_dist_shard ds\n            WHERE css.shard_id = ds.shardid\n            GROUP BY ds.logicalrelid\n        ) table_sizes ON (table_sizes.table_id = p.logicalrelid)\n    WHERE\n        -- filter out tables owned by extensions\n        logicalrelid NOT IN (\n            SELECT\n                objid\n            FROM\n                pg_depend\n            WHERE\n                classid = 'pg_class'::regclass AND refclassid = 'pg_extension'::regclass AND deptype = 'e'\n        )\n    ORDER BY\n        logicalrelid::text;\n$CTCQ$;\n\nIF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'public') THEN\n    EXECUTE format(citus_tables_create_query, 'public');\n    GRANT SELECT ON public.citus_tables TO public;\nELSE\n    EXECUTE format(citus_tables_create_query, 'citus');\n    ALTER VIEW citus.citus_tables SET SCHEMA pg_catalog;\n    GRANT SELECT ON pg_catalog.citus_tables TO public;\nEND IF;\n\nEND;\n$$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_tables/latest.sql",
    "content": "DO $$\ndeclare\ncitus_tables_create_query text;\nBEGIN\ncitus_tables_create_query=$CTCQ$\n    CREATE OR REPLACE VIEW %I.citus_tables AS\n    SELECT\n        logicalrelid AS table_name,\n        CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_schema) THEN 'schema'\n            WHEN partkey IS NOT NULL THEN 'distributed'\n            WHEN repmodel = 't' THEN 'reference'\n            WHEN colocationid = 0 THEN 'local'\n            ELSE 'distributed'\n        END AS citus_table_type,\n        coalesce(column_to_column_name(logicalrelid, partkey), '<none>') AS distribution_column,\n        colocationid AS colocation_id,\n        pg_size_pretty(table_sizes.table_size) AS table_size,\n        (select count(*) from pg_dist_shard where logicalrelid = p.logicalrelid) AS shard_count,\n        pg_get_userbyid(relowner) AS table_owner,\n        amname AS access_method\n    FROM\n        pg_dist_partition p\n    JOIN\n        pg_class c ON (p.logicalrelid = c.oid)\n    LEFT JOIN\n        pg_am a ON (a.oid = c.relam)\n    JOIN\n        (\n            SELECT ds.logicalrelid AS table_id, SUM(css.size) AS table_size\n            FROM citus_shard_sizes() css, pg_dist_shard ds\n            WHERE css.shard_id = ds.shardid\n            GROUP BY ds.logicalrelid\n        ) table_sizes ON (table_sizes.table_id = p.logicalrelid)\n    WHERE\n        -- filter out tables owned by extensions\n        logicalrelid NOT IN (\n            SELECT\n                objid\n            FROM\n                pg_depend\n            WHERE\n                classid = 'pg_class'::regclass AND refclassid = 'pg_extension'::regclass AND deptype = 'e'\n        )\n    ORDER BY\n        logicalrelid::text;\n$CTCQ$;\n\nIF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'public') THEN\n    EXECUTE format(citus_tables_create_query, 'public');\n    GRANT SELECT ON public.citus_tables TO public;\nELSE\n    EXECUTE format(citus_tables_create_query, 'citus');\n    ALTER VIEW citus.citus_tables SET SCHEMA pg_catalog;\n    GRANT SELECT ON pg_catalog.citus_tables TO public;\nEND IF;\n\nEND;\n$$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_task_wait/11.2-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_task_wait(taskid bigint, desired_status pg_catalog.citus_task_status DEFAULT NULL)\n    RETURNS VOID\n    LANGUAGE C\n    AS 'MODULE_PATHNAME',$$citus_task_wait$$;\nCOMMENT ON FUNCTION pg_catalog.citus_task_wait(taskid bigint, desired_status pg_catalog.citus_task_status)\n    IS 'blocks till the task identified by taskid is at the specified status, or reached a terminal status. Only waits for terminal status when no desired_status was specified. The return value indicates if the desired status was reached or not. When no desired status was specified it will assume any terminal status was desired';\n\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_task_wait(taskid bigint, desired_status pg_catalog.citus_task_status) TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_task_wait/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_task_wait(taskid bigint, desired_status pg_catalog.citus_task_status DEFAULT NULL)\n    RETURNS VOID\n    LANGUAGE C\n    AS 'MODULE_PATHNAME',$$citus_task_wait$$;\nCOMMENT ON FUNCTION pg_catalog.citus_task_wait(taskid bigint, desired_status pg_catalog.citus_task_status)\n    IS 'blocks till the task identified by taskid is at the specified status, or reached a terminal status. Only waits for terminal status when no desired_status was specified. The return value indicates if the desired status was reached or not. When no desired status was specified it will assume any terminal status was desired';\n\nGRANT EXECUTE ON FUNCTION pg_catalog.citus_task_wait(taskid bigint, desired_status pg_catalog.citus_task_status) TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_total_relation_size/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_total_relation_size(logicalrelid regclass, fail_on_error boolean default true)\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_total_relation_size$$;\nCOMMENT ON FUNCTION pg_catalog.citus_total_relation_size(logicalrelid regclass, boolean)\n    IS 'get total disk space used by the specified table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_total_relation_size/7.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_total_relation_size(logicalrelid regclass)\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_total_relation_size$$;\nCOMMENT ON FUNCTION pg_catalog.citus_total_relation_size(logicalrelid regclass)\n    IS 'get total disk space used by the specified table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_total_relation_size/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_total_relation_size(logicalrelid regclass, fail_on_error boolean default true)\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_total_relation_size$$;\nCOMMENT ON FUNCTION pg_catalog.citus_total_relation_size(logicalrelid regclass, boolean)\n    IS 'get total disk space used by the specified table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_unmark_object_distributed/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_unmark_object_distributed$$;\nCOMMENT ON FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int)\n    IS 'remove an object address from citus.pg_dist_object once the object has been deleted';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_unmark_object_distributed/13.1-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int, checkobjectexistence boolean DEFAULT true)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_unmark_object_distributed$$;\nCOMMENT ON FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int, checkobjectexistence boolean)\n    IS 'Removes an object from citus.pg_dist_object after deletion. If checkobjectexistence is true, object existence check performed.'\n       'Otherwise, object existence check is skipped.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_unmark_object_distributed/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int, checkobjectexistence boolean DEFAULT true)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_unmark_object_distributed$$;\nCOMMENT ON FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int, checkobjectexistence boolean)\n    IS 'Removes an object from citus.pg_dist_object after deletion. If checkobjectexistence is true, object existence check performed.'\n       'Otherwise, object existence check is skipped.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_update_node/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_update_node(node_id int,\n                                              new_node_name text,\n                                              new_node_port int,\n                                              force bool DEFAULT false,\n                                              lock_cooldown int DEFAULT 10000)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$citus_update_node$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_update_node(node_id int,\n                                       new_node_name text,\n                                       new_node_port int,\n                                       force bool,\n                                       lock_cooldown int)\n  IS 'change the location of a node. when force => true it will wait lock_cooldown ms before killing competing locks';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_update_node(int,text,int,bool,int) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_update_node/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_update_node(node_id int,\n                                              new_node_name text,\n                                              new_node_port int,\n                                              force bool DEFAULT false,\n                                              lock_cooldown int DEFAULT 10000)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$citus_update_node$$;\n\nCOMMENT ON FUNCTION pg_catalog.citus_update_node(node_id int,\n                                       new_node_name text,\n                                       new_node_port int,\n                                       force bool,\n                                       lock_cooldown int)\n  IS 'change the location of a node. when force => true it will wait lock_cooldown ms before killing competing locks';\n\nREVOKE ALL ON FUNCTION pg_catalog.citus_update_node(int,text,int,bool,int) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_update_shard_statistics/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_update_shard_statistics(shard_id bigint)\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_update_shard_statistics$$;\nCOMMENT ON FUNCTION pg_catalog.citus_update_shard_statistics(bigint)\n    IS 'updates shard statistics and returns the updated shard size';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_update_shard_statistics/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_update_shard_statistics(shard_id bigint)\n    RETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_update_shard_statistics$$;\nCOMMENT ON FUNCTION pg_catalog.citus_update_shard_statistics(bigint)\n    IS 'updates shard statistics and returns the updated shard size';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_update_table_statistics/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.citus_update_table_statistics(relation regclass)\nRETURNS VOID AS $$\nDECLARE\n\tcolocated_tables regclass[];\nBEGIN\n\tSELECT get_colocated_table_array(relation) INTO colocated_tables;\n\n\tPERFORM\n\t\tmaster_update_shard_statistics(shardid)\n\tFROM\n\t\tpg_dist_shard\n\tWHERE\n\t\tlogicalrelid = ANY (colocated_tables);\nEND;\n$$ LANGUAGE 'plpgsql';\nCOMMENT ON FUNCTION pg_catalog.citus_update_table_statistics(regclass)\n\tIS 'updates shard statistics of the given table and its colocated tables';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_update_table_statistics/10.0-3.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_update_table_statistics(relation regclass)\n\tRETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_update_table_statistics$$;\nCOMMENT ON FUNCTION pg_catalog.citus_update_table_statistics(regclass)\n\tIS 'updates shard statistics of the given table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_update_table_statistics/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_update_table_statistics(relation regclass)\n\tRETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$citus_update_table_statistics$$;\nCOMMENT ON FUNCTION pg_catalog.citus_update_table_statistics(regclass)\n\tIS 'updates shard statistics of the given table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_validate_rebalance_strategy_functions/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_validate_rebalance_strategy_functions(\n    shard_cost_function regproc,\n    node_capacity_function regproc,\n    shard_allowed_on_node_function regproc\n)\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C STRICT VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.citus_validate_rebalance_strategy_functions(regproc,regproc,regproc)\n  IS 'internal function used by citus to validate signatures of functions used in rebalance strategy';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/citus_validate_rebalance_strategy_functions/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_validate_rebalance_strategy_functions(\n    shard_cost_function regproc,\n    node_capacity_function regproc,\n    shard_allowed_on_node_function regproc\n)\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C STRICT VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.citus_validate_rebalance_strategy_functions(regproc,regproc,regproc)\n  IS 'internal function used by citus to validate signatures of functions used in rebalance strategy';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/commit_management_command_2pc/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.commit_management_command_2pc()\n    RETURNS VOID\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$commit_management_command_2pc$$;\n\nCOMMENT ON FUNCTION citus_internal.commit_management_command_2pc()\n    IS 'commits the coordinated remote transactions, is a wrapper function for CoordinatedRemoteTransactionsCommit';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/commit_management_command_2pc/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.commit_management_command_2pc()\n    RETURNS VOID\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$commit_management_command_2pc$$;\n\nCOMMENT ON FUNCTION citus_internal.commit_management_command_2pc()\n    IS 'commits the coordinated remote transactions, is a wrapper function for CoordinatedRemoteTransactionsCommit';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/coord_binary_combine_agg/14.0-1.sql",
    "content": "\n-- select coord_binary_combine_agg(agg, col) is similar to coord_combine_agg but\n-- takes binary representation of the state as input\nCREATE AGGREGATE pg_catalog.coord_binary_combine_agg(oid, bytea, anyelement) (\n    STYPE = internal,\n    SFUNC = pg_catalog.coord_binary_combine_agg_sfunc,\n    FINALFUNC = pg_catalog.coord_binary_combine_agg_ffunc,\n    FINALFUNC_EXTRA\n);\nCOMMENT ON AGGREGATE pg_catalog.coord_binary_combine_agg(oid, bytea, anyelement)\n    IS 'support aggregate for implementing combining partial aggregate results from workers';\n\nREVOKE ALL ON FUNCTION pg_catalog.coord_binary_combine_agg FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.coord_binary_combine_agg TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/coord_binary_combine_agg/latest.sql",
    "content": "\n-- select coord_binary_combine_agg(agg, col) is similar to coord_combine_agg but\n-- takes binary representation of the state as input\nCREATE AGGREGATE pg_catalog.coord_binary_combine_agg(oid, bytea, anyelement) (\n    STYPE = internal,\n    SFUNC = pg_catalog.coord_binary_combine_agg_sfunc,\n    FINALFUNC = pg_catalog.coord_binary_combine_agg_ffunc,\n    FINALFUNC_EXTRA\n);\nCOMMENT ON AGGREGATE pg_catalog.coord_binary_combine_agg(oid, bytea, anyelement)\n    IS 'support aggregate for implementing combining partial aggregate results from workers';\n\nREVOKE ALL ON FUNCTION pg_catalog.coord_binary_combine_agg FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.coord_binary_combine_agg TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/coord_binary_combine_agg_ffunc/14.0-1.sql",
    "content": "\nCREATE FUNCTION pg_catalog.coord_binary_combine_agg_ffunc(internal, oid, bytea, anyelement)\nRETURNS anyelement\nAS 'MODULE_PATHNAME'\nLANGUAGE C PARALLEL SAFE;\nCOMMENT ON FUNCTION pg_catalog.coord_binary_combine_agg_ffunc(internal, oid, bytea, anyelement)\n    IS 'finalizer for coord_binary_combine_agg';\n\nREVOKE ALL ON FUNCTION pg_catalog.coord_binary_combine_agg_ffunc FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.coord_binary_combine_agg_ffunc TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/coord_binary_combine_agg_ffunc/latest.sql",
    "content": "\nCREATE FUNCTION pg_catalog.coord_binary_combine_agg_ffunc(internal, oid, bytea, anyelement)\nRETURNS anyelement\nAS 'MODULE_PATHNAME'\nLANGUAGE C PARALLEL SAFE;\nCOMMENT ON FUNCTION pg_catalog.coord_binary_combine_agg_ffunc(internal, oid, bytea, anyelement)\n    IS 'finalizer for coord_binary_combine_agg';\n\nREVOKE ALL ON FUNCTION pg_catalog.coord_binary_combine_agg_ffunc FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.coord_binary_combine_agg_ffunc TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/coord_binary_combine_agg_sfunc/14.0-1.sql",
    "content": "\nCREATE FUNCTION pg_catalog.coord_binary_combine_agg_sfunc(internal, oid, bytea, anyelement)\nRETURNS internal\nAS 'MODULE_PATHNAME'\nLANGUAGE C PARALLEL SAFE;\nCOMMENT ON FUNCTION pg_catalog.coord_binary_combine_agg_sfunc(internal, oid, bytea, anyelement)\n    IS 'transition function for coord_binary_combine_agg';\n\nREVOKE ALL ON FUNCTION pg_catalog.coord_binary_combine_agg_sfunc FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.coord_binary_combine_agg_sfunc TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/coord_binary_combine_agg_sfunc/latest.sql",
    "content": "\nCREATE FUNCTION pg_catalog.coord_binary_combine_agg_sfunc(internal, oid, bytea, anyelement)\nRETURNS internal\nAS 'MODULE_PATHNAME'\nLANGUAGE C PARALLEL SAFE;\nCOMMENT ON FUNCTION pg_catalog.coord_binary_combine_agg_sfunc(internal, oid, bytea, anyelement)\n    IS 'transition function for coord_binary_combine_agg';\n\nREVOKE ALL ON FUNCTION pg_catalog.coord_binary_combine_agg_sfunc FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.coord_binary_combine_agg_sfunc TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_citus_local_table/10.0-1.sql",
    "content": "DROP FUNCTION pg_catalog.create_citus_local_table(regclass);\nCREATE OR REPLACE FUNCTION pg_catalog.citus_add_local_table_to_metadata(table_name regclass, cascade_via_foreign_keys boolean default false)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_add_local_table_to_metadata$$;\nCOMMENT ON FUNCTION pg_catalog.citus_add_local_table_to_metadata(table_name regclass, cascade_via_foreign_keys boolean)\n\tIS 'create a citus local table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_citus_local_table/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.create_citus_local_table(table_name regclass)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$create_citus_local_table$$;\nCOMMENT ON FUNCTION pg_catalog.create_citus_local_table(table_name regclass)\n\tIS 'create a citus local table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_citus_local_table/latest.sql",
    "content": "DROP FUNCTION pg_catalog.create_citus_local_table(regclass);\nCREATE OR REPLACE FUNCTION pg_catalog.citus_add_local_table_to_metadata(table_name regclass, cascade_via_foreign_keys boolean default false)\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$citus_add_local_table_to_metadata$$;\nCOMMENT ON FUNCTION pg_catalog.citus_add_local_table_to_metadata(table_name regclass, cascade_via_foreign_keys boolean)\n\tIS 'create a citus local table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_distributed_function/11.0-1.sql",
    "content": "DROP FUNCTION pg_catalog.create_distributed_function(regprocedure, text, text);\n\nCREATE OR REPLACE FUNCTION pg_catalog.create_distributed_function(function_name regprocedure,\n\t\t\t\t\t\t       distribution_arg_name text DEFAULT NULL,\n\t\t\t\t\t\t       colocate_with text DEFAULT 'default',\n\t\t\t\t\t\t       force_delegation bool DEFAULT NULL)\n  RETURNS void\n  LANGUAGE C CALLED ON NULL INPUT\n  AS 'MODULE_PATHNAME', $$create_distributed_function$$;\n\nCOMMENT ON FUNCTION pg_catalog.create_distributed_function(function_name regprocedure,\n\t\t\t\t\t\tdistribution_arg_name text,\n\t\t\t\t\t\tcolocate_with text,\n\t\t\t\t\t\tforce_delegation bool)\n  IS 'creates a distributed function';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_distributed_function/9.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION create_distributed_function(function_name regprocedure,\n\t\t\t\t\t\t       distribution_arg_name text DEFAULT NULL,\n\t\t\t\t\t\t       colocate_with text DEFAULT 'default')\n  RETURNS void\n  LANGUAGE C CALLED ON NULL INPUT\n  AS 'MODULE_PATHNAME', $$create_distributed_function$$;\n\nCOMMENT ON FUNCTION create_distributed_function(function_name regprocedure,\n\t\t\t\t\t\tdistribution_arg_name text,\n\t\t\t\t\t\tcolocate_with text)\n  IS 'creates a distributed function';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_distributed_function/latest.sql",
    "content": "DROP FUNCTION pg_catalog.create_distributed_function(regprocedure, text, text);\n\nCREATE OR REPLACE FUNCTION pg_catalog.create_distributed_function(function_name regprocedure,\n\t\t\t\t\t\t       distribution_arg_name text DEFAULT NULL,\n\t\t\t\t\t\t       colocate_with text DEFAULT 'default',\n\t\t\t\t\t\t       force_delegation bool DEFAULT NULL)\n  RETURNS void\n  LANGUAGE C CALLED ON NULL INPUT\n  AS 'MODULE_PATHNAME', $$create_distributed_function$$;\n\nCOMMENT ON FUNCTION pg_catalog.create_distributed_function(function_name regprocedure,\n\t\t\t\t\t\tdistribution_arg_name text,\n\t\t\t\t\t\tcolocate_with text,\n\t\t\t\t\t\tforce_delegation bool)\n  IS 'creates a distributed function';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_distributed_table/10.1-1.sql",
    "content": "DROP FUNCTION create_distributed_table(regclass, text,  citus.distribution_type, text);\nCREATE OR REPLACE FUNCTION create_distributed_table(table_name regclass,\n                                                    distribution_column text,\n                                                    distribution_type citus.distribution_type DEFAULT 'hash',\n                                                    colocate_with text DEFAULT 'default',\n                                                    shard_count int DEFAULT NULL)\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$create_distributed_table$$;\nCOMMENT ON FUNCTION create_distributed_table(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t\t distribution_column text,\n\t\t\t\t\t\t\t\t\t\t\t distribution_type citus.distribution_type,\n\t\t\t\t\t\t\t\t\t\t\t colocate_with text,\n                                             shard_count int)\n    IS 'creates a distributed table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_distributed_table/latest.sql",
    "content": "DROP FUNCTION create_distributed_table(regclass, text,  citus.distribution_type, text);\nCREATE OR REPLACE FUNCTION create_distributed_table(table_name regclass,\n                                                    distribution_column text,\n                                                    distribution_type citus.distribution_type DEFAULT 'hash',\n                                                    colocate_with text DEFAULT 'default',\n                                                    shard_count int DEFAULT NULL)\n    RETURNS void\n    LANGUAGE C\n    AS 'MODULE_PATHNAME', $$create_distributed_table$$;\nCOMMENT ON FUNCTION create_distributed_table(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t\t distribution_column text,\n\t\t\t\t\t\t\t\t\t\t\t distribution_type citus.distribution_type,\n\t\t\t\t\t\t\t\t\t\t\t colocate_with text,\n                                             shard_count int)\n    IS 'creates a distributed table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_distributed_table_concurrently/11.1-1.sql",
    "content": "CREATE FUNCTION pg_catalog.create_distributed_table_concurrently(table_name regclass,\n                                                                 distribution_column text,\n                                                                 distribution_type citus.distribution_type DEFAULT 'hash',\n                                                                 colocate_with text DEFAULT 'default',\n                                                                 shard_count int DEFAULT NULL)\n  RETURNS void\n  LANGUAGE C\n  AS 'MODULE_PATHNAME', $$create_distributed_table_concurrently$$;\nCOMMENT ON FUNCTION pg_catalog.create_distributed_table_concurrently(table_name regclass,\n                                                                     distribution_column text,\n                                                                     distribution_type citus.distribution_type,\n                                                                     colocate_with text,\n                                                                     shard_count int)\n    IS 'creates a distributed table and avoids blocking writes';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_distributed_table_concurrently/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.create_distributed_table_concurrently(table_name regclass,\n                                                                 distribution_column text,\n                                                                 distribution_type citus.distribution_type DEFAULT 'hash',\n                                                                 colocate_with text DEFAULT 'default',\n                                                                 shard_count int DEFAULT NULL)\n  RETURNS void\n  LANGUAGE C\n  AS 'MODULE_PATHNAME', $$create_distributed_table_concurrently$$;\nCOMMENT ON FUNCTION pg_catalog.create_distributed_table_concurrently(table_name regclass,\n                                                                     distribution_column text,\n                                                                     distribution_type citus.distribution_type,\n                                                                     colocate_with text,\n                                                                     shard_count int)\n    IS 'creates a distributed table and avoids blocking writes';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_time_partitions/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.create_time_partitions(\n    table_name regclass,\n    partition_interval INTERVAL,\n    end_at timestamptz,\n    start_from timestamptz DEFAULT now())\nreturns boolean\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    -- partitioned table name\n    schema_name_text name;\n    table_name_text name;\n\n    -- record for to-be-created partition\n    missing_partition_record record;\n\n    -- result indiciates whether any partitions were created\n    partition_created bool := false;\nBEGIN\n    IF start_from >= end_at THEN\n        RAISE 'start_from (%) must be older than end_at (%)', start_from, end_at;\n    END IF;\n\n    SELECT nspname, relname\n    INTO schema_name_text, table_name_text\n    FROM pg_class JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid\n    WHERE pg_class.oid = table_name::oid;\n\n    -- Get missing partition range info using the get_missing_partition_ranges\n    -- and create partitions using that info.\n    FOR missing_partition_record IN\n        SELECT *\n        FROM get_missing_time_partition_ranges(table_name, partition_interval, end_at, start_from)\n    LOOP\n        EXECUTE format('CREATE TABLE %I.%I PARTITION OF %I.%I FOR VALUES FROM (%L) TO (%L)',\n        schema_name_text,\n        missing_partition_record.partition_name,\n        schema_name_text,\n        table_name_text,\n        missing_partition_record.range_from_value,\n        missing_partition_record.range_to_value);\n\n        partition_created := true;\n    END LOOP;\n\n    RETURN partition_created;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.create_time_partitions(\n    table_name regclass,\n    partition_interval INTERVAL,\n    end_at timestamptz,\n    start_from timestamptz)\nIS 'create time partitions for the given range';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_time_partitions/13.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.create_time_partitions(\n    table_name regclass,\n    partition_interval INTERVAL,\n    end_at timestamptz,\n    start_from timestamptz DEFAULT now())\nreturns boolean\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    -- partitioned table name\n    schema_name_text name;\n    table_name_text name;\n\n    -- record for to-be-created partition\n    missing_partition_record record;\n\n    -- result indiciates whether any partitions were created\n    partition_created bool := false;\nBEGIN\n    IF start_from >= end_at THEN\n        RAISE 'start_from (%) must be older than end_at (%)', start_from, end_at;\n    END IF;\n\n    IF NOT isfinite(partition_interval) THEN\n        RAISE 'Partition interval must be a finite value';\n    END IF;\n\n    SELECT nspname, relname\n    INTO schema_name_text, table_name_text\n    FROM pg_class JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid\n    WHERE pg_class.oid = table_name::oid;\n\n    -- Get missing partition range info using the get_missing_partition_ranges\n    -- and create partitions using that info.\n    FOR missing_partition_record IN\n        SELECT *\n        FROM get_missing_time_partition_ranges(table_name, partition_interval, end_at, start_from)\n    LOOP\n        EXECUTE format('CREATE TABLE %I.%I PARTITION OF %I.%I FOR VALUES FROM (%L) TO (%L)',\n        schema_name_text,\n        missing_partition_record.partition_name,\n        schema_name_text,\n        table_name_text,\n        missing_partition_record.range_from_value,\n        missing_partition_record.range_to_value);\n\n        partition_created := true;\n    END LOOP;\n\n    RETURN partition_created;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.create_time_partitions(\n    table_name regclass,\n    partition_interval INTERVAL,\n    end_at timestamptz,\n    start_from timestamptz)\nIS 'create time partitions for the given range';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/create_time_partitions/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.create_time_partitions(\n    table_name regclass,\n    partition_interval INTERVAL,\n    end_at timestamptz,\n    start_from timestamptz DEFAULT now())\nreturns boolean\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    -- partitioned table name\n    schema_name_text name;\n    table_name_text name;\n\n    -- record for to-be-created partition\n    missing_partition_record record;\n\n    -- result indiciates whether any partitions were created\n    partition_created bool := false;\nBEGIN\n    IF start_from >= end_at THEN\n        RAISE 'start_from (%) must be older than end_at (%)', start_from, end_at;\n    END IF;\n\n    IF NOT isfinite(partition_interval) THEN\n        RAISE 'Partition interval must be a finite value';\n    END IF;\n\n    SELECT nspname, relname\n    INTO schema_name_text, table_name_text\n    FROM pg_class JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid\n    WHERE pg_class.oid = table_name::oid;\n\n    -- Get missing partition range info using the get_missing_partition_ranges\n    -- and create partitions using that info.\n    FOR missing_partition_record IN\n        SELECT *\n        FROM get_missing_time_partition_ranges(table_name, partition_interval, end_at, start_from)\n    LOOP\n        EXECUTE format('CREATE TABLE %I.%I PARTITION OF %I.%I FOR VALUES FROM (%L) TO (%L)',\n        schema_name_text,\n        missing_partition_record.partition_name,\n        schema_name_text,\n        table_name_text,\n        missing_partition_record.range_from_value,\n        missing_partition_record.range_to_value);\n\n        partition_created := true;\n    END LOOP;\n\n    RETURN partition_created;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.create_time_partitions(\n    table_name regclass,\n    partition_interval INTERVAL,\n    end_at timestamptz,\n    start_from timestamptz)\nIS 'create time partitions for the given range';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/distributed_tables_colocated/9.0-2.sql",
    "content": "--\n-- distributed_tables_colocated returns true if given tables are co-located, false otherwise.\n-- The function checks shard definitions, matches shard placements for given tables.\n--\nCREATE OR REPLACE FUNCTION pg_catalog.distributed_tables_colocated(table1 regclass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   table2 regclass)\nRETURNS bool\nLANGUAGE plpgsql\nAS $function$\nDECLARE\n\ttable1_colocationid int;\n\ttable2_colocationid int;\nBEGIN\n\tSELECT colocationid INTO table1_colocationid\n\tFROM pg_catalog.pg_dist_partition WHERE logicalrelid = table1;\n\n\tSELECT colocationid INTO table2_colocationid\n\tFROM pg_catalog.pg_dist_partition WHERE logicalrelid = table2;\n\n\tRETURN table1_colocationid = table2_colocationid;\nEND;\n$function$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/distributed_tables_colocated/latest.sql",
    "content": "--\n-- distributed_tables_colocated returns true if given tables are co-located, false otherwise.\n-- The function checks shard definitions, matches shard placements for given tables.\n--\nCREATE OR REPLACE FUNCTION pg_catalog.distributed_tables_colocated(table1 regclass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   table2 regclass)\nRETURNS bool\nLANGUAGE plpgsql\nAS $function$\nDECLARE\n\ttable1_colocationid int;\n\ttable2_colocationid int;\nBEGIN\n\tSELECT colocationid INTO table1_colocationid\n\tFROM pg_catalog.pg_dist_partition WHERE logicalrelid = table1;\n\n\tSELECT colocationid INTO table2_colocationid\n\tFROM pg_catalog.pg_dist_partition WHERE logicalrelid = table2;\n\n\tRETURN table1_colocationid = table2_colocationid;\nEND;\n$function$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/drop_old_time_partitions/10.2-1.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.drop_old_time_partitions(\n\ttable_name regclass,\n\tolder_than timestamptz)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    -- properties of the partitioned table\n    number_of_partition_columns int;\n    partition_column_index int;\n    partition_column_type regtype;\n\n    r record;\nBEGIN\n    -- check whether the table is time partitioned table, if not error out\n    SELECT partnatts, partattrs[0]\n    INTO number_of_partition_columns, partition_column_index\n    FROM pg_catalog.pg_partitioned_table\n    WHERE partrelid = table_name;\n\n    IF NOT FOUND THEN\n        RAISE '% is not partitioned', table_name::text;\n    ELSIF number_of_partition_columns <> 1 THEN\n        RAISE 'partitioned tables with multiple partition columns are not supported';\n    END IF;\n\n    -- get datatype here to check interval-table type\n    SELECT atttypid\n    INTO partition_column_type\n    FROM pg_attribute\n    WHERE attrelid = table_name::oid\n    AND attnum = partition_column_index;\n\n    -- we currently only support partitioning by date, timestamp, and timestamptz\n    IF partition_column_type <> 'date'::regtype\n    AND partition_column_type <> 'timestamp'::regtype\n    AND partition_column_type <> 'timestamptz'::regtype  THEN\n        RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name;\n    END IF;\n\n    FOR r IN\n\t\tSELECT partition, nspname AS schema_name, relname AS table_name, from_value, to_value\n\t\tFROM pg_catalog.time_partitions, pg_catalog.pg_class c, pg_catalog.pg_namespace n\n\t\tWHERE parent_table = table_name AND partition = c.oid AND c.relnamespace = n.oid\n\t\tAND to_value IS NOT NULL\n\t\tAND to_value::timestamptz <= older_than\n\t\tORDER BY to_value::timestamptz\n    LOOP\n        RAISE NOTICE 'dropping % with start time % and end time %', r.partition, r.from_value, r.to_value;\n        EXECUTE format('DROP TABLE %I.%I', r.schema_name, r.table_name);\n    END LOOP;\nEND;\n$$;\nCOMMENT ON PROCEDURE pg_catalog.drop_old_time_partitions(\n\ttable_name regclass,\n\tolder_than timestamptz)\nIS 'drop old partitions of a time-partitioned table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/drop_old_time_partitions/12.0-1.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.drop_old_time_partitions(\n    table_name regclass,\n    older_than timestamptz)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    -- properties of the partitioned table\n    number_of_partition_columns int;\n    partition_column_index int;\n    partition_column_type regtype;\n\n    -- used to support dynamic type casting between the partition column type and timestamptz\n    custom_cast text;\n    is_partition_column_castable boolean;\n    older_partitions_query text;\n\n    r record;\nBEGIN\n    -- check whether the table is time partitioned table, if not error out\n    SELECT partnatts, partattrs[0]\n    INTO number_of_partition_columns, partition_column_index\n    FROM pg_catalog.pg_partitioned_table\n    WHERE partrelid = table_name;\n\n    IF NOT FOUND THEN\n        RAISE '% is not partitioned', table_name::text;\n    ELSIF number_of_partition_columns <> 1 THEN\n        RAISE 'partitioned tables with multiple partition columns are not supported';\n    END IF;\n\n    -- get datatype here to check interval-table type\n    SELECT atttypid\n    INTO partition_column_type\n    FROM pg_attribute\n    WHERE attrelid = table_name::oid\n    AND attnum = partition_column_index;\n\n    -- we currently only support partitioning by date, timestamp, and timestamptz\n    custom_cast = '';\n    IF partition_column_type <> 'date'::regtype\n    AND partition_column_type <> 'timestamp'::regtype\n    AND partition_column_type <> 'timestamptz'::regtype  THEN\n      SELECT EXISTS(SELECT OID FROM pg_cast WHERE castsource = partition_column_type AND casttarget = 'timestamptz'::regtype) AND\n             EXISTS(SELECT OID FROM pg_cast WHERE castsource = 'timestamptz'::regtype AND casttarget = partition_column_type)\n      INTO is_partition_column_castable;\n      IF not is_partition_column_castable THEN\n        RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name;\n      END IF;\n      custom_cast = format('::%s', partition_column_type);\n    END IF;\n\n    older_partitions_query = format('SELECT partition, nspname AS schema_name, relname AS table_name, from_value, to_value\n        FROM pg_catalog.time_partitions, pg_catalog.pg_class c, pg_catalog.pg_namespace n\n        WHERE parent_table = $1 AND partition = c.oid AND c.relnamespace = n.oid\n        AND to_value IS NOT NULL\n        AND to_value%1$s::timestamptz <= $2\n        ORDER BY to_value%1$s::timestamptz', custom_cast);\n    FOR r IN EXECUTE older_partitions_query USING table_name, older_than\n    LOOP\n        RAISE NOTICE 'dropping % with start time % and end time %', r.partition, r.from_value, r.to_value;\n        EXECUTE format('DROP TABLE %I.%I', r.schema_name, r.table_name);\n    END LOOP;\nEND;\n$$;\nCOMMENT ON PROCEDURE pg_catalog.drop_old_time_partitions(\n    table_name regclass,\n    older_than timestamptz)\nIS 'drop old partitions of a time-partitioned table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/drop_old_time_partitions/latest.sql",
    "content": "CREATE OR REPLACE PROCEDURE pg_catalog.drop_old_time_partitions(\n    table_name regclass,\n    older_than timestamptz)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    -- properties of the partitioned table\n    number_of_partition_columns int;\n    partition_column_index int;\n    partition_column_type regtype;\n\n    -- used to support dynamic type casting between the partition column type and timestamptz\n    custom_cast text;\n    is_partition_column_castable boolean;\n    older_partitions_query text;\n\n    r record;\nBEGIN\n    -- check whether the table is time partitioned table, if not error out\n    SELECT partnatts, partattrs[0]\n    INTO number_of_partition_columns, partition_column_index\n    FROM pg_catalog.pg_partitioned_table\n    WHERE partrelid = table_name;\n\n    IF NOT FOUND THEN\n        RAISE '% is not partitioned', table_name::text;\n    ELSIF number_of_partition_columns <> 1 THEN\n        RAISE 'partitioned tables with multiple partition columns are not supported';\n    END IF;\n\n    -- get datatype here to check interval-table type\n    SELECT atttypid\n    INTO partition_column_type\n    FROM pg_attribute\n    WHERE attrelid = table_name::oid\n    AND attnum = partition_column_index;\n\n    -- we currently only support partitioning by date, timestamp, and timestamptz\n    custom_cast = '';\n    IF partition_column_type <> 'date'::regtype\n    AND partition_column_type <> 'timestamp'::regtype\n    AND partition_column_type <> 'timestamptz'::regtype  THEN\n      SELECT EXISTS(SELECT OID FROM pg_cast WHERE castsource = partition_column_type AND casttarget = 'timestamptz'::regtype) AND\n             EXISTS(SELECT OID FROM pg_cast WHERE castsource = 'timestamptz'::regtype AND casttarget = partition_column_type)\n      INTO is_partition_column_castable;\n      IF not is_partition_column_castable THEN\n        RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name;\n      END IF;\n      custom_cast = format('::%s', partition_column_type);\n    END IF;\n\n    older_partitions_query = format('SELECT partition, nspname AS schema_name, relname AS table_name, from_value, to_value\n        FROM pg_catalog.time_partitions, pg_catalog.pg_class c, pg_catalog.pg_namespace n\n        WHERE parent_table = $1 AND partition = c.oid AND c.relnamespace = n.oid\n        AND to_value IS NOT NULL\n        AND to_value%1$s::timestamptz <= $2\n        ORDER BY to_value%1$s::timestamptz', custom_cast);\n    FOR r IN EXECUTE older_partitions_query USING table_name, older_than\n    LOOP\n        RAISE NOTICE 'dropping % with start time % and end time %', r.partition, r.from_value, r.to_value;\n        EXECUTE format('DROP TABLE %I.%I', r.schema_name, r.table_name);\n    END LOOP;\nEND;\n$$;\nCOMMENT ON PROCEDURE pg_catalog.drop_old_time_partitions(\n    table_name regclass,\n    older_than timestamptz)\nIS 'drop old partitions of a time-partitioned table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/fetch_intermediate_results/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.fetch_intermediate_results(\n    result_ids text[],\n    node_name text,\n    node_port int)\nRETURNS bigint\nLANGUAGE C STRICT VOLATILE\nAS 'MODULE_PATHNAME', $$fetch_intermediate_results$$;\nCOMMENT ON FUNCTION pg_catalog.fetch_intermediate_results(text[],text,int)\nIS 'fetch array of intermediate results from a remote node. returns number of bytes read.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/fetch_intermediate_results/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.fetch_intermediate_results(\n    result_ids text[],\n    node_name text,\n    node_port int)\nRETURNS bigint\nLANGUAGE C STRICT VOLATILE\nAS 'MODULE_PATHNAME', $$fetch_intermediate_results$$;\nCOMMENT ON FUNCTION pg_catalog.fetch_intermediate_results(text[],text,int)\nIS 'fetch array of intermediate results from a remote node. returns number of bytes read.';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/fix_all_partition_shard_index_names/10.2-4.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.fix_all_partition_shard_index_names()\n  RETURNS SETOF regclass\n  LANGUAGE plpgsql\n  AS $$\nDECLARE\n\tdist_partitioned_table_name regclass;\nBEGIN\n  FOR dist_partitioned_table_name IN SELECT p.logicalrelid\n                                     FROM pg_dist_partition p\n                                     JOIN pg_class c ON p.logicalrelid = c.oid\n                                     WHERE c.relkind = 'p'\n\t\t                                 ORDER BY c.relname, c.oid\n    LOOP\n      EXECUTE 'SELECT fix_partition_shard_index_names( ' || quote_literal(dist_partitioned_table_name) || ' )';\n      RETURN NEXT dist_partitioned_table_name;\n    END LOOP;\n  RETURN;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.fix_all_partition_shard_index_names()\n  IS 'fix index names on partition shards of all tables';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/fix_all_partition_shard_index_names/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.fix_all_partition_shard_index_names()\n  RETURNS SETOF regclass\n  LANGUAGE plpgsql\n  AS $$\nDECLARE\n\tdist_partitioned_table_name regclass;\nBEGIN\n  FOR dist_partitioned_table_name IN SELECT p.logicalrelid\n                                     FROM pg_dist_partition p\n                                     JOIN pg_class c ON p.logicalrelid = c.oid\n                                     WHERE c.relkind = 'p'\n\t\t                                 ORDER BY c.relname, c.oid\n    LOOP\n      EXECUTE 'SELECT fix_partition_shard_index_names( ' || quote_literal(dist_partitioned_table_name) || ' )';\n      RETURN NEXT dist_partitioned_table_name;\n    END LOOP;\n  RETURN;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.fix_all_partition_shard_index_names()\n  IS 'fix index names on partition shards of all tables';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/fix_partition_shard_index_names/10.2-4.sql",
    "content": "CREATE FUNCTION pg_catalog.fix_partition_shard_index_names(table_name regclass)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$fix_partition_shard_index_names$$;\nCOMMENT ON FUNCTION pg_catalog.fix_partition_shard_index_names(table_name regclass)\n  IS 'fix index names on partition shards of given table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/fix_partition_shard_index_names/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.fix_partition_shard_index_names(table_name regclass)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$fix_partition_shard_index_names$$;\nCOMMENT ON FUNCTION pg_catalog.fix_partition_shard_index_names(table_name regclass)\n  IS 'fix index names on partition shards of given table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/fix_pre_citus10_partitioned_table_constraint_names/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.fix_pre_citus10_partitioned_table_constraint_names(table_name regclass)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$fix_pre_citus10_partitioned_table_constraint_names$$;\nCOMMENT ON FUNCTION pg_catalog.fix_pre_citus10_partitioned_table_constraint_names(table_name regclass)\n  IS 'fix constraint names on partition shards';\n\nCREATE OR REPLACE FUNCTION pg_catalog.fix_pre_citus10_partitioned_table_constraint_names()\n  RETURNS SETOF regclass\n  LANGUAGE plpgsql\n  AS $$\nDECLARE\n\toid regclass;\nBEGIN\n    FOR oid IN SELECT c.oid\n            FROM pg_dist_partition p\n            JOIN pg_class c ON p.logicalrelid = c.oid\n\t\t\tJOIN pg_namespace n ON c.relnamespace = n.oid\n        WHERE c.relkind = 'p'\n\t\tORDER BY n.nspname, c.relname\n    LOOP\n        EXECUTE 'SELECT fix_pre_citus10_partitioned_table_constraint_names( ' || quote_literal(oid) || ' )';\n        RETURN NEXT oid;\n    END LOOP;\n    RETURN;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.fix_pre_citus10_partitioned_table_constraint_names()\n  IS 'fix constraint names on all partition shards';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/fix_pre_citus10_partitioned_table_constraint_names/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.fix_pre_citus10_partitioned_table_constraint_names(table_name regclass)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$fix_pre_citus10_partitioned_table_constraint_names$$;\nCOMMENT ON FUNCTION pg_catalog.fix_pre_citus10_partitioned_table_constraint_names(table_name regclass)\n  IS 'fix constraint names on partition shards';\n\nCREATE OR REPLACE FUNCTION pg_catalog.fix_pre_citus10_partitioned_table_constraint_names()\n  RETURNS SETOF regclass\n  LANGUAGE plpgsql\n  AS $$\nDECLARE\n\toid regclass;\nBEGIN\n    FOR oid IN SELECT c.oid\n            FROM pg_dist_partition p\n            JOIN pg_class c ON p.logicalrelid = c.oid\n\t\t\tJOIN pg_namespace n ON c.relnamespace = n.oid\n        WHERE c.relkind = 'p'\n\t\tORDER BY n.nspname, c.relname\n    LOOP\n        EXECUTE 'SELECT fix_pre_citus10_partitioned_table_constraint_names( ' || quote_literal(oid) || ' )';\n        RETURN NEXT oid;\n    END LOOP;\n    RETURN;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.fix_pre_citus10_partitioned_table_constraint_names()\n  IS 'fix constraint names on all partition shards';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/fix_pre_citus14_colocation_group_collation_mismatches/14.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.fix_pre_citus14_colocation_group_collation_mismatches()\nRETURNS VOID AS $func$\nDECLARE\n    v_colocationid oid;\n    v_tables_to_move_out_grouped_by_collation json;\n    v_collationid oid;\n    v_tables_to_move_out oid[];\n    v_table_to_move_out oid;\n    v_first_table oid;\nBEGIN\n    SET LOCAL search_path TO pg_catalog;\n\n    FOR v_colocationid, v_tables_to_move_out_grouped_by_collation\n    IN\n       WITH colocation_groups_and_tables_with_collation_mismatches AS (\n           SELECT pdc.colocationid, pa.attcollation as distkeycollation, pdp.logicalrelid\n             FROM pg_dist_colocation pdc\n             JOIN pg_dist_partition pdp\n               ON pdc.colocationid = pdp.colocationid\n             JOIN pg_attribute pa\n               ON pa.attrelid = pdp.logicalrelid\n              AND pa.attname = column_to_column_name(pdp.logicalrelid, pdp.partkey)\n                  -- column_to_column_name() returns NULL if partkey is NULL, so we're already\n                  -- implicitly ignoring the tables that don't have a distribution column, such\n                  -- as reference tables, but let's still explicitly discard such tables below.\n            WHERE pdp.partkey IS NOT NULL\n                  -- ignore the table if its distribution column collation matches the collation saved for the colocation group\n              AND pdc.distributioncolumncollation != pa.attcollation\n       )\n       SELECT\n           colocationid,\n           json_object_agg(distkeycollation, rels) AS tables_to_move_out_grouped_by_collation\n       FROM (\n               SELECT colocationid,\n                      distkeycollation,\n                      array_agg(logicalrelid::oid) AS rels\n                 FROM colocation_groups_and_tables_with_collation_mismatches\n             GROUP BY colocationid, distkeycollation\n       ) q\n       GROUP BY colocationid\n    LOOP\n        RAISE DEBUG 'Processing colocation group with id %', v_colocationid;\n\n        FOR v_collationid, v_tables_to_move_out\n        IN\n              SELECT key::oid AS collationid,\n                     array_agg(elem::oid) AS tables_to_move_out\n                FROM json_each(v_tables_to_move_out_grouped_by_collation) AS e(key, value),\n             LATERAL json_array_elements_text(e.value) AS elem\n            GROUP BY key\n        LOOP\n            RAISE DEBUG 'Moving out tables with collation id % from colocation group %', v_collationid, v_colocationid;\n\n            v_first_table := NULL;\n\n            FOR v_table_to_move_out IN SELECT unnest(v_tables_to_move_out)\n            LOOP\n                IF v_first_table IS NULL then\n                    -- Move the first table out to start a new colocation group.\n                    --\n                    -- Could check if there is an appropriate colocation group to move to instead of 'none',\n                    -- but this won't be super easy. Plus, even if we had such a colocation group, the user\n                    -- was anyways okay with having this in a different colocation group in the first place.\n\n                    RAISE DEBUG 'Moving out table with oid % to a new colocation group', v_table_to_move_out;\n                    PERFORM update_distributed_table_colocation(v_table_to_move_out, colocate_with => 'none');\n\n                    -- save the first table to colocate the rest of the tables with it\n                    v_first_table := v_table_to_move_out;\n                ELSE\n                    -- Move the rest of the tables to colocate with the first table.\n\n                    RAISE DEBUG 'Moving out table with oid % to colocate with table with oid %', v_table_to_move_out, v_first_table;\n                    PERFORM update_distributed_table_colocation(v_table_to_move_out, colocate_with => v_first_table::regclass::text);\n                END IF;\n            END LOOP;\n        END LOOP;\n    END LOOP;\nEND;\n$func$\nLANGUAGE plpgsql;\nCOMMENT ON FUNCTION pg_catalog.fix_pre_citus14_colocation_group_collation_mismatches()\n  IS 'Fix distributed tables whose colocation group collations do not match their distribution columns by moving them to new colocation groups';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/fix_pre_citus14_colocation_group_collation_mismatches/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.fix_pre_citus14_colocation_group_collation_mismatches()\nRETURNS VOID AS $func$\nDECLARE\n    v_colocationid oid;\n    v_tables_to_move_out_grouped_by_collation json;\n    v_collationid oid;\n    v_tables_to_move_out oid[];\n    v_table_to_move_out oid;\n    v_first_table oid;\nBEGIN\n    SET LOCAL search_path TO pg_catalog;\n\n    FOR v_colocationid, v_tables_to_move_out_grouped_by_collation\n    IN\n       WITH colocation_groups_and_tables_with_collation_mismatches AS (\n           SELECT pdc.colocationid, pa.attcollation as distkeycollation, pdp.logicalrelid\n             FROM pg_dist_colocation pdc\n             JOIN pg_dist_partition pdp\n               ON pdc.colocationid = pdp.colocationid\n             JOIN pg_attribute pa\n               ON pa.attrelid = pdp.logicalrelid\n              AND pa.attname = column_to_column_name(pdp.logicalrelid, pdp.partkey)\n                  -- column_to_column_name() returns NULL if partkey is NULL, so we're already\n                  -- implicitly ignoring the tables that don't have a distribution column, such\n                  -- as reference tables, but let's still explicitly discard such tables below.\n            WHERE pdp.partkey IS NOT NULL\n                  -- ignore the table if its distribution column collation matches the collation saved for the colocation group\n              AND pdc.distributioncolumncollation != pa.attcollation\n       )\n       SELECT\n           colocationid,\n           json_object_agg(distkeycollation, rels) AS tables_to_move_out_grouped_by_collation\n       FROM (\n               SELECT colocationid,\n                      distkeycollation,\n                      array_agg(logicalrelid::oid) AS rels\n                 FROM colocation_groups_and_tables_with_collation_mismatches\n             GROUP BY colocationid, distkeycollation\n       ) q\n       GROUP BY colocationid\n    LOOP\n        RAISE DEBUG 'Processing colocation group with id %', v_colocationid;\n\n        FOR v_collationid, v_tables_to_move_out\n        IN\n              SELECT key::oid AS collationid,\n                     array_agg(elem::oid) AS tables_to_move_out\n                FROM json_each(v_tables_to_move_out_grouped_by_collation) AS e(key, value),\n             LATERAL json_array_elements_text(e.value) AS elem\n            GROUP BY key\n        LOOP\n            RAISE DEBUG 'Moving out tables with collation id % from colocation group %', v_collationid, v_colocationid;\n\n            v_first_table := NULL;\n\n            FOR v_table_to_move_out IN SELECT unnest(v_tables_to_move_out)\n            LOOP\n                IF v_first_table IS NULL then\n                    -- Move the first table out to start a new colocation group.\n                    --\n                    -- Could check if there is an appropriate colocation group to move to instead of 'none',\n                    -- but this won't be super easy. Plus, even if we had such a colocation group, the user\n                    -- was anyways okay with having this in a different colocation group in the first place.\n\n                    RAISE DEBUG 'Moving out table with oid % to a new colocation group', v_table_to_move_out;\n                    PERFORM update_distributed_table_colocation(v_table_to_move_out, colocate_with => 'none');\n\n                    -- save the first table to colocate the rest of the tables with it\n                    v_first_table := v_table_to_move_out;\n                ELSE\n                    -- Move the rest of the tables to colocate with the first table.\n\n                    RAISE DEBUG 'Moving out table with oid % to colocate with table with oid %', v_table_to_move_out, v_first_table;\n                    PERFORM update_distributed_table_colocation(v_table_to_move_out, colocate_with => v_first_table::regclass::text);\n                END IF;\n            END LOOP;\n        END LOOP;\n    END LOOP;\nEND;\n$func$\nLANGUAGE plpgsql;\nCOMMENT ON FUNCTION pg_catalog.fix_pre_citus14_colocation_group_collation_mismatches()\n  IS 'Fix distributed tables whose colocation group collations do not match their distribution columns by moving them to new colocation groups';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_all_active_transactions/11.0-1.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.get_all_active_transactions();\nCREATE OR REPLACE FUNCTION pg_catalog.get_all_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4,\n                                                                  OUT worker_query BOOL, OUT transaction_number int8, OUT transaction_stamp timestamptz,\n                                                                  OUT global_pid int8)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$get_all_active_transactions$$;\n\nCOMMENT ON FUNCTION pg_catalog.get_all_active_transactions(OUT datid oid, OUT datname text, OUT process_id int, OUT initiator_node_identifier int4,\n                                                           OUT worker_query BOOL, OUT transaction_number int8, OUT transaction_stamp timestamptz,\n                                                           OUT global_pid int8)\nIS 'returns transaction information for all Citus initiated transactions';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_all_active_transactions/11.1-1.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.get_all_active_transactions();\nCREATE OR REPLACE FUNCTION pg_catalog.get_all_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4,\n                                                                  OUT worker_query BOOL, OUT transaction_number int8, OUT transaction_stamp timestamptz,\n                                                                  OUT global_pid int8)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$get_all_active_transactions$$;\n\nCOMMENT ON FUNCTION pg_catalog.get_all_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4,\n                                                           OUT worker_query BOOL, OUT transaction_number int8, OUT transaction_stamp timestamptz,\n                                                           OUT global_pid int8)\nIS 'returns transaction information for all Citus initiated transactions';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_all_active_transactions/latest.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.get_all_active_transactions();\nCREATE OR REPLACE FUNCTION pg_catalog.get_all_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4,\n                                                                  OUT worker_query BOOL, OUT transaction_number int8, OUT transaction_stamp timestamptz,\n                                                                  OUT global_pid int8)\nRETURNS SETOF RECORD\nLANGUAGE C STRICT AS 'MODULE_PATHNAME',\n$$get_all_active_transactions$$;\n\nCOMMENT ON FUNCTION pg_catalog.get_all_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4,\n                                                           OUT worker_query BOOL, OUT transaction_number int8, OUT transaction_stamp timestamptz,\n                                                           OUT global_pid int8)\nIS 'returns transaction information for all Citus initiated transactions';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_global_active_transactions/11.0-1.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.get_global_active_transactions();\nCREATE OR REPLACE FUNCTION pg_catalog.get_global_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4, OUT worker_query BOOL,\n                                                          \t\t\t OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT global_pid int8)\n  RETURNS SETOF RECORD\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$get_global_active_transactions$$;\nCOMMENT ON FUNCTION pg_catalog.get_global_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4, OUT worker_query BOOL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT global_pid int8)\n     IS 'returns transaction information for all Citus initiated transactions from each node of the cluster';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_global_active_transactions/latest.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.get_global_active_transactions();\nCREATE OR REPLACE FUNCTION pg_catalog.get_global_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4, OUT worker_query BOOL,\n                                                          \t\t\t OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT global_pid int8)\n  RETURNS SETOF RECORD\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$get_global_active_transactions$$;\nCOMMENT ON FUNCTION pg_catalog.get_global_active_transactions(OUT datid oid, OUT process_id int, OUT initiator_node_identifier int4, OUT worker_query BOOL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  OUT transaction_number int8, OUT transaction_stamp timestamptz, OUT global_pid int8)\n     IS 'returns transaction information for all Citus initiated transactions from each node of the cluster';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_missing_time_partition_ranges/10.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.get_missing_time_partition_ranges(\n    table_name regclass,\n    partition_interval INTERVAL,\n    to_value timestamptz,\n    from_value timestamptz DEFAULT now())\nreturns table(\n    partition_name text,\n    range_from_value text,\n    range_to_value text)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    -- properties of the partitioned table\n    table_name_text text;\n    table_schema_text text;\n    number_of_partition_columns int;\n    partition_column_index int;\n    partition_column_type regtype;\n\n    -- used for generating time ranges\n    current_range_from_value timestamptz := NULL;\n    current_range_to_value timestamptz := NULL;\n    current_range_from_value_text text;\n    current_range_to_value_text text;\n\n    -- used to check whether there are misaligned (manually created) partitions\n    manual_partition regclass;\n    manual_partition_from_value_text text;\n    manual_partition_to_value_text text;\n\n    -- used for partition naming\n    partition_name_format text;\n    max_table_name_length int := current_setting('max_identifier_length');\n\n    -- used to determine whether the partition_interval is a day multiple\n    is_day_multiple boolean;\nBEGIN\n    -- check whether the table is time partitioned table, if not error out\n    SELECT relname, nspname, partnatts, partattrs[0]\n    INTO table_name_text, table_schema_text, number_of_partition_columns, partition_column_index\n    FROM pg_catalog.pg_partitioned_table, pg_catalog.pg_class c, pg_catalog.pg_namespace n\n    WHERE partrelid = c.oid AND c.oid = table_name\n    AND c.relnamespace = n.oid;\n    IF NOT FOUND THEN\n        RAISE '% is not partitioned', table_name;\n    ELSIF number_of_partition_columns <> 1 THEN\n        RAISE 'partitioned tables with multiple partition columns are not supported';\n    END IF;\n\n    -- to not to have partitions to be created in parallel\n    EXECUTE format('LOCK TABLE %I.%I IN SHARE UPDATE EXCLUSIVE MODE', table_schema_text, table_name_text);\n\n    -- get datatype here to check interval-table type alignment and generate range values in the right data format\n    SELECT atttypid\n    INTO partition_column_type\n    FROM pg_attribute\n    WHERE attrelid = table_name::oid\n    AND attnum = partition_column_index;\n\n    -- we currently only support partitioning by date, timestamp, and timestamptz\n    IF partition_column_type <> 'date'::regtype\n    AND partition_column_type <> 'timestamp'::regtype\n    AND partition_column_type <> 'timestamptz'::regtype  THEN\n        RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name;\n    END IF;\n\n    IF partition_column_type = 'date'::regtype AND partition_interval IS NOT NULL THEN\n        SELECT date_trunc('day', partition_interval) = partition_interval\n        INTO is_day_multiple;\n\n        IF NOT is_day_multiple THEN\n            RAISE 'partition interval of date partitioned table must be day or multiple days';\n        END IF;\n    END IF;\n\n    -- If no partition exists, truncate from_value to find intuitive initial value.\n    -- If any partition exist, use the initial partition as the pivot partition.\n    -- tp.to_value and tp.from_value are equal to '', if default partition exists.\n    SELECT tp.from_value::timestamptz, tp.to_value::timestamptz\n    INTO current_range_from_value, current_range_to_value\n    FROM pg_catalog.time_partitions tp\n    WHERE parent_table = table_name AND tp.to_value <> '' AND tp.from_value <> ''\n    ORDER BY tp.from_value::timestamptz ASC\n    LIMIT 1;\n\n    IF NOT FOUND THEN\n        -- Decide on the current_range_from_value of the initial partition according to interval of the table.\n        -- Since we will create all other partitions by adding intervals, truncating given start time will provide\n        -- more intuitive interval ranges, instead of starting from from_value directly.\n        IF partition_interval < INTERVAL '1 hour' THEN\n            current_range_from_value = date_trunc('minute', from_value);\n        ELSIF partition_interval < INTERVAL '1 day' THEN\n            current_range_from_value = date_trunc('hour', from_value);\n        ELSIF partition_interval < INTERVAL '1 week' THEN\n            current_range_from_value = date_trunc('day', from_value);\n        ELSIF partition_interval < INTERVAL '1 month' THEN\n            current_range_from_value = date_trunc('week', from_value);\n        ELSIF partition_interval = INTERVAL '3 months' THEN\n            current_range_from_value = date_trunc('quarter', from_value);\n        ELSIF partition_interval < INTERVAL '1 year' THEN\n            current_range_from_value = date_trunc('month', from_value);\n        ELSE\n            current_range_from_value = date_trunc('year', from_value);\n        END IF;\n\n        current_range_to_value := current_range_from_value + partition_interval;\n    ELSE\n        -- if from_value is newer than pivot's from value, go forward, else go backward\n        IF from_value >= current_range_from_value THEN\n            WHILE current_range_from_value < from_value LOOP\n                    current_range_from_value := current_range_from_value + partition_interval;\n            END LOOP;\n        ELSE\n            WHILE current_range_from_value > from_value LOOP\n                    current_range_from_value := current_range_from_value - partition_interval;\n            END LOOP;\n        END IF;\n        current_range_to_value := current_range_from_value + partition_interval;\n    END IF;\n\n    -- reuse pg_partman naming scheme for back-and-forth migration\n    IF partition_interval = INTERVAL '3 months' THEN\n        -- include quarter in partition name\n        partition_name_format = 'YYYY\"q\"Q';\n    ELSIF partition_interval = INTERVAL '1 week' THEN\n        -- include week number in partition name\n        partition_name_format := 'IYYY\"w\"IW';\n    ELSE\n        -- always start with the year\n        partition_name_format := 'YYYY';\n\n        IF partition_interval < INTERVAL '1 year' THEN\n            -- include month in partition name\n            partition_name_format := partition_name_format || '_MM';\n        END IF;\n\n        IF partition_interval < INTERVAL '1 month' THEN\n            -- include day of month in partition name\n            partition_name_format := partition_name_format || '_DD';\n        END IF;\n\n        IF partition_interval < INTERVAL '1 day' THEN\n            -- include time of day in partition name\n            partition_name_format := partition_name_format || '_HH24MI';\n        END IF;\n\n        IF partition_interval < INTERVAL '1 minute' THEN\n             -- include seconds in time of day in partition name\n             partition_name_format := partition_name_format || 'SS';\n        END IF;\n    END IF;\n\n    WHILE current_range_from_value < to_value LOOP\n        -- Check whether partition with given range has already been created\n        -- Since partition interval can be given with different types, we are converting\n        -- all variables to timestamptz to make sure that we are comparing same type of parameters\n        PERFORM * FROM pg_catalog.time_partitions tp\n        WHERE\n            tp.from_value::timestamptz = current_range_from_value::timestamptz AND\n            tp.to_value::timestamptz = current_range_to_value::timestamptz AND\n            parent_table = table_name;\n        IF found THEN\n            current_range_from_value := current_range_to_value;\n            current_range_to_value := current_range_to_value + partition_interval;\n            CONTINUE;\n        END IF;\n\n        -- Check whether any other partition covers from_value or to_value\n        -- That means some partitions doesn't align with the initial partition.\n        -- In other words, gap(s) exist between partitions which is not multiple of intervals.\n        SELECT partition, tp.from_value::text, tp.to_value::text\n        INTO manual_partition, manual_partition_from_value_text, manual_partition_to_value_text\n        FROM pg_catalog.time_partitions tp\n        WHERE\n            ((current_range_from_value::timestamptz >= tp.from_value::timestamptz AND current_range_from_value < tp.to_value::timestamptz) OR\n            (current_range_to_value::timestamptz > tp.from_value::timestamptz AND current_range_to_value::timestamptz < tp.to_value::timestamptz)) AND\n            parent_table = table_name;\n\n        IF found THEN\n            RAISE 'partition % with the range from % to % does not align with the initial partition given the partition interval',\n            manual_partition::text,\n            manual_partition_from_value_text,\n            manual_partition_to_value_text\n\t\t\tUSING HINT = 'Only use partitions of the same size, without gaps between partitions.';\n        END IF;\n\n        IF partition_column_type = 'date'::regtype THEN\n            SELECT current_range_from_value::date::text INTO current_range_from_value_text;\n            SELECT current_range_to_value::date::text INTO current_range_to_value_text;\n        ELSIF partition_column_type = 'timestamp without time zone'::regtype THEN\n            SELECT current_range_from_value::timestamp::text INTO current_range_from_value_text;\n            SELECT current_range_to_value::timestamp::text INTO current_range_to_value_text;\n        ELSIF partition_column_type = 'timestamp with time zone'::regtype THEN\n            SELECT current_range_from_value::timestamptz::text INTO current_range_from_value_text;\n            SELECT current_range_to_value::timestamptz::text INTO current_range_to_value_text;\n        ELSE\n            RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name;\n        END IF;\n\n        -- use range values within the name of partition to have unique partition names\n        RETURN QUERY\n        SELECT\n            substring(table_name_text, 0, max_table_name_length - length(to_char(current_range_from_value, partition_name_format)) - 1) || '_p' ||\n            to_char(current_range_from_value, partition_name_format),\n            current_range_from_value_text,\n            current_range_to_value_text;\n\n        current_range_from_value := current_range_to_value;\n        current_range_to_value := current_range_to_value + partition_interval;\n    END LOOP;\n    RETURN;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.get_missing_time_partition_ranges(\n\ttable_name regclass,\n    partition_interval INTERVAL,\n    to_value timestamptz,\n    from_value timestamptz)\nIS 'get missing partitions ranges for table within the range using the given interval';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_missing_time_partition_ranges/12.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.get_missing_time_partition_ranges(\n    table_name regclass,\n    partition_interval INTERVAL,\n    to_value timestamptz,\n    from_value timestamptz DEFAULT now())\nreturns table(\n    partition_name text,\n    range_from_value text,\n    range_to_value text)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    -- properties of the partitioned table\n    table_name_text text;\n    table_schema_text text;\n    number_of_partition_columns int;\n    partition_column_index int;\n    partition_column_type regtype;\n\n    -- used for generating time ranges\n    current_range_from_value timestamptz := NULL;\n    current_range_to_value timestamptz := NULL;\n    current_range_from_value_text text;\n    current_range_to_value_text text;\n\n    -- used to check whether there are misaligned (manually created) partitions\n    manual_partition regclass;\n    manual_partition_from_value_text text;\n    manual_partition_to_value_text text;\n\n    -- used for partition naming\n    partition_name_format text;\n    max_table_name_length int := current_setting('max_identifier_length');\n\n    -- used to determine whether the partition_interval is a day multiple\n    is_day_multiple boolean;\n\n    -- used to support dynamic type casting between the partition column type and timestamptz\n    custom_cast text;\n    is_partition_column_castable boolean;\n    partition regclass;\n    partition_covers_query text;\n    partition_exist_query text;\nBEGIN\n    -- check whether the table is time partitioned table, if not error out\n    SELECT relname, nspname, partnatts, partattrs[0]\n    INTO table_name_text, table_schema_text, number_of_partition_columns, partition_column_index\n    FROM pg_catalog.pg_partitioned_table, pg_catalog.pg_class c, pg_catalog.pg_namespace n\n    WHERE partrelid = c.oid AND c.oid = table_name\n    AND c.relnamespace = n.oid;\n    IF NOT FOUND THEN\n        RAISE '% is not partitioned', table_name;\n    ELSIF number_of_partition_columns <> 1 THEN\n        RAISE 'partitioned tables with multiple partition columns are not supported';\n    END IF;\n\n    -- to not to have partitions to be created in parallel\n    EXECUTE format('LOCK TABLE %I.%I IN SHARE UPDATE EXCLUSIVE MODE', table_schema_text, table_name_text);\n\n    -- get datatype here to check interval-table type alignment and generate range values in the right data format\n    SELECT atttypid\n    INTO partition_column_type\n    FROM pg_attribute\n    WHERE attrelid = table_name::oid\n    AND attnum = partition_column_index;\n\n    -- we currently only support partitioning by date, timestamp, and timestamptz\n    custom_cast = '';\n    IF partition_column_type <> 'date'::regtype\n    AND partition_column_type <> 'timestamp'::regtype\n    AND partition_column_type <> 'timestamptz'::regtype THEN\n      SELECT EXISTS(SELECT OID FROM pg_cast WHERE castsource = partition_column_type AND casttarget = 'timestamptz'::regtype) AND\n             EXISTS(SELECT OID FROM pg_cast WHERE castsource = 'timestamptz'::regtype AND casttarget = partition_column_type)\n      INTO is_partition_column_castable;\n      IF not is_partition_column_castable THEN\n        RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name;\n      END IF;\n      custom_cast = format('::%s', partition_column_type);\n    END IF;\n\n    IF partition_column_type = 'date'::regtype AND partition_interval IS NOT NULL THEN\n        SELECT date_trunc('day', partition_interval) = partition_interval\n        INTO is_day_multiple;\n\n        IF NOT is_day_multiple THEN\n            RAISE 'partition interval of date partitioned table must be day or multiple days';\n        END IF;\n    END IF;\n\n    -- If no partition exists, truncate from_value to find intuitive initial value.\n    -- If any partition exist, use the initial partition as the pivot partition.\n    -- tp.to_value and tp.from_value are equal to '', if default partition exists.\n    EXECUTE format('SELECT tp.from_value%1$s::timestamptz, tp.to_value%1$s::timestamptz\n        FROM pg_catalog.time_partitions tp\n        WHERE parent_table = $1 AND tp.to_value <> '' AND tp.from_value <> ''\n        ORDER BY tp.from_value%1$s::timestamptz ASC\n        LIMIT 1', custom_cast)\n    INTO current_range_from_value, current_range_to_value\n    USING table_name;\n\n    IF current_range_from_value is NULL THEN\n        -- Decide on the current_range_from_value of the initial partition according to interval of the table.\n        -- Since we will create all other partitions by adding intervals, truncating given start time will provide\n        -- more intuitive interval ranges, instead of starting from from_value directly.\n        IF partition_interval < INTERVAL '1 hour' THEN\n            current_range_from_value = date_trunc('minute', from_value);\n        ELSIF partition_interval < INTERVAL '1 day' THEN\n            current_range_from_value = date_trunc('hour', from_value);\n        ELSIF partition_interval < INTERVAL '1 week' THEN\n            current_range_from_value = date_trunc('day', from_value);\n        ELSIF partition_interval < INTERVAL '1 month' THEN\n            current_range_from_value = date_trunc('week', from_value);\n        ELSIF partition_interval = INTERVAL '3 months' THEN\n            current_range_from_value = date_trunc('quarter', from_value);\n        ELSIF partition_interval < INTERVAL '1 year' THEN\n            current_range_from_value = date_trunc('month', from_value);\n        ELSE\n            current_range_from_value = date_trunc('year', from_value);\n        END IF;\n\n        current_range_to_value := current_range_from_value + partition_interval;\n    ELSE\n        -- if from_value is newer than pivot's from value, go forward, else go backward\n        IF from_value >= current_range_from_value THEN\n            WHILE current_range_from_value < from_value LOOP\n                    current_range_from_value := current_range_from_value + partition_interval;\n            END LOOP;\n        ELSE\n            WHILE current_range_from_value > from_value LOOP\n                    current_range_from_value := current_range_from_value - partition_interval;\n            END LOOP;\n        END IF;\n        current_range_to_value := current_range_from_value + partition_interval;\n    END IF;\n\n    -- reuse pg_partman naming scheme for back-and-forth migration\n    IF partition_interval = INTERVAL '3 months' THEN\n        -- include quarter in partition name\n        partition_name_format = 'YYYY\"q\"Q';\n    ELSIF partition_interval = INTERVAL '1 week' THEN\n        -- include week number in partition name\n        partition_name_format := 'IYYY\"w\"IW';\n    ELSE\n        -- always start with the year\n        partition_name_format := 'YYYY';\n\n        IF partition_interval < INTERVAL '1 year' THEN\n            -- include month in partition name\n            partition_name_format := partition_name_format || '_MM';\n        END IF;\n\n        IF partition_interval < INTERVAL '1 month' THEN\n            -- include day of month in partition name\n            partition_name_format := partition_name_format || '_DD';\n        END IF;\n\n        IF partition_interval < INTERVAL '1 day' THEN\n            -- include time of day in partition name\n            partition_name_format := partition_name_format || '_HH24MI';\n        END IF;\n\n        IF partition_interval < INTERVAL '1 minute' THEN\n             -- include seconds in time of day in partition name\n             partition_name_format := partition_name_format || 'SS';\n        END IF;\n    END IF;\n\n    partition_exist_query = format('SELECT partition FROM pg_catalog.time_partitions tp\n        WHERE tp.from_value%1$s::timestamptz = $1 AND tp.to_value%1$s::timestamptz = $2 AND parent_table = $3',\n        custom_cast);\n    partition_covers_query = format('SELECT partition, tp.from_value, tp.to_value\n        FROM pg_catalog.time_partitions tp\n        WHERE\n            (($1 >= tp.from_value%1$s::timestamptz AND $1 < tp.to_value%1$s::timestamptz) OR\n            ($2 > tp.from_value%1$s::timestamptz AND $2 < tp.to_value%1$s::timestamptz)) AND\n            parent_table = $3',\n        custom_cast);\n\n    WHILE current_range_from_value < to_value LOOP\n        -- Check whether partition with given range has already been created\n        -- Since partition interval can be given with different types, we are converting\n        -- all variables to timestamptz to make sure that we are comparing same type of parameters\n        EXECUTE partition_exist_query into partition using current_range_from_value, current_range_to_value, table_name;\n\n        IF partition is not NULL THEN\n            current_range_from_value := current_range_to_value;\n            current_range_to_value := current_range_to_value + partition_interval;\n            CONTINUE;\n        END IF;\n\n        -- Check whether any other partition covers from_value or to_value\n        -- That means some partitions doesn't align with the initial partition.\n        -- In other words, gap(s) exist between partitions which is not multiple of intervals.\n        EXECUTE partition_covers_query\n        INTO manual_partition, manual_partition_from_value_text, manual_partition_to_value_text\n        using current_range_from_value, current_range_to_value, table_name;\n\n        IF manual_partition is not NULL THEN\n            RAISE 'partition % with the range from % to % does not align with the initial partition given the partition interval',\n            manual_partition::text,\n            manual_partition_from_value_text,\n            manual_partition_to_value_text\n            USING HINT = 'Only use partitions of the same size, without gaps between partitions.';\n        END IF;\n\n        IF partition_column_type = 'date'::regtype THEN\n            SELECT current_range_from_value::date::text INTO current_range_from_value_text;\n            SELECT current_range_to_value::date::text INTO current_range_to_value_text;\n        ELSIF partition_column_type = 'timestamp without time zone'::regtype THEN\n            SELECT current_range_from_value::timestamp::text INTO current_range_from_value_text;\n            SELECT current_range_to_value::timestamp::text INTO current_range_to_value_text;\n        ELSIF partition_column_type = 'timestamp with time zone'::regtype THEN\n            SELECT current_range_from_value::timestamptz::text INTO current_range_from_value_text;\n            SELECT current_range_to_value::timestamptz::text INTO current_range_to_value_text;\n        ELSE\n            EXECUTE format('SELECT $1%s::text', custom_cast) INTO current_range_from_value_text using current_range_from_value;\n            EXECUTE format('SELECT $1%s::text', custom_cast) INTO current_range_to_value_text using current_range_to_value;\n        END IF;\n\n        -- use range values within the name of partition to have unique partition names\n        RETURN QUERY\n        SELECT\n            substring(table_name_text, 0, max_table_name_length - length(to_char(current_range_from_value, partition_name_format)) - 1) || '_p' ||\n            to_char(current_range_from_value, partition_name_format),\n            current_range_from_value_text,\n            current_range_to_value_text;\n\n        current_range_from_value := current_range_to_value;\n        current_range_to_value := current_range_to_value + partition_interval;\n    END LOOP;\n    RETURN;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.get_missing_time_partition_ranges(\n    table_name regclass,\n    partition_interval INTERVAL,\n    to_value timestamptz,\n    from_value timestamptz)\nIS 'get missing partitions ranges for table within the range using the given interval';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_missing_time_partition_ranges/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.get_missing_time_partition_ranges(\n    table_name regclass,\n    partition_interval INTERVAL,\n    to_value timestamptz,\n    from_value timestamptz DEFAULT now())\nreturns table(\n    partition_name text,\n    range_from_value text,\n    range_to_value text)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    -- properties of the partitioned table\n    table_name_text text;\n    table_schema_text text;\n    number_of_partition_columns int;\n    partition_column_index int;\n    partition_column_type regtype;\n\n    -- used for generating time ranges\n    current_range_from_value timestamptz := NULL;\n    current_range_to_value timestamptz := NULL;\n    current_range_from_value_text text;\n    current_range_to_value_text text;\n\n    -- used to check whether there are misaligned (manually created) partitions\n    manual_partition regclass;\n    manual_partition_from_value_text text;\n    manual_partition_to_value_text text;\n\n    -- used for partition naming\n    partition_name_format text;\n    max_table_name_length int := current_setting('max_identifier_length');\n\n    -- used to determine whether the partition_interval is a day multiple\n    is_day_multiple boolean;\n\n    -- used to support dynamic type casting between the partition column type and timestamptz\n    custom_cast text;\n    is_partition_column_castable boolean;\n    partition regclass;\n    partition_covers_query text;\n    partition_exist_query text;\nBEGIN\n    -- check whether the table is time partitioned table, if not error out\n    SELECT relname, nspname, partnatts, partattrs[0]\n    INTO table_name_text, table_schema_text, number_of_partition_columns, partition_column_index\n    FROM pg_catalog.pg_partitioned_table, pg_catalog.pg_class c, pg_catalog.pg_namespace n\n    WHERE partrelid = c.oid AND c.oid = table_name\n    AND c.relnamespace = n.oid;\n    IF NOT FOUND THEN\n        RAISE '% is not partitioned', table_name;\n    ELSIF number_of_partition_columns <> 1 THEN\n        RAISE 'partitioned tables with multiple partition columns are not supported';\n    END IF;\n\n    -- to not to have partitions to be created in parallel\n    EXECUTE format('LOCK TABLE %I.%I IN SHARE UPDATE EXCLUSIVE MODE', table_schema_text, table_name_text);\n\n    -- get datatype here to check interval-table type alignment and generate range values in the right data format\n    SELECT atttypid\n    INTO partition_column_type\n    FROM pg_attribute\n    WHERE attrelid = table_name::oid\n    AND attnum = partition_column_index;\n\n    -- we currently only support partitioning by date, timestamp, and timestamptz\n    custom_cast = '';\n    IF partition_column_type <> 'date'::regtype\n    AND partition_column_type <> 'timestamp'::regtype\n    AND partition_column_type <> 'timestamptz'::regtype THEN\n      SELECT EXISTS(SELECT OID FROM pg_cast WHERE castsource = partition_column_type AND casttarget = 'timestamptz'::regtype) AND\n             EXISTS(SELECT OID FROM pg_cast WHERE castsource = 'timestamptz'::regtype AND casttarget = partition_column_type)\n      INTO is_partition_column_castable;\n      IF not is_partition_column_castable THEN\n        RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name;\n      END IF;\n      custom_cast = format('::%s', partition_column_type);\n    END IF;\n\n    IF partition_column_type = 'date'::regtype AND partition_interval IS NOT NULL THEN\n        SELECT date_trunc('day', partition_interval) = partition_interval\n        INTO is_day_multiple;\n\n        IF NOT is_day_multiple THEN\n            RAISE 'partition interval of date partitioned table must be day or multiple days';\n        END IF;\n    END IF;\n\n    -- If no partition exists, truncate from_value to find intuitive initial value.\n    -- If any partition exist, use the initial partition as the pivot partition.\n    -- tp.to_value and tp.from_value are equal to '', if default partition exists.\n    EXECUTE format('SELECT tp.from_value%1$s::timestamptz, tp.to_value%1$s::timestamptz\n        FROM pg_catalog.time_partitions tp\n        WHERE parent_table = $1 AND tp.to_value <> '' AND tp.from_value <> ''\n        ORDER BY tp.from_value%1$s::timestamptz ASC\n        LIMIT 1', custom_cast)\n    INTO current_range_from_value, current_range_to_value\n    USING table_name;\n\n    IF current_range_from_value is NULL THEN\n        -- Decide on the current_range_from_value of the initial partition according to interval of the table.\n        -- Since we will create all other partitions by adding intervals, truncating given start time will provide\n        -- more intuitive interval ranges, instead of starting from from_value directly.\n        IF partition_interval < INTERVAL '1 hour' THEN\n            current_range_from_value = date_trunc('minute', from_value);\n        ELSIF partition_interval < INTERVAL '1 day' THEN\n            current_range_from_value = date_trunc('hour', from_value);\n        ELSIF partition_interval < INTERVAL '1 week' THEN\n            current_range_from_value = date_trunc('day', from_value);\n        ELSIF partition_interval < INTERVAL '1 month' THEN\n            current_range_from_value = date_trunc('week', from_value);\n        ELSIF partition_interval = INTERVAL '3 months' THEN\n            current_range_from_value = date_trunc('quarter', from_value);\n        ELSIF partition_interval < INTERVAL '1 year' THEN\n            current_range_from_value = date_trunc('month', from_value);\n        ELSE\n            current_range_from_value = date_trunc('year', from_value);\n        END IF;\n\n        current_range_to_value := current_range_from_value + partition_interval;\n    ELSE\n        -- if from_value is newer than pivot's from value, go forward, else go backward\n        IF from_value >= current_range_from_value THEN\n            WHILE current_range_from_value < from_value LOOP\n                    current_range_from_value := current_range_from_value + partition_interval;\n            END LOOP;\n        ELSE\n            WHILE current_range_from_value > from_value LOOP\n                    current_range_from_value := current_range_from_value - partition_interval;\n            END LOOP;\n        END IF;\n        current_range_to_value := current_range_from_value + partition_interval;\n    END IF;\n\n    -- reuse pg_partman naming scheme for back-and-forth migration\n    IF partition_interval = INTERVAL '3 months' THEN\n        -- include quarter in partition name\n        partition_name_format = 'YYYY\"q\"Q';\n    ELSIF partition_interval = INTERVAL '1 week' THEN\n        -- include week number in partition name\n        partition_name_format := 'IYYY\"w\"IW';\n    ELSE\n        -- always start with the year\n        partition_name_format := 'YYYY';\n\n        IF partition_interval < INTERVAL '1 year' THEN\n            -- include month in partition name\n            partition_name_format := partition_name_format || '_MM';\n        END IF;\n\n        IF partition_interval < INTERVAL '1 month' THEN\n            -- include day of month in partition name\n            partition_name_format := partition_name_format || '_DD';\n        END IF;\n\n        IF partition_interval < INTERVAL '1 day' THEN\n            -- include time of day in partition name\n            partition_name_format := partition_name_format || '_HH24MI';\n        END IF;\n\n        IF partition_interval < INTERVAL '1 minute' THEN\n             -- include seconds in time of day in partition name\n             partition_name_format := partition_name_format || 'SS';\n        END IF;\n    END IF;\n\n    partition_exist_query = format('SELECT partition FROM pg_catalog.time_partitions tp\n        WHERE tp.from_value%1$s::timestamptz = $1 AND tp.to_value%1$s::timestamptz = $2 AND parent_table = $3',\n        custom_cast);\n    partition_covers_query = format('SELECT partition, tp.from_value, tp.to_value\n        FROM pg_catalog.time_partitions tp\n        WHERE\n            (($1 >= tp.from_value%1$s::timestamptz AND $1 < tp.to_value%1$s::timestamptz) OR\n            ($2 > tp.from_value%1$s::timestamptz AND $2 < tp.to_value%1$s::timestamptz)) AND\n            parent_table = $3',\n        custom_cast);\n\n    WHILE current_range_from_value < to_value LOOP\n        -- Check whether partition with given range has already been created\n        -- Since partition interval can be given with different types, we are converting\n        -- all variables to timestamptz to make sure that we are comparing same type of parameters\n        EXECUTE partition_exist_query into partition using current_range_from_value, current_range_to_value, table_name;\n\n        IF partition is not NULL THEN\n            current_range_from_value := current_range_to_value;\n            current_range_to_value := current_range_to_value + partition_interval;\n            CONTINUE;\n        END IF;\n\n        -- Check whether any other partition covers from_value or to_value\n        -- That means some partitions doesn't align with the initial partition.\n        -- In other words, gap(s) exist between partitions which is not multiple of intervals.\n        EXECUTE partition_covers_query\n        INTO manual_partition, manual_partition_from_value_text, manual_partition_to_value_text\n        using current_range_from_value, current_range_to_value, table_name;\n\n        IF manual_partition is not NULL THEN\n            RAISE 'partition % with the range from % to % does not align with the initial partition given the partition interval',\n            manual_partition::text,\n            manual_partition_from_value_text,\n            manual_partition_to_value_text\n            USING HINT = 'Only use partitions of the same size, without gaps between partitions.';\n        END IF;\n\n        IF partition_column_type = 'date'::regtype THEN\n            SELECT current_range_from_value::date::text INTO current_range_from_value_text;\n            SELECT current_range_to_value::date::text INTO current_range_to_value_text;\n        ELSIF partition_column_type = 'timestamp without time zone'::regtype THEN\n            SELECT current_range_from_value::timestamp::text INTO current_range_from_value_text;\n            SELECT current_range_to_value::timestamp::text INTO current_range_to_value_text;\n        ELSIF partition_column_type = 'timestamp with time zone'::regtype THEN\n            SELECT current_range_from_value::timestamptz::text INTO current_range_from_value_text;\n            SELECT current_range_to_value::timestamptz::text INTO current_range_to_value_text;\n        ELSE\n            EXECUTE format('SELECT $1%s::text', custom_cast) INTO current_range_from_value_text using current_range_from_value;\n            EXECUTE format('SELECT $1%s::text', custom_cast) INTO current_range_to_value_text using current_range_to_value;\n        END IF;\n\n        -- use range values within the name of partition to have unique partition names\n        RETURN QUERY\n        SELECT\n            substring(table_name_text, 0, max_table_name_length - length(to_char(current_range_from_value, partition_name_format)) - 1) || '_p' ||\n            to_char(current_range_from_value, partition_name_format),\n            current_range_from_value_text,\n            current_range_to_value_text;\n\n        current_range_from_value := current_range_to_value;\n        current_range_to_value := current_range_to_value + partition_interval;\n    END LOOP;\n    RETURN;\nEND;\n$$;\nCOMMENT ON FUNCTION pg_catalog.get_missing_time_partition_ranges(\n    table_name regclass,\n    partition_interval INTERVAL,\n    to_value timestamptz,\n    from_value timestamptz)\nIS 'get missing partitions ranges for table within the range using the given interval';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_rebalance_progress/10.1-1.sql",
    "content": "DROP FUNCTION pg_catalog.get_rebalance_progress();\n\nCREATE OR REPLACE FUNCTION pg_catalog.get_rebalance_progress()\n  RETURNS TABLE(sessionid integer,\n                table_name regclass,\n                shardid bigint,\n                shard_size bigint,\n                sourcename text,\n                sourceport int,\n                targetname text,\n                targetport int,\n                progress bigint,\n                source_shard_size bigint,\n                target_shard_size bigint)\n  AS 'MODULE_PATHNAME'\n  LANGUAGE C STRICT;\nCOMMENT ON FUNCTION pg_catalog.get_rebalance_progress()\n    IS 'provides progress information about the ongoing rebalance operations';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_rebalance_progress/11.1-1.sql",
    "content": "DROP FUNCTION pg_catalog.get_rebalance_progress();\n\nCREATE OR REPLACE FUNCTION pg_catalog.get_rebalance_progress()\n  RETURNS TABLE(sessionid integer,\n                table_name regclass,\n                shardid bigint,\n                shard_size bigint,\n                sourcename text,\n                sourceport int,\n                targetname text,\n                targetport int,\n                progress bigint,\n                source_shard_size bigint,\n                target_shard_size bigint,\n                operation_type text\n            )\n  AS 'MODULE_PATHNAME'\n  LANGUAGE C STRICT;\nCOMMENT ON FUNCTION pg_catalog.get_rebalance_progress()\n    IS 'provides progress information about the ongoing rebalance operations';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_rebalance_progress/11.2-1.sql",
    "content": "DROP FUNCTION pg_catalog.get_rebalance_progress();\n\nCREATE OR REPLACE FUNCTION pg_catalog.get_rebalance_progress()\n  RETURNS TABLE(sessionid integer,\n                table_name regclass,\n                shardid bigint,\n                shard_size bigint,\n                sourcename text,\n                sourceport int,\n                targetname text,\n                targetport int,\n                progress bigint,\n                source_shard_size bigint,\n                target_shard_size bigint,\n                operation_type text,\n                source_lsn pg_lsn,\n                target_lsn pg_lsn,\n                status text\n            )\n  AS 'MODULE_PATHNAME'\n  LANGUAGE C STRICT;\nCOMMENT ON FUNCTION pg_catalog.get_rebalance_progress()\n    IS 'provides progress information about the ongoing rebalance operations';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_rebalance_progress/9.0-1.sql",
    "content": "-- get_rebalance_progress returns the list of shard placement move operations along with\n-- their progressions for ongoing rebalance operations.\n--\nCREATE OR REPLACE FUNCTION pg_catalog.get_rebalance_progress()\n  RETURNS TABLE(sessionid integer,\n                table_name regclass,\n                shardid bigint,\n                shard_size bigint,\n                sourcename text,\n                sourceport int,\n                targetname text,\n                targetport int,\n                progress bigint)\n  AS 'MODULE_PATHNAME'\n  LANGUAGE C STRICT;\nCOMMENT ON FUNCTION pg_catalog.get_rebalance_progress()\n    IS 'provides progress information about the ongoing rebalance operations';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_rebalance_progress/latest.sql",
    "content": "DROP FUNCTION pg_catalog.get_rebalance_progress();\n\nCREATE OR REPLACE FUNCTION pg_catalog.get_rebalance_progress()\n  RETURNS TABLE(sessionid integer,\n                table_name regclass,\n                shardid bigint,\n                shard_size bigint,\n                sourcename text,\n                sourceport int,\n                targetname text,\n                targetport int,\n                progress bigint,\n                source_shard_size bigint,\n                target_shard_size bigint,\n                operation_type text,\n                source_lsn pg_lsn,\n                target_lsn pg_lsn,\n                status text\n            )\n  AS 'MODULE_PATHNAME'\n  LANGUAGE C STRICT;\nCOMMENT ON FUNCTION pg_catalog.get_rebalance_progress()\n    IS 'provides progress information about the ongoing rebalance operations';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_rebalance_table_shards_plan/10.1-1.sql",
    "content": "-- get_rebalance_table_shards_plan shows the actual events that will be performed\n-- if a rebalance operation will be performed with the same arguments, which allows users\n-- to understand the impact of the change overall availability of the application and\n-- network trafic.\n--\nDROP FUNCTION pg_catalog.get_rebalance_table_shards_plan;\nCREATE OR REPLACE FUNCTION pg_catalog.get_rebalance_table_shards_plan(\n        relation regclass default NULL,\n        threshold float4 default NULL,\n        max_shard_moves int default 1000000,\n        excluded_shard_list bigint[] default '{}',\n        drain_only boolean default false,\n        rebalance_strategy name default NULL,\n        improvement_threshold float4 DEFAULT NULL\n    )\n    RETURNS TABLE (table_name regclass,\n                   shardid bigint,\n                   shard_size bigint,\n                   sourcename text,\n                   sourceport int,\n                   targetname text,\n                   targetport int)\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.get_rebalance_table_shards_plan(regclass, float4, int, bigint[], boolean, name, float4)\n    IS 'returns the list of shard placement moves to be done on a rebalance operation';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_rebalance_table_shards_plan/9.0-1.sql",
    "content": "-- get_rebalance_table_shards_plan shows the actual events that will be performed\n-- if a rebalance operation will be performed with the same arguments, which allows users\n-- to understand the impact of the change overall availability of the application and\n-- network trafic.\n--\nCREATE OR REPLACE FUNCTION pg_catalog.get_rebalance_table_shards_plan(\n        relation regclass,\n        threshold float4 default 0,\n        max_shard_moves int default 1000000,\n        excluded_shard_list bigint[] default '{}')\n    RETURNS TABLE (table_name regclass,\n                   shardid bigint,\n                   shard_size bigint,\n                   sourcename text,\n                   sourceport int,\n                   targetname text,\n                   targetport int)\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C STRICT VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.get_rebalance_table_shards_plan(regclass, float4, int, bigint[])\n    IS 'returns the list of shard placement moves to be done on a rebalance operation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_rebalance_table_shards_plan/9.1-1.sql",
    "content": "-- get_rebalance_table_shards_plan shows the actual events that will be performed\n-- if a rebalance operation will be performed with the same arguments, which allows users\n-- to understand the impact of the change overall availability of the application and\n-- network trafic.\n--\nDROP FUNCTION pg_catalog.get_rebalance_table_shards_plan;\nCREATE OR REPLACE FUNCTION pg_catalog.get_rebalance_table_shards_plan(\n        relation regclass default NULL,\n        threshold float4 default 0,\n        max_shard_moves int default 1000000,\n        excluded_shard_list bigint[] default '{}',\n        drain_only boolean default false)\n    RETURNS TABLE (table_name regclass,\n                   shardid bigint,\n                   shard_size bigint,\n                   sourcename text,\n                   sourceport int,\n                   targetname text,\n                   targetport int)\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.get_rebalance_table_shards_plan(regclass, float4, int, bigint[], boolean)\n    IS 'returns the list of shard placement moves to be done on a rebalance operation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_rebalance_table_shards_plan/9.2-1.sql",
    "content": "-- get_rebalance_table_shards_plan shows the actual events that will be performed\n-- if a rebalance operation will be performed with the same arguments, which allows users\n-- to understand the impact of the change overall availability of the application and\n-- network trafic.\n--\nDROP FUNCTION pg_catalog.get_rebalance_table_shards_plan;\nCREATE OR REPLACE FUNCTION pg_catalog.get_rebalance_table_shards_plan(\n        relation regclass default NULL,\n        threshold float4 default NULL,\n        max_shard_moves int default 1000000,\n        excluded_shard_list bigint[] default '{}',\n        drain_only boolean default false,\n        rebalance_strategy name default NULL\n    )\n    RETURNS TABLE (table_name regclass,\n                   shardid bigint,\n                   shard_size bigint,\n                   sourcename text,\n                   sourceport int,\n                   targetname text,\n                   targetport int)\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.get_rebalance_table_shards_plan(regclass, float4, int, bigint[], boolean, name)\n    IS 'returns the list of shard placement moves to be done on a rebalance operation';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_rebalance_table_shards_plan/latest.sql",
    "content": "-- get_rebalance_table_shards_plan shows the actual events that will be performed\n-- if a rebalance operation will be performed with the same arguments, which allows users\n-- to understand the impact of the change overall availability of the application and\n-- network trafic.\n--\nDROP FUNCTION pg_catalog.get_rebalance_table_shards_plan;\nCREATE OR REPLACE FUNCTION pg_catalog.get_rebalance_table_shards_plan(\n        relation regclass default NULL,\n        threshold float4 default NULL,\n        max_shard_moves int default 1000000,\n        excluded_shard_list bigint[] default '{}',\n        drain_only boolean default false,\n        rebalance_strategy name default NULL,\n        improvement_threshold float4 DEFAULT NULL\n    )\n    RETURNS TABLE (table_name regclass,\n                   shardid bigint,\n                   shard_size bigint,\n                   sourcename text,\n                   sourceport int,\n                   targetname text,\n                   targetport int)\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.get_rebalance_table_shards_plan(regclass, float4, int, bigint[], boolean, name, float4)\n    IS 'returns the list of shard placement moves to be done on a rebalance operation';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_snapshot_based_node_split_plan/13.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.get_snapshot_based_node_split_plan(\n    primary_node_name text,\n    primary_node_port integer,\n    replica_node_name text,\n    replica_node_port integer,\n    rebalance_strategy name DEFAULT NULL\n    )\n    RETURNS TABLE (table_name regclass,\n                   shardid bigint,\n                   shard_size bigint,\n                   placement_node text)\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\n\nCOMMENT ON FUNCTION pg_catalog.get_snapshot_based_node_split_plan(text, int, text, int, name)\n    IS 'shows the shard placements to balance shards between primary and replica worker nodes';\n\nREVOKE ALL ON FUNCTION pg_catalog.get_snapshot_based_node_split_plan(text, int, text, int, name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/get_snapshot_based_node_split_plan/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.get_snapshot_based_node_split_plan(\n    primary_node_name text,\n    primary_node_port integer,\n    replica_node_name text,\n    replica_node_port integer,\n    rebalance_strategy name DEFAULT NULL\n    )\n    RETURNS TABLE (table_name regclass,\n                   shardid bigint,\n                   shard_size bigint,\n                   placement_node text)\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\n\nCOMMENT ON FUNCTION pg_catalog.get_snapshot_based_node_split_plan(text, int, text, int, name)\n    IS 'shows the shard placements to balance shards between primary and replica worker nodes';\n\nREVOKE ALL ON FUNCTION pg_catalog.get_snapshot_based_node_split_plan(text, int, text, int, name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/11.1-1.sql",
    "content": "CREATE FUNCTION pg_catalog.isolate_tenant_to_new_shard(\n  table_name regclass,\n  tenant_id \"any\",\n  cascade_option text DEFAULT '',\n  shard_transfer_mode citus.shard_transfer_mode DEFAULT 'auto')\nRETURNS bigint LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$isolate_tenant_to_new_shard$$;\n\nCOMMENT ON FUNCTION pg_catalog.isolate_tenant_to_new_shard(\n  table_name regclass,\n  tenant_id \"any\",\n  cascade_option text,\n  shard_transfer_mode citus.shard_transfer_mode)\nIS 'isolate a tenant to its own shard and return the new shard id';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/8.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.isolate_tenant_to_new_shard(table_name regclass, tenant_id \"any\", cascade_option text DEFAULT '')\n\tRETURNS bigint\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$isolate_tenant_to_new_shard$$;\n\nCOMMENT ON FUNCTION pg_catalog.isolate_tenant_to_new_shard(table_name regclass, tenant_id \"any\", cascade_option text)\n    IS 'isolate a tenant to its own shard and return the new shard id';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/isolate_tenant_to_new_shard/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.isolate_tenant_to_new_shard(\n  table_name regclass,\n  tenant_id \"any\",\n  cascade_option text DEFAULT '',\n  shard_transfer_mode citus.shard_transfer_mode DEFAULT 'auto')\nRETURNS bigint LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$isolate_tenant_to_new_shard$$;\n\nCOMMENT ON FUNCTION pg_catalog.isolate_tenant_to_new_shard(\n  table_name regclass,\n  tenant_id \"any\",\n  cascade_option text,\n  shard_transfer_mode citus.shard_transfer_mode)\nIS 'isolate a tenant to its own shard and return the new shard id';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/master_add_inactive_node/9.1-1.sql",
    "content": "-- Update the default groupId to -1\nDROP FUNCTION master_add_inactive_node(text, integer, integer, noderole, name);\nCREATE FUNCTION master_add_inactive_node(nodename text,\n                                         nodeport integer,\n                                         groupid integer default -1,\n                                         noderole noderole default 'primary',\n                                         nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME',$$master_add_inactive_node$$;\nCOMMENT ON FUNCTION master_add_inactive_node(nodename text,nodeport integer,\n                                             groupid integer, noderole noderole,\n                                             nodecluster name)\n  IS 'prepare node by adding it to pg_dist_node';\nREVOKE ALL ON FUNCTION master_add_inactive_node(text,int,int,noderole,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/master_add_inactive_node/latest.sql",
    "content": "-- Update the default groupId to -1\nDROP FUNCTION master_add_inactive_node(text, integer, integer, noderole, name);\nCREATE FUNCTION master_add_inactive_node(nodename text,\n                                         nodeport integer,\n                                         groupid integer default -1,\n                                         noderole noderole default 'primary',\n                                         nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME',$$master_add_inactive_node$$;\nCOMMENT ON FUNCTION master_add_inactive_node(nodename text,nodeport integer,\n                                             groupid integer, noderole noderole,\n                                             nodecluster name)\n  IS 'prepare node by adding it to pg_dist_node';\nREVOKE ALL ON FUNCTION master_add_inactive_node(text,int,int,noderole,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/master_add_node/9.1-1.sql",
    "content": "-- Update the default groupId to -1\nDROP FUNCTION master_add_node(text, integer, integer, noderole, name);\nCREATE FUNCTION master_add_node(nodename text,\n                                nodeport integer,\n                                groupid integer default -1,\n                                noderole noderole default 'primary',\n                                nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$master_add_node$$;\nCOMMENT ON FUNCTION master_add_node(nodename text, nodeport integer,\n                                    groupid integer, noderole noderole, nodecluster name)\n  IS 'add node to the cluster';\nREVOKE ALL ON FUNCTION master_add_node(text,int,int,noderole,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/master_add_node/latest.sql",
    "content": "-- Update the default groupId to -1\nDROP FUNCTION master_add_node(text, integer, integer, noderole, name);\nCREATE FUNCTION master_add_node(nodename text,\n                                nodeport integer,\n                                groupid integer default -1,\n                                noderole noderole default 'primary',\n                                nodecluster name default 'default')\n  RETURNS INTEGER\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$master_add_node$$;\nCOMMENT ON FUNCTION master_add_node(nodename text, nodeport integer,\n                                    groupid integer, noderole noderole, nodecluster name)\n  IS 'add node to the cluster';\nREVOKE ALL ON FUNCTION master_add_node(text,int,int,noderole,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/master_drain_node/9.1-1.sql",
    "content": "CREATE FUNCTION pg_catalog.master_drain_node(\n    nodename text,\n    nodeport integer,\n    shard_transfer_mode citus.shard_transfer_mode default 'auto')\n  RETURNS VOID\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$master_drain_node$$;\nCOMMENT ON FUNCTION pg_catalog.master_drain_node(text,int,citus.shard_transfer_mode)\n  IS 'mark a node to be drained of data and actually drain it as well';\n\nREVOKE ALL ON FUNCTION pg_catalog.master_drain_node(text,int,citus.shard_transfer_mode) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/master_drain_node/9.2-1.sql",
    "content": "DROP FUNCTION pg_catalog.master_drain_node;\nCREATE FUNCTION pg_catalog.master_drain_node(\n    nodename text,\n    nodeport integer,\n    shard_transfer_mode citus.shard_transfer_mode default 'auto',\n    rebalance_strategy name default NULL\n  )\n  RETURNS VOID\n  LANGUAGE C\n  AS 'MODULE_PATHNAME', $$master_drain_node$$;\nCOMMENT ON FUNCTION pg_catalog.master_drain_node(text,int,citus.shard_transfer_mode,name)\n  IS 'mark a node to be drained of data and actually drain it as well';\n\nREVOKE ALL ON FUNCTION pg_catalog.master_drain_node(text,int,citus.shard_transfer_mode,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/master_drain_node/latest.sql",
    "content": "DROP FUNCTION pg_catalog.master_drain_node;\nCREATE FUNCTION pg_catalog.master_drain_node(\n    nodename text,\n    nodeport integer,\n    shard_transfer_mode citus.shard_transfer_mode default 'auto',\n    rebalance_strategy name default NULL\n  )\n  RETURNS VOID\n  LANGUAGE C\n  AS 'MODULE_PATHNAME', $$master_drain_node$$;\nCOMMENT ON FUNCTION pg_catalog.master_drain_node(text,int,citus.shard_transfer_mode,name)\n  IS 'mark a node to be drained of data and actually drain it as well';\n\nREVOKE ALL ON FUNCTION pg_catalog.master_drain_node(text,int,citus.shard_transfer_mode,name) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/master_set_node_property/9.1-1.sql",
    "content": "CREATE FUNCTION pg_catalog.master_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  RETURNS VOID\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', 'master_set_node_property';\nCOMMENT ON FUNCTION pg_catalog.master_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  IS 'set a property of a node in pg_dist_node';\n\nREVOKE ALL ON FUNCTION pg_catalog.master_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/master_set_node_property/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.master_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  RETURNS VOID\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', 'master_set_node_property';\nCOMMENT ON FUNCTION pg_catalog.master_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  IS 'set a property of a node in pg_dist_node';\n\nREVOKE ALL ON FUNCTION pg_catalog.master_set_node_property(\n    nodename text,\n    nodeport integer,\n    property text,\n    value boolean)\n  FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/pg_cancel_backend/11.0-1.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.pg_cancel_backend(global_pid bigint) CASCADE;\n\nCREATE OR REPLACE FUNCTION pg_catalog.pg_cancel_backend(global_pid bigint)\n    RETURNS BOOL\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$citus_cancel_backend$$;\n\nCOMMENT ON FUNCTION pg_catalog.pg_cancel_backend(global_pid bigint)\n    IS 'cancels a Citus query which might be on any node in the Citus cluster';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/pg_cancel_backend/latest.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.pg_cancel_backend(global_pid bigint) CASCADE;\n\nCREATE OR REPLACE FUNCTION pg_catalog.pg_cancel_backend(global_pid bigint)\n    RETURNS BOOL\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$citus_cancel_backend$$;\n\nCOMMENT ON FUNCTION pg_catalog.pg_cancel_backend(global_pid bigint)\n    IS 'cancels a Citus query which might be on any node in the Citus cluster';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/pg_dist_rebalance_strategy_trigger_func/9.2-1.sql",
    "content": "-- Ensures that only a single default strategy is possible\nCREATE OR REPLACE FUNCTION citus_internal.pg_dist_rebalance_strategy_trigger_func()\nRETURNS TRIGGER AS $$\n  BEGIN\n    -- citus_add_rebalance_strategy also takes out a ShareRowExclusiveLock\n    LOCK TABLE pg_dist_rebalance_strategy IN SHARE ROW EXCLUSIVE MODE;\n\n    PERFORM citus_validate_rebalance_strategy_functions(\n        NEW.shard_cost_function,\n        NEW.node_capacity_function,\n        NEW.shard_allowed_on_node_function);\n\n    IF NEW.default_threshold < NEW.minimum_threshold THEN\n      RAISE EXCEPTION 'default_threshold cannot be smaller than minimum_threshold';\n    END IF;\n\n    IF NOT NEW.default_strategy THEN\n      RETURN NEW;\n    END IF;\n    IF TG_OP = 'UPDATE' AND NEW.default_strategy = OLD.default_strategy THEN\n        return NEW;\n    END IF;\n    IF EXISTS (SELECT 1 FROM pg_dist_rebalance_strategy WHERE default_strategy) THEN\n      RAISE EXCEPTION 'there cannot be two default strategies';\n    END IF;\n    RETURN NEW;\n  END;\n$$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/pg_dist_rebalance_strategy_trigger_func/latest.sql",
    "content": "-- Ensures that only a single default strategy is possible\nCREATE OR REPLACE FUNCTION citus_internal.pg_dist_rebalance_strategy_trigger_func()\nRETURNS TRIGGER AS $$\n  BEGIN\n    -- citus_add_rebalance_strategy also takes out a ShareRowExclusiveLock\n    LOCK TABLE pg_dist_rebalance_strategy IN SHARE ROW EXCLUSIVE MODE;\n\n    PERFORM citus_validate_rebalance_strategy_functions(\n        NEW.shard_cost_function,\n        NEW.node_capacity_function,\n        NEW.shard_allowed_on_node_function);\n\n    IF NEW.default_threshold < NEW.minimum_threshold THEN\n      RAISE EXCEPTION 'default_threshold cannot be smaller than minimum_threshold';\n    END IF;\n\n    IF NOT NEW.default_strategy THEN\n      RETURN NEW;\n    END IF;\n    IF TG_OP = 'UPDATE' AND NEW.default_strategy = OLD.default_strategy THEN\n        return NEW;\n    END IF;\n    IF EXISTS (SELECT 1 FROM pg_dist_rebalance_strategy WHERE default_strategy) THEN\n      RAISE EXCEPTION 'there cannot be two default strategies';\n    END IF;\n    RETURN NEW;\n  END;\n$$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/pg_dist_shard_placement_trigger_func/9.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.pg_dist_shard_placement_trigger_func()\nRETURNS TRIGGER AS $$\n  BEGIN\n    IF (TG_OP = 'DELETE') THEN\n      DELETE FROM pg_dist_placement WHERE placementid = OLD.placementid;\n      RETURN OLD;\n    ELSIF (TG_OP = 'UPDATE') THEN\n      UPDATE pg_dist_placement\n        SET shardid = NEW.shardid, shardstate = NEW.shardstate,\n            shardlength = NEW.shardlength, placementid = NEW.placementid,\n            groupid = citus_internal.find_groupid_for_node(NEW.nodename, NEW.nodeport)\n        WHERE placementid = OLD.placementid;\n      RETURN NEW;\n    ELSIF (TG_OP = 'INSERT') THEN\n      INSERT INTO pg_dist_placement\n        (placementid, shardid, shardstate, shardlength, groupid)\n      VALUES (NEW.placementid, NEW.shardid, NEW.shardstate, NEW.shardlength,\n        citus_internal.find_groupid_for_node(NEW.nodename, NEW.nodeport));\n      RETURN NEW;\n    END IF;\n  END;\n$$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/pg_dist_shard_placement_trigger_func/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.pg_dist_shard_placement_trigger_func()\nRETURNS TRIGGER AS $$\n  BEGIN\n    IF (TG_OP = 'DELETE') THEN\n      DELETE FROM pg_dist_placement WHERE placementid = OLD.placementid;\n      RETURN OLD;\n    ELSIF (TG_OP = 'UPDATE') THEN\n      UPDATE pg_dist_placement\n        SET shardid = NEW.shardid, shardstate = NEW.shardstate,\n            shardlength = NEW.shardlength, placementid = NEW.placementid,\n            groupid = citus_internal.find_groupid_for_node(NEW.nodename, NEW.nodeport)\n        WHERE placementid = OLD.placementid;\n      RETURN NEW;\n    ELSIF (TG_OP = 'INSERT') THEN\n      INSERT INTO pg_dist_placement\n        (placementid, shardid, shardstate, shardlength, groupid)\n      VALUES (NEW.placementid, NEW.shardid, NEW.shardstate, NEW.shardlength,\n        citus_internal.find_groupid_for_node(NEW.nodename, NEW.nodeport));\n      RETURN NEW;\n    END IF;\n  END;\n$$ LANGUAGE plpgsql;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/pg_terminate_backend/11.0-1.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.pg_terminate_backend(global_pid bigint, timeout bigint) CASCADE;\n\nCREATE OR REPLACE FUNCTION pg_catalog.pg_terminate_backend(global_pid bigint, timeout bigint DEFAULT 0)\n    RETURNS BOOL\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$citus_terminate_backend$$;\n\nCOMMENT ON FUNCTION pg_catalog.pg_terminate_backend(global_pid bigint, timeout bigint)\n    IS 'terminates a Citus query which might be on any node in the Citus cluster';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/pg_terminate_backend/latest.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.pg_terminate_backend(global_pid bigint, timeout bigint) CASCADE;\n\nCREATE OR REPLACE FUNCTION pg_catalog.pg_terminate_backend(global_pid bigint, timeout bigint DEFAULT 0)\n    RETURNS BOOL\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$citus_terminate_backend$$;\n\nCOMMENT ON FUNCTION pg_catalog.pg_terminate_backend(global_pid bigint, timeout bigint)\n    IS 'terminates a Citus query which might be on any node in the Citus cluster';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/read_intermediate_results/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.read_intermediate_results(\n    result_ids text[],\n    format pg_catalog.citus_copy_format default 'csv')\nRETURNS SETOF record\nLANGUAGE C STRICT VOLATILE PARALLEL SAFE\nAS 'MODULE_PATHNAME', $$read_intermediate_result_array$$;\nCOMMENT ON FUNCTION pg_catalog.read_intermediate_results(text[],pg_catalog.citus_copy_format)\nIS 'read a set files and return them as a set of records';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/read_intermediate_results/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.read_intermediate_results(\n    result_ids text[],\n    format pg_catalog.citus_copy_format default 'csv')\nRETURNS SETOF record\nLANGUAGE C STRICT VOLATILE PARALLEL SAFE\nAS 'MODULE_PATHNAME', $$read_intermediate_result_array$$;\nCOMMENT ON FUNCTION pg_catalog.read_intermediate_results(text[],pg_catalog.citus_copy_format)\nIS 'read a set files and return them as a set of records';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/rebalance_table_shards/9.0-1.sql",
    "content": "-- rebalance_table_shards uses the shard rebalancer's C UDF functions to rebalance\n-- shards of the given relation.\n--\nCREATE OR REPLACE FUNCTION pg_catalog.rebalance_table_shards(\n        relation regclass,\n        threshold float4 default 0,\n        max_shard_moves int default 1000000,\n        excluded_shard_list bigint[] default '{}',\n        shard_transfer_mode citus.shard_transfer_mode default 'auto')\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C STRICT VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.rebalance_table_shards(regclass, float4, int, bigint[], citus.shard_transfer_mode)\n    IS 'rebalance the shards of the given table across the worker nodes (including colocated shards of other tables)';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/rebalance_table_shards/9.1-1.sql",
    "content": "-- rebalance_table_shards uses the shard rebalancer's C UDF functions to rebalance\n-- shards of the given relation.\n--\nDROP FUNCTION pg_catalog.rebalance_table_shards;\nCREATE OR REPLACE FUNCTION pg_catalog.rebalance_table_shards(\n        relation regclass default NULL,\n        threshold float4 default 0,\n        max_shard_moves int default 1000000,\n        excluded_shard_list bigint[] default '{}',\n        shard_transfer_mode citus.shard_transfer_mode default 'auto',\n        drain_only boolean default false)\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.rebalance_table_shards(regclass, float4, int, bigint[], citus.shard_transfer_mode, boolean)\n    IS 'rebalance the shards of the given table across the worker nodes (including colocated shards of other tables)';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/rebalance_table_shards/9.2-1.sql",
    "content": "-- rebalance_table_shards uses the shard rebalancer's C UDF functions to rebalance\n-- shards of the given relation.\n--\nDROP FUNCTION pg_catalog.rebalance_table_shards;\nCREATE OR REPLACE FUNCTION pg_catalog.rebalance_table_shards(\n        relation regclass default NULL,\n        threshold float4 default NULL,\n        max_shard_moves int default 1000000,\n        excluded_shard_list bigint[] default '{}',\n        shard_transfer_mode citus.shard_transfer_mode default 'auto',\n        drain_only boolean default false,\n        rebalance_strategy name default NULL\n    )\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.rebalance_table_shards(regclass, float4, int, bigint[], citus.shard_transfer_mode, boolean, name)\n    IS 'rebalance the shards of the given table across the worker nodes (including colocated shards of other tables)';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/rebalance_table_shards/latest.sql",
    "content": "-- rebalance_table_shards uses the shard rebalancer's C UDF functions to rebalance\n-- shards of the given relation.\n--\nDROP FUNCTION pg_catalog.rebalance_table_shards;\nCREATE OR REPLACE FUNCTION pg_catalog.rebalance_table_shards(\n        relation regclass default NULL,\n        threshold float4 default NULL,\n        max_shard_moves int default 1000000,\n        excluded_shard_list bigint[] default '{}',\n        shard_transfer_mode citus.shard_transfer_mode default 'auto',\n        drain_only boolean default false,\n        rebalance_strategy name default NULL\n    )\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C VOLATILE;\nCOMMENT ON FUNCTION pg_catalog.rebalance_table_shards(regclass, float4, int, bigint[], citus.shard_transfer_mode, boolean, name)\n    IS 'rebalance the shards of the given table across the worker nodes (including colocated shards of other tables)';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/remove_local_tables_from_metadata/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.remove_local_tables_from_metadata()\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$remove_local_tables_from_metadata$$;\nCOMMENT ON FUNCTION pg_catalog.remove_local_tables_from_metadata()\n\tIS 'undistribute citus local tables that are not chained with any reference tables via foreign keys';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/remove_local_tables_from_metadata/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.remove_local_tables_from_metadata()\n\tRETURNS void\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$remove_local_tables_from_metadata$$;\nCOMMENT ON FUNCTION pg_catalog.remove_local_tables_from_metadata()\n\tIS 'undistribute citus local tables that are not chained with any reference tables via foreign keys';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/repl_origin_helper/11.3-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_start_replication_origin_tracking()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_start_replication_origin_tracking$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_start_replication_origin_tracking()\n    IS 'To start replication origin tracking for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_stop_replication_origin_tracking()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_stop_replication_origin_tracking$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_stop_replication_origin_tracking()\n    IS 'To stop replication origin tracking for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_is_replication_origin_tracking_active()\nRETURNS boolean\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_is_replication_origin_tracking_active$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_is_replication_origin_tracking_active()\n    IS 'To check if replication origin tracking is active for skipping publishing of duplicated events during internal data movements for CDC';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/repl_origin_helper/13.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.start_replication_origin_tracking()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_start_replication_origin_tracking$$;\nCOMMENT ON FUNCTION citus_internal.start_replication_origin_tracking()\n    IS 'To start replication origin tracking for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION citus_internal.stop_replication_origin_tracking()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_stop_replication_origin_tracking$$;\nCOMMENT ON FUNCTION citus_internal.stop_replication_origin_tracking()\n    IS 'To stop replication origin tracking for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION citus_internal.is_replication_origin_tracking_active()\nRETURNS boolean\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_is_replication_origin_tracking_active$$;\nCOMMENT ON FUNCTION citus_internal.is_replication_origin_tracking_active()\n    IS 'To check if replication origin tracking is active for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_start_replication_origin_tracking()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_start_replication_origin_tracking$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_start_replication_origin_tracking()\n    IS 'To start replication origin tracking for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_stop_replication_origin_tracking()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_stop_replication_origin_tracking$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_stop_replication_origin_tracking()\n    IS 'To stop replication origin tracking for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_is_replication_origin_tracking_active()\nRETURNS boolean\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_is_replication_origin_tracking_active$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_is_replication_origin_tracking_active()\n    IS 'To check if replication origin tracking is active for skipping publishing of duplicated events during internal data movements for CDC';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/repl_origin_helper/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION citus_internal.start_replication_origin_tracking()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_start_replication_origin_tracking$$;\nCOMMENT ON FUNCTION citus_internal.start_replication_origin_tracking()\n    IS 'To start replication origin tracking for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION citus_internal.stop_replication_origin_tracking()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_stop_replication_origin_tracking$$;\nCOMMENT ON FUNCTION citus_internal.stop_replication_origin_tracking()\n    IS 'To stop replication origin tracking for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION citus_internal.is_replication_origin_tracking_active()\nRETURNS boolean\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_is_replication_origin_tracking_active$$;\nCOMMENT ON FUNCTION citus_internal.is_replication_origin_tracking_active()\n    IS 'To check if replication origin tracking is active for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_start_replication_origin_tracking()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_start_replication_origin_tracking$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_start_replication_origin_tracking()\n    IS 'To start replication origin tracking for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_stop_replication_origin_tracking()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_stop_replication_origin_tracking$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_stop_replication_origin_tracking()\n    IS 'To stop replication origin tracking for skipping publishing of duplicated events during internal data movements for CDC';\n\nCREATE OR REPLACE FUNCTION pg_catalog.citus_internal_is_replication_origin_tracking_active()\nRETURNS boolean\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$citus_internal_is_replication_origin_tracking_active$$;\nCOMMENT ON FUNCTION pg_catalog.citus_internal_is_replication_origin_tracking_active()\n    IS 'To check if replication origin tracking is active for skipping publishing of duplicated events during internal data movements for CDC';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/replicate_reference_tables/11.1-1.sql",
    "content": "DROP FUNCTION pg_catalog.replicate_reference_tables;\nCREATE FUNCTION pg_catalog.replicate_reference_tables(shard_transfer_mode citus.shard_transfer_mode default 'auto')\n  RETURNS VOID\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$replicate_reference_tables$$;\nCOMMENT ON FUNCTION pg_catalog.replicate_reference_tables(citus.shard_transfer_mode)\n  IS 'replicate reference tables to all nodes';\nREVOKE ALL ON FUNCTION pg_catalog.replicate_reference_tables(citus.shard_transfer_mode) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/replicate_reference_tables/9.3-2.sql",
    "content": "CREATE FUNCTION pg_catalog.replicate_reference_tables()\n  RETURNS VOID\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$replicate_reference_tables$$;\nCOMMENT ON FUNCTION pg_catalog.replicate_reference_tables()\n  IS 'replicate reference tables to all nodes';\nREVOKE ALL ON FUNCTION pg_catalog.replicate_reference_tables() FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/replicate_reference_tables/latest.sql",
    "content": "DROP FUNCTION pg_catalog.replicate_reference_tables;\nCREATE FUNCTION pg_catalog.replicate_reference_tables(shard_transfer_mode citus.shard_transfer_mode default 'auto')\n  RETURNS VOID\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$replicate_reference_tables$$;\nCOMMENT ON FUNCTION pg_catalog.replicate_reference_tables(citus.shard_transfer_mode)\n  IS 'replicate reference tables to all nodes';\nREVOKE ALL ON FUNCTION pg_catalog.replicate_reference_tables(citus.shard_transfer_mode) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/replicate_table_shards/9.0-1.sql",
    "content": "-- replicate_table_shards uses the shard rebalancer's C UDF functions to replicate\n-- under-replicated shards of the given table.\n--\nCREATE FUNCTION pg_catalog.replicate_table_shards(\n        relation regclass,\n        shard_replication_factor int default current_setting('citus.shard_replication_factor')::int,\n        max_shard_copies int default 1000000,\n        excluded_shard_list bigint[] default '{}',\n        shard_transfer_mode citus.shard_transfer_mode default 'auto')\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C STRICT;\nCOMMENT ON FUNCTION pg_catalog.replicate_table_shards(regclass, int, int, bigint[], citus.shard_transfer_mode)\n    IS 'replicates under replicated shards of the the given table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/replicate_table_shards/latest.sql",
    "content": "-- replicate_table_shards uses the shard rebalancer's C UDF functions to replicate\n-- under-replicated shards of the given table.\n--\nCREATE FUNCTION pg_catalog.replicate_table_shards(\n        relation regclass,\n        shard_replication_factor int default current_setting('citus.shard_replication_factor')::int,\n        max_shard_copies int default 1000000,\n        excluded_shard_list bigint[] default '{}',\n        shard_transfer_mode citus.shard_transfer_mode default 'auto')\n    RETURNS VOID\n    AS 'MODULE_PATHNAME'\n    LANGUAGE C STRICT;\nCOMMENT ON FUNCTION pg_catalog.replicate_table_shards(regclass, int, int, bigint[], citus.shard_transfer_mode)\n    IS 'replicates under replicated shards of the the given table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/run_command_on_all_nodes/11.0-1.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.run_command_on_all_nodes;\n\nCREATE FUNCTION pg_catalog.run_command_on_all_nodes(command text, parallel bool default true, give_warning_for_connection_errors bool default false,\n\t\t\t\t\t\t\t\t\t\t\t\t\tOUT nodeid int, OUT success bool, OUT result text)\n\tRETURNS SETOF record\n\tLANGUAGE plpgsql\n\tAS $function$\nDECLARE\n\tnodenames text[];\n\tports int[];\n\tcommands text[];\n\tcurrent_node_is_in_metadata boolean;\n\tcommand_result_of_current_node text;\nBEGIN\n\tWITH citus_nodes AS (\n\t\tSELECT * FROM pg_dist_node\n\t\tWHERE isactive = 't' AND nodecluster = current_setting('citus.cluster_name')\n\t\tAND (\n\t\t\t(current_setting('citus.use_secondary_nodes') = 'never' AND noderole = 'primary')\n\t\t\tOR\n\t\t\t(current_setting('citus.use_secondary_nodes') = 'always' AND noderole = 'secondary')\n\t\t)\n\t\tORDER BY nodename, nodeport\n\t)\n\tSELECT array_agg(citus_nodes.nodename), array_agg(citus_nodes.nodeport), array_agg(command)\n\tINTO nodenames, ports, commands\n\tFROM citus_nodes;\n\n\tSELECT count(*) > 0 FROM pg_dist_node\n\tWHERE isactive = 't'\n\tAND nodecluster = current_setting('citus.cluster_name')\n\tAND groupid IN (SELECT groupid FROM pg_dist_local_group)\n\tINTO current_node_is_in_metadata;\n\n\t-- This will happen when we call this function on coordinator and\n\t-- the coordinator is not added to the metadata.\n\t-- We'll manually add current node to the lists to actually run on all nodes.\n\t-- But when the coordinator is not added to metadata and this function\n\t-- is called from a worker node, this will not be enough and we'll\n\t-- not be able run on all nodes.\n\tIF NOT current_node_is_in_metadata THEN\n\t\tSELECT\n\t\tarray_append(nodenames, current_setting('citus.local_hostname')),\n\t\tarray_append(ports, current_setting('port')::int),\n\t\tarray_append(commands, command)\n\t\tINTO nodenames, ports, commands;\n\tEND IF;\n\n\tFOR nodeid, success, result IN\n\t\tSELECT coalesce(pg_dist_node.nodeid, 0) AS nodeid, mrow.success, mrow.result\n\t\tFROM master_run_on_worker(nodenames, ports, commands, parallel) mrow\n\t\tLEFT JOIN pg_dist_node ON mrow.node_name = pg_dist_node.nodename AND mrow.node_port = pg_dist_node.nodeport\n\tLOOP\n\t\tIF give_warning_for_connection_errors AND NOT success THEN\n\t\t\tRAISE WARNING 'Error on node with node id %: %', nodeid, result;\n\t\tEND IF;\n\t\tRETURN NEXT;\n\tEND LOOP;\nEND;\n$function$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/run_command_on_all_nodes/latest.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.run_command_on_all_nodes;\n\nCREATE FUNCTION pg_catalog.run_command_on_all_nodes(command text, parallel bool default true, give_warning_for_connection_errors bool default false,\n\t\t\t\t\t\t\t\t\t\t\t\t\tOUT nodeid int, OUT success bool, OUT result text)\n\tRETURNS SETOF record\n\tLANGUAGE plpgsql\n\tAS $function$\nDECLARE\n\tnodenames text[];\n\tports int[];\n\tcommands text[];\n\tcurrent_node_is_in_metadata boolean;\n\tcommand_result_of_current_node text;\nBEGIN\n\tWITH citus_nodes AS (\n\t\tSELECT * FROM pg_dist_node\n\t\tWHERE isactive = 't' AND nodecluster = current_setting('citus.cluster_name')\n\t\tAND (\n\t\t\t(current_setting('citus.use_secondary_nodes') = 'never' AND noderole = 'primary')\n\t\t\tOR\n\t\t\t(current_setting('citus.use_secondary_nodes') = 'always' AND noderole = 'secondary')\n\t\t)\n\t\tORDER BY nodename, nodeport\n\t)\n\tSELECT array_agg(citus_nodes.nodename), array_agg(citus_nodes.nodeport), array_agg(command)\n\tINTO nodenames, ports, commands\n\tFROM citus_nodes;\n\n\tSELECT count(*) > 0 FROM pg_dist_node\n\tWHERE isactive = 't'\n\tAND nodecluster = current_setting('citus.cluster_name')\n\tAND groupid IN (SELECT groupid FROM pg_dist_local_group)\n\tINTO current_node_is_in_metadata;\n\n\t-- This will happen when we call this function on coordinator and\n\t-- the coordinator is not added to the metadata.\n\t-- We'll manually add current node to the lists to actually run on all nodes.\n\t-- But when the coordinator is not added to metadata and this function\n\t-- is called from a worker node, this will not be enough and we'll\n\t-- not be able run on all nodes.\n\tIF NOT current_node_is_in_metadata THEN\n\t\tSELECT\n\t\tarray_append(nodenames, current_setting('citus.local_hostname')),\n\t\tarray_append(ports, current_setting('port')::int),\n\t\tarray_append(commands, command)\n\t\tINTO nodenames, ports, commands;\n\tEND IF;\n\n\tFOR nodeid, success, result IN\n\t\tSELECT coalesce(pg_dist_node.nodeid, 0) AS nodeid, mrow.success, mrow.result\n\t\tFROM master_run_on_worker(nodenames, ports, commands, parallel) mrow\n\t\tLEFT JOIN pg_dist_node ON mrow.node_name = pg_dist_node.nodename AND mrow.node_port = pg_dist_node.nodeport\n\tLOOP\n\t\tIF give_warning_for_connection_errors AND NOT success THEN\n\t\t\tRAISE WARNING 'Error on node with node id %: %', nodeid, result;\n\t\tEND IF;\n\t\tRETURN NEXT;\n\tEND LOOP;\nEND;\n$function$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/run_command_on_coordinator/11.0-2.sql",
    "content": "-- run_command_on_coordinator tries to closely follow the semantics of run_command_on_all_nodes,\n-- but only runs the command on the coordinator\nCREATE FUNCTION pg_catalog.run_command_on_coordinator(command text, give_warning_for_connection_errors bool default false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  OUT nodeid int, OUT success bool, OUT result text)\n\tRETURNS SETOF record\n\tLANGUAGE plpgsql\n\tAS $function$\nDECLARE\n\tnodenames text[];\n\tports int[];\n\tcommands text[];\n\tcoordinator_is_in_metadata boolean;\n\tparallel boolean := false;\nBEGIN\n\tWITH citus_nodes AS (\n\t\tSELECT * FROM pg_dist_node\n\t\tWHERE isactive AND nodecluster = current_setting('citus.cluster_name') AND groupid = 0\n\t\tAND (\n\t\t\t(current_setting('citus.use_secondary_nodes') = 'never' AND noderole = 'primary')\n\t\t\tOR\n\t\t\t(current_setting('citus.use_secondary_nodes') = 'always' AND noderole = 'secondary')\n\t\t)\n\t\tORDER BY nodename, nodeport\n\t)\n\tSELECT array_agg(citus_nodes.nodename), array_agg(citus_nodes.nodeport), array_agg(command), count(*) > 0\n\tFROM citus_nodes\n\tINTO nodenames, ports, commands, coordinator_is_in_metadata;\n\n\tIF NOT coordinator_is_in_metadata THEN\n\t\t-- This will happen when we call this function on coordinator and\n\t\t-- the coordinator is not added to the metadata.\n\t\t-- We'll manually add current node to the lists to actually run on all nodes.\n\t\t-- But when the coordinator is not added to metadata and this function\n\t\t-- is called from a worker node, this will not be enough and we'll\n\t\t-- not be able run on all nodes.\n\t\tIF citus_is_coordinator() THEN\n\t\t\tSELECT\n\t\t\t\tarray_append(nodenames, current_setting('citus.local_hostname')),\n\t\t\t\tarray_append(ports, current_setting('port')::int),\n\t\t\t\tarray_append(commands, command)\n\t\t\tINTO nodenames, ports, commands;\n\t\tELSE\n\t\t\tRAISE EXCEPTION 'the coordinator is not added to the metadata'\n\t\t\tUSING HINT = 'Add the node as a coordinator by using: SELECT citus_set_coordinator_host(''<hostname>'')';\n\t\tEND IF;\n\tEND IF;\n\n\tFOR nodeid, success, result IN\n\t\tSELECT coalesce(pg_dist_node.nodeid, 0) AS nodeid, mrow.success, mrow.result\n\t\tFROM master_run_on_worker(nodenames, ports, commands, parallel) mrow\n\t\tLEFT JOIN pg_dist_node ON mrow.node_name = pg_dist_node.nodename AND mrow.node_port = pg_dist_node.nodeport\n\tLOOP\n\t\tIF give_warning_for_connection_errors AND NOT success THEN\n\t\t\tRAISE WARNING 'Error on node with node id %: %', nodeid, result;\n\t\tEND IF;\n\t\tRETURN NEXT;\n\tEND LOOP;\nEND;\n$function$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/run_command_on_coordinator/latest.sql",
    "content": "-- run_command_on_coordinator tries to closely follow the semantics of run_command_on_all_nodes,\n-- but only runs the command on the coordinator\nCREATE FUNCTION pg_catalog.run_command_on_coordinator(command text, give_warning_for_connection_errors bool default false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  OUT nodeid int, OUT success bool, OUT result text)\n\tRETURNS SETOF record\n\tLANGUAGE plpgsql\n\tAS $function$\nDECLARE\n\tnodenames text[];\n\tports int[];\n\tcommands text[];\n\tcoordinator_is_in_metadata boolean;\n\tparallel boolean := false;\nBEGIN\n\tWITH citus_nodes AS (\n\t\tSELECT * FROM pg_dist_node\n\t\tWHERE isactive AND nodecluster = current_setting('citus.cluster_name') AND groupid = 0\n\t\tAND (\n\t\t\t(current_setting('citus.use_secondary_nodes') = 'never' AND noderole = 'primary')\n\t\t\tOR\n\t\t\t(current_setting('citus.use_secondary_nodes') = 'always' AND noderole = 'secondary')\n\t\t)\n\t\tORDER BY nodename, nodeport\n\t)\n\tSELECT array_agg(citus_nodes.nodename), array_agg(citus_nodes.nodeport), array_agg(command), count(*) > 0\n\tFROM citus_nodes\n\tINTO nodenames, ports, commands, coordinator_is_in_metadata;\n\n\tIF NOT coordinator_is_in_metadata THEN\n\t\t-- This will happen when we call this function on coordinator and\n\t\t-- the coordinator is not added to the metadata.\n\t\t-- We'll manually add current node to the lists to actually run on all nodes.\n\t\t-- But when the coordinator is not added to metadata and this function\n\t\t-- is called from a worker node, this will not be enough and we'll\n\t\t-- not be able run on all nodes.\n\t\tIF citus_is_coordinator() THEN\n\t\t\tSELECT\n\t\t\t\tarray_append(nodenames, current_setting('citus.local_hostname')),\n\t\t\t\tarray_append(ports, current_setting('port')::int),\n\t\t\t\tarray_append(commands, command)\n\t\t\tINTO nodenames, ports, commands;\n\t\tELSE\n\t\t\tRAISE EXCEPTION 'the coordinator is not added to the metadata'\n\t\t\tUSING HINT = 'Add the node as a coordinator by using: SELECT citus_set_coordinator_host(''<hostname>'')';\n\t\tEND IF;\n\tEND IF;\n\n\tFOR nodeid, success, result IN\n\t\tSELECT coalesce(pg_dist_node.nodeid, 0) AS nodeid, mrow.success, mrow.result\n\t\tFROM master_run_on_worker(nodenames, ports, commands, parallel) mrow\n\t\tLEFT JOIN pg_dist_node ON mrow.node_name = pg_dist_node.nodename AND mrow.node_port = pg_dist_node.nodeport\n\tLOOP\n\t\tIF give_warning_for_connection_errors AND NOT success THEN\n\t\t\tRAISE WARNING 'Error on node with node id %: %', nodeid, result;\n\t\tEND IF;\n\t\tRETURN NEXT;\n\tEND LOOP;\nEND;\n$function$;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/shard_name/13.1-1.sql",
    "content": "-- skip_qualify_public is set to true by default just for backward compatibility\nDROP FUNCTION pg_catalog.shard_name(object_name regclass, shard_id bigint);\nCREATE FUNCTION pg_catalog.shard_name(object_name regclass, shard_id bigint, skip_qualify_public boolean DEFAULT true)\n    RETURNS text\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$shard_name$$;\nCOMMENT ON FUNCTION pg_catalog.shard_name(object_name regclass, shard_id bigint, skip_qualify_public boolean)\n    IS 'returns schema-qualified, shard-extended identifier of object name';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/shard_name/latest.sql",
    "content": "-- skip_qualify_public is set to true by default just for backward compatibility\nDROP FUNCTION pg_catalog.shard_name(object_name regclass, shard_id bigint);\nCREATE FUNCTION pg_catalog.shard_name(object_name regclass, shard_id bigint, skip_qualify_public boolean DEFAULT true)\n    RETURNS text\n    LANGUAGE C STABLE STRICT\n    AS 'MODULE_PATHNAME', $$shard_name$$;\nCOMMENT ON FUNCTION pg_catalog.shard_name(object_name regclass, shard_id bigint, skip_qualify_public boolean)\n    IS 'returns schema-qualified, shard-extended identifier of object name';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/start_metadata_sync_to_all_nodes/11.0-2.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.start_metadata_sync_to_all_nodes()\n RETURNS bool\n LANGUAGE C\n STRICT\nAS 'MODULE_PATHNAME', $$start_metadata_sync_to_all_nodes$$;\nCOMMENT ON FUNCTION pg_catalog.start_metadata_sync_to_all_nodes()\n IS 'sync metadata to all active primary nodes';\n\nREVOKE ALL ON FUNCTION pg_catalog.start_metadata_sync_to_all_nodes() FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/start_metadata_sync_to_all_nodes/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.start_metadata_sync_to_all_nodes()\n RETURNS bool\n LANGUAGE C\n STRICT\nAS 'MODULE_PATHNAME', $$start_metadata_sync_to_all_nodes$$;\nCOMMENT ON FUNCTION pg_catalog.start_metadata_sync_to_all_nodes()\n IS 'sync metadata to all active primary nodes';\n\nREVOKE ALL ON FUNCTION pg_catalog.start_metadata_sync_to_all_nodes() FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/stop_metadata_sync_to_node/10.2-1.sql",
    "content": "CREATE FUNCTION pg_catalog.stop_metadata_sync_to_node(nodename text, nodeport integer, clear_metadata bool DEFAULT true)\n\tRETURNS VOID\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$stop_metadata_sync_to_node$$;\nCOMMENT ON FUNCTION pg_catalog.stop_metadata_sync_to_node(nodename text, nodeport integer, clear_metadata bool)\n    IS 'stop metadata sync to node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/stop_metadata_sync_to_node/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.stop_metadata_sync_to_node(nodename text, nodeport integer, clear_metadata bool DEFAULT true)\n\tRETURNS VOID\n\tLANGUAGE C STRICT\n\tAS 'MODULE_PATHNAME', $$stop_metadata_sync_to_node$$;\nCOMMENT ON FUNCTION pg_catalog.stop_metadata_sync_to_node(nodename text, nodeport integer, clear_metadata bool)\n    IS 'stop metadata sync to node';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/time_partition_range/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.time_partition_range(\n    table_name regclass,\n    OUT lower_bound text,\n    OUT upper_bound text)\nRETURNS record\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$time_partition_range$$;\n\nCOMMENT ON FUNCTION pg_catalog.time_partition_range(regclass)\nIS 'returns the start and end of partition boundaries';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/time_partition_range/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.time_partition_range(\n    table_name regclass,\n    OUT lower_bound text,\n    OUT upper_bound text)\nRETURNS record\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$time_partition_range$$;\n\nCOMMENT ON FUNCTION pg_catalog.time_partition_range(regclass)\nIS 'returns the start and end of partition boundaries';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/time_partitions/10.0-1.sql",
    "content": "CREATE VIEW citus.time_partitions AS\nSELECT partrelid AS parent_table, attname AS partition_column, relid AS partition, lower_bound AS from_value, upper_bound AS to_value, amname AS access_method\nFROM (\n  SELECT partrelid::regclass AS partrelid, attname, c.oid::regclass AS relid, lower_bound, upper_bound, amname\n  FROM pg_class c\n  JOIN pg_inherits i ON (c.oid = inhrelid)\n  JOIN pg_partitioned_table p ON (inhparent = partrelid)\n  JOIN pg_attribute a ON (partrelid = attrelid)\n  JOIN pg_type t ON (atttypid = t.oid)\n  JOIN pg_namespace tn ON (t.typnamespace = tn.oid)\n  LEFT JOIN pg_am am ON (c.relam = am.oid),\n  pg_catalog.time_partition_range(c.oid)\n  WHERE c.relpartbound IS NOT NULL AND p.partstrat = 'r' AND p.partnatts = 1\n  AND a.attnum = ANY(partattrs::int2[])\n) partitions\nORDER BY partrelid::text, lower_bound;\n\nALTER VIEW citus.time_partitions SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.time_partitions TO public;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/time_partitions/latest.sql",
    "content": "CREATE VIEW citus.time_partitions AS\nSELECT partrelid AS parent_table, attname AS partition_column, relid AS partition, lower_bound AS from_value, upper_bound AS to_value, amname AS access_method\nFROM (\n  SELECT partrelid::regclass AS partrelid, attname, c.oid::regclass AS relid, lower_bound, upper_bound, amname\n  FROM pg_class c\n  JOIN pg_inherits i ON (c.oid = inhrelid)\n  JOIN pg_partitioned_table p ON (inhparent = partrelid)\n  JOIN pg_attribute a ON (partrelid = attrelid)\n  JOIN pg_type t ON (atttypid = t.oid)\n  JOIN pg_namespace tn ON (t.typnamespace = tn.oid)\n  LEFT JOIN pg_am am ON (c.relam = am.oid),\n  pg_catalog.time_partition_range(c.oid)\n  WHERE c.relpartbound IS NOT NULL AND p.partstrat = 'r' AND p.partnatts = 1\n  AND a.attnum = ANY(partattrs::int2[])\n) partitions\nORDER BY partrelid::text, lower_bound;\n\nALTER VIEW citus.time_partitions SET SCHEMA pg_catalog;\nGRANT SELECT ON pg_catalog.time_partitions TO public;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/truncate_local_data_after_distributing_table/9.3-2.sql",
    "content": "CREATE OR REPLACE FUNCTION truncate_local_data_after_distributing_table(function_name regclass)\n  RETURNS void\n  LANGUAGE C CALLED ON NULL INPUT\n  AS 'MODULE_PATHNAME', $$truncate_local_data_after_distributing_table$$;\n\nCOMMENT ON FUNCTION truncate_local_data_after_distributing_table(function_name regclass)\n  IS 'truncates local records of a distributed table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/truncate_local_data_after_distributing_table/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION truncate_local_data_after_distributing_table(function_name regclass)\n  RETURNS void\n  LANGUAGE C CALLED ON NULL INPUT\n  AS 'MODULE_PATHNAME', $$truncate_local_data_after_distributing_table$$;\n\nCOMMENT ON FUNCTION truncate_local_data_after_distributing_table(function_name regclass)\n  IS 'truncates local records of a distributed table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/undistribute_table/10.0-1.sql",
    "content": "DROP FUNCTION pg_catalog.undistribute_table(regclass);\nCREATE OR REPLACE FUNCTION pg_catalog.undistribute_table(\n    table_name regclass, cascade_via_foreign_keys boolean default false)\n    RETURNS VOID\n    LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$undistribute_table$$;\n\nCOMMENT ON FUNCTION pg_catalog.undistribute_table(\n    table_name regclass, cascade_via_foreign_keys boolean)\n    IS 'undistributes a distributed table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/undistribute_table/9.5-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.undistribute_table(\n    table_name regclass)\n    RETURNS VOID\n    LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$undistribute_table$$;\n\nCOMMENT ON FUNCTION pg_catalog.undistribute_table(\n    table_name regclass)\n    IS 'undistributes a distributed table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/undistribute_table/latest.sql",
    "content": "DROP FUNCTION pg_catalog.undistribute_table(regclass);\nCREATE OR REPLACE FUNCTION pg_catalog.undistribute_table(\n    table_name regclass, cascade_via_foreign_keys boolean default false)\n    RETURNS VOID\n    LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$undistribute_table$$;\n\nCOMMENT ON FUNCTION pg_catalog.undistribute_table(\n    table_name regclass, cascade_via_foreign_keys boolean)\n    IS 'undistributes a distributed table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/update_distributed_table_colocation/9.3-2.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.update_distributed_table_colocation(table_name regclass, colocate_with text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$update_distributed_table_colocation$$;\nCOMMENT ON FUNCTION pg_catalog.update_distributed_table_colocation(table_name regclass, colocate_with text)\n    IS 'updates colocation of a table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/update_distributed_table_colocation/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.update_distributed_table_colocation(table_name regclass, colocate_with text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$update_distributed_table_colocation$$;\nCOMMENT ON FUNCTION pg_catalog.update_distributed_table_colocation(table_name regclass, colocate_with text)\n    IS 'updates colocation of a table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/upgrade_to_reference_table/8.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.upgrade_to_reference_table(table_name regclass)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$upgrade_to_reference_table$$;\nCOMMENT ON FUNCTION pg_catalog.upgrade_to_reference_table(table_name regclass)\n    IS 'upgrades an existing broadcast table to a reference table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/upgrade_to_reference_table/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.upgrade_to_reference_table(table_name regclass)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$upgrade_to_reference_table$$;\nCOMMENT ON FUNCTION pg_catalog.upgrade_to_reference_table(table_name regclass)\n    IS 'upgrades an existing broadcast table to a reference table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_adjust_identity_column_seq_ranges/11.2-2.sql",
    "content": "-- Since we backported the UDF below from version 11.3, the definition is the same\n#include \"11.3-1.sql\"\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_adjust_identity_column_seq_ranges/11.3-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_adjust_identity_column_seq_ranges(regclass)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_adjust_identity_column_seq_ranges$$;\nCOMMENT ON FUNCTION pg_catalog.worker_adjust_identity_column_seq_ranges(regclass)\n    IS 'modify identity column seq ranges to produce globally unique values';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_adjust_identity_column_seq_ranges/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_adjust_identity_column_seq_ranges(regclass)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_adjust_identity_column_seq_ranges$$;\nCOMMENT ON FUNCTION pg_catalog.worker_adjust_identity_column_seq_ranges(regclass)\n    IS 'modify identity column seq ranges to produce globally unique values';\n\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_binary_partial_agg/14.0-1.sql",
    "content": "\n-- similar to worker_partial_agg but returns binary representation of the state\nCREATE AGGREGATE pg_catalog.worker_binary_partial_agg(oid, anyelement) (\n    STYPE = internal,\n    SFUNC = pg_catalog.worker_partial_agg_sfunc,\n    FINALFUNC = pg_catalog.worker_binary_partial_agg_ffunc\n);\nCOMMENT ON AGGREGATE pg_catalog.worker_binary_partial_agg(oid, anyelement)\n    IS 'support aggregate for implementing partial binary aggregation on workers';\nREVOKE ALL ON FUNCTION pg_catalog.worker_binary_partial_agg FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.worker_binary_partial_agg TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_binary_partial_agg/latest.sql",
    "content": "\n-- similar to worker_partial_agg but returns binary representation of the state\nCREATE AGGREGATE pg_catalog.worker_binary_partial_agg(oid, anyelement) (\n    STYPE = internal,\n    SFUNC = pg_catalog.worker_partial_agg_sfunc,\n    FINALFUNC = pg_catalog.worker_binary_partial_agg_ffunc\n);\nCOMMENT ON AGGREGATE pg_catalog.worker_binary_partial_agg(oid, anyelement)\n    IS 'support aggregate for implementing partial binary aggregation on workers';\nREVOKE ALL ON FUNCTION pg_catalog.worker_binary_partial_agg FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.worker_binary_partial_agg TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_binary_partial_agg_ffunc/14.0-1.sql",
    "content": "\nCREATE FUNCTION pg_catalog.worker_binary_partial_agg_ffunc(internal)\nRETURNS bytea\nAS 'MODULE_PATHNAME'\nLANGUAGE C PARALLEL SAFE;\nCOMMENT ON FUNCTION pg_catalog.worker_binary_partial_agg_ffunc(internal)\n    IS 'finalizer for worker_binary_partial_agg';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_binary_partial_agg_ffunc FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.worker_binary_partial_agg_ffunc TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_binary_partial_agg_ffunc/latest.sql",
    "content": "\nCREATE FUNCTION pg_catalog.worker_binary_partial_agg_ffunc(internal)\nRETURNS bytea\nAS 'MODULE_PATHNAME'\nLANGUAGE C PARALLEL SAFE;\nCOMMENT ON FUNCTION pg_catalog.worker_binary_partial_agg_ffunc(internal)\n    IS 'finalizer for worker_binary_partial_agg';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_binary_partial_agg_ffunc FROM PUBLIC;\nGRANT EXECUTE ON FUNCTION pg_catalog.worker_binary_partial_agg_ffunc TO PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_change_sequence_dependency/10.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_change_sequence_dependency(\n    sequence regclass,\n    source_table regclass,\n    target_table regclass)\n    RETURNS VOID\n    LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_change_sequence_dependency$$;\n\nCOMMENT ON FUNCTION pg_catalog.worker_change_sequence_dependency(\n    sequence regclass,\n    source_table regclass,\n    target_table regclass)\n    IS 'changes sequence''s dependency from source table to target table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_change_sequence_dependency/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_change_sequence_dependency(\n    sequence regclass,\n    source_table regclass,\n    target_table regclass)\n    RETURNS VOID\n    LANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_change_sequence_dependency$$;\n\nCOMMENT ON FUNCTION pg_catalog.worker_change_sequence_dependency(\n    sequence regclass,\n    source_table regclass,\n    target_table regclass)\n    IS 'changes sequence''s dependency from source table to target table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_copy_table_to_node/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_copy_table_to_node(\n    source_table regclass,\n    target_node_id integer)\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_copy_table_to_node$$;\nCOMMENT ON FUNCTION pg_catalog.worker_copy_table_to_node(regclass, integer)\n    IS 'Perform copy of a shard';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_copy_table_to_node/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_copy_table_to_node(\n    source_table regclass,\n    target_node_id integer)\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_copy_table_to_node$$;\nCOMMENT ON FUNCTION pg_catalog.worker_copy_table_to_node(regclass, integer)\n    IS 'Perform copy of a shard';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_create_or_alter_role/9.3-2.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_create_or_alter_role(\n    role_name text,\n    create_role_utility_query text,\n    alter_role_utility_query text)\n    RETURNS BOOL\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$worker_create_or_alter_role$$;\n\nCOMMENT ON FUNCTION pg_catalog.worker_create_or_alter_role(\n    role_name text,\n    create_role_utility_query text,\n    alter_role_utility_query text)\n    IS 'runs the create role query, if the role doesn''t exists, runs the alter role query if it does';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_create_or_alter_role/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_create_or_alter_role(\n    role_name text,\n    create_role_utility_query text,\n    alter_role_utility_query text)\n    RETURNS BOOL\n    LANGUAGE C\nAS 'MODULE_PATHNAME', $$worker_create_or_alter_role$$;\n\nCOMMENT ON FUNCTION pg_catalog.worker_create_or_alter_role(\n    role_name text,\n    create_role_utility_query text,\n    alter_role_utility_query text)\n    IS 'runs the create role query, if the role doesn''t exists, runs the alter role query if it does';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_create_or_replace_object/11.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_create_or_replace_object(statement text)\n  RETURNS bool\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$worker_create_or_replace_object$$;\n\nCOMMENT ON FUNCTION pg_catalog.worker_create_or_replace_object(statement text)\n    IS 'takes a sql CREATE statement, before executing the create it will check if an object with that name already exists and safely replaces that named object with the new object';\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_create_or_replace_object(statements text[])\n  RETURNS bool\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$worker_create_or_replace_object_array$$;\n\nCOMMENT ON FUNCTION pg_catalog.worker_create_or_replace_object(statements text[])\n    IS 'takes an array of sql statements, before executing these it will check if the object already exists in that exact state otherwise replaces that named object with the new object';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_create_or_replace_object/9.0-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_create_or_replace_object(statement text)\n  RETURNS bool\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$worker_create_or_replace_object$$;\nCOMMENT ON FUNCTION pg_catalog.worker_create_or_replace_object(statement text)\n    IS 'takes a sql CREATE statement, before executing the create it will check if an object with that name already exists and safely replaces that named object with the new object';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_create_or_replace_object/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_create_or_replace_object(statement text)\n  RETURNS bool\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$worker_create_or_replace_object$$;\n\nCOMMENT ON FUNCTION pg_catalog.worker_create_or_replace_object(statement text)\n    IS 'takes a sql CREATE statement, before executing the create it will check if an object with that name already exists and safely replaces that named object with the new object';\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_create_or_replace_object(statements text[])\n  RETURNS bool\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$worker_create_or_replace_object_array$$;\n\nCOMMENT ON FUNCTION pg_catalog.worker_create_or_replace_object(statements text[])\n    IS 'takes an array of sql statements, before executing these it will check if the object already exists in that exact state otherwise replaces that named object with the new object';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_create_schema/9.1-1.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_create_schema(bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_create_schema$$;\nCOMMENT ON FUNCTION pg_catalog.worker_create_schema(bigint)\n    IS 'create schema in remote node';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_create_schema(bigint) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_create_schema/9.2-2.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.worker_create_schema(jobid bigint);\n\nCREATE FUNCTION pg_catalog.worker_create_schema(jobid bigint, username text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_create_schema$$;\nCOMMENT ON FUNCTION pg_catalog.worker_create_schema(bigint, text)\n    IS 'create schema in remote node';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_create_schema(bigint, text) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_create_schema/latest.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.worker_create_schema(jobid bigint);\n\nCREATE FUNCTION pg_catalog.worker_create_schema(jobid bigint, username text)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_create_schema$$;\nCOMMENT ON FUNCTION pg_catalog.worker_create_schema(bigint, text)\n    IS 'create schema in remote node';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_create_schema(bigint, text) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_drop_all_shell_tables/11.3-1.sql",
    "content": " -- During metadata sync, when we send many ddls over single transaction, worker node can error due\n-- to reaching at max allocation block size for invalidation messages. To find a workaround for the problem,\n-- we added nontransactional metadata sync mode where we create many transaction while dropping shell tables\n-- via https://github.com/citusdata/citus/pull/6728.\nCREATE OR REPLACE PROCEDURE pg_catalog.worker_drop_all_shell_tables(singleTransaction bool DEFAULT true)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    table_name text;\nBEGIN\n    -- drop shell tables within single or multiple transactions according to the flag singleTransaction\n    FOR table_name IN SELECT logicalrelid::regclass::text FROM pg_dist_partition\n    LOOP\n        PERFORM pg_catalog.worker_drop_shell_table(table_name);\n        IF not singleTransaction THEN\n            COMMIT;\n        END IF;\n    END LOOP;\nEND;\n$$;\nCOMMENT ON PROCEDURE worker_drop_all_shell_tables(singleTransaction bool)\n    IS 'drop all distributed tables only without the metadata within single transaction or '\n        'multiple transaction specified by the flag singleTransaction';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_drop_all_shell_tables/latest.sql",
    "content": " -- During metadata sync, when we send many ddls over single transaction, worker node can error due\n-- to reaching at max allocation block size for invalidation messages. To find a workaround for the problem,\n-- we added nontransactional metadata sync mode where we create many transaction while dropping shell tables\n-- via https://github.com/citusdata/citus/pull/6728.\nCREATE OR REPLACE PROCEDURE pg_catalog.worker_drop_all_shell_tables(singleTransaction bool DEFAULT true)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    table_name text;\nBEGIN\n    -- drop shell tables within single or multiple transactions according to the flag singleTransaction\n    FOR table_name IN SELECT logicalrelid::regclass::text FROM pg_dist_partition\n    LOOP\n        PERFORM pg_catalog.worker_drop_shell_table(table_name);\n        IF not singleTransaction THEN\n            COMMIT;\n        END IF;\n    END LOOP;\nEND;\n$$;\nCOMMENT ON PROCEDURE worker_drop_all_shell_tables(singleTransaction bool)\n    IS 'drop all distributed tables only without the metadata within single transaction or '\n        'multiple transaction specified by the flag singleTransaction';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_drop_sequence_dependency/11.0-1.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.worker_drop_sequence_dependency(table_name text);\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_drop_sequence_dependency(table_name text)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_drop_sequence_dependency$$;\nCOMMENT ON FUNCTION pg_catalog.worker_drop_sequence_dependency(table_name text)\n    IS 'drop the Citus tables sequence dependency';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_drop_sequence_dependency/latest.sql",
    "content": "DROP FUNCTION IF EXISTS pg_catalog.worker_drop_sequence_dependency(table_name text);\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_drop_sequence_dependency(table_name text)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_drop_sequence_dependency$$;\nCOMMENT ON FUNCTION pg_catalog.worker_drop_sequence_dependency(table_name text)\n    IS 'drop the Citus tables sequence dependency';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_drop_shell_table/11.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_drop_shell_table(table_name text)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_drop_shell_table$$;\n\nCOMMENT ON FUNCTION worker_drop_shell_table(table_name text)\n    IS 'drop the distributed table only without the metadata';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_drop_shell_table/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_drop_shell_table(table_name text)\n    RETURNS VOID\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_drop_shell_table$$;\n\nCOMMENT ON FUNCTION worker_drop_shell_table(table_name text)\n    IS 'drop the distributed table only without the metadata';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_fix_partition_shard_index_names/10.2-4.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_fix_partition_shard_index_names(parent_shard_index regclass,\n                                                                  partition_shard text,\n                                                                  new_partition_shard_index_name text)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$worker_fix_partition_shard_index_names$$;\nCOMMENT ON FUNCTION pg_catalog.worker_fix_partition_shard_index_names(parent_shard_index regclass,\n                                                                      partition_shard text,\n                                                                      new_partition_shard_index_name text)\n  IS 'fix the name of the index on given partition shard that is child of given parent_index';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_fix_partition_shard_index_names/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_fix_partition_shard_index_names(parent_shard_index regclass,\n                                                                  partition_shard text,\n                                                                  new_partition_shard_index_name text)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$worker_fix_partition_shard_index_names$$;\nCOMMENT ON FUNCTION pg_catalog.worker_fix_partition_shard_index_names(parent_shard_index regclass,\n                                                                      partition_shard text,\n                                                                      new_partition_shard_index_name text)\n  IS 'fix the name of the index on given partition shard that is child of given parent_index';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_fix_pre_citus10_partitioned_table_constraint_names/10.0-1.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_fix_pre_citus10_partitioned_table_constraint_names(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardid bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconstraint_name text)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$worker_fix_pre_citus10_partitioned_table_constraint_names$$;\nCOMMENT ON FUNCTION pg_catalog.worker_fix_pre_citus10_partitioned_table_constraint_names(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardid bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconstraint_name text)\n  IS 'fix constraint names on partition shards on worker nodes';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_fix_pre_citus10_partitioned_table_constraint_names/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_fix_pre_citus10_partitioned_table_constraint_names(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardid bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconstraint_name text)\n  RETURNS void\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', $$worker_fix_pre_citus10_partitioned_table_constraint_names$$;\nCOMMENT ON FUNCTION pg_catalog.worker_fix_pre_citus10_partitioned_table_constraint_names(table_name regclass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardid bigint,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconstraint_name text)\n  IS 'fix constraint names on partition shards on worker nodes';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_last_saved_explain_analyze/13.2-1.sql",
    "content": "\nDROP FUNCTION pg_catalog.worker_last_saved_explain_analyze();\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_last_saved_explain_analyze()\n    RETURNS TABLE(explain_analyze_output TEXT, execution_duration DOUBLE PRECISION,\n        execution_ntuples DOUBLE PRECISION, execution_nloops DOUBLE PRECISION)\n    LANGUAGE C STRICT\n    AS 'citus';\nCOMMENT ON FUNCTION pg_catalog.worker_last_saved_explain_analyze() IS\n    'Returns the saved explain analyze output for the last run query';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_last_saved_explain_analyze/9.4-1.sql",
    "content": "\nCREATE OR REPLACE FUNCTION pg_catalog.worker_last_saved_explain_analyze()\n    RETURNS TABLE(explain_analyze_output TEXT, execution_duration DOUBLE PRECISION)\n    LANGUAGE C STRICT\n    AS 'citus';\nCOMMENT ON FUNCTION pg_catalog.worker_last_saved_explain_analyze() IS\n    'Returns the saved explain analyze output for the last run query';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_last_saved_explain_analyze/latest.sql",
    "content": "\nDROP FUNCTION pg_catalog.worker_last_saved_explain_analyze();\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_last_saved_explain_analyze()\n    RETURNS TABLE(explain_analyze_output TEXT, execution_duration DOUBLE PRECISION,\n        execution_ntuples DOUBLE PRECISION, execution_nloops DOUBLE PRECISION)\n    LANGUAGE C STRICT\n    AS 'citus';\nCOMMENT ON FUNCTION pg_catalog.worker_last_saved_explain_analyze() IS\n    'Returns the saved explain analyze output for the last run query';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_nextval/10.2-1.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_nextval(sequence regclass)\n    RETURNS int\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_nextval$$;\nCOMMENT ON FUNCTION pg_catalog.worker_nextval(regclass)\n    IS 'calculates nextval() for column defaults of type int or smallint';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_nextval/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_nextval(sequence regclass)\n    RETURNS int\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_nextval$$;\nCOMMENT ON FUNCTION pg_catalog.worker_nextval(regclass)\n    IS 'calculates nextval() for column defaults of type int or smallint';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_partition_query_result/11.0-1.sql",
    "content": "DROP FUNCTION pg_catalog.worker_partition_query_result(text, text, int, citus.distribution_type, text[], text[], boolean);\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_partition_query_result(\n    result_prefix text,\n    query text,\n    partition_column_index int,\n    partition_method citus.distribution_type,\n    partition_min_values text[],\n    partition_max_values text[],\n    binary_copy boolean,\n    allow_null_partition_column boolean DEFAULT false,\n    generate_empty_results boolean DEFAULT false,\n    OUT partition_index int,\n    OUT rows_written bigint,\n    OUT bytes_written bigint)\nRETURNS SETOF record\nLANGUAGE C STRICT VOLATILE\nAS 'MODULE_PATHNAME', $$worker_partition_query_result$$;\nCOMMENT ON FUNCTION pg_catalog.worker_partition_query_result(text, text, int, citus.distribution_type, text[], text[], boolean, boolean, boolean)\nIS 'execute a query and partitions its results in set of local result files';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_partition_query_result/9.2-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_partition_query_result(\n    result_prefix text,\n    query text,\n    partition_column_index int,\n    partition_method citus.distribution_type,\n    partition_min_values text[],\n    partition_max_values text[],\n    binaryCopy boolean,\n    OUT partition_index int,\n    OUT rows_written bigint,\n    OUT bytes_written bigint)\nRETURNS SETOF record\nLANGUAGE C STRICT VOLATILE\nAS 'MODULE_PATHNAME', $$worker_partition_query_result$$;\nCOMMENT ON FUNCTION pg_catalog.worker_partition_query_result(text, text, int, citus.distribution_type, text[], text[], boolean)\nIS 'execute a query and partitions its results in set of local result files';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_partition_query_result/latest.sql",
    "content": "DROP FUNCTION pg_catalog.worker_partition_query_result(text, text, int, citus.distribution_type, text[], text[], boolean);\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_partition_query_result(\n    result_prefix text,\n    query text,\n    partition_column_index int,\n    partition_method citus.distribution_type,\n    partition_min_values text[],\n    partition_max_values text[],\n    binary_copy boolean,\n    allow_null_partition_column boolean DEFAULT false,\n    generate_empty_results boolean DEFAULT false,\n    OUT partition_index int,\n    OUT rows_written bigint,\n    OUT bytes_written bigint)\nRETURNS SETOF record\nLANGUAGE C STRICT VOLATILE\nAS 'MODULE_PATHNAME', $$worker_partition_query_result$$;\nCOMMENT ON FUNCTION pg_catalog.worker_partition_query_result(text, text, int, citus.distribution_type, text[], text[], boolean, boolean, boolean)\nIS 'execute a query and partitions its results in set of local result files';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_partitioned_relation_size/10.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION worker_partitioned_relation_size(relation regclass)\nRETURNS bigint AS $$\n    SELECT sum(pg_relation_size(relid))::bigint\n\t\t\t\t\t  FROM (SELECT relid from pg_partition_tree(relation)) partition_tree;\n$$ LANGUAGE SQL;\nCOMMENT ON FUNCTION pg_catalog.worker_partitioned_relation_size(regclass)\n    IS 'Calculates and returns the size of a partitioned relation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_partitioned_relation_size/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION worker_partitioned_relation_size(relation regclass)\nRETURNS bigint AS $$\n    SELECT sum(pg_relation_size(relid))::bigint\n\t\t\t\t\t  FROM (SELECT relid from pg_partition_tree(relation)) partition_tree;\n$$ LANGUAGE SQL;\nCOMMENT ON FUNCTION pg_catalog.worker_partitioned_relation_size(regclass)\n    IS 'Calculates and returns the size of a partitioned relation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_partitioned_relation_total_size/10.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION worker_partitioned_relation_total_size(relation regclass)\nRETURNS bigint AS $$\n    SELECT sum(pg_total_relation_size(relid))::bigint\n\t\t\t\t\t  FROM (SELECT relid from pg_partition_tree(relation)) partition_tree;\n$$ LANGUAGE SQL;\nCOMMENT ON FUNCTION worker_partitioned_relation_total_size(regclass)\n\tIS 'Calculates and returns the total size of a partitioned relation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_partitioned_relation_total_size/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION worker_partitioned_relation_total_size(relation regclass)\nRETURNS bigint AS $$\n    SELECT sum(pg_total_relation_size(relid))::bigint\n\t\t\t\t\t  FROM (SELECT relid from pg_partition_tree(relation)) partition_tree;\n$$ LANGUAGE SQL;\nCOMMENT ON FUNCTION worker_partitioned_relation_total_size(regclass)\n\tIS 'Calculates and returns the total size of a partitioned relation';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_partitioned_table_size/10.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION worker_partitioned_table_size(relation regclass)\nRETURNS bigint AS $$\n    SELECT sum(pg_table_size(relid))::bigint\n\t\t\t\t\t  FROM (SELECT relid from pg_partition_tree(relation)) partition_tree;\n$$ LANGUAGE SQL;\nCOMMENT ON FUNCTION pg_catalog.worker_partitioned_table_size(regclass)\n    IS 'Calculates and returns the size of a partitioned table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_partitioned_table_size/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION worker_partitioned_table_size(relation regclass)\nRETURNS bigint AS $$\n    SELECT sum(pg_table_size(relid))::bigint\n\t\t\t\t\t  FROM (SELECT relid from pg_partition_tree(relation)) partition_tree;\n$$ LANGUAGE SQL;\nCOMMENT ON FUNCTION pg_catalog.worker_partitioned_table_size(regclass)\n    IS 'Calculates and returns the size of a partitioned table';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_record_sequence_dependency/9.5-1.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_record_sequence_dependency(seq_name regclass, table_name regclass, column_name name)\n  RETURNS VOID\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', 'worker_record_sequence_dependency';\nCOMMENT ON FUNCTION pg_catalog.worker_record_sequence_dependency(regclass,regclass,name)\n  IS 'record the fact that the sequence depends on the table in pg_depend';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_record_sequence_dependency(regclass,regclass,name)\n  FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_record_sequence_dependency/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_record_sequence_dependency(seq_name regclass, table_name regclass, column_name name)\n  RETURNS VOID\n  LANGUAGE C STRICT\n  AS 'MODULE_PATHNAME', 'worker_record_sequence_dependency';\nCOMMENT ON FUNCTION pg_catalog.worker_record_sequence_dependency(regclass,regclass,name)\n  IS 'record the fact that the sequence depends on the table in pg_depend';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_record_sequence_dependency(regclass,regclass,name)\n  FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_repartition_cleanup/9.1-1.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_repartition_cleanup(bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_repartition_cleanup$$;\nCOMMENT ON FUNCTION pg_catalog.worker_repartition_cleanup(bigint)\n    IS 'remove job in remote node';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_repartition_cleanup(bigint) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_repartition_cleanup/latest.sql",
    "content": "CREATE FUNCTION pg_catalog.worker_repartition_cleanup(bigint)\n    RETURNS void\n    LANGUAGE C STRICT\n    AS 'MODULE_PATHNAME', $$worker_repartition_cleanup$$;\nCOMMENT ON FUNCTION pg_catalog.worker_repartition_cleanup(bigint)\n    IS 'remove job in remote node';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_repartition_cleanup(bigint) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_save_query_explain_analyze/9.4-1.sql",
    "content": "\nCREATE OR REPLACE FUNCTION pg_catalog.worker_save_query_explain_analyze(\n      query text, options jsonb)\n    RETURNS SETOF record\n    LANGUAGE C STRICT\n    AS 'citus';\n\nCOMMENT ON FUNCTION pg_catalog.worker_save_query_explain_analyze(text, jsonb) IS\n    'Executes and returns results of query while saving its EXPLAIN ANALYZE to be fetched later';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_save_query_explain_analyze/latest.sql",
    "content": "\nCREATE OR REPLACE FUNCTION pg_catalog.worker_save_query_explain_analyze(\n      query text, options jsonb)\n    RETURNS SETOF record\n    LANGUAGE C STRICT\n    AS 'citus';\n\nCOMMENT ON FUNCTION pg_catalog.worker_save_query_explain_analyze(text, jsonb) IS\n    'Executes and returns results of query while saving its EXPLAIN ANALYZE to be fetched later';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_split_copy/11.1-1.sql",
    "content": "-- We want to create the type in pg_catalog but doing that leads to an error\n-- \"ERROR:  permission denied to create \"pg_catalog.split_copy_info\"\n-- \"DETAIL:  System catalog modifications are currently disallowed. \"\"\n-- As a workaround, we create the type in the citus schema and then later modify it to pg_catalog.\nDROP TYPE IF EXISTS citus.split_copy_info;\nCREATE TYPE citus.split_copy_info AS (\n    destination_shard_id bigint,\n    destination_shard_min_value text,\n    destination_shard_max_value text,\n    -- A 'nodeId' is a uint32 in CITUS [1, 4294967296] but postgres does not have unsigned type support.\n    -- Use integer (consistent with other previously defined UDFs that take nodeId as integer) as for all practical purposes it is big enough.\n    destination_shard_node_id integer);\nALTER TYPE citus.split_copy_info SET SCHEMA pg_catalog;\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_split_copy(\n    source_shard_id bigint,\n\tdistribution_column text,\n    splitCopyInfos pg_catalog.split_copy_info[])\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_split_copy$$;\nCOMMENT ON FUNCTION pg_catalog.worker_split_copy(source_shard_id bigint, distribution_column text, splitCopyInfos pg_catalog.split_copy_info[])\n    IS 'Perform split copy for shard';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_split_copy/latest.sql",
    "content": "-- We want to create the type in pg_catalog but doing that leads to an error\n-- \"ERROR:  permission denied to create \"pg_catalog.split_copy_info\"\n-- \"DETAIL:  System catalog modifications are currently disallowed. \"\"\n-- As a workaround, we create the type in the citus schema and then later modify it to pg_catalog.\nDROP TYPE IF EXISTS citus.split_copy_info;\nCREATE TYPE citus.split_copy_info AS (\n    destination_shard_id bigint,\n    destination_shard_min_value text,\n    destination_shard_max_value text,\n    -- A 'nodeId' is a uint32 in CITUS [1, 4294967296] but postgres does not have unsigned type support.\n    -- Use integer (consistent with other previously defined UDFs that take nodeId as integer) as for all practical purposes it is big enough.\n    destination_shard_node_id integer);\nALTER TYPE citus.split_copy_info SET SCHEMA pg_catalog;\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_split_copy(\n    source_shard_id bigint,\n\tdistribution_column text,\n    splitCopyInfos pg_catalog.split_copy_info[])\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_split_copy$$;\nCOMMENT ON FUNCTION pg_catalog.worker_split_copy(source_shard_id bigint, distribution_column text, splitCopyInfos pg_catalog.split_copy_info[])\n    IS 'Perform split copy for shard';\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_split_shard_release_dsm/11.1-1.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_split_shard_release_dsm()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_split_shard_release_dsm$$;\nCOMMENT ON FUNCTION pg_catalog.worker_split_shard_release_dsm()\n    IS 'Releases shared memory segment allocated by non-blocking split workflow';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_split_shard_release_dsm() FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_split_shard_release_dsm/latest.sql",
    "content": "CREATE OR REPLACE FUNCTION pg_catalog.worker_split_shard_release_dsm()\nRETURNS void\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_split_shard_release_dsm$$;\nCOMMENT ON FUNCTION pg_catalog.worker_split_shard_release_dsm()\n    IS 'Releases shared memory segment allocated by non-blocking split workflow';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_split_shard_release_dsm() FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_split_shard_replication_setup/11.1-1.sql",
    "content": "CREATE TYPE citus.split_shard_info AS (\n    source_shard_id bigint,\n    distribution_column text,\n    child_shard_id bigint,\n    shard_min_value text,\n    shard_max_value text,\n    node_id integer);\nALTER TYPE citus.split_shard_info SET SCHEMA pg_catalog;\nCOMMENT ON TYPE pg_catalog.split_shard_info\n    IS 'Stores split child shard information';\n\nCREATE TYPE citus.replication_slot_info AS(node_id integer, slot_owner text, slot_name text);\nALTER TYPE citus.replication_slot_info SET SCHEMA pg_catalog;\nCOMMENT ON TYPE pg_catalog.replication_slot_info\n    IS 'Replication slot information to be used for subscriptions during non blocking shard split';\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_split_shard_replication_setup(\n    splitShardInfo pg_catalog.split_shard_info[])\nRETURNS setof pg_catalog.replication_slot_info\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_split_shard_replication_setup$$;\nCOMMENT ON FUNCTION pg_catalog.worker_split_shard_replication_setup(splitShardInfo pg_catalog.split_shard_info[])\n    IS 'Replication setup for splitting a shard';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_split_shard_replication_setup(pg_catalog.split_shard_info[]) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_split_shard_replication_setup/11.2-1.sql",
    "content": "DROP FUNCTION pg_catalog.worker_split_shard_replication_setup(pg_catalog.split_shard_info[]);\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_split_shard_replication_setup(\n    splitShardInfo pg_catalog.split_shard_info[], operation_id bigint)\nRETURNS setof pg_catalog.replication_slot_info\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_split_shard_replication_setup$$;\nCOMMENT ON FUNCTION pg_catalog.worker_split_shard_replication_setup(splitShardInfo pg_catalog.split_shard_info[], operation_id bigint)\n    IS 'Replication setup for splitting a shard';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_split_shard_replication_setup(pg_catalog.split_shard_info[], bigint) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/sql/udfs/worker_split_shard_replication_setup/latest.sql",
    "content": "DROP FUNCTION pg_catalog.worker_split_shard_replication_setup(pg_catalog.split_shard_info[]);\n\nCREATE OR REPLACE FUNCTION pg_catalog.worker_split_shard_replication_setup(\n    splitShardInfo pg_catalog.split_shard_info[], operation_id bigint)\nRETURNS setof pg_catalog.replication_slot_info\nLANGUAGE C STRICT\nAS 'MODULE_PATHNAME', $$worker_split_shard_replication_setup$$;\nCOMMENT ON FUNCTION pg_catalog.worker_split_shard_replication_setup(splitShardInfo pg_catalog.split_shard_info[], operation_id bigint)\n    IS 'Replication setup for splitting a shard';\n\nREVOKE ALL ON FUNCTION pg_catalog.worker_split_shard_replication_setup(pg_catalog.split_shard_info[], bigint) FROM PUBLIC;\n"
  },
  {
    "path": "src/backend/distributed/stats/query_stats.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * query_stats.c\n *    Statement-level statistics for distributed queries.\n *    Code is mostly taken from postgres/contrib/pg_stat_statements\n *    and adapted to citus.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n#include \"safe_lib.h\"\n\n#include \"access/hash.h\"\n#include \"catalog/pg_authid.h\"\n#include \"storage/fd.h\"\n#include \"storage/ipc.h\"\n#include \"storage/spin.h\"\n#include \"tcop/utility.h\"\n#include \"utils/builtins.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/function_utils.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/stats/query_stats.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/version_compat.h\"\n\n#define CITUS_STATS_DUMP_FILE \"pg_stat/citus_query_stats.stat\"\n#define CITUS_STAT_STATEMENTS_COLS 6\n#define CITUS_STAT_STATAMENTS_QUERY_ID 0\n#define CITUS_STAT_STATAMENTS_USER_ID 1\n#define CITUS_STAT_STATAMENTS_DB_ID 2\n#define CITUS_STAT_STATAMENTS_EXECUTOR_TYPE 3\n#define CITUS_STAT_STATAMENTS_PARTITION_KEY 4\n#define CITUS_STAT_STATAMENTS_CALLS 5\n\n\n#define USAGE_DECREASE_FACTOR (0.99)    /* decreased every CitusQueryStatsEntryDealloc */\n#define STICKY_DECREASE_FACTOR (0.50)   /* factor for sticky entries */\n#define USAGE_DEALLOC_PERCENT 5         /* free this % of entries at once */\n#define USAGE_INIT (1.0)                /* including initial planning */\n\n#define MAX_KEY_LENGTH NAMEDATALEN\n\nstatic const uint32 CITUS_QUERY_STATS_FILE_HEADER = 0x0d756e0f;\n\n/* time interval in seconds for maintenance daemon to call CitusQueryStatsSynchronizeEntries */\nint StatStatementsPurgeInterval = 10;\n\n/* maximum number of entries in queryStats hash, controlled by GUC citus.stat_statements_max */\nint StatStatementsMax = 50000;\n\n/* tracking all or none, for citus_stat_statements, controlled by GUC citus.stat_statements_track */\nint StatStatementsTrack = STAT_STATEMENTS_TRACK_NONE;\n\n/*\n * Hashtable key that defines the identity of a hashtable entry.  We use the\n * same hash as pg_stat_statements\n */\ntypedef struct QueryStatsHashKey\n{\n\tOid userid;                     /* user OID */\n\tOid dbid;                       /* database OID */\n\tuint64 queryid;                 /* query identifier */\n\tMultiExecutorType executorType; /* executor type */\n\tchar partitionKey[MAX_KEY_LENGTH];\n} QueryStatsHashKey;\n\n/*\n * Statistics per query and executor type\n */\ntypedef struct queryStatsEntry\n{\n\tQueryStatsHashKey key;   /* hash key of entry - MUST BE FIRST */\n\tint64 calls;       /* # of times executed */\n\tdouble usage;      /* hashtable usage factor */\n\tslock_t mutex;     /* protects the counters only */\n} QueryStatsEntry;\n\n/*\n * Global shared state\n */\ntypedef struct QueryStatsSharedState\n{\n\tLWLockId lock;                      /* protects hashtable search/modification */\n\tdouble cur_median_usage;            /* current median usage in hashtable */\n} QueryStatsSharedState;\n\n/* lookup table for existing pg_stat_statements entries */\ntypedef struct ExistingStatsHashKey\n{\n\tOid userid;                     /* user OID */\n\tOid dbid;                       /* database OID */\n\tuint64 queryid;                 /* query identifier */\n} ExistingStatsHashKey;\n\n/* saved hook address in case of unload */\nstatic shmem_startup_hook_type prev_shmem_startup_hook = NULL;\n\n/* Links to shared memory state */\nstatic QueryStatsSharedState *queryStats = NULL;\nstatic HTAB *queryStatsHash = NULL;\n\n/*--- Functions --- */\n\nDatum citus_query_stats_reset(PG_FUNCTION_ARGS);\nDatum citus_query_stats(PG_FUNCTION_ARGS);\n\nPG_FUNCTION_INFO_V1(citus_stat_statements_reset);\nPG_FUNCTION_INFO_V1(citus_query_stats);\nPG_FUNCTION_INFO_V1(citus_executor_name);\n\n\nstatic char * CitusExecutorName(MultiExecutorType executorType);\n\n\nstatic void CitusQueryStatsShmemStartup(void);\nstatic void CitusQueryStatsShmemShutdown(int code, Datum arg);\nstatic QueryStatsEntry * CitusQueryStatsEntryAlloc(QueryStatsHashKey *key, bool sticky);\nstatic void CitusQueryStatsEntryDealloc(void);\nstatic void CitusQueryStatsEntryReset(void);\nstatic uint32 CitusQuerysStatsHashFn(const void *key, Size keysize);\nstatic int CitusQuerysStatsMatchFn(const void *key1, const void *key2, Size keysize);\n\nstatic HTAB * BuildExistingQueryIdHash(void);\nstatic int GetPGStatStatementsMax(void);\nstatic void CitusQueryStatsRemoveExpiredEntries(HTAB *existingQueryIdHash);\n\nvoid\nInitializeCitusQueryStats(void)\n{\n\t/* Install hook */\n\tprev_shmem_startup_hook = shmem_startup_hook;\n\tshmem_startup_hook = CitusQueryStatsShmemStartup;\n}\n\n\nstatic void\nCitusQueryStatsShmemStartup(void)\n{\n\tbool found;\n\tHASHCTL info;\n\tuint32 header;\n\tint32 num;\n\tQueryStatsEntry *buffer = NULL;\n\n\tif (prev_shmem_startup_hook)\n\t{\n\t\tprev_shmem_startup_hook();\n\t}\n\n\t/* Create or attach to the shared memory state */\n\tLWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);\n\n\t/* global access lock */\n\tqueryStats = ShmemInitStruct(STATS_SHARED_MEM_NAME,\n\t\t\t\t\t\t\t\t sizeof(QueryStatsSharedState),\n\t\t\t\t\t\t\t\t &found);\n\n\tif (!found)\n\t{\n\t\t/* First time through ... */\n\t\tqueryStats->lock = &(GetNamedLWLockTranche(STATS_SHARED_MEM_NAME))->lock;\n\t}\n\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(QueryStatsHashKey);\n\tinfo.entrysize = sizeof(QueryStatsEntry);\n\tinfo.hash = CitusQuerysStatsHashFn;\n\tinfo.match = CitusQuerysStatsMatchFn;\n\n\t/* allocate stats shared memory hash */\n\tqueryStatsHash = ShmemInitHash(\"citus_query_stats hash\",\n\t\t\t\t\t\t\t\t   StatStatementsMax, StatStatementsMax,\n\t\t\t\t\t\t\t\t   &info,\n\t\t\t\t\t\t\t\t   HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);\n\n\tLWLockRelease(AddinShmemInitLock);\n\n\tif (!IsUnderPostmaster)\n\t{\n\t\ton_shmem_exit(CitusQueryStatsShmemShutdown, (Datum) 0);\n\t}\n\n\t/*\n\t * Done if some other process already completed our initialization.\n\t */\n\tif (found)\n\t{\n\t\treturn;\n\t}\n\n\t/* Load stat file, don't care about locking */\n\tFILE *file = AllocateFile(CITUS_STATS_DUMP_FILE, PG_BINARY_R);\n\tif (file == NULL)\n\t{\n\t\tif (errno == ENOENT)\n\t\t{\n\t\t\treturn;         /* ignore not-found error */\n\t\t}\n\t\tgoto error;\n\t}\n\n\t/* check is header is valid */\n\tif (fread(&header, sizeof(uint32), 1, file) != 1 ||\n\t\theader != CITUS_QUERY_STATS_FILE_HEADER)\n\t{\n\t\tgoto error;\n\t}\n\n\t/* get number of entries */\n\tif (fread(&num, sizeof(int32), 1, file) != 1)\n\t{\n\t\tgoto error;\n\t}\n\n\tfor (int i = 0; i < num; i++)\n\t{\n\t\tQueryStatsEntry temp;\n\n\t\tif (fread(&temp, sizeof(QueryStatsEntry), 1, file) != 1)\n\t\t{\n\t\t\tgoto error;\n\t\t}\n\n\t\t/* Skip loading \"sticky\" entries */\n\t\tif (temp.calls == 0)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tQueryStatsEntry *entry = CitusQueryStatsEntryAlloc(&temp.key, false);\n\n\t\t/* copy in the actual stats */\n\t\tentry->calls = temp.calls;\n\t\tentry->usage = temp.usage;\n\n\t\t/* don't initialize spinlock, already done */\n\t}\n\n\tFreeFile(file);\n\n\t/*\n\t * Remove the file so it's not included in backups/replicas, etc. A new file will be\n\t * written on next shutdown.\n\t */\n\tunlink(CITUS_STATS_DUMP_FILE);\n\n\treturn;\n\nerror:\n\tereport(LOG,\n\t\t\t(errcode_for_file_access(),\n\t\t\t errmsg(\"could not read citus_query_stats file \\\"%s\\\": %m\",\n\t\t\t\t\tCITUS_STATS_DUMP_FILE)));\n\tif (buffer)\n\t{\n\t\tpfree(buffer);\n\t}\n\tif (file)\n\t{\n\t\tFreeFile(file);\n\t}\n\n\t/* delete bogus file, don't care of errors in this case */\n\tunlink(CITUS_STATS_DUMP_FILE);\n}\n\n\n/*\n * CitusQueryStatsShmemShutdown is a shmem_shutdown hook,\n * it dumps statistics into file.\n */\nstatic void\nCitusQueryStatsShmemShutdown(int code, Datum arg)\n{\n\tHASH_SEQ_STATUS hash_seq;\n\tQueryStatsEntry *entry;\n\n\t/* Don't try to dump during a crash. */\n\tif (code)\n\t{\n\t\treturn;\n\t}\n\n\tif (!queryStats)\n\t{\n\t\treturn;\n\t}\n\n\tFILE *file = AllocateFile(CITUS_STATS_DUMP_FILE \".tmp\", PG_BINARY_W);\n\tif (file == NULL)\n\t{\n\t\tgoto error;\n\t}\n\n\tif (fwrite(&CITUS_QUERY_STATS_FILE_HEADER, sizeof(uint32), 1, file) != 1)\n\t{\n\t\tgoto error;\n\t}\n\n\tint32 num_entries = hash_get_num_entries(queryStatsHash);\n\n\tif (fwrite(&num_entries, sizeof(int32), 1, file) != 1)\n\t{\n\t\tgoto error;\n\t}\n\n\thash_seq_init(&hash_seq, queryStatsHash);\n\twhile ((entry = hash_seq_search(&hash_seq)) != NULL)\n\t{\n\t\tif (fwrite(entry, sizeof(QueryStatsEntry), 1, file) != 1)\n\t\t{\n\t\t\t/* note: we assume hash_seq_term won't change errno */\n\t\t\thash_seq_term(&hash_seq);\n\t\t\tgoto error;\n\t\t}\n\t}\n\n\tif (FreeFile(file))\n\t{\n\t\tfile = NULL;\n\t\tgoto error;\n\t}\n\n\t/*\n\t * Rename file inplace\n\t */\n\tif (rename(CITUS_STATS_DUMP_FILE \".tmp\", CITUS_STATS_DUMP_FILE) != 0)\n\t{\n\t\tereport(LOG,\n\t\t\t\t(errcode_for_file_access(),\n\t\t\t\t errmsg(\"could not rename citus_query_stats file \\\"%s\\\": %m\",\n\t\t\t\t\t\tCITUS_STATS_DUMP_FILE \".tmp\")));\n\t}\n\n\treturn;\n\nerror:\n\tereport(LOG,\n\t\t\t(errcode_for_file_access(),\n\t\t\t errmsg(\"could not read citus_query_stats file \\\"%s\\\": %m\",\n\t\t\t\t\tCITUS_STATS_DUMP_FILE)));\n\n\tif (file)\n\t{\n\t\tFreeFile(file);\n\t}\n\tunlink(CITUS_STATS_DUMP_FILE);\n}\n\n\n/*\n * CitusQueryStatsSharedMemSize calculates and returns shared memory size\n * required to keep query statistics.\n */\nSize\nCitusQueryStatsSharedMemSize(void)\n{\n\tAssert(StatStatementsMax >= 0);\n\n\tSize size = MAXALIGN(sizeof(QueryStatsSharedState));\n\tsize = add_size(size, hash_estimate_size(StatStatementsMax, sizeof(QueryStatsEntry)));\n\n\treturn size;\n}\n\n\n/*\n * CitusQueryStatsExecutorsEntry is the function to update statistics\n * for a given query id.\n */\nvoid\nCitusQueryStatsExecutorsEntry(uint64 queryId, MultiExecutorType executorType,\n\t\t\t\t\t\t\t  char *partitionKey)\n{\n\tQueryStatsHashKey key;\n\n\t/* Safety check... */\n\tif (!queryStats || !queryStatsHash)\n\t{\n\t\treturn;\n\t}\n\n\t/* early return if tracking is disabled */\n\tif (!StatStatementsTrack)\n\t{\n\t\treturn;\n\t}\n\n\t/* Set up key for hashtable search */\n\tkey.userid = GetUserId();\n\tkey.dbid = MyDatabaseId;\n\tkey.queryid = queryId;\n\tkey.executorType = executorType;\n\tmemset(key.partitionKey, 0, MAX_KEY_LENGTH);\n\tif (partitionKey != NULL)\n\t{\n\t\tstrlcpy(key.partitionKey, partitionKey, MAX_KEY_LENGTH);\n\t}\n\n\t/* Lookup the hash table entry with shared lock. */\n\tLWLockAcquire(queryStats->lock, LW_SHARED);\n\n\tQueryStatsEntry *entry = (QueryStatsEntry *) hash_search(queryStatsHash, &key,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t HASH_FIND, NULL);\n\n\t/* Create new entry, if not present */\n\tif (!entry)\n\t{\n\t\t/* Need exclusive lock to make a new hashtable entry - promote */\n\t\tLWLockRelease(queryStats->lock);\n\t\tLWLockAcquire(queryStats->lock, LW_EXCLUSIVE);\n\n\t\t/* OK to create a new hashtable entry */\n\t\tentry = CitusQueryStatsEntryAlloc(&key, false);\n\t}\n\n\t/*\n\t * Grab the spinlock while updating the counters (see comment about\n\t * locking rules at the head of the pg_stat_statements file)\n\t */\n\tvolatile QueryStatsEntry *e = (volatile QueryStatsEntry *) entry;\n\n\tSpinLockAcquire(&e->mutex);\n\n\t/* \"Unstick\" entry if it was previously sticky */\n\tif (e->calls == 0)\n\t{\n\t\te->usage = USAGE_INIT;\n\t}\n\n\te->calls += 1;\n\n\tSpinLockRelease(&e->mutex);\n\n\tLWLockRelease(queryStats->lock);\n}\n\n\n/*\n * Allocate a new hashtable entry.\n * caller must hold an exclusive lock on queryStats->lock\n */\nstatic QueryStatsEntry *\nCitusQueryStatsEntryAlloc(QueryStatsHashKey *key, bool sticky)\n{\n\tbool found;\n\tlong StatStatementsMaxLong = StatStatementsMax;\n\n\t/* Make space if needed */\n\twhile (hash_get_num_entries(queryStatsHash) >= StatStatementsMaxLong)\n\t{\n\t\tCitusQueryStatsEntryDealloc();\n\t}\n\n\t/* Find or create an entry with desired hash code */\n\tQueryStatsEntry *entry = (QueryStatsEntry *) hash_search(queryStatsHash, key,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t HASH_ENTER, &found);\n\n\tif (!found)\n\t{\n\t\t/* New entry, initialize it */\n\n\t\t/* set the appropriate initial usage count */\n\t\tentry->usage = sticky ? queryStats->cur_median_usage : USAGE_INIT;\n\n\t\t/* re-initialize the mutex each time ... we assume no one using it */\n\t\tSpinLockInit(&entry->mutex);\n\t}\n\n\tentry->calls = 0;\n\tentry->usage = (0.0);\n\n\treturn entry;\n}\n\n\n/*\n * entry_cmp is qsort comparator for sorting into increasing usage order\n */\nstatic int\nentry_cmp(const void *lhs, const void *rhs)\n{\n\tdouble l_usage = (*(QueryStatsEntry *const *) lhs)->usage;\n\tdouble r_usage = (*(QueryStatsEntry *const *) rhs)->usage;\n\n\tif (l_usage < r_usage)\n\t{\n\t\treturn -1;\n\t}\n\telse if (l_usage > r_usage)\n\t{\n\t\treturn +1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * CitusQueryStatsEntryDealloc deallocates least used entries.\n * Caller must hold an exclusive lock on queryStats->lock.\n */\nstatic void\nCitusQueryStatsEntryDealloc(void)\n{\n\tHASH_SEQ_STATUS hash_seq;\n\tQueryStatsEntry *entry;\n\n\t/*\n\t * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.\n\t * While we're scanning the table, apply the decay factor to the usage\n\t * values.\n\t */\n\tQueryStatsEntry **entries = palloc(hash_get_num_entries(queryStatsHash) *\n\t\t\t\t\t\t\t\t\t   sizeof(QueryStatsEntry *));\n\n\tint i = 0;\n\thash_seq_init(&hash_seq, queryStatsHash);\n\twhile ((entry = hash_seq_search(&hash_seq)) != NULL)\n\t{\n\t\tentries[i++] = entry;\n\n\t\t/* \"Sticky\" entries get a different usage decay rate. */\n\t\tif (entry->calls == 0)\n\t\t{\n\t\t\tentry->usage *= STICKY_DECREASE_FACTOR;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tentry->usage *= USAGE_DECREASE_FACTOR;\n\t\t}\n\t}\n\n\tSafeQsort(entries, i, sizeof(QueryStatsEntry *), entry_cmp);\n\n\tif (i > 0)\n\t{\n\t\t/* Record the (approximate) median usage */\n\t\tqueryStats->cur_median_usage = entries[i / 2]->usage;\n\t}\n\n\tint nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);\n\tnvictims = Min(nvictims, i);\n\n\tfor (i = 0; i < nvictims; i++)\n\t{\n\t\thash_search(queryStatsHash, &entries[i]->key, HASH_REMOVE, NULL);\n\t}\n\n\tpfree(entries);\n}\n\n\n/*\n * CitusQueryStatsEntryReset resets statistics.\n */\nstatic void\nCitusQueryStatsEntryReset(void)\n{\n\tHASH_SEQ_STATUS hash_seq;\n\tQueryStatsEntry *entry;\n\n\tLWLockAcquire(queryStats->lock, LW_EXCLUSIVE);\n\n\thash_seq_init(&hash_seq, queryStatsHash);\n\twhile ((entry = hash_seq_search(&hash_seq)) != NULL)\n\t{\n\t\thash_search(queryStatsHash, &entry->key, HASH_REMOVE, NULL);\n\t}\n\n\tLWLockRelease(queryStats->lock);\n}\n\n\n/*\n * CitusQuerysStatsHashFn calculates and returns hash value for a key\n */\nstatic uint32\nCitusQuerysStatsHashFn(const void *key, Size keysize)\n{\n\tconst QueryStatsHashKey *k = (const QueryStatsHashKey *) key;\n\n\tif (k->partitionKey[0] != '\\0')\n\t{\n\t\treturn hash_uint32((uint32) k->userid) ^\n\t\t\t   hash_uint32((uint32) k->dbid) ^\n\t\t\t   hash_any((const unsigned char *) &(k->queryid), sizeof(uint64)) ^\n\t\t\t   hash_uint32((uint32) k->executorType) ^\n\t\t\t   hash_any((const unsigned char *) (k->partitionKey), strlen(\n\t\t\t\t\t\t\tk->partitionKey));\n\t}\n\telse\n\t{\n\t\treturn hash_uint32((uint32) k->userid) ^\n\t\t\t   hash_uint32((uint32) k->dbid) ^\n\t\t\t   hash_any((const unsigned char *) &(k->queryid), sizeof(uint64)) ^\n\t\t\t   hash_uint32((uint32) k->executorType);\n\t}\n}\n\n\n/*\n * CitusQuerysStatsMatchFn compares two keys - zero means match.\n * See definition of HashCompareFunc in hsearch.h for more info.\n */\nstatic int\nCitusQuerysStatsMatchFn(const void *key1, const void *key2, Size keysize)\n{\n\tconst QueryStatsHashKey *k1 = (const QueryStatsHashKey *) key1;\n\tconst QueryStatsHashKey *k2 = (const QueryStatsHashKey *) key2;\n\n\tif (k1->userid == k2->userid &&\n\t\tk1->dbid == k2->dbid &&\n\t\tk1->queryid == k2->queryid &&\n\t\tk1->executorType == k2->executorType)\n\t{\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n\n\n/*\n * Reset statistics.\n */\nDatum\ncitus_stat_statements_reset(PG_FUNCTION_ARGS)\n{\n\tCitusQueryStatsEntryReset();\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_query_stats returns query stats kept in memory.\n */\nDatum\ncitus_query_stats(PG_FUNCTION_ARGS)\n{\n\tTupleDesc tupdesc;\n\tHASH_SEQ_STATUS hash_seq;\n\tQueryStatsEntry *entry;\n\tOid currentUserId = GetUserId();\n\tbool canSeeStats = superuser();\n\n\tif (!queryStats)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t errmsg(\"citus_query_stats: shared memory not initialized\")));\n\t}\n\n\tif (is_member_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS))\n\t{\n\t\tcanSeeStats = true;\n\t}\n\n\tTuplestorestate *tupstore = SetupTuplestore(fcinfo, &tupdesc);\n\n\n\t/* exclusive lock on queryStats->lock is acquired and released inside the function */\n\tCitusQueryStatsSynchronizeEntries();\n\n\tLWLockAcquire(queryStats->lock, LW_SHARED);\n\n\thash_seq_init(&hash_seq, queryStatsHash);\n\twhile ((entry = hash_seq_search(&hash_seq)) != NULL)\n\t{\n\t\tDatum values[CITUS_STAT_STATEMENTS_COLS];\n\t\tbool nulls[CITUS_STAT_STATEMENTS_COLS];\n\n\t\t/* following vars are to keep data for processing after spinlock release */\n\t\tuint64 queryid = 0;\n\t\tOid userid = InvalidOid;\n\t\tOid dbid = InvalidOid;\n\t\tMultiExecutorType executorType = MULTI_EXECUTOR_INVALID_FIRST;\n\t\tchar partitionKey[MAX_KEY_LENGTH];\n\t\tint64 calls = 0;\n\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(nulls, 0, sizeof(nulls));\n\t\tmemset(partitionKey, 0, MAX_KEY_LENGTH);\n\n\t\tSpinLockAcquire(&entry->mutex);\n\n\t\t/*\n\t\t * Skip entry if unexecuted (ie, it's a pending \"sticky\" entry) or\n\t\t * the user does not have permission to view it.\n\t\t */\n\t\tif (entry->calls == 0 || !(currentUserId == entry->key.userid || canSeeStats))\n\t\t{\n\t\t\tSpinLockRelease(&entry->mutex);\n\t\t\tcontinue;\n\t\t}\n\n\t\tqueryid = entry->key.queryid;\n\t\tuserid = entry->key.userid;\n\t\tdbid = entry->key.dbid;\n\t\texecutorType = entry->key.executorType;\n\n\t\tif (entry->key.partitionKey[0] != '\\0')\n\t\t{\n\t\t\tmemcpy_s(partitionKey, sizeof(partitionKey), entry->key.partitionKey,\n\t\t\t\t\t sizeof(entry->key.partitionKey));\n\t\t}\n\n\t\tcalls = entry->calls;\n\n\t\tSpinLockRelease(&entry->mutex);\n\n\t\tvalues[CITUS_STAT_STATAMENTS_QUERY_ID] = UInt64GetDatum(queryid);\n\t\tvalues[CITUS_STAT_STATAMENTS_USER_ID] = ObjectIdGetDatum(userid);\n\t\tvalues[CITUS_STAT_STATAMENTS_DB_ID] = ObjectIdGetDatum(dbid);\n\t\tvalues[CITUS_STAT_STATAMENTS_EXECUTOR_TYPE] = UInt32GetDatum(\n\t\t\t(uint32) executorType);\n\n\t\tif (partitionKey[0] != '\\0')\n\t\t{\n\t\t\tvalues[CITUS_STAT_STATAMENTS_PARTITION_KEY] = CStringGetTextDatum(\n\t\t\t\tpartitionKey);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnulls[CITUS_STAT_STATAMENTS_PARTITION_KEY] = true;\n\t\t}\n\n\t\tvalues[CITUS_STAT_STATAMENTS_CALLS] = Int64GetDatumFast(calls);\n\n\t\ttuplestore_putvalues(tupstore, tupdesc, values, nulls);\n\t}\n\n\tLWLockRelease(queryStats->lock);\n\n\treturn (Datum) 0;\n}\n\n\n/*\n * CitusQueryStatsSynchronizeEntries removes all entries in queryStats hash\n * that does not have matching queryId in pg_stat_statements.\n *\n * A function called inside (CitusQueryStatsRemoveExpiredEntries) acquires\n * an exclusive lock on queryStats->lock.\n */\nvoid\nCitusQueryStatsSynchronizeEntries(void)\n{\n\tHTAB *existingQueryIdHash = BuildExistingQueryIdHash();\n\tif (existingQueryIdHash != NULL)\n\t{\n\t\tCitusQueryStatsRemoveExpiredEntries(existingQueryIdHash);\n\t\thash_destroy(existingQueryIdHash);\n\t}\n}\n\n\n/*\n * BuildExistingQueryIdHash goes over entries in pg_stat_statements and prepare\n * a hash table of queryId's. The function returns null if\n * public.pg_stat_statements(bool) function is not available. Returned hash\n * table is allocated on the CurrentMemoryContext, and caller is responsible\n * for deallocation.\n */\nstatic HTAB *\nBuildExistingQueryIdHash(void)\n{\n\tconst int userIdAttributeNumber = 1;\n\tconst int dbIdAttributeNumber = 2;\n\tconst int queryIdAttributeNumber = 4;\n\tDatum commandTypeDatum = (Datum) 0;\n\tbool missingOK = true;\n\n\tOid pgStatStatementsOid = FunctionOidExtended(\"public\", \"pg_stat_statements\", 1,\n\t\t\t\t\t\t\t\t\t\t\t\t  missingOK);\n\tif (!OidIsValid(pgStatStatementsOid))\n\t{\n\t\treturn NULL;\n\t}\n\n\n\t/* fetch pg_stat_statements.max, it is expected to be available, if not bail out */\n\tint pgStatStatementsMax = GetPGStatStatementsMax();\n\tif (pgStatStatementsMax == 0)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"Cannot access pg_stat_statements.max\")));\n\t\treturn NULL;\n\t}\n\n\tFmgrInfo *fmgrPGStatStatements = (FmgrInfo *) palloc0(sizeof(FmgrInfo));\n\tcommandTypeDatum = BoolGetDatum(false);\n\n\tfmgr_info(pgStatStatementsOid, fmgrPGStatStatements);\n\n\tReturnSetInfo *statStatementsReturnSet = FunctionCallGetTupleStore1(\n\t\tfmgrPGStatStatements->fn_addr,\n\t\tpgStatStatementsOid,\n\t\tcommandTypeDatum);\n\tTupleTableSlot *tupleTableSlot = MakeSingleTupleTableSlot(\n\t\tstatStatementsReturnSet->setDesc,\n\t\t&TTSOpsMinimalTuple);\n\n\t/*\n\t * Allocate more hash slots (twice as much) than necessary to minimize\n\t * collisions.\n\t */\n\tassert_valid_hash_key3(ExistingStatsHashKey, userid, dbid, queryid);\n\tHTAB *queryIdHashTable = CreateSimpleHashSetWithNameAndSize(\n\t\tExistingStatsHashKey,\n\t\t\"pg_stats_statements queryId hash\",\n\t\tpgStatStatementsMax * 2);\n\n\t/* iterate over tuples in tuple store, and add queryIds to hash table */\n\twhile (true)\n\t{\n\t\tbool isNull = false;\n\n\t\tbool tuplePresent = tuplestore_gettupleslot(statStatementsReturnSet->setResult,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttupleTableSlot);\n\n\t\tif (!tuplePresent)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tDatum userIdDatum = slot_getattr(tupleTableSlot, userIdAttributeNumber, &isNull);\n\t\tDatum dbIdDatum = slot_getattr(tupleTableSlot, dbIdAttributeNumber, &isNull);\n\t\tDatum queryIdDatum = slot_getattr(tupleTableSlot, queryIdAttributeNumber,\n\t\t\t\t\t\t\t\t\t\t  &isNull);\n\n\n\t\t/*\n\t\t * queryId may be returned as NULL when current user is not authorized to see other\n\t\t * users' stats.\n\t\t */\n\t\tif (!isNull)\n\t\t{\n\t\t\tExistingStatsHashKey key;\n\t\t\tkey.userid = DatumGetInt32(userIdDatum);\n\t\t\tkey.dbid = DatumGetInt32(dbIdDatum);\n\t\t\tkey.queryid = DatumGetInt64(queryIdDatum);\n\t\t\thash_search(queryIdHashTable, (void *) &key, HASH_ENTER, NULL);\n\t\t}\n\n\t\tExecClearTuple(tupleTableSlot);\n\t}\n\n\tExecDropSingleTupleTableSlot(tupleTableSlot);\n\n\ttuplestore_end(statStatementsReturnSet->setResult);\n\n\tpfree(fmgrPGStatStatements);\n\n\treturn queryIdHashTable;\n}\n\n\n/*\n * GetPGStatStatementsMax returns GUC value pg_stat_statements.max. The\n * function returns 0 if for some reason it can not access\n * pg_stat_statements.max value.\n */\nstatic int\nGetPGStatStatementsMax(void)\n{\n\tconst char *name = \"pg_stat_statements.max\";\n\tint maxValue = 0;\n\n\tconst char *pgssMax = GetConfigOption(name, true, false);\n\n\t/*\n\t * Retrieving pg_stat_statements.max can fail if the extension is loaded\n\t * after citus in shared_preload_libraries, or not at all.\n\t */\n\tif (pgssMax)\n\t{\n\t\tmaxValue = pg_strtoint32(pgssMax);\n\t}\n\n\treturn maxValue;\n}\n\n\n/*\n * CitusQueryStatsRemoveExpiredEntries iterates over queryStats hash entries\n * and removes entries with keys that do not exists in the provided hash of\n * queryIds.\n *\n * Acquires and releases exclusive lock on queryStats->lock.\n */\nstatic void\nCitusQueryStatsRemoveExpiredEntries(HTAB *existingQueryIdHash)\n{\n\tHASH_SEQ_STATUS hash_seq;\n\tQueryStatsEntry *entry;\n\tint removedCount = 0;\n\tbool canSeeStats = superuser();\n\tOid currentUserId = GetUserId();\n\n\tif (is_member_of_role(currentUserId, ROLE_PG_READ_ALL_STATS))\n\t{\n\t\tcanSeeStats = true;\n\t}\n\n\tLWLockAcquire(queryStats->lock, LW_EXCLUSIVE);\n\n\thash_seq_init(&hash_seq, queryStatsHash);\n\twhile ((entry = hash_seq_search(&hash_seq)) != NULL)\n\t{\n\t\tbool found = false;\n\t\tExistingStatsHashKey existingStatsKey = { 0, 0, 0 };\n\n\t\t/*\n\t\t * pg_stat_statements returns NULL in the queryId field for queries\n\t\t * belonging to other users. Those queries are therefore not reflected\n\t\t * in the existingQueryIdHash, but that does not mean that we should\n\t\t * remove them as they are relevant to other users.\n\t\t */\n\t\tif (!(currentUserId == entry->key.userid || canSeeStats))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\texistingStatsKey.userid = entry->key.userid;\n\t\texistingStatsKey.dbid = entry->key.dbid;\n\t\texistingStatsKey.queryid = entry->key.queryid;\n\n\t\thash_search(existingQueryIdHash, (void *) &existingStatsKey, HASH_FIND, &found);\n\t\tif (!found)\n\t\t{\n\t\t\thash_search(queryStatsHash, &entry->key, HASH_REMOVE, NULL);\n\t\t\tremovedCount++;\n\t\t}\n\t}\n\n\tLWLockRelease(queryStats->lock);\n\n\tif (removedCount > 0)\n\t{\n\t\telog(DEBUG2, \"citus_stat_statements removed %d expired entries\", removedCount);\n\t}\n}\n\n\n/*\n * citus_executor_name is a UDF that returns the name of the executor\n * given the internal enum value.\n */\nDatum\ncitus_executor_name(PG_FUNCTION_ARGS)\n{\n\tMultiExecutorType executorType = PG_GETARG_UINT32(0);\n\n\tchar *executorName = CitusExecutorName(executorType);\n\n\tPG_RETURN_TEXT_P(cstring_to_text(executorName));\n}\n\n\n/*\n * CitusExecutorName returns the name of the executor given the internal\n * enum value.\n */\nstatic char *\nCitusExecutorName(MultiExecutorType executorType)\n{\n\tswitch (executorType)\n\t{\n\t\tcase MULTI_EXECUTOR_ADAPTIVE:\n\t\t{\n\t\t\treturn \"adaptive\";\n\t\t}\n\n\t\tcase MULTI_EXECUTOR_NON_PUSHABLE_INSERT_SELECT:\n\t\t{\n\t\t\treturn \"insert-select\";\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn \"unknown\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/stats/stat_counters.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * stat_counters.c\n *\n * This file contains functions to track various statistic counters for\n * Citus.\n *\n * We create an array of \"BackendStatsSlot\"s in shared memory, one for\n * each backend. Each backend increments its own stat counters in its\n * own slot via IncrementStatCounterForMyDb(). And when a backend exits,\n * it saves its stat counters from its slot via\n * SaveBackendStatsIntoSavedBackendStatsHash() into a hash table in\n * shared memory, whose entries are \"SavedBackendStatsHashEntry\"s and\n * the key is the database id. In other words, each entry of the hash\n * table is used to aggregate the stat counters for backends that were\n * connected to that database and exited since the last server restart.\n * Plus, each entry is responsible for keeping track of the reset\n * timestamp for both active and exited backends too.\n * Note that today we don't evict the entries of the said hash table\n * that point to dropped databases because the wrapper view anyway\n * filters them out (thanks to LEFT JOIN) and we don't expect a\n * performance hit due to that unless users have a lot of databases\n * that are dropped and recreated frequently.\n *\n * The reason why we save the stat counters for exited backends in the\n * shared hash table is that we cannot guarantee that the backend slot\n * that was used by an exited backend will be reused by another backend\n * connected to the same database. For this reason, we need to save the\n * stat counters for exited backends into a shared hash table so that we\n * can reset the counters within the corresponding backend slots while\n * the backends exit.\n *\n * When citus_stat_counters() is called, we first aggregate the stat\n * counters from the backend slots of all the active backends and then\n * we add the aggregated stat counters from the exited backends that\n * are stored in the shared hash table. Also, we don't persist backend\n * stats on server shutdown, but we might want to do that in the future.\n *\n * Similarly, when citus_stat_counters_reset() is called, we reset the\n * stat counters for the active backends and the exited backends that are\n * stored in the shared hash table. Then, it also updates the\n * resetTimestamp in the shared hash table entry appropriately. So,\n * similarly, when citus_stat_counters() is called, we just report\n * resetTimestamp as stats_reset column.\n *\n * Caveats:\n *\n * There is chance that citus_stat_counters_reset() might race with a\n * backend that is trying to increment one of the counters in its slot\n * and as a result it can effectively fail to reset that counter due to\n * the reasons documented in IncrementStatCounterForMyDb() function.\n * However, this should be a very rare case and we can live with that\n * for now.\n *\n * Also, citus_stat_counters() might observe the counters for a backend\n * twice or perhaps unsee it if it's concurrently exiting, depending on\n * the order we call CollectActiveBackendStatsIntoHTAB() and\n * CollectSavedBackendStatsIntoHTAB() in citus_stat_counters(). However,\n * the next call to citus_stat_counters() will see the correct values\n * for the counters, so we can live with that for now.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"common/hashfn.h\"\n#include \"port/atomics.h\"\n#include \"storage/ipc.h\"\n#include \"storage/proc.h\"\n#include \"utils/hsearch.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/argutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/stats/stat_counters.h\"\n#include \"distributed/tuplestore.h\"\n\n\n/*\n * saved backend stats - hash table constants\n *\n * Configurations used to create the hash table for saved backend stats.\n * The places where SAVED_BACKEND_STATS_HASH_MAX_DATABASES is used do not\n * impose a hard limit on the number of databases that can be tracked but\n * in ShmemInitHash() it's documented that the access efficiency will degrade\n * if it is exceeded substantially.\n *\n * XXX: Consider using dshash_table instead of (shared) HTAB if that becomes\n *      a concern.\n */\n#define SAVED_BACKEND_STATS_HASH_INIT_DATABASES 8\n#define SAVED_BACKEND_STATS_HASH_MAX_DATABASES 1024\n\n\n/* fixed size array types to store the stat counters */\ntypedef pg_atomic_uint64 AtomicStatCounters[N_CITUS_STAT_COUNTERS];\ntypedef uint64 StatCounters[N_CITUS_STAT_COUNTERS];\n\n/*\n * saved backend stats - hash entry definition\n *\n * This is used to define & access the shared hash table used to aggregate the stat\n * counters for the backends exited so far since last server restart. It's also\n * responsible for keeping track of the reset timestamp.\n */\ntypedef struct SavedBackendStatsHashEntry\n{\n\t/* hash entry key, must always be the first */\n\tOid databaseId;\n\n\t/*\n\t * Needs to be locked whenever we read / write counters or resetTimestamp\n\t * in this struct since we don't use atomic counters for this struct. Plus,\n\t * we want to update the stat counters and resetTimestamp atomically.\n\t */\n\tslock_t mutex;\n\n\t/*\n\t * While \"counters\" only represents the stat counters for exited backends,\n\t * the \"resetTimestamp\" doesn't only represent the reset timestamp for exited\n\t * backends' stat counters but also for the active backends.\n\t */\n\tStatCounters counters;\n\tTimestampTz resetTimestamp;\n} SavedBackendStatsHashEntry;\n\n/*\n * Hash entry definition used for the local hash table created by\n * citus_stat_counters() at the runtime to aggregate the stat counters\n * across all backends.\n */\ntypedef struct DatabaseStatsHashEntry\n{\n\t/* hash entry key, must always be the first */\n\tOid databaseId;\n\n\tStatCounters counters;\n\tTimestampTz resetTimestamp;\n} DatabaseStatsHashEntry;\n\n/* definition of a one per-backend stat counters slot in shared memory */\ntypedef struct BackendStatsSlot\n{\n\tAtomicStatCounters counters;\n} BackendStatsSlot;\n\n\n/*\n * GUC variable\n *\n * This only controls whether we track the stat counters or not, via\n * IncrementStatCounterForMyDb() and\n * SaveBackendStatsIntoSavedBackendStatsHash(). In other words, even\n * when the GUC is disabled, we still allocate the shared memory\n * structures etc. and citus_stat_counters() / citus_stat_counters_reset()\n * will still work.\n */\nbool EnableStatCounters = ENABLE_STAT_COUNTERS_DEFAULT;\n\n/* saved backend stats - shared memory variables */\nstatic LWLockId *SharedSavedBackendStatsHashLock = NULL;\nstatic HTAB *SharedSavedBackendStatsHash = NULL;\n\n/* per-backend stat counter slots - shared memory array */\nBackendStatsSlot *SharedBackendStatsSlotArray = NULL;\n\n/*\n * We don't expect the callsites that check this (via\n * EnsureStatCountersShmemInitDone()) to be executed before\n * StatCountersShmemInit() is done. Plus, once StatCountersShmemInit()\n * is done, we also don't expect shared memory variables to be\n * initialized improperly. However, we still set this to true only\n * once StatCountersShmemInit() is done and if all three of the shared\n * memory variables above are initialized properly. And in the callsites\n * where these shared memory variables are accessed, we check this\n * variable first just to be on the safe side.\n */\nstatic bool StatCountersShmemInitDone = false;\n\n/* saved shmem_startup_hook */\nstatic shmem_startup_hook_type prev_shmem_startup_hook = NULL;\n\n\n/* shared memory init & management */\nstatic void StatCountersShmemInit(void);\nstatic Size SharedBackendStatsSlotArrayShmemSize(void);\n\n/* helper functions for citus_stat_counters() */\nstatic void CollectActiveBackendStatsIntoHTAB(Oid databaseId, HTAB *databaseStats);\nstatic void CollectSavedBackendStatsIntoHTAB(Oid databaseId, HTAB *databaseStats);\nstatic DatabaseStatsHashEntry * DatabaseStatsHashEntryFindOrCreate(Oid databaseId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   HTAB *databaseStats);\nstatic void StoreDatabaseStatsIntoTupStore(HTAB *databaseStats,\n\t\t\t\t\t\t\t\t\t\t   Tuplestorestate *tupleStore,\n\t\t\t\t\t\t\t\t\t\t   TupleDesc tupleDescriptor);\n\n/* helper functions for citus_stat_counters_reset() */\nstatic bool ResetActiveBackendStats(Oid databaseId);\nstatic void ResetSavedBackendStats(Oid databaseId, bool force);\n\n/* saved backend stats */\nstatic SavedBackendStatsHashEntry * SavedBackendStatsHashEntryCreateIfNotExists(Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdatabaseId);\n\n\n/* sql exports */\nPG_FUNCTION_INFO_V1(citus_stat_counters);\nPG_FUNCTION_INFO_V1(citus_stat_counters_reset);\n\n\n/*\n * EnsureStatCountersShmemInitDone returns true if the shared memory\n * data structures used for keeping track of stat counters have been\n * properly initialized, otherwise, returns false and emits a warning.\n */\nstatic inline bool\nEnsureStatCountersShmemInitDone(void)\n{\n\tif (!StatCountersShmemInitDone)\n\t{\n\t\tereport(WARNING,\n\t\t\t\t(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t errmsg(\"shared memory for stat counters was not properly initialized\")));\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * citus_stat_counters returns stats counters for the given database id.\n *\n * This only returns rows for the databases which have been connected to\n * by at least one backend since the last server restart (even if no\n * observations have been made for none of the counters or if they were\n * reset) and it considers such a database even if it has been dropped later.\n *\n * When InvalidOid is provided, all such databases are considered; otherwise\n * only the database with the given id is considered.\n *\n * So, as an outcome, when a database id that is different than InvalidOid\n * is provided and no backend has connected to it since the last server\n * restart, or, if we didn't ever have such a database, then the function\n * returns an empty set.\n *\n * Finally, stats_reset column is set to NULL if the stat counters for the\n * database were never reset since the last server restart.\n */\nDatum\ncitus_stat_counters(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\t/*\n\t * Function's sql definition allows Postgres to silently\n\t * ignore NULL, but we still check.\n\t */\n\tPG_ENSURE_ARGNOTNULL(0, \"database_id\");\n\tOid databaseId = PG_GETARG_OID(0);\n\n\t/* just to be on the safe side */\n\tif (!EnsureStatCountersShmemInitDone())\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tHASHCTL info;\n\tuint32 hashFlags = (HASH_ELEM | HASH_FUNCTION);\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(Oid);\n\tinfo.hash = oid_hash;\n\tinfo.entrysize = sizeof(DatabaseStatsHashEntry);\n\n\tHTAB *databaseStats = hash_create(\"Citus Database Stats Collect Hash\", 8, &info,\n\t\t\t\t\t\t\t\t\t  hashFlags);\n\n\tCollectActiveBackendStatsIntoHTAB(databaseId, databaseStats);\n\tCollectSavedBackendStatsIntoHTAB(databaseId, databaseStats);\n\n\tStoreDatabaseStatsIntoTupStore(databaseStats, tupleStore, tupleDescriptor);\n\n\thash_destroy(databaseStats);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_stat_counters_reset resets Citus stat counters for given database\n * id or for the current database if InvalidOid is provided.\n *\n * If a valid database id is provided, stat counters for that database are\n * reset, even if it was dropped later.\n *\n * Otherwise, if the provided database id is not valid, then the function\n * effectively does nothing.\n */\nDatum\ncitus_stat_counters_reset(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\t/*\n\t * Function's sql definition allows Postgres to silently\n\t * ignore NULL, but we still check.\n\t */\n\tPG_ENSURE_ARGNOTNULL(0, \"database_id\");\n\tOid databaseId = PG_GETARG_OID(0);\n\n\t/*\n\t * If the database id is InvalidOid, then we assume that\n\t * the caller wants to reset the stat counters for the\n\t * current database.\n\t */\n\tif (databaseId == InvalidOid)\n\t{\n\t\tdatabaseId = MyDatabaseId;\n\t}\n\n\t/* just to be on the safe side */\n\tif (!EnsureStatCountersShmemInitDone())\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\tbool foundAnyBackendsForDb = ResetActiveBackendStats(databaseId);\n\n\t/*\n\t * Even when we don't have an entry for the given database id in the\n\t * saved backend stats hash table, we still want to create one for\n\t * it to save the resetTimestamp if we currently have at least backend\n\t * connected to it. By providing foundAnyBackendsForDb, we effectively\n\t * let the function do that. Since ResetActiveBackendStats() doesn't\n\t * filter the active backends, foundAnyBackendsForDb being true\n\t * not always means that at least one backend is connected to it right\n\t * now, but it means that we had such a backend at some point in time\n\t * since the last server restart. If all backends refered to in the\n\t * shared array are already exited, then we should already have an\n\t * entry for it in the saved backend stats hash table, so providing\n\t * a \"true\" wouldn't do anything in that case. Otherwise, if at least\n\t * one backend is still connected to it, providing a \"true\" will\n\t * effectively create a new entry for it if it doesn't exist yet,\n\t * which is what we actually want to do.\n\t *\n\t * That way, we can save the resetTimestamp for the active backends\n\t * into the relevant entry of the saved backend stats hash table.\n\t * Note that we don't do that for the databases that don't have\n\t * any active backends connected to them because we actually don't\n\t * reset anything for such databases.\n\t */\n\tResetSavedBackendStats(databaseId, foundAnyBackendsForDb);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * InitializeStatCountersShmem saves the previous shmem_startup_hook and sets\n * up a new shmem_startup_hook for initializing the shared memory data structures\n * used for keeping track of stat counters.\n */\nvoid\nInitializeStatCountersShmem(void)\n{\n\tprev_shmem_startup_hook = shmem_startup_hook;\n\tshmem_startup_hook = StatCountersShmemInit;\n}\n\n\n/*\n * StatCountersShmemSize calculates and returns shared memory size\n * required for the shared memory data structures used for keeping track of\n * stat counters.\n */\nSize\nStatCountersShmemSize(void)\n{\n\tSize backendStatsSlotArraySize = SharedBackendStatsSlotArrayShmemSize();\n\tSize savedBackendStatsHashLockSize = MAXALIGN(sizeof(LWLockId));\n\tSize savedBackendStatsHashSize = hash_estimate_size(\n\t\tSAVED_BACKEND_STATS_HASH_MAX_DATABASES, sizeof(SavedBackendStatsHashEntry));\n\n\treturn add_size(add_size(backendStatsSlotArraySize, savedBackendStatsHashLockSize),\n\t\t\t\t\tsavedBackendStatsHashSize);\n}\n\n\n/*\n * IncrementStatCounterForMyDb increments the stat counter for the given statId\n * for this backend.\n */\nvoid\nIncrementStatCounterForMyDb(int statId)\n{\n\tif (!EnableStatCounters)\n\t{\n\t\treturn;\n\t}\n\n\t/* just to be on the safe side */\n\tif (!EnsureStatCountersShmemInitDone())\n\t{\n\t\treturn;\n\t}\n\n\tint myBackendSlotIdx = getProcNo_compat(MyProc);\n\tBackendStatsSlot *myBackendStatsSlot =\n\t\t&SharedBackendStatsSlotArray[myBackendSlotIdx];\n\n\t/*\n\t * When there cannot be any other writers, incrementing an atomic\n\t * counter via pg_atomic_read_u64() and pg_atomic_write_u64() is\n\t * same as incrementing it via pg_atomic_fetch_add_u64(). Plus, the\n\t * former is cheaper than the latter because the latter has to do\n\t * extra work to deal with concurrent writers.\n\t *\n\t * In our case, the only concurrent writer could be the backend that\n\t * is executing citus_stat_counters_reset(). So, there is chance that\n\t * we read the counter value, then it gets reset by a concurrent call\n\t * made to citus_stat_counters_reset() and then we write the\n\t * incremented value back, by effectively overriding the reset value.\n\t * But this should be a rare case and we can live with that, for the\n\t * sake of lock-free implementation of this function.\n\t */\n\tpg_atomic_uint64 *statPtr = &myBackendStatsSlot->counters[statId];\n\tpg_atomic_write_u64(statPtr, pg_atomic_read_u64(statPtr) + 1);\n}\n\n\n/*\n * SaveBackendStatsIntoSavedBackendStatsHash saves the stat counters\n * for this backend into the saved backend stats hash table.\n *\n * So, this is only supposed to be called when a backend exits.\n *\n * Also, we do our best to avoid throwing errors in this function because\n * this function is called when a backend is exiting and throwing errors\n * at that point will cause the backend to crash.\n */\nvoid\nSaveBackendStatsIntoSavedBackendStatsHash(void)\n{\n\tif (!EnableStatCounters)\n\t{\n\t\treturn;\n\t}\n\n\t/* just to be on the safe side */\n\tif (!EnsureStatCountersShmemInitDone())\n\t{\n\t\treturn;\n\t}\n\n\tOid databaseId = MyDatabaseId;\n\n\tLWLockAcquire(*SharedSavedBackendStatsHashLock, LW_SHARED);\n\n\tSavedBackendStatsHashEntry *dbSavedBackendStatsEntry =\n\t\t(SavedBackendStatsHashEntry *) hash_search(\n\t\t\tSharedSavedBackendStatsHash,\n\t\t\t(void *) &databaseId,\n\t\t\tHASH_FIND,\n\t\t\tNULL);\n\tif (!dbSavedBackendStatsEntry)\n\t{\n\t\t/* promote the lock to exclusive to insert the new entry for this database */\n\t\tLWLockRelease(*SharedSavedBackendStatsHashLock);\n\t\tLWLockAcquire(*SharedSavedBackendStatsHashLock, LW_EXCLUSIVE);\n\n\t\tdbSavedBackendStatsEntry =\n\t\t\tSavedBackendStatsHashEntryCreateIfNotExists(databaseId);\n\n\t\tLWLockRelease(*SharedSavedBackendStatsHashLock);\n\n\t\tif (!dbSavedBackendStatsEntry)\n\t\t{\n\t\t\t/*\n\t\t\t * Couldn't allocate a new hash entry because we're out of\n\t\t\t * (shared) memory. In that case, we just log a warning and\n\t\t\t * return, instead of throwing an error due to the reasons\n\t\t\t * mentioned in function's comment.\n\t\t\t */\n\t\t\tereport(WARNING,\n\t\t\t\t\t(errcode(ERRCODE_OUT_OF_MEMORY),\n\t\t\t\t\t errmsg(\"failed to allocate saved backend stats hash entry\")));\n\t\t\treturn;\n\t\t}\n\n\t\t/* re-acquire the shared lock */\n\t\tLWLockAcquire(*SharedSavedBackendStatsHashLock, LW_SHARED);\n\t}\n\n\tint myBackendSlotIdx = getProcNo_compat(MyProc);\n\tBackendStatsSlot *myBackendStatsSlot =\n\t\t&SharedBackendStatsSlotArray[myBackendSlotIdx];\n\n\tSpinLockAcquire(&dbSavedBackendStatsEntry->mutex);\n\n\tfor (int statIdx = 0; statIdx < N_CITUS_STAT_COUNTERS; statIdx++)\n\t{\n\t\tdbSavedBackendStatsEntry->counters[statIdx] +=\n\t\t\tpg_atomic_read_u64(&myBackendStatsSlot->counters[statIdx]);\n\n\t\t/*\n\t\t * Given that this function is only called when a backend exits, later on\n\t\t * another backend might be assigned to the same slot. So, we reset each\n\t\t * stat counter of this slot to 0 after saving it.\n\t\t */\n\t\tpg_atomic_write_u64(&myBackendStatsSlot->counters[statIdx], 0);\n\t}\n\n\tSpinLockRelease(&dbSavedBackendStatsEntry->mutex);\n\n\tLWLockRelease(*SharedSavedBackendStatsHashLock);\n}\n\n\n/*\n * StatCountersShmemInit initializes the shared memory data structures used\n * for keeping track of stat counters.\n */\nstatic void\nStatCountersShmemInit(void)\n{\n\tif (prev_shmem_startup_hook != NULL)\n\t{\n\t\tprev_shmem_startup_hook();\n\t}\n\n\tLWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);\n\n\tbool sharedBackendStatsSlotArrayAlreadyInit = false;\n\tSharedBackendStatsSlotArray = (BackendStatsSlot *)\n\t\t\t\t\t\t\t\t  ShmemInitStruct(\n\t\t\"Citus Shared Backend Stats Slot Array\",\n\t\tSharedBackendStatsSlotArrayShmemSize(),\n\t\t&sharedBackendStatsSlotArrayAlreadyInit);\n\n\tbool sharedSavedBackendStatsHashLockAlreadyInit = false;\n\tSharedSavedBackendStatsHashLock = ShmemInitStruct(\n\t\tSAVED_BACKEND_STATS_HASH_LOCK_TRANCHE_NAME,\n\t\tsizeof(LWLockId),\n\t\t&\n\t\tsharedSavedBackendStatsHashLockAlreadyInit);\n\n\tHASHCTL hashInfo = {\n\t\t.keysize = sizeof(Oid),\n\t\t.entrysize = sizeof(SavedBackendStatsHashEntry),\n\t\t.hash = oid_hash,\n\t};\n\tSharedSavedBackendStatsHash = ShmemInitHash(\"Citus Shared Saved Backend Stats Hash\",\n\t\t\t\t\t\t\t\t\t\t\t\tSAVED_BACKEND_STATS_HASH_INIT_DATABASES,\n\t\t\t\t\t\t\t\t\t\t\t\tSAVED_BACKEND_STATS_HASH_MAX_DATABASES,\n\t\t\t\t\t\t\t\t\t\t\t\t&hashInfo,\n\t\t\t\t\t\t\t\t\t\t\t\tHASH_ELEM | HASH_FUNCTION);\n\n\tAssert(sharedBackendStatsSlotArrayAlreadyInit ==\n\t\t   sharedSavedBackendStatsHashLockAlreadyInit);\n\tif (!sharedBackendStatsSlotArrayAlreadyInit)\n\t{\n\t\tfor (int backendSlotIdx = 0; backendSlotIdx < MaxBackends; ++backendSlotIdx)\n\t\t{\n\t\t\tBackendStatsSlot *backendStatsSlot =\n\t\t\t\t&SharedBackendStatsSlotArray[backendSlotIdx];\n\n\t\t\tfor (int statIdx = 0; statIdx < N_CITUS_STAT_COUNTERS; statIdx++)\n\t\t\t{\n\t\t\t\tpg_atomic_init_u64(&backendStatsSlot->counters[statIdx], 0);\n\t\t\t}\n\t\t}\n\n\t\t*SharedSavedBackendStatsHashLock = &(\n\t\t\tGetNamedLWLockTranche(\n\t\t\t\tSAVED_BACKEND_STATS_HASH_LOCK_TRANCHE_NAME)\n\t\t\t)->lock;\n\t}\n\n\tLWLockRelease(AddinShmemInitLock);\n\n\t/*\n\t * At this point, they should have been set to non-null values already,\n\t * but we still check them just to be sure.\n\t */\n\tif (SharedBackendStatsSlotArray &&\n\t\tSharedSavedBackendStatsHashLock &&\n\t\tSharedSavedBackendStatsHash)\n\t{\n\t\tStatCountersShmemInitDone = true;\n\t}\n}\n\n\n/*\n * SharedBackendStatsSlotArrayShmemSize returns the size of the shared\n * backend stats slot array.\n */\nstatic Size\nSharedBackendStatsSlotArrayShmemSize(void)\n{\n\treturn mul_size(sizeof(BackendStatsSlot), MaxBackends);\n}\n\n\n/*\n * CollectActiveBackendStatsIntoHTAB aggregates the stat counters for the\n * given database id from all the active backends into the databaseStats\n * hash table. The function doesn't actually filter the slots of active\n * backends but it's just fine to read the stat counters from all because\n * exited backends anyway zero out their stat counters when they exit.\n *\n * If the database id is InvalidOid, then all the active backends will be\n * considered regardless of the database they are connected to.\n *\n * Otherwise, if the database id is different than InvalidOid, then only\n * the active backends whose PGPROC->databaseId is the same as the given\n * database id will be considered, if any.\n */\nstatic void\nCollectActiveBackendStatsIntoHTAB(Oid databaseId, HTAB *databaseStats)\n{\n\tfor (int backendSlotIdx = 0; backendSlotIdx < MaxBackends; ++backendSlotIdx)\n\t{\n\t\tPGPROC *backendProc = GetPGProcByNumber(backendSlotIdx);\n\n\t\tif (backendProc->pid == 0)\n\t\t{\n\t\t\t/* unused slot */\n\t\t\tcontinue;\n\t\t}\n\n\t\tOid procDatabaseId = backendProc->databaseId;\n\t\tif (procDatabaseId == InvalidOid)\n\t\t{\n\t\t\t/*\n\t\t\t * Not connected to any database, something like logical replication\n\t\t\t * launcher, autovacuum launcher or such.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (databaseId != InvalidOid && databaseId != procDatabaseId)\n\t\t{\n\t\t\t/* not a database we are interested in */\n\t\t\tcontinue;\n\t\t}\n\n\t\tDatabaseStatsHashEntry *dbStatsEntry =\n\t\t\tDatabaseStatsHashEntryFindOrCreate(procDatabaseId, databaseStats);\n\n\t\tBackendStatsSlot *backendStatsSlot =\n\t\t\t&SharedBackendStatsSlotArray[backendSlotIdx];\n\n\t\tfor (int statIdx = 0; statIdx < N_CITUS_STAT_COUNTERS; statIdx++)\n\t\t{\n\t\t\tdbStatsEntry->counters[statIdx] +=\n\t\t\t\tpg_atomic_read_u64(&backendStatsSlot->counters[statIdx]);\n\t\t}\n\t}\n}\n\n\n/*\n * CollectSavedBackendStatsIntoHTAB fetches the saved stat counters and\n * resetTimestamp for the given database id from the saved backend stats\n * hash table and saves them into the databaseStats hash table.\n *\n * If the database id is InvalidOid, then all the databases that present\n * in the saved backend stats hash table will be considered.\n *\n * Otherwise, if the database id is different than InvalidOid, then only\n * the entry that belongs to given database will be considered, if there\n * is such an entry.\n */\nstatic void\nCollectSavedBackendStatsIntoHTAB(Oid databaseId, HTAB *databaseStats)\n{\n\tLWLockAcquire(*SharedSavedBackendStatsHashLock, LW_SHARED);\n\n\tif (databaseId != InvalidOid)\n\t{\n\t\tSavedBackendStatsHashEntry *dbSavedBackendStatsEntry =\n\t\t\t(SavedBackendStatsHashEntry *) hash_search(\n\t\t\t\tSharedSavedBackendStatsHash,\n\t\t\t\t(void *) &databaseId,\n\t\t\t\tHASH_FIND,\n\t\t\t\tNULL);\n\n\t\tif (dbSavedBackendStatsEntry)\n\t\t{\n\t\t\tDatabaseStatsHashEntry *dbStatsEntry =\n\t\t\t\tDatabaseStatsHashEntryFindOrCreate(databaseId, databaseStats);\n\n\t\t\tSpinLockAcquire(&dbSavedBackendStatsEntry->mutex);\n\n\t\t\tfor (int statIdx = 0; statIdx < N_CITUS_STAT_COUNTERS; statIdx++)\n\t\t\t{\n\t\t\t\tdbStatsEntry->counters[statIdx] +=\n\t\t\t\t\tdbSavedBackendStatsEntry->counters[statIdx];\n\t\t\t}\n\n\t\t\tdbStatsEntry->resetTimestamp =\n\t\t\t\tdbSavedBackendStatsEntry->resetTimestamp;\n\n\t\t\tSpinLockRelease(&dbSavedBackendStatsEntry->mutex);\n\t\t}\n\t}\n\telse\n\t{\n\t\tHASH_SEQ_STATUS hashSeqStatus;\n\t\thash_seq_init(&hashSeqStatus, SharedSavedBackendStatsHash);\n\n\t\tSavedBackendStatsHashEntry *dbSavedBackendStatsEntry = NULL;\n\t\twhile ((dbSavedBackendStatsEntry = hash_seq_search(&hashSeqStatus)) != NULL)\n\t\t{\n\t\t\tDatabaseStatsHashEntry *dbStatsEntry =\n\t\t\t\tDatabaseStatsHashEntryFindOrCreate(dbSavedBackendStatsEntry->databaseId,\n\t\t\t\t\t\t\t\t\t\t\t\t   databaseStats);\n\n\t\t\tSpinLockAcquire(&dbSavedBackendStatsEntry->mutex);\n\n\t\t\tfor (int statIdx = 0; statIdx < N_CITUS_STAT_COUNTERS; statIdx++)\n\t\t\t{\n\t\t\t\tdbStatsEntry->counters[statIdx] +=\n\t\t\t\t\tdbSavedBackendStatsEntry->counters[statIdx];\n\t\t\t}\n\n\t\t\tdbStatsEntry->resetTimestamp =\n\t\t\t\tdbSavedBackendStatsEntry->resetTimestamp;\n\n\t\t\tSpinLockRelease(&dbSavedBackendStatsEntry->mutex);\n\t\t}\n\t}\n\n\tLWLockRelease(*SharedSavedBackendStatsHashLock);\n}\n\n\n/*\n * DatabaseStatsHashEntryFindOrCreate creates a new entry in databaseStats\n * hash table for the given database id if it doesn't already exist and\n * initializes it, or just returns the existing entry if it does.\n */\nstatic DatabaseStatsHashEntry *\nDatabaseStatsHashEntryFindOrCreate(Oid databaseId, HTAB *databaseStats)\n{\n\tbool found = false;\n\tDatabaseStatsHashEntry *dbStatsEntry = (DatabaseStatsHashEntry *)\n\t\t\t\t\t\t\t\t\t\t   hash_search(databaseStats, &databaseId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER, &found);\n\n\tif (!found)\n\t{\n\t\tMemSet(dbStatsEntry->counters, 0, sizeof(StatCounters));\n\t\tdbStatsEntry->resetTimestamp = 0;\n\t}\n\n\treturn dbStatsEntry;\n}\n\n\n/*\n * StoreDatabaseStatsIntoTupStore stores the database stats from the\n * databaseStats hash table into given tuple store.\n */\nstatic void\nStoreDatabaseStatsIntoTupStore(HTAB *databaseStats, Tuplestorestate *tupleStore,\n\t\t\t\t\t\t\t   TupleDesc tupleDescriptor)\n{\n\tHASH_SEQ_STATUS hashSeqStatus;\n\thash_seq_init(&hashSeqStatus, databaseStats);\n\n\tDatabaseStatsHashEntry *dbStatsEntry = NULL;\n\twhile ((dbStatsEntry = hash_seq_search(&hashSeqStatus)) != NULL)\n\t{\n\t\t/* +2 for database_id (first) and the stats_reset (last) column */\n\t\tDatum values[N_CITUS_STAT_COUNTERS + 2] = { 0 };\n\t\tbool isNulls[N_CITUS_STAT_COUNTERS + 2] = { 0 };\n\n\t\tvalues[0] = ObjectIdGetDatum(dbStatsEntry->databaseId);\n\n\t\tfor (int statIdx = 0; statIdx < N_CITUS_STAT_COUNTERS; statIdx++)\n\t\t{\n\t\t\tuint64 statCounter = dbStatsEntry->counters[statIdx];\n\t\t\tvalues[statIdx + 1] = UInt64GetDatum(statCounter);\n\t\t}\n\n\t\t/* set stats_reset column to NULL if it was never reset */\n\t\tif (dbStatsEntry->resetTimestamp == 0)\n\t\t{\n\t\t\tisNulls[N_CITUS_STAT_COUNTERS + 1] = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvalues[N_CITUS_STAT_COUNTERS + 1] =\n\t\t\t\tTimestampTzGetDatum(dbStatsEntry->resetTimestamp);\n\t\t}\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);\n\t}\n}\n\n\n/*\n * ResetActiveBackendStats resets the stat counters for the given database\n * id for all the active backends. The function doesn't actually filter the\n * slots of active backends but it's just fine to reset the stat counters\n * for all because doing so just means resetting the stat counters for\n * exited backends once again, which were already reset when they exited.\n *\n * Only active backends whose PGPROC->databaseId is the same as the given\n * database id will be considered, if any.\n *\n * Returns true if any active backend was found.\n */\nstatic bool\nResetActiveBackendStats(Oid databaseId)\n{\n\tbool foundAny = false;\n\n\tfor (int backendSlotIdx = 0; backendSlotIdx < MaxBackends; ++backendSlotIdx)\n\t{\n\t\tPGPROC *backendProc = GetPGProcByNumber(backendSlotIdx);\n\n\t\tif (backendProc->pid == 0)\n\t\t{\n\t\t\t/* unused slot */\n\t\t\tcontinue;\n\t\t}\n\n\t\tOid procDatabaseId = backendProc->databaseId;\n\t\tif (procDatabaseId == InvalidOid)\n\t\t{\n\t\t\t/*\n\t\t\t * not connected to any database, something like logical replication\n\t\t\t * launcher, autovacuum launcher, etc.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (databaseId != procDatabaseId)\n\t\t{\n\t\t\t/* not a database we are interested in */\n\t\t\tcontinue;\n\t\t}\n\n\t\tfoundAny = true;\n\n\t\tBackendStatsSlot *backendStatsSlot =\n\t\t\t&SharedBackendStatsSlotArray[backendSlotIdx];\n\n\t\tfor (int statIdx = 0; statIdx < N_CITUS_STAT_COUNTERS; statIdx++)\n\t\t{\n\t\t\tpg_atomic_write_u64(&backendStatsSlot->counters[statIdx], 0);\n\t\t}\n\t}\n\n\treturn foundAny;\n}\n\n\n/*\n * ResetSavedBackendStats resets the saved stat counters for the given\n * database id and sets the resetTimestamp for it to the current timestamp.\n *\n * If force is true, then we first make sure that we have an entry for\n * the given database id in the saved backend stats hash table.\n */\nstatic void\nResetSavedBackendStats(Oid databaseId, bool force)\n{\n\tLWLockAcquire(*SharedSavedBackendStatsHashLock, LW_SHARED);\n\n\tSavedBackendStatsHashEntry *dbSavedBackendStatsEntry =\n\t\t(SavedBackendStatsHashEntry *) hash_search(\n\t\t\tSharedSavedBackendStatsHash,\n\t\t\t(void *) &databaseId,\n\t\t\tHASH_FIND,\n\t\t\tNULL);\n\n\tif (!dbSavedBackendStatsEntry && force)\n\t{\n\t\t/* promote the lock to exclusive to insert the new entry for this database */\n\t\tLWLockRelease(*SharedSavedBackendStatsHashLock);\n\t\tLWLockAcquire(*SharedSavedBackendStatsHashLock, LW_EXCLUSIVE);\n\n\t\tdbSavedBackendStatsEntry =\n\t\t\tSavedBackendStatsHashEntryCreateIfNotExists(databaseId);\n\n\t\tLWLockRelease(*SharedSavedBackendStatsHashLock);\n\n\t\tif (!dbSavedBackendStatsEntry)\n\t\t{\n\t\t\t/*\n\t\t\t * Couldn't allocate a new hash entry because we're out of\n\t\t\t * (shared) memory.\n\t\t\t */\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_OUT_OF_MEMORY),\n\t\t\t\t\t errmsg(\"failed to allocate saved backend stats hash entry\")));\n\t\t\treturn;\n\t\t}\n\n\t\t/* re-acquire the shared lock */\n\t\tLWLockAcquire(*SharedSavedBackendStatsHashLock, LW_SHARED);\n\t}\n\n\t/*\n\t * Actually reset the stat counters for the exited backends and set\n\t * the resetTimestamp to the current timestamp if we already had\n\t * an entry for it or if we just created it.\n\t */\n\tif (dbSavedBackendStatsEntry)\n\t{\n\t\tSpinLockAcquire(&dbSavedBackendStatsEntry->mutex);\n\n\t\tmemset(dbSavedBackendStatsEntry->counters, 0, sizeof(StatCounters));\n\t\tdbSavedBackendStatsEntry->resetTimestamp = GetCurrentTimestamp();\n\n\t\tSpinLockRelease(&dbSavedBackendStatsEntry->mutex);\n\t}\n\n\tLWLockRelease(*SharedSavedBackendStatsHashLock);\n}\n\n\n/*\n * SavedBackendStatsHashEntryCreateIfNotExists creates a new entry in the\n * saved backend stats hash table for the given database id if it doesn't\n * already exist and initializes it.\n *\n * Assumes that the caller has exclusive access to the hash table since it\n * performs HASH_ENTER_NULL.\n *\n * Returns NULL if the entry didn't exist and couldn't be allocated since\n * we're out of (shared) memory.\n */\nstatic SavedBackendStatsHashEntry *\nSavedBackendStatsHashEntryCreateIfNotExists(Oid databaseId)\n{\n\tbool found = false;\n\tSavedBackendStatsHashEntry *dbSavedBackendStatsEntry =\n\t\t(SavedBackendStatsHashEntry *) hash_search(SharedSavedBackendStatsHash,\n\t\t\t\t\t\t\t\t\t\t\t\t   (void *) &databaseId,\n\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER_NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t   &found);\n\n\tif (!dbSavedBackendStatsEntry)\n\t{\n\t\t/*\n\t\t * As we provided HASH_ENTER_NULL, returning NULL means OOM.\n\t\t * In that case, we return and let the caller decide what to do.\n\t\t */\n\t\treturn NULL;\n\t}\n\n\tif (!found)\n\t{\n\t\tmemset(dbSavedBackendStatsEntry->counters, 0, sizeof(StatCounters));\n\n\t\tdbSavedBackendStatsEntry->resetTimestamp = 0;\n\n\t\tSpinLockInit(&dbSavedBackendStatsEntry->mutex);\n\t}\n\n\treturn dbSavedBackendStatsEntry;\n}\n"
  },
  {
    "path": "src/backend/distributed/stats/stat_tenants.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * stat_tenants.c\n *\t  Routines related to the multi tenant monitor.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <time.h>\n\n#include \"postgres.h\"\n\n#include \"unistd.h\"\n\n#include \"access/hash.h\"\n#include \"common/pg_prng.h\"\n#include \"executor/execdesc.h\"\n#include \"storage/ipc.h\"\n#include \"storage/lwlock.h\"\n#include \"storage/shmem.h\"\n#include \"sys/time.h\"\n#include \"utils/builtins.h\"\n#include \"utils/datetime.h\"\n#include \"utils/json.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/jsonbutils.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/stats/stat_tenants.h\"\n#include \"distributed/tenant_schema_metadata.h\"\n#include \"distributed/tuplestore.h\"\n\nstatic void AttributeMetricsIfApplicable(void);\n\nExecutorEnd_hook_type prev_ExecutorEnd = NULL;\n\n#define ATTRIBUTE_PREFIX \"/*{\\\"cId\\\":\"\n#define ATTRIBUTE_STRING_FORMAT \"/*{\\\"cId\\\":%d,\\\"tId\\\":%s}*/\"\n#define ATTRIBUTE_STRING_FORMAT_WITHOUT_TID \"/*{\\\"cId\\\":%d}*/\"\n#define STAT_TENANTS_COLUMNS 9\n#define ONE_QUERY_SCORE 1000000000\n\n/* this doesn't attempt dereferencing given input and is computed in compile-time, so it's safe */\n#define TENANT_STATS_SCORE_FIELD_BIT_LENGTH (sizeof(((TenantStats *) NULL)->score) * 8)\n\nstatic char AttributeToTenant[MAX_TENANT_ATTRIBUTE_LENGTH] = \"\";\nstatic CmdType AttributeToCommandType = CMD_UNKNOWN;\nstatic int AttributeToColocationGroupId = INVALID_COLOCATION_ID;\nstatic clock_t QueryStartClock = { 0 };\nstatic clock_t QueryEndClock = { 0 };\n\nstatic const char *SharedMemoryNameForMultiTenantMonitor =\n\t\"Shared memory for multi tenant monitor\";\nstatic char *MonitorTrancheName = \"Multi Tenant Monitor Tranche\";\n\nstatic shmem_startup_hook_type prev_shmem_startup_hook = NULL;\n\nstatic int CompareTenantScore(const void *leftElement, const void *rightElement);\nstatic void UpdatePeriodsIfNecessary(TenantStats *tenantStats, TimestampTz queryTime);\nstatic void ReduceScoreIfNecessary(TenantStats *tenantStats, TimestampTz queryTime);\nstatic void EvictTenantsIfNecessary(TimestampTz queryTime);\nstatic void RecordTenantStats(TenantStats *tenantStats, TimestampTz queryTime);\nstatic MultiTenantMonitor * CreateSharedMemoryForMultiTenantMonitor(void);\nstatic MultiTenantMonitor * GetMultiTenantMonitor(void);\nstatic void MultiTenantMonitorSMInit(void);\nstatic TenantStats * CreateTenantStats(MultiTenantMonitor *monitor, TimestampTz\n\t\t\t\t\t\t\t\t\t   queryTime);\nstatic void FillTenantStatsHashKey(TenantStatsHashKey *key, char *tenantAttribute, uint32\n\t\t\t\t\t\t\t\t   colocationGroupId);\nstatic TenantStats * FindTenantStats(MultiTenantMonitor *monitor);\nstatic size_t MultiTenantMonitorshmemSize(void);\nstatic char * ExtractTopComment(const char *inputString);\nstatic char * EscapeCommentChars(const char *str);\nstatic char * UnescapeCommentChars(const char *str);\n\nint StatTenantsLogLevel = CITUS_LOG_LEVEL_OFF;\nint StatTenantsPeriod = (time_t) 60;\nint StatTenantsLimit = 100;\nint StatTenantsTrack = STAT_TENANTS_TRACK_NONE;\ndouble StatTenantsSampleRateForNewTenants = 1;\n\nPG_FUNCTION_INFO_V1(citus_stat_tenants_local);\nPG_FUNCTION_INFO_V1(citus_stat_tenants_local_reset);\n\n\n/*\n * citus_stat_tenants_local finds, updates and returns the statistics for tenants.\n */\nDatum\ncitus_stat_tenants_local(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\t/*\n\t * We keep more than StatTenantsLimit tenants in our monitor.\n\t * We do this to not lose data if a tenant falls out of top StatTenantsLimit in case they need to return soon.\n\t * Normally we return StatTenantsLimit tenants but if returnAllTenants is true we return all of them.\n\t */\n\tbool returnAllTenants = PG_GETARG_BOOL(0);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\tTimestampTz monitoringTime = GetCurrentTimestamp();\n\n\tDatum values[STAT_TENANTS_COLUMNS];\n\tbool isNulls[STAT_TENANTS_COLUMNS];\n\n\tMultiTenantMonitor *monitor = GetMultiTenantMonitor();\n\n\tif (monitor == NULL)\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\tLWLockAcquire(&monitor->lock, LW_EXCLUSIVE);\n\n\tint numberOfRowsToReturn = 0;\n\tint tenantStatsCount = hash_get_num_entries(monitor->tenants);\n\tif (returnAllTenants)\n\t{\n\t\tnumberOfRowsToReturn = tenantStatsCount;\n\t}\n\telse\n\t{\n\t\tnumberOfRowsToReturn = Min(tenantStatsCount,\n\t\t\t\t\t\t\t\t   StatTenantsLimit);\n\t}\n\n\t/* Allocate an array to hold the tenants. */\n\tTenantStats **stats = palloc(tenantStatsCount *\n\t\t\t\t\t\t\t\t sizeof(TenantStats *));\n\n\tHASH_SEQ_STATUS hash_seq;\n\tTenantStats *stat;\n\n\t/* Get all the tenants from the hash table. */\n\tint j = 0;\n\thash_seq_init(&hash_seq, monitor->tenants);\n\twhile ((stat = hash_seq_search(&hash_seq)) != NULL)\n\t{\n\t\tstats[j++] = stat;\n\t\tUpdatePeriodsIfNecessary(stat, monitoringTime);\n\t\tReduceScoreIfNecessary(stat, monitoringTime);\n\t}\n\n\t/* Sort the tenants by their score. */\n\tSafeQsort(stats, j, sizeof(TenantStats *),\n\t\t\t  CompareTenantScore);\n\n\tfor (int i = 0; i < numberOfRowsToReturn; i++)\n\t{\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(isNulls, false, sizeof(isNulls));\n\n\t\tTenantStats *tenantStats = stats[i];\n\n\t\tvalues[0] = Int32GetDatum(tenantStats->key.colocationGroupId);\n\n\t\tif (tenantStats->key.tenantAttribute[0] == '\\0')\n\t\t{\n\t\t\tisNulls[1] = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvalues[1] = PointerGetDatum(cstring_to_text(\n\t\t\t\t\t\t\t\t\t\t\ttenantStats->key.tenantAttribute));\n\t\t}\n\n\t\tvalues[2] = Int32GetDatum(tenantStats->readsInThisPeriod);\n\t\tvalues[3] = Int32GetDatum(tenantStats->readsInLastPeriod);\n\t\tvalues[4] = Int32GetDatum(tenantStats->readsInThisPeriod +\n\t\t\t\t\t\t\t\t  tenantStats->writesInThisPeriod);\n\t\tvalues[5] = Int32GetDatum(tenantStats->readsInLastPeriod +\n\t\t\t\t\t\t\t\t  tenantStats->writesInLastPeriod);\n\t\tvalues[6] = Float8GetDatum(tenantStats->cpuUsageInThisPeriod);\n\t\tvalues[7] = Float8GetDatum(tenantStats->cpuUsageInLastPeriod);\n\t\tvalues[8] = Int64GetDatum(tenantStats->score);\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);\n\t}\n\n\tpfree(stats);\n\n\tLWLockRelease(&monitor->lock);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_stat_tenants_local_reset resets monitor for tenant statistics\n * on the local node.\n */\nDatum\ncitus_stat_tenants_local_reset(PG_FUNCTION_ARGS)\n{\n\tMultiTenantMonitor *monitor = GetMultiTenantMonitor();\n\n\t/* if monitor is not created yet, there is nothing to reset */\n\tif (monitor == NULL)\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\tHASH_SEQ_STATUS hash_seq;\n\tTenantStats *stats;\n\n\tLWLockAcquire(&monitor->lock, LW_EXCLUSIVE);\n\n\thash_seq_init(&hash_seq, monitor->tenants);\n\twhile ((stats = hash_seq_search(&hash_seq)) != NULL)\n\t{\n\t\thash_search(monitor->tenants, &stats->key, HASH_REMOVE, NULL);\n\t}\n\n\tLWLockRelease(&monitor->lock);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * AttributeQueryIfAnnotated checks the query annotation and if the query is annotated\n * for the tenant statistics monitoring this function records the tenant attributes.\n */\nvoid\nAttributeQueryIfAnnotated(const char *query_string, CmdType commandType)\n{\n\tif (StatTenantsTrack == STAT_TENANTS_TRACK_NONE)\n\t{\n\t\treturn;\n\t}\n\n\tAttributeToColocationGroupId = INVALID_COLOCATION_ID;\n\n\tif (query_string == NULL)\n\t{\n\t\treturn;\n\t}\n\n\tif (strncmp(ATTRIBUTE_PREFIX, query_string, strlen(ATTRIBUTE_PREFIX)) == 0)\n\t{\n\t\tchar *annotation = ExtractTopComment(query_string);\n\t\tif (annotation != NULL)\n\t\t{\n\t\t\tDatum jsonbDatum = DirectFunctionCall1(jsonb_in, PointerGetDatum(annotation));\n\n\t\t\ttext *tenantIdTextP = ExtractFieldTextP(jsonbDatum, \"tId\");\n\t\t\tchar *tenantId = NULL;\n\t\t\tif (tenantIdTextP != NULL)\n\t\t\t{\n\t\t\t\ttenantId = UnescapeCommentChars(text_to_cstring(tenantIdTextP));\n\t\t\t}\n\n\t\t\tint colocationId = ExtractFieldInt32(jsonbDatum, \"cId\",\n\t\t\t\t\t\t\t\t\t\t\t\t INVALID_COLOCATION_ID);\n\n\t\t\tAttributeTask(tenantId, colocationId, commandType);\n\t\t}\n\t}\n}\n\n\n/*\n * AttributeTask assigns the given attributes of a tenant and starts a timer\n */\nvoid\nAttributeTask(char *tenantId, int colocationId, CmdType commandType)\n{\n\tif (StatTenantsTrack == STAT_TENANTS_TRACK_NONE ||\n\t\tcolocationId == INVALID_COLOCATION_ID)\n\t{\n\t\treturn;\n\t}\n\n\tTenantStatsHashKey key = { 0 };\n\tFillTenantStatsHashKey(&key, tenantId, colocationId);\n\n\tMultiTenantMonitor *monitor = GetMultiTenantMonitor();\n\tbool found = false;\n\n\t/* Acquire the lock in shared mode to check if the tenant is already in the hash table. */\n\tLWLockAcquire(&monitor->lock, LW_SHARED);\n\n\thash_search(monitor->tenants, &key, HASH_FIND, &found);\n\n\tLWLockRelease(&monitor->lock);\n\n\t/* If the tenant is not found in the hash table, we will track the query with a probability of StatTenantsSampleRateForNewTenants. */\n\tif (!found)\n\t{\n\t\tdouble randomValue = pg_prng_double(&pg_global_prng_state);\n\t\tbool shouldTrackQuery = randomValue <= StatTenantsSampleRateForNewTenants;\n\t\tif (!shouldTrackQuery)\n\t\t{\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/*\n\t * if tenantId is NULL, it must be a schema-based tenant and\n\t * we try to get the tenantId from the colocationId to lookup schema name and use it as a tenantId\n\t */\n\tif (tenantId == NULL)\n\t{\n\t\tif (!IsTenantSchemaColocationGroup(colocationId))\n\t\t{\n\t\t\treturn;\n\t\t}\n\t}\n\n\tAttributeToColocationGroupId = colocationId;\n\tif (tenantId != NULL)\n\t{\n\t\tstrncpy_s(AttributeToTenant, MAX_TENANT_ATTRIBUTE_LENGTH, tenantId,\n\t\t\t\t  MAX_TENANT_ATTRIBUTE_LENGTH - 1);\n\t}\n\telse\n\t{\n\t\tstrcpy_s(AttributeToTenant, sizeof(AttributeToTenant), \"\");\n\t}\n\tAttributeToCommandType = commandType;\n\tQueryStartClock = clock();\n}\n\n\n/*\n * AnnotateQuery annotates the query with tenant attributes.\n * if the query has a partition key, we annotate it with the partition key value and colocationId\n * if the query doesn't have a partition key and if it's a schema-based tenant, we annotate it with the colocationId only.\n */\nchar *\nAnnotateQuery(char *queryString, Const *partitionKeyValue, int colocationId)\n{\n\tif (StatTenantsTrack == STAT_TENANTS_TRACK_NONE ||\n\t\tcolocationId == INVALID_COLOCATION_ID)\n\t{\n\t\treturn queryString;\n\t}\n\n\tStringInfo newQuery = makeStringInfo();\n\n\t/* if the query doesn't have a parititon key value, check if it is a tenant schema */\n\tif (partitionKeyValue == NULL)\n\t{\n\t\tif (IsTenantSchemaColocationGroup(colocationId))\n\t\t{\n\t\t\t/* If it is a schema-based tenant, we only annotate the query with colocationId */\n\t\t\tappendStringInfo(newQuery, ATTRIBUTE_STRING_FORMAT_WITHOUT_TID,\n\t\t\t\t\t\t\t colocationId);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* If it is not a schema-based tenant query and doesn't have a parititon key,\n\t\t\t * we don't annotate it\n\t\t\t */\n\t\t\treturn queryString;\n\t\t}\n\t}\n\telse\n\t{\n\t\t/* if the query has a partition key value, we annotate it with both tenantId and colocationId */\n\t\tchar *partitionKeyValueString = DatumToString(partitionKeyValue->constvalue,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionKeyValue->consttype);\n\n\t\tchar *commentCharsEscaped = EscapeCommentChars(partitionKeyValueString);\n\t\tStringInfo escapedSourceName = makeStringInfo();\n\t\tescape_json(escapedSourceName, commentCharsEscaped);\n\n\t\tappendStringInfo(newQuery, ATTRIBUTE_STRING_FORMAT, colocationId,\n\t\t\t\t\t\t escapedSourceName->data\n\t\t\t\t\t\t );\n\t}\n\n\tappendStringInfoString(newQuery, queryString);\n\n\treturn newQuery->data;\n}\n\n\n/*\n * CitusAttributeToEnd keeps the statistics for the tenant and calls the previously installed end hook\n * or the standard executor end function.\n */\nvoid\nCitusAttributeToEnd(QueryDesc *queryDesc)\n{\n\t/*\n\t * At the end of the Executor is the last moment we have to attribute the previous\n\t * attribution to a tenant, if applicable\n\t */\n\tAttributeMetricsIfApplicable();\n\n\t/* now call in to the previously installed hook, or the standard implementation */\n\tif (prev_ExecutorEnd)\n\t{\n\t\tprev_ExecutorEnd(queryDesc);\n\t}\n\telse\n\t{\n\t\tstandard_ExecutorEnd(queryDesc);\n\t}\n}\n\n\n/*\n * CompareTenantScore is used to sort the tenant statistics by score\n * in descending order.\n */\nstatic int\nCompareTenantScore(const void *leftElement, const void *rightElement)\n{\n\tdouble l_usage = (*(TenantStats *const *) leftElement)->score;\n\tdouble r_usage = (*(TenantStats *const *) rightElement)->score;\n\n\tif (l_usage > r_usage)\n\t{\n\t\treturn -1;\n\t}\n\telse if (l_usage < r_usage)\n\t{\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\n\n/*\n * AttributeMetricsIfApplicable updates the metrics for current tenant's statistics\n */\nstatic void\nAttributeMetricsIfApplicable()\n{\n\tif (StatTenantsTrack == STAT_TENANTS_TRACK_NONE ||\n\t\tAttributeToColocationGroupId == INVALID_COLOCATION_ID)\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * return if we are not in the top level to make sure we are not\n\t * stopping counting time for a sub-level execution\n\t */\n\tif (ExecutorLevel != 0 || PlannerLevel != 0)\n\t{\n\t\treturn;\n\t}\n\n\tQueryEndClock = clock();\n\n\tTimestampTz queryTime = GetCurrentTimestamp();\n\n\tMultiTenantMonitor *monitor = GetMultiTenantMonitor();\n\n\t/*\n\t * We need to acquire the monitor lock in shared mode to check if the tenant is\n\t * already in the monitor. If it is not, we need to acquire the lock in\n\t * exclusive mode to add the tenant to the monitor.\n\t *\n\t * We need to check again if the tenant is in the monitor after acquiring the\n\t * exclusive lock to avoid adding the tenant twice. Some other backend might\n\t * have added the tenant while we were waiting for the lock.\n\t *\n\t * After releasing the exclusive lock, we need to acquire the lock in shared\n\t * mode to update the tenant's statistics. We need to check again if the tenant\n\t * is in the monitor after acquiring the shared lock because some other backend\n\t * might have removed the tenant while we were waiting for the lock.\n\t */\n\tLWLockAcquire(&monitor->lock, LW_SHARED);\n\n\tTenantStats *tenantStats = FindTenantStats(monitor);\n\n\tif (tenantStats != NULL)\n\t{\n\t\tSpinLockAcquire(&tenantStats->lock);\n\n\t\tUpdatePeriodsIfNecessary(tenantStats, queryTime);\n\t\tReduceScoreIfNecessary(tenantStats, queryTime);\n\t\tRecordTenantStats(tenantStats, queryTime);\n\n\t\tSpinLockRelease(&tenantStats->lock);\n\t}\n\telse\n\t{\n\t\tLWLockRelease(&monitor->lock);\n\n\t\tLWLockAcquire(&monitor->lock, LW_EXCLUSIVE);\n\t\ttenantStats = FindTenantStats(monitor);\n\n\t\tif (tenantStats == NULL)\n\t\t{\n\t\t\ttenantStats = CreateTenantStats(monitor, queryTime);\n\t\t}\n\n\t\tLWLockRelease(&monitor->lock);\n\n\t\tLWLockAcquire(&monitor->lock, LW_SHARED);\n\t\ttenantStats = FindTenantStats(monitor);\n\t\tif (tenantStats != NULL)\n\t\t{\n\t\t\tSpinLockAcquire(&tenantStats->lock);\n\n\t\t\tUpdatePeriodsIfNecessary(tenantStats, queryTime);\n\t\t\tReduceScoreIfNecessary(tenantStats, queryTime);\n\t\t\tRecordTenantStats(tenantStats, queryTime);\n\n\t\t\tSpinLockRelease(&tenantStats->lock);\n\t\t}\n\t}\n\tLWLockRelease(&monitor->lock);\n\n\tAttributeToColocationGroupId = INVALID_COLOCATION_ID;\n}\n\n\n/*\n * UpdatePeriodsIfNecessary moves the query counts to previous periods if a enough time has passed.\n *\n * If 1 period has passed after the latest query, this function moves this period's counts to the last period\n * and cleans this period's statistics.\n *\n * If 2 or more periods has passed after the last query, this function cleans all both this and last period's\n * statistics.\n */\nstatic void\nUpdatePeriodsIfNecessary(TenantStats *tenantStats, TimestampTz queryTime)\n{\n\tlong long int periodInMicroSeconds = StatTenantsPeriod * USECS_PER_SEC;\n\tlong long int periodInMilliSeconds = StatTenantsPeriod * 1000;\n\tTimestampTz periodStart = queryTime - (queryTime % periodInMicroSeconds);\n\n\t/*\n\t * If the last query in this tenant was before the start of current period\n\t * but there are some query count for this period we move them to the last period.\n\t */\n\tif (tenantStats->lastQueryTime < periodStart &&\n\t\t(tenantStats->writesInThisPeriod || tenantStats->readsInThisPeriod))\n\t{\n\t\ttenantStats->writesInLastPeriod = tenantStats->writesInThisPeriod;\n\t\ttenantStats->writesInThisPeriod = 0;\n\n\t\ttenantStats->readsInLastPeriod = tenantStats->readsInThisPeriod;\n\t\ttenantStats->readsInThisPeriod = 0;\n\n\t\ttenantStats->cpuUsageInLastPeriod = tenantStats->cpuUsageInThisPeriod;\n\t\ttenantStats->cpuUsageInThisPeriod = 0;\n\t}\n\n\t/*\n\t * If the last query is more than two periods ago, we clean the last period counts too.\n\t */\n\tif (TimestampDifferenceExceeds(tenantStats->lastQueryTime, periodStart,\n\t\t\t\t\t\t\t\t   periodInMilliSeconds))\n\t{\n\t\ttenantStats->writesInLastPeriod = 0;\n\n\t\ttenantStats->readsInLastPeriod = 0;\n\n\t\ttenantStats->cpuUsageInLastPeriod = 0;\n\t}\n}\n\n\n/*\n * ReduceScoreIfNecessary reduces the tenant score only if it is necessary.\n *\n * We halve the tenants' scores after each period. This function checks the number of\n * periods that passed after the lsat score reduction and reduces the score accordingly.\n */\nstatic void\nReduceScoreIfNecessary(TenantStats *tenantStats, TimestampTz queryTime)\n{\n\tlong long int periodInMicroSeconds = StatTenantsPeriod * USECS_PER_SEC;\n\tTimestampTz periodStart = queryTime - (queryTime % periodInMicroSeconds);\n\n\t/*\n\t * With each query we increase the score of tenant by ONE_QUERY_SCORE.\n\t * After one period we halve the scores.\n\t *\n\t * Here we calculate how many periods passed after the last time we did score reduction\n\t * If the latest score reduction was in this period this number should be 0,\n\t * if it was in the last period this number should be 1 and so on.\n\t */\n\tint periodCountAfterLastScoreReduction = (periodStart -\n\t\t\t\t\t\t\t\t\t\t\t  tenantStats->lastScoreReduction +\n\t\t\t\t\t\t\t\t\t\t\t  periodInMicroSeconds - 1) /\n\t\t\t\t\t\t\t\t\t\t\t periodInMicroSeconds;\n\n\t/*\n\t * This should not happen but let's make sure\n\t */\n\tif (periodCountAfterLastScoreReduction < 0)\n\t{\n\t\tperiodCountAfterLastScoreReduction = 0;\n\t}\n\n\t/*\n\t * If the last score reduction was not in this period we do score reduction now.\n\t */\n\tif (periodCountAfterLastScoreReduction > 0)\n\t{\n\t\ttenantStats->lastScoreReduction = queryTime;\n\n\t\t/* addtional check to avoid undefined behavior */\n\t\ttenantStats->score = (periodCountAfterLastScoreReduction <\n\t\t\t\t\t\t\t  TENANT_STATS_SCORE_FIELD_BIT_LENGTH)\n\t\t\t\t\t\t\t ? tenantStats->score >> periodCountAfterLastScoreReduction\n\t\t\t\t\t\t\t : 0;\n\t}\n}\n\n\n/*\n * EvictTenantsIfNecessary sorts and evicts the tenants if the tenant count is more than or\n * equal to 3 * StatTenantsLimit.\n */\nstatic void\nEvictTenantsIfNecessary(TimestampTz queryTime)\n{\n\tMultiTenantMonitor *monitor = GetMultiTenantMonitor();\n\n\t/*\n\t * We keep up to StatTenantsLimit * 3 tenants instead of StatTenantsLimit,\n\t * so we don't lose data immediately after a tenant is out of top StatTenantsLimit\n\t *\n\t * Every time tenant count hits StatTenantsLimit * 3, we reduce it back to StatTenantsLimit * 2.\n\t */\n\tlong tenantStatsCount = hash_get_num_entries(monitor->tenants);\n\tif (tenantStatsCount >= StatTenantsLimit * 3)\n\t{\n\t\tHASH_SEQ_STATUS hash_seq;\n\t\tTenantStats *stat;\n\t\tTenantStats **stats = palloc(tenantStatsCount *\n\t\t\t\t\t\t\t\t\t sizeof(TenantStats *));\n\n\t\tint i = 0;\n\t\thash_seq_init(&hash_seq, monitor->tenants);\n\t\twhile ((stat = hash_seq_search(&hash_seq)) != NULL)\n\t\t{\n\t\t\tstats[i++] = stat;\n\t\t}\n\n\t\tSafeQsort(stats, i, sizeof(TenantStats *), CompareTenantScore);\n\n\t\tfor (i = StatTenantsLimit * 2; i < tenantStatsCount; i++)\n\t\t{\n\t\t\thash_search(monitor->tenants, &stats[i]->key, HASH_REMOVE, NULL);\n\t\t}\n\n\t\tpfree(stats);\n\t}\n}\n\n\n/*\n * RecordTenantStats records the query statistics for the tenant.\n */\nstatic void\nRecordTenantStats(TenantStats *tenantStats, TimestampTz queryTime)\n{\n\tif (tenantStats->score < LLONG_MAX - ONE_QUERY_SCORE)\n\t{\n\t\ttenantStats->score += ONE_QUERY_SCORE;\n\t}\n\telse\n\t{\n\t\ttenantStats->score = LLONG_MAX;\n\t}\n\n\tif (AttributeToCommandType == CMD_SELECT)\n\t{\n\t\ttenantStats->readsInThisPeriod++;\n\t}\n\telse if (AttributeToCommandType == CMD_UPDATE ||\n\t\t\t AttributeToCommandType == CMD_INSERT ||\n\t\t\t AttributeToCommandType == CMD_DELETE)\n\t{\n\t\ttenantStats->writesInThisPeriod++;\n\t}\n\n\tdouble queryCpuTime = ((double) (QueryEndClock - QueryStartClock)) / CLOCKS_PER_SEC;\n\ttenantStats->cpuUsageInThisPeriod += queryCpuTime;\n\n\ttenantStats->lastQueryTime = queryTime;\n}\n\n\n/*\n * CreateSharedMemoryForMultiTenantMonitor creates a dynamic shared memory segment for multi tenant monitor.\n */\nstatic MultiTenantMonitor *\nCreateSharedMemoryForMultiTenantMonitor()\n{\n\tbool found = false;\n\tMultiTenantMonitor *monitor = ShmemInitStruct(SharedMemoryNameForMultiTenantMonitor,\n\t\t\t\t\t\t\t\t\t\t\t\t  MultiTenantMonitorshmemSize(),\n\t\t\t\t\t\t\t\t\t\t\t\t  &found);\n\tif (found)\n\t{\n\t\treturn monitor;\n\t}\n\n\tmonitor->namedLockTranche.trancheId = LWLockNewTrancheId();\n\tmonitor->namedLockTranche.trancheName = MonitorTrancheName;\n\n\tLWLockRegisterTranche(monitor->namedLockTranche.trancheId,\n\t\t\t\t\t\t  monitor->namedLockTranche.trancheName);\n\tLWLockInitialize(&monitor->lock, monitor->namedLockTranche.trancheId);\n\n\tHASHCTL info;\n\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(TenantStatsHashKey);\n\tinfo.entrysize = sizeof(TenantStats);\n\n\tmonitor->tenants = ShmemInitHash(\"citus_stats_tenants hash\",\n\t\t\t\t\t\t\t\t\t StatTenantsLimit * 3, StatTenantsLimit * 3,\n\t\t\t\t\t\t\t\t\t &info, HASH_ELEM |\n\t\t\t\t\t\t\t\t\t HASH_SHARED_MEM | HASH_BLOBS);\n\n\treturn monitor;\n}\n\n\n/*\n * GetMultiTenantMonitor returns the data structure for multi tenant monitor.\n */\nstatic MultiTenantMonitor *\nGetMultiTenantMonitor()\n{\n\tbool found = false;\n\tMultiTenantMonitor *monitor = ShmemInitStruct(SharedMemoryNameForMultiTenantMonitor,\n\t\t\t\t\t\t\t\t\t\t\t\t  MultiTenantMonitorshmemSize(),\n\t\t\t\t\t\t\t\t\t\t\t\t  &found);\n\n\tif (!found)\n\t{\n\t\telog(WARNING, \"monitor not found\");\n\t\treturn NULL;\n\t}\n\n\treturn monitor;\n}\n\n\n/*\n * InitializeMultiTenantMonitorSMHandleManagement sets up the shared memory startup hook\n * so that the multi tenant monitor can be initialized and stored in shared memory.\n */\nvoid\nInitializeMultiTenantMonitorSMHandleManagement()\n{\n\tprev_shmem_startup_hook = shmem_startup_hook;\n\tshmem_startup_hook = MultiTenantMonitorSMInit;\n}\n\n\n/*\n * MultiTenantMonitorSMInit initializes the shared memory for MultiTenantMonitorSMData.\n */\nstatic void\nMultiTenantMonitorSMInit()\n{\n\tCreateSharedMemoryForMultiTenantMonitor();\n\n\tif (prev_shmem_startup_hook != NULL)\n\t{\n\t\tprev_shmem_startup_hook();\n\t}\n}\n\n\n/*\n * CreateTenantStats creates the data structure for a tenant's statistics.\n *\n * Calling this function should be protected by the monitor->lock in LW_EXCLUSIVE mode.\n */\nstatic TenantStats *\nCreateTenantStats(MultiTenantMonitor *monitor, TimestampTz queryTime)\n{\n\t/*\n\t * If the tenant count reached 3 * StatTenantsLimit, we evict the tenants\n\t * with the lowest score.\n\t */\n\tEvictTenantsIfNecessary(queryTime);\n\n\tTenantStatsHashKey key = { 0 };\n\tFillTenantStatsHashKey(&key, AttributeToTenant, AttributeToColocationGroupId);\n\n\tTenantStats *stats = (TenantStats *) hash_search(monitor->tenants, &key,\n\t\t\t\t\t\t\t\t\t\t\t\t\t HASH_ENTER, NULL);\n\n\tstats->writesInLastPeriod = 0;\n\tstats->writesInThisPeriod = 0;\n\tstats->readsInLastPeriod = 0;\n\tstats->readsInThisPeriod = 0;\n\tstats->cpuUsageInLastPeriod = 0;\n\tstats->cpuUsageInThisPeriod = 0;\n\tstats->score = 0;\n\tstats->lastScoreReduction = 0;\n\n\tSpinLockInit(&stats->lock);\n\n\treturn stats;\n}\n\n\n/*\n * FindTenantStats finds the current tenant's statistics.\n */\nstatic TenantStats *\nFindTenantStats(MultiTenantMonitor *monitor)\n{\n\tTenantStatsHashKey key = { 0 };\n\tFillTenantStatsHashKey(&key, AttributeToTenant, AttributeToColocationGroupId);\n\n\tTenantStats *stats = (TenantStats *) hash_search(monitor->tenants, &key,\n\t\t\t\t\t\t\t\t\t\t\t\t\t HASH_FIND, NULL);\n\n\treturn stats;\n}\n\n\nstatic void\nFillTenantStatsHashKey(TenantStatsHashKey *key, char *tenantAttribute, uint32\n\t\t\t\t\t   colocationGroupId)\n{\n\tmemset(key->tenantAttribute, 0, MAX_TENANT_ATTRIBUTE_LENGTH);\n\n\tif (tenantAttribute != NULL)\n\t{\n\t\tstrlcpy(key->tenantAttribute, tenantAttribute, MAX_TENANT_ATTRIBUTE_LENGTH);\n\t}\n\n\tkey->colocationGroupId = colocationGroupId;\n}\n\n\n/*\n * MultiTenantMonitorshmemSize calculates the size of the multi tenant monitor using\n * StatTenantsLimit parameter.\n */\nstatic size_t\nMultiTenantMonitorshmemSize(void)\n{\n\tSize size = sizeof(MultiTenantMonitor);\n\tsize = add_size(size, mul_size(sizeof(TenantStats), StatTenantsLimit * 3));\n\n\treturn size;\n}\n\n\n/*\n * ExtractTopComment extracts the top-level multi-line comment from a given input string.\n */\nstatic char *\nExtractTopComment(const char *inputString)\n{\n\tint commentCharsLength = 2;\n\tint inputStringLen = strlen(inputString);\n\tif (inputStringLen < commentCharsLength)\n\t{\n\t\treturn NULL;\n\t}\n\n\tconst char *commentStartChars = \"/*\";\n\tconst char *commentEndChars = \"*/\";\n\n\t/* If query doesn't start with a comment, return NULL */\n\tif (strstr(inputString, commentStartChars) != inputString)\n\t{\n\t\treturn NULL;\n\t}\n\n\tStringInfo commentData = makeStringInfo();\n\n\t/* Skip the comment start characters */\n\tconst char *commentStart = inputString + commentCharsLength;\n\n\t/* Find the first comment end character */\n\tconst char *commentEnd = strstr(commentStart, commentEndChars);\n\tif (commentEnd == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\t/* Append the comment to the StringInfo buffer */\n\tint commentLength = commentEnd - commentStart;\n\tappendStringInfo(commentData, \"%.*s\", commentLength, commentStart);\n\n\t/* Return the extracted comment */\n\treturn commentData->data;\n}\n\n\n/*  EscapeCommentChars adds a backslash before each occurrence of '*' or '/' in the input string */\nstatic char *\nEscapeCommentChars(const char *str)\n{\n\tint originalStringLength = strlen(str);\n\tStringInfo escapedString = makeStringInfo();\n\n\tfor (int originalStringIndex = 0; originalStringIndex < originalStringLength;\n\t\t originalStringIndex++)\n\t{\n\t\tif (str[originalStringIndex] == '*' || str[originalStringIndex] == '/')\n\t\t{\n\t\t\tappendStringInfoChar(escapedString, '\\\\');\n\t\t}\n\n\t\tappendStringInfoChar(escapedString, str[originalStringIndex]);\n\t}\n\n\treturn escapedString->data;\n}\n\n\n/*  UnescapeCommentChars removes the backslash that precedes '*' or '/' in the input string. */\nstatic char *\nUnescapeCommentChars(const char *str)\n{\n\tint originalStringLength = strlen(str);\n\tStringInfo unescapedString = makeStringInfo();\n\n\tfor (int originalStringindex = 0; originalStringindex < originalStringLength;\n\t\t originalStringindex++)\n\t{\n\t\tif (str[originalStringindex] == '\\\\' &&\n\t\t\toriginalStringindex < originalStringLength - 1 &&\n\t\t\t(str[originalStringindex + 1] == '*' ||\n\t\t\t str[originalStringindex + 1] == '/'))\n\t\t{\n\t\t\toriginalStringindex++;\n\t\t}\n\t\tappendStringInfoChar(unescapedString, str[originalStringindex]);\n\t}\n\n\treturn unescapedString->data;\n}\n"
  },
  {
    "path": "src/backend/distributed/test/backend_counter.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/backend_counter.c\n *\n * This file contains functions to test the active backend counter\n * within Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"distributed/backend_data.h\"\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(get_all_active_client_backend_count);\n\n\n/*\n * get_all_active_client_backend_count returns the active\n * client backend count.\n */\nDatum\nget_all_active_client_backend_count(PG_FUNCTION_ARGS)\n{\n\tPG_RETURN_UINT32(GetExternalClientBackendCount());\n}\n"
  },
  {
    "path": "src/backend/distributed/test/citus_depended_object.c",
    "content": "/*\n * citus_depended_object.c\n *\n * Implements udf function related to hiding citus depended objects while executing\n * postgres vanilla tests.\n *\n * Copyright (c) Citus Data, Inc.\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/pg_aggregate.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_amop.h\"\n#include \"catalog/pg_amproc.h\"\n#include \"catalog/pg_attrdef.h\"\n#include \"catalog/pg_attribute.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_enum.h\"\n#include \"catalog/pg_event_trigger.h\"\n#include \"catalog/pg_language.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_opclass.h\"\n#include \"catalog/pg_operator.h\"\n#include \"catalog/pg_opfamily.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_rewrite.h\"\n#include \"catalog/pg_sequence.h\"\n#include \"catalog/pg_statistic.h\"\n#include \"catalog/pg_trigger.h\"\n#include \"catalog/pg_ts_config.h\"\n#include \"catalog/pg_ts_dict.h\"\n#include \"catalog/pg_ts_template.h\"\n#include \"catalog/pg_type.h\"\n\n#include \"distributed/citus_depended_object.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_cache.h\"\n\nstatic bool IsCitusDependentObject(ObjectAddress objectAddress);\n\nPG_FUNCTION_INFO_V1(is_citus_depended_object);\n\n/*\n * is_citus_depended_object a wrapper around IsCitusDependentObject, so\n * see the details there.\n *\n * The first parameter expects an oid for\n * a pg meta class, and the second parameter expects an oid for\n * the object which is found in the pg meta class.\n */\nDatum\nis_citus_depended_object(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tif (PG_ARGISNULL(0) || PG_ARGISNULL(1))\n\t{\n\t\t/* Because we want to return false for null arguments, we donot use strict keyword while creating that function. */\n\t\tPG_RETURN_BOOL(false);\n\t}\n\n\tOid metaTableId = PG_GETARG_OID(0);\n\tOid objectId = PG_GETARG_OID(1);\n\n\tif (!OidIsValid(metaTableId) || !OidIsValid(objectId))\n\t{\n\t\t/* we cannot continue without valid meta table or object oid */\n\t\tPG_RETURN_BOOL(false);\n\t}\n\n\tbool dependsOnCitus = false;\n\n\tObjectAddress objectAdress = { metaTableId, objectId, 0 };\n\n\tswitch (metaTableId)\n\t{\n\t\tcase ProcedureRelationId:\n\t\tcase AccessMethodRelationId:\n\t\tcase EventTriggerRelationId:\n\t\tcase TriggerRelationId:\n\t\tcase OperatorRelationId:\n\t\tcase OperatorClassRelationId:\n\t\tcase OperatorFamilyRelationId:\n\t\tcase AccessMethodOperatorRelationId:\n\t\tcase AccessMethodProcedureRelationId:\n\t\tcase TSConfigRelationId:\n\t\tcase TSTemplateRelationId:\n\t\tcase TSDictionaryRelationId:\n\t\tcase LanguageRelationId:\n\t\tcase RewriteRelationId:\n\t\tcase AttrDefaultRelationId:\n\t\tcase NamespaceRelationId:\n\t\tcase ConstraintRelationId:\n\t\tcase TypeRelationId:\n\t\tcase RelationRelationId:\n\t\t{\n\t\t\t/* meta classes that access their own oid */\n\t\t\tdependsOnCitus = IsCitusDependentObject(objectAdress);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase EnumRelationId:\n\t\t{\n\t\t\t/*\n\t\t\t * we do not directly access the oid in pg_enum,\n\t\t\t * because it does not exist in pg_depend, but its type does\n\t\t\t */\n\t\t\tobjectAdress.classId = TypeRelationId;\n\t\t\tdependsOnCitus = IsCitusDependentObject(objectAdress);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase IndexRelationId:\n\t\tcase AttributeRelationId:\n\t\tcase SequenceRelationId:\n\t\tcase StatisticRelationId:\n\t\t{\n\t\t\t/* meta classes that access their relation's oid */\n\t\t\tobjectAdress.classId = RelationRelationId;\n\t\t\tdependsOnCitus = IsCitusDependentObject(objectAdress);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase AggregateRelationId:\n\t\t{\n\t\t\t/* We access procedure oid for aggregates. */\n\t\t\tobjectAdress.classId = ProcedureRelationId;\n\t\t\tdependsOnCitus = IsCitusDependentObject(objectAdress);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tPG_RETURN_BOOL(dependsOnCitus);\n}\n\n\n/*\n * IsCitusDependentObject returns true if the given object depends on the citus extension.\n */\nstatic bool\nIsCitusDependentObject(ObjectAddress objectAddress)\n{\n\tif (IsObjectAddressOwnedByCitus(&objectAddress))\n\t{\n\t\t/* object itself is owned by citus */\n\t\treturn true;\n\t}\n\n\t/* check if object's any dependency is owned by citus. */\n\tList *citusDependencies = GetAllCitusDependedDependenciesForObject(&objectAddress);\n\treturn list_length(citusDependencies) > 0;\n}\n"
  },
  {
    "path": "src/backend/distributed/test/citus_stat_tenants.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_stat_tenants.c\n *\n * This file contains functions to test citus_stat_tenants.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"sys/time.h\"\n\n#include \"distributed/stats/stat_tenants.h\"\n\nPG_FUNCTION_INFO_V1(sleep_until_next_period);\n\n/*\n * sleep_until_next_period sleeps until the next monitoring period starts.\n */\nDatum\nsleep_until_next_period(PG_FUNCTION_ARGS)\n{\n\tstruct timeval currentTime;\n\tgettimeofday(&currentTime, NULL);\n\n\tlong int nextPeriodStart = currentTime.tv_sec -\n\t\t\t\t\t\t\t   (currentTime.tv_sec % StatTenantsPeriod) +\n\t\t\t\t\t\t\t   StatTenantsPeriod;\n\n\tlong int sleepTime = (nextPeriodStart - currentTime.tv_sec) * 1000000 -\n\t\t\t\t\t\t currentTime.tv_usec + 100000;\n\tpg_usleep(sleepTime);\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/colocation_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/colocations_utils.c\n *\n * This file contains functions to test co-location functionality\n * within Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"catalog/pg_type.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/utils/array_type.h\"\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(get_table_colocation_id);\nPG_FUNCTION_INFO_V1(tables_colocated);\nPG_FUNCTION_INFO_V1(shards_colocated);\nPG_FUNCTION_INFO_V1(get_colocated_table_array);\nPG_FUNCTION_INFO_V1(find_shard_interval_index);\n\n\n/*\n * get_table_colocation_id returns colocation id of given distributed table.\n */\nDatum\nget_table_colocation_id(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\tuint32 colocationId = TableColocationId(distributedTableId);\n\n\tPG_RETURN_INT32(colocationId);\n}\n\n\n/*\n * tables_colocated checks if given two tables are co-located or not. If they are\n * co-located, this function returns true.\n */\nDatum\ntables_colocated(PG_FUNCTION_ARGS)\n{\n\tOid leftDistributedTableId = PG_GETARG_OID(0);\n\tOid rightDistributedTableId = PG_GETARG_OID(1);\n\tbool tablesColocated = TablesColocated(leftDistributedTableId,\n\t\t\t\t\t\t\t\t\t\t   rightDistributedTableId);\n\n\tPG_RETURN_BOOL(tablesColocated);\n}\n\n\n/*\n * shards_colocated checks if given two shards are co-located or not. If they are\n * co-located, this function returns true.\n */\nDatum\nshards_colocated(PG_FUNCTION_ARGS)\n{\n\tuint32 leftShardId = PG_GETARG_UINT32(0);\n\tuint32 rightShardId = PG_GETARG_UINT32(1);\n\tShardInterval *leftShard = LoadShardInterval(leftShardId);\n\tShardInterval *rightShard = LoadShardInterval(rightShardId);\n\n\tbool shardsColocated = ShardsColocated(leftShard, rightShard);\n\n\tPG_RETURN_BOOL(shardsColocated);\n}\n\n\n/*\n * get_colocated_tables_array returns array of table oids which are co-located with given\n * distributed table.\n */\nDatum\nget_colocated_table_array(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\n\tList *colocatedTableList = ColocatedTableList(distributedTableId);\n\tint colocatedTableCount = list_length(colocatedTableList);\n\tDatum *colocatedTablesDatumArray = palloc0(colocatedTableCount * sizeof(Datum));\n\tOid arrayTypeId = OIDOID;\n\tint colocatedTableIndex = 0;\n\n\tOid colocatedTableId = InvalidOid;\n\tforeach_declared_oid(colocatedTableId, colocatedTableList)\n\t{\n\t\tDatum colocatedTableDatum = ObjectIdGetDatum(colocatedTableId);\n\n\t\tcolocatedTablesDatumArray[colocatedTableIndex] = colocatedTableDatum;\n\t\tcolocatedTableIndex++;\n\t}\n\n\tArrayType *colocatedTablesArrayType = DatumArrayToArrayType(colocatedTablesDatumArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolocatedTableCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tarrayTypeId);\n\n\tPG_RETURN_ARRAYTYPE_P(colocatedTablesArrayType);\n}\n\n\n/*\n * find_shard_interval_index finds index of given shard in sorted shard interval list.\n */\nDatum\nfind_shard_interval_index(PG_FUNCTION_ARGS)\n{\n\tuint32 shardId = PG_GETARG_UINT32(0);\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\tuint32 shardIndex = ShardIndex(shardInterval);\n\n\tPG_RETURN_INT32(shardIndex);\n}\n"
  },
  {
    "path": "src/backend/distributed/test/create_shards.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/create_shards.c\n *\n * This file contains functions to exercise shard creation functionality\n * within Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <string.h>\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/listutils.h\"\n\n\n/* local function forward declarations */\nstatic int CompareStrings(const void *leftElement, const void *rightElement);\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(sort_names);\n\n\n/*\n * sort_names accepts three strings, places them in a list, then calls PGSSortList\n * to test its sort functionality. Returns a string containing sorted lines.\n */\nDatum\nsort_names(PG_FUNCTION_ARGS)\n{\n\tchar *first = PG_GETARG_CSTRING(0);\n\tchar *second = PG_GETARG_CSTRING(1);\n\tchar *third = PG_GETARG_CSTRING(2);\n\tList *nameList = SortList(list_make3(first, second, third),\n\t\t\t\t\t\t\t  (int (*)(const void *, const void *))(&CompareStrings));\n\tStringInfo sortedNames = makeStringInfo();\n\n\tconst char *name = NULL;\n\tforeach_declared_ptr(name, nameList)\n\t{\n\t\tappendStringInfo(sortedNames, \"%s\\n\", name);\n\t}\n\n\n\tPG_RETURN_CSTRING(sortedNames->data);\n}\n\n\n/*\n * A simple wrapper around strcmp suitable for use with PGSSortList or qsort.\n */\nstatic int\nCompareStrings(const void *leftElement, const void *rightElement)\n{\n\tconst char *leftString = *((const char **) leftElement);\n\tconst char *rightString = *((const char **) rightElement);\n\n\treturn strcmp(leftString, rightString);\n}\n"
  },
  {
    "path": "src/backend/distributed/test/deparse_function_query.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/deparse_function_query.c\n *\n * This file contains functions to exercise deparsing of\n *  CREATE|ALTER|DROP [...] {FUNCTION|PROCEDURE} ...\n * queries\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"utils/builtins.h\"\n\n#include \"distributed/deparser.h\"\n#include \"distributed/multi_executor.h\"\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(deparse_test);\n\n\n/*\n * deparse_test UDF is a UDF to test deparsing in Citus.\n *\n * This function accepts a query string; parses, qualifies and then deparses it to create\n * a qualified query string.\n */\nDatum\ndeparse_test(PG_FUNCTION_ARGS)\n{\n\ttext *queryStringText = PG_GETARG_TEXT_P(0);\n\n\tchar *queryStringChar = text_to_cstring(queryStringText);\n\tQuery *query = ParseQueryString(queryStringChar, NULL, 0);\n\n\tQualifyTreeNode(query->utilityStmt);\n\tconst char *deparsedQuery = DeparseTreeNode(query->utilityStmt);\n\n\tPG_RETURN_TEXT_P(cstring_to_text(deparsedQuery));\n}\n"
  },
  {
    "path": "src/backend/distributed/test/deparse_shard_query.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/depase_shard_query.c\n *\n * This file contains functions to exercise deparsing of INSERT .. SELECT queries\n * for distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <stddef.h>\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n\n#include \"catalog/pg_type.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/value.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/palloc.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/multi_router_planner.h\"\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(deparse_shard_query_test);\n\n\nDatum\ndeparse_shard_query_test(PG_FUNCTION_ARGS)\n{\n\ttext *queryString = PG_GETARG_TEXT_P(0);\n\n\tchar *queryStringChar = text_to_cstring(queryString);\n\tList *parseTreeList = pg_parse_query(queryStringChar);\n\n\tNode *parsetree = NULL;\n\tforeach_declared_ptr(parsetree, parseTreeList)\n\t{\n\t\tList *queryTreeList = pg_analyze_and_rewrite_fixedparams((RawStmt *) parsetree,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t queryStringChar,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t NULL, 0, NULL);\n\n\t\tQuery *query = NULL;\n\t\tforeach_declared_ptr(query, queryTreeList)\n\t\t{\n\t\t\tStringInfo buffer = makeStringInfo();\n\n\t\t\t/* reoreder the target list only for INSERT .. SELECT queries */\n\t\t\tif (InsertSelectIntoCitusTable(query))\n\t\t\t{\n\t\t\t\tRangeTblEntry *insertRte = linitial(query->rtable);\n\t\t\t\tRangeTblEntry *subqueryRte = lsecond(query->rtable);\n\n\n\t\t\t\tReorderInsertSelectTargetLists(query, insertRte, subqueryRte);\n\t\t\t}\n\n\t\t\tdeparse_shard_query(query, InvalidOid, 0, buffer);\n\n\t\t\telog(INFO, \"query: %s\", buffer->data);\n\t\t}\n\t}\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/dependency.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/dependency.c\n *\n * This file contains functions to exercise dependency resolution for objects.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/tuplestore.h\"\n\n\nPG_FUNCTION_INFO_V1(citus_get_all_dependencies_for_object);\nPG_FUNCTION_INFO_V1(citus_get_dependencies_for_object);\n\n\n/*\n * citus_get_all_dependencies_for_object(classid oid, objid oid, objsubid int)\n *\n * citus_get_all_dependencies_for_object gets an object and returns all of its\n * dependencies irrespective of whether the dependencies are already distributed\n * or not.\n *\n * This is to emulate what Citus would qualify as dependency when adding a new\n * node.\n */\nDatum\ncitus_get_all_dependencies_for_object(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid classid = PG_GETARG_OID(0);\n\tOid objid = PG_GETARG_OID(1);\n\tint32 objsubid = PG_GETARG_INT32(2);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tObjectAddress address = { 0 };\n\tObjectAddressSubSet(address, classid, objid, objsubid);\n\n\tList *dependencies = GetAllSupportedDependenciesForObject(&address);\n\tObjectAddress *dependency = NULL;\n\tforeach_declared_ptr(dependency, dependencies)\n\t{\n\t\tDatum values[3];\n\t\tbool isNulls[3];\n\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(isNulls, 0, sizeof(isNulls));\n\n\t\tvalues[0] = ObjectIdGetDatum(dependency->classId);\n\t\tvalues[1] = ObjectIdGetDatum(dependency->objectId);\n\t\tvalues[2] = Int32GetDatum(dependency->objectSubId);\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_get_dependencies_for_object(classid oid, objid oid, objsubid int)\n *\n * citus_get_dependencies_for_object gets an object and returns all of its\n * dependencies that are not already distributed.\n *\n * This is to emulate what Citus would qualify as dependency when creating\n * a new object.\n */\nDatum\ncitus_get_dependencies_for_object(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid classid = PG_GETARG_OID(0);\n\tOid objid = PG_GETARG_OID(1);\n\tint32 objsubid = PG_GETARG_INT32(2);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tObjectAddress address = { 0 };\n\tObjectAddressSubSet(address, classid, objid, objsubid);\n\n\tList *dependencies = GetDependenciesForObject(&address);\n\tObjectAddress *dependency = NULL;\n\tforeach_declared_ptr(dependency, dependencies)\n\t{\n\t\tDatum values[3];\n\t\tbool isNulls[3];\n\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(isNulls, 0, sizeof(isNulls));\n\n\t\tvalues[0] = ObjectIdGetDatum(dependency->classId);\n\t\tvalues[1] = ObjectIdGetDatum(dependency->objectId);\n\t\tvalues[2] = Int32GetDatum(dependency->objectSubId);\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);\n\t}\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/distributed_deadlock_detection.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/distributed_deadlock_detection.c\n *\n * This file contains functions to exercise distributed deadlock detection\n * related lower level functionality.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"access/hash.h\"\n#include \"nodes/pg_list.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/timestamp.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/distributed_deadlock_detection.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/transaction_identifier.h\"\n#include \"distributed/tuplestore.h\"\n\n\nPG_FUNCTION_INFO_V1(get_adjacency_list_wait_graph);\n\n\n/*\n * get_adjacency_list_wait_graph returns the wait graph in adjacency list format. For the\n * details see BuildAdjacencyListForWaitGraph().\n *\n * This function is mostly useful for testing and debugging purposes.\n */\nDatum\nget_adjacency_list_wait_graph(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tTupleDesc tupleDescriptor = NULL;\n\n\tHASH_SEQ_STATUS status;\n\tTransactionNode *transactionNode = NULL;\n\n\tDatum values[2];\n\tbool isNulls[2];\n\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\t/* distributed deadlock detection only considers distributed txs */\n\tbool onlyDistributedTx = true;\n\tWaitGraph *waitGraph = BuildGlobalWaitGraph(onlyDistributedTx);\n\tHTAB *adjacencyList = BuildAdjacencyListsForWaitGraph(waitGraph);\n\n\t/* iterate on all nodes */\n\thash_seq_init(&status, adjacencyList);\n\n\twhile ((transactionNode = (TransactionNode *) hash_seq_search(&status)) != 0)\n\t{\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(isNulls, false, sizeof(isNulls));\n\n\t\tvalues[0] = UInt64GetDatum(transactionNode->transactionId.transactionNumber);\n\t\tvalues[1] = CStringGetDatum(WaitsForToString(transactionNode->waitsFor));\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);\n\t}\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/distributed_intermediate_results.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/distributed_intermediate_results.c\n *\n * This file contains functions to test functions related to\n * src/backend/distributed/executor/distributed_intermediate_results.c.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"catalog/pg_type.h\"\n#include \"tcop/tcopprot.h\"\n\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/version_compat.h\"\n\nPG_FUNCTION_INFO_V1(partition_task_list_results);\nPG_FUNCTION_INFO_V1(redistribute_task_list_results);\n\n/*\n * partition_task_list_results partitions results of each of distributed\n * tasks for the given query with the ranges of the given relation.\n * Partitioned results for a task are stored on the node that the task\n * was targeted for.\n */\nDatum\npartition_task_list_results(PG_FUNCTION_ARGS)\n{\n\ttext *resultIdPrefixText = PG_GETARG_TEXT_P(0);\n\tchar *resultIdPrefix = text_to_cstring(resultIdPrefixText);\n\ttext *queryText = PG_GETARG_TEXT_P(1);\n\tchar *queryString = text_to_cstring(queryText);\n\tOid relationId = PG_GETARG_OID(2);\n\tbool binaryFormat = PG_GETARG_BOOL(3);\n\n\tQuery *parsedQuery = ParseQueryString(queryString, NULL, 0);\n\tPlannedStmt *queryPlan = pg_plan_query(parsedQuery,\n\t\t\t\t\t\t\t\t\t\t   queryString,\n\t\t\t\t\t\t\t\t\t\t   CURSOR_OPT_PARALLEL_OK,\n\t\t\t\t\t\t\t\t\t\t   NULL);\n\tif (!IsCitusCustomScan(queryPlan->planTree))\n\t{\n\t\tereport(ERROR, (errmsg(\"query must be distributed and shouldn't require \"\n\t\t\t\t\t\t\t   \"any merging on the coordinator.\")));\n\t}\n\n\tCustomScan *customScan = (CustomScan *) queryPlan->planTree;\n\tDistributedPlan *distributedPlan = GetDistributedPlan(customScan);\n\n\tJob *job = distributedPlan->workerJob;\n\tList *taskList = job->taskList;\n\n\tCitusTableCacheEntry *targetRelation = GetCitusTableCacheEntry(relationId);\n\n\t/*\n\t * Here SELECT query's target list should match column list of target relation,\n\t * so their partition column indexes are equal.\n\t */\n\tint partitionColumnIndex = 0;\n\n\tif (IsCitusTableTypeCacheEntry(targetRelation, DISTRIBUTED_TABLE) && IsA(\n\t\t\ttargetRelation->partitionColumn, Var))\n\t{\n\t\tpartitionColumnIndex = targetRelation->partitionColumn->varattno - 1;\n\t}\n\n\tList *fragmentList = PartitionTasklistResults(resultIdPrefix, taskList,\n\t\t\t\t\t\t\t\t\t\t\t\t  partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t  targetRelation, binaryFormat);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tDistributedResultFragment *fragment = NULL;\n\tforeach_declared_ptr(fragment, fragmentList)\n\t{\n\t\tbool columnNulls[5] = { 0 };\n\t\tDatum columnValues[5] = {\n\t\t\tCStringGetTextDatum(fragment->resultId),\n\t\t\tUInt32GetDatum(fragment->nodeId),\n\t\t\tInt64GetDatum(fragment->rowCount),\n\t\t\tUInt64GetDatum(fragment->targetShardId),\n\t\t\tInt32GetDatum(fragment->targetShardIndex)\n\t\t};\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, columnValues, columnNulls);\n\t}\n\tPG_RETURN_DATUM(0);\n}\n\n\n/*\n * redistribute_task_list_results exposes RedistributeTaskListResult for testing.\n * It executes a query and repartitions and colocates its results according to\n * a relation.\n */\nDatum\nredistribute_task_list_results(PG_FUNCTION_ARGS)\n{\n\ttext *resultIdPrefixText = PG_GETARG_TEXT_P(0);\n\tchar *resultIdPrefix = text_to_cstring(resultIdPrefixText);\n\ttext *queryText = PG_GETARG_TEXT_P(1);\n\tchar *queryString = text_to_cstring(queryText);\n\tOid relationId = PG_GETARG_OID(2);\n\tbool binaryFormat = PG_GETARG_BOOL(3);\n\n\tQuery *parsedQuery = ParseQueryString(queryString, NULL, 0);\n\tPlannedStmt *queryPlan = pg_plan_query(parsedQuery,\n\t\t\t\t\t\t\t\t\t\t   queryString,\n\t\t\t\t\t\t\t\t\t\t   CURSOR_OPT_PARALLEL_OK,\n\t\t\t\t\t\t\t\t\t\t   NULL);\n\tif (!IsCitusCustomScan(queryPlan->planTree))\n\t{\n\t\tereport(ERROR, (errmsg(\"query must be distributed and shouldn't require \"\n\t\t\t\t\t\t\t   \"any merging on the coordinator.\")));\n\t}\n\n\tCustomScan *customScan = (CustomScan *) queryPlan->planTree;\n\tDistributedPlan *distributedPlan = GetDistributedPlan(customScan);\n\n\tJob *job = distributedPlan->workerJob;\n\tList *taskList = job->taskList;\n\n\tCitusTableCacheEntry *targetRelation = GetCitusTableCacheEntry(relationId);\n\n\t/*\n\t * Here SELECT query's target list should match column list of target relation,\n\t * so their partition column indexes are equal.\n\t */\n\tint partitionColumnIndex = IsCitusTableTypeCacheEntry(targetRelation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  DISTRIBUTED_TABLE) ?\n\t\t\t\t\t\t\t   targetRelation->partitionColumn->varattno - 1 : 0;\n\n\tList **shardResultIds = RedistributeTaskListResults(resultIdPrefix, taskList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetRelation, binaryFormat);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\tint shardCount = targetRelation->shardIntervalArrayLength;\n\n\tfor (int shardIndex = 0; shardIndex < shardCount; shardIndex++)\n\t{\n\t\tShardInterval *shardInterval =\n\t\t\ttargetRelation->sortedShardIntervalArray[shardIndex];\n\t\tuint64 shardId = shardInterval->shardId;\n\n\t\tint fragmentCount = list_length(shardResultIds[shardIndex]);\n\t\tDatum *resultIdValues = palloc0(fragmentCount * sizeof(Datum));\n\t\tList *sortedResultIds = SortList(shardResultIds[shardIndex], pg_qsort_strcmp);\n\n\t\tconst char *resultId = NULL;\n\t\tint resultIdIndex = 0;\n\t\tforeach_declared_ptr(resultId, sortedResultIds)\n\t\t{\n\t\t\tresultIdValues[resultIdIndex++] = CStringGetTextDatum(resultId);\n\t\t}\n\n\t\tArrayType *resultIdArray = DatumArrayToArrayType(resultIdValues, fragmentCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t TEXTOID);\n\n\t\tbool columnNulls[2] = { 0 };\n\t\tDatum columnValues[2] = {\n\t\t\tInt64GetDatum(shardId),\n\t\t\tPointerGetDatum(resultIdArray)\n\t\t};\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, columnValues, columnNulls);\n\t}\n\tPG_RETURN_DATUM(0);\n}\n"
  },
  {
    "path": "src/backend/distributed/test/distribution_metadata.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/distribution_metadata.c\n *\n * This file contains functions to exercise distributed table metadata\n * functionality within Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n\n#include \"access/heapam.h\"\n#include \"catalog/pg_type.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n#include \"storage/lock.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/errcodes.h\"\n#include \"utils/palloc.h\"\n\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/distribution_column.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/pg_dist_shard.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/utils/array_type.h\"\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(load_shard_id_array);\nPG_FUNCTION_INFO_V1(load_shard_interval_array);\nPG_FUNCTION_INFO_V1(load_shard_placement_array);\nPG_FUNCTION_INFO_V1(partition_column_id);\nPG_FUNCTION_INFO_V1(partition_type);\nPG_FUNCTION_INFO_V1(is_distributed_table);\nPG_FUNCTION_INFO_V1(create_monolithic_shard_row);\nPG_FUNCTION_INFO_V1(acquire_shared_shard_lock);\nPG_FUNCTION_INFO_V1(relation_count_in_query);\n\n\n/*\n * load_shard_id_array returns the shard identifiers for a particular\n * distributed table as a bigint array. If the table is not distributed\n * yet, the function errors-out.\n */\nDatum\nload_shard_id_array(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\tint shardIdIndex = 0;\n\tOid shardIdTypeId = INT8OID;\n\n\tList *shardList = LoadShardIntervalList(distributedTableId);\n\n\tint shardIdCount = list_length(shardList);\n\tDatum *shardIdDatumArray = palloc0(shardIdCount * sizeof(Datum));\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardList)\n\t{\n\t\tDatum shardIdDatum = Int64GetDatum(shardInterval->shardId);\n\n\t\tshardIdDatumArray[shardIdIndex] = shardIdDatum;\n\t\tshardIdIndex++;\n\t}\n\n\tArrayType *shardIdArrayType = DatumArrayToArrayType(shardIdDatumArray, shardIdCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardIdTypeId);\n\n\tPG_RETURN_ARRAYTYPE_P(shardIdArrayType);\n}\n\n\n/*\n * load_shard_interval_array loads a shard interval using a provided identifier\n * and returns a two-element array consisting of min/max values contained in\n * that shard interval. If no such interval can be found, this function raises\n * an error instead.\n */\nDatum\nload_shard_interval_array(PG_FUNCTION_ARGS)\n{\n\tint64 shardId = PG_GETARG_INT64(0);\n\tOid expectedType PG_USED_FOR_ASSERTS_ONLY = get_fn_expr_argtype(fcinfo->flinfo, 1);\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\tDatum shardIntervalArray[] = { shardInterval->minValue, shardInterval->maxValue };\n\n\tAssert(expectedType == shardInterval->valueTypeId);\n\n\tArrayType *shardIntervalArrayType = DatumArrayToArrayType(shardIntervalArray, 2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardInterval->valueTypeId);\n\n\tPG_RETURN_ARRAYTYPE_P(shardIntervalArrayType);\n}\n\n\n/*\n * load_shard_placement_array loads a shard interval using the provided ID\n * and returns an array of strings containing the node name and port for each\n * placement of the specified shard interval. If the second argument is true,\n * only active placements are returned; otherwise, all are. If no such shard\n * interval can be found, this function raises an error instead.\n */\nDatum\nload_shard_placement_array(PG_FUNCTION_ARGS)\n{\n\tint64 shardId = PG_GETARG_INT64(0);\n\tbool onlyActive = PG_GETARG_BOOL(1);\n\tList *placementList = NIL;\n\tint placementIndex = 0;\n\tOid placementTypeId = TEXTOID;\n\tStringInfo placementInfo = makeStringInfo();\n\n\tif (onlyActive)\n\t{\n\t\tplacementList = ActiveShardPlacementList(shardId);\n\t}\n\telse\n\t{\n\t\tplacementList = ShardPlacementList(shardId);\n\t}\n\n\tplacementList = SortList(placementList, CompareShardPlacementsByWorker);\n\n\tint placementCount = list_length(placementList);\n\tDatum *placementDatumArray = palloc0(placementCount * sizeof(Datum));\n\n\tShardPlacement *placement = NULL;\n\tforeach_declared_ptr(placement, placementList)\n\t{\n\t\tappendStringInfo(placementInfo, \"%s:%d\", placement->nodeName,\n\t\t\t\t\t\t placement->nodePort);\n\n\t\tplacementDatumArray[placementIndex] = CStringGetTextDatum(placementInfo->data);\n\t\tplacementIndex++;\n\t\tresetStringInfo(placementInfo);\n\t}\n\n\tArrayType *placementArrayType = DatumArrayToArrayType(placementDatumArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  placementCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  placementTypeId);\n\n\tPG_RETURN_ARRAYTYPE_P(placementArrayType);\n}\n\n\n/*\n * partition_column_id simply finds a distributed table using the provided Oid\n * and returns the column_id of its partition column. If the specified table is\n * not distributed, this function raises an error instead.\n */\nDatum\npartition_column_id(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\tuint32 rangeTableId = 1;\n\tif (!IsCitusTableType(distributedTableId, HASH_DISTRIBUTED))\n\t{\n\t\tereport(ERROR, (errmsg(\"table needs to be hash distributed\")));\n\t}\n\tVar *partitionColumn = PartitionColumn(distributedTableId, rangeTableId);\n\n\tPG_RETURN_INT16((int16) partitionColumn->varattno);\n}\n\n\n/*\n * partition_type simply finds a distributed table using the provided Oid and\n * returns the type of partitioning in use by that table. If the specified\n * table is not distributed, this function raises an error instead.\n */\nDatum\npartition_type(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\tchar partitionType = PartitionMethod(distributedTableId);\n\n\tPG_RETURN_CHAR(partitionType);\n}\n\n\n/*\n * is_distributed_table simply returns whether a given table is distributed. No\n * errors, just a boolean.\n */\nDatum\nis_distributed_table(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\tbool isCitusTable = IsCitusTable(distributedTableId);\n\n\tPG_RETURN_BOOL(isCitusTable);\n}\n\n\n/*\n * create_monolithic_shard_row creates a single shard covering all possible\n * hash values for a given table and inserts a row representing that shard\n * into the backing store. It returns the primary key of the new row.\n */\nDatum\ncreate_monolithic_shard_row(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\tStringInfo minInfo = makeStringInfo();\n\tStringInfo maxInfo = makeStringInfo();\n\tuint64 newShardId = GetNextShardId();\n\n\tappendStringInfo(minInfo, \"%d\", PG_INT32_MIN);\n\tappendStringInfo(maxInfo, \"%d\", PG_INT32_MAX);\n\n\ttext *minInfoText = cstring_to_text(minInfo->data);\n\ttext *maxInfoText = cstring_to_text(maxInfo->data);\n\n\tInsertShardRow(distributedTableId, newShardId, SHARD_STORAGE_TABLE, minInfoText,\n\t\t\t\t   maxInfoText);\n\n\tPG_RETURN_INT64(newShardId);\n}\n\n\n/*\n * acquire_shared_shard_lock grabs a shared lock for the specified shard.\n */\nDatum\nacquire_shared_shard_lock(PG_FUNCTION_ARGS)\n{\n\tint64 shardId = PG_GETARG_INT64(0);\n\n\tLockShardResource(shardId, ShareLock);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * relation_count_in_query return the first query's relation count.\n */\nDatum\nrelation_count_in_query(PG_FUNCTION_ARGS)\n{\n\ttext *queryString = PG_GETARG_TEXT_P(0);\n\n\tchar *queryStringChar = text_to_cstring(queryString);\n\tList *parseTreeList = pg_parse_query(queryStringChar);\n\n\tNode *parsetree = NULL;\n\tforeach_declared_ptr(parsetree, parseTreeList)\n\t{\n\t\tList *queryTreeList = pg_analyze_and_rewrite_fixedparams((RawStmt *) parsetree,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t queryStringChar,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t NULL, 0, NULL);\n\n\t\tQuery *query = NULL;\n\t\tforeach_declared_ptr(query, queryTreeList)\n\t\t{\n\t\t\tList *rangeTableList = NIL;\n\n\t\t\tExtractRangeTableRelationWalker((Node *) query, &rangeTableList);\n\n\t\t\tPG_RETURN_INT32(list_length(rangeTableList));\n\t\t}\n\t}\n\n\tPG_RETURN_INT32(0);\n}\n"
  },
  {
    "path": "src/backend/distributed/test/fake_am.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * fake_am.c\n *\t  fake table access method code\n *\n * Copyright (c) Citus Data, Inc.\n *\n * IDENTIFICATION\n *\t  Based on https://github.com/michaelpq/pg_plugins/blob/master/blackhole_am/blackhole_am.c\n *\n *\n * NOTES\n *\t  This file introduces the table access method \"fake\", which delegates\n *    bare minimum functionality for testing to heapam to provide an append\n *    only access method, and doesn't implement rest of the functionality.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/amapi.h\"\n#include \"access/heapam.h\"\n#include \"access/multixact.h\"\n#include \"access/tableam.h\"\n#include \"access/xact.h\"\n#include \"catalog/index.h\"\n#include \"catalog/storage.h\"\n#include \"catalog/storage_xlog.h\"\n#include \"commands/vacuum.h\"\n#include \"executor/tuptable.h\"\n#include \"storage/smgr.h\"\n#include \"utils/snapmgr.h\"\n\n#include \"pg_version_compat.h\"\n#include \"pg_version_constants.h\"\n\nPG_FUNCTION_INFO_V1(fake_am_handler);\n\nstatic const TableAmRoutine fake_methods;\n\n\n/* ------------------------------------------------------------------------\n * Slot related callbacks for fake AM\n * ------------------------------------------------------------------------\n */\nstatic const TupleTableSlotOps *\nfake_slot_callbacks(Relation relation)\n{\n\treturn &TTSOpsBufferHeapTuple;\n}\n\n\n/* ------------------------------------------------------------------------\n * Table Scan Callbacks for fake AM\n * ------------------------------------------------------------------------\n */\nstatic TableScanDesc\nfake_scan_begin(Relation relation, Snapshot snapshot,\n\t\t\t\tint nkeys, ScanKey key,\n\t\t\t\tParallelTableScanDesc parallel_scan,\n\t\t\t\tuint32 flags)\n{\n\treturn heap_beginscan(relation, snapshot, nkeys, key, parallel_scan, flags);\n}\n\n\nstatic void\nfake_scan_end(TableScanDesc sscan)\n{\n\theap_endscan(sscan);\n}\n\n\nstatic void\nfake_scan_rescan(TableScanDesc sscan, ScanKey key, bool set_params,\n\t\t\t\t bool allow_strat, bool allow_sync, bool allow_pagemode)\n{\n\theap_rescan(sscan, key, set_params, allow_strat, allow_sync, allow_pagemode);\n}\n\n\nstatic bool\nfake_scan_getnextslot(TableScanDesc sscan, ScanDirection direction,\n\t\t\t\t\t  TupleTableSlot *slot)\n{\n\tereport(WARNING, (errmsg(\"fake_scan_getnextslot\")));\n\treturn heap_getnextslot(sscan, direction, slot);\n}\n\n\n/* ------------------------------------------------------------------------\n * Index Scan Callbacks for fake AM\n * ------------------------------------------------------------------------\n */\nstatic IndexFetchTableData *\nfake_index_fetch_begin(Relation rel)\n{\n\telog(ERROR, \"fake_index_fetch_begin not implemented\");\n\treturn NULL;\n}\n\n\nstatic void\nfake_index_fetch_reset(IndexFetchTableData *scan)\n{\n\telog(ERROR, \"fake_index_fetch_reset not implemented\");\n}\n\n\nstatic void\nfake_index_fetch_end(IndexFetchTableData *scan)\n{\n\telog(ERROR, \"fake_index_fetch_end not implemented\");\n}\n\n\nstatic bool\nfake_index_fetch_tuple(struct IndexFetchTableData *scan,\n\t\t\t\t\t   ItemPointer tid,\n\t\t\t\t\t   Snapshot snapshot,\n\t\t\t\t\t   TupleTableSlot *slot,\n\t\t\t\t\t   bool *call_again, bool *all_dead)\n{\n\telog(ERROR, \"fake_index_fetch_tuple not implemented\");\n\treturn false;\n}\n\n\n/* ------------------------------------------------------------------------\n * Callbacks for non-modifying operations on individual tuples for\n * fake AM.\n * ------------------------------------------------------------------------\n */\nstatic bool\nfake_fetch_row_version(Relation relation,\n\t\t\t\t\t   ItemPointer tid,\n\t\t\t\t\t   Snapshot snapshot,\n\t\t\t\t\t   TupleTableSlot *slot)\n{\n\telog(ERROR, \"fake_fetch_row_version not implemented\");\n\treturn false;\n}\n\n\nstatic void\nfake_get_latest_tid(TableScanDesc sscan,\n\t\t\t\t\tItemPointer tid)\n{\n\telog(ERROR, \"fake_get_latest_tid not implemented\");\n}\n\n\nstatic bool\nfake_tuple_tid_valid(TableScanDesc scan, ItemPointer tid)\n{\n\telog(ERROR, \"fake_tuple_tid_valid not implemented\");\n\treturn false;\n}\n\n\nstatic bool\nfake_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot,\n\t\t\t\t\t\t\t  Snapshot snapshot)\n{\n\telog(ERROR, \"fake_tuple_satisfies_snapshot not implemented\");\n\treturn false;\n}\n\n\nstatic TransactionId\nfake_index_delete_tuples(Relation rel,\n\t\t\t\t\t\t TM_IndexDeleteOp *delstate)\n{\n\telog(ERROR, \"fake_index_delete_tuples not implemented\");\n\treturn InvalidTransactionId;\n}\n\n\n/* ----------------------------------------------------------------------------\n *  Functions for manipulations of physical tuples for fake AM.\n * ----------------------------------------------------------------------------\n */\nstatic void\nfake_tuple_insert(Relation relation, TupleTableSlot *slot,\n\t\t\t\t  CommandId cid, int options, BulkInsertState bistate)\n{\n\tereport(WARNING, (errmsg(\"fake_tuple_insert\")));\n\n\t/*\n\t * Code below this point is from heapam_tuple_insert from\n\t * heapam_handler.c\n\t */\n\n\tbool shouldFree = true;\n\tHeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree);\n\n\t/* Update the tuple with table oid */\n\tslot->tts_tableOid = RelationGetRelid(relation);\n\ttuple->t_tableOid = slot->tts_tableOid;\n\n\t/* Perform the insertion, and copy the resulting ItemPointer */\n\theap_insert(relation, tuple, cid, options, bistate);\n\tItemPointerCopy(&tuple->t_self, &slot->tts_tid);\n\n\tif (shouldFree)\n\t{\n\t\tpfree(tuple);\n\t}\n}\n\n\nstatic void\nfake_tuple_insert_speculative(Relation relation, TupleTableSlot *slot,\n\t\t\t\t\t\t\t  CommandId cid, int options,\n\t\t\t\t\t\t\t  BulkInsertState bistate,\n\t\t\t\t\t\t\t  uint32 specToken)\n{\n\telog(ERROR, \"fake_tuple_insert_speculative not implemented\");\n}\n\n\nstatic void\nfake_tuple_complete_speculative(Relation relation, TupleTableSlot *slot,\n\t\t\t\t\t\t\t\tuint32 spekToken, bool succeeded)\n{\n\telog(ERROR, \"fake_tuple_complete_speculative not implemented\");\n}\n\n\nstatic void\nfake_multi_insert(Relation relation, TupleTableSlot **slots,\n\t\t\t\t  int ntuples, CommandId cid, int options,\n\t\t\t\t  BulkInsertState bistate)\n{\n\tereport(WARNING, (errmsg(\"fake_multi_insert\")));\n\n\theap_multi_insert(relation, slots, ntuples, cid, options, bistate);\n}\n\n\nstatic TM_Result\nfake_tuple_delete(Relation relation, ItemPointer tid, CommandId cid,\n\t\t\t\t  Snapshot snapshot, Snapshot crosscheck, bool wait,\n\t\t\t\t  TM_FailureData *tmfd, bool changingPart)\n{\n\telog(ERROR, \"fake_tuple_delete not implemented\");\n}\n\n\nstatic TM_Result\nfake_tuple_update(Relation relation, ItemPointer otid,\n\t\t\t\t  TupleTableSlot *slot, CommandId cid,\n\t\t\t\t  Snapshot snapshot, Snapshot crosscheck,\n\t\t\t\t  bool wait, TM_FailureData *tmfd,\n\t\t\t\t  LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)\n{\n\telog(ERROR, \"fake_tuple_update not implemented\");\n}\n\n\nstatic TM_Result\nfake_tuple_lock(Relation relation, ItemPointer tid, Snapshot snapshot,\n\t\t\t\tTupleTableSlot *slot, CommandId cid, LockTupleMode mode,\n\t\t\t\tLockWaitPolicy wait_policy, uint8 flags,\n\t\t\t\tTM_FailureData *tmfd)\n{\n\telog(ERROR, \"fake_tuple_lock not implemented\");\n}\n\n\nstatic void\nfake_finish_bulk_insert(Relation relation, int options)\n{\n\t/* nothing to do here */\n}\n\n\n/* ------------------------------------------------------------------------\n * DDL related callbacks for fake AM.\n * ------------------------------------------------------------------------\n */\nstatic void\nfake_relation_set_new_filenode(Relation rel,\n\t\t\t\t\t\t\t   const RelFileLocator *newrnode,\n\t\t\t\t\t\t\t   char persistence,\n\t\t\t\t\t\t\t   TransactionId *freezeXid,\n\t\t\t\t\t\t\t   MultiXactId *minmulti)\n{\n\t/*\n\t * Code below is copied from heapam_relation_set_new_filenode in\n\t * heapam_handler.c.\n\t */\n\n\n\t/*\n\t * Initialize to the minimum XID that could put tuples in the table. We\n\t * know that no xacts older than RecentXmin are still running, so that\n\t * will do.\n\t */\n\t*freezeXid = RecentXmin;\n\n\t/*\n\t * Similarly, initialize the minimum Multixact to the first value that\n\t * could possibly be stored in tuples in the table.  Running transactions\n\t * could reuse values from their local cache, so we are careful to\n\t * consider all currently running multis.\n\t *\n\t * XXX this could be refined further, but is it worth the hassle?\n\t */\n\t*minmulti = GetOldestMultiXactId();\n\n\tSMgrRelation srel = RelationCreateStorage(*newrnode, persistence, true);\n\n\t/*\n\t * If required, set up an init fork for an unlogged table so that it can\n\t * be correctly reinitialized on restart.  An immediate sync is required\n\t * even if the page has been logged, because the write did not go through\n\t * shared_buffers and therefore a concurrent checkpoint may have moved the\n\t * redo pointer past our xlog record.  Recovery may as well remove it\n\t * while replaying, for example, XLOG_DBASE_CREATE or XLOG_TBLSPC_CREATE\n\t * record. Therefore, logging is necessary even if wal_level=minimal.\n\t */\n\tif (persistence == RELPERSISTENCE_UNLOGGED)\n\t{\n\t\tAssert(rel->rd_rel->relkind == RELKIND_RELATION ||\n\t\t\t   rel->rd_rel->relkind == RELKIND_MATVIEW ||\n\t\t\t   rel->rd_rel->relkind == RELKIND_TOASTVALUE);\n\t\tsmgrcreate(srel, INIT_FORKNUM, false);\n\t\tlog_smgrcreate(newrnode, INIT_FORKNUM);\n\t\tsmgrimmedsync(srel, INIT_FORKNUM);\n\t}\n\n\tsmgrclose(srel);\n}\n\n\nstatic void\nfake_relation_nontransactional_truncate(Relation rel)\n{\n\telog(ERROR, \"fake_relation_nontransactional_truncate not implemented\");\n}\n\n\nstatic void\nfake_copy_data(Relation rel, const RelFileLocator *newrnode)\n{\n\telog(ERROR, \"fake_copy_data not implemented\");\n}\n\n\nstatic void\nfake_copy_for_cluster(Relation OldTable, Relation NewTable,\n\t\t\t\t\t  Relation OldIndex, bool use_sort,\n\t\t\t\t\t  TransactionId OldestXmin,\n\t\t\t\t\t  TransactionId *xid_cutoff,\n\t\t\t\t\t  MultiXactId *multi_cutoff,\n\t\t\t\t\t  double *num_tuples,\n\t\t\t\t\t  double *tups_vacuumed,\n\t\t\t\t\t  double *tups_recently_dead)\n{\n\telog(ERROR, \"fake_copy_for_cluster not implemented\");\n}\n\n\nstatic void\nfake_vacuum(Relation onerel, VacuumParams *params,\n\t\t\tBufferAccessStrategy bstrategy)\n{\n\telog(WARNING, \"fake_copy_for_cluster not implemented\");\n}\n\n\nstatic bool\nfake_scan_analyze_next_block(TableScanDesc scan,\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\t\t\t\t\t\t ReadStream *stream)\n#else\n\t\t\t\t\t\t\t BlockNumber blockno,\n\t\t\t\t\t\t\t BufferAccessStrategy bstrategy)\n#endif\n{\n\t/* we don't support analyze, so return false */\n\treturn false;\n}\n\n\nstatic bool\nfake_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,\n\t\t\t\t\t\t\t double *liverows, double *deadrows,\n\t\t\t\t\t\t\t TupleTableSlot *slot)\n{\n\telog(ERROR, \"fake_scan_analyze_next_tuple not implemented\");\n}\n\n\nstatic double\nfake_index_build_range_scan(Relation tableRelation,\n\t\t\t\t\t\t\tRelation indexRelation,\n\t\t\t\t\t\t\tIndexInfo *indexInfo,\n\t\t\t\t\t\t\tbool allow_sync,\n\t\t\t\t\t\t\tbool anyvisible,\n\t\t\t\t\t\t\tbool progress,\n\t\t\t\t\t\t\tBlockNumber start_blockno,\n\t\t\t\t\t\t\tBlockNumber numblocks,\n\t\t\t\t\t\t\tIndexBuildCallback callback,\n\t\t\t\t\t\t\tvoid *callback_state,\n\t\t\t\t\t\t\tTableScanDesc scan)\n{\n\telog(ERROR, \"fake_index_build_range_scan not implemented\");\n}\n\n\nstatic void\nfake_index_validate_scan(Relation tableRelation,\n\t\t\t\t\t\t Relation indexRelation,\n\t\t\t\t\t\t IndexInfo *indexInfo,\n\t\t\t\t\t\t Snapshot snapshot,\n\t\t\t\t\t\t ValidateIndexState *state)\n{\n\telog(ERROR, \"fake_index_build_range_scan not implemented\");\n}\n\n\n/* ------------------------------------------------------------------------\n * Miscellaneous callbacks for the fake AM\n * ------------------------------------------------------------------------\n */\nstatic uint64\nfake_relation_size(Relation rel, ForkNumber forkNumber)\n{\n\t/*\n\t * Code below is copied from heapam_relation_size from\n\t * heapam_handler.c.\n\t */\n\n\tuint64 nblocks = 0;\n\n\t/* InvalidForkNumber indicates returning the size for all forks */\n\tif (forkNumber == InvalidForkNumber)\n\t{\n\t\tfor (int i = 0; i < MAX_FORKNUM; i++)\n\t\t{\n\t\t\tnblocks += smgrnblocks(RelationGetSmgr(rel), i);\n\t\t}\n\t}\n\telse\n\t{\n\t\tnblocks = smgrnblocks(RelationGetSmgr(rel), forkNumber);\n\t}\n\n\treturn nblocks * BLCKSZ;\n}\n\n\n/*\n * Check to see whether the table needs a TOAST table.\n */\nstatic bool\nfake_relation_needs_toast_table(Relation rel)\n{\n\t/* we don't test toastable data with this, so no toast table needed */\n\treturn false;\n}\n\n\n/* ------------------------------------------------------------------------\n * Planner related callbacks for the fake AM\n * ------------------------------------------------------------------------\n */\nstatic void\nfake_estimate_rel_size(Relation rel, int32 *attr_widths,\n\t\t\t\t\t   BlockNumber *pages, double *tuples,\n\t\t\t\t\t   double *allvisfrac)\n{\n\t/* no data available */\n\t*attr_widths = 0;\n\t*tuples = 0;\n\t*allvisfrac = 0;\n\t*pages = 0;\n}\n\n\n/* ------------------------------------------------------------------------\n * Executor related callbacks for the fake AM\n * ------------------------------------------------------------------------\n */\n#if PG_VERSION_NUM < PG_VERSION_18\nstatic bool\nfake_scan_bitmap_next_block(TableScanDesc scan,\n\t\t\t\t\t\t\tTBMIterateResult *tbmres)\n{\n\telog(ERROR, \"fake_scan_bitmap_next_block not implemented\");\n}\n\n\nstatic bool\nfake_scan_bitmap_next_tuple(TableScanDesc scan,\n\t\t\t\t\t\t\tTBMIterateResult *tbmres,\n\t\t\t\t\t\t\tTupleTableSlot *slot)\n{\n\telog(ERROR, \"fake_scan_bitmap_next_tuple not implemented\");\n}\n\n\n#endif\n\nstatic bool\nfake_scan_sample_next_block(TableScanDesc scan,\n\t\t\t\t\t\t\tSampleScanState *scanstate)\n{\n\telog(ERROR, \"fake_scan_sample_next_block not implemented\");\n}\n\n\nstatic bool\nfake_scan_sample_next_tuple(TableScanDesc scan,\n\t\t\t\t\t\t\tSampleScanState *scanstate,\n\t\t\t\t\t\t\tTupleTableSlot *slot)\n{\n\telog(ERROR, \"fake_scan_sample_next_tuple not implemented\");\n}\n\n\n/* ------------------------------------------------------------------------\n * Definition of the fake table access method.\n * ------------------------------------------------------------------------\n */\n\nstatic const TableAmRoutine fake_methods = {\n\t.type = T_TableAmRoutine,\n\n\t.slot_callbacks = fake_slot_callbacks,\n\n\t.scan_begin = fake_scan_begin,\n\t.scan_end = fake_scan_end,\n\t.scan_rescan = fake_scan_rescan,\n\t.scan_getnextslot = fake_scan_getnextslot,\n\n\t/* these are common helper functions */\n\t.parallelscan_estimate = table_block_parallelscan_estimate,\n\t.parallelscan_initialize = table_block_parallelscan_initialize,\n\t.parallelscan_reinitialize = table_block_parallelscan_reinitialize,\n\n\t.index_fetch_begin = fake_index_fetch_begin,\n\t.index_fetch_reset = fake_index_fetch_reset,\n\t.index_fetch_end = fake_index_fetch_end,\n\t.index_fetch_tuple = fake_index_fetch_tuple,\n\n\t.tuple_insert = fake_tuple_insert,\n\t.tuple_insert_speculative = fake_tuple_insert_speculative,\n\t.tuple_complete_speculative = fake_tuple_complete_speculative,\n\t.multi_insert = fake_multi_insert,\n\t.tuple_delete = fake_tuple_delete,\n\t.tuple_update = fake_tuple_update,\n\t.tuple_lock = fake_tuple_lock,\n\t.finish_bulk_insert = fake_finish_bulk_insert,\n\n\t.tuple_fetch_row_version = fake_fetch_row_version,\n\t.tuple_get_latest_tid = fake_get_latest_tid,\n\t.tuple_tid_valid = fake_tuple_tid_valid,\n\t.tuple_satisfies_snapshot = fake_tuple_satisfies_snapshot,\n\t.index_delete_tuples = fake_index_delete_tuples,\n\n\t.relation_set_new_filelocator = fake_relation_set_new_filenode,\n\t.relation_nontransactional_truncate = fake_relation_nontransactional_truncate,\n\t.relation_copy_data = fake_copy_data,\n\t.relation_copy_for_cluster = fake_copy_for_cluster,\n\t.relation_vacuum = fake_vacuum,\n\t.scan_analyze_next_block = fake_scan_analyze_next_block,\n\t.scan_analyze_next_tuple = fake_scan_analyze_next_tuple,\n\t.index_build_range_scan = fake_index_build_range_scan,\n\t.index_validate_scan = fake_index_validate_scan,\n\n\t.relation_size = fake_relation_size,\n\t.relation_needs_toast_table = fake_relation_needs_toast_table,\n\n\t.relation_estimate_size = fake_estimate_rel_size,\n\n#if PG_VERSION_NUM < PG_VERSION_18\n\n\t/* these two fields were removed in PG 18 */\n\t.scan_bitmap_next_block = fake_scan_bitmap_next_block,\n\t.scan_bitmap_next_tuple = fake_scan_bitmap_next_tuple,\n#endif\n\n\t.scan_sample_next_block = fake_scan_sample_next_block,\n\t.scan_sample_next_tuple = fake_scan_sample_next_tuple\n};\n\n\nDatum\nfake_am_handler(PG_FUNCTION_ARGS)\n{\n\tPG_RETURN_POINTER(&fake_methods);\n}\n"
  },
  {
    "path": "src/backend/distributed/test/fake_fdw.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/fake_fdw.c\n *\n * This file contains a barebones FDW implementation, suitable for use in\n * test code. Inspired by Andrew Dunstan's blackhole_fdw.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <stddef.h>\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n\n#include \"executor/tuptable.h\"\n#include \"foreign/fdwapi.h\"\n#include \"nodes/execnodes.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/pathnodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/plannodes.h\"\n#include \"optimizer/pathnode.h\"\n#include \"optimizer/planmain.h\"\n#include \"optimizer/restrictinfo.h\"\n#include \"utils/palloc.h\"\n\n#include \"pg_version_compat.h\"\n\n/* local function forward declarations */\nstatic void FakeGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel,\n\t\t\t\t\t\t\t\t  Oid foreigntableid);\nstatic void FakeGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel,\n\t\t\t\t\t\t\t\tOid foreigntableid);\nstatic ForeignScan * FakeGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel,\n\t\t\t\t\t\t\t\t\t\tOid foreigntableid, ForeignPath *best_path,\n\t\t\t\t\t\t\t\t\t\tList *tlist, List *scan_clauses,\n\t\t\t\t\t\t\t\t\t\tPlan *outer_plan);\nstatic void FakeBeginForeignScan(ForeignScanState *node, int eflags);\nstatic TupleTableSlot * FakeIterateForeignScan(ForeignScanState *node);\nstatic void FakeReScanForeignScan(ForeignScanState *node);\nstatic void FakeEndForeignScan(ForeignScanState *node);\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(fake_fdw_handler);\n\n\n/*\n * fake_fdw_handler populates an FdwRoutine with pointers to the functions\n * implemented within this file.\n */\nDatum\nfake_fdw_handler(PG_FUNCTION_ARGS)\n{\n\tFdwRoutine *fdwroutine = makeNode(FdwRoutine);\n\n\tfdwroutine->GetForeignRelSize = FakeGetForeignRelSize;\n\tfdwroutine->GetForeignPaths = FakeGetForeignPaths;\n\tfdwroutine->GetForeignPlan = FakeGetForeignPlan;\n\tfdwroutine->BeginForeignScan = FakeBeginForeignScan;\n\tfdwroutine->IterateForeignScan = FakeIterateForeignScan;\n\tfdwroutine->ReScanForeignScan = FakeReScanForeignScan;\n\tfdwroutine->EndForeignScan = FakeEndForeignScan;\n\n\tPG_RETURN_POINTER(fdwroutine);\n}\n\n\n/*\n * FakeGetForeignRelSize populates baserel with a fake relation size.\n */\nstatic void\nFakeGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)\n{\n\tbaserel->rows = 0;\n\tbaserel->fdw_private = (void *) palloc0(1);\n}\n\n\n/*\n * FakeGetForeignPaths adds a single fake foreign path to baserel.\n */\nstatic void\nFakeGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid)\n{\n\tCost startup_cost = 0;\n\tCost total_cost = startup_cost + baserel->rows;\n\n\tadd_path(baserel, (Path *) create_foreignscan_path_compat(root, baserel, NULL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  baserel->rows,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  startup_cost, total_cost,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  NIL,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL, NULL, NIL, NIL));\n}\n\n\n/*\n * FakeGetForeignPlan builds a fake foreign plan.\n */\nstatic ForeignScan *\nFakeGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid,\n\t\t\t\t   ForeignPath *best_path, List *tlist, List *scan_clauses,\n\t\t\t\t   Plan *outer_plan)\n{\n\tIndex scan_relid = baserel->relid;\n\tscan_clauses = extract_actual_clauses(scan_clauses, false);\n\n\treturn make_foreignscan(tlist, scan_clauses, scan_relid, NIL, NIL, NIL, NIL,\n\t\t\t\t\t\t\touter_plan);\n}\n\n\n/*\n * FakeBeginForeignScan begins the fake plan (i.e. does nothing).\n */\nstatic void\nFakeBeginForeignScan(ForeignScanState *node, int eflags)\n{\n\t/* this comment is for indentation consistency */\n}\n\n\n/*\n * FakeIterateForeignScan continues the fake plan (i.e. does nothing).\n */\nstatic TupleTableSlot *\nFakeIterateForeignScan(ForeignScanState *node)\n{\n\tTupleTableSlot *slot = node->ss.ss_ScanTupleSlot;\n\tExecClearTuple(slot);\n\n\treturn slot;\n}\n\n\n/*\n * FakeReScanForeignScan restarts the fake plan (i.e. does nothing).\n */\nstatic void\nFakeReScanForeignScan(ForeignScanState *node)\n{\n\t/* this comment is for indentation consistency */\n}\n\n\n/*\n * FakeEndForeignScan ends the fake plan (i.e. does nothing).\n */\nstatic void\nFakeEndForeignScan(ForeignScanState *node)\n{\n\t/* this comment is for indentation consistency */\n}\n"
  },
  {
    "path": "src/backend/distributed/test/foreign_key_relationship_query.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * foreign_key_relationship_query.c\n *\n * This file contains UDFs for getting foreign constraint relationship between\n * distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"funcapi.h\"\n\n#include \"catalog/dependency.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/foreign_key_relationship.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/version_compat.h\"\n\n\n#define GET_FKEY_CONNECTED_RELATIONS_COLUMNS 1\n\n\n/* these functions are only exported in the regression tests */\nPG_FUNCTION_INFO_V1(get_referencing_relation_id_list);\nPG_FUNCTION_INFO_V1(get_referenced_relation_id_list);\nPG_FUNCTION_INFO_V1(get_foreign_key_connected_relations);\nPG_FUNCTION_INFO_V1(drop_constraint_cascade_via_perform_deletion);\n\n\n/*\n * drop_constraint_cascade_via_perform_deletion simply drops constraint on\n * relation via performDeletion.\n */\nDatum\ndrop_constraint_cascade_via_perform_deletion(PG_FUNCTION_ARGS)\n{\n\tOid relationId = PG_GETARG_OID(0);\n\n\tif (PG_ARGISNULL(1))\n\t{\n\t\t/* avoid unexpected crashes in regression tests */\n\t\tereport(ERROR, (errmsg(\"cannot perform operation without constraint \"\n\t\t\t\t\t\t\t   \"name argument\")));\n\t}\n\n\ttext *constraintNameText = PG_GETARG_TEXT_P(1);\n\tchar *constraintName = text_to_cstring(constraintNameText);\n\n\t/* error if constraint does not exist */\n\tbool missingOk = false;\n\tOid constraintId = get_relation_constraint_oid(relationId, constraintName, missingOk);\n\n\tObjectAddress constraintObjectAddress;\n\tconstraintObjectAddress.classId = ConstraintRelationId;\n\tconstraintObjectAddress.objectId = constraintId;\n\tconstraintObjectAddress.objectSubId = 0;\n\n\tperformDeletion(&constraintObjectAddress, DROP_CASCADE, 0);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * get_referencing_relation_id_list returns the list of table oids that is referencing\n * by given oid recursively. It uses the list cached in the distributed table cache\n * entry.\n */\nDatum\nget_referencing_relation_id_list(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tFuncCallContext *functionContext = NULL;\n\tListCell *foreignRelationCell = NULL;\n\n\t/* for the first we call this UDF, we need to populate the result to return set */\n\tif (SRF_IS_FIRSTCALL())\n\t{\n\t\tOid relationId = PG_GETARG_OID(0);\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\n\t\t/* create a function context for cross-call persistence */\n\t\tfunctionContext = SRF_FIRSTCALL_INIT();\n\n\t\tMemoryContext oldContext =\n\t\t\tMemoryContextSwitchTo(functionContext->multi_call_memory_ctx);\n\t\tList *refList = list_copy(\n\t\t\tcacheEntry->referencingRelationsViaForeignKey);\n\t\tListCellAndListWrapper *wrapper = palloc0(sizeof(ListCellAndListWrapper));\n\t\tforeignRelationCell = list_head(refList);\n\t\twrapper->list = refList;\n\t\twrapper->listCell = foreignRelationCell;\n\t\tfunctionContext->user_fctx = wrapper;\n\t\tMemoryContextSwitchTo(oldContext);\n\t}\n\n\t/*\n\t * On every call to this function, we get the current position in the\n\t * statement list. We then iterate to the next position in the list and\n\t * return the current statement, if we have not yet reached the end of\n\t * list.\n\t */\n\tfunctionContext = SRF_PERCALL_SETUP();\n\n\tListCellAndListWrapper *wrapper =\n\t\t(ListCellAndListWrapper *) functionContext->user_fctx;\n\tif (wrapper->listCell != NULL)\n\t{\n\t\tOid refId = lfirst_oid(wrapper->listCell);\n\n\t\twrapper->listCell = lnext(wrapper->list, wrapper->listCell);\n\n\t\tSRF_RETURN_NEXT(functionContext, ObjectIdGetDatum(refId));\n\t}\n\telse\n\t{\n\t\tSRF_RETURN_DONE(functionContext);\n\t}\n}\n\n\n/*\n * get_referenced_relation_id_list returns the list of table oids that is referenced\n * by given oid recursively. It uses the list cached in the distributed table cache\n * entry.\n */\nDatum\nget_referenced_relation_id_list(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tFuncCallContext *functionContext = NULL;\n\tListCell *foreignRelationCell = NULL;\n\n\t/* for the first we call this UDF, we need to populate the result to return set */\n\tif (SRF_IS_FIRSTCALL())\n\t{\n\t\tOid relationId = PG_GETARG_OID(0);\n\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\n\t\t/* create a function context for cross-call persistence */\n\t\tfunctionContext = SRF_FIRSTCALL_INIT();\n\n\t\tMemoryContext oldContext =\n\t\t\tMemoryContextSwitchTo(functionContext->multi_call_memory_ctx);\n\t\tList *refList = list_copy(cacheEntry->referencedRelationsViaForeignKey);\n\t\tforeignRelationCell = list_head(refList);\n\t\tListCellAndListWrapper *wrapper = palloc0(sizeof(ListCellAndListWrapper));\n\t\twrapper->list = refList;\n\t\twrapper->listCell = foreignRelationCell;\n\t\tfunctionContext->user_fctx = wrapper;\n\t\tMemoryContextSwitchTo(oldContext);\n\t}\n\n\t/*\n\t * On every call to this function, we get the current position in the\n\t * statement list. We then iterate to the next position in the list and\n\t * return the current statement, if we have not yet reached the end of\n\t * list.\n\t */\n\tfunctionContext = SRF_PERCALL_SETUP();\n\n\tListCellAndListWrapper *wrapper =\n\t\t(ListCellAndListWrapper *) functionContext->user_fctx;\n\n\tif (wrapper->listCell != NULL)\n\t{\n\t\tOid refId = lfirst_oid(wrapper->listCell);\n\n\t\twrapper->listCell = lnext(wrapper->list, wrapper->listCell);\n\n\t\tSRF_RETURN_NEXT(functionContext, ObjectIdGetDatum(refId));\n\t}\n\telse\n\t{\n\t\tSRF_RETURN_DONE(functionContext);\n\t}\n}\n\n\n/*\n * get_foreign_key_connected_relations takes a relation, and returns relations\n * that are connected to input relation via a foreign key graph.\n */\nDatum\nget_foreign_key_connected_relations(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tOid connectedRelationId;\n\tList *fkeyConnectedRelationIdList = GetForeignKeyConnectedRelationIdList(relationId);\n\tforeach_declared_oid(connectedRelationId, fkeyConnectedRelationIdList)\n\t{\n\t\tDatum values[GET_FKEY_CONNECTED_RELATIONS_COLUMNS];\n\t\tbool nulls[GET_FKEY_CONNECTED_RELATIONS_COLUMNS];\n\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(nulls, false, sizeof(nulls));\n\n\t\tvalues[0] = ObjectIdGetDatum(connectedRelationId);\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, nulls);\n\t}\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/global_pid.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/global_pid.c\n *\n * This file contains functions to test the global pid.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/metadata_cache.h\"\n\nPG_FUNCTION_INFO_V1(test_assign_global_pid);\n\n\n/*\n * test_assign_global_pid is the wrapper UDF for AssignGlobalPID and is only meant for use\n * in tests.\n */\nDatum\ntest_assign_global_pid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tAssignGlobalPID(application_name);\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/hide_shards.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * hide_shards.c\n *\n * This file contains functions to provide helper UDFs for hiding\n * shards from the applications.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/worker_shard_visibility.h\"\n\n\nPG_FUNCTION_INFO_V1(set_backend_type);\n\n/*\n * set_backend_type is an external API to set the MyBackendType and\n * re-checks the shard visibility.\n */\nDatum\nset_backend_type(PG_FUNCTION_ARGS)\n{\n\tEnsureSuperUser();\n\n\tMyBackendType = PG_GETARG_INT32(0);\n\n\telog(NOTICE, \"backend type switched to: %s\",\n\t\t GetBackendTypeDesc(MyBackendType));\n\n\tResetHideShardsDecision();\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/intermediate_results.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/intermediate_results.c\n *\n * This file contains functions to test functions related to\n * src/backend/distributed/executor/intermediate_results.c.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/remote_commands.h\"\n\nPG_FUNCTION_INFO_V1(store_intermediate_result_on_node);\n\n\n/*\n * store_intermediate_result_on_node executes a query and streams the results\n * into a file on the given node.\n */\nDatum\nstore_intermediate_result_on_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *nodeNameText = PG_GETARG_TEXT_P(0);\n\tchar *nodeNameString = text_to_cstring(nodeNameText);\n\tint nodePort = PG_GETARG_INT32(1);\n\ttext *resultIdText = PG_GETARG_TEXT_P(2);\n\tchar *resultIdString = text_to_cstring(resultIdText);\n\ttext *queryText = PG_GETARG_TEXT_P(3);\n\tchar *queryString = text_to_cstring(queryText);\n\tbool writeLocalFile = false;\n\tParamListInfo paramListInfo = NULL;\n\n\tWorkerNode *workerNode = FindWorkerNodeOrError(nodeNameString, nodePort);\n\n\t/*\n\t * Make sure that this transaction has a distributed transaction ID.\n\t *\n\t * Intermediate results will be stored in a directory that is derived\n\t * from the distributed transaction ID.\n\t */\n\tUseCoordinatedTransaction();\n\n\tEState *estate = CreateExecutorState();\n\tDestReceiver *resultDest = CreateRemoteFileDestReceiver(resultIdString, estate,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlist_make1(workerNode),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twriteLocalFile);\n\n\tExecuteQueryStringIntoDestReceiver(queryString, paramListInfo,\n\t\t\t\t\t\t\t\t\t   (DestReceiver *) resultDest);\n\n\tFreeExecutorState(estate);\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/make_external_connection.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/make_external_connection.c\n *\n * This file contains UDF to connect to a node without using the Citus\n * internal application_name.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/xact.h\"\n#include \"executor/spi.h\"\n#include \"lib/stringinfo.h\"\n#include \"postmaster/postmaster.h\"\n#include \"utils/builtins.h\"\n#include \"utils/memutils.h\"\n\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/function_utils.h\"\n#include \"distributed/intermediate_result_pruning.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/run_from_same_connection.h\"\n#include \"distributed/version_compat.h\"\n\n\nPG_FUNCTION_INFO_V1(make_external_connection_to_node);\n\n\n/*\n * make_external_connection_to_node opens a conneciton to a node\n * and keeps it until the end of the session.\n */\nDatum\nmake_external_connection_to_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tchar *nodeName = text_to_cstring(PG_GETARG_TEXT_P(0));\n\tuint32 nodePort = PG_GETARG_UINT32(1);\n\tchar *userName = text_to_cstring(PG_GETARG_TEXT_P(2));\n\tchar *databaseName = text_to_cstring(PG_GETARG_TEXT_P(3));\n\n\tStringInfo connectionString = makeStringInfo();\n\tappendStringInfo(connectionString,\n\t\t\t\t\t \"host=%s port=%d user=%s dbname=%s\",\n\t\t\t\t\t nodeName, nodePort, userName, databaseName);\n\n\tPGconn *pgConn = PQconnectdb(connectionString->data);\n\n\tif (PQstatus(pgConn) != CONNECTION_OK)\n\t{\n\t\tPQfinish(pgConn);\n\n\t\tereport(ERROR, (errmsg(\"connection failed\")));\n\t}\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/metadata_sync.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/metadata_sync.c\n *\n * This file contains functions to exercise the metadata snapshoy\n * generation functionality within Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n#include \"miscadmin.h\"\n\n#include \"catalog/pg_type.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/latch.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/connection_management.h\"\n#include \"distributed/intermediate_result_pruning.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/maintenanced.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/worker_manager.h\"\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(activate_node_snapshot);\nPG_FUNCTION_INFO_V1(wait_until_metadata_sync);\nPG_FUNCTION_INFO_V1(trigger_metadata_sync);\nPG_FUNCTION_INFO_V1(raise_error_in_metadata_sync);\n\n\n/*\n * activate_node_snapshot prints all the queries that are required\n * to activate a node.\n */\nDatum\nactivate_node_snapshot(PG_FUNCTION_ARGS)\n{\n\t/*\n\t * Activate node commands are created using the given worker node,\n\t * so we are using first primary worker node just for test purposes.\n\t */\n\tWorkerNode *dummyWorkerNode = GetFirstPrimaryWorkerNode();\n\tif (dummyWorkerNode == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"no worker nodes found\"),\n\t\t\t\t\t\terrdetail(\"Function activate_node_snapshot is meant to be \"\n\t\t\t\t\t\t\t\t  \"used when running tests on a multi-node cluster \"\n\t\t\t\t\t\t\t\t  \"with workers.\")));\n\t}\n\n\t/*\n\t * Create MetadataSyncContext which is used throughout nodes' activation.\n\t * As we set collectCommands to true, it would not create connections to workers.\n\t * Instead it would collect and return sync commands to be sent to workers.\n\t */\n\tbool collectCommands = true;\n\tbool nodesAddedInSameTransaction = false;\n\tMetadataSyncContext *context = CreateMetadataSyncContext(list_make1(dummyWorkerNode),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t collectCommands,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t nodesAddedInSameTransaction);\n\n\tActivateNodeList(context);\n\n\tList *activateNodeCommandList = context->collectedCommands;\n\tint activateNodeCommandIndex = 0;\n\tOid ddlCommandTypeId = TEXTOID;\n\n\tint activateNodeCommandCount = list_length(activateNodeCommandList);\n\tDatum *activateNodeCommandDatumArray = palloc0(activateNodeCommandCount *\n\t\t\t\t\t\t\t\t\t\t\t\t   sizeof(Datum));\n\n\tconst char *activateNodeSnapshotCommand = NULL;\n\tforeach_declared_ptr(activateNodeSnapshotCommand, activateNodeCommandList)\n\t{\n\t\tDatum activateNodeSnapshotCommandDatum = CStringGetTextDatum(\n\t\t\tactivateNodeSnapshotCommand);\n\n\t\tactivateNodeCommandDatumArray[activateNodeCommandIndex] =\n\t\t\tactivateNodeSnapshotCommandDatum;\n\t\tactivateNodeCommandIndex++;\n\t}\n\n\tArrayType *activateNodeCommandArrayType = DatumArrayToArrayType(\n\t\tactivateNodeCommandDatumArray,\n\t\tactivateNodeCommandCount,\n\t\tddlCommandTypeId);\n\n\tPG_RETURN_ARRAYTYPE_P(activateNodeCommandArrayType);\n}\n\n\n/*\n * IsMetadataSynced checks the workers to see if all workers with metadata are\n * synced.\n */\nstatic bool\nIsMetadataSynced(void)\n{\n\tList *workerList = ActivePrimaryNonCoordinatorNodeList(NoLock);\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerList)\n\t{\n\t\tif (workerNode->hasMetadata && !workerNode->metadataSynced)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * wait_until_metadata_sync waits until the maintenance daemon does a metadata\n * sync, or times out.\n */\nDatum\nwait_until_metadata_sync(PG_FUNCTION_ARGS)\n{\n\tuint32 timeout = PG_GETARG_UINT32(0);\n\n\t/* First we start listening. */\n\tMultiConnection *connection = GetNodeConnection(FORCE_NEW_CONNECTION,\n\t\t\t\t\t\t\t\t\t\t\t\t\tLocalHostName, PostPortNumber);\n\tExecuteCriticalRemoteCommand(connection, \"LISTEN \" METADATA_SYNC_CHANNEL);\n\n\t/*\n\t * If all the metadata nodes have already been synced, we should not wait.\n\t * That's primarily because the maintenance deamon might have already sent\n\t * the notification and we'd wait unnecessarily here. Worse, the test outputs\n\t * might be inconsistent across executions due to the warning.\n\t */\n\tif (IsMetadataSynced())\n\t{\n\t\tCloseConnection(connection);\n\t\tPG_RETURN_VOID();\n\t}\n\n\tint waitFlags = WL_SOCKET_READABLE | WL_TIMEOUT | WL_POSTMASTER_DEATH;\n\tint waitResult = WaitLatchOrSocket(NULL, waitFlags, PQsocket(connection->pgConn),\n\t\t\t\t\t\t\t\t\t   timeout, 0);\n\tif (waitResult & WL_POSTMASTER_DEATH)\n\t{\n\t\tereport(ERROR, (errmsg(\"postmaster was shut down, exiting\")));\n\t}\n\telse if (waitResult & WL_SOCKET_MASK)\n\t{\n\t\tClearResults(connection, true);\n\t}\n\telse if (waitResult & WL_TIMEOUT && !IsMetadataSynced())\n\t{\n\t\telog(WARNING, \"waiting for metadata sync timed out\");\n\t}\n\n\tCloseConnection(connection);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * trigger_metadata_sync triggers metadata sync for testing.\n */\nDatum\ntrigger_metadata_sync(PG_FUNCTION_ARGS)\n{\n\tTriggerNodeMetadataSyncOnCommit();\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * raise_error_in_metadata_sync causes metadata sync to raise an error.\n */\nDatum\nraise_error_in_metadata_sync(PG_FUNCTION_ARGS)\n{\n\t/* metadata sync uses SIGALRM to test errors */\n\tSignalMetadataSyncDaemon(MyDatabaseId, SIGALRM);\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/partitioning_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/partitioning_utils.c\n *\n * This file contains functions to test partitioning utility functions\n * implemented in Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"catalog/pg_type.h\"\n#include \"lib/stringinfo.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/listutils.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/reference_table_utils.h\"\n\n\nPG_FUNCTION_INFO_V1(generate_alter_table_detach_partition_command);\nPG_FUNCTION_INFO_V1(generate_alter_table_attach_partition_command);\nPG_FUNCTION_INFO_V1(generate_partition_information);\nPG_FUNCTION_INFO_V1(print_partitions);\nPG_FUNCTION_INFO_V1(table_inherits);\nPG_FUNCTION_INFO_V1(table_inherited);\n\n\n/*\n * Just a wrapper around GenereateDetachPartitionCommand().\n */\nDatum\ngenerate_alter_table_detach_partition_command(PG_FUNCTION_ARGS)\n{\n\tchar *command = \"\";\n\n\tcommand = GenerateDetachPartitionCommand(PG_GETARG_OID(0));\n\n\tPG_RETURN_TEXT_P(cstring_to_text(command));\n}\n\n\n/*\n * Just a wrapper around GenerateAlterTableAttachPartitionCommand().\n */\nDatum\ngenerate_alter_table_attach_partition_command(PG_FUNCTION_ARGS)\n{\n\tchar *command = \"\";\n\n\tcommand = GenerateAlterTableAttachPartitionCommand(PG_GETARG_OID(0));\n\n\tPG_RETURN_TEXT_P(cstring_to_text(command));\n}\n\n\n/*\n * Just a wrapper around GenereatePartitioningInformation().\n */\nDatum\ngenerate_partition_information(PG_FUNCTION_ARGS)\n{\n\tchar *command = \"\";\n\n\tcommand = GeneratePartitioningInformation(PG_GETARG_OID(0));\n\n\tPG_RETURN_TEXT_P(cstring_to_text(command));\n}\n\n\n/*\n * Just a wrapper around PartitionList() with human readable table name outpus.\n */\nDatum\nprint_partitions(PG_FUNCTION_ARGS)\n{\n\tStringInfo resultRelationNames = makeStringInfo();\n\n\tList *partitionList = PartitionList(PG_GETARG_OID(0));\n\tpartitionList = SortList(partitionList, CompareOids);\n\n\tOid partitionOid = InvalidOid;\n\tforeach_declared_oid(partitionOid, partitionList)\n\t{\n\t\t/* at least one table is already added, add comma */\n\t\tif (resultRelationNames->len > 0)\n\t\t{\n\t\t\tappendStringInfoString(resultRelationNames, \",\");\n\t\t}\n\n\t\tappendStringInfoString(resultRelationNames, get_rel_name(partitionOid));\n\t}\n\n\tPG_RETURN_TEXT_P(cstring_to_text(resultRelationNames->data));\n}\n\n\n/*\n * Just a wrapper around IsChildTable()\n */\nDatum\ntable_inherits(PG_FUNCTION_ARGS)\n{\n\treturn IsChildTable(PG_GETARG_OID(0));\n}\n\n\n/*\n * Just a wrapper around IsParentTable()\n */\nDatum\ntable_inherited(PG_FUNCTION_ARGS)\n{\n\treturn IsParentTable(PG_GETARG_OID(0));\n}\n"
  },
  {
    "path": "src/backend/distributed/test/progress_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * progress_utils.c\n *\n * This file contains functions to exercise progress monitoring functionality\n * within Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"nodes/execnodes.h\"\n#include \"utils/tuplestore.h\"\n\n#include \"distributed/listutils.h\"\n#include \"distributed/multi_progress.h\"\n#include \"distributed/tuplestore.h\"\n\n\nPG_FUNCTION_INFO_V1(create_progress);\nPG_FUNCTION_INFO_V1(update_progress);\nPG_FUNCTION_INFO_V1(finish_progress);\nPG_FUNCTION_INFO_V1(show_progress);\n\n\nDatum\ncreate_progress(PG_FUNCTION_ARGS)\n{\n\tuint64 magicNumber = PG_GETARG_INT64(0);\n\tint stepCount = PG_GETARG_INT32(1);\n\tdsm_handle dsmHandle;\n\tProgressMonitorData *monitor = CreateProgressMonitor(stepCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t sizeof(uint64), &dsmHandle);\n\n\tif (monitor != NULL)\n\t{\n\t\tuint64 *steps = (uint64 *) ProgressMonitorSteps(monitor);\n\n\t\tint i = 0;\n\t\tfor (; i < stepCount; i++)\n\t\t{\n\t\t\tsteps[i] = 0;\n\t\t}\n\t}\n\n\tRegisterProgressMonitor(magicNumber, 0, dsmHandle);\n\tPG_RETURN_VOID();\n}\n\n\nDatum\nupdate_progress(PG_FUNCTION_ARGS)\n{\n\tuint64 step = PG_GETARG_INT64(0);\n\tuint64 newValue = PG_GETARG_INT64(1);\n\n\tProgressMonitorData *monitor = GetCurrentProgressMonitor();\n\n\tif (monitor != NULL && step < monitor->stepCount)\n\t{\n\t\tuint64 *steps = (uint64 *) ProgressMonitorSteps(monitor);\n\t\tsteps[step] = newValue;\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\nDatum\nfinish_progress(PG_FUNCTION_ARGS)\n{\n\tFinalizeCurrentProgressMonitor();\n\n\tPG_RETURN_VOID();\n}\n\n\nDatum\nshow_progress(PG_FUNCTION_ARGS)\n{\n\tuint64 magicNumber = PG_GETARG_INT64(0);\n\tList *attachedDSMSegments = NIL;\n\tList *monitorList = ProgressMonitorList(magicNumber, &attachedDSMSegments);\n\tTupleDesc tupdesc;\n\tTuplestorestate *tupstore = SetupTuplestore(fcinfo, &tupdesc);\n\n\tProgressMonitorData *monitor = NULL;\n\tforeach_declared_ptr(monitor, monitorList)\n\t{\n\t\tuint64 *steps = ProgressMonitorSteps(monitor);\n\n\t\tfor (int stepIndex = 0; stepIndex < monitor->stepCount; stepIndex++)\n\t\t{\n\t\t\tuint64 step = steps[stepIndex];\n\n\t\t\tDatum values[2];\n\t\t\tbool nulls[2];\n\n\t\t\tmemset(values, 0, sizeof(values));\n\t\t\tmemset(nulls, 0, sizeof(nulls));\n\n\t\t\tvalues[0] = Int32GetDatum(stepIndex);\n\t\t\tvalues[1] = UInt64GetDatum(step);\n\n\t\t\ttuplestore_putvalues(tupstore, tupdesc, values, nulls);\n\t\t}\n\t}\n\n\tDetachFromDSMSegments(attachedDSMSegments);\n\n\treturn (Datum) 0;\n}\n"
  },
  {
    "path": "src/backend/distributed/test/prune_shard_list.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/create_shards.c\n *\n * This file contains functions to exercise shard creation functionality\n * within Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <string.h>\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n\n#include \"access/stratnum.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n#include \"optimizer/clauses.h\"\n#include \"utils/array.h\"\n#include \"utils/palloc.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/utils/array_type.h\"\n\n\n/* local function forward declarations */\nstatic Expr * MakeTextPartitionExpression(Oid distributedTableId, text *value);\nstatic ArrayType * PrunedShardIdsForTable(Oid distributedTableId, List *whereClauseList);\nstatic ArrayType * SortedShardIntervalArray(Oid distributedTableId);\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(prune_using_no_values);\nPG_FUNCTION_INFO_V1(prune_using_single_value);\nPG_FUNCTION_INFO_V1(prune_using_either_value);\nPG_FUNCTION_INFO_V1(prune_using_both_values);\nPG_FUNCTION_INFO_V1(debug_equality_expression);\nPG_FUNCTION_INFO_V1(print_sorted_shard_intervals);\n\n\n/*\n * prune_using_no_values returns the shards for the specified distributed table\n * after pruning using an empty clause list.\n */\nDatum\nprune_using_no_values(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\tList *whereClauseList = NIL;\n\tArrayType *shardIdArrayType = PrunedShardIdsForTable(distributedTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t whereClauseList);\n\n\tPG_RETURN_ARRAYTYPE_P(shardIdArrayType);\n}\n\n\n/*\n * prune_using_single_value returns the shards for the specified distributed\n * table after pruning using a single value provided by the caller.\n */\nDatum\nprune_using_single_value(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\ttext *value = (PG_ARGISNULL(1)) ? NULL : PG_GETARG_TEXT_P(1);\n\tExpr *equalityExpr = MakeTextPartitionExpression(distributedTableId, value);\n\tList *whereClauseList = list_make1(equalityExpr);\n\tArrayType *shardIdArrayType = PrunedShardIdsForTable(distributedTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t whereClauseList);\n\n\tPG_RETURN_ARRAYTYPE_P(shardIdArrayType);\n}\n\n\n/*\n * prune_using_either_value returns the shards for the specified distributed\n * table after pruning using either of two values provided by the caller (OR).\n */\nDatum\nprune_using_either_value(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\ttext *firstValue = PG_GETARG_TEXT_P(1);\n\ttext *secondValue = PG_GETARG_TEXT_P(2);\n\tExpr *firstQual = MakeTextPartitionExpression(distributedTableId, firstValue);\n\tExpr *secondQual = MakeTextPartitionExpression(distributedTableId, secondValue);\n\tExpr *orClause = make_orclause(list_make2(firstQual, secondQual));\n\tList *whereClauseList = list_make1(orClause);\n\tArrayType *shardIdArrayType = PrunedShardIdsForTable(distributedTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t whereClauseList);\n\n\tPG_RETURN_ARRAYTYPE_P(shardIdArrayType);\n}\n\n\n/*\n * prune_using_both_values returns the shards for the specified distributed\n * table after pruning using both of the values provided by the caller (AND).\n */\nDatum\nprune_using_both_values(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\ttext *firstValue = PG_GETARG_TEXT_P(1);\n\ttext *secondValue = PG_GETARG_TEXT_P(2);\n\tExpr *firstQual = MakeTextPartitionExpression(distributedTableId, firstValue);\n\tExpr *secondQual = MakeTextPartitionExpression(distributedTableId, secondValue);\n\n\tList *whereClauseList = list_make2(firstQual, secondQual);\n\tArrayType *shardIdArrayType = PrunedShardIdsForTable(distributedTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t whereClauseList);\n\n\tPG_RETURN_ARRAYTYPE_P(shardIdArrayType);\n}\n\n\n/*\n * debug_equality_expression returns the textual representation of an equality\n * expression generated by a call to MakeOpExpression.\n */\nDatum\ndebug_equality_expression(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\tuint32 rangeTableId = 1;\n\tif (!IsCitusTableType(distributedTableId, HASH_DISTRIBUTED))\n\t{\n\t\tereport(ERROR, (errmsg(\"table needs to be hash distributed\")));\n\t}\n\tVar *partitionColumn = PartitionColumn(distributedTableId, rangeTableId);\n\tOpExpr *equalityExpression = MakeOpExpression(partitionColumn, BTEqualStrategyNumber);\n\n\tPG_RETURN_CSTRING(nodeToString(equalityExpression));\n}\n\n\n/*\n * print_sorted_shard_intervals prints the sorted shard interval array that is in the\n * metadata cache. This function aims to test sorting functionality.\n */\nDatum\nprint_sorted_shard_intervals(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\n\tArrayType *shardIdArrayType = SortedShardIntervalArray(distributedTableId);\n\n\tPG_RETURN_ARRAYTYPE_P(shardIdArrayType);\n}\n\n\n/*\n * MakeTextPartitionExpression returns an equality expression between the\n * specified table's partition column and the provided values.\n */\nstatic Expr *\nMakeTextPartitionExpression(Oid distributedTableId, text *value)\n{\n\tuint32 rangeTableId = 1;\n\tVar *partitionColumn = PartitionColumn(distributedTableId, rangeTableId);\n\tExpr *partitionExpression = NULL;\n\n\tif (value != NULL)\n\t{\n\t\tOpExpr *equalityExpr = MakeOpExpression(partitionColumn, BTEqualStrategyNumber);\n\t\tNode *rightOp = get_rightop((Expr *) equalityExpr);\n\n\t\tAssert(rightOp != NULL);\n\t\tAssert(IsA(rightOp, Const));\n\t\tConst *rightConst = (Const *) rightOp;\n\n\t\trightConst->constvalue = (Datum) value;\n\t\trightConst->constisnull = false;\n\t\trightConst->constbyval = false;\n\n\t\tpartitionExpression = (Expr *) equalityExpr;\n\t}\n\telse\n\t{\n\t\tNullTest *nullTest = makeNode(NullTest);\n\t\tnullTest->arg = (Expr *) partitionColumn;\n\t\tnullTest->nulltesttype = IS_NULL;\n\n\t\tpartitionExpression = (Expr *) nullTest;\n\t}\n\n\treturn partitionExpression;\n}\n\n\n/*\n * PrunedShardIdsForTable loads the shard intervals for the specified table,\n * prunes them using the provided clauses. It returns an ArrayType containing\n * the shard identifiers, suitable for return from an SQL-facing function.\n */\nstatic ArrayType *\nPrunedShardIdsForTable(Oid distributedTableId, List *whereClauseList)\n{\n\tint shardIdIndex = 0;\n\tOid shardIdTypeId = INT8OID;\n\tIndex tableId = 1;\n\n\n\tList *shardList = PruneShards(distributedTableId, tableId, whereClauseList, NULL);\n\n\tint shardIdCount = list_length(shardList);\n\tDatum *shardIdDatumArray = palloc0(shardIdCount * sizeof(Datum));\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardList)\n\t{\n\t\tDatum shardIdDatum = Int64GetDatum(shardInterval->shardId);\n\n\t\tshardIdDatumArray[shardIdIndex] = shardIdDatum;\n\t\tshardIdIndex++;\n\t}\n\n\tArrayType *shardIdArrayType = DatumArrayToArrayType(shardIdDatumArray, shardIdCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardIdTypeId);\n\n\treturn shardIdArrayType;\n}\n\n\n/*\n * SortedShardIntervalArray simply returns the shard interval ids in the sorted shard\n * interval cache as a datum array.\n */\nstatic ArrayType *\nSortedShardIntervalArray(Oid distributedTableId)\n{\n\tOid shardIdTypeId = INT8OID;\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(distributedTableId);\n\tShardInterval **shardIntervalArray = cacheEntry->sortedShardIntervalArray;\n\tint shardIdCount = cacheEntry->shardIntervalArrayLength;\n\tDatum *shardIdDatumArray = palloc0(shardIdCount * sizeof(Datum));\n\n\tfor (int shardIndex = 0; shardIndex < shardIdCount; ++shardIndex)\n\t{\n\t\tShardInterval *shardId = shardIntervalArray[shardIndex];\n\t\tDatum shardIdDatum = Int64GetDatum(shardId->shardId);\n\n\t\tshardIdDatumArray[shardIndex] = shardIdDatum;\n\t}\n\n\tArrayType *shardIdArrayType = DatumArrayToArrayType(shardIdDatumArray, shardIdCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardIdTypeId);\n\n\treturn shardIdArrayType;\n}\n"
  },
  {
    "path": "src/backend/distributed/test/relation_access_tracking.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/relation_acess_tracking.c\n *\n * Some test UDF for tracking relation accesses within transaction blocks.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n\n#include \"distributed/relation_access_tracking.h\"\n\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(relation_select_access_mode);\nPG_FUNCTION_INFO_V1(relation_dml_access_mode);\nPG_FUNCTION_INFO_V1(relation_ddl_access_mode);\n\n/*\n * relation_select_access_mode returns the SELECT access\n * type (e.g., single shard - multi shard) for the given relation.\n */\nDatum\nrelation_select_access_mode(PG_FUNCTION_ARGS)\n{\n\tOid relationId = PG_GETARG_OID(0);\n\n\tPG_RETURN_INT64(GetRelationSelectAccessMode(relationId));\n}\n\n\n/*\n * relation_dml_access_mode returns the DML access type (e.g.,\n * single shard - multi shard) for the given relation.\n */\nDatum\nrelation_dml_access_mode(PG_FUNCTION_ARGS)\n{\n\tOid relationId = PG_GETARG_OID(0);\n\n\tPG_RETURN_INT64(GetRelationDMLAccessMode(relationId));\n}\n\n\n/*\n * relation_ddl_access_mode returns the DDL access type (e.g.,\n * single shard - multi shard) for the given relation.\n */\nDatum\nrelation_ddl_access_mode(PG_FUNCTION_ARGS)\n{\n\tOid relationId = PG_GETARG_OID(0);\n\n\tPG_RETURN_INT64(GetRelationDDLAccessMode(relationId));\n}\n"
  },
  {
    "path": "src/backend/distributed/test/run_from_same_connection.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/run_from_same_connection.c\n *\n * This file contains UDF to run consecutive commands on worker node from the\n * same connection. UDFs will be used to test MX functionalities in isolation\n * tests.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/xact.h\"\n#include \"executor/spi.h\"\n#include \"lib/stringinfo.h\"\n#include \"postmaster/postmaster.h\"\n#include \"utils/builtins.h\"\n#include \"utils/memutils.h\"\n\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/function_utils.h\"\n#include \"distributed/intermediate_result_pruning.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/run_from_same_connection.h\"\n#include \"distributed/version_compat.h\"\n\n\n#define ALTER_CURRENT_PROCESS_ID \\\n\t\t\"ALTER SYSTEM SET citus.isolation_test_session_process_id TO %d\"\n#define ALTER_CURRENT_WORKER_PROCESS_ID \\\n\t\t\"ALTER SYSTEM SET citus.isolation_test_session_remote_process_id TO %ld\"\n#define GET_PROCESS_ID \"SELECT process_id FROM get_current_transaction_id()\"\n\n\nstatic bool allowNonIdleRemoteTransactionOnXactHandling = false;\nstatic MultiConnection *singleConnection = NULL;\n\n\n/*\n * Config variables which will be used by isolation framework to check transactions\n * initiated from worker nodes.\n */\nint IsolationTestSessionRemoteProcessID = -1;\nint IsolationTestSessionProcessID = -1;\n\n\nstatic int64 GetRemoteProcessId(void);\n\n/* declarations for dynamic loading */\nPG_FUNCTION_INFO_V1(start_session_level_connection_to_node);\nPG_FUNCTION_INFO_V1(run_commands_on_session_level_connection_to_node);\nPG_FUNCTION_INFO_V1(stop_session_level_connection_to_node);\nPG_FUNCTION_INFO_V1(override_backend_data_gpid);\n\n\n/*\n * AllowNonIdleTransactionOnXactHandling allows connection opened with\n * SESSION_LIFESPAN remain opened even if it is not idle.\n */\nbool\nAllowNonIdleTransactionOnXactHandling(void)\n{\n\treturn allowNonIdleRemoteTransactionOnXactHandling;\n}\n\n\n/*\n * start_session_level_connection_to_node helps us to open and keep connections\n * open while sending consecutive commands, even if they are outside the transaction.\n * To use the connection opened with an open transaction, we have implemented a hacky\n * solution by setting a static flag, allowNonIdleRemoteTransactionOnXactHandling, on\n * this file to true. That gives us to chance to keep that connection open.\n *\n * Note that, this UDF shouldn't be used outside the isolation tests.\n */\nDatum\nstart_session_level_connection_to_node(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *nodeName = PG_GETARG_TEXT_P(0);\n\tuint32 nodePort = PG_GETARG_UINT32(1);\n\tchar *nodeNameString = text_to_cstring(nodeName);\n\tint connectionFlags = 0;\n\n\tif (singleConnection != NULL && (strcmp(singleConnection->hostname,\n\t\t\t\t\t\t\t\t\t\t\tnodeNameString) != 0 ||\n\t\t\t\t\t\t\t\t\t singleConnection->port != nodePort))\n\t{\n\t\telog(ERROR,\n\t\t\t \"can not connect different worker nodes from the same session using start_session_level_connection_to_node\");\n\t}\n\n\t/*\n\t * In order to keep connection open even with an open transaction,\n\t * allowSessionLifeSpanWithOpenTransaction is set to true.\n\t */\n\tif (singleConnection == NULL)\n\t{\n\t\tsingleConnection = GetNodeConnection(connectionFlags, nodeNameString, nodePort);\n\t\tallowNonIdleRemoteTransactionOnXactHandling = true;\n\t}\n\n\tif (PQstatus(singleConnection->pgConn) != CONNECTION_OK)\n\t{\n\t\telog(ERROR, \"failed to connect to %s:%d\", nodeNameString, (int) nodePort);\n\t}\n\n\t/* pretend we are a regular client to avoid citus-initiated backend checks */\n\tconst char *setAppName =\n\t\t\"SET application_name TO run_commands_on_session_level_connection_to_node\";\n\n\tExecuteCriticalRemoteCommand(singleConnection, setAppName);\n\n\t/*\n\t * We are hackily overriding the remote processes' gpid value such that\n\t * blocked process detection continues to work.\n\t */\n\tStringInfo overrideBackendDataCommandOriginator = makeStringInfo();\n\tappendStringInfo(overrideBackendDataCommandOriginator,\n\t\t\t\t\t \"SELECT override_backend_data_gpid(%lu);\",\n\t\t\t\t\t GetGlobalPID());\n\tExecuteCriticalRemoteCommand(singleConnection,\n\t\t\t\t\t\t\t\t overrideBackendDataCommandOriginator->data);\n\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * run_commands_on_session_level_connection_to_node runs to consecutive commands\n * from the same connection opened by start_session_level_connection_to_node.\n *\n * Since transactions can be initiated from worker nodes with MX, we need to\n * keep them open on the worker node to check whether there exist a waiting\n * transaction in test steps. In order to release the locks taken in the\n * transaction we need to send related unlock commands from the same connection\n * as well.\n */\nDatum\nrun_commands_on_session_level_connection_to_node(PG_FUNCTION_ARGS)\n{\n\ttext *queryText = PG_GETARG_TEXT_P(0);\n\tchar *queryString = text_to_cstring(queryText);\n\n\tStringInfo processStringInfo = makeStringInfo();\n\tStringInfo workerProcessStringInfo = makeStringInfo();\n\tMultiConnection *localConnection = GetNodeConnection(0, LocalHostName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t PostPortNumber);\n\n\tif (!singleConnection)\n\t{\n\t\telog(ERROR,\n\t\t\t \"start_session_level_connection_to_node must be called first to open a session level connection\");\n\t}\n\n\tappendStringInfo(processStringInfo, ALTER_CURRENT_PROCESS_ID, MyProcPid);\n\tappendStringInfo(workerProcessStringInfo, ALTER_CURRENT_WORKER_PROCESS_ID,\n\t\t\t\t\t GetRemoteProcessId());\n\n\tExecuteCriticalRemoteCommand(singleConnection, queryString);\n\n\t/*\n\t * Since we cannot run `ALTER SYSTEM` command within a transaction, we are\n\t * calling it from a self-connected session.\n\t */\n\tExecuteCriticalRemoteCommand(localConnection, processStringInfo->data);\n\tExecuteCriticalRemoteCommand(localConnection, workerProcessStringInfo->data);\n\n\tCloseConnection(localConnection);\n\n\t/* Call pg_reload_conf UDF to update changed GUCs above on each backend */\n\tOid pgReloadConfOid = FunctionOid(\"pg_catalog\", \"pg_reload_conf\", 0);\n\tOidFunctionCall0(pgReloadConfOid);\n\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * override_backend_data_gpid is a wrapper around SetBackendDataGpid().\n * Also sets distributedCommandOriginator to true since the only caller of\n * this method calls this function actually wants this backend to\n * be treated as a distributed command originator with the given global pid.\n */\nDatum\noverride_backend_data_gpid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tuint64 gpid = PG_GETARG_INT64(0);\n\n\tSetBackendDataGlobalPID(gpid);\n\tSetBackendDataDistributedCommandOriginator(true);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * stop_session_level_connection_to_node closes the connection opened by the\n * start_session_level_connection_to_node and set the flag to false which\n * allows connection API to keep connections with open transaction.\n */\nDatum\nstop_session_level_connection_to_node(PG_FUNCTION_ARGS)\n{\n\tallowNonIdleRemoteTransactionOnXactHandling = false;\n\n\tif (singleConnection != NULL)\n\t{\n\t\tCloseConnection(singleConnection);\n\t\tsingleConnection = NULL;\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * GetRemoteProcessId() get the process id of remote transaction opened\n * by the connection.\n */\nstatic int64\nGetRemoteProcessId()\n{\n\tStringInfo queryStringInfo = makeStringInfo();\n\tPGresult *result = NULL;\n\n\tappendStringInfo(queryStringInfo, GET_PROCESS_ID);\n\n\tint queryResult = ExecuteOptionalRemoteCommand(singleConnection,\n\t\t\t\t\t\t\t\t\t\t\t\t   queryStringInfo->data, &result);\n\tif (queryResult != RESPONSE_OKAY)\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\tint64 rowCount = PQntuples(result);\n\tif (rowCount != 1)\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\tint64 resultValue = ParseIntField(result, 0, 0);\n\n\tPQclear(result);\n\tClearResults(singleConnection, false);\n\n\treturn resultValue;\n}\n"
  },
  {
    "path": "src/backend/distributed/test/sequential_execution.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/sequential_execution.c\n *\n * This file contains functions to test setting citus.multi_shard_modify_mode\n * GUC.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"distributed/multi_executor.h\"\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(set_local_multi_shard_modify_mode_to_sequential);\n\n\n/*\n * set_local_multi_shard_modify_mode_to_sequential is a SQL\n * interface for testing SetLocalMultiShardModifyModeToSequential().\n */\nDatum\nset_local_multi_shard_modify_mode_to_sequential(PG_FUNCTION_ARGS)\n{\n\tSetLocalMultiShardModifyModeToSequential();\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/shard_rebalancer.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/shard_rebalancer.c\n *\n * This file contains functions used for unit testing the planning part of the\n * shard rebalancer.\n *\n * Copyright (c) 2014-2019, Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"safe_lib.h\"\n\n#include \"catalog/pg_type.h\"\n#include \"utils/builtins.h\"\n#include \"utils/json.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/shard_rebalancer.h\"\n\n/* static declarations for json conversion */\nstatic List * JsonArrayToShardPlacementTestInfoList(ArrayType *\n\t\t\t\t\t\t\t\t\t\t\t\t\tshardPlacementJsonArrayObject);\nstatic List * JsonArrayToWorkerTestInfoList(ArrayType *workerNodeJsonArrayObject);\nstatic bool JsonFieldValueBoolDefault(Datum jsonDocument, const char *key,\n\t\t\t\t\t\t\t\t\t  bool defaultValue);\nstatic uint32 JsonFieldValueUInt32Default(Datum jsonDocument, const char *key,\n\t\t\t\t\t\t\t\t\t\t  uint32 defaultValue);\nstatic uint64 JsonFieldValueUInt64Default(Datum jsonDocument, const char *key,\n\t\t\t\t\t\t\t\t\t\t  uint64 defaultValue);\nstatic char * JsonFieldValueString(Datum jsonDocument, const char *key);\nstatic ArrayType * PlacementUpdateListToJsonArray(List *placementUpdateList);\nstatic bool ShardAllowedOnNode(uint64 shardId, WorkerNode *workerNode, void *context);\nstatic float NodeCapacity(WorkerNode *workerNode, void *context);\nstatic ShardCost GetShardCost(uint64 shardId, void *context);\n\n\nPG_FUNCTION_INFO_V1(shard_placement_rebalance_array);\nPG_FUNCTION_INFO_V1(shard_placement_replication_array);\nPG_FUNCTION_INFO_V1(worker_node_responsive);\nPG_FUNCTION_INFO_V1(run_try_drop_marked_resources);\n\ntypedef struct ShardPlacementTestInfo\n{\n\tShardPlacement *placement;\n\tuint64 cost;\n\tbool nextColocationGroup;\n} ShardPlacementTestInfo;\n\ntypedef struct WorkerTestInfo\n{\n\tWorkerNode *node;\n\tList *disallowedShardIds;\n\tfloat capacity;\n} WorkerTestInfo;\n\ntypedef struct RebalancePlanContext\n{\n\tList *workerTestInfoList;\n\tList *shardPlacementTestInfoList;\n} RebalancePlacementContext;\n\n/*\n * run_try_drop_marked_resources is a wrapper to run TryDropOrphanedResources.\n */\nDatum\nrun_try_drop_marked_resources(PG_FUNCTION_ARGS)\n{\n\tTryDropOrphanedResources();\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * shard_placement_rebalance_array returns a list of operations which can make a\n * cluster consisting of given shard placements and worker nodes balanced with\n * respect to the given threshold. Threshold is a value between 0 and 1 which\n * determines the evenness in shard distribution. When threshold is 0, then all\n * nodes should have equal number of shards. As threshold increases, cluster's\n * evenness requirements decrease, and we can rebalance the cluster using less\n * operations.\n */\nDatum\nshard_placement_rebalance_array(PG_FUNCTION_ARGS)\n{\n\tArrayType *workerNodeJsonArray = PG_GETARG_ARRAYTYPE_P(0);\n\tArrayType *shardPlacementJsonArray = PG_GETARG_ARRAYTYPE_P(1);\n\tfloat threshold = PG_GETARG_FLOAT4(2);\n\tint32 maxShardMoves = PG_GETARG_INT32(3);\n\tbool drainOnly = PG_GETARG_BOOL(4);\n\tfloat utilizationImproventThreshold = PG_GETARG_FLOAT4(5);\n\n\tList *workerNodeList = NIL;\n\tList *shardPlacementListList = NIL;\n\tList *shardPlacementList = NIL;\n\tWorkerTestInfo *workerTestInfo = NULL;\n\tShardPlacementTestInfo *shardPlacementTestInfo = NULL;\n\tRebalancePlanFunctions rebalancePlanFunctions = {\n\t\t.shardAllowedOnNode = ShardAllowedOnNode,\n\t\t.nodeCapacity = NodeCapacity,\n\t\t.shardCost = GetShardCost,\n\t};\n\tRebalancePlacementContext context = {\n\t\t.workerTestInfoList = NULL,\n\t};\n\n\tcontext.workerTestInfoList = JsonArrayToWorkerTestInfoList(workerNodeJsonArray);\n\tcontext.shardPlacementTestInfoList = JsonArrayToShardPlacementTestInfoList(\n\t\tshardPlacementJsonArray);\n\n\t/* we don't need original arrays any more, so we free them to save memory */\n\tpfree(workerNodeJsonArray);\n\tpfree(shardPlacementJsonArray);\n\n\t/* map workerTestInfoList to a list of its WorkerNodes */\n\tforeach_declared_ptr(workerTestInfo, context.workerTestInfoList)\n\t{\n\t\tworkerNodeList = lappend(workerNodeList, workerTestInfo->node);\n\t}\n\n\t/* map shardPlacementTestInfoList to a list of list of its ShardPlacements */\n\tforeach_declared_ptr(shardPlacementTestInfo, context.shardPlacementTestInfoList)\n\t{\n\t\tif (shardPlacementTestInfo->nextColocationGroup)\n\t\t{\n\t\t\tshardPlacementList = SortList(shardPlacementList, CompareShardPlacements);\n\t\t\tshardPlacementListList = lappend(shardPlacementListList, shardPlacementList);\n\t\t\tshardPlacementList = NIL;\n\t\t}\n\t\tshardPlacementList = lappend(shardPlacementList,\n\t\t\t\t\t\t\t\t\t shardPlacementTestInfo->placement);\n\t}\n\tshardPlacementList = SortList(shardPlacementList, CompareShardPlacements);\n\tshardPlacementListList = lappend(shardPlacementListList, shardPlacementList);\n\n\tList *unbalancedShards = NIL;\n\tListCell *shardPlacementListCell = NULL;\n\tforeach(shardPlacementListCell, shardPlacementListList)\n\t{\n\t\tList *placementList = (List *) lfirst(shardPlacementListCell);\n\n\t\tif (list_length(placementList) < list_length(workerNodeList))\n\t\t{\n\t\t\tunbalancedShards = list_concat(unbalancedShards,\n\t\t\t\t\t\t\t\t\t\t   placementList);\n\t\t\tshardPlacementListList = foreach_delete_current(shardPlacementListList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardPlacementListCell);\n\t\t}\n\t}\n\n\tif (list_length(unbalancedShards) > 0)\n\t{\n\t\tshardPlacementListList = lappend(shardPlacementListList, unbalancedShards);\n\t}\n\n\trebalancePlanFunctions.context = &context;\n\n\t/* sort the lists to make the function more deterministic */\n\tworkerNodeList = SortList(workerNodeList, CompareWorkerNodes);\n\n\tList *placementUpdateList = RebalancePlacementUpdates(workerNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardPlacementListList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  threshold,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  maxShardMoves,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  drainOnly,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  utilizationImproventThreshold,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &rebalancePlanFunctions);\n\tArrayType *placementUpdateJsonArray = PlacementUpdateListToJsonArray(\n\t\tplacementUpdateList);\n\n\tPG_RETURN_ARRAYTYPE_P(placementUpdateJsonArray);\n}\n\n\n/*\n * ShardAllowedOnNode is the function that checks if shard is allowed to be on\n * a worker when running the shard rebalancer unit tests.\n */\nstatic bool\nShardAllowedOnNode(uint64 shardId, WorkerNode *workerNode, void *voidContext)\n{\n\tRebalancePlacementContext *context = voidContext;\n\tWorkerTestInfo *workerTestInfo = NULL;\n\tuint64 *disallowedShardIdPtr = NULL;\n\tforeach_declared_ptr(workerTestInfo, context->workerTestInfoList)\n\t{\n\t\tif (workerTestInfo->node == workerNode)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\tAssert(workerTestInfo != NULL);\n\n\tforeach_declared_ptr(disallowedShardIdPtr, workerTestInfo->disallowedShardIds)\n\t{\n\t\tif (shardId == *disallowedShardIdPtr)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\n\n/*\n * NodeCapacity is the function that gets the capacity of a worker when running\n * the shard rebalancer unit tests.\n */\nstatic float\nNodeCapacity(WorkerNode *workerNode, void *voidContext)\n{\n\tRebalancePlacementContext *context = voidContext;\n\tWorkerTestInfo *workerTestInfo = NULL;\n\tforeach_declared_ptr(workerTestInfo, context->workerTestInfoList)\n\t{\n\t\tif (workerTestInfo->node == workerNode)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\tAssert(workerTestInfo != NULL);\n\treturn workerTestInfo->capacity;\n}\n\n\n/*\n * GetShardCost is the function that gets the ShardCost of a shard when running\n * the shard rebalancer unit tests.\n */\nstatic ShardCost\nGetShardCost(uint64 shardId, void *voidContext)\n{\n\tRebalancePlacementContext *context = voidContext;\n\tShardCost shardCost;\n\tmemset_struct_0(shardCost);\n\tshardCost.shardId = shardId;\n\n\tShardPlacementTestInfo *shardPlacementTestInfo = NULL;\n\tforeach_declared_ptr(shardPlacementTestInfo, context->shardPlacementTestInfoList)\n\t{\n\t\tif (shardPlacementTestInfo->placement->shardId == shardId)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\tAssert(shardPlacementTestInfo != NULL);\n\tshardCost.cost = shardPlacementTestInfo->cost;\n\treturn shardCost;\n}\n\n\n/*\n * shard_placement_replication_array returns a list of operations which will\n * replicate under-replicated shards in a cluster consisting of given shard\n * placements and worker nodes. A shard is under-replicated if it has less\n * active placements than the given shard replication factor.\n */\nDatum\nshard_placement_replication_array(PG_FUNCTION_ARGS)\n{\n\tArrayType *workerNodeJsonArray = PG_GETARG_ARRAYTYPE_P(0);\n\tArrayType *shardPlacementJsonArray = PG_GETARG_ARRAYTYPE_P(1);\n\tuint32 shardReplicationFactor = PG_GETARG_INT32(2);\n\n\tList *workerNodeList = NIL;\n\tList *shardPlacementList = NIL;\n\tWorkerTestInfo *workerTestInfo = NULL;\n\tShardPlacementTestInfo *shardPlacementTestInfo = NULL;\n\n\t/* validate shard replication factor */\n\tif (shardReplicationFactor < SHARD_REPLICATION_FACTOR_MINIMUM ||\n\t\tshardReplicationFactor > SHARD_REPLICATION_FACTOR_MAXIMUM)\n\t{\n\t\tereport(ERROR, (errmsg(\"invalid shard replication factor\"),\n\t\t\t\t\t\terrhint(\"Shard replication factor must be an integer \"\n\t\t\t\t\t\t\t\t\"between %d and %d\", SHARD_REPLICATION_FACTOR_MINIMUM,\n\t\t\t\t\t\t\t\tSHARD_REPLICATION_FACTOR_MAXIMUM)));\n\t}\n\n\tList *workerTestInfoList = JsonArrayToWorkerTestInfoList(workerNodeJsonArray);\n\tList *shardPlacementTestInfoList = JsonArrayToShardPlacementTestInfoList(\n\t\tshardPlacementJsonArray);\n\n\t/* we don't need original arrays any more, so we free them to save memory */\n\tpfree(workerNodeJsonArray);\n\tpfree(shardPlacementJsonArray);\n\n\tforeach_declared_ptr(workerTestInfo, workerTestInfoList)\n\t{\n\t\tworkerNodeList = lappend(workerNodeList, workerTestInfo->node);\n\t}\n\n\tforeach_declared_ptr(shardPlacementTestInfo, shardPlacementTestInfoList)\n\t{\n\t\tshardPlacementList = lappend(shardPlacementList,\n\t\t\t\t\t\t\t\t\t shardPlacementTestInfo->placement);\n\t}\n\n\tList *activeShardPlacementList = shardPlacementList;\n\n\t/* sort the lists to make the function more deterministic */\n\tworkerNodeList = SortList(workerNodeList, CompareWorkerNodes);\n\tactiveShardPlacementList = SortList(activeShardPlacementList, CompareShardPlacements);\n\n\tList *placementUpdateList = ReplicationPlacementUpdates(workerNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tactiveShardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardReplicationFactor);\n\tArrayType *placementUpdateJsonArray = PlacementUpdateListToJsonArray(\n\t\tplacementUpdateList);\n\n\tPG_RETURN_ARRAYTYPE_P(placementUpdateJsonArray);\n}\n\n\n/*\n * JsonArrayToShardPlacementTestInfoList converts the given shard placement json array\n * to a list of ShardPlacement structs.\n */\nstatic List *\nJsonArrayToShardPlacementTestInfoList(ArrayType *shardPlacementJsonArrayObject)\n{\n\tList *shardPlacementTestInfoList = NIL;\n\tDatum *shardPlacementJsonArray = NULL;\n\tint placementCount = 0;\n\n\t/*\n\t * Memory is not automatically freed when we call UDFs using DirectFunctionCall.\n\t * We call these functions in functionCallContext, so we can free the memory\n\t * once they return.\n\t */\n\tMemoryContext functionCallContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Function Call Context\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MINSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_INITSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MAXSIZE);\n\n\tdeconstruct_array(shardPlacementJsonArrayObject, JSONOID, -1, false, 'i',\n\t\t\t\t\t  &shardPlacementJsonArray, NULL, &placementCount);\n\n\tfor (int placementIndex = 0; placementIndex < placementCount; placementIndex++)\n\t{\n\t\tDatum placementJson = shardPlacementJsonArray[placementIndex];\n\t\tShardPlacementTestInfo *placementTestInfo = palloc0(\n\t\t\tsizeof(ShardPlacementTestInfo));\n\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(functionCallContext);\n\n\t\tuint64 shardId = JsonFieldValueUInt64Default(\n\t\t\tplacementJson, FIELD_NAME_SHARD_ID, placementIndex + 1);\n\t\tuint64 shardLength = JsonFieldValueUInt64Default(\n\t\t\tplacementJson, FIELD_NAME_SHARD_LENGTH, 1);\n\t\tchar *nodeName = JsonFieldValueString(placementJson, FIELD_NAME_NODE_NAME);\n\t\tif (nodeName == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(FIELD_NAME_NODE_NAME \" needs be set\")));\n\t\t}\n\t\tint nodePort = JsonFieldValueUInt32Default(\n\t\t\tplacementJson, FIELD_NAME_NODE_PORT, 5432);\n\t\tuint64 placementId = JsonFieldValueUInt64Default(\n\t\t\tplacementJson, FIELD_NAME_PLACEMENT_ID, placementIndex + 1);\n\n\t\tuint64 cost = JsonFieldValueUInt64Default(placementJson, \"cost\", 1);\n\t\tbool nextColocationGroup =\n\t\t\tJsonFieldValueBoolDefault(placementJson, \"next_colocation\", false);\n\n\t\tMemoryContextSwitchTo(oldContext);\n\n\t\tplacementTestInfo->placement = palloc0(sizeof(ShardPlacement));\n\t\tplacementTestInfo->placement->shardId = shardId;\n\t\tplacementTestInfo->placement->shardLength = shardLength;\n\t\tplacementTestInfo->placement->nodeName = pstrdup(nodeName);\n\t\tplacementTestInfo->placement->nodePort = nodePort;\n\t\tplacementTestInfo->placement->placementId = placementId;\n\t\tplacementTestInfo->cost = cost;\n\t\tplacementTestInfo->nextColocationGroup = nextColocationGroup;\n\n\t\t/*\n\t\t * We have copied whatever we needed from the UDF calls, so we can free\n\t\t * the memory allocated by them.\n\t\t */\n\t\tMemoryContextReset(functionCallContext);\n\n\n\t\tshardPlacementTestInfoList = lappend(shardPlacementTestInfoList,\n\t\t\t\t\t\t\t\t\t\t\t placementTestInfo);\n\t}\n\n\tpfree(shardPlacementJsonArray);\n\n\treturn shardPlacementTestInfoList;\n}\n\n\n/*\n * JsonArrayToWorkerNodeList converts the given worker node json array to a list\n * of WorkerNode structs.\n */\nstatic List *\nJsonArrayToWorkerTestInfoList(ArrayType *workerNodeJsonArrayObject)\n{\n\tList *workerTestInfoList = NIL;\n\tDatum *workerNodeJsonArray = NULL;\n\tint workerNodeCount = 0;\n\n\tdeconstruct_array(workerNodeJsonArrayObject, JSONOID, -1, false, 'i',\n\t\t\t\t\t  &workerNodeJsonArray, NULL, &workerNodeCount);\n\n\tfor (int workerNodeIndex = 0; workerNodeIndex < workerNodeCount; workerNodeIndex++)\n\t{\n\t\tDatum workerNodeJson = workerNodeJsonArray[workerNodeIndex];\n\t\tchar *workerName = JsonFieldValueString(workerNodeJson, FIELD_NAME_WORKER_NAME);\n\t\tif (workerName == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(FIELD_NAME_WORKER_NAME \" needs be set\")));\n\t\t}\n\t\tuint32 workerPort = JsonFieldValueUInt32Default(workerNodeJson,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tFIELD_NAME_WORKER_PORT, 5432);\n\t\tList *disallowedShardIdList = NIL;\n\n\n\t\tWorkerTestInfo *workerTestInfo = palloc0(sizeof(WorkerTestInfo));\n\t\tWorkerNode *workerNode = palloc0(sizeof(WorkerNode));\n\t\tstrncpy_s(workerNode->workerName, sizeof(workerNode->workerName), workerName,\n\t\t\t\t  WORKER_LENGTH);\n\t\tworkerNode->nodeId = workerNodeIndex;\n\t\tworkerNode->workerPort = workerPort;\n\t\tworkerNode->shouldHaveShards = true;\n\t\tworkerNode->nodeRole = PrimaryNodeRoleId();\n\t\tworkerTestInfo->node = workerNode;\n\n\t\tworkerTestInfo->capacity = JsonFieldValueUInt64Default(workerNodeJson,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"capacity\", 1);\n\n\t\tworkerNode->isActive = JsonFieldValueBoolDefault(workerNodeJson,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"isActive\", true);\n\n\t\tworkerTestInfoList = lappend(workerTestInfoList, workerTestInfo);\n\t\tchar *disallowedShardsString = JsonFieldValueString(\n\t\t\tworkerNodeJson, \"disallowed_shards\");\n\n\t\tif (disallowedShardsString == NULL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tchar *strtokPosition = NULL;\n\t\tchar *shardString = strtok_r(disallowedShardsString, \",\", &strtokPosition);\n\t\twhile (shardString != NULL)\n\t\t{\n\t\t\tuint64 *shardInt = palloc0(sizeof(uint64));\n\t\t\t*shardInt = SafeStringToUint64(shardString);\n\t\t\tdisallowedShardIdList = lappend(disallowedShardIdList, shardInt);\n\t\t\tshardString = strtok_r(NULL, \",\", &strtokPosition);\n\t\t}\n\t\tworkerTestInfo->disallowedShardIds = disallowedShardIdList;\n\t}\n\n\treturn workerTestInfoList;\n}\n\n\n/*\n * JsonFieldValueBoolDefault gets the value of the given key in the given json\n * document and returns it as a boolean. If the field does not exist in the\n * JSON it returns defaultValue.\n */\nstatic bool\nJsonFieldValueBoolDefault(Datum jsonDocument, const char *key, bool defaultValue)\n{\n\tchar *valueString = JsonFieldValueString(jsonDocument, key);\n\tif (valueString == NULL)\n\t{\n\t\treturn defaultValue;\n\t}\n\tDatum valueBoolDatum = DirectFunctionCall1(boolin, CStringGetDatum(valueString));\n\n\treturn DatumGetBool(valueBoolDatum);\n}\n\n\n/*\n * JsonFieldValueUInt32Default gets the value of the given key in the given json\n * document and returns it as an unsigned 32-bit integer. If the field does not\n * exist in the JSON it returns defaultValue.\n */\nstatic uint32\nJsonFieldValueUInt32Default(Datum jsonDocument, const char *key, uint32 defaultValue)\n{\n\tchar *valueString = JsonFieldValueString(jsonDocument, key);\n\tif (valueString == NULL)\n\t{\n\t\treturn defaultValue;\n\t}\n\tDatum valueInt4Datum = DirectFunctionCall1(int4in, CStringGetDatum(valueString));\n\n\tuint32 valueUInt32 = DatumGetInt32(valueInt4Datum);\n\treturn valueUInt32;\n}\n\n\n/*\n * JsonFieldValueUInt64 gets the value of the given key in the given json\n * document and returns it as an unsigned 64-bit integer. If the field does not\n * exist in the JSON it returns defaultValue.\n */\nstatic uint64\nJsonFieldValueUInt64Default(Datum jsonDocument, const char *key, uint64 defaultValue)\n{\n\tchar *valueString = JsonFieldValueString(jsonDocument, key);\n\tif (valueString == NULL)\n\t{\n\t\treturn defaultValue;\n\t}\n\tDatum valueInt8Datum = DirectFunctionCall1(int8in, CStringGetDatum(valueString));\n\n\tuint64 valueUInt64 = DatumGetInt64(valueInt8Datum);\n\treturn valueUInt64;\n}\n\n\n/*\n * DirectFunctionalCall2Null is a version of DirectFunctionCall2 that can\n * return NULL. It still does not support NULL arguments though.\n */\nstatic Datum\nDirectFunctionCall2Null(PGFunction func, bool *isnull, Datum arg1, Datum arg2)\n{\n\tLOCAL_FCINFO(fcinfo, 2);\n\n\tInitFunctionCallInfoData(*fcinfo, NULL, 2, InvalidOid, NULL, NULL);\n\n\tfcinfo->args[0].value = arg1;\n\tfcinfo->args[0].isnull = false;\n\tfcinfo->args[1].value = arg2;\n\tfcinfo->args[1].isnull = false;\n\n\tDatum result = (*func)(fcinfo);\n\tif (fcinfo->isnull)\n\t{\n\t\t*isnull = true;\n\t\treturn 0;\n\t}\n\n\t*isnull = false;\n\treturn result;\n}\n\n\n/*\n * JsonFieldValueString gets the value of the given key in the given json\n * document and returns it as a string. If the field does not exist in the JSON\n * it returns NULL.\n */\nstatic char *\nJsonFieldValueString(Datum jsonDocument, const char *key)\n{\n\tbool isnull = false;\n\tDatum keyDatum = PointerGetDatum(cstring_to_text(key));\n\n\tDatum valueTextDatum = DirectFunctionCall2Null(\n\t\tjson_object_field_text, &isnull, jsonDocument, keyDatum);\n\tif (isnull)\n\t{\n\t\treturn NULL;\n\t}\n\n\tchar *valueString = TextDatumGetCString(valueTextDatum);\n\treturn valueString;\n}\n\n\n/*\n * PlacementUpdateListToJsonArray converts the given list of placement update\n * data to a json array.\n */\nstatic ArrayType *\nPlacementUpdateListToJsonArray(List *placementUpdateList)\n{\n\tListCell *placementUpdateCell = NULL;\n\tint placementUpdateIndex = 0;\n\n\tint placementUpdateCount = list_length(placementUpdateList);\n\tDatum *placementUpdateJsonArray = palloc0(placementUpdateCount * sizeof(Datum));\n\n\tforeach(placementUpdateCell, placementUpdateList)\n\t{\n\t\tPlacementUpdateEvent *placementUpdateEvent = lfirst(placementUpdateCell);\n\t\tWorkerNode *sourceNode = placementUpdateEvent->sourceNode;\n\t\tWorkerNode *targetNode = placementUpdateEvent->targetNode;\n\n\t\tStringInfo escapedSourceName = makeStringInfo();\n\t\tescape_json(escapedSourceName, sourceNode->workerName);\n\n\t\tStringInfo escapedTargetName = makeStringInfo();\n\t\tescape_json(escapedTargetName, targetNode->workerName);\n\n\t\tStringInfo placementUpdateJsonString = makeStringInfo();\n\t\tappendStringInfo(placementUpdateJsonString, PLACEMENT_UPDATE_JSON_FORMAT,\n\t\t\t\t\t\t placementUpdateEvent->updateType, placementUpdateEvent->shardId,\n\t\t\t\t\t\t escapedSourceName->data, sourceNode->workerPort,\n\t\t\t\t\t\t escapedTargetName->data, targetNode->workerPort);\n\n\t\tDatum placementUpdateStringDatum = CStringGetDatum(\n\t\t\tplacementUpdateJsonString->data);\n\t\tDatum placementUpdateJsonDatum = DirectFunctionCall1(json_in,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t placementUpdateStringDatum);\n\n\t\tplacementUpdateJsonArray[placementUpdateIndex] = placementUpdateJsonDatum;\n\t\tplacementUpdateIndex++;\n\t}\n\n\tArrayType *placementUpdateObject = construct_array(placementUpdateJsonArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   placementUpdateCount, JSONOID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   -1, false, 'i');\n\n\treturn placementUpdateObject;\n}\n\n\n/*\n * worker_node_responsive returns true if the given worker node is responsive.\n * Otherwise, it returns false.\n */\nDatum\nworker_node_responsive(PG_FUNCTION_ARGS)\n{\n\ttext *workerNameText = PG_GETARG_TEXT_PP(0);\n\tuint32 workerPort = PG_GETARG_INT32(1);\n\tint connectionFlag = FORCE_NEW_CONNECTION;\n\n\tbool workerNodeResponsive = false;\n\tconst char *workerName = text_to_cstring(workerNameText);\n\n\tMultiConnection *connection = GetNodeConnection(connectionFlag, workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tworkerPort);\n\n\tif (connection != NULL && connection->pgConn != NULL)\n\t{\n\t\tif (PQstatus(connection->pgConn) == CONNECTION_OK)\n\t\t{\n\t\t\tworkerNodeResponsive = true;\n\t\t}\n\n\t\tCloseConnection(connection);\n\t}\n\n\tPG_RETURN_BOOL(workerNodeResponsive);\n}\n"
  },
  {
    "path": "src/backend/distributed/test/shared_connection_counters.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * test/src/sequential_execution.c\n *\n * This file contains functions to test setting citus.multi_shard_modify_mode\n * GUC.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"miscadmin.h\"\n\n#include \"nodes/parsenodes.h\"\n#include \"utils/guc.h\"\n\n#include \"distributed/listutils.h\"\n#include \"distributed/shared_connection_stats.h\"\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(wake_up_connection_pool_waiters);\nPG_FUNCTION_INFO_V1(set_max_shared_pool_size);\n\n\n/*\n * wake_up_waiters_backends is a SQL\n * interface for testing WakeupWaiterBackendsForSharedConnection().\n */\nDatum\nwake_up_connection_pool_waiters(PG_FUNCTION_ARGS)\n{\n\tWakeupWaiterBackendsForSharedConnection();\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * makeIntConst creates a Const Node that stores a given integer\n *\n * copied from backend/parser/gram.c\n */\nstatic Node *\nmakeIntConst(int val, int location)\n{\n\tA_Const *n = makeNode(A_Const);\n\n\tn->val.ival.type = T_Integer;\n\tn->val.ival.ival = val;\n\tn->location = location;\n\n\treturn (Node *) n;\n}\n\n\n/*\n * set_max_shared_pool_size is a SQL\n * interface for setting MaxSharedPoolSize. We use this function in isolation\n * tester where ALTER SYSTEM is not allowed.\n */\nDatum\nset_max_shared_pool_size(PG_FUNCTION_ARGS)\n{\n\tint value = PG_GETARG_INT32(0);\n\n\tAlterSystemStmt *alterSystemStmt = palloc0(sizeof(AlterSystemStmt));\n\n\tA_Const *aConstValue = castNode(A_Const, makeIntConst(value, 0));\n\n\talterSystemStmt->setstmt = makeNode(VariableSetStmt);\n\talterSystemStmt->setstmt->name = \"citus.max_shared_pool_size\";\n\talterSystemStmt->setstmt->is_local = false;\n\talterSystemStmt->setstmt->kind = VAR_SET_VALUE;\n\talterSystemStmt->setstmt->args = list_make1(aConstValue);\n\n\tAlterSystemSetConfigFile(alterSystemStmt);\n\n\tkill(PostmasterPid, SIGHUP);\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/test/xact_stats.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * xact_stats.c\n *\n * This file contains functions to provide helper UDFs for testing transaction\n * statistics.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"distributed/transaction_management.h\"\n#include \"distributed/version_compat.h\"\n\n\nstatic Size MemoryContextTotalSpace(MemoryContext context);\n\nPG_FUNCTION_INFO_V1(top_transaction_context_size);\nPG_FUNCTION_INFO_V1(coordinated_transaction_should_use_2PC);\n\n\n/*\n * top_transaction_context_size returns current size of TopTransactionContext.\n */\nDatum\ntop_transaction_context_size(PG_FUNCTION_ARGS)\n{\n\tSize totalSpace = MemoryContextTotalSpace(TopTransactionContext);\n\tPG_RETURN_INT64(totalSpace);\n}\n\n\n/*\n * MemoryContextTotalSpace returns total space allocated in context and its children.\n */\nstatic Size\nMemoryContextTotalSpace(MemoryContext context)\n{\n\tSize totalSpace = 0;\n\n\tMemoryContextCounters totals = { 0 };\n\tTopTransactionContext->methods->stats(TopTransactionContext, NULL, NULL,\n\t\t\t\t\t\t\t\t\t\t  &totals, true);\n\ttotalSpace += totals.totalspace;\n\n\tfor (MemoryContext child = context->firstchild;\n\t\t child != NULL;\n\t\t child = child->nextchild)\n\t{\n\t\ttotalSpace += MemoryContextTotalSpace(child);\n\t}\n\n\treturn totalSpace;\n}\n\n\n/*\n * coordinated_transaction_should_use_2PC returns true if the transaction is in a\n * coordinated transaction and uses 2PC. If the transaction is nott in a\n * coordinated transaction, the function throws an error.\n */\nDatum\ncoordinated_transaction_should_use_2PC(PG_FUNCTION_ARGS)\n{\n\tif (!InCoordinatedTransaction())\n\t{\n\t\tereport(ERROR, (errmsg(\"The transaction is not a coordinated transaction\")));\n\t}\n\n\tPG_RETURN_BOOL(GetCoordinatedTransactionShouldUse2PC());\n}\n"
  },
  {
    "path": "src/backend/distributed/transaction/backend_data.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * backend_data.c\n *\n *  Infrastructure for managing per backend data that can efficiently\n *  accessed by all sessions.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n#include \"safe_lib.h\"\n#include \"unistd.h\"\n\n#include \"access/htup_details.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_type.h\"\n#include \"datatype/timestamp.h\"\n#include \"nodes/execnodes.h\"\n#include \"postmaster/autovacuum.h\" /* to access autovacuum_max_workers */\n#include \"replication/walsender.h\"\n#include \"storage/ipc.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/lwlock.h\"\n#include \"storage/proc.h\"\n#include \"storage/procarray.h\"\n#include \"storage/s_lock.h\"\n#include \"storage/spin.h\"\n#include \"utils/timestamp.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/shared_connection_stats.h\"\n#include \"distributed/transaction_identifier.h\"\n#include \"distributed/tuplestore.h\"\n#include \"distributed/worker_manager.h\"\n\n\n#define GET_ACTIVE_TRANSACTION_QUERY \"SELECT * FROM get_all_active_transactions();\"\n#define ACTIVE_TRANSACTION_COLUMN_COUNT 7\n#define GLOBAL_PID_NODE_ID_MULTIPLIER 10000000000\n\n/*\n * Each backend's data reside in the shared memory\n * on the BackendManagementShmemData.\n */\ntypedef struct BackendManagementShmemData\n{\n\tint trancheId;\n\tNamedLWLockTranche namedLockTranche;\n\tLWLock lock;\n\n\t/*\n\t * We prefer to use an atomic integer over sequences for two\n\t * reasons (i) orders of magnitude performance difference\n\t * (ii) allowing read-only replicas to be able to generate ids\n\t */\n\tpg_atomic_uint64 nextTransactionNumber;\n\n\t/*\n\t * Total number of external client backends that are authenticated.\n\t *\n\t * Note that the counter does not consider any background workers\n\t * or such, and also exludes internal connections between nodes.\n\t */\n\tpg_atomic_uint32 externalClientBackendCounter;\n\n\tBackendData backends[FLEXIBLE_ARRAY_MEMBER];\n} BackendManagementShmemData;\n\n/*\n * CitusBackendType reflects what type of backend we are in. This\n * can change depending on the application_name.\n */\ntypedef enum CitusBackendType\n{\n\tCITUS_BACKEND_NOT_ASSIGNED,\n\tCITUS_INTERNAL_BACKEND,\n\tCITUS_REBALANCER_BACKEND,\n\tCITUS_RUN_COMMAND_BACKEND,\n\tEXTERNAL_CLIENT_BACKEND\n} CitusBackendType;\n\nstatic const char *CitusBackendPrefixes[] = {\n\tCITUS_APPLICATION_NAME_PREFIX,\n\tCITUS_REBALANCER_APPLICATION_NAME_PREFIX,\n\tCITUS_RUN_COMMAND_APPLICATION_NAME_PREFIX,\n};\n\nstatic const CitusBackendType CitusBackendTypes[] = {\n\tCITUS_INTERNAL_BACKEND,\n\tCITUS_REBALANCER_BACKEND,\n\tCITUS_RUN_COMMAND_BACKEND,\n};\n\n\nstatic void StoreAllActiveTransactions(Tuplestorestate *tupleStore, TupleDesc\n\t\t\t\t\t\t\t\t\t   tupleDescriptor);\nstatic bool UserHasPermissionToViewStatsOf(Oid currentUserId, Oid backendOwnedId);\nstatic uint64 CalculateGlobalPID(int32 nodeId, pid_t pid);\nstatic uint64 GenerateGlobalPID(void);\n\nstatic shmem_startup_hook_type prev_shmem_startup_hook = NULL;\nstatic BackendManagementShmemData *backendManagementShmemData = NULL;\nstatic BackendData *MyBackendData = NULL;\nstatic CitusBackendType CurrentBackendType = CITUS_BACKEND_NOT_ASSIGNED;\n\n\nPG_FUNCTION_INFO_V1(assign_distributed_transaction_id);\nPG_FUNCTION_INFO_V1(get_current_transaction_id);\nPG_FUNCTION_INFO_V1(get_global_active_transactions);\nPG_FUNCTION_INFO_V1(get_all_active_transactions);\nPG_FUNCTION_INFO_V1(citus_calculate_gpid);\nPG_FUNCTION_INFO_V1(citus_backend_gpid);\nPG_FUNCTION_INFO_V1(citus_nodeid_for_gpid);\nPG_FUNCTION_INFO_V1(citus_pid_for_gpid);\n\n\n/*\n * assign_distributed_transaction_id updates the shared memory allocated for this backend\n * and sets initiatorNodeIdentifier, transactionNumber, timestamp fields with the given\n * inputs. Also, the function sets the database id and process id via the information that\n * Postgres provides.\n *\n * This function is only intended for internal use for managing distributed transactions.\n * Users should not use this function for any purpose.\n */\nDatum\nassign_distributed_transaction_id(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\t/* prepare data before acquiring spinlock to protect against errors */\n\tint32 initiatorNodeIdentifier = PG_GETARG_INT32(0);\n\tuint64 transactionNumber = PG_GETARG_INT64(1);\n\tTimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(2);\n\n\t/* MyBackendData should always be avaliable, just out of paranoia */\n\tif (!MyBackendData)\n\t{\n\t\tereport(ERROR, (errmsg(\"backend is not ready for distributed transactions\")));\n\t}\n\n\t/*\n\t * Note that we don't need to lock shared memory (i.e., LockBackendSharedMemory()) here\n\t * since this function is executed after AssignDistributedTransactionId() issued on the\n\t * initiator node, which already takes the required lock to enforce the consistency.\n\t */\n\n\tSpinLockAcquire(&MyBackendData->mutex);\n\n\t/* if an id is already assigned, release the lock and error */\n\tif (MyBackendData->transactionId.transactionNumber != 0)\n\t{\n\t\tSpinLockRelease(&MyBackendData->mutex);\n\n\t\tereport(ERROR, (errmsg(\"the backend has already been assigned a \"\n\t\t\t\t\t\t\t   \"transaction id\")));\n\t}\n\n\tMyBackendData->transactionId.initiatorNodeIdentifier = initiatorNodeIdentifier;\n\tMyBackendData->transactionId.transactionNumber = transactionNumber;\n\tMyBackendData->transactionId.timestamp = timestamp;\n\tMyBackendData->transactionId.transactionOriginator = false;\n\n\tSpinLockRelease(&MyBackendData->mutex);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * get_current_transaction_id returns a tuple with (databaseId, processId,\n * initiatorNodeIdentifier, transactionNumber, timestamp) that exists in the\n * shared memory associated with this backend. Note that if the backend\n * is not in a transaction, the function returns uninitialized data where\n * transactionNumber equals to 0.\n */\nDatum\nget_current_transaction_id(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tTupleDesc tupleDescriptor = NULL;\n\n\tDatum values[5];\n\tbool isNulls[5];\n\n\n\t/* build a tuple descriptor for our result type */\n\tif (get_call_result_type(fcinfo, NULL, &tupleDescriptor) != TYPEFUNC_COMPOSITE)\n\t{\n\t\telog(ERROR, \"return type must be a row type\");\n\t}\n\n\t/* MyBackendData should always be avaliable, just out of paranoia */\n\tif (!MyBackendData)\n\t{\n\t\tereport(ERROR, (errmsg(\"backend is not ready for distributed transactions\")));\n\t}\n\n\tDistributedTransactionId *distributedTransctionId =\n\t\tGetCurrentDistributedTransactionId();\n\n\tmemset(values, 0, sizeof(values));\n\tmemset(isNulls, false, sizeof(isNulls));\n\n\t/* first two fields do not change for this backend, so get directly */\n\tvalues[0] = ObjectIdGetDatum(MyDatabaseId);\n\tvalues[1] = Int32GetDatum(MyProcPid);\n\n\tvalues[2] = Int32GetDatum(distributedTransctionId->initiatorNodeIdentifier);\n\tvalues[3] = UInt64GetDatum(distributedTransctionId->transactionNumber);\n\n\t/* provide a better output */\n\tif (distributedTransctionId->transactionNumber != 0)\n\t{\n\t\tvalues[4] = TimestampTzGetDatum(distributedTransctionId->timestamp);\n\t}\n\telse\n\t{\n\t\tisNulls[4] = true;\n\t}\n\n\tHeapTuple heapTuple = heap_form_tuple(tupleDescriptor, values, isNulls);\n\n\tPG_RETURN_DATUM(HeapTupleGetDatum(heapTuple));\n}\n\n\n/*\n * get_global_active_transactions returns all the available information about all\n * the active backends from each node of the cluster. If you call that function from\n * the coordinator, it will returns back active transaction from the coordinator as\n * well. Yet, if you call it from the worker, result won't include the transactions\n * on the coordinator node, since worker nodes are not aware of the coordinator.\n */\nDatum\nget_global_active_transactions(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tList *workerNodeList = ActivePrimaryNonCoordinatorNodeList(NoLock);\n\tList *connectionList = NIL;\n\tStringInfo queryToSend = makeStringInfo();\n\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tappendStringInfo(queryToSend, GET_ACTIVE_TRANSACTION_QUERY);\n\n\t/* add active transactions for local node */\n\tStoreAllActiveTransactions(tupleStore, tupleDescriptor);\n\n\tint32 localGroupId = GetLocalGroupId();\n\n\t/* open connections in parallel */\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tconst char *nodeName = workerNode->workerName;\n\t\tint nodePort = workerNode->workerPort;\n\t\tint connectionFlags = 0;\n\n\t\tif (workerNode->groupId == localGroupId)\n\t\t{\n\t\t\t/* we already get these transactions via GetAllActiveTransactions() */\n\t\t\tcontinue;\n\t\t}\n\n\t\tMultiConnection *connection = StartNodeConnection(connectionFlags, nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodePort);\n\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\tFinishConnectionListEstablishment(connectionList);\n\n\t/* send commands in parallel */\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tint querySent = SendRemoteCommand(connection, queryToSend->data);\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tReportConnectionError(connection, WARNING);\n\t\t}\n\t}\n\n\t/* receive query results */\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tbool raiseInterrupts = true;\n\t\tDatum values[ACTIVE_TRANSACTION_COLUMN_COUNT];\n\t\tbool isNulls[ACTIVE_TRANSACTION_COLUMN_COUNT];\n\n\t\tif (PQstatus(connection->pgConn) != CONNECTION_OK)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\t\tif (!IsResponseOK(result))\n\t\t{\n\t\t\tReportResultError(connection, result, WARNING);\n\t\t\tcontinue;\n\t\t}\n\n\t\tint64 rowCount = PQntuples(result);\n\t\tint64 colCount = PQnfields(result);\n\n\t\t/* Although it is not expected */\n\t\tif (colCount != ACTIVE_TRANSACTION_COLUMN_COUNT)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"unexpected number of columns from \"\n\t\t\t\t\t\t\t\t\t \"get_all_active_transactions\")));\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (int64 rowIndex = 0; rowIndex < rowCount; rowIndex++)\n\t\t{\n\t\t\tmemset(values, 0, sizeof(values));\n\t\t\tmemset(isNulls, false, sizeof(isNulls));\n\n\t\t\tvalues[0] = ParseIntField(result, rowIndex, 0);\n\t\t\tvalues[1] = ParseIntField(result, rowIndex, 1);\n\t\t\tvalues[2] = ParseIntField(result, rowIndex, 2);\n\t\t\tvalues[3] = ParseBoolField(result, rowIndex, 3);\n\t\t\tvalues[4] = ParseIntField(result, rowIndex, 4);\n\t\t\tvalues[5] = ParseTimestampTzField(result, rowIndex, 5);\n\t\t\tvalues[6] = ParseIntField(result, rowIndex, 6);\n\n\t\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);\n\t\t}\n\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * get_all_active_transactions returns all the avaliable information about all\n * the active backends.\n */\nDatum\nget_all_active_transactions(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tTupleDesc tupleDescriptor = NULL;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor);\n\n\tStoreAllActiveTransactions(tupleStore, tupleDescriptor);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * StoreAllActiveTransactions gets active transaction from the local node and inserts\n * them into the given tuplestore.\n */\nstatic void\nStoreAllActiveTransactions(Tuplestorestate *tupleStore, TupleDesc tupleDescriptor)\n{\n\tDatum values[ACTIVE_TRANSACTION_COLUMN_COUNT];\n\tbool isNulls[ACTIVE_TRANSACTION_COLUMN_COUNT];\n\tbool showAllBackends = superuser();\n\tconst Oid userId = GetUserId();\n\n\tif (!showAllBackends && is_member_of_role(userId, ROLE_PG_MONITOR))\n\t{\n\t\tshowAllBackends = true;\n\t}\n\n\t/* we're reading all distributed transactions, prevent new backends */\n\tLockBackendSharedMemory(LW_SHARED);\n\n\tfor (int backendIndex = 0; backendIndex < TotalProcCount(); ++backendIndex)\n\t{\n\t\tbool showCurrentBackendDetails = showAllBackends;\n\t\tBackendData *currentBackend =\n\t\t\t&backendManagementShmemData->backends[backendIndex];\n\t\tPGPROC *currentProc = GetPGProcByNumber(backendIndex);\n\n\t\t/* to work on data after releasing g spinlock to protect against errors */\n\t\tuint64 transactionNumber = 0;\n\n\t\tSpinLockAcquire(&currentBackend->mutex);\n\n\t\tif (currentProc->pid == 0 || !currentBackend->activeBackend)\n\t\t{\n\t\t\t/* unused PGPROC slot or the backend already exited */\n\t\t\tSpinLockRelease(&currentBackend->mutex);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Unless the user has a role that allows seeing all transactions (superuser,\n\t\t * pg_monitor), we only follow pg_stat_statements owner checks.\n\t\t */\n\t\tif (!showCurrentBackendDetails &&\n\t\t\tUserHasPermissionToViewStatsOf(userId, currentProc->roleId))\n\t\t{\n\t\t\tshowCurrentBackendDetails = true;\n\t\t}\n\n\t\tOid databaseId = currentBackend->databaseId;\n\t\tint backendPid = GetPGProcByNumber(backendIndex)->pid;\n\n\t\t/*\n\t\t * We prefer to use worker_query instead of distributedCommandOriginator in\n\t\t * the user facing functions since it's more intuitive. Thus,\n\t\t * we negate the result before returning.\n\t\t */\n\t\tbool distributedCommandOriginator =\n\t\t\tcurrentBackend->distributedCommandOriginator;\n\n\t\ttransactionNumber = currentBackend->transactionId.transactionNumber;\n\t\tTimestampTz transactionIdTimestamp = currentBackend->transactionId.timestamp;\n\n\t\tSpinLockRelease(&currentBackend->mutex);\n\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(isNulls, false, sizeof(isNulls));\n\n\t\t/*\n\t\t * We imitate pg_stat_activity such that if a user doesn't have enough\n\t\t * privileges, we only show the minimal information including the pid,\n\t\t * global pid and distributedCommandOriginator.\n\t\t *\n\t\t * pid is already can be found in pg_stat_activity for any process, and\n\t\t * the rest doesn't reveal anything critial for under priviledge users\n\t\t * but still could be useful for monitoring purposes of Citus.\n\t\t */\n\t\tif (showCurrentBackendDetails)\n\t\t{\n\t\t\tbool missingOk = true;\n\t\t\tint initiatorNodeId =\n\t\t\t\tExtractNodeIdFromGlobalPID(currentBackend->globalPID, missingOk);\n\n\t\t\tvalues[0] = ObjectIdGetDatum(databaseId);\n\t\t\tvalues[1] = Int32GetDatum(backendPid);\n\t\t\tvalues[2] = Int32GetDatum(initiatorNodeId);\n\t\t\tvalues[3] = !distributedCommandOriginator;\n\t\t\tvalues[4] = UInt64GetDatum(transactionNumber);\n\t\t\tvalues[5] = TimestampTzGetDatum(transactionIdTimestamp);\n\t\t\tvalues[6] = UInt64GetDatum(currentBackend->globalPID);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tisNulls[0] = true;\n\t\t\tvalues[1] = Int32GetDatum(backendPid);\n\t\t\tisNulls[2] = true;\n\t\t\tvalues[3] = !distributedCommandOriginator;\n\t\t\tisNulls[4] = true;\n\t\t\tisNulls[5] = true;\n\t\t\tvalues[6] = UInt64GetDatum(currentBackend->globalPID);\n\t\t}\n\n\t\ttuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls);\n\n\t\t/*\n\t\t * We don't want to initialize memory while spinlock is held so we\n\t\t * prefer to do it here. This initialization is done for the rows\n\t\t * starting from the second one.\n\t\t */\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(isNulls, false, sizeof(isNulls));\n\t}\n\n\tUnlockBackendSharedMemory();\n}\n\n\n/*\n * UserHasPermissionToViewStatsOf returns true if currentUserId can\n * see backends of backendOwnedId.\n *\n * We follow the same approach with pg_stat_activity.\n */\nstatic\nbool\nUserHasPermissionToViewStatsOf(Oid currentUserId, Oid backendOwnedId)\n{\n\tif (has_privs_of_role(currentUserId, backendOwnedId))\n\t{\n\t\treturn true;\n\t}\n\n\tif (is_member_of_role(currentUserId,\n\t\t\t\t\t\t  ROLE_PG_READ_ALL_STATS))\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * InitializeBackendManagement requests the necessary shared memory\n * from Postgres and sets up the shared memory startup hook.\n */\nvoid\nInitializeBackendManagement(void)\n{\n\tprev_shmem_startup_hook = shmem_startup_hook;\n\tshmem_startup_hook = BackendManagementShmemInit;\n}\n\n\n/*\n * BackendManagementShmemInit is the callback that is to be called on shared\n * memory startup hook. The function sets up the necessary shared memory\n * segment for the backend manager.\n */\nvoid\nBackendManagementShmemInit(void)\n{\n\tbool alreadyInitialized = false;\n\n\t/* we may update the shmem, acquire lock exclusively */\n\tLWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);\n\n\tbackendManagementShmemData =\n\t\t(BackendManagementShmemData *) ShmemInitStruct(\n\t\t\t\"Backend Management Shmem\",\n\t\t\tBackendManagementShmemSize(),\n\t\t\t&alreadyInitialized);\n\n\tif (!alreadyInitialized)\n\t{\n\t\tchar *trancheName = \"Backend Management Tranche\";\n\n\t\tNamedLWLockTranche *namedLockTranche =\n\t\t\t&backendManagementShmemData->namedLockTranche;\n\n\t\t/* start by zeroing out all the memory */\n\t\tmemset(backendManagementShmemData, 0,\n\t\t\t   BackendManagementShmemSize());\n\n\t\tnamedLockTranche->trancheId = LWLockNewTrancheId();\n\n\t\tLWLockRegisterTranche(namedLockTranche->trancheId, trancheName);\n\t\tLWLockInitialize(&backendManagementShmemData->lock,\n\t\t\t\t\t\t namedLockTranche->trancheId);\n\n\t\t/* start the distributed transaction ids from 1 */\n\t\tpg_atomic_init_u64(&backendManagementShmemData->nextTransactionNumber, 1);\n\n\t\t/* there are no active backends yet, so start with zero */\n\t\tpg_atomic_init_u32(&backendManagementShmemData->externalClientBackendCounter, 0);\n\n\t\t/*\n\t\t * We need to init per backend's spinlock before any backend\n\t\t * starts its execution. Note that we initialize TotalProcs (e.g., not\n\t\t * MaxBackends) since some of the blocking processes could be prepared\n\t\t * transactions, which aren't covered by MaxBackends.\n\t\t *\n\t\t * We also initiate initiatorNodeIdentifier to -1, which can never be\n\t\t * used as a node id.\n\t\t */\n\t\tint totalProcs = TotalProcCount();\n\t\tfor (int backendIndex = 0; backendIndex < totalProcs; ++backendIndex)\n\t\t{\n\t\t\tBackendData *backendData =\n\t\t\t\t&backendManagementShmemData->backends[backendIndex];\n\t\t\tSpinLockInit(&backendData->mutex);\n\t\t}\n\t}\n\n\tLWLockRelease(AddinShmemInitLock);\n\n\tif (prev_shmem_startup_hook != NULL)\n\t{\n\t\tprev_shmem_startup_hook();\n\t}\n}\n\n\n/*\n * BackendManagementShmemSize returns the size that should be allocated\n * on the shared memory for backend management.\n */\nsize_t\nBackendManagementShmemSize(void)\n{\n\tSize size = 0;\n\tint totalProcs = TotalProcCount();\n\n\tsize = add_size(size, sizeof(BackendManagementShmemData));\n\tsize = add_size(size, mul_size(sizeof(BackendData), totalProcs));\n\n\treturn size;\n}\n\n\n/*\n * TotalProcCount returns the total processes that could run via the current\n * postgres server. See the details in the function comments.\n *\n * There is one thing we should warn the readers. Citus enforces to be loaded\n * as the first extension in shared_preload_libraries. However, if any other\n * extension overrides MaxConnections, autovacuum_max_workers or\n * max_worker_processes, our reasoning in this function may not work as expected.\n * Given that it is not a usual pattern for extension, we consider Citus' behaviour\n * good enough for now.\n */\nint\nTotalProcCount(void)\n{\n\tint maxBackends = 0;\n\tint totalProcs = 0;\n\n#ifdef WIN32\n\n\t/* autovacuum_max_workers is not PGDLLIMPORT, so use a high estimate for windows */\n\tint estimatedMaxAutovacuumWorkers = 30;\n\tmaxBackends =\n\t\tMaxConnections + estimatedMaxAutovacuumWorkers + 1 + max_worker_processes;\n#else\n\n\t/*\n\t * We're simply imitating Postgrsql's InitializeMaxBackends(). Given that all\n\t * the items used here PGC_POSTMASTER, should be safe to access them\n\t * anytime during the execution even before InitializeMaxBackends() is called.\n\t */\n\tmaxBackends = MaxConnections + autovacuum_max_workers + 1 + max_worker_processes;\n#endif\n\n\t/*\n\t * We prefer to maintain space for auxiliary procs or preperad transactions in\n\t * the backend space because they could be blocking processes and our current\n\t * implementation of distributed deadlock detection could process them\n\t * as a regular backend. In the future, we could consider changing deadlock\n\t * detection algorithm to ignore auxiliary procs or prepared transactions and\n\t * save some space.\n\t */\n\ttotalProcs = maxBackends + NUM_AUXILIARY_PROCS + max_prepared_xacts;\n\n\ttotalProcs += max_wal_senders;\n\n\treturn totalProcs;\n}\n\n\n/*\n * InitializeBackendData initialises MyBackendData to the shared memory segment\n * belonging to the current backend.\n *\n * The function is called through CitusHasBeenLoaded when we first detect that\n * the Citus extension is present, and after any subsequent invalidation of\n * pg_dist_partition (see InvalidateMetadataSystemCache()).\n *\n * We only need to initialise MyBackendData once. The main goal here is to make\n * sure that we don't use the backend data from a previous backend with the same\n * pgprocno. Resetting the backend data after a distributed transaction happens\n * on COMMIT/ABORT through transaction callbacks.\n *\n * We do also initialize the distributedCommandOriginator and globalPID values\n * based on these values. This is to make sure that once the backend date is\n * initialized this backend can be correctly shown in citus_lock_waits.\n */\nvoid\nInitializeBackendData(const char *applicationName)\n{\n\tif (MyBackendData != NULL)\n\t{\n\t\t/*\n\t\t * We already initialized MyBackendData before. We definitely should\n\t\t * not initialise it again, because we might be in the middle of a\n\t\t * distributed transaction.\n\t\t */\n\t\treturn;\n\t}\n\n\tuint64 gpid = ExtractGlobalPID(applicationName);\n\n\tMyBackendData = &backendManagementShmemData->backends[getProcNo_compat(MyProc)];\n\n\tAssert(MyBackendData);\n\n\tLockBackendSharedMemory(LW_EXCLUSIVE);\n\n\t/* zero out the backend its transaction id */\n\tUnSetDistributedTransactionId();\n\tUnSetGlobalPID();\n\n\tSpinLockAcquire(&MyBackendData->mutex);\n\tMyBackendData->distributedCommandOriginator = IsExternalClientBackend();\n\tMyBackendData->globalPID = gpid;\n\tSpinLockRelease(&MyBackendData->mutex);\n\n\t/*\n\t * Signal that this backend is active and should show up\n\t * on activity monitors.\n\t */\n\tSetActiveMyBackend(true);\n\n\tUnlockBackendSharedMemory();\n}\n\n\n/*\n * UnSetDistributedTransactionId simply acquires the mutex and resets the backend's\n * distributed transaction data in shared memory to the initial values.\n */\nvoid\nUnSetDistributedTransactionId(void)\n{\n\t/* backend does not exist if the extension is not created */\n\tif (MyBackendData)\n\t{\n\t\tSpinLockAcquire(&MyBackendData->mutex);\n\n\t\tMyBackendData->cancelledDueToDeadlock = false;\n\t\tMyBackendData->transactionId.initiatorNodeIdentifier = 0;\n\t\tMyBackendData->transactionId.transactionOriginator = false;\n\t\tMyBackendData->transactionId.transactionNumber = 0;\n\t\tMyBackendData->transactionId.timestamp = 0;\n\n\t\tSpinLockRelease(&MyBackendData->mutex);\n\t}\n}\n\n\n/*\n * UnSetGlobalPID resets the global pid for the current backend.\n */\nvoid\nUnSetGlobalPID(void)\n{\n\t/* backend does not exist if the extension is not created */\n\tif (MyBackendData)\n\t{\n\t\tSpinLockAcquire(&MyBackendData->mutex);\n\n\t\tMyBackendData->globalPID = 0;\n\t\tMyBackendData->databaseId = 0;\n\t\tMyBackendData->distributedCommandOriginator = false;\n\n\t\tSpinLockRelease(&MyBackendData->mutex);\n\t}\n}\n\n\n/*\n * SetActiveMyBackend is a wrapper around MyBackendData->activeBackend.\n */\nvoid\nSetActiveMyBackend(bool value)\n{\n\t/* backend does not exist if the extension is not created */\n\tif (MyBackendData)\n\t{\n\t\tSpinLockAcquire(&MyBackendData->mutex);\n\n\t\tMyBackendData->activeBackend = value;\n\n\t\tSpinLockRelease(&MyBackendData->mutex);\n\t}\n}\n\n\n/*\n * LockBackendSharedMemory is a simple wrapper around LWLockAcquire on the\n * shared memory lock.\n *\n * We use the backend shared memory lock for preventing new backends to be part\n * of a new distributed transaction or an existing backend to leave a distributed\n * transaction while we're reading the all backends' data.\n *\n * The primary goal is to provide consistent view of the current distributed\n * transactions while doing the deadlock detection.\n */\nvoid\nLockBackendSharedMemory(LWLockMode lockMode)\n{\n\tLWLockAcquire(&backendManagementShmemData->lock, lockMode);\n}\n\n\n/*\n * UnlockBackendSharedMemory is a simple wrapper around LWLockRelease on the\n * shared memory lock.\n */\nvoid\nUnlockBackendSharedMemory(void)\n{\n\tLWLockRelease(&backendManagementShmemData->lock);\n}\n\n\n/*\n * GetCurrentDistributedTransactionId reads the backend's distributed transaction id and\n * returns a copy of it.\n *\n * When called from a parallel worker, it uses the parent's transaction ID per the logic\n * in GetBackendDataForProc.\n */\nDistributedTransactionId *\nGetCurrentDistributedTransactionId(void)\n{\n\tDistributedTransactionId *currentDistributedTransactionId =\n\t\t(DistributedTransactionId *) palloc(sizeof(DistributedTransactionId));\n\tBackendData backendData;\n\n\tGetBackendDataForProc(MyProc, &backendData);\n\n\tcurrentDistributedTransactionId->initiatorNodeIdentifier =\n\t\tbackendData.transactionId.initiatorNodeIdentifier;\n\tcurrentDistributedTransactionId->transactionOriginator =\n\t\tbackendData.transactionId.transactionOriginator;\n\tcurrentDistributedTransactionId->transactionNumber =\n\t\tbackendData.transactionId.transactionNumber;\n\tcurrentDistributedTransactionId->timestamp =\n\t\tbackendData.transactionId.timestamp;\n\n\treturn currentDistributedTransactionId;\n}\n\n\n/*\n * AssignDistributedTransactionId generates a new distributed transaction id and\n * sets it for the current backend. It also sets the databaseId and\n * processId fields.\n *\n * This function should only be called on UseCoordinatedTransaction(). Any other\n * callers is very likely to break the distributed transaction management.\n */\nvoid\nAssignDistributedTransactionId(void)\n{\n\t/*\n\t * MyBackendData should always be available. However, we observed some\n\t * crashes where certain hooks were not executed.\n\t * Bug 3697586: Server crashes when assigning distributed transaction\n\t */\n\tif (!MyBackendData)\n\t{\n\t\tereport(ERROR, (errmsg(\"backend is not ready for distributed transactions\")));\n\t}\n\n\tpg_atomic_uint64 *transactionNumberSequence =\n\t\t&backendManagementShmemData->nextTransactionNumber;\n\n\tuint64 nextTransactionNumber = pg_atomic_fetch_add_u64(transactionNumberSequence, 1);\n\tint32 localGroupId = GetLocalGroupId();\n\tTimestampTz currentTimestamp = GetCurrentTimestamp();\n\n\tSpinLockAcquire(&MyBackendData->mutex);\n\n\tMyBackendData->transactionId.initiatorNodeIdentifier = localGroupId;\n\tMyBackendData->transactionId.transactionOriginator = true;\n\tMyBackendData->transactionId.transactionNumber = nextTransactionNumber;\n\tMyBackendData->transactionId.timestamp = currentTimestamp;\n\n\tSpinLockRelease(&MyBackendData->mutex);\n}\n\n\n/*\n * AssignGlobalPID assigns a global process id for the current backend based on\n * the given applicationName. If this is a Citus initiated backend, which means\n * it is distributed part of a distributed query, then this function assigns\n * the global pid extracted from the application name. If not, this function\n * assigns a new generated global pid.\n *\n * There's one special case where we don't want to assign a new pid and keep\n * the old pid on purpose: The current backend is an external backend and the\n * node id of the current node changed since the previous call to\n * AssingGlobalPID. Updating the gpid to match the nodeid might seem like a\n * desirable property, but that's not the case. Mainly, because existing cached\n * connections will still report as the old gpid on the worker. So updating the\n * gpid with the new nodeid would mess up distributed deadlock and originator\n * detection of queries done using those old connections. So if this is an\n * external backend for which a gpid was already generated, then we don't\n * change the gpid.\n *\n * NOTE: This function can be called arbitrary amount of times for the same\n * backend, due to being called by StartupCitusBackend.\n */\nvoid\nAssignGlobalPID(const char *applicationName)\n{\n\tuint64 globalPID = INVALID_CITUS_INTERNAL_BACKEND_GPID;\n\tbool distributedCommandOriginator = IsExternalClientBackend();\n\n\tif (distributedCommandOriginator)\n\t{\n\t\tglobalPID = GenerateGlobalPID();\n\t}\n\telse\n\t{\n\t\tglobalPID = ExtractGlobalPID(applicationName);\n\t}\n\n\tSpinLockAcquire(&MyBackendData->mutex);\n\n\t/*\n\t * Skip updating globalpid when we were a command originator and still are\n\t * and we already have a valid global pid assigned.\n\t * See function comment for detailed explanation.\n\t */\n\tif (!MyBackendData->distributedCommandOriginator ||\n\t\t!distributedCommandOriginator ||\n\t\tMyBackendData->globalPID == INVALID_CITUS_INTERNAL_BACKEND_GPID)\n\t{\n\t\tMyBackendData->globalPID = globalPID;\n\t\tMyBackendData->distributedCommandOriginator = distributedCommandOriginator;\n\t}\n\tSpinLockRelease(&MyBackendData->mutex);\n}\n\n\n/*\n * SetBackendDataDatabaseId sets the databaseId in the backend data.\n *\n * NOTE: this needs to be run after the auth hook, because in the auth hook the\n * database is not known yet.\n */\nvoid\nSetBackendDataDatabaseId(void)\n{\n\tAssert(MyDatabaseId != InvalidOid);\n\tSpinLockAcquire(&MyBackendData->mutex);\n\tMyBackendData->databaseId = MyDatabaseId;\n\tSpinLockRelease(&MyBackendData->mutex);\n}\n\n\n/*\n * SetBackendDataGlobalPID is used to set the gpid field on MyBackendData.\n *\n * IMPORTANT: This should not be used for normal operations. It's a very hacky\n * way of setting the gpid, which is only used in our MX isolation tests. The\n * main problem is that it does not set distributedCommandOriginator to the\n * correct value.\n */\nvoid\nSetBackendDataGlobalPID(uint64 gpid)\n{\n\tif (!MyBackendData)\n\t{\n\t\treturn;\n\t}\n\tSpinLockAcquire(&MyBackendData->mutex);\n\tMyBackendData->globalPID = gpid;\n\tSpinLockRelease(&MyBackendData->mutex);\n}\n\n\n/*\n * SetBackendDataDistributedCommandOriginator sets the distributedCommandOriginator\n * field on MyBackendData.\n */\nvoid\nSetBackendDataDistributedCommandOriginator(bool distributedCommandOriginator)\n{\n\tif (!MyBackendData)\n\t{\n\t\treturn;\n\t}\n\tSpinLockAcquire(&MyBackendData->mutex);\n\tMyBackendData->distributedCommandOriginator = distributedCommandOriginator;\n\tSpinLockRelease(&MyBackendData->mutex);\n}\n\n\n/*\n * GetGlobalPID returns the global process id of the current backend.\n */\nuint64\nGetGlobalPID(void)\n{\n\tuint64 globalPID = INVALID_CITUS_INTERNAL_BACKEND_GPID;\n\n\tif (MyBackendData)\n\t{\n\t\tSpinLockAcquire(&MyBackendData->mutex);\n\t\tglobalPID = MyBackendData->globalPID;\n\t\tSpinLockRelease(&MyBackendData->mutex);\n\t}\n\n\treturn globalPID;\n}\n\n\n/*\n * citus_calculate_gpid calculates the gpid for any given process on any\n * given node.\n */\nDatum\ncitus_calculate_gpid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint32 nodeId = PG_GETARG_INT32(0);\n\tint32 pid = PG_GETARG_INT32(1);\n\n\tPG_RETURN_UINT64(CalculateGlobalPID(nodeId, pid));\n}\n\n\n/*\n * CalculateGlobalPID gets a nodeId and pid, and returns the global pid\n * that can be assigned for a process with the given input.\n */\nstatic uint64\nCalculateGlobalPID(int32 nodeId, pid_t pid)\n{\n\t/*\n\t * We try to create a human readable global pid that consists of node id and process id.\n\t * By multiplying node id with 10^10 and adding pid we generate a number where the smallest\n\t * 10 digit represent the pid and the remaining digits are the node id.\n\t *\n\t * Both node id and pid are 32 bit. We use 10^10 to fit all possible pids. Some very large\n\t * node ids might cause overflow. But even for the applications that scale around 50 nodes every\n\t * day it'd take about 100K years. So we are not worried.\n\t */\n\treturn (((uint64) nodeId) * GLOBAL_PID_NODE_ID_MULTIPLIER) + pid;\n}\n\n\n/*\n * GenerateGlobalPID generates the global process id for the current backend.\n * See CalculateGlobalPID for the details.\n */\nstatic uint64\nGenerateGlobalPID(void)\n{\n\treturn CalculateGlobalPID(GetLocalNodeId(), getpid());\n}\n\n\n/*\n * citus_backend_gpid similar to pg_backend_pid, but returns Citus\n * assigned gpid.\n */\nDatum\ncitus_backend_gpid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tPG_RETURN_UINT64(GetGlobalPID());\n}\n\n\n/*\n * citus_nodeid_for_gpid returns node id for the global process with given global pid\n */\nDatum\ncitus_nodeid_for_gpid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tuint64 globalPID = PG_GETARG_INT64(0);\n\n\tbool missingOk = false;\n\tPG_RETURN_INT32(ExtractNodeIdFromGlobalPID(globalPID, missingOk));\n}\n\n\n/*\n * citus_pid_for_gpid returns process id for the global process with given global pid\n */\nDatum\ncitus_pid_for_gpid(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tuint64 globalPID = PG_GETARG_INT64(0);\n\n\tPG_RETURN_INT32(ExtractProcessIdFromGlobalPID(globalPID));\n}\n\n\n/*\n * ExtractGlobalPID extracts the global process id from the application name and returns it\n * if the application name is not compatible with Citus' application names returns 0.\n */\nuint64\nExtractGlobalPID(const char *applicationName)\n{\n\t/* does application name exist */\n\tif (!applicationName)\n\t{\n\t\treturn INVALID_CITUS_INTERNAL_BACKEND_GPID;\n\t}\n\n\t/* we create our own copy of application name incase the original changes */\n\tchar *applicationNameCopy = pstrdup(applicationName);\n\n\tfor (int i = 0; i < lengthof(CitusBackendPrefixes); i++)\n\t{\n\t\tuint64 prefixLength = strlen(CitusBackendPrefixes[i]);\n\n\t\t/* does application name start with this prefix prefix */\n\t\tif (strncmp(applicationNameCopy, CitusBackendPrefixes[i], prefixLength) != 0)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tchar *globalPIDString = &applicationNameCopy[prefixLength];\n\t\tuint64 globalPID = strtoul(globalPIDString, NULL, 10);\n\t\tif (globalPID == 0)\n\t\t{\n\t\t\t/*\n\t\t\t * INVALID_CITUS_INTERNAL_BACKEND_GPID is 0, but just to be explicit\n\t\t\t * about how we handle strtoul errors.\n\t\t\t */\n\t\t\treturn INVALID_CITUS_INTERNAL_BACKEND_GPID;\n\t\t}\n\n\t\treturn globalPID;\n\t}\n\treturn INVALID_CITUS_INTERNAL_BACKEND_GPID;\n}\n\n\n/*\n * ExtractNodeIdFromGlobalPID extracts the node id from the global pid.\n * Global pid is constructed by multiplying node id with GLOBAL_PID_NODE_ID_MULTIPLIER\n * and adding process id. So integer division of global pid by GLOBAL_PID_NODE_ID_MULTIPLIER\n * gives us the node id.\n */\nint\nExtractNodeIdFromGlobalPID(uint64 globalPID, bool missingOk)\n{\n\tint nodeId = (int) (globalPID / GLOBAL_PID_NODE_ID_MULTIPLIER);\n\n\tif (!missingOk &&\n\t\tnodeId == GLOBAL_PID_NODE_ID_FOR_NODES_NOT_IN_METADATA)\n\t{\n\t\tereport(ERROR, (errmsg(\"originator node of the query with the global pid \"\n\t\t\t\t\t\t\t   \"%lu is not in Citus' metadata\", globalPID),\n\t\t\t\t\t\terrhint(\"connect to the node directly run pg_cancel_backend(pid) \"\n\t\t\t\t\t\t\t\t\"or pg_terminate_backend(pid)\")));\n\t}\n\n\treturn nodeId;\n}\n\n\n/*\n * ExtractProcessIdFromGlobalPID extracts the process id from the global pid.\n * Global pid is constructed by multiplying node id with GLOBAL_PID_NODE_ID_MULTIPLIER\n * and adding process id. So global pid mod GLOBAL_PID_NODE_ID_MULTIPLIER gives us the\n * process id.\n */\nint\nExtractProcessIdFromGlobalPID(uint64 globalPID)\n{\n\treturn (int) (globalPID % GLOBAL_PID_NODE_ID_MULTIPLIER);\n}\n\n\n/*\n * CurrentDistributedTransactionNumber returns the transaction number of the\n * current distributed transaction. The caller must make sure a distributed\n * transaction is in progress.\n */\nuint64\nCurrentDistributedTransactionNumber(void)\n{\n\tAssert(MyBackendData != NULL);\n\n\treturn MyBackendData->transactionId.transactionNumber;\n}\n\n\n/*\n * GetBackendDataForProc writes the backend data for the given process to\n * result. If the process is part of a lock group (parallel query) it\n * returns the leader data instead.\n */\nvoid\nGetBackendDataForProc(PGPROC *proc, BackendData *result)\n{\n\tint pgprocno = getProcNo_compat(proc);\n\n\tif (proc->lockGroupLeader != NULL)\n\t{\n\t\tpgprocno = getProcNo_compat(proc->lockGroupLeader);\n\t}\n\n\tBackendData *backendData = &backendManagementShmemData->backends[pgprocno];\n\n\tSpinLockAcquire(&backendData->mutex);\n\n\t*result = *backendData;\n\n\tSpinLockRelease(&backendData->mutex);\n}\n\n\n/*\n * CancelTransactionDueToDeadlock cancels the input proc and also marks the backend\n * data with this information.\n */\nvoid\nCancelTransactionDueToDeadlock(PGPROC *proc)\n{\n\tBackendData *backendData = &backendManagementShmemData->backends[getProcNo_compat(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t proc)];\n\n\t/* backend might not have used citus yet and thus not initialized backend data */\n\tif (!backendData)\n\t{\n\t\treturn;\n\t}\n\n\tSpinLockAcquire(&backendData->mutex);\n\n\t/* send a SIGINT only if the process is still in a distributed transaction */\n\tif (backendData->transactionId.transactionNumber != 0)\n\t{\n\t\tbackendData->cancelledDueToDeadlock = true;\n\t\tSpinLockRelease(&backendData->mutex);\n\n\t\tif (kill(proc->pid, SIGINT) != 0)\n\t\t{\n\t\t\tereport(WARNING,\n\t\t\t\t\t(errmsg(\"attempted to cancel this backend (pid: %d) to resolve a \"\n\t\t\t\t\t\t\t\"distributed deadlock but the backend could not \"\n\t\t\t\t\t\t\t\"be cancelled\", proc->pid)));\n\t\t}\n\t}\n\telse\n\t{\n\t\tSpinLockRelease(&backendData->mutex);\n\t}\n}\n\n\n/*\n * MyBackendGotCancelledDueToDeadlock returns whether the current distributed\n * transaction was cancelled due to a deadlock. If the backend is not in a\n * distributed transaction, the function returns false.\n * We keep some session level state to keep track of if we were cancelled\n * because of a distributed deadlock. When clearState is true, this function\n * also resets that state. So after calling this function with clearState true,\n * a second would always return false.\n */\nbool\nMyBackendGotCancelledDueToDeadlock(bool clearState)\n{\n\tbool cancelledDueToDeadlock = false;\n\n\t/* backend might not have used citus yet and thus not initialized backend data */\n\tif (!MyBackendData)\n\t{\n\t\treturn false;\n\t}\n\n\tSpinLockAcquire(&MyBackendData->mutex);\n\n\tif (IsInDistributedTransaction(MyBackendData))\n\t{\n\t\tcancelledDueToDeadlock = MyBackendData->cancelledDueToDeadlock;\n\t}\n\tif (clearState)\n\t{\n\t\tMyBackendData->cancelledDueToDeadlock = false;\n\t}\n\n\tSpinLockRelease(&MyBackendData->mutex);\n\n\treturn cancelledDueToDeadlock;\n}\n\n\n/*\n * ActiveDistributedTransactionNumbers returns a list of pointers to\n * transaction numbers of distributed transactions that are in progress\n * and were started by the node on which it is called.\n */\nList *\nActiveDistributedTransactionNumbers(void)\n{\n\tList *activeTransactionNumberList = NIL;\n\n\t/* build list of starting procs */\n\tfor (int curBackend = 0; curBackend < MaxBackends; curBackend++)\n\t{\n\t\tPGPROC *currentProc = GetPGProcByNumber(curBackend);\n\t\tBackendData currentBackendData;\n\n\t\tif (currentProc->pid == 0)\n\t\t{\n\t\t\t/* unused PGPROC slot */\n\t\t\tcontinue;\n\t\t}\n\n\t\tGetBackendDataForProc(currentProc, &currentBackendData);\n\t\tif (!currentBackendData.activeBackend)\n\t\t{\n\t\t\t/*\n\t\t\t * Skip if the PGPROC slot is unused. We should normally use\n\t\t\t * IsBackendPid() to be able to skip reliably all the exited\n\t\t\t * processes. However, that is a costly operation. Instead, we\n\t\t\t * keep track of activeBackend in Citus code.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!IsInDistributedTransaction(&currentBackendData))\n\t\t{\n\t\t\t/* not a distributed transaction */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!currentBackendData.transactionId.transactionOriginator)\n\t\t{\n\t\t\t/* not a coordinator process */\n\t\t\tcontinue;\n\t\t}\n\n\t\tuint64 *transactionNumber = (uint64 *) palloc0(sizeof(uint64));\n\t\t*transactionNumber = currentBackendData.transactionId.transactionNumber;\n\n\t\tactiveTransactionNumberList = lappend(activeTransactionNumberList,\n\t\t\t\t\t\t\t\t\t\t\t  transactionNumber);\n\t}\n\n\treturn activeTransactionNumberList;\n}\n\n\n/*\n * GetMyProcLocalTransactionId() is a wrapper for\n * getting lxid of MyProc.\n */\nLocalTransactionId\nGetMyProcLocalTransactionId(void)\n{\n\treturn getLxid_compat(MyProc);\n}\n\n\n/*\n * GetExternalClientBackendCount returns externalClientBackendCounter in\n * the shared memory.\n */\nint\nGetExternalClientBackendCount(void)\n{\n\tuint32 activeBackendCount =\n\t\tpg_atomic_read_u32(&backendManagementShmemData->externalClientBackendCounter);\n\n\treturn activeBackendCount;\n}\n\n\n/*\n * IncrementExternalClientBackendCounter increments externalClientBackendCounter in\n * the shared memory by one.\n */\nuint32\nIncrementExternalClientBackendCounter(void)\n{\n\treturn pg_atomic_add_fetch_u32(\n\t\t&backendManagementShmemData->externalClientBackendCounter, 1);\n}\n\n\n/*\n * DecrementExternalClientBackendCounter decrements externalClientBackendCounter in\n * the shared memory by one.\n */\nvoid\nDecrementExternalClientBackendCounter(void)\n{\n\tpg_atomic_sub_fetch_u32(&backendManagementShmemData->externalClientBackendCounter, 1);\n}\n\n\n/*\n * IsRebalancerInitiatedBackend returns true if we are in a backend that citus\n * rebalancer initiated.\n */\nbool\nIsRebalancerInternalBackend(void)\n{\n\tif (CurrentBackendType == CITUS_BACKEND_NOT_ASSIGNED)\n\t{\n\t\tDetermineCitusBackendType(application_name);\n\t}\n\n\treturn CurrentBackendType == CITUS_REBALANCER_BACKEND;\n}\n\n\n/*\n * IsCitusInitiatedRemoteBackend returns true if we are in a backend that citus\n * initiated via remote connection.\n */\nbool\nIsCitusInternalBackend(void)\n{\n\tif (CurrentBackendType == CITUS_BACKEND_NOT_ASSIGNED)\n\t{\n\t\tDetermineCitusBackendType(application_name);\n\t}\n\n\treturn CurrentBackendType == CITUS_INTERNAL_BACKEND;\n}\n\n\n/*\n * IsCitusRunCommandBackend returns true if we are in a backend that one of\n * the run_command_on_* functions initiated.\n */\nbool\nIsCitusRunCommandBackend(void)\n{\n\tif (CurrentBackendType == CITUS_BACKEND_NOT_ASSIGNED)\n\t{\n\t\tDetermineCitusBackendType(application_name);\n\t}\n\n\treturn CurrentBackendType == CITUS_RUN_COMMAND_BACKEND;\n}\n\n\nbool\nIsExternalClientBackend(void)\n{\n\tif (CurrentBackendType == CITUS_BACKEND_NOT_ASSIGNED)\n\t{\n\t\tDetermineCitusBackendType(application_name);\n\t}\n\n\treturn CurrentBackendType == EXTERNAL_CLIENT_BACKEND;\n}\n\n\n/*\n * IsRebalancerInitiatedBackend returns true if we are in a backend that citus\n * rebalancer initiated.\n */\nbool\nIsCitusShardTransferBackend(void)\n{\n\tint prefixLength = strlen(CITUS_SHARD_TRANSFER_APPLICATION_NAME_PREFIX);\n\n\treturn strncmp(application_name,\n\t\t\t\t   CITUS_SHARD_TRANSFER_APPLICATION_NAME_PREFIX,\n\t\t\t\t   prefixLength) == 0;\n}\n\n\n/*\n * DetermineCitusBackendType determines the type of backend based on the application_name.\n */\nvoid\nDetermineCitusBackendType(const char *applicationName)\n{\n\tif (applicationName &&\n\t\tExtractGlobalPID(applicationName) != INVALID_CITUS_INTERNAL_BACKEND_GPID)\n\t{\n\t\tfor (int i = 0; i < lengthof(CitusBackendPrefixes); i++)\n\t\t{\n\t\t\tuint64 prefixLength = strlen(CitusBackendPrefixes[i]);\n\n\t\t\t/* does application name start with this prefix prefix */\n\t\t\tif (strncmp(applicationName, CitusBackendPrefixes[i], prefixLength) == 0)\n\t\t\t{\n\t\t\t\tCurrentBackendType = CitusBackendTypes[i];\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\tCurrentBackendType = EXTERNAL_CLIENT_BACKEND;\n}\n"
  },
  {
    "path": "src/backend/distributed/transaction/citus_dist_stat_activity.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_dist_stat_activity.c\n *\n *\tThe methods in the file are deprecated.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\nPG_FUNCTION_INFO_V1(citus_dist_stat_activity);\nPG_FUNCTION_INFO_V1(citus_worker_stat_activity);\n\n/* This UDF is deprecated. */\nDatum\ncitus_dist_stat_activity(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"This UDF is deprecated.\")));\n\n\tPG_RETURN_NULL();\n}\n\n\n/* This UDF is deprecated. */\nDatum\ncitus_worker_stat_activity(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"This UDF is deprecated.\")));\n\n\tPG_RETURN_NULL();\n}\n"
  },
  {
    "path": "src/backend/distributed/transaction/distributed_deadlock_detection.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * distributed_deadlock_detection.c\n *\n *  Functions for performing distributed deadlock detection.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"access/hash.h\"\n#include \"nodes/pg_list.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/timestamp.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/distributed_deadlock_detection.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/transaction_identifier.h\"\n\n\n/* used only for finding the deadlock cycle path */\ntypedef struct QueuedTransactionNode\n{\n\tTransactionNode *transactionNode;\n\n\tint currentStackDepth;\n} QueuedTransactionNode;\n\n\n/* GUC, determining whether debug messages for deadlock detection sent to LOG */\nbool LogDistributedDeadlockDetection = false;\n\n\nstatic bool CheckDeadlockForTransactionNode(TransactionNode *startingTransactionNode,\n\t\t\t\t\t\t\t\t\t\t\tint maxStackDepth,\n\t\t\t\t\t\t\t\t\t\t\tList **deadlockPath);\nstatic void PrependOutgoingNodesToQueue(TransactionNode *queuedTransactionNode,\n\t\t\t\t\t\t\t\t\t\tint currentStackDepth,\n\t\t\t\t\t\t\t\t\t\tList **toBeVisitedNodes);\nstatic void BuildDeadlockPathList(QueuedTransactionNode *cycledTransactionNode,\n\t\t\t\t\t\t\t\t  TransactionNode **transactionNodeStack,\n\t\t\t\t\t\t\t\t  List **deadlockPath);\nstatic void ResetVisitedFields(HTAB *adjacencyList);\nstatic bool AssociateDistributedTransactionWithBackendProc(TransactionNode *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   transactionNode);\nstatic TransactionNode * GetOrCreateTransactionNode(HTAB *adjacencyList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistributedTransactionId *\n\t\t\t\t\t\t\t\t\t\t\t\t\ttransactionId);\nstatic uint32 DistributedTransactionIdHash(const void *key, Size keysize);\nstatic int DistributedTransactionIdCompare(const void *a, const void *b, Size keysize);\nstatic void LogCancellingBackend(TransactionNode *transactionNode);\nstatic void LogTransactionNode(TransactionNode *transactionNode);\nstatic void LogDistributedDeadlockDebugMessage(const char *errorMessage);\n\nPG_FUNCTION_INFO_V1(check_distributed_deadlocks);\n\n\n/*\n * check_distributed_deadlocks is the external API for manually\n * checking for distributed deadlocks. For the details, see\n * CheckForDistributedDeadlocks().\n */\nDatum\ncheck_distributed_deadlocks(PG_FUNCTION_ARGS)\n{\n\tbool deadlockFound = CheckForDistributedDeadlocks();\n\n\treturn BoolGetDatum(deadlockFound);\n}\n\n\n/*\n * CheckForDistributedDeadlocks is the entry point for detecting\n * distributed deadlocks.\n *\n * In plain words, the function first builds a wait graph by\n * adding the wait edges from the local node and then adding the\n * remote wait edges to form a global wait graph. Later, the wait\n * graph is converted into another graph representation (adjacency\n * lists) for more efficient searches. Finally, a DFS is done on\n * the adjacency lists. Finding a cycle in the graph unveils a\n * distributed deadlock. Upon finding a deadlock, the youngest\n * participant backend is cancelled.\n *\n * The complexity of the algorithm is O(N) for each distributed\n * transaction that's checked for deadlocks. Note that there exists\n *  0 to MaxBackends number of transactions.\n *\n * The function returns true if a deadlock is found. Otherwise, returns\n * false.\n */\nbool\nCheckForDistributedDeadlocks(void)\n{\n\tHASH_SEQ_STATUS status;\n\tTransactionNode *transactionNode = NULL;\n\tint32 localGroupId = GetLocalGroupId();\n\tList *workerNodeList = ActiveReadableNodeList();\n\n\t/*\n\t * We don't need to do any distributed deadlock checking if there\n\t * are no worker nodes. This might even be problematic for a non-mx\n\t * worker node which has the same group id with its master (i.e., 0),\n\t * which may erroneously decide to kill the deadlocks happening on it.\n\t */\n\tif (list_length(workerNodeList) == 0)\n\t{\n\t\treturn false;\n\t}\n\n\t/* distributed deadlock detection only considers distributed txs */\n\tbool onlyDistributedTx = true;\n\tWaitGraph *waitGraph = BuildGlobalWaitGraph(onlyDistributedTx);\n\tHTAB *adjacencyLists = BuildAdjacencyListsForWaitGraph(waitGraph);\n\n\tint edgeCount = waitGraph->edgeCount;\n\n\t/*\n\t * We iterate on transaction nodes and search for deadlocks where the\n\t * starting node is the given transaction node.\n\t */\n\thash_seq_init(&status, adjacencyLists);\n\twhile ((transactionNode = (TransactionNode *) hash_seq_search(&status)) != 0)\n\t{\n\t\tList *deadlockPath = NIL;\n\n\t\t/*\n\t\t * Since we only see nodes which are waiting or being waited upon it's not\n\t\t * possible to have more than edgeCount + 1 nodes.\n\t\t */\n\t\tint maxStackDepth = edgeCount + 1;\n\n\t\t/* we're only interested in finding deadlocks originating from this node */\n\t\tif (transactionNode->transactionId.initiatorNodeIdentifier != localGroupId)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tResetVisitedFields(adjacencyLists);\n\n\t\tbool deadlockFound = CheckDeadlockForTransactionNode(transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t maxStackDepth,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t &deadlockPath);\n\t\tif (deadlockFound)\n\t\t{\n\t\t\tTransactionNode *youngestAliveTransaction = NULL;\n\n\t\t\t/*\n\t\t\t * There should generally be at least two transactions to get into a\n\t\t\t * deadlock. However, in case Citus gets into a self-deadlock, we may\n\t\t\t * find a deadlock with a single transaction.\n\t\t\t */\n\t\t\tAssert(list_length(deadlockPath) >= 1);\n\n\t\t\tLogDistributedDeadlockDebugMessage(\"Distributed deadlock found among the \"\n\t\t\t\t\t\t\t\t\t\t\t   \"following distributed transactions:\");\n\n\t\t\t/*\n\t\t\t * We search for the youngest participant for two reasons\n\t\t\t * (i) predictable results (ii) cancel the youngest transaction\n\t\t\t * (i.e., if a DDL continues for 1 hour and deadlocks with a\n\t\t\t * SELECT continues for 10 msec, we prefer to cancel the SELECT).\n\t\t\t *\n\t\t\t * We're also searching for the youngest transactions initiated by\n\t\t\t * this node.\n\t\t\t */\n\t\t\tTransactionNode *currentNode = NULL;\n\t\t\tforeach_declared_ptr(currentNode, deadlockPath)\n\t\t\t{\n\t\t\t\tbool transactionAssociatedWithProc =\n\t\t\t\t\tAssociateDistributedTransactionWithBackendProc(currentNode);\n\n\t\t\t\tLogTransactionNode(currentNode);\n\n\t\t\t\t/* we couldn't find the backend process originated the transaction */\n\t\t\t\tif (!transactionAssociatedWithProc)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (youngestAliveTransaction == NULL)\n\t\t\t\t{\n\t\t\t\t\tyoungestAliveTransaction = currentNode;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tTimestampTz youngestTimestamp =\n\t\t\t\t\tyoungestAliveTransaction->transactionId.timestamp;\n\t\t\t\tTimestampTz currentTimestamp = currentNode->transactionId.timestamp;\n\t\t\t\tif (timestamptz_cmp_internal(currentTimestamp, youngestTimestamp) == 1)\n\t\t\t\t{\n\t\t\t\t\tyoungestAliveTransaction = currentNode;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* we found the deadlock and its associated proc exists */\n\t\t\tif (youngestAliveTransaction)\n\t\t\t{\n\t\t\t\tCancelTransactionDueToDeadlock(youngestAliveTransaction->initiatorProc);\n\t\t\t\tLogCancellingBackend(youngestAliveTransaction);\n\n\t\t\t\thash_seq_term(&status);\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CheckDeadlockForTransactionNode does a DFS starting with the given\n * transaction node and checks for a cycle (i.e., the node can be reached again\n * while traversing the graph).\n *\n * Finding a cycle indicates a distributed deadlock and the function returns\n * true on that case. Also, the deadlockPath is filled with the transaction\n * nodes that form the cycle.\n */\nstatic bool\nCheckDeadlockForTransactionNode(TransactionNode *startingTransactionNode,\n\t\t\t\t\t\t\t\tint maxStackDepth,\n\t\t\t\t\t\t\t\tList **deadlockPath)\n{\n\tList *toBeVisitedNodes = NIL;\n\tconst int rootStackDepth = 0;\n\tTransactionNode **transactionNodeStack =\n\t\tpalloc0(maxStackDepth * sizeof(TransactionNode *));\n\n\t/*\n\t * We keep transactionNodeStack to keep track of the deadlock paths. At this point,\n\t * adjust the depth of the starting node and set the stack's first element with\n\t * the starting node.\n\t */\n\ttransactionNodeStack[rootStackDepth] = startingTransactionNode;\n\n\tPrependOutgoingNodesToQueue(startingTransactionNode, rootStackDepth,\n\t\t\t\t\t\t\t\t&toBeVisitedNodes);\n\n\t/* traverse the graph and search for the deadlocks */\n\twhile (toBeVisitedNodes != NIL)\n\t{\n\t\tQueuedTransactionNode *queuedTransactionNode =\n\t\t\t(QueuedTransactionNode *) linitial(toBeVisitedNodes);\n\t\tTransactionNode *currentTransactionNode = queuedTransactionNode->transactionNode;\n\n\t\ttoBeVisitedNodes = list_delete_first(toBeVisitedNodes);\n\n\t\t/* cycle found, let the caller know about the cycle */\n\t\tif (currentTransactionNode == startingTransactionNode)\n\t\t{\n\t\t\tBuildDeadlockPathList(queuedTransactionNode, transactionNodeStack,\n\t\t\t\t\t\t\t\t  deadlockPath);\n\n\t\t\tpfree(transactionNodeStack);\n\t\t\treturn true;\n\t\t}\n\n\t\t/* don't need to revisit the node again */\n\t\tif (currentTransactionNode->transactionVisited)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tcurrentTransactionNode->transactionVisited = true;\n\n\t\t/* set the stack's corresponding element with the current node */\n\t\tint currentStackDepth = queuedTransactionNode->currentStackDepth;\n\t\tAssert(currentStackDepth < maxStackDepth);\n\t\ttransactionNodeStack[currentStackDepth] = currentTransactionNode;\n\n\t\tPrependOutgoingNodesToQueue(currentTransactionNode, currentStackDepth,\n\t\t\t\t\t\t\t\t\t&toBeVisitedNodes);\n\t}\n\n\tpfree(transactionNodeStack);\n\treturn false;\n}\n\n\n/*\n * PrependOutgoingNodesToQueue prepends the waiters of the input transaction nodes to the\n * toBeVisitedNodes.\n */\nstatic void\nPrependOutgoingNodesToQueue(TransactionNode *transactionNode, int currentStackDepth,\n\t\t\t\t\t\t\tList **toBeVisitedNodes)\n{\n\t/* as we traverse outgoing edges, increment the depth */\n\tcurrentStackDepth++;\n\n\t/* prepend to the list to continue depth-first search */\n\tTransactionNode *waitForTransaction = NULL;\n\tforeach_declared_ptr(waitForTransaction, transactionNode->waitsFor)\n\t{\n\t\tQueuedTransactionNode *queuedNode = palloc0(sizeof(QueuedTransactionNode));\n\n\t\tqueuedNode->transactionNode = waitForTransaction;\n\t\tqueuedNode->currentStackDepth = currentStackDepth;\n\n\t\t*toBeVisitedNodes = lcons(queuedNode, *toBeVisitedNodes);\n\t}\n}\n\n\n/*\n * BuildDeadlockPathList fills deadlockPath with a list of transactions involved\n * in a distributed deadlock (i.e. a cycle in the graph).\n */\nstatic void\nBuildDeadlockPathList(QueuedTransactionNode *cycledTransactionNode,\n\t\t\t\t\t  TransactionNode **transactionNodeStack,\n\t\t\t\t\t  List **deadlockPath)\n{\n\tint deadlockStackDepth = cycledTransactionNode->currentStackDepth;\n\n\t*deadlockPath = NIL;\n\n\tfor (int stackIndex = 0; stackIndex < deadlockStackDepth; stackIndex++)\n\t{\n\t\t*deadlockPath = lappend(*deadlockPath, transactionNodeStack[stackIndex]);\n\t}\n}\n\n\n/*\n * ResetVisitedFields goes over all the elements of the input adjacency list\n * and sets transactionVisited to false.\n */\nstatic void\nResetVisitedFields(HTAB *adjacencyList)\n{\n\tHASH_SEQ_STATUS status;\n\tTransactionNode *resetNode = NULL;\n\n\t/* reset all visited fields */\n\thash_seq_init(&status, adjacencyList);\n\n\twhile ((resetNode = (TransactionNode *) hash_seq_search(&status)) != 0)\n\t{\n\t\tresetNode->transactionVisited = false;\n\t}\n}\n\n\n/*\n * AssociateDistributedTransactionWithBackendProc gets a transaction node\n * and searches the corresponding backend. Once found, transactionNodes'\n * initiatorProc is set to it.\n *\n * The function goes over all the backends, checks for the backend with\n * the same transaction number as the given transaction node.\n *\n * If the transaction cannot be associated with a backend process, the function\n * returns false. Otherwise, the function returns true.\n */\nstatic bool\nAssociateDistributedTransactionWithBackendProc(TransactionNode *transactionNode)\n{\n\tint32 localGroupId PG_USED_FOR_ASSERTS_ONLY = GetLocalGroupId();\n\n\tfor (int backendIndex = 0; backendIndex < MaxBackends; ++backendIndex)\n\t{\n\t\tPGPROC *currentProc = GetPGProcByNumber(backendIndex);\n\t\tBackendData currentBackendData;\n\n\t\t/* we're not interested in processes that are not active or waiting on a lock */\n\t\tif (currentProc->pid <= 0)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tGetBackendDataForProc(currentProc, &currentBackendData);\n\n\t\t/* we're only interested in distribtued transactions */\n\t\tif (!IsInDistributedTransaction(&currentBackendData))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tDistributedTransactionId *currentTransactionId =\n\t\t\t&currentBackendData.transactionId;\n\n\t\tif (currentTransactionId->transactionNumber != transactionNode->transactionId.\n\t\t\ttransactionNumber)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* we're only interested in transactions started on this node */\n\t\tif (!currentTransactionId->transactionOriginator)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* at the point we should only have transactions initiated by this node */\n\t\tAssert(currentTransactionId->initiatorNodeIdentifier == localGroupId);\n\n\t\ttransactionNode->initiatorProc = currentProc;\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * BuildAdjacencyListsForWaitGraph converts the input wait graph to\n * an adjacency list for further processing.\n *\n * The input wait graph consists of set of wait edges between all\n * backends in the Citus cluster.\n *\n * We represent the adjacency list with an HTAB structure. Each node is\n * represented with a DistributedTransactionId and each edge is represented with\n * a TransactionNode structure.\n *\n * While iterating over the input wait edges, we follow the algorithm\n * below:\n *    for each edge in waitGraph:\n *      - find the corresponding nodes for waiting and\n *        blocking transactions in the adjacency list\n *          - if not found, add new node(s) to the list\n *      - Add blocking transaction to the waiting transaction's waitFor\n *        list\n *\n *  The format of the adjacency list becomes the following:\n *      [transactionId] = [transactionNode->waitsFor {list of waiting transaction nodes}]\n */\nextern HTAB *\nBuildAdjacencyListsForWaitGraph(WaitGraph *waitGraph)\n{\n\tHASHCTL info;\n\tint edgeCount = waitGraph->edgeCount;\n\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(DistributedTransactionId);\n\tinfo.entrysize = sizeof(TransactionNode);\n\tinfo.hash = DistributedTransactionIdHash;\n\tinfo.match = DistributedTransactionIdCompare;\n\tinfo.hcxt = CurrentMemoryContext;\n\tuint32 hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT | HASH_COMPARE);\n\n\tHTAB *adjacencyList = hash_create(\"distributed deadlock detection\", 64, &info,\n\t\t\t\t\t\t\t\t\t  hashFlags);\n\n\tfor (int edgeIndex = 0; edgeIndex < edgeCount; edgeIndex++)\n\t{\n\t\tWaitEdge *edge = &waitGraph->edges[edgeIndex];\n\t\tbool transactionOriginator = false;\n\n\t\tDistributedTransactionId waitingId = {\n\t\t\tedge->waitingNodeId,\n\t\t\ttransactionOriginator,\n\t\t\tedge->waitingTransactionNum,\n\t\t\tedge->waitingTransactionStamp\n\t\t};\n\n\t\tDistributedTransactionId blockingId = {\n\t\t\tedge->blockingNodeId,\n\t\t\ttransactionOriginator,\n\t\t\tedge->blockingTransactionNum,\n\t\t\tedge->blockingTransactionStamp\n\t\t};\n\n\t\tTransactionNode *waitingTransaction =\n\t\t\tGetOrCreateTransactionNode(adjacencyList, &waitingId);\n\t\tTransactionNode *blockingTransaction =\n\t\t\tGetOrCreateTransactionNode(adjacencyList, &blockingId);\n\n\t\twaitingTransaction->waitsFor = lappend(waitingTransaction->waitsFor,\n\t\t\t\t\t\t\t\t\t\t\t   blockingTransaction);\n\t}\n\n\treturn adjacencyList;\n}\n\n\n/*\n * GetOrCreateTransactionNode searches distributedTransactionHash for the given\n * given transactionId. If the transaction is not found, a new transaction node\n * with the given transaction identifier is added.\n */\nstatic TransactionNode *\nGetOrCreateTransactionNode(HTAB *adjacencyList, DistributedTransactionId *transactionId)\n{\n\tbool found = false;\n\n\tTransactionNode *transactionNode = (TransactionNode *) hash_search(adjacencyList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   transactionId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &found);\n\tif (!found)\n\t{\n\t\ttransactionNode->waitsFor = NIL;\n\t\ttransactionNode->initiatorProc = NULL;\n\t}\n\n\treturn transactionNode;\n}\n\n\n/*\n * DistributedTransactionIdHash returns hashed value for a given distributed\n * transaction id.\n */\nstatic uint32\nDistributedTransactionIdHash(const void *key, Size keysize)\n{\n\tDistributedTransactionId *entry = (DistributedTransactionId *) key;\n\n\tuint32 hash = hash_uint32(entry->initiatorNodeIdentifier);\n\thash = hash_combine(hash, hash_any((unsigned char *) &entry->transactionNumber,\n\t\t\t\t\t\t\t\t\t   sizeof(int64)));\n\thash = hash_combine(hash, hash_any((unsigned char *) &entry->timestamp,\n\t\t\t\t\t\t\t\t\t   sizeof(TimestampTz)));\n\n\treturn hash;\n}\n\n\n/*\n * DistributedTransactionIdCompare compares DistributedTransactionId's a and b\n * and returns -1 if a < b, 1 if a > b, 0 if they are equal.\n *\n * DistributedTransactionId are first compared by their timestamp, then transaction\n * number, then node identifier.\n */\nstatic int\nDistributedTransactionIdCompare(const void *a, const void *b, Size keysize)\n{\n\tDistributedTransactionId *xactIdA = (DistributedTransactionId *) a;\n\tDistributedTransactionId *xactIdB = (DistributedTransactionId *) b;\n\n\tif (!TimestampDifferenceExceeds(xactIdB->timestamp, xactIdA->timestamp, 0))\n\t{\n\t\t/* ! (B <= A) = A < B */\n\t\treturn -1;\n\t}\n\telse if (!TimestampDifferenceExceeds(xactIdA->timestamp, xactIdB->timestamp, 0))\n\t{\n\t\t/* ! (A <= B) = A > B */\n\t\treturn 1;\n\t}\n\telse if (xactIdA->transactionNumber < xactIdB->transactionNumber)\n\t{\n\t\treturn -1;\n\t}\n\telse if (xactIdA->transactionNumber > xactIdB->transactionNumber)\n\t{\n\t\treturn 1;\n\t}\n\telse if (xactIdA->initiatorNodeIdentifier < xactIdB->initiatorNodeIdentifier)\n\t{\n\t\treturn -1;\n\t}\n\telse if (xactIdA->initiatorNodeIdentifier > xactIdB->initiatorNodeIdentifier)\n\t{\n\t\treturn 1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * LogCancellingBackend should only be called when a distributed transaction's\n * backend is cancelled due to distributed deadlocks. It sends which transaction\n * is cancelled and its corresponding pid to the log.\n */\nstatic void\nLogCancellingBackend(TransactionNode *transactionNode)\n{\n\tif (!LogDistributedDeadlockDetection)\n\t{\n\t\treturn;\n\t}\n\n\tStringInfo logMessage = makeStringInfo();\n\n\tappendStringInfo(logMessage, \"Cancelling the following backend \"\n\t\t\t\t\t\t\t\t \"to resolve distributed deadlock \"\n\t\t\t\t\t\t\t\t \"(transaction number = \" UINT64_FORMAT \", pid = %d)\",\n\t\t\t\t\t transactionNode->transactionId.transactionNumber,\n\t\t\t\t\t transactionNode->initiatorProc->pid);\n\n\tLogDistributedDeadlockDebugMessage(logMessage->data);\n}\n\n\n/*\n * LogTransactionNode converts the transaction node to a human readable form\n * and sends to the logs via LogDistributedDeadlockDebugMessage().\n */\nstatic void\nLogTransactionNode(TransactionNode *transactionNode)\n{\n\tif (!LogDistributedDeadlockDetection)\n\t{\n\t\treturn;\n\t}\n\n\tStringInfo logMessage = makeStringInfo();\n\tDistributedTransactionId *transactionId = &(transactionNode->transactionId);\n\n\tappendStringInfo(logMessage,\n\t\t\t\t\t \"[DistributedTransactionId: (%d, \" UINT64_FORMAT \", %s)] = \",\n\t\t\t\t\t transactionId->initiatorNodeIdentifier,\n\t\t\t\t\t transactionId->transactionNumber,\n\t\t\t\t\t timestamptz_to_str(transactionId->timestamp));\n\n\tappendStringInfo(logMessage, \"[WaitsFor transaction numbers: %s]\",\n\t\t\t\t\t WaitsForToString(transactionNode->waitsFor));\n\n\t/* log the backend query if the proc is associated with the transaction */\n\tif (transactionNode->initiatorProc != NULL)\n\t{\n\t\tconst char *backendQuery =\n\t\t\tpgstat_get_backend_current_activity(transactionNode->initiatorProc->pid,\n\t\t\t\t\t\t\t\t\t\t\t\tfalse);\n\n\t\tappendStringInfo(logMessage, \"[Backend Query: %s]\", backendQuery);\n\t}\n\n\tLogDistributedDeadlockDebugMessage(logMessage->data);\n}\n\n\n/*\n * LogDistributedDeadlockDebugMessage checks EnableDistributedDeadlockDebugging flag. If\n * it is true, the input message is sent to the logs with LOG level. Also, current timestamp\n * is prepanded to the message.\n */\nstatic void\nLogDistributedDeadlockDebugMessage(const char *errorMessage)\n{\n\tif (!LogDistributedDeadlockDetection)\n\t{\n\t\treturn;\n\t}\n\n\tereport(LOG, (errmsg(\"[%s] %s\", timestamptz_to_str(GetCurrentTimestamp()),\n\t\t\t\t\t\t errorMessage)));\n}\n\n\n/*\n * WaitsForToString is only intended for testing and debugging. It gets a\n * waitsForList and returns the list of transaction nodes' transactionNumber\n * in a string.\n */\nchar *\nWaitsForToString(List *waitsFor)\n{\n\tStringInfo transactionIdStr = makeStringInfo();\n\n\tTransactionNode *waitingNode = NULL;\n\tforeach_declared_ptr(waitingNode, waitsFor)\n\t{\n\t\tif (transactionIdStr->len != 0)\n\t\t{\n\t\t\tappendStringInfoString(transactionIdStr, \",\");\n\t\t}\n\n\t\tappendStringInfo(transactionIdStr, UINT64_FORMAT,\n\t\t\t\t\t\t waitingNode->transactionId.transactionNumber);\n\t}\n\n\treturn transactionIdStr->data;\n}\n"
  },
  {
    "path": "src/backend/distributed/transaction/lock_graph.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * lock_graph.c\n *\n *  Functions for obtaining local and global lock graphs in which each\n *  node is a distributed transaction, and an edge represent a waiting-for\n *  relationship.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/hash.h\"\n#include \"storage/proc.h\"\n#include \"utils/builtins.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/timestamp.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/tuplestore.h\"\n\n\n/*\n * PROCStack is a stack of PGPROC pointers used to perform a depth-first search\n * through the lock graph. It also keeps track of which processes have been\n * added to the stack to avoid visiting the same process multiple times.\n */\ntypedef struct PROCStack\n{\n\tint procCount;\n\tPGPROC **procs;\n\tbool *procAdded;\n} PROCStack;\n\n\nstatic void AddWaitEdgeFromResult(WaitGraph *waitGraph, PGresult *result, int rowIndex);\nstatic void ReturnWaitGraph(WaitGraph *waitGraph, FunctionCallInfo fcinfo);\nstatic void AddWaitEdgeFromBlockedProcessResult(WaitGraph *waitGraph, PGresult *result,\n\t\t\t\t\t\t\t\t\t\t\t\tint rowIndex);\nstatic void ReturnBlockedProcessGraph(WaitGraph *waitGraph, FunctionCallInfo fcinfo);\nstatic WaitGraph * BuildLocalWaitGraph(bool onlyDistributedTx);\nstatic bool IsProcessWaitingForSafeOperations(PGPROC *proc);\nstatic void LockLockData(void);\nstatic void UnlockLockData(void);\nstatic void AddEdgesForLockWaits(WaitGraph *waitGraph, PGPROC *waitingProc,\n\t\t\t\t\t\t\t\t PROCStack *remaining);\nstatic void AddEdgesForWaitQueue(WaitGraph *waitGraph, PGPROC *waitingProc,\n\t\t\t\t\t\t\t\t PROCStack *remaining);\nstatic void AddWaitEdge(WaitGraph *waitGraph, PGPROC *waitingProc, PGPROC *blockingProc,\n\t\t\t\t\t\tPROCStack *remaining);\nstatic WaitEdge * AllocWaitEdge(WaitGraph *waitGraph);\nstatic void AddProcToVisit(PROCStack *remaining, PGPROC *proc);\nstatic bool IsSameLockGroup(PGPROC *leftProc, PGPROC *rightProc);\nstatic bool IsConflictingLockMask(int holdMask, int conflictMask);\n\n/*\n * We almost have 2 sets of identical functions. The first set (e.g., dump_wait_edges)\n * functions are intended for distributed deadlock detection purposes.\n *\n * The second set of functions (e.g., citus_internal_local_blocked_processes) are\n * intended for citus_lock_waits view.\n *\n * The main difference is that the former functions only show processes that are blocked\n * inside a distributed transaction (e.g., see AssignDistributedTransactionId()).\n * The latter functions return a superset, where any blocked process is returned.\n *\n * We kept two different set of functions for two purposes. First, the deadlock detection\n * is a performance critical code-path happening very frequently and we don't add any\n * performance overhead. Secondly, to be able to do rolling upgrades, we cannot change\n * the API of dump_global_wait_edges/dump_local_wait_edges such that they take a boolean\n * parameter. If we do that, until all nodes are upgraded, the deadlock detection would fail,\n * which is not acceptable.\n */\nPG_FUNCTION_INFO_V1(dump_local_wait_edges);\nPG_FUNCTION_INFO_V1(dump_global_wait_edges);\n\nPG_FUNCTION_INFO_V1(citus_internal_local_blocked_processes);\nPG_FUNCTION_INFO_V1(citus_internal_global_blocked_processes);\n\n\n/*\n * dump_global_wait_edges returns global wait edges for distributed transactions\n * originating from the node on which it is started.\n */\nDatum\ndump_global_wait_edges(PG_FUNCTION_ARGS)\n{\n\tbool onlyDistributedTx = true;\n\n\tWaitGraph *waitGraph = BuildGlobalWaitGraph(onlyDistributedTx);\n\n\tReturnWaitGraph(waitGraph, fcinfo);\n\n\treturn (Datum) 0;\n}\n\n\n/*\n * citus_internal_global_blocked_processes returns global wait edges\n * including all processes running on the cluster.\n */\nDatum\ncitus_internal_global_blocked_processes(PG_FUNCTION_ARGS)\n{\n\tbool onlyDistributedTx = false;\n\n\tWaitGraph *waitGraph = BuildGlobalWaitGraph(onlyDistributedTx);\n\n\tReturnBlockedProcessGraph(waitGraph, fcinfo);\n\n\treturn (Datum) 0;\n}\n\n\n/*\n * BuildGlobalWaitGraph builds a wait graph for distributed transactions\n * that originate from this node, including edges from all (other) worker\n * nodes.\n *\n *\n * If onlyDistributedTx is true, we only return distributed transactions\n * (e.g., AssignDistributedTransaction() or assign_distributed_transactions())\n * has been called for the process. Distributed deadlock detection only\n * interested in these processes.\n */\nWaitGraph *\nBuildGlobalWaitGraph(bool onlyDistributedTx)\n{\n\tList *workerNodeList = ActiveReadableNodeList();\n\tchar *nodeUser = CitusExtensionOwnerName();\n\tList *connectionList = NIL;\n\tint32 localGroupId = GetLocalGroupId();\n\n\t/* deadlock detection is only interested in distributed transactions */\n\tWaitGraph *waitGraph = BuildLocalWaitGraph(onlyDistributedTx);\n\n\t/* open connections in parallel */\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tconst char *nodeName = workerNode->workerName;\n\t\tint nodePort = workerNode->workerPort;\n\t\tint connectionFlags = 0;\n\n\t\tif (workerNode->groupId == localGroupId)\n\t\t{\n\t\t\t/* we already have local wait edges */\n\t\t\tcontinue;\n\t\t}\n\n\t\tMultiConnection *connection = StartNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodeUser, NULL);\n\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\tFinishConnectionListEstablishment(connectionList);\n\n\t/* send commands in parallel */\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tStringInfo queryString = makeStringInfo();\n\n\t\tif (onlyDistributedTx)\n\t\t{\n\t\t\tappendStringInfo(queryString,\n\t\t\t\t\t\t\t \"SELECT waiting_pid, waiting_node_id, \"\n\t\t\t\t\t\t\t \"waiting_transaction_num, waiting_transaction_stamp, \"\n\t\t\t\t\t\t\t \"blocking_pid, blocking_node_id, blocking_transaction_num, \"\n\t\t\t\t\t\t\t \"blocking_transaction_stamp, blocking_transaction_waiting \"\n\t\t\t\t\t\t\t \"FROM dump_local_wait_edges()\");\n\t\t}\n\t\telse\n\t\t{\n\t\t\tappendStringInfo(queryString,\n\t\t\t\t\t\t\t \"SELECT waiting_global_pid, waiting_pid, \"\n\t\t\t\t\t\t\t \"waiting_node_id, waiting_transaction_num, waiting_transaction_stamp, \"\n\t\t\t\t\t\t\t \"blocking_global_pid,blocking_pid, blocking_node_id, \"\n\t\t\t\t\t\t\t \"blocking_transaction_num, blocking_transaction_stamp, blocking_transaction_waiting \"\n\t\t\t\t\t\t\t \"FROM citus_internal.local_blocked_processes()\");\n\t\t}\n\n\t\tint querySent = SendRemoteCommand(connection, queryString->data);\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tReportConnectionError(connection, WARNING);\n\t\t}\n\t}\n\n\t/* receive dump_local_wait_edges results */\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tbool raiseInterrupts = true;\n\n\t\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\t\tif (!IsResponseOK(result))\n\t\t{\n\t\t\tReportResultError(connection, result, WARNING);\n\t\t\tcontinue;\n\t\t}\n\n\t\tint64 rowCount = PQntuples(result);\n\t\tint64 colCount = PQnfields(result);\n\n\t\tif (onlyDistributedTx && colCount != 9)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"unexpected number of columns from \"\n\t\t\t\t\t\t\t\t\t \"dump_local_wait_edges\")));\n\t\t\tcontinue;\n\t\t}\n\t\telse if (!onlyDistributedTx && colCount != 11)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"unexpected number of columns from \"\n\t\t\t\t\t\t\t\t\t \"citus_internal.local_blocked_processes\")));\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (int64 rowIndex = 0; rowIndex < rowCount; rowIndex++)\n\t\t{\n\t\t\tif (onlyDistributedTx)\n\t\t\t{\n\t\t\t\tAddWaitEdgeFromResult(waitGraph, result, rowIndex);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tAddWaitEdgeFromBlockedProcessResult(waitGraph, result, rowIndex);\n\t\t\t}\n\t\t}\n\n\t\tPQclear(result);\n\t\tForgetResults(connection);\n\t}\n\n\treturn waitGraph;\n}\n\n\n/*\n * AddWaitEdgeFromResult adds an edge to the wait graph that is read from\n * a PGresult.\n */\nstatic void\nAddWaitEdgeFromResult(WaitGraph *waitGraph, PGresult *result, int rowIndex)\n{\n\tWaitEdge *waitEdge = AllocWaitEdge(waitGraph);\n\n\twaitEdge->waitingGPid = 0; /* not requested for deadlock detection */\n\twaitEdge->waitingPid = ParseIntField(result, rowIndex, 0);\n\twaitEdge->waitingNodeId = ParseIntField(result, rowIndex, 1);\n\twaitEdge->waitingTransactionNum = ParseIntField(result, rowIndex, 2);\n\twaitEdge->waitingTransactionStamp = ParseTimestampTzField(result, rowIndex, 3);\n\twaitEdge->blockingGPid = 0; /* not requested for deadlock detection */\n\twaitEdge->blockingPid = ParseIntField(result, rowIndex, 4);\n\twaitEdge->blockingNodeId = ParseIntField(result, rowIndex, 5);\n\twaitEdge->blockingTransactionNum = ParseIntField(result, rowIndex, 6);\n\twaitEdge->blockingTransactionStamp = ParseTimestampTzField(result, rowIndex, 7);\n\twaitEdge->isBlockingXactWaiting = ParseBoolField(result, rowIndex, 8);\n}\n\n\n/*\n * AddWaitEdgeFromBlockedProcessResult adds an edge to the wait graph that\n * is read from a PGresult.\n */\nstatic void\nAddWaitEdgeFromBlockedProcessResult(WaitGraph *waitGraph, PGresult *result, int rowIndex)\n{\n\tWaitEdge *waitEdge = AllocWaitEdge(waitGraph);\n\n\twaitEdge->waitingGPid = ParseIntField(result, rowIndex, 0);\n\twaitEdge->waitingPid = ParseIntField(result, rowIndex, 1);\n\twaitEdge->waitingNodeId = ParseIntField(result, rowIndex, 2);\n\twaitEdge->waitingTransactionNum = ParseIntField(result, rowIndex, 3);\n\twaitEdge->waitingTransactionStamp = ParseTimestampTzField(result, rowIndex, 4);\n\twaitEdge->blockingGPid = ParseIntField(result, rowIndex, 5);\n\twaitEdge->blockingPid = ParseIntField(result, rowIndex, 6);\n\twaitEdge->blockingNodeId = ParseIntField(result, rowIndex, 7);\n\twaitEdge->blockingTransactionNum = ParseIntField(result, rowIndex, 8);\n\twaitEdge->blockingTransactionStamp = ParseTimestampTzField(result, rowIndex, 9);\n\twaitEdge->isBlockingXactWaiting = ParseBoolField(result, rowIndex, 10);\n}\n\n\n/*\n * ParseIntField parses a int64 from a remote result or returns 0 if the\n * result is NULL.\n */\nint64\nParseIntField(PGresult *result, int rowIndex, int colIndex)\n{\n\tif (PQgetisnull(result, rowIndex, colIndex))\n\t{\n\t\treturn 0;\n\t}\n\n\tchar *resultString = PQgetvalue(result, rowIndex, colIndex);\n\n\treturn strtou64(resultString, NULL, 10);\n}\n\n\n/*\n * ParseBoolField parses a bool from a remote result or returns false if the\n * result is NULL.\n */\nbool\nParseBoolField(PGresult *result, int rowIndex, int colIndex)\n{\n\tif (PQgetisnull(result, rowIndex, colIndex))\n\t{\n\t\treturn false;\n\t}\n\n\tchar *resultString = PQgetvalue(result, rowIndex, colIndex);\n\tif (strlen(resultString) != 1)\n\t{\n\t\treturn false;\n\t}\n\n\treturn resultString[0] == 't';\n}\n\n\n/*\n * ParseTimestampTzField parses a timestamptz from a remote result or returns\n * 0 if the result is NULL.\n */\nTimestampTz\nParseTimestampTzField(PGresult *result, int rowIndex, int colIndex)\n{\n\tif (PQgetisnull(result, rowIndex, colIndex))\n\t{\n\t\treturn DT_NOBEGIN;\n\t}\n\n\tchar *resultString = PQgetvalue(result, rowIndex, colIndex);\n\tDatum resultStringDatum = CStringGetDatum(resultString);\n\tDatum timestampDatum = DirectFunctionCall3(timestamptz_in, resultStringDatum, 0, -1);\n\n\treturn DatumGetTimestampTz(timestampDatum);\n}\n\n\n/*\n * dump_local_wait_edges returns wait edges for distributed transactions\n * running on the node on which it is called, which originate from the source node.\n */\nDatum\ndump_local_wait_edges(PG_FUNCTION_ARGS)\n{\n\tbool onlyDistributedTx = true;\n\n\tWaitGraph *waitGraph = BuildLocalWaitGraph(onlyDistributedTx);\n\tReturnWaitGraph(waitGraph, fcinfo);\n\n\treturn (Datum) 0;\n}\n\n\n/*\n * citus_internal_local_blocked_processes returns global wait edges\n * including all processes running on the node.\n */\nDatum\ncitus_internal_local_blocked_processes(PG_FUNCTION_ARGS)\n{\n\tbool onlyDistributedTx = false;\n\n\tWaitGraph *waitGraph = BuildLocalWaitGraph(onlyDistributedTx);\n\tReturnBlockedProcessGraph(waitGraph, fcinfo);\n\n\treturn (Datum) 0;\n}\n\n\n/*\n * ReturnWaitGraph returns a wait graph for a set returning function.\n */\nstatic void\nReturnWaitGraph(WaitGraph *waitGraph, FunctionCallInfo fcinfo)\n{\n\tTupleDesc tupleDesc;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDesc);\n\n\t/*\n\t * Columns:\n\t * 00: waiting_pid\n\t * 01: waiting_node_id\n\t * 02: waiting_transaction_num\n\t * 03: waiting_transaction_stamp\n\t * 04: blocking_pid\n\t * 05: blocking__node_id\n\t * 06: blocking_transaction_num\n\t * 07: blocking_transaction_stamp\n\t * 08: blocking_transaction_waiting\n\t */\n\tfor (size_t curEdgeNum = 0; curEdgeNum < waitGraph->edgeCount; curEdgeNum++)\n\t{\n\t\tDatum values[9];\n\t\tbool nulls[9];\n\t\tWaitEdge *curEdge = &waitGraph->edges[curEdgeNum];\n\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(nulls, 0, sizeof(nulls));\n\n\t\tvalues[0] = Int32GetDatum(curEdge->waitingPid);\n\t\tvalues[1] = Int32GetDatum(curEdge->waitingNodeId);\n\t\tif (curEdge->waitingTransactionNum != 0)\n\t\t{\n\t\t\tvalues[2] = Int64GetDatum(curEdge->waitingTransactionNum);\n\t\t\tvalues[3] = TimestampTzGetDatum(curEdge->waitingTransactionStamp);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnulls[2] = true;\n\t\t\tnulls[3] = true;\n\t\t}\n\n\t\tvalues[4] = Int32GetDatum(curEdge->blockingPid);\n\t\tvalues[5] = Int32GetDatum(curEdge->blockingNodeId);\n\t\tif (curEdge->blockingTransactionNum != 0)\n\t\t{\n\t\t\tvalues[6] = Int64GetDatum(curEdge->blockingTransactionNum);\n\t\t\tvalues[7] = TimestampTzGetDatum(curEdge->blockingTransactionStamp);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnulls[6] = true;\n\t\t\tnulls[7] = true;\n\t\t}\n\t\tvalues[8] = BoolGetDatum(curEdge->isBlockingXactWaiting);\n\n\t\ttuplestore_putvalues(tupleStore, tupleDesc, values, nulls);\n\t}\n}\n\n\n/*\n * ReturnBlockedProcessGraph returns a wait graph for a set returning function.\n */\nstatic void\nReturnBlockedProcessGraph(WaitGraph *waitGraph, FunctionCallInfo fcinfo)\n{\n\tTupleDesc tupleDesc;\n\tTuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDesc);\n\n\t/*\n\t * Columns:\n\t * 00: waiting_global_pid\n\t * 01: waiting_pid\n\t * 02: waiting_node_id\n\t * 03: waiting_transaction_num\n\t * 04: waiting_transaction_stamp\n\t * 05: blocking_global_pid\n\t * 06: blocking_pid\n\t * 07: blocking__node_id\n\t * 08: blocking_transaction_num\n\t * 09: blocking_transaction_stamp\n\t * 10: blocking_transaction_waiting\n\t */\n\tfor (size_t curEdgeNum = 0; curEdgeNum < waitGraph->edgeCount; curEdgeNum++)\n\t{\n\t\tDatum values[11];\n\t\tbool nulls[11];\n\t\tWaitEdge *curEdge = &waitGraph->edges[curEdgeNum];\n\n\t\tmemset(values, 0, sizeof(values));\n\t\tmemset(nulls, 0, sizeof(nulls));\n\n\t\tvalues[0] = UInt64GetDatum(curEdge->waitingGPid);\n\t\tvalues[1] = Int32GetDatum(curEdge->waitingPid);\n\t\tvalues[2] = Int32GetDatum(curEdge->waitingNodeId);\n\t\tif (curEdge->waitingTransactionNum != 0)\n\t\t{\n\t\t\tvalues[3] = Int64GetDatum(curEdge->waitingTransactionNum);\n\t\t\tvalues[4] = TimestampTzGetDatum(curEdge->waitingTransactionStamp);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnulls[3] = true;\n\t\t\tnulls[4] = true;\n\t\t}\n\n\t\tvalues[5] = UInt64GetDatum(curEdge->blockingGPid);\n\t\tvalues[6] = Int32GetDatum(curEdge->blockingPid);\n\t\tvalues[7] = Int32GetDatum(curEdge->blockingNodeId);\n\t\tif (curEdge->blockingTransactionNum != 0)\n\t\t{\n\t\t\tvalues[8] = Int64GetDatum(curEdge->blockingTransactionNum);\n\t\t\tvalues[9] = TimestampTzGetDatum(curEdge->blockingTransactionStamp);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tnulls[8] = true;\n\t\t\tnulls[9] = true;\n\t\t}\n\t\tvalues[10] = BoolGetDatum(curEdge->isBlockingXactWaiting);\n\n\t\ttuplestore_putvalues(tupleStore, tupleDesc, values, nulls);\n\t}\n}\n\n\n/*\n * BuildLocalWaitGraph builds a wait graph for distributed transactions\n * that originate from the local node.\n *\n * If onlyDistributedTx is true, we only return distributed transactions\n * (e.g., AssignDistributedTransaction() or assign_distributed_transactions())\n * has been called for the process. Distributed deadlock detection only\n * interested in these processes.\n */\nstatic WaitGraph *\nBuildLocalWaitGraph(bool onlyDistributedTx)\n{\n\tPROCStack remaining;\n\tint totalProcs = TotalProcCount();\n\n\t/*\n\t * Try hard to avoid allocations while holding lock. Thus we pre-allocate\n\t * space for locks in large batches - for common scenarios this should be\n\t * more than enough space to build the list of wait edges without a single\n\t * allocation.\n\t */\n\tWaitGraph *waitGraph = (WaitGraph *) palloc0(sizeof(WaitGraph));\n\twaitGraph->localNodeId = GetLocalGroupId();\n\twaitGraph->allocatedSize = totalProcs * 3;\n\twaitGraph->edgeCount = 0;\n\twaitGraph->edges = (WaitEdge *) palloc(waitGraph->allocatedSize * sizeof(WaitEdge));\n\n\tremaining.procs = (PGPROC **) palloc(sizeof(PGPROC *) * totalProcs);\n\tremaining.procAdded = (bool *) palloc0(sizeof(bool *) * totalProcs);\n\tremaining.procCount = 0;\n\n\tLockLockData();\n\n\t/*\n\t * Build lock-graph.  We do so by first finding all procs which we are\n\t * interested in (in a distributed transaction, and blocked).  Once\n\t * those are collected, do depth first search over all procs blocking\n\t * those.\n\t */\n\n\t/* build list of starting procs */\n\tfor (int curBackend = 0; curBackend < totalProcs; curBackend++)\n\t{\n\t\tPGPROC *currentProc = GetPGProcByNumber(curBackend);\n\t\tBackendData currentBackendData;\n\n\t\tif (currentProc->pid == 0)\n\t\t{\n\t\t\t/* skip if the PGPROC slot is unused */\n\t\t\tcontinue;\n\t\t}\n\n\t\tGetBackendDataForProc(currentProc, &currentBackendData);\n\t\tif (!currentBackendData.activeBackend)\n\t\t{\n\t\t\t/*\n\t\t\t * Skip if the PGPROC slot is unused. We should normally use\n\t\t\t * IsBackendPid() to be able to skip reliably all the exited\n\t\t\t * processes. However, that is a costly operation. Instead, we\n\t\t\t * keep track of activeBackend in Citus code.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Only start searching from distributed transactions, since we only\n\t\t * care about distributed transactions for the purpose of distributed\n\t\t * deadlock detection.\n\t\t */\n\t\tif (onlyDistributedTx &&\n\t\t\t!IsInDistributedTransaction(&currentBackendData))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* skip if the process is not blocked */\n\t\tif (!IsProcessWaitingForLock(currentProc))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* skip if the process is blocked for relation extension */\n\t\tif (IsProcessWaitingForSafeOperations(currentProc))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tAddProcToVisit(&remaining, currentProc);\n\t}\n\n\twhile (remaining.procCount > 0)\n\t{\n\t\tPGPROC *waitingProc = remaining.procs[--remaining.procCount];\n\n\t\t/* only blocked processes result in wait edges */\n\t\tif (!IsProcessWaitingForLock(waitingProc))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* skip if the process is blocked for relation extension */\n\t\tif (IsProcessWaitingForSafeOperations(waitingProc))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Record an edge for everyone already holding the lock in a\n\t\t * conflicting manner (\"hard edges\" in postgres parlance).\n\t\t */\n\t\tAddEdgesForLockWaits(waitGraph, waitingProc, &remaining);\n\n\t\t/*\n\t\t * Record an edge for everyone in front of us in the wait-queue\n\t\t * for the lock (\"soft edges\" in postgres parlance).\n\t\t */\n\t\tAddEdgesForWaitQueue(waitGraph, waitingProc, &remaining);\n\t}\n\n\tUnlockLockData();\n\n\treturn waitGraph;\n}\n\n\n/*\n * IsProcessWaitingForSafeOperations returns true if the given PROC\n * waiting on relation extension locks, page locks or speculative locks.\n *\n * The function also returns true if the waiting process is an autovacuum\n * process given that autovacuum cannot contribute to any distributed\n * deadlocks.\n *\n * In general for the purpose of distributed deadlock detection, we should\n * skip if the process blocked on the locks that may not be part of deadlocks.\n * Those locks are held for a short duration while the relation or the index\n * is actually extended on the disk and released as soon as the extension is\n * done, even before the execution of the command that triggered the extension\n * finishes. Thus, recording such waits on our lock graphs could yield detecting\n * wrong distributed deadlocks.\n */\nstatic bool\nIsProcessWaitingForSafeOperations(PGPROC *proc)\n{\n\tif (proc->waitStatus != PROC_WAIT_STATUS_WAITING)\n\t{\n\t\treturn false;\n\t}\n\n\tif (proc->statusFlags & PROC_IS_AUTOVACUUM)\n\t{\n\t\treturn true;\n\t}\n\n\tPROCLOCK *waitProcLock = proc->waitProcLock;\n\tLOCK *waitLock = waitProcLock->tag.myLock;\n\n\treturn waitLock->tag.locktag_type == LOCKTAG_RELATION_EXTEND ||\n\t\t   waitLock->tag.locktag_type == LOCKTAG_PAGE ||\n\t\t   waitLock->tag.locktag_type == LOCKTAG_SPECULATIVE_TOKEN;\n}\n\n\n/*\n * LockLockData takes locks the shared lock data structure, which prevents\n * concurrent lock acquisitions/releases.\n *\n * The function also acquires lock on the backend shared memory to prevent\n * new backends to start.\n */\nstatic void\nLockLockData(void)\n{\n\tLockBackendSharedMemory(LW_SHARED);\n\n\tfor (int partitionNum = 0; partitionNum < NUM_LOCK_PARTITIONS; partitionNum++)\n\t{\n\t\tLWLockAcquire(LockHashPartitionLockByIndex(partitionNum), LW_SHARED);\n\t}\n}\n\n\n/*\n * UnlockLockData unlocks the locks on the shared lock data structure in reverse\n * order since LWLockRelease searches the given lock from the end of the\n * held_lwlocks array.\n *\n * The function also releases the shared memory lock to allow new backends to\n * start.\n */\nstatic void\nUnlockLockData(void)\n{\n\tfor (int partitionNum = NUM_LOCK_PARTITIONS - 1; partitionNum >= 0; partitionNum--)\n\t{\n\t\tLWLockRelease(LockHashPartitionLockByIndex(partitionNum));\n\t}\n\n\tUnlockBackendSharedMemory();\n}\n\n\n/*\n * AddEdgesForLockWaits adds an edge to the wait graph for every granted lock\n * that waitingProc is waiting for.\n *\n * This function iterates over the procLocks data structure in shared memory,\n * which also contains entries for locks which have not been granted yet, but\n * it does not reflect the order of the wait queue. We therefore handle the\n * wait queue separately.\n *\n * We have separate blocks for PG16 and <PG16 because SHM_QUEUE is completely\n * removed from PG16\n */\n\n/*\n * AddEdgesForWaitQueue adds an edge to the wait graph for processes in front of\n * waitingProc in the wait queue that are trying to acquire a conflicting lock.\n *\n * We have separate blocks for PG16 and <PG16 because SHM_QUEUE is completely\n * removed from PG16\n */\nstatic void\nAddEdgesForLockWaits(WaitGraph *waitGraph, PGPROC *waitingProc, PROCStack *remaining)\n{\n\t/* the lock for which this process is waiting */\n\tLOCK *waitLock = waitingProc->waitLock;\n\n\t/* determine the conflict mask for the lock level used by the process */\n\tLockMethod lockMethodTable = GetLocksMethodTable(waitLock);\n\tint conflictMask = lockMethodTable->conflictTab[waitingProc->waitLockMode];\n\n\t/* iterate through the queue of processes holding the lock */\n\tdlist_head *procLocks = &waitLock->procLocks;\n\n\tdlist_iter iter;\n\tdlist_foreach(iter, procLocks)\n\t{\n\t\tPROCLOCK *procLock = dlist_container(PROCLOCK, lockLink, iter.cur);\n\t\tPGPROC *currentProc = procLock->tag.myProc;\n\n\t\t/*\n\t\t * Skip processes from the same lock group, processes that don't conflict,\n\t\t * and processes that are waiting on safe operations.\n\t\t */\n\t\tif (!IsSameLockGroup(waitingProc, currentProc) &&\n\t\t\tIsConflictingLockMask(procLock->holdMask, conflictMask) &&\n\t\t\t!IsProcessWaitingForSafeOperations(currentProc))\n\t\t{\n\t\t\tAddWaitEdge(waitGraph, waitingProc, currentProc, remaining);\n\t\t}\n\t}\n}\n\n\nstatic void\nAddEdgesForWaitQueue(WaitGraph *waitGraph, PGPROC *waitingProc, PROCStack *remaining)\n{\n\t/* the lock for which this process is waiting */\n\tLOCK *waitLock = waitingProc->waitLock;\n\n\t/* determine the conflict mask for the lock level used by the process */\n\tLockMethod lockMethodTable = GetLocksMethodTable(waitLock);\n\tint conflictMask = lockMethodTable->conflictTab[waitingProc->waitLockMode];\n\n\t/* iterate through the wait queue */\n\tdclist_head *waitQueue = &waitLock->waitProcs;\n\n\tdlist_iter iter;\n\tdclist_foreach(iter, waitQueue)\n\t{\n\t\tPGPROC *currentProc = dlist_container(PGPROC, links, iter.cur);\n\n\t\tif (currentProc == waitingProc)\n\t\t{\n\t\t\t/*\n\t\t\t * Iterate through the queue from the start until we encounter waitingProc,\n\t\t\t * since we only care about processes in front of waitingProc in the queue.\n\t\t\t */\n\t\t\tbreak;\n\t\t}\n\n\t\tint awaitMask = LOCKBIT_ON(currentProc->waitLockMode);\n\n\t\t/*\n\t\t * Skip processes from the same lock group, processes that don't conflict,\n\t\t * and processes that are waiting on safe operations.\n\t\t */\n\t\tif (!IsSameLockGroup(waitingProc, currentProc) &&\n\t\t\tIsConflictingLockMask(awaitMask, conflictMask) &&\n\t\t\t!IsProcessWaitingForSafeOperations(currentProc))\n\t\t{\n\t\t\tAddWaitEdge(waitGraph, waitingProc, currentProc, remaining);\n\t\t}\n\n\t\tcurrentProc = (PGPROC *) currentProc->links.next;\n\t}\n}\n\n\n/*\n * AddWaitEdge adds a new wait edge to a wait graph. The nodes in the graph are\n * transactions and an edge indicates the \"waiting\" process is blocked on a lock\n * held by the \"blocking\" process.\n *\n * If the blocking process is itself waiting then it is added to the remaining\n * stack.\n */\nstatic void\nAddWaitEdge(WaitGraph *waitGraph, PGPROC *waitingProc, PGPROC *blockingProc,\n\t\t\tPROCStack *remaining)\n{\n\tWaitEdge *curEdge = AllocWaitEdge(waitGraph);\n\tBackendData waitingBackendData;\n\tBackendData blockingBackendData;\n\n\tGetBackendDataForProc(waitingProc, &waitingBackendData);\n\tGetBackendDataForProc(blockingProc, &blockingBackendData);\n\n\tcurEdge->isBlockingXactWaiting =\n\t\tIsProcessWaitingForLock(blockingProc) &&\n\t\t!IsProcessWaitingForSafeOperations(blockingProc);\n\tif (curEdge->isBlockingXactWaiting)\n\t{\n\t\tAddProcToVisit(remaining, blockingProc);\n\t}\n\n\tcurEdge->waitingPid = waitingProc->pid;\n\tcurEdge->waitingGPid = waitingBackendData.globalPID;\n\n\tif (IsInDistributedTransaction(&waitingBackendData))\n\t{\n\t\tDistributedTransactionId *waitingTransactionId =\n\t\t\t&waitingBackendData.transactionId;\n\n\t\tcurEdge->waitingNodeId = waitingTransactionId->initiatorNodeIdentifier;\n\t\tcurEdge->waitingTransactionNum = waitingTransactionId->transactionNumber;\n\t\tcurEdge->waitingTransactionStamp = waitingTransactionId->timestamp;\n\t}\n\telse\n\t{\n\t\tcurEdge->waitingNodeId = waitGraph->localNodeId;\n\t\tcurEdge->waitingTransactionNum = 0;\n\t\tcurEdge->waitingTransactionStamp = 0;\n\t}\n\n\tcurEdge->blockingPid = blockingProc->pid;\n\tcurEdge->blockingGPid = blockingBackendData.globalPID;\n\n\tif (IsInDistributedTransaction(&blockingBackendData))\n\t{\n\t\tDistributedTransactionId *blockingTransactionId =\n\t\t\t&blockingBackendData.transactionId;\n\n\t\tcurEdge->blockingNodeId = blockingTransactionId->initiatorNodeIdentifier;\n\t\tcurEdge->blockingTransactionNum = blockingTransactionId->transactionNumber;\n\t\tcurEdge->blockingTransactionStamp = blockingTransactionId->timestamp;\n\t}\n\telse\n\t{\n\t\tcurEdge->blockingNodeId = waitGraph->localNodeId;\n\t\tcurEdge->blockingTransactionNum = 0;\n\t\tcurEdge->blockingTransactionStamp = 0;\n\t}\n}\n\n\n/*\n * AllocWaitEdge allocates a wait edge as part of the given wait graph.\n * If the wait graph has insufficient space its size is doubled using\n * repalloc.\n */\nstatic WaitEdge *\nAllocWaitEdge(WaitGraph *waitGraph)\n{\n\t/* ensure space for new edge */\n\tif (waitGraph->allocatedSize == waitGraph->edgeCount)\n\t{\n\t\twaitGraph->allocatedSize *= 2;\n\t\twaitGraph->edges = (WaitEdge *)\n\t\t\t\t\t\t   repalloc(waitGraph->edges, sizeof(WaitEdge) *\n\t\t\t\t\t\t\t\t\twaitGraph->allocatedSize);\n\t}\n\n\treturn &waitGraph->edges[waitGraph->edgeCount++];\n}\n\n\n/*\n * AddProcToVisit adds a process to the stack of processes to visit\n * in the depth-first search, unless it was already added.\n */\nstatic void\nAddProcToVisit(PROCStack *remaining, PGPROC *proc)\n{\n\tif (remaining->procAdded[getProcNo_compat(proc)])\n\t{\n\t\treturn;\n\t}\n\n\tAssert(remaining->procCount < TotalProcCount());\n\n\tremaining->procs[remaining->procCount++] = proc;\n\tremaining->procAdded[getProcNo_compat(proc)] = true;\n}\n\n\n/*\n * IsProcessWaitingForLock returns whether a given process is waiting for a lock.\n */\nbool\nIsProcessWaitingForLock(PGPROC *proc)\n{\n\treturn proc->waitStatus == PROC_WAIT_STATUS_WAITING;\n}\n\n\n/*\n * IsSameLockGroup returns whether two processes are part of the same lock group,\n * meaning they are either the same process, or have the same lock group leader.\n */\nstatic bool\nIsSameLockGroup(PGPROC *leftProc, PGPROC *rightProc)\n{\n\treturn leftProc == rightProc ||\n\t\t   (leftProc->lockGroupLeader != NULL &&\n\t\t\tleftProc->lockGroupLeader == rightProc->lockGroupLeader);\n}\n\n\n/*\n * IsConflictingLockMask returns whether the given conflict mask conflicts with the\n * holdMask.\n *\n * holdMask is a bitmask with the i-th bit turned on if a lock mode i is held.\n *\n * conflictMask is a bitmask with the j-th bit turned on if it conflicts with\n * lock mode i.\n */\nstatic bool\nIsConflictingLockMask(int holdMask, int conflictMask)\n{\n\treturn (holdMask & conflictMask) != 0;\n}\n\n\n/*\n * IsInDistributedTransaction returns whether the given backend is in a\n * distributed transaction.\n */\nbool\nIsInDistributedTransaction(BackendData *backendData)\n{\n\treturn backendData->transactionId.transactionNumber != 0;\n}\n"
  },
  {
    "path": "src/backend/distributed/transaction/relation_access_tracking.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * relation_access_tracking.c\n *\n *   Transaction access tracking for Citus. The functions in this file\n *   are intended to track the relation accesses within a transaction. The\n *   logic here is mostly useful when a reference table is referred by\n *   a distributed table via a foreign key. Whenever such a pair of tables\n *   are accessed inside a transaction, Citus should detect and act\n *   accordingly.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/xact.h\"\n#include \"common/hashfn.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"    /* for ALLOCSET_DEFAULT_MINSIZE, _INITSIZE, _MAXSIZE */\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n\n\n/* Config variables managed via guc.c */\nbool EnforceForeignKeyRestrictions = true;\n\n#define PARALLEL_MODE_FLAG_OFFSET 3\n\n/* simply set parallel bits as defined below for select, dml and ddl */\n#define PARALLEL_ACCESS_MASK (int) (0 | \\\n\t\t\t\t\t\t\t\t\t(1 << (PLACEMENT_ACCESS_SELECT + \\\n\t\t\t\t\t\t\t\t\t\t   PARALLEL_MODE_FLAG_OFFSET)) | \\\n\t\t\t\t\t\t\t\t\t(1 << (PLACEMENT_ACCESS_DML + \\\n\t\t\t\t\t\t\t\t\t\t   PARALLEL_MODE_FLAG_OFFSET)) | \\\n\t\t\t\t\t\t\t\t\t(1 << (PLACEMENT_ACCESS_DDL + \\\n\t\t\t\t\t\t\t\t\t\t   PARALLEL_MODE_FLAG_OFFSET)))\n\nMemoryContext RelationAcessContext = NULL;\n\n\n/*\n * Hash table mapping relations to the\n *      (relationId) = (relationAccessType and relationAccessMode)\n *\n * RelationAccessHash is used to keep track of relation accesses types (e.g., select,\n * dml or ddl) along with access modes (e.g., no access, sequential access or\n * parallel access).\n *\n * We keep an integer per relation and use some of the bits to identify the access types\n * and access modes.\n *\n * We store the access types in the first 3 bits:\n *  - 0th bit is set for SELECT accesses to a relation\n *  - 1st bit is set for DML accesses to a relation\n *  - 2nd bit is set for DDL accesses to a relation\n *\n * and, access modes in the next 3 bits:\n *  - 3rd bit is set for PARALLEL SELECT accesses to a relation\n *  - 4th bit is set for PARALLEL DML accesses to a relation\n *  - 5th bit is set for PARALLEL DDL accesses to a relation\n *\n */\ntypedef struct RelationAccessHashKey\n{\n\tOid relationId;\n} RelationAccessHashKey;\n\ntypedef struct RelationAccessHashEntry\n{\n\tRelationAccessHashKey key;\n\n\tint relationAccessMode;\n} RelationAccessHashEntry;\n\nstatic HTAB *RelationAccessHash;\n\n/* functions related to access recording */\nstatic void AllocateRelationAccessHash(void);\nstatic void RecordRelationAccessBase(Oid relationId, ShardPlacementAccessType accessType);\nstatic void RecordPlacementAccessToCache(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t ShardPlacementAccessType accessType);\nstatic void RecordRelationParallelSelectAccessForTask(Task *task);\nstatic void RecordRelationParallelModifyAccessForTask(Task *task);\nstatic void RecordRelationParallelDDLAccessForTask(Task *task);\nstatic RelationAccessMode GetRelationAccessMode(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\tShardPlacementAccessType accessType);\nstatic void RecordParallelRelationAccess(Oid relationId, ShardPlacementAccessType\n\t\t\t\t\t\t\t\t\t\t placementAccess);\nstatic void RecordParallelRelationAccessToCache(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\tShardPlacementAccessType placementAccess);\n\n/* functions related to access conflict checks */\nstatic char * PlacementAccessTypeToText(ShardPlacementAccessType accessType);\nstatic void CheckConflictingRelationAccesses(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t ShardPlacementAccessType accessType);\nstatic bool HoldsConflictingLockWithReferencingRelations(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t ShardPlacementAccessType\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t placementAccess,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t Oid *conflictingRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t ShardPlacementAccessType *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t conflictingAccessMode);\nstatic void CheckConflictingParallelRelationAccesses(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t ShardPlacementAccessType\n\t\t\t\t\t\t\t\t\t\t\t\t\t accessType);\nstatic bool HoldsConflictingLockWithReferencedRelations(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tShardPlacementAccessType\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tplacementAccess,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tOid *conflictingRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tShardPlacementAccessType *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tconflictingAccessMode);\n\n\n/*\n * InitRelationAccessHash performs initialization of the\n * infrastructure in this file at backend start.\n */\nvoid\nInitRelationAccessHash(void)\n{\n\t/* allocate (relationId) = [relationAccessMode] hash */\n\tAllocateRelationAccessHash();\n}\n\n\n/*\n * Empty RelationAccessHash, without destroying the hash table itself.\n */\nvoid\nResetRelationAccessHash()\n{\n\thash_delete_all(RelationAccessHash);\n}\n\n\n/*\n * Allocate RelationAccessHash.\n */\nstatic void\nAllocateRelationAccessHash(void)\n{\n\t/*\n\t * Create a single context for relation access related memory\n\t * management. Doing so, instead of allocating in TopMemoryContext, makes\n\t * it easier to associate used memory.\n\t */\n\tRelationAcessContext = AllocSetContextCreateInternal(TopMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Relation Access Context\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_MINSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_INITSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t ALLOCSET_DEFAULT_MAXSIZE);\n\n\tHASHCTL info;\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(RelationAccessHashKey);\n\tinfo.entrysize = sizeof(RelationAccessHashEntry);\n\tinfo.hash = tag_hash;\n\tinfo.hcxt = RelationAcessContext;\n\tuint32 hashFlags = (HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);\n\n\tRelationAccessHash = hash_create(\"citus relation access cache (relationid)\",\n\t\t\t\t\t\t\t\t\t 8, &info, hashFlags);\n}\n\n\n/*\n * RecordRelationAccessIfNonDistTable marks the relation accessed if it is a\n * reference relation.\n *\n * The function is a wrapper around RecordRelationAccessBase().\n */\nvoid\nRecordRelationAccessIfNonDistTable(Oid relationId, ShardPlacementAccessType accessType)\n{\n\tif (!ShouldRecordRelationAccess())\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * We keep track of relation accesses for the purposes of foreign keys to\n\t * reference tables. So, other distributed tables are not relevant for now.\n\t * Additionally, partitioned tables with lots of partitions might require\n\t * recursively calling RecordRelationAccessBase(), so becareful about\n\t * removing this check.\n\t */\n\tif (IsCitusTableType(relationId, DISTRIBUTED_TABLE))\n\t{\n\t\treturn;\n\t}\n\n\tRecordRelationAccessBase(relationId, accessType);\n}\n\n\n/*\n * PlacementAccessTypeToText converts ShardPlacementAccessType to\n * text representation.\n */\nstatic char *\nPlacementAccessTypeToText(ShardPlacementAccessType accessType)\n{\n\tswitch (accessType)\n\t{\n\t\tcase PLACEMENT_ACCESS_SELECT:\n\t\t{\n\t\t\treturn \"SELECT\";\n\t\t\tbreak;\n\t\t}\n\n\t\tcase PLACEMENT_ACCESS_DML:\n\t\t{\n\t\t\treturn \"DML\";\n\t\t}\n\n\t\tcase PLACEMENT_ACCESS_DDL:\n\t\t{\n\t\t\treturn \"DDL\";\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn \"None\";\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n/*\n * RecordRelationAccessBase associates the access to the distributed relation. The\n * function takes partitioned relations into account as well.\n *\n * We implemented this function to prevent accessing placement metadata during\n * recursive calls of the function itself (e.g., avoid\n * RecordRelationAccessBase()).\n */\nstatic void\nRecordRelationAccessBase(Oid relationId, ShardPlacementAccessType accessType)\n{\n\tif (IsCitusTableType(relationId, REFERENCE_TABLE))\n\t{\n\t\t/*\n\t\t * We don't support partitioned reference tables.\n\t\t */\n\t\tAssert(!PartitionedTable(relationId) && !PartitionTable(relationId));\n\t}\n\n\t/* make sure that this is not a conflicting access */\n\tCheckConflictingRelationAccesses(relationId, accessType);\n\n\t/* always record the relation that is being considered */\n\tRecordPlacementAccessToCache(relationId, accessType);\n}\n\n\n/*\n * RecordPlacementAccessToCache is a utility function which saves the given\n * relation id's access to the RelationAccessHash.\n */\nstatic void\nRecordPlacementAccessToCache(Oid relationId, ShardPlacementAccessType accessType)\n{\n\tRelationAccessHashKey hashKey;\n\tbool found = false;\n\n\thashKey.relationId = relationId;\n\n\tRelationAccessHashEntry *hashEntry = hash_search(RelationAccessHash, &hashKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t HASH_ENTER, &found);\n\tif (!found)\n\t{\n\t\thashEntry->relationAccessMode = 0;\n\t}\n\n\t/* set the bit representing the access type */\n\thashEntry->relationAccessMode |= (1 << (accessType));\n}\n\n\n/*\n * RecordParallelRelationAccessForTaskList gets a task list and records\n * the necessary parallel relation accesses for the task list.\n *\n * This function is used to enforce foreign keys from distributed\n * tables to reference tables.\n */\nvoid\nRecordParallelRelationAccessForTaskList(List *taskList)\n{\n\tif (MultiShardConnectionType == SEQUENTIAL_CONNECTION)\n\t{\n\t\t/* sequential mode prevents parallel access */\n\t\treturn;\n\t}\n\n\tif (list_length(taskList) < 2)\n\t{\n\t\t/* single shard task doesn't mean parallel access in our definition */\n\t\treturn;\n\t}\n\n\t/*\n\t * Since all the tasks in a task list is expected to operate on the same\n\t * distributed table(s), we only need to process the first task.\n\t */\n\tTask *firstTask = linitial(taskList);\n\n\tif (firstTask->taskType == READ_TASK)\n\t{\n\t\tRecordRelationParallelSelectAccessForTask(firstTask);\n\t}\n\telse if (firstTask->taskType == MODIFY_TASK)\n\t{\n\t\tif (firstTask->rowValuesLists != NIL)\n\t\t{\n\t\t\t/*\n\t\t\t * We always run multi-row INSERTs in a sequential\n\t\t\t * mode (hard-coded). Thus, we do not mark as parallel\n\t\t\t * access even if the prerequisites hold.\n\t\t\t */\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * We prefer to mark with all remaining multi-shard modifications\n\t\t\t * with both modify and select accesses.\n\t\t\t */\n\t\t\tRecordRelationParallelModifyAccessForTask(firstTask);\n\t\t\tRecordRelationParallelSelectAccessForTask(firstTask);\n\t\t}\n\t}\n\telse\n\t{\n\t\tRecordRelationParallelDDLAccessForTask(firstTask);\n\t}\n}\n\n\n/*\n * RecordRelationParallelSelectAccessForTask goes over all the relations\n * in the relationShardList and records the select access per each table.\n */\nstatic void\nRecordRelationParallelSelectAccessForTask(Task *task)\n{\n\tOid lastRelationId = InvalidOid;\n\n\t/* no point in recoding accesses in non-transaction blocks, skip the loop */\n\tif (!ShouldRecordRelationAccess())\n\t{\n\t\treturn;\n\t}\n\n\tList *relationShardList = task->relationShardList;\n\n\tRelationShard *relationShard = NULL;\n\tforeach_declared_ptr(relationShard, relationShardList)\n\t{\n\t\tOid currentRelationId = relationShard->relationId;\n\n\t\t/*\n\t\t * An optimization, skip going to hash table if we've already\n\t\t * recorded the relation.\n\t\t */\n\t\tif (currentRelationId == lastRelationId)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tRecordParallelSelectAccess(currentRelationId);\n\n\t\tlastRelationId = currentRelationId;\n\t}\n}\n\n\n/*\n * RecordRelationParallelModifyAccessForTask gets a task and records\n * the accesses. Note that the target relation is recorded with modify access\n * where as the subqueries inside the modify query is recorded with select\n * access.\n */\nstatic void\nRecordRelationParallelModifyAccessForTask(Task *task)\n{\n\tList *relationShardList = NULL;\n\tOid lastRelationId = InvalidOid;\n\n\t/* no point in recoding accesses in non-transaction blocks, skip the loop */\n\tif (!ShouldRecordRelationAccess())\n\t{\n\t\treturn;\n\t}\n\n\t/* anchor shard is always associated with modify access */\n\tRecordParallelModifyAccess(RelationIdForShard(task->anchorShardId));\n\n\tif (task->modifyWithSubquery)\n\t{\n\t\trelationShardList = task->relationShardList;\n\t\tRelationShard *relationShard = NULL;\n\t\tforeach_declared_ptr(relationShard, relationShardList)\n\t\t{\n\t\t\tOid currentRelationId = relationShard->relationId;\n\n\t\t\t/*\n\t\t\t * An optimization, skip going to hash table if we've already\n\t\t\t * recorded the relation.\n\t\t\t */\n\t\t\tif (currentRelationId == lastRelationId)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tRecordParallelSelectAccess(currentRelationId);\n\n\t\t\tlastRelationId = currentRelationId;\n\t\t}\n\t}\n}\n\n\n/*\n * RecordRelationParallelDDLAccessForTask marks all the relationShards\n * with parallel DDL access if exists. That case is valid for inter-shard\n * DDL commands such as foreign key creation. The function also records\n * the relation that anchorShardId belongs to.\n */\nstatic void\nRecordRelationParallelDDLAccessForTask(Task *task)\n{\n\tList *relationShardList = task->relationShardList;\n\tOid lastRelationId = InvalidOid;\n\n\tRelationShard *relationShard = NULL;\n\tforeach_declared_ptr(relationShard, relationShardList)\n\t{\n\t\tOid currentRelationId = relationShard->relationId;\n\n\t\t/*\n\t\t * An optimization, skip going to hash table if we've already\n\t\t * recorded the relation.\n\t\t */\n\t\tif (currentRelationId == lastRelationId)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tRecordParallelDDLAccess(currentRelationId);\n\t\tlastRelationId = currentRelationId;\n\t}\n\n\tif (task->anchorShardId != INVALID_SHARD_ID)\n\t{\n\t\tRecordParallelDDLAccess(RelationIdForShard(task->anchorShardId));\n\t}\n}\n\n\n/*\n * RecordParallelSelectAccess is a wrapper around RecordParallelRelationAccess()\n */\nvoid\nRecordParallelSelectAccess(Oid relationId)\n{\n\tRecordParallelRelationAccess(relationId, PLACEMENT_ACCESS_SELECT);\n}\n\n\n/*\n * RecordParallelModifyAccess is a wrapper around RecordParallelRelationAccess()\n */\nvoid\nRecordParallelModifyAccess(Oid relationId)\n{\n\tRecordParallelRelationAccess(relationId, PLACEMENT_ACCESS_DML);\n}\n\n\n/*\n * RecordParallelDDLAccess is a wrapper around RecordParallelRelationAccess()\n */\nvoid\nRecordParallelDDLAccess(Oid relationId)\n{\n\tRecordParallelRelationAccess(relationId, PLACEMENT_ACCESS_DDL);\n}\n\n\n/*\n * RecordParallelRelationAccess records the relation access mode as parallel\n * for the given access type (e.g., select, dml or ddl) in the RelationAccessHash.\n *\n * The function also takes partitions and partitioned tables into account.\n */\nstatic void\nRecordParallelRelationAccess(Oid relationId, ShardPlacementAccessType placementAccess)\n{\n\tif (!ShouldRecordRelationAccess())\n\t{\n\t\treturn;\n\t}\n\n\t/* act accordingly if it's a conflicting access */\n\tCheckConflictingParallelRelationAccesses(relationId, placementAccess);\n\n\t/*\n\t * CheckConflictingParallelRelationAccesses might switch to sequential\n\t * execution. If that's the case, no need to continue because the executor\n\t * would take the necessary actions to switch to sequential execution\n\t * immediately.\n\t */\n\tif (MultiShardConnectionType == SEQUENTIAL_CONNECTION)\n\t{\n\t\treturn;\n\t}\n\n\t/* If a relation is partitioned, record accesses to all of its partitions as well. */\n\tif (PartitionedTable(relationId))\n\t{\n\t\tList *partitionList = PartitionList(relationId);\n\n\t\tOid partitionOid = InvalidOid;\n\t\tforeach_declared_oid(partitionOid, partitionList)\n\t\t{\n\t\t\t/* recursively record all relation accesses of its partitions */\n\t\t\tRecordParallelRelationAccess(partitionOid, placementAccess);\n\t\t}\n\t}\n\telse if (PartitionTable(relationId))\n\t{\n\t\tOid parentOid = PartitionParentOid(relationId);\n\n\t\t/* only record the parent */\n\t\tRecordParallelRelationAccessToCache(parentOid, placementAccess);\n\t}\n\n\tRecordParallelRelationAccessToCache(relationId, placementAccess);\n}\n\n\n/*\n * RecordParallelRelationAccessToCache is a utility function which saves the given\n * relation id's access to the RelationAccessHash.\n */\nstatic void\nRecordParallelRelationAccessToCache(Oid relationId,\n\t\t\t\t\t\t\t\t\tShardPlacementAccessType placementAccess)\n{\n\tRelationAccessHashKey hashKey;\n\tbool found = false;\n\n\thashKey.relationId = relationId;\n\n\tRelationAccessHashEntry *hashEntry = hash_search(RelationAccessHash, &hashKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t HASH_ENTER, &found);\n\tif (!found)\n\t{\n\t\thashEntry->relationAccessMode = 0;\n\t}\n\n\t/* set the bit representing the access type */\n\thashEntry->relationAccessMode |= (1 << (placementAccess));\n\n\t/* set the bit representing access mode */\n\tint parallelRelationAccessBit = placementAccess + PARALLEL_MODE_FLAG_OFFSET;\n\thashEntry->relationAccessMode |= (1 << parallelRelationAccessBit);\n}\n\n\n/*\n * ParallelQueryExecutedInTransaction returns true if any parallel query\n * is executed in the current transaction.\n */\nbool\nParallelQueryExecutedInTransaction(void)\n{\n\tHASH_SEQ_STATUS status;\n\n\tif (!ShouldRecordRelationAccess() || RelationAccessHash == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\thash_seq_init(&status, RelationAccessHash);\n\n\tRelationAccessHashEntry *hashEntry = (RelationAccessHashEntry *) hash_seq_search(\n\t\t&status);\n\twhile (hashEntry != NULL)\n\t{\n\t\tint relationAccessMode = hashEntry->relationAccessMode;\n\t\tif ((relationAccessMode & PARALLEL_ACCESS_MASK))\n\t\t{\n\t\t\thash_seq_term(&status);\n\t\t\treturn true;\n\t\t}\n\n\t\thashEntry = (RelationAccessHashEntry *) hash_seq_search(&status);\n\t}\n\n\treturn false;\n}\n\n\n/*\n * GetRelationSelectAccessMode is a wrapper around GetRelationAccessMode.\n */\nRelationAccessMode\nGetRelationSelectAccessMode(Oid relationId)\n{\n\treturn GetRelationAccessMode(relationId, PLACEMENT_ACCESS_SELECT);\n}\n\n\n/*\n * GetRelationDMLAccessMode is a wrapper around GetRelationAccessMode.\n */\nRelationAccessMode\nGetRelationDMLAccessMode(Oid relationId)\n{\n\treturn GetRelationAccessMode(relationId, PLACEMENT_ACCESS_DML);\n}\n\n\n/*\n * GetRelationDDLAccessMode is a wrapper around GetRelationAccessMode.\n */\nRelationAccessMode\nGetRelationDDLAccessMode(Oid relationId)\n{\n\treturn GetRelationAccessMode(relationId, PLACEMENT_ACCESS_DDL);\n}\n\n\n/*\n * GetRelationAccessMode returns the relation access mode (e.g., none, sequential\n * or parallel) for the given access type (e.g., select, dml or ddl).\n */\nstatic RelationAccessMode\nGetRelationAccessMode(Oid relationId, ShardPlacementAccessType accessType)\n{\n\tRelationAccessHashKey hashKey;\n\tbool found = false;\n\tint parallelRelationAccessBit = accessType + PARALLEL_MODE_FLAG_OFFSET;\n\n\t/* no point in getting the mode when not inside a transaction block */\n\tif (!ShouldRecordRelationAccess())\n\t{\n\t\treturn RELATION_NOT_ACCESSED;\n\t}\n\n\thashKey.relationId = relationId;\n\n\tRelationAccessHashEntry *hashEntry = hash_search(RelationAccessHash, &hashKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t HASH_FIND, &found);\n\tif (!found)\n\t{\n\t\t/* relation not accessed at all */\n\t\treturn RELATION_NOT_ACCESSED;\n\t}\n\n\n\tint relationAcessMode = hashEntry->relationAccessMode;\n\tif (!(relationAcessMode & (1 << accessType)))\n\t{\n\t\t/* relation not accessed with the given access type */\n\t\treturn RELATION_NOT_ACCESSED;\n\t}\n\n\tif (relationAcessMode & (1 << parallelRelationAccessBit))\n\t{\n\t\treturn RELATION_PARALLEL_ACCESSED;\n\t}\n\telse\n\t{\n\t\treturn RELATION_REFERENCE_ACCESSED;\n\t}\n}\n\n\n/*\n * ShouldRecordRelationAccess returns true when we should keep track\n * of the relation accesses.\n *\n * In many cases, we'd only need IsMultiStatementTransaction(), however, for some\n * cases such as CTEs, where Citus uses the same connections across multiple queries,\n * we should still record the relation accesses even not inside an explicit transaction\n * block. Thus, keeping track of the relation accesses inside coordinated transactions\n * is also required.\n */\nbool\nShouldRecordRelationAccess()\n{\n\tif (EnforceForeignKeyRestrictions &&\n\t\t(IsMultiStatementTransaction() || InCoordinatedTransaction()))\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CheckConflictingRelationAccesses is mostly a wrapper around\n * HoldsConflictingLockWithReferencingRelations(). We're only interested in\n * accesses to reference tables and citus local tables that are referenced via\n * a foreign constraint by a hash distributed table.\n */\nstatic void\nCheckConflictingRelationAccesses(Oid relationId, ShardPlacementAccessType accessType)\n{\n\tOid conflictingReferencingRelationId = InvalidOid;\n\tShardPlacementAccessType conflictingAccessType = PLACEMENT_ACCESS_SELECT;\n\n\tif (!EnforceForeignKeyRestrictions || !IsCitusTable(relationId))\n\t{\n\t\treturn;\n\t}\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\n\tif (IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE) ||\n\t\tcacheEntry->referencingRelationsViaForeignKey == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tif (HoldsConflictingLockWithReferencingRelations(relationId, accessType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t &conflictingReferencingRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t &conflictingAccessType))\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\t\tchar *conflictingRelationName = get_rel_name(conflictingReferencingRelationId);\n\n\t\tchar *accessTypeText = PlacementAccessTypeToText(accessType);\n\t\tchar *conflictingAccessTypeText =\n\t\t\tPlacementAccessTypeToText(conflictingAccessType);\n\n\t\t/*\n\t\t * Relation could already be dropped if the accessType is DDL and the\n\t\t * command that we were executing were a DROP command. In that case,\n\t\t * as this function is executed via DROP trigger, standard_ProcessUtility\n\t\t * had already dropped the table from PostgreSQL's perspective. Hence, it\n\t\t * returns NULL pointer for the name of the relation.\n\t\t */\n\t\tif (relationName == NULL)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot execute %s on table because there was \"\n\t\t\t\t\t\t\t\t   \"a parallel %s access to distributed table \"\n\t\t\t\t\t\t\t\t   \"\\\"%s\\\" in the same transaction\",\n\t\t\t\t\t\t\t\t   accessTypeText, conflictingAccessTypeText,\n\t\t\t\t\t\t\t\t   conflictingRelationName),\n\t\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot execute %s on table \\\"%s\\\" because \"\n\t\t\t\t\t\t\t\t   \"there was a parallel %s access to distributed \"\n\t\t\t\t\t\t\t\t   \"table \\\"%s\\\" in the same transaction\",\n\t\t\t\t\t\t\t\t   accessTypeText, relationName,\n\t\t\t\t\t\t\t\t   conflictingAccessTypeText,\n\t\t\t\t\t\t\t\t   conflictingRelationName),\n\t\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t\t}\n\t}\n\telse if (cacheEntry->referencingRelationsViaForeignKey != NIL &&\n\t\t\t accessType > PLACEMENT_ACCESS_SELECT)\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tif (ParallelQueryExecutedInTransaction())\n\t\t{\n\t\t\t/*\n\t\t\t * If there has already been a parallel query executed, the sequential mode\n\t\t\t * would still use the already opened parallel connections to the workers,\n\t\t\t * thus contradicting our purpose of using sequential mode.\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\"cannot modify table \\\"%s\\\" because there was \"\n\t\t\t\t\t\t\t\t   \"a parallel operation on a distributed table\",\n\t\t\t\t\t\t\t\t   relationName),\n\t\t\t\t\t\t\terrdetail(\"When there is a foreign key to a reference \"\n\t\t\t\t\t\t\t\t\t  \"table or to a local table, Citus needs \"\n\t\t\t\t\t\t\t\t\t  \"to perform all operations over a single \"\n\t\t\t\t\t\t\t\t\t  \"connection per node to ensure consistency.\"),\n\t\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t\t}\n\t\telse if (MultiShardConnectionType == PARALLEL_CONNECTION)\n\t\t{\n\t\t\t/*\n\t\t\t * We can still continue with multi-shard queries in sequential mode, so\n\t\t\t * set it.\n\t\t\t */\n\t\t\tereport(DEBUG1, (errmsg(\"switching to sequential query execution mode\"),\n\t\t\t\t\t\t\t errdetail(\n\t\t\t\t\t\t\t\t \"Table \\\"%s\\\" is modified, which might lead to data \"\n\t\t\t\t\t\t\t\t \"inconsistencies or distributed deadlocks via \"\n\t\t\t\t\t\t\t\t \"parallel accesses to hash distributed tables due to \"\n\t\t\t\t\t\t\t\t \"foreign keys. Any parallel modification to \"\n\t\t\t\t\t\t\t\t \"those hash distributed tables in the same \"\n\t\t\t\t\t\t\t\t \"transaction can only be executed in sequential query \"\n\t\t\t\t\t\t\t\t \"execution mode\",\n\t\t\t\t\t\t\t\t relationName != NULL ? relationName : \"<dropped>\")));\n\n\t\t\t/*\n\t\t\t * Switching to sequential mode is admittedly confusing and, could be useless\n\t\t\t * and less performant in some cases. However, if we do not switch to\n\t\t\t * sequential mode at this point, we'd lose the opportunity to do so\n\t\t\t * later when a parallel query is executed on the hash distributed relations\n\t\t\t * that are referencing this reference table.\n\t\t\t */\n\t\t\tSetLocalMultiShardModifyModeToSequential();\n\t\t}\n\t}\n}\n\n\n/*\n * CheckConflictingParallelRelationAccesses is mostly a wrapper around\n * HoldsConflictingLockWithReferencedRelations().  We're only interested in parallel\n * accesses to distributed tables that refers reference tables via foreign constraint.\n *\n */\nstatic void\nCheckConflictingParallelRelationAccesses(Oid relationId, ShardPlacementAccessType\n\t\t\t\t\t\t\t\t\t\t accessType)\n{\n\tOid conflictingReferencingRelationId = InvalidOid;\n\tShardPlacementAccessType conflictingAccessType = PLACEMENT_ACCESS_SELECT;\n\n\tif (!EnforceForeignKeyRestrictions || !IsCitusTable(relationId))\n\t{\n\t\treturn;\n\t}\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\tif (!(IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED) &&\n\t\t  cacheEntry->referencedRelationsViaForeignKey != NIL))\n\t{\n\t\treturn;\n\t}\n\n\tif (MultiShardConnectionType == PARALLEL_CONNECTION &&\n\t\tHoldsConflictingLockWithReferencedRelations(relationId, accessType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t&conflictingReferencingRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t&conflictingAccessType))\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\t\tchar *conflictingRelationName = get_rel_name(conflictingReferencingRelationId);\n\n\t\tchar *accessTypeText = PlacementAccessTypeToText(accessType);\n\t\tchar *conflictingAccessTypeText =\n\t\t\tPlacementAccessTypeToText(conflictingAccessType);\n\n\t\tif (ParallelQueryExecutedInTransaction())\n\t\t{\n\t\t\t/*\n\t\t\t * If there has already been a parallel query executed, the sequential mode\n\t\t\t * would still use the already opened parallel connections to the workers,\n\t\t\t * thus contradicting our purpose of using sequential mode.\n\t\t\t */\n\t\t\tereport(ERROR, (errmsg(\"cannot execute parallel %s on table \\\"%s\\\" \"\n\t\t\t\t\t\t\t\t   \"after %s command on reference table \"\n\t\t\t\t\t\t\t\t   \"\\\"%s\\\" because there is a foreign key between \"\n\t\t\t\t\t\t\t\t   \"them and \\\"%s\\\" has been accessed in this transaction\",\n\t\t\t\t\t\t\t\t   accessTypeText, relationName,\n\t\t\t\t\t\t\t\t   conflictingAccessTypeText, conflictingRelationName,\n\t\t\t\t\t\t\t\t   conflictingRelationName),\n\t\t\t\t\t\t\terrdetail(\"When there is a foreign key to a reference \"\n\t\t\t\t\t\t\t\t\t  \"table, Citus needs to perform all operations \"\n\t\t\t\t\t\t\t\t\t  \"over a single connection per node to ensure \"\n\t\t\t\t\t\t\t\t\t  \"consistency.\"),\n\t\t\t\t\t\t\terrhint(\"Try re-running the transaction with \"\n\t\t\t\t\t\t\t\t\t\"\\\"SET LOCAL citus.multi_shard_modify_mode TO \"\n\t\t\t\t\t\t\t\t\t\"\\'sequential\\';\\\"\")));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(DEBUG1, (errmsg(\"switching to sequential query execution mode\"),\n\t\t\t\t\t\t\t errdetail(\"cannot execute parallel %s on table \\\"%s\\\" \"\n\t\t\t\t\t\t\t\t\t   \"after %s command on reference table \"\n\t\t\t\t\t\t\t\t\t   \"\\\"%s\\\" because there is a foreign key between \"\n\t\t\t\t\t\t\t\t\t   \"them and \\\"%s\\\" has been accessed in this transaction\",\n\t\t\t\t\t\t\t\t\t   accessTypeText, relationName,\n\t\t\t\t\t\t\t\t\t   conflictingAccessTypeText, conflictingRelationName,\n\t\t\t\t\t\t\t\t\t   conflictingRelationName)));\n\n\t\t\tSetLocalMultiShardModifyModeToSequential();\n\t\t}\n\t}\n}\n\n\n/*\n * HoldsConflictingLockWithReferencedRelations returns true if the input relationId is a\n * hash distributed table and it holds any conflicting locks with the reference tables that\n * the distributed table has a foreign key to the reference table.\n */\nstatic bool\nHoldsConflictingLockWithReferencedRelations(Oid relationId, ShardPlacementAccessType\n\t\t\t\t\t\t\t\t\t\t\tplacementAccess,\n\t\t\t\t\t\t\t\t\t\t\tOid *conflictingRelationId,\n\n\t\t\t\t\t\t\t\t\t\t\tShardPlacementAccessType *\n\t\t\t\t\t\t\t\t\t\t\tconflictingAccessMode)\n{\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\n\tOid referencedRelation = InvalidOid;\n\tforeach_declared_oid(referencedRelation, cacheEntry->referencedRelationsViaForeignKey)\n\t{\n\t\t/*\n\t\t * We're only interested in foreign keys to reference tables and citus\n\t\t * local tables.\n\t\t */\n\t\tif (IsCitusTableType(referencedRelation, DISTRIBUTED_TABLE))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * A select on a reference table could conflict with a DDL\n\t\t * on a distributed table.\n\t\t */\n\t\tRelationAccessMode selectMode = GetRelationSelectAccessMode(referencedRelation);\n\t\tif (placementAccess == PLACEMENT_ACCESS_DDL &&\n\t\t\tselectMode != RELATION_NOT_ACCESSED)\n\t\t{\n\t\t\t*conflictingRelationId = referencedRelation;\n\t\t\t*conflictingAccessMode = PLACEMENT_ACCESS_SELECT;\n\n\t\t\treturn true;\n\t\t}\n\n\t\t/*\n\t\t * Both DML and DDL operations on a reference table conflicts with\n\t\t * any parallel operation on distributed tables.\n\t\t */\n\t\tRelationAccessMode dmlMode = GetRelationDMLAccessMode(referencedRelation);\n\t\tif (dmlMode != RELATION_NOT_ACCESSED)\n\t\t{\n\t\t\t*conflictingRelationId = referencedRelation;\n\t\t\t*conflictingAccessMode = PLACEMENT_ACCESS_DML;\n\n\t\t\treturn true;\n\t\t}\n\n\t\tRelationAccessMode ddlMode = GetRelationDDLAccessMode(referencedRelation);\n\t\tif (ddlMode != RELATION_NOT_ACCESSED)\n\t\t{\n\t\t\t*conflictingRelationId = referencedRelation;\n\t\t\t*conflictingAccessMode = PLACEMENT_ACCESS_DDL;\n\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * HoldsConflictingLockWithReferencingRelations returns true when the input relationId is a\n * reference table and it holds any conflicting locks with the distributed tables where\n * the distributed table has a foreign key to the reference table.\n *\n * If returns true, the referencing relation and conflictingAccessMode are also set.\n */\nstatic bool\nHoldsConflictingLockWithReferencingRelations(Oid relationId, ShardPlacementAccessType\n\t\t\t\t\t\t\t\t\t\t\t placementAccess, Oid *conflictingRelationId,\n\t\t\t\t\t\t\t\t\t\t\t ShardPlacementAccessType *\n\t\t\t\t\t\t\t\t\t\t\t conflictingAccessMode)\n{\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\tbool holdsConflictingLocks = false;\n\n\tAssert(!IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE));\n\n\tOid referencingRelation = InvalidOid;\n\tforeach_declared_oid(referencingRelation,\n\t\t\t\t\t\t cacheEntry->referencingRelationsViaForeignKey)\n\t{\n\t\t/*\n\t\t * We're only interested in foreign keys to reference tables from\n\t\t * hash distributed tables.\n\t\t */\n\t\tif (!IsCitusTableType(referencingRelation, HASH_DISTRIBUTED))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Rules that we apply:\n\t\t *      - SELECT on a reference might table conflict with\n\t\t *        a previous parallel DDL on a distributed table\n\t\t *      - DML on a reference table might conflict with\n\t\t *        a previous parallel DML or DDL on a distributed\n\t\t *        table\n\t\t *      - DDL on a reference table might conflict with\n\t\t *        a parellel SELECT, DML or DDL on a distributed\n\t\t *        table\n\t\t */\n\t\tif (placementAccess == PLACEMENT_ACCESS_SELECT)\n\t\t{\n\t\t\tRelationAccessMode ddlMode = GetRelationDDLAccessMode(referencingRelation);\n\n\t\t\tif (ddlMode == RELATION_PARALLEL_ACCESSED)\n\t\t\t{\n\t\t\t\t/* SELECT on a distributed table conflicts with DDL / TRUNCATE */\n\t\t\t\tholdsConflictingLocks = true;\n\t\t\t\t*conflictingAccessMode = PLACEMENT_ACCESS_DDL;\n\t\t\t}\n\t\t}\n\t\telse if (placementAccess == PLACEMENT_ACCESS_DML)\n\t\t{\n\t\t\tRelationAccessMode dmlMode = GetRelationDMLAccessMode(referencingRelation);\n\n\t\t\tif (dmlMode == RELATION_PARALLEL_ACCESSED)\n\t\t\t{\n\t\t\t\tholdsConflictingLocks = true;\n\t\t\t\t*conflictingAccessMode = PLACEMENT_ACCESS_DML;\n\t\t\t}\n\n\t\t\tRelationAccessMode ddlMode = GetRelationDDLAccessMode(referencingRelation);\n\t\t\tif (ddlMode == RELATION_PARALLEL_ACCESSED)\n\t\t\t{\n\t\t\t\t/* SELECT on a distributed table conflicts with DDL / TRUNCATE */\n\t\t\t\tholdsConflictingLocks = true;\n\t\t\t\t*conflictingAccessMode = PLACEMENT_ACCESS_DDL;\n\t\t\t}\n\t\t}\n\t\telse if (placementAccess == PLACEMENT_ACCESS_DDL)\n\t\t{\n\t\t\tRelationAccessMode selectMode = GetRelationSelectAccessMode(\n\t\t\t\treferencingRelation);\n\t\t\tif (selectMode == RELATION_PARALLEL_ACCESSED)\n\t\t\t{\n\t\t\t\tholdsConflictingLocks = true;\n\t\t\t\t*conflictingAccessMode = PLACEMENT_ACCESS_SELECT;\n\t\t\t}\n\n\t\t\tRelationAccessMode dmlMode = GetRelationDMLAccessMode(referencingRelation);\n\t\t\tif (dmlMode == RELATION_PARALLEL_ACCESSED)\n\t\t\t{\n\t\t\t\tholdsConflictingLocks = true;\n\t\t\t\t*conflictingAccessMode = PLACEMENT_ACCESS_DML;\n\t\t\t}\n\n\t\t\tRelationAccessMode ddlMode = GetRelationDDLAccessMode(referencingRelation);\n\t\t\tif (ddlMode == RELATION_PARALLEL_ACCESSED)\n\t\t\t{\n\t\t\t\tholdsConflictingLocks = true;\n\t\t\t\t*conflictingAccessMode = PLACEMENT_ACCESS_DDL;\n\t\t\t}\n\t\t}\n\n\t\tif (holdsConflictingLocks)\n\t\t{\n\t\t\t*conflictingRelationId = referencingRelation;\n\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "src/backend/distributed/transaction/remote_transaction.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * remote_transaction.c\n *   Management of transaction spanning more than one node.\n *\n *   Since the functions defined in this file mostly allocate in\n *   CitusXactCallbackContext, we mostly try doing allocations on stack.\n *   And when it's hard to do so, we at least try freeing the heap memory\n *   immediately after an object becomes useless.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/xact.h\"\n#include \"postmaster/postmaster.h\"\n#include \"utils/builtins.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/xid8.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/remote_transaction.h\"\n#include \"distributed/transaction_identifier.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/transaction_recovery.h\"\n#include \"distributed/worker_manager.h\"\n\n\n#define PREPARED_TRANSACTION_NAME_FORMAT \"citus_%u_%u_\"UINT64_FORMAT \"_%u\"\n\n\nstatic char * BeginTransactionCommand(void);\nstatic char * AssignDistributedTransactionIdCommand(void);\nstatic void StartRemoteTransactionSavepointBegin(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t\t SubTransactionId subId);\nstatic void FinishRemoteTransactionSavepointBegin(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t\t  SubTransactionId subId);\nstatic void StartRemoteTransactionSavepointRelease(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t\t   SubTransactionId subId);\nstatic void FinishRemoteTransactionSavepointRelease(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t\t\tSubTransactionId subId);\nstatic void StartRemoteTransactionSavepointRollback(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t\t\tSubTransactionId subId);\nstatic void FinishRemoteTransactionSavepointRollback(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t SubTransactionId subId);\n\nstatic void Assign2PCIdentifier(MultiConnection *connection);\n\nPG_FUNCTION_INFO_V1(start_management_transaction);\nPG_FUNCTION_INFO_V1(execute_command_on_remote_nodes_as_user);\nPG_FUNCTION_INFO_V1(commit_management_command_2pc);\n\nstatic char *IsolationLevelName[] = {\n\t\"READ UNCOMMITTED\",\n\t\"READ COMMITTED\",\n\t\"REPEATABLE READ\",\n\t\"SERIALIZABLE\"\n};\n\n/*\n * These variables are necessary for running queries from a database that is not\n * the Citus main database. Some of these queries need to be propagated to the\n * workers and Citus main database will be used for these queries, such as\n * CREATE ROLE. For that we create a connection to the Citus main database and\n * run queries from there.\n */\n\n/* The MultiConnection used for connecting Citus main database. */\nMultiConnection *MainDBConnection = NULL;\n\n/*\n * IsMainDBCommand is true if this is a query in the Citus main database that is started\n * by a query from a different database.\n */\nbool IsMainDBCommand = false;\n\n/*\n * The transaction id of the query from the other database that started the\n * main database query.\n */\nFullTransactionId OuterXid;\n\n/*\n * Shows if this is the Citus main database or not. We needed a variable instead of\n * checking if this database's name is the same as MainDb because we sometimes need\n * this value outside a transaction where we cannot reach the current database name.\n */\nbool IsMainDB = true;\n\n/*\n * Name of a superuser role to be used during main database connections.\n */\nchar *SuperuserRole = NULL;\n\n/*\n * IsMainDBCommandInXact shows if the query sent to the main database requires\n * a transaction\n */\nbool IsMainDBCommandInXact = true;\n\n\n/*\n * start_management_transaction starts a management transaction\n * in the main database by recording the outer transaction's transaction id and setting\n * IsMainDBCommand to true.\n */\nDatum\nstart_management_transaction(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureSuperUser();\n\n\tOuterXid = PG_GETARG_FULLTRANSACTIONID(0);\n\tIsMainDBCommand = true;\n\n\tUse2PCForCoordinatedTransaction();\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * execute_command_on_remote_nodes_as_user executes the query on the nodes\n * other than the current node, using the user passed.\n */\nDatum\nexecute_command_on_remote_nodes_as_user(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureSuperUser();\n\n\ttext *queryText = PG_GETARG_TEXT_P(0);\n\tchar *query = text_to_cstring(queryText);\n\n\ttext *usernameText = PG_GETARG_TEXT_P(1);\n\tchar *username = text_to_cstring(usernameText);\n\n\tStringInfo queryToSend = makeStringInfo();\n\n\tappendStringInfo(queryToSend, \"%s;%s;%s\", DISABLE_METADATA_SYNC, query,\n\t\t\t\t\t ENABLE_METADATA_SYNC);\n\n\tSendCommandToWorkersAsUser(REMOTE_NODES, username, queryToSend->data);\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * commit_management_command_2pc is a wrapper UDF for\n * CoordinatedRemoteTransactionsCommit\n */\nDatum\ncommit_management_command_2pc(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureSuperUser();\n\n\tRecoverTwoPhaseCommits();\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * RunCitusMainDBQuery creates a connection to Citus main database if necessary\n * and runs the query over the connection in the main database.\n */\nvoid\nRunCitusMainDBQuery(char *query)\n{\n\tif (MainDBConnection == NULL)\n\t{\n\t\tif (strlen(SuperuserRole) == 0)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"No superuser role is given for Citus main \"\n\t\t\t\t\t\t\t\t   \"database connection\"),\n\t\t\t\t\t\t\terrhint(\"Set citus.superuser to a superuser role name\")));\n\t\t}\n\t\tint flags = 0;\n\t\tMainDBConnection = GetNodeUserDatabaseConnection(flags, LocalHostName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t PostPortNumber,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t SuperuserRole,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t MainDb);\n\n\t\tif (IsMainDBCommandInXact)\n\t\t{\n\t\t\tRemoteTransactionBegin(MainDBConnection);\n\t\t}\n\t}\n\n\tSendRemoteCommand(MainDBConnection, query);\n\n\tPGresult *result = GetRemoteCommandResult(MainDBConnection, true);\n\n\tif (!IsResponseOK(result))\n\t{\n\t\tReportResultError(MainDBConnection, result, ERROR);\n\t}\n\n\tForgetResults(MainDBConnection);\n}\n\n\n/*\n * CleanCitusMainDBConnection closes and removes the connection to Citus main database.\n */\nvoid\nCleanCitusMainDBConnection(void)\n{\n\tif (MainDBConnection == NULL)\n\t{\n\t\treturn;\n\t}\n\tCloseConnection(MainDBConnection);\n\tMainDBConnection = NULL;\n}\n\n\n/*\n * StartRemoteTransactionBegin initiates beginning the remote transaction in\n * a non-blocking manner. The function sends \"BEGIN\" followed by\n * assign_distributed_transaction_id() to assign the distributed transaction\n * id on the remote node.\n */\nvoid\nStartRemoteTransactionBegin(struct MultiConnection *connection)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\tAssert(transaction->transactionState == REMOTE_TRANS_NOT_STARTED);\n\n\t/* remember transaction as being in-progress */\n\tdlist_push_tail(&InProgressTransactions, &connection->transactionNode);\n\tconnection->transactionInProgress = true;\n\n\ttransaction->transactionState = REMOTE_TRANS_STARTING;\n\n\tStringInfo beginAndSetDistributedTransactionId = makeStringInfo();\n\n\t/*\n\t * Explicitly specify READ COMMITTED, the default on the remote\n\t * side might have been changed, and that would cause problematic\n\t * behaviour.\n\t */\n\tappendStringInfoString(beginAndSetDistributedTransactionId,\n\t\t\t\t\t\t   BeginTransactionCommand());\n\n\t/* append context for in-progress SAVEPOINTs for this transaction */\n\tList *activeSubXacts = ActiveSubXactContexts();\n\ttransaction->lastSuccessfulSubXact = TopSubTransactionId;\n\ttransaction->lastQueuedSubXact = TopSubTransactionId;\n\n\tSubXactContext *subXactState = NULL;\n\tforeach_declared_ptr(subXactState, activeSubXacts)\n\t{\n\t\t/* append SET LOCAL state from when SAVEPOINT was encountered... */\n\t\tif (subXactState->setLocalCmds != NULL)\n\t\t{\n\t\t\tappendStringInfoString(beginAndSetDistributedTransactionId,\n\t\t\t\t\t\t\t\t   subXactState->setLocalCmds->data);\n\t\t}\n\n\t\t/* ... then append SAVEPOINT to enter this subxact */\n\t\tappendStringInfo(beginAndSetDistributedTransactionId,\n\t\t\t\t\t\t \"SAVEPOINT savepoint_%u;\", subXactState->subId);\n\t\ttransaction->lastQueuedSubXact = subXactState->subId;\n\t}\n\n\t/* we've pushed into deepest subxact: apply in-progress SET context */\n\tif (activeSetStmts != NULL)\n\t{\n\t\tappendStringInfoString(beginAndSetDistributedTransactionId, activeSetStmts->data);\n\t}\n\n\tchar *assignDistributedTransactionIdCommand = AssignDistributedTransactionIdCommand();\n\n\t/* add SELECT assign_distributed_transaction_id ... */\n\tappendStringInfoString(beginAndSetDistributedTransactionId,\n\t\t\t\t\t\t   assignDistributedTransactionIdCommand);\n\n\tpfree(assignDistributedTransactionIdCommand);\n\n\tbool success = SendRemoteCommand(connection,\n\t\t\t\t\t\t\t\t\t beginAndSetDistributedTransactionId->data);\n\n\tpfree(beginAndSetDistributedTransactionId->data);\n\tpfree(beginAndSetDistributedTransactionId);\n\n\tif (!success)\n\t{\n\t\tconst bool raiseErrors = true;\n\n\t\tHandleRemoteTransactionConnectionError(connection, raiseErrors);\n\t}\n\n\ttransaction->beginSent = true;\n}\n\n\n/*\n * BeginAndSetDistributedTransactionIdCommand returns a command which starts\n * a transaction and assigns the current distributed transaction id.\n */\nStringInfo\nBeginAndSetDistributedTransactionIdCommand(void)\n{\n\tStringInfo beginAndSetDistributedTransactionId = makeStringInfo();\n\n\t/*\n\t * Explicitly specify READ COMMITTED, the default on the remote\n\t * side might have been changed, and that would cause problematic\n\t * behaviour.\n\t */\n\tappendStringInfoString(beginAndSetDistributedTransactionId,\n\t\t\t\t\t\t   BeginTransactionCommand());\n\n\tappendStringInfoString(beginAndSetDistributedTransactionId,\n\t\t\t\t\t\t   AssignDistributedTransactionIdCommand());\n\n\treturn beginAndSetDistributedTransactionId;\n}\n\n\n/*\n * BeginTransactionCommand returns the BEGIN command to use for the current isolation\n * level.\n *\n * Transactions have 3 properties that we care about here:\n * - XactIsoLevel (isolation level)\n * - XactDeferrable (deferrable)\n * - XactReadOnly (read only)\n *\n * These properties can be set in several ways:\n * - via BEGIN TRANSACTION ISOLATION LEVEL ...\n * - via default_transaction_isolation, ...\n * - via SET TRANSACTION .. (or plain SET transaction_isolation ...)\n *\n * We want to make sure that the properties that are passed to the worker nodes\n * match the coordinator as much as possible. However, we do not want to waste\n * bytes repeating the current values ad infinitum.\n *\n * The trade-off we make is that we send the isolation level in all cases,\n * but only set deferrable and read-only if they were explicitly specified\n * in the BEGIN by the user. The implication is that we may not follow the\n * default_transaction_* settings on the coordinator if they differ on the\n * worker.\n */\nstatic char *\nBeginTransactionCommand(void)\n{\n\tStringInfo beginCommand = makeStringInfo();\n\n\t/*\n\t * XactIsoLevel can only be set at the start of the transaction, before the\n\t * first query. Since Citus does not send BEGIN until the first query, we\n\t * can simply use the current values, and they will match the values for the\n\t * outer transaction after any BEGIN and SET TRANSACTION that may have occurred.\n\t */\n\tappendStringInfo(beginCommand, \"BEGIN TRANSACTION ISOLATION LEVEL %s\",\n\t\t\t\t\t IsolationLevelName[XactIsoLevel]);\n\n\tif (BeginXactDeferrable == BeginXactDeferrable_Enabled)\n\t{\n\t\tappendStringInfoString(beginCommand, \" DEFERRABLE\");\n\t}\n\telse if (BeginXactDeferrable == BeginXactDeferrable_Disabled)\n\t{\n\t\tappendStringInfoString(beginCommand, \" NOT DEFERRABLE\");\n\t}\n\n\tif (BeginXactReadOnly == BeginXactReadOnly_Enabled)\n\t{\n\t\tappendStringInfoString(beginCommand, \" READ ONLY\");\n\t}\n\telse if (BeginXactReadOnly == BeginXactReadOnly_Disabled)\n\t{\n\t\tappendStringInfoString(beginCommand, \" READ WRITE\");\n\t}\n\n\tappendStringInfoChar(beginCommand, ';');\n\n\treturn beginCommand->data;\n}\n\n\n/*\n * AssignDistributedTransactionIdCommand returns a command to set the local\n * distributed transaction ID on a remote transaction.\n */\nstatic char *\nAssignDistributedTransactionIdCommand(void)\n{\n\tStringInfo assignDistributedTransactionId = makeStringInfo();\n\n\t/*\n\t * Append BEGIN and assign_distributed_transaction_id() statements into a single command\n\t * and send both in one step. The reason is purely performance, we don't want\n\t * seperate roundtrips for these two statements.\n\t */\n\tDistributedTransactionId *distributedTransactionId =\n\t\tGetCurrentDistributedTransactionId();\n\tconst char *timestamp = timestamptz_to_str(distributedTransactionId->timestamp);\n\tappendStringInfo(assignDistributedTransactionId,\n\t\t\t\t\t \"SELECT assign_distributed_transaction_id(%d, \" UINT64_FORMAT\n\t\t\t\t\t \", '%s');\",\n\t\t\t\t\t distributedTransactionId->initiatorNodeIdentifier,\n\t\t\t\t\t distributedTransactionId->transactionNumber,\n\t\t\t\t\t timestamp);\n\n\t/* free the StringInfo but not the buffer itself */\n\tchar *command = assignDistributedTransactionId->data;\n\tpfree(assignDistributedTransactionId);\n\n\treturn command;\n}\n\n\n/*\n * FinishRemoteTransactionBegin finishes the work StartRemoteTransactionBegin\n * initiated. It blocks if necessary (i.e. if PQisBusy() would return true).\n */\nvoid\nFinishRemoteTransactionBegin(struct MultiConnection *connection)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\tbool raiseErrors = true;\n\n\tAssert(transaction->transactionState == REMOTE_TRANS_STARTING);\n\n\tbool clearSuccessful = ClearResults(connection, raiseErrors);\n\tif (clearSuccessful)\n\t{\n\t\ttransaction->transactionState = REMOTE_TRANS_STARTED;\n\t\ttransaction->lastSuccessfulSubXact = transaction->lastQueuedSubXact;\n\t}\n\n\tif (!transaction->transactionFailed)\n\t{\n\t\tAssert(PQtransactionStatus(connection->pgConn) == PQTRANS_INTRANS);\n\t}\n}\n\n\n/*\n * RemoteTransactionBegin begins a remote transaction in a blocking manner.\n */\nvoid\nRemoteTransactionBegin(struct MultiConnection *connection)\n{\n\tStartRemoteTransactionBegin(connection);\n\tFinishRemoteTransactionBegin(connection);\n}\n\n\n/*\n * RemoteTransactionListBegin sends BEGIN over all connections in the\n * given connection list and waits for all of them to finish.\n */\nvoid\nRemoteTransactionListBegin(List *connectionList)\n{\n\tMultiConnection *connection = NULL;\n\n\t/* send BEGIN to all nodes */\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tStartRemoteTransactionBegin(connection);\n\t}\n\n\t/* wait for BEGIN to finish on all nodes */\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tFinishRemoteTransactionBegin(connection);\n\t}\n}\n\n\n/*\n * StartRemoteTransactionCommit initiates transaction commit in a non-blocking\n * manner.  If the transaction is in a failed state, it'll instead get rolled\n * back.\n */\nvoid\nStartRemoteTransactionCommit(MultiConnection *connection)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\tconst bool raiseErrors = false;\n\n\t/* can only commit if transaction is in progress */\n\tAssert(transaction->transactionState != REMOTE_TRANS_NOT_STARTED);\n\n\t/* can't commit if we already started to commit or abort */\n\tAssert(transaction->transactionState < REMOTE_TRANS_1PC_ABORTING);\n\n\tif (transaction->transactionFailed)\n\t{\n\t\t/* abort the transaction if it failed */\n\t\ttransaction->transactionState = REMOTE_TRANS_1PC_ABORTING;\n\n\t\t/*\n\t\t * Try sending an ROLLBACK; Depending on the state that won't\n\t\t * succeed, but let's try.  Have to clear previous results\n\t\t * first.\n\t\t */\n\t\tForgetResults(connection); /* try to clear pending stuff */\n\t\tif (!SendRemoteCommand(connection, \"ROLLBACK\"))\n\t\t{\n\t\t\t/* no point in reporting a likely redundant message */\n\t\t}\n\t}\n\telse if (transaction->transactionState == REMOTE_TRANS_PREPARED)\n\t{\n\t\t/*\n\t\t * Commit the prepared transaction.\n\t\t *\n\t\t * We need to allocate 420 bytes for command buffer (including '\\0'):\n\t\t *  - len(\"COMMIT PREPARED \") = 16\n\t\t *  - maximum quoted length of transaction->preparedName = 2 * 200 + 3 = 403\n\t\t */\n\t\tchar command[420];\n\t\tchar *quotedPrepName = quote_literal_cstr(transaction->preparedName);\n\t\tSafeSnprintf(command, sizeof(command), \"COMMIT PREPARED %s\", quotedPrepName);\n\t\tpfree(quotedPrepName);\n\n\t\ttransaction->transactionState = REMOTE_TRANS_2PC_COMMITTING;\n\n\t\tif (!SendRemoteCommand(connection, command))\n\t\t{\n\t\t\tHandleRemoteTransactionConnectionError(connection, raiseErrors);\n\t\t}\n\t}\n\telse\n\t{\n\t\t/* initiate remote transaction commit */\n\t\ttransaction->transactionState = REMOTE_TRANS_1PC_COMMITTING;\n\n\t\tif (!SendRemoteCommand(connection, \"COMMIT\"))\n\t\t{\n\t\t\t/*\n\t\t\t * For a moment there I thought we were in trouble.\n\t\t\t *\n\t\t\t * Failing in this state means that we don't know whether the\n\t\t\t * commit has succeeded.\n\t\t\t */\n\t\t\tHandleRemoteTransactionConnectionError(connection, raiseErrors);\n\t\t}\n\t}\n}\n\n\n/*\n * FinishRemoteTransactionCommit finishes the work\n * StartRemoteTransactionCommit initiated. It blocks if necessary (i.e. if\n * PQisBusy() would return true).\n */\nvoid\nFinishRemoteTransactionCommit(MultiConnection *connection)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\tconst bool raiseErrors = false;\n\n\tAssert(transaction->transactionState == REMOTE_TRANS_1PC_ABORTING ||\n\t\t   transaction->transactionState == REMOTE_TRANS_1PC_COMMITTING ||\n\t\t   transaction->transactionState == REMOTE_TRANS_2PC_COMMITTING);\n\n\tPGresult *result = GetRemoteCommandResult(connection, raiseErrors);\n\n\tif (!IsResponseOK(result))\n\t{\n\t\tHandleRemoteTransactionResultError(connection, result, raiseErrors);\n\n\t\t/*\n\t\t * Failing in this state means that we will often not know whether\n\t\t * the commit has succeeded (particularly in case of network\n\t\t * troubles).\n\t\t *\n\t\t * XXX: It might be worthwhile to discern cases where we got a\n\t\t * proper error back from postgres (i.e. COMMIT was received but\n\t\t * produced an error) from cases where the connection failed\n\t\t * before getting a reply.\n\t\t */\n\n\t\tif (transaction->transactionState == REMOTE_TRANS_1PC_COMMITTING)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"failed to commit transaction on %s:%d\",\n\t\t\t\t\t\t\t\t\t connection->hostname, connection->port)));\n\t\t}\n\t\telse if (transaction->transactionState == REMOTE_TRANS_2PC_COMMITTING)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"failed to commit transaction on %s:%d\",\n\t\t\t\t\t\t\t\t\t connection->hostname, connection->port)));\n\t\t}\n\t}\n\telse if (transaction->transactionState == REMOTE_TRANS_1PC_ABORTING ||\n\t\t\t transaction->transactionState == REMOTE_TRANS_2PC_ABORTING)\n\t{\n\t\ttransaction->transactionState = REMOTE_TRANS_ABORTED;\n\t}\n\telse\n\t{\n\t\ttransaction->transactionState = REMOTE_TRANS_COMMITTED;\n\t}\n\n\tPQclear(result);\n\n\tForgetResults(connection);\n}\n\n\n/*\n * RemoteTransactionCommit commits (or aborts, if the transaction failed) a\n * remote transaction in a blocking manner.\n */\nvoid\nRemoteTransactionCommit(MultiConnection *connection)\n{\n\tStartRemoteTransactionCommit(connection);\n\tFinishRemoteTransactionCommit(connection);\n}\n\n\n/*\n * StartRemoteTransactionAbort initiates abortin the transaction in a\n * non-blocking manner.\n */\nvoid\nStartRemoteTransactionAbort(MultiConnection *connection)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\tconst bool raiseErrors = false;\n\n\tAssert(transaction->transactionState != REMOTE_TRANS_NOT_STARTED);\n\n\t/*\n\t * Clear previous results, so we have a better chance to send ROLLBACK\n\t * [PREPARED]. If we've previously sent a PREPARE TRANSACTION, we always\n\t * want to wait for that result, as that shouldn't take long and will\n\t * reserve resources.  But if there's another query running, we don't want\n\t * to wait, because a long running statement may be running, so force it to\n\t * be killed in that case.\n\t */\n\tif (transaction->transactionState == REMOTE_TRANS_PREPARING ||\n\t\ttransaction->transactionState == REMOTE_TRANS_PREPARED)\n\t{\n\t\tForgetResults(connection);\n\n\t\t/*\n\t\t * Await PREPARE TRANSACTION results, closing the connection would leave it dangling.\n\t\t *\n\t\t * We need to allocate 422 bytes for command buffer (including '\\0'):\n\t\t *  - len(\"ROLLBACK PREPARED \") = 18\n\t\t *  - maximum quoted length of transaction->preparedName = 2 * 200 + 3 = 403\n\t\t */\n\t\tchar command[422];\n\t\tchar *quotedPrepName = quote_literal_cstr(transaction->preparedName);\n\t\tSafeSnprintf(command, sizeof(command), \"ROLLBACK PREPARED %s\", quotedPrepName);\n\t\tpfree(quotedPrepName);\n\n\t\tif (!SendRemoteCommand(connection, command))\n\t\t{\n\t\t\tHandleRemoteTransactionConnectionError(connection, raiseErrors);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttransaction->transactionState = REMOTE_TRANS_2PC_ABORTING;\n\t\t}\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * In case of a cancellation, the connection might still be working\n\t\t * on some commands. Try to consume the results such that the\n\t\t * connection can be reused, but do not want to wait for commands\n\t\t * to finish. Instead we just close the connection if the command\n\t\t * is still busy.\n\t\t */\n\t\tif (!ClearResultsIfReady(connection))\n\t\t{\n\t\t\tShutdownConnection(connection);\n\n\t\t\t/* FinishRemoteTransactionAbort will emit warning */\n\t\t\treturn;\n\t\t}\n\n\t\tif (!SendRemoteCommand(connection, \"ROLLBACK\"))\n\t\t{\n\t\t\t/* no point in reporting a likely redundant message */\n\t\t\tMarkRemoteTransactionFailed(connection, raiseErrors);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttransaction->transactionState = REMOTE_TRANS_1PC_ABORTING;\n\t\t}\n\t}\n}\n\n\n/*\n * FinishRemoteTransactionAbort finishes the work StartRemoteTransactionAbort\n * initiated. It blocks if necessary (i.e. if PQisBusy() would return true).\n */\nvoid\nFinishRemoteTransactionAbort(MultiConnection *connection)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\tconst bool raiseErrors = false;\n\n\tif (transaction->transactionState == REMOTE_TRANS_2PC_ABORTING)\n\t{\n\t\tPGresult *result = GetRemoteCommandResult(connection, raiseErrors);\n\t\tif (!IsResponseOK(result))\n\t\t{\n\t\t\tHandleRemoteTransactionResultError(connection, result, raiseErrors);\n\t\t}\n\n\t\tPQclear(result);\n\t}\n\n\t/*\n\t * Try to consume results of any in-progress commands. In the 1PC case\n\t * this is also where we consume the result of the ROLLBACK.\n\t *\n\t * If we don't succeed the connection will be in a bad state, so we close it.\n\t */\n\tif (!ClearResults(connection, raiseErrors))\n\t{\n\t\tShutdownConnection(connection);\n\t}\n\n\ttransaction->transactionState = REMOTE_TRANS_ABORTED;\n}\n\n\n/*\n * RemoteTransactionAbort aborts a remote transaction in a blocking manner.\n */\nvoid\nRemoteTransactionAbort(MultiConnection *connection)\n{\n\tStartRemoteTransactionAbort(connection);\n\tFinishRemoteTransactionAbort(connection);\n}\n\n\n/*\n * StartRemoteTransactionPrepare initiates preparing the transaction in a\n * non-blocking manner.\n */\nvoid\nStartRemoteTransactionPrepare(struct MultiConnection *connection)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\tconst bool raiseErrors = true;\n\n\t/* can't prepare a nonexistant transaction */\n\tAssert(transaction->transactionState != REMOTE_TRANS_NOT_STARTED);\n\n\t/* can't prepare in a failed transaction */\n\tAssert(!transaction->transactionFailed);\n\n\t/* can't prepare if already started to prepare/abort/commit */\n\tAssert(transaction->transactionState < REMOTE_TRANS_PREPARING);\n\n\tAssign2PCIdentifier(connection);\n\n\t/* log transactions to workers in pg_dist_transaction */\n\tWorkerNode *workerNode = FindWorkerNode(connection->hostname, connection->port);\n\tif (workerNode != NULL)\n\t{\n\t\tLogTransactionRecord(workerNode->groupId, transaction->preparedName, OuterXid);\n\t}\n\n\t/*\n\t * We need to allocate 424 bytes for command buffer (including '\\0'):\n\t *  - len(\"PREPARE TRANSACTION \") = 20\n\t *  - maximum quoted length of transaction->preparedName = 2 * 200 + 3 = 403\n\t */\n\tchar command[424];\n\tchar *quotedPrepName = quote_literal_cstr(transaction->preparedName);\n\tSafeSnprintf(command, sizeof(command), \"PREPARE TRANSACTION %s\", quotedPrepName);\n\tpfree(quotedPrepName);\n\n\tif (!SendRemoteCommand(connection, command))\n\t{\n\t\tHandleRemoteTransactionConnectionError(connection, raiseErrors);\n\t}\n\telse\n\t{\n\t\ttransaction->transactionState = REMOTE_TRANS_PREPARING;\n\t}\n}\n\n\n/*\n * FinishRemoteTransactionPrepare finishes the work\n * StartRemoteTransactionPrepare initiated. It blocks if necessary (i.e. if\n * PQisBusy() would return true).\n */\nvoid\nFinishRemoteTransactionPrepare(struct MultiConnection *connection)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\tconst bool raiseErrors = true;\n\n\tAssert(transaction->transactionState == REMOTE_TRANS_PREPARING);\n\n\tPGresult *result = GetRemoteCommandResult(connection, raiseErrors);\n\n\tif (!IsResponseOK(result))\n\t{\n\t\ttransaction->transactionState = REMOTE_TRANS_ABORTED;\n\t\tHandleRemoteTransactionResultError(connection, result, raiseErrors);\n\t}\n\telse\n\t{\n\t\ttransaction->transactionState = REMOTE_TRANS_PREPARED;\n\t}\n\n\tPQclear(result);\n\n\t/*\n\t * Try to consume results of PREPARE TRANSACTION command. If we don't\n\t * succeed, rollback the transaction. Note that we've not committed on\n\t * any node yet, and we're not sure about the state of the worker node.\n\t * So rollbacking seems to be the safest action if the worker is\n\t * in a state where it can actually rollback.\n\t */\n\tif (!ClearResults(connection, raiseErrors))\n\t{\n\t\tereport(ERROR, (errmsg(\"failed to prepare transaction '%s' on host %s:%d\",\n\t\t\t\t\t\t\t   transaction->preparedName, connection->hostname,\n\t\t\t\t\t\t\t   connection->port),\n\t\t\t\t\t\terrhint(\"Try re-running the command.\")));\n\t}\n}\n\n\n/*\n * RemoteTransactionBeginIfNecessary is a convenience wrapper around\n * RemoteTransactionsBeginIfNecessary(), for a single connection.\n */\nvoid\nRemoteTransactionBeginIfNecessary(MultiConnection *connection)\n{\n\t/* just delegate */\n\tif (InCoordinatedTransaction())\n\t{\n\t\tList *connectionList = list_make1(connection);\n\n\t\tRemoteTransactionsBeginIfNecessary(connectionList);\n\t\tlist_free(connectionList);\n\t}\n}\n\n\n/*\n * RemoteTransactionsBeginIfNecessary begins, if necessary according to this\n * session's coordinated transaction state, and the remote transaction's\n * state, an explicit transaction on all the connections.  This is done in\n * parallel, to lessen latency penalties.\n */\nvoid\nRemoteTransactionsBeginIfNecessary(List *connectionList)\n{\n\tMultiConnection *connection = NULL;\n\n\t/*\n\t * Don't do anything if not in a coordinated transaction. That allows the\n\t * same code to work both in situations that uses transactions, and when\n\t * not.\n\t */\n\tif (!InCoordinatedTransaction())\n\t{\n\t\treturn;\n\t}\n\n\t/* issue BEGIN to all connections needing it */\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t\t/* can't send BEGIN if a command already is in progress */\n\t\tAssert(PQtransactionStatus(connection->pgConn) != PQTRANS_ACTIVE);\n\n\t\t/*\n\t\t * If a transaction already is in progress (including having failed),\n\t\t * don't start it again. That's quite normal if a piece of code allows\n\t\t * cached connections.\n\t\t */\n\t\tif (transaction->transactionState != REMOTE_TRANS_NOT_STARTED)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tStartRemoteTransactionBegin(connection);\n\t}\n\n\tbool raiseInterrupts = true;\n\tWaitForAllConnections(connectionList, raiseInterrupts);\n\n\t/* get result of all the BEGINs */\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t\t/*\n\t\t * Only handle BEGIN results on connections that are in process of\n\t\t * starting a transaction, and haven't already failed (e.g. by not\n\t\t * being able to send BEGIN due to a network failure).\n\t\t */\n\t\tif (transaction->transactionFailed ||\n\t\t\ttransaction->transactionState != REMOTE_TRANS_STARTING)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tFinishRemoteTransactionBegin(connection);\n\t}\n}\n\n\n/*\n * HandleRemoteTransactionConnectionError records a transaction as having failed\n * and throws a connection error if the transaction was critical and raiseErrors\n * is true, or a warning otherwise.\n */\nvoid\nHandleRemoteTransactionConnectionError(MultiConnection *connection, bool raiseErrors)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\ttransaction->transactionFailed = true;\n\n\tif (transaction->transactionCritical && raiseErrors)\n\t{\n\t\tReportConnectionError(connection, ERROR);\n\t}\n\telse\n\t{\n\t\tReportConnectionError(connection, WARNING);\n\t}\n}\n\n\n/*\n * HandleRemoteTransactionResultError records a transaction as having failed\n * and throws a result error if the transaction was critical and raiseErrors\n * is true, or a warning otherwise.\n */\nvoid\nHandleRemoteTransactionResultError(MultiConnection *connection, PGresult *result, bool\n\t\t\t\t\t\t\t\t   raiseErrors)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\ttransaction->transactionFailed = true;\n\n\tif (transaction->transactionCritical && raiseErrors)\n\t{\n\t\tReportResultError(connection, result, ERROR);\n\t}\n\telse\n\t{\n\t\tReportResultError(connection, result, WARNING);\n\t}\n}\n\n\n/*\n * MarkRemoteTransactionFailed records a transaction as having failed.\n *\n * If the connection is marked as critical, and allowErrorPromotion is true,\n * this routine will ERROR out. The allowErrorPromotion case is primarily\n * required for the transaction management code itself. Usually it is helpful\n * to fail as soon as possible. If !allowErrorPromotion transaction commit\n * will instead issue an error before committing on any node.\n */\nvoid\nMarkRemoteTransactionFailed(MultiConnection *connection, bool allowErrorPromotion)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\ttransaction->transactionFailed = true;\n\n\t/*\n\t * If the connection is marked as critical, fail the entire coordinated\n\t * transaction. If allowed.\n\t */\n\tif (transaction->transactionCritical && allowErrorPromotion)\n\t{\n\t\tereport(ERROR, (errmsg(\"failure on connection marked as essential: %s:%d\",\n\t\t\t\t\t\t\t   connection->hostname, connection->port)));\n\t}\n}\n\n\n/*\n * MarkRemoteTransactionCritical signals that failures on this remote\n * transaction should fail the entire coordinated transaction.\n */\nvoid\nMarkRemoteTransactionCritical(struct MultiConnection *connection)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\ttransaction->transactionCritical = true;\n}\n\n\n/*\n * ResetRemoteTransaction resets the state of the transaction after the end of\n * the main transaction, if the connection is being reused.\n */\nvoid\nResetRemoteTransaction(struct MultiConnection *connection)\n{\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t/* unlink from list of open transactions, if necessary */\n\tif (connection->transactionInProgress)\n\t{\n\t\t/* XXX: Should we error out for a critical transaction? */\n\n\t\tdlist_delete(&connection->transactionNode);\n\t\tconnection->transactionInProgress = false;\n\t\tmemset(&connection->transactionNode, 0, sizeof(connection->transactionNode));\n\t}\n\n\t/* just reset the entire state, relying on 0 being invalid/false */\n\tmemset(transaction, 0, sizeof(*transaction));\n\n\tResetShardPlacementAssociation(connection);\n\n\t/* reset copy state */\n\tconnection->copyBytesWrittenSinceLastFlush = 0;\n}\n\n\n/*\n * CoordinatedRemoteTransactionsPrepare PREPAREs a 2PC transaction on all\n * non-failed transactions participating in the coordinated transaction.\n */\nvoid\nCoordinatedRemoteTransactionsPrepare(void)\n{\n\tdlist_iter iter;\n\tList *connectionList = NIL;\n\n\t/* issue PREPARE TRANSACTION; to all relevant remote nodes */\n\n\t/* asynchronously send PREPARE */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t\tAssert(transaction->transactionState != REMOTE_TRANS_NOT_STARTED);\n\n\t\t/* can't PREPARE a transaction that failed */\n\t\tif (transaction->transactionFailed)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Check if any DML or DDL is executed over the connection on any\n\t\t * placement/table. If yes, we start preparing the transaction, otherwise\n\t\t * we skip prepare since the connection didn't perform any write (read-only)\n\t\t */\n\t\tif (ConnectionModifiedPlacement(connection))\n\t\t{\n\t\t\tStartRemoteTransactionPrepare(connection);\n\t\t\tconnectionList = lappend(connectionList, connection);\n\t\t}\n\t}\n\n\tbool raiseInterrupts = true;\n\tWaitForAllConnections(connectionList, raiseInterrupts);\n\n\t/* Wait for result */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t\tif (transaction->transactionState != REMOTE_TRANS_PREPARING)\n\t\t{\n\t\t\t/*\n\t\t\t * Verify that either the transaction failed, hence we couldn't prepare\n\t\t\t * or the connection didn't modify any placement\n\t\t\t */\n\t\t\tAssert(transaction->transactionFailed ||\n\t\t\t\t   !ConnectionModifiedPlacement(connection));\n\t\t\tcontinue;\n\t\t}\n\n\t\tFinishRemoteTransactionPrepare(connection);\n\t}\n\n\tCurrentCoordinatedTransactionState = COORD_TRANS_PREPARED;\n\n\tlist_free(connectionList);\n}\n\n\n/*\n * CoordinatedRemoteTransactionsCommit performs distributed transactions\n * handling at commit time. This will be called at XACT_EVENT_PRE_COMMIT if\n * 1PC commits are used - so shards can still be invalidated - and at\n * XACT_EVENT_COMMIT if 2PC is being used.\n *\n * Note that this routine has to issue rollbacks for failed transactions.\n */\nvoid\nCoordinatedRemoteTransactionsCommit(void)\n{\n\tdlist_iter iter;\n\tList *connectionList = NIL;\n\n\t/*\n\t * Issue appropriate transaction commands to remote nodes. If everything\n\t * went well that's going to be COMMIT or COMMIT PREPARED, if individual\n\t * connections had errors, some or all of them might require a ROLLBACK.\n\t *\n\t * First send the command asynchronously over all connections.\n\t */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t\tif (transaction->transactionState == REMOTE_TRANS_NOT_STARTED ||\n\t\t\ttransaction->transactionState == REMOTE_TRANS_1PC_COMMITTING ||\n\t\t\ttransaction->transactionState == REMOTE_TRANS_2PC_COMMITTING ||\n\t\t\ttransaction->transactionState == REMOTE_TRANS_COMMITTED ||\n\t\t\ttransaction->transactionState == REMOTE_TRANS_ABORTED)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tStartRemoteTransactionCommit(connection);\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\tbool raiseInterrupts = false;\n\tWaitForAllConnections(connectionList, raiseInterrupts);\n\n\t/* wait for the replies to the commands to come in */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t\t/* nothing to do if not committing / aborting */\n\t\tif (transaction->transactionState != REMOTE_TRANS_1PC_COMMITTING &&\n\t\t\ttransaction->transactionState != REMOTE_TRANS_2PC_COMMITTING &&\n\t\t\ttransaction->transactionState != REMOTE_TRANS_1PC_ABORTING &&\n\t\t\ttransaction->transactionState != REMOTE_TRANS_2PC_ABORTING)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tFinishRemoteTransactionCommit(connection);\n\t}\n\n\tlist_free(connectionList);\n}\n\n\n/*\n * CoordinatedRemoteTransactionsAbort performs distributed transactions\n * handling at abort time.\n *\n * This issues ROLLBACKS and ROLLBACK PREPARED depending on whether the remote\n * transaction has been prepared or not.\n */\nvoid\nCoordinatedRemoteTransactionsAbort(void)\n{\n\tdlist_iter iter;\n\tList *connectionList = NIL;\n\n\t/* asynchronously send ROLLBACK [PREPARED] */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t\tif (transaction->transactionState == REMOTE_TRANS_NOT_STARTED ||\n\t\t\ttransaction->transactionState == REMOTE_TRANS_1PC_ABORTING ||\n\t\t\ttransaction->transactionState == REMOTE_TRANS_2PC_ABORTING ||\n\t\t\ttransaction->transactionState == REMOTE_TRANS_ABORTED)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tStartRemoteTransactionAbort(connection);\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\tbool raiseInterrupts = false;\n\tWaitForAllConnections(connectionList, raiseInterrupts);\n\n\t/* and wait for the results */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t\tif (transaction->transactionState != REMOTE_TRANS_1PC_ABORTING &&\n\t\t\ttransaction->transactionState != REMOTE_TRANS_2PC_ABORTING)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tFinishRemoteTransactionAbort(connection);\n\t}\n\n\tlist_free(connectionList);\n}\n\n\n/*\n * CoordinatedRemoteTransactionsSavepointBegin sends the SAVEPOINT command for\n * the given sub-transaction id to all connections participating in the current\n * transaction.\n */\nvoid\nCoordinatedRemoteTransactionsSavepointBegin(SubTransactionId subId)\n{\n\tdlist_iter iter;\n\tconst bool raiseInterrupts = true;\n\tList *connectionList = NIL;\n\n\t/* asynchronously send SAVEPOINT */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\t\tif (transaction->transactionFailed)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tStartRemoteTransactionSavepointBegin(connection, subId);\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\tWaitForAllConnections(connectionList, raiseInterrupts);\n\n\t/* and wait for the results */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\t\tif (transaction->transactionFailed)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tFinishRemoteTransactionSavepointBegin(connection, subId);\n\n\t\tif (!transaction->transactionFailed)\n\t\t{\n\t\t\ttransaction->lastSuccessfulSubXact = subId;\n\t\t}\n\t}\n\n\tlist_free(connectionList);\n}\n\n\n/*\n * CoordinatedRemoteTransactionsSavepointRelease sends the RELEASE SAVEPOINT\n * command for the given sub-transaction id to all connections participating in\n * the current transaction.\n */\nvoid\nCoordinatedRemoteTransactionsSavepointRelease(SubTransactionId subId)\n{\n\tdlist_iter iter;\n\tconst bool raiseInterrupts = true;\n\tList *connectionList = NIL;\n\n\t/* asynchronously send RELEASE SAVEPOINT */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\t\tif (transaction->transactionFailed)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tStartRemoteTransactionSavepointRelease(connection, subId);\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\tWaitForAllConnections(connectionList, raiseInterrupts);\n\n\t/* and wait for the results */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\t\tif (transaction->transactionFailed)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tFinishRemoteTransactionSavepointRelease(connection, subId);\n\t}\n\n\tlist_free(connectionList);\n}\n\n\n/*\n * CoordinatedRemoteTransactionsSavepointRollback sends the ROLLBACK TO SAVEPOINT\n * command for the given sub-transaction id to all connections participating in\n * the current transaction.\n */\nvoid\nCoordinatedRemoteTransactionsSavepointRollback(SubTransactionId subId)\n{\n\tdlist_iter iter;\n\tconst bool raiseInterrupts = false;\n\tList *connectionList = NIL;\n\n\t/* asynchronously send ROLLBACK TO SAVEPOINT */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\t\t/* cancel any ongoing queries before issuing rollback */\n\t\tSendCancelationRequest(connection);\n\n\t\t/* clear results, but don't show cancelation warning messages from workers. */\n\t\tClearResultsDiscardWarnings(connection, raiseInterrupts);\n\n\t\tif (transaction->transactionFailed)\n\t\t{\n\t\t\tif (transaction->lastSuccessfulSubXact <= subId)\n\t\t\t{\n\t\t\t\ttransaction->transactionRecovering = true;\n\n\t\t\t\t/*\n\t\t\t\t * Clear the results of the failed query so we can send the ROLLBACK\n\t\t\t\t * TO SAVEPOINT command for a savepoint that can recover the transaction\n\t\t\t\t * from failure.\n\t\t\t\t */\n\t\t\t\tForgetResults(connection);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tStartRemoteTransactionSavepointRollback(connection, subId);\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\tWaitForAllConnections(connectionList, raiseInterrupts);\n\n\t/* and wait for the results */\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\t\tif (transaction->transactionFailed && !transaction->transactionRecovering)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tFinishRemoteTransactionSavepointRollback(connection, subId);\n\n\t\t/*\n\t\t * We unclaim the connection now so it can be used again when\n\t\t * continuing after the ROLLBACK TO SAVEPOINT.\n\t\t * XXX: We do not undo our hadDML/hadDDL flags. This could result in\n\t\t * some queries not being allowed on Citus that would actually be fine\n\t\t * to execute.  Changing this would require us to keep track for each\n\t\t * savepoint which placement connections had DDL/DML executed at that\n\t\t * point and if they were already. We also do not call\n\t\t * ResetShardPlacementAssociation. This might result in suboptimal\n\t\t * parallelism, because of placement associations that are not really\n\t\t * necessary anymore because of ROLLBACK TO SAVEPOINT. To change this\n\t\t * we would need to keep track of when a connection becomes associated\n\t\t * to a placement.\n\t\t */\n\t\tUnclaimConnection(connection);\n\t}\n\n\tlist_free(connectionList);\n}\n\n\n/*\n * StartRemoteTransactionSavepointBegin initiates SAVEPOINT command for the given\n * subtransaction id in a non-blocking manner.\n */\nstatic void\nStartRemoteTransactionSavepointBegin(MultiConnection *connection, SubTransactionId subId)\n{\n\tconst bool raiseErrors = true;\n\n\t/*\n\t * We need to allocate 31 bytes for command buffer (including '\\0'):\n\t *  - len(\"SAVEPOINT savepoint_\") = 20\n\t *  - maximum length of str(subId) = 10\n\t */\n\tchar savepointCommand[31];\n\tSafeSnprintf(savepointCommand, sizeof(savepointCommand), \"SAVEPOINT savepoint_%u\",\n\t\t\t\t subId);\n\n\tif (!SendRemoteCommand(connection, savepointCommand))\n\t{\n\t\tHandleRemoteTransactionConnectionError(connection, raiseErrors);\n\t}\n}\n\n\n/*\n * FinishRemoteTransactionSavepointBegin finishes the work\n * StartRemoteTransactionSavepointBegin initiated. It blocks if necessary (i.e.\n * if PQisBusy() would return true).\n */\nstatic void\nFinishRemoteTransactionSavepointBegin(MultiConnection *connection, SubTransactionId subId)\n{\n\tconst bool raiseErrors = true;\n\tPGresult *result = GetRemoteCommandResult(connection, raiseErrors);\n\tif (!IsResponseOK(result))\n\t{\n\t\tHandleRemoteTransactionResultError(connection, result, raiseErrors);\n\t}\n\n\tPQclear(result);\n\tForgetResults(connection);\n}\n\n\n/*\n * StartRemoteTransactionSavepointRelease initiates RELEASE SAVEPOINT command for\n * the given subtransaction id in a non-blocking manner.\n */\nstatic void\nStartRemoteTransactionSavepointRelease(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t   SubTransactionId subId)\n{\n\tconst bool raiseErrors = true;\n\n\t/*\n\t * We need to allocate 39 bytes for command buffer (including '\\0'):\n\t *  - len(\"RELEASE SAVEPOINT savepoint_\") = 28\n\t *  - maximum length of str(subId) = 10\n\t */\n\tchar savepointCommand[39];\n\tSafeSnprintf(savepointCommand, sizeof(savepointCommand),\n\t\t\t\t \"RELEASE SAVEPOINT savepoint_%u\", subId);\n\n\tif (!SendRemoteCommand(connection, savepointCommand))\n\t{\n\t\tHandleRemoteTransactionConnectionError(connection, raiseErrors);\n\t}\n}\n\n\n/*\n * FinishRemoteTransactionSavepointRelease finishes the work\n * StartRemoteTransactionSavepointRelease initiated. It blocks if necessary (i.e.\n * if PQisBusy() would return true).\n */\nstatic void\nFinishRemoteTransactionSavepointRelease(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\tSubTransactionId subId)\n{\n\tconst bool raiseErrors = true;\n\tPGresult *result = GetRemoteCommandResult(connection, raiseErrors);\n\tif (!IsResponseOK(result))\n\t{\n\t\tHandleRemoteTransactionResultError(connection, result, raiseErrors);\n\t}\n\n\tPQclear(result);\n\tForgetResults(connection);\n}\n\n\n/*\n * StartRemoteTransactionSavepointRollback initiates ROLLBACK TO SAVEPOINT command\n * for the given subtransaction id in a non-blocking manner.\n */\nstatic void\nStartRemoteTransactionSavepointRollback(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\tSubTransactionId subId)\n{\n\tconst bool raiseErrors = false;\n\n\t/*\n\t * We need to allocate 43 bytes for command buffer (including '\\0'):\n\t *  - len(\"ROLLBACK TO SAVEPOINT savepoint_\") = 32\n\t *  - maximum length of str(subId) = 10\n\t */\n\tchar savepointCommand[43];\n\tSafeSnprintf(savepointCommand, sizeof(savepointCommand),\n\t\t\t\t \"ROLLBACK TO SAVEPOINT savepoint_%u\", subId);\n\n\tif (!SendRemoteCommand(connection, savepointCommand))\n\t{\n\t\tHandleRemoteTransactionConnectionError(connection, raiseErrors);\n\t}\n}\n\n\n/*\n * FinishRemoteTransactionSavepointRollback finishes the work\n * StartRemoteTransactionSavepointRollback initiated. It blocks if necessary (i.e.\n * if PQisBusy() would return true). It also recovers the transaction from failure\n * if transaction is recovering and the rollback command succeeds.\n */\nstatic void\nFinishRemoteTransactionSavepointRollback(MultiConnection *connection, SubTransactionId\n\t\t\t\t\t\t\t\t\t\t subId)\n{\n\tconst bool raiseErrors = false;\n\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\n\tPGresult *result = GetRemoteCommandResult(connection, raiseErrors);\n\tif (!IsResponseOK(result))\n\t{\n\t\tHandleRemoteTransactionResultError(connection, result, raiseErrors);\n\t}\n\n\t/* ROLLBACK TO SAVEPOINT succeeded, check if it recovers the transaction */\n\telse if (transaction->transactionRecovering)\n\t{\n\t\ttransaction->transactionFailed = false;\n\t\ttransaction->transactionRecovering = false;\n\t}\n\n\tPQclear(result);\n\tForgetResults(connection);\n\n\t/* reset transaction state so the executor can accept next commands in transaction */\n\ttransaction->transactionState = REMOTE_TRANS_STARTED;\n}\n\n\n/*\n * CheckRemoteTransactionsHealth checks if any of the participating transactions in a\n * coordinated transaction failed, and what consequence that should have.\n * This needs to be called before the coordinated transaction commits (but\n * after they've been PREPAREd if 2PC is in use).\n */\nvoid\nCheckRemoteTransactionsHealth(void)\n{\n\tdlist_iter iter;\n\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection, transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\t\tRemoteTransaction *transaction = &connection->remoteTransaction;\n\t\tPGTransactionStatusType status = PQtransactionStatus(connection->pgConn);\n\n\t\t/* if the connection is in a bad state, so is the transaction's state */\n\t\tif (status == PQTRANS_INERROR || status == PQTRANS_UNKNOWN)\n\t\t{\n\t\t\ttransaction->transactionFailed = true;\n\t\t}\n\n\t\t/*\n\t\t * If a critical connection is marked as failed (and no error has been\n\t\t * raised yet) do so now.\n\t\t */\n\t\tif (transaction->transactionFailed && transaction->transactionCritical)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"failure on connection marked as essential: %s:%d\",\n\t\t\t\t\t\t\t\t   connection->hostname, connection->port)));\n\t\t}\n\t}\n}\n\n\n/*\n * Assign2PCIdentifier computes the 2PC transaction name to use for a\n * transaction. Every prepared transaction should get a new name, i.e. this\n * function will need to be called again.\n *\n * The format of the name is:\n *\n * citus_<source group>_<pid>_<distributed transaction number>_<connection number>\n *\n * (at most 5+1+10+1+10+1+20+1+10 = 59 characters, while limit is 64)\n *\n * The source group is used to distinguish 2PCs started by different\n * coordinators. A coordinator will only attempt to recover its own 2PCs.\n *\n * The pid is used to distinguish different processes on the coordinator, mainly\n * to provide some entropy across restarts.\n *\n * The distributed transaction number is used to distinguish different\n * transactions originating from the same node (since restart).\n *\n * The connection number is used to distinguish connections made to a node\n * within the same transaction.\n *\n */\nstatic void\nAssign2PCIdentifier(MultiConnection *connection)\n{\n\t/* local sequence number used to distinguish different connections */\n\tstatic uint32 connectionNumber = 0;\n\n\t/* transaction identifier that is unique across processes */\n\tuint64 transactionNumber = CurrentDistributedTransactionNumber();\n\n\t/* print all numbers as unsigned to guarantee no minus symbols appear in the name */\n\tSafeSnprintf(connection->remoteTransaction.preparedName, NAMEDATALEN,\n\t\t\t\t PREPARED_TRANSACTION_NAME_FORMAT, GetLocalGroupId(), MyProcPid,\n\t\t\t\t transactionNumber, connectionNumber++);\n}\n\n\n/*\n * ParsePreparedTransactionName parses a prepared transaction name to extract\n * the initiator group ID, initiator process ID, distributed transaction number,\n * and the connection number. If the transaction name does not match the expected\n * format ParsePreparedTransactionName returns false, and true otherwise.\n */\nbool\nParsePreparedTransactionName(char *preparedTransactionName,\n\t\t\t\t\t\t\t int32 *groupId, int *procId,\n\t\t\t\t\t\t\t uint64 *transactionNumber,\n\t\t\t\t\t\t\t uint32 *connectionNumber)\n{\n\tchar *currentCharPointer = preparedTransactionName;\n\n\tcurrentCharPointer = strchr(currentCharPointer, '_');\n\tif (currentCharPointer == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\t/* step ahead of the current '_' character */\n\t++currentCharPointer;\n\n\t*groupId = strtol(currentCharPointer, NULL, 10);\n\n\tif ((*groupId == COORDINATOR_GROUP_ID && errno == EINVAL) ||\n\t\t(*groupId == INT_MAX && errno == ERANGE))\n\t{\n\t\treturn false;\n\t}\n\n\tcurrentCharPointer = strchr(currentCharPointer, '_');\n\tif (currentCharPointer == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\t/* step ahead of the current '_' character */\n\t++currentCharPointer;\n\n\t*procId = strtol(currentCharPointer, NULL, 10);\n\tif ((*procId == 0 && errno == EINVAL) ||\n\t\t(*procId == INT_MAX && errno == ERANGE))\n\t{\n\t\treturn false;\n\t}\n\n\tcurrentCharPointer = strchr(currentCharPointer, '_');\n\tif (currentCharPointer == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\t/* step ahead of the current '_' character */\n\t++currentCharPointer;\n\n\t*transactionNumber = strtou64(currentCharPointer, NULL, 10);\n\tif ((*transactionNumber == 0 && errno != 0) ||\n\t\t(*transactionNumber == ULLONG_MAX && errno == ERANGE))\n\t{\n\t\treturn false;\n\t}\n\n\tcurrentCharPointer = strchr(currentCharPointer, '_');\n\tif (currentCharPointer == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\t/* step ahead of the current '_' character */\n\t++currentCharPointer;\n\n\t*connectionNumber = strtoul(currentCharPointer, NULL, 10);\n\tif ((*connectionNumber == 0 && errno == EINVAL) ||\n\t\t(*connectionNumber == UINT_MAX && errno == ERANGE))\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/backend/distributed/transaction/transaction_management.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * transaction_management.c\n *\n *   Transaction management for Citus.  Most of the work is delegated to other\n *   subsystems, this files, and especially CoordinatedTransactionCallback,\n *   coordinates the work between them.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/twophase.h\"\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"common/hashfn.h\"\n#include \"nodes/print.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/fd.h\"\n#include \"utils/datum.h\"\n#include \"utils/guc.h\"\n#include \"utils/guc_tables.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/memutils.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/function_call_delegation.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/locally_reserved_shared_connections.h\"\n#include \"distributed/maintenanced.h\"\n#include \"distributed/metadata/dependency.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_explain.h\"\n#include \"distributed/multi_logical_replication.h\"\n#include \"distributed/placement_connection.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/repartition_join_execution.h\"\n#include \"distributed/replication_origin_session_utils.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/shared_connection_stats.h\"\n#include \"distributed/subplan_execution.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_log_messages.h\"\n\n#define COMMIT_MANAGEMENT_COMMAND_2PC \\\n\t\t\"SELECT citus_internal.commit_management_command_2pc()\"\n\n\nCoordinatedTransactionState CurrentCoordinatedTransactionState = COORD_TRANS_NONE;\n\n/*\n * GUC that determines whether a SELECT in a transaction block should also run in\n * a transaction block on the worker even if no writes have occurred yet.\n */\nbool SelectOpensTransactionBlock = true;\n\n/* controls use of locks to enforce safe commutativity */\nbool AllModificationsCommutative = false;\n\n/* we've deprecated this flag, keeping here for some time not to break existing users */\nbool EnableDeadlockPrevention = true;\n\n/* number of nested stored procedure call levels we are currently in */\nint StoredProcedureLevel = 0;\n\n/* number of nested DO block levels we are currently in */\nint DoBlockLevel = 0;\n\n/* state needed to keep track of operations used during a transaction */\nXactModificationType XactModificationLevel = XACT_MODIFICATION_NONE;\n\n/* list of connections that are part of the current coordinated transaction */\ndlist_head InProgressTransactions = DLIST_STATIC_INIT(InProgressTransactions);\n\n/*\n * activeSetStmts keeps track of SET LOCAL statements executed within the current\n * subxact and will be set to NULL when pushing into new subxact or ending top xact.\n */\nStringInfo activeSetStmts;\n\n/*\n * Though a list, we treat this as a stack, pushing on subxact contexts whenever\n * e.g. a SAVEPOINT is executed (though this is actually performed by providing\n * PostgreSQL with a sub-xact callback). At present, the context of a subxact\n * includes\n *  - a subxact identifier,\n *  - any SET LOCAL statements propagated to workers during the sub-transaction,\n *  - all objects propagated to workers during the sub-transaction.\n *\n * To be clear, last item of activeSubXactContexts list corresponds to top of\n * stack.\n */\nstatic List *activeSubXactContexts = NIL;\n\n/*\n * PropagatedObjectsInTx is a set of objects propagated in the root transaction.\n * We also keep track of objects propagated in sub-transactions in activeSubXactContexts.\n * Any committed sub-transaction would cause the objects, which are propagated during\n * the sub-transaction, to be moved to upper transaction's set. Objects are discarded\n * when the sub-transaction is aborted.\n */\nstatic HTAB *PropagatedObjectsInTx = NULL;\n\n/* some pre-allocated memory so we don't need to call malloc() during callbacks */\nMemoryContext CitusXactCallbackContext = NULL;\n\n/*\n * Should this coordinated transaction use 2PC? Set by\n * CoordinatedTransactionUse2PC(), e.g. if any modification\n * is issued and us 2PC. But, even if this flag is set,\n * the transaction manager is smart enough to only\n * do 2PC on the remote connections that did a modification.\n *\n * As a variable name ShouldCoordinatedTransactionUse2PC could\n * be improved. We use Use2PCForCoordinatedTransaction() as the\n * public API function, hence couldn't come up with a better name\n * for the underlying variable at the moment.\n */\nbool ShouldCoordinatedTransactionUse2PC = false;\n\n/*\n * Distribution function argument (along with colocationId) when delegated\n * using forceDelegation flag.\n */\nAllowedDistributionColumn AllowedDistributionColumnValue;\n\n/* if disabled, distributed statements in a function may run as separate transactions */\nbool FunctionOpensTransactionBlock = true;\n\n/* if true, we should trigger node metadata sync on commit */\nbool NodeMetadataSyncOnCommit = false;\n\n/*\n * In an explicit BEGIN ...; we keep track of top-level transaction characteristics\n * specified by the user.\n */\nBeginXactReadOnlyState BeginXactReadOnly = BeginXactReadOnly_NotSet;\nBeginXactDeferrableState BeginXactDeferrable = BeginXactDeferrable_NotSet;\n\n\n/* transaction management functions */\nstatic void CoordinatedTransactionCallback(XactEvent event, void *arg);\nstatic void CoordinatedSubTransactionCallback(SubXactEvent event, SubTransactionId subId,\n\t\t\t\t\t\t\t\t\t\t\t  SubTransactionId parentSubid, void *arg);\n\n/* remaining functions */\nstatic void AdjustMaxPreparedTransactions(void);\nstatic void PushSubXact(SubTransactionId subId);\nstatic void PopSubXact(SubTransactionId subId, bool commit);\nstatic void ResetGlobalVariables(void);\nstatic bool SwallowErrors(void (*func)(void));\nstatic void ForceAllInProgressConnectionsToClose(void);\nstatic void EnsurePrepareTransactionIsAllowed(void);\nstatic HTAB * CurrentTransactionPropagatedObjects(bool readonly);\nstatic HTAB * ParentTransactionPropagatedObjects(bool readonly);\nstatic void MovePropagatedObjectsToParentTransaction(void);\nstatic bool DependencyInPropagatedObjectsHash(HTAB *propagatedObjects,\n\t\t\t\t\t\t\t\t\t\t\t  const ObjectAddress *dependency);\nstatic HTAB * CreateTxPropagatedObjectsHash(void);\n\n\n/*\n * UseCoordinatedTransaction sets up the necessary variables to use\n * a coordinated transaction, unless one is already in progress.\n */\nvoid\nUseCoordinatedTransaction(void)\n{\n\tif (CurrentCoordinatedTransactionState == COORD_TRANS_STARTED)\n\t{\n\t\treturn;\n\t}\n\n\tif (CurrentCoordinatedTransactionState != COORD_TRANS_NONE &&\n\t\tCurrentCoordinatedTransactionState != COORD_TRANS_IDLE)\n\t{\n\t\tereport(ERROR, (errmsg(\"starting transaction in wrong state\")));\n\t}\n\n\tCurrentCoordinatedTransactionState = COORD_TRANS_STARTED;\n\n\t/*\n\t * If assign_distributed_transaction_id() has been called, we should reuse\n\t * that identifier so distributed deadlock detection works properly.\n\t */\n\tDistributedTransactionId *transactionId = GetCurrentDistributedTransactionId();\n\tif (transactionId->transactionNumber == 0)\n\t{\n\t\tAssignDistributedTransactionId();\n\t}\n}\n\n\n/*\n * EnsureDistributedTransactionId makes sure that the current transaction\n * has a distributed transaction id. It is either assigned by a previous\n * call of assign_distributed_transaction_id(), or by starting a coordinated\n * transaction.\n */\nvoid\nEnsureDistributedTransactionId(void)\n{\n\tDistributedTransactionId *transactionId = GetCurrentDistributedTransactionId();\n\tif (transactionId->transactionNumber == 0)\n\t{\n\t\tUseCoordinatedTransaction();\n\t}\n}\n\n\n/*\n * InCoordinatedTransaction returns whether a coordinated transaction has been\n * started.\n */\nbool\nInCoordinatedTransaction(void)\n{\n\treturn CurrentCoordinatedTransactionState != COORD_TRANS_NONE &&\n\t\t   CurrentCoordinatedTransactionState != COORD_TRANS_IDLE;\n}\n\n\n/*\n * Use2PCForCoordinatedTransaction() signals that the current coordinated\n * transaction should use 2PC to commit.\n *\n * Note that even if 2PC is enabled, it is only used for connections that make\n * modification (DML or DDL).\n */\nvoid\nUse2PCForCoordinatedTransaction(void)\n{\n\t/*\n\t * If this transaction is also a coordinated\n\t * transaction, use 2PC. Otherwise, this\n\t * state change does nothing.\n\t *\n\t * In other words, when this flag is set,\n\t * we \"should\" use 2PC when needed (e.g.,\n\t * we are in a coordinated transaction and\n\t * the coordinated transaction does a remote\n\t * modification).\n\t */\n\tShouldCoordinatedTransactionUse2PC = true;\n}\n\n\n/*\n * GetCoordinatedTransactionShouldUse2PC is a wrapper function to read the value\n * of CoordinatedTransactionShouldUse2PCFlag.\n */\nbool\nGetCoordinatedTransactionShouldUse2PC(void)\n{\n\treturn ShouldCoordinatedTransactionUse2PC;\n}\n\n\nvoid\nInitializeTransactionManagement(void)\n{\n\t/* hook into transaction machinery */\n\tRegisterXactCallback(CoordinatedTransactionCallback, NULL);\n\tRegisterSubXactCallback(CoordinatedSubTransactionCallback, NULL);\n\n\tAdjustMaxPreparedTransactions();\n\n\t/* set aside 8kb of memory for use in CoordinatedTransactionCallback */\n\tCitusXactCallbackContext = AllocSetContextCreateInternal(TopMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"CitusXactCallbackContext\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t 8 * 1024,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t 8 * 1024,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t 8 * 1024);\n}\n\n\n/*\n * Transaction management callback, handling coordinated transaction, and\n * transaction independent connection management.\n *\n * NB: There should only ever be a single transaction callback in citus, the\n * ordering between the callbacks and the actions within those callbacks\n * otherwise becomes too undeterministic / hard to reason about.\n */\nstatic void\nCoordinatedTransactionCallback(XactEvent event, void *arg)\n{\n\tswitch (event)\n\t{\n\t\tcase XACT_EVENT_COMMIT:\n\t\t{\n\t\t\t/*\n\t\t\t * ERRORs thrown during XACT_EVENT_COMMIT will cause postgres to abort, at\n\t\t\t * this point enough work has been done that it's not possible to rollback.\n\t\t\t *\n\t\t\t * One possible source of errors is memory allocation failures. To minimize\n\t\t\t * the chance of those happening we've pre-allocated some memory in the\n\t\t\t * CitusXactCallbackContext, it has 8kb of memory that we're allowed to use.\n\t\t\t *\n\t\t\t * We only do this in the COMMIT callback because:\n\t\t\t * - Errors thrown in other callbacks (such as PRE_COMMIT) won't cause\n\t\t\t *   crashes, they will simply cause the ABORT handler to be called.\n\t\t\t * - The exception is ABORT, errors thrown there could also cause crashes, but\n\t\t\t *   postgres already creates a TransactionAbortContext which performs this\n\t\t\t *   trick, so there's no need for us to do it again.\n\t\t\t */\n\t\t\tMemoryContext previousContext =\n\t\t\t\tMemoryContextSwitchTo(CitusXactCallbackContext);\n\n\t\t\tif (CurrentCoordinatedTransactionState == COORD_TRANS_PREPARED &&\n\t\t\t\t!IsMainDBCommand)\n\t\t\t{\n\t\t\t\t/* handles both already prepared and open transactions */\n\t\t\t\tCoordinatedRemoteTransactionsCommit();\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If this is a non-Citus main database we should try to commit the prepared\n\t\t\t * transactions created by the Citus main database on the worker nodes.\n\t\t\t */\n\t\t\tif (!IsMainDB && MainDBConnection != NULL && IsMainDBCommandInXact)\n\t\t\t{\n\t\t\t\tRunCitusMainDBQuery(COMMIT_MANAGEMENT_COMMAND_2PC);\n\t\t\t\tCleanCitusMainDBConnection();\n\t\t\t}\n\n\t\t\t/* close connections etc. */\n\t\t\tif (CurrentCoordinatedTransactionState != COORD_TRANS_NONE)\n\t\t\t{\n\t\t\t\tResetPlacementConnectionManagement();\n\t\t\t\tAfterXactConnectionHandling(true);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Changes to catalog tables are now visible to the metadata sync\n\t\t\t * daemon, so we can trigger node metadata sync if necessary.\n\t\t\t */\n\t\t\tif (NodeMetadataSyncOnCommit)\n\t\t\t{\n\t\t\t\tTriggerNodeMetadataSync(MyDatabaseId);\n\t\t\t}\n\n\t\t\tResetGlobalVariables();\n\t\t\tResetRelationAccessHash();\n\t\t\tResetPropagatedObjects();\n\n\t\t\t/*\n\t\t\t * Make sure that we give the shared connections back to the shared\n\t\t\t * pool if any. This operation is a no-op if the reserved connections\n\t\t\t * are already given away.\n\t\t\t */\n\t\t\tDeallocateReservedConnections();\n\n\t\t\tUnSetDistributedTransactionId();\n\n\t\t\tPlacementMovedUsingLogicalReplicationInTX = false;\n\n\t\t\t/* empty the CitusXactCallbackContext to ensure we're not leaking memory */\n\t\t\tMemoryContextSwitchTo(previousContext);\n\t\t\tMemoryContextReset(CitusXactCallbackContext);\n\n\t\t\t/* Set CreateCitusTransactionLevel to 0 since original transaction is about to be\n\t\t\t * committed.\n\t\t\t */\n\n\t\t\tif (GetCitusCreationLevel() > 0)\n\t\t\t{\n\t\t\t\t/* Check CitusCreationLevel was correctly decremented to 1 */\n\t\t\t\tAssert(GetCitusCreationLevel() == 1);\n\t\t\t\tSetCreateCitusTransactionLevel(0);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase XACT_EVENT_ABORT:\n\t\t{\n\t\t\t/* stop propagating notices from workers, we know the query is failed */\n\t\t\tDisableWorkerMessagePropagation();\n\n\t\t\tRemoveIntermediateResultsDirectories();\n\n\t\t\tCleanCitusMainDBConnection();\n\n\t\t\t/* handles both already prepared and open transactions */\n\t\t\tif (CurrentCoordinatedTransactionState > COORD_TRANS_IDLE)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Since CoordinateRemoteTransactionsAbort may cause an error and it is\n\t\t\t\t * not allowed to error out at that point, swallow the error if any.\n\t\t\t\t *\n\t\t\t\t * Particular error we've observed was CreateWaitEventSet throwing an error\n\t\t\t\t * when out of file descriptor.\n\t\t\t\t *\n\t\t\t\t * If an error is swallowed, connections of all active transactions must\n\t\t\t\t * be forced to close at the end of the transaction explicitly.\n\t\t\t\t */\n\t\t\t\tbool errorSwallowed = SwallowErrors(CoordinatedRemoteTransactionsAbort);\n\t\t\t\tif (errorSwallowed == true)\n\t\t\t\t{\n\t\t\t\t\tForceAllInProgressConnectionsToClose();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Close connections etc. Contrary to a successful transaction we reset the\n\t\t\t * placement connection management irregardless of state of the statemachine\n\t\t\t * as recorded in CurrentCoordinatedTransactionState.\n\t\t\t * The hashmaps recording the connection management live a memory context\n\t\t\t * higher compared to most of the data referenced in the hashmap. This causes\n\t\t\t * use after free errors when the contents are retained due to an error caused\n\t\t\t * before the CurrentCoordinatedTransactionState changed.\n\t\t\t */\n\t\t\tResetPlacementConnectionManagement();\n\t\t\tAfterXactConnectionHandling(false);\n\n\t\t\tResetGlobalVariables();\n\t\t\tResetRelationAccessHash();\n\t\t\tResetPropagatedObjects();\n\n\t\t\t/* Reset any local replication origin session since transaction has been aborted.*/\n\t\t\tResetReplicationOriginLocalSession();\n\n\t\t\t/* empty the CitusXactCallbackContext to ensure we're not leaking memory */\n\t\t\tMemoryContextReset(CitusXactCallbackContext);\n\n\t\t\t/*\n\t\t\t * Clear MetadataCache table if we're aborting from a CREATE EXTENSION Citus\n\t\t\t * so that any created OIDs from the table are cleared and invalidated. We\n\t\t\t * also set CreateCitusTransactionLevel to 0 since that process has been aborted\n\t\t\t */\n\t\t\tif (GetCitusCreationLevel() > 0)\n\t\t\t{\n\t\t\t\t/* Checks CitusCreationLevel correctly decremented to 1 */\n\t\t\t\tAssert(GetCitusCreationLevel() == 1);\n\n\t\t\t\tInvalidateMetadataSystemCache();\n\t\t\t\tSetCreateCitusTransactionLevel(0);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Make sure that we give the shared connections back to the shared\n\t\t\t * pool if any. This operation is a no-op if the reserved connections\n\t\t\t * are already given away.\n\t\t\t */\n\t\t\tDeallocateReservedConnections();\n\n\t\t\t/*\n\t\t\t * We reset these mainly for posterity. The only way we would normally\n\t\t\t * get here with ExecutorLevel or PlannerLevel > 0 is during a fatal\n\t\t\t * error when the process is about to end.\n\t\t\t */\n\t\t\tExecutorLevel = 0;\n\t\t\tPlannerLevel = 0;\n\n\t\t\t/*\n\t\t\t * We should reset SubPlanLevel in case a transaction is aborted,\n\t\t\t * otherwise this variable would stay +ve if the transaction is\n\t\t\t * aborted in the middle of a CTE/complex subquery execution\n\t\t\t * which would cause the subsequent queries to error out in\n\t\t\t * case the copy size is greater than\n\t\t\t * citus.max_intermediate_result_size\n\t\t\t */\n\t\t\tSubPlanLevel = 0;\n\t\t\tUnSetDistributedTransactionId();\n\n\t\t\tPlacementMovedUsingLogicalReplicationInTX = false;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase XACT_EVENT_PARALLEL_COMMIT:\n\t\tcase XACT_EVENT_PARALLEL_ABORT:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tcase XACT_EVENT_PREPARE:\n\t\t{\n\t\t\t/* we need to reset SavedExplainPlan before TopTransactionContext is deleted */\n\t\t\tFreeSavedExplainPlan();\n\n\t\t\t/*\n\t\t\t * This callback is only relevant for worker queries since\n\t\t\t * distributed queries cannot be executed with 2PC, see\n\t\t\t * XACT_EVENT_PRE_PREPARE.\n\t\t\t *\n\t\t\t * We should remove the intermediate results before unsetting the\n\t\t\t * distributed transaction id. That is necessary, otherwise Citus\n\t\t\t * would try to remove a non-existing folder and leak some of the\n\t\t\t * existing folders that are associated with distributed transaction\n\t\t\t * ids on the worker nodes.\n\t\t\t */\n\t\t\tRemoveIntermediateResultsDirectories();\n\n\t\t\tUnSetDistributedTransactionId();\n\t\t\tbreak;\n\t\t}\n\n\t\tcase XACT_EVENT_PRE_COMMIT:\n\t\t{\n\t\t\t/*\n\t\t\t * If the distributed query involves 2PC, we already removed\n\t\t\t * the intermediate result directory on XACT_EVENT_PREPARE. However,\n\t\t\t * if not, we should remove it here on the COMMIT. Since\n\t\t\t * RemoveIntermediateResultsDirectories() is idempotent, we're safe\n\t\t\t * to call it here again even if the transaction involves 2PC.\n\t\t\t */\n\t\t\tRemoveIntermediateResultsDirectories();\n\n\t\t\t/* nothing further to do if there's no managed remote xacts */\n\t\t\tif (CurrentCoordinatedTransactionState == COORD_TRANS_NONE)\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\n\t\t\t/*\n\t\t\t * If this is a non-Citus main database we should commit the Citus\n\t\t\t * main database query. So if some error happens on the distributed main\n\t\t\t * database query we wouldn't have committed the current query.\n\t\t\t */\n\t\t\tif (!IsMainDB && MainDBConnection != NULL && IsMainDBCommandInXact)\n\t\t\t{\n\t\t\t\tRunCitusMainDBQuery(\"COMMIT\");\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * TODO: It'd probably be a good idea to force constraints and\n\t\t\t * such to 'immediate' here. Deferred triggers might try to send\n\t\t\t * stuff to the remote side, which'd not be good.  Doing so\n\t\t\t * remotely would also catch a class of errors where committing\n\t\t\t * fails, which can lead to divergence when not using 2PC.\n\t\t\t */\n\n\t\t\tif (ShouldCoordinatedTransactionUse2PC)\n\t\t\t{\n\t\t\t\tCoordinatedRemoteTransactionsPrepare();\n\t\t\t\tCurrentCoordinatedTransactionState = COORD_TRANS_PREPARED;\n\n\t\t\t\t/*\n\t\t\t\t * Make sure we did not have any failures on connections marked as\n\t\t\t\t * critical before committing.\n\t\t\t\t */\n\t\t\t\tCheckRemoteTransactionsHealth();\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tCheckRemoteTransactionsHealth();\n\n\t\t\t\t/*\n\t\t\t\t * Have to commit remote transactions in PRE_COMMIT, to allow\n\t\t\t\t * us to mark failed placements as invalid.  Better don't use\n\t\t\t\t * this for anything important (i.e. DDL/metadata).\n\t\t\t\t */\n\t\t\t\tif (IsMainDB)\n\t\t\t\t{\n\t\t\t\t\tCoordinatedRemoteTransactionsCommit();\n\t\t\t\t}\n\t\t\t\tCurrentCoordinatedTransactionState = COORD_TRANS_COMMITTED;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Check again whether shards/placement successfully\n\t\t\t * committed. This handles failure at COMMIT time.\n\t\t\t */\n\t\t\tErrorIfPostCommitFailedShardPlacements();\n\t\t\tbreak;\n\t\t}\n\n\t\tcase XACT_EVENT_PARALLEL_PRE_COMMIT:\n\t\tcase XACT_EVENT_PRE_PREPARE:\n\t\t{\n\t\t\tEnsurePrepareTransactionIsAllowed();\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n/*\n * ForceAllInProgressConnectionsToClose forces all connections of in progress transactions\n * to close at the end of the transaction.\n */\nstatic void\nForceAllInProgressConnectionsToClose(void)\n{\n\tdlist_iter iter;\n\tdlist_foreach(iter, &InProgressTransactions)\n\t{\n\t\tMultiConnection *connection = dlist_container(MultiConnection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  transactionNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  iter.cur);\n\n\t\tconnection->forceCloseAtTransactionEnd = true;\n\t}\n}\n\n\n/*\n * If an ERROR is thrown while processing a transaction the ABORT handler is called.\n * ERRORS thrown during ABORT are not treated any differently, the ABORT handler is also\n * called during processing of those. If an ERROR was raised the first time through it's\n * unlikely that the second try will succeed; more likely that an ERROR will be thrown\n * again. This loop continues until Postgres notices and PANICs, complaining about a stack\n * overflow.\n *\n * Instead of looping and crashing, SwallowErrors lets us attempt to continue running the\n * ABORT logic. This wouldn't be safe in most other parts of the codebase, in\n * approximately none of the places where we emit ERROR do we first clean up after\n * ourselves! It's fine inside the ABORT handler though; Postgres is going to clean\n * everything up before control passes back to us.\n *\n * If it swallows any error, returns true. Otherwise, returns false.\n */\nstatic bool\nSwallowErrors(void (*func)())\n{\n\tMemoryContext savedContext = CurrentMemoryContext;\n\tvolatile bool anyErrorSwallowed = false;\n\n\tPG_TRY();\n\t{\n\t\tfunc();\n\t}\n\tPG_CATCH();\n\t{\n\t\tMemoryContextSwitchTo(savedContext);\n\t\tErrorData *edata = CopyErrorData();\n\t\tFlushErrorState();\n\n\t\t/* rethrow as WARNING */\n\t\tedata->elevel = WARNING;\n\t\tThrowErrorData(edata);\n\n\t\tanyErrorSwallowed = true;\n\t}\n\tPG_END_TRY();\n\n\treturn anyErrorSwallowed;\n}\n\n\n/*\n * ResetGlobalVariables resets global variables that\n * might be changed during the execution of queries.\n */\nstatic void\nResetGlobalVariables()\n{\n\tCurrentCoordinatedTransactionState = COORD_TRANS_NONE;\n\tXactModificationLevel = XACT_MODIFICATION_NONE;\n\tSetLocalExecutionStatus(LOCAL_EXECUTION_OPTIONAL);\n\tFreeSavedExplainPlan();\n\tdlist_init(&InProgressTransactions);\n\tactiveSetStmts = NULL;\n\tShouldCoordinatedTransactionUse2PC = false;\n\tTransactionModifiedNodeMetadata = false;\n\tNodeMetadataSyncOnCommit = false;\n\tInTopLevelDelegatedFunctionCall = false;\n\tInTableTypeConversionFunctionCall = false;\n\tCurrentOperationId = INVALID_OPERATION_ID;\n\tBeginXactReadOnly = BeginXactReadOnly_NotSet;\n\tBeginXactDeferrable = BeginXactDeferrable_NotSet;\n\tResetWorkerErrorIndication();\n\tmemset(&AllowedDistributionColumnValue, 0,\n\t\t   sizeof(AllowedDistributionColumn));\n}\n\n\n/*\n * CoordinatedSubTransactionCallback is the callback used to implement\n * distributed ROLLBACK TO SAVEPOINT.\n */\nstatic void\nCoordinatedSubTransactionCallback(SubXactEvent event, SubTransactionId subId,\n\t\t\t\t\t\t\t\t  SubTransactionId parentSubid, void *arg)\n{\n\tswitch (event)\n\t{\n\t\t/*\n\t\t * Our sub-transaction stack should be consistent with postgres' internal\n\t\t * transaction stack. In case of subxact begin, postgres calls our\n\t\t * callback after it has pushed the transaction into stack, so we have to\n\t\t * do the same even if worker commands fail, so we PushSubXact() first.\n\t\t * In case of subxact commit, callback is called before pushing subxact to\n\t\t * the postgres transaction stack, so we call PopSubXact() after making sure\n\t\t * worker commands didn't fail. Otherwise, Postgres would roll back that\n\t\t * would cause us to call PopSubXact again.\n\t\t */\n\t\tcase SUBXACT_EVENT_START_SUB:\n\t\t{\n\t\t\tMemoryContext previousContext =\n\t\t\t\tMemoryContextSwitchTo(CitusXactCallbackContext);\n\n\t\t\tPushSubXact(subId);\n\t\t\tif (InCoordinatedTransaction())\n\t\t\t{\n\t\t\t\tCoordinatedRemoteTransactionsSavepointBegin(subId);\n\t\t\t}\n\n\t\t\tMemoryContextSwitchTo(previousContext);\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase SUBXACT_EVENT_COMMIT_SUB:\n\t\t{\n\t\t\tMemoryContext previousContext =\n\t\t\t\tMemoryContextSwitchTo(CitusXactCallbackContext);\n\n\t\t\tif (InCoordinatedTransaction())\n\t\t\t{\n\t\t\t\tCoordinatedRemoteTransactionsSavepointRelease(subId);\n\t\t\t}\n\t\t\tPopSubXact(subId, true);\n\n\t\t\t/* Set CachedDuringCitusCreation to one level lower to represent citus creation is done */\n\n\t\t\tif (GetCitusCreationLevel() == GetCurrentTransactionNestLevel())\n\t\t\t{\n\t\t\t\tSetCreateCitusTransactionLevel(GetCitusCreationLevel() - 1);\n\t\t\t}\n\n\t\t\tMemoryContextSwitchTo(previousContext);\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase SUBXACT_EVENT_ABORT_SUB:\n\t\t{\n\t\t\tMemoryContext previousContext =\n\t\t\t\tMemoryContextSwitchTo(CitusXactCallbackContext);\n\n\t\t\t/*\n\t\t\t * Stop showing message for now, will re-enable when executing\n\t\t\t * the next statement.\n\t\t\t */\n\t\t\tDisableWorkerMessagePropagation();\n\n\t\t\t/*\n\t\t\t * Given that we aborted, worker error indications can be ignored.\n\t\t\t */\n\t\t\tResetWorkerErrorIndication();\n\n\t\t\tif (InCoordinatedTransaction())\n\t\t\t{\n\t\t\t\tCoordinatedRemoteTransactionsSavepointRollback(subId);\n\t\t\t}\n\t\t\tPopSubXact(subId, false);\n\n\t\t\t/*\n\t\t\t * Clear MetadataCache table if we're aborting from a CREATE EXTENSION Citus\n\t\t\t * so that any created OIDs from the table are cleared and invalidated. We\n\t\t\t * also set CreateCitusTransactionLevel to 0 since subtransaction has been aborted\n\t\t\t */\n\t\t\tif (GetCitusCreationLevel() == GetCurrentTransactionNestLevel())\n\t\t\t{\n\t\t\t\tInvalidateMetadataSystemCache();\n\t\t\t\tSetCreateCitusTransactionLevel(0);\n\t\t\t}\n\n\t\t\t/* Reset any local replication origin session since subtransaction has been aborted.*/\n\t\t\tResetReplicationOriginLocalSession();\n\t\t\tMemoryContextSwitchTo(previousContext);\n\n\t\t\tbreak;\n\t\t}\n\n\t\tcase SUBXACT_EVENT_PRE_COMMIT_SUB:\n\t\t{\n\t\t\t/* nothing to do */\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n/*\n * AdjustMaxPreparedTransactions configures the number of available prepared\n * transaction slots at startup.\n */\nstatic void\nAdjustMaxPreparedTransactions(void)\n{\n\t/*\n\t * As Citus uses 2PC internally, there always should be some available. As\n\t * the default is 0, we increase it to something appropriate\n\t * (connections * 2 currently).  If the user explicitly configured 2PC, we\n\t * leave the configuration alone - there might have been intent behind the\n\t * decision.\n\t *\n\t * find_option is declared static in guc.c for older versions, so we can't\n\t * really check if max_prepared_xacts is configured by the user explicitly,\n\t * so check if it's value is default.\n\t */\n\tstruct config_generic *gconf = find_option(\"max_prepared_transactions\",\n\t\t\t\t\t\t\t\t\t\t\t   false, false, ERROR);\n\tif (gconf->source == PGC_S_DEFAULT)\n\t{\n\t\tchar newvalue[12];\n\n\t\tSafeSnprintf(newvalue, sizeof(newvalue), \"%d\", MaxConnections * 2);\n\n\t\tSetConfigOption(\"max_prepared_transactions\", newvalue, PGC_POSTMASTER,\n\t\t\t\t\t\tPGC_S_OVERRIDE);\n\n\t\tereport(LOG, (errmsg(\"number of prepared transactions has not been \"\n\t\t\t\t\t\t\t \"configured, overriding\"),\n\t\t\t\t\t  errdetail(\"max_prepared_transactions is now set to %s\",\n\t\t\t\t\t\t\t\tnewvalue)));\n\t}\n}\n\n\n/* PushSubXact pushes subId to the stack of active sub-transactions. */\nstatic void\nPushSubXact(SubTransactionId subId)\n{\n\t/* save provided subId as well as propagated SET LOCAL stmts */\n\tSubXactContext *state = palloc(sizeof(SubXactContext));\n\tstate->subId = subId;\n\tstate->setLocalCmds = activeSetStmts;\n\n\t/* we lazily create hashset when any object is propagated during sub-transaction */\n\tstate->propagatedObjects = NULL;\n\n\t/* append to list and reset active set stmts for upcoming sub-xact */\n\tactiveSubXactContexts = lappend(activeSubXactContexts, state);\n\tactiveSetStmts = makeStringInfo();\n}\n\n\n/* PopSubXact pops subId from the stack of active sub-transactions. */\nstatic void\nPopSubXact(SubTransactionId subId, bool commit)\n{\n\tSubXactContext *state = llast(activeSubXactContexts);\n\n\tAssert(state->subId == subId);\n\n\t/*\n\t * Free activeSetStmts to avoid memory leaks when we create subxacts\n\t * for each row, e.g. in exception handling of UDFs.\n\t */\n\tif (activeSetStmts != NULL)\n\t{\n\t\tpfree(activeSetStmts->data);\n\t\tpfree(activeSetStmts);\n\t}\n\n\t/*\n\t * SET LOCAL commands are local to subxact blocks. When a subxact commits\n\t * or rolls back, we should roll back our set of SET LOCAL commands to the\n\t * ones we had in the upper commit.\n\t */\n\tactiveSetStmts = state->setLocalCmds;\n\n\t/*\n\t * Keep subtransaction's propagated objects at parent transaction\n\t * if subtransaction committed. Otherwise, discard them.\n\t */\n\tif (commit)\n\t{\n\t\tMovePropagatedObjectsToParentTransaction();\n\t}\n\thash_destroy(state->propagatedObjects);\n\n\t/*\n\t * Free state to avoid memory leaks when we create subxacts for each row,\n\t * e.g. in exception handling of UDFs.\n\t */\n\tpfree(state);\n\n\tactiveSubXactContexts = list_delete_last(activeSubXactContexts);\n}\n\n\n/* ActiveSubXactContexts returns the list of active sub-xact context in temporal order. */\nList *\nActiveSubXactContexts(void)\n{\n\treturn activeSubXactContexts;\n}\n\n\n/*\n * IsMultiStatementTransaction determines whether the current statement is\n * part of a bigger multi-statement transaction. This is the case when the\n * statement is wrapped in a transaction block (comes after BEGIN), or it\n * is called from a stored procedure or function.\n */\nbool\nIsMultiStatementTransaction(void)\n{\n\tif (IsTransactionBlock())\n\t{\n\t\t/* in a BEGIN...END block */\n\t\treturn true;\n\t}\n\telse if (DoBlockLevel > 0)\n\t{\n\t\t/* in (a transaction within) a do block */\n\t\treturn true;\n\t}\n\telse if (StoredProcedureLevel > 0)\n\t{\n\t\t/* in (a transaction within) a stored procedure */\n\t\treturn true;\n\t}\n\telse if (MaybeExecutingUDF() && FunctionOpensTransactionBlock)\n\t{\n\t\t/* in a language-handler function call, open a transaction if configured to do so */\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\treturn false;\n\t}\n}\n\n\n/*\n * MaybeExecutingUDF returns true if we are possibly executing a function call.\n * We use nested level of executor to check this, so this can return true for\n * CTEs, etc. which also start nested executors.\n *\n * If the planner is being called from the executor, then we may also be in\n * a UDF.\n */\nbool\nMaybeExecutingUDF(void)\n{\n\treturn ExecutorLevel > 1 || (ExecutorLevel == 1 && PlannerLevel > 0);\n}\n\n\n/*\n * TriggerNodeMetadataSyncOnCommit sets a flag to do node metadata sync\n * on commit. This is because new metadata only becomes visible to the\n * metadata sync daemon after commit happens.\n */\nvoid\nTriggerNodeMetadataSyncOnCommit(void)\n{\n\tNodeMetadataSyncOnCommit = true;\n}\n\n\n/*\n * Function raises an exception, if the current backend started a coordinated\n * transaction and got a PREPARE event to become a participant in a 2PC\n * transaction coordinated by another node.\n */\nstatic void\nEnsurePrepareTransactionIsAllowed(void)\n{\n\tif (!InCoordinatedTransaction())\n\t{\n\t\t/* If the backend has not started a coordinated transaction. */\n\t\treturn;\n\t}\n\n\tif (IsCitusInternalBackend())\n\t{\n\t\t/*\n\t\t * If this is a Citus-initiated backend.\n\t\t */\n\t\treturn;\n\t}\n\n\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\terrmsg(\"cannot use 2PC in transactions involving \"\n\t\t\t\t\t\t   \"multiple servers\")));\n}\n\n\n/*\n * CurrentTransactionPropagatedObjects returns the objects propagated in current\n * sub-transaction or the root transaction if no sub-transaction exists.\n *\n * If the propagated objects are readonly it will not create the hashmap if it does not\n * already exist in the current sub-transaction.\n */\nstatic HTAB *\nCurrentTransactionPropagatedObjects(bool readonly)\n{\n\tif (activeSubXactContexts == NIL)\n\t{\n\t\t/* hashset in the root transaction if there is no sub-transaction */\n\t\tif (PropagatedObjectsInTx == NULL && !readonly)\n\t\t{\n\t\t\t/* lazily create hashset for root transaction, for mutating uses */\n\t\t\tPropagatedObjectsInTx = CreateTxPropagatedObjectsHash();\n\t\t}\n\t\treturn PropagatedObjectsInTx;\n\t}\n\n\t/* hashset in top level sub-transaction */\n\tSubXactContext *state = llast(activeSubXactContexts);\n\tif (state->propagatedObjects == NULL && !readonly)\n\t{\n\t\t/* lazily create hashset for sub-transaction, for mutating uses */\n\t\tstate->propagatedObjects = CreateTxPropagatedObjectsHash();\n\t}\n\treturn state->propagatedObjects;\n}\n\n\n/*\n * ParentTransactionPropagatedObjects returns the objects propagated in parent\n * transaction of active sub-transaction. It returns the root transaction if\n * no sub-transaction exists.\n *\n * If the propagated objects are readonly it will not create the hashmap if it does not\n * already exist in the target sub-transaction.\n */\nstatic HTAB *\nParentTransactionPropagatedObjects(bool readonly)\n{\n\tint nestingLevel = list_length(activeSubXactContexts);\n\tif (nestingLevel <= 1)\n\t{\n\t\t/*\n\t\t * The parent is the root transaction, when there is single level sub-transaction\n\t\t * or no sub-transaction.\n\t\t */\n\t\tif (PropagatedObjectsInTx == NULL && !readonly)\n\t\t{\n\t\t\t/* lazily create hashset for root transaction, for mutating uses */\n\t\t\tPropagatedObjectsInTx = CreateTxPropagatedObjectsHash();\n\t\t}\n\t\treturn PropagatedObjectsInTx;\n\t}\n\n\t/* parent is upper sub-transaction */\n\tAssert(nestingLevel >= 2);\n\tSubXactContext *state = list_nth(activeSubXactContexts, nestingLevel - 2);\n\tif (state->propagatedObjects == NULL && !readonly)\n\t{\n\t\t/* lazily create hashset for parent sub-transaction */\n\t\tstate->propagatedObjects = CreateTxPropagatedObjectsHash();\n\t}\n\treturn state->propagatedObjects;\n}\n\n\n/*\n * MovePropagatedObjectsToParentTransaction moves all objects propagated in the current\n * sub-transaction to the parent transaction. This should only be called when there is\n * active sub-transaction.\n */\nstatic void\nMovePropagatedObjectsToParentTransaction(void)\n{\n\tAssert(llast(activeSubXactContexts) != NULL);\n\tHTAB *currentPropagatedObjects = CurrentTransactionPropagatedObjects(true);\n\tif (currentPropagatedObjects == NULL)\n\t{\n\t\t/* nothing to move */\n\t\treturn;\n\t}\n\n\t/*\n\t * Only after we know we have objects to move into the parent do we get a handle on\n\t * a guaranteed existing parent hash table. This makes sure that the parents only\n\t * get populated once there are objects to be tracked.\n\t */\n\tHTAB *parentPropagatedObjects = ParentTransactionPropagatedObjects(false);\n\n\tHASH_SEQ_STATUS propagatedObjectsSeq;\n\thash_seq_init(&propagatedObjectsSeq, currentPropagatedObjects);\n\tObjectAddress *objectAddress = NULL;\n\twhile ((objectAddress = hash_seq_search(&propagatedObjectsSeq)) != NULL)\n\t{\n\t\thash_search(parentPropagatedObjects, objectAddress, HASH_ENTER, NULL);\n\t}\n}\n\n\n/*\n * DependencyInPropagatedObjectsHash checks if dependency is in given hashset\n * of propagated objects.\n */\nstatic bool\nDependencyInPropagatedObjectsHash(HTAB *propagatedObjects, const\n\t\t\t\t\t\t\t\t  ObjectAddress *dependency)\n{\n\tif (propagatedObjects == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tbool found = false;\n\thash_search(propagatedObjects, dependency, HASH_FIND, &found);\n\treturn found;\n}\n\n\n/*\n * CreateTxPropagatedObjectsHash creates a hashset to keep track of the objects\n * propagated in the current root transaction or sub-transaction.\n */\nstatic HTAB *\nCreateTxPropagatedObjectsHash(void)\n{\n\tHASHCTL info;\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = sizeof(ObjectAddress);\n\tinfo.entrysize = sizeof(ObjectAddress);\n\tinfo.hash = tag_hash;\n\tinfo.hcxt = CitusXactCallbackContext;\n\n\tint hashFlags = (HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION);\n\treturn hash_create(\"Tx Propagated Objects\", 16, &info, hashFlags);\n}\n\n\n/*\n * TrackPropagatedObject adds given object into the objects propagated in the current\n * sub-transaction.\n */\nvoid\nTrackPropagatedObject(const ObjectAddress *objectAddress)\n{\n\tHTAB *currentPropagatedObjects = CurrentTransactionPropagatedObjects(false);\n\thash_search(currentPropagatedObjects, objectAddress, HASH_ENTER, NULL);\n}\n\n\n/*\n * TrackPropagatedTableAndSequences adds given table and its sequences to the objects\n * propagated in the current sub-transaction.\n */\nvoid\nTrackPropagatedTableAndSequences(Oid relationId)\n{\n\t/* track table */\n\tObjectAddress *tableAddress = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*tableAddress, RelationRelationId, relationId);\n\tTrackPropagatedObject(tableAddress);\n\n\t/* track its sequences */\n\tList *ownedSeqIdList = getOwnedSequences(relationId);\n\tOid ownedSeqId = InvalidOid;\n\tforeach_declared_oid(ownedSeqId, ownedSeqIdList)\n\t{\n\t\tObjectAddress *seqAddress = palloc0(sizeof(ObjectAddress));\n\t\tObjectAddressSet(*seqAddress, RelationRelationId, ownedSeqId);\n\t\tTrackPropagatedObject(seqAddress);\n\t}\n}\n\n\n/*\n * ResetPropagatedObjects destroys hashset of propagated objects in the root transaction.\n */\nvoid\nResetPropagatedObjects(void)\n{\n\thash_destroy(PropagatedObjectsInTx);\n\tPropagatedObjectsInTx = NULL;\n}\n\n\n/*\n * HasAnyObjectInPropagatedObjects decides if any of the objects in given list are\n * propagated in the current transaction.\n */\nbool\nHasAnyObjectInPropagatedObjects(List *objectList)\n{\n\tObjectAddress *object = NULL;\n\tforeach_declared_ptr(object, objectList)\n\t{\n\t\t/* first search in root transaction */\n\t\tif (DependencyInPropagatedObjectsHash(PropagatedObjectsInTx, object))\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\t/* search in all nested sub-transactions */\n\t\tif (activeSubXactContexts == NIL)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tSubXactContext *state = NULL;\n\t\tforeach_declared_ptr(state, activeSubXactContexts)\n\t\t{\n\t\t\tif (DependencyInPropagatedObjectsHash(state->propagatedObjects, object))\n\t\t\t{\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "src/backend/distributed/transaction/transaction_recovery.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * transaction_recovery.c\n *\n * Routines for recovering two-phase commits started by this node if a\n * failure occurs between prepare and commit/abort.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/relscan.h\"\n#include \"access/xact.h\"\n#include \"catalog/indexing.h\"\n#include \"lib/stringinfo.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/lock.h\"\n#include \"storage/procarray.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/memutils.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n#include \"utils/xid8.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/pg_dist_transaction.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/transaction_recovery.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_manager.h\"\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(recover_prepared_transactions);\n\n\n/* Local functions forward declarations */\nstatic int RecoverWorkerTransactions(WorkerNode *workerNode,\n\t\t\t\t\t\t\t\t\t MultiConnection *connection);\nstatic List * PendingWorkerTransactionList(MultiConnection *connection);\nstatic bool IsTransactionInProgress(HTAB *activeTransactionNumberSet,\n\t\t\t\t\t\t\t\t\tchar *preparedTransactionName);\nstatic bool RecoverPreparedTransactionOnWorker(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t   char *transactionName, bool shouldCommit);\n\n\n/*\n * recover_prepared_transactions recovers any pending prepared\n * transactions started by this node on other nodes.\n */\nDatum\nrecover_prepared_transactions(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tint recoveredTransactionCount = RecoverTwoPhaseCommits();\n\n\tPG_RETURN_INT32(recoveredTransactionCount);\n}\n\n\n/*\n * LogTransactionRecord registers the fact that a transaction has been\n * prepared on a worker. The presence of this record indicates that the\n * prepared transaction should be committed.\n */\nvoid\nLogTransactionRecord(int32 groupId, char *transactionName, FullTransactionId outerXid)\n{\n\t/* open transaction relation */\n\tRelation pgDistTransaction = table_open(DistTransactionRelationId(),\n\t\t\t\t\t\t\t\t\t\t\tRowExclusiveLock);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistTransaction);\n\n\t/* form new transaction tuple */\n\tDatum *values = (Datum *) palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNulls = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tvalues[Anum_pg_dist_transaction_groupid - 1] = Int32GetDatum(groupId);\n\tvalues[Anum_pg_dist_transaction_gid - 1] = CStringGetTextDatum(transactionName);\n\tvalues[GetOuterXidAttrIndexInPgDistTransaction(tupleDescriptor)] =\n\t\tFullTransactionIdGetDatum(outerXid);\n\n\tHeapTuple heapTuple = heap_form_tuple(tupleDescriptor, values, isNulls);\n\n\t/* insert new tuple */\n\tPushActiveSnapshot(GetTransactionSnapshot());\n\tCatalogTupleInsert(pgDistTransaction, heapTuple);\n\tPopActiveSnapshot();\n\n\tCommandCounterIncrement();\n\n\t/* close relation and invalidate previous cache entry */\n\ttable_close(pgDistTransaction, NoLock);\n\n\tpfree(values);\n\tpfree(isNulls);\n}\n\n\n/*\n * RecoverTwoPhaseCommits recovers any pending prepared\n * transactions started by this node on other nodes.\n */\nint\nRecoverTwoPhaseCommits(void)\n{\n\tint recoveredTransactionCount = 0;\n\n\t/* take advisory lock first to avoid running concurrently */\n\tLockTransactionRecovery(ShareUpdateExclusiveLock);\n\n\tList *workerList = ActivePrimaryNodeList(NoLock);\n\tList *workerConnections = NIL;\n\tWorkerNode *workerNode = NULL;\n\tMultiConnection *connection = NULL;\n\n\t/*\n\t * Pre-establish all connections to worker nodes.\n\t *\n\t * We do this to enforce a consistent lock acquisition order and prevent deadlocks.\n\t * Currently, during extension updates, we take strong locks on the Citus\n\t * catalog tables in a specific order: first on pg_dist_authinfo, then on\n\t * pg_dist_transaction. It's critical that any operation locking these two\n\t * tables adheres to this order, or a deadlock could occur.\n\t *\n\t * Note that RecoverWorkerTransactions() retains its lock until the end\n\t * of the transaction, while GetNodeConnection() releases its lock after\n\t * the catalog lookup. So when there are multiple workers in the active primary\n\t * node list, the lock acquisition order may reverse in subsequent iterations\n\t * of the loop calling RecoverWorkerTransactions(), increasing the risk\n\t * of deadlock.\n\t *\n\t * By establishing all worker connections upfront, we ensure that\n\t * RecoverWorkerTransactions() deals with a single distributed catalog table,\n\t * thereby preventing deadlocks regardless of the lock acquisition sequence\n\t * used in the upgrade extension script.\n\t */\n\n\tforeach_declared_ptr(workerNode, workerList)\n\t{\n\t\tint connectionFlags = 0;\n\t\tchar *nodeName = workerNode->workerName;\n\t\tint nodePort = workerNode->workerPort;\n\n\t\tconnection = GetNodeConnection(connectionFlags, nodeName, nodePort);\n\t\tAssert(connection != NULL);\n\n\t\t/*\n\t\t * We don't verify connection validity here.\n\t\t * Instead, RecoverWorkerTransactions() performs the necessary\n\t\t * sanity checks on the connection state.\n\t\t */\n\t\tworkerConnections = lappend(workerConnections, connection);\n\t}\n\tforboth_ptr(workerNode, workerList, connection, workerConnections)\n\t{\n\t\trecoveredTransactionCount += RecoverWorkerTransactions(workerNode, connection);\n\t}\n\n\treturn recoveredTransactionCount;\n}\n\n\n/*\n * RecoverWorkerTransactions recovers any pending prepared transactions\n * started by this node on the specified worker.\n */\nstatic int\nRecoverWorkerTransactions(WorkerNode *workerNode, MultiConnection *connection)\n{\n\tint recoveredTransactionCount = 0;\n\n\tint32 groupId = workerNode->groupId;\n\tchar *nodeName = workerNode->workerName;\n\tint nodePort = workerNode->workerPort;\n\n\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\tbool indexOK = true;\n\tHeapTuple heapTuple = NULL;\n\n\tHASH_SEQ_STATUS status;\n\n\tbool recoveryFailed = false;\n\n\tAssert(connection != NULL);\n\tif (connection->pgConn == NULL || PQstatus(connection->pgConn) != CONNECTION_OK)\n\t{\n\t\tereport(WARNING, (errmsg(\"transaction recovery cannot connect to %s:%d\",\n\t\t\t\t\t\t\t\t nodeName, nodePort)));\n\n\t\treturn 0;\n\t}\n\n\tMemoryContext localContext = AllocSetContextCreateInternal(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"RecoverWorkerTransactions\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_MINSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_INITSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_MAXSIZE);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\tRelation pgDistTransaction = table_open(DistTransactionRelationId(),\n\t\t\t\t\t\t\t\t\t\t\tRowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistTransaction);\n\n\t/*\n\t * We're going to check the list of prepared transactions on the worker,\n\t * but some of those prepared transactions might belong to ongoing\n\t * distributed transactions.\n\t *\n\t * We could avoid this by temporarily blocking new prepared transactions\n\t * from being created by taking an ExclusiveLock on pg_dist_transaction.\n\t * However, this hurts write performance, so instead we avoid blocking\n\t * by consulting the list of active distributed transactions, and follow\n\t * a carefully chosen order to avoid race conditions:\n\t *\n\t * 1) P = prepared transactions on worker\n\t * 2) A = active distributed transactions\n\t * 3) T = pg_dist_transaction snapshot\n\t * 4) Q = prepared transactions on worker\n\t *\n\t * By observing A after P, we get a conclusive answer to which distributed\n\t * transactions we observed in P are still in progress. It is safe to recover\n\t * the transactions in P - A based on the presence or absence of a record\n\t * in T.\n\t *\n\t * We also remove records in T if there is no prepared transaction, which\n\t * we assume means the transaction committed. However, a transaction could\n\t * have left prepared transactions and committed between steps 1 and 2.\n\t * In that case, we would incorrectly remove the records, while the\n\t * prepared transaction is still in place.\n\t *\n\t * We therefore observe the set of prepared transactions one more time in\n\t * step 4. The aforementioned transactions would show up in Q, but not in\n\t * P. We can skip those transactions and recover them later.\n\t */\n\n\t/* find stale prepared transactions on the remote node */\n\tList *pendingTransactionList = PendingWorkerTransactionList(connection);\n\tHTAB *pendingTransactionSet = ListToHashSet(pendingTransactionList, NAMEDATALEN,\n\t\t\t\t\t\t\t\t\t\t\t\ttrue);\n\n\t/* find in-progress distributed transactions */\n\tList *activeTransactionNumberList = ActiveDistributedTransactionNumbers();\n\tHTAB *activeTransactionNumberSet = ListToHashSet(activeTransactionNumberList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t sizeof(uint64), false);\n\n\t/* scan through all recovery records of the current worker */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_transaction_groupid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(groupId));\n\n\t/* get a snapshot of pg_dist_transaction */\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistTransaction,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistTransactionGroupIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\t/* find stale prepared transactions on the remote node */\n\tList *recheckTransactionList = PendingWorkerTransactionList(connection);\n\tHTAB *recheckTransactionSet = ListToHashSet(recheckTransactionList, NAMEDATALEN,\n\t\t\t\t\t\t\t\t\t\t\t\ttrue);\n\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tbool isNull = false;\n\t\tbool foundPreparedTransactionBeforeCommit = false;\n\t\tbool foundPreparedTransactionAfterCommit = false;\n\n\t\tDatum transactionNameDatum = heap_getattr(heapTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t  Anum_pg_dist_transaction_gid,\n\t\t\t\t\t\t\t\t\t\t\t\t  tupleDescriptor, &isNull);\n\t\tchar *transactionName = TextDatumGetCString(transactionNameDatum);\n\n\t\tbool isTransactionInProgress = IsTransactionInProgress(activeTransactionNumberSet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   transactionName);\n\t\tif (isTransactionInProgress)\n\t\t{\n\t\t\t/*\n\t\t\t * Do not touch in progress transactions as we might mistakenly\n\t\t\t * commit a transaction that is actually in the process of\n\t\t\t * aborting or vice-versa.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tbool outerXidIsNull = false;\n\t\tDatum outerXidDatum = 0;\n\t\tif (EnableVersionChecks ||\n\t\t\tSearchSysCacheExistsAttName(DistTransactionRelationId(), \"outer_xid\"))\n\t\t{\n\t\t\t/* Check if the transaction is created by an outer transaction from a non-main database */\n\t\t\touterXidDatum =\n\t\t\t\theap_getattr(heapTuple,\n\t\t\t\t\t\t\t GetOuterXidAttrIndexInPgDistTransaction(tupleDescriptor) + 1,\n\t\t\t\t\t\t\t tupleDescriptor, &outerXidIsNull);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * Normally we don't try to recover prepared transactions when the\n\t\t\t * binary version doesn't match the sql version. However, we skip\n\t\t\t * those checks in regression tests by disabling\n\t\t\t * citus.enable_version_checks. And when this is the case, while\n\t\t\t * the C code looks for \"outer_xid\" attribute, pg_dist_transaction\n\t\t\t * doesn't yet have it.\n\t\t\t */\n\t\t\tAssert(!EnableVersionChecks);\n\t\t}\n\n\t\tTransactionId outerXid = 0;\n\t\tif (!outerXidIsNull)\n\t\t{\n\t\t\tFullTransactionId outerFullXid = DatumGetFullTransactionId(outerXidDatum);\n\t\t\touterXid = XidFromFullTransactionId(outerFullXid);\n\t\t}\n\n\t\tif (outerXid != 0)\n\t\t{\n\t\t\tbool outerXactIsInProgress = TransactionIdIsInProgress(outerXid);\n\t\t\tbool outerXactDidCommit = TransactionIdDidCommit(outerXid);\n\t\t\tif (outerXactIsInProgress && !outerXactDidCommit)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * The transaction is initiated from an outer transaction and the outer\n\t\t\t\t * transaction is not yet committed, so we should not commit either.\n\t\t\t\t * We remove this transaction from the pendingTransactionSet so it'll\n\t\t\t\t * not be aborted by the loop below.\n\t\t\t\t */\n\t\t\t\thash_search(pendingTransactionSet, transactionName, HASH_REMOVE,\n\t\t\t\t\t\t\t&foundPreparedTransactionBeforeCommit);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse if (!outerXactIsInProgress && !outerXactDidCommit)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Since outer transaction isn't in progress and did not commit we need to\n\t\t\t\t * abort the prepared transaction too. We do this by simply doing the same\n\t\t\t\t * thing we would  do for transactions that are initiated from the main\n\t\t\t\t * database.\n\t\t\t\t */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Outer transaction did commit, so we can try to commit the prepared\n\t\t\t\t * transaction too.\n\t\t\t\t */\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Remove the transaction from the pending list such that only transactions\n\t\t * that need to be aborted remain at the end.\n\t\t */\n\t\thash_search(pendingTransactionSet, transactionName, HASH_REMOVE,\n\t\t\t\t\t&foundPreparedTransactionBeforeCommit);\n\n\t\thash_search(recheckTransactionSet, transactionName, HASH_FIND,\n\t\t\t\t\t&foundPreparedTransactionAfterCommit);\n\n\t\tif (foundPreparedTransactionBeforeCommit && foundPreparedTransactionAfterCommit)\n\t\t{\n\t\t\t/*\n\t\t\t * The transaction was committed, but the prepared transaction still exists\n\t\t\t * on the worker. Try committing it.\n\t\t\t *\n\t\t\t * We double check that the recovery record exists both before and after\n\t\t\t * checking ActiveDistributedTransactionNumbers(), since we may have\n\t\t\t * observed a prepared transaction that was committed immediately after.\n\t\t\t */\n\t\t\tbool shouldCommit = true;\n\t\t\tbool commitSucceeded = RecoverPreparedTransactionOnWorker(connection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  transactionName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shouldCommit);\n\t\t\tif (!commitSucceeded)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Failed to commit on the current worker. Stop without throwing\n\t\t\t\t * an error to allow recover_prepared_transactions to continue with\n\t\t\t\t * other workers.\n\t\t\t\t */\n\t\t\t\trecoveryFailed = true;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\trecoveredTransactionCount++;\n\n\t\t\t/*\n\t\t\t * We successfully committed the prepared transaction, safe to delete\n\t\t\t * the recovery record.\n\t\t\t */\n\t\t}\n\t\telse if (foundPreparedTransactionAfterCommit)\n\t\t{\n\t\t\t/*\n\t\t\t * We found a committed pg_dist_transaction record that initially did\n\t\t\t * not have a prepared transaction, but did when we checked again.\n\t\t\t *\n\t\t\t * If a transaction started and committed just after we observed the\n\t\t\t * set of prepared transactions, and just before we called\n\t\t\t * ActiveDistributedTransactionNumbers, then we would see a recovery\n\t\t\t * record without a prepared transaction in pendingTransactionSet,\n\t\t\t * but there may be prepared transactions that failed to commit.\n\t\t\t * We should not delete the records for those prepared transactions,\n\t\t\t * since we would otherwise roll back them on the next call to\n\t\t\t * recover_prepared_transactions.\n\t\t\t *\n\t\t\t * In addition, if the transaction started after the call to\n\t\t\t * ActiveDistributedTransactionNumbers and finished just before our\n\t\t\t * pg_dist_transaction snapshot, then it may still be in the process\n\t\t\t * of committing the prepared transactions in the post-commit callback\n\t\t\t * and we should not touch the prepared transactions.\n\t\t\t *\n\t\t\t * To handle these cases, we just leave the records and prepared\n\t\t\t * transactions for the next call to recover_prepared_transactions\n\t\t\t * and skip them here.\n\t\t\t */\n\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * We found a recovery record without any prepared transaction. It\n\t\t\t * must have already been committed, so it's safe to delete the\n\t\t\t * recovery record.\n\t\t\t *\n\t\t\t * Transactions that started after we observed pendingTransactionSet,\n\t\t\t * but successfully committed their prepared transactions before\n\t\t\t * ActiveDistributedTransactionNumbers are indistinguishable from\n\t\t\t * transactions that committed at an earlier time, in which case it's\n\t\t\t * safe delete the recovery record as well.\n\t\t\t */\n\t\t}\n\n\t\tsimple_heap_delete(pgDistTransaction, &heapTuple->t_self);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistTransaction, NoLock);\n\n\tif (!recoveryFailed)\n\t{\n\t\tchar *pendingTransactionName = NULL;\n\t\tbool abortSucceeded = true;\n\n\t\t/*\n\t\t * All remaining prepared transactions that are not part of an in-progress\n\t\t * distributed transaction should be aborted since we did not find a recovery\n\t\t * record, which implies the disributed transaction aborted.\n\t\t */\n\t\thash_seq_init(&status, pendingTransactionSet);\n\n\t\twhile ((pendingTransactionName = hash_seq_search(&status)) != NULL)\n\t\t{\n\t\t\tbool isTransactionInProgress = IsTransactionInProgress(\n\t\t\t\tactiveTransactionNumberSet,\n\t\t\t\tpendingTransactionName);\n\t\t\tif (isTransactionInProgress)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tbool shouldCommit = false;\n\t\t\tabortSucceeded = RecoverPreparedTransactionOnWorker(connection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpendingTransactionName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshouldCommit);\n\t\t\tif (!abortSucceeded)\n\t\t\t{\n\t\t\t\thash_seq_term(&status);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\trecoveredTransactionCount++;\n\t\t}\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\tMemoryContextDelete(localContext);\n\n\treturn recoveredTransactionCount;\n}\n\n\n/*\n * PendingWorkerTransactionList returns a list of pending prepared\n * transactions on a remote node that were started by this node.\n */\nstatic List *\nPendingWorkerTransactionList(MultiConnection *connection)\n{\n\tStringInfo command = makeStringInfo();\n\tbool raiseInterrupts = true;\n\tList *transactionNames = NIL;\n\tint32 coordinatorId = GetLocalGroupId();\n\n\tappendStringInfo(command,\n\t\t\t\t\t \"SELECT gid FROM pg_prepared_xacts \"\n\t\t\t\t\t \"WHERE gid COLLATE pg_catalog.default LIKE 'citus\\\\_%d\\\\_%%' COLLATE pg_catalog.default AND database = current_database()\",\n\t\t\t\t\t coordinatorId);\n\n\tint querySent = SendRemoteCommand(connection, command->data);\n\tif (querySent == 0)\n\t{\n\t\tReportConnectionError(connection, ERROR);\n\t}\n\n\tPGresult *result = GetRemoteCommandResult(connection, raiseInterrupts);\n\tif (!IsResponseOK(result))\n\t{\n\t\tReportResultError(connection, result, ERROR);\n\t}\n\n\tint rowCount = PQntuples(result);\n\n\tfor (int rowIndex = 0; rowIndex < rowCount; rowIndex++)\n\t{\n\t\tconst int columnIndex = 0;\n\t\tchar *transactionName = PQgetvalue(result, rowIndex, columnIndex);\n\n\t\ttransactionNames = lappend(transactionNames, pstrdup(transactionName));\n\t}\n\n\tPQclear(result);\n\tForgetResults(connection);\n\n\treturn transactionNames;\n}\n\n\n/*\n * IsTransactionInProgress returns whether the distributed transaction to which\n * preparedTransactionName belongs is still in progress, or false if the\n * transaction name cannot be parsed. This can happen when the user manually\n * inserts into pg_dist_transaction.\n */\nstatic bool\nIsTransactionInProgress(HTAB *activeTransactionNumberSet, char *preparedTransactionName)\n{\n\tint32 groupId = 0;\n\tint procId = 0;\n\tuint32 connectionNumber = 0;\n\tuint64 transactionNumber = 0;\n\tbool isTransactionInProgress = false;\n\n\tbool isValidName = ParsePreparedTransactionName(preparedTransactionName, &groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t&procId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t&transactionNumber,\n\t\t\t\t\t\t\t\t\t\t\t\t\t&connectionNumber);\n\tif (isValidName)\n\t{\n\t\thash_search(activeTransactionNumberSet, &transactionNumber, HASH_FIND,\n\t\t\t\t\t&isTransactionInProgress);\n\t}\n\n\treturn isTransactionInProgress;\n}\n\n\n/*\n * RecoverPreparedTransactionOnWorker recovers a single prepared transaction over\n * the given connection. If shouldCommit is true we send\n */\nstatic bool\nRecoverPreparedTransactionOnWorker(MultiConnection *connection, char *transactionName,\n\t\t\t\t\t\t\t\t   bool shouldCommit)\n{\n\tStringInfo command = makeStringInfo();\n\tPGresult *result = NULL;\n\tbool raiseInterrupts = false;\n\n\tif (shouldCommit)\n\t{\n\t\t/* should have committed this prepared transaction */\n\t\tappendStringInfo(command, \"COMMIT PREPARED %s\",\n\t\t\t\t\t\t quote_literal_cstr(transactionName));\n\t}\n\telse\n\t{\n\t\t/* should have aborted this prepared transaction */\n\t\tappendStringInfo(command, \"ROLLBACK PREPARED %s\",\n\t\t\t\t\t\t quote_literal_cstr(transactionName));\n\t}\n\n\tint executeCommand = ExecuteOptionalRemoteCommand(connection, command->data, &result);\n\tif (executeCommand == QUERY_SEND_FAILED)\n\t{\n\t\treturn false;\n\t}\n\tif (executeCommand == RESPONSE_NOT_OKAY)\n\t{\n\t\treturn false;\n\t}\n\n\tPQclear(result);\n\tClearResults(connection, raiseInterrupts);\n\n\tereport(LOG, (errmsg(\"recovered a prepared transaction on %s:%d\",\n\t\t\t\t\t\t connection->hostname, connection->port),\n\t\t\t\t  errcontext(\"%s\", command->data)));\n\n\treturn true;\n}\n\n\n/*\n * DeleteWorkerTransactions deletes the entries on pg_dist_transaction for a given\n * worker node. It's implemented to be called at master_remove_node.\n */\nvoid\nDeleteWorkerTransactions(WorkerNode *workerNode)\n{\n\tif (workerNode == NULL)\n\t{\n\t\t/*\n\t\t * We don't expect this, but let's be defensive since crashing is much worse\n\t\t * than leaving pg_dist_transction entries.\n\t\t */\n\t\treturn;\n\t}\n\n\tbool indexOK = true;\n\tint scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\tint32 groupId = workerNode->groupId;\n\tHeapTuple heapTuple = NULL;\n\n\tRelation pgDistTransaction = table_open(DistTransactionRelationId(),\n\t\t\t\t\t\t\t\t\t\t\tRowExclusiveLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_transaction_groupid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(groupId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistTransaction,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistTransactionGroupIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\twhile (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tsimple_heap_delete(pgDistTransaction, &heapTuple->t_self);\n\t}\n\n\tCommandCounterIncrement();\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistTransaction, NoLock);\n}\n\n\n/*\n * GetOuterXidAttrIndexInPgDistTransaction returns attrnum for outer_xid attr.\n *\n * outer_xid attr was added to table pg_dist_transaction using alter operation after\n * the version where Citus started supporting downgrades, and it's only column that we've\n * introduced to pg_dist_transaction since then.\n *\n * And in case of a downgrade + upgrade, tupleDesc->natts becomes greater than\n * Natts_pg_dist_transaction and when this happens, then we know that attrnum outer_xid is\n * not Anum_pg_dist_transaction_outerxid anymore but tupleDesc->natts - 1.\n */\nint\nGetOuterXidAttrIndexInPgDistTransaction(TupleDesc tupleDesc)\n{\n\treturn tupleDesc->natts == Natts_pg_dist_transaction\n\t\t   ? (Anum_pg_dist_transaction_outerxid - 1)\n\t\t   : tupleDesc->natts - 1;\n}\n"
  },
  {
    "path": "src/backend/distributed/transaction/worker_transaction.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_transaction.c\n *\n * Routines for performing transactions across all workers.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/xact.h\"\n#include \"utils/builtins.h\"\n#include \"utils/memutils.h\"\n#include \"utils/uuid.h\"\n\n#include \"distributed/connection_management.h\"\n#include \"distributed/jsonbutils.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/pg_dist_node.h\"\n#include \"distributed/pg_dist_transaction.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/transaction_recovery.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_transaction.h\"\n\n\nPG_FUNCTION_INFO_V1(citus_server_id);\n\n\nstatic void SendBareCommandListToMetadataNodesInternal(List *commandList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   TargetWorkerSet targetWorkerSet);\nstatic void SendCommandToMetadataWorkersParams(const char *command,\n\t\t\t\t\t\t\t\t\t\t\t   const char *user, int parameterCount,\n\t\t\t\t\t\t\t\t\t\t\t   const Oid *parameterTypes,\n\t\t\t\t\t\t\t\t\t\t\t   const char *const *parameterValues);\nstatic void SendCommandToWorkersParamsInternal(TargetWorkerSet targetWorkerSet,\n\t\t\t\t\t\t\t\t\t\t\t   const char *command, const char *user,\n\t\t\t\t\t\t\t\t\t\t\t   int parameterCount,\n\t\t\t\t\t\t\t\t\t\t\t   const Oid *parameterTypes,\n\t\t\t\t\t\t\t\t\t\t\t   const char *const *parameterValues);\nstatic void ErrorIfAnyMetadataNodeOutOfSync(List *metadataNodeList);\n\n\n/*\n * SendCommandToWorker sends a command to a particular worker as part of the\n * 2PC.\n */\nvoid\nSendCommandToWorker(const char *nodeName, int32 nodePort, const char *command)\n{\n\tconst char *nodeUser = CurrentUserName();\n\tSendCommandToWorkerAsUser(nodeName, nodePort, nodeUser, command);\n}\n\n\n/*\n * SendCommandToWorkersAsUser sends a command to targetWorkerSet as a particular user\n * as part of the 2PC.\n */\nvoid\nSendCommandToWorkersAsUser(TargetWorkerSet targetWorkerSet, const char *nodeUser,\n\t\t\t\t\t\t   const char *command)\n{\n\tList *workerNodeList = TargetWorkerSetNodeList(targetWorkerSet, RowShareLock);\n\n\t/* run commands serially */\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tconst char *nodeName = workerNode->workerName;\n\t\tint nodePort = workerNode->workerPort;\n\n\t\tSendCommandToWorkerAsUser(nodeName, nodePort, nodeUser, command);\n\t}\n}\n\n\n/*\n * SendCommandToWorkerAsUser sends a command to a particular worker as a particular user\n * as part of the 2PC.\n */\nvoid\nSendCommandToWorkerAsUser(const char *nodeName, int32 nodePort, const char *nodeUser,\n\t\t\t\t\t\t  const char *command)\n{\n\tuint32 connectionFlags = 0;\n\n\tUseCoordinatedTransaction();\n\tUse2PCForCoordinatedTransaction();\n\n\tMultiConnection *transactionConnection = GetNodeUserDatabaseConnection(\n\t\tconnectionFlags, nodeName,\n\t\tnodePort,\n\t\tnodeUser, NULL);\n\n\tMarkRemoteTransactionCritical(transactionConnection);\n\tRemoteTransactionBeginIfNecessary(transactionConnection);\n\tExecuteCriticalRemoteCommand(transactionConnection, command);\n}\n\n\n/*\n * SendCommandToWorkers sends a command to all workers in\n * parallel. Commands are committed on the workers when the local\n * transaction commits.\n */\nvoid\nSendCommandToWorkersWithMetadata(const char *command)\n{\n\tSendCommandToMetadataWorkersParams(command, CurrentUserName(),\n\t\t\t\t\t\t\t\t\t   0, NULL, NULL);\n}\n\n\n/*\n * SendCommandToWorkersWithMetadataViaSuperUser sends a command to all workers in\n * parallel by opening a super user connection. Commands are committed on the workers\n * when the local transaction commits. The connection are made as the extension\n * owner to ensure write access to the Citus metadata tables.\n *\n * Since we prevent to open superuser connections for metadata tables, it is\n * discouraged to use it. Consider using it only for propagating pg_dist_object\n * tuples for dependent objects.\n */\nvoid\nSendCommandToWorkersWithMetadataViaSuperUser(const char *command)\n{\n\tSendCommandToMetadataWorkersParams(command, CitusExtensionOwnerName(),\n\t\t\t\t\t\t\t\t\t   0, NULL, NULL);\n}\n\n\n/*\n * SendCommandListToWorkersWithMetadata sends all commands to all metadata workers\n * with the current user. See `SendCommandToWorkersWithMetadata`for details.\n */\nvoid\nSendCommandListToWorkersWithMetadata(List *commands)\n{\n\tchar *command = NULL;\n\tforeach_declared_ptr(command, commands)\n\t{\n\t\tSendCommandToWorkersWithMetadata(command);\n\t}\n}\n\n\n/*\n * SendCommandToRemoteNodesWithMetadata sends a command to remote nodes in\n * parallel. Commands are committed on the nodes when the local transaction\n * commits.\n */\nvoid\nSendCommandToRemoteNodesWithMetadata(const char *command)\n{\n\tSendCommandToRemoteMetadataNodesParams(command, CurrentUserName(),\n\t\t\t\t\t\t\t\t\t\t   0, NULL, NULL);\n}\n\n\n/*\n * SendCommandToRemoteNodesWithMetadataViaSuperUser sends a command to remote\n * nodes in parallel by opening a super user connection. Commands are committed\n * on the nodes when the local transaction commits. The connection are made as\n * the extension owner to ensure write access to the Citus metadata tables.\n *\n * Since we prevent to open superuser connections for metadata tables, it is\n * discouraged to use it. Consider using it only for propagating pg_dist_object\n * tuples for dependent objects.\n */\nvoid\nSendCommandToRemoteNodesWithMetadataViaSuperUser(const char *command)\n{\n\tSendCommandToRemoteMetadataNodesParams(command, CitusExtensionOwnerName(),\n\t\t\t\t\t\t\t\t\t\t   0, NULL, NULL);\n}\n\n\n/*\n * SendCommandListToRemoteNodesWithMetadata sends all commands to remote nodes\n * with the current user. See `SendCommandToRemoteNodesWithMetadata`for details.\n */\nvoid\nSendCommandListToRemoteNodesWithMetadata(List *commands)\n{\n\tchar *command = NULL;\n\tforeach_declared_ptr(command, commands)\n\t{\n\t\tSendCommandToRemoteNodesWithMetadata(command);\n\t}\n}\n\n\n/*\n * SendCommandToRemoteMetadataNodesParams is a wrapper around\n * SendCommandToWorkersParamsInternal() that can be used to send commands\n * to remote metadata nodes.\n */\nvoid\nSendCommandToRemoteMetadataNodesParams(const char *command,\n\t\t\t\t\t\t\t\t\t   const char *user, int parameterCount,\n\t\t\t\t\t\t\t\t\t   const Oid *parameterTypes,\n\t\t\t\t\t\t\t\t\t   const char *const *parameterValues)\n{\n\t/* use METADATA_NODES so that ErrorIfAnyMetadataNodeOutOfSync checks local node as well */\n\tList *workerNodeList = TargetWorkerSetNodeList(METADATA_NODES,\n\t\t\t\t\t\t\t\t\t\t\t\t   RowShareLock);\n\n\tErrorIfAnyMetadataNodeOutOfSync(workerNodeList);\n\n\tSendCommandToWorkersParamsInternal(REMOTE_METADATA_NODES, command, user,\n\t\t\t\t\t\t\t\t\t   parameterCount, parameterTypes, parameterValues);\n}\n\n\n/*\n * TargetWorkerSetNodeList returns a list of WorkerNode's that satisfies the\n * TargetWorkerSet.\n */\nList *\nTargetWorkerSetNodeList(TargetWorkerSet targetWorkerSet, LOCKMODE lockMode)\n{\n\tList *workerNodeList = NIL;\n\tif (targetWorkerSet == ALL_SHARD_NODES ||\n\t\ttargetWorkerSet == METADATA_NODES)\n\t{\n\t\tworkerNodeList = ActivePrimaryNodeList(lockMode);\n\t}\n\telse if (targetWorkerSet == REMOTE_NODES || targetWorkerSet == REMOTE_METADATA_NODES)\n\t{\n\t\tworkerNodeList = ActivePrimaryRemoteNodeList(lockMode);\n\t}\n\telse if (targetWorkerSet == NON_COORDINATOR_METADATA_NODES ||\n\t\t\t targetWorkerSet == NON_COORDINATOR_NODES)\n\t{\n\t\tworkerNodeList = ActivePrimaryNonCoordinatorNodeList(lockMode);\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"invalid target worker set: %d\", targetWorkerSet)));\n\t}\n\n\tList *result = NIL;\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tif ((targetWorkerSet == NON_COORDINATOR_METADATA_NODES ||\n\t\t\t targetWorkerSet == REMOTE_METADATA_NODES ||\n\t\t\t targetWorkerSet == METADATA_NODES) &&\n\t\t\t!workerNode->hasMetadata)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tresult = lappend(result, workerNode);\n\t}\n\n\treturn result;\n}\n\n\n/*\n * SendBareCommandListToRemoteMetadataNodes is a wrapper around\n * SendBareCommandListToMetadataNodesInternal() that can be used to send\n * bare commands to remote metadata nodes.\n */\nvoid\nSendBareCommandListToRemoteMetadataNodes(List *commandList)\n{\n\tSendBareCommandListToMetadataNodesInternal(commandList,\n\t\t\t\t\t\t\t\t\t\t\t   REMOTE_METADATA_NODES);\n}\n\n\n/*\n * SendBareCommandListToMetadataWorkers is a wrapper around\n * SendBareCommandListToMetadataNodesInternal() that can be used to send\n * bare commands to metadata workers.\n */\nvoid\nSendBareCommandListToMetadataWorkers(List *commandList)\n{\n\tSendBareCommandListToMetadataNodesInternal(commandList,\n\t\t\t\t\t\t\t\t\t\t\t   NON_COORDINATOR_METADATA_NODES);\n}\n\n\n/*\n * SendBareCommandListToMetadataNodesInternal sends a list of commands to given\n * target worker set in serial. Commands are committed immediately: new connections\n * are always used and no transaction block is used (hence \"bare\"). The connections\n * are made as the extension owner to ensure write access to the Citus metadata\n * tables. Primarly useful for INDEX commands using CONCURRENTLY.\n */\nstatic void\nSendBareCommandListToMetadataNodesInternal(List *commandList,\n\t\t\t\t\t\t\t\t\t\t   TargetWorkerSet targetWorkerSet)\n{\n\tList *workerNodeList = TargetWorkerSetNodeList(targetWorkerSet, RowShareLock);\n\tchar *nodeUser = CurrentUserName();\n\n\tErrorIfAnyMetadataNodeOutOfSync(workerNodeList);\n\n\t/* run commands serially */\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tconst char *nodeName = workerNode->workerName;\n\t\tint nodePort = workerNode->workerPort;\n\t\tint connectionFlags = FORCE_NEW_CONNECTION;\n\n\t\tMultiConnection *workerConnection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodeUser, NULL);\n\n\t\t/* iterate over the commands and execute them in the same connection */\n\t\tconst char *commandString = NULL;\n\t\tforeach_declared_ptr(commandString, commandList)\n\t\t{\n\t\t\tExecuteCriticalRemoteCommand(workerConnection, commandString);\n\t\t}\n\n\t\tCloseConnection(workerConnection);\n\t}\n}\n\n\n/*\n * SendCommandToMetadataWorkersParams is a wrapper around\n * SendCommandToWorkersParamsInternal() enforcing some extra checks.\n */\nstatic void\nSendCommandToMetadataWorkersParams(const char *command,\n\t\t\t\t\t\t\t\t   const char *user, int parameterCount,\n\t\t\t\t\t\t\t\t   const Oid *parameterTypes,\n\t\t\t\t\t\t\t\t   const char *const *parameterValues)\n{\n\tList *workerNodeList = TargetWorkerSetNodeList(NON_COORDINATOR_METADATA_NODES,\n\t\t\t\t\t\t\t\t\t\t\t\t   RowShareLock);\n\n\tErrorIfAnyMetadataNodeOutOfSync(workerNodeList);\n\n\tSendCommandToWorkersParamsInternal(NON_COORDINATOR_METADATA_NODES, command, user,\n\t\t\t\t\t\t\t\t\t   parameterCount, parameterTypes,\n\t\t\t\t\t\t\t\t\t   parameterValues);\n}\n\n\n/*\n * SendCommandToWorkersParamsInternal sends a command to all workers in parallel.\n * Commands are committed on the workers when the local transaction commits. The\n * connection are made as the extension owner to ensure write access to the Citus\n * metadata tables. Parameters can be specified as for PQexecParams, except that\n * paramLengths, paramFormats and resultFormat are hard-coded to NULL, NULL and 0\n * respectively.\n */\nstatic void\nSendCommandToWorkersParamsInternal(TargetWorkerSet targetWorkerSet, const char *command,\n\t\t\t\t\t\t\t\t   const char *user, int parameterCount,\n\t\t\t\t\t\t\t\t   const Oid *parameterTypes,\n\t\t\t\t\t\t\t\t   const char *const *parameterValues)\n{\n\tList *connectionList = NIL;\n\tList *workerNodeList = TargetWorkerSetNodeList(targetWorkerSet, RowShareLock);\n\n\tUseCoordinatedTransaction();\n\tUse2PCForCoordinatedTransaction();\n\n\t/* open connections in parallel */\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tconst char *nodeName = workerNode->workerName;\n\t\tint nodePort = workerNode->workerPort;\n\t\tint32 connectionFlags = REQUIRE_METADATA_CONNECTION;\n\n\t\tMultiConnection *connection = StartNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  user, NULL);\n\n\t\t/*\n\t\t * connection can only be NULL for optional connections, which we don't\n\t\t * support in this codepath.\n\t\t */\n\t\tAssert((connectionFlags & OPTIONAL_CONNECTION) == 0);\n\t\tAssert(connection != NULL);\n\t\tMarkRemoteTransactionCritical(connection);\n\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\t/* finish opening connections */\n\tFinishConnectionListEstablishment(connectionList);\n\n\tRemoteTransactionsBeginIfNecessary(connectionList);\n\n\t/* send commands in parallel */\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tint querySent = SendRemoteCommandParams(connection, command, parameterCount,\n\t\t\t\t\t\t\t\t\t\t\t\tparameterTypes, parameterValues, false);\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tReportConnectionError(connection, ERROR);\n\t\t}\n\t}\n\n\t/* get results */\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tPGresult *result = GetRemoteCommandResult(connection, true);\n\t\tif (!IsResponseOK(result))\n\t\t{\n\t\t\tReportResultError(connection, result, ERROR);\n\t\t}\n\n\t\tPQclear(result);\n\n\t\tForgetResults(connection);\n\t}\n}\n\n\n/*\n * EnsureNoModificationsHaveBeenDone reports an error if we have performed any\n * modification in the current transaction to prevent opening a connection is such cases.\n */\nvoid\nEnsureNoModificationsHaveBeenDone()\n{\n\tif (XactModificationLevel > XACT_MODIFICATION_NONE)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),\n\t\t\t\t\t\terrmsg(\"cannot open new connections after the first modification \"\n\t\t\t\t\t\t\t   \"command within a transaction\")));\n\t}\n}\n\n\n/*\n * SendCommandListToWorkerOutsideTransaction forces to open a new connection\n * to the node with the given nodeName and nodePort. Then, the connection starts\n * a transaction on the remote node and executes the commands in the transaction.\n * The function raises error if any of the queries fails.\n */\nvoid\nSendCommandListToWorkerOutsideTransaction(const char *nodeName, int32 nodePort,\n\t\t\t\t\t\t\t\t\t\t  const char *nodeUser, List *commandList)\n{\n\tint connectionFlags = FORCE_NEW_CONNECTION;\n\n\tMultiConnection *workerConnection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodeUser, NULL);\n\n\tSendCommandListToWorkerOutsideTransactionWithConnection(workerConnection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommandList);\n\tCloseConnection(workerConnection);\n}\n\n\n/*\n * SendCommandListToWorkerOutsideTransactionWithConnection sends the command list\n * over the specified connection. This opens a new transaction on the\n * connection, thus it's important that no transaction is currently open.\n * This function is mainly useful to avoid opening an closing\n * connections excessively by allowing reusing a single connection to send\n * multiple separately committing transactions. The function raises an error if\n * any of the queries fail.\n */\nvoid\nSendCommandListToWorkerOutsideTransactionWithConnection(MultiConnection *workerConnection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *commandList)\n{\n\tMarkRemoteTransactionCritical(workerConnection);\n\tRemoteTransactionBegin(workerConnection);\n\n\t/* iterate over the commands and execute them in the same connection */\n\tconst char *commandString = NULL;\n\tforeach_declared_ptr(commandString, commandList)\n\t{\n\t\tExecuteCriticalRemoteCommand(workerConnection, commandString);\n\t}\n\n\tRemoteTransactionCommit(workerConnection);\n\tResetRemoteTransaction(workerConnection);\n}\n\n\n/*\n * SendCommandListToWorkerListWithBareConnections sends the command list\n * over the specified bare connections. This function is mainly useful to\n * avoid opening an closing connections excessively by allowing reusing\n * connections to send multiple separate bare commands. The function\n * raises an error if any of the queries fail.\n */\nvoid\nSendCommandListToWorkerListWithBareConnections(List *workerConnectionList,\n\t\t\t\t\t\t\t\t\t\t\t   List *commandList)\n{\n\tAssert(!InCoordinatedTransaction());\n\tAssert(!GetCoordinatedTransactionShouldUse2PC());\n\n\tif (list_length(commandList) == 0 || list_length(workerConnectionList) == 0)\n\t{\n\t\t/* nothing to do */\n\t\treturn;\n\t}\n\n\t/*\n\t * In order to avoid round-trips per query in queryStringList,\n\t * we join the string and send as a single command. Also,\n\t * if there is only a single command, avoid additional call to\n\t * StringJoin given that some strings can be quite large.\n\t */\n\tchar *stringToSend = (list_length(commandList) == 1) ?\n\t\t\t\t\t\t linitial(commandList) : StringJoin(commandList, ';');\n\n\t/* send commands in parallel */\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, workerConnectionList)\n\t{\n\t\tint querySent = SendRemoteCommand(connection, stringToSend);\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tReportConnectionError(connection, ERROR);\n\t\t}\n\t}\n\n\tbool failOnError = true;\n\tforeach_declared_ptr(connection, workerConnectionList)\n\t{\n\t\tClearResults(connection, failOnError);\n\t}\n}\n\n\n/*\n * SendCommandListToWorkerInCoordinatedTransaction opens connection to the node\n * with the given nodeName and nodePort. The commands are sent as part of the\n * coordinated transaction. Any failures aborts the coordinated transaction.\n */\nvoid\nSendMetadataCommandListToWorkerListInCoordinatedTransaction(List *workerNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst char *nodeUser,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *commandList)\n{\n\tif (list_length(commandList) == 0 || list_length(workerNodeList) == 0)\n\t{\n\t\t/* nothing to do */\n\t\treturn;\n\t}\n\n\tErrorIfAnyMetadataNodeOutOfSync(workerNodeList);\n\n\tUseCoordinatedTransaction();\n\n\tList *connectionList = NIL;\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tconst char *nodeName = workerNode->workerName;\n\t\tint nodePort = workerNode->workerPort;\n\t\tint connectionFlags = REQUIRE_METADATA_CONNECTION;\n\n\t\tMultiConnection *connection =\n\t\t\tStartNodeConnection(connectionFlags, nodeName, nodePort);\n\n\t\tMarkRemoteTransactionCritical(connection);\n\n\t\t/*\n\t\t * connection can only be NULL for optional connections, which we don't\n\t\t * support in this codepath.\n\t\t */\n\t\tAssert((connectionFlags & OPTIONAL_CONNECTION) == 0);\n\t\tAssert(connection != NULL);\n\t\tconnectionList = lappend(connectionList, connection);\n\t}\n\n\tFinishConnectionListEstablishment(connectionList);\n\n\t/* must open transaction blocks to use intermediate results */\n\tRemoteTransactionsBeginIfNecessary(connectionList);\n\n\t/*\n\t * In order to avoid round-trips per query in queryStringList,\n\t * we join the string and send as a single command. Also,\n\t * if there is only a single command, avoid additional call to\n\t * StringJoin given that some strings can be quite large.\n\t */\n\tchar *stringToSend = (list_length(commandList) == 1) ?\n\t\t\t\t\t\t linitial(commandList) : StringJoin(commandList, ';');\n\n\t/* send commands in parallel */\n\tbool failOnError = true;\n\tMultiConnection *connection = NULL;\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tint querySent = SendRemoteCommand(connection, stringToSend);\n\t\tif (querySent == 0)\n\t\t{\n\t\t\tReportConnectionError(connection, ERROR);\n\t\t}\n\t}\n\n\tforeach_declared_ptr(connection, connectionList)\n\t{\n\t\tClearResults(connection, failOnError);\n\t}\n}\n\n\n/*\n * SendOptionalCommandListToWorkerOutsideTransactionWithConnection sends the\n * given command list over a specified connection in a single transaction that\n * is outside of the coordinated tranaction.\n *\n * If any of the commands fail, it rollbacks the transaction, and otherwise commits.\n * A successful commit is indicated by returning true, and a failed commit by returning\n * false.\n */\nbool\nSendOptionalCommandListToWorkerOutsideTransactionWithConnection(MultiConnection *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerConnection, List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommandList)\n{\n\tif (PQstatus(workerConnection->pgConn) != CONNECTION_OK)\n\t{\n\t\treturn false;\n\t}\n\tRemoteTransactionBegin(workerConnection);\n\n\t/* iterate over the commands and execute them in the same connection */\n\tbool failed = false;\n\tconst char *commandString = NULL;\n\tforeach_declared_ptr(commandString, commandList)\n\t{\n\t\tif (ExecuteOptionalRemoteCommand(workerConnection, commandString, NULL) != 0)\n\t\t{\n\t\t\tfailed = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (failed)\n\t{\n\t\tRemoteTransactionAbort(workerConnection);\n\t}\n\telse\n\t{\n\t\tRemoteTransactionCommit(workerConnection);\n\t}\n\n\tResetRemoteTransaction(workerConnection);\n\n\treturn !failed;\n}\n\n\n/*\n * SendOptionalCommandListToWorkerOutsideTransaction sends the given command\n * list to the given worker in a single transaction that is outside of the\n * coordinated tranaction. If any of the commands fail, it rollbacks the\n * transaction, and otherwise commits.\n */\nbool\nSendOptionalCommandListToWorkerOutsideTransaction(const char *nodeName, int32 nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t  const char *nodeUser, List *commandList)\n{\n\tint connectionFlags = FORCE_NEW_CONNECTION;\n\n\tMultiConnection *workerConnection = GetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodeName, nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  nodeUser, NULL);\n\tbool failed = SendOptionalCommandListToWorkerOutsideTransactionWithConnection(\n\t\tworkerConnection,\n\t\tcommandList);\n\tCloseConnection(workerConnection);\n\n\treturn !failed;\n}\n\n\n/*\n * SendOptionalMetadataCommandListToWorkerInCoordinatedTransaction sends the given\n * command list to the given worker as part of the coordinated transaction.\n * If any of the commands fail, the function returns false.\n */\nbool\nSendOptionalMetadataCommandListToWorkerInCoordinatedTransaction(const char *nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tint32 nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst char *nodeUser,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *commandList)\n{\n\tint connectionFlags = REQUIRE_METADATA_CONNECTION;\n\tbool failed = false;\n\n\tUseCoordinatedTransaction();\n\n\tMultiConnection *workerConnection =\n\t\tGetNodeUserDatabaseConnection(connectionFlags, nodeName, nodePort,\n\t\t\t\t\t\t\t\t\t  nodeUser, NULL);\n\tif (PQstatus(workerConnection->pgConn) != CONNECTION_OK)\n\t{\n\t\treturn false;\n\t}\n\n\tRemoteTransactionsBeginIfNecessary(list_make1(workerConnection));\n\n\t/* iterate over the commands and execute them in the same connection */\n\tconst char *commandString = NULL;\n\tforeach_declared_ptr(commandString, commandList)\n\t{\n\t\tif (ExecuteOptionalRemoteCommand(workerConnection, commandString, NULL) !=\n\t\t\tRESPONSE_OKAY)\n\t\t{\n\t\t\tfailed = true;\n\n\t\t\tbool raiseErrors = false;\n\t\t\tMarkRemoteTransactionFailed(workerConnection, raiseErrors);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn !failed;\n}\n\n\n/*\n * ErrorIfAnyMetadataNodeOutOfSync raises an error if any of the given\n * metadata nodes are out of sync. It is safer to avoid metadata changing\n * commands (e.g. DDL or node addition) until all metadata nodes have\n * been synced.\n *\n * An example of we could get in a bad situation without doing so is:\n *  1. Create a reference table\n *  2. After the node becomes out of sync, add a new active node\n *  3. Insert into the reference table from the out of sync node\n *\n * Since the out-of-sync might not know about the new node, it won't propagate\n * the changes to the new node and replicas will be in an inconsistent state.\n */\nstatic void\nErrorIfAnyMetadataNodeOutOfSync(List *metadataNodeList)\n{\n\tWorkerNode *metadataNode = NULL;\n\tforeach_declared_ptr(metadataNode, metadataNodeList)\n\t{\n\t\tAssert(metadataNode->hasMetadata);\n\n\t\tif (!metadataNode->metadataSynced)\n\t\t{\n\t\t\tconst char *workerName = metadataNode->workerName;\n\t\t\tint workerPort = metadataNode->workerPort;\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"%s:%d is a metadata node, but is out of sync\",\n\t\t\t\t\t\t\t\t   workerName, workerPort),\n\t\t\t\t\t\t\terrhint(\"If the node is up, wait until metadata\"\n\t\t\t\t\t\t\t\t\t\" gets synced to it and try again.\")));\n\t\t}\n\t}\n}\n\n\n/*\n * IsWorkerTheCurrentNode checks if the given worker refers to the\n * the current node by comparing the server id of the worker and of the\n * current nodefrom pg_dist_node_metadata\n */\nbool\nIsWorkerTheCurrentNode(WorkerNode *workerNode)\n{\n\tint connectionFlags = REQUIRE_METADATA_CONNECTION;\n\n\tMultiConnection *workerConnection =\n\t\tGetNodeUserDatabaseConnection(connectionFlags,\n\t\t\t\t\t\t\t\t\t  workerNode->workerName,\n\t\t\t\t\t\t\t\t\t  workerNode->workerPort,\n\t\t\t\t\t\t\t\t\t  CurrentUserName(),\n\t\t\t\t\t\t\t\t\t  NULL);\n\n\tconst char *command =\n\t\t\"SELECT metadata ->> 'server_id' AS server_id FROM pg_dist_node_metadata\";\n\n\tint resultCode = SendRemoteCommand(workerConnection, command);\n\n\tif (resultCode == 0)\n\t{\n\t\tCloseConnection(workerConnection);\n\t\treturn false;\n\t}\n\n\tPGresult *result = GetRemoteCommandResult(workerConnection, true);\n\n\tif (result == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tList *commandResult = ReadFirstColumnAsText(result);\n\n\tPQclear(result);\n\tForgetResults(workerConnection);\n\n\tif ((list_length(commandResult) != 1))\n\t{\n\t\treturn false;\n\t}\n\n\tStringInfo resultInfo = (StringInfo) linitial(commandResult);\n\tchar *workerServerId = resultInfo->data;\n\n\tDatum metadata = DistNodeMetadata();\n\ttext *currentServerIdTextP = ExtractFieldTextP(metadata, \"server_id\");\n\n\tif (currentServerIdTextP == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tchar *currentServerId = text_to_cstring(currentServerIdTextP);\n\n\treturn strcmp(workerServerId, currentServerId) == 0;\n}\n\n\n/*\n * citus_server_id returns a random UUID value as server identifier. This is\n * modeled after PostgreSQL's pg_random_uuid().\n */\nDatum\ncitus_server_id(PG_FUNCTION_ARGS)\n{\n\tuint8 *buf = (uint8 *) palloc(UUID_LEN);\n\n\t/*\n\t * If pg_strong_random() fails, fall-back to using random(). In previous\n\t * versions of postgres we don't have pg_strong_random(), so use it by\n\t * default in that case.\n\t */\n\tif (!pg_strong_random((char *) buf, UUID_LEN))\n\t{\n\t\tfor (int bufIdx = 0; bufIdx < UUID_LEN; bufIdx++)\n\t\t{\n\t\t\tbuf[bufIdx] = (uint8) (random() & 0xFF);\n\t\t}\n\t}\n\n\t/*\n\t * Set magic numbers for a \"version 4\" (pseudorandom) UUID, see\n\t * http://tools.ietf.org/html/rfc4122#section-4.4\n\t */\n\tbuf[6] = (buf[6] & 0x0f) | 0x40;    /* \"version\" field */\n\tbuf[8] = (buf[8] & 0x3f) | 0x80;    /* \"variant\" field */\n\n\tPG_RETURN_UUID_P((pg_uuid_t *) buf);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/acquire_lock.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * acquire_lock.c\n *\t  A dynamic background worker that can help your backend to acquire its locks. This is\n *\t  an intrusive way of getting your way. The primary use of this will be to allow\n *\t  master_update_node to make progress during failure. When the system cannot possibly\n *\t  finish a transaction due to the host required to finish the transaction has failed\n *\t  it might be better to actively cancel the backend instead of waiting for it to fail.\n *\n * This file provides infrastructure for launching exactly one a background\n * worker for every database in which citus is used.  That background worker\n * can then perform work like deadlock detection, prepared transaction\n * recovery, and cleanup.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"access/xact.h\"\n#include \"catalog/pg_type.h\"\n#include \"executor/spi.h\"\n#include \"portability/instr_time.h\"\n#include \"storage/ipc.h\"\n#include \"storage/latch.h\"\n#include \"utils/snapmgr.h\"\n\n#include \"distributed/background_worker_utils.h\"\n#include \"distributed/citus_acquire_lock.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/version_compat.h\"\n\n/* forward declaration of background worker entrypoint */\nextern PGDLLEXPORT void LockAcquireHelperMain(Datum main_arg);\n\n/* forward declaration of helper functions */\nstatic void lock_acquire_helper_sigterm(SIGNAL_ARGS);\nstatic void EnsureStopLockAcquireHelper(void *arg);\n\n/* LockAcquireHelperArgs contains extra arguments to be used to start the worker */\ntypedef struct LockAcquireHelperArgs\n{\n\tOid DatabaseId;\n\tint32 lock_cooldown;\n} LockAcquireHelperArgs;\n\nstatic bool got_sigterm = false;\n\n\n/*\n * StartLockAcquireHelperBackgroundWorker creates a background worker that will help the\n * backend passed in as an argument to complete. The worker that is started will be\n * terminated once the current memory context gets reset, to make sure it is cleaned up in\n * all situations. It is however advised to call TerminateBackgroundWorker on the handle\n * returned on the first possible moment the help is no longer required.\n */\nBackgroundWorkerHandle *\nStartLockAcquireHelperBackgroundWorker(int backendToHelp, int32 lock_cooldown)\n{\n\tLockAcquireHelperArgs args;\n\tmemset(&args, 0, sizeof(args));\n\n\t/* collect the extra arguments required for the background worker */\n\targs.DatabaseId = MyDatabaseId;\n\targs.lock_cooldown = lock_cooldown;\n\n\tchar workerName[BGW_MAXLEN];\n\tSafeSnprintf(workerName, BGW_MAXLEN,\n\t\t\t\t \"Citus Lock Acquire Helper: %d/%u\", backendToHelp, MyDatabaseId);\n\n\tCitusBackgroundWorkerConfig config = {\n\t\t.workerName = workerName,\n\t\t.functionName = \"LockAcquireHelperMain\",\n\t\t.mainArg = Int32GetDatum(backendToHelp),\n\t\t.extensionOwner = InvalidOid,\n\t\t.needsNotification = false,\n\t\t.waitForStartup = false,\n\t\t.restartTime = CITUS_BGW_NEVER_RESTART,\n\t\t.startTime = BgWorkerStart_RecoveryFinished,\n\t\t.workerType = \"citus_lock_aqcuire\",\n\t\t.extraData = &args,\n\t\t.extraDataSize = sizeof(args)\n\t};\n\n\tBackgroundWorkerHandle *handle = RegisterCitusBackgroundWorker(&config);\n\tif (!handle)\n\t{\n\t\treturn NULL;\n\t}\n\n\tMemoryContextCallback *workerCleanup = palloc0(sizeof(MemoryContextCallback));\n\tworkerCleanup->func = EnsureStopLockAcquireHelper;\n\tworkerCleanup->arg = handle;\n\n\tMemoryContextRegisterResetCallback(CurrentMemoryContext, workerCleanup);\n\n\treturn handle;\n}\n\n\n/*\n * EnsureStopLockAcquireHelper is designed to be called as a MemoryContextCallback. It\n * takes a handle to the background worker and Terminates it. It is safe to be called on a\n * handle that has already been terminated due to the guard around the generation number\n * implemented in the handle by postgres.\n */\nstatic void\nEnsureStopLockAcquireHelper(void *arg)\n{\n\tBackgroundWorkerHandle *handle = (BackgroundWorkerHandle *) arg;\n\tTerminateBackgroundWorker(handle);\n}\n\n\n/*\n * Signal handler for SIGTERM\n *\t\tSet a flag to let the main loop to terminate, and set our latch to wake\n *\t\tit up.\n */\nstatic void\nlock_acquire_helper_sigterm(SIGNAL_ARGS)\n{\n\tint save_errno = errno;\n\n\tgot_sigterm = true;\n\tSetLatch(MyLatch);\n\n\terrno = save_errno;\n}\n\n\n/*\n * ShouldAcquireLock tests if our backend should still proceed with acquiring the lock,\n * and thus keep terminating conflicting backends. This function returns true until a\n * SIGTERM, background worker termination signal, has been received.\n *\n * The function blocks for at most sleepms when called. During operation without being\n * terminated this is the time between invocations to the backend termination logic.\n */\nstatic bool\nShouldAcquireLock(long sleepms)\n{\n\t/* early escape in case we already got the signal to stop acquiring the lock */\n\tif (got_sigterm)\n\t{\n\t\treturn false;\n\t}\n\n\tint rc = WaitLatch(MyLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,\n\t\t\t\t\t   sleepms * 1L, PG_WAIT_EXTENSION);\n\tResetLatch(MyLatch);\n\n\t/* emergency bailout if postmaster has died */\n\tif (rc & WL_POSTMASTER_DEATH)\n\t{\n\t\tproc_exit(1);\n\t}\n\n\tCHECK_FOR_INTERRUPTS();\n\n\treturn !got_sigterm;\n}\n\n\n/*\n * LockAcquireHelperMain runs in a dynamic background worker to help master_update_node to\n * acquire its locks.\n */\nvoid\nLockAcquireHelperMain(Datum main_arg)\n{\n\tint backendPid = DatumGetInt32(main_arg);\n\tStringInfoData sql;\n\tLockAcquireHelperArgs *args = (LockAcquireHelperArgs *) MyBgworkerEntry->bgw_extra;\n\tlong timeout = 0;\n\tinstr_time connectionStart;\n\tINSTR_TIME_SET_CURRENT(connectionStart);\n\n\t/* parameters for sql query to be executed */\n\tconst int paramCount = 1;\n\tOid paramTypes[1] = { INT4OID };\n\tDatum paramValues[1];\n\n\tpqsignal(SIGTERM, lock_acquire_helper_sigterm);\n\n\tBackgroundWorkerUnblockSignals();\n\n\telog(LOG, \"lock acquiring backend started for backend %d (cooldown %dms)\", backendPid,\n\t\t args->lock_cooldown);\n\n\t/*\n\t * this loop waits till the deadline is reached (eg. lock_cooldown has passed) OR we\n\t * no longer need to acquire the lock due to the termination of this backend.\n\t * Only after the timeout the code will continue with the section that will acquire\n\t * the lock.\n\t */\n\tdo {\n\t\ttimeout = MillisecondsToTimeout(connectionStart, args->lock_cooldown);\n\t} while (timeout > 0 && ShouldAcquireLock(timeout));\n\n\t/* connecting to the database */\n\tBackgroundWorkerInitializeConnectionByOid(args->DatabaseId, InvalidOid, 0);\n\n\t/*\n\t * The query below sends a SIGTERM signal to conflicting backends using\n\t * pg_terminate_backend() function.\n\t *\n\t * The result is are rows of pid,bool indicating a conflicting backend and\n\t * whether the SIGTERM was successfully delivered. These will be logged\n\t * accordingly below for an administrator to correlate in the logs with the\n\t * termination message.\n\t */\n\tinitStringInfo(&sql);\n\tappendStringInfo(&sql,\n\t\t\t\t\t \"WITH pids AS (\\n\"\n\t\t\t\t\t \"  SELECT DISTINCT pid\\n\"\n\t\t\t\t\t \"  FROM pg_catalog.unnest(pg_catalog.pg_blocking_pids($1)) AS pid\\n\"\n\t\t\t\t\t \") SELECT pid, pg_catalog.pg_terminate_backend(pid) FROM pids\");\n\tparamValues[0] = Int32GetDatum(backendPid);\n\n\twhile (ShouldAcquireLock(100))\n\t{\n\t\telog(LOG, \"canceling competing backends for backend %d\", backendPid);\n\n\t\t/*\n\t\t * Begin our transaction\n\t\t */\n\t\tSetCurrentStatementStartTimestamp();\n\t\tStartTransactionCommand();\n\t\tSPI_connect();\n\t\tPushActiveSnapshot(GetTransactionSnapshot());\n\t\tpgstat_report_activity(STATE_RUNNING, sql.data);\n\n\t\tint spiStatus = SPI_execute_with_args(sql.data, paramCount, paramTypes,\n\t\t\t\t\t\t\t\t\t\t\t  paramValues,\n\t\t\t\t\t\t\t\t\t\t\t  NULL, false, 0);\n\n\t\tif (spiStatus == SPI_OK_SELECT)\n\t\t{\n\t\t\tfor (uint64 row = 0; row < SPI_processed; row++)\n\t\t\t{\n\t\t\t\tbool isnull = false;\n\n\t\t\t\tint signaledPid = DatumGetInt32(SPI_getbinval(SPI_tuptable->vals[0],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  SPI_tuptable->tupdesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  1, &isnull));\n\n\t\t\t\tbool isSignaled = DatumGetBool(SPI_getbinval(SPI_tuptable->vals[0],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t SPI_tuptable->tupdesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t 2, &isnull));\n\n\t\t\t\tif (isSignaled)\n\t\t\t\t{\n\t\t\t\t\telog(WARNING, \"terminating conflicting backend %d\", signaledPid);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\telog(INFO,\n\t\t\t\t\t\t \"attempt to terminate conflicting backend %d was unsuccessful\",\n\t\t\t\t\t\t signaledPid);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\telog(FATAL, \"cannot cancel competing backends for backend %d\", backendPid);\n\t\t}\n\n\t\t/*\n\t\t * And finish our transaction.\n\t\t */\n\t\tSPI_finish();\n\t\tPopActiveSnapshot();\n\t\tCommitTransactionCommand();\n\t\tpgstat_report_stat(false);\n\t\tpgstat_report_activity(STATE_IDLE, NULL);\n\t}\n\n\n\telog(LOG, \"lock acquiring backend finished for backend %d\", backendPid);\n\n\t/* safely got to the end, exit without problem */\n\tproc_exit(0);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/aggregate_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * aggregate_utils.c\n *\n * Implementation of UDFs distributing execution of aggregates across workers.\n *\n * When an aggregate has a combinefunc, we use worker_partial_agg to skip\n * calling finalfunc on workers, instead passing state to coordinator where\n * it uses combinefunc in coord_combine_agg & applying finalfunc only at end.\n *\n * Copyright Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"miscadmin.h\"\n#include \"pg_config_manual.h\"\n\n#include \"access/htup_details.h\"\n#include \"catalog/pg_aggregate.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"utils/acl.h\"\n#include \"utils/builtins.h\"\n#include \"utils/datum.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n#include \"utils/typcache.h\"\n\n#include \"distributed/version_compat.h\"\n\nPG_FUNCTION_INFO_V1(worker_partial_agg_sfunc);\nPG_FUNCTION_INFO_V1(worker_partial_agg_ffunc);\nPG_FUNCTION_INFO_V1(coord_combine_agg_sfunc);\nPG_FUNCTION_INFO_V1(coord_combine_agg_ffunc);\n\nPG_FUNCTION_INFO_V1(worker_binary_partial_agg_ffunc);\nPG_FUNCTION_INFO_V1(coord_binary_combine_agg_sfunc);\nPG_FUNCTION_INFO_V1(coord_binary_combine_agg_ffunc);\n\n/*\n * Holds information describing the structure of aggregation arguments\n * and helps to efficiently handle both a single argument and multiple\n * arguments wrapped in a tuple/record. It exploits the fact that\n * aggregation argument types do not change between subsequent\n * calls to SFUNC.\n */\ntypedef struct AggregationArgumentContext\n{\n\t/* immutable fields */\n\tint argumentCount;\n\tbool isTuple;\n\tTupleDesc tupleDesc;\n\n\t/* mutable fields */\n\tHeapTuple tuple;\n\tDatum *values;\n\tbool *nulls;\n} AggregationArgumentContext;\n\n/*\n * internal type for support aggregates to pass transition state alongside\n * aggregation bookkeeping\n */\ntypedef struct StypeBox\n{\n\tDatum value;\n\tOid agg;\n\tOid transtype;\n\tint16_t transtypeLen;\n\tbool transtypeByVal;\n\tbool valueNull;\n\tbool valueInit;\n\tAggregationArgumentContext *aggregationArgumentContext;\n} StypeBox;\n\nstatic HeapTuple GetAggregateForm(Oid oid, Form_pg_aggregate *form);\nstatic HeapTuple GetProcForm(Oid oid, Form_pg_proc *form);\nstatic HeapTuple GetTypeForm(Oid oid, Form_pg_type *form);\nstatic void * pallocInAggContext(FunctionCallInfo fcinfo, size_t size);\nstatic void aclcheckAggregate(ObjectType objectType, Oid userOid, Oid funcOid);\nstatic Datum GetAggInitVal(Datum textInitVal, Oid transtype);\nstatic void InitializeStypeBox(FunctionCallInfo fcinfo, StypeBox *box,\n\t\t\t\t\t\t\t   HeapTuple aggTuple, Oid transtype,\n\t\t\t\t\t\t\t   AggregationArgumentContext *aggregationArgumentContext);\nstatic StypeBox * TryCreateStypeBoxFromFcinfoAggref(FunctionCallInfo fcinfo);\nstatic AggregationArgumentContext * CreateAggregationArgumentContext(FunctionCallInfo\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t fcinfo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t int argumentIndex);\nstatic void ExtractAggregationValues(FunctionCallInfo fcinfo, int argumentIndex,\n\t\t\t\t\t\t\t\t\t AggregationArgumentContext\n\t\t\t\t\t\t\t\t\t *aggregationArgumentContext);\nstatic void HandleTransition(StypeBox *box, FunctionCallInfo fcinfo,\n\t\t\t\t\t\t\t FunctionCallInfo innerFcinfo);\nstatic void HandleStrictUninit(StypeBox *box, FunctionCallInfo fcinfo, Datum value);\nstatic bool TypecheckWorkerPartialAggArgType(FunctionCallInfo fcinfo, StypeBox *box);\nstatic bool TypecheckCoordCombineAggReturnType(FunctionCallInfo fcinfo, Oid ffunc,\n\t\t\t\t\t\t\t\t\t\t\t   StypeBox *box);\n\n/*\n * GetAggregateForm loads corresponding tuple & Form_pg_aggregate for oid\n */\nstatic HeapTuple\nGetAggregateForm(Oid oid, Form_pg_aggregate *form)\n{\n\tHeapTuple tuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(oid));\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\telog(ERROR, \"citus cache lookup failed for aggregate %u\", oid);\n\t}\n\t*form = (Form_pg_aggregate) GETSTRUCT(tuple);\n\treturn tuple;\n}\n\n\n/*\n * GetProcForm loads corresponding tuple & Form_pg_proc for oid\n */\nstatic HeapTuple\nGetProcForm(Oid oid, Form_pg_proc *form)\n{\n\tHeapTuple tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(oid));\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\telog(ERROR, \"citus cache lookup failed for function %u\", oid);\n\t}\n\t*form = (Form_pg_proc) GETSTRUCT(tuple);\n\treturn tuple;\n}\n\n\n/*\n * GetTypeForm loads corresponding tuple & Form_pg_type for oid\n */\nstatic HeapTuple\nGetTypeForm(Oid oid, Form_pg_type *form)\n{\n\tHeapTuple tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(oid));\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\telog(ERROR, \"citus cache lookup failed for type %u\", oid);\n\t}\n\t*form = (Form_pg_type) GETSTRUCT(tuple);\n\treturn tuple;\n}\n\n\n/*\n * pallocInAggContext calls palloc in fcinfo's aggregate context\n */\nstatic void *\npallocInAggContext(FunctionCallInfo fcinfo, size_t size)\n{\n\tMemoryContext aggregateContext;\n\tif (!AggCheckCallContext(fcinfo, &aggregateContext))\n\t{\n\t\telog(ERROR, \"Aggregate function called without an aggregate context\");\n\t}\n\treturn MemoryContextAlloc(aggregateContext, size);\n}\n\n\n/*\n * aclcheckAggregate verifies that the given user has ACL_EXECUTE to the given proc\n */\nstatic void\naclcheckAggregate(ObjectType objectType, Oid userOid, Oid funcOid)\n{\n\tAclResult aclresult;\n\tif (funcOid != InvalidOid)\n\t{\n\t\taclresult = object_aclcheck(ProcedureRelationId, funcOid, userOid,\n\t\t\t\t\t\t\t\t\tACL_EXECUTE);\n\t\tif (aclresult != ACLCHECK_OK)\n\t\t{\n\t\t\taclcheck_error(aclresult, objectType, get_func_name(funcOid));\n\t\t}\n\t}\n}\n\n\n/* Copied from nodeAgg.c */\nstatic Datum\nGetAggInitVal(Datum textInitVal, Oid transtype)\n{\n\t/* *INDENT-OFF* */\n\tOid\t\t\ttypinput,\n\t\t\t\ttypioparam;\n\tchar\t   *strInitVal;\n\tDatum\t\tinitVal;\n\n\tgetTypeInputInfo(transtype, &typinput, &typioparam);\n\tstrInitVal = TextDatumGetCString(textInitVal);\n\tinitVal = OidInputFunctionCall(typinput, strInitVal,\n\t\t\t\t\t\t\t\t   typioparam, -1);\n\tpfree(strInitVal);\n\treturn initVal;\n\t/* *INDENT-ON* */\n}\n\n\n/*\n * InitializeStypeBox fills in the rest of an StypeBox's fields besides agg,\n * handling both permission checking & setting up the initial transition state.\n */\nstatic void\nInitializeStypeBox(FunctionCallInfo fcinfo, StypeBox *box, HeapTuple aggTuple, Oid\n\t\t\t\t   transtype, AggregationArgumentContext *aggregationArgumentContext)\n{\n\tForm_pg_aggregate aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);\n\tOid userId = GetUserId();\n\n\t/* First we make ACL_EXECUTE checks as would be done in nodeAgg.c */\n\taclcheckAggregate(OBJECT_AGGREGATE, userId, aggform->aggfnoid);\n\taclcheckAggregate(OBJECT_FUNCTION, userId, aggform->aggfinalfn);\n\taclcheckAggregate(OBJECT_FUNCTION, userId, aggform->aggtransfn);\n\taclcheckAggregate(OBJECT_FUNCTION, userId, aggform->aggdeserialfn);\n\taclcheckAggregate(OBJECT_FUNCTION, userId, aggform->aggserialfn);\n\taclcheckAggregate(OBJECT_FUNCTION, userId, aggform->aggcombinefn);\n\n\tDatum textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple,\n\t\t\t\t\t\t\t\t\t\tAnum_pg_aggregate_agginitval,\n\t\t\t\t\t\t\t\t\t\t&box->valueNull);\n\tbox->transtype = transtype;\n\tbox->valueInit = !box->valueNull;\n\tbox->aggregationArgumentContext = aggregationArgumentContext;\n\tif (box->valueNull)\n\t{\n\t\tbox->value = (Datum) 0;\n\t}\n\telse\n\t{\n\t\tMemoryContext aggregateContext;\n\t\tif (!AggCheckCallContext(fcinfo, &aggregateContext))\n\t\t{\n\t\t\telog(ERROR, \"InitializeStypeBox called from non aggregate context\");\n\t\t}\n\t\tMemoryContext oldContext = MemoryContextSwitchTo(aggregateContext);\n\n\t\tbox->value = GetAggInitVal(textInitVal, transtype);\n\n\t\tMemoryContextSwitchTo(oldContext);\n\t}\n}\n\n\n/*\n * TryCreateStypeBoxFromFcinfoAggref attempts to initialize an StypeBox through\n * introspection of the fcinfo's Aggref from AggGetAggref. This is required\n * when we receive no intermediate rows.\n *\n * Returns NULL if the Aggref isn't our expected shape.\n */\nstatic StypeBox *\nTryCreateStypeBoxFromFcinfoAggref(FunctionCallInfo fcinfo)\n{\n\tAggref *aggref = AggGetAggref(fcinfo);\n\tif (aggref == NULL || aggref->args == NIL)\n\t{\n\t\treturn NULL;\n\t}\n\n\tTargetEntry *aggArg = linitial(aggref->args);\n\tif (!IsA(aggArg->expr, Const))\n\t{\n\t\treturn NULL;\n\t}\n\n\tConst *aggConst = (Const *) aggArg->expr;\n\tif (aggConst->consttype != OIDOID && aggConst->consttype != REGPROCEDUREOID)\n\t{\n\t\treturn NULL;\n\t}\n\n\tForm_pg_aggregate aggform;\n\tStypeBox *box = pallocInAggContext(fcinfo, sizeof(StypeBox));\n\tbox->agg = DatumGetObjectId(aggConst->constvalue);\n\tHeapTuple aggTuple = GetAggregateForm(box->agg, &aggform);\n\tInitializeStypeBox(fcinfo, box, aggTuple, aggform->aggtranstype, NULL);\n\tReleaseSysCache(aggTuple);\n\n\treturn box;\n}\n\n\n/*\n * CreateAggregationArgumentContext creates an AggregationArgumentContext tailored\n * to handling the aggregation of input arguments identical to type at\n * 'argumentIndex' in 'fcinfo'.\n */\nstatic AggregationArgumentContext *\nCreateAggregationArgumentContext(FunctionCallInfo fcinfo, int argumentIndex)\n{\n\tAggregationArgumentContext *aggregationArgumentContext =\n\t\tpallocInAggContext(fcinfo, sizeof(AggregationArgumentContext));\n\n\t/* check if input comes combined into tuple/record */\n\tif (RECORDOID == get_fn_expr_argtype(fcinfo->flinfo, argumentIndex))\n\t{\n\t\t/* initialize context to handle aggregation argument combined into tuple */\n\t\tif (fcGetArgNull(fcinfo, argumentIndex))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"worker_partial_agg_sfunc: null record input\"),\n\t\t\t\t\t\t\terrhint(\"Elements of record may be null\")));\n\t\t}\n\n\t\t/* retrieve tuple header */\n\t\tHeapTupleHeader tupleHeader = PG_GETARG_HEAPTUPLEHEADER(argumentIndex);\n\n\t\t/* extract type info from the tuple */\n\t\tTupleDesc tupleDesc =\n\t\t\tlookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tupleHeader),\n\t\t\t\t\t\t\t\t   HeapTupleHeaderGetTypMod(tupleHeader));\n\n\t\t/* create a copy we can keep */\n\t\tTupleDesc tupleDescCopy = pallocInAggContext(fcinfo, TupleDescSize(tupleDesc));\n\t\tTupleDescCopy(tupleDescCopy, tupleDesc);\n\t\tReleaseTupleDesc(tupleDesc);\n\n\t\t/* build a HeapTuple control structure */\n\t\tHeapTuple tuple = pallocInAggContext(fcinfo, sizeof(HeapTupleData));\n\t\tItemPointerSetInvalid(&(tuple->t_self));\n\t\ttuple->t_tableOid = InvalidOid;\n\n\t\t/* initialize context to handle multiple aggregation arguments */\n\t\taggregationArgumentContext->argumentCount = tupleDescCopy->natts;\n\n\t\taggregationArgumentContext->values =\n\t\t\tpallocInAggContext(fcinfo, tupleDescCopy->natts * sizeof(Datum));\n\n\t\taggregationArgumentContext->nulls =\n\t\t\tpallocInAggContext(fcinfo, tupleDescCopy->natts * sizeof(bool));\n\n\t\taggregationArgumentContext->isTuple = true;\n\t\taggregationArgumentContext->tupleDesc = tupleDescCopy;\n\t\taggregationArgumentContext->tuple = tuple;\n\t}\n\telse\n\t{\n\t\t/* initialize context to handle single aggregation argument */\n\t\taggregationArgumentContext->argumentCount = 1;\n\t\taggregationArgumentContext->values = pallocInAggContext(fcinfo, sizeof(Datum));\n\t\taggregationArgumentContext->nulls = pallocInAggContext(fcinfo, sizeof(bool));\n\t\taggregationArgumentContext->isTuple = false;\n\t\taggregationArgumentContext->tupleDesc = NULL;\n\t\taggregationArgumentContext->tuple = NULL;\n\t}\n\n\treturn aggregationArgumentContext;\n}\n\n\n/*\n * ExtractAggregationValues extracts aggregation argument values and stores them in\n * the mutable fields of AggregationArgumentContext.\n */\nstatic void\nExtractAggregationValues(FunctionCallInfo fcinfo, int argumentIndex,\n\t\t\t\t\t\t AggregationArgumentContext *aggregationArgumentContext)\n{\n\tif (aggregationArgumentContext->isTuple)\n\t{\n\t\tif (fcGetArgNull(fcinfo, argumentIndex))\n\t\t{\n\t\t\t/* handle null record input */\n\t\t\tfor (int i = 0; i < aggregationArgumentContext->argumentCount; i++)\n\t\t\t{\n\t\t\t\taggregationArgumentContext->values[i] = 0;\n\t\t\t\taggregationArgumentContext->nulls[i] = true;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* handle tuple/record input */\n\t\t\tHeapTupleHeader tupleHeader =\n\t\t\t\tDatumGetHeapTupleHeader(fcGetArgValue(fcinfo, argumentIndex));\n\n\t\t\tif (HeapTupleHeaderGetNatts(\n\t\t\t\t\ttupleHeader) != aggregationArgumentContext->argumentCount ||\n\t\t\t\tHeapTupleHeaderGetTypeId(\n\t\t\t\t\ttupleHeader) != aggregationArgumentContext->tupleDesc->tdtypeid ||\n\t\t\t\tHeapTupleHeaderGetTypMod(\n\t\t\t\t\ttupleHeader) != aggregationArgumentContext->tupleDesc->tdtypmod)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"worker_partial_agg_sfunc received \"\n\t\t\t\t\t\t\t\t\t   \"incompatible record\")));\n\t\t\t}\n\n\t\t\taggregationArgumentContext->tuple->t_len =\n\t\t\t\tHeapTupleHeaderGetDatumLength(tupleHeader);\n\n\t\t\taggregationArgumentContext->tuple->t_data = tupleHeader;\n\n\t\t\t/* break down the tuple into fields */\n\t\t\theap_deform_tuple(\n\t\t\t\taggregationArgumentContext->tuple,\n\t\t\t\taggregationArgumentContext->tupleDesc,\n\t\t\t\taggregationArgumentContext->values,\n\t\t\t\taggregationArgumentContext->nulls);\n\t\t}\n\t}\n\telse\n\t{\n\t\t/* extract single argument value */\n\t\taggregationArgumentContext->values[0] = fcGetArgValue(fcinfo, argumentIndex);\n\t\taggregationArgumentContext->nulls[0] = fcGetArgNull(fcinfo, argumentIndex);\n\t}\n}\n\n\n/*\n * HandleTransition copies logic used in nodeAgg's advance_transition_function\n * for handling result of transition function.\n */\nstatic void\nHandleTransition(StypeBox *box, FunctionCallInfo fcinfo, FunctionCallInfo innerFcinfo)\n{\n\tDatum newVal = FunctionCallInvoke(innerFcinfo);\n\tbool newValIsNull = innerFcinfo->isnull;\n\n\tif (!box->transtypeByVal &&\n\t\tDatumGetPointer(newVal) != DatumGetPointer(box->value))\n\t{\n\t\tif (!newValIsNull)\n\t\t{\n\t\t\tMemoryContext aggregateContext;\n\n\t\t\tif (!AggCheckCallContext(fcinfo, &aggregateContext))\n\t\t\t{\n\t\t\t\telog(ERROR,\n\t\t\t\t\t \"HandleTransition called from non aggregate context\");\n\t\t\t}\n\n\t\t\tMemoryContext oldContext = MemoryContextSwitchTo(aggregateContext);\n\t\t\tif (!(DatumIsReadWriteExpandedObject(newVal,\n\t\t\t\t\t\t\t\t\t\t\t\t false, box->transtypeLen) &&\n\t\t\t\t  MemoryContextGetParent(DatumGetEOHP(newVal)->eoh_context) ==\n\t\t\t\t  CurrentMemoryContext))\n\t\t\t{\n\t\t\t\tnewVal = datumCopy(newVal, box->transtypeByVal, box->transtypeLen);\n\t\t\t}\n\t\t\tMemoryContextSwitchTo(oldContext);\n\t\t}\n\n\t\tif (!box->valueNull)\n\t\t{\n\t\t\tif (DatumIsReadWriteExpandedObject(box->value,\n\t\t\t\t\t\t\t\t\t\t\t   false, box->transtypeLen))\n\t\t\t{\n\t\t\t\tDeleteExpandedObject(box->value);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tpfree(DatumGetPointer(box->value));\n\t\t\t}\n\t\t}\n\t}\n\n\tbox->value = newVal;\n\tbox->valueNull = newValIsNull;\n}\n\n\n/*\n * HandleStrictUninit handles initialization of state for when\n * transition function is strict & state has not yet been initialized.\n */\nstatic void\nHandleStrictUninit(StypeBox *box, FunctionCallInfo fcinfo, Datum value)\n{\n\tMemoryContext aggregateContext;\n\n\tif (!AggCheckCallContext(fcinfo, &aggregateContext))\n\t{\n\t\telog(ERROR, \"HandleStrictUninit called from non aggregate context\");\n\t}\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(aggregateContext);\n\tbox->value = datumCopy(value, box->transtypeByVal, box->transtypeLen);\n\tMemoryContextSwitchTo(oldContext);\n\n\tbox->valueNull = false;\n\tbox->valueInit = true;\n}\n\n\n/*\n * worker_partial_agg_sfunc advances transition state,\n * essentially implementing the following pseudocode:\n *\n * (box, agg, ...) -> box\n * box.agg = agg;\n * box.value = agg.sfunc(box.value, ...);\n * return box\n */\nDatum\nworker_partial_agg_sfunc(PG_FUNCTION_ARGS)\n{\n\tStypeBox *box = NULL;\n\tForm_pg_aggregate aggform;\n\tLOCAL_FCINFO(innerFcinfo, FUNC_MAX_ARGS);\n\tFmgrInfo info;\n\tint argumentIndex = 0;\n\tbool initialCall = PG_ARGISNULL(0);\n\n\tif (initialCall)\n\t{\n\t\tif (PG_ARGISNULL(1))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"worker_partial_agg_sfunc received invalid null \"\n\t\t\t\t\t\t\t\t   \"input for second argument\")));\n\t\t}\n\t\tbox = pallocInAggContext(fcinfo, sizeof(StypeBox));\n\t\tbox->agg = PG_GETARG_OID(1);\n\t\tbox->aggregationArgumentContext = CreateAggregationArgumentContext(fcinfo, 2);\n\n\t\tif (!TypecheckWorkerPartialAggArgType(fcinfo, box))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"worker_partial_agg_sfunc could not confirm type \"\n\t\t\t\t\t\t\t\t   \"correctness\")));\n\t\t}\n\t}\n\telse\n\t{\n\t\tbox = (StypeBox *) PG_GETARG_POINTER(0);\n\t\tAssert(box->agg == PG_GETARG_OID(1));\n\t}\n\n\tHeapTuple aggtuple = GetAggregateForm(box->agg, &aggform);\n\tOid aggsfunc = aggform->aggtransfn;\n\n\tif (initialCall)\n\t{\n\t\tInitializeStypeBox(fcinfo, box, aggtuple, aggform->aggtranstype,\n\t\t\t\t\t\t   box->aggregationArgumentContext);\n\t}\n\tReleaseSysCache(aggtuple);\n\tif (initialCall)\n\t{\n\t\tget_typlenbyval(box->transtype,\n\t\t\t\t\t\t&box->transtypeLen,\n\t\t\t\t\t\t&box->transtypeByVal);\n\t}\n\n\t/*\n\t * Get aggregation values, which may be either wrapped in a\n\t * tuple (multi-argument case) or a singular, unwrapped value.\n\t */\n\tExtractAggregationValues(fcinfo, 2, box->aggregationArgumentContext);\n\n\tfmgr_info(aggsfunc, &info);\n\tif (info.fn_strict)\n\t{\n\t\tfor (argumentIndex = 0;\n\t\t\t argumentIndex < box->aggregationArgumentContext->argumentCount;\n\t\t\t argumentIndex++)\n\t\t{\n\t\t\tif (box->aggregationArgumentContext->nulls[argumentIndex])\n\t\t\t{\n\t\t\t\tPG_RETURN_POINTER(box);\n\t\t\t}\n\t\t}\n\n\t\tif (!box->valueInit)\n\t\t{\n\t\t\t/* For 'strict' transition functions, if the initial state value is null\n\t\t\t * then the first argument value of the first row with all-nonnull input\n\t\t\t * values replaces the state value.\n\t\t\t */\n\t\t\tDatum stateValue = box->aggregationArgumentContext->values[0];\n\t\t\tHandleStrictUninit(box, fcinfo, stateValue);\n\n\t\t\tPG_RETURN_POINTER(box);\n\t\t}\n\n\t\tif (box->valueNull)\n\t\t{\n\t\t\tPG_RETURN_POINTER(box);\n\t\t}\n\t}\n\n\t/* if aggregate function has N parameters, corresponding SFUNC has N+1 */\n\tInitFunctionCallInfoData(*innerFcinfo, &info,\n\t\t\t\t\t\t\t box->aggregationArgumentContext->argumentCount + 1,\n\t\t\t\t\t\t\t fcinfo->fncollation,\n\t\t\t\t\t\t\t fcinfo->context, fcinfo->resultinfo);\n\tfcSetArgExt(innerFcinfo, 0, box->value, box->valueNull);\n\n\n\tfor (argumentIndex = 0;\n\t\t argumentIndex < box->aggregationArgumentContext->argumentCount;\n\t\t argumentIndex++)\n\t{\n\t\tfcSetArgExt(innerFcinfo, argumentIndex + 1,\n\t\t\t\t\tbox->aggregationArgumentContext->values[argumentIndex],\n\t\t\t\t\tbox->aggregationArgumentContext->nulls[argumentIndex]);\n\t}\n\n\tHandleTransition(box, fcinfo, innerFcinfo);\n\n\tPG_RETURN_POINTER(box);\n}\n\n\n/*\n * Given an STypeBox, returns its transition type Oid\n * for the worker aggregate being computed.\n */\nstatic Oid\nGetAggregateTransitionType(StypeBox *box)\n{\n\tForm_pg_aggregate aggform;\n\tHeapTuple aggtuple = GetAggregateForm(box->agg, &aggform);\n\n\tif (aggform->aggcombinefn == InvalidOid)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"worker_partial_agg_ffunc expects an aggregate with COMBINEFUNC\")));\n\t}\n\n\tif (aggform->aggtranstype == INTERNALOID &&\n\t\taggform->aggserialfn == InvalidOid)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\n\t\t\t\t\t \"worker_partial_agg_ffunc does not support aggregates with INTERNAL transition state\")));\n\t}\n\n\tOid transType = aggform->aggtranstype;\n\tReleaseSysCache(aggtuple);\n\n\treturn transType;\n}\n\n\n/*\n * serializes transition state,\n * returning an StypeBox and setting transtype to the transition type Oid.\n */\nstatic StypeBox *\nWorkerPartialAggregateApplyFFunc(PG_FUNCTION_ARGS)\n{\n\tStypeBox *box = (StypeBox *) (PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0));\n\n\tif (box == NULL)\n\t{\n\t\tbox = TryCreateStypeBoxFromFcinfoAggref(fcinfo);\n\t}\n\n\tif (box == NULL || box->valueNull)\n\t{\n\t\treturn NULL;\n\t}\n\n\treturn box;\n}\n\n\n/* If the transtype is internal, we need to use the SERIALFUNC of the aggregate\n * to serialize the value in the worker. The COMBINEFUNC on the coordinator will\n * then use the DESERIALFUNC to deserialize the value.\n */\nstatic Datum\nCheckAndCallSerialFunc(PG_FUNCTION_ARGS, StypeBox *box, bool *outputIsNull)\n{\n\tLOCAL_FCINFO(serialFcInfo, 1);\n\tFmgrInfo serialInfo;\n\tForm_pg_aggregate aggform;\n\tHeapTuple aggtuple = GetAggregateForm(box->agg, &aggform);\n\n\tif (aggform->aggserialfn == InvalidOid)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\n\t\t\t\t\t \"worker_partial_agg_ffunc expects aggregates with internal transition state to have a SERIALFUNC\")));\n\t}\n\n\t/* Otherwise, first invoke the serialfunc to ensure that we produce a serialiable value from the boxValue */\n\tAssert(get_func_rettype(aggform->aggserialfn) == BYTEAOID);\n\tfmgr_info(aggform->aggserialfn, &serialInfo);\n\n\tInitFunctionCallInfoData(*serialFcInfo, &serialInfo, 1, fcinfo->fncollation,\n\t\t\t\t\t\t\t fcinfo->context, fcinfo->resultinfo);\n\tfcSetArgExt(serialFcInfo, 0, box->value, box->valueNull);\n\n\tDatum result = FunctionCallInvoke(serialFcInfo);\n\t*outputIsNull = serialFcInfo->isnull;\n\tReleaseSysCache(aggtuple);\n\treturn result;\n}\n\n\n/*\n * worker_partial_agg_ffunc serializes transition state,\n * essentially implementing the following pseudocode:\n *\n * (box) -> text\n * return box.agg.stype.output(box.value)\n */\nDatum\nworker_partial_agg_ffunc(PG_FUNCTION_ARGS)\n{\n\tLOCAL_FCINFO(innerFcinfo, 1);\n\n\tFmgrInfo info;\n\tOid typoutput = InvalidOid;\n\tbool typIsVarlena = false;\n\tStypeBox *box = WorkerPartialAggregateApplyFFunc(fcinfo);\n\tif (box == NULL)\n\t{\n\t\tPG_RETURN_NULL();\n\t}\n\n\tOid transtype = GetAggregateTransitionType(box);\n\n\tDatum boxValue = box->value;\n\tbool boxValueNull = box->valueNull;\n\tif (transtype == INTERNALOID)\n\t{\n\t\tereport(ERROR, (errmsg(\"worker_partial_agg_ffunc does not support output\"\n\t\t\t\t\t\t\t   \" of aggregates with INTERNAL transition state\")));\n\t}\n\n\tgetTypeOutputInfo(transtype, &typoutput, &typIsVarlena);\n\tfmgr_info(typoutput, &info);\n\n\tInitFunctionCallInfoData(*innerFcinfo, &info, 1, fcinfo->fncollation,\n\t\t\t\t\t\t\t fcinfo->context, fcinfo->resultinfo);\n\tfcSetArgExt(innerFcinfo, 0, boxValue, boxValueNull);\n\n\tDatum result = FunctionCallInvoke(innerFcinfo);\n\n\tif (innerFcinfo->isnull)\n\t{\n\t\tPG_RETURN_NULL();\n\t}\n\tPG_RETURN_DATUM(result);\n}\n\n\n/*\n * worker_partial_binary_agg_ffunc serializes transition state,\n * essentially implementing the following pseudocode:\n *\n * (box) -> bytea\n * return box.agg.stype.output(box.value)\n */\nDatum\nworker_binary_partial_agg_ffunc(PG_FUNCTION_ARGS)\n{\n\tLOCAL_FCINFO(innerFcinfo, 1);\n\n\tFmgrInfo info;\n\tOid typoutput = InvalidOid;\n\tbool typIsVarlena = false;\n\tStypeBox *box = WorkerPartialAggregateApplyFFunc(fcinfo);\n\tif (box == NULL)\n\t{\n\t\tPG_RETURN_NULL();\n\t}\n\n\tOid transtype = GetAggregateTransitionType(box);\n\n\n\tDatum boxValue = box->value;\n\tbool boxValueNull = box->valueNull;\n\tif (transtype == INTERNALOID)\n\t{\n\t\t/* Call and store the output of the SERIALFUNC - the output type\n\t\t * then is always BYTEAOID. */\n\t\tboxValue = CheckAndCallSerialFunc(fcinfo, box, &boxValueNull);\n\t\ttranstype = BYTEAOID;\n\t}\n\n\tgetTypeBinaryOutputInfo(transtype, &typoutput, &typIsVarlena);\n\tfmgr_info(typoutput, &info);\n\n\tInitFunctionCallInfoData(*innerFcinfo, &info, 1, fcinfo->fncollation,\n\t\t\t\t\t\t\t fcinfo->context, fcinfo->resultinfo);\n\tfcSetArgExt(innerFcinfo, 0, boxValue, boxValueNull);\n\n\tDatum result = FunctionCallInvoke(innerFcinfo);\n\n\tif (innerFcinfo->isnull)\n\t{\n\t\tPG_RETURN_NULL();\n\t}\n\tPG_RETURN_DATUM(result);\n}\n\n\nstatic Datum\nDeserializeBoxValue(Oid deserialFunc, Datum value, bool valueNull,\n\t\t\t\t\tFunctionCallInfo fcinfo,\n\t\t\t\t\tbool *outputIsNull)\n{\n\tLOCAL_FCINFO(deserialFcInfo, 3);\n\tFmgrInfo deserialInfo;\n\n\tfmgr_info(deserialFunc, &deserialInfo);\n\n\tInitFunctionCallInfoData(*deserialFcInfo, &deserialInfo, 2, fcinfo->fncollation,\n\t\t\t\t\t\t\t fcinfo->context, fcinfo->resultinfo);\n\tfcSetArgExt(deserialFcInfo, 0, value, valueNull);\n\n\t/* Arg1 is not used and is internal */\n\tfcSetArgExt(deserialFcInfo, 1, (Datum) 0, false);\n\n\tDatum result = FunctionCallInvoke(deserialFcInfo);\n\t*outputIsNull = deserialFcInfo->isnull;\n\treturn result;\n}\n\n\nstatic Datum\nCoordinatorCombineAggSfuncCore(PG_FUNCTION_ARGS, bool isBinaryInput)\n{\n\tLOCAL_FCINFO(innerFcinfo, 3);\n\tFmgrInfo info;\n\tForm_pg_aggregate aggform;\n\tForm_pg_type transtypeform;\n\tOid deserialFunc = InvalidOid;\n\tDatum value;\n\tStypeBox *box = NULL;\n\n\tif (PG_ARGISNULL(0))\n\t{\n\t\tbox = pallocInAggContext(fcinfo, sizeof(StypeBox));\n\t\tbox->agg = PG_GETARG_OID(1);\n\t}\n\telse\n\t{\n\t\tbox = (StypeBox *) PG_GETARG_POINTER(0);\n\t\tAssert(box->agg == PG_GETARG_OID(1));\n\t}\n\n\tHeapTuple aggtuple = GetAggregateForm(box->agg, &aggform);\n\n\tif (aggform->aggcombinefn == InvalidOid)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"coord_combine_agg_sfunc expects an aggregate with COMBINEFUNC\")));\n\t}\n\n\tif (aggform->aggtranstype == INTERNALOID)\n\t{\n\t\tif (aggform->aggdeserialfn == InvalidOid)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errmsg(\n\t\t\t\t\t\t \"coord_combine_agg_sfunc does not support aggregates with INTERNAL transition state\")));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdeserialFunc = aggform->aggdeserialfn;\n\t\t}\n\t}\n\n\tOid combine = aggform->aggcombinefn;\n\n\tif (PG_ARGISNULL(0))\n\t{\n\t\tInitializeStypeBox(fcinfo, box, aggtuple, aggform->aggtranstype, NULL);\n\t}\n\n\tReleaseSysCache(aggtuple);\n\n\tif (PG_ARGISNULL(0))\n\t{\n\t\tget_typlenbyval(box->transtype,\n\t\t\t\t\t\t&box->transtypeLen,\n\t\t\t\t\t\t&box->transtypeByVal);\n\t}\n\n\tbool valueNull = PG_ARGISNULL(2);\n\n\t/* If the stype is internal, this needs to through first\n\t * deserializing the wire output to the intermediate state.\n\t */\n\tOid deserializationType = box->transtype;\n\tif (box->transtype == INTERNALOID)\n\t{\n\t\t/* For a deserialfunc, the input is a BYTEAOID */\n\t\tdeserializationType = BYTEAOID;\n\t}\n\n\tHeapTuple deserializationTypeTuple = GetTypeForm(deserializationType, &transtypeform);\n\tOid ioparam = getTypeIOParam(deserializationTypeTuple);\n\tOid deserial = isBinaryInput ? transtypeform->typreceive : transtypeform->typinput;\n\tReleaseSysCache(deserializationTypeTuple);\n\n\tfmgr_info(deserial, &info);\n\tif (valueNull && info.fn_strict)\n\t{\n\t\tvalue = (Datum) 0;\n\t}\n\telse if (isBinaryInput)\n\t{\n\t\tStringInfoData buf;\n\n\t\tInitFunctionCallInfoData(*innerFcinfo, &info, 3, fcinfo->fncollation,\n\t\t\t\t\t\t\t\t fcinfo->context, fcinfo->resultinfo);\n\t\tif (valueNull)\n\t\t{\n\t\t\tfcSetArgExt(innerFcinfo, 0, (Datum) 0, valueNull);\n\n\t\t\tfcSetArg(innerFcinfo, 1, ObjectIdGetDatum(ioparam));\n\t\t\tfcSetArg(innerFcinfo, 2, Int32GetDatum(-1)); /* typmod */\n\n\t\t\tvalue = FunctionCallInvoke(innerFcinfo);\n\t\t\tvalueNull = innerFcinfo->isnull;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbytea *byteaInput = PG_GETARG_BYTEA_PP(2);\n#if PG_VERSION_NUM >= 170000\n\t\t\tinitReadOnlyStringInfo(&buf,\n\t\t\t\t\t\t\t\t   (char *) VARDATA_ANY(byteaInput),\n\t\t\t\t\t\t\t\t   VARSIZE_ANY_EXHDR(byteaInput));\n\t\t\tfcSetArg(innerFcinfo, 0, PointerGetDatum(&buf));\n\t\t\tfcSetArg(innerFcinfo, 1, ObjectIdGetDatum(ioparam));\n\t\t\tfcSetArg(innerFcinfo, 2, Int32GetDatum(-1)); /* typmod */\n\n\t\t\tvalue = FunctionCallInvoke(innerFcinfo);\n\t\t\tvalueNull = innerFcinfo->isnull;\n#else\n\n\t\t\t/*\n\t\t\t * Read Only StringInfo is not a characteristic in pg16\n\t\t\t * or below. We can't follow what's there in arrayfuncs since\n\t\t\t * the Send function won't guarantee to append an extra null byte at the end.\n\t\t\t * So we manually set up a StringInfo with a trailing null byte.\n\t\t\t */\n\t\t\tinitStringInfo(&buf);\n\t\t\tappendBinaryStringInfo(&buf,\n\t\t\t\t\t\t\t\t   (char *) VARDATA_ANY(byteaInput),\n\t\t\t\t\t\t\t\t   VARSIZE_ANY_EXHDR(byteaInput));\n\t\t\tfcSetArg(innerFcinfo, 0, PointerGetDatum(&buf));\n\t\t\tfcSetArg(innerFcinfo, 1, ObjectIdGetDatum(ioparam));\n\t\t\tfcSetArg(innerFcinfo, 2, Int32GetDatum(-1)); /* typmod */\n\n\t\t\tvalue = FunctionCallInvoke(innerFcinfo);\n\t\t\tvalueNull = innerFcinfo->isnull;\n\t\t\tpfree(buf.data);\n#endif\n\t\t}\n\t}\n\telse\n\t{\n\t\tInitFunctionCallInfoData(*innerFcinfo, &info, 3, fcinfo->fncollation,\n\t\t\t\t\t\t\t\t fcinfo->context, fcinfo->resultinfo);\n\t\tfcSetArgExt(innerFcinfo, 0, PG_GETARG_DATUM(2), valueNull);\n\t\tfcSetArg(innerFcinfo, 1, ObjectIdGetDatum(ioparam));\n\t\tfcSetArg(innerFcinfo, 2, Int32GetDatum(-1)); /* typmod */\n\n\t\tvalue = FunctionCallInvoke(innerFcinfo);\n\t\tvalueNull = innerFcinfo->isnull;\n\t}\n\n\t/* If the stype is internal, we need to go through one additional step\n\t * of now calling the deserialfunc to go from the serialized type to\n\t * internal before we call the combine function.\n\t */\n\tif (box->transtype == INTERNALOID && !valueNull)\n\t{\n\t\tvalue = DeserializeBoxValue(deserialFunc, value, valueNull, fcinfo, &valueNull);\n\t}\n\n\tfmgr_info(combine, &info);\n\n\tif (info.fn_strict)\n\t{\n\t\tif (valueNull)\n\t\t{\n\t\t\tPG_RETURN_POINTER(box);\n\t\t}\n\n\t\tif (!box->valueInit)\n\t\t{\n\t\t\tHandleStrictUninit(box, fcinfo, value);\n\t\t\tPG_RETURN_POINTER(box);\n\t\t}\n\n\t\tif (box->valueNull)\n\t\t{\n\t\t\tPG_RETURN_POINTER(box);\n\t\t}\n\t}\n\n\tInitFunctionCallInfoData(*innerFcinfo, &info, 2, fcinfo->fncollation,\n\t\t\t\t\t\t\t fcinfo->context, fcinfo->resultinfo);\n\tfcSetArgExt(innerFcinfo, 0, box->value, box->valueNull);\n\tfcSetArgExt(innerFcinfo, 1, value, valueNull);\n\n\tHandleTransition(box, fcinfo, innerFcinfo);\n\n\tPG_RETURN_POINTER(box);\n}\n\n\n/*\n * coord_combine_agg_sfunc deserializes transition state from worker\n * & advances transition state using combinefunc,\n * essentially implementing the following pseudocode:\n *\n * (box, agg, text) -> box\n * box.agg = agg\n * box.value = agg.combine(box.value, agg.stype.input(text))\n * return box\n */\nDatum\ncoord_combine_agg_sfunc(PG_FUNCTION_ARGS)\n{\n\tbool isBinaryInput = false;\n\treturn CoordinatorCombineAggSfuncCore(fcinfo, isBinaryInput);\n}\n\n\n/*\n * coord_binary_combine_agg_sfunc deserializes transition state from worker\n * & advances transition state using combinefunc,\n * essentially implementing the following pseudocode:\n *\n * (box, agg, bytea) -> box\n * box.agg = agg\n * box.value = agg.combine(box.value, agg.stype.receive(bytea))\n * return box\n */\nDatum\ncoord_binary_combine_agg_sfunc(PG_FUNCTION_ARGS)\n{\n\tbool isBinaryInput = true;\n\treturn CoordinatorCombineAggSfuncCore(fcinfo, isBinaryInput);\n}\n\n\n/*\n * Applies finalfunc of aggregate to state,\n * essentially implementing the following pseudocode:\n *\n * (box, ...) -> fval\n * return box.agg.ffunc(box.value)\n * Used by both the binary and text versions of the finalfunc.\n */\nstatic Datum\nCoordCombineAggFuncCore(PG_FUNCTION_ARGS)\n{\n\tStypeBox *box = (StypeBox *) (PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0));\n\tLOCAL_FCINFO(innerFcinfo, FUNC_MAX_ARGS);\n\tFmgrInfo info;\n\tint innerNargs = 0;\n\tForm_pg_aggregate aggform;\n\tForm_pg_proc ffuncform;\n\n\tif (box == NULL)\n\t{\n\t\tbox = TryCreateStypeBoxFromFcinfoAggref(fcinfo);\n\n\t\tif (box == NULL)\n\t\t{\n\t\t\tPG_RETURN_NULL();\n\t\t}\n\t}\n\n\tHeapTuple aggtuple = GetAggregateForm(box->agg, &aggform);\n\tOid ffunc = aggform->aggfinalfn;\n\tbool fextra = aggform->aggfinalextra;\n\tReleaseSysCache(aggtuple);\n\n\tif (!TypecheckCoordCombineAggReturnType(fcinfo, ffunc, box))\n\t{\n\t\tereport(ERROR, (errmsg(\"coord_combine_agg_ffunc could not \"\n\t\t\t\t\t\t\t   \"confirm type correctness\")));\n\t}\n\n\tif (ffunc == InvalidOid)\n\t{\n\t\tif (box->valueNull)\n\t\t{\n\t\t\tPG_RETURN_NULL();\n\t\t}\n\t\tPG_RETURN_DATUM(box->value);\n\t}\n\n\tHeapTuple ffunctuple = GetProcForm(ffunc, &ffuncform);\n\tbool finalStrict = ffuncform->proisstrict;\n\tReleaseSysCache(ffunctuple);\n\n\tif (finalStrict && box->valueNull)\n\t{\n\t\tPG_RETURN_NULL();\n\t}\n\n\tif (fextra)\n\t{\n\t\tinnerNargs = fcinfo->nargs;\n\t}\n\telse\n\t{\n\t\tinnerNargs = 1;\n\t}\n\tfmgr_info(ffunc, &info);\n\tInitFunctionCallInfoData(*innerFcinfo, &info, innerNargs, fcinfo->fncollation,\n\t\t\t\t\t\t\t fcinfo->context, fcinfo->resultinfo);\n\tfcSetArgExt(innerFcinfo, 0, box->value, box->valueNull);\n\tfor (int argumentIndex = 1; argumentIndex < innerNargs; argumentIndex++)\n\t{\n\t\tfcSetArgNull(innerFcinfo, argumentIndex);\n\t}\n\n\tDatum result = FunctionCallInvoke(innerFcinfo);\n\tfcinfo->isnull = innerFcinfo->isnull;\n\treturn result;\n}\n\n\n/*\n * coord_combine_agg_ffunc applies finalfunc of aggregate to state,\n * essentially implementing the following pseudocode:\n *\n * (box, ...) -> fval\n * return box.agg.ffunc(box.value)\n */\nDatum\ncoord_combine_agg_ffunc(PG_FUNCTION_ARGS)\n{\n\treturn CoordCombineAggFuncCore(fcinfo);\n}\n\n\n/*\n * coord_binary_combine_agg_ffunc applies finalfunc of aggregate to state,\n * essentially implementing the following pseudocode:\n *\n * (box, ...) -> fval\n * return box.agg.ffunc(box.value)\n */\nDatum\ncoord_binary_combine_agg_ffunc(PG_FUNCTION_ARGS)\n{\n\treturn CoordCombineAggFuncCore(fcinfo);\n}\n\n\n/*\n * TypecheckWorkerPartialAggArgType returns whether the arguments being passed to\n * worker_partial_agg match the arguments expected by the aggregate being distributed.\n */\nstatic bool\nTypecheckWorkerPartialAggArgType(FunctionCallInfo fcinfo, StypeBox *box)\n{\n\tAggref *aggref = AggGetAggref(fcinfo);\n\tif (aggref == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tAssert(list_length(aggref->args) == 2);\n\tTargetEntry *aggarg = list_nth(aggref->args, 1);\n\n\tbool argtypesNull;\n\tHeapTuple proctuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(box->agg));\n\tif (!HeapTupleIsValid(proctuple))\n\t{\n\t\treturn false;\n\t}\n\n\tDatum argtypes = SysCacheGetAttr(PROCOID, proctuple,\n\t\t\t\t\t\t\t\t\t Anum_pg_proc_proargtypes,\n\t\t\t\t\t\t\t\t\t &argtypesNull);\n\tAssert(!argtypesNull);\n\tReleaseSysCache(proctuple);\n\n\tif (ARR_NDIM(DatumGetArrayTypeP(argtypes)) != 1)\n\t{\n\t\telog(ERROR, \"worker_partial_agg_sfunc cannot type check aggregates \"\n\t\t\t\t\t\"taking multi-dimensional arguments\");\n\t}\n\n\tint aggregateArgCount = ARR_DIMS(DatumGetArrayTypeP(argtypes))[0];\n\n\t/* we expect aggregate function to have at least a single parameter */\n\tif (box->aggregationArgumentContext->argumentCount != aggregateArgCount)\n\t{\n\t\treturn false;\n\t}\n\n\tint aggregateArgIndex = 0;\n\tDatum argType;\n\n\tif (box->aggregationArgumentContext->isTuple)\n\t{\n\t\t/* check if record element types match aggregate input parameters */\n\t\tfor (aggregateArgIndex = 0; aggregateArgIndex < aggregateArgCount;\n\t\t\t aggregateArgIndex++)\n\t\t{\n\t\t\targType = array_get_element(argtypes, 1, &aggregateArgIndex, -1, sizeof(Oid),\n\t\t\t\t\t\t\t\t\t\ttrue, 'i', &argtypesNull);\n\t\t\tAssert(!argtypesNull);\n\t\t\tTupleDesc tupleDesc = box->aggregationArgumentContext->tupleDesc;\n\t\t\tif (argType != TupleDescAttr(tupleDesc, aggregateArgIndex)->atttypid)\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\telse\n\t{\n\t\targType = array_get_element(argtypes, 1, &aggregateArgIndex, -1, sizeof(Oid),\n\t\t\t\t\t\t\t\t\ttrue, 'i', &argtypesNull);\n\t\tAssert(!argtypesNull);\n\n\t\treturn exprType((Node *) aggarg->expr) == DatumGetObjectId(argType);\n\t}\n}\n\n\n/*\n * TypecheckCoordCombineAggReturnType returns whether the return type of the aggregate\n * being distributed by coord_combine_agg matches the null constant used to inform postgres\n * what the aggregate's expected return type is.\n */\nstatic bool\nTypecheckCoordCombineAggReturnType(FunctionCallInfo fcinfo, Oid ffunc, StypeBox *box)\n{\n\tAggref *aggref = AggGetAggref(fcinfo);\n\tif (aggref == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tOid finalType = ffunc == InvalidOid ?\n\t\t\t\t\tbox->transtype : get_func_rettype(ffunc);\n\n\tAssert(list_length(aggref->args) == 3);\n\tTargetEntry *nulltag = list_nth(aggref->args, 2);\n\n\treturn nulltag != NULL && IsA(nulltag->expr, Const) &&\n\t\t   ((Const *) nulltag->expr)->consttype == finalType;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/array_type.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * array_type.c\n *\n * Utility functions for dealing with array types.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/pg_type.h\"\n#include \"nodes/pg_list.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/utils/array_type.h\"\n\n\n/*\n * DeconstructArrayObject takes in a single dimensional array, and deserializes\n * this array's members into an array of datum objects. The function then\n * returns this datum array.\n */\nDatum *\nDeconstructArrayObject(ArrayType *arrayObject)\n{\n\tDatum *datumArray = NULL;\n\tbool *datumArrayNulls = NULL;\n\tint datumArrayLength = 0;\n\n\tbool typeByVal = false;\n\tchar typeAlign = 0;\n\tint16 typeLength = 0;\n\n\tbool arrayHasNull = ARR_HASNULL(arrayObject);\n\tif (arrayHasNull)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),\n\t\t\t\t\t\terrmsg(\"worker array object cannot contain null values\")));\n\t}\n\n\tOid typeId = ARR_ELEMTYPE(arrayObject);\n\tget_typlenbyvalalign(typeId, &typeLength, &typeByVal, &typeAlign);\n\n\tdeconstruct_array(arrayObject, typeId, typeLength, typeByVal, typeAlign,\n\t\t\t\t\t  &datumArray, &datumArrayNulls, &datumArrayLength);\n\n\treturn datumArray;\n}\n\n\n/*\n * ArrayObjectCount takes in a single dimensional array, and returns the number\n * of elements in this array.\n */\nint32\nArrayObjectCount(ArrayType *arrayObject)\n{\n\tint32 dimensionCount = ARR_NDIM(arrayObject);\n\tint32 *dimensionLengthArray = ARR_DIMS(arrayObject);\n\n\tif (dimensionCount == 0)\n\t{\n\t\treturn 0;\n\t}\n\n\t/* we currently allow split point arrays to have only one subarray */\n\tAssert(dimensionCount == 1);\n\n\tint32 arrayLength = ArrayGetNItems(dimensionCount, dimensionLengthArray);\n\tif (arrayLength <= 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),\n\t\t\t\t\t\terrmsg(\"worker array object cannot be empty\")));\n\t}\n\n\treturn arrayLength;\n}\n\n\n/*\n * DatumArrayToArrayType converts the provided Datum array (of the specified\n * length and type) into an ArrayType suitable for returning from a UDF.\n */\nArrayType *\nDatumArrayToArrayType(Datum *datumArray, int datumCount, Oid datumTypeId)\n{\n\tint16 typeLength = 0;\n\tbool typeByValue = false;\n\tchar typeAlignment = 0;\n\n\tget_typlenbyvalalign(datumTypeId, &typeLength, &typeByValue, &typeAlignment);\n\tArrayType *arrayObject = construct_array(datumArray, datumCount, datumTypeId,\n\t\t\t\t\t\t\t\t\t\t\t typeLength, typeByValue, typeAlignment);\n\n\treturn arrayObject;\n}\n\n\n/*\n * Converts ArrayType to List.\n */\nList *\nIntegerArrayTypeToList(ArrayType *arrayObject)\n{\n\tList *list = NULL;\n\tDatum *datumObjectArray = DeconstructArrayObject(arrayObject);\n\tint arrayObjectCount = ArrayObjectCount(arrayObject);\n\n\tfor (int index = 0; index < arrayObjectCount; index++)\n\t{\n\t\tint32 intObject = DatumGetInt32(datumObjectArray[index]);\n\t\tlist = lappend_int(list, intObject);\n\t}\n\n\treturn list;\n}\n\n\n/*\n * Converts Text ArrayType to Integer List.\n */\nextern List *\nTextArrayTypeToIntegerList(ArrayType *arrayObject)\n{\n\tList *list = NULL;\n\tDatum *datumObjectArray = DeconstructArrayObject(arrayObject);\n\tint arrayObjectCount = ArrayObjectCount(arrayObject);\n\n\tfor (int index = 0; index < arrayObjectCount; index++)\n\t{\n\t\tchar *intAsStr = text_to_cstring(DatumGetTextP(datumObjectArray[index]));\n\t\tlist = lappend_int(list, pg_strtoint32(intAsStr));\n\t}\n\n\treturn list;\n}\n\n\n/*\n * IntArrayToDatum\n *\n * Convert an integer array to the datum int array format\n * (currently used for nodes_involved in pg_dist_background_task)\n *\n * Returns the array in the form of a Datum, or PointerGetDatum(NULL)\n * if the int_array is empty.\n */\nDatum\nIntArrayToDatum(uint32 int_array_size, int int_array[])\n{\n\tif (int_array_size == 0)\n\t{\n\t\treturn PointerGetDatum(NULL);\n\t}\n\n\tArrayBuildState *astate = NULL;\n\tfor (int i = 0; i < int_array_size; i++)\n\t{\n\t\tDatum dvalue = Int32GetDatum(int_array[i]);\n\t\tbool disnull = false;\n\t\tOid element_type = INT4OID;\n\t\tastate = accumArrayResult(astate, dvalue, disnull, element_type,\n\t\t\t\t\t\t\t\t  CurrentMemoryContext);\n\t}\n\n\treturn makeArrayResult(astate, CurrentMemoryContext);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/background_jobs.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * background_jobs.c\n *\t  Background jobs run as a background worker, spawned from the\n *\t  maintenance daemon. Jobs have tasks, tasks can depend on other\n *\t  tasks before execution.\n *\n * This file contains the code for two separate background workers to\n * achieve the goal of running background tasks asynchronously from the\n * main database workload. This first background worker is the\n * Background Tasks Queue Monitor. This background worker keeps track of\n * tasks recorded in pg_dist_background_task and ensures execution based\n * on a statemachine. When a task needs to be executed it starts a\n * Background Task Executor that executes the sql statement defined in the\n * task. The output of the Executor is shared with the Monitor via a\n * shared memory queue.\n *\n * To make sure there is only ever exactly one monitor running per database\n * it takes an exclusive lock on the CITUS_BACKGROUND_TASK_MONITOR\n * operation. This lock is consulted from the maintenance daemon to only\n * spawn a new monitor when the lock is not held.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n#include \"pgstat.h\"\n#include \"safe_mem_lib.h\"\n\n#include \"access/xact.h\"\n#include \"commands/dbcommands.h\"\n#include \"common/hashfn.h\"\n#include \"libpq/pqformat.h\"\n#include \"libpq/pqmq.h\"\n#include \"libpq/pqsignal.h\"\n#include \"parser/analyze.h\"\n#include \"storage/dsm.h\"\n#include \"storage/ipc.h\"\n#include \"storage/procarray.h\"\n#include \"storage/shm_mq.h\"\n#include \"storage/shm_toc.h\"\n#include \"tcop/pquery.h\"\n#include \"tcop/tcopprot.h\"\n#include \"tcop/utility.h\"\n#include \"utils/fmgrprotos.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/memutils.h\"\n#include \"utils/portal.h\"\n#include \"utils/ps_status.h\"\n#include \"utils/resowner.h\"\n#include \"utils/snapmgr.h\"\n#include \"utils/timeout.h\"\n\n#include \"distributed/background_jobs.h\"\n#include \"distributed/background_worker_utils.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/maintenanced.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/shard_rebalancer.h\"\n\n/* Table-of-contents constants for our dynamic shared memory segment. */\n#define CITUS_BACKGROUND_TASK_MAGIC 0x51028081\n#define CITUS_BACKGROUND_TASK_KEY_DATABASE 0\n#define CITUS_BACKGROUND_TASK_KEY_USERNAME 1\n#define CITUS_BACKGROUND_TASK_KEY_COMMAND 2\n#define CITUS_BACKGROUND_TASK_KEY_QUEUE 3\n#define CITUS_BACKGROUND_TASK_KEY_TASK_ID 4\n#define CITUS_BACKGROUND_TASK_KEY_JOB_ID 5\n#define CITUS_BACKGROUND_TASK_NKEYS 6\n\nstatic BackgroundWorkerHandle * StartCitusBackgroundTaskExecutor(char *database,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t char *user,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t char *command,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t int64 taskId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t int64 jobId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t dsm_segment **pSegment);\nstatic void ExecuteSqlString(const char *sql);\nstatic shm_mq_result ConsumeTaskWorkerOutput(shm_mq_handle *responseq, StringInfo message,\n\t\t\t\t\t\t\t\t\t\t\t bool *hadError);\nstatic void UpdateDependingTasks(BackgroundTask *task);\nstatic int64 CalculateBackoffDelay(int retryCount);\nstatic bool NewExecutorExceedsCitusLimit(QueueMonitorExecutionContext *\n\t\t\t\t\t\t\t\t\t\t queueMonitorExecutionContext);\nstatic bool NewExecutorExceedsPgMaxWorkers(BackgroundWorkerHandle *handle,\n\t\t\t\t\t\t\t\t\t\t   QueueMonitorExecutionContext *\n\t\t\t\t\t\t\t\t\t\t   queueMonitorExecutionContext);\nstatic bool AssignRunnableTaskToNewExecutor(BackgroundTask *runnableTask,\n\t\t\t\t\t\t\t\t\t\t\tQueueMonitorExecutionContext *\n\t\t\t\t\t\t\t\t\t\t\tqueueMonitorExecutionContext);\nstatic void AssignRunnableTasks(QueueMonitorExecutionContext *\n\t\t\t\t\t\t\t\tqueueMonitorExecutionContext);\nstatic List * GetRunningTaskEntries(HTAB *currentExecutors);\nstatic shm_mq_result ReadFromExecutorQueue(BackgroundExecutorHashEntry *\n\t\t\t\t\t\t\t\t\t\t   backgroundExecutorHashEntry,\n\t\t\t\t\t\t\t\t\t\t   bool *hadError);\nstatic void CheckAndResetLastWorkerAllocationFailure(QueueMonitorExecutionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t queueMonitorExecutionContext);\nstatic TaskExecutionStatus TaskConcurrentCancelCheck(TaskExecutionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t taskExecutionContext);\nstatic TaskExecutionStatus ConsumeExecutorQueue(TaskExecutionContext *\n\t\t\t\t\t\t\t\t\t\t\t\ttaskExecutionContext);\nstatic void TaskHadError(TaskExecutionContext *taskExecutionContext);\nstatic void TaskEnded(TaskExecutionContext *taskExecutionContext);\nstatic void TerminateAllTaskExecutors(HTAB *currentExecutors);\nstatic HTAB * GetRunningUniqueJobIds(HTAB *currentExecutors);\nstatic void CancelAllTaskExecutors(HTAB *currentExecutors);\nstatic bool MonitorGotTerminationOrCancellationRequest();\nstatic void QueueMonitorSigTermHandler(SIGNAL_ARGS);\nstatic void QueueMonitorSigIntHandler(SIGNAL_ARGS);\nstatic void QueueMonitorSigHupHandler(SIGNAL_ARGS);\nstatic void DecrementParallelTaskCountForNodesInvolved(BackgroundTask *task);\n\n/* flags set by signal handlers */\nstatic volatile sig_atomic_t GotSigterm = false;\nstatic volatile sig_atomic_t GotSigint = false;\nstatic volatile sig_atomic_t GotSighup = false;\n\n/* keeping track of parallel background tasks per node */\nHTAB *ParallelTasksPerNode = NULL;\nint MaxBackgroundTaskExecutorsPerNode = 1;\n\nPG_FUNCTION_INFO_V1(citus_job_cancel);\nPG_FUNCTION_INFO_V1(citus_job_wait);\nPG_FUNCTION_INFO_V1(citus_task_wait);\n\n\n/*\n * pg_catalog.citus_job_cancel(jobid bigint) void\n *   cancels a scheduled/running job\n *\n * When cancelling a job there are two phases.\n *  1. scan all associated tasks and transition all tasks that are not already in their\n *     terminal state to cancelled. Except if the task is currently running.\n *  2. for all running tasks we send a cancelation signal to the backend running the\n *     query. The background executor/monitor will transition this task to cancelled.\n *\n * We apply the same policy checks as pg_cancel_backend to check if a user can cancel a\n * job.\n */\nDatum\ncitus_job_cancel(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tint64 jobid = PG_GETARG_INT64(0);\n\n\t/* Cancel all tasks that were scheduled before */\n\tList *pids = CancelTasksForJob(jobid);\n\n\t/* send cancellation to any running backends */\n\tint pid = 0;\n\tforeach_declared_int(pid, pids)\n\t{\n\t\tDatum pidDatum = Int32GetDatum(pid);\n\t\tDatum signalSuccessDatum = DirectFunctionCall1(pg_cancel_backend, pidDatum);\n\t\tbool signalSuccess = DatumGetBool(signalSuccessDatum);\n\t\tif (!signalSuccess)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"could not send signal to process %d: %m\", pid)));\n\t\t}\n\t}\n\n\tUpdateBackgroundJob(jobid);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * pg_catalog.citus_job_wait(jobid bigint,\n *                            desired_status citus_job_status DEFAULT NULL) boolean\n *   waits till a job reaches a desired status, or can't reach the status anymore because\n *   it reached a (different) terminal state. When no desired_status is given it will\n *   assume any terminal state as its desired status. The function returns if the\n *   desired_state was reached.\n *\n * The current implementation is a polling implementation with an interval of 1 second.\n * Ideally we would have some synchronization between the background tasks queue monitor\n * and any backend calling this function to receive a signal when the job changes state.\n */\nDatum\ncitus_job_wait(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tint64 jobid = PG_GETARG_INT64(0);\n\n\t/* parse the optional desired_status argument */\n\tbool hasDesiredStatus = !PG_ARGISNULL(1);\n\tBackgroundJobStatus desiredStatus = { 0 };\n\tif (hasDesiredStatus)\n\t{\n\t\tdesiredStatus = BackgroundJobStatusByOid(PG_GETARG_OID(1));\n\t}\n\n\tcitus_job_wait_internal(jobid, hasDesiredStatus ? &desiredStatus : NULL);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * pg_catalog.citus_task_wait(taskid bigint,\n *                            desired_status citus_task_status DEFAULT NULL) boolean\n *   waits till a task reaches a desired status, or can't reach the status anymore because\n *   it reached a (different) terminal state. When no desired_status is given it will\n *   assume any terminal state as its desired status. The function returns if the\n *   desired_state was reached.\n *\n * The current implementation is a polling implementation with an interval of 0.1 seconds.\n * Ideally we would have some synchronization between the background tasks queue monitor\n * and any backend calling this function to receive a signal when the task changes state.\n */\nDatum\ncitus_task_wait(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tint64 taskid = PG_GETARG_INT64(0);\n\n\t/* parse the optional desired_status argument */\n\tbool hasDesiredStatus = !PG_ARGISNULL(1);\n\tBackgroundTaskStatus desiredStatus = { 0 };\n\tif (hasDesiredStatus)\n\t{\n\t\tdesiredStatus = BackgroundTaskStatusByOid(PG_GETARG_OID(1));\n\t}\n\n\tcitus_task_wait_internal(taskid, hasDesiredStatus ? &desiredStatus : NULL);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * citus_job_wait_internal implements the waiting on a job for reuse in other areas where\n * we want to wait on jobs. eg the background rebalancer.\n *\n * When a desiredStatus is provided it will provide an error when a different state is\n * reached and the state cannot ever reach the desired state anymore.\n */\nvoid\ncitus_job_wait_internal(int64 jobid, BackgroundJobStatus *desiredStatus)\n{\n\t/*\n\t * Since we are wait polling we will actually allocate memory on every poll. To make\n\t * sure we don't put unneeded pressure on the memory we create a context that we clear\n\t * every iteration.\n\t */\n\tMemoryContext waitContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"JobsWaitContext\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MINSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_INITSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MAXSIZE);\n\tMemoryContext oldContext = MemoryContextSwitchTo(waitContext);\n\n\twhile (true)\n\t{\n\t\tMemoryContextReset(waitContext);\n\n\t\tBackgroundJob *job = GetBackgroundJobByJobId(jobid);\n\t\tif (!job)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"no job found for job with jobid: %ld\", jobid)));\n\t\t}\n\n\t\tif (desiredStatus && job->state == *desiredStatus)\n\t\t{\n\t\t\t/* job has reached its desired status, done waiting */\n\t\t\tbreak;\n\t\t}\n\n\t\tif (IsBackgroundJobStatusTerminal(job->state))\n\t\t{\n\t\t\tif (desiredStatus)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We have reached a terminal state, which is not the desired state we\n\t\t\t\t * were waiting for, otherwise we would have escaped earlier. Since it is\n\t\t\t\t * a terminal state we know that we can never reach the desired state.\n\t\t\t\t */\n\n\t\t\t\tOid reachedStatusOid = BackgroundJobStatusOid(job->state);\n\t\t\t\tDatum reachedStatusNameDatum = DirectFunctionCall1(enum_out,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   reachedStatusOid);\n\t\t\t\tchar *reachedStatusName = DatumGetCString(reachedStatusNameDatum);\n\n\t\t\t\tOid desiredStatusOid = BackgroundJobStatusOid(*desiredStatus);\n\t\t\t\tDatum desiredStatusNameDatum = DirectFunctionCall1(enum_out,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   desiredStatusOid);\n\t\t\t\tchar *desiredStatusName = DatumGetCString(desiredStatusNameDatum);\n\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errmsg(\"Job reached terminal state \\\"%s\\\" instead of desired \"\n\t\t\t\t\t\t\t\t\"state \\\"%s\\\"\", reachedStatusName, desiredStatusName)));\n\t\t\t}\n\n\t\t\t/* job has reached its terminal state, done waiting */\n\t\t\tbreak;\n\t\t}\n\n\t\t/* sleep for a while, before rechecking the job status */\n\t\tCHECK_FOR_INTERRUPTS();\n\t\tconst long delay_ms = 1000;\n\t\t(void) WaitLatch(MyLatch,\n\t\t\t\t\t\t WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,\n\t\t\t\t\t\t delay_ms,\n\t\t\t\t\t\t WAIT_EVENT_PG_SLEEP);\n\n\t\tResetLatch(MyLatch);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\tMemoryContextDelete(waitContext);\n}\n\n\n/*\n * citus_task_wait_internal implements the waiting on a task for reuse in other areas where\n * we want to wait on tasks.\n *\n * When a desiredStatus is provided it will provide an error when a different state is\n * reached and the state cannot ever reach the desired state anymore.\n */\nvoid\ncitus_task_wait_internal(int64 taskid, BackgroundTaskStatus *desiredStatus)\n{\n\t/*\n\t * Since we are wait polling we will actually allocate memory on every poll. To make\n\t * sure we don't put unneeded pressure on the memory we create a context that we clear\n\t * every iteration.\n\t */\n\tMemoryContext waitContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"TasksWaitContext\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MINSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_INITSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ALLOCSET_DEFAULT_MAXSIZE);\n\tMemoryContext oldContext = MemoryContextSwitchTo(waitContext);\n\n\twhile (true)\n\t{\n\t\tMemoryContextReset(waitContext);\n\n\t\tBackgroundTask *task = GetBackgroundTaskByTaskId(taskid);\n\t\tif (!task)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"no task found with taskid: %ld\", taskid)));\n\t\t}\n\n\t\tif (desiredStatus && task->status == *desiredStatus)\n\t\t{\n\t\t\t/* task has reached its desired status, done waiting */\n\t\t\tbreak;\n\t\t}\n\n\t\tif (IsBackgroundTaskStatusTerminal(task->status))\n\t\t{\n\t\t\tif (desiredStatus)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * We have reached a terminal state, which is not the desired state we\n\t\t\t\t * were waiting for, otherwise we would have escaped earlier. Since it is\n\t\t\t\t * a terminal state we know that we can never reach the desired state.\n\t\t\t\t */\n\n\t\t\t\tOid reachedStatusOid = BackgroundTaskStatusOid(task->status);\n\t\t\t\tDatum reachedStatusNameDatum = DirectFunctionCall1(enum_out,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   reachedStatusOid);\n\t\t\t\tchar *reachedStatusName = DatumGetCString(reachedStatusNameDatum);\n\n\t\t\t\tOid desiredStatusOid = BackgroundTaskStatusOid(*desiredStatus);\n\t\t\t\tDatum desiredStatusNameDatum = DirectFunctionCall1(enum_out,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   desiredStatusOid);\n\t\t\t\tchar *desiredStatusName = DatumGetCString(desiredStatusNameDatum);\n\n\t\t\t\tereport(ERROR,\n\t\t\t\t\t\t(errmsg(\"Task reached terminal state \\\"%s\\\" instead of desired \"\n\t\t\t\t\t\t\t\t\"state \\\"%s\\\"\", reachedStatusName, desiredStatusName)));\n\t\t\t}\n\n\t\t\t/* task has reached its terminal state, done waiting */\n\t\t\tbreak;\n\t\t}\n\n\t\t/* sleep for a while, before rechecking the task status */\n\t\tCHECK_FOR_INTERRUPTS();\n\t\tconst long delay_ms = 100;\n\t\t(void) WaitLatch(MyLatch,\n\t\t\t\t\t\t WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,\n\t\t\t\t\t\t delay_ms,\n\t\t\t\t\t\t WAIT_EVENT_PG_SLEEP);\n\n\t\tResetLatch(MyLatch);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\tMemoryContextDelete(waitContext);\n}\n\n\n/*\n * StartCitusBackgroundTaskQueueMonitor spawns a new background worker connected to the\n * current database and owner. This background worker consumes the tasks that are ready\n * for execution.\n */\nBackgroundWorkerHandle *\nStartCitusBackgroundTaskQueueMonitor(Oid database, Oid extensionOwner)\n{\n\tchar workerName[BGW_MAXLEN];\n\n\tSafeSnprintf(workerName, BGW_MAXLEN,\n\t\t\t\t \"Citus Background Task Queue Monitor: %u/%u\",\n\t\t\t\t database, extensionOwner);\n\n\tCitusBackgroundWorkerConfig config = {\n\t\t.workerName = workerName,\n\t\t.functionName = \"CitusBackgroundTaskQueueMonitorMain\",\n\t\t.mainArg = ObjectIdGetDatum(MyDatabaseId),\n\t\t.extensionOwner = extensionOwner,\n\t\t.needsNotification = true,\n\t\t.waitForStartup = true,\n\t\t.restartTime = CITUS_BGW_NEVER_RESTART,\n\t\t.startTime = CITUS_BGW_DEFAULT_START_TIME,\n\t\t.workerType = NULL, /* use default */\n\t\t.extraData = NULL,\n\t\t.extraDataSize = 0\n\t};\n\treturn RegisterCitusBackgroundWorker(&config);\n}\n\n\n/*\n * context for any log/error messages emitted from the background task queue monitor.\n */\ntypedef struct CitusBackgroundTaskQueueMonitorErrorCallbackContext\n{\n\tconst char *database;\n} CitusBackgroundTaskQueueMonitorCallbackContext;\n\n\n/*\n * CitusBackgroundTaskQueueMonitorErrorCallback is a callback handler that gets called for\n * any ereport to add extra context to the message.\n */\nstatic void\nCitusBackgroundTaskQueueMonitorErrorCallback(void *arg)\n{\n\tCitusBackgroundTaskQueueMonitorCallbackContext *context =\n\t\t(CitusBackgroundTaskQueueMonitorCallbackContext *) arg;\n\terrcontext(\"Citus Background Task Queue Monitor: %s\", context->database);\n}\n\n\n/*\n * NewExecutorExceedsCitusLimit returns true if currently we reached Citus' max worker count.\n */\nstatic bool\nNewExecutorExceedsCitusLimit(QueueMonitorExecutionContext *queueMonitorExecutionContext)\n{\n\tif (queueMonitorExecutionContext->currentExecutorCount >= MaxBackgroundTaskExecutors)\n\t{\n\t\t/*\n\t\t * we hit to Citus' maximum task executor count. Warn for the first failure\n\t\t * after a successful worker allocation happened, that is, we do not warn if\n\t\t * we repeatedly come here without a successful worker allocation.\n\t\t */\n\t\tif (queueMonitorExecutionContext->backgroundWorkerFailedStartTime == 0)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"unable to start background worker for \"\n\t\t\t\t\t\t\t\t\t \"background task execution\"),\n\t\t\t\t\t\t\t  errdetail(\n\t\t\t\t\t\t\t\t  \"Already reached the maximum number of task \"\n\t\t\t\t\t\t\t\t  \"executors: %ld/%d\",\n\t\t\t\t\t\t\t\t  queueMonitorExecutionContext->currentExecutorCount,\n\t\t\t\t\t\t\t\t  MaxBackgroundTaskExecutors)));\n\t\t\tqueueMonitorExecutionContext->backgroundWorkerFailedStartTime =\n\t\t\t\tGetCurrentTimestamp();\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * NewExecutorExceedsPgMaxWorkers returns true if currently we reached Postgres' max worker count.\n */\nstatic bool\nNewExecutorExceedsPgMaxWorkers(BackgroundWorkerHandle *handle,\n\t\t\t\t\t\t\t   QueueMonitorExecutionContext *queueMonitorExecutionContext)\n{\n\tif (handle == NULL)\n\t{\n\t\t/*\n\t\t * we are unable to start a background worker for the task execution.\n\t\t * Probably we are out of background workers. Warn for the first failure\n\t\t * after a successful worker allocation happened, that is, we do not warn if\n\t\t * we repeatedly come here without a successful worker allocation.\n\t\t */\n\t\tif (queueMonitorExecutionContext->backgroundWorkerFailedStartTime == 0)\n\t\t{\n\t\t\tereport(WARNING, (errmsg(\"unable to start background worker for \"\n\t\t\t\t\t\t\t\t\t \"background task execution\"),\n\t\t\t\t\t\t\t  errdetail(\n\t\t\t\t\t\t\t\t  \"Current number of task \"\n\t\t\t\t\t\t\t\t  \"executors: %ld/%d\",\n\t\t\t\t\t\t\t\t  queueMonitorExecutionContext->currentExecutorCount,\n\t\t\t\t\t\t\t\t  MaxBackgroundTaskExecutors)));\n\t\t\tqueueMonitorExecutionContext->backgroundWorkerFailedStartTime =\n\t\t\t\tGetCurrentTimestamp();\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * AssignRunnableTaskToNewExecutor tries to assign given runnable task to a new task executor.\n * It reports the assignment status as return value.\n */\nstatic bool\nAssignRunnableTaskToNewExecutor(BackgroundTask *runnableTask,\n\t\t\t\t\t\t\t\tQueueMonitorExecutionContext *\n\t\t\t\t\t\t\t\tqueueMonitorExecutionContext)\n{\n\tAssert(runnableTask && runnableTask->status == BACKGROUND_TASK_STATUS_RUNNABLE);\n\n\tif (NewExecutorExceedsCitusLimit(queueMonitorExecutionContext))\n\t{\n\t\t/* escape if we hit citus executor limit */\n\t\treturn false;\n\t}\n\n\tchar *databaseName = get_database_name(MyDatabaseId);\n\tchar *userName = GetUserNameFromId(runnableTask->owner, false);\n\n\t/* try to create new executor and make it alive during queue monitor lifetime */\n\tMemoryContext oldContext = MemoryContextSwitchTo(queueMonitorExecutionContext->ctx);\n\tdsm_segment *seg = NULL;\n\tBackgroundWorkerHandle *handle =\n\t\tStartCitusBackgroundTaskExecutor(databaseName, userName, runnableTask->command,\n\t\t\t\t\t\t\t\t\t\t runnableTask->taskid, runnableTask->jobid, &seg);\n\tMemoryContextSwitchTo(oldContext);\n\n\tif (NewExecutorExceedsPgMaxWorkers(handle, queueMonitorExecutionContext))\n\t{\n\t\t/* escape if we hit pg worker limit */\n\t\treturn false;\n\t}\n\n\t/* assign the allocated executor to the runnable task and increment total executor count */\n\tbool handleEntryFound = false;\n\tBackgroundExecutorHashEntry *handleEntry = hash_search(\n\t\tqueueMonitorExecutionContext->currentExecutors,\n\t\t&runnableTask->taskid,\n\t\tHASH_ENTER, &handleEntryFound);\n\tAssert(!handleEntryFound);\n\thandleEntry->handle = handle;\n\thandleEntry->seg = seg;\n\thandleEntry->jobid = runnableTask->jobid;\n\n\t/* reset worker allocation timestamp and log time elapsed since the last failure */\n\tCheckAndResetLastWorkerAllocationFailure(queueMonitorExecutionContext);\n\n\t/* make message alive during queue monitor lifetime */\n\toldContext = MemoryContextSwitchTo(queueMonitorExecutionContext->ctx);\n\thandleEntry->message = makeStringInfo();\n\tMemoryContextSwitchTo(oldContext);\n\n\t/* set runnable task's status as running */\n\trunnableTask->status = BACKGROUND_TASK_STATUS_RUNNING;\n\tUpdateBackgroundTask(runnableTask);\n\tUpdateBackgroundJob(runnableTask->jobid);\n\n\tqueueMonitorExecutionContext->currentExecutorCount++;\n\n\tereport(LOG, (errmsg(\"task jobid/taskid started: %ld/%ld\",\n\t\t\t\t\t\t runnableTask->jobid, runnableTask->taskid)));\n\n\treturn true;\n}\n\n\n/*\n * AssignRunnableTasks tries to assign all runnable tasks to a new task executor.\n * If an assignment fails, it stops in case we hit some limitation. We do not load\n * all the runnable tasks in memory at once as it can load memory much + we have\n * limited worker to which we can assign task.\n */\nstatic void\nAssignRunnableTasks(QueueMonitorExecutionContext *queueMonitorExecutionContext)\n{\n\tBackgroundTask *runnableTask = NULL;\n\tbool taskAssigned = false;\n\tdo {\n\t\t/* fetch a runnable task from catalog */\n\t\trunnableTask = GetRunnableBackgroundTask();\n\t\tif (runnableTask)\n\t\t{\n\t\t\ttaskAssigned = AssignRunnableTaskToNewExecutor(runnableTask,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   queueMonitorExecutionContext);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttaskAssigned = false;\n\t\t}\n\t} while (taskAssigned);\n}\n\n\n/*\n * GetRunningTaskEntries returns list of BackgroundExecutorHashEntry from given hash table\n */\nstatic List *\nGetRunningTaskEntries(HTAB *currentExecutors)\n{\n\tList *runningTaskEntries = NIL;\n\n\tHASH_SEQ_STATUS status;\n\tBackgroundExecutorHashEntry *backgroundExecutorHashEntry;\n\tforeach_htab(backgroundExecutorHashEntry, &status, currentExecutors)\n\t{\n\t\trunningTaskEntries = lappend(runningTaskEntries, backgroundExecutorHashEntry);\n\t}\n\n\treturn runningTaskEntries;\n}\n\n\n/*\n * CheckAndResetLastWorkerAllocationFailure checks the last time background worker allocation\n * is failed. If it is set, we print how long we have waited to successfully allocate the worker.\n * It also resets the failure timestamp.\n */\nstatic void\nCheckAndResetLastWorkerAllocationFailure(QueueMonitorExecutionContext *\n\t\t\t\t\t\t\t\t\t\t queueMonitorExecutionContext)\n{\n\tif (queueMonitorExecutionContext->backgroundWorkerFailedStartTime > 0)\n\t{\n\t\t/*\n\t\t * we had a delay in starting the background worker for task execution. Report\n\t\t * the actual delay and reset the time. This allows a subsequent task to\n\t\t * report again if it can't start a background worker directly.\n\t\t */\n\t\tlong secs = 0;\n\t\tint microsecs = 0;\n\t\tTimestampDifference(\n\t\t\tqueueMonitorExecutionContext->\n\t\t\tbackgroundWorkerFailedStartTime,\n\t\t\tGetCurrentTimestamp(),\n\t\t\t&secs, &microsecs);\n\t\tereport(LOG, (errmsg(\n\t\t\t\t\t\t  \"able to start a background worker with %ld seconds \"\n\t\t\t\t\t\t  \"delay\", secs)));\n\n\t\tqueueMonitorExecutionContext->backgroundWorkerFailedStartTime = 0;\n\t}\n}\n\n\n/*\n * TaskConcurrentCancelCheck checks if concurrent task cancellation or removal happened by\n * taking Exclusive lock. It mutates task's pid and status. Returns execution status for the\n * task.\n */\nstatic TaskExecutionStatus\nTaskConcurrentCancelCheck(TaskExecutionContext *taskExecutionContext)\n{\n\t/*\n\t * here we take exclusive lock on pg_dist_background_task table to prevent a\n\t * concurrent modification. A separate process could have cancelled or removed\n\t * the task by now, they would not see the pid and status update, so it is our\n\t * responsibility to stop the backend and update the pid and status.\n\t *\n\t * The lock will release on transaction commit.\n\t */\n\tLockRelationOid(DistBackgroundTaskRelationId(), ExclusiveLock);\n\n\tBackgroundExecutorHashEntry *handleEntry = taskExecutionContext->handleEntry;\n\tBackgroundTask *task = GetBackgroundTaskByTaskId(handleEntry->taskid);\n\ttaskExecutionContext->task = task;\n\tif (!task)\n\t{\n\t\tereport(ERROR, (errmsg(\"unexpected missing task id: %ld\", handleEntry->taskid)));\n\t}\n\n\tif (task->status == BACKGROUND_TASK_STATUS_CANCELLING)\n\t{\n\t\t/*\n\t\t * being in that step means that a concurrent cancel or removal happened. we should\n\t\t * mark task status as cancelled. We also want to reflect cancel message by consuming\n\t\t * task executor queue.\n\t\t */\n\t\tbool hadError = false;\n\t\tReadFromExecutorQueue(handleEntry, &hadError);\n\n\t\tereport(LOG, (errmsg(\n\t\t\t\t\t\t  \"task jobid/taskid is cancelled: %ld/%ld\",\n\t\t\t\t\t\t  task->jobid, task->taskid)));\n\n\t\ttask->status = BACKGROUND_TASK_STATUS_CANCELLED;\n\n\t\treturn TASK_EXECUTION_STATUS_CANCELLED;\n\t}\n\telse\n\t{\n\t\t/*\n\t\t * now that we have verified the task has not been cancelled and still exist we\n\t\t * update it to reflect the new state. If task is already in running status,\n\t\t * the operation is idempotent. But for runnable tasks, we make their status\n\t\t * as running.\n\t\t */\n\n\t\tpid_t pid = 0;\n\t\tGetBackgroundWorkerPid(handleEntry->handle, &pid);\n\t\ttask->status = BACKGROUND_TASK_STATUS_RUNNING;\n\t\tSET_NULLABLE_FIELD(task, pid, pid);\n\n\t\t/* Update task status to indicate it is running */\n\t\tUpdateBackgroundTask(task);\n\t\tUpdateBackgroundJob(task->jobid);\n\n\t\treturn TASK_EXECUTION_STATUS_RUNNING;\n\t}\n}\n\n\n/*\n * ConsumeExecutorQueue consumes executor's shared memory queue and returns execution status\n * for the task.\n */\nstatic TaskExecutionStatus\nConsumeExecutorQueue(TaskExecutionContext *taskExecutionContext)\n{\n\tBackgroundExecutorHashEntry *handleEntry = taskExecutionContext->handleEntry;\n\tBackgroundTask *task = taskExecutionContext->task;\n\n\t/*\n\t * we consume task executor response queue.\n\t * possible response codes can lead us different steps below.\n\t */\n\tbool hadError = false;\n\tshm_mq_result mq_res = ReadFromExecutorQueue(handleEntry, &hadError);\n\n\tif (hadError)\n\t{\n\t\tereport(LOG, (errmsg(\"task jobid/taskid failed: %ld/%ld\",\n\t\t\t\t\t\t\t task->jobid, task->taskid)));\n\n\t\treturn TASK_EXECUTION_STATUS_ERROR;\n\t}\n\telse if (mq_res == SHM_MQ_DETACHED)\n\t{\n\t\tereport(LOG, (errmsg(\"task jobid/taskid succeeded: %ld/%ld\",\n\t\t\t\t\t\t\t task->jobid, task->taskid)));\n\n\t\t/* update task status as done. */\n\t\ttask->status = BACKGROUND_TASK_STATUS_DONE;\n\n\t\treturn TASK_EXECUTION_STATUS_SUCCESS;\n\t}\n\telse\n\t{\n\t\t/* still running the task */\n\t\tAssert(mq_res == SHM_MQ_WOULD_BLOCK);\n\t\treturn TASK_EXECUTION_STATUS_WOULDBLOCK;\n\t}\n}\n\n\n/*\n * TaskHadError updates retry count of a failed task inside taskExecutionContext.\n * If maximum retry count is reached, task status is marked as failed. Otherwise, backoff\n * delay is calculated, notBefore time is updated and the task is marked as runnable.\n */\nstatic void\nTaskHadError(TaskExecutionContext *taskExecutionContext)\n{\n\tBackgroundTask *task = taskExecutionContext->task;\n\n\t/*\n\t * when we had an error in response queue, we need to decide if we want to retry (keep the\n\t * runnable state), or move to error state\n\t */\n\n\tif (!task->retry_count)\n\t{\n\t\tSET_NULLABLE_FIELD(task, retry_count, 1);\n\t}\n\telse\n\t{\n\t\t(*task->retry_count)++;\n\t}\n\n\t/*\n\t * based on the retry count we either transition the task to its error\n\t * state, or we calculate a new backoff time for future execution.\n\t */\n\tint64 delayMs = CalculateBackoffDelay(*(task->retry_count));\n\tif (delayMs < 0)\n\t{\n\t\ttask->status = BACKGROUND_TASK_STATUS_ERROR;\n\t\tUNSET_NULLABLE_FIELD(task, not_before);\n\t}\n\telse\n\t{\n\t\tTimestampTz notBefore = TimestampTzPlusMilliseconds(\n\t\t\tGetCurrentTimestamp(), delayMs);\n\t\tSET_NULLABLE_FIELD(task, not_before, notBefore);\n\n\t\ttask->status = BACKGROUND_TASK_STATUS_RUNNABLE;\n\t}\n\n\tTaskEnded(taskExecutionContext);\n}\n\n\n/*\n * TaskEnded updates task inside taskExecutionContext. It also updates depending\n * tasks and the job to which task belongs. At the end, it also updates executor map and\n * count inside queueMonitorExecutionContext after terminating the executor.\n */\nstatic void\nTaskEnded(TaskExecutionContext *taskExecutionContext)\n{\n\tQueueMonitorExecutionContext *queueMonitorExecutionContext =\n\t\ttaskExecutionContext->queueMonitorExecutionContext;\n\n\tHTAB *currentExecutors = queueMonitorExecutionContext->currentExecutors;\n\tBackgroundExecutorHashEntry *handleEntry = taskExecutionContext->handleEntry;\n\tBackgroundTask *task = taskExecutionContext->task;\n\n\t/*\n\t * we update task and job fields. We also update depending jobs.\n\t * At the end, do cleanup.\n\t */\n\tUNSET_NULLABLE_FIELD(task, pid);\n\ttask->message = handleEntry->message->data;\n\n\tUpdateBackgroundTask(task);\n\tUpdateDependingTasks(task);\n\tUpdateBackgroundJob(task->jobid);\n\tDecrementParallelTaskCountForNodesInvolved(task);\n\n\t/* we are sure that at least one task did not block on current iteration */\n\tqueueMonitorExecutionContext->allTasksWouldBlock = false;\n\n\thash_search(currentExecutors, &task->taskid,\n\t\t\t\tHASH_REMOVE, NULL);\n\tWaitForBackgroundWorkerShutdown(handleEntry->handle);\n\tqueueMonitorExecutionContext->currentExecutorCount--;\n}\n\n\n/*\n * IncrementParallelTaskCountForNodesInvolved\n * Checks whether we have reached the limit of parallel tasks per node\n * per each of the nodes involved with the task\n * If at least one limit is reached, it returns false.\n * If limits aren't reached, it increments the parallel task count\n * for each of the nodes involved with the task, and returns true.\n */\nbool\nIncrementParallelTaskCountForNodesInvolved(BackgroundTask *task)\n{\n\tif (task->nodesInvolved)\n\t{\n\t\tint node;\n\n\t\t/* first check whether we have reached the limit for any of the nodes */\n\t\tforeach_declared_int(node, task->nodesInvolved)\n\t\t{\n\t\t\tbool found;\n\t\t\tParallelTasksPerNodeEntry *hashEntry = hash_search(\n\t\t\t\tParallelTasksPerNode, &(node), HASH_ENTER, &found);\n\t\t\tif (!found)\n\t\t\t{\n\t\t\t\thashEntry->counter = 0;\n\t\t\t}\n\t\t\telse if (hashEntry->counter >= MaxBackgroundTaskExecutorsPerNode)\n\t\t\t{\n\t\t\t\t/* at least one node's limit is reached */\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t/* then, increment the parallel task count per each node */\n\t\tforeach_declared_int(node, task->nodesInvolved)\n\t\t{\n\t\t\tParallelTasksPerNodeEntry *hashEntry = hash_search(\n\t\t\t\tParallelTasksPerNode, &(node), HASH_FIND, NULL);\n\t\t\tAssert(hashEntry);\n\t\t\thashEntry->counter += 1;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * DecrementParallelTaskCountForNodesInvolved\n * Decrements the parallel task count for each of the nodes involved\n * with the task.\n * We call this function after the task has gone through Running state\n * and then has ended.\n */\nstatic void\nDecrementParallelTaskCountForNodesInvolved(BackgroundTask *task)\n{\n\tif (task->nodesInvolved)\n\t{\n\t\tint node;\n\t\tforeach_declared_int(node, task->nodesInvolved)\n\t\t{\n\t\t\tParallelTasksPerNodeEntry *hashEntry = hash_search(ParallelTasksPerNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &(node),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   HASH_FIND, NULL);\n\n\t\t\thashEntry->counter -= 1;\n\t\t}\n\t}\n}\n\n\n/*\n * QueueMonitorSigHupHandler handles SIGHUP to update monitor related config params.\n */\nstatic void\nQueueMonitorSigHupHandler(SIGNAL_ARGS)\n{\n\tint saved_errno = errno;\n\n\tGotSighup = true;\n\n\tif (MyProc)\n\t{\n\t\tSetLatch(&MyProc->procLatch);\n\t}\n\n\terrno = saved_errno;\n}\n\n\n/*\n * MonitorGotTerminationOrCancellationRequest returns true if monitor had SIGTERM or SIGINT signals\n */\nstatic bool\nMonitorGotTerminationOrCancellationRequest()\n{\n\treturn GotSigterm || GotSigint;\n}\n\n\n/*\n * QueueMonitorSigTermHandler handles SIGTERM by setting a flag to inform the monitor process\n * so that it can terminate active task executors properly. It also sets the latch to awake the\n * monitor if it waits on it.\n */\nstatic void\nQueueMonitorSigTermHandler(SIGNAL_ARGS)\n{\n\tint saved_errno = errno;\n\n\tGotSigterm = true;\n\n\tif (MyProc)\n\t{\n\t\tSetLatch(&MyProc->procLatch);\n\t}\n\n\terrno = saved_errno;\n}\n\n\n/*\n * QueueMonitorSigIntHandler handles SIGINT by setting a flag to inform the monitor process\n * so that it can terminate active task executors properly. It also sets the latch to awake the\n * monitor if it waits on it.\n */\nstatic void\nQueueMonitorSigIntHandler(SIGNAL_ARGS)\n{\n\tint saved_errno = errno;\n\n\tGotSigint = true;\n\n\tif (MyProc)\n\t{\n\t\tSetLatch(&MyProc->procLatch);\n\t}\n\n\terrno = saved_errno;\n}\n\n\n/*\n * TerminateAllTaskExecutors terminates task executors given in the hash map.\n */\nstatic void\nTerminateAllTaskExecutors(HTAB *currentExecutors)\n{\n\tHASH_SEQ_STATUS status;\n\tBackgroundExecutorHashEntry *backgroundExecutorHashEntry;\n\tforeach_htab(backgroundExecutorHashEntry, &status, currentExecutors)\n\t{\n\t\tTerminateBackgroundWorker(backgroundExecutorHashEntry->handle);\n\t}\n}\n\n\n/*\n * GetRunningUniqueJobIds returns unique job ids from currentExecutors\n */\nstatic HTAB *\nGetRunningUniqueJobIds(HTAB *currentExecutors)\n{\n\t/* create a set to store unique job ids for currently executing tasks */\n\tHTAB *uniqueJobIds = CreateSimpleHashSetWithSize(int64, MAX_BG_TASK_EXECUTORS);\n\n\tHASH_SEQ_STATUS status;\n\tBackgroundExecutorHashEntry *backgroundExecutorHashEntry;\n\tforeach_htab(backgroundExecutorHashEntry, &status, currentExecutors)\n\t{\n\t\thash_search(uniqueJobIds, &backgroundExecutorHashEntry->jobid, HASH_ENTER, NULL);\n\t}\n\n\treturn uniqueJobIds;\n}\n\n\n/*\n * CancelAllTaskExecutors cancels task executors given in the hash map.\n */\nstatic void\nCancelAllTaskExecutors(HTAB *currentExecutors)\n{\n\tStartTransactionCommand();\n\tPushActiveSnapshot(GetTransactionSnapshot());\n\n\t/* get unique job id set for running tasks in currentExecutors */\n\tHTAB *uniqueJobIds = GetRunningUniqueJobIds(currentExecutors);\n\n\tHASH_SEQ_STATUS status;\n\tint64 *uniqueJobId;\n\tforeach_htab(uniqueJobId, &status, uniqueJobIds)\n\t{\n\t\tereport(DEBUG1, (errmsg(\"cancelling job: %ld\", *uniqueJobId)));\n\t\tDatum jobidDatum = Int64GetDatum(*uniqueJobId);\n\t\tDirectFunctionCall1(citus_job_cancel, jobidDatum);\n\t}\n\n\tPopActiveSnapshot();\n\tCommitTransactionCommand();\n}\n\n\n/*\n * CitusBackgroundTaskQueueMonitorMain is the main entry point for the background worker\n * running the background tasks queue monitor.\n *\n * It's mainloop reads a runnable task from pg_dist_background_task and progressing the\n * tasks and jobs state machines associated with the task. When no new task can be found\n * it will exit(0) and lets the maintenance daemon poll for new tasks.\n *\n * The main loop is implemented as asynchronous loop stepping through the task\n * and update its state before going to the next. Loop assigns runnable tasks to new task\n * executors as much as possible. If the max task executor limit is hit, the tasks will be\n * waiting in runnable status until currently running tasks finish. Each parallel worker\n * executes one task at a time without blocking each other by using nonblocking api.\n */\nvoid\nCitusBackgroundTaskQueueMonitorMain(Datum arg)\n{\n\t/* handle SIGTERM to properly terminate active task executors */\n\tpqsignal(SIGTERM, QueueMonitorSigTermHandler);\n\n\t/* handle SIGINT to properly cancel active task executors */\n\tpqsignal(SIGINT, QueueMonitorSigIntHandler);\n\n\t/* handle SIGHUP to update MaxBackgroundTaskExecutors and MaxBackgroundTaskExecutorsPerNode */\n\tpqsignal(SIGHUP, QueueMonitorSigHupHandler);\n\n\t/* ready to handle signals */\n\tBackgroundWorkerUnblockSignals();\n\n\tOid databaseOid = DatumGetObjectId(arg);\n\n\t/* extension owner is passed via bgw_extra */\n\tOid extensionOwner = InvalidOid;\n\tmemcpy_s(&extensionOwner, sizeof(extensionOwner),\n\t\t\t MyBgworkerEntry->bgw_extra, sizeof(Oid));\n\n\t/* connect to database, after that we can actually access catalogs */\n\tBackgroundWorkerInitializeConnectionByOid(databaseOid, extensionOwner, 0);\n\n\t/*\n\t * save old context until monitor loop exits, we use backgroundTaskContext for\n\t * all allocations.\n\t */\n\tMemoryContext firstContext = CurrentMemoryContext;\n\tMemoryContext backgroundTaskContext = AllocSetContextCreate(TopMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"BackgroundTaskContext\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tALLOCSET_DEFAULT_SIZES);\n\n\tStartTransactionCommand();\n\tPushActiveSnapshot(GetTransactionSnapshot());\n\n\tconst char *databasename = get_database_name(MyDatabaseId);\n\n\t/* make databasename alive during queue monitor lifetime */\n\tMemoryContext oldContext = MemoryContextSwitchTo(backgroundTaskContext);\n\tdatabasename = pstrdup(databasename);\n\tMemoryContextSwitchTo(oldContext);\n\n\t/* setup error context to indicate the errors came from a running background task */\n\tErrorContextCallback errorCallback = { 0 };\n\tstruct CitusBackgroundTaskQueueMonitorErrorCallbackContext context = {\n\t\t.database = databasename,\n\t};\n\terrorCallback.callback = CitusBackgroundTaskQueueMonitorErrorCallback;\n\terrorCallback.arg = (void *) &context;\n\terrorCallback.previous = error_context_stack;\n\terror_context_stack = &errorCallback;\n\n\tPopActiveSnapshot();\n\tCommitTransactionCommand();\n\n\t/*\n\t * There should be exactly one background task monitor running, running multiple would\n\t * cause conflicts on processing the tasks in the catalog table as well as violate\n\t * parallelism guarantees. To make sure there is at most, exactly one backend running\n\t * we take a session lock on the CITUS_BACKGROUND_TASK_MONITOR operation.\n\t */\n\tLOCKTAG tag = { 0 };\n\tSET_LOCKTAG_CITUS_OPERATION(tag, CITUS_BACKGROUND_TASK_MONITOR);\n\tconst bool sessionLock = true;\n\tconst bool dontWait = true;\n\tLockAcquireResult locked =\n\t\tLockAcquire(&tag, AccessExclusiveLock, sessionLock, dontWait);\n\tif (locked == LOCKACQUIRE_NOT_AVAIL)\n\t{\n\t\tereport(ERROR, (errmsg(\"background task queue monitor already running for \"\n\t\t\t\t\t\t\t   \"database\")));\n\t}\n\n\t/* make worker recognizable in pg_stat_activity */\n\tpgstat_report_appname(\"citus background task queue monitor\");\n\n\tereport(DEBUG1, (errmsg(\"started citus background task queue monitor\")));\n\n\t/*\n\t * First we find all jobs that are running, we need to check if they are still running\n\t * if not reset their state back to scheduled.\n\t */\n\tStartTransactionCommand();\n\tPushActiveSnapshot(GetTransactionSnapshot());\n\n\tResetRunningBackgroundTasks();\n\n\tPopActiveSnapshot();\n\tCommitTransactionCommand();\n\n\t/* create a map to store parallel task executors. Persist it in monitor memory context */\n\toldContext = MemoryContextSwitchTo(backgroundTaskContext);\n\tHTAB *currentExecutors = CreateSimpleHashWithNameAndSize(int64,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t BackgroundExecutorHashEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Background Executor Hash\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t MAX_BG_TASK_EXECUTORS);\n\tMemoryContextSwitchTo(oldContext);\n\n\t/*\n\t * monitor execution context that is useful during the monitor loop.\n\t * we store current executor count, last background failure timestamp,\n\t * currently executed task context and also a memory context to persist\n\t * some allocations throughout the loop.\n\t */\n\tQueueMonitorExecutionContext queueMonitorExecutionContext = {\n\t\t.currentExecutorCount = 0,\n\t\t.backgroundWorkerFailedStartTime = 0,\n\t\t.allTasksWouldBlock = true,\n\t\t.currentExecutors = currentExecutors,\n\t\t.ctx = backgroundTaskContext\n\t};\n\n\t/* flag to prevent duplicate termination and cancellation of task executors */\n\tbool terminateExecutorsStarted = false;\n\tbool cancelExecutorsStarted = false;\n\n\t/* loop exits if there is no running or runnable tasks left */\n\tbool hasAnyTask = true;\n\twhile (hasAnyTask)\n\t{\n\t\t/* handle signals */\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\t/* invalidate cache for new data in catalog */\n\t\tInvalidateMetadataSystemCache();\n\n\t\t/*\n\t\t * if the flag is set, we should terminate all task executor workers to prevent duplicate\n\t\t * runs of the same task on the next start of the monitor, which is dangerous for non-idempotent\n\t\t * tasks. We do not break the loop here as we want to reflect tasks' messages. Hence, we wait until\n\t\t * all tasks finish and also do not allow new runnable tasks to start running. After all current tasks\n\t\t * finish, we can exit the loop safely.\n\t\t */\n\t\tif (GotSigterm && !terminateExecutorsStarted)\n\t\t{\n\t\t\tereport(LOG, (errmsg(\"handling termination signal\")));\n\t\t\tterminateExecutorsStarted = true;\n\t\t\tTerminateAllTaskExecutors(queueMonitorExecutionContext.currentExecutors);\n\t\t}\n\n\t\tif (GotSigint && !cancelExecutorsStarted)\n\t\t{\n\t\t\tereport(LOG, (errmsg(\"handling cancellation signal\")));\n\t\t\tcancelExecutorsStarted = true;\n\t\t\tCancelAllTaskExecutors(queueMonitorExecutionContext.currentExecutors);\n\t\t}\n\n\t\tif (GotSighup)\n\t\t{\n\t\t\tGotSighup = false;\n\n\t\t\t/* update max_background_task_executors and max_background_task_executors_per_node if changed */\n\t\t\tProcessConfigFile(PGC_SIGHUP);\n\t\t}\n\n\t\tif (ParallelTasksPerNode == NULL)\n\t\t{\n\t\t\tParallelTasksPerNode = CreateSimpleHash(int32, ParallelTasksPerNodeEntry);\n\t\t}\n\n\t\t/* assign runnable tasks, if any, to new task executors in a transaction if we do not have SIGTERM or SIGINT */\n\t\tif (!MonitorGotTerminationOrCancellationRequest())\n\t\t{\n\t\t\tStartTransactionCommand();\n\t\t\tPushActiveSnapshot(GetTransactionSnapshot());\n\t\t\tAssignRunnableTasks(&queueMonitorExecutionContext);\n\t\t\tPopActiveSnapshot();\n\t\t\tCommitTransactionCommand();\n\t\t}\n\n\t\t/* get running task entries from hash table */\n\t\tList *runningTaskEntries = GetRunningTaskEntries(\n\t\t\tqueueMonitorExecutionContext.currentExecutors);\n\t\thasAnyTask = list_length(runningTaskEntries) > 0;\n\n\t\t/* useful to sleep if all tasks ewouldblock on current iteration */\n\t\tqueueMonitorExecutionContext.allTasksWouldBlock = true;\n\n\t\t/* monitor executors inside transaction */\n\t\tStartTransactionCommand();\n\t\tPushActiveSnapshot(GetTransactionSnapshot());\n\n\t\t/* iterate over all handle entries and monitor each task's output */\n\t\tBackgroundExecutorHashEntry *handleEntry = NULL;\n\t\tforeach_declared_ptr(handleEntry, runningTaskEntries)\n\t\t{\n\t\t\t/* create task execution context and assign it to queueMonitorExecutionContext */\n\t\t\tTaskExecutionContext taskExecutionContext = {\n\t\t\t\t.queueMonitorExecutionContext = &queueMonitorExecutionContext,\n\t\t\t\t.handleEntry = handleEntry,\n\t\t\t\t.task = NULL\n\t\t\t};\n\n\t\t\t/* check if concurrent cancellation occurred */\n\t\t\tTaskExecutionStatus taskExecutionStatus = TaskConcurrentCancelCheck(\n\t\t\t\t&taskExecutionContext);\n\n\t\t\t/*\n\t\t\t * check task status. If it is cancelled, we do not need to consume queue\n\t\t\t * as we already consumed the queue.\n\t\t\t */\n\t\t\tif (taskExecutionStatus == TASK_EXECUTION_STATUS_CANCELLED)\n\t\t\t{\n\t\t\t\tTaskEnded(&taskExecutionContext);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttaskExecutionStatus = ConsumeExecutorQueue(&taskExecutionContext);\n\t\t\tif (taskExecutionStatus == TASK_EXECUTION_STATUS_ERROR)\n\t\t\t{\n\t\t\t\tTaskHadError(&taskExecutionContext);\n\t\t\t}\n\t\t\telse if (taskExecutionStatus == TASK_EXECUTION_STATUS_SUCCESS)\n\t\t\t{\n\t\t\t\tTaskEnded(&taskExecutionContext);\n\t\t\t}\n\t\t}\n\n\t\tPopActiveSnapshot();\n\t\tCommitTransactionCommand();\n\n\t\tif (queueMonitorExecutionContext.allTasksWouldBlock)\n\t\t{\n\t\t\t/*\n\t\t\t * sleep to lower cpu consumption if all tasks responded with EWOULD_BLOCK on the last iteration.\n\t\t\t * That will also let those tasks to progress to generate some output probably.\n\t\t\t */\n\t\t\tconst long delay_ms = 1000;\n\t\t\t(void) WaitLatch(MyLatch, WL_LATCH_SET | WL_TIMEOUT |\n\t\t\t\t\t\t\t WL_EXIT_ON_PM_DEATH,\n\t\t\t\t\t\t\t delay_ms, WAIT_EVENT_PG_SLEEP);\n\t\t\tResetLatch(MyLatch);\n\t\t}\n\t}\n\n\tMemoryContextSwitchTo(firstContext);\n\tMemoryContextDelete(backgroundTaskContext);\n\n\tproc_exit(0);\n}\n\n\n/*\n * ReadFromExecutorQueue reads from task executor's response queue into the message.\n * It also sets hadError flag if an error response is encountered in the queue.\n */\nstatic shm_mq_result\nReadFromExecutorQueue(BackgroundExecutorHashEntry *backgroundExecutorHashEntry,\n\t\t\t\t\t  bool *hadError)\n{\n\tdsm_segment *seg = backgroundExecutorHashEntry->seg;\n\tshm_toc *toc = shm_toc_attach(CITUS_BACKGROUND_TASK_MAGIC,\n\t\t\t\t\t\t\t\t  dsm_segment_address(seg));\n\tshm_mq *mq = shm_toc_lookup(toc, CITUS_BACKGROUND_TASK_KEY_QUEUE, false);\n\tshm_mq_handle *responseq = shm_mq_attach(mq, seg, NULL);\n\n\t/*\n\t * Consume background executor's queue and get a response code.\n\t */\n\tStringInfo message = backgroundExecutorHashEntry->message;\n\tshm_mq_result mq_res = ConsumeTaskWorkerOutput(responseq, message, hadError);\n\treturn mq_res;\n}\n\n\n/*\n * CalculateBackoffDelay calculates the time to backoff between retries.\n *\n * Per try we increase the delay as follows:\n *   retry 1: 5 sec\n *   retry 2: 20 sec\n *   retry 3-32 (30 tries in total): 1 min\n *\n * returns -1 when retrying should stop.\n *\n * In the future we would like a callback on the job_type that could\n * distinguish the retry count and delay + potential jitter on a\n * job_type basis. For now we only assume this to be used by the\n * rebalancer and settled on the retry scheme above.\n */\nstatic int64\nCalculateBackoffDelay(int retryCount)\n{\n\tif (retryCount == 1)\n\t{\n\t\treturn 5 * 1000;\n\t}\n\telse if (retryCount == 2)\n\t{\n\t\treturn 20 * 1000;\n\t}\n\telse if (retryCount <= 32)\n\t{\n\t\treturn 60 * 1000;\n\t}\n\treturn -1;\n}\n\n\n/*\n * bgw_generate_returned_message -\n *      generates the message to be inserted into the job_run_details table\n *      first part is comming from error_severity (elog.c)\n */\nstatic void\nbgw_generate_returned_message(StringInfoData *display_msg, ErrorData edata)\n{\n\tconst char *prefix = error_severity(edata.elevel);\n\tappendStringInfo(display_msg, \"%s: %s\", prefix, edata.message);\n\tif (edata.detail != NULL)\n\t{\n\t\tappendStringInfo(display_msg, \"\\nDETAIL: %s\", edata.detail);\n\t}\n\n\tif (edata.hint != NULL)\n\t{\n\t\tappendStringInfo(display_msg, \"\\nHINT: %s\", edata.hint);\n\t}\n\n\tif (edata.context != NULL)\n\t{\n\t\tappendStringInfo(display_msg, \"\\nCONTEXT: %s\", edata.context);\n\t}\n}\n\n\n/*\n * UpdateDependingTasks updates all depending tasks, based on the type of terminal state\n * the current task reached.\n */\nstatic void\nUpdateDependingTasks(BackgroundTask *task)\n{\n\tswitch (task->status)\n\t{\n\t\tcase BACKGROUND_TASK_STATUS_DONE:\n\t\t{\n\t\t\tUnblockDependingBackgroundTasks(task);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase BACKGROUND_TASK_STATUS_ERROR:\n\t\t{\n\t\t\t/* when we error this task, we need to unschedule all dependant tasks */\n\t\t\tUnscheduleDependentTasks(task);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\t/* nothing to do for other states */\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\n/*\n * ConsumeTaskWorkerOutput consumes the output of an executor and sets the message as\n * the last message read from the queue. It also sets hadError as true if executor had\n * error.\n */\nstatic shm_mq_result\nConsumeTaskWorkerOutput(shm_mq_handle *responseq, StringInfo message, bool *hadError)\n{\n\tshm_mq_result res;\n\n\t/*\n\t * Message-parsing routines operate on a null-terminated StringInfo,\n\t * so we must construct one.\n\t */\n\tStringInfoData msg = { 0 };\n\tinitStringInfo(&msg);\n\n\tfor (;;)\n\t{\n\t\tresetStringInfo(&msg);\n\n\t\t/*\n\t\t * non-blocking receive to not block other bg workers\n\t\t */\n\t\tSize nbytes = 0;\n\t\tvoid *data = NULL;\n\t\tconst bool noWait = true;\n\t\tres = shm_mq_receive(responseq, &nbytes, &data, noWait);\n\n\t\tif (res != SHM_MQ_SUCCESS)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\n\t\tappendBinaryStringInfo(&msg, data, nbytes);\n\n\t\t/*\n\t\t * msgtype seems to be documented on\n\t\t * https://www.postgresql.org/docs/current/protocol-message-formats.html\n\t\t *\n\t\t * Here we mostly handle the same message types as supported in pg_cron as the\n\t\t * executor is highly influenced by the implementation there.\n\t\t */\n\t\tchar msgtype = pq_getmsgbyte(&msg);\n\t\tswitch (msgtype)\n\t\t{\n\t\t\tcase 'E': /* ErrorResponse */\n\t\t\t{\n\t\t\t\tif (hadError)\n\t\t\t\t{\n\t\t\t\t\t*hadError = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* FALLTHROUGH */\n\n\t\t\tcase 'N': /* NoticeResponse */\n\t\t\t{\n\t\t\t\tErrorData edata = { 0 };\n\t\t\t\tStringInfoData display_msg = { 0 };\n\n\t\t\t\tpq_parse_errornotice(&msg, &edata);\n\t\t\t\tinitStringInfo(&display_msg);\n\t\t\t\tbgw_generate_returned_message(&display_msg, edata);\n\n\t\t\t\t/* we keep only the last message */\n\t\t\t\tresetStringInfo(message);\n\t\t\t\tappendStringInfoString(message, display_msg.data);\n\t\t\t\tappendStringInfoChar(message, '\\n');\n\n\t\t\t\tpfree(display_msg.data);\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'C': /* CommandComplete */\n\t\t\t{\n\t\t\t\tconst char *tag = pq_getmsgstring(&msg);\n\n\t\t\t\tchar *nonconst_tag = pstrdup(tag);\n\n\t\t\t\t/* append the nonconst_tag to the task's message */\n\t\t\t\tappendStringInfoString(message, nonconst_tag);\n\t\t\t\tappendStringInfoChar(message, '\\n');\n\n\t\t\t\tpfree(nonconst_tag);\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'A':\n\t\t\tcase 'D':\n\t\t\tcase 'G':\n\t\t\tcase 'H':\n\t\t\tcase 'T':\n\t\t\tcase 'W':\n\t\t\tcase 'Z':\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t{\n\t\t\t\telog(WARNING, \"unknown message type: %c (%zu bytes)\",\n\t\t\t\t\t msg.data[0], nbytes);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tpfree(msg.data);\n\treturn res;\n}\n\n\n/*\n * StoreArgumentsInDSM creates a dynamic shared memory segment to pass the query and its\n * environment to the executor.\n */\nstatic dsm_segment *\nStoreArgumentsInDSM(char *database, char *username, char *command,\n\t\t\t\t\tint64 taskId, int64 jobId)\n{\n\t/*\n\t * Create the shared memory that we will pass to the background\n\t * worker process.  We use DSM_CREATE_NULL_IF_MAXSEGMENTS so that we\n\t * do not ERROR here.  This way, we can mark the job as failed and\n\t * keep the launcher process running normally.\n\t */\n\tshm_toc_estimator e = { 0 };\n\tshm_toc_initialize_estimator(&e);\n\tshm_toc_estimate_chunk(&e, strlen(database) + 1);\n\tshm_toc_estimate_chunk(&e, strlen(username) + 1);\n\tshm_toc_estimate_chunk(&e, strlen(command) + 1);\n#define QUEUE_SIZE ((Size) 65536)\n\tshm_toc_estimate_chunk(&e, QUEUE_SIZE);\n\tshm_toc_estimate_chunk(&e, sizeof(int64));\n\tshm_toc_estimate_chunk(&e, sizeof(int64));\n\tshm_toc_estimate_keys(&e, CITUS_BACKGROUND_TASK_NKEYS);\n\tSize segsize = shm_toc_estimate(&e);\n\n\tdsm_segment *seg = dsm_create(segsize, DSM_CREATE_NULL_IF_MAXSEGMENTS);\n\n\tif (seg == NULL)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"max number of DSM segments may has been reached\")));\n\n\t\treturn NULL;\n\t}\n\n\tshm_toc *toc = shm_toc_create(CITUS_BACKGROUND_TASK_MAGIC, dsm_segment_address(seg),\n\t\t\t\t\t\t\t\t  segsize);\n\n\tSize size = strlen(database) + 1;\n\tchar *databaseTarget = shm_toc_allocate(toc, size);\n\tstrcpy_s(databaseTarget, size, database);\n\tshm_toc_insert(toc, CITUS_BACKGROUND_TASK_KEY_DATABASE, databaseTarget);\n\n\tsize = strlen(username) + 1;\n\tchar *usernameTarget = shm_toc_allocate(toc, size);\n\tstrcpy_s(usernameTarget, size, username);\n\tshm_toc_insert(toc, CITUS_BACKGROUND_TASK_KEY_USERNAME, usernameTarget);\n\n\tsize = strlen(command) + 1;\n\tchar *commandTarget = shm_toc_allocate(toc, size);\n\tstrcpy_s(commandTarget, size, command);\n\tshm_toc_insert(toc, CITUS_BACKGROUND_TASK_KEY_COMMAND, commandTarget);\n\n\tshm_mq *mq = shm_mq_create(shm_toc_allocate(toc, QUEUE_SIZE), QUEUE_SIZE);\n\tshm_toc_insert(toc, CITUS_BACKGROUND_TASK_KEY_QUEUE, mq);\n\tshm_mq_set_receiver(mq, MyProc);\n\n\tint64 *taskIdTarget = shm_toc_allocate(toc, sizeof(int64));\n\t*taskIdTarget = taskId;\n\tshm_toc_insert(toc, CITUS_BACKGROUND_TASK_KEY_TASK_ID, taskIdTarget);\n\n\tint64 *jobIdTarget = shm_toc_allocate(toc, sizeof(int64));\n\t*jobIdTarget = jobId;\n\tshm_toc_insert(toc, CITUS_BACKGROUND_TASK_KEY_JOB_ID, jobIdTarget);\n\n\tshm_mq_attach(mq, seg, NULL);\n\n\t/*\n\t * when we have CurrentResourceOwner != NULL, segment will be released upon CurrentResourceOwner release,\n\t * but we may consume the queue in segment even after CurrentResourceOwner released. 'dsm_pin_mapping' helps\n\t * persisting the segment until the session ends or the segment is detached explicitly by 'dsm_detach'.\n\t */\n\tdsm_pin_mapping(seg);\n\n\treturn seg;\n}\n\n\n/*\n * StartCitusBackgroundTaskExecutor start a new background worker for the execution of a\n * background task. Callers interested in the shared memory segment that is created\n * between the background worker and the current backend can pass in a segOut to get a\n * pointer to the dynamic shared memory.\n */\nstatic BackgroundWorkerHandle *\nStartCitusBackgroundTaskExecutor(char *database, char *user, char *command,\n\t\t\t\t\t\t\t\t int64 taskId, int64 jobId, dsm_segment **pSegment)\n{\n\tdsm_segment *seg = StoreArgumentsInDSM(database, user, command, taskId, jobId);\n\n\tchar workerName[BGW_MAXLEN];\n\tSafeSnprintf(workerName, BGW_MAXLEN,\n\t\t\t\t \"Citus Background Task Queue Executor: %s/%s for (%ld/%ld)\",\n\t\t\t\t database, user, jobId, taskId);\n\n\tCitusBackgroundWorkerConfig config = {\n\t\t.workerName = workerName,\n\t\t.functionName = \"CitusBackgroundTaskExecutor\",\n\t\t.mainArg = UInt32GetDatum(dsm_segment_handle(seg)),\n\t\t.extensionOwner = InvalidOid,\n\t\t.needsNotification = true,\n\t\t.waitForStartup = true,\n\t\t.restartTime = CITUS_BGW_NEVER_RESTART,\n\t\t.startTime = CITUS_BGW_DEFAULT_START_TIME,\n\t\t.workerType = NULL, /* use default */\n\t\t.extraData = NULL,\n\t\t.extraDataSize = 0\n\t};\n\tBackgroundWorkerHandle *handle = RegisterCitusBackgroundWorker(&config);\n\tif (!handle)\n\t{\n\t\tdsm_detach(seg);\n\t\treturn NULL;\n\t}\n\n\tif (pSegment)\n\t{\n\t\t*pSegment = seg;\n\t}\n\n\treturn handle;\n}\n\n\n/*\n * context for any log/error messages emitted from the background task executor.\n */\ntypedef struct CitusBackgroundJobExecutorErrorCallbackContext\n{\n\tconst char *database;\n\tconst char *username;\n\tint64 taskId;\n\tint64 jobId;\n} CitusBackgroundJobExecutorErrorCallbackContext;\n\n\n/*\n * CitusBackgroundJobExecutorErrorCallback is a callback handler that gets called for any\n * ereport to add extra context to the message.\n */\nstatic void\nCitusBackgroundJobExecutorErrorCallback(void *arg)\n{\n\tCitusBackgroundJobExecutorErrorCallbackContext *context =\n\t\t(CitusBackgroundJobExecutorErrorCallbackContext *) arg;\n\terrcontext(\"Citus Background Task Queue Executor: %s/%s for (%ld/%ld)\",\n\t\t\t   context->database, context->username,\n\t\t\t   context->jobId, context->taskId);\n}\n\n\n/*\n * CitusBackgroundTaskExecutor is the main function of the background tasks queue\n * executor. This backend attaches to a shared memory segment as identified by the\n * main_arg of the background worker.\n *\n * This is mostly based on the background worker logic in pg_cron\n */\nvoid\nCitusBackgroundTaskExecutor(Datum main_arg)\n{\n\t/* handles SIGTERM similar to backends */\n\tpqsignal(SIGTERM, die);\n\tBackgroundWorkerUnblockSignals();\n\n\t/* Set up a dynamic shared memory segment. */\n\tdsm_segment *seg = dsm_attach(DatumGetInt32(main_arg));\n\tif (seg == NULL)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t errmsg(\"unable to map dynamic shared memory segment\")));\n\t}\n\n\tshm_toc *toc = shm_toc_attach(CITUS_BACKGROUND_TASK_MAGIC, dsm_segment_address(seg));\n\tif (toc == NULL)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t errmsg(\"bad magic number in dynamic shared memory segment\")));\n\t}\n\n\tchar *database = shm_toc_lookup(toc, CITUS_BACKGROUND_TASK_KEY_DATABASE, false);\n\tchar *username = shm_toc_lookup(toc, CITUS_BACKGROUND_TASK_KEY_USERNAME, false);\n\tchar *command = shm_toc_lookup(toc, CITUS_BACKGROUND_TASK_KEY_COMMAND, false);\n\tint64 *taskId = shm_toc_lookup(toc, CITUS_BACKGROUND_TASK_KEY_TASK_ID, false);\n\tint64 *jobId = shm_toc_lookup(toc, CITUS_BACKGROUND_TASK_KEY_JOB_ID, false);\n\tshm_mq *mq = shm_toc_lookup(toc, CITUS_BACKGROUND_TASK_KEY_QUEUE, false);\n\n\tshm_mq_set_sender(mq, MyProc);\n\tshm_mq_handle *responseq = shm_mq_attach(mq, seg, NULL);\n\tpq_redirect_to_shm_mq(seg, responseq);\n\n\t/* setup error context to indicate the errors came from a running background task */\n\tErrorContextCallback errorCallback = { 0 };\n\tCitusBackgroundJobExecutorErrorCallbackContext context = {\n\t\t.database = database,\n\t\t.username = username,\n\t\t.taskId = *taskId,\n\t\t.jobId = *jobId,\n\t};\n\terrorCallback.callback = CitusBackgroundJobExecutorErrorCallback;\n\terrorCallback.arg = (void *) &context;\n\terrorCallback.previous = error_context_stack;\n\terror_context_stack = &errorCallback;\n\n\tBackgroundWorkerInitializeConnection(database, username, 0);\n\n\t/* make sure we are the only backend running for this task */\n\tLOCKTAG locktag = { 0 };\n\tSET_LOCKTAG_BACKGROUND_TASK(locktag, *taskId);\n\tconst bool sessionLock = true;\n\tconst bool dontWait = true;\n\tLockAcquireResult locked =\n\t\tLockAcquire(&locktag, AccessExclusiveLock, sessionLock, dontWait);\n\tif (locked == LOCKACQUIRE_NOT_AVAIL)\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to acquire background task lock for taskId: %ld\",\n\t\t\t\t\t\t\t   *taskId),\n\t\t\t\t\t\terrdetail(\"this indicates that an other backend is already \"\n\t\t\t\t\t\t\t\t  \"executing this task\")));\n\t}\n\n\t/* Execute the query. */\n\tStartTransactionCommand();\n\tExecuteSqlString(command);\n\tCommitTransactionCommand();\n\n\t/* Signal that we are done. */\n\tReadyForQuery(DestRemote);\n\n\tdsm_detach(seg);\n\tproc_exit(0);\n}\n\n\n/*\n * Execute given SQL string without SPI or a libpq session.\n */\nstatic void\nExecuteSqlString(const char *sql)\n{\n\t/*\n\t * Parse the SQL string into a list of raw parse trees.\n\t *\n\t * Because we allow statements that perform internal transaction control,\n\t * we can't do this in TopTransactionContext; the parse trees might get\n\t * blown away before we're done executing them.\n\t */\n\tMemoryContext parsecontext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"query parse/plan\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_MINSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_INITSIZE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_MAXSIZE);\n\tMemoryContext oldcontext = MemoryContextSwitchTo(parsecontext);\n\tList *raw_parsetree_list = pg_parse_query(sql);\n\tint commands_remaining = list_length(raw_parsetree_list);\n\tbool isTopLevel = commands_remaining == 1;\n\tMemoryContextSwitchTo(oldcontext);\n\n\t/*\n\t * Do parse analysis, rule rewrite, planning, and execution for each raw\n\t * parsetree.  We must fully execute each query before beginning parse\n\t * analysis on the next one, since there may be interdependencies.\n\t */\n\tRawStmt *parsetree = NULL;\n\tforeach_declared_ptr(parsetree, raw_parsetree_list)\n\t{\n\t\t/*\n\t\t * We don't allow transaction-control commands like COMMIT and ABORT\n\t\t * here.  The entire SQL statement is executed as a single transaction\n\t\t * which commits if no errors are encountered.\n\t\t */\n\t\tif (IsA(parsetree, TransactionStmt))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\"transaction control statements are not allowed in background job\")));\n\t\t}\n\n\t\t/*\n\t\t * Get the command name for use in status display (it also becomes the\n\t\t * default completion tag, down inside PortalRun).  Set ps_status and\n\t\t * do any special start-of-SQL-command processing needed by the\n\t\t * destination.\n\t\t */\n\t\tCommandTag commandTag = CreateCommandTag(parsetree->stmt);\n\t\tset_ps_display(GetCommandTagName(commandTag));\n\t\tBeginCommand(commandTag, DestNone);\n\n\t\t/* Set up a snapshot if parse analysis/planning will need one. */\n\t\tbool snapshot_set = false;\n\t\tif (analyze_requires_snapshot(parsetree))\n\t\t{\n\t\t\tPushActiveSnapshot(GetTransactionSnapshot());\n\t\t\tsnapshot_set = true;\n\t\t}\n\n\t\t/*\n\t\t * OK to analyze, rewrite, and plan this query.\n\t\t *\n\t\t * As with parsing, we need to make sure this data outlives the\n\t\t * transaction, because of the possibility that the statement might\n\t\t * perform internal transaction control.\n\t\t */\n\t\toldcontext = MemoryContextSwitchTo(parsecontext);\n\n#if PG_VERSION_NUM >= 150000\n\t\tList *querytree_list =\n\t\t\tpg_analyze_and_rewrite_fixedparams(parsetree, sql, NULL, 0, NULL);\n#else\n\t\tList *querytree_list =\n\t\t\tpg_analyze_and_rewrite(parsetree, sql, NULL, 0, NULL);\n#endif\n\n\t\tList *plantree_list = pg_plan_queries(querytree_list, sql, 0, NULL);\n\n\t\t/* Done with the snapshot used for parsing/planning */\n\t\tif (snapshot_set)\n\t\t{\n\t\t\tPopActiveSnapshot();\n\t\t}\n\n\t\t/* If we got a cancel signal in analysis or planning, quit */\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\t/*\n\t\t * Execute the query using the unnamed portal.\n\t\t */\n\t\tPortal portal = CreatePortal(\"\", true, true);\n\n\t\t/* Don't display the portal in pg_cursors */\n\t\tportal->visible = false;\n\n\t\t/* PG17-: six‐arg signature */\n\t\tPortalDefineQuery(\n\t\t\tportal,\n\t\t\tNULL,             /* no prepared‐stmt name */\n\t\t\tsql,              /* the query text */\n\t\t\tcommandTag,       /* the CommandTag */\n\t\t\tplantree_list,    /* List of PlannedStmt* */\n\t\t\tNULL              /* no CachedPlan */\n\t\t\t);\n\n\t\tPortalStart(portal, NULL, 0, InvalidSnapshot);\n\t\tint16 format[] = { 1 };\n\t\tPortalSetResultFormat(portal, lengthof(format), format);        /* binary format */\n\n\t\tcommands_remaining--;\n\t\tDestReceiver *receiver = CreateDestReceiver(DestNone);\n\n\t\t/*\n\t\t * Only once the portal and destreceiver have been established can\n\t\t * we return to the transaction context.  All that stuff needs to\n\t\t * survive an internal commit inside PortalRun!\n\t\t */\n\t\tMemoryContextSwitchTo(oldcontext);\n\n\t\t/* Here's where we actually execute the command. */\n\t\tQueryCompletion qc = { 0 };\n\n/* Execute the portal, dropping the `run_once` arg on PG18+ */\n#if PG_VERSION_NUM >= PG_VERSION_18\n\t\t(void) PortalRun(\n\t\t\tportal,\n\t\t\tFETCH_ALL,  /* count */\n\t\t\tisTopLevel, /* isTopLevel */\n\t\t\treceiver,   /* DestReceiver *dest */\n\t\t\treceiver,   /* DestReceiver *altdest */\n\t\t\t&qc         /* QueryCompletion *qc */\n\t\t\t);\n#else\n\t\t(void) PortalRun(\n\t\t\tportal,\n\t\t\tFETCH_ALL,  /* count */\n\t\t\tisTopLevel, /* isTopLevel */\n\t\t\ttrue,       /* run_once */\n\t\t\treceiver,   /* DestReceiver *dest */\n\t\t\treceiver,   /* DestReceiver *altdest */\n\t\t\t&qc         /* QueryCompletion *qc */\n\t\t\t);\n#endif\n\n\t\t/* Clean up the receiver. */\n\t\t(*receiver->rDestroy)(receiver);\n\n\t\t/*\n\t\t * Send a CommandComplete message even if we suppressed the query\n\t\t * results.  The user backend will report these in the absence of\n\t\t * any true query results.\n\t\t */\n\t\tEndCommand(&qc, DestRemote, false);\n\n\t\t/* Clean up the portal. */\n\t\tPortalDrop(portal, false);\n\t}\n\n\t/* Be sure to advance the command counter after the last script command */\n\tCommandCounterIncrement();\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/background_worker_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * background_worker_utils.c\n *    Common utilities for initializing PostgreSQL background workers\n *    used by Citus distributed infrastructure.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"postmaster/bgworker.h\"\n#include \"storage/proc.h\"\n\n#include \"distributed/background_worker_utils.h\"\n#include \"distributed/citus_safe_lib.h\"\n\n/*\n * InitializeCitusBackgroundWorker initializes a BackgroundWorker struct\n * with common Citus background worker settings.\n */\nvoid\nInitializeCitusBackgroundWorker(BackgroundWorker *worker,\n\t\t\t\t\t\t\t\tconst CitusBackgroundWorkerConfig *config)\n{\n\tAssert(worker != NULL);\n\tAssert(config != NULL);\n\tAssert(config->workerName != NULL);\n\tAssert(config->functionName != NULL);\n\n\t/* Initialize the worker structure */\n\tmemset(worker, 0, sizeof(BackgroundWorker));\n\n\t/* Set worker name */\n\tstrcpy_s(worker->bgw_name, sizeof(worker->bgw_name), config->workerName);\n\n\t/* Set worker type if provided */\n\tif (config->workerType != NULL)\n\t{\n\t\tstrcpy_s(worker->bgw_type, sizeof(worker->bgw_type), config->workerType);\n\t}\n\n\t/* Set standard flags for Citus workers */\n\tworker->bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;\n\n\t/* Set start time - use custom start time if provided, otherwise use default */\n\tworker->bgw_start_time = (config->startTime != 0) ? config->startTime :\n\t\t\t\t\t\t\t CITUS_BGW_DEFAULT_START_TIME;\n\n\t/* Set restart behavior */\n\tworker->bgw_restart_time = config->restartTime;\n\n\t/* Set library and function names */\n\tstrcpy_s(worker->bgw_library_name, sizeof(worker->bgw_library_name), \"citus\");\n\tstrcpy_s(worker->bgw_function_name, sizeof(worker->bgw_function_name),\n\t\t\t config->functionName);\n\n\t/* Set main argument */\n\tworker->bgw_main_arg = config->mainArg;\n\n\t/* Set extension owner if provided */\n\tif (OidIsValid(config->extensionOwner))\n\t{\n\t\tmemcpy_s(worker->bgw_extra, sizeof(worker->bgw_extra),\n\t\t\t\t &config->extensionOwner, sizeof(Oid));\n\t}\n\n\t/* Set additional extra data if provided */\n\tif (config->extraData != NULL && config->extraDataSize > 0)\n\t{\n\t\tsize_t remainingSpace = sizeof(worker->bgw_extra);\n\t\tsize_t usedSpace = OidIsValid(config->extensionOwner) ? sizeof(Oid) : 0;\n\n\t\tif (usedSpace + config->extraDataSize <= remainingSpace)\n\t\t{\n\t\t\tmemcpy_s(((char *) worker->bgw_extra) + usedSpace,\n\t\t\t\t\t remainingSpace - usedSpace,\n\t\t\t\t\t config->extraData,\n\t\t\t\t\t config->extraDataSize);\n\t\t}\n\t}\n\n\t/* Set notification PID if needed */\n\tif (config->needsNotification)\n\t{\n\t\tworker->bgw_notify_pid = MyProcPid;\n\t}\n}\n\n\n/*\n * RegisterCitusBackgroundWorker creates and registers a Citus background worker\n * with the specified configuration. Returns the worker handle on success,\n * NULL on failure.\n */\nBackgroundWorkerHandle *\nRegisterCitusBackgroundWorker(const CitusBackgroundWorkerConfig *config)\n{\n\tBackgroundWorker worker;\n\tBackgroundWorkerHandle *handle = NULL;\n\n\t/* Initialize the worker structure */\n\tInitializeCitusBackgroundWorker(&worker, config);\n\n\t/* Register the background worker */\n\tif (!RegisterDynamicBackgroundWorker(&worker, &handle))\n\t{\n\t\treturn NULL;\n\t}\n\n\t/* Wait for startup if requested */\n\tif (config->waitForStartup && handle != NULL)\n\t{\n\t\tpid_t pid = 0;\n\t\tWaitForBackgroundWorkerStartup(handle, &pid);\n\t}\n\n\treturn handle;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/cancel_utils.c",
    "content": "/*\n * cancel_utils.c\n *\n * Utilities related to query cancellation\n *\n * Copyright (c) Citus Data, Inc.\n */\n\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"distributed/cancel_utils.h\"\n\n\n/*\n * IsHoldOffCancellationReceived returns true if a cancel signal\n * was sent and HOLD_INTERRUPTS was called prior to this. The motivation\n * here is that since our queries can take a long time, in some places\n * we do not want to wait even if HOLD_INTERRUPTS was called.\n */\nbool\nIsHoldOffCancellationReceived()\n{\n\treturn InterruptHoldoffCount > 0 && (QueryCancelPending || ProcDiePending);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/citus_clauses.c",
    "content": "/*\n * citus_clauses.c\n *\n * Routines roughly equivalent to postgres' util/clauses.\n *\n * Copyright (c) Citus Data, Inc.\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_type.h\"\n#include \"executor/executor.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/primnodes.h\"\n#include \"optimizer/clauses.h\"\n#include \"optimizer/optimizer.h\"\n#include \"optimizer/planmain.h\"\n#include \"utils/datum.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/citus_clauses.h\"\n#include \"distributed/insert_select_planner.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/version_compat.h\"\n\n\n/* private function declarations */\nstatic bool IsVariableExpression(Node *node);\nstatic Expr * citus_evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,\n\t\t\t\t\t\t\t\t  Oid result_collation,\n\t\t\t\t\t\t\t\t  CoordinatorEvaluationContext *\n\t\t\t\t\t\t\t\t  coordinatorEvaluationContext);\nstatic bool CitusIsVolatileFunctionIdChecker(Oid func_id, void *context);\nstatic bool CitusIsMutableFunctionIdChecker(Oid func_id, void *context);\nstatic bool ShouldEvaluateExpression(Expr *expression);\nstatic bool ShouldEvaluateFunctions(CoordinatorEvaluationContext *evaluationContext);\nstatic void FixFunctionArguments(Node *expr);\nstatic bool FixFunctionArgumentsWalker(Node *expr, void *context);\nstatic bool CheckExprExecutorSafe(Node *expr);\n\n\n/*\n * RequiresCoordinatorEvaluation returns the executor needs to reparse and\n * try to execute this query, which is the case if the query contains\n * any stable or volatile function.\n */\nbool\nRequiresCoordinatorEvaluation(Query *query)\n{\n\tif (query->commandType == CMD_SELECT && !query->hasModifyingCTE)\n\t{\n\t\treturn false;\n\t}\n\n\treturn FindNodeMatchingCheckFunction((Node *) query, CitusIsMutableFunction);\n}\n\n\n/*\n * ExecuteCoordinatorEvaluableExpressions evaluates expressions and parameters\n * that can be resolved to a constant.\n */\nvoid\nExecuteCoordinatorEvaluableExpressions(Query *query, PlanState *planState)\n{\n\tCoordinatorEvaluationContext coordinatorEvaluationContext;\n\n\tcoordinatorEvaluationContext.planState = planState;\n\tif (query->commandType == CMD_SELECT)\n\t{\n\t\tcoordinatorEvaluationContext.evaluationMode = EVALUATE_PARAMS;\n\t}\n\telse\n\t{\n\t\tcoordinatorEvaluationContext.evaluationMode = EVALUATE_FUNCTIONS_PARAMS;\n\t}\n\n\tPartiallyEvaluateExpression((Node *) query, &coordinatorEvaluationContext);\n}\n\n\n/*\n * PartiallyEvaluateExpression descends into an expression tree to evaluate\n * expressions that can be resolved to a constant on the master. Expressions\n * containing a Var are skipped, since the value of the Var is not known\n * on the master.\n */\nNode *\nPartiallyEvaluateExpression(Node *expression,\n\t\t\t\t\t\t\tCoordinatorEvaluationContext *coordinatorEvaluationContext)\n{\n\tif (expression == NULL || IsA(expression, Const))\n\t{\n\t\treturn expression;\n\t}\n\n\tNodeTag nodeTag = nodeTag(expression);\n\n\t/* ExecInitExpr cannot handle some expressions (PARAM_MULTIEXPR and PARAM_SUBLINK) */\n\tif (!CheckExprExecutorSafe(expression))\n\t{\n\t\treturn expression;\n\t}\n\n\t/* ExecInitExpr cannot handle PARAM_MULTIEXPR and PARAM_SUBLINK but we have guards */\n\telse if (nodeTag == T_Param)\n\t{\n\t\tAssert(((Param *) expression)->paramkind != PARAM_MULTIEXPR &&\n\t\t\t   ((Param *) expression)->paramkind != PARAM_SUBLINK);\n\t\treturn (Node *) citus_evaluate_expr((Expr *) expression,\n\t\t\t\t\t\t\t\t\t\t\texprType(expression),\n\t\t\t\t\t\t\t\t\t\t\texprTypmod(expression),\n\t\t\t\t\t\t\t\t\t\t\texprCollation(expression),\n\t\t\t\t\t\t\t\t\t\t\tcoordinatorEvaluationContext);\n\t}\n\telse if (ShouldEvaluateExpression((Expr *) expression) &&\n\t\t\t ShouldEvaluateFunctions(coordinatorEvaluationContext))\n\t{\n\t\t/*\n\t\t * The planner normally evaluates constant expressions, but we may be\n\t\t * working on the original query tree. We could rely on\n\t\t * citus_evaluate_expr to evaluate constant expressions, but there are\n\t\t * certain node types that citus_evaluate_expr does not expect because\n\t\t * the planner normally replaces them (in particular, CollateExpr).\n\t\t * Hence, we first evaluate constant expressions using\n\t\t * eval_const_expressions before continuing.\n\t\t *\n\t\t * NOTE: We do not use expression_planner here, since all it does\n\t\t * apart from calling eval_const_expressions is call fix_opfuncids.\n\t\t * We do not need this, since that is already called in\n\t\t * citus_evaluate_expr. So we won't needlessly traverse the expression\n\t\t * tree by calling it another time.\n\t\t */\n\t\texpression = eval_const_expressions(NULL, expression);\n\n\t\t/*\n\t\t * It's possible that after evaluating const expressions we\n\t\t * actually don't need to evaluate this expression anymore e.g:\n\t\t *\n\t\t * 1 = 0 AND now() > timestamp '10-10-2000 00:00'\n\t\t *\n\t\t * This statement would simply resolve to false, because 1 = 0 is\n\t\t * false. That's why we now check again if we should evaluate the\n\t\t * expression and only continue if we still do.\n\t\t */\n\t\tif (!ShouldEvaluateExpression((Expr *) expression))\n\t\t{\n\t\t\treturn (Node *) expression_tree_mutator(expression,\n\t\t\t\t\t\t\t\t\t\t\t\t\tPartiallyEvaluateExpression,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcoordinatorEvaluationContext);\n\t\t}\n\n\t\tif (FindNodeMatchingCheckFunction(expression, IsVariableExpression))\n\t\t{\n\t\t\t/*\n\t\t\t * The expression contains a variable expression (e.g. a stable function,\n\t\t\t * which has a column reference as its input). That means that we cannot\n\t\t\t * evaluate the expression on the coordinator, since the result depends\n\t\t\t * on the input.\n\t\t\t *\n\t\t\t * Skipping function evaluation for these expressions is safe in most\n\t\t\t * cases, since the function will always be re-evaluated for every input\n\t\t\t * value. An exception is function calls that call another stable function\n\t\t\t * that should not be re-evaluated, such as now().\n\t\t\t */\n\t\t\treturn (Node *) expression_tree_mutator(expression,\n\t\t\t\t\t\t\t\t\t\t\t\t\tPartiallyEvaluateExpression,\n\t\t\t\t\t\t\t\t\t\t\t\t\tcoordinatorEvaluationContext);\n\t\t}\n\n\t\treturn (Node *) citus_evaluate_expr((Expr *) expression,\n\t\t\t\t\t\t\t\t\t\t\texprType(expression),\n\t\t\t\t\t\t\t\t\t\t\texprTypmod(expression),\n\t\t\t\t\t\t\t\t\t\t\texprCollation(expression),\n\t\t\t\t\t\t\t\t\t\t\tcoordinatorEvaluationContext);\n\t}\n\telse if (nodeTag == T_Query)\n\t{\n\t\tQuery *query = (Query *) expression;\n\t\tCoordinatorEvaluationContext subContext = *coordinatorEvaluationContext;\n\t\tif (query->commandType != CMD_SELECT)\n\t\t{\n\t\t\t/*\n\t\t\t * Currently INSERT SELECT evaluates stable functions on master,\n\t\t\t * while a plain SELECT does not. For evaluating SELECT evaluationMode is\n\t\t\t * EVALUATE_PARAMS, but if recursing into a modifying CTE switch into\n\t\t\t * EVALUATE_FUNCTIONS_PARAMS.\n\t\t\t */\n\t\t\tsubContext.evaluationMode = EVALUATE_FUNCTIONS_PARAMS;\n\t\t}\n\n\t\treturn (Node *) query_tree_mutator(query,\n\t\t\t\t\t\t\t\t\t\t   PartiallyEvaluateExpression,\n\t\t\t\t\t\t\t\t\t\t   &subContext,\n\t\t\t\t\t\t\t\t\t\t   QTW_DONT_COPY_QUERY);\n\t}\n\telse\n\t{\n\t\treturn (Node *) expression_tree_mutator(expression,\n\t\t\t\t\t\t\t\t\t\t\t\tPartiallyEvaluateExpression,\n\t\t\t\t\t\t\t\t\t\t\t\tcoordinatorEvaluationContext);\n\t}\n\n\treturn expression;\n}\n\n\n/*\n * ShouldEvaluateFunctions is a helper function which is used to\n * decide whether the function/expression should be evaluated with the input\n * coordinatorEvaluationContext.\n */\nstatic bool\nShouldEvaluateFunctions(CoordinatorEvaluationContext *evaluationContext)\n{\n\tif (evaluationContext == NULL)\n\t{\n\t\t/* if no context provided, evaluate, which is the default behaviour */\n\t\treturn true;\n\t}\n\n\treturn evaluationContext->evaluationMode == EVALUATE_FUNCTIONS_PARAMS;\n}\n\n\n/*\n * ShouldEvaluateExpression returns true if Citus should evaluate the\n * input node on the coordinator.\n */\nstatic bool\nShouldEvaluateExpression(Expr *expression)\n{\n\tNodeTag nodeTag = nodeTag(expression);\n\n\tswitch (nodeTag)\n\t{\n\t\tcase T_FuncExpr:\n\t\t{\n\t\t\tFuncExpr *funcExpr = (FuncExpr *) expression;\n\n\t\t\t/* we cannot evaluate set returning functions */\n\t\t\tbool isSetReturningFunction = funcExpr->funcretset;\n\t\t\treturn !isSetReturningFunction;\n\t\t}\n\n\t\tcase T_OpExpr:\n\t\tcase T_DistinctExpr:\n\t\tcase T_NullIfExpr:\n\t\tcase T_CoerceViaIO:\n\t\tcase T_ArrayCoerceExpr:\n\t\tcase T_ScalarArrayOpExpr:\n\t\tcase T_RowExpr:\n\t\tcase T_RowCompareExpr:\n\t\tcase T_RelabelType:\n\t\tcase T_CoerceToDomain:\n\t\tcase T_NextValueExpr:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\n\n/*\n * IsVariableExpression returns whether the given node is a variable expression,\n * meaning its result depends on the input data and is not constant for the whole\n * query.\n */\nstatic bool\nIsVariableExpression(Node *node)\n{\n\tif (IsA(node, Aggref))\n\t{\n\t\treturn true;\n\t}\n\n\tif (IsA(node, WindowFunc))\n\t{\n\t\treturn true;\n\t}\n\n\tif (IsA(node, Param))\n\t{\n\t\t/* ExecInitExpr cannot handle PARAM_SUBLINK */\n\t\treturn ((Param *) node)->paramkind == PARAM_SUBLINK;\n\t}\n\n\treturn IsA(node, Var);\n}\n\n\n/*\n * a copy of pg's evaluate_expr, pre-evaluate a constant expression\n *\n * We use the executor's routine ExecEvalExpr() to avoid duplication of\n * code and ensure we get the same result as the executor would get.\n *\n * *INDENT-OFF*\n */\nstatic Expr *\ncitus_evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,\n\t\t\t\t\tOid result_collation,\n\t\t\t\t\tCoordinatorEvaluationContext *coordinatorEvaluationContext)\n{\n\tPlanState *planState = NULL;\n\tEState     *estate;\n\tExprState  *exprstate;\n\tDatum\t\tconst_val;\n\tbool\t\tconst_is_null;\n\tint16\t\tresultTypLen;\n\tbool\t\tresultTypByVal;\n\n\tif (coordinatorEvaluationContext)\n\t{\n\t\tplanState = coordinatorEvaluationContext->planState;\n\n\t\tif (IsA(expr, Param))\n\t\t{\n\t\t\tif (coordinatorEvaluationContext->evaluationMode == EVALUATE_NONE)\n\t\t\t{\n\t\t\t\t/* bail out, the caller doesn't want params to be evaluated  */\n\t\t\t\treturn expr;\n\t\t\t}\n\t\t}\n\t\telse if (coordinatorEvaluationContext->evaluationMode != EVALUATE_FUNCTIONS_PARAMS)\n\t\t{\n\t\t\t/* should only get here for node types we should evaluate */\n\t\t\tAssert(ShouldEvaluateExpression(expr));\n\n\t\t\t/* bail out, the caller doesn't want functions/expressions to be evaluated */\n\t\t\treturn expr;\n\t\t}\n\t}\n\n\t/*\n\t * To use the executor, we need an EState.\n\t */\n\testate = CreateExecutorState();\n\n\t/* We can use the estate's working context to avoid memory leaks. */\n\tMemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);\n\n\t/* handles default values */\n\tFixFunctionArguments((Node *) expr);\n\n\t/* Make sure any opfuncids are filled in. */\n\tfix_opfuncids((Node *) expr);\n\n\t/*\n\t * Prepare expr for execution.  (Note: we can't use ExecPrepareExpr\n\t * because it'd result in recursively invoking eval_const_expressions.)\n\t */\n\texprstate = ExecInitExpr(expr, planState);\n\n\t/*\n\t * Get short lived per tuple context as evaluate_expr does. Here we don't\n\t * use planState->ExprContext as it might cause double-free'ing executor\n\t * state.\n\t */\n\tExprContext *econtext = GetPerTupleExprContext(estate);\n\tif (planState)\n\t{\n\t\t/*\n\t\t * If planState exists, then we add es_param_list_info to per tuple\n\t\t * ExprContext as we need them when evaluating prepared statements.\n\t\t */\n\t\tecontext->ecxt_param_list_info = planState->state->es_param_list_info;\n\t}\n\n\t/*\n\t * And evaluate it.\n\t */\n\tconst_val = ExecEvalExprSwitchContext(exprstate, econtext, &const_is_null);\n\n\t/* Get info needed about result datatype */\n\tget_typlenbyval(result_type, &resultTypLen, &resultTypByVal);\n\n\t/* Get back to outer memory context */\n\tMemoryContextSwitchTo(oldcontext);\n\n\t/*\n\t * Must copy result out of sub-context used by expression eval.\n\t *\n\t * Also, if it's varlena, forcibly detoast it.  This protects us against\n\t * storing TOAST pointers into plans that might outlive the referenced\n\t * data.  (makeConst would handle detoasting anyway, but it's worth a few\n\t * extra lines here so that we can do the copy and detoast in one step.)\n\t */\n\tif (!const_is_null)\n\t{\n\t\tif (resultTypLen == -1)\n\t\t\tconst_val = PointerGetDatum(PG_DETOAST_DATUM_COPY(const_val));\n\t\telse\n\t\t\tconst_val = datumCopy(const_val, resultTypByVal, resultTypLen);\n\t}\n\n\t/* Release all the junk we just created */\n\tFreeExecutorState(estate);\n\n\t/*\n\t * Make the constant result node.\n\t */\n\treturn (Expr *) makeConst(result_type, result_typmod, result_collation,\n\t\t\t\t\t\t\t  resultTypLen,\n\t\t\t\t\t\t\t  const_val, const_is_null,\n\t\t\t\t\t\t\t  resultTypByVal);\n}\n\n/* *INDENT-ON* */\n\n\n/*\n * CitusIsVolatileFunctionIdChecker checks if the given function id is\n * a volatile function other than read_intermediate_result().\n */\nstatic bool\nCitusIsVolatileFunctionIdChecker(Oid func_id, void *context)\n{\n\tif (func_id == CitusReadIntermediateResultFuncId() ||\n\t\tfunc_id == CitusReadIntermediateResultArrayFuncId())\n\t{\n\t\treturn false;\n\t}\n\n\treturn (func_volatile(func_id) == PROVOLATILE_VOLATILE);\n}\n\n\n/*\n * CitusIsVolatileFunction checks if the given node is a volatile function\n * other than Citus's internal functions.\n */\nbool\nCitusIsVolatileFunction(Node *node)\n{\n\t/* Check for volatile functions in node itself */\n\tif (check_functions_in_node(node, CitusIsVolatileFunctionIdChecker, NULL))\n\t{\n\t\treturn true;\n\t}\n\n\tif (IsA(node, NextValueExpr))\n\t{\n\t\t/* NextValueExpr is volatile */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CitusIsMutableFunctionIdChecker checks if the given function id is\n * a mutable function other than read_intermediate_result().\n */\nstatic bool\nCitusIsMutableFunctionIdChecker(Oid func_id, void *context)\n{\n\tif (func_id == CitusReadIntermediateResultFuncId() ||\n\t\tfunc_id == CitusReadIntermediateResultArrayFuncId())\n\t{\n\t\treturn false;\n\t}\n\telse\n\t{\n\t\treturn (func_volatile(func_id) != PROVOLATILE_IMMUTABLE);\n\t}\n}\n\n\n/*\n * CitusIsMutableFunction checks if the given node is a mutable function\n * other than Citus's internal functions.\n */\nbool\nCitusIsMutableFunction(Node *node)\n{\n\t/* Check for mutable functions in node itself */\n\tif (check_functions_in_node(node, CitusIsMutableFunctionIdChecker, NULL))\n\t{\n\t\treturn true;\n\t}\n\n\tif (IsA(node, SQLValueFunction))\n\t{\n\t\t/* all variants of SQLValueFunction are stable */\n\t\treturn true;\n\t}\n\n\tif (IsA(node, NextValueExpr))\n\t{\n\t\t/* NextValueExpr is volatile */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/* FixFunctionArguments applies expand_function_arguments to all function calls. */\nstatic void\nFixFunctionArguments(Node *expr)\n{\n\tFixFunctionArgumentsWalker(expr, NULL);\n}\n\n\n/* FixFunctionArgumentsWalker is the helper function for fix_funcargs. */\nstatic bool\nFixFunctionArgumentsWalker(Node *expr, void *context)\n{\n\tif (expr == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(expr, FuncExpr))\n\t{\n\t\tFuncExpr *funcExpr = castNode(FuncExpr, expr);\n\t\tHeapTuple func_tuple =\n\t\t\tSearchSysCache1(PROCOID, ObjectIdGetDatum(funcExpr->funcid));\n\t\tif (!HeapTupleIsValid(func_tuple))\n\t\t{\n\t\t\telog(ERROR, \"cache lookup failed for function %u\", funcExpr->funcid);\n\t\t}\n\n\t\tfuncExpr->args = expand_function_arguments(funcExpr->args, false,\n\t\t\t\t\t\t\t\t\t\t\t\t   funcExpr->funcresulttype,\n\t\t\t\t\t\t\t\t\t\t\t\t   func_tuple);\n\n\t\tReleaseSysCache(func_tuple);\n\t}\n\n\treturn expression_tree_walker(expr, FixFunctionArgumentsWalker, NULL);\n}\n\n\n/*\n * Recursively explore an expression to ensure it can be used in the PostgreSQL\n * ExecInitExpr.\n * Currently only search for PARAM_MULTIEXPR or PARAM_SUBLINK.\n */\nstatic bool\nCheckExprExecutorSafe(Node *expr)\n{\n\tif (expr == NULL)\n\t{\n\t\treturn true;\n\t}\n\n\t/*\n\t * If it's a Param, we're done traversing the tree.\n\t * Just check if it contins a sublink or multiexpr.\n\t */\n\telse if (IsA(expr, Param))\n\t{\n\t\tParam *param = (Param *) expr;\n\t\tif (param->paramkind == PARAM_MULTIEXPR ||\n\t\t\tparam->paramkind == PARAM_SUBLINK)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/* If it's a FuncExpr, search in arguments */\n\telse if (IsA(expr, FuncExpr))\n\t{\n\t\tFuncExpr *func = (FuncExpr *) expr;\n\t\tListCell *lc;\n\n\t\tforeach(lc, func->args)\n\t\t{\n\t\t\tif (!CheckExprExecutorSafe((Node *) lfirst(lc)))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/citus_copyfuncs.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_copyfuncs.c\n *    Citus specific node copy functions\n *\n * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group\n * Portions Copyright (c) 1994, Regents of the University of California\n * Portions Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"utils/datum.h\"\n\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/multi_server_executor.h\"\n\n\n/*\n * Macros to simplify copying of different kinds of fields.  Use these\n * wherever possible to reduce the chance for silly typos.  Note that these\n * hard-wire the convention that the local variables in a Copy routine are\n * named 'newnode' and 'from'.\n */\nstatic inline Node *\nCitusSetTag(Node *node, int tag)\n{\n\tCitusNode *citus_node = (CitusNode *) node;\n\tcitus_node->citus_tag = tag;\n\treturn node;\n}\n\n\n#define DECLARE_FROM_AND_NEW_NODE(nodeTypeName) \\\n\t\tnodeTypeName *newnode = \\\n\t\t\t(nodeTypeName *) CitusSetTag((Node *) target_node, T_ ## nodeTypeName); \\\n\t\tnodeTypeName *from = (nodeTypeName *) source_node\n\n/* Copy a simple scalar field (int, float, bool, enum, etc) */\n#define COPY_SCALAR_FIELD(fldname) \\\n\t\t(newnode->fldname = from->fldname)\n\n/* Copy a field that is a pointer to some kind of Node or Node tree */\n#define COPY_NODE_FIELD(fldname) \\\n\t\t(newnode->fldname = copyObject(from->fldname))\n\n/* Copy a field that is a pointer to a C string, or perhaps NULL */\n#define COPY_STRING_FIELD(fldname) \\\n\t\t(newnode->fldname = from->fldname ? pstrdup(from->fldname) : (char *) NULL)\n\n/* Copy a node array. Target array is also allocated. */\n#define COPY_NODE_ARRAY(fldname, type, count) \\\n\t\tdo { \\\n\t\t\tint i = 0; \\\n\t\t\tnewnode->fldname = (type **) palloc(count * sizeof(type *)); \\\n\t\t\tfor (i = 0; i < count; ++i) \\\n\t\t\t{ \\\n\t\t\t\tnewnode->fldname[i] = copyObject(from->fldname[i]); \\\n\t\t\t} \\\n\t\t} \\\n\t\twhile (0)\n\n/* Copy a scalar array. Target array is also allocated. */\n#define COPY_SCALAR_ARRAY(fldname, type, count) \\\n\t\tdo { \\\n\t\t\tint i = 0; \\\n\t\t\tnewnode->fldname = (type *) palloc(count * sizeof(type)); \\\n\t\t\tfor (i = 0; i < count; ++i) \\\n\t\t\t{ \\\n\t\t\t\tnewnode->fldname[i] = from->fldname[i]; \\\n\t\t\t} \\\n\t\t} \\\n\t\twhile (0)\n\n#define COPY_STRING_LIST(fldname) \\\n\t\tdo { \\\n\t\t\tchar *curString = NULL; \\\n\t\t\tList *newList = NIL; \\\n\t\t\tforeach_declared_ptr(curString, from->fldname) { \\\n\t\t\t\tchar *newString = curString ? pstrdup(curString) : (char *) NULL; \\\n\t\t\t\tnewList = lappend(newList, newString); \\\n\t\t\t} \\\n\t\t\tnewnode->fldname = newList; \\\n\t\t} \\\n\t\twhile (0)\n\nstatic void CopyTaskQuery(Task *newnode, Task *from);\n\nstatic void\ncopyJobInfo(Job *newnode, Job *from)\n{\n\tCOPY_SCALAR_FIELD(jobId);\n\tCOPY_NODE_FIELD(jobQuery);\n\tCOPY_NODE_FIELD(taskList);\n\tCOPY_NODE_FIELD(dependentJobList);\n\tCOPY_SCALAR_FIELD(subqueryPushdown);\n\tCOPY_SCALAR_FIELD(requiresCoordinatorEvaluation);\n\tCOPY_SCALAR_FIELD(deferredPruning);\n\tCOPY_NODE_FIELD(partitionKeyValue);\n\tCOPY_NODE_FIELD(localPlannedStatements);\n\tCOPY_SCALAR_FIELD(parametersInJobQueryResolved);\n}\n\n\nvoid\nCopyNodeJob(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(Job);\n\n\tcopyJobInfo(newnode, from);\n}\n\n\nvoid\nCopyNodeDistributedPlan(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(DistributedPlan);\n\n\tCOPY_SCALAR_FIELD(planId);\n\tCOPY_SCALAR_FIELD(modLevel);\n\tCOPY_SCALAR_FIELD(expectResults);\n\n\tCOPY_NODE_FIELD(workerJob);\n\tCOPY_NODE_FIELD(combineQuery);\n\tCOPY_SCALAR_FIELD(queryId);\n\tCOPY_NODE_FIELD(relationIdList);\n\tCOPY_SCALAR_FIELD(targetRelationId);\n\tCOPY_NODE_FIELD(modifyQueryViaCoordinatorOrRepartition);\n\tCOPY_NODE_FIELD(selectPlanForModifyViaCoordinatorOrRepartition);\n\tCOPY_SCALAR_FIELD(modifyWithSelectMethod);\n\tCOPY_STRING_FIELD(intermediateResultIdPrefix);\n\n\tCOPY_NODE_FIELD(subPlanList);\n\tCOPY_NODE_FIELD(usedSubPlanNodeList);\n\tCOPY_SCALAR_FIELD(fastPathRouterPlan);\n\tCOPY_SCALAR_FIELD(numberOfTimesExecuted);\n\tCOPY_NODE_FIELD(planningError);\n\n\tCOPY_SCALAR_FIELD(sourceResultRepartitionColumnIndex);\n}\n\n\nvoid\nCopyNodeDistributedSubPlan(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(DistributedSubPlan);\n\n\tCOPY_SCALAR_FIELD(subPlanId);\n\tCOPY_NODE_FIELD(plan);\n\tCOPY_SCALAR_FIELD(bytesSentPerWorker);\n\tCOPY_SCALAR_FIELD(remoteWorkerCount);\n\tCOPY_SCALAR_FIELD(durationMillisecs);\n\tCOPY_SCALAR_FIELD(writeLocalFile);\n\n\tif (newnode->totalExplainOutput)\n\t{\n\t\tMemSet(newnode->totalExplainOutput, 0, sizeof(newnode->totalExplainOutput));\n\t}\n\n\t/* copy each SubPlanExplainOutput element */\n\tfor (int i = 0; i < from->numTasksOutput; i++)\n\t{\n\t\t/* copy the explainOutput string pointer */\n\t\tCOPY_STRING_FIELD(totalExplainOutput[i].explainOutput);\n\n\t\t/* copy the executionDuration (double) */\n\t\tCOPY_SCALAR_FIELD(totalExplainOutput[i].executionDuration);\n\n\t\t/* copy the totalReceivedTupleData (uint64) */\n\t\tCOPY_SCALAR_FIELD(totalExplainOutput[i].totalReceivedTupleData);\n\t}\n\n\tCOPY_SCALAR_FIELD(numTasksOutput);\n\tCOPY_SCALAR_FIELD(ntuples);\n}\n\n\nvoid\nCopyNodeUsedDistributedSubPlan(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(UsedDistributedSubPlan);\n\n\tCOPY_STRING_FIELD(subPlanId);\n\tCOPY_SCALAR_FIELD(accessType);\n}\n\n\nvoid\nCopyNodeShardInterval(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(ShardInterval);\n\n\tCOPY_SCALAR_FIELD(relationId);\n\tCOPY_SCALAR_FIELD(storageType);\n\tCOPY_SCALAR_FIELD(valueTypeId);\n\tCOPY_SCALAR_FIELD(valueTypeLen);\n\tCOPY_SCALAR_FIELD(valueByVal);\n\tCOPY_SCALAR_FIELD(minValueExists);\n\tCOPY_SCALAR_FIELD(maxValueExists);\n\n\tif (from->minValueExists)\n\t{\n\t\tnewnode->minValue = datumCopy(from->minValue,\n\t\t\t\t\t\t\t\t\t  from->valueByVal,\n\t\t\t\t\t\t\t\t\t  from->valueTypeLen);\n\t}\n\tif (from->maxValueExists)\n\t{\n\t\tnewnode->maxValue = datumCopy(from->maxValue,\n\t\t\t\t\t\t\t\t\t  from->valueByVal,\n\t\t\t\t\t\t\t\t\t  from->valueTypeLen);\n\t}\n\n\tCOPY_SCALAR_FIELD(shardId);\n\tCOPY_SCALAR_FIELD(shardIndex);\n}\n\n\nvoid\nCopyNodeMapMergeJob(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(MapMergeJob);\n\n\tcopyJobInfo(&newnode->job, &from->job);\n\n\tCOPY_SCALAR_FIELD(partitionType);\n\tCOPY_NODE_FIELD(partitionColumn);\n\tCOPY_SCALAR_FIELD(partitionCount);\n\tCOPY_SCALAR_FIELD(sortedShardIntervalArrayLength);\n\n\tint arrayLength = from->sortedShardIntervalArrayLength;\n\n\t/* now build & read sortedShardIntervalArray */\n\tCOPY_NODE_ARRAY(sortedShardIntervalArray, ShardInterval, arrayLength);\n\n\tCOPY_NODE_FIELD(mapTaskList);\n\tCOPY_NODE_FIELD(mergeTaskList);\n}\n\n\nvoid\nCopyNodeShardPlacement(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(ShardPlacement);\n\n\tCOPY_SCALAR_FIELD(placementId);\n\tCOPY_SCALAR_FIELD(shardId);\n\tCOPY_SCALAR_FIELD(shardLength);\n\tCOPY_SCALAR_FIELD(groupId);\n\tCOPY_STRING_FIELD(nodeName);\n\tCOPY_SCALAR_FIELD(nodePort);\n\tCOPY_SCALAR_FIELD(nodeId);\n\tCOPY_SCALAR_FIELD(partitionMethod);\n\tCOPY_SCALAR_FIELD(colocationGroupId);\n\tCOPY_SCALAR_FIELD(representativeValue);\n}\n\n\nvoid\nCopyNodeGroupShardPlacement(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(GroupShardPlacement);\n\n\tCOPY_SCALAR_FIELD(placementId);\n\tCOPY_SCALAR_FIELD(shardId);\n\tCOPY_SCALAR_FIELD(shardLength);\n\tCOPY_SCALAR_FIELD(groupId);\n}\n\n\nvoid\nCopyNodeRelationShard(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(RelationShard);\n\n\tCOPY_SCALAR_FIELD(relationId);\n\tCOPY_SCALAR_FIELD(shardId);\n}\n\n\nvoid\nCopyNodeRelationRowLock(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(RelationRowLock);\n\n\tCOPY_SCALAR_FIELD(relationId);\n\tCOPY_SCALAR_FIELD(rowLockStrength);\n}\n\n\nstatic void\nCopyTaskQuery(Task *newnode, Task *from)\n{\n\tCOPY_SCALAR_FIELD(taskQuery.queryType);\n\tswitch (from->taskQuery.queryType)\n\t{\n\t\tcase TASK_QUERY_TEXT:\n\t\t{\n\t\t\tCOPY_STRING_FIELD(taskQuery.data.queryStringLazy);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase TASK_QUERY_OBJECT:\n\t\t{\n\t\t\tCOPY_NODE_FIELD(taskQuery.data.jobQueryReferenceForLazyDeparsing);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase TASK_QUERY_TEXT_LIST:\n\t\t{\n\t\t\tCOPY_STRING_LIST(taskQuery.data.queryStringList);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase TASK_QUERY_LOCAL_PLAN:\n\t\t{\n\t\t\tnewnode->taskQuery.data.localCompiled =\n\t\t\t\t(LocalCompilation *) palloc0(sizeof(LocalCompilation));\n\t\t\tCOPY_NODE_FIELD(taskQuery.data.localCompiled->plan);\n\t\t\tCOPY_NODE_FIELD(taskQuery.data.localCompiled->query);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n\nvoid\nCopyNodeTask(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(Task);\n\n\tCOPY_SCALAR_FIELD(taskType);\n\tCOPY_SCALAR_FIELD(jobId);\n\tCOPY_SCALAR_FIELD(taskId);\n\tCopyTaskQuery(newnode, from);\n\tCOPY_SCALAR_FIELD(anchorDistributedTableId);\n\tCOPY_SCALAR_FIELD(anchorShardId);\n\tCOPY_NODE_FIELD(taskPlacementList);\n\tCOPY_NODE_FIELD(dependentTaskList);\n\tCOPY_SCALAR_FIELD(partitionId);\n\tCOPY_SCALAR_FIELD(upstreamTaskId);\n\tCOPY_NODE_FIELD(shardInterval);\n\tCOPY_SCALAR_FIELD(assignmentConstrained);\n\tCOPY_SCALAR_FIELD(replicationModel);\n\tCOPY_SCALAR_FIELD(modifyWithSubquery);\n\tCOPY_NODE_FIELD(relationShardList);\n\tCOPY_NODE_FIELD(relationRowLockList);\n\tCOPY_NODE_FIELD(rowValuesLists);\n\tCOPY_SCALAR_FIELD(partiallyLocalOrRemote);\n\tCOPY_SCALAR_FIELD(parametersInQueryStringResolved);\n\tCOPY_SCALAR_FIELD(tupleDest);\n\tCOPY_SCALAR_FIELD(queryCount);\n\tCOPY_SCALAR_FIELD(totalReceivedTupleData);\n\tCOPY_SCALAR_FIELD(fetchedExplainAnalyzePlacementIndex);\n\tCOPY_STRING_FIELD(fetchedExplainAnalyzePlan);\n\tCOPY_SCALAR_FIELD(fetchedExplainAnalyzeExecutionDuration);\n\tCOPY_SCALAR_FIELD(isLocalTableModification);\n\tCOPY_SCALAR_FIELD(cannotBeExecutedInTransaction);\n}\n\n\nvoid\nCopyNodeLocalPlannedStatement(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(LocalPlannedStatement);\n\n\tCOPY_SCALAR_FIELD(shardId);\n\tCOPY_SCALAR_FIELD(localGroupId);\n\tCOPY_NODE_FIELD(localPlan);\n}\n\n\nvoid\nCopyNodeDeferredErrorMessage(COPYFUNC_ARGS)\n{\n\tDECLARE_FROM_AND_NEW_NODE(DeferredErrorMessage);\n\n\tCOPY_SCALAR_FIELD(code);\n\tCOPY_STRING_FIELD(message);\n\tCOPY_STRING_FIELD(detail);\n\tCOPY_STRING_FIELD(hint);\n\tCOPY_STRING_FIELD(filename);\n\tCOPY_SCALAR_FIELD(linenumber);\n\tCOPY_STRING_FIELD(functionname);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/citus_depended_object.c",
    "content": "/*\n * citus_depended_object.c\n *\n * Implements exposed functions related to hiding citus depended objects.\n *\n * Copyright (c) Citus Data, Inc.\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_aggregate.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_amop.h\"\n#include \"catalog/pg_amproc.h\"\n#include \"catalog/pg_attrdef.h\"\n#include \"catalog/pg_attribute.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_enum.h\"\n#include \"catalog/pg_event_trigger.h\"\n#include \"catalog/pg_language.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_opclass.h\"\n#include \"catalog/pg_operator.h\"\n#include \"catalog/pg_opfamily.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_rewrite.h\"\n#include \"catalog/pg_sequence.h\"\n#include \"catalog/pg_statistic.h\"\n#include \"catalog/pg_trigger.h\"\n#include \"catalog/pg_ts_config.h\"\n#include \"catalog/pg_ts_dict.h\"\n#include \"catalog/pg_ts_template.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/parsenodes.h\"\n#include \"parser/parse_type.h\"\n#include \"storage/large_object.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/citus_depended_object.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/shared_library_init.h\"\n\n/*\n * GUC hides any objects, which depends on citus extension, from pg meta class queries,\n * it is intended to be used in vanilla tests to not break postgres test logs\n */\nbool HideCitusDependentObjects = false;\n\nstatic Node * CreateCitusDependentObjectExpr(int pgMetaTableVarno, int pgMetaTableOid);\nstatic List * GetCitusDependedObjectArgs(int pgMetaTableVarno, int pgMetaTableOid);\nstatic bool AlterRoleSetStatementContainsAll(Node *node);\nstatic bool HasDropCommandViolatesOwnership(Node *node);\nstatic bool AnyObjectViolatesOwnership(DropStmt *dropStmt);\n\n/*\n * IsPgLocksTable returns true if RTE is pg_locks table.\n */\nbool\nIsPgLocksTable(RangeTblEntry *rte)\n{\n\tOid pgLocksId = get_relname_relid(\"pg_locks\", get_namespace_oid(\"pg_catalog\", false));\n\treturn rte->relid == pgLocksId;\n}\n\n\n/*\n * SetLocalHideCitusDependentObjectsDisabledWhenAlreadyEnabled disables the GUC HideCitusDependentObjects\n * if only it is enabled for local transaction.\n */\nvoid\nSetLocalHideCitusDependentObjectsDisabledWhenAlreadyEnabled(void)\n{\n\tif (!HideCitusDependentObjects)\n\t{\n\t\treturn;\n\t}\n\n\tset_config_option(\"citus.hide_citus_dependent_objects\", \"false\",\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n}\n\n\n/*\n * SetLocalClientMinMessagesIfRunningPGTests sets client_min_message locally to the given value\n * if EnableUnsupportedFeatureMessages is set to false.\n */\nvoid\nSetLocalClientMinMessagesIfRunningPGTests(int clientMinMessageLevel)\n{\n\tif (EnableUnsupportedFeatureMessages)\n\t{\n\t\treturn;\n\t}\n\n\tconst char *clientMinMessageLevelName = GetClientMinMessageLevelNameForValue(\n\t\tclientMinMessageLevel);\n\n\tset_config_option(\"client_min_messages\", clientMinMessageLevelName,\n\t\t\t\t\t  (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION,\n\t\t\t\t\t  GUC_ACTION_LOCAL, true, 0, false);\n}\n\n\n/*\n * HideCitusDependentObjectsOnQueriesOfPgMetaTables adds a NOT is_citus_depended_object(oid, oid) expr\n * to the quals of meta class RTEs that we are interested in.\n */\nbool\nHideCitusDependentObjectsOnQueriesOfPgMetaTables(Node *node, void *context)\n{\n\tif (!CitusHasBeenLoaded() || !HideCitusDependentObjects || node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\t\tMemoryContext queryContext = GetMemoryChunkContext(query);\n\n\t\t/*\n\t\t * We process the whole rtable rather than visiting individual RangeTblEntry's\n\t\t * in the walker, since we need to know the varno to generate the right\n\t\t * filter.\n\t\t */\n\t\tint varno = 0;\n\t\tRangeTblEntry *rangeTableEntry = NULL;\n\n\t\tforeach_declared_ptr(rangeTableEntry, query->rtable)\n\t\t{\n\t\t\tvarno++;\n\n\t\t\tif (rangeTableEntry->rtekind == RTE_RELATION)\n\t\t\t{\n\t\t\t\t/* make sure the expression is in the right memory context */\n\t\t\t\tMemoryContext originalContext = MemoryContextSwitchTo(queryContext);\n\n\t\t\t\tOid metaTableOid = InvalidOid;\n\n\t\t\t\t/*\n\t\t\t\t * add NOT is_citus_depended_object(oid, oid) to the quals\n\t\t\t\t * of the RTE if it is a pg meta table that we are interested in.\n\t\t\t\t */\n\t\t\t\tswitch (rangeTableEntry->relid)\n\t\t\t\t{\n\t\t\t\t\t/* pg_class */\n\t\t\t\t\tcase RelationRelationId:\n\n\t\t\t\t\t/* pg_proc */\n\t\t\t\t\tcase ProcedureRelationId:\n\n\t\t\t\t\t/* pg_am */\n\t\t\t\t\tcase AccessMethodRelationId:\n\n\t\t\t\t\t/* pg_type */\n\t\t\t\t\tcase TypeRelationId:\n\n\t\t\t\t\t/* pg_enum */\n\t\t\t\t\tcase EnumRelationId:\n\n\t\t\t\t\t/* pg_event_trigger */\n\t\t\t\t\tcase EventTriggerRelationId:\n\n\t\t\t\t\t/* pg_trigger */\n\t\t\t\t\tcase TriggerRelationId:\n\n\t\t\t\t\t/* pg_rewrite */\n\t\t\t\t\tcase RewriteRelationId:\n\n\t\t\t\t\t/* pg_attrdef */\n\t\t\t\t\tcase AttrDefaultRelationId:\n\n\t\t\t\t\t/* pg_constraint */\n\t\t\t\t\tcase ConstraintRelationId:\n\n\t\t\t\t\t/* pg_ts_config */\n\t\t\t\t\tcase TSConfigRelationId:\n\n\t\t\t\t\t/* pg_ts_template */\n\t\t\t\t\tcase TSTemplateRelationId:\n\n\t\t\t\t\t/* pg_ts_dict */\n\t\t\t\t\tcase TSDictionaryRelationId:\n\n\t\t\t\t\t/* pg_language */\n\t\t\t\t\tcase LanguageRelationId:\n\n\t\t\t\t\t/* pg_namespace */\n\t\t\t\t\tcase NamespaceRelationId:\n\n\t\t\t\t\t/* pg_sequence */\n\t\t\t\t\tcase SequenceRelationId:\n\n\t\t\t\t\t/* pg_statistic */\n\t\t\t\t\tcase StatisticRelationId:\n\n\t\t\t\t\t/* pg_attribute */\n\t\t\t\t\tcase AttributeRelationId:\n\n\t\t\t\t\t/* pg_index */\n\t\t\t\t\tcase IndexRelationId:\n\n\t\t\t\t\t/* pg_operator */\n\t\t\t\t\tcase OperatorRelationId:\n\n\t\t\t\t\t/* pg_opclass */\n\t\t\t\t\tcase OperatorClassRelationId:\n\n\t\t\t\t\t/* pg_opfamily */\n\t\t\t\t\tcase OperatorFamilyRelationId:\n\n\t\t\t\t\t/* pg_amop */\n\t\t\t\t\tcase AccessMethodOperatorRelationId:\n\n\t\t\t\t\t/* pg_amproc */\n\t\t\t\t\tcase AccessMethodProcedureRelationId:\n\n\t\t\t\t\t/* pg_aggregate */\n\t\t\t\t\tcase AggregateRelationId:\n\t\t\t\t\t{\n\t\t\t\t\t\tmetaTableOid = rangeTableEntry->relid;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t{\n\t\t\t\t\t\tmetaTableOid = InvalidOid;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (OidIsValid(metaTableOid))\n\t\t\t\t{\n\t\t\t\t\tbool mergeJoinCondition = false;\n#if PG_VERSION_NUM >= PG_VERSION_17\n\n\t\t\t\t\t/*\n\t\t\t\t\t * In Postgres 17, the query tree has a specific field for the merge condition.\n\t\t\t\t\t * So we shouldn't modify the jointree, but rather the mergeJoinCondition here\n\t\t\t\t\t * Relevant PG17 commit: 0294df2f1\n\t\t\t\t\t */\n\t\t\t\t\tmergeJoinCondition = query->mergeJoinCondition;\n#endif\n\n\t\t\t\t\t/*\n\t\t\t\t\t * We found a valid pg meta class in query,\n\t\t\t\t\t * so we assert below conditions.\n\t\t\t\t\t */\n\t\t\t\t\tAssert(mergeJoinCondition ||\n\t\t\t\t\t\t   (query->jointree != NULL &&\n\t\t\t\t\t\t\tquery->jointree->fromlist != NULL));\n\n\t\t\t\t\tNode *citusDependentObjExpr =\n\t\t\t\t\t\tCreateCitusDependentObjectExpr(varno, metaTableOid);\n\n\t\t\t\t\t/*\n\t\t\t\t\t * We do not use security quals because a postgres vanilla test fails\n\t\t\t\t\t * with a change of order for its result.\n\t\t\t\t\t */\n\t\t\t\t\tif (!mergeJoinCondition)\n\t\t\t\t\t{\n\t\t\t\t\t\tquery->jointree->quals = make_and_qual(\n\t\t\t\t\t\t\tquery->jointree->quals, citusDependentObjExpr);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n#if PG_VERSION_NUM >= PG_VERSION_17\n\t\t\t\t\t\tquery->mergeJoinCondition = make_and_qual(\n\t\t\t\t\t\t\tquery->mergeJoinCondition, citusDependentObjExpr);\n#endif\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tMemoryContextSwitchTo(originalContext);\n\t\t\t}\n\t\t}\n\n\t\treturn query_tree_walker((Query *) node,\n\t\t\t\t\t\t\t\t HideCitusDependentObjectsOnQueriesOfPgMetaTables,\n\t\t\t\t\t\t\t\t context, 0);\n\t}\n\n\treturn expression_tree_walker(node, HideCitusDependentObjectsOnQueriesOfPgMetaTables,\n\t\t\t\t\t\t\t\t  context);\n}\n\n\n/*\n * CreateCitusDependentObjectExpr constructs an expression of the form:\n * NOT pg_catalog.is_citus_depended_object(oid, oid)\n */\nstatic Node *\nCreateCitusDependentObjectExpr(int pgMetaTableVarno, int pgMetaTableOid)\n{\n\t/* build the call to read_intermediate_result */\n\tFuncExpr *funcExpr = makeNode(FuncExpr);\n\tfuncExpr->funcid = CitusDependentObjectFuncId();\n\tfuncExpr->funcretset = false;\n\tfuncExpr->funcvariadic = false;\n\tfuncExpr->funcformat = 0;\n\tfuncExpr->funccollid = 0;\n\tfuncExpr->inputcollid = 0;\n\tfuncExpr->location = -1;\n\tfuncExpr->args = GetCitusDependedObjectArgs(pgMetaTableVarno, pgMetaTableOid);\n\n\tBoolExpr *notExpr = makeNode(BoolExpr);\n\tnotExpr->boolop = NOT_EXPR;\n\tnotExpr->args = list_make1(funcExpr);\n\tnotExpr->location = -1;\n\n\treturn (Node *) notExpr;\n}\n\n\n/*\n * GetCitusDependedObjectArgs returns func arguments for pg_catalog.is_citus_depended_object\n */\nstatic List *\nGetCitusDependedObjectArgs(int pgMetaTableVarno, int pgMetaTableOid)\n{\n\t/*\n\t * set attribute number for the oid, which we are insterest in, inside pg meta tables.\n\t * We are accessing the 1. col(their own oid or their relation's oid) to get the related\n\t * object's oid for all of the pg meta tables except pg_enum and pg_index. For pg_enum,\n\t * class, we access its 2. col(its type's oid) to see if its type depends on citus,\n\t * so it does. For pg_index, we access its 2. col (its relation's oid) to see if its relation\n\t * depends on citus, so it does.\n\t */\n\tAttrNumber oidAttNum = 1;\n\tif (pgMetaTableOid == EnumRelationId || pgMetaTableOid == IndexRelationId)\n\t{\n\t\toidAttNum = 2;\n\t}\n\n\t/* create const for meta table oid */\n\tConst *metaTableOidConst = makeConst(OIDOID, -1, InvalidOid, sizeof(Oid),\n\t\t\t\t\t\t\t\t\t\t ObjectIdGetDatum(pgMetaTableOid),\n\t\t\t\t\t\t\t\t\t\t false, true);\n\n\t/*\n\t * create a var for the oid that we are interested in,\n\t * col type should be regproc for pg_aggregate table; else oid\n\t */\n\tOid varType = (pgMetaTableOid == AggregateRelationId) ? REGPROCOID : OIDOID;\n\tVar *oidVar = makeVar(pgMetaTableVarno, oidAttNum,\n\t\t\t\t\t\t  varType, -1, InvalidOid, 0);\n\n\treturn list_make2((Node *) metaTableOidConst, (Node *) oidVar);\n}\n\n\n/*\n * DistOpsValidityState returns validation state for given dist ops.\n */\nDistOpsValidationState\nDistOpsValidityState(Node *node, const DistributeObjectOps *ops)\n{\n\tif (ops && ops->operationType == DIST_OPS_CREATE)\n\t{\n\t\t/*\n\t\t * We should beware of qualifying the CREATE statement too early.\n\t\t */\n\t\tif (nodeTag(node) == T_CreateDomainStmt)\n\t\t{\n\t\t\t/*\n\t\t\t * Create Domain statements should be qualified after local creation\n\t\t\t * because in case of an error in creation, we don't want to print\n\t\t\t * the error with the qualified name, as that would differ with\n\t\t\t * vanilla Postgres error output.\n\t\t\t */\n\t\t\treturn ShouldQualifyAfterLocalCreation;\n\t\t}\n\n\t\t/*\n\t\t * We should not validate CREATE statements because no address exists\n\t\t * here yet.\n\t\t */\n\t\treturn NoAddressResolutionRequired;\n\t}\n\telse if (AlterRoleSetStatementContainsAll(node))\n\t{\n\t\t/*\n\t\t * We should not validate 'ALTER ROLE ALL [SET|UNSET] because for the role ALL\n\t\t * AlterRoleSetStmtObjectAddress returns an invalid address even though it should not.\n\t\t */\n\t\treturn NoAddressResolutionRequired;\n\t}\n\telse if (HasDropCommandViolatesOwnership(node))\n\t{\n\t\t/*\n\t\t * found object with an invalid ownership, PG will complain if there is any object\n\t\t * with an invalid ownership.\n\t\t */\n\t\treturn HasObjectWithInvalidOwnership;\n\t}\n\n\tif (ops && ops->address)\n\t{\n\t\tbool missingOk = true;\n\t\tbool isPostprocess = false;\n\t\tList *objectAddresses = ops->address(node, missingOk, isPostprocess);\n\t\tObjectAddress *objectAddress = NULL;\n\t\tforeach_declared_ptr(objectAddress, objectAddresses)\n\t\t{\n\t\t\tif (OidIsValid(objectAddress->objectId))\n\t\t\t{\n\t\t\t\t/* found one valid object */\n\t\t\t\treturn HasAtLeastOneValidObject;\n\t\t\t}\n\t\t}\n\n\t\t/* no valid objects */\n\t\treturn HasNoneValidObject;\n\t}\n\telse\n\t{\n\t\t/* if the object doesn't have address defined, we donot validate */\n\t\treturn NoAddressResolutionRequired;\n\t}\n}\n\n\n/*\n * DistOpsInValidState returns true if given state is valid to execute\n * preprocess and qualify steps.\n */\nbool\nDistOpsInValidState(DistOpsValidationState distOpsValidationState)\n{\n\treturn distOpsValidationState == HasAtLeastOneValidObject || distOpsValidationState ==\n\t\t   NoAddressResolutionRequired;\n}\n\n\n/*\n * AlterRoleSetStatementContainsAll returns true if the statement is a\n * ALTER ROLE ALL (SET / RESET).\n */\nstatic bool\nAlterRoleSetStatementContainsAll(Node *node)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (nodeTag(node) == T_AlterRoleSetStmt)\n\t{\n\t\t/* rolespec is null for the role 'ALL' */\n\t\tAlterRoleSetStmt *alterRoleSetStmt = castNode(AlterRoleSetStmt, node);\n\n\t\treturn alterRoleSetStmt->role == NULL;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * HasDropCommandViolatesOwnership returns true if any object in the given\n * statement violates object ownership.\n *\n * Currently there is only one test which fails due to object ownership.\n * The command that is failing is DROP. If in the future we hit other\n * commands like this, we should expand this function.\n */\nstatic bool\nHasDropCommandViolatesOwnership(Node *node)\n{\n\tif (!IsA(node, DropStmt))\n\t{\n\t\treturn false;\n\t}\n\n\tDropStmt *dropStmt = castNode(DropStmt, node);\n\tif (AnyObjectViolatesOwnership(dropStmt))\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * AnyObjectViolatesOwnership return true if given object in stmt violates ownership.\n */\nstatic bool\nAnyObjectViolatesOwnership(DropStmt *dropStmt)\n{\n\tbool hasOwnershipViolation = false;\n\tObjectAddress objectAddress = { 0 };\n\tvolatile Relation relation = NULL;\n\tObjectType objectType = dropStmt->removeType;\n\tbool missingOk = dropStmt->missing_ok;\n\n\tMemoryContext savedContext = CurrentMemoryContext;\n\tResourceOwner savedOwner = CurrentResourceOwner;\n\tBeginInternalSubTransaction(NULL);\n\tMemoryContextSwitchTo(savedContext);\n\n\tPG_TRY();\n\t{\n\t\tNode *object = NULL;\n\t\tforeach_declared_ptr(object, dropStmt->objects)\n\t\t{\n\t\t\tRelation rel = NULL;\n\t\t\tobjectAddress = get_object_address(objectType, object,\n\t\t\t\t\t\t\t\t\t\t\t   &rel, AccessShareLock, missingOk);\n\n\t\t\t/*\n\t\t\t * The object relation is qualified with volatile and its value is obtained from\n\t\t\t * get_object_address(). Unless we can qualify the corresponding parameter of\n\t\t\t * get_object_address() with volatile (this is a function defined in PostgreSQL),\n\t\t\t * we cannot get rid of this assignment.\n\t\t\t */\n\t\t\trelation = rel;\n\n\t\t\tif (OidIsValid(objectAddress.objectId))\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * if object violates ownership, check_object_ownership will throw error.\n\t\t\t\t */\n\t\t\t\tcheck_object_ownership(GetUserId(),\n\t\t\t\t\t\t\t\t\t   objectType,\n\t\t\t\t\t\t\t\t\t   objectAddress,\n\t\t\t\t\t\t\t\t\t   object, relation);\n\t\t\t}\n\n\t\t\tif (relation != NULL)\n\t\t\t{\n\t\t\t\trelation_close(relation, NoLock);\n\t\t\t\trelation = NULL;\n\t\t\t}\n\t\t}\n\n\t\tReleaseCurrentSubTransaction();\n\t\tMemoryContextSwitchTo(savedContext);\n\t\tCurrentResourceOwner = savedOwner;\n\t}\n\tPG_CATCH();\n\t{\n\t\tMemoryContextSwitchTo(savedContext);\n\t\tErrorData *edata = CopyErrorData();\n\t\tFlushErrorState();\n\n\t\thasOwnershipViolation = true;\n\t\tif (relation != NULL)\n\t\t{\n\t\t\trelation_close(relation, NoLock);\n\t\t\trelation = NULL;\n\t\t}\n\t\tRollbackAndReleaseCurrentSubTransaction();\n\t\tMemoryContextSwitchTo(savedContext);\n\t\tCurrentResourceOwner = savedOwner;\n\n\t\t/* Rethrow error with LOG_SERVER_ONLY to prevent log to be sent to client */\n\t\tedata->elevel = LOG_SERVER_ONLY;\n\t\tThrowErrorData(edata);\n\t}\n\tPG_END_TRY();\n\n\treturn hasOwnershipViolation;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/citus_nodefuncs.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_nodefuncs.c\n *\t  Helper functions for dealing with nodes\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/pg_type.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_router_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n\nstatic const char *CitusNodeTagNamesD[] = {\n\t\"MultiNode\",\n\t\"MultiTreeRoot\",\n\t\"MultiProject\",\n\t\"MultiCollect\",\n\t\"MultiSelect\",\n\t\"MultiTable\",\n\t\"MultiJoin\",\n\t\"MultiPartition\",\n\t\"MultiCartesianProduct\",\n\t\"MultiExtendedOp\",\n\t\"Job\",\n\t\"MapMergeJob\",\n\t\"DistributedPlan\",\n\t\"DistributedSubPlan\",\n\t\"UsedDistributedSubPlan\",\n\t\"Task\",\n\t\"LocalPlannedStatement\",\n\t\"ShardInterval\",\n\t\"ShardPlacement\",\n\t\"RelationShard\",\n\t\"RelationRowLock\",\n\t\"DeferredErrorMessage\",\n\t\"GroupShardPlacement\",\n\t\"TableDDLCommand\"\n};\n\nconst char **CitusNodeTagNames = CitusNodeTagNamesD;\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(citus_extradata_container);\n\n\n/*\n * SetRangeTblExtraData adds additional data to a RTE, overwriting previous\n * values, if present.\n *\n * The data is stored as RTE_FUNCTION type RTE of a special\n * citus_extradata_container function, with the extra data serialized into the\n * function arguments. That works, because these RTEs aren't used by Postgres\n * to any significant degree, and Citus' variant of ruleutils.c knows how to\n * deal with these extended RTEs. Note that rte->eref needs to be set prior\n * to calling SetRangeTblExtraData to ensure the funccolcount can be set\n * correctly.\n *\n * NB: If used for postgres defined RTEKinds, fields specific to that RTEKind\n * will not be handled by out/readfuncs.c. For the current uses that's ok.\n */\nvoid\nSetRangeTblExtraData(RangeTblEntry *rte, CitusRTEKind rteKind, char *fragmentSchemaName,\n\t\t\t\t\t char *fragmentTableName, List *tableIdList, List *funcColumnNames,\n\t\t\t\t\t List *funcColumnTypes, List *funcColumnTypeMods,\n\t\t\t\t\t List *funcCollations)\n{\n\tAssert(rte->eref);\n\n\t/* store RTE kind as a plain int4 */\n\tConst *rteKindData = makeNode(Const);\n\trteKindData->consttype = INT4OID;\n\trteKindData->constlen = 4;\n\trteKindData->constvalue = Int32GetDatum(rteKind);\n\trteKindData->constbyval = true;\n\trteKindData->constisnull = false;\n\trteKindData->location = -1;\n\n\t/* store the fragment schema as a cstring */\n\tConst *fragmentSchemaData = makeNode(Const);\n\tfragmentSchemaData->consttype = CSTRINGOID;\n\tfragmentSchemaData->constlen = -2;\n\tfragmentSchemaData->constvalue = CStringGetDatum(fragmentSchemaName);\n\tfragmentSchemaData->constbyval = false;\n\tfragmentSchemaData->constisnull = fragmentSchemaName == NULL;\n\tfragmentSchemaData->location = -1;\n\n\t/* store the fragment name as a cstring */\n\tConst *fragmentTableData = makeNode(Const);\n\tfragmentTableData->consttype = CSTRINGOID;\n\tfragmentTableData->constlen = -2;\n\tfragmentTableData->constvalue = CStringGetDatum(fragmentTableName);\n\tfragmentTableData->constbyval = false;\n\tfragmentTableData->constisnull = fragmentTableName == NULL;\n\tfragmentTableData->location = -1;\n\n\t/* store the table id list as an array of integers: FIXME */\n\tConst *tableIdListData = makeNode(Const);\n\ttableIdListData->consttype = CSTRINGOID;\n\ttableIdListData->constbyval = false;\n\ttableIdListData->constlen = -2;\n\ttableIdListData->location = -1;\n\n\t/* serialize tableIdList to a string, seems simplest that way */\n\tif (tableIdList != NIL)\n\t{\n\t\tchar *serializedList = nodeToString(tableIdList);\n\t\ttableIdListData->constisnull = false;\n\t\ttableIdListData->constvalue = CStringGetDatum(serializedList);\n\t}\n\telse\n\t{\n\t\ttableIdListData->constisnull = true;\n\t}\n\n\t/* create function expression to store our faux arguments in */\n\tFuncExpr *fauxFuncExpr = makeNode(FuncExpr);\n\tfauxFuncExpr->funcid = CitusExtraDataContainerFuncId();\n\tfauxFuncExpr->funcresulttype = RECORDOID;\n\tfauxFuncExpr->funcretset = true;\n\tfauxFuncExpr->location = -1;\n\tfauxFuncExpr->args = list_make4(rteKindData, fragmentSchemaData,\n\t\t\t\t\t\t\t\t\tfragmentTableData, tableIdListData);\n\n\tRangeTblFunction *fauxFunction = makeNode(RangeTblFunction);\n\tfauxFunction->funcexpr = (Node *) fauxFuncExpr;\n\n\t/* set the column count to pass ruleutils checks, not used elsewhere */\n\tif (rte->relid != 0)\n\t{\n\t\tRelation rel = RelationIdGetRelation(rte->relid);\n\t\tfauxFunction->funccolcount = RelationGetNumberOfAttributes(rel);\n\t\tRelationClose(rel);\n\t}\n\telse\n\t{\n\t\tfauxFunction->funccolcount = list_length(rte->eref->colnames);\n\t}\n\n\tfauxFunction->funccolnames = funcColumnNames;\n\tfauxFunction->funccoltypes = funcColumnTypes;\n\tfauxFunction->funccoltypmods = funcColumnTypeMods;\n\tfauxFunction->funccolcollations = funcCollations;\n\n\trte->rtekind = RTE_FUNCTION;\n\trte->functions = list_make1(fauxFunction);\n}\n\n\n/*\n * ExtractRangeTblExtraData extracts extra data stored for a range table entry\n * that previously has been stored with\n * Set/ModifyRangeTblExtraData. Parameters can be NULL if unintersting. It is\n * valid to use the function on a RTE without extra data.\n */\nvoid\nExtractRangeTblExtraData(RangeTblEntry *rte, CitusRTEKind *rteKind,\n\t\t\t\t\t\t char **fragmentSchemaName, char **fragmentTableName,\n\t\t\t\t\t\t List **tableIdList)\n{\n\t/* set base rte kind first, so this can be used for 'non-extended' RTEs as well */\n\tif (rteKind != NULL)\n\t{\n\t\t*rteKind = (CitusRTEKind) rte->rtekind;\n\t}\n\n\t/* reset values of optionally-present fields, will later be overwritten, if present */\n\tif (fragmentSchemaName != NULL)\n\t{\n\t\t*fragmentSchemaName = NULL;\n\t}\n\n\tif (fragmentTableName != NULL)\n\t{\n\t\t*fragmentTableName = NULL;\n\t}\n\n\tif (tableIdList != NULL)\n\t{\n\t\t*tableIdList = NIL;\n\t}\n\n\n\t/* only function RTEs have our special extra data */\n\tif (rte->rtekind != RTE_FUNCTION)\n\t{\n\t\treturn;\n\t}\n\n\t/* we only ever generate one argument */\n\tif (list_length(rte->functions) != 1)\n\t{\n\t\treturn;\n\t}\n\n\t/* should pretty much always be a FuncExpr, but be liberal in what we expect... */\n\tRangeTblFunction *fauxFunction = linitial(rte->functions);\n\tif (!IsA(fauxFunction->funcexpr, FuncExpr))\n\t{\n\t\treturn;\n\t}\n\n\tFuncExpr *fauxFuncExpr = (FuncExpr *) fauxFunction->funcexpr;\n\n\t/*\n\t * There will never be a range table entry with this function id, but for\n\t * the purpose of this file.\n\t */\n\tif (fauxFuncExpr->funcid != CitusExtraDataContainerFuncId())\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * Extra data for rtes is stored in the function arguments. The first\n\t * argument stores the rtekind, second fragmentSchemaName, third\n\t * fragmentTableName, fourth tableIdList.\n\t */\n\tif (list_length(fauxFuncExpr->args) != 4)\n\t{\n\t\tereport(ERROR, (errmsg(\"unexpected number of function arguments to \"\n\t\t\t\t\t\t\t   \"citus_extradata_container\")));\n\t\treturn;\n\t}\n\n\t/* extract rteKind */\n\tConst *tmpConst = (Const *) linitial(fauxFuncExpr->args);\n\tAssert(IsA(tmpConst, Const));\n\tAssert(tmpConst->consttype == INT4OID);\n\tif (rteKind != NULL)\n\t{\n\t\t*rteKind = DatumGetInt32(tmpConst->constvalue);\n\t}\n\n\t/* extract fragmentSchemaName */\n\ttmpConst = (Const *) lsecond(fauxFuncExpr->args);\n\tAssert(IsA(tmpConst, Const));\n\tAssert(tmpConst->consttype == CSTRINGOID);\n\tif (fragmentSchemaName != NULL && !tmpConst->constisnull)\n\t{\n\t\t*fragmentSchemaName = DatumGetCString(tmpConst->constvalue);\n\t}\n\n\t/* extract fragmentTableName */\n\ttmpConst = (Const *) lthird(fauxFuncExpr->args);\n\tAssert(IsA(tmpConst, Const));\n\tAssert(tmpConst->consttype == CSTRINGOID);\n\tif (fragmentTableName != NULL && !tmpConst->constisnull)\n\t{\n\t\t*fragmentTableName = DatumGetCString(tmpConst->constvalue);\n\t}\n\n\t/* extract tableIdList, stored as a serialized integer list */\n\ttmpConst = (Const *) lfourth(fauxFuncExpr->args);\n\tAssert(IsA(tmpConst, Const));\n\tAssert(tmpConst->consttype == CSTRINGOID);\n\tif (tableIdList != NULL && !tmpConst->constisnull)\n\t{\n\t\tNode *deserializedList = stringToNode(DatumGetCString(tmpConst->constvalue));\n\t\tAssert(IsA(deserializedList, IntList));\n\n\t\t*tableIdList = (List *) deserializedList;\n\t}\n}\n\n\n/*\n * ModifyRangeTblExtraData sets the RTE extra data fields for the passed\n * fields, leaving the current values in place for the ones not specified.\n *\n * rteKind has to be specified, fragmentSchemaName, fragmentTableName,\n * tableIdList can be set to NULL/NIL respectively to leave the current values\n * in-place.\n */\nvoid\nModifyRangeTblExtraData(RangeTblEntry *rte, CitusRTEKind rteKind,\n\t\t\t\t\t\tchar *fragmentSchemaName, char *fragmentTableName,\n\t\t\t\t\t\tList *tableIdList)\n{\n\t/* load existing values for the arguments not specifying a new value */\n\tExtractRangeTblExtraData(rte, NULL,\n\t\t\t\t\t\t\t fragmentSchemaName == NULL ? &fragmentSchemaName : NULL,\n\t\t\t\t\t\t\t fragmentTableName == NULL ? &fragmentTableName : NULL,\n\t\t\t\t\t\t\t tableIdList == NIL ? &tableIdList : NULL);\n\n\tSetRangeTblExtraData(rte, rteKind,\n\t\t\t\t\t\t fragmentSchemaName, fragmentTableName,\n\t\t\t\t\t\t tableIdList, NIL, NIL, NIL, NIL);\n}\n\n\n/* GetRangeTblKind returns rtekind of a RTE, be it an extended one or not. */\nCitusRTEKind\nGetRangeTblKind(RangeTblEntry *rte)\n{\n\tCitusRTEKind rteKind = CITUS_RTE_RELATION /* invalid */;\n\n\tswitch (rte->rtekind)\n\t{\n\t\t/* directly rtekind if it's not possibly an extended RTE */\n\t\tcase RTE_TABLEFUNC:\n\t\tcase RTE_NAMEDTUPLESTORE:\n\t\tcase RTE_RELATION:\n\t\tcase RTE_SUBQUERY:\n\t\tcase RTE_JOIN:\n\t\tcase RTE_VALUES:\n\t\tcase RTE_CTE:\n\t\tcase RTE_RESULT:\n\t\t{\n\t\t\trteKind = (CitusRTEKind) rte->rtekind;\n\t\t\tbreak;\n\t\t}\n\n\t\t#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t\t/* new in PG18: GROUP RTE, just map it straight through */\n\t\tcase RTE_GROUP:\n\t\t{\n\t\t\trteKind = (CitusRTEKind) rte->rtekind;\n\t\t\tbreak;\n\t\t}\n\n\t\t#endif\n\t\tcase RTE_FUNCTION:\n\t\t{\n\t\t\t/*\n\t\t\t * Extract extra data - correct even if a plain RTE_FUNCTION, not\n\t\t\t * an extended one, ExtractRangeTblExtraData handles that case\n\t\t\t * transparently.\n\t\t\t */\n\t\t\tExtractRangeTblExtraData(rte, &rteKind, NULL, NULL, NULL);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn rteKind;\n}\n\n\n/*\n * citus_extradata_container is a placeholder function to store information\n * needed by Citus in plain postgres node trees. Executor and other hooks\n * should always intercept statements containing calls to this function. It's\n * not actually SQL callable by the user because of an INTERNAL argument.\n */\nDatum\ncitus_extradata_container(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"not supposed to get here, did you cheat?\")));\n\tPG_RETURN_NULL();\n}\n\n\nstatic void\nCopyUnsupportedCitusNode(struct ExtensibleNode *newnode,\n\t\t\t\t\t\t const struct ExtensibleNode *oldnode)\n{\n\tereport(ERROR, (errmsg(\"not implemented\")));\n}\n\n\nstatic bool\nEqualUnsupportedCitusNode(const struct ExtensibleNode *a,\n\t\t\t\t\t\t  const struct ExtensibleNode *b)\n{\n\tereport(ERROR, (errmsg(\"not implemented\")));\n}\n\n\n/* *INDENT-OFF* */\n#define DEFINE_NODE_METHODS(type) \\\n\t{ \\\n\t\t#type, \\\n\t\tsizeof(type), \\\n\t\tCopyNode##type, \\\n\t\tEqualUnsupportedCitusNode, \\\n\t\tOut##type, \\\n\t\tReadUnsupportedCitusNode \\\n\t}\n\n#define DEFINE_NODE_METHODS_NO_READ(type) \\\n\t{ \\\n\t\t#type, \\\n\t\tsizeof(type), \\\n\t\tCopyUnsupportedCitusNode, \\\n\t\tEqualUnsupportedCitusNode, \\\n\t\tOut##type, \\\n\t\tReadUnsupportedCitusNode \\\n\t}\n\n\n/* *INDENT-ON* */\nconst ExtensibleNodeMethods nodeMethods[] =\n{\n\tDEFINE_NODE_METHODS(DistributedPlan),\n\tDEFINE_NODE_METHODS(DistributedSubPlan),\n\tDEFINE_NODE_METHODS(UsedDistributedSubPlan),\n\tDEFINE_NODE_METHODS(Job),\n\tDEFINE_NODE_METHODS(ShardInterval),\n\tDEFINE_NODE_METHODS(MapMergeJob),\n\tDEFINE_NODE_METHODS(ShardPlacement),\n\tDEFINE_NODE_METHODS(RelationShard),\n\tDEFINE_NODE_METHODS(RelationRowLock),\n\tDEFINE_NODE_METHODS(Task),\n\tDEFINE_NODE_METHODS(LocalPlannedStatement),\n\tDEFINE_NODE_METHODS(DeferredErrorMessage),\n\tDEFINE_NODE_METHODS(GroupShardPlacement),\n\n\t/* nodes with only output support */\n\tDEFINE_NODE_METHODS_NO_READ(MultiNode),\n\tDEFINE_NODE_METHODS_NO_READ(MultiTreeRoot),\n\tDEFINE_NODE_METHODS_NO_READ(MultiProject),\n\tDEFINE_NODE_METHODS_NO_READ(MultiCollect),\n\tDEFINE_NODE_METHODS_NO_READ(MultiSelect),\n\tDEFINE_NODE_METHODS_NO_READ(MultiTable),\n\tDEFINE_NODE_METHODS_NO_READ(MultiJoin),\n\tDEFINE_NODE_METHODS_NO_READ(MultiPartition),\n\tDEFINE_NODE_METHODS_NO_READ(MultiCartesianProduct),\n\tDEFINE_NODE_METHODS_NO_READ(MultiExtendedOp),\n\tDEFINE_NODE_METHODS_NO_READ(TableDDLCommand)\n};\n\nvoid\nRegisterNodes(void)\n{\n\tStaticAssertExpr(lengthof(nodeMethods) == lengthof(CitusNodeTagNamesD),\n\t\t\t\t\t \"number of node methods and names do not match\");\n\n\tfor (int off = 0; off < lengthof(nodeMethods); off++)\n\t{\n\t\tRegisterExtensibleNodeMethods(&nodeMethods[off]);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/citus_outfuncs.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_outfuncs.c\n *\t  Output functions for Citus tree nodes.\n *\n * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group\n * Portions Copyright (c) 1994, Regents of the University of California\n * Portions Copyright (c) Citus Data, Inc.\n *\n * NOTES\n *\t  This is a wrapper around postgres' nodeToString() that additionally\n *\t  supports Citus node types.\n *\n *    Keep as closely aligned with the upstream version as possible.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"pg_version_constants.h\"\n\n#include <ctype.h>\n\n#include \"distributed/citus_nodefuncs.h\"\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/plannodes.h\"\n#include \"nodes/pathnodes.h\"\n#include \"utils/datum.h\"\n\n\n/*\n * Macros to simplify output of different kinds of fields.  Use these\n * wherever possible to reduce the chance for silly typos.  Note that these\n * hard-wire conventions about the names of the local variables in an Out\n * routine.\n */\n\n/* Store const reference to raw input node in local named 'node' */\n#define WRITE_LOCALS(nodeTypeName) \\\n\t\tconst nodeTypeName *node = (const nodeTypeName *) raw_node\n\n/* Write the label for the node type */\n#define WRITE_NODE_TYPE(nodelabel) \\\n\t(void) 0\n\n/* Write an integer field (anything written as \":fldname %d\") */\n#define WRITE_INT_FIELD(fldname) \\\n\tappendStringInfo(str, \" :\" CppAsString(fldname) \" %d\", node->fldname)\n\n/* Write an 64-bit integer field (anything written as \":fldname %d\") */\n#define WRITE_INT64_FIELD(fldname) \\\n\tappendStringInfo(str, \" :\" CppAsString(fldname) \" \" INT64_FORMAT, node->fldname)\n\n\n/* Write an unsigned integer field (anything written as \":fldname %u\") */\n#define WRITE_UINT_FIELD(fldname) \\\n\tappendStringInfo(str, \" :\" CppAsString(fldname) \" %u\", node->fldname)\n\n/* XXX: Citus: Write an unsigned 64-bit integer field */\n#define WRITE_UINT64_FIELD(fldname) \\\n\tappendStringInfo(str, \" :\" CppAsString(fldname) \" \" UINT64_FORMAT, node->fldname)\n\n/* Write an OID field (don't hard-wire assumption that OID is same as uint) */\n#define WRITE_OID_FIELD(fldname) \\\n\tappendStringInfo(str, \" :\" CppAsString(fldname) \" %u\", node->fldname)\n\n/* Write a char field (ie, one ascii character) */\n#define WRITE_CHAR_FIELD(fldname) \\\n\tappendStringInfo(str, \" :\" CppAsString(fldname) \" %c\", node->fldname)\n\n/* Write an enumerated-type field as an integer code */\n#define WRITE_ENUM_FIELD(fldname, enumtype) \\\n\tappendStringInfo(str, \" :\" CppAsString(fldname) \" %d\", \\\n\t\t\t\t\t (int) node->fldname)\n\n/* Write a float field --- caller must give format to define precision */\n#define WRITE_FLOAT_FIELD(fldname,format) \\\n\tappendStringInfo(str, \" :\" CppAsString(fldname) \" \" format, node->fldname)\n\n/* Write a boolean field */\n#define WRITE_BOOL_FIELD(fldname) \\\n\tappendStringInfo(str, \" :\" CppAsString(fldname) \" %s\", \\\n\t\t\t\t\t booltostr(node->fldname))\n\n/* Write a character-string (possibly NULL) field */\n#define WRITE_STRING_FIELD(fldname) \\\n\t(appendStringInfo(str, \" :\" CppAsString(fldname) \" \"), \\\n\t outToken(str, node->fldname))\n\n/* Write a parse location field (actually same as INT case) */\n#define WRITE_LOCATION_FIELD(fldname) \\\n\tappendStringInfo(str, \" :\" CppAsString(fldname) \" %d\", node->fldname)\n\n/* Write a Node field */\n#define WRITE_NODE_FIELD(fldname) \\\n\t(appendStringInfo(str, \" :\" CppAsString(fldname) \" \"), \\\n\t outNode(str, node->fldname))\n\n/* Write a bitmapset field */\n#define WRITE_BITMAPSET_FIELD(fldname) \\\n\t(appendStringInfo(str, \" :\" CppAsString(fldname) \" \"), \\\n\t _outBitmapset(str, node->fldname))\n\n#define WRITE_CUSTOM_FIELD(fldname, fldvalue) \\\n\t(appendStringInfo(str, \" :\" CppAsString(fldname) \" \"), \\\n\tappendStringInfoString(str, (fldvalue)))\n\n\n/* Write an integer array (anything written as \":fldname (%d, %d\") */\n#define WRITE_INT_ARRAY(fldname, count) \\\n\tappendStringInfo(str, \" :\" CppAsString(fldname) \" (\"); \\\n\t{ \\\n\t\tint i;\\\n\t\tfor (i = 0; i < count; i++) \\\n\t\t{ \\\n\t\t\tif (i > 0) \\\n\t\t\t{ \\\n\t\t\t\tappendStringInfo(str, \", \"); \\\n\t\t\t} \\\n\t\t\tappendStringInfo(str, \"%d\", node->fldname[i]); \\\n\t\t}\\\n\t}\\\n\tappendStringInfo(str, \")\")\n\n\n/* Write an enum array (anything written as \":fldname (%d, %d\") */\n#define WRITE_ENUM_ARRAY(fldname, count) WRITE_INT_ARRAY(fldname, count)\n\n\n#define booltostr(x)  ((x) ? \"true\" : \"false\")\nstatic void WriteTaskQuery(OUTFUNC_ARGS);\n\n/*****************************************************************************\n *\tOutput routines for Citus node types\n *****************************************************************************/\n\nstatic void\nOutMultiUnaryNodeFields(StringInfo str, const MultiUnaryNode *node)\n{\n\tWRITE_NODE_FIELD(childNode);\n}\n\n\nstatic void\nOutMultiBinaryNodeFields(StringInfo str, const MultiBinaryNode *node)\n{\n\tWRITE_NODE_FIELD(leftChildNode);\n\tWRITE_NODE_FIELD(rightChildNode);\n}\n\nvoid\nOutMultiNode(OUTFUNC_ARGS)\n{\n\tWRITE_NODE_TYPE(\"MULTINODE\");\n}\n\n\nvoid\nOutMultiTreeRoot(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(MultiTreeRoot);\n\n\tWRITE_NODE_TYPE(\"MULTITREEROOT\");\n\n\tOutMultiUnaryNodeFields(str, (const MultiUnaryNode *) node);\n}\n\n\nvoid\nOutDistributedPlan(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(DistributedPlan);\n\n\tWRITE_NODE_TYPE(\"DISTRIBUTEDPLAN\");\n\n\tWRITE_UINT64_FIELD(planId);\n\tWRITE_ENUM_FIELD(modLevel, RowModifyLevel);\n\tWRITE_BOOL_FIELD(expectResults);\n\n\tWRITE_NODE_FIELD(workerJob);\n\tWRITE_NODE_FIELD(combineQuery);\n\tWRITE_UINT64_FIELD(queryId);\n\tWRITE_NODE_FIELD(relationIdList);\n\tWRITE_OID_FIELD(targetRelationId);\n\tWRITE_NODE_FIELD(modifyQueryViaCoordinatorOrRepartition);\n\tWRITE_NODE_FIELD(selectPlanForModifyViaCoordinatorOrRepartition);\n\tWRITE_ENUM_FIELD(modifyWithSelectMethod, ModifyWithSelectMethod);\n\tWRITE_STRING_FIELD(intermediateResultIdPrefix);\n\n\tWRITE_NODE_FIELD(subPlanList);\n\tWRITE_NODE_FIELD(usedSubPlanNodeList);\n\tWRITE_BOOL_FIELD(fastPathRouterPlan);\n\tWRITE_UINT_FIELD(numberOfTimesExecuted);\n\n\tWRITE_NODE_FIELD(planningError);\n\tWRITE_INT_FIELD(sourceResultRepartitionColumnIndex);\n}\n\n\nvoid\nOutDistributedSubPlan(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(DistributedSubPlan);\n\n\tWRITE_NODE_TYPE(\"DISTRIBUTEDSUBPLAN\");\n\n\tWRITE_UINT_FIELD(subPlanId);\n\tWRITE_NODE_FIELD(plan);\n\tWRITE_UINT64_FIELD(bytesSentPerWorker);\n\tWRITE_INT_FIELD(remoteWorkerCount);\n\tWRITE_FLOAT_FIELD(durationMillisecs, \"%.2f\");\n\tWRITE_BOOL_FIELD(writeLocalFile);\n\n\tappendStringInfoString(str, \" totalExplainOutput [\");\n\tfor (int i = 0; i < node->numTasksOutput; i++)\n\t{\n\t\tconst SubPlanExplainOutputData *e = &node->totalExplainOutput[i];\n\n\t\t/* skip empty slots */\n\t\tif (e->explainOutput == NULL &&\n\t\t\te->executionDuration == 0\n\t\t\t\t&& e->totalReceivedTupleData == 0)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (i > 0)\n\t\t{\n\t\t\tappendStringInfoChar(str, ' ');\n\t\t}\n\n\t\tappendStringInfoChar(str, '(');\n\n\t\t/* string pointer – prints quoted or NULL */\n\t\tWRITE_STRING_FIELD(totalExplainOutput[i].explainOutput);\n\n\t\t/* double field */\n\t\tWRITE_FLOAT_FIELD(totalExplainOutput[i].executionDuration, \"%.2f\");\n\n\t\t/* 64-bit unsigned – use the uint64 macro */\n\t\tWRITE_UINT64_FIELD(totalExplainOutput[i].totalReceivedTupleData);\n\n\t\tappendStringInfoChar(str, ')');\n\t}\n\n\tappendStringInfoChar(str, ']');\n\n\tWRITE_INT_FIELD(numTasksOutput);\n\tWRITE_FLOAT_FIELD(ntuples, \"%.2f\");\n\n}\n\nvoid\nOutUsedDistributedSubPlan(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(UsedDistributedSubPlan);\n\n\tWRITE_NODE_TYPE(\"USEDDISTRIBUTEDSUBPLAN\");\n\n\tWRITE_STRING_FIELD(subPlanId);\n\tWRITE_ENUM_FIELD(accessType, SubPlanAccessType);\n}\n\n\nvoid\nOutMultiProject(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(MultiProject);\n\tWRITE_NODE_TYPE(\"MULTIPROJECT\");\n\n\tWRITE_NODE_FIELD(columnList);\n\n\tOutMultiUnaryNodeFields(str, (const MultiUnaryNode *) node);\n}\n\n\nvoid\nOutMultiCollect(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(MultiCollect);\n\tWRITE_NODE_TYPE(\"MULTICOLLECT\");\n\n\tOutMultiUnaryNodeFields(str, (const MultiUnaryNode *) node);\n}\n\n\nvoid\nOutMultiSelect(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(MultiSelect);\n\tWRITE_NODE_TYPE(\"MULTISELECT\");\n\n\tWRITE_NODE_FIELD(selectClauseList);\n\n\tOutMultiUnaryNodeFields(str, (const MultiUnaryNode *) node);\n}\n\n\nvoid\nOutMultiTable(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(MultiTable);\n\tWRITE_NODE_TYPE(\"MULTITABLE\");\n\n\tWRITE_OID_FIELD(relationId);\n\tWRITE_INT_FIELD(rangeTableId);\n\n\tOutMultiUnaryNodeFields(str, (const MultiUnaryNode *) node);\n}\n\n\nvoid\nOutMultiJoin(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(MultiJoin);\n\tWRITE_NODE_TYPE(\"MULTIJOIN\");\n\n\tWRITE_NODE_FIELD(joinClauseList);\n\tWRITE_ENUM_FIELD(joinRuleType, JoinRuleType);\n\tWRITE_ENUM_FIELD(joinType, JoinType);\n\n\tOutMultiBinaryNodeFields(str, (const MultiBinaryNode *) node);\n}\n\n\nvoid\nOutMultiPartition(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(MultiPartition);\n\tWRITE_NODE_TYPE(\"MULTIPARTITION\");\n\n\tWRITE_NODE_FIELD(partitionColumn);\n\n\tOutMultiUnaryNodeFields(str, (const MultiUnaryNode *) node);\n}\n\n\nvoid\nOutMultiCartesianProduct(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(MultiCartesianProduct);\n\tWRITE_NODE_TYPE(\"MULTICARTESIANPRODUCT\");\n\n\tOutMultiBinaryNodeFields(str, (const MultiBinaryNode *) node);\n}\n\n\n\n\nvoid\nOutMultiExtendedOp(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(MultiExtendedOp);\n\tWRITE_NODE_TYPE(\"MULTIEXTENDEDOP\");\n\n\tWRITE_NODE_FIELD(targetList);\n\tWRITE_NODE_FIELD(groupClauseList);\n\tWRITE_NODE_FIELD(sortClauseList);\n\tWRITE_NODE_FIELD(limitCount);\n\tWRITE_NODE_FIELD(limitOffset);\n\tWRITE_ENUM_FIELD(limitOption, LimitOption);\n\tWRITE_NODE_FIELD(havingQual);\n\tWRITE_BOOL_FIELD(hasDistinctOn);\n\tWRITE_NODE_FIELD(distinctClause);\n\tWRITE_BOOL_FIELD(hasWindowFuncs);\n\tWRITE_BOOL_FIELD(onlyPushableWindowFunctions);\n\tWRITE_NODE_FIELD(windowClause);\n\n\tOutMultiUnaryNodeFields(str, (const MultiUnaryNode *) node);\n}\n\nstatic void\nOutJobFields(StringInfo str, const Job *node)\n{\n\tWRITE_UINT64_FIELD(jobId);\n\tWRITE_NODE_FIELD(jobQuery);\n\tWRITE_NODE_FIELD(taskList);\n\tWRITE_NODE_FIELD(dependentJobList);\n\tWRITE_BOOL_FIELD(subqueryPushdown);\n\tWRITE_BOOL_FIELD(requiresCoordinatorEvaluation);\n\tWRITE_BOOL_FIELD(deferredPruning);\n\tWRITE_NODE_FIELD(partitionKeyValue);\n\tWRITE_NODE_FIELD(localPlannedStatements);\n\tWRITE_BOOL_FIELD(parametersInJobQueryResolved);\n}\n\n\nvoid\nOutJob(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(Job);\n\tWRITE_NODE_TYPE(\"JOB\");\n\n\tOutJobFields(str, node);\n}\n\n\nvoid\nOutShardInterval(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(ShardInterval);\n\tWRITE_NODE_TYPE(\"SHARDINTERVAL\");\n\n\tWRITE_OID_FIELD(relationId);\n\tWRITE_CHAR_FIELD(storageType);\n\tWRITE_OID_FIELD(valueTypeId);\n\tWRITE_INT_FIELD(valueTypeLen);\n\tWRITE_BOOL_FIELD(valueByVal);\n\tWRITE_BOOL_FIELD(minValueExists);\n\tWRITE_BOOL_FIELD(maxValueExists);\n\n\tappendStringInfoString(str, \" :minValue \");\n\tif (!node->minValueExists)\n\t\tappendStringInfoString(str, \"<>\");\n\telse\n\t\toutDatum(str, node->minValue, node->valueTypeLen, node->valueByVal);\n\n\tappendStringInfoString(str, \" :maxValue \");\n\tif (!node->maxValueExists)\n\t\tappendStringInfoString(str, \"<>\");\n\telse\n\t\toutDatum(str, node->maxValue, node->valueTypeLen, node->valueByVal);\n\n\tWRITE_UINT64_FIELD(shardId);\n\tWRITE_INT_FIELD(shardIndex);\n}\n\n\nvoid\nOutMapMergeJob(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(MapMergeJob);\n\tint arrayLength = node->sortedShardIntervalArrayLength;\n\tint i;\n\n\tWRITE_NODE_TYPE(\"MAPMERGEJOB\");\n\n\tOutJobFields(str, (Job *) node);\n\tWRITE_ENUM_FIELD(partitionType, PartitionType);\n\tWRITE_NODE_FIELD(partitionColumn);\n\tWRITE_UINT_FIELD(partitionCount);\n\tWRITE_INT_FIELD(sortedShardIntervalArrayLength);\n\n\tfor (i = 0; i < arrayLength; ++i)\n\t{\n\t\toutNode(str, node->sortedShardIntervalArray[i]);\n\t}\n\n\tWRITE_NODE_FIELD(mapTaskList);\n\tWRITE_NODE_FIELD(mergeTaskList);\n}\n\n\nvoid\nOutShardPlacement(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(ShardPlacement);\n\tWRITE_NODE_TYPE(\"SHARDPLACEMENT\");\n\n\tWRITE_UINT64_FIELD(placementId);\n\tWRITE_UINT64_FIELD(shardId);\n\tWRITE_UINT64_FIELD(shardLength);\n\tWRITE_INT_FIELD(groupId);\n\tWRITE_STRING_FIELD(nodeName);\n\tWRITE_UINT_FIELD(nodePort);\n\tWRITE_UINT_FIELD(nodeId);\n\t/* so we can deal with 0 */\n\tWRITE_INT_FIELD(partitionMethod);\n\tWRITE_UINT_FIELD(colocationGroupId);\n\tWRITE_UINT_FIELD(representativeValue);\n}\n\n\nvoid\nOutGroupShardPlacement(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(GroupShardPlacement);\n\tWRITE_NODE_TYPE(\"GROUPSHARDPLACEMENT\");\n\n\tWRITE_UINT64_FIELD(placementId);\n\tWRITE_UINT64_FIELD(shardId);\n\tWRITE_UINT64_FIELD(shardLength);\n\tWRITE_INT_FIELD(groupId);\n}\n\n\nvoid\nOutRelationShard(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(RelationShard);\n\tWRITE_NODE_TYPE(\"RELATIONSHARD\");\n\n\tWRITE_OID_FIELD(relationId);\n\tWRITE_UINT64_FIELD(shardId);\n}\n\n\nvoid\nOutRelationRowLock(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(RelationRowLock);\n\tWRITE_NODE_TYPE(\"RELATIONROWLOCK\");\n\n\tWRITE_OID_FIELD(relationId);\n\tWRITE_ENUM_FIELD(rowLockStrength, LockClauseStrength);\n}\n\nstatic void WriteTaskQuery(OUTFUNC_ARGS) {\n\tWRITE_LOCALS(Task);\n\n\tWRITE_ENUM_FIELD(taskQuery.queryType, TaskQueryType);\n\n\tswitch (node->taskQuery.queryType)\n\t{\n\t\tcase TASK_QUERY_TEXT:\n\t\t{\n\t\t\tWRITE_STRING_FIELD(taskQuery.data.queryStringLazy);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase TASK_QUERY_OBJECT:\n\t\t{\n\t\t\tWRITE_NODE_FIELD(taskQuery.data.jobQueryReferenceForLazyDeparsing);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase TASK_QUERY_TEXT_LIST:\n\t\t{\n\t\t\tWRITE_NODE_FIELD(taskQuery.data.queryStringList);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid\nOutTask(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(Task);\n\tWRITE_NODE_TYPE(\"TASK\");\n\n\tWRITE_ENUM_FIELD(taskType, TaskType);\n\tWRITE_UINT64_FIELD(jobId);\n\tWRITE_UINT_FIELD(taskId);\n\tWriteTaskQuery(str, raw_node);\n\tWRITE_OID_FIELD(anchorDistributedTableId);\n\tWRITE_UINT64_FIELD(anchorShardId);\n\tWRITE_NODE_FIELD(taskPlacementList);\n\tWRITE_NODE_FIELD(dependentTaskList);\n\tWRITE_UINT_FIELD(partitionId);\n\tWRITE_UINT_FIELD(upstreamTaskId);\n\tWRITE_NODE_FIELD(shardInterval);\n\tWRITE_BOOL_FIELD(assignmentConstrained);\n\tWRITE_CHAR_FIELD(replicationModel);\n\tWRITE_BOOL_FIELD(modifyWithSubquery);\n\tWRITE_NODE_FIELD(relationShardList);\n\tWRITE_NODE_FIELD(relationRowLockList);\n\tWRITE_NODE_FIELD(rowValuesLists);\n\tWRITE_BOOL_FIELD(partiallyLocalOrRemote);\n\tWRITE_BOOL_FIELD(parametersInQueryStringResolved);\n\tWRITE_INT_FIELD(queryCount);\n\tWRITE_UINT64_FIELD(totalReceivedTupleData);\n\tWRITE_INT_FIELD(fetchedExplainAnalyzePlacementIndex);\n\tWRITE_STRING_FIELD(fetchedExplainAnalyzePlan);\n\tWRITE_FLOAT_FIELD(fetchedExplainAnalyzeExecutionDuration, \"%.2f\");\n\tWRITE_BOOL_FIELD(isLocalTableModification);\n\tWRITE_BOOL_FIELD(cannotBeExecutedInTransaction);\n}\n\n\nvoid\nOutLocalPlannedStatement(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(LocalPlannedStatement);\n\n\tWRITE_NODE_TYPE(\"LocalPlannedStatement\");\n\n\tWRITE_UINT64_FIELD(shardId);\n\tWRITE_UINT_FIELD(localGroupId);\n\tWRITE_NODE_FIELD(localPlan);\n}\n\nvoid\nOutDeferredErrorMessage(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(DeferredErrorMessage);\n\tWRITE_NODE_TYPE(\"DEFERREDERRORMESSAGE\");\n\n\tWRITE_INT_FIELD(code);\n\tWRITE_STRING_FIELD(message);\n\tWRITE_STRING_FIELD(detail);\n\tWRITE_STRING_FIELD(hint);\n\tWRITE_STRING_FIELD(filename);\n\tWRITE_INT_FIELD(linenumber);\n\tWRITE_STRING_FIELD(functionname);\n}\n\n\nvoid\nOutTableDDLCommand(OUTFUNC_ARGS)\n{\n\tWRITE_LOCALS(TableDDLCommand);\n\tWRITE_NODE_TYPE(\"TableDDLCommand\");\n\n\tswitch (node->type)\n\t{\n\t\tcase TABLE_DDL_COMMAND_STRING:\n\t\t{\n\t\t\tWRITE_STRING_FIELD(commandStr);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase TABLE_DDL_COMMAND_FUNCTION:\n\t\t{\n\t\t\tchar *example = node->function.function(node->function.context);\n\t\t\tWRITE_CUSTOM_FIELD(function, example);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/citus_readfuncs.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_readfuncs.c\n *    Citus specific node functions\n *\n * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group\n * Portions Copyright (c) 1994, Regents of the University of California\n * Portions Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"distributed/citus_nodefuncs.h\"\n\nvoid\nReadUnsupportedCitusNode(READFUNC_ARGS)\n{\n\tereport(ERROR, (errmsg(\"not implemented\")));\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/citus_safe_lib.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * safe_lib.c\n *\n * This file contains all SafeXXXX helper functions that we implement to\n * replace missing xxxx_s functions implemented by safestringlib. It also\n * contains a constraint handler for use in both our SafeXXX and safestringlib\n * its xxxx_s functions.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <limits.h>\n\n#include \"postgres.h\"\n\n#include \"safe_lib.h\"\n\n#include \"lib/stringinfo.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n\n\n/*\n * ereport_constraint_handler is a constraint handler that calls ereport. A\n * constraint handler is called whenever an error occurs in any of the\n * safestringlib xxxx_s functions or our SafeXXXX functions.\n *\n * More info on constraint handlers can be found here:\n * https://en.cppreference.com/w/c/error/set_constraint_handler_s\n */\nvoid\nereport_constraint_handler(const char *message,\n\t\t\t\t\t\t   void *pointer,\n\t\t\t\t\t\t   errno_t error)\n{\n\tif (message && error)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg(\n\t\t\t\t\t\t\t\"Memory constraint error: %s (errno %d)\", message, error)));\n\t}\n\telse if (message)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg(\n\t\t\t\t\t\t\t\"Memory constraint error: %s\", message)));\n\t}\n\telse if (error)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg(\n\t\t\t\t\t\t\t\"Unknown function failed with memory constraint error (errno %d)\",\n\t\t\t\t\t\t\terror)));\n\t}\n\telse\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg(\n\t\t\t\t\t\t\t\"Unknown function failed with memory constraint error\")));\n\t}\n}\n\n\n/*\n * SafeStringToInt64 converts a string containing a number to a int64. When it\n * fails it calls ereport.\n *\n * The different error cases are inspired by\n * https://stackoverflow.com/a/26083517/2570866\n */\nint64\nSafeStringToInt64(const char *str)\n{\n\tchar *endptr;\n\terrno = 0;\n\tlong long number = strtoll(str, &endptr, 10);\n\n\tif (str == endptr)\n\t{\n\t\tereport(ERROR, (errmsg(\"Error parsing %s as int64, no digits found\\n\", str)));\n\t}\n\telse if ((errno == ERANGE && number == LLONG_MIN) || number < INT64_MIN)\n\t{\n\t\tereport(ERROR, (errmsg(\"Error parsing %s as int64, underflow occurred\\n\", str)));\n\t}\n\telse if ((errno == ERANGE && number == LLONG_MAX) || number > INT64_MAX)\n\t{\n\t\tereport(ERROR, (errmsg(\"Error parsing %s as int64, overflow occurred\\n\", str)));\n\t}\n\telse if (errno == EINVAL)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"Error parsing %s as int64, base contains unsupported value\\n\",\n\t\t\t\t\t\t\tstr)));\n\t}\n\telse if (errno != 0 && number == 0)\n\t{\n\t\tint err = errno;\n\t\tereport(ERROR, (errmsg(\"Error parsing %s as int64, errno %d\\n\", str, err)));\n\t}\n\telse if (errno == 0 && str && *endptr != '\\0')\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"Error parsing %s as int64, aditional characters remain after int64\\n\",\n\t\t\t\t\t\t\tstr)));\n\t}\n\treturn number;\n}\n\n\n/*\n * SafeStringToInt32 converts a string containing a number to a int32. When it\n * fails it calls ereport.\n *\n * The different error cases are inspired by\n * https://stackoverflow.com/a/26083517/2570866\n */\nint32\nSafeStringToInt32(const char *str)\n{\n\tchar *endptr;\n\terrno = 0;\n\tlong number = strtol(str, &endptr, 10);\n\n\tif (str == endptr)\n\t{\n\t\tereport(ERROR, (errmsg(\"Error parsing %s as int32, no digits found\\n\", str)));\n\t}\n\telse if ((errno == ERANGE && number == LONG_MIN) || number < INT32_MIN)\n\t{\n\t\tereport(ERROR, (errmsg(\"Error parsing %s as int32, underflow occurred\\n\", str)));\n\t}\n\telse if ((errno == ERANGE && number == LONG_MAX) || number > INT32_MAX)\n\t{\n\t\tereport(ERROR, (errmsg(\"Error parsing %s as int32, overflow occurred\\n\", str)));\n\t}\n\telse if (errno == EINVAL)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"Error parsing %s as int32, base contains unsupported value\\n\",\n\t\t\t\t\t\t\tstr)));\n\t}\n\telse if (errno != 0 && number == 0)\n\t{\n\t\tint err = errno;\n\t\tereport(ERROR, (errmsg(\"Error parsing %s as int32, errno %d\\n\", str, err)));\n\t}\n\telse if (errno == 0 && str && *endptr != '\\0')\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"Error parsing %s as int32, aditional characters remain after int32\\n\",\n\t\t\t\t\t\t\tstr)));\n\t}\n\treturn number;\n}\n\n\n/*\n * SafeStringToUint64 converts a string containing a number to a uint64. When it\n * fails it calls ereport.\n *\n * The different error cases are inspired by\n * https://stackoverflow.com/a/26083517/2570866\n */\nuint64\nSafeStringToUint64(const char *str)\n{\n\tchar *endptr;\n\terrno = 0;\n\tunsigned long long number = strtoull(str, &endptr, 10);\n\n\tif (str == endptr)\n\t{\n\t\tereport(ERROR, (errmsg(\"Error parsing %s as uint64, no digits found\\n\", str)));\n\t}\n\telse if ((errno == ERANGE && number == ULLONG_MAX) || number > UINT64_MAX)\n\t{\n\t\tereport(ERROR, (errmsg(\"Error parsing %s as uint64, overflow occurred\\n\", str)));\n\t}\n\telse if (errno == EINVAL)\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"Error parsing %s as uint64, base contains unsupported value\\n\",\n\t\t\t\t\t\t\tstr)));\n\t}\n\telse if (errno != 0 && number == 0)\n\t{\n\t\tint err = errno;\n\t\tereport(ERROR, (errmsg(\"Error parsing %s as uint64, errno %d\\n\", str, err)));\n\t}\n\telse if (errno == 0 && str && *endptr != '\\0')\n\t{\n\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\"Error parsing %s as uint64, aditional characters remain after uint64\\n\",\n\t\t\t\t\t\t\tstr)));\n\t}\n\treturn number;\n}\n\n\n/*\n * SafeQsort is the non reentrant version of qsort (qsort vs qsort_r), but it\n * does the input checks required for qsort_s:\n *  1. count or size is greater than RSIZE_MAX\n *  2. ptr or comp is a null pointer (unless count is zero)\n * source: https://en.cppreference.com/w/c/algorithm/qsort\n *\n * When it hits these errors it calls the ereport_constraint_handler.\n *\n * NOTE: this functions calls pg_qsort instead of stdlib qsort.\n */\nvoid\nSafeQsort(void *ptr, rsize_t count, rsize_t size,\n\t\t  int (*comp)(const void *, const void *))\n{\n\tif (count > RSIZE_MAX_MEM)\n\t{\n\t\tereport_constraint_handler(\"SafeQsort: count exceeds max\",\n\t\t\t\t\t\t\t\t   NULL, ESLEMAX);\n\t}\n\n\tif (size > RSIZE_MAX_MEM)\n\t{\n\t\tereport_constraint_handler(\"SafeQsort: size exceeds max\",\n\t\t\t\t\t\t\t\t   NULL, ESLEMAX);\n\t}\n\tif (size != 0)\n\t{\n\t\tif (ptr == NULL)\n\t\t{\n\t\t\tereport_constraint_handler(\"SafeQsort: ptr is NULL\", NULL, ESNULLP);\n\t\t}\n\t\tif (comp == NULL)\n\t\t{\n\t\t\tereport_constraint_handler(\"SafeQsort: comp is NULL\", NULL, ESNULLP);\n\t\t}\n\t}\n\tpg_qsort(ptr, count, size, comp);\n}\n\n\n/*\n * SafeBsearch is a non reentrant version of bsearch, but it does the\n * input checks required for bsearch_s:\n *  1. count or size is greater than RSIZE_MAX\n *  2. key, ptr or comp is a null pointer (unless count is zero)\n * source: https://en.cppreference.com/w/c/algorithm/bsearch\n *\n * When it hits these errors it calls the ereport_constraint_handler.\n *\n * NOTE: this functions calls pg_qsort instead of stdlib qsort.\n */\nvoid *\nSafeBsearch(const void *key, const void *ptr, rsize_t count, rsize_t size,\n\t\t\tint (*comp)(const void *, const void *))\n{\n\tif (count > RSIZE_MAX_MEM)\n\t{\n\t\tereport_constraint_handler(\"SafeBsearch: count exceeds max\",\n\t\t\t\t\t\t\t\t   NULL, ESLEMAX);\n\t}\n\n\tif (size > RSIZE_MAX_MEM)\n\t{\n\t\tereport_constraint_handler(\"SafeBsearch: size exceeds max\",\n\t\t\t\t\t\t\t\t   NULL, ESLEMAX);\n\t}\n\tif (size != 0)\n\t{\n\t\tif (key == NULL)\n\t\t{\n\t\t\tereport_constraint_handler(\"SafeBsearch: key is NULL\", NULL, ESNULLP);\n\t\t}\n\t\tif (ptr == NULL)\n\t\t{\n\t\t\tereport_constraint_handler(\"SafeBsearch: ptr is NULL\", NULL, ESNULLP);\n\t\t}\n\t\tif (comp == NULL)\n\t\t{\n\t\t\tereport_constraint_handler(\"SafeBsearch: comp is NULL\", NULL, ESNULLP);\n\t\t}\n\t}\n\n\t/*\n\t * Explanation of IGNORE-BANNED:\n\t * bsearch is safe to use here since we check the same thing bsearch_s\n\t * does. We cannot use bsearch_s as a replacement, since it's not available\n\t * in safestringlib.\n\t */\n\treturn bsearch(key, ptr, count, size, comp); /* IGNORE-BANNED */\n}\n\n\n/*\n * SafeSnprintf is a safer replacement for snprintf, which is needed since\n * safestringlib doesn't implement snprintf_s.\n *\n * The required failure modes of snprint_s are as follows (in parentheses if\n * this implements it and how):\n * 1. the conversion specifier %n is present in format (yes, %n is not\n *    supported by pg_vsnprintf)\n * 2. any of the arguments corresponding to %s is a null pointer (half, checked\n *    in postgres when asserts are enabled)\n * 3. format or buffer is a null pointer (yes, checked by this function)\n * 4. bufsz is zero or greater than RSIZE_MAX (yes, checked by this function)\n * 5. encoding errors occur in any of string and character conversion\n *    specifiers (no clue what postgres does in this case)\n * source: https://en.cppreference.com/w/c/io/fprintf\n */\nint\nSafeSnprintf(char *restrict buffer, rsize_t bufsz, const char *restrict format, ...)\n{\n\t/* failure mode 3 */\n\tif (buffer == NULL)\n\t{\n\t\tereport_constraint_handler(\"SafeSnprintf: buffer is NULL\", NULL, ESNULLP);\n\t}\n\tif (format == NULL)\n\t{\n\t\tereport_constraint_handler(\"SafeSnprintf: format is NULL\", NULL, ESNULLP);\n\t}\n\n\t/* failure mode 4 */\n\tif (bufsz == 0)\n\t{\n\t\tereport_constraint_handler(\"SafeSnprintf: bufsz is 0\",\n\t\t\t\t\t\t\t\t   NULL, ESZEROL);\n\t}\n\n\tif (bufsz > RSIZE_MAX_STR)\n\t{\n\t\tereport_constraint_handler(\"SafeSnprintf: bufsz exceeds max\",\n\t\t\t\t\t\t\t\t   NULL, ESLEMAX);\n\t}\n\n\tva_list args;\n\n\tva_start(args, format);\n\tint result = pg_vsnprintf(buffer, bufsz, format, args);\n\tva_end(args);\n\treturn result;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/citus_version.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_version.c\n *\n * This file contains functions for displaying the Citus version string\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"utils/builtins.h\"\n\n#include \"citus_version.h\"\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(citus_version);\n\n/* GIT_VERSION is passed in as a compiler flag during builds that have git installed */\n#ifdef GIT_VERSION\n#define GIT_REF \" gitref: \" GIT_VERSION\n#else\n#define GIT_REF\n#endif\n\nDatum\ncitus_version(PG_FUNCTION_ARGS)\n{\n\tPG_RETURN_TEXT_P(cstring_to_text(CITUS_VERSION_STR GIT_REF));\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/clonenode_utils.c",
    "content": "#include <arpa/inet.h>\n#include <netdb.h>\n#include <netinet/in.h>\n#include <sys/socket.h>\n\n#include \"postgres.h\"\n\n#include \"utils/fmgrprotos.h\"\n#include \"utils/pg_lsn.h\"\n\n#include \"distributed/argutils.h\"\n#include \"distributed/clonenode_utils.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/shard_rebalancer.h\"\n\n/*\n * GetReplicationLag calculates the replication lag between the primary and replica nodes.\n * It returns the lag in bytes.\n */\nint64\nGetReplicationLag(WorkerNode *primaryWorkerNode, WorkerNode *replicaWorkerNode)\n{\n\t/* Input validation */\n\tif (primaryWorkerNode == NULL || replicaWorkerNode == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"primary or replica worker node is NULL\")));\n\t}\n\n#if PG_VERSION_NUM >= 100000\n\tconst char *primary_lsn_query = \"SELECT pg_current_wal_lsn()\";\n\tconst char *replica_lsn_query = \"SELECT pg_last_wal_replay_lsn()\";\n#else\n\tconst char *primary_lsn_query = \"SELECT pg_current_xlog_location()\";\n\tconst char *replica_lsn_query = \"SELECT pg_last_xlog_replay_location()\";\n#endif\n\n\tint connectionFlag = 0;\n\tMultiConnection *primaryConnection = GetNodeConnection(connectionFlag,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   primaryWorkerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   primaryWorkerNode->workerPort);\n\tif (PQstatus(primaryConnection->pgConn) != CONNECTION_OK)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"cannot connect to primary node %s:%d to fetch replication status\",\n\t\t\t\t\t\t\tprimaryWorkerNode->workerName, primaryWorkerNode->\n\t\t\t\t\t\t\tworkerPort)));\n\t}\n\tMultiConnection *replicaConnection = GetNodeConnection(connectionFlag,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   replicaWorkerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   replicaWorkerNode->workerPort);\n\n\tif (PQstatus(replicaConnection->pgConn) != CONNECTION_OK)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"cannot connect to clone node %s:%d to fetch replication status\",\n\t\t\t\t\t\t\treplicaWorkerNode->workerName, replicaWorkerNode->\n\t\t\t\t\t\t\tworkerPort)));\n\t}\n\n\tint primaryResultCode = SendRemoteCommand(primaryConnection, primary_lsn_query);\n\tif (primaryResultCode == 0)\n\t{\n\t\tReportConnectionError(primaryConnection, ERROR);\n\t}\n\n\tPGresult *primaryResult = GetRemoteCommandResult(primaryConnection, true);\n\tif (!IsResponseOK(primaryResult))\n\t{\n\t\tReportResultError(primaryConnection, primaryResult, ERROR);\n\t}\n\n\tint replicaResultCode = SendRemoteCommand(replicaConnection, replica_lsn_query);\n\tif (replicaResultCode == 0)\n\t{\n\t\tReportConnectionError(replicaConnection, ERROR);\n\t}\n\tPGresult *replicaResult = GetRemoteCommandResult(replicaConnection, true);\n\tif (!IsResponseOK(replicaResult))\n\t{\n\t\tReportResultError(replicaConnection, replicaResult, ERROR);\n\t}\n\n\n\tList *primaryLsnList = ReadFirstColumnAsText(primaryResult);\n\tif (list_length(primaryLsnList) != 1)\n\t{\n\t\tPQclear(primaryResult);\n\t\tClearResults(primaryConnection, true);\n\t\tCloseConnection(primaryConnection);\n\t\tPQclear(replicaResult);\n\t\tClearResults(replicaConnection, true);\n\t\tCloseConnection(replicaConnection);\n\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\"cannot parse primary LSN result from %s:%d\",\n\t\t\t\t\t\t\t   primaryWorkerNode->workerName,\n\t\t\t\t\t\t\t   primaryWorkerNode->workerPort),\n\t\t\t\t\t\terrdetail(\"Expected exactly one row with LSN value\")));\n\t}\n\tStringInfo primaryLsnQueryResInfo = (StringInfo) linitial(primaryLsnList);\n\tchar *primary_lsn_str = primaryLsnQueryResInfo->data;\n\n\tList *replicaLsnList = ReadFirstColumnAsText(replicaResult);\n\tif (list_length(replicaLsnList) != 1)\n\t{\n\t\tPQclear(primaryResult);\n\t\tClearResults(primaryConnection, true);\n\t\tCloseConnection(primaryConnection);\n\t\tPQclear(replicaResult);\n\t\tClearResults(replicaConnection, true);\n\t\tCloseConnection(replicaConnection);\n\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\"cannot parse clone LSN result from %s:%d\",\n\t\t\t\t\t\t\t   replicaWorkerNode->workerName,\n\t\t\t\t\t\t\t   replicaWorkerNode->workerPort),\n\t\t\t\t\t\terrdetail(\"Expected exactly one row with LSN value\")));\n\t}\n\tStringInfo replicaLsnQueryResInfo = (StringInfo) linitial(replicaLsnList);\n\tchar *replica_lsn_str = replicaLsnQueryResInfo->data;\n\n\tint64 primary_lsn = DatumGetLSN(DirectFunctionCall1(pg_lsn_in, CStringGetDatum(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tprimary_lsn_str)));\n\tint64 replica_lsn = DatumGetLSN(DirectFunctionCall1(pg_lsn_in, CStringGetDatum(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treplica_lsn_str)));\n\n\tint64 lag_bytes = primary_lsn - replica_lsn;\n\n\tPQclear(primaryResult);\n\tForgetResults(primaryConnection);\n\tCloseConnection(primaryConnection);\n\n\tPQclear(replicaResult);\n\tForgetResults(replicaConnection);\n\tCloseConnection(replicaConnection);\n\n\tereport(DEBUG2, (errmsg(\n\t\t\t\t\t\t \"successfully measured replication lag: primary LSN %s, clone LSN %s\",\n\t\t\t\t\t\t primary_lsn_str, replica_lsn_str)));\n\tereport(DEBUG1, (errmsg(\"replication lag between %s:%d and %s:%d is %ld bytes\",\n\t\t\t\t\t\t\tprimaryWorkerNode->workerName, primaryWorkerNode->workerPort,\n\t\t\t\t\t\t\treplicaWorkerNode->workerName, replicaWorkerNode->workerPort,\n\t\t\t\t\t\t\tlag_bytes)));\n\treturn lag_bytes;\n}\n\n\n/*\n * EnsureValidCloneMode verifies that a clone node has a valid replication\n * relationship with the specified primary node.\n *\n * This function performs several critical checks:\n * 1. Validates that the clone is actually connected to and replicating from\n *    the specified primary node\n * 2. Ensures the clone is not configured as a synchronous replica, which\n *    would block 2PC commits on the primary when the clone gets promoted\n * 3. Verifies the replication connection is active and healthy\n *\n * The function connects to the primary node and queries pg_stat_replication\n * to find the clone's replication slot. It resolves hostnames to IP addresses\n * for robust matching since PostgreSQL may report different address formats.\n *\n * Parameters:\n *   primaryWorkerNode - The primary node that should be sending replication data\n *   cloneHostname - Hostname/IP of the clone node to verify\n *   clonePort - Port of the clone node to verify\n *   operation - Description of the operation being performed (for error messages)\n *\n * Throws ERROR if:\n *   - Primary or clone parameters are invalid\n *   - Cannot connect to the primary node\n *   - Clone is not found in the primary's replication slots\n *   - Clone is configured as a synchronous replica\n *   - Replication connection is not active\n */\nvoid\nEnsureValidCloneMode(WorkerNode *primaryWorkerNode,\n\t\t\t\t\t char *cloneHostname, int clonePort, char *operation)\n{\n\tAssert(operation != NULL);\n\n\tif (primaryWorkerNode == NULL || cloneHostname == NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"primary or clone worker node is NULL\")));\n\t}\n\n\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t \"checking replication relationship between primary %s:%d and clone %s:%d\",\n\t\t\t\t\t\t primaryWorkerNode->workerName, primaryWorkerNode->workerPort,\n\t\t\t\t\t\t cloneHostname, clonePort)));\n\n\t/* Connect to primary node to check replication status */\n\tint connectionFlag = 0;\n\tMultiConnection *primaryConnection = GetNodeConnection(connectionFlag,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   primaryWorkerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   primaryWorkerNode->workerPort);\n\tif (PQstatus(primaryConnection->pgConn) != CONNECTION_OK)\n\t{\n\t\tReportConnectionError(primaryConnection, ERROR);\n\t}\n\n\t/* Build query to check if clone is connected and get its sync state */\n\tStringInfo replicationCheckQuery = makeStringInfo();\n\n\t/* First, try to resolve the hostname to IP address for more robust matching */\n\tchar *resolvedIP = NULL;\n\tstruct addrinfo hints, *result, *rp;\n\n\tmemset(&hints, 0, sizeof(hints));\n\thints.ai_family = AF_UNSPEC;     /* Allow IPv4 or IPv6 */\n\thints.ai_socktype = SOCK_STREAM; /* TCP socket */\n\thints.ai_flags = AI_PASSIVE;     /* For wildcard IP address */\n\n\tint getaddrinfo_result = getaddrinfo(cloneHostname, NULL, &hints, &result);\n\tif (getaddrinfo_result == 0)\n\t{\n\t\t/* Get the first resolved IP address */\n\t\tfor (rp = result; rp != NULL; rp = rp->ai_next)\n\t\t{\n\t\t\tif (rp->ai_family == AF_INET)\n\t\t\t{\n\t\t\t\t/* IPv4 */\n\t\t\t\tstruct sockaddr_in *addr_in = (struct sockaddr_in *) rp->ai_addr;\n\t\t\t\tresolvedIP = palloc(INET_ADDRSTRLEN);\n\t\t\t\tinet_ntop(AF_INET, &(addr_in->sin_addr), resolvedIP, INET_ADDRSTRLEN);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (rp->ai_family == AF_INET6)\n\t\t\t{\n\t\t\t\t/* IPv6 */\n\t\t\t\tstruct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *) rp->ai_addr;\n\t\t\t\tresolvedIP = palloc(INET6_ADDRSTRLEN);\n\t\t\t\tinet_ntop(AF_INET6, &(addr_in6->sin6_addr), resolvedIP, INET6_ADDRSTRLEN);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tfreeaddrinfo(result);\n\t}\n\n\tereport(NOTICE, (errmsg(\"checking replication status of clone node %s:%d\",\n\t\t\t\t\t\t\tcloneHostname,\n\t\t\t\t\t\t\tclonePort)));\n\n\t/* Build query to check if clone is connected and get its sync state */\n\n\t/* We check multiple fields to handle different scenarios:\n\t * 1. application_name - if it's set to the node name\n\t * 2. client_hostname - if it's the hostname\n\t * 3. client_addr - if it's the IP address (most reliable)\n\t */\n\tif (resolvedIP != NULL)\n\t{\n\t\tappendStringInfo(replicationCheckQuery,\n\t\t\t\t\t\t \"SELECT sync_state, state FROM pg_stat_replication WHERE \"\n\t\t\t\t\t\t \"application_name = '%s' OR \"\n\t\t\t\t\t\t \"client_hostname = '%s' OR \"\n\t\t\t\t\t\t \"client_addr = '%s'\",\n\t\t\t\t\t\t cloneHostname,\n\t\t\t\t\t\t cloneHostname,\n\t\t\t\t\t\t resolvedIP);\n\t\tpfree(resolvedIP);\n\t}\n\telse\n\t{\n\t\t/* Fallback to hostname-only check if IP resolution fails */\n\t\tappendStringInfo(replicationCheckQuery,\n\t\t\t\t\t\t \"SELECT sync_state, state FROM pg_stat_replication WHERE \"\n\t\t\t\t\t\t \"application_name = '%s' OR \"\n\t\t\t\t\t\t \"client_hostname = '%s'\",\n\t\t\t\t\t\t cloneHostname,\n\t\t\t\t\t\t cloneHostname);\n\t}\n\n\tereport(DEBUG2, (errmsg(\"sending replication status check query: %s to primary %s:%d\",\n\t\t\t\t\t\t\treplicationCheckQuery->data,\n\t\t\t\t\t\t\tprimaryWorkerNode->workerName,\n\t\t\t\t\t\t\tprimaryWorkerNode->workerPort)));\n\n\tint replicationCheckResultCode = SendRemoteCommand(primaryConnection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   replicationCheckQuery->data);\n\tif (replicationCheckResultCode == 0)\n\t{\n\t\tpfree(replicationCheckQuery->data);\n\t\tpfree(replicationCheckQuery);\n\t\tCloseConnection(primaryConnection);\n\t\tReportConnectionError(primaryConnection, ERROR);\n\t}\n\n\tPGresult *replicationCheckResult = GetRemoteCommandResult(primaryConnection, true);\n\tif (!IsResponseOK(replicationCheckResult))\n\t{\n\t\tReportResultError(primaryConnection, replicationCheckResult, ERROR);\n\t}\n\n\tList *replicationStateList = ReadFirstColumnAsText(replicationCheckResult);\n\n\t/* Check if clone is connected to this primary */\n\tif (list_length(replicationStateList) == 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"clone %s:%d is not connected to primary %s:%d\",\n\t\t\t\t\t\t\t   cloneHostname, clonePort,\n\t\t\t\t\t\t\t   primaryWorkerNode->workerName, primaryWorkerNode->\n\t\t\t\t\t\t\t   workerPort),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"The clone must be actively replicating from the specified primary node\"),\n\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\"Verify the clone is running and properly configured for replication\")));\n\t}\n\n\t/* Check if clone is synchronous */\n\tif (list_length(replicationStateList) > 0)\n\t{\n\t\tStringInfo syncStateInfo = (StringInfo) linitial(replicationStateList);\n\t\tif (syncStateInfo && syncStateInfo->data &&\n\t\t\t(strcmp(syncStateInfo->data, \"sync\") == 0 || strcmp(syncStateInfo->data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"quorum\") == 0))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\t\"cannot %s clone %s:%d as it is configured as a synchronous replica\",\n\t\t\t\t\t\t\t\toperation, cloneHostname, clonePort),\n\t\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\t\"Promoting a synchronous clone can cause data consistency issues\"),\n\t\t\t\t\t\t\terrhint(\n\t\t\t\t\t\t\t\t\"Configure clone as an asynchronous replica\")));\n\t\t}\n\t}\n\n\t/* Cleanup resources */\n\tbool raiseErrors = false;\n\tPQclear(replicationCheckResult);\n\tClearResults(primaryConnection, raiseErrors);\n\tpfree(replicationCheckQuery->data);\n\tpfree(replicationCheckQuery);\n\tCloseConnection(primaryConnection);\n\n\tereport(NOTICE, (errmsg(\n\t\t\t\t\t\t \"clone %s:%d is properly connected to primary %s:%d and is not synchronous\",\n\t\t\t\t\t\t cloneHostname, clonePort,\n\t\t\t\t\t\t primaryWorkerNode->workerName, primaryWorkerNode->workerPort))\n\t\t\t);\n}\n\n\n/*\n * EnsureValidStreamingReplica verifies that a node is a valid streaming replica\n * of the specified primary node.\n *\n * This function performs comprehensive validation to ensure the replica is:\n * 1. Currently in recovery mode (acting as a replica, not a primary)\n * 2. Has the same system identifier as the primary (ensuring they're part of\n *    the same PostgreSQL cluster/timeline)\n *\n * The function connects to both the replica and primary nodes to perform these\n * checks. This validation is critical before performing operations like promotion\n * or failover to ensure data consistency and prevent split-brain scenarios.\n *\n * Parameters:\n *   primaryWorkerNode - The primary node that should be the source of replication\n *   replicaHostname - Hostname/IP of the replica node to validate\n *   replicaPort - Port of the replica node to validate\n *\n * Throws ERROR if:\n *   - Cannot connect to the replica or primary node\n *   - Replica is not in recovery mode (indicating it's not acting as a replica)\n *   - System identifiers don't match between primary and replica\n *   - Any database queries fail during validation\n *\n */\nvoid\nEnsureValidStreamingReplica(WorkerNode *primaryWorkerNode, char *replicaHostname, int\n\t\t\t\t\t\t\treplicaPort)\n{\n\tint connectionFlag = FORCE_NEW_CONNECTION;\n\tMultiConnection *replicaConnection = GetNodeConnection(connectionFlag, replicaHostname\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   replicaPort);\n\n\tif (PQstatus(replicaConnection->pgConn) != CONNECTION_OK)\n\t{\n\t\tReportConnectionError(replicaConnection, ERROR);\n\t}\n\n\tconst char *replica_recovery_query = \"SELECT pg_is_in_recovery()\";\n\n\tint resultCode = SendRemoteCommand(replicaConnection, replica_recovery_query);\n\n\tif (resultCode == 0)\n\t{\n\t\tereport(DEBUG2, (errmsg(\n\t\t\t\t\t\t\t \"cannot connect to %s:%d to check if it is in recovery mode\",\n\t\t\t\t\t\t\t replicaHostname, replicaPort)));\n\t\tReportConnectionError(replicaConnection, ERROR);\n\t}\n\n\tbool raiseInterrupts = true;\n\tPGresult *result = GetRemoteCommandResult(replicaConnection, raiseInterrupts);\n\n\tif (!IsResponseOK(result))\n\t{\n\t\tereport(DEBUG2, (errmsg(\"failed to execute pg_is_in_recovery\")));\n\t\tReportResultError(replicaConnection, result, ERROR);\n\t}\n\n\tList *sizeList = ReadFirstColumnAsText(result);\n\tif (list_length(sizeList) != 1)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\"cannot parse pg_is_in_recovery() result from %s:%d\",\n\t\t\t\t\t\t\t   replicaHostname,\n\t\t\t\t\t\t\t   replicaPort)));\n\t}\n\n\tStringInfo isInRecoveryQueryResInfo = (StringInfo) linitial(sizeList);\n\tchar *isInRecoveryQueryResStr = isInRecoveryQueryResInfo->data;\n\n\tif (strcmp(isInRecoveryQueryResStr, \"t\") != 0 && strcmp(isInRecoveryQueryResStr,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"true\") != 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\terrmsg(\"node %s:%d is not in recovery mode\",\n\t\t\t\t\t\t\t   replicaHostname, replicaPort)));\n\t}\n\n\tPQclear(result);\n\tForgetResults(replicaConnection);\n\n\t/* Step2: Get the system identifier from replica */\n\tconst char *sysidQuery =\n\t\t\"SELECT system_identifier FROM pg_control_system()\";\n\n\tresultCode = SendRemoteCommand(replicaConnection, sysidQuery);\n\n\tif (resultCode == 0)\n\t{\n\t\tereport(DEBUG2, (errmsg(\"cannot connect to %s:%d to get system identifier\",\n\t\t\t\t\t\t\t\treplicaHostname, replicaPort)));\n\t\tReportConnectionError(replicaConnection, ERROR);\n\t}\n\n\tresult = GetRemoteCommandResult(replicaConnection, raiseInterrupts);\n\tif (!IsResponseOK(result))\n\t{\n\t\tereport(DEBUG2, (errmsg(\"failed to execute get system identifier\")));\n\t\tReportResultError(replicaConnection, result, ERROR);\n\t}\n\n\tList *sysidList = ReadFirstColumnAsText(result);\n\tif (list_length(sysidList) != 1)\n\t{\n\t\tCloseConnection(replicaConnection);\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\"cannot parse get system identifier result from %s:%d\",\n\t\t\t\t\t\t\t   replicaHostname,\n\t\t\t\t\t\t\t   replicaPort)));\n\t}\n\n\tStringInfo sysidQueryResInfo = (StringInfo) linitial(sysidList);\n\tchar *sysidQueryResStr = sysidQueryResInfo->data;\n\n\tereport(DEBUG2, (errmsg(\"system identifier of %s:%d is %s\",\n\t\t\t\t\t\t\treplicaHostname, replicaPort, sysidQueryResStr)));\n\n\t/* We do not need the connection anymore */\n\tPQclear(result);\n\tForgetResults(replicaConnection);\n\tCloseConnection(replicaConnection);\n\n\t/* Step3: Get system identifier from primary */\n\tereport(DEBUG2, (errmsg(\"getting system identifier from primary %s:%d\",\n\t\t\t\t\t\t\tprimaryWorkerNode->workerName,\n\t\t\t\t\t\t\tprimaryWorkerNode->workerPort)));\n\n\tint primaryConnectionFlag = 0;\n\tMultiConnection *primaryConnection = GetNodeConnection(primaryConnectionFlag,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   primaryWorkerNode->workerName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   primaryWorkerNode->workerPort);\n\n\tif (PQstatus(primaryConnection->pgConn) != CONNECTION_OK)\n\t{\n\t\tReportConnectionError(primaryConnection, ERROR);\n\t}\n\n\tint primaryResultCode = SendRemoteCommand(primaryConnection, sysidQuery);\n\tif (primaryResultCode == 0)\n\t{\n\t\tReportConnectionError(primaryConnection, ERROR);\n\t}\n\n\tPGresult *primaryResult = GetRemoteCommandResult(primaryConnection, raiseInterrupts);\n\tif (!IsResponseOK(primaryResult))\n\t{\n\t\tereport(DEBUG2, (errmsg(\"failed to execute get system identifier\")));\n\t\tReportResultError(primaryConnection, primaryResult, ERROR);\n\t}\n\tList *primarySizeList = ReadFirstColumnAsText(primaryResult);\n\tif (list_length(primarySizeList) != 1)\n\t{\n\t\tCloseConnection(primaryConnection);\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\"cannot parse get system identifier result from %s:%d\",\n\t\t\t\t\t\t\t   primaryWorkerNode->workerName,\n\t\t\t\t\t\t\t   primaryWorkerNode->workerPort)));\n\t}\n\tStringInfo primarySysidQueryResInfo = (StringInfo) linitial(primarySizeList);\n\tchar *primarySysidQueryResStr = primarySysidQueryResInfo->data;\n\n\tereport(DEBUG2, (errmsg(\"system identifier of %s:%d is %s\",\n\t\t\t\t\t\t\tprimaryWorkerNode->workerName, primaryWorkerNode->workerPort,\n\t\t\t\t\t\t\tprimarySysidQueryResStr)));\n\n\t/* verify both identifiers */\n\tif (strcmp(sysidQueryResStr, primarySysidQueryResStr) != 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),\n\t\t\t\t\t\terrmsg(\n\t\t\t\t\t\t\t\"system identifiers do not match: %s (clone) vs %s (primary)\",\n\t\t\t\t\t\t\tsysidQueryResStr, primarySysidQueryResStr)));\n\t}\n\tPQclear(primaryResult);\n\tClearResults(primaryConnection, true);\n\tCloseConnection(primaryConnection);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/colocation_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * colocation_utils.c\n *\n * This file contains functions to perform useful operations on co-located tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"access/xact.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/sequence.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/pg_dist_colocation.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/tenant_schema_metadata.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_transaction.h\"\n\n\n/* local function forward declarations */\nstatic void MarkTablesColocated(Oid sourceRelationId, Oid targetRelationId);\nstatic bool ShardsIntervalsEqual(ShardInterval *leftShardInterval,\n\t\t\t\t\t\t\t\t ShardInterval *rightShardInterval);\nstatic bool HashPartitionedShardIntervalsEqual(ShardInterval *leftShardInterval,\n\t\t\t\t\t\t\t\t\t\t\t   ShardInterval *rightShardInterval);\nstatic int CompareShardPlacementsByNode(const void *leftElement,\n\t\t\t\t\t\t\t\t\t\tconst void *rightElement);\nstatic uint32 CreateColocationGroupForRelation(Oid sourceRelationId);\nstatic void BreakColocation(Oid sourceRelationId);\nstatic uint32 SingleShardTableGetNodeId(Oid relationId);\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(mark_tables_colocated);\nPG_FUNCTION_INFO_V1(get_colocated_shard_array);\nPG_FUNCTION_INFO_V1(update_distributed_table_colocation);\n\n\n/*\n * mark_tables_colocated puts target tables to same colocation group with the\n * source table. If the source table is in INVALID_COLOCATION_ID group, then it\n * creates a new colocation group and assigns all tables to this new colocation\n * group.\n */\nDatum\nmark_tables_colocated(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\terrmsg(\"this function is deprecated and no longer is used\")));\n}\n\n\n/*\n * update_distributed_table_colocation updates the colocation of a table.\n * if colocate_with -> 'none' then the table is assigned a new\n * colocation group.\n */\nDatum\nupdate_distributed_table_colocation(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tOid targetRelationId = PG_GETARG_OID(0);\n\ttext *colocateWithTableNameText = PG_GETARG_TEXT_P(1);\n\n\tEnsureTableOwner(targetRelationId);\n\tErrorIfTenantTable(targetRelationId, TenantOperationNames[TENANT_UPDATE_COLOCATION]);\n\n\tchar *colocateWithTableName = text_to_cstring(colocateWithTableNameText);\n\tif (IsColocateWithNone(colocateWithTableName))\n\t{\n\t\tEnsureHashOrSingleShardDistributedTable(targetRelationId);\n\t\tBreakColocation(targetRelationId);\n\t}\n\telse\n\t{\n\t\tOid colocateWithTableId = ResolveRelationId(colocateWithTableNameText, false);\n\t\tErrorIfTenantTable(colocateWithTableId,\n\t\t\t\t\t\t   TenantOperationNames[TENANT_COLOCATE_WITH]);\n\t\tEnsureTableOwner(colocateWithTableId);\n\t\tMarkTablesColocated(colocateWithTableId, targetRelationId);\n\t}\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * IsColocateWithNone returns true if the given table is\n * the special keyword \"none\".\n */\nbool\nIsColocateWithNone(char *colocateWithTableName)\n{\n\treturn pg_strncasecmp(colocateWithTableName, \"none\", NAMEDATALEN) == 0;\n}\n\n\n/*\n * IsColocateWithDefault returns true if the given table is\n * the special keyword \"default\".\n */\nbool\nIsColocateWithDefault(char *colocateWithTableName)\n{\n\treturn pg_strncasecmp(colocateWithTableName, \"default\", NAMEDATALEN) == 0;\n}\n\n\n/*\n * BreakColocation breaks the colocations of the given relation id.\n * If t1, t2 and t3 are colocated and we call this function with t2,\n * t1 and t3 will stay colocated but t2 will have a new colocation id.\n * Note that this function does not move any data around for the new colocation.\n */\nstatic void\nBreakColocation(Oid sourceRelationId)\n{\n\t/*\n\t * Get an exclusive lock on the colocation system catalog. Therefore, we\n\t * can be sure that there will no modifications on the colocation table\n\t * until this transaction is committed.\n\t */\n\tRelation pgDistColocation = table_open(DistColocationRelationId(), ExclusiveLock);\n\n\tuint32 oldColocationId = TableColocationId(sourceRelationId);\n\tCreateColocationGroupForRelation(sourceRelationId);\n\n\t/* if there is not any remaining table in the old colocation group, delete it */\n\tDeleteColocationGroupIfNoTablesBelong(oldColocationId);\n\n\ttable_close(pgDistColocation, NoLock);\n}\n\n\n/*\n * get_colocated_shards_array returns array of shards ids which are co-located with given\n * shard.\n */\nDatum\nget_colocated_shard_array(PG_FUNCTION_ARGS)\n{\n\tuint32 shardId = PG_GETARG_UINT32(0);\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\n\tList *colocatedShardList = ColocatedShardIntervalList(shardInterval);\n\tint colocatedShardCount = list_length(colocatedShardList);\n\tDatum *colocatedShardsDatumArray = palloc0(colocatedShardCount * sizeof(Datum));\n\tOid arrayTypeId = OIDOID;\n\tint colocatedShardIndex = 0;\n\n\tShardInterval *colocatedShardInterval = NULL;\n\tforeach_declared_ptr(colocatedShardInterval, colocatedShardList)\n\t{\n\t\tuint64 colocatedShardId = colocatedShardInterval->shardId;\n\n\t\tDatum colocatedShardDatum = Int64GetDatum(colocatedShardId);\n\n\t\tcolocatedShardsDatumArray[colocatedShardIndex] = colocatedShardDatum;\n\t\tcolocatedShardIndex++;\n\t}\n\n\tArrayType *colocatedShardsArrayType = DatumArrayToArrayType(colocatedShardsDatumArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolocatedShardCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tarrayTypeId);\n\n\tPG_RETURN_ARRAYTYPE_P(colocatedShardsArrayType);\n}\n\n\n/*\n * CreateColocationGroupForRelation creates colocation entry in\n * pg_dist_colocation and updated the colocation id in pg_dist_partition\n * for the given relation.\n */\nstatic uint32\nCreateColocationGroupForRelation(Oid sourceRelationId)\n{\n\tuint32 shardCount = ShardIntervalCount(sourceRelationId);\n\tuint32 shardReplicationFactor = TableShardReplicationFactor(sourceRelationId);\n\n\tVar *sourceDistributionColumn = DistPartitionKey(sourceRelationId);\n\tOid sourceDistributionColumnType = InvalidOid;\n\tOid sourceDistributionColumnCollation = InvalidOid;\n\n\t/* reference tables has NULL distribution column */\n\tif (sourceDistributionColumn != NULL)\n\t{\n\t\tsourceDistributionColumnType = sourceDistributionColumn->vartype;\n\t\tsourceDistributionColumnCollation = sourceDistributionColumn->varcollid;\n\t}\n\n\tuint32 sourceColocationId = CreateColocationGroup(shardCount, shardReplicationFactor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  sourceDistributionColumnType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  sourceDistributionColumnCollation);\n\tbool localOnly = false;\n\tUpdateRelationColocationGroup(sourceRelationId, sourceColocationId, localOnly);\n\treturn sourceColocationId;\n}\n\n\n/*\n * MarkTablesColocated puts both tables to same colocation group. If the\n * source table is in INVALID_COLOCATION_ID group, then it creates a new\n * colocation group and assigns both tables to same colocation group. Otherwise,\n * it adds the target table to colocation group of the source table.\n */\nstatic void\nMarkTablesColocated(Oid sourceRelationId, Oid targetRelationId)\n{\n\tif (IsCitusTableType(sourceRelationId, CITUS_LOCAL_TABLE) ||\n\t\tIsCitusTableType(targetRelationId, CITUS_LOCAL_TABLE))\n\t{\n\t\tereport(ERROR, (errmsg(\"local tables cannot be colocated with \"\n\t\t\t\t\t\t\t   \"other tables\")));\n\t}\n\n\tEnsureHashOrSingleShardDistributedTable(sourceRelationId);\n\tEnsureHashOrSingleShardDistributedTable(targetRelationId);\n\tCheckReplicationModel(sourceRelationId, targetRelationId);\n\tCheckDistributionColumnType(sourceRelationId, targetRelationId);\n\n\t/*\n\t * Get an exclusive lock on the colocation system catalog. Therefore, we\n\t * can be sure that there will no modifications on the colocation table\n\t * until this transaction is committed.\n\t */\n\tRelation pgDistColocation = table_open(DistColocationRelationId(), ExclusiveLock);\n\n\t/* check if shard placements are colocated */\n\tErrorIfShardPlacementsNotColocated(sourceRelationId, targetRelationId);\n\n\t/*\n\t * Get colocation group of the source table, if the source table does not\n\t * have a colocation group, create a new one, and set it for the source table.\n\t */\n\tuint32 sourceColocationId = TableColocationId(sourceRelationId);\n\tif (sourceColocationId == INVALID_COLOCATION_ID)\n\t{\n\t\tsourceColocationId = CreateColocationGroupForRelation(sourceRelationId);\n\t}\n\n\tuint32 targetColocationId = TableColocationId(targetRelationId);\n\n\t/* finally set colocation group for the target relation */\n\tbool localOnly = false;\n\tUpdateRelationColocationGroup(targetRelationId, sourceColocationId, localOnly);\n\n\t/* if there is not any remaining table in the colocation group, delete it */\n\tDeleteColocationGroupIfNoTablesBelong(targetColocationId);\n\n\ttable_close(pgDistColocation, NoLock);\n}\n\n\n/*\n * ErrorIfShardPlacementsNotColocated checks if the shard placements of the\n * given two relations are physically colocated. It errors out in any of\n * following cases:\n * 1.Shard counts are different,\n * 2.Shard intervals don't match\n * 3.Matching shard intervals have different number of shard placements\n * 4.Shard placements are not colocated (not on the same node)\n * 5.Shard placements have different health states\n *\n * Note that, this functions assumes that both tables are hash distributed.\n */\nvoid\nErrorIfShardPlacementsNotColocated(Oid leftRelationId, Oid rightRelationId)\n{\n\t/* get sorted shard interval lists for both tables */\n\tList *leftShardIntervalList = LoadShardIntervalList(leftRelationId);\n\tList *rightShardIntervalList = LoadShardIntervalList(rightRelationId);\n\n\t/* prevent concurrent placement changes */\n\tLockShardListMetadata(leftShardIntervalList, ShareLock);\n\tLockShardListMetadata(rightShardIntervalList, ShareLock);\n\n\tchar *leftRelationName = get_rel_name(leftRelationId);\n\tchar *rightRelationName = get_rel_name(rightRelationId);\n\n\tuint32 leftShardCount = list_length(leftShardIntervalList);\n\tuint32 rightShardCount = list_length(rightShardIntervalList);\n\n\tif (leftShardCount != rightShardCount)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot colocate tables %s and %s\",\n\t\t\t\t\t\t\t   leftRelationName, rightRelationName),\n\t\t\t\t\t\terrdetail(\"Shard counts don't match for %s and %s.\",\n\t\t\t\t\t\t\t\t  leftRelationName, rightRelationName)));\n\t}\n\n\t/* compare shard intervals one by one */\n\tShardInterval *leftInterval = NULL;\n\tShardInterval *rightInterval = NULL;\n\tforboth_ptr(leftInterval, leftShardIntervalList,\n\t\t\t\trightInterval, rightShardIntervalList)\n\t{\n\t\tuint64 leftShardId = leftInterval->shardId;\n\t\tuint64 rightShardId = rightInterval->shardId;\n\n\t\tbool shardsIntervalsEqual = ShardsIntervalsEqual(leftInterval, rightInterval);\n\t\tif (!shardsIntervalsEqual)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot colocate tables %s and %s\",\n\t\t\t\t\t\t\t\t   leftRelationName, rightRelationName),\n\t\t\t\t\t\t\terrdetail(\"Shard intervals don't match for %s and %s.\",\n\t\t\t\t\t\t\t\t\t  leftRelationName, rightRelationName)));\n\t\t}\n\n\t\tList *leftPlacementList = ShardPlacementList(leftShardId);\n\t\tList *rightPlacementList = ShardPlacementList(rightShardId);\n\n\t\tif (list_length(leftPlacementList) != list_length(rightPlacementList))\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot colocate tables %s and %s\",\n\t\t\t\t\t\t\t\t   leftRelationName, rightRelationName),\n\t\t\t\t\t\t\terrdetail(\"Shard \" UINT64_FORMAT\n\t\t\t\t\t\t\t\t\t  \" of %s and shard \" UINT64_FORMAT\n\t\t\t\t\t\t\t\t\t  \" of %s have different number of shard placements.\",\n\t\t\t\t\t\t\t\t\t  leftShardId, leftRelationName,\n\t\t\t\t\t\t\t\t\t  rightShardId, rightRelationName)));\n\t\t}\n\n\t\t/* sort shard placements according to the node */\n\t\tList *sortedLeftPlacementList = SortList(leftPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t CompareShardPlacementsByNode);\n\t\tList *sortedRightPlacementList = SortList(rightPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t  CompareShardPlacementsByNode);\n\n\t\t/* compare shard placements one by one */\n\t\tShardPlacement *leftPlacement = NULL;\n\t\tShardPlacement *rightPlacement = NULL;\n\t\tforboth_ptr(leftPlacement, sortedLeftPlacementList,\n\t\t\t\t\trightPlacement, sortedRightPlacementList)\n\t\t{\n\t\t\t/*\n\t\t\t * If shard placements are on different nodes, these shard\n\t\t\t * placements are not colocated.\n\t\t\t */\n\t\t\tint nodeCompare = CompareShardPlacementsByNode((void *) &leftPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   (void *) &rightPlacement);\n\t\t\tif (nodeCompare != 0)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"cannot colocate tables %s and %s\",\n\t\t\t\t\t\t\t\t\t   leftRelationName, rightRelationName),\n\t\t\t\t\t\t\t\terrdetail(\"Shard \" UINT64_FORMAT \" of %s and shard \"\n\t\t\t\t\t\t\t\t\t\t  UINT64_FORMAT \" of %s are not colocated.\",\n\t\t\t\t\t\t\t\t\t\t  leftShardId, leftRelationName,\n\t\t\t\t\t\t\t\t\t\t  rightShardId, rightRelationName)));\n\t\t\t}\n\t\t}\n\t}\n}\n\n\n/*\n * ShardsIntervalsEqual checks if two shard intervals of distributed\n * tables are equal.\n *\n * Notes on the function:\n * (i)   The function returns true if both shard intervals are the same.\n * (ii)  The function returns false even if the shard intervals equal, but,\n *       their distribution method are different.\n * (iii) The function returns false for append and range partitioned tables\n *       excluding (i) case.\n * (iv)  For reference tables, all shards are equal (i.e., same replication factor\n *       and shard min/max values). Thus, always return true for shards of reference\n *       tables.\n */\nstatic bool\nShardsIntervalsEqual(ShardInterval *leftShardInterval, ShardInterval *rightShardInterval)\n{\n\tchar leftIntervalPartitionMethod = PartitionMethod(leftShardInterval->relationId);\n\tchar rightIntervalPartitionMethod = PartitionMethod(rightShardInterval->relationId);\n\n\t/* if both shards are the same, return true */\n\tif (leftShardInterval->shardId == rightShardInterval->shardId)\n\t{\n\t\treturn true;\n\t}\n\n\t/* if partition methods are not the same, shards cannot be considered as co-located */\n\tleftIntervalPartitionMethod = PartitionMethod(leftShardInterval->relationId);\n\trightIntervalPartitionMethod = PartitionMethod(rightShardInterval->relationId);\n\tif (leftIntervalPartitionMethod != rightIntervalPartitionMethod)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsCitusTableType(leftShardInterval->relationId, HASH_DISTRIBUTED))\n\t{\n\t\treturn HashPartitionedShardIntervalsEqual(leftShardInterval, rightShardInterval);\n\t}\n\telse if (!HasDistributionKey(leftShardInterval->relationId))\n\t{\n\t\t/*\n\t\t * Reference tables has only a single shard and all reference tables\n\t\t * are always co-located with each other.\n\t\t */\n\n\t\treturn true;\n\t}\n\n\t/* append and range partitioned shard never co-located */\n\treturn false;\n}\n\n\n/*\n * HashPartitionedShardIntervalsEqual checks if two shard intervals of hash distributed\n * tables are equal. Note that, this function doesn't work with non-hash\n * partitioned table's shards.\n *\n * We do min/max value check here to decide whether two shards are colocated,\n * instead we can simply use ShardIndex function on both shards then\n * but do index check, but we avoid it because this way it is more cheaper.\n */\nstatic bool\nHashPartitionedShardIntervalsEqual(ShardInterval *leftShardInterval,\n\t\t\t\t\t\t\t\t   ShardInterval *rightShardInterval)\n{\n\tint32 leftShardMinValue = DatumGetInt32(leftShardInterval->minValue);\n\tint32 leftShardMaxValue = DatumGetInt32(leftShardInterval->maxValue);\n\tint32 rightShardMinValue = DatumGetInt32(rightShardInterval->minValue);\n\tint32 rightShardMaxValue = DatumGetInt32(rightShardInterval->maxValue);\n\n\tbool minValuesEqual = leftShardMinValue == rightShardMinValue;\n\tbool maxValuesEqual = leftShardMaxValue == rightShardMaxValue;\n\n\treturn minValuesEqual && maxValuesEqual;\n}\n\n\n/*\n * CompareShardPlacementsByNode compares two shard placements by their nodename\n * and nodeport.\n */\nstatic int\nCompareShardPlacementsByNode(const void *leftElement, const void *rightElement)\n{\n\tconst ShardPlacement *leftPlacement = *((const ShardPlacement **) leftElement);\n\tconst ShardPlacement *rightPlacement = *((const ShardPlacement **) rightElement);\n\n\t/* if node names are same, check node ports */\n\tif (leftPlacement->nodeId < rightPlacement->nodeId)\n\t{\n\t\treturn -1;\n\t}\n\telse if (leftPlacement->nodeId > rightPlacement->nodeId)\n\t{\n\t\treturn 1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * ColocationId searches pg_dist_colocation for shard count, replication factor,\n * distribution column type, and distribution column collation. If a matching entry\n * is found, it returns the colocation id, otherwise returns INVALID_COLOCATION_ID.\n */\nuint32\nColocationId(int shardCount, int replicationFactor, Oid distributionColumnType, Oid\n\t\t\t distributionColumnCollation)\n{\n\tuint32 colocationId = INVALID_COLOCATION_ID;\n\tconst int scanKeyCount = 4;\n\tScanKeyData scanKey[4];\n\tbool indexOK = true;\n\n\tRelation pgDistColocation = table_open(DistColocationRelationId(), AccessShareLock);\n\n\t/* set scan arguments */\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_colocation_distributioncolumntype,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(distributionColumnType));\n\tScanKeyInit(&scanKey[1], Anum_pg_dist_colocation_shardcount,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(shardCount));\n\tScanKeyInit(&scanKey[2], Anum_pg_dist_colocation_replicationfactor,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(replicationFactor));\n\tScanKeyInit(&scanKey[3], Anum_pg_dist_colocation_distributioncolumncollation,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(\n\t\t\t\t\tdistributionColumnCollation));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistColocation,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistColocationConfigurationIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK, NULL, scanKeyCount, scanKey);\n\n\tHeapTuple colocationTuple = systable_getnext(scanDescriptor);\n\n\twhile (HeapTupleIsValid(colocationTuple))\n\t{\n\t\tForm_pg_dist_colocation colocationForm =\n\t\t\t(Form_pg_dist_colocation) GETSTRUCT(colocationTuple);\n\n\t\t/* avoid chosing a colocation group that belongs to a tenant schema */\n\t\tif (IsTenantSchemaColocationGroup(colocationForm->colocationid))\n\t\t{\n\t\t\tcolocationTuple = systable_getnext(scanDescriptor);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (colocationId == INVALID_COLOCATION_ID ||\n\t\t\tcolocationId > colocationForm->colocationid)\n\t\t{\n\t\t\t/*\n\t\t\t * We assign the smallest colocation id among all the matches so that we\n\t\t\t * assign the same colocation group for similar distributed tables\n\t\t\t */\n\t\t\tcolocationId = colocationForm->colocationid;\n\t\t}\n\t\tcolocationTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistColocation, AccessShareLock);\n\n\treturn colocationId;\n}\n\n\n/*\n * AcquireColocationDefaultLock serializes concurrent creation of a colocation entry\n * for default group.\n */\nvoid\nAcquireColocationDefaultLock(void)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = false;\n\n\tSET_LOCKTAG_CITUS_OPERATION(tag, CITUS_CREATE_COLOCATION_DEFAULT);\n\n\t(void) LockAcquire(&tag, ExclusiveLock, sessionLock, dontWait);\n}\n\n\n/*\n * ReleaseColocationDefaultLock releases the lock for concurrent creation of a colocation entry\n * for default group.\n */\nvoid\nReleaseColocationDefaultLock(void)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\n\tSET_LOCKTAG_CITUS_OPERATION(tag, CITUS_CREATE_COLOCATION_DEFAULT);\n\n\tLockRelease(&tag, ExclusiveLock, sessionLock);\n}\n\n\n/*\n * CreateColocationGroup creates a new colocation id and writes it into\n * pg_dist_colocation with the given configuration. It also returns the created\n * colocation id.\n */\nuint32\nCreateColocationGroup(int shardCount, int replicationFactor, Oid distributionColumnType,\n\t\t\t\t\t  Oid distributionColumnCollation)\n{\n\tuint32 colocationId = GetNextColocationId();\n\n\tInsertColocationGroupLocally(colocationId, shardCount, replicationFactor,\n\t\t\t\t\t\t\t\t distributionColumnType, distributionColumnCollation);\n\n\tSyncNewColocationGroupToNodes(colocationId, shardCount, replicationFactor,\n\t\t\t\t\t\t\t\t  distributionColumnType, distributionColumnCollation);\n\n\treturn colocationId;\n}\n\n\n/*\n * InsertColocationGroupLocally inserts a record into pg_dist_colocation.\n */\nvoid\nInsertColocationGroupLocally(uint32 colocationId, int shardCount, int replicationFactor,\n\t\t\t\t\t\t\t Oid distributionColumnType,\n\t\t\t\t\t\t\t Oid distributionColumnCollation)\n{\n\tDatum values[Natts_pg_dist_colocation];\n\tbool isNulls[Natts_pg_dist_colocation];\n\n\t/* form new colocation tuple */\n\tmemset(values, 0, sizeof(values));\n\tmemset(isNulls, false, sizeof(isNulls));\n\n\tvalues[Anum_pg_dist_colocation_colocationid - 1] = UInt32GetDatum(colocationId);\n\tvalues[Anum_pg_dist_colocation_shardcount - 1] = UInt32GetDatum(shardCount);\n\tvalues[Anum_pg_dist_colocation_replicationfactor - 1] =\n\t\tUInt32GetDatum(replicationFactor);\n\tvalues[Anum_pg_dist_colocation_distributioncolumntype - 1] =\n\t\tObjectIdGetDatum(distributionColumnType);\n\tvalues[Anum_pg_dist_colocation_distributioncolumncollation - 1] =\n\t\tObjectIdGetDatum(distributionColumnCollation);\n\n\t/* open colocation relation and insert the new tuple */\n\tRelation pgDistColocation = table_open(DistColocationRelationId(), RowExclusiveLock);\n\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistColocation);\n\tHeapTuple heapTuple = heap_form_tuple(tupleDescriptor, values, isNulls);\n\n\tCatalogTupleInsert(pgDistColocation, heapTuple);\n\n\t/* increment the counter so that next command can see the row */\n\tCommandCounterIncrement();\n\ttable_close(pgDistColocation, NoLock);\n}\n\n\n/*\n * GetNextColocationId allocates and returns a unique colocationId for the\n * colocation group to be created. This allocation occurs both in shared memory\n * and in write ahead logs; writing to logs avoids the risk of having\n * colocationId collisions.\n *\n * Please note that the caller is still responsible for finalizing colocationId\n * with the master node. Further note that this function relies on an internal\n * sequence created in initdb to generate unique identifiers.\n */\nuint32\nGetNextColocationId()\n{\n\ttext *sequenceName = cstring_to_text(COLOCATIONID_SEQUENCE_NAME);\n\tOid sequenceId = ResolveRelationId(sequenceName, false);\n\tDatum sequenceIdDatum = ObjectIdGetDatum(sequenceId);\n\tOid savedUserId = InvalidOid;\n\tint savedSecurityContext = 0;\n\n\tGetUserIdAndSecContext(&savedUserId, &savedSecurityContext);\n\tSetUserIdAndSecContext(CitusExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE);\n\n\t/* generate new and unique colocation id from sequence */\n\tDatum colocationIdDatum = DirectFunctionCall1(nextval_oid, sequenceIdDatum);\n\n\tSetUserIdAndSecContext(savedUserId, savedSecurityContext);\n\n\tuint32 colocationId = DatumGetUInt32(colocationIdDatum);\n\n\treturn colocationId;\n}\n\n\n/*\n * CheckReplicationModel checks if given relations are from the same\n * replication model. Otherwise, it errors out.\n */\nvoid\nCheckReplicationModel(Oid sourceRelationId, Oid targetRelationId)\n{\n\tCitusTableCacheEntry *sourceTableEntry = GetCitusTableCacheEntry(sourceRelationId);\n\tchar sourceReplicationModel = sourceTableEntry->replicationModel;\n\n\tCitusTableCacheEntry *targetTableEntry = GetCitusTableCacheEntry(targetRelationId);\n\tchar targetReplicationModel = targetTableEntry->replicationModel;\n\n\tif (sourceReplicationModel != targetReplicationModel)\n\t{\n\t\tchar *sourceRelationName = get_rel_name(sourceRelationId);\n\t\tchar *targetRelationName = get_rel_name(targetRelationId);\n\n\t\tereport(ERROR, (errmsg(\"cannot colocate tables %s and %s\",\n\t\t\t\t\t\t\t   sourceRelationName, targetRelationName),\n\t\t\t\t\t\terrdetail(\"Replication models don't match for %s and %s.\",\n\t\t\t\t\t\t\t\t  sourceRelationName, targetRelationName)));\n\t}\n}\n\n\n/*\n * CheckDistributionColumnType checks if distribution column types of relations\n * are same. Otherwise, it errors out.\n */\nvoid\nCheckDistributionColumnType(Oid sourceRelationId, Oid targetRelationId)\n{\n\t/* reference tables have NULL distribution column */\n\tVar *sourceDistributionColumn = DistPartitionKey(sourceRelationId);\n\n\t/* reference tables have NULL distribution column */\n\tVar *targetDistributionColumn = DistPartitionKey(targetRelationId);\n\n\tEnsureColumnTypeEquality(sourceRelationId, targetRelationId,\n\t\t\t\t\t\t\t sourceDistributionColumn, targetDistributionColumn);\n}\n\n\n/*\n * EnsureColumnTypeEquality checks if distribution column types and collations\n * of the given columns are same. The function sets the boolean pointers.\n */\nvoid\nEnsureColumnTypeEquality(Oid sourceRelationId, Oid targetRelationId,\n\t\t\t\t\t\t Var *sourceDistributionColumn, Var *targetDistributionColumn)\n{\n\tOid sourceDistributionColumnType = InvalidOid;\n\tOid targetDistributionColumnType = InvalidOid;\n\tOid sourceDistributionColumnCollation = InvalidOid;\n\tOid targetDistributionColumnCollation = InvalidOid;\n\n\tif (sourceDistributionColumn != NULL)\n\t{\n\t\tsourceDistributionColumnType = sourceDistributionColumn->vartype;\n\t\tsourceDistributionColumnCollation = sourceDistributionColumn->varcollid;\n\t}\n\n\tif (targetDistributionColumn != NULL)\n\t{\n\t\ttargetDistributionColumnType = targetDistributionColumn->vartype;\n\t\ttargetDistributionColumnCollation = targetDistributionColumn->varcollid;\n\t}\n\n\tbool columnTypesSame = sourceDistributionColumnType == targetDistributionColumnType;\n\tbool columnCollationsSame =\n\t\tsourceDistributionColumnCollation == targetDistributionColumnCollation;\n\n\tif (!columnTypesSame)\n\t{\n\t\tchar *sourceRelationName = get_rel_name(sourceRelationId);\n\t\tchar *targetRelationName = get_rel_name(targetRelationId);\n\n\t\tereport(ERROR, (errmsg(\"cannot colocate tables %s and %s\",\n\t\t\t\t\t\t\t   sourceRelationName, targetRelationName),\n\t\t\t\t\t\terrdetail(\"Distribution column types don't match for \"\n\t\t\t\t\t\t\t\t  \"%s and %s.\", sourceRelationName,\n\t\t\t\t\t\t\t\t  targetRelationName)));\n\t}\n\n\tif (!columnCollationsSame)\n\t{\n\t\tchar *sourceRelationName = get_rel_name(sourceRelationId);\n\t\tchar *targetRelationName = get_rel_name(targetRelationId);\n\n\t\tereport(ERROR, (errmsg(\"cannot colocate tables %s and %s\",\n\t\t\t\t\t\t\t   sourceRelationName, targetRelationName),\n\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\"Distribution column collations don't match for \"\n\t\t\t\t\t\t\t\"%s and %s.\", sourceRelationName,\n\t\t\t\t\t\t\ttargetRelationName)));\n\t}\n}\n\n\n/*\n * UpdateRelationColocationGroup updates colocation group in pg_dist_partition\n * for the given relation.\n *\n * When localOnly is true, the function does not propagate changes to the\n * metadata workers.\n */\nvoid\nUpdateRelationColocationGroup(Oid distributedRelationId, uint32 colocationId,\n\t\t\t\t\t\t\t  bool localOnly)\n{\n\tbool indexOK = true;\n\tint scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\n\tDatum *values = (Datum *) palloc0(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNull = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\tbool *replace = (bool *) palloc0(tupleDescriptor->natts * sizeof(bool));\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_partition_logicalrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(distributedRelationId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPartitionLogicalRelidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tchar *distributedRelationName = get_rel_name(distributedRelationId);\n\t\tereport(ERROR, (errmsg(\"could not find valid entry for relation %s\",\n\t\t\t\t\t\t\t   distributedRelationName)));\n\t}\n\n\tvalues[Anum_pg_dist_partition_colocationid - 1] = UInt32GetDatum(colocationId);\n\tisNull[Anum_pg_dist_partition_colocationid - 1] = false;\n\treplace[Anum_pg_dist_partition_colocationid - 1] = true;\n\n\theapTuple = heap_modify_tuple(heapTuple, tupleDescriptor, values, isNull, replace);\n\n\n\tCatalogTupleUpdate(pgDistPartition, &heapTuple->t_self, heapTuple);\n\n\tCitusInvalidateRelcacheByRelid(distributedRelationId);\n\n\tCommandCounterIncrement();\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistPartition, NoLock);\n\n\tpfree(values);\n\tpfree(isNull);\n\tpfree(replace);\n\n\tbool shouldSyncMetadata = ShouldSyncTableMetadata(distributedRelationId);\n\tif (shouldSyncMetadata && !localOnly)\n\t{\n\t\tchar *updateColocationIdCommand = ColocationIdUpdateCommand(distributedRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolocationId);\n\n\t\tSendCommandToWorkersWithMetadata(updateColocationIdCommand);\n\t}\n}\n\n\n/*\n * TableColocationId function returns co-location id of given table. This function\n * errors out if given table is not distributed.\n */\nuint32\nTableColocationId(Oid distributedTableId)\n{\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(distributedTableId);\n\n\treturn cacheEntry->colocationId;\n}\n\n\n/*\n * TablesColocated function checks whether given two tables are co-located and\n * returns true if they are co-located. A table is always co-located with itself.\n * If given two tables are different and they are not distributed, this function\n * errors out.\n */\nbool\nTablesColocated(Oid leftDistributedTableId, Oid rightDistributedTableId)\n{\n\tif (leftDistributedTableId == rightDistributedTableId)\n\t{\n\t\treturn true;\n\t}\n\n\tuint32 leftColocationId = TableColocationId(leftDistributedTableId);\n\tuint32 rightColocationId = TableColocationId(rightDistributedTableId);\n\tif (leftColocationId == INVALID_COLOCATION_ID ||\n\t\trightColocationId == INVALID_COLOCATION_ID)\n\t{\n\t\treturn false;\n\t}\n\n\treturn leftColocationId == rightColocationId;\n}\n\n\n/*\n * ShardsColocated function checks whether given two shards are co-located and\n * returns true if they are co-located. Two shards are co-located either;\n * - They are same (A shard is always co-located with itself).\n * OR\n * - Tables are hash partitioned.\n * - Tables containing the shards are co-located.\n * - Min/Max values of the shards are same.\n */\nbool\nShardsColocated(ShardInterval *leftShardInterval, ShardInterval *rightShardInterval)\n{\n\tbool tablesColocated = TablesColocated(leftShardInterval->relationId,\n\t\t\t\t\t\t\t\t\t\t   rightShardInterval->relationId);\n\n\tif (tablesColocated)\n\t{\n\t\tbool shardIntervalEqual = ShardsIntervalsEqual(leftShardInterval,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   rightShardInterval);\n\t\treturn shardIntervalEqual;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ColocatedTableList function returns list of relation ids which are co-located\n * with given table. If given table is not hash distributed, co-location is not\n * valid for that table and it is only co-located with itself.\n */\nList *\nColocatedTableList(Oid distributedTableId)\n{\n\tuint32 tableColocationId = TableColocationId(distributedTableId);\n\tList *colocatedTableList = NIL;\n\n\t/*\n\t * If distribution type of the table is not hash, the table is only co-located\n\t * with itself.\n\t */\n\tif (tableColocationId == INVALID_COLOCATION_ID)\n\t{\n\t\tcolocatedTableList = lappend_oid(colocatedTableList, distributedTableId);\n\t\treturn colocatedTableList;\n\t}\n\n\tint count = 0;\n\tcolocatedTableList = ColocationGroupTableList(tableColocationId, count);\n\n\treturn colocatedTableList;\n}\n\n\n/*\n * ColocationGroupTableList returns the list of tables in the given colocation\n * group. If the colocation group is INVALID_COLOCATION_ID, it returns NIL.\n *\n * If count is zero then the command is executed for all rows that it applies to.\n * If count is greater than zero, then no more than count rows will be retrieved;\n * execution stops when the count is reached, much like adding a LIMIT clause\n * to the query.\n */\nList *\nColocationGroupTableList(uint32 colocationId, uint32 count)\n{\n\tList *colocatedTableList = NIL;\n\tbool indexOK = true;\n\tint scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\n\t/*\n\t * If distribution type of the table is not hash, the table is only co-located\n\t * with itself.\n\t */\n\tif (colocationId == INVALID_COLOCATION_ID)\n\t{\n\t\treturn NIL;\n\t}\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_partition_colocationid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(colocationId));\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPartitionColocationidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK, NULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tDatum *datumArray = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNullArray = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tmemset(datumArray, 0, tupleDescriptor->natts * sizeof(Datum));\n\t\tmemset(isNullArray, 0, tupleDescriptor->natts * sizeof(bool));\n\t\theap_deform_tuple(heapTuple, tupleDescriptor, datumArray, isNullArray);\n\t\tOid colocatedTableId = DatumGetObjectId(\n\t\t\tdatumArray[Anum_pg_dist_partition_logicalrelid - 1]);\n\n\t\tcolocatedTableList = lappend_oid(colocatedTableList, colocatedTableId);\n\t\theapTuple = systable_getnext(scanDescriptor);\n\n\t\tif (count == 0)\n\t\t{\n\t\t\t/* fetch all rows */\n\t\t\tcontinue;\n\t\t}\n\t\telse if (list_length(colocatedTableList) >= count)\n\t\t{\n\t\t\t/* we are done */\n\t\t\tbreak;\n\t\t}\n\t}\n\tpfree(datumArray);\n\tpfree(isNullArray);\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistPartition, AccessShareLock);\n\n\treturn colocatedTableList;\n}\n\n\n/*\n * ColocatedShardIntervalList function returns list of shard intervals which are\n * co-located with given shard. If given shard is belong to append or range distributed\n * table, co-location is not valid for that shard. Therefore such shard is only co-located\n * with itself.\n */\nList *\nColocatedShardIntervalList(ShardInterval *shardInterval)\n{\n\tOid distributedTableId = shardInterval->relationId;\n\tList *colocatedShardList = NIL;\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(distributedTableId);\n\n\t/*\n\t * If distribution type of the table is append or range, each shard of\n\t * the shard is only co-located with itself.\n\t */\n\tif (IsCitusTableTypeCacheEntry(cacheEntry, APPEND_DISTRIBUTED) ||\n\t\tIsCitusTableTypeCacheEntry(cacheEntry, RANGE_DISTRIBUTED))\n\t{\n\t\tShardInterval *copyShardInterval = CopyShardInterval(shardInterval);\n\n\t\tcolocatedShardList = lappend(colocatedShardList, copyShardInterval);\n\n\t\treturn colocatedShardList;\n\t}\n\n\tint shardIntervalIndex = ShardIndex(shardInterval);\n\tList *colocatedTableList = ColocatedTableList(distributedTableId);\n\n\t/* ShardIndex have to find index of given shard */\n\tAssert(shardIntervalIndex >= 0);\n\n\tOid colocatedTableId = InvalidOid;\n\tforeach_declared_oid(colocatedTableId, colocatedTableList)\n\t{\n\t\tCitusTableCacheEntry *colocatedTableCacheEntry =\n\t\t\tGetCitusTableCacheEntry(colocatedTableId);\n\n\t\t/*\n\t\t * Since we iterate over co-located tables, shard count of each table should be\n\t\t * same and greater than shardIntervalIndex.\n\t\t */\n\t\tAssert(cacheEntry->shardIntervalArrayLength == colocatedTableCacheEntry->\n\t\t\t   shardIntervalArrayLength);\n\n\t\tShardInterval *colocatedShardInterval =\n\t\t\tcolocatedTableCacheEntry->sortedShardIntervalArray[shardIntervalIndex];\n\n\t\tShardInterval *copyShardInterval = CopyShardInterval(colocatedShardInterval);\n\n\t\tcolocatedShardList = lappend(colocatedShardList, copyShardInterval);\n\t}\n\n\tAssert(list_length(colocatedTableList) == list_length(colocatedShardList));\n\n\treturn SortList(colocatedShardList, CompareShardIntervalsById);\n}\n\n\n/*\n * ColocatedNonPartitionShardIntervalList function returns list of shard intervals\n * which are co-located with given shard, except partitions. If given shard is belong\n * to append or range distributed table, co-location is not valid for that shard.\n * Therefore such shard is only co-located with itself.\n */\nList *\nColocatedNonPartitionShardIntervalList(ShardInterval *shardInterval)\n{\n\tOid distributedTableId = shardInterval->relationId;\n\tList *colocatedShardList = NIL;\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(distributedTableId);\n\n\t/*\n\t * If distribution type of the table is append or range, each shard of the shard\n\t * is only co-located with itself. We don't expect this case to happen, since\n\t * distributing partitioned tables in only supported for hash-distributed tables.\n\t * Therefore, currently we can't cover here with a test.\n\t */\n\tif (IsCitusTableTypeCacheEntry(cacheEntry, APPEND_DISTRIBUTED) ||\n\t\tIsCitusTableTypeCacheEntry(cacheEntry, RANGE_DISTRIBUTED))\n\t{\n\t\tShardInterval *copyShardInterval = CopyShardInterval(shardInterval);\n\n\t\tcolocatedShardList = lappend(colocatedShardList, copyShardInterval);\n\n\t\treturn colocatedShardList;\n\t}\n\n\tereport(DEBUG1, (errmsg(\"skipping child tables for relation named: %s\",\n\t\t\t\t\t\t\tget_rel_name(distributedTableId))));\n\n\tint shardIntervalIndex = ShardIndex(shardInterval);\n\tList *colocatedTableList = ColocatedTableList(distributedTableId);\n\n\t/* ShardIndex have to find index of given shard */\n\tAssert(shardIntervalIndex >= 0);\n\n\tOid colocatedTableId = InvalidOid;\n\tforeach_declared_oid(colocatedTableId, colocatedTableList)\n\t{\n\t\tif (PartitionTable(colocatedTableId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tCitusTableCacheEntry *colocatedTableCacheEntry =\n\t\t\tGetCitusTableCacheEntry(colocatedTableId);\n\n\t\t/*\n\t\t * Since we iterate over co-located tables, shard count of each table should be\n\t\t * same and greater than shardIntervalIndex.\n\t\t */\n\t\tAssert(cacheEntry->shardIntervalArrayLength == colocatedTableCacheEntry->\n\t\t\t   shardIntervalArrayLength);\n\n\t\tShardInterval *colocatedShardInterval =\n\t\t\tcolocatedTableCacheEntry->sortedShardIntervalArray[shardIntervalIndex];\n\n\t\tShardInterval *copyShardInterval = CopyShardInterval(colocatedShardInterval);\n\n\t\tcolocatedShardList = lappend(colocatedShardList, copyShardInterval);\n\t}\n\n\treturn SortList(colocatedShardList, CompareShardIntervalsById);\n}\n\n\n/*\n * ColocatedTableId returns an arbitrary table which belongs to given colocation\n * group. If there is not such a colocation group, it returns invalid oid.\n *\n * This function also takes an AccessShareLock on the co-colocated table to\n * guarantee that the table isn't dropped for the remainder of the transaction.\n */\nOid\nColocatedTableId(int32 colocationId)\n{\n\tOid colocatedTableId = InvalidOid;\n\tbool indexOK = true;\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\n\t/*\n\t * We may have a distributed table whose colocation id is INVALID_COLOCATION_ID.\n\t * In this case, we do not want to send that table's id as colocated table id.\n\t */\n\tif (colocationId == INVALID_COLOCATION_ID)\n\t{\n\t\treturn colocatedTableId;\n\t}\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_partition_colocationid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(colocationId));\n\n\tRelation pgDistPartition = table_open(DistPartitionRelationId(), AccessShareLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition);\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistPartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistPartitionColocationidIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOK, NULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tDatum *datumArray = (Datum *) palloc(tupleDescriptor->natts * sizeof(Datum));\n\tbool *isNullArray = (bool *) palloc(tupleDescriptor->natts * sizeof(bool));\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tmemset(datumArray, 0, tupleDescriptor->natts * sizeof(Datum));\n\t\tmemset(isNullArray, 0, tupleDescriptor->natts * sizeof(bool));\n\t\theap_deform_tuple(heapTuple, tupleDescriptor, datumArray, isNullArray);\n\t\tcolocatedTableId = DatumGetObjectId(\n\t\t\tdatumArray[Anum_pg_dist_partition_logicalrelid - 1]);\n\n\t\t/*\n\t\t * Make sure the relation isn't dropped for the remainder of\n\t\t * the transaction.\n\t\t */\n\t\tLockRelationOid(colocatedTableId, AccessShareLock);\n\n\t\t/*\n\t\t * The relation might have been dropped just before we locked it.\n\t\t * Let's look it up.\n\t\t */\n\t\tRelation colocatedRelation = RelationIdGetRelation(colocatedTableId);\n\t\tif (RelationIsValid(colocatedRelation))\n\t\t{\n\t\t\t/* relation still exists, we can use it */\n\t\t\tRelationClose(colocatedRelation);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* relation was dropped, try the next one */\n\t\tcolocatedTableId = InvalidOid;\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\tpfree(datumArray);\n\tpfree(isNullArray);\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistPartition, AccessShareLock);\n\n\treturn colocatedTableId;\n}\n\n\n/*\n * SingleShardTableColocationNodeId takes a colocation id that presumably\n * belongs to colocation group used to colocate a set of single-shard\n * tables and returns id of the node that stores / is expected to store\n * the shards within the colocation group.\n */\nuint32\nSingleShardTableColocationNodeId(uint32 colocationId)\n{\n\tList *tablesInColocationGroup = ColocationGroupTableList(colocationId, 0);\n\tif (list_length(tablesInColocationGroup) == 0)\n\t{\n\t\tint workerNodeIndex =\n\t\t\tEmptySingleShardTableColocationDecideNodeId(colocationId);\n\t\tList *workerNodeList = DistributedTablePlacementNodeList(RowShareLock);\n\t\tWorkerNode *workerNode = (WorkerNode *) list_nth(workerNodeList, workerNodeIndex);\n\n\t\treturn workerNode->nodeId;\n\t}\n\telse\n\t{\n\t\tOid colocatedTableId = ColocatedTableId(colocationId);\n\t\treturn SingleShardTableGetNodeId(colocatedTableId);\n\t}\n}\n\n\n/*\n * SingleShardTableGetNodeId returns id of the node that stores shard of\n * given single-shard table.\n */\nstatic uint32\nSingleShardTableGetNodeId(Oid relationId)\n{\n\tif (!IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED))\n\t{\n\t\tereport(ERROR, (errmsg(\"table is not a single-shard distributed table\")));\n\t}\n\n\tint64 shardId = GetFirstShardId(relationId);\n\tList *shardPlacementList = ShardPlacementList(shardId);\n\tif (list_length(shardPlacementList) != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"table shard does not have a single shard placement\")));\n\t}\n\n\treturn ((ShardPlacement *) linitial(shardPlacementList))->nodeId;\n}\n\n\n/*\n * ColocatedShardIdInRelation returns shardId of the shard from given relation, so that\n * returned shard is co-located with given shard.\n */\nuint64\nColocatedShardIdInRelation(Oid relationId, int shardIndex)\n{\n\tCitusTableCacheEntry *tableCacheEntry = GetCitusTableCacheEntry(relationId);\n\n\treturn tableCacheEntry->sortedShardIntervalArray[shardIndex]->shardId;\n}\n\n\n/*\n * DeleteColocationGroupIfNoTablesBelong function deletes given co-location group if there\n * is no relation in that co-location group. A co-location group may become empty after\n * update_distributed_table_colocation UDF calls. In that case we need to\n * remove empty co-location group to prevent orphaned co-location groups.\n */\nvoid\nDeleteColocationGroupIfNoTablesBelong(uint32 colocationId)\n{\n\tif (colocationId != INVALID_COLOCATION_ID)\n\t{\n\t\tint count = 1;\n\t\tList *colocatedTableList = ColocationGroupTableList(colocationId, count);\n\t\tint colocatedTableCount = list_length(colocatedTableList);\n\n\t\tif (colocatedTableCount == 0)\n\t\t{\n\t\t\tDeleteColocationGroup(colocationId);\n\t\t}\n\t}\n}\n\n\n/*\n * DeleteColocationGroup deletes the colocation group from pg_dist_colocation\n * throughout the cluster and dissociates the tenant schema if any.\n */\nvoid\nDeleteColocationGroup(uint32 colocationId)\n{\n\tDeleteColocationGroupLocally(colocationId);\n\tSyncDeleteColocationGroupToNodes(colocationId);\n}\n\n\n/*\n * DeleteColocationGroupLocally deletes the colocation group from pg_dist_colocation.\n */\nvoid\nDeleteColocationGroupLocally(uint32 colocationId)\n{\n\tint scanKeyCount = 1;\n\tScanKeyData scanKey[1];\n\tbool indexOK = false;\n\n\tRelation pgDistColocation = table_open(DistColocationRelationId(), RowExclusiveLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_colocation_colocationid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(colocationId));\n\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistColocation, InvalidOid, indexOK,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\t/* if a record is found, delete it */\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\t/*\n\t\t * simple_heap_delete() expects that the caller has at least an\n\t\t * AccessShareLock on primary key index.\n\t\t *\n\t\t * XXX: This does not seem required, do we really need to acquire this lock?\n\t\t * Postgres doesn't acquire such locks on indexes before deleting catalog tuples.\n\t\t * Linking here the reasons we added this lock acquirement:\n\t\t * https://github.com/citusdata/citus/pull/2851#discussion_r306569462\n\t\t * https://github.com/citusdata/citus/pull/2855#discussion_r313628554\n\t\t * https://github.com/citusdata/citus/issues/1890\n\t\t */\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t\t/* PG 18+ expects a second “deferrable_ok” flag */\n\t\tRelation replicaIndex = index_open(\n\t\t\tRelationGetPrimaryKeyIndex(pgDistColocation, false),\n\t\t\tAccessShareLock\n\t\t\t);\n#else\n\n\t\t/* PG 17- had a single-arg signature */\n\t\tRelation replicaIndex = index_open(\n\t\t\tRelationGetPrimaryKeyIndex(pgDistColocation),\n\t\t\tAccessShareLock\n\t\t\t);\n#endif\n\t\tsimple_heap_delete(pgDistColocation, &(heapTuple->t_self));\n\n\t\tCitusInvalidateRelcacheByRelid(DistColocationRelationId());\n\t\tCommandCounterIncrement();\n\t\ttable_close(replicaIndex, AccessShareLock);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistColocation, NoLock);\n}\n\n\n/*\n * FindColocateWithColocationId tries to find a colocation ID for a given\n * colocate_with clause passed to create_distributed_table.\n */\nuint32\nFindColocateWithColocationId(Oid relationId, char replicationModel,\n\t\t\t\t\t\t\t Var *distributionColumn,\n\t\t\t\t\t\t\t int shardCount, bool shardCountIsStrict,\n\t\t\t\t\t\t\t char *colocateWithTableName)\n{\n\tuint32 colocationId = INVALID_COLOCATION_ID;\n\n\tif (IsColocateWithDefault(colocateWithTableName))\n\t{\n\t\t/* distributionColumn can only be null for single-shard tables */\n\t\tOid distributionColumnType =\n\t\t\tdistributionColumn ? distributionColumn->vartype : InvalidOid;\n\t\tOid distributionColumnCollation =\n\t\t\tdistributionColumn ? distributionColumn->varcollid : InvalidOid;\n\n\t\t/* check for default colocation group */\n\t\tcolocationId = ColocationId(shardCount, ShardReplicationFactor,\n\t\t\t\t\t\t\t\t\tdistributionColumnType,\n\t\t\t\t\t\t\t\t\tdistributionColumnCollation);\n\n\t\t/*\n\t\t * if the shardCount is strict then we check if the shard count\n\t\t * of the colocated table is actually shardCount\n\t\t */\n\t\tif (shardCountIsStrict && colocationId != INVALID_COLOCATION_ID)\n\t\t{\n\t\t\tOid colocatedTableId = ColocatedTableId(colocationId);\n\n\t\t\tif (colocatedTableId != InvalidOid)\n\t\t\t{\n\t\t\t\tCitusTableCacheEntry *cacheEntry =\n\t\t\t\t\tGetCitusTableCacheEntry(colocatedTableId);\n\t\t\t\tint colocatedTableShardCount = cacheEntry->shardIntervalArrayLength;\n\n\t\t\t\tif (colocatedTableShardCount != shardCount)\n\t\t\t\t{\n\t\t\t\t\tcolocationId = INVALID_COLOCATION_ID;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\telse if (!IsColocateWithNone(colocateWithTableName))\n\t{\n\t\ttext *colocateWithTableNameText = cstring_to_text(colocateWithTableName);\n\t\tOid sourceRelationId = ResolveRelationId(colocateWithTableNameText, false);\n\n\t\tEnsureTableCanBeColocatedWith(relationId, replicationModel,\n\t\t\t\t\t\t\t\t\t  distributionColumn, sourceRelationId);\n\n\t\tcolocationId = TableColocationId(sourceRelationId);\n\t}\n\n\treturn colocationId;\n}\n\n\n/*\n * EnsureTableCanBeColocatedWith checks whether a given replication model and\n * distribution column type is suitable to distribute a table to be colocated\n * with given source table.\n *\n * We only pass relationId to provide meaningful error messages.\n */\nvoid\nEnsureTableCanBeColocatedWith(Oid relationId, char replicationModel,\n\t\t\t\t\t\t\t  Var *distributionColumn, Oid sourceRelationId)\n{\n\tCitusTableCacheEntry *sourceTableEntry = GetCitusTableCacheEntry(sourceRelationId);\n\n\tif (IsCitusTableTypeCacheEntry(sourceTableEntry, APPEND_DISTRIBUTED) ||\n\t\tIsCitusTableTypeCacheEntry(sourceTableEntry, RANGE_DISTRIBUTED) ||\n\t\tIsCitusTableTypeCacheEntry(sourceTableEntry, CITUS_LOCAL_TABLE))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot distribute relation\"),\n\t\t\t\t\t\terrdetail(\"Currently, colocate_with option is not supported \"\n\t\t\t\t\t\t\t\t  \"with append / range distributed tables and local \"\n\t\t\t\t\t\t\t\t  \"tables added to metadata.\")));\n\t}\n\n\tchar sourceReplicationModel = sourceTableEntry->replicationModel;\n\tif (sourceReplicationModel != replicationModel)\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\t\tchar *sourceRelationName = get_rel_name(sourceRelationId);\n\n\t\tereport(ERROR, (errmsg(\"cannot colocate tables %s and %s\",\n\t\t\t\t\t\t\t   sourceRelationName, relationName),\n\t\t\t\t\t\terrdetail(\"Replication models don't match for %s and %s.\",\n\t\t\t\t\t\t\t\t  sourceRelationName, relationName)));\n\t}\n\n\tVar *sourceDistributionColumn = DistPartitionKey(sourceRelationId);\n\tEnsureColumnTypeEquality(sourceRelationId, relationId,\n\t\t\t\t\t\t\t sourceDistributionColumn, distributionColumn);\n\n\t/* prevent colocating regular tables with tenant tables */\n\tOid sourceRelationSchemaId = get_rel_namespace(sourceRelationId);\n\tOid targetRelationSchemaId = get_rel_namespace(relationId);\n\tif (IsTenantSchema(sourceRelationSchemaId) &&\n\t\tsourceRelationSchemaId != targetRelationSchemaId)\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\t\tchar *sourceRelationName = get_rel_name(sourceRelationId);\n\t\tchar *sourceRelationSchemaName = get_namespace_name(sourceRelationSchemaId);\n\n\t\tereport(ERROR, (errmsg(\"cannot colocate tables %s and %s\",\n\t\t\t\t\t\t\t   sourceRelationName, relationName),\n\t\t\t\t\t\terrdetail(\"Cannot colocate tables with distributed schema tables\"\n\t\t\t\t\t\t\t\t  \" by using colocate_with option.\"),\n\t\t\t\t\t\terrhint(\"Consider using \\\"CREATE TABLE\\\" statement \"\n\t\t\t\t\t\t\t\t\"to create this table as a single-shard distributed \"\n\t\t\t\t\t\t\t\t\"table in the same schema to automatically colocate \"\n\t\t\t\t\t\t\t\t\"it with %s.%s\",\n\t\t\t\t\t\t\t\tsourceRelationSchemaName, sourceRelationName)));\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/directory.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * directory.c\n *\n * Utility functions for dealing with directories.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n#include \"distributed/utils/directory.h\"\n\n\n/* Local functions forward declarations */\nstatic bool FileIsLink(const char *filename, struct stat filestat);\n\n\n/*\n * CitusCreateDirectory creates a new directory with the given directory name.\n */\nvoid\nCitusCreateDirectory(StringInfo directoryName)\n{\n\tint makeOK = MakePGDirectory(directoryName->data);\n\tif (makeOK != 0)\n\t{\n\t\tereport(ERROR, (errcode_for_file_access(),\n\t\t\t\t\t\terrmsg(\"could not create directory \\\"%s\\\": %m\",\n\t\t\t\t\t\t\t   directoryName->data)));\n\t}\n}\n\n\n/*\n * FileIsLink checks whether a file is a symbolic link.\n */\nstatic bool\nFileIsLink(const char *filename, struct stat filestat)\n{\n\treturn S_ISLNK(filestat.st_mode);\n}\n\n\n/*\n * CitusRemoveDirectory first checks if the given directory exists. If it does, the\n * function recursively deletes the contents of the given directory, and then\n * deletes the directory itself. This function is modeled on the Boost file\n * system library's remove_all() method.\n */\nvoid\nCitusRemoveDirectory(const char *filename)\n{\n\t/* files may be added during execution, loop when that occurs */\n\twhile (true)\n\t{\n\t\tstruct stat fileStat;\n\t\tint removed = 0;\n\n\t\tint statOK = stat(filename, &fileStat);\n\t\tif (statOK < 0)\n\t\t{\n\t\t\tif (errno == ENOENT)\n\t\t\t{\n\t\t\t\treturn;  /* if file does not exist, return */\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode_for_file_access(),\n\t\t\t\t\t\t\t\terrmsg(\"could not stat file \\\"%s\\\": %m\", filename)));\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * If this is a directory, iterate over all its contents and for each\n\t\t * content, recurse into this function. Also, make sure that we do not\n\t\t * recurse into symbolic links.\n\t\t */\n\t\tif (S_ISDIR(fileStat.st_mode) && !FileIsLink(filename, fileStat))\n\t\t{\n\t\t\tconst char *directoryName = filename;\n\n\t\t\tDIR *directory = AllocateDir(directoryName);\n\t\t\tif (directory == NULL)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode_for_file_access(),\n\t\t\t\t\t\t\t\terrmsg(\"could not open directory \\\"%s\\\": %m\",\n\t\t\t\t\t\t\t\t\t   directoryName)));\n\t\t\t}\n\n\t\t\tStringInfo fullFilename = makeStringInfo();\n\t\t\tstruct dirent *directoryEntry = ReadDir(directory, directoryName);\n\t\t\tfor (; directoryEntry != NULL; directoryEntry = ReadDir(directory,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdirectoryName))\n\t\t\t{\n\t\t\t\tconst char *baseFilename = directoryEntry->d_name;\n\n\t\t\t\t/* if system file, skip it */\n\t\t\t\tif (strncmp(baseFilename, \".\", MAXPGPATH) == 0 ||\n\t\t\t\t\tstrncmp(baseFilename, \"..\", MAXPGPATH) == 0)\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tresetStringInfo(fullFilename);\n\t\t\t\tappendStringInfo(fullFilename, \"%s/%s\", directoryName, baseFilename);\n\n\t\t\t\tCitusRemoveDirectory(fullFilename->data);\n\t\t\t}\n\n\t\t\tpfree(fullFilename->data);\n\t\t\tpfree(fullFilename);\n\t\t\tFreeDir(directory);\n\t\t}\n\n\t\t/* we now have an empty directory or a regular file, remove it */\n\t\tif (S_ISDIR(fileStat.st_mode))\n\t\t{\n\t\t\t/*\n\t\t\t * We ignore the TOCTUO race condition static analysis warning\n\t\t\t * here, since we don't actually read the files or directories. We\n\t\t\t * simply want to remove them.\n\t\t\t */\n\t\t\tremoved = rmdir(filename); /* lgtm[cpp/toctou-race-condition] */\n\n\t\t\tif (errno == ENOTEMPTY || errno == EEXIST)\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/*\n\t\t\t * We ignore the TOCTUO race condition static analysis warning\n\t\t\t * here, since we don't actually read the files or directories. We\n\t\t\t * simply want to remove them.\n\t\t\t */\n\t\t\tremoved = unlink(filename); /* lgtm[cpp/toctou-race-condition] */\n\t\t}\n\n\t\tif (removed != 0 && errno != ENOENT)\n\t\t{\n\t\t\tereport(ERROR, (errcode_for_file_access(),\n\t\t\t\t\t\t\terrmsg(\"could not remove file \\\"%s\\\": %m\", filename)));\n\t\t}\n\n\t\treturn;\n\t}\n}\n\n\n/*\n * CleanupJobCacheDirectory cleans up all files in the job cache directory\n * as part of this process's start-up logic.\n */\nvoid\nCleanupJobCacheDirectory(void)\n{\n\t/* use the default tablespace in {datadir}/base */\n\tStringInfo jobCacheDirectory = makeStringInfo();\n\tappendStringInfo(jobCacheDirectory, \"base/%s\", PG_JOB_CACHE_DIR);\n\n\tCitusRemoveDirectory(jobCacheDirectory->data);\n\tCitusCreateDirectory(jobCacheDirectory);\n\n\tpfree(jobCacheDirectory->data);\n\tpfree(jobCacheDirectory);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/distribution_column.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * distribution_column.c\n *\n * This file contains functions for translating distribution columns in\n * metadata tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/attnum.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/primnodes.h\"\n#include \"parser/parse_relation.h\"\n#include \"parser/scansup.h\"\n#include \"utils/builtins.h\"\n#include \"utils/elog.h\"\n#include \"utils/errcodes.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/relcache.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/distribution_column.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/version_compat.h\"\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(column_name_to_column);\nPG_FUNCTION_INFO_V1(column_name_to_column_id);\nPG_FUNCTION_INFO_V1(column_to_column_name);\n\n\n/*\n * column_name_to_column is an internal UDF to obtain a textual representation\n * of a particular column node (Var), given a relation identifier and column\n * name. There is no requirement that the table be distributed; this function\n * simply returns the textual representation of a Var representing a column.\n * This function will raise an ERROR if no such column can be found or if the\n * provided name refers to a system column.\n */\nDatum\ncolumn_name_to_column(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\ttext *columnText = PG_GETARG_TEXT_P(1);\n\tchar *columnName = text_to_cstring(columnText);\n\n\tVar *column = BuildDistributionKeyFromColumnName(relationId, columnName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t AccessShareLock);\n\tAssert(column != NULL);\n\tchar *columnNodeString = nodeToString(column);\n\ttext *columnNodeText = cstring_to_text(columnNodeString);\n\n\tPG_RETURN_TEXT_P(columnNodeText);\n}\n\n\n/*\n * column_name_to_column_id takes a relation identifier and a name of a column\n * in that relation and returns the index of that column in the relation. If\n * the provided name is a system column or no column at all, this function will\n * throw an error instead.\n */\nDatum\ncolumn_name_to_column_id(PG_FUNCTION_ARGS)\n{\n\tOid distributedTableId = PG_GETARG_OID(0);\n\tchar *columnName = PG_GETARG_CSTRING(1);\n\n\tVar *column = BuildDistributionKeyFromColumnName(distributedTableId, columnName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t AccessExclusiveLock);\n\tAssert(column != NULL);\n\n\tPG_RETURN_INT16((int16) column->varattno);\n}\n\n\n/*\n * column_to_column_name is an internal UDF to obtain the human-readable name\n * of a column given a relation identifier and the column's internal textual\n * (Var) representation. This function will raise an ERROR if no such column\n * can be found or if the provided Var refers to a system column.\n */\nDatum\ncolumn_to_column_name(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\ttext *columnNodeText = PG_GETARG_TEXT_P(1);\n\n\tchar *columnNodeString = text_to_cstring(columnNodeText);\n\tNode *columnNode = stringToNode(columnNodeString);\n\n\tchar *columnName = ColumnToColumnName(relationId, columnNode);\n\n\ttext *columnText = cstring_to_text(columnName);\n\n\tPG_RETURN_TEXT_P(columnText);\n}\n\n\n/*\n * BuildDistributionKeyFromColumnName builds a simple distribution key consisting\n * only out of a reference to the column of name columnName. Errors out if the\n * specified column does not exist or is not suitable to be used as a\n * distribution column.\n *\n * The function returns NULL if the passed column name is NULL. That case only\n * corresponds to reference tables.\n */\nVar *\nBuildDistributionKeyFromColumnName(Oid relationId, char *columnName, LOCKMODE lockMode)\n{\n\tRelation relation = try_relation_open(relationId, lockMode);\n\n\tif (relation == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"relation does not exist\")));\n\t}\n\n\trelation_close(relation, NoLock);\n\n\tchar *tableName = get_rel_name(relationId);\n\n\t/* short circuit for reference tables and single-shard tables */\n\tif (columnName == NULL)\n\t{\n\t\treturn NULL;\n\t}\n\n\t/* it'd probably better to downcase identifiers consistent with SQL case folding */\n\ttruncate_identifier(columnName, strlen(columnName), true);\n\n\t/* lookup column definition */\n\tHeapTuple columnTuple = SearchSysCacheAttName(relationId, columnName);\n\tif (!HeapTupleIsValid(columnTuple))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN),\n\t\t\t\t\t\terrmsg(\"column \\\"%s\\\" of relation \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t   columnName, tableName)));\n\t}\n\n\tForm_pg_attribute columnForm = (Form_pg_attribute) GETSTRUCT(columnTuple);\n\n\t/* check if the column may be referenced in the distribution key */\n\tif (columnForm->attnum <= 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot reference system column \\\"%s\\\" in relation \\\"%s\\\"\",\n\t\t\t\t\t\t\t   columnName, tableName)));\n\t}\n\n\t/* build Var referencing only the chosen distribution column */\n\tVar *distributionColumn = makeVar(1, columnForm->attnum, columnForm->atttypid,\n\t\t\t\t\t\t\t\t\t  columnForm->atttypmod, columnForm->attcollation, 0);\n\n\tReleaseSysCache(columnTuple);\n\n\treturn distributionColumn;\n}\n\n\n/*\n * EnsureValidDistributionColumn Errors out if the\n * specified column does not exist or is not suitable to be used as a\n * distribution column. It does not hold locks.\n */\nvoid\nEnsureValidDistributionColumn(Oid relationId, char *columnName)\n{\n\tRelation relation = try_relation_open(relationId, AccessShareLock);\n\n\tif (relation == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"relation does not exist\")));\n\t}\n\n\tchar *tableName = get_rel_name(relationId);\n\n\t/* it'd probably better to downcase identifiers consistent with SQL case folding */\n\ttruncate_identifier(columnName, strlen(columnName), true);\n\n\t/* lookup column definition */\n\tHeapTuple columnTuple = SearchSysCacheAttName(relationId, columnName);\n\tif (!HeapTupleIsValid(columnTuple))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN),\n\t\t\t\t\t\terrmsg(\"column \\\"%s\\\" of relation \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t   columnName, tableName)));\n\t}\n\n\tForm_pg_attribute columnForm = (Form_pg_attribute) GETSTRUCT(columnTuple);\n\n\t/* check if the column may be referenced in the distribution key */\n\tif (columnForm->attnum <= 0)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot reference system column \\\"%s\\\" in relation \\\"%s\\\"\",\n\t\t\t\t\t\t\t   columnName, tableName)));\n\t}\n\n\tReleaseSysCache(columnTuple);\n\n\trelation_close(relation, AccessShareLock);\n}\n\n\n/*\n * ColumnTypeIdForRelationColumnName returns type id for the given relation's column name.\n */\nOid\nColumnTypeIdForRelationColumnName(Oid relationId, char *columnName)\n{\n\tAssert(columnName != NULL);\n\n\tAttrNumber attNum = get_attnum(relationId, columnName);\n\n\tif (attNum == InvalidAttrNumber)\n\t{\n\t\tereport(ERROR, (errmsg(\"invalid attr %s\", columnName)));\n\t}\n\n\tRelation relation = relation_open(relationId, AccessShareLock);\n\n\tOid typeId = attnumTypeId(relation, attNum);\n\n\trelation_close(relation, AccessShareLock);\n\n\treturn typeId;\n}\n\n\n/*\n * ColumnToColumnName returns the human-readable name of a column given a\n * relation identifier and the column's internal (Var) representation.\n * This function will raise an ERROR if no such column can be found or if the\n * provided Var refers to a system column.\n */\nchar *\nColumnToColumnName(Oid relationId, Node *columnNode)\n{\n\tif (columnNode == NULL || !IsA(columnNode, Var))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"not a valid column\")));\n\t}\n\n\tVar *column = (Var *) columnNode;\n\n\tAttrNumber columnNumber = column->varattno;\n\tif (!AttrNumberIsForUserDefinedAttr(columnNumber))\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),\n\t\t\t\t\t\terrmsg(\"attribute %d of relation \\\"%s\\\" is a system column\",\n\t\t\t\t\t\t\t   columnNumber, relationName)));\n\t}\n\n\tchar *columnName = get_attname(relationId, column->varattno, false);\n\tif (columnName == NULL)\n\t{\n\t\tchar *relationName = get_rel_name(relationId);\n\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN),\n\t\t\t\t\t\terrmsg(\"attribute %d of relation \\\"%s\\\" does not exist\",\n\t\t\t\t\t\t\t   columnNumber, relationName)));\n\t}\n\n\treturn columnName;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/distribution_column_map.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * distribution_column_map.c\n *\t  Implementation of a relation OID to distribution column map.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"common/hashfn.h\"\n#include \"nodes/primnodes.h\"\n\n#include \"distributed/distribution_column.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/utils/distribution_column_map.h\"\n\n\n/*\n * RelationIdDistributionColumnMapEntry is used to map relation IDs to\n * distribution column Vars.\n */\ntypedef struct RelationIdDistributionColumnMapEntry\n{\n\t/* OID of the relation */\n\tOid relationId;\n\n\t/* a Var describing the distribution column */\n\tVar *distributionColumn;\n} RelationIdDistributionColumnMapEntry;\n\n\n/*\n * CreateDistributionColumnMap creates an empty (OID -> distribution column Var) map.\n */\nDistributionColumnMap *\nCreateDistributionColumnMap(void)\n{\n\tHASHCTL info = { 0 };\n\tinfo.keysize = sizeof(Oid);\n\tinfo.entrysize = sizeof(RelationIdDistributionColumnMapEntry);\n\tinfo.hash = oid_hash;\n\tinfo.hcxt = CurrentMemoryContext;\n\n\tuint32 hashFlags = (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);\n\n\tHTAB *distributionColumnMap = hash_create(\"Distribution Column Map\", 32,\n\t\t\t\t\t\t\t\t\t\t\t  &info, hashFlags);\n\n\treturn distributionColumnMap;\n}\n\n\n/*\n * AddDistributionColumnForRelation adds the given OID and its distribution column\n * to the hash, as well as any child partitions.\n */\nvoid\nAddDistributionColumnForRelation(DistributionColumnMap *distributionColumnMap,\n\t\t\t\t\t\t\t\t Oid relationId,\n\t\t\t\t\t\t\t\t char *distributionColumnName)\n{\n\tbool entryFound = false;\n\tRelationIdDistributionColumnMapEntry *entry =\n\t\thash_search(distributionColumnMap, &relationId, HASH_ENTER, &entryFound);\n\n\tAssert(!entryFound);\n\n\tentry->distributionColumn =\n\t\tBuildDistributionKeyFromColumnName(relationId, distributionColumnName, NoLock);\n\n\tif (PartitionedTable(relationId))\n\t{\n\t\t/*\n\t\t * Recursively add partitions as well.\n\t\t */\n\t\tList *partitionList = PartitionList(relationId);\n\t\tOid partitionRelationId = InvalidOid;\n\n\t\tforeach_declared_oid(partitionRelationId, partitionList)\n\t\t{\n\t\t\tAddDistributionColumnForRelation(distributionColumnMap, partitionRelationId,\n\t\t\t\t\t\t\t\t\t\t\t distributionColumnName);\n\t\t}\n\t}\n}\n\n\n/*\n * GetDistributionColumnFromMap returns the distribution column for a given\n * relation ID from the distribution column map.\n */\nVar *\nGetDistributionColumnFromMap(DistributionColumnMap *distributionColumnMap,\n\t\t\t\t\t\t\t Oid relationId)\n{\n\tbool entryFound = false;\n\n\tRelationIdDistributionColumnMapEntry *entry =\n\t\thash_search(distributionColumnMap, &relationId, HASH_FIND, &entryFound);\n\n\tif (entryFound)\n\t{\n\t\treturn entry->distributionColumn;\n\t}\n\telse\n\t{\n\t\treturn NULL;\n\t}\n}\n\n\n/*\n * GetDistributionColumnWithOverrides returns the distribution column for a given\n * relation from the distribution column overrides map, or the metadata if no\n * override is specified.\n */\nVar *\nGetDistributionColumnWithOverrides(Oid relationId,\n\t\t\t\t\t\t\t\t   DistributionColumnMap *distributionColumnOverrides)\n{\n\tVar *distributionColumn = NULL;\n\n\tif (distributionColumnOverrides != NULL)\n\t{\n\t\tdistributionColumn = GetDistributionColumnFromMap(distributionColumnOverrides,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  relationId);\n\t\tif (distributionColumn != NULL)\n\t\t{\n\t\t\treturn distributionColumn;\n\t\t}\n\t}\n\n\t/* no override defined, use distribution column from metadata */\n\treturn DistPartitionKey(relationId);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/enable_ssl.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * enable_ssl.c\n *    UDF and Utilities for enabling ssl during citus setup\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n\n#include \"postgres.h\"\n\n/*\n * Make sure that functions marked as deprecated in OpenSSL 3.0 don't trigger\n * deprecation warnings by indicating that we're using the OpenSSL 1.0.1\n * compatibile API. Postgres does this by already in PG14, so we should not do\n * it otherwise we get warnings about redefining this value. This needs to be\n * done before including libpq.h.\n */\n#include \"miscadmin.h\"\n\n#include \"libpq/libpq.h\"\n#include \"nodes/parsenodes.h\"\n#include \"postmaster/postmaster.h\"\n#include \"utils/guc.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/connection_management.h\"\n#include \"distributed/memutils.h\"\n#include \"distributed/worker_protocol.h\"\n\n#ifdef USE_OPENSSL\n#include \"openssl/dsa.h\"\n#include \"openssl/err.h\"\n#include \"openssl/pem.h\"\n#include \"openssl/rsa.h\"\n#include \"openssl/ssl.h\"\n#include \"openssl/x509.h\"\n#endif\n\n\n#define ENABLE_SSL_QUERY \"ALTER SYSTEM SET ssl TO on;\"\n#define RESET_CITUS_NODE_CONNINFO \\\n\t\t\"ALTER SYSTEM SET citus.node_conninfo TO 'sslmode=prefer';\"\n\n#define CITUS_AUTO_SSL_COMMON_NAME \"citus-auto-ssl\"\n#define X509_SUBJECT_COMMON_NAME \"CN\"\n\n#define POSTGRES_DEFAULT_SSL_CIPHERS \"HIGH:MEDIUM:+3DES:!aNULL\"\n\n/*\n * Microsoft approved cipher string.\n * This cipher string implicitely enables only TLSv1.2+, because these ciphers\n * were all added in TLSv1.2. This can be confirmed by running:\n * openssl -v <below strings concatenated>\n */\n#define CITUS_DEFAULT_SSL_CIPHERS \"ECDHE-ECDSA-AES128-GCM-SHA256:\" \\\n\t\t\t\t\t\t\t\t  \"ECDHE-ECDSA-AES256-GCM-SHA384:\" \\\n\t\t\t\t\t\t\t\t  \"ECDHE-RSA-AES128-GCM-SHA256:\" \\\n\t\t\t\t\t\t\t\t  \"ECDHE-RSA-AES256-GCM-SHA384:\" \\\n\t\t\t\t\t\t\t\t  \"ECDHE-ECDSA-AES128-SHA256:\" \\\n\t\t\t\t\t\t\t\t  \"ECDHE-ECDSA-AES256-SHA384:\" \\\n\t\t\t\t\t\t\t\t  \"ECDHE-RSA-AES128-SHA256:\" \\\n\t\t\t\t\t\t\t\t  \"ECDHE-RSA-AES256-SHA384\"\n#define SET_CITUS_SSL_CIPHERS_QUERY \\\n\t\t\"ALTER SYSTEM SET ssl_ciphers TO '\" CITUS_DEFAULT_SSL_CIPHERS \"';\"\n\n\n/* forward declaration of helper functions */\nstatic void GloballyReloadConfig(void);\n\n\n#ifdef USE_SSL\n\n/* forward declaration of functions used when compiled with ssl */\nstatic bool ShouldUseAutoSSL(void);\nstatic bool CreateCertificatesWhenNeeded(void);\nstatic EVP_PKEY * GeneratePrivateKey(void);\nstatic X509 * CreateCertificate(EVP_PKEY *privateKey);\nstatic bool StoreCertificate(EVP_PKEY *privateKey, X509 *certificate);\n#endif /* USE_SSL */\n\n\nPG_FUNCTION_INFO_V1(citus_setup_ssl);\nPG_FUNCTION_INFO_V1(citus_check_defaults_for_sslmode);\n\n\n/*\n * citus_setup_ssl is called during the first creation of a citus extension. It configures\n * postgres to use ssl if not already on. During this process it will create certificates\n * if they are not already installed in the configured location.\n */\nDatum\ncitus_setup_ssl(PG_FUNCTION_ARGS)\n{\n#ifndef USE_SSL\n\tereport(WARNING, (errmsg(\"can not setup ssl on postgres that is not compiled with \"\n\t\t\t\t\t\t\t \"ssl support\")));\n#else /* USE_SSL */\n\tif (!EnableSSL && ShouldUseAutoSSL())\n\t{\n\t\tereport(LOG, (errmsg(\"citus extension created on postgres without ssl enabled, \"\n\t\t\t\t\t\t\t \"turning it on during creation of the extension\")));\n\n\t\t/* execute the alter system statement to enable ssl on within postgres */\n\t\tNode *enableSSLParseTree = ParseTreeNode(ENABLE_SSL_QUERY);\n\t\tAlterSystemSetConfigFile((AlterSystemStmt *) enableSSLParseTree);\n\n\t\tif (strcmp(SSLCipherSuites, POSTGRES_DEFAULT_SSL_CIPHERS) == 0)\n\t\t{\n\t\t\t/*\n\t\t\t * postgres default cipher suite is configured, these allow TSL 1 and TLS 1.1,\n\t\t\t * citus will upgrade to TLS1.2+HIGH and above.\n\t\t\t */\n\t\t\tNode *citusSSLCiphersParseTree = ParseTreeNode(SET_CITUS_SSL_CIPHERS_QUERY);\n\t\t\tAlterSystemSetConfigFile((AlterSystemStmt *) citusSSLCiphersParseTree);\n\t\t}\n\n\t\t/*\n\t\t * ssl=on requires that a key and certificate are present, since we have\n\t\t * enabled ssl mode here chances are the user didn't install credentials already.\n\t\t *\n\t\t * This function will check if they are available and if not it will generate a\n\t\t * self singed certificate.\n\t\t */\n\t\tCreateCertificatesWhenNeeded();\n\n\t\tGloballyReloadConfig();\n\t}\n#endif /* USE_SSL */\n\n\tPG_RETURN_NULL();\n}\n\n\n/*\n * citus_check_defaults_for_sslmode is called in the extension upgrade path when\n * users upgrade from a previous version to a version that has ssl enabled by default, and\n * only when the changed default value conflicts with the setup of the user.\n *\n * Once it is determined that the default value for citus.node_conninfo is used verbatim\n * with ssl not enabled on the cluster it will reinstate the old default value for\n * citus.node_conninfo.\n *\n * In effect this is to not impose the overhead of ssl on an already existing cluster that\n * didn't have it enabled already.\n */\nDatum\ncitus_check_defaults_for_sslmode(PG_FUNCTION_ARGS)\n{\n\tbool configChanged = false;\n\n\tif (EnableSSL)\n\t{\n\t\t/* since ssl is on we do not have to change any sslmode back to prefer */\n\t\tPG_RETURN_NULL();\n\t}\n\n\t/*\n\t * test if the node_conninfo setting is exactly set to the default value used when\n\t * Citus started to enable SSL. This is to make sure upgrades restores the previous\n\t * value so users will not have unexpected changes during upgrades.\n\t */\n\tif (strcmp(NodeConninfo, \"sslmode=require\") == 0)\n\t{\n\t\t/* execute the alter system statement to reset node_conninfo to the old default */\n\n\t\tereport(LOG, (errmsg(\"reset citus.node_conninfo to old default value as the new \"\n\t\t\t\t\t\t\t \"value is incompatible with the current ssl setting\")));\n\n\t\tNode *resetCitusNodeConnInfoParseTree = ParseTreeNode(RESET_CITUS_NODE_CONNINFO);\n\t\tAlterSystemSetConfigFile((AlterSystemStmt *) resetCitusNodeConnInfoParseTree);\n\t\tconfigChanged = true;\n\t}\n\n\tif (configChanged)\n\t{\n\t\tGloballyReloadConfig();\n\t}\n\n\tPG_RETURN_NULL();\n}\n\n\n/*\n * GloballyReloadConfig signals postmaster to reload the configuration as well as\n * reloading the configuration in the current backend. By reloading the configuration in\n * the current backend the changes will also be reflected in the current transaction.\n */\nstatic void\nGloballyReloadConfig()\n{\n\tif (kill(PostmasterPid, SIGHUP))\n\t{\n\t\tereport(WARNING, (errmsg(\"failed to send signal to postmaster: %m\")));\n\t}\n\tProcessConfigFile(PGC_SIGHUP);\n}\n\n\n#ifdef USE_SSL\n\n\n/*\n * ShouldUseAutoSSL checks if citus should enable ssl based on the connection settings it\n * uses for outward connections. When the outward connection is configured to require ssl\n * it assumes the other nodes in the network have the same setting and therefore it will\n * automatically enable ssl during installation.\n */\nstatic bool\nShouldUseAutoSSL(void)\n{\n\tconst char *sslmode = NULL;\n\tsslmode = GetConnParam(\"sslmode\");\n\n\tif (sslmode != NULL && strcmp(sslmode, \"require\") == 0)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * CreateCertificatesWhenNeeded checks if the certificates exists. When they don't exist\n * they will be created. The return value tells whether or not new certificates have been\n * created. After this function it is guaranteed that certificates are in place. It is not\n * guaranteed they have the right permissions as we will not touch the keys if they exist.\n */\nstatic bool\nCreateCertificatesWhenNeeded()\n{\n\tEVP_PKEY *privateKey = NULL;\n\tX509 *certificate = NULL;\n\tbool certificateWritten = false;\n\tSSL_CTX *sslContext = NULL;\n\n\t/*\n\t * Ensure the OpenSSL library is initialized so we can create our SSL context.\n\t * On OpenSSL ≥ 1.1.0 we call OPENSSL_init_ssl() (which also loads the default\n\t * config), and on older versions we fall back to SSL_library_init().\n\t * PostgreSQL itself will perform its full SSL setup when it reloads\n\t * its configuration with ssl enabled.\n\t */\n#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L\n\n\t/* OpenSSL 1.1.0+ */\n\tOPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);\n#else\n\n\t/* OpenSSL < 1.1.0 */\n\tSSL_library_init();\n#endif\n\n#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L\n\tsslContext = SSL_CTX_new(TLS_method());\n#else\n\tsslContext = SSL_CTX_new(SSLv23_method());\n#endif\n\n\tif (!sslContext)\n\t{\n\t\tereport(WARNING, (errmsg(\"unable to create ssl context, please verify ssl \"\n\t\t\t\t\t\t\t\t \"settings for postgres\"),\n\t\t\t\t\t\t  errdetail(\"Citus could not create the ssl context to verify \"\n\t\t\t\t\t\t\t\t\t\"the ssl settings for postgres and possibly setup \"\n\t\t\t\t\t\t\t\t\t\"certificates. Since Citus requires connections \"\n\t\t\t\t\t\t\t\t\t\"between nodes to use ssl communication between \"\n\t\t\t\t\t\t\t\t\t\"nodes might return an error until ssl is setup \"\n\t\t\t\t\t\t\t\t\t\"correctly.\")));\n\t\treturn false;\n\t}\n\tEnsureReleaseResource((MemoryContextCallbackFunction) (&SSL_CTX_free),\n\t\t\t\t\t\t  sslContext);\n\n\t/*\n\t * check if we can load the certificate, when we can we assume the certificates are in\n\t * place. No need to create the certificates and we can exit the function.\n\t *\n\t * This also makes the whole ssl enabling idempotent as writing the certificate is the\n\t * last step.\n\t */\n\tif (SSL_CTX_use_certificate_chain_file(sslContext, ssl_cert_file) == 1)\n\t{\n\t\treturn false;\n\t}\n\tereport(LOG, (errmsg(\"no certificate present, generating self signed certificate\")));\n\n\tprivateKey = GeneratePrivateKey();\n\tif (!privateKey)\n\t{\n\t\tereport(ERROR, (errmsg(\"error while generating private key\")));\n\t}\n\n\tcertificate = CreateCertificate(privateKey);\n\tif (!certificate)\n\t{\n\t\tereport(ERROR, (errmsg(\"error while generating certificate\")));\n\t}\n\n\tcertificateWritten = StoreCertificate(privateKey, certificate);\n\tif (!certificateWritten)\n\t{\n\t\tereport(ERROR, (errmsg(\"error while storing key and certificate\")));\n\t}\n\n\treturn true;\n}\n\n\n/*\n * GeneratePrivateKey uses open ssl functions to generate an RSA private key of 2048 bits.\n * All OpenSSL resources created during the process are added to the memory context active\n * when the function is called and therefore should not be freed by the caller.\n */\nstatic EVP_PKEY *\nGeneratePrivateKey()\n{\n\t/* Allocate memory for the EVP_PKEY structure. */\n\tEVP_PKEY *privateKey = EVP_PKEY_new();\n\tif (!privateKey)\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to allocate space for private key\")));\n\t}\n\tEnsureReleaseResource((MemoryContextCallbackFunction) (&EVP_PKEY_free),\n\t\t\t\t\t\t  privateKey);\n\n\tBIGNUM *exponent = BN_new();\n\tEnsureReleaseResource((MemoryContextCallbackFunction) (&BN_free), exponent);\n\n\t/* load the exponent to use for the generation of the key */\n\tint success = BN_set_word(exponent, RSA_F4);\n\tif (success != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to prepare exponent for RSA algorithm\")));\n\t}\n\n\tRSA *rsa = RSA_new();\n\tsuccess = RSA_generate_key_ex(rsa, 2048, exponent, NULL);\n\tif (success != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to generate RSA key\")));\n\t}\n\n\tif (!EVP_PKEY_assign_RSA(privateKey, rsa))\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to assign RSA key to use as private key\")));\n\t}\n\n\t/* The key has been generated, return it. */\n\treturn privateKey;\n}\n\n\n/*\n * CreateCertificate creates a self signed certificate for citus to use. The certificate\n * will contain the public parts of the private key and will be signed in the end by the\n * private part to make it self signed.\n */\nstatic X509 *\nCreateCertificate(EVP_PKEY *privateKey)\n{\n\tX509 *certificate = X509_new();\n\tif (!certificate)\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to allocate space for the x509 certificate\")));\n\t}\n\tEnsureReleaseResource((MemoryContextCallbackFunction) (&X509_free),\n\t\t\t\t\t\t  certificate);\n\n\t/* Set the serial number. */\n\tASN1_INTEGER_set(X509_get_serialNumber(certificate), 1);\n\n\t/*\n\t * Set the expiry of the certificate.\n\t *\n\t * the functions X509_get_notBefore and X509_get_notAfter are deprecated, these are\n\t * replaced with mutable and non-mutable variants in openssl 1.1, however they are\n\t * better supported than the newer versions. In 1.1 they are aliasses to the mutable\n\t * variant (X509_getm_notBefore, ...) that we actually need, so they will actually use\n\t * the correct function in newer versions.\n\t *\n\t * Postgres does not check the validity on the certificates, but we can't omit the\n\t * dates either to create a certificate that can be parsed. We settled on a validity\n\t * of 0 seconds. When postgres would fix the validity check in a future version it\n\t * would fail right after an upgrade. Instead of working until the certificate\n\t * expiration date and then suddenly erroring out.\n\t */\n#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L\n\n\t/* New mutable accessors (present in 1.1, 3.x). */\n\tX509_gmtime_adj(X509_getm_notBefore(certificate), 0);\n\tX509_gmtime_adj(X509_getm_notAfter(certificate), 0);\n#else\n\n\t/* Legacy functions kept for 1.0.x compatibility. */\n\tX509_gmtime_adj(X509_get_notBefore(certificate), 0);\n\tX509_gmtime_adj(X509_get_notAfter(certificate), 0);\n#endif\n\n\t/* Set the public key for our certificate */\n\tX509_set_pubkey(certificate, privateKey);\n\n\t/* Set the common name for the certificate */\n\tX509_NAME *subjectName = X509_get_subject_name(certificate);\n\tX509_NAME_add_entry_by_txt(subjectName, X509_SUBJECT_COMMON_NAME, MBSTRING_ASC,\n\t\t\t\t\t\t\t   (unsigned char *) CITUS_AUTO_SSL_COMMON_NAME, -1, -1,\n\t\t\t\t\t\t\t   0);\n\n\t/* For a self signed certificate we set the isser name to our own name */\n\tX509_set_issuer_name(certificate, subjectName);\n\n\t/* With all information filled out we sign the certificate with our own key */\n\tif (!X509_sign(certificate, privateKey, EVP_sha256()))\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to create signature for the x509 certificate\")));\n\t}\n\n\treturn certificate;\n}\n\n\n/*\n * StoreCertificate stores both the private key and its certificate to the files\n * configured in postgres.\n */\nstatic bool\nStoreCertificate(EVP_PKEY *privateKey, X509 *certificate)\n{\n\tconst char *privateKeyFilename = ssl_key_file;\n\tconst char *certificateFilename = ssl_cert_file;\n\n\n\t/* Open the private key file and write the private key in PEM format to it */\n\tint privateKeyFileDescriptor = open(privateKeyFilename, O_WRONLY | O_CREAT, 0600);\n\tif (privateKeyFileDescriptor == -1)\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to open private key file '%s' for writing\",\n\t\t\t\t\t\t\t   privateKeyFilename)));\n\t}\n\n\tFILE *privateKeyFile = fdopen(privateKeyFileDescriptor, \"wb\");\n\tif (!privateKeyFile)\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to open private key file '%s' for writing\",\n\t\t\t\t\t\t\t   privateKeyFilename)));\n\t}\n\n\tint success = PEM_write_PrivateKey(privateKeyFile, privateKey, NULL, NULL, 0, NULL,\n\t\t\t\t\t\t\t\t\t   NULL);\n\tfclose(privateKeyFile);\n\tif (!success)\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to store private key\")));\n\t}\n\n\tint certificateFileDescriptor = open(certificateFilename, O_WRONLY | O_CREAT, 0600);\n\tif (certificateFileDescriptor == -1)\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to open private key file '%s' for writing\",\n\t\t\t\t\t\t\t   privateKeyFilename)));\n\t}\n\n\t/* Open the certificate file and write the certificate in the PEM format to it */\n\tFILE *certificateFile = fdopen(certificateFileDescriptor, \"wb\");\n\tif (!certificateFile)\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to open certificate file '%s' for writing\",\n\t\t\t\t\t\t\t   certificateFilename)));\n\t}\n\n\tsuccess = PEM_write_X509(certificateFile, certificate);\n\tfclose(certificateFile);\n\tif (!success)\n\t{\n\t\tereport(ERROR, (errmsg(\"unable to store certificate\")));\n\t}\n\n\treturn true;\n}\n\n\n#endif /* USE_SSL */\n"
  },
  {
    "path": "src/backend/distributed/utils/errormessage.c",
    "content": "/*\n * errormessage.c\n *\t  Error handling related support functionality.\n *\n * Copyright (c) Citus Data, Inc.\n */\n\n#include \"postgres.h\"\n\n#include \"common/sha2.h\"\n#include \"utils/builtins.h\"\n#include \"utils/memutils.h\"\n\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/log_utils.h\"\n\n\n/*\n * DeferredErrorInternal is a helper function for DeferredError().\n */\nDeferredErrorMessage *\nDeferredErrorInternal(int code, const char *message, const char *detail, const char *hint,\n\t\t\t\t\t  const char *filename, int linenumber, const char *functionname)\n{\n\tDeferredErrorMessage *error = CitusMakeNode(DeferredErrorMessage);\n\n\tAssert(message != NULL);\n\n\terror->code = code;\n\terror->message = message;\n\terror->detail = detail;\n\terror->hint = hint;\n\terror->filename = filename;\n\terror->linenumber = linenumber;\n\terror->functionname = functionname;\n\treturn error;\n}\n\n\n/*\n * RaiseDeferredErrorInternal is a helper function for RaiseDeferredError().\n */\nvoid\nRaiseDeferredErrorInternal(DeferredErrorMessage *error, int elevel)\n{\n\tErrorData *errorData = palloc0(sizeof(ErrorData));\n\n\terrorData->sqlerrcode = error->code;\n\terrorData->elevel = elevel;\n\terrorData->message = pstrdup(error->message);\n\tif (error->detail)\n\t{\n\t\terrorData->detail = pstrdup(error->detail);\n\t}\n\tif (error->hint)\n\t{\n\t\terrorData->hint = pstrdup(error->hint);\n\t}\n\terrorData->filename = pstrdup(error->filename);\n\terrorData->lineno = error->linenumber;\n\terrorData->funcname = error->functionname;\n\n\terrorData->assoc_context = ErrorContext;\n\n\tThrowErrorData(errorData);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/foreign_key_relationship.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * foreign_key_relationship.c\n *   This file contains functions for creating foreign key relationship graph\n *   between distributed tables. Created relationship graph will be hold by\n *   a static variable defined in this file until an invalidation comes in.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup_details.h\"\n#include \"access/stratnum.h\"\n#include \"access/table.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"common/hashfn.h\"\n#include \"nodes/pg_list.h\"\n#include \"storage/lockdefs.h\"\n#include \"utils/catcache.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/inval.h\"\n#include \"utils/memutils.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/foreign_key_relationship.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/version_compat.h\"\n\n\n/*\n * ForeignConstraintRelationshipGraph holds the graph data structure for foreign constraint relationship\n * between relations. We will only have single static instance of that struct and it\n * will be invalidated after change on any foreign constraint.\n */\ntypedef struct ForeignConstraintRelationshipGraph\n{\n\tHTAB *nodeMap;\n\tbool isValid;\n}ForeignConstraintRelationshipGraph;\n\n/*\n * ForeignConstraintRelationshipNode holds the data for each node of the ForeignConstraintRelationshipGraph\n * For each node we have relation id, which is the Oid of that relation, visiting\n * information for that node in the latest DFS and the list of adjacency nodes.\n * Note that we also hold back adjacency nodes for getting referenced node over\n * that one.\n */\ntypedef struct ForeignConstraintRelationshipNode\n{\n\tOid relationId;\n\tList *adjacencyList;\n\tList *backAdjacencyList;\n}ForeignConstraintRelationshipNode;\n\n\n/*\n * ForeignConstraintRelationshipEdge will only be used while creating the ForeignConstraintRelationshipGraph.\n * It won't show edge information on the graph, yet will be used in the pre-processing\n * phase.\n */\ntypedef struct ForeignConstraintRelationshipEdge\n{\n\tOid referencingRelationOID;\n\tOid referencedRelationOID;\n}ForeignConstraintRelationshipEdge;\n\n\nstatic ForeignConstraintRelationshipGraph *fConstraintRelationshipGraph = NULL;\n\nstatic List * GetRelationshipNodesForFKeyConnectedRelations(\n\tForeignConstraintRelationshipNode *relationshipNode);\nstatic List * GetAllNeighboursList(ForeignConstraintRelationshipNode *relationshipNode);\nstatic ForeignConstraintRelationshipNode * GetRelationshipNodeForRelationId(Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbool *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tisFound);\nstatic void CreateForeignConstraintRelationshipGraph(void);\nstatic bool IsForeignConstraintRelationshipGraphValid(void);\nstatic List * GetNeighbourList(ForeignConstraintRelationshipNode *relationshipNode,\n\t\t\t\t\t\t\t   bool isReferencing);\nstatic List * GetRelationIdsFromRelationshipNodeList(List *fKeyRelationshipNodeList);\nstatic void PopulateAdjacencyLists(void);\nstatic int CompareForeignConstraintRelationshipEdges(const void *leftElement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t const void *rightElement);\nstatic void AddForeignConstraintRelationshipEdge(Oid referencingOid, Oid referencedOid);\nstatic ForeignConstraintRelationshipNode * CreateOrFindNode(HTAB *adjacencyLists, Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trelid);\nstatic List * GetConnectedListHelper(ForeignConstraintRelationshipNode *node,\n\t\t\t\t\t\t\t\t\t bool isReferencing);\nstatic List * GetForeignConstraintRelationshipHelper(Oid relationId, bool isReferencing);\n\nMemoryContext ForeignConstraintRelationshipMemoryContext = NULL;\n\n\n/*\n * GetForeignKeyConnectedRelationIdList returns a list of relation id's for\n * relations that are connected to relation with relationId via a foreign\n * key graph.\n */\nList *\nGetForeignKeyConnectedRelationIdList(Oid relationId)\n{\n\t/* use ShareRowExclusiveLock to prevent concurent foreign key creation */\n\tLOCKMODE lockMode = ShareRowExclusiveLock;\n\tRelation relation = try_relation_open(relationId, lockMode);\n\tif (!RelationIsValid(relation))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"relation with OID %d does not exist\",\n\t\t\t\t\t\t\t   relationId)));\n\t}\n\n\trelation_close(relation, NoLock);\n\n\tbool foundInFKeyGraph = false;\n\tForeignConstraintRelationshipNode *relationshipNode =\n\t\tGetRelationshipNodeForRelationId(relationId, &foundInFKeyGraph);\n\tif (!foundInFKeyGraph)\n\t{\n\t\t/*\n\t\t * Relation could not be found in foreign key graph, then it has no\n\t\t * foreign key relationships.\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tList *fKeyConnectedRelationshipNodeList =\n\t\tGetRelationshipNodesForFKeyConnectedRelations(relationshipNode);\n\tList *fKeyConnectedRelationIdList =\n\t\tGetRelationIdsFromRelationshipNodeList(fKeyConnectedRelationshipNodeList);\n\treturn fKeyConnectedRelationIdList;\n}\n\n\n/*\n * ShouldUndistributeCitusLocalTable returns true if given relationId needs\n * to be undistributed. Here we do not undistribute table if it's converted by the user,\n * or connected to a table converted by the user, or a reference table, via foreign keys.\n */\nbool\nShouldUndistributeCitusLocalTable(Oid relationId)\n{\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\tif (!cacheEntry->autoConverted)\n\t{\n\t\t/*\n\t\t * The relation is not added to metadata automatically,\n\t\t * we shouldn't undistribute it.\n\t\t */\n\t\treturn false;\n\t}\n\n\t/*\n\t * As we will operate on foreign key connected relations, here we\n\t * invalidate foreign key graph so that we act on fresh graph.\n\t */\n\tInvalidateForeignKeyGraph();\n\n\tList *fkeyConnectedRelations = GetForeignKeyConnectedRelationIdList(relationId);\n\n\treturn !RelationIdListHasReferenceTable(fkeyConnectedRelations);\n}\n\n\n/*\n * GetRelationshipNodesForFKeyConnectedRelations performs breadth-first search\n * starting from input ForeignConstraintRelationshipNode and returns a list\n * of ForeignConstraintRelationshipNode objects for relations that are connected\n * to given relation node via a foreign key relationhip graph.\n */\nstatic List *\nGetRelationshipNodesForFKeyConnectedRelations(ForeignConstraintRelationshipNode *\n\t\t\t\t\t\t\t\t\t\t\t  relationshipNode)\n{\n\tHTAB *oidVisitedMap = CreateSimpleHashSetWithName(Oid, \"oid visited hash set\");\n\n\tVisitOid(oidVisitedMap, relationshipNode->relationId);\n\tList *relationshipNodeList = list_make1(relationshipNode);\n\n\tForeignConstraintRelationshipNode *currentNode = NULL;\n\tforeach_ptr_append(currentNode, relationshipNodeList)\n\t{\n\t\tList *allNeighboursList = GetAllNeighboursList(currentNode);\n\t\tForeignConstraintRelationshipNode *neighbourNode = NULL;\n\t\tforeach_declared_ptr(neighbourNode, allNeighboursList)\n\t\t{\n\t\t\tOid neighbourRelationId = neighbourNode->relationId;\n\t\t\tif (OidVisited(oidVisitedMap, neighbourRelationId))\n\t\t\t{\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tVisitOid(oidVisitedMap, neighbourRelationId);\n\t\t\trelationshipNodeList = lappend(relationshipNodeList, neighbourNode);\n\t\t}\n\t}\n\n\treturn relationshipNodeList;\n}\n\n\n/*\n * GetAllNeighboursList returns a list of ForeignConstraintRelationshipNode\n * objects by concatenating both (referencing & referenced) adjacency lists\n * of given relationship node.\n */\nstatic List *\nGetAllNeighboursList(ForeignConstraintRelationshipNode *relationshipNode)\n{\n\tbool isReferencing = false;\n\tList *referencedNeighboursList = GetNeighbourList(relationshipNode, isReferencing);\n\n\tisReferencing = true;\n\tList *referencingNeighboursList = GetNeighbourList(relationshipNode, isReferencing);\n\n\t/*\n\t * GetNeighbourList returns list from graph as is, so first copy it as\n\t * list_concat might invalidate it.\n\t */\n\tList *allNeighboursList = list_copy(referencedNeighboursList);\n\tallNeighboursList = list_concat_unique_ptr(allNeighboursList,\n\t\t\t\t\t\t\t\t\t\t\t   referencingNeighboursList);\n\treturn allNeighboursList;\n}\n\n\n/*\n * ReferencedRelationIdList is a wrapper function around GetForeignConstraintRelationshipHelper\n * to get list of relation IDs which are referenced by the given relation id.\n *\n * Note that, if relation A is referenced by relation B and relation B is referenced\n * by relation C, then the result list for relation A consists of the relation\n * IDs of relation B and relation C.\n */\nList *\nReferencedRelationIdList(Oid relationId)\n{\n\treturn GetForeignConstraintRelationshipHelper(relationId, false);\n}\n\n\n/*\n * ReferencingRelationIdList is a wrapper function around GetForeignConstraintRelationshipHelper\n * to get list of relation IDs which are referencing to given relation id.\n *\n * Note that, if relation A is referenced by relation B and relation B is referenced\n * by relation C, then the result list for relation C consists of the relation\n * IDs of relation A and relation B.\n */\nList *\nReferencingRelationIdList(Oid relationId)\n{\n\treturn GetForeignConstraintRelationshipHelper(relationId, true);\n}\n\n\n/*\n * GetForeignConstraintRelationshipHelper returns the list of oids referenced or\n * referencing given relation id. It is a helper function for providing results\n * to public functions ReferencedRelationIdList and ReferencingRelationIdList.\n */\nstatic List *\nGetForeignConstraintRelationshipHelper(Oid relationId, bool isReferencing)\n{\n\tbool isFound = false;\n\tForeignConstraintRelationshipNode *relationshipNode =\n\t\tGetRelationshipNodeForRelationId(relationId, &isFound);\n\n\tif (!isFound)\n\t{\n\t\t/*\n\t\t * If there is no node with the given relation id, that means given table\n\t\t * is not referencing and is not referenced by any table\n\t\t */\n\t\treturn NIL;\n\t}\n\n\tList *connectedNodeList = GetConnectedListHelper(relationshipNode, isReferencing);\n\tList *relationIdList = GetRelationIdsFromRelationshipNodeList(connectedNodeList);\n\treturn relationIdList;\n}\n\n\n/*\n * GetRelationshipNodeForRelationId searches foreign key graph for relation\n * with relationId and returns ForeignConstraintRelationshipNode object for\n * relation if it exists in graph. Otherwise, sets isFound to false.\n *\n * Also before searching foreign key graph, this function implicitly builds\n * foreign key graph if it's invalid or not built yet.\n */\nstatic ForeignConstraintRelationshipNode *\nGetRelationshipNodeForRelationId(Oid relationId, bool *isFound)\n{\n\tCreateForeignConstraintRelationshipGraph();\n\n\tForeignConstraintRelationshipNode *relationshipNode =\n\t\t(ForeignConstraintRelationshipNode *) hash_search(\n\t\t\tfConstraintRelationshipGraph->nodeMap, &relationId,\n\t\t\tHASH_FIND, isFound);\n\n\treturn relationshipNode;\n}\n\n\n/*\n * CreateForeignConstraintRelationshipGraph creates the foreign constraint relation graph using\n * foreign constraint provided by pg_constraint metadata table.\n */\nstatic void\nCreateForeignConstraintRelationshipGraph()\n{\n\t/* if we have already created the graph, use it */\n\tif (IsForeignConstraintRelationshipGraphValid())\n\t{\n\t\treturn;\n\t}\n\n\t/*\n\t * Lazily create our memory context once and reset on every reuse.\n\t * Since we have cleared and invalidated the fConstraintRelationshipGraph, right\n\t * before we can simply reset the context if it was already existing.\n\t */\n\tif (ForeignConstraintRelationshipMemoryContext == NULL)\n\t{\n\t\t/* make sure we've initialized CacheMemoryContext */\n\t\tif (CacheMemoryContext == NULL)\n\t\t{\n\t\t\tCreateCacheMemoryContext();\n\t\t}\n\n\t\tForeignConstraintRelationshipMemoryContext = AllocSetContextCreate(\n\t\t\tCacheMemoryContext,\n\t\t\t\"Foreign Constraint Relationship Graph Context\",\n\t\t\tALLOCSET_DEFAULT_MINSIZE,\n\t\t\tALLOCSET_DEFAULT_INITSIZE,\n\t\t\tALLOCSET_DEFAULT_MAXSIZE);\n\t}\n\telse\n\t{\n\t\tfConstraintRelationshipGraph = NULL;\n\t\tMemoryContextReset(ForeignConstraintRelationshipMemoryContext);\n\t}\n\n\tAssert(fConstraintRelationshipGraph == NULL);\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(\n\t\tForeignConstraintRelationshipMemoryContext);\n\n\tfConstraintRelationshipGraph = (ForeignConstraintRelationshipGraph *) palloc(\n\t\tsizeof(ForeignConstraintRelationshipGraph));\n\tfConstraintRelationshipGraph->isValid = false;\n\n\tfConstraintRelationshipGraph->nodeMap = CreateSimpleHash(Oid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ForeignConstraintRelationshipNode);\n\n\tPopulateAdjacencyLists();\n\n\tfConstraintRelationshipGraph->isValid = true;\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * IsForeignConstraintGraphValid check whether there is a valid graph.\n */\nstatic bool\nIsForeignConstraintRelationshipGraphValid()\n{\n\t/*\n\t * We might have some concurrent metadata changes. In order to get the changes,\n\t * we first need to accept the cache invalidation messages.\n\t */\n\tAcceptInvalidationMessages();\n\n\tif (fConstraintRelationshipGraph != NULL && fConstraintRelationshipGraph->isValid)\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * SetForeignConstraintGraphInvalid sets the validity of the graph to false.\n */\nvoid\nSetForeignConstraintRelationshipGraphInvalid()\n{\n\tif (fConstraintRelationshipGraph != NULL)\n\t{\n\t\tfConstraintRelationshipGraph->isValid = false;\n\t}\n}\n\n\n/*\n * GetConnectedListHelper returns list of ForeignConstraintRelationshipNode\n * objects for relations referenced by or referencing to given relation\n * according to isReferencing flag.\n *\n */\nstatic List *\nGetConnectedListHelper(ForeignConstraintRelationshipNode *node, bool isReferencing)\n{\n\tHTAB *oidVisitedMap = CreateSimpleHashSetWithName(Oid, \"oid visited hash set\");\n\n\tList *connectedNodeList = NIL;\n\n\tList *relationshipNodeStack = list_make1(node);\n\twhile (list_length(relationshipNodeStack) != 0)\n\t{\n\t\t/*\n\t\t * Note that this loop considers leftmost element of\n\t\t * relationshipNodeStack as top of the stack.\n\t\t */\n\n\t\t/* pop top element from stack */\n\t\tForeignConstraintRelationshipNode *currentNode = linitial(relationshipNodeStack);\n\t\trelationshipNodeStack = list_delete_first(relationshipNodeStack);\n\n\t\tOid currentRelationId = currentNode->relationId;\n\t\tif (!OidVisited(oidVisitedMap, currentRelationId))\n\t\t{\n\t\t\tconnectedNodeList = lappend(connectedNodeList, currentNode);\n\t\t\tVisitOid(oidVisitedMap, currentRelationId);\n\t\t}\n\n\t\tList *neighbourList = GetNeighbourList(currentNode, isReferencing);\n\t\tForeignConstraintRelationshipNode *neighbourNode = NULL;\n\t\tforeach_declared_ptr(neighbourNode, neighbourList)\n\t\t{\n\t\t\tOid neighbourRelationId = neighbourNode->relationId;\n\t\t\tif (!OidVisited(oidVisitedMap, neighbourRelationId))\n\t\t\t{\n\t\t\t\t/* push to stack */\n\t\t\t\trelationshipNodeStack = lcons(neighbourNode, relationshipNodeStack);\n\t\t\t}\n\t\t}\n\t}\n\n\thash_destroy(oidVisitedMap);\n\n\t/* finally remove yourself from list */\n\tconnectedNodeList = list_delete_first(connectedNodeList);\n\treturn connectedNodeList;\n}\n\n\n/*\n * OidVisited returns true if given oid is visited according to given oid hash-set.\n */\nbool\nOidVisited(HTAB *oidVisitedMap, Oid oid)\n{\n\tbool found = false;\n\thash_search(oidVisitedMap, &oid, HASH_FIND, &found);\n\treturn found;\n}\n\n\n/*\n * VisitOid sets given oid as visited in given hash-set.\n */\nvoid\nVisitOid(HTAB *oidVisitedMap, Oid oid)\n{\n\tbool found = false;\n\thash_search(oidVisitedMap, &oid, HASH_ENTER, &found);\n}\n\n\n/*\n * GetNeighbourList returns copy of relevant adjacency list of given\n * ForeignConstraintRelationshipNode object depending on the isReferencing\n * flag.\n */\nstatic List *\nGetNeighbourList(ForeignConstraintRelationshipNode *relationshipNode, bool isReferencing)\n{\n\tif (isReferencing)\n\t{\n\t\treturn relationshipNode->backAdjacencyList;\n\t}\n\telse\n\t{\n\t\treturn relationshipNode->adjacencyList;\n\t}\n}\n\n\n/*\n * GetRelationIdsFromRelationshipNodeList returns list of relationId's for\n * given ForeignConstraintRelationshipNode object list.\n */\nstatic List *\nGetRelationIdsFromRelationshipNodeList(List *fKeyRelationshipNodeList)\n{\n\tList *relationIdList = NIL;\n\n\tForeignConstraintRelationshipNode *fKeyRelationshipNode = NULL;\n\tforeach_declared_ptr(fKeyRelationshipNode, fKeyRelationshipNodeList)\n\t{\n\t\tOid relationId = fKeyRelationshipNode->relationId;\n\t\trelationIdList = lappend_oid(relationIdList, relationId);\n\t}\n\n\treturn relationIdList;\n}\n\n\n/*\n * PopulateAdjacencyLists gets foreign constraint relationship information from pg_constraint\n * metadata table and populates them to the foreign constraint relation graph.\n */\nstatic void\nPopulateAdjacencyLists(void)\n{\n\tHeapTuple tuple;\n\tScanKeyData scanKey[1];\n\tint scanKeyCount = 1;\n\n\tOid prevReferencingOid = InvalidOid;\n\tOid prevReferencedOid = InvalidOid;\n\tList *frelEdgeList = NIL;\n\n\tRelation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_constraint_contype, BTEqualStrategyNumber, F_CHAREQ,\n\t\t\t\tCharGetDatum(CONSTRAINT_FOREIGN));\n\tSysScanDesc scanDescriptor = systable_beginscan(pgConstraint, InvalidOid, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\twhile (HeapTupleIsValid(tuple = systable_getnext(scanDescriptor)))\n\t{\n\t\tForm_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(tuple);\n\n\t\tForeignConstraintRelationshipEdge *currentFConstraintRelationshipEdge = palloc(\n\t\t\tsizeof(ForeignConstraintRelationshipEdge));\n\t\tcurrentFConstraintRelationshipEdge->referencingRelationOID =\n\t\t\tconstraintForm->conrelid;\n\t\tcurrentFConstraintRelationshipEdge->referencedRelationOID =\n\t\t\tconstraintForm->confrelid;\n\n\t\tfrelEdgeList = lappend(frelEdgeList, currentFConstraintRelationshipEdge);\n\t}\n\n\t/*\n\t * Since there is no index on columns we are planning to sort tuples\n\t * sorting tuples manually instead of using scan keys\n\t */\n\tfrelEdgeList = SortList(frelEdgeList, CompareForeignConstraintRelationshipEdges);\n\n\tForeignConstraintRelationshipEdge *currentFConstraintRelationshipEdge = NULL;\n\tforeach_declared_ptr(currentFConstraintRelationshipEdge, frelEdgeList)\n\t{\n\t\t/* we just saw this edge, no need to add it twice */\n\t\tif (currentFConstraintRelationshipEdge->referencingRelationOID ==\n\t\t\tprevReferencingOid &&\n\t\t\tcurrentFConstraintRelationshipEdge->referencedRelationOID == prevReferencedOid\n\t\t\t)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tAddForeignConstraintRelationshipEdge(\n\t\t\tcurrentFConstraintRelationshipEdge->referencingRelationOID,\n\t\t\tcurrentFConstraintRelationshipEdge->\n\t\t\treferencedRelationOID);\n\n\t\tprevReferencingOid = currentFConstraintRelationshipEdge->referencingRelationOID;\n\t\tprevReferencedOid = currentFConstraintRelationshipEdge->referencedRelationOID;\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgConstraint, AccessShareLock);\n}\n\n\n/*\n * CompareForeignConstraintRelationshipEdges is a helper function to compare two\n * ForeignConstraintRelationshipEdge using referencing and referenced ids respectively.\n */\nstatic int\nCompareForeignConstraintRelationshipEdges(const void *leftElement,\n\t\t\t\t\t\t\t\t\t\t  const void *rightElement)\n{\n\tconst ForeignConstraintRelationshipEdge *leftEdge =\n\t\t*((const ForeignConstraintRelationshipEdge **) leftElement);\n\tconst ForeignConstraintRelationshipEdge *rightEdge =\n\t\t*((const ForeignConstraintRelationshipEdge **) rightElement);\n\n\tint referencingDiff = leftEdge->referencingRelationOID -\n\t\t\t\t\t\t  rightEdge->referencingRelationOID;\n\tint referencedDiff = leftEdge->referencedRelationOID -\n\t\t\t\t\t\t rightEdge->referencedRelationOID;\n\n\tif (referencingDiff != 0)\n\t{\n\t\treturn referencingDiff;\n\t}\n\n\treturn referencedDiff;\n}\n\n\n/*\n * AddForeignConstraintRelationshipEdge adds edge between the nodes having given OIDs\n * by adding referenced node to the adjacency list referencing node and adding\n * referencing node to the back adjacency list of referenced node.\n */\nstatic void\nAddForeignConstraintRelationshipEdge(Oid referencingOid, Oid referencedOid)\n{\n\tForeignConstraintRelationshipNode *referencingNode = CreateOrFindNode(\n\t\tfConstraintRelationshipGraph->nodeMap, referencingOid);\n\tForeignConstraintRelationshipNode *referencedNode = CreateOrFindNode(\n\t\tfConstraintRelationshipGraph->nodeMap, referencedOid);\n\n\treferencingNode->adjacencyList = lappend(referencingNode->adjacencyList,\n\t\t\t\t\t\t\t\t\t\t\t referencedNode);\n\treferencedNode->backAdjacencyList = lappend(referencedNode->backAdjacencyList,\n\t\t\t\t\t\t\t\t\t\t\t\treferencingNode);\n}\n\n\n/*\n * CreateOrFindNode either gets or adds new node to the foreign constraint relation graph\n */\nstatic ForeignConstraintRelationshipNode *\nCreateOrFindNode(HTAB *adjacencyLists, Oid relid)\n{\n\tbool found = false;\n\tForeignConstraintRelationshipNode *node =\n\t\t(ForeignConstraintRelationshipNode *) hash_search(adjacencyLists,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &relid, HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &found);\n\n\tif (!found)\n\t{\n\t\tnode->adjacencyList = NIL;\n\t\tnode->backAdjacencyList = NIL;\n\t}\n\n\treturn node;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/function.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * function.c\n *\n * Utility functions for dealing with functions.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"miscadmin.h\"\n\n#include \"commands/defrem.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/utils/function.h\"\n\n\n/*\n * GetFunctionInfo first resolves the operator for the given data type, access\n * method, and support procedure. The function then uses the resolved operator's\n * identifier to fill in a function manager object, and returns this object.\n */\nFmgrInfo *\nGetFunctionInfo(Oid typeId, Oid accessMethodId, int16 procedureId)\n{\n\tFmgrInfo *functionInfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo));\n\n\t/* get default operator class from pg_opclass for datum type */\n\tOid operatorClassId = GetDefaultOpClass(typeId, accessMethodId);\n\n\tOid operatorFamilyId = get_opclass_family(operatorClassId);\n\tOid operatorClassInputType = get_opclass_input_type(operatorClassId);\n\n\tOid operatorId = get_opfamily_proc(operatorFamilyId, operatorClassInputType,\n\t\t\t\t\t\t\t\t\t   operatorClassInputType, procedureId);\n\n\tif (operatorId == InvalidOid)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find function for data typeId %u\", typeId)));\n\t}\n\n\t/* fill in the FmgrInfo struct using the operatorId */\n\tfmgr_info(operatorId, functionInfo);\n\n\treturn functionInfo;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/function_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * function_utils.c\n *\t  Utilities regarding calls to PG functions\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/namespace.h\"\n#include \"executor/executor.h\"\n#include \"utils/builtins.h\"\n#include \"utils/regproc.h\"\n\n#include \"distributed/function_utils.h\"\n#include \"distributed/version_compat.h\"\n\n\n/*\n * FunctionOid searches for a function that has the given name and the given\n * number of arguments, and returns the corresponding function's oid. The\n * function reports error if the target function is not found, or it found more\n * matching instances.\n */\nOid\nFunctionOid(const char *schemaName, const char *functionName, int argumentCount)\n{\n\treturn FunctionOidExtended(schemaName, functionName, argumentCount, false);\n}\n\n\n/*\n * FunctionOidExtended searches for a given function identified by schema,\n * functionName, and argumentCount. It reports error if the function is not\n * found or there are more than one match. If the missingOK parameter is set\n * and there are no matches, then the function returns InvalidOid.\n */\nOid\nFunctionOidExtended(const char *schemaName, const char *functionName, int argumentCount,\n\t\t\t\t\tbool missingOK)\n{\n\tchar *qualifiedFunctionName = quote_qualified_identifier(schemaName, functionName);\n\tList *qualifiedFunctionNameList = stringToQualifiedNameList(qualifiedFunctionName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tNULL);\n\tList *argumentList = NIL;\n\tconst bool findVariadics = false;\n\tconst bool findDefaults = false;\n\n\tFuncCandidateList functionList = FuncnameGetCandidates(\n\t\tqualifiedFunctionNameList,\n\t\targumentCount,\n\t\targumentList,\n\t\tfindVariadics,\n\t\tfindDefaults,\n\t\tfalse,\n\t\ttrue);\n\n\tif (functionList == NULL)\n\t{\n\t\tif (missingOK)\n\t\t{\n\t\t\treturn InvalidOid;\n\t\t}\n\n\t\tereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION),\n\t\t\t\t\t\terrmsg(\"function \\\"%s\\\" does not exist\", functionName)));\n\t}\n\telse if (functionList->next != NULL)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION),\n\t\t\t\t\t\terrmsg(\"more than one function named \\\"%s\\\"\", functionName)));\n\t}\n\n\t/* get function oid from function list's head */\n\tOid functionOid = functionList->oid;\n\n\treturn functionOid;\n}\n\n\n/*\n * FunctionCallGetTupleStore1 calls the given set-returning PGFunction with the given\n * argument and returns the ResultSetInfo filled by the called function.\n */\nReturnSetInfo *\nFunctionCallGetTupleStore1(PGFunction function, Oid functionId, Datum argument)\n{\n\tLOCAL_FCINFO(fcinfo, 1);\n\tFmgrInfo flinfo;\n\tReturnSetInfo *rsinfo = makeNode(ReturnSetInfo);\n\tEState *estate = CreateExecutorState();\n\trsinfo->econtext = GetPerTupleExprContext(estate);\n\trsinfo->allowedModes = SFRM_Materialize;\n\n\tfmgr_info(functionId, &flinfo);\n\tInitFunctionCallInfoData(*fcinfo, &flinfo, 1, InvalidOid, NULL, (Node *) rsinfo);\n\n\tfcSetArg(fcinfo, 0, argument);\n\n\t(*function)(fcinfo);\n\n\treturn rsinfo;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/hash_helpers.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * hash_helpers.c\n *   Helpers for dynahash.c style hash tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"common/hashfn.h\"\n#include \"utils/hsearch.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/hash_helpers.h\"\n\n\n/*\n * Empty a hash, without destroying the hash table itself.\n */\nvoid\nhash_delete_all(HTAB *htab)\n{\n\tHASH_SEQ_STATUS status;\n\tvoid *entry = NULL;\n\n\thash_seq_init(&status, htab);\n\twhile ((entry = hash_seq_search(&status)) != 0)\n\t{\n\t\tbool found = false;\n\n\t\thash_search(htab, entry, HASH_REMOVE, &found);\n\t\tAssert(found);\n\t}\n}\n\n\n/*\n * CreateSimpleHashWithNameAndSize creates a hashmap that hashes its key using\n * tag_hash function and stores the entries in the current memory context.\n */\nHTAB *\nCreateSimpleHashWithNameAndSizeInternal(Size keySize, Size entrySize,\n\t\t\t\t\t\t\t\t\t\tchar *name, long nelem)\n{\n\tHASHCTL info;\n\tmemset_struct_0(info);\n\tinfo.keysize = keySize;\n\tinfo.entrysize = entrySize;\n\tinfo.hcxt = CurrentMemoryContext;\n\n\tint hashFlags = (HASH_ELEM | HASH_CONTEXT | HASH_BLOBS);\n\n\tHTAB *publicationInfoHash = hash_create(name, nelem, &info, hashFlags);\n\treturn publicationInfoHash;\n}\n\n\n/*\n * foreach_htab_cleanup cleans up the hash iteration state after the iteration\n * is done. This is only needed when break statements are present in the\n * foreach block.\n */\nvoid\nforeach_htab_cleanup(void *var, HASH_SEQ_STATUS *status)\n{\n\tif ((var) != NULL)\n\t{\n\t\thash_seq_term(status);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/jsonbutils.c",
    "content": "#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_type.h\"\n#include \"utils/array.h\"\n#include \"utils/builtins.h\"\n#include \"utils/json.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/jsonbutils.h\"\n#include \"distributed/metadata_cache.h\"\n\n\n/*\n * ExtractFieldJsonb gets value of fieldName from jsonbDoc and puts it\n * into result. If not found, returns false. Otherwise, returns true.\n * The field is returned as a Text* Datum if as_text is true, or a Jsonb*\n * Datum if as_text is false.\n */\nstatic bool\nExtractFieldJsonb(Datum jsonbDoc, const char *fieldName, Datum *result, bool as_text)\n{\n\tDatum pathArray[1] = { CStringGetTextDatum(fieldName) };\n\tbool pathNulls[1] = { false };\n\tbool typeByValue = false;\n\tchar typeAlignment = 0;\n\tint16 typeLength = 0;\n\tint dimensions[1] = { 1 };\n\tint lowerbounds[1] = { 1 };\n\n\tget_typlenbyvalalign(TEXTOID, &typeLength, &typeByValue, &typeAlignment);\n\n\tArrayType *pathArrayObject = construct_md_array(pathArray, pathNulls, 1, dimensions,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlowerbounds, TEXTOID, typeLength,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttypeByValue, typeAlignment);\n\tDatum pathDatum = PointerGetDatum(pathArrayObject);\n\n\tFmgrInfo fmgrInfo;\n\n\tif (as_text)\n\t{\n\t\tfmgr_info(JsonbExtractPathTextFuncId(), &fmgrInfo);\n\t}\n\telse\n\t{\n\t\tfmgr_info(JsonbExtractPathFuncId(), &fmgrInfo);\n\t}\n\n\tLOCAL_FCINFO(functionCallInfo, 2);\n\tInitFunctionCallInfoData(*functionCallInfo, &fmgrInfo, 2, DEFAULT_COLLATION_OID, NULL,\n\t\t\t\t\t\t\t NULL);\n\n\tfcSetArg(functionCallInfo, 0, jsonbDoc);\n\tfcSetArg(functionCallInfo, 1, pathDatum);\n\n\t*result = FunctionCallInvoke(functionCallInfo);\n\treturn !functionCallInfo->isnull;\n}\n\n\n/*\n * ExtractFieldBoolean gets value of fieldName from jsonbDoc, or returns\n * defaultValue if it doesn't exist.\n */\nbool\nExtractFieldBoolean(Datum jsonbDoc, const char *fieldName, bool defaultValue)\n{\n\tDatum jsonbDatum = 0;\n\tbool found = ExtractFieldJsonb(jsonbDoc, fieldName, &jsonbDatum, false);\n\tif (!found)\n\t{\n\t\treturn defaultValue;\n\t}\n\n\tDatum boolDatum = DirectFunctionCall1(jsonb_bool, jsonbDatum);\n\treturn DatumGetBool(boolDatum);\n}\n\n\n/*\n * ExtractFieldInt32 gets value of fieldName from jsonbDoc, or returns\n * defaultValue if it doesn't exist.\n */\nint32\nExtractFieldInt32(Datum jsonbDoc, const char *fieldName, int32 defaultValue)\n{\n\tDatum jsonbDatum = 0;\n\tbool found = ExtractFieldJsonb(jsonbDoc, fieldName, &jsonbDatum, false);\n\tif (!found)\n\t{\n\t\treturn defaultValue;\n\t}\n\n\tDatum int32Datum = DirectFunctionCall1(jsonb_int4, jsonbDatum);\n\treturn DatumGetInt32(int32Datum);\n}\n\n\n/*\n * ExtractFieldTextP gets value of fieldName as text* from jsonbDoc, or\n * returns NULL if it doesn't exist.\n */\ntext *\nExtractFieldTextP(Datum jsonbDoc, const char *fieldName)\n{\n\tDatum jsonbDatum = 0;\n\n\tbool found = ExtractFieldJsonb(jsonbDoc, fieldName, &jsonbDatum, true);\n\tif (!found)\n\t{\n\t\treturn NULL;\n\t}\n\n\treturn DatumGetTextP(jsonbDatum);\n}\n\n\n/*\n * ExtractFieldJsonbDatum gets value of fieldName from jsonbDoc and puts it\n * into result. If not found, returns false. Otherwise, returns true.\n */\nbool\nExtractFieldJsonbDatum(Datum jsonbDoc, const char *fieldName, Datum *result)\n{\n\treturn ExtractFieldJsonb(jsonbDoc, fieldName, result, false);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/listutils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * listutils.c\n *\n * This file contains functions to perform useful operations on lists.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"port.h\"\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/pg_list.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/listutils.h\"\n\n\n/*\n * SortList takes in a list of void pointers, and sorts these pointers (and the\n * values they point to) by applying the given comparison function. The function\n * then returns the sorted list of pointers.\n *\n * Because the input list is a list of pointers, and because qsort expects to\n * compare pointers to the list elements, the provided comparison function must\n * compare pointers to pointers to elements. In addition, this sort function\n * naturally exhibits the same lack of stability exhibited by qsort. See that\n * function's man page for more details.\n */\nList *\nSortList(List *pointerList, int (*comparisonFunction)(const void *, const void *))\n{\n\tList *sortedList = NIL;\n\tuint32 arrayIndex = 0;\n\tuint32 arraySize = (uint32) list_length(pointerList);\n\tvoid **array = (void **) palloc0(arraySize * sizeof(void *));\n\n\tvoid *pointer = NULL;\n\tforeach_declared_ptr(pointer, pointerList)\n\t{\n\t\tarray[arrayIndex] = pointer;\n\n\t\tarrayIndex++;\n\t}\n\n\t/* sort the array of pointers using the comparison function */\n\tSafeQsort(array, arraySize, sizeof(void *), comparisonFunction);\n\n\t/* convert the sorted array of pointers back to a sorted list */\n\tfor (arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)\n\t{\n\t\tvoid *sortedPointer = array[arrayIndex];\n\t\tsortedList = lappend(sortedList, sortedPointer);\n\t}\n\n\tpfree(array);\n\n\tif (sortedList != NIL)\n\t{\n\t\tsortedList->type = pointerList->type;\n\t}\n\n\treturn sortedList;\n}\n\n\n/*\n * PointerArrayFromList converts a list of pointers to an array of pointers.\n */\nvoid **\nPointerArrayFromList(List *pointerList)\n{\n\tint pointerCount = list_length(pointerList);\n\tvoid **pointerArray = (void **) palloc0(pointerCount * sizeof(void *));\n\tint pointerIndex = 0;\n\n\tvoid *pointer = NULL;\n\tforeach_declared_ptr(pointer, pointerList)\n\t{\n\t\tpointerArray[pointerIndex] = pointer;\n\t\tpointerIndex += 1;\n\t}\n\n\treturn pointerArray;\n}\n\n\n/*\n * ListToHashSet creates a hash table in which the keys are copied from\n * from itemList and the values are the same as the keys. This can\n * be used for fast lookups of the presence of a byte array in a set.\n * itemList should be a list of pointers.\n *\n * If isStringList is true, then look-ups are performed through string\n * comparison of strings up to keySize in length. If isStringList is\n * false, then look-ups are performed by comparing exactly keySize\n * bytes pointed to by the pointers in itemList.\n */\nHTAB *\nListToHashSet(List *itemList, Size keySize, bool isStringList)\n{\n\tHASHCTL info;\n\tint flags = HASH_ELEM | HASH_CONTEXT;\n\n\t/* allocate sufficient capacity for O(1) expected look-up time */\n\tint capacity = (int) (list_length(itemList) / 0.75) + 1;\n\n\t/* initialise the hash table for looking up keySize bytes */\n\tmemset(&info, 0, sizeof(info));\n\tinfo.keysize = keySize;\n\tinfo.entrysize = keySize;\n\tinfo.hcxt = CurrentMemoryContext;\n\n\tif (isStringList)\n\t{\n\t\tflags |= HASH_STRINGS;\n\t}\n\telse\n\t{\n\t\tflags |= HASH_BLOBS;\n\t}\n\n\tHTAB *itemSet = hash_create(\"ListToHashSet\", capacity, &info, flags);\n\n\tvoid *item = NULL;\n\tforeach_declared_ptr(item, itemList)\n\t{\n\t\tbool foundInSet = false;\n\n\t\thash_search(itemSet, item, HASH_ENTER, &foundInSet);\n\t}\n\n\treturn itemSet;\n}\n\n\n/*\n * GeneratePositiveIntSequenceList generates a positive int\n * sequence list up to the given number. The list will have:\n * [1:upto]\n */\nList *\nGeneratePositiveIntSequenceList(int upTo)\n{\n\tList *intList = NIL;\n\tfor (int i = 1; i <= upTo; i++)\n\t{\n\t\tintList = lappend_int(intList, i);\n\t}\n\treturn intList;\n}\n\n\n/*\n * StringJoin gets a list of char * and then simply\n * returns a newly allocated char * joined with the\n * given delimiter. It uses ';' as the delimiter by\n * default.\n */\nchar *\nStringJoin(List *stringList, char delimiter)\n{\n\treturn StringJoinParams(stringList, delimiter, NULL, NULL);\n}\n\n\n/*\n * StringJoin gets a list of char * and then simply\n * returns a newly allocated char * joined with the\n * given delimiter, prefix and postfix.\n */\nchar *\nStringJoinParams(List *stringList, char delimiter, char *prefix, char *postfix)\n{\n\tStringInfo joinedString = makeStringInfo();\n\n\tif (prefix != NULL)\n\t{\n\t\tappendStringInfoString(joinedString, prefix);\n\t}\n\n\tconst char *command = NULL;\n\tint curIndex = 0;\n\tforeach_declared_ptr(command, stringList)\n\t{\n\t\tif (curIndex > 0)\n\t\t{\n\t\t\tappendStringInfoChar(joinedString, delimiter);\n\t\t}\n\t\tappendStringInfoString(joinedString, command);\n\t\tcurIndex++;\n\t}\n\n\tif (postfix != NULL)\n\t{\n\t\tappendStringInfoString(joinedString, postfix);\n\t}\n\n\treturn joinedString->data;\n}\n\n\n/*\n * ListTake returns the first size elements of given list. If size is greater\n * than list's length, it returns all elements of list. This is modeled after\n * the \"take\" function used in some Scheme implementations.\n */\nList *\nListTake(List *pointerList, int size)\n{\n\tList *result = NIL;\n\tint listIndex = 0;\n\n\tvoid *pointer = NULL;\n\tforeach_declared_ptr(pointer, pointerList)\n\t{\n\t\tresult = lappend(result, pointer);\n\t\tlistIndex++;\n\t\tif (listIndex >= size)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n\n/*\n * safe_list_nth first checks if given index is valid and errors out if it is\n * not. Otherwise, it directly calls list_nth.\n */\nvoid *\nsafe_list_nth(const List *list, int index)\n{\n\tint listLength = list_length(list);\n\tif (index < 0 || index >= listLength)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),\n\t\t\t\t\t\terrmsg(\"invalid list access: list length was %d but \"\n\t\t\t\t\t\t\t   \"element at index %d was requested \",\n\t\t\t\t\t\t\t   listLength, index)));\n\t}\n\n\treturn list_nth(list, index);\n}\n\n\n/*\n * GenerateListFromElement returns a new list with length of listLength\n * such that all the elements are identical with input listElement pointer.\n */\nList *\nGenerateListFromElement(void *listElement, int listLength)\n{\n\tList *list = NIL;\n\tfor (int i = 0; i < listLength; i++)\n\t{\n\t\tlist = lappend(list, listElement);\n\t}\n\n\treturn list;\n}\n\n\n/*\n * list_filter_oid filters a list of oid-s based on a keepElement\n * function\n */\nList *\nlist_filter_oid(List *list, bool (*keepElement)(Oid element))\n{\n\tList *result = NIL;\n\tOid element = InvalidOid;\n\tforeach_declared_oid(element, list)\n\t{\n\t\tif (keepElement(element))\n\t\t{\n\t\t\tresult = lappend_oid(result, element);\n\t\t}\n\t}\n\n\treturn result;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/log_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * log_utils.c\n *\t  Utilities regarding logs\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"common/cryptohash.h\"\n#include \"common/sha2.h\"\n#include \"utils/builtins.h\"\n#include \"utils/guc.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/errormessage.h\"\n#include \"distributed/log_utils.h\"\n\n\n/*\n * GUC controls showing of some of the unwanted citus messages, it is intended to be set false\n * before vanilla tests to not break postgres test logs.\n */\nbool EnableUnsupportedFeatureMessages = true;\n\n/*\n * IsLoggableLevel returns true if either of client or server log guc is configured to\n * log the given log level.\n * In postgres, log can be configured differently for clients and servers.\n */\nbool\nIsLoggableLevel(int logLevel)\n{\n\treturn log_min_messages <= logLevel || client_min_messages <= logLevel;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/maintenanced.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * maintenanced.c\n *\t  Background worker run for each citus using database in a postgres\n *    cluster.\n *\n * This file provides infrastructure for launching exactly one a background\n * worker for every database in which citus is used.  That background worker\n * can then perform work like deadlock detection, prepared transaction\n * recovery, and cleanup.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include <time.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n#include \"pgstat.h\"\n\n#include \"access/xact.h\"\n#include \"access/xlog.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_extension.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"commands/async.h\"\n#include \"commands/extension.h\"\n#include \"common/hashfn.h\"\n#include \"libpq/pqsignal.h\"\n#include \"nodes/makefuncs.h\"\n#include \"postmaster/bgworker.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/ipc.h\"\n#include \"storage/latch.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/lwlock.h\"\n#include \"storage/proc.h\"\n#include \"tcop/tcopprot.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/memutils.h\"\n\n#include \"citus_version.h\"\n#include \"pg_version_constants.h\"\n\n#include \"distributed/background_jobs.h\"\n#include \"distributed/background_worker_utils.h\"\n#include \"distributed/citus_safe_lib.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/distributed_deadlock_detection.h\"\n#include \"distributed/maintenanced.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_cleaner.h\"\n#include \"distributed/stats/query_stats.h\"\n#include \"distributed/transaction_recovery.h\"\n#include \"distributed/version_compat.h\"\n\n/*\n * Shared memory data for all maintenance workers.\n */\ntypedef struct MaintenanceDaemonControlData\n{\n\t/*\n\t * Lock protecting the shared memory state.  This is to be taken when\n\t * looking up (shared mode) or inserting (exclusive mode) per-database\n\t * data in MaintenanceDaemonDBHash.\n\t */\n\tint trancheId;\n\tchar *lockTrancheName;\n\tLWLock lock;\n} MaintenanceDaemonControlData;\n\n\n/*\n * Per database worker state.\n */\ntypedef struct MaintenanceDaemonDBData\n{\n\t/* hash key: database to run on */\n\tOid databaseOid;\n\n\t/* information: which user to use */\n\tOid userOid;\n\tpid_t workerPid;\n\tbool daemonStarted;\n\tbool triggerNodeMetadataSync;\n\tLatch *latch; /* pointer to the background worker's latch */\n} MaintenanceDaemonDBData;\n\n/* config variable for distributed deadlock detection timeout */\ndouble DistributedDeadlockDetectionTimeoutFactor = 2.0;\nint Recover2PCInterval = 60000;\nint DeferShardDeleteInterval = 15000;\nint BackgroundTaskQueueCheckInterval = 5000;\nint MaxBackgroundTaskExecutors = 1;\nchar *MainDb = \"\";\n\n/* config variables for metadata sync timeout */\nint MetadataSyncInterval = 60000;\nint MetadataSyncRetryInterval = 5000;\n\nstatic shmem_startup_hook_type prev_shmem_startup_hook = NULL;\nstatic MaintenanceDaemonControlData *MaintenanceDaemonControl = NULL;\n\n/*\n * Hash-table of workers, one entry for each database with citus\n * activated.\n */\nstatic HTAB *MaintenanceDaemonDBHash;\nstatic ErrorContextCallback errorCallback = { 0 };\nstatic volatile sig_atomic_t got_SIGHUP = false;\nstatic volatile sig_atomic_t got_SIGTERM = false;\n\n/* set to true when becoming a maintenance daemon */\nstatic bool IsMaintenanceDaemon = false;\n\nstatic void MaintenanceDaemonSigTermHandler(SIGNAL_ARGS);\nstatic void MaintenanceDaemonSigHupHandler(SIGNAL_ARGS);\nstatic void MaintenanceDaemonShmemExit(int code, Datum arg);\nstatic void MaintenanceDaemonErrorContext(void *arg);\nstatic bool MetadataSyncTriggeredCheckAndReset(MaintenanceDaemonDBData *dbData);\nstatic void WarnMaintenanceDaemonNotStarted(void);\nstatic MaintenanceDaemonDBData * GetMaintenanceDaemonDBHashEntry(Oid databaseId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t bool *found);\n\n/*\n * InitializeMaintenanceDaemon, called at server start, is responsible for\n * requesting shared memory and related infrastructure required by maintenance\n * daemons.\n */\nvoid\nInitializeMaintenanceDaemon(void)\n{\n\tprev_shmem_startup_hook = shmem_startup_hook;\n\tshmem_startup_hook = MaintenanceDaemonShmemInit;\n}\n\n\n/*\n * GetMaintenanceDaemonDBHashEntry searches the MaintenanceDaemonDBHash for the\n * databaseId. It returns the entry if found or creates a new entry and initializes\n * the value with zeroes.\n */\nMaintenanceDaemonDBData *\nGetMaintenanceDaemonDBHashEntry(Oid databaseId, bool *found)\n{\n\tMaintenanceDaemonDBData *dbData = (MaintenanceDaemonDBData *) hash_search(\n\t\tMaintenanceDaemonDBHash,\n\t\t&MyDatabaseId,\n\t\tHASH_ENTER_NULL,\n\t\tfound);\n\n\tif (!dbData)\n\t{\n\t\telog(LOG,\n\t\t\t \"cannot create or find the maintenance deamon hash entry for database %u\",\n\t\t\t databaseId);\n\t\treturn NULL;\n\t}\n\n\tif (!*found)\n\t{\n\t\t/* ensure the values in MaintenanceDaemonDBData are zero */\n\t\tmemset(((char *) dbData) + sizeof(Oid), 0,\n\t\t\t   sizeof(MaintenanceDaemonDBData) - sizeof(Oid));\n\t}\n\n\treturn dbData;\n}\n\n\n/*\n * InitializeMaintenanceDaemonForMainDb is called in _PG_Init\n * at which stage we are not in a transaction or have databaseOid\n */\nvoid\nInitializeMaintenanceDaemonForMainDb(void)\n{\n\tif (strcmp(MainDb, \"\") == 0)\n\t{\n\t\telog(LOG, \"There is no designated Main database.\");\n\t\treturn;\n\t}\n\n\tCitusBackgroundWorkerConfig config = {\n\t\t.workerName = \"Citus Maintenance Daemon for Main DB\",\n\t\t.functionName = \"CitusMaintenanceDaemonMain\",\n\t\t.mainArg = (Datum) 0,\n\t\t.extensionOwner = InvalidOid,\n\t\t.needsNotification = false,\n\t\t.waitForStartup = false,\n\t\t.restartTime = CITUS_BGW_DEFAULT_RESTART_TIME,\n\t\t.startTime = CITUS_BGW_DEFAULT_START_TIME,\n\t\t.workerType = NULL, /* use default */\n\t\t.extraData = NULL,\n\t\t.extraDataSize = 0\n\t};\n\tBackgroundWorker worker;\n\tInitializeCitusBackgroundWorker(&worker, &config);\n\tRegisterBackgroundWorker(&worker);\n}\n\n\n/*\n * InitializeMaintenanceDaemonBackend, called at backend start and\n * configuration changes, is responsible for starting a per-database\n * maintenance worker if necessary.\n */\nvoid\nInitializeMaintenanceDaemonBackend(void)\n{\n\tOid extensionOwner = CitusExtensionOwner();\n\tbool found = false;\n\n\tLWLockAcquire(&MaintenanceDaemonControl->lock, LW_EXCLUSIVE);\n\n\tMaintenanceDaemonDBData *dbData = GetMaintenanceDaemonDBHashEntry(MyDatabaseId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  &found);\n\n\tif (dbData == NULL)\n\t{\n\t\tWarnMaintenanceDaemonNotStarted();\n\t\tLWLockRelease(&MaintenanceDaemonControl->lock);\n\t\treturn;\n\t}\n\n\tif (IsMaintenanceDaemon)\n\t{\n\t\t/*\n\t\t * InitializeMaintenanceDaemonBackend is called by the maintenance daemon\n\t\t * itself. In that case, we clearly don't need to start another maintenance\n\t\t * daemon.\n\t\t */\n\t\tLWLockRelease(&MaintenanceDaemonControl->lock);\n\t\treturn;\n\t}\n\n\tif (!found || !dbData->daemonStarted)\n\t{\n\t\tAssert(dbData->workerPid == 0);\n\n\t\tchar workerName[BGW_MAXLEN];\n\n\t\tSafeSnprintf(workerName, sizeof(workerName),\n\t\t\t\t\t \"Citus Maintenance Daemon: %u/%u\",\n\t\t\t\t\t MyDatabaseId, extensionOwner);\n\n\t\tCitusBackgroundWorkerConfig config = {\n\t\t\t.workerName = workerName,\n\t\t\t.functionName = \"CitusMaintenanceDaemonMain\",\n\t\t\t.mainArg = ObjectIdGetDatum(MyDatabaseId),\n\t\t\t.extensionOwner = extensionOwner,\n\t\t\t.needsNotification = true,\n\t\t\t.waitForStartup = true,\n\t\t\t.restartTime = CITUS_BGW_DEFAULT_RESTART_TIME,\n\t\t\t.startTime = CITUS_BGW_DEFAULT_START_TIME,\n\t\t\t.workerType = NULL, /* use default */\n\t\t\t.extraData = NULL,\n\t\t\t.extraDataSize = 0\n\t\t};\n\t\tBackgroundWorkerHandle *handle = RegisterCitusBackgroundWorker(&config);\n\n\t\tif (!handle)\n\t\t{\n\t\t\tWarnMaintenanceDaemonNotStarted();\n\t\t\tdbData->daemonStarted = false;\n\t\t\tLWLockRelease(&MaintenanceDaemonControl->lock);\n\n\t\t\treturn;\n\t\t}\n\n\t\tdbData->daemonStarted = true;\n\t\tdbData->userOid = extensionOwner;\n\t\tdbData->workerPid = 0;\n\t\tdbData->triggerNodeMetadataSync = false;\n\t\tLWLockRelease(&MaintenanceDaemonControl->lock);\n\n\t\tpfree(handle);\n\t}\n\telse\n\t{\n\t\tAssert(dbData->daemonStarted);\n\n\t\t/*\n\t\t * If owner of extension changed, wake up daemon. It'll notice and\n\t\t * restart.\n\t\t */\n\t\tif (dbData->userOid != extensionOwner)\n\t\t{\n\t\t\tdbData->userOid = extensionOwner;\n\t\t\tif (dbData->latch)\n\t\t\t{\n\t\t\t\tSetLatch(dbData->latch);\n\t\t\t}\n\t\t}\n\t\tLWLockRelease(&MaintenanceDaemonControl->lock);\n\t}\n}\n\n\n/*\n * WarnMaintenanceDaemonNotStarted warns that maintenanced couldn't be started.\n */\nstatic void\nWarnMaintenanceDaemonNotStarted(void)\n{\n\tereport(WARNING, (errmsg(\"could not start maintenance background worker\"),\n\t\t\t\t\t  errhint(\"Increasing max_worker_processes might help.\")));\n}\n\n\n/*\n * ConnectToDatabase connects to the database for the given databaseOid.\n * if databaseOid is 0, connects to MainDb and then creates a hash entry.\n * If a hash entry cannot be created for MainDb it exits the process requesting a restart.\n * However for regular databases, it exits without requesting a restart since another\n * subsequent backend is expected to start the Maintenance Daemon.\n * If the found hash entry has a valid workerPid, it exits\n * without requesting a restart since there is already a daemon running.\n */\nstatic MaintenanceDaemonDBData *\nConnectToDatabase(Oid databaseOid)\n{\n\tMaintenanceDaemonDBData *myDbData = NULL;\n\n\n\tbool isMainDb = false;\n\n\tLWLockAcquire(&MaintenanceDaemonControl->lock, LW_EXCLUSIVE);\n\n\n\tif (databaseOid == 0)\n\t{\n\t\tchar *databaseName = MainDb;\n\n\t\t/*\n\t\t * Since we cannot query databaseOid without initializing Postgres\n\t\t * first, connect to the database by name.\n\t\t */\n\t\tBackgroundWorkerInitializeConnection(databaseName, NULL, 0);\n\n\t\t/*\n\t\t * Now we have a valid MyDatabaseId.\n\t\t * Insert the hash entry for the database to the Maintenance Deamon Hash.\n\t\t */\n\t\tbool found = false;\n\n\t\tmyDbData = GetMaintenanceDaemonDBHashEntry(MyDatabaseId, &found);\n\n\t\tif (!myDbData)\n\t\t{\n\t\t\t/*\n\t\t\t * If an entry cannot be created,\n\t\t\t * return code of 1 requests worker restart\n\t\t\t * Since BackgroundWorker for the MainDb is only registered\n\t\t\t * once during server startup, we need to retry.\n\t\t\t */\n\t\t\tproc_exit(1);\n\t\t}\n\n\t\tif (found && myDbData->workerPid != 0)\n\t\t{\n\t\t\t/* Another maintenance daemon is running.*/\n\n\t\t\tproc_exit(0);\n\t\t}\n\n\t\tdatabaseOid = MyDatabaseId;\n\t\tmyDbData->userOid = GetSessionUserId();\n\t\tisMainDb = true;\n\t}\n\telse\n\t{\n\t\tmyDbData = (MaintenanceDaemonDBData *)\n\t\t\t\t   hash_search(MaintenanceDaemonDBHash, &databaseOid,\n\t\t\t\t\t\t\t   HASH_FIND, NULL);\n\n\t\tif (!myDbData)\n\t\t{\n\t\t\t/*\n\t\t\t * When the database crashes, background workers are restarted, but\n\t\t\t * the state in shared memory is lost. In that case, we exit and\n\t\t\t * wait for a session to call InitializeMaintenanceDaemonBackend\n\t\t\t * to properly add it to the hash.\n\t\t\t */\n\n\t\t\tproc_exit(0);\n\t\t}\n\n\t\tif (myDbData->workerPid != 0)\n\t\t{\n\t\t\t/*\n\t\t\t * Another maintenance daemon is running. This usually happens because\n\t\t\t * postgres restarts the daemon after an non-zero exit, and\n\t\t\t * InitializeMaintenanceDaemonBackend started one before postgres did.\n\t\t\t * In that case, the first one stays and the last one exits.\n\t\t\t */\n\n\t\t\tproc_exit(0);\n\t\t}\n\t}\n\n\tbefore_shmem_exit(MaintenanceDaemonShmemExit, ObjectIdGetDatum(databaseOid));\n\n\t/*\n\t * Signal that I am the maintenance daemon now.\n\t *\n\t * From this point, DROP DATABASE/EXTENSION will send a SIGTERM to me.\n\t */\n\tmyDbData->workerPid = MyProcPid;\n\n\t/*\n\t * Signal that we are running. This in mainly needed in case of restart after\n\t * an error, otherwise the daemonStarted flag is already true.\n\t */\n\tmyDbData->daemonStarted = true;\n\n\t/* wire up signals */\n\tpqsignal(SIGTERM, MaintenanceDaemonSigTermHandler);\n\tpqsignal(SIGHUP, MaintenanceDaemonSigHupHandler);\n\tBackgroundWorkerUnblockSignals();\n\n\tmyDbData->latch = MyLatch;\n\n\tIsMaintenanceDaemon = true;\n\n\tLWLockRelease(&MaintenanceDaemonControl->lock);\n\n\tmemset(&errorCallback, 0, sizeof(errorCallback));\n\terrorCallback.callback = MaintenanceDaemonErrorContext;\n\terrorCallback.arg = (void *) myDbData;\n\terrorCallback.previous = error_context_stack;\n\terror_context_stack = &errorCallback;\n\n\telog(LOG, \"starting maintenance daemon on database %u user %u\",\n\t\t databaseOid, myDbData->userOid);\n\n\tif (!isMainDb)\n\t{\n\t\t/* connect to database, after that we can actually access catalogs */\n\t\tBackgroundWorkerInitializeConnectionByOid(databaseOid, myDbData->userOid, 0);\n\t}\n\n\treturn myDbData;\n}\n\n\n/*\n * CitusMaintenanceDaemonMain is the maintenance daemon's main routine, it'll\n * be started by the background worker infrastructure.  If it errors out,\n * it'll be restarted after a few seconds.\n */\nvoid\nCitusMaintenanceDaemonMain(Datum main_arg)\n{\n\tOid databaseOid = DatumGetObjectId(main_arg);\n\tTimestampTz lastRecoveryTime = 0;\n\tTimestampTz lastShardCleanTime = 0;\n\tTimestampTz lastStatStatementsPurgeTime = 0;\n\tTimestampTz nextMetadataSyncTime = 0;\n\n\t/* state kept for the background tasks queue monitor */\n\tTimestampTz lastBackgroundTaskQueueCheck = GetCurrentTimestamp();\n\tBackgroundWorkerHandle *backgroundTasksQueueBgwHandle = NULL;\n\tbool backgroundTasksQueueWarnedForLock = false;\n\n\n\t/*\n\t * We do metadata sync in a separate background worker. We need its\n\t * handle to be able to check its status.\n\t */\n\tBackgroundWorkerHandle *metadataSyncBgwHandle = NULL;\n\n\tMaintenanceDaemonDBData *myDbData = ConnectToDatabase(databaseOid);\n\n\t/* make worker recognizable in pg_stat_activity */\n\tpgstat_report_appname(\"Citus Maintenance Daemon\");\n\n\t/*\n\t * Terminate orphaned metadata sync daemons spawned from previously terminated\n\t * or crashed maintenanced instances.\n\t */\n\tSignalMetadataSyncDaemon(MyDatabaseId, SIGTERM);\n\n\t/* enter main loop */\n\twhile (!got_SIGTERM)\n\t{\n\t\tint latchFlags = WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH;\n\t\tdouble timeout = 10000.0; /* use this if the deadlock detection is disabled */\n\t\tbool foundDeadlock = false;\n\n\t\tCHECK_FOR_INTERRUPTS();\n\n\t\tCitusTableCacheFlushInvalidatedEntries();\n\n\t\t/*\n\t\t * XXX: Each task should clear the metadata cache before every iteration\n\t\t * by calling InvalidateMetadataSystemCache(), because otherwise it\n\t\t * might contain stale OIDs. It appears that in some cases invalidation\n\t\t * messages for a DROP EXTENSION may arrive during these tasks and\n\t\t * this causes us to cache a stale pg_dist_node OID. We'd actually expect\n\t\t * all invalidations to arrive after obtaining a lock in LockCitusExtension.\n\t\t */\n\n\t\t/*\n\t\t * Perform Work. If a specific task needs to be called sooner than\n\t\t * timeout indicates, it's ok to lower it to that value. Expensive\n\t\t * tasks should do their own time math about whether to re-run checks.\n\t\t */\n\n\t\tpid_t metadataSyncBgwPid = 0;\n\t\tBgwHandleStatus metadataSyncStatus =\n\t\t\tmetadataSyncBgwHandle != NULL ?\n\t\t\tGetBackgroundWorkerPid(metadataSyncBgwHandle, &metadataSyncBgwPid) :\n\t\t\tBGWH_STOPPED;\n\n\t\tif (metadataSyncStatus != BGWH_STOPPED &&\n\t\t\tGetCurrentTimestamp() >= nextMetadataSyncTime)\n\t\t{\n\t\t\t/*\n\t\t\t * Metadata sync is still running, recheck in a short while.\n\t\t\t */\n\t\t\tint nextTimeout = MetadataSyncRetryInterval;\n\t\t\tnextMetadataSyncTime =\n\t\t\t\tTimestampTzPlusMilliseconds(GetCurrentTimestamp(), nextTimeout);\n\t\t\ttimeout = Min(timeout, nextTimeout);\n\t\t}\n\t\telse if (!RecoveryInProgress() &&\n\t\t\t\t metadataSyncStatus == BGWH_STOPPED &&\n\t\t\t\t (MetadataSyncTriggeredCheckAndReset(myDbData) ||\n\t\t\t\t  GetCurrentTimestamp() >= nextMetadataSyncTime))\n\t\t{\n\t\t\tif (metadataSyncBgwHandle)\n\t\t\t{\n\t\t\t\tpfree(metadataSyncBgwHandle);\n\t\t\t\tmetadataSyncBgwHandle = NULL;\n\t\t\t}\n\n\t\t\tInvalidateMetadataSystemCache();\n\t\t\tStartTransactionCommand();\n\t\t\tPushActiveSnapshot(GetTransactionSnapshot());\n\n\t\t\tint nextTimeout = MetadataSyncRetryInterval;\n\t\t\tbool syncMetadata = false;\n\n\t\t\tif (!LockCitusExtension())\n\t\t\t{\n\t\t\t\tereport(DEBUG1, (errmsg(\"could not lock the citus extension, \"\n\t\t\t\t\t\t\t\t\t\t\"skipping metadata sync\")));\n\t\t\t}\n\t\t\telse if (CheckCitusVersion(DEBUG1) && CitusHasBeenLoaded())\n\t\t\t{\n\t\t\t\tbool lockFailure = false;\n\t\t\t\tsyncMetadata = ShouldInitiateMetadataSync(&lockFailure);\n\n\t\t\t\t/*\n\t\t\t\t * If lock fails, we need to recheck in a short while. If we are\n\t\t\t\t * going to sync metadata, we should recheck in a short while to\n\t\t\t\t * see if it failed. Otherwise, we can wait longer.\n\t\t\t\t */\n\t\t\t\tnextTimeout = (lockFailure || syncMetadata) ?\n\t\t\t\t\t\t\t  MetadataSyncRetryInterval :\n\t\t\t\t\t\t\t  MetadataSyncInterval;\n\t\t\t}\n\n\t\t\tPopActiveSnapshot();\n\t\t\tCommitTransactionCommand();\n\n\t\t\tif (syncMetadata)\n\t\t\t{\n\t\t\t\tmetadataSyncBgwHandle =\n\t\t\t\t\tSpawnSyncNodeMetadataToNodes(MyDatabaseId, myDbData->userOid);\n\t\t\t}\n\n\t\t\tnextMetadataSyncTime =\n\t\t\t\tTimestampTzPlusMilliseconds(GetCurrentTimestamp(), nextTimeout);\n\t\t\ttimeout = Min(timeout, nextTimeout);\n\t\t}\n\n\t\t/*\n\t\t * If enabled, run 2PC recovery on primary nodes (where !RecoveryInProgress()),\n\t\t * since we'll write to the pg_dist_transaction log.\n\t\t */\n\t\tif (Recover2PCInterval > 0 && !RecoveryInProgress() &&\n\t\t\tTimestampDifferenceExceeds(lastRecoveryTime, GetCurrentTimestamp(),\n\t\t\t\t\t\t\t\t\t   Recover2PCInterval))\n\t\t{\n\t\t\tint recoveredTransactionCount = 0;\n\n\t\t\tInvalidateMetadataSystemCache();\n\t\t\tStartTransactionCommand();\n\n\t\t\tif (!LockCitusExtension())\n\t\t\t{\n\t\t\t\tereport(DEBUG1, (errmsg(\"could not lock the citus extension, \"\n\t\t\t\t\t\t\t\t\t\t\"skipping 2PC recovery\")));\n\t\t\t}\n\t\t\telse if (CheckCitusVersion(DEBUG1) && CitusHasBeenLoaded())\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Record last recovery time at start to ensure we run once per\n\t\t\t\t * Recover2PCInterval even if RecoverTwoPhaseCommits takes some time.\n\t\t\t\t */\n\t\t\t\tlastRecoveryTime = GetCurrentTimestamp();\n\n\t\t\t\trecoveredTransactionCount = RecoverTwoPhaseCommits();\n\t\t\t}\n\n\t\t\tCommitTransactionCommand();\n\n\t\t\tif (recoveredTransactionCount > 0)\n\t\t\t{\n\t\t\t\tereport(LOG, (errmsg(\"maintenance daemon recovered %d distributed \"\n\t\t\t\t\t\t\t\t\t \"transactions\",\n\t\t\t\t\t\t\t\t\t recoveredTransactionCount)));\n\t\t\t}\n\n\t\t\t/* make sure we don't wait too long */\n\t\t\ttimeout = Min(timeout, Recover2PCInterval);\n\t\t}\n\n\t\t/* the config value -1 disables the distributed deadlock detection  */\n\t\tif (DistributedDeadlockDetectionTimeoutFactor != -1.0)\n\t\t{\n\t\t\tdouble deadlockTimeout =\n\t\t\t\tDistributedDeadlockDetectionTimeoutFactor * (double) DeadlockTimeout;\n\n\t\t\tInvalidateMetadataSystemCache();\n\t\t\tStartTransactionCommand();\n\n\t\t\t/*\n\t\t\t * We skip the deadlock detection if citus extension\n\t\t\t * is not accessible.\n\t\t\t *\n\t\t\t * Similarly, we skip to run the deadlock checks if\n\t\t\t * there exists any version mismatch or the extension\n\t\t\t * is not fully created yet.\n\t\t\t */\n\t\t\tif (!LockCitusExtension())\n\t\t\t{\n\t\t\t\tereport(DEBUG1, (errmsg(\"could not lock the citus extension, \"\n\t\t\t\t\t\t\t\t\t\t\"skipping deadlock detection\")));\n\t\t\t}\n\t\t\telse if (CheckCitusVersion(DEBUG1) && CitusHasBeenLoaded())\n\t\t\t{\n\t\t\t\tfoundDeadlock = CheckForDistributedDeadlocks();\n\t\t\t}\n\n\t\t\tCommitTransactionCommand();\n\n\t\t\t/*\n\t\t\t * If we find any deadlocks, run the distributed deadlock detection\n\t\t\t * more often since it is quite possible that there are other\n\t\t\t * deadlocks need to be resolved.\n\t\t\t *\n\t\t\t * Thus, we use 1/20 of the calculated value. With the default\n\t\t\t * values (i.e., deadlock_timeout 1 seconds,\n\t\t\t * citus.distributed_deadlock_detection_factor 2), we'd be able to cancel\n\t\t\t * ~10 distributed deadlocks per second.\n\t\t\t */\n\t\t\tif (foundDeadlock)\n\t\t\t{\n\t\t\t\tdeadlockTimeout = deadlockTimeout / 20.0;\n\t\t\t}\n\n\t\t\t/* make sure we don't wait too long */\n\t\t\ttimeout = Min(timeout, deadlockTimeout);\n\t\t}\n\n\t\tif (!RecoveryInProgress() && DeferShardDeleteInterval > 0 &&\n\t\t\tTimestampDifferenceExceeds(lastShardCleanTime, GetCurrentTimestamp(),\n\t\t\t\t\t\t\t\t\t   DeferShardDeleteInterval))\n\t\t{\n\t\t\tint numberOfDroppedResources = 0;\n\n\t\t\tInvalidateMetadataSystemCache();\n\t\t\tStartTransactionCommand();\n\n\t\t\tif (!LockCitusExtension())\n\t\t\t{\n\t\t\t\tereport(DEBUG1, (errmsg(\n\t\t\t\t\t\t\t\t\t \"could not lock the citus extension, skipping shard cleaning\")));\n\t\t\t}\n\t\t\telse if (CheckCitusVersion(DEBUG1) && CitusHasBeenLoaded())\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Record last shard clean time at start to ensure we run once per\n\t\t\t\t * DeferShardDeleteInterval.\n\t\t\t\t */\n\t\t\t\tlastShardCleanTime = GetCurrentTimestamp();\n\n\t\t\t\tnumberOfDroppedResources = TryDropOrphanedResources();\n\t\t\t}\n\n\t\t\tCommitTransactionCommand();\n\n\t\t\tif (numberOfDroppedResources > 0)\n\t\t\t{\n\t\t\t\tereport(LOG, (errmsg(\"maintenance daemon dropped %d \"\n\t\t\t\t\t\t\t\t\t \"resources previously marked to be removed\",\n\t\t\t\t\t\t\t\t\t numberOfDroppedResources)));\n\t\t\t}\n\n\t\t\t/* make sure we don't wait too long */\n\t\t\ttimeout = Min(timeout, DeferShardDeleteInterval);\n\t\t}\n\n\t\tif (StatStatementsPurgeInterval > 0 &&\n\t\t\tStatStatementsTrack != STAT_STATEMENTS_TRACK_NONE &&\n\t\t\tTimestampDifferenceExceeds(lastStatStatementsPurgeTime, GetCurrentTimestamp(),\n\t\t\t\t\t\t\t\t\t   (StatStatementsPurgeInterval * 1000)))\n\t\t{\n\t\t\tStartTransactionCommand();\n\n\t\t\tif (!LockCitusExtension())\n\t\t\t{\n\t\t\t\tereport(DEBUG1, (errmsg(\"could not lock the citus extension, \"\n\t\t\t\t\t\t\t\t\t\t\"skipping stat statements purging\")));\n\t\t\t}\n\t\t\telse if (CheckCitusVersion(DEBUG1) && CitusHasBeenLoaded())\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Record last time we perform the purge to ensure we run once per\n\t\t\t\t * StatStatementsPurgeInterval.\n\t\t\t\t */\n\t\t\t\tlastStatStatementsPurgeTime = GetCurrentTimestamp();\n\n\t\t\t\tCitusQueryStatsSynchronizeEntries();\n\t\t\t}\n\n\t\t\tCommitTransactionCommand();\n\n\t\t\t/* make sure we don't wait too long, need to convert seconds to milliseconds */\n\t\t\ttimeout = Min(timeout, (StatStatementsPurgeInterval * 1000));\n\t\t}\n\n\t\tpid_t backgroundTaskQueueWorkerPid = 0;\n\t\tBgwHandleStatus backgroundTaskQueueWorkerStatus =\n\t\t\tbackgroundTasksQueueBgwHandle != NULL ? GetBackgroundWorkerPid(\n\t\t\t\tbackgroundTasksQueueBgwHandle, &backgroundTaskQueueWorkerPid) :\n\t\t\tBGWH_STOPPED;\n\t\tif (!RecoveryInProgress() && BackgroundTaskQueueCheckInterval > 0 &&\n\t\t\tTimestampDifferenceExceeds(lastBackgroundTaskQueueCheck,\n\t\t\t\t\t\t\t\t\t   GetCurrentTimestamp(),\n\t\t\t\t\t\t\t\t\t   BackgroundTaskQueueCheckInterval) &&\n\t\t\tbackgroundTaskQueueWorkerStatus == BGWH_STOPPED)\n\t\t{\n\t\t\t/* clear old background worker for task queue before checking for new tasks */\n\t\t\tif (backgroundTasksQueueBgwHandle)\n\t\t\t{\n\t\t\t\tpfree(backgroundTasksQueueBgwHandle);\n\t\t\t\tbackgroundTasksQueueBgwHandle = NULL;\n\t\t\t}\n\n\t\t\tStartTransactionCommand();\n\n\t\t\tbool shouldStartBackgroundTaskQueueBackgroundWorker = false;\n\t\t\tif (!LockCitusExtension())\n\t\t\t{\n\t\t\t\tereport(DEBUG1, (errmsg(\"could not lock the citus extension, \"\n\t\t\t\t\t\t\t\t\t\t\"skipping stat statements purging\")));\n\t\t\t}\n\t\t\telse if (CheckCitusVersion(DEBUG1) && CitusHasBeenLoaded())\n\t\t\t{\n\t\t\t\t/* perform catalog precheck */\n\t\t\t\tshouldStartBackgroundTaskQueueBackgroundWorker =\n\t\t\t\t\tHasRunnableBackgroundTask();\n\t\t\t}\n\n\t\t\tCommitTransactionCommand();\n\n\t\t\tif (shouldStartBackgroundTaskQueueBackgroundWorker)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * Before we start the background worker we want to check if an orphaned\n\t\t\t\t * one is still running. This could happen when the maintenance daemon\n\t\t\t\t * restarted in a way where the background task queue monitor wasn't\n\t\t\t\t * restarted.\n\t\t\t\t *\n\t\t\t\t * To check if an orphaned background task queue monitor is still running\n\t\t\t\t * we quickly acquire the lock without waiting. If we can't acquire the\n\t\t\t\t * lock this means that some other backed still has the lock. We prevent a\n\t\t\t\t * new backend from starting and log a warning that we found that another\n\t\t\t\t * process still holds the lock.\n\t\t\t\t */\n\t\t\t\tLOCKTAG tag = { 0 };\n\t\t\t\tSET_LOCKTAG_CITUS_OPERATION(tag, CITUS_BACKGROUND_TASK_MONITOR);\n\t\t\t\tconst bool sessionLock = false;\n\t\t\t\tconst bool dontWait = true;\n\t\t\t\tLockAcquireResult locked =\n\t\t\t\t\tLockAcquire(&tag, AccessExclusiveLock, sessionLock, dontWait);\n\n\t\t\t\tif (locked == LOCKACQUIRE_NOT_AVAIL)\n\t\t\t\t{\n\t\t\t\t\tif (!backgroundTasksQueueWarnedForLock)\n\t\t\t\t\t{\n\t\t\t\t\t\tereport(WARNING, (errmsg(\"background task queue monitor already \"\n\t\t\t\t\t\t\t\t\t\t\t\t \"held\"),\n\t\t\t\t\t\t\t\t\t\t  errdetail(\"the background task queue monitor \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"lock is held by another backend, \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"indicating the maintenance daemon \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"has lost track of an already \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"running background task queue \"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"monitor, not starting a new one\")));\n\t\t\t\t\t\tbackgroundTasksQueueWarnedForLock = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tLockRelease(&tag, AccessExclusiveLock, sessionLock);\n\n\t\t\t\t\t/* we were able to acquire the lock, reset the warning tracker */\n\t\t\t\t\tbackgroundTasksQueueWarnedForLock = false;\n\n\t\t\t\t\t/* spawn background worker */\n\t\t\t\t\tereport(LOG, (errmsg(\"found scheduled background tasks, starting new \"\n\t\t\t\t\t\t\t\t\t\t \"background task queue monitor\")));\n\n\t\t\t\t\tbackgroundTasksQueueBgwHandle =\n\t\t\t\t\t\tStartCitusBackgroundTaskQueueMonitor(MyDatabaseId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t myDbData->userOid);\n\n\t\t\t\t\tif (!backgroundTasksQueueBgwHandle ||\n\t\t\t\t\t\tGetBackgroundWorkerPid(backgroundTasksQueueBgwHandle,\n\t\t\t\t\t\t\t\t\t\t\t   &backgroundTaskQueueWorkerPid) ==\n\t\t\t\t\t\tBGWH_STOPPED)\n\t\t\t\t\t{\n\t\t\t\t\t\tereport(WARNING, (errmsg(\"unable to start background worker for \"\n\t\t\t\t\t\t\t\t\t\t\t\t \"background task execution\")));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* interval management */\n\t\t\tlastBackgroundTaskQueueCheck = GetCurrentTimestamp();\n\t\t\ttimeout = Min(timeout, BackgroundTaskQueueCheckInterval);\n\t\t}\n\n\t\t/*\n\t\t * Wait until timeout, or until somebody wakes us up. Also cast the timeout to\n\t\t * integer where we've calculated it using double for not losing the precision.\n\t\t */\n\t\tint rc = WaitLatch(MyLatch, latchFlags, (long) timeout, PG_WAIT_EXTENSION);\n\n\t\t/* emergency bailout if postmaster has died */\n\t\tif (rc & WL_POSTMASTER_DEATH)\n\t\t{\n\t\t\tproc_exit(1);\n\t\t}\n\n\t\tif (rc & WL_LATCH_SET)\n\t\t{\n\t\t\tResetLatch(MyLatch);\n\t\t\tCHECK_FOR_INTERRUPTS();\n\n\t\t\t/* check for changed configuration */\n\t\t\tif (myDbData->userOid != GetSessionUserId())\n\t\t\t{\n\t\t\t\t/* return code of 1 requests worker restart */\n\t\t\t\tproc_exit(1);\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Could also add code checking whether extension still exists,\n\t\t\t * but that'd complicate things a bit, because we'd have to delete\n\t\t\t * the shared memory entry.  There'd potentially be a race\n\t\t\t * condition where the extension gets re-created, checking that\n\t\t\t * this entry still exists, and it getting deleted just after.\n\t\t\t * Doesn't seem worth catering for that.\n\t\t\t */\n\t\t}\n\n\t\tif (got_SIGHUP)\n\t\t{\n\t\t\tgot_SIGHUP = false;\n\t\t\tProcessConfigFile(PGC_SIGHUP);\n\t\t}\n\t}\n\n\tif (metadataSyncBgwHandle)\n\t{\n\t\tTerminateBackgroundWorker(metadataSyncBgwHandle);\n\t}\n}\n\n\n/*\n * MaintenanceDaemonShmemSize computes how much shared memory is required.\n */\nsize_t\nMaintenanceDaemonShmemSize(void)\n{\n\tSize size = 0;\n\n\tsize = add_size(size, sizeof(MaintenanceDaemonControlData));\n\n\t/*\n\t * We request enough shared memory to have one hash-table entry for each\n\t * worker process. We couldn't start more anyway, so there's little point\n\t * in allocating more.\n\t */\n\tSize hashSize = hash_estimate_size(max_worker_processes,\n\t\t\t\t\t\t\t\t\t   sizeof(MaintenanceDaemonDBData));\n\tsize = add_size(size, hashSize);\n\n\treturn size;\n}\n\n\n/*\n * MaintenanceDaemonShmemInit initializes the requested shared memory for the\n * maintenance daemon.\n */\nvoid\nMaintenanceDaemonShmemInit(void)\n{\n\tbool alreadyInitialized = false;\n\tHASHCTL hashInfo;\n\n\tLWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);\n\n\tMaintenanceDaemonControl =\n\t\t(MaintenanceDaemonControlData *) ShmemInitStruct(\"Citus Maintenance Daemon\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t MaintenanceDaemonShmemSize(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t &alreadyInitialized);\n\n\t/*\n\t * Might already be initialized on EXEC_BACKEND type platforms that call\n\t * shared library initialization functions in every backend.\n\t */\n\tif (!alreadyInitialized)\n\t{\n\t\tMaintenanceDaemonControl->trancheId = LWLockNewTrancheId();\n\t\tMaintenanceDaemonControl->lockTrancheName = \"Citus Maintenance Daemon\";\n\t\tLWLockRegisterTranche(MaintenanceDaemonControl->trancheId,\n\t\t\t\t\t\t\t  MaintenanceDaemonControl->lockTrancheName);\n\n\t\tLWLockInitialize(&MaintenanceDaemonControl->lock,\n\t\t\t\t\t\t MaintenanceDaemonControl->trancheId);\n\t}\n\n\n\tmemset(&hashInfo, 0, sizeof(hashInfo));\n\thashInfo.keysize = sizeof(Oid);\n\thashInfo.entrysize = sizeof(MaintenanceDaemonDBData);\n\thashInfo.hash = tag_hash;\n\tint hashFlags = (HASH_ELEM | HASH_FUNCTION);\n\n\tMaintenanceDaemonDBHash = ShmemInitHash(\"Maintenance Database Hash\",\n\t\t\t\t\t\t\t\t\t\t\tmax_worker_processes, max_worker_processes,\n\t\t\t\t\t\t\t\t\t\t\t&hashInfo, hashFlags);\n\n\tLWLockRelease(AddinShmemInitLock);\n\n\tif (prev_shmem_startup_hook != NULL)\n\t{\n\t\tprev_shmem_startup_hook();\n\t}\n}\n\n\n/*\n * MaintenaceDaemonShmemExit is the before_shmem_exit handler for cleaning up MaintenanceDaemonDBHash\n */\nstatic void\nMaintenanceDaemonShmemExit(int code, Datum arg)\n{\n\tOid databaseOid = DatumGetObjectId(arg);\n\n\tLWLockAcquire(&MaintenanceDaemonControl->lock, LW_EXCLUSIVE);\n\n\tMaintenanceDaemonDBData *myDbData = (MaintenanceDaemonDBData *)\n\t\t\t\t\t\t\t\t\t\thash_search(MaintenanceDaemonDBHash, &databaseOid,\n\t\t\t\t\t\t\t\t\t\t\t\t\tHASH_FIND, NULL);\n\n\t/* myDbData is NULL after StopMaintenanceDaemon */\n\tif (myDbData != NULL)\n\t{\n\t\t/*\n\t\t * Once the maintenance daemon fails (e.g., due to an error in the main loop),\n\t\t * both Postgres tries to restart the failed daemon and Citus attempt to start\n\t\t * a new one. In that case, the one started by Citus ends up here.\n\t\t *\n\t\t * As the maintenance daemon that Citus tried to start, we might see the entry\n\t\t * for the daemon restarted by Postgres if the system was so slow that it\n\t\t * took a long time for us to be re-scheduled to call MaintenanceDaemonShmemExit(),\n\t\t * e.g., under valgrind testing.\n\t\t *\n\t\t * In that case, we should unregister ourself only if we are still the registered\n\t\t * maintenance daemon.\n\t\t */\n\t\tif (myDbData->workerPid == MyProcPid)\n\t\t{\n\t\t\tmyDbData->daemonStarted = false;\n\t\t\tmyDbData->workerPid = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(LOG, (errmsg(\n\t\t\t\t\t\t\t  \"maintenance daemon for database %u has already been replaced by \"\n\t\t\t\t\t\t\t  \"Postgres, skipping to unregister this maintenance daemon\",\n\t\t\t\t\t\t\t  databaseOid)));\n\t\t}\n\t}\n\n\tLWLockRelease(&MaintenanceDaemonControl->lock);\n}\n\n\n/* MaintenanceDaemonSigTermHandler sets the got_SIGTERM flag.*/\nstatic void\nMaintenanceDaemonSigTermHandler(SIGNAL_ARGS)\n{\n\tint save_errno = errno;\n\n\tgot_SIGTERM = true;\n\tif (MyProc != NULL)\n\t{\n\t\tSetLatch(&MyProc->procLatch);\n\t}\n\n\terrno = save_errno;\n}\n\n\n/*\n * MaintenanceDaemonSigHupHandler set a flag to re-read config file at next\n * convenient time.\n */\nstatic void\nMaintenanceDaemonSigHupHandler(SIGNAL_ARGS)\n{\n\tint save_errno = errno;\n\n\tgot_SIGHUP = true;\n\tif (MyProc != NULL)\n\t{\n\t\tSetLatch(&MyProc->procLatch);\n\t}\n\n\terrno = save_errno;\n}\n\n\n/*\n * MaintenanceDaemonErrorContext adds some context to log messages to make it\n * easier to associate them with the maintenance daemon.\n */\nstatic void\nMaintenanceDaemonErrorContext(void *arg)\n{\n\tMaintenanceDaemonDBData *myDbData = (MaintenanceDaemonDBData *) arg;\n\terrcontext(\"Citus maintenance daemon for database %u user %u\",\n\t\t\t   myDbData->databaseOid, myDbData->userOid);\n}\n\n\n/*\n * LockCitusExtension acquires a lock on the Citus extension or returns\n * false if the extension does not exist or is being dropped.\n */\nbool\nLockCitusExtension(void)\n{\n\tOid extensionOid = get_extension_oid(\"citus\", true);\n\tif (extensionOid == InvalidOid)\n\t{\n\t\t/* citus extension does not exist */\n\t\treturn false;\n\t}\n\n\tLockDatabaseObject(ExtensionRelationId, extensionOid, 0, AccessShareLock);\n\n\t/*\n\t * The extension may have been dropped and possibly recreated prior to\n\t * obtaining a lock. Check whether we still get the expected OID.\n\t */\n\tOid recheckExtensionOid = get_extension_oid(\"citus\", true);\n\tif (recheckExtensionOid != extensionOid)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * StopMaintenanceDaemon stops the maintenance daemon for the\n * given database and removes it from the maintenance daemon\n * control hash.\n */\nvoid\nStopMaintenanceDaemon(Oid databaseId)\n{\n\tbool found = false;\n\tpid_t workerPid = 0;\n\n\tLWLockAcquire(&MaintenanceDaemonControl->lock, LW_EXCLUSIVE);\n\n\tMaintenanceDaemonDBData *dbData = (MaintenanceDaemonDBData *) hash_search(\n\t\tMaintenanceDaemonDBHash,\n\t\t&databaseId,\n\t\tHASH_REMOVE, &found);\n\n\tif (found)\n\t{\n\t\tworkerPid = dbData->workerPid;\n\t}\n\n\tLWLockRelease(&MaintenanceDaemonControl->lock);\n\n\tif (workerPid > 0)\n\t{\n\t\tkill(workerPid, SIGTERM);\n\t}\n}\n\n\n/*\n * TriggerMetadataSync triggers the maintenance daemon to do\n * a node metadata sync for the given database.\n */\nvoid\nTriggerNodeMetadataSync(Oid databaseId)\n{\n\tbool found = false;\n\n\tLWLockAcquire(&MaintenanceDaemonControl->lock, LW_EXCLUSIVE);\n\n\tMaintenanceDaemonDBData *dbData = (MaintenanceDaemonDBData *) hash_search(\n\t\tMaintenanceDaemonDBHash,\n\t\t&databaseId,\n\t\tHASH_FIND, &found);\n\tif (found)\n\t{\n\t\tdbData->triggerNodeMetadataSync = true;\n\n\t\t/* set latch to wake-up the maintenance loop */\n\t\tSetLatch(dbData->latch);\n\t}\n\n\tLWLockRelease(&MaintenanceDaemonControl->lock);\n}\n\n\n/*\n * MetadataSyncTriggeredCheckAndReset checks if metadata sync has been\n * triggered for the given database, and resets the flag.\n */\nstatic bool\nMetadataSyncTriggeredCheckAndReset(MaintenanceDaemonDBData *dbData)\n{\n\tLWLockAcquire(&MaintenanceDaemonControl->lock, LW_EXCLUSIVE);\n\n\tbool metadataSyncTriggered = dbData->triggerNodeMetadataSync;\n\tdbData->triggerNodeMetadataSync = false;\n\n\tLWLockRelease(&MaintenanceDaemonControl->lock);\n\n\treturn metadataSyncTriggered;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/multi_partitioning_utils.c",
    "content": "/*\n * multi_partitioning_utils.c\n *\t  Utility functions for declarative partitioning\n *\n * Copyright (c) Citus Data, Inc.\n */\n#include \"postgres.h\"\n\n#include \"pgstat.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"catalog/index.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/partition.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_inherits.h\"\n#include \"commands/tablecmds.h\"\n#include \"common/string.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/pg_list.h\"\n#include \"partitioning/partdesc.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n#include \"utils/syscache.h\"\n#include \"utils/varlena.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/adaptive_executor.h\"\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\nstatic char * PartitionBound(Oid partitionId);\nstatic Relation try_relation_open_nolock(Oid relationId);\nstatic List * CreateFixPartitionConstraintsTaskList(Oid relationId);\nstatic List * WorkerFixPartitionConstraintCommandList(Oid relationId, uint64 shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *checkConstraintList);\nstatic void CreateFixPartitionShardIndexNames(Oid parentRelationId,\n\t\t\t\t\t\t\t\t\t\t\t  Oid partitionRelationId,\n\t\t\t\t\t\t\t\t\t\t\t  Oid parentIndexOid);\nstatic List * WorkerFixPartitionShardIndexNamesCommandList(uint64 parentShardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   List *indexIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   Oid partitionRelationId);\nstatic List * WorkerFixPartitionShardIndexNamesCommandListForParentShardIndex(char *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  qualifiedParentShardIndexName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  parentIndexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionRelationId);\nstatic List * WorkerFixPartitionShardIndexNamesCommandListForPartitionIndex(Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionIndexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tqualifiedParentShardIndexName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionId);\nstatic List * CheckConstraintNameListForRelation(Oid relationId);\nstatic bool RelationHasConstraint(Oid relationId, char *constraintName);\nstatic char * RenameConstraintCommand(Oid relationId, char *constraintName,\n\t\t\t\t\t\t\t\t\t  char *newConstraintName);\n\n\nPG_FUNCTION_INFO_V1(fix_pre_citus10_partitioned_table_constraint_names);\nPG_FUNCTION_INFO_V1(worker_fix_pre_citus10_partitioned_table_constraint_names);\nPG_FUNCTION_INFO_V1(fix_partition_shard_index_names);\nPG_FUNCTION_INFO_V1(worker_fix_partition_shard_index_names);\n\n\n/*\n * fix_pre_citus10_partitioned_table_constraint_names fixes the constraint names of\n * partitioned table shards on workers.\n *\n * Constraint names for partitioned table shards should have shardId suffixes if and only\n * if they are unique or foreign key constraints. We mistakenly appended shardIds to\n * constraint names on ALTER TABLE dist_part_table ADD CONSTRAINT .. queries prior to\n * Citus 10. fix_pre_citus10_partitioned_table_constraint_names determines if this is the\n * case, and renames constraints back to their original names on shards.\n */\nDatum\nfix_pre_citus10_partitioned_table_constraint_names(PG_FUNCTION_ARGS)\n{\n\tOid relationId = PG_GETARG_OID(0);\n\tEnsureCoordinator();\n\n\tif (!PartitionedTable(relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not fix partition constraints: \"\n\t\t\t\t\t\t\t   \"relation does not exist or is not partitioned\")));\n\t}\n\tif (!IsCitusTable(relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"fix_pre_citus10_partitioned_table_constraint_names can \"\n\t\t\t\t\t\t\t   \"only be called for distributed partitioned tables\")));\n\t}\n\n\tList *taskList = CreateFixPartitionConstraintsTaskList(relationId);\n\n\t/* do not do anything if there are no constraints that should be fixed */\n\tif (taskList != NIL)\n\t{\n\t\tbool localExecutionSupported = true;\n\t\tExecuteUtilityTaskList(taskList, localExecutionSupported);\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * worker_fix_pre_citus10_partitioned_table_constraint_names fixes the constraint names on a worker given a shell\n * table name and shard id.\n */\nDatum\nworker_fix_pre_citus10_partitioned_table_constraint_names(PG_FUNCTION_ARGS)\n{\n\tOid relationId = PG_GETARG_OID(0);\n\tint64 shardId = PG_GETARG_INT32(1);\n\ttext *constraintNameText = PG_GETARG_TEXT_P(2);\n\n\tif (!PartitionedTable(relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not fix partition constraints: \"\n\t\t\t\t\t\t\t   \"relation does not exist or is not partitioned\")));\n\t}\n\n\tchar *constraintName = text_to_cstring(constraintNameText);\n\tchar *shardIdAppendedConstraintName = pstrdup(constraintName);\n\tAppendShardIdToName(&shardIdAppendedConstraintName, shardId);\n\n\t/* if shardId was appended to the constraint name, rename back to original */\n\tif (RelationHasConstraint(relationId, shardIdAppendedConstraintName))\n\t{\n\t\tchar *renameConstraintDDLCommand =\n\t\t\tRenameConstraintCommand(relationId, shardIdAppendedConstraintName,\n\t\t\t\t\t\t\t\t\tconstraintName);\n\t\tExecuteAndLogUtilityCommand(renameConstraintDDLCommand);\n\t}\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * fix_partition_shard_index_names fixes the index names of shards of partitions of\n * partitioned tables on workers. If the input is a partition rather than a partitioned\n * table, we only fix the index names of shards of that particular partition.\n *\n * When running CREATE INDEX on parent_table, we didn't explicitly create the index on\n * each partition as well. Postgres created indexes for partitions in the coordinator,\n * and also in the workers. Actually, Postgres auto-generates index names when auto-creating\n * indexes on each partition shard of the parent shards. If index name is too long, it\n * truncates the name and adds _idx postfix to it. However, when truncating the name, the\n * shardId of the partition shard can be lost. This may result in the same index name used for\n * the partition shell table and one of the partition shards.\n * For more details, check issue #4962 https://github.com/citusdata/citus/issues/4962\n *\n * fix_partition_shard_index_names renames indexes of shards of partition tables to include\n * the shardId at the end of the name, regardless of whether index name was long or short\n * As a result there will be no index name ending in _idx, rather all will end in _{shardid}\n *\n * Algorithm is:\n * foreach parentShard in shardListOfParentTableId:\n *  foreach parentIndex on parent:\n *      generate qualifiedParentShardIndexName -> parentShardIndex\n *      foreach inheritedPartitionIndex on parentIndex:\n *          get table relation of inheritedPartitionIndex -> partitionId\n *          foreach partitionShard in shardListOfPartitionid:\n *              generate qualifiedPartitionShardName -> partitionShard\n *              generate newPartitionShardIndexName\n *              (the following happens in the worker node)\n *              foreach inheritedPartitionShardIndex on parentShardIndex:\n *                  if table relation of inheritedPartitionShardIndex is partitionShard:\n *                      if inheritedPartitionShardIndex does not have proper name:\n *                          Rename(inheritedPartitionShardIndex, newPartitionShardIndexName)\n *                      break\n */\nDatum\nfix_partition_shard_index_names(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\tEnsureCoordinator();\n\n\tOid relationId = PG_GETARG_OID(0);\n\tOid parentIndexOid = InvalidOid; /* fix all the indexes */\n\n\tif (!IsCitusTable(relationId))\n\t{\n\t\tereport(ERROR, (errmsg(\"fix_partition_shard_index_names can only be called \"\n\t\t\t\t\t\t\t   \"for Citus tables\")));\n\t}\n\n\tEnsureTableOwner(relationId);\n\n\tFixPartitionShardIndexNames(relationId, parentIndexOid);\n\n\t/*\n\t * This UDF is called from fix_all_partition_shard_index_names() which iterates\n\t * over all the partitioned tables. There is no need to hold all the distributed\n\t * table metadata until the end of the transaction for the input table.\n\t */\n\tCitusTableCacheFlushInvalidatedEntries();\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * worker_fix_partition_shard_index_names fixes the index name of the index on given\n * partition shard that has parent the given parent index.\n * The parent index should be the index of a shard of a distributed partitioned table.\n */\nDatum\nworker_fix_partition_shard_index_names(PG_FUNCTION_ARGS)\n{\n\tOid parentShardIndexId = PG_GETARG_OID(0);\n\n\ttext *partitionShardName = PG_GETARG_TEXT_P(1);\n\n\t/* resolve partitionShardId from passed in schema and partition shard name */\n\tList *partitionShardNameList = textToQualifiedNameList(partitionShardName);\n\tRangeVar *partitionShard = makeRangeVarFromNameList(partitionShardNameList);\n\n\t/* lock the relation with the lock mode */\n\tbool missing_ok = true;\n\tOid partitionShardId = RangeVarGetRelid(partitionShard, NoLock, missing_ok);\n\n\tif (!OidIsValid(partitionShardId))\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\tCheckCitusVersion(ERROR);\n\tEnsureTableOwner(partitionShardId);\n\n\ttext *newPartitionShardIndexNameText = PG_GETARG_TEXT_P(2);\n\tchar *newPartitionShardIndexName = text_to_cstring(\n\t\tnewPartitionShardIndexNameText);\n\n\tif (!has_subclass(parentShardIndexId))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not fix child index names: \"\n\t\t\t\t\t\t\t   \"index is not partitioned\")));\n\t}\n\n\tList *partitionShardIndexIds = find_inheritance_children(parentShardIndexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ShareRowExclusiveLock);\n\tOid partitionShardIndexId = InvalidOid;\n\tforeach_declared_oid(partitionShardIndexId, partitionShardIndexIds)\n\t{\n\t\tif (IndexGetRelation(partitionShardIndexId, false) == partitionShardId)\n\t\t{\n\t\t\tchar *partitionShardIndexName = get_rel_name(partitionShardIndexId);\n\t\t\tif (ExtractShardIdFromTableName(partitionShardIndexName, missing_ok) ==\n\t\t\t\tINVALID_SHARD_ID)\n\t\t\t{\n\t\t\t\t/*\n\t\t\t\t * ExtractShardIdFromTableName will return INVALID_SHARD_ID if\n\t\t\t\t * partitionShardIndexName doesn't end in _shardid. In that case,\n\t\t\t\t * we want to rename this partition shard index to newPartitionShardIndexName,\n\t\t\t\t * which ends in _shardid, hence we maintain naming consistency:\n\t\t\t\t * we can reach this partition shard index by conventional Citus naming\n\t\t\t\t */\n\t\t\t\tRenameStmt *stmt = makeNode(RenameStmt);\n\n\t\t\t\tstmt->renameType = OBJECT_INDEX;\n\t\t\t\tstmt->missing_ok = false;\n\t\t\t\tchar *idxNamespace = get_namespace_name(get_rel_namespace(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionShardIndexId));\n\t\t\t\tstmt->relation = makeRangeVar(idxNamespace, partitionShardIndexName, -1);\n\t\t\t\tstmt->newname = newPartitionShardIndexName;\n\n\t\t\t\tRenameRelation(stmt);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * FixPartitionShardIndexNames gets a relationId. The input relationId should be\n * either a parent or partition table. If it is a parent table, then all the\n * index names on all the partitions are fixed. If it is a partition, only the\n * specific partition is fixed.\n *\n * The second parentIndexOid parameter is optional. If provided a valid Oid, only\n * that specific index name is fixed.\n */\nvoid\nFixPartitionShardIndexNames(Oid relationId, Oid parentIndexOid)\n{\n\tRelation relation = try_relation_open(relationId, AccessShareLock);\n\n\tif (relation == NULL)\n\t{\n\t\tereport(NOTICE, (errmsg(\"relation with OID %u does not exist, skipping\",\n\t\t\t\t\t\t\t\trelationId)));\n\t\treturn;\n\t}\n\n\t/* at this point, we should only be dealing with Citus tables */\n\tAssert(IsCitusTable(relationId));\n\n\tOid parentRelationId = InvalidOid;\n\tOid partitionRelationId = InvalidOid;\n\n\tif (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)\n\t{\n\t\tparentRelationId = relationId;\n\t}\n\telse if (PartitionTable(relationId))\n\t{\n\t\tparentRelationId = PartitionParentOid(relationId);\n\t\tpartitionRelationId = relationId;\n\t}\n\telse\n\t{\n\t\tchar *relname = pstrdup(RelationGetRelationName(relation));\n\t\trelation_close(relation, NoLock);\n\t\tereport(ERROR, (errmsg(\"Fixing shard index names is only applicable to \"\n\t\t\t\t\t\t\t   \"partitioned tables or partitions, \"\n\t\t\t\t\t\t\t   \"and \\\"%s\\\" is neither\",\n\t\t\t\t\t\t\t   relname)));\n\t}\n\n\tCreateFixPartitionShardIndexNames(parentRelationId,\n\t\t\t\t\t\t\t\t\t  partitionRelationId,\n\t\t\t\t\t\t\t\t\t  parentIndexOid);\n\n\trelation_close(relation, NoLock);\n}\n\n\n/*\n * CreateFixPartitionConstraintsTaskList goes over all the partitions of a distributed\n * partitioned table, and creates the list of tasks to execute\n * worker_fix_pre_citus10_partitioned_table_constraint_names UDF on worker nodes.\n */\nstatic List *\nCreateFixPartitionConstraintsTaskList(Oid relationId)\n{\n\tList *taskList = NIL;\n\n\t/* enumerate the tasks when putting them to the taskList */\n\tint taskId = 1;\n\tList *checkConstraintList = CheckConstraintNameListForRelation(relationId);\n\n\t/* early exit if the relation does not have any check constraints */\n\tif (checkConstraintList == NIL)\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\n\t/* lock metadata before getting placement lists */\n\tLockShardListMetadata(shardIntervalList, ShareLock);\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tuint64 shardId = shardInterval->shardId;\n\n\t\tList *queryStringList = WorkerFixPartitionConstraintCommandList(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcheckConstraintList);\n\n\t\tTask *task = CitusMakeNode(Task);\n\t\ttask->jobId = INVALID_JOB_ID;\n\t\ttask->taskId = taskId++;\n\n\t\ttask->taskType = DDL_TASK;\n\t\tSetTaskQueryStringList(task, queryStringList);\n\t\ttask->dependentTaskList = NULL;\n\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\ttask->anchorShardId = shardId;\n\t\ttask->taskPlacementList = ActiveShardPlacementList(shardId);\n\n\t\ttaskList = lappend(taskList, task);\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * CheckConstraintNameListForRelation returns a list of names of CHECK constraints\n * for a relation.\n */\nstatic List *\nCheckConstraintNameListForRelation(Oid relationId)\n{\n\tList *constraintNameList = NIL;\n\n\tint scanKeyCount = 2;\n\tScanKeyData scanKey[2];\n\n\tRelation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));\n\tScanKeyInit(&scanKey[1], Anum_pg_constraint_contype,\n\t\t\t\tBTEqualStrategyNumber, F_CHAREQ, CharGetDatum(CONSTRAINT_CHECK));\n\n\tbool useIndex = false;\n\tSysScanDesc scanDescriptor = systable_beginscan(pgConstraint, InvalidOid, useIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\twhile (HeapTupleIsValid(heapTuple))\n\t{\n\t\tForm_pg_constraint constraintForm = (Form_pg_constraint) GETSTRUCT(heapTuple);\n\t\tchar *constraintName = NameStr(constraintForm->conname);\n\t\tconstraintNameList = lappend(constraintNameList, pstrdup(constraintName));\n\n\t\theapTuple = systable_getnext(scanDescriptor);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgConstraint, NoLock);\n\n\treturn constraintNameList;\n}\n\n\n/*\n * WorkerFixPartitionConstraintCommandList creates a list of queries that will fix\n * all check constraint names of a shard.\n */\nstatic List *\nWorkerFixPartitionConstraintCommandList(Oid relationId, uint64 shardId,\n\t\t\t\t\t\t\t\t\t\tList *checkConstraintList)\n{\n\tList *commandList = NIL;\n\tOid schemaId = get_rel_namespace(relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\tchar *relationName = get_rel_name(relationId);\n\tchar *shardRelationName = pstrdup(relationName);\n\n\t/* build shard relation name */\n\tAppendShardIdToName(&shardRelationName, shardId);\n\n\tchar *quotedShardName = quote_qualified_identifier(schemaName, shardRelationName);\n\n\tchar *constraintName = NULL;\n\tforeach_declared_ptr(constraintName, checkConstraintList)\n\t{\n\t\tStringInfo shardQueryString = makeStringInfo();\n\t\tappendStringInfo(shardQueryString,\n\t\t\t\t\t\t \"SELECT worker_fix_pre_citus10_partitioned_table_constraint_names(%s::regclass, \"\n\t\t\t\t\t\t UINT64_FORMAT \", %s::text)\",\n\t\t\t\t\t\t quote_literal_cstr(quotedShardName), shardId,\n\t\t\t\t\t\t quote_literal_cstr(constraintName));\n\t\tcommandList = lappend(commandList, shardQueryString->data);\n\t}\n\n\treturn commandList;\n}\n\n\n/*\n * CreateFixPartitionShardIndexNamesTaskList goes over all the\n * indexes of a distributed partitioned table unless parentIndexOid\n * is valid. If it is valid, only the given index is processed.\n *\n * The function creates the list of tasks to execute\n * worker_fix_partition_shard_index_names() on worker nodes.\n *\n * When the partitionRelationId is a valid Oid, the function only operates on the\n * given partition. Otherwise, the function create tasks for all the partitions.\n *\n * So, for example, if a new partition is created, we only need to fix only for the\n * new partition, hence partitionRelationId should be a valid Oid. However, if a new\n * index/constraint is created on the parent, we should fix all the partitions, hence\n * partitionRelationId should be InvalidOid.\n *\n * As a reflection of the above, we always create parent_table_shard_count tasks.\n * When we need to fix all the partitions, each task with parent_indexes_count\n * times partition_count query strings. When we need to fix a single\n * partition each task will have parent_indexes_count query strings. When we need\n * to fix a single index, parent_indexes_count becomes 1.\n */\nstatic void\nCreateFixPartitionShardIndexNames(Oid parentRelationId, Oid partitionRelationId,\n\t\t\t\t\t\t\t\t  Oid parentIndexOid)\n{\n\tList *partitionList = PartitionList(parentRelationId);\n\tif (partitionList == NIL)\n\t{\n\t\t/* early exit if the parent relation does not have any partitions */\n\t\treturn;\n\t}\n\n\tRelation parentRelation = RelationIdGetRelation(parentRelationId);\n\tif (!RelationIsValid(parentRelation))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not open relation with OID %u\", parentRelationId)));\n\t}\n\n\tList *parentIndexIdList = NIL;\n\n\tif (parentIndexOid != InvalidOid)\n\t{\n\t\tparentIndexIdList = list_make1_oid(parentIndexOid);\n\t}\n\telse\n\t{\n\t\tparentIndexIdList = RelationGetIndexList(parentRelation);\n\t}\n\n\tif (parentIndexIdList == NIL)\n\t{\n\t\t/* early exit if the parent relation does not have any indexes */\n\t\tRelationClose(parentRelation);\n\t\treturn;\n\t}\n\n\t/*\n\t * Lock shard metadata, if a specific partition is provided, lock that. Otherwise,\n\t * lock all partitions.\n\t */\n\tif (OidIsValid(partitionRelationId))\n\t{\n\t\t/* if a partition was provided we only need to lock that partition's metadata */\n\t\tList *partitionShardIntervalList = LoadShardIntervalList(partitionRelationId);\n\t\tLockShardListMetadata(partitionShardIntervalList, ShareLock);\n\t}\n\telse\n\t{\n\t\tOid partitionId = InvalidOid;\n\t\tforeach_declared_oid(partitionId, partitionList)\n\t\t{\n\t\t\tList *partitionShardIntervalList = LoadShardIntervalList(partitionId);\n\t\t\tLockShardListMetadata(partitionShardIntervalList, ShareLock);\n\t\t}\n\t}\n\n\tList *parentShardIntervalList = LoadShardIntervalList(parentRelationId);\n\n\t/* lock metadata before getting placement lists */\n\tLockShardListMetadata(parentShardIntervalList, ShareLock);\n\n\tMemoryContext localContext = AllocSetContextCreate(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"CreateFixPartitionShardIndexNames\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t   ALLOCSET_DEFAULT_SIZES);\n\tMemoryContext oldContext = MemoryContextSwitchTo(localContext);\n\n\tint taskId = 1;\n\n\tShardInterval *parentShardInterval = NULL;\n\tforeach_declared_ptr(parentShardInterval, parentShardIntervalList)\n\t{\n\t\tuint64 parentShardId = parentShardInterval->shardId;\n\n\t\tList *queryStringList =\n\t\t\tWorkerFixPartitionShardIndexNamesCommandList(parentShardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t parentIndexIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t partitionRelationId);\n\t\tif (queryStringList != NIL)\n\t\t{\n\t\t\tTask *task = CitusMakeNode(Task);\n\t\t\ttask->jobId = INVALID_JOB_ID;\n\t\t\ttask->taskId = taskId++;\n\t\t\ttask->taskType = DDL_TASK;\n\n\t\t\tchar *prefix = \"SELECT pg_catalog.citus_run_local_command($$\";\n\t\t\tchar *postfix = \"$$)\";\n\t\t\tchar *string = StringJoinParams(queryStringList, ';', prefix, postfix);\n\n\t\t\tSetTaskQueryString(task, string);\n\n\n\t\t\ttask->dependentTaskList = NULL;\n\t\t\ttask->replicationModel = REPLICATION_MODEL_INVALID;\n\t\t\ttask->anchorShardId = parentShardId;\n\t\t\ttask->taskPlacementList = ActiveShardPlacementList(parentShardId);\n\n\t\t\tbool localExecutionSupported = true;\n\t\t\tExecuteUtilityTaskList(list_make1(task), localExecutionSupported);\n\t\t}\n\n\t\t/* after every iteration, clean-up all the memory associated with it */\n\t\tMemoryContextReset(localContext);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\n\tRelationClose(parentRelation);\n}\n\n\n/*\n * WorkerFixPartitionShardIndexNamesCommandList creates a list of queries that will fix\n * all child index names of parent indexes on given shard of parent partitioned table.\n */\nstatic List *\nWorkerFixPartitionShardIndexNamesCommandList(uint64 parentShardId,\n\t\t\t\t\t\t\t\t\t\t\t List *parentIndexIdList,\n\t\t\t\t\t\t\t\t\t\t\t Oid partitionRelationId)\n{\n\tList *commandList = NIL;\n\tOid parentIndexId = InvalidOid;\n\tforeach_declared_oid(parentIndexId, parentIndexIdList)\n\t{\n\t\tif (!has_subclass(parentIndexId))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Get the qualified name of the corresponding index of given parent index\n\t\t * in the parent shard with given parentShardId\n\t\t */\n\t\tchar *parentIndexName = get_rel_name(parentIndexId);\n\t\tchar *parentShardIndexName = pstrdup(parentIndexName);\n\t\tAppendShardIdToName(&parentShardIndexName, parentShardId);\n\t\tOid schemaId = get_rel_namespace(parentIndexId);\n\t\tchar *schemaName = get_namespace_name(schemaId);\n\t\tchar *qualifiedParentShardIndexName = quote_qualified_identifier(schemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t parentShardIndexName);\n\t\tList *commands = WorkerFixPartitionShardIndexNamesCommandListForParentShardIndex(\n\t\t\tqualifiedParentShardIndexName, parentIndexId, partitionRelationId);\n\t\tcommandList = list_concat(commandList, commands);\n\t}\n\n\treturn commandList;\n}\n\n\n/*\n * WorkerFixPartitionShardIndexNamesCommandListForParentShardIndex creates a list\n * of queries that will fix the child index names of given index on shard\n * of parent partitioned table.\n *\n * In case a partition was provided as argument (partitionRelationId isn't InvalidOid)\n * the list of queries will include only the child indexes whose relation is the\n * given partition. Otherwise, all the partitions are included.\n */\nstatic List *\nWorkerFixPartitionShardIndexNamesCommandListForParentShardIndex(char *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tqualifiedParentShardIndexName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOid parentIndexId, Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpartitionRelationId)\n{\n\tList *commandList = NIL;\n\n\t/*\n\t * Get the list of all partition indexes that are children of current\n\t * index on parent\n\t */\n\tList *partitionIndexIds =\n\t\tfind_inheritance_children(parentIndexId, ShareRowExclusiveLock);\n\n\tbool addAllPartitions = (partitionRelationId == InvalidOid);\n\tOid partitionIndexId = InvalidOid;\n\tforeach_declared_oid(partitionIndexId, partitionIndexIds)\n\t{\n\t\tOid partitionId = IndexGetRelation(partitionIndexId, false);\n\t\tif (addAllPartitions || partitionId == partitionRelationId)\n\t\t{\n\t\t\tList *commands =\n\t\t\t\tWorkerFixPartitionShardIndexNamesCommandListForPartitionIndex(\n\t\t\t\t\tpartitionIndexId, qualifiedParentShardIndexName, partitionId);\n\t\t\tcommandList = list_concat(commandList, commands);\n\t\t}\n\t}\n\treturn commandList;\n}\n\n\n/*\n * WorkerFixPartitionShardIndexNamesCommandListForPartitionIndex creates a list of queries that will fix\n * all child index names of given index on shard of parent partitioned table, whose table relation is a shard\n * of the partition that is the table relation of given partitionIndexId, which is partitionId\n */\nstatic List *\nWorkerFixPartitionShardIndexNamesCommandListForPartitionIndex(Oid partitionIndexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  char *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  qualifiedParentShardIndexName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Oid partitionId)\n{\n\tList *commandList = NIL;\n\n\t/* get info for this partition relation of this index*/\n\tchar *partitionIndexName = get_rel_name(partitionIndexId);\n\tchar *partitionName = get_rel_name(partitionId);\n\tchar *partitionSchemaName = get_namespace_name(get_rel_namespace(partitionId));\n\tList *partitionShardIntervalList = LoadShardIntervalList(partitionId);\n\n\tShardInterval *partitionShardInterval = NULL;\n\tforeach_declared_ptr(partitionShardInterval, partitionShardIntervalList)\n\t{\n\t\t/*\n\t\t * Prepare commands for each shard of current partition\n\t\t * to fix the index name that corresponds to the\n\t\t * current parent index name\n\t\t */\n\t\tuint64 partitionShardId = partitionShardInterval->shardId;\n\n\t\t/* get qualified partition shard name */\n\t\tchar *partitionShardName = pstrdup(partitionName);\n\t\tAppendShardIdToName(&partitionShardName, partitionShardId);\n\t\tchar *qualifiedPartitionShardName = quote_qualified_identifier(\n\t\t\tpartitionSchemaName,\n\t\t\tpartitionShardName);\n\n\t\t/* generate the new correct index name */\n\t\tchar *newPartitionShardIndexName = pstrdup(partitionIndexName);\n\t\tAppendShardIdToName(&newPartitionShardIndexName, partitionShardId);\n\n\t\t/* create worker_fix_partition_shard_index_names command */\n\t\tStringInfo shardQueryString = makeStringInfo();\n\t\tappendStringInfo(shardQueryString,\n\t\t\t\t\t\t \"SELECT worker_fix_partition_shard_index_names(%s::regclass, %s, %s)\",\n\t\t\t\t\t\t quote_literal_cstr(qualifiedParentShardIndexName),\n\t\t\t\t\t\t quote_literal_cstr(qualifiedPartitionShardName),\n\t\t\t\t\t\t quote_literal_cstr(newPartitionShardIndexName));\n\t\tcommandList = lappend(commandList, shardQueryString->data);\n\t}\n\n\treturn commandList;\n}\n\n\n/*\n * RelationHasConstraint checks if a relation has a constraint with a given name.\n */\nstatic bool\nRelationHasConstraint(Oid relationId, char *constraintName)\n{\n\tbool found = false;\n\n\tint scanKeyCount = 2;\n\tScanKeyData scanKey[2];\n\n\tRelation pgConstraint = table_open(ConstraintRelationId, AccessShareLock);\n\n\tScanKeyInit(&scanKey[0], Anum_pg_constraint_conrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId));\n\tScanKeyInit(&scanKey[1], Anum_pg_constraint_conname,\n\t\t\t\tBTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(constraintName));\n\n\tbool useIndex = false;\n\tSysScanDesc scanDescriptor = systable_beginscan(pgConstraint, InvalidOid, useIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNULL, scanKeyCount, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext(scanDescriptor);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tfound = true;\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgConstraint, NoLock);\n\n\treturn found;\n}\n\n\n/*\n * RenameConstraintCommand creates the query string that will rename a constraint\n */\nstatic char *\nRenameConstraintCommand(Oid relationId, char *constraintName, char *newConstraintName)\n{\n\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\tconst char *quotedConstraintName = quote_identifier(constraintName);\n\tconst char *quotedNewConstraintName = quote_identifier(newConstraintName);\n\n\tStringInfo renameCommand = makeStringInfo();\n\tappendStringInfo(renameCommand, \"ALTER TABLE %s RENAME CONSTRAINT %s TO %s\",\n\t\t\t\t\t qualifiedRelationName, quotedConstraintName,\n\t\t\t\t\t quotedNewConstraintName);\n\n\treturn renameCommand->data;\n}\n\n\n/*\n * Returns true if the given relation is a partitioned table.\n */\nbool\nPartitionedTable(Oid relationId)\n{\n\tRelation rel = try_relation_open(relationId, AccessShareLock);\n\n\t/* don't error out for tables that are dropped */\n\tif (rel == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tbool partitionedTable = false;\n\n\tif (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)\n\t{\n\t\tpartitionedTable = true;\n\t}\n\n\t/* keep the lock */\n\ttable_close(rel, NoLock);\n\n\treturn partitionedTable;\n}\n\n\n/*\n * Returns true if the given relation is a partitioned table. The function\n * doesn't acquire any locks on the input relation, thus the caller is\n * reponsible for holding the appropriate locks.\n */\nbool\nPartitionedTableNoLock(Oid relationId)\n{\n\tRelation rel = try_relation_open_nolock(relationId);\n\tbool partitionedTable = false;\n\n\t/* don't error out for tables that are dropped */\n\tif (rel == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)\n\t{\n\t\tpartitionedTable = true;\n\t}\n\n\t/* keep the lock */\n\ttable_close(rel, NoLock);\n\n\treturn partitionedTable;\n}\n\n\n/*\n * Returns true if the given relation is a partition.\n */\nbool\nPartitionTable(Oid relationId)\n{\n\tRelation rel = try_relation_open(relationId, AccessShareLock);\n\n\t/* don't error out for tables that are dropped */\n\tif (rel == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tbool partitionTable = rel->rd_rel->relispartition;\n\n\t/* keep the lock */\n\ttable_close(rel, NoLock);\n\n\treturn partitionTable;\n}\n\n\n/*\n * Returns true if the given relation is a partition.  The function\n * doesn't acquire any locks on the input relation, thus the caller is\n * reponsible for holding the appropriate locks.\n */\nbool\nPartitionTableNoLock(Oid relationId)\n{\n\tRelation rel = try_relation_open_nolock(relationId);\n\n\t/* don't error out for tables that are dropped */\n\tif (rel == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tbool partitionTable = rel->rd_rel->relispartition;\n\n\t/* keep the lock */\n\ttable_close(rel, NoLock);\n\n\treturn partitionTable;\n}\n\n\n/*\n * try_relation_open_nolock opens a relation with given relationId without\n * acquiring locks. PostgreSQL's try_relation_open() asserts that caller\n * has already acquired a lock on the relation, which we don't always do.\n *\n * ATTENTION:\n *   1. Sync this with try_relation_open(). It hasn't changed for 10 to 12\n *      releases though.\n *   2. We should remove this after we fix the locking/distributed deadlock\n *      issues with MX Truncate. See https://github.com/citusdata/citus/pull/2894\n *      for more discussion.\n */\nstatic Relation\ntry_relation_open_nolock(Oid relationId)\n{\n\tif (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relationId)))\n\t{\n\t\treturn NULL;\n\t}\n\n\tRelation relation = RelationIdGetRelation(relationId);\n\tif (!RelationIsValid(relation))\n\t{\n\t\treturn NULL;\n\t}\n\n\tpgstat_init_relation(relation);\n\n\treturn relation;\n}\n\n\n/*\n * IsChildTable returns true if the table is inherited. Note that\n * partition tables inherites by default. However, this function\n * returns false if the given table is a partition.\n */\nbool\nIsChildTable(Oid relationId)\n{\n\tScanKeyData key[1];\n\tHeapTuple inheritsTuple = NULL;\n\tbool tableInherits = false;\n\n\tRelation pgInherits = table_open(InheritsRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0], Anum_pg_inherits_inhrelid,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(relationId));\n\n\tSysScanDesc scan = systable_beginscan(pgInherits, InvalidOid, false,\n\t\t\t\t\t\t\t\t\t\t  NULL, 1, key);\n\n\twhile ((inheritsTuple = systable_getnext(scan)) != NULL)\n\t{\n\t\tOid inheritedRelationId =\n\t\t\t((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid;\n\n\t\tif (relationId == inheritedRelationId)\n\t\t{\n\t\t\ttableInherits = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tsystable_endscan(scan);\n\ttable_close(pgInherits, AccessShareLock);\n\n\tif (tableInherits && PartitionTable(relationId))\n\t{\n\t\ttableInherits = false;\n\t}\n\n\treturn tableInherits;\n}\n\n\n/*\n * IsParentTable returns true if the table is inherited. Note that\n * partitioned tables inherited by default. However, this function\n * returns false if the given table is a partitioned table.\n */\nbool\nIsParentTable(Oid relationId)\n{\n\tScanKeyData key[1];\n\tbool tableInherited = false;\n\n\tRelation pgInherits = table_open(InheritsRelationId, AccessShareLock);\n\n\tScanKeyInit(&key[0], Anum_pg_inherits_inhparent,\n\t\t\t\tBTEqualStrategyNumber, F_OIDEQ,\n\t\t\t\tObjectIdGetDatum(relationId));\n\n\tSysScanDesc scan = systable_beginscan(pgInherits, InheritsParentIndexId, true,\n\t\t\t\t\t\t\t\t\t\t  NULL, 1, key);\n\n\tif (systable_getnext(scan) != NULL)\n\t{\n\t\ttableInherited = true;\n\t}\n\tsystable_endscan(scan);\n\ttable_close(pgInherits, AccessShareLock);\n\n\tRelation relation = try_relation_open(relationId, AccessShareLock);\n\tif (relation == NULL)\n\t{\n\t\tereport(ERROR, (errmsg(\"relation with OID %u does not exist\", relationId)));\n\t}\n\n\tif (tableInherited && PartitionedTableNoLock(relationId))\n\t{\n\t\ttableInherited = false;\n\t}\n\n\trelation_close(relation, AccessShareLock);\n\n\treturn tableInherited;\n}\n\n\n/*\n * Wrapper around get_partition_parent\n *\n * Note: Because this function assumes that the relation whose OID is passed\n * as an argument will have precisely one parent, it should only be called\n * when it is known that the relation is a partition.\n */\nOid\nPartitionParentOid(Oid partitionOid)\n{\n\tOid partitionParentOid = get_partition_parent(partitionOid, false);\n\n\treturn partitionParentOid;\n}\n\n\n/*\n * PartitionWithLongestNameRelationId is a utility function that returns the\n * oid of the partition table that has the longest name in terms of number of\n * characters.\n */\nOid\nPartitionWithLongestNameRelationId(Oid parentRelationId)\n{\n\tOid longestNamePartitionId = InvalidOid;\n\tint longestNameLength = 0;\n\tList *partitionList = PartitionList(parentRelationId);\n\n\tOid partitionRelationId = InvalidOid;\n\tforeach_declared_oid(partitionRelationId, partitionList)\n\t{\n\t\tchar *partitionName = get_rel_name(partitionRelationId);\n\t\tint partitionNameLength = strnlen(partitionName, NAMEDATALEN);\n\t\tif (partitionNameLength > longestNameLength)\n\t\t{\n\t\t\tlongestNamePartitionId = partitionRelationId;\n\t\t\tlongestNameLength = partitionNameLength;\n\t\t}\n\t}\n\n\treturn longestNamePartitionId;\n}\n\n\n/*\n * Takes a parent relation and returns Oid list of its partitions. The\n * function errors out if the given relation is not a parent.\n */\nList *\nPartitionList(Oid parentRelationId)\n{\n\tRelation rel = table_open(parentRelationId, AccessShareLock);\n\tList *partitionList = NIL;\n\n\n\tif (!PartitionedTable(parentRelationId))\n\t{\n\t\tchar *relationName = get_rel_name(parentRelationId);\n\n\t\tereport(ERROR, (errmsg(\"\\\"%s\\\" is not a parent table\", relationName)));\n\t}\n\tPartitionDesc partDesc = RelationGetPartitionDesc(rel, true);\n\tAssert(partDesc != NULL);\n\n\tint partitionCount = partDesc->nparts;\n\tfor (int partitionIndex = 0; partitionIndex < partitionCount; ++partitionIndex)\n\t{\n\t\tpartitionList =\n\t\t\tlappend_oid(partitionList, partDesc->oids[partitionIndex]);\n\t}\n\n\t/* keep the lock */\n\ttable_close(rel, NoLock);\n\n\treturn partitionList;\n}\n\n\n/*\n * GenerateDetachPartitionCommand gets a partition table and returns\n * \"ALTER TABLE parent_table DETACH PARTITION partitionName\" command.\n */\nchar *\nGenerateDetachPartitionCommand(Oid partitionTableId)\n{\n\tStringInfo detachPartitionCommand = makeStringInfo();\n\n\tif (!PartitionTable(partitionTableId))\n\t{\n\t\tchar *relationName = get_rel_name(partitionTableId);\n\n\t\tereport(ERROR, (errmsg(\"\\\"%s\\\" is not a partition\", relationName)));\n\t}\n\n\tOid parentId = get_partition_parent(partitionTableId, false);\n\tchar *tableQualifiedName = generate_qualified_relation_name(partitionTableId);\n\tchar *parentTableQualifiedName = generate_qualified_relation_name(parentId);\n\n\tappendStringInfo(detachPartitionCommand,\n\t\t\t\t\t \"ALTER TABLE IF EXISTS %s DETACH PARTITION %s;\",\n\t\t\t\t\t parentTableQualifiedName, tableQualifiedName);\n\n\treturn detachPartitionCommand->data;\n}\n\n\n/*\n * GenerateDetachPartitionCommandRelationIdList returns the necessary command list to\n * detach the given partitions from their parents.\n */\nList *\nGenerateDetachPartitionCommandRelationIdList(List *relationIds)\n{\n\tList *detachPartitionCommands = NIL;\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIds)\n\t{\n\t\tAssert(PartitionTable(relationId));\n\t\tchar *detachCommand = GenerateDetachPartitionCommand(relationId);\n\t\tdetachPartitionCommands = lappend(detachPartitionCommands, detachCommand);\n\t}\n\n\treturn detachPartitionCommands;\n}\n\n\n/*\n * GenereatePartitioningInformation returns the partitioning type and partition column\n * for the given parent table in the form of \"PARTITION TYPE (partitioning column(s)/expression(s))\".\n */\nchar *\nGeneratePartitioningInformation(Oid parentTableId)\n{\n\tchar *partitionBoundCString = \"\";\n\n\tif (!PartitionedTable(parentTableId))\n\t{\n\t\tchar *relationName = get_rel_name(parentTableId);\n\n\t\tereport(ERROR, (errmsg(\"\\\"%s\\\" is not a parent table\", relationName)));\n\t}\n\n\tDatum partitionBoundDatum = DirectFunctionCall1(pg_get_partkeydef,\n\t\t\t\t\t\t\t\t\t\t\t\t\tObjectIdGetDatum(parentTableId));\n\n\tpartitionBoundCString = TextDatumGetCString(partitionBoundDatum);\n\n\treturn partitionBoundCString;\n}\n\n\n/*\n * GenerateAttachShardPartitionCommand generates command to attach a child table\n * table to its parent in a partitioning hierarchy.\n */\nchar *\nGenerateAttachShardPartitionCommand(ShardInterval *shardInterval)\n{\n\tOid schemaId = get_rel_namespace(shardInterval->relationId);\n\tchar *schemaName = get_namespace_name(schemaId);\n\tchar *escapedSchemaName = quote_literal_cstr(schemaName);\n\n\tchar *command = GenerateAlterTableAttachPartitionCommand(shardInterval->relationId);\n\tchar *escapedCommand = quote_literal_cstr(command);\n\tint shardIndex = ShardIndex(shardInterval);\n\n\n\tStringInfo attachPartitionCommand = makeStringInfo();\n\n\tOid parentRelationId = PartitionParentOid(shardInterval->relationId);\n\tif (parentRelationId == InvalidOid)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),\n\t\t\t\t\t\terrmsg(\"cannot attach partition\"),\n\t\t\t\t\t\terrdetail(\"Referenced relation cannot be found.\")));\n\t}\n\n\tOid parentSchemaId = get_rel_namespace(parentRelationId);\n\tchar *parentSchemaName = get_namespace_name(parentSchemaId);\n\tchar *escapedParentSchemaName = quote_literal_cstr(parentSchemaName);\n\tuint64 parentShardId = ColocatedShardIdInRelation(parentRelationId, shardIndex);\n\n\tappendStringInfo(attachPartitionCommand,\n\t\t\t\t\t WORKER_APPLY_INTER_SHARD_DDL_COMMAND, parentShardId,\n\t\t\t\t\t escapedParentSchemaName, shardInterval->shardId,\n\t\t\t\t\t escapedSchemaName, escapedCommand);\n\n\treturn attachPartitionCommand->data;\n}\n\n\n/*\n * GenerateAlterTableAttachPartitionCommand returns the necessary command to\n * attach the given partition to its parent.\n */\nchar *\nGenerateAlterTableAttachPartitionCommand(Oid partitionTableId)\n{\n\tStringInfo createPartitionCommand = makeStringInfo();\n\n\n\tif (!PartitionTable(partitionTableId))\n\t{\n\t\tchar *relationName = get_rel_name(partitionTableId);\n\n\t\tereport(ERROR, (errmsg(\"\\\"%s\\\" is not a partition\", relationName)));\n\t}\n\n\tOid parentId = get_partition_parent(partitionTableId, false);\n\tchar *tableQualifiedName = generate_qualified_relation_name(partitionTableId);\n\tchar *parentTableQualifiedName = generate_qualified_relation_name(parentId);\n\n\tchar *partitionBoundCString = PartitionBound(partitionTableId);\n\n\tappendStringInfo(createPartitionCommand, \"ALTER TABLE %s ATTACH PARTITION %s %s;\",\n\t\t\t\t\t parentTableQualifiedName, tableQualifiedName,\n\t\t\t\t\t partitionBoundCString);\n\n\treturn createPartitionCommand->data;\n}\n\n\n/*\n * GenerateAttachPartitionCommandRelationIdList returns the necessary command list to\n * attach the given partitions to their parents.\n */\nList *\nGenerateAttachPartitionCommandRelationIdList(List *relationIds)\n{\n\tList *attachPartitionCommands = NIL;\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationIds)\n\t{\n\t\tchar *attachCommand = GenerateAlterTableAttachPartitionCommand(relationId);\n\t\tattachPartitionCommands = lappend(attachPartitionCommands, attachCommand);\n\t}\n\n\treturn attachPartitionCommands;\n}\n\n\n/*\n * This function heaviliy inspired from RelationBuildPartitionDesc()\n * which is avaliable in src/backend/catalog/partition.c.\n *\n * The function simply reads the pg_class and gets the partition bound.\n * Later, converts it to text format and returns.\n */\nstatic char *\nPartitionBound(Oid partitionId)\n{\n\tbool isnull = false;\n\n\tHeapTuple tuple = SearchSysCache1(RELOID, partitionId);\n\tif (!HeapTupleIsValid(tuple))\n\t{\n\t\telog(ERROR, \"cache lookup failed for relation %u\", partitionId);\n\t}\n\n\t/*\n\t * It is possible that the pg_class tuple of a partition has not been\n\t * updated yet to set its relpartbound field.  The only case where\n\t * this happens is when we open the parent relation to check using its\n\t * partition descriptor that a new partition's bound does not overlap\n\t * some existing partition.\n\t */\n\tif (!((Form_pg_class) GETSTRUCT(tuple))->relispartition)\n\t{\n\t\tReleaseSysCache(tuple);\n\t\treturn \"\";\n\t}\n\n\tDatum datum = SysCacheGetAttr(RELOID, tuple,\n\t\t\t\t\t\t\t\t  Anum_pg_class_relpartbound,\n\t\t\t\t\t\t\t\t  &isnull);\n\tAssert(!isnull);\n\n\tDatum partitionBoundDatum =\n\t\tDirectFunctionCall2(pg_get_expr, datum, ObjectIdGetDatum(partitionId));\n\n\tchar *partitionBoundString = TextDatumGetCString(partitionBoundDatum);\n\n\tReleaseSysCache(tuple);\n\n\treturn partitionBoundString;\n}\n\n\n/*\n * ListShardsUnderParentRelation returns a list of ShardInterval for every\n * shard under a given relation, meaning it includes the shards of child\n * tables in a partitioning hierarchy.\n */\nList *\nListShardsUnderParentRelation(Oid relationId)\n{\n\tList *shardList = LoadShardIntervalList(relationId);\n\n\tif (PartitionedTable(relationId))\n\t{\n\t\tList *partitionList = PartitionList(relationId);\n\t\tOid partitionRelationId = InvalidOid;\n\n\t\tforeach_declared_oid(partitionRelationId, partitionList)\n\t\t{\n\t\t\tList *childShardList = ListShardsUnderParentRelation(partitionRelationId);\n\t\t\tshardList = list_concat(shardList, childShardList);\n\t\t}\n\t}\n\n\treturn shardList;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/namespace_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * namespace_utils.c\n *\n * Utility functions related to namespace changes.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"utils/guc.h\"\n#include \"utils/regproc.h\"\n\n#include \"distributed/namespace_utils.h\"\n\n/*\n * We use the equivalent of a function SET option to allow the setting to\n * persist for the exact duration of the transaction, guc.c takes care of\n * undoing the setting on error.\n *\n * We set search_path to \"pg_catalog\" instead of \"\" to expose useful utilities.\n */\nint\nPushEmptySearchPath()\n{\n\tint saveNestLevel = NewGUCNestLevel();\n\t(void) set_config_option(\"search_path\", \"pg_catalog\",\n\t\t\t\t\t\t\t PGC_USERSET, PGC_S_SESSION,\n\t\t\t\t\t\t\t GUC_ACTION_SAVE, true, 0, false);\n\treturn saveNestLevel;\n}\n\n\n/*\n * Restore the GUC variable search_path we set in PushEmptySearchPath\n */\nvoid\nPopEmptySearchPath(int saveNestLevel)\n{\n\tAtEOXact_GUC(true, saveNestLevel);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/param_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * param_utils.c\n *\t  Utilities to process paramaters.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"nodes/bitmapset.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/params.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/primnodes.h\"\n\n#include \"distributed/param_utils.h\"\n\n/*\n * IsExternParamUsedInQuery returns true if the passed in paramId\n * is used in the query, false otherwise.\n */\nbool\nGetParamsUsedInQuery(Node *expression, Bitmapset **paramBitmap)\n{\n\tif (expression == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(expression, Param))\n\t{\n\t\tParam *param = (Param *) expression;\n\t\tint paramId = param->paramid;\n\n\t\t/* only care about user supplied parameters */\n\t\tif (param->paramkind != PARAM_EXTERN)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t/* Found a parameter, mark it in the bitmap and continue */\n\t\t*paramBitmap = bms_add_member(*paramBitmap, paramId);\n\n\t\t/* Continue searching */\n\t\treturn false;\n\t}\n\n\t/* keep traversing */\n\tif (IsA(expression, Query))\n\t{\n\t\treturn query_tree_walker((Query *) expression,\n\t\t\t\t\t\t\t\t GetParamsUsedInQuery,\n\t\t\t\t\t\t\t\t paramBitmap,\n\t\t\t\t\t\t\t\t 0);\n\t}\n\telse\n\t{\n\t\treturn expression_tree_walker(expression,\n\t\t\t\t\t\t\t\t\t  GetParamsUsedInQuery,\n\t\t\t\t\t\t\t\t\t  paramBitmap);\n\t}\n}\n\n\n/*\n * MarkUnreferencedExternParams marks parameter's type to zero if the\n * parameter is not used in the query.\n */\nvoid\nMarkUnreferencedExternParams(Node *expression, ParamListInfo boundParams)\n{\n\tint parameterCount = boundParams->numParams;\n\tBitmapset *paramBitmap = NULL;\n\n\t/* Fetch all parameters used in the query */\n\tGetParamsUsedInQuery(expression, &paramBitmap);\n\n\t/* Check for any missing parameters */\n\tfor (int parameterNum = 1; parameterNum <= parameterCount; parameterNum++)\n\t{\n\t\tif (!bms_is_member(parameterNum, paramBitmap))\n\t\t{\n\t\t\tboundParams->params[parameterNum - 1].ptype = 0;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/priority.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * priority.c\n *\t  Utilities for managing CPU priority.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include <sys/resource.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"distributed/priority.h\"\n\nint CpuPriority = 0;\nint CpuPriorityLogicalRepSender = CPU_PRIORITY_INHERIT;\nint MaxHighPriorityBackgroundProcesess = 2;\n\n\n/*\n * SetOwnPriority changes the CPU priority of the current backend to the given\n * priority. If the OS disallows us to set the priority to the given value, we\n * only warn about it.\n */\nvoid\nSetOwnPriority(int priority)\n{\n\tif (priority == CPU_PRIORITY_INHERIT)\n\t{\n\t\treturn;\n\t}\n\n\tif (setpriority(PRIO_PROCESS, getpid(), priority) == -1)\n\t{\n\t\tereport(WARNING, (\n\t\t\t\t\terrmsg(\"could not set cpu priority to %d: %m\", priority),\n\t\t\t\t\terrhint(\"Try changing the 'nice' resource limit by changing \"\n\t\t\t\t\t\t\t\"/etc/security/limits.conf for the postgres user \"\n\t\t\t\t\t\t\t\"and/or by setting LimitNICE in your the systemd \"\n\t\t\t\t\t\t\t\"service file (depending on how you start \"\n\t\t\t\t\t\t\t\"postgres).\"\n\t\t\t\t\t\t\t)));\n\t}\n}\n\n\n/*\n * GetOwnPriority returns the current CPU priority value of the backend.\n */\nint\nGetOwnPriority(void)\n{\n\terrno = 0;\n\tint result = getpriority(PRIO_PROCESS, getpid());\n\n\t/*\n\t * We explicitly check errno too because getpriority can return -1 on\n\t * success too, if the actual priority value is -1\n\t */\n\tif (result == -1 && errno != 0)\n\t{\n\t\tereport(WARNING, (errmsg(\"could not get current cpu priority value, \"\n\t\t\t\t\t\t\t\t \"assuming 0: %m\")));\n\t\treturn 0;\n\t}\n\treturn result;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/query_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * query_utils.c\n *\n * Query-walker utility functions to be used to construct a logical plan\n * tree from the given query tree structure.\n *\n * Copyright (c), Citus Data, Inc.\n *\n **----------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"catalog/pg_class.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"nodes/primnodes.h\"\n\n#include \"distributed/listutils.h\"\n#include \"distributed/query_utils.h\"\n#include \"distributed/version_compat.h\"\n\n\nstatic bool CitusQueryableRangeTableRelation(RangeTblEntry *rangeTableEntry);\n\n/*\n * ExtractRangeTableList walks over a tree to gather entries.\n * Execution is parameterized by passing walkerMode flag via ExtractRangeTableWalkerContext\n * as we cannot pass more than one parameter to query_tree_walker\n */\nbool\nExtractRangeTableList(Node *node, ExtractRangeTableWalkerContext *context)\n{\n\t/* get parameters from context */\n\tList **rangeTableRelationList = context->rangeTableList;\n\tExtractRangeTableMode walkerMode = context->walkerMode;\n\n\tbool walkIsComplete = false;\n\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, RangeTblEntry))\n\t{\n\t\tRangeTblEntry *rangeTable = (RangeTblEntry *) node;\n\n\t\tif (walkerMode == EXTRACT_ALL_ENTRIES ||\n\t\t\t(walkerMode == EXTRACT_RELATION_ENTRIES &&\n\t\t\t CitusQueryableRangeTableRelation(rangeTable)))\n\t\t{\n\t\t\t(*rangeTableRelationList) = lappend(*rangeTableRelationList, rangeTable);\n\t\t}\n\t}\n\telse if (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\n\t\tif (query->hasSubLinks || query->cteList || query->setOperations)\n\t\t{\n\t\t\t/* descend into all parts of the query */\n\t\t\twalkIsComplete = query_tree_walker(query,\n\t\t\t\t\t\t\t\t\t\t\t   ExtractRangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t   context,\n\t\t\t\t\t\t\t\t\t\t\t   QTW_EXAMINE_RTES_BEFORE);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* descend only into RTEs */\n\t\t\twalkIsComplete = range_table_walker(query->rtable,\n\t\t\t\t\t\t\t\t\t\t\t\tExtractRangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\t\t\t\t\t\tQTW_EXAMINE_RTES_BEFORE);\n\t\t}\n\t}\n\telse\n\t{\n\t\twalkIsComplete = expression_tree_walker(node, ExtractRangeTableList,\n\t\t\t\t\t\t\t\t\t\t\t\tcontext);\n\t}\n\n\treturn walkIsComplete;\n}\n\n\n/*\n * CitusQueryableRangeTableRelation returns true if the input range table\n * entry is a relation and it can be used in a distributed query, including\n * local tables and materialized views as well.\n */\nstatic bool\nCitusQueryableRangeTableRelation(RangeTblEntry *rangeTableEntry)\n{\n\tchar relationKind = '\\0';\n\n\tif (rangeTableEntry->rtekind != RTE_RELATION)\n\t{\n\t\t/* we're only interested in relations */\n\t\treturn false;\n\t}\n\n\trelationKind = rangeTableEntry->relkind;\n\tif (relationKind == RELKIND_RELATION || relationKind == RELKIND_PARTITIONED_TABLE ||\n\t\trelationKind == RELKIND_FOREIGN_TABLE || relationKind == RELKIND_MATVIEW)\n\t{\n\t\t/*\n\t\t * RELKIND_VIEW are automatically replaced with a subquery in\n\t\t * the query tree, so we ignore them here.\n\t\t *\n\t\t * RELKIND_MATVIEW is equivalent of a local table in postgres.\n\t\t */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * ExtractRangeTableRelationWalker gathers all range table relation entries\n * in a query. The caller is responsible for checking whether the returned\n * entries are distributed or not.\n */\nbool\nExtractRangeTableRelationWalker(Node *node, List **rangeTableRelationList)\n{\n\tExtractRangeTableWalkerContext context;\n\n\tcontext.rangeTableList = rangeTableRelationList;\n\tcontext.walkerMode = EXTRACT_RELATION_ENTRIES;\n\n\treturn ExtractRangeTableList(node, &context);\n}\n\n\n/*\n * ExtractRangeTableEntryWalker walks over a query tree, and finds all range\n * table entries. For recursing into the query tree, this function uses the\n * query tree walker since the expression tree walker doesn't recurse into\n * sub-queries.\n */\nbool\nExtractRangeTableEntryWalker(Node *node, List **rangeTableList)\n{\n\tExtractRangeTableWalkerContext context;\n\n\tcontext.rangeTableList = rangeTableList;\n\tcontext.walkerMode = EXTRACT_ALL_ENTRIES;\n\n\treturn ExtractRangeTableList(node, &context);\n}\n\n\n/*\n * ExtractRangeTableIndexWalker walks over a join tree, and finds all range\n * table indexes in that tree.\n */\nbool\nExtractRangeTableIndexWalker(Node *node, List **rangeTableIndexList)\n{\n\tbool walkerResult = false;\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, RangeTblRef))\n\t{\n\t\tint rangeTableIndex = ((RangeTblRef *) node)->rtindex;\n\t\t(*rangeTableIndexList) = lappend_int(*rangeTableIndexList, rangeTableIndex);\n\t}\n\telse\n\t{\n\t\twalkerResult = expression_tree_walker(node, ExtractRangeTableIndexWalker,\n\t\t\t\t\t\t\t\t\t\t\t  rangeTableIndexList);\n\t}\n\n\treturn walkerResult;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/reference_table_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * reference_table_utils.c\n *\n * Declarations for public utility functions related to reference tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/htup_details.h\"\n#include \"postmaster/postmaster.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relation_access_tracking.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shard_transfer.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/transaction_management.h\"\n#include \"distributed/worker_manager.h\"\n#include \"distributed/worker_transaction.h\"\n\n\n/* local function forward declarations */\nstatic List * WorkersWithoutReferenceTablePlacement(uint64 shardId, LOCKMODE lockMode);\nstatic StringInfo CopyShardPlacementToWorkerNodeQuery(ShardPlacement *sourceShardPlacement\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  WorkerNode *workerNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  char transferMode);\nstatic bool AnyRelationsModifiedInTransaction(List *relationIdList);\nstatic List * ReplicatedMetadataSyncedDistributedTableList(void);\nstatic bool NodeHasAllReferenceTableReplicas(WorkerNode *workerNode);\ntypedef struct ShardTaskEntry\n{\n\tuint64 shardId;\n\tint64 taskId;\n} ShardTaskEntry;\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(upgrade_to_reference_table);\nPG_FUNCTION_INFO_V1(replicate_reference_tables);\n\n/*\n * replicate_reference_tables is a UDF to ensure that allreference tables are\n * replicated to all nodes.\n */\nDatum\nreplicate_reference_tables(PG_FUNCTION_ARGS)\n{\n\tOid shardReplicationModeOid = PG_GETARG_OID(0);\n\tchar shardReplicationMode = LookupShardTransferMode(shardReplicationModeOid);\n\n\t/* to prevent concurrent node additions while copying reference tables */\n\tLockRelationOid(DistNodeRelationId(), ShareLock);\n\tEnsureReferenceTablesExistOnAllNodesExtended(shardReplicationMode);\n\n\t/*\n\t * Given the copying of reference tables and updating metadata have been done via a\n\t * loopback connection we do not have to retain the lock on pg_dist_node anymore.\n\t */\n\tUnlockRelationOid(DistNodeRelationId(), ShareLock);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * EnsureReferenceTablesExistOnAllNodes ensures that a shard placement for every\n * reference table exists on all nodes. If a node does not have a set of shard\n * placements, then citus_copy_shard_placement is called in a subtransaction\n * to pull the data to the new node.\n */\nvoid\nEnsureReferenceTablesExistOnAllNodes(void)\n{\n\tEnsureReferenceTablesExistOnAllNodesExtended(TRANSFER_MODE_BLOCK_WRITES);\n}\n\n\n/*\n * EnsureReferenceTablesExistOnAllNodesExtended ensures that a shard placement for every\n * reference table exists on all nodes. If a node does not have a set of shard placements,\n * then citus_copy_shard_placement is called in a subtransaction to pull the data to the\n * new node.\n *\n * The transferMode is passed on to the implementation of the copy to control the locks\n * and transferMode.\n */\nvoid\nEnsureReferenceTablesExistOnAllNodesExtended(char transferMode)\n{\n\tList *referenceTableIdList = NIL;\n\tuint64 shardId = INVALID_SHARD_ID;\n\tList *newWorkersList = NIL;\n\tconst char *referenceTableName = NULL;\n\tint colocationId = GetReferenceTableColocationId();\n\n\tif (colocationId == INVALID_COLOCATION_ID)\n\t{\n\t\t/* we have no reference table yet. */\n\t\treturn;\n\t}\n\n\t/*\n\t * Most of the time this function should result in a conclusion where we do not need\n\t * to copy any reference tables. To prevent excessive locking the majority of the time\n\t * we run our precondition checks first with a lower lock. If, after checking with the\n\t * lower lock, that we might need to copy reference tables we check with a more\n\t * aggressive and self conflicting lock. It is important to be self conflicting in the\n\t * second run to make sure that two concurrent calls to this routine will actually not\n\t * run concurrently after the initial check.\n\t *\n\t * If after two iterations of precondition checks we still find the need for copying\n\t * reference tables we exit the loop with all locks held. This will prevent concurrent\n\t * DROP TABLE and create_reference_table calls so that the list of reference tables we\n\t * operate on are stable.\n\t *\n\t * Since the changes to the reference table placements are made via loopback\n\t * connections we release the locks held at the end of this function. Due to Citus\n\t * only running transactions in READ COMMITTED mode we can be sure that other\n\t * transactions correctly find the metadata entries.\n\t */\n\tLOCKMODE lockmodes[] = { AccessShareLock, ExclusiveLock };\n\tfor (int lockmodeIndex = 0; lockmodeIndex < lengthof(lockmodes); lockmodeIndex++)\n\t{\n\t\tLockColocationId(colocationId, lockmodes[lockmodeIndex]);\n\n\t\treferenceTableIdList = CitusTableTypeIdList(REFERENCE_TABLE);\n\t\tif (referenceTableIdList == NIL)\n\t\t{\n\t\t\t/*\n\t\t\t * No reference tables exist, make sure that any locks obtained earlier are\n\t\t\t * released. It will probably not matter, but we release the locks in the\n\t\t\t * reverse order we obtained them in.\n\t\t\t */\n\t\t\tfor (int releaseLockmodeIndex = lockmodeIndex; releaseLockmodeIndex >= 0;\n\t\t\t\t releaseLockmodeIndex--)\n\t\t\t{\n\t\t\t\tUnlockColocationId(colocationId, lockmodes[releaseLockmodeIndex]);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tOid referenceTableId = linitial_oid(referenceTableIdList);\n\t\treferenceTableName = get_rel_name(referenceTableId);\n\t\tList *shardIntervalList = LoadShardIntervalList(referenceTableId);\n\t\tif (list_length(shardIntervalList) == 0)\n\t\t{\n\t\t\t/* check for corrupt metadata */\n\t\t\tereport(ERROR, (errmsg(\"reference table \\\"%s\\\" does not have a shard\",\n\t\t\t\t\t\t\t\t   referenceTableName)));\n\t\t}\n\n\t\tShardInterval *shardInterval = (ShardInterval *) linitial(shardIntervalList);\n\t\tshardId = shardInterval->shardId;\n\n\t\t/*\n\t\t * We only take an access share lock, otherwise we'll hold up citus_add_node.\n\t\t * In case of create_reference_table() where we don't want concurrent writes\n\t\t * to pg_dist_node, we have already acquired ShareLock on pg_dist_node.\n\t\t */\n\t\tnewWorkersList = WorkersWithoutReferenceTablePlacement(shardId, AccessShareLock);\n\t\tif (list_length(newWorkersList) == 0)\n\t\t{\n\t\t\t/*\n\t\t\t * All workers already have a copy of the reference tables, make sure that\n\t\t\t * any locks obtained earlier are released. It will probably not matter, but\n\t\t\t * we release the locks in the reverse order we obtained them in.\n\t\t\t */\n\t\t\tfor (int releaseLockmodeIndex = lockmodeIndex; releaseLockmodeIndex >= 0;\n\t\t\t\t releaseLockmodeIndex--)\n\t\t\t{\n\t\t\t\tUnlockColocationId(colocationId, lockmodes[releaseLockmodeIndex]);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/*\n\t * citus_copy_shard_placement triggers metadata sync-up, which tries to\n\t * acquire a ShareLock on pg_dist_node. We do master_copy_shad_placement\n\t * in a separate connection. If we have modified pg_dist_node in the\n\t * current backend, this will cause a deadlock.\n\t */\n\tif (TransactionModifiedNodeMetadata)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot replicate reference tables in a transaction \"\n\t\t\t\t\t\t\t   \"that modified node metadata\")));\n\t}\n\n\t/*\n\t * Modifications to reference tables in current transaction are not visible\n\t * to citus_copy_shard_placement, since it is done in a separate backend.\n\t */\n\tif (AnyRelationsModifiedInTransaction(referenceTableIdList))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot replicate reference tables in a transaction \"\n\t\t\t\t\t\t\t   \"that modified a reference table\")));\n\t}\n\n\tbool missingOk = false;\n\tShardPlacement *sourceShardPlacement = ActiveShardPlacement(shardId, missingOk);\n\tif (sourceShardPlacement == NULL)\n\t{\n\t\t/* check for corrupt metadata */\n\t\tereport(ERROR, (errmsg(\"reference table shard \"\n\t\t\t\t\t\t\t   UINT64_FORMAT\n\t\t\t\t\t\t\t   \" does not have an active shard placement\",\n\t\t\t\t\t\t\t   shardId)));\n\t}\n\n\tWorkerNode *newWorkerNode = NULL;\n\tforeach_declared_ptr(newWorkerNode, newWorkersList)\n\t{\n\t\tereport(DEBUG2, (errmsg(\"replicating reference table '%s' to %s:%d ...\",\n\t\t\t\t\t\t\t\treferenceTableName, newWorkerNode->workerName,\n\t\t\t\t\t\t\t\tnewWorkerNode->workerPort)));\n\n\t\t/*\n\t\t * Call citus_copy_shard_placement using citus extension owner. Current\n\t\t * user might not have permissions to do the copy.\n\t\t */\n\t\tconst char *userName = CitusExtensionOwnerName();\n\t\tint connectionFlags = OUTSIDE_TRANSACTION;\n\n\t\tMultiConnection *connection = GetNodeUserDatabaseConnection(\n\t\t\tconnectionFlags, LocalHostName, PostPortNumber,\n\t\t\tuserName, NULL);\n\n\t\tif (PQstatus(connection->pgConn) == CONNECTION_OK)\n\t\t{\n\t\t\tUseCoordinatedTransaction();\n\n\t\t\tRemoteTransactionBegin(connection);\n\t\t\tStringInfo placementCopyCommand =\n\t\t\t\tCopyShardPlacementToWorkerNodeQuery(sourceShardPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\tnewWorkerNode,\n\t\t\t\t\t\t\t\t\t\t\t\t\ttransferMode);\n\n\t\t\t/*\n\t\t\t * The placement copy command uses distributed execution to copy\n\t\t\t * the shard. This is allowed when indicating that the backend is a\n\t\t\t * rebalancer backend.\n\t\t\t */\n\t\t\tExecuteCriticalRemoteCommand(connection, psprintf(\n\t\t\t\t\t\t\t\t\t\t\t \"SET LOCAL application_name TO '%s%ld'\",\n\t\t\t\t\t\t\t\t\t\t\t CITUS_REBALANCER_APPLICATION_NAME_PREFIX,\n\t\t\t\t\t\t\t\t\t\t\t GetGlobalPID()));\n\t\t\tExecuteCriticalRemoteCommand(connection, placementCopyCommand->data);\n\t\t\tRemoteTransactionCommit(connection);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"could not open a connection to localhost \"\n\t\t\t\t\t\t\t\t   \"when replicating reference tables\"),\n\t\t\t\t\t\t\terrdetail(\n\t\t\t\t\t\t\t\t\"citus.replicate_reference_tables_on_activate = false \"\n\t\t\t\t\t\t\t\t\"requires localhost connectivity.\")));\n\t\t}\n\n\t\tCloseConnection(connection);\n\t}\n\n\t/*\n\t * Since reference tables have been copied via a loopback connection we do not have\n\t * to retain our locks. Since Citus only runs well in READ COMMITTED mode we can be\n\t * sure that other transactions will find the reference tables copied.\n\t * We have obtained and held multiple locks, here we unlock them all in the reverse\n\t * order we have obtained them in.\n\t */\n\tfor (int releaseLockmodeIndex = lengthof(lockmodes) - 1; releaseLockmodeIndex >= 0;\n\t\t releaseLockmodeIndex--)\n\t{\n\t\tUnlockColocationId(colocationId, lockmodes[releaseLockmodeIndex]);\n\t}\n}\n\n\n/*\n * ScheduleTasksToParallelCopyReferenceTablesOnAllMissingNodes is essentially a\n * twin of EnsureReferenceTablesExistOnAllNodesExtended. The difference is instead of\n * copying the missing tables on to the worker nodes this function creates the background\n * tasks for each required copy operation and schedule it in the background job.\n * Another difference is that instead of moving all the colocated shards sequencially\n * this function creates a seperate background task for each shard, even when the shards\n * are part of same colocated shard group.\n *\n * For transfering the shards in parallel the function creates a task for each shard\n * move and than schedules another task that creates the shard relationships (if any)\n * between shards and that task wait for the completion of all shard transfer tasks.\n *\n * The function returns an array of task ids that are created for creating the shard\n * relationships, effectively completion of these tasks signals the completion of\n * of reference table setup on the worker nodes. Any process that needs to wait for\n * the completion of the reference table setup can wait for these tasks to complete.\n *\n * The transferMode is passed to this function gets ignored for now and it only uses\n * block write mode.\n */\nint64 *\nScheduleTasksToParallelCopyReferenceTablesOnAllMissingNodes(int64 jobId, char transferMode\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tint *nDependTasks)\n{\n\tList *referenceTableIdList = NIL;\n\tuint64 shardId = INVALID_SHARD_ID;\n\tList *newWorkersList = NIL;\n\tint64 *dependsTaskArray = NULL;\n\tconst char *referenceTableName = NULL;\n\tint colocationId = GetReferenceTableColocationId();\n\n\t*nDependTasks = 0;\n\tif (colocationId == INVALID_COLOCATION_ID)\n\t{\n\t\t/* we have no reference table yet. */\n\t\treturn 0;\n\t}\n\n\t/*\n\t * Most of the time this function should result in a conclusion where we do not need\n\t * to copy any reference tables. To prevent excessive locking the majority of the time\n\t * we run our precondition checks first with a lower lock. If, after checking with the\n\t * lower lock, that we might need to copy reference tables we check with a more\n\t * aggressive and self conflicting lock. It is important to be self conflicting in the\n\t * second run to make sure that two concurrent calls to this routine will actually not\n\t * run concurrently after the initial check.\n\t *\n\t * If after two iterations of precondition checks we still find the need for copying\n\t * reference tables we exit the loop with all locks held. This will prevent concurrent\n\t * DROP TABLE and create_reference_table calls so that the list of reference tables we\n\t * operate on are stable.\n\t *\n\t *\n\t * Since the changes to the reference table placements are made via loopback\n\t * connections we release the locks held at the end of this function. Due to Citus\n\t * only running transactions in READ COMMITTED mode we can be sure that other\n\t * transactions correctly find the metadata entries.\n\t */\n\tLOCKMODE lockmodes[] = { AccessShareLock, ExclusiveLock };\n\tfor (int lockmodeIndex = 0; lockmodeIndex < lengthof(lockmodes); lockmodeIndex++)\n\t{\n\t\tLockColocationId(colocationId, lockmodes[lockmodeIndex]);\n\n\t\treferenceTableIdList = CitusTableTypeIdList(REFERENCE_TABLE);\n\t\tif (referenceTableIdList == NIL)\n\t\t{\n\t\t\t/*\n\t\t\t * No reference tables exist, make sure that any locks obtained earlier are\n\t\t\t * released. It will probably not matter, but we release the locks in the\n\t\t\t * reverse order we obtained them in.\n\t\t\t */\n\t\t\tfor (int releaseLockmodeIndex = lockmodeIndex; releaseLockmodeIndex >= 0;\n\t\t\t\t releaseLockmodeIndex--)\n\t\t\t{\n\t\t\t\tUnlockColocationId(colocationId, lockmodes[releaseLockmodeIndex]);\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tOid referenceTableId = linitial_oid(referenceTableIdList);\n\t\treferenceTableName = get_rel_name(referenceTableId);\n\t\tList *shardIntervalList = LoadShardIntervalList(referenceTableId);\n\t\tif (list_length(shardIntervalList) == 0)\n\t\t{\n\t\t\t/* check for corrupt metadata */\n\t\t\tereport(ERROR, (errmsg(\"reference table \\\"%s\\\" does not have a shard\",\n\t\t\t\t\t\t\t\t   referenceTableName)));\n\t\t}\n\n\t\tShardInterval *shardInterval = (ShardInterval *) linitial(shardIntervalList);\n\t\tshardId = shardInterval->shardId;\n\n\t\t/*\n\t\t * We only take an access share lock, otherwise we'll hold up citus_add_node.\n\t\t * In case of create_reference_table() where we don't want concurrent writes\n\t\t * to pg_dist_node, we have already acquired ShareLock on pg_dist_node.\n\t\t */\n\t\tnewWorkersList = WorkersWithoutReferenceTablePlacement(shardId, AccessShareLock);\n\t\tif (list_length(newWorkersList) == 0)\n\t\t{\n\t\t\t/*\n\t\t\t * All workers already have a copy of the reference tables, make sure that\n\t\t\t * any locks obtained earlier are released. It will probably not matter, but\n\t\t\t * we release the locks in the reverse order we obtained them in.\n\t\t\t */\n\t\t\tfor (int releaseLockmodeIndex = lockmodeIndex; releaseLockmodeIndex >= 0;\n\t\t\t\t releaseLockmodeIndex--)\n\t\t\t{\n\t\t\t\tUnlockColocationId(colocationId, lockmodes[releaseLockmodeIndex]);\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/*\n\t * citus_copy_shard_placement triggers metadata sync-up, which tries to\n\t * acquire a ShareLock on pg_dist_node. We do master_copy_shad_placement\n\t * in a separate connection. If we have modified pg_dist_node in the\n\t * current backend, this will cause a deadlock.\n\t */\n\tif (TransactionModifiedNodeMetadata)\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot replicate reference tables in a transaction \"\n\t\t\t\t\t\t\t   \"that modified node metadata\")));\n\t}\n\n\t/*\n\t * Modifications to reference tables in current transaction are not visible\n\t * to citus_copy_shard_placement, since it is done in a separate backend.\n\t */\n\tif (AnyRelationsModifiedInTransaction(referenceTableIdList))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"cannot replicate reference tables in a transaction \"\n\t\t\t\t\t\t\t   \"that modified a reference table\")));\n\t}\n\n\tbool missingOk = false;\n\tShardPlacement *sourceShardPlacement = ActiveShardPlacement(shardId, missingOk);\n\tif (sourceShardPlacement == NULL)\n\t{\n\t\t/* check for corrupt metadata */\n\t\tereport(ERROR, (errmsg(\"reference table shard \"\n\t\t\t\t\t\t\t   UINT64_FORMAT\n\t\t\t\t\t\t\t   \" does not have an active shard placement\",\n\t\t\t\t\t\t\t   shardId)));\n\t}\n\n\tWorkerNode *newWorkerNode = NULL;\n\tBackgroundTask *task = NULL;\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\tList *depTasksList = NIL;\n\tconst char *transferModeString =\n\t\ttransferMode == TRANSFER_MODE_BLOCK_WRITES ? \"block_writes\" :\n\t\ttransferMode == TRANSFER_MODE_FORCE_LOGICAL ? \"force_logical\" :\n\t\t\"auto\";\n\n\n\tforeach_declared_ptr(newWorkerNode, newWorkersList)\n\t{\n\t\tereport(DEBUG2, (errmsg(\"replicating reference table '%s' to %s:%d ...\",\n\t\t\t\t\t\t\t\treferenceTableName, newWorkerNode->workerName,\n\t\t\t\t\t\t\t\tnewWorkerNode->workerPort)));\n\n\t\tOid relationId = InvalidOid;\n\t\tList *nodeTasksList = NIL;\n\t\tHTAB *shardTaskMap = CreateSimpleHashWithNameAndSize(uint64,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ShardTaskEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Shard_Task_Map\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t list_length(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t referenceTableIdList));\n\n\t\tforeach_declared_oid(relationId, referenceTableIdList)\n\t\t{\n\t\t\treferenceTableName = get_rel_name(relationId);\n\t\t\tList *shardIntervalList = LoadShardIntervalList(relationId);\n\t\t\tif (list_length(shardIntervalList) != 1)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"reference table \\\"%s\\\" does not have a shard\",\n\t\t\t\t\t\t\t\t\t   referenceTableName)));\n\t\t\t}\n\t\t\tShardInterval *shardInterval = (ShardInterval *) linitial(shardIntervalList);\n\t\t\tshardId = shardInterval->shardId;\n\n\t\t\tresetStringInfo(&buf);\n\t\t\tuint32 shardTransferFlags = SHARD_TRANSFER_SINGLE_SHARD_ONLY |\n\t\t\t\t\t\t\t\t\t\tSHARD_TRANSFER_SKIP_CREATE_RELATIONSHIPS;\n\n\t\t\t/* Temporary hack until we get background task config support PR */\n\t\t\tappendStringInfo(&buf,\n\t\t\t\t\t\t\t \"SET LOCAL application_name TO '%s%ld';\",\n\t\t\t\t\t\t\t CITUS_REBALANCER_APPLICATION_NAME_PREFIX,\n\t\t\t\t\t\t\t GetGlobalPID());\n\n\t\t\t/*\n\t\t\t * In first step just create and load data in the shards but defer the\n\t\t\t * creation of the shard relationships to the next step.\n\t\t\t * The reason we want to defer the creation of the shard relationships is that\n\t\t\t * we want to make sure that all the parallel shard copy task are finished\n\t\t\t * before we create the relationships. Otherwise we might end up with\n\t\t\t * a situation where the dependent-shard task is still running and trying to\n\t\t\t * create the shard relationships will result in ERROR.\n\t\t\t */\n\t\t\tappendStringInfo(&buf,\n\t\t\t\t\t\t\t \"SELECT \"\n\t\t\t\t\t\t\t \"citus_internal.citus_internal_copy_single_shard_placement\"\n\t\t\t\t\t\t\t \"(%ld,%u,%u,%u,%s)\",\n\t\t\t\t\t\t\t shardId,\n\t\t\t\t\t\t\t sourceShardPlacement->nodeId,\n\t\t\t\t\t\t\t newWorkerNode->nodeId,\n\t\t\t\t\t\t\t shardTransferFlags,\n\t\t\t\t\t\t\t quote_literal_cstr(transferModeString));\n\n\t\t\tereport(DEBUG2,\n\t\t\t\t\t(errmsg(\"replicating reference table '%s' to %s:%d ... QUERY= %s\",\n\t\t\t\t\t\t\treferenceTableName, newWorkerNode->workerName,\n\t\t\t\t\t\t\tnewWorkerNode->workerPort, buf.data)));\n\n\t\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\t\t\tList *relatedRelations = list_concat(cacheEntry->\n\t\t\t\t\t\t\t\t\t\t\t\t referencedRelationsViaForeignKey,\n\t\t\t\t\t\t\t\t\t\t\t\t cacheEntry->\n\t\t\t\t\t\t\t\t\t\t\t\t referencingRelationsViaForeignKey);\n\t\t\tList *dependencyTaskList = NIL;\n\n\t\t\tOid relatedRelationId = InvalidOid;\n\t\t\tforeach_declared_oid(relatedRelationId, relatedRelations)\n\t\t\t{\n\t\t\t\tif (!list_member_oid(referenceTableIdList, relatedRelationId))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tList *relatedShardIntervalList = LoadShardIntervalList(\n\t\t\t\t\trelatedRelationId);\n\t\t\t\tShardInterval *relatedShardInterval = (ShardInterval *) linitial(\n\t\t\t\t\trelatedShardIntervalList);\n\t\t\t\tuint64 relatedShardId = relatedShardInterval->shardId;\n\t\t\t\tbool taskFound = false;\n\t\t\t\tShardTaskEntry *taskEntry = hash_search(shardTaskMap, &relatedShardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tHASH_FIND, &taskFound);\n\t\t\t\tif (taskFound)\n\t\t\t\t{\n\t\t\t\t\tdependencyTaskList = lappend(dependencyTaskList, taskEntry);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tint nDepends = list_length(dependencyTaskList);\n\t\t\tint64 *dependsArray = NULL;\n\t\t\tif (nDepends > 0)\n\t\t\t{\n\t\t\t\tdependsArray = (int64 *) palloc(sizeof(int64) * nDepends);\n\t\t\t\tint i = 0;\n\t\t\t\tListCell *lc;\n\t\t\t\tforeach(lc, dependencyTaskList)\n\t\t\t\t{\n\t\t\t\t\tdependsArray[i++] = ((ShardTaskEntry *) lfirst(lc))->taskId;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tint32 nodesInvolved[2] = { 0 };\n\t\t\tnodesInvolved[0] = sourceShardPlacement->nodeId;\n\t\t\tnodesInvolved[1] = newWorkerNode->nodeId;\n\n\t\t\ttask = ScheduleBackgroundTask(jobId, CitusExtensionOwner(), buf.data,\n\t\t\t\t\t\t\t\t\t\t  nDepends,\n\t\t\t\t\t\t\t\t\t\t  dependsArray, 2,\n\t\t\t\t\t\t\t\t\t\t  nodesInvolved);\n\n\t\t\tbool found = false;\n\t\t\tShardTaskEntry *taskEntry = hash_search(shardTaskMap, &shardId, HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t\t&found);\n\t\t\tif (!found)\n\t\t\t{\n\t\t\t\ttaskEntry->taskId = task->taskid;\n\t\t\t\tereport(DEBUG2,\n\t\t\t\t\t\t(errmsg(\n\t\t\t\t\t\t\t \"Added hash entry in scheduled task hash \"\n\t\t\t\t\t\t\t \"with task %ld for shard %ld\",\n\t\t\t\t\t\t\t task->taskid, shardId)));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tereport(ERROR, (errmsg(\"failed to record task dependency for shard %ld\",\n\t\t\t\t\t\t\t\t\t   shardId)));\n\t\t\t}\n\n\t\t\tnodeTasksList = lappend(nodeTasksList, task);\n\t\t\tif (dependsArray)\n\t\t\t{\n\t\t\t\tpfree(dependsArray);\n\t\t\t}\n\t\t\tlist_free(dependencyTaskList);\n\t\t}\n\n\t\tif (list_length(nodeTasksList) > 0)\n\t\t{\n\t\t\tint nDepends = list_length(nodeTasksList);\n\t\t\tint32 nodesInvolved[2] = { 0 };\n\t\t\tnodesInvolved[0] = sourceShardPlacement->nodeId;\n\t\t\tnodesInvolved[1] = newWorkerNode->nodeId;\n\t\t\tint64 *dependsArray = palloc(sizeof(int64) * list_length(nodeTasksList));\n\t\t\tint idx = 0;\n\t\t\tforeach_declared_ptr(task, nodeTasksList)\n\t\t\t{\n\t\t\t\tdependsArray[idx++] = task->taskid;\n\t\t\t}\n\t\t\tresetStringInfo(&buf);\n\t\t\tuint32 shardTransferFlags = SHARD_TRANSFER_CREATE_RELATIONSHIPS_ONLY;\n\t\t\tappendStringInfo(&buf,\n\t\t\t\t\t\t\t \"SET LOCAL application_name TO '%s%ld';\\n\",\n\t\t\t\t\t\t\t CITUS_REBALANCER_APPLICATION_NAME_PREFIX,\n\t\t\t\t\t\t\t GetGlobalPID());\n\t\t\tappendStringInfo(&buf,\n\t\t\t\t\t\t\t \"SELECT \"\n\t\t\t\t\t\t\t \"citus_internal.citus_internal_copy_single_shard_placement\"\n\t\t\t\t\t\t\t \"(%ld,%u,%u,%u,%s)\",\n\t\t\t\t\t\t\t shardId,\n\t\t\t\t\t\t\t sourceShardPlacement->nodeId,\n\t\t\t\t\t\t\t newWorkerNode->nodeId,\n\t\t\t\t\t\t\t shardTransferFlags,\n\t\t\t\t\t\t\t quote_literal_cstr(transferModeString));\n\n\t\t\tereport(DEBUG2,\n\t\t\t\t\t(errmsg(\n\t\t\t\t\t\t \"creating relations for reference table '%s' on %s:%d ... \"\n\t\t\t\t\t\t \"QUERY= %s\",\n\t\t\t\t\t\t referenceTableName, newWorkerNode->workerName,\n\t\t\t\t\t\t newWorkerNode->workerPort, buf.data)));\n\n\t\t\ttask = ScheduleBackgroundTask(jobId, CitusExtensionOwner(), buf.data,\n\t\t\t\t\t\t\t\t\t\t  nDepends,\n\t\t\t\t\t\t\t\t\t\t  dependsArray, 2,\n\t\t\t\t\t\t\t\t\t\t  nodesInvolved);\n\n\t\t\tdepTasksList = lappend(depTasksList, task);\n\n\t\t\tpfree(dependsArray);\n\t\t\tlist_free(nodeTasksList);\n\t\t\tnodeTasksList = NIL;\n\t\t}\n\n\t\thash_destroy(shardTaskMap);\n\t}\n\n\t/*\n\t * compute a dependent task list array to be used to indicate the completion of all\n\t * reference table shards copy, so that we can start with distributed shard copy\n\t */\n\tif (list_length(depTasksList) > 0)\n\t{\n\t\t*nDependTasks = list_length(depTasksList);\n\t\tdependsTaskArray = palloc(sizeof(int64) * *nDependTasks);\n\t\tint idx = 0;\n\t\tforeach_declared_ptr(task, depTasksList)\n\t\t{\n\t\t\tdependsTaskArray[idx++] = task->taskid;\n\t\t}\n\t\tlist_free(depTasksList);\n\t}\n\n\t/*\n\t * Since reference tables have been copied via a loopback connection we do not have to\n\t * retain our locks. Since Citus only runs well in READ COMMITTED mode we can be sure\n\t * that other transactions will find the reference tables copied.\n\t * We have obtained and held multiple locks, here we unlock them all in the reverse\n\t * order we have obtained them in.\n\t */\n\tfor (int releaseLockmodeIndex = lengthof(lockmodes) - 1; releaseLockmodeIndex >= 0;\n\t\t releaseLockmodeIndex--)\n\t{\n\t\tUnlockColocationId(colocationId, lockmodes[releaseLockmodeIndex]);\n\t}\n\treturn dependsTaskArray;\n}\n\n\n/*\n * HasNodesWithMissingReferenceTables checks if all reference tables are already copied to\n * all nodes. When a node doesn't have a copy of the reference tables we call them missing\n * and this function will return true.\n *\n * The caller might be interested in the list of all reference tables after this check and\n * this the list of tables is written to *referenceTableList if a non-null pointer is\n * passed.\n */\nbool\nHasNodesWithMissingReferenceTables(List **referenceTableList)\n{\n\tint colocationId = GetReferenceTableColocationId();\n\n\tif (colocationId == INVALID_COLOCATION_ID)\n\t{\n\t\t/* we have no reference table yet. */\n\t\treturn false;\n\t}\n\tLockColocationId(colocationId, AccessShareLock);\n\n\tList *referenceTableIdList = CitusTableTypeIdList(REFERENCE_TABLE);\n\tif (referenceTableList)\n\t{\n\t\t*referenceTableList = referenceTableIdList;\n\t}\n\n\tif (list_length(referenceTableIdList) <= 0)\n\t{\n\t\treturn false;\n\t}\n\n\tOid referenceTableId = linitial_oid(referenceTableIdList);\n\tList *shardIntervalList = LoadShardIntervalList(referenceTableId);\n\tif (list_length(shardIntervalList) == 0)\n\t{\n\t\tconst char *referenceTableName = get_rel_name(referenceTableId);\n\n\t\t/* check for corrupt metadata */\n\t\tereport(ERROR, (errmsg(\"reference table \\\"%s\\\" does not have a shard\",\n\t\t\t\t\t\t\t   referenceTableName)));\n\t}\n\n\tShardInterval *shardInterval = (ShardInterval *) linitial(shardIntervalList);\n\tuint64 shardId = shardInterval->shardId;\n\tList *newWorkersList = WorkersWithoutReferenceTablePlacement(shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t AccessShareLock);\n\n\tif (list_length(newWorkersList) <= 0)\n\t{\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n\n/*\n * AnyRelationsModifiedInTransaction returns true if any of the given relations\n * were modified in the current transaction.\n */\nstatic bool\nAnyRelationsModifiedInTransaction(List *relationIdList)\n{\n\tOid relationId = InvalidOid;\n\n\tforeach_declared_oid(relationId, relationIdList)\n\t{\n\t\tif (GetRelationDDLAccessMode(relationId) != RELATION_NOT_ACCESSED ||\n\t\t\tGetRelationDMLAccessMode(relationId) != RELATION_NOT_ACCESSED)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * WorkersWithoutReferenceTablePlacement returns a list of workers (WorkerNode) that\n * do not yet have a placement for the given reference table shard ID, but are\n * supposed to.\n */\nstatic List *\nWorkersWithoutReferenceTablePlacement(uint64 shardId, LOCKMODE lockMode)\n{\n\tList *workersWithoutPlacements = NIL;\n\n\tList *shardPlacementList = ActiveShardPlacementList(shardId);\n\n\tList *workerNodeList = ReferenceTablePlacementNodeList(lockMode);\n\tworkerNodeList = SortList(workerNodeList, CompareWorkerNodes);\n\n\tWorkerNode *workerNode = NULL;\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tchar *nodeName = workerNode->workerName;\n\t\tuint32 nodePort = workerNode->workerPort;\n\t\tShardPlacement *targetPlacement = SearchShardPlacementInList(shardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t nodeName, nodePort);\n\t\tif (targetPlacement == NULL)\n\t\t{\n\t\t\tworkersWithoutPlacements = lappend(workersWithoutPlacements, workerNode);\n\t\t}\n\t}\n\n\treturn workersWithoutPlacements;\n}\n\n\n/*\n * CopyShardPlacementToWorkerNodeQuery returns the citus_copy_shard_placement\n * command to copy the given shard placement to given node.\n */\nstatic StringInfo\nCopyShardPlacementToWorkerNodeQuery(ShardPlacement *sourceShardPlacement,\n\t\t\t\t\t\t\t\t\tWorkerNode *workerNode,\n\t\t\t\t\t\t\t\t\tchar transferMode)\n{\n\tStringInfo queryString = makeStringInfo();\n\n\tconst char *transferModeString =\n\t\ttransferMode == TRANSFER_MODE_BLOCK_WRITES ? \"block_writes\" :\n\t\ttransferMode == TRANSFER_MODE_FORCE_LOGICAL ? \"force_logical\" :\n\t\t\"auto\";\n\n\tappendStringInfo(queryString,\n\t\t\t\t\t \"SELECT pg_catalog.citus_copy_shard_placement(\"\n\t\t\t\t\t UINT64_FORMAT \", %d, %d, \"\n\t\t\t\t\t \"transfer_mode := %s)\",\n\t\t\t\t\t sourceShardPlacement->shardId,\n\t\t\t\t\t sourceShardPlacement->nodeId,\n\t\t\t\t\t workerNode->nodeId,\n\t\t\t\t\t quote_literal_cstr(transferModeString));\n\n\treturn queryString;\n}\n\n\n/*\n * upgrade_to_reference_table was removed, but we maintain a dummy implementation\n * to support downgrades.\n */\nDatum\nupgrade_to_reference_table(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\terrmsg(\"this function is deprecated and no longer used\")));\n}\n\n\n/*\n * CreateReferenceTableColocationId creates a new co-location id for reference tables and\n * writes it into pg_dist_colocation, then returns the created co-location id. Since there\n * can be only one colocation group for all kinds of reference tables, if a co-location id\n * is already created for reference tables, this function returns it without creating\n * anything.\n */\nuint32\nCreateReferenceTableColocationId()\n{\n\tint shardCount = 1;\n\tOid distributionColumnType = InvalidOid;\n\tOid distributionColumnCollation = InvalidOid;\n\n\t/*\n\t * We don't maintain replication factor of reference tables anymore and\n\t * just use -1 instead. We don't use this value in any places.\n\t */\n\tint replicationFactor = -1;\n\n\t/* check for existing colocations */\n\tuint32 colocationId =\n\t\tColocationId(shardCount, replicationFactor, distributionColumnType,\n\t\t\t\t\t distributionColumnCollation);\n\n\tif (colocationId == INVALID_COLOCATION_ID)\n\t{\n\t\tcolocationId = CreateColocationGroup(shardCount, replicationFactor,\n\t\t\t\t\t\t\t\t\t\t\t distributionColumnType,\n\t\t\t\t\t\t\t\t\t\t\t distributionColumnCollation);\n\t}\n\n\treturn colocationId;\n}\n\n\nuint32\nGetReferenceTableColocationId()\n{\n\tint shardCount = 1;\n\tOid distributionColumnType = InvalidOid;\n\tOid distributionColumnCollation = InvalidOid;\n\n\t/*\n\t * We don't maintain replication factor of reference tables anymore and\n\t * just use -1 instead. We don't use this value in any places.\n\t */\n\tint replicationFactor = -1;\n\n\t/* check for existing colocations */\n\tuint32 colocationId =\n\t\tColocationId(shardCount, replicationFactor, distributionColumnType,\n\t\t\t\t\t distributionColumnCollation);\n\n\treturn colocationId;\n}\n\n\n/*\n * GetAllReplicatedTableList returns all tables which has replicated placements.\n * i.e. (all reference tables) + (distributed tables with more than 1 placements)\n */\nList *\nGetAllReplicatedTableList(void)\n{\n\tList *referenceTableList = CitusTableTypeIdList(REFERENCE_TABLE);\n\tList *replicatedMetadataSyncedDistributedTableList =\n\t\tReplicatedMetadataSyncedDistributedTableList();\n\n\tList *replicatedTableList =\n\t\tlist_concat(referenceTableList, replicatedMetadataSyncedDistributedTableList);\n\n\treturn replicatedTableList;\n}\n\n\n/*\n * ReplicatedPlacementsForNodeGroup filters all replicated placements for given\n * node group id.\n */\nList *\nReplicatedPlacementsForNodeGroup(int32 groupId)\n{\n\tList *replicatedTableList = GetAllReplicatedTableList();\n\n\tif (list_length(replicatedTableList) == 0)\n\t{\n\t\treturn NIL;\n\t}\n\n\tList *replicatedPlacementsForNodeGroup = NIL;\n\tOid replicatedTableId = InvalidOid;\n\tforeach_declared_oid(replicatedTableId, replicatedTableList)\n\t{\n\t\tList *placements =\n\t\t\tGroupShardPlacementsForTableOnGroup(replicatedTableId, groupId);\n\t\tif (list_length(placements) == 0)\n\t\t{\n\t\t\t/*\n\t\t\t * This happens either the node was previously disabled or the table\n\t\t\t * doesn't have placement on this node.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\treplicatedPlacementsForNodeGroup = list_concat(replicatedPlacementsForNodeGroup,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   placements);\n\t}\n\n\treturn replicatedPlacementsForNodeGroup;\n}\n\n\n/*\n * DeleteShardPlacementCommand returns a command for deleting given placement from\n * metadata.\n */\nchar *\nDeleteShardPlacementCommand(uint64 placementId)\n{\n\tStringInfo deletePlacementCommand = makeStringInfo();\n\tappendStringInfo(deletePlacementCommand,\n\t\t\t\t\t \"DELETE FROM pg_catalog.pg_dist_placement \"\n\t\t\t\t\t \"WHERE placementid = \" UINT64_FORMAT, placementId);\n\treturn deletePlacementCommand->data;\n}\n\n\n/*\n * DeleteAllReplicatedTablePlacementsFromNodeGroup function iterates over\n * list of reference and replicated hash distributed tables and deletes\n * all placements from pg_dist_placement table for given group.\n */\nvoid\nDeleteAllReplicatedTablePlacementsFromNodeGroup(int32 groupId, bool localOnly)\n{\n\tList *replicatedPlacementListForGroup = ReplicatedPlacementsForNodeGroup(groupId);\n\n\t/* if there are no replicated tables for the group, we do not need to do anything */\n\tif (list_length(replicatedPlacementListForGroup) == 0)\n\t{\n\t\treturn;\n\t}\n\n\tGroupShardPlacement *placement = NULL;\n\tforeach_declared_ptr(placement, replicatedPlacementListForGroup)\n\t{\n\t\tLockShardDistributionMetadata(placement->shardId, ExclusiveLock);\n\n\t\tif (!localOnly)\n\t\t{\n\t\t\tchar *deletePlacementCommand =\n\t\t\t\tDeleteShardPlacementCommand(placement->placementId);\n\n\t\t\tSendCommandToWorkersWithMetadata(deletePlacementCommand);\n\t\t}\n\n\t\tDeleteShardPlacementRow(placement->placementId);\n\t}\n}\n\n\n/*\n * DeleteAllReplicatedTablePlacementsFromNodeGroupViaMetadataContext does the same as\n * DeleteAllReplicatedTablePlacementsFromNodeGroup except it uses metadataSyncContext for\n * connections.\n */\nvoid\nDeleteAllReplicatedTablePlacementsFromNodeGroupViaMetadataContext(MetadataSyncContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  context, int32 groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  bool localOnly)\n{\n\tList *replicatedPlacementListForGroup = ReplicatedPlacementsForNodeGroup(groupId);\n\n\t/* if there are no replicated tables for the group, we do not need to do anything */\n\tif (list_length(replicatedPlacementListForGroup) == 0)\n\t{\n\t\treturn;\n\t}\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(context->context);\n\tGroupShardPlacement *placement = NULL;\n\tforeach_declared_ptr(placement, replicatedPlacementListForGroup)\n\t{\n\t\tLockShardDistributionMetadata(placement->shardId, ExclusiveLock);\n\n\t\tif (!localOnly)\n\t\t{\n\t\t\tchar *deletePlacementCommand =\n\t\t\t\tDeleteShardPlacementCommand(placement->placementId);\n\n\t\t\tSendOrCollectCommandListToMetadataNodes(context,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlist_make1(deletePlacementCommand));\n\t\t}\n\n\t\t/* do not execute local transaction if we collect commands */\n\t\tif (!MetadataSyncCollectsCommands(context))\n\t\t{\n\t\t\tDeleteShardPlacementRow(placement->placementId);\n\t\t}\n\n\t\tResetMetadataSyncMemoryContext(context);\n\t}\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * ReplicatedMetadataSyncedDistributedTableList is a helper function which returns the\n * list of replicated hash distributed tables.\n */\nstatic List *\nReplicatedMetadataSyncedDistributedTableList(void)\n{\n\tList *distributedRelationList = CitusTableTypeIdList(DISTRIBUTED_TABLE);\n\tList *replicatedHashDistributedTableList = NIL;\n\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, distributedRelationList)\n\t{\n\t\tif (ShouldSyncTableMetadata(relationId) && !SingleReplicatedTable(relationId))\n\t\t{\n\t\t\treplicatedHashDistributedTableList =\n\t\t\t\tlappend_oid(replicatedHashDistributedTableList, relationId);\n\t\t}\n\t}\n\n\treturn replicatedHashDistributedTableList;\n}\n\n\n/* CompareOids is a comparison function for sort shard oids */\nint\nCompareOids(const void *leftElement, const void *rightElement)\n{\n\tOid *leftId = (Oid *) leftElement;\n\tOid *rightId = (Oid *) rightElement;\n\n\tif (*leftId > *rightId)\n\t{\n\t\treturn 1;\n\t}\n\telse if (*leftId < *rightId)\n\t{\n\t\treturn -1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * ErrorIfNotAllNodesHaveReferenceTableReplicas throws an error when one of the\n * nodes in the list does not have reference table replicas.\n */\nvoid\nErrorIfNotAllNodesHaveReferenceTableReplicas(List *workerNodeList)\n{\n\tWorkerNode *workerNode = NULL;\n\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\tif (!NodeHasAllReferenceTableReplicas(workerNode))\n\t\t{\n\t\t\tereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),\n\t\t\t\t\t\t\terrmsg(\"reference tables have not been replicated to \"\n\t\t\t\t\t\t\t\t   \"node %s:%d yet\",\n\t\t\t\t\t\t\t\t   workerNode->workerName,\n\t\t\t\t\t\t\t\t   workerNode->workerPort),\n\t\t\t\t\t\t\terrdetail(\"Reference tables are lazily replicated after \"\n\t\t\t\t\t\t\t\t\t  \"adding a node, but must exist before shards can \"\n\t\t\t\t\t\t\t\t\t  \"be created on that node.\"),\n\t\t\t\t\t\t\terrhint(\"Run SELECT replicate_reference_tables(); to \"\n\t\t\t\t\t\t\t\t\t\"ensure reference tables exist on all nodes.\")));\n\t\t}\n\t}\n}\n\n\n/*\n * NodeHasAllReferenceTablesReplicas returns whether the given worker node has reference\n * table replicas. If there are no reference tables the function returns true.\n *\n * This function does not do any locking, so the situation could change immediately after,\n * though we can only ever transition from false to true, so only \"false\" could be the\n * incorrect answer.\n *\n * In the case where the function returns true because no reference tables exist\n * on the node, a reference table could be created immediately after. However, the\n * creation logic guarantees that this reference table will be created on all the\n * nodes, so our answer was correct.\n */\nstatic bool\nNodeHasAllReferenceTableReplicas(WorkerNode *workerNode)\n{\n\tList *referenceTableIdList = CitusTableTypeIdList(REFERENCE_TABLE);\n\n\tif (list_length(referenceTableIdList) == 0)\n\t{\n\t\t/* no reference tables exist */\n\t\treturn true;\n\t}\n\n\tOid referenceTableId = linitial_oid(referenceTableIdList);\n\tList *shardIntervalList = LoadShardIntervalList(referenceTableId);\n\tif (list_length(shardIntervalList) != 1)\n\t{\n\t\t/* check for corrupt metadata */\n\t\tereport(ERROR, (errmsg(\"reference table \\\"%s\\\" can only have 1 shard\",\n\t\t\t\t\t\t\t   get_rel_name(referenceTableId))));\n\t}\n\n\tShardInterval *shardInterval = (ShardInterval *) linitial(shardIntervalList);\n\tList *shardPlacementList = ActiveShardPlacementList(shardInterval->shardId);\n\n\tShardPlacement *placement = NULL;\n\tforeach_declared_ptr(placement, shardPlacementList)\n\t{\n\t\tif (placement->groupId == workerNode->groupId)\n\t\t{\n\t\t\t/* our worker has a reference table placement */\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/relation_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * relation_utils.c\n *\n * This file contains functions similar to rel.h to perform useful\n * operations on Relation objects.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"utils/lsyscache.h\"\n#include \"utils/rel.h\"\n\n#include \"distributed/relation_utils.h\"\n\n\n/*\n * RelationGetNamespaceName returns the relation's namespace name.\n */\nchar *\nRelationGetNamespaceName(Relation relation)\n{\n\tOid namespaceId = RelationGetNamespace(relation);\n\tchar *namespaceName = get_namespace_name(namespaceId);\n\treturn namespaceName;\n}\n\n\n/*\n * GetFilledPermissionInfo creates RTEPermissionInfo for a given RTE\n * and fills it with given data and returns this RTEPermissionInfo object.\n * Added this function since Postgres's addRTEPermissionInfo doesn't fill the data.\n *\n * Given data consists of relid, inh and requiredPerms\n * Took a quick look around Postgres, unless specified otherwise,\n * we are dealing with GetUserId().\n * Currently the following entries are filled like this:\n *      perminfo->checkAsUser = GetUserId();\n */\nRTEPermissionInfo *\nGetFilledPermissionInfo(Oid relid, bool inh, AclMode requiredPerms)\n{\n\tRTEPermissionInfo *perminfo = makeNode(RTEPermissionInfo);\n\tperminfo->relid = relid;\n\tperminfo->inh = inh;\n\tperminfo->requiredPerms = requiredPerms;\n\tperminfo->checkAsUser = GetUserId();\n\treturn perminfo;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/replication_origin_session_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * replication_origin_session_utils.c\n *   Functions for managing replication origin session.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"utils/builtins.h\"\n\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/replication_origin_session_utils.h\"\n\nstatic bool IsRemoteReplicationOriginSessionSetup(MultiConnection *connection);\n\nstatic void SetupMemoryContextResetReplicationOriginHandler(void);\n\nstatic void SetupReplicationOriginSessionHelper(bool isContexResetSetupNeeded);\n\nPG_FUNCTION_INFO_V1(citus_internal_start_replication_origin_tracking);\nPG_FUNCTION_INFO_V1(citus_internal_stop_replication_origin_tracking);\nPG_FUNCTION_INFO_V1(citus_internal_is_replication_origin_tracking_active);\n\n/*\n * This variable is used to remember the replication origin id of the current session\n * before resetting it to DoNotReplicateId in SetupReplicationOriginLocalSession.\n */\nstatic RepOriginId OriginalOriginId = InvalidRepOriginId;\n\n/*\n * Setting that controls whether replication origin tracking is enabled\n */\nbool EnableChangeDataCapture = false;\n\n\n/* citus_internal_start_replication_origin_tracking starts a new replication origin session\n * in the local node. This function is used to avoid publishing the WAL records to the\n * replication slot by setting replication origin to DoNotReplicateId in WAL records.\n * It remembers the previous replication origin for the current session which will be\n * used to reset the replication origin to the previous value when the session ends.\n */\nDatum\ncitus_internal_start_replication_origin_tracking(PG_FUNCTION_ARGS)\n{\n\tif (!EnableChangeDataCapture)\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\tSetupReplicationOriginSessionHelper(false);\n\tPG_RETURN_VOID();\n}\n\n\n/* citus_internal_stop_replication_origin_tracking ends the current replication origin session\n * in the local node. This function is used to reset the replication origin to the\n * earlier value of replication origin.\n */\nDatum\ncitus_internal_stop_replication_origin_tracking(PG_FUNCTION_ARGS)\n{\n\tResetReplicationOriginLocalSession();\n\tPG_RETURN_VOID();\n}\n\n\n/* IsLocalReplicationOriginSessionActive checks if the current replication origin\n * session is active in the local node.\n */\nstatic inline bool\nIsLocalReplicationOriginSessionActive(void)\n{\n\treturn (replorigin_session_origin == DoNotReplicateId);\n}\n\n\n/* citus_internal_is_replication_origin_tracking_active checks if the current replication origin\n * session is active in the local node.\n */\nDatum\ncitus_internal_is_replication_origin_tracking_active(PG_FUNCTION_ARGS)\n{\n\tbool result = IsLocalReplicationOriginSessionActive();\n\tPG_RETURN_BOOL(result);\n}\n\n\n/*\n * SetupMemoryContextResetReplicationOriginHandler registers a callback function\n * that resets the replication origin session in case of any error for the current\n * memory context.\n */\nstatic void\nSetupMemoryContextResetReplicationOriginHandler()\n{\n\tMemoryContextCallback *replicationOriginResetCallback = palloc0(\n\t\tsizeof(MemoryContextCallback));\n\treplicationOriginResetCallback->func =\n\t\tResetReplicationOriginLocalSessionCallbackHandler;\n\treplicationOriginResetCallback->arg = NULL;\n\tMemoryContextRegisterResetCallback(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t   replicationOriginResetCallback);\n}\n\n\n/*\n * SetupReplicationOriginSessionHelper sets up a new replication origin session in a\n * local session. It takes an argument isContexResetSetupNeeded to decide whether\n * to register a callback function that resets the replication origin session in case\n * of any error for the current memory context.\n */\nstatic void\nSetupReplicationOriginSessionHelper(bool isContexResetSetupNeeded)\n{\n\tif (!EnableChangeDataCapture)\n\t{\n\t\treturn;\n\t}\n\tOriginalOriginId = replorigin_session_origin;\n\treplorigin_session_origin = DoNotReplicateId;\n\tif (isContexResetSetupNeeded)\n\t{\n\t\tSetupMemoryContextResetReplicationOriginHandler();\n\t}\n}\n\n\n/*\n * SetupReplicationOriginLocalSession sets up a new replication origin session in a\n * local session.\n */\nvoid\nSetupReplicationOriginLocalSession()\n{\n\tSetupReplicationOriginSessionHelper(true);\n}\n\n\n/*\n * ResetReplicationOriginLocalSession resets the replication origin session in a\n * local node.\n */\nvoid\nResetReplicationOriginLocalSession(void)\n{\n\tif (replorigin_session_origin != DoNotReplicateId)\n\t{\n\t\treturn;\n\t}\n\n\treplorigin_session_origin = OriginalOriginId;\n}\n\n\n/*\n * ResetReplicationOriginLocalSessionCallbackHandler is a callback function that\n * resets the replication origin session in a local node. This is used to register\n * with MemoryContextRegisterResetCallback to reset the replication origin session\n * in case of any error for the given memory context.\n */\nvoid\nResetReplicationOriginLocalSessionCallbackHandler(void *arg)\n{\n\tResetReplicationOriginLocalSession();\n}\n\n\n/*\n * SetupReplicationOriginRemoteSession sets up a new replication origin session in a\n * remote session. The identifier is used to create a unique replication origin name\n * for the session in the remote node.\n */\nvoid\nSetupReplicationOriginRemoteSession(MultiConnection *connection)\n{\n\tif (!EnableChangeDataCapture)\n\t{\n\t\treturn;\n\t}\n\tif (connection != NULL && !IsRemoteReplicationOriginSessionSetup(connection))\n\t{\n\t\tStringInfo replicationOriginSessionSetupQuery = makeStringInfo();\n\t\tappendStringInfo(replicationOriginSessionSetupQuery,\n\t\t\t\t\t\t \"select citus_internal.start_replication_origin_tracking();\");\n\t\tExecuteCriticalRemoteCommand(connection,\n\t\t\t\t\t\t\t\t\t replicationOriginSessionSetupQuery->data);\n\t\tconnection->isReplicationOriginSessionSetup = true;\n\t}\n}\n\n\n/*\n * ResetReplicationOriginRemoteSession resets the replication origin session in a\n * remote node.\n */\nvoid\nResetReplicationOriginRemoteSession(MultiConnection *connection)\n{\n\tif (connection != NULL && connection->isReplicationOriginSessionSetup)\n\t{\n\t\tStringInfo replicationOriginSessionResetQuery = makeStringInfo();\n\t\tappendStringInfo(replicationOriginSessionResetQuery,\n\t\t\t\t\t\t \"select citus_internal.stop_replication_origin_tracking();\");\n\t\tExecuteCriticalRemoteCommand(connection,\n\t\t\t\t\t\t\t\t\t replicationOriginSessionResetQuery->data);\n\t\tconnection->isReplicationOriginSessionSetup = false;\n\t}\n}\n\n\n/*\n * IsRemoteReplicationOriginSessionSetup checks if the replication origin is setup\n * already in the remote session by calliing the UDF\n * citus_internal_is_replication_origin_tracking_active(). This is also remembered\n * in the connection object to avoid calling the UDF again next time.\n */\nstatic bool\nIsRemoteReplicationOriginSessionSetup(MultiConnection *connection)\n{\n\tif (connection->isReplicationOriginSessionSetup)\n\t{\n\t\treturn true;\n\t}\n\n\tStringInfo isReplicationOriginSessionSetupQuery = makeStringInfo();\n\tappendStringInfo(isReplicationOriginSessionSetupQuery,\n\t\t\t\t\t \"SELECT citus_internal.is_replication_origin_tracking_active()\");\n\tbool result =\n\t\tExecuteRemoteCommandAndCheckResult(connection,\n\t\t\t\t\t\t\t\t\t\t   isReplicationOriginSessionSetupQuery->data,\n\t\t\t\t\t\t\t\t\t\t   \"t\");\n\n\tconnection->isReplicationOriginSessionSetup = result;\n\treturn result;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/resource_lock.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * resource_lock.c\n *\t  Locking Infrastructure for Citus.\n *\n * To avoid introducing a new type of locktag - that then could not be\n * displayed by core functionality - we reuse advisory locks. If we'd just\n * reused them directly we'd run into danger conflicting with user-defined\n * advisory locks, but luckily advisory locks only two values for 'field4' in\n * the locktag.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"miscadmin.h\"\n\n#include \"access/xact.h\"\n#include \"catalog/namespace.h\"\n#include \"commands/tablecmds.h\"\n#include \"storage/lmgr.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/varlena.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/reference_table_utils.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/utils/array_type.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_shard_visibility.h\"\n#include \"distributed/worker_transaction.h\"\n\n#define LOCK_RELATION_IF_EXISTS \\\n\t\t\"SELECT pg_catalog.lock_relation_if_exists(%s, %s);\"\n\n/* static definition and declarations */\nstruct LockModeToStringType\n{\n\tLOCKMODE lockMode;\n\tconst char *name;\n};\n\n/*\n * list of lock mode mappings, number of items need to be kept in sync\n * with lock_mode_to_string_map_count.\n */\nstatic const struct LockModeToStringType lockmode_to_string_map[] = {\n\t{ NoLock, \"NoLock\" },\n\t{ AccessShareLock, \"ACCESS SHARE\" },\n\t{ RowShareLock, \"ROW SHARE\" },\n\t{ RowExclusiveLock, \"ROW EXCLUSIVE\" },\n\t{ ShareUpdateExclusiveLock, \"SHARE UPDATE EXCLUSIVE\" },\n\t{ ShareLock, \"SHARE\" },\n\t{ ShareRowExclusiveLock, \"SHARE ROW EXCLUSIVE\" },\n\t{ ExclusiveLock, \"EXCLUSIVE\" },\n\t{ AccessExclusiveLock, \"ACCESS EXCLUSIVE\" }\n};\nstatic const int lock_mode_to_string_map_count = sizeof(lockmode_to_string_map) /\n\t\t\t\t\t\t\t\t\t\t\t\t sizeof(lockmode_to_string_map[0]);\n\n/*\n * LockRelationRecord holds the oid of a relation to be locked\n * and a boolean inh to determine whether its decendants\n * should be locked as well\n */\ntypedef struct LockRelationRecord\n{\n\tOid relationId;\n\tbool inh;\n} LockRelationRecord;\n\n\n/* local function forward declarations */\nstatic LOCKMODE IntToLockMode(int mode);\nstatic void LockReferencedReferenceShardResources(uint64 shardId, LOCKMODE lockMode);\nstatic bool AnyTableReplicated(List *shardIntervalList,\n\t\t\t\t\t\t\t   List **replicatedShardIntervalList);\nstatic void LockShardListResources(List *shardIntervalList, LOCKMODE lockMode);\nstatic void LockShardListResourcesOnFirstWorker(LOCKMODE lockmode,\n\t\t\t\t\t\t\t\t\t\t\t\tList *shardIntervalList);\nstatic bool IsFirstWorkerNode();\nstatic void CitusRangeVarCallbackForLockTable(const RangeVar *rangeVar, Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t  Oid oldRelationId, void *arg);\nstatic AclResult CitusLockTableAclCheck(Oid relationId, LOCKMODE lockmode, Oid userId);\nstatic void SetLocktagForShardDistributionMetadata(int64 shardId, LOCKTAG *tag);\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(lock_shard_metadata);\nPG_FUNCTION_INFO_V1(lock_shard_resources);\nPG_FUNCTION_INFO_V1(lock_relation_if_exists);\n\n/* Config variable managed via guc.c */\nbool EnableAcquiringUnsafeLockFromWorkers = false;\nbool SkipAdvisoryLockPermissionChecks = false;\n\n\n/*\n * lock_shard_metadata allows the shard distribution metadata to be locked\n * remotely to block concurrent writes from workers in MX tables.\n *\n * This function does not sort the array to avoid deadlock, callers\n * must ensure a consistent order.\n */\nDatum\nlock_shard_metadata(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tLOCKMODE lockMode = IntToLockMode(PG_GETARG_INT32(0));\n\tArrayType *shardIdArrayObject = PG_GETARG_ARRAYTYPE_P(1);\n\n\tif (ARR_NDIM(shardIdArrayObject) == 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"no locks specified\")));\n\t}\n\n\tint shardIdCount = ArrayObjectCount(shardIdArrayObject);\n\tDatum *shardIdArrayDatum = DeconstructArrayObject(shardIdArrayObject);\n\n\tfor (int shardIdIndex = 0; shardIdIndex < shardIdCount; shardIdIndex++)\n\t{\n\t\tint64 shardId = DatumGetInt64(shardIdArrayDatum[shardIdIndex]);\n\n\t\t/*\n\t\t * We don't want random users to block writes. The callers of this\n\t\t * function either operates on all the colocated placements, such\n\t\t * as shard moves, or requires superuser such as adding node.\n\t\t * In other words, the coordinator initiated operations has already\n\t\t * ensured table owner, we are preventing any malicious attempt to\n\t\t * use this function.\n\t\t */\n\t\tbool missingOk = true;\n\t\tEnsureShardOwner(shardId, missingOk);\n\n\t\tLockShardDistributionMetadata(shardId, lockMode);\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * EnsureShardOwner gets the shardId and reads pg_dist_partition to find\n * the corresponding relationId. If the relation does not exist, the function\n * returns. If the relation exists, the function ensures if the current\n * user is the owner of the table.\n *\n */\nvoid\nEnsureShardOwner(uint64 shardId, bool missingOk)\n{\n\tOid relationId = LookupShardRelationFromCatalog(shardId, missingOk);\n\n\tif (!OidIsValid(relationId) && missingOk)\n\t{\n\t\t/*\n\t\t * This could happen in two ways. First, a malicious user is trying\n\t\t * to acquire locks on non-existing shards. Second, the metadata has\n\t\t * not been synced (or not yet visible) to this node. In the second\n\t\t * case, there is no point in locking the shards because no other\n\t\t * transaction can be accessing the table.\n\t\t */\n\t\treturn;\n\t}\n\n\tEnsureTableOwner(relationId);\n}\n\n\n/*\n * lock_shard_resources allows shard resources to be locked\n * remotely to serialise non-commutative writes on shards.\n *\n * This function does not sort the array to avoid deadlock, callers\n * must ensure a consistent order.\n */\nDatum\nlock_shard_resources(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tLOCKMODE lockMode = IntToLockMode(PG_GETARG_INT32(0));\n\tArrayType *shardIdArrayObject = PG_GETARG_ARRAYTYPE_P(1);\n\n\tif (ARR_NDIM(shardIdArrayObject) == 0)\n\t{\n\t\tereport(ERROR, (errmsg(\"no locks specified\")));\n\t}\n\n\tint shardIdCount = ArrayObjectCount(shardIdArrayObject);\n\tDatum *shardIdArrayDatum = DeconstructArrayObject(shardIdArrayObject);\n\n\t/*\n\t * The executor calls this UDF for modification queries. So, any user\n\t * who has the the rights to modify this table are actually able\n\t * to call the UDF.\n\t *\n\t * So, at this point, we make sure that any malicious user who doesn't\n\t * have modification privileges to call this UDF.\n\t *\n\t * Update/Delete/Truncate commands already acquires ExclusiveLock\n\t * on the executor. However, for INSERTs, the user might have only\n\t * INSERTs granted, so add a special case for it.\n\t */\n\tAclMode aclMode = ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE;\n\tAclMaskHow aclMaskHow = ACLMASK_ANY;\n\n\tif (lockMode == RowExclusiveLock)\n\t{\n\t\taclMode |= ACL_INSERT;\n\t}\n\n\tfor (int shardIdIndex = 0; shardIdIndex < shardIdCount; shardIdIndex++)\n\t{\n\t\tint64 shardId = DatumGetInt64(shardIdArrayDatum[shardIdIndex]);\n\n\t\t/*\n\t\t * We don't want random users to block writes. If the current user\n\t\t * has privileges to modify the shard, then the user can already\n\t\t * acquire the lock. So, we allow.\n\t\t */\n\t\tbool missingOk = true;\n\t\tOid relationId = LookupShardRelationFromCatalog(shardId, missingOk);\n\n\t\tif (!OidIsValid(relationId) && missingOk)\n\t\t{\n\t\t\t/*\n\t\t\t * This could happen in two ways. First, a malicious user is trying\n\t\t\t * to acquire locks on non-existing shards. Second, the metadata has\n\t\t\t * not been synced (or not yet visible) to this node. In the second\n\t\t\t * case, there is no point in locking the shards because no other\n\t\t\t * transaction can be accessing the table.\n\t\t\t */\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!SkipAdvisoryLockPermissionChecks)\n\t\t{\n\t\t\tEnsureTablePermissions(relationId, aclMode, aclMaskHow);\n\t\t}\n\n\t\tLockShardResource(shardId, lockMode);\n\t}\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * LockShardListResourcesOnFirstWorker acquires the resource locks for the specified\n * shards on the first worker. Acquiring a lock with or without metadata does not\n * matter for us. So, worker does not have to be an MX node, acquiring the lock\n * on any worker node is enough. Note that the function does not sort the shard list,\n * therefore the caller should sort the shard list in order to avoid deadlocks.\n */\nstatic void\nLockShardListResourcesOnFirstWorker(LOCKMODE lockmode, List *shardIntervalList)\n{\n\tif (!AllowModificationsFromWorkersToReplicatedTables)\n\t{\n\t\t/*\n\t\t * Allowing modifications from worker nodes for replicated tables requires\n\t\t * to serialize modifications, see AcquireExecutorShardLocksForExecution()\n\t\t * for the details.\n\t\t *\n\t\t * If the user opted for disabling modifications from the workers, we do not\n\t\t * need to acquire these remote locks. Returning early saves us from an additional\n\t\t * network round-trip.\n\t\t */\n\t\tAssert(AnyTableReplicated(shardIntervalList, NULL));\n\t\treturn;\n\t}\n\n\tStringInfo lockCommand = makeStringInfo();\n\tint processedShardIntervalCount = 0;\n\tint totalShardIntervalCount = list_length(shardIntervalList);\n\tWorkerNode *firstWorkerNode = GetFirstPrimaryWorkerNode();\n\tint connectionFlags = 0;\n\tconst char *currentUser = CurrentUserName();\n\n\tappendStringInfo(lockCommand, \"SELECT lock_shard_resources(%d, ARRAY[\", lockmode);\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tint64 shardId = shardInterval->shardId;\n\n\t\tappendStringInfo(lockCommand, \"%lu\", shardId);\n\n\t\tprocessedShardIntervalCount++;\n\t\tif (processedShardIntervalCount != totalShardIntervalCount)\n\t\t{\n\t\t\tappendStringInfo(lockCommand, \", \");\n\t\t}\n\t}\n\n\tappendStringInfo(lockCommand, \"])\");\n\n\t/* need to hold the lock until commit */\n\tUseCoordinatedTransaction();\n\n\t/*\n\t * Use the superuser connection to make sure we are allowed to lock.\n\t * This also helps ensure we only use one connection.\n\t */\n\tMultiConnection *firstWorkerConnection = GetNodeUserDatabaseConnection(\n\t\tconnectionFlags,\n\t\tfirstWorkerNode\n\t\t->workerName,\n\t\tfirstWorkerNode\n\t\t->workerPort,\n\t\tcurrentUser,\n\t\tNULL);\n\n\t/* the SELECT .. FOR UPDATE breaks if we lose the connection */\n\tMarkRemoteTransactionCritical(firstWorkerConnection);\n\n\t/* make sure we are in a tranasaction block to hold the lock until commit */\n\tRemoteTransactionBeginIfNecessary(firstWorkerConnection);\n\n\t/* grab the lock on the first worker node */\n\tExecuteCriticalRemoteCommand(firstWorkerConnection, lockCommand->data);\n}\n\n\n/*\n * IsFirstWorkerNode checks whether the node is the first worker node sorted\n * according to the host name and port number.\n */\nstatic bool\nIsFirstWorkerNode()\n{\n\tList *workerNodeList = ActivePrimaryNonCoordinatorNodeList(NoLock);\n\n\tworkerNodeList = SortList(workerNodeList, CompareWorkerNodes);\n\n\tif (list_length(workerNodeList) == 0)\n\t{\n\t\treturn false;\n\t}\n\n\tWorkerNode *firstWorkerNode = (WorkerNode *) linitial(workerNodeList);\n\n\tif (firstWorkerNode->groupId == GetLocalGroupId())\n\t{\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * LockShardListMetadataOnWorkers acquires the matadata locks for the specified shards on\n * metadata workers. Note that the function does not sort the shard list, therefore the\n * caller should sort the shard list in order to avoid deadlocks.\n */\nvoid\nLockShardListMetadataOnWorkers(LOCKMODE lockmode, List *shardIntervalList)\n{\n\tStringInfo lockCommand = makeStringInfo();\n\tint processedShardIntervalCount = 0;\n\tint totalShardIntervalCount = list_length(shardIntervalList);\n\n\tif (list_length(shardIntervalList) == 0)\n\t{\n\t\treturn;\n\t}\n\n\tappendStringInfo(lockCommand, \"SELECT lock_shard_metadata(%d, ARRAY[\", lockmode);\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tint64 shardId = shardInterval->shardId;\n\n\t\tappendStringInfo(lockCommand, \"%lu\", shardId);\n\n\t\tprocessedShardIntervalCount++;\n\t\tif (processedShardIntervalCount != totalShardIntervalCount)\n\t\t{\n\t\t\tappendStringInfo(lockCommand, \", \");\n\t\t}\n\t}\n\n\tappendStringInfo(lockCommand, \"])\");\n\n\t/*\n\t * Disable idle_in_transaction_session_timeout on metadata workers before\n\t * acquiring locks. In block_writes mode, these connections stay open for\n\t * the entire shard copy which can take hours for large shards. Without\n\t * this, the timeout would kill the connection and fail the move.\n\t * SET LOCAL scopes the change to this transaction only.\n\t */\n\tList *commandList = list_make2(\n\t\t\"SET LOCAL idle_in_transaction_session_timeout = 0\",\n\t\tlockCommand->data);\n\tSendCommandListToWorkersWithMetadata(commandList);\n}\n\n\n/*\n * IntToLockMode verifies whether the specified integer is an accepted lock mode\n * and returns it as a LOCKMODE enum.\n */\nstatic LOCKMODE\nIntToLockMode(int mode)\n{\n\tif (mode == ExclusiveLock)\n\t{\n\t\treturn ExclusiveLock;\n\t}\n\telse if (mode == ShareLock)\n\t{\n\t\treturn ShareLock;\n\t}\n\telse if (mode == AccessShareLock)\n\t{\n\t\treturn AccessShareLock;\n\t}\n\telse if (mode == RowExclusiveLock)\n\t{\n\t\treturn RowExclusiveLock;\n\t}\n\telse\n\t{\n\t\telog(ERROR, \"unsupported lockmode %d\", mode);\n\t}\n}\n\n\n/*\n * LockColocationId returns after acquiring a co-location ID lock, typically used\n * for rebalancing and replication.\n */\nvoid\nLockColocationId(int colocationId, LOCKMODE lockMode)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = false;\n\n\tSET_LOCKTAG_REBALANCE_COLOCATION(tag, (int64) colocationId);\n\t(void) LockAcquire(&tag, lockMode, sessionLock, dontWait);\n}\n\n\n/*\n * UnlockColocationId releases a co-location ID lock.\n */\nvoid\nUnlockColocationId(int colocationId, LOCKMODE lockMode)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\n\tSET_LOCKTAG_REBALANCE_COLOCATION(tag, (int64) colocationId);\n\tLockRelease(&tag, lockMode, sessionLock);\n}\n\n\n/*\n * LockShardDistributionMetadata returns after grabbing a lock for distribution\n * metadata related to the specified shard, blocking if required. Any locks\n * acquired using this method are released at transaction end.\n */\nvoid\nLockShardDistributionMetadata(int64 shardId, LOCKMODE lockMode)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = false;\n\n\tSetLocktagForShardDistributionMetadata(shardId, &tag);\n\t(void) LockAcquire(&tag, lockMode, sessionLock, dontWait);\n}\n\n\nstatic void\nSetLocktagForShardDistributionMetadata(int64 shardId, LOCKTAG *tag)\n{\n\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\tOid citusTableId = shardInterval->relationId;\n\tCitusTableCacheEntry *citusTable = GetCitusTableCacheEntry(citusTableId);\n\tuint32 colocationId = citusTable->colocationId;\n\n\tif (colocationId == INVALID_COLOCATION_ID ||\n\t\t(!IsCitusTableTypeCacheEntry(citusTable, HASH_DISTRIBUTED) &&\n\t\t !IsCitusTableTypeCacheEntry(citusTable, SINGLE_SHARD_DISTRIBUTED)))\n\t{\n\t\tSET_LOCKTAG_SHARD_METADATA_RESOURCE(*tag, MyDatabaseId, shardId);\n\t}\n\telse\n\t{\n\t\tSET_LOCKTAG_COLOCATED_SHARDS_METADATA_RESOURCE(*tag, MyDatabaseId, colocationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   shardInterval->shardIndex);\n\t}\n}\n\n\n/*\n * LockReferencedReferenceShardDistributionMetadata acquires shard distribution\n * metadata locks with the given lock mode on the reference tables which has a\n * foreign key from the given relation.\n *\n * It also gets metadata locks on worker nodes to prevent concurrent write\n * operations on reference tables from metadata nodes.\n */\nvoid\nLockReferencedReferenceShardDistributionMetadata(uint64 shardId, LOCKMODE lockMode)\n{\n\tOid relationId = RelationIdForShard(shardId);\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\tList *referencedRelationList = cacheEntry->referencedRelationsViaForeignKey;\n\tList *shardIntervalList = GetSortedReferenceShardIntervals(referencedRelationList);\n\n\tif (list_length(shardIntervalList) > 0 && ClusterHasKnownMetadataWorkers())\n\t{\n\t\tLockShardListMetadataOnWorkers(lockMode, shardIntervalList);\n\t}\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tLockShardDistributionMetadata(shardInterval->shardId, lockMode);\n\t}\n}\n\n\n/*\n * LockReferencedReferenceShardResources acquires resource locks with the\n * given lock mode on the reference tables which has a foreign key from\n * the given relation.\n *\n * It also gets resource locks on worker nodes to prevent concurrent write\n * operations on reference tables from metadata nodes.\n */\nstatic void\nLockReferencedReferenceShardResources(uint64 shardId, LOCKMODE lockMode)\n{\n\tOid relationId = RelationIdForShard(shardId);\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\n\t/*\n\t * Note that referencedRelationsViaForeignKey contains transitively referenced\n\t * relations too.\n\t */\n\tList *referencedRelationList = cacheEntry->referencedRelationsViaForeignKey;\n\tList *referencedShardIntervalList =\n\t\tGetSortedReferenceShardIntervals(referencedRelationList);\n\n\tif (list_length(referencedShardIntervalList) > 0 &&\n\t\tClusterHasKnownMetadataWorkers() &&\n\t\t!IsFirstWorkerNode())\n\t{\n\t\t/*\n\t\t * When there is metadata, all nodes can write to the reference table,\n\t\t * but the writes need to be serialised. To achieve that, all nodes will\n\t\t * take the shard resource lock on the first worker node via RPC, except\n\t\t * for the first worker node which will just take it the regular way.\n\t\t */\n\t\tLockShardListResourcesOnFirstWorker(lockMode, referencedShardIntervalList);\n\t}\n\n\tShardInterval *referencedShardInterval = NULL;\n\tforeach_declared_ptr(referencedShardInterval, referencedShardIntervalList)\n\t{\n\t\tLockShardResource(referencedShardInterval->shardId, lockMode);\n\t}\n}\n\n\n/*\n * GetSortedReferenceShardIntervals iterates through the given relation list,\n * lists the shards of reference tables, and returns the list after sorting.\n */\nList *\nGetSortedReferenceShardIntervals(List *relationList)\n{\n\tList *shardIntervalList = NIL;\n\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationList)\n\t{\n\t\tif (!IsCitusTableType(relationId, REFERENCE_TABLE))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tList *currentShardIntervalList = LoadShardIntervalList(relationId);\n\t\tshardIntervalList = lappend(shardIntervalList, linitial(\n\t\t\t\t\t\t\t\t\t\tcurrentShardIntervalList));\n\t}\n\n\tshardIntervalList = SortList(shardIntervalList, CompareShardIntervalsById);\n\n\treturn shardIntervalList;\n}\n\n\n/*\n * LockShardResource acquires a lock needed to modify data on a remote shard.\n * This task may be assigned to multiple backends at the same time, so the lock\n * manages any concurrency issues associated with shard file fetching and DML\n * command execution.\n */\nvoid\nLockShardResource(uint64 shardId, LOCKMODE lockmode)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = false;\n\n\tSET_LOCKTAG_SHARD_RESOURCE(tag, MyDatabaseId, shardId);\n\n\t(void) LockAcquire(&tag, lockmode, sessionLock, dontWait);\n}\n\n\n/* LockTransactionRecovery acquires a lock for transaction recovery */\nvoid\nLockTransactionRecovery(LOCKMODE lockmode)\n{\n\tLOCKTAG tag;\n\tconst bool sessionLock = false;\n\tconst bool dontWait = false;\n\n\tSET_LOCKTAG_CITUS_OPERATION(tag, CITUS_TRANSACTION_RECOVERY);\n\n\t(void) LockAcquire(&tag, lockmode, sessionLock, dontWait);\n}\n\n\n/*\n * LockShardListMetadata takes shared locks on the metadata of all shards in\n * shardIntervalList to prevents concurrent placement changes.\n */\nvoid\nLockShardListMetadata(List *shardIntervalList, LOCKMODE lockMode)\n{\n\t/* lock shards in order of shard id to prevent deadlock */\n\tshardIntervalList = SortList(shardIntervalList, CompareShardIntervalsById);\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tint64 shardId = shardInterval->shardId;\n\n\t\tLockShardDistributionMetadata(shardId, lockMode);\n\t}\n}\n\n\n/*\n * LockShardsInPlacementListMetadata takes locks on the metadata of all shards in\n * shardPlacementList to prevent concurrent placement changes.\n */\nvoid\nLockShardsInPlacementListMetadata(List *shardPlacementList, LOCKMODE lockMode)\n{\n\t/* lock shards in order of shard id to prevent deadlock */\n\tshardPlacementList =\n\t\tSortList(shardPlacementList, CompareShardPlacementsByShardId);\n\n\tGroupShardPlacement *placement = NULL;\n\tforeach_declared_ptr(placement, shardPlacementList)\n\t{\n\t\tint64 shardId = placement->shardId;\n\n\t\tLockShardDistributionMetadata(shardId, lockMode);\n\t}\n}\n\n\n/*\n * SerializeNonCommutativeWrites acquires the required locks to prevent concurrent\n * writes on the given shards.\n *\n * If the modified shard is a reference table's shard and the cluster is an MX\n * cluster we need to get shard resource lock on the first worker node to\n * prevent divergence possibility between placements of the reference table.\n *\n * In other workers, by acquiring a lock on the first worker, we're serializing\n * non-commutative modifications to a reference table. If the node executing the\n * command is the first worker, defined via IsFirstWorker(), we skip acquiring\n * the lock remotely to avoid an extra round-trip and/or self-deadlocks.\n *\n * Finally, if we're not dealing with reference tables on MX cluster, we'll\n * always acquire the lock with LockShardListResources() call.\n */\nvoid\nSerializeNonCommutativeWrites(List *shardIntervalList, LOCKMODE lockMode)\n{\n\tif (shardIntervalList == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tList *replicatedShardList = NIL;\n\tbool anyTableReplicated = AnyTableReplicated(shardIntervalList, &replicatedShardList);\n\n\t/*\n\t * Acquire locks on the modified table.\n\t * If the table is replicated, the locks are first acquired on the first worker node then locally.\n\t * But if we're already on the first worker, acquiring on the first worker node and locally are the same operation.\n\t * So we only acquire locally in that case.\n\t */\n\tif (anyTableReplicated && ClusterHasKnownMetadataWorkers() && !IsFirstWorkerNode())\n\t{\n\t\tLockShardListResourcesOnFirstWorker(lockMode, replicatedShardList);\n\t}\n\tLockShardListResources(shardIntervalList, lockMode);\n\n\t/*\n\t * Next, acquire locks on the reference tables that are referenced by a foreign key if there are any.\n\t * Note that LockReferencedReferenceShardResources() first acquires locks on the first worker,\n\t * then locally.\n\t */\n\tif (anyTableReplicated)\n\t{\n\t\tShardInterval *firstShardInterval =\n\t\t\t(ShardInterval *) linitial(replicatedShardList);\n\t\tif (ReferenceTableShardId(firstShardInterval->shardId))\n\t\t{\n\t\t\t/*\n\t\t\t * Referenced tables can cascade their changes to this table, and we\n\t\t\t * want to serialize changes to keep different replicas consistent.\n\t\t\t *\n\t\t\t * We currently only support foreign keys to reference tables, which are\n\t\t\t * single shard. So, getting the first shard should be sufficient here.\n\t\t\t */\n\t\t\tLockReferencedReferenceShardResources(firstShardInterval->shardId, lockMode);\n\t\t}\n\t}\n}\n\n\n/*\n * AnyTableReplicated iterates on the shard list and returns true\n * if any of the shard is a replicated table. We qualify replicated\n * tables as any reference table or any distributed table with\n * replication factor > 1.\n *\n * If the optional replicatedShardIntervalList is passed, the function\n * fills it with the replicated shard intervals.\n */\nstatic bool\nAnyTableReplicated(List *shardIntervalList, List **replicatedShardIntervalList)\n{\n\tList *localList = NIL;\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tint64 shardId = shardInterval->shardId;\n\n\t\tOid relationId = RelationIdForShard(shardId);\n\t\tif (ReferenceTableShardId(shardId))\n\t\t{\n\t\t\tlocalList =\n\t\t\t\tlappend(localList, LoadShardInterval(shardId));\n\t\t}\n\t\telse if (!SingleReplicatedTable(relationId))\n\t\t{\n\t\t\tlocalList =\n\t\t\t\tlappend(localList, LoadShardInterval(shardId));\n\t\t}\n\t}\n\n\tif (replicatedShardIntervalList != NULL)\n\t{\n\t\t*replicatedShardIntervalList = localList;\n\t}\n\n\treturn list_length(localList) > 0;\n}\n\n\n/*\n * LockShardListResources takes locks on all shards in shardIntervalList to\n * prevent concurrent DML statements on those shards.\n */\nstatic void\nLockShardListResources(List *shardIntervalList, LOCKMODE lockMode)\n{\n\t/* lock shards in order of shard id to prevent deadlock */\n\tshardIntervalList = SortList(shardIntervalList, CompareShardIntervalsById);\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tint64 shardId = shardInterval->shardId;\n\n\t\tLockShardResource(shardId, lockMode);\n\t}\n}\n\n\n/*\n * LockRelationShardResources takes locks on all shards in a list of RelationShards\n * to prevent concurrent DML statements on those shards.\n */\nvoid\nLockRelationShardResources(List *relationShardList, LOCKMODE lockMode)\n{\n\tif (relationShardList == NIL)\n\t{\n\t\treturn;\n\t}\n\n\tList *shardIntervalList = NIL;\n\tRelationShard *relationShard = NULL;\n\tforeach_declared_ptr(relationShard, relationShardList)\n\t{\n\t\tuint64 shardId = relationShard->shardId;\n\n\t\tShardInterval *shardInterval = LoadShardInterval(shardId);\n\n\t\tshardIntervalList = lappend(shardIntervalList, shardInterval);\n\t}\n\n\t/* lock shards in a consistent order to prevent deadlock */\n\tshardIntervalList = SortList(shardIntervalList, CompareShardIntervalsById);\n\tSerializeNonCommutativeWrites(shardIntervalList, lockMode);\n}\n\n\n/*\n * LockParentShardResourceIfPartition checks whether the given shard belongs\n * to a partition. If it does, LockParentShardResourceIfPartition acquires a\n * shard resource lock on the colocated shard of the parent table.\n */\nvoid\nLockParentShardResourceIfPartition(List *shardIntervalList, LOCKMODE lockMode)\n{\n\tList *parentShardIntervalList = NIL;\n\n\tShardInterval *shardInterval = NULL;\n\tforeach_declared_ptr(shardInterval, shardIntervalList)\n\t{\n\t\tOid relationId = shardInterval->relationId;\n\n\t\tif (PartitionTable(relationId))\n\t\t{\n\t\t\tint shardIndex = ShardIndex(shardInterval);\n\t\t\tOid parentRelationId = PartitionParentOid(relationId);\n\t\t\tuint64 parentShardId = ColocatedShardIdInRelation(parentRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardIndex);\n\n\t\t\tShardInterval *parentShardInterval = LoadShardInterval(parentShardId);\n\t\t\tparentShardIntervalList = lappend(parentShardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t  parentShardInterval);\n\t\t}\n\t}\n\n\tLockShardListResources(parentShardIntervalList, lockMode);\n}\n\n\n/*\n * LockModeTextToLockMode gets a lockMode name and returns its corresponding LOCKMODE.\n * The function errors out if the input lock mode isn't defined in the PostgreSQL's\n * explicit locking table.\n */\nLOCKMODE\nLockModeTextToLockMode(const char *lockModeName)\n{\n\tLOCKMODE lockMode = -1;\n\n\tfor (int lockIndex = 0; lockIndex < lock_mode_to_string_map_count; lockIndex++)\n\t{\n\t\tconst struct LockModeToStringType *lockMap = lockmode_to_string_map + lockIndex;\n\t\tif (pg_strncasecmp(lockMap->name, lockModeName, NAMEDATALEN) == 0)\n\t\t{\n\t\t\tlockMode = lockMap->lockMode;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* we could not find the lock mode we are looking for */\n\tif (lockMode == -1)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_LOCK_NOT_AVAILABLE),\n\t\t\t\t errmsg(\"unknown lock mode: %s\", lockModeName)));\n\t}\n\n\treturn lockMode;\n}\n\n\n/*\n * LockModeToLockModeText gets a lockMode enum and returns its corresponding text\n * representation.\n * The function errors out if the input lock mode isn't defined in the PostgreSQL's\n * explicit locking table.\n */\nconst char *\nLockModeToLockModeText(LOCKMODE lockMode)\n{\n\tconst char *lockModeText = NULL;\n\n\tfor (int lockIndex = 0; lockIndex < lock_mode_to_string_map_count; lockIndex++)\n\t{\n\t\tconst struct LockModeToStringType *lockMap = lockmode_to_string_map + lockIndex;\n\t\tif (lockMode == lockMap->lockMode)\n\t\t{\n\t\t\tlockModeText = lockMap->name;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* we could not find the lock mode we are looking for */\n\tif (lockModeText == NULL)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_LOCK_NOT_AVAILABLE),\n\t\t\t\t errmsg(\"unknown lock mode enum: %d\", (int) lockMode)));\n\t}\n\n\treturn lockModeText;\n}\n\n\n/*\n * lock_relation_if_exists gets a relation name and lock mode\n * and returns true if the relation exists and can be locked with\n * the given lock mode. If the relation doesn't exists, the function\n * return false.\n *\n * The relation name should be qualified with the schema name.\n *\n * The function errors out if the lockmode isn't defined in the PostgreSQL's\n * explicit locking table.\n */\nDatum\nlock_relation_if_exists(PG_FUNCTION_ARGS)\n{\n\ttext *relationName = PG_GETARG_TEXT_P(0);\n\ttext *lockModeText = PG_GETARG_TEXT_P(1);\n\tchar *lockModeCString = text_to_cstring(lockModeText);\n\n\t/* get the lock mode */\n\tLOCKMODE lockMode = LockModeTextToLockMode(lockModeCString);\n\n\t/* resolve relationId from passed in schema and relation name */\n\tList *relationNameList = textToQualifiedNameList(relationName);\n\tRangeVar *relation = makeRangeVarFromNameList(relationNameList);\n\n\t/* lock the relation with the lock mode */\n\tOid relationId = RangeVarGetRelidExtended(relation, lockMode, RVR_MISSING_OK,\n\t\t\t\t\t\t\t\t\t\t\t  CitusRangeVarCallbackForLockTable,\n\t\t\t\t\t\t\t\t\t\t\t  (void *) &lockMode);\n\tbool relationExists = OidIsValid(relationId);\n\tPG_RETURN_BOOL(relationExists);\n}\n\n\n/*\n * CitusRangeVarCallbackForLockTable is a callback for RangeVarGetRelidExtended used\n * to check whether the user has permission to lock a table in a particular mode.\n *\n * This function is a copy of RangeVarCallbackForLockTable in lockcmds.c adapted to\n * Citus code style.\n */\nstatic void\nCitusRangeVarCallbackForLockTable(const RangeVar *rangeVar, Oid relationId,\n\t\t\t\t\t\t\t\t  Oid oldRelationId, void *arg)\n{\n\tLOCKMODE lockmode = *(LOCKMODE *) arg;\n\n\tif (!OidIsValid(relationId))\n\t{\n\t\t/* table doesn't exist, so no permissions check */\n\t\treturn;\n\t}\n\n\t/* we only allow tables, views and foreign tables to be locked */\n\tif (!RegularTable(relationId) && !IsForeignTable(relationId))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE),\n\t\t\t\t\t\terrmsg(\"\\\"%s\\\" is not a table\", rangeVar->relname)));\n\t}\n\n\t/* check permissions */\n\tAclResult aclResult = CitusLockTableAclCheck(relationId, lockmode, GetUserId());\n\tif (aclResult != ACLCHECK_OK)\n\t{\n\t\taclcheck_error(aclResult, get_relkind_objtype(get_rel_relkind(relationId)),\n\t\t\t\t\t   rangeVar->relname);\n\t}\n}\n\n\n/*\n * CitusLockTableAclCheck checks whether a user has permission to lock a relation\n * in the given lock mode.\n *\n * This function is a copy of LockTableAclCheck in lockcmds.c adapted to Citus\n * code style.\n */\nstatic AclResult\nCitusLockTableAclCheck(Oid relationId, LOCKMODE lockmode, Oid userId)\n{\n\tAclMode aclMask;\n\n\t/* verify adequate privilege */\n\tif (lockmode == AccessShareLock)\n\t{\n\t\taclMask = ACL_SELECT;\n\t}\n\telse if (lockmode == RowExclusiveLock)\n\t{\n\t\taclMask = ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE;\n\t}\n\telse\n\t{\n\t\taclMask = ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE;\n\t}\n\n\tAclResult aclResult = pg_class_aclcheck(relationId, userId, aclMask);\n\n\treturn aclResult;\n}\n\n\n/*\n * EnsureCanAcquireLock checks if currect user has the permissions\n * to acquire a lock on the table and throws an error if the user does\n * not have the permissions\n */\nstatic void\nEnsureCanAcquireLock(Oid relationId, LOCKMODE lockMode)\n{\n\tAclResult aclResult = CitusLockTableAclCheck(relationId, lockMode,\n\t\t\t\t\t\t\t\t\t\t\t\t GetUserId());\n\tif (aclResult != ACLCHECK_OK)\n\t{\n\t\taclcheck_error(aclResult,\n\t\t\t\t\t   get_relkind_objtype(get_rel_relkind(relationId)),\n\t\t\t\t\t   get_rel_name(relationId));\n\t}\n}\n\n\n/*\n * CreateLockTerminationString creates a string that can be appended to the\n * end of a partial lock command to properly terminate the command\n */\nstatic const char *\nCreateLockTerminationString(const char *lockModeText, bool nowait)\n{\n\tStringInfo lockTerminationStringInfo = makeStringInfo();\n\tappendStringInfo(lockTerminationStringInfo, nowait ? \" IN %s MODE NOWAIT;\\n\" :\n\t\t\t\t\t \" IN %s MODE;\\n\", lockModeText);\n\treturn lockTerminationStringInfo->data;\n}\n\n\n/*\n * FinishLockCommandIfNecessary appends the lock termination string if the lock command is partial.\n * Sets the partialLockCommand flag to false\n */\nstatic void\nFinishLockCommandIfNecessary(StringInfo lockCommand, const char *lockTerminationString,\n\t\t\t\t\t\t\t bool *partialLockCommand)\n{\n\tif (*partialLockCommand)\n\t{\n\t\tappendStringInfo(lockCommand, \"%s\", lockTerminationString);\n\t}\n\n\t*partialLockCommand = false;\n}\n\n\n/*\n * LockRelationRecordListMember checks if a relation id is present in the\n * LockRelationRecord list\n */\nstatic bool\nLockRelationRecordListMember(List *lockRelationRecordList, Oid relationId)\n{\n\tLockRelationRecord *record = NULL;\n\tforeach_declared_ptr(record, lockRelationRecordList)\n\t{\n\t\tif (record->relationId == relationId)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * MakeLockRelationRecord makes a LockRelationRecord using the relation oid\n * and the inh boolean while properly allocating the structure\n */\nstatic LockRelationRecord *\nMakeLockRelationRecord(Oid relationId, bool inh)\n{\n\tLockRelationRecord *lockRelationRecord = palloc(sizeof(LockRelationRecord));\n\tlockRelationRecord->relationId = relationId;\n\tlockRelationRecord->inh = inh;\n\treturn lockRelationRecord;\n}\n\n\n/*\n * ConcatLockRelationRecordList concats a list of LockRelationRecord with\n * another list of LockRelationRecord created from a list of relation oid-s\n * which are not present in the first list and an inh bool which will be\n * applied across all LockRelationRecords\n */\nstatic List *\nConcatLockRelationRecordList(List *lockRelationRecordList, List *relationOidList, bool\n\t\t\t\t\t\t\t inh)\n{\n\tList *constructedList = NIL;\n\n\tOid relationId = InvalidOid;\n\tforeach_declared_oid(relationId, relationOidList)\n\t{\n\t\tif (!LockRelationRecordListMember(lockRelationRecordList, relationId))\n\t\t{\n\t\t\tLockRelationRecord *record = MakeLockRelationRecord(relationId, inh);\n\t\t\tconstructedList = lappend(constructedList, (void *) record);\n\t\t}\n\t}\n\n\treturn list_concat(lockRelationRecordList, constructedList);\n}\n\n\n/*\n * AcquireDistributedLockOnRelations_Internal acquire a distributed lock on worker nodes\n * for given list of relations ids. Worker node list is sorted so that the lock\n * is acquired in the same order regardless of which node it was run on.\n *\n * A nowait flag is used to require the locks to be available immediately\n * and if that is not the case, an error will be thrown\n *\n * Notice that no validation or filtering is done on the relationIds, that is the responsibility\n * of this function's caller.\n */\nstatic void\nAcquireDistributedLockOnRelations_Internal(List *lockRelationRecordList,\n\t\t\t\t\t\t\t\t\t\t   LOCKMODE lockMode, bool nowait)\n{\n\tconst char *lockModeText = LockModeToLockModeText(lockMode);\n\n\tUseCoordinatedTransaction();\n\n\tStringInfo lockRelationsCommand = makeStringInfo();\n\tappendStringInfo(lockRelationsCommand, \"%s;\\n\", DISABLE_DDL_PROPAGATION);\n\n\t/*\n\t * In the following loop, when there are foreign tables, we need to switch from\n\t * LOCK * statement to lock_relation_if_exists() and vice versa since pg\n\t * does not support using LOCK on foreign tables.\n\t */\n\tbool startedLockCommand = false;\n\n\t/* create a lock termination string used to terminate a partial lock command */\n\tconst char *lockTerminationString = CreateLockTerminationString(lockModeText, nowait);\n\n\tint lockedRelations = 0;\n\tLockRelationRecord *lockRelationRecord;\n\tforeach_declared_ptr(lockRelationRecord, lockRelationRecordList)\n\t{\n\t\tOid relationId = lockRelationRecord->relationId;\n\t\tbool lockDescendants = lockRelationRecord->inh;\n\t\tchar *qualifiedRelationName = generate_qualified_relation_name(relationId);\n\n\t\t/*\n\t\t * As of pg14 we cannot use LOCK to lock a FOREIGN TABLE\n\t\t * so we have to use the lock_relation_if_exist udf\n\t\t */\n\t\tif (get_rel_relkind(relationId) == RELKIND_FOREIGN_TABLE)\n\t\t{\n\t\t\tFinishLockCommandIfNecessary(lockRelationsCommand, lockTerminationString,\n\t\t\t\t\t\t\t\t\t\t &startedLockCommand);\n\n\t\t\t/*\n\t\t\t * The user should not be able to trigger this codepath\n\t\t\t * with nowait = true or lockDescendants = false since the\n\t\t\t * only way to do that is calling LOCK * IN * MODE NOWAIT;\n\t\t\t * and LOCK ONLY * IN * MODE; respectively but foreign tables\n\t\t\t * cannot be locked with LOCK command as of pg14\n\t\t\t */\n\t\t\tAssert(nowait == false);\n\t\t\tAssert(lockDescendants == true);\n\n\t\t\t/* use lock_relation_if_exits to lock foreign table */\n\t\t\tappendStringInfo(lockRelationsCommand, LOCK_RELATION_IF_EXISTS,\n\t\t\t\t\t\t\t quote_literal_cstr(qualifiedRelationName),\n\t\t\t\t\t\t\t quote_literal_cstr(lockModeText));\n\t\t\tappendStringInfoChar(lockRelationsCommand, '\\n');\n\t\t}\n\t\telse if (startedLockCommand)\n\t\t{\n\t\t\t/* append relation to partial lock statement */\n\t\t\tappendStringInfo(lockRelationsCommand, \",%s%s\",\n\t\t\t\t\t\t\t lockDescendants ? \" \" : \" ONLY \",\n\t\t\t\t\t\t\t qualifiedRelationName);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* start a new partial lock statement */\n\t\t\tappendStringInfo(lockRelationsCommand, \"LOCK%s%s\",\n\t\t\t\t\t\t\t lockDescendants ? \" \" : \" ONLY \",\n\t\t\t\t\t\t\t qualifiedRelationName);\n\t\t\tstartedLockCommand = true;\n\t\t}\n\n\t\tlockedRelations++;\n\t}\n\n\tif (lockedRelations == 0)\n\t{\n\t\treturn;\n\t}\n\n\tFinishLockCommandIfNecessary(lockRelationsCommand, lockTerminationString,\n\t\t\t\t\t\t\t\t &startedLockCommand);\n\n\tappendStringInfo(lockRelationsCommand, ENABLE_DDL_PROPAGATION);\n\tchar *lockCommand = lockRelationsCommand->data;\n\n\tList *workerNodeList = TargetWorkerSetNodeList(METADATA_NODES, NoLock);\n\n\t/*\n\t * We want to acquire locks in the same order across the nodes.\n\t * Although relation ids may change, their ordering will not.\n\t */\n\tworkerNodeList = SortList(workerNodeList, CompareWorkerNodes);\n\n\tint32 localGroupId = GetLocalGroupId();\n\n\tWorkerNode *workerNode = NULL;\n\tconst char *currentUser = CurrentUserName();\n\tforeach_declared_ptr(workerNode, workerNodeList)\n\t{\n\t\t/* if local node is one of the targets, acquire the lock locally */\n\t\tif (workerNode->groupId == localGroupId)\n\t\t{\n\t\t\tExecuteUtilityCommand(lockCommand);\n\t\t\tcontinue;\n\t\t}\n\n\t\tSendMetadataCommandListToWorkerListInCoordinatedTransaction(list_make1(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerNode),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcurrentUser,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlist_make1(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlockCommand));\n\t}\n}\n\n\n/*\n * AcquireDistributedLockOnRelations filters relations before passing them to\n * AcquireDistributedLockOnRelations_Internal to acquire the locks.\n *\n * Only tables, views, and foreign tables can be locked with this function. Other relations\n * will cause an error.\n *\n * Skips non distributed relations.\n * configs parameter is used to configure what relation will be locked and if the lock\n * should throw an error if it cannot be acquired immediately\n */\nvoid\nAcquireDistributedLockOnRelations(List *relationList, LOCKMODE lockMode, uint32 configs)\n{\n\tif (!ClusterHasKnownMetadataWorkers() || !EnableMetadataSync || !EnableDDLPropagation)\n\t{\n\t\treturn;\n\t}\n\n\t/* used to store the relations that will be locked */\n\tList *distributedRelationRecordsList = NIL;\n\n\tbool nowait = (configs & DIST_LOCK_NOWAIT) > 0;\n\n\tRangeVar *rangeVar = NULL;\n\tforeach_declared_ptr(rangeVar, relationList)\n\t{\n\t\tOid relationId = RangeVarGetRelid(rangeVar, NoLock, false);\n\n\t\tLockRelationRecord *lockRelationRecord = MakeLockRelationRecord(relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trangeVar->inh);\n\n\t\t/*\n\t\t * Note that allowing the user to lock shards could lead to\n\t\t * distributed deadlocks due to shards not being locked when\n\t\t * a distributed table is locked.\n\t\t * However, because citus.enable_manual_changes_to_shards\n\t\t * is a guc which is not visible by default, whoever is using this\n\t\t * guc will hopefully know what they're doing and avoid such scenarios.\n\t\t */\n\t\tErrorIfIllegallyChangingKnownShard(relationId);\n\n\t\t/*\n\t\t * we want to prevent under privileged users to trigger establishing connections,\n\t\t * that's why we have this additional check. Otherwise, we'd get the errors as\n\t\t * soon as we execute the command on any node\n\t\t */\n\t\tEnsureCanAcquireLock(relationId, lockMode);\n\n\t\tbool isView = get_rel_relkind(relationId) == RELKIND_VIEW;\n\t\tif ((isView && !IsViewDistributed(relationId)) ||\n\t\t\t(!isView && !ShouldSyncTableMetadata(relationId)))\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!LockRelationRecordListMember(distributedRelationRecordsList, relationId))\n\t\t{\n\t\t\tdistributedRelationRecordsList = lappend(distributedRelationRecordsList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t (void *) lockRelationRecord);\n\t\t}\n\n\t\tchar relkind = get_rel_relkind(relationId);\n\t\tbool relationCanBeReferenced = (relkind == RELKIND_RELATION ||\n\t\t\t\t\t\t\t\t\t\trelkind == RELKIND_PARTITIONED_TABLE);\n\t\tif (relationCanBeReferenced &&\n\t\t\t(configs & DIST_LOCK_REFERENCING_TABLES) > 0)\n\t\t{\n\t\t\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId);\n\t\t\tAssert(cacheEntry != NULL);\n\n\t\t\tList *referencingTableList = cacheEntry->referencingRelationsViaForeignKey;\n\n\t\t\t/* remove the relations which should not be synced */\n\t\t\treferencingTableList = list_filter_oid(referencingTableList,\n\t\t\t\t\t\t\t\t\t\t\t\t   &ShouldSyncTableMetadata);\n\n\t\t\tdistributedRelationRecordsList = ConcatLockRelationRecordList(\n\t\t\t\tdistributedRelationRecordsList, referencingTableList, true);\n\t\t}\n\t}\n\n\tif (distributedRelationRecordsList != NIL)\n\t{\n\t\tif (!IsCoordinator() && !CoordinatorAddedAsWorkerNode() &&\n\t\t\t!EnableAcquiringUnsafeLockFromWorkers)\n\t\t{\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errmsg(\n\t\t\t\t\t\t \"Cannot acquire a distributed lock from a worker node since the \"\n\t\t\t\t\t\t \"coordinator is not in the metadata.\"),\n\t\t\t\t\t errhint(\n\t\t\t\t\t\t \"Either run this command on the coordinator or add the coordinator \"\n\t\t\t\t\t\t \"in the metadata by using: SELECT citus_set_coordinator_host('<hostname>', <port>);\\n\"\n\t\t\t\t\t\t \"Alternatively, though it is not recommended, you can allow this command by running: \"\n\t\t\t\t\t\t \"SET citus.allow_unsafe_locks_from_workers TO 'on';\")));\n\t\t}\n\n\t\tAcquireDistributedLockOnRelations_Internal(distributedRelationRecordsList,\n\t\t\t\t\t\t\t\t\t\t\t\t   lockMode, nowait);\n\t}\n}\n\n\n/**\n * PreprocessLockStatement makes sure that the lock is allowed\n * before calling AcquireDistributedLockOnRelations on the locked tables/views\n * with flags DIST_LOCK_VIEWS_RECUR and DIST_LOCK_NOWAIT if nowait is true for\n * the lock statement\n */\nvoid\nPreprocessLockStatement(LockStmt *stmt, ProcessUtilityContext context)\n{\n\tbool isTopLevel = context == PROCESS_UTILITY_TOPLEVEL;\n\tRequireTransactionBlock(isTopLevel, \"LOCK TABLE\");\n\n\tuint32 nowaitFlag = stmt->nowait ? DIST_LOCK_NOWAIT : 0;\n\tAcquireDistributedLockOnRelations(stmt->relations, stmt->mode, nowaitFlag);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/role.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * role.c\n *\n * Utilities for ALTER ROLE statments.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"tcop/dest.h\"\n#include \"tcop/utility.h\"\n#include \"utils/builtins.h\"\n\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/worker_protocol.h\"\n\nPG_FUNCTION_INFO_V1(alter_role_if_exists);\nPG_FUNCTION_INFO_V1(worker_create_or_alter_role);\n\n/*\n * alter_role_if_exists checks if the role, whose name is given\n * in the first parameter exists and then runs the query, which is the second\n * parameter. This UDF is particularly used for ALTER ROLE queries, how ever it\n * can run any other query too.\n */\nDatum\nalter_role_if_exists(PG_FUNCTION_ARGS)\n{\n\ttext *rolenameText = PG_GETARG_TEXT_P(0);\n\tconst char *rolename = text_to_cstring(rolenameText);\n\ttext *utilityQueryText = PG_GETARG_TEXT_P(1);\n\tconst char *utilityQuery = text_to_cstring(utilityQueryText);\n\n\tif (get_role_oid(rolename, true) == InvalidOid)\n\t{\n\t\tPG_RETURN_BOOL(false);\n\t}\n\n\tNode *parseTree = ParseTreeNode(utilityQuery);\n\n\tProcessUtilityParseTree(parseTree, utilityQuery, PROCESS_UTILITY_QUERY, NULL,\n\t\t\t\t\t\t\tNone_Receiver, NULL);\n\n\tPG_RETURN_BOOL(true);\n}\n\n\n/*\n * worker_create_or_alter_role(\n *   role_name text,\n *   create_role_utility_query text,\n *   alter_role_utility_query text)\n *\n * This UDF checks if the role, whose name is given in role_name exists.\n *\n * If the role does not exist it will run the query provided in create_role_utility_query\n * which is expected to be a CreateRoleStmt. If a different statement is provided the call\n * will raise an error,\n *\n * If the role does exist it will run the query provided in alter_role_utility_query to\n * change the existing user in such a way that it is compatible with the user on the\n * coordinator. This query is expected to be a AlterRoleStmt, if a different statement is\n * provided the function will raise an error.\n *\n * For both queries a NULL value can be passed to omit the execution of that condition.\n *\n * The function returns true if a command has been successfully executed, false if no\n * command was executed, and raises an error if something went wrong.\n */\nDatum\nworker_create_or_alter_role(PG_FUNCTION_ARGS)\n{\n\tif (PG_ARGISNULL(0))\n\t{\n\t\tereport(ERROR, (errmsg(\"role name cannot be NULL\")));\n\t}\n\n\ttext *rolenameText = PG_GETARG_TEXT_P(0);\n\tconst char *rolename = text_to_cstring(rolenameText);\n\n\tif (get_role_oid(rolename, true) == InvalidOid)\n\t{\n\t\tif (PG_ARGISNULL(1))\n\t\t{\n\t\t\tPG_RETURN_BOOL(false);\n\t\t}\n\n\t\ttext *createRoleUtilityQueryText = PG_GETARG_TEXT_P(1);\n\t\tconst char *createRoleUtilityQuery = text_to_cstring(createRoleUtilityQueryText);\n\t\tNode *parseTree = ParseTreeNode(createRoleUtilityQuery);\n\n\t\tif (nodeTag(parseTree) != T_CreateRoleStmt)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot create role\"),\n\t\t\t\t\t\t\terrdetail(\"the role %s does not exist \"\n\t\t\t\t\t\t\t\t\t  \"but %s is not a correct CREATE ROLE query\",\n\t\t\t\t\t\t\t\t\t  quote_literal_cstr(rolename),\n\t\t\t\t\t\t\t\t\t  quote_literal_cstr(createRoleUtilityQuery))));\n\t\t}\n\n\t\tProcessUtilityParseTree(parseTree,\n\t\t\t\t\t\t\t\tcreateRoleUtilityQuery,\n\t\t\t\t\t\t\t\tPROCESS_UTILITY_QUERY,\n\t\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\t\tNone_Receiver, NULL);\n\n\t\tPG_RETURN_BOOL(true);\n\t}\n\telse\n\t{\n\t\tif (PG_ARGISNULL(2))\n\t\t{\n\t\t\tPG_RETURN_BOOL(false);\n\t\t}\n\n\t\ttext *alterRoleUtilityQueryText = PG_GETARG_TEXT_P(2);\n\t\tconst char *alterRoleUtilityQuery = text_to_cstring(alterRoleUtilityQueryText);\n\t\tNode *parseTree = ParseTreeNode(alterRoleUtilityQuery);\n\n\t\tif (nodeTag(parseTree) != T_AlterRoleStmt)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"cannot alter role\"),\n\t\t\t\t\t\t\terrdetail(\"the role %s exists \"\n\t\t\t\t\t\t\t\t\t  \"but %s is not a correct alter ROLE query\",\n\t\t\t\t\t\t\t\t\t  quote_literal_cstr(rolename),\n\t\t\t\t\t\t\t\t\t  quote_literal_cstr(alterRoleUtilityQuery))));\n\t\t}\n\n\t\tProcessUtilityParseTree(parseTree,\n\t\t\t\t\t\t\t\talterRoleUtilityQuery,\n\t\t\t\t\t\t\t\tPROCESS_UTILITY_QUERY,\n\t\t\t\t\t\t\t\tNULL,\n\t\t\t\t\t\t\t\tNone_Receiver, NULL);\n\n\t\tPG_RETURN_BOOL(true);\n\t}\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/shard_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_utils.c\n *\n * This file contains functions to perform useful operations on shards.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"utils/builtins.h\"\n#include \"utils/fmgrprotos.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/shard_utils.h\"\n\nstatic int GetLargestShardId(void);\n\n/*\n * GetTableLocalShardOid returns the oid of the shard from the given distributed\n * relation with the shardId.\n */\nOid\nGetTableLocalShardOid(Oid citusTableOid, uint64 shardId)\n{\n\tconst char *citusTableName = get_rel_name(citusTableOid);\n\n\tAssert(citusTableName != NULL);\n\n\t/* construct shard relation name */\n\tchar *shardRelationName = pstrdup(citusTableName);\n\tAppendShardIdToName(&shardRelationName, shardId);\n\n\tOid citusTableSchemaOid = get_rel_namespace(citusTableOid);\n\n\tOid shardRelationOid = get_relname_relid(shardRelationName, citusTableSchemaOid);\n\n\treturn shardRelationOid;\n}\n\n\n/*\n * GetLongestShardName is a utility function that returns the name of the shard of a\n * table that has the longest name in terms of number of characters.\n *\n * Both the Oid and name of the table are required so we can create longest shard names\n * after a RENAME.\n */\nchar *\nGetLongestShardName(Oid citusTableOid, char *finalRelationName)\n{\n\tchar *longestShardName = pstrdup(finalRelationName);\n\tShardInterval *shardInterval = LoadShardIntervalWithLongestShardName(citusTableOid);\n\tAppendShardIdToName(&longestShardName, shardInterval->shardId);\n\n\treturn longestShardName;\n}\n\n\n/*\n * GetLongestShardNameForLocalPartition is a utility function that creates a hypothetical shard\n * name for a partition table that is not distributed yet.\n */\nchar *\nGetLongestShardNameForLocalPartition(Oid parentTableOid, char *partitionRelationName)\n{\n\tchar *longestShardName = pstrdup(partitionRelationName);\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(parentTableOid);\n\tint shardIntervalCount = cacheEntry->shardIntervalArrayLength;\n\tint newShardId = GetLargestShardId() + shardIntervalCount;\n\tAppendShardIdToName(&longestShardName, newShardId);\n\n\treturn longestShardName;\n}\n\n\n/*\n * GetLargestShardId returns the biggest shard id, and returns a 10^6 in case of failure\n * to get the last value from the sequence.\n */\nint\nGetLargestShardId()\n{\n\tOid savedUserId = InvalidOid;\n\tint savedSecurityContext = 0;\n\n\tGetUserIdAndSecContext(&savedUserId, &savedSecurityContext);\n\tSetUserIdAndSecContext(CitusExtensionOwner(), SECURITY_LOCAL_USERID_CHANGE);\n\n\ttext *sequenceName = cstring_to_text(SHARDID_SEQUENCE_NAME);\n\tOid sequenceId = ResolveRelationId(sequenceName, false);\n\tDatum sequenceIdDatum = ObjectIdGetDatum(sequenceId);\n\n\tvolatile int64 largestShardId = 0;\n\n\t/*\n\t * pg_sequence_last_value() returns NULL if the sequence value is not yet used.\n\t * DirectFunctionCall1() gives an ERROR message on NULL return values, and that's why we\n\t * need a PG_TRY block.\n\t */\n\tPG_TRY();\n\t{\n\t\tDatum lastShardIdDatum = DirectFunctionCall1(pg_sequence_last_value,\n\t\t\t\t\t\t\t\t\t\t\t\t\t sequenceIdDatum);\n\t\tlargestShardId = DatumGetInt64(lastShardIdDatum);\n\t}\n\tPG_CATCH();\n\t{\n\t\t/* assume that we have a shardId with 7 digits */\n\t\tlargestShardId = 1000000;\n\t}\n\tPG_END_TRY();\n\n\tSetUserIdAndSecContext(savedUserId, savedSecurityContext);\n\n\treturn largestShardId;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/shardinterval_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * shardinterval_utils.c\n *\n * This file contains functions to perform useful operations on shard intervals.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#include \"postgres.h\"\n\n#include \"stdint.h\"\n\n#include \"access/nbtree.h\"\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_type.h\"\n#include \"utils/catcache.h\"\n#include \"utils/memutils.h\"\n\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/shard_pruning.h\"\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/*\n * SortedShardIntervalArray sorts the input shardIntervalArray. Shard intervals with\n * no min/max values are placed at the end of the array.\n */\nShardInterval **\nSortShardIntervalArray(ShardInterval **shardIntervalArray, int shardCount,\n\t\t\t\t\t   Oid collation, FmgrInfo *shardIntervalSortCompareFunction)\n{\n\tSortShardIntervalContext sortContext = {\n\t\t.comparisonFunction = shardIntervalSortCompareFunction,\n\t\t.collation = collation\n\t};\n\n\t/* short cut if there are no shard intervals in the array */\n\tif (shardCount == 0)\n\t{\n\t\treturn shardIntervalArray;\n\t}\n\n\t/* if a shard doesn't have min/max values, it's placed in the end of the array */\n\tqsort_arg(shardIntervalArray, shardCount, sizeof(ShardInterval *),\n\t\t\t  (qsort_arg_comparator) CompareShardIntervals, (void *) &sortContext);\n\n\treturn shardIntervalArray;\n}\n\n\n/*\n * CompareShardIntervals acts as a helper function to compare two shard intervals\n * by their minimum values, using the value's type comparison function.\n *\n * If a shard interval does not have min/max value, it's treated as being greater\n * than the other.\n */\nint\nCompareShardIntervals(const void *leftElement, const void *rightElement,\n\t\t\t\t\t  SortShardIntervalContext *sortContext)\n{\n\tShardInterval *leftShardInterval = *((ShardInterval **) leftElement);\n\tShardInterval *rightShardInterval = *((ShardInterval **) rightElement);\n\tint comparisonResult = 0;\n\tbool leftHasNull = (!leftShardInterval->minValueExists ||\n\t\t\t\t\t\t!leftShardInterval->maxValueExists);\n\tbool rightHasNull = (!rightShardInterval->minValueExists ||\n\t\t\t\t\t\t !rightShardInterval->maxValueExists);\n\n\tAssert(sortContext->comparisonFunction != NULL);\n\n\tif (leftHasNull && rightHasNull)\n\t{\n\t\tcomparisonResult = 0;\n\t}\n\telse if (leftHasNull)\n\t{\n\t\tcomparisonResult = 1;\n\t}\n\telse if (rightHasNull)\n\t{\n\t\tcomparisonResult = -1;\n\t}\n\telse\n\t{\n\t\t/* if both shard interval have min/max values, calculate comparison result */\n\t\tDatum leftDatum = leftShardInterval->minValue;\n\t\tDatum rightDatum = rightShardInterval->minValue;\n\t\tDatum comparisonDatum = FunctionCall2Coll(sortContext->comparisonFunction,\n\t\t\t\t\t\t\t\t\t\t\t\t  sortContext->collation, leftDatum,\n\t\t\t\t\t\t\t\t\t\t\t\t  rightDatum);\n\t\tcomparisonResult = DatumGetInt32(comparisonDatum);\n\t}\n\n\t/* Two different shards should never be equal */\n\tif (comparisonResult == 0)\n\t{\n\t\treturn CompareShardIntervalsById(leftElement, rightElement);\n\t}\n\n\treturn comparisonResult;\n}\n\n\n/*\n * CompareShardIntervalsById is a comparison function for sort shard\n * intervals by their shard ID.\n */\nint\nCompareShardIntervalsById(const void *leftElement, const void *rightElement)\n{\n\tShardInterval *leftInterval = *((ShardInterval **) leftElement);\n\tShardInterval *rightInterval = *((ShardInterval **) rightElement);\n\tint64 leftShardId = leftInterval->shardId;\n\tint64 rightShardId = rightInterval->shardId;\n\n\t/* we compare 64-bit integers, instead of casting their difference to int */\n\tif (leftShardId > rightShardId)\n\t{\n\t\treturn 1;\n\t}\n\telse if (leftShardId < rightShardId)\n\t{\n\t\treturn -1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * CompareShardPlacementsByShardId is a comparison function for sorting shard\n * placement by their shard ID.\n */\nint\nCompareShardPlacementsByShardId(const void *leftElement, const void *rightElement)\n{\n\tGroupShardPlacement *left = *((GroupShardPlacement **) leftElement);\n\tGroupShardPlacement *right = *((GroupShardPlacement **) rightElement);\n\tint64 leftShardId = left->shardId;\n\tint64 rightShardId = right->shardId;\n\n\t/* we compare 64-bit integers, instead of casting their difference to int */\n\tif (leftShardId > rightShardId)\n\t{\n\t\treturn 1;\n\t}\n\telse if (leftShardId < rightShardId)\n\t{\n\t\treturn -1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * CompareRelationShards is a comparison function for sorting relation\n * to shard mappings by their relation ID and then shard ID.\n */\nint\nCompareRelationShards(const void *leftElement, const void *rightElement)\n{\n\tRelationShard *leftRelationShard = *((RelationShard **) leftElement);\n\tRelationShard *rightRelationShard = *((RelationShard **) rightElement);\n\tOid leftRelationId = leftRelationShard->relationId;\n\tOid rightRelationId = rightRelationShard->relationId;\n\tint64 leftShardId = leftRelationShard->shardId;\n\tint64 rightShardId = rightRelationShard->shardId;\n\n\tif (leftRelationId > rightRelationId)\n\t{\n\t\treturn 1;\n\t}\n\telse if (leftRelationId < rightRelationId)\n\t{\n\t\treturn -1;\n\t}\n\telse if (leftShardId > rightShardId)\n\t{\n\t\treturn 1;\n\t}\n\telse if (leftShardId < rightShardId)\n\t{\n\t\treturn -1;\n\t}\n\telse\n\t{\n\t\treturn 0;\n\t}\n}\n\n\n/*\n * ShardIndex finds the index of given shard in sorted shard interval array.\n *\n * For hash partitioned tables, it calculates hash value of a number in its\n * range (e.g. min value) and finds which shard should contain the hashed\n * value. For the tables that don't have a shard key, it simply returns 0.\n * For the other table types, the function errors out.\n */\nint\nShardIndex(ShardInterval *shardInterval)\n{\n\tint shardIndex = INVALID_SHARD_INDEX;\n\tOid distributedTableId = shardInterval->relationId;\n\tDatum shardMinValue = shardInterval->minValue;\n\n\tCitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(distributedTableId);\n\n\t/*\n\t * Note that, we can also support append and range distributed tables, but\n\t * currently it is not required.\n\t */\n\tif (!IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED) &&\n\t\tHasDistributionKeyCacheEntry(cacheEntry))\n\t{\n\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t\terrmsg(\"finding index of a given shard is only supported for \"\n\t\t\t\t\t\t\t   \"hash distributed tables, reference tables and local \"\n\t\t\t\t\t\t\t   \"tables that are added to citus metadata\")));\n\t}\n\n\t/* short-circuit for the tables that don't have a distribution key */\n\tif (!HasDistributionKeyCacheEntry(cacheEntry))\n\t{\n\t\t/*\n\t\t * Such tables have only a single shard, so the index is fixed to 0.\n\t\t */\n\t\tshardIndex = 0;\n\n\t\treturn shardIndex;\n\t}\n\n\tshardIndex = FindShardIntervalIndex(shardMinValue, cacheEntry);\n\n\treturn shardIndex;\n}\n\n\n/*\n * FindShardInterval finds a single shard interval in the cache for the\n * given partition column value. Note that reference tables do not have\n * partition columns, thus, pass partitionColumnValue and compareFunction\n * as NULL for them.\n */\nShardInterval *\nFindShardInterval(Datum partitionColumnValue, CitusTableCacheEntry *cacheEntry)\n{\n\tDatum searchedValue = partitionColumnValue;\n\n\tif (IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED))\n\t{\n\t\tsearchedValue = FunctionCall1Coll(cacheEntry->hashFunction,\n\t\t\t\t\t\t\t\t\t\t  cacheEntry->partitionColumn->varcollid,\n\t\t\t\t\t\t\t\t\t\t  partitionColumnValue);\n\t}\n\n\tint shardIndex = FindShardIntervalIndex(searchedValue, cacheEntry);\n\n\tif (shardIndex == INVALID_SHARD_INDEX)\n\t{\n\t\treturn NULL;\n\t}\n\n\treturn cacheEntry->sortedShardIntervalArray[shardIndex];\n}\n\n\n/*\n * FindShardIntervalIndex finds the index of the shard interval which covers\n * the searched value. Note that the searched value must be the hashed value\n * of the original value if the distribution method is hash.\n *\n * Note that, if the searched value can not be found for hash partitioned\n * tables, we error out (unless there are no shards, in which case\n * INVALID_SHARD_INDEX is returned). This should only happen if something is\n * terribly wrong, either metadata tables are corrupted or we have a bug\n * somewhere. Such as a hash function which returns a value not in the range\n * of [PG_INT32_MIN, PG_INT32_MAX] can fire this.\n */\nint\nFindShardIntervalIndex(Datum searchedValue, CitusTableCacheEntry *cacheEntry)\n{\n\tShardInterval **shardIntervalCache = cacheEntry->sortedShardIntervalArray;\n\tint shardCount = cacheEntry->shardIntervalArrayLength;\n\tFmgrInfo *compareFunction = cacheEntry->shardIntervalCompareFunction;\n\tbool useBinarySearch = (!IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED) ||\n\t\t\t\t\t\t\t!cacheEntry->hasUniformHashDistribution);\n\tint shardIndex = INVALID_SHARD_INDEX;\n\n\tif (shardCount == 0)\n\t{\n\t\treturn INVALID_SHARD_INDEX;\n\t}\n\n\tif (IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED))\n\t{\n\t\tif (useBinarySearch)\n\t\t{\n\t\t\tAssert(compareFunction != NULL);\n\n\t\t\tOid shardIntervalCollation = cacheEntry->partitionColumn->varcollid;\n\t\t\tshardIndex = SearchCachedShardInterval(searchedValue, shardIntervalCache,\n\t\t\t\t\t\t\t\t\t\t\t\t   shardCount, shardIntervalCollation,\n\t\t\t\t\t\t\t\t\t\t\t\t   compareFunction);\n\n\t\t\t/* we should always return a valid shard index for hash partitioned tables */\n\t\t\tif (shardIndex == INVALID_SHARD_INDEX)\n\t\t\t{\n\t\t\t\tereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),\n\t\t\t\t\t\t\t\terrmsg(\"cannot find shard interval\"),\n\t\t\t\t\t\t\t\terrdetail(\"Hash of the partition column value \"\n\t\t\t\t\t\t\t\t\t\t  \"does not fall into any shards.\")));\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint hashedValue = DatumGetInt32(searchedValue);\n\n\t\t\tshardIndex = CalculateUniformHashRangeIndex(hashedValue, shardCount);\n\t\t}\n\t}\n\telse if (!HasDistributionKeyCacheEntry(cacheEntry))\n\t{\n\t\t/* non-distributed tables have a single shard, all values mapped to that shard */\n\t\tAssert(shardCount == 1);\n\n\t\tshardIndex = 0;\n\t}\n\telse\n\t{\n\t\tAssert(compareFunction != NULL);\n\n\t\tOid shardIntervalCollation = cacheEntry->partitionColumn->varcollid;\n\t\tshardIndex = SearchCachedShardInterval(searchedValue, shardIntervalCache,\n\t\t\t\t\t\t\t\t\t\t\t   shardCount, shardIntervalCollation,\n\t\t\t\t\t\t\t\t\t\t\t   compareFunction);\n\t}\n\n\treturn shardIndex;\n}\n\n\n/*\n * SearchCachedShardInterval performs a binary search for a shard interval\n * matching a given partition column value and returns its index in the cached\n * array. If it can not find any shard interval with the given value, it returns\n * INVALID_SHARD_INDEX.\n *\n * TODO: Data re-partitioning logic (worker_partition_query_resul))\n * on the worker nodes relies on this function in order to be consistent\n * with shard pruning. Since the worker nodes don't have the metadata, a\n * synthetically generated ShardInterval ** is passed to this\n * function. The synthetic shard intervals contain only shardmin and shardmax\n * values. A proper implementation of this approach should be introducing an\n * intermediate data structure (e.g., ShardRange) on which this function\n * operates instead of operating shard intervals.\n */\nint\nSearchCachedShardInterval(Datum partitionColumnValue, ShardInterval **shardIntervalCache,\n\t\t\t\t\t\t  int shardCount, Oid shardIntervalCollation,\n\t\t\t\t\t\t  FmgrInfo *compareFunction)\n{\n\tint lowerBoundIndex = 0;\n\tint upperBoundIndex = shardCount;\n\n\twhile (lowerBoundIndex < upperBoundIndex)\n\t{\n\t\tint middleIndex = (lowerBoundIndex + upperBoundIndex) / 2;\n\n\t\tint minValueComparison = FunctionCall2Coll(compareFunction,\n\t\t\t\t\t\t\t\t\t\t\t\t   shardIntervalCollation,\n\t\t\t\t\t\t\t\t\t\t\t\t   partitionColumnValue,\n\t\t\t\t\t\t\t\t\t\t\t\t   shardIntervalCache[middleIndex]->\n\t\t\t\t\t\t\t\t\t\t\t\t   minValue);\n\n\t\tif (DatumGetInt32(minValueComparison) < 0)\n\t\t{\n\t\t\tupperBoundIndex = middleIndex;\n\t\t\tcontinue;\n\t\t}\n\n\t\tint maxValueComparison = FunctionCall2Coll(compareFunction,\n\t\t\t\t\t\t\t\t\t\t\t\t   shardIntervalCollation,\n\t\t\t\t\t\t\t\t\t\t\t\t   partitionColumnValue,\n\t\t\t\t\t\t\t\t\t\t\t\t   shardIntervalCache[middleIndex]->\n\t\t\t\t\t\t\t\t\t\t\t\t   maxValue);\n\n\t\tif (DatumGetInt32(maxValueComparison) <= 0)\n\t\t{\n\t\t\treturn middleIndex;\n\t\t}\n\n\t\tlowerBoundIndex = middleIndex + 1;\n\t}\n\n\treturn INVALID_SHARD_INDEX;\n}\n\n\n/*\n * CalculateUniformHashRangeIndex returns the index of the hash range in\n * which hashedValue falls, assuming shardCount uniform hash ranges.\n *\n * We use 64-bit integers to avoid overflow issues during arithmetic.\n *\n * NOTE: This function is ONLY for hash-distributed tables with uniform\n * hash ranges.\n */\nint\nCalculateUniformHashRangeIndex(int hashedValue, int shardCount)\n{\n\tint64 hashedValue64 = (int64) hashedValue;\n\n\t/* normalize to the 0-UINT32_MAX range */\n\tint64 normalizedHashValue = hashedValue64 - PG_INT32_MIN;\n\n\t/* size of each hash range */\n\tint64 hashRangeSize = HASH_TOKEN_COUNT / shardCount;\n\n\t/* index of hash range into which the hash value falls */\n\tint shardIndex = (int) (normalizedHashValue / hashRangeSize);\n\n\tif (shardIndex < 0 || shardIndex > shardCount)\n\t{\n\t\tereport(ERROR, (errmsg(\"bug: shard index %d out of bounds\", shardIndex)));\n\t}\n\n\t/*\n\t * If the shard count is not power of 2, the range of the last\n\t * shard becomes larger than others. For that extra piece of range,\n\t * we still need to use the last shard.\n\t */\n\tif (shardIndex == shardCount)\n\t{\n\t\tshardIndex = shardCount - 1;\n\t}\n\n\treturn shardIndex;\n}\n\n\n/*\n * SingleReplicatedTable checks whether all shards of a distributed table, do not have\n * more than one replica. If even one shard has more than one replica, this function\n * returns false, otherwise it returns true.\n */\nbool\nSingleReplicatedTable(Oid relationId)\n{\n\tList *shardList = LoadShardList(relationId);\n\tList *shardPlacementList = NIL;\n\n\t/* we could have append/range distributed tables without shards */\n\tif (list_length(shardList) == 0)\n\t{\n\t\treturn false;\n\t}\n\n\tuint64 *shardIdPointer = NULL;\n\tforeach_declared_ptr(shardIdPointer, shardList)\n\t{\n\t\tuint64 shardId = *shardIdPointer;\n\t\tshardPlacementList = ShardPlacementList(shardId);\n\n\t\tif (list_length(shardPlacementList) != 1)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/string_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * string_utils.c\n *\n * This file contains functions to perform useful operations on strings.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"distributed/relay_utility.h\"\n#include \"distributed/string_utils.h\"\n\n/*\n * ConvertIntToString returns the string version of given integer.\n */\nchar *\nConvertIntToString(int val)\n{\n\tStringInfo str = makeStringInfo();\n\n\tappendStringInfo(str, \"%d\", val);\n\n\treturn str->data;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/task_execution_utils.c",
    "content": "\n#include <math.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"commands/dbcommands.h\"\n#include \"common/hashfn.h\"\n#include \"storage/fd.h\"\n#include \"utils/builtins.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/timestamp.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/deparse_shard_query.h\"\n#include \"distributed/distributed_execution_locks.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/subplan_execution.h\"\n#include \"distributed/task_execution_utils.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n/* TaskMapKey is used as a key in task hash */\ntypedef struct TaskMapKey\n{\n\tTaskType taskType;\n\tuint32 taskId;\n\tuint64 jobId;\n} TaskMapKey;\n\n\n/*\n * TaskMapEntry is used as entry in task hash. We need to keep a pointer\n * of the task in the entry.\n */\ntypedef struct TaskMapEntry\n{\n\tTaskMapKey key;\n\tTask *task;\n} TaskMapEntry;\n\nstatic Task * TaskHashEnter(HTAB *taskHash, Task *task);\nstatic Task * TaskHashLookup(HTAB *trackerHash, TaskType taskType, uint64 jobId,\n\t\t\t\t\t\t\t uint32 taskId);\n\n/*\n * CreateTaskListForJobTree visits all tasks in the job tree (by following dependentTaskList),\n * starting with the given job's task list. The function then returns the list.\n */\nList *\nCreateTaskListForJobTree(List *jobTaskList)\n{\n\tList *taskList = NIL;\n\tconst int topLevelTaskHashSize = 32;\n\tint taskHashSize = list_length(jobTaskList) * topLevelTaskHashSize;\n\tassert_valid_hash_key3(TaskMapKey, taskType, taskId, jobId);\n\tHTAB *taskHash = CreateSimpleHashWithSize(TaskMapKey, TaskMapEntry, taskHashSize);\n\n\t/*\n\t * We walk over the task tree using breadth-first search. For the search, we\n\t * first queue top level tasks in the task tree.\n\t */\n\tList *taskQueue = list_copy(jobTaskList);\n\twhile (taskQueue != NIL)\n\t{\n\t\t/* pop first element from the task queue */\n\t\tTask *task = (Task *) linitial(taskQueue);\n\t\ttaskQueue = list_delete_first(taskQueue);\n\n\t\ttaskList = lappend(taskList, task);\n\n\t\tList *dependendTaskList = task->dependentTaskList;\n\n\t\t/*\n\t\t * Push task node's children into the task queue, if and only if\n\t\t * they're not already there. As task dependencies have to form a\n\t\t * directed-acyclic-graph and are processed in a breadth-first search\n\t\t * we can never re-encounter nodes we've already processed.\n\t\t *\n\t\t * While we're checking this, we can also fix the problem that\n\t\t * copyObject() might have duplicated nodes in the graph - if a node\n\t\t * isn't pushed to the graph because it is already planned to be\n\t\t * visited, we can simply replace it with the copy. Note that, here\n\t\t * we only consider dependend tasks. Since currently top level tasks\n\t\t * cannot be on any dependend task list, we do not check them for duplicates.\n\t\t *\n\t\t * taskHash is used to reduce the complexity of keeping track of\n\t\t * the tasks that are already encountered.\n\t\t */\n\t\tListCell *dependentTaskCell = NULL;\n\t\tforeach(dependentTaskCell, dependendTaskList)\n\t\t{\n\t\t\tTask *dependendTask = lfirst(dependentTaskCell);\n\t\t\tTask *dependendTaskInHash = TaskHashLookup(taskHash,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   dependendTask->taskType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   dependendTask->jobId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   dependendTask->taskId);\n\n\t\t\t/*\n\t\t\t * If the dependend task encountered for the first time, add it to the hash.\n\t\t\t * Also, add this task to the task queue. Note that, we do not need to\n\t\t\t * add the tasks to the queue which are already encountered, because\n\t\t\t * they are already added to the queue.\n\t\t\t */\n\t\t\tif (!dependendTaskInHash)\n\t\t\t{\n\t\t\t\tdependendTaskInHash = TaskHashEnter(taskHash, dependendTask);\n\t\t\t\ttaskQueue = lappend(taskQueue, dependendTaskInHash);\n\t\t\t}\n\n\t\t\t/* update dependentTaskList element to the one which is in the hash */\n\t\t\tlfirst(dependentTaskCell) = dependendTaskInHash;\n\t\t}\n\t}\n\n\treturn taskList;\n}\n\n\n/*\n * TaskHashEnter creates a reference to the task entry in the given task\n * hash. The function errors-out if the same key exists multiple times.\n */\nstatic Task *\nTaskHashEnter(HTAB *taskHash, Task *task)\n{\n\tbool handleFound = false;\n\n\tTaskMapKey taskKey;\n\tmemset(&taskKey, 0, sizeof(TaskMapKey));\n\n\ttaskKey.taskType = task->taskType;\n\ttaskKey.jobId = task->jobId;\n\ttaskKey.taskId = task->taskId;\n\n\tvoid *hashKey = (void *) &taskKey;\n\tTaskMapEntry *taskInTheHash = (TaskMapEntry *) hash_search(taskHash, hashKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   HASH_ENTER,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &handleFound);\n\n\t/* if same node appears twice, we error-out */\n\tif (handleFound)\n\t{\n\t\tereport(ERROR, (errmsg(\"multiple entries for task: \\\"%d:\" UINT64_FORMAT \":%u\\\"\",\n\t\t\t\t\t\t\t   task->taskType, task->jobId, task->taskId)));\n\t}\n\n\t/* save the pointer to the original task in the hash */\n\ttaskInTheHash->task = task;\n\n\treturn task;\n}\n\n\n/*\n * TaskHashLookup looks for the tasks that corresponds to the given\n * taskType, jobId and taskId, and returns the found task, NULL otherwise.\n */\nstatic Task *\nTaskHashLookup(HTAB *taskHash, TaskType taskType, uint64 jobId, uint32 taskId)\n{\n\tTask *task = NULL;\n\tbool handleFound = false;\n\n\tTaskMapKey taskKey;\n\tmemset(&taskKey, 0, sizeof(TaskMapKey));\n\n\ttaskKey.taskType = taskType;\n\ttaskKey.jobId = jobId;\n\ttaskKey.taskId = taskId;\n\n\tvoid *hashKey = (void *) &taskKey;\n\tTaskMapEntry *taskEntry = (TaskMapEntry *) hash_search(taskHash, hashKey, HASH_FIND,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   &handleFound);\n\n\tif (taskEntry != NULL)\n\t{\n\t\ttask = taskEntry->task;\n\t}\n\n\treturn task;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/tenant_schema_metadata.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * tenant_schema_metadata.c\n *\n * This file contains functions to query and modify tenant schema metadata,\n * which is used to track the schemas used for schema-based sharding in\n * Citus.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/genam.h\"\n#include \"access/htup.h\"\n#include \"access/table.h\"\n#include \"catalog/pg_namespace_d.h\"\n#include \"storage/lockdefs.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/relcache.h\"\n\n#include \"distributed/colocation_utils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/pg_dist_schema.h\"\n#include \"distributed/tenant_schema_metadata.h\"\n\n\n/*\n * IsTenantSchema returns true if there is a tenant schema with given schemaId.\n */\nbool\nIsTenantSchema(Oid schemaId)\n{\n\t/*\n\t * Bail out early if there is a version mismatch between the Citus\n\t * shared library and the installed SQL extension. This prevents\n\t * querying pg_dist_schema when the catalog table may not exist\n\t * (e.g., during multi_extension tests that install versions older\n\t * than 12.0).\n\t *\n\t * When version checks are disabled (citus.enable_version_checks=off),\n\t * CheckCitusVersion() returns true unconditionally, even if there is\n\t * a real version mismatch. In that case, we need to verify that\n\t * pg_dist_schema actually exists before querying it, since the\n\t * multi_extension test installs old versions (e.g. 8.0-1) with\n\t * version checks disabled and creates tables in that state.\n\t *\n\t * On the normal path (version checks enabled, versions compatible),\n\t * this adds no overhead -- the !EnableVersionChecks branch is skipped\n\t * entirely.\n\t */\n\tif (!CheckCitusVersion(DEBUG4))\n\t{\n\t\treturn false;\n\t}\n\n\tif (!EnableVersionChecks)\n\t{\n\t\tOid distSchemaOid = get_relname_relid(\"pg_dist_schema\",\n\t\t\t\t\t\t\t\t\t\t\t  PG_CATALOG_NAMESPACE);\n\t\tif (!OidIsValid(distSchemaOid))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn SchemaIdGetTenantColocationId(schemaId) != INVALID_COLOCATION_ID;\n}\n\n\n/*\n * IsTenantSchemaColocationGroup returns true if there is a tenant schema\n * that is associated with given colocation id.\n */\nbool\nIsTenantSchemaColocationGroup(uint32 colocationId)\n{\n\treturn OidIsValid(ColocationIdGetTenantSchemaId(colocationId));\n}\n\n\n/*\n * SchemaIdGetTenantColocationId returns the colocation id associated with\n * the tenant schema with given id.\n *\n * Returns INVALID_COLOCATION_ID if there is no tenant schema with given id.\n */\nuint32\nSchemaIdGetTenantColocationId(Oid schemaId)\n{\n\tuint32 colocationId = INVALID_COLOCATION_ID;\n\n\tif (!OidIsValid(schemaId))\n\t{\n\t\tereport(ERROR, (errmsg(\"schema id is invalid\")));\n\t}\n\n\tRelation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(),\n\t\t\t\t\t\t\t\t\t\t\t AccessShareLock);\n\tScanKeyData scanKey[1];\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_schema_schemaid, BTEqualStrategyNumber,\n\t\t\t\tF_OIDEQ, ObjectIdGetDatum(schemaId));\n\n\tbool indexOk = true;\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistTenantSchema,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistTenantSchemaPrimaryKeyIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOk, NULL, 1, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext_ordered(scanDescriptor, ForwardScanDirection);\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tbool isNull = false;\n\t\tcolocationId = DatumGetUInt32(\n\t\t\theap_getattr(heapTuple,\n\t\t\t\t\t\t Anum_pg_dist_schema_colocationid,\n\t\t\t\t\t\t RelationGetDescr(pgDistTenantSchema),\n\t\t\t\t\t\t &isNull));\n\t\tAssert(!isNull);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistTenantSchema, AccessShareLock);\n\n\treturn colocationId;\n}\n\n\n/*\n * ColocationIdGetTenantSchemaId returns the oid of the tenant schema that\n * is associated with given colocation id.\n *\n * Returns InvalidOid if there is no such tenant schema.\n */\nOid\nColocationIdGetTenantSchemaId(uint32 colocationId)\n{\n\tif (colocationId == INVALID_COLOCATION_ID)\n\t{\n\t\tereport(ERROR, (errmsg(\"colocation id is invalid\")));\n\t}\n\n\tRelation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(),\n\t\t\t\t\t\t\t\t\t\t\t AccessShareLock);\n\tScanKeyData scanKey[1];\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_schema_colocationid,\n\t\t\t\tBTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(colocationId));\n\n\tbool indexOk = true;\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistTenantSchema,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistTenantSchemaUniqueColocationIdIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOk, NULL, 1, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext_ordered(scanDescriptor, ForwardScanDirection);\n\tOid schemaId = InvalidOid;\n\tif (HeapTupleIsValid(heapTuple))\n\t{\n\t\tbool isNull = false;\n\t\tschemaId = heap_getattr(heapTuple, Anum_pg_dist_schema_schemaid,\n\t\t\t\t\t\t\t\tRelationGetDescr(pgDistTenantSchema), &isNull);\n\t\tAssert(!isNull);\n\t}\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistTenantSchema, AccessShareLock);\n\n\treturn schemaId;\n}\n\n\n/*\n * InsertTenantSchemaLocally inserts an entry into pg_dist_schema\n * with given schemaId and colocationId.\n *\n * Throws a constraint violation error if there is already an entry with\n * given schemaId, or if given colocation id is already associated with\n * another tenant schema.\n */\nvoid\nInsertTenantSchemaLocally(Oid schemaId, uint32 colocationId)\n{\n\tif (!OidIsValid(schemaId))\n\t{\n\t\tereport(ERROR, (errmsg(\"schema id is invalid\")));\n\t}\n\n\tif (colocationId == INVALID_COLOCATION_ID)\n\t{\n\t\tereport(ERROR, (errmsg(\"colocation id is invalid\")));\n\t}\n\n\tDatum values[Natts_pg_dist_schema] = { 0 };\n\tbool isNulls[Natts_pg_dist_schema] = { 0 };\n\n\tvalues[Anum_pg_dist_schema_schemaid - 1] = ObjectIdGetDatum(schemaId);\n\tvalues[Anum_pg_dist_schema_colocationid - 1] = UInt32GetDatum(colocationId);\n\n\tRelation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(),\n\t\t\t\t\t\t\t\t\t\t\t RowExclusiveLock);\n\tTupleDesc tupleDescriptor = RelationGetDescr(pgDistTenantSchema);\n\tHeapTuple heapTuple = heap_form_tuple(tupleDescriptor, values, isNulls);\n\n\tCatalogTupleInsert(pgDistTenantSchema, heapTuple);\n\tCommandCounterIncrement();\n\n\ttable_close(pgDistTenantSchema, NoLock);\n}\n\n\n/*\n * DeleteTenantSchemaLocally deletes the entry for given schemaId from\n * pg_dist_schema.\n *\n * Throws an error if there is no such tenant schema.\n */\nvoid\nDeleteTenantSchemaLocally(Oid schemaId)\n{\n\tif (!OidIsValid(schemaId))\n\t{\n\t\tereport(ERROR, (errmsg(\"schema id is invalid\")));\n\t}\n\n\tRelation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(),\n\t\t\t\t\t\t\t\t\t\t\t RowExclusiveLock);\n\tScanKeyData scanKey[1];\n\tScanKeyInit(&scanKey[0], Anum_pg_dist_schema_schemaid, BTEqualStrategyNumber,\n\t\t\t\tF_OIDEQ, ObjectIdGetDatum(schemaId));\n\n\tbool indexOk = true;\n\tSysScanDesc scanDescriptor = systable_beginscan(pgDistTenantSchema,\n\t\t\t\t\t\t\t\t\t\t\t\t\tDistTenantSchemaPrimaryKeyIndexId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tindexOk, NULL, 1, scanKey);\n\n\tHeapTuple heapTuple = systable_getnext_ordered(scanDescriptor, ForwardScanDirection);\n\tif (!HeapTupleIsValid(heapTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"could not find tuple for tenant schema %u\", schemaId)));\n\t}\n\n\tCatalogTupleDelete(pgDistTenantSchema, &heapTuple->t_self);\n\tCommandCounterIncrement();\n\n\tsystable_endscan(scanDescriptor);\n\ttable_close(pgDistTenantSchema, NoLock);\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/tuplestore.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * tuplestore.c\n *\t  Utilities regarding calls to PG functions\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"distributed/tuplestore.h\"\n\n/*\n * CheckTuplestoreReturn checks if a tuplestore can be returned in the callsite\n * of the UDF.\n */\nReturnSetInfo *\nCheckTuplestoreReturn(FunctionCallInfo fcinfo, TupleDesc *tupdesc)\n{\n\tReturnSetInfo *returnSetInfo = (ReturnSetInfo *) fcinfo->resultinfo;\n\n\t/* check to see if caller supports us returning a tuplestore */\n\tif (returnSetInfo == NULL || !IsA(returnSetInfo, ReturnSetInfo))\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t errmsg(\"set-valued function called in context that cannot \" \\\n\t\t\t\t\t\t\"accept a set\")));\n\t}\n\tif (!(returnSetInfo->allowedModes & SFRM_Materialize))\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t errmsg(\"materialize mode required, but it is not \" \\\n\t\t\t\t\t\t\"allowed in this context\")));\n\t}\n\tswitch (get_call_result_type(fcinfo, NULL, tupdesc))\n\t{\n\t\tcase TYPEFUNC_COMPOSITE:\n\t\t{\n\t\t\t/* success */\n\t\t\tbreak;\n\t\t}\n\n\t\tcase TYPEFUNC_RECORD:\n\t\t{\n\t\t\t/* failed to determine actual type of RECORD */\n\t\t\tereport(ERROR,\n\t\t\t\t\t(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\n\t\t\t\t\t errmsg(\"function returning record called in context \"\n\t\t\t\t\t\t\t\"that cannot accept type record\")));\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\t/* result type isn't composite */\n\t\t\telog(ERROR, \"return type must be a row type\");\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn returnSetInfo;\n}\n\n\n/*\n * SetupTuplestore sets up a tuplestore for returning data.\n */\nTuplestorestate *\nSetupTuplestore(FunctionCallInfo fcinfo, TupleDesc *tupleDescriptor)\n{\n\tReturnSetInfo *resultSet = CheckTuplestoreReturn(fcinfo, tupleDescriptor);\n\tMemoryContext perQueryContext = resultSet->econtext->ecxt_per_query_memory;\n\n\tMemoryContext oldContext = MemoryContextSwitchTo(perQueryContext);\n\tTuplestorestate *tupstore = tuplestore_begin_heap(true, false, work_mem);\n\tresultSet->returnMode = SFRM_Materialize;\n\tresultSet->setResult = tupstore;\n\tresultSet->setDesc = *tupleDescriptor;\n\tMemoryContextSwitchTo(oldContext);\n\n\treturn tupstore;\n}\n"
  },
  {
    "path": "src/backend/distributed/utils/type_utils.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * type_utils.c\n *\n * Utility functions related to types.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"libpq-fe.h\"\n\n#include \"catalog/pg_type.h\"\n#include \"libpq/pqformat.h\"\n#include \"nodes/pg_list.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/causal_clock.h\"\n\n#define NUM_CLUSTER_CLOCK_ARGS 2\n#define LDELIM '('\n#define RDELIM ')'\n#define DELIM ','\n\nstatic ClusterClock * cluster_clock_in_internal(char *clockString);\n\nPG_FUNCTION_INFO_V1(cluster_clock_in);\nPG_FUNCTION_INFO_V1(cluster_clock_out);\nPG_FUNCTION_INFO_V1(cluster_clock_recv);\nPG_FUNCTION_INFO_V1(cluster_clock_send);\n\n/*\n * cluster_clock_in_internal generic routine to parse the cluster_clock format of (logical, counter),\n * (%lu, %u), in string format to ClusterClock struct internal format.\n */\nstatic ClusterClock *\ncluster_clock_in_internal(char *clockString)\n{\n\tchar *clockFields[NUM_CLUSTER_CLOCK_ARGS];\n\tint numClockField = 0;\n\n\tfor (char *currentChar = clockString;\n\t\t (*currentChar) && (numClockField < NUM_CLUSTER_CLOCK_ARGS) && (*currentChar !=\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRDELIM);\n\t\t currentChar++)\n\t{\n\t\tif (*currentChar == DELIM || (*currentChar == LDELIM && !numClockField))\n\t\t{\n\t\t\tclockFields[numClockField++] = currentChar + 1;\n\t\t}\n\t}\n\n\tif (numClockField < NUM_CLUSTER_CLOCK_ARGS)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),\n\t\t\t\t errmsg(\"invalid input syntax for type %s: \\\"%s\\\"\",\n\t\t\t\t\t\t\"cluster_clock\",\n\t\t\t\t\t\tclockString)));\n\t}\n\n\tchar *endingChar = NULL;\n\terrno = 0;\n\tint64 logical = strtoul(clockFields[0], &endingChar, 10);\n\n\tif (errno || (*endingChar != DELIM) || (logical > MAX_LOGICAL) || logical < 0)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),\n\t\t\t\t errmsg(\"invalid input syntax for type %s: \\\"%s\\\"\",\n\t\t\t\t\t\t\"cluster_clock\",\n\t\t\t\t\t\tclockString)));\n\t}\n\n\tint64 counter = strtol(clockFields[1], &endingChar, 10);\n\n\tif (errno || (*endingChar != RDELIM) || (counter > MAX_COUNTER) || counter < 0)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),\n\t\t\t\t errmsg(\"invalid input syntax for type %s: \\\"%s\\\"\",\n\t\t\t\t\t\t\"cluster_clock\",\n\t\t\t\t\t\tclockString)));\n\t}\n\n\tClusterClock *clusterClock = (ClusterClock *) palloc(sizeof(ClusterClock));\n\tclusterClock->logical = logical;\n\tclusterClock->counter = counter;\n\n\treturn clusterClock;\n}\n\n\n/*\n * cluster_clock_in converts the cstring input format to the ClusterClock type.\n */\nDatum\ncluster_clock_in(PG_FUNCTION_ARGS)\n{\n\tchar *clockString = PG_GETARG_CSTRING(0);\n\n\tPG_RETURN_POINTER(cluster_clock_in_internal(clockString));\n}\n\n\n/*\n * cluster_clock_out converts the internal ClusterClock format to cstring output.\n */\nDatum\ncluster_clock_out(PG_FUNCTION_ARGS)\n{\n\tClusterClock *clusterClock = (ClusterClock *) PG_GETARG_POINTER(0);\n\n\tif (clusterClock == NULL)\n\t{\n\t\tPG_RETURN_CSTRING(\"\");\n\t}\n\n\tchar *clockString = psprintf(\"(%lu,%u)\", clusterClock->logical,\n\t\t\t\t\t\t\t\t clusterClock->counter);\n\n\tPG_RETURN_CSTRING(clockString);\n}\n\n\n/*\n * cluster_clock_recv converts external binary format to ClusterClock.\n */\nDatum\ncluster_clock_recv(PG_FUNCTION_ARGS)\n{\n\tStringInfo clockBuffer = (StringInfo) PG_GETARG_POINTER(0);\n\tClusterClock *clusterClock = (ClusterClock *) palloc(sizeof(ClusterClock));\n\n\tclusterClock->logical = pq_getmsgint64(clockBuffer);\n\tclusterClock->counter = pq_getmsgint(clockBuffer, sizeof(int));\n\n\tPG_RETURN_POINTER(clusterClock);\n}\n\n\n/*\n * cluster_clock_send converts ClusterClock to binary format.\n */\nDatum\ncluster_clock_send(PG_FUNCTION_ARGS)\n{\n\tClusterClock *clusterClock = (ClusterClock *) PG_GETARG_POINTER(0);\n\tStringInfoData clockBuffer;\n\n\tpq_begintypsend(&clockBuffer);\n\tpq_sendint64(&clockBuffer, clusterClock->logical);\n\tpq_sendint32(&clockBuffer, clusterClock->counter);\n\n\tPG_RETURN_BYTEA_P(pq_endtypsend(&clockBuffer));\n}\n\n\n/*****************************************************************************\n*       PUBLIC ROUTINES                                                     *\n*****************************************************************************/\n\nPG_FUNCTION_INFO_V1(cluster_clock_lt);\nPG_FUNCTION_INFO_V1(cluster_clock_le);\nPG_FUNCTION_INFO_V1(cluster_clock_eq);\nPG_FUNCTION_INFO_V1(cluster_clock_ne);\nPG_FUNCTION_INFO_V1(cluster_clock_gt);\nPG_FUNCTION_INFO_V1(cluster_clock_ge);\nPG_FUNCTION_INFO_V1(cluster_clock_cmp);\nPG_FUNCTION_INFO_V1(cluster_clock_logical);\n\n\n/*\n * cluster_clock_cmp_internal generic compare routine, and must be used for all\n * operators, including Btree Indexes when comparing cluster_clock data type.\n * Return values are\n *   1 -- clock1 is > clock2\n *   0 -- clock1 is = clock2\n *  -1 -- clock1 is < clock2\n */\nint\ncluster_clock_cmp_internal(ClusterClock *clusterClock1, ClusterClock *clusterClock2)\n{\n\tAssert(clusterClock1 && clusterClock2);\n\n\tint retcode = 0;\n\n\t/* Logical value takes precedence when comparing two clocks */\n\tif (clusterClock1->logical != clusterClock2->logical)\n\t{\n\t\tretcode = (clusterClock1->logical > clusterClock2->logical) ? 1 : -1;\n\t\treturn retcode;\n\t}\n\n\t/* Logical values are equal, let's compare ticks */\n\tif (clusterClock1->counter != clusterClock2->counter)\n\t{\n\t\tretcode = (clusterClock1->counter > clusterClock2->counter) ? 1 : -1;\n\t\treturn retcode;\n\t}\n\n\t/* Ticks are equal too, return zero */\n\treturn retcode;\n}\n\n\n/*\n * cluster_clock_lt returns true if clock1 is less than clock2.\n */\nDatum\ncluster_clock_lt(PG_FUNCTION_ARGS)\n{\n\tClusterClock *clock1 = (ClusterClock *) PG_GETARG_POINTER(0);\n\tClusterClock *clock2 = (ClusterClock *) PG_GETARG_POINTER(1);\n\n\tPG_RETURN_BOOL(cluster_clock_cmp_internal(clock1, clock2) < 0);\n}\n\n\n/*\n * cluster_clock_le returns true if clock1 is less than or equal to clock2.\n */\nDatum\ncluster_clock_le(PG_FUNCTION_ARGS)\n{\n\tClusterClock *clock1 = (ClusterClock *) PG_GETARG_POINTER(0);\n\tClusterClock *clock2 = (ClusterClock *) PG_GETARG_POINTER(1);\n\n\tPG_RETURN_BOOL(cluster_clock_cmp_internal(clock1, clock2) <= 0);\n}\n\n\n/*\n * cluster_clock_eq returns true if clock1 is equal to clock2.\n */\nDatum\ncluster_clock_eq(PG_FUNCTION_ARGS)\n{\n\tClusterClock *clock1 = (ClusterClock *) PG_GETARG_POINTER(0);\n\tClusterClock *clock2 = (ClusterClock *) PG_GETARG_POINTER(1);\n\n\tPG_RETURN_BOOL(cluster_clock_cmp_internal(clock1, clock2) == 0);\n}\n\n\n/*\n * cluster_clock_ne returns true if clock1 is not equal to clock2.\n */\nDatum\ncluster_clock_ne(PG_FUNCTION_ARGS)\n{\n\tClusterClock *clock1 = (ClusterClock *) PG_GETARG_POINTER(0);\n\tClusterClock *clock2 = (ClusterClock *) PG_GETARG_POINTER(1);\n\n\tPG_RETURN_BOOL(cluster_clock_cmp_internal(clock1, clock2) != 0);\n}\n\n\n/*\n * cluster_clock_gt returns true if clock1 is greater than clock2.\n */\nDatum\ncluster_clock_gt(PG_FUNCTION_ARGS)\n{\n\tClusterClock *clock1 = (ClusterClock *) PG_GETARG_POINTER(0);\n\tClusterClock *clock2 = (ClusterClock *) PG_GETARG_POINTER(1);\n\n\tPG_RETURN_BOOL(cluster_clock_cmp_internal(clock1, clock2) > 0);\n}\n\n\n/*\n * cluster_clock_ge returns true if clock1 is greater than or equal to clock2\n */\nDatum\ncluster_clock_ge(PG_FUNCTION_ARGS)\n{\n\tClusterClock *clock1 = (ClusterClock *) PG_GETARG_POINTER(0);\n\tClusterClock *clock2 = (ClusterClock *) PG_GETARG_POINTER(1);\n\n\tPG_RETURN_BOOL(cluster_clock_cmp_internal(clock1, clock2) >= 0);\n}\n\n\n/*\n * cluster_clock_cmp returns 1 if clock1 is greater than clock2, returns -1 if\n * clock1 is less than clock2, and zero if they are equal.\n */\nDatum\ncluster_clock_cmp(PG_FUNCTION_ARGS)\n{\n\tClusterClock *clock1 = (ClusterClock *) PG_GETARG_POINTER(0);\n\tClusterClock *clock2 = (ClusterClock *) PG_GETARG_POINTER(1);\n\n\tPG_RETURN_INT32(cluster_clock_cmp_internal(clock1, clock2));\n}\n\n\n/*\n * cluster_clock_logical return the logical part from <logical, counter> type\n * clock, which is basically the epoch value in milliseconds.\n */\nDatum\ncluster_clock_logical(PG_FUNCTION_ARGS)\n{\n\tClusterClock *clusterClock = (ClusterClock *) PG_GETARG_POINTER(0);\n\n\tPG_RETURN_INT64(clusterClock->logical);\n}\n\n\n/*\n * ParseClusterClockPGresult parses a ClusterClock remote result and returns the value or\n * returns 0 if the result is NULL.\n */\nClusterClock *\nParseClusterClockPGresult(PGresult *result, int rowIndex, int colIndex)\n{\n\tif (PQgetisnull(result, rowIndex, colIndex))\n\t{\n\t\treturn 0;\n\t}\n\n\tchar *resultString = PQgetvalue(result, rowIndex, colIndex);\n\treturn cluster_clock_in_internal(resultString);\n}\n"
  },
  {
    "path": "src/backend/distributed/worker/task_tracker_protocol.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * task_tracker_protocol.c\n *\n * The methods in the file are deprecated.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(task_tracker_assign_task);\nPG_FUNCTION_INFO_V1(task_tracker_task_status);\nPG_FUNCTION_INFO_V1(task_tracker_cleanup_job);\nPG_FUNCTION_INFO_V1(task_tracker_conninfo_cache_invalidate);\n\n\n/* This UDF is deprecated.*/\nDatum\ntask_tracker_assign_task(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"This UDF is deprecated.\")));\n\n\tPG_RETURN_NULL();\n}\n\n\n/* This UDF is deprecated.*/\nDatum\ntask_tracker_task_status(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"This UDF is deprecated.\")));\n\n\tPG_RETURN_UINT32(0);\n}\n\n\n/* This UDF is deprecated.*/\nDatum\ntask_tracker_cleanup_job(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"This UDF is deprecated.\")));\n\n\tPG_RETURN_NULL();\n}\n\n\n/* This UDF is deprecated.*/\nDatum\ntask_tracker_conninfo_cache_invalidate(PG_FUNCTION_ARGS)\n{\n\tPG_RETURN_DATUM(PointerGetDatum(NULL));\n}\n"
  },
  {
    "path": "src/backend/distributed/worker/worker_create_or_replace.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_create_or_replace.c\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n#include \"funcapi.h\"\n\n#include \"access/htup_details.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_ts_config.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodes.h\"\n#include \"parser/parse_type.h\"\n#include \"tcop/dest.h\"\n#include \"tcop/utility.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/regproc.h\"\n#include \"utils/syscache.h\"\n\n#include \"distributed/commands.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/worker_create_or_replace.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/*\n * OnCollisionAction describes what to do when the created object\n * and existing object do not match.\n */\ntypedef enum OnCollisionAction\n{\n\tON_COLLISION_RENAME,\n\tON_COLLISION_DROP\n} OnCollisionAction;\n\n\nstatic List * CreateStmtListByObjectAddress(const ObjectAddress *address);\nstatic bool CompareStringList(List *list1, List *list2);\nstatic OnCollisionAction GetOnCollisionAction(const ObjectAddress *address);\n\n\nPG_FUNCTION_INFO_V1(worker_create_or_replace_object);\nPG_FUNCTION_INFO_V1(worker_create_or_replace_object_array);\nstatic bool WorkerCreateOrReplaceObject(List *sqlStatements);\n\n\n/*\n * WrapCreateOrReplace takes a sql CREATE command and wraps it in a call to citus' udf to\n * create or replace the existing object based on its create command.\n */\nchar *\nWrapCreateOrReplace(const char *sql)\n{\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\tappendStringInfo(&buf, CREATE_OR_REPLACE_COMMAND, quote_literal_cstr(sql));\n\treturn buf.data;\n}\n\n\n/*\n * WrapCreateOrReplaceList takes a list of sql commands and wraps it in a call to citus'\n * udf to create or replace the existing object based on its create commands.\n */\nchar *\nWrapCreateOrReplaceList(List *sqls)\n{\n\tStringInfoData textArrayLitteral = { 0 };\n\tinitStringInfo(&textArrayLitteral);\n\n\tappendStringInfoString(&textArrayLitteral, \"ARRAY[\");\n\tconst char *sql = NULL;\n\tbool first = true;\n\tforeach_declared_ptr(sql, sqls)\n\t{\n\t\tif (!first)\n\t\t{\n\t\t\tappendStringInfoString(&textArrayLitteral, \", \");\n\t\t}\n\t\tappendStringInfoString(&textArrayLitteral, quote_literal_cstr(sql));\n\t\tfirst = false;\n\t}\n\tappendStringInfoString(&textArrayLitteral, \"]::text[]\");\n\n\tStringInfoData buf = { 0 };\n\tinitStringInfo(&buf);\n\tappendStringInfo(&buf, CREATE_OR_REPLACE_COMMAND, textArrayLitteral.data);\n\treturn buf.data;\n}\n\n\n/*\n * worker_create_or_replace_object(statement text)\n *\n * function is called, by the coordinator, with a CREATE statement for an object. This\n * function implements the CREATE ... IF NOT EXISTS functionality for objects that do not\n * have this functionality or where their implementation is not sufficient.\n *\n * Besides checking if an object of said name exists it tries to compare the object to be\n * created with the one in the local catalog. If there is a difference the one in the local\n * catalog will be renamed after which the statement can be executed on this worker to\n * create the object.\n *\n * Renaming has two purposes\n *  - free the identifier for creation\n *  - non destructive if there is data store that would be destroyed if the object was\n *    used in a table on this node, eg. types. If the type would be dropped with a cascade\n *    it would drop any column holding user data for this type.\n */\nDatum\nworker_create_or_replace_object(PG_FUNCTION_ARGS)\n{\n\ttext *sqlStatementText = PG_GETARG_TEXT_P(0);\n\tchar *sqlStatement = text_to_cstring(sqlStatementText);\n\tList *sqlStatements = list_make1(sqlStatement);\n\n\tPG_RETURN_BOOL(WorkerCreateOrReplaceObject(sqlStatements));\n}\n\n\n/*\n * worker_create_or_replace_object(statements text[])\n *\n * function is called, by the coordinator, with a CREATE statement for an object. This\n * function implements the CREATE ... IF NOT EXISTS functionality for objects that do not\n * have this functionality or where their implementation is not sufficient.\n *\n * Besides checking if an object of said name exists it tries to compare the object to be\n * created with the one in the local catalog. If there is a difference the one in the local\n * catalog will be renamed after which the statement can be executed on this worker to\n * create the object. If more statements are provided, all are compared in order with the\n * statements generated on the worker. This works assuming a) both citus versions are the\n * same, b) the objects are exactly the same.\n *\n * Renaming has two purposes\n *  - free the identifier for creation\n *  - non destructive if there is data store that would be destroyed if the object was\n *    used in a table on this node, eg. types. If the type would be dropped with a cascade\n *    it would drop any column holding user data for this type.\n */\nDatum\nworker_create_or_replace_object_array(PG_FUNCTION_ARGS)\n{\n\tList *sqlStatements = NIL;\n\tDatum *textArray = NULL;\n\tint length = 0;\n\tdeconstruct_array(PG_GETARG_ARRAYTYPE_P(0), TEXTOID, -1, false, 'i', &textArray,\n\t\t\t\t\t  NULL, &length);\n\n\tfor (int i = 0; i < length; i++)\n\t{\n\t\tsqlStatements = lappend(sqlStatements, TextDatumGetCString(textArray[i]));\n\t}\n\n\tif (list_length(sqlStatements) < 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"expected atleast 1 statement to be provided\")));\n\t}\n\n\tPG_RETURN_BOOL(WorkerCreateOrReplaceObject(sqlStatements));\n}\n\n\n/*\n * WorkerCreateOrReplaceObject implements the logic used by both variants of\n * worker_create_or_replace_object to either create the object or coming to the conclusion\n * the object already exists in the correct state.\n *\n * Returns true if the object has been created, false if it was already in the exact state\n * it was asked for.\n */\nstatic bool\nWorkerCreateOrReplaceObject(List *sqlStatements)\n{\n\t/*\n\t * To check which object we are changing we find the object address from the first\n\t * statement passed into the UDF. Later we will check if all object addresses are the\n\t * same.\n\t *\n\t * Although many of the objects will only have one statement in this call, more\n\t * complex objects might come with a list of statements. We assume they all are on the\n\t * same subject.\n\t */\n\tNode *parseTree = ParseTreeNode(linitial(sqlStatements));\n\tList *addresses = GetObjectAddressListFromParseTree(parseTree, true, false);\n\tAssert(list_length(addresses) == 1);\n\n\t/* We have already asserted that we have exactly 1 address in the addresses. */\n\tObjectAddress *address = linitial(addresses);\n\n\tif (ObjectExists(address))\n\t{\n\t\t/*\n\t\t * Object with name from statement is already found locally, check if states are\n\t\t * identical. If objects differ we will rename the old object (non- destructively)\n\t\t * or drop it (if safe) as to make room to create the new object according to the\n\t\t * spec sent.\n\t\t */\n\n\t\t/*\n\t\t * Based on the local catalog we generate the list of commands we would send to\n\t\t * recreate our version of the object. This we can compare to what the coordinator\n\t\t * sent us. If they match we don't do anything.\n\t\t */\n\t\tList *localSqlStatements = CreateStmtListByObjectAddress(address);\n\t\tif (CompareStringList(sqlStatements, localSqlStatements))\n\t\t{\n\t\t\t/*\n\t\t\t * statements sent by the coordinator are the same as we would create for our\n\t\t\t * object, therefore we can omit the statements locally and not create the\n\t\t\t * object as it already exists in the correct shape.\n\t\t\t *\n\t\t\t * We let the coordinator know we didn't create the object.\n\t\t\t */\n\t\t\treturn false;\n\t\t}\n\n\t\tNode *utilityStmt = NULL;\n\n\t\tif (GetOnCollisionAction(address) == ON_COLLISION_DROP)\n\t\t{\n\t\t\t/* drop the existing object */\n\t\t\tutilityStmt = (Node *) CreateDropStmt(address);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t/* rename the existing object */\n\t\t\tchar *newName = GenerateBackupNameForCollision(address);\n\t\t\tutilityStmt = (Node *) CreateRenameStatement(address, newName);\n\t\t}\n\n\t\tconst char *commandString = DeparseTreeNode(utilityStmt);\n\t\tProcessUtilityParseTree(utilityStmt, commandString,\n\t\t\t\t\t\t\t\tPROCESS_UTILITY_QUERY,\n\t\t\t\t\t\t\t\tNULL, None_Receiver, NULL);\n\t}\n\n\t/* apply all statement locally */\n\tchar *sqlStatement = NULL;\n\tforeach_declared_ptr(sqlStatement, sqlStatements)\n\t{\n\t\tparseTree = ParseTreeNode(sqlStatement);\n\t\tProcessUtilityParseTree(parseTree, sqlStatement, PROCESS_UTILITY_QUERY, NULL,\n\t\t\t\t\t\t\t\tNone_Receiver, NULL);\n\n\t\t/*  TODO verify all statements are about exactly 1 subject, mostly a sanity check\n\t\t * to prevent unintentional use of this UDF, needs to come after the local\n\t\t * execution to be able to actually resolve the ObjectAddress of the newly created\n\t\t * object */\n\t}\n\n\t/* type has been created */\n\treturn true;\n}\n\n\nstatic bool\nCompareStringList(List *list1, List *list2)\n{\n\tif (list_length(list1) != list_length(list2))\n\t{\n\t\treturn false;\n\t}\n\n\tconst char *str1 = NULL;\n\tconst char *str2 = NULL;\n\tforboth_ptr(str1, list1, str2, list2)\n\t{\n\t\tif (strcmp(str1, str2) != 0)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n/*\n * CreateStmtByObjectAddress returns a parsetree that will recreate the object addressed\n * by the ObjectAddress provided.\n *\n * Note: this tree does not contain position information that is normally in a parsetree,\n * therefore you cannot equal this tree against parsed statement. Instead it can be\n * deparsed to do a string comparison.\n */\nstatic List *\nCreateStmtListByObjectAddress(const ObjectAddress *address)\n{\n\tswitch (getObjectClass(address))\n\t{\n\t\tcase OCLASS_COLLATION:\n\t\t{\n\t\t\treturn list_make1(CreateCollationDDL(address->objectId));\n\t\t}\n\n\t\tcase OCLASS_PROC:\n\t\t{\n\t\t\treturn list_make1(GetFunctionDDLCommand(address->objectId, false));\n\t\t}\n\n\t\tcase OCLASS_PUBLICATION:\n\t\t{\n\t\t\treturn list_make1(CreatePublicationDDLCommand(address->objectId));\n\t\t}\n\n\t\tcase OCLASS_TSCONFIG:\n\t\t{\n\t\t\tList *stmts = GetCreateTextSearchConfigStatements(address);\n\t\t\treturn DeparseTreeNodes(stmts);\n\t\t}\n\n\t\tcase OCLASS_TSDICT:\n\t\t{\n\t\t\tList *stmts = GetCreateTextSearchDictionaryStatements(address);\n\t\t\treturn DeparseTreeNodes(stmts);\n\t\t}\n\n\t\tcase OCLASS_TYPE:\n\t\t{\n\t\t\treturn list_make1(DeparseTreeNode(CreateTypeStmtByObjectAddress(address)));\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\t\t\"unsupported object to construct a create statement\")));\n\t\t}\n\t}\n}\n\n\n/*\n * GetOnCollisionAction decides what to do when the object already exists.\n */\nstatic OnCollisionAction\nGetOnCollisionAction(const ObjectAddress *address)\n{\n\tswitch (getObjectClass(address))\n\t{\n\t\tcase OCLASS_PUBLICATION:\n\t\t{\n\t\t\t/*\n\t\t\t * We prefer to drop publications because they can be\n\t\t\t * harmful (cause update/delete failures) and are relatively\n\t\t\t * safe to drop.\n\t\t\t */\n\t\t\treturn ON_COLLISION_DROP;\n\t\t}\n\n\t\tcase OCLASS_COLLATION:\n\t\tcase OCLASS_PROC:\n\t\tcase OCLASS_TSCONFIG:\n\t\tcase OCLASS_TSDICT:\n\t\tcase OCLASS_TYPE:\n\t\tdefault:\n\t\t{\n\t\t\treturn ON_COLLISION_RENAME;\n\t\t}\n\t}\n}\n\n\n/*\n * GenerateBackupNameForCollision calculate a backup name for a given object by its\n * address. This name should be used when renaming an existing object before creating the\n * new object locally on the worker.\n */\nchar *\nGenerateBackupNameForCollision(const ObjectAddress *address)\n{\n\tswitch (getObjectClass(address))\n\t{\n\t\tcase OCLASS_COLLATION:\n\t\t{\n\t\t\treturn GenerateBackupNameForCollationCollision(address);\n\t\t}\n\n\t\tcase OCLASS_PROC:\n\t\t{\n\t\t\treturn GenerateBackupNameForProcCollision(address);\n\t\t}\n\n\t\tcase OCLASS_TSCONFIG:\n\t\t{\n\t\t\treturn GenerateBackupNameForTextSearchConfiguration(address);\n\t\t}\n\n\t\tcase OCLASS_TYPE:\n\t\t{\n\t\t\treturn GenerateBackupNameForTypeCollision(address);\n\t\t}\n\n\t\tcase OCLASS_CLASS:\n\t\t{\n\t\t\tchar relKind = get_rel_relkind(address->objectId);\n\t\t\tif (relKind == RELKIND_SEQUENCE)\n\t\t\t{\n\t\t\t\treturn GenerateBackupNameForSequenceCollision(address);\n\t\t\t}\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tereport(ERROR, (errmsg(\"unsupported object to construct a rename statement\"),\n\t\t\t\t\terrdetail(\"unable to generate a backup name for the old type\")));\n}\n\n\n/*\n * CreateDropPublicationStmt creates a DROP PUBLICATION statement for the\n * publication at the given address.\n */\nstatic DropStmt *\nCreateDropPublicationStmt(const ObjectAddress *address)\n{\n\tAssert(address->classId == PublicationRelationId);\n\n\tDropStmt *dropStmt = makeNode(DropStmt);\n\tdropStmt->removeType = OBJECT_PUBLICATION;\n\tdropStmt->behavior = DROP_RESTRICT;\n\n\tHeapTuple publicationTuple =\n\t\tSearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(address->objectId));\n\n\tif (!HeapTupleIsValid(publicationTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot find publication with oid: %d\",\n\t\t\t\t\t\t\t   address->objectId)));\n\t}\n\n\tForm_pg_publication publicationForm =\n\t\t(Form_pg_publication) GETSTRUCT(publicationTuple);\n\n\tchar *publicationName = NameStr(publicationForm->pubname);\n\tdropStmt->objects = list_make1(makeString(publicationName));\n\n\tReleaseSysCache(publicationTuple);\n\n\treturn dropStmt;\n}\n\n\n/*\n * CreateDropStmt returns a DROP statement for the given object.\n */\nDropStmt *\nCreateDropStmt(const ObjectAddress *address)\n{\n\tswitch (getObjectClass(address))\n\t{\n\t\tcase OCLASS_PUBLICATION:\n\t\t{\n\t\t\treturn CreateDropPublicationStmt(address);\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tereport(ERROR, (errmsg(\"unsupported object to construct a drop statement\"),\n\t\t\t\t\terrdetail(\"unable to generate a parsetree for the drop\")));\n}\n\n\n/*\n * CreateRenameTypeStmt creates a rename statement for a type based on its ObjectAddress.\n * The rename statement will rename the existing object on its address to the value\n * provided in newName.\n */\nstatic RenameStmt *\nCreateRenameCollationStmt(const ObjectAddress *address, char *newName)\n{\n\tRenameStmt *stmt = makeNode(RenameStmt);\n\tOid collid = address->objectId;\n\n\tHeapTuple colltup = SearchSysCache1(COLLOID, collid);\n\tif (!HeapTupleIsValid(colltup))\n\t{\n\t\tereport(ERROR, (errmsg(\"citus cache lookup error\")));\n\t}\n\tForm_pg_collation collationForm =\n\t\t(Form_pg_collation) GETSTRUCT(colltup);\n\n\tchar *schemaName = get_namespace_name(collationForm->collnamespace);\n\tchar *collationName = NameStr(collationForm->collname);\n\tList *name = list_make2(makeString(schemaName), makeString(collationName));\n\tReleaseSysCache(colltup);\n\n\tstmt->renameType = OBJECT_COLLATION;\n\tstmt->object = (Node *) name;\n\tstmt->newname = newName;\n\n\treturn stmt;\n}\n\n\n/*\n * CreateRenameTypeStmt creates a rename statement for a type based on its ObjectAddress.\n * The rename statement will rename the existing object on its address to the value\n * provided in newName.\n */\nstatic RenameStmt *\nCreateRenameTypeStmt(const ObjectAddress *address, char *newName)\n{\n\tRenameStmt *stmt = makeNode(RenameStmt);\n\n\tstmt->renameType = OBJECT_TYPE;\n\tstmt->object = (Node *) stringToQualifiedNameList(format_type_be_qualified(address->\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   objectId),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  NULL);\n\tstmt->newname = newName;\n\n\n\treturn stmt;\n}\n\n\n/*\n * CreateRenameTextSearchStmt creates a rename statement for a text search configuration\n * based on its ObjectAddress. The rename statement will rename the existing object on its\n * address to the value provided in newName.\n */\nstatic RenameStmt *\nCreateRenameTextSearchStmt(const ObjectAddress *address, char *newName)\n{\n\tAssert(address->classId == TSConfigRelationId);\n\tRenameStmt *stmt = makeNode(RenameStmt);\n\n\tstmt->renameType = OBJECT_TSCONFIGURATION;\n\tstmt->object = (Node *) get_ts_config_namelist(address->objectId);\n\tstmt->newname = newName;\n\n\treturn stmt;\n}\n\n\n/*\n * CreateRenameTypeStmt creates a rename statement for a type based on its ObjectAddress.\n * The rename statement will rename the existing object on its address to the value\n * provided in newName.\n */\nstatic RenameStmt *\nCreateRenameProcStmt(const ObjectAddress *address, char *newName)\n{\n\tRenameStmt *stmt = makeNode(RenameStmt);\n\n\tstmt->renameType = OBJECT_ROUTINE;\n\tstmt->object = (Node *) ObjectWithArgsFromOid(address->objectId);\n\tstmt->newname = newName;\n\n\treturn stmt;\n}\n\n\n/*\n * CreateRenameSequenceStmt creates a rename statement for a sequence based on its\n * ObjectAddress. The rename statement will rename the existing object on its address\n * to the value provided in newName.\n */\nstatic RenameStmt *\nCreateRenameSequenceStmt(const ObjectAddress *address, char *newName)\n{\n\tRenameStmt *stmt = makeNode(RenameStmt);\n\tOid seqOid = address->objectId;\n\n\tHeapTuple seqClassTuple = SearchSysCache1(RELOID, seqOid);\n\tif (!HeapTupleIsValid(seqClassTuple))\n\t{\n\t\tereport(ERROR, (errmsg(\"citus cache lookup error\")));\n\t}\n\tForm_pg_class seqClassForm = (Form_pg_class) GETSTRUCT(seqClassTuple);\n\n\tchar *schemaName = get_namespace_name(seqClassForm->relnamespace);\n\tchar *seqName = NameStr(seqClassForm->relname);\n\tList *name = list_make2(makeString(schemaName), makeString(seqName));\n\tReleaseSysCache(seqClassTuple);\n\n\tstmt->renameType = OBJECT_SEQUENCE;\n\tstmt->object = (Node *) name;\n\tstmt->relation = makeRangeVar(schemaName, seqName, -1);\n\tstmt->newname = newName;\n\n\treturn stmt;\n}\n\n\n/*\n * CreateRenameStatement creates a rename statement for an existing object to rename the\n * object to newName.\n */\nRenameStmt *\nCreateRenameStatement(const ObjectAddress *address, char *newName)\n{\n\tswitch (getObjectClass(address))\n\t{\n\t\tcase OCLASS_COLLATION:\n\t\t{\n\t\t\treturn CreateRenameCollationStmt(address, newName);\n\t\t}\n\n\t\tcase OCLASS_PROC:\n\t\t{\n\t\t\treturn CreateRenameProcStmt(address, newName);\n\t\t}\n\n\t\tcase OCLASS_TSCONFIG:\n\t\t{\n\t\t\treturn CreateRenameTextSearchStmt(address, newName);\n\t\t}\n\n\t\tcase OCLASS_TYPE:\n\t\t{\n\t\t\treturn CreateRenameTypeStmt(address, newName);\n\t\t}\n\n\t\tcase OCLASS_CLASS:\n\t\t{\n\t\t\tchar relKind = get_rel_relkind(address->objectId);\n\t\t\tif (relKind == RELKIND_SEQUENCE)\n\t\t\t{\n\t\t\t\treturn CreateRenameSequenceStmt(address, newName);\n\t\t\t}\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tereport(ERROR, (errmsg(\"unsupported object to construct a rename statement\"),\n\t\t\t\t\terrdetail(\"unable to generate a parsetree for the rename\")));\n}\n"
  },
  {
    "path": "src/backend/distributed/worker/worker_data_fetch_protocol.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_data_fetch_protocol.c\n *\n * Routines for fetching remote resources from other nodes to this worker node,\n * and materializing these resources on this node if necessary.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"libpq-fe.h\"\n#include \"miscadmin.h\"\n\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_type.h\"\n#include \"commands/copy.h\"\n#include \"commands/dbcommands.h\"\n#include \"commands/extension.h\"\n#include \"commands/sequence.h\"\n#include \"executor/spi.h\"\n#include \"nodes/makefuncs.h\"\n#include \"parser/parse_relation.h\"\n#include \"storage/lmgr.h\"\n#include \"tcop/tcopprot.h\"\n#include \"tcop/utility.h\"\n#include \"utils/builtins.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/regproc.h\"\n#include \"utils/varlena.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands.h\"\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/deparser.h\"\n#include \"distributed/intermediate_results.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/multi_logical_optimizer.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/remote_commands.h\"\n#include \"distributed/resource_lock.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_create_or_replace.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n/* Local functions forward declarations */\nstatic bool check_log_statement(List *stmt_list);\nstatic void AlterSequenceMinMax(Oid sequenceId, char *schemaName, char *sequenceName,\n\t\t\t\t\t\t\t\tOid sequenceTypeId);\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(worker_apply_shard_ddl_command);\nPG_FUNCTION_INFO_V1(worker_apply_inter_shard_ddl_command);\nPG_FUNCTION_INFO_V1(worker_apply_sequence_command);\nPG_FUNCTION_INFO_V1(worker_adjust_identity_column_seq_ranges);\nPG_FUNCTION_INFO_V1(worker_append_table_to_shard);\nPG_FUNCTION_INFO_V1(worker_nextval);\n\n\n/*\n * worker_apply_shard_ddl_command extends table, index, or constraint names in\n * the given DDL command. The function then applies this extended DDL command\n * against the database.\n */\nDatum\nworker_apply_shard_ddl_command(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tuint64 shardId = PG_GETARG_INT64(0);\n\ttext *schemaNameText = PG_GETARG_TEXT_P(1);\n\ttext *ddlCommandText = PG_GETARG_TEXT_P(2);\n\n\tchar *schemaName = text_to_cstring(schemaNameText);\n\tconst char *ddlCommand = text_to_cstring(ddlCommandText);\n\tNode *ddlCommandNode = ParseTreeNode(ddlCommand);\n\n\t/* extend names in ddl command and apply extended command */\n\tRelayEventExtendNames(ddlCommandNode, schemaName, shardId);\n\tProcessUtilityParseTree(ddlCommandNode, ddlCommand, PROCESS_UTILITY_QUERY, NULL,\n\t\t\t\t\t\t\tNone_Receiver, NULL);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * worker_apply_inter_shard_ddl_command extends table, index, or constraint names in\n * the given DDL command. The function then applies this extended DDL command\n * against the database.\n */\nDatum\nworker_apply_inter_shard_ddl_command(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tuint64 leftShardId = PG_GETARG_INT64(0);\n\ttext *leftShardSchemaNameText = PG_GETARG_TEXT_P(1);\n\tuint64 rightShardId = PG_GETARG_INT64(2);\n\ttext *rightShardSchemaNameText = PG_GETARG_TEXT_P(3);\n\ttext *ddlCommandText = PG_GETARG_TEXT_P(4);\n\n\tchar *leftShardSchemaName = text_to_cstring(leftShardSchemaNameText);\n\tchar *rightShardSchemaName = text_to_cstring(rightShardSchemaNameText);\n\tconst char *ddlCommand = text_to_cstring(ddlCommandText);\n\tNode *ddlCommandNode = ParseTreeNode(ddlCommand);\n\n\t/* extend names in ddl command and apply extended command */\n\tRelayEventExtendNamesForInterShardCommands(ddlCommandNode, leftShardId,\n\t\t\t\t\t\t\t\t\t\t\t   leftShardSchemaName, rightShardId,\n\t\t\t\t\t\t\t\t\t\t\t   rightShardSchemaName);\n\tProcessUtilityParseTree(ddlCommandNode, ddlCommand, PROCESS_UTILITY_QUERY, NULL,\n\t\t\t\t\t\t\tNone_Receiver, NULL);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * worker_adjust_identity_column_seq_ranges takes a table oid, runs an ALTER SEQUENCE statement\n * for each identity column to adjust the minvalue and maxvalue of the sequence owned by\n * identity column such that the sequence creates globally unique values.\n * We use table oid instead of sequence name to avoid any potential conflicts between sequences of different tables. This way, we can safely iterate through identity columns on a specific table without any issues. While this may introduce a small amount of business logic to workers, it's a much safer approach overall.\n */\nDatum\nworker_adjust_identity_column_seq_ranges(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid tableRelationId = PG_GETARG_OID(0);\n\n\tEnsureTableOwner(tableRelationId);\n\n\tRelation tableRelation = relation_open(tableRelationId, AccessShareLock);\n\tTupleDesc tableTupleDesc = RelationGetDescr(tableRelation);\n\n\tbool missingSequenceOk = false;\n\n\tfor (int attributeIndex = 0; attributeIndex < tableTupleDesc->natts;\n\t\t attributeIndex++)\n\t{\n\t\tForm_pg_attribute attributeForm = TupleDescAttr(tableTupleDesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tattributeIndex);\n\n\t\t/* skip dropped columns */\n\t\tif (attributeForm->attisdropped)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (attributeForm->attidentity)\n\t\t{\n\t\t\tOid sequenceOid = getIdentitySequence(identitySequenceRelation_compat(\n\t\t\t\t\t\t\t\t\t\t\t\t\t  tableRelation),\n\t\t\t\t\t\t\t\t\t\t\t\t  attributeForm->attnum,\n\t\t\t\t\t\t\t\t\t\t\t\t  missingSequenceOk);\n\n\t\t\tOid sequenceSchemaOid = get_rel_namespace(sequenceOid);\n\t\t\tchar *sequenceSchemaName = get_namespace_name(sequenceSchemaOid);\n\t\t\tchar *sequenceName = get_rel_name(sequenceOid);\n\t\t\tOid sequenceTypeId = pg_get_sequencedef(sequenceOid)->seqtypid;\n\n\t\t\tAlterSequenceMinMax(sequenceOid, sequenceSchemaName, sequenceName,\n\t\t\t\t\t\t\t\tsequenceTypeId);\n\t\t}\n\t}\n\n\trelation_close(tableRelation, NoLock);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * worker_apply_sequence_command takes a CREATE SEQUENCE command string, runs the\n * CREATE SEQUENCE command then creates and runs an ALTER SEQUENCE statement\n * which adjusts the minvalue and maxvalue of the sequence such that the sequence\n * creates globally unique values.\n */\nDatum\nworker_apply_sequence_command(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *commandText = PG_GETARG_TEXT_P(0);\n\tOid sequenceTypeId = PG_GETARG_OID(1);\n\tconst char *commandString = text_to_cstring(commandText);\n\tNode *commandNode = ParseTreeNode(commandString);\n\n\tNodeTag nodeType = nodeTag(commandNode);\n\n\tif (nodeType != T_CreateSeqStmt)\n\t{\n\t\tereport(ERROR,\n\t\t\t\t(errmsg(\"must call worker_apply_sequence_command with a CREATE\"\n\t\t\t\t\t\t\" SEQUENCE command string\")));\n\t}\n\n\t/*\n\t * If sequence with the same name exist for different type, it must have been\n\t * stayed on that node after a rollbacked create_distributed_table operation.\n\t * We must change its name first to create the sequence with the correct type.\n\t */\n\tCreateSeqStmt *createSequenceStatement = (CreateSeqStmt *) commandNode;\n\tRenameExistingSequenceWithDifferentTypeIfExists(createSequenceStatement->sequence,\n\t\t\t\t\t\t\t\t\t\t\t\t\tsequenceTypeId);\n\n\t/* run the CREATE SEQUENCE command */\n\tProcessUtilityParseTree(commandNode, commandString, PROCESS_UTILITY_QUERY, NULL,\n\t\t\t\t\t\t\tNone_Receiver, NULL);\n\tCommandCounterIncrement();\n\n\tOid sequenceRelationId = RangeVarGetRelid(createSequenceStatement->sequence,\n\t\t\t\t\t\t\t\t\t\t\t  AccessShareLock, false);\n\tchar *sequenceName = createSequenceStatement->sequence->relname;\n\tchar *sequenceSchema = createSequenceStatement->sequence->schemaname;\n\n\tAssert(sequenceRelationId != InvalidOid);\n\n\tAlterSequenceMinMax(sequenceRelationId, sequenceSchema, sequenceName, sequenceTypeId);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * ExtractShardIdFromTableName tries to extract shard id from the given table name,\n * and returns the shard id if table name is formatted as shard name.\n * Else, the function returns INVALID_SHARD_ID.\n */\nuint64\nExtractShardIdFromTableName(const char *tableName, bool missingOk)\n{\n\tchar *shardIdStringEnd = NULL;\n\n\t/* find the last underscore and increment for shardId string */\n\tchar *shardIdString = strrchr(tableName, SHARD_NAME_SEPARATOR);\n\tif (shardIdString == NULL && !missingOk)\n\t{\n\t\tereport(ERROR, (errmsg(\"could not extract shardId from table name \\\"%s\\\"\",\n\t\t\t\t\t\t\t   tableName)));\n\t}\n\telse if (shardIdString == NULL && missingOk)\n\t{\n\t\treturn INVALID_SHARD_ID;\n\t}\n\n\tshardIdString++;\n\n\terrno = 0;\n\tuint64 shardId = strtou64(shardIdString, &shardIdStringEnd, 0);\n\n\tif (errno != 0 || (*shardIdStringEnd != '\\0'))\n\t{\n\t\tif (!missingOk)\n\t\t{\n\t\t\tereport(ERROR, (errmsg(\"could not extract shardId from table name \\\"%s\\\"\",\n\t\t\t\t\t\t\t\t   tableName)));\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn INVALID_SHARD_ID;\n\t\t}\n\t}\n\n\treturn shardId;\n}\n\n\n/*\n * Parses the given DDL command, and returns the tree node for parsed command.\n */\nNode *\nParseTreeNode(const char *ddlCommand)\n{\n\tNode *parseTreeNode = ParseTreeRawStmt(ddlCommand);\n\n\tparseTreeNode = ((RawStmt *) parseTreeNode)->stmt;\n\n\treturn parseTreeNode;\n}\n\n\n/*\n * Parses the given DDL command, and returns the tree node for parsed command.\n */\nNode *\nParseTreeRawStmt(const char *ddlCommand)\n{\n\tList *parseTreeList = pg_parse_query(ddlCommand);\n\n\t/* log immediately if dictated by log statement */\n\tif (check_log_statement(parseTreeList))\n\t{\n\t\tereport(LOG, (errmsg(\"statement: %s\", ddlCommand),\n\t\t\t\t\t  errhidestmt(true)));\n\t}\n\n\tuint32 parseTreeCount = list_length(parseTreeList);\n\tif (parseTreeCount != 1)\n\t{\n\t\tereport(ERROR, (errmsg(\"cannot execute multiple utility events\")));\n\t}\n\n\t/*\n\t * xact.c rejects certain commands that are unsafe to run inside transaction\n\t * blocks. Since we only apply commands that relate to creating tables and\n\t * those commands are safe, we can safely set the ProcessUtilityContext to\n\t * PROCESS_UTILITY_TOPLEVEL.\n\t */\n\tNode *parseTreeNode = (Node *) linitial(parseTreeList);\n\n\treturn parseTreeNode;\n}\n\n\n/*\n * worker_append_table_to_shard is deprecated.\n */\nDatum\nworker_append_table_to_shard(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"worker_append_table_to_shard has been deprecated\")));\n}\n\n\n/*\n * worker_nextval calculates nextval() in worker nodes\n * for int and smallint column default types\n * TODO: not error out but get the proper nextval()\n */\nDatum\nworker_nextval(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\n\t\t\t\t\t\t\"nextval(sequence) calls in worker nodes are not supported\"\n\t\t\t\t\t\t\" for column defaults of type int or smallint\")));\n\tPG_RETURN_INT32(0);\n}\n\n\n/*\n * check_log_statement is a copy of postgres' check_log_statement function and\n * returns whether a statement ought to be logged or not.\n */\nstatic bool\ncheck_log_statement(List *statementList)\n{\n\tif (log_statement == LOGSTMT_NONE)\n\t{\n\t\treturn false;\n\t}\n\n\tif (log_statement == LOGSTMT_ALL)\n\t{\n\t\treturn true;\n\t}\n\n\t/* else we have to inspect the statement(s) to see whether to log */\n\tNode *statement = NULL;\n\tforeach_declared_ptr(statement, statementList)\n\t{\n\t\tif (GetCommandLogLevel(statement) <= log_statement)\n\t\t{\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n\n/*\n * AlterSequenceMinMax arranges the min and max value of the given sequence. The function\n * creates ALTER SEQUENCE statemenet which sets the start, minvalue and maxvalue of\n * the given sequence.\n *\n * The function provides the uniqueness by shifting the start of the sequence by\n * GetLocalGroupId() << 48 + 1 and sets a maxvalue which stops it from passing out any\n * values greater than: (GetLocalGroupID() + 1) << 48.\n *\n * For serial we only have 32 bits and therefore shift by 28, and for smallserial\n * we only have 16 bits and therefore shift by 12.\n *\n * This is to ensure every group of workers passes out values from a unique range,\n * and therefore that all values generated for the sequence are globally unique.\n */\nstatic void\nAlterSequenceMinMax(Oid sequenceId, char *schemaName, char *sequenceName,\n\t\t\t\t\tOid sequenceTypeId)\n{\n\tForm_pg_sequence sequenceData = pg_get_sequencedef(sequenceId);\n\tint64 sequenceMaxValue = sequenceData->seqmax;\n\tint64 sequenceMinValue = sequenceData->seqmin;\n\tint valueBitLength = 48;\n\n\t/*\n\t * For int and smallint, we don't currently support insertion from workers\n\t * Check issue #5126 and PR #5254 for details.\n\t * https://github.com/citusdata/citus/issues/5126\n\t * So, no need to alter sequence min/max for now\n\t * We call setval(sequence, maxvalue) such that manually using\n\t * nextval(sequence) in the workers will error out as well.\n\t */\n\tif (sequenceTypeId != INT8OID)\n\t{\n\t\tDirectFunctionCall2(setval_oid,\n\t\t\t\t\t\t\tObjectIdGetDatum(sequenceId),\n\t\t\t\t\t\t\tInt64GetDatum(sequenceMaxValue));\n\t\treturn;\n\t}\n\n\t/* calculate min/max values that the sequence can generate in this worker */\n\tint64 startValue = (((int64) GetLocalGroupId()) << valueBitLength) + 1;\n\tint64 maxValue = startValue + ((int64) 1 << valueBitLength);\n\n\t/*\n\t * We alter the sequence if the previously set min and max values are not equal to\n\t * their correct values.\n\t */\n\tif (sequenceMinValue != startValue || sequenceMaxValue != maxValue)\n\t{\n\t\tStringInfo startNumericString = makeStringInfo();\n\t\tStringInfo maxNumericString = makeStringInfo();\n\t\tAlterSeqStmt *alterSequenceStatement = makeNode(AlterSeqStmt);\n\t\tconst char *dummyString = \"-\";\n\n\t\talterSequenceStatement->sequence = makeRangeVar(schemaName, sequenceName, -1);\n\n\t\t/*\n\t\t * DefElem->arg can only hold literal ints up to int4, in order to represent\n\t\t * larger numbers we need to construct a float represented as a string.\n\t\t */\n\t\tappendStringInfo(startNumericString, INT64_FORMAT, startValue);\n\t\tNode *startFloatArg = (Node *) makeFloat(startNumericString->data);\n\n\t\tappendStringInfo(maxNumericString, INT64_FORMAT, maxValue);\n\t\tNode *maxFloatArg = (Node *) makeFloat(maxNumericString->data);\n\n\t\tSetDefElemArg(alterSequenceStatement, \"start\", startFloatArg);\n\t\tSetDefElemArg(alterSequenceStatement, \"minvalue\", startFloatArg);\n\t\tSetDefElemArg(alterSequenceStatement, \"maxvalue\", maxFloatArg);\n\n\t\tSetDefElemArg(alterSequenceStatement, \"restart\", startFloatArg);\n\n\t\t/* since the command is an AlterSeqStmt, a dummy command string works fine */\n\t\tProcessUtilityParseTree((Node *) alterSequenceStatement, dummyString,\n\t\t\t\t\t\t\t\tPROCESS_UTILITY_QUERY, NULL, None_Receiver, NULL);\n\t}\n}\n\n\n/*\n * SetDefElemArg scans through all the DefElem's of an AlterSeqStmt and\n * and sets the arg of the one with a defname of name to arg.\n *\n * If a DefElem with the given defname does not exist it is created and\n * added to the AlterSeqStmt.\n */\nvoid\nSetDefElemArg(AlterSeqStmt *statement, const char *name, Node *arg)\n{\n\tDefElem *defElem = NULL;\n\tforeach_declared_ptr(defElem, statement->options)\n\t{\n\t\tif (strcmp(defElem->defname, name) == 0)\n\t\t{\n\t\t\tpfree(defElem->arg);\n\t\t\tdefElem->arg = arg;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tdefElem = makeDefElem((char *) name, arg, -1);\n\n\tstatement->options = lappend(statement->options, defElem);\n}\n"
  },
  {
    "path": "src/backend/distributed/worker/worker_drop_protocol.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_drop_protocol.c\n *\n * Routines for dropping distributed tables and their metadata on worker nodes.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"access/genam.h\"\n#include \"access/heapam.h\"\n#include \"access/xact.h\"\n#include \"catalog/dependency.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_foreign_server.h\"\n#include \"foreign/foreign.h\"\n#include \"tcop/utility.h\"\n#include \"utils/builtins.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/distribution_column.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/metadata/distobject.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_partitioning_utils.h\"\n#include \"distributed/worker_protocol.h\"\n\nPG_FUNCTION_INFO_V1(worker_drop_distributed_table);\nPG_FUNCTION_INFO_V1(worker_drop_shell_table);\nPG_FUNCTION_INFO_V1(worker_drop_sequence_dependency);\n\nstatic void WorkerDropDistributedTable(Oid relationId);\n\n\n/*\n * worker_drop_distributed_table drops the distributed table with the given oid,\n * then, removes the associated rows from pg_dist_partition, pg_dist_shard and\n * pg_dist_placement.\n *\n * Note that drop fails if any dependent objects are present for any of the\n * distributed tables. Also, shard placements of the distributed tables are\n * not dropped as in the case of \"DROP TABLE distributed_table;\" command.\n *\n * The function errors out if the input relation Oid is not a regular or foreign table.\n * The function is meant to be called only by the coordinator, therefore requires\n * superuser privileges.\n */\nDatum\nworker_drop_distributed_table(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *relationName = PG_GETARG_TEXT_P(0);\n\tOid relationId = ResolveRelationId(relationName, true);\n\n\tif (!OidIsValid(relationId))\n\t{\n\t\tereport(NOTICE, (errmsg(\"relation %s does not exist, skipping\",\n\t\t\t\t\t\t\t\ttext_to_cstring(relationName))));\n\t\tPG_RETURN_VOID();\n\t}\n\n\tEnsureTableOwner(relationId);\n\n\tif (PartitionedTable(relationId))\n\t{\n\t\t/*\n\t\t * When \"DROP SCHEMA .. CASCADE\" happens, we rely on Postgres' drop trigger\n\t\t * to send the individual DROP TABLE commands for tables.\n\t\t *\n\t\t * In case of partitioned tables, we have no control on the order of DROP\n\t\t * commands that is sent to the extension. We can try to sort while processing\n\t\t * on the coordinator, but we prefer to handle it in a more flexible manner.\n\t\t *\n\t\t * That's why, whenever we see a partitioned table, we drop all the corresponding\n\t\t * partitions first. Otherwise, WorkerDropDistributedTable() would already drop\n\t\t * the shell tables of the partitions (e.g., due to performDeletion(..CASCADE),\n\t\t * and further WorkerDropDistributedTable() on the partitions would become no-op.\n\t\t *\n\t\t * If, say one partition has already been dropped earlier, that should also be fine\n\t\t * because we read the existing partitions.\n\t\t */\n\t\tList *partitionList = PartitionList(relationId);\n\t\tOid partitionOid = InvalidOid;\n\t\tforeach_declared_oid(partitionOid, partitionList)\n\t\t{\n\t\t\tWorkerDropDistributedTable(partitionOid);\n\t\t}\n\t}\n\n\tWorkerDropDistributedTable(relationId);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * WorkerDropDistributedTable is a helper function for worker_drop_distributed_table, see\n * tha function for the details.\n */\nstatic void\nWorkerDropDistributedTable(Oid relationId)\n{\n\t/* first check the relation type */\n\tRelation distributedRelation = relation_open(relationId, AccessShareLock);\n\n\tEnsureRelationKindSupported(relationId);\n\n\t/* close the relation since we do not need anymore */\n\trelation_close(distributedRelation, AccessShareLock);\n\n\t/* prepare distributedTableObject for dropping the table */\n\tObjectAddress *distributedTableObject = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*distributedTableObject, RelationRelationId, relationId);\n\n\t/* Drop dependent sequences from pg_dist_object */\n\tList *ownedSequences = getOwnedSequences(relationId);\n\n\tOid ownedSequenceOid = InvalidOid;\n\tforeach_declared_oid(ownedSequenceOid, ownedSequences)\n\t{\n\t\tObjectAddress ownedSequenceAddress = { 0 };\n\t\tObjectAddressSet(ownedSequenceAddress, RelationRelationId, ownedSequenceOid);\n\t\tUnmarkObjectDistributed(&ownedSequenceAddress);\n\t}\n\n\tUnmarkObjectDistributed(distributedTableObject);\n\n\t/*\n\t * Remove metadata before object's itself to make functions no-op within\n\t * drop event trigger for undistributed objects on worker nodes except\n\t * removing pg_dist_object entries.\n\t */\n\tList *shardList = LoadShardList(relationId);\n\tuint64 *shardIdPointer = NULL;\n\tforeach_declared_ptr(shardIdPointer, shardList)\n\t{\n\t\tuint64 shardId = *shardIdPointer;\n\n\t\tList *shardPlacementList = ShardPlacementList(shardId);\n\t\tShardPlacement *placement = NULL;\n\t\tforeach_declared_ptr(placement, shardPlacementList)\n\t\t{\n\t\t\t/* delete the row from pg_dist_placement */\n\t\t\tDeleteShardPlacementRow(placement->placementId);\n\t\t}\n\n\t\t/* delete the row from pg_dist_shard */\n\t\tDeleteShardRow(shardId);\n\t}\n\n\t/* delete the row from pg_dist_partition */\n\tDeletePartitionRow(relationId);\n\n\t/*\n\t * If the table is owned by an extension, we cannot drop it, nor should we\n\t * until the user runs DROP EXTENSION. Therefore, we skip dropping the\n\t * table.\n\t */\n\tif (!IsAnyObjectAddressOwnedByExtension(list_make1(distributedTableObject), NULL))\n\t{\n\t\tStringInfo dropCommand = makeStringInfo();\n\t\tappendStringInfo(dropCommand, \"DROP%sTABLE %s CASCADE\",\n\t\t\t\t\t\t IsForeignTable(relationId) ? \" FOREIGN \" : \" \",\n\t\t\t\t\t\t generate_qualified_relation_name(relationId));\n\n\t\tNode *dropCommandNode = ParseTreeNode(dropCommand->data);\n\n\t\t/*\n\t\t * We use ProcessUtilityParseTree (instead of performDeletion) to make sure that\n\t\t * we also drop objects that depend on the table and call the drop event trigger\n\t\t * which removes them from pg_dist_object.\n\t\t */\n\t\tProcessUtilityParseTree(dropCommandNode, dropCommand->data,\n\t\t\t\t\t\t\t\tPROCESS_UTILITY_QUERY, NULL, None_Receiver, NULL);\n\t}\n}\n\n\n/*\n * worker_drop_shell_table drops the shell table of with the given distributed\n * table without deleting related entries on pg_dist_placement, pg_dist_shard\n * and pg_dist_placement. We've separated that logic since we handle object\n * dependencies and table metadata separately while activating nodes.\n */\nDatum\nworker_drop_shell_table(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\ttext *relationName = PG_GETARG_TEXT_P(0);\n\tOid relationId = ResolveRelationId(relationName, true);\n\n\tif (!OidIsValid(relationId))\n\t{\n\t\tereport(NOTICE, (errmsg(\"relation %s does not exist, skipping\",\n\t\t\t\t\t\t\t\ttext_to_cstring(relationName))));\n\t\tPG_RETURN_VOID();\n\t}\n\n\tEnsureTableOwner(relationId);\n\n\tif (GetLocalGroupId() == COORDINATOR_GROUP_ID)\n\t{\n\t\tereport(ERROR, (errmsg(\"worker_drop_shell_table is only allowed to run\"\n\t\t\t\t\t\t\t   \" on worker nodes\")));\n\t}\n\n\t/* first check the relation type */\n\tRelation distributedRelation = relation_open(relationId, AccessShareLock);\n\tEnsureRelationKindSupported(relationId);\n\n\t/* close the relation since we do not need anymore */\n\trelation_close(distributedRelation, AccessShareLock);\n\n\t/* prepare distributedTableObject for dropping the table */\n\tObjectAddress *distributedTableObject = palloc0(sizeof(ObjectAddress));\n\tObjectAddressSet(*distributedTableObject, RelationRelationId, relationId);\n\tif (IsAnyObjectAddressOwnedByExtension(list_make1(distributedTableObject), NULL))\n\t{\n\t\tPG_RETURN_VOID();\n\t}\n\n\t/* Drop dependent sequences from pg_dist_object */\n\tList *ownedSequences = getOwnedSequences(relationId);\n\n\tOid ownedSequenceOid = InvalidOid;\n\tforeach_declared_oid(ownedSequenceOid, ownedSequences)\n\t{\n\t\tObjectAddress ownedSequenceAddress = { 0 };\n\t\tObjectAddressSet(ownedSequenceAddress, RelationRelationId, ownedSequenceOid);\n\t\tUnmarkObjectDistributed(&ownedSequenceAddress);\n\t}\n\n\t/*\n\t * If the table is owned by an extension, we cannot drop it, nor should we\n\t * until the user runs DROP EXTENSION. Therefore, we skip dropping the\n\t * table and only delete the metadata.\n\t *\n\t * We drop the table with cascade since other tables may be referring to it.\n\t */\n\tperformDeletion(distributedTableObject, DROP_CASCADE,\n\t\t\t\t\tPERFORM_DELETION_INTERNAL);\n\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * worker_drop_sequence_dependency is a UDF that removes the dependency\n * of all the sequences for the given table.\n *\n * The main purpose of this UDF is to prevent dropping the sequences while\n * re-creating the same table such as changing the shard count, converting\n * a citus local table to a distributed table or re-syncing the metadata.\n */\nDatum\nworker_drop_sequence_dependency(PG_FUNCTION_ARGS)\n{\n\ttext *relationName = PG_GETARG_TEXT_P(0);\n\tOid relationId = ResolveRelationId(relationName, true);\n\n\tif (!OidIsValid(relationId))\n\t{\n\t\tereport(NOTICE, (errmsg(\"relation %s does not exist, skipping\",\n\t\t\t\t\t\t\t\ttext_to_cstring(relationName))));\n\t\tPG_RETURN_VOID();\n\t}\n\n\tEnsureTableOwner(relationId);\n\n\t/* break the dependent sequences from the table */\n\tList *ownedSequences = getOwnedSequences(relationId);\n\n\tOid ownedSequenceOid = InvalidOid;\n\tforeach_declared_oid(ownedSequenceOid, ownedSequences)\n\t{\n\t\t/* the caller doesn't want to drop the sequence, so break the dependency */\n\t\tdeleteDependencyRecordsForSpecific(RelationRelationId, ownedSequenceOid,\n\t\t\t\t\t\t\t\t\t\t   DEPENDENCY_AUTO, RelationRelationId,\n\t\t\t\t\t\t\t\t\t\t   relationId);\n\t}\n\n\tif (list_length(ownedSequences) > 0)\n\t{\n\t\t/* if we delete at least one dependency, let next commands know */\n\t\tCommandCounterIncrement();\n\t}\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/backend/distributed/worker/worker_partition_protocol.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_partition_protocol.c\n *\n * Deprecated functions related to generating re-partitioning files.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"miscadmin.h\"\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(worker_cleanup_job_schema_cache);\nPG_FUNCTION_INFO_V1(worker_create_schema);\nPG_FUNCTION_INFO_V1(worker_fetch_foreign_file);\nPG_FUNCTION_INFO_V1(worker_fetch_partition_file);\nPG_FUNCTION_INFO_V1(worker_hash_partition_table);\nPG_FUNCTION_INFO_V1(worker_merge_files_into_table);\nPG_FUNCTION_INFO_V1(worker_merge_files_and_run_query);\nPG_FUNCTION_INFO_V1(worker_range_partition_table);\nPG_FUNCTION_INFO_V1(worker_repartition_cleanup);\n\n\n/*\n * worker_range_partition_table is a deprecated function that we keep around for\n * testing downgrade scripts.\n */\nDatum\nworker_range_partition_table(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"this function is deprecated and only kept for testing \"\n\t\t\t\t\t\t   \"downgrade scripts\")));\n}\n\n\n/*\n * worker_hash_partition_table is a deprecated function that we keep around for\n * testing downgrade scripts.\n */\nDatum\nworker_hash_partition_table(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"this function is deprecated and only kept for testing \"\n\t\t\t\t\t\t   \"downgrade scripts\")));\n}\n\n\n/*\n * worker_create_schema is deprecated and only kept for testing downgrade scripts.\n */\nDatum\nworker_create_schema(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"this function is deprecated and only kept for testing \"\n\t\t\t\t\t\t   \"downgrade scripts\")));\n}\n\n\n/*\n * worker_repartition_cleanup removes the job directory and schema with the given job id .\n */\nDatum\nworker_repartition_cleanup(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"this function is deprecated and only kept for testing \"\n\t\t\t\t\t\t   \"downgrade scripts\")));\n}\n\n\n/*\n * worker_merge_files_into_table is deprecated and only kept for testing downgrade\n * scripts.\n */\nDatum\nworker_merge_files_into_table(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"this function is deprecated and only kept for testing \"\n\t\t\t\t\t\t   \"downgrade scripts\")));\n}\n\n\n/*\n * worker_merge_files_and_run_query is deprecated and only kept for testing downgrade\n * scripts.\n */\nDatum\nworker_merge_files_and_run_query(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"this function is deprecated and only kept for testing \"\n\t\t\t\t\t\t   \"downgrade scripts\")));\n}\n\n\n/*\n * worker_cleanup_job_schema_cache is deprecated and only kept for testing downgrade\n * scripts.\n */\nDatum\nworker_cleanup_job_schema_cache(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"this function is deprecated and only kept for testing \"\n\t\t\t\t\t\t   \"downgrade scripts\")));\n}\n\n\n/*\n * worker_fetch_foreign_file UDF is a stub UDF to install Citus flawlessly.\n * Otherwise we need to delete them from our sql files, which is confusing\n */\nDatum\nworker_fetch_foreign_file(PG_FUNCTION_ARGS)\n{\n\tereport(DEBUG2, (errmsg(\"this function is deprecated and no longer is used\")));\n\tPG_RETURN_VOID();\n}\n\n\n/*\n * worker_fetch_partition_file is a deprecated function that we keep around for\n * testing downgrade scripts.\n */\nDatum\nworker_fetch_partition_file(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"this function is deprecated and only kept for testing \"\n\t\t\t\t\t\t   \"downgrade scripts\")));\n}\n"
  },
  {
    "path": "src/backend/distributed/worker/worker_shard_visibility.c",
    "content": "/*\n * worker_shard_visibility.c\n *\n * Implements the functions for hiding shards on the Citus MX\n * worker (data) nodes.\n *\n * Copyright (c) Citus Data, Inc.\n */\n\n#include \"postgres.h\"\n\n#include \"miscadmin.h\"\n\n#include \"catalog/index.h\"\n#include \"catalog/namespace.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_type.h\"\n#include \"nodes/makefuncs.h\"\n#include \"nodes/nodeFuncs.h\"\n#include \"utils/lsyscache.h\"\n#include \"utils/syscache.h\"\n#include \"utils/varlena.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/local_executor.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/query_colocation_checker.h\"\n#include \"distributed/worker_protocol.h\"\n#include \"distributed/worker_shard_visibility.h\"\n\n\n/* HideShardsMode is used to determine whether to hide shards */\ntypedef enum HideShardsMode\n{\n\tCHECK_APPLICATION_NAME,\n\tHIDE_SHARDS_FROM_APPLICATION,\n\tDO_NOT_HIDE_SHARDS\n} HideShardsMode;\n\n/* Config variable managed via guc.c */\nbool OverrideTableVisibility = true;\nbool EnableManualChangesToShards = false;\n\n/* show shards when the application_name starts with one of: */\nchar *ShowShardsForAppNamePrefixes = \"\";\n\n/* cache of whether or not to hide shards */\nstatic HideShardsMode HideShards = CHECK_APPLICATION_NAME;\n\nstatic bool ShouldHideShards(void);\nstatic bool ShouldHideShardsInternal(void);\nstatic bool IsPgBgWorker(void);\nstatic bool FilterShardsFromPgclass(Node *node, void *context);\nstatic Node * CreateRelationIsAKnownShardFilter(int pgClassVarno);\nstatic bool HasRangeTableRef(Node *node, int *varno);\n\nPG_FUNCTION_INFO_V1(citus_table_is_visible);\nPG_FUNCTION_INFO_V1(relation_is_a_known_shard);\n\n\n/*\n * relation_is_a_known_shard a wrapper around RelationIsAKnownShard(), so\n * see the details there. The function also treats the indexes on shards\n * as if they were shards.\n */\nDatum\nrelation_is_a_known_shard(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\tPG_RETURN_BOOL(RelationIsAKnownShard(relationId));\n}\n\n\n/*\n * citus_table_is_visible aims to behave exactly the same with\n * pg_table_is_visible with only one exception. The former one\n * returns false for the relations that are known to be shards.\n */\nDatum\ncitus_table_is_visible(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\tchar relKind = '\\0';\n\n\t/*\n\t * We don't want to deal with not valid/existing relations\n\t * as pg_table_is_visible does.\n\t */\n\tif (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relationId)))\n\t{\n\t\tPG_RETURN_NULL();\n\t}\n\n\tif (!RelationIsVisible(relationId))\n\t{\n\t\t/* relation is not on the search path */\n\t\tPG_RETURN_BOOL(false);\n\t}\n\n\tif (RelationIsAKnownShard(relationId))\n\t{\n\t\t/*\n\t\t * If the input relation is an index we simply replace the\n\t\t * relationId with the corresponding relation to hide indexes\n\t\t * as well. See RelationIsAKnownShard() for the details and give\n\t\t * more meaningful debug message here.\n\t\t */\n\t\trelKind = get_rel_relkind(relationId);\n\t\tif (relKind == RELKIND_INDEX || relKind == RELKIND_PARTITIONED_INDEX)\n\t\t{\n\t\t\tereport(DEBUG2, (errmsg(\"skipping index \\\"%s\\\" since it belongs to a shard\",\n\t\t\t\t\t\t\t\t\tget_rel_name(relationId))));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tereport(DEBUG2, (errmsg(\"skipping relation \\\"%s\\\" since it is a shard\",\n\t\t\t\t\t\t\t\t\tget_rel_name(relationId))));\n\t\t}\n\n\t\tPG_RETURN_BOOL(false);\n\t}\n\n\tPG_RETURN_BOOL(RelationIsVisible(relationId));\n}\n\n\n/*\n * ErrorIfRelationIsAKnownShard errors out if the relation with relationId is\n * a shard relation.\n */\nvoid\nErrorIfRelationIsAKnownShard(Oid relationId)\n{\n\tif (!RelationIsAKnownShard(relationId))\n\t{\n\t\treturn;\n\t}\n\n\tconst char *relationName = get_rel_name(relationId);\n\n\tereport(ERROR, (errmsg(\"relation \\\"%s\\\" is a shard relation \", relationName)));\n}\n\n\n/*\n * ErrorIfIllegallyChangingKnownShard errors out if the relation with relationId is\n * a known shard and manual changes on known shards are disabled. This is\n * valid for only non-citus (external) connections.\n */\nvoid\nErrorIfIllegallyChangingKnownShard(Oid relationId)\n{\n\t/* allow Citus to make changes, and allow the user if explicitly enabled */\n\tif (LocalExecutorShardId != INVALID_SHARD_ID ||\n\t\tIsCitusInternalBackend() ||\n\t\tIsRebalancerInternalBackend() ||\n\t\tEnableManualChangesToShards)\n\t{\n\t\treturn;\n\t}\n\n\tif (RelationIsAKnownShard(relationId))\n\t{\n\t\tconst char *relationName = get_rel_name(relationId);\n\t\tereport(ERROR, (errmsg(\"cannot modify \\\"%s\\\" because it is a shard of \"\n\t\t\t\t\t\t\t   \"a distributed table\",\n\t\t\t\t\t\t\t   relationName),\n\t\t\t\t\t\terrhint(\"Use the distributed table or set \"\n\t\t\t\t\t\t\t\t\"citus.enable_manual_changes_to_shards to on \"\n\t\t\t\t\t\t\t\t\"to modify shards directly\")));\n\t}\n}\n\n\n/*\n * RelationIsAKnownShard gets a relationId, check whether it's a shard of\n * any distributed table.\n */\nbool\nRelationIsAKnownShard(Oid shardRelationId)\n{\n\tbool missingOk = true;\n\tchar relKind = '\\0';\n\n\tif (!OidIsValid(shardRelationId))\n\t{\n\t\t/* we cannot continue without a valid Oid */\n\t\treturn false;\n\t}\n\n\tif (IsCoordinator())\n\t{\n\t\tbool coordinatorIsKnown = false;\n\t\tPrimaryNodeForGroup(0, &coordinatorIsKnown);\n\n\t\tif (!coordinatorIsKnown)\n\t\t{\n\t\t\t/*\n\t\t\t * We're not interested in shards in the coordinator\n\t\t\t * or non-mx worker nodes, unless the coordinator is\n\t\t\t * in pg_dist_node.\n\t\t\t */\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/*\n\t * We do not take locks here, because that might block a query on pg_class.\n\t */\n\n\tif (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(shardRelationId)))\n\t{\n\t\t/* relation does not exist */\n\t\treturn false;\n\t}\n\n\t/*\n\t * If the input relation is an index we simply replace the\n\t * relationId with the corresponding relation to hide indexes\n\t * as well.\n\t */\n\trelKind = get_rel_relkind(shardRelationId);\n\tif (relKind == RELKIND_INDEX || relKind == RELKIND_PARTITIONED_INDEX)\n\t{\n\t\tshardRelationId = IndexGetRelation(shardRelationId, false);\n\t}\n\n\t/* get the shard's relation name */\n\tchar *shardRelationName = get_rel_name(shardRelationId);\n\n\tuint64 shardId = ExtractShardIdFromTableName(shardRelationName, missingOk);\n\tif (shardId == INVALID_SHARD_ID)\n\t{\n\t\t/*\n\t\t * The format of the table name does not align with\n\t\t * our shard name definition.\n\t\t */\n\t\treturn false;\n\t}\n\n\t/* try to get the relation id */\n\tOid relationId = LookupShardRelationFromCatalog(shardId, true);\n\tif (!OidIsValid(relationId))\n\t{\n\t\t/* there is no such relation */\n\t\treturn false;\n\t}\n\n\t/* verify that their namespaces are the same */\n\tif (get_rel_namespace(shardRelationId) != get_rel_namespace(relationId))\n\t{\n\t\treturn false;\n\t}\n\n\t/*\n\t * Now get the relation name and append the shardId to it. We need\n\t * to do that because otherwise a local table with a valid shardId\n\t * appended to its name could be misleading.\n\t */\n\tchar *generatedRelationName = get_rel_name(relationId);\n\tAppendShardIdToName(&generatedRelationName, shardId);\n\tif (strncmp(shardRelationName, generatedRelationName, NAMEDATALEN) == 0)\n\t{\n\t\t/* we found the distributed table that the input shard belongs to */\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * HideShardsFromSomeApplications transforms queries to pg_class to\n * filter out known shards if the application_name does not match any of\n * the prefixes in citus.show_shards_for_app_name_prefix.\n */\nvoid\nHideShardsFromSomeApplications(Query *query)\n{\n\tif (!OverrideTableVisibility || HideShards == DO_NOT_HIDE_SHARDS ||\n\t\t!CitusHasBeenLoaded() || !CheckCitusVersion(DEBUG2))\n\t{\n\t\treturn;\n\t}\n\n\tif (ShouldHideShards())\n\t{\n\t\tFilterShardsFromPgclass((Node *) query, NULL);\n\t}\n}\n\n\n/*\n * ShouldHideShards returns whether we should hide shards in the current\n * session. It only checks the application_name once and then uses a\n * cached response unless either the application_name or\n * citus.show_shards_for_app_name_prefix changes.\n */\nstatic bool\nShouldHideShards(void)\n{\n\tif (HideShards == CHECK_APPLICATION_NAME)\n\t{\n\t\tif (ShouldHideShardsInternal())\n\t\t{\n\t\t\tHideShards = HIDE_SHARDS_FROM_APPLICATION;\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tHideShards = DO_NOT_HIDE_SHARDS;\n\t\t\treturn false;\n\t\t}\n\t}\n\telse\n\t{\n\t\treturn HideShards == HIDE_SHARDS_FROM_APPLICATION;\n\t}\n}\n\n\n/*\n * ResetHideShardsDecision resets the decision whether to hide shards.\n */\nvoid\nResetHideShardsDecision(void)\n{\n\tHideShards = CHECK_APPLICATION_NAME;\n}\n\n\n/*\n * ShouldHideShardsInternal determines whether we should hide shards based on\n * the current application name.\n */\nstatic bool\nShouldHideShardsInternal(void)\n{\n\tif (MyBackendType == B_BG_WORKER)\n\t{\n\t\tif (IsPgBgWorker())\n\t\t{\n\t\t\t/*\n\t\t\t * If a background worker belongs to Postgres, we should\n\t\t\t * never hide shards. For other background workers, enforce\n\t\t\t * the application_name check below.\n\t\t\t */\n\t\t\treturn false;\n\t\t}\n\t}\n\telse if (MyBackendType != B_BACKEND && MyBackendType != B_WAL_SENDER)\n\t{\n\t\t/*\n\t\t * We are aiming only to hide shards from client\n\t\t * backends or certain background workers(see above),\n\t\t */\n\t\treturn false;\n\t}\n\n\tif (IsCitusInternalBackend() || IsRebalancerInternalBackend() ||\n\t\tIsCitusRunCommandBackend() || IsCitusShardTransferBackend())\n\t{\n\t\t/* we never hide shards from Citus */\n\t\treturn false;\n\t}\n\n\tList *prefixList = NIL;\n\n\t/* SplitGUCList scribbles on the input */\n\tchar *splitCopy = pstrdup(ShowShardsForAppNamePrefixes);\n\n\tif (!SplitGUCList(splitCopy, ',', &prefixList))\n\t{\n\t\t/* invalid GUC value, ignore */\n\t\treturn true;\n\t}\n\n\tchar *appNamePrefix = NULL;\n\tforeach_declared_ptr(appNamePrefix, prefixList)\n\t{\n\t\t/* never hide shards when one of the prefixes is * */\n\t\tif (strcmp(appNamePrefix, \"*\") == 0)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t/* compare only the first first <prefixLength> characters */\n\t\tint prefixLength = strlen(appNamePrefix);\n\t\tif (strncmp(application_name, appNamePrefix, prefixLength) == 0)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/* default behaviour: hide shards */\n\treturn true;\n}\n\n\n/*\n * IsPgBgWorker returns true if the current background worker\n * belongs to Postgres.\n */\nstatic bool\nIsPgBgWorker(void)\n{\n\tAssert(MyBackendType == B_BG_WORKER);\n\n\tif (MyBgworkerEntry)\n\t{\n\t\treturn strcmp(MyBgworkerEntry->bgw_library_name, \"postgres\") == 0;\n\t}\n\n\treturn false;\n}\n\n\n/*\n * FilterShardsFromPgclass adds a \"relation_is_a_known_shard(oid) IS NOT TRUE\"\n * filter to the quals of queries that query pg_class.\n */\nstatic bool\nFilterShardsFromPgclass(Node *node, void *context)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, Query))\n\t{\n\t\tQuery *query = (Query *) node;\n\t\tMemoryContext queryContext = GetMemoryChunkContext(query);\n\n\t\t/*\n\t\t * We process the whole rtable rather than visiting individual RangeTblEntry's\n\t\t * in the walker, since we need to know the varno to generate the right\n\t\t * filter.\n\t\t */\n\t\tint varno = 0;\n\t\tRangeTblEntry *rangeTableEntry = NULL;\n\n\t\tforeach_declared_ptr(rangeTableEntry, query->rtable)\n\t\t{\n\t\t\tvarno++;\n\n\t\t\tif (rangeTableEntry->rtekind != RTE_RELATION ||\n\t\t\t\trangeTableEntry->relid != RelationRelationId)\n\t\t\t{\n\t\t\t\t/* not pg_class */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Skip if pg_class is not actually queried. This is possible on\n\t\t\t * INSERT statements that insert into pg_class.\n\t\t\t */\n\t\t\tif (!expression_tree_walker((Node *) query->jointree->fromlist,\n\t\t\t\t\t\t\t\t\t\tHasRangeTableRef, &varno))\n\t\t\t{\n\t\t\t\t/* the query references pg_class */\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* make sure the expression is in the right memory context */\n\t\t\tMemoryContext originalContext = MemoryContextSwitchTo(queryContext);\n\n\t\t\t/* add relation_is_a_known_shard(oid) IS NOT TRUE to the quals of the query */\n\t\t\tNode *newQual = CreateRelationIsAKnownShardFilter(varno);\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\n\t\t\t/*\n\t\t\t * In PG17, MERGE queries introduce a new struct `mergeJoinCondition`.\n\t\t\t * We need to handle this condition safely.\n\t\t\t */\n\t\t\tif (query->mergeJoinCondition != NULL)\n\t\t\t{\n\t\t\t\t/* Add the filter to mergeJoinCondition */\n\t\t\t\tquery->mergeJoinCondition = (Node *) makeBoolExpr(\n\t\t\t\t\tAND_EXPR,\n\t\t\t\t\tlist_make2(query->mergeJoinCondition, newQual),\n\t\t\t\t\t-1);\n\t\t\t}\n\t\t\telse\n#endif\n\t\t\t{\n\t\t\t\t/* Handle older versions or queries without mergeJoinCondition */\n\t\t\t\tNode *oldQuals = query->jointree->quals;\n\t\t\t\tif (oldQuals)\n\t\t\t\t{\n\t\t\t\t\tquery->jointree->quals = (Node *) makeBoolExpr(\n\t\t\t\t\t\tAND_EXPR,\n\t\t\t\t\t\tlist_make2(oldQuals, newQual),\n\t\t\t\t\t\t-1);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tquery->jointree->quals = newQual;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tMemoryContextSwitchTo(originalContext);\n\t\t}\n\n\t\treturn query_tree_walker((Query *) node, FilterShardsFromPgclass, context, 0);\n\t}\n\n\treturn expression_tree_walker(node, FilterShardsFromPgclass, context);\n}\n\n\n/*\n * HasRangeTableRef passed to expression_tree_walker to check if a node is a\n * RangeTblRef of the given varno is present in a fromlist.\n */\nstatic bool\nHasRangeTableRef(Node *node, int *varno)\n{\n\tif (node == NULL)\n\t{\n\t\treturn false;\n\t}\n\n\tif (IsA(node, RangeTblRef))\n\t{\n\t\tRangeTblRef *rangeTblRef = (RangeTblRef *) node;\n\t\treturn rangeTblRef->rtindex == *varno;\n\t}\n\n\treturn expression_tree_walker(node, HasRangeTableRef, varno);\n}\n\n\n/*\n * CreateRelationIsAKnownShardFilter constructs an expression of the form:\n * pg_catalog.relation_is_a_known_shard(oid) IS NOT TRUE\n *\n * The difference between \"NOT pg_catalog.relation_is_a_known_shard(oid)\" and\n * \"pg_catalog.relation_is_a_known_shard(oid) IS NOT TRUE\" is that the former\n * will return FALSE if the function returns NULL, while the second will return\n * TRUE. This difference is important in the case of outer joins, because this\n * filter might be applied on an oid that is then NULL.\n */\nstatic Node *\nCreateRelationIsAKnownShardFilter(int pgClassVarno)\n{\n\t/* oid is always the first column */\n\tAttrNumber oidAttNum = 1;\n\n\tVar *oidVar = makeVar(pgClassVarno, oidAttNum, OIDOID, -1, InvalidOid, 0);\n\n\t/* build the call to read_intermediate_result */\n\tFuncExpr *funcExpr = makeNode(FuncExpr);\n\tfuncExpr->funcid = RelationIsAKnownShardFuncId();\n\tfuncExpr->funcretset = false;\n\tfuncExpr->funcvariadic = false;\n\tfuncExpr->funcformat = 0;\n\tfuncExpr->funccollid = 0;\n\tfuncExpr->inputcollid = 0;\n\tfuncExpr->location = -1;\n\tfuncExpr->args = list_make1(oidVar);\n\n\tBooleanTest *notExpr = makeNode(BooleanTest);\n\tnotExpr->booltesttype = IS_NOT_TRUE;\n\tnotExpr->arg = (Expr *) funcExpr;\n\tnotExpr->location = -1;\n\n\treturn (Node *) notExpr;\n}\n"
  },
  {
    "path": "src/backend/distributed/worker/worker_sql_task_protocol.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_sql_task_protocol.c\n *\n * Routines for executing SQL tasks.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n/* necessary to get S_IRUSR, S_IWUSR definitions on illumos */\n#include <sys/stat.h>\n\n#include \"postgres.h\"\n\n#include \"funcapi.h\"\n#include \"pgstat.h\"\n\n#include \"utils/builtins.h\"\n#include \"utils/memutils.h\"\n\n#include \"distributed/commands/multi_copy.h\"\n#include \"distributed/multi_executor.h\"\n#include \"distributed/transmit.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_protocol.h\"\n\n\n#define COPY_BUFFER_SIZE (4 * 1024 * 1024)\n\n/* TaskFileDestReceiver can be used to stream results into a file */\ntypedef struct TaskFileDestReceiver\n{\n\t/* public DestReceiver interface */\n\tDestReceiver pub;\n\n\t/* descriptor of the tuples that are sent to the worker */\n\tTupleDesc tupleDescriptor;\n\n\t/* context for per-tuple memory allocation */\n\tMemoryContext tupleContext;\n\n\t/* MemoryContext for DestReceiver session */\n\tMemoryContext memoryContext;\n\n\t/* output file */\n\tchar *filePath;\n\tFileCompat fileCompat;\n\tbool binaryCopyFormat;\n\n\t/* state on how to copy out data types */\n\tCopyOutState copyOutState;\n\tFmgrInfo *columnOutputFunctions;\n\n\t/* statistics */\n\tuint64 tuplesSent;\n\tuint64 bytesSent;\n} TaskFileDestReceiver;\n\n\nstatic void TaskFileDestReceiverStartup(DestReceiver *dest, int operation,\n\t\t\t\t\t\t\t\t\t\tTupleDesc inputTupleDescriptor);\nstatic bool TaskFileDestReceiverReceive(TupleTableSlot *slot, DestReceiver *dest);\nstatic void WriteToLocalFile(StringInfo copyData, TaskFileDestReceiver *taskFileDest);\nstatic void TaskFileDestReceiverShutdown(DestReceiver *destReceiver);\nstatic void TaskFileDestReceiverDestroy(DestReceiver *destReceiver);\n\n\n/* exports for SQL callable functions */\nPG_FUNCTION_INFO_V1(worker_execute_sql_task);\n\n\n/*\n * worker_execute_sql_task executes a query and writes the results to\n * a file according to the usual task naming scheme.\n */\nDatum\nworker_execute_sql_task(PG_FUNCTION_ARGS)\n{\n\tereport(ERROR, (errmsg(\"This UDF is deprecated.\")));\n\n\tPG_RETURN_INT64(0);\n}\n\n\n/*\n * CreateFileDestReceiver creates a DestReceiver for writing query results\n * to a file.\n */\nDestReceiver *\nCreateFileDestReceiver(char *filePath, MemoryContext tupleContext, bool binaryCopyFormat)\n{\n\tTaskFileDestReceiver *taskFileDest = (TaskFileDestReceiver *) palloc0(\n\t\tsizeof(TaskFileDestReceiver));\n\n\t/* set up the DestReceiver function pointers */\n\ttaskFileDest->pub.receiveSlot = TaskFileDestReceiverReceive;\n\ttaskFileDest->pub.rStartup = TaskFileDestReceiverStartup;\n\ttaskFileDest->pub.rShutdown = TaskFileDestReceiverShutdown;\n\ttaskFileDest->pub.rDestroy = TaskFileDestReceiverDestroy;\n\ttaskFileDest->pub.mydest = DestCopyOut;\n\n\t/* set up output parameters */\n\ttaskFileDest->tupleContext = tupleContext;\n\ttaskFileDest->memoryContext = CurrentMemoryContext;\n\ttaskFileDest->filePath = pstrdup(filePath);\n\ttaskFileDest->binaryCopyFormat = binaryCopyFormat;\n\n\treturn (DestReceiver *) taskFileDest;\n}\n\n\n/*\n * TaskFileDestReceiverStartup implements the rStartup interface of\n * TaskFileDestReceiver. It opens the destination file and sets up\n * the CopyOutState.\n */\nstatic void\nTaskFileDestReceiverStartup(DestReceiver *dest, int operation,\n\t\t\t\t\t\t\tTupleDesc inputTupleDescriptor)\n{\n\tTaskFileDestReceiver *taskFileDest = (TaskFileDestReceiver *) dest;\n\n\tconst char *delimiterCharacter = \"\\t\";\n\tconst char *nullPrintCharacter = \"\\\\N\";\n\n\tconst int fileFlags = (O_APPEND | O_CREAT | O_RDWR | O_TRUNC | PG_BINARY);\n\n\t/* use the memory context that was in place when the DestReceiver was created */\n\tMemoryContext oldContext = MemoryContextSwitchTo(taskFileDest->memoryContext);\n\n\ttaskFileDest->tupleDescriptor = inputTupleDescriptor;\n\n\t/* define how tuples will be serialised */\n\tCopyOutState copyOutState = (CopyOutState) palloc0(sizeof(CopyOutStateData));\n\tcopyOutState->delim = (char *) delimiterCharacter;\n\tcopyOutState->null_print = (char *) nullPrintCharacter;\n\tcopyOutState->null_print_client = (char *) nullPrintCharacter;\n\tcopyOutState->binary = taskFileDest->binaryCopyFormat;\n\tcopyOutState->fe_msgbuf = makeStringInfo();\n\tcopyOutState->rowcontext = taskFileDest->tupleContext;\n\ttaskFileDest->copyOutState = copyOutState;\n\n\ttaskFileDest->columnOutputFunctions = ColumnOutputFunctions(inputTupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcopyOutState->binary);\n\n\ttaskFileDest->fileCompat = FileCompatFromFileStart(FileOpenForTransmit(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   taskFileDest->filePath,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   fileFlags));\n\n\tif (copyOutState->binary)\n\t{\n\t\t/* write headers when using binary encoding */\n\t\tAppendCopyBinaryHeaders(copyOutState);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n}\n\n\n/*\n * TaskFileDestReceiverReceive implements the receiveSlot function of\n * TaskFileDestReceiver. It takes a TupleTableSlot and writes the contents\n * to a local file.\n */\nstatic bool\nTaskFileDestReceiverReceive(TupleTableSlot *slot, DestReceiver *dest)\n{\n\tTaskFileDestReceiver *taskFileDest = (TaskFileDestReceiver *) dest;\n\n\tTupleDesc tupleDescriptor = taskFileDest->tupleDescriptor;\n\n\tCopyOutState copyOutState = taskFileDest->copyOutState;\n\tFmgrInfo *columnOutputFunctions = taskFileDest->columnOutputFunctions;\n\n\tStringInfo copyData = copyOutState->fe_msgbuf;\n\n\tMemoryContext executorTupleContext = taskFileDest->tupleContext;\n\tMemoryContext oldContext = MemoryContextSwitchTo(executorTupleContext);\n\n\tslot_getallattrs(slot);\n\n\tDatum *columnValues = slot->tts_values;\n\tbool *columnNulls = slot->tts_isnull;\n\n\t/* construct row in COPY format */\n\tAppendCopyRowData(columnValues, columnNulls, tupleDescriptor,\n\t\t\t\t\t  copyOutState, columnOutputFunctions, NULL);\n\n\tif (copyData->len > COPY_BUFFER_SIZE)\n\t{\n\t\tWriteToLocalFile(copyOutState->fe_msgbuf, taskFileDest);\n\t\tresetStringInfo(copyData);\n\t}\n\n\tMemoryContextSwitchTo(oldContext);\n\n\ttaskFileDest->tuplesSent++;\n\n\tMemoryContextReset(executorTupleContext);\n\n\treturn true;\n}\n\n\n/*\n * WriteToLocalResultsFile writes the bytes in a StringInfo to a local file.\n */\nstatic void\nWriteToLocalFile(StringInfo copyData, TaskFileDestReceiver *taskFileDest)\n{\n\tint bytesWritten = FileWriteCompat(&taskFileDest->fileCompat, copyData->data,\n\t\t\t\t\t\t\t\t\t   copyData->len, PG_WAIT_IO);\n\tif (bytesWritten < 0)\n\t{\n\t\tereport(ERROR, (errcode_for_file_access(),\n\t\t\t\t\t\terrmsg(\"could not append to file: %m\")));\n\t}\n\n\ttaskFileDest->bytesSent += bytesWritten;\n}\n\n\n/*\n * TaskFileDestReceiverShutdown implements the rShutdown interface of\n * TaskFileDestReceiver. It writes the footer and closes the file.\n * the relation.\n */\nstatic void\nTaskFileDestReceiverShutdown(DestReceiver *destReceiver)\n{\n\tTaskFileDestReceiver *taskFileDest = (TaskFileDestReceiver *) destReceiver;\n\tCopyOutState copyOutState = taskFileDest->copyOutState;\n\n\tif (copyOutState->fe_msgbuf->len > 0)\n\t{\n\t\tWriteToLocalFile(copyOutState->fe_msgbuf, taskFileDest);\n\t\tresetStringInfo(copyOutState->fe_msgbuf);\n\t}\n\n\tif (copyOutState->binary)\n\t{\n\t\t/* write footers when using binary encoding */\n\t\tAppendCopyBinaryFooters(copyOutState);\n\t\tWriteToLocalFile(copyOutState->fe_msgbuf, taskFileDest);\n\t\tresetStringInfo(copyOutState->fe_msgbuf);\n\t}\n\n\tFileClose(taskFileDest->fileCompat.fd);\n}\n\n\n/*\n * TaskFileDestReceiverDestroy frees memory allocated as part of the\n * TaskFileDestReceiver and closes file descriptors.\n */\nstatic void\nTaskFileDestReceiverDestroy(DestReceiver *destReceiver)\n{\n\tTaskFileDestReceiver *taskFileDest = (TaskFileDestReceiver *) destReceiver;\n\n\tif (taskFileDest->copyOutState)\n\t{\n\t\tpfree(taskFileDest->copyOutState);\n\t\ttaskFileDest->copyOutState = NULL;\n\t}\n\n\tif (taskFileDest->columnOutputFunctions)\n\t{\n\t\tpfree(taskFileDest->columnOutputFunctions);\n\t\ttaskFileDest->columnOutputFunctions = NULL;\n\t}\n\n\tif (taskFileDest->filePath)\n\t{\n\t\tpfree(taskFileDest->filePath);\n\t\ttaskFileDest->filePath = NULL;\n\t}\n}\n\n\n/*\n * FileDestReceiverStats returns statistics for the given file dest receiver.\n */\nvoid\nFileDestReceiverStats(DestReceiver *dest, uint64 *rowsSent, uint64 *bytesSent)\n{\n\tTaskFileDestReceiver *fileDestReceiver = (TaskFileDestReceiver *) dest;\n\t*rowsSent = fileDestReceiver->tuplesSent;\n\t*bytesSent = fileDestReceiver->bytesSent;\n}\n"
  },
  {
    "path": "src/backend/distributed/worker/worker_truncate_trigger_protocol.c",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_create_truncate_trigger_protocol.c\n *\n * Routines for creating truncate triggers on distributed tables on worker nodes.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"utils/elog.h\"\n#include \"utils/fmgroids.h\"\n#include \"utils/lsyscache.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n#include \"distributed/metadata_utility.h\"\n\n\nPG_FUNCTION_INFO_V1(worker_create_truncate_trigger);\n\n\n/*\n * worker_create_truncate_trigger creates a truncate trigger for the given distributed\n * table on current metadata worker. The function is intended to be called by the\n * coordinator node during metadata propagation of mx tables or during the upgrades from\n * citus version <=5.2 to >=6.1. The function requires superuser permissions.\n */\nDatum\nworker_create_truncate_trigger(PG_FUNCTION_ARGS)\n{\n\tCheckCitusVersion(ERROR);\n\n\tOid relationId = PG_GETARG_OID(0);\n\tEnsureTableOwner(relationId);\n\n\t/* Create the truncate trigger */\n\tCreateTruncateTrigger(relationId);\n\n\tPG_RETURN_VOID();\n}\n"
  },
  {
    "path": "src/include/.gitignore",
    "content": "/stamp-h\n/stamp-ext-h\n/citus_config.h\n/citus_config.h.in~\n/citus_version.h\n/citus_version.h.in~\n"
  },
  {
    "path": "src/include/citus_config.h.in",
    "content": "/* src/include/citus_config.h.in.  Generated from configure.ac by autoheader.  */\n\n\n/*\n * citus_config.h.in is generated by autoconf/autoheader and\n * converted into citus_config.h by configure.  Include when code needs to\n * depend on determinations made by configure.\n *\n * Do not manually edit!\n */\n\n\n/* Citus edition as a string */\n#undef CITUS_EDITION\n\n/* Extension version expected by this Citus build */\n#undef CITUS_EXTENSIONVERSION\n\n/* Citus major version as a string */\n#undef CITUS_MAJORVERSION\n\n/* Citus full name as a string */\n#undef CITUS_NAME\n\n/* Citus version as a string */\n#undef CITUS_VERSION\n\n/* Citus version as a number */\n#undef CITUS_VERSION_NUM\n\n/* A string containing the version number, platform, and C compiler */\n#undef CITUS_VERSION_STR\n\n/* Define to 1 to build with lz4 support. (--with-lz4) */\n#undef HAVE_CITUS_LIBLZ4\n\n/* Define to 1 if you have the <inttypes.h> header file. */\n#undef HAVE_INTTYPES_H\n\n/* Define to 1 if you have the `lz4' library (-llz4). */\n#undef HAVE_LIBLZ4\n\n/* Define to 1 if you have the `zstd' library (-lzstd). */\n#undef HAVE_LIBZSTD\n\n/* Define to 1 if you have the <memory.h> header file. */\n#undef HAVE_MEMORY_H\n\n/* Define to 1 if you have the <stdint.h> header file. */\n#undef HAVE_STDINT_H\n\n/* Define to 1 if you have the <stdlib.h> header file. */\n#undef HAVE_STDLIB_H\n\n/* Define to 1 if you have the <strings.h> header file. */\n#undef HAVE_STRINGS_H\n\n/* Define to 1 if you have the <string.h> header file. */\n#undef HAVE_STRING_H\n\n/* Define to 1 if you have the <sys/stat.h> header file. */\n#undef HAVE_SYS_STAT_H\n\n/* Define to 1 if you have the <sys/types.h> header file. */\n#undef HAVE_SYS_TYPES_H\n\n/* Define to 1 if you have the <unistd.h> header file. */\n#undef HAVE_UNISTD_H\n\n/* Define to the address where bug reports for this package should be sent. */\n#undef PACKAGE_BUGREPORT\n\n/* Define to the full name of this package. */\n#undef PACKAGE_NAME\n\n/* Define to the full name and version of this package. */\n#undef PACKAGE_STRING\n\n/* Define to the one symbol short name of this package. */\n#undef PACKAGE_TARNAME\n\n/* Define to the home page for this package. */\n#undef PACKAGE_URL\n\n/* Define to the version of this package. */\n#undef PACKAGE_VERSION\n\n/* The size of `void *', as computed by sizeof. */\n#undef SIZEOF_VOID_P\n\n/* Define to 1 if you have the ANSI C header files. */\n#undef STDC_HEADERS\n"
  },
  {
    "path": "src/include/citus_version.h.in",
    "content": "/* This file is created manually */\n\n/* Citus full name as a string */\n#undef CITUS_NAME\n\n/* Citus edition as a string */\n#undef CITUS_EDITION\n\n/* Extension version expected by this Citus build */\n#undef CITUS_EXTENSIONVERSION\n\n/* Citus major version as a string */\n#undef CITUS_MAJORVERSION\n\n/* Citus version as a string */\n#undef CITUS_VERSION\n\n/* Citus version as a number */\n#undef CITUS_VERSION_NUM\n\n/* A string containing the version number, platform, and C compiler */\n#undef CITUS_VERSION_STR\n\n/* Define to 1 if you have the `liblz4' library (-llz4). */\n#undef HAVE_CITUS_LIBLZ4\n\n/* Define to 1 if you have the `libzstd' library (-lzstd). */\n#undef HAVE_LIBZSTD\n\n/* Base URL for statistics collection and update checks */\n#undef REPORTS_BASE_URL\n"
  },
  {
    "path": "src/include/columnar/columnar.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar.h\n *\n * Type and function declarations for Columnar\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef COLUMNAR_H\n#define COLUMNAR_H\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"storage/bufpage.h\"\n#include \"storage/lockdefs.h\"\n#include \"storage/relfilelocator.h\"\n#include \"utils/relcache.h\"\n#include \"utils/snapmgr.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"columnar/columnar_compression.h\"\n#include \"columnar/columnar_metadata.h\"\n\n#define COLUMNAR_AM_NAME \"columnar\"\n#define COLUMNAR_MODULE_NAME \"citus_columnar\"\n\n#define COLUMNAR_SETOPTIONS_HOOK_SYM \"ColumnarTableSetOptions_hook\"\n\n/* Defines for valid option names */\n#define OPTION_NAME_COMPRESSION_TYPE \"compression\"\n#define OPTION_NAME_STRIPE_ROW_COUNT \"stripe_row_limit\"\n#define OPTION_NAME_CHUNK_ROW_COUNT \"chunk_group_row_limit\"\n\n/* Limits for option parameters */\n#define STRIPE_ROW_COUNT_MINIMUM 1000\n#define STRIPE_ROW_COUNT_MAXIMUM 10000000\n#define CHUNK_ROW_COUNT_MINIMUM 1000\n#define CHUNK_ROW_COUNT_MAXIMUM 100000\n#define COMPRESSION_LEVEL_MIN 1\n#define COMPRESSION_LEVEL_MAX 19\n\n/* Columnar file signature */\n#define COLUMNAR_VERSION_MAJOR 2\n#define COLUMNAR_VERSION_MINOR 0\n\n/* miscellaneous defines */\n#define COLUMNAR_TUPLE_COST_MULTIPLIER 10\n#define COLUMNAR_POSTSCRIPT_SIZE_LENGTH 1\n#define COLUMNAR_POSTSCRIPT_SIZE_MAX 256\n#define COLUMNAR_BYTES_PER_PAGE (BLCKSZ - SizeOfPageHeaderData)\n\n/*global variables for citus_columnar fake version Y */\n#define CITUS_COLUMNAR_INTERNAL_VERSION \"11.1-0\"\n\n/*\n * We can't rely on RelidByRelfilenumber for temp tables since PG18(it was backpatched\n * through PG13), so we can use this macro to define relid within relation in case of\n * temp relations. Otherwise RelidByRelfilenumber should be used.\n */\n#define RelationPrecomputeOid(a) (RelationUsesLocalBuffers(a) ? RelationGetRelid(a) : \\\n\t\t\t\t\t\t\t\t  InvalidOid)\n\n/*\n * ColumnarOptions holds the option values to be used when reading or writing\n * a columnar table. To resolve these values, we first check foreign table's options,\n * and if not present, we then fall back to the default values specified above.\n */\ntypedef struct ColumnarOptions\n{\n\tuint64 stripeRowCount;\n\tuint32 chunkRowCount;\n\tCompressionType compressionType;\n\tint compressionLevel;\n} ColumnarOptions;\n\n\n/* ColumnChunkSkipNode contains statistics for a ColumnChunkData. */\ntypedef struct ColumnChunkSkipNode\n{\n\t/* statistics about values of a column chunk */\n\tbool hasMinMax;\n\tDatum minimumValue;\n\tDatum maximumValue;\n\tuint64 rowCount;\n\n\t/*\n\t * Offsets and sizes of value and exists streams in the column data.\n\t * These enable us to skip reading suppressed row chunks, and start reading\n\t * a chunk without reading previous chunks.\n\t */\n\tuint64 valueChunkOffset;\n\tuint64 valueLength;\n\tuint64 existsChunkOffset;\n\tuint64 existsLength;\n\n\t/*\n\t * This is used for (1) determining destination size when decompressing,\n\t * (2) calculating compression rates when logging stats.\n\t */\n\tuint64 decompressedValueSize;\n\n\tCompressionType valueCompressionType;\n\tint valueCompressionLevel;\n} ColumnChunkSkipNode;\n\n\n/*\n * StripeSkipList can be used for skipping row chunks. It contains a column chunk\n * skip node for each chunk of each column. chunkSkipNodeArray[column][chunk]\n * is the entry for the specified column chunk.\n */\ntypedef struct StripeSkipList\n{\n\tColumnChunkSkipNode **chunkSkipNodeArray;\n\tuint32 *chunkGroupRowCounts;\n\tuint32 columnCount;\n\tuint32 chunkCount;\n} StripeSkipList;\n\n\n/*\n * ChunkData represents a chunk of data for multiple columns. valueArray stores\n * the values of data, and existsArray stores whether a value is present.\n * valueBuffer is used to store (uncompressed) serialized values\n * referenced by Datum's in valueArray. It is only used for by-reference Datum's.\n * There is a one-to-one correspondence between valueArray and existsArray.\n */\ntypedef struct ChunkData\n{\n\tuint32 rowCount;\n\tuint32 columnCount;\n\n\t/*\n\t * Following are indexed by [column][row]. If a column is not projected,\n\t * then existsArray[column] and valueArray[column] are NULL.\n\t */\n\tbool **existsArray;\n\tDatum **valueArray;\n\n\t/* valueBuffer keeps actual data for type-by-reference datums from valueArray. */\n\tStringInfo *valueBufferArray;\n} ChunkData;\n\n\n/*\n * ColumnChunkBuffers represents a chunk of serialized data in a column.\n * valueBuffer stores the serialized values of data, and existsBuffer stores\n * serialized value of presence information. valueCompressionType contains\n * compression type if valueBuffer is compressed. Finally rowCount has\n * the number of rows in this chunk.\n */\ntypedef struct ColumnChunkBuffers\n{\n\tStringInfo existsBuffer;\n\tStringInfo valueBuffer;\n\tCompressionType valueCompressionType;\n\tuint64 decompressedValueSize;\n} ColumnChunkBuffers;\n\n\n/*\n * ColumnBuffers represents data buffers for a column in a row stripe. Each\n * column is made of multiple column chunks.\n */\ntypedef struct ColumnBuffers\n{\n\tColumnChunkBuffers **chunkBuffersArray;\n} ColumnBuffers;\n\n\n/* StripeBuffers represents data for a row stripe. */\ntypedef struct StripeBuffers\n{\n\tuint32 columnCount;\n\tuint32 rowCount;\n\tColumnBuffers **columnBuffersArray;\n\n\tuint32 *selectedChunkGroupRowCounts;\n} StripeBuffers;\n\n\n/* return value of StripeWriteState to decide stripe write state */\ntypedef enum StripeWriteStateEnum\n{\n\t/* stripe write is flushed to disk, so it's readable */\n\tSTRIPE_WRITE_FLUSHED,\n\n\t/*\n\t * Writer transaction did abort either before inserting into\n\t * columnar.stripe or after.\n\t */\n\tSTRIPE_WRITE_ABORTED,\n\n\t/*\n\t * Writer transaction is still in-progress. Note that it is not certain\n\t * if it is being written by current backend's current transaction or\n\t * another backend.\n\t */\n\tSTRIPE_WRITE_IN_PROGRESS\n} StripeWriteStateEnum;\n\ntypedef bool (*ColumnarSupportsIndexAM_type)(char *);\ntypedef const char *(*CompressionTypeStr_type)(CompressionType);\ntypedef bool (*IsColumnarTableAmTable_type)(Oid);\ntypedef bool (*ReadColumnarOptions_type)(Oid, ColumnarOptions *);\n\n/* ColumnarReadState represents state of a columnar scan. */\nstruct ColumnarReadState;\ntypedef struct ColumnarReadState ColumnarReadState;\n\n\n/* ColumnarWriteState represents state of a columnar write operation. */\nstruct ColumnarWriteState;\ntypedef struct ColumnarWriteState ColumnarWriteState;\n\n/* GUCs */\nextern int columnar_compression;\nextern int columnar_stripe_row_limit;\nextern int columnar_chunk_group_row_limit;\nextern int columnar_compression_level;\n\n/* called when the user changes options on the given relation */\ntypedef void (*ColumnarTableSetOptions_hook_type)(Oid relid, ColumnarOptions options);\n\nextern void columnar_init(void);\nextern void columnar_init_gucs(void);\n\nextern CompressionType ParseCompressionType(const char *compressionTypeString);\n\n/* Function declarations for writing to a columnar table */\nextern ColumnarWriteState * ColumnarBeginWrite(Relation rel,\n\t\t\t\t\t\t\t\t\t\t\t   ColumnarOptions options,\n\t\t\t\t\t\t\t\t\t\t\t   TupleDesc tupleDescriptor);\nextern uint64 ColumnarWriteRow(ColumnarWriteState *state, Datum *columnValues,\n\t\t\t\t\t\t\t   bool *columnNulls);\nextern void ColumnarFlushPendingWrites(ColumnarWriteState *state);\nextern void ColumnarEndWrite(ColumnarWriteState *state);\nextern bool ContainsPendingWrites(ColumnarWriteState *state);\nextern MemoryContext ColumnarWritePerTupleContext(ColumnarWriteState *state);\n\n/* Function declarations for reading from columnar table */\n\n/* functions applicable for both sequential and random access */\nextern ColumnarReadState * ColumnarBeginRead(Relation relation,\n\t\t\t\t\t\t\t\t\t\t\t TupleDesc tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t\t List *projectedColumnList,\n\t\t\t\t\t\t\t\t\t\t\t List *qualConditions,\n\t\t\t\t\t\t\t\t\t\t\t MemoryContext scanContext,\n\t\t\t\t\t\t\t\t\t\t\t Snapshot snaphot,\n\t\t\t\t\t\t\t\t\t\t\t bool randomAccess);\nextern void ColumnarReadFlushPendingWrites(ColumnarReadState *readState);\nextern void ColumnarEndRead(ColumnarReadState *state);\nextern void ColumnarResetRead(ColumnarReadState *readState);\n\n/* functions only applicable for sequential access */\nextern bool ColumnarReadNextRow(ColumnarReadState *state, Datum *columnValues,\n\t\t\t\t\t\t\t\tbool *columnNulls, uint64 *rowNumber);\nextern int64 ColumnarReadChunkGroupsFiltered(ColumnarReadState *state);\nextern void ColumnarRescan(ColumnarReadState *readState, List *scanQual);\n\n/* functions only applicable for random access */\nextern void ColumnarReadRowByRowNumberOrError(ColumnarReadState *readState,\n\t\t\t\t\t\t\t\t\t\t\t  uint64 rowNumber, Datum *columnValues,\n\t\t\t\t\t\t\t\t\t\t\t  bool *columnNulls);\nextern bool ColumnarReadRowByRowNumber(ColumnarReadState *readState,\n\t\t\t\t\t\t\t\t\t   uint64 rowNumber, Datum *columnValues,\n\t\t\t\t\t\t\t\t\t   bool *columnNulls);\n\n/* Function declarations for common functions */\nextern FmgrInfo * GetFunctionInfoOrNull(Oid typeId, Oid accessMethodId,\n\t\t\t\t\t\t\t\t\t\tint16 procedureId);\nextern ChunkData * CreateEmptyChunkData(uint32 columnCount, bool *columnMask,\n\t\t\t\t\t\t\t\t\t\tuint32 chunkGroupRowCount);\nextern void FreeChunkData(ChunkData *chunkData);\nextern uint64 ColumnarTableRowCount(Relation relation);\nextern PGDLLEXPORT const char * CompressionTypeStr(CompressionType type);\n\n/* columnar_metadata_tables.c */\nextern PGDLLEXPORT void InitColumnarOptions(Oid regclass);\nextern PGDLLEXPORT void SetColumnarOptions(Oid regclass, ColumnarOptions *options);\nextern PGDLLEXPORT bool DeleteColumnarTableOptions(Oid regclass, bool missingOk);\nextern PGDLLEXPORT bool ReadColumnarOptions(Oid regclass, ColumnarOptions *options);\nextern PGDLLEXPORT bool IsColumnarTableAmTable(Oid relationId);\n\n/* columnar_metadata_tables.c */\nextern void DeleteMetadataRows(Relation rel);\nextern uint64 ColumnarMetadataNewStorageId(void);\nextern uint64 GetHighestUsedAddress(Relation rel);\nextern EmptyStripeReservation * ReserveEmptyStripe(Relation rel, uint64 columnCount,\n\t\t\t\t\t\t\t\t\t\t\t\t   uint64 chunkGroupRowCount,\n\t\t\t\t\t\t\t\t\t\t\t\t   uint64 stripeRowCount);\nextern StripeMetadata * CompleteStripeReservation(Relation rel, uint64 stripeId,\n\t\t\t\t\t\t\t\t\t\t\t\t  uint64 sizeBytes, uint64 rowCount,\n\t\t\t\t\t\t\t\t\t\t\t\t  uint64 chunkCount);\nextern void SaveStripeSkipList(Oid relid, RelFileLocator relfilelocator, uint64 stripe,\n\t\t\t\t\t\t\t   StripeSkipList *stripeSkipList,\n\t\t\t\t\t\t\t   TupleDesc tupleDescriptor);\nextern void SaveChunkGroups(Oid relid, RelFileLocator relfilelocator, uint64 stripe,\n\t\t\t\t\t\t\tList *chunkGroupRowCounts);\nextern StripeSkipList * ReadStripeSkipList(Relation rel, uint64 stripe,\n\t\t\t\t\t\t\t\t\t\t   TupleDesc tupleDescriptor,\n\t\t\t\t\t\t\t\t\t\t   uint32 chunkCount,\n\t\t\t\t\t\t\t\t\t\t   Snapshot snapshot);\nextern StripeMetadata * FindNextStripeByRowNumber(Relation relation, uint64 rowNumber,\n\t\t\t\t\t\t\t\t\t\t\t\t  Snapshot snapshot);\nextern StripeMetadata * FindStripeByRowNumber(Relation relation, uint64 rowNumber,\n\t\t\t\t\t\t\t\t\t\t\t  Snapshot snapshot);\nextern StripeMetadata * FindStripeWithMatchingFirstRowNumber(Relation relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t uint64 rowNumber,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Snapshot snapshot);\nextern StripeWriteStateEnum StripeWriteState(StripeMetadata *stripeMetadata);\nextern uint64 StripeGetHighestRowNumber(StripeMetadata *stripeMetadata);\nextern StripeMetadata * FindStripeWithHighestRowNumber(Relation relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   Snapshot snapshot);\nextern Datum columnar_relation_storageid(PG_FUNCTION_ARGS);\nextern Oid ColumnarRelationId(Oid relid, RelFileLocator relfilelocator);\n\n\n/* write_state_management.c */\nextern ColumnarWriteState * columnar_init_write_state(Relation relation, TupleDesc\n\t\t\t\t\t\t\t\t\t\t\t\t\t  tupdesc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  Oid tupSlotRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  SubTransactionId currentSubXid);\nextern void FlushWriteStateForRelfilenumber(RelFileNumber relfilenumber,\n\t\t\t\t\t\t\t\t\t\t\tSubTransactionId currentSubXid);\nextern void FlushWriteStateForAllRels(SubTransactionId currentSubXid, SubTransactionId\n\t\t\t\t\t\t\t\t\t  parentSubXid);\nextern void DiscardWriteStateForAllRels(SubTransactionId currentSubXid, SubTransactionId\n\t\t\t\t\t\t\t\t\t\tparentSubXid);\nextern void MarkRelfilenumberDropped(RelFileNumber relfilenumber,\n\t\t\t\t\t\t\t\t\t SubTransactionId currentSubXid);\nextern void NonTransactionDropWriteState(RelFileNumber relfilenumber);\nextern bool PendingWritesInUpperTransactions(RelFileNumber relfilenumber,\n\t\t\t\t\t\t\t\t\t\t\t SubTransactionId currentSubXid);\nextern MemoryContext GetWriteContextForDebug(void);\n\n#endif /* COLUMNAR_H */\n"
  },
  {
    "path": "src/include/columnar/columnar_compression.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_compression.h\n *\n * Type and function declarations for compression methods.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef COLUMNAR_COMPRESSION_H\n#define COLUMNAR_COMPRESSION_H\n\n/* Enumaration for columnar table's compression method */\ntypedef enum\n{\n\tCOMPRESSION_TYPE_INVALID = -1,\n\tCOMPRESSION_NONE = 0,\n\tCOMPRESSION_PG_LZ = 1,\n\tCOMPRESSION_LZ4 = 2,\n\tCOMPRESSION_ZSTD = 3,\n\n\tCOMPRESSION_COUNT\n} CompressionType;\n\nextern bool CompressBuffer(StringInfo inputBuffer,\n\t\t\t\t\t\t   StringInfo outputBuffer,\n\t\t\t\t\t\t   CompressionType compressionType,\n\t\t\t\t\t\t   int compressionLevel);\nextern StringInfo DecompressBuffer(StringInfo buffer, CompressionType compressionType,\n\t\t\t\t\t\t\t\t   uint64 decompressedSize);\n\n#endif /* COLUMNAR_COMPRESSION_H */\n"
  },
  {
    "path": "src/include/columnar/columnar_customscan.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_customscan.h\n *\n * Forward declarations of functions to hookup the custom scan feature of\n * columnar.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef COLUMNAR_CUSTOMSCAN_H\n#define COLUMNAR_CUSTOMSCAN_H\n\nvoid columnar_customscan_init(void);\n\n\n#endif /* COLUMNAR_CUSTOMSCAN_H */\n"
  },
  {
    "path": "src/include/columnar/columnar_metadata.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_metadata.h\n *\n * Type and function declarations for Columnar metadata.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef COLUMNAR_METADATA_H\n#define COLUMNAR_METADATA_H\n\n#include \"postgres.h\"\n\n#include \"storage/relfilelocator.h\"\n\n#include \"pg_version_compat.h\"\n#include \"pg_version_constants.h\"\n\n\n/*\n * StripeMetadata represents information about a stripe. This information is\n * stored in the metadata table \"columnar.stripe\".\n */\ntypedef struct StripeMetadata\n{\n\tuint64 fileOffset;\n\tuint64 dataLength;\n\tuint32 columnCount;\n\tuint32 chunkCount;\n\tuint32 chunkGroupRowCount;\n\tuint64 rowCount;\n\tuint64 id;\n\tuint64 firstRowNumber;\n\n\t/* see StripeWriteState */\n\tbool aborted;\n\n\t/*\n\t * If write operation is in-progress (i.e. StripeWriteState returned\n\t * STRIPE_WRITE_IN_PROGRESS), then insertedByCurrentXact is used to\n\t * distinguish whether it's being written by current transaction or\n\t * not.\n\t */\n\tbool insertedByCurrentXact;\n} StripeMetadata;\n\n/*\n * EmptyStripeReservation represents information for an empty stripe\n * reservation.\n */\ntypedef struct EmptyStripeReservation\n{\n\tuint64 stripeId;\n\tuint64 stripeFirstRowNumber;\n} EmptyStripeReservation;\n\nextern List * StripesForRelfilelocator(Relation rel);\nextern void ColumnarStorageUpdateIfNeeded(Relation rel, bool isUpgrade);\nextern List * ExtractColumnarRelOptions(List *inOptions, List **outColumnarOptions);\nextern void SetColumnarRelOptions(RangeVar *rv, List *reloptions);\n\n#endif /* COLUMNAR_METADATA_H */\n"
  },
  {
    "path": "src/include/columnar/columnar_storage.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_storage.h\n *\n * Type and function declarations for storage of columnar data in blocks.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef COLUMNAR_STORAGE_H\n#define COLUMNAR_STORAGE_H\n\n#include \"postgres.h\"\n\n#include \"storage/smgr.h\"\n#include \"utils/rel.h\"\n\n#include \"columnar/columnar_tableam.h\"\n\n\n#define COLUMNAR_INVALID_ROW_NUMBER ((uint64) 0)\n#define COLUMNAR_FIRST_ROW_NUMBER ((uint64) 1)\n#define COLUMNAR_MAX_ROW_NUMBER ((uint64) \\\n\t\t\t\t\t\t\t\t (COLUMNAR_FIRST_ROW_NUMBER + \\\n\t\t\t\t\t\t\t\t  VALID_ITEMPOINTER_OFFSETS * \\\n\t\t\t\t\t\t\t\t  VALID_BLOCKNUMBERS))\n\n\n/*\n * Logical offsets never fall on the first two physical pages. See\n * comments in columnar_storage.c.\n */\n#define ColumnarInvalidLogicalOffset 0\n#define ColumnarFirstLogicalOffset ((BLCKSZ - SizeOfPageHeaderData) * 2)\n#define ColumnarLogicalOffsetIsValid(X) ((X) >= ColumnarFirstLogicalOffset)\n\n\nextern void ColumnarStorageInit(SMgrRelation srel, uint64 storageId);\nextern bool ColumnarStorageIsCurrent(Relation rel);\nextern void ColumnarStorageUpdateCurrent(Relation rel, bool upgrade,\n\t\t\t\t\t\t\t\t\t\t uint64 reservedStripeId,\n\t\t\t\t\t\t\t\t\t\t uint64 reservedRowNumber,\n\t\t\t\t\t\t\t\t\t\t uint64 reservedOffset);\n\nextern uint64 ColumnarStorageGetVersionMajor(Relation rel, bool force);\nextern uint64 ColumnarStorageGetVersionMinor(Relation rel, bool force);\nextern uint64 ColumnarStorageGetStorageId(Relation rel, bool force);\nextern uint64 ColumnarStorageGetReservedStripeId(Relation rel, bool force);\nextern uint64 ColumnarStorageGetReservedRowNumber(Relation rel, bool force);\nextern uint64 ColumnarStorageGetReservedOffset(Relation rel, bool force);\n\nextern uint64 ColumnarStorageReserveData(Relation rel, uint64 amount);\nextern uint64 ColumnarStorageReserveRowNumber(Relation rel, uint64 nrows);\nextern uint64 ColumnarStorageReserveStripeId(Relation rel);\n\nextern void ColumnarStorageRead(Relation rel, uint64 logicalOffset,\n\t\t\t\t\t\t\t\tchar *data, uint32 amount);\nextern void ColumnarStorageWrite(Relation rel, uint64 logicalOffset,\n\t\t\t\t\t\t\t\t char *data, uint32 amount);\nextern bool ColumnarStorageTruncate(Relation rel, uint64 newDataReservation);\n\n#endif /* COLUMNAR_STORAGE_H */\n"
  },
  {
    "path": "src/include/columnar/columnar_tableam.h",
    "content": "#ifndef COLUMNAR_TABLEAM_H\n#define COLUMNAR_TABLEAM_H\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"access/heapam.h\"\n#include \"access/skey.h\"\n#include \"access/tableam.h\"\n#include \"catalog/indexing.h\"\n#include \"nodes/bitmapset.h\"\n#include \"utils/acl.h\"\n\n#include \"citus_version.h\"\n\n/*\n * Number of valid ItemPointer Offset's for \"row number\" <> \"ItemPointer\"\n * mapping.\n *\n * Postgres has some asserts calling either ItemPointerIsValid or\n * OffsetNumberIsValid. That constraints itemPointer.offsetNumber\n * for columnar tables to the following interval:\n * [FirstOffsetNumber, MaxOffsetNumber].\n *\n * However, bitmap scan logic assumes that itemPointer.offsetNumber cannot\n * be larger than MaxHeapTuplesPerPage (see tbm_add_tuples).\n *\n * For this reason, we restrict itemPointer.offsetNumber\n * to the following interval: [FirstOffsetNumber, MaxHeapTuplesPerPage].\n */\n#define VALID_ITEMPOINTER_OFFSETS \\\n\t\t((uint64) (MaxHeapTuplesPerPage - FirstOffsetNumber + 1))\n\n/*\n * Number of valid ItemPointer BlockNumber's for \"row number\" <> \"ItemPointer\"\n * mapping.\n *\n * Similar to VALID_ITEMPOINTER_OFFSETS, due to asserts around\n * itemPointer.blockNumber, we can only use values upto and including\n * MaxBlockNumber.\n * Note that postgres doesn't restrict blockNumber to a lower boundary.\n *\n * For this reason, we restrict itemPointer.blockNumber\n * to the following interval: [0, MaxBlockNumber].\n */\n#define VALID_BLOCKNUMBERS ((uint64) (MaxBlockNumber + 1))\n\n\nstruct ColumnarScanDescData;\ntypedef struct ColumnarScanDescData *ColumnarScanDesc;\n\n\nconst TableAmRoutine * GetColumnarTableAmRoutine(void);\nextern void columnar_tableam_init(void);\nextern TableScanDesc columnar_beginscan_extended(Relation relation, Snapshot snapshot,\n\t\t\t\t\t\t\t\t\t\t\t\t int nkeys, ScanKey key,\n\t\t\t\t\t\t\t\t\t\t\t\t ParallelTableScanDesc parallel_scan,\n\t\t\t\t\t\t\t\t\t\t\t\t uint32 flags, Bitmapset *attr_needed,\n\t\t\t\t\t\t\t\t\t\t\t\t List *scanQual);\nextern int64 ColumnarScanChunkGroupsFiltered(ColumnarScanDesc columnarScanDesc);\nextern PGDLLEXPORT bool ColumnarSupportsIndexAM(char *indexAMName);\nextern bool IsColumnarTableAmTable(Oid relationId);\nextern void CheckCitusColumnarCreateExtensionStmt(Node *parseTree);\nextern void CheckCitusColumnarAlterExtensionStmt(Node *parseTree);\nextern DefElem * GetExtensionOption(List *extensionOptions,\n\t\t\t\t\t\t\t\t\tconst char *defname);\n\n#endif /* COLUMNAR_TABLEAM_H */\n"
  },
  {
    "path": "src/include/columnar/columnar_version_compat.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * columnar_version_compat.h\n *\n *  Compatibility macros for writing code agnostic to PostgreSQL versions\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef COLUMNAR_VERSION_COMPAT_H\n#define COLUMNAR_VERSION_COMPAT_H\n\n#include \"pg_version_constants.h\"\n\n/* for PG_VERSION_NUM and TupleDescAttr() */\n#include \"postgres.h\"\n\n#include \"access/htup_details.h\"\n\n\n#define ACLCHECK_OBJECT_TABLE OBJECT_TABLE\n\n#define ExplainPropertyLong(qlabel, value, es) \\\n\t\tExplainPropertyInteger(qlabel, NULL, value, es)\n\n#endif /* COLUMNAR_COMPAT_H */\n"
  },
  {
    "path": "src/include/distributed/adaptive_executor.h",
    "content": "#ifndef ADAPTIVE_EXECUTOR_H\n#define ADAPTIVE_EXECUTOR_H\n\n#include \"distributed/multi_physical_planner.h\"\n\n/* GUC, determining whether Citus opens 1 connection per task */\nextern bool ForceMaxQueryParallelization;\nextern int MaxAdaptiveExecutorPoolSize;\nextern bool EnableBinaryProtocol;\n\n\n/* GUC, number of ms to wait between opening connections to the same worker */\nextern int ExecutorSlowStartInterval;\nextern bool EnableCostBasedConnectionEstablishment;\nextern bool PreventIncompleteConnectionEstablishment;\n\nextern uint64 ExecuteTaskList(RowModifyLevel modLevel, List *taskList);\nextern uint64 ExecuteUtilityTaskList(List *utilityTaskList, bool localExecutionSupported);\nextern uint64 ExecuteUtilityTaskListExtended(List *utilityTaskList, int poolSize,\n\t\t\t\t\t\t\t\t\t\t\t bool localExecutionSupported);\nextern uint64 ExecuteTaskListOutsideTransaction(RowModifyLevel modLevel, List *taskList,\n\t\t\t\t\t\t\t\t\t\t\t\tint targetPoolSize, List *jobIdList);\n\n\n#endif /* ADAPTIVE_EXECUTOR_H */\n"
  },
  {
    "path": "src/include/distributed/argutils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * argutils.h\n *\n * Macros to help with argument parsing in UDFs.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n/*\n * PG_ENSURE_ARGNOTNULL ensures that a UDF argument is not NULL and throws an\n * error otherwise. This is useful for non STRICT UDFs where only some\n * arguments are allowed to be NULL.\n */\n#define PG_ENSURE_ARGNOTNULL(argIndex, argName) \\\n\t\tif (PG_ARGISNULL(argIndex)) \\\n\t\t{ \\\n\t\t\tereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), \\\n\t\t\t\t\t\t\terrmsg(\"%s cannot be NULL\", argName))); \\\n\t\t}\n\n/*\n * PG_GETARG_TEXT_TO_CSTRING is the same as PG_GETARG_TEXT_P, but instead of\n * text* it returns char*. Just like most other PG_GETARG_* macros this assumes\n * the argument is not NULL.\n */\n#define PG_GETARG_TEXT_TO_CSTRING(argIndex) \\\n\t\ttext_to_cstring(PG_GETARG_TEXT_P(argIndex))\n\n/*\n * PG_GETARG_TEXT_TO_CSTRING_OR_NULL is the same as PG_GETARG_TEXT_TO_CSTRING,\n * but it supports the case where the argument is NULL. In this case it will\n * return a NULL pointer.\n */\n#define PG_GETARG_TEXT_TO_CSTRING_OR_NULL(argIndex) \\\n\t\tPG_ARGISNULL(argIndex) ? NULL : PG_GETARG_TEXT_TO_CSTRING(argIndex)\n\n/*\n * PG_GETARG_NAME_OR_NULL is the same as PG_GETARG_NAME, but it supports the\n * case where the argument is NULL. In this case it will return a NULL pointer.\n */\n#define PG_GETARG_NAME_OR_NULL(argIndex) \\\n\t\tPG_ARGISNULL(argIndex) ? NULL : PG_GETARG_NAME(argIndex)\n\n/*\n * PG_GETARG_FLOAT4_OR is the same as PG_GETARG_FLOAT4, but it supports the\n * case where the argument is NULL. In that case it will return the provided\n * fallback.\n */\n#define PG_GETARG_FLOAT4_OR_DEFAULT(argIndex, fallback) \\\n\t\tPG_ARGISNULL(argIndex) ? (fallback) : PG_GETARG_FLOAT4(argIndex)\n"
  },
  {
    "path": "src/include/distributed/backend_data.h",
    "content": "/*\n * backend_data.h\n *\n * Data structure definition for managing backend data and related function\n * declarations.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef BACKEND_DATA_H\n#define BACKEND_DATA_H\n\n\n#include \"access/twophase.h\"\n#include \"datatype/timestamp.h\"\n#include \"nodes/pg_list.h\"\n#include \"storage/lwlock.h\"\n#include \"storage/proc.h\"\n#include \"storage/s_lock.h\"\n\n#include \"distributed/transaction_identifier.h\"\n\n\n/*\n * Each backend's active distributed transaction information is tracked via\n * BackendData in shared memory.\n *\n * DistributedTransactionId already has the same fields that CitusInitiatedBackend\n * has. However, we prefer to keep them seperate since CitusInitiatedBackend is a\n * broader concept which covers the backends that are not initiated via a distributed\n * transaction as well. In other words, we could have backends that\n * CitusInitiatedBackend is set but DistributedTransactionId is not set such as an\n * \"INSERT\" query which is not inside a transaction block.\n */\ntypedef struct BackendData\n{\n\tOid databaseId;\n\tslock_t mutex;\n\tbool cancelledDueToDeadlock;\n\tuint64 globalPID;\n\tbool distributedCommandOriginator;\n\tDistributedTransactionId transactionId;\n\tbool activeBackend; /* set to false when backend exists */\n} BackendData;\n\n\nextern void BackendManagementShmemInit(void);\nextern size_t BackendManagementShmemSize(void);\nextern void InitializeBackendManagement(void);\nextern int TotalProcCount(void);\nextern void InitializeBackendData(const char *applicationName);\nextern void LockBackendSharedMemory(LWLockMode lockMode);\nextern void UnlockBackendSharedMemory(void);\nextern void UnSetDistributedTransactionId(void);\nextern void UnSetGlobalPID(void);\nextern void SetActiveMyBackend(bool value);\nextern void AssignDistributedTransactionId(void);\nextern void AssignGlobalPID(const char *applicationName);\nextern uint64 GetGlobalPID(void);\nextern void SetBackendDataDatabaseId(void);\nextern void SetBackendDataGlobalPID(uint64 gpid);\nextern void SetBackendDataDistributedCommandOriginator(bool distributedCommandOriginator);\nextern uint64 ExtractGlobalPID(const char *applicationName);\nextern int ExtractNodeIdFromGlobalPID(uint64 globalPID, bool missingOk);\nextern int ExtractProcessIdFromGlobalPID(uint64 globalPID);\nextern void GetBackendDataForProc(PGPROC *proc, BackendData *result);\nextern void CancelTransactionDueToDeadlock(PGPROC *proc);\nextern bool MyBackendGotCancelledDueToDeadlock(bool clearState);\nextern List * ActiveDistributedTransactionNumbers(void);\nextern LocalTransactionId GetMyProcLocalTransactionId(void);\nextern int GetExternalClientBackendCount(void);\nextern uint32 IncrementExternalClientBackendCounter(void);\nextern void DecrementExternalClientBackendCounter(void);\nextern void DetermineCitusBackendType(const char *applicationName);\nextern bool IsCitusInternalBackend(void);\nextern bool IsRebalancerInternalBackend(void);\nextern bool IsCitusRunCommandBackend(void);\nextern bool IsExternalClientBackend(void);\nextern bool IsCitusShardTransferBackend(void);\n\n#define INVALID_CITUS_INTERNAL_BACKEND_GPID 0\n#define GLOBAL_PID_NODE_ID_FOR_NODES_NOT_IN_METADATA 99999999\n\n#endif /* BACKEND_DATA_H */\n"
  },
  {
    "path": "src/include/distributed/background_jobs.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * background_jobs.h\n *\t  Functions related to running the background tasks queue monitor.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_BACKGROUND_JOBS_H\n#define CITUS_BACKGROUND_JOBS_H\n\n#include \"postgres.h\"\n\n#include \"postmaster/bgworker.h\"\n\n#include \"distributed/metadata_utility.h\"\n\n\n/*\n * BackgroundExecutorHashEntry hash table entry to refer existing task executors\n */\ntypedef struct BackgroundExecutorHashEntry\n{\n\t/* hash key must be the first to hash correctly */\n\tint64 taskid;\n\n\tBackgroundWorkerHandle *handle;\n\tdsm_segment *seg;\n\tint64 jobid;\n\tStringInfo message;\n} BackgroundExecutorHashEntry;\n\n\n/*\n * TaskExecutionStatus status for task execution in queue monitor\n */\ntypedef enum TaskExecutionStatus\n{\n\tTASK_EXECUTION_STATUS_SUCCESS = 0,\n\tTASK_EXECUTION_STATUS_ERROR,\n\tTASK_EXECUTION_STATUS_CANCELLED,\n\tTASK_EXECUTION_STATUS_RUNNING,\n\tTASK_EXECUTION_STATUS_WOULDBLOCK\n} TaskExecutionStatus;\n\n\n/*\n * QueueMonitorExecutionContext encapsulates info related to executors and tasks\n * in queue monitor\n */\ntypedef struct QueueMonitorExecutionContext\n{\n\t/* current total # of parallel task executors */\n\tint64 currentExecutorCount;\n\n\t/* map of current executors */\n\tHTAB *currentExecutors;\n\n\t/* last background allocation failure timestamp */\n\tTimestampTz backgroundWorkerFailedStartTime;\n\n\t/* useful to track if all tasks EWOULDBLOCK'd at current iteration */\n\tbool allTasksWouldBlock;\n\n\t/* context for monitor related allocations */\n\tMemoryContext ctx;\n} QueueMonitorExecutionContext;\n\n\n/*\n * TaskExecutionContext encapsulates info for currently executed task in queue monitor\n */\ntypedef struct TaskExecutionContext\n{\n\t/* active background executor entry */\n\tBackgroundExecutorHashEntry *handleEntry;\n\n\t/* active background task */\n\tBackgroundTask *task;\n\n\t/* context for queue monitor */\n\tQueueMonitorExecutionContext *queueMonitorExecutionContext;\n} TaskExecutionContext;\n\n\n/*\n * ParallelTasksPerNodeEntry is the struct used\n * to track the number of concurrent background tasks that\n * involve a particular node (the key to the entry)\n */\ntypedef struct ParallelTasksPerNodeEntry\n{\n\t/* Used as hash key. */\n\tint32 node_id;\n\n\t/* number of concurrent background tasks that involve node node_id */\n\tuint32 counter;\n} ParallelTasksPerNodeEntry;\n\n\nextern BackgroundWorkerHandle * StartCitusBackgroundTaskQueueMonitor(Oid database,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Oid extensionOwner);\nextern PGDLLEXPORT void CitusBackgroundTaskQueueMonitorMain(Datum arg);\nextern PGDLLEXPORT void CitusBackgroundTaskExecutor(Datum main_arg);\n\nextern Datum citus_job_cancel(PG_FUNCTION_ARGS);\nextern Datum citus_job_wait(PG_FUNCTION_ARGS);\nextern Datum citus_task_wait(PG_FUNCTION_ARGS);\nextern void citus_job_wait_internal(int64 jobid, BackgroundJobStatus *desiredStatus);\nextern void citus_task_wait_internal(int64 taskid, BackgroundTaskStatus *desiredStatus);\nextern bool IncrementParallelTaskCountForNodesInvolved(BackgroundTask *task);\n\n#endif /*CITUS_BACKGROUND_JOBS_H */\n"
  },
  {
    "path": "src/include/distributed/background_worker_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * background_worker_utils.h\n *    Common utilities for initializing PostgreSQL background workers\n *    used by Citus distributed infrastructure.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef BACKGROUND_WORKER_UTILS_H\n#define BACKGROUND_WORKER_UTILS_H\n\n#include \"postgres.h\"\n\n#include \"postmaster/bgworker.h\"\n\n/*\n * Background worker configuration parameters\n */\ntypedef struct CitusBackgroundWorkerConfig\n{\n\t/* Worker identification */\n\tconst char *workerName;\n\tconst char *functionName;\n\tconst char *workerType;\n\n\t/* Worker parameters */\n\tDatum mainArg;\n\tOid extensionOwner;\n\n\t/* Worker behavior flags */\n\tbool needsNotification;\n\tbool waitForStartup;\n\tint restartTime;\n\n\t/* Worker timing */\n\tBgWorkerStartTime startTime;\n\n\t/* Optional extra data */\n\tconst void *extraData;\n\tsize_t extraDataSize;\n} CitusBackgroundWorkerConfig;\n\n/* Default configuration values */\n#define CITUS_BGW_DEFAULT_RESTART_TIME 5\n#define CITUS_BGW_NEVER_RESTART BGW_NEVER_RESTART\n#define CITUS_BGW_DEFAULT_START_TIME BgWorkerStart_ConsistentState\n\n/* Function declarations */\nextern BackgroundWorkerHandle * RegisterCitusBackgroundWorker(const\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  CitusBackgroundWorkerConfig\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  *config);\n\nextern void InitializeCitusBackgroundWorker(BackgroundWorker *worker,\n\t\t\t\t\t\t\t\t\t\t\tconst CitusBackgroundWorkerConfig *config);\n\n#endif /* BACKGROUND_WORKER_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/cancel_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * cencel_utils.h\n *\t  Utilities related to query cancellation\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CANCEL_UTILS_H\n#define CANCEL_UTILS_H\n\n\nextern bool IsHoldOffCancellationReceived(void);\n\n#endif /* CANCEL_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/causal_clock.h",
    "content": "/*\n * causal_clock.h\n *\n * Data structure definitions for managing hybrid logical clock and\n * related function declarations.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CAUSAL_CLOCK_H\n#define CAUSAL_CLOCK_H\n\n#include \"distributed/type_utils.h\"\n\n/*\n * Clock components - Unsigned 64 bit <LC, C>\n * Logical clock   (LC): 42 bits\n * Counter          (C): 22 bits\n *\n * 2^42 milliseconds - 4398046511104 milliseconds, which is ~139 years.\n * 2^22 ticks - maximum of four million operations per millisecond.\n *\n */\n\n#define LOGICAL_BITS 42\n#define COUNTER_BITS 22\n#define LOGICAL_MASK ((1U << COUNTER_BITS) - 1)\n\n#define MAX_LOGICAL ((1LU << LOGICAL_BITS) - 1)\n#define MAX_COUNTER LOGICAL_MASK\n\nextern bool EnableClusterClock;\n\nextern void LogicalClockShmemInit(void);\nextern size_t LogicalClockShmemSize(void);\nextern void InitializeClusterClockMem(void);\nextern ClusterClock * GetEpochTimeAsClock(void);\n\n#endif /* CAUSAL_CLOCK_H */\n"
  },
  {
    "path": "src/include/distributed/citus_acquire_lock.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_acquire_lock.h\n *\t  Background worker to help with acquiering locks by canceling competing backends.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_ACQUIRE_LOCK_H\n#define CITUS_ACQUIRE_LOCK_H\n\n\n#include \"postmaster/bgworker.h\"\n\nBackgroundWorkerHandle * StartLockAcquireHelperBackgroundWorker(int backendToHelp,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tint32 lock_cooldown);\n\n#endif /* CITUS_ACQUIRE_LOCK_H */\n"
  },
  {
    "path": "src/include/distributed/citus_clauses.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_clauses.h\n *  Routines roughly equivalent to postgres' util/clauses.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_CLAUSES_H\n#define CITUS_CLAUSES_H\n\n#include \"nodes/execnodes.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n\n\n/*\n * CoordinatorEvaluationMode is used to signal what expressions in the query\n * should be evaluated on the coordinator.\n */\ntypedef enum CoordinatorEvaluationMode\n{\n\t/* evaluate nothing */\n\tEVALUATE_NONE = 0,\n\n\t/* evaluate only external parameters */\n\tEVALUATE_PARAMS,\n\n\t/* evaluate both the functions/expressions and the external paramaters */\n\tEVALUATE_FUNCTIONS_PARAMS\n} CoordinatorEvaluationMode;\n\n/*\n * This struct is used to pass information to master\n * evaluation logic.\n */\ntypedef struct CoordinatorEvaluationContext\n{\n\tPlanState *planState;\n\tCoordinatorEvaluationMode evaluationMode;\n} CoordinatorEvaluationContext;\n\n\nextern bool RequiresCoordinatorEvaluation(Query *query);\nextern void ExecuteCoordinatorEvaluableExpressions(Query *query, PlanState *planState);\nextern Node * PartiallyEvaluateExpression(Node *expression,\n\t\t\t\t\t\t\t\t\t\t  CoordinatorEvaluationContext *\n\t\t\t\t\t\t\t\t\t\t  coordinatorEvaluationContext);\nextern bool CitusIsVolatileFunction(Node *node);\nextern bool CitusIsMutableFunction(Node *node);\n\n#endif /* CITUS_CLAUSES_H */\n"
  },
  {
    "path": "src/include/distributed/citus_custom_scan.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_custom_scan.h\n *\t  Export all custom scan and custom exec methods.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_CUSTOM_SCAN_H\n#define CITUS_CUSTOM_SCAN_H\n\n#include \"executor/execdesc.h\"\n#include \"nodes/plannodes.h\"\n\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n\ntypedef struct CitusScanState\n{\n\tCustomScanState customScanState;  /* underlying custom scan node */\n\n\t/* function that gets called before postgres starts its execution */\n\tbool finishedPreScan;          /* flag to check if the pre scan is finished */\n\tvoid (*PreExecScan)(struct CitusScanState *scanState);\n\n\tDistributedPlan *distributedPlan; /* distributed execution plan */\n\tMultiExecutorType executorType;   /* distributed executor type */\n\tbool finishedRemoteScan;          /* flag to check if remote scan is finished */\n\tTuplestorestate *tuplestorestate; /* tuple store to store distributed results */\n} CitusScanState;\n\n\n/* custom scan methods for all executors */\nextern CustomScanMethods AdaptiveExecutorCustomScanMethods;\nextern CustomScanMethods NonPushableInsertSelectCustomScanMethods;\nextern CustomScanMethods DelayedErrorCustomScanMethods;\nextern CustomScanMethods NonPushableMergeCommandCustomScanMethods;\n\n\nextern void RegisterCitusCustomScanMethods(void);\nextern void CitusExplainScan(CustomScanState *node, List *ancestors, struct\n\t\t\t\t\t\t\t ExplainState *es);\nextern TupleDesc ScanStateGetTupleDescriptor(CitusScanState *scanState);\nextern EState * ScanStateGetExecutorState(CitusScanState *scanState);\n\nextern CustomScan * FetchCitusCustomScanIfExists(Plan *plan);\nextern bool IsCitusPlan(Plan *plan);\nextern bool IsCitusCustomScan(Plan *plan);\n\nextern void SetJobColocationId(Job *job);\n\n#endif /* CITUS_CUSTOM_SCAN_H */\n"
  },
  {
    "path": "src/include/distributed/citus_depended_object.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_depended_object.h\n *   Exposes functions related to hiding citus depended objects while executing\n *   postgres vanilla tests.\n *\n * Copyright (c) CitusDependent Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_DEPENDED_OBJECT_H\n#define CITUS_DEPENDED_OBJECT_H\n\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n\n#include \"distributed/commands.h\"\n\nextern bool HideCitusDependentObjects;\n\n/* DistOpsValidationState to be used to determine validity of dist ops */\ntypedef enum DistOpsValidationState\n{\n\tHasAtLeastOneValidObject,\n\tHasNoneValidObject,\n\tHasObjectWithInvalidOwnership,\n\tNoAddressResolutionRequired,\n\tShouldQualifyAfterLocalCreation\n} DistOpsValidationState;\n\nextern void SetLocalClientMinMessagesIfRunningPGTests(int\n\t\t\t\t\t\t\t\t\t\t\t\t\t  clientMinMessageLevel);\nextern void SetLocalHideCitusDependentObjectsDisabledWhenAlreadyEnabled(void);\nextern bool HideCitusDependentObjectsOnQueriesOfPgMetaTables(Node *node, void *context);\nextern bool IsPgLocksTable(RangeTblEntry *rte);\nextern DistOpsValidationState DistOpsValidityState(Node *node, const\n\t\t\t\t\t\t\t\t\t\t\t\t   DistributeObjectOps *ops);\nextern bool DistOpsInValidState(DistOpsValidationState distOpsValidationState);\n\n#endif /* CITUS_DEPENDED_OBJECT_H */\n"
  },
  {
    "path": "src/include/distributed/citus_nodefuncs.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_nodefuncs.h\n *\t  Node (de-)serialization support for Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_NODEFUNCS_H\n#define CITUS_NODEFUNCS_H\n\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n\n#include \"distributed/multi_physical_planner.h\"\n\n/* citus_nodefuncs.c */\nextern void SetRangeTblExtraData(RangeTblEntry *rte, CitusRTEKind rteKind,\n\t\t\t\t\t\t\t\t char *fragmentSchemaName, char *fragmentTableName,\n\t\t\t\t\t\t\t\t List *tableIdList, List *funcColumnNames,\n\t\t\t\t\t\t\t\t List *funcColumnTypes, List *funcColumnTypeMods,\n\t\t\t\t\t\t\t\t List *funcCollations);\nextern void ModifyRangeTblExtraData(RangeTblEntry *rte, CitusRTEKind rteKind,\n\t\t\t\t\t\t\t\t\tchar *fragmentSchemaName, char *fragmentTableName,\n\t\t\t\t\t\t\t\t\tList *tableIdList);\nextern void ExtractRangeTblExtraData(RangeTblEntry *rte, CitusRTEKind *rteKind,\n\t\t\t\t\t\t\t\t\t char **fragmentSchemaName, char **fragmentTableName,\n\t\t\t\t\t\t\t\t\t List **tableIdList);\nextern CitusRTEKind GetRangeTblKind(RangeTblEntry *rte);\n\nextern void RegisterNodes(void);\n\n#define READFUNC_ARGS struct ExtensibleNode *node\n#define OUTFUNC_ARGS StringInfo str, const struct ExtensibleNode *raw_node\n#define COPYFUNC_ARGS struct ExtensibleNode *target_node, const struct \\\n\t\tExtensibleNode *source_node\n\nextern void ReadUnsupportedCitusNode(READFUNC_ARGS);\n\nextern void OutJob(OUTFUNC_ARGS);\nextern void OutDistributedPlan(OUTFUNC_ARGS);\nextern void OutDistributedSubPlan(OUTFUNC_ARGS);\nextern void OutUsedDistributedSubPlan(OUTFUNC_ARGS);\nextern void OutShardInterval(OUTFUNC_ARGS);\nextern void OutMapMergeJob(OUTFUNC_ARGS);\nextern void OutShardPlacement(OUTFUNC_ARGS);\nextern void OutRelationShard(OUTFUNC_ARGS);\nextern void OutRelationRowLock(OUTFUNC_ARGS);\nextern void OutTask(OUTFUNC_ARGS);\nextern void OutLocalPlannedStatement(OUTFUNC_ARGS);\nextern void OutDeferredErrorMessage(OUTFUNC_ARGS);\nextern void OutGroupShardPlacement(OUTFUNC_ARGS);\n\nextern void OutMultiNode(OUTFUNC_ARGS);\nextern void OutMultiTreeRoot(OUTFUNC_ARGS);\nextern void OutMultiProject(OUTFUNC_ARGS);\nextern void OutMultiCollect(OUTFUNC_ARGS);\nextern void OutMultiSelect(OUTFUNC_ARGS);\nextern void OutMultiTable(OUTFUNC_ARGS);\nextern void OutMultiJoin(OUTFUNC_ARGS);\nextern void OutMultiPartition(OUTFUNC_ARGS);\nextern void OutMultiCartesianProduct(OUTFUNC_ARGS);\nextern void OutMultiExtendedOp(OUTFUNC_ARGS);\nextern void OutTableDDLCommand(OUTFUNC_ARGS);\n\nextern void CopyNodeJob(COPYFUNC_ARGS);\nextern void CopyNodeDistributedPlan(COPYFUNC_ARGS);\nextern void CopyNodeDistributedSubPlan(COPYFUNC_ARGS);\nextern void CopyNodeUsedDistributedSubPlan(COPYFUNC_ARGS);\nextern void CopyNodeShardInterval(COPYFUNC_ARGS);\nextern void CopyNodeMapMergeJob(COPYFUNC_ARGS);\nextern void CopyNodeShardPlacement(COPYFUNC_ARGS);\nextern void CopyNodeGroupShardPlacement(COPYFUNC_ARGS);\nextern void CopyNodeRelationShard(COPYFUNC_ARGS);\nextern void CopyNodeRelationRowLock(COPYFUNC_ARGS);\nextern void CopyNodeTask(COPYFUNC_ARGS);\nextern void CopyNodeLocalPlannedStatement(COPYFUNC_ARGS);\nextern void CopyNodeTaskQuery(COPYFUNC_ARGS);\nextern void CopyNodeDeferredErrorMessage(COPYFUNC_ARGS);\n\n#endif /* CITUS_NODEFUNCS_H */\n"
  },
  {
    "path": "src/include/distributed/citus_nodes.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_nodes.h\n *\t  Additional node types, and related infrastructure, for Citus.\n *\n * To add a new node type to Citus, perform the following:\n *\n *   * Add a new CitusNodeTag value to use as a tag for the node. Add\n *     the node's name at a corresponding offset within the array named\n *     CitusNodeTagNamesD at the top of citus_nodefuncs.c\n *\n *   * Describe the node in a struct, which must have a CitusNode as\n *     its first element\n *\n *   * Implement an 'outfunc' for the node in citus_outfuncs.c, using\n *     the macros defined within that file. This function will handle\n *     converting the node to a string\n *\n *   * Implement a 'readfunc' for the node in citus_readfuncs.c, using\n *     the macros defined within that file. This function will handle\n *     converting strings into instances of the node\n *\n *   * Use DEFINE_NODE_METHODS within the nodeMethods array (near the\n *     bottom of citus_nodefuncs.c) to register the node in PostgreSQL\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_NODES_H\n#define CITUS_NODES_H\n\n#include \"nodes/extensible.h\"\n\n/*\n * Citus Node Tags\n *\n * These have to be distinct from the ideas used in postgres' nodes.h\n *\n * NOTE: This list must match CitusNodeTagNamesD from citus_nodefuncs.c\n */\n#define CITUS_NODE_TAG_START\t1200\ntypedef enum CitusNodeTag\n{\n\tT_MultiNode = CITUS_NODE_TAG_START, /* FIXME: perhaps use something less predicable? */\n\tT_MultiTreeRoot,\n\tT_MultiProject,\n\tT_MultiCollect,\n\tT_MultiSelect,\n\tT_MultiTable,\n\tT_MultiJoin,\n\tT_MultiPartition,\n\tT_MultiCartesianProduct,\n\tT_MultiExtendedOp,\n\tT_Job,\n\tT_MapMergeJob,\n\tT_DistributedPlan,\n\tT_DistributedSubPlan,\n\tT_UsedDistributedSubPlan,\n\tT_Task,\n\tT_LocalPlannedStatement,\n\tT_ShardInterval,\n\tT_ShardPlacement,\n\tT_RelationShard,\n\tT_RelationRowLock,\n\tT_DeferredErrorMessage,\n\tT_GroupShardPlacement,\n\tT_TableDDLCommand\n} CitusNodeTag;\n\n\nextern const char** CitusNodeTagNames;\n\n\ntypedef struct CitusNode\n{\n\tExtensibleNode extensible;\n\tCitusNodeTag citus_tag; /* for quick type determination */\n} CitusNode;\n\n#define CitusNodeTag(nodeptr)\t\tCitusNodeTagI((Node*) nodeptr)\n\nstatic inline int\nCitusNodeTagI(Node *node)\n{\n\tif (!IsA(node, ExtensibleNode))\n\t{\n\t\treturn nodeTag(node);\n\t}\n\n\treturn ((CitusNode*)(node))->citus_tag;\n}\n\n\n/* Citus variant of newNode(), don't use directly. */\nstatic inline CitusNode *\nCitusNewNode(size_t size, CitusNodeTag tag)\n{\n\tCitusNode\t   *result;\n\n\tAssert(size >= sizeof(CitusNode));\t\t/* need the ExtensibleNode and the tag, at least */\n\tresult = (CitusNode *) palloc0(size);\n\tresult->extensible.type = T_ExtensibleNode;\n\tresult->extensible.extnodename = CitusNodeTagNames[tag - CITUS_NODE_TAG_START];\n\tresult->citus_tag = (int) (tag);\n\n\treturn result;\n}\n\n/*\n * IsA equivalent that compares node tags, including Citus-specific nodes.\n */\n#define CitusIsA(nodeptr,_type_)\t(CitusNodeTag(nodeptr) == T_##_type_)\n\n\n/*\n * CitusMakeNode is Citus variant of makeNode(). Use it to create nodes of\n * the types listed in the CitusNodeTag enum and plain NodeTag. Initializes\n * memory, besides the node tag, to 0.\n */\n#define CitusMakeNode(_type_) ((_type_ *) CitusNewNode(sizeof(_type_),T_##_type_))\n\n\n#endif /* CITUS_NODES_H */\n"
  },
  {
    "path": "src/include/distributed/citus_ruleutils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * citus_ruleutils.h\n *\t  Citus ruleutils wrapper functions and exported PostgreSQL ruleutils\n *\t  functions.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_RULEUTILS_H\n#define CITUS_RULEUTILS_H\n\n#include \"postgres.h\" /* IWYU pragma: keep */\n\n#include \"catalog/pg_sequence.h\"\n#include \"commands/sequence.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/coordinator_protocol.h\"\n\n/* Function declarations for version independent Citus ruleutils wrapper functions */\nextern char * pg_get_extensiondef_string(Oid tableRelationId);\nextern Oid get_extension_schema(Oid ext_oid);\nextern char * get_extension_version(Oid extensionId);\nextern char * pg_get_serverdef_string(Oid tableRelationId);\nextern char * pg_get_sequencedef_string(Oid sequenceRelid);\nextern Form_pg_sequence pg_get_sequencedef(Oid sequenceRelationId);\nextern char * pg_get_tableschemadef_string(Oid tableRelationId,\n\t\t\t\t\t\t\t\t\t\t   IncludeSequenceDefaults\n\t\t\t\t\t\t\t\t\t\t   includeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\t   IncludeIdentities includeIdentityDefaults,\n\t\t\t\t\t\t\t\t\t\t   char *accessMethod);\nextern void EnsureRelationKindSupported(Oid relationId);\nextern char * pg_get_tablecolumnoptionsdef_string(Oid tableRelationId);\nextern void deparse_shard_index_statement(IndexStmt *origStmt, Oid distrelid,\n\t\t\t\t\t\t\t\t\t\t  int64 shardid, StringInfo buffer);\nextern void deparse_shard_reindex_statement(ReindexStmt *origStmt, Oid distrelid,\n\t\t\t\t\t\t\t\t\t\t\tint64 shardid, StringInfo buffer);\nextern char * pg_get_indexclusterdef_string(Oid indexRelationId);\nextern List * pg_get_table_grants(Oid relationId);\nextern bool contain_nextval_expression_walker(Node *node, void *context);\nextern char * pg_get_replica_identity_command(Oid tableRelationId);\nextern List * pg_get_row_level_security_commands(Oid relationId);\nextern const char * RoleSpecString(RoleSpec *spec, bool withQuoteIdentifier);\nextern List * ExpandMergedSubscriptingRefEntries(List *targetEntryList);\nextern char * flatten_reloptions(Oid relid);\n\n/* Function declarations for version dependent PostgreSQL ruleutils functions */\nextern void pg_get_query_def(Query *query, StringInfo buffer);\nbool get_merged_argument_list(CallStmt *stmt, List **mergedNamedArgList,\n\t\t\t\t\t\t\t  Oid **mergedNamedArgTypes, List **mergedArgumentList,\n\t\t\t\t\t\t\t  int *totalArguments);\nchar * pg_get_rule_expr(Node *expression);\nextern void deparse_shard_query(Query *query, Oid distrelid, int64 shardid,\n\t\t\t\t\t\t\t\tStringInfo buffer);\nextern char * generate_relation_name(Oid relid, List *namespaces);\nextern char * generate_qualified_relation_name(Oid relid);\nextern char * generate_operator_name(Oid operid, Oid arg1, Oid arg2);\nextern List * getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype);\nextern void AppendOptionListToString(StringInfo stringData, List *options);\n\nextern void ensure_update_targetlist_in_param_order(List *targetList);\n\n#endif /* CITUS_RULEUTILS_H */\n"
  },
  {
    "path": "src/include/distributed/citus_safe_lib.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * safe_lib.h\n *\n * This file contains helper functions to expand on the _s functions from\n * safestringlib.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_safe_lib_H\n#define CITUS_safe_lib_H\n\n#include \"postgres.h\"\n\n#include \"safe_lib.h\"\n\nextern void ereport_constraint_handler(const char *message, void *pointer, errno_t error);\nextern int64 SafeStringToInt64(const char *str);\nextern int32 SafeStringToInt32(const char *str);\nextern uint64 SafeStringToUint64(const char *str);\nextern void SafeQsort(void *ptr, rsize_t count, rsize_t size,\n\t\t\t\t\t  int (*comp)(const void *, const void *));\nvoid * SafeBsearch(const void *key, const void *ptr, rsize_t count, rsize_t size,\n\t\t\t\t   int (*comp)(const void *, const void *));\nint SafeSnprintf(char *str, rsize_t count, const char *fmt, ...) pg_attribute_printf(3,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t 4);\n\n#define memset_struct_0(variable) memset(&variable, 0, sizeof(variable))\n\n#endif\n"
  },
  {
    "path": "src/include/distributed/clonenode_utils.h",
    "content": "#ifndef CLONENODE_UTILS_H\n#define CLONENODE_UTILS_H\n\n#include \"distributed/metadata_cache.h\"\n\nextern int64 GetReplicationLag(WorkerNode *primaryWorkerNode, WorkerNode *\n\t\t\t\t\t\t\t   replicaWorkerNode);\nextern void EnsureValidStreamingReplica(WorkerNode *primaryWorkerNode, char *\n\t\t\t\t\t\t\t\t\t\treplicaHostname, int replicaPort);\nextern void EnsureValidCloneMode(WorkerNode *primaryWorkerNode, char *cloneHostname, int\n\t\t\t\t\t\t\t\t clonePort, char *operation);\n\n#endif /* CLONE_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/colocation_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * colocation_utils.h\n *\n * Declarations for public utility functions related to co-located tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef COLOCATION_UTILS_H_\n#define COLOCATION_UTILS_H_\n\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/shardinterval_utils.h\"\n\n#define INVALID_COLOCATION_ID 0\n\nextern uint32 TableColocationId(Oid distributedTableId);\nextern bool TablesColocated(Oid leftDistributedTableId, Oid rightDistributedTableId);\nextern bool ShardsColocated(ShardInterval *leftShardInterval,\n\t\t\t\t\t\t\tShardInterval *rightShardInterval);\nextern List * ColocatedTableList(Oid distributedTableId);\nextern List * ColocatedShardIntervalList(ShardInterval *shardInterval);\nextern List * ColocatedNonPartitionShardIntervalList(ShardInterval *shardInterval);\nextern Oid ColocatedTableId(int32 colocationId);\nextern uint32 SingleShardTableColocationNodeId(uint32 colocationId);\nextern uint64 ColocatedShardIdInRelation(Oid relationId, int shardIndex);\nuint32 ColocationId(int shardCount, int replicationFactor, Oid distributionColumnType,\n\t\t\t\t\tOid distributionColumnCollation);\nextern uint32 CreateColocationGroup(int shardCount, int replicationFactor,\n\t\t\t\t\t\t\t\t\tOid distributionColumnType,\n\t\t\t\t\t\t\t\t\tOid distributionColumnCollation);\nextern void InsertColocationGroupLocally(uint32 colocationId, int shardCount,\n\t\t\t\t\t\t\t\t\t\t int replicationFactor,\n\t\t\t\t\t\t\t\t\t\t Oid distributionColumnType,\n\t\t\t\t\t\t\t\t\t\t Oid distributionColumnCollation);\nextern bool IsColocateWithNone(char *colocateWithTableName);\nextern bool IsColocateWithDefault(char *colocateWithTableName);\nextern uint32 GetNextColocationId(void);\nextern void ErrorIfShardPlacementsNotColocated(Oid leftRelationId, Oid rightRelationId);\nextern void CheckReplicationModel(Oid sourceRelationId, Oid targetRelationId);\nextern void CheckDistributionColumnType(Oid sourceRelationId, Oid targetRelationId);\nextern void EnsureColumnTypeEquality(Oid sourceRelationId, Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t Var *sourceDistributionColumn,\n\t\t\t\t\t\t\t\t\t Var *targetDistributionColumn);\nextern void UpdateRelationColocationGroup(Oid distributedRelationId, uint32 colocationId,\n\t\t\t\t\t\t\t\t\t\t  bool localOnly);\nextern void DeleteColocationGroupIfNoTablesBelong(uint32 colocationId);\nextern List * ColocationGroupTableList(uint32 colocationId, uint32 count);\nextern void DeleteColocationGroup(uint32 colocationId);\nextern void DeleteColocationGroupLocally(uint32 colocationId);\nextern uint32 FindColocateWithColocationId(Oid relationId, char replicationModel,\n\t\t\t\t\t\t\t\t\t\t   Var *distributionColumn,\n\t\t\t\t\t\t\t\t\t\t   int shardCount, bool shardCountIsStrict,\n\t\t\t\t\t\t\t\t\t\t   char *colocateWithTableName);\nextern void EnsureTableCanBeColocatedWith(Oid relationId, char replicationModel,\n\t\t\t\t\t\t\t\t\t\t  Var *distributionColumn, Oid sourceRelationId);\nextern void AcquireColocationDefaultLock(void);\nextern void ReleaseColocationDefaultLock(void);\n\n#endif /* COLOCATION_UTILS_H_ */\n"
  },
  {
    "path": "src/include/distributed/combine_query_planner.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * combine_query_planner.h\n *\t  Function declarations for building planned statements; these statements\n *\t  are then executed on the coordinator node.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef COMBINE_QUERY_PLANNER_H\n#define COMBINE_QUERY_PLANNER_H\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pathnodes.h\"\n#include \"nodes/plannodes.h\"\n\n\n/* Function declarations for building local plans on the coordinator node */\nstruct DistributedPlan;\nstruct CustomScan;\nextern Path * CreateCitusCustomScanPath(PlannerInfo *root, RelOptInfo *relOptInfo,\n\t\t\t\t\t\t\t\t\t\tIndex restrictionIndex, RangeTblEntry *rte,\n\t\t\t\t\t\t\t\t\t\tCustomScan *remoteScan);\nextern PlannedStmt * PlanCombineQuery(struct DistributedPlan *distributedPlan,\n\t\t\t\t\t\t\t\t\t  struct CustomScan *dataScan);\nextern bool FindCitusExtradataContainerRTE(Node *node, RangeTblEntry **result);\nextern bool ReplaceCitusExtraDataContainer;\nextern CustomScan *ReplaceCitusExtraDataContainerWithCustomScan;\n\n#endif   /* COMBINE_QUERY_PLANNER_H */\n"
  },
  {
    "path": "src/include/distributed/commands/multi_copy.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_copy.h\n *    Declarations for public functions and variables used in COPY for\n *    distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef MULTI_COPY_H\n#define MULTI_COPY_H\n\n\n#include \"nodes/execnodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"parser/parse_coerce.h\"\n#include \"tcop/dest.h\"\n\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/version_compat.h\"\n\n\n#define INVALID_PARTITION_COLUMN_INDEX -1\n\n\n/*\n * CitusCopyDest indicates the source or destination of a COPY command.\n */\ntypedef enum CitusCopyDest\n{\n\tCOPY_FILE,                  /* to/from file (or a piped program) */\n\tCOPY_FRONTEND,              /* to frontend */\n\tCOPY_CALLBACK               /* to/from callback function */\n} CitusCopyDest;\n\n\n/*\n * A smaller version of copy.c's CopyStateData, trimmed to the elements\n * necessary to copy out results. While it'd be a bit nicer to share code,\n * it'd require changing core postgres code.\n */\ntypedef struct CopyOutStateData\n{\n\tCitusCopyDest copy_dest;    /* type of copy source/destination */\n\tStringInfo fe_msgbuf;       /* used for all dests during COPY TO, only for\n\t                             * dest == COPY_NEW_FE in COPY FROM */\n\tList *attnumlist;           /* integer list of attnums to copy */\n\tint file_encoding;          /* file or remote side's character encoding */\n\tbool need_transcoding;              /* file encoding diff from server? */\n\tbool binary;                /* binary format? */\n\tchar *null_print;           /* NULL marker string (server encoding!) */\n\tchar *null_print_client;            /* same converted to file encoding */\n\tchar *delim;                /* column delimiter (must be 1 byte) */\n\n\tMemoryContext rowcontext;   /* per-row evaluation context */\n} CopyOutStateData;\n\ntypedef struct CopyOutStateData *CopyOutState;\n\n/* struct type to keep both hostname and port */\ntypedef struct NodeAddress\n{\n\tchar *nodeName;\n\tint32 nodePort;\n} NodeAddress;\n\n/* struct to allow rReceive to coerce tuples before sending them to workers  */\ntypedef struct CopyCoercionData\n{\n\tCoercionPathType coercionType;\n\tFmgrInfo coerceFunction;\n\n\tFmgrInfo inputFunction;\n\tFmgrInfo outputFunction;\n\tOid typioparam; /* inputFunction has an extra param */\n} CopyCoercionData;\n\n/* CopyDestReceiver can be used to stream results into a distributed table */\ntypedef struct CitusCopyDestReceiver\n{\n\t/* public DestReceiver interface */\n\tDestReceiver pub;\n\n\t/* relation and columns to which to copy */\n\tOid distributedRelationId;\n\tList *columnNameList;\n\tint partitionColumnIndex;\n\n\t/* open relation handle */\n\tRelation distributedRelation;\n\n\t/* descriptor of the tuples that are sent to the worker */\n\tTupleDesc tupleDescriptor;\n\n\t/* EState for per-tuple memory allocation */\n\tEState *executorState;\n\n\t/* MemoryContext for DestReceiver session */\n\tMemoryContext memoryContext;\n\n\t/* template for COPY statement to send to workers */\n\tCopyStmt *copyStatement;\n\n\t/*\n\t * shardId to CopyShardState map. Also used in insert_select_executor.c for\n\t * task pruning.\n\t */\n\tHTAB *shardStateHash;\n\n\t/* socket to CopyConnectionState map */\n\tHTAB *connectionStateHash;\n\n\t/* state on how to copy out data types */\n\tCopyOutState copyOutState;\n\tFmgrInfo *columnOutputFunctions;\n\n\t/* instructions for coercing incoming tuples */\n\tCopyCoercionData *columnCoercionPaths;\n\n\t/* number of tuples sent */\n\tint64 tuplesSent;\n\n\t/* useful for tracking multi shard accesses */\n\tbool multiShardCopy;\n\n\t/* if true, should copy to local placements in the current session */\n\tbool shouldUseLocalCopy;\n\n\t/*\n\t * if true, the data from this dest receiver should be published for CDC clients.\n\t * This is set tot false for internal transfers like shard split/move/rebalance etc.\n\t */\n\tbool isPublishable;\n\n\t/*\n\t * Copy into colocated intermediate result. When this is set, the\n\t * COPY assumes there are hypothetical colocated shards to the\n\t * relation that are files. And, COPY writes the data to the\n\t * files as if they are shards.\n\t */\n\tchar *colocatedIntermediateResultIdPrefix;\n\n\t/*\n\t * When copying into append-partitioned tables, the destination shard is chosen\n\t * upfront.\n\t */\n\tuint64 appendShardId;\n\n\t/*\n\t * When copying to intermediate files, we can skip coercions and run them\n\t * when merging into the target tables.\n\t */\n\tbool skipCoercions;\n\n\t/*\n\t * Determines whether the COPY command should track query stat counters.\n\t */\n\tbool trackQueryCounters;\n} CitusCopyDestReceiver;\n\n\n/* GUCs */\nextern bool SkipJsonbValidationInCopy;\n\n/* managed via GUC, the default is 4MB */\nextern int CopySwitchOverThresholdBytes;\n\n\n/* function declarations for copying into a distributed table */\nextern CitusCopyDestReceiver * CreateCitusCopyDestReceiver(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   List *columnNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   int partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   EState *executorState,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   char *intermediateResultPrefix,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   bool isPublishable,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   bool trackQueryCounters);\nextern FmgrInfo * ColumnOutputFunctions(TupleDesc rowDescriptor, bool binaryFormat);\nextern bool CanUseBinaryCopyFormat(TupleDesc tupleDescription);\nextern bool CanUseBinaryCopyFormatForTargetList(List *targetEntryList);\nextern bool CanUseBinaryCopyFormatForType(Oid typeId);\nextern void AppendCopyRowData(Datum *valueArray, bool *isNullArray,\n\t\t\t\t\t\t\t  TupleDesc rowDescriptor,\n\t\t\t\t\t\t\t  CopyOutState rowOutputState,\n\t\t\t\t\t\t\t  FmgrInfo *columnOutputFunctions,\n\t\t\t\t\t\t\t  CopyCoercionData *columnCoercionPaths);\nextern void AppendCopyBinaryHeaders(CopyOutState headerOutputState);\nextern void AppendCopyBinaryFooters(CopyOutState footerOutputState);\nextern void EndRemoteCopy(int64 shardId, List *connectionList);\nextern List * CreateRangeTable(Relation rel);\nextern Node * ProcessCopyStmt(CopyStmt *copyStatement,\n\t\t\t\t\t\t\t  QueryCompletion *completionTag,\n\t\t\t\t\t\t\t  const char *queryString);\nextern void CheckCopyPermissions(CopyStmt *copyStatement);\nextern bool IsCopyResultStmt(CopyStmt *copyStatement);\nextern void ConversionPathForTypes(Oid inputType, Oid destType, CopyCoercionData *result);\nextern Datum CoerceColumnValue(Datum inputValue, CopyCoercionData *coercionPath);\nextern void ReportCopyError(MultiConnection *connection, PGresult *result);\n\n\n#endif /* MULTI_COPY_H */\n"
  },
  {
    "path": "src/include/distributed/commands/sequence.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * sequence.h\n *\t  Functions for dealing with sequences\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_SEQUENCE_H\n#define CITUS_SEQUENCE_H\n\n#include \"access/attnum.h\"\n#include \"nodes/pg_list.h\"\n\n\nextern bool ColumnDefaultsToNextVal(Oid relationId, AttrNumber attrNumber);\nextern void ExtractDefaultColumnsAndOwnedSequences(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t   List **columnNameList,\n\t\t\t\t\t\t\t\t\t\t\t\t   List **ownedSequenceIdList);\n\n\n#endif /* CITUS_SEQUENCE_H */\n"
  },
  {
    "path": "src/include/distributed/commands/serialize_distributed_ddls.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * serialize_distributed_ddls.h\n *\n * Declarations for public functions related to serializing distributed\n * DDLs.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef SERIALIZE_DDLS_OVER_CATALOG_H\n#define SERIALIZE_DDLS_OVER_CATALOG_H\n\n#include \"postgres.h\"\n\n#include \"catalog/dependency.h\"\n\n/*\n * Note that those two lock types don't conflict with each other and are\n * acquired for different purposes. The lock on the object class\n * --SerializeDistributedDDLsOnObjectClass()-- is used to serialize DDLs\n * that target the object class itself, e.g., when creating a new object\n * of that class, and the latter one --SerializeDistributedDDLsOnObjectClassObject()--\n * is used to serialize DDLs that target a specific object of that class,\n * e.g., when altering an object.\n *\n * In some cases, we may want to acquire both locks at the same time. For\n * example, when renaming a database, we want to acquire both lock types\n * because while the object class lock is used to ensure that another session\n * doesn't create a new database with the same name, the object lock is used\n * to ensure that another session doesn't alter the same database.\n */\nextern void SerializeDistributedDDLsOnObjectClass(ObjectClass objectClass);\nextern void SerializeDistributedDDLsOnObjectClassObject(ObjectClass objectClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar *qualifiedObjectName);\n\n#endif /* SERIALIZE_DDLS_OVER_CATALOG_H */\n"
  },
  {
    "path": "src/include/distributed/commands/utility_hook.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * utility_hook.h\n *\t  Citus utility hook and related functionality.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef MULTI_UTILITY_H\n#define MULTI_UTILITY_H\n\n#include \"postgres.h\"\n\n#include \"tcop/utility.h\"\n#include \"utils/relcache.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/function_call_delegation.h\"\n#include \"distributed/version_compat.h\"\n#include \"distributed/worker_transaction.h\"\n\ntypedef enum\n{\n\tCREATE_OBJECT_PROPAGATION_DEFERRED = 0,\n\tCREATE_OBJECT_PROPAGATION_AUTOMATIC = 1,\n\tCREATE_OBJECT_PROPAGATION_IMMEDIATE = 2\n} CreateObjectPropagationOptions;\n\ntypedef enum\n{\n\tPROPSETCMD_INVALID = -1,\n\tPROPSETCMD_NONE, /* do not propagate SET commands */\n\tPROPSETCMD_LOCAL, /* propagate SET LOCAL commands */\n\tPROPSETCMD_SESSION, /* propagate SET commands, but not SET LOCAL ones */\n\tPROPSETCMD_ALL /* propagate all SET commands */\n} PropSetCmdBehavior;\nextern PropSetCmdBehavior PropagateSetCommands;\nextern bool EnableDDLPropagation;\nextern int CreateObjectPropagationMode;\nextern bool EnableCreateDatabasePropagation;\nextern bool EnableCreateTypePropagation;\nextern bool EnableCreateRolePropagation;\nextern bool EnableAlterRolePropagation;\nextern bool EnableAlterRoleSetPropagation;\nextern bool EnableAlterDatabaseOwner;\nextern int UtilityHookLevel;\nextern bool InDelegatedProcedureCall;\n\n\n/*\n * A DDLJob encapsulates the remote tasks and commands needed to process all or\n * part of a distributed DDL command. It hold the target object's address,\n * the original DDL command string (for MX DDL propagation), and a task list of\n * DDL_TASK-type Tasks to be executed.\n */\ntypedef struct DDLJob\n{\n\tObjectAddress targetObjectAddress;      /* target distributed object address */\n\n\t/*\n\t * Whether to commit and start a new transaction before sending commands\n\t * (only applies to CONCURRENTLY commands). This is needed for REINDEX CONCURRENTLY\n\t * and CREATE INDEX CONCURRENTLY on local shards, which would otherwise\n\t * get blocked waiting for the current transaction to finish.\n\t */\n\tbool startNewTransaction;\n\n\t/*\n\t * Command to run in MX nodes when metadata is synced\n\t * This is usually the initial (coordinator) DDL command string\n\t */\n\tconst char *metadataSyncCommand;\n\n\tList *taskList;            /* worker DDL tasks to execute */\n\n\t/*\n\t * Only applicable when any of the tasks cannot be executed in a\n\t * transaction block.\n\t *\n\t * Controls whether to emit a warning within the utility hook in case of a\n\t * failure.\n\t */\n\tbool warnForPartialFailure;\n} DDLJob;\n\nextern ProcessUtility_hook_type PrevProcessUtility;\n\nextern void citus_ProcessUtility(PlannedStmt *pstmt, const char *queryString,\n\t\t\t\t\t\t\t\t bool readOnlyTree,\n\t\t\t\t\t\t\t\t ProcessUtilityContext context, ParamListInfo params,\n\t\t\t\t\t\t\t\t struct QueryEnvironment *queryEnv, DestReceiver *dest,\n\t\t\t\t\t\t\t\t QueryCompletion *completionTag\n\t\t\t\t\t\t\t\t );\nextern void ProcessUtilityParseTree(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\tProcessUtilityContext context, ParamListInfo\n\t\t\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t\t\t\tDestReceiver *dest,\n\t\t\t\t\t\t\t\t\tQueryCompletion *completionTag\n\t\t\t\t\t\t\t\t\t);\nextern void MarkInvalidateForeignKeyGraph(void);\nextern void InvalidateForeignKeyGraphForDDL(void);\nextern List * DDLTaskList(Oid relationId, const char *commandString);\nextern List * NontransactionalNodeDDLTaskList(TargetWorkerSet targets, List *commands,\n\t\t\t\t\t\t\t\t\t\t\t  bool warnForPartialFailure);\nextern List * NodeDDLTaskList(TargetWorkerSet targets, List *commands);\nextern bool AlterTableInProgress(void);\nextern bool DropSchemaOrDBInProgress(void);\nextern void UndistributeDisconnectedCitusLocalTables(void);\nextern void NotifyUtilityHookConstraintDropped(void);\nextern void ResetConstraintDropped(void);\nextern void ExecuteDistributedDDLJob(DDLJob *ddlJob);\nextern bool IsDroppedOrGenerated(Form_pg_attribute attr);\n\n#endif /* MULTI_UTILITY_H */\n"
  },
  {
    "path": "src/include/distributed/commands.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * commands.h\n *    Declarations for public functions and variables used for all commands\n *    and DDL operations for citus. All declarations are grouped by the\n *    file that implements them.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_COMMANDS_H\n#define CITUS_COMMANDS_H\n\n#include \"postgres.h\"\n\n#include \"nodes/parsenodes.h\"\n#include \"tcop/dest.h\"\n#include \"tcop/utility.h\"\n#include \"utils/acl.h\"\n#include \"utils/rel.h\"\n\n#include \"distributed/metadata_utility.h\"\n\n\nextern bool AddAllLocalTablesToMetadata;\nextern bool EnableSchemaBasedSharding;\n\n/* controlled via GUC, should be accessed via EnableLocalReferenceForeignKeys() */\nextern bool EnableLocalReferenceForeignKeys;\n\n/*\n * GUC that controls whether to allow unique/exclude constraints without\n * distribution column.\n */\nextern bool AllowUnsafeConstraints;\n\nextern bool EnableUnsafeTriggers;\n\nextern int MaxMatViewSizeToAutoRecreate;\n\nextern bool EnforceLocalObjectRestrictions;\n\nextern void SwitchToSequentialAndLocalExecutionIfRelationNameTooLong(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t char *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t finalRelationName);\nextern void SwitchToSequentialAndLocalExecutionIfPartitionNameTooLong(Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  parentRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionRelationId)\n;\n\n/* DistOpsOperationType to be used in DistributeObjectOps */\ntypedef enum DistOpsOperationType\n{\n\tDIST_OPS_NONE,\n\tDIST_OPS_CREATE,\n\tDIST_OPS_ALTER,\n\tDIST_OPS_DROP,\n} DistOpsOperationType;\n\n\n/*\n * DistributeObjectOps specifies handlers for node/object type pairs.\n * Instances of this type should all be declared in deparse.c.\n * Handlers expect to receive the same Node passed to GetDistributeObjectOps.\n * Handlers may be NULL.\n *\n * Handlers:\n * deparse: return a string representation of the node.\n * qualify: replace names in tree with qualified names.\n * preprocess: executed before standard_ProcessUtility.\n * postprocess: executed after standard_ProcessUtility.\n * address: return an ObjectAddress for the subject of the statement.\n *          2nd parameter is missing_ok, and\n *          3rd parameter is isPostProcess.\n * markDistribued: true if the object will be distributed.\n *\n * preprocess/postprocess return a List of DDLJobs.\n */\ntypedef struct DistributeObjectOps\n{\n\tchar *(*deparse)(Node *);\n\tvoid (*qualify)(Node *);\n\tList *(*preprocess)(Node *, const char *, ProcessUtilityContext);\n\tList *(*postprocess)(Node *, const char *);\n\tList *(*address)(Node *, bool, bool);\n\tbool markDistributed;\n\n\t/* fields used by common implementations, omitted for specialized implementations */\n\tObjectType objectType;\n\n\t/*\n\t * Points to the varriable that contains the GUC'd feature flag, when turned off the\n\t * common propagation functions will not propagate the creation of the object.\n\t */\n\tbool *featureFlag;\n\n\t/* specifies the type of the operation */\n\tDistOpsOperationType operationType;\n} DistributeObjectOps;\n\n#define CITUS_TRUNCATE_TRIGGER_NAME \"citus_truncate_trigger\"\n\nconst DistributeObjectOps * GetDistributeObjectOps(Node *node);\n\n/* functions to support node-wide object management commands from non-main dbs */\nextern bool RunPreprocessNonMainDBCommand(Node *parsetree);\nextern void RunPostprocessNonMainDBCommand(Node *parsetree);\n\n/*\n * Flags that can be passed to GetForeignKeyOids to indicate\n * which foreign key constraint OIDs are to be extracted\n */\ntypedef enum ExtractForeignKeyConstraintsMode\n{\n\t/* extract the foreign key OIDs where the table is the referencing one */\n\tINCLUDE_REFERENCING_CONSTRAINTS = 1 << 0,\n\n\t/* extract the foreign key OIDs the table is the referenced one */\n\tINCLUDE_REFERENCED_CONSTRAINTS = 1 << 1,\n\n\t/* exclude the self-referencing foreign keys */\n\tEXCLUDE_SELF_REFERENCES = 1 << 2,\n\n\t/* any combination of the 5 flags below is supported */\n\t/* include foreign keys when the other table is a distributed table*/\n\tINCLUDE_DISTRIBUTED_TABLES = 1 << 3,\n\n\t/* include foreign keys when the other table is a reference table*/\n\tINCLUDE_REFERENCE_TABLES = 1 << 4,\n\n\t/* include foreign keys when the other table is a citus local table*/\n\tINCLUDE_CITUS_LOCAL_TABLES = 1 << 5,\n\n\t/* include foreign keys when the other table is a Postgres local table*/\n\tINCLUDE_LOCAL_TABLES = 1 << 6,\n\n\t/* include foreign keys when the other table is a single shard table*/\n\tINCLUDE_SINGLE_SHARD_TABLES = 1 << 7,\n\n\t/* include foreign keys regardless of the other table's type */\n\tINCLUDE_ALL_TABLE_TYPES = INCLUDE_DISTRIBUTED_TABLES | INCLUDE_REFERENCE_TABLES |\n\t\t\t\t\t\t\t  INCLUDE_CITUS_LOCAL_TABLES | INCLUDE_LOCAL_TABLES |\n\t\t\t\t\t\t\t  INCLUDE_SINGLE_SHARD_TABLES\n} ExtractForeignKeyConstraintMode;\n\n\n/*\n * Flags that can be passed to GetForeignKeyIdsForColumn to\n * indicate whether relationId argument should match:\n *   - referencing relation or,\n *   - referenced relation,\n *  or we are searching for both sides.\n */\ntypedef enum SearchForeignKeyColumnFlags\n{\n\t/* relationId argument should match referencing relation */\n\tSEARCH_REFERENCING_RELATION = 1 << 0,\n\n\t/* relationId argument should match referenced relation */\n\tSEARCH_REFERENCED_RELATION = 1 << 1,\n\n\t/* callers can also pass union of above flags */\n} SearchForeignKeyColumnFlags;\n\n\ntypedef enum TenantOperation\n{\n\tTENANT_UNDISTRIBUTE_TABLE = 0,\n\tTENANT_ALTER_TABLE,\n\tTENANT_COLOCATE_WITH,\n\tTENANT_UPDATE_COLOCATION,\n\tTENANT_SET_SCHEMA,\n} TenantOperation;\n\n#define TOTAL_TENANT_OPERATION 5\nextern const char *TenantOperationNames[TOTAL_TENANT_OPERATION];\n\n/* begin.c - forward declarations */\nextern void SaveBeginCommandProperties(TransactionStmt *transactionStmt);\n\n/* cluster.c - forward declarations */\nextern List * PreprocessClusterStmt(Node *node, const char *clusterCommand,\n\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext);\n\n/* common.c - forward declarations*/\nextern List * PostprocessCreateDistributedObjectFromCatalogStmt(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst char *queryString);\nextern List * PreprocessAlterDistributedObjectStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t   ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t   processUtilityContext);\nextern List * PostprocessAlterDistributedObjectStmt(Node *stmt, const char *queryString);\nextern List * PreprocessDropDistributedObjectStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t  ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t  processUtilityContext);\nextern List * DropTextSearchConfigObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\tisPostprocess);\nextern List * DropTextSearchDictObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t  isPostprocess);\n\n/* index.c */\ntypedef void (*PGIndexProcessor)(Form_pg_index, List **, int);\n\n\n/* call.c */\nextern bool CallDistributedProcedureRemotely(CallStmt *callStmt, DestReceiver *dest);\n\n\n/* collation.c - forward declarations */\nextern char * CreateCollationDDL(Oid collationId);\nextern List * CreateCollationDDLsIdempotent(Oid collationId);\nextern List * AlterCollationOwnerObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t   isPostprocess);\nextern List * RenameCollationStmtObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t   isPostprocess);\nextern List * AlterCollationSchemaStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbool missing_ok, bool isPostprocess);\nextern char * GenerateBackupNameForCollationCollision(const ObjectAddress *address);\nextern List * DefineCollationStmtObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t   isPostprocess);\n\n/* database.c - forward declarations */\nextern List * AlterDatabaseOwnerObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern List * DatabaseOwnerDDLCommands(const ObjectAddress *address);\n\nextern List * PreprocessGrantOnDatabaseStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext);\n\nextern List * PreprocessAlterDatabaseStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\n\nextern List * PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t\t processUtilityContext);\nextern List * GetDatabaseMetadataSyncCommands(Oid dbOid);\n\n\nextern List * PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext);\n\nextern List * PreprocessCreateDatabaseStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext);\nextern List * PostprocessCreateDatabaseStmt(Node *node, const char *queryString);\nextern List * PreprocessDropDatabaseStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext);\nextern List * DropDatabaseStmtObjectAddress(Node *node, bool missingOk,\n\t\t\t\t\t\t\t\t\t\t\tbool isPostprocess);\nextern List * CreateDatabaseStmtObjectAddress(Node *node, bool missingOk,\n\t\t\t\t\t\t\t\t\t\t\t  bool isPostprocess);\nextern List * GenerateGrantDatabaseCommandList(void);\nextern List * PreprocessAlterDatabaseRenameStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\tprocessUtilityContext);\nextern List * PostprocessAlterDatabaseRenameStmt(Node *node, const char *queryString);\nextern void EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt);\nextern char * CreateDatabaseDDLCommand(Oid dbId);\n\n\n/* domain.c - forward declarations */\nextern List * CreateDomainStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\tisPostprocess);\nextern List * AlterDomainStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t   isPostprocess);\nextern List * DomainRenameConstraintStmtObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern List * AlterDomainOwnerStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\tisPostprocess);\nextern List * RenameDomainStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\tisPostprocess);\nextern CreateDomainStmt * RecreateDomainStmt(Oid domainOid);\nextern Oid get_constraint_typid(Oid conoid);\n\n\n/* extension.c - forward declarations */\nextern bool IsDropCitusExtensionStmt(Node *parsetree);\nextern List * GetDependentFDWsToExtension(Oid extensionId);\nextern bool IsCreateAlterExtensionUpdateCitusStmt(Node *parsetree);\nextern void PreprocessCreateExtensionStmtForCitusColumnar(Node *parsetree);\nextern void PreprocessAlterExtensionCitusStmtForCitusColumnar(Node *parsetree);\nextern void PostprocessAlterExtensionCitusStmtForCitusColumnar(Node *parsetree);\nextern bool ShouldMarkRelationDistributed(Oid relationId);\nextern void ErrorIfUnstableCreateOrAlterExtensionStmt(Node *parsetree);\nextern List * PostprocessCreateExtensionStmt(Node *stmt, const char *queryString);\nextern List * PreprocessDropExtensionStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\nextern List * PreprocessAlterExtensionSchemaStmt(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t processUtilityContext);\nextern List * PostprocessAlterExtensionSchemaStmt(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t  const char *queryString);\nextern List * PreprocessAlterExtensionUpdateStmt(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t processUtilityContext);\nextern void PostprocessAlterExtensionCitusUpdateStmt(Node *node);\nextern List * PreprocessAlterExtensionContentsStmt(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t   const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t   ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t   processUtilityContext);\nextern List * CreateExtensionDDLCommand(const ObjectAddress *extensionAddress);\nextern List * AlterExtensionSchemaStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbool missing_ok, bool isPostprocess);\nextern List * AlterExtensionUpdateStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbool missing_ok, bool isPostprocess);\nextern void CreateExtensionWithVersion(char *extname, char *extVersion);\nextern void AlterExtensionUpdateStmt(char *extname, char *extVersion);\nextern int GetExtensionVersionNumber(char *extVersion);\n\n/* foreign_constraint.c - forward declarations */\nextern bool ConstraintIsAForeignKeyToReferenceTable(char *constraintName,\n\t\t\t\t\t\t\t\t\t\t\t\t\tOid leftRelationId);\nextern void ErrorIfUnsupportedForeignConstraintExists(Relation relation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  char referencingReplicationModel,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  char distributionMethod,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  Var *distributionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  uint32 colocationId);\nextern void EnsureNoFKeyFromTableType(Oid relationId, int tableTypeFlag);\nextern void EnsureNoFKeyToTableType(Oid relationId, int tableTypeFlag);\nextern void ErrorOutForFKeyBetweenPostgresAndCitusLocalTable(Oid localTableId);\nextern bool ColumnReferencedByAnyForeignKey(char *columnName, Oid relationId);\nextern bool ColumnAppearsInForeignKey(char *columnName, Oid relationId);\nextern bool ColumnAppearsInForeignKeyToReferenceTable(char *columnName, Oid\n\t\t\t\t\t\t\t\t\t\t\t\t\t  relationId);\nextern List * GetReferencingForeignConstaintCommands(Oid relationOid);\nextern List * GetForeignConstraintToReferenceTablesCommands(Oid relationId);\nextern List * GetForeignConstraintFromOtherReferenceTablesCommands(Oid relationId);\nextern List * GetForeignConstraintToDistributedTablesCommands(Oid relationId);\nextern List * GetForeignConstraintFromDistributedTablesCommands(Oid relationId);\nextern List * GetForeignConstraintCommandsInternal(Oid relationId, int flags);\nextern Oid DropFKeysAndUndistributeTable(Oid relationId);\nextern void DropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag);\nextern List * GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   int tableTypeFlag);\nextern bool AnyForeignKeyDependsOnIndex(Oid indexId);\nextern bool HasForeignKeyWithLocalTable(Oid relationId);\nextern bool HasForeignKeyToReferenceTable(Oid relationOid);\nextern List * GetForeignKeysFromLocalTables(Oid relationId);\nextern bool TableReferenced(Oid relationOid);\nextern bool TableReferencing(Oid relationOid);\nextern bool ConstraintIsAUniquenessConstraint(char *inputConstaintName, Oid relationId);\nextern bool ConstraintIsAForeignKey(char *inputConstaintName, Oid relationOid);\nextern bool ConstraintWithNameIsOfType(char *inputConstaintName, Oid relationId,\n\t\t\t\t\t\t\t\t\t   char targetConstraintType);\nextern bool ConstraintWithIdIsOfType(Oid constraintId, char targetConstraintType);\nextern bool TableHasExternalForeignKeys(Oid relationId);\nextern List * GetForeignKeyOids(Oid relationId, int flags);\nextern Oid GetReferencedTableId(Oid foreignKeyId);\nextern Oid GetReferencingTableId(Oid foreignKeyId);\nextern bool RelationInvolvedInAnyNonInheritedForeignKeys(Oid relationId);\n\n\n/* foreign_data_wrapper.c - forward declarations */\nextern List * PreprocessGrantOnFDWStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext);\nextern Acl * GetPrivilegesForFDW(Oid FDWOid);\n\n\n/* foreign_server.c - forward declarations */\nextern List * PreprocessGrantOnForeignServerStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t processUtilityContext);\nextern List * CreateForeignServerStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t   isPostprocess);\nextern List * AlterForeignServerStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern List * RenameForeignServerStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t   isPostprocess);\nextern List * AlterForeignServerOwnerStmtObjectAddress(Node *node, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t   missing_ok, bool isPostprocess);\nextern List * GetForeignServerCreateDDLCommand(Oid serverId);\n\n\n/* foreign_table.c - forward declarations */\nextern List * PreprocessAlterForeignTableSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t\tprocessUtilityContext);\n\n\n/* function.c - forward declarations */\nextern List * PreprocessCreateFunctionStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext);\nextern List * PostprocessCreateFunctionStmt(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\tconst char *queryString);\nextern List * CreateFunctionStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t  bool missing_ok, bool isPostprocess);\nextern List * DefineAggregateStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t   bool missing_ok, bool isPostprocess);\nextern List * PreprocessAlterFunctionStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\nextern List * AlterFunctionStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t bool missing_ok, bool isPostprocess);\nextern List * RenameFunctionStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t  bool missing_ok, bool isPostprocess);\nextern List * AlterFunctionOwnerObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t  bool missing_ok, bool isPostprocess);\nextern List * AlterFunctionSchemaStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t   bool missing_ok, bool isPostprocess);\nextern List * PreprocessAlterFunctionDependsStmt(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t processUtilityContext);\nextern List * AlterFunctionDependsStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbool missing_ok, bool isPostprocess);\nextern List * PreprocessGrantOnFunctionStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext);\nextern List * PostprocessGrantOnFunctionStmt(Node *node, const char *queryString);\n\n\n/* grant.c - forward declarations */\nextern List * PreprocessGrantStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\nextern void deparsePrivileges(StringInfo privsString, GrantStmt *grantStmt);\nextern void deparseGrantees(StringInfo granteesString, GrantStmt *grantStmt);\n\n\n/* index.c - forward declarations */\nextern bool IsIndexRenameStmt(RenameStmt *renameStmt);\nextern List * PreprocessIndexStmt(Node *createIndexStatement,\n\t\t\t\t\t\t\t\t  const char *createIndexCommand,\n\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\nextern char * ChooseIndexName(const char *tabname, Oid namespaceId,\n\t\t\t\t\t\t\t  List *colnames, List *exclusionOpNames,\n\t\t\t\t\t\t\t  bool primary, bool isconstraint);\nextern char * ChooseIndexNameAddition(List *colnames);\nextern List * ChooseIndexColumnNames(List *indexElems);\nextern LOCKMODE GetCreateIndexRelationLockMode(IndexStmt *createIndexStatement);\nextern List * PreprocessReindexStmt(Node *ReindexStatement,\n\t\t\t\t\t\t\t\t\tconst char *ReindexCommand,\n\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext);\nextern List * ReindexStmtObjectAddress(Node *stmt, bool missing_ok, bool isPostprocess);\nextern List * PreprocessDropIndexStmt(Node *dropIndexStatement,\n\t\t\t\t\t\t\t\t\t  const char *dropIndexCommand,\n\t\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\nextern List * PostprocessIndexStmt(Node *node,\n\t\t\t\t\t\t\t\t   const char *queryString);\nextern void ErrorIfUnsupportedAlterIndexStmt(AlterTableStmt *alterTableStatement);\nextern void MarkIndexValid(IndexStmt *indexStmt);\nextern List * ExecuteFunctionOnEachTableIndex(Oid relationId, PGIndexProcessor\n\t\t\t\t\t\t\t\t\t\t\t  pgIndexProcessor, int flags);\nextern bool IsReindexWithParam_compat(ReindexStmt *stmt, char *paramName);\n\n/* objectaddress.c - forward declarations */\nextern List * CreateExtensionStmtObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t   isPostprocess);\n\n/* owned.c -  forward declarations */\nextern List * PreprocessDropOwnedStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\nextern List * PostprocessReassignOwnedStmt(Node *node, const char *queryString);\n\n/* policy.c -  forward declarations */\nextern List * CreatePolicyCommands(Oid relationId);\nextern void ErrorIfUnsupportedPolicy(Relation relation);\nextern void ErrorIfUnsupportedPolicyExpr(Node *expr);\nextern List * PostprocessCreatePolicyStmt(Node *node, const char *queryString);\nextern List * PreprocessAlterPolicyStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext);\nextern List * PreprocessDropPolicyStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext);\nextern bool IsPolicyRenameStmt(RenameStmt *stmt);\nextern void CreatePolicyEventExtendNames(CreatePolicyStmt *stmt, const char *schemaName,\n\t\t\t\t\t\t\t\t\t\t uint64 shardId);\nextern void AlterPolicyEventExtendNames(AlterPolicyStmt *stmt, const char *schemaName,\n\t\t\t\t\t\t\t\t\t\tuint64 shardId);\nextern void RenamePolicyEventExtendNames(RenameStmt *stmt, const char *schemaName, uint64\n\t\t\t\t\t\t\t\t\t\t shardId);\nextern void DropPolicyEventExtendNames(DropStmt *stmt, const char *schemaName, uint64\n\t\t\t\t\t\t\t\t\t   shardId);\n\nextern void AddRangeTableEntryToQueryCompat(ParseState *parseState, Relation relation);\n\n/* publication.c - forward declarations */\nextern List * PostProcessCreatePublicationStmt(Node *node, const char *queryString);\nextern List * CreatePublicationDDLCommandsIdempotent(const ObjectAddress *address);\nextern char * CreatePublicationDDLCommand(Oid publicationId);\nextern List * PreprocessAlterPublicationStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityCtx);\nextern List * GetAlterPublicationDDLCommandsForTable(Oid relationId, bool isAdd);\nextern char * GetAlterPublicationTableDDLCommand(Oid publicationId, Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t bool isAdd);\nextern List * AlterPublicationOwnerStmtObjectAddress(Node *node, bool missingOk,\n\t\t\t\t\t\t\t\t\t\t\t\t\t bool isPostProcess);\nextern List * AlterPublicationStmtObjectAddress(Node *node, bool missingOk,\n\t\t\t\t\t\t\t\t\t\t\t\tbool isPostProcess);\nextern List * CreatePublicationStmtObjectAddress(Node *node, bool missingOk,\n\t\t\t\t\t\t\t\t\t\t\t\t bool isPostProcess);\nextern List * RenamePublicationStmtObjectAddress(Node *node, bool missingOk,\n\t\t\t\t\t\t\t\t\t\t\t\t bool isPostProcess);\n\n/* rename.c - forward declarations*/\nextern List * PreprocessRenameStmt(Node *renameStmt, const char *renameCommand,\n\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext);\nextern void ErrorIfUnsupportedRenameStmt(RenameStmt *renameStmt);\nextern List * PreprocessRenameAttributeStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext);\n\n\n/* role.c - forward declarations*/\nextern List * PostprocessAlterRoleStmt(Node *stmt, const char *queryString);\nextern List * PreprocessAlterRoleSetStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext);\n\nextern List * PreprocessAlterRoleRenameStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext);\n\nextern List * GenerateAlterRoleSetCommandForRole(Oid roleid);\nextern List * AlterRoleStmtObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t bool missing_ok, bool isPostprocess);\nextern List * AlterRoleSetStmtObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\tbool missing_ok, bool isPostprocess);\nextern List * PreprocessCreateRoleStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext);\nextern List * PreprocessDropRoleStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext);\nextern List * PreprocessGrantRoleStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\nextern List * PostprocessGrantRoleStmt(Node *stmt, const char *queryString);\nextern List * GenerateCreateOrAlterRoleCommand(Oid roleOid);\nextern List * CreateRoleStmtObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t  isPostprocess);\n\nextern List * RenameRoleStmtObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t  isPostprocess);\n\nextern void UnmarkRolesDistributed(List *roles);\nextern List * FilterDistributedRoles(List *roles);\n\n/* schema.c - forward declarations */\nextern List * PostprocessCreateSchemaStmt(Node *node, const char *queryString);\nextern List * PreprocessDropSchemaStmt(Node *dropSchemaStatement,\n\t\t\t\t\t\t\t\t\t   const char *queryString,\n\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext);\nextern List * PreprocessAlterObjectSchemaStmt(Node *alterObjectSchemaStmt,\n\t\t\t\t\t\t\t\t\t\t\t  const char *alterObjectSchemaCommand);\nextern List * PreprocessGrantOnSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\nextern List * CreateSchemaStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\tisPostprocess);\nextern List * AlterSchemaOwnerStmtObjectAddress(Node *node, bool missing_ok,\n\t\t\t\t\t\t\t\t\t\t\t\tbool isPostprocess);\nextern List * AlterSchemaRenameStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t isPostprocess);\n\n/* seclabel.c - forward declarations*/\nextern List * PostprocessAnySecLabelStmt(Node *node, const char *queryString);\nextern List * PostprocessRoleSecLabelStmt(Node *node, const char *queryString);\nextern List * PostprocessTableOrColumnSecLabelStmt(Node *node, const char *queryString);\nextern List * SecLabelStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess);\nextern void citus_test_object_relabel(const ObjectAddress *object, const char *seclabel);\n\n/* sequence.c - forward declarations */\nextern List * PreprocessAlterSequenceStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\nextern List * PreprocessAlterSequenceSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\tprocessUtilityContext);\nextern List * PostprocessAlterSequenceSchemaStmt(Node *node, const char *queryString);\nextern List * PreprocessAlterSequenceOwnerStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t   ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t   processUtilityContext);\nextern List * PostprocessAlterSequenceOwnerStmt(Node *node, const char *queryString);\nextern List * PreprocessAlterSequencePersistenceStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t\t processUtilityContext);\nextern List * PreprocessSequenceAlterTableStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t   ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t   processUtilityContext);\nextern List * PreprocessDropSequenceStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext);\nextern List * SequenceDropStmtObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\tisPostprocess);\nextern List * PreprocessRenameSequenceStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext);\nextern List * PreprocessGrantOnSequenceStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext);\nextern List * PostprocessGrantOnSequenceStmt(Node *node, const char *queryString);\nextern List * AlterSequenceStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t isPostprocess);\nextern List * AlterSequenceSchemaStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t   isPostprocess);\nextern List * AlterSequenceOwnerStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern List * AlterSequencePersistenceStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tisPostprocess);\nextern List * RenameSequenceStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern void ErrorIfUnsupportedSeqStmt(CreateSeqStmt *createSeqStmt);\nextern void ErrorIfDistributedAlterSeqOwnedBy(AlterSeqStmt *alterSeqStmt);\nextern char * GenerateBackupNameForSequenceCollision(const ObjectAddress *address);\nextern void RenameExistingSequenceWithDifferentTypeIfExists(RangeVar *sequence,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOid desiredSeqTypeId);\n\n/* statistics.c - forward declarations */\nextern List * PreprocessCreateStatisticsStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext);\nextern List * PostprocessCreateStatisticsStmt(Node *node, const char *queryString);\nextern List * CreateStatisticsStmtObjectAddress(Node *node, bool missingOk, bool\n\t\t\t\t\t\t\t\t\t\t\t\tisPostprocess);\nextern List * PreprocessDropStatisticsStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext);\nextern List * DropStatisticsObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern List * PreprocessAlterStatisticsRenameStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t  ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t  processUtilityContext);\nextern List * PreprocessAlterStatisticsSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t  ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t  processUtilityContext);\nextern List * PostprocessAlterStatisticsSchemaStmt(Node *node, const char *queryString);\nextern List * AlterStatisticsSchemaStmtObjectAddress(Node *node, bool missingOk, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t isPostprocess);\nextern List * PreprocessAlterStatisticsStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext);\nextern List * PreprocessAlterStatisticsOwnerStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\t processUtilityContext);\nextern List * PostprocessAlterStatisticsOwnerStmt(Node *node, const char *queryString);\nextern List * GetExplicitStatisticsCommandList(Oid relationId);\nextern List * GetExplicitStatisticsSchemaIdList(Oid relationId);\nextern List * GetAlterIndexStatisticsCommands(Oid indexOid);\n\n/* subscription.c - forward declarations */\nextern Node * ProcessCreateSubscriptionStmt(CreateSubscriptionStmt *createSubStmt);\n\n\n/* table.c - forward declarations */\nextern List * PreprocessDropTableStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\nextern void PostprocessCreateTableStmt(CreateStmt *createStatement,\n\t\t\t\t\t\t\t\t\t   const char *queryString);\nextern bool ShouldEnableLocalReferenceForeignKeys(void);\nextern List * PreprocessAlterTableStmtAttachPartition(AlterTableStmt *alterTableStatement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  const char *queryString);\nextern List * PostprocessAlterTableSchemaStmt(Node *node, const char *queryString);\nextern void PrepareAlterTableStmtForConstraint(AlterTableStmt *alterTableStatement,\n\t\t\t\t\t\t\t\t\t\t\t   Oid relationId, Constraint *constraint);\nextern List * PreprocessAlterTableStmt(Node *node, const char *alterTableCommand,\n\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext);\nextern List * PreprocessAlterTableMoveAllStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t  ProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t  processUtilityContext);\nextern List * PreprocessAlterTableSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext);\nextern void SkipForeignKeyValidationIfConstraintIsFkey(AlterTableStmt *alterTableStmt,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   bool processLocalRelation);\nextern bool IsAlterTableRenameStmt(RenameStmt *renameStmt);\nextern void ErrorIfAlterDropsPartitionColumn(AlterTableStmt *alterTableStatement);\nextern void PostprocessAlterTableStmt(AlterTableStmt *pStmt);\nextern void FixAlterTableStmtIndexNames(AlterTableStmt *pStmt);\nextern void ErrorUnsupportedAlterTableAddColumn(Oid relationId, AlterTableCmd *command,\n\t\t\t\t\t\t\t\t\t\t\t\tConstraint *constraint);\nextern void ErrorIfUnsupportedConstraint(Relation relation, char distributionMethod,\n\t\t\t\t\t\t\t\t\t\t char referencingReplicationModel,\n\t\t\t\t\t\t\t\t\t\t Var *distributionColumn, uint32 colocationId);\nextern List * InterShardDDLTaskList(Oid leftRelationId, Oid rightRelationId,\n\t\t\t\t\t\t\t\t\tconst char *commandString);\nextern List * AlterTableSchemaStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\tbool missing_ok, bool isPostprocess);\nextern List * MakeNameListFromRangeVar(const RangeVar *rel);\nextern Oid GetSequenceOid(Oid relationId, AttrNumber attnum);\nextern bool ConstrTypeUsesIndex(ConstrType constrType);\nextern bool ConstrTypeCitusCanDefaultName(ConstrType constrType);\nextern char * GetAlterColumnWithNextvalDefaultCmd(Oid sequenceOid, Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t  char *colname, bool missingTableOk);\n\nextern void ErrorIfTableHasIdentityColumn(Oid relationId);\nextern void ConvertNewTableIfNecessary(Node *createStmt);\nextern void ConvertToTenantTableIfNecessary(AlterObjectSchemaStmt *alterObjectSchemaStmt);\n\n/* text_search.c - forward declarations */\nextern List * GetCreateTextSearchConfigStatements(const ObjectAddress *address);\nextern List * GetCreateTextSearchDictionaryStatements(const ObjectAddress *address);\nextern List * CreateTextSearchConfigDDLCommandsIdempotent(const ObjectAddress *address);\nextern List * CreateTextSearchDictDDLCommandsIdempotent(const ObjectAddress *address);\nextern List * CreateTextSearchConfigurationObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t isPostprocess);\nextern List * CreateTextSearchDictObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\tbool missing_ok, bool isPostprocess);\nextern List * RenameTextSearchConfigurationStmtObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t isPostprocess);\nextern List * RenameTextSearchDictionaryStmtObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern List * AlterTextSearchConfigurationStmtObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tisPostprocess);\nextern List * AlterTextSearchDictionaryStmtObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t isPostprocess);\nextern List * AlterTextSearchConfigurationSchemaStmtObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern List * AlterTextSearchDictionarySchemaStmtObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   isPostprocess);\nextern List * AlterTextSearchConfigurationOwnerObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t isPostprocess);\nextern List * AlterTextSearchDictOwnerObjectAddress(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbool missing_ok, bool isPostprocess);\nextern char * GenerateBackupNameForTextSearchConfiguration(const ObjectAddress *address);\nextern char * GenerateBackupNameForTextSearchDict(const ObjectAddress *address);\nextern List * get_ts_config_namelist(Oid tsconfigOid);\n\n/* truncate.c - forward declarations */\nextern void PreprocessTruncateStatement(TruncateStmt *truncateStatement);\n\n/* type.c - forward declarations */\nextern List * PreprocessRenameTypeAttributeStmt(Node *stmt, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\tprocessUtilityContext);\nextern Node * CreateTypeStmtByObjectAddress(const ObjectAddress *address);\nextern List * CompositeTypeStmtObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t isPostprocess);\nextern List * CreateEnumStmtObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern List * AlterTypeStmtObjectAddress(Node *stmt, bool missing_ok, bool isPostprocess);\nextern List * AlterEnumStmtObjectAddress(Node *stmt, bool missing_ok, bool isPostprocess);\nextern List * RenameTypeStmtObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern List * AlterTypeSchemaStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t   bool missing_ok, bool isPostprocess);\nextern List * RenameTypeAttributeStmtObjectAddress(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t   bool missing_ok);\nextern List * AlterTypeOwnerObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern List * CreateTypeDDLCommandsIdempotent(const ObjectAddress *typeAddress);\nextern char * GenerateBackupNameForTypeCollision(const ObjectAddress *address);\n\n/* function.c - forward declarations */\nextern List * CreateFunctionDDLCommandsIdempotent(const ObjectAddress *functionAddress);\nextern char * GetFunctionDDLCommand(const RegProcedure funcOid, bool useCreateOrReplace);\nextern char * GenerateBackupNameForProcCollision(const ObjectAddress *address);\nextern ObjectWithArgs * ObjectWithArgsFromOid(Oid funcOid);\nextern void UpdateFunctionDistributionInfo(const ObjectAddress *distAddress,\n\t\t\t\t\t\t\t\t\t\t   int *distribution_argument_index,\n\t\t\t\t\t\t\t\t\t\t   int *colocationId,\n\t\t\t\t\t\t\t\t\t\t   bool *forceDelegation);\n\n/* vacuum.c - forward declarations */\nextern List * PostprocessVacuumStmt(Node *node, const char *vacuumCommand);\n\n/* view.c - forward declarations */\nextern List * PreprocessViewStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext);\nextern List * PostprocessViewStmt(Node *node, const char *queryString);\nextern List * ViewStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess);\nextern List * AlterViewStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess);\nextern List * PreprocessDropViewStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t ProcessUtilityContext processUtilityContext);\nextern List * DropViewStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess);\nextern char * CreateViewDDLCommand(Oid viewOid);\nextern List * GetViewCreationCommandsOfTable(Oid relationId);\nextern char * AlterViewOwnerCommand(Oid viewOid);\nextern char * DeparseViewStmt(Node *node);\nextern char * DeparseDropViewStmt(Node *node);\nextern bool IsViewDistributed(Oid viewOid);\nextern List * CreateViewDDLCommandsIdempotent(Oid viewOid);\nextern List * PreprocessAlterViewStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t  ProcessUtilityContext processUtilityContext);\nextern List * PostprocessAlterViewStmt(Node *node, const char *queryString);\nextern List * PreprocessRenameViewStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t   ProcessUtilityContext processUtilityContext);\nextern List * RenameViewStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t  isPostprocess);\nextern List * PreprocessAlterViewSchemaStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext);\nextern List * PostprocessAlterViewSchemaStmt(Node *node, const char *queryString);\nextern List * AlterViewSchemaStmtObjectAddress(Node *node, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t   isPostprocess);\nextern bool IsViewRenameStmt(RenameStmt *renameStmt);\n\n/* trigger.c - forward declarations */\nextern List * GetExplicitTriggerCommandList(Oid relationId);\nextern HeapTuple GetTriggerTupleById(Oid triggerId, bool missingOk);\nextern List * GetExplicitTriggerIdList(Oid relationId);\nextern List * PostprocessCreateTriggerStmt(Node *node, const char *queryString);\nextern List * CreateTriggerStmtObjectAddress(Node *node, bool missingOk, bool\n\t\t\t\t\t\t\t\t\t\t\t isPostprocess);\nextern void CreateTriggerEventExtendNames(CreateTrigStmt *createTriggerStmt,\n\t\t\t\t\t\t\t\t\t\t  char *schemaName, uint64 shardId);\nextern List * PostprocessAlterTriggerRenameStmt(Node *node, const char *queryString);\nextern void AlterTriggerRenameEventExtendNames(RenameStmt *renameTriggerStmt,\n\t\t\t\t\t\t\t\t\t\t\t   char *schemaName, uint64 shardId);\nextern List * PostprocessAlterTriggerDependsStmt(Node *node, const char *queryString);\nextern List * PreprocessAlterTriggerDependsStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\t\t\tProcessUtilityContext\n\t\t\t\t\t\t\t\t\t\t\t\tprocessUtilityContext);\nextern void AlterTriggerDependsEventExtendNames(AlterObjectDependsStmt *\n\t\t\t\t\t\t\t\t\t\t\t\talterTriggerDependsStmt,\n\t\t\t\t\t\t\t\t\t\t\t\tchar *schemaName, uint64 shardId);\nextern void ErrorOutForTriggerIfNotSupported(Oid relationId);\nextern void ErrorIfRelationHasUnsupportedTrigger(Oid relationId);\nextern List * PreprocessDropTriggerStmt(Node *node, const char *queryString,\n\t\t\t\t\t\t\t\t\t\tProcessUtilityContext processUtilityContext);\nextern void DropTriggerEventExtendNames(DropStmt *dropTriggerStmt, char *schemaName,\n\t\t\t\t\t\t\t\t\t\tuint64 shardId);\nextern List * CitusCreateTriggerCommandDDLJob(Oid relationId, char *triggerName,\n\t\t\t\t\t\t\t\t\t\t\t  const char *queryString);\nextern Oid GetTriggerFunctionId(Oid triggerId);\n\n/* cascade_table_operation_for_connected_relations.c */\n\n/*\n * Flags that can be passed to CascadeOperationForFkeyConnectedRelations, and\n * CascadeOperationForRelationIdList to specify\n * citus table function to be executed in cascading mode.\n */\ntypedef enum CascadeOperationType\n{\n\tINVALID_OPERATION = 1 << 0,\n\n\t/* execute UndistributeTable on each relation */\n\tCASCADE_FKEY_UNDISTRIBUTE_TABLE = 1 << 1,\n\n\t/* execute CreateCitusLocalTable on each relation, with autoConverted = false */\n\tCASCADE_USER_ADD_LOCAL_TABLE_TO_METADATA = 1 << 2,\n\n\t/* execute CreateCitusLocalTable on each relation, with autoConverted = true */\n\tCASCADE_AUTO_ADD_LOCAL_TABLE_TO_METADATA = 1 << 3,\n} CascadeOperationType;\n\nextern void CascadeOperationForFkeyConnectedRelations(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  LOCKMODE relLockMode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  CascadeOperationType\n\t\t\t\t\t\t\t\t\t\t\t\t\t  cascadeOperationType);\nextern void CascadeOperationForRelationIdList(List *relationIdList, LOCKMODE lockMode,\n\t\t\t\t\t\t\t\t\t\t\t  CascadeOperationType cascadeOperationType);\nextern void ErrorIfAnyPartitionRelationInvolvedInNonInheritedFKey(List *relationIdList);\nextern bool RelationIdListHasReferenceTable(List *relationIdList);\nextern List * GetFKeyCreationCommandsForRelationIdList(List *relationIdList);\nextern void DropRelationForeignKeys(Oid relationId, int flags);\nextern void SetLocalEnableLocalReferenceForeignKeys(bool state);\nextern void ExecuteAndLogUtilityCommandListInTableTypeConversionViaSPI(List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   utilityCmdList);\nextern void ExecuteAndLogUtilityCommandList(List *ddlCommandList);\nextern void ExecuteAndLogUtilityCommand(const char *commandString);\nextern void ExecuteForeignKeyCreateCommandList(List *ddlCommandList,\n\t\t\t\t\t\t\t\t\t\t\t   bool skip_validation);\n\n/* create_citus_local_table.c */\nextern void CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys,\n\t\t\t\t\t\t\t\t  bool autoConverted);\nextern bool ShouldAddNewTableToMetadata(Oid relationId);\nextern List * GetExplicitIndexOidList(Oid relationId);\n\nextern bool ShouldPropagateSetCommand(VariableSetStmt *setStmt);\nextern void PostprocessVariableSetStmt(VariableSetStmt *setStmt, const char *setCommand);\n\nextern void CreateCitusLocalTablePartitionOf(CreateStmt *createStatement,\n\t\t\t\t\t\t\t\t\t\t\t Oid relationId, Oid parentRelationId);\nextern void UpdateAutoConvertedForConnectedRelations(List *relationId, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t autoConverted);\n\n/* schema_based_sharding.c */\nextern bool ShouldUseSchemaBasedSharding(char *schemaName);\nextern bool ShouldCreateTenantSchemaTable(Oid relationId);\nextern void EnsureTenantTable(Oid relationId, char *operationName);\nextern void ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t Oid partitionRelationId);\nextern void CreateTenantSchemaTable(Oid relationId);\nextern void ErrorIfTenantTable(Oid relationId, const char *operationName);\nextern uint32 CreateTenantSchemaColocationId(void);\n\n#endif /*CITUS_COMMANDS_H */\n"
  },
  {
    "path": "src/include/distributed/comment.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * comment.h\n *    Declarations for comment related operations.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef COMMENT_H\n#define COMMENT_H\n\n#include \"postgres.h\"\n\n#include \"nodes/parsenodes.h\"\n\n\nextern const char *ObjectTypeNames[];\n\n\nextern List * GetCommentPropagationCommands(Oid classOid, Oid oid, char *objectName,\n\t\t\t\t\t\t\t\t\t\t\tObjectType objectType);\nextern List * CommentObjectAddress(Node *node, bool missing_ok, bool isPostprocess);\n\n# endif /* COMMENT_H */\n"
  },
  {
    "path": "src/include/distributed/connection_management.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * connection_management.h\n *   Central management of connections and their life-cycle\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CONNECTION_MANAGMENT_H\n#define CONNECTION_MANAGMENT_H\n\n#include \"postgres.h\"\n\n#include \"pg_config.h\"\n\n#include \"lib/ilist.h\"\n#include \"portability/instr_time.h\"\n#include \"storage/latch.h\"\n#include \"utils/guc.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/timestamp.h\"\n\n#include \"distributed/remote_transaction.h\"\n#include \"distributed/transaction_management.h\"\n\n/* maximum (textual) lengths of hostname and port */\n#define MAX_NODE_LENGTH 255 /* includes 0 byte */\n\n/* used for libpq commands that get an error buffer. Postgres docs recommend 256. */\n#define ERROR_BUFFER_SIZE 256\n\n/* values with special behavior for authinfo lookup */\n#define WILDCARD_NODE_ID 0\n#define LOCALHOST_NODE_ID -1\n\n/* application name used for internal connections in Citus */\n#define CITUS_APPLICATION_NAME_PREFIX \"citus_internal gpid=\"\n\n/* application name used for internal connections in rebalancer */\n#define CITUS_REBALANCER_APPLICATION_NAME_PREFIX \"citus_rebalancer gpid=\"\n\n/* application name used for connections made by run_command_on_* */\n#define CITUS_RUN_COMMAND_APPLICATION_NAME_PREFIX \"citus_run_command gpid=\"\n\n/*\n * application name prefix for move/split replication connections.\n *\n * This application_name is set to the subscription name by logical replication\n * workers, so there is no GPID.\n */\n#define CITUS_SHARD_TRANSFER_APPLICATION_NAME_PREFIX \"citus_shard_\"\n\n/* deal with waiteventset errors */\n#define WAIT_EVENT_SET_INDEX_NOT_INITIALIZED -1\n#define WAIT_EVENT_SET_INDEX_FAILED -2\n\n/*\n * UINT32_MAX is reserved in pg_dist_node, so we can use it safely.\n */\n#define LOCAL_NODE_ID UINT32_MAX\n\n/* forward declare, to avoid forcing large headers on everyone */\nstruct pg_conn; /* target of the PGconn typedef */\nstruct MemoryContextData;\n\n/*\n * Flags determining connection establishment behaviour.\n */\nenum MultiConnectionMode\n{\n\t/* force establishment of a new connection */\n\tFORCE_NEW_CONNECTION = 1 << 0,\n\n\tFOR_DDL = 1 << 1,\n\n\tFOR_DML = 1 << 2,\n\n\t/*\n\t * During COPY we do not want to use a connection that accessed non-co-located\n\t * placements. If there is a connection that did not access another placement,\n\t * then use it. Otherwise open a new clean connection.\n\t */\n\tREQUIRE_CLEAN_CONNECTION = 1 << 3,\n\n\tOUTSIDE_TRANSACTION = 1 << 4,\n\n\t/*\n\t * All metadata changes should go through the same connection, otherwise\n\t * self-deadlocks are possible. That is because the same metadata (e.g.,\n\t * metadata includes the distributed table on the workers) can be modified\n\t * accross multiple connections.\n\t *\n\t * With this flag, we guarantee that there is a single metadata connection.\n\t * But note that this connection can be used for any other operation.\n\t * In other words, this connection is not exclusively reserved for metadata\n\t * operations.\n\t */\n\tREQUIRE_METADATA_CONNECTION = 1 << 5,\n\n\t/*\n\t * Some connections are optional such as when adaptive executor is executing\n\t * a multi-shard command and requires the second (or further) connections\n\t * per node. In that case, the connection manager may decide not to allow the\n\t * connection.\n\t */\n\tOPTIONAL_CONNECTION = 1 << 6,\n\n\t/*\n\t * When this flag is passed, via connection throttling, the connection\n\t * establishments may be suspended until a connection slot is available to\n\t * the remote host.\n\t */\n\tWAIT_FOR_CONNECTION = 1 << 7,\n\n\t/*\n\t * Use the flag to start a connection for streaming replication.\n\t * This flag constructs additional libpq connection parameters needed for streaming\n\t * replication protocol. It adds 'replication=database' param which instructs\n\t * the backend to go into logical replication walsender mode.\n\t *  https://www.postgresql.org/docs/current/protocol-replication.html\n\t *\n\t * This is need to run 'CREATE_REPLICATION_SLOT' command.\n\t */\n\tREQUIRE_REPLICATION_CONNECTION_PARAM = 1 << 8\n};\n\n\n/*\n * This state is used for keeping track of the initialization\n * of the underlying pg_conn struct.\n */\ntypedef enum MultiConnectionState\n{\n\tMULTI_CONNECTION_INITIAL,\n\tMULTI_CONNECTION_CONNECTING,\n\tMULTI_CONNECTION_CONNECTED,\n\tMULTI_CONNECTION_FAILED,\n\tMULTI_CONNECTION_LOST,\n\tMULTI_CONNECTION_TIMED_OUT\n} MultiConnectionState;\n\n\n/*\n * This state is used for keeping track of the initialization\n * of MultiConnection struct, not specifically the underlying\n * pg_conn. The state is useful to determine the action during\n * clean-up of connections.\n */\ntypedef enum MultiConnectionStructInitializationState\n{\n\tPOOL_STATE_NOT_INITIALIZED,\n\tPOOL_STATE_COUNTER_INCREMENTED,\n\tPOOL_STATE_INITIALIZED\n} MultiConnectionStructInitializationState;\n\n\n/* declaring this directly above causes uncrustify to format it badly */\ntypedef enum MultiConnectionMode MultiConnectionMode;\n\ntypedef struct MultiConnection\n{\n\t/* connection details, useful for error messages and such. */\n\tchar hostname[MAX_NODE_LENGTH];\n\tint32 port;\n\tchar user[NAMEDATALEN];\n\tchar database[NAMEDATALEN];\n\n\t/* underlying libpq connection */\n\tstruct pg_conn *pgConn;\n\n\t/* connection id */\n\tuint64 connectionId;\n\n\t/* state of the connection */\n\tMultiConnectionState connectionState;\n\n\t/* signal that the connection is ready for read/write */\n\tbool ioReady;\n\n\t/* whether to wait for read/write */\n\tint waitFlags;\n\n\t/* force the connection to be closed at the end of the transaction */\n\tbool forceCloseAtTransactionEnd;\n\n\t/* is the connection currently in use, and shouldn't be used by anything else */\n\tbool claimedExclusively;\n\n\t/* is the replication origin session has already been setup for this connection. */\n\tbool isReplicationOriginSessionSetup;\n\n\t/*\n\t * Should be used to access/modify metadata. See REQUIRE_METADATA_CONNECTION for\n\t * the details.\n\t */\n\tbool useForMetadataOperations;\n\n\t/* time connection establishment was started, for timeout and executor stats */\n\tinstr_time connectionEstablishmentStart;\n\tinstr_time connectionEstablishmentEnd;\n\n\t/* membership in list of connections in ConnectionHashEntry */\n\tdlist_node connectionNode;\n\n\t/* information about the associated remote transaction */\n\tRemoteTransaction remoteTransaction;\n\n\t/*\n\t * membership in list of in-progress transactions and a flag to indicate\n\t * that the connection was added to this list\n\t */\n\tdlist_node transactionNode;\n\tbool transactionInProgress;\n\n\t/* list of all placements referenced by this connection */\n\tdlist_head referencedPlacements;\n\n\t/* number of bytes sent to PQputCopyData() since last flush */\n\tuint64 copyBytesWrittenSinceLastFlush;\n\n\t/* replication option */\n\tbool requiresReplication;\n\n\tMultiConnectionStructInitializationState initializationState;\n} MultiConnection;\n\n\n/*\n * Central connection management hash, mapping (host, port, user, database) to\n * a list of connections.\n *\n * This hash is used to keep track of which connections are open to which\n * node. Besides allowing connection reuse, that information is e.g. used to\n * handle closing connections after the end of a transaction.\n */\n\n/* hash key */\ntypedef struct ConnectionHashKey\n{\n\tchar hostname[MAX_NODE_LENGTH];\n\tint32 port;\n\tchar user[NAMEDATALEN];\n\tchar database[NAMEDATALEN];\n\tbool replicationConnParam;\n} ConnectionHashKey;\n\n/* hash entry */\ntypedef struct ConnectionHashEntry\n{\n\tConnectionHashKey key;\n\tdlist_head *connections;\n\n\t/* connections list is valid or not */\n\tbool isValid;\n} ConnectionHashEntry;\n\n/* hash entry for cached connection parameters */\ntypedef struct ConnParamsHashEntry\n{\n\tConnectionHashKey key;\n\tbool isValid;\n\tIndex runtimeParamStart;\n\tchar **keywords;\n\tchar **values;\n} ConnParamsHashEntry;\n\n\n/* maximum duration to wait for connection */\nextern int NodeConnectionTimeout;\n\n/* maximum number of connections to cache per worker per session */\nextern int MaxCachedConnectionsPerWorker;\n\n/* maximum lifetime of connections in miliseconds */\nextern int MaxCachedConnectionLifetime;\n\n/* parameters used for outbound connections */\nextern char *NodeConninfo;\nextern char *LocalHostName;\nextern bool checkAtBootPassed;\n\n/* the hash tables are externally accessiable */\nextern HTAB *ConnectionHash;\nextern HTAB *ConnParamsHash;\n\n/* context for all connection and transaction related memory */\nextern struct MemoryContextData *ConnectionContext;\n\n\nextern void AfterXactConnectionHandling(bool isCommit);\nextern void InitializeConnectionManagement(void);\n\nextern char * GetAuthinfo(char *hostname, int32 port, char *user);\nextern void InitConnParams(void);\nextern void ResetConnParams(void);\nextern void InvalidateConnParamsHashEntries(void);\nextern void AddConnParam(const char *keyword, const char *value);\nextern void GetConnParams(ConnectionHashKey *key, char ***keywords, char ***values,\n\t\t\t\t\t\t  Index *runtimeParamStart, MemoryContext context);\nextern const char * GetConnParam(const char *keyword);\nextern bool CheckConninfo(const char *conninfo, const char **allowedConninfoKeywords,\n\t\t\t\t\t\t  Size allowedConninfoKeywordsLength, char **errmsg);\n\n\n/* Low-level connection establishment APIs */\nextern MultiConnection * GetNodeConnection(uint32 flags, const char *hostname,\n\t\t\t\t\t\t\t\t\t\t   int32 port);\nextern MultiConnection * StartNodeConnection(uint32 flags, const char *hostname,\n\t\t\t\t\t\t\t\t\t\t\t int32 port);\nextern MultiConnection * GetNodeUserDatabaseConnection(uint32 flags, const char *hostname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   int32 port, const char *user,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   const char *database);\nextern MultiConnection * GetConnectionForLocalQueriesOutsideTransaction(char *userName);\nextern MultiConnection * StartNodeUserDatabaseConnection(uint32 flags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t const char *hostname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t int32 port,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t const char *user,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t const char *database);\nextern void RestartConnection(MultiConnection *connection);\nextern void CloseAllConnectionsAfterTransaction(void);\nextern void CloseNodeConnectionsAfterTransaction(char *nodeName, int nodePort);\nextern MultiConnection * ConnectionAvailableToNode(char *hostName, int nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t   const char *userName,\n\t\t\t\t\t\t\t\t\t\t\t\t   const char *database);\nextern void CloseConnection(MultiConnection *connection);\nextern void ShutdownAllConnections(void);\nextern void ShutdownConnection(MultiConnection *connection);\n\n/* dealing with a connection */\nextern void FinishConnectionListEstablishment(List *multiConnectionList);\nextern void FinishConnectionEstablishment(MultiConnection *connection);\nextern void ForceConnectionCloseAtTransactionEnd(MultiConnection *connection);\nextern void ClaimConnectionExclusively(MultiConnection *connection);\nextern void UnclaimConnection(MultiConnection *connection);\nextern void MarkConnectionConnected(MultiConnection *connection, bool newConnection);\n\n/* waiteventset utilities */\nextern int CitusAddWaitEventSetToSet(WaitEventSet *set, uint32 events, pgsocket fd,\n\t\t\t\t\t\t\t\t\t Latch *latch, void *user_data);\n\nextern bool CitusModifyWaitEvent(WaitEventSet *set, int pos, uint32 events,\n\t\t\t\t\t\t\t\t Latch *latch);\n\n/* time utilities */\nextern double MillisecondsPassedSince(instr_time moment);\nextern long MillisecondsToTimeout(instr_time start, long msAfterStart);\n\n#endif /* CONNECTION_MANAGMENT_H */\n"
  },
  {
    "path": "src/include/distributed/coordinator_protocol.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * coordinator_protocol.h\n *\t  Header for shared declarations for access to coordinator node data.\n *    These data are used to create new shards or update existing ones.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef COORDINATOR_PROTOCOL_H\n#define COORDINATOR_PROTOCOL_H\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n#include \"fmgr.h\"\n\n#include \"nodes/pg_list.h\"\n\n#include \"columnar/columnar.h\"\n\n#include \"distributed/connection_management.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/shardinterval_utils.h\"\n\n/*\n * In our distributed database, we need a mechanism to make remote procedure\n * calls between clients, the coordinator node, and worker nodes. These remote calls\n * require serializing and deserializing values and function signatures between\n * nodes; and for these, we currently use PostgreSQL's built-in type and\n * function definition system. This approach is by no means ideal however; and\n * our implementation: (i) cannot perform compile-time type checks, (ii)\n * requires additional effort when upgrading to new function signatures, and\n * (iii) hides argument and return value names and types behind complicated\n * pg_proc.h definitions.\n *\n * An ideal implementation should overcome these problems, and make it much\n * easier to pass values back and forth between nodes. One such implementation\n * that comes close to ideal is Google's Protocol Buffers. Nonetheless, we do\n * not use it in here as its inclusion requires changes to PostgreSQL's make\n * system, and a native C version is currently unavailable.\n */\n\n\n/* Number of tuple fields that coordinator node functions return */\n#define TABLE_METADATA_FIELDS 7\n#define CANDIDATE_NODE_FIELDS 2\n#define WORKER_NODE_FIELDS 2\n\n/* transfer mode for citus_copy_shard_placement */\n#define TRANSFER_MODE_AUTOMATIC 'a'\n#define TRANSFER_MODE_FORCE_LOGICAL 'l'\n#define TRANSFER_MODE_BLOCK_WRITES 'b'\n\n#define SHARDID_SEQUENCE_NAME \"pg_dist_shardid_seq\"\n#define PLACEMENTID_SEQUENCE_NAME \"pg_dist_placement_placementid_seq\"\n\n/* Remote call definitions to help with data staging and deletion */\n#define WORKER_APPLY_SHARD_DDL_COMMAND \\\n\t\t\"SELECT worker_apply_shard_ddl_command (\" UINT64_FORMAT \", %s, %s)\"\n#define WORKER_APPLY_SHARD_DDL_COMMAND_WITHOUT_SCHEMA \\\n\t\t\"SELECT worker_apply_shard_ddl_command (\" UINT64_FORMAT \", %s)\"\n#define WORKER_APPLY_INTER_SHARD_DDL_COMMAND \\\n\t\t\"SELECT worker_apply_inter_shard_ddl_command (\" UINT64_FORMAT \\\n\t\t\", %s, \" UINT64_FORMAT \\\n\t\t\", %s, %s)\"\n#define SHARD_RANGE_QUERY \"SELECT min(%s), max(%s) FROM %s\"\n#define SHARD_TABLE_SIZE_QUERY \"SELECT pg_table_size(%s)\"\n#define SHARD_CSTORE_TABLE_SIZE_QUERY \"SELECT cstore_table_size(%s)\"\n#define DROP_REGULAR_TABLE_COMMAND \"DROP TABLE IF EXISTS %s CASCADE\"\n#define DROP_FOREIGN_TABLE_COMMAND \"DROP FOREIGN TABLE IF EXISTS %s CASCADE\"\n#define CREATE_SCHEMA_COMMAND \"CREATE SCHEMA IF NOT EXISTS %s AUTHORIZATION %s\"\n\n/*\n * TableDDLCommandType encodes the implementation used by TableDDLCommand. See comments in\n * TableDDLCpmmand for details.\n */\ntypedef enum TableDDLCommandType\n{\n\tTABLE_DDL_COMMAND_STRING,\n\tTABLE_DDL_COMMAND_FUNCTION,\n} TableDDLCommandType;\n\n\n/*\n * IndexDefinitionDeparseFlags helps to control which parts of the\n * index creation commands are deparsed.\n */\ntypedef enum IndexDefinitionDeparseFlags\n{\n\tINCLUDE_CREATE_INDEX_STATEMENTS = 1 << 0,\n\tINCLUDE_CREATE_CONSTRAINT_STATEMENTS = 1 << 1,\n\tINCLUDE_INDEX_CLUSTERED_STATEMENTS = 1 << 2,\n\tINCLUDE_INDEX_STATISTICS_STATEMENTTS = 1 << 3,\n\tINCLUDE_INDEX_ALL_STATEMENTS = INCLUDE_CREATE_INDEX_STATEMENTS |\n\t\t\t\t\t\t\t\t   INCLUDE_CREATE_CONSTRAINT_STATEMENTS |\n\t\t\t\t\t\t\t\t   INCLUDE_INDEX_CLUSTERED_STATEMENTS |\n\t\t\t\t\t\t\t\t   INCLUDE_INDEX_STATISTICS_STATEMENTTS\n} IndexDefinitionDeparseFlags;\n\n\n/*\n * IncludeSequenceDefaults decides on inclusion of DEFAULT clauses for columns\n * getting their default values from a sequence when creating the definition\n * of a table.\n */\ntypedef enum IncludeSequenceDefaults\n{\n\tNO_SEQUENCE_DEFAULTS = 0, /* don't include sequence defaults */\n\tNEXTVAL_SEQUENCE_DEFAULTS = 1, /* include sequence defaults */\n\n\t/*\n\t * Include sequence defaults, but use worker_nextval instead of nextval\n\t * when the default will be called in worker node, and the column type is\n\t * int or smallint.\n\t */\n\tWORKER_NEXTVAL_SEQUENCE_DEFAULTS = 2,\n} IncludeSequenceDefaults;\n\n\n/*\n * IncludeIdentities decides on how we include identity information\n * when creating the definition of a table.\n */\ntypedef enum IncludeIdentities\n{\n\tNO_IDENTITY = 0, /* don't include identities */\n\tINCLUDE_IDENTITY = 1 /* include identities as-is*/\n} IncludeIdentities;\n\n\nstruct TableDDLCommand;\ntypedef struct TableDDLCommand TableDDLCommand;\ntypedef char *(*TableDDLFunction)(void *context);\ntypedef char *(*TableDDLShardedFunction)(uint64 shardId, void *context);\n\n/*\n * TableDDLCommand holds the definition of a command to be executed to bring the table and\n * or shard into a certain state. The command needs to be able to serialized into two\n * versions:\n *  - one version should have the vanilla commands operating on the base table. These are\n *    used for example to create the MX table shards\n *  - the second versions should replace all identifiers with an identifier containing the\n *    shard id.\n *\n * Current implementations are\n *  - command string, created via makeTableDDLCommandString. This variant contains a ddl\n *    command that will be wrapped in `worker_apply_shard_ddl_command` when applied\n *    against a shard.\n */\nstruct TableDDLCommand\n{\n\tCitusNode node;\n\n\t/* encoding the type this TableDDLCommand contains */\n\tTableDDLCommandType type;\n\n\t/*\n\t * This union contains one (1) typed field for every implementation for\n\t * TableDDLCommand. A union enforces no overloading of fields but instead requires at\n\t * most one of the fields to be used at any time.\n\t */\n\tunion\n\t{\n\t\t/*\n\t\t * CommandStr is used when type is set to TABLE_DDL_COMMAND_STRING. It holds the\n\t\t * sql ddl command string representing the ddl command.\n\t\t */\n\t\tchar *commandStr;\n\n\t\t/*\n\t\t * function is used when type is set to TABLE_DDL_COMMAND_FUNCTION. It contains\n\t\t * function pointers and a context to be passed to the functions to be able to\n\t\t * construct the sql commands for sharded and non-sharded tables.\n\t\t */\n\t\tstruct\n\t\t{\n\t\t\tTableDDLFunction function;\n\t\t\tTableDDLShardedFunction shardedFunction;\n\t\t\tvoid *context;\n\t\t}\n\t\tfunction;\n\t};\n};\n\n/*\n * ColumnarTableDDLContext holds the instance variable for the TableDDLCommandFunction\n * instance described below.\n */\ntypedef struct ColumnarTableDDLContext\n{\n\tchar *schemaName;\n\tchar *relationName;\n\tColumnarOptions options;\n} ColumnarTableDDLContext;\n\n/* make functions for TableDDLCommand */\nextern TableDDLCommand * makeTableDDLCommandString(char *commandStr);\nextern TableDDLCommand * makeTableDDLCommandFunction(TableDDLFunction function,\n\t\t\t\t\t\t\t\t\t\t\t\t\t TableDDLShardedFunction\n\t\t\t\t\t\t\t\t\t\t\t\t\t shardedFunction,\n\t\t\t\t\t\t\t\t\t\t\t\t\t void *context);\n\nextern char * GetShardedTableDDLCommand(TableDDLCommand *command, uint64 shardId,\n\t\t\t\t\t\t\t\t\t\tchar *schemaName);\nextern char * GetShardedTableDDLCommandColumnar(uint64 shardId, void *context);\nextern char * GetTableDDLCommand(TableDDLCommand *command);\nextern TableDDLCommand * ColumnarGetCustomTableOptionsDDL(char *schemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  char *relationName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ColumnarOptions options);\n\n\n/* Config variables managed via guc.c */\nextern int ShardCount;\nextern int ShardReplicationFactor;\nextern int NextShardId;\nextern int NextPlacementId;\n\n\nextern bool IsCoordinator(void);\n\n/* Function declarations local to the distributed module */\nextern uint64 GetNextShardId(void);\nextern uint64 GetNextPlacementId(void);\nextern Oid ResolveRelationId(text *relationName, bool missingOk);\nextern List * GetFullTableCreationCommands(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t   IncludeSequenceDefaults\n\t\t\t\t\t\t\t\t\t\t   includeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\t   IncludeIdentities includeIdentityDefaults,\n\t\t\t\t\t\t\t\t\t\t   bool creatingShellTableOnRemoteNode);\nextern List * GetPostLoadTableCreationCommands(Oid relationId, bool includeIndexes,\n\t\t\t\t\t\t\t\t\t\t\t   bool includeReplicaIdentity);\nextern List * GetPreLoadTableCreationCommands(Oid relationId, IncludeSequenceDefaults\n\t\t\t\t\t\t\t\t\t\t\t  includeSequenceDefaults,\n\t\t\t\t\t\t\t\t\t\t\t  IncludeIdentities\n\t\t\t\t\t\t\t\t\t\t\t  includeIdentityDefaults,\n\t\t\t\t\t\t\t\t\t\t\t  char *accessMethod);\nextern List * GetTableRowLevelSecurityCommands(Oid relationId);\nextern List * GetTableIndexAndConstraintCommands(Oid relationId, int indexFlags);\nextern List * GetTableIndexAndConstraintCommandsExcludingReplicaIdentity(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t int indexFlags);\nextern Oid GetRelationIdentityOrPK(Relation rel);\nextern void GatherIndexAndConstraintDefinitionList(Form_pg_index indexForm,\n\t\t\t\t\t\t\t\t\t\t\t\t   List **indexDDLEventList,\n\t\t\t\t\t\t\t\t\t\t\t\t   int indexFlags);\nextern bool IndexImpliedByAConstraint(Form_pg_index indexForm);\nextern List * GetTableReplicaIdentityCommand(Oid relationId);\nextern char ShardStorageType(Oid relationId);\nextern bool DistributedTableReplicationIsEnabled(void);\nextern void CheckDistributedTable(Oid relationId);\nextern void CreateAppendDistributedShardPlacements(Oid relationId, int64 shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t   List *workerNodeList, int\n\t\t\t\t\t\t\t\t\t\t\t\t   replicationFactor);\nextern void CreateShardsOnWorkers(Oid distributedRelationId, List *shardPlacements,\n\t\t\t\t\t\t\t\t  bool useExclusiveConnection);\nextern void InsertShardPlacementRows(Oid relationId, int64 shardId,\n\t\t\t\t\t\t\t\t\t List *workerNodeList, int workerStartIndex,\n\t\t\t\t\t\t\t\t\t int replicationFactor);\nextern uint64 UpdateShardStatistics(int64 shardId);\nextern void CreateShardsWithRoundRobinPolicy(Oid distributedTableId, int32 shardCount,\n\t\t\t\t\t\t\t\t\t\t\t int32 replicationFactor,\n\t\t\t\t\t\t\t\t\t\t\t bool useExclusiveConnections);\nextern void CreateColocatedShards(Oid targetRelationId, Oid sourceRelationId,\n\t\t\t\t\t\t\t\t  bool useExclusiveConnections);\nextern void CreateReferenceTableShard(Oid distributedTableId);\nextern void CreateSingleShardTableShardWithRoundRobinPolicy(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tuint32 colocationId);\nextern int EmptySingleShardTableColocationDecideNodeId(uint32 colocationId);\nextern List * WorkerCreateShardCommandList(Oid relationId, uint64 shardId,\n\t\t\t\t\t\t\t\t\t\t   List *ddlCommandList);\nextern Oid ForeignConstraintGetReferencedTableId(const char *queryString);\nextern void CheckHashPartitionedTable(Oid distributedTableId);\nextern void CheckTableSchemaNameForDrop(Oid relationId, char **schemaName,\n\t\t\t\t\t\t\t\t\t\tchar **tableName);\nextern text * IntegerToText(int32 value);\n\n/* Function declarations for generating metadata for shard and placement creation */\nextern Datum master_get_table_metadata(PG_FUNCTION_ARGS);\nextern Datum master_get_table_ddl_events(PG_FUNCTION_ARGS);\nextern Datum master_get_new_shardid(PG_FUNCTION_ARGS);\nextern Datum master_get_new_placementid(PG_FUNCTION_ARGS);\nextern Datum master_get_active_worker_nodes(PG_FUNCTION_ARGS);\nextern Datum master_get_round_robin_candidate_nodes(PG_FUNCTION_ARGS);\nextern Datum master_stage_shard_row(PG_FUNCTION_ARGS);\nextern Datum master_stage_shard_placement_row(PG_FUNCTION_ARGS);\n\n/* Function declarations to help with data staging and deletion */\nextern Datum master_create_empty_shard(PG_FUNCTION_ARGS);\nextern Datum master_update_shard_statistics(PG_FUNCTION_ARGS);\nextern Datum master_drop_sequences(PG_FUNCTION_ARGS);\nextern Datum master_modify_multiple_shards(PG_FUNCTION_ARGS);\nextern Datum lock_relation_if_exists(PG_FUNCTION_ARGS);\nextern Datum citus_drop_all_shards(PG_FUNCTION_ARGS);\nextern Datum master_drop_all_shards(PG_FUNCTION_ARGS);\nextern int MasterDropAllShards(Oid relationId, char *schemaName, char *relationName);\n\n/* function declarations for shard creation functionality */\nextern Datum master_create_worker_shards(PG_FUNCTION_ARGS);\nextern Datum isolate_tenant_to_new_shard(PG_FUNCTION_ARGS);\n\n/* function declarations for shard split functionality */\nextern Datum citus_split_shard_by_split_points(PG_FUNCTION_ARGS);\n\n/* function declarations for shard copy functinality */\nextern List * CopyShardForeignConstraintCommandList(ShardInterval *shardInterval);\nextern void CopyShardForeignConstraintCommandListGrouped(ShardInterval *shardInterval,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t List **\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t colocatedShardForeignConstraintCommandList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t List **\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t referenceTableForeignConstraintList);\nextern ShardPlacement * SearchShardPlacementInList(List *shardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t   const char *nodeName, uint32 nodePort);\nextern ShardPlacement * SearchShardPlacementInListOrError(List *shardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  const char *nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  uint32 nodePort);\nextern char LookupShardTransferMode(Oid shardReplicationModeOid);\nextern void BlockWritesToShardList(List *shardList);\nextern List * WorkerApplyShardDDLCommandList(List *ddlCommandList, int64 shardId);\n\n\n#endif   /* COORDINATOR_PROTOCOL_H */\n"
  },
  {
    "path": "src/include/distributed/cte_inline.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * cte_inline.h\n *\tFunctions and global variables to control cte inlining.\n *\n * Copyright (c) 2019, Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CTE_INLINE_H\n#define CTE_INLINE_H\n\n#include \"nodes/parsenodes.h\"\n\nextern void RecursivelyInlineCtesInQueryTree(Query *query);\nextern bool QueryTreeContainsInlinableCTE(Query *queryTree);\n\n#endif /* CTE_INLINE_H */\n"
  },
  {
    "path": "src/include/distributed/deparse_shard_query.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparse_shard_query.h\n *\n * Declarations for public functions and types related to deparsing shard\n * queries.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef DEPARSE_SHARD_QUERY_H\n#define DEPARSE_SHARD_QUERY_H\n\n#include \"c.h\"\n\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/query_utils.h\"\n\n\nextern void RebuildQueryStrings(Job *workerJob);\nextern bool UpdateRelationToShardNames(Node *node, List *relationShardList);\nextern void UpdateWhereClauseToPushdownRecurringOuterJoin(Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  List *relationShardList);\nextern bool UpdateWhereClauseToPushdownRecurringOuterJoinWalker(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *relationShardList);\nNode * CreateQualsForShardInterval(RelationShard *relationShard, int attnum,\n\t\t\t\t\t\t\t\t   int outerRtIndex);\nextern void SetTaskQueryIfShouldLazyDeparse(Task *task, Query *query);\nextern void SetTaskQueryString(Task *task, char *queryString);\nextern void SetTaskQueryStringList(Task *task, List *queryStringList);\nextern void SetTaskQueryPlan(Task *task, Query *query, PlannedStmt *localPlan);\nextern char * TaskQueryString(Task *task);\nextern PlannedStmt * TaskQueryLocalPlan(Task *task);\nextern char * TaskQueryStringAtIndex(Task *task, int index);\nextern int GetTaskQueryType(Task *task);\nextern void AddInsertAliasIfNeeded(Query *query);\n\n\n#endif /* DEPARSE_SHARD_QUERY_H */\n"
  },
  {
    "path": "src/include/distributed/deparser.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * deparser.h\n *\t  Used when deparsing any ddl parsetree into its sql from.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_DEPARSER_H\n#define CITUS_DEPARSER_H\n\n#include \"postgres.h\"\n\n#include \"catalog/objectaddress.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n\n/* forward declarations for format_collate.c */\n/* Control flags for FormatCollateExtended, compatible with format_type_extended */\n#define FORMAT_COLLATE_ALLOW_INVALID 0x02       /* allow invalid types */\n#define FORMAT_COLLATE_FORCE_QUALIFY 0x04       /* force qualification of collate */\nextern char * FormatCollateBEQualified(Oid collate_oid);\nextern char * FormatCollateExtended(Oid collid, bits16 flags);\n\nextern void AssertObjectTypeIsFunctional(ObjectType type);\n\nextern void QualifyTreeNode(Node *stmt);\nextern char * DeparseTreeNode(Node *stmt);\nextern List * DeparseTreeNodes(List *stmts);\n\n/* forward declarations for qualify_aggregate_stmts.c */\nextern void QualifyDefineAggregateStmt(Node *node);\n\n/* forward declarations for deparse_attribute_stmts.c */\nextern char * DeparseRenameAttributeStmt(Node *);\n\n/* forward declarations for deparse_collation_stmts.c */\nextern char * DeparseDropCollationStmt(Node *stmt);\nextern char * DeparseRenameCollationStmt(Node *stmt);\nextern char * DeparseAlterCollationSchemaStmt(Node *stmt);\nextern char * DeparseAlterCollationOwnerStmt(Node *stmt);\n\nextern void QualifyDropCollationStmt(Node *stmt);\nextern void QualifyRenameCollationStmt(Node *stmt);\nextern void QualifyAlterCollationSchemaStmt(Node *stmt);\nextern void QualifyAlterCollationOwnerStmt(Node *stmt);\n\n/* forward declarations for deparse_domain_stmts.c */\nextern char * DeparseCreateDomainStmt(Node *node);\nextern char * DeparseDropDomainStmt(Node *node);\nextern char * DeparseAlterDomainStmt(Node *node);\nextern char * DeparseDomainRenameConstraintStmt(Node *node);\nextern char * DeparseAlterDomainOwnerStmt(Node *node);\nextern char * DeparseRenameDomainStmt(Node *node);\nextern char * DeparseAlterDomainSchemaStmt(Node *node);\n\nextern void QualifyCreateDomainStmt(Node *node);\nextern void QualifyDropDomainStmt(Node *node);\nextern void QualifyAlterDomainStmt(Node *node);\nextern void QualifyDomainRenameConstraintStmt(Node *node);\nextern void QualifyAlterDomainOwnerStmt(Node *node);\nextern void QualifyRenameDomainStmt(Node *node);\nextern void QualifyAlterDomainSchemaStmt(Node *node);\n\n/* forward declarations for deparse_foreign_data_wrapper_stmts.c */\nextern char * DeparseGrantOnFDWStmt(Node *node);\n\n/* forward declarations for deparse_foreign_server_stmts.c */\nextern char * DeparseCreateForeignServerStmt(Node *node);\nextern char * DeparseAlterForeignServerStmt(Node *node);\nextern char * DeparseAlterForeignServerRenameStmt(Node *node);\nextern char * DeparseAlterForeignServerOwnerStmt(Node *node);\nextern char * DeparseDropForeignServerStmt(Node *node);\nextern char * DeparseGrantOnForeignServerStmt(Node *node);\n\n/* forward declarations for deparse_table_stmts.c */\nextern char * DeparseAlterTableSchemaStmt(Node *stmt);\nextern char * DeparseAlterTableStmt(Node *node);\n\nextern void QualifyAlterTableSchemaStmt(Node *stmt);\n\n/* forward declarations for deparse_text_search.c */\nextern char * DeparseAlterTextSearchConfigurationOwnerStmt(Node *node);\nextern char * DeparseAlterTextSearchConfigurationSchemaStmt(Node *node);\nextern char * DeparseAlterTextSearchConfigurationStmt(Node *node);\nextern char * DeparseAlterTextSearchDictionaryOwnerStmt(Node *node);\nextern char * DeparseAlterTextSearchDictionarySchemaStmt(Node *node);\nextern char * DeparseAlterTextSearchDictionaryStmt(Node *node);\nextern char * DeparseCreateTextSearchConfigurationStmt(Node *node);\nextern char * DeparseCreateTextSearchDictionaryStmt(Node *node);\nextern char * DeparseDropTextSearchConfigurationStmt(Node *node);\nextern char * DeparseDropTextSearchDictionaryStmt(Node *node);\nextern char * DeparseRenameTextSearchConfigurationStmt(Node *node);\nextern char * DeparseRenameTextSearchDictionaryStmt(Node *node);\nextern char * DeparseTextSearchConfigurationCommentStmt(Node *node);\nextern char * DeparseTextSearchDictionaryCommentStmt(Node *node);\n\n/* forward declarations for deparse_schema_stmts.c */\nextern char * DeparseCreateSchemaStmt(Node *node);\nextern char * DeparseDropSchemaStmt(Node *node);\nextern char * DeparseGrantOnSchemaStmt(Node *stmt);\nextern char * DeparseAlterSchemaRenameStmt(Node *stmt);\nextern char * DeparseAlterSchemaOwnerStmt(Node *node);\n\nextern void AppendGrantPrivileges(StringInfo buf, GrantStmt *stmt);\nextern void AppendGrantGrantees(StringInfo buf, GrantStmt *stmt);\nextern void AppendWithGrantOption(StringInfo buf, GrantStmt *stmt);\nextern void AppendGrantOptionFor(StringInfo buf, GrantStmt *stmt);\nextern void AppendGrantRestrictAndCascadeForRoleSpec(StringInfo buf, DropBehavior\n\t\t\t\t\t\t\t\t\t\t\t\t\t behavior, bool isGrant);\nextern void AppendGrantRestrictAndCascade(StringInfo buf, GrantStmt *stmt);\nextern void AppendGrantedByInGrantForRoleSpec(StringInfo buf, RoleSpec *grantor, bool\n\t\t\t\t\t\t\t\t\t\t\t  isGrant);\nextern void AppendGrantedByInGrant(StringInfo buf, GrantStmt *stmt);\n\nextern void AppendGrantSharedPrefix(StringInfo buf, GrantStmt *stmt);\nextern void AppendGrantSharedSuffix(StringInfo buf, GrantStmt *stmt);\n\nextern void AppendColumnNameList(StringInfo buf, List *columns);\n\n/* Common deparser utils */\n\ntypedef struct DefElemOptionFormat\n{\n\tchar *name;\n\tchar *format;\n\tint type;\n} DefElemOptionFormat;\n\ntypedef enum OptionFormatType\n{\n\tOPTION_FORMAT_STRING,\n\tOPTION_FORMAT_LITERAL_CSTR,\n\tOPTION_FORMAT_BOOLEAN,\n\tOPTION_FORMAT_INTEGER\n} OptionFormatType;\n\n\nextern void DefElemOptionToStatement(StringInfo buf, DefElem *option,\n\t\t\t\t\t\t\t\t\t const DefElemOptionFormat *opt_formats,\n\t\t\t\t\t\t\t\t\t int opt_formats_len);\n\n/* forward declarations for deparse_comment_stmts.c */\nextern char * DeparseCommentStmt(Node *node);\n\n\n/* forward declarations for deparse_statistics_stmts.c */\nextern char * DeparseCreateStatisticsStmt(Node *node);\nextern char * DeparseDropStatisticsStmt(List *nameList, bool ifExists);\nextern char * DeparseAlterStatisticsRenameStmt(Node *node);\nextern char * DeparseAlterStatisticsSchemaStmt(Node *node);\nextern char * DeparseAlterStatisticsStmt(Node *node);\nextern char * DeparseAlterStatisticsOwnerStmt(Node *node);\n\nextern void QualifyCreateStatisticsStmt(Node *node);\nextern void QualifyDropStatisticsStmt(Node *node);\nextern void QualifyAlterStatisticsRenameStmt(Node *node);\nextern void QualifyAlterStatisticsSchemaStmt(Node *node);\nextern void QualifyAlterStatisticsStmt(Node *node);\nextern void QualifyAlterStatisticsOwnerStmt(Node *node);\n\n/* forward declarations for deparse_type_stmts.c */\nextern char * DeparseCompositeTypeStmt(Node *stmt);\nextern char * DeparseCreateEnumStmt(Node *stmt);\nextern char * DeparseDropTypeStmt(Node *stmt);\nextern char * DeparseAlterEnumStmt(Node *stmt);\nextern char * DeparseAlterTypeStmt(Node *stmt);\nextern char * DeparseRenameTypeStmt(Node *stmt);\nextern char * DeparseRenameTypeAttributeStmt(Node *stmt);\nextern char * DeparseAlterTypeSchemaStmt(Node *stmt);\nextern char * DeparseAlterTypeOwnerStmt(Node *stmt);\n\nextern void QualifyRenameAttributeStmt(Node *stmt);\nextern void QualifyRenameTypeStmt(Node *stmt);\nextern void QualifyRenameTypeAttributeStmt(Node *stmt);\nextern void QualifyAlterEnumStmt(Node *stmt);\nextern void QualifyAlterTypeStmt(Node *stmt);\nextern void QualifyCompositeTypeStmt(Node *stmt);\nextern void QualifyCreateEnumStmt(Node *stmt);\nextern void QualifyAlterTypeSchemaStmt(Node *stmt);\nextern void QualifyAlterTypeOwnerStmt(Node *stmt);\n\nextern char * GetTypeNamespaceNameByNameList(List *names);\nextern Oid TypeOidGetNamespaceOid(Oid typeOid);\n\nextern List * GetObjectAddressListFromParseTree(Node *parseTree, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t\tisPostprocess);\nextern List * RenameAttributeStmtObjectAddress(Node *stmt, bool missing_ok, bool\n\t\t\t\t\t\t\t\t\t\t\t   isPostprocess);\n\n/* forward declarations for deparse_view_stmts.c */\nextern void QualifyDropViewStmt(Node *node);\nextern void QualifyAlterViewStmt(Node *node);\nextern void QualifyRenameViewStmt(Node *node);\nextern void QualifyAlterViewSchemaStmt(Node *node);\nextern char * DeparseRenameViewStmt(Node *stmt);\nextern char * DeparseAlterViewStmt(Node *node);\nextern char * DeparseDropViewStmt(Node *node);\nextern char * DeparseAlterViewSchemaStmt(Node *node);\n\n\n/* forward declarations for deparse_function_stmts.c */\nextern bool isFunction(ObjectType objectType);\n\nextern char * DeparseDropFunctionStmt(Node *stmt);\nextern char * DeparseAlterFunctionStmt(Node *stmt);\n\nextern char * DeparseRenameFunctionStmt(Node *stmt);\nextern char * DeparseAlterFunctionSchemaStmt(Node *stmt);\nextern char * DeparseAlterFunctionOwnerStmt(Node *stmt);\nextern char * DeparseAlterFunctionDependsStmt(Node *stmt);\n\nextern char * DeparseGrantOnFunctionStmt(Node *node);\n\nextern void AppendVariableSet(StringInfo buf, VariableSetStmt *setStmt);\n\nextern void QualifyAlterFunctionStmt(Node *stmt);\nextern void QualifyRenameFunctionStmt(Node *stmt);\nextern void QualifyAlterFunctionSchemaStmt(Node *stmt);\nextern void QualifyAlterFunctionOwnerStmt(Node *stmt);\nextern void QualifyAlterFunctionDependsStmt(Node *stmt);\n\n/* forward declarations for deparse_role_stmts.c */\nextern char * DeparseAlterRoleStmt(Node *stmt);\nextern char * DeparseAlterRoleSetStmt(Node *stmt);\nextern char * DeparseRenameRoleStmt(Node *stmt);\n\nextern List * MakeSetStatementArguments(char *configurationName,\n\t\t\t\t\t\t\t\t\t\tchar *configurationValue);\nextern void QualifyAlterRoleSetStmt(Node *stmt);\nextern char * DeparseCreateRoleStmt(Node *stmt);\nextern char * DeparseDropRoleStmt(Node *stmt);\nextern char * DeparseGrantRoleStmt(Node *stmt);\nextern char * DeparseReassignOwnedStmt(Node *node);\n\n/* forward declarations for deparse_owned_stmts.c */\nextern char * DeparseDropOwnedStmt(Node *node);\n\n/* forward declarations for deparse_extension_stmts.c */\nextern DefElem * GetExtensionOption(List *extensionOptions,\n\t\t\t\t\t\t\t\t\tconst char *defname);\nextern char * DeparseCreateExtensionStmt(Node *stmt);\nextern char * DeparseDropExtensionStmt(Node *stmt);\nextern char * DeparseAlterExtensionSchemaStmt(Node *stmt);\nextern char * DeparseAlterExtensionStmt(Node *stmt);\n\n/* forward declarations for deparse_database_stmts.c */\nextern char * DeparseAlterDatabaseOwnerStmt(Node *node);\nextern char * DeparseGrantOnDatabaseStmt(Node *node);\nextern char * DeparseAlterDatabaseStmt(Node *node);\nextern char * DeparseAlterDatabaseRefreshCollStmt(Node *node);\nextern char * DeparseAlterDatabaseSetStmt(Node *node);\nextern char * DeparseCreateDatabaseStmt(Node *node);\nextern char * DeparseDropDatabaseStmt(Node *node);\nextern char * DeparseAlterDatabaseRenameStmt(Node *node);\n\n\n/* forward declaration for deparse_publication_stmts.c */\nextern char * DeparseCreatePublicationStmt(Node *stmt);\nextern char * DeparseCreatePublicationStmtExtended(Node *node,\n\t\t\t\t\t\t\t\t\t\t\t\t   bool whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\t\t\t\t   bool includeLocalTables);\nextern char * DeparseAlterPublicationStmt(Node *stmt);\nextern char * DeparseAlterPublicationStmtExtended(Node *stmt,\n\t\t\t\t\t\t\t\t\t\t\t\t  bool whereClauseNeedsTransform,\n\t\t\t\t\t\t\t\t\t\t\t\t  bool includeLocalTables);\nextern char * DeparseAlterPublicationOwnerStmt(Node *stmt);\nextern char * DeparseAlterPublicationSchemaStmt(Node *node);\nextern char * DeparseDropPublicationStmt(Node *stmt);\nextern char * DeparseRenamePublicationStmt(Node *node);\n\nextern void QualifyCreatePublicationStmt(Node *node);\nextern void QualifyAlterPublicationStmt(Node *node);\n\n/* forward declatations for deparse_text_search_stmts.c */\nextern void QualifyAlterTextSearchConfigurationOwnerStmt(Node *node);\nextern void QualifyAlterTextSearchConfigurationSchemaStmt(Node *node);\nextern void QualifyAlterTextSearchConfigurationStmt(Node *node);\nextern void QualifyAlterTextSearchDictionaryOwnerStmt(Node *node);\nextern void QualifyAlterTextSearchDictionarySchemaStmt(Node *node);\nextern void QualifyAlterTextSearchDictionaryStmt(Node *node);\nextern void QualifyDropTextSearchConfigurationStmt(Node *node);\nextern void QualifyDropTextSearchDictionaryStmt(Node *node);\nextern void QualifyRenameTextSearchConfigurationStmt(Node *node);\nextern void QualifyRenameTextSearchDictionaryStmt(Node *node);\nextern void QualifyTextSearchConfigurationCommentStmt(Node *node);\nextern void QualifyTextSearchDictionaryCommentStmt(Node *node);\n\n/* forward declarations for deparse_seclabel_stmts.c */\nextern char * DeparseRoleSecLabelStmt(Node *node);\nextern char * DeparseTableSecLabelStmt(Node *node);\nextern char * DeparseColumnSecLabelStmt(Node *node);\n\n/* forward declarations for deparse_sequence_stmts.c */\nextern char * DeparseDropSequenceStmt(Node *node);\nextern char * DeparseRenameSequenceStmt(Node *node);\nextern char * DeparseAlterSequenceSchemaStmt(Node *node);\nextern char * DeparseAlterSequenceOwnerStmt(Node *node);\nextern char * DeparseAlterSequencePersistenceStmt(Node *node);\nextern char * DeparseGrantOnSequenceStmt(Node *node);\n\n/* forward declarations for qualify_sequence_stmt.c */\nextern void QualifyRenameSequenceStmt(Node *node);\nextern void QualifyDropSequenceStmt(Node *node);\nextern void QualifyAlterSequenceSchemaStmt(Node *node);\nextern void QualifyAlterSequenceOwnerStmt(Node *node);\nextern void QualifyAlterSequencePersistenceStmt(Node *node);\nextern void QualifyGrantOnSequenceStmt(Node *node);\n\n#endif /* CITUS_DEPARSER_H */\n"
  },
  {
    "path": "src/include/distributed/directed_acyclic_graph_execution.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * directed_acyclic_graph_execution.h\n *\t  Execution logic for directed acyclic graph tasks.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef DIRECTED_ACYCLIC_GRAPH_EXECUTION_H\n#define DIRECTED_ACYCLIC_GRAPH_EXECUTION_H\n\n#include \"postgres.h\"\n\n#include \"nodes/pg_list.h\"\n\nextern void ExecuteTasksInDependencyOrder(List *allTasks, List *excludedTasks,\n\t\t\t\t\t\t\t\t\t\t  List *jobIds);\n\n\n#endif /* DIRECTED_ACYCLIC_GRAPH_EXECUTION_H */\n"
  },
  {
    "path": "src/include/distributed/distributed_deadlock_detection.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * distributed_deadlock_detection.h\n *\t  Type and function declarations used for performing distributed deadlock\n *\t  detection.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef DISTRIBUTED_DEADLOCK_DETECTION_H\n#define DISTRIBUTED_DEADLOCK_DETECTION_H\n\n#include \"postgres.h\"\n\n#include \"access/hash.h\"\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/backend_data.h\"\n#include \"distributed/listutils.h\"\n#include \"distributed/lock_graph.h\"\n#include \"distributed/transaction_identifier.h\"\n\ntypedef struct TransactionNode\n{\n\tDistributedTransactionId transactionId;\n\n\t/* list of TransactionNode that this distributed transaction is waiting for */\n\tList *waitsFor;\n\n\t/* backend that is on the initiator node */\n\tPGPROC *initiatorProc;\n\n\tbool transactionVisited;\n} TransactionNode;\n\n\n/* GUC, determining whether debug messages for deadlock detection sent to LOG */\nextern bool LogDistributedDeadlockDetection;\n\n\nextern bool CheckForDistributedDeadlocks(void);\nextern HTAB * BuildAdjacencyListsForWaitGraph(WaitGraph *waitGraph);\nextern char * WaitsForToString(List *waitsFor);\n\n\n#endif /* DISTRIBUTED_DEADLOCK_DETECTION_H */\n"
  },
  {
    "path": "src/include/distributed/distributed_execution_locks.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * distributed_execution_locks.h\n *\t  Locking Infrastructure for distributed execution.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef DISTRIBUTED_EXECUTION_LOCKS_H\n#define DISTRIBUTED_EXECUTION_LOCKS_H\n\n#include \"postgres.h\"\n\n#include \"nodes/pg_list.h\"\n#include \"storage/lockdefs.h\"\n\n#include \"distributed/multi_physical_planner.h\"\n\nextern void AcquireExecutorShardLocksForExecution(RowModifyLevel modLevel,\n\t\t\t\t\t\t\t\t\t\t\t\t  List *taskList);\nextern void AcquireExecutorShardLocksForRelationRowLockList(List *relationRowLockList);\nextern bool RequiresConsistentSnapshot(Task *task);\nextern void AcquireMetadataLocks(List *taskList);\nextern void LockPartitionsInRelationList(List *relationIdList, LOCKMODE lockmode);\nextern void LockPartitionRelations(Oid relationId, LOCKMODE lockMode);\nextern void LockPartitionsForDistributedPlan(DistributedPlan *distributedPlan);\n\n\n#endif /* DISTRIBUTED_EXECUTION_LOCKS_H */\n"
  },
  {
    "path": "src/include/distributed/distributed_planner.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * distributed_planner.h\n *\t  General Citus planner code.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef DISTRIBUTED_PLANNER_H\n#define DISTRIBUTED_PLANNER_H\n\n#include \"postgres.h\"\n\n#include \"nodes/pathnodes.h\"\n#include \"nodes/plannodes.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/log_utils.h\"\n\n\n/* values used by jobs and tasks which do not require identifiers */\n#define INVALID_JOB_ID 0\n#define INVALID_TASK_ID 0\n\n#define CURSOR_OPT_FORCE_DISTRIBUTED 0x080000\n\n\n/* level of planner calls */\nextern int PlannerLevel;\n\n\n/* RouterPlanType is used to determine the router plan to invoke */\ntypedef enum RouterPlanType\n{\n\tINSERT_SELECT_INTO_CITUS_TABLE,\n\tINSERT_SELECT_INTO_LOCAL_TABLE,\n\tDML_QUERY,\n\tSELECT_QUERY,\n\tMERGE_QUERY,\n\tREPLAN_WITH_BOUND_PARAMETERS\n} RouterPlanType;\n\n\ntypedef struct RelationRestrictionContext\n{\n\tbool allReferenceTables;\n\tList *relationRestrictionList;\n} RelationRestrictionContext;\n\n\ntypedef struct RootPlanParams\n{\n\tPlannerInfo *root;\n\n\t/*\n\t * Copy of root->plan_params. root->plan_params is not preserved in\n\t * relation_restriction_equivalence, so we need to create a copy.\n\t */\n\tList *plan_params;\n} RootPlanParams;\n\ntypedef struct RelationRestriction\n{\n\tIndex index;\n\tOid relationId;\n\tbool citusTable;\n\tRangeTblEntry *rte;\n\tRelOptInfo *relOptInfo;\n\tPlannerInfo *plannerInfo;\n\n\t/* list of RootPlanParams for all outer nodes */\n\tList *outerPlanParamsList;\n\n\t/* list of translated vars, this is copied from postgres since it gets deleted on postgres*/\n\tList *translatedVars;\n} RelationRestriction;\n\ntypedef struct JoinRestrictionContext\n{\n\tList *joinRestrictionList;\n\tbool hasSemiJoin;\n\tbool hasOuterJoin;\n} JoinRestrictionContext;\n\ntypedef struct JoinRestriction\n{\n\tJoinType joinType;\n\tList *joinRestrictInfoList;\n\tPlannerInfo *plannerInfo;\n\tRelids innerrelRelids;\n\tRelids outerrelRelids;\n} JoinRestriction;\n\ntypedef struct FastPathRestrictionContext\n{\n\tbool fastPathRouterQuery;\n\n\t/*\n\t * While calculating fastPathRouterQuery, we could sometimes be\n\t * able to extract the distribution key value as well (such as when\n\t * there are no prepared statements). Could be NULL when the distribution\n\t * key contains parameter, so check for it before using.\n\t */\n\tConst *distributionKeyValue;\n\n\t/*\n\t * Set to true when distKey = Param; in the queryTree\n\t */\n\tbool distributionKeyHasParam;\n\n\t/*\n\t * Indicates to hold off calling the fast path planner until its\n\t * known if the shard is local or not.\n\t */\n\tbool delayFastPathPlanning;\n} FastPathRestrictionContext;\n\nstruct DistributedPlanningContext;\ntypedef struct PlannerRestrictionContext\n{\n\tRelationRestrictionContext *relationRestrictionContext;\n\tJoinRestrictionContext *joinRestrictionContext;\n\n\t/*\n\t * When the query is qualified for fast path, we don't have\n\t * the RelationRestrictionContext and JoinRestrictionContext\n\t * since those are dependent to calling standard_planner.\n\t * Instead, we keep this struct to pass some extra information.\n\t */\n\tFastPathRestrictionContext *fastPathRestrictionContext;\n\tMemoryContext memoryContext;\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n\n\t/*\n\t * Enable access to the distributed planning context from\n\t * planner hooks called by Postgres. Enables Citus to track\n\t * changes made by Postgres to the query tree (such as\n\t * expansion of virtual columns) and ensure they are reflected\n\t * back to subsequent distributed planning.\n\t */\n\tstruct DistributedPlanningContext *planContext;\n#endif\n} PlannerRestrictionContext;\n\ntypedef struct RelationShard\n{\n\tCitusNode type;\n\tOid relationId;\n\tuint64 shardId;\n} RelationShard;\n\ntypedef struct RelationRowLock\n{\n\tCitusNode type;\n\tOid relationId;\n\tLockClauseStrength rowLockStrength;\n} RelationRowLock;\n\n\n/*\n * Parameters to be set according to range table entries of a query.\n */\ntypedef struct RTEListProperties\n{\n\tbool hasPostgresLocalTable;\n\n\tbool hasReferenceTable;\n\tbool hasCitusLocalTable;\n\n\t/* includes hash, single-shard, append and range partitioned tables */\n\tbool hasDistributedTable;\n\n\t/*\n\t * Effectively, hasDistributedTable is equal to\n\t *  \"hasDistTableWithShardKey || hasSingleShardDistTable\".\n\t *\n\t * We provide below two for the callers that want to know what kind of\n\t * distributed tables that given query has references to.\n\t */\n\tbool hasDistTableWithShardKey;\n\tbool hasSingleShardDistTable;\n\n\t/* union of hasReferenceTable, hasCitusLocalTable and hasDistributedTable */\n\tbool hasCitusTable;\n\n\tbool hasMaterializedView;\n} RTEListProperties;\n\n\ntypedef struct DistributedPlanningContext\n{\n\t/* The parsed query that is given to the planner. It is a slightly modified\n\t * to work with the standard_planner */\n\tQuery *query;\n\n\t/* A copy of the original parsed query that is given to the planner. This\n\t * doesn't contain most of the changes that are made to parse. There's one\n\t * that change that is made for non fast path router queries though, which\n\t * is the assigning of RTE identities using AssignRTEIdentities. This is\n\t * NULL for non distributed plans, since those don't need it. */\n\tQuery *originalQuery;\n\n\t/* the cursor options given to the planner */\n\tint cursorOptions;\n\n\t/* the ParamListInfo that is given to the planner */\n\tParamListInfo boundParams;\n\n\t/* Plan created either by standard_planner or by FastPathPlanner */\n\tPlannedStmt *plan;\n\n\t/* Our custom restriction context */\n\tPlannerRestrictionContext *plannerRestrictionContext;\n} DistributedPlanningContext;\n\n\n/*\n * CitusCustomScanPath is injected into the planner during the combine query planning\n * phase of the logical planner.\n *\n * We call out to the standard planner to plan the combine query part for the output of\n * the logical planner. This makes it easier to implement new sql features into the\n * logical planner by not having to manually implement the plan creation for the combine\n * query on the coordinator..\n */\ntypedef struct CitusCustomScanPath\n{\n\tCustomPath custom_path;\n\n\t/*\n\t * Custom scan node computed by the citus planner that will produce the tuples for the\n\t * path we are injecting during the planning of the combine query\n\t */\n\tCustomScan *remoteScan;\n} CitusCustomScanPath;\n\n\nextern PlannedStmt * distributed_planner(Query *parse,\n\t\t\t\t\t\t\t\t\t\t const char *query_string,\n\t\t\t\t\t\t\t\t\t\t int cursorOptions,\n\t\t\t\t\t\t\t\t\t\t ParamListInfo boundParams);\n\n\n/*\n * Common hint message to workaround using postgres local and citus local tables\n * in distributed queries\n */\n#define LOCAL_TABLE_SUBQUERY_CTE_HINT \\\n\t\t\"Use CTE's or subqueries to select from local tables and use them in joins\"\n\nextern List * ExtractRangeTableEntryList(Query *query);\nextern bool NeedsDistributedPlanning(Query *query);\nextern List * TranslatedVarsForRteIdentity(int rteIdentity);\nextern struct DistributedPlan * GetDistributedPlan(CustomScan *node);\nextern void multi_relation_restriction_hook(PlannerInfo *root, RelOptInfo *relOptInfo,\n\t\t\t\t\t\t\t\t\t\t\tIndex restrictionIndex, RangeTblEntry *rte);\nextern void multi_get_relation_info_hook(PlannerInfo *root, Oid relationObjectId, bool\n\t\t\t\t\t\t\t\t\t\t inhparent, RelOptInfo *rel);\nextern void multi_join_restriction_hook(PlannerInfo *root,\n\t\t\t\t\t\t\t\t\t\tRelOptInfo *joinrel,\n\t\t\t\t\t\t\t\t\t\tRelOptInfo *outerrel,\n\t\t\t\t\t\t\t\t\t\tRelOptInfo *innerrel,\n\t\t\t\t\t\t\t\t\t\tJoinType jointype,\n\t\t\t\t\t\t\t\t\t\tJoinPathExtraData *extra);\nextern bool HasUnresolvedExternParamsWalker(Node *expression, ParamListInfo boundParams);\nextern bool IsModifyCommand(Query *query);\nextern void EnsurePartitionTableNotReplicated(Oid relationId);\nextern Node * ResolveExternalParams(Node *inputNode, ParamListInfo boundParams);\nextern bool IsMultiTaskPlan(struct DistributedPlan *distributedPlan);\nextern RangeTblEntry * RemoteScanRangeTableEntry(List *columnNameList);\nextern int GetRTEIdentity(RangeTblEntry *rte);\nextern bool GetOriginalInh(RangeTblEntry *rte);\nextern LOCKMODE GetQueryLockMode(Query *query);\nextern int32 BlessRecordExpression(Expr *expr);\nextern void DissuadePlannerFromUsingPlan(PlannedStmt *plan);\nextern PlannedStmt * FinalizePlan(PlannedStmt *localPlan,\n\t\t\t\t\t\t\t\t  struct DistributedPlan *distributedPlan);\nextern bool ContainsSingleShardTable(Query *query);\nextern RTEListProperties * GetRTEListPropertiesForQuery(Query *query);\n\n\nextern struct DistributedPlan * CreateDistributedPlan(uint64 planId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  bool allowRecursivePlanning,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ParamListInfo boundParams,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  bool hasUnresolvedParams,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext);\n\n#endif /* DISTRIBUTED_PLANNER_H */\n"
  },
  {
    "path": "src/include/distributed/distribution_column.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * distribution_column.h\n *\t  Type and function declarations used for handling the distribution\n *    column of distributed tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef DISTRIBUTION_COLUMN_H\n#define DISTRIBUTION_COLUMN_H\n\n\n#include \"utils/rel.h\"\n\n\n/* Remaining metadata utility functions  */\nextern Var * BuildDistributionKeyFromColumnName(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\tchar *columnName,\n\t\t\t\t\t\t\t\t\t\t\t\tLOCKMODE lockMode);\nextern char * ColumnToColumnName(Oid relationId, Node *columnNode);\nextern Oid ColumnTypeIdForRelationColumnName(Oid relationId, char *columnName);\nextern void EnsureValidDistributionColumn(Oid relationId, char *columnName);\n\n#endif   /* DISTRIBUTION_COLUMN_H */\n"
  },
  {
    "path": "src/include/distributed/enterprise.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * enterprise.h\n *\n * Utilities related to enterprise code in the community version.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_ENTERPRISE_H\n#define CITUS_ENTERPRISE_H\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n\n#define NOT_SUPPORTED_IN_COMMUNITY(name) \\\n\t\tPG_FUNCTION_INFO_V1(name); \\\n\t\tDatum name(PG_FUNCTION_ARGS) { \\\n\t\t\tereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \\\n\t\t\t\t\t\t\terrmsg(# name \"() is only supported on Citus Enterprise\"))); \\\n\t\t}\n\n#endif\n"
  },
  {
    "path": "src/include/distributed/error_codes.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * error_codes.h\n *   Error codes that are specific to Citus\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_ERROR_CODES_H\n#define CITUS_ERROR_CODES_H\n\n\n#include \"utils/elog.h\"\n\n\n#define ERRCODE_CITUS_INTERMEDIATE_RESULT_NOT_FOUND MAKE_SQLSTATE('C', 'I', 'I', 'N', 'F')\n\n\n#endif /* CITUS_ERROR_CODES_H */\n"
  },
  {
    "path": "src/include/distributed/errormessage.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * errormessage.h\n *\t  Error handling related support functionality.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef ERRORMESSAGE_H\n#define ERRORMESSAGE_H\n\n#include \"c.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/citus_nodes.h\"\n\n\ntypedef struct DeferredErrorMessage\n{\n\tCitusNode tag;\n\n\tint code;\n\tconst char *message;\n\tconst char *detail;\n\tconst char *hint;\n\tconst char *filename;\n\tint linenumber;\n\tconst char *functionname;\n} DeferredErrorMessage;\n\n\n/*\n * DeferredError allocates a deferred error message, that can later be emitted\n * using RaiseDeferredError().  These error messages can be\n * serialized/copied/deserialized, i.e. can be embedded in plans and such.\n */\n#define DeferredError(code, message, detail, hint) \\\n\t\tDeferredErrorInternal(code, message, detail, hint, __FILE__, __LINE__, \\\n\t\t\t\t\t\t\t  __func__)\n\nDeferredErrorMessage * DeferredErrorInternal(int code, const char *message,\n\t\t\t\t\t\t\t\t\t\t\t const char *detail, const char *hint,\n\t\t\t\t\t\t\t\t\t\t\t const char *filename, int linenumber, const\n\t\t\t\t\t\t\t\t\t\t\t char *functionname);\n\n/*\n * RaiseDeferredError emits a previously allocated error using the specified\n * severity.\n *\n * The trickery with __builtin_constant_p/pg_unreachable aims to have the\n * compiler understand that the function will not return if elevel >= ERROR.\n */\n#ifdef HAVE__BUILTIN_CONSTANT_P\n#define RaiseDeferredError(error, elevel) \\\n\t\tdo { \\\n\t\t\tRaiseDeferredErrorInternal(error, elevel); \\\n\t\t\tif (__builtin_constant_p(elevel) && (elevel) >= ERROR) { \\\n\t\t\t\tpg_unreachable(); } \\\n\t\t} \\\n\t\twhile (0)\n#else  /* !HAVE_BUILTIN_CONSTANT_P */\n#define RaiseDeferredError(error, elevel) \\\n\t\tdo { \\\n\t\t\tconst int elevel_ = (elevel); \\\n\t\t\tRaiseDeferredErrorInternal(error, elevel_); \\\n\t\t\tif (elevel_ >= ERROR) { \\\n\t\t\t\tpg_unreachable(); } \\\n\t\t} \\\n\t\twhile (0)\n#endif /* HAVE_BUILTIN_CONSTANT_P */\n\nvoid RaiseDeferredErrorInternal(DeferredErrorMessage *error, int elevel);\n\n#endif\n"
  },
  {
    "path": "src/include/distributed/executor_util.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * executor_util.h\n *\t  Utility functions for executing task lists.\n *\n *-------------------------------------------------------------------------\n */\n#ifndef EXECUTOR_UTIL_H\n#define EXECUTOR_UTIL_H\n\n#include \"fmgr.h\"\n#include \"funcapi.h\"\n\n#include \"access/tupdesc.h\"\n#include \"nodes/params.h\"\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/multi_physical_planner.h\"\n\n\n/* utility functions for dealing with tasks in the executor */\nextern bool TaskListModifiesDatabase(RowModifyLevel modLevel, List *taskList);\nextern bool TaskListRequiresRollback(List *taskList);\nextern bool TaskListRequires2PC(List *taskList);\nextern bool TaskListCannotBeExecutedInTransaction(List *taskList);\nextern bool SelectForUpdateOnReferenceTable(List *taskList);\nextern bool ReadOnlyTask(TaskType taskType);\nextern bool ModifiedTableReplicated(List *taskList);\nextern bool ShouldRunTasksSequentially(List *taskList);\n\n/* utility functions for handling parameters in the executor */\nextern void ExtractParametersForRemoteExecution(ParamListInfo paramListInfo,\n\t\t\t\t\t\t\t\t\t\t\t\tOid **parameterTypes,\n\t\t\t\t\t\t\t\t\t\t\t\tconst char ***parameterValues);\nextern void ExtractParametersFromParamList(ParamListInfo paramListInfo,\n\t\t\t\t\t\t\t\t\t\t   Oid **parameterTypes,\n\t\t\t\t\t\t\t\t\t\t   const char ***parameterValues, bool\n\t\t\t\t\t\t\t\t\t\t   useOriginalCustomTypeOids);\n\n/* utility functions for processing tuples in the executor */\nextern AttInMetadata * TupleDescGetAttBinaryInMetadata(TupleDesc tupdesc);\nextern HeapTuple BuildTupleFromBytes(AttInMetadata *attinmeta, fmStringInfo *values);\n\n\n#endif /* EXECUTOR_UTIL_H */\n"
  },
  {
    "path": "src/include/distributed/extended_op_node_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * extended_op_node_utils.h\n *\t  General Citus planner code.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef EXTENDED_OP_NODE_UTILS_H_\n#define EXTENDED_OP_NODE_UTILS_H_\n\n#include \"distributed/multi_logical_planner.h\"\n\n\n/*\n * ExtendedOpNodeProperties is a helper structure that is used to\n * share the common information among the worker and coordinator extended\n * op nodes.\n *\n * It is designed to be a read-only singleton object per extended op node\n * generation and processing.\n */\ntypedef struct ExtendedOpNodeProperties\n{\n\tbool groupedByDisjointPartitionColumn;\n\tbool repartitionSubquery;\n\tbool hasNonPartitionColumnDistinctAgg;\n\tbool pullDistinctColumns;\n\tbool hasWindowFuncs;\n\tbool onlyPushableWindowFunctions;\n\tbool pullUpIntermediateRows;\n\tbool pushDownGroupingAndHaving;\n\n\t/* indicates whether the MultiExtendedOp has a GROUP BY */\n\tbool hasGroupBy;\n\n\t/* indicates whether the MultiExtendedOp has an aggregate on the target list */\n\tbool hasAggregate;\n} ExtendedOpNodeProperties;\n\n\nextern ExtendedOpNodeProperties BuildExtendedOpNodeProperties(MultiExtendedOp *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  extendedOpNode, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  hasNonDistributableAggregates);\n\n\n#endif /* EXTENDED_OP_NODE_UTILS_H_ */\n"
  },
  {
    "path": "src/include/distributed/foreign_key_relationship.h",
    "content": "/*-------------------------------------------------------------------------\n * foreign_key_relationship.h\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef FOREIGN_KEY_RELATIONSHIP_H\n#define FOREIGN_KEY_RELATIONSHIP_H\n\n#include \"postgres.h\"\n\n#include \"postgres_ext.h\"\n\n#include \"nodes/primnodes.h\"\n#include \"utils/hsearch.h\"\n#include \"utils/relcache.h\"\n\nextern List * GetForeignKeyConnectedRelationIdList(Oid relationId);\nextern bool ShouldUndistributeCitusLocalTable(Oid relationId);\nextern List * ReferencedRelationIdList(Oid relationId);\nextern List * ReferencingRelationIdList(Oid relationId);\nextern void SetForeignConstraintRelationshipGraphInvalid(void);\nextern bool OidVisited(HTAB *oidVisitedMap, Oid oid);\nextern void VisitOid(HTAB *oidVisitedMap, Oid oid);\n\n#endif\n"
  },
  {
    "path": "src/include/distributed/function_call_delegation.h",
    "content": "/*\n * function_call_delegation.h\n *    Declarations for public functions and variables used to delegate\n *    function calls to worker nodes.\n *\n * Copyright (c), Citus Data, Inc.\n */\n\n#ifndef FUNCTION_CALL_DELEGATION_H\n#define FUNCTION_CALL_DELEGATION_H\n\n#include \"postgres.h\"\n\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n\n\n/*\n * These flags keep track of whether the process is currently in a delegated\n * function or procedure call.\n */\nextern bool InTopLevelDelegatedFunctionCall;\nextern bool InDelegatedProcedureCall;\n\nPlannedStmt * TryToDelegateFunctionCall(DistributedPlanningContext *planContext);\nextern void CheckAndResetAllowedShardKeyValueIfNeeded(void);\nextern bool IsShardKeyValueAllowed(Const *shardKey, uint32 colocationId);\n\n#endif /* FUNCTION_CALL_DELEGATION_H */\n"
  },
  {
    "path": "src/include/distributed/function_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * function_utils.h\n *\t  Utilities regarding calls to PG functions\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_FUNCTION_UTILS_H\n#define CITUS_FUNCTION_UTILS_H\n\n#include \"postgres.h\"\n\n#include \"nodes/execnodes.h\"\n\n\n/* Function declaration for getting oid for the given function name */\nextern Oid FunctionOid(const char *schemaName, const char *functionName,\n\t\t\t\t\t   int argumentCount);\nextern Oid FunctionOidExtended(const char *schemaName, const char *functionName, int\n\t\t\t\t\t\t\t   argumentCount, bool missingOK);\nextern ReturnSetInfo * FunctionCallGetTupleStore1(PGFunction function, Oid functionId,\n\t\t\t\t\t\t\t\t\t\t\t\t  Datum argument);\n\n#endif /* CITUS_FUNCTION_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/hash_helpers.h",
    "content": "/*-------------------------------------------------------------------------\n * hash_helpers.h\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef HASH_HELPERS_H\n#define HASH_HELPERS_H\n\n#include \"postgres.h\"\n\n#include \"utils/hsearch.h\"\n\n#include \"pg_version_constants.h\"\n\n/*\n * assert_valid_hash_key2 checks if a type that contains 2 fields contains no\n * padding bytes. This is necessary to use a type as a hash key with tag_hash.\n */\n#define assert_valid_hash_key2(type, field1, field2) \\\n\t\tStaticAssertDecl( \\\n\t\t\tsizeof(type) == sizeof(((type) { 0 }).field1) \\\n\t\t\t+ sizeof(((type) { 0 }).field2), \\\n\t\t\t# type \" has padding bytes, but is used as a hash key in a simple hash\");\n\n/*\n * assert_valid_hash_key3 checks if a type that contains 3 fields contains no\n * padding bytes. This is necessary to use a type as a hash key with tag_hash.\n */\n#define assert_valid_hash_key3(type, field1, field2, field3) \\\n\t\tStaticAssertDecl( \\\n\t\t\tsizeof(type) == sizeof(((type) { 0 }).field1) \\\n\t\t\t+ sizeof(((type) { 0 }).field2) \\\n\t\t\t+ sizeof(((type) { 0 }).field3), \\\n\t\t\t# type \" has padding bytes, but is used as a hash key in a simple hash\");\n\nextern void hash_delete_all(HTAB *htab);\n\n/*\n * foreach_htab -\n *\t  a convenience macro which loops through a HTAB\n */\n\n#define foreach_htab(var, status, htab) \\\n\t\thash_seq_init((status), (htab)); \\\n\t\tfor ((var) = hash_seq_search(status); \\\n\t\t\t (var) != NULL; \\\n\t\t\t (var) = hash_seq_search(status))\n\nextern void foreach_htab_cleanup(void *var, HASH_SEQ_STATUS *status);\n\nextern HTAB * CreateSimpleHashWithNameAndSizeInternal(Size keysize, Size entrysize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  char *name, long nelem);\n\n/*\n * CreatesSimpleHash creates a hash table that hash its key using the tag_hash\n * and stores then entries in the CurrentMemoryContext.\n *\n * We use 32 as the initial number of elements that fit into this hash\n * table. This value seems a reasonable tradeof between two issues:\n * 1. An empty hashmap shouldn't take up a lot of space\n * 2. Doing a few inserts shouldn't require growing the hashmap\n *\n * NOTE: No performance testing has been performed when choosing this\n * value. If this ever turns out to be a problem, feel free to do some\n * performance tests.\n *\n * IMPORTANT: Because this uses tag_hash it's required that the keyType\n * contains no automatic padding bytes, because that will result in tag_hash\n * returning undefined values. You can check this using assert_valid_hash_keyX.\n */\n#define CreateSimpleHash(keyType, entryType) \\\n\t\tCreateSimpleHashWithNameAndSize(keyType, entryType, \\\n\t\t\t\t\t\t\t\t\t\t# entryType \"Hash\", 32)\n\n/*\n * Same as CreateSimpleHash but allows specifying the name\n */\n#define CreateSimpleHashWithName(keyType, entryType, name) \\\n\t\tCreateSimpleHashWithNameAndSize(keyType, entryType, \\\n\t\t\t\t\t\t\t\t\t\tname, 32)\n\n/*\n * CreateSimpleHashWithSize is the same as CreateSimpleHash, but allows\n * configuring of the amount of elements that initially fit in the hash table.\n */\n#define CreateSimpleHashWithSize(keyType, entryType, size) \\\n\t\tCreateSimpleHashWithNameAndSize(keyType, entryType, \\\n\t\t\t\t\t\t\t\t\t\t# entryType \"Hash\", size)\n\n#define CreateSimpleHashWithNameAndSize(keyType, entryType, name, size) \\\n\t\tCreateSimpleHashWithNameAndSizeInternal(sizeof(keyType), \\\n\t\t\t\t\t\t\t\t\t\t\t\tsizeof(entryType), \\\n\t\t\t\t\t\t\t\t\t\t\t\tname, size)\n\n\n/*\n * CreatesSimpleHashSet creates a hash set that hashes its values using the\n * tag_hash and stores the values in the CurrentMemoryContext.\n */\n#define CreateSimpleHashSet(keyType) \\\n\t\tCreateSimpleHashWithName(keyType, keyType, \\\n\t\t\t\t\t\t\t\t # keyType \"HashSet\")\n\n/*\n * CreatesSimpleHashSetWithSize creates a hash set that hashes its values using\n * the tag_hash and stores the values in the CurrentMemoryContext. It allows\n * specifying its number of elements.\n */\n#define CreateSimpleHashSetWithSize(keyType, size) \\\n\t\tCreateSimpleHashWithNameAndSize(keyType, keyType, # keyType \"HashSet\", size)\n\n/*\n * CreatesSimpleHashSetWithName creates a hash set that hashes its values using the\n * tag_hash and stores the values in the CurrentMemoryContext. It allows\n * specifying its name.\n */\n#define CreateSimpleHashSetWithName(keyType, name) \\\n\t\tCreateSimpleHashWithName(keyType, keyType, name)\n\n/*\n * CreatesSimpleHashSetWithName creates a hash set that hashes its values using the\n * tag_hash and stores the values in the CurrentMemoryContext. It allows\n * specifying its name and number of elements.\n */\n#define CreateSimpleHashSetWithNameAndSize(keyType, name, size) \\\n\t\tCreateSimpleHashWithNameAndSize(keyType, keyType, name, size)\n\n\n#endif\n"
  },
  {
    "path": "src/include/distributed/insert_select_executor.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * insert_select_executor.h\n *\n * Declarations for public functions and types related to executing\n * INSERT..SELECT commands.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef INSERT_SELECT_EXECUTOR_H\n#define INSERT_SELECT_EXECUTOR_H\n\n\n#include \"executor/execdesc.h\"\n\n\nextern TupleTableSlot * NonPushableInsertSelectExecScan(CustomScanState *node);\nextern List * BuildColumnNameListFromTargetList(Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\tList *insertTargetList);\n\n#endif /* INSERT_SELECT_EXECUTOR_H */\n"
  },
  {
    "path": "src/include/distributed/insert_select_planner.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * insert_select_planner.h\n *\n * Declarations for public functions and types related to planning\n * INSERT..SELECT commands.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef INSERT_SELECT_PLANNER_H\n#define INSERT_SELECT_PLANNER_H\n\n\n#include \"postgres.h\"\n\n#include \"nodes/execnodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/plannodes.h\"\n\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n\n\nextern bool InsertSelectIntoCitusTable(Query *query);\nextern bool CheckInsertSelectQuery(Query *query);\nextern bool InsertSelectIntoLocalTable(Query *query);\nextern Query * ReorderInsertSelectTargetLists(Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t  RangeTblEntry *insertRte,\n\t\t\t\t\t\t\t\t\t\t\t  RangeTblEntry *subqueryRte);\nextern void NonPushableInsertSelectExplainScan(CustomScanState *node, List *ancestors,\n\t\t\t\t\t\t\t\t\t\t\t   struct ExplainState *es);\nextern DistributedPlan * CreateInsertSelectPlan(uint64 planId, Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\tPlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\tParamListInfo boundParams);\nextern DistributedPlan * CreateInsertSelectIntoLocalTablePlan(uint64 planId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ParamListInfo\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  boundParams, bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  hasUnresolvedParams,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext);\nextern char * InsertSelectResultIdPrefix(uint64 planId);\nextern bool PlanningInsertSelect(void);\nextern Query * WrapSubquery(Query *subquery);\n\n\n#endif /* INSERT_SELECT_PLANNER_H */\n"
  },
  {
    "path": "src/include/distributed/intermediate_result_pruning.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * intermediate_result_pruning.h\n *   Functions for pruning intermediate result broadcasting.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef INTERMEDIATE_RESULT_PRUNING_H\n#define INTERMEDIATE_RESULT_PRUNING_H\n\n#include \"distributed/subplan_execution.h\"\n\nextern bool LogIntermediateResults;\n\nextern List * FindSubPlanUsages(DistributedPlan *plan);\nextern List * FindAllWorkerNodesUsingSubplan(HTAB *intermediateResultsHash,\n\t\t\t\t\t\t\t\t\t\t\t char *resultId);\nextern HTAB * MakeIntermediateResultHTAB(void);\nextern void RecordSubplanExecutionsOnNodes(HTAB *intermediateResultsHash,\n\t\t\t\t\t\t\t\t\t\t   DistributedPlan *distributedPlan);\nextern IntermediateResultsHashEntry * SearchIntermediateResult(HTAB *resultsHash,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   char *resultId);\n\n#endif /* INTERMEDIATE_RESULT_PRUNING_H */\n"
  },
  {
    "path": "src/include/distributed/intermediate_results.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * intermediate_results.h\n *   Functions for writing and reading intermediate results.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef INTERMEDIATE_RESULTS_H\n#define INTERMEDIATE_RESULTS_H\n\n\n#include \"fmgr.h\"\n\n#include \"nodes/execnodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"tcop/dest.h\"\n#include \"utils/builtins.h\"\n#include \"utils/palloc.h\"\n\n#include \"distributed/commands/multi_copy.h\"\n\n\n/*\n * DistributedResultFragment represents a fragment of a distributed result.\n */\ntypedef struct DistributedResultFragment\n{\n\t/* result's id, which can be used by read_intermediate_results(), ... */\n\tchar *resultId;\n\n\t/* location of the result */\n\tuint32 nodeId;\n\n\t/* number of rows in the result file */\n\tint rowCount;\n\n\t/*\n\t * The fragment contains the rows which match the partitioning method\n\t * and partitioning ranges of targetShardId. The shape of each row matches\n\t * the schema of the relation to which targetShardId belongs to.\n\t */\n\tuint64 targetShardId;\n\n\t/* what is the index of targetShardId in its relation's sorted shard list? */\n\tint targetShardIndex;\n} DistributedResultFragment;\n\n\n/*\n * NodePair contains the source and destination node in a NodeToNodeFragmentsTransfer.\n * It is a separate struct to use it as a key in a hash table.\n */\ntypedef struct NodePair\n{\n\tuint32 sourceNodeId;\n\tuint32 targetNodeId;\n} NodePair;\n\n\n/*\n * NodeToNodeFragmentsTransfer contains all fragments that need to be fetched from\n * the source node to the destination node in the NodePair.\n */\ntypedef struct NodeToNodeFragmentsTransfer\n{\n\tNodePair nodes;\n\tList *fragmentList;\n} NodeToNodeFragmentsTransfer;\n\n/* Forward Declarations */\nstruct CitusTableCacheEntry;\n\n/* intermediate_results.c */\nextern DestReceiver * CreateRemoteFileDestReceiver(const char *resultId,\n\t\t\t\t\t\t\t\t\t\t\t\t   EState *executorState,\n\t\t\t\t\t\t\t\t\t\t\t\t   List *initialNodeList, bool\n\t\t\t\t\t\t\t\t\t\t\t\t   writeLocalFile);\nextern DestReceiver * CreatePartitionedResultDestReceiver(int partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  int partitionCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  CitusTableCacheEntry *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardSearchInfo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  DestReceiver **\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  partitionedDestReceivers,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  bool lazyStartup,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  bool allowNullPartitionValues);\nextern CitusTableCacheEntry * QueryTupleShardSearchInfo(ArrayType *minValuesArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tArrayType *maxValuesArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar partitionMethod,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tVar *partitionColumn);\nextern void WriteToLocalFile(StringInfo copyData, FileCompat *fileCompat);\nextern uint64 RemoteFileDestReceiverBytesSent(DestReceiver *destReceiver);\nextern void SendQueryResultViaCopy(const char *resultId);\nextern void ReceiveQueryResultViaCopy(const char *resultId);\nextern void RemoveIntermediateResultsDirectories(void);\nextern int64 IntermediateResultSize(const char *resultId);\nextern char * QueryResultFileName(const char *resultId);\nextern char * CreateIntermediateResultsDirectory(void);\nextern ArrayType * CreateArrayFromDatums(Datum *datumArray, bool *nullsArray, int\n\t\t\t\t\t\t\t\t\t\t datumCount, Oid typeId);\n\n\n/* distributed_intermediate_results.c */\nextern List ** RedistributeTaskListResults(const char *resultIdPrefix,\n\t\t\t\t\t\t\t\t\t\t   List *selectTaskList,\n\t\t\t\t\t\t\t\t\t\t   int partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t\t   CitusTableCacheEntry *targetRelation,\n\t\t\t\t\t\t\t\t\t\t   bool binaryFormat);\nextern List * PartitionTasklistResults(const char *resultIdPrefix, List *selectTaskList,\n\t\t\t\t\t\t\t\t\t   int partitionColumnIndex,\n\t\t\t\t\t\t\t\t\t   CitusTableCacheEntry *distributionScheme,\n\t\t\t\t\t\t\t\t\t   bool binaryFormat);\nextern char * QueryStringForFragmentsTransfer(NodeToNodeFragmentsTransfer *\n\t\t\t\t\t\t\t\t\t\t\t  fragmentsTransfer);\nextern void ShardMinMaxValueArrays(ShardInterval **shardIntervalArray, int shardCount,\n\t\t\t\t\t\t\t\t   Oid intervalTypeId, ArrayType **minValueArray,\n\t\t\t\t\t\t\t\t   ArrayType **maxValueArray);\n\n#endif /* INTERMEDIATE_RESULTS_H */\n"
  },
  {
    "path": "src/include/distributed/jsonbutils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * jsonbutils.h\n *\n * Declarations for public utility functions related to jsonb.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#ifndef CITUS_JSONBUTILS_H\n#define CITUS_JSONBUTILS_H\n\n#include \"postgres.h\"\n\nbool ExtractFieldJsonbDatum(Datum jsonbDoc, const char *fieldName, Datum *result);\ntext * ExtractFieldTextP(Datum jsonbDoc, const char *fieldName);\nbool ExtractFieldBoolean(Datum jsonbDoc, const char *fieldName, bool defaultValue);\nint32 ExtractFieldInt32(Datum jsonbDoc, const char *fieldName, int32 defaultValue);\n\n#endif /* CITUS_JSONBUTILS_H */\n"
  },
  {
    "path": "src/include/distributed/listutils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * listutils.h\n *\n * Declarations for public utility functions related to lists.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_LISTUTILS_H\n#define CITUS_LISTUTILS_H\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n\n#include \"nodes/pg_list.h\"\n#include \"utils/array.h\"\n#include \"utils/hsearch.h\"\n\n#include \"pg_version_compat.h\"\n\n\n/*\n * ListCellAndListWrapper stores a list and list cell.\n * This struct is used for functionContext. When iterating a list\n * in separate function calls, we need both the list and the current cell.\n * Therefore this wrapper stores both of them.\n */\ntypedef struct ListCellAndListWrapper\n{\n\tList *list;\n\tListCell *listCell;\n} ListCellAndListWrapper;\n\n/*\n * foreach_declared_ptr -\n *\t  a convenience macro which loops through a pointer list without needing a\n *\t  ListCell, just a declared pointer variable to store the pointer of the\n *\t  cell in.\n *\n *   How it works:\n *\t  - A ListCell is declared with the name {var}CellDoNotUse and used\n *\t    throughout the for loop using ## to concat.\n *\t  - To assign to var it needs to be done in the condition of the for loop,\n *\t    because we cannot use the initializer since a ListCell* variable is\n *\t    declared there.\n *\t  - || true is used to always enter the loop when cell is not null even if\n *\t    var is NULL.\n */\n#define foreach_declared_ptr(var, l) \\\n\t\tfor (ListCell *(var ## CellDoNotUse) = list_head(l); \\\n\t\t\t (var ## CellDoNotUse) != NULL && \\\n\t\t\t (((var) = lfirst(var ## CellDoNotUse)) || true); \\\n\t\t\t var ## CellDoNotUse = lnext(l, var ## CellDoNotUse))\n\n\n/*\n * foreach_declared_int -\n *\t  a convenience macro which loops through an int list without needing a\n *\t  ListCell, just a declared int variable to store the int of the cell in.\n *\t  For explanation of how it works see foreach_declared_ptr.\n */\n#define foreach_declared_int(var, l) \\\n\t\tfor (ListCell *(var ## CellDoNotUse) = list_head(l); \\\n\t\t\t (var ## CellDoNotUse) != NULL && \\\n\t\t\t (((var) = lfirst_int(var ## CellDoNotUse)) || true); \\\n\t\t\t var ## CellDoNotUse = lnext(l, var ## CellDoNotUse))\n\n\n/*\n * foreach_declared_oid -\n *\t  a convenience macro which loops through an oid list without needing a\n *\t  ListCell, just a declared Oid variable to store the oid of the cell in.\n *\t  For explanation of how it works see foreach_declared_ptr.\n */\n#define foreach_declared_oid(var, l) \\\n\t\tfor (ListCell *(var ## CellDoNotUse) = list_head(l); \\\n\t\t\t (var ## CellDoNotUse) != NULL && \\\n\t\t\t (((var) = lfirst_oid(var ## CellDoNotUse)) || true); \\\n\t\t\t var ## CellDoNotUse = lnext(l, var ## CellDoNotUse))\n\n/*\n * forboth_ptr -\n *\t  a convenience macro which loops through two lists of pointers at the same\n *\t  time, without needing a ListCell. It only needs two declared pointer\n *\t  variables to store the pointer of each of the two cells in.\n */\n#define forboth_ptr(var1, l1, var2, l2) \\\n\t\tfor (ListCell *(var1 ## CellDoNotUse) = list_head(l1), \\\n\t\t\t *(var2 ## CellDoNotUse) = list_head(l2); \\\n\t\t\t (var1 ## CellDoNotUse) != NULL && \\\n\t\t\t (var2 ## CellDoNotUse) != NULL && \\\n\t\t\t (((var1) = lfirst(var1 ## CellDoNotUse)) || true) && \\\n\t\t\t (((var2) = lfirst(var2 ## CellDoNotUse)) || true); \\\n\t\t\t var1 ## CellDoNotUse = lnext(l1, var1 ## CellDoNotUse), \\\n\t\t\t var2 ## CellDoNotUse = lnext(l2, var2 ## CellDoNotUse) \\\n\t\t\t )\n\n/*\n * forboth_ptr_oid -\n *\t  a convenience macro which loops through two lists at the same time. The\n *\t  first list should contain pointers and the second list should contain\n *\t  Oids. It does not need a ListCell to do this. It only needs two declared\n *\t  variables to store the pointer and the Oid of each of the two cells in.\n */\n#define forboth_ptr_oid(var1, l1, var2, l2) \\\n\t\tfor (ListCell *(var1 ## CellDoNotUse) = list_head(l1), \\\n\t\t\t *(var2 ## CellDoNotUse) = list_head(l2); \\\n\t\t\t (var1 ## CellDoNotUse) != NULL && \\\n\t\t\t (var2 ## CellDoNotUse) != NULL && \\\n\t\t\t (((var1) = lfirst(var1 ## CellDoNotUse)) || true) && \\\n\t\t\t (((var2) = lfirst_oid(var2 ## CellDoNotUse)) || true); \\\n\t\t\t var1 ## CellDoNotUse = lnext(l1, var1 ## CellDoNotUse), \\\n\t\t\t var2 ## CellDoNotUse = lnext(l2, var2 ## CellDoNotUse) \\\n\t\t\t )\n\n/*\n * forboth_int_oid -\n *\t  a convenience macro which loops through two lists at the same time. The\n *\t  first list should contain integers and the second list should contain\n *\t  Oids. It does not need a ListCell to do this. It only needs two declared\n *\t  variables to store the int and the Oid of each of the two cells in.\n */\n#define forboth_int_oid(var1, l1, var2, l2) \\\n\t\tfor (ListCell *(var1 ## CellDoNotUse) = list_head(l1), \\\n\t\t\t *(var2 ## CellDoNotUse) = list_head(l2); \\\n\t\t\t (var1 ## CellDoNotUse) != NULL && \\\n\t\t\t (var2 ## CellDoNotUse) != NULL && \\\n\t\t\t (((var1) = lfirst_int(var1 ## CellDoNotUse)) || true) && \\\n\t\t\t (((var2) = lfirst_oid(var2 ## CellDoNotUse)) || true); \\\n\t\t\t var1 ## CellDoNotUse = lnext(l1, var1 ## CellDoNotUse), \\\n\t\t\t var2 ## CellDoNotUse = lnext(l2, var2 ## CellDoNotUse) \\\n\t\t\t )\n\n/*\n * foreach_ptr_append -\n *\t  a convenience macro which loops through a pointer List and can append list\n *\t  elements without needing a ListCell or and index variable, just a declared\n *\t  pointer variable to store the iterated values.\n *\n *\t  PostgreSQL 13 changed the representation of Lists to expansible arrays,\n *\t  not chains of cons-cells. This changes the costs for accessing and\n *\t  mutating List contents. Therefore different implementations are provided.\n *\n *\t  For more information, see postgres commit with sha\n *\t  1cff1b95ab6ddae32faa3efe0d95a820dbfdc164\n *\n *\t  How it works:\n *\t  - An index is declared with the name {var}PositionDoNotUse and used\n *\t    throughout the for loop using ## to concat.\n *\t  - To assign to var it needs to be done in the condition of the for loop,\n *\t    because we cannot use the initializer since the index variable is\n *\t    declared there.\n *\t  - || true is used to always enter the loop even if var is NULL.\n */\n#define foreach_ptr_append(var, l) \\\n\t\tfor (int var ## PositionDoNotUse = 0; \\\n\t\t\t (var ## PositionDoNotUse) < list_length(l) && \\\n\t\t\t (((var) = list_nth(l, var ## PositionDoNotUse)) || true); \\\n\t\t\t var ## PositionDoNotUse++)\n\n/* utility functions declaration shared within this module */\nextern List * SortList(List *pointerList,\n\t\t\t\t\t   int (*ComparisonFunction)(const void *, const void *));\nextern void ** PointerArrayFromList(List *pointerList);\nextern HTAB * ListToHashSet(List *pointerList, Size keySize, bool isStringList);\nextern char * StringJoin(List *stringList, char delimiter);\nextern char * StringJoinParams(List *stringList, char delimiter,\n\t\t\t\t\t\t\t   char *prefix, char *postfix);\nextern List * ListTake(List *pointerList, int size);\nextern void * safe_list_nth(const List *list, int index);\nextern List * GeneratePositiveIntSequenceList(int upTo);\nextern List * GenerateListFromElement(void *listElement, int listLength);\nextern List * list_filter_oid(List *list, bool (*keepElement)(Oid element));\n\n#endif /* CITUS_LISTUTILS_H */\n"
  },
  {
    "path": "src/include/distributed/local_distributed_join_planner.h",
    "content": "\n/*-------------------------------------------------------------------------\n *\n * local_distributed_join_planner.h\n *\n * Declarations for functions to handle local-distributed table joins.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef LOCAL_DISTRIBUTED_JOIN_PLANNER_H\n#define LOCAL_DISTRIBUTED_JOIN_PLANNER_H\n\n#include \"postgres.h\"\n\n#include \"distributed/recursive_planning.h\"\n\n/* managed via guc.c */\ntypedef enum\n{\n\tLOCAL_JOIN_POLICY_NEVER = 0,\n\tLOCAL_JOIN_POLICY_PREFER_LOCAL = 1,\n\tLOCAL_JOIN_POLICY_PREFER_DISTRIBUTED = 2,\n\tLOCAL_JOIN_POLICY_AUTO = 3,\n} LocalJoinPolicy;\n\nextern int LocalTableJoinPolicy;\n\nextern bool ShouldConvertLocalTableJoinsToSubqueries(List *rangeTableList);\nextern void RecursivelyPlanLocalTableJoins(Query *query,\n\t\t\t\t\t\t\t\t\t\t   RecursivePlanningContext *context);\nextern List * RequiredAttrNumbersForRelation(RangeTblEntry *relationRte,\n\t\t\t\t\t\t\t\t\t\t\t PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\nextern List * RequiredAttrNumbersForRelationInternal(Query *queryToProcess, int rteIndex);\n\n#endif /* LOCAL_DISTRIBUTED_JOIN_PLANNER_H */\n"
  },
  {
    "path": "src/include/distributed/local_executor.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * local_executor.h\n *\tFunctions and global variables to control local query execution.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef LOCAL_EXECUTION_H\n#define LOCAL_EXECUTION_H\n\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/tuple_destination.h\"\n\n/* enabled with GUCs*/\nextern bool EnableLocalExecution;\nextern bool LogLocalCommands;\n\n/* global variable that tracks whether the local execution is on a shard */\nextern uint64 LocalExecutorShardId;\n\ntypedef enum LocalExecutionStatus\n{\n\tLOCAL_EXECUTION_REQUIRED,\n\tLOCAL_EXECUTION_OPTIONAL,\n\tLOCAL_EXECUTION_DISABLED\n} LocalExecutionStatus;\n\n/* extern function declarations */\nextern LocalExecutionStatus GetCurrentLocalExecutionStatus(void);\nextern uint64 ExecuteLocalTaskList(List *taskList, TupleDestination *defaultTupleDest);\nextern uint64 ExecuteLocalUtilityTaskList(List *utilityTaskList);\nextern uint64 ExecuteLocalTaskListExtended(List *taskList, ParamListInfo\n\t\t\t\t\t\t\t\t\t\t   orig_paramListInfo,\n\t\t\t\t\t\t\t\t\t\t   DistributedPlan *distributedPlan,\n\t\t\t\t\t\t\t\t\t\t   TupleDestination *defaultTupleDest,\n\t\t\t\t\t\t\t\t\t\t   bool isUtilityCommand);\nextern void ExtractLocalAndRemoteTasks(bool readOnlyPlan, List *taskList,\n\t\t\t\t\t\t\t\t\t   List **localTaskList, List **remoteTaskList);\nextern void ExecuteUtilityCommand(const char *utilityCommand);\nextern bool ShouldExecuteTasksLocally(List *taskList);\nextern bool AnyTaskAccessesLocalNode(List *taskList);\nextern bool TaskAccessesLocalNode(Task *task);\nextern void EnsureCompatibleLocalExecutionState(List *taskList);\nextern void ErrorIfTransactionAccessedPlacementsLocally(void);\nextern void DisableLocalExecution(void);\nextern void SetLocalExecutionStatus(LocalExecutionStatus newStatus);\nextern void ExtractParametersForLocalExecution(ParamListInfo paramListInfo,\n\t\t\t\t\t\t\t\t\t\t\t   Oid **parameterTypes,\n\t\t\t\t\t\t\t\t\t\t\t   const char ***parameterValues);\n\n#endif /* LOCAL_EXECUTION_H */\n"
  },
  {
    "path": "src/include/distributed/local_multi_copy.h",
    "content": "\n#ifndef LOCAL_MULTI_COPY\n#define LOCAL_MULTI_COPY\n\n/*\n * LocalCopyFlushThresholdByte is the threshold for local copy to be flushed.\n * There will be one buffer for each local placement, when the buffer size\n * exceeds this threshold, it will be flushed.\n *\n * Managed via GUC, the default is 512 kB.\n */\nextern int LocalCopyFlushThresholdByte;\n\nextern void WriteTupleToLocalShard(TupleTableSlot *slot, CitusCopyDestReceiver *copyDest,\n\t\t\t\t\t\t\t\t   int64\n\t\t\t\t\t\t\t\t   shardId,\n\t\t\t\t\t\t\t\t   CopyOutState localCopyOutState);\nextern void WriteTupleToLocalFile(TupleTableSlot *slot, CitusCopyDestReceiver *copyDest,\n\t\t\t\t\t\t\t\t  int64 shardId, CopyOutState localFileCopyOutState,\n\t\t\t\t\t\t\t\t  FileCompat *fileCompat);\nextern void FinishLocalCopyToShard(CitusCopyDestReceiver *copyDest, int64 shardId,\n\t\t\t\t\t\t\t\t   CopyOutState localCopyOutState);\nextern void FinishLocalCopyToFile(CopyOutState localFileCopyOutState,\n\t\t\t\t\t\t\t\t  FileCompat *fileCompat);\n\n#endif /* LOCAL_MULTI_COPY */\n"
  },
  {
    "path": "src/include/distributed/local_plan_cache.h",
    "content": "#ifndef LOCAL_PLAN_CACHE\n#define LOCAL_PLAN_CACHE\n\nextern bool IsLocalPlanCachingSupported(Job *currentJob,\n\t\t\t\t\t\t\t\t\t\tDistributedPlan *originalDistributedPlan);\nextern PlannedStmt * GetCachedLocalPlan(Task *task, DistributedPlan *distributedPlan);\nextern void CacheLocalPlanForShardQuery(Task *task,\n\t\t\t\t\t\t\t\t\t\tDistributedPlan *originalDistributedPlan,\n\t\t\t\t\t\t\t\t\t\tParamListInfo paramListInfo);\n\n#endif /* LOCAL_PLAN_CACHE */\n"
  },
  {
    "path": "src/include/distributed/locally_reserved_shared_connections.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * locally_reserved_shared_connection_stats.h\n *   Management of connection reservations in shard memory pool\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef LOCALLY_RESERVED_SHARED_CONNECTIONS_H_\n#define LOCALLY_RESERVED_SHARED_CONNECTIONS_H_\n\n#include \"distributed/connection_management.h\"\n\n\nextern void InitializeLocallyReservedSharedConnections(void);\nextern bool CanUseReservedConnection(const char *hostName, int nodePort,\n\t\t\t\t\t\t\t\t\t Oid userId, Oid databaseOid);\nextern void MarkReservedConnectionUsed(const char *hostName, int nodePort,\n\t\t\t\t\t\t\t\t\t   Oid userId, Oid databaseOid);\nextern void DeallocateReservedConnections(void);\nextern void EnsureConnectionPossibilityForRemotePrimaryNodes(void);\nextern bool TryConnectionPossibilityForLocalPrimaryNode(void);\nextern bool IsReservationPossible(void);\n\n#endif /* LOCALLY_RESERVED_SHARED_CONNECTIONS_H_ */\n"
  },
  {
    "path": "src/include/distributed/lock_graph.h",
    "content": "/*\n * lock_graph.h\n *\n * Data structures and functions for gathering lock graphs between\n * distributed transactions.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef LOCK_GRAPH_H\n#define LOCK_GRAPH_H\n\n\n#include \"postgres.h\"\n\n#include \"libpq-fe.h\"\n\n#include \"datatype/timestamp.h\"\n#include \"storage/lock.h\"\n\n#include \"distributed/backend_data.h\"\n\n\n/*\n * Describes an edge in a waiting-for graph of locks.  This isn't used for\n * deadlock-checking directly, but to gather the information necessary to\n * do so.\n *\n * The datatypes here are a bit looser than strictly necessary, because\n * they're transported as the return type from an SQL function.\n */\ntypedef struct WaitEdge\n{\n\tuint64 waitingGPid;\n\tint waitingPid;\n\tint waitingNodeId;\n\tint64 waitingTransactionNum;\n\tTimestampTz waitingTransactionStamp;\n\n\tuint64 blockingGPid;\n\tint blockingPid;\n\tint blockingNodeId;\n\tint64 blockingTransactionNum;\n\tTimestampTz blockingTransactionStamp;\n\n\t/* blocking transaction is also waiting on a lock */\n\tbool isBlockingXactWaiting;\n} WaitEdge;\n\n\n/*\n * WaitGraph represent a graph of wait edges as an adjacency list.\n */\ntypedef struct WaitGraph\n{\n\tint localNodeId;\n\tint allocatedSize;\n\tint edgeCount;\n\tWaitEdge *edges;\n} WaitGraph;\n\n\nextern WaitGraph * BuildGlobalWaitGraph(bool onlyDistributedTx);\nextern bool IsProcessWaitingForLock(PGPROC *proc);\nextern bool IsInDistributedTransaction(BackendData *backendData);\nextern TimestampTz ParseTimestampTzField(PGresult *result, int rowIndex, int colIndex);\nextern int64 ParseIntField(PGresult *result, int rowIndex, int colIndex);\n\n/* some utility function to parse results */\nextern int64 ParseIntField(PGresult *result, int rowIndex, int colIndex);\nextern bool ParseBoolField(PGresult *result, int rowIndex, int colIndex);\nextern TimestampTz ParseTimestampTzField(PGresult *result, int rowIndex, int colIndex);\n\n\n#endif /* LOCK_GRAPH_H */\n"
  },
  {
    "path": "src/include/distributed/log_utils.h",
    "content": "/*-------------------------------------------------------------------------\n * log_utils.h\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef LOG_UTILS_H\n#define LOG_UTILS_H\n\n\n#include \"utils/guc.h\"\n\n/* do not log */\n#define CITUS_LOG_LEVEL_OFF 0\n\n\nextern bool EnableUnsupportedFeatureMessages;\n\nextern bool IsLoggableLevel(int logLevel);\n\n#undef ereport\n\n#define ereport(elevel, rest) \\\n\t\tdo { \\\n\t\t\tint ereport_loglevel = elevel; \\\n\t\t\t(void) (ereport_loglevel); \\\n\t\t\tereport_domain(elevel, TEXTDOMAIN, rest); \\\n\t\t} while (0)\n\n#endif /* LOG_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/maintenanced.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * maintenanced.h\n *\t  Background worker run for each citus using database in a postgres\n *    cluster.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef MAINTENANCED_H\n#define MAINTENANCED_H\n\n/* collect statistics every 24 hours */\n#define STATS_COLLECTION_TIMEOUT_MILLIS (24 * 60 * 60 * 1000)\n\n/* if statistics collection fails, retry in 1 minute */\n#define STATS_COLLECTION_RETRY_TIMEOUT_MILLIS (60 * 1000)\n\n/* config variable for */\nextern double DistributedDeadlockDetectionTimeoutFactor;\nextern char *MainDb;\n\nextern void StopMaintenanceDaemon(Oid databaseId);\nextern void TriggerNodeMetadataSync(Oid databaseId);\nextern void InitializeMaintenanceDaemon(void);\nextern size_t MaintenanceDaemonShmemSize(void);\nextern void MaintenanceDaemonShmemInit(void);\nextern void InitializeMaintenanceDaemonBackend(void);\nextern void InitializeMaintenanceDaemonForMainDb(void);\nextern bool LockCitusExtension(void);\n\nextern PGDLLEXPORT void CitusMaintenanceDaemonMain(Datum main_arg);\n\n#endif /* MAINTENANCED_H */\n"
  },
  {
    "path": "src/include/distributed/memutils.h",
    "content": "/*\n * memutils.h\n *   utility functions to help with postgres' memory management primitives\n */\n\n#ifndef CITUS_MEMUTILS_H\n#define CITUS_MEMUTILS_H\n\n#include \"utils/palloc.h\"\n\n\n/*\n * EnsureReleaseResource is an abstraction on MemoryContextRegisterResetCallback that\n * allocates the space for the MemoryContextCallback and registers it to the current\n * MemoryContext, ensuring the call of callback with arg as its argument during either the\n * Reset of Delete of a MemoryContext.\n */\nstatic inline void\nEnsureReleaseResource(MemoryContextCallbackFunction callback, void *arg)\n{\n\tMemoryContextCallback *cb = MemoryContextAllocZero(CurrentMemoryContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   sizeof(MemoryContextCallback));\n\tcb->func = callback;\n\tcb->arg = arg;\n\tMemoryContextRegisterResetCallback(CurrentMemoryContext, cb);\n}\n\n\n#endif /*CITUS_MEMUTILS_H */\n"
  },
  {
    "path": "src/include/distributed/merge_executor.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * merge_executor.h\n *\n * Declarations for public functions and types related to executing\n * MERGE INTO ...  SQL commands.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#ifndef MERGE_EXECUTOR_H\n#define MERGE_EXECUTOR_H\n\nextern TupleTableSlot * NonPushableMergeCommandExecScan(CustomScanState *node);\n\n#endif /* MERGE_EXECUTOR_H */\n"
  },
  {
    "path": "src/include/distributed/merge_planner.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * merge_planner.h\n *\n * Declarations for public functions and types related to router planning.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef MERGE_PLANNER_H\n#define MERGE_PLANNER_H\n\n#include \"c.h\"\n\n#include \"nodes/parsenodes.h\"\n\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/multi_physical_planner.h\"\n\nextern DistributedPlan * CreateMergePlan(uint64 planId, Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t Query *query,\n\t\t\t\t\t\t\t\t\t\t PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t ParamListInfo boundParams);\nextern bool IsLocalTableModification(Oid targetRelationId, Query *query,\n\t\t\t\t\t\t\t\t\t uint64 shardId,\n\t\t\t\t\t\t\t\t\t RTEListProperties *rteProperties);\nextern void NonPushableMergeCommandExplainScan(CustomScanState *node, List *ancestors,\n\t\t\t\t\t\t\t\t\t\t\t   struct ExplainState *es);\nextern Var * FetchAndValidateInsertVarIfExists(Oid targetRelationId, Query *query);\nextern RangeTblEntry * ExtractMergeSourceRangeTableEntry(Query *query, bool joinSourceOk);\nextern FromExpr * GetMergeJoinTree(Query *mergeQuery);\n\n\n#endif /* MERGE_PLANNER_H */\n"
  },
  {
    "path": "src/include/distributed/metadata/dependency.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * dependency.c\n *    Functions to follow and record dependencies for objects to be\n *    created in the right order.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_DEPENDENCY_H\n#define CITUS_DEPENDENCY_H\n\n#include \"postgres.h\"\n\n#include \"catalog/objectaddress.h\"\n#include \"catalog/pg_depend.h\"\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/errormessage.h\"\n\ntypedef bool (*AddressPredicate)(const ObjectAddress *);\n\nextern List * GetUniqueDependenciesList(List *objectAddressesList);\nextern List * GetDependenciesForObject(const ObjectAddress *target);\nextern List * GetAllSupportedDependenciesForObject(const ObjectAddress *target);\nextern List * GetAllDependenciesForObject(const ObjectAddress *target);\nextern bool ErrorOrWarnIfAnyObjectHasUnsupportedDependency(List *objectAddresses);\nextern DeferredErrorMessage * DeferErrorIfAnyObjectHasUnsupportedDependency(const List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tobjectAddresses);\nextern List * GetAllCitusDependedDependenciesForObject(const ObjectAddress *target);\nextern List * OrderObjectAddressListInDependencyOrder(List *objectAddressList);\nextern bool SupportedDependencyByCitus(const ObjectAddress *address);\nextern List * GetPgDependTuplesForDependingObjects(Oid targetObjectClassId,\n\t\t\t\t\t\t\t\t\t\t\t\t   Oid targetObjectId);\nextern List * GetDependingViews(Oid relationId);\nextern Oid GetDependingView(Form_pg_depend pg_depend);\nextern List * FilterObjectAddressListByPredicate(List *objectAddressList,\n\t\t\t\t\t\t\t\t\t\t\t\t AddressPredicate predicate);\n\n#endif /* CITUS_DEPENDENCY_H */\n"
  },
  {
    "path": "src/include/distributed/metadata/distobject.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * distobject.h\n *\t  Declarations for functions to work with pg_dist_object\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_METADATA_DISTOBJECT_H\n#define CITUS_METADATA_DISTOBJECT_H\n\n#include \"postgres.h\"\n\n#include \"catalog/objectaddress.h\"\n\n#define INVALID_DISTRIBUTION_ARGUMENT_INDEX -1\n#define NO_FORCE_PUSHDOWN 0\n\nextern bool ObjectExists(const ObjectAddress *address);\nextern bool CitusExtensionObject(const ObjectAddress *objectAddress);\nextern bool IsAnyObjectDistributed(const List *addresses);\nextern bool IsAnyParentObjectDistributed(const List *addresses);\nextern bool ClusterHasDistributedFunctionWithDistArgument(void);\nextern void MarkObjectDistributed(const ObjectAddress *distAddress);\nextern void MarkObjectDistributedWithName(const ObjectAddress *distAddress, char *name,\n\t\t\t\t\t\t\t\t\t\t  bool useConnectionForLocalQuery,\n\t\t\t\t\t\t\t\t\t\t  char *connectionUser);\nextern void MarkObjectDistributedViaSuperUser(const ObjectAddress *distAddress);\nextern void MarkObjectDistributedLocally(const ObjectAddress *distAddress);\nextern void UnmarkObjectDistributed(const ObjectAddress *address);\nextern void UnmarkNodeWideObjectsDistributed(Node *node);\nextern bool IsTableOwnedByExtension(Oid relationId);\nextern bool ObjectAddressDependsOnExtension(const ObjectAddress *target);\nextern bool IsAnyObjectAddressOwnedByExtension(const List *targets,\n\t\t\t\t\t\t\t\t\t\t\t   ObjectAddress *extensionAddress);\nextern ObjectAddress * FirstExtensionWithSchema(Oid schemaId);\nextern bool IsObjectAddressOwnedByCitus(const ObjectAddress *objectAddress);\nextern ObjectAddress PgGetObjectAddress(char *ttype, ArrayType *namearr,\n\t\t\t\t\t\t\t\t\t\tArrayType *argsarr);\nextern List * GetDistributedObjectAddressList(void);\nextern RoleSpec * GetRoleSpecObjectForUser(Oid roleOid);\nextern void UpdateDistributedObjectColocationId(uint32 oldColocationId, uint32\n\t\t\t\t\t\t\t\t\t\t\t\tnewColocationId);\nextern List * DistributedFunctionList(void);\nextern List * DistributedSequenceList(void);\n#endif /* CITUS_METADATA_DISTOBJECT_H */\n"
  },
  {
    "path": "src/include/distributed/metadata/pg_dist_object.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_object.h\n *\t  definition of the system distributed objects relation (pg_dist_object).\n *\n * This table keeps metadata on all postgres objects that are distributed\n * to all the nodes in the network. Objects in this table should all be\n * present on all workers and kept in sync throughout their existance.\n * This also means that all nodes joining the network are assumed to\n * recreate all these objects.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_OBJECT_H\n#define PG_DIST_OBJECT_H\n\n\n/* ----------------\n *\t\tpg_dist_object definition.\n * ----------------\n */\ntypedef struct FormData_pg_dist_object\n{\n\tOid classid;      /* class of the distributed object */\n\tOid objid;        /* object id of the distributed object */\n\tint32 objsubid;   /* object sub id of the distributed object, eg. attnum */\n\n#ifdef CATALOG_VARLEN           /* variable-length fields start here */\n\ttext type;\n\ttext[] object_names;\n\ttext[] object_arguments;\n\n\tuint32 distribution_argument_index; /* only valid for distributed functions/procedures */\n\tuint32 colocationid;            /* only valid for distributed functions/procedures */\n\tboolean forced_pushdown;        /* only valid for distributed functions */\n#endif\n} FormData_pg_dist_object;\n\n/* ----------------\n *      Form_pg_dist_partitions corresponds to a pointer to a tuple with\n *      the format of pg_dist_partitions relation.\n * ----------------\n */\ntypedef FormData_pg_dist_object *Form_pg_dist_object;\n\n/* ----------------\n *      compiler constants for pg_dist_object\n * ----------------\n */\n#define Natts_pg_dist_object 9\n#define Anum_pg_dist_object_classid 1\n#define Anum_pg_dist_object_objid 2\n#define Anum_pg_dist_object_objsubid 3\n#define Anum_pg_dist_object_type 4\n#define Anum_pg_dist_object_object_names 5\n#define Anum_pg_dist_object_object_args 6\n#define Anum_pg_dist_object_distribution_argument_index 7\n#define Anum_pg_dist_object_colocationid 8\n#define Anum_pg_dist_object_force_delegation 9\n\nextern int GetForceDelegationAttrIndexInPgDistObject(TupleDesc tupleDesc);\n\n#endif /* PG_DIST_OBJECT_H */\n"
  },
  {
    "path": "src/include/distributed/metadata_cache.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * metadata_cache.h\n *\t  Executor support for Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef METADATA_CACHE_H\n#define METADATA_CACHE_H\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"utils/hsearch.h\"\n\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/pg_dist_partition.h\"\n#include \"distributed/worker_manager.h\"\n\nextern bool EnableVersionChecks;\n\n/* managed via guc.c */\ntypedef enum\n{\n\tUSE_SECONDARY_NODES_NEVER = 0,\n\tUSE_SECONDARY_NODES_ALWAYS = 1\n} ReadFromSecondariesType;\nextern int ReadFromSecondaries;\n\n\n/*\n * While upgrading pg_dist_local_group can be empty temporarily, in that\n * case we use GROUP_ID_UPGRADING as the local group id to communicate\n * this to other functions.\n */\n#define GROUP_ID_UPGRADING -2\n\n\n/*\n * Representation of a table's metadata that is frequently used for\n * distributed execution. Cached.\n */\ntypedef struct\n{\n\t/* lookup key - must be first. A pg_class.oid oid. */\n\tOid relationId;\n\n\t/*\n\t * Has an invalidation been received for this entry, requiring a rebuild\n\t * of the cache entry?\n\t */\n\tbool isValid;\n\n\tbool hasUninitializedShardInterval;\n\tbool hasUniformHashDistribution; /* valid for hash partitioned tables */\n\tbool hasOverlappingShardInterval;\n\n\t/* pg_dist_partition metadata for this table */\n\tchar *partitionKeyString;\n\tVar *partitionColumn;\n\tchar partitionMethod;\n\tuint32 colocationId;\n\tchar replicationModel;\n\tbool autoConverted; /* table auto-added to metadata, valid for citus local tables */\n\n\t/* pg_dist_shard metadata (variable-length ShardInterval array) for this table */\n\tint shardIntervalArrayLength;\n\tShardInterval **sortedShardIntervalArray;\n\n\t/* comparator for partition column's type, NULL if DISTRIBUTE_BY_NONE */\n\tFmgrInfo *shardColumnCompareFunction;\n\n\t/*\n\t * Comparator for partition interval type (different from\n\t * shardColumnCompareFunction if hash-partitioned), NULL if\n\t * DISTRIBUTE_BY_NONE.\n\t */\n\tFmgrInfo *shardIntervalCompareFunction;\n\tFmgrInfo *hashFunction; /* NULL if table is not distributed by hash */\n\n\t/*\n\t * The following two lists consists of relationIds that this distributed\n\t * relation has a foreign key to (e.g., referencedRelationsViaForeignKey) or\n\t * other relations has a foreign key to this relation (e.g.,\n\t * referencingRelationsViaForeignKey).\n\t *\n\t * Note that we're keeping all transitive foreign key references as well\n\t * such that if relation A refers to B, and B refers to C, we keep A and B\n\t * in C's referencingRelationsViaForeignKey.\n\t */\n\tList *referencedRelationsViaForeignKey;\n\tList *referencingRelationsViaForeignKey;\n\n\t/* pg_dist_placement metadata */\n\tGroupShardPlacement **arrayOfPlacementArrays;\n\tint *arrayOfPlacementArrayLengths;\n} CitusTableCacheEntry;\n\ntypedef struct DistObjectCacheEntryKey\n{\n\tOid classid;\n\tOid objid;\n\tint32 objsubid;\n} DistObjectCacheEntryKey;\n\ntypedef struct DistObjectCacheEntry\n{\n\t/* lookup key - must be first. */\n\tDistObjectCacheEntryKey key;\n\n\tbool isValid;\n\tbool isDistributed;\n\n\tint distributionArgIndex;\n\tint colocationId;\n\tbool forceDelegation;\n} DistObjectCacheEntry;\n\ntypedef enum\n{\n\tHASH_DISTRIBUTED,\n\tAPPEND_DISTRIBUTED,\n\tRANGE_DISTRIBUTED,\n\tSINGLE_SHARD_DISTRIBUTED,\n\n\t/* hash, range or append distributed table */\n\tDISTRIBUTED_TABLE,\n\n\t/* hash- or range-distributed table */\n\tSTRICTLY_PARTITIONED_DISTRIBUTED_TABLE,\n\n\tREFERENCE_TABLE,\n\tCITUS_LOCAL_TABLE,\n\n\tANY_CITUS_TABLE_TYPE\n} CitusTableType;\n\nvoid InvalidateDistRelationCacheCallback(Datum argument, Oid relationId);\n\nextern List * AllCitusTableIds(void);\nextern bool IsCitusTableType(Oid relationId, CitusTableType tableType);\nextern CitusTableType GetCitusTableType(CitusTableCacheEntry *tableEntry);\nextern bool IsCitusTableTypeCacheEntry(CitusTableCacheEntry *tableEtnry,\n\t\t\t\t\t\t\t\t\t   CitusTableType tableType);\nextern bool IsFirstShard(CitusTableCacheEntry *tableEntry, uint64 shardId);\nbool HasDistributionKey(Oid relationId);\nbool HasDistributionKeyCacheEntry(CitusTableCacheEntry *tableEntry);\nextern char * GetTableTypeName(Oid tableId);\n\nextern void SetCreateCitusTransactionLevel(int val);\nextern int GetCitusCreationLevel(void);\nextern bool IsCitusTable(Oid relationId);\nextern bool IsCitusTableRangeVar(RangeVar *rangeVar, LOCKMODE lockMode, bool missingOk);\nextern bool IsCitusTableViaCatalog(Oid relationId);\nextern char PgDistPartitionViaCatalog(Oid relationId);\nextern List * LookupDistShardTuples(Oid relationId);\nextern char PartitionMethodViaCatalog(Oid relationId);\nextern Var * PartitionColumnViaCatalog(Oid relationId);\nextern uint32 ColocationIdViaCatalog(Oid relationId);\nbool IsReferenceTableByDistParams(char partitionMethod, char replicationModel);\nextern bool IsCitusLocalTableByDistParams(char partitionMethod, char replicationModel,\n\t\t\t\t\t\t\t\t\t\t  uint32 colocationId);\nextern bool IsSingleShardTableByDistParams(char partitionMethod, char replicationModel,\n\t\t\t\t\t\t\t\t\t\t   uint32 colocationId);\nextern List * CitusTableList(void);\nextern ShardInterval * LoadShardInterval(uint64 shardId);\nextern bool ShardExists(uint64 shardId);\nextern Oid RelationIdForShard(uint64 shardId);\nextern bool ReferenceTableShardId(uint64 shardId);\nextern bool DistributedTableShardId(uint64 shardId);\nextern ShardPlacement * ShardPlacementOnGroupIncludingOrphanedPlacements(int32 groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t uint64 shardId);\nextern ShardPlacement * ActiveShardPlacementOnGroup(int32 groupId, uint64 shardId);\nextern GroupShardPlacement * LoadGroupShardPlacement(uint64 shardId, uint64 placementId);\nextern ShardPlacement * LoadShardPlacement(uint64 shardId, uint64 placementId);\nextern CitusTableCacheEntry * GetCitusTableCacheEntry(Oid distributedRelationId);\nextern CitusTableCacheEntry * LookupCitusTableCacheEntry(Oid relationId);\nextern DistObjectCacheEntry * LookupDistObjectCacheEntry(Oid classid, Oid objid, int32\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t objsubid);\nextern int32 GetLocalGroupId(void);\nextern int32 GetLocalNodeId(void);\nextern void CitusTableCacheFlushInvalidatedEntries(void);\nextern Oid LookupShardRelationFromCatalog(int64 shardId, bool missing_ok);\nextern List * ShardPlacementList(uint64 shardId);\nextern void CitusInvalidateRelcacheByRelid(Oid relationId);\nextern void CitusInvalidateRelcacheByShardId(int64 shardId);\nextern void InvalidateForeignKeyGraph(void);\nextern void FlushDistTableCache(void);\nextern void InvalidateMetadataSystemCache(void);\nextern List * CitusTableTypeIdList(CitusTableType citusTableType);\nextern Datum DistNodeMetadata(void);\nextern bool HasUniformHashDistribution(ShardInterval **shardIntervalArray,\n\t\t\t\t\t\t\t\t\t   int shardIntervalArrayLength);\nextern bool HasUninitializedShardInterval(ShardInterval **sortedShardIntervalArray,\n\t\t\t\t\t\t\t\t\t\t  int shardCount);\nextern bool HasOverlappingShardInterval(ShardInterval **shardIntervalArray,\n\t\t\t\t\t\t\t\t\t\tint shardIntervalArrayLength,\n\t\t\t\t\t\t\t\t\t\tOid shardIntervalCollation,\n\t\t\t\t\t\t\t\t\t\tFmgrInfo *shardIntervalSortCompareFunction);\n\nextern ShardPlacement * ShardPlacementForFunctionColocatedWithSingleShardTable(\n\tCitusTableCacheEntry *cacheEntry);\nextern ShardPlacement * ShardPlacementForFunctionColocatedWithReferenceTable(\n\tCitusTableCacheEntry *cacheEntry);\nextern ShardPlacement * ShardPlacementForFunctionColocatedWithDistTable(\n\tDistObjectCacheEntry *procedure, List *argumentList, Var *partitionColumn,\n\tCitusTableCacheEntry\n\t*cacheEntry,\n\tPlannedStmt *\n\tplan);\nextern bool CitusHasBeenLoaded(void);\nextern bool CheckCitusVersion(int elevel);\nextern bool CheckAvailableVersion(int elevel);\nextern bool InstalledAndAvailableVersionsSame(void);\nextern bool MajorVersionsCompatible(char *leftVersion, char *rightVersion);\nextern bool MinorVersionsCompatibleRelaxed(char *leftVersion, char *rightVersion);\nextern void ErrorIfInconsistentShardIntervals(CitusTableCacheEntry *cacheEntry);\nextern void EnsureModificationsCanRun(void);\nextern void EnsureModificationsCanRunOnRelation(Oid relationId);\nextern char LookupDistributionMethod(Oid distributionMethodOid);\nextern bool RelationExists(Oid relationId);\nextern ShardInterval * TupleToShardInterval(HeapTuple heapTuple,\n\t\t\t\t\t\t\t\t\t\t\tTupleDesc tupleDescriptor, Oid intervalTypeId,\n\t\t\t\t\t\t\t\t\t\t\tint32 intervalTypeMod);\n\n/* access WorkerNodeHash */\nextern bool HasAnyNodes(void);\nextern HTAB * GetWorkerNodeHash(void);\nextern WorkerNode * LookupNodeByNodeId(uint32 nodeId);\nextern WorkerNode * LookupNodeByNodeIdOrError(uint32 nodeId);\nextern WorkerNode * LookupNodeForGroup(int32 groupId);\n\n/* namespace oids */\nextern Oid CitusCatalogNamespaceId(void);\n\n/* relation oids */\nextern Oid DistCleanupRelationId(void);\nextern Oid DistColocationRelationId(void);\nextern Oid DistColocationConfigurationIndexId(void);\nextern Oid DistPartitionRelationId(void);\nextern Oid DistShardRelationId(void);\nextern Oid DistPlacementRelationId(void);\nextern Oid DistNodeRelationId(void);\nextern Oid DistBackgroundJobRelationId(void);\nextern Oid DistBackgroundTaskRelationId(void);\nextern Oid DistRebalanceStrategyRelationId(void);\nextern Oid DistLocalGroupIdRelationId(void);\nextern Oid DistObjectRelationId(void);\nextern Oid DistEnabledCustomAggregatesId(void);\nextern Oid DistTenantSchemaRelationId(void);\n\n/* index oids */\nextern Oid DistNodeNodeIdIndexId(void);\nextern Oid DistPartitionLogicalRelidIndexId(void);\nextern Oid DistPartitionColocationidIndexId(void);\nextern Oid DistBackgroundJobPKeyIndexId(void);\nextern Oid DistBackgroundTaskPKeyIndexId(void);\nextern Oid DistBackgroundTaskJobIdTaskIdIndexId(void);\nextern Oid DistBackgroundTaskStatusTaskIdIndexId(void);\nextern Oid DistBackgroundTaskDependRelationId(void);\nextern Oid DistBackgroundTaskDependTaskIdIndexId(void);\nextern Oid DistBackgroundTaskDependDependsOnIndexId(void);\nextern Oid DistShardLogicalRelidIndexId(void);\nextern Oid DistShardShardidIndexId(void);\nextern Oid DistPlacementShardidIndexId(void);\nextern Oid DistPlacementPlacementidIndexId(void);\nextern Oid DistColocationIndexId(void);\nextern Oid DistTransactionRelationId(void);\nextern Oid DistTransactionGroupIndexId(void);\nextern Oid DistPlacementGroupidIndexId(void);\nextern Oid DistObjectPrimaryKeyIndexId(void);\nextern Oid DistCleanupPrimaryKeyIndexId(void);\nextern Oid DistTenantSchemaPrimaryKeyIndexId(void);\nextern Oid DistTenantSchemaUniqueColocationIdIndexId(void);\n\n/* sequence oids */\nextern Oid DistBackgroundJobJobIdSequenceId(void);\nextern Oid DistBackgroundTaskTaskIdSequenceId(void);\nextern Oid DistClockLogicalSequenceId(void);\n\n/* type oids */\nextern Oid LookupTypeOid(char *schemaNameSting, char *typeNameString);\nextern Oid CitusCopyFormatTypeId(void);\n\n/* function oids */\nextern Oid CitusReadIntermediateResultFuncId(void);\nOid CitusReadIntermediateResultArrayFuncId(void);\nextern Oid CitusExtraDataContainerFuncId(void);\nextern Oid CitusAnyValueFunctionId(void);\nextern Oid CitusTextSendAsJsonbFunctionId(void);\nextern Oid TextOutFunctionId(void);\nextern Oid RelationIsAKnownShardFuncId(void);\nextern Oid JsonbExtractPathFuncId(void);\nextern Oid JsonbExtractPathTextFuncId(void);\nextern Oid CitusDependentObjectFuncId(void);\n\n/* enum oids */\nextern Oid PrimaryNodeRoleId(void);\nextern Oid SecondaryNodeRoleId(void);\nextern Oid UnavailableNodeRoleId(void);\nextern Oid CitusCopyFormatTypeId(void);\nextern Oid TextCopyFormatId(void);\nextern Oid BinaryCopyFormatId(void);\nextern Oid CitusJobStatusScheduledId(void);\nextern Oid CitusJobStatusRunningId(void);\nextern Oid CitusJobStatusCancellingId(void);\nextern Oid CitusJobStatusFinishedId(void);\nextern Oid CitusJobStatusCancelledId(void);\nextern Oid CitusJobStatusFailedId(void);\nextern Oid CitusJobStatusFailingId(void);\nextern Oid CitusTaskStatusBlockedId(void);\nextern Oid CitusTaskStatusRunnableId(void);\nextern Oid CitusTaskStatusRunningId(void);\nextern Oid CitusTaskStatusDoneId(void);\nextern Oid CitusTaskStatusErrorId(void);\nextern Oid CitusTaskStatusUnscheduledId(void);\nextern Oid CitusTaskStatusCancelledId(void);\nextern Oid CitusTaskStatusCancellingId(void);\n\n/* user related functions */\nextern Oid CitusExtensionOwner(void);\nextern char * CitusExtensionOwnerName(void);\nextern char * CurrentUserName(void);\nextern const char * CurrentDatabaseName(void);\n\n/* connection-related functions */\nextern char * GetAuthinfoViaCatalog(const char *roleName, int64 nodeId);\nextern char * GetPoolinfoViaCatalog(int32 nodeId);\n\n#endif /* METADATA_CACHE_H */\n"
  },
  {
    "path": "src/include/distributed/metadata_sync.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * metadata_sync.h\n *\t  Type and function declarations used to sync metadata across all\n *\t  workers.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef METADATA_SYNC_H\n#define METADATA_SYNC_H\n\n\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/commands/utility_hook.h\"\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/metadata_cache.h\"\n\n/* managed via guc.c */\ntypedef enum\n{\n\tMETADATA_SYNC_TRANSACTIONAL = 0,\n\tMETADATA_SYNC_NON_TRANSACTIONAL = 1\n} MetadataSyncTransactionMode;\n\n/* config variables */\nextern int MetadataSyncInterval;\nextern int MetadataSyncRetryInterval;\nextern int MetadataSyncTransMode;\n\n/*\n * MetadataSyncContext is used throughout metadata sync.\n */\ntypedef struct MetadataSyncContext\n{\n\tList *activatedWorkerNodeList; /* activated worker nodes */\n\tList *activatedWorkerBareConnections; /* bare connections to activated nodes */\n\tMemoryContext context; /* memory context for all allocations */\n\tMetadataSyncTransactionMode transactionMode; /* transaction mode for the sync */\n\tbool collectCommands; /* if we collect commands instead of sending and resetting */\n\tList *collectedCommands; /* collected commands. (NIL if collectCommands == false) */\n\tbool nodesAddedInSameTransaction; /* if the nodes are added just before activation */\n} MetadataSyncContext;\n\ntypedef enum\n{\n\tNODE_METADATA_SYNC_SUCCESS = 0,\n\tNODE_METADATA_SYNC_FAILED_LOCK = 1,\n\tNODE_METADATA_SYNC_FAILED_SYNC = 2\n} NodeMetadataSyncResult;\n\n/*\n * Information about dependent sequences. We do not have the\n * dependent relationId as no caller needs. But, could be added\n * here if needed.\n */\ntypedef struct SequenceInfo\n{\n\tOid sequenceOid;\n\tint attributeNumber;\n\n\t/*\n\t * true for nexval(seq) -- which also includes serials\n\t * false when only OWNED BY col\n\t */\n\tbool isNextValDefault;\n} SequenceInfo;\n\n\n/* Functions declarations for metadata syncing */\nextern void citus_internal_add_placement_metadata_internal(int64 shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   int64 shardLength,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   int32 groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   int64 placementId);\nextern void SyncCitusTableMetadata(Oid relationId);\nextern void EnsureSequentialModeMetadataOperations(void);\nextern bool ClusterHasKnownMetadataWorkers(void);\nextern char * LocalGroupIdUpdateCommand(int32 groupId);\nextern bool ShouldSyncUserCommandForObject(ObjectAddress objectAddress);\nextern bool ShouldSyncTableMetadata(Oid relationId);\nextern bool ShouldSyncTableMetadataViaCatalog(Oid relationId);\nextern Oid FetchRelationIdFromPgPartitionHeapTuple(HeapTuple heapTuple,\n\t\t\t\t\t\t\t\t\t\t\t\t   TupleDesc tupleDesc);\nextern bool ShouldSyncSequenceMetadata(Oid relationId);\nextern List * NodeMetadataCreateCommands(void);\nextern List * CitusTableMetadataCreateCommandList(Oid relationId);\nextern List * NodeMetadataDropCommands(void);\nextern char * MarkObjectsDistributedCreateCommand(List *addresses,\n\t\t\t\t\t\t\t\t\t\t\t\t  List *names,\n\t\t\t\t\t\t\t\t\t\t\t\t  List *distributionArgumentIndexes,\n\t\t\t\t\t\t\t\t\t\t\t\t  List *colocationIds,\n\t\t\t\t\t\t\t\t\t\t\t\t  List *forceDelegations);\nextern char * DistributionCreateCommand(CitusTableCacheEntry *cacheEntry);\nextern char * DistributionDeleteCommand(const char *schemaName,\n\t\t\t\t\t\t\t\t\t\tconst char *tableName);\nextern char * DistributionDeleteMetadataCommand(Oid relationId);\nextern char * TableOwnerResetCommand(Oid distributedRelationId);\nextern char * NodeListInsertCommand(List *workerNodeList);\nchar * NodeListIdempotentInsertCommand(List *workerNodeList);\nextern List * ShardListInsertCommand(List *shardIntervalList);\nextern List * ShardDeleteCommandList(ShardInterval *shardInterval);\nextern char * NodeDeleteCommand(uint32 nodeId);\nextern char * NodeStateUpdateCommand(uint32 nodeId, bool isActive);\nextern char * ShouldHaveShardsUpdateCommand(uint32 nodeId, bool shouldHaveShards);\nextern char * ColocationIdUpdateCommand(Oid relationId, uint32 colocationId);\nextern char * CreateSchemaDDLCommand(Oid schemaId);\nextern List * GrantOnSchemaDDLCommands(Oid schemaId);\nextern List * GrantOnFunctionDDLCommands(Oid functionOid);\nextern List * GrantOnDatabaseDDLCommands(Oid databaseOid);\nextern List * GrantOnForeignServerDDLCommands(Oid serverId);\nextern List * GenerateGrantOnForeignServerQueriesFromAclItem(Oid serverId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t AclItem *aclItem);\nextern List * GenerateGrantOnFDWQueriesFromAclItem(Oid serverId, AclItem *aclItem);\nextern char * PlacementUpsertCommand(uint64 shardId, uint64 placementId,\n\t\t\t\t\t\t\t\t\t uint64 shardLength, int32 groupId);\nextern TableDDLCommand * TruncateTriggerCreateCommand(Oid relationId);\nextern void CreateInterTableRelationshipOfRelationOnWorkers(Oid relationId);\nextern List * InterTableRelationshipOfRelationCommandList(Oid relationId);\nextern List * DetachPartitionCommandList(void);\nextern void SyncNodeMetadataToNodes(void);\nextern BackgroundWorkerHandle * SpawnSyncNodeMetadataToNodes(Oid database, Oid owner);\nextern PGDLLEXPORT void SyncNodeMetadataToNodesMain(Datum main_arg);\nextern void SignalMetadataSyncDaemon(Oid database, int sig);\nextern bool ShouldInitiateMetadataSync(bool *lockFailure);\nextern List * SequenceDependencyCommandList(Oid relationId);\nextern List * IdentitySequenceDependencyCommandList(Oid targetRelationId);\n\nextern List * DDLCommandsForSequence(Oid sequenceOid, char *ownerName);\nextern List * GetSequencesFromAttrDef(Oid attrdefOid);\nextern List * GetAttrDefsFromSequence(Oid seqOid);\nextern void GetDependentSequencesWithRelation(Oid relationId, List **seqInfoList,\n\t\t\t\t\t\t\t\t\t\t\t  AttrNumber attnum, char depType);\nextern List * GetDependentRelationsWithSequence(Oid seqId, char depType);\nextern List * GetDependentFunctionsWithRelation(Oid relationId);\nextern Oid GetAttributeTypeOid(Oid relationId, AttrNumber attnum);\nextern void SetLocalEnableMetadataSync(bool state);\nextern void SyncNewColocationGroupToNodes(uint32 colocationId, int shardCount,\n\t\t\t\t\t\t\t\t\t\t  int replicationFactor,\n\t\t\t\t\t\t\t\t\t\t  Oid distributionColumType,\n\t\t\t\t\t\t\t\t\t\t  Oid distributionColumnCollation);\nextern void SyncDeleteColocationGroupToNodes(uint32 colocationId);\nextern char * TenantSchemaInsertCommand(Oid schemaId, uint32 colocationId);\nextern char * TenantSchemaDeleteCommand(char *schemaName);\nextern char * UpdateNoneDistTableMetadataCommand(Oid relationId, char replicationModel,\n\t\t\t\t\t\t\t\t\t\t\t\t uint32 colocationId, bool autoConverted);\nextern char * AddPlacementMetadataCommand(uint64 shardId, uint64 placementId,\n\t\t\t\t\t\t\t\t\t\t  uint64 shardLength, int32 groupId);\nextern char * DeletePlacementMetadataCommand(uint64 placementId);\n\nextern MetadataSyncContext * CreateMetadataSyncContext(List *nodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   bool collectCommands,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   bool nodesAddedInSameTransaction);\nextern void EstablishAndSetMetadataSyncBareConnections(MetadataSyncContext *context);\nextern void SetMetadataSyncNodesFromNodeList(MetadataSyncContext *context,\n\t\t\t\t\t\t\t\t\t\t\t List *nodeList);\nextern void ResetMetadataSyncMemoryContext(MetadataSyncContext *context);\nextern bool MetadataSyncCollectsCommands(MetadataSyncContext *context);\nextern void SendOrCollectCommandListToActivatedNodes(MetadataSyncContext *context,\n\t\t\t\t\t\t\t\t\t\t\t\t\t List *commands);\nextern void SendOrCollectCommandListToMetadataNodes(MetadataSyncContext *context,\n\t\t\t\t\t\t\t\t\t\t\t\t\tList *commands);\nextern void SendOrCollectCommandListToSingleNode(MetadataSyncContext *context,\n\t\t\t\t\t\t\t\t\t\t\t\t List *commands, int nodeIdx);\n\nextern void ActivateNodeList(MetadataSyncContext *context);\n\nextern char * WorkerDropAllShellTablesCommand(bool singleTransaction);\nextern char * WorkerDropSequenceDependencyCommand(Oid relationId);\n\nextern void SyncDistributedObjects(MetadataSyncContext *context);\nextern void SendNodeWideObjectsSyncCommands(MetadataSyncContext *context);\nextern void SendShellTableDeletionCommands(MetadataSyncContext *context);\nextern void SendMetadataDeletionCommands(MetadataSyncContext *context);\nextern void SendColocationMetadataCommands(MetadataSyncContext *context);\nextern void SendTenantSchemaMetadataCommands(MetadataSyncContext *context);\nextern void SendDependencyCreationCommands(MetadataSyncContext *context);\nextern void SendDistTableMetadataCommands(MetadataSyncContext *context);\nextern void SendDistObjectCommands(MetadataSyncContext *context);\nextern void SendInterTableRelationshipCommands(MetadataSyncContext *context);\n\n#define DELETE_ALL_NODES \"DELETE FROM pg_dist_node\"\n#define DELETE_ALL_PLACEMENTS \"DELETE FROM pg_dist_placement\"\n#define DELETE_ALL_SHARDS \"DELETE FROM pg_dist_shard\"\n#define DELETE_ALL_DISTRIBUTED_OBJECTS \"DELETE FROM pg_catalog.pg_dist_object\"\n#define DELETE_ALL_PARTITIONS \"DELETE FROM pg_dist_partition\"\n#define DELETE_ALL_COLOCATION \"DELETE FROM pg_catalog.pg_dist_colocation\"\n#define DELETE_ALL_TENANT_SCHEMAS \"DELETE FROM pg_catalog.pg_dist_schema\"\n#define WORKER_DROP_ALL_SHELL_TABLES \\\n\t\t\"CALL pg_catalog.worker_drop_all_shell_tables(%s)\"\n#define CITUS_INTERNAL_MARK_NODE_NOT_SYNCED \\\n\t\t\"SELECT citus_internal.mark_node_not_synced(%d, %d)\"\n\n#define REMOVE_ALL_CITUS_TABLES_COMMAND \\\n\t\t\"SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition\"\n#define BREAK_ALL_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND \\\n\t\t\"SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition\"\n#define BREAK_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND \\\n\t\t\"SELECT pg_catalog.worker_drop_sequence_dependency(%s);\"\n\n#define DISABLE_DDL_PROPAGATION \"SET citus.enable_ddl_propagation TO 'off'\"\n#define ENABLE_DDL_PROPAGATION \"SET citus.enable_ddl_propagation TO 'on'\"\n#define DISABLE_METADATA_SYNC \"SET citus.enable_metadata_sync TO 'off'\"\n#define ENABLE_METADATA_SYNC \"SET citus.enable_metadata_sync TO 'on'\"\n#define WORKER_APPLY_SEQUENCE_COMMAND \"SELECT worker_apply_sequence_command (%s,%s)\"\n#define UPSERT_PLACEMENT \\\n\t\t\"INSERT INTO pg_dist_placement \" \\\n\t\t\"(shardid, shardstate, shardlength, \" \\\n\t\t\"groupid, placementid) \" \\\n\t\t\"VALUES (\" UINT64_FORMAT \", 1, \" UINT64_FORMAT \\\n\t\t\", %d, \" UINT64_FORMAT \\\n\t\t\") \" \\\n\t\t\"ON CONFLICT (shardid, groupid) DO UPDATE SET \" \\\n\t\t\"shardstate = EXCLUDED.shardstate, \" \\\n\t\t\"shardlength = EXCLUDED.shardlength, \" \\\n\t\t\"placementid = EXCLUDED.placementid\"\n#define METADATA_SYNC_CHANNEL \"metadata_sync\"\n\n#define WORKER_ADJUST_IDENTITY_COLUMN_SEQ_RANGES \\\n\t\t\"SELECT pg_catalog.worker_adjust_identity_column_seq_ranges(%s)\"\n\n/* controlled via GUC */\nextern char *EnableManualMetadataChangesForUser;\nextern bool EnableMetadataSync;\n\n#endif /* METADATA_SYNC_H */\n"
  },
  {
    "path": "src/include/distributed/metadata_utility.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * metadata_utility.h\n *\t  Type and function declarations used for reading and modifying\n *    coordinator node's metadata.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef METADATA_UTILITY_H\n#define METADATA_UTILITY_H\n\n#include \"postgres.h\"\n\n#include \"access/heapam.h\"\n#include \"access/htup.h\"\n#include \"access/tupdesc.h\"\n#include \"catalog/indexing.h\"\n#include \"catalog/objectaddress.h\"\n#include \"utils/acl.h\"\n#include \"utils/relcache.h\"\n\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/connection_management.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/relay_utility.h\"\n#include \"distributed/worker_manager.h\"\n\n\n/* total number of hash tokens (2^32) */\n#define HASH_TOKEN_COUNT INT64CONST(4294967296)\n#define SELECT_TRUE_QUERY \"SELECT TRUE FROM %s LIMIT 1\"\n#define PG_TABLE_SIZE_FUNCTION \"pg_table_size(%s)\"\n#define PG_RELATION_SIZE_FUNCTION \"pg_relation_size(%s)\"\n#define PG_TOTAL_RELATION_SIZE_FUNCTION \"pg_total_relation_size(%s)\"\n#define WORKER_PARTITIONED_TABLE_SIZE_FUNCTION \"worker_partitioned_table_size(%s)\"\n#define WORKER_PARTITIONED_RELATION_SIZE_FUNCTION \"worker_partitioned_relation_size(%s)\"\n#define WORKER_PARTITIONED_RELATION_TOTAL_SIZE_FUNCTION \\\n\t\t\"worker_partitioned_relation_total_size(%s)\"\n\n#define SHARD_SIZES_COLUMN_COUNT (2)\n\n/*\n * Flag to keep track of whether the process is currently in a function converting the\n * type of the table. Since it only affects the level of the log shown while dropping/\n * recreating table within the table type conversion, rollbacking to the savepoint hasn't\n * been implemented for the sake of simplicity. If you are planning to use that flag for\n * any other purpose, please consider implementing that.\n */\nextern bool InTableTypeConversionFunctionCall;\n\n/* In-memory representation of a typed tuple in pg_dist_shard. */\ntypedef struct ShardInterval\n{\n\tCitusNode type;\n\tOid relationId;\n\tchar storageType;\n\tOid valueTypeId;    /* min/max value datum's typeId */\n\tint valueTypeLen;   /* min/max value datum's typelen */\n\tbool valueByVal;    /* min/max value datum's byval */\n\tbool minValueExists;\n\tbool maxValueExists;\n\tDatum minValue;     /* a shard's typed min value datum */\n\tDatum maxValue;     /* a shard's typed max value datum */\n\tuint64 shardId;\n\tint shardIndex;\n} ShardInterval;\n\n\n/* In-memory representation of a tuple in pg_dist_placement. */\ntypedef struct GroupShardPlacement\n{\n\tCitusNode type;\n\tuint64 placementId;     /* sequence that implies this placement creation order */\n\tuint64 shardId;\n\tuint64 shardLength;\n\tint32 groupId;\n} GroupShardPlacement;\n\n\n/* A GroupShardPlacement which has had some extra data resolved */\ntypedef struct ShardPlacement\n{\n\t/*\n\t * careful, the rest of the code assumes this exactly matches GroupShardPlacement\n\t */\n\tCitusNode type;\n\tuint64 placementId;\n\tuint64 shardId;\n\tuint64 shardLength;\n\tint32 groupId;\n\n\t/* the rest of the fields aren't from pg_dist_placement */\n\tchar *nodeName;\n\tuint32 nodePort;\n\tuint32 nodeId;\n\tchar partitionMethod;\n\tuint32 colocationGroupId;\n\tuint32 representativeValue;\n} ShardPlacement;\n\n\ntypedef enum CascadeToColocatedOption\n{\n\tCASCADE_TO_COLOCATED_UNSPECIFIED,\n\tCASCADE_TO_COLOCATED_YES,\n\tCASCADE_TO_COLOCATED_NO,\n\tCASCADE_TO_COLOCATED_NO_ALREADY_CASCADED\n}CascadeToColocatedOption;\n\n/*\n * TableConversionParameters are the parameters that are given to\n * table conversion UDFs: undistribute_table, alter_distributed_table,\n * alter_table_set_access_method.\n *\n * When passing a TableConversionParameters object to one of the table\n * conversion functions some of the parameters needs to be set:\n * UndistributeTable: relationId\n * AlterDistributedTable: relationId, distributionColumn, shardCountIsNull,\n * shardCount, colocateWith, cascadeToColocated\n * AlterTableSetAccessMethod: relationId, accessMethod\n *\n * conversionType parameter will be automatically set by the function.\n *\n * TableConversionState objects can be created using TableConversionParameters\n * objects with CreateTableConversion function.\n */\ntypedef struct TableConversionParameters\n{\n\t/*\n\t * Determines type of conversion: UNDISTRIBUTE_TABLE,\n\t * ALTER_DISTRIBUTED_TABLE, ALTER_TABLE_SET_ACCESS_METHOD.\n\t */\n\tchar conversionType;\n\n\t/* Oid of the table to do conversion on */\n\tOid relationId;\n\n\t/*\n\t * Options to do conversions on the table\n\t * distributionColumn is the name of the new distribution column,\n\t * shardCountIsNull is if the shardCount variable is not given\n\t * shardCount is the new shard count,\n\t * colocateWith is the name of the table to colocate with, 'none', or\n\t * 'default'\n\t * accessMethod is the name of the new accessMethod for the table\n\t */\n\tchar *distributionColumn;\n\tbool shardCountIsNull;\n\tint shardCount;\n\tchar *colocateWith;\n\tchar *accessMethod;\n\n\t/*\n\t * cascadeToColocated determines whether the shardCount and\n\t * colocateWith will be cascaded to the currently colocated tables\n\t */\n\tCascadeToColocatedOption cascadeToColocated;\n\n\t/*\n\t * cascadeViaForeignKeys determines if the conversion operation\n\t * will be cascaded to the graph connected with foreign keys\n\t * to the table\n\t */\n\tbool cascadeViaForeignKeys;\n\n\t/*\n\t * suppressNoticeMessages determines if we want to suppress NOTICE\n\t * messages that we explicitly issue\n\t */\n\tbool suppressNoticeMessages;\n\n\t/*\n\t * bypassTenantCheck skips tenant table checks to allow some internal\n\t * operations which are normally disallowed\n\t */\n\tbool bypassTenantCheck;\n} TableConversionParameters;\n\ntypedef struct TableConversionReturn\n{\n\t/*\n\t * commands to create foreign keys for the table\n\t *\n\t * When the table conversion is cascaded we can recreate\n\t * some of the foreign keys of the cascaded tables. So with this\n\t * list we can return it to the initial conversion operation so\n\t * foreign keys can be created after every colocated table is\n\t * converted.\n\t */\n\tList *foreignKeyCommands;\n}TableConversionReturn;\n\n\n/*\n * Size query types for PG and Citus\n * For difference details, please see:\n * https://www.postgresql.org/docs/13/functions-admin.html#FUNCTIONS-ADMIN-DBSIZE\n */\ntypedef enum SizeQueryType\n{\n\tRELATION_SIZE, /* pg_relation_size() */\n\tTOTAL_RELATION_SIZE, /* pg_total_relation_size() */\n\tTABLE_SIZE /* pg_table_size() */\n} SizeQueryType;\n\n\ntypedef enum\n{\n\tCOLOCATE_WITH_TABLE_LIKE_OPT,\n\tCOLOCATE_WITH_COLOCATION_ID\n} ColocationParamType;\n\n/*\n * Param used to specify the colocation target of a distributed table. It can\n * be either a table name or a colocation id.\n *\n * When colocationParamType is COLOCATE_WITH_COLOCATION_ID, colocationId is\n * expected to be a valid colocation id. When colocationParamType is set to\n * COLOCATE_WITH_TABLE_LIKE_OPT, colocateWithTableName is expected to\n * be a valid table name, \"default\" or \"none\".\n *\n * Among the functions used to create a Citus table, right now only\n * CreateSingleShardTable() accepts a ColocationParam.\n */\ntypedef struct\n{\n\tunion\n\t{\n\t\tchar *colocateWithTableName;\n\t\tuint32 colocationId;\n\t};\n\n\tColocationParamType colocationParamType;\n} ColocationParam;\n\n\ntypedef enum BackgroundJobStatus\n{\n\tBACKGROUND_JOB_STATUS_SCHEDULED,\n\tBACKGROUND_JOB_STATUS_RUNNING,\n\tBACKGROUND_JOB_STATUS_FINISHED,\n\tBACKGROUND_JOB_STATUS_CANCELLING,\n\tBACKGROUND_JOB_STATUS_CANCELLED,\n\tBACKGROUND_JOB_STATUS_FAILING,\n\tBACKGROUND_JOB_STATUS_FAILED\n} BackgroundJobStatus;\n\ntypedef struct BackgroundJob\n{\n\tint64 jobid;\n\tBackgroundJobStatus state;\n\tchar *jobType;\n\tchar *description;\n\tTimestampTz *started_at;\n\tTimestampTz *finished_at;\n\n\t/* extra space to store values for nullable value types above */\n\tstruct\n\t{\n\t\tTimestampTz started_at;\n\t\tTimestampTz finished_at;\n\t} __nullable_storage;\n} BackgroundJob;\n\ntypedef enum BackgroundTaskStatus\n{\n\tBACKGROUND_TASK_STATUS_BLOCKED,\n\tBACKGROUND_TASK_STATUS_RUNNABLE,\n\tBACKGROUND_TASK_STATUS_RUNNING,\n\tBACKGROUND_TASK_STATUS_CANCELLING,\n\tBACKGROUND_TASK_STATUS_DONE,\n\tBACKGROUND_TASK_STATUS_ERROR,\n\tBACKGROUND_TASK_STATUS_UNSCHEDULED,\n\tBACKGROUND_TASK_STATUS_CANCELLED\n} BackgroundTaskStatus;\n\ntypedef struct BackgroundTask\n{\n\tint64 jobid;\n\tint64 taskid;\n\tOid owner;\n\tint32 *pid;\n\tBackgroundTaskStatus status;\n\tchar *command;\n\tint32 *retry_count;\n\tTimestampTz *not_before;\n\tchar *message;\n\tList *nodesInvolved;\n\n\t/* extra space to store values for nullable value types above */\n\tstruct\n\t{\n\t\tint32 pid;\n\t\tint32 retry_count;\n\t\tTimestampTz not_before;\n\t} __nullable_storage;\n} BackgroundTask;\n\n#define SET_NULLABLE_FIELD(ptr, field, value) \\\n\t\t(ptr)->__nullable_storage.field = (value); \\\n\t\t(ptr)->field = &((ptr)->__nullable_storage.field)\n\n#define UNSET_NULLABLE_FIELD(ptr, field) \\\n\t\t(ptr)->field = NULL; \\\n\t\tmemset_struct_0((ptr)->__nullable_storage.field)\n\n/* Size functions */\nextern Datum citus_table_size(PG_FUNCTION_ARGS);\nextern Datum citus_total_relation_size(PG_FUNCTION_ARGS);\nextern Datum citus_relation_size(PG_FUNCTION_ARGS);\n\n/* Function declarations to read shard and shard placement data */\nextern uint32 TableShardReplicationFactor(Oid relationId);\nextern List * LoadShardIntervalList(Oid relationId);\nextern List * LoadUnsortedShardIntervalListViaCatalog(Oid relationId);\nextern ShardInterval * LoadShardIntervalWithLongestShardName(Oid relationId);\nextern int ShardIntervalCount(Oid relationId);\nextern List * LoadShardList(Oid relationId);\nextern ShardInterval * CopyShardInterval(ShardInterval *srcInterval);\nextern uint64 ShardLength(uint64 shardId);\nextern bool NodeGroupHasShardPlacements(int32 groupId);\nextern bool IsActiveShardPlacement(ShardPlacement *ShardPlacement);\nextern bool IsRemoteShardPlacement(ShardPlacement *shardPlacement);\nextern bool IsPlacementOnWorkerNode(ShardPlacement *placement, WorkerNode *workerNode);\nextern List * FilterShardPlacementList(List *shardPlacementList, bool (*filter)(\n\t\t\t\t\t\t\t\t\t\t   ShardPlacement *));\nextern List * FilterActiveShardPlacementListByNode(List *shardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t\t   WorkerNode *workerNode);\nextern List * ActiveShardPlacementListOnGroup(uint64 shardId, int32 groupId);\nextern List * ActiveShardPlacementList(uint64 shardId);\nextern List * ShardPlacementListSortedByWorker(uint64 shardId);\nextern ShardPlacement * ActiveShardPlacement(uint64 shardId, bool missingOk);\nextern WorkerNode * ActiveShardPlacementWorkerNode(uint64 shardId);\nextern List * BuildShardPlacementList(int64 shardId);\nextern List * AllShardPlacementsOnNodeGroup(int32 groupId);\nextern List * GroupShardPlacementsForTableOnGroup(Oid relationId, int32 groupId);\nextern void LookupTaskPlacementHostAndPort(ShardPlacement *taskPlacement, char **nodeName,\n\t\t\t\t\t\t\t\t\t\t   int *nodePort);\nextern bool IsDummyPlacement(ShardPlacement *taskPlacement);\nextern StringInfo GenerateSizeQueryOnMultiplePlacements(List *shardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tOid indexId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSizeQueryType sizeQueryType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbool optimizePartitionCalculations\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t);\nextern List * RemoveCoordinatorPlacementIfNotSingleNode(List *placementList);\n\n/* Function declarations to modify shard and shard placement data */\nextern void InsertShardRow(Oid relationId, uint64 shardId, char storageType,\n\t\t\t\t\t\t   text *shardMinValue, text *shardMaxValue);\nextern void DeleteShardRow(uint64 shardId);\nextern ShardPlacement * InsertShardPlacementRowGlobally(uint64 shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tuint64 placementId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tuint64 shardLength,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tint32 groupId);\nextern uint64 InsertShardPlacementRow(uint64 shardId, uint64 placementId,\n\t\t\t\t\t\t\t\t\t  uint64 shardLength, int32 groupId);\nextern void InsertIntoPgDistPartition(Oid relationId, char distributionMethod,\n\t\t\t\t\t\t\t\t\t  Var *distributionColumn, uint32 colocationId,\n\t\t\t\t\t\t\t\t\t  char replicationModel, bool autoConverted);\nextern void UpdatePgDistPartitionAutoConverted(Oid citusTableId, bool autoConverted);\nextern void UpdateDistributionColumnGlobally(Oid relationId, char distributionMethod,\n\t\t\t\t\t\t\t\t\t\t\t Var *distributionColumn, int colocationId);\nextern void UpdateDistributionColumn(Oid relationId, char distributionMethod,\n\t\t\t\t\t\t\t\t\t Var *distributionColumn, int colocationId);\nextern void DeletePartitionRow(Oid distributedRelationId);\nextern void UpdateNoneDistTableMetadataGlobally(Oid relationId, char replicationModel,\n\t\t\t\t\t\t\t\t\t\t\t\tuint32 colocationId, bool autoConverted);\nextern void UpdateNoneDistTableMetadata(Oid relationId, char replicationModel,\n\t\t\t\t\t\t\t\t\t\tuint32 colocationId, bool autoConverted);\nextern void DeleteShardRow(uint64 shardId);\nextern void UpdatePlacementGroupId(uint64 placementId, int groupId);\nextern void DeleteShardPlacementRowGlobally(uint64 placementId);\nextern void DeleteShardPlacementRow(uint64 placementId);\nextern void CreateSingleShardTable(Oid relationId, ColocationParam colocationParam);\nextern void CreateDistributedTable(Oid relationId, char *distributionColumnName,\n\t\t\t\t\t\t\t\t   char distributionMethod, int shardCount,\n\t\t\t\t\t\t\t\t   bool shardCountIsStrict, char *colocateWithTableName);\nextern void CreateReferenceTable(Oid relationId);\nextern void CreateTruncateTrigger(Oid relationId);\nextern uint64 CopyFromLocalTableIntoDistTable(Oid localTableId, Oid distributedTableId);\nextern void EnsureUndistributeTenantTableSafe(Oid relationId, const char *operationName);\nextern TableConversionReturn * UndistributeTable(TableConversionParameters *params);\nextern void UndistributeTables(List *relationIdList);\n\nextern void EnsureObjectAndDependenciesExistOnAllNodes(const ObjectAddress *target);\nextern void EnsureAllObjectDependenciesExistOnAllNodes(const List *targets);\nextern DeferredErrorMessage * DeferErrorIfCircularDependencyExists(const\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ObjectAddress *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   objectAddress);\nextern List * GetDistributableDependenciesForObject(const ObjectAddress *target);\nextern List * GetAllDependencyCreateDDLCommands(const List *dependencies);\nextern bool ShouldPropagate(void);\nextern bool ShouldPropagateCreateInCoordinatedTransction(void);\nextern bool ShouldPropagateAnyObject(List *addresses);\n\n/* Remaining metadata utility functions  */\nextern Oid TableOwnerOid(Oid relationId);\nextern char * TableOwner(Oid relationId);\nextern void EnsureTablePermissions(Oid relationId, AclMode mode, AclMaskHow mask);\nextern void EnsureTableOwner(Oid relationId);\nextern void EnsureHashDistributedTable(Oid relationId);\nextern void EnsureHashOrSingleShardDistributedTable(Oid relationId);\nextern void EnsureFunctionOwner(Oid functionId);\nextern void EnsureSchemaOwner(Oid schemaId);\nextern void EnsureSuperUser(void);\nextern void ErrorIfTableIsACatalogTable(Relation relation);\nextern void EnsureTableNotDistributed(Oid relationId);\nextern void EnsureRelationExists(Oid relationId);\nextern bool RegularTable(Oid relationId);\nextern bool TableEmpty(Oid tableId);\nextern bool IsForeignTable(Oid relationId);\nextern bool ForeignTableDropsTableNameOption(List *optionList);\nextern bool ServerUsesPostgresFdw(Oid serverId);\nextern char * ConstructQualifiedShardName(ShardInterval *shardInterval);\nextern uint64 GetFirstShardId(Oid relationId);\nextern Datum StringToDatum(char *inputString, Oid dataType);\nextern char * DatumToString(Datum datum, Oid dataType);\nextern int CompareShardPlacementsByWorker(const void *leftElement,\n\t\t\t\t\t\t\t\t\t\t  const void *rightElement);\nextern int CompareShardPlacementsByGroupId(const void *leftElement,\n\t\t\t\t\t\t\t\t\t\t   const void *rightElement);\nextern ShardInterval * DeformedDistShardTupleToShardInterval(Datum *datumArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t bool *isNullArray,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Oid intervalTypeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t int32 intervalTypeMod);\nextern void GetIntervalTypeInfo(char partitionMethod, Var *partitionColumn,\n\t\t\t\t\t\t\t\tOid *intervalTypeId, int32 *intervalTypeMod);\nextern List * SendShardStatisticsQueriesInParallel(List *citusTableIds,\n\t\t\t\t\t\t\t\t\t\t\t\t   bool useDistributedTransaction);\nextern bool GetNodeDiskSpaceStatsForConnection(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t   uint64 *availableBytes,\n\t\t\t\t\t\t\t\t\t\t\t   uint64 *totalBytes);\nextern void ExecuteQueryViaSPI(char *query, int SPIOK);\nextern void ExecuteAndLogQueryViaSPI(char *query, int SPIOK, int logLevel);\nextern void EnsureSequenceTypeSupported(Oid seqOid, Oid attributeTypeId, Oid\n\t\t\t\t\t\t\t\t\t\townerRelationId);\nextern void AlterSequenceType(Oid seqOid, Oid typeOid);\nextern void EnsureRelationHasCompatibleSequenceTypes(Oid relationId);\nextern bool HasRunnableBackgroundTask(void);\nextern bool HasNonTerminalJobOfType(const char *jobType, int64 *jobIdOut);\nextern int64 CreateBackgroundJob(const char *jobType, const char *description);\nextern BackgroundTask * ScheduleBackgroundTask(int64 jobId, Oid owner, char *command,\n\t\t\t\t\t\t\t\t\t\t\t   int dependingTaskCount,\n\t\t\t\t\t\t\t\t\t\t\t   int64 dependingTaskIds[],\n\t\t\t\t\t\t\t\t\t\t\t   int nodesInvolvedCount,\n\t\t\t\t\t\t\t\t\t\t\t   int32 nodesInvolved[]);\nextern BackgroundTask * GetRunnableBackgroundTask(void);\nextern void ResetRunningBackgroundTasks(void);\nextern BackgroundJob * GetBackgroundJobByJobId(int64 jobId);\nextern BackgroundTask * GetBackgroundTaskByTaskId(int64 taskId);\nextern void UpdateBackgroundJob(int64 jobId);\nextern void UpdateBackgroundTask(BackgroundTask *task);\nextern void UpdateJobStatus(int64 taskId, const pid_t *pid, BackgroundTaskStatus status,\n\t\t\t\t\t\t\tconst int32 *retry_count, char *message);\nextern bool UpdateJobError(BackgroundTask *job, ErrorData *edata);\nextern List * CancelTasksForJob(int64 jobid);\nextern void UnscheduleDependentTasks(BackgroundTask *task);\nextern void UnblockDependingBackgroundTasks(BackgroundTask *task);\nextern BackgroundJobStatus BackgroundJobStatusByOid(Oid enumOid);\nextern BackgroundTaskStatus BackgroundTaskStatusByOid(Oid enumOid);\nextern bool IsBackgroundJobStatusTerminal(BackgroundJobStatus status);\nextern bool IsBackgroundTaskStatusTerminal(BackgroundTaskStatus status);\nextern Oid BackgroundJobStatusOid(BackgroundJobStatus status);\nextern Oid BackgroundTaskStatusOid(BackgroundTaskStatus status);\nextern int GetAutoConvertedAttrIndexInPgDistPartition(TupleDesc tupleDEsc);\n\n/* from node_metadata.c */\nextern void LockShardsInWorkerPlacementList(WorkerNode *workerNode, LOCKMODE lockMode);\nextern void ActivateCloneNodeAsPrimary(WorkerNode *workerNode);\n#endif   /* METADATA_UTILITY_H */\n"
  },
  {
    "path": "src/include/distributed/multi_executor.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_executor.h\n *\t  Executor support for Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef MULTI_EXECUTOR_H\n#define MULTI_EXECUTOR_H\n\n#include \"executor/execdesc.h\"\n#include \"nodes/execnodes.h\"\n#include \"nodes/parsenodes.h\"\n\n#include \"distributed/citus_custom_scan.h\"\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/multi_server_executor.h\"\n#include \"distributed/tuple_destination.h\"\n\n\n/* managed via guc.c */\ntypedef enum\n{\n\tPARALLEL_CONNECTION = 0,\n\tSEQUENTIAL_CONNECTION = 1\n} MultiShardConnectionTypes;\n\n/*\n * TransactionBlocksUsage indicates whether to use remote transaction\n * blocks according to one of the following policies:\n * - opening a remote transaction is required\n * - opening a remote transaction does not matter, so it is allowed but not required.\n * - opening a remote transaction is disallowed\n */\ntypedef enum TransactionBlocksUsage\n{\n\tTRANSACTION_BLOCKS_REQUIRED,\n\tTRANSACTION_BLOCKS_ALLOWED,\n\tTRANSACTION_BLOCKS_DISALLOWED,\n} TransactionBlocksUsage;\n\n/*\n * TransactionProperties reflects how we should execute a task list\n * given previous commands in the transaction and the type of task list.\n */\ntypedef struct TransactionProperties\n{\n\t/* if true, any failure on the worker causes the execution to end immediately */\n\tbool errorOnAnyFailure;\n\n\t/*\n\t * Determines whether transaction blocks on workers are required, disallowed, or\n\t * allowed (will use them if already in a coordinated transaction).\n\t */\n\tTransactionBlocksUsage useRemoteTransactionBlocks;\n\n\t/* if true, the current execution requires 2PC to be globally enabled */\n\tbool requires2PC;\n} TransactionProperties;\n\n\nextern bool AllowNestedDistributedExecution;\nextern bool SkipConstraintValidation;\nextern int MultiShardConnectionType;\nextern bool WritableStandbyCoordinator;\nextern bool AllowModificationsFromWorkersToReplicatedTables;\nextern bool ForceMaxQueryParallelization;\nextern int MaxAdaptiveExecutorPoolSize;\nextern int ExecutorSlowStartInterval;\nextern bool SortReturning;\nextern int ExecutorLevel;\n\n\nextern void CitusExecutorStart(QueryDesc *queryDesc, int eflags);\nextern void CitusExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count,\n\t\t\t\t\t\t\t bool execute_once);\nextern void AdaptiveExecutorPreExecutorRun(CitusScanState *scanState);\nextern TupleTableSlot * AdaptiveExecutor(CitusScanState *scanState);\n\n\n/*\n * ExecutionParams contains parameters that are used during the execution.\n * Some of these can be the zero value if it is not needed during the execution.\n */\ntypedef struct ExecutionParams\n{\n\t/* modLevel is the access level for rows.*/\n\tRowModifyLevel modLevel;\n\n\t/* taskList contains the tasks for the execution.*/\n\tList *taskList;\n\n\t/* where to forward each tuple received */\n\tTupleDestination *tupleDestination;\n\n\t/* expectResults is true if this execution will return some result. */\n\tbool expectResults;\n\n\t/* targetPoolSize is the maximum amount of connections per worker */\n\tint targetPoolSize;\n\n\t/* xactProperties contains properties for transactions, such as if we should use 2pc. */\n\tTransactionProperties xactProperties;\n\n\t/* jobIdList contains all job ids for the execution */\n\tList *jobIdList;\n\n\t/* localExecutionSupported is true if we can use local execution, if it is false\n\t * local execution will not be used. */\n\tbool localExecutionSupported;\n\n\t/* isUtilityCommand is true if the current execution is for a utility\n\t * command such as a DDL command.*/\n\tbool isUtilityCommand;\n\n\t/* pass bind parameters to the distributed executor for parameterized plans */\n\tParamListInfo paramListInfo;\n} ExecutionParams;\n\nExecutionParams * CreateBasicExecutionParams(RowModifyLevel modLevel,\n\t\t\t\t\t\t\t\t\t\t\t List *taskList,\n\t\t\t\t\t\t\t\t\t\t\t int targetPoolSize,\n\t\t\t\t\t\t\t\t\t\t\t bool localExecutionSupported);\n\nextern uint64 ExecuteTaskListExtended(ExecutionParams *executionParams);\nextern uint64 ExecuteTaskListIntoTupleDestWithParam(RowModifyLevel modLevel,\n\t\t\t\t\t\t\t\t\t\t\t\t\tList *taskList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tTupleDestination *tupleDest,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbool expectResults,\n\t\t\t\t\t\t\t\t\t\t\t\t\tParamListInfo paramListInfo);\nextern uint64 ExecuteTaskListIntoTupleDest(RowModifyLevel modLevel, List *taskList,\n\t\t\t\t\t\t\t\t\t\t   TupleDestination *tupleDest,\n\t\t\t\t\t\t\t\t\t\t   bool expectResults);\nextern bool IsCitusCustomState(PlanState *planState);\nextern TupleTableSlot * CitusExecScan(CustomScanState *node);\nextern TupleTableSlot * ReturnTupleFromTuplestore(CitusScanState *scanState);\nextern void ReadFileIntoTupleStore(char *fileName, char *copyFormat, TupleDesc\n\t\t\t\t\t\t\t\t   tupleDescriptor, Tuplestorestate *tupstore);\nextern Query * ParseQueryString(const char *queryString, Oid *paramOids, int numParams);\nextern Query * RewriteRawQueryStmt(RawStmt *rawStmt, const char *queryString,\n\t\t\t\t\t\t\t\t   Oid *paramOids, int numParams);\nextern void ExecuteQueryStringIntoDestReceiver(const char *queryString, ParamListInfo\n\t\t\t\t\t\t\t\t\t\t\t   params,\n\t\t\t\t\t\t\t\t\t\t\t   DestReceiver *dest);\nextern void ExecuteQueryIntoDestReceiver(Query *query, ParamListInfo params,\n\t\t\t\t\t\t\t\t\t\t DestReceiver *dest);\nextern uint64 ExecutePlanIntoDestReceiver(PlannedStmt *queryPlan, ParamListInfo params,\n\t\t\t\t\t\t\t\t\t\t  DestReceiver *dest);\nextern void SetLocalMultiShardModifyModeToSequential(void);\nextern void EnsureSequentialMode(ObjectType objType);\nextern void SetLocalForceMaxQueryParallelization(void);\nextern void SortTupleStore(CitusScanState *scanState);\nextern ParamListInfo ExecutorBoundParams(void);\nextern void EnsureTaskExecutionAllowed(bool isRemote);\n\n\n#endif /* MULTI_EXECUTOR_H */\n"
  },
  {
    "path": "src/include/distributed/multi_explain.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_explain.h\n *\t  Explain support for Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef MULTI_EXPLAIN_H\n#define MULTI_EXPLAIN_H\n\n#include \"tuple_destination.h\"\n\n#include \"executor/executor.h\"\n\ntypedef enum\n{\n\tEXPLAIN_ANALYZE_SORT_BY_TIME = 0,\n\tEXPLAIN_ANALYZE_SORT_BY_TASK_ID = 1\n} ExplainAnalyzeSortMethods;\n\n/* Config variables managed via guc.c to explain distributed query plans */\nextern bool ExplainDistributedQueries;\nextern bool ExplainAllTasks;\nextern int ExplainAnalyzeSortMethod;\n\nextern void FreeSavedExplainPlan(void);\nextern void CitusExplainOneQuery(Query *query, int cursorOptions, IntoClause *into,\n\t\t\t\t\t\t\t\t ExplainState *es, const char *queryString, ParamListInfo\n\t\t\t\t\t\t\t\t params,\n\t\t\t\t\t\t\t\t QueryEnvironment *queryEnv);\nextern List * ExplainAnalyzeTaskList(List *originalTaskList,\n\t\t\t\t\t\t\t\t\t TupleDestination *defaultTupleDest, TupleDesc\n\t\t\t\t\t\t\t\t\t tupleDesc, ParamListInfo params);\nextern bool RequestedForExplainAnalyze(CitusScanState *node);\nextern void ResetExplainAnalyzeData(List *taskList);\n\n#endif /* MULTI_EXPLAIN_H */\n"
  },
  {
    "path": "src/include/distributed/multi_join_order.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_join_order.h\n *\n * Type and function declarations for determining a left-only join order for a\n * distributed query plan.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef MULTI_JOIN_ORDER_H\n#define MULTI_JOIN_ORDER_H\n\n#include \"postgres.h\"\n\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n\n\n/*\n * JoinRuleType determines the type of the join rule that applies between two\n * tables or row sources. The rule types are ordered below according to their\n * costs, with the cheapes rule appearing at the top. Note that changing the\n * order of these enums *will* change the order in which the rules are applied.\n */\ntypedef enum JoinRuleType\n{\n\tJOIN_RULE_INVALID_FIRST = 0,\n\tREFERENCE_JOIN = 1,\n\tLOCAL_PARTITION_JOIN = 2,\n\tSINGLE_HASH_PARTITION_JOIN = 3,\n\tSINGLE_RANGE_PARTITION_JOIN = 4,\n\tDUAL_PARTITION_JOIN = 5,\n\tCARTESIAN_PRODUCT_REFERENCE_JOIN = 6,\n\tCARTESIAN_PRODUCT = 7,\n\n\t/*\n\t * Add new join rule types above this comment. After adding, you must also\n\t * update these arrays: RuleEvalFunctionArray, RuleApplyFunctionArray, and\n\t * RuleNameArray.\n\t */\n\tJOIN_RULE_LAST\n} JoinRuleType;\n\n\n/*\n * TableEntry represents a table used when determining the join order. A table\n * entry corresponds to an ordinary relation reference (RTE_RELATION) in the\n * query range table list.\n */\ntypedef struct TableEntry\n{\n\tOid relationId;\n\tuint32 rangeTableId;\n} TableEntry;\n\n\n/*\n * JoinOrderNode represents an element in the join order list; and this list\n * keeps the total join order for a distributed query. The first node in this\n * list later becomes the leftmost table in the join tree, and the successive\n * elements in the list are the joining tables in the left-deep tree.\n */\ntypedef struct JoinOrderNode\n{\n\tTableEntry *tableEntry;     /* this node's relation and range table id */\n\tJoinRuleType joinRuleType;  /* not relevant for the first table */\n\tJoinType joinType;          /* not relevant for the first table */\n\n\t/*\n\t * We keep track of all unique partition columns in the relation to correctly find\n\t * join clauses that can be applied locally.\n\t */\n\tList *partitionColumnList;\n\n\tchar partitionMethod;\n\tList *joinClauseList;       /* not relevant for the first table */\n\tTableEntry *anchorTable;\n} JoinOrderNode;\n\n\n/* Config variables managed via guc.c */\nextern bool LogMultiJoinOrder;\nextern bool EnableSingleHashRepartitioning;\n\n\n/* Function declaration for determining table join orders */\nextern List * JoinExprList(FromExpr *fromExpr);\nextern List * JoinOrderList(List *rangeTableEntryList, List *joinClauseList);\nextern bool IsApplicableJoinClause(List *leftTableIdList, uint32 rightTableId,\n\t\t\t\t\t\t\t\t   Node *joinClause);\nextern List * ApplicableJoinClauses(List *leftTableIdList, uint32 rightTableId,\n\t\t\t\t\t\t\t\t\tList *joinClauseList);\nextern bool NodeIsEqualsOpExpr(Node *node);\nextern bool IsSupportedReferenceJoin(JoinType joinType, bool leftIsReferenceTable,\n\t\t\t\t\t\t\t\t\t bool rightIsReferenceTable);\nextern OpExpr * SinglePartitionJoinClause(List *partitionColumnList,\n\t\t\t\t\t\t\t\t\t\t  List *applicableJoinClauses,\n\t\t\t\t\t\t\t\t\t\t  bool *foundTypeMismatch);\nextern OpExpr * DualPartitionJoinClause(List *applicableJoinClauses);\nextern Var * LeftColumnOrNULL(OpExpr *joinClause);\nextern Var * RightColumnOrNULL(OpExpr *joinClause);\nextern Var * PartitionColumn(Oid relationId, uint32 rangeTableId);\nextern Var * DistPartitionKey(Oid relationId);\nextern Var * DistPartitionKeyOrError(Oid relationId);\nextern char PartitionMethod(Oid relationId);\nextern char TableReplicationModel(Oid relationId);\nextern bool JoinOnColumns(List *currentPartitionColumnList, Var *candidatePartitionColumn,\n\t\t\t\t\t\t  List *joinClauseList);\n\n\n#endif   /* MULTI_JOIN_ORDER_H */\n"
  },
  {
    "path": "src/include/distributed/multi_logical_optimizer.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_logical_optimizer.h\n *\t  Type and function declarations for optimizing multi-relation based logical\n *\t  plans.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef MULTI_LOGICAL_OPTIMIZER_H\n#define MULTI_LOGICAL_OPTIMIZER_H\n\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/relation_restriction_equivalence.h\"\n\n\n/* Definitions local to logical plan optimizer */\n#define DIVISION_OPER_NAME \"/\"\n#define DISABLE_LIMIT_APPROXIMATION -1\n#define DISABLE_DISTINCT_APPROXIMATION 0.0\n#define ARRAY_CAT_AGGREGATE_NAME \"array_cat_agg\"\n#define JSONB_CAT_AGGREGATE_NAME \"jsonb_cat_agg\"\n#define JSON_CAT_AGGREGATE_NAME \"json_cat_agg\"\n#define WORKER_PARTIAL_AGGREGATE_NAME \"worker_partial_agg\"\n#define COORD_COMBINE_AGGREGATE_NAME \"coord_combine_agg\"\n\n#define WORKER_BINARY_PARTIAL_AGGREGATE_NAME \"worker_binary_partial_agg\"\n#define COORD_BINARY_COMBINE_AGGREGATE_NAME \"coord_binary_combine_agg\"\n\n#define WORKER_COLUMN_FORMAT \"worker_column_%d\"\n\n/* Definitions related to count(distinct) approximations */\n#define HLL_EXTENSION_NAME \"hll\"\n#define HLL_TYPE_NAME \"hll\"\n#define HLL_HASH_INTEGER_FUNC_NAME \"hll_hash_integer\"\n#define HLL_HASH_BIGINT_FUNC_NAME \"hll_hash_bigint\"\n#define HLL_HASH_TEXT_FUNC_NAME \"hll_hash_text\"\n#define HLL_HASH_ANY_FUNC_NAME \"hll_hash_any\"\n#define HLL_ADD_AGGREGATE_NAME \"hll_add_agg\"\n#define HLL_UNION_AGGREGATE_NAME \"hll_union_agg\"\n#define HLL_CARDINALITY_FUNC_NAME \"hll_cardinality\"\n#define HLL_FORCE_GROUPAGG_GUC_NAME \"hll.force_groupagg\"\n\n/* Definitions related to Top-N approximations */\n#define TOPN_ADD_AGGREGATE_NAME \"topn_add_agg\"\n#define TOPN_UNION_AGGREGATE_NAME \"topn_union_agg\"\n\n\n/*\n * AggregateType represents an aggregate function's type, where the function is\n * used in the context of a query. We use this function type to determine how to\n * modify the plan when creating the logical distributed plan.\n *\n * Please note that the order of values in this enumeration is tied to the order\n * of elements in the following AggregateNames array. This order needs to be\n * preserved.\n */\ntypedef enum\n{\n\tAGGREGATE_INVALID_FIRST = 0,\n\tAGGREGATE_AVERAGE = 1,\n\tAGGREGATE_MIN = 2,\n\tAGGREGATE_MAX = 3,\n\tAGGREGATE_SUM = 4,\n\tAGGREGATE_COUNT = 5,\n\tAGGREGATE_ARRAY_AGG = 6,\n\tAGGREGATE_JSONB_AGG = 7,\n\tAGGREGATE_JSONB_OBJECT_AGG = 8,\n\tAGGREGATE_JSON_AGG = 9,\n\tAGGREGATE_JSON_OBJECT_AGG = 10,\n\tAGGREGATE_BIT_AND = 11,\n\tAGGREGATE_BIT_OR = 12,\n\tAGGREGATE_BOOL_AND = 13,\n\tAGGREGATE_BOOL_OR = 14,\n\tAGGREGATE_EVERY = 15,\n\tAGGREGATE_HLL_ADD = 16,\n\tAGGREGATE_HLL_UNION = 17,\n\tAGGREGATE_TOPN_ADD_AGG = 18,\n\tAGGREGATE_TOPN_UNION_AGG = 19,\n\tAGGREGATE_ANY_VALUE = 20,\n\n\t/* support for github.com/tvondra/tdigest */\n\tAGGREGATE_TDIGEST_COMBINE = 21,\n\tAGGREGATE_TDIGEST_ADD_DOUBLE = 22,\n\tAGGREGATE_TDIGEST_PERCENTILE_ADD_DOUBLE = 23,\n\tAGGREGATE_TDIGEST_PERCENTILE_ADD_DOUBLEARRAY = 24,\n\tAGGREGATE_TDIGEST_PERCENTILE_TDIGEST_DOUBLE = 25,\n\tAGGREGATE_TDIGEST_PERCENTILE_TDIGEST_DOUBLEARRAY = 26,\n\tAGGREGATE_TDIGEST_PERCENTILE_OF_ADD_DOUBLE = 27,\n\tAGGREGATE_TDIGEST_PERCENTILE_OF_ADD_DOUBLEARRAY = 28,\n\tAGGREGATE_TDIGEST_PERCENTILE_OF_TDIGEST_DOUBLE = 29,\n\tAGGREGATE_TDIGEST_PERCENTILE_OF_TDIGEST_DOUBLEARRAY = 30,\n\n\t/* AGGREGATE_CUSTOM must come last */\n\tAGGREGATE_CUSTOM_COMBINE = 31,\n\tAGGREGATE_CUSTOM_ROW_GATHER = 32,\n} AggregateType;\n\n\n/* Enumeration for citus.coordinator_aggregation GUC */\ntypedef enum\n{\n\tCOORDINATOR_AGGREGATION_DISABLED,\n\tCOORDINATOR_AGGREGATION_ROW_GATHER,\n} CoordinatorAggregationStrategyType;\n\n\n/*\n * PushDownStatus indicates whether a node can be pushed down below its child\n * using the commutative and distributive relational algebraic properties.\n */\ntypedef enum\n{\n\tPUSH_DOWN_INVALID_FIRST = 0,\n\tPUSH_DOWN_VALID = 1,\n\tPUSH_DOWN_NOT_VALID = 2,\n\tPUSH_DOWN_SPECIAL_CONDITIONS = 3\n} PushDownStatus;\n\n\n/*\n * PullUpStatus indicates whether a node can be pulled up above its parent using\n * the commutative and factorizable relational algebraic properties.\n */\ntypedef enum\n{\n\tPULL_UP_INVALID_FIRST = 0,\n\tPULL_UP_VALID = 1,\n\tPULL_UP_NOT_VALID = 2\n} PullUpStatus;\n\n\n/*\n * AggregateNames is an array that stores cstring names for aggregate functions;\n * these cstring names act as an intermediary when mapping aggregate function\n * oids to AggregateType enumerations. For this mapping to occur, we use the\n * aggregate function oid to find the corresponding cstring name in pg_proc. We\n * then compare that name against entries in this array, and return the\n * appropriate AggregateType value.\n *\n * Please note that the order of elements in this array is tied to the order of\n * values in the preceding AggregateType enum. This order needs to be preserved.\n */\nstatic const char *const AggregateNames[] = {\n\t\"invalid\", \"avg\", \"min\", \"max\",\n\t\"sum\", \"count\", \"array_agg\",\n\t\"jsonb_agg\", \"jsonb_object_agg\",\n\t\"json_agg\", \"json_object_agg\",\n\t\"bit_and\", \"bit_or\", \"bool_and\", \"bool_or\", \"every\",\n\t\"hll_add_agg\", \"hll_union_agg\",\n\t\"topn_add_agg\", \"topn_union_agg\",\n\t\"any_value\"\n};\n\n\n/* Config variable managed via guc.c */\nextern int LimitClauseRowFetchCount;\nextern double CountDistinctErrorRate;\nextern int CoordinatorAggregationStrategy;\n\n\n/* Function declaration for optimizing logical plans */\nextern void MultiLogicalPlanOptimize(MultiTreeRoot *multiTree);\n\n/* Function declaration for getting partition method for the given relation */\nextern char PartitionMethod(Oid relationId);\n\n/* Function declaration for helper functions in subquery pushdown */\nextern bool GroupedByColumn(List *groupClauseList, List *targetList, Var *column);\nextern List * SubqueryMultiTableList(MultiNode *multiNode);\nextern List * GroupTargetEntryList(List *groupClauseList, List *targetEntryList);\nextern bool ExtractQueryWalker(Node *node, List **queryList);\nextern bool IsPartitionColumn(Expr *columnExpression, Query *query, bool skipOuterVars);\nextern void FindReferencedTableColumn(Expr *columnExpression, List *parentQueryList,\n\t\t\t\t\t\t\t\t\t  Query *query, Var **column,\n\t\t\t\t\t\t\t\t\t  RangeTblEntry **rteContainingReferencedColumn,\n\t\t\t\t\t\t\t\t\t  bool skipOuterVars);\nextern char * WorkerColumnName(AttrNumber resno);\nextern bool IsGroupBySubsetOfDistinct(List *groupClauses, List *distinctClauses);\nextern bool TargetListHasAggregates(List *targetEntryList);\n\n#endif   /* MULTI_LOGICAL_OPTIMIZER_H */\n"
  },
  {
    "path": "src/include/distributed/multi_logical_planner.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_logical_planner.h\n *\t  Type declarations for multi-relational algebra operators, and function\n *\t  declarations for building logical plans over distributed relations.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef MULTI_LOGICAL_PLANNER_H\n#define MULTI_LOGICAL_PLANNER_H\n\n#include \"nodes/nodes.h\"\n#include \"nodes/parsenodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/multi_join_order.h\"\n#include \"distributed/relation_restriction_equivalence.h\"\n\n\n#define SUBQUERY_RANGE_TABLE_ID -1\n#define SUBQUERY_RELATION_ID 10000\n#define SUBQUERY_PUSHDOWN_RELATION_ID 10001\n\n\n/*\n * MultiNode represents the base node type for all multi-relational algebra\n * nodes. By creating this base node, we can simulate inheritance and represent\n * derived operator nodes as a MultiNode type. A similar structure to simulate\n * inheritance is also used in PostgreSQL's plan nodes.\n */\ntypedef struct MultiNode\n{\n\tCitusNode type;\n\n\tstruct MultiNode *parentNode;\n\n\t/* child node(s) are defined in unary and binary nodes */\n} MultiNode;\n\n\n/* Represents unary nodes that have only one child */\ntypedef struct MultiUnaryNode\n{\n\tMultiNode node;\n\n\tstruct MultiNode *childNode;\n} MultiUnaryNode;\n\n\n/* Represents binary nodes that have two children */\ntypedef struct MultiBinaryNode\n{\n\tMultiNode node;\n\n\tstruct MultiNode *leftChildNode;\n\tstruct MultiNode *rightChildNode;\n} MultiBinaryNode;\n\n\n/*\n * MultiTreeRoot keeps a pointer to the root node in the multi-relational\n * operator tree. This node is always on the top of every logical plan.\n */\ntypedef struct MultiTreeRoot\n{\n\tMultiUnaryNode unaryNode;\n} MultiTreeRoot;\n\n\n/*\n * MultiTable represents a distributed table in a logical query plan. Note that\n * this node does not represent a query operator, and differs from the nodes\n * that follow in that sense.\n */\ntypedef struct MultiTable\n{\n\tMultiUnaryNode unaryNode;\n\tOid relationId;\n\tint rangeTableId;\n\tVar *partitionColumn;\n\tAlias *alias;\n\tAlias *referenceNames;\n\tQuery *subquery; /* this field is only valid for non-relation subquery types */\n\tbool includePartitions;\n\n\t/* FROM .. TABLESAMPLE clause */\n\tTableSampleClause *tablesample;\n} MultiTable;\n\n\n/* Defines the columns to project in multi-relational algebra */\ntypedef struct MultiProject\n{\n\tMultiUnaryNode unaryNode;\n\tList *columnList;\n} MultiProject;\n\n\n/*\n * MultiCollect defines the Collect operator in multi-relational algebra. This\n * operator collects data from remote nodes; the collected data are output from\n * the query operator that is beneath this Collect in the logical query tree.\n */\ntypedef struct MultiCollect\n{\n\tMultiUnaryNode unaryNode;\n} MultiCollect;\n\n\n/*\n * MultiSelect defines the MultiSelect operator in multi-relational algebra.\n * This operator contains select predicates which apply to a selected logical\n * relation.\n */\ntypedef struct MultiSelect\n{\n\tMultiUnaryNode unaryNode;\n\tList *selectClauseList;\n} MultiSelect;\n\n\n/*\n * MultiJoin joins the output of two query operators that are beneath it in the\n * query tree. The operator also keeps the join rule that applies between the\n * two operators, and the partition key to use if the join is distributed.\n */\ntypedef struct MultiJoin\n{\n\tMultiBinaryNode binaryNode;\n\tList *joinClauseList;\n\tJoinRuleType joinRuleType;\n\tJoinType joinType;\n} MultiJoin;\n\n\n/* Defines the (re-)Partition operator in multi-relational algebra */\ntypedef struct MultiPartition\n{\n\tMultiUnaryNode unaryNode;\n\tVar *partitionColumn;\n\tuint32 splitPointTableId;\n} MultiPartition;\n\n\n/* Defines the CartesianProduct operator in multi-relational algebra */\ntypedef struct MultiCartesianProduct\n{\n\tMultiBinaryNode binaryNode;\n} MultiCartesianProduct;\n\n\n/*\n * MultiExtendedOp defines a set of extended operators that operate on columns\n * in relational algebra. This node allows us to distinguish between operations\n * in the coordinator and worker nodes, and also captures the following:\n *\n * (1) Aggregate functions such as sums or averages;\n * (2) Grouping of attributes; these groupings may also be tied to aggregates;\n * (3) Extended projection expressions including columns, arithmetic and string\n * functions;\n * (4) User's intented display information, such as column display order;\n * (5) Sort clauses on columns, expressions, or aggregates; and\n * (6) Limit count and offset clause.\n */\ntypedef struct MultiExtendedOp\n{\n\tMultiUnaryNode unaryNode;\n\tList *targetList;\n\tList *groupClauseList;\n\tList *sortClauseList;\n\tNode *limitCount;\n\tNode *limitOffset;\n\tLimitOption limitOption;\n\tNode *havingQual;\n\tList *distinctClause;\n\tList *windowClause;\n\tbool hasDistinctOn;\n\tbool hasWindowFuncs;\n\tbool onlyPushableWindowFunctions;\n} MultiExtendedOp;\n\n\n/* Function declarations for building logical plans */\nextern MultiTreeRoot * MultiLogicalPlanCreate(Query *originalQuery, Query *queryTree,\n\t\t\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext);\nextern bool FindNodeMatchingCheckFunction(Node *node, bool (*check)(Node *));\nextern bool TargetListOnPartitionColumn(Query *query, List *targetEntryList);\nextern bool FindNodeMatchingCheckFunctionInRangeTableList(List *rtable, bool (*check)(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Node *));\nextern bool IsCitusTableRTE(Node *node);\nextern bool IsDistributedOrReferenceTableRTE(Node *node);\nextern bool IsDistributedTableRTE(Node *node);\nextern bool IsReferenceTableRTE(Node *node);\nextern bool IsTableWithDistKeyRTE(Node *node);\nextern bool IsCitusExtraDataContainerRelation(RangeTblEntry *rte);\nextern bool ContainsReadIntermediateResultFunction(Node *node);\nextern bool ContainsReadIntermediateResultArrayFunction(Node *node);\nextern bool IsReadIntermediateResultFunction(Node *node);\nextern char * FindIntermediateResultIdIfExists(RangeTblEntry *rte);\nextern MultiNode * ParentNode(MultiNode *multiNode);\nextern MultiNode * ChildNode(MultiUnaryNode *multiNode);\nextern MultiNode * GrandChildNode(MultiUnaryNode *multiNode);\nextern void SetChild(MultiUnaryNode *parent, MultiNode *child);\nextern void SetLeftChild(MultiBinaryNode *parent, MultiNode *leftChild);\nextern void SetRightChild(MultiBinaryNode *parent, MultiNode *rightChild);\nextern bool UnaryOperator(MultiNode *node);\nextern bool BinaryOperator(MultiNode *node);\nextern List * OutputTableIdList(MultiNode *multiNode);\nextern List * FindNodesOfType(MultiNode *node, int type);\nextern List * JoinClauseList(List *whereClauseList);\nextern bool IsJoinClause(Node *clause);\nextern List * SubqueryEntryList(Query *queryTree);\nextern DeferredErrorMessage * DeferErrorIfQueryNotSupported(Query *queryTree);\nextern List * WhereClauseList(FromExpr *fromExpr);\nextern List * QualifierList(FromExpr *fromExpr);\nextern List * TableEntryList(List *rangeTableList);\nextern List * UsedTableEntryList(Query *query);\nextern List * pull_var_clause_default(Node *node);\nextern bool OperatorImplementsEquality(Oid opno);\nextern DeferredErrorMessage * DeferErrorIfUnsupportedClause(List *clauseList);\nextern MultiProject * MultiProjectNode(List *targetEntryList);\nextern MultiExtendedOp * MultiExtendedOpNode(Query *queryTree, Query *originalQuery);\nextern DeferredErrorMessage * DeferErrorIfUnsupportedSubqueryRepartition(Query *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t subqueryTree);\nextern MultiNode * MultiNodeTree(Query *queryTree);\n\n\n#endif   /* MULTI_LOGICAL_PLANNER_H */\n"
  },
  {
    "path": "src/include/distributed/multi_logical_replication.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_logical_replication.h\n *\n *    Declarations for public functions and variables used in logical replication\n *    on the distributed tables while moving shards.\n *\n * Copyright (c) 2017, Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#ifndef MULTI_LOGICAL_REPLICATION_H_\n#define MULTI_LOGICAL_REPLICATION_H_\n\n#include \"c.h\"\n\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/connection_management.h\"\n#include \"distributed/hash_helpers.h\"\n#include \"distributed/shard_cleaner.h\"\n\n\n/* Config variables managed via guc.c */\nextern int LogicalReplicationTimeout;\n\nextern bool PlacementMovedUsingLogicalReplicationInTX;\n\n/*\n * NodeAndOwner should be used as a key for structs that should be hashed by a\n * combination of node and owner.\n */\ntypedef struct NodeAndOwner\n{\n\tuint32_t nodeId;\n\tOid tableOwnerId;\n} NodeAndOwner;\nassert_valid_hash_key2(NodeAndOwner, nodeId, tableOwnerId);\n\n\n/*\n * ReplicationSlotInfo stores the info that defines a replication slot. For\n * shard splits this information is built by parsing the result of the\n * 'worker_split_shard_replication_setup' UDF.\n */\ntypedef struct ReplicationSlotInfo\n{\n\tuint32 targetNodeId;\n\tOid tableOwnerId;\n\tchar *name;\n} ReplicationSlotInfo;\n\n/*\n * PublicationInfo stores the information that defines a publication.\n */\ntypedef struct PublicationInfo\n{\n\tNodeAndOwner key;\n\tchar *name;\n\tList *shardIntervals;\n\tstruct LogicalRepTarget *target;\n} PublicationInfo;\n\n/*\n * Stores information necesary to create all the th\n */\ntypedef struct LogicalRepTarget\n{\n\t/*\n\t * The Oid of the user that owns the shards in newShards. This Oid is the\n\t * Oid of the user on the coordinator, this Oid is likely different than\n\t * the Oid of the user on the logical replication source or target.\n\t */\n\tOid tableOwnerId;\n\tchar *subscriptionName;\n\n\t/*\n\t * The name of the user that's used as the owner of the subscription. This\n\t * is not the same as the name of the user that matches tableOwnerId.\n\t * Instead we create a temporary user with the same permissions as that\n\t * user, with its only purpose being owning the subscription.\n\t */\n\tchar *subscriptionOwnerName;\n\tReplicationSlotInfo *replicationSlot;\n\tPublicationInfo *publication;\n\n\t/*\n\t * The shardIntervals that we want to create on this logical replication\n\t * target. This can be different from the shard intervals that are part of\n\t * the publication for two reasons:\n\t * 1. The publication does not contain partitioned tables, only their\n\t *    children. The partition parent tables ARE part of newShards.\n\t * 2. For shard splits the publication also contains dummy shards, these\n\t *    ARE NOT part of newShards.\n\t */\n\tList *newShards;\n\n\t/*\n\t * The superuserConnection is shared between all LogicalRepTargets that have\n\t * the same node. This can be initialized easily by using\n\t * CreateGroupedLogicalRepTargetsConnections.\n\t */\n\tMultiConnection *superuserConnection;\n} LogicalRepTarget;\n\n/*\n * GroupedLogicalRepTargets groups LogicalRepTargets by node. This allows to\n * create a hashmap where we can filter by search by nodeId. Which is useful\n * because these targets can all use the same superuserConection for\n * management, which allows us to batch certain operations such as getting\n * state of the subscriptions.\n */\ntypedef struct GroupedLogicalRepTargets\n{\n\tuint32 nodeId;\n\tList *logicalRepTargetList;\n\tMultiConnection *superuserConnection;\n} GroupedLogicalRepTargets;\n\n\n/*\n * LogicalRepType is used for various functions to do something different for\n * shard moves than for shard splits. Such as using a different prefix for a\n * subscription name.\n */\ntypedef enum LogicalRepType\n{\n\tSHARD_MOVE,\n\tSHARD_SPLIT,\n} LogicalRepType;\n\nextern void LogicallyReplicateShards(List *shardList, char *sourceNodeName,\n\t\t\t\t\t\t\t\t\t int sourceNodePort, char *targetNodeName,\n\t\t\t\t\t\t\t\t\t int targetNodePort,\n\t\t\t\t\t\t\t\t\t bool skipInterShardRelationshipCreation);\n\nextern void ConflictWithIsolationTestingBeforeCopy(void);\nextern void ConflictWithIsolationTestingAfterCopy(void);\nextern void CreateReplicaIdentities(List *subscriptionInfoList);\nextern void CreateReplicaIdentitiesOnNode(List *shardList,\n\t\t\t\t\t\t\t\t\t\t  char *nodeName,\n\t\t\t\t\t\t\t\t\t\t  int32 nodePort);\nextern XLogRecPtr GetRemoteLogPosition(MultiConnection *connection);\nextern List * GetQueryResultStringList(MultiConnection *connection, char *query);\n\nextern MultiConnection * GetReplicationConnection(char *nodeName, int nodePort);\nextern void CreatePublications(MultiConnection *sourceConnection,\n\t\t\t\t\t\t\t   HTAB *publicationInfoHash);\nextern void CreateSubscriptions(MultiConnection *sourceConnection,\n\t\t\t\t\t\t\t\tchar *databaseName, List *subscriptionInfoList);\nextern char * CreateReplicationSlots(MultiConnection *sourceConnection,\n\t\t\t\t\t\t\t\t\t MultiConnection *sourceReplicationConnection,\n\t\t\t\t\t\t\t\t\t List *subscriptionInfoList,\n\t\t\t\t\t\t\t\t\t char *outputPlugin);\nextern void EnableSubscriptions(List *subscriptionInfoList);\n\nextern char * PublicationName(LogicalRepType type, uint32_t nodeId, Oid ownerId);\nextern char * ReplicationSlotNameForNodeAndOwnerForOperation(LogicalRepType type,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t uint32_t nodeId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Oid ownerId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t OperationId operationId);\nextern char * SubscriptionName(LogicalRepType type, Oid ownerId);\nextern char * SubscriptionRoleName(LogicalRepType type, Oid ownerId);\n\nextern void WaitForAllSubscriptionsToCatchUp(MultiConnection *sourceConnection,\n\t\t\t\t\t\t\t\t\t\t\t HTAB *groupedLogicalRepTargetsHash);\nextern void WaitForShardSubscriptionToCatchUp(MultiConnection *targetConnection,\n\t\t\t\t\t\t\t\t\t\t\t  XLogRecPtr sourcePosition,\n\t\t\t\t\t\t\t\t\t\t\t  Bitmapset *tableOwnerIds,\n\t\t\t\t\t\t\t\t\t\t\t  char *operationPrefix);\nextern HTAB * CreateGroupedLogicalRepTargetsHash(List *subscriptionInfoList);\nextern void CreateGroupedLogicalRepTargetsConnections(HTAB *groupedLogicalRepTargetsHash,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  char *user,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  char *databaseName);\nextern void CloseGroupedLogicalRepTargetsConnections(HTAB *groupedLogicalRepTargetsHash);\nextern void CompleteNonBlockingShardTransfer(List *shardList,\n\t\t\t\t\t\t\t\t\t\t\t MultiConnection *sourceConnection,\n\t\t\t\t\t\t\t\t\t\t\t HTAB *publicationInfoHash,\n\t\t\t\t\t\t\t\t\t\t\t List *logicalRepTargetList,\n\t\t\t\t\t\t\t\t\t\t\t HTAB *groupedLogicalRepTargetsHash,\n\t\t\t\t\t\t\t\t\t\t\t LogicalRepType type,\n\t\t\t\t\t\t\t\t\t\t\t bool skipInterShardRelationshipCreation);\nextern void CreateUncheckedForeignKeyConstraints(List *logicalRepTargetList);\nextern void CreatePartitioningHierarchy(List *logicalRepTargetList);\n\n#endif /* MULTI_LOGICAL_REPLICATION_H_ */\n"
  },
  {
    "path": "src/include/distributed/multi_partitioning_utils.h",
    "content": "/*\n * multi_partitioning_utils.h\n *\t  Utility functions declarations for declarative partitioning\n *\n * Copyright (c) Citus Data, Inc.\n */\n#ifndef MULTI_PARTITIONING_UTILS_H_\n#define MULTI_PARTITIONING_UTILS_H_\n\n\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/metadata_utility.h\"\n\n\nextern bool PartitionedTable(Oid relationId);\nextern bool PartitionedTableNoLock(Oid relationId);\nextern bool PartitionTable(Oid relationId);\nextern bool PartitionTableNoLock(Oid relationId);\nextern bool IsChildTable(Oid relationId);\nextern bool IsParentTable(Oid relationId);\nextern Oid PartitionParentOid(Oid partitionOid);\nextern Oid PartitionWithLongestNameRelationId(Oid parentRelationId);\nextern List * PartitionList(Oid parentRelationId);\nextern char * GenerateDetachPartitionCommand(Oid partitionTableId);\nextern List * GenerateDetachPartitionCommandRelationIdList(List *relationIds);\nextern char * GenerateAttachShardPartitionCommand(ShardInterval *shardInterval);\nextern char * GenerateAlterTableAttachPartitionCommand(Oid partitionTableId);\nextern List * GenerateAttachPartitionCommandRelationIdList(List *relationIds);\nextern char * GeneratePartitioningInformation(Oid tableId);\nextern void FixPartitionConstraintsOnWorkers(Oid relationId);\nextern void FixLocalPartitionConstraints(Oid relationId, int64 shardId);\nextern void FixPartitionShardIndexNames(Oid relationId, Oid parentIndexOid);\nextern List * ListShardsUnderParentRelation(Oid relationId);\n\n#endif /* MULTI_PARTITIONING_UTILS_H_ */\n"
  },
  {
    "path": "src/include/distributed/multi_physical_planner.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_physical_planner.h\n *\t  Type and function declarations used in creating the distributed execution\n *\t  plan.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef MULTI_PHYSICAL_PLANNER_H\n#define MULTI_PHYSICAL_PLANNER_H\n\n#include \"postgres.h\"\n\n#include \"c.h\"\n\n#include \"datatype/timestamp.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"utils/array.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/citus_nodes.h\"\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/worker_manager.h\"\n\n\n/* Definitions local to the physical planner */\n#define NON_PRUNABLE_JOIN -1\n#define RESERVED_HASHED_COLUMN_ID MaxAttrNumber\n\nextern int RepartitionJoinBucketCountPerNode;\n\ntypedef enum CitusRTEKind\n{\n\tCITUS_RTE_RELATION = RTE_RELATION,  /* ordinary relation reference */\n\tCITUS_RTE_SUBQUERY = RTE_SUBQUERY,  /* subquery in FROM */\n\tCITUS_RTE_JOIN = RTE_JOIN,          /* join */\n\tCITUS_RTE_FUNCTION = RTE_FUNCTION,  /* function in FROM */\n\tCITUS_RTE_TABLEFUNC = RTE_TABLEFUNC, /* TableFunc(.., column list) */\n\tCITUS_RTE_VALUES = RTE_VALUES,      /* VALUES (<exprlist>), (<exprlist>), ... */\n\tCITUS_RTE_CTE = RTE_CTE,            /* common table expr (WITH list element) */\n\tCITUS_RTE_NAMEDTUPLESTORE = RTE_NAMEDTUPLESTORE, /* tuplestore, e.g. for triggers */\n\tCITUS_RTE_RESULT = RTE_RESULT,      /* RTE represents an empty FROM clause */\n\tCITUS_RTE_SHARD,\n\tCITUS_RTE_REMOTE_QUERY\n} CitusRTEKind;\n\n\n/* Enumeration that defines the partition type for a remote job */\ntypedef enum\n{\n\tPARTITION_INVALID_FIRST = 0,\n\tRANGE_PARTITION_TYPE = 1,\n\tSINGLE_HASH_PARTITION_TYPE = 2,\n\tDUAL_HASH_PARTITION_TYPE = 3\n} PartitionType;\n\n\n/* Enumeration that defines different task types */\ntypedef enum\n{\n\tTASK_TYPE_INVALID_FIRST,\n\tREAD_TASK,\n\tMAP_TASK,\n\tMERGE_TASK,\n\tMAP_OUTPUT_FETCH_TASK,\n\tMERGE_FETCH_TASK,\n\tMODIFY_TASK,\n\tDDL_TASK,\n\tVACUUM_ANALYZE_TASK\n} TaskType;\n\n\n/* Enumeration that defines the task assignment policy to use */\ntypedef enum\n{\n\tTASK_ASSIGNMENT_INVALID_FIRST = 0,\n\tTASK_ASSIGNMENT_GREEDY = 1,\n\tTASK_ASSIGNMENT_ROUND_ROBIN = 2,\n\tTASK_ASSIGNMENT_FIRST_REPLICA = 3\n} TaskAssignmentPolicyType;\n\n\n/* Enumeration that defines different job types */\ntypedef enum\n{\n\tJOB_INVALID_FIRST = 0,\n\tJOIN_MAP_MERGE_JOB = 1,\n\tTOP_LEVEL_WORKER_JOB = 2\n} BoundaryNodeJobType;\n\n\n/* Enumeration that specifies extent of DML modifications */\ntypedef enum RowModifyLevel\n{\n\tROW_MODIFY_NONE = 0,\n\tROW_MODIFY_READONLY = 1,\n\tROW_MODIFY_COMMUTATIVE = 2,\n\tROW_MODIFY_NONCOMMUTATIVE = 3\n} RowModifyLevel;\n\n\n/*\n * LocalPlannedStatement represents a local plan of a shard. The scope\n * for the LocalPlannedStatement is Task.\n */\ntypedef struct LocalPlannedStatement\n{\n\tCitusNode type;\n\n\tuint64 shardId;\n\tuint32 localGroupId;\n\tPlannedStmt *localPlan;\n} LocalPlannedStatement;\n\n\n/*\n * Job represents a logical unit of work that contains one set of data transfers\n * in our physical plan. The physical planner maps each SQL query into one or\n * more jobs depending on the query's complexity, and sets dependencies between\n * these jobs. Each job consists of multiple executable tasks; and these tasks\n * either operate on base shards, or repartitioned tables.\n */\ntypedef struct Job\n{\n\tCitusNode type;\n\tuint64 jobId;\n\tQuery *jobQuery;\n\tList *taskList;\n\tList *dependentJobList;\n\tbool subqueryPushdown;\n\tbool requiresCoordinatorEvaluation; /* only applies to modify jobs */\n\tbool deferredPruning;\n\tConst *partitionKeyValue;\n\n\t/* for local shard queries, we may save the local plan here */\n\tList *localPlannedStatements;\n\n\t/*\n\t * When we evaluate functions and parameters in jobQuery then we\n\t * should no longer send the list of parameters along with the\n\t * query.\n\t */\n\tbool parametersInJobQueryResolved;\n\tuint32 colocationId; /* common colocation group ID of the relations */\n} Job;\n\n\n/* Defines a repartitioning job and holds additional related data. */\ntypedef struct MapMergeJob\n{\n\tJob job;\n\tPartitionType partitionType;\n\tVar *partitionColumn;\n\tuint32 partitionCount;\n\tint sortedShardIntervalArrayLength;\n\tShardInterval **sortedShardIntervalArray; /* only applies to range partitioning */\n\tList *mapTaskList;\n\tList *mergeTaskList;\n} MapMergeJob;\n\ntypedef enum TaskQueryType\n{\n\tTASK_QUERY_NULL,\n\tTASK_QUERY_TEXT,\n\tTASK_QUERY_OBJECT,\n\tTASK_QUERY_TEXT_LIST,\n\tTASK_QUERY_LOCAL_PLAN,\n} TaskQueryType;\n\ntypedef struct LocalCompilation\n{\n\tPlannedStmt *plan; /* the local plan for this task */\n\tQuery *query; /* query to deparse for EXPLAIN ANALYZE or local command logging */\n} LocalCompilation;\n\ntypedef struct TaskQuery\n{\n\tTaskQueryType queryType;\n\n\tunion\n\t{\n\t\t/*\n\t\t * For most queries jobQueryReferenceForLazyDeparsing and/or queryStringLazy is not\n\t\t * NULL. This means we have a single query for all placements.\n\t\t *\n\t\t * If this is not the case, the length of perPlacementQueryStrings is\n\t\t * non-zero and equal to length of taskPlacementList. Like this it can\n\t\t * assign a different query for each placement. We need this flexibility\n\t\t * when a query should return node specific values. For example, on which\n\t\t * node did we succeed storing some result files?\n\t\t *\n\t\t * jobQueryReferenceForLazyDeparsing is only not null when the planner thinks the\n\t\t * query could possibly be locally executed. In that case deparsing+parsing\n\t\t * the query might not be necessary, so we do that lazily.\n\t\t *\n\t\t * jobQueryReferenceForLazyDeparsing should only be set by using SetTaskQueryIfShouldLazyDeparse()\n\t\t */\n\t\tQuery *jobQueryReferenceForLazyDeparsing;\n\n\t\t/*\n\t\t * In almost all cases queryStringLazy should be read only indirectly by\n\t\t * using TaskQueryString(). This will populate the field if only the\n\t\t * jobQueryReferenceForLazyDeparsing field is not NULL.\n\t\t *\n\t\t * This field should only be set by using SetTaskQueryString() (or as a\n\t\t * side effect from TaskQueryString()). Otherwise it might not be in sync\n\t\t * with jobQueryReferenceForLazyDeparsing.\n\t\t */\n\t\tchar *queryStringLazy;\n\n\t\t/*\n\t\t * queryStringList contains query strings. They should be\n\t\t * run sequentially. The concatenated version of this list\n\t\t * will already be set for queryStringLazy, this can be useful\n\t\t * when we want to access each query string.\n\t\t */\n\t\tList *queryStringList;\n\n\t\t/*\n\t\t * For tasks that can be executed locally, this field contains the\n\t\t * local plan for the task. This is only set when the shard that the\n\t\t * task is assigned to is local to the node that executes the task.\n\t\t * The query field is used to deparse the query for EXPLAIN ANALYZE\n\t\t * or local command logging.\n\t\t */\n\t\tLocalCompilation *localCompiled; /* only applies to local tasks */\n\t}data;\n}TaskQuery;\n\nstruct TupleDestination;\n\ntypedef struct Task\n{\n\tCitusNode type;\n\tTaskType taskType;\n\tuint64 jobId;\n\tuint32 taskId;\n\n\t/*\n\t * taskQuery contains query string information. The way we get queryString can be different\n\t * so this is abstracted with taskQuery.\n\t */\n\tTaskQuery taskQuery;\n\n\t/*\n\t * A task can have multiple queries, in which case queryCount will be > 1, and\n\t * taskQuery->queryType == TASK_QUERY_TEXT_LIST.\n\t */\n\tint queryCount;\n\n\tOid anchorDistributedTableId;     /* only applies to insert tasks */\n\tuint64 anchorShardId;       /* only applies to compute tasks */\n\tList *taskPlacementList;    /* only applies to compute tasks */\n\tList *dependentTaskList;     /* only applies to compute tasks */\n\n\tuint32 partitionId;\n\tuint32 upstreamTaskId;         /* only applies to data fetch tasks */\n\tShardInterval *shardInterval;  /* only applies to merge tasks */\n\tbool assignmentConstrained;    /* only applies to merge tasks */\n\n\t/* for merge tasks, this is set to the target list of the map task */\n\tList *mapJobTargetList;\n\n\tchar replicationModel;         /* only applies to modify tasks */\n\n\t/*\n\t * List of struct RelationRowLock. This contains an entry for each\n\t * query identified as a FOR [KEY] UPDATE/SHARE target. Citus\n\t * converts PostgreSQL's RowMarkClause to RelationRowLock in\n\t * RowLocksOnRelations().\n\t */\n\tList *relationRowLockList;\n\n\tbool modifyWithSubquery;\n\n\t/*\n\t * List of struct RelationShard. This represents the mapping of relations\n\t * in the RTE list to shard IDs for a task for the purposes of:\n\t *  - Locking: See AcquireExecutorShardLocks()\n\t *  - Deparsing: See UpdateRelationToShardNames()\n\t *  - Relation Access Tracking\n\t */\n\tList *relationShardList;\n\n\tList *rowValuesLists;          /* rows to use when building multi-row INSERT */\n\n\t/*\n\t * Used only when local execution happens. Indicates that this task is part of\n\t * both local and remote executions. We use \"or\" in the field name because this\n\t * is set to true for both the remote and local tasks generated for such\n\t * executions. The most common example is modifications to reference tables where\n\t * the task splitted into local and remote tasks.\n\t */\n\tbool partiallyLocalOrRemote;\n\n\t/*\n\t * When we evaluate functions and parameters in the query string then\n\t * we should no longer send the list of parameters along with the\n\t * query.\n\t */\n\tbool parametersInQueryStringResolved;\n\n\t/*\n\t * Destination of tuples generated as a result of executing this task. Can be\n\t * NULL, in which case executor might use a default destination.\n\t */\n\tstruct TupleDestination *tupleDest;\n\n\t/*\n\t * totalReceivedTupleData only counts the data for a single placement. So\n\t * for RETURNING DML this is not really correct. This is used by\n\t * EXPLAIN ANALYZE, to display the amount of received bytes. The local execution\n\t * does not increment this value, so only used for remote execution.\n\t */\n\tuint64 totalReceivedTupleData;\n\n\t/*\n\t * EXPLAIN ANALYZE output fetched from worker. This is saved to be used later\n\t * by RemoteExplain().\n\t */\n\tchar *fetchedExplainAnalyzePlan;\n\tint fetchedExplainAnalyzePlacementIndex;\n\n\t/*\n\t * Execution Duration fetched from worker. This is saved to be used later by\n\t * ExplainTaskList().\n\t */\n\tdouble fetchedExplainAnalyzeExecutionDuration;\n\n\t/*\n\t * isLocalTableModification is true if the task is on modifying a local table.\n\t */\n\tbool isLocalTableModification;\n\n\t/*\n\t * Vacuum, create/drop/reindex concurrently cannot be executed in a transaction.\n\t */\n\tbool cannotBeExecutedInTransaction;\n\n\tConst *partitionKeyValue;\n\tint colocationId;\n} Task;\n\n\n/*\n * RangeTableFragment represents a fragment of a range table. This fragment\n * could be a regular shard or a merged table formed in a MapMerge job.\n */\ntypedef struct RangeTableFragment\n{\n\tCitusRTEKind fragmentType;\n\tvoid *fragmentReference;\n\tuint32 rangeTableId;\n} RangeTableFragment;\n\n\n/*\n * JoinSequenceNode represents a range table in an ordered sequence of tables\n * joined together. This representation helps build combinations of all range\n * table fragments during task generation.\n */\ntypedef struct JoinSequenceNode\n{\n\tuint32 rangeTableId;\n\tint32 joiningRangeTableId;\n} JoinSequenceNode;\n\n\n/*\n * ModifyWithSelectMethod represents the method to use for INSERT INTO ... SELECT\n * or MERGE type of queries.\n *\n * Note that there is a third method which is not represented here, which is\n * pushing down the MERGE/INSERT INTO ... SELECT to workers. This method is\n * executed similar to other distributed queries and doesn't need a special\n * execution code, so we don't need to represent it here.\n */\ntypedef enum ModifyWithSelectMethod\n{\n\tMODIFY_WITH_SELECT_VIA_COORDINATOR,\n\tMODIFY_WITH_SELECT_REPARTITION\n} ModifyWithSelectMethod;\n\n\n/*\n * DistributedPlan contains all information necessary to execute a\n * distribute query.\n */\ntypedef struct DistributedPlan\n{\n\tCitusNode type;\n\n\t/* unique identifier of the plan within the session */\n\tuint64 planId;\n\n\t/* specifies nature of modifications in query */\n\tRowModifyLevel modLevel;\n\n\t/*\n\t * specifies whether plan returns results,\n\t * either as a SELECT or a DML which has RETURNING.\n\t */\n\tbool expectResults;\n\n\t/* job tree containing the tasks to be executed on workers */\n\tJob *workerJob;\n\n\t/* local query that merges results from the workers */\n\tQuery *combineQuery;\n\n\t/* query identifier (copied from the top-level PlannedStmt) */\n\tuint64 queryId;\n\n\t/* which relations are accessed by this distributed plan */\n\tList *relationIdList;\n\n\t/* target relation of a modification */\n\tOid targetRelationId;\n\n\t/*\n\t * Modifications performed using the output of a source query via\n\t * the coordinator or repartition.\n\t */\n\tQuery *modifyQueryViaCoordinatorOrRepartition;\n\tPlannedStmt *selectPlanForModifyViaCoordinatorOrRepartition;\n\tModifyWithSelectMethod modifyWithSelectMethod;\n\n\t/*\n\t * If intermediateResultIdPrefix is non-null, the source query\n\t * results are written to a set of intermediate results named\n\t * according to <intermediateResultIdPrefix>_<anchorShardId>.\n\t * That way we can run a distributed modification query which\n\t * requires evaluating source query results at the coordinator.\n\t * Once results are captured in intermediate files, modification\n\t * is done from the intermediate results into the target relation.\n\t *\n\t */\n\tchar *intermediateResultIdPrefix;\n\n\t/* list of subplans to execute before the distributed query */\n\tList *subPlanList;\n\n\t/*\n\t * List of subPlans that are used in the DistributedPlan\n\t * Note that this is different that \"subPlanList\" field which\n\t * contains the subplans generated as part of the DistributedPlan.\n\t *\n\t * On the other hand, usedSubPlanNodeList keeps track of which subPlans\n\t * are used within this distributed plan as a list of\n\t * UsedDistributedSubPlan pointers.\n\t *\n\t * The list may contain duplicates if the subplan is referenced multiple\n\t * times (e.g. a CTE appears in the query tree multiple times).\n\t */\n\tList *usedSubPlanNodeList;\n\n\t/*\n\t * When the query is very simple such that we don't need to call\n\t * standard_planner(). See FastPathRouterQuery() for the definition.\n\t */\n\tbool fastPathRouterPlan;\n\n\t/* number of times this plan has been used (as a prepared statement) */\n\tuint32 numberOfTimesExecuted;\n\n\t/*\n\t * NULL if this a valid plan, an error description otherwise. This will\n\t * e.g. be set if SQL features are present that a planner doesn't support,\n\t * or if prepared statement parameters prevented successful planning.\n\t */\n\tDeferredErrorMessage *planningError;\n\n\t/*\n\t * When performing query execution scenarios that require repartitioning\n\t * the source rows, this field stores the index of the column in the list\n\t * of source rows to be repartitioned for colocation with the target.\n\t */\n\tint sourceResultRepartitionColumnIndex;\n} DistributedPlan;\n\n\n/*\n * SubPlanExplainOutputData Holds the EXPLAIN ANALYZE output and collected\n * statistics for a single task executed by a worker during distributed\n * query execution.\n * explainOutput — raw EXPLAIN ANALYZE output for the task\n * executionDuration — wall‑clock time taken to run the task\n * totalReceivedTupleData — total bytes of tuple data received from the worker\n */\ntypedef struct SubPlanExplainOutputData\n{\n\tchar *explainOutput;\n\tdouble executionDuration;\n\tdouble executionNtuples;\n\tdouble executionNloops;\n\tuint64 totalReceivedTupleData;\n} SubPlanExplainOutputData;\n\n\n/*\n * DistributedSubPlan contains a subplan of a distributed plan. Subplans are\n * executed before the distributed query and their results are written to\n * temporary files. This is used to execute CTEs and subquery joins that\n * cannot be distributed.\n */\ntypedef struct DistributedSubPlan\n{\n\tCitusNode type;\n\n\tuint32 subPlanId;\n\tPlannedStmt *plan;\n\n\t/* EXPLAIN ANALYZE instrumentations */\n\tuint64 bytesSentPerWorker;\n\tuint32 remoteWorkerCount;\n\tdouble durationMillisecs;\n\tbool writeLocalFile;\n\tSubPlanExplainOutputData *totalExplainOutput;\n\tuint32 numTasksOutput; /* actual size of the above array */\n\tdouble ntuples; /* total tuples produced */\n} DistributedSubPlan;\n\n\n/* defines how a subplan is used by a distributed query */\ntypedef enum SubPlanAccessType\n{\n\tSUBPLAN_ACCESS_NONE,\n\tSUBPLAN_ACCESS_LOCAL,\n\tSUBPLAN_ACCESS_REMOTE,\n\tSUBPLAN_ACCESS_ANYWHERE\n} SubPlanAccessType;\n\n\n/*\n * UsedDistributedSubPlan contains information about a subPlan that is used in a\n * distributed plan.\n */\ntypedef struct UsedDistributedSubPlan\n{\n\tCitusNode type;\n\n\t/* subplan used by the distributed query */\n\tchar *subPlanId;\n\n\t/* how the subplan is used by a distributed query */\n\tSubPlanAccessType accessType;\n} UsedDistributedSubPlan;\n\n\n/* OperatorCacheEntry contains information for each element in OperatorCache */\ntypedef struct OperatorCacheEntry\n{\n\t/* cache key consists of typeId, accessMethodId and strategyNumber */\n\tOid typeId;\n\tOid accessMethodId;\n\tint16 strategyNumber;\n\tOid operatorId;\n\tOid operatorClassInputType;\n\tchar typeType;\n} OperatorCacheEntry;\n\n\n/* Named function pointer type for reordering Task lists */\ntypedef List *(*ReorderFunction)(List *);\n\n\n/* Config variable managed via guc.c */\nextern int TaskAssignmentPolicy;\nextern bool EnableUniqueJobIds;\n\n\n/* Function declarations for building physical plans and constructing queries */\nextern DistributedPlan * CreatePhysicalDistributedPlan(MultiTreeRoot *multiTree,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nextern Task * CreateBasicTask(uint64 jobId, uint32 taskId, TaskType taskType,\n\t\t\t\t\t\t\t  char *queryString);\nextern OpExpr * MakeOpExpressionExtended(Var *leftVar, Expr *rightArg,\n\t\t\t\t\t\t\t\t\t\t int16 strategyNumber);\nextern OpExpr * MakeOpExpression(Var *variable, int16 strategyNumber);\nextern Node *  WrapUngroupedVarsInAnyValueAggregate(Node *expression,\n\t\t\t\t\t\t\t\t\t\t\t\t\tList *groupClauseList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tList *targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbool checkExpressionEquality);\n\n/*\n * Function declarations for building, updating constraints and simple operator\n * expression check.\n */\nextern Node * BuildBaseConstraint(Var *column);\nextern void UpdateConstraint(Node *baseConstraint, ShardInterval *shardInterval);\nextern bool BinaryOpExpression(Expr *clause, Node **leftOperand, Node **rightOperand);\n\n/* helper functions */\nextern Var * MakeInt4Column(void);\nextern int CompareShardPlacements(const void *leftElement, const void *rightElement);\nextern int CompareGroupShardPlacements(const void *leftElement, const void *rightElement);\nextern bool ShardIntervalsOverlap(ShardInterval *firstInterval,\n\t\t\t\t\t\t\t\t  ShardInterval *secondInterval);\nextern bool ShardIntervalsOverlapWithParams(Datum firstMin, Datum firstMax,\n\t\t\t\t\t\t\t\t\t\t\tDatum secondMin, Datum secondMax,\n\t\t\t\t\t\t\t\t\t\t\tFmgrInfo *comparisonFunction,\n\t\t\t\t\t\t\t\t\t\t\tOid collation);\nextern bool CoPartitionedTables(Oid firstRelationId, Oid secondRelationId);\nextern ShardInterval ** GenerateSyntheticShardIntervalArray(int partitionCount);\nextern RowModifyLevel RowModifyLevelForQuery(Query *query);\nextern StringInfo ArrayObjectToString(ArrayType *arrayObject,\n\t\t\t\t\t\t\t\t\t  Oid columnType, int32 columnTypeMod);\n\n\n/* function declarations for Task and Task list operations */\nextern bool TasksEqual(const Task *a, const Task *b);\nextern bool TaskListMember(const List *taskList, const Task *task);\nextern List * TaskListDifference(const List *list1, const List *list2);\nextern List * AssignAnchorShardTaskList(List *taskList);\nextern List * FirstReplicaAssignTaskList(List *taskList);\nextern List * RoundRobinAssignTaskList(List *taskList);\nextern List * RoundRobinReorder(List *placementList);\nextern void SetPlacementNodeMetadata(ShardPlacement *placement, WorkerNode *workerNode);\nextern int CompareTasksByTaskId(const void *leftElement, const void *rightElement);\nextern int CompareTasksByExecutionDuration(const void *leftElement, const\n\t\t\t\t\t\t\t\t\t\t   void *rightElement);\n\n/* function declaration for creating Task */\nextern List * QueryPushdownSqlTaskList(Query *query, uint64 jobId,\n\t\t\t\t\t\t\t\t\t   RelationRestrictionContext *\n\t\t\t\t\t\t\t\t\t   relationRestrictionContext,\n\t\t\t\t\t\t\t\t\t   List *prunedRelationShardList, TaskType taskType,\n\t\t\t\t\t\t\t\t\t   bool modifyRequiresCoordinatorEvaluation,\n\t\t\t\t\t\t\t\t\t   DeferredErrorMessage **planningError);\n\nextern bool ModifyLocalTableJob(Job *job);\n\n/* function declarations for managing jobs */\nextern uint64 UniqueJobId(void);\n\n\nextern List * DerivedColumnNameList(uint32 columnCount, uint64 generatingJobId);\nextern RangeTblEntry * DerivedRangeTableEntry(MultiNode *multiNode, List *columnList,\n\t\t\t\t\t\t\t\t\t\t\t  List *tableIdList,\n\t\t\t\t\t\t\t\t\t\t\t  List *funcColumnNames,\n\t\t\t\t\t\t\t\t\t\t\t  List *funcColumnTypes,\n\t\t\t\t\t\t\t\t\t\t\t  List *funcColumnTypeMods,\n\t\t\t\t\t\t\t\t\t\t\t  List *funcCollations);\n\nextern List * FetchEqualityAttrNumsForRTE(Node *quals);\n\n#endif   /* MULTI_PHYSICAL_PLANNER_H */\n"
  },
  {
    "path": "src/include/distributed/multi_progress.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_progress.h\n *    Declarations for public functions and variables used in progress\n *    tracking functions in Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef MULTI_PROGRESS_H\n#define MULTI_PROGRESS_H\n\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"nodes/pg_list.h\"\n#include \"storage/dsm.h\"\n\n\ntypedef struct ProgressMonitorData\n{\n\tuint64 processId;\n\tint stepCount;\n} ProgressMonitorData;\n\n\nextern ProgressMonitorData * CreateProgressMonitor(int stepCount, Size stepSize,\n\t\t\t\t\t\t\t\t\t\t\t\t   dsm_handle *dsmHandle);\nextern void RegisterProgressMonitor(uint64 progressTypeMagicNumber,\n\t\t\t\t\t\t\t\t\tOid relationId,\n\t\t\t\t\t\t\t\t\tdsm_handle dsmHandle);\nextern ProgressMonitorData * GetCurrentProgressMonitor(void);\nextern void FinalizeCurrentProgressMonitor(void);\nextern bool HasProgressMonitor(void);\nextern List * ProgressMonitorList(uint64 commandTypeMagicNumber,\n\t\t\t\t\t\t\t\t  List **attachedDSMSegmentList);\nextern void DetachFromDSMSegments(List *dsmSegmentList);\nextern void * ProgressMonitorSteps(ProgressMonitorData *monitor);\n\n\n#endif /* MULTI_PROGRESS_H */\n"
  },
  {
    "path": "src/include/distributed/multi_router_planner.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_router_planner.h\n *\n * Declarations for public functions and types related to router planning.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef MULTI_ROUTER_PLANNER_H\n#define MULTI_ROUTER_PLANNER_H\n\n#include \"c.h\"\n\n#include \"nodes/parsenodes.h\"\n\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n\n\n/* reserved alias name for UPSERTs */\n#define CITUS_TABLE_ALIAS \"citus_table_alias\"\n\nextern bool EnableRouterExecution;\nextern bool EnableFastPathRouterPlanner;\nextern bool EnableLocalFastPathQueryOptimization;\n\nextern bool EnableNonColocatedRouterQueryPushdown;\n\nextern DistributedPlan * CreateRouterPlan(Query *originalQuery, Query *query,\n\t\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext);\nextern DistributedPlan * CreateModifyPlan(Query *originalQuery, Query *query,\n\t\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext);\nextern DeferredErrorMessage * PlanRouterQuery(Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t  List **placementList, uint64 *anchorShardId,\n\t\t\t\t\t\t\t\t\t\t\t  List **relationShardList,\n\t\t\t\t\t\t\t\t\t\t\t  List **prunedShardIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t  bool replacePrunedQueryWithDummy,\n\t\t\t\t\t\t\t\t\t\t\t  bool *multiShardModifyQuery,\n\t\t\t\t\t\t\t\t\t\t\t  Const **partitionValueConst,\n\t\t\t\t\t\t\t\t\t\t\t  bool *containOnlyLocalTable);\nextern List * RelationShardListForShardIntervalList(List *shardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\t\tbool *shardsPresent);\nextern List * CreateTaskPlacementListForShardIntervals(List *shardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   bool shardsPresent,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   bool generateDummyPlacement,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   bool hasLocalRelation);\nextern List * RouterInsertTaskList(Query *query, bool parametersInQueryResolved,\n\t\t\t\t\t\t\t\t   DeferredErrorMessage **planningError);\nextern Const * ExtractInsertPartitionKeyValue(Query *query);\nextern List * TargetShardIntervalsForRestrictInfo(RelationRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t  restrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t  bool *multiShardQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t  Const **partitionValueConst);\nextern List * PlacementsForWorkersContainingAllShards(List *shardIntervalListList);\nextern List * IntersectPlacementList(List *lhsPlacementList, List *rhsPlacementList);\nextern DeferredErrorMessage * ModifyQuerySupported(Query *queryTree, Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t   bool multiShardQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nextern DeferredErrorMessage * ErrorIfOnConflictNotSupported(Query *queryTree);\nextern List * ShardIntervalOpExpressions(ShardInterval *shardInterval, Index rteIndex);\nextern RelationRestrictionContext * CopyRelationRestrictionContext(\n\tRelationRestrictionContext *oldContext);\n\nextern Oid ExtractFirstCitusTableId(Query *query);\nextern RangeTblEntry * ExtractSelectRangeTableEntry(Query *query);\nextern Oid ModifyQueryResultRelationId(Query *query);\nextern RangeTblEntry * ExtractResultRelationRTE(Query *query);\nextern RangeTblEntry * ExtractResultRelationRTEOrError(Query *query);\nextern RangeTblEntry * ExtractDistributedInsertValuesRTE(Query *query);\nextern bool IsMultiRowInsert(Query *query);\nextern void AddPartitionKeyNotNullFilterToSelect(Query *subqery);\nextern bool UpdateOrDeleteOrMergeQuery(Query *query);\nextern bool IsMergeQuery(Query *query);\n\nextern uint64 GetAnchorShardId(List *relationShardList);\nextern List * TargetShardIntervalForFastPathQuery(Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\t  bool *isMultiShardQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t  Const *inputDistributionKeyValue,\n\t\t\t\t\t\t\t\t\t\t\t\t  Const **outGoingPartitionValueConst);\nextern void GenerateSingleShardRouterTaskList(Job *job,\n\t\t\t\t\t\t\t\t\t\t\t  List *relationShardList,\n\t\t\t\t\t\t\t\t\t\t\t  List *placementList,\n\t\t\t\t\t\t\t\t\t\t\t  uint64 shardId,\n\t\t\t\t\t\t\t\t\t\t\t  bool isLocalTableModification,\n\t\t\t\t\t\t\t\t\t\t\t  bool delayedFastPath);\n\n/*\n * FastPathPlanner is a subset of router planner, that's why we prefer to\n * keep the external function here.\n */extern PlannedStmt * GeneratePlaceHolderPlannedStmt(Query *parse);\n\nextern void FastPathPreprocessParseTree(Query *parse);\nextern PlannedStmt * FastPathPlanner(Query *originalQuery, Query *parse, ParamListInfo\n\t\t\t\t\t\t\t\t\t boundParams);\nextern bool FastPathRouterQuery(Query *query,\n\t\t\t\t\t\t\t\tFastPathRestrictionContext *fastPathContext);\nextern bool JoinConditionIsOnFalse(List *relOptInfo);\nextern Oid ResultRelationOidForQuery(Query *query);\nextern DeferredErrorMessage * TargetlistAndFunctionsSupported(Oid resultRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  FromExpr *joinTree,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Node *quals,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  List *targetList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  CmdType commandType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  List *returningList);\nextern bool NodeIsFieldStore(Node *node);\nextern bool TargetEntryChangesValue(TargetEntry *targetEntry, Var *column,\n\t\t\t\t\t\t\t\t\tFromExpr *joinTree);\nextern bool MasterIrreducibleExpression(Node *expression, bool *varArgument,\n\t\t\t\t\t\t\t\t\t\tbool *badCoalesce);\nextern bool HasDangerousJoinUsing(List *rtableList, Node *jtnode);\nextern Job * RouterJob(Query *originalQuery,\n\t\t\t\t\t   PlannerRestrictionContext *plannerRestrictionContext,\n\t\t\t\t\t   DeferredErrorMessage **planningError);\nextern bool ContainsOnlyLocalOrReferenceTables(RTEListProperties *rteProperties);\nextern RangeTblEntry * ExtractSourceResultRangeTableEntry(Query *query);\nextern void CheckAndBuildDelayedFastPathPlan(DistributedPlanningContext *planContext,\n\t\t\t\t\t\t\t\t\t\t\t DistributedPlan *plan);\n\n#endif /* MULTI_ROUTER_PLANNER_H */\n"
  },
  {
    "path": "src/include/distributed/multi_server_executor.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * multi_server_executor.h\n *\t  Type and function declarations for executing remote jobs from a backend;\n *\t  the ensemble of these jobs form the distributed execution plan.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef MULTI_SERVER_EXECUTOR_H\n#define MULTI_SERVER_EXECUTOR_H\n\n#include \"distributed/multi_physical_planner.h\"\n#include \"distributed/worker_manager.h\"\n\n/* Adaptive executor repartitioning related defines */\n#define WORKER_CREATE_SCHEMA_QUERY \"SELECT worker_create_schema (\" UINT64_FORMAT \", %s);\"\n#define WORKER_REPARTITION_CLEANUP_QUERY \"SELECT worker_repartition_cleanup (\" \\\n\t\tUINT64_FORMAT \\\n\t\t\");\"\n\n\n/* Enumeration that represents distributed executor types */\ntypedef enum\n{\n\tMULTI_EXECUTOR_INVALID_FIRST = 0,\n\tMULTI_EXECUTOR_ADAPTIVE = 1,\n\tMULTI_EXECUTOR_NON_PUSHABLE_INSERT_SELECT = 2,\n\tMULTI_EXECUTOR_NON_PUSHABLE_MERGE_QUERY = 3\n} MultiExecutorType;\n\n\n/* Config variable managed via guc.c */\nextern int RemoteTaskCheckInterval;\nextern int TaskExecutorType;\nextern bool EnableRepartitionJoins;\nextern int MultiTaskQueryLogLevel;\n\n\n/* Function declarations common to more than one executor */\nextern MultiExecutorType JobExecutorType(DistributedPlan *distributedPlan);\n\n#endif /* MULTI_SERVER_EXECUTOR_H */\n"
  },
  {
    "path": "src/include/distributed/namespace_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * namespace_utils.h\n *\t  Utility function declarations related to namespace changes.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef NAMESPACE_UTILS_H\n#define NAMESPACE_UTILS_H\n\nextern int PushEmptySearchPath(void);\nextern void PopEmptySearchPath(int saveNestLevel);\n\n#endif /* NAMESPACE_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/param_utils.h",
    "content": "/*-------------------------------------------------------------------------\n * param_utils.h\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PARAM_UTILS_H\n#define PARAM_UTILS_H\n\nextern bool GetParamsUsedInQuery(Node *expression, Bitmapset **paramBitmap);\nextern void MarkUnreferencedExternParams(Node *expression, ParamListInfo boundParams);\n\n#endif /* PARAM_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_background_job.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_background_job.h\n *\t  definition of the relation that holds the jobs metadata\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_PG_DIST_BACKGROUND_JOB_H\n#define CITUS_PG_DIST_BACKGROUND_JOB_H\n\n/* ----------------\n *      compiler constants for pg_dist_background_job\n * ----------------\n */\n#define Natts_pg_dist_background_job 6\n#define Anum_pg_dist_background_job_job_id 1\n#define Anum_pg_dist_background_job_state 2\n#define Anum_pg_dist_background_job_job_type 3\n#define Anum_pg_dist_background_job_description 4\n#define Anum_pg_dist_background_job_started_at 5\n#define Anum_pg_dist_background_job_finished_at 6\n\n#endif /* CITUS_PG_DIST_BACKGROUND_JOB_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_background_task.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_background_task.h\n *\t  definition of the relation that holds the tasks metadata\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_PG_DIST_BACKGROUND_TASK_H\n#define CITUS_PG_DIST_BACKGROUND_TASK_H\n\n/* ----------------\n *      compiler constants for pg_dist_background_task\n * ----------------\n */\n#define Natts_pg_dist_background_task 10\n#define Anum_pg_dist_background_task_job_id 1\n#define Anum_pg_dist_background_task_task_id 2\n#define Anum_pg_dist_background_task_owner 3\n#define Anum_pg_dist_background_task_pid 4\n#define Anum_pg_dist_background_task_status 5\n#define Anum_pg_dist_background_task_command 6\n#define Anum_pg_dist_background_task_retry_count 7\n#define Anum_pg_dist_background_task_not_before 8\n#define Anum_pg_dist_background_task_message 9\n#define Anum_pg_dist_background_task_nodes_involved 10\n\nextern int GetNodesInvolvedAttrIndexInPgDistBackgroundTask(TupleDesc tupleDesc);\n\n#endif /* CITUS_PG_DIST_BACKGROUND_TASK_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_backrgound_task_depend.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_background_task_depend.h\n *\t  definition of the relation that holds which tasks depend on each\n *\t  other.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_PG_DIST_BACKGROUND_TASK_DEPEND_H\n#define CITUS_PG_DIST_BACKGROUND_TASK_DEPEND_H\n\ntypedef struct FormData_pg_dist_background_task_depend\n{\n\tint64 job_id;\n\tint64 task_id;\n\tint64 depends_on;\n#ifdef CATALOG_VARLEN           /* variable-length fields start here */\n#endif\n} FormData_pg_dist_background_task_depend;\ntypedef FormData_pg_dist_background_task_depend *Form_pg_dist_background_task_depend;\n\n\n/* ----------------\n *      compiler constants for pg_dist_background_task_depend\n * ----------------\n */\n#define Natts_pg_dist_background_task_depend 3\n#define Anum_pg_dist_background_task_depend_job_id 1\n#define Anum_pg_dist_background_task_depend_task_id 2\n#define Anum_pg_dist_background_task_depend_depends_on 3\n\n#endif /* CITUS_PG_DIST_BACKGROUND_TASK_DEPEND_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_cleanup.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_cleanup.h\n *\t  definition of the relation that holds the resources to be cleaned up\n *\t  in cluster (pg_dist_cleanup).\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_CLEANUP_H\n#define PG_DIST_CLEANUP_H\n\n/* ----------------\n *      compiler constants for pg_dist_cleanup\n * ----------------\n */\n\n#define Natts_pg_dist_cleanup 6\n#define Anum_pg_dist_cleanup_record_id 1\n#define Anum_pg_dist_cleanup_operation_id 2\n#define Anum_pg_dist_cleanup_object_type 3\n#define Anum_pg_dist_cleanup_object_name 4\n#define Anum_pg_dist_cleanup_node_group_id 5\n#define Anum_pg_dist_cleanup_policy_type 6\n\n#define PG_CATALOG \"pg_catalog\"\n#define PG_DIST_CLEANUP \"pg_dist_cleanup\"\n#define OPERATIONID_SEQUENCE_NAME \"pg_dist_operationid_seq\"\n#define CLEANUPRECORDID_SEQUENCE_NAME \"pg_dist_cleanup_recordid_seq\"\n\n#endif /* PG_DIST_CLEANUP_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_colocation.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_colocation.h\n *\t  definition of the relation that holds the colocation information on the\n *\t  cluster (pg_dist_colocation).\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_COLOCATION_H\n#define PG_DIST_COLOCATION_H\n\n/* ----------------\n *\t\tpg_dist_colocation definition.\n * ----------------\n */\ntypedef struct FormData_pg_dist_colocation\n{\n\tuint32 colocationid;\n\tuint32 shardcount;\n\tuint32 replicationfactor;\n\tOid distributioncolumntype;\n\tOid distributioncolumncollation;\n} FormData_pg_dist_colocation;\n\n/* ----------------\n *      Form_pg_dist_colocation corresponds to a pointer to a tuple with\n *      the format of pg_dist_colocation relation.\n * ----------------\n */\ntypedef FormData_pg_dist_colocation *Form_pg_dist_colocation;\n\n/* ----------------\n *      compiler constants for pg_dist_colocation\n * ----------------\n */\n#define Natts_pg_dist_colocation 5\n#define Anum_pg_dist_colocation_colocationid 1\n#define Anum_pg_dist_colocation_shardcount 2\n#define Anum_pg_dist_colocation_replicationfactor 3\n#define Anum_pg_dist_colocation_distributioncolumntype 4\n#define Anum_pg_dist_colocation_distributioncolumncollation 5\n\n#define COLOCATIONID_SEQUENCE_NAME \"pg_dist_colocationid_seq\"\n\n\n#endif /* PG_DIST_COLOCATION_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_local_group.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_local_group.h\n *\t  definition of the relation that holds the local group id (pg_dist_local_group).\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_LOCAL_GROUP_H\n#define PG_DIST_LOCAL_GROUP_H\n\n/* ----------------\n *\t\tpg_dist_local_group definition.\n * ----------------\n */\ntypedef struct FormData_pg_dist_local_group\n{\n\tint groupid;\n} FormData_pg_dist_local_group;\n\n/* ----------------\n *      FormData_pg_dist_local_group corresponds to a pointer to a tuple with\n *      the format of pg_dist_local_group relation.\n * ----------------\n */\ntypedef FormData_pg_dist_local_group *Form_pg_dist_local_group;\n\n/* ----------------\n *      compiler constants for pg_dist_local_group\n * ----------------\n */\n#define Natts_pg_dist_local_group 1\n#define Anum_pg_dist_local_groupid 1\n\n#endif /* PG_DIST_LOCAL_GROUP_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_node.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_node.h\n *\t  definition of the relation that holds the nodes on the cluster (pg_dist_node).\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_NODE_H\n#define PG_DIST_NODE_H\n\n/* ----------------\n *      compiler constants for pg_dist_node\n * ----------------\n *\n *  n.b. citus_add_node, citus_add_inactive_node, and citus_activate_node all\n *  directly return pg_dist_node tuples. This means their definitions (and\n *  in particular their OUT parameters) must be changed whenever the definition of\n *  pg_dist_node changes.\n */\n#define Natts_pg_dist_node 13\n#define Anum_pg_dist_node_nodeid 1\n#define Anum_pg_dist_node_groupid 2\n#define Anum_pg_dist_node_nodename 3\n#define Anum_pg_dist_node_nodeport 4\n#define Anum_pg_dist_node_noderack 5\n#define Anum_pg_dist_node_hasmetadata 6\n#define Anum_pg_dist_node_isactive 7\n#define Anum_pg_dist_node_noderole 8\n#define Anum_pg_dist_node_nodecluster 9\n#define Anum_pg_dist_node_metadatasynced 10\n#define Anum_pg_dist_node_shouldhaveshards 11\n#define Anum_pg_dist_node_nodeisclone 12\n#define Anum_pg_dist_node_nodeprimarynodeid 13\n\n#define GROUPID_SEQUENCE_NAME \"pg_dist_groupid_seq\"\n#define NODEID_SEQUENCE_NAME \"pg_dist_node_nodeid_seq\"\n\n#endif /* PG_DIST_NODE_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_node_metadata.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_node_metadata.h\n *\t  definition of the relation that holds the metadata table (pg_dist_node_metadata).\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_NODE_METADATA_H\n#define PG_DIST_NODE_METADATA_H\n\n\n/* ----------------\n *      compiler constants for pg_dist_node_metadata\n * ----------------\n */\n#define Natts_pg_dist_node_metadata 1\n#define Anum_pg_dist_node_metadata_metadata 1\n\nextern int FindCoordinatorNodeId(void);\n\n#endif /* PG_DIST_NODE_METADATA_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_partition.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_partition.h\n *\t  definition of the system \"remote partition\" relation (pg_dist_partition).\n *\n * This table keeps metadata on logical tables that the user requested remote\n * partitioning for (smaller physical tables that we partition data to are\n * handled in another system catalog).\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_PARTITION_H\n#define PG_DIST_PARTITION_H\n\n/* ----------------\n *\t\tpg_dist_partition definition.\n * ----------------\n */\ntypedef struct FormData_pg_dist_partition\n{\n\tOid logicalrelid;    /* logical relation id; references pg_class oid */\n\tchar partmethod;     /* partition method; see codes below */\n#ifdef CATALOG_VARLEN    /* variable-length fields start here */\n\ttext partkey;        /* partition key expression */\n\tuint32 colocationid; /* id of the co-location group of particular table belongs to */\n\tchar repmodel;       /* replication model; see codes below */\n#endif\n\tbool autoconverted;\n} FormData_pg_dist_partition;\n\n/* ----------------\n *      Form_pg_dist_partitions corresponds to a pointer to a tuple with\n *      the format of pg_dist_partitions relation.\n * ----------------\n */\ntypedef FormData_pg_dist_partition *Form_pg_dist_partition;\n\n/* ----------------\n *      compiler constants for pg_dist_partitions\n * ----------------\n */\n#define Natts_pg_dist_partition 6\n#define Anum_pg_dist_partition_logicalrelid 1\n#define Anum_pg_dist_partition_partmethod 2\n#define Anum_pg_dist_partition_partkey 3\n#define Anum_pg_dist_partition_colocationid 4\n#define Anum_pg_dist_partition_repmodel 5\n#define Anum_pg_dist_partition_autoconverted 6\n\n/* valid values for partmethod include append, hash, and range */\n#define DISTRIBUTE_BY_APPEND 'a'\n#define DISTRIBUTE_BY_HASH 'h'\n#define DISTRIBUTE_BY_RANGE 'r'\n#define DISTRIBUTE_BY_NONE 'n'\n#define REDISTRIBUTE_BY_HASH 'x'\n#define DISTRIBUTE_BY_INVALID '\\0'\n\n/*\n * Valid values for repmodel are 'c' for coordinator, 's' for streaming\n * and 't' for two-phase-commit. We also use an invalid replication model\n * ('i') for distinguishing uninitialized variables where necessary.\n */\n#define REPLICATION_MODEL_COORDINATOR 'c'\n#define REPLICATION_MODEL_STREAMING 's'\n#define REPLICATION_MODEL_2PC 't'\n#define REPLICATION_MODEL_INVALID 'i'\n\n\n#endif   /* PG_DIST_PARTITION_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_placement.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_placement.h\n *\t  definition of the \"server\" relation (pg_dist_placement).\n *\n * This table keeps information on remote shards and their whereabouts on the\n * coordinator node. The table's contents are updated and used as follows: (i) the\n * worker nodes send periodic reports about the shards they contain, and (ii)\n * the coordinator reconciles these shard reports, and determines outdated, under-\n * and over-replicated shards.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_PLACEMENT_H\n#define PG_DIST_PLACEMENT_H\n\n/* ----------------\n *\t\tpg_dist_placement definition.\n * ----------------\n */\ntypedef struct FormData_pg_dist_placement\n{\n\tint64 placementid;          /* global placementId on remote node */\n\tint64 shardid;              /* global shardId on remote node */\n\tint32 shardstate;           /* shard state on remote node; see ShardState */\n\tint64 shardlength;          /* shard length on remote node; stored as bigint */\n\tint32 groupid;              /* the group the shard is placed on */\n} FormData_pg_dist_placement;\n\n/* ----------------\n *\t\tForm_pg_dist_placement corresponds to a pointer to a tuple with\n *\t\tthe format of pg_dist_placement relation.\n * ----------------\n */\ntypedef FormData_pg_dist_placement *Form_pg_dist_placement;\n\n/* ----------------\n *\t\tcompiler constants for pg_dist_placement\n * ----------------\n */\n#define Natts_pg_dist_placement 5\n#define Anum_pg_dist_placement_placementid 1\n#define Anum_pg_dist_placement_shardid 2\n#define Anum_pg_dist_placement_shardstate 3\n#define Anum_pg_dist_placement_shardlength 4\n#define Anum_pg_dist_placement_groupid 5\n\n\n#endif   /* PG_DIST_PLACEMENT_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_rebalance_strategy.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_rebalance_strategy.h\n *\t  definition of the \"rebalance strategy\" relation (pg_dist_rebalance_strategy).\n *\n * This table contains all the available strategies for rebalancing.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_REBALANCE_STRATEGY_H\n#define PG_DIST_REBALANCE_STRATEGY_H\n\n#include \"postgres.h\"\n\n\n/* ----------------\n *\t\tpg_dist_shard definition.\n * ----------------\n */\ntypedef struct FormData_pg_dist_rebalance_strategy\n{\n\tNameData name;        /* user readable name of the strategy */\n\tbool default_strategy;         /* if this strategy is the default strategy */\n\tOid shardCostFunction;         /* function to calculate the shard cost */\n\tOid nodeCapacityFunction;        /* function to get the capacity of a node */\n\tOid shardAllowedOnNodeFunction;  /* function to check if shard is allowed on node */\n\tfloat4 defaultThreshold;         /* default threshold that is used */\n\tfloat4 minimumThreshold;         /* minimum threshold that is allowed */\n\tfloat4 improvementThreshold;     /* the shard size threshold that is used */\n} FormData_pg_dist_rebalance_strategy;\n\n/* ----------------\n *      Form_pg_dist_shards corresponds to a pointer to a tuple with\n *      the format of pg_dist_shards relation.\n * ----------------\n */\ntypedef FormData_pg_dist_rebalance_strategy *Form_pg_dist_rebalance_strategy;\n\n/* ----------------\n *      compiler constants for pg_dist_rebalance_strategy\n * ----------------\n */\n#define Natts_pg_dist_rebalance_strategy 7\n#define Anum_pg_dist_rebalance_strategy_name 1\n#define Anum_pg_dist_rebalance_strategy_default_strategy 2\n#define Anum_pg_dist_rebalance_strategy_shard_cost_function 3\n#define Anum_pg_dist_rebalance_strategy_node_capacity_function 4\n#define Anum_pg_dist_rebalance_strategy_shard_allowed_on_node_function 5\n#define Anum_pg_dist_rebalance_strategy_default_threshold 6\n#define Anum_pg_dist_rebalance_strategy_minimum_threshold 7\n\n#endif   /* PG_DIST_REBALANCE_STRATEGY_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_schema.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_schema.h\n *\t  definition of the system catalog for the schemas used for schema-based\n *\t  sharding in Citus.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_SCHEMA_H\n#define PG_DIST_SCHEMA_H\n\n#include \"postgres.h\"\n\n\n/* ----------------\n *\t\tpg_dist_schema definition.\n * ----------------\n */\ntypedef struct FormData_pg_dist_schema\n{\n\tOid schemaid;\n\tuint32 colocationid;\n} FormData_pg_dist_schema;\n\n/* ----------------\n *      Form_pg_dist_schema corresponds to a pointer to a tuple with\n *      the format of pg_dist_schema relation.\n * ----------------\n */\ntypedef FormData_pg_dist_schema *Form_pg_dist_schema;\n\n/* ----------------\n *      compiler constants for pg_dist_schema\n * ----------------\n */\n#define Natts_pg_dist_schema 2\n#define Anum_pg_dist_schema_schemaid 1\n#define Anum_pg_dist_schema_colocationid 2\n\n#endif /* PG_DIST_SCHEMA_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_shard.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_shard.h\n *\t  definition of the \"shard\" relation (pg_dist_shard).\n *\n * This table maps logical tables to their remote partitions (from this point\n * on, we use the terms remote partition and shard interchangeably). All changes\n * concerning the creation, deletion, merging, and split of remote partitions\n * reference this table.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_SHARD_H\n#define PG_DIST_SHARD_H\n\n/* ----------------\n *\t\tpg_dist_shard definition.\n * ----------------\n */\ntypedef struct FormData_pg_dist_shard\n{\n\tOid logicalrelid;         /* logical relation id; references pg_class oid */\n\tint64 shardid;            /* global shardId representing remote partition */\n\tchar shardstorage;        /* shard storage type; see codes below */\n#ifdef CATALOG_VARLEN           /* variable-length fields start here */\n\ttext shardalias_DROPPED;      /* dropped column, not in use */\n\ttext shardminvalue;        /* partition key's minimum value in shard */\n\ttext shardmaxvalue;        /* partition key's maximum value in shard */\n#endif\n} FormData_pg_dist_shard;\n\n/* ----------------\n *      Form_pg_dist_shards corresponds to a pointer to a tuple with\n *      the format of pg_dist_shards relation.\n * ----------------\n */\ntypedef FormData_pg_dist_shard *Form_pg_dist_shard;\n\n/* ----------------\n *      compiler constants for pg_dist_shards\n * ----------------\n */\n#define Natts_pg_dist_shard 6\n#define Anum_pg_dist_shard_logicalrelid 1\n#define Anum_pg_dist_shard_shardid 2\n#define Anum_pg_dist_shard_shardstorage 3\n#define Anum_pg_dist_shard_shardalias_DROPPED 4\n#define Anum_pg_dist_shard_shardminvalue 5\n#define Anum_pg_dist_shard_shardmaxvalue 6\n\n/*\n * Valid values for shard storage types include foreign table, (standard) table\n * and columnar table.\n */\n#define SHARD_STORAGE_FOREIGN 'f'\n#define SHARD_STORAGE_TABLE 't'\n#define SHARD_STORAGE_VIRTUAL 'v'\n\n\n#endif   /* PG_DIST_SHARD_H */\n"
  },
  {
    "path": "src/include/distributed/pg_dist_transaction.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_dist_transaction.h\n *\t  definition of the \"transaction\" relation (pg_dist_transaction).\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_DIST_TRANSACTION_H\n#define PG_DIST_TRANSACTION_H\n\n\n/* ----------------\n *\t\tpg_dist_transaction definition.\n * ----------------\n */\ntypedef struct FormData_pg_dist_transaction\n{\n\tint32 groupid;             /* id of the replication group */\n\ttext gid;                  /* global transaction identifier */\n} FormData_pg_dist_transaction;\n\n\n/* ----------------\n *      Form_pg_dist_transactions corresponds to a pointer to a tuple with\n *      the format of pg_dist_transactions relation.\n * ----------------\n */\ntypedef FormData_pg_dist_transaction *Form_pg_dist_transaction;\n\n\n/* ----------------\n *      compiler constants for pg_dist_transaction\n * ----------------\n */\n#define Natts_pg_dist_transaction 3\n#define Anum_pg_dist_transaction_groupid 1\n#define Anum_pg_dist_transaction_gid 2\n#define Anum_pg_dist_transaction_outerxid 3\n\nextern int GetOuterXidAttrIndexInPgDistTransaction(TupleDesc tupleDesc);\n\n\n#endif   /* PG_DIST_TRANSACTION_H */\n"
  },
  {
    "path": "src/include/distributed/placement_access.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * placement_access.h\n *      Declarations of the structs and functions used in generating the\n *      placement accesses for distributed query execution.\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef PLACEMENT_ACCESS_H\n#define PLACEMENT_ACCESS_H\n\n#include \"postgres.h\"\n\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/multi_physical_planner.h\"\n\n/* forward declare, to avoid dependency on ShardPlacement definition */\nstruct ShardPlacement;\n\n/* represents the way in which a placement is accessed */\ntypedef enum ShardPlacementAccessType\n{\n\t/* read from placement */\n\tPLACEMENT_ACCESS_SELECT,\n\n\t/* modify rows in placement */\n\tPLACEMENT_ACCESS_DML,\n\n\t/* modify placement schema */\n\tPLACEMENT_ACCESS_DDL\n} ShardPlacementAccessType;\n\n/* represents access to a placement */\ntypedef struct ShardPlacementAccess\n{\n\t/* placement that is accessed */\n\tstruct ShardPlacement *placement;\n\n\t/* the way in which the placement is accessed */\n\tShardPlacementAccessType accessType;\n} ShardPlacementAccess;\n\n\nextern List * PlacementAccessListForTask(Task *task, ShardPlacement *taskPlacement);\nextern ShardPlacementAccess * CreatePlacementAccess(ShardPlacement *placement,\n\t\t\t\t\t\t\t\t\t\t\t\t\tShardPlacementAccessType accessType);\n\n#endif /* PLACEMENT_ACCESS_H */\n"
  },
  {
    "path": "src/include/distributed/placement_connection.h",
    "content": "/*-------------------------------------------------------------------------\n * placement_connection.h\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PLACEMENT_CONNECTION_H\n#define PLACEMENT_CONNECTION_H\n\n\n#include \"distributed/connection_management.h\"\n#include \"distributed/placement_access.h\"\n\n\nextern MultiConnection * GetPlacementConnection(uint32 flags,\n\t\t\t\t\t\t\t\t\t\t\t\tstruct ShardPlacement *placement,\n\t\t\t\t\t\t\t\t\t\t\t\tconst char *userName);\nextern MultiConnection * StartPlacementConnection(uint32 flags,\n\t\t\t\t\t\t\t\t\t\t\t\t  struct ShardPlacement *placement,\n\t\t\t\t\t\t\t\t\t\t\t\t  const char *userName);\nextern MultiConnection *  GetConnectionIfPlacementAccessedInXact(int flags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t placementAccessList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t const char *userName);\nextern MultiConnection * StartPlacementListConnection(uint32 flags,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *placementAccessList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  const char *userName);\nextern ShardPlacementAccess * CreatePlacementAccess(ShardPlacement *placement,\n\t\t\t\t\t\t\t\t\t\t\t\t\tShardPlacementAccessType accessType);\nextern void AssignPlacementListToConnection(List *placementAccessList,\n\t\t\t\t\t\t\t\t\t\t\tMultiConnection *connection);\n\nextern void ResetPlacementConnectionManagement(void);\nextern void ErrorIfPostCommitFailedShardPlacements(void);\n\nextern void CloseShardPlacementAssociation(struct MultiConnection *connection);\nextern void ResetShardPlacementAssociation(struct MultiConnection *connection);\n\nextern void InitPlacementConnectionManagement(void);\n\nextern bool ConnectionModifiedPlacement(MultiConnection *connection);\nextern bool UseConnectionPerPlacement(void);\n\n#endif /* PLACEMENT_CONNECTION_H */\n"
  },
  {
    "path": "src/include/distributed/priority.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * priority.h\n *\t  Shared declarations for managing CPU priority.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_PRIORITY_H\n#define CITUS_PRIORITY_H\n\n#include \"c.h\"\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"storage/fd.h\"\n\nextern int CpuPriority;\nextern int CpuPriorityLogicalRepSender;\nextern int MaxHighPriorityBackgroundProcesess;\n\n#define CPU_PRIORITY_INHERIT 1234\n\n/* Function declarations for transmitting files between two nodes */\nextern void SetOwnPriority(int priority);\nextern int GetOwnPriority(void);\n\n\n#endif   /* CITUS_PRIORITY_H */\n"
  },
  {
    "path": "src/include/distributed/query_colocation_checker.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * query_colocation_checker.h\n *\t  General Citus planner code.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef QUERY_COLOCATION_CHECKER_H\n#define QUERY_COLOCATION_CHECKER_H\n\n\n#include \"nodes/parsenodes.h\"\n#include \"nodes/primnodes.h\"\n\n#include \"distributed/distributed_planner.h\"\n\n\n/*\n * ColocatedJoinChecker is a helper structure that is used to decide\n * whether any subqueries should be recursively planned due joins non\n * colocated joins.\n */\ntypedef struct ColocatedJoinChecker\n{\n\tQuery *subquery;\n\tList *anchorAttributeEquivalences;\n\tList *anchorRelationRestrictionList;\n\tPlannerRestrictionContext *subqueryPlannerRestriction;\n} ColocatedJoinChecker;\n\n\nextern ColocatedJoinChecker CreateColocatedJoinChecker(Query *subquery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t   restrictionContext);\nextern bool SubqueryColocated(Query *subquery, ColocatedJoinChecker *context);\nextern Query * WrapRteRelationIntoSubquery(RangeTblEntry *rteRelation,\n\t\t\t\t\t\t\t\t\t\t   List *requiredAttributes,\n\t\t\t\t\t\t\t\t\t\t   RTEPermissionInfo *perminfo);\nextern List * CreateAllTargetListForRelation(Oid relationId, List *requiredAttributes);\nextern List * CreateFilteredTargetListForRelation(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\t  List *requiredAttributes);\n\n#endif /* QUERY_COLOCATION_CHECKER_H */\n"
  },
  {
    "path": "src/include/distributed/query_pushdown_planning.h",
    "content": "/*-------------------------------------------------------------------------\n * query_pushdown_planning.h\n *\n * Copyright (c) Citus Data, Inc.\n *    Function declarations used in query pushdown logic.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef QUERY_PUSHDOWN_PLANNING\n#define QUERY_PUSHDOWN_PLANNING\n\n#include \"postgres.h\"\n\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/multi_logical_planner.h\"\n#include \"distributed/multi_physical_planner.h\"\n\n\n/* Config variables managed via guc.c */\nextern bool SubqueryPushdown;\nextern int ValuesMaterializationThreshold;\nextern bool AllowAggregateWorkerCombineOnInternalTypes;\n\n\nextern bool CanPushdownSubquery(Query *subqueryTree, bool outerMostQueryHasLimit);\nextern bool ShouldUseSubqueryPushDown(Query *originalQuery, Query *rewrittenQuery,\n\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t  plannerRestrictionContext);\nextern bool JoinTreeContainsSubquery(Query *query);\nextern bool IsNodeSubquery(Node *node);\nextern bool HasEmptyJoinTree(Query *query);\nextern bool WhereOrHavingClauseContainsSubquery(Query *query);\nextern bool TargetListContainsSubquery(List *targetList);\nextern bool SafeToPushdownWindowFunction(Query *query, StringInfo *errorDetail);\nextern MultiNode * SubqueryMultiNodeTree(Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t Query *queryTree,\n\t\t\t\t\t\t\t\t\t\t PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\nextern DeferredErrorMessage * DeferErrorIfUnsupportedSubqueryPushdown(Query *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  PlannerRestrictionContext\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  bool plannerPhase);\nextern DeferredErrorMessage * DeferErrorIfCannotPushdownSubquery(Query *subqueryTree,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t bool\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t outerMostQueryHasLimit);\nextern DeferredErrorMessage * DeferErrorIfUnsupportedUnionQuery(Query *queryTree);\nextern bool IsJsonTableRTE(RangeTblEntry *rte);\nextern bool IsOuterJoinExpr(Node *node);\nextern void FlattenGroupExprs(Query *query);\n\n#endif /* QUERY_PUSHDOWN_PLANNING_H */\n"
  },
  {
    "path": "src/include/distributed/query_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * query_utils.h\n *\n * Declarations for query-walker utility functions and related types.\n *\n * Copyright (c), Citus Data, Inc.\n *\n **---------------------------------------------------------------------------\n */\n#ifndef QUERY_UTILS_H\n#define QUERY_UTILS_H\n\n#include \"postgres.h\"\n\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n\n/* Enum to define execution flow of ExtractRangeTableList */\ntypedef enum ExtractRangeTableMode\n{\n\tEXTRACT_RELATION_ENTRIES, /* inclduding local, foreign and partitioned tables */\n\tEXTRACT_ALL_ENTRIES\n} ExtractRangeTableMode;\n\n/* Struct to pass rtable list and execution flow flag to query_walker_tree */\ntypedef struct ExtractRangeTableWalkerContext\n{\n\tList **rangeTableList;\n\tExtractRangeTableMode walkerMode;\n} ExtractRangeTableWalkerContext;\n\n/* Function declarations for query-walker utility functions */\nextern bool ExtractRangeTableList(Node *node, ExtractRangeTableWalkerContext *context);\n\n/* Below two functions wrap ExtractRangeTableList function to determine the execution flow */\nextern bool ExtractRangeTableRelationWalker(Node *node, List **rangeTableList);\nextern bool ExtractRangeTableEntryWalker(Node *node, List **rangeTableList);\n\nextern bool ExtractRangeTableIndexWalker(Node *node, List **rangeTableIndexList);\n\n#endif /* QUERY_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/recursive_planning.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * recursive_planning.h\n *\t  General Citus planner code.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef RECURSIVE_PLANNING_H\n#define RECURSIVE_PLANNING_H\n\n#include \"nodes/pathnodes.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n\n#include \"pg_version_constants.h\"\n\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/errormessage.h\"\n#include \"distributed/log_utils.h\"\n#include \"distributed/relation_restriction_equivalence.h\"\n\nextern bool EnableRecurringOuterJoinPushdown;\nextern bool EnableOuterJoinsWithPseudoconstantQualsPrePG17;\ntypedef struct RecursivePlanningContextInternal RecursivePlanningContext;\n\ntypedef struct RangeTblEntryIndex\n{\n\tRangeTblEntry *rangeTableEntry;\n\tIndex rteIndex;\n}RangeTblEntryIndex;\n\nextern PlannerRestrictionContext * GetPlannerRestrictionContext(RecursivePlanningContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trecursivePlanningContext);\nextern List * GenerateSubplansForSubqueriesAndCTEs(uint64 planId, Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t   RouterPlanType routerPlan);\nextern char * GenerateResultId(uint64 planId, uint32 subPlanId);\nextern Query * BuildSubPlanResultQuery(List *targetEntryList, List *columnAliasList,\n\t\t\t\t\t\t\t\t\t   char *resultId);\nextern Query * BuildReadIntermediateResultsArrayQuery(List *targetEntryList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *columnAliasList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *resultIdList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  bool useBinaryCopyFormat);\nextern Query * BuildEmptyResultQuery(List *targetEntryList, char *resultId);\nextern bool GeneratingSubplans(void);\nextern bool ContainsLocalTableDistributedTableJoin(List *rangeTableList);\nextern void ReplaceRTERelationWithRteSubquery(RangeTblEntry *rangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t\t  List *requiredAttrNumbers,\n\t\t\t\t\t\t\t\t\t\t\t  RecursivePlanningContext *context,\n\t\t\t\t\t\t\t\t\t\t\t  RTEPermissionInfo *perminfo);\nextern bool IsRecursivelyPlannableRelation(RangeTblEntry *rangeTableEntry);\nextern bool IsRelationLocalTableOrMatView(Oid relationId);\nextern bool ContainsReferencesToOuterQuery(Query *query);\nextern void UpdateVarNosInNode(Node *node, Index newVarNo);\nextern bool CanPushdownRecurringOuterJoinExtended(JoinExpr *joinExpr, Query *query,\n\t\t\t\t\t\t\t\t\t\t\t\t  int *outerRtIndex,\n\t\t\t\t\t\t\t\t\t\t\t\t  RangeTblEntry **outerRte,\n\t\t\t\t\t\t\t\t\t\t\t\t  RangeTblEntry **distRte,\n\t\t\t\t\t\t\t\t\t\t\t\t  int *attnum);\nbool ResolveBaseVarFromSubquery(Var *var, Query *query, Var **baseVar,\n\t\t\t\t\t\t\t\tRangeTblEntry **baseRte);\n#endif /* RECURSIVE_PLANNING_H */\n"
  },
  {
    "path": "src/include/distributed/reference_table_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * reference_table_utils.h\n *\n * Declarations for public utility functions related to reference tables.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef REFERENCE_TABLE_UTILS_H_\n#define REFERENCE_TABLE_UTILS_H_\n\n#include \"postgres.h\"\n\n#include \"listutils.h\"\n\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_sync.h\"\n\nextern void EnsureReferenceTablesExistOnAllNodes(void);\nextern void EnsureReferenceTablesExistOnAllNodesExtended(char transferMode);\nextern int64 * ScheduleTasksToParallelCopyReferenceTablesOnAllMissingNodes(int64 jobId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   char\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   transferMode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   int *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   nDependTasks);\nextern bool HasNodesWithMissingReferenceTables(List **referenceTableList);\nextern uint32 CreateReferenceTableColocationId(void);\nextern uint32 GetReferenceTableColocationId(void);\nextern List * GetAllReplicatedTableList(void);\nextern List * ReplicatedPlacementsForNodeGroup(int32 groupId);\nextern char * DeleteShardPlacementCommand(uint64 placementId);\nextern void DeleteAllReplicatedTablePlacementsFromNodeGroup(int32 groupId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbool localOnly);\nextern void DeleteAllReplicatedTablePlacementsFromNodeGroupViaMetadataContext(\n\tMetadataSyncContext *context, int32 groupId, bool localOnly);\nextern int CompareOids(const void *leftElement, const void *rightElement);\nextern void ReplicateAllReferenceTablesToNode(WorkerNode *workerNode);\nextern void ErrorIfNotAllNodesHaveReferenceTableReplicas(List *workerNodeList);\n\n#endif /* REFERENCE_TABLE_UTILS_H_ */\n"
  },
  {
    "path": "src/include/distributed/relation_access_tracking.h",
    "content": "/*\n * relation_access_tracking.h\n *\n * Function declartions for transaction access tracking.\n *\n * Copyright (c) Citus Data, Inc.\n */\n\n#ifndef RELATION_ACCESS_TRACKING_H_\n#define RELATION_ACCESS_TRACKING_H_\n\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_physical_planner.h\" /* access Task struct */\n#include \"distributed/placement_connection.h\"\n\n/* Config variables managed via guc.c */\nextern bool EnforceForeignKeyRestrictions;\n\n\n/* forward declare, to avoid dependency on ShardPlacement definition */\nstruct ShardPlacement;\n\ntypedef enum RelationAccessMode\n{\n\tRELATION_NOT_ACCESSED,\n\n\t/* only valid for reference tables */\n\tRELATION_REFERENCE_ACCESSED,\n\n\t/*\n\t * Only valid for distributed tables and set\n\t * if table is accessed in parallel mode\n\t */\n\tRELATION_PARALLEL_ACCESSED\n} RelationAccessMode;\n\nextern void InitRelationAccessHash(void);\nextern void ResetRelationAccessHash(void);\nextern void RecordRelationAccessIfNonDistTable(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t   ShardPlacementAccessType accessType);\nextern void RecordParallelRelationAccessForTaskList(List *taskList);\nextern void RecordParallelSelectAccess(Oid relationId);\nextern void RecordParallelModifyAccess(Oid relationId);\nextern void RecordParallelDDLAccess(Oid relationId);\nextern RelationAccessMode GetRelationDDLAccessMode(Oid relationId);\nextern RelationAccessMode GetRelationDMLAccessMode(Oid relationId);\nextern RelationAccessMode GetRelationSelectAccessMode(Oid relationId);\nextern bool ShouldRecordRelationAccess(void);\nextern bool ParallelQueryExecutedInTransaction(void);\n\n\n#endif /* RELATION_ACCESS_TRACKING_H_ */\n"
  },
  {
    "path": "src/include/distributed/relation_restriction_equivalence.h",
    "content": "/*\n * relation_restriction_equivalence.h\n *\n * This file contains functions helper functions for planning\n * queries with colocated tables and subqueries.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef RELATION_RESTRICTION_EQUIVALENCE_H\n#define RELATION_RESTRICTION_EQUIVALENCE_H\n\n#include \"distributed/distributed_planner.h\"\n#include \"distributed/metadata_cache.h\"\n\n#define SINGLE_RTE_INDEX 1\n\nextern bool AllDistributionKeysInQueryAreEqual(Query *originalQuery,\n\t\t\t\t\t\t\t\t\t\t\t   PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nextern bool IsRelOptOuterJoin(PlannerInfo *root, int varNo);\nextern bool SafeToPushdownUnionSubquery(Query *originalQuery, PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext);\nextern bool ContainsUnionSubquery(Query *queryTree);\nextern bool RestrictionEquivalenceForPartitionKeys(PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nbool RestrictionEquivalenceForPartitionKeysViaEquivalences(PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   allAttributeEquivalenceList);\nextern List * GenerateAllAttributeEquivalences(PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t   plannerRestrictionContext);\nextern uint32 UniqueRelationCount(RelationRestrictionContext *restrictionContext,\n\t\t\t\t\t\t\t\t  CitusTableType tableType);\nextern List * DistributedRelationIdList(Query *query);\nextern PlannerRestrictionContext * FilterPlannerRestrictionForQuery(\n\tPlannerRestrictionContext *plannerRestrictionContext,\n\tQuery *query);\nextern List * GetRestrictInfoListForRelation(RangeTblEntry *rangeTblEntry,\n\t\t\t\t\t\t\t\t\t\t\t PlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t plannerRestrictionContext);\nextern RelationRestriction * RelationRestrictionForRelation(RangeTblEntry *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trangeTableEntry,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPlannerRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tplannerRestrictionContext);\nextern JoinRestrictionContext * RemoveDuplicateJoinRestrictions(JoinRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tjoinRestrictionContext);\n\nextern bool EquivalenceListContainsRelationsEquality(List *attributeEquivalenceList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t RelationRestrictionContext *\n\t\t\t\t\t\t\t\t\t\t\t\t\t restrictionContext);\nextern RelationRestrictionContext * FilterRelationRestrictionContext(\n\tRelationRestrictionContext *relationRestrictionContext,\n\tRelids\n\tqueryRteIdentities);\nextern bool AllDistributedRelationsInRTEListColocated(List *rangeTableEntryList);\nextern bool AllDistributedRelationsInListColocated(List *relationList);\n#endif /* RELATION_RESTRICTION_EQUIVALENCE_H */\n"
  },
  {
    "path": "src/include/distributed/relation_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * relation_utils.h\n *   Utilities related to Relation objects.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef RELATION_UTILS_H\n#define RELATION_UTILS_H\n\n#include \"postgres.h\"\n\n#include \"parser/parse_relation.h\"\n#include \"utils/relcache.h\"\n\n#include \"pg_version_constants.h\"\n\nextern char * RelationGetNamespaceName(Relation relation);\nextern RTEPermissionInfo * GetFilledPermissionInfo(Oid relid, bool inh,\n\t\t\t\t\t\t\t\t\t\t\t\t   AclMode requiredPerms);\n\n#endif /* RELATION_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/relay_utility.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * relay_utility.h\n *\n * Header and type declarations that extend relation, index and constraint names\n * with the appropriate shard identifiers.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id:$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef RELAY_UTILITY_H\n#define RELAY_UTILITY_H\n\n#include \"fmgr.h\"\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/nodes.h\"\n\n\n/* Shard name and identifier related defines */\n#define SHARD_NAME_SEPARATOR '_'\n#define INVALID_SHARD_ID 0\n#define INVALID_PLACEMENT_ID 0\n\n\n/* Function declarations to extend names in DDL commands */\nextern void RelayEventExtendNames(Node *parseTree, char *schemaName, uint64 shardId);\nextern void RelayEventExtendNamesForInterShardCommands(Node *parseTree,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   uint64 leftShardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   char *leftShardSchemaName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   uint64 rightShardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   char *rightShardSchemaName);\nextern void AppendShardIdToName(char **name, uint64 shardId);\n\nextern void SetSchemaNameIfNotExist(char **schemaName, const char *newSchemaName);\n\n#endif   /* RELAY_UTILITY_H */\n"
  },
  {
    "path": "src/include/distributed/remote_commands.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * remote_commands.h\n *\t  Helpers to execute commands on remote nodes, over libpq.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef REMOTE_COMMAND_H\n#define REMOTE_COMMAND_H\n\n#include \"distributed/connection_management.h\"\n\n/* errors which ExecuteRemoteCommand might return */\n#define RESPONSE_OKAY 0\n#define QUERY_SEND_FAILED 1\n#define RESPONSE_NOT_OKAY 2\n\n/* GUC, determining whether statements sent to remote nodes are logged */\nextern bool LogRemoteCommands;\nextern char *GrepRemoteCommands;\n\n/* GUC that determines the number of bytes after which remote COPY is flushed */\nextern int RemoteCopyFlushThreshold;\n\n\n/* simple helpers */\nextern bool IsResponseOK(PGresult *result);\nextern void ForgetResults(MultiConnection *connection);\nextern bool ClearResults(MultiConnection *connection, bool raiseErrors);\nextern bool ClearResultsDiscardWarnings(MultiConnection *connection, bool raiseErrors);\nextern bool ClearResultsIfReady(MultiConnection *connection);\n\n/* report errors & warnings */\nextern void ReportConnectionError(MultiConnection *connection, int elevel);\nextern void ReportResultError(MultiConnection *connection, PGresult *result,\n\t\t\t\t\t\t\t  int elevel);\nextern char * pchomp(const char *in);\nextern void LogRemoteCommand(MultiConnection *connection, const char *command);\nextern bool CommandMatchesLogGrepPattern(const char *command);\n\n/* wrappers around libpq functions, with command logging support */\nextern void ExecuteCriticalRemoteCommandList(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t List *commandList);\nextern void ExecuteCriticalRemoteCommand(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t const char *command);\nextern void ExecuteRemoteCommandInConnectionList(List *nodeConnectionList,\n\t\t\t\t\t\t\t\t\t\t\t\t const char *command);\nextern bool ExecuteRemoteCommandAndCheckResult(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t   char *command, char *expected);\nextern int ExecuteOptionalRemoteCommand(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\tconst char *command,\n\t\t\t\t\t\t\t\t\t\tPGresult **result);\nextern int SendRemoteCommand(MultiConnection *connection, const char *command);\nextern int SendRemoteCommandParams(MultiConnection *connection, const char *command,\n\t\t\t\t\t\t\t\t   int parameterCount, const Oid *parameterTypes,\n\t\t\t\t\t\t\t\t   const char *const *parameterValues,\n\t\t\t\t\t\t\t\t   bool binaryResults);\nextern List * ReadFirstColumnAsText(PGresult *queryResult);\nextern PGresult * GetRemoteCommandResult(MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t bool raiseInterrupts);\nextern bool PutRemoteCopyData(MultiConnection *connection, const char *buffer,\n\t\t\t\t\t\t\t  int nbytes);\nextern bool PutRemoteCopyEnd(MultiConnection *connection, const char *errormsg);\n\n/* waiting for multiple command results */\nextern void WaitForAllConnections(List *connectionList, bool raiseInterrupts);\n\nextern bool SendCancelationRequest(MultiConnection *connection);\n\nextern bool EvaluateSingleQueryResult(MultiConnection *connection, PGresult *queryResult,\n\t\t\t\t\t\t\t\t\t  StringInfo queryResultString);\nextern void StoreErrorMessage(MultiConnection *connection, StringInfo queryResultString);\n\nextern bool IsSettingSafeToPropagate(const char *name);\n\n\n#endif /* REMOTE_COMMAND_H */\n"
  },
  {
    "path": "src/include/distributed/remote_transaction.h",
    "content": "/*-------------------------------------------------------------------------\n * remote_transaction.h\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n\n#ifndef REMOTE_TRANSACTION_H\n#define REMOTE_TRANSACTION_H\n\n\n#include \"libpq-fe.h\"\n\n#include \"lib/ilist.h\"\n#include \"nodes/pg_list.h\"\n\n\n/* forward declare, to avoid recursive includes */\nstruct MultiConnection;\n\n/*\n * Enum that defines different remote transaction states, of a single remote\n * transaction.\n */\ntypedef enum\n{\n\t/* no transaction active */\n\tREMOTE_TRANS_NOT_STARTED = 0,\n\n\t/* transaction start */\n\tREMOTE_TRANS_STARTING,\n\tREMOTE_TRANS_STARTED,\n\n\t/* command execution */\n\tREMOTE_TRANS_SENT_BEGIN,\n\tREMOTE_TRANS_SENT_COMMAND,\n\tREMOTE_TRANS_FETCHING_RESULTS,\n\tREMOTE_TRANS_CLEARING_RESULTS,\n\n\t/* 2pc prepare */\n\tREMOTE_TRANS_PREPARING,\n\tREMOTE_TRANS_PREPARED,\n\n\t/* transaction abort */\n\tREMOTE_TRANS_1PC_ABORTING,\n\tREMOTE_TRANS_2PC_ABORTING,\n\tREMOTE_TRANS_ABORTED,\n\n\t/* transaction commit */\n\tREMOTE_TRANS_1PC_COMMITTING,\n\tREMOTE_TRANS_2PC_COMMITTING,\n\tREMOTE_TRANS_COMMITTED\n} RemoteTransactionState;\n\n\n/*\n * Transaction state associated associated with a single MultiConnection.\n */\ntypedef struct RemoteTransaction\n{\n\t/* what state is the remote side transaction in */\n\tRemoteTransactionState transactionState;\n\n\t/* failures on this connection should abort entire coordinated transaction */\n\tbool transactionCritical;\n\n\t/* failed in current transaction */\n\tbool transactionFailed;\n\n\t/*\n\t * Id of last savepoint that successfully began before transaction failure.\n\t * Since savepoint ids are assigned incrementally, rolling back to any savepoint\n\t * with id equal to or less than this id recovers the transaction from failures.\n\t */\n\tSubTransactionId lastSuccessfulSubXact;\n\n\t/* Id of last savepoint queued before first query of transaction */\n\tSubTransactionId lastQueuedSubXact;\n\n\t/* waiting for the result of a recovering ROLLBACK TO SAVEPOINT command */\n\tbool transactionRecovering;\n\n\t/* 2PC transaction name currently associated with connection */\n\tchar preparedName[NAMEDATALEN];\n\n\t/* set when BEGIN is sent over the connection */\n\tbool beginSent;\n} RemoteTransaction;\n\n\n/* utility functions for dealing with remote transactions */\nextern bool ParsePreparedTransactionName(char *preparedTransactionName, int32 *groupId,\n\t\t\t\t\t\t\t\t\t\t int *procId, uint64 *transactionNumber,\n\t\t\t\t\t\t\t\t\t\t uint32 *connectionNumber);\n\n/* change an individual remote transaction's state */\nextern void StartRemoteTransactionBegin(struct MultiConnection *connection);\nextern void FinishRemoteTransactionBegin(struct MultiConnection *connection);\nextern void RemoteTransactionBegin(struct MultiConnection *connection);\nextern void RemoteTransactionListBegin(List *connectionList);\n\nextern void StartRemoteTransactionPrepare(struct MultiConnection *connection);\nextern void FinishRemoteTransactionPrepare(struct MultiConnection *connection);\n\nextern void StartRemoteTransactionCommit(struct MultiConnection *connection);\nextern void FinishRemoteTransactionCommit(struct MultiConnection *connection);\nextern void RemoteTransactionCommit(struct MultiConnection *connection);\n\nextern void StartRemoteTransactionAbort(struct MultiConnection *connection);\nextern void FinishRemoteTransactionAbort(struct MultiConnection *connection);\nextern void RemoteTransactionAbort(struct MultiConnection *connection);\n\n/* start transaction if necessary */\nextern void RemoteTransactionBeginIfNecessary(struct MultiConnection *connection);\nextern void RemoteTransactionsBeginIfNecessary(List *connectionList);\n\n/* other public functionality */\nextern void HandleRemoteTransactionConnectionError(struct MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t\t   bool raiseError);\nextern void HandleRemoteTransactionResultError(struct MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\t\t   PGresult *result, bool raiseErrors);\nextern void MarkRemoteTransactionFailed(struct MultiConnection *connection,\n\t\t\t\t\t\t\t\t\t\tbool allowErrorPromotion);\nextern void MarkRemoteTransactionCritical(struct MultiConnection *connection);\n\n\n/*\n * The following functions should all only be called by connection /\n * transaction managment code.\n */\n\nextern void ResetRemoteTransaction(struct MultiConnection *connection);\n\n/* perform handling for all in-progress transactions */\nextern void CoordinatedRemoteTransactionsPrepare(void);\nextern void CoordinatedRemoteTransactionsCommit(void);\nextern void CoordinatedRemoteTransactionsAbort(void);\nextern void CheckRemoteTransactionsHealth(void);\n\n/* remote savepoint commands */\nextern void CoordinatedRemoteTransactionsSavepointBegin(SubTransactionId subId);\nextern void CoordinatedRemoteTransactionsSavepointRelease(SubTransactionId subId);\nextern void CoordinatedRemoteTransactionsSavepointRollback(SubTransactionId subId);\n\nextern void RunCitusMainDBQuery(char *query);\nextern void CleanCitusMainDBConnection(void);\n\nextern bool IsMainDBCommand;\nextern bool IsMainDB;\nextern char *SuperuserRole;\nextern char *MainDb;\nextern struct MultiConnection *MainDBConnection;\nextern bool IsMainDBCommandInXact;\n\n#endif /* REMOTE_TRANSACTION_H */\n"
  },
  {
    "path": "src/include/distributed/repartition_executor.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * repartition_executor.h\n *\n * Declarations for public functions and types related to repartition of\n * select query results.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef REPARTITION_EXECUTOR_H\n#define REPARTITION_EXECUTOR_H\n\nextern bool EnableRepartitionedInsertSelect;\n\nextern int DistributionColumnIndex(List *insertTargetList, Var *distributionColumn);\nextern List * GenerateTaskListWithColocatedIntermediateResults(Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   Query *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   modifyQueryViaCoordinatorOrRepartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   char *resultIdPrefix);\nextern List * GenerateTaskListWithRedistributedResults(Query *\n\t\t\t\t\t\t\t\t\t\t\t\t\t   modifyQueryViaCoordinatorOrRepartition,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   CitusTableCacheEntry *\n\t\t\t\t\t\t\t\t\t\t\t\t\t   targetRelation,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   List **redistributedResults,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   bool useBinaryFormat);\nextern bool IsSupportedRedistributionTarget(Oid targetRelationId);\nextern bool IsRedistributablePlan(Plan *selectPlan);\nextern bool HasMergeNotMatchedBySource(Query *query);\nextern void AdjustTaskQueryForEmptySource(Oid targetRelationId,\n\t\t\t\t\t\t\t\t\t\t  Query *mergeQuery,\n\t\t\t\t\t\t\t\t\t\t  List *emptySourceTaskList,\n\t\t\t\t\t\t\t\t\t\t  char *resultIdPrefix);\n\n#endif /* REPARTITION_EXECUTOR_H */\n"
  },
  {
    "path": "src/include/distributed/repartition_join_execution.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * repartition_join_execution.h\n *\t  Execution logic for repartition queries.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef REPARTITION_JOIN_EXECUTION_H\n#define REPARTITION_JOIN_EXECUTION_H\n\n#include \"nodes/pg_list.h\"\n\nextern List * ExecuteDependentTasks(List *taskList, Job *topLevelJob);\n\n\n#endif /* REPARTITION_JOIN_EXECUTION_H */\n"
  },
  {
    "path": "src/include/distributed/replicate_none_dist_table_shard.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * replicate_none_dist_table_shard.h\n *\t  Routines to replicate shard of none-distributed table to\n *    a remote node.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef REPLICA_LOCAL_TABLE_SHARD_H\n#define REPLICA_LOCAL_TABLE_SHARD_H\n\nextern void NoneDistTableReplicateCoordinatorPlacement(Oid noneDistTableId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   List *targetNodeList);\nextern void NoneDistTableDeleteCoordinatorPlacement(Oid noneDistTableId);\nextern void NoneDistTableDropCoordinatorPlacementTable(Oid noneDistTableId);\n\n#endif /* REPLICA_LOCAL_TABLE_SHARD_H */\n"
  },
  {
    "path": "src/include/distributed/replication_origin_session_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * replication_origin_utils.h\n *   Utilities related to replication origin.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef REPLICATION_ORIGIN_SESSION_UTILS_H\n#define REPLICATION_ORIGIN_SESSION_UTILS_H\n\n#include \"postgres.h\"\n\n#include \"replication/origin.h\"\n\n#include \"distributed/connection_management.h\"\n\nextern void InitializeReplicationOriginSessionUtils(void);\n\nextern void SetupReplicationOriginRemoteSession(MultiConnection *connection);\nextern void ResetReplicationOriginRemoteSession(MultiConnection *connection);\n\nextern void SetupReplicationOriginLocalSession(void);\nextern void ResetReplicationOriginLocalSession(void);\nextern void ResetReplicationOriginLocalSessionCallbackHandler(void *arg);\n\n\nextern bool EnableChangeDataCapture;\n\n\n#endif /* REPLICATION_ORIGIN_SESSION_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/resource_lock.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * resource_lock.h\n *\t  Locking Infrastructure for Citus.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef RESOURCE_LOCK_H\n#define RESOURCE_LOCK_H\n\n#include \"postgres.h\" /* IWYU pragma: keep */\n\n#include \"c.h\"\n\n#include \"catalog/dependency.h\"\n#include \"nodes/pg_list.h\"\n#include \"storage/lock.h\"\n#include \"tcop/utility.h\"\n\n#include \"distributed/worker_transaction.h\"\n\n\n/*\n * Postgres' advisory locks use 'field4' to discern between different kind of\n * advisory locks. Only 1 and 2 are used allowing us to define non-conflicting\n * lock methods.\n *\n * In case postgres starts to use additional values, Citus's values\n * will have to be changed. That just requires re-compiling and a restart.\n */\ntypedef enum AdvisoryLocktagClass\n{\n\t/* values defined in postgres' lockfuncs.c */\n\tADV_LOCKTAG_CLASS_INT64 = 1,\n\tADV_LOCKTAG_CLASS_INT32 = 2,\n\n\t/* Citus lock types */\n\tADV_LOCKTAG_CLASS_CITUS_SHARD_METADATA = 4,\n\tADV_LOCKTAG_CLASS_CITUS_SHARD = 5,\n\tADV_LOCKTAG_CLASS_CITUS_JOB = 6,\n\tADV_LOCKTAG_CLASS_CITUS_REBALANCE_COLOCATION = 7,\n\tADV_LOCKTAG_CLASS_CITUS_COLOCATED_SHARDS_METADATA = 8,\n\tADV_LOCKTAG_CLASS_CITUS_OPERATIONS = 9,\n\tADV_LOCKTAG_CLASS_CITUS_CLEANUP_OPERATION_ID = 10,\n\tADV_LOCKTAG_CLASS_CITUS_LOGICAL_REPLICATION = 12, /* Not used anymore */\n\tADV_LOCKTAG_CLASS_CITUS_REBALANCE_PLACEMENT_COLOCATION = 13,\n\tADV_LOCKTAG_CLASS_CITUS_BACKGROUND_TASK = 14,\n\tADV_LOCKTAG_CLASS_CITUS_GLOBAL_DDL_SERIALIZATION = 15,\n\tADV_LOCKTAG_CLASS_CITUS_SHARD_MOVE = 16\n} AdvisoryLocktagClass;\n\n/* CitusOperations has constants for citus operations */\ntypedef enum CitusOperations\n{\n\tCITUS_TRANSACTION_RECOVERY = 0,\n\tCITUS_NONBLOCKING_SPLIT = 1,\n\tCITUS_CREATE_DISTRIBUTED_TABLE_CONCURRENTLY = 2,\n\tCITUS_CREATE_COLOCATION_DEFAULT = 3,\n\tCITUS_BACKGROUND_TASK_MONITOR = 4\n} CitusOperations;\n\n/* reuse advisory lock, but with different, unused field 4 (4)*/\n#define SET_LOCKTAG_SHARD_METADATA_RESOURCE(tag, db, shardid) \\\n\t\tSET_LOCKTAG_ADVISORY(tag, \\\n\t\t\t\t\t\t\t db, \\\n\t\t\t\t\t\t\t (uint32) ((shardid) >> 32), \\\n\t\t\t\t\t\t\t (uint32) (shardid), \\\n\t\t\t\t\t\t\t ADV_LOCKTAG_CLASS_CITUS_SHARD_METADATA)\n\n#define SET_LOCKTAG_COLOCATED_SHARDS_METADATA_RESOURCE(tag, db, colocationId, \\\n\t\t\t\t\t\t\t\t\t\t\t\t\t   shardIntervalIndex) \\\n\t\tSET_LOCKTAG_ADVISORY(tag, \\\n\t\t\t\t\t\t\t db, \\\n\t\t\t\t\t\t\t (uint32) shardIntervalIndex, \\\n\t\t\t\t\t\t\t (uint32) colocationId, \\\n\t\t\t\t\t\t\t ADV_LOCKTAG_CLASS_CITUS_COLOCATED_SHARDS_METADATA)\n\n/* reuse advisory lock, but with different, unused field 4 (5)*/\n#define SET_LOCKTAG_SHARD_RESOURCE(tag, db, shardid) \\\n\t\tSET_LOCKTAG_ADVISORY(tag, \\\n\t\t\t\t\t\t\t db, \\\n\t\t\t\t\t\t\t (uint32) ((shardid) >> 32), \\\n\t\t\t\t\t\t\t (uint32) (shardid), \\\n\t\t\t\t\t\t\t ADV_LOCKTAG_CLASS_CITUS_SHARD)\n\n/* advisory lock for citus shard move/copy operations,\n * also it has the database hardcoded to MyDatabaseId,\n * to ensure the locks are local to each database */\n#define SET_LOCKTAG_SHARD_MOVE(tag, shardid) \\\n\t\tSET_LOCKTAG_ADVISORY(tag, \\\n\t\t\t\t\t\t\t MyDatabaseId, \\\n\t\t\t\t\t\t\t (uint32) ((shardid) >> 32), \\\n\t\t\t\t\t\t\t (uint32) (shardid), \\\n\t\t\t\t\t\t\t ADV_LOCKTAG_CLASS_CITUS_SHARD_MOVE)\n\n/* reuse advisory lock, but with different, unused field 4 (7)\n * Also it has the database hardcoded to MyDatabaseId, to ensure the locks\n * are local to each database */\n#define SET_LOCKTAG_REBALANCE_COLOCATION(tag, colocationOrTableId) \\\n\t\tSET_LOCKTAG_ADVISORY(tag, \\\n\t\t\t\t\t\t\t MyDatabaseId, \\\n\t\t\t\t\t\t\t (uint32) ((colocationOrTableId) >> 32), \\\n\t\t\t\t\t\t\t (uint32) (colocationOrTableId), \\\n\t\t\t\t\t\t\t ADV_LOCKTAG_CLASS_CITUS_REBALANCE_COLOCATION)\n\n/* reuse advisory lock, but with different, unused field 4 (13)\n * Also it has the database hardcoded to MyDatabaseId, to ensure the locks\n * are local to each database */\n#define SET_LOCKTAG_REBALANCE_PLACEMENT_COLOCATION(tag, colocationOrTableId) \\\n\t\tSET_LOCKTAG_ADVISORY(tag, \\\n\t\t\t\t\t\t\t MyDatabaseId, \\\n\t\t\t\t\t\t\t (uint32) ((colocationOrTableId) >> 32), \\\n\t\t\t\t\t\t\t (uint32) (colocationOrTableId), \\\n\t\t\t\t\t\t\t ADV_LOCKTAG_CLASS_CITUS_REBALANCE_PLACEMENT_COLOCATION)\n\n\n/* advisory lock for citus operations, also it has the database hardcoded to MyDatabaseId,\n * to ensure the locks are local to each database */\n#define SET_LOCKTAG_CITUS_OPERATION(tag, operationId) \\\n\t\tSET_LOCKTAG_ADVISORY(tag, \\\n\t\t\t\t\t\t\t MyDatabaseId, \\\n\t\t\t\t\t\t\t (uint32) 0, \\\n\t\t\t\t\t\t\t (uint32) operationId, \\\n\t\t\t\t\t\t\t ADV_LOCKTAG_CLASS_CITUS_OPERATIONS)\n\n/* reuse advisory lock, but with different, unused field 4 (10)\n * Also it has the database hardcoded to MyDatabaseId, to ensure the locks\n * are local to each database */\n#define SET_LOCKTAG_CLEANUP_OPERATION_ID(tag, operationId) \\\n\t\tSET_LOCKTAG_ADVISORY(tag, \\\n\t\t\t\t\t\t\t MyDatabaseId, \\\n\t\t\t\t\t\t\t (uint32) ((operationId) >> 32), \\\n\t\t\t\t\t\t\t (uint32) operationId, \\\n\t\t\t\t\t\t\t ADV_LOCKTAG_CLASS_CITUS_CLEANUP_OPERATION_ID)\n\n/* reuse advisory lock, but with different, unused field 4 (14)\n * Also it has the database hardcoded to MyDatabaseId, to ensure the locks\n * are local to each database */\n#define SET_LOCKTAG_BACKGROUND_TASK(tag, taskId) \\\n\t\tSET_LOCKTAG_ADVISORY(tag, \\\n\t\t\t\t\t\t\t MyDatabaseId, \\\n\t\t\t\t\t\t\t (uint32) ((taskId) >> 32), \\\n\t\t\t\t\t\t\t (uint32) (taskId), \\\n\t\t\t\t\t\t\t ADV_LOCKTAG_CLASS_CITUS_BACKGROUND_TASK)\n\n/*\n * IsNodeWideObjectClass returns true if the given object class is node-wide,\n * i.e., that is not bound to a particular database but to whole server.\n *\n * Defined here as an inlined function so that SET_LOCKTAG_GLOBAL_DDL_SERIALIZATION\n * macro can use it.\n */\nstatic inline bool\nIsNodeWideObjectClass(ObjectClass objectClass)\n{\n\tif ((int) objectClass < 0 || objectClass > LAST_OCLASS)\n\t{\n\t\telog(ERROR, \"invalid object class: %d\", objectClass);\n\t}\n\n\t/*\n\t * We don't expect Postgres to change an object class to a node-wide one in the\n\t * future, but a newly added object class may be node-wide.\n\t *\n\t * So we put a static assert here to make sure that the developer who adds support\n\t * for a new Postgres version is aware of this.\n\t *\n\t * If new object classes are added and none of them are node-wide, then update\n\t * this assertion check based on latest supported major Postgres version.\n\t */\n\tStaticAssertStmt(PG_MAJORVERSION_NUM <= 18,\n\t\t\t\t\t \"better to check if any of newly added ObjectClass'es are node-wide\")\n\t;\n\n\tswitch (objectClass)\n\t{\n\t\tcase OCLASS_ROLE:\n\t\tcase OCLASS_DATABASE:\n\t\tcase OCLASS_TBLSPACE:\n\t\tcase OCLASS_PARAMETER_ACL:\n\t\tcase OCLASS_ROLE_MEMBERSHIP:\n\t\t{\n\t\t\treturn true;\n\t\t}\n\n\t\tdefault:\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\n\n/*\n * Automatically sets databaseId to InvalidOid if the object class is\n * node-wide, i.e., that is not bound to a particular database but to\n * whole server. If the object class is not node-wide, sets databaseId\n * to MyDatabaseId.\n *\n * That way, the lock is local to each database if the object class is\n * not node-wide, and global if it is.\n */\n#define SET_LOCKTAG_GLOBAL_DDL_SERIALIZATION(tag, objectClass, oid) \\\n\t\tSET_LOCKTAG_ADVISORY(tag, \\\n\t\t\t\t\t\t\t (uint32) (IsNodeWideObjectClass(objectClass) ? InvalidOid : \\\n\t\t\t\t\t\t\t\t\t   MyDatabaseId), \\\n\t\t\t\t\t\t\t (uint32) objectClass, \\\n\t\t\t\t\t\t\t (uint32) oid, \\\n\t\t\t\t\t\t\t ADV_LOCKTAG_CLASS_CITUS_GLOBAL_DDL_SERIALIZATION)\n\n/*\n * DistLockConfigs are used to configure the locking behaviour of AcquireDistributedLockOnRelations\n */\nenum DistLockConfigs\n{\n\t/*\n\t * lock citus tables\n\t */\n\tDIST_LOCK_DEFAULT = 0,\n\n\t/*\n\t * lock tables that refer to locked citus tables with a foreign key\n\t */\n\tDIST_LOCK_REFERENCING_TABLES = 1,\n\n\t/*\n\t * throw an error if the lock is not immediately available\n\t */\n\tDIST_LOCK_NOWAIT = 2\n};\n\n\n/* Lock shard/relation metadata for safe modifications */\nextern void LockShardDistributionMetadata(int64 shardId, LOCKMODE lockMode);\nextern void EnsureShardOwner(uint64 shardId, bool missingOk);\nextern void LockShardListMetadataOnWorkers(LOCKMODE lockmode, List *shardIntervalList);\nextern void BlockWritesToShardList(List *shardList);\n\n/* Lock shard/relation metadata of the referenced reference table if exists */\nextern void LockReferencedReferenceShardDistributionMetadata(uint64 shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t LOCKMODE lock);\n\n/* Lock shard data, for DML commands or remote fetches */\nextern void LockShardResource(uint64 shardId, LOCKMODE lockmode);\n\n/* Lock a co-location group */\nextern void LockColocationId(int colocationId, LOCKMODE lockMode);\nextern void UnlockColocationId(int colocationId, LOCKMODE lockMode);\n\n/* Lock multiple shards for safe modification */\nextern void LockShardListMetadata(List *shardIntervalList, LOCKMODE lockMode);\nextern void LockShardListMetadataOnWorkers(LOCKMODE lockmode, List *shardIntervalList);\nextern void LockShardsInPlacementListMetadata(List *shardPlacementList,\n\t\t\t\t\t\t\t\t\t\t\t  LOCKMODE lockMode);\n\nextern void LockTransactionRecovery(LOCKMODE lockMode);\n\nextern void SerializeNonCommutativeWrites(List *shardIntervalList, LOCKMODE lockMode);\nextern void LockRelationShardResources(List *relationShardList, LOCKMODE lockMode);\nextern List * GetSortedReferenceShardIntervals(List *relationList);\n\nvoid AcquireCreateDistributedTableConcurrentlyLock(Oid relationId);\n\n/* Lock parent table's colocated shard resource */\nextern void LockParentShardResourceIfPartition(List *shardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t   LOCKMODE lockMode);\n\n/* Lock mode translation between text and enum */\nextern LOCKMODE LockModeTextToLockMode(const char *lockModeName);\nextern const char * LockModeToLockModeText(LOCKMODE lockMode);\nextern void AcquireDistributedLockOnRelations(List *relationList, LOCKMODE lockMode,\n\t\t\t\t\t\t\t\t\t\t\t  uint32 configs);\nextern void PreprocessLockStatement(LockStmt *stmt, ProcessUtilityContext context);\n\nextern bool EnableAcquiringUnsafeLockFromWorkers;\nextern bool SkipAdvisoryLockPermissionChecks;\n\n#endif /* RESOURCE_LOCK_H */\n"
  },
  {
    "path": "src/include/distributed/run_from_same_connection.h",
    "content": "/*\n * run_from_same_connection.h\n *\n * Sending commands from same connection to test transactions initiated from\n * worker nodes in the isolation framework.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n#ifndef RUN_FROM_SAME_CONNECTION_H\n#define RUN_FROM_SAME_CONNECTION_H\n\n/*\n * Config variables which will be used by isolation framework to check transactions\n * initiated from worker nodes.\n */\nextern int IsolationTestSessionRemoteProcessID;\nextern int IsolationTestSessionProcessID;\n\nbool AllowNonIdleTransactionOnXactHandling(void);\n\n#endif /* RUN_FROM_SAME_CONNECTION_H */\n"
  },
  {
    "path": "src/include/distributed/shard_cleaner.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_cleaner.h\n *\t  Type and function declarations used in background shard cleaning\n *\n * Copyright (c) 2018, Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_SHARD_CLEANER_H\n#define CITUS_SHARD_CLEANER_H\n\n#define MAX_BG_TASK_EXECUTORS 1000\n\n/* GUC to configure deferred shard deletion */\nextern int DeferShardDeleteInterval;\nextern int BackgroundTaskQueueCheckInterval;\nextern int MaxBackgroundTaskExecutors;\nextern double DesiredPercentFreeAfterMove;\nextern bool CheckAvailableSpaceBeforeMove;\n\nextern int NextOperationId;\nextern int NextCleanupRecordId;\n\nextern int TryDropOrphanedResources(void);\nextern void DropOrphanedResourcesInSeparateTransaction(void);\nextern void ErrorIfCleanupRecordForShardExists(char *shardName);\n\n/* Members for cleanup infrastructure */\ntypedef uint64 OperationId;\nextern OperationId CurrentOperationId;\n\n/*\n * CleanupResource represents the Resource type in cleanup records.\n */\ntypedef enum CleanupObject\n{\n\tCLEANUP_OBJECT_INVALID = 0,\n\tCLEANUP_OBJECT_SHARD_PLACEMENT = 1,\n\tCLEANUP_OBJECT_SUBSCRIPTION = 2,\n\tCLEANUP_OBJECT_REPLICATION_SLOT = 3,\n\tCLEANUP_OBJECT_PUBLICATION = 4,\n\tCLEANUP_OBJECT_USER = 5,\n\tCLEANUP_OBJECT_DATABASE = 6\n} CleanupObject;\n\n/*\n * CleanupPolicy represents the policy type for cleanup records.\n */\ntypedef enum CleanupPolicy\n{\n\t/*\n\t * Resources that are transient and always need clean up after the operation is completed.\n\t * (Example: Dummy Shards for Non-Blocking splits)\n\t */\n\tCLEANUP_ALWAYS = 0,\n\n\t/*\n\t * Resources that are cleanup only on failure.\n\t * (Example: Split Children for Blocking/Non-Blocking splits)\n\t */\n\tCLEANUP_ON_FAILURE = 1,\n\n\t/*\n\t * Resources that need 'deferred' clean up only on success .\n\t * (Example: Parent child being split for Blocking/Non-Blocking splits)\n\t */\n\tCLEANUP_DEFERRED_ON_SUCCESS = 2,\n} CleanupPolicy;\n\n/* Global Constants */\n#define INVALID_OPERATION_ID 0\n#define INVALID_CLEANUP_RECORD_ID 0\n\n/* APIs for cleanup infrastructure */\n\n/*\n * RegisterOperationNeedingCleanup is be called by an operation to register\n * for cleanup.\n */\nextern OperationId RegisterOperationNeedingCleanup(void);\n\n/*\n * InsertCleanupOnSuccessRecordInCurrentTransaction inserts a new pg_dist_cleanup entry\n * as part of the current transaction.\n *\n * This is primarily useful for deferred cleanup (CLEANUP_DEFERRED_ON_SUCCESS)\n * scenarios, since the records would roll back in case of failure. And for the\n * same reason, always sets the policy type to CLEANUP_DEFERRED_ON_SUCCESS.\n */\nextern void InsertCleanupOnSuccessRecordInCurrentTransaction(CleanupObject objectType,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t char *objectName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t int nodeGroupId);\n\n/*\n * InsertCleanupRecordInSeparateTransaction inserts a new pg_dist_cleanup entry\n * in a separate transaction to ensure the record persists after rollback.\n *\n * This is used in scenarios where we need to cleanup resources on operation\n * completion (CLEANUP_ALWAYS) or on failure (CLEANUP_ON_FAILURE).\n */\nextern void InsertCleanupRecordOutsideTransaction(CleanupObject objectType,\n\t\t\t\t\t\t\t\t\t\t\t\t  char *objectName,\n\t\t\t\t\t\t\t\t\t\t\t\t  int nodeGroupId,\n\t\t\t\t\t\t\t\t\t\t\t\t  CleanupPolicy policy);\n\n/*\n * FinalizeOperationNeedingCleanupOnSuccess is be called by an operation to signal\n * completion on success. This will trigger cleanup of appropriate resources\n * and cleanup records.\n */\nextern void FinalizeOperationNeedingCleanupOnSuccess(const char *operationName);\n\n#endif /*CITUS_SHARD_CLEANER_H */\n"
  },
  {
    "path": "src/include/distributed/shard_pruning.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_pruning.h\n *   Shard pruning infrastructure.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef SHARD_PRUNING_H_\n#define SHARD_PRUNING_H_\n\n#include \"nodes/primnodes.h\"\n\n#include \"distributed/metadata_cache.h\"\n\n#define INVALID_SHARD_INDEX -1\n\n/* Function declarations for shard pruning */\nextern List * PruneShards(Oid relationId, Index rangeTableId, List *whereClauseList,\n\t\t\t\t\t\t  Const **partitionValueConst);\nextern bool ContainsFalseClause(List *whereClauseList);\nextern List * get_all_actual_clauses(List *restrictinfo_list);\nextern Const * TransformPartitionRestrictionValue(Var *partitionColumn,\n\t\t\t\t\t\t\t\t\t\t\t\t  Const *restrictionValue,\n\t\t\t\t\t\t\t\t\t\t\t\t  bool missingOk);\nbool VarConstOpExprClause(OpExpr *opClause, Var **varClause, Const **constantClause);\n\n#endif /* SHARD_PRUNING_H_ */\n"
  },
  {
    "path": "src/include/distributed/shard_rebalancer.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_rebalancer.h\n *\n * Type and function declarations for the shard rebalancer tool.\n *\n * Copyright (c) 2016, Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef SHARD_REBALANCER_H\n#define SHARD_REBALANCER_H\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/coordinator_protocol.h\"\n#include \"distributed/worker_manager.h\"\n\n\n/* Limits for function parameters */\n#define SHARD_REPLICATION_FACTOR_MINIMUM 1\n#define SHARD_REPLICATION_FACTOR_MAXIMUM 100\n\n/* Definitions for metadata update commands */\n#define INSERT_SHARD_PLACEMENT_COMMAND \"INSERT INTO pg_dist_shard_placement VALUES(\" \\\n\t\tUINT64_FORMAT \", %d, \" UINT64_FORMAT \", '%s', %d)\"\n#define DELETE_SHARD_PLACEMENT_COMMAND \"DELETE FROM pg_dist_shard_placement WHERE \" \\\n\t\t\t\t\t\t\t\t\t   \"shardid=\" UINT64_FORMAT \\\n\t\t\" AND nodename='%s' AND nodeport=%d\"\n\n/*\n * Definitions for shard placement json field names. These names should match\n * the column names in pg_dist_shard_placement.\n */\n#define FIELD_NAME_SHARD_ID \"shardid\"\n#define FIELD_NAME_SHARD_LENGTH \"shardlength\"\n#define FIELD_NAME_SHARD_STATE \"shardstate\"\n#define FIELD_NAME_NODE_NAME \"nodename\"\n#define FIELD_NAME_NODE_PORT \"nodeport\"\n#define FIELD_NAME_PLACEMENT_ID \"placementid\"\n\n/*\n * Definitions for worker node json field names. These names should match the\n * column names in master_get_active_worker_nodes().\n */\n#define FIELD_NAME_WORKER_NAME \"node_name\"\n#define FIELD_NAME_WORKER_PORT \"node_port\"\n\n/* Definitions for placement update json field names */\n#define FIELD_NAME_UPDATE_TYPE \"updatetype\"\n#define FIELD_NAME_SOURCE_NAME \"sourcename\"\n#define FIELD_NAME_SOURCE_PORT \"sourceport\"\n#define FIELD_NAME_TARGET_NAME \"targetname\"\n#define FIELD_NAME_TARGET_PORT \"targetport\"\n\n/* *INDENT-OFF* */\n/* Definition for format of placement update json document */\n#define PLACEMENT_UPDATE_JSON_FORMAT \\\n\"{\"\\\n   \"\\\"\" FIELD_NAME_UPDATE_TYPE \"\\\":%d,\"\\\n   \"\\\"\" FIELD_NAME_SHARD_ID \"\\\":\" UINT64_FORMAT \",\"\\\n   \"\\\"\" FIELD_NAME_SOURCE_NAME \"\\\":%s,\"\\\n   \"\\\"\" FIELD_NAME_SOURCE_PORT \"\\\":%d,\"\\\n   \"\\\"\" FIELD_NAME_TARGET_NAME \"\\\":%s,\"\\\n   \"\\\"\" FIELD_NAME_TARGET_PORT \"\\\":%d\"\\\n\"}\"\n\n/* *INDENT-ON* */\n\n#define REBALANCE_ACTIVITY_MAGIC_NUMBER 1337\n#define REBALANCE_PROGRESS_WAITING 0\n#define REBALANCE_PROGRESS_MOVING 1\n#define REBALANCE_PROGRESS_MOVED 2\n\n/* Enumeration that defines different placement update types */\ntypedef enum\n{\n\tPLACEMENT_UPDATE_INVALID_FIRST = 0,\n\tPLACEMENT_UPDATE_MOVE = 1,\n\tPLACEMENT_UPDATE_COPY = 2\n} PlacementUpdateType;\n\ntypedef enum\n{\n\tPLACEMENT_UPDATE_STATUS_NOT_STARTED_YET = 0,\n\tPLACEMENT_UPDATE_STATUS_SETTING_UP = 1,\n\tPLACEMENT_UPDATE_STATUS_COPYING_DATA = 2,\n\tPLACEMENT_UPDATE_STATUS_CATCHING_UP = 3,\n\tPLACEMENT_UPDATE_STATUS_CREATING_CONSTRAINTS = 4,\n\tPLACEMENT_UPDATE_STATUS_FINAL_CATCH_UP = 5,\n\tPLACEMENT_UPDATE_STATUS_CREATING_FOREIGN_KEYS = 6,\n\tPLACEMENT_UPDATE_STATUS_COMPLETING = 7,\n\tPLACEMENT_UPDATE_STATUS_COMPLETED = 8,\n} PlacementUpdateStatus;\n\n\n/*\n * PlacementUpdateEvent represents a logical unit of work that copies or\n * moves a shard placement.\n */\ntypedef struct PlacementUpdateEvent\n{\n\tPlacementUpdateType updateType;\n\tuint64 shardId;\n\tWorkerNode *sourceNode;\n\tWorkerNode *targetNode;\n} PlacementUpdateEvent;\n\n\ntypedef struct PlacementUpdateEventProgress\n{\n\tuint64 shardId;\n\tchar sourceName[255];\n\tint sourcePort;\n\tchar targetName[255];\n\tint targetPort;\n\tPlacementUpdateType updateType;\n\tpg_atomic_uint64 progress;\n\tpg_atomic_uint64 updateStatus;\n} PlacementUpdateEventProgress;\n\ntypedef struct NodeFillState\n{\n\tWorkerNode *node;\n\n\t/*\n\t * capacity is how big this node is, relative to the other nodes in the\n\t * cluster. This has no unit, it can represent whatever the user wants.\n\t * Some examples:\n\t * 1. GBs of RAM\n\t * 2. number of CPUs\n\t * 3. GBs of disk\n\t * 4. relative improvement of new CPU generation in newly added nodes\n\t */\n\tfloat4 capacity;\n\n\t/*\n\t * totalCost is the costs of ShardCosts on the node added together. This\n\t * doesn't have a unit. See the ShardCost->cost comment for some examples.\n\t */\n\tfloat4 totalCost;\n\n\t/*\n\t * utilization is how \"full\" the node is. This is always totalCost divided\n\t * by capacity. Since neither of those have a unit, this also doesn't have\n\t * one.\n\t */\n\tfloat4 utilization;\n\n\t/*\n\t * shardCostListDesc contains all ShardCosts that are on the current node,\n\t * ordered from high cost to low cost.\n\t */\n\tList *shardCostListDesc;\n} NodeFillState;\n\ntypedef struct ShardCost\n{\n\tuint64 shardId;\n\n\t/*\n\t * cost is the cost of the shard. This doesn't have a unit.\n\t * Some examples of what this could represent:\n\t * 1. GBs of data\n\t * 2. number of queries per day\n\t */\n\tfloat4 cost;\n} ShardCost;\n\ntypedef struct DisallowedPlacement\n{\n\tShardCost *shardCost;\n\tNodeFillState *fillState;\n} DisallowedPlacement;\n\ntypedef struct RebalancePlanFunctions\n{\n\tbool (*shardAllowedOnNode)(uint64 shardId, WorkerNode *workerNode, void *context);\n\tfloat4 (*nodeCapacity)(WorkerNode *workerNode, void *context);\n\tShardCost (*shardCost)(uint64 shardId, void *context);\n\tvoid *context;\n} RebalancePlanFunctions;\n\nextern char *VariablesToBePassedToNewConnections;\nextern int MaxRebalancerLoggedIgnoredMoves;\nextern int RebalancerByDiskSizeBaseCost;\nextern bool RunningUnderCitusTestSuite;\nextern bool PropagateSessionSettingsForLoopbackConnection;\nextern int MaxBackgroundTaskExecutorsPerNode;\n\n/* External function declarations */\nextern Datum shard_placement_rebalance_array(PG_FUNCTION_ARGS);\nextern Datum shard_placement_replication_array(PG_FUNCTION_ARGS);\nextern Datum worker_node_responsive(PG_FUNCTION_ARGS);\nextern Datum update_shard_placement(PG_FUNCTION_ARGS);\nextern Datum init_rebalance_monitor(PG_FUNCTION_ARGS);\nextern Datum finalize_rebalance_monitor(PG_FUNCTION_ARGS);\nextern Datum get_rebalance_progress(PG_FUNCTION_ARGS);\n\nextern List * RebalancePlacementUpdates(List *workerNodeList,\n\t\t\t\t\t\t\t\t\t\tList *shardPlacementListList,\n\t\t\t\t\t\t\t\t\t\tdouble threshold,\n\t\t\t\t\t\t\t\t\t\tint32 maxShardMoves,\n\t\t\t\t\t\t\t\t\t\tbool drainOnly,\n\t\t\t\t\t\t\t\t\t\tfloat4 utilizationImproventThreshold,\n\t\t\t\t\t\t\t\t\t\tRebalancePlanFunctions *rebalancePlanFunctions);\nextern List * ReplicationPlacementUpdates(List *workerNodeList, List *shardPlacementList,\n\t\t\t\t\t\t\t\t\t\t  int shardReplicationFactor);\nextern void ExecuteRebalancerCommandInSeparateTransaction(char *command);\nextern void AcquirePlacementColocationLock(Oid relationId, int lockMode,\n\t\t\t\t\t\t\t\t\t\t   const char *operationName);\n\nextern void SetupRebalanceMonitor(List *placementUpdateList,\n\t\t\t\t\t\t\t\t  Oid relationId,\n\t\t\t\t\t\t\t\t  uint64 initialProgressState,\n\t\t\t\t\t\t\t\t  PlacementUpdateStatus initialStatus);\n\nextern void SplitShardsBetweenPrimaryAndClone(WorkerNode *primaryNode,\n\t\t\t\t\t\t\t\t\t\t\t  WorkerNode *cloneNode,\n\t\t\t\t\t\t\t\t\t\t\t  Name strategyName);\n#endif   /* SHARD_REBALANCER_H */\n"
  },
  {
    "path": "src/include/distributed/shard_split.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_split.h\n *\n * API for shard splits.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef SHARDSPLIT_H_\n#define SHARDSPLIT_H_\n\n#include \"distributed/utils/distribution_column_map.h\"\n\n/* Split Modes supported by Shard Split API */\ntypedef enum SplitMode\n{\n\tBLOCKING_SPLIT = 0,\n\tNON_BLOCKING_SPLIT = 1,\n\tAUTO_SPLIT = 2\n} SplitMode;\n\n\n/*\n * User Scenario calling Split Shard API.\n * The 'SplitOperation' type is used to customize info/error messages based on user scenario.\n */\ntypedef enum SplitOperation\n{\n\tSHARD_SPLIT_API = 0,\n\tISOLATE_TENANT_TO_NEW_SHARD,\n\tCREATE_DISTRIBUTED_TABLE\n} SplitOperation;\n\n/*\n * SplitShard API to split a given shard (or shard group) using split mode and\n * specified split points to a set of destination nodes.\n */\nextern void SplitShard(SplitMode splitMode,\n\t\t\t\t\t   SplitOperation splitOperation,\n\t\t\t\t\t   uint64 shardIdToSplit,\n\t\t\t\t\t   List *shardSplitPointsList,\n\t\t\t\t\t   List *nodeIdsForPlacementList,\n\t\t\t\t\t   DistributionColumnMap *distributionColumnOverrides,\n\t\t\t\t\t   List *colocatedShardIntervalList,\n\t\t\t\t\t   uint32 targetColocationId);\n\nextern SplitMode LookupSplitMode(Oid shardTransferModeOid);\n\nextern void ErrorIfMultipleNonblockingMoveSplitInTheSameTransaction(void);\n\n#endif /* SHARDSPLIT_H_ */\n"
  },
  {
    "path": "src/include/distributed/shard_transfer.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_transfer.h\n *\t  Code used to move shards around.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#include \"postgres.h\"\n\n#include \"nodes/pg_list.h\"\n\n#include \"distributed/shard_rebalancer.h\"\n\nextern Datum citus_move_shard_placement(PG_FUNCTION_ARGS);\nextern Datum citus_move_shard_placement_with_nodeid(PG_FUNCTION_ARGS);\n\ntypedef enum\n{\n\tSHARD_TRANSFER_INVALID_FIRST = 0,\n\tSHARD_TRANSFER_MOVE = 1,\n\tSHARD_TRANSFER_COPY = 2\n} ShardTransferType;\n\n/*\n * ShardTransferOperationMode is used to pass flags to the shard transfer\n * function. The flags are used to control the behavior of the transfer\n * function.\n * Currently, optionFlags are only used to customize reference table transfers.\n * For distributed tables, optionFlags should always be set to 0.\n */\ntypedef enum\n{\n\t/*\n\t * This flag instructs the transfer function to only transfer single shard\n\t * rather than transfer all the colocated shards for the shard interval.\n\t * Using this flag mean we might break the colocated shard\n\t * relationship on the source node. So this is only usefull when setting up\n\t * the new node and we are sure that the node would not be used until we have\n\t * transfered all the shards.\n\t * The reason we need this flag is that we want to be able to transfer\n\t * colocated shards in parallel and for now it is only used for the reference\n\t * table shards.\n\t * Finally if you are using this flag, you should also use consider defering\n\t * the creation of the relationships on the source node until all colocated\n\t * shards are transfered (see: SHARD_TRANSFER_SKIP_CREATE_RELATIONSHIPS).\n\t */\n\tSHARD_TRANSFER_SINGLE_SHARD_ONLY = 1 << 0,\n\n\t/* With this flag the shard transfer function does not create any constrainsts\n\t * or foreign relations defined on the shard, This can be used to defer the\n\t * creation of the relationships until all the shards are transfered.\n\t * This is usefull when we are transfering colocated shards in parallel and\n\t * we want to avoid the creation of the relationships on the source node\n\t * until all the shards are transfered.\n\t */\n\tSHARD_TRANSFER_SKIP_CREATE_RELATIONSHIPS = 1 << 1,\n\n\t/* This flag is used to indicate that the shard transfer function should\n\t * only create the relationships on the target node and not transfer any data.\n\t * This is can be used to create the relationships that were defered\n\t * during the transfering of shards.\n\t */\n\tSHARD_TRANSFER_CREATE_RELATIONSHIPS_ONLY = 1 << 2\n} ShardTransferOperationMode;\n\n\nextern void TransferShards(int64 shardId,\n\t\t\t\t\t\t   char *sourceNodeName, int32 sourceNodePort,\n\t\t\t\t\t\t   char *targetNodeName, int32 targetNodePort,\n\t\t\t\t\t\t   char shardReplicationMode, ShardTransferType transferType,\n\t\t\t\t\t\t   uint32 optionFlags);\nextern uint64 ShardListSizeInBytes(List *colocatedShardList,\n\t\t\t\t\t\t\t\t   char *workerNodeName, uint32 workerNodePort);\nextern void ErrorIfMoveUnsupportedTableType(Oid relationId);\nextern void CopyShardsToNode(WorkerNode *sourceNode, WorkerNode *targetNode,\n\t\t\t\t\t\t\t List *shardIntervalList, char *snapshotName);\nextern void VerifyTablesHaveReplicaIdentity(List *colocatedTableList);\nextern bool RelationCanPublishAllModifications(Oid relationId);\nextern void UpdatePlacementUpdateStatusForShardIntervalList(List *shardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar *sourceName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tint sourcePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPlacementUpdateStatus status);\nextern void InsertDeferredDropCleanupRecordsForShards(List *shardIntervalList);\nextern void InsertCleanupRecordsForShardPlacementsOnNode(List *shardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t int32 groupId);\n\nextern void AdjustShardsForPrimaryCloneNodeSplit(WorkerNode *primaryNode,\n\t\t\t\t\t\t\t\t\t\t\t\t WorkerNode *cloneNode,\n\t\t\t\t\t\t\t\t\t\t\t\t List *primaryShardList,\n\t\t\t\t\t\t\t\t\t\t\t\t List *cloneShardList);\n"
  },
  {
    "path": "src/include/distributed/shard_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * shard_utils.h\n *   Utilities related to shards.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef SHARD_UTILS_H\n#define SHARD_UTILS_H\n\n#include \"postgres.h\"\n\nextern Oid GetTableLocalShardOid(Oid citusTableOid, uint64 shardId);\nextern char * GetLongestShardName(Oid citusTableOid, char *finalRelationName);\nextern char * GetLongestShardNameForLocalPartition(Oid parentTableOid,\n\t\t\t\t\t\t\t\t\t\t\t\t   char *partitionRelationName);\n\n#endif /* SHARD_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/shardinterval_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * shardinterval_utils.h\n *\n * Declarations for public utility functions related to shard intervals.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef SHARDINTERVAL_UTILS_H_\n#define SHARDINTERVAL_UTILS_H_\n\n#include \"nodes/primnodes.h\"\n\n#include \"distributed/metadata_cache.h\"\n#include \"distributed/metadata_utility.h\"\n\n#define INVALID_SHARD_INDEX -1\n\n/* OperatorCacheEntry contains information for each element in OperatorCache */\ntypedef struct ShardIntervalCompareFunctionCacheEntry\n{\n\tVar *partitionColumn;\n\tchar partitionMethod;\n\tFmgrInfo *functionInfo;\n} ShardIntervalCompareFunctionCacheEntry;\n\n/*\n * SortShardIntervalContext is the context parameter in SortShardIntervalArray\n */\ntypedef struct SortShardIntervalContext\n{\n\tFmgrInfo *comparisonFunction;\n\tOid collation;\n} SortShardIntervalContext;\n\nextern ShardInterval ** SortShardIntervalArray(ShardInterval **shardIntervalArray, int\n\t\t\t\t\t\t\t\t\t\t\t   shardCount, Oid collation,\n\t\t\t\t\t\t\t\t\t\t\t   FmgrInfo *\n\t\t\t\t\t\t\t\t\t\t\t   shardIntervalSortCompareFunction);\nextern int CompareShardIntervals(const void *leftElement, const void *rightElement,\n\t\t\t\t\t\t\t\t SortShardIntervalContext *sortContext);\nextern int CompareShardIntervalsById(const void *leftElement, const void *rightElement);\nextern int CompareShardPlacementsByShardId(const void *leftElement,\n\t\t\t\t\t\t\t\t\t\t   const void *rightElement);\nextern int CompareRelationShards(const void *leftElement,\n\t\t\t\t\t\t\t\t const void *rightElement);\nextern int ShardIndex(ShardInterval *shardInterval);\nextern int CalculateUniformHashRangeIndex(int hashedValue, int shardCount);\nextern ShardInterval * FindShardInterval(Datum partitionColumnValue,\n\t\t\t\t\t\t\t\t\t\t CitusTableCacheEntry *cacheEntry);\nextern int FindShardIntervalIndex(Datum searchedValue, CitusTableCacheEntry *cacheEntry);\nextern int SearchCachedShardInterval(Datum partitionColumnValue,\n\t\t\t\t\t\t\t\t\t ShardInterval **shardIntervalCache,\n\t\t\t\t\t\t\t\t\t int shardCount, Oid shardIntervalCollation,\n\t\t\t\t\t\t\t\t\t FmgrInfo *compareFunction);\nextern bool SingleReplicatedTable(Oid relationId);\n\n\n#endif /* SHARDINTERVAL_UTILS_H_ */\n"
  },
  {
    "path": "src/include/distributed/shardsplit_logical_replication.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * shardsplit_logical_replication.h\n *\n * Function declarations for logically replicating shard to split children.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef SHARDSPLIT_LOGICAL_REPLICATION_H\n#define SHARDSPLIT_LOGICAL_REPLICATION_H\n\n#include \"distributed/metadata_utility.h\"\n#include \"distributed/multi_logical_replication.h\"\n#include \"distributed/worker_manager.h\"\n\n/*\n * GroupedShardSplitInfos groups all ShardSplitInfos belonging to the same node\n * and table owner together. This data structure its only purpose is creating a\n * hashmap that allows us to search ShardSplitInfos by node and owner.\n */\ntypedef struct GroupedShardSplitInfos\n{\n\tNodeAndOwner key;\n\tList *shardSplitInfoList;\n} GroupedShardSplitInfos;\n\n\n/* Functions for subscriber metadata management */\nextern List * PopulateShardSplitSubscriptionsMetadataList(HTAB *shardSplitInfoHashMap,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  List *replicationSlotInfoList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  List *workersForPlacementList);\nextern HTAB *  CreateShardSplitInfoMapForPublication(List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t sourceColocatedShardIntervalList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t shardGroupSplitIntervalListList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t List *destinationWorkerNodesList);\n\n/* Functions to drop publisher-subscriber resources */\nextern void DropAllShardSplitLeftOvers(WorkerNode *sourceNode,\n\t\t\t\t\t\t\t\t\t   HTAB *shardSplitMapOfPublications);\nextern void DropShardSplitPublications(MultiConnection *sourceConnection,\n\t\t\t\t\t\t\t\t\t   HTAB *shardInfoHashMapForPublication);\nextern void DropShardSplitSubsriptions(List *shardSplitSubscribersMetadataList);\nextern void DropShardSplitReplicationSlots(MultiConnection *sourceConnection,\n\t\t\t\t\t\t\t\t\t\t   List *replicationSlotInfoList);\n\n#endif /* SHARDSPLIT_LOGICAL_REPLICATION_H */\n"
  },
  {
    "path": "src/include/distributed/shardsplit_shared_memory.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * shardsplit_shared_memory.h\n *    API's for creating and accessing shared memory segments to store\n *    shard split information. 'worker_split_shard_replication_setup' UDF creates the\n *    shared memory and populates the contents. WAL sender processes are consumer\n *    of split information for appropriate tuple routing.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef SHARDSPLIT_SHARED_MEMORY_H\n#define SHARDSPLIT_SHARED_MEMORY_H\n\n#include \"postgres.h\"\n\n/*\n * In-memory mapping of a split child shard.\n */\ntypedef struct ShardSplitInfo\n{\n\tOid distributedTableOid;     /* citus distributed table Oid */\n\tint partitionColumnIndex;    /* partition column index */\n\tOid sourceShardOid;          /* parent shard Oid */\n\tOid splitChildShardOid;      /* child shard Oid */\n\tint32 shardMinValue;         /* min hash value */\n\tint32 shardMaxValue;         /* max hash value */\n\tuint32_t nodeId;             /* node where child shard is to be placed */\n\tuint64 sourceShardId;        /* parent shardId */\n\tuint64 splitChildShardId;        /* child shardId*/\n\tchar slotName[NAMEDATALEN];  /* replication slot name belonging to this node */\n} ShardSplitInfo;\n\n\n/*\n * Header of the shared memory segment where shard split information is stored.\n */\ntypedef struct ShardSplitInfoSMHeader\n{\n\tint count;          /* number of elements in the shared memory */\n\tShardSplitInfo splitInfoArray[FLEXIBLE_ARRAY_MEMBER];\n} ShardSplitInfoSMHeader;\n\n/*\n * Shard split information is populated and stored in shared memory in the form of one dimensional\n * array by 'worker_split_shard_replication_setup'. Information belonging to same replication\n * slot is grouped together and stored contiguously within this array.\n * 'SourceToDestinationShardMap' maps list of child(destination) shards that should be processed by a replication\n * slot corresponding to a parent(source) shard. When a parent shard receives a change, the decoder can use this map\n * to traverse only the list of child shards corresponding the given parent.\n */\ntypedef struct SourceToDestinationShardMapEntry\n{\n\tOid sourceShardKey;\n\tList *shardSplitInfoList;\n} SourceToDestinationShardMapEntry;\n\ntypedef struct ShardSplitShmemData\n{\n\tint trancheId;\n\tNamedLWLockTranche namedLockTranche;\n\tLWLock lock;\n\n\tdsm_handle dsmHandle;\n} ShardSplitShmemData;\n\n/* Functions for creating and accessing shared memory used for dsm handle managment */\nvoid InitializeShardSplitSMHandleManagement(void);\n\nvoid StoreShardSplitSharedMemoryHandle(dsm_handle dsmHandle);\n\n/* Functions for creating and accessing shared memory segments consisting shard split information */\nextern ShardSplitInfoSMHeader * CreateSharedMemoryForShardSplitInfo(int\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tshardSplitInfoCount,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdsm_handle *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdsmHandle);\nextern void ReleaseSharedMemoryOfShardSplitInfo(void);\n\nextern ShardSplitInfoSMHeader *  GetShardSplitInfoSMHeader(void);\n\nextern HTAB * PopulateSourceToDestinationShardMapForSlot(char *slotName, MemoryContext\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t cxt);\n#endif /* SHARDSPLIT_SHARED_MEMORY_H */\n"
  },
  {
    "path": "src/include/distributed/shared_connection_stats.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * shared_connection_stats.h\n *   Central management of connections and their life-cycle\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef SHARED_CONNECTION_STATS_H\n#define SHARED_CONNECTION_STATS_H\n\n#define ADJUST_POOLSIZE_AUTOMATICALLY 0\n#define DISABLE_CONNECTION_THROTTLING -1\n#define DISABLE_REMOTE_CONNECTIONS_FOR_LOCAL_QUERIES -1\n#define ALLOW_ALL_EXTERNAL_CONNECTIONS -1\n\n\nextern int MaxSharedPoolSize;\nextern int LocalSharedPoolSize;\nextern int MaxClientConnections;\n\n\nextern void InitializeSharedConnectionStats(void);\nextern void WaitForSharedConnection(void);\nextern void WakeupWaiterBackendsForSharedConnection(void);\nextern size_t SharedConnectionStatsShmemSize(void);\nextern void SharedConnectionStatsShmemInit(void);\nextern int GetMaxClientConnections(void);\nextern int GetMaxSharedPoolSize(void);\nextern int GetLocalSharedPoolSize(void);\nextern bool TryToIncrementSharedConnectionCounter(const char *hostname, int port);\nextern void WaitLoopForSharedConnection(const char *hostname, int port);\nextern void DecrementSharedConnectionCounter(const char *hostname, int port);\nextern void IncrementSharedConnectionCounter(const char *hostname, int port);\nextern int AdaptiveConnectionManagementFlag(bool connectToLocalNode, int\n\t\t\t\t\t\t\t\t\t\t\tactiveConnectionCount);\n\n#endif /* SHARED_CONNECTION_STATS_H */\n"
  },
  {
    "path": "src/include/distributed/shared_library_init.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * shared_library_init.h\n *\t  Functionality related to the initialization of the Citus extension.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef SHARED_LIBRARY_INIT_H\n#define SHARED_LIBRARY_INIT_H\n\n#include \"columnar/columnar.h\"\n\n#define GUC_STANDARD 0\n#define MAX_SHARD_COUNT 64000\n#define MAX_SHARD_REPLICATION_FACTOR 100\n\nextern PGDLLEXPORT ColumnarSupportsIndexAM_type extern_ColumnarSupportsIndexAM;\nextern PGDLLEXPORT CompressionTypeStr_type extern_CompressionTypeStr;\nextern PGDLLEXPORT IsColumnarTableAmTable_type extern_IsColumnarTableAmTable;\nextern PGDLLEXPORT ReadColumnarOptions_type extern_ReadColumnarOptions;\n\nextern void StartupCitusBackend(void);\nextern const char * GetClientMinMessageLevelNameForValue(int minMessageLevel);\n\n#endif /* SHARED_LIBRARY_INIT_H */\n"
  },
  {
    "path": "src/include/distributed/stats/query_stats.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * stats_statements.h\n *    Statement-level statistics for distributed queries.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef QUERY_STATS_H\n#define QUERY_STATS_H\n\n#include \"distributed/multi_server_executor.h\"\n\n#define STATS_SHARED_MEM_NAME \"citus_query_stats\"\n\nextern Size CitusQueryStatsSharedMemSize(void);\nextern void InitializeCitusQueryStats(void);\nextern void CitusQueryStatsExecutorsEntry(uint64 queryId, MultiExecutorType executorType,\n\t\t\t\t\t\t\t\t\t\t  char *partitionKey);\nextern void CitusQueryStatsSynchronizeEntries(void);\nextern int StatStatementsPurgeInterval;\nextern int StatStatementsMax;\nextern int StatStatementsTrack;\n\n\ntypedef enum\n{\n\tSTAT_STATEMENTS_TRACK_NONE = 0,\n\tSTAT_STATEMENTS_TRACK_ALL = 1\n} StatStatementsTrackType;\n\n#endif /* QUERY_STATS_H */\n"
  },
  {
    "path": "src/include/distributed/stats/stat_counters.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * stat_counters.h\n *\n * This file contains the exported functions to track various statistic\n * counters for Citus.\n *\n * -------------------------------------------------------------------------\n */\n\n#ifndef STAT_COUNTERS_H\n#define STAT_COUNTERS_H\n\n\n/* saved backend stats - constants */\n#define SAVED_BACKEND_STATS_HASH_LOCK_TRANCHE_NAME \\\n\t\t\"citus_stat_counters saved backend stats hash\"\n\n/* default value for the GUC variable */\n#define ENABLE_STAT_COUNTERS_DEFAULT false\n\n\n/*\n * Must be in the same order as the output columns defined in citus_stat_counters() UDF,\n * see src/backend/distributed/sql/udfs/citus_stat_counters/latest.sql\n */\ntypedef enum\n{\n\t/*\n\t * These are mainly tracked by connection_management.c and\n\t * adaptive_executor.c.\n\t */\n\tSTAT_CONNECTION_ESTABLISHMENT_SUCCEEDED,\n\tSTAT_CONNECTION_ESTABLISHMENT_FAILED,\n\tSTAT_CONNECTION_REUSED,\n\n\t/*\n\t * These are maintained by ExecCustomScan methods implemented\n\t * for CustomScan nodes provided by Citus to account for actual\n\t * execution of the queries and subplans. By maintaining these\n\t * counters in ExecCustomScan callbacks, we ensure avoid\n\t * incrementing them for plain EXPLAIN (i.e., without ANALYZE).\n\t * queries. And, prefering the executor methods rather than the\n\t * planner methods helps us capture the execution of prepared\n\t * statements too.\n\t */\n\tSTAT_QUERY_EXECUTION_SINGLE_SHARD,\n\tSTAT_QUERY_EXECUTION_MULTI_SHARD,\n\n\t/* do not use this and ensure it is the last entry */\n\tN_CITUS_STAT_COUNTERS\n} StatType;\n\n\n/* GUC variable */\nextern bool EnableStatCounters;\n\n\n/* shared memory init */\nextern void InitializeStatCountersShmem(void);\nextern Size StatCountersShmemSize(void);\n\n/* main entry point for the callers who want to increment the stat counters */\nextern void IncrementStatCounterForMyDb(int statId);\n\n/*\n * Exported to define a before_shmem_exit() callback that saves\n * the stat counters for exited backends into the shared memory.\n */\nextern void SaveBackendStatsIntoSavedBackendStatsHash(void);\n\n#endif /* STAT_COUNTERS_H */\n"
  },
  {
    "path": "src/include/distributed/stats/stat_tenants.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * stat_tenants.h\n *\t  Routines related to the multi tenant monitor.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_ATTRIBUTE_H\n#define CITUS_ATTRIBUTE_H\n\n#include \"executor/execdesc.h\"\n#include \"executor/executor.h\"\n#include \"storage/lwlock.h\"\n#include \"utils/datetime.h\"\n#include \"utils/hsearch.h\"\n\n#include \"distributed/hash_helpers.h\"\n\n#define MAX_TENANT_ATTRIBUTE_LENGTH 100\n\n/*\n * Hashtable key that defines the identity of a hashtable entry.\n * The key is the attribute value, e.g distribution column and the colocation group id of the tenant.\n */\ntypedef struct TenantStatsHashKey\n{\n\tchar tenantAttribute[MAX_TENANT_ATTRIBUTE_LENGTH];\n\tint colocationGroupId;\n} TenantStatsHashKey;\nassert_valid_hash_key2(TenantStatsHashKey, tenantAttribute, colocationGroupId);\n\n/*\n * TenantStats is the struct that keeps statistics about one tenant.\n */\ntypedef struct TenantStats\n{\n\tTenantStatsHashKey key;   /* hash key of entry - MUST BE FIRST */\n\n\n\t/*\n\t * Number of SELECT queries this tenant ran in this and last periods.\n\t */\n\tint readsInLastPeriod;\n\tint readsInThisPeriod;\n\n\t/*\n\t * Number of INSERT, UPDATE, and DELETE queries this tenant ran in this and last periods.\n\t */\n\tint writesInLastPeriod;\n\tint writesInThisPeriod;\n\n\n\t/*\n\t * CPU time usage of this tenant in this and last periods.\n\t */\n\tdouble cpuUsageInLastPeriod;\n\tdouble cpuUsageInThisPeriod;\n\n\t/*\n\t * The latest time this tenant ran a query. This value is used to update the score later.\n\t */\n\tTimestampTz lastQueryTime;\n\n\t/*\n\t * The tenant monitoring score of this tenant. This value is increased by ONE_QUERY_SCORE at every query\n\t * and halved after every period. This custom scoring mechanism is used to rank the tenants based on\n\t * the recency and frequency of their activity. The score is used to rank the tenants and decide which\n\t * tenants should be removed from the monitor.\n\t */\n\tlong long score;\n\n\t/*\n\t * The latest time the score of this tenant is halved. This value is used to correctly calculate the reduction later.\n\t */\n\tTimestampTz lastScoreReduction;\n\n\t/*\n\t * Locks needed to update this tenant's statistics.\n\t */\n\tslock_t lock;\n} TenantStats;\n\n/*\n * MultiTenantMonitor is the struct for keeping the statistics\n * of the tenants\n */\ntypedef struct MultiTenantMonitor\n{\n\t/*\n\t * Lock mechanism for the monitor.\n\t * Each tenant update acquires the lock in shared mode and\n\t * the tenant number reduction and monitor view acquires in exclusive mode.\n\t */\n\tNamedLWLockTranche namedLockTranche;\n\tLWLock lock;\n\n\t/*\n\t * The max length of tenants hashtable is 3 * citus.stat_tenants_limit\n\t */\n\tHTAB *tenants;\n} MultiTenantMonitor;\n\ntypedef enum\n{\n\tSTAT_TENANTS_TRACK_NONE = 0,\n\tSTAT_TENANTS_TRACK_ALL = 1\n} StatTenantsTrackType;\n\nextern void CitusAttributeToEnd(QueryDesc *queryDesc);\nextern void AttributeQueryIfAnnotated(const char *queryString, CmdType commandType);\nextern char * AnnotateQuery(char *queryString, Const *partitionKeyValue,\n\t\t\t\t\t\t\tint colocationId);\nextern void InitializeMultiTenantMonitorSMHandleManagement(void);\nextern void AttributeTask(char *tenantId, int colocationGroupId, CmdType commandType);\n\nextern ExecutorEnd_hook_type prev_ExecutorEnd;\n\nextern int StatTenantsLogLevel;\nextern int StatTenantsPeriod;\nextern int StatTenantsLimit;\nextern int StatTenantsTrack;\nextern double StatTenantsSampleRateForNewTenants;\n\n#endif /*CITUS_ATTRIBUTE_H */\n"
  },
  {
    "path": "src/include/distributed/string_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * string_utils.h\n *   Utilities related to strings.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_STRING_UTILS_H\n#define CITUS_STRING_UTILS_H\n\n#include \"postgres.h\"\n\nextern char * ConvertIntToString(int val);\n\n#define StringStartsWith(str, prefix) \\\n\t\t(strncmp(str, prefix, strlen(prefix)) == 0)\n\n#endif /* CITUS_STRING_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/subplan_execution.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * subplan_execution.h\n *\n * Functions for execution subplans.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef SUBPLAN_EXECUTION_H\n#define SUBPLAN_EXECUTION_H\n\n\n#include \"distributed/multi_physical_planner.h\"\n\nextern int MaxIntermediateResult;\nextern int SubPlanLevel;\n\nextern void ExecuteSubPlans(DistributedPlan *distributedPlan, bool explainAnalyzeEnabled);\n\n/**\n * IntermediateResultsHashEntry is used to store which nodes need to receive\n * intermediate results. Given an intermediate result name, you can lookup\n * the list of nodes that can possibly run a query that will use the\n * intermediate results.\n *\n * The nodeIdList contains a set of unique WorkerNode ids that have placements\n * that can be used in non-colocated subquery joins with the intermediate result\n * given in the key.\n *\n * writeLocalFile indicates if the intermediate result is accessed during local\n * execution. Note that there can possibly be an item for the local node in the\n * NodeIdList.\n */\ntypedef struct IntermediateResultsHashEntry\n{\n\tchar key[NAMEDATALEN];\n\tList *nodeIdList;\n\tbool writeLocalFile;\n} IntermediateResultsHashEntry;\n\n#endif /* SUBPLAN_EXECUTION_H */\n"
  },
  {
    "path": "src/include/distributed/task_execution_utils.h",
    "content": "#ifndef TASK_EXECUTION_UTILS_H\n#define TASK_EXECUTION_UTILS_H\n\nextern List * CreateTaskListForJobTree(List *jobTaskList);\n\n#endif /* TASK_EXECUTION_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/tdigest_extension.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * tdigest_extension.c\n *    Helper functions to get access to tdigest specific data.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_TDIGEST_EXTENSION_H\n#define CITUS_TDIGEST_EXTENSION_H\n\n/* tdigest related functions */\nextern Oid TDigestExtensionSchema(void);\nextern Oid TDigestExtensionTypeOid(void);\nextern Oid TDigestExtensionAggTDigest1(void);\nextern Oid TDigestExtensionAggTDigest2(void);\nextern Oid TDigestExtensionAggTDigestPercentile2(void);\nextern Oid TDigestExtensionAggTDigestPercentile2a(void);\nextern Oid TDigestExtensionAggTDigestPercentile3(void);\nextern Oid TDigestExtensionAggTDigestPercentile3a(void);\nextern Oid TDigestExtensionAggTDigestPercentileOf2(void);\nextern Oid TDigestExtensionAggTDigestPercentileOf2a(void);\nextern Oid TDigestExtensionAggTDigestPercentileOf3(void);\nextern Oid TDigestExtensionAggTDigestPercentileOf3a(void);\n\n#endif /* CITUS_TDIGEST_EXTENSION_H */\n"
  },
  {
    "path": "src/include/distributed/tenant_schema_metadata.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * tenant_schema_metadata.h\n *\n * This file contains functions to query and modify tenant schema metadata,\n * which is used to track the schemas used for schema-based sharding in\n * Citus.\n *\n * -------------------------------------------------------------------------\n */\n\n#ifndef TENANT_SCHEMA_METADATA_H\n#define TENANT_SCHEMA_METADATA_H\n\n#include \"postgres.h\"\n\n/* accessors */\nextern Oid ColocationIdGetTenantSchemaId(uint32 colocationId);\nextern uint32 SchemaIdGetTenantColocationId(Oid schemaId);\nextern bool IsTenantSchema(Oid schemaId);\nextern bool IsTenantSchemaColocationGroup(uint32 colocationId);\n\n/*\n * Local only modifiers.\n *\n * These functions may not make much sense by themselves. They are mainly\n * exported for tenant-schema management (schema_based_sharding.c) and\n * metadata-sync layer (metadata_sync.c).\n */\nextern void InsertTenantSchemaLocally(Oid schemaId, uint32 colocationId);\nextern void DeleteTenantSchemaLocally(Oid schemaId);\n\n#endif /* TENANT_SCHEMA_METADATA_H */\n"
  },
  {
    "path": "src/include/distributed/time_constants.h",
    "content": "/*-------------------------------------------------------------------------\n * time_constants.h\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef TIME_CONSTANTS_H\n#define TIME_CONSTANTS_H\n\n#define MS 1\n#define MS_PER_SECOND MS * 1000\n#define MS_PER_MINUTE MS_PER_SECOND * 60\n#define MS_PER_HOUR MS_PER_MINUTE * 60\n#define MS_PER_DAY MS_PER_HOUR * 24\n\n#endif /* TIME_CONSTANTS_H */\n"
  },
  {
    "path": "src/include/distributed/transaction_identifier.h",
    "content": "/*\n * transaction_identifier.h\n *\n *    Data structure for distributed transaction id and related function\n *    declarations.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef TRANSACTION_IDENTIFIER_H\n#define TRANSACTION_IDENTIFIER_H\n\n\n#include \"datatype/timestamp.h\"\n\n\n/*\n * Citus identifies a distributed transaction with a triplet consisting of\n *\n *  -  initiatorNodeIdentifier: A unique identifier of the node that initiated\n *     the distributed transaction\n *  -  transactionOriginator: Set to true only for the transactions initialized on\n *     the coordinator. This is only useful for MX in order to distinguish the transaction\n *     that started the distributed transaction on the coordinator where we could\n *     have the same transactions' worker queries on the same node\n *  -  transactionNumber: A locally unique identifier assigned for the distributed\n *     transaction on the node that initiated the distributed transaction\n *  -  timestamp: The current timestamp of distributed transaction initiation\n *\n */\ntypedef struct DistributedTransactionId\n{\n\tint initiatorNodeIdentifier;\n\tbool transactionOriginator;\n\tuint64 transactionNumber;\n\tTimestampTz timestamp;\n} DistributedTransactionId;\n\n\nextern DistributedTransactionId * GetCurrentDistributedTransactionId(void);\nextern uint64 CurrentDistributedTransactionNumber(void);\n\n#endif /* TRANSACTION_IDENTIFIER_H */\n"
  },
  {
    "path": "src/include/distributed/transaction_management.h",
    "content": "/*-------------------------------------------------------------------------\n * transaction_management.h\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef TRANSACTION_MANAGMENT_H\n#define TRANSACTION_MANAGMENT_H\n\n#include \"access/xact.h\"\n#include \"catalog/objectaddress.h\"\n#include \"lib/ilist.h\"\n#include \"lib/stringinfo.h\"\n#include \"nodes/pg_list.h\"\n#include \"nodes/primnodes.h\"\n#include \"utils/hsearch.h\"\n\n/* forward declare, to avoid recursive includes */\nstruct DistObjectCacheEntry;\n\n\n/* describes what kind of modifications have occurred in the current transaction */\ntypedef enum\n{\n\tXACT_MODIFICATION_INVALID = 0, /* placeholder initial value */\n\tXACT_MODIFICATION_NONE,        /* no modifications have taken place */\n\tXACT_MODIFICATION_DATA,        /* data modifications (DML) have occurred */\n\tXACT_MODIFICATION_MULTI_SHARD  /* multi-shard modifications have occurred */\n} XactModificationType;\n\n\n/*\n * Enum defining the state of a coordinated (i.e. a transaction potentially\n * spanning several nodes).\n */\ntypedef enum CoordinatedTransactionState\n{\n\t/* no coordinated transaction in progress, no connections established */\n\tCOORD_TRANS_NONE,\n\n\t/* no coordinated transaction in progress, but connections established */\n\tCOORD_TRANS_IDLE,\n\n\t/* coordinated transaction in progress */\n\tCOORD_TRANS_STARTED,\n\n\t/* coordinated transaction prepared on all workers */\n\tCOORD_TRANS_PREPARED,\n\n\t/* coordinated transaction committed */\n\tCOORD_TRANS_COMMITTED\n} CoordinatedTransactionState;\n\n\n/* Enumeration to keep track of context within nested sub-transactions */\ntypedef struct SubXactContext\n{\n\tSubTransactionId subId;\n\tStringInfo setLocalCmds;\n\tHTAB *propagatedObjects;\n} SubXactContext;\n\n/*\n * Function delegated with force_delegation call enforces the distribution argument\n * along with the colocationId. The latter one is equally important to not allow\n * the same partition key value into another distributed table which is not co-located\n * and therefore might be on a different node.\n */\ntypedef struct AllowedDistributionColumn\n{\n\tConst *distributionColumnValue;\n\tuint32 colocationId;\n\tbool isActive;\n\n\t/* In nested executor, track the level at which value is set */\n\tint executorLevel;\n} AllowedDistributionColumn;\n\n/*\n * BeginXactDeferrableState reflects the value of the DEFERRABLE property\n * in the BEGIN of a transaction block.\n */\ntypedef enum BeginXactDeferrableState\n{\n\tBeginXactDeferrable_NotSet,\n\tBeginXactDeferrable_Disabled,\n\tBeginXactDeferrable_Enabled,\n} BeginXactDeferrableState;\n\n/*\n * BeginXactReadOnlyState reflects the value of the READ ONLY property\n * in the BEGIN of a transaction block.\n */\ntypedef enum BeginXactReadOnlyState\n{\n\tBeginXactReadOnly_NotSet,\n\tBeginXactReadOnly_Disabled,\n\tBeginXactReadOnly_Enabled,\n} BeginXactReadOnlyState;\n\n/*\n * The current distribution column value passed as an argument to a forced\n * function call delegation.\n */\nextern AllowedDistributionColumn AllowedDistributionColumnValue;\n\n/*\n * GUC that determines whether a SELECT in a transaction block should also run in\n * a transaction block on the worker.\n */\nextern bool SelectOpensTransactionBlock;\n\n/*\n * GUC that determines whether a function should be considered a transaction\n * block.\n */\nextern bool FunctionOpensTransactionBlock;\n\n/* state needed to prevent new connections during modifying transactions */\nextern XactModificationType XactModificationLevel;\n\nextern CoordinatedTransactionState CurrentCoordinatedTransactionState;\n\n/* list of connections that are part of the current coordinated transaction */\nextern dlist_head InProgressTransactions;\n\n/* controls use of locks to enforce safe commutativity */\nextern bool AllModificationsCommutative;\n\n/* we've deprecated this flag, keeping here for some time not to break existing users */\nextern bool EnableDeadlockPrevention;\n\n/* number of nested stored procedure call levels we are currently in */\nextern int StoredProcedureLevel;\n\n/* number of nested DO block levels we are currently in */\nextern int DoBlockLevel;\n\n/* SET LOCAL statements active in the current (sub-)transaction. */\nextern StringInfo activeSetStmts;\n\n/* did current transaction modify pg_dist_node? */\nextern bool TransactionModifiedNodeMetadata;\n\n/* after an explicit BEGIN, keep track of top-level transaction characteristics */\nextern BeginXactReadOnlyState BeginXactReadOnly;\nextern BeginXactDeferrableState BeginXactDeferrable;\n\n/*\n * Coordinated transaction management.\n */\nextern void UseCoordinatedTransaction(void);\nextern bool InCoordinatedTransaction(void);\nextern void Use2PCForCoordinatedTransaction(void);\nextern bool GetCoordinatedTransactionShouldUse2PC(void);\nextern bool IsMultiStatementTransaction(void);\nextern void EnsureDistributedTransactionId(void);\nextern bool MaybeExecutingUDF(void);\n\n/* functions for tracking the objects propagated in current transaction */\nextern void TrackPropagatedObject(const ObjectAddress *objectAddress);\nextern void TrackPropagatedTableAndSequences(Oid relationId);\nextern void ResetPropagatedObjects(void);\nextern bool HasAnyObjectInPropagatedObjects(List *objectList);\n\n/* initialization function(s) */\nextern void InitializeTransactionManagement(void);\n\n/* other functions */\nextern List * ActiveSubXactContexts(void);\nextern StringInfo BeginAndSetDistributedTransactionIdCommand(void);\nextern void TriggerNodeMetadataSyncOnCommit(void);\n\n\n#endif /*  TRANSACTION_MANAGMENT_H */\n"
  },
  {
    "path": "src/include/distributed/transaction_recovery.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * transaction_recovery.h\n *\t  Type and function declarations used in recovering 2PC transactions.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef TRANSACTION_RECOVERY_H\n#define TRANSACTION_RECOVERY_H\n\n\n/* GUC to configure interval for 2PC auto-recovery */\nextern int Recover2PCInterval;\n\n\n/* Functions declarations for worker transactions */\nextern void LogTransactionRecord(int32 groupId, char *transactionName,\n\t\t\t\t\t\t\t\t FullTransactionId outerXid);\nextern int RecoverTwoPhaseCommits(void);\nextern void DeleteWorkerTransactions(WorkerNode *workerNode);\n\n#endif /* TRANSACTION_RECOVERY_H */\n"
  },
  {
    "path": "src/include/distributed/transmit.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * transmit.h\n *\t  Shared declarations for transmitting files between remote nodes.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef TRANSMIT_H\n#define TRANSMIT_H\n\n#include \"c.h\"\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"storage/fd.h\"\n\n\n/* Function declarations for transmitting files between two nodes */\nextern void RedirectCopyDataToRegularFile(const char *filename);\nextern void SendRegularFile(const char *filename);\nextern File FileOpenForTransmit(const char *filename, int fileFlags);\nextern File FileOpenForTransmitPerm(const char *filename, int fileFlags, int fileMode);\n\n\n#endif   /* TRANSMIT_H */\n"
  },
  {
    "path": "src/include/distributed/tuple_destination.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * tuple_destination.h\n *\t  Tuple destination generic struct.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef TUPLE_DESTINATION_H\n#define TUPLE_DESTINATION_H\n\n#include \"access/tupdesc.h\"\n#include \"tcop/dest.h\"\n#include \"utils/tuplestore.h\"\n\n#include \"distributed/multi_physical_planner.h\"\n\n\ntypedef struct TupleDestination TupleDestination;\n\n\n/*\n * TupleDestinationStats holds the size related stats.\n *\n * totalIntermediateResultSize is a counter to keep the size\n * of the intermediate results of complex subqueries and CTEs\n * so that we can put a limit on the size.\n */\ntypedef struct TupleDestinationStats\n{\n\tuint64 totalIntermediateResultSize;\n} TupleDestinationStats;\n\n\n/*\n * TupleDestination provides a generic interface for where to send tuples.\n *\n * Users of the executor can set task->tupleDest for custom processing of\n * the result tuples.\n *\n * Since a task can have multiple queries, methods of TupleDestination also\n * accept a queryNumber parameter which denotes the index of the query that\n * tuple belongs to.\n */\nstruct TupleDestination\n{\n\t/* putTuple implements custom processing of a tuple */\n\tvoid (*putTuple)(TupleDestination *self, Task *task,\n\t\t\t\t\t int placementIndex, int queryNumber,\n\t\t\t\t\t HeapTuple tuple, uint64 tupleLibpqSize);\n\n\t/* tupleDescForQuery returns tuple descriptor for a query number. Can return NULL. */\n\tTupleDesc (*tupleDescForQuery)(TupleDestination *self, int queryNumber);\n\n\t/*\n\t * Used to enforce citus.max_intermediate_result_size, could be NULL\n\t * if the caller is not interested in the size.\n\t */\n\tTupleDestinationStats *tupleDestinationStats;\n};\n\nextern TupleDestination * CreateTupleStoreTupleDest(Tuplestorestate *tupleStore, TupleDesc\n\t\t\t\t\t\t\t\t\t\t\t\t\ttupleDescriptor);\nextern TupleDestination * CreateTupleDestNone(void);\nextern DestReceiver * CreateTupleDestDestReceiver(TupleDestination *tupleDest,\n\t\t\t\t\t\t\t\t\t\t\t\t  Task *task, int placementIndex);\n\n#endif\n"
  },
  {
    "path": "src/include/distributed/tuplestore.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * tuplestore.h\n *\t  Utilities regarding calls to PG functions\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n\n#ifndef CITUS_TUPLESTORE_H\n#define CITUS_TUPLESTORE_H\n#include \"funcapi.h\"\n\n/* Function declaration for getting oid for the given function name */\nextern\nReturnSetInfo * CheckTuplestoreReturn(FunctionCallInfo fcinfo, TupleDesc *tupdesc);\n\nextern\nTuplestorestate * SetupTuplestore(FunctionCallInfo fcinfo, TupleDesc *tupdesc);\n#endif\n"
  },
  {
    "path": "src/include/distributed/type_utils.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * type_utils.h\n *\t  Utility functions related to types.\n *\n * Copyright (c) Citus Data, Inc.\n *-------------------------------------------------------------------------\n */\n\n#ifndef TYPE_UTILS_H\n#define TYPE_UTILS_H\n\ntypedef struct ClusterClock\n{\n\tuint64 logical;     /* cluster clock logical timestamp at the commit */\n\tuint32 counter;     /* cluster clock counter value at the commit */\n} ClusterClock;\n\n\nextern ClusterClock * ParseClusterClockPGresult(PGresult *result,\n\t\t\t\t\t\t\t\t\t\t\t\tint rowIndex, int colIndex);\nextern int cluster_clock_cmp_internal(ClusterClock *clusterClock1,\n\t\t\t\t\t\t\t\t\t  ClusterClock *clusterClock2);\n\n#endif /* TYPE_UTILS_H */\n"
  },
  {
    "path": "src/include/distributed/utils/array_type.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * array_type.h\n *\t  Utility functions for dealing with array types.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_ARRAY_TYPE_H\n#define CITUS_ARRAY_TYPE_H\n\n#include \"postgres.h\"\n\n#include \"utils/array.h\"\n\n\nextern Datum * DeconstructArrayObject(ArrayType *arrayObject);\nextern int32 ArrayObjectCount(ArrayType *arrayObject);\nextern ArrayType * DatumArrayToArrayType(Datum *datumArray, int datumCount,\n\t\t\t\t\t\t\t\t\t\t Oid datumTypeId);\nextern List * IntegerArrayTypeToList(ArrayType *arrayObject);\nextern List * TextArrayTypeToIntegerList(ArrayType *arrayObject);\nextern Datum IntArrayToDatum(uint32 int_array_size, int int_array[]);\n\n#endif   /* CITUS_ARRAY_TYPE_H */\n"
  },
  {
    "path": "src/include/distributed/utils/directory.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * directory.h\n *\t  Utility functions for dealing with directories.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_DIRECTORY_H\n#define CITUS_DIRECTORY_H\n\n#include \"postgres.h\"\n\n#include \"lib/stringinfo.h\"\n\n\n#define PG_JOB_CACHE_DIR \"pgsql_job_cache\"\n\n\nextern void CleanupJobCacheDirectory(void);\nextern void CitusCreateDirectory(StringInfo directoryName);\nextern void CitusRemoveDirectory(const char *filename);\n\n\n#endif   /* CITUS_DIRECTORY_H */\n"
  },
  {
    "path": "src/include/distributed/utils/distribution_column_map.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * distribution_column_map.h\n *\t  Declarations for a relation OID to distribution column hash.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef DISTRIBUTION_COLUMN_HASH_H\n#define DISTRIBUTION_COLUMN_HASH_H\n\n#include \"postgres.h\"\n\n#include \"nodes/primnodes.h\"\n#include \"utils/hsearch.h\"\n\n\ntypedef HTAB DistributionColumnMap;\n\n\nextern DistributionColumnMap * CreateDistributionColumnMap(void);\nextern void AddDistributionColumnForRelation(DistributionColumnMap *distributionColumns,\n\t\t\t\t\t\t\t\t\t\t\t Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t char *distributionColumnName);\nextern Var * GetDistributionColumnFromMap(DistributionColumnMap *distributionColumnMap,\n\t\t\t\t\t\t\t\t\t\t  Oid relationId);\nextern Var * GetDistributionColumnWithOverrides(Oid relationId,\n\t\t\t\t\t\t\t\t\t\t\t\tDistributionColumnMap *overrides);\n\n#endif   /* DISTRIBUTION_COLUMN_HASH_H */\n"
  },
  {
    "path": "src/include/distributed/utils/function.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * function.h\n *\t  Utility functions for dealing with functions.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef CITUS_FUNCTION_H\n#define CITUS_FUNCTION_H\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n\nextern FmgrInfo * GetFunctionInfo(Oid typeId, Oid accessMethodId, int16 procedureId);\n\n\n#endif   /* CITUS_FUNCTION_H */\n"
  },
  {
    "path": "src/include/distributed/version_compat.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * version_compat.h\n *\t  Compatibility macros for writing code agnostic to PostgreSQL versions\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef VERSION_COMPAT_H\n#define VERSION_COMPAT_H\n\n#include \"postgres.h\"\n\n#include \"access/heapam.h\"\n#include \"access/sdir.h\"\n#include \"catalog/namespace.h\"\n#include \"commands/explain.h\"\n#include \"executor/tuptable.h\"\n#include \"nodes/parsenodes.h\"\n#include \"optimizer/optimizer.h\"\n#include \"parser/parse_func.h\"\n#include \"tcop/tcopprot.h\"\n\n#include \"pg_version_compat.h\"\n\n#include \"distributed/citus_ruleutils.h\"\n#include \"distributed/citus_safe_lib.h\"\n\ntypedef struct\n{\n\tFile fd;\n\toff_t offset;\n} FileCompat;\n\nstatic inline int\nFileWriteCompat(FileCompat *file, char *buffer, int amount, uint32 wait_event_info)\n{\n\tint count = FileWrite(file->fd, buffer, amount, file->offset, wait_event_info);\n\tif (count > 0)\n\t{\n\t\tfile->offset += count;\n\t}\n\treturn count;\n}\n\n\nstatic inline int\nFileReadCompat(FileCompat *file, char *buffer, int amount, uint32 wait_event_info)\n{\n\tint count = FileRead(file->fd, buffer, amount, file->offset, wait_event_info);\n\tif (count > 0)\n\t{\n\t\tfile->offset += count;\n\t}\n\treturn count;\n}\n\n\nstatic inline FileCompat\nFileCompatFromFileStart(File fileDesc)\n{\n\tFileCompat fc;\n\n\t/* ensure uninitialized padding doesn't escape the function */\n\tmemset_struct_0(fc);\n\tfc.fd = fileDesc;\n\tfc.offset = 0;\n\n\treturn fc;\n}\n\n\n#endif   /* VERSION_COMPAT_H */\n"
  },
  {
    "path": "src/include/distributed/worker_create_or_replace.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_create_or_replace.h\n *\t  Header for handling CREATE OR REPLACE of objects,\n *\t  even if postgres lacks CREATE OR REPLACE for those objects\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef WORKER_CREATE_OR_REPLACE_H\n#define WORKER_CREATE_OR_REPLACE_H\n\n#include \"catalog/objectaddress.h\"\n\n#define CREATE_OR_REPLACE_COMMAND \"SELECT worker_create_or_replace_object(%s);\"\n\nextern char * WrapCreateOrReplace(const char *sql);\nextern char * WrapCreateOrReplaceList(List *sqls);\nextern char * GenerateBackupNameForCollision(const ObjectAddress *address);\nextern DropStmt * CreateDropStmt(const ObjectAddress *address);\nextern RenameStmt * CreateRenameStatement(const ObjectAddress *address, char *newName);\n\n#endif /* WORKER_CREATE_OR_REPLACE_H */\n"
  },
  {
    "path": "src/include/distributed/worker_log_messages.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_log_messages.h\n *   Functions for handling log messages from the workers.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef WORKER_LOG_MESSAGES_H\n#define WORKER_LOG_MESSAGES_H\n\n\n#include \"distributed/connection_management.h\"\n\n\n/* minimum log level for worker messages to be propagated */\nextern int WorkerMinMessages;\n\nvoid SetCitusNoticeReceiver(MultiConnection *connection);\nvoid EnableWorkerMessagePropagation(void);\nvoid DisableWorkerMessagePropagation(void);\nvoid ErrorIfWorkerErrorIndicationReceived(void);\nvoid ResetWorkerErrorIndication(void);\n\n\n#endif /* WORKER_LOG_MESSAGES_H */\n"
  },
  {
    "path": "src/include/distributed/worker_manager.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_manager.h\n *\t  Header and type declarations for managing worker nodes and for placing\n *\t  shards on worker nodes in an intelligent manner.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef WORKER_MANAGER_H\n#define WORKER_MANAGER_H\n\n#include \"postgres.h\"\n\n#include \"nodes/pg_list.h\"\n#include \"storage/lmgr.h\"\n#include \"storage/lockdefs.h\"\n\n\n/* Worker nodeName's, nodePort's, and nodeCluster's maximum length */\n#define WORKER_LENGTH 256\n\n/* Maximum length of worker port number (represented as string) */\n#define MAX_PORT_LENGTH 10\n\n/* Implementation specific definitions used in finding worker nodes */\n#define WORKER_RACK_TRIES 5\n#define WORKER_DEFAULT_RACK \"default\"\n\n#define WORKER_DEFAULT_CLUSTER \"default\"\n\n#define COORDINATOR_GROUP_ID 0\n\n/*\n * In memory representation of pg_dist_node table elements. The elements are hold in\n * WorkerNodeHash table.\n * IMPORTANT: The order of the fields in this definition should match the\n * column order of pg_dist_node\n */\ntypedef struct WorkerNode\n{\n\tuint32 nodeId;                      /* node's unique id, key of the hash table */\n\tuint32 workerPort;                  /* node's port */\n\tchar workerName[WORKER_LENGTH];     /* node's name */\n\tint32 groupId;                      /* node's groupId; same for the nodes that are in the same group */\n\tchar workerRack[WORKER_LENGTH];     /* node's network location */\n\tbool hasMetadata;                   /* node gets metadata changes */\n\tbool isActive;                      /* node's state */\n\tOid nodeRole;                       /* the node's role in its group */\n\tchar nodeCluster[NAMEDATALEN];      /* the cluster the node is a part of */\n\tbool metadataSynced;                /* node has the most recent metadata */\n\tbool shouldHaveShards;              /* if the node should have distributed table shards on it or not */\n\tbool nodeisclone;                 /* whether this node is a replica */\n\tint32 nodeprimarynodeid;            /* nodeid of the primary for this replica */\n} WorkerNode;\n\n\n/* Config variables managed via guc.c */\nextern int MaxWorkerNodesTracked;\nextern char *WorkerListFileName;\nextern char *CurrentCluster;\n\n/* Function declarations for finding worker nodes to place shards on */\nextern WorkerNode * WorkerGetRandomCandidateNode(List *currentNodeList);\nextern WorkerNode * WorkerGetRoundRobinCandidateNode(List *workerNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t uint64 shardId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t uint32 placementIndex);\nextern uint32 ActivePrimaryNonCoordinatorNodeCount(void);\nextern uint32 ActiveReadableNodeCount(void);\nextern List * ActivePrimaryNonCoordinatorNodeList(LOCKMODE lockMode);\nextern List * ActivePrimaryNodeList(LOCKMODE lockMode);\nextern List * ActivePrimaryRemoteNodeList(LOCKMODE lockMode);\nextern bool CoordinatorAddedAsWorkerNode(void);\nextern List * ReferenceTablePlacementNodeList(LOCKMODE lockMode);\nextern WorkerNode * CoordinatorNodeIfAddedAsWorkerOrError(void);\nextern void ErrorIfCoordinatorNotAddedAsWorkerNode(void);\nextern List * DistributedTablePlacementNodeList(LOCKMODE lockMode);\nextern bool NodeCanHaveDistTablePlacements(WorkerNode *node);\nextern List * ActiveReadableNonCoordinatorNodeList(void);\nextern List * ActiveReadableNodeList(void);\nextern WorkerNode * FindWorkerNode(const char *nodeName, int32 nodePort);\nextern WorkerNode * FindWorkerNodeOrError(const char *nodeName, int32 nodePort);\nextern WorkerNode * FindWorkerNodeAnyCluster(const char *nodeName, int32 nodePort);\nextern WorkerNode * FindNodeWithNodeId(int nodeId, bool missingOk);\nextern WorkerNode * FindNodeAnyClusterByNodeId(uint32 nodeId);\nextern WorkerNode * ModifiableWorkerNode(const char *nodeName, int32 nodePort);\nextern List * ReadDistNode(bool includeNodesFromOtherClusters);\nextern void EnsureCoordinator(void);\nextern void EnsurePropagationToCoordinator(void);\nextern void EnsureCoordinatorIsInMetadata(void);\nextern void InsertCoordinatorIfClusterEmpty(void);\nextern uint32 GroupForNode(char *nodeName, int32 nodePort);\nextern WorkerNode * PrimaryNodeForGroup(int32 groupId, bool *groupContainsNodes);\nextern bool NodeIsPrimaryAndRemote(WorkerNode *worker);\nextern bool NodeIsPrimary(WorkerNode *worker);\nextern bool NodeIsSecondary(WorkerNode *worker);\nextern bool NodeIsReadable(WorkerNode *worker);\nextern bool NodeIsCoordinator(WorkerNode *node);\nextern WorkerNode * SetWorkerColumn(WorkerNode *workerNode, int columnIndex, Datum value);\nextern WorkerNode * SetWorkerColumnOptional(WorkerNode *workerNode, int columnIndex, Datum\n\t\t\t\t\t\t\t\t\t\t\tvalue);\nextern WorkerNode * SetWorkerColumnLocalOnly(WorkerNode *workerNode, int columnIndex,\n\t\t\t\t\t\t\t\t\t\t\t Datum value);\nextern uint32 CountPrimariesWithMetadata(void);\nextern WorkerNode * GetFirstPrimaryWorkerNode(void);\n\n/* Function declarations for worker node utilities */\nextern int CompareWorkerNodes(const void *leftElement, const void *rightElement);\nextern uint32 WorkerNodeHashCode(const void *key, Size keySize);\nextern int WorkerNodeCompare(const void *lhsKey, const void *rhsKey, Size keySize);\nextern int NodeNamePortCompare(const char *workerLhsName, const char *workerRhsName,\n\t\t\t\t\t\t\t   int workerLhsPort, int workerRhsPort);\n\n#endif   /* WORKER_MANAGER_H */\n"
  },
  {
    "path": "src/include/distributed/worker_protocol.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_protocol.h\n *\t  Header for shared declarations that are used for caching remote resources\n *\t  on worker nodes, and also for applying distributed execution primitives.\n *\n * Copyright (c) Citus Data, Inc.\n *\n * $Id$\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef WORKER_PROTOCOL_H\n#define WORKER_PROTOCOL_H\n\n#include \"postgres.h\"\n\n#include \"fmgr.h\"\n\n#include \"lib/stringinfo.h\"\n#include \"nodes/parsenodes.h\"\n#include \"storage/fd.h\"\n#include \"utils/array.h\"\n\n#include \"distributed/shardinterval_utils.h\"\n#include \"distributed/version_compat.h\"\n\n\n/* Number of rows to prefetch when reading data with a cursor */\n#define ROW_PREFETCH_COUNT 50\n\n/* Defines that relate to creating tables */\n#define GET_TABLE_DDL_EVENTS \"SELECT master_get_table_ddl_events('%s')\"\n#define SET_SEARCH_PATH_COMMAND \"SET search_path TO %s\"\n#define CREATE_TABLE_COMMAND \"CREATE TABLE %s (%s)\"\n#define CREATE_TABLE_AS_COMMAND \"CREATE TABLE %s (%s) AS (%s)\"\n\n\n/* Function declarations local to the worker module */\nextern uint64 ExtractShardIdFromTableName(const char *tableName, bool missingOk);\nextern void SetDefElemArg(AlterSeqStmt *statement, const char *name, Node *arg);\n\n\n/* Function declarations shared with the master planner */\nextern DestReceiver * CreateFileDestReceiver(char *filePath,\n\t\t\t\t\t\t\t\t\t\t\t MemoryContext tupleContext,\n\t\t\t\t\t\t\t\t\t\t\t bool binaryCopyFormat);\nextern void FileDestReceiverStats(DestReceiver *dest,\n\t\t\t\t\t\t\t\t  uint64 *rowsSent,\n\t\t\t\t\t\t\t\t  uint64 *bytesSent);\n\n/* Function declaration for parsing tree node */\nextern Node * ParseTreeNode(const char *ddlCommand);\nextern Node * ParseTreeRawStmt(const char *ddlCommand);\n\n/* Function declarations for applying distributed execution primitives */\nextern Datum worker_apply_shard_ddl_command(PG_FUNCTION_ARGS);\n\n/* Function declarations for fetching regular and foreign tables */\nextern Datum worker_append_table_to_shard(PG_FUNCTION_ARGS);\n\n/* Function declaration for calculating hashed value */\nextern Datum worker_hash(PG_FUNCTION_ARGS);\n\n/* Function declaration for calculating nextval() in worker */\nextern Datum worker_nextval(PG_FUNCTION_ARGS);\n\n#endif   /* WORKER_PROTOCOL_H */\n"
  },
  {
    "path": "src/include/distributed/worker_shard_copy.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_shard_copy.c\n *\t Copy data to destination shard in a push approach.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef WORKER_SHARD_COPY_H_\n#define WORKER_SHARD_COPY_H_\n\n/* GUC, determining whether Binary Copy is enabled */\nextern bool EnableBinaryProtocol;\n\nextern DestReceiver * CreateShardCopyDestReceiver(EState *executorState,\n\t\t\t\t\t\t\t\t\t\t\t\t  List *\n\t\t\t\t\t\t\t\t\t\t\t\t  destinationShardFullyQualifiedName,\n\t\t\t\t\t\t\t\t\t\t\t\t  uint32_t destinationNodeId);\n\nextern const char * CopyableColumnNamesFromRelationName(const char *schemaName, const\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar *relationName);\n\nextern const char * CopyableColumnNamesFromTupleDesc(TupleDesc tupdesc);\n\n#endif /* WORKER_SHARD_COPY_H_ */\n"
  },
  {
    "path": "src/include/distributed/worker_shard_visibility.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_shard_visibility.h\n *   Hide shard names on MX worker nodes.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef WORKER_SHARD_VISIBILITY_H\n#define WORKER_SHARD_VISIBILITY_H\n\n#include \"nodes/nodes.h\"\n\nextern bool OverrideTableVisibility;\nextern bool EnableManualChangesToShards;\nextern char *ShowShardsForAppNamePrefixes;\n\n\nextern void HideShardsFromSomeApplications(Query *query);\nextern void ResetHideShardsDecision(void);\nextern void ErrorIfRelationIsAKnownShard(Oid relationId);\nextern void ErrorIfIllegallyChangingKnownShard(Oid relationId);\nextern bool RelationIsAKnownShard(Oid shardRelationId);\n\n\n#endif /* WORKER_SHARD_VISIBILITY_H */\n"
  },
  {
    "path": "src/include/distributed/worker_transaction.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * worker_transaction.h\n *\t  Type and function declarations used in performing transactions across\n *\t  workers.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef WORKER_TRANSACTION_H\n#define WORKER_TRANSACTION_H\n\n#include \"storage/lockdefs.h\"\n\n#include \"distributed/connection_management.h\"\n#include \"distributed/worker_manager.h\"\n\n\n/*\n * TargetWorkerSet is used for determining the type of workers that a command\n * is targeted to.\n */\ntypedef enum TargetWorkerSet\n{\n\t/*\n\t * All the active primary nodes in the metadata which have metadata\n\t * except the coordinator\n\t */\n\tNON_COORDINATOR_METADATA_NODES,\n\n\t/*\n\t * All the active primary nodes in the metadata which have metadata\n\t * except the local node\n\t */\n\tREMOTE_METADATA_NODES,\n\n\t/*\n\t * All the active primary nodes in the metadata except the coordinator\n\t */\n\tNON_COORDINATOR_NODES,\n\n\t/*\n\t * All the active primary nodes in the metadata except the local node\n\t */\n\tREMOTE_NODES,\n\n\t/*\n\t * All active primary nodes in the metadata\n\t */\n\tALL_SHARD_NODES,\n\n\t/*\n\t * All the active primary nodes in the metadata which have metadata\n\t * (includes the coodinator if it is added)\n\t */\n\tMETADATA_NODES\n} TargetWorkerSet;\n\n\n/* Functions declarations for worker transactions */\nextern List * GetWorkerTransactions(void);\nextern List * TargetWorkerSetNodeList(TargetWorkerSet targetWorkerSet, LOCKMODE lockMode);\nextern void SendCommandToWorker(const char *nodeName, int32 nodePort, const\n\t\t\t\t\t\t\t\tchar *command);\nextern void SendCommandToWorkersAsUser(TargetWorkerSet targetWorkerSet,\n\t\t\t\t\t\t\t\t\t   const char *nodeUser, const char *command);\nextern void SendCommandToWorkerAsUser(const char *nodeName, int32 nodePort,\n\t\t\t\t\t\t\t\t\t  const char *nodeUser, const char *command);\nextern void SendCommandToRemoteMetadataNodesParams(const char *command,\n\t\t\t\t\t\t\t\t\t\t\t\t   const char *user, int parameterCount,\n\t\t\t\t\t\t\t\t\t\t\t\t   const Oid *parameterTypes,\n\t\t\t\t\t\t\t\t\t\t\t\t   const char *const *parameterValues);\nextern bool SendOptionalCommandListToWorkerOutsideTransaction(const char *nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  int32 nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  const char *nodeUser,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  List *commandList);\nextern bool SendOptionalCommandListToWorkerOutsideTransactionWithConnection(\n\tMultiConnection *workerConnection,\n\tList *\n\tcommandList);\nextern bool SendOptionalMetadataCommandListToWorkerInCoordinatedTransaction(const\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tchar *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tint32\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst char *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeUser,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommandList);\nextern void SendCommandToWorkersWithMetadata(const char *command);\nextern void SendCommandToWorkersWithMetadataViaSuperUser(const char *command);\nextern void SendCommandListToWorkersWithMetadata(List *commands);\nextern void SendCommandToRemoteNodesWithMetadata(const char *command);\nextern void SendCommandToRemoteNodesWithMetadataViaSuperUser(const char *command);\nextern void SendCommandListToRemoteNodesWithMetadata(List *commands);\nextern void SendBareCommandListToRemoteMetadataNodes(List *commandList);\nextern void SendBareCommandListToMetadataWorkers(List *commandList);\nextern void EnsureNoModificationsHaveBeenDone(void);\nextern void SendCommandListToWorkerOutsideTransaction(const char *nodeName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  int32 nodePort,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  const char *nodeUser,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  List *commandList);\nextern void SendCommandListToWorkerOutsideTransactionWithConnection(MultiConnection *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerConnection,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *commandList);\nextern void SendCommandListToWorkerListWithBareConnections(List *workerConnections,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   List *commandList);\nextern void SendMetadataCommandListToWorkerListInCoordinatedTransaction(List *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tworkerNodeList,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst char *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnodeUser,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tList *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcommandList);\nextern void RemoveWorkerTransaction(const char *nodeName, int32 nodePort);\n\n/* helper functions for worker transactions */\nextern bool IsWorkerTransactionActive(void);\n\nextern bool IsWorkerTheCurrentNode(WorkerNode *workerNode);\n\n#endif /* WORKER_TRANSACTION_H */\n"
  },
  {
    "path": "src/include/pg_version_compat.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_version_compat.h\n *\t  Compatibility macros for writing code agnostic to PostgreSQL versions\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_VERSION_COMPAT_H\n#define PG_VERSION_COMPAT_H\n\n#include \"pg_version_constants.h\"\n\n#if PG_VERSION_NUM >= PG_VERSION_18\n#define create_foreignscan_path_compat(a, b, c, d, e, f, g, h, i, j, k) \\\n\t\tcreate_foreignscan_path( \\\n\t\t\t(a),        /* root            */ \\\n\t\t\t(b),        /* rel             */ \\\n\t\t\t(c),        /* target          */ \\\n\t\t\t(d),        /* rows            */ \\\n\t\t\t0,          /* disabled_nodes  */ \\\n\t\t\t(e),        /* startup_cost    */ \\\n\t\t\t(f),        /* total_cost      */ \\\n\t\t\t(g),        /* pathkeys        */ \\\n\t\t\t(h),        /* required_outer  */ \\\n\t\t\t(i),        /* fdw_outerpath   */ \\\n\t\t\t(j),        /* fdw_restrictinfo*/ \\\n\t\t\t(k)         /* fdw_private     */ \\\n\t\t\t)\n\n/* PG-18 introduced get_op_index_interpretation, old name was get_op_btree_interpretation */\n#define get_op_btree_interpretation(opno) get_op_index_interpretation(opno)\n\n/* PG-18 unified row-compare operator codes under COMPARE_* */\n#define ROWCOMPARE_NE COMPARE_NE\n\n#elif PG_VERSION_NUM >= PG_VERSION_17\n#define create_foreignscan_path_compat(a, b, c, d, e, f, g, h, i, j, k) \\\n\t\tcreate_foreignscan_path( \\\n\t\t\t(a), (b), (c), (d), \\\n\t\t\t(e), (f), \\\n\t\t\t(g), (h), (i), (j), (k) \\\n\t\t\t)\n\n#endif\n\n#if PG_VERSION_NUM >= PG_VERSION_17\n\n#include \"catalog/pg_am.h\"\n#include \"catalog/pg_amop.h\"\n#include \"catalog/pg_amproc.h\"\n#include \"catalog/pg_attrdef.h\"\n#include \"catalog/pg_auth_members.h\"\n#include \"catalog/pg_authid.h\"\n#include \"catalog/pg_cast.h\"\n#include \"catalog/pg_class.h\"\n#include \"catalog/pg_collation.h\"\n#include \"catalog/pg_constraint.h\"\n#include \"catalog/pg_conversion.h\"\n#include \"catalog/pg_database.h\"\n#include \"catalog/pg_default_acl.h\"\n#include \"catalog/pg_depend.h\"\n#include \"catalog/pg_event_trigger.h\"\n#include \"catalog/pg_extension.h\"\n#include \"catalog/pg_foreign_data_wrapper.h\"\n#include \"catalog/pg_foreign_server.h\"\n#include \"catalog/pg_init_privs.h\"\n#include \"catalog/pg_language.h\"\n#include \"catalog/pg_largeobject.h\"\n#include \"catalog/pg_namespace.h\"\n#include \"catalog/pg_opclass.h\"\n#include \"catalog/pg_operator.h\"\n#include \"catalog/pg_opfamily.h\"\n#include \"catalog/pg_parameter_acl.h\"\n#include \"catalog/pg_policy.h\"\n#include \"catalog/pg_proc.h\"\n#include \"catalog/pg_publication.h\"\n#include \"catalog/pg_publication_namespace.h\"\n#include \"catalog/pg_publication_rel.h\"\n#include \"catalog/pg_rewrite.h\"\n#include \"catalog/pg_statistic_ext.h\"\n#include \"catalog/pg_subscription.h\"\n#include \"catalog/pg_tablespace.h\"\n#include \"catalog/pg_transform.h\"\n#include \"catalog/pg_trigger.h\"\n#include \"catalog/pg_ts_config.h\"\n#include \"catalog/pg_ts_dict.h\"\n#include \"catalog/pg_ts_parser.h\"\n#include \"catalog/pg_ts_template.h\"\n#include \"catalog/pg_type.h\"\n#include \"catalog/pg_user_mapping.h\"\n\n/*\n * This enum covers all system catalogs whose OIDs can appear in\n * pg_depend.classId or pg_shdepend.classId.\n */\ntypedef enum ObjectClass\n{\n\tOCLASS_CLASS,               /* pg_class */\n\tOCLASS_PROC,                /* pg_proc */\n\tOCLASS_TYPE,                /* pg_type */\n\tOCLASS_CAST,                /* pg_cast */\n\tOCLASS_COLLATION,           /* pg_collation */\n\tOCLASS_CONSTRAINT,          /* pg_constraint */\n\tOCLASS_CONVERSION,          /* pg_conversion */\n\tOCLASS_DEFAULT,             /* pg_attrdef */\n\tOCLASS_LANGUAGE,            /* pg_language */\n\tOCLASS_LARGEOBJECT,         /* pg_largeobject */\n\tOCLASS_OPERATOR,            /* pg_operator */\n\tOCLASS_OPCLASS,             /* pg_opclass */\n\tOCLASS_OPFAMILY,            /* pg_opfamily */\n\tOCLASS_AM,                  /* pg_am */\n\tOCLASS_AMOP,                /* pg_amop */\n\tOCLASS_AMPROC,              /* pg_amproc */\n\tOCLASS_REWRITE,             /* pg_rewrite */\n\tOCLASS_TRIGGER,             /* pg_trigger */\n\tOCLASS_SCHEMA,              /* pg_namespace */\n\tOCLASS_STATISTIC_EXT,       /* pg_statistic_ext */\n\tOCLASS_TSPARSER,            /* pg_ts_parser */\n\tOCLASS_TSDICT,              /* pg_ts_dict */\n\tOCLASS_TSTEMPLATE,          /* pg_ts_template */\n\tOCLASS_TSCONFIG,            /* pg_ts_config */\n\tOCLASS_ROLE,                /* pg_authid */\n\tOCLASS_ROLE_MEMBERSHIP,     /* pg_auth_members */\n\tOCLASS_DATABASE,            /* pg_database */\n\tOCLASS_TBLSPACE,            /* pg_tablespace */\n\tOCLASS_FDW,                 /* pg_foreign_data_wrapper */\n\tOCLASS_FOREIGN_SERVER,      /* pg_foreign_server */\n\tOCLASS_USER_MAPPING,        /* pg_user_mapping */\n\tOCLASS_DEFACL,              /* pg_default_acl */\n\tOCLASS_EXTENSION,           /* pg_extension */\n\tOCLASS_EVENT_TRIGGER,       /* pg_event_trigger */\n\tOCLASS_PARAMETER_ACL,       /* pg_parameter_acl */\n\tOCLASS_POLICY,              /* pg_policy */\n\tOCLASS_PUBLICATION,         /* pg_publication */\n\tOCLASS_PUBLICATION_NAMESPACE,   /* pg_publication_namespace */\n\tOCLASS_PUBLICATION_REL,     /* pg_publication_rel */\n\tOCLASS_SUBSCRIPTION,        /* pg_subscription */\n\tOCLASS_TRANSFORM,           /* pg_transform */\n} ObjectClass;\n\n#define LAST_OCLASS OCLASS_TRANSFORM\n\n/*\n * Determine the class of a given object identified by objectAddress.\n *\n * We implement it as a function instead of an array because the OIDs aren't\n * consecutive.\n */\nstatic inline ObjectClass\ngetObjectClass(const ObjectAddress *object)\n{\n\t/* only pg_class entries can have nonzero objectSubId */\n\tif (object->classId != RelationRelationId &&\n\t\tobject->objectSubId != 0)\n\t{\n\t\telog(ERROR, \"invalid non-zero objectSubId for object class %u\",\n\t\t\t object->classId);\n\t}\n\n\tswitch (object->classId)\n\t{\n\t\tcase RelationRelationId:\n\t\t{\n\t\t\t/* caller must check objectSubId */\n\t\t\treturn OCLASS_CLASS;\n\t\t}\n\n\t\tcase ProcedureRelationId:\n\t\t{\n\t\t\treturn OCLASS_PROC;\n\t\t}\n\n\t\tcase TypeRelationId:\n\t\t{\n\t\t\treturn OCLASS_TYPE;\n\t\t}\n\n\t\tcase CastRelationId:\n\t\t{\n\t\t\treturn OCLASS_CAST;\n\t\t}\n\n\t\tcase CollationRelationId:\n\t\t{\n\t\t\treturn OCLASS_COLLATION;\n\t\t}\n\n\t\tcase ConstraintRelationId:\n\t\t{\n\t\t\treturn OCLASS_CONSTRAINT;\n\t\t}\n\n\t\tcase ConversionRelationId:\n\t\t{\n\t\t\treturn OCLASS_CONVERSION;\n\t\t}\n\n\t\tcase AttrDefaultRelationId:\n\t\t{\n\t\t\treturn OCLASS_DEFAULT;\n\t\t}\n\n\t\tcase LanguageRelationId:\n\t\t{\n\t\t\treturn OCLASS_LANGUAGE;\n\t\t}\n\n\t\tcase LargeObjectRelationId:\n\t\t{\n\t\t\treturn OCLASS_LARGEOBJECT;\n\t\t}\n\n\t\tcase OperatorRelationId:\n\t\t{\n\t\t\treturn OCLASS_OPERATOR;\n\t\t}\n\n\t\tcase OperatorClassRelationId:\n\t\t{\n\t\t\treturn OCLASS_OPCLASS;\n\t\t}\n\n\t\tcase OperatorFamilyRelationId:\n\t\t{\n\t\t\treturn OCLASS_OPFAMILY;\n\t\t}\n\n\t\tcase AccessMethodRelationId:\n\t\t{\n\t\t\treturn OCLASS_AM;\n\t\t}\n\n\t\tcase AccessMethodOperatorRelationId:\n\t\t{\n\t\t\treturn OCLASS_AMOP;\n\t\t}\n\n\t\tcase AccessMethodProcedureRelationId:\n\t\t{\n\t\t\treturn OCLASS_AMPROC;\n\t\t}\n\n\t\tcase RewriteRelationId:\n\t\t{\n\t\t\treturn OCLASS_REWRITE;\n\t\t}\n\n\t\tcase TriggerRelationId:\n\t\t{\n\t\t\treturn OCLASS_TRIGGER;\n\t\t}\n\n\t\tcase NamespaceRelationId:\n\t\t{\n\t\t\treturn OCLASS_SCHEMA;\n\t\t}\n\n\t\tcase StatisticExtRelationId:\n\t\t{\n\t\t\treturn OCLASS_STATISTIC_EXT;\n\t\t}\n\n\t\tcase TSParserRelationId:\n\t\t{\n\t\t\treturn OCLASS_TSPARSER;\n\t\t}\n\n\t\tcase TSDictionaryRelationId:\n\t\t{\n\t\t\treturn OCLASS_TSDICT;\n\t\t}\n\n\t\tcase TSTemplateRelationId:\n\t\t{\n\t\t\treturn OCLASS_TSTEMPLATE;\n\t\t}\n\n\t\tcase TSConfigRelationId:\n\t\t{\n\t\t\treturn OCLASS_TSCONFIG;\n\t\t}\n\n\t\tcase AuthIdRelationId:\n\t\t{\n\t\t\treturn OCLASS_ROLE;\n\t\t}\n\n\t\tcase AuthMemRelationId:\n\t\t{\n\t\t\treturn OCLASS_ROLE_MEMBERSHIP;\n\t\t}\n\n\t\tcase DatabaseRelationId:\n\t\t{\n\t\t\treturn OCLASS_DATABASE;\n\t\t}\n\n\t\tcase TableSpaceRelationId:\n\t\t{\n\t\t\treturn OCLASS_TBLSPACE;\n\t\t}\n\n\t\tcase ForeignDataWrapperRelationId:\n\t\t{\n\t\t\treturn OCLASS_FDW;\n\t\t}\n\n\t\tcase ForeignServerRelationId:\n\t\t{\n\t\t\treturn OCLASS_FOREIGN_SERVER;\n\t\t}\n\n\t\tcase UserMappingRelationId:\n\t\t{\n\t\t\treturn OCLASS_USER_MAPPING;\n\t\t}\n\n\t\tcase DefaultAclRelationId:\n\t\t{\n\t\t\treturn OCLASS_DEFACL;\n\t\t}\n\n\t\tcase ExtensionRelationId:\n\t\t{\n\t\t\treturn OCLASS_EXTENSION;\n\t\t}\n\n\t\tcase EventTriggerRelationId:\n\t\t{\n\t\t\treturn OCLASS_EVENT_TRIGGER;\n\t\t}\n\n\t\tcase ParameterAclRelationId:\n\t\t{\n\t\t\treturn OCLASS_PARAMETER_ACL;\n\t\t}\n\n\t\tcase PolicyRelationId:\n\t\t{\n\t\t\treturn OCLASS_POLICY;\n\t\t}\n\n\t\tcase PublicationNamespaceRelationId:\n\t\t{\n\t\t\treturn OCLASS_PUBLICATION_NAMESPACE;\n\t\t}\n\n\t\tcase PublicationRelationId:\n\t\t{\n\t\t\treturn OCLASS_PUBLICATION;\n\t\t}\n\n\t\tcase PublicationRelRelationId:\n\t\t{\n\t\t\treturn OCLASS_PUBLICATION_REL;\n\t\t}\n\n\t\tcase SubscriptionRelationId:\n\t\t{\n\t\t\treturn OCLASS_SUBSCRIPTION;\n\t\t}\n\n\t\tcase TransformRelationId:\n\t\t{\n\t\t\treturn OCLASS_TRANSFORM;\n\t\t}\n\t}\n\n\t/* shouldn't get here */\n\telog(ERROR, \"unrecognized object class: %u\", object->classId);\n\treturn OCLASS_CLASS;        /* keep compiler quiet */\n}\n\n\n#include \"commands/tablecmds.h\"\n\nstatic inline void\nRangeVarCallbackOwnsTable(const RangeVar *relation,\n\t\t\t\t\t\t  Oid relId, Oid oldRelId, void *arg)\n{\n\treturn RangeVarCallbackMaintainsTable(relation, relId, oldRelId, arg);\n}\n\n\n#include \"catalog/pg_attribute.h\"\n#include \"utils/syscache.h\"\n\nstatic inline int\ngetAttstattarget_compat(HeapTuple attTuple)\n{\n\tbool isnull;\n\tDatum dat = SysCacheGetAttr(ATTNUM, attTuple,\n\t\t\t\t\t\t\t\tAnum_pg_attribute_attstattarget, &isnull);\n\treturn (isnull ? -1 : DatumGetInt16(dat));\n}\n\n\n#include \"catalog/pg_statistic_ext.h\"\n\nstatic inline int\ngetStxstattarget_compat(HeapTuple tup)\n{\n\tbool isnull;\n\tDatum dat = SysCacheGetAttr(STATEXTOID, tup,\n\t\t\t\t\t\t\t\tAnum_pg_statistic_ext_stxstattarget, &isnull);\n\treturn (isnull ? -1 : DatumGetInt16(dat));\n}\n\n\n#define getAlterStatsStxstattarget_compat(a) ((Node *) makeInteger(a))\n#define getIntStxstattarget_compat(a) (intVal(a))\n\n#define WaitEventSetTracker_compat CurrentResourceOwner\n\n#define identitySequenceRelation_compat(a) (a)\n\n#define matched_compat(a) (a->matchKind == MERGE_WHEN_MATCHED)\n\n#define getProcNo_compat(a) (a->vxid.procNumber)\n#define getLxid_compat(a) (a->vxid.lxid)\n\n#else\n\n#define Anum_pg_collation_colllocale Anum_pg_collation_colliculocale\n#define Anum_pg_database_datlocale Anum_pg_database_daticulocale\n\n#include \"access/htup_details.h\"\nstatic inline int\ngetAttstattarget_compat(HeapTuple attTuple)\n{\n\treturn ((Form_pg_attribute) GETSTRUCT(attTuple))->attstattarget;\n}\n\n\n#include \"catalog/pg_statistic_ext.h\"\nstatic inline int\ngetStxstattarget_compat(HeapTuple tup)\n{\n\treturn ((Form_pg_statistic_ext) GETSTRUCT(tup))->stxstattarget;\n}\n\n\n#define getAlterStatsStxstattarget_compat(a) (a)\n#define getIntStxstattarget_compat(a) (a)\n\n#define WaitEventSetTracker_compat CurrentMemoryContext\n\n#define identitySequenceRelation_compat(a) (RelationGetRelid(a))\n\n#define matched_compat(a) (a->matched)\n\n#define create_foreignscan_path_compat(a, b, c, d, e, f, g, h, i, j, \\\n\t\t\t\t\t\t\t\t\t   k) create_foreignscan_path(a, b, c, d, e, f, g, h, \\\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  i, k)\n\n#define getProcNo_compat(a) (a->pgprocno)\n#define getLxid_compat(a) (a->lxid)\n\n#define COLLPROVIDER_BUILTIN 'b'\n\n#endif\n\n#define SetListCellPtr(a, b) ((a)->ptr_value = (b))\n#define RangeTableEntryFromNSItem(a) ((a)->p_rte)\n#define fcGetArgValue(fc, n) ((fc)->args[n].value)\n#define fcGetArgNull(fc, n) ((fc)->args[n].isnull)\n#define fcSetArgExt(fc, n, val, is_null) \\\n\t\t(((fc)->args[n].isnull = (is_null)), ((fc)->args[n].value = (val)))\n#define fcSetArg(fc, n, value) fcSetArgExt(fc, n, value, false)\n#define fcSetArgNull(fc, n) fcSetArgExt(fc, n, (Datum) 0, true)\n\n#define CREATE_SEQUENCE_COMMAND \\\n\t\t\"CREATE %sSEQUENCE IF NOT EXISTS %s AS %s INCREMENT BY \" INT64_FORMAT \\\n\t\t\" MINVALUE \" INT64_FORMAT \" MAXVALUE \" INT64_FORMAT \\\n\t\t\" START WITH \" INT64_FORMAT \" CACHE \" INT64_FORMAT \" %sCYCLE\"\n\n#endif   /* PG_VERSION_COMPAT_H */\n"
  },
  {
    "path": "src/include/pg_version_constants.h",
    "content": "/*-------------------------------------------------------------------------\n *\n * pg_version_constants.h\n *\t   pg version related constants.\n *\n * Copyright (c) Citus Data, Inc.\n *\n *-------------------------------------------------------------------------\n */\n\n#ifndef PG_VERSION_CONSTANTS\n#define PG_VERSION_CONSTANTS\n\n#define PG_VERSION_16 160000\n#define PG_VERSION_17 170000\n#define PG_VERSION_18 180000\n#define PG_VERSION_19 190000\n\n#endif   /* PG_VERSION_CONSTANTS */\n"
  },
  {
    "path": "src/test/cdc/Makefile",
    "content": "#-------------------------------------------------------------------------\n#\n# Makefile for src/test/cdc\n#\n# Test that CDC publication works correctly.\n#\n#-------------------------------------------------------------------------\n\nsubdir = src/test/cdc\ntop_builddir = ../../..\ninclude $(top_builddir)/Makefile.global\n\npg_version = $(shell $(PG_CONFIG) --version 2>/dev/null)\npg_whole_version = $(shell echo \"$(pg_version)\"| sed -e 's/^PostgreSQL \\([0-9]*\\)\\(\\.[0-9]*\\)\\{0,1\\}\\(.*\\)/\\1\\2/')\npg_major_version = $(shell echo \"$(pg_whole_version)\"| sed -e 's/^\\([0-9]\\{2\\}\\)\\(.*\\)/\\1/')\nexport pg_major_version\n\ntest_path = t/*.pl\n\n\n# copied from pgxs/Makefile.global to use postgres' abs build dir for pg_regress\nifeq ($(enable_tap_tests),yes)\n\ndefine citus_prove_installcheck\nrm -rf '$(CURDIR)'/tmp_check\n$(MKDIR_P) '$(CURDIR)'/tmp_check\ncd $(srcdir) && \\\nTESTDIR='$(CURDIR)' \\\nPATH=\"$(bindir):$$PATH\" \\\nPGPORT='6$(DEF_PGPORT)' \\\ntop_builddir='$(CURDIR)/$(top_builddir)' \\\nPG_REGRESS='$(pgxsdir)/src/test/regress/pg_regress' \\\nTEMP_CONFIG='$(CURDIR)'/postgresql.conf \\\n$(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) $(if $(PROVE_TESTS),$(PROVE_TESTS),$(test_path))\nendef\n\nelse\ncitus_prove_installcheck = @echo \"TAP tests not enabled when postgres was compiled\"\nendif\n\ninstallcheck:\n\t$(citus_prove_installcheck)\n\nclean distclean maintainer-clean:\n\trm -rf tmp_check\n"
  },
  {
    "path": "src/test/cdc/postgresql.conf",
    "content": "shared_preload_libraries='citus'\n"
  },
  {
    "path": "src/test/cdc/t/001_cdc_create_distributed_table_test.pl",
    "content": "# Basic CDC test for create_distributed_table\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(2,\"localhost\",57636);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\n# Create the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_coordinator->safe_psql('postgres','ALTER TABLE sensors REPLICA IDENTITY FULL;');\n\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'sensors');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\ncreate_cdc_slots_for_workers(\\@workers);\n\n# Distribut the sensors table to worker nodes.\n$node_coordinator->safe_psql('postgres',\"SELECT create_distributed_table('sensors', 'measureid');\");\n\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Insert some data to the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\n INSERT INTO sensors\n\tSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\n\tFROM generate_series(0,10)i;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC basic test - distributed table insert data');\n\n\n# Update some data in the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\nUPDATE sensors\n\tSET\n\teventdatetime=NOW(),\n\tmeasure_data = jsonb_set(measure_data, '{val}', measureid::text::jsonb , TRUE),\n\tmeasure_status = CASE\n\t\tWHEN measureid % 2 = 0\n\t\t\tTHEN 'y'\n\t\t\tELSE 'n'\n\t\tEND,\n\tmeasure_comment= 'Comment:' || measureid::text;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC basic test - distributed table update data');\n\n# Delete some data from the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\nDELETE FROM sensors\n    WHERE (measureid % 2) = 0;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC basic test - distributed table delete data');\n\n$node_coordinator->safe_psql('postgres',\"TRUNCATE sensors;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC basic test - distributed table delete data');\n\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/002_cdc_create_distributed_table_concurrently.pl",
    "content": "# CDC test for create_distributed_table_concurrently\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(2,\"localhost\",57636);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\n# Creeate the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'sensors');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\ncreate_cdc_slots_for_workers(\\@workers);\n\n# Distribut the sensors table to worker nodes.\n$node_coordinator->safe_psql('postgres',\"SELECT create_distributed_table_concurrently('sensors', 'measureid');\");\n\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Insert some data to the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\n INSERT INTO sensors\n\tSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\n\tFROM generate_series(0,10)i;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC test - create_distributed_table_concurrently insert data');\n\n\n# Update some data in the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\nUPDATE sensors\n\tSET\n\teventdatetime=NOW(),\n\tmeasure_data = jsonb_set(measure_data, '{val}', measureid::text::jsonb , TRUE),\n\tmeasure_status = CASE\n\t\tWHEN measureid % 2 = 0\n\t\t\tTHEN 'y'\n\t\t\tELSE 'n'\n\t\tEND,\n\tmeasure_comment= 'Comment:' || measureid::text;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC test - create_distributed_table_concurrently update data');\n\n# Delete some data from the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\nDELETE FROM sensors\n    WHERE (measureid % 2) = 0;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC test - create_distributed_table_concurrently delete data');\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/003_cdc_parallel_insert.pl",
    "content": "# CDC test for inserts during create distributed table concurrently\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\nuse threads;\n\n\n# Initialize co-ordinator node\nour $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $add_local_meta_data_stmt = qq(SELECT citus_add_local_table_to_metadata('sensors'););\nmy $result = 0;\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(2,\"localhost\",57636);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639, \"\");\n\n# Creeate the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_coordinator->safe_psql('postgres',$add_local_meta_data_stmt);\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\n\ncreate_cdc_publication_and_replication_slots_for_citus_cluster($node_coordinator,\\@workers,'sensors');\nconnect_cdc_client_to_citus_cluster_publications($node_coordinator,\\@workers,$node_cdc_client);\n\n#insert data into the sensors table in the coordinator node before distributing the table.\n$node_coordinator->safe_psql('postgres',\"\n\tINSERT INTO sensors\nSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\nFROM generate_series(0,10)i;\");\n\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator,\\@workers);\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC create_distributed_table - insert data');\n\nsub create_distributed_table_thread() {\n\t$node_coordinator->safe_psql('postgres',\"SELECT create_distributed_table_concurrently('sensors', 'measureid');\");\n}\n\nsub insert_data_into_distributed_table_thread() {\n\t# Insert some data to the sensors table in the coordinator node.\n\t$node_coordinator->safe_psql('postgres',\"\n\t INSERT INTO sensors\n\t\tSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\n\t\tFROM generate_series(-10,-1)i;\");\n}\n\n# Create the distributed table concurrently in a separate thread.\nmy $thr_create = threads->create(\\&create_distributed_table_thread);\nmy $thr_insert = threads->create(\\&insert_data_into_distributed_table_thread);\n$thr_create->join();\n$thr_insert->join();\n\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator,\\@workers);\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC create_distributed_table - insert data');\n\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/004_cdc_move_shard.pl",
    "content": "# Schema change CDC test for Citus\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(2,\"localhost\",57636);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\nprint(\"coordinator port: \" . $node_coordinator->port() . \"\\n\");\nprint(\"worker0 port:\" . $workers[0]->port() . \"\\n\");\nprint(\"worker1 port:\" . $workers[1]->port() . \"\\n\");\nprint(\"cdc_client port:\" .$node_cdc_client->port() . \"\\n\");\n\n# Creeate the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'sensors');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\ncreate_cdc_slots_for_workers(\\@workers);\n\n#insert data into the sensors table in the coordinator node before distributing the table.\n$node_coordinator->safe_psql('postgres',\"\n\tINSERT INTO sensors\nSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\nFROM generate_series(0,100)i;\");\n\n$node_coordinator->safe_psql('postgres',\"SET citus.shard_count = 2; SELECT create_distributed_table_concurrently('sensors', 'measureid');\");\n\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC create_distributed_table - schema change before move');\n\n\n\nmy $shard_to_move = $node_coordinator->safe_psql('postgres',\n        \"SELECT shardid FROM citus_shards ORDER BY shardid LIMIT 1;\");\nmy $host1 = $node_coordinator->safe_psql('postgres',\n        \"SELECT nodename FROM citus_shards ORDER BY shardid LIMIT 1;\");\nmy $port1 = $node_coordinator->safe_psql('postgres',\n        \"SELECT nodeport FROM citus_shards ORDER BY shardid LIMIT 1;\");\n\nmy $shard_last = $node_coordinator->safe_psql('postgres',\n        \"SELECT shardid FROM citus_shards ORDER BY shardid DESC LIMIT 1;\");\nmy $host2 = $node_coordinator->safe_psql('postgres',\n        \"SELECT nodename FROM citus_shards ORDER BY shardid DESC LIMIT 1;\");\nmy $port2 = $node_coordinator->safe_psql('postgres',\n        \"SELECT nodeport FROM citus_shards ORDER BY shardid DESC LIMIT 1;\");\n\nmy $move_params = \"select citus_move_shard_placement($shard_to_move,'$host1',$port1,'$host2',$port2,'force_logical');\";\nprint(\"move_params: $move_params\\n\");\n$node_coordinator->safe_psql('postgres',$move_params);\n\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator,\\@workers);\n\n\n#wait_for_cdc_client_to_catch_up_with_workers(\\@workers);\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC create_distributed_table - schema change and move shard');\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/005_cdc_reference_table_test.pl",
    "content": "# Basic CDC test for create_distributed_table\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\nmy $ref_select_stmt = qq(SELECT * FROM reference_table ORDER BY measureid;);\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(2,\"localhost\",57636);\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\n$node_coordinator->safe_psql('postgres',\"CREATE TABLE reference_table(measureid integer PRIMARY KEY);\");\n$node_cdc_client->safe_psql('postgres',\"CREATE TABLE reference_table(measureid integer PRIMARY KEY);\");\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'reference_table');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\n# Create the reference table in the coordinator and cdc client nodes.\n$node_coordinator->safe_psql('postgres',\"SELECT create_reference_table('reference_table');\");\n\ncreate_cdc_slots_for_workers(\\@workers);\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\n\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$ref_select_stmt);\nis($result, 1, 'CDC reference taable test 1');\n\n\n# Insert data to the reference table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"INSERT INTO reference_table SELECT i FROM generate_series(0,100)i;\");\n\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$ref_select_stmt);\nis($result, 1, 'CDC reference taable test 2');\n\n\n$node_coordinator->safe_psql('postgres',\"INSERT INTO reference_table SELECT i FROM generate_series(101,200)i;\");\n\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$ref_select_stmt);\nis($result, 1, 'CDC reference taable test 3');\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/006_cdc_schema_change_and_move.pl",
    "content": "# Schema change CDC test for Citus\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $select_stmt_after_drop = qq(SELECT measureid, eventdatetime, measure_data, measure_status, measure_comment FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(2,\"localhost\",57636);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\nprint(\"coordinator port: \" . $node_coordinator->port() . \"\\n\");\nprint(\"worker0 port:\" . $workers[0]->port() . \"\\n\");\nprint(\"worker1 port:\" . $workers[1]->port() . \"\\n\");\nprint(\"cdc_client port:\" .$node_cdc_client->port() . \"\\n\");\n\n# Creeate the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_coordinator->safe_psql('postgres','ALTER TABLE sensors REPLICA IDENTITY FULL;');\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'sensors');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\n#insert data into the sensors table in the coordinator node before distributing the table.\n$node_coordinator->safe_psql('postgres',\"\n\tINSERT INTO sensors\nSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\nFROM generate_series(0,100)i;\");\n\n$node_coordinator->safe_psql('postgres',\"SET citus.shard_count = 2; SELECT create_distributed_table_concurrently('sensors', 'measureid');\");\n\n#connect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\ncreate_cdc_slots_for_workers(\\@workers);\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC create_distributed_table - schema change before move');\n\n\n$node_coordinator->safe_psql('postgres',\"ALTER TABLE sensors DROP COLUMN meaure_quantity;\");\n\n\nmy $shard_to_move = $node_coordinator->safe_psql('postgres',\n        \"SELECT shardid FROM citus_shards ORDER BY shardid LIMIT 1;\");\nmy $host1 = $node_coordinator->safe_psql('postgres',\n        \"SELECT nodename FROM citus_shards ORDER BY shardid LIMIT 1;\");\nmy $port1 = $node_coordinator->safe_psql('postgres',\n        \"SELECT nodeport FROM citus_shards ORDER BY shardid LIMIT 1;\");\n\nmy $shard_last = $node_coordinator->safe_psql('postgres',\n        \"SELECT shardid FROM citus_shards ORDER BY shardid DESC LIMIT 1;\");\nmy $host2 = $node_coordinator->safe_psql('postgres',\n        \"SELECT nodename FROM citus_shards ORDER BY shardid DESC LIMIT 1;\");\nmy $port2 = $node_coordinator->safe_psql('postgres',\n        \"SELECT nodeport FROM citus_shards ORDER BY shardid DESC LIMIT 1;\");\n\nmy $move_params = \"select citus_move_shard_placement($shard_to_move,'$host1',$port1,'$host2',$port2,'force_logical');\";\nprint(\"move_params: $move_params\\n\");\n$node_coordinator->safe_psql('postgres',$move_params);\n\n\n\n$node_coordinator->safe_psql('postgres',\"\n  \tINSERT INTO sensors\n  SELECT i, '2020-01-05', '{}', 'A', 'I <3 Citus'\n  FROM generate_series(-10,-1)i;\");\n\n\n$node_cdc_client->safe_psql('postgres',\"ALTER TABLE sensors DROP COLUMN meaure_quantity;\");\n\nwait_for_cdc_client_to_catch_up_with_workers(\\@workers);\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC create_distributed_table - schema change and move shard');\n\n# Update some data in the sensors table to check the schema change handling logic in CDC decoder.\n$node_coordinator->safe_psql('postgres',\"\nUPDATE sensors\n\tSET\n\tmeasure_status = CASE\n\t\tWHEN measureid % 2 = 0\n\t\t\tTHEN 'y'\n\t\t\tELSE 'n'\n\t\tEND;\");\n\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator,\\@workers);\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC create_distributed_table - update data after schema change');\n\n# Update some data in the sensors table to check the schema change handling logic in CDC decoder.\n$node_coordinator->safe_psql('postgres',\"\nDELETE FROM sensors\n\tWHERE\n\tmeasure_status = 'n';\");\n\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator,\\@workers);\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC create_distributed_table - delete data after schem change');\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/007_cdc_undistributed_table_test.pl",
    "content": "# Basic CDC test for create_distributed_table\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(2,\"localhost\",57636);\n\nmy $command = \"UPDATE pg_dist_node SET shouldhaveshards = true;\";\n$node_coordinator->safe_psql('postgres',$command);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\n# Create the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'sensors');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\ncreate_cdc_slots_for_workers(\\@workers);\n\n# Distribut the sensors table to worker nodes.\n$node_coordinator->safe_psql('postgres',\"SELECT create_distributed_table('sensors', 'measureid');\");\n\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Insert some data to the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\n INSERT INTO sensors\n\tSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\n\tFROM generate_series(0,10)i;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC basic test - distributed table insert data');\n\n\n# Update some data in the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\nUPDATE sensors\n\tSET\n\teventdatetime=NOW(),\n\tmeasure_data = jsonb_set(measure_data, '{val}', measureid::text::jsonb , TRUE),\n\tmeasure_status = CASE\n\t\tWHEN measureid % 2 = 0\n\t\t\tTHEN 'y'\n\t\t\tELSE 'n'\n\t\tEND,\n\tmeasure_comment= 'Comment:' || measureid::text;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC basic test - distributed table update data');\n\n# Delete some data from the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\nDELETE FROM sensors\n    WHERE (measureid % 2) = 0;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC basic test - distributed table delete data');\n\n$node_coordinator->safe_psql('postgres',\"\nSELECT undistribute_table('sensors',cascade_via_foreign_keys=>true);\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC basic test - distributed table delete data');\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/008_cdc_shard_split_test.pl",
    "content": "# Basic CDC test for create_distributed_table\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\nmy $citus_config = \"\ncitus.shard_count = 2\ncitus.shard_replication_factor = 1\n\";\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(1,\"localhost\",57636, $citus_config);\n\nmy $command = \"UPDATE pg_dist_node SET shouldhaveshards = true;\";\n$node_coordinator->safe_psql('postgres',$command);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\n# Create the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'sensors');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\ncreate_cdc_slots_for_workers(\\@workers);\n\n# Distribut the sensors table to worker nodes.\n\n$node_coordinator->safe_psql('postgres',\"SELECT create_distributed_table('sensors', 'measureid');\");\n\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC split test - distributed table create data');\n\n# Insert some data to the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\n INSERT INTO sensors\n\tSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\n\tFROM generate_series(-100,100)i;\");\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC split test - distributed table insert data');\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n$node_coordinator->safe_psql('postgres',\"\n SELECT citus_split_shard_by_split_points(102008,ARRAY['-50'],ARRAY[1,2], 'block_writes');\");\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC split test - distributed table split data');\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/009_cdc_shard_split_test_non_blocking.pl",
    "content": "# Basic CDC test for create_distributed_table\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\nmy $citus_config = \"\ncitus.shard_count = 2\ncitus.shard_replication_factor = 1\n\";\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(1,\"localhost\",57636, $citus_config);\n\nmy $command = \"UPDATE pg_dist_node SET shouldhaveshards = true;\";\n$node_coordinator->safe_psql('postgres',$command);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\n# Create the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'sensors');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\ncreate_cdc_slots_for_workers(\\@workers);\n\n# Distribut the sensors table to worker nodes.\n\n$node_coordinator->safe_psql('postgres',\"SELECT create_distributed_table('sensors', 'measureid');\");\n\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC split test - distributed table create data');\n\n# Insert some data to the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\n INSERT INTO sensors\n\tSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\n\tFROM generate_series(-100,100)i;\");\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC split test - distributed table insert data');\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n$node_coordinator->safe_psql('postgres',\"\n SELECT citus_split_shard_by_split_points(102008,ARRAY['-50'],ARRAY[1,2], 'force_logical');\");\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC split test - distributed table split data');\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/010_cdc_shard_split_parallel_insert.pl",
    "content": "# Basic CDC test for create_distributed_table\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\nuse threads;\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\nmy $citus_config = \"\ncitus.shard_count = 2\ncitus.shard_replication_factor = 1\n\";\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(1,\"localhost\",57636, $citus_config);\n\nmy $command = \"UPDATE pg_dist_node SET shouldhaveshards = true;\";\n$node_coordinator->safe_psql('postgres',$command);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\n# Create the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'sensors');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\ncreate_cdc_slots_for_workers(\\@workers);\n\n# Distribut the sensors table to worker nodes.\n$node_coordinator->safe_psql('postgres',\"SELECT create_distributed_table('sensors', 'measureid');\");\n\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC split test - distributed table create data');\n\n# Insert some data to the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\n INSERT INTO sensors\n\tSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\n\tFROM generate_series(-100,100)i;\");\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC split test - distributed table insert data');\n\n\nsub insert_data_into_distributed_table_thread() {\n        # Insert some data to the sensors table in the coordinator node.\n        $node_coordinator->safe_psql('postgres',\"\n        INSERT INTO sensors\n                SELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\n                FROM generate_series(101,200)i;\");\n}\n\nsub split_distributed_table_thread() {\n        $node_coordinator->safe_psql('postgres',\"\n        SELECT citus_split_shard_by_split_points(102008,ARRAY['-50'],ARRAY[1,2], 'force_logical');\");\n}\n\n# Create the distributed table concurrently in a separate thread.\nmy $thr_create = threads->create(\\&split_distributed_table_thread);\n\n# Insert some data to the sensors table in the coordinator node while the table is being distributed.\nmy $thr_insert = threads->create(\\&insert_data_into_distributed_table_thread);\n\n# Wait for the threads to finish.\n$thr_create->join();\n$thr_insert->join();\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC split test - distributed table split data');\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/011_cdc_alter_distributed_table.pl",
    "content": "# Basic CDC test for create_distributed_table\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $add_local_meta_data_stmt = qq(SELECT citus_add_local_table_to_metadata('sensors'););\nmy $result = 0;\nmy $citus_config = \"\ncitus.shard_count = 2\ncitus.shard_replication_factor = 1\n\";\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(1,\"localhost\",57636, $citus_config);\n\nmy $command = \"UPDATE pg_dist_node SET shouldhaveshards = true;\";\n$node_coordinator->safe_psql('postgres',$command);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\n# Create the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_coordinator->safe_psql('postgres',$add_local_meta_data_stmt);\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\ncreate_cdc_publication_and_replication_slots_for_citus_cluster($node_coordinator,\\@workers,'sensors');\nconnect_cdc_client_to_citus_cluster_publications($node_coordinator,\\@workers,$node_cdc_client);\n\n# Distribut the sensors table to worker nodes.\n$node_coordinator->safe_psql('postgres',\"SELECT create_distributed_table_concurrently('sensors', 'measureid');\");\n\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator,\\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC split test - distributed table create data');\n\n# Insert some data to the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\n SELECT alter_distributed_table('sensors', shard_count:=6, cascade_to_colocated:=true);\");\n\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Compare the data in the coordinator and cdc client nodes.\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC split test - alter distributed table ');\n\n#$node_cdc_client->safe_psql(\"postgres\",\"alter subscription cdc_subscription refresh publication;\");\n$node_cdc_client->safe_psql(\"postgres\",\"alter subscription cdc_subscription_1 refresh publication;\");\n\n\n#Drop the CDC client subscription and recreate them , since the\n#alter_distributed_table has changed the Oid of the distributed table.\n#So the  CDC client has to create Oid to table mappings again for\n#CDC to work again.\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ncreate_cdc_publication_and_replication_slots_for_citus_cluster($node_coordinator,\\@workers,'sensors');\nconnect_cdc_client_to_citus_cluster_publications($node_coordinator,\\@workers,$node_cdc_client);\n\n# Insert some data to the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\n INSERT INTO sensors\n\tSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\n\tFROM generate_series(0,10)i;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC basic test - distributed table insert data');\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\n\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/012_cdc_restart_test.pl",
    "content": "# Basic CDC test for create_distributed_table\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(2,\"localhost\",57636);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\n# Create the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'sensors');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\ncreate_cdc_slots_for_workers(\\@workers);\n\n# Distribut the sensors table to worker nodes.\n$node_coordinator->safe_psql('postgres',\"SELECT create_distributed_table('sensors', 'measureid');\");\n\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC restart test - distributed table creation');\n\n\n# Insert some data to the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\n INSERT INTO sensors\n\tSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\n\tFROM generate_series(0,10)i;\");\n\n# Wait for the data changes to be replicated to the cdc client node.\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC restart test - distributed table insert data');\n\n\nprint(\"stopping worker 0\");\n$workers[0]->stop();\nprint(\"starting worker 0 againg..\");\n$workers[0]->start();\n\n\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n# Insert some data to the sensors table in the coordinator node.\n$node_coordinator->safe_psql('postgres',\"\n INSERT INTO sensors\n\tSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\n\tFROM generate_series(11,20)i;\");\n\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC restart test - distributed table after restart');\n\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/013_cdc_drop_last_column_for_one_shard.pl",
    "content": "# Schema change CDC test for Citus\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $select_stmt_after_drop = qq(SELECT measureid, eventdatetime, measure_data, meaure_quantity, measure_status FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(2,\"localhost\",57636);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\nprint(\"coordinator port: \" . $node_coordinator->port() . \"\\n\");\nprint(\"worker0 port:\" . $workers[0]->port() . \"\\n\");\nprint(\"worker1 port:\" . $workers[1]->port() . \"\\n\");\nprint(\"cdc_client port:\" .$node_cdc_client->port() . \"\\n\");\n\n# Creeate the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_coordinator->safe_psql('postgres','ALTER TABLE sensors REPLICA IDENTITY FULL;');\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'sensors');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\n#insert data into the sensors table in the coordinator node before distributing the table.\n$node_coordinator->safe_psql('postgres',\"\n\tINSERT INTO sensors\nSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\nFROM generate_series(0,100)i;\");\n\n$node_coordinator->safe_psql('postgres',\"SET citus.shard_count = 2; SELECT create_distributed_table_concurrently('sensors', 'measureid');\");\n\n#connect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\ncreate_cdc_slots_for_workers(\\@workers);\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC create_distributed_table - schema change before move');\n\n\nmy $shard_id = $workers[1]->safe_psql('postgres',\n        \"SELECT shardid FROM citus_shards ORDER BY shardid LIMIT 1;\");\n\nmy $shard_to_drop_column = \"sensors_\" . $shard_id;\n\n\n$workers[1]->safe_psql('postgres',\"ALTER TABLE $shard_to_drop_column DROP COLUMN measure_comment;\");\n\n\n$workers[1]->safe_psql('postgres',\"\n  \tINSERT INTO sensors\n  SELECT i, '2020-01-05', '{}', 11011.10, 'A'\n  FROM generate_series(-10,-1)i;\");\n\n\nwait_for_cdc_client_to_catch_up_with_workers(\\@workers);\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt_after_drop);\nis($result, 1, 'CDC create_distributed_table - schema change and move shard');\n\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/014_cdc_with_table_like_shard_name.pl",
    "content": "# Schema change CDC test for Citus\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $select_stmt_after_drop = qq(SELECT measureid, eventdatetime, measure_data, meaure_quantity, measure_status FROM sensors ORDER BY measureid, eventdatetime, measure_data;);\nmy $result = 0;\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(2,\"localhost\",57636);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\nprint(\"coordinator port: \" . $node_coordinator->port() . \"\\n\");\nprint(\"worker0 port:\" . $workers[0]->port() . \"\\n\");\nprint(\"worker1 port:\" . $workers[1]->port() . \"\\n\");\nprint(\"cdc_client port:\" .$node_cdc_client->port() . \"\\n\");\n\n# Creeate the sensors table and ndexes.\nmy $initial_schema = \"\n        CREATE TABLE sensors(\n        measureid               integer,\n        eventdatetime           timestamptz,\n        measure_data            jsonb,\n        meaure_quantity         decimal(15, 2),\n        measure_status          char(1),\n        measure_comment         varchar(44),\n        PRIMARY KEY (measureid, eventdatetime, measure_data));\n\n        CREATE INDEX index_on_sensors ON sensors(lower(measureid::text));\n        ALTER INDEX index_on_sensors ALTER COLUMN 1 SET STATISTICS 1000;\n        CREATE INDEX hash_index_on_sensors ON sensors USING HASH((measure_data->'IsFailed'));\n        CREATE INDEX index_with_include_on_sensors ON sensors ((measure_data->'IsFailed')) INCLUDE (measure_data, eventdatetime, measure_status);\n        CREATE STATISTICS stats_on_sensors (dependencies) ON measureid, eventdatetime FROM sensors;\";\n\nmy $shard_like_table_schema = \"\n        CREATE TABLE data_100008(\n                id  integer,\n                data integer,\n                PRIMARY KEY (data));\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_coordinator->safe_psql('postgres',$shard_like_table_schema);\n$node_coordinator->safe_psql('postgres','ALTER TABLE sensors REPLICA IDENTITY FULL;');\n\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n$node_cdc_client->safe_psql('postgres',$shard_like_table_schema);\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'sensors,data_100008');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\n#insert data into the sensors table in the coordinator node before distributing the table.\n$node_coordinator->safe_psql('postgres',\"\n\tINSERT INTO sensors\nSELECT i, '2020-01-05', '{}', 11011.10, 'A', 'I <3 Citus'\nFROM generate_series(0,100)i;\");\n\n$node_coordinator->safe_psql('postgres',\"SET citus.shard_count = 2; SELECT create_distributed_table_concurrently('sensors', 'measureid');\");\n\n#connect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\ncreate_cdc_slots_for_workers(\\@workers);\nconnect_cdc_client_to_workers_publication(\\@workers, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_citus_cluster($node_coordinator, \\@workers);\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC create_distributed_table - basic test');\n\n$workers[1]->safe_psql('postgres',$shard_like_table_schema);\n$workers[1]->safe_psql('postgres','\\d');\n\n$workers[1]->safe_psql('postgres',\"\n  \tINSERT INTO data_100008\n  SELECT i, i*10\n  FROM generate_series(-10,10)i;\");\n\n\nwait_for_cdc_client_to_catch_up_with_workers(\\@workers);\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt_after_drop);\nis($result, 1, 'CDC create_distributed_table - normal table with name like shard');\n\ndrop_cdc_client_subscriptions($node_cdc_client,\\@workers);\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/015_cdc_without_citus.pl",
    "content": "# Schema change CDC test for Citus\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM data_100008 ORDER BY id;);\nmy $result = 0;\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(2,\"localhost\",57636);\n\nour $node_cdc_client = create_node('cdc_client', 0, \"localhost\", 57639);\n\nprint(\"coordinator port: \" . $node_coordinator->port() . \"\\n\");\nprint(\"worker0 port:\" . $workers[0]->port() . \"\\n\");\nprint(\"worker1 port:\" . $workers[1]->port() . \"\\n\");\nprint(\"cdc_client port:\" .$node_cdc_client->port() . \"\\n\");\n\nmy $initial_schema = \"\n        CREATE TABLE data_100008(\n                id  integer,\n                data integer,\n                PRIMARY KEY (data));\";\n\n$node_coordinator->safe_psql('postgres','DROP extension citus');\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_coordinator->safe_psql('postgres','ALTER TABLE data_100008 REPLICA IDENTITY FULL;');\n\n$node_cdc_client->safe_psql('postgres',$initial_schema);\n\n\ncreate_cdc_publication_and_slots_for_coordinator($node_coordinator,'data_100008');\nconnect_cdc_client_to_coordinator_publication($node_coordinator, $node_cdc_client);\nwait_for_cdc_client_to_catch_up_with_coordinator($node_coordinator);\n\n#insert data into the sensors table in the coordinator node before distributing the table.\n$node_coordinator->safe_psql('postgres',\"\n  \tINSERT INTO data_100008\n  SELECT i, i*10\n  FROM generate_series(-10,10)i;\");\n\n\n$result = compare_tables_in_different_nodes($node_coordinator,$node_cdc_client,'postgres',$select_stmt);\nis($result, 1, 'CDC create_distributed_table - basic test');\n\n$node_cdc_client->safe_psql('postgres',\"drop subscription cdc_subscription\");\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/016_cdc_wal2json.pl",
    "content": "# Schema change CDC test for Citus\nuse strict;\nuse warnings;\n\nuse Test::More;\n\nuse lib './t';\nuse cdctestlib;\n\nuse threads;\n\n# Initialize co-ordinator node\nmy $select_stmt = qq(SELECT * FROM data_100008 ORDER BY id;);\nmy $result = 0;\n\n### Create the citus cluster with coordinator and two worker nodes\nour ($node_coordinator, @workers) = create_citus_cluster(1,\"localhost\",57636);\n\nprint(\"coordinator port: \" . $node_coordinator->port() . \"\\n\");\nprint(\"worker0 port:\" . $workers[0]->port() . \"\\n\");\n\nmy $initial_schema = \"\n        CREATE TABLE data_100008(\n                id  integer,\n                data text,\n                PRIMARY KEY (data));\";\n\n$node_coordinator->safe_psql('postgres',$initial_schema);\n$node_coordinator->safe_psql('postgres','ALTER TABLE data_100008 REPLICA IDENTITY FULL;');\n$node_coordinator->safe_psql('postgres',\"SELECT pg_catalog.pg_create_logical_replication_slot('cdc_replication_slot','wal2json');\");\n\n#insert data into the data_100008 table in the coordinator node before distributing the table.\n$node_coordinator->safe_psql('postgres',\"\n  \tINSERT INTO data_100008\n  SELECT i, 'my test data ' || i\n  FROM generate_series(-1,1)i;\");\n\nmy $output = $node_coordinator->safe_psql('postgres',\"SELECT * FROM pg_logical_slot_get_changes('cdc_replication_slot', NULL, NULL);\");\nprint($output);\n\nmy $change_string_expected = '[0,\"my test data 0\"]';\nif ($output =~ /$change_string_expected/) {\n  $result = 1;\n} else {\n  $result = 0;\n}\n\nis($result, 1, 'CDC create_distributed_table - wal2json test');\n$node_coordinator->safe_psql('postgres',\"SELECT pg_drop_replication_slot('cdc_replication_slot');\");\n\ndone_testing();\n"
  },
  {
    "path": "src/test/cdc/t/cdctestlib.pm",
    "content": "use strict;\nuse warnings;\n\nmy $pg_major_version =  int($ENV{'pg_major_version'});\nprint(\"working with PG major version : $pg_major_version\\n\");\nif ($pg_major_version >= 15) {\n    eval \"use PostgreSQL::Test::Cluster\";\n    eval \"use PostgreSQL::Test::Utils\";\n} else {\n    eval \"use PostgresNode\";\n}\n\n\n#use PostgresNode;\nuse DBI;\n\nour $NODE_TYPE_COORDINATOR = 1;\nour $NODE_TYPE_WORKER = 2;\nour $NODE_TYPE_CDC_CLIENT = 3;\n\nsub compare_tables_in_different_nodes\n{\n    my $result = 1;\n    my ($node1, $node2, $dbname, $stmt) = @_;\n\n    # Connect to the first database node\n    my $dbh1 = DBI->connect(\"dbi:Pg:\" . $node1->connstr($dbname));\n\n    # Connect to the second database node\n    my $dbh2 = DBI->connect(\"dbi:Pg:\" . $node2->connstr($dbname));\n\n    # Define the SQL query for the first database node\n    my $sth1 = $dbh1->prepare($stmt);\n    $sth1->execute();\n\n    # Define the SQL query for the second database node\n    my $sth2 = $dbh2->prepare($stmt);\n    $sth2->execute();\n\n    # Get the field names for the table\n    my @field_names = @{$sth2->{NAME}};\n\n    #$sth1->dump_results();\n    #$sth2->dump_results();\n\n    our @row1, our @row2;\n\n    # Use a cursor to iterate over the first database node's data\n    while (1) {\n\n        @row1 = $sth1->fetchrow_array();\n        @row2 = $sth2->fetchrow_array();\n        #print(\"row1: @row1\\n\");\n        #print(\"row2: @row2\\n\");\n\n        # Use a cursor to iterate over the second database node's data\n        if (@row1 and @row2) {\n            #print(\"row1: @row1\\n\");\n            #print(\"row2: @row2\\n\");\n            my $field_count_row1 = scalar @row1;\n            my $field_count_row2 = scalar @row2;\n            if ($field_count_row1 != $field_count_row2) {\n                print \"Field count mismatch: $field_count_row1 != $field_count_row2 \\n\";\n                print \"First row: @row1\\n\";\n                #print \"Second row: @row2\\n\";\n                for (my $i = 0; $i < scalar @row2; $i++) {\n                    print(\"Field $i, field name: $field_names[$i], value: $row2[$i] \\n\");\n                }\n                $result = 0;\n                last;\n            }\n            # Compare the data in each field in each row of the two nodes\n            for (my $i = 0; $i < scalar @row1; $i++) {\n                if ($row1[$i] ne $row2[$i]) {\n                    print \"Data mismatch in field '$field_names[$i]'\\n\";\n                    print \"$row1[$i] != $row2[$i]\\n\";\n                    print \"First row: @row1\\n\";\n                    print \"Second row: @row2\\n\";\n                    $result = 0;\n                    last;\n                }\n            }\n        } elsif (@row1 and !@row2) {\n            print \"First node has more rows than the second node\\n\";\n            $result = 0;\n            last;\n        } elsif (!@row1 and @row2) {\n            print \"Second node has more rows than the first node\\n\";\n            $result = 0;\n            last;\n        } else {\n            last;\n        }\n    }\n\n    $sth1->finish();\n    $sth2->finish();\n    $dbh1->disconnect();\n    $dbh2->disconnect();\n    return $result;\n}\n\nsub create_node {\n    my ($name,$node_type,$host, $port, $config) = @_;\n    if (!defined($config)) {\n        $config = \"\"\n    }\n\n    our $node;\n\n    if ($pg_major_version >= 15) {\n        $PostgreSQL::Test::Cluster::use_unix_sockets = 0;\n        $PostgreSQL::Test::Cluster::use_tcp = 1;\n        $PostgreSQL::Test::Cluster::test_pghost = 'localhost';\n        my %params = ( \"port\" => $port, \"host\" => \"localhost\");\n        $node = PostgreSQL::Test::Cluster->new($name, %params);\n    } else {\n        $PostgresNode::use_tcp = 1;\n        $PostgresNode::test_pghost = '127.0.0.1';\n        my %params = ( \"port\" => $port, \"host\" => \"localhost\");\n        $node = get_new_node($name, %params);\n    }\n    print(\"node's port:\" . $node->port . \"\\n\");\n\n    $port += 1;\n\n    my $citus_config_options = \"\nmax_connections = 100\nmax_wal_senders = 100\nmax_replication_slots = 100\ncitus.enable_change_data_capture = on\nlog_statement = 'all'\ncitus.override_table_visibility = off\";\n\n    if ($config ne \"\") {\n        $citus_config_options = $citus_config_options . $config\n    }\n\n    my $client_config_options = \"\nmax_connections = 100\nmax_wal_senders = 100\nmax_replication_slots = 100\n\";\n    $node->init(allows_streaming => 'logical');\n    if ($node_type == $NODE_TYPE_COORDINATOR || $node_type == $NODE_TYPE_WORKER) {\n        $node->append_conf(\"postgresql.conf\",$citus_config_options);\n    } else {\n        $node->append_conf(\"postgresql.conf\",$citus_config_options);\n    }\n\n    $node->start();\n\n    if ($node_type == $NODE_TYPE_COORDINATOR || $node_type == $NODE_TYPE_WORKER) {\n        $node->safe_psql('postgres', \"CREATE EXTENSION citus;\");\n        my $value = $node->safe_psql('postgres', \"SHOW citus.enable_change_data_capture;\");\n        print(\"citus.enable_change_data_capture value is $value\\n\")\n    }\n\n    return $node;\n}\n\n# Create a Citus cluster with the given number of workers\nsub create_citus_cluster {\n    my ($no_workers,$host,$port,$citus_config) = @_;\n    my @workers = ();\n    my $node_coordinator;\n    print(\"citus_config :\", $citus_config);\n    if ($citus_config ne \"\") {\n        $node_coordinator = create_node('coordinator', $NODE_TYPE_COORDINATOR,$host, $port, $citus_config);\n    } else {\n        $node_coordinator = create_node('coordinator', $NODE_TYPE_COORDINATOR,$host, $port);\n    }\n    my $coord_host = $node_coordinator->host();\n    my $coord_port = $node_coordinator->port();\n    $node_coordinator->safe_psql('postgres',\"SELECT pg_catalog.citus_set_coordinator_host('$coord_host', $coord_port);\");\n    for (my $i = 0; $i < $no_workers; $i++) {\n        $port = $port + 1;\n        my $node_worker;\n        if ($citus_config ne \"\") {\n            $node_worker = create_node(\"worker$i\", $NODE_TYPE_WORKER,\"localhost\", $port, $citus_config);\n        } else {\n            $node_worker = create_node(\"worker$i\", $NODE_TYPE_WORKER,\"localhost\", $port);\n        }\n        my $node_worker_host = $node_worker->host();\n        my $node_worker_port = $node_worker->port();\n        $node_coordinator->safe_psql('postgres',\"SELECT pg_catalog.citus_add_node('$node_worker_host', $node_worker_port);\");\n        push @workers, $node_worker;\n    }\n    return $node_coordinator, @workers;\n}\n\nsub create_cdc_publication_and_replication_slots_for_citus_cluster {\n    my $node_coordinator = $_[0];\n    my $workersref = $_[1];\n    my $table_names = $_[2];\n\n    create_cdc_publication_and_slots_for_coordinator($node_coordinator, $table_names);\n    create_cdc_slots_for_workers($workersref);\n}\n\nsub create_cdc_publication_and_slots_for_coordinator {\n    my $node_coordinator = $_[0];\n    my $table_names = $_[1];\n    print(\"node node_coordinator connstr: \\n\" . $node_coordinator->connstr());\n    my $pub = $node_coordinator->safe_psql('postgres',\"SELECT * FROM pg_publication WHERE pubname = 'cdc_publication';\");\n    if ($pub ne \"\") {\n        $node_coordinator->safe_psql('postgres',\"DROP PUBLICATION IF EXISTS cdc_publication;\");\n    }\n    $node_coordinator->safe_psql('postgres',\"CREATE PUBLICATION cdc_publication FOR TABLE $table_names;\");\n    $node_coordinator->safe_psql('postgres',\"SELECT pg_catalog.pg_create_logical_replication_slot('cdc_replication_slot','pgoutput',false)\");\n}\n\nsub create_cdc_slots_for_workers {\n    my $workersref = $_[0];\n    for (@$workersref) {\n        my $slot = $_->safe_psql('postgres',\"select * from pg_replication_slots where  slot_name = 'cdc_replication_slot';\");\n        if ($slot ne \"\") {\n            $_->safe_psql('postgres',\"SELECT pg_catalog.pg_drop_replication_slot('cdc_replication_slot');\");\n        }\n        $_->safe_psql('postgres',\"SELECT pg_catalog.pg_create_logical_replication_slot('cdc_replication_slot','pgoutput',false)\");\n    }\n}\n\n\nsub connect_cdc_client_to_citus_cluster_publications {\n    my $node_coordinator = $_[0];\n    my $workersref = $_[1];\n    my $node_cdc_client = $_[2];\n    my $num_args = scalar(@_);\n\n\n    if ($num_args > 3) {\n         my $copy_arg = $_[3];\n        connect_cdc_client_to_coordinator_publication($node_coordinator,$node_cdc_client, $copy_arg);\n    } else {\n        connect_cdc_client_to_coordinator_publication($node_coordinator,$node_cdc_client);\n    }\n    connect_cdc_client_to_workers_publication($workersref, $node_cdc_client);\n}\n\nsub connect_cdc_client_to_coordinator_publication {\n    my $node_coordinator = $_[0];\n    my $node_cdc_client = $_[1];\n    my $num_args = scalar(@_);\n    my $copy_data = \"\";\n    if ($num_args > 2) {\n        my $copy_arg = $_[2];\n        $copy_data = 'copy_data='. $copy_arg;\n    } else {\n        $copy_data = 'copy_data=false';\n    }\n\n    my $conn_str = $node_coordinator->connstr() . \" dbname=postgres\";\n    my $subscription = 'cdc_subscription';\n    print \"creating subscription $subscription for coordinator: $conn_str\\n\";\n    $node_cdc_client->safe_psql('postgres',\"\n        CREATE SUBSCRIPTION $subscription\n            CONNECTION '$conn_str'\n            PUBLICATION cdc_publication\n            WITH (\n                create_slot=false,\n                enabled=true,\n                slot_name=cdc_replication_slot,\"\n                . $copy_data. \");\"\n    );\n}\n\nsub connect_cdc_client_to_workers_publication {\n    my $workersref = $_[0];\n    my $node_cdc_client = $_[1];\n    my $i = 1;\n    for (@$workersref) {\n        my $conn_str = $_->connstr() . \" dbname=postgres\";\n        my $subscription = 'cdc_subscription_' . $i;\n        print \"creating subscription $subscription for node$i: $conn_str\\n\";\n        my $subscription_stmt = \"CREATE SUBSCRIPTION $subscription\n            CONNECTION '$conn_str'\n            PUBLICATION cdc_publication\n            WITH (\n                create_slot=false,\n                enabled=true,\n                slot_name=cdc_replication_slot,\n                copy_data=false);\n            \";\n\n        $node_cdc_client->safe_psql('postgres',$subscription_stmt);\n        $i++;\n    }\n}\n\nsub wait_for_cdc_client_to_catch_up_with_citus_cluster {\n        my $node_coordinator = $_[0];\n        my ($workersref) = $_[1];\n\n        my $subscription = 'cdc_subscription';\n        print \"coordinator: waiting for cdc client subscription $subscription to catch up\\n\";\n        $node_coordinator->wait_for_catchup($subscription);\n        wait_for_cdc_client_to_catch_up_with_workers($workersref);\n}\n\nsub wait_for_cdc_client_to_catch_up_with_coordinator {\n        my $node_coordinator = $_[0];\n        my $subscription = 'cdc_subscription';\n        print \"coordinator: waiting for cdc client subscription $subscription to catch up\\n\";\n        $node_coordinator->wait_for_catchup($subscription);\n}\n\nsub wait_for_cdc_client_to_catch_up_with_workers {\n        my ($workersref) = $_[0];\n        my $i = 1;\n        for (@$workersref) {\n            my $subscription = 'cdc_subscription_' . $i;\n            print \"node$i: waiting for cdc client subscription $subscription to catch up\\n\";\n            $_->wait_for_catchup($subscription);\n            $i++;\n        }\n}\n\nsub drop_cdc_client_subscriptions {\n    my $node = $_[0];\n    my ($workersref) = $_[1];\n\n    $node->safe_psql('postgres',\"drop subscription cdc_subscription\");\n    my $i = 1;\n    for (@$workersref) {\n        my $subscription = 'cdc_subscription_' . $i;\n        $node->safe_psql('postgres',\"drop subscription \" . $subscription);\n        $i++;\n    }\n}\n\n"
  },
  {
    "path": "src/test/hammerdb/README.md",
    "content": "\n# How to trigger hammerdb benchmark jobs\n\nYou can trigger two types of hammerdb benchmark jobs:\n-ch_benchmark (analytical and transactional queries)\n-tpcc_benchmark (only transactional queries)\n\nYour branch will be run against `master` branch.\n\nIn order to trigger the jobs prepend `ch_benchmark/` or `tpcc_benchmark/` to your branch and push it.\n\nFor example if you were running on a feature/improvement branch with name `improve/adaptive_executor`. In order to trigger a tpcc benchmark, you can do the following:\n\n```bash\ngit checkout improve/adaptive_executor\ngit checkout -b tpcc_benchmark/improve/adaptive_executor\ngit push origin tpcc_benchmark/improve/adaptive_executor # the tpcc benchmark job will be triggered.\n```\n\nYou will see the results in a branch in [https://github.com/citusdata/release-test-results](https://github.com/citusdata/release-test-results).\n\nThe branch name will be something like: `citus_github_push/citusbot_tpcc_benchmark_rg/<date>/<date>`.\n\nOn success, which is the vast majority of the cases, the resource groups on Azure which are automatically created for this purpose are deleted automatically. On failure, you need to manually drop the resource groups named: `citusbot_ch_benchmark_rg` and `citusbot_tpcc_benchmark_rg`. To check whether the job failed, go to Azure portal and search for these resource group names. If the resource group doesn't have any virtual machines or the virtual machines don't have any CPU/Memory load at all, it is highly possible that the job failed. Delete the resource groups and re-try with another branch.\n\nThese jobs use the hammerdb tool under the hood, please see [https://github.com/citusdata/test-automation#running-automated-hammerdb-benchmark](https://github.com/citusdata/test-automation#running-automated-hammerdb-benchmark) for more details.\n"
  },
  {
    "path": "src/test/hammerdb/run_hammerdb.sh",
    "content": "#!/bin/bash\n\n# fail if trying to reference a variable that is not set.\nset -u\n# exit immediately if a command fails\nset -e\n# echo commands\nset -x\n\nrg=$1\n\nexport RESOURCE_GROUP_NAME=\"${rg}\"\nexport AZURE_REGION=westus2\n# the branch name is stored in CIRCLE_BRANCH env variable in CI jobs.\nexport BRANCH=\"${CIRCLE_BRANCH}\"\n\n# add github to known hosts\necho \"github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==\" >> ~/.ssh/known_hosts\n\nset +x\ngit config --global credential.helper store\ngit clone https://${GIT_USERNAME}:${GIT_TOKEN}@github.com/citusdata/test-automation\nset -x\n\ncd test-automation\n\ntest_automation_dir=$(pwd)\n# add the ssh keys\neval `ssh-agent -s`\nssh-add\n\nssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub\n\nnow=$(date +\"%m_%d_%Y_%s\")\nnew_branch_name=citus_github_push/\"${rg}\"/\"${now}\"\ngit checkout -b \"${new_branch_name}\"\n\ncd ./fabfile/hammerdb_confs\nbranch_config=current_branch.ini\n# create a config for this branch\ncp master.ini \"${branch_config}\"\n\n# put the branch name to the config file.\nsed -i \"s@master@${BRANCH}@g\" \"${branch_config}\"\n\ncd \"${test_automation_dir}\"\ncd ./hammerdb\n\ngit config --global user.email \"citus-bot@microsoft.com\"\ngit config --global user.name \"citus bot\"\ngit add -A\ngit commit -m \"test hammerdb: ${rg} vs master\"\ngit push origin \"${new_branch_name}\"\n\ncd \"${test_automation_dir}\"\ncd ./hammerdb\n\nif [ \"$rg\" = \"citusbot_ch_benchmark_rg\" ]; then\n    export IS_CH=true\n    export IS_TPCC=true\nfi\n\nif [ \"$rg\" = \"citusbot_tpcc_benchmark_rg\" ]; then\n    export IS_CH=false\n    export IS_TPCC=true\nfi\n\n# create cluster and run the hammerd benchmark\n./create-run.sh\n\n\n"
  },
  {
    "path": "src/test/regress/.gitignore",
    "content": "# Local binaries\n/pg_regress\n\n# Generated subdirectories\n/tmp_check/\n/tmp_upgrade/\n/tmp_citus_upgrade/\n/tmp_citus_tarballs/\n/tmp_citus_test/\n/build/\n/results/\n/log/\n/bin/test/results/\n\n# Regression test output\n/regression.diffs\n/regression.out\n/test_times.log\n\n# Regression test timing\n/test_times.log\n\n# Failure test side effets\n/proxy.output\n\n# python\n*.pyc\n\n# core dumps\ncore\n\n# pg_vanilla output folders\npg_vanilla_outputs\n"
  },
  {
    "path": "src/test/regress/Makefile",
    "content": "# Makefile for tests of the Citus extension\n\ncitus_subdir = src/test/regress\ncitus_top_builddir = ../../..\n\ninclude $(citus_top_builddir)/Makefile.global\n\n# ensure MAJORVERSION is defined (missing in older versions)\nifndef MAJORVERSION\nMAJORVERSION := $(basename $(VERSION))\nendif\n\nifndef CITUSVERSION\nCITUSVERSION :=\nendif\n\nifndef CITUSLIBDIR\nCITUSLIBDIR :=\nendif\n\nifndef N1MODE\nN1MODE :=\nendif\n# Add ./bin to $PATH so we use our custom diff instead of the default diff tool.\n# We do this to be able to mask shard Ids, placement Ids, node ports, etc.\nMAKEFILE_DIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))\nexport PATH := $(MAKEFILE_DIR)/bin:$(PATH)\nexport PG_REGRESS_DIFF_OPTS = -dU10 -w\n# Use lower isolation test timeout, the 5 minute default is waaay too long for\n# us so we use 60 seconds instead. We should detect blockages very quickly and\n# most queries that we run are also very fast. So fast even that 60 seconds is\n# usually too long. However, any commands involving logical replication can be\n# quite slow, especially shard splits and especially on CI. So we still keep\n# this value at the pretty high 60 seconds because even those slow commands are\n# definitly stuck when they take longer than that.\nexport PGISOLATIONTIMEOUT = 60\n\n##\n## Citus regression support\n##\nMULTI_INSTALLDIR=$(CURDIR)/tmp_check/install\npg_regress_multi_check = $(PERL) $(citus_abs_srcdir)/pg_regress_multi.pl --pgxsdir=\"$(pgxsdir)\" --bindir=\"$(bindir)\" --libdir=\"$(libdir)\" --majorversion=\"$(MAJORVERSION)\" --postgres-builddir=\"$(postgres_abs_builddir)\" --postgres-srcdir=\"$(postgres_abs_srcdir)\" --citus_abs_srcdir=\"$(citus_abs_srcdir)\" $(if $(CITUSVERSION),--citus-version=$(CITUSVERSION)) $(if $(CITUSLIBDIR),--citus-libdir=\"$(CITUSLIBDIR)\") $(if $(N1MODE),--n-1-mode=$(N1MODE))\nMULTI_REGRESS_OPTS = --inputdir=$(citus_abs_srcdir) $(pg_regress_locale_flags) --launcher=\"$(citus_abs_srcdir)/log_test_times\"\n\npg_upgrade_check = $(citus_abs_srcdir)/citus_tests/upgrade/pg_upgrade_test.py\ncitus_upgrade_check =CITUS_OLD_VERSION=$(citus-old-version) $(citus_abs_srcdir)/citus_tests/upgrade/citus_upgrade_test.py\narbitrary_config_check = $(citus_abs_srcdir)/citus_tests/arbitrary_configs/citus_arbitrary_configs.py\nquery_generator_check = $(citus_abs_srcdir)/citus_tests/query_generator/bin/run_query_compare_test.py\n\ntemplate_isolation_files = $(shell find $(citus_abs_srcdir)/spec/ -name '*.spec')\ngenerated_isolation_files = $(patsubst $(citus_abs_srcdir)/spec/%,$(citus_abs_srcdir)/build/specs/%,$(template_isolation_files))\n\nvanilla_diffs_file = $(citus_abs_srcdir)/pg_vanilla_outputs/$(MAJORVERSION)/regression.diffs\n\n# have make check actually run all tests, but keep check-full as an\n# intermediate, for muscle memory backward compatibility.\ncheck: check-full check-enterprise-full\n# check-full triggers all tests that ought to be run routinely\ncheck-full: check-multi check-multi-mx check-multi-1 check-multi-1-create-citus check-operations check-add-backup-node check-follower-cluster check-isolation check-failure check-split check-vanilla check-columnar check-columnar-isolation check-pg-upgrade check-arbitrary-configs check-citus-upgrade check-citus-upgrade-mixed check-citus-upgrade-local check-citus-upgrade-mixed-local check-pytest check-query-generator check-tap\n# check-enterprise-full triggers all enterprise specific tests\ncheck-enterprise-full: check-enterprise check-enterprise-isolation check-enterprise-failure check-enterprise-isolation-logicalrep-1 check-enterprise-isolation-logicalrep-2 check-enterprise-isolation-logicalrep-3\n\n\nISOLATION_DEPDIR=.deps/isolation\nISOLATION_BUILDDIR=build/specs\n\n# this can be used to print a value of variable\n# ex: make print-generated_isolation_files\nprint-%  : ; @echo $* = $($*)\n\n.PHONY: create-symbolic-link\n\ncreate-symbolic-link:\n\tmkdir -p $(citus_abs_srcdir)/build\n\tln -fsn $(citus_abs_srcdir)/expected $(citus_abs_srcdir)/build/\n\n# How this target works:\n# cpp is used before running isolation tests to preprocess spec files.\n# This way we can include any file we want to. Currently this is used to include mx common part.\n# spec files are put to /build/specs for clear separation between generated files and template files\n# a symbolic link is created for /expected in build/expected/.\n# when running isolation tests, as the inputdir, build is passed so\n# it runs the spec files from build/specs and checks the expected output from build/expected.\n# /specs is renamed as /spec because postgres first look at the specs file under current directory,\n# so this is renamed to avoid that since we are running the isolation tests from build/specs now.\n$(generated_isolation_files): $(citus_abs_srcdir)/build/specs/%: $(citus_abs_srcdir)/spec/% Makefile\n\t@mkdir -p $(citus_abs_srcdir)/$(ISOLATION_DEPDIR) $(citus_abs_srcdir)/$(ISOLATION_BUILDDIR)\n\t@# -MF is used to store dependency files(.Po) in another directory for separation\n\t@# -MT is used to change the target of the rule emitted by dependency generation.\n\t@# -P is used to inhibit generation of linemarkers in the output from the preprocessor.\n\t@# -undef is used to not predefine any system-specific or GCC-specific macros.\n\t@# `man cpp` for further information\n\t@# sed is used to strip the final // comments, because OSX cpp is weird\n\tcd $(citus_abs_srcdir) && cpp -undef -w -P -MMD -MP -MF$(ISOLATION_DEPDIR)/$(*F).Po -MT$@ $< | sed '/^\\/\\//d' | sed '/^$$/d' > $@\n\n\nIsolation_Po_files := $(wildcard $(ISOLATION_DEPDIR)/*.Po)\nifneq (,$(Isolation_Po_files))\ninclude $(Isolation_Po_files)\nendif\n\nisolation_test_files=$(generated_isolation_files) create-symbolic-link\n\nifndef CITUS_VALGRIND_LOG_FILE\nCITUS_VALGRIND_LOG_FILE := citus_valgrind_test_log.txt\nendif\n\n# check-base only sets up a testing environment so you can specify all your tests using EXTRA_TESTS\ncheck-base: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/base_schedule $(EXTRA_TESTS)\n\n# check-minimal only sets up the cluster\ncheck-minimal: all\n\t$(pg_regress_multi_check) --load-extension=citus --load-extension=hll \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/minimal_schedule $(EXTRA_TESTS)\n\ncheck-base-vg: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t--valgrind --pg_ctl-timeout=360 --connection-timeout=500000 --valgrind-path=valgrind --valgrind-log-file=$(CITUS_VALGRIND_LOG_FILE) \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/base_schedule $(EXTRA_TESTS)\n\ncheck-base-mx: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/mx_base_schedule $(EXTRA_TESTS)\n\ncheck-minimal-mx: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/mx_minimal_schedule $(EXTRA_TESTS)\n\ncheck-custom-schedule: all\n\t$(pg_regress_multi_check) --load-extension=citus --worker-count=$(WORKERCOUNT) \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/$(SCHEDULE) $(EXTRA_TESTS)\n\ncheck-failure-custom-schedule: all\n\t$(pg_regress_multi_check) --load-extension=citus --mitmproxy --worker-count=$(WORKERCOUNT) \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/$(SCHEDULE) $(EXTRA_TESTS)\n\ncheck-isolation-custom-schedule: all  $(isolation_test_files)\n\t$(pg_regress_multi_check) --load-extension=citus --isolationtester --worker-count=$(WORKERCOUNT) \\\n\t-- $(MULTI_REGRESS_OPTS) --inputdir=$(citus_abs_srcdir)/build --schedule=$(citus_abs_srcdir)/$(SCHEDULE) $(EXTRA_TESTS)\n\ncheck-custom-schedule-vg: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t--valgrind --pg_ctl-timeout=360 --connection-timeout=500000 --worker-count=$(WORKERCOUNT) \\\n\t--valgrind-path=valgrind --valgrind-log-file=$(CITUS_VALGRIND_LOG_FILE) \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/$(SCHEDULE) $(EXTRA_TESTS)\n\ncheck-failure-custom-schedule-vg: all\n\t$(pg_regress_multi_check) --load-extension=citus --mitmproxy \\\n\t--valgrind --pg_ctl-timeout=360 --connection-timeout=500000 --worker-count=$(WORKERCOUNT) \\\n\t--valgrind-path=valgrind --valgrind-log-file=$(CITUS_VALGRIND_LOG_FILE) \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/$(SCHEDULE) $(EXTRA_TESTS)\n\ncheck-isolation-custom-schedule-vg: all  $(isolation_test_files)\n\t$(pg_regress_multi_check) --load-extension=citus --isolationtester --worker-count=$(WORKERCOUNT) \\\n\t--valgrind --pg_ctl-timeout=360 --connection-timeout=500000 --valgrind-path=valgrind --valgrind-log-file=$(CITUS_VALGRIND_LOG_FILE) \\\n\t-- $(MULTI_REGRESS_OPTS) --inputdir=$(citus_abs_srcdir)/build --schedule=$(citus_abs_srcdir)/$(SCHEDULE) $(EXTRA_TESTS)\n\ncheck-empty: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) $(EXTRA_TESTS)\n\ncheck-multi: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/multi_schedule $(EXTRA_TESTS)\n\ncheck-enterprise: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/enterprise_schedule $(EXTRA_TESTS)\n\ncheck-multi-1: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/multi_1_schedule $(EXTRA_TESTS)\n\ncheck-multi-1-create-citus: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/multi_1_create_citus_schedule $(EXTRA_TESTS)\n\ncheck-multi-hyperscale: all\n\t$(pg_regress_multi_check) --conninfo=\"$(conninfo)\" --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/multi_schedule_hyperscale $(EXTRA_TESTS)\n\ncheck-multi-hyperscale-superuser: all\n\t$(pg_regress_multi_check) --conninfo=\"$(conninfo)\" --worker-1-public-hostname=\"$(worker_1_public_hostname)\" --worker-2-public-hostname=\"$(worker_2_public_hostname)\" --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/multi_schedule_hyperscale_superuser $(EXTRA_TESTS)\n\ncheck-multi-vg: all\n\t$(pg_regress_multi_check) --load-extension=citus --valgrind \\\n\t--pg_ctl-timeout=360 --connection-timeout=500000 --valgrind-path=valgrind --valgrind-log-file=$(CITUS_VALGRIND_LOG_FILE) \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/multi_schedule $(EXTRA_TESTS)\n\ncheck-multi-1-vg: all\n\t$(pg_regress_multi_check) --load-extension=citus --valgrind \\\n\t--pg_ctl-timeout=360 --connection-timeout=500000 --valgrind-path=valgrind --valgrind-log-file=$(CITUS_VALGRIND_LOG_FILE) \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/multi_1_schedule $(EXTRA_TESTS)\n\ncheck-isolation: all  $(isolation_test_files)\n\t$(pg_regress_multi_check) --load-extension=citus --isolationtester \\\n\t-- $(MULTI_REGRESS_OPTS) --inputdir=$(citus_abs_srcdir)/build --schedule=$(citus_abs_srcdir)/isolation_schedule $(EXTRA_TESTS)\n\ncheck-enterprise-isolation: all $(isolation_test_files)\n\t$(pg_regress_multi_check) --load-extension=citus --isolationtester \\\n\t-- $(MULTI_REGRESS_OPTS) --inputdir=$(citus_abs_srcdir)/build --schedule=$(citus_abs_srcdir)/enterprise_isolation_schedule $(EXTRA_TESTS)\n\n# we have separate targets for logical replication tests because they take very long to complete\n# hence this increases parallelism a lot without sacrifing any coverage.\ncheck-enterprise-isolation-logicalrep-1: all $(isolation_test_files)\n\t$(pg_regress_multi_check) --load-extension=citus --isolationtester \\\n\t-- $(MULTI_REGRESS_OPTS) --inputdir=$(citus_abs_srcdir)/build --schedule=$(citus_abs_srcdir)/enterprise_isolation_logicalrep_1_schedule $(EXTRA_TESTS)\n\ncheck-enterprise-isolation-logicalrep-2: all $(isolation_test_files)\n\t$(pg_regress_multi_check) --load-extension=citus --isolationtester \\\n\t-- $(MULTI_REGRESS_OPTS) --inputdir=$(citus_abs_srcdir)/build --schedule=$(citus_abs_srcdir)/enterprise_isolation_logicalrep_2_schedule $(EXTRA_TESTS)\n\ncheck-enterprise-isolation-logicalrep-3: all $(isolation_test_files)\n\t$(pg_regress_multi_check) --load-extension=citus --isolationtester \\\n\t-- $(MULTI_REGRESS_OPTS) --inputdir=$(citus_abs_srcdir)/build --schedule=$(citus_abs_srcdir)/enterprise_isolation_logicalrep_3_schedule $(EXTRA_TESTS)\n\ncheck-isolation-base: all  $(isolation_test_files)\n\t$(pg_regress_multi_check) --load-extension=citus --isolationtester \\\n\t-- $(MULTI_REGRESS_OPTS) --inputdir=$(citus_abs_srcdir)/build --schedule=$(citus_abs_srcdir)/base_isolation_schedule $(EXTRA_TESTS)\n\n# ci takes regression.diffs output from another location, so copy diffs file at the end.\ncheck-vanilla: all\n\t$(pg_regress_multi_check) --vanillatest || (cp $(vanilla_diffs_file) $(citus_abs_srcdir)/regression.diffs && false)\n\ncheck-multi-mx: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/multi_mx_schedule $(EXTRA_TESTS)\n\ncheck-tap:\n\t$(MAKE) -C $(citus_top_srcdir)/src/test/tap installcheck\n\ncheck-follower-cluster: all\n\t$(pg_regress_multi_check) --load-extension=citus --follower-cluster \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/multi_follower_schedule $(EXTRA_TESTS)\n\ncheck-add-backup-node: all\n\t$(pg_regress_multi_check) --load-extension=citus --follower-cluster --backupnodetest --worker-count=6 \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/multi_add_backup_node_schedule $(EXTRA_TESTS)\n\ncheck-operations: all\n\t$(pg_regress_multi_check) --load-extension=citus  --worker-count=6 \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/operations_schedule $(EXTRA_TESTS)\n\ncheck-columnar-minimal:\n\t$(pg_regress_multi_check) --load-extension=citus_columnar \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/minimal_columnar_schedule $(EXTRA_TESTS)\n\ncheck-columnar: all\n\t$(pg_regress_multi_check) --load-extension=citus_columnar \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/columnar_schedule $(EXTRA_TESTS)\n\ncheck-columnar-vg: all\n\t$(pg_regress_multi_check) --load-extension=citus_columnar --valgrind \\\n\t--pg_ctl-timeout=360 --connection-timeout=500000 --valgrind-path=valgrind --valgrind-log-file=$(CITUS_VALGRIND_LOG_FILE) \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/columnar_schedule $(EXTRA_TESTS)\n\ncheck-columnar-isolation: all  $(isolation_test_files)\n\t$(pg_regress_multi_check) --load-extension=citus_columnar --isolationtester \\\n\t-- $(MULTI_REGRESS_OPTS) --inputdir=$(citus_abs_srcdir)/build --schedule=$(citus_abs_srcdir)/columnar_isolation_schedule $(EXTRA_TESTS)\n\ncheck-columnar-custom-schedule: all\n\t$(pg_regress_multi_check) --load-extension=citus_columnar \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/$(SCHEDULE) $(EXTRA_TESTS)\n\ncheck-columnar-custom-schedule-vg: all\n\t$(pg_regress_multi_check) --load-extension=citus_columnar \\\n\t--valgrind --pg_ctl-timeout=360 --connection-timeout=500000 \\\n\t--valgrind-path=valgrind --valgrind-log-file=$(CITUS_VALGRIND_LOG_FILE) \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/$(SCHEDULE) $(EXTRA_TESTS)\n\ncheck-columnar-isolation-custom-schedule: all  $(isolation_test_files)\n\t$(pg_regress_multi_check) --load-extension=citus_columnar --isolationtester \\\n\t-- $(MULTI_REGRESS_OPTS) --inputdir=$(citus_abs_srcdir)/build --schedule=$(citus_abs_srcdir)/$(SCHEDULE) $(EXTRA_TESTS)\n\ncheck-columnar-isolation-custom-schedule-vg: all  $(isolation_test_files)\n\t$(pg_regress_multi_check) --load-extension=citus_columnar --isolationtester \\\n\t--valgrind --pg_ctl-timeout=360 --connection-timeout=500000 --valgrind-path=valgrind --valgrind-log-file=$(CITUS_VALGRIND_LOG_FILE) \\\n\t-- $(MULTI_REGRESS_OPTS) --inputdir=$(citus_abs_srcdir)/build --schedule=$(citus_abs_srcdir)/$(SCHEDULE) $(EXTRA_TESTS)\n\ncheck-split: all\n\t$(pg_regress_multi_check) --load-extension=citus \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/split_schedule $(EXTRA_TESTS)\n\ncheck-failure: all\n\t$(pg_regress_multi_check) --load-extension=citus --mitmproxy \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/failure_schedule $(EXTRA_TESTS)\n\ncheck-failure-base: all\n\t$(pg_regress_multi_check) --load-extension=citus --mitmproxy \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/failure_base_schedule $(EXTRA_TESTS)\n\ncheck-enterprise-failure: all\n\t$(pg_regress_multi_check) --load-extension=citus --mitmproxy \\\n\t-- $(MULTI_REGRESS_OPTS) --schedule=$(citus_abs_srcdir)/enterprise_failure_schedule $(EXTRA_TESTS)\n\ncheck-pg-upgrade: all\n\t$(pg_upgrade_check) --old-bindir=$(old-bindir) --new-bindir=$(new-bindir) --pgxsdir=$(pgxsdir) $(if $(filter true,$(test-with-columnar)),--test-with-columnar)\n\ncheck-arbitrary-configs: all\n\t${arbitrary_config_check} --bindir=$(bindir) --pgxsdir=$(pgxsdir) --parallel=$(parallel) --configs=$(CONFIGS) --seed=$(seed)\n\ncheck-arbitrary-base: all\n\t${arbitrary_config_check} --bindir=$(bindir) --pgxsdir=$(pgxsdir) --parallel=$(parallel) --configs=$(CONFIGS) --seed=$(seed) --base\n\ncheck-pytest:\n\tpytest -n auto\n\ncheck-query-generator: all\n\t${query_generator_check} --bindir=$(bindir) --pgxsdir=$(pgxsdir) --seed=$(seed)\n\ncheck-citus-upgrade: all\n\t$(citus_upgrade_check) \\\n\t\t--bindir=$(bindir) \\\n\t\t--pgxsdir=$(pgxsdir) \\\n\t\t--citus-pre-tar=$(citus-pre-tar) \\\n\t\t--citus-post-tar=$(citus-post-tar)\n\ncheck-citus-upgrade-mixed: all\n\t$(citus_upgrade_check) \\\n\t\t--bindir=$(bindir) \\\n\t\t--pgxsdir=$(pgxsdir) \\\n\t\t--citus-pre-tar=$(citus-pre-tar) \\\n\t\t--citus-post-tar=$(citus-post-tar) \\\n\t\t--mixed\n\ncheck-citus-upgrade-local: all clean-upgrade-artifacts\n\t$(citus_upgrade_check) \\\n\t\t--bindir=$(bindir) \\\n\t\t--pgxsdir=$(pgxsdir) \\\n\t\t--citus-old-version=$(citus-old-version)\n\ncheck-citus-upgrade-mixed-local: all clean-upgrade-artifacts\n\t$(citus_upgrade_check) \\\n\t\t--bindir=$(bindir) \\\n\t\t--pgxsdir=$(pgxsdir) \\\n\t\t--citus-old-version=$(citus-old-version) \\\n\t\t--mixed\n\ncheck-citus-minor-upgrade: all\n\t$(citus_upgrade_check) \\\n\t\t--bindir=$(bindir) \\\n\t\t--pgxsdir=$(pgxsdir) \\\n\t\t--citus-pre-tar=$(citus-pre-tar) \\\n\t\t--citus-post-tar=$(citus-post-tar) \\\n\t\t--minor-upgrade\n\ncheck-citus-minor-upgrade-local: all clean-upgrade-artifacts\n\t$(citus_upgrade_check) \\\n\t\t--bindir=$(bindir) \\\n\t\t--pgxsdir=$(pgxsdir) \\\n\t\t--citus-old-version=$(citus-old-version) \\\n\t\t--minor-upgrade\n\nclean-upgrade-artifacts:\n\trm -rf $(citus_abs_srcdir)/tmp_citus_upgrade/ /tmp/citus_copy/\n\nclean distclean maintainer-clean:\n\trm -rf input/ output/\n\trm -rf tmp_check/\n\trm -rf tmp_citus_test/\n"
  },
  {
    "path": "src/test/regress/Pipfile",
    "content": "[[source]]\nname = \"pypi\"\nurl = \"https://pypi.python.org/simple\"\nverify_ssl = true\n\n[packages]\nmitmproxy = {git = \"https://github.com/citusdata/mitmproxy.git\", ref = \"main\"}\n\"aioquic\" = \">=1.2.0,<1.3.0\"\n\"mitmproxy-rs\" = \">=0.12.6,<0.13.0\"\nargon2-cffi = \">=23.1.0\"\nbcrypt = \">=4.1.2\"\nbrotli = \"<=1.2.0\"\nh11 = \"==0.16.0\"\nh2 = \"==4.3.0\"\ntornado = \">=6.5.1,<6.6.0\"\nzstandard = \">=0.25.0\"\nconstruct = \"*\"\ndocopt = \"==0.6.2\"\ncryptography = \"==46.0.5\"\npytest = \"*\"\npsycopg = \"*\"\nfilelock = \"*\"\npytest-asyncio = \"*\"\npytest-timeout = \"*\"\npytest-xdist = \"*\"\npytest-repeat = \"*\"\npyyaml = \"*\"\nwerkzeug = \"==3.1.5\"\n\"typing-extensions\" = \">=4.13.2,<5\"\npyperclip = \"==1.9.0\"\n\n[dev-packages]\nblack = \"==24.10.0\"\nisort = \"*\"\nflake8 = \"*\"\nflake8-bugbear = \"*\"\n\n[requires]\npython_version = \"3.12\"\n"
  },
  {
    "path": "src/test/regress/README.md",
    "content": "# How our testing works\n\nWe use the test tooling of postgres to run our tests. This tooling is very\nsimple but effective. The basics it runs a series of `.sql` scripts, gets\ntheir output and stores that in `results/$sqlfilename.out`. It then compares the\nactual output to the expected output with a simple `diff` command:\n\n```bash\ndiff results/$sqlfilename.out expected/$sqlfilename.out\n```\n\n## Schedules\n\nWhich sql scripts to run is defined in a schedule file, e.g. `multi_schedule`,\n`multi_mx_schedule`.\n\n## Makefile\n\nIn our `Makefile` we have rules to run the different types of test schedules.\nYou can run them from the root of the repository like so:\n\n```bash\n# e.g. the multi_schedule\nmake install -j9 && make -C src/test/regress/ check-multi\n```\n\nTake a look at the makefile for a list of all the testing targets.\n\n### Running a specific test\n\nOften you want to run a specific test and don't want to run everything. You can\nsimply use `run_test.py [test_name]` script like below in that case. It detects the test schedule\nand make target to run the given test.\n\n```bash\nsrc/test/regress/citus_tests/run_test.py multi_utility_warnings\n```\nYou can pass `--repeat` or `r` parameter to run the given test for multiple times.\n\n```bash\nsrc/test/regress/citus_tests/run_test.py multi_utility_warnings -r 1000\n```\n\nTo force the script to use base schedules rather than minimal ones, you can\npass `-b` or `--use-base-schedule`.\n\n```bash\nsrc/test/regress/citus_tests/run_test.py coordinator_shouldhaveshards -r 1000 --use-base-schedule\n```\n\nIf you would like to run a specific test on a certain target you can use one\nof the following commands to do so:\n\n```bash\n# If your tests needs almost no setup you can use check-minimal\nmake install -j9 && make -C src/test/regress/ check-minimal EXTRA_TESTS='multi_utility_warnings'\n# For columnar specific tests, use check-columnar-minimal instead of check-minimal\nmake install -j9 && make -C src/test/regress/ check-columnar-minimal\n# Often tests need some testing data, if you get missing table errors using\n# check-minimal you should try check-base\nmake install -j9 && make -C src/test/regress/ check-base EXTRA_TESTS='with_prepare'\n# Sometimes this is still not enough and some other test needs to be run before\n# the test you want to run. You can do so by adding it to EXTRA_TESTS too.\nmake install -j9 && make -C src/test/regress/ check-base EXTRA_TESTS='add_coordinator coordinator_shouldhaveshards'\n```\n\n\n## Normalization\n\nThe output of tests is sadly not completely predictable. Still we want to\ncompare the output of different runs and error when the important things are\ndifferent. We do this by not using the regular system `diff` to compare files.\nInstead we use `src/test/regress/bin/diff` which does the following things:\n\n1. Change the `$sqlfilename.out` file by running it through `sed` using the\n   `src/test/regress/bin/normalize.sed` file. This does stuff like replacing\n   numbers that keep changing across runs with an `XXX` string, e.g. portnumbers\n   or transaction numbers.\n2. Backup the original output to `$sqlfilename.out.unmodified` in case it's\n   needed for debugging\n3. Compare the changed `results` and `expected` files with the system `diff`\n   command.\n\n## Updating the expected test output\n\nSometimes you add a test to an existing file, or test output changes in a way\nthat's not bad (possibly even good if support for queries is added). In those\ncases you want to update the expected test output.\nThe way to do this is very simple, you run the test and copy the new .out file\nin the `results` directory to the `expected` directory, e.g.:\n\n```bash\nmake install -j9 && make -C src/test/regress/ check-minimal EXTRA_TESTS='multi_utility_warnings'\ncp src/test/regress/{results,expected}/multi_utility_warnings.out\n```\n\nOr if it's a columnar test, you can use:\n\n```bash\nmake install -j9 && make -C src/test/regress/ check-columnar-minimal EXTRA_TESTS='multi_utility_warnings'\ncp src/test/regress/{results,expected}/multi_utility_warnings.out\n```\n\n## Adding a new test file\n\nAdding a new test file is quite simple:\n\n1. Write the SQL file in the `sql` directory\n2. Add it to a schedule file, to make sure it's run in CI\n3. Run the test\n4. Check that the output is as expected\n5. Copy the `.out` file from `results` to `expected`\n\n## Isolation testing\n\nSee [`src/test/regress/spec/README.md`](https://github.com/citusdata/citus/blob/master/src/test/regress/spec/README.md)\n\n## Pytest testing\n\nSee [`src/test/regress/citus_tests/test/README.md`](https://github.com/citusdata/citus/blob/master/src/test/regress/citus_tests/test/README.md)\n\n## Upgrade testing\n\nSee [`src/test/regress/citus_tests/upgrade/README.md`](https://github.com/citusdata/citus/blob/master/src/test/regress/citus_tests/upgrade/README.md)\n\n## Arbitrary configs testing\n\nSee [`src/test/regress/citus_tests/arbitrary_configs/README.md`](https://github.com/citusdata/citus/blob/master/src/test/regress/citus_tests/arbitrary_configsupgrade/README.md)\n\n## Failure testing\n\nSee [`src/test/regress/mitmscripts/README.md`](https://github.com/citusdata/citus/blob/master/src/test/regress/mitmscripts/README.md)\n\n## Perl test setup script\n\nTo automatically setup a citus cluster in tests we use our\n`src/test/regress/pg_regress_multi.pl` script. This sets up a citus cluster and\nthen starts the standard postgres test tooling. You almost never have to change\nthis file.\n\n## Handling different test outputs\n\nSometimes the test output changes because we run tests in different configurations.\nThe most common example is an output that changes in different Postgres versions.\nWe highly encourage to find a way to avoid these test outputs.\nYou can try the following, if applicable to the changing output:\n- Change the test such that you still test what you want, but you avoid the different test outputs.\n- Reduce the test verbosity via: `\\set VERBOSITY terse`, `SET client_min_messages TO error`, etc\n- Drop the specific test lines altogether, if the test is not critical.\n- Use utility functions that modify the output to your preference,\nlike [coordinator_plan](https://github.com/citusdata/citus/blob/main/src/test/regress/sql/multi_test_helpers.sql#L23),\nwhich modifies EXPLAIN output\n- Add [a normalization rule](https://github.com/citusdata/citus/blob/main/ci/README.md#normalize_expectedsh)\n\nAlternative test output files are highly discouraged, so only add one when strictly necessary.\nIn order to maintain a clean test suite, make sure to explain why it has an alternative\noutput in the test header, and when we can drop the alternative output file in the future.\n\nFor example:\n\n```sql\n--\n-- MULTI_INSERT_SELECT\n--\n-- This test file has an alternative output because of the change in the\n-- display of SQL-standard function's arguments in INSERT/SELECT in PG15.\n-- The alternative output can be deleted when we drop support for PG14\n--\n```\nIncluding important keywords, like \"PG14\", \"PG15\", \"alternative output\" will\nhelp cleaning up in the future.\n\n## Randomly failing tests\n\nIn CI sometimes a test fails randomly, we call these tests \"flaky\". To fix these\nflaky tests see [`src/test/regress/flaky_tests.md`](https://github.com/citusdata/citus/blob/main/src/test/regress/flaky_tests.md)\n\n## Running regression tests in mixed-version scenarios\n\nThe test runner supports running regression tests in mixed-version (N/N-1) scenarios,\nwhere some or all nodes use an older Citus version. This is useful for verifying\nbackward compatibility.\n\n### Environment variables\n\nThree environment variables control mixed-version behavior:\n\n| Variable | Description |\n|---|---|\n| `CITUSVERSION` | The Citus extension version to create (e.g. `13.2-1`). When set, `CREATE EXTENSION citus VERSION '<version>'` is used instead of the default. |\n| `CITUSLIBDIR` | Path to a directory containing the older `citus.so` library (e.g. `~/citus-libs/17/v13.2.0`). When set, the specified library is loaded instead of the currently installed one. |\n| `N1MODE` | Controls which nodes use the older version. Valid values: `all` (all nodes), `workeronly` (only worker 1), `coordinatoronly` (only the coordinator). |\n\nWhen `CITUSVERSION` or `CITUSLIBDIR` is set, the test runner automatically disables\nversion checks (`citus.enable_version_checks=off`, `columnar.enable_version_checks=off`).\n\n### Supported scenarios\n\n| Scenario | Description | Variables |\n|---|---|---|\n| SQL version N-1 | All nodes create the Citus extension at an older version | `CITUSVERSION=13.2-1 N1MODE=all` |\n| Library version N-1 | All nodes load `citus.so` from an older release | `CITUSLIBDIR=~/citus-libs/17/v13.2.0 N1MODE=all` |\n| Worker N-1 | Only worker 1 uses the older library and extension version | `CITUSLIBDIR=~/citus-libs/18/v14.0 CITUSVERSION=14.0-1` |\n| Coordinator N-1 | Only the coordinator uses the older library and extension version | `CITUSLIBDIR=~/citus-libs/17/v13.2.0 CITUSVERSION=13.2-1 N1MODE=coordinatoronly` |\n\n### Schedule considerations\n\nNot all schedules are compatible with N-1 testing:\n\n- **`check-multi-1-create-citus`** and **`check-multi-mx`**: These schedules drop and recreate the\n   Citus extension using the default version, which is incompatible with N-1 setups.\n- **`check-vanilla`**: The test preparation steps have not yet been adapted for N-1 workflows.\n\nThe `multi_1_create_citus_schedule` was split out from `multi_1_schedule` to isolate tests that\ndrop/create the Citus extension, so the remaining `multi_1_schedule` tests can be reused safely\nin N-1 scenarios.\n\n### Local testing\n\n#### Prerequisites\n\n1. Install the older Citus version you want to test against (e.g. 13.2).\n2. Copy the `citus.so` from that installation to a known directory:\n   ```bash\n   mkdir -p ~/citus-libs/17/v13.2.0\n   cp /path/to/old/citus.so ~/citus-libs/17/v13.2.0/\n   ```\n3. Install the current (HEAD) version of Citus:\n   ```bash\n   make install -j9\n   ```\n\n#### Example commands\n\n```bash\n# Test with only the SQL extension version pinned to N-1 on all nodes\nCITUSVERSION=13.2-1 N1MODE=all make -C src/test/regress check-minimal\n\n# Test with both the library and extension version pinned to N-1 on worker only\nCITUSLIBDIR=~/citus-libs/18/v14.0 CITUSVERSION=14.0-1 N1MODE=workeronly make -C src/test/regress check-minimal\n\n# Run a full schedule with coordinator at N-1\nCITUSLIBDIR=~/citus-libs/18/v14.0 CITUSVERSION=14.0-1 N1MODE=coordinatoronly make -C src/test/regress check-multi-1\n\n# Run a single test using run_test.py\nCITUSLIBDIR=~/citus-libs/18/v14.0 CITUSVERSION=14.0-1 N1MODE=workeronly src/test/regress/citus_tests/run_test.py check_mx\n```\n\n# Regression test best practices\n\n* Instead of connecting to different nodes to check catalog tables, should use `run_command_on_all_nodes()` because it's faster than keep disconnecting / connecting to different nodes.\n\n* Tests should **define functions** for repetitive actions, e.g., by wrapping usual queries used to check catalog tables.\n  If the function is presumed to be used by other tests in future, then the function needs to defined in `multi_test_helpers.sql`.\n\n* If you're adding a new file, consider using `src/test/regress/bin/create_test.py your_new_test_name` to create the file. Or if you want to manually create it, make sure that your test file creates a schema and that it drops the schema at the end of the test to make sure that it doesn't leak any objects behind. See which lines `src/test/regress/bin/create_test.py` adds to the test file to understand what you need to do.\n\n  For the object that are not bound to a schema, make sure to drop them at the end of the test too, such as databases and roles.\n"
  },
  {
    "path": "src/test/regress/after_citus_upgrade_coord_schedule",
    "content": "# this schedule is to be run only on coordinators\n\ntest: upgrade_citus_finish_citus_upgrade\ntest: upgrade_pg_dist_cleanup_after\ntest: upgrade_basic_after\ntest: upgrade_basic_after_non_mixed\ntest: upgrade_post_11_after\ntest: upgrade_post_14_after\n"
  },
  {
    "path": "src/test/regress/after_pg_upgrade_with_columnar_schedule",
    "content": "test: upgrade_basic_after upgrade_ref2ref_after upgrade_type_after upgrade_distributed_function_after upgrade_rebalance_strategy_after upgrade_list_citus_objects upgrade_autoconverted_after upgrade_citus_stat_activity upgrade_citus_locks upgrade_single_shard_table_after upgrade_schema_based_sharding_after upgrade_basic_after_non_mixed\n\n# This test cannot be run with run_test.py currently due to its dependence on\n# the specific PG versions that we use to run upgrade tests. For now we leave\n# it out of the parallel line, so that flaky test detection can at least work\n# for the other tests.\ntest: upgrade_distributed_triggers_after\n\n# This attempts dropping citus extension (and rollbacks), so please do\n# not run in parallel with any other tests.\ntest: upgrade_columnar_after\n"
  },
  {
    "path": "src/test/regress/after_pg_upgrade_without_columnar_schedule",
    "content": "test: upgrade_basic_after upgrade_ref2ref_after upgrade_type_after upgrade_distributed_function_after upgrade_rebalance_strategy_after upgrade_list_citus_objects upgrade_autoconverted_after upgrade_citus_stat_activity upgrade_citus_locks upgrade_single_shard_table_after upgrade_schema_based_sharding_after upgrade_basic_after_non_mixed\n\n# This test cannot be run with run_test.py currently due to its dependence on\n# the specific PG versions that we use to run upgrade tests. For now we leave\n# it out of the parallel line, so that flaky test detection can at least work\n# for the other tests.\ntest: upgrade_distributed_triggers_after\n\n# The last test to ensure citus columnar not automatically created and upgrade\n# went fine without automatically creating it.\ntest: ensure_citus_columnar_not_exists\n"
  },
  {
    "path": "src/test/regress/base_isolation_schedule",
    "content": "# ----------\n# isolation setup steps\n# ----------\ntest: isolation_setup\ntest: isolation_cluster_management\n"
  },
  {
    "path": "src/test/regress/base_schedule",
    "content": "# ----------\n# Only run few basic tests to set up a testing environment\n# ----------\ntest: multi_test_helpers multi_test_helpers_superuser multi_create_fdw failure_test_helpers\ntest: multi_cluster_management\ntest: multi_test_catalog_views\ntest: multi_create_table\ntest: multi_behavioral_analytics_create_table\ntest: multi_create_table_superuser multi_behavioral_analytics_create_table_superuser\ntest: multi_load_data multi_load_data_superuser tablespace\ntest: check_mx\n"
  },
  {
    "path": "src/test/regress/before_citus_upgrade_coord_schedule",
    "content": "# this schedule is to be run on only coordinators\n\ntest: upgrade_basic_before upgrade_basic_before_non_mixed\ntest: upgrade_pg_dist_cleanup_before\ntest: upgrade_post_11_before\ntest: upgrade_post_14_before\n"
  },
  {
    "path": "src/test/regress/before_pg_upgrade_with_columnar_schedule",
    "content": "# The basic tests runs analyze which depends on shard numbers\ntest: multi_test_helpers multi_test_helpers_superuser upgrade_basic_before_non_mixed\ntest: multi_test_catalog_views\ntest: upgrade_basic_before\ntest: upgrade_ref2ref_before\ntest: upgrade_type_before\ntest: upgrade_distributed_function_before upgrade_rebalance_strategy_before\ntest: upgrade_autoconverted_before upgrade_single_shard_table_before upgrade_schema_based_sharding_before\ntest: upgrade_citus_stat_activity\ntest: upgrade_citus_locks\ntest: upgrade_distributed_triggers_before\n\n# upgrade_columnar_before renames public schema to citus_schema, so let's\n# run this test as the last one.\ntest: upgrade_columnar_before\n"
  },
  {
    "path": "src/test/regress/before_pg_upgrade_without_columnar_schedule",
    "content": "# The basic tests runs analyze which depends on shard numbers\ntest: multi_test_helpers multi_test_helpers_superuser upgrade_basic_before_non_mixed\ntest: multi_test_catalog_views\ntest: upgrade_basic_before\ntest: upgrade_ref2ref_before\ntest: upgrade_type_before\ntest: upgrade_distributed_function_before upgrade_rebalance_strategy_before\ntest: upgrade_autoconverted_before upgrade_single_shard_table_before upgrade_schema_based_sharding_before\ntest: upgrade_citus_stat_activity\ntest: upgrade_citus_locks\ntest: upgrade_distributed_triggers_before\n\n# The last test, i.e., upgrade_columnar_before, in before_pg_upgrade_with_columnar_schedule\n# renames public schema to citus_schema and re-creates public schema, so we also do the same\n# here to have compatible output in after schedule tests for both schedules.\ntest: rename_public_to_citus_schema_and_recreate\n"
  },
  {
    "path": "src/test/regress/bin/copy_modified",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\n# This is to make the following command work to update the test output:\n# cp src/test/regress/{results,expected}/multi_cluster_management.out\n#\n# This can not be done in the custom \"diff\" command, because that is executed\n# multiple times\n\nfor modified_file in {results,expected}/*.modified; do\n\toriginal=${modified_file%.modified}\n\tmv \"$original\" \"$original.unmodified\"\n\tmv \"$modified_file\" \"$original\"\ndone\n"
  },
  {
    "path": "src/test/regress/bin/copy_modified_wrapper",
    "content": "#!/bin/bash\n\nset -euo pipefail\n\n## Set mydir to the directory containing the script\n## The ${var%pattern} format will remove the shortest match of\n## pattern from the end of the string. Here, it will remove the\n## script's name,. leaving only the directory.\ndatadir=\"${0%/*}\"\ncd \"${datadir}\"\n\n# shellcheck source=copy_modified\nsource copy_modified\n"
  },
  {
    "path": "src/test/regress/bin/create_test.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport random\nimport sys\n\nif __name__ == \"__main__\":\n    if len(sys.argv) != 2:\n        print(\n            \"ERROR: Expected the name of the new test as an argument, such as:\\n\"\n            \"src/test/regress/bin/create_test.py my_awesome_test\"\n        )\n        sys.exit(1)\n\n    test_name = sys.argv[1]\n\n    regress_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))\n    filename = os.path.join(regress_dir, \"sql\", f\"{test_name}.sql\")\n\n    if os.path.isfile(filename):\n        print(f\"ERROR: test file '{filename}' already exists\")\n        sys.exit(1)\n\n    shard_id = random.randint(1, 999999) * 100\n\n    contents = f\"\"\"CREATE SCHEMA {test_name};\nSET search_path TO {test_name};\nSET citus.shard_count TO 4;\nSET citus.shard_replication_factor TO 1;\nSET citus.next_shard_id TO {shard_id};\n\n-- add tests here\n\nSET client_min_messages TO WARNING;\nDROP SCHEMA {test_name} CASCADE;\n\"\"\"\n\n    with open(filename, \"w\") as f:\n        f.write(contents)\n\n    print(f\"Created {filename}\")\n    print(f\"Don't forget to add '{test_name}' in multi_schedule somewhere\")\n"
  },
  {
    "path": "src/test/regress/bin/diff",
    "content": "#!/usr/bin/env bash\n\n# Our custom diff tool which normalizes result and expected files before\n# doing the actual diff. Rules for normalization are in normalize.sed. See\n# the comments there for more information.\n#\n# Note that src/test/regress/Makefile adds this directory to $PATH so\n# pg_regress uses this diff tool instead of the system diff tool.\nset -eu -o pipefail\n\nfile1=\"${@:(-2):1}\"\nfile2=\"${@:(-1):1}\"\n# pg_regress passes the expected file as the first argument ($file1 above),\n# and results file as the second argument ($file2 above). We take the base\n# filename of the expected file as the test name. We can have multiple\n# expected files for a single test with _0, _1, ... suffixes.\n# So for the test name, we also strip the additional suffix.\ntest=$(basename \"$file1\" .out | sed -E \"s/_[0-9]+$//\")\nargs=${@:1:$#-2}\nBASEDIR=$(dirname \"$0\")\n\nDIFF=/usr/bin/diff\nif [ ! -f \"$DIFF\" ]\nthen\n\t# whereis searches for standard unix places before $PATH. So select the\n\t# first entry as the original diff tool.\n\t# With the default WSL2 configuration whereis is very slow though ~400ms,\n\t# so we only use it if /usr/bin/diff does not exist.\n\tDIFF=$(whereis diff | sed \"s/diff://g\" | awk '{print $1}')\n\tif [ -z \"$DIFF\" ]\n\tthen\n\t\techo \"ERROR: could not find diff command\" 1>&2\n\t\texit 1\n\tfi\nfi\n\nif test -z \"${VANILLATEST:-}\"\nthen\n\ttouch \"$file1\"  # when adding a new test the expected file does not exist\n\tnormalize_file=\"$BASEDIR/normalize.sed\"\n\t# when running tests on an existing cluster some changes need to be done on\n\t# normalize.sed file. So a new file is used.\n\tif [[ -f \"$BASEDIR/normalize_modified.sed\" ]]\n\tthen\n\t\tnormalize_file=\"$BASEDIR/normalize_modified.sed\"\n\tfi\n\tsed -Ef \"$normalize_file\" < \"$file1\" > \"$file1.modified\"\n\tsed -Ef \"$normalize_file\" < \"$file2\" > \"$file2.modified\"\n\t\"$DIFF\" -w $args \"$file1.modified\" \"$file2.modified\" | LC_CTYPE=C.UTF-8 diff-filter \"$BASEDIR/normalize.sed\"\n\texit ${PIPESTATUS[0]}\nelse\n\texec \"$DIFF\" -w $args \"$file1\" \"$file2\"\nfi\n\n"
  },
  {
    "path": "src/test/regress/bin/diff-filter",
    "content": "#!/usr/bin/env python3\n\n\"\"\"\ndiff-filter denormalizes diff output by having lines beginning with ' ' or '+'\ncome from file2's unmodified version.\n\"\"\"\n\nimport re\nfrom sys import argv, stdin, stdout\n\n\nclass FileScanner:\n    \"\"\"\n    FileScanner is an iterator over the lines of a file.\n    It can apply a rewrite rule which can be used to skip lines.\n    \"\"\"\n\n    def __init__(self, file, rewrite=lambda x: x):\n        self.file = file\n        self.line = 1\n        self.rewrite = rewrite\n\n    def __next__(self):\n        while True:\n            nextline = self.rewrite(next(self.file))\n            if nextline is not None:\n                self.line += 1\n                return nextline\n\n\ndef main():\n    # we only test //d rules, as we need to ignore those lines\n    regexregex = re.compile(r\"^/(?P<rule>.*)/d$\")\n    regexpipeline = []\n    for line in open(argv[1]):\n        line = line.strip()\n        if not line or line.startswith(\"#\") or not line.endswith(\"d\"):\n            continue\n        rule = regexregex.match(line)\n        if not rule:\n            raise \"Failed to parse regex rule: %s\" % line\n        regexpipeline.append(re.compile(rule.group(\"rule\")))\n\n    def sed(line):\n        if any(regex.search(line) for regex in regexpipeline):\n            return None\n        return line\n\n    for line in stdin:\n        if line.startswith(\"+++ \"):\n            tab = line.rindex(\"\\t\")\n            fname = line[4:tab]\n            file2 = FileScanner(\n                open(fname.replace(\".modified\", \"\"), encoding=\"utf8\"), sed\n            )\n            stdout.write(line)\n        elif line.startswith(\"@@ \"):\n            idx_start = line.index(\"+\") + 1\n            idx_end = idx_start + 1\n            while line[idx_end].isdigit():\n                idx_end += 1\n            linenum = int(line[idx_start:idx_end])\n            while file2.line < linenum:\n                next(file2)\n            stdout.write(line)\n        elif line.startswith(\" \"):\n            stdout.write(\" \")\n            stdout.write(next(file2))\n        elif line.startswith(\"+\"):\n            stdout.write(\"+\")\n            stdout.write(next(file2))\n        else:\n            stdout.write(line)\n\n\nmain()\n"
  },
  {
    "path": "src/test/regress/bin/normalize.sed",
    "content": "# Rules to normalize test outputs. Our custom diff tool passes test output\n# of tests through the substitution rules in this file before doing the\n# actual comparison.\n#\n# An example of when this is useful is when an error happens on a different\n# port number, or a different worker shard, or a different placement, etc.\n# because we are running the tests in a different configuration.\n\n# In all tests, normalize worker ports, placement ids, and shard ids\ns/localhost:[0-9]+/localhost:xxxxx/g\ns/ port=[0-9]+ / port=xxxxx /g\ns/placement [0-9]+/placement xxxxx/g\ns/shard [0-9]+/shard xxxxx/g\ns/Shard [0-9]+/Shard xxxxx/g\ns/assigned task [0-9]+ to node/assigned task to node/\ns/node group [12] (but|does)/node group \\1/\n\n# Differing names can have differing table column widths\ns/^-[+-]{2,}$/---------------------------------------------------------------------/g\n\n# In foreign_key_to_reference_table, normalize shard table names, etc in\n# the generated plan\ns/\"(foreign_key_2_|fkey_ref_to_dist_|fkey_ref_|fkey_to_ref_)[0-9]+\"/\"\\1xxxxxxx\"/g\ns/\"(referenced_table_|referencing_table_|referencing_table2_)[0-9]+\"/\"\\1xxxxxxx\"/g\ns/\"(referencing_table_0_|referencing_table_4_|referenced_table2_)[0-9]+\"/\"\\1xxxxxxx\"/g\ns/\\(id\\)=\\([0-9]+\\)/(id)=(X)/g\ns/\\(ref_id\\)=\\([0-9]+\\)/(ref_id)=(X)/g\n\n# shard table names for multi_subtransactions\ns/\"t2_[0-9]+\"/\"t2_xxxxxxx\"/g\n\n# shard table names for MERGE tests\ns/merge_schema\\.([_a-z0-9]+)_40[0-9]+ /merge_schema.\\1_xxxxxxx /g\ns/pgmerge_schema\\.([_a-z0-9]+)_40[0-9]+ /pgmerge_schema.\\1_xxxxxxx /g\ns/merge_vcore_schema\\.([_a-z0-9]+)_40[0-9]+ /pgmerge_schema.\\1_xxxxxxx /g\n\n# shard table names for multi_subquery\ns/ keyval(1|2|ref)_[0-9]+ / keyval\\1_xxxxxxx /g\n\n# shard table names for custom_aggregate_support\ns/ daily_uniques_[0-9]+ / daily_uniques_xxxxxxx /g\n\n# shard table names for isolation_create_citus_local_table\ns/\"citus_local_table_([0-9]+)_[0-9]+\"/\"citus_local_table_\\1_xxxxxxx\"/g\n\n# normalize relation oid suffix for the truncate triggers created by citus\ns/truncate_trigger_[0-9]+/truncate_trigger_xxxxxxx/g\n\n# shard move subscription and publication names contain the oid of the\n# table owner, which can change across runs\ns/(citus_shard_(move|split)_subscription_role_)[0-9]+_[0-9]+/\\1xxxxxxx_xxxxxxx/g\ns/(citus_shard_(move|split)_subscription_)[0-9]+_[0-9]+/\\1xxxxxxx_xxxxxxx/g\ns/(citus_shard_(move|split)_(slot|publication)_)[0-9]+_[0-9]+_[0-9]+/\\1xxxxxxx_xxxxxxx_xxxxxxx/g\n\n# In foreign_key_restriction_enforcement, normalize shard names\ns/\"(on_update_fkey_table_|fkey_)[0-9]+\"/\"\\1xxxxxxx\"/g\n\n# In multi_insert_select_conflict, normalize shard name and constraints\ns/\"(target_table_|target_table_|test_ref_table_)[0-9]+\"/\"\\1xxxxxxx\"/g\ns/\\(col_1\\)=\\([0-9]+\\)/(col_1)=(X)/g\n\n# In multi_name_lengths, normalize shard names\ns/name_len_12345678901234567890123456789012345678_fcd8ab6f_[0-9]+/name_len_12345678901234567890123456789012345678_fcd8ab6f_xxxxx/g\n\n# ignore page split with pg13, and WAL warnings\n# they can randomly appear when DEBUG logs are on\n/DEBUG:  concurrent ROOT page split/d\n/DEBUG: .+creating and filling new WAL file/d\n\n# normalize debug connection failure\ns/DEBUG:  connection to the remote node/WARNING:  connection to the remote node/g\n\n# normalize file names for partitioned files\ns/(task_[0-9]+\\.)[0-9]+/\\1xxxx/g\ns/(job_[0-9]+\\/task_[0-9]+\\/p_[0-9]+\\.)[0-9]+/\\1xxxx/g\n\n# isolation_ref2ref_foreign_keys\ns/\"(ref_table_[0-9]_|ref_table_[0-9]_value_fkey_)[0-9]+\"/\"\\1xxxxxxx\"/g\n\n# commands cascading to shard relations\ns/(NOTICE:  .*_)[0-9]{5,}( CASCADE)/\\1xxxxx\\2/g\ns/(NOTICE:  [a-z]+ cascades to table \".*)_[0-9]{5,}\"/\\1_xxxxx\"/g\n\n# Line info varies between versions\n/^LINE [0-9]+:.*$/d\n/^ *\\^$/d\n\n# connection id\ns/connectionId: [0-9]+/connectionId: xxxxxxx/g\n\n# Remove trailing whitespace\ns/ *$//g\n\n# pg13 changes\ns/of relation \".*\" violates not-null constraint/violates not-null constraint/g\n/DEBUG:  index \".*\" can safely use deduplication.*$/d\n/DEBUG:  index \".*\" cannot use deduplication.*$/d\n/DEBUG:  building index \".*\" on table \".*\" serially.*$/d\ns/partition \".*\" would be violated by some row/partition would be violated by some row/g\ns/of relation \".*\" contains null values/contains null values/g\n\ns/(Citus Background Task Queue Executor: regression\\/postgres for \\()[0-9]+\\/[0-9]+\\)/\\1xxxxx\\/xxxxx\\)/g\n\n# Changed outputs after minor bump to PG14.5 and PG13.8\ns/(ERROR: |WARNING: |error:) invalid socket/\\1 connection not open/g\n\n# Extra outputs after minor bump to PG14.5 and PG13.8\n/^\\s*invalid socket$/d\n\n# pg15 changes\ns/ AS \"\\?column\\?\"//g\n# We ignore multiline error messages, and substitute first line with a single line\n# alternative that is used in some older libpq versions.\ns/(ERROR: |WARNING: |error:) server closed the connection unexpectedly/\\1 connection not open/g\n/^\\s*This probably means the server terminated abnormally$/d\n/^\\s*before or while processing the request.$/d\n/^\\s*connection not open$/d\n\n# intermediate_results\ns/(ERROR.*)pgsql_job_cache\\/([0-9]+_[0-9]+_[0-9]+)\\/(.*).data/\\1pgsql_job_cache\\/xx_x_xxx\\/\\3.data/g\n\n# assign_distributed_transaction id params\ns/(NOTICE.*)assign_distributed_transaction_id\\([0-9]+, [0-9]+, '.*'\\)/\\1assign_distributed_transaction_id\\(xx, xx, 'xxxxxxx'\\)/g\ns/(NOTICE.*)PREPARE TRANSACTION 'citus_[0-9]+_[0-9]+_[0-9]+_[0-9]+'/\\1PREPARE TRANSACTION 'citus_xx_xx_xx_xx'/g\ns/(NOTICE.*)COMMIT PREPARED 'citus_[0-9]+_[0-9]+_[0-9]+_[0-9]+'/\\1COMMIT PREPARED 'citus_xx_xx_xx_xx'/g\n\n# toast tables\ns/pg_toast_[0-9]+/pg_toast_xxxxx/g\n\n# Plan numbers are not very stable, so we normalize those\n# subplan numbers are quite stable so we keep those\ns/DEBUG:  Plan [0-9]+/DEBUG:  Plan XXX/g\ns/generating subplan [0-9]+\\_/generating subplan XXX\\_/g\ns/read_intermediate_result\\('[0-9]+_/read_intermediate_result('XXX_/g\ns/Subplan [0-9]+\\_/Subplan XXX\\_/g\n\n# Plan numbers in insert select\ns/read_intermediate_result\\('insert_select_[0-9]+_/read_intermediate_result('insert_select_XXX_/g\n# Plan numbers in merge into\ns/read_intermediate_result\\('merge_into_[0-9]+_/read_intermediate_result('merge_into_XXX_/g\n\n# ignore job id in repartitioned insert/select\ns/repartitioned_results_[0-9]+/repartitioned_results_xxxxx/g\n\n# ignore job id in worker_hash_partition_table\ns/worker_hash_partition_table  \\([0-9]+/worker_hash_partition_table  \\(xxxxxxx/g\n\n# ignore referene table replication messages\n/replicating reference table.*$/d\n\n# ignore memory usage output\n/.*Memory Usage:.*/d\n\n# Warnings in multi_explain\ns/prepared transaction with identifier .* does not exist/prepared transaction with identifier \"citus_x_yyyyyy_zzz_w\" does not exist/g\ns/failed to roll back prepared transaction '.*'/failed to roll back prepared transaction 'citus_x_yyyyyy_zzz_w'/g\n\n# Errors with binary decoding where OIDs should be normalized\ns/wrong data type: [0-9]+, expected [0-9]+/wrong data type: XXXX, expected XXXX/g\n\n# Errors with relation OID does not exist\ns/relation with OID [0-9]+ does not exist/relation with OID XXXX does not exist/g\n\n# ignore event triggers, mainly due to the event trigger for columnar\n/^DEBUG:  EventTriggerInvoke [0-9]+$/d\n\n# ignore DEBUG1 messages that Postgres generates\n/^DEBUG:  rehashing catalog cache id .*$/d\n\n# ignore JIT related messages\n/^DEBUG:  probing availability of JIT.*/d\n/^DEBUG:  provider not available, disabling JIT for current session.*/d\n/^DEBUG:  time to inline:.*/d\n/^DEBUG:  successfully loaded JIT.*/d\n\n# ignore timing statistics for VACUUM VERBOSE\n/CPU: user: .*s, system: .*s, elapsed: .*s/d\n\n# normalize storage id of columnar tables\ns/^storage id: [0-9]+$/storage id: xxxxx/g\n\n# normalize notice messages in citus_local_tables\ns/(NOTICE:  executing.*)citus_local_tables_test_schema.citus_local_table_4_[0-9]+(.*)/\\1citus_local_tables_test_schema.citus_local_table_4_xxxx\\2/g\ns/(NOTICE:  executing.*)\\([0-9]+, 'citus_local_tables_test_schema', [0-9]+(.*)/\\1\\(xxxxx, 'citus_local_tables_test_schema', xxxxx\\2/g\ns/citus_local_table_4_idx_[0-9]+/citus_local_table_4_idx_xxxxxx/g\ns/citus_local_table_4_[0-9]+/citus_local_table_4_xxxxxx/g\ns/ERROR:  cannot append to shardId [0-9]+/ERROR:  cannot append to shardId xxxxxx/g\n\n# hide notice/hint message that we get when converting local tables automatically\n/local tables that are added to metadata automatically by citus, but not chained with reference tables via foreign keys might be automatically converted back to postgres tables$/d\n/Executing citus_add_local_table_to_metadata(.*) prevents this for the given relation, and all of the connected relations$/d\n\n# normalize for distributed deadlock delay in isolation_metadata_sync_deadlock\n# isolation tester first detects a lock, but then deadlock detector cancels the\n# session. Sometimes happens that deadlock detector cancels the session before\n# lock detection, so we normalize it by removing these two lines.\n/^ <waiting ...>$/ {\n    N; /\\nstep s1-update-2: <... completed>$/ {\n        s/.*//g\n    }\n}\n\n# normalize long table shard name errors for alter_table_set_access_method and alter_distributed_table\ns/^(ERROR:  child table is missing constraint \"\\w+)_([0-9])+\"/\\1_xxxxxx\"/g\ns/^(DEBUG:  the name of the shard \\(abcde_01234567890123456789012345678901234567890_f7ff6612)_([0-9])+/\\1_xxxxxx/g\ns/^(ERROR:  cannot distribute relation: numeric_negative_scale)_([0-9]+)/\\1_xxxxxx\"/g\n\n# normalize long index name errors for multi_index_statements\ns/^(ERROR:  The index name \\(test_index_creation1_p2020_09_26)_([0-9])+_(tenant_id_timeperiod_idx)/\\1_xxxxxx_\\3/g\ns/^(DEBUG:  the index name on the shards of the partition is too long, switching to sequential and local execution mode to prevent self deadlocks: test_index_creation1_p2020_09_26)_([0-9])+_(tenant_id_timeperiod_idx)/\\1_xxxxxx_\\3/g\n\n# normalize errors for not being able to connect to a non-existing host\ns/could not translate host name \"([A-Za-z0-9\\.\\-]+)\" to address: .*$/could not translate host name \"\\1\" to address: <system specific error>/g\n\n# ignore PL/pgSQL line numbers that differ on Mac builds\ns/(CONTEXT:  PL\\/pgSQL function .* line )([0-9]+)/\\1XX/g\ns/^(PL\\/pgSQL function .* line) [0-9]+ (.*)/\\1 XX \\2/g\n\n# normalize a test difference in multi_move_mx\ns/ connection to server at \"\\w+\" (\\(127\\.0\\.0\\.1\\)|\\(::1\\)), port [0-9]+ failed://g\n\n# normalize differences in tablespace of new index\ns/pg14\\.idx.*/pg14\\.xxxxx/g\ns/CREATE TABLESPACE test_tablespace LOCATION.*/CREATE TABLESPACE test_tablespace LOCATION XXXX/g\n\n# columnar log for var correlation\ns/(.*absolute correlation \\()([0,1]\\.[0-9]+)(\\) of var attribute [0-9]+ is smaller than.*)/\\1X\\.YZ\\3/g\n\n# normalize differences in multi_fix_partition_shard_index_names test\ns/NOTICE:  issuing WITH placement_data\\(shardid, shardlength, groupid, placementid\\)  AS \\(VALUES \\([0-9]+, [0-9]+, [0-9]+, [0-9]+\\)\\)/NOTICE:  issuing WITH placement_data\\(shardid, shardlength, groupid, placementid\\)  AS \\(VALUES \\(xxxxxx, xxxxxx, xxxxxx, xxxxxx\\)\\)/g\n\n# global_pid when pg_cancel_backend is sent to workers\ns/pg_cancel_backend\\('[0-9]+'::bigint\\)/pg_cancel_backend('xxxxx'::bigint)/g\ns/issuing SELECT pg_cancel_backend\\([0-9]+::integer\\)/issuing SELECT pg_cancel_backend(xxxxx::integer)/g\n\n# shard_rebalancer output for flaky nodeIds\ns/issuing SELECT pg_catalog.citus_copy_shard_placement\\(43[0-9]+,[0-9]+,[0-9]+,'block_writes'\\)/issuing SELECT pg_catalog.citus_copy_shard_placement(43xxxx,xx,xx,'block_writes')/g\n\n# node id in run_command_on_all_nodes warning\ns/Error on node with node id [0-9]+/Error on node with node id xxxxx/g\n\n# Temp schema names in error messages regarding dependencies that we cannot distribute\n#\n# 1) Schema of the depending object in the error message:\n#\n# e.g.:\n#   WARNING:  \"function pg_temp_3.f(bigint)\" has dependency on unsupported object \"<foo>\"\n# will be replaced with\n#   WARNING:  \"function pg_temp_xxx.f(bigint)\" has dependency on unsupported object \"<foo>\"\ns/^(WARNING|ERROR)(:  \"[a-z\\ ]+ )pg_temp_[0-9]+(\\..*\" has dependency on unsupported object \".*\")$/\\1\\2pg_temp_xxx\\3/g\n\n# 2) Schema of the depending object in the error detail:\ns/^(DETAIL:  \"[a-z\\ ]+ )pg_temp_[0-9]+(\\..*\" will be created only locally)$/\\1pg_temp_xxx\\2/g\n\n# 3) Schema that the object depends in the error message:\n# e.g.:\n#   WARNING:  \"function func(bigint)\" has dependency on unsupported object \"schema pg_temp_3\"\n# will be replaced with\n#   WARNING:  \"function func(bigint)\" has dependency on unsupported object \"schema pg_temp_xxx\"\ns/^(WARNING|ERROR)(:  \"[a-z\\ ]+ .*\" has dependency on unsupported object) \"schema pg_temp_[0-9]+\"$/\\1\\2 \"schema pg_temp_xxx\"/g\n\n# remove jobId's from the messages of the background rebalancer\ns/^ERROR:  A rebalance is already running as job [0-9]+$/ERROR:  A rebalance is already running as job xxx/g\ns/^NOTICE:  Scheduled ([0-9]+) moves as job [0-9]+$/NOTICE:  Scheduled \\1 moves as job xxx/g\ns/^HINT: (.*) job_id = [0-9]+ (.*)$/HINT: \\1 job_id = xxx \\2/g\n\n# In clock tests, normalize epoch value(s) and the DEBUG messages printed\ns/^(DEBUG:  |LOG:  )(coordinator|final global|Set) transaction clock [0-9]+.*$/\\1\\2 transaction clock xxxxxx/g\n# Look for >= 13 digit logical value\ns/^ (\\(localhost,)([0-9]+)(,t,\"\\([1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]+,[0-9]+\\)\")/\\1 xxx,t,\"(xxxxxxxxxxxxx,x)\"/g\ns/^ \\([1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]+,[0-9]+\\)/ (xxxxxxxxxxxxx,x)/g\ns/^(DEBUG:  |LOG:  )(node\\([0-9]+\\)) transaction clock [0-9]+.*$/\\1node xxxx transaction clock xxxxxx/g\ns/^(NOTICE:  )(clock).*LC:[0-9]+,.*C:[0-9]+,.*$/\\1\\2 xxxxxx/g\n/^(DEBUG:  )(adjusted to remote clock: <logical)\\([0-9]+\\) counter\\([0-9]+\\)>$/d\n/^DEBUG:  persisting transaction.*counter.*$/d\n/^DEBUG:  both logical clock values are equal\\([0-9]+\\), pick remote.*$/d\n\n# The following 2 lines are to normalize duration and cost in the EXPLAIN output\ns/LOG:  duration: [0-9].[0-9]+ ms/LOG:  duration: xxxx ms/g\ns/\"Total Cost\": [0-9].[0-9]+/\"Total Cost\": xxxx/g\n\n# normalize gpids\ns/(NOTICE:  issuing SET LOCAL application_name TO 'citus_rebalancer gpid=)[0-9]+/\\1xxxxx/g\n\n# shard_rebalancer output, flaky improvement number\ns/improvement of 0.1[0-9]* is lower/improvement of 0.1xxxxx is lower/g\n# normalize tenants statistics annotations\ns/\\/\\*\\{\"cId\":.*\\*\\///g\n\n# Notice message that contains current columnar version that makes it harder to bump versions\ns/(NOTICE:  issuing CREATE EXTENSION IF NOT EXISTS citus_columnar WITH SCHEMA  pg_catalog VERSION )\"[0-9]+\\.[0-9]+-[0-9]+\"/\\1 \"x.y-z\"/\n\n# pg17 changes\n# can be removed when dropping PG16 support\n#if PG_VERSION_NUM < PG_VERSION_17\n# (This is not preprocessor directive, but a reminder for the developer that will drop PG16 support )\n\ns/COPY DEFAULT only available using COPY FROM/COPY DEFAULT cannot be used with COPY TO/\ns/COPY delimiter must not appear in the DEFAULT specification/COPY delimiter character must not appear in the DEFAULT specification/\n\n#endif /* PG_VERSION_NUM < PG_VERSION_17 */\n\n# PG 17 Removes outer parentheses from CHECK constraints\n# we add them back for pg16 compatibility\n# e.g. change CHECK other_col >= 100 to CHECK (other_col >= 100)\ns/\\| CHECK ([a-zA-Z])(.*)/| CHECK \\(\\1\\2\\)/g\n\n# pg17 change: this is a rule that ignores additional DEBUG logging\n# for CREATE MATERIALIZED VIEW (commit b4da732fd64). This could be\n# changed to a normalization rule when 17 becomes the minimum\n# supported Postgres version.\n\n/DEBUG:  drop auto-cascades to type [a-zA-Z_]*.pg_temp_[0-9]*/d\n\n# --- PG18 Actual Rows normalization ---\n# New in PG18: Actual Rows in EXPLAIN output are now rounded to\n# 1) 0.50 (and 0.5, 0.5000...) -> 0\ns/(actual[[:space:]]*rows[[:space:]]*[=:][[:space:]]*)0\\.50*/\\10/gI\ns/(actual[^)]*rows[[:space:]]*=[[:space:]]*)0\\.50*/\\10/gI\n\n# 2) 0.51+ -> 1\ns/(actual[[:space:]]*rows[[:space:]]*[=:][[:space:]]*)0\\.(5[1-9][0-9]*|[6-9][0-9]*)/\\11/gI\ns/(actual[^)]*rows[[:space:]]*=[[:space:]]*)0\\.(5[1-9][0-9]*|[6-9][0-9]*)/\\11/gI\n\n# 3) Strip trivial trailing \".0...\" (6.00 -> 6)  [keep your existing cross-format rules]\ns/(actual[[:space:]]*rows[[:space:]]*[=:][[:space:]]*)([0-9]+)\\.0+/\\1\\2/gI\ns/(actual[^)]*rows[[:space:]]*=[[:space:]]*)([0-9]+)\\.0+/\\1\\2/gI\n\n# 4) YAML/XML/JSON: strip trailing \".0...\"\ns/(Actual[[:space:]]+Rows:[[:space:]]*[0-9]+)\\.0+/\\1/gI\ns/(<Actual-Rows>[0-9]+)\\.0+(<\\/Actual-Rows>)/\\1\\2/g\ns/(\"Actual[[:space:]]+Rows\":[[:space:]]*[0-9]+)\\.0+/\\1/gI\n\n# 5) Placeholder cleanups (kept from existing rules; harmless if unused)\n#    JSON placeholder cleanup: '\"Actual Rows\": N.N' -> N\ns/(\"Actual[[:space:]]+Rows\":[[:space:]]*)N\\.N/\\1N/gI\n#    Text EXPLAIN collapse: \"rows=N.N\" -> \"rows=N\"\ns/(rows[[:space:]]*=[[:space:]]*)N\\.N/\\1N/gI\n#    YAML placeholder: \"Actual Rows: N.N\" -> \"Actual Rows: N\"\ns/(Actual[[:space:]]+Rows:[[:space:]]*)N\\.N/\\1N/gI\n# --- PG18 Actual Rows normalization ---\n\n# pg18 “Disabled” change start\n# ignore any “Disabled:” lines in test output\n/^\\s*Disabled:/d\n\n# ignore XML <Disabled>true</Disabled> or <Disabled>false</Disabled>\n/^\\s*<Disabled>.*<\\/Disabled>/d\n# pg18 “Disabled” change end\n\n# PG18 psql: headings changed from \"List of relations\" to per-type titles\ns/^([ \\t]*)List of tables$/\\1List of relations/g\ns/^([ \\t]*)List of indexes$/\\1List of relations/g\ns/^([ \\t]*)List of sequences$/\\1List of relations/g\n\n# --- PG18 FK wording -> legacy generic form ---\n# e.g., \"violates RESTRICT setting of foreign key constraint\" -> \"violates foreign key constraint\"\ns/violates RESTRICT setting of foreign key constraint/violates foreign key constraint/g\n# DETAIL line changed \"is referenced\" -> old \"is still referenced\"\ns/\\<is referenced from table\\>/is still referenced from table/g\n\n# pg18 extension_control_path GUC debugs\n# ignore any \"find_in_path:\" lines in test output\n/DEBUG:  find_in_path: trying .*/d\n\n# EXPLAIN (PG18+): hide Materialize storage instrumentation\n# this rule can be removed when PG18 is the minimum supported version\n/^[ \\t]*Storage:[ \\t].*$/d\n\n# PG18: drop 'subscription \"<name>\"' prefix\n# this rule can be removed when PG18 is the minimum supported version\ns/^[[:space:]]*ERROR:[[:space:]]+subscription \"[^\"]+\" could not connect to the publisher:[[:space:]]*/ERROR:  could not connect to the publisher: /I\n# PG18: drop verbose 'connection to server … failed:' preamble\ns/^[[:space:]]*ERROR:[[:space:]]+could not connect to the publisher:[[:space:]]*connection to server .* failed:[[:space:]]*/ERROR:  could not connect to the publisher: /I\n\n# PG18: replace named window refs like \"OVER w1\" with neutral \"OVER (?)\"\n# this rule can be removed when PG18 is the minimum supported version\n# only on Sort Key / Group Key / Output lines\n# Sort Key\n/^[[:space:]]*Sort Key:/ s/(OVER[[:space:]]+)w[0-9]+/\\1(?)/g\n# Group Key\n/^[[:space:]]*Group Key:/ s/(OVER[[:space:]]+)w[0-9]+/\\1(?)/g\n# Output\n/^[[:space:]]*Output:/   s/(OVER[[:space:]]+)w[0-9]+/\\1(?)/g\n# end PG18 window ref normalization\n\n# pg18 varreturningtype - change needed for PG16, PG17 tests\n# can be removed when dropping pg17 support\ns/(:varnullingrels \\(b\\) :varlevelsup 0) (:varnosyn 1)/\\1 :varreturningtype 0 \\2/g\n# end pg18 varreturningtype\n"
  },
  {
    "path": "src/test/regress/bin/test/expected/different.out",
    "content": "--- file.out.modified\n+++ file_different.out.modified\n@@ -1,3 +1,2 @@\n-This line is missing in file_different\n Ports are replaced with xxxxx: localhost:2187\n This line is the same\n@@ -7,6 +6,8 @@\n Filler 2, localhost:1111\n Filler 3, localhost:111\n-This line is missing in file_different\n+This line has been inserted\n+This line has also been inserted, localhost:10812\n Line below will be removed, localhost:2781\n-This line will be changed ✓\n+This line has been changed ✇\n End of file\n+New line at end\n"
  },
  {
    "path": "src/test/regress/bin/test/expected/same.out",
    "content": ""
  },
  {
    "path": "src/test/regress/bin/test/file.out",
    "content": "This line is missing in file_different\nPorts are replaced with xxxxx: localhost:1728\nThis line is the same\nThis line is also the same\nDEBUG: we remove this line creating and filling new WAL file\nLine above was deleted é\nFiller 1, localhost:21870\nFiller 2, localhost:2187\nFiller 3, localhost:218\nThis line is missing in file_different\nLine below will be removed, localhost:2187\nDEBUG: we remove this line creating and filling new WAL file\nThis line will be changed ✓\nEnd of file\n"
  },
  {
    "path": "src/test/regress/bin/test/file_different.out",
    "content": "Ports are replaced with xxxxx: localhost:2187\nThis line is the same\nThis line is also the same\nDEBUG: we remove this line creating and filling new WAL file\nLine above was deleted é\nFiller 1, localhost:11111\nFiller 2, localhost:1111\nFiller 3, localhost:111\nThis line has been inserted\nThis line has also been inserted, localhost:10812\nDEBUG: we remove this line creating and filling new WAL file\nLine below will be removed, localhost:2781\nThis line has been changed ✇\nEnd of file\nNew line at end\n"
  },
  {
    "path": "src/test/regress/bin/test/file_same.out",
    "content": "This line is missing in file_different\nPorts are replaced with xxxxx: localhost:1728\nThis line is the same\nThis line is also the same\nDEBUG: we remove this line creating and filling new WAL file\nLine above was deleted é\nFiller 1, localhost:21870\nFiller 2, localhost:2187\nFiller 3, localhost:218\nThis line is missing in file_different\nLine below will be removed, localhost:2187\nDEBUG: we remove this line creating and filling new WAL file\nThis line will be changed ✓\nEnd of file\n"
  },
  {
    "path": "src/test/regress/bin/test_diff",
    "content": "#!/bin/bash\n\nSCRIPT=`realpath -s \"$0\"`\nSCRIPTPATH=`dirname \"$SCRIPT\"`\nPATH=\"$SCRIPTPATH:$PATH\"\n\ncp \"$SCRIPTPATH/test/file.out\" \"$SCRIPTPATH/test/file_same.out\"\nmkdir -p \"$SCRIPTPATH/test/results\"\n\n# diff file.out against file_$1.out, also strip out timestamps & file paths\nfunction create_result()\n{\n\tdiff -dU2 -w \"$SCRIPTPATH/test/file.out\" \"$SCRIPTPATH/test/file_$1.out\" \\\n\t\t| sed -E 's/^(\\+\\+\\+|---).+\\/([^/]+)\\t.+$/\\1 \\2/' \\\n\t\t> \"$SCRIPTPATH/test/results/$1.out\"\n}\n\n# compare whether result is same as expected\nfunction check_result()\n{\n\tcmp -s \"$SCRIPTPATH/test/expected/$1.out\" \"$SCRIPTPATH/test/results/$1.out\"\n\tif test $? -ne 0\n\tthen\n\t\tdiff -u \"$SCRIPTPATH/test/expected/$1.out\" \"$SCRIPTPATH/test/results/$1.out\"\n\t\texit 1\n\tfi\n}\n\nfunction test_case()\n{\n\t# call twice as tests invoke diff multiple times\n\tcreate_result \"$1\"\n\tcreate_result \"$1\"\n\tcheck_result \"$1\"\n}\n\ntest_case \"same\"\ntest_case \"different\"\n"
  },
  {
    "path": "src/test/regress/citus_tests/__init__.py",
    "content": ""
  },
  {
    "path": "src/test/regress/citus_tests/arbitrary_configs/README.md",
    "content": "# Arbitrary Configs\n\n## Usage\n\nTo run tests in parallel use:\n\n```bash\n# will run 4 configs in parallel\nmake check-arbitrary-configs parallel=4\n```\n\nTo run tests sequentially use:\n\n```bash\nmake check-arbitrary-configs parallel=1\n```\n\nTo run only some configs:\n\n```bash\n# Config names should be comma separated\nmake check-arbitrary-configs CONFIGS=CitusSingleNodeClusterConfig,CitusSmallSharedPoolSizeConfig\n```\n\nTo run only some test files with some config:\n\n```bash\nmake check-arbitrary-base CONFIGS=CitusSingleNodeClusterConfig EXTRA_TESTS=dropped_columns_1\n```\n\nTo get a deterministic run, you can give the random's seed:\n\n```bash\nmake check-arbitrary-configs parallel=4 seed=12312\n```\n\nThe `seed` will be in the output of the run.\n\n## General Info\n\nIn our regular regression tests, we can see all the details about either planning or execution but this means\nwe need to run the same query under different configs/cluster setups again and again, which is not really maintanable.\n\nWhen we don't care about the internals of how planning/execution is done but the correctness, especially with different configs\nthis infrastructure can be used.\n\nWith `check-arbitrary-configs` target, the following happens:\n\n-   a bunch of configs are loaded, which are defined in `config.py`. These configs have different settings such as different shard count, different citus settings, postgres settings, worker amount, or different metadata.\n-   For each config, a separate data directory is created for tests in `tmp_citus_test` with the config's name.\n-   For each config, `create_schedule` is run on the coordinator to setup the necessary tables.\n-   For each config, `sql_schedule` is run. `sql_schedule` is run on the coordinator if it is a non-mx cluster. And if it is mx, it is either run on the coordinator or a random worker.\n-   Tests results are checked if they match with the expected.\n\nWhen tests results don't match, you can see the regression diffs in a config's datadir, such as `tmp_citus_tests/dataCitusSingleNodeClusterConfig`.\n\nWe also have a PostgresConfig which runs all the test suite with Postgres.\nBy default configs use regular user, but we have a config to run as a superuser as well.\n\nSo the infrastructure tests:\n\n-   Postgres vs Citus\n-   Mx vs Non-Mx\n-   Superuser vs regular user\n-   Arbitrary Citus configs\n\n## Adding a new test\n\nWhen you want to add a new test, you can add the create statements to `create_schedule` and add the sql queries to `sql_schedule`.\nIf you are adding Citus UDFs that should be a NO-OP for Postgres, make sure to override the UDFs in `postgres.sql`.\n\nIf the test needs to be skipped in some configs, you can do that by adding the test names in the `skip_tests` array for\neach config. The test files associated with the skipped test will be set to empty so the test will pass without the actual test\nbeing run.\n\n## Adding a new config\n\nYou can add your new config to `config.py`. Make sure to extend either `CitusDefaultClusterConfig` or `CitusMXBaseClusterConfig`.\n\n## Debugging failures\n\nOn the CI, upon a failure, all logfiles will be uploaded as artifacts, so you can check the artifacts tab.\nAll the regressions will be shown as part of the job on CI.\n\nIn your local, you can check the regression diffs in config's datadirs as in `tmp_citus_tests/dataCitusSingleNodeClusterConfig`.\n"
  },
  {
    "path": "src/test/regress/citus_tests/arbitrary_configs/__init__.py",
    "content": ""
  },
  {
    "path": "src/test/regress/citus_tests/arbitrary_configs/citus_arbitrary_configs.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"citus_arbitrary_configs\nUsage:\n    citus_arbitrary_configs --bindir=<bindir> --pgxsdir=<pgxsdir> --parallel=<parallel> --configs=<configs> --seed=<seed> [--base]\n\nOptions:\n    --bindir=<bindir>              The PostgreSQL executable directory(ex: '~/.pgenv/pgsql-11.3/bin')\n    --pgxsdir=<pgxsdir>           \t       Path to the PGXS directory(ex: ~/.pgenv/src/postgresql-11.3)\n    --parallel=<parallel>           how many configs to run in parallel\n    --configs=<configs>     the config names to run\n    --seed=<seed>                   random number seed\n    --base                          whether to use the base sql schedule or not\n\"\"\"\nimport concurrent.futures\nimport multiprocessing\nimport os\nimport random\nimport shutil\nimport sys\nimport time\n\nfrom docopt import docopt\n\n# https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time/14132912#14132912\nsys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\n# ignore E402 because these imports require addition to path\nimport common  # noqa: E402\n\nimport config as cfg  # noqa: E402\n\ntestResults = {}\nparallel_thread_amount = 1\n\n\ndef _run_pg_regress_on_port(config, port, schedule_name, extra_tests=\"\"):\n    return common.run_pg_regress_without_exit(\n        config.bindir,\n        config.pg_srcdir,\n        port,\n        schedule_name,\n        config.output_dir,\n        config.input_dir,\n        config.user,\n        extra_tests,\n    )\n\n\ndef run_for_config(config, lock, sql_schedule_name):\n    name = config.name\n    print(\"Running test for: {}\".format(name))\n    start_time = time.time()\n    common.initialize_citus_cluster(\n        config.bindir, config.datadir, config.settings, config\n    )\n    if config.user == cfg.REGULAR_USER_NAME:\n        common.create_role(\n            config.bindir,\n            config.node_name_to_ports.values(),\n            config.user,\n        )\n\n    copy_copy_modified_binary(config.datadir)\n    copy_test_files(config)\n\n    exitCode = 0\n    if not config.is_citus:\n        exitCode |= common.run_pg_regress_without_exit(\n            config.bindir,\n            config.pg_srcdir,\n            config.coordinator_port(),\n            cfg.POSTGRES_SCHEDULE,\n            config.output_dir,\n            config.input_dir,\n            cfg.SUPER_USER_NAME,\n        )\n        common.save_regression_diff(\"postgres\", config.output_dir)\n    elif config.all_null_dist_key:\n        exitCode |= common.run_pg_regress_without_exit(\n            config.bindir,\n            config.pg_srcdir,\n            config.coordinator_port(),\n            cfg.SINGLE_SHARD_PREP_SCHEDULE,\n            config.output_dir,\n            config.input_dir,\n            cfg.SUPER_USER_NAME,\n        )\n        common.save_regression_diff(\n            \"single_shard_table_prep_regression\", config.output_dir\n        )\n\n    exitCode |= _run_pg_regress_on_port(\n        config, config.coordinator_port(), cfg.CREATE_SCHEDULE\n    )\n    common.save_regression_diff(\"create\", config.output_dir)\n\n    extra_tests = os.getenv(\"EXTRA_TESTS\", \"\")\n    if config.is_mx and config.worker_amount > 0:\n        exitCode |= _run_pg_regress_on_port(\n            config, config.random_port(), sql_schedule_name, extra_tests=extra_tests\n        )\n    else:\n        exitCode |= _run_pg_regress_on_port(\n            config,\n            config.coordinator_port(),\n            sql_schedule_name,\n            extra_tests=extra_tests,\n        )\n\n    run_time = time.time() - start_time\n    with lock:\n        testResults[name] = (\n            \"SUCCESS\"\n            if exitCode == 0\n            else \"FAIL: see {}\".format(config.output_dir + \"/run.out\")\n        )\n        testResults[name] += \" runtime: {} seconds\".format(run_time)\n\n    common.stop_databases(\n        config.bindir, config.datadir, config.node_name_to_ports, config.name\n    )\n    common.save_regression_diff(\"sql\", config.output_dir)\n    return exitCode\n\n\ndef copy_copy_modified_binary(datadir):\n    shutil.copy(\"bin/copy_modified\", datadir)\n    shutil.copy(\"bin/copy_modified_wrapper\", datadir)\n\n\ndef copy_test_files(config):\n    sql_dir_path = os.path.join(config.datadir, \"sql\")\n    expected_dir_path = os.path.join(config.datadir, \"expected\")\n\n    common.initialize_temp_dir(sql_dir_path)\n    common.initialize_temp_dir(expected_dir_path)\n    for scheduleName in cfg.ARBITRARY_SCHEDULE_NAMES:\n        with open(scheduleName) as file:\n            lines = file.readlines()\n            for line in lines:\n                colon_index = line.find(\":\")\n                # skip empty lines\n                if colon_index == -1:\n                    continue\n\n                line = line[colon_index + 1 :].strip()\n                test_names = line.split(\" \")\n                copy_test_files_with_names(\n                    test_names, sql_dir_path, expected_dir_path, config\n                )\n\n\ndef copy_test_files_with_names(test_names, sql_dir_path, expected_dir_path, config):\n    for test_name in test_names:\n        # make empty files for the skipped tests\n        if test_name in config.skip_tests:\n            expected_sql_file = os.path.join(sql_dir_path, test_name + \".sql\")\n            open(expected_sql_file, \"x\").close()\n\n            expected_out_file = os.path.join(expected_dir_path, test_name + \".out\")\n            open(expected_out_file, \"x\").close()\n\n            continue\n\n        sql_name = os.path.join(\"./sql\", test_name + \".sql\")\n        shutil.copy(sql_name, sql_dir_path)\n\n        # for a test named <t>, all files:\n        # <t>.out, <t>_0.out, <t>_1.out ...\n        # are considered as valid outputs for the test\n        # by the testing tool (pg_regress)\n        # so copy such files to the testing directory\n        output_name = os.path.join(\"./expected\", test_name + \".out\")\n        alt_output_version_no = 0\n        while os.path.isfile(output_name):\n            # it might be the first time we run this test and the expected file\n            # might not be there yet, in that case, we don't want to error out\n            # while copying the file.\n            shutil.copy(output_name, expected_dir_path)\n            output_name = os.path.join(\n                \"./expected\", f\"{test_name}_{alt_output_version_no}.out\"\n            )\n            alt_output_version_no += 1\n\n\ndef run_tests(configs, sql_schedule_name):\n    failCount = 0\n    common.initialize_temp_dir(cfg.CITUS_ARBITRARY_TEST_DIR)\n    with concurrent.futures.ThreadPoolExecutor(\n        max_workers=parallel_thread_amount\n    ) as executor:\n        manager = multiprocessing.Manager()\n        lock = manager.Lock()\n        futures = [\n            executor.submit(run_for_config, config, lock, sql_schedule_name)\n            for config in configs\n        ]\n        try:\n            for future in futures:\n                exitCode = future.result()\n                if exitCode != 0:\n                    failCount += 1\n        except KeyboardInterrupt:\n            exit(1)\n\n    return failCount\n\n\ndef read_configs(docoptRes):\n    configs = []\n    # We fill the configs from all of the possible classes in config.py so that if we add a new config,\n    # we don't need to add it here. And this avoids the problem where we forget to add it here\n    for x in cfg.__dict__.values():\n        if cfg.should_include_config(x):\n            configs.append(x(docoptRes))\n    return configs\n\n\ndef read_arguments(docoptRes):\n    global parallel_thread_amount\n\n    if \"--parallel\" in docoptRes and docoptRes[\"--parallel\"] != \"\":\n        parallel_thread_amount = int(docoptRes[\"--parallel\"])\n\n    seed = random.randint(1, 1000000)\n\n    if \"--seed\" in docoptRes and docoptRes[\"--seed\"] != \"\":\n        seed = int(docoptRes[\"--seed\"])\n\n    random.seed(seed)\n\n    configs = read_configs(docoptRes)\n    if \"--configs\" in docoptRes and docoptRes[\"--configs\"] != \"\":\n        given_configs = docoptRes[\"--configs\"].split(\",\")\n        new_configs = []\n        for config in configs:\n            if config.name in given_configs:\n                new_configs.append(config)\n        if len(new_configs) > 0:\n            configs = new_configs\n\n    sql_schedule_name = cfg.SQL_SCHEDULE\n    if \"--base\" in docoptRes and docoptRes[\"--base\"]:\n        sql_schedule_name = cfg.SQL_BASE_SCHEDULE\n    return configs, sql_schedule_name, seed\n\n\ndef show_results(configs, testResults, runtime, seed):\n    for testName, testResult in testResults.items():\n        print(\"{}: {}\".format(testName, testResult))\n\n    print(\"--- {} seconds to run all tests! ---\".format(end_time - start_time))\n    print(\"---SEED: {} ---\".format(seed))\n\n    configCount = len(configs)\n    if len(testResults) != configCount or failCount > 0:\n        print(\n            \"actual {} expected {}, failCount: {}\".format(\n                len(testResults), configCount, failCount\n            )\n        )\n        sys.exit(1)\n\n\nif __name__ == \"__main__\":\n    docoptRes = docopt(__doc__)\n\n    start_time = time.time()\n    configs, sql_schedule_name, seed = read_arguments(docoptRes)\n    failCount = run_tests(configs, sql_schedule_name)\n    end_time = time.time()\n\n    show_results(configs, testResults, end_time - start_time, seed)\n"
  },
  {
    "path": "src/test/regress/citus_tests/common.py",
    "content": "import asyncio\nimport atexit\nimport concurrent.futures\nimport os\nimport pathlib\nimport platform\nimport random\nimport re\nimport shutil\nimport socket\nimport subprocess\nimport sys\nimport time\nimport typing\nfrom abc import ABC, abstractmethod\nfrom contextlib import asynccontextmanager, closing, contextmanager\nfrom datetime import datetime, timedelta\nfrom pathlib import Path\nfrom tempfile import gettempdir\n\nimport filelock\nimport psycopg\nimport psycopg.sql\nimport utils\nfrom psycopg import sql\nfrom utils import USER\n\n# This SQL returns true ( 't' ) if the Citus version >= 11.0.\nIS_CITUS_VERSION_11_SQL = \"SELECT (split_part(extversion, '.', 1)::int >= 11) as is_11 FROM pg_extension WHERE extname = 'citus';\"\n\nLINUX = False\nMACOS = False\nFREEBSD = False\nOPENBSD = False\n\nif platform.system() == \"Linux\":\n    LINUX = True\nelif platform.system() == \"Darwin\":\n    MACOS = True\nelif platform.system() == \"FreeBSD\":\n    FREEBSD = True\nelif platform.system() == \"OpenBSD\":\n    OPENBSD = True\n\nBSD = MACOS or FREEBSD or OPENBSD\n\nTIMEOUT_DEFAULT = timedelta(seconds=int(os.getenv(\"PG_TEST_TIMEOUT_DEFAULT\", \"10\")))\nFORCE_PORTS = os.getenv(\"PG_FORCE_PORTS\", \"NO\").lower() not in (\"no\", \"0\", \"n\", \"\")\n\nREGRESS_DIR = pathlib.Path(os.path.realpath(__file__)).parent.parent\nREPO_ROOT = REGRESS_DIR.parent.parent.parent\nCI = os.environ.get(\"CI\") == \"true\"\n\n\ndef eprint(*args, **kwargs):\n    \"\"\"eprint prints to stderr\"\"\"\n\n    print(*args, file=sys.stderr, **kwargs)\n\n\ndef run(command, *args, check=True, shell=True, silent=False, **kwargs):\n    \"\"\"run runs the given command and prints it to stderr\"\"\"\n\n    if not silent:\n        eprint(f\"+ {command} \")\n    if silent:\n        kwargs.setdefault(\"stdout\", subprocess.DEVNULL)\n    return subprocess.run(command, *args, check=check, shell=shell, **kwargs)\n\n\ndef capture(command, *args, **kwargs):\n    \"\"\"runs the given command and returns its output as a string\"\"\"\n    return run(command, *args, stdout=subprocess.PIPE, text=True, **kwargs).stdout\n\n\nPG_CONFIG = os.environ.get(\"PG_CONFIG\", \"pg_config\")\nPG_CONFIG_ARGS = capture([PG_CONFIG, \"--configure\"], shell=False).rstrip()\nPG_SUPPORTS_SSL = \"--with-ssl\" in PG_CONFIG_ARGS or \"--with-openssl\" in PG_CONFIG_ARGS\n\nPG_BINDIR = capture([PG_CONFIG, \"--bindir\"], shell=False).rstrip()\nos.environ[\"PATH\"] = PG_BINDIR + os.pathsep + os.environ[\"PATH\"]\n\n\ndef get_pg_major_version():\n    full_version_string = run(\n        \"initdb --version\", stdout=subprocess.PIPE, encoding=\"utf-8\", silent=True\n    ).stdout\n    major_version_string = re.search(\"[0-9]+\", full_version_string)\n    assert major_version_string is not None\n    return int(major_version_string.group(0))\n\n\nPG_MAJOR_VERSION = get_pg_major_version()\n\nOLDEST_SUPPORTED_CITUS_VERSION_MATRIX = {\n    14: \"10.2.0\",\n    15: \"11.1.5\",\n    16: \"12.1.5\",\n    17: \"13.0.1\",\n    18: \"15.0devel\",\n}\n\nOLDEST_SUPPORTED_CITUS_VERSION = OLDEST_SUPPORTED_CITUS_VERSION_MATRIX[PG_MAJOR_VERSION]\n\n\ndef initialize_temp_dir(temp_dir):\n    if os.path.exists(temp_dir):\n        shutil.rmtree(temp_dir)\n    os.mkdir(temp_dir)\n    # Give full access to TEMP_DIR so that postgres user can use it.\n    os.chmod(temp_dir, 0o777)\n\n\ndef initialize_temp_dir_if_not_exists(temp_dir):\n    if os.path.exists(temp_dir):\n        return\n    os.mkdir(temp_dir)\n    # Give full access to TEMP_DIR so that postgres user can use it.\n    os.chmod(temp_dir, 0o777)\n\n\ndef parallel_run(function, items, *args, **kwargs):\n    with concurrent.futures.ThreadPoolExecutor() as executor:\n        futures = [executor.submit(function, item, *args, **kwargs) for item in items]\n        for future in futures:\n            future.result()\n\n\ndef initialize_db_for_cluster(pg_path, rel_data_path, settings, node_names):\n    subprocess.run([\"mkdir\", rel_data_path], check=True)\n\n    def initialize(node_name):\n        abs_data_path = os.path.abspath(os.path.join(rel_data_path, node_name))\n        command = [\n            os.path.join(pg_path, \"initdb\"),\n            \"--pgdata\",\n            abs_data_path,\n            \"--username\",\n            USER,\n            \"--no-sync\",\n            # --allow-group-access is used to ensure we set permissions on\n            # private keys correctly\n            \"--allow-group-access\",\n            \"--data-checksums\",\n            \"--encoding\",\n            \"UTF8\",\n            \"--locale\",\n            \"POSIX\",\n        ]\n        subprocess.run(command, check=True)\n        add_settings(abs_data_path, settings)\n\n    parallel_run(initialize, node_names)\n\n\ndef add_settings(abs_data_path, settings):\n    conf_path = os.path.join(abs_data_path, \"postgresql.conf\")\n    with open(conf_path, \"a\") as conf_file:\n        for setting_key, setting_val in settings.items():\n            setting = \"{setting_key} = '{setting_val}'\\n\".format(\n                setting_key=setting_key, setting_val=setting_val\n            )\n            conf_file.write(setting)\n\n\ndef create_role(pg_path, node_ports, user_name):\n    def create(port):\n        command = (\n            \"SET citus.enable_ddl_propagation TO OFF;\"\n            + \"SELECT worker_create_or_alter_role('{}', 'CREATE ROLE {} WITH LOGIN CREATEROLE CREATEDB;', NULL)\".format(\n                user_name, user_name\n            )\n        )\n        utils.psql(pg_path, port, command)\n        command = \"SET citus.enable_ddl_propagation TO OFF; GRANT CREATE ON DATABASE postgres to {}\".format(\n            user_name\n        )\n        utils.psql(pg_path, port, command)\n\n    parallel_run(create, node_ports)\n\n\ndef coordinator_should_haveshards(pg_path, port):\n    command = \"SELECT citus_set_node_property('localhost', {}, 'shouldhaveshards', true)\".format(\n        port\n    )\n    utils.psql(pg_path, port, command)\n\n\ndef start_databases(\n    pg_path, rel_data_path, node_name_to_ports, logfile_prefix, env_variables\n):\n    def start(node_name):\n        abs_data_path = os.path.abspath(os.path.join(rel_data_path, node_name))\n        node_port = node_name_to_ports[node_name]\n        command = [\n            os.path.join(pg_path, \"pg_ctl\"),\n            \"start\",\n            \"--pgdata\",\n            abs_data_path,\n            \"-U\",\n            USER,\n            \"-o\",\n            \"-p {}\".format(node_port),\n            \"--log\",\n            os.path.join(abs_data_path, logfile_name(logfile_prefix, node_name)),\n        ]\n\n        # set the application name if requires\n        if env_variables != {}:\n            os.environ.update(env_variables)\n\n        subprocess.run(command, check=True)\n\n    parallel_run(start, node_name_to_ports.keys())\n\n    # We don't want parallel shutdown here because that will fail when it's\n    # tried in this atexit call with an error like:\n    # cannot schedule new futures after interpreter shutdown\n    atexit.register(\n        stop_databases,\n        pg_path,\n        rel_data_path,\n        node_name_to_ports,\n        logfile_prefix,\n        no_output=True,\n        parallel=False,\n    )\n\n\ndef create_citus_extension(pg_path, node_ports):\n    def create(port):\n        utils.psql(pg_path, port, \"CREATE EXTENSION citus;\")\n\n    parallel_run(create, node_ports)\n\n\ndef run_pg_regress(pg_path, pg_srcdir, port, schedule):\n    should_exit = True\n    try:\n        _run_pg_regress(pg_path, pg_srcdir, port, schedule, should_exit)\n    finally:\n        subprocess.run(\"bin/copy_modified\", check=True)\n\n\ndef run_pg_regress_without_exit(\n    pg_path,\n    pg_srcdir,\n    port,\n    schedule,\n    output_dir=\".\",\n    input_dir=\".\",\n    user=\"postgres\",\n    extra_tests=\"\",\n):\n    should_exit = False\n    exit_code = _run_pg_regress(\n        pg_path,\n        pg_srcdir,\n        port,\n        schedule,\n        should_exit,\n        output_dir,\n        input_dir,\n        user,\n        extra_tests,\n    )\n    copy_binary_path = os.path.join(input_dir, \"copy_modified_wrapper\")\n\n    exit_code |= subprocess.call(copy_binary_path)\n    return exit_code\n\n\ndef _run_pg_regress(\n    pg_path,\n    pg_srcdir,\n    port,\n    schedule,\n    should_exit,\n    output_dir=\".\",\n    input_dir=\".\",\n    user=\"postgres\",\n    extra_tests=\"\",\n):\n    command = [\n        os.path.join(pg_srcdir, \"src/test/regress/pg_regress\"),\n        \"--port\",\n        str(port),\n        \"--schedule\",\n        schedule,\n        \"--bindir\",\n        pg_path,\n        \"--user\",\n        user,\n        \"--dbname\",\n        \"postgres\",\n        \"--inputdir\",\n        input_dir,\n        \"--outputdir\",\n        output_dir,\n        \"--use-existing\",\n    ]\n    if PG_MAJOR_VERSION >= 16:\n        command.append(\"--expecteddir\")\n        command.append(output_dir)\n    if extra_tests != \"\":\n        command.append(extra_tests)\n\n    exit_code = subprocess.call(command)\n    if should_exit and exit_code != 0:\n        sys.exit(exit_code)\n    return exit_code\n\n\ndef save_regression_diff(name, output_dir):\n    path = os.path.join(output_dir, \"regression.diffs\")\n    if not os.path.exists(path):\n        return\n    new_file_path = os.path.join(output_dir, \"./regression_{}.diffs\".format(name))\n    print(\"new file path:\", new_file_path)\n    shutil.move(path, new_file_path)\n\n\ndef stop_metadata_to_workers(pg_path, worker_ports, coordinator_port):\n    for port in worker_ports:\n        command = (\n            \"SELECT * from stop_metadata_sync_to_node('localhost', {port});\".format(\n                port=port\n            )\n        )\n        utils.psql(pg_path, coordinator_port, command)\n\n\ndef add_coordinator_to_metadata(pg_path, coordinator_port):\n    command = \"SELECT citus_set_coordinator_host('localhost');\"\n    utils.psql(pg_path, coordinator_port, command)\n\n\ndef add_workers(pg_path, worker_ports, coordinator_port):\n    for port in worker_ports:\n        command = \"SELECT * from master_add_node('localhost', {port});\".format(\n            port=port\n        )\n        utils.psql(pg_path, coordinator_port, command)\n\n\ndef logfile_name(logfile_prefix, node_name):\n    return \"logfile_\" + logfile_prefix + \"_\" + node_name\n\n\ndef stop_databases(\n    pg_path,\n    rel_data_path,\n    node_name_to_ports,\n    logfile_prefix,\n    no_output=False,\n    parallel=True,\n):\n    def stop(node_name):\n        abs_data_path = os.path.abspath(os.path.join(rel_data_path, node_name))\n        node_port = node_name_to_ports[node_name]\n        command = [\n            os.path.join(pg_path, \"pg_ctl\"),\n            \"stop\",\n            \"--pgdata\",\n            abs_data_path,\n            \"-U\",\n            USER,\n            \"-o\",\n            \"-p {}\".format(node_port),\n            \"--log\",\n            os.path.join(abs_data_path, logfile_name(logfile_prefix, node_name)),\n        ]\n        if no_output:\n            subprocess.call(\n                command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL\n            )\n        else:\n            subprocess.call(command)\n\n    if parallel:\n        parallel_run(stop, node_name_to_ports.keys())\n    else:\n        for node_name in node_name_to_ports.keys():\n            stop(node_name)\n\n\ndef is_citus_set_coordinator_host_udf_exist(pg_path, port):\n    return utils.psql_capture(pg_path, port, IS_CITUS_VERSION_11_SQL) == b\" t\\n\\n\"\n\n\ndef initialize_citus_cluster(bindir, datadir, settings, config):\n    # In case there was a leftover from previous runs, stop the databases\n    stop_databases(\n        bindir, datadir, config.node_name_to_ports, config.name, no_output=True\n    )\n    initialize_db_for_cluster(\n        bindir, datadir, settings, config.node_name_to_ports.keys()\n    )\n    start_databases(\n        bindir, datadir, config.node_name_to_ports, config.name, config.env_variables\n    )\n    create_citus_extension(bindir, config.node_name_to_ports.values())\n\n    # In upgrade tests, it is possible that Citus version < 11.0\n    # where the citus_set_coordinator_host UDF does not exist.\n    if is_citus_set_coordinator_host_udf_exist(bindir, config.coordinator_port()):\n        add_coordinator_to_metadata(bindir, config.coordinator_port())\n\n    add_workers(bindir, config.worker_ports, config.coordinator_port())\n    if not config.is_mx:\n        stop_metadata_to_workers(bindir, config.worker_ports, config.coordinator_port())\n\n    config.setup_steps()\n\n\ndef sudo(command, *args, shell=True, **kwargs):\n    \"\"\"\n    A version of run that prefixes the command with sudo when the process is\n    not already run as root\n    \"\"\"\n    effective_user_id = os.geteuid()\n    if effective_user_id == 0:\n        return run(command, *args, shell=shell, **kwargs)\n    if shell:\n        return run(f\"sudo {command}\", *args, shell=shell, **kwargs)\n    else:\n        return run([\"sudo\", *command])\n\n\n# this is out of ephemeral port range for many systems hence\n# it is a lower chance that it will conflict with \"in-use\" ports\nPORT_LOWER_BOUND = 10200\n\n# ephemeral port start on many Linux systems\nPORT_UPPER_BOUND = 32768\n\nnext_port = PORT_LOWER_BOUND\n\n\ndef notice_handler(diag: psycopg.errors.Diagnostic):\n    print(f\"{diag.severity}: {diag.message_primary}\")\n    if diag.message_detail:\n        print(f\"DETAIL: {diag.message_detail}\")\n    if diag.message_hint:\n        print(f\"HINT: {diag.message_hint}\")\n    if diag.context:\n        print(f\"CONTEXT: {diag.context}\")\n\n\ndef cleanup_test_leftovers(nodes):\n    \"\"\"\n    Cleaning up test leftovers needs to be done in a specific order, because\n    some of these leftovers depend on others having been removed. They might\n    even depend on leftovers on other nodes being removed. So this takes a list\n    of nodes, so that we can clean up all test leftovers globally in the\n    correct order.\n    \"\"\"\n    for node in nodes:\n        node.cleanup_subscriptions()\n\n    for node in nodes:\n        node.cleanup_publications()\n\n    for node in nodes:\n        node.cleanup_replication_slots()\n\n    for node in nodes:\n        node.cleanup_schemas()\n\n    for node in nodes:\n        node.cleanup_databases()\n\n    for node in nodes:\n        node.cleanup_users()\n\n\nclass PortLock:\n    \"\"\"PortLock allows you to take a lock an a specific port.\n\n    While a port is locked by one process, other processes using PortLock won't\n    get the same port.\n    \"\"\"\n\n    def __init__(self):\n        global next_port\n        first_port = next_port\n        while True:\n            next_port += 1\n            if next_port >= PORT_UPPER_BOUND:\n                next_port = PORT_LOWER_BOUND\n\n            # avoid infinite loop\n            if first_port == next_port:\n                raise Exception(\"Could not find port\")\n\n            self.lock = filelock.FileLock(Path(gettempdir()) / f\"port-{next_port}.lock\")\n            try:\n                self.lock.acquire(timeout=0)\n            except filelock.Timeout:\n                continue\n\n            if FORCE_PORTS:\n                self.port = next_port\n                break\n\n            with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:\n                try:\n                    s.bind((\"127.0.0.1\", next_port))\n                    self.port = next_port\n                    break\n                except Exception:\n                    self.lock.release()\n                    continue\n\n    def release(self):\n        \"\"\"Call release when you are done with the port.\n\n        This way other processes can use it again.\n        \"\"\"\n        self.lock.release()\n\n\nclass QueryRunner(ABC):\n    \"\"\"A subclassable interface class that can be used to run queries.\n\n    This is mostly useful to be generic across differnt types of things that\n    implement the Postgres interface, such as Postgres, PgBouncer, or a Citus\n    cluster.\n\n    This implements some helpers send queries in a simpler manner than psycopg\n    allows by default.\n    \"\"\"\n\n    @abstractmethod\n    def set_default_connection_options(self, options: dict[str, typing.Any]):\n        \"\"\"Sets the default connection options on the given options dictionary\n\n        This is the only method that the class that subclasses QueryRunner\n        needs to implement.\n        \"\"\"\n        ...\n\n    def make_conninfo(self, **kwargs) -> str:\n        self.set_default_connection_options(kwargs)\n        return psycopg.conninfo.make_conninfo(**kwargs)\n\n    def conn(self, *, autocommit=True, **kwargs):\n        \"\"\"Open a psycopg connection to this server\"\"\"\n        self.set_default_connection_options(kwargs)\n        conn = psycopg.connect(\n            autocommit=autocommit,\n            **kwargs,\n        )\n        conn.add_notice_handler(notice_handler)\n        return conn\n\n    def aconn(self, *, autocommit=True, **kwargs):\n        \"\"\"Open an asynchronous psycopg connection to this server\"\"\"\n        self.set_default_connection_options(kwargs)\n        return psycopg.AsyncConnection.connect(\n            autocommit=autocommit,\n            **kwargs,\n        )\n\n    @contextmanager\n    def cur(self, autocommit=True, **kwargs):\n        \"\"\"Open an psycopg cursor to this server\n\n        The connection and the cursors automatically close once you leave the\n        \"with\" block\n        \"\"\"\n        with self.conn(\n            autocommit=autocommit,\n            **kwargs,\n        ) as conn:\n            with conn.cursor() as cur:\n                yield cur\n\n    @asynccontextmanager\n    async def acur(self, **kwargs):\n        \"\"\"Open an asynchronous psycopg cursor to this server\n\n        The connection and the cursors automatically close once you leave the\n        \"async with\" block\n        \"\"\"\n        async with await self.aconn(**kwargs) as conn:\n            async with conn.cursor() as cur:\n                yield cur\n\n    def sql(self, query, params=None, **kwargs):\n        \"\"\"Run an SQL query\n\n        This opens a new connection and closes it once the query is done\n        \"\"\"\n        with self.cur(**kwargs) as cur:\n            cur.execute(query, params=params)\n\n    def sql_prepared(self, query, params=None, **kwargs):\n        \"\"\"Run an SQL query, with prepare=True\n\n        This opens a new connection and closes it once the query is done\n        \"\"\"\n        with self.cur(**kwargs) as cur:\n            cur.execute(query, params=params, prepare=True)\n\n    def sql_row(self, query, params=None, allow_empty_result=False, **kwargs):\n        \"\"\"Run an SQL query that returns a single row and returns this row\n\n        This opens a new connection and closes it once the query is done\n        \"\"\"\n        with self.cur(**kwargs) as cur:\n            cur.execute(query, params=params)\n            result = cur.fetchall()\n\n            if allow_empty_result and len(result) == 0:\n                return None\n\n            assert len(result) == 1, \"sql_row returns more than one row\"\n            return result[0]\n\n    def sql_value(self, query, params=None, allow_empty_result=False, **kwargs):\n        \"\"\"Run an SQL query that returns a single cell and return this value\n\n        This opens a new connection and closes it once the query is done\n        \"\"\"\n        with self.cur(**kwargs) as cur:\n            cur.execute(query, params=params)\n            result = cur.fetchall()\n\n            if allow_empty_result and len(result) == 0:\n                return None\n\n            assert len(result) == 1\n            assert len(result[0]) == 1\n            value = result[0][0]\n            return value\n\n    def asql(self, query, **kwargs):\n        \"\"\"Run an SQL query in asynchronous task\n\n        This opens a new connection and closes it once the query is done\n        \"\"\"\n        return asyncio.ensure_future(self.asql_coroutine(query, **kwargs))\n\n    async def asql_coroutine(\n        self, query, params=None, **kwargs\n    ) -> typing.Optional[typing.List[typing.Any]]:\n        async with self.acur(**kwargs) as cur:\n            await cur.execute(query, params=params)\n            try:\n                return await cur.fetchall()\n            except psycopg.ProgrammingError as e:\n                if \"the last operation didn't produce a result\" == str(e):\n                    return None\n                raise\n\n    def psql(self, query, **kwargs):\n        \"\"\"Run an SQL query using psql instead of psycopg\n\n        This opens a new connection and closes it once the query is done\n        \"\"\"\n\n        conninfo = self.make_conninfo(**kwargs)\n\n        run(\n            [\"psql\", \"-X\", f\"{conninfo}\", \"-c\", query],\n            shell=False,\n            silent=True,\n        )\n\n    def poll_query_until(self, query, params=None, expected=True, **kwargs):\n        \"\"\"Run query repeatedly until it returns the expected result\"\"\"\n        start = datetime.now()\n        result = None\n\n        while start + TIMEOUT_DEFAULT > datetime.now():\n            result = self.sql_value(\n                query, params=params, allow_empty_result=True, **kwargs\n            )\n            if result == expected:\n                return\n\n            time.sleep(0.1)\n\n        raise Exception(\n            f\"Timeout reached while polling query, last result was: {result}\"\n        )\n\n    @contextmanager\n    def transaction(self, **kwargs):\n        with self.cur(**kwargs) as cur:\n            with cur.connection.transaction():\n                yield cur\n\n    def sleep(self, duration=3, **kwargs):\n        \"\"\"Run pg_sleep\"\"\"\n        return self.sql(f\"select pg_sleep({duration})\", **kwargs)\n\n    def asleep(self, duration=3, times=1, sequentially=False, **kwargs):\n        \"\"\"Run pg_sleep asynchronously in a task.\n\n        times:\n            You can create a single task that opens multiple connections, which\n            run pg_sleep concurrently. The asynchronous task will only complete\n            once all these pg_sleep calls are finished.\n        sequentially:\n            Instead of running all pg_sleep calls spawned by providing\n            times > 1 concurrently, this will run them sequentially.\n        \"\"\"\n        return asyncio.ensure_future(\n            self.asleep_coroutine(\n                duration=duration, times=times, sequentially=sequentially, **kwargs\n            )\n        )\n\n    async def asleep_coroutine(self, duration=3, times=1, sequentially=False, **kwargs):\n        \"\"\"This is the coroutine that the asleep task runs internally\"\"\"\n        if not sequentially:\n            await asyncio.gather(\n                *[\n                    self.asql(f\"select pg_sleep({duration})\", **kwargs)\n                    for _ in range(times)\n                ]\n            )\n        else:\n            for _ in range(times):\n                await self.asql(f\"select pg_sleep({duration})\", **kwargs)\n\n    def test(self, **kwargs):\n        \"\"\"Test if you can connect\"\"\"\n        return self.sql(\"select 1\", **kwargs)\n\n    def atest(self, **kwargs):\n        \"\"\"Test if you can connect asynchronously\"\"\"\n        return self.asql(\"select 1\", **kwargs)\n\n    def psql_test(self, **kwargs):\n        \"\"\"Test if you can connect with psql instead of psycopg\"\"\"\n        return self.psql(\"select 1\", **kwargs)\n\n    def debug(self):\n        print(\"Connect manually to:\\n   \", repr(self.make_conninfo()))\n        print(\"Press Enter to continue running the test...\")\n        input()\n\n    def psql_debug(self, **kwargs):\n        conninfo = self.make_conninfo(**kwargs)\n        run(\n            [\"psql\", f\"{conninfo}\"],\n            shell=False,\n            silent=True,\n        )\n\n\nclass Postgres(QueryRunner):\n    \"\"\"A class that represents a Postgres instance on this machine\n\n    You can query it by using the interface provided by QueryRunner or use many\n    of the helper methods.\n    \"\"\"\n\n    def __init__(self, pgdata):\n        self.port_lock = PortLock()\n\n        # These values should almost never be changed after initialization\n        self.host = \"127.0.0.1\"\n        self.port = self.port_lock.port\n\n        # These values can be changed when needed\n        self.dbname = \"postgres\"\n        self.user = \"postgres\"\n        self.schema = None\n\n        self.pgdata = pgdata\n        self.log_path = self.pgdata / \"pg.log\"\n\n        # Used to track objects that we want to clean up at the end of a test\n        self.subscriptions = set()\n        self.publications = set()\n        self.replication_slots = set()\n        self.databases = set()\n        self.schemas = set()\n        self.users = set()\n\n    def set_default_connection_options(self, options):\n        options.setdefault(\"host\", self.host)\n        options.setdefault(\"port\", self.port)\n        options.setdefault(\"dbname\", self.dbname)\n        options.setdefault(\"user\", self.user)\n        if self.schema is not None:\n            options.setdefault(\"options\", f\"-c search_path={self.schema}\")\n        options.setdefault(\"connect_timeout\", 3)\n        # needed for Ubuntu 18.04\n        options.setdefault(\"client_encoding\", \"UTF8\")\n\n    def initdb(self):\n        run(\n            f\"initdb -A trust --nosync --username postgres --pgdata {self.pgdata} --allow-group-access --encoding UTF8 --locale POSIX\",\n            stdout=subprocess.DEVNULL,\n        )\n\n        with self.conf_path.open(mode=\"a\") as pgconf:\n            # Allow connecting over unix sockets\n            pgconf.write(\"unix_socket_directories = '/tmp'\\n\")\n\n            # Useful logs for debugging issues\n            pgconf.write(\"log_replication_commands = on\\n\")\n            # The following to are also useful for debugging, but quite noisy.\n            # So better to enable them manually by uncommenting.\n            # pgconf.write(\"log_connections = on\\n\")\n            # pgconf.write(\"log_disconnections = on\\n\")\n\n            # Enable citus\n            pgconf.write(\"shared_preload_libraries = 'citus'\\n\")\n\n            # Allow CREATE SUBSCRIPTION to work\n            pgconf.write(\"wal_level = 'logical'\\n\")\n            # Faster logical replication status update so tests with logical replication\n            # run faster\n            pgconf.write(\"wal_receiver_status_interval = 1\\n\")\n\n            # Faster logical replication apply worker launch so tests with logical\n            # replication run faster. This is used in ApplyLauncherMain in\n            # src/backend/replication/logical/launcher.c.\n            pgconf.write(\"wal_retrieve_retry_interval = '250ms'\\n\")\n\n            # Make sure there's enough logical replication resources for most\n            # of our tests\n            pgconf.write(\"max_logical_replication_workers = 50\\n\")\n            pgconf.write(\"max_wal_senders = 50\\n\")\n            pgconf.write(\"max_worker_processes = 50\\n\")\n            pgconf.write(\"max_replication_slots = 50\\n\")\n\n            # We need to make the log go to stderr so that the tests can\n            # check what is being logged.  This should be the default, but\n            # some packagings change the default configuration.\n            pgconf.write(\"log_destination = stderr\\n\")\n            # We don't need the logs anywhere else than stderr\n            pgconf.write(\"logging_collector = off\\n\")\n\n            # This makes tests run faster and we don't care about crash safety\n            # of our test data.\n            pgconf.write(\"fsync = false\\n\")\n\n            # conservative settings to ensure we can run multiple postmasters:\n            pgconf.write(\"shared_buffers = 1MB\\n\")\n            # limit disk space consumption, too:\n            pgconf.write(\"max_wal_size = 128MB\\n\")\n\n            # don't restart after crashes to make it obvious that a crash\n            # happened\n            pgconf.write(\"restart_after_crash = off\\n\")\n\n        os.truncate(self.hba_path, 0)\n        if PG_SUPPORTS_SSL:\n            self.ssl_access(\"all\", \"trust\")\n        self.nossl_access(\"all\", \"trust\")\n        self.commit_hba()\n\n    def init_with_citus(self):\n        self.initdb()\n        self.start()\n        self.sql(\"CREATE EXTENSION citus\")\n\n        if PG_SUPPORTS_SSL:\n            # Manually turn on ssl, so that we can safely truncate\n            # postgresql.auto.conf later. We can only do this after creating the\n            # citus extension because that creates the self signed certificates.\n            with self.conf_path.open(mode=\"a\") as pgconf:\n                pgconf.write(\"ssl = on\\n\")\n\n    def pgctl(self, command, **kwargs):\n        run(f\"pg_ctl -w --pgdata {self.pgdata} {command}\", **kwargs)\n\n    def apgctl(self, command, **kwargs):\n        return asyncio.create_subprocess_shell(\n            f\"pg_ctl -w --pgdata {self.pgdata} {command}\", **kwargs\n        )\n\n    def start(self):\n        try:\n            self.pgctl(f'-o \"-p {self.port}\" -l {self.log_path} start')\n        except Exception:\n            print(f\"\\n\\nPG_LOG: {self.pgdata}\\n\")\n            with self.log_path.open() as f:\n                print(f.read())\n            raise\n\n    def stop(self, mode=\"fast\"):\n        self.pgctl(f\"-m {mode} stop\", check=False)\n\n    def cleanup(self):\n        self.stop()\n        self.port_lock.release()\n\n    def restart(self):\n        self.stop()\n        self.start()\n\n    def reload(self):\n        self.pgctl(\"reload\")\n        # Sadly UNIX signals are asynchronous, so we sleep a bit and hope that\n        # Postgres actually processed the SIGHUP signal after the sleep.\n        time.sleep(0.1)\n\n    async def arestart(self):\n        process = await self.apgctl(\"-m fast restart\")\n        await process.communicate()\n\n    def nossl_access(self, dbname, auth_type):\n        \"\"\"Prepends a local non-SSL access to the HBA file\"\"\"\n        with self.hba_path.open() as pghba:\n            old_contents = pghba.read()\n        with self.hba_path.open(mode=\"w\") as pghba:\n            pghba.write(f\"local      {dbname}   all                {auth_type}\\n\")\n            pghba.write(f\"hostnossl  {dbname}   all  127.0.0.1/32  {auth_type}\\n\")\n            pghba.write(f\"hostnossl  {dbname}   all  ::1/128       {auth_type}\\n\")\n            pghba.write(old_contents)\n\n    def ssl_access(self, dbname, auth_type):\n        \"\"\"Prepends a local SSL access rule to the HBA file\"\"\"\n        with self.hba_path.open() as pghba:\n            old_contents = pghba.read()\n        with self.hba_path.open(mode=\"w\") as pghba:\n            pghba.write(f\"hostssl  {dbname}   all  127.0.0.1/32  {auth_type}\\n\")\n            pghba.write(f\"hostssl  {dbname}   all  ::1/128       {auth_type}\\n\")\n            pghba.write(old_contents)\n\n    @property\n    def hba_path(self):\n        return self.pgdata / \"pg_hba.conf\"\n\n    @property\n    def conf_path(self):\n        return self.pgdata / \"postgresql.conf\"\n\n    def commit_hba(self):\n        \"\"\"Mark the current HBA contents as non-resetable by reset_hba\"\"\"\n        with self.hba_path.open() as pghba:\n            old_contents = pghba.read()\n        with self.hba_path.open(mode=\"w\") as pghba:\n            pghba.write(\"# committed-rules\\n\")\n            pghba.write(old_contents)\n\n    def reset_hba(self):\n        \"\"\"Remove any HBA rules that were added after the last call to commit_hba\"\"\"\n        with self.hba_path.open() as f:\n            hba_contents = f.read()\n        committed = hba_contents[hba_contents.find(\"# committed-rules\\n\") :]\n        with self.hba_path.open(\"w\") as f:\n            f.write(committed)\n\n    def prepare_reset(self):\n        \"\"\"Prepares all changes to reset Postgres settings and objects\n\n        To actually apply the prepared changes a restart might still be needed.\n        \"\"\"\n        self.reset_hba()\n        os.truncate(self.pgdata / \"postgresql.auto.conf\", 0)\n\n    def reset(self):\n        \"\"\"Resets any changes to Postgres settings from previous tests\"\"\"\n        self.prepare_reset()\n        self.restart()\n\n    async def delayed_start(self, delay=1):\n        \"\"\"Start Postgres after a delay\n\n        NOTE: The sleep is asynchronous, but while waiting for Postgres to\n        start the pg_ctl start command will block the event loop. This is\n        currently acceptable for our usage of this method in the existing\n        tests and this way it was easiest to implement. However, it seems\n        totally reasonable to change this behaviour in the future if necessary.\n        \"\"\"\n        await asyncio.sleep(delay)\n        self.start()\n\n    def configure(self, *configs):\n        \"\"\"Configure specific Postgres settings using ALTER SYSTEM SET\n\n        NOTE: after configuring a call to reload or restart is needed for the\n        settings to become effective.\n        \"\"\"\n        for config in configs:\n            self.sql(f\"alter system set {config}\")\n\n    def log_handle(self):\n        \"\"\"Returns the opened logfile at the current end of the log\n\n        By later calling read on this file you can read the contents that were\n        written from this moment on.\n\n        IMPORTANT: This handle should be closed once it's not needed anymore\n        \"\"\"\n        f = self.log_path.open()\n        f.seek(0, os.SEEK_END)\n        return f\n\n    @contextmanager\n    def log_contains(self, re_string, times=None):\n        \"\"\"Checks if during this with block the log matches re_string\n\n        re_string:\n            The regex to search for.\n        times:\n            If None, any number of matches is accepted. If a number, only that\n            specific number of matches is accepted.\n        \"\"\"\n        with self.log_handle() as f:\n            yield\n            content = f.read()\n            if times is None:\n                assert re.search(re_string, content)\n            else:\n                match_count = len(re.findall(re_string, content))\n                assert match_count == times\n\n    def create_user(self, name, args: typing.Optional[psycopg.sql.Composable] = None):\n        self.users.add(name)\n        if args is None:\n            args = sql.SQL(\"\")\n        self.sql(sql.SQL(\"CREATE USER {} {}\").format(sql.Identifier(name), args))\n\n    def create_database(self, name):\n        self.databases.add(name)\n        self.sql(sql.SQL(\"CREATE DATABASE {}\").format(sql.Identifier(name)))\n\n    def create_schema(self, name):\n        self.schemas.add(name)\n        self.sql(sql.SQL(\"CREATE SCHEMA {}\").format(sql.Identifier(name)))\n\n    def create_publication(self, name: str, args: psycopg.sql.Composable):\n        self.publications.add(name)\n        self.sql(sql.SQL(\"CREATE PUBLICATION {} {}\").format(sql.Identifier(name), args))\n\n    def create_logical_replication_slot(\n        self, name, plugin, temporary=False, twophase=False\n    ):\n        self.replication_slots.add(name)\n        self.sql(\n            \"SELECT pg_catalog.pg_create_logical_replication_slot(%s,%s,%s,%s)\",\n            (name, plugin, temporary, twophase),\n        )\n\n    def create_subscription(self, name: str, args: psycopg.sql.Composable):\n        self.subscriptions.add(name)\n        self.sql(\n            sql.SQL(\"CREATE SUBSCRIPTION {} {}\").format(sql.Identifier(name), args)\n        )\n\n    def cleanup_users(self):\n        for user in self.users:\n            self.sql(sql.SQL(\"DROP USER IF EXISTS {}\").format(sql.Identifier(user)))\n\n    def cleanup_databases(self):\n        for database in self.databases:\n            self.sql(\n                sql.SQL(\"DROP DATABASE IF EXISTS {}\").format(sql.Identifier(database))\n            )\n\n    def cleanup_schemas(self):\n        for schema in self.schemas:\n            self.sql(\n                sql.SQL(\"DROP SCHEMA IF EXISTS {} CASCADE\").format(\n                    sql.Identifier(schema)\n                )\n            )\n\n    def cleanup_publications(self):\n        for publication in self.publications:\n            self.sql(\n                sql.SQL(\"DROP PUBLICATION IF EXISTS {}\").format(\n                    sql.Identifier(publication)\n                )\n            )\n\n    def cleanup_replication_slots(self):\n        for slot in self.replication_slots:\n            start = time.time()\n            while True:\n                try:\n                    self.sql(\n                        \"SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = %s\",\n                        (slot,),\n                    )\n                except psycopg.errors.ObjectInUse:\n                    if time.time() < start + 10:\n                        time.sleep(0.5)\n                        continue\n                    raise\n                break\n\n    def cleanup_subscriptions(self):\n        for subscription in self.subscriptions:\n            try:\n                self.sql(\n                    sql.SQL(\"ALTER SUBSCRIPTION {} DISABLE\").format(\n                        sql.Identifier(subscription)\n                    )\n                )\n            except psycopg.errors.UndefinedObject:\n                # Subscription didn't exist already\n                continue\n            self.sql(\n                sql.SQL(\"ALTER SUBSCRIPTION {} SET (slot_name = NONE)\").format(\n                    sql.Identifier(subscription)\n                )\n            )\n            self.sql(\n                sql.SQL(\"DROP SUBSCRIPTION {}\").format(sql.Identifier(subscription))\n            )\n\n    def lsn(self, mode):\n        \"\"\"Returns the lsn for the given mode\"\"\"\n        queries = {\n            \"insert\": \"SELECT pg_current_wal_insert_lsn()\",\n            \"flush\": \"SELECT pg_current_wal_flush_lsn()\",\n            \"write\": \"SELECT pg_current_wal_lsn()\",\n            \"receive\": \"SELECT pg_last_wal_receive_lsn()\",\n            \"replay\": \"SELECT pg_last_wal_replay_lsn()\",\n        }\n        return self.sql_value(queries[mode])\n\n    def wait_for_catchup(self, subscription_name, mode=\"replay\", target_lsn=None):\n        \"\"\"Waits until the subscription has caught up\"\"\"\n        if target_lsn is None:\n            target_lsn = self.lsn(\"write\")\n\n        # Before release 12 walreceiver just set the application name to\n        # \"walreceiver\"\n        self.poll_query_until(\n            sql.SQL(\n                \"\"\"\n            SELECT {} <= {} AND state = 'streaming'\n            FROM pg_catalog.pg_stat_replication\n            WHERE application_name IN ({}, 'walreceiver')\n            \"\"\"\n            ).format(target_lsn, sql.Identifier(f\"{mode}_lsn\"), subscription_name)\n        )\n\n    @contextmanager\n    def _enable_firewall(self):\n        \"\"\"Enables the firewall for the platform that you are running\n\n        Normally this should not be called directly, and instead drop_traffic\n        or reject_traffic should be used.\n        \"\"\"\n        fw_token = None\n        if BSD:\n            if MACOS:\n                command_stderr = sudo(\n                    \"pfctl -E\", stderr=subprocess.PIPE, text=True\n                ).stderr\n                match = re.search(r\"^Token : (\\d+)\", command_stderr, flags=re.MULTILINE)\n                assert match is not None\n                fw_token = match.group(1)\n            sudo(\n                'bash -c \"'\n                f\"echo 'anchor \\\\\\\"port_{self.port}\\\\\\\"'\"\n                f' | pfctl -a citus_test -f -\"'\n            )\n        try:\n            yield\n        finally:\n            if MACOS:\n                sudo(f\"pfctl -X {fw_token}\")\n\n    @contextmanager\n    def drop_traffic(self):\n        \"\"\"Drops all TCP packets to this query runner\"\"\"\n        with self._enable_firewall():\n            if LINUX:\n                sudo(\n                    \"iptables --append OUTPUT \"\n                    \"--protocol tcp \"\n                    f\"--destination {self.host} \"\n                    f\"--destination-port {self.port} \"\n                    \"--jump DROP \"\n                )\n            elif BSD:\n                sudo(\n                    \"bash -c '\"\n                    f'echo \"block drop out proto tcp from any to {self.host} port {self.port}\"'\n                    f\"| pfctl -a citus_test/port_{self.port} -f -'\"\n                )\n            else:\n                raise Exception(\"This OS cannot run this test\")\n            try:\n                yield\n            finally:\n                if LINUX:\n                    sudo(\n                        \"iptables --delete OUTPUT \"\n                        \"--protocol tcp \"\n                        f\"--destination {self.host} \"\n                        f\"--destination-port {self.port} \"\n                        \"--jump DROP \"\n                    )\n                elif BSD:\n                    sudo(f\"pfctl -a citus_test/port_{self.port} -F all\")\n\n    @contextmanager\n    def reject_traffic(self):\n        \"\"\"Rejects all traffic to this query runner with a TCP RST message\"\"\"\n        with self._enable_firewall():\n            if LINUX:\n                sudo(\n                    \"iptables --append OUTPUT \"\n                    \"--protocol tcp \"\n                    f\"--destination {self.host} \"\n                    f\"--destination-port {self.port} \"\n                    \"--jump REJECT \"\n                    \"--reject-with tcp-reset\"\n                )\n            elif BSD:\n                sudo(\n                    \"bash -c '\"\n                    f'echo \"block return-rst out out proto tcp from any to {self.host} port {self.port}\"'\n                    f\"| pfctl -a citus_test/port_{self.port} -f -'\"\n                )\n            else:\n                raise Exception(\"This OS cannot run this test\")\n            try:\n                yield\n            finally:\n                if LINUX:\n                    sudo(\n                        \"iptables --delete OUTPUT \"\n                        \"--protocol tcp \"\n                        f\"--destination {self.host} \"\n                        f\"--destination-port {self.port} \"\n                        \"--jump REJECT \"\n                        \"--reject-with tcp-reset\"\n                    )\n                elif BSD:\n                    sudo(f\"pfctl -a citus_test/port_{self.port} -F all\")\n\n\nclass CitusCluster(QueryRunner):\n    \"\"\"A class that represents a Citus cluster on this machine\n\n    The nodes in the cluster can be accessed directly using the coordinator,\n    workers, and nodes properties.\n\n    If it doesn't matter which of the nodes in the cluster is used to run a\n    query, then you can use the methods provided by QueryRunner directly on the\n    cluster. In that case a random node will be chosen to run your query.\n    \"\"\"\n\n    def __init__(self, basedir: Path, worker_count: int):\n        self.coordinator = Postgres(basedir / \"coordinator\")\n        self.workers = [Postgres(basedir / f\"worker{i}\") for i in range(worker_count)]\n        self.nodes = [self.coordinator] + self.workers\n        self._schema = None\n        self.failed_reset = False\n\n        parallel_run(Postgres.init_with_citus, self.nodes)\n        with self.coordinator.cur() as cur:\n            cur.execute(\n                \"SELECT pg_catalog.citus_set_coordinator_host(%s, %s)\",\n                (self.coordinator.host, self.coordinator.port),\n            )\n            for worker in self.workers:\n                cur.execute(\n                    \"SELECT pg_catalog.citus_add_node(%s, %s)\",\n                    (worker.host, worker.port),\n                )\n\n    def set_default_connection_options(self, options):\n        random.choice(self.nodes).set_default_connection_options(options)\n\n    @property\n    def schema(self):\n        return self._schema\n\n    @schema.setter\n    def schema(self, value):\n        self._schema = value\n        for node in self.nodes:\n            node.schema = value\n\n    def reset(self):\n        \"\"\"Resets any changes to Postgres settings from previous tests\"\"\"\n        parallel_run(Postgres.prepare_reset, self.nodes)\n        parallel_run(Postgres.restart, self.nodes)\n\n    def cleanup(self):\n        parallel_run(Postgres.cleanup, self.nodes)\n\n    def debug(self):\n        \"\"\"Print information to stdout to help with debugging your cluster\"\"\"\n        print(\"The nodes in this cluster and their connection strings are:\")\n        for node in self.nodes:\n            print(f\"{node.pgdata}:\\n   \", repr(node.make_conninfo()))\n        print(\"Press Enter to continue running the test...\")\n        input()\n"
  },
  {
    "path": "src/test/regress/citus_tests/config.py",
    "content": "import inspect\nimport os\nimport random\nimport socket\nimport threading\nfrom contextlib import closing\nfrom os.path import expanduser\n\nimport common\n\nCOORDINATOR_NAME = \"coordinator\"\nWORKER1 = \"worker1\"\nWORKER2 = \"worker2\"\n\nREGULAR_USER_NAME = \"regularuser\"\nSUPER_USER_NAME = \"postgres\"\n\nDATABASE_NAME = \"postgres\"\n\nARBITRARY_SCHEDULE_NAMES = [\n    \"create_schedule\",\n    \"sql_schedule\",\n    \"sql_base_schedule\",\n    \"postgres_schedule\",\n    \"single_shard_table_prep_schedule\",\n]\n\nBEFORE_PG_UPGRADE_WITH_COLUMNAR_SCHEDULE = \"./before_pg_upgrade_with_columnar_schedule\"\nBEFORE_PG_UPGRADE_WITHOUT_COLUMNAR_SCHEDULE = (\n    \"./before_pg_upgrade_without_columnar_schedule\"\n)\nAFTER_PG_UPGRADE_WITH_COLUMNAR_SCHEDULE = \"./after_pg_upgrade_with_columnar_schedule\"\nAFTER_PG_UPGRADE_WITHOUT_COLUMNAR_SCHEDULE = (\n    \"./after_pg_upgrade_without_columnar_schedule\"\n)\n\nCREATE_SCHEDULE = \"./create_schedule\"\nPOSTGRES_SCHEDULE = \"./postgres_schedule\"\nSINGLE_SHARD_PREP_SCHEDULE = \"./single_shard_table_prep_schedule\"\nSQL_SCHEDULE = \"./sql_schedule\"\nSQL_BASE_SCHEDULE = \"./sql_base_schedule\"\n\nAFTER_CITUS_UPGRADE_COORD_SCHEDULE = \"./after_citus_upgrade_coord_schedule\"\nBEFORE_CITUS_UPGRADE_COORD_SCHEDULE = \"./before_citus_upgrade_coord_schedule\"\nMIXED_BEFORE_CITUS_UPGRADE_SCHEDULE = \"./mixed_before_citus_upgrade_schedule\"\nMIXED_AFTER_CITUS_UPGRADE_SCHEDULE = \"./mixed_after_citus_upgrade_schedule\"\n\nCITUS_ARBITRARY_TEST_DIR = \"./tmp_citus_test\"\n\nMASTER = \"master\"\n# This should be updated when citus version changes\nMASTER_VERSION = \"15.0\"\n\nHOME = expanduser(\"~\")\n\n\nCITUS_VERSION_SQL = \"SELECT extversion FROM pg_extension WHERE extname = 'citus';\"\n\n\n# this is out of ephemeral port range for many systems hence\n# it is a lower change that it will conflict with \"in-use\" ports\nnext_port = 10200\n\n# ephemeral port start on many linux systems\nPORT_UPPER = 32768\n\nport_lock = threading.Lock()\n\n\ndef should_include_config(class_name):\n    if inspect.isclass(class_name) and issubclass(\n        class_name, CitusDefaultClusterConfig\n    ):\n        return True\n    return False\n\n\ndef find_free_port():\n    global next_port\n    with port_lock:\n        while next_port < PORT_UPPER:\n            with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:\n                try:\n                    s.bind((\"localhost\", next_port))\n                    port = next_port\n                    next_port += 1\n                    return port\n                except Exception:\n                    next_port += 1\n    # we couldn't find a port\n    raise Exception(\"Couldn't find a port to use\")\n\n\nclass NewInitCaller(type):\n    def __call__(cls, *args, **kwargs):\n        obj = type.__call__(cls, *args, **kwargs)\n        obj.post_init()\n        return obj\n\n\nclass CitusBaseClusterConfig(object, metaclass=NewInitCaller):\n    def __init__(self, arguments):\n        if \"--bindir\" in arguments:\n            self.bindir = arguments[\"--bindir\"]\n        self.pg_srcdir = arguments[\"--pgxsdir\"]\n        self.temp_dir = CITUS_ARBITRARY_TEST_DIR\n        self.worker_amount = 2\n        self.user = REGULAR_USER_NAME\n        self.dbname = DATABASE_NAME\n        self.is_mx = True\n        self.is_citus = True\n        self.all_null_dist_key = False\n        self.test_with_columnar = False\n        self.name = type(self).__name__\n        self.settings = {\n            \"shared_preload_libraries\": \"citus\",\n            \"log_error_verbosity\": \"terse\",\n            \"fsync\": False,\n            \"citus.node_conninfo\": \"sslmode=prefer\",\n            \"citus.enable_repartition_joins\": True,\n            \"citus.repartition_join_bucket_count_per_node\": 2,\n            \"citus.log_distributed_deadlock_detection\": True,\n            \"max_connections\": 1200,\n        }\n        self.new_settings = {}\n        self.env_variables = {}\n        self.skip_tests = []\n\n    def post_init(self):\n        self._init_node_name_ports()\n\n        self.datadir = self.temp_dir + \"/data\"\n        self.datadir += self.name\n        self.input_dir = self.datadir\n        self.output_dir = self.datadir\n        self.output_file = os.path.join(self.datadir, \"run.out\")\n        if self.worker_amount > 0:\n            self.chosen_random_worker_port = self.random_worker_port()\n        self.settings.update(self.new_settings)\n\n    def coordinator_port(self):\n        return self.node_name_to_ports[COORDINATOR_NAME]\n\n    def setup_steps(self):\n        pass\n\n    def random_worker_port(self):\n        return random.choice(self.worker_ports)\n\n    def random_port(self):\n        return random.choice(list(self.node_name_to_ports.values()))\n\n    def _init_node_name_ports(self):\n        self.node_name_to_ports = {}\n        self.worker_ports = []\n        cur_port = self._get_and_update_next_port()\n        self.node_name_to_ports[COORDINATOR_NAME] = cur_port\n        for i in range(self.worker_amount):\n            cur_port = self._get_and_update_next_port()\n            cur_worker_name = \"worker{}\".format(i)\n            self.node_name_to_ports[cur_worker_name] = cur_port\n            self.worker_ports.append(cur_port)\n\n    def _get_and_update_next_port(self):\n        if hasattr(self, \"fixed_port\"):\n            next_port = self.fixed_port\n            self.fixed_port += 1\n            return next_port\n        return find_free_port()\n\n\nclass CitusDefaultClusterConfig(CitusBaseClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        new_settings = {\n            \"client_min_messages\": \"WARNING\",\n            \"citus.sort_returning\": True,\n            \"citus.use_citus_managed_tables\": True,\n        }\n        self.settings.update(new_settings)\n        self.skip_tests = [\n            # Alter Table statement cannot be run from an arbitrary node so this test will fail\n            \"arbitrary_configs_alter_table_add_constraint_without_name_create\",\n            \"arbitrary_configs_alter_table_add_constraint_without_name\",\n        ]\n\n\nclass CitusUpgradeConfig(CitusBaseClusterConfig):\n    def __init__(self, arguments, pre_tar, post_tar):\n        super().__init__(arguments)\n        self.pre_tar_path = pre_tar\n        self.post_tar_path = post_tar\n        self.temp_dir = \"./tmp_citus_upgrade\"\n        self.new_settings = {\"citus.enable_version_checks\": \"false\"}\n        self.user = SUPER_USER_NAME\n        self.mixed_mode = arguments[\"--mixed\"]\n        self.minor_upgrade = arguments.get(\"--minor-upgrade\", False)\n        self.fixed_port = 57635\n\n\nclass PostgresConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.worker_amount = 0\n        self.is_citus = False\n        self.new_settings = {\n            \"citus.use_citus_managed_tables\": False,\n        }\n        self.skip_tests = [\n            \"nested_execution\",\n            # Alter Table statement cannot be run from an arbitrary node so this test will fail\n            \"arbitrary_configs_alter_table_add_constraint_without_name_create\",\n            \"arbitrary_configs_alter_table_add_constraint_without_name\",\n        ]\n\n\nclass AllSingleShardTableDefaultConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.all_null_dist_key = True\n        self.skip_tests += [\n            # One of the distributed functions created in \"function_create\"\n            # requires setting a distribution column, which cannot be the\n            # case with single shard tables.\n            \"function_create\",\n            \"functions\",\n            # In \"nested_execution\", one of the tests that query\n            # \"dist_query_single_shard\" table  acts differently when the table\n            # has a single shard. This is explained with a comment in the test.\n            \"nested_execution\",\n            \"merge_arbitrary\",\n        ]\n\n\nclass CitusSingleNodeClusterConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.worker_amount = 0\n\n    def setup_steps(self):\n        common.coordinator_should_haveshards(self.bindir, self.coordinator_port())\n\n\nclass CitusSingleWorkerClusterConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.worker_amount = 1\n\n\nclass CitusSuperUserDefaultClusterConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.user = SUPER_USER_NAME\n\n\nclass CitusThreeWorkersManyShardsClusterConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.new_settings = {\"citus.shard_count\": 191}\n        self.worker_amount = 3\n\n    def setup_steps(self):\n        common.coordinator_should_haveshards(self.bindir, self.coordinator_port())\n\n\nclass CitusSmallSharedPoolSizeConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.new_settings = {\n            \"citus.local_shared_pool_size\": 5,\n            \"citus.max_shared_pool_size\": 5,\n        }\n\n\nclass CitusSmallExecutorPoolSizeConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.new_settings = {\n            \"citus.max_adaptive_executor_pool_size\": 2,\n        }\n\n\nclass CitusSequentialExecutionConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.new_settings = {\n            \"citus.multi_shard_modify_mode\": \"sequential\",\n        }\n\n\nclass CitusCacheManyConnectionsConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.new_settings = {\n            \"citus.max_cached_conns_per_worker\": 4,\n        }\n\n\nclass CitusUnusualExecutorConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.new_settings = {\n            \"citus.max_adaptive_executor_pool_size\": 7,\n            \"citus.executor_slow_start_interval\": 1,\n            \"citus.prevent_incomplete_connection_establishment\": False,\n            \"citus.enable_cost_based_connection_establishment\": False,\n            \"citus.max_cached_connection_lifetime\": \"10ms\",\n            # https://github.com/citusdata/citus/issues/5345\n            # \"citus.force_max_query_parallelization\": \"on\",\n            \"citus.enable_binary_protocol\": False,\n            \"citus.local_table_join_policy\": \"prefer-distributed\",\n        }\n\n        # this setting does not necessarily need to be here\n        # could go any other test\n        self.env_variables = {\"PGAPPNAME\": \"test_app\"}\n\n\nclass CitusSmallCopyBuffersConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.new_settings = {\n            \"citus.copy_switchover_threshold\": \"1B\",\n            \"citus.local_copy_flush_threshold\": \"1B\",\n            \"citus.remote_copy_flush_threshold\": \"1B\",\n        }\n\n\nclass CitusUnusualQuerySettingsConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.new_settings = {\n            \"citus.use_citus_managed_tables\": False,\n            \"citus.task_assignment_policy\": \"first-replica\",\n            \"citus.enable_fast_path_router_planner\": False,\n            \"citus.enable_local_execution\": False,\n            \"citus.enable_single_hash_repartition_joins\": True,\n            \"citus.recover_2pc_interval\": \"1s\",\n            \"citus.remote_task_check_interval\": \"1ms\",\n            \"citus.values_materialization_threshold\": \"0\",\n        }\n\n        self.skip_tests = [\n            # Creating a reference table from a table referred to by a fk\n            # requires the table with the fk to be converted to a citus_local_table.\n            # As of c11, there is no way to do that through remote execution so this test\n            # will fail\n            \"arbitrary_configs_truncate_cascade_create\",\n            \"arbitrary_configs_truncate_cascade\",\n            # Alter Table statement cannot be run from an arbitrary node so this test will fail\n            \"arbitrary_configs_alter_table_add_constraint_without_name_create\",\n            \"arbitrary_configs_alter_table_add_constraint_without_name\",\n        ]\n\n\nclass CitusSingleNodeSingleShardClusterConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.worker_amount = 0\n        self.new_settings = {\"citus.shard_count\": 1}\n\n    def setup_steps(self):\n        common.coordinator_should_haveshards(self.bindir, self.coordinator_port())\n\n\nclass CitusShardReplicationFactorClusterConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.new_settings = {\"citus.shard_replication_factor\": 2}\n        self.skip_tests = [\n            # citus does not support foreign keys in distributed tables\n            # when citus.shard_replication_factor >= 2\n            \"arbitrary_configs_truncate_partition_create\",\n            \"arbitrary_configs_truncate_partition\",\n            # citus does not support modifying a partition when\n            # citus.shard_replication_factor >= 2\n            \"arbitrary_configs_truncate_cascade_create\",\n            \"arbitrary_configs_truncate_cascade\",\n            # citus does not support colocating functions with distributed tables when\n            # citus.shard_replication_factor >= 2\n            \"function_create\",\n            \"functions\",\n            # Alter Table statement cannot be run from an arbitrary node so this test will fail\n            \"arbitrary_configs_alter_table_add_constraint_without_name_create\",\n            \"arbitrary_configs_alter_table_add_constraint_without_name\",\n        ]\n\n\nclass CitusSingleShardClusterConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.new_settings = {\"citus.shard_count\": 1}\n\n\nclass CitusNonMxClusterConfig(CitusDefaultClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.is_mx = False\n        # citus does not support distributed functions\n        # when metadata is not synced\n        self.skip_tests = [\"function_create\", \"functions\", \"nested_execution\"]\n\n\nclass PGUpgradeConfig(CitusBaseClusterConfig):\n    def __init__(self, arguments):\n        super().__init__(arguments)\n        self.old_bindir = arguments[\"--old-bindir\"]\n        self.new_bindir = arguments[\"--new-bindir\"]\n        self.temp_dir = \"./tmp_upgrade\"\n        self.old_datadir = self.temp_dir + \"/oldData\"\n        self.new_datadir = self.temp_dir + \"/newData\"\n        self.user = SUPER_USER_NAME\n        self.test_with_columnar = arguments[\"--test-with-columnar\"]\n"
  },
  {
    "path": "src/test/regress/citus_tests/print_test_names.py",
    "content": "#!/usr/bin/env python3\n\nimport config as cfg\n\n\ndef read_config_names():\n    config_names = []\n    # We fill the configs from all of the possible classes in config.py so that if we add a new config,\n    # we don't need to add it here. And this avoids the problem where we forget to add it here\n    for x in cfg.__dict__.values():\n        if cfg.should_include_config(x):\n            config_names.append(x.__name__)\n    return config_names\n\n\ndef print_config_names():\n    config_names = read_config_names()\n    for config_name in config_names:\n        print(config_name)\n\n\nif __name__ == \"__main__\":\n    print_config_names()\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/.gitignore",
    "content": "__pycache__\n*-env\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/README.md",
    "content": "## Goal of the Tool\nTool supports a simple syntax to be useful to generate queries with different join orders. Main motivation for me to create the tool was to compare results of the generated queries for different [Citus](https://github.com/citusdata/citus) tables and Postgres tables. That is why we support a basic syntax for now. It can be extended to support different queries.\n\n## Query Generator for Postgres\nTool generates SELECT queries, whose depth can be configured, with different join orders. It also generates DDLs required for query execution.\nYou can also tweak configuration parameters for data inserting command generation.\n\n## How to Run Citus Join Verification?\nYou can verify if Citus breaks any default PG join behaviour via `citus_compare_dist_local_joins.sh`. It creates\ntables specified in config. Then, it runs generated queries on those tables and saves the results into `out/dist_queries.out`.\nAfter running those queries for Citus tables, it creates PG tables with the same names as previous run, executes the same\nqueries, and saves the results into `out/local_queries.out`. In final step, it generates diff between local and distributed results.\nYou can see the contents of `out/local_dist_diffs` to see if there is any Citus unsupported query.\n\n1. Create a Citus local cluster with 2 workers by using [citus_dev](https://github.com/citusdata/tools/tree/develop/citus_dev) tool\n(Note: make sure you do not configure psql via .psqlrc file as it would fail the test.)\n```bash\ncitus_dev make testCluster --destroy\n```\n2. Run the test,\n```bash\ncd src/test/regress/citus_tests/query_generator/bin\nbash citus_compare_dist_local_joins.sh <username> <dbname> <coordinator_port> Optional:<seed>\n```\n3. See the diff content in `src/test/regress/citus_tests/query_generator/out/local_dist_diffs`\n\nNote: `seed` can be used to reproduce a run of Citus test by generating the same queries and results via the given seed.\n\n### Configuration\nYou can configure 3 different parts:\n\n- [DDL Configuration](#ddl-configuration)\n- [Data Insertion Configuration](#data-insertion-configuration)\n- [Query Configuration](#query-configuration)\n\n## DDL Configuration\nTool generates related ddl commands before generating queries.\n\nSchema for DDL configuration:\n```yaml\nddlOutFile: <string>\ncommonColName: <string>\ntargetTables: <Table[]>\n  - Table:\n      name: <string>\n      citusType: <CitusType>\n      maxAllowedUseOnQuery: <int>\n      rowCount: <int>\n      nullRate: <float>\n      duplicateRate: <float>\n      columns: <Column[]>\n        - Column:\n          name: <string>\n          type: <string>\n      distinctCopyCount: <int>\n```\n\nExplanation:\n```yaml\nddlOutFile: \"file to write generated DDL commands\"\ncommonColName: \"name of the column that will be used as distribution column, filter column in restrictions and target column in selections\"\ntargetTables: \"array of tables that will be used in generated queries\"\n  - Table:\n      name: \"name prefix of table\"\n      citusType: \"citus type of table\"\n      maxAllowedUseOnQuery: \"limits how many times table can appear in query\"\n      rowCount: \"total # of rows that will be inserted into table\"\n      nullRate: \"percentage of null rows in rowCount that will be inserted into table\"\n      duplicateRate: \"percentage of duplicates in rowCount that will be inserted into table\"\n      columns: \"array of columns in table\"\n        - Column:\n          name: \"name of column\"\n          type: \"name of data type of column(only support 'int' now)\"\n      distinctCopyCount: \"how many tables with the same configuration we should create(only by changing full name, still using the same name prefix)\"\n```\n\n\n## Data Insertion Configuration\nTool generates data insertion commands if you want tables with filled data. You can configure total number of rows, what percentage of them should\nbe null and what percentage of them should be duplicated. For related configuration see Table schema at [DDL Configuration](#ddl-configuration). You\ncan also configure range of the randomly generated data. See `dataRange` at [Query Configuration](#query-configuration).\n\n## Query Configuration\nAfter generation of ddls and data insertion commands, the tool generates queries.\n\nSchema for Query configuration:\n```yaml\nqueryCount: <int>\nqueryOutFile: <string>\nsemiAntiJoin: <bool>\ncartesianProduct: <bool>\nlimit: <bool>\norderby: <bool>\nforceOrderbyWithLimit: <bool>\nuseAvgAtTopLevelTarget: <bool>\ndataRange:\n  from: <int>\n  to: <int>\nfilterRange:\n  from: <int>\n  to: <int>\nlimitRange:\n  from: <int>\n  to: <int>\ntargetRteCount: <int>\ntargetCteCount: <int>\ntargetCteRteCount: <int>\ntargetJoinTypes: <JoinType[]>\ntargetRteTypes: <RteType[]>\ntargetRestrictOps: <RestrictOp[]>\n```\n\nExplanation:\n```yaml\nqueryCount: \"number of queries to generate\"\nqueryOutFile: \"file to write generated queries\"\nsemiAntiJoin: \"should we support semi joins (WHERE col IN (Subquery))\"\ncartesianProduct: \"should we support cartesian joins\"\nlimit: \"should we support limit clause\"\norderby: \"should we support order by clause\"\nforceOrderbyWithLimit: \"should we force order by when we use limit\"\nuseAvgAtTopLevelTarget: \"should we make top level query as select avg() from (subquery)\"\ndataRange:\n  from: \"starting boundary for data generation\"\n  to: \"end boundary for data generation\"\nfilterRange:\n  from: \"starting boundary for restriction clause\"\n  to: \"end boundary for restriction clause\"\nlimitRange:\n  from: \"starting boundary for limit clause\"\n  to: \"end boundary for data limit clause\"\ntargetRteCount: \"limits how many rtes should exist in non-cte part of the query\"\ntargetCteCount: \"limits how many ctes should exist in query\"\ntargetCteRteCount: \"limits how many rtes should exist in cte part of the query\"\ntargetJoinTypes: \"supported join types\"\ntargetRteTypes: \"supported rte types\"\ntargetRestrictOps: \"supported restrict ops\"\n```\n\n## Misc Configuration\nTool has some configuration options which does not suit above 3 parts.\n\nSchema for misc configuration:\n```yaml\ninteractiveMode: <bool>\n```\n\nExplanation:\n```yaml\ninteractiveMode: \"when true, interactively prints generated ddls and queries. Otherwise, it writes them to configured files.\"\n```\n\n## Supported Operations\nIt uses `commonColName` for any kind of target selection required for any supported query clause.\n\n### Column Type Support\nTool currently supports only int data type, but plans to support other basic types.\n\n### Join Support\nTool supports following joins:\n```yaml\ntargetJoinTypes:\n  - INNER\n  - LEFT\n  - RIGHT\n  - FULL\n```\n\n### Citus Table Support\nTool supports following citus table types:\n```yaml\ntargetTables:\n  - Table:\n    ...\n    citusType: <one of (DISTRIBUTED || REFERENCE || POSTGRES)>\n    ...\n```\n\n### Restrict Operation Support\nTool supports following restrict operations:\n```yaml\ntargetRestrictOps:\n  - LT\n  - GT\n  - EQ\n```\n\n### Rte Support\nTool supports following rtes:\n```yaml\ntargetRteTypes:\n  - RELATION\n  - SUBQUERY\n  - CTE\n  - VALUES\n```\n\n## How to Generate Queries?\nYou have 2 different options.\n\n- [Interactive Mode](#interactive-mode)\n- [File Mode](#file-mode)\n\n### Interactive Mode\nIn this mode, you will be prompted to continue generating a query. When you hit to `Enter`, it will continue generating them.\nYou will need to hit to `x` to exit.\n\n1. Configure `interactiveMode: true` in config.yml,\n2. Run the command shown below\n```bash\ncd src/test/regress/citus_tests/query_generator\npython generate_queries.py\n```\n\n### File Mode\nIn this mode, generated ddls and queries will be written into the files configured in [config.yml](./config/config.yaml).\n\n1. Configure `interactiveMode: false`,\n2. Configure `queryCount: <total_query>`,\n3. Configure `queryOutFile: <query_file_path>` and `ddlOutFile: <ddlfile_path>`\n4. Run the command shown below\n```bash\ncd src/test/regress/citus_tests/query_generator\npython generate_queries.py\n```\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/bin/citus_compare_dist_local_joins.sh",
    "content": "#!/bin/bash\n\n# make bash behave\nset -euo pipefail\n\npsql_user=$1\npsql_db=$2\npsql_port=$3\nseed=${4:-\"\"}\n\nrunDDLs()\n{\n    # run ddls\n    psql -U \"${psql_user}\" -d \"${psql_db}\" -p \"${psql_port}\" -f \"${out_folder}\"/ddls.sql > /dev/null\n}\n\nrunUndistributeTables()\n{\n    undistribute_all_tables_command='SELECT undistribute_table(logicalrelid) FROM pg_dist_partition;'\n    # run undistribute all tables\n    psql -U \"${psql_user}\" -d \"${psql_db}\" -p \"${psql_port}\" -c \"${undistribute_all_tables_command}\" > /dev/null\n}\n\nrunQueries()\n{\n    out_filename=$1\n\n    # run dmls\n    # echo queries and comments for query tracing\n    psql -U \"${psql_user}\" -d \"${psql_db}\" -p \"${psql_port}\" \\\n        --echo-all \\\n        -f \"${out_folder}\"/queries.sql > \"${out_filename}\" 2>&1\n}\n\nshowDiffs()\n{\n    pushd . && cd \"${script_folder}\" && python3 diff-checker.py && popd\n}\n\n# run query generator and let it create output ddls and queries\nscript_folder=$(dirname \"$0\")\nout_folder=\"${script_folder}\"/../out\npushd . && cd \"${script_folder}\"/.. && python3 generate_queries.py --seed=\"${seed}\" && popd\n\n# remove result files if exists\nrm -rf \"${out_folder}\"/dist_queries.out \"${out_folder}\"/local_queries.out\n\n# run ddls\nrunDDLs\n\n# runs dmls for distributed tables\nrunQueries \"${out_folder}\"/dist_queries.out\n\n# undistribute all dist tables\nrunUndistributeTables\n\n# runs the same dmls for pg local tables\nrunQueries \"${out_folder}\"/local_queries.out\n\n# see diffs in results\nshowDiffs\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/bin/diff-checker.py",
    "content": "import os\nimport re\nimport subprocess\nimport sys\n\n\ndef createPatternForFailedQueryBlock(acceptableErrors):\n    totalAcceptableOrders = len(acceptableErrors)\n\n    failedQueryBlockPattern = \"-- queryId: [0-9]+\\n.*\\npsql:.*(?:\"\n    for acceptableErrorIdx in range(totalAcceptableOrders):\n        failedQueryBlockPattern += acceptableErrors[acceptableErrorIdx]\n        if acceptableErrorIdx < totalAcceptableOrders - 1:\n            failedQueryBlockPattern += \"|\"\n    failedQueryBlockPattern += \")\"\n\n    return failedQueryBlockPattern\n\n\ndef findFailedQueriesFromFile(queryOutFile, acceptableErrors):\n    failedQueryIds = []\n    outFileContent = \"\"\n    failedQueryBlockPattern = createPatternForFailedQueryBlock(acceptableErrors)\n    queryIdPattern = \"queryId: ([0-9]+)\"\n    with open(queryOutFile, \"r\") as f:\n        outFileContent = f.read()\n        failedQueryContents = re.findall(failedQueryBlockPattern, outFileContent)\n        failedQueryIds = [\n            re.search(queryIdPattern, failedQueryContent)[1]\n            for failedQueryContent in failedQueryContents\n        ]\n\n    return failedQueryIds\n\n\ndef removeFailedQueryOutputFromFile(outFile, failedQueryIds):\n    if len(failedQueryIds) == 0:\n        return\n\n    distOutFileContentAsLines = []\n    with open(outFile, \"r\") as f:\n        distOutFileContentAsLines = f.readlines()\n\n    with open(outFile, \"w\") as f:\n        clear = False\n        nextIdx = 0\n        nextQueryIdToDelete = failedQueryIds[nextIdx]\n        queryIdPattern = \"queryId: ([0-9]+)\"\n\n        for line in distOutFileContentAsLines:\n            matched = re.search(queryIdPattern, line)\n            # founded line which contains query id\n            if matched:\n                # query id matches with the next failed query's id\n                if nextQueryIdToDelete == matched[1]:\n                    # clear lines until we find succesfull query\n                    clear = True\n                    nextIdx += 1\n                    if nextIdx < len(failedQueryIds):\n                        nextQueryIdToDelete = failedQueryIds[nextIdx]\n                else:\n                    # we found successfull query\n                    clear = False\n\n            if not clear:\n                f.write(line)\n    return\n\n\ndef removeFailedQueryOutputFromFiles(distQueryOutFile, localQueryOutFile):\n    # remove the failed distributed query from both local and distributed query files to prevent diff for acceptable errors\n    # some of generated queries fails with below errors due to https://github.com/citusdata/citus/issues/6653, we skip those until we support them\n    acceptableErrors = [\n        \"ERROR:  complex joins are only supported when all distributed tables are co-located and joined on their distribution columns\",\n        \"ERROR:  recursive complex joins are only supported when all distributed tables are co-located and joined on their distribution columns\",\n    ]\n    failedDistQueryIds = findFailedQueriesFromFile(distQueryOutFile, acceptableErrors)\n    removeFailedQueryOutputFromFile(distQueryOutFile, failedDistQueryIds)\n    removeFailedQueryOutputFromFile(localQueryOutFile, failedDistQueryIds)\n    return\n\n\ndef showDiffs(distQueryOutFile, localQueryOutFile, diffFile):\n    exitCode = 1\n    with open(diffFile, \"w\") as f:\n        diffCommand = \"diff -u {} {}\".format(localQueryOutFile, distQueryOutFile)\n        process = subprocess.Popen(diffCommand.split(), stdout=f, stderr=f, shell=False)\n        process.wait()\n        exitCode = process.returncode\n\n    print(\"diff exit {}\".format(exitCode))\n    return exitCode\n\n\ndef exitIfAnyLocalQueryFailed(localQueryOutFile):\n    allErrors = [\"ERROR:\"]\n    failedLocalQueryIds = findFailedQueriesFromFile(localQueryOutFile, allErrors)\n    assert (\n        len(failedLocalQueryIds) == 0\n    ), \"\"\"There might be an internal error related to query generator or\n                                              we might find a Postgres bug. Check local_queries.out to see the error.\"\"\"\n    return\n\n\nif __name__ == \"__main__\":\n    scriptDirPath = os.path.dirname(os.path.abspath(__file__))\n    outFolderPath = scriptDirPath + \"/../out\"\n    localQueryOutFile = \"{}/local_queries.out\".format(outFolderPath)\n    distQueryOutFile = \"{}/dist_queries.out\".format(outFolderPath)\n    diffFile = \"{}/local_dist.diffs\".format(outFolderPath)\n\n    # exit if we have any error from local queries\n    exitIfAnyLocalQueryFailed(localQueryOutFile)\n\n    # find failed queries from distQueryOutFile and then remove failed queries and their results from\n    # both distQueryOutFile and localQueryOutFile\n    removeFailedQueryOutputFromFiles(distQueryOutFile, localQueryOutFile)\n\n    # show diffs in unified format\n    exitCode = showDiffs(distQueryOutFile, localQueryOutFile, diffFile)\n\n    sys.exit(exitCode)\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/bin/run_query_compare_test.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"query_gen_test\nUsage:\n    run_query_compare_test --bindir=<bindir> --pgxsdir=<pgxsdir> --seed=<seed>\n\nOptions:\n    --bindir=<bindir>                      PostgreSQL executable directory(ex: '~/.pgenv/pgsql-10.4/bin')\n    --pgxsdir=<pgxsdir>           \t       Path to the PGXS directory(ex: ~/.pgenv/src/postgresql-11.3)\n    --seed=<seed>                          Seed number used by the query generator.(ex: 123)\n\"\"\"\n\nimport os\nimport subprocess\nimport sys\n\nfrom docopt import docopt\n\n# https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time/14132912#14132912\nsys.path.append(\n    os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n)\n\n\n# ignore E402 because these imports require addition to path\nimport common  # noqa: E402\n\nimport config as cfg  # noqa: E402\n\n\ndef run_test(config, seed):\n    # start cluster\n    common.initialize_temp_dir(cfg.CITUS_ARBITRARY_TEST_DIR)\n    common.initialize_citus_cluster(\n        config.bindir, config.datadir, config.settings, config\n    )\n\n    # run test\n    scriptDirPath = os.path.dirname(os.path.abspath(__file__))\n    testRunCommand = \"bash {}/citus_compare_dist_local_joins.sh {} {} {} {}\".format(\n        scriptDirPath, config.user, config.dbname, config.coordinator_port(), seed\n    )\n    process = subprocess.Popen(\n        testRunCommand.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE\n    )\n    stdout, stderr = process.communicate()\n\n    # stop cluster\n    common.stop_databases(\n        config.bindir, config.datadir, config.node_name_to_ports, config.name\n    )\n\n    print(stdout)\n    print(stderr)\n    print(process.returncode)\n    sys.exit(process.returncode)\n\n\nif __name__ == \"__main__\":\n    arguments = docopt(__doc__, version=\"run_query_compare_test\")\n    citusClusterConfig = cfg.CitusSuperUserDefaultClusterConfig(arguments)\n\n    seed = \"\"\n    if \"--seed\" in arguments and arguments[\"--seed\"] != \"\":\n        seed = arguments[\"--seed\"]\n    run_test(citusClusterConfig, seed)\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/config/config.py",
    "content": "import copy\nimport os\n\nimport yaml\nfrom config.config_parser import (\n    parseJoinTypeArray,\n    parseRange,\n    parseRestrictOpArray,\n    parseRteTypeArray,\n    parseTableArray,\n)\nfrom node_defs import CitusType\n\n\nclass Config:\n    def __init__(self):\n        configObj = Config.parseConfigFile(\n            f\"{os.path.dirname(os.path.abspath(__file__))}/config.yaml\"\n        )\n\n        self.targetTables = _distinctCopyTables(\n            parseTableArray(configObj[\"targetTables\"])\n        )\n        self.targetJoinTypes = parseJoinTypeArray(configObj[\"targetJoinTypes\"])\n        self.targetRteTypes = parseRteTypeArray(configObj[\"targetRteTypes\"])\n        self.targetRestrictOps = parseRestrictOpArray(configObj[\"targetRestrictOps\"])\n        self.commonColName = configObj[\"commonColName\"]\n        self.targetRteCount = configObj[\"targetRteCount\"]\n        self.targetCteCount = configObj[\"targetCteCount\"]\n        self.targetCteRteCount = configObj[\"targetCteRteCount\"]\n        self.semiAntiJoin = configObj[\"semiAntiJoin\"]\n        self.cartesianProduct = configObj[\"cartesianProduct\"]\n        self.limit = configObj[\"limit\"]\n        self.orderby = configObj[\"orderby\"]\n        self.forceOrderbyWithLimit = configObj[\"forceOrderbyWithLimit\"]\n        self.useAvgAtTopLevelTarget = configObj[\"useAvgAtTopLevelTarget\"]\n        self.interactiveMode = configObj[\"interactiveMode\"]\n        self.queryOutFile = configObj[\"queryOutFile\"]\n        self.ddlOutFile = configObj[\"ddlOutFile\"]\n        self.queryCount = configObj[\"queryCount\"]\n        self.dataRange = parseRange(configObj[\"dataRange\"])\n        self.filterRange = parseRange(configObj[\"filterRange\"])\n        self.limitRange = parseRange(configObj[\"limitRange\"])\n        self._ensureRteLimitsAreSane()\n        # print(self)\n\n    def __repr__(self):\n        rep = \"targetRteCount: {}\\n\".format(self.targetRteCount)\n        rep += \"targetCteCount: {}\\n\".format(self.targetCteCount)\n        rep += \"targetCteRteCount: {}\\n\".format(self.targetCteRteCount)\n\n        rep += \"targetRteTypes:\\n\"\n        for rteType in self.targetRteTypes:\n            rep += \"\\t{}\\n\".format(rteType)\n\n        rep += \"targetJoinTypes:\\n\"\n        for joinType in self.targetJoinTypes:\n            rep += \"\\t{}\\n\".format(joinType)\n\n        rep += \"restrictOps:\\n\"\n        for restrictOp in self.targetRestrictOps:\n            rep += \"\\t{}\\n\".format(restrictOp)\n\n        return rep\n\n    def _ensureRteLimitsAreSane(self):\n        totalAllowedUseForAllTables = 0\n        for table in self.targetTables:\n            totalAllowedUseForAllTables += table.maxAllowedUseOnQuery\n        assert (\n            totalAllowedUseForAllTables >= self.targetRteCount\n        ), \"\"\"targetRteCount cannot be greater than sum of maxAllowedUseOnQuery for all tables.\n              Set targetRteCount to a lower value or increase maxAllowedUseOnQuery for tables at config.yml.\"\"\"\n\n    @staticmethod\n    def parseConfigFile(path):\n        try:\n            with open(path, \"r\") as configFile:\n                return yaml.load(configFile, yaml.Loader)\n        except IOError as e:\n            print(f\"I/O error({0}): {1}\".format(e.errno, e.strerror))\n            raise BaseException(\"cannot parse config.yaml\")\n        except Exception:\n            print(\"Unexpected error while parsing config.yml.\")\n\n\n_config = None\n\n\ndef resetConfig():\n    global _config\n    _config = Config()\n\n\ndef getConfig():\n    return _config\n\n\ndef getAllTableNames():\n    \"\"\"returns table names from target tables given at config\"\"\"\n    tables = getConfig().targetTables\n    tableNames = [table.name for table in tables]\n    return tableNames\n\n\ndef getMaxAllowedCountForTable(tableName):\n    tables = getConfig().targetTables\n    filtered = filter(lambda el: el.name == tableName, tables)\n    filtered = list(filtered)\n    assert len(filtered) == 1\n    return filtered[0].maxAllowedUseOnQuery\n\n\ndef isTableDistributed(table):\n    return table.citusType == CitusType.DISTRIBUTED\n\n\ndef isTableReference(table):\n    return table.citusType == CitusType.REFERENCE\n\n\ndef _distinctCopyTables(tables):\n    distinctCopyTables = []\n    for table in tables:\n        distinctCopyCount = table.distinctCopyCount\n        for tblIdx in range(1, distinctCopyCount):\n            distinctCopyTable = copy.deepcopy(table)\n            distinctCopyTable.name += str(tblIdx)\n            distinctCopyTables.append(distinctCopyTable)\n        table.name += \"0\"\n    tables.extend(distinctCopyTables)\n    return tables\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/config/config.yaml",
    "content": "interactiveMode: false\nqueryCount: 250\nqueryOutFile: queries.sql\nddlOutFile: ddls.sql\nsemiAntiJoin: true\ncartesianProduct: false\nlimit: true\norderby: true\nforceOrderbyWithLimit: true\nuseAvgAtTopLevelTarget: true\ndataRange:\n  from: 0\n  to: 10\nfilterRange:\n  from: 0\n  to: 10\nlimitRange:\n  from: 0\n  to: 10\ntargetRteCount: 40\ntargetCteCount: 3\ntargetCteRteCount: 2\n\ncommonColName: id\n\ntargetTables:\n  - Table:\n      name: dist\n      citusType: DISTRIBUTED\n      maxAllowedUseOnQuery: 10\n      rowCount: 10\n      nullRate: 0.1\n      duplicateRate: 0.1\n      columns:\n        - Column:\n          name: id\n          type: int\n      distinctCopyCount: 2\n  - Table:\n      name: ref\n      citusType: REFERENCE\n      maxAllowedUseOnQuery: 10\n      rowCount: 10\n      nullRate: 0.1\n      duplicateRate: 0.1\n      columns:\n        - Column:\n          name: id\n          type: int\n      distinctCopyCount: 2\n\ntargetJoinTypes:\n  - INNER\n  - LEFT\n  - RIGHT\n  - FULL\n\ntargetRteTypes:\n  - RELATION\n  - SUBQUERY\n  - CTE\n\ntargetRestrictOps:\n  - LT\n  - GT\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/config/config_parser.py",
    "content": "from node_defs import CitusType, Column, JoinType, RestrictOp, RTEType, Table\n\n\ndef parseJoinType(joinTypeText):\n    return JoinType[joinTypeText]\n\n\ndef parseJoinTypeArray(joinTypeTexts):\n    joinTypes = []\n    for joinTypeText in joinTypeTexts:\n        joinType = parseJoinType(joinTypeText)\n        joinTypes.append(joinType)\n    return joinTypes\n\n\ndef parseRteType(rteTypeText):\n    return RTEType[rteTypeText]\n\n\ndef parseRteTypeArray(rteTypeTexts):\n    rteTypes = []\n    for rteTypeText in rteTypeTexts:\n        rteType = parseRteType(rteTypeText)\n        rteTypes.append(rteType)\n    return rteTypes\n\n\ndef parseRestrictOp(restrictOpText):\n    return RestrictOp[restrictOpText]\n\n\ndef parseRestrictOpArray(restrictOpTexts):\n    restrictOps = []\n    for restrictOpText in restrictOpTexts:\n        restrictOp = parseRestrictOp(restrictOpText)\n        restrictOps.append(restrictOp)\n    return restrictOps\n\n\ndef parseTable(targetTableDict):\n    name = targetTableDict[\"name\"]\n    citusType = CitusType[targetTableDict[\"citusType\"]]\n    maxAllowedUseOnQuery = targetTableDict[\"maxAllowedUseOnQuery\"]\n    rowCount = targetTableDict[\"rowCount\"]\n    nullRate = targetTableDict[\"nullRate\"]\n    duplicateRate = targetTableDict[\"duplicateRate\"]\n    columns = []\n    for columnDict in targetTableDict[\"columns\"]:\n        col = parseColumn(columnDict)\n        columns.append(col)\n    distinctCopyCount = targetTableDict[\"distinctCopyCount\"]\n    return Table(\n        name,\n        citusType,\n        maxAllowedUseOnQuery,\n        rowCount,\n        nullRate,\n        duplicateRate,\n        columns,\n        distinctCopyCount,\n    )\n\n\ndef parseTableArray(targetTableDicts):\n    tables = []\n    for targetTableDict in targetTableDicts:\n        table = parseTable(targetTableDict[\"Table\"])\n        tables.append(table)\n    return tables\n\n\ndef parseColumn(targetColumnDict):\n    name = targetColumnDict[\"name\"]\n    type = targetColumnDict[\"type\"]\n    return Column(name, type)\n\n\ndef parseRange(rangeDict):\n    fromVal = rangeDict[\"from\"]\n    toVal = rangeDict[\"to\"]\n    return (fromVal, toVal)\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/data_gen.py",
    "content": "from node_defs import CitusType\n\nfrom config.config import getConfig\n\n\ndef getTableData():\n    dataGenerationSql = \"\"\n\n    tableIdx = 1\n    (fromVal, toVal) = getConfig().dataRange\n    tables = getConfig().targetTables\n    for table in tables:\n        # generate base rows\n        dataGenerationSql += _genOverlappingData(table.name, fromVal, table.rowCount)\n        dataGenerationSql += \"\\n\"\n        dataGenerationSql += _genNonOverlappingData(table.name, toVal, tableIdx)\n        dataGenerationSql += \"\\n\"\n\n        # generate null rows\n        if not table.citusType == CitusType.DISTRIBUTED:\n            targetNullRows = int(table.rowCount * table.nullRate)\n            dataGenerationSql += _genNullData(table.name, targetNullRows)\n            dataGenerationSql += \"\\n\"\n\n        # generate duplicate rows\n        targetDuplicateRows = int(table.rowCount * table.duplicateRate)\n        dataGenerationSql += _genDupData(table.name, targetDuplicateRows)\n        dataGenerationSql += \"\\n\\n\"\n        tableIdx += 1\n    return dataGenerationSql\n\n\ndef _genOverlappingData(tableName, startVal, rowCount):\n    \"\"\"returns string to fill table with [startVal,startVal+rowCount] range of integers\"\"\"\n    dataGenerationSql = \"\"\n    dataGenerationSql += \"INSERT INTO \" + tableName\n    dataGenerationSql += (\n        \" SELECT i FROM generate_series(\"\n        + str(startVal)\n        + \",\"\n        + str(startVal + rowCount)\n        + \") i;\"\n    )\n    return dataGenerationSql\n\n\ndef _genNullData(tableName, nullCount):\n    \"\"\"returns string to fill table with NULLs\"\"\"\n    dataGenerationSql = \"\"\n    dataGenerationSql += \"INSERT INTO \" + tableName\n    dataGenerationSql += (\n        \" SELECT NULL FROM generate_series(0,\" + str(nullCount) + \") i;\"\n    )\n    return dataGenerationSql\n\n\ndef _genDupData(tableName, dupRowCount):\n    \"\"\"returns string to fill table with duplicate integers which are fetched from given table\"\"\"\n    dataGenerationSql = \"\"\n    dataGenerationSql += \"INSERT INTO \" + tableName\n    dataGenerationSql += (\n        \" SELECT * FROM \"\n        + tableName\n        + \" ORDER BY \"\n        + getConfig().commonColName\n        + \" LIMIT \"\n        + str(dupRowCount)\n        + \";\"\n    )\n    return dataGenerationSql\n\n\ndef _genNonOverlappingData(tableName, startVal, tableIdx):\n    \"\"\"returns string to fill table with different integers for given table\"\"\"\n    startVal = startVal + tableIdx * 20\n    endVal = startVal + 20\n    dataGenerationSql = \"\"\n    dataGenerationSql += \"INSERT INTO \" + tableName\n    dataGenerationSql += (\n        \" SELECT i FROM generate_series(\" + str(startVal) + \",\" + str(endVal) + \") i;\"\n    )\n    return dataGenerationSql\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/ddl_gen.py",
    "content": "from config.config import getConfig, isTableDistributed, isTableReference\n\n\ndef getTableDDLs():\n    ddls = \"\"\n    tables = getConfig().targetTables\n    for table in tables:\n        ddls += _genTableDDL(table)\n        ddls += \"\\n\"\n    return ddls\n\n\ndef _genTableDDL(table):\n    ddl = \"\"\n    ddl += \"DROP TABLE IF EXISTS \" + table.name + \";\"\n    ddl += \"\\n\"\n\n    ddl += \"CREATE TABLE \" + table.name + \"(\"\n    for column in table.columns[:-1]:\n        ddl += _genColumnDDL(column)\n        ddl += \",\\n\"\n    if len(table.columns) > 0:\n        ddl += _genColumnDDL(table.columns[-1])\n    ddl += \");\\n\"\n\n    if isTableDistributed(table):\n        ddl += (\n            \"SELECT create_distributed_table(\"\n            + \"'\"\n            + table.name\n            + \"','\"\n            + getConfig().commonColName\n            + \"'\"\n            + \");\"\n        )\n        ddl += \"\\n\"\n    elif isTableReference(table):\n        ddl += \"SELECT create_reference_table(\" + \"'\" + table.name + \"'\" + \");\"\n        ddl += \"\\n\"\n    return ddl\n\n\ndef _genColumnDDL(column):\n    ddl = \"\"\n    ddl += column.name\n    ddl += \" \"\n    ddl += column.type\n    return ddl\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/generate_queries.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"generate_queries\nUsage:\n    generate_queries --seed=<seed>\n\nOptions:\n    --seed=<seed>                          Seed number used by the query generator.(ex: 123)\n\"\"\"\n\nimport os\nimport random\nimport signal\nimport sys\n\nfrom data_gen import getTableData\nfrom ddl_gen import getTableDDLs\nfrom docopt import docopt\nfrom query_gen import newQuery\nfrom random_selections import currentMilliSecs\n\nfrom config.config import getConfig, resetConfig\n\n\ndef _signal_handler(sig, frame):\n    sys.exit(0)\n\n\ndef _interactiveMode(ddls, data):\n    print(ddls)\n    print(data)\n\n    while True:\n        res = input(\"Press x to exit or Enter to generate more\")\n        if res.lower() == \"x\":\n            print(\"Exit from query generation mode!\")\n            sys.exit(0)\n\n        query = newQuery()\n        print(query)\n\n        resetConfig()\n\n\ndef _fileMode(ddls, data):\n    ddlFileName = (\n        f\"{os.path.dirname(os.path.abspath(__file__))}/out/{getConfig().ddlOutFile}\"\n    )\n    with open(ddlFileName, \"w\") as ddlFile:\n        ddlFile.writelines([ddls, data])\n\n    queryCount = getConfig().queryCount\n    fileName = (\n        f\"{os.path.dirname(os.path.abspath(__file__))}/out/{getConfig().queryOutFile}\"\n    )\n    with open(fileName, \"w\") as f:\n        # enable repartition joins due to https://github.com/citusdata/citus/issues/6865\n        enableRepartitionJoinCommand = \"SET citus.enable_repartition_joins TO on;\\n\"\n        queryLines = [enableRepartitionJoinCommand]\n        queryId = 1\n        for _ in range(queryCount):\n            query = newQuery()\n\n            queryLine = \"-- queryId: \" + str(queryId) + \"\\n\"\n            queryLine += query + \"\\n\\n\"\n\n            queryLines.append(queryLine)\n            queryId += 1\n        f.writelines(queryLines)\n\n\nif __name__ == \"__main__\":\n    signal.signal(signal.SIGINT, _signal_handler)\n\n    arguments = docopt(__doc__, version=\"generate_queries\")\n    seed = -1\n    if \"--seed\" in arguments and arguments[\"--seed\"] != \"\":\n        seed = int(arguments[\"--seed\"])\n    else:\n        seed = currentMilliSecs()\n    assert seed > 0\n\n    random.seed(seed)\n    print(f\"---SEED: {seed} ---\")\n\n    resetConfig()\n\n    ddls = getTableDDLs()\n    data = getTableData()\n\n    if getConfig().interactiveMode:\n        _interactiveMode(ddls, data)\n    else:\n        _fileMode(ddls, data)\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/node_defs.py",
    "content": "from enum import Enum\n\n\nclass JoinType(Enum):\n    INNER = 1\n    LEFT = 2\n    RIGHT = 3\n    FULL = 4\n\n\nclass RTEType(Enum):\n    RELATION = 1\n    SUBQUERY = 2\n    CTE = 3\n    VALUES = 4\n\n\nclass RestrictOp(Enum):\n    LT = 1\n    GT = 2\n    EQ = 3\n\n\nclass CitusType(Enum):\n    DISTRIBUTED = 1\n    REFERENCE = 2\n    POSTGRES = 3\n\n\nclass Table:\n    def __init__(\n        self,\n        name,\n        citusType,\n        maxAllowedUseOnQuery,\n        rowCount,\n        nullRate,\n        duplicateRate,\n        columns,\n        distinctCopyCount,\n    ):\n        self.name = name\n        self.citusType = citusType\n        self.maxAllowedUseOnQuery = maxAllowedUseOnQuery\n        self.rowCount = rowCount\n        self.nullRate = nullRate\n        self.duplicateRate = duplicateRate\n        self.columns = columns\n        self.distinctCopyCount = distinctCopyCount\n\n\nclass Column:\n    def __init__(self, name, type):\n        self.name = name\n        self.type = type\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/out/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/query_gen.py",
    "content": "import random\n\nfrom node_defs import RTEType\nfrom random_selections import (\n    randomJoinOp,\n    randomRestrictOp,\n    randomRteType,\n    shouldSelectThatBranch,\n)\n\nfrom config.config import getAllTableNames, getConfig, getMaxAllowedCountForTable\n\n# query_gen.py generates a new query from grammar rules below. It randomly chooses allowed rules\n# to generate a query. Here are some important notes about the query generation:\n#\n# - We enforce the max allowed # of usage for each table. It also enforces global total # of tables.\n# - Table names, restriction types and any other selections are chosen between the values provided by\n#   configuration file (config.yml).\n# - Entry point for the generator is newQuery and all other methods are internal methods. We pass a context\n#   object named GeneratorContext to all internal methods as parameter to perform checks and generations\n#   on the query via the context.\n# - shouldSelectThatBranch() is useful utility method to randomly chose a grammar rule. Some of the rules\n#   are only selected if we allowed them in configuration file.\n# - We enforce table limits separately if we are inside cte part of the query(see targetCteRteCount).\n#   We also enforce max # of ctes for a query.\n# - commonColName from the config is used at select and where clauses.\n# - useAvgAtTopLevelTarget is useful to return single row as query result. It is also useful to track Citus\n#   query bugs via run_query_compare_test.py.\n# - '=' restriction is removed from the config by default to return values different than null most of the time.\n# - 'RTE.VALUES' is also removed from the config for the same reason as above.\n# - Filter range is selected as same with data range for the same reason as above.\n# - aliasStack at GeneratorContext is useful to put correct table names into where clause.\n\n# grammar syntax\n#\n# ======Assumptions======\n# 1. Tables has common dist col\n# 2. All operations execute on common column for all tables.\n#\n# TODO: RTE_FUNCTION, RTE_TABLEFUNC\n#\n# ====SYNTAX====\n# ===Nonterminals===\n#   Query\n#   SelectExpr\n#   FromExpr\n#   RestrictExpr\n#   RteList\n#   Rte\n#   SubqueryRte\n#   RelationRte\n#   JoinList\n#   JoinOp\n#   Using\n#   RestrictList\n#   Restrict\n#   CteRte\n#   CteList\n#   Cte\n#   ValuesRte\n#   Limit\n#   OrderBy\n#\n# ===Terminals===\n#   e 'SELECT' 'FROM' 'INNER JOIN' 'LEFT JOIN' 'RIGHT JOIN' 'FULL JOIN' 'WHERE' 'LIMIT' 'USING' 'WITH'\n#     'ORDER BY' 'VALUES' 'IN' 'NOT' 'AS' '<' '>' '=' '*' ',' ';' '(' ')'\n#\n# ===Rules===\n# Start -> Query ';' || 'WITH' CteList Query ';'\n# Query -> SelectExpr FromExpr [OrderBy] [Limit] || 'SELECT' 'avg(avgsub.DistColName)' 'FROM' SubqueryRte 'AS avgsub'\n# SelectExpr -> 'SELECT' 'curAlias()' '.' DistColName\n# FromExpr -> 'FROM' (Rte JoinList JoinOp Rte Using || RteList) ['WHERE' 'nextRandomAlias()' '.' DistColName RestrictExpr]\n# RestrictExpr -> ('<' || '>' || '=') Int || ['NOT'] 'IN' SubqueryRte\n# JoinList ->  JoinOp Rte Using JoinList || e\n# Using -> 'USING' '(' DistColName ')'\n# RteList -> Rte [, RteList] || Rte\n# Rte -> SubqueryRte 'AS' 'nextRandomAlias()' || RelationRte 'AS' 'nextRandomAlias()' ||\n#        CteRte 'AS' 'nextRandomAlias()' || ValuesRte 'AS' 'nextRandomAlias()'\n# SubqueryRte -> '(' Query ')'\n# RelationRte -> 'nextRandomTableName()'\n# CteRte -> 'randomCteName()'\n# CteList -> Cte [',' CteList] || Cte\n# Cte -> 'nextRandomAlias()' 'AS' '(' Query ')'\n# ValuesRte -> '(' 'VALUES' '(' 'random()' ')' ')'\n# JoinOp -> 'INNER JOIN' || 'LEFT JOIN' || 'RIGHT JOIN' || 'FULL JOIN'\n# Limit -> 'LIMIT' 'random()'\n# OrderBy -> 'ORDER BY' DistColName\n# DistColName -> 'hardwired(get from config)'\n\n\nclass GeneratorContext:\n    \"\"\"context to store some variables which should be available during generation\"\"\"\n\n    def __init__(self):\n        # each level's last table is used in WHERE clause for the level\n        self.aliasStack = []\n        # tracks if we are inside cte as we should not refer cte inside cte\n        self.insideCte = False\n        # total rtes in cte + non-cte parts\n        self.totalRteCount = 0\n        # rte count in non-cte part to enforce non-cte rte limit\n        self.currentRteCount = 0\n        # cte count to enforce cte limit\n        self.currentCteCount = 0\n        # rte count in cte part to enforce rte limit in cte\n        self.currentCteRteCount = 0\n        # rte counts per table to enforce rte limit per table\n        self.perTableRtes = {}\n        # tables which hit count limit\n        self.disallowedTables = set()\n        # useful to track usage avg only at first select\n        self.usedAvg = False\n\n    def randomCteName(self):\n        \"\"\"returns a randomly selected cte name\"\"\"\n        randCteRef = random.randint(0, self.currentCteCount - 1)\n        return \" cte_\" + str(randCteRef)\n\n    def curAlias(self):\n        \"\"\"returns current alias name to be used for the current table\"\"\"\n        return \" table_\" + str(self.totalRteCount)\n\n    def curCteAlias(self):\n        \"\"\"returns current alias name to be used for the current cte\"\"\"\n        return \" cte_\" + str(self.currentCteCount)\n\n    def hasAnyCte(self):\n        \"\"\"returns if context has any cte\"\"\"\n        return self.currentCteCount > 0\n\n    def canGenerateNewRte(self):\n        \"\"\"checks if context exceeds allowed rte count\"\"\"\n        return self.currentRteCount < getConfig().targetRteCount\n\n    def canGenerateNewCte(self):\n        \"\"\"checks if context exceeds allowed cte count\"\"\"\n        return self.currentCteCount < getConfig().targetCteCount\n\n    def canGenerateNewRteInsideCte(self):\n        \"\"\"checks if context exceeds allowed rte count inside a cte\"\"\"\n        return self.currentCteRteCount < getConfig().targetCteRteCount\n\n    def addAlias(self, alias):\n        \"\"\"adds given alias to the stack\"\"\"\n        self.aliasStack.append(alias)\n\n    def removeLastAlias(self):\n        \"\"\"removes the latest added alias from the stack\"\"\"\n        return self.aliasStack.pop()\n\n    def getRteNameEnforcingRteLimits(self):\n        \"\"\"returns rteName by enforcing rte count limits for tables\"\"\"\n        # do not enforce per table rte limit if we are inside cte\n        if self.insideCte:\n            rteName = random.choice(getAllTableNames())\n            return \" \" + rteName + \" \"\n\n        while True:\n            # keep trying to find random table by eliminating the ones which hit table limit\n            allowedNames = set(getAllTableNames()) - self.disallowedTables\n            assert len(allowedNames) > 0\n            rteName = random.choice(list(allowedNames))\n\n            # not yet added to rte count map, so we can allow the name\n            if rteName not in self.perTableRtes:\n                self.perTableRtes[rteName] = 0\n                break\n            # limit is not exceeded, so we can allow the name\n            if self.perTableRtes[rteName] < getMaxAllowedCountForTable(rteName):\n                break\n            else:\n                self.disallowedTables.add(rteName)\n\n        # increment rte count for the table name\n        self.perTableRtes[rteName] += 1\n        return \" \" + rteName + \" \"\n\n\ndef newQuery():\n    \"\"\"returns generated query\"\"\"\n    genCtx = GeneratorContext()\n    return _start(genCtx)\n\n\ndef _start(genCtx):\n    # Query ';' || 'WITH' CteList Query ';'\n    query = \"\"\n    if not genCtx.canGenerateNewCte() or shouldSelectThatBranch():\n        query += _genQuery(genCtx)\n    else:\n        genCtx.insideCte = True\n        query += \" WITH \"\n        query += _genCteList(genCtx)\n        genCtx.insideCte = False\n        query += _genQuery(genCtx)\n    query += \";\"\n    return query\n\n\ndef _genQuery(genCtx):\n    # SelectExpr FromExpr [OrderBy] [Limit] || 'SELECT' 'avg(avgsub.DistColName)' 'FROM' SubqueryRte 'AS avgsub'\n    query = \"\"\n    if (\n        getConfig().useAvgAtTopLevelTarget\n        and not genCtx.insideCte\n        and not genCtx.usedAvg\n    ):\n        genCtx.usedAvg = True\n        query += \"SELECT \"\n        query += \"count(*), avg(avgsub.\" + getConfig().commonColName + \") FROM \"\n        query += _genSubqueryRte(genCtx)\n        query += \" AS avgsub\"\n    else:\n        query += _genSelectExpr(genCtx)\n        query += _genFromExpr(genCtx)\n        choseOrderby = False\n        if getConfig().orderby and shouldSelectThatBranch():\n            query += _genOrderBy(genCtx)\n            choseOrderby = True\n        if getConfig().limit and shouldSelectThatBranch():\n            if not choseOrderby and getConfig().forceOrderbyWithLimit:\n                query += _genOrderBy(genCtx)\n            query += _genLimit(genCtx)\n    return query\n\n\ndef _genOrderBy(genCtx):\n    # 'ORDER BY' DistColName\n    query = \"\"\n    query += \" ORDER BY \"\n    query += getConfig().commonColName + \" \"\n    return query\n\n\ndef _genLimit(genCtx):\n    # 'LIMIT' 'random()'\n    query = \"\"\n    query += \" LIMIT \"\n    (fromVal, toVal) = getConfig().limitRange\n    query += str(random.randint(fromVal, toVal))\n    return query\n\n\ndef _genSelectExpr(genCtx):\n    # 'SELECT' 'curAlias()' '.' DistColName\n    query = \"\"\n    query += \" SELECT \"\n    commonColName = getConfig().commonColName\n    query += genCtx.curAlias() + \".\" + commonColName + \" \"\n\n    return query\n\n\ndef _genFromExpr(genCtx):\n    # 'FROM' (Rte JoinList JoinOp Rte Using || RteList) ['WHERE' 'nextRandomAlias()' '.' DistColName RestrictExpr]\n    query = \"\"\n    query += \" FROM \"\n\n    if shouldSelectThatBranch():\n        query += _genRte(genCtx)\n        query += _genJoinList(genCtx)\n        query += randomJoinOp()\n        query += _genRte(genCtx)\n        query += _genUsing(genCtx)\n    else:\n        query += _genRteList(genCtx)\n\n    alias = genCtx.removeLastAlias()\n    if shouldSelectThatBranch():\n        query += \" WHERE \"\n        query += alias + \".\" + getConfig().commonColName\n        query += _genRestrictExpr(genCtx)\n    return query\n\n\ndef _genRestrictExpr(genCtx):\n    # ('<' || '>' || '=') Int || ['NOT'] 'IN' '(' SubqueryRte ')'\n    query = \"\"\n    if (\n        not getConfig().semiAntiJoin\n        or not genCtx.canGenerateNewRte()\n        or shouldSelectThatBranch()\n    ):\n        query += randomRestrictOp()\n        (fromVal, toVal) = getConfig().filterRange\n        query += str(random.randint(fromVal, toVal))\n    else:\n        if shouldSelectThatBranch():\n            query += \" NOT\"\n        query += \" IN \"\n        query += _genSubqueryRte(genCtx)\n    return query\n\n\ndef _genCteList(genCtx):\n    # Cte [',' CteList] || Cte\n    query = \"\"\n\n    if shouldSelectThatBranch():\n        query += _genCte(genCtx)\n        if not genCtx.canGenerateNewCte():\n            return query\n        query += \",\"\n        query += _genCteList(genCtx)\n    else:\n        query += _genCte(genCtx)\n    return query\n\n\ndef _genCte(genCtx):\n    # 'nextRandomAlias()' 'AS' '(' Query ')'\n    query = \"\"\n    query += genCtx.curCteAlias()\n    genCtx.currentCteCount += 1\n    query += \" AS \"\n    query += \" ( \"\n    query += _genQuery(genCtx)\n    query += \" ) \"\n    return query\n\n\ndef _genRteList(genCtx):\n    # RteList -> Rte [, RteList] || Rte\n    query = \"\"\n    if getConfig().cartesianProduct and shouldSelectThatBranch():\n        query += _genRte(genCtx)\n        if not genCtx.canGenerateNewRte():\n            return query\n        if genCtx.insideCte and not genCtx.canGenerateNewRteInsideCte():\n            return query\n        query += \",\"\n        query += _genRteList(genCtx)\n    else:\n        query += _genRte(genCtx)\n    return query\n\n\ndef _genJoinList(genCtx):\n    # JoinOp Rte Using JoinList || e\n    query = \"\"\n\n    if shouldSelectThatBranch():\n        if not genCtx.canGenerateNewRte():\n            return query\n        if genCtx.insideCte and not genCtx.canGenerateNewRteInsideCte():\n            return query\n        query += randomJoinOp()\n        query += _genRte(genCtx)\n        query += _genUsing(genCtx)\n        query += _genJoinList(genCtx)\n    return query\n\n\ndef _genUsing(genCtx):\n    # 'USING' '(' DistColName ')'\n    query = \"\"\n    query += \" USING (\" + getConfig().commonColName + \" ) \"\n    return query\n\n\ndef _genRte(genCtx):\n    # SubqueryRte as 'nextRandomAlias()' || RelationRte as 'curAlias()' ||\n    # CteRte as 'curAlias()' || ValuesRte 'AS' 'nextRandomAlias()'\n    alias = genCtx.curAlias()\n    modifiedAlias = None\n\n    if genCtx.insideCte:\n        genCtx.currentCteRteCount += 1\n    else:\n        genCtx.currentRteCount += 1\n    genCtx.totalRteCount += 1\n\n    # donot dive into recursive subquery further if we hit into rte limit, replace it with relation rte\n    rteType = randomRteType()\n    if not genCtx.canGenerateNewRte():\n        rteType = RTEType.RELATION\n\n    # donot dive into recursive subquery further if we hit into rte in cte limit, replace it with relation rte\n    if genCtx.insideCte and not genCtx.canGenerateNewRteInsideCte():\n        rteType = RTEType.RELATION\n\n    # we cannot refer to cte if we are inside it or we donot have any cte\n    if (genCtx.insideCte or not genCtx.hasAnyCte()) and rteType == RTEType.CTE:\n        rteType = RTEType.RELATION\n\n    query = \"\"\n    if rteType == RTEType.SUBQUERY:\n        query += _genSubqueryRte(genCtx)\n    elif rteType == RTEType.RELATION:\n        query += _genRelationRte(genCtx)\n    elif rteType == RTEType.CTE:\n        query += _genCteRte(genCtx)\n    elif rteType == RTEType.VALUES:\n        query += _genValuesRte(genCtx)\n        modifiedAlias = alias + \"(\" + getConfig().commonColName + \") \"\n    else:\n        raise BaseException(\"unknown RTE type\")\n\n    query += \" AS \"\n    query += alias if not modifiedAlias else modifiedAlias\n    genCtx.addAlias(alias)\n\n    return query\n\n\ndef _genSubqueryRte(genCtx):\n    # '(' Query ')'\n    query = \"\"\n    query += \" ( \"\n    query += _genQuery(genCtx)\n    query += \" ) \"\n    return query\n\n\ndef _genRelationRte(genCtx):\n    # 'randomAllowedTableName()'\n    query = \"\"\n    query += genCtx.getRteNameEnforcingRteLimits()\n    return query\n\n\ndef _genCteRte(genCtx):\n    # 'randomCteName()'\n    query = \"\"\n    query += genCtx.randomCteName()\n    return query\n\n\ndef _genValuesRte(genCtx):\n    # '( VALUES(random()) )'\n    query = \"\"\n    (fromVal, toVal) = getConfig().dataRange\n    query += \" ( VALUES(\" + str(random.randint(fromVal, toVal)) + \" ) ) \"\n    return query\n"
  },
  {
    "path": "src/test/regress/citus_tests/query_generator/random_selections.py",
    "content": "import random\nimport time\n\nfrom node_defs import RestrictOp\n\nfrom config.config import getConfig\n\n\ndef shouldSelectThatBranch():\n    \"\"\"returns 0 or 1 randomly\"\"\"\n    return random.randint(0, 1)\n\n\ndef currentMilliSecs():\n    \"\"\"returns total milliseconds since epoch\"\"\"\n    return round(time.time() * 1000)\n\n\ndef randomRteType():\n    \"\"\"returns a randomly selected RteType given at config\"\"\"\n    rtes = getConfig().targetRteTypes\n    return random.choice(rtes)\n\n\ndef randomJoinOp():\n    \"\"\"returns a randomly selected JoinOp given at config\"\"\"\n    joinTypes = getConfig().targetJoinTypes\n    return \" \" + random.choice(joinTypes).name + \" JOIN\"\n\n\ndef randomRestrictOp():\n    \"\"\"returns a randomly selected RestrictOp given at config\"\"\"\n    restrictOps = getConfig().targetRestrictOps\n    restrictOp = random.choice(restrictOps)\n    opText = \"\"\n    if restrictOp == RestrictOp.LT:\n        opText = \"<\"\n    elif restrictOp == RestrictOp.GT:\n        opText = \">\"\n    elif restrictOp == RestrictOp.EQ:\n        opText = \"=\"\n    else:\n        raise BaseException(\"Unknown restrict op\")\n\n    return \" \" + opText + \" \"\n"
  },
  {
    "path": "src/test/regress/citus_tests/run_test.py",
    "content": "#!/usr/bin/env python3\nfrom __future__ import annotations\n\nimport argparse\nimport os\nimport pathlib\nimport random\nimport re\nimport shutil\nimport sys\nfrom collections import OrderedDict\nfrom contextlib import contextmanager\nfrom typing import Optional\n\nimport common\nfrom common import OLDEST_SUPPORTED_CITUS_VERSION, PG_BINDIR, REGRESS_DIR, capture, run\nfrom upgrade import generate_citus_tarball, run_citus_upgrade_tests\n\nfrom config import ARBITRARY_SCHEDULE_NAMES, CitusBaseClusterConfig, CitusUpgradeConfig\n\n\ndef main():\n    args = parse_arguments()\n\n    test_name = get_test_name(args)\n\n    # All python tests start with test_ and all other tests don't. This is by\n    # convention.\n    if test_name.startswith(\"test_\"):\n        run_python_test(test_name, args)\n        # above function never returns\n    else:\n        run_regress_test(test_name, args)\n\n\ndef parse_arguments():\n    args = argparse.ArgumentParser()\n    args.add_argument(\n        \"test_name\", help=\"Test name (must be included in a schedule.)\", nargs=\"?\"\n    )\n    args.add_argument(\n        \"-p\",\n        \"--path\",\n        required=False,\n        help=\"Relative path for test file (must have a .sql or .spec extension)\",\n        type=pathlib.Path,\n    )\n    args.add_argument(\n        \"-r\", \"--repeat\", help=\"Number of test to run\", type=int, default=1\n    )\n    args.add_argument(\n        \"-b\",\n        \"--use-base-schedule\",\n        required=False,\n        help=\"Choose base-schedules rather than minimal-schedules\",\n        action=\"store_true\",\n    )\n    args.add_argument(\n        \"-w\",\n        \"--use-whole-schedule-line\",\n        required=False,\n        help=\"Use the whole line found in related schedule\",\n        action=\"store_true\",\n    )\n    args.add_argument(\n        \"--valgrind\",\n        required=False,\n        help=\"Run the test with valgrind enabled\",\n        action=\"store_true\",\n    )\n\n    return vars(args.parse_args())\n\n\nclass TestDeps:\n    schedule: Optional[str]\n    direct_extra_tests: list[str]\n\n    def __init__(\n        self,\n        schedule,\n        extra_tests=None,\n        repeatable=True,\n        worker_count=2,\n        citus_upgrade_infra=False,\n    ):\n        self.schedule = schedule\n        self.direct_extra_tests = extra_tests or []\n        self.repeatable = repeatable\n        self.worker_count = worker_count\n        self.citus_upgrade_infra = citus_upgrade_infra\n\n    def extra_tests(self):\n        all_deps = OrderedDict()\n        for direct_dep in self.direct_extra_tests:\n            if direct_dep in DEPS:\n                for indirect_dep in DEPS[direct_dep].extra_tests():\n                    all_deps[indirect_dep] = True\n            all_deps[direct_dep] = True\n\n        return list(all_deps.keys())\n\n\nDEPS = {\n    \"multi_cluster_management\": TestDeps(\n        None, [\"multi_test_helpers_superuser\"], repeatable=False\n    ),\n    \"minimal_cluster_management\": TestDeps(\n        None, [\"multi_test_helpers_superuser\"], repeatable=False\n    ),\n    \"multi_behavioral_analytics_create_table\": TestDeps(\n        \"minimal_schedule\", [\"multi_test_helpers_superuser\"], repeatable=False\n    ),\n    \"create_role_propagation\": TestDeps(None, [\"multi_cluster_management\"]),\n    \"single_node_enterprise\": TestDeps(None),\n    \"multi_add_node_from_backup\": TestDeps(None, repeatable=False, worker_count=5),\n    \"multi_add_node_from_backup_negative\": TestDeps(\n        None, [\"multi_add_node_from_backup\"], worker_count=5, repeatable=False\n    ),\n    \"multi_add_node_from_backup_sync_replica\": TestDeps(\n        None, repeatable=False, worker_count=5\n    ),\n    \"single_node\": TestDeps(None, [\"multi_test_helpers\"]),\n    \"single_node_truncate\": TestDeps(None),\n    \"multi_explain\": TestDeps(\n        \"base_schedule\", [\"multi_insert_select_non_pushable_queries\"]\n    ),\n    \"multi_extension\": TestDeps(None, repeatable=False),\n    \"multi_test_helpers\": TestDeps(None),\n    \"multi_insert_select\": TestDeps(\"base_schedule\"),\n    \"multi_partitioning\": TestDeps(\"base_schedule\"),\n    \"multi_mx_create_table\": TestDeps(\n        None,\n        [\n            \"multi_mx_node_metadata\",\n            \"multi_cluster_management\",\n            \"multi_mx_function_table_reference\",\n        ],\n    ),\n    \"alter_distributed_table\": TestDeps(\n        \"minimal_schedule\", [\"multi_behavioral_analytics_create_table\"]\n    ),\n    \"alter_role_propagation\": TestDeps(\"minimal_schedule\"),\n    \"background_rebalance\": TestDeps(\n        None, [\"multi_test_helpers\", \"multi_cluster_management\"], worker_count=3\n    ),\n    \"background_rebalance_parallel\": TestDeps(\n        None, [\"multi_test_helpers\", \"multi_cluster_management\"], worker_count=6\n    ),\n    \"background_rebalance_parallel_reference_tables\": TestDeps(\n        None,\n        [\"multi_test_helpers\", \"multi_cluster_management\"],\n        repeatable=False,\n        worker_count=6,\n    ),\n    \"function_propagation\": TestDeps(\"minimal_schedule\"),\n    \"citus_shards\": TestDeps(\"minimal_schedule\"),\n    \"grant_on_foreign_server_propagation\": TestDeps(\"minimal_schedule\"),\n    \"multi_modifying_xacts\": TestDeps(\"minimal_schedule\"),\n    \"multi_mx_modifying_xacts\": TestDeps(None, [\"multi_mx_create_table\"]),\n    \"multi_mx_router_planner\": TestDeps(None, [\"multi_mx_create_table\"]),\n    \"multi_mx_copy_data\": TestDeps(None, [\"multi_mx_create_table\"]),\n    \"multi_mx_modifications\": TestDeps(None, [\"multi_mx_create_table\"]),\n    \"multi_mx_schema_support\": TestDeps(None, [\"multi_mx_copy_data\"]),\n    \"multi_simple_queries\": TestDeps(\"base_schedule\"),\n    \"create_single_shard_table\": TestDeps(\"minimal_schedule\"),\n    \"isolation_extension_commands\": TestDeps(\n        None, [\"isolation_setup\", \"isolation_add_remove_node\"]\n    ),\n    \"isolation_update_node\": TestDeps(\n        None, [\"isolation_setup\", \"isolation_add_remove_node\"]\n    ),\n    \"schema_based_sharding\": TestDeps(\"minimal_schedule\"),\n    \"multi_sequence_default\": TestDeps(\n        None, [\"multi_test_helpers\", \"multi_cluster_management\", \"multi_table_ddl\"]\n    ),\n    \"grant_on_schema_propagation\": TestDeps(\"minimal_schedule\"),\n    \"propagate_extension_commands\": TestDeps(\"minimal_schedule\"),\n    \"multi_size_queries\": TestDeps(\"base_schedule\", [\"multi_copy\"]),\n    \"multi_mx_node_metadata\": TestDeps(\n        None, [\"multi_extension\", \"multi_test_helpers\", \"multi_test_helpers_superuser\"]\n    ),\n    \"multi_mx_function_table_reference\": TestDeps(\n        None,\n        [\"multi_cluster_management\", \"remove_coordinator_from_metadata\"],\n        # because it queries node group id and it changes as we add / remove nodes\n        repeatable=False,\n    ),\n    \"multi_mx_add_coordinator\": TestDeps(\n        None,\n        [\n            \"multi_cluster_management\",\n            \"remove_coordinator_from_metadata\",\n            \"multi_mx_function_table_reference\",\n        ],\n    ),\n    \"metadata_sync_helpers\": TestDeps(\n        None, [\"multi_mx_node_metadata\", \"multi_cluster_management\"]\n    ),\n    \"multi_utilities\": TestDeps(\"minimal_schedule\", [\"multi_data_types\"]),\n    \"multi_tenant_isolation_nonblocking\": TestDeps(\n        \"minimal_schedule\", [\"multi_data_types\", \"remove_coordinator_from_metadata\"]\n    ),\n    \"remove_non_default_nodes\": TestDeps(\n        None, [\"multi_mx_node_metadata\", \"multi_cluster_management\"], repeatable=False\n    ),\n    \"citus_split_shard_columnar_partitioned\": TestDeps(\n        \"minimal_schedule\", [\"remove_coordinator_from_metadata\"]\n    ),\n    \"add_coordinator\": TestDeps(\n        \"minimal_schedule\", [\"remove_coordinator_from_metadata\"], repeatable=False\n    ),\n    \"multi_multiuser_auth\": TestDeps(\n        \"minimal_schedule\",\n        [\"multi_create_table\", \"multi_create_users\", \"multi_multiuser_load_data\"],\n        repeatable=False,\n    ),\n    \"multi_prepare_plsql\": TestDeps(\"base_schedule\"),\n    \"pg15\": TestDeps(\"base_schedule\"),\n    \"foreign_key_to_reference_shard_rebalance\": TestDeps(\n        \"minimal_schedule\", [\"remove_coordinator_from_metadata\"]\n    ),\n    \"limit_intermediate_size\": TestDeps(\"base_schedule\"),\n    \"columnar_drop\": TestDeps(\n        \"minimal_columnar_schedule\",\n        [\"columnar_create\", \"columnar_load\"],\n        repeatable=False,\n    ),\n    \"multi_metadata_sync\": TestDeps(\n        None,\n        [\n            \"multi_sequence_default\",\n            \"alter_database_propagation\",\n            \"alter_role_propagation\",\n            \"multi_test_catalog_views\",\n            \"multi_drop_extension\",\n        ],\n        repeatable=False,\n    ),\n    \"pg17\": TestDeps(\"minimal_schedule\", [\"multi_behavioral_analytics_create_table\"]),\n    \"multi_subquery_misc\": TestDeps(\n        \"minimal_schedule\", [\"multi_behavioral_analytics_create_table\"]\n    ),\n    \"multi_subquery_union\": TestDeps(\n        \"minimal_schedule\", [\"multi_behavioral_analytics_create_table\"]\n    ),\n    \"multi_subquery_in_where_clause\": TestDeps(\n        \"minimal_schedule\", [\"multi_behavioral_analytics_create_table\"]\n    ),\n    \"multi_limit_clause_approximate\": TestDeps(\n        \"minimal_schedule\",\n        [\"multi_create_table\", \"multi_create_users\", \"multi_load_data\"],\n    ),\n    \"multi_single_relation_subquery\": TestDeps(\n        \"minimal_schedule\",\n        [\"multi_create_table\", \"multi_create_users\", \"multi_load_data\"],\n    ),\n    \"multi_subquery_complex_reference_clause\": TestDeps(\n        \"minimal_schedule\", [\"multi_behavioral_analytics_create_table\"]\n    ),\n    \"multi_subquery_in_where_reference_clause\": TestDeps(\n        \"minimal_schedule\", [\"multi_behavioral_analytics_create_table\"]\n    ),\n    \"subquery_in_where\": TestDeps(\n        \"minimal_schedule\", [\"multi_behavioral_analytics_create_table\"]\n    ),\n    \"subquery_in_targetlist\": TestDeps(\n        \"minimal_schedule\", [\"multi_behavioral_analytics_create_table\"]\n    ),\n    \"window_functions\": TestDeps(\n        \"minimal_schedule\", [\"multi_behavioral_analytics_create_table\"]\n    ),\n    \"multi_subquery_window_functions\": TestDeps(\n        \"minimal_schedule\", [\"multi_behavioral_analytics_create_table\"]\n    ),\n}\n\n\ndef run_python_test(test_name, args):\n    \"\"\"Runs the test using pytest\n\n    This function never returns as it usese os.execlp to replace the current\n    process with a new pytest process.\n    \"\"\"\n    test_path = REGRESS_DIR / \"citus_tests\" / \"test\" / f\"{test_name}.py\"\n    if not test_path.exists():\n        raise Exception(\"Test could not be found in any schedule\")\n\n    os.execlp(\n        \"pytest\",\n        \"pytest\",\n        \"--numprocesses\",\n        \"auto\",\n        \"--count\",\n        str(args[\"repeat\"]),\n        str(test_path),\n    )\n\n\ndef run_regress_test(test_name, args):\n    original_schedule, schedule_line = find_test_schedule_and_line(test_name, args)\n    print(f\"SCHEDULE: {original_schedule}\")\n    print(f\"SCHEDULE_LINE: {schedule_line}\")\n\n    dependencies = test_dependencies(test_name, original_schedule, schedule_line, args)\n\n    with tmp_schedule(test_name, dependencies, schedule_line, args) as schedule:\n        if \"upgrade\" in original_schedule:\n            run_schedule_with_python(test_name, schedule, dependencies)\n        else:\n            run_schedule_with_multiregress(test_name, schedule, dependencies, args)\n\n\ndef run_schedule_with_python(test_name, schedule, dependencies):\n    pgxs_path = pathlib.Path(capture(\"pg_config --pgxs\").rstrip())\n\n    os.chdir(REGRESS_DIR)\n    os.environ[\"PATH\"] = str(REGRESS_DIR / \"bin\") + os.pathsep + os.environ[\"PATH\"]\n    os.environ[\"PG_REGRESS_DIFF_OPTS\"] = \"-dU10 -w\"\n\n    fake_config_args = {\n        \"--pgxsdir\": str(pgxs_path.parent.parent.parent),\n        \"--bindir\": PG_BINDIR,\n        \"--mixed\": False,\n    }\n\n    if dependencies.citus_upgrade_infra:\n        run_single_citus_upgrade_test(test_name, schedule, fake_config_args)\n        return\n\n    config = CitusBaseClusterConfig(fake_config_args)\n    common.initialize_temp_dir(config.temp_dir)\n    common.initialize_citus_cluster(\n        config.bindir, config.datadir, config.settings, config\n    )\n    common.run_pg_regress(\n        config.bindir, config.pg_srcdir, config.coordinator_port(), schedule\n    )\n\n\ndef run_single_citus_upgrade_test(test_name, schedule, fake_config_args):\n    os.environ[\"CITUS_OLD_VERSION\"] = f\"v{OLDEST_SUPPORTED_CITUS_VERSION}\"\n    citus_tarball_path = generate_citus_tarball(f\"v{OLDEST_SUPPORTED_CITUS_VERSION}\")\n    config = CitusUpgradeConfig(fake_config_args, citus_tarball_path, None)\n\n    # Before tests are a simple case, because no actual upgrade is needed\n    if \"_before\" in test_name:\n        run_citus_upgrade_tests(config, schedule, None)\n        return\n\n    before_schedule_name = f\"{schedule}_before\"\n    before_schedule_path = REGRESS_DIR / before_schedule_name\n\n    before_test_name = test_name.replace(\"_after\", \"_before\")\n    with open(before_schedule_path, \"w\") as before_schedule_file:\n        before_schedule_file.write(f\"test: {before_test_name}\\n\")\n\n    try:\n        run_citus_upgrade_tests(config, before_schedule_name, schedule)\n    finally:\n        os.remove(before_schedule_path)\n\n\ndef run_schedule_with_multiregress(test_name, schedule, dependencies, args):\n    worker_count = needed_worker_count(test_name, dependencies)\n\n    # find suitable make recipe\n    if dependencies.schedule == \"base_isolation_schedule\" or test_name.startswith(\n        \"isolation\"\n    ):\n        make_recipe = \"check-isolation-custom-schedule\"\n    elif dependencies.schedule == \"failure_base_schedule\" or test_name.startswith(\n        \"failure\"\n    ):\n        make_recipe = \"check-failure-custom-schedule\"\n    elif test_name.startswith(\"columnar\"):\n        if dependencies.schedule is None:\n            # Columanar isolation tests don't depend on a base schedule,\n            # so this must be a columnar isolation test.\n            make_recipe = \"check-columnar-isolation-custom-schedule\"\n        elif dependencies.schedule == \"minimal_columnar_schedule\":\n            make_recipe = \"check-columnar-custom-schedule\"\n        else:\n            raise Exception(\"Columnar test could not be found in any schedule\")\n    else:\n        make_recipe = \"check-custom-schedule\"\n\n    if args[\"valgrind\"]:\n        make_recipe += \"-vg\"\n\n    # prepare command to run tests\n    test_command = (\n        f\"make -C {REGRESS_DIR} {make_recipe} \"\n        f\"WORKERCOUNT={worker_count} \"\n        f\"SCHEDULE='{schedule}'\"\n    )\n    run(test_command)\n\n\ndef default_base_schedule(test_schedule, args):\n    if \"isolation\" in test_schedule:\n        if \"columnar\" in test_schedule:\n            # we don't have pre-requisites for columnar isolation tests\n            return None\n        return \"base_isolation_schedule\"\n\n    if \"failure\" in test_schedule:\n        return \"failure_base_schedule\"\n\n    if \"enterprise\" in test_schedule:\n        return \"enterprise_minimal_schedule\"\n\n    if \"split\" in test_schedule:\n        return \"minimal_schedule\"\n\n    if \"mx\" in test_schedule:\n        if args[\"use_base_schedule\"]:\n            return \"mx_base_schedule\"\n        return \"mx_minimal_schedule\"\n\n    if \"operations\" in test_schedule:\n        return \"minimal_schedule\"\n\n    if \"pg_upgrade\" in test_schedule:\n        return \"minimal_pg_upgrade_schedule\"\n\n    if \"columnar\" in test_schedule:\n        return \"minimal_columnar_schedule\"\n\n    if test_schedule in ARBITRARY_SCHEDULE_NAMES:\n        print(f\"WARNING: Arbitrary config schedule ({test_schedule}) is not supported.\")\n        sys.exit(0)\n\n    if args[\"use_base_schedule\"]:\n        return \"base_schedule\"\n    return \"minimal_schedule\"\n\n\n# we run the tests with 2 workers by default.\n# If we find any dependency which requires more workers, we update the worker count.\ndef worker_count_for(test_name):\n    if test_name in DEPS:\n        return DEPS[test_name].worker_count\n    return 2\n\n\ndef get_test_name(args):\n    if args[\"test_name\"]:\n        return args[\"test_name\"]\n\n    if not args[\"path\"]:\n        print(\"FATAL: No test given.\")\n        sys.exit(2)\n\n    absolute_test_path = os.path.join(os.getcwd(), args[\"path\"])\n\n    if not os.path.isfile(absolute_test_path):\n        print(f\"ERROR: test file '{absolute_test_path}' does not exist\")\n        sys.exit(2)\n\n    if pathlib.Path(absolute_test_path).suffix not in (\".spec\", \".sql\", \".py\"):\n        print(\n            \"ERROR: Unrecognized test extension. Valid extensions are: .sql, .spec, and .py\"\n        )\n        sys.exit(1)\n\n    return pathlib.Path(absolute_test_path).stem\n\n\ndef find_test_schedule_and_line(test_name, args):\n    for schedule_file_path in sorted(REGRESS_DIR.glob(\"*_schedule\")):\n        for schedule_line in open(schedule_file_path, \"r\"):\n            if re.search(r\"^test:.*\\b\" + test_name + r\"\\b\", schedule_line):\n                test_schedule = pathlib.Path(schedule_file_path).stem\n                if args[\"use_whole_schedule_line\"]:\n                    return test_schedule, schedule_line\n                return test_schedule, f\"test: {test_name}\\n\"\n    raise Exception(\"Test could not be found in any schedule\")\n\n\ndef test_dependencies(test_name, test_schedule, schedule_line, args):\n    if test_name in DEPS:\n        return DEPS[test_name]\n\n    if \"citus_upgrade\" in test_schedule:\n        return TestDeps(None, citus_upgrade_infra=True)\n\n    if schedule_line_is_upgrade_after(schedule_line):\n        # upgrade_xxx_after tests always depend on upgrade_xxx_before\n        test_names = schedule_line.split()[1:]\n        before_tests = []\n        # _after tests have implicit dependencies on _before tests\n        for test_name in test_names:\n            if \"_after\" in test_name:\n                before_tests.append(test_name.replace(\"_after\", \"_before\"))\n\n        # the upgrade_columnar_before renames the schema, on which other\n        # \"after\" tests depend. So we make sure to execute it always.\n        if \"upgrade_columnar_before\" not in before_tests:\n            before_tests.append(\"upgrade_columnar_before\")\n\n        return TestDeps(default_base_schedule(test_schedule, args), before_tests)\n\n    # before_ tests leave stuff around on purpose for the after tests. So they\n    # are not repeatable by definition.\n    if \"before_\" in test_schedule:\n        repeatable = False\n    else:\n        repeatable = True\n\n    return TestDeps(default_base_schedule(test_schedule, args), repeatable=repeatable)\n\n\n# Returns true if given test_schedule_line is of the form:\n#   \"test: upgrade_ ... _after ..\"\ndef schedule_line_is_upgrade_after(test_schedule_line: str) -> bool:\n    return (\n        test_schedule_line.startswith(\"test: upgrade_\")\n        and \"_after\" in test_schedule_line\n    )\n\n\n@contextmanager\ndef tmp_schedule(test_name, dependencies, schedule_line, args):\n    tmp_schedule_path = REGRESS_DIR / f\"tmp_schedule_{random.randint(1, 10000)}\"\n\n    # Prefill the temporary schedule with the base schedule that this test\n    # depends on. Some tests don't need a base schedule to run though,\n    # e.g tests that are in the first place in their own schedule\n    if dependencies.schedule:\n        shutil.copy2(REGRESS_DIR / dependencies.schedule, tmp_schedule_path)\n\n    with open(tmp_schedule_path, \"a\") as myfile:\n        # Add any specific dependencies\n        for dependency in dependencies.extra_tests():\n            myfile.write(f\"test: {dependency}\\n\")\n\n        repetition_cnt = args[\"repeat\"]\n        if repetition_cnt > 1 and not dependencies.repeatable:\n            repetition_cnt = 1\n            print(f\"WARNING: Cannot repeatably run this test: '{test_name}'\")\n        for _ in range(repetition_cnt):\n            myfile.write(schedule_line)\n\n    try:\n        yield tmp_schedule_path.stem\n    finally:\n        os.remove(tmp_schedule_path)\n\n\ndef needed_worker_count(test_name, dependencies):\n    worker_count = worker_count_for(test_name)\n    for dependency in dependencies.extra_tests():\n        worker_count = max(worker_count_for(dependency), worker_count)\n    return worker_count\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "src/test/regress/citus_tests/test/README.md",
    "content": "# Pytest based tests\n\n## Usage\n\nRun all tests in parallel:\n\n```bash\npytest -n auto\n```\n\nRun all tests sequentially:\n```bash\npytest\n```\n\nRun a specific test:\n```bash\npytest test/test_columnar.py::test_recovery\n```\n\nRun a specific test file in parallel:\n```bash\npytest -n auto test/test_columnar.py\n```\n\nRun any test that contains a certain string in the name:\n```bash\npytest -k recovery\n```\n\nRun tests without it capturing stdout/stderr. This can be useful to see the\nlogs of a passing test:\n```bash\npytest -s test/test_columnar.py::test_recovery\n```\n\n## General info\n\nOur other tests work by comparing output of a sequence of SQL commands that's\nexecuted by `psql` to an expected output. If there's a difference between the\nexpected and actual output, then the tests fails. This works fine for many\ncases, but certain types of tests are hard to write and a lot of care usually\nhas to be taken to make sure output is completely identical in every run.\n\nThe tests in this directory use a different approach and use\n[`pytest`][pytest-docs] to run tests that are written in the Python programming\nlanguage. This idea is similar to TAP tests that are part of Postgres, with the\nimportant difference that those are written in Perl.\n\nIn the sections below you can find most stuff you'll need to know about\n`pytest` to run and write such tests, but if you want more detailed info some\nuseful references are:\n- [A blog with pytest tips and tricks][pytest-tips]\n- [The official pytest docs][pytest-docs]\n\n[pytest-docs]: https://docs.pytest.org/en/stable/\n[pytest-tips]: https://pythontest.com/pytest-tips-tricks/\n\n## Adding a new test\n\nTests are automatically discovered by `pytest` using a simple but effective\nheuristic. In this directory (`src/test/regress/citus_tests/test`) it finds\nall of the files that are named `test_{some name}.py`. Those files\nare then searched for function names starting with the `test_` prefix. All those\nfunctions are considered tests by `pytest`.\n\n\n### Fixtures aka Dependency Injection aka Teardown/Cleanup\n\nAn important part of tests is that they have some dependencies. The most\nimportant dependency for us is usually a running Citus cluster. These\ndependencies are provided by what `pytest` calls [fixtures]. Fixtures are\nfunctions that `yield` a value. Anything before the `yield` is done during setup\nand anything after the yield is done during teardown of the test (or whole\nsession). All our fixtures are defined in `conftest.py`.\n\n\nUsing a fixture in a test is very easy, but looks like a lot of magic. All you\nhave to do is make sure your test function has an argument with the same name as\nthe name of the fixture. For example:\n\n```python\ndef test_some_query(cluster):\n    cluster.coordinator.sql(\"SELECT 1\")\n\tassert cluster.workers[0].sql_value('SELECT 2') == 2\n```\n\nIf you need a cluster of a specific size you can use the `cluster_factory`\nfixture:\n```python\ndef test_with_100_workers(cluster_factory):\n    cluster = cluster_factory(100)\n```\n\nIf you want more details on how fixtures work a few useful pages of the pytest\ndocs are:\n- [About fixtures][fixtures]\n- [How to use fixtures][fixtures-how-to]\n- [Fixtures reference][fixtures-reference]\n\n[fixtures]: https://docs.pytest.org/en/stable/explanation/fixtures.html\n[fixtures-how-to]: https://docs.pytest.org/en/stable/how-to/fixtures.html\n[fixtures-reference]: https://docs.pytest.org/en/stable/reference/fixtures.html\n## Connecting to a test postgres\n\nSometimes your test is failing in an unexpected way and the easiest way to find\nout why is to connect to Postgres at a certain point interactively.\n\n### Using `psql_debug`\nThe easiest way is to use the `psql_debug()` method of your `Cluster` or\n`Postgres` instance.\n```python\ndef test_something(cluster):\n    # working stuff\n\n    cluster.coordinator.psql_debug()\n\n    # unexpectedly failing test\n```\n\nThen run this test with stdout/stderr capturing disabled (`-s`) and it will show\nyou an interactive `psql` prompt right at that point in the test:\n```bash\n$ pytest -s test/test_your_thing.py::test_something\n\n...\n\npsql (15.2)\nSSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)\nType \"help\" for help.\n\n127.0.0.1 postgres@postgres:10201-20016=\n> select 1;\n\n```\n\n\n### Debug manually\n\nSometimes you need to connect to more than one node though. For that you can use\na `Cluster` its `debug` method instead.\n\n```python\ndef test_something(cluster):\n    # working stuff\n\n    cluster.debug()\n\n    # unexpectedly failing test\n```\n\n\nThen run this test with stdout/stderr capturing disabled (`-s`) and it will show\nyou the connection string for each of the nodes in the cluster:\n```bash\n$ PG_FORCE_PORTS=true pytest -s test/test_your_thing.py::test_something\n...\n\nThe nodes in this cluster and their connection strings are:\n/tmp/pytest-of-jelte/pytest-752/cluster2-0/coordinator:\n    \"host=127.0.0.1 port=10202 dbname=postgres user=postgres options='-c search_path=test_recovery' connect_timeout=3 client_encoding=UTF8\"\n/tmp/pytest-of-jelte/pytest-752/cluster2-0/worker0:\n    \"host=127.0.0.1 port=10203 dbname=postgres user=postgres options='-c search_path=test_recovery' connect_timeout=3 client_encoding=UTF8\"\n/tmp/pytest-of-jelte/pytest-752/cluster2-0/worker1:\n    \"host=127.0.0.1 port=10204 dbname=postgres user=postgres options='-c search_path=test_recovery' connect_timeout=3 client_encoding=UTF8\"\nPress Enter to continue running the test...\n```\n\nThen in another terminal you can manually connect to as many of them as you want.\nUsing `PG_FORCE_PORTS` is recommended here, to make sure that the ports will\nstay the same across runs of the tests. That way you can reuse the connection\nstrings that you got from a previous run, if you need to debug again.\n"
  },
  {
    "path": "src/test/regress/citus_tests/test/__init__.py",
    "content": ""
  },
  {
    "path": "src/test/regress/citus_tests/test/conftest.py",
    "content": "import pytest\nfrom common import CitusCluster, Postgres, cleanup_test_leftovers, parallel_run\n\n\n@pytest.fixture(scope=\"session\")\ndef cluster_factory_session(tmp_path_factory):\n    \"\"\"The session level pytest fixture that creates and caches citus clusters\n\n    IMPORTANT: This should not be used directly, but only indirectly through\n    the cluster_factory fixture.\n    \"\"\"\n    clusters = {}\n\n    def _make_or_get_cluster(worker_count: int):\n        if worker_count not in clusters:\n            clusters[worker_count] = CitusCluster(\n                tmp_path_factory.mktemp(f\"cluster{worker_count}-\"), worker_count\n            )\n        return clusters[worker_count]\n\n    yield _make_or_get_cluster\n\n    parallel_run(CitusCluster.cleanup, clusters.values())\n\n\n@pytest.fixture\ndef cluster_factory(cluster_factory_session, request):\n    \"\"\"The pytest fixture that creates and caches citus clusters\n\n    When the function provided by the factory is called, it returns a cluster\n    with the given worker count. This cluster is cached across tests, so that\n    future invocations with the same worker count don't need to create a\n    cluster again, but can reuse the previously created one.\n\n    To try and make sure that tests don't depend on eachother this tries very\n    hard to clean up anything that is created during the test.\n\n    It also prints the Postgres logs that were produced during the test to\n    stdout. Normally these will be hidden, but when a test fails pytest will\n    show all stdout output produced during the test. Thus showing the Postgres\n    logs in that case makes it easier to debug.\n    \"\"\"\n\n    log_handles = []\n    clusters = []\n    nodes = []\n\n    def _make_or_get_cluster(worker_count: int):\n        nonlocal log_handles\n        nonlocal nodes\n        cluster = cluster_factory_session(worker_count)\n        if cluster.failed_reset:\n            cluster.reset()\n            cluster.failed_reset = False\n        clusters.append(cluster)\n        log_handles += [(node, node.log_handle()) for node in cluster.nodes]\n        nodes += cluster.nodes\n\n        # Create a dedicated schema for the test and use it by default\n        cluster.coordinator.create_schema(request.node.originalname)\n        cluster.schema = request.node.originalname\n\n        return cluster\n\n    yield _make_or_get_cluster\n\n    try:\n        # We clean up test leftovers on all nodes together, instead of per\n        # cluster. The reason for this is that some subscriptions/publication\n        # pairs might be between different clusters. And by cleaning them up\n        # all together, the ordering of the DROPs is easy to make correct.\n        cleanup_test_leftovers(nodes)\n        parallel_run(Postgres.prepare_reset, nodes)\n        parallel_run(Postgres.restart, nodes)\n    except Exception:\n        for cluster in clusters:\n            cluster.failed_reset = True\n        raise\n    finally:\n        for node, log in log_handles:\n            print(f\"\\n\\nPG_LOG: {node.pgdata}\\n\")\n            print(log.read())\n            log.close()\n\n\n@pytest.fixture(name=\"coord\")\ndef coordinator(cluster_factory):\n    \"\"\"Sets up a clean single-node Citus cluster for this test\"\"\"\n    yield cluster_factory(0).coordinator\n\n\n@pytest.fixture\ndef cluster(cluster_factory):\n    \"\"\"Sets up a clean 2-worker Citus cluste for this test\"\"\"\n    yield cluster_factory(2)\n"
  },
  {
    "path": "src/test/regress/citus_tests/test/test_columnar.py",
    "content": "import psycopg\nimport pytest\n\n\ndef test_freezing(coord):\n    coord.sql(\"CREATE EXTENSION IF NOT EXISTS citus_columnar\")\n\n    coord.configure(\"vacuum_freeze_min_age = 50000\", \"vacuum_freeze_table_age = 50000\")\n    coord.restart()\n\n    # create columnar table and insert simple data to verify the data survives\n    # a crash\n    coord.sql(\"CREATE TABLE test_row(i int)\")\n    coord.sql(\"INSERT INTO test_row VALUES (1) \")\n    coord.sql(\n        \"CREATE TABLE test_columnar_freeze(i int) USING columnar WITH(autovacuum_enabled=false)\"\n    )\n    coord.sql(\"INSERT INTO test_columnar_freeze VALUES (1)\")\n\n    for _ in range(0, 7):\n        with coord.cur() as cur:\n            for _ in range(0, 10_000):\n                cur.execute(\"UPDATE test_row SET i = i + 1\")\n\n    frozen_age = coord.sql_value(\n        \"\"\"\n        select age(relfrozenxid)\n        from pg_class where relname='test_columnar_freeze';\n    \"\"\"\n    )\n\n    assert frozen_age > 70_000, \"columnar table was frozen\"\n    coord.sql(\"VACUUM FREEZE test_columnar_freeze\")\n\n    frozen_age = coord.sql_value(\n        \"\"\"\n        select age(relfrozenxid)\n        from pg_class where relname='test_columnar_freeze';\n    \"\"\"\n    )\n    assert frozen_age < 70_000, \"columnar table was not frozen\"\n\n    coord.sql(\"DROP EXTENSION citus_columnar CASCADE\")\n\n\ndef test_recovery(coord):\n    coord.sql(\"CREATE EXTENSION IF NOT EXISTS citus_columnar\")\n\n    # create columnar table and insert simple data to verify the data survives a crash\n    coord.sql(\"CREATE TABLE t1 (a int, b text) USING columnar\")\n    coord.sql(\n        \"INSERT INTO t1 SELECT a, 'hello world ' || a FROM generate_series(1,1002) AS a\"\n    )\n\n    # simulate crash\n    coord.stop(\"immediate\")\n    coord.start()\n\n    row_count = coord.sql_value(\"SELECT count(*) FROM t1\")\n    assert row_count == 1002, \"columnar didn't recover data before crash correctly\"\n\n    # truncate the table to verify the truncation survives a crash\n    coord.sql(\"TRUNCATE t1\")\n    # simulate crash\n    coord.stop(\"immediate\")\n    coord.start()\n\n    row_count = coord.sql_value(\"SELECT count(*) FROM t1\")\n    assert row_count == 0, \"columnar didn't recover the truncate correctly\"\n\n    # test crashing while having an open transaction\n    with pytest.raises(\n        psycopg.OperationalError,\n        match=(\n            \"server closed the connection unexpectedly|\"\n            \"consuming input failed: EOF detected|\"\n            \"SSL SYSCALL error: EOF detected|\"\n            \"SSL error: unexpected eof while reading\"\n        ),\n    ):\n        with coord.transaction() as cur:\n            cur.execute(\n                \"INSERT INTO t1 SELECT a, 'hello world ' || a FROM generate_series(1,1003) AS a\"\n            )\n            # simulate crash\n            coord.stop(\"immediate\")\n\n    coord.start()\n\n    row_count = coord.sql_value(\"SELECT count(*) FROM t1\")\n    assert row_count == 0, \"columnar didn't recover uncommited transaction\"\n\n    # test crashing while having a prepared transaction\n    with pytest.raises(\n        psycopg.OperationalError,\n        match=(\n            \"server closed the connection unexpectedly|\"\n            \"consuming input failed: EOF detected|\"\n            \"consuming input failed: SSL SYSCALL error: EOF detected|\"\n            \"SSL error: unexpected eof while reading\"\n        ),\n    ):\n        with coord.transaction() as cur:\n            cur.execute(\n                \"INSERT INTO t1 SELECT a, 'hello world ' || a FROM generate_series(1,1004) AS a\"\n            )\n            cur.execute(\"PREPARE TRANSACTION 'prepared_xact_crash'\")\n            # simulate crash\n            coord.stop(\"immediate\")\n\n    coord.start()\n\n    row_count = coord.sql_value(\"SELECT count(*) FROM t1\")\n    assert row_count == 0, \"columnar didn't recover uncommitted prepared transaction\"\n\n    coord.sql(\"COMMIT PREPARED 'prepared_xact_crash'\")\n\n    row_count = coord.sql_value(\"SELECT count(*) FROM t1\")\n    assert row_count == 1004, \"columnar didn't recover committed transaction\"\n\n    # test crash recovery with copied data\n    with coord.cur() as cur:\n        with cur.copy(\"COPY t1 FROM STDIN\") as copy:\n            copy.write_row((1, \"a\"))\n            copy.write_row((2, \"b\"))\n            copy.write_row((3, \"c\"))\n\n    # simulate crash\n    coord.stop(\"immediate\")\n    coord.start()\n\n    row_count = coord.sql_value(\"SELECT count(*) FROM t1\")\n    assert row_count == 1007, \"columnar didn't recover after copy\"\n\n    coord.sql(\"DROP EXTENSION citus_columnar CASCADE\")\n"
  },
  {
    "path": "src/test/regress/citus_tests/test/test_extension.py",
    "content": "import psycopg\nimport pytest\n\n\ndef test_create_drop_citus(coord):\n    with coord.cur() as cur1:\n        with coord.cur() as cur2:\n            # Conn1 drops the extension\n            # and Conn2 cannot use it.\n            cur1.execute(\"DROP EXTENSION citus\")\n\n            with pytest.raises(psycopg.errors.UndefinedFunction):\n                # Conn1 dropped the extension. citus_version udf\n                # cannot be found.sycopg.errors.UndefinedFunction\n                # is expected here.\n                cur2.execute(\"SELECT citus_version();\")\n\n            # Conn2 creates the extension,\n            # Conn1 is able to use it immediadtely.\n            cur2.execute(\"CREATE EXTENSION citus\")\n            cur1.execute(\"SELECT citus_version();\")\n            cur1.execute(\"DROP EXTENSION citus;\")\n\n    with coord.cur() as cur1:\n        with coord.cur() as cur2:\n            # A connection is able to create and use the extension\n            # within a transaction block.\n            cur1.execute(\"BEGIN;\")\n            cur1.execute(\"CREATE TABLE t1(id int);\")\n            cur1.execute(\"CREATE EXTENSION citus;\")\n            cur1.execute(\"SELECT create_reference_table('t1')\")\n            cur1.execute(\"ABORT;\")\n\n            # Conn1 aborted so Conn2 is be able to create and\n            # use the extension within a transaction block.\n            cur2.execute(\"BEGIN;\")\n            cur2.execute(\"CREATE TABLE t1(id int);\")\n            cur2.execute(\"CREATE EXTENSION citus;\")\n            cur2.execute(\"SELECT create_reference_table('t1')\")\n            cur2.execute(\"COMMIT;\")\n\n            # Conn2 commited so Conn1 is be able to use the\n            # extension immediately.\n            cur1.execute(\"SELECT citus_version();\")\n"
  },
  {
    "path": "src/test/regress/citus_tests/test/test_prepared_statements.py",
    "content": "def test_call_param(cluster):\n    # create a distributed table and an associated distributed procedure\n    # to ensure parameterized CALL succeed, even when the param is the\n    # distribution key.\n    coord = cluster.coordinator\n    coord.sql(\"CREATE TABLE test(i int)\")\n    coord.sql(\n        \"\"\"\n        CREATE PROCEDURE p(_i INT) LANGUAGE plpgsql AS $$\n        BEGIN\n        INSERT INTO test(i) VALUES (_i);\n        END; $$\n        \"\"\"\n    )\n    sql = \"CALL p(%s)\"\n\n    # prepare/exec before distributing\n    coord.sql_prepared(sql, (1,))\n\n    coord.sql(\"SELECT create_distributed_table('test', 'i')\")\n    coord.sql(\n        \"SELECT create_distributed_function('p(int)', distribution_arg_name := '_i', colocate_with := 'test')\"\n    )\n\n    # prepare/exec after distribution\n    coord.sql_prepared(sql, (2,))\n\n    sum_i = coord.sql_value(\"select sum(i) from test;\")\n\n    assert sum_i == 3\n"
  },
  {
    "path": "src/test/regress/citus_tests/upgrade/README.md",
    "content": "# Upgrade Tests\n\n## Postgres Upgrade Test\n\nPostgres upgrade test is used for testing postgres version upgrade with citus installed.\nBefore running the script, make sure that:\n\n-   You have downloaded citus.\n-   You have two different postgres versions.\n-   Citus is installed to both of the postgres versions. For each postgres version:\n\n    -   In citus source directory run:\n\n    ```bash\n      make clean\n      ./configure PG_CONFIG=<your path to postgres pg config>\n      PG_CONFIG=<your path to postgres pg config> make\n      sudo PG_CONFIG=<your path to postgres pg config> make install\n    ```\n\n    Make sure you do this for both postgres versions, pg_config should be different for each postgres version.\n\n-   Install `pipenv` and run in `citus/src/test/regress`:\n\n```bash\n    pipenv install\n    pipenv shell\n```\n\n-   Finally run upgrade test in `citus/src/test/regress`:\n\n```bash\n    pipenv run make check-pg-upgrade old-bindir=<old-bindir> new-bindir=<new-bindir> test-with-columnar=<true|false>\n```\n\nWhen test-with-columnar is provided as true, before_pg_upgrade_with_columnar_schedule /\nafter_pg_upgrade_with_columnar_schedule is used before / after upgrading Postgres during the\ntests and before_pg_upgrade_without_columnar_schedule / after_pg_upgrade_without_columnar_schedule\nis used otherwise.\n\nTo see full command list:\n\n```bash\n    pipenv run citus_tests/upgrade/pg_upgrade_test.py -help\n```\n\nHow the postgres upgrade test works:\n\n-   Temporary folder `tmp_upgrade` is created in `src/test/regress/`, if one exists it is removed first.\n-   Database is initialized and citus cluster is created(1 coordinator + 2 workers) with old postgres.\n-   `before_pg_upgrade_with_columnar_schedule` / `before_pg_upgrade_without_columnar_schedule` is run with `pg_regress`. This schedule sets up any\n    objects and data that will be tested for preservation after the upgrade. It\n-   `after_pg_upgrade_with_columnar_schedule` / `after_pg_upgrade_without_columnar_schedule` is run with `pg_regress` to verify that the output\n    of those tests is the same before the upgrade as after.\n-   `citus_prepare_pg_upgrade` is run in coordinators and workers.\n-   Old database is stopped.\n-   A new database is initialized with new postgres under `tmp_upgrade`.\n-   Postgres upgrade is performed.\n-   New database is started in both coordinators and workers.\n-   `citus_finish_pg_upgrade` is run in coordinators and workers to finalize the upgrade step.\n-   `after_pg_upgrade_with_columnar_schedule` / `after_pg_upgrade_without_columnar_schedule` is run with `pg_regress` to verify that the previously created tables, and data still exist. Router and realtime queries are used to verify this.\n\n### Writing new PG upgrade tests\n\nThe main important thing is that we have `upgrade_{name}_before` and\n`upgrade_{name}_after` tests. The `before` files are used to setup any objects\nand data before the upgrade. The `after` tests shouldn't have any side effects\nsince they are run twice (once before and once after the upgrade).\n\nFurthermore, anything that is basic Citus functionality should go in the\n`upgrade_basic_before`/`upgrade_basic_after` tests. This test file is used\nduring PG upgrades and Citus upgrades. Any features that don't work in old Citus\nversions should thus be added to their own file, because that file will then\nonly be run during PG versions.\n\n## Citus Upgrade Test\n\nCitus upgrade test is used for testing citus version upgrades from specific version to master. The purpose of this test is to ensure that a newly made change does not result in unexpected upgrade errors.\n\nCurrently the citus upgrade test assumes that:\n\n-   You have citus artifact tarballs, both for old version and master.\n\nHow the citus upgrade test work:\n\n-   The script takes `citus-pre-tar` and `citus-post-tar` which should contain citus artifacts.\n-   It installs the given citus version from `citus-pre-tar`.\n-   It creates a citus cluster(1 coordinator 2 workers).\n-   It reports the initial versions.\n-   It installs the checked out citus version from `citus-post-tar`.\n-   It restarts the database and runs `ALTER EXTENSION citus UPGRADE`.\n-   It runs `after_citus_upgrade` schedule to verify that the upgrade is successful.\n-   It stops the cluster.\n\nNote that when the version of citus changes, we should update `MASTER_VERSION` with the new version of citus otherwise that will be outdated and it will fail.\n\nThere is a target for citus upgrade test. We run citus upgrade tests both in normal mode and in mixed mode. In mixed mode, we don't upgrade one of the workers. `'citus.enable_version_checks' : 'false'` is used to prevent citus from giving an error for mixed mode.\n\nTo see full command list:\n\n```bash\n    pipenv run upgrade/citus_upgrade_test.py -help\n```\n\nIn order to run citus upgrade tests locally you can use:\n\n```bash\n    pipenv run make check-citus-upgrade-local citus-old-version=v8.0.0\n```\n\nFor mixed mode:\n\n```bash\n    pipenv run make check-citus-upgrade-mixed-local citus-old-version=v8.0.0\n```\n"
  },
  {
    "path": "src/test/regress/citus_tests/upgrade/__init__.py",
    "content": "from .citus_upgrade_test import generate_citus_tarball, run_citus_upgrade_tests  # noqa\n"
  },
  {
    "path": "src/test/regress/citus_tests/upgrade/citus_upgrade_test.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"citus_upgrade_test\nUsage:\n    citus_upgrade_test [options] --bindir=<bindir> --pgxsdir=<pgxsdir> [--citus-old-version=<citus-old-version>]\n\nOptions:\n    --bindir=<bindir>                       The PostgreSQL executable directory(ex: '~/.pgenv/pgsql-11.3/bin')\n    --citus-pre-tar=<citus-pre-tar>         Tarball with the citus artifacts to use as the base version to upgrade from\n    --citus-post-tar=<citus-post-tar>       Tarball with the citus artifacts to use as the new version to upgrade to\n    --pgxsdir=<pgxsdir>           \t        Path to the PGXS directory(ex: ~/.pgenv/src/postgresql-11.3)\n    --citus-old-version=<citus-old-version> Citus old version for local run(ex v8.0.0)\n    --mixed                                 Run the verification phase with one node not upgraded.\n    --minor-upgrade                         Use minor version upgrade test schedules instead of major version schedules.\n\"\"\"\n\nimport multiprocessing\nimport os\nimport re\nimport subprocess\nimport sys\n\nfrom docopt import docopt\n\n# https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time/14132912#14132912\nsys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\n# ignore E402 because these imports require addition to path\nimport common  # noqa: E402\nimport utils  # noqa: E402\nfrom common import CI, PG_MAJOR_VERSION, REPO_ROOT, run  # noqa: E402\nfrom utils import USER  # noqa: E402\n\nfrom config import (  # noqa: E402\n    AFTER_CITUS_UPGRADE_COORD_SCHEDULE,\n    BEFORE_CITUS_UPGRADE_COORD_SCHEDULE,\n    CITUS_VERSION_SQL,\n    MASTER_VERSION,\n    MIXED_AFTER_CITUS_UPGRADE_SCHEDULE,\n    MIXED_BEFORE_CITUS_UPGRADE_SCHEDULE,\n    CitusUpgradeConfig,\n)\n\n\ndef main(config):\n    before_upgrade_schedule = get_before_upgrade_schedule(config.mixed_mode)\n    after_upgrade_schedule = get_after_upgrade_schedule(config.mixed_mode)\n    run_citus_upgrade_tests(config, before_upgrade_schedule, after_upgrade_schedule)\n\n\ndef run_citus_upgrade_tests(config, before_upgrade_schedule, after_upgrade_schedule):\n    install_citus(config.pre_tar_path)\n    common.initialize_temp_dir(config.temp_dir)\n    common.initialize_citus_cluster(\n        config.bindir, config.datadir, config.settings, config\n    )\n\n    report_initial_version(config)\n\n    # Store the pre-upgrade GUCs and UDFs for minor version upgrades\n    pre_upgrade = None\n    if config.minor_upgrade:\n        pre_upgrade = get_citus_catalog_info(config)\n\n    run_test_on_coordinator(config, before_upgrade_schedule)\n\n    remove_citus(config.pre_tar_path)\n    if after_upgrade_schedule is None:\n        return\n\n    install_citus(config.post_tar_path)\n\n    restart_databases(config.bindir, config.datadir, config.mixed_mode, config)\n    run_alter_citus(config.bindir, config.mixed_mode, config)\n    verify_upgrade(config, config.mixed_mode, config.node_name_to_ports.values())\n\n    # For minor version upgrades, verify GUCs and UDFs does not have breaking changes\n    breaking_changes = []\n    if config.minor_upgrade:\n        breaking_changes = compare_citus_catalog_info(config, pre_upgrade)\n\n    run_test_on_coordinator(config, after_upgrade_schedule)\n    remove_citus(config.post_tar_path)\n\n    # Fail the test if there are any breaking changes\n    if breaking_changes:\n        common.eprint(\"\\n=== BREAKING CHANGES DETECTED ===\")\n        for change in breaking_changes:\n            common.eprint(f\"  - {change}\")\n        common.eprint(\"==================================\\n\")\n        sys.exit(1)\n\n\ndef get_citus_catalog_info(config):\n    results = {}\n    # Store GUCs\n    guc_results = utils.psql_capture(\n        config.bindir,\n        config.coordinator_port(),\n        \"SELECT name, boot_val FROM pg_settings WHERE name LIKE 'citus.%' ORDER BY name;\",\n    )\n\n    guc_lines = guc_results.decode(\"utf-8\").strip().split(\"\\n\")\n    results[\"gucs\"] = {}\n    for line in guc_lines[2:]:  # Skip header lines\n        name, boot_val = line.split(\"|\")\n        results[\"gucs\"][name.strip()] = boot_val.strip()\n\n    # Store UDFs\n    udf_results = utils.psql_capture(\n        config.bindir,\n        config.coordinator_port(),\n        \"\"\"\n        SELECT\n        n.nspname AS schema_name,\n        p.proname AS function_name,\n        pg_get_function_arguments(p.oid) AS full_args,\n        pg_get_function_result(p.oid) AS return_type\n        FROM pg_proc p\n        JOIN pg_namespace n ON n.oid = p.pronamespace\n        JOIN pg_depend d ON d.objid = p.oid\n        JOIN pg_extension e ON e.oid = d.refobjid\n        WHERE e.extname = 'citus'\n        AND d.deptype = 'e'\n        ORDER BY schema_name, function_name, full_args;\n        \"\"\",\n    )\n\n    udf_lines = udf_results.decode(\"utf-8\").strip().split(\"\\n\")\n    results[\"udfs\"] = {}\n    for line in udf_lines[2:]:  # Skip header lines\n        schema_name, function_name, full_args, return_type = line.split(\"|\")\n        key = (schema_name.strip(), function_name.strip())\n        signature = (full_args.strip(), return_type.strip())\n\n        if key not in results[\"udfs\"]:\n            results[\"udfs\"][key] = set()\n        results[\"udfs\"][key].add(signature)\n\n    # Store types, exclude composite types (t.typrelid = 0) and\n    # exclude auto-created array types\n    # (t.typname LIKE '\\_%' AND t.typelem <> 0)\n    type_results = utils.psql_capture(\n        config.bindir,\n        config.coordinator_port(),\n        \"\"\"\n        SELECT n.nspname, t.typname, t.typtype\n        FROM pg_type t\n        JOIN pg_depend d ON d.objid = t.oid\n        JOIN pg_extension e ON e.oid = d.refobjid\n        JOIN pg_namespace n ON n.oid = t.typnamespace\n        WHERE e.extname = 'citus'\n        AND t.typrelid = 0\n        AND NOT (t.typname LIKE '\\\\_%%' AND t.typelem <> 0)\n        ORDER BY n.nspname, t.typname;\n        \"\"\",\n    )\n    type_lines = type_results.decode(\"utf-8\").strip().split(\"\\n\")\n    results[\"types\"] = {}\n\n    for line in type_lines[2:]:  # Skip header lines\n        nspname, typname, typtype = line.split(\"|\")\n        key = (nspname.strip(), typname.strip())\n        results[\"types\"][key] = typtype.strip()\n\n    # Store tables and views\n    table_results = utils.psql_capture(\n        config.bindir,\n        config.coordinator_port(),\n        \"\"\"\n        SELECT n.nspname, c.relname, a.attname, t.typname\n        FROM pg_class c\n        JOIN pg_namespace n ON n.oid = c.relnamespace\n        JOIN pg_attribute a ON a.attrelid = c.oid\n        JOIN pg_type t ON t.oid = a.atttypid\n        JOIN pg_depend d ON d.objid = c.oid\n        JOIN pg_extension e ON e.oid = d.refobjid\n        WHERE e.extname = 'citus'\n        AND a.attnum > 0\n        AND NOT a.attisdropped\n        ORDER BY n.nspname, c.relname, a.attname;\n        \"\"\",\n    )\n\n    table_lines = table_results.decode(\"utf-8\").strip().split(\"\\n\")\n    results[\"tables\"] = {}\n    for line in table_lines[2:]:  # Skip header lines\n        nspname, relname, attname, typname = line.split(\"|\")\n        key = (nspname.strip(), relname.strip())\n\n        if key not in results[\"tables\"]:\n            results[\"tables\"][key] = {}\n        results[\"tables\"][key][attname.strip()] = typname.strip()\n\n    return results\n\n\ndef compare_citus_catalog_info(config, pre_upgrade):\n    post_upgrade = get_citus_catalog_info(config)\n    breaking_changes = []\n\n    # Compare GUCs\n    for name, boot_val in pre_upgrade[\"gucs\"].items():\n        if name not in post_upgrade[\"gucs\"]:\n            breaking_changes.append(f\"GUC {name} was removed\")\n        elif post_upgrade[\"gucs\"][name] != boot_val and name != \"citus.version\":\n            breaking_changes.append(\n                f\"The default value of GUC {name} was changed from {boot_val} to {post_upgrade['gucs'][name]}\"\n            )\n\n    # Compare UDFs - check if any pre-upgrade signatures were removed\n    for (schema_name, function_name), pre_signatures in pre_upgrade[\"udfs\"].items():\n        if (schema_name, function_name) not in post_upgrade[\"udfs\"]:\n            breaking_changes.append(\n                f\"UDF {schema_name}.{function_name} was completely removed\"\n            )\n        else:\n            post_signatures = post_upgrade[\"udfs\"][(schema_name, function_name)]\n            removed_signatures = pre_signatures - post_signatures\n\n            if removed_signatures:\n                for full_args, return_type in removed_signatures:\n                    if not find_compatible_udf_signature(\n                        full_args, return_type, post_signatures\n                    ):\n                        breaking_changes.append(\n                            f\"UDF signature removed: {schema_name}.{function_name}({full_args}) RETURNS {return_type}\"\n                        )\n\n    # Compare Types - check if any pre-upgrade types were removed or changed\n    for (nspname, typname), typtype in pre_upgrade[\"types\"].items():\n        if (nspname, typname) not in post_upgrade[\"types\"]:\n            breaking_changes.append(f\"Type {nspname}.{typname} was removed\")\n        elif post_upgrade[\"types\"][(nspname, typname)] != typtype:\n            breaking_changes.append(\n                f\"Type {nspname}.{typname} changed type from {typtype} to {post_upgrade['types'][(nspname, typname)]}\"\n            )\n\n    # Compare tables / views - check if any pre-upgrade tables or columns were removed or changed\n    for (nspname, relname), columns in pre_upgrade[\"tables\"].items():\n        if (nspname, relname) not in post_upgrade[\"tables\"]:\n            breaking_changes.append(f\"Table/view {nspname}.{relname} was removed\")\n        else:\n            post_columns = post_upgrade[\"tables\"][(nspname, relname)]\n\n            for col_name, col_type in columns.items():\n                if col_name not in post_columns:\n                    breaking_changes.append(\n                        f\"Column {col_name} in table/view {nspname}.{relname} was removed\"\n                    )\n                elif post_columns[col_name] != col_type:\n                    breaking_changes.append(\n                        f\"Column {col_name} in table/view {nspname}.{relname} changed type from {col_type} to {post_columns[col_name]}\"\n                    )\n\n    return breaking_changes\n\n\ndef find_compatible_udf_signature(full_args, return_type, post_signatures):\n    pre_args_list = [arg.strip() for arg in full_args.split(\",\") if arg.strip()]\n\n    for post_full_args, post_return_type in post_signatures:\n        if post_return_type == return_type:\n            post_args_list = [\n                arg.strip() for arg in post_full_args.split(\",\") if arg.strip()\n            ]\n            \"\"\" Here check if the function signatures are compatible, they are compatible if:\n            post_args_list has all the arguments of pre_args_list in the same order, but can have\n            additional arguments with default values \"\"\"\n            pre_index = 0\n            post_index = 0\n            compatible = True\n            while pre_index < len(pre_args_list) and post_index < len(post_args_list):\n                if pre_args_list[pre_index] == post_args_list[post_index]:\n                    pre_index += 1\n                else:\n                    # Check if the argument in post_args_list has a default value\n                    if \"default\" not in post_args_list[post_index].lower():\n                        compatible = False\n                        break\n                post_index += 1\n            if pre_index < len(pre_args_list):\n                compatible = False\n                continue\n\n            while post_index < len(post_args_list):\n                if \"default\" not in post_args_list[post_index].lower():\n                    compatible = False\n                    break\n                post_index += 1\n\n            if compatible:\n                return True\n\n    return False\n\n\ndef install_citus(tar_path):\n    if tar_path:\n        with utils.cd(\"/\"):\n            run([\"tar\", \"xvf\", tar_path], shell=False)\n    else:\n        with utils.cd(REPO_ROOT):\n            run(f\"make -j{multiprocessing.cpu_count()} -s install\")\n\n\ndef report_initial_version(config):\n    for port in config.node_name_to_ports.values():\n        actual_citus_version = get_actual_citus_version(config.bindir, port)\n        print(\"port:{} citus version {}\".format(port, actual_citus_version))\n\n\ndef get_version_number(version):\n    return re.findall(r\"\\d+.\\d+\", version)[0]\n\n\ndef get_actual_citus_version(pg_path, port):\n    citus_version = utils.psql_capture(pg_path, port, CITUS_VERSION_SQL)\n    citus_version = citus_version.decode(\"utf-8\")\n    return get_version_number(citus_version)\n\n\ndef run_test_on_coordinator(config, schedule):\n    common.run_pg_regress(\n        config.bindir, config.pg_srcdir, config.coordinator_port(), schedule\n    )\n\n\ndef remove_citus(tar_path):\n    if tar_path:\n        with utils.cd(\"/\"):\n            remove_tar_files(tar_path)\n\n\ndef remove_tar_files(tar_path):\n    ps = subprocess.Popen((\"tar\", \"tf\", tar_path), stdout=subprocess.PIPE)\n    subprocess.check_output((\"xargs\", \"rm\", \"-v\"), stdin=ps.stdout)\n    ps.wait()\n\n\ndef restart_databases(pg_path, rel_data_path, mixed_mode, config):\n    for node_name in config.node_name_to_ports.keys():\n        if mixed_mode and config.node_name_to_ports[node_name] in (\n            config.chosen_random_worker_port,\n            config.coordinator_port(),\n        ):\n            continue\n        abs_data_path = os.path.abspath(os.path.join(rel_data_path, node_name))\n        restart_database(\n            pg_path=pg_path,\n            abs_data_path=abs_data_path,\n            node_name=node_name,\n            node_ports=config.node_name_to_ports,\n            logfile_prefix=config.name,\n        )\n\n\ndef restart_database(pg_path, abs_data_path, node_name, node_ports, logfile_prefix):\n    command = [\n        os.path.join(pg_path, \"pg_ctl\"),\n        \"restart\",\n        \"--pgdata\",\n        abs_data_path,\n        \"-U\",\n        USER,\n        \"-o\",\n        \"-p {}\".format(node_ports[node_name]),\n        \"--log\",\n        os.path.join(abs_data_path, common.logfile_name(logfile_prefix, node_name)),\n    ]\n    subprocess.run(command, check=True)\n\n\ndef run_alter_citus(pg_path, mixed_mode, config):\n    for port in config.node_name_to_ports.values():\n        if mixed_mode and port in (\n            config.chosen_random_worker_port,\n            config.coordinator_port(),\n        ):\n            continue\n        utils.psql(pg_path, port, \"ALTER EXTENSION citus UPDATE;\")\n\n\ndef verify_upgrade(config, mixed_mode, node_ports):\n    for port in node_ports:\n        actual_citus_version = get_actual_citus_version(config.bindir, port)\n        expected_citus_version = MASTER_VERSION\n        if expected_citus_version != actual_citus_version and not (\n            mixed_mode\n            and port in (config.chosen_random_worker_port, config.coordinator_port())\n        ):\n            print(\n                \"port: {} citus version {} expected {}\".format(\n                    port, actual_citus_version, expected_citus_version\n                )\n            )\n            sys.exit(1)\n        else:\n            print(\"port:{} citus version {}\".format(port, actual_citus_version))\n\n\ndef get_before_upgrade_schedule(mixed_mode):\n    if mixed_mode:\n        return MIXED_BEFORE_CITUS_UPGRADE_SCHEDULE\n    else:\n        return BEFORE_CITUS_UPGRADE_COORD_SCHEDULE\n\n\ndef get_after_upgrade_schedule(mixed_mode):\n    if mixed_mode:\n        return MIXED_AFTER_CITUS_UPGRADE_SCHEDULE\n    else:\n        return AFTER_CITUS_UPGRADE_COORD_SCHEDULE\n\n\ndef generate_citus_tarball(citus_version):\n    tmp_dir = \"tmp_citus_tarballs\"\n    citus_old_tarpath = os.path.abspath(\n        os.path.join(tmp_dir, f\"install-pg{PG_MAJOR_VERSION}-citus{citus_version}.tar\")\n    )\n\n    common.initialize_temp_dir_if_not_exists(tmp_dir)\n    dirpath = os.path.dirname(os.path.realpath(__file__))\n    local_script_path = os.path.join(dirpath, \"generate_citus_tarballs.sh\")\n    with utils.cd(tmp_dir):\n        subprocess.check_call([local_script_path, str(PG_MAJOR_VERSION), citus_version])\n\n    return citus_old_tarpath\n\n\nif __name__ == \"__main__\":\n    args = docopt(__doc__, version=\"citus_upgrade_test\")\n    if not CI:\n        citus_tarball_path = generate_citus_tarball(args[\"--citus-old-version\"])\n        config = CitusUpgradeConfig(args, citus_tarball_path, None)\n    else:\n        config = CitusUpgradeConfig(\n            args, args[\"--citus-pre-tar\"], args[\"--citus-post-tar\"]\n        )\n\n    main(config)\n"
  },
  {
    "path": "src/test/regress/citus_tests/upgrade/generate_citus_tarballs.sh",
    "content": "#!/bin/bash\n\nset -euxo pipefail\n\npg_version=$1\ncitus_old_version=$2\n\nbase=\"$(pwd)\"\n\ninstall_citus_and_tar() {\n    # do everything in a subdirectory to avoid clutter in current directory\n    mkdir -p \"${builddir}\" && cd \"${builddir}\"\n\n    \"${citus_dir}/configure\"\n\n    installdir=\"${builddir}/install\"\n    make \"-j$(nproc)\" && mkdir -p \"${installdir}\" && make DESTDIR=\"${installdir}\" install\n\n    cd \"${installdir}\" && find . -type f -print >\"${builddir}/files.lst\"\n\n    tar cvf \"${basedir}/install-pg${pg_version}-citus${citus_version}.tar\" $(cat \"${builddir}\"/files.lst)\n    mv \"${basedir}/install-pg${pg_version}-citus${citus_version}.tar\" \"${base}/install-pg${pg_version}-citus${citus_version}.tar\"\n\n    cd \"${builddir}\" && rm -rf install files.lst && make clean\n}\n\nbuild_ext() {\n    citus_version=\"$1\"\n    # If tarball already exsists we're good\n    if [ -f \"${base}/install-pg${pg_version}-citus${citus_version}.tar\" ]; then\n        return\n    fi\n\n    basedir=\"${base}/${citus_version}\"\n\n    rm -rf \"${basedir}\"\n    mkdir -p \"${basedir}\"\n    cd \"${basedir}\"\n    citus_dir=${basedir}/citus_$citus_version\n    git clone --branch \"$citus_version\" https://github.com/citusdata/citus.git --depth 1 citus_\"$citus_version\"\n    builddir=\"${basedir}/build\"\n\n    install_citus_and_tar\n}\n\nbuild_ext \"${citus_old_version}\"\n"
  },
  {
    "path": "src/test/regress/citus_tests/upgrade/pg_upgrade_test.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\"upgrade_test\nUsage:\n    upgrade_test --old-bindir=<old-bindir> --new-bindir=<new-bindir> --pgxsdir=<pgxsdir> [--test-with-columnar]\n\nOptions:\n    --old-bindir=<old-bindir>              The old PostgreSQL executable directory(ex: '~/.pgenv/pgsql-10.4/bin')\n    --new-bindir=<new-bindir>              The new PostgreSQL executable directory(ex: '~/.pgenv/pgsql-11.3/bin')\n    --pgxsdir=<pgxsdir>                    Path to the PGXS directory(ex: ~/.pgenv/src/postgresql-11.3)\n    --test-with-columnar                   Enable automatically creating citus_columnar extension\n\"\"\"\n\nimport atexit\nimport os\nimport subprocess\nimport sys\n\nfrom docopt import docopt\n\n# https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time/14132912#14132912\nsys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\n# ignore E402 because these imports require addition to path\nimport common  # noqa: E402\nimport utils  # noqa: E402\nfrom utils import USER  # noqa: E402\n\nfrom config import (  # noqa: E402\n    AFTER_PG_UPGRADE_WITH_COLUMNAR_SCHEDULE,\n    AFTER_PG_UPGRADE_WITHOUT_COLUMNAR_SCHEDULE,\n    BEFORE_PG_UPGRADE_WITH_COLUMNAR_SCHEDULE,\n    BEFORE_PG_UPGRADE_WITHOUT_COLUMNAR_SCHEDULE,\n    PGUpgradeConfig,\n)\n\n\ndef citus_prepare_pg_upgrade(pg_path, node_ports):\n    for port in node_ports:\n        utils.psql(pg_path, port, \"SELECT citus_prepare_pg_upgrade();\")\n\n\ndef perform_postgres_upgrade(\n    old_bindir, new_bindir, old_datadir, new_datadir, node_names\n):\n    for node_name in node_names:\n        base_new_data_path = os.path.abspath(new_datadir)\n        base_old_data_path = os.path.abspath(old_datadir)\n        with utils.cd(base_new_data_path):\n            abs_new_data_path = os.path.join(base_new_data_path, node_name)\n            abs_old_data_path = os.path.join(base_old_data_path, node_name)\n            command = [\n                os.path.join(new_bindir, \"pg_upgrade\"),\n                \"--username\",\n                USER,\n                \"--old-bindir\",\n                old_bindir,\n                \"--new-bindir\",\n                new_bindir,\n                \"--old-datadir\",\n                abs_old_data_path,\n                \"--new-datadir\",\n                abs_new_data_path,\n            ]\n            subprocess.run(command, check=True)\n\n\ndef citus_finish_pg_upgrade(pg_path, node_ports):\n    for port in node_ports:\n        utils.psql(pg_path, port, \"SELECT citus_finish_pg_upgrade();\")\n\n\ndef stop_all_databases(old_bindir, new_bindir, old_datadir, new_datadir, config):\n    common.stop_databases(\n        old_bindir, old_datadir, config.node_name_to_ports, config.name\n    )\n    common.stop_databases(\n        new_bindir, new_datadir, config.node_name_to_ports, config.name\n    )\n\n\ndef main(config):\n    common.initialize_temp_dir(config.temp_dir)\n    common.initialize_citus_cluster(\n        config.old_bindir, config.old_datadir, config.settings, config\n    )\n    common.run_pg_regress(\n        config.old_bindir,\n        config.pg_srcdir,\n        config.coordinator_port(),\n        (\n            BEFORE_PG_UPGRADE_WITH_COLUMNAR_SCHEDULE\n            if config.test_with_columnar\n            else BEFORE_PG_UPGRADE_WITHOUT_COLUMNAR_SCHEDULE\n        ),\n    )\n    common.run_pg_regress(\n        config.old_bindir,\n        config.pg_srcdir,\n        config.coordinator_port(),\n        (\n            AFTER_PG_UPGRADE_WITH_COLUMNAR_SCHEDULE\n            if config.test_with_columnar\n            else AFTER_PG_UPGRADE_WITHOUT_COLUMNAR_SCHEDULE\n        ),\n    )\n\n    citus_prepare_pg_upgrade(config.old_bindir, config.node_name_to_ports.values())\n    # prepare should be idempotent, calling it a second time should never fail.\n    citus_prepare_pg_upgrade(config.old_bindir, config.node_name_to_ports.values())\n    common.stop_databases(\n        config.old_bindir, config.old_datadir, config.node_name_to_ports, config.name\n    )\n\n    common.initialize_db_for_cluster(\n        config.new_bindir,\n        config.new_datadir,\n        config.settings,\n        config.node_name_to_ports.keys(),\n    )\n    perform_postgres_upgrade(\n        config.old_bindir,\n        config.new_bindir,\n        config.old_datadir,\n        config.new_datadir,\n        config.node_name_to_ports.keys(),\n    )\n    common.start_databases(\n        config.new_bindir,\n        config.new_datadir,\n        config.node_name_to_ports,\n        config.name,\n        {},\n    )\n    citus_finish_pg_upgrade(config.new_bindir, config.node_name_to_ports.values())\n\n    common.run_pg_regress(\n        config.new_bindir,\n        config.pg_srcdir,\n        config.coordinator_port(),\n        (\n            AFTER_PG_UPGRADE_WITH_COLUMNAR_SCHEDULE\n            if config.test_with_columnar\n            else AFTER_PG_UPGRADE_WITHOUT_COLUMNAR_SCHEDULE\n        ),\n    )\n\n\nif __name__ == \"__main__\":\n    config = PGUpgradeConfig(docopt(__doc__, version=\"upgrade_test\"))\n    atexit.register(\n        stop_all_databases,\n        config.old_bindir,\n        config.new_bindir,\n        config.old_datadir,\n        config.new_datadir,\n        config,\n    )\n    main(config)\n"
  },
  {
    "path": "src/test/regress/citus_tests/utils.py",
    "content": "import os\nimport subprocess\n\nUSER = \"postgres\"\n\n\ndef psql(pg_path, port, command):\n    return subprocess.run(\n        [\n            os.path.join(pg_path, \"psql\"),\n            \"-U\",\n            USER,\n            \"-p\",\n            str(port),\n            \"-c\",\n            command,\n            \"-P\",\n            \"pager=off\",\n            \"--no-psqlrc\",\n        ],\n        check=True,\n    )\n\n\ndef psql_capture(pg_path, port, command):\n    return subprocess.check_output(\n        [\n            os.path.join(pg_path, \"psql\"),\n            \"-U\",\n            USER,\n            \"-p\",\n            str(port),\n            \"-c\",\n            command,\n            \"-P\",\n            \"pager=off\",\n            \"--no-psqlrc\",\n            \"--tuples-only\",\n        ],\n    )\n\n\n# Taken from https://stackoverflow.com/questions/431684/how-do-i-change-directory-cd-in-python/13197763#13197763\n\n\nclass cd(object):\n    \"\"\"Context manager for changing the current working directory\"\"\"\n\n    def __init__(self, newPath):\n        self.newPath = os.path.expanduser(newPath)\n\n    def __enter__(self):\n        self.savedPath = os.getcwd()\n        print(self.savedPath)\n        os.chdir(self.newPath)\n\n    def __exit__(self, etype, value, traceback):\n        os.chdir(self.savedPath)\n"
  },
  {
    "path": "src/test/regress/columnar_isolation_schedule",
    "content": "test: columnar_write_concurrency columnar_write_concurrency_index\ntest: columnar_vacuum_vs_insert\ntest: columnar_temp_tables\ntest: columnar_index_concurrency\n"
  },
  {
    "path": "src/test/regress/columnar_schedule",
    "content": "test: columnar_test_helpers\n\ntest: columnar_create\ntest: columnar_load\ntest: columnar_query\ntest: columnar_first_row_number\ntest: columnar_analyze\ntest: columnar_data_types\ntest: columnar_drop\ntest: columnar_indexes\ntest: columnar_fallback_scan columnar_paths\ntest: columnar_partitioning\ntest: columnar_permissions\ntest: columnar_empty\ntest: columnar_insert\ntest: columnar_update_delete\ntest: columnar_cursor\ntest: columnar_copyto\ntest: columnar_alter\ntest: columnar_alter_set_type\ntest: columnar_lz4 columnar_zstd\ntest: columnar_rollback\ntest: columnar_truncate\ntest: columnar_vacuum\ntest: columnar_clean\ntest: columnar_types_without_comparison\ntest: columnar_chunk_filtering\ntest: columnar_join\ntest: columnar_pg15\ntest: columnar_trigger\ntest: columnar_tableoptions\ntest: columnar_recursive\ntest: columnar_transactions\ntest: columnar_matview\ntest: columnar_memory\n"
  },
  {
    "path": "src/test/regress/create_schedule",
    "content": "test: intermediate_result_pruning_create\ntest: prepared_statements_create_load ch_benchmarks_create_load\ntest: dropped_columns_create_load distributed_planning_create_load\ntest: local_dist_join_load nested_execution_create\ntest: partitioned_indexes_create\ntest: connectivity_checks\ntest: schemas_create\ntest: views_create\ntest: sequences_create\ntest: index_create\ntest: function_create\ntest: arbitrary_configs_truncate_create\ntest: arbitrary_configs_truncate_cascade_create\ntest: arbitrary_configs_truncate_partition_create\ntest: arbitrary_configs_alter_table_add_constraint_without_name_create\ntest: merge_arbitrary_create\ntest: arbitrary_configs_router_create\n"
  },
  {
    "path": "src/test/regress/data/ads.csv",
    "content": "474,8,59,Accuracy,https://placehold.it/600x100.png,http://wunsch.com/wilson.corwin,0,0,2017-06-13 16:42:34.608325,2017-06-13 16:42:34.67835\n475,8,59,Mind Blast,https://placehold.it/600x100.png,http://wunsch.com/valentina,0,0,2017-06-13 16:42:34.691426,2017-06-13 16:42:34.769046\n476,8,59,Telekinesis,https://placehold.it/600x100.png,http://wunsch.com/emmet.bogisich,0,0,2017-06-13 16:42:34.78347,2017-06-13 16:42:34.842037\n477,8,59,Cloaking,https://placehold.it/600x100.png,http://wunsch.com/lottie.fahey,0,0,2017-06-13 16:42:34.856078,2017-06-13 16:42:34.945625\n478,8,59,Sub-Mariner,https://placehold.it/600x100.png,http://wunsch.com/percival,0,0,2017-06-13 16:42:34.961286,2017-06-13 16:42:35.01756\n479,8,59,Human physical perfection,https://placehold.it/600x100.png,http://wunsch.com/kelly,0,0,2017-06-13 16:42:35.03094,2017-06-13 16:42:35.111088\n480,8,59,Radiation Absorption,https://placehold.it/600x100.png,http://wunsch.com/dudley.towne,0,0,2017-06-13 16:42:35.125719,2017-06-13 16:42:35.194242\n481,8,60,Time Travel,https://placehold.it/600x100.png,http://tromp.co/cecilia.bernier,0,0,2017-06-13 16:42:35.234614,2017-06-13 16:42:35.315544\n482,8,60,Power Augmentation,https://placehold.it/600x100.png,http://tromp.co/josiah,0,0,2017-06-13 16:42:35.330366,2017-06-13 16:42:35.381389\n483,8,60,Size Changing,https://placehold.it/600x100.png,http://tromp.co/nichole.lindgren,0,0,2017-06-13 16:42:35.395987,2017-06-13 16:42:35.471563\n484,8,60,Hydrokinesis,https://placehold.it/600x100.png,http://tromp.co/anastacio,0,0,2017-06-13 16:42:35.490591,2017-06-13 16:42:35.576482\n485,8,60,Animal Oriented Powers,https://placehold.it/600x100.png,http://tromp.co/adeline_prosacco,0,0,2017-06-13 16:42:35.59005,2017-06-13 16:42:35.65499\n486,8,60,Vision - Thermal,https://placehold.it/600x100.png,http://tromp.co/jamaal.langosh,0,0,2017-06-13 16:42:35.670428,2017-06-13 16:42:35.754608\n487,8,60,Entropy Projection,https://placehold.it/600x100.png,http://tromp.co/je_ullrich,0,0,2017-06-13 16:42:35.767417,2017-06-13 16:42:35.832125\n488,8,60,Cold Resistance,https://placehold.it/600x100.png,http://tromp.co/ashly,0,0,2017-06-13 16:42:35.846377,2017-06-13 16:42:35.922403\n489,8,60,Enhanced Smell,https://placehold.it/600x100.png,http://tromp.co/georgianna.champlin,0,0,2017-06-13 16:42:35.935626,2017-06-13 16:42:35.997004\n490,8,60,Latent Abilities,https://placehold.it/600x100.png,http://tromp.co/elyse,0,0,2017-06-13 16:42:36.011106,2017-06-13 16:42:36.079066\n491,8,61,Matter Absorption,https://placehold.it/600x100.png,http://pourosmclaughlin.info/isaac,0,0,2017-06-13 16:42:36.112581,2017-06-13 16:42:36.17948\n492,8,61,Separation,https://placehold.it/600x100.png,http://pourosmclaughlin.info/dallin.kutch,0,0,2017-06-13 16:42:36.195558,2017-06-13 16:42:36.263427\n493,8,61,Omnilingualism,https://placehold.it/600x100.png,http://pourosmclaughlin.info/sarai.blick,0,0,2017-06-13 16:42:36.277127,2017-06-13 16:42:36.344861\n494,8,61,Enhanced Senses,https://placehold.it/600x100.png,http://pourosmclaughlin.info/monique.paucek,0,0,2017-06-13 16:42:36.360244,2017-06-13 16:42:36.415685\n495,8,61,Web Creation,https://placehold.it/600x100.png,http://pourosmclaughlin.info/erick,0,0,2017-06-13 16:42:36.431581,2017-06-13 16:42:36.498975\n496,8,61,Invulnerability,https://placehold.it/600x100.png,http://pourosmclaughlin.info/lucinda.keeling,0,0,2017-06-13 16:42:36.513616,2017-06-13 16:42:36.591947\n497,8,61,Phasing,https://placehold.it/600x100.png,http://pourosmclaughlin.info/palma.waters,0,0,2017-06-13 16:42:36.606594,2017-06-13 16:42:36.680658\n498,8,62,Longevity,https://placehold.it/600x100.png,http://casper.co/helene.tillman,0,0,2017-06-13 16:42:36.706329,2017-06-13 16:42:36.773721\n499,8,62,Ability Shift,https://placehold.it/600x100.png,http://casper.co/myrtice_pouros,0,0,2017-06-13 16:42:36.787875,2017-06-13 16:42:36.828517\n500,8,62,Symbiote Costume,https://placehold.it/600x100.png,http://casper.co/meaghan,0,0,2017-06-13 16:42:36.843316,2017-06-13 16:42:36.901191\n501,8,62,Enhanced Smell,https://placehold.it/600x100.png,http://casper.co/carmine_hickle,0,0,2017-06-13 16:42:36.914554,2017-06-13 16:42:36.983131\n502,8,62,Vaporising Beams,https://placehold.it/600x100.png,http://casper.co/joshuah_barton,0,0,2017-06-13 16:42:36.99614,2017-06-13 16:42:37.039987\n503,8,62,Changing Armor,https://placehold.it/600x100.png,http://casper.co/manuela,0,0,2017-06-13 16:42:37.053766,2017-06-13 16:42:37.122629\n504,8,63,Telepathy,https://placehold.it/600x100.png,http://kerlukegibson.info/karine,0,0,2017-06-13 16:42:37.14871,2017-06-13 16:42:37.229672\n505,8,63,Energy Manipulation,https://placehold.it/600x100.png,http://kerlukegibson.info/freeman.koepp,0,0,2017-06-13 16:42:37.245137,2017-06-13 16:42:37.288871\n506,8,63,Orbing,https://placehold.it/600x100.png,http://kerlukegibson.info/nathanael,0,0,2017-06-13 16:42:37.304128,2017-06-13 16:42:37.370034\n507,8,63,Mind Control,https://placehold.it/600x100.png,http://kerlukegibson.info/harvey_batz,0,0,2017-06-13 16:42:37.385162,2017-06-13 16:42:37.462222\n508,8,63,Physical Anomaly,https://placehold.it/600x100.png,http://kerlukegibson.info/brannon.dickens,0,0,2017-06-13 16:42:37.476992,2017-06-13 16:42:37.544666\n509,8,63,Physical Anomaly,https://placehold.it/600x100.png,http://kerlukegibson.info/wilbert,0,0,2017-06-13 16:42:37.55834,2017-06-13 16:42:37.631954\n510,8,63,Vision - Night,https://placehold.it/600x100.png,http://kerlukegibson.info/jamarcus.graham,0,0,2017-06-13 16:42:37.646964,2017-06-13 16:42:37.72198\n511,8,63,Cryokinesis,https://placehold.it/600x100.png,http://kerlukegibson.info/tyson,0,0,2017-06-13 16:42:37.737892,2017-06-13 16:42:37.793224\n512,8,63,Weapon-based Powers,https://placehold.it/600x100.png,http://kerlukegibson.info/kasandra,0,0,2017-06-13 16:42:37.80835,2017-06-13 16:42:37.883791\n513,8,64,Vision - Infrared,https://placehold.it/600x100.png,http://sawaynkris.name/brent.mccullough,0,0,2017-06-13 16:42:37.913044,2017-06-13 16:42:37.972777\n514,8,64,Wishing,https://placehold.it/600x100.png,http://sawaynkris.name/scotty.torphy,0,0,2017-06-13 16:42:37.987515,2017-06-13 16:42:38.046315\n515,8,64,Clairvoyance,https://placehold.it/600x100.png,http://sawaynkris.name/jeremy.ward,0,0,2017-06-13 16:42:38.063407,2017-06-13 16:42:38.142325\n1,1,1,Astral Projection,https://placehold.it/600x100.png,http://ullrichreynolds.info/miouri,0,0,2017-06-13 16:41:52.743418,2017-06-13 16:41:52.821056\n2,1,1,Endurance,https://placehold.it/600x100.png,http://ullrichreynolds.info/ally,0,0,2017-06-13 16:41:52.842525,2017-06-13 16:41:52.896993\n3,1,1,Self-Sustenance,https://placehold.it/600x100.png,http://ullrichreynolds.info/kyler_kaulke,0,0,2017-06-13 16:41:52.91236,2017-06-13 16:41:52.976566\n4,1,1,Flight,https://placehold.it/600x100.png,http://ullrichreynolds.info/earl,0,0,2017-06-13 16:41:52.992233,2017-06-13 16:41:53.040091\n5,1,1,Web Creation,https://placehold.it/600x100.png,http://ullrichreynolds.info/lonie,0,0,2017-06-13 16:41:53.062708,2017-06-13 16:41:53.145829\n6,1,1,Enhanced Senses,https://placehold.it/600x100.png,http://ullrichreynolds.info/fred,0,0,2017-06-13 16:41:53.161932,2017-06-13 16:41:53.239383\n7,1,1,Psionic Powers,https://placehold.it/600x100.png,http://ullrichreynolds.info/emil_cole,0,0,2017-06-13 16:41:53.255107,2017-06-13 16:41:53.326385\n8,1,2,Qwardian Power Ring,https://placehold.it/600x100.png,http://gottlieb.org/janelle,0,0,2017-06-13 16:41:53.356046,2017-06-13 16:41:53.417944\n9,1,2,Photokinesis,https://placehold.it/600x100.png,http://gottlieb.org/kamron,0,0,2017-06-13 16:41:53.431432,2017-06-13 16:41:53.502793\n10,1,2,Orbing,https://placehold.it/600x100.png,http://gottlieb.org/stefan_cruickshank,0,0,2017-06-13 16:41:53.51603,2017-06-13 16:41:53.564467\n11,1,2,Vision - Infrared,https://placehold.it/600x100.png,http://gottlieb.org/nyah.yost,0,0,2017-06-13 16:41:53.579014,2017-06-13 16:41:53.641534\n12,1,2,Magic,https://placehold.it/600x100.png,http://gottlieb.org/abraham_vonrueden,0,0,2017-06-13 16:41:53.657284,2017-06-13 16:41:53.719554\n13,1,2,Projection,https://placehold.it/600x100.png,http://gottlieb.org/johnnie,0,0,2017-06-13 16:41:53.73225,2017-06-13 16:41:53.807951\n14,1,3,Empathy,https://placehold.it/600x100.png,http://bartell.org/abigail,0,0,2017-06-13 16:41:53.831665,2017-06-13 16:41:53.899213\n15,1,3,Aerokinesis,https://placehold.it/600x100.png,http://bartell.org/clementine.harvey,0,0,2017-06-13 16:41:53.912298,2017-06-13 16:41:53.977334\n16,1,3,Qwardian Power Ring,https://placehold.it/600x100.png,http://bartell.org/lilyan,0,0,2017-06-13 16:41:53.991443,2017-06-13 16:41:54.057883\n17,1,3,Animal Oriented Powers,https://placehold.it/600x100.png,http://bartell.org/ayana_considine,0,0,2017-06-13 16:41:54.073168,2017-06-13 16:41:54.150101\n18,1,3,Lantern Power Ring,https://placehold.it/600x100.png,http://bartell.org/violet_white,0,0,2017-06-13 16:41:54.16378,2017-06-13 16:41:54.241198\n19,1,3,Force Fields,https://placehold.it/600x100.png,http://bartell.org/lewis,0,0,2017-06-13 16:41:54.257117,2017-06-13 16:41:54.303101\n20,1,3,Animal Control,https://placehold.it/600x100.png,http://bartell.org/louie,0,0,2017-06-13 16:41:54.320371,2017-06-13 16:41:54.393469\n21,1,4,Symbiote Costume,https://placehold.it/600x100.png,http://weberwilkinson.biz/rose,0,0,2017-06-13 16:41:54.423479,2017-06-13 16:41:54.479025\n22,1,4,Flight,https://placehold.it/600x100.png,http://weberwilkinson.biz/orland,0,0,2017-06-13 16:41:54.493675,2017-06-13 16:41:54.553835\n23,1,4,Astral Projection,https://placehold.it/600x100.png,http://weberwilkinson.biz/albin,0,0,2017-06-13 16:41:54.568075,2017-06-13 16:41:54.630676\n24,1,4,Apotheosis,https://placehold.it/600x100.png,http://weberwilkinson.biz/ralph.lesch,0,0,2017-06-13 16:41:54.646647,2017-06-13 16:41:54.730156\n25,1,4,Elemental Transmogrification,https://placehold.it/600x100.png,http://weberwilkinson.biz/haley_bradtke,0,0,2017-06-13 16:41:54.746373,2017-06-13 16:41:54.818255\n26,1,4,Physical Anomaly,https://placehold.it/600x100.png,http://weberwilkinson.biz/grayson_block,0,0,2017-06-13 16:41:54.833244,2017-06-13 16:41:54.886698\n27,1,5,Intangibility,https://placehold.it/600x100.png,http://tromphauck.com/aliyah_cremin,0,0,2017-06-13 16:41:54.916506,2017-06-13 16:41:54.984771\n28,1,5,Immortality,https://placehold.it/600x100.png,http://tromphauck.com/kariane_haley,0,0,2017-06-13 16:41:54.99844,2017-06-13 16:41:55.076524\n29,1,5,Grim Reaping,https://placehold.it/600x100.png,http://tromphauck.com/leila_mckenzie,0,0,2017-06-13 16:41:55.089888,2017-06-13 16:41:55.158945\n30,1,5,Vision - Cryo,https://placehold.it/600x100.png,http://tromphauck.com/kylee,0,0,2017-06-13 16:41:55.173877,2017-06-13 16:41:55.235255\n31,1,5,Omnitrix,https://placehold.it/600x100.png,http://tromphauck.com/athena_fritsch,0,0,2017-06-13 16:41:55.250178,2017-06-13 16:41:55.31541\n32,1,5,Density Control,https://placehold.it/600x100.png,http://tromphauck.com/jedediah_miller,0,0,2017-06-13 16:41:55.3313,2017-06-13 16:41:55.392267\n33,1,5,Regeneration,https://placehold.it/600x100.png,http://tromphauck.com/rodrick.windler,0,0,2017-06-13 16:41:55.407676,2017-06-13 16:41:55.486182\n34,1,6,Entropy Projection,https://placehold.it/600x100.png,http://pfeffer.info/gerson_mayer,0,0,2017-06-13 16:41:55.514712,2017-06-13 16:41:55.588303\n35,1,6,Chlorokinesis,https://placehold.it/600x100.png,http://pfeffer.info/larry,0,0,2017-06-13 16:41:55.603978,2017-06-13 16:41:55.671785\n36,1,6,Jump,https://placehold.it/600x100.png,http://pfeffer.info/declan_waters,0,0,2017-06-13 16:41:55.686352,2017-06-13 16:41:55.76281\n37,1,6,Matter Absorption,https://placehold.it/600x100.png,http://pfeffer.info/leda_klein,0,0,2017-06-13 16:41:55.777003,2017-06-13 16:41:55.844437\n38,1,6,Force Fields,https://placehold.it/600x100.png,http://pfeffer.info/jay,0,0,2017-06-13 16:41:55.857952,2017-06-13 16:41:55.915902\n39,1,6,Self-Sustenance,https://placehold.it/600x100.png,http://pfeffer.info/lennie,0,0,2017-06-13 16:41:55.928361,2017-06-13 16:41:56.005911\n40,1,6,Phasing,https://placehold.it/600x100.png,http://pfeffer.info/rachel,0,0,2017-06-13 16:41:56.01984,2017-06-13 16:41:56.092961\n41,1,6,Self-Sustenance,https://placehold.it/600x100.png,http://pfeffer.info/idella.pacocha,0,0,2017-06-13 16:41:56.109324,2017-06-13 16:41:56.172858\n42,1,6,Umbrakinesis,https://placehold.it/600x100.png,http://pfeffer.info/guillermo,0,0,2017-06-13 16:41:56.187473,2017-06-13 16:41:56.271745\n43,1,6,Melting,https://placehold.it/600x100.png,http://pfeffer.info/tre,0,0,2017-06-13 16:41:56.286616,2017-06-13 16:41:56.348768\n516,8,64,Vision - Night,https://placehold.it/600x100.png,http://sawaynkris.name/karley.schamberger,0,0,2017-06-13 16:42:38.155149,2017-06-13 16:42:38.213451\n517,8,64,Magic Resistance,https://placehold.it/600x100.png,http://sawaynkris.name/karolann,0,0,2017-06-13 16:42:38.227688,2017-06-13 16:42:38.309546\n518,8,64,Time Travel,https://placehold.it/600x100.png,http://sawaynkris.name/kareem_rodriguez,0,0,2017-06-13 16:42:38.322352,2017-06-13 16:42:38.40506\n519,8,64,Vitakinesis,https://placehold.it/600x100.png,http://sawaynkris.name/reinhold,0,0,2017-06-13 16:42:38.420398,2017-06-13 16:42:38.474493\n520,8,65,Orbing,https://placehold.it/600x100.png,http://altenwerth.biz/carson,0,0,2017-06-13 16:42:38.50261,2017-06-13 16:42:38.552952\n521,8,65,Enhanced Memory,https://placehold.it/600x100.png,http://altenwerth.biz/halle,0,0,2017-06-13 16:42:38.567391,2017-06-13 16:42:38.6301\n522,8,65,Gravitokinesis,https://placehold.it/600x100.png,http://altenwerth.biz/isaias.bauch,0,0,2017-06-13 16:42:38.645625,2017-06-13 16:42:38.7071\n523,8,65,Sonic Scream,https://placehold.it/600x100.png,http://altenwerth.biz/emelie,0,0,2017-06-13 16:42:38.721179,2017-06-13 16:42:38.800573\n524,8,65,Power Cosmic,https://placehold.it/600x100.png,http://altenwerth.biz/imani,0,0,2017-06-13 16:42:38.815318,2017-06-13 16:42:38.889674\n525,8,65,Intuitive aptitude,https://placehold.it/600x100.png,http://altenwerth.biz/macie,0,0,2017-06-13 16:42:38.905747,2017-06-13 16:42:38.969082\n44,1,7,Duplication,https://placehold.it/600x100.png,http://oconner.io/kamren,0,0,2017-06-13 16:41:56.380371,2017-06-13 16:41:56.440708\n45,1,7,Astral Trap,https://placehold.it/600x100.png,http://oconner.io/austyn,0,0,2017-06-13 16:41:56.456295,2017-06-13 16:41:56.520117\n46,1,7,Magic,https://placehold.it/600x100.png,http://oconner.io/leie,0,0,2017-06-13 16:41:56.537561,2017-06-13 16:41:56.623912\n47,1,7,Death Touch,https://placehold.it/600x100.png,http://oconner.io/francisco,0,0,2017-06-13 16:41:56.640652,2017-06-13 16:41:56.698514\n48,1,7,Astral Projection,https://placehold.it/600x100.png,http://oconner.io/shyann,0,0,2017-06-13 16:41:56.713923,2017-06-13 16:41:56.782984\n49,1,7,Duplication,https://placehold.it/600x100.png,http://oconner.io/kaela.white,0,0,2017-06-13 16:41:56.799672,2017-06-13 16:41:56.874138\n50,1,7,Cross-Dimensional Awareness,https://placehold.it/600x100.png,http://oconner.io/frederique,0,0,2017-06-13 16:41:56.889036,2017-06-13 16:41:56.955786\n51,1,7,Enhanced Memory,https://placehold.it/600x100.png,http://oconner.io/isaiah.stoltenberg,0,0,2017-06-13 16:41:56.972734,2017-06-13 16:41:57.043972\n263,5,34,Vision - Cryo,https://placehold.it/600x100.png,http://smith.name/colby,0,0,2017-06-13 16:42:16.162406,2017-06-13 16:42:16.207805\n264,5,34,Enhanced Hearing,https://placehold.it/600x100.png,http://smith.name/liliana.sawayn,0,0,2017-06-13 16:42:16.221792,2017-06-13 16:42:16.306247\n265,5,34,Underwater breathing,https://placehold.it/600x100.png,http://smith.name/kennith_jacobs,0,0,2017-06-13 16:42:16.319694,2017-06-13 16:42:16.385433\n266,5,34,Empathy,https://placehold.it/600x100.png,http://smith.name/johnnie_marks,0,0,2017-06-13 16:42:16.398534,2017-06-13 16:42:16.47872\n267,5,34,Photographic Reflexes,https://placehold.it/600x100.png,http://smith.name/arnulfo,0,0,2017-06-13 16:42:16.492138,2017-06-13 16:42:16.556796\n268,5,34,Hypnokinesis,https://placehold.it/600x100.png,http://smith.name/damian_abshire,0,0,2017-06-13 16:42:16.571833,2017-06-13 16:42:16.634833\n269,5,35,Vitakinesis,https://placehold.it/600x100.png,http://herman.name/maximilian,0,0,2017-06-13 16:42:16.660121,2017-06-13 16:42:16.746777\n270,5,35,Illumination,https://placehold.it/600x100.png,http://herman.name/ebony.stanton,0,0,2017-06-13 16:42:16.76317,2017-06-13 16:42:16.842035\n271,5,35,Flight,https://placehold.it/600x100.png,http://herman.name/jonas,0,0,2017-06-13 16:42:16.855602,2017-06-13 16:42:16.920217\n272,5,35,Latent Abilities,https://placehold.it/600x100.png,http://herman.name/jaydon,0,0,2017-06-13 16:42:16.935107,2017-06-13 16:42:16.999501\n273,5,35,Power Sense,https://placehold.it/600x100.png,http://herman.name/germaine_gerlach,0,0,2017-06-13 16:42:17.012844,2017-06-13 16:42:17.074256\n274,5,35,Animation,https://placehold.it/600x100.png,http://herman.name/cynthia_hackett,0,0,2017-06-13 16:42:17.086759,2017-06-13 16:42:17.145545\n275,5,35,Web Creation,https://placehold.it/600x100.png,http://herman.name/jerel,0,0,2017-06-13 16:42:17.159392,2017-06-13 16:42:17.224966\n276,5,35,Adaptation,https://placehold.it/600x100.png,http://herman.name/hilda,0,0,2017-06-13 16:42:17.239934,2017-06-13 16:42:17.311339\n277,5,35,Elasticity,https://placehold.it/600x100.png,http://herman.name/shanny.hills,0,0,2017-06-13 16:42:17.325049,2017-06-13 16:42:17.3875\n278,5,35,Electrokinesis,https://placehold.it/600x100.png,http://herman.name/eduardo,0,0,2017-06-13 16:42:17.404187,2017-06-13 16:42:17.46846\n279,5,36,Reality Warping,https://placehold.it/600x100.png,http://feil.biz/nya_little,0,0,2017-06-13 16:42:17.496607,2017-06-13 16:42:17.571994\n280,5,36,Animal Control,https://placehold.it/600x100.png,http://feil.biz/joshua_connelly,0,0,2017-06-13 16:42:17.586525,2017-06-13 16:42:17.649273\n281,5,36,Darkforce Manipulation,https://placehold.it/600x100.png,http://feil.biz/amparo_hilll,0,0,2017-06-13 16:42:17.674162,2017-06-13 16:42:17.752422\n282,5,36,Astral Projection,https://placehold.it/600x100.png,http://feil.biz/gardner_feil,0,0,2017-06-13 16:42:17.769774,2017-06-13 16:42:17.8617\n283,5,36,Thirstokinesis,https://placehold.it/600x100.png,http://feil.biz/dariana_rodriguez,0,0,2017-06-13 16:42:17.877147,2017-06-13 16:42:17.949145\n284,5,36,Banish,https://placehold.it/600x100.png,http://feil.biz/howell.collins,0,0,2017-06-13 16:42:17.966361,2017-06-13 16:42:18.027674\n285,5,36,Audiokinesis,https://placehold.it/600x100.png,http://feil.biz/kiara,0,0,2017-06-13 16:42:18.042808,2017-06-13 16:42:18.118722\n286,5,36,Hypnokinesis,https://placehold.it/600x100.png,http://feil.biz/benny_gibson,0,0,2017-06-13 16:42:18.131505,2017-06-13 16:42:18.186998\n287,5,36,Nova Force,https://placehold.it/600x100.png,http://feil.biz/lacy,0,0,2017-06-13 16:42:18.202092,2017-06-13 16:42:18.256864\n288,5,37,Super Breath,https://placehold.it/600x100.png,http://harberbarton.org/augusta_spinka,0,0,2017-06-13 16:42:18.281835,2017-06-13 16:42:18.335603\n289,5,37,Vision - Heat,https://placehold.it/600x100.png,http://harberbarton.org/mabel.king,0,0,2017-06-13 16:42:18.347493,2017-06-13 16:42:18.416212\n290,5,37,Vitakinesis,https://placehold.it/600x100.png,http://harberbarton.org/patience,0,0,2017-06-13 16:42:18.431689,2017-06-13 16:42:18.50215\n291,5,37,Stamina,https://placehold.it/600x100.png,http://harberbarton.org/zella_reynolds,0,0,2017-06-13 16:42:18.515605,2017-06-13 16:42:18.564853\n292,5,37,Vision - Telescopic,https://placehold.it/600x100.png,http://harberbarton.org/treie,0,0,2017-06-13 16:42:18.580649,2017-06-13 16:42:18.660398\n293,5,37,Longevity,https://placehold.it/600x100.png,http://harberbarton.org/emmie.miller,0,0,2017-06-13 16:42:18.674778,2017-06-13 16:42:18.735704\n294,5,37,Darkforce Manipulation,https://placehold.it/600x100.png,http://harberbarton.org/carter_runolfsdottir,0,0,2017-06-13 16:42:18.748656,2017-06-13 16:42:18.808957\n295,5,38,Thirstokinesis,https://placehold.it/600x100.png,http://wehner.info/briana,0,0,2017-06-13 16:42:18.836199,2017-06-13 16:42:18.900668\n296,5,38,Regeneration,https://placehold.it/600x100.png,http://wehner.info/jammie,0,0,2017-06-13 16:42:18.913762,2017-06-13 16:42:18.972692\n297,5,38,Force Fields,https://placehold.it/600x100.png,http://wehner.info/vella,0,0,2017-06-13 16:42:18.985338,2017-06-13 16:42:19.042965\n298,5,38,Reflexes,https://placehold.it/600x100.png,http://wehner.info/morgan,0,0,2017-06-13 16:42:19.05659,2017-06-13 16:42:19.126796\n299,5,38,Sonic Scream,https://placehold.it/600x100.png,http://wehner.info/courtney,0,0,2017-06-13 16:42:19.142045,2017-06-13 16:42:19.198281\n300,5,38,Illusions,https://placehold.it/600x100.png,http://wehner.info/rose,0,0,2017-06-13 16:42:19.214866,2017-06-13 16:42:19.2976\n301,5,39,Changing Armor,https://placehold.it/600x100.png,http://altenwerthfriesen.com/brielle.ferry,0,0,2017-06-13 16:42:19.323564,2017-06-13 16:42:19.391349\n302,5,39,Probability Manipulation,https://placehold.it/600x100.png,http://altenwerthfriesen.com/letha.kiehn,0,0,2017-06-13 16:42:19.405213,2017-06-13 16:42:19.498867\n303,5,39,Toxin and Disease Resistance,https://placehold.it/600x100.png,http://altenwerthfriesen.com/merl_rempel,0,0,2017-06-13 16:42:19.517393,2017-06-13 16:42:19.590909\n304,5,39,Thermokinesis,https://placehold.it/600x100.png,http://altenwerthfriesen.com/nia.kuhn,0,0,2017-06-13 16:42:19.606901,2017-06-13 16:42:19.687948\n305,5,39,Physical Anomaly,https://placehold.it/600x100.png,http://altenwerthfriesen.com/susan_lind,0,0,2017-06-13 16:42:19.704308,2017-06-13 16:42:19.763318\n306,5,39,Underwater breathing,https://placehold.it/600x100.png,http://altenwerthfriesen.com/liza_okeefe,0,0,2017-06-13 16:42:19.799245,2017-06-13 16:42:19.874997\n307,5,39,Radiation Immunity,https://placehold.it/600x100.png,http://altenwerthfriesen.com/ciara,0,0,2017-06-13 16:42:19.897113,2017-06-13 16:42:19.994026\n308,5,40,Absorption,https://placehold.it/600x100.png,http://simonis.co/adelia,0,0,2017-06-13 16:42:20.020978,2017-06-13 16:42:20.085306\n309,5,40,Lantern Power Ring,https://placehold.it/600x100.png,http://simonis.co/alva_balistreri,0,0,2017-06-13 16:42:20.099438,2017-06-13 16:42:20.162317\n310,5,40,Omnitrix,https://placehold.it/600x100.png,http://simonis.co/riley,0,0,2017-06-13 16:42:20.178052,2017-06-13 16:42:20.253952\n311,5,40,Power Nullifier,https://placehold.it/600x100.png,http://simonis.co/camren_wisoky,0,0,2017-06-13 16:42:20.269262,2017-06-13 16:42:20.341557\n312,5,40,Force Fields,https://placehold.it/600x100.png,http://simonis.co/karlie_zemlak,0,0,2017-06-13 16:42:20.356619,2017-06-13 16:42:20.425887\n313,5,40,Apotheosis,https://placehold.it/600x100.png,http://simonis.co/malcolm_streich,0,0,2017-06-13 16:42:20.441071,2017-06-13 16:42:20.505276\n314,5,40,Photokinesis,https://placehold.it/600x100.png,http://simonis.co/addison,0,0,2017-06-13 16:42:20.518554,2017-06-13 16:42:20.596769\n315,5,40,Molecular Dissipation,https://placehold.it/600x100.png,http://simonis.co/ernestina.larson,0,0,2017-06-13 16:42:20.609757,2017-06-13 16:42:20.661025\n316,5,40,Intangibility,https://placehold.it/600x100.png,http://simonis.co/heber_barton,0,0,2017-06-13 16:42:20.674742,2017-06-13 16:42:20.754247\n317,5,41,Energy Armor,https://placehold.it/600x100.png,http://mraz.info/meghan.nienow,0,0,2017-06-13 16:42:20.782879,2017-06-13 16:42:20.840814\n318,5,41,Toxikinesis,https://placehold.it/600x100.png,http://mraz.info/enoch_leuschke,0,0,2017-06-13 16:42:20.854318,2017-06-13 16:42:20.921452\n319,5,41,Death Touch,https://placehold.it/600x100.png,http://mraz.info/elisa.powlowski,0,0,2017-06-13 16:42:20.936985,2017-06-13 16:42:20.987241\n320,5,41,Hyperkinesis,https://placehold.it/600x100.png,http://mraz.info/myles.ohara,0,0,2017-06-13 16:42:21.003609,2017-06-13 16:42:21.079362\n321,5,41,Magic Resistance,https://placehold.it/600x100.png,http://mraz.info/talia,0,0,2017-06-13 16:42:21.09468,2017-06-13 16:42:21.177664\n322,5,41,Omnilingualism,https://placehold.it/600x100.png,http://mraz.info/ewald_bruen,0,0,2017-06-13 16:42:21.194168,2017-06-13 16:42:21.253242\n323,5,41,Mind Blast,https://placehold.it/600x100.png,http://mraz.info/salvatore.marvin,0,0,2017-06-13 16:42:21.265753,2017-06-13 16:42:21.327758\n324,5,41,Duplication,https://placehold.it/600x100.png,http://mraz.info/khalid.gleason,0,0,2017-06-13 16:42:21.341988,2017-06-13 16:42:21.408118\n325,5,41,Animal Control,https://placehold.it/600x100.png,http://mraz.info/simeon,0,0,2017-06-13 16:42:21.421917,2017-06-13 16:42:21.48722\n326,5,41,Telepathy Resistance,https://placehold.it/600x100.png,http://mraz.info/karli,0,0,2017-06-13 16:42:21.502431,2017-06-13 16:42:21.563172\n327,5,42,Insanity,https://placehold.it/600x100.png,http://hermann.net/kiana_watsica,0,0,2017-06-13 16:42:21.588969,2017-06-13 16:42:21.648471\n328,5,42,Omnipotence,https://placehold.it/600x100.png,http://hermann.net/elody_pfeffer,0,0,2017-06-13 16:42:21.662236,2017-06-13 16:42:21.747112\n329,5,42,Vision - Night,https://placehold.it/600x100.png,http://hermann.net/michael.keebler,0,0,2017-06-13 16:42:21.762926,2017-06-13 16:42:21.844281\n330,5,42,Weapons Master,https://placehold.it/600x100.png,http://hermann.net/vilma,0,0,2017-06-13 16:42:21.859198,2017-06-13 16:42:21.913085\n331,5,42,Radiation Control,https://placehold.it/600x100.png,http://hermann.net/hillard.olson,0,0,2017-06-13 16:42:21.937447,2017-06-13 16:42:22.016871\n332,5,42,Enhanced Sight,https://placehold.it/600x100.png,http://hermann.net/ettie,0,0,2017-06-13 16:42:22.035241,2017-06-13 16:42:22.081754\n333,5,42,Energy Manipulation,https://placehold.it/600x100.png,http://hermann.net/brandyn_flatley,0,0,2017-06-13 16:42:22.095659,2017-06-13 16:42:22.17818\n334,5,42,Enhanced Smell,https://placehold.it/600x100.png,http://hermann.net/christy.farrell,0,0,2017-06-13 16:42:22.192815,2017-06-13 16:42:22.263038\n335,5,42,Human physical perfection,https://placehold.it/600x100.png,http://hermann.net/chris,0,0,2017-06-13 16:42:22.277711,2017-06-13 16:42:22.34046\n195,4,26,Melting,https://placehold.it/600x100.png,http://reichertemard.info/nels.bailey,0,0,2017-06-13 16:42:09.926522,2017-06-13 16:42:09.98811\n196,4,26,Vision - Thermal,https://placehold.it/600x100.png,http://reichertemard.info/destiney.parker,0,0,2017-06-13 16:42:10.01493,2017-06-13 16:42:10.098757\n197,4,26,Cryokinesis,https://placehold.it/600x100.png,http://reichertemard.info/carmel.green,0,0,2017-06-13 16:42:10.115465,2017-06-13 16:42:10.171335\n198,4,26,Enhanced Senses,https://placehold.it/600x100.png,http://reichertemard.info/bertha,0,0,2017-06-13 16:42:10.184966,2017-06-13 16:42:10.272063\n199,4,26,Qwardian Power Ring,https://placehold.it/600x100.png,http://reichertemard.info/mack_schoen,0,0,2017-06-13 16:42:10.288014,2017-06-13 16:42:10.400737\n200,4,26,Molecular Combustion,https://placehold.it/600x100.png,http://reichertemard.info/tracey,0,0,2017-06-13 16:42:10.415436,2017-06-13 16:42:10.462692\n201,4,26,Hypnokinesis,https://placehold.it/600x100.png,http://reichertemard.info/jermey_homenick,0,0,2017-06-13 16:42:10.476775,2017-06-13 16:42:10.547303\n202,4,26,Size Changing,https://placehold.it/600x100.png,http://reichertemard.info/karson,0,0,2017-06-13 16:42:10.562942,2017-06-13 16:42:10.648992\n203,4,26,Spatial Awareness,https://placehold.it/600x100.png,http://reichertemard.info/aglae,0,0,2017-06-13 16:42:10.662146,2017-06-13 16:42:10.749766\n204,4,26,Reality Warping,https://placehold.it/600x100.png,http://reichertemard.info/trinity,0,0,2017-06-13 16:42:10.765629,2017-06-13 16:42:10.85367\n205,4,27,Vaporising Beams,https://placehold.it/600x100.png,http://brekke.info/gaetano_kreiger,0,0,2017-06-13 16:42:10.881735,2017-06-13 16:42:10.949966\n206,4,27,Death Touch,https://placehold.it/600x100.png,http://brekke.info/americo,0,0,2017-06-13 16:42:10.965366,2017-06-13 16:42:11.033852\n207,4,27,Entropy Projection,https://placehold.it/600x100.png,http://brekke.info/ezequiel,0,0,2017-06-13 16:42:11.046569,2017-06-13 16:42:11.104646\n208,4,27,Vision - X-Ray,https://placehold.it/600x100.png,http://brekke.info/eduardo,0,0,2017-06-13 16:42:11.120397,2017-06-13 16:42:11.180013\n209,4,27,Energy Armor,https://placehold.it/600x100.png,http://brekke.info/yadira,0,0,2017-06-13 16:42:11.19538,2017-06-13 16:42:11.250759\n210,4,27,Mind Control,https://placehold.it/600x100.png,http://brekke.info/isabell.legros,0,0,2017-06-13 16:42:11.264326,2017-06-13 16:42:11.324623\n211,4,27,Illumination,https://placehold.it/600x100.png,http://brekke.info/jacky.osinski,0,0,2017-06-13 16:42:11.341134,2017-06-13 16:42:11.428056\n212,4,28,Cold Resistance,https://placehold.it/600x100.png,http://nader.net/quincy.adams,0,0,2017-06-13 16:42:11.455535,2017-06-13 16:42:11.537865\n213,4,28,Animal Attributes,https://placehold.it/600x100.png,http://nader.net/craig,0,0,2017-06-13 16:42:11.552403,2017-06-13 16:42:11.631115\n214,4,28,Aerokinesis,https://placehold.it/600x100.png,http://nader.net/wade,0,0,2017-06-13 16:42:11.644744,2017-06-13 16:42:11.697\n215,4,28,Super Speed,https://placehold.it/600x100.png,http://nader.net/era.bahringer,0,0,2017-06-13 16:42:11.712336,2017-06-13 16:42:11.787007\n216,4,28,Immortality,https://placehold.it/600x100.png,http://nader.net/jaquan_auer,0,0,2017-06-13 16:42:11.801254,2017-06-13 16:42:11.864441\n217,4,28,Power Augmentation,https://placehold.it/600x100.png,http://nader.net/van,0,0,2017-06-13 16:42:11.876843,2017-06-13 16:42:11.944514\n218,4,28,Gravitokinesis,https://placehold.it/600x100.png,http://nader.net/esta,0,0,2017-06-13 16:42:11.958474,2017-06-13 16:42:12.035329\n219,4,28,Magnetokinesis,https://placehold.it/600x100.png,http://nader.net/percival,0,0,2017-06-13 16:42:12.050091,2017-06-13 16:42:12.113839\n220,4,28,Geokinesis,https://placehold.it/600x100.png,http://nader.net/paxton.fritsch,0,0,2017-06-13 16:42:12.128613,2017-06-13 16:42:12.186174\n221,4,29,Illumination,https://placehold.it/600x100.png,http://powlowski.info/melvin,0,0,2017-06-13 16:42:12.214896,2017-06-13 16:42:12.28574\n222,4,29,Telekinesis,https://placehold.it/600x100.png,http://powlowski.info/odie,0,0,2017-06-13 16:42:12.298268,2017-06-13 16:42:12.354219\n223,4,29,Molecular Immobilization,https://placehold.it/600x100.png,http://powlowski.info/leland,0,0,2017-06-13 16:42:12.365801,2017-06-13 16:42:12.480926\n224,4,29,Telepathy Resistance,https://placehold.it/600x100.png,http://powlowski.info/linnie_gerhold,0,0,2017-06-13 16:42:12.495242,2017-06-13 16:42:12.548984\n225,4,29,Thirstokinesis,https://placehold.it/600x100.png,http://powlowski.info/leopold.hayes,0,0,2017-06-13 16:42:12.563367,2017-06-13 16:42:12.626435\n226,4,29,Elemental Transmogrification,https://placehold.it/600x100.png,http://powlowski.info/turner.prohaska,0,0,2017-06-13 16:42:12.641204,2017-06-13 16:42:12.713979\n227,4,29,Sonic Scream,https://placehold.it/600x100.png,http://powlowski.info/audrey.frami,0,0,2017-06-13 16:42:12.731361,2017-06-13 16:42:12.800301\n228,4,29,Invisibility,https://placehold.it/600x100.png,http://powlowski.info/imogene_steuber,0,0,2017-06-13 16:42:12.819249,2017-06-13 16:42:12.902741\n229,4,29,Thermokinesis,https://placehold.it/600x100.png,http://powlowski.info/kelvin.schmitt,0,0,2017-06-13 16:42:12.921349,2017-06-13 16:42:13.001917\n230,4,29,The Force,https://placehold.it/600x100.png,http://powlowski.info/hollis,0,0,2017-06-13 16:42:13.019277,2017-06-13 16:42:13.096331\n231,4,30,Spatial Awareness,https://placehold.it/600x100.png,http://kovacek.biz/kiley_weimann,0,0,2017-06-13 16:42:13.128857,2017-06-13 16:42:13.19951\n232,4,30,Hypnokinesis,https://placehold.it/600x100.png,http://kovacek.biz/werner,0,0,2017-06-13 16:42:13.218297,2017-06-13 16:42:13.299542\n233,4,30,Omnitrix,https://placehold.it/600x100.png,http://kovacek.biz/bartholome,0,0,2017-06-13 16:42:13.314483,2017-06-13 16:42:13.396859\n234,4,30,Empathy,https://placehold.it/600x100.png,http://kovacek.biz/peter,0,0,2017-06-13 16:42:13.413567,2017-06-13 16:42:13.483935\n235,4,30,Agility,https://placehold.it/600x100.png,http://kovacek.biz/tatum_ebert,0,0,2017-06-13 16:42:13.500539,2017-06-13 16:42:13.582682\n236,4,30,Probability Manipulation,https://placehold.it/600x100.png,http://kovacek.biz/hillary,0,0,2017-06-13 16:42:13.599466,2017-06-13 16:42:13.695078\n237,4,30,Illumination,https://placehold.it/600x100.png,http://kovacek.biz/geovanny_lockman,0,0,2017-06-13 16:42:13.729134,2017-06-13 16:42:13.810826\n238,4,31,Probability Manipulation,https://placehold.it/600x100.png,http://hyattbosco.name/darrin,0,0,2017-06-13 16:42:13.852567,2017-06-13 16:42:13.966448\n239,4,31,Vision - Infrared,https://placehold.it/600x100.png,http://hyattbosco.name/edmond_feeney,0,0,2017-06-13 16:42:13.980191,2017-06-13 16:42:14.051847\n240,4,31,Shapeshifting,https://placehold.it/600x100.png,http://hyattbosco.name/mia,0,0,2017-06-13 16:42:14.06857,2017-06-13 16:42:14.12891\n241,4,31,Matter Absorption,https://placehold.it/600x100.png,http://hyattbosco.name/emmie,0,0,2017-06-13 16:42:14.143312,2017-06-13 16:42:14.229655\n242,4,31,Cross-Dimensional Travel,https://placehold.it/600x100.png,http://hyattbosco.name/ellie.moore,0,0,2017-06-13 16:42:14.245168,2017-06-13 16:42:14.319963\n243,4,31,Vitakinesis,https://placehold.it/600x100.png,http://hyattbosco.name/howard,0,0,2017-06-13 16:42:14.334896,2017-06-13 16:42:14.397326\n244,4,31,Astral Trap,https://placehold.it/600x100.png,http://hyattbosco.name/vita,0,0,2017-06-13 16:42:14.411435,2017-06-13 16:42:14.467397\n245,4,31,Geokinesis,https://placehold.it/600x100.png,http://hyattbosco.name/antonetta_schamberger,0,0,2017-06-13 16:42:14.480336,2017-06-13 16:42:14.551423\n246,4,31,Entropy Projection,https://placehold.it/600x100.png,http://hyattbosco.name/frida,0,0,2017-06-13 16:42:14.564282,2017-06-13 16:42:14.637422\n247,4,31,Agility,https://placehold.it/600x100.png,http://hyattbosco.name/cyril_douglas,0,0,2017-06-13 16:42:14.651284,2017-06-13 16:42:14.716513\n248,4,32,Elasticity,https://placehold.it/600x100.png,http://macgyverdach.name/lesley.leuschke,0,0,2017-06-13 16:42:14.744501,2017-06-13 16:42:14.801894\n249,4,32,Elasticity,https://placehold.it/600x100.png,http://macgyverdach.name/simeon.renner,0,0,2017-06-13 16:42:14.815489,2017-06-13 16:42:14.881319\n250,4,32,Energy Blasts,https://placehold.it/600x100.png,http://macgyverdach.name/eusebio,0,0,2017-06-13 16:42:14.894441,2017-06-13 16:42:14.951624\n251,4,32,Jump,https://placehold.it/600x100.png,http://macgyverdach.name/mohamed_mcdermott,0,0,2017-06-13 16:42:14.964376,2017-06-13 16:42:15.052968\n252,4,32,Energy Constructs,https://placehold.it/600x100.png,http://macgyverdach.name/jewell,0,0,2017-06-13 16:42:15.066124,2017-06-13 16:42:15.123939\n253,4,32,Magic,https://placehold.it/600x100.png,http://macgyverdach.name/emie,0,0,2017-06-13 16:42:15.13865,2017-06-13 16:42:15.194685\n254,4,32,Resurrection,https://placehold.it/600x100.png,http://macgyverdach.name/halie_heaney,0,0,2017-06-13 16:42:15.209184,2017-06-13 16:42:15.292166\n255,4,32,Telepathy,https://placehold.it/600x100.png,http://macgyverdach.name/wilhelmine,0,0,2017-06-13 16:42:15.308347,2017-06-13 16:42:15.384001\n256,4,32,Echokinesis,https://placehold.it/600x100.png,http://macgyverdach.name/heaven_flatley,0,0,2017-06-13 16:42:15.397995,2017-06-13 16:42:15.463031\n257,4,33,Cryokinesis,https://placehold.it/600x100.png,http://christiansen.biz/humberto_bechtelar,0,0,2017-06-13 16:42:15.489538,2017-06-13 16:42:15.56185\n258,4,33,Molecular Combustion,https://placehold.it/600x100.png,http://christiansen.biz/aron_macgyver,0,0,2017-06-13 16:42:15.577872,2017-06-13 16:42:15.65217\n259,4,33,Hypnokinesis,https://placehold.it/600x100.png,http://christiansen.biz/tatum_ankunding,0,0,2017-06-13 16:42:15.665302,2017-06-13 16:42:15.70958\n260,4,33,Orbing,https://placehold.it/600x100.png,http://christiansen.biz/emery,0,0,2017-06-13 16:42:15.728428,2017-06-13 16:42:15.798829\n261,4,33,Vision - X-Ray,https://placehold.it/600x100.png,http://christiansen.biz/michelle_cruickshank,0,0,2017-06-13 16:42:15.812196,2017-06-13 16:42:15.852943\n262,4,33,Molecular Combustion,https://placehold.it/600x100.png,http://christiansen.biz/danika_ratke,0,0,2017-06-13 16:42:15.865508,2017-06-13 16:42:15.929214\n403,7,51,Durability,https://placehold.it/600x100.png,http://klinghansen.com/ron,0,0,2017-06-13 16:42:28.604309,2017-06-13 16:42:28.678298\n404,7,51,Electrokinesis,https://placehold.it/600x100.png,http://klinghansen.com/jamison.murray,0,0,2017-06-13 16:42:28.693114,2017-06-13 16:42:28.73854\n405,7,51,Hydrokinesis,https://placehold.it/600x100.png,http://klinghansen.com/carmen_murray,0,0,2017-06-13 16:42:28.752645,2017-06-13 16:42:28.815855\n406,7,51,Ability Shift,https://placehold.it/600x100.png,http://klinghansen.com/alvera_murray,0,0,2017-06-13 16:42:28.829952,2017-06-13 16:42:28.882325\n407,7,51,Toxin and Disease Resistance,https://placehold.it/600x100.png,http://klinghansen.com/reyna_greenfelder,0,0,2017-06-13 16:42:28.897239,2017-06-13 16:42:28.965269\n408,7,51,Atmokinesis,https://placehold.it/600x100.png,http://klinghansen.com/caroline,0,0,2017-06-13 16:42:28.979254,2017-06-13 16:42:29.033103\n409,7,51,Radiation Immunity,https://placehold.it/600x100.png,http://klinghansen.com/deron,0,0,2017-06-13 16:42:29.047041,2017-06-13 16:42:29.102933\n410,7,51,Power Absorption,https://placehold.it/600x100.png,http://klinghansen.com/litzy,0,0,2017-06-13 16:42:29.115026,2017-06-13 16:42:29.19022\n411,7,51,Hydrokinesis,https://placehold.it/600x100.png,http://klinghansen.com/roscoe.thompson,0,0,2017-06-13 16:42:29.203687,2017-06-13 16:42:29.262901\n412,7,52,Molecular Dissipation,https://placehold.it/600x100.png,http://grady.co/genoveva.hartmann,0,0,2017-06-13 16:42:29.296123,2017-06-13 16:42:29.377637\n413,7,52,Portal Creation,https://placehold.it/600x100.png,http://grady.co/danika,0,0,2017-06-13 16:42:29.391003,2017-06-13 16:42:29.458785\n414,7,52,Stamina,https://placehold.it/600x100.png,http://grady.co/salma,0,0,2017-06-13 16:42:29.472321,2017-06-13 16:42:29.545044\n415,7,52,Geokinesis,https://placehold.it/600x100.png,http://grady.co/matt_klocko,0,0,2017-06-13 16:42:29.558831,2017-06-13 16:42:29.627347\n416,7,52,Cloaking,https://placehold.it/600x100.png,http://grady.co/otilia,0,0,2017-06-13 16:42:29.642138,2017-06-13 16:42:29.716773\n417,7,52,Animation,https://placehold.it/600x100.png,http://grady.co/audreanne_connelly,0,0,2017-06-13 16:42:29.730007,2017-06-13 16:42:29.807748\n418,7,52,Molecular Combustion,https://placehold.it/600x100.png,http://grady.co/maybell_huels,0,0,2017-06-13 16:42:29.820471,2017-06-13 16:42:29.889149\n419,7,52,Photographic Reflexes,https://placehold.it/600x100.png,http://grady.co/ethelyn,0,0,2017-06-13 16:42:29.90302,2017-06-13 16:42:29.981066\n420,7,52,Radiation Immunity,https://placehold.it/600x100.png,http://grady.co/leilani,0,0,2017-06-13 16:42:29.996324,2017-06-13 16:42:30.053946\n421,7,53,Enhanced Senses,https://placehold.it/600x100.png,http://davistowne.biz/keaton,0,0,2017-06-13 16:42:30.080147,2017-06-13 16:42:30.155064\n422,7,53,Elasticity,https://placehold.it/600x100.png,http://davistowne.biz/isobel,0,0,2017-06-13 16:42:30.168813,2017-06-13 16:42:30.239331\n423,7,53,Adaptation,https://placehold.it/600x100.png,http://davistowne.biz/wendell.emmerich,0,0,2017-06-13 16:42:30.254269,2017-06-13 16:42:30.313076\n424,7,53,Vision - Night,https://placehold.it/600x100.png,http://davistowne.biz/jerome_cummings,0,0,2017-06-13 16:42:30.326348,2017-06-13 16:42:30.406846\n425,7,53,Animal Attributes,https://placehold.it/600x100.png,http://davistowne.biz/kaylie,0,0,2017-06-13 16:42:30.41931,2017-06-13 16:42:30.484838\n426,7,53,Size Changing,https://placehold.it/600x100.png,http://davistowne.biz/laria,0,0,2017-06-13 16:42:30.49803,2017-06-13 16:42:30.556733\n427,7,53,Portal Creation,https://placehold.it/600x100.png,http://davistowne.biz/eveline,0,0,2017-06-13 16:42:30.570974,2017-06-13 16:42:30.641847\n428,7,53,Enhanced Smell,https://placehold.it/600x100.png,http://davistowne.biz/patience_dach,0,0,2017-06-13 16:42:30.657014,2017-06-13 16:42:30.699382\n429,7,53,Enhanced Smell,https://placehold.it/600x100.png,http://davistowne.biz/geraldine.wolff,0,0,2017-06-13 16:42:30.714557,2017-06-13 16:42:30.77683\n430,7,53,Durability,https://placehold.it/600x100.png,http://davistowne.biz/arthur.reichert,0,0,2017-06-13 16:42:30.796571,2017-06-13 16:42:30.872621\n431,7,54,Self-Sustenance,https://placehold.it/600x100.png,http://skiles.co/darien,0,0,2017-06-13 16:42:30.900325,2017-06-13 16:42:30.981206\n432,7,54,Timeframe Control,https://placehold.it/600x100.png,http://skiles.co/mara.olson,0,0,2017-06-13 16:42:30.995774,2017-06-13 16:42:31.053294\n433,7,54,Cryokinesis,https://placehold.it/600x100.png,http://skiles.co/myrtle,0,0,2017-06-13 16:42:31.067833,2017-06-13 16:42:31.135892\n434,7,54,Gravitokinesis,https://placehold.it/600x100.png,http://skiles.co/eileen.carter,0,0,2017-06-13 16:42:31.150801,2017-06-13 16:42:31.210469\n435,7,54,Physical Anomaly,https://placehold.it/600x100.png,http://skiles.co/domenic,0,0,2017-06-13 16:42:31.224606,2017-06-13 16:42:31.282036\n436,7,54,Orbing,https://placehold.it/600x100.png,http://skiles.co/amelia,0,0,2017-06-13 16:42:31.296307,2017-06-13 16:42:31.364396\n437,7,54,Hydrokinesis,https://placehold.it/600x100.png,http://skiles.co/kayleigh_stark,0,0,2017-06-13 16:42:31.37953,2017-06-13 16:42:31.463535\n438,7,54,Banish,https://placehold.it/600x100.png,http://skiles.co/annalise,0,0,2017-06-13 16:42:31.477699,2017-06-13 16:42:31.540072\n439,7,54,Astral Trap,https://placehold.it/600x100.png,http://skiles.co/kenna,0,0,2017-06-13 16:42:31.556365,2017-06-13 16:42:31.629206\n440,7,54,Vision - Cryo,https://placehold.it/600x100.png,http://skiles.co/norris,0,0,2017-06-13 16:42:31.644734,2017-06-13 16:42:31.713326\n441,7,55,Molecular Combustion,https://placehold.it/600x100.png,http://swift.co/lambert.witting,0,0,2017-06-13 16:42:31.744059,2017-06-13 16:42:31.805383\n442,7,55,Energy Blasts,https://placehold.it/600x100.png,http://swift.co/marlene,0,0,2017-06-13 16:42:31.819,2017-06-13 16:42:31.886085\n443,7,55,Chronokinesis,https://placehold.it/600x100.png,http://swift.co/marilou,0,0,2017-06-13 16:42:31.900555,2017-06-13 16:42:31.979509\n444,7,55,Longevity,https://placehold.it/600x100.png,http://swift.co/maureen.dickens,0,0,2017-06-13 16:42:31.992875,2017-06-13 16:42:32.067624\n445,7,55,Adaptation,https://placehold.it/600x100.png,http://swift.co/judson.keler,0,0,2017-06-13 16:42:32.080885,2017-06-13 16:42:32.154812\n446,7,55,Cloaking,https://placehold.it/600x100.png,http://swift.co/magnus,0,0,2017-06-13 16:42:32.170243,2017-06-13 16:42:32.232727\n447,7,55,Projection,https://placehold.it/600x100.png,http://swift.co/makayla.padberg,0,0,2017-06-13 16:42:32.246729,2017-06-13 16:42:32.292045\n448,7,55,Magnetokinesis,https://placehold.it/600x100.png,http://swift.co/nikolas.muller,0,0,2017-06-13 16:42:32.306589,2017-06-13 16:42:32.361817\n449,7,55,Radiation Control,https://placehold.it/600x100.png,http://swift.co/gwen,0,0,2017-06-13 16:42:32.376305,2017-06-13 16:42:32.435641\n450,7,55,Separation,https://placehold.it/600x100.png,http://swift.co/carlotta,0,0,2017-06-13 16:42:32.447937,2017-06-13 16:42:32.506509\n451,7,56,Precognition,https://placehold.it/600x100.png,http://muller.com/emie,0,0,2017-06-13 16:42:32.532825,2017-06-13 16:42:32.586822\n452,7,56,Vision - Telescopic,https://placehold.it/600x100.png,http://muller.com/savanah.weinat,0,0,2017-06-13 16:42:32.598892,2017-06-13 16:42:32.661789\n453,7,56,Energy Armor,https://placehold.it/600x100.png,http://muller.com/roselyn,0,0,2017-06-13 16:42:32.67583,2017-06-13 16:42:32.747526\n454,7,56,Symbiote Costume,https://placehold.it/600x100.png,http://muller.com/devan_schuppe,0,0,2017-06-13 16:42:32.761974,2017-06-13 16:42:32.829835\n455,7,56,Enhanced Smell,https://placehold.it/600x100.png,http://muller.com/kasey_farrell,0,0,2017-06-13 16:42:32.844693,2017-06-13 16:42:32.904123\n456,7,56,Apotheosis,https://placehold.it/600x100.png,http://muller.com/zoila_gibson,0,0,2017-06-13 16:42:32.9171,2017-06-13 16:42:32.984825\n457,7,57,Absorption,https://placehold.it/600x100.png,http://price.info/heidi,0,0,2017-06-13 16:42:33.015761,2017-06-13 16:42:33.087538\n458,7,57,Omnipotence,https://placehold.it/600x100.png,http://price.info/hal,0,0,2017-06-13 16:42:33.103637,2017-06-13 16:42:33.176102\n459,7,57,Invulnerability,https://placehold.it/600x100.png,http://price.info/yasmeen_runolfsdottir,0,0,2017-06-13 16:42:33.1894,2017-06-13 16:42:33.261465\n460,7,57,Wallcrawling,https://placehold.it/600x100.png,http://price.info/vena_legros,0,0,2017-06-13 16:42:33.275881,2017-06-13 16:42:33.326074\n461,7,57,Shapeshifting,https://placehold.it/600x100.png,http://price.info/walter.balistreri,0,0,2017-06-13 16:42:33.340048,2017-06-13 16:42:33.421558\n462,7,57,Nova Force,https://placehold.it/600x100.png,http://price.info/imani_kihn,0,0,2017-06-13 16:42:33.436373,2017-06-13 16:42:33.507189\n463,7,57,Duplication,https://placehold.it/600x100.png,http://price.info/merle,0,0,2017-06-13 16:42:33.520825,2017-06-13 16:42:33.610244\n464,7,57,Intuitive aptitude,https://placehold.it/600x100.png,http://price.info/mattie,0,0,2017-06-13 16:42:33.623721,2017-06-13 16:42:33.703095\n465,7,57,Latent Abilities,https://placehold.it/600x100.png,http://price.info/leonie_leuschke,0,0,2017-06-13 16:42:33.717448,2017-06-13 16:42:33.778371\n466,7,58,Psionic Powers,https://placehold.it/600x100.png,http://heaneyhoppe.biz/hugh.barton,0,0,2017-06-13 16:42:33.805653,2017-06-13 16:42:33.857479\n467,7,58,Qwardian Power Ring,https://placehold.it/600x100.png,http://heaneyhoppe.biz/kattie.howe,0,0,2017-06-13 16:42:33.871106,2017-06-13 16:42:33.914541\n468,7,58,Illumination,https://placehold.it/600x100.png,http://heaneyhoppe.biz/colin_pagac,0,0,2017-06-13 16:42:33.932708,2017-06-13 16:42:33.99832\n469,7,58,Clairvoyance,https://placehold.it/600x100.png,http://heaneyhoppe.biz/jalon,0,0,2017-06-13 16:42:34.011514,2017-06-13 16:42:34.061216\n470,7,58,Stamina,https://placehold.it/600x100.png,http://heaneyhoppe.biz/erin_corkery,0,0,2017-06-13 16:42:34.075924,2017-06-13 16:42:34.138604\n471,7,58,Omnitrix,https://placehold.it/600x100.png,http://heaneyhoppe.biz/erica,0,0,2017-06-13 16:42:34.151339,2017-06-13 16:42:34.218896\n472,7,58,Size Changing,https://placehold.it/600x100.png,http://heaneyhoppe.biz/rebeka,0,0,2017-06-13 16:42:34.232041,2017-06-13 16:42:34.294881\n473,7,58,Mind Control Resistance,https://placehold.it/600x100.png,http://heaneyhoppe.biz/chaim_cummerata,0,0,2017-06-13 16:42:34.310382,2017-06-13 16:42:34.3921\n126,3,18,Telekinesis,https://placehold.it/600x100.png,http://feeneyking.org/eve_jones,0,0,2017-06-13 16:42:03.809046,2017-06-13 16:42:03.893836\n127,3,18,Cryokinesis,https://placehold.it/600x100.png,http://feeneyking.org/anderson,0,0,2017-06-13 16:42:03.907537,2017-06-13 16:42:03.949249\n128,3,18,Energy Armor,https://placehold.it/600x100.png,http://feeneyking.org/teagan,0,0,2017-06-13 16:42:03.963271,2017-06-13 16:42:04.042923\n129,3,18,Natural Armor,https://placehold.it/600x100.png,http://feeneyking.org/sidney.parker,0,0,2017-06-13 16:42:04.057484,2017-06-13 16:42:04.140856\n130,3,18,Natural Weapons,https://placehold.it/600x100.png,http://feeneyking.org/ettie,0,0,2017-06-13 16:42:04.156869,2017-06-13 16:42:04.233039\n131,3,18,Banish,https://placehold.it/600x100.png,http://feeneyking.org/susanna.anderson,0,0,2017-06-13 16:42:04.246535,2017-06-13 16:42:04.313148\n132,3,18,Camouflage,https://placehold.it/600x100.png,http://feeneyking.org/mitchel_doyle,0,0,2017-06-13 16:42:04.330694,2017-06-13 16:42:04.415642\n133,3,18,Stamina,https://placehold.it/600x100.png,http://feeneyking.org/nina,0,0,2017-06-13 16:42:04.429892,2017-06-13 16:42:04.487595\n134,3,18,Portal Creation,https://placehold.it/600x100.png,http://feeneyking.org/sterling,0,0,2017-06-13 16:42:04.502836,2017-06-13 16:42:04.565242\n135,3,18,Illusions,https://placehold.it/600x100.png,http://feeneyking.org/tyrese_cole,0,0,2017-06-13 16:42:04.581041,2017-06-13 16:42:04.655729\n136,3,19,Natural Weapons,https://placehold.it/600x100.png,http://paucek.biz/lamont,0,0,2017-06-13 16:42:04.686014,2017-06-13 16:42:04.743433\n137,3,19,Probability Manipulation,https://placehold.it/600x100.png,http://paucek.biz/chesley.heathcote,0,0,2017-06-13 16:42:04.757826,2017-06-13 16:42:04.844329\n138,3,19,Telekinesis,https://placehold.it/600x100.png,http://paucek.biz/houston,0,0,2017-06-13 16:42:04.860048,2017-06-13 16:42:04.945833\n139,3,19,Cross-Dimensional Travel,https://placehold.it/600x100.png,http://paucek.biz/giovanny_marquardt,0,0,2017-06-13 16:42:04.960175,2017-06-13 16:42:05.021506\n140,3,19,Underwater breathing,https://placehold.it/600x100.png,http://paucek.biz/lynn,0,0,2017-06-13 16:42:05.03948,2017-06-13 16:42:05.111025\n141,3,19,Physical Anomaly,https://placehold.it/600x100.png,http://paucek.biz/ned,0,0,2017-06-13 16:42:05.127417,2017-06-13 16:42:05.204073\n142,3,19,Illusions,https://placehold.it/600x100.png,http://paucek.biz/federico,0,0,2017-06-13 16:42:05.216885,2017-06-13 16:42:05.303466\n143,3,19,Cloaking,https://placehold.it/600x100.png,http://paucek.biz/arden,0,0,2017-06-13 16:42:05.317382,2017-06-13 16:42:05.3914\n144,3,19,Teleportation,https://placehold.it/600x100.png,http://paucek.biz/jayne,0,0,2017-06-13 16:42:05.406914,2017-06-13 16:42:05.4594\n145,3,19,Longevity,https://placehold.it/600x100.png,http://paucek.biz/jeffery_hilll,0,0,2017-06-13 16:42:05.476436,2017-06-13 16:42:05.547683\n146,3,20,Echokinesis,https://placehold.it/600x100.png,http://gutmann.com/gladys.mckenzie,0,0,2017-06-13 16:42:05.579384,2017-06-13 16:42:05.626708\n147,3,20,Elasticity,https://placehold.it/600x100.png,http://gutmann.com/mohammed.bashirian,0,0,2017-06-13 16:42:05.642674,2017-06-13 16:42:05.705984\n148,3,20,Substance Secretion,https://placehold.it/600x100.png,http://gutmann.com/penelope.corkery,0,0,2017-06-13 16:42:05.720665,2017-06-13 16:42:05.780659\n149,3,20,Animal Attributes,https://placehold.it/600x100.png,http://gutmann.com/efren.torp,0,0,2017-06-13 16:42:05.797722,2017-06-13 16:42:05.865206\n150,3,20,Self-Sustenance,https://placehold.it/600x100.png,http://gutmann.com/lavon.schuster,0,0,2017-06-13 16:42:05.877946,2017-06-13 16:42:05.941094\n151,3,20,Vision - Infrared,https://placehold.it/600x100.png,http://gutmann.com/naomie,0,0,2017-06-13 16:42:05.955333,2017-06-13 16:42:06.02343\n152,3,20,Orbing,https://placehold.it/600x100.png,http://gutmann.com/tad,0,0,2017-06-13 16:42:06.038315,2017-06-13 16:42:06.10998\n153,3,20,Clairvoyance,https://placehold.it/600x100.png,http://gutmann.com/tyrique,0,0,2017-06-13 16:42:06.122497,2017-06-13 16:42:06.179545\n154,3,20,Geokinesis,https://placehold.it/600x100.png,http://gutmann.com/winnifred,0,0,2017-06-13 16:42:06.191793,2017-06-13 16:42:06.246337\n155,3,20,Vision - Heat,https://placehold.it/600x100.png,http://gutmann.com/carlos,0,0,2017-06-13 16:42:06.261953,2017-06-13 16:42:06.31341\n156,3,21,Omnitrix,https://placehold.it/600x100.png,http://dare.io/carroll,0,0,2017-06-13 16:42:06.341123,2017-06-13 16:42:06.387925\n157,3,21,Super Strength,https://placehold.it/600x100.png,http://dare.io/lisandro,0,0,2017-06-13 16:42:06.40589,2017-06-13 16:42:06.465698\n158,3,21,Astral Travel,https://placehold.it/600x100.png,http://dare.io/laverna.feil,0,0,2017-06-13 16:42:06.481883,2017-06-13 16:42:06.558315\n159,3,21,Vision - Microscopic,https://placehold.it/600x100.png,http://dare.io/macey,0,0,2017-06-13 16:42:06.574333,2017-06-13 16:42:06.634626\n160,3,21,Timeframe Control,https://placehold.it/600x100.png,http://dare.io/hudson,0,0,2017-06-13 16:42:06.648858,2017-06-13 16:42:06.724307\n161,3,21,The Force,https://placehold.it/600x100.png,http://dare.io/zora_mosciski,0,0,2017-06-13 16:42:06.739704,2017-06-13 16:42:06.793667\n162,3,22,Energy Absorption,https://placehold.it/600x100.png,http://jerde.net/charlotte,0,0,2017-06-13 16:42:06.820607,2017-06-13 16:42:06.869208\n163,3,22,Omnitrix,https://placehold.it/600x100.png,http://jerde.net/abelardo,0,0,2017-06-13 16:42:06.882798,2017-06-13 16:42:06.955947\n164,3,22,Enhanced Touch,https://placehold.it/600x100.png,http://jerde.net/coralie_steuber,0,0,2017-06-13 16:42:06.974081,2017-06-13 16:42:07.040691\n165,3,22,Physical Anomaly,https://placehold.it/600x100.png,http://jerde.net/leopoldo.hamill,0,0,2017-06-13 16:42:07.056526,2017-06-13 16:42:07.139174\n166,3,22,Photographic Reflexes,https://placehold.it/600x100.png,http://jerde.net/jody_becker,0,0,2017-06-13 16:42:07.155253,2017-06-13 16:42:07.219213\n167,3,22,Molecular Immobilization,https://placehold.it/600x100.png,http://jerde.net/marguerite,0,0,2017-06-13 16:42:07.233126,2017-06-13 16:42:07.320242\n168,3,22,The Force,https://placehold.it/600x100.png,http://jerde.net/americo,0,0,2017-06-13 16:42:07.334481,2017-06-13 16:42:07.407912\n169,3,22,Gliding,https://placehold.it/600x100.png,http://jerde.net/alejandrin,0,0,2017-06-13 16:42:07.421185,2017-06-13 16:42:07.486228\n170,3,22,Vision - Infrared,https://placehold.it/600x100.png,http://jerde.net/judson.beier,0,0,2017-06-13 16:42:07.500938,2017-06-13 16:42:07.559577\n171,3,22,Power Cosmic,https://placehold.it/600x100.png,http://jerde.net/uriah.stehr,0,0,2017-06-13 16:42:07.574059,2017-06-13 16:42:07.622781\n172,3,23,Stealth,https://placehold.it/600x100.png,http://pfefferjenkins.co/janick_crist,0,0,2017-06-13 16:42:07.651837,2017-06-13 16:42:07.740867\n173,3,23,Insanity,https://placehold.it/600x100.png,http://pfefferjenkins.co/francesca.schimmel,0,0,2017-06-13 16:42:07.75642,2017-06-13 16:42:07.834273\n174,3,23,Cross-Dimensional Travel,https://placehold.it/600x100.png,http://pfefferjenkins.co/gene,0,0,2017-06-13 16:42:07.84881,2017-06-13 16:42:07.932184\n175,3,23,Intangibility,https://placehold.it/600x100.png,http://pfefferjenkins.co/jade.waelchi,0,0,2017-06-13 16:42:07.948427,2017-06-13 16:42:08.021368\n176,3,23,Terrakinesis,https://placehold.it/600x100.png,http://pfefferjenkins.co/helen.weinat,0,0,2017-06-13 16:42:08.037737,2017-06-13 16:42:08.110735\n177,3,23,Hydrokinesis,https://placehold.it/600x100.png,http://pfefferjenkins.co/berniece,0,0,2017-06-13 16:42:08.128169,2017-06-13 16:42:08.199452\n178,3,24,Illumination,https://placehold.it/600x100.png,http://yostrunolfon.info/jakayla_towne,0,0,2017-06-13 16:42:08.231144,2017-06-13 16:42:08.31204\n179,3,24,Reality Warping,https://placehold.it/600x100.png,http://yostrunolfon.info/maud.haley,0,0,2017-06-13 16:42:08.325801,2017-06-13 16:42:08.387129\n180,3,24,Elemental Transmogrification,https://placehold.it/600x100.png,http://yostrunolfon.info/nat,0,0,2017-06-13 16:42:08.401184,2017-06-13 16:42:08.451499\n181,3,24,Elasticity,https://placehold.it/600x100.png,http://yostrunolfon.info/effie.hudson,0,0,2017-06-13 16:42:08.464181,2017-06-13 16:42:08.51401\n182,3,24,Technopath/Cyberpath,https://placehold.it/600x100.png,http://yostrunolfon.info/jackson,0,0,2017-06-13 16:42:08.528378,2017-06-13 16:42:08.585054\n183,3,24,Astral Travel,https://placehold.it/600x100.png,http://yostrunolfon.info/abraham,0,0,2017-06-13 16:42:08.59736,2017-06-13 16:42:08.671576\n184,3,24,Changing Armor,https://placehold.it/600x100.png,http://yostrunolfon.info/pasquale.streich,0,0,2017-06-13 16:42:08.684427,2017-06-13 16:42:08.734522\n185,3,25,Molecular Combustion,https://placehold.it/600x100.png,http://mueller.biz/enrique,0,0,2017-06-13 16:42:08.777743,2017-06-13 16:42:08.868558\n186,3,25,The Force,https://placehold.it/600x100.png,http://mueller.biz/marcelina.stracke,0,0,2017-06-13 16:42:08.885621,2017-06-13 16:42:08.955883\n187,3,25,Duplication,https://placehold.it/600x100.png,http://mueller.biz/margaret,0,0,2017-06-13 16:42:08.973615,2017-06-13 16:42:09.045737\n188,3,25,Gravitokinesis,https://placehold.it/600x100.png,http://mueller.biz/schuyler,0,0,2017-06-13 16:42:09.064227,2017-06-13 16:42:09.125999\n189,3,25,Heat Generation,https://placehold.it/600x100.png,http://mueller.biz/norene.treutel,0,0,2017-06-13 16:42:09.142832,2017-06-13 16:42:09.219551\n190,3,25,Illumination,https://placehold.it/600x100.png,http://mueller.biz/roy.hamill,0,0,2017-06-13 16:42:09.238599,2017-06-13 16:42:09.284909\n191,3,25,Mind Control Resistance,https://placehold.it/600x100.png,http://mueller.biz/lavinia.willms,0,0,2017-06-13 16:42:09.300653,2017-06-13 16:42:09.384197\n192,3,25,Time Travel,https://placehold.it/600x100.png,http://mueller.biz/arianna_nolan,0,0,2017-06-13 16:42:09.401135,2017-06-13 16:42:09.494179\n193,3,25,Levitation,https://placehold.it/600x100.png,http://mueller.biz/aliya.ebert,0,0,2017-06-13 16:42:09.511977,2017-06-13 16:42:09.589935\n194,3,25,Cold Resistance,https://placehold.it/600x100.png,http://mueller.biz/beau,0,0,2017-06-13 16:42:09.606813,2017-06-13 16:42:09.696173\n336,6,43,Cold Resistance,https://placehold.it/600x100.png,http://trantow.info/allene,0,0,2017-06-13 16:42:22.54747,2017-06-13 16:42:22.619579\n337,6,43,Energy Resistance,https://placehold.it/600x100.png,http://trantow.info/zackery,0,0,2017-06-13 16:42:22.633866,2017-06-13 16:42:22.708133\n338,6,43,Portal Creation,https://placehold.it/600x100.png,http://trantow.info/valentine.schultz,0,0,2017-06-13 16:42:22.723297,2017-06-13 16:42:22.802213\n339,6,43,Weapon-based Powers,https://placehold.it/600x100.png,http://trantow.info/lea_bartell,0,0,2017-06-13 16:42:22.816411,2017-06-13 16:42:22.883117\n340,6,43,Umbrakinesis,https://placehold.it/600x100.png,http://trantow.info/conner.buckridge,0,0,2017-06-13 16:42:22.89868,2017-06-13 16:42:22.961783\n341,6,43,Sonar,https://placehold.it/600x100.png,http://trantow.info/sedrick.kuphal,0,0,2017-06-13 16:42:22.97485,2017-06-13 16:42:23.040428\n342,6,43,Elasticity,https://placehold.it/600x100.png,http://trantow.info/patricia,0,0,2017-06-13 16:42:23.051936,2017-06-13 16:42:23.121264\n343,6,43,Danger Sense,https://placehold.it/600x100.png,http://trantow.info/patience_baumbach,0,0,2017-06-13 16:42:23.134886,2017-06-13 16:42:23.187287\n344,6,43,Physical Anomaly,https://placehold.it/600x100.png,http://trantow.info/laurine,0,0,2017-06-13 16:42:23.200617,2017-06-13 16:42:23.279953\n345,6,44,Technopath/Cyberpath,https://placehold.it/600x100.png,http://schmittkeler.org/joey,0,0,2017-06-13 16:42:23.307659,2017-06-13 16:42:23.361456\n346,6,44,Hydrokinesis,https://placehold.it/600x100.png,http://schmittkeler.org/terrell,0,0,2017-06-13 16:42:23.37575,2017-06-13 16:42:23.426955\n347,6,44,Separation,https://placehold.it/600x100.png,http://schmittkeler.org/constantin.torphy,0,0,2017-06-13 16:42:23.443194,2017-06-13 16:42:23.504532\n348,6,44,Radiation Immunity,https://placehold.it/600x100.png,http://schmittkeler.org/kameron_bruen,0,0,2017-06-13 16:42:23.518493,2017-06-13 16:42:23.602878\n349,6,44,Dexterity,https://placehold.it/600x100.png,http://schmittkeler.org/marques_vonrueden,0,0,2017-06-13 16:42:23.617001,2017-06-13 16:42:23.687554\n350,6,44,Vision - Microscopic,https://placehold.it/600x100.png,http://schmittkeler.org/kaleb,0,0,2017-06-13 16:42:23.70204,2017-06-13 16:42:23.762041\n351,6,45,Radiation Control,https://placehold.it/600x100.png,http://kingwalsh.org/kavon.murray,0,0,2017-06-13 16:42:23.788708,2017-06-13 16:42:23.838007\n352,6,45,Accuracy,https://placehold.it/600x100.png,http://kingwalsh.org/jennie.little,0,0,2017-06-13 16:42:23.852718,2017-06-13 16:42:23.932507\n353,6,45,Photokinesis,https://placehold.it/600x100.png,http://kingwalsh.org/dereck,0,0,2017-06-13 16:42:23.945676,2017-06-13 16:42:24.047288\n354,6,45,Animal Control,https://placehold.it/600x100.png,http://kingwalsh.org/yasmine_purdy,0,0,2017-06-13 16:42:24.060147,2017-06-13 16:42:24.130783\n355,6,45,Animal Control,https://placehold.it/600x100.png,http://kingwalsh.org/royal.wuckert,0,0,2017-06-13 16:42:24.145601,2017-06-13 16:42:24.210153\n356,6,45,Geokinesis,https://placehold.it/600x100.png,http://kingwalsh.org/angelita,0,0,2017-06-13 16:42:24.222364,2017-06-13 16:42:24.315466\n357,6,46,Substance Secretion,https://placehold.it/600x100.png,http://maggio.name/tina_vandervort,0,0,2017-06-13 16:42:24.3455,2017-06-13 16:42:24.398655\n358,6,46,Molecular Manipulation,https://placehold.it/600x100.png,http://maggio.name/brennon,0,0,2017-06-13 16:42:24.411667,2017-06-13 16:42:24.467224\n359,6,46,Enhanced Touch,https://placehold.it/600x100.png,http://maggio.name/genesis_sawayn,0,0,2017-06-13 16:42:24.480213,2017-06-13 16:42:24.544346\n360,6,46,Energy Constructs,https://placehold.it/600x100.png,http://maggio.name/cydney,0,0,2017-06-13 16:42:24.558557,2017-06-13 16:42:24.611128\n361,6,46,Substance Secretion,https://placehold.it/600x100.png,http://maggio.name/bridgette,0,0,2017-06-13 16:42:24.625051,2017-06-13 16:42:24.675576\n362,6,46,Atmokinesis,https://placehold.it/600x100.png,http://maggio.name/ayana,0,0,2017-06-13 16:42:24.690354,2017-06-13 16:42:24.737523\n363,6,46,Photokinesis,https://placehold.it/600x100.png,http://maggio.name/mavis,0,0,2017-06-13 16:42:24.754694,2017-06-13 16:42:24.824135\n364,6,46,Regeneration,https://placehold.it/600x100.png,http://maggio.name/margaretta,0,0,2017-06-13 16:42:24.836943,2017-06-13 16:42:25.205806\n365,6,46,Size Changing,https://placehold.it/600x100.png,http://maggio.name/edwina_brakus,0,0,2017-06-13 16:42:25.220389,2017-06-13 16:42:25.286963\n366,6,46,Fire Resistance,https://placehold.it/600x100.png,http://maggio.name/pauline_stanton,0,0,2017-06-13 16:42:25.301557,2017-06-13 16:42:25.359201\n367,6,47,Possession,https://placehold.it/600x100.png,http://keebler.name/serenity,0,0,2017-06-13 16:42:25.389001,2017-06-13 16:42:25.443057\n368,6,47,Anti-Gravity,https://placehold.it/600x100.png,http://keebler.name/zula,0,0,2017-06-13 16:42:25.462868,2017-06-13 16:42:25.507943\n369,6,47,Cross-Dimensional Awareness,https://placehold.it/600x100.png,http://keebler.name/cleo,0,0,2017-06-13 16:42:25.522579,2017-06-13 16:42:25.576707\n370,6,47,Longevity,https://placehold.it/600x100.png,http://keebler.name/brannon_donnelly,0,0,2017-06-13 16:42:25.590399,2017-06-13 16:42:25.661514\n371,6,47,Vision - X-Ray,https://placehold.it/600x100.png,http://keebler.name/angelo_rippin,0,0,2017-06-13 16:42:25.674631,2017-06-13 16:42:25.742287\n372,6,47,Energy Manipulation,https://placehold.it/600x100.png,http://keebler.name/dario,0,0,2017-06-13 16:42:25.756989,2017-06-13 16:42:25.836984\n373,6,47,Self-Sustenance,https://placehold.it/600x100.png,http://keebler.name/felipe.oreilly,0,0,2017-06-13 16:42:25.853061,2017-06-13 16:42:25.916555\n374,6,47,Teleportation,https://placehold.it/600x100.png,http://keebler.name/enos,0,0,2017-06-13 16:42:25.931299,2017-06-13 16:42:26.003001\n375,6,47,Astral Travel,https://placehold.it/600x100.png,http://keebler.name/paula,0,0,2017-06-13 16:42:26.01667,2017-06-13 16:42:26.082117\n376,6,47,Vision - Cryo,https://placehold.it/600x100.png,http://keebler.name/mandy,0,0,2017-06-13 16:42:26.097139,2017-06-13 16:42:26.158086\n377,6,48,Power Suit,https://placehold.it/600x100.png,http://haag.io/melvin,0,0,2017-06-13 16:42:26.184963,2017-06-13 16:42:26.244193\n378,6,48,Telekinesis,https://placehold.it/600x100.png,http://haag.io/yolanda,0,0,2017-06-13 16:42:26.259224,2017-06-13 16:42:26.3272\n379,6,48,Photographic Reflexes,https://placehold.it/600x100.png,http://haag.io/bella,0,0,2017-06-13 16:42:26.341133,2017-06-13 16:42:26.413343\n380,6,48,Power Cosmic,https://placehold.it/600x100.png,http://haag.io/jordan_dach,0,0,2017-06-13 16:42:26.429433,2017-06-13 16:42:26.499291\n381,6,48,Astral Projection,https://placehold.it/600x100.png,http://haag.io/johanna,0,0,2017-06-13 16:42:26.514178,2017-06-13 16:42:26.582176\n382,6,48,Biokinesis,https://placehold.it/600x100.png,http://haag.io/dorothea,0,0,2017-06-13 16:42:26.596519,2017-06-13 16:42:26.665202\n383,6,48,Lantern Power Ring,https://placehold.it/600x100.png,http://haag.io/lonie.kuphal,0,0,2017-06-13 16:42:26.680984,2017-06-13 16:42:26.750244\n384,6,48,Invisibility,https://placehold.it/600x100.png,http://haag.io/gino,0,0,2017-06-13 16:42:26.76451,2017-06-13 16:42:26.821239\n385,6,48,Super Strength,https://placehold.it/600x100.png,http://haag.io/mikel.jakubowski,0,0,2017-06-13 16:42:26.83487,2017-06-13 16:42:26.922366\n386,6,48,Web Creation,https://placehold.it/600x100.png,http://haag.io/urban_block,0,0,2017-06-13 16:42:26.939612,2017-06-13 16:42:27.033537\n387,6,49,Astral Projection,https://placehold.it/600x100.png,http://pollich.net/heaven,0,0,2017-06-13 16:42:27.062225,2017-06-13 16:42:27.132599\n388,6,49,Gravitokinesis,https://placehold.it/600x100.png,http://pollich.net/mittie,0,0,2017-06-13 16:42:27.151083,2017-06-13 16:42:27.225265\n389,6,49,Enhanced Senses,https://placehold.it/600x100.png,http://pollich.net/keyshawn.hauck,0,0,2017-06-13 16:42:27.244387,2017-06-13 16:42:27.308748\n390,6,49,Vision - Heat,https://placehold.it/600x100.png,http://pollich.net/clementine_littel,0,0,2017-06-13 16:42:27.323688,2017-06-13 16:42:27.390503\n391,6,49,Super Strength,https://placehold.it/600x100.png,http://pollich.net/jaren,0,0,2017-06-13 16:42:27.405382,2017-06-13 16:42:27.473043\n392,6,49,Banish,https://placehold.it/600x100.png,http://pollich.net/adrienne.eichmann,0,0,2017-06-13 16:42:27.486391,2017-06-13 16:42:27.560828\n393,6,49,Toxin and Disease Resistance,https://placehold.it/600x100.png,http://pollich.net/dell_kuvalis,0,0,2017-06-13 16:42:27.575557,2017-06-13 16:42:27.633337\n394,6,49,Force Fields,https://placehold.it/600x100.png,http://pollich.net/carroll_schamberger,0,0,2017-06-13 16:42:27.647365,2017-06-13 16:42:27.701748\n395,6,49,Animation,https://placehold.it/600x100.png,http://pollich.net/alice,0,0,2017-06-13 16:42:27.717997,2017-06-13 16:42:27.778461\n396,6,50,Vision - Infrared,https://placehold.it/600x100.png,http://ratke.name/john,0,0,2017-06-13 16:42:27.807222,2017-06-13 16:42:27.895033\n397,6,50,Longevity,https://placehold.it/600x100.png,http://ratke.name/natalia,0,0,2017-06-13 16:42:27.915154,2017-06-13 16:42:28.011595\n398,6,50,Vision - Infrared,https://placehold.it/600x100.png,http://ratke.name/torrey.balistreri,0,0,2017-06-13 16:42:28.02799,2017-06-13 16:42:28.098461\n399,6,50,Cold Resistance,https://placehold.it/600x100.png,http://ratke.name/christine,0,0,2017-06-13 16:42:28.114082,2017-06-13 16:42:28.180828\n400,6,50,Heat Resistance,https://placehold.it/600x100.png,http://ratke.name/robb.kub,0,0,2017-06-13 16:42:28.195933,2017-06-13 16:42:28.253087\n401,6,50,Resurrection,https://placehold.it/600x100.png,http://ratke.name/angeline,0,0,2017-06-13 16:42:28.266944,2017-06-13 16:42:28.331602\n402,6,50,Healing Factor,https://placehold.it/600x100.png,http://ratke.name/hildegard_rowe,0,0,2017-06-13 16:42:28.344034,2017-06-13 16:42:28.401812\n52,2,8,Possession,https://placehold.it/600x100.png,http://renner.org/dwight,0,0,2017-06-13 16:41:57.254284,2017-06-13 16:41:57.320506\n53,2,8,Power Nullifier,https://placehold.it/600x100.png,http://renner.org/fae,0,0,2017-06-13 16:41:57.336567,2017-06-13 16:41:57.423065\n54,2,8,Healing Factor,https://placehold.it/600x100.png,http://renner.org/derrick.shields,0,0,2017-06-13 16:41:57.438646,2017-06-13 16:41:57.515199\n55,2,8,Probability Manipulation,https://placehold.it/600x100.png,http://renner.org/ole,0,0,2017-06-13 16:41:57.528455,2017-06-13 16:41:57.599034\n56,2,8,Thermokinesis,https://placehold.it/600x100.png,http://renner.org/leopold.dibbert,0,0,2017-06-13 16:41:57.611384,2017-06-13 16:41:57.670595\n57,2,8,Power Augmentation,https://placehold.it/600x100.png,http://renner.org/mohammed_schneider,0,0,2017-06-13 16:41:57.684605,2017-06-13 16:41:57.755518\n58,2,9,Animation,https://placehold.it/600x100.png,http://rath.net/alda,0,0,2017-06-13 16:41:57.783929,2017-06-13 16:41:57.864984\n59,2,9,Seismic Power,https://placehold.it/600x100.png,http://rath.net/jamarcus,0,0,2017-06-13 16:41:57.881804,2017-06-13 16:41:57.945827\n60,2,9,Apotheosis,https://placehold.it/600x100.png,http://rath.net/felicity,0,0,2017-06-13 16:41:57.959157,2017-06-13 16:41:58.035709\n61,2,9,Cloaking,https://placehold.it/600x100.png,http://rath.net/holden,0,0,2017-06-13 16:41:58.050128,2017-06-13 16:41:58.122997\n62,2,9,Cryokinesis,https://placehold.it/600x100.png,http://rath.net/lonnie,0,0,2017-06-13 16:41:58.136565,2017-06-13 16:41:58.190822\n63,2,9,Omnilingualism,https://placehold.it/600x100.png,http://rath.net/bernard_rempel,0,0,2017-06-13 16:41:58.211175,2017-06-13 16:41:58.302561\n64,2,10,Duplication,https://placehold.it/600x100.png,http://davis.io/mason,0,0,2017-06-13 16:41:58.334676,2017-06-13 16:41:58.411971\n65,2,10,Power Absorption,https://placehold.it/600x100.png,http://davis.io/norval_gleichner,0,0,2017-06-13 16:41:58.429915,2017-06-13 16:41:58.514911\n66,2,10,Chlorokinesis,https://placehold.it/600x100.png,http://davis.io/onie,0,0,2017-06-13 16:41:58.533822,2017-06-13 16:41:58.606623\n67,2,10,Aerokinesis,https://placehold.it/600x100.png,http://davis.io/paris,0,0,2017-06-13 16:41:58.621321,2017-06-13 16:41:58.702946\n68,2,10,Flight,https://placehold.it/600x100.png,http://davis.io/abdullah,0,0,2017-06-13 16:41:58.721529,2017-06-13 16:41:58.773786\n69,2,10,Radiation Immunity,https://placehold.it/600x100.png,http://davis.io/alfonso,0,0,2017-06-13 16:41:58.788581,2017-06-13 16:41:58.858677\n70,2,10,Cross-Dimensional Travel,https://placehold.it/600x100.png,http://davis.io/chris,0,0,2017-06-13 16:41:58.87368,2017-06-13 16:41:58.958767\n71,2,10,Electrical Transport,https://placehold.it/600x100.png,http://davis.io/crystal_macgyver,0,0,2017-06-13 16:41:58.974751,2017-06-13 16:41:59.041036\n72,2,11,Magic Resistance,https://placehold.it/600x100.png,http://murphy.com/toney,0,0,2017-06-13 16:41:59.067952,2017-06-13 16:41:59.11737\n73,2,11,Sonar,https://placehold.it/600x100.png,http://murphy.com/lorenza_cummings,0,0,2017-06-13 16:41:59.131646,2017-06-13 16:41:59.205101\n74,2,11,Radiation Absorption,https://placehold.it/600x100.png,http://murphy.com/vida,0,0,2017-06-13 16:41:59.21827,2017-06-13 16:41:59.271412\n75,2,11,Vision - Infrared,https://placehold.it/600x100.png,http://murphy.com/jean.wiegand,0,0,2017-06-13 16:41:59.284775,2017-06-13 16:41:59.374152\n76,2,11,Omnipotence,https://placehold.it/600x100.png,http://murphy.com/alia_gleason,0,0,2017-06-13 16:41:59.386785,2017-06-13 16:41:59.457859\n77,2,11,Resurrection,https://placehold.it/600x100.png,http://murphy.com/nina,0,0,2017-06-13 16:41:59.473568,2017-06-13 16:41:59.5416\n78,2,12,Vision - Telescopic,https://placehold.it/600x100.png,http://heller.com/van,0,0,2017-06-13 16:41:59.565909,2017-06-13 16:41:59.647627\n79,2,12,Time Travel,https://placehold.it/600x100.png,http://heller.com/ayla,0,0,2017-06-13 16:41:59.665771,2017-06-13 16:41:59.714259\n80,2,12,Symbiote Costume,https://placehold.it/600x100.png,http://heller.com/kirsten,0,0,2017-06-13 16:41:59.729906,2017-06-13 16:41:59.785213\n81,2,12,Umbrakinesis,https://placehold.it/600x100.png,http://heller.com/simeon,0,0,2017-06-13 16:41:59.799354,2017-06-13 16:41:59.867031\n82,2,12,Jump,https://placehold.it/600x100.png,http://heller.com/polly_kerluke,0,0,2017-06-13 16:41:59.883848,2017-06-13 16:41:59.961005\n83,2,12,Enhanced Memory,https://placehold.it/600x100.png,http://heller.com/cordelia_braun,0,0,2017-06-13 16:41:59.975421,2017-06-13 16:42:00.041536\n84,2,12,Telekinesis,https://placehold.it/600x100.png,http://heller.com/antonio,0,0,2017-06-13 16:42:00.055007,2017-06-13 16:42:00.120545\n85,2,12,Vaporising Beams,https://placehold.it/600x100.png,http://heller.com/lindsay,0,0,2017-06-13 16:42:00.134856,2017-06-13 16:42:00.178369\n86,2,13,Stamina,https://placehold.it/600x100.png,http://kunze.biz/victoria.rowe,0,0,2017-06-13 16:42:00.207665,2017-06-13 16:42:00.2827\n87,2,13,Fire Resistance,https://placehold.it/600x100.png,http://kunze.biz/marcos.jacobi,0,0,2017-06-13 16:42:00.297293,2017-06-13 16:42:00.376913\n88,2,13,Probability Manipulation,https://placehold.it/600x100.png,http://kunze.biz/roy.oconnell,0,0,2017-06-13 16:42:00.390982,2017-06-13 16:42:00.451078\n89,2,13,Intelligence,https://placehold.it/600x100.png,http://kunze.biz/carroll,0,0,2017-06-13 16:42:00.46511,2017-06-13 16:42:00.547278\n90,2,13,Banish,https://placehold.it/600x100.png,http://kunze.biz/manuel.bosco,0,0,2017-06-13 16:42:00.563048,2017-06-13 16:42:00.645009\n91,2,13,The Force,https://placehold.it/600x100.png,http://kunze.biz/ebba_mcdermott,0,0,2017-06-13 16:42:00.663591,2017-06-13 16:42:00.716856\n92,2,13,Possession,https://placehold.it/600x100.png,http://kunze.biz/cristian,0,0,2017-06-13 16:42:00.729407,2017-06-13 16:42:00.797286\n93,2,13,Clairvoyance,https://placehold.it/600x100.png,http://kunze.biz/norbert.jakubowski,0,0,2017-06-13 16:42:00.812074,2017-06-13 16:42:00.889136\n94,2,13,Bullet Time,https://placehold.it/600x100.png,http://kunze.biz/efrain_mcclure,0,0,2017-06-13 16:42:00.905391,2017-06-13 16:42:00.971897\n95,2,13,Pyrokinesis,https://placehold.it/600x100.png,http://kunze.biz/jee,0,0,2017-06-13 16:42:00.987795,2017-06-13 16:42:01.05311\n96,2,14,Vitakinesis,https://placehold.it/600x100.png,http://conn.biz/minerva_skiles,0,0,2017-06-13 16:42:01.07882,2017-06-13 16:42:01.146378\n97,2,14,Self-Sustenance,https://placehold.it/600x100.png,http://conn.biz/gillian_erdman,0,0,2017-06-13 16:42:01.163896,2017-06-13 16:42:01.239235\n98,2,14,Telepathy Resistance,https://placehold.it/600x100.png,http://conn.biz/kamren,0,0,2017-06-13 16:42:01.254879,2017-06-13 16:42:01.310392\n99,2,14,Radar Sense,https://placehold.it/600x100.png,http://conn.biz/anabelle.bahringer,0,0,2017-06-13 16:42:01.324379,2017-06-13 16:42:01.381244\n100,2,14,Energy Resistance,https://placehold.it/600x100.png,http://conn.biz/moie,0,0,2017-06-13 16:42:01.397053,2017-06-13 16:42:01.46703\n101,2,14,Intangibility,https://placehold.it/600x100.png,http://conn.biz/vena_rosenbaum,0,0,2017-06-13 16:42:01.482865,2017-06-13 16:42:01.550407\n102,2,15,Electrical Transport,https://placehold.it/600x100.png,http://hansen.net/georgiana_stokes,0,0,2017-06-13 16:42:01.580298,2017-06-13 16:42:01.661925\n103,2,15,Phasing,https://placehold.it/600x100.png,http://hansen.net/freeda,0,0,2017-06-13 16:42:01.678295,2017-06-13 16:42:01.743686\n104,2,15,Pyrokinesis,https://placehold.it/600x100.png,http://hansen.net/martina_casper,0,0,2017-06-13 16:42:01.756784,2017-06-13 16:42:01.830191\n105,2,15,Elasticity,https://placehold.it/600x100.png,http://hansen.net/kane,0,0,2017-06-13 16:42:01.845698,2017-06-13 16:42:01.915996\n106,2,15,Animal Control,https://placehold.it/600x100.png,http://hansen.net/murphy,0,0,2017-06-13 16:42:01.931099,2017-06-13 16:42:01.988951\n107,2,15,Enhanced Senses,https://placehold.it/600x100.png,http://hansen.net/griffin_gleason,0,0,2017-06-13 16:42:02.004217,2017-06-13 16:42:02.082313\n108,2,15,Animation,https://placehold.it/600x100.png,http://hansen.net/shemar,0,0,2017-06-13 16:42:02.096371,2017-06-13 16:42:02.154741\n109,2,15,Omnipotence,https://placehold.it/600x100.png,http://hansen.net/clark.davis,0,0,2017-06-13 16:42:02.16873,2017-06-13 16:42:02.235534\n110,2,15,Symbiote Costume,https://placehold.it/600x100.png,http://hansen.net/camron_wintheiser,0,0,2017-06-13 16:42:02.249841,2017-06-13 16:42:02.304958\n111,2,16,Regeneration,https://placehold.it/600x100.png,http://vandervortbayer.co/linnie,0,0,2017-06-13 16:42:02.336109,2017-06-13 16:42:02.412784\n112,2,16,Power Sense,https://placehold.it/600x100.png,http://vandervortbayer.co/katrina,0,0,2017-06-13 16:42:02.4283,2017-06-13 16:42:02.508817\n113,2,16,Mind Blast,https://placehold.it/600x100.png,http://vandervortbayer.co/adalberto,0,0,2017-06-13 16:42:02.522811,2017-06-13 16:42:02.572642\n114,2,16,Invisibility,https://placehold.it/600x100.png,http://vandervortbayer.co/annabel,0,0,2017-06-13 16:42:02.588723,2017-06-13 16:42:02.656822\n115,2,16,Summoning,https://placehold.it/600x100.png,http://vandervortbayer.co/leonardo.gutkowski,0,0,2017-06-13 16:42:02.671477,2017-06-13 16:42:02.728037\n116,2,16,Duplication,https://placehold.it/600x100.png,http://vandervortbayer.co/scot,0,0,2017-06-13 16:42:02.741896,2017-06-13 16:42:02.81086\n117,2,16,Astral Trap,https://placehold.it/600x100.png,http://vandervortbayer.co/dora.hackett,0,0,2017-06-13 16:42:02.823574,2017-06-13 16:42:02.893394\n118,2,16,Vision - Cryo,https://placehold.it/600x100.png,http://vandervortbayer.co/kitty_beahan,0,0,2017-06-13 16:42:02.907004,2017-06-13 16:42:02.987976\n119,2,17,Hyperkinesis,https://placehold.it/600x100.png,http://donnellypredovic.info/sean,0,0,2017-06-13 16:42:03.019672,2017-06-13 16:42:03.094867\n120,2,17,Absorption,https://placehold.it/600x100.png,http://donnellypredovic.info/sonny,0,0,2017-06-13 16:42:03.108791,2017-06-13 16:42:03.186472\n121,2,17,Power Nullifier,https://placehold.it/600x100.png,http://donnellypredovic.info/lilliana_watsica,0,0,2017-06-13 16:42:03.204521,2017-06-13 16:42:03.261758\n122,2,17,Geokinesis,https://placehold.it/600x100.png,http://donnellypredovic.info/king.heaney,0,0,2017-06-13 16:42:03.276369,2017-06-13 16:42:03.330756\n123,2,17,Cold Resistance,https://placehold.it/600x100.png,http://donnellypredovic.info/ansel_schuster,0,0,2017-06-13 16:42:03.345959,2017-06-13 16:42:03.427401\n124,2,17,Cross-Dimensional Awareness,https://placehold.it/600x100.png,http://donnellypredovic.info/kayden_ziemann,0,0,2017-06-13 16:42:03.442446,2017-06-13 16:42:03.506236\n125,2,17,Teleportation,https://placehold.it/600x100.png,http://donnellypredovic.info/daisy.wuckert,0,0,2017-06-13 16:42:03.51888,2017-06-13 16:42:03.606551\n"
  },
  {
    "path": "src/test/regress/data/agg.data",
    "content": "56\t7.8\n100\t99.097\n0\t0.09561\n42\t324.78\n"
  },
  {
    "path": "src/test/regress/data/agg_type.data",
    "content": "1.0\t2.343\t23.44\n2.0\t3.544\t324.4\n3.0\t3.5666\t2332.9\n4.5\t6.34343\t2332.9\n"
  },
  {
    "path": "src/test/regress/data/array_types.csv",
    "content": "\"{1,2,3}\",\"{1,2,3}\",\"{a,b,c}\"\n{},{},{}\n\"{-2147483648,2147483647}\",\"{-9223372036854775808,9223372036854775807}\",\"{\"\"\"\"}\"\n"
  },
  {
    "path": "src/test/regress/data/campaigns.csv",
    "content": "59,8,Gorilla Grodd I,cost_per_click,running,935,,2017-06-13 16:42:34.594681,2017-06-13 16:42:34.594681\n60,8,The Doppelganger the Hunter,cost_per_impression,paused,8975,,2017-06-13 16:42:35.207761,2017-06-13 16:42:35.207761\n61,8,Falcon Brain,cost_per_click,running,8428,,2017-06-13 16:42:36.097274,2017-06-13 16:42:36.097274\n62,8,Red Goku of Hearts,cost_per_click,archived,6531,,2017-06-13 16:42:36.693675,2017-06-13 16:42:36.693675\n63,8,Green Rambo,cost_per_click,archived,2451,,2017-06-13 16:42:37.136045,2017-06-13 16:42:37.136045\n64,8,Winter Soldier Dragon,cost_per_click,running,1752,,2017-06-13 16:42:37.897502,2017-06-13 16:42:37.897502\n65,8,Overtkill,cost_per_click,running,8232,,2017-06-13 16:42:38.489345,2017-06-13 16:42:38.489345\n1,1,Mandarin,cost_per_impression,paused,7895,,2017-06-13 16:41:52.707903,2017-06-13 16:41:52.707903\n2,1,Red Leader,cost_per_click,paused,4891,,2017-06-13 16:41:53.341818,2017-06-13 16:41:53.341818\n3,1,Morlun,cost_per_impression,paused,1182,,2017-06-13 16:41:53.819101,2017-06-13 16:41:53.819101\n4,1,Illustrious Absorbing IX,cost_per_click,archived,4763,,2017-06-13 16:41:54.407686,2017-06-13 16:41:54.407686\n5,1,Captain Azrael Woman,cost_per_click,paused,9005,,2017-06-13 16:41:54.902299,2017-06-13 16:41:54.902299\n6,1,Cyborg Hancock II,cost_per_click,archived,8604,,2017-06-13 16:41:55.498523,2017-06-13 16:41:55.498523\n7,1,Ultra Justice Eyes,cost_per_click,paused,6982,,2017-06-13 16:41:56.367163,2017-06-13 16:41:56.367163\n34,5,Supah Bat Lord,cost_per_impression,archived,4307,,2017-06-13 16:42:16.142651,2017-06-13 16:42:16.142651\n35,5,Shocker,cost_per_impression,paused,3914,,2017-06-13 16:42:16.647064,2017-06-13 16:42:16.647064\n36,5,Giant Toxin Girl,cost_per_click,running,4057,,2017-06-13 16:42:17.481264,2017-06-13 16:42:17.481264\n37,5,Supah Scorpion Wolf,cost_per_impression,running,7420,,2017-06-13 16:42:18.269976,2017-06-13 16:42:18.269976\n38,5,Lyja,cost_per_click,archived,2369,,2017-06-13 16:42:18.821974,2017-06-13 16:42:18.821974\n39,5,Captain Annihilus,cost_per_click,archived,9352,,2017-06-13 16:42:19.310638,2017-06-13 16:42:19.310638\n40,5,Agent Rachel Pirzad of Hearts,cost_per_click,archived,3538,,2017-06-13 16:42:20.009205,2017-06-13 16:42:20.009205\n41,5,Giant Maya Herrera I,cost_per_click,archived,3712,,2017-06-13 16:42:20.769031,2017-06-13 16:42:20.769031\n42,5,Dark Groot Boy,cost_per_impression,running,6496,,2017-06-13 16:42:21.576833,2017-06-13 16:42:21.576833\n26,4,Magnificent Cy-Gor Thirteen,cost_per_impression,paused,5900,,2017-06-13 16:42:09.902924,2017-06-13 16:42:09.902924\n27,4,Wolverine Dragon,cost_per_impression,running,3713,,2017-06-13 16:42:10.866183,2017-06-13 16:42:10.866183\n28,4,Goku,cost_per_click,paused,1960,,2017-06-13 16:42:11.441643,2017-06-13 16:42:11.441643\n29,4,Ripcord Claw,cost_per_impression,archived,6994,,2017-06-13 16:42:12.198094,2017-06-13 16:42:12.198094\n30,4,Dark Aqua,cost_per_impression,paused,2663,,2017-06-13 16:42:13.113204,2017-06-13 16:42:13.113204\n31,4,Warbird Girl,cost_per_click,paused,4425,,2017-06-13 16:42:13.834842,2017-06-13 16:42:13.834842\n32,4,Cyborg Rogue,cost_per_click,archived,6281,,2017-06-13 16:42:14.730579,2017-06-13 16:42:14.730579\n33,4,Mr Mera III,cost_per_impression,paused,2192,,2017-06-13 16:42:15.47731,2017-06-13 16:42:15.47731\n51,7,Giant Chameleon Man,cost_per_click,running,3176,,2017-06-13 16:42:28.591585,2017-06-13 16:42:28.591585\n52,7,Tinkerer Strike,cost_per_impression,running,5198,,2017-06-13 16:42:29.277142,2017-06-13 16:42:29.277142\n53,7,Zatanna,cost_per_impression,running,150,,2017-06-13 16:42:30.065958,2017-06-13 16:42:30.065958\n54,7,Giant Question Claw,cost_per_impression,paused,2819,,2017-06-13 16:42:30.886949,2017-06-13 16:42:30.886949\n55,7,Question,cost_per_impression,paused,1642,,2017-06-13 16:42:31.728957,2017-06-13 16:42:31.728957\n56,7,Annihilus,cost_per_impression,running,137,,2017-06-13 16:42:32.519651,2017-06-13 16:42:32.519651\n57,7,Venom,cost_per_impression,archived,8340,,2017-06-13 16:42:32.999851,2017-06-13 16:42:32.999851\n58,7,The Longshot Strike,cost_per_click,paused,8977,,2017-06-13 16:42:33.791989,2017-06-13 16:42:33.791989\n18,3,Red Siren,cost_per_click,paused,2065,,2017-06-13 16:42:03.79514,2017-06-13 16:42:03.79514\n19,3,Titan the Fated,cost_per_impression,archived,7617,,2017-06-13 16:42:04.670896,2017-06-13 16:42:04.670896\n20,3,Metamorpho,cost_per_click,paused,6950,,2017-06-13 16:42:05.56454,2017-06-13 16:42:05.56454\n21,3,Mr Forge the Hunter,cost_per_impression,running,3085,,2017-06-13 16:42:06.326961,2017-06-13 16:42:06.326961\n22,3,Supah Scorpia,cost_per_click,running,480,,2017-06-13 16:42:06.808002,2017-06-13 16:42:06.808002\n23,3,Giant Maya Herrera,cost_per_impression,archived,8309,,2017-06-13 16:42:07.636385,2017-06-13 16:42:07.636385\n24,3,Mandarin,cost_per_impression,archived,3492,,2017-06-13 16:42:08.215445,2017-06-13 16:42:08.215445\n25,3,The Huntress,cost_per_click,paused,5367,,2017-06-13 16:42:08.748961,2017-06-13 16:42:08.748961\n43,6,Mystique Brain,cost_per_click,paused,1342,,2017-06-13 16:42:22.534609,2017-06-13 16:42:22.534609\n44,6,Mr Doomsday Brain,cost_per_impression,paused,1958,,2017-06-13 16:42:23.293625,2017-06-13 16:42:23.293625\n45,6,Ultra Monarch,cost_per_click,archived,5374,,2017-06-13 16:42:23.775571,2017-06-13 16:42:23.775571\n46,6,Supah Curse,cost_per_click,running,7865,,2017-06-13 16:42:24.331765,2017-06-13 16:42:24.331765\n47,6,Thing,cost_per_impression,running,2707,,2017-06-13 16:42:25.374042,2017-06-13 16:42:25.374042\n48,6,Red Colin Wagner,cost_per_click,paused,9873,,2017-06-13 16:42:26.172275,2017-06-13 16:42:26.172275\n49,6,Green Speedball,cost_per_click,archived,1032,,2017-06-13 16:42:27.047698,2017-06-13 16:42:27.047698\n50,6,Boom Boom I,cost_per_impression,archived,9896,,2017-06-13 16:42:27.792139,2017-06-13 16:42:27.792139\n8,2,Meltdown of Hearts,cost_per_impression,paused,4293,,2017-06-13 16:41:57.24128,2017-06-13 16:41:57.24128\n9,2,Bloodaxe I,cost_per_click,running,5130,,2017-06-13 16:41:57.770862,2017-06-13 16:41:57.770862\n10,2,Blink Claw,cost_per_impression,archived,1374,,2017-06-13 16:41:58.319465,2017-06-13 16:41:58.319465\n11,2,Electro Wolf,cost_per_impression,running,7194,,2017-06-13 16:41:59.053908,2017-06-13 16:41:59.053908\n12,2,Polaris,cost_per_impression,paused,4748,,2017-06-13 16:41:59.553231,2017-06-13 16:41:59.553231\n13,2,Omega Red,cost_per_click,paused,748,,2017-06-13 16:42:00.19296,2017-06-13 16:42:00.19296\n14,2,Spawn,cost_per_click,archived,5445,,2017-06-13 16:42:01.064903,2017-06-13 16:42:01.064903\n15,2,Blink,cost_per_click,archived,3643,,2017-06-13 16:42:01.564672,2017-06-13 16:42:01.564672\n16,2,Spectre Fist,cost_per_click,running,3841,,2017-06-13 16:42:02.320521,2017-06-13 16:42:02.320521\n17,2,Carnage Knight,cost_per_impression,paused,7221,,2017-06-13 16:42:03.006055,2017-06-13 16:42:03.006055\n"
  },
  {
    "path": "src/test/regress/data/clicks.csv",
    "content": "1,1,1,2017-06-12 08:44:51,http://pollich.info/dameon.bayer,,130.70.206.43,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n2,1,1,2017-01-21 06:53:55,http://lednerfranecki.io/ward,,176.239.100.21,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n3,1,1,2017-05-02 11:06:11,http://krajcik.com/eldred,,102.154.56.47,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n4,1,1,2017-01-27 21:44:27,http://schulist.info/fae_stoltenberg,,161.226.163.83,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n5,1,1,2017-05-07 07:43:20,http://ullrich.io/jeie,,176.239.100.21,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n6,1,2,2017-05-23 03:07:37,http://friesenpredovic.io/wilfredo,,136.239.79.72,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n7,1,2,2017-04-18 05:41:12,http://homenick.name/broderick.dibbert,,148.127.195.70,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n8,1,3,2017-02-01 21:22:59,http://boehm.io/dedrick_konopelski,,79.251.206.155,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n9,1,3,2017-05-16 15:06:39,http://haagkertzmann.com/gregory,,197.29.133.181,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n10,1,3,2017-03-04 07:14:14,http://leuschkeko.co/daija,,197.29.133.181,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n11,1,3,2016-12-30 19:38:21,http://windler.org/jerod,,218.4.220.11,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n12,1,3,2016-12-19 01:38:41,http://white.org/genevieve.schuster,,3.232.171.243,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n13,1,3,2017-03-28 15:17:07,http://dickens.com/kristy,,188.50.189.129,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n14,1,3,2016-12-29 15:52:33,http://reynolds.co/virgie,,224.86.119.31,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n15,1,3,2017-03-23 00:41:26,http://boyer.info/mercedes,,2.153.179.122,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n16,1,3,2017-05-30 12:47:03,http://ondrickanader.biz/arianna,,184.181.183.61,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n17,1,4,2017-02-26 16:39:46,http://batz.biz/jaida,,4.45.90.244,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n18,1,4,2017-04-04 16:32:24,http://kovacek.info/kyle,,171.63.44.91,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n19,1,4,2017-03-18 05:24:03,http://blicklang.info/krystel.hoppe,,90.200.195.135,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n20,1,4,2017-04-28 01:30:31,http://oconnell.com/alvis,,123.108.32.214,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n21,1,4,2017-02-09 19:17:21,http://renner.co/nettie.olson,,56.166.226.70,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n22,1,4,2017-04-29 03:14:02,http://cormiergibson.io/fausto,,84.123.247.153,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n23,1,5,2017-05-04 11:11:30,http://champlin.org/thora_harber,,46.164.41.83,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n24,1,5,2017-06-08 11:27:19,http://ornzboncak.net/orlo_hoeger,,214.18.177.74,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n25,1,5,2017-03-13 05:20:57,http://bosco.org/sienna,,9.35.183.96,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n26,1,5,2017-02-12 02:34:13,http://ziemegutkowski.com/ro_boehm,,127.116.151.208,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n27,1,5,2017-04-21 05:03:22,http://marquardt.org/okey,,87.92.164.74,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n28,1,5,2017-04-08 02:33:38,http://friesen.info/timothy,,247.232.194.24,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n29,1,5,2017-03-05 22:37:27,http://doyle.biz/rosamond_rutherford,,58.113.118.10,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n30,1,5,2017-05-31 18:47:21,http://harveydouglas.com/caesar_kozey,,182.225.238.154,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n31,1,5,2017-04-27 05:41:05,http://mayert.info/thora.boyer,,207.201.119.79,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n32,1,5,2017-04-15 16:39:33,http://jacobson.info/aliza.brekke,,248.160.88.37,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n33,1,5,2017-04-25 23:44:51,http://binscollins.info/rhoda.cormier,,229.202.70.147,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n34,1,5,2017-02-11 02:50:31,http://wyman.name/tina_toy,,58.113.118.10,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n35,1,5,2017-05-24 09:28:53,http://green.com/saige,,230.175.93.46,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n36,1,5,2017-04-29 17:31:56,http://kuhncartwright.com/fanny_durgan,,56.50.239.44,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n37,1,5,2017-05-25 23:28:02,http://ortiz.info/rico_waelchi,,21.199.217.190,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n38,1,5,2017-05-04 14:00:30,http://millerdaugherty.biz/leora.reynolds,,222.252.41.76,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n39,1,6,2017-04-11 09:49:39,http://maggiofunk.com/edwina.gorczany,,220.221.95.245,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n40,1,6,2017-05-24 22:13:06,http://kohler.name/freddy,,63.200.15.158,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n41,1,6,2017-03-28 18:45:25,http://steuber.co/jeie.olson,,156.44.156.135,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n42,1,6,2017-05-06 23:39:13,http://larsonkihn.net/kavon.morar,,32.185.126.197,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n43,1,6,2016-12-23 09:10:18,http://wilkinson.com/wellington.murphy,,32.88.25.183,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n44,1,6,2017-05-26 11:07:53,http://yost.net/kennedy,,160.154.45.137,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n45,1,6,2017-03-15 15:03:19,http://dach.net/delbert,,97.77.185.183,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n46,1,6,2017-04-11 19:16:34,http://fayweber.com/jamal,,121.26.155.40,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n47,1,6,2017-05-01 06:41:41,http://weimannhodkiewicz.name/gus,,29.84.70.245,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n48,1,6,2017-05-07 11:02:47,http://deckow.org/ayden,,12.89.127.107,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n49,1,6,2017-01-15 13:55:22,http://cartwright.org/rosanna,,29.84.70.245,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n50,1,6,2016-12-15 12:18:53,http://spencerratke.org/harmon_paucek,,153.142.61.93,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n51,1,6,2017-01-21 02:38:38,http://mclaughlin.org/gladys_okon,,106.187.40.128,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n52,1,6,2017-02-23 16:45:25,http://lebsack.name/earnest_crist,,68.29.181.197,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n53,1,6,2017-05-29 09:52:34,http://jenkins.net/gianni,,134.203.215.223,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n54,1,7,2016-12-30 18:45:55,http://parkerlueilwitz.com/franz,,172.246.70.245,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n55,1,7,2017-05-21 01:28:20,http://hartmannhauck.net/mia,,194.216.49.11,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n4995,8,474,2016-12-18 02:40:30,http://bogisichtorphy.co/susie,0.0000000000,89.148.71.17,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n4996,8,474,2017-05-24 02:14:09,http://gutkowski.org/leonardo,0.0000000000,103.6.226.229,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n4997,8,474,2017-03-29 20:07:44,http://barrowpencer.name/darby_nolan,1.0000000000,243.212.193.204,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n4998,8,474,2017-04-23 03:12:09,http://bins.org/isadore,1.0000000000,241.111.144.230,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n4999,8,474,2017-04-17 12:14:23,http://feestsipes.org/jackson.kunde,0.0000000000,22.178.2.131,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n5000,8,474,2017-03-01 10:21:41,http://weberstrosin.io/aaron,0.0000000000,73.125.121.41,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n5001,8,474,2017-03-23 00:53:10,http://schmitt.com/rodrick,0.0000000000,211.17.120.135,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n5002,8,474,2016-12-24 01:53:19,http://abernathybradtke.com/ocie,1.0000000000,89.148.71.17,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n5003,8,474,2017-02-25 04:50:28,http://osinskibeier.biz/celia.weinat,1.0000000000,146.91.118.53,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n5004,8,474,2017-02-11 19:04:55,http://kohlerhegmann.io/rogers,0.0000000000,89.148.71.17,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n5005,8,475,2016-12-24 17:11:28,http://graham.io/josiane,2.0000000000,173.158.19.64,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n5006,8,475,2017-04-05 07:30:47,http://roob.net/major.lang,0.0000000000,161.145.20.150,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n5007,8,475,2017-03-16 18:18:32,http://baumbach.co/annalise.fritsch,0.0000000000,159.227.94.130,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n5008,8,475,2017-01-05 03:32:15,http://lebsack.io/carol,2.0000000000,18.66.152.170,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n5009,8,475,2017-04-06 23:16:22,http://thielroberts.biz/ari_blick,0.0000000000,200.53.155.78,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n5010,8,475,2017-01-30 15:32:32,http://feest.co/keeley,0.0000000000,140.171.144.91,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n5011,8,475,2016-12-31 18:31:09,http://donnelly.io/elvera,0.0000000000,232.67.218.242,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n5012,8,476,2017-05-25 22:20:45,http://huelcorkery.co/lisandro.tillman,2.0000000000,204.246.119.117,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n5013,8,476,2017-05-18 20:39:35,http://hayeshartmann.name/aliya,0.0000000000,21.173.187.17,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n5014,8,476,2017-02-09 08:41:54,http://schaden.biz/enrico,2.0000000000,197.239.245.27,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n5015,8,477,2017-04-11 15:53:48,http://hayesboyle.name/lou,2.0000000000,196.30.65.147,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n5016,8,477,2017-01-07 02:59:49,http://funk.info/nedra.hoppe,1.0000000000,98.135.245.39,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n5017,8,477,2017-02-13 23:12:29,http://reynolds.org/elmore_baumbach,0.0000000000,196.14.194.73,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n5018,8,477,2017-02-24 02:06:34,http://moriette.net/rachel,1.0000000000,98.135.245.39,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n5019,8,477,2017-05-05 17:48:53,http://kunzestracke.io/jaydon,2.0000000000,103.125.27.164,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n5020,8,477,2017-05-15 04:45:50,http://witting.net/annamarie,0.0000000000,16.218.59.75,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n5021,8,477,2017-05-03 01:33:18,http://gulgowski.info/augustus,2.0000000000,142.71.188.161,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n5022,8,477,2017-03-11 16:36:36,http://ruel.co/manuela,1.0000000000,121.63.170.93,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n5023,8,477,2017-03-20 02:43:12,http://rempel.co/kathleen,0.0000000000,172.40.227.12,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n5024,8,477,2017-06-09 13:50:27,http://harber.com/elya_hammes,1.0000000000,93.234.91.159,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n5025,8,477,2016-12-28 16:06:13,http://ruelrath.info/alysa,1.0000000000,172.40.227.12,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n5026,8,477,2017-01-26 16:49:03,http://mcglynn.io/emilia,1.0000000000,238.97.190.84,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n5027,8,478,2017-02-23 11:55:02,http://considinequitzon.info/liam_kaulke,1.0000000000,22.204.95.37,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n5028,8,478,2017-05-09 07:13:43,http://lubowitz.org/lori,0.0000000000,163.14.145.192,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n5029,8,478,2017-03-06 23:24:01,http://stoltenberg.biz/keith_conn,1.0000000000,249.206.51.98,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n5030,8,479,2017-02-01 22:02:28,http://dickenswehner.com/jamir_stamm,1.0000000000,217.133.182.65,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n5031,8,479,2017-04-13 23:35:21,http://schroederthiel.info/abagail.johnston,0.0000000000,3.220.12.202,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n5032,8,479,2017-03-12 18:12:24,http://wuckert.biz/napoleon,2.0000000000,3.12.27.210,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n5033,8,479,2017-01-27 05:09:22,http://bartonbuckridge.info/lonnie,2.0000000000,210.157.215.166,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n5034,8,479,2016-12-15 18:00:26,http://bogan.org/roie.boyer,1.0000000000,126.218.143.193,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n5035,8,479,2017-06-07 05:39:10,http://pacocha.co/arlene_schimmel,0.0000000000,165.131.167.110,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n5036,8,479,2017-02-17 03:29:05,http://vonkuphal.io/jamel.west,1.0000000000,28.162.133.101,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n5037,8,479,2017-04-18 20:23:33,http://marvin.net/mallory_prosacco,1.0000000000,240.124.109.138,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n5038,8,479,2016-12-16 17:12:34,http://walker.co/estevan,1.0000000000,242.218.26.164,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n5039,8,479,2017-04-16 08:20:32,http://littel.name/dorthy,0.0000000000,229.216.76.98,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n5040,8,479,2017-04-21 18:18:12,http://ortizstark.info/eliseo_rice,1.0000000000,231.178.99.86,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n5041,8,479,2017-01-09 21:22:14,http://bogisich.info/arturo,1.0000000000,13.164.126.97,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n5042,8,479,2017-04-27 23:58:06,http://casper.org/guadalupe,1.0000000000,75.35.50.27,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n5043,8,479,2017-02-12 03:47:02,http://armstrong.info/cloyd,2.0000000000,109.235.104.62,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n5044,8,479,2017-02-11 14:15:19,http://dietrichborer.org/hardy,2.0000000000,46.83.48.83,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n56,1,7,2017-04-25 19:58:57,http://dicki.net/norberto.considine,,113.50.173.188,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n57,1,7,2017-03-29 10:38:36,http://torphyruecker.name/monty_hilpert,,86.198.75.31,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n58,1,7,2017-02-11 23:04:06,http://crooks.com/bruce_swift,,86.198.75.31,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n59,1,7,2016-12-15 07:09:56,http://mclaughlin.com/lucious_rolfson,,153.190.154.47,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n60,1,7,2016-12-23 09:24:23,http://klinghettinger.org/josefa.ruecker,,190.210.172.134,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n61,1,7,2017-01-02 09:08:45,http://jacobs.com/iac,,172.246.70.245,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n62,1,7,2017-06-04 21:43:08,http://bins.biz/rickie.hauck,,137.27.224.89,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n63,1,7,2017-01-06 15:20:56,http://walter.name/alice,,134.247.219.94,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n64,1,7,2017-06-03 17:59:51,http://sauerleannon.net/raina_christiansen,,229.37.123.85,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n65,1,7,2017-05-07 15:56:22,http://johnson.name/destiney.crist,,137.27.224.89,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n66,1,7,2017-01-15 19:49:48,http://thiel.org/mae.towne,,177.26.106.161,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n67,1,7,2017-06-10 02:14:40,http://markswaters.io/anabel_toy,,177.184.46.13,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n68,1,7,2017-05-11 16:36:45,http://balistreri.info/chanelle,,153.190.154.47,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n69,1,7,2017-03-23 08:49:50,http://treutel.io/forrest.abbott,,190.210.172.134,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n70,1,7,2017-05-15 21:55:59,http://schuppemarks.info/joannie,,194.216.49.11,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n71,1,7,2016-12-15 19:02:29,http://ziemann.info/eda_berge,,29.244.43.243,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n72,1,7,2017-06-08 23:34:22,http://bernhard.com/kiara,,242.200.40.75,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n73,1,8,2016-12-25 22:53:05,http://konopelski.net/molly,1.0000000000,153.220.141.226,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n74,1,8,2016-12-31 21:30:52,http://zulauf.co/sally_wyman,1.0000000000,150.150.105.101,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n75,1,8,2017-04-02 08:59:22,http://herzog.com/dorothea,0.0000000000,153.120.103.128,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n76,1,8,2017-05-20 19:57:01,http://durgan.net/joan,0.0000000000,9.234.14.36,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n77,1,8,2017-01-31 04:00:54,http://grady.info/stephanie_labadie,1.0000000000,156.96.140.159,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n78,1,8,2017-05-22 04:47:38,http://rippin.name/jeica.roberts,1.0000000000,153.120.103.128,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n79,1,8,2017-05-15 00:47:49,http://kihn.org/zackary.marvin,0.0000000000,92.49.186.30,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n80,1,8,2016-12-19 07:26:28,http://murphy.io/betsy_schuppe,0.0000000000,184.47.164.254,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n81,1,8,2017-05-21 08:23:29,http://stanton.co/milford,1.0000000000,110.208.27.149,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n82,1,8,2017-03-25 04:22:35,http://schuppe.info/clinton,1.0000000000,184.47.164.254,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n83,1,9,2017-04-06 17:45:08,http://boganschmeler.biz/amalia,1.0000000000,197.91.193.64,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n84,1,9,2017-03-13 23:54:40,http://pfannerstillbarton.co/lois,0.0000000000,165.79.104.156,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n85,1,9,2017-02-15 12:21:12,http://jenkins.name/mayra,0.0000000000,217.60.75.15,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n86,1,9,2017-01-29 13:30:54,http://wiza.name/bernard_upton,1.0000000000,10.218.34.162,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n87,1,10,2017-03-30 22:17:08,http://crist.net/scotty.rice,2.0000000000,57.155.245.154,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n88,1,10,2017-02-22 08:38:10,http://marks.com/sandra,3.0000000000,124.152.162.119,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n89,1,10,2017-05-21 08:54:16,http://lynchjakubowski.name/elmo,0.0000000000,249.133.137.221,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n90,1,10,2017-01-09 17:21:09,http://wuckert.net/terrence,0.0000000000,125.144.122.228,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n91,1,10,2017-05-04 05:15:38,http://schultzspencer.co/chandler,1.0000000000,125.144.122.228,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n92,1,11,2017-03-28 17:47:18,http://pollich.name/joel,0.0000000000,235.157.40.79,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n93,1,11,2017-02-22 06:52:18,http://ferrywalsh.org/cecile.green,0.0000000000,235.157.40.79,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n94,1,12,2017-01-08 20:58:48,http://becker.net/luisa,1.0000000000,212.122.53.48,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n95,1,12,2017-02-11 06:45:01,http://greenfelder.net/aron_bartell,0.0000000000,34.131.97.72,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n96,1,12,2017-04-14 12:57:57,http://konopelski.com/ruben_jenkins,0.0000000000,233.148.42.6,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n97,1,12,2017-05-17 00:45:30,http://langworth.io/joannie,1.0000000000,103.73.204.105,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n98,1,12,2017-01-02 06:40:06,http://waelchi.io/tony_kulas,0.0000000000,132.252.152.28,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n99,1,12,2017-03-08 11:42:42,http://sanford.info/sanford,0.0000000000,132.252.152.28,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n100,1,13,2017-03-12 13:57:04,http://lind.io/ryder_jacobs,1.0000000000,100.2.206.179,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n101,1,13,2017-01-01 10:35:05,http://luettgen.com/jalyn,1.0000000000,64.108.234.232,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n102,1,13,2017-03-16 14:09:52,http://gleichner.net/devante,0.0000000000,209.136.120.80,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n103,1,13,2017-05-15 18:05:38,http://swift.com/katlynn.romaguera,0.0000000000,203.32.168.121,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n104,1,13,2017-04-12 19:17:11,http://von.io/bailee,1.0000000000,226.6.126.75,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n105,1,13,2017-05-25 15:48:32,http://yost.org/felix,1.0000000000,65.211.177.182,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n106,1,13,2017-03-16 01:18:22,http://collier.net/carter,0.0000000000,48.167.63.144,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n107,1,13,2017-03-20 08:44:24,http://mraz.net/verlie,1.0000000000,97.15.210.10,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n108,1,13,2017-04-01 12:34:57,http://runolfsdottir.co/claudine,1.0000000000,203.158.111.152,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n109,1,13,2017-03-27 07:12:39,http://bruenrenner.io/percy.denesik,0.0000000000,175.113.113.181,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n5045,8,480,2017-05-13 17:32:59,http://spinka.name/muriel,0.0000000000,58.185.207.95,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n5046,8,480,2017-05-14 10:52:08,http://ferrykirlin.co/cielo.olson,1.0000000000,142.208.114.223,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n5047,8,480,2017-05-28 10:32:26,http://hermannhomenick.info/jadon.jacobson,0.0000000000,208.168.29.4,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n5048,8,481,2017-06-09 21:50:23,http://hermann.org/helene.raynor,,15.254.59.75,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n5049,8,481,2017-01-01 21:58:09,http://mertzrath.io/ursula,,17.178.107.53,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n5050,8,481,2016-12-18 04:20:14,http://cummerataherzog.biz/meda,,146.85.159.244,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n5051,8,481,2017-04-22 06:48:09,http://schultzfeeney.biz/reanna_gleason,,252.213.232.3,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n5052,8,481,2017-02-26 07:03:48,http://gleichner.io/aurelia,,191.209.228.129,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n5053,8,481,2017-02-17 00:00:15,http://gibson.info/rosalind,,178.98.49.155,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n5054,8,481,2017-04-27 01:37:27,http://stracke.org/jan,,59.140.183.157,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n5055,8,481,2017-06-05 06:40:05,http://satterfield.com/helene.johns,,67.142.20.145,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n5056,8,481,2017-03-12 06:10:30,http://schiller.org/zack,,252.213.232.3,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n5057,8,481,2017-03-15 04:21:23,http://funk.name/sammy_balistreri,,129.167.7.147,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n5058,8,481,2017-05-01 05:36:12,http://wildermanemard.name/murl,,228.35.169.167,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n5059,8,481,2017-03-31 23:46:50,http://skilestillman.co/mathias,,36.113.42.69,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n5060,8,482,2017-04-23 23:43:54,http://dickinsonokuneva.org/liam.emmerich,,144.233.252.168,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n5061,8,483,2017-02-04 08:11:43,http://harberkautzer.io/brendan,,244.181.17.231,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n5062,8,483,2016-12-25 02:56:39,http://stoltenbergmcclure.com/mariam,,2.116.204.19,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n5063,8,483,2017-01-09 23:16:10,http://ruel.co/maxime,,194.190.28.160,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n5064,8,483,2017-05-16 05:46:41,http://cormierziemann.info/aleen,,11.139.79.174,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n5065,8,483,2017-05-09 04:58:01,http://schowalteryost.org/jedidiah_wolff,,83.169.207.2,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n5066,8,483,2017-06-12 06:35:20,http://sanford.org/esperanza_mitchell,,198.137.173.251,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n5067,8,483,2017-03-07 17:42:50,http://conroylarson.org/kolby.collier,,97.192.165.188,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n5068,8,483,2016-12-31 18:55:17,http://okuneva.org/dustin_witting,,81.13.168.109,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n5069,8,483,2017-06-06 09:38:26,http://frami.name/roberta,,97.63.168.166,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n5070,8,483,2017-06-11 06:29:44,http://legros.org/monique,,83.169.207.2,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n5071,8,483,2017-01-01 11:06:05,http://bins.info/camilla_kemmer,,58.38.35.70,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n5072,8,483,2017-03-01 12:27:33,http://hicklekrajcik.io/jerald.herzog,,194.190.28.160,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n5073,8,483,2017-05-08 17:27:02,http://hermann.com/olin.langosh,,131.123.28.145,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n5074,8,483,2017-04-09 09:32:24,http://will.biz/selina.sawayn,,11.139.79.174,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n5075,8,483,2017-05-11 12:13:07,http://tillman.com/erika,,97.63.168.166,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n5076,8,483,2017-02-02 20:59:15,http://robel.co/esther_leuschke,,74.125.33.224,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n5077,8,484,2017-01-24 09:27:22,http://jast.biz/richard,,194.223.148.51,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n5078,8,484,2017-04-15 03:50:03,http://daugherty.org/augusta.koepp,,239.178.146.73,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n5079,8,484,2017-04-18 14:07:01,http://eichmann.io/katelynn_rutherford,,68.134.239.18,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n5080,8,484,2016-12-14 22:20:40,http://corkery.org/jena,,234.17.57.162,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n5081,8,484,2017-06-02 05:49:03,http://bailey.biz/adeline,,194.223.148.51,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n5082,8,484,2017-01-31 05:49:07,http://abbott.org/emery.adams,,205.177.182.6,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n5083,8,484,2017-04-10 11:38:18,http://stehr.io/hilda_roob,,144.164.59.136,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n5084,8,484,2017-05-19 17:17:08,http://howell.net/marlene,,116.83.106.83,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n5085,8,484,2017-04-21 04:53:41,http://murazik.name/kris,,64.204.178.136,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n5086,8,484,2017-05-25 09:13:48,http://dachlang.info/obie,,81.78.145.202,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n5087,8,484,2017-03-31 13:02:25,http://hettinger.co/ova_senger,,4.74.124.44,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n5088,8,484,2017-03-29 11:43:38,http://gutkowskilind.com/jarrett,,168.214.174.152,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n5089,8,485,2017-05-25 23:53:18,http://beer.com/george.frami,,235.137.76.4,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n5090,8,485,2017-04-02 11:45:54,http://lubowitzbosco.info/orrin_nader,,28.170.58.79,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n5091,8,485,2016-12-22 18:38:36,http://watsicadicki.biz/devan,,14.201.188.4,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n5092,8,485,2017-01-29 05:21:42,http://wehnertromp.biz/brice_witting,,6.103.81.39,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n5093,8,485,2016-12-19 00:05:48,http://lesch.info/zoey,,197.19.140.105,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n5094,8,485,2017-04-18 08:42:03,http://hills.biz/coy,,227.44.4.9,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n5095,8,485,2017-03-17 23:16:05,http://kemmerrosenbaum.com/adell,,180.22.172.27,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n5096,8,485,2016-12-15 07:01:22,http://yost.info/golden.gutkowski,,15.37.168.67,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n5097,8,486,2017-01-02 17:17:52,http://hoeger.name/keagan,,114.169.202.150,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n5098,8,486,2017-01-27 22:25:42,http://kozey.name/kaleigh_schimmel,,206.175.146.183,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n5099,8,486,2017-06-12 23:30:37,http://andersonkub.co/thora,,158.5.248.197,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n110,1,13,2017-04-06 01:18:58,http://johnstonbarton.net/roderick_altenwerth,1.0000000000,125.203.157.34,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n111,1,13,2016-12-26 03:52:53,http://brekke.org/ora,1.0000000000,122.111.204.246,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n112,1,13,2017-02-28 17:51:27,http://dibbert.info/frances,1.0000000000,165.219.39.78,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n113,1,13,2017-03-22 22:02:07,http://collins.com/shawn,1.0000000000,53.194.85.149,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n114,1,13,2017-04-17 22:36:27,http://casper.co/dagmar.macejkovic,1.0000000000,235.231.120.52,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n115,1,13,2017-02-27 03:36:52,http://swift.org/katherine,0.0000000000,49.145.31.127,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n116,1,13,2017-04-08 09:18:30,http://nitzsche.biz/kristin,1.0000000000,92.29.37.200,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n117,1,13,2017-01-26 09:17:40,http://hegmann.biz/audreanne.lesch,1.0000000000,122.111.204.246,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n118,1,14,2017-05-04 22:21:33,http://damore.biz/reggie.ledner,,4.16.78.229,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n119,1,14,2016-12-16 20:26:47,http://schultz.com/addison,,91.144.186.14,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n120,1,14,2017-06-08 02:58:56,http://rogahnlittle.com/breanne.ratke,,166.244.207.39,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n121,1,14,2016-12-13 19:06:07,http://jaskolski.org/johnathon,,180.69.135.140,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n122,1,14,2017-01-05 02:26:17,http://johnson.io/rosie,,77.215.102.213,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n123,1,14,2017-01-05 15:07:15,http://bogisich.co/talia,,112.219.73.105,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n124,1,14,2017-03-07 01:58:46,http://gusikowskihane.info/katheryn,,163.173.151.22,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n125,1,15,2017-05-28 09:10:04,http://ernsernader.co/milford.ebert,,92.34.252.21,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n126,1,15,2017-06-11 09:59:01,http://stammmertz.com/vernon.eichmann,,101.66.100.220,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n127,1,15,2017-05-18 09:39:54,http://lehner.info/kay_pouros,,87.160.127.187,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n128,1,15,2017-06-10 04:17:35,http://reinger.name/americo,,211.178.80.176,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n129,1,15,2017-01-16 01:12:28,http://moen.net/dayne_herman,,92.34.252.21,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n130,1,15,2017-05-12 20:22:43,http://gusikowski.co/davin,,211.178.80.176,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n131,1,15,2017-05-29 04:05:26,http://gusikowskipurdy.org/arvel_zieme,,251.217.158.103,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n132,1,15,2016-12-24 01:37:52,http://deckow.org/nikolas,,223.222.220.239,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n133,1,15,2017-01-20 05:39:14,http://lynchhayes.name/laila,,161.84.223.200,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n134,1,15,2017-03-06 21:33:28,http://rohanbashirian.net/braxton,,221.233.25.115,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n135,1,15,2017-02-16 07:05:02,http://bartoletti.info/enid,,210.30.115.194,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n136,1,15,2017-03-01 11:01:53,http://koelpin.org/eloise.hayes,,141.99.108.10,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n137,1,15,2017-01-28 04:51:51,http://walsh.net/joey,,68.3.160.12,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n138,1,15,2017-01-16 11:22:16,http://champlin.biz/mavis.stamm,,144.141.166.77,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n139,1,15,2017-01-19 02:15:44,http://rath.org/brown,,211.178.80.176,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n140,1,15,2017-02-13 21:27:11,http://ferryschmeler.io/breanna_graham,,178.109.238.51,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n141,1,16,2017-01-19 22:05:02,http://greenfelderoberbrunner.com/antone,,208.239.95.189,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n142,1,16,2016-12-18 15:47:21,http://padberggerhold.co/hilma,,111.159.232.223,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n143,1,16,2017-06-13 02:24:11,http://schambergerankunding.org/kylie,,137.254.40.244,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n144,1,16,2017-06-11 09:40:11,http://hegmann.net/jody,,160.86.178.11,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n145,1,16,2017-05-27 18:49:50,http://bogan.name/rosario.fay,,164.78.228.42,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n146,1,16,2016-12-31 13:21:15,http://schulist.com/malvina.lakin,,241.121.25.102,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n147,1,16,2017-05-01 14:07:19,http://littelpadberg.net/max,,180.211.239.105,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n148,1,16,2017-01-08 20:42:11,http://gutmann.net/jaleel.schmitt,,196.226.154.33,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n149,1,16,2016-12-31 20:10:13,http://robel.co/noah,,200.251.12.43,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n150,1,16,2016-12-21 02:10:46,http://murphy.name/tristin,,28.227.57.197,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n151,1,16,2017-01-06 08:49:00,http://little.name/edyth.wyman,,18.4.135.176,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n152,1,16,2017-05-13 19:24:24,http://pollich.com/austin,,217.94.118.186,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n153,1,16,2017-04-24 13:42:56,http://nikolauspouros.biz/eric_balistreri,,18.4.135.176,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n154,1,16,2017-05-15 12:44:22,http://larsonkeebler.co/misael,,65.64.24.12,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n155,1,16,2017-04-01 15:35:34,http://brekke.org/helene,,99.222.163.122,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n156,1,16,2017-01-09 19:50:05,http://dooleyweber.info/devon,,160.86.178.11,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n157,1,16,2017-05-10 20:32:31,http://torpheel.name/alfreda,,172.245.58.254,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n158,1,16,2016-12-29 03:06:25,http://effertzfahey.net/wendell,,65.64.24.12,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n159,1,16,2017-04-23 09:07:58,http://satterfield.co/roxanne,,46.245.224.45,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n160,1,16,2017-03-30 15:19:04,http://heaney.com/hubert,,99.222.163.122,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n161,1,17,2016-12-24 23:59:46,http://becker.info/howell,,11.127.105.64,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n162,1,17,2017-01-04 12:05:08,http://white.co/lily_kohler,,64.142.118.61,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n163,1,17,2017-01-10 01:14:26,http://fay.org/dangelo_goodwin,,3.68.20.223,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n164,1,17,2017-01-04 13:35:02,http://runte.biz/moshe,,211.132.206.71,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n165,1,17,2017-04-19 06:40:49,http://kuhn.name/emely,,217.120.35.226,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n166,1,17,2017-01-19 19:10:37,http://buckridge.biz/juanita,,217.120.35.226,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n5100,8,486,2017-01-28 19:00:18,http://bednar.name/kaycee_schowalter,,157.108.156.25,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n5101,8,486,2017-04-27 08:52:16,http://nienow.biz/anne.hodkiewicz,,234.122.140.185,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n5102,8,486,2017-04-05 18:32:14,http://wiegandrohan.net/dawson_monahan,,78.150.100.36,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n5103,8,486,2017-01-07 03:43:28,http://collierprohaska.info/davion.reynolds,,50.49.223.196,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n5104,8,486,2017-03-09 06:35:36,http://blicklindgren.io/humberto,,156.23.75.123,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n5105,8,486,2017-01-13 09:27:07,http://corkeryfay.com/noble.jast,,191.204.173.17,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n5106,8,486,2017-04-28 15:06:56,http://schiller.io/eduardo.little,,134.27.223.82,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n5107,8,486,2017-01-14 07:25:13,http://lueilwitzbreitenberg.info/aaliyah_kling,,209.236.156.35,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n5108,8,486,2017-05-05 03:50:21,http://nicolas.org/jaquelin,,114.169.202.150,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n5109,8,486,2016-12-14 01:51:12,http://ankunding.info/arnoldo_corwin,,220.74.83.110,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n5110,8,486,2017-03-01 22:00:07,http://vandervort.com/gabrielle.hauck,,156.23.75.123,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n5111,8,486,2016-12-13 08:36:28,http://rohan.net/leopoldo.wintheiser,,157.7.220.14,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n5112,8,486,2017-03-20 09:23:51,http://ullrich.info/elvis_hagenes,,209.236.156.35,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n5113,8,486,2017-04-19 05:51:14,http://paucek.info/lucius_swaniawski,,166.97.171.170,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n5114,8,486,2017-02-18 19:22:59,http://hills.com/kylie,,220.74.83.110,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n5115,8,486,2016-12-21 23:48:58,http://framibeatty.co/elmer,,50.49.223.196,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n5116,8,486,2017-03-10 07:15:16,http://bartell.net/josie.grant,,14.250.45.44,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n5117,8,487,2017-01-12 20:44:54,http://stehr.io/kelley,,188.224.198.161,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n5118,8,487,2016-12-16 05:24:13,http://mrazfarrell.info/uriel,,158.223.154.120,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n5119,8,487,2017-04-05 10:06:01,http://huelheel.co/kameron_schuppe,,229.8.167.173,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n5120,8,487,2017-03-04 08:39:10,http://macejkovic.biz/ryan,,108.108.213.63,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n5121,8,487,2017-05-12 10:39:41,http://flatley.name/kitty,,148.131.181.80,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n5122,8,487,2017-03-15 11:02:52,http://veum.info/forrest_keeling,,197.178.21.250,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n5123,8,487,2017-02-24 22:55:35,http://feest.org/dayne,,134.115.247.156,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n5124,8,487,2017-03-06 11:28:18,http://waters.io/audreanne,,188.224.198.161,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n5125,8,487,2017-04-08 09:28:35,http://dicki.net/tiana_zboncak,,50.101.87.119,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n5126,8,487,2017-03-06 05:42:13,http://kshlerin.org/yasmin_bauch,,104.157.94.128,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n5127,8,487,2017-03-06 00:46:26,http://mohr.name/annetta.veum,,69.108.223.44,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n5128,8,487,2017-04-20 21:28:33,http://kling.io/johnpaul,,49.189.202.195,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n5129,8,487,2017-06-10 05:47:39,http://miller.biz/cicero_leannon,,4.51.36.51,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n5130,8,487,2017-01-03 07:33:23,http://abbott.org/erick,,51.116.171.120,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n5131,8,487,2017-03-02 20:23:18,http://harveygrimes.name/theodora_rau,,215.132.27.109,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n5132,8,488,2016-12-18 17:13:04,http://gerlachschultz.com/enola.kris,,97.50.68.95,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n5133,8,488,2017-02-28 07:27:14,http://volkman.biz/tyson,,38.153.211.113,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n5134,8,488,2017-06-08 03:48:53,http://batz.com/gianni,,82.134.17.167,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n5135,8,488,2017-03-14 04:28:34,http://hilll.com/mckayla,,25.223.182.214,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n5136,8,488,2017-05-16 17:06:25,http://kihn.net/benny,,181.197.185.219,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n5137,8,488,2017-04-21 09:36:22,http://willms.io/janie,,38.153.211.113,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n5138,8,488,2016-12-19 23:09:36,http://langworthpfeffer.name/brent.jakubowski,,108.89.12.177,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n5139,8,488,2017-06-09 06:05:49,http://fritsch.co/randall.veum,,101.209.132.21,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n5140,8,488,2017-03-06 00:55:26,http://streichspencer.io/murphy.stokes,,115.248.193.24,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n5141,8,488,2016-12-25 22:01:49,http://wilderman.io/kyla,,73.156.26.52,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n5142,8,488,2017-06-03 17:42:26,http://ondrickalangworth.name/elouise.wilderman,,207.238.22.60,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n5143,8,488,2017-02-03 10:37:45,http://andersonpollich.com/pablo_nolan,,87.160.54.29,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n5144,8,488,2017-02-11 03:33:29,http://sawayn.co/makayla,,30.251.83.117,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n5145,8,489,2017-04-20 20:23:49,http://christiansenhodkiewicz.co/ernestina,,239.23.190.63,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n5146,8,490,2017-05-21 09:07:42,http://goyette.biz/rafael,,208.176.155.69,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n5147,8,490,2017-03-29 12:25:12,http://king.co/rollin_dach,,182.202.228.145,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n5148,8,490,2017-04-21 15:41:16,http://leannon.net/shad,,102.175.191.121,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n5149,8,490,2017-02-14 16:35:25,http://mills.biz/arvid,,132.98.39.221,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n5150,8,490,2016-12-28 15:09:11,http://schmidtbergnaum.info/diana,,182.202.228.145,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n5151,8,490,2017-03-12 15:33:14,http://connelly.co/jamel.will,,254.208.112.78,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n5152,8,490,2017-04-04 14:09:56,http://wisoky.biz/myles,,81.61.139.203,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n5153,8,490,2016-12-31 01:01:04,http://botsfordkihn.com/linda,,102.175.191.121,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n5154,8,490,2017-02-22 16:45:10,http://pouros.net/joseph,,11.19.6.197,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n5155,8,490,2017-02-28 22:48:17,http://hahnskiles.info/yasmine,,254.208.112.78,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n5156,8,490,2016-12-27 18:29:02,http://oberbrunnervonrueden.com/kailyn_boyle,,254.208.112.78,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n5157,8,490,2017-01-14 09:22:08,http://renner.biz/christy.stroman,,132.98.39.221,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n5158,8,490,2017-05-06 19:22:28,http://pagac.net/burley,,142.83.111.54,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n5159,8,491,2017-06-02 14:23:00,http://kuphalconnelly.name/savanah,1.0000000000,68.125.71.247,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n5160,8,491,2017-01-29 13:25:09,http://ratke.info/aliya,1.0000000000,54.112.138.141,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n5161,8,491,2017-06-13 19:01:56,http://gutmann.org/guy,0.0000000000,38.176.207.118,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n5162,8,491,2017-01-08 02:19:46,http://hammes.name/kamryn_sporer,2.0000000000,222.124.33.186,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n5163,8,491,2017-02-20 18:39:35,http://schimmel.net/gladyce_oconnell,2.0000000000,49.106.162.10,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n5164,8,491,2017-03-18 14:47:18,http://smithbartell.org/brice,0.0000000000,119.154.206.69,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n5165,8,491,2017-05-06 22:21:40,http://fay.name/margaretta.ankunding,1.0000000000,171.136.111.246,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n5166,8,491,2017-01-05 17:31:48,http://hilll.co/asia.greenholt,1.0000000000,101.60.159.103,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n5167,8,491,2017-05-30 04:04:53,http://klockowisozk.com/clair.hodkiewicz,2.0000000000,120.232.60.19,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n5168,8,491,2017-05-29 19:51:49,http://dickinsonkeebler.net/franz,0.0000000000,181.6.59.27,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n5169,8,491,2017-01-13 00:24:54,http://hackett.org/vicente,0.0000000000,250.227.210.90,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n5170,8,491,2017-01-22 22:22:22,http://mccullough.net/marguerite,0.0000000000,89.124.68.64,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n5171,8,491,2016-12-25 19:39:03,http://blick.net/modesto.frami,2.0000000000,27.103.205.92,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n5172,8,491,2017-04-17 12:05:23,http://hicklegutmann.info/ariane,0.0000000000,254.77.2.32,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n5173,8,491,2017-04-18 23:48:14,http://krajcik.io/randal,2.0000000000,107.232.112.244,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n5174,8,491,2017-06-14 01:06:04,http://kub.co/chyna.miller,1.0000000000,146.20.27.20,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n5175,8,491,2016-12-23 08:08:33,http://mccullough.biz/krystina,1.0000000000,222.124.33.186,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n5176,8,491,2017-01-17 01:25:57,http://hoppe.com/vivienne.reilly,1.0000000000,181.6.59.27,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n5177,8,492,2017-02-19 15:50:18,http://corkery.com/samir,3.0000000000,84.226.91.111,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n5178,8,492,2017-01-18 08:04:20,http://waelchi.co/owen,0.0000000000,240.206.93.248,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n5179,8,492,2017-05-29 00:57:36,http://klocko.net/twila_torphy,1.0000000000,172.159.253.45,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n5180,8,492,2017-02-25 12:46:19,http://luettgen.io/zelma,0.0000000000,177.54.237.98,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n5181,8,492,2017-02-15 00:30:29,http://emardjenkins.info/breana,1.0000000000,112.111.95.177,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n5182,8,492,2016-12-18 00:52:05,http://weinatwalter.org/toby_beer,0.0000000000,38.18.62.64,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n5183,8,492,2017-06-13 09:30:34,http://steuber.info/lillie,1.0000000000,205.188.73.202,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n5184,8,492,2017-02-07 22:17:30,http://streich.net/jettie,3.0000000000,226.186.156.92,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n5185,8,492,2017-01-15 12:43:55,http://turcotte.biz/rollin,2.0000000000,32.167.82.157,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n5186,8,492,2017-03-07 20:42:27,http://botsford.com/vivienne,1.0000000000,240.254.239.236,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n5187,8,492,2017-04-08 19:59:16,http://romaguerastamm.biz/hector_ernser,1.0000000000,240.254.239.236,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n5188,8,492,2017-04-02 13:03:49,http://vandervortwhite.biz/bart,0.0000000000,35.136.67.33,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n5189,8,492,2017-06-02 07:22:26,http://raynor.co/romaine.vonrueden,1.0000000000,157.53.197.53,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n5190,8,492,2017-04-18 03:51:41,http://frami.io/clint,3.0000000000,186.22.41.30,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n5191,8,492,2017-02-02 15:03:00,http://lakindare.info/sandy.padberg,2.0000000000,191.86.44.17,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n5192,8,492,2017-05-20 22:34:58,http://abbottsanford.net/trycia,3.0000000000,2.16.168.30,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n5193,8,493,2017-02-13 12:15:09,http://fritsch.org/jaylon,0.0000000000,180.220.98.82,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n5194,8,493,2017-01-23 05:57:04,http://howell.io/icie.macejkovic,2.0000000000,116.237.233.225,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n5195,8,493,2017-06-08 12:35:36,http://rueckerkrajcik.com/breanna,2.0000000000,144.153.229.244,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n5196,8,493,2017-01-17 08:41:39,http://harris.co/rasheed_goodwin,0.0000000000,180.220.98.82,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n5197,8,493,2017-02-19 10:37:46,http://ondricka.name/therese,2.0000000000,222.12.97.124,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n5198,8,493,2016-12-14 23:56:58,http://sawayn.biz/xavier.williamson,1.0000000000,48.26.139.25,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n5199,8,493,2017-06-10 02:57:29,http://murphy.biz/eldridge.berge,0.0000000000,206.50.193.114,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n5200,8,493,2017-03-14 19:07:46,http://klocko.io/dorian,2.0000000000,140.28.118.101,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n5201,8,493,2017-05-09 06:13:37,http://jones.name/donny_miller,2.0000000000,140.28.118.101,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n5202,8,493,2017-05-20 08:11:01,http://leuschke.name/verda_williamson,0.0000000000,116.237.233.225,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n5203,8,493,2017-03-08 15:44:29,http://pacocha.info/nikko.ko,1.0000000000,123.67.83.25,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n5204,8,493,2017-05-24 11:49:44,http://turneradams.io/floy.johnston,1.0000000000,158.221.125.212,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n5205,8,493,2017-01-05 00:57:22,http://mosciski.com/maye.daugherty,1.0000000000,41.162.226.178,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n5206,8,493,2017-05-16 19:49:21,http://kuphalebert.org/archibald,1.0000000000,111.158.102.106,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n5207,8,493,2017-05-22 08:03:11,http://gutkowskiupton.net/annamarie,0.0000000000,151.45.8.74,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n5208,8,493,2017-03-13 11:51:57,http://koepp.io/ellsworth_gibson,0.0000000000,215.253.125.219,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n5209,8,493,2017-01-10 18:50:08,http://wiegand.org/ernestina.toy,2.0000000000,186.155.110.97,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n5210,8,493,2017-01-19 09:57:07,http://botsford.name/marlon,0.0000000000,186.155.110.97,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n5211,8,493,2017-04-07 13:26:39,http://conroy.name/rickey,2.0000000000,79.182.78.127,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n5212,8,493,2017-06-01 04:31:14,http://block.name/coty,2.0000000000,158.221.125.212,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n5213,8,494,2017-02-22 17:49:06,http://kerluke.biz/tyson_lind,0.0000000000,224.214.234.239,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n5214,8,494,2017-01-30 17:23:38,http://hyattsanford.com/chelsey,0.0000000000,180.211.252.58,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n5215,8,494,2017-01-05 02:40:14,http://hackett.biz/santino,1.0000000000,153.98.128.98,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n5216,8,494,2017-03-04 13:59:10,http://okunevagoodwin.name/elvie_champlin,1.0000000000,156.176.3.63,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n5217,8,494,2017-04-07 03:49:08,http://herzog.org/alberta,0.0000000000,136.72.126.15,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n5218,8,494,2017-03-08 01:38:13,http://braunhahn.io/florian.klein,1.0000000000,224.214.234.239,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n5219,8,495,2017-04-21 22:22:37,http://wunschkemmer.co/else,1.0000000000,202.147.206.115,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n5220,8,495,2017-03-27 18:34:05,http://oconner.biz/ferne_thiel,1.0000000000,91.63.25.60,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n5221,8,495,2017-02-19 07:05:29,http://nikolaus.name/lorenz,0.0000000000,178.161.204.244,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n5222,8,495,2017-04-30 09:43:25,http://sporerboehm.org/waldo,1.0000000000,119.32.137.126,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n5223,8,495,2017-01-25 07:59:03,http://franecki.co/rory,1.0000000000,2.159.121.233,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n5224,8,495,2017-01-16 10:32:40,http://stantongreenfelder.org/ronny.wyman,2.0000000000,12.183.199.191,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n5225,8,495,2017-03-21 06:48:04,http://wintheiser.biz/katharina,1.0000000000,37.72.219.225,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n5226,8,495,2017-03-16 03:16:42,http://conroy.name/efrain_erdman,1.0000000000,36.45.243.203,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n5227,8,495,2017-03-26 14:19:48,http://bradtke.co/jodie,0.0000000000,218.120.174.50,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n5228,8,495,2017-03-26 18:30:26,http://lind.biz/rene,0.0000000000,225.214.27.204,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n5229,8,495,2017-04-19 01:12:09,http://moriette.net/hudson_littel,1.0000000000,158.23.123.7,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n5230,8,495,2017-04-29 14:23:19,http://predovic.net/lorenzo.ryan,2.0000000000,113.196.197.220,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n5231,8,495,2017-01-31 17:21:53,http://will.biz/hannah,0.0000000000,81.89.235.61,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n5232,8,495,2016-12-27 18:01:05,http://kuhic.net/clemens,1.0000000000,184.66.214.13,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n5233,8,495,2017-04-16 20:03:15,http://doyle.co/lorena,2.0000000000,118.95.243.63,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n5234,8,495,2016-12-29 07:06:54,http://pfefferlangworth.biz/george.rath,2.0000000000,225.214.27.204,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n5235,8,496,2017-04-01 18:16:08,http://mayer.io/waylon,0.0000000000,103.27.160.227,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n5236,8,496,2017-03-28 17:35:43,http://wolfzboncak.info/armando,0.0000000000,30.247.46.66,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n5237,8,496,2017-03-19 22:01:37,http://stark.biz/laurence.douglas,0.0000000000,92.85.87.154,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n5238,8,496,2017-02-11 02:32:01,http://krisolson.io/richard.wisozk,0.0000000000,203.32.75.137,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n5239,8,496,2016-12-26 01:55:10,http://marvin.info/ania,0.0000000000,89.174.203.75,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n5240,8,496,2017-04-20 16:43:18,http://hartmann.net/lacey.mueller,0.0000000000,92.213.107.2,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n5241,8,496,2017-06-13 23:22:46,http://franecki.info/lesley.dickinson,0.0000000000,14.27.132.213,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n5242,8,496,2017-01-12 21:07:17,http://will.com/miles.kilback,0.0000000000,185.196.173.237,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n5243,8,496,2017-04-24 10:43:49,http://wymanreilly.info/kacey_hane,0.0000000000,78.81.125.120,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n5244,8,497,2017-01-13 06:34:57,http://shieldsemard.org/thalia.bruen,0.0000000000,146.156.212.234,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n5245,8,497,2017-04-26 20:05:35,http://kling.info/korbin_carter,0.0000000000,239.125.204.25,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n5246,8,497,2017-02-02 20:06:16,http://batz.name/kylie_kuhic,0.0000000000,180.96.105.99,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n5247,8,497,2017-05-29 12:11:53,http://heaney.org/rae_gleason,0.0000000000,29.120.136.66,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n5248,8,497,2017-05-26 18:57:57,http://hyatt.net/johnson_kuhic,1.0000000000,105.238.242.61,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n5249,8,497,2017-05-09 21:11:11,http://lebsack.io/virgie,1.0000000000,202.107.42.65,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n5250,8,497,2017-06-07 00:59:00,http://graham.info/elliot,1.0000000000,67.143.35.3,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n5251,8,497,2017-04-07 19:44:34,http://dibbert.name/yoshiko,1.0000000000,16.39.113.108,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n5252,8,497,2017-02-20 19:32:53,http://satterfieldvon.com/caterina,1.0000000000,4.11.63.54,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n5253,8,497,2017-04-14 17:27:25,http://abshire.net/demetris.hartmann,0.0000000000,102.252.130.43,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n5254,8,497,2017-03-09 14:10:05,http://lindgren.io/milo_marks,0.0000000000,16.39.113.108,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n5255,8,497,2017-02-10 07:25:22,http://schimmelreichert.co/stephania.runolfon,1.0000000000,35.176.199.233,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n5256,8,497,2017-02-16 18:48:33,http://langhills.io/lexi_ebert,1.0000000000,44.108.145.51,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n5257,8,497,2017-04-18 23:02:22,http://abshireheller.info/cornelius,1.0000000000,56.174.69.148,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n5258,8,497,2017-02-22 21:02:47,http://breitenberg.io/stone.farrell,0.0000000000,16.39.113.108,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n5259,8,497,2017-01-06 05:50:31,http://gusikowskithompson.co/hunter_wuckert,1.0000000000,233.172.28.108,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n5260,8,497,2017-03-03 17:47:38,http://price.co/haleigh_kub,0.0000000000,209.184.143.56,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n5261,8,497,2016-12-19 08:01:02,http://frami.biz/lou.gutkowski,0.0000000000,77.115.39.58,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n5262,8,497,2017-01-06 05:38:41,http://mayerkoch.info/grant_lubowitz,1.0000000000,180.96.105.99,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n5263,8,498,2017-06-03 11:52:40,http://mcdermott.com/suzanne,1.0000000000,106.52.179.97,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n5264,8,498,2017-01-12 19:20:52,http://prosacco.net/bridie,0.0000000000,69.43.203.250,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n5265,8,498,2017-04-28 19:16:23,http://kuhn.name/roger,0.0000000000,215.172.85.51,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n5266,8,499,2017-04-05 17:54:14,http://williamson.biz/kayleigh,1.0000000000,44.21.20.24,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n5267,8,499,2017-04-05 01:24:03,http://dooley.co/hillard_hettinger,0.0000000000,202.93.42.79,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n5268,8,499,2017-01-24 23:52:16,http://ruel.io/jovany,1.0000000000,44.21.20.24,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n5269,8,499,2017-01-09 01:17:21,http://grantmclaughlin.info/madison.kunze,0.0000000000,61.12.179.59,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n5270,8,500,2017-06-01 05:01:00,http://goodwin.com/elwyn.nicolas,0.0000000000,49.68.161.238,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n5271,8,500,2017-05-05 12:57:26,http://turcotte.co/jeanette,0.0000000000,145.125.14.43,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n5272,8,500,2017-04-11 09:30:58,http://fritschfisher.name/josiane,0.0000000000,2.47.51.155,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n5273,8,500,2017-05-22 14:26:08,http://reichelhettinger.name/jeremie.crona,0.0000000000,13.150.27.243,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n5274,8,500,2017-01-13 00:58:45,http://trantownicolas.net/kaitlyn_quigley,0.0000000000,193.4.4.92,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n5275,8,500,2017-04-07 21:48:24,http://witting.io/milan,0.0000000000,46.81.58.21,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n5276,8,500,2017-02-19 22:02:12,http://marvin.info/reginald.reichert,0.0000000000,116.69.131.253,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n5277,8,501,2017-06-08 01:32:28,http://lockmanheathcote.org/darion,0.0000000000,66.100.91.108,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n5278,8,501,2016-12-20 22:33:13,http://funk.biz/dion,2.0000000000,30.35.124.38,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n5279,8,501,2017-05-09 04:24:18,http://yundt.name/karl_mcclure,2.0000000000,109.62.56.22,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n5280,8,501,2017-04-10 04:43:47,http://marvin.io/dylan,1.0000000000,228.46.142.103,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n5281,8,501,2017-05-04 05:55:24,http://andersonkuvalis.biz/shyanne.will,2.0000000000,16.49.212.67,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n5282,8,501,2017-04-19 02:03:30,http://collinscrist.io/kris.reichel,0.0000000000,205.127.51.193,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n5283,8,501,2017-03-18 00:10:29,http://olson.co/darrin_cartwright,1.0000000000,85.86.158.15,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n5284,8,501,2017-03-16 02:03:21,http://windlerhowe.io/leola_brakus,2.0000000000,26.54.145.98,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n5285,8,502,2017-04-15 22:00:19,http://hoppewehner.biz/herta_marks,0.0000000000,183.64.51.38,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n5286,8,502,2017-05-17 15:32:35,http://welch.name/elise,1.0000000000,115.11.28.82,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n5287,8,503,2017-04-23 17:14:43,http://torp.name/adam,3.0000000000,212.55.202.24,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n5288,8,503,2017-01-14 15:28:38,http://schroedersteuber.co/theodore_doyle,1.0000000000,246.160.80.126,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n5289,8,503,2017-03-28 21:16:08,http://haneblock.com/marianna.steuber,1.0000000000,239.158.39.115,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n5290,8,503,2017-03-17 12:30:48,http://krisvon.co/camylle,3.0000000000,88.70.251.62,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n5291,8,503,2017-02-20 07:39:11,http://wintheiser.org/frieda.douglas,0.0000000000,85.56.238.200,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n5292,8,503,2017-03-13 16:54:17,http://hoegergleichner.biz/nicolas,0.0000000000,132.153.143.205,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n5293,8,503,2017-01-16 18:00:01,http://powlowskidaniel.co/francesca,1.0000000000,26.127.228.35,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n5294,8,503,2017-02-14 11:28:28,http://sipes.biz/arlie,3.0000000000,14.253.125.60,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n5295,8,503,2017-04-07 06:06:44,http://lubowitz.net/connor,1.0000000000,218.3.166.102,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n5296,8,503,2017-04-12 00:14:27,http://goldner.org/damon,3.0000000000,40.2.240.157,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n5297,8,503,2017-06-02 18:35:34,http://becker.co/dax,3.0000000000,246.160.80.126,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n5298,8,503,2017-03-11 01:33:41,http://baileyyost.co/molly_macejkovic,1.0000000000,212.55.202.24,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n5299,8,503,2017-01-01 03:36:41,http://lynch.name/herminio.buckridge,3.0000000000,218.3.166.102,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n5300,8,503,2017-06-05 03:07:37,http://oconner.name/rigoberto,3.0000000000,201.175.247.60,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n5301,8,503,2017-02-04 22:55:17,http://davis.co/deie_kuhn,2.0000000000,124.178.3.191,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n5302,8,503,2017-05-22 11:12:00,http://willjacobs.net/candace.hermann,0.0000000000,252.136.82.196,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n5303,8,503,2017-02-28 21:40:43,http://fritsch.org/antonetta,1.0000000000,246.160.80.126,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n5304,8,503,2017-04-06 23:37:30,http://metz.org/delmer,0.0000000000,251.248.245.191,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n5305,8,504,2017-05-24 14:32:16,http://fahey.net/hattie,0.0000000000,72.93.124.208,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n5306,8,504,2017-06-10 16:13:52,http://satterfield.io/jadon_hauck,0.0000000000,187.194.91.107,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n5307,8,504,2017-05-09 23:15:18,http://collier.io/kelsie.champlin,0.0000000000,86.63.23.45,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n5308,8,504,2017-03-24 06:20:12,http://reynolds.name/glen,0.0000000000,232.214.87.247,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n5309,8,504,2017-03-17 12:00:48,http://cremin.io/victor,0.0000000000,56.221.94.164,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n5310,8,504,2017-04-10 19:08:57,http://gradyolson.info/isidro,0.0000000000,40.196.114.41,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n5311,8,504,2017-01-10 02:05:24,http://handgutmann.co/laria,0.0000000000,72.57.194.73,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n5312,8,504,2017-01-06 07:37:13,http://leannon.biz/toby.cormier,0.0000000000,117.68.23.220,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n5313,8,504,2017-02-08 05:09:10,http://senger.info/gino_morar,0.0000000000,117.68.23.220,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n5314,8,504,2017-03-23 11:17:23,http://lueilwitz.net/janice,0.0000000000,155.86.59.112,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n5315,8,504,2017-01-08 13:35:04,http://legros.io/solon.zulauf,0.0000000000,130.253.54.245,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n5316,8,504,2017-02-05 20:49:07,http://klingnolan.co/joe.hilll,0.0000000000,6.179.177.119,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n5317,8,504,2017-01-19 13:07:03,http://wizabradtke.co/winona,0.0000000000,48.113.74.188,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n5318,8,505,2017-02-28 14:44:43,http://von.biz/hazel.adams,3.0000000000,132.209.245.7,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n5319,8,505,2017-03-10 04:58:08,http://heidenreich.biz/deven.schoen,0.0000000000,45.209.243.200,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n5320,8,505,2017-03-21 11:41:47,http://jenkins.name/clifford_deckow,2.0000000000,181.210.55.214,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n5321,8,505,2017-05-29 17:41:38,http://tromproberts.info/michel,1.0000000000,162.78.174.133,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n5322,8,505,2017-01-13 09:04:46,http://lynch.co/alfredo.keebler,0.0000000000,181.210.55.214,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n5323,8,505,2017-06-09 02:51:06,http://jacobsonhagenes.biz/dallas_crona,3.0000000000,228.117.188.3,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n5324,8,505,2017-03-28 21:18:49,http://hegmann.net/albert,0.0000000000,60.224.137.248,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n5325,8,505,2017-06-08 17:28:33,http://dach.io/dashawn,1.0000000000,228.117.188.3,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n5326,8,505,2017-01-30 08:27:19,http://aufderhar.io/sammy.robel,1.0000000000,7.95.10.121,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n5327,8,506,2017-04-06 04:27:04,http://schmeler.org/mackenzie,0.0000000000,100.153.104.82,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n5328,8,506,2017-04-08 19:38:56,http://volkmandare.net/rubye.hackett,0.0000000000,174.21.5.161,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n5329,8,506,2017-02-01 11:33:35,http://erdman.io/derrick.carroll,0.0000000000,236.143.131.215,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n5330,8,506,2017-01-05 23:22:34,http://homenickblock.info/fidel,0.0000000000,236.143.131.215,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n5331,8,506,2017-04-12 19:27:37,http://rohan.info/colton,0.0000000000,231.66.221.4,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n5332,8,506,2017-01-16 06:13:24,http://walterdach.co/bridgette,0.0000000000,85.140.253.70,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n5333,8,506,2017-06-08 18:33:41,http://koeppboyle.org/llewellyn_spinka,0.0000000000,54.167.226.30,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n5334,8,506,2017-04-11 04:32:23,http://eichmann.net/lyda_runolfon,0.0000000000,42.100.126.145,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n5335,8,506,2017-04-06 06:10:17,http://miller.info/olin,0.0000000000,168.150.197.249,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n5336,8,506,2017-01-17 18:44:26,http://veum.biz/hoyt_howell,0.0000000000,158.90.189.144,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n5337,8,506,2017-05-19 03:12:12,http://thiel.name/mallory,0.0000000000,168.150.197.249,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n5338,8,506,2017-01-29 09:54:12,http://terry.info/chadrick,0.0000000000,102.222.189.236,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n5339,8,506,2017-02-02 07:29:16,http://weber.io/darien,0.0000000000,16.42.129.190,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n5340,8,506,2017-01-25 19:13:23,http://maggio.net/neal_weinat,0.0000000000,42.100.126.145,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n5341,8,506,2016-12-29 01:01:01,http://jakubowski.co/ward.friesen,0.0000000000,231.66.221.4,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n5342,8,506,2016-12-23 13:36:54,http://daniel.biz/raquel_mitchell,0.0000000000,155.111.169.43,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n5343,8,507,2017-01-24 09:24:16,http://streichpagac.co/corene,0.0000000000,9.104.152.83,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n5344,8,507,2017-04-06 01:11:54,http://haley.co/carlee.oberbrunner,0.0000000000,165.67.161.193,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n5345,8,507,2017-06-02 23:38:54,http://ortiz.org/buster_pagac,0.0000000000,141.203.12.242,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n5346,8,507,2017-03-28 02:10:41,http://terryterry.io/maxwell.fritsch,0.0000000000,222.173.40.128,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n5347,8,507,2017-02-04 12:27:38,http://schuster.com/carlee.cole,0.0000000000,239.209.219.246,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n5348,8,507,2017-02-25 05:12:56,http://roberts.info/maya,0.0000000000,42.84.231.207,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n5349,8,507,2017-03-17 04:19:17,http://ledner.info/astrid,0.0000000000,44.17.83.177,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n5350,8,507,2017-03-09 11:14:24,http://mohr.info/arvid_shanahan,0.0000000000,211.240.105.187,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n5351,8,507,2017-04-01 01:33:43,http://hermiston.co/nora.breitenberg,0.0000000000,132.241.217.118,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n5352,8,507,2017-03-13 03:34:31,http://marquardt.org/jaeden.balistreri,0.0000000000,89.218.139.92,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n5353,8,507,2017-04-06 22:52:40,http://sipesheller.com/aurelio,0.0000000000,231.228.122.123,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n5354,8,507,2017-05-12 15:53:17,http://wehnerweinat.com/maxwell.jacobi,0.0000000000,161.102.121.155,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n5355,8,507,2017-01-13 18:06:06,http://schroedermuller.org/pascale,0.0000000000,92.143.45.72,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n5356,8,507,2017-01-11 00:14:15,http://homenick.io/ricardo,0.0000000000,103.133.128.65,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n5357,8,508,2017-01-22 07:08:52,http://pfeffercasper.biz/alberto,0.0000000000,39.86.47.114,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n5358,8,508,2017-03-26 04:04:03,http://schaefer.co/cortney,0.0000000000,68.210.118.62,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n5359,8,508,2017-05-05 15:55:19,http://gutkowskikunde.io/bradford,0.0000000000,48.33.116.180,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n5360,8,509,2017-01-12 12:55:44,http://schusterpaucek.name/eliezer_schowalter,1.0000000000,236.239.104.216,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n5361,8,509,2017-03-09 06:48:17,http://padberg.co/kaylah,2.0000000000,160.130.107.6,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n5362,8,509,2017-01-23 11:33:32,http://turcotte.co/fae.ruel,2.0000000000,33.50.22.113,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n5363,8,509,2017-03-08 18:04:58,http://wolf.org/raul,0.0000000000,200.42.157.40,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n5364,8,509,2016-12-23 11:07:56,http://gutkowski.net/amber.ziemann,2.0000000000,163.65.70.68,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n5365,8,509,2017-02-08 05:50:11,http://rowe.net/jee_bradtke,2.0000000000,201.40.208.243,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n5366,8,509,2017-05-14 12:54:04,http://bartonlueilwitz.biz/kayleigh_robel,2.0000000000,166.160.24.180,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n5367,8,510,2017-02-27 08:21:24,http://kuhn.info/horace.jerde,0.0000000000,47.239.211.15,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n5368,8,510,2017-01-02 09:44:06,http://harris.co/kiley,2.0000000000,143.228.38.33,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n5369,8,510,2017-02-15 07:08:45,http://kerluke.io/floy_jaskolski,1.0000000000,19.138.242.34,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n5370,8,510,2017-01-22 03:12:32,http://tillmanmckenzie.net/antonetta.fadel,1.0000000000,235.178.36.105,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n5371,8,510,2017-01-12 14:52:27,http://vandervort.org/peggie_towne,0.0000000000,128.80.57.154,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n5372,8,510,2017-05-26 07:15:02,http://mayer.biz/rae,1.0000000000,169.126.195.34,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n5373,8,511,2016-12-31 13:57:33,http://hermann.net/erika,1.0000000000,26.80.109.112,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n5374,8,512,2017-05-25 22:45:31,http://dach.net/pete_gislason,0.0000000000,179.156.226.18,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n5375,8,512,2017-02-09 08:43:56,http://kovacekreinger.co/cordelia,0.0000000000,161.87.104.73,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n5376,8,512,2017-04-18 13:15:20,http://jerde.co/ari.robel,1.0000000000,202.150.249.114,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n5377,8,512,2016-12-28 01:02:26,http://beattyquitzon.io/andre.stoltenberg,0.0000000000,202.150.249.114,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n5378,8,512,2016-12-13 06:40:00,http://greenjaskolski.co/sylvan,0.0000000000,62.70.214.228,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n5379,8,512,2017-04-16 08:56:30,http://lind.org/mariano.larkin,1.0000000000,13.162.125.248,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n5380,8,512,2017-01-28 16:04:07,http://hermistonswaniawski.biz/nichole.franecki,0.0000000000,137.213.121.48,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n5381,8,512,2016-12-13 08:33:07,http://hoegerdeckow.org/ludie.price,1.0000000000,163.115.226.245,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n5382,8,512,2017-04-18 19:21:38,http://kuhic.net/deondre,1.0000000000,5.126.85.253,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n5383,8,512,2017-03-09 22:44:00,http://fadel.name/evert,0.0000000000,161.87.104.73,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n5384,8,512,2017-02-15 08:27:08,http://ko.name/armand.blanda,1.0000000000,223.231.138.91,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n5385,8,512,2017-05-02 07:24:15,http://hamilldickinson.net/reece,1.0000000000,172.50.119.105,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n5386,8,512,2017-04-22 06:35:54,http://mullerwolff.com/bruce_ziemann,1.0000000000,132.52.96.115,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n5387,8,512,2017-06-13 19:05:40,http://hoppe.com/major.kertzmann,1.0000000000,179.156.226.18,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n5388,8,512,2017-04-30 11:58:49,http://wolff.co/theresia,1.0000000000,167.81.87.6,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n5389,8,512,2017-03-25 05:26:28,http://wisozktillman.info/nannie,0.0000000000,8.125.64.24,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n5390,8,513,2017-03-04 00:03:51,http://conroy.com/delaney_mccullough,2.0000000000,143.224.189.42,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n5391,8,513,2017-05-02 11:11:08,http://murraywuckert.info/cali_mayert,0.0000000000,96.33.181.204,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n5392,8,513,2016-12-17 18:42:38,http://schuppe.org/moshe.mayer,2.0000000000,218.95.13.43,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n5393,8,513,2017-01-03 08:36:22,http://labadie.info/gustave.boyer,1.0000000000,111.247.110.127,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n5394,8,513,2017-05-08 20:26:16,http://fisher.info/marilou_dicki,3.0000000000,212.56.197.211,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n5395,8,513,2017-03-16 13:13:12,http://gerhold.info/efrain,3.0000000000,212.56.197.211,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n5396,8,514,2017-04-16 08:17:40,http://goyette.co/mackenzie,2.0000000000,42.103.103.187,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n5397,8,514,2016-12-31 14:54:07,http://schneidercarroll.info/idell,0.0000000000,51.218.89.51,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n5398,8,514,2016-12-16 15:22:42,http://weber.co/rosalee.wiza,1.0000000000,195.246.116.251,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n5399,8,514,2017-06-01 10:24:46,http://lockmanoreilly.info/leta,2.0000000000,38.215.49.114,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n5400,8,514,2017-04-20 08:19:36,http://schuster.co/abner,3.0000000000,195.246.116.251,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n5401,8,514,2016-12-26 14:36:03,http://hermistonkris.name/adan,3.0000000000,41.9.37.197,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n5402,8,514,2017-03-14 01:40:31,http://bosco.name/andreane,0.0000000000,252.240.229.100,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n5403,8,514,2017-06-13 01:45:46,http://gorczany.io/lupe,1.0000000000,82.70.24.237,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n5404,8,514,2017-02-07 06:09:41,http://cristpadberg.org/enola,0.0000000000,37.64.79.10,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n5405,8,514,2017-05-01 06:13:56,http://hammes.info/leone,3.0000000000,19.158.134.237,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n5406,8,514,2017-03-10 05:45:18,http://beer.org/garrick,0.0000000000,239.170.174.78,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n5407,8,514,2017-04-29 03:07:30,http://hahn.co/maudie,3.0000000000,42.103.103.187,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n5408,8,514,2017-03-27 21:16:01,http://borer.com/rita,3.0000000000,252.240.229.100,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n5409,8,514,2017-02-17 19:05:58,http://ondricka.org/stanford,1.0000000000,139.75.82.166,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n5410,8,515,2017-05-13 20:53:45,http://leffler.com/lexi_mccullough,0.0000000000,25.200.119.190,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n5411,8,515,2017-03-11 04:02:52,http://pfefferbogan.com/syble,0.0000000000,237.39.47.158,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n5412,8,515,2017-02-01 06:26:18,http://maggio.com/christophe.ohara,0.0000000000,56.209.24.155,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n5413,8,515,2017-03-28 02:57:17,http://halvorson.net/calista_friesen,2.0000000000,153.230.167.162,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n167,1,17,2017-05-22 00:50:04,http://johnson.info/kareem.bernier,,129.192.50.219,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n168,1,17,2017-06-10 00:34:32,http://effertz.info/edythe,,16.187.217.144,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n169,1,17,2017-02-14 06:25:38,http://ryan.net/vida_brown,,129.192.50.219,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n170,1,17,2016-12-18 09:38:18,http://torphy.org/rahsaan,,192.92.225.49,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n171,1,17,2017-01-23 16:35:35,http://pagac.com/crystel.feeney,,177.101.164.145,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n172,1,17,2016-12-17 11:01:18,http://prosacco.biz/kayli,,238.245.225.92,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n173,1,17,2017-02-02 20:18:48,http://kilback.biz/tommie.ledner,,238.245.225.92,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n174,1,17,2017-04-15 19:07:54,http://rath.co/omari,,224.105.29.6,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n175,1,17,2017-05-07 13:39:46,http://ritchie.info/ronaldo,,177.101.164.145,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n176,1,18,2017-02-18 23:57:16,http://franecki.biz/trea_stark,,159.180.41.98,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n177,1,18,2017-04-18 06:35:03,http://weber.org/kathryne,,97.148.9.70,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n178,1,18,2017-05-09 08:14:20,http://kertzmann.biz/zaria,,208.81.138.12,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n179,1,18,2017-03-12 05:21:14,http://ortiz.net/aric.ruel,,24.185.14.98,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n180,1,18,2017-01-20 22:58:43,http://nicolas.info/darien.zulauf,,97.148.9.70,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n181,1,18,2017-04-03 09:01:20,http://cronin.biz/antonia,,188.216.5.102,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n182,1,18,2017-06-05 03:42:13,http://batz.info/harmony_lockman,,43.207.221.234,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n183,1,18,2017-02-08 13:50:31,http://pfannerstill.name/cindy_pouros,,246.205.23.181,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n184,1,18,2017-03-26 02:27:35,http://conn.biz/luna,,28.205.158.179,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n185,1,18,2016-12-23 06:35:31,http://lynchrolfson.info/sasha_bahringer,,228.45.251.38,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n186,1,18,2017-02-09 02:51:07,http://fritschreichel.co/irma.veum,,126.215.74.135,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n187,1,18,2017-04-18 20:31:19,http://donnelly.org/keith.muller,,159.186.19.58,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n188,1,18,2017-03-29 22:51:25,http://muller.io/leila,,136.68.23.53,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n189,1,18,2017-01-16 14:01:45,http://greenkeeling.com/merlin,,213.130.58.10,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n190,1,18,2017-05-07 06:15:31,http://dare.org/hildegard,,228.45.251.38,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n191,1,18,2017-06-11 03:41:18,http://nader.name/mollie,,210.159.76.243,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n192,1,18,2017-01-12 12:27:14,http://strosin.org/citlalli_tromp,,129.231.100.200,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n193,1,19,2016-12-25 19:08:18,http://nikolaus.co/horace,,226.193.71.137,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n194,1,19,2017-05-28 11:36:14,http://ullrich.net/lura,,103.78.135.172,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n195,1,20,2017-01-12 01:51:29,http://runtemuller.co/wellington_robel,,205.55.12.116,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n196,1,21,2017-02-01 20:10:13,http://corkerymurazik.biz/dena.corwin,1.0000000000,225.168.159.171,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n197,1,21,2017-03-06 09:22:07,http://lemke.com/angus.pouros,1.0000000000,237.34.84.211,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n198,1,22,2017-04-30 05:01:22,http://nikolaus.info/jayson,0.0000000000,95.170.190.50,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n199,1,22,2017-01-03 12:21:45,http://cartwright.net/malvina_walker,0.0000000000,213.61.68.163,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n200,1,22,2017-03-13 02:11:35,http://dubuque.com/kameron_glover,0.0000000000,59.193.153.48,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n201,1,22,2017-01-08 02:59:50,http://quitzon.net/lisandro,0.0000000000,16.193.193.97,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n202,1,22,2017-01-22 18:35:25,http://hodkiewicz.info/robyn,0.0000000000,95.75.144.22,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n203,1,22,2016-12-28 22:19:58,http://greenholt.name/terrance,0.0000000000,233.248.190.222,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n204,1,23,2017-01-08 06:52:18,http://klocko.info/aubrey,0.0000000000,144.78.75.75,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n205,1,23,2017-06-01 01:42:38,http://towne.info/alia.kling,1.0000000000,209.9.110.159,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n206,1,23,2017-01-19 08:37:42,http://carrolldooley.io/jeanne,0.0000000000,144.78.75.75,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n207,1,23,2017-04-27 12:45:42,http://doyleziemann.io/jasper,0.0000000000,84.132.209.189,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n208,1,23,2017-01-05 09:43:23,http://botsford.org/cathryn,1.0000000000,218.62.30.246,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n209,1,23,2016-12-27 22:46:36,http://hilpert.info/rudy.herzog,0.0000000000,39.44.143.75,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n210,1,23,2017-05-13 17:15:54,http://nikolausmurphy.net/letha,0.0000000000,177.225.19.77,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n211,1,23,2017-01-20 14:23:08,http://schumm.biz/myrl,1.0000000000,160.122.117.59,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n212,1,23,2017-01-04 01:56:05,http://fritsch.io/aniyah,0.0000000000,39.44.143.75,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n213,1,23,2017-01-08 20:30:34,http://leschgibson.org/samara,1.0000000000,253.121.59.225,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n214,1,24,2017-02-07 03:12:46,http://wyman.io/velma,0.0000000000,106.177.249.40,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n215,1,24,2017-05-20 02:35:42,http://bartell.co/tania.pacocha,1.0000000000,202.157.4.188,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n216,1,24,2017-02-07 04:42:36,http://vandervortdaniel.biz/jordy_bechtelar,1.0000000000,170.8.171.20,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n217,1,24,2017-05-14 19:55:26,http://schuppe.org/reymundo_bechtelar,1.0000000000,23.40.252.204,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n218,1,24,2017-06-08 04:49:59,http://goldner.name/yvette_lynch,1.0000000000,152.36.134.85,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n219,1,24,2017-04-12 05:06:35,http://nader.co/herminia_runolfsdottir,0.0000000000,240.101.40.157,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n220,1,24,2017-04-28 13:55:33,http://cronalehner.net/jerrell.cummerata,1.0000000000,170.8.171.20,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n5414,8,515,2017-02-22 06:20:31,http://abbott.co/olga,1.0000000000,134.149.41.34,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n5415,8,515,2017-01-23 03:16:47,http://crooks.net/gay.bode,2.0000000000,243.155.231.110,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n5416,8,515,2017-02-08 13:55:10,http://becker.co/darby,2.0000000000,168.109.190.124,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n5417,8,515,2017-06-07 21:40:51,http://bartolettimonahan.net/lydia,2.0000000000,114.195.49.188,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n5418,8,515,2017-04-04 04:53:26,http://crona.biz/keanu.stiedemann,1.0000000000,181.125.243.61,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n5419,8,515,2017-06-02 19:33:55,http://goodwin.com/marina,2.0000000000,119.232.138.87,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n5420,8,515,2017-01-22 00:03:09,http://armstrong.name/kaley.torphy,2.0000000000,105.42.172.116,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n5421,8,515,2017-05-14 21:07:24,http://jenkins.io/maxwell,1.0000000000,216.158.121.162,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n5422,8,515,2017-01-10 20:02:30,http://reichel.name/beulah_stokes,0.0000000000,219.249.114.77,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n5423,8,515,2016-12-31 06:32:22,http://mullerzieme.org/warren.schinner,2.0000000000,25.200.119.190,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n5424,8,515,2017-03-27 13:15:57,http://goldnercummerata.io/osbaldo.schaden,1.0000000000,233.64.81.102,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n5425,8,515,2017-01-27 19:45:26,http://wiegandmarvin.co/jackson,2.0000000000,83.76.99.203,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n5426,8,515,2017-04-13 17:39:03,http://kub.io/ansley.mclaughlin,2.0000000000,124.204.209.57,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n5427,8,516,2017-06-12 06:27:13,http://walter.io/chyna.stanton,1.0000000000,33.246.121.161,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n5428,8,516,2017-03-04 17:26:51,http://lehnermertz.name/caterina_schmidt,0.0000000000,200.96.180.237,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n5429,8,516,2017-02-13 02:12:07,http://carterfadel.name/reie_murray,1.0000000000,201.174.228.176,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n5430,8,516,2017-05-27 20:17:20,http://dare.org/benny.dicki,1.0000000000,203.28.140.229,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n5431,8,516,2016-12-20 17:31:40,http://handchristiansen.info/hildegard.rogahn,0.0000000000,86.180.4.123,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n5432,8,517,2017-02-14 15:31:11,http://sawayn.org/aidan,0.0000000000,53.240.156.251,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n5433,8,517,2016-12-18 10:58:32,http://jaskolski.name/dameon,0.0000000000,76.129.41.252,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n5434,8,517,2017-04-27 19:52:42,http://nader.net/noah,0.0000000000,57.152.64.49,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n5435,8,517,2017-05-25 19:30:48,http://baumbachbednar.biz/lesly,0.0000000000,218.177.246.177,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n5436,8,517,2017-06-06 06:04:54,http://davis.info/llewellyn,0.0000000000,39.114.39.139,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n5437,8,517,2017-01-30 06:08:48,http://ziemannschoen.io/bud_hammes,0.0000000000,156.216.219.110,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n5438,8,517,2017-04-30 10:01:43,http://danielbatz.com/wellington,0.0000000000,202.128.107.213,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n5439,8,517,2017-03-04 16:04:43,http://conroy.io/wyman,0.0000000000,127.232.122.71,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n5440,8,517,2017-02-25 05:03:16,http://barton.info/clay.kreiger,0.0000000000,163.72.158.20,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n5441,8,517,2017-03-15 09:04:24,http://hills.io/darrin,0.0000000000,73.163.60.198,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n5442,8,517,2016-12-26 11:10:59,http://ohara.name/fae,0.0000000000,52.239.105.222,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n5443,8,517,2017-05-29 02:07:53,http://hilpert.io/skylar,0.0000000000,205.8.163.70,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n5444,8,517,2017-02-02 08:32:44,http://bergstrom.name/jazmyn.mueller,0.0000000000,27.190.37.96,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n5445,8,517,2017-01-19 09:18:29,http://bartoletti.io/ladarius_lemke,0.0000000000,205.8.163.70,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n5446,8,518,2017-01-18 13:29:10,http://bartoletti.co/marjolaine,0.0000000000,180.14.175.133,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n5447,8,518,2017-03-01 01:17:39,http://bernhard.org/kelly,0.0000000000,16.213.51.116,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n5448,8,518,2017-05-06 18:17:42,http://wildermanhomenick.info/william.kautzer,0.0000000000,180.14.175.133,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n5449,8,518,2017-03-05 23:50:09,http://howesawayn.info/jacky,0.0000000000,183.124.96.242,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n5450,8,518,2017-01-23 10:16:54,http://raynorsimonis.co/daniella_botsford,0.0000000000,84.39.80.227,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n5451,8,518,2016-12-26 20:59:37,http://pfeffer.biz/joel_nitzsche,0.0000000000,189.45.213.142,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n5452,8,518,2017-04-10 01:30:15,http://ratke.info/maud_macejkovic,0.0000000000,56.251.219.82,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n5453,8,518,2017-05-11 12:22:34,http://schmittpollich.com/dortha,0.0000000000,207.16.222.156,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n5454,8,518,2017-03-05 00:34:00,http://hayes.biz/darrell,0.0000000000,189.66.110.233,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n5455,8,518,2017-04-18 11:50:23,http://bradtke.org/millie,0.0000000000,74.127.145.24,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n5456,8,518,2017-03-26 20:38:35,http://cartwright.net/willis,0.0000000000,189.45.213.142,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n5457,8,518,2016-12-20 17:55:47,http://kuvalis.info/lela_ernser,0.0000000000,61.28.148.247,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n5458,8,519,2017-02-17 23:40:02,http://ziemann.info/howard.lubowitz,0.0000000000,167.222.246.35,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n5459,8,519,2017-04-03 11:48:50,http://bergnaum.io/roie_collins,1.0000000000,139.6.4.59,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n5460,8,519,2017-02-21 16:01:37,http://dibbertnader.net/daisy.zieme,1.0000000000,127.197.105.202,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n5461,8,519,2017-03-03 21:21:18,http://hammes.name/nichole_mante,2.0000000000,127.197.105.202,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n5462,8,519,2017-03-06 10:22:32,http://schaeferwelch.io/stanley.konopelski,3.0000000000,122.77.78.119,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n5463,8,519,2016-12-19 19:17:14,http://fisher.io/cora,1.0000000000,229.146.160.40,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n5464,8,519,2017-04-13 10:42:22,http://schaden.org/godfrey,0.0000000000,238.123.47.53,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n221,1,24,2017-02-01 15:36:56,http://murrayfay.name/beryl,1.0000000000,121.83.83.231,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n222,1,24,2017-01-26 14:07:58,http://cruickshank.org/june,1.0000000000,57.118.31.98,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n223,1,24,2017-04-07 14:59:55,http://hand.info/bella_padberg,1.0000000000,24.214.117.177,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n224,1,24,2017-01-28 22:59:55,http://trantowlubowitz.co/fermin_howe,0.0000000000,183.96.146.199,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n225,1,24,2017-05-10 12:09:19,http://zemlakbeahan.biz/moshe,0.0000000000,102.240.56.15,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n226,1,24,2017-03-01 14:19:28,http://oconner.biz/carmelo,1.0000000000,241.40.6.49,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n227,1,24,2016-12-16 14:26:51,http://adams.info/waino.rosenbaum,1.0000000000,24.214.117.177,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n228,1,24,2017-04-26 22:06:24,http://bartell.biz/teagan,0.0000000000,37.110.174.148,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n229,1,24,2017-01-21 10:51:16,http://cain.org/jennyfer,0.0000000000,226.80.88.161,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n230,1,24,2017-02-08 05:20:02,http://hermiston.name/elinore.kaulke,0.0000000000,49.72.126.82,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n231,1,24,2016-12-29 07:22:42,http://beattymedhurst.biz/lucienne.gusikowski,0.0000000000,131.48.157.169,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n232,1,25,2017-01-24 06:54:47,http://hoppe.io/thea_lang,0.0000000000,189.173.223.128,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n233,1,25,2017-05-16 07:29:49,http://blockaltenwerth.io/mozell.leannon,0.0000000000,61.35.36.172,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n234,1,25,2017-05-17 01:36:45,http://schimmelschaefer.co/georgianna,0.0000000000,215.70.175.52,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n235,1,25,2017-02-01 06:23:33,http://sipes.name/terrance_mckenzie,0.0000000000,215.82.122.123,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n236,1,25,2017-05-19 21:09:06,http://goyetteturner.org/david.robel,0.0000000000,69.32.156.26,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n237,1,25,2017-01-17 09:46:54,http://anderson.net/johnathan.oconnell,0.0000000000,224.27.231.43,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n238,1,25,2016-12-20 06:58:05,http://shanahan.io/jeica_wisoky,0.0000000000,17.30.86.76,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n239,1,25,2017-02-28 15:04:31,http://mrazleannon.name/alison.goodwin,0.0000000000,166.11.125.91,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n240,1,25,2017-04-18 00:07:58,http://marquardtweimann.org/jovany.mayer,0.0000000000,197.12.249.27,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n241,1,25,2017-02-25 17:49:03,http://gislasoncollier.name/abbie.turner,0.0000000000,16.16.253.108,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n242,1,25,2017-01-16 21:37:18,http://heathcote.net/werner_deckow,0.0000000000,106.43.236.8,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n243,1,25,2017-02-22 12:29:15,http://mayer.com/carole.hickle,0.0000000000,68.69.166.152,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n244,1,25,2016-12-19 03:16:36,http://morietteharris.name/mafalda,0.0000000000,17.30.86.76,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n245,1,25,2017-03-27 23:15:28,http://jacobihermann.biz/dariana,0.0000000000,139.46.201.117,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n246,1,25,2017-01-20 19:50:27,http://boehm.biz/kade,0.0000000000,61.68.220.177,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n247,1,25,2017-02-20 03:49:46,http://kris.com/amos_wisozk,0.0000000000,195.232.106.119,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n248,1,25,2017-01-01 19:46:14,http://johnstonbauch.biz/adriana,0.0000000000,8.91.29.216,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n249,1,25,2016-12-18 13:43:32,http://howell.info/gudrun_mertz,0.0000000000,52.221.47.70,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n250,1,25,2017-06-10 03:24:24,http://jacobscrist.com/constantin,0.0000000000,17.30.86.76,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n251,1,26,2017-01-06 17:43:06,http://ankundingmoen.io/shaylee_beahan,0.0000000000,94.211.243.83,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n252,1,26,2017-02-24 07:36:31,http://konopelski.net/valerie_shanahan,0.0000000000,90.251.103.22,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n253,1,26,2017-02-13 21:55:08,http://abbott.info/mariah_lubowitz,1.0000000000,193.128.177.24,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n254,1,26,2017-04-02 03:21:34,http://swaniawskihoeger.name/savanna.rosenbaum,0.0000000000,187.233.127.122,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n255,1,26,2017-01-07 23:58:24,http://osinskiferry.biz/linnea_mckenzie,2.0000000000,178.194.40.165,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n256,1,26,2017-02-15 04:40:38,http://feesthills.info/jamal.oreilly,2.0000000000,59.112.58.252,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n257,1,26,2017-03-17 00:04:03,http://ratke.net/jordane,1.0000000000,235.180.98.160,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n258,1,26,2017-01-10 23:09:35,http://schroeder.info/haylie.jacobi,2.0000000000,179.142.252.238,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n259,1,26,2017-05-11 03:54:16,http://gottlieb.com/leie_jacobson,2.0000000000,18.218.175.123,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n260,1,27,2016-12-15 05:37:31,http://streich.co/colby.oconner,0.0000000000,189.29.200.198,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n261,1,27,2017-05-27 19:42:14,http://zulauf.net/keith,0.0000000000,219.23.174.109,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n262,1,27,2017-04-14 00:14:12,http://jacobi.org/vincenza,0.0000000000,51.146.109.132,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n263,1,27,2017-06-08 15:03:58,http://hegmann.co/marie.ohara,0.0000000000,247.45.150.148,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n264,1,27,2016-12-22 01:00:23,http://nicolas.net/eva,0.0000000000,122.94.29.235,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n265,1,28,2017-02-24 08:43:07,http://hettingerlakin.co/frederic,0.0000000000,254.64.57.168,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n266,1,28,2017-01-17 10:39:31,http://howe.org/zander_weinat,0.0000000000,49.164.92.24,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n267,1,29,2017-01-18 22:19:22,http://wardharvey.org/lela,2.0000000000,186.28.111.145,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n268,1,29,2017-02-17 15:33:46,http://mayertgreen.info/jammie,2.0000000000,72.234.226.191,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n269,1,29,2017-05-21 19:28:24,http://kertzmannbartell.co/fleta_ratke,1.0000000000,27.99.147.44,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n270,1,29,2017-03-04 12:57:43,http://wiegand.io/theodora,0.0000000000,55.66.181.77,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n271,1,29,2017-02-13 18:19:13,http://crona.net/shyann,1.0000000000,151.122.12.247,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n272,1,29,2017-01-15 01:54:51,http://trantowprohaska.name/dannie.marvin,1.0000000000,221.115.95.86,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n5465,8,519,2017-06-05 21:12:27,http://swaniawski.biz/keegan.douglas,2.0000000000,247.151.26.47,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n5466,8,519,2017-04-04 18:51:25,http://stamm.name/gwendolyn.grady,3.0000000000,104.85.190.132,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n5467,8,519,2017-01-13 09:41:31,http://tillman.org/eliseo_ritchie,2.0000000000,199.92.225.99,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n5468,8,519,2017-03-11 05:22:12,http://swiftjohnson.org/elbert_emard,3.0000000000,247.151.26.47,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n5469,8,519,2017-02-14 00:44:13,http://tromp.co/melisa,0.0000000000,247.151.26.47,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n5470,8,519,2017-04-22 21:08:37,http://trantow.com/osbaldo,2.0000000000,212.119.22.175,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n5471,8,519,2017-06-03 02:58:41,http://leuschke.name/shayna_keler,0.0000000000,212.119.22.175,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n5472,8,520,2016-12-14 17:14:52,http://durgan.org/rex_swaniawski,0.0000000000,160.55.113.28,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n5473,8,520,2017-04-24 06:33:27,http://emmerichmacejkovic.com/edward.abernathy,1.0000000000,178.99.85.175,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n5474,8,520,2017-04-04 21:59:43,http://johns.info/blanca,1.0000000000,21.64.110.127,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n5475,8,520,2017-02-04 17:28:34,http://lakinkulas.net/brigitte.gleichner,1.0000000000,252.170.102.79,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n5476,8,520,2017-04-14 19:24:22,http://parisian.com/nash_hagenes,1.0000000000,4.170.250.119,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n5477,8,520,2017-01-10 21:09:41,http://stark.biz/roslyn.bergnaum,0.0000000000,140.19.214.145,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n5478,8,520,2017-05-14 12:14:33,http://larkin.co/mittie,0.0000000000,252.170.102.79,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n5479,8,520,2017-03-22 17:05:18,http://bartell.biz/nat,1.0000000000,172.237.162.43,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n5480,8,521,2017-06-07 18:16:53,http://mclaughlinfay.info/maybelle.hoppe,0.0000000000,177.114.95.198,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n5481,8,521,2017-02-10 18:47:53,http://lubowitz.net/retta,0.0000000000,218.240.22.159,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n5482,8,521,2017-03-24 05:28:06,http://goyette.net/juwan.mertz,0.0000000000,26.46.200.18,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n5483,8,521,2017-06-08 14:22:47,http://mertz.org/wilmer_connelly,0.0000000000,20.73.172.106,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n5484,8,521,2017-03-06 01:58:20,http://hayes.org/eva,0.0000000000,173.190.12.187,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n5485,8,521,2016-12-22 04:33:40,http://durgan.com/florencio,0.0000000000,113.252.139.69,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n5486,8,521,2017-04-25 20:28:49,http://heller.biz/jaime,0.0000000000,41.49.176.156,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n5487,8,521,2016-12-26 02:45:25,http://robel.biz/maddison,0.0000000000,150.112.216.138,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n5488,8,521,2017-03-10 11:40:06,http://emmerich.org/blanche_smitham,0.0000000000,245.132.147.222,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n5489,8,521,2017-01-16 01:56:35,http://streich.org/rosina.gibson,0.0000000000,67.36.34.241,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n5490,8,521,2017-06-02 09:53:27,http://waelchi.net/torrey,0.0000000000,66.240.3.109,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n5491,8,521,2017-03-22 05:49:47,http://lesch.com/jolie,0.0000000000,66.240.3.109,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n5492,8,521,2017-05-16 08:04:58,http://hudson.biz/joanne.olson,0.0000000000,94.35.220.128,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n5493,8,521,2017-03-10 11:38:49,http://hartmann.biz/chesley.abernathy,0.0000000000,173.42.196.106,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n5494,8,522,2017-01-22 18:41:57,http://olson.info/terence,0.0000000000,60.71.131.224,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n5495,8,522,2017-01-23 04:51:50,http://haley.biz/herbert.bartoletti,0.0000000000,208.31.160.41,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n5496,8,522,2017-05-11 08:32:26,http://schowalterschmidt.biz/dino,0.0000000000,60.71.131.224,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n5497,8,522,2017-04-06 16:10:19,http://handherzog.org/elouise,0.0000000000,43.169.198.35,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n5498,8,522,2017-05-25 16:24:12,http://vandervort.co/elisha,0.0000000000,11.29.123.149,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n5499,8,522,2017-03-19 00:00:13,http://oconnerlehner.info/pasquale,0.0000000000,134.18.32.116,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n5500,8,522,2017-06-06 22:07:41,http://ankunding.info/leopold,0.0000000000,123.64.75.133,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n5501,8,522,2017-05-14 09:44:50,http://rempel.org/gustave_langworth,0.0000000000,168.172.24.124,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n5502,8,522,2017-01-24 21:38:43,http://tillman.io/angel,0.0000000000,27.113.167.114,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n5503,8,522,2017-02-04 01:08:58,http://konopelskikeler.com/marlin,0.0000000000,168.172.24.124,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n5504,8,522,2017-02-01 03:29:42,http://fay.name/enrico.corwin,0.0000000000,165.43.140.213,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n5505,8,522,2017-03-03 12:56:15,http://prohaskareilly.biz/winifred,0.0000000000,71.172.75.240,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n5506,8,523,2017-06-11 02:57:29,http://lowe.io/raven_king,1.0000000000,216.46.254.175,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n5507,8,523,2016-12-28 07:50:37,http://johnson.net/deontae_breitenberg,1.0000000000,243.136.88.60,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n5508,8,523,2017-04-10 23:58:09,http://crist.name/rory.lemke,2.0000000000,160.128.53.211,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n5509,8,523,2016-12-23 08:15:23,http://goldnerfeil.io/marcia.welch,0.0000000000,252.22.80.89,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n5510,8,523,2017-02-15 08:00:56,http://hammes.io/jay,2.0000000000,28.98.68.157,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n5511,8,523,2017-05-30 15:07:51,http://baumbachhammes.co/kaya,1.0000000000,198.190.238.131,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n5512,8,523,2017-02-23 15:22:07,http://gulgowski.io/brando_dach,2.0000000000,200.192.220.153,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n5513,8,523,2017-02-17 04:58:02,http://wiegand.co/kevon.ankunding,0.0000000000,16.148.120.113,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n5514,8,523,2016-12-18 05:54:51,http://mohrhintz.net/lon,2.0000000000,70.182.196.176,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n5515,8,523,2017-01-18 08:23:03,http://reingerhuel.name/wilson_crist,0.0000000000,56.44.228.44,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n5516,8,523,2017-02-13 08:07:55,http://starkkautzer.name/jett_runolfon,2.0000000000,16.134.14.192,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n273,1,29,2017-02-02 05:54:08,http://gerhold.biz/norris_stamm,0.0000000000,148.102.86.198,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n274,1,29,2017-04-16 18:39:20,http://ernser.biz/fanny,2.0000000000,18.188.161.28,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n275,1,29,2017-03-24 02:30:32,http://rogahn.io/bethel,3.0000000000,245.38.130.246,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n276,1,29,2017-03-25 03:00:58,http://gaylordsteuber.biz/maia,3.0000000000,60.224.111.123,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n277,1,29,2017-05-17 05:17:49,http://lehner.com/alberta,0.0000000000,18.188.161.28,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n278,1,29,2017-04-01 03:36:50,http://nader.co/rubye,0.0000000000,151.13.53.59,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n279,1,29,2017-03-02 05:26:26,http://bartell.org/katarina,3.0000000000,224.245.95.149,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n280,1,29,2017-03-28 19:40:48,http://mitchell.info/dorthy,1.0000000000,55.66.181.77,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n281,1,30,2017-03-02 11:58:28,http://cartwrighthand.co/novella.rohan,0.0000000000,111.36.12.238,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n282,1,30,2017-05-13 17:44:46,http://waelchimacgyver.name/jerrold.boehm,0.0000000000,125.246.138.12,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n283,1,30,2017-01-15 07:05:18,http://turnerhintz.co/aurore_reinger,0.0000000000,168.194.154.31,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n284,1,30,2017-06-13 15:01:21,http://beckerdavis.io/israel,0.0000000000,25.147.67.91,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n285,1,30,2017-02-11 19:28:47,http://hauck.biz/maureen_durgan,0.0000000000,50.43.62.62,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n286,1,30,2016-12-29 01:32:25,http://gorczany.net/xavier_schiller,0.0000000000,215.91.116.164,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n287,1,30,2017-05-08 02:40:50,http://pollich.net/roosevelt.keeling,0.0000000000,230.236.77.86,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n288,1,30,2017-05-01 08:08:54,http://leffler.co/tatum.nikolaus,0.0000000000,68.17.173.171,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n289,1,30,2017-05-16 13:14:09,http://rolfsonbruen.io/obie_simonis,0.0000000000,155.236.15.168,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n290,1,31,2017-04-18 04:01:14,http://bins.org/mae,1.0000000000,136.125.159.147,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n291,1,31,2017-02-02 00:36:04,http://heidenreichborer.biz/brady.kiehn,0.0000000000,33.26.244.205,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n292,1,31,2017-03-24 16:22:52,http://schulist.net/jedediah_wehner,1.0000000000,211.96.214.86,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n293,1,31,2017-05-18 11:54:36,http://denesik.co/micaela,1.0000000000,169.121.11.85,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n294,1,31,2017-05-16 21:40:39,http://mrazmills.org/davonte.von,1.0000000000,173.183.191.184,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n295,1,31,2017-04-21 21:37:55,http://rogahn.io/amara_sauer,1.0000000000,227.86.15.230,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n296,1,32,2016-12-27 12:54:03,http://boehmmedhurst.org/dangelo,0.0000000000,118.174.96.212,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n297,1,32,2017-05-15 02:22:56,http://harber.net/raleigh,0.0000000000,141.227.254.7,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n298,1,32,2017-04-16 15:03:50,http://crooks.io/francisco_hayes,1.0000000000,113.58.165.151,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n299,1,32,2017-06-03 12:14:42,http://hintzcorwin.info/lela_grimes,0.0000000000,229.250.195.180,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n300,1,32,2017-04-27 02:34:29,http://leuschkevon.name/ivory,1.0000000000,147.86.154.248,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n301,1,32,2017-01-11 11:12:44,http://wintheiser.name/norma.ernser,1.0000000000,229.250.195.180,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n302,1,32,2017-01-25 15:33:33,http://kling.info/deshaun.mills,0.0000000000,134.236.50.140,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n303,1,32,2017-06-01 12:50:06,http://ritchieskiles.info/marjorie_kautzer,0.0000000000,144.152.69.220,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n304,1,32,2017-04-04 23:08:48,http://bogisich.biz/kamille,1.0000000000,248.14.251.188,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n305,1,32,2017-05-23 22:37:13,http://rohan.name/oral.abernathy,1.0000000000,134.236.50.140,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n306,1,32,2017-01-14 09:45:38,http://larson.info/hildegard,0.0000000000,11.200.134.64,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n307,1,32,2017-03-10 06:15:54,http://kunze.org/shanna,0.0000000000,199.80.210.50,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n308,1,32,2016-12-30 01:03:10,http://oberbrunnerkeeling.org/deion,1.0000000000,181.23.74.127,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n309,1,32,2017-02-21 22:44:51,http://collierpadberg.biz/ed_kuvalis,1.0000000000,147.86.154.248,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n310,1,32,2017-05-05 22:18:50,http://reilly.org/timothy.wolff,1.0000000000,228.195.75.204,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n311,1,32,2017-05-06 08:54:58,http://herzog.co/donavon,1.0000000000,114.49.140.83,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n312,1,33,2017-03-06 09:38:27,http://lakinboyer.info/eloise,0.0000000000,216.230.179.184,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n313,1,33,2017-02-10 07:55:48,http://gutkowski.info/marc_langworth,0.0000000000,163.60.88.62,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n314,1,33,2017-03-20 13:52:27,http://runolfon.org/beatrice.streich,0.0000000000,95.17.231.156,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n315,1,33,2017-02-10 02:37:03,http://weimann.org/eudora,0.0000000000,93.41.207.99,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n316,1,33,2017-03-31 06:38:41,http://hodkiewiczrunolfon.info/blanche,0.0000000000,183.92.82.238,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n317,1,33,2017-01-08 09:07:55,http://roberts.net/paul,0.0000000000,95.17.231.156,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n318,1,33,2017-03-04 21:49:34,http://dietrichhaley.co/lolita,0.0000000000,123.157.129.112,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n319,1,33,2017-06-03 00:58:46,http://durgan.co/stefan_reilly,0.0000000000,84.110.191.115,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n320,1,33,2017-04-12 04:43:15,http://auerziemann.co/mackenzie.hilpert,0.0000000000,38.230.23.42,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n321,1,33,2017-03-04 22:51:28,http://heaney.io/yolanda_nikolaus,0.0000000000,102.11.214.137,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n322,1,33,2017-05-03 19:13:43,http://murphystehr.org/mabel,0.0000000000,100.199.206.163,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n323,1,33,2017-04-24 05:07:03,http://colekuhn.biz/mona.dickinson,0.0000000000,214.230.233.180,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n324,1,33,2017-06-11 04:04:51,http://volkmanpadberg.org/mortimer,0.0000000000,114.40.180.171,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n5517,8,523,2017-02-23 09:40:19,http://wisozk.net/ima_turcotte,0.0000000000,116.140.95.123,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n5518,8,523,2017-05-17 20:05:05,http://okon.net/zita.willms,2.0000000000,241.159.195.79,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n5519,8,523,2017-03-15 14:42:53,http://mante.co/alba,0.0000000000,16.134.14.192,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n5520,8,523,2016-12-25 15:58:52,http://fahey.io/turner,2.0000000000,31.61.75.201,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n5521,8,523,2017-01-11 06:43:04,http://volkman.biz/aidan.howe,2.0000000000,164.12.207.122,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n5522,8,523,2017-02-13 11:19:47,http://medhurststamm.name/demetris,1.0000000000,66.56.237.93,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n5523,8,523,2017-04-01 12:13:33,http://metz.biz/emmy,1.0000000000,129.3.207.134,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n5524,8,523,2017-03-29 02:05:24,http://rowegoyette.co/kiley,1.0000000000,30.168.245.207,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n5525,8,524,2017-01-23 19:41:21,http://prosacco.co/gerhard.jacobs,1.0000000000,204.54.186.123,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n5526,8,524,2016-12-19 13:50:22,http://moen.info/retta,2.0000000000,190.216.84.246,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n5527,8,524,2017-06-10 00:10:52,http://kshlerin.biz/diamond,1.0000000000,87.90.185.8,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n5528,8,524,2017-02-18 14:56:00,http://bergstromabshire.com/jaleel_padberg,1.0000000000,187.26.16.84,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n5529,8,524,2016-12-30 07:14:14,http://auer.biz/eugene_turcotte,1.0000000000,87.250.146.56,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n5530,8,524,2017-02-19 11:50:50,http://johns.io/vergie_aufderhar,0.0000000000,81.74.143.164,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n5531,8,524,2017-01-14 04:50:26,http://leannon.io/tyrique_blanda,0.0000000000,81.74.143.164,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n5532,8,524,2017-05-09 16:12:27,http://okuneva.org/eleanora_bradtke,0.0000000000,11.132.48.249,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n5533,8,524,2017-03-19 08:21:35,http://raynor.name/syble,1.0000000000,159.157.253.62,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n5534,8,524,2017-02-15 19:23:22,http://kautzer.org/allison,0.0000000000,26.199.118.88,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n5535,8,524,2017-05-31 00:03:58,http://tillmanjacobi.io/dorian_stehr,1.0000000000,187.26.16.84,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n5536,8,524,2017-05-29 17:28:12,http://stamm.net/delores,1.0000000000,153.8.120.28,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n5537,8,524,2017-01-19 00:59:48,http://bernier.net/eladio,2.0000000000,122.149.77.244,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n5538,8,524,2017-03-17 15:40:12,http://nienowrunolfon.co/esta_stehr,2.0000000000,70.131.79.131,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n5539,8,524,2017-04-07 12:30:49,http://heathcote.net/major,0.0000000000,190.217.76.170,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n5540,8,525,2017-01-16 03:23:32,http://altenwerthmarks.name/rosalinda,2.0000000000,173.151.26.146,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n5541,8,525,2017-03-10 09:27:14,http://klein.name/joanie_keeling,1.0000000000,178.83.232.95,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n325,1,33,2017-04-26 23:29:33,http://boscorau.co/alta.ebert,0.0000000000,123.157.129.112,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n326,1,33,2017-03-12 15:04:38,http://wisozkrolfson.io/wilfred,0.0000000000,201.19.214.183,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n327,1,33,2017-02-24 11:25:12,http://blandawuckert.co/lilla,0.0000000000,149.51.104.181,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n328,1,33,2017-03-18 07:48:17,http://predovic.info/shanie,0.0000000000,57.4.56.71,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n329,1,33,2017-04-08 17:57:18,http://herzog.io/troy_okuneva,0.0000000000,7.68.103.8,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n330,1,33,2017-04-15 19:50:06,http://mertzhaag.org/germaine,0.0000000000,63.56.20.77,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n331,1,34,2017-05-27 02:20:40,http://litteltoy.co/eloise,2.0000000000,238.232.229.17,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n332,1,34,2017-02-20 17:06:39,http://hegmann.io/eldora_stroman,3.0000000000,51.246.130.100,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n333,1,34,2017-06-09 19:45:44,http://pollich.co/mellie_kutch,1.0000000000,210.125.100.109,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n334,1,34,2017-04-12 14:12:07,http://runolfon.info/richie,1.0000000000,50.222.59.171,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n335,1,34,2017-04-07 08:48:06,http://schulist.co/julio.kohler,0.0000000000,10.248.130.230,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n336,1,34,2016-12-17 05:37:24,http://ko.com/sabryna,2.0000000000,91.10.113.168,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n337,1,34,2016-12-25 18:54:57,http://sipes.biz/destiny_feeney,0.0000000000,95.152.139.55,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n338,1,34,2017-06-09 08:30:41,http://bednargraham.biz/jakob,1.0000000000,56.194.191.194,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n339,1,34,2016-12-22 07:43:51,http://krishuels.name/oral,0.0000000000,103.169.127.143,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n340,1,34,2017-02-28 13:33:44,http://heaney.biz/rhiannon,3.0000000000,137.107.37.200,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n341,1,34,2017-03-27 18:04:24,http://rohanschmeler.io/kasey,2.0000000000,148.70.250.49,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n342,1,34,2017-04-07 15:44:18,http://schinnerframi.biz/laron,2.0000000000,203.184.153.242,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n343,1,34,2017-03-11 15:22:57,http://bashirianrenner.biz/vivienne.dooley,2.0000000000,137.107.37.200,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n344,1,34,2017-02-25 19:47:40,http://lubowitz.org/florian_gibson,0.0000000000,210.125.100.109,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n345,1,34,2017-05-08 16:47:49,http://corkery.name/hilbert,3.0000000000,50.222.59.171,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n346,1,34,2017-05-28 23:00:45,http://vandervort.net/alfred,1.0000000000,34.32.100.22,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n347,1,35,2016-12-16 11:46:49,http://shieldswilliamson.biz/delbert_wilderman,0.0000000000,143.210.138.177,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n348,1,35,2017-01-27 14:30:28,http://lynch.com/asha.bahringer,0.0000000000,84.78.118.133,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n349,1,35,2017-03-14 18:20:31,http://wiegand.io/amber,0.0000000000,246.77.30.185,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n350,1,35,2017-03-25 08:02:47,http://quitzon.info/elmo,0.0000000000,173.140.171.156,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n351,1,35,2017-03-16 21:29:17,http://macejkovic.net/giovani.pagac,0.0000000000,85.248.193.179,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n352,1,36,2017-04-04 04:07:35,http://okon.net/hannah,2.0000000000,85.121.215.213,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n353,1,36,2016-12-26 01:21:14,http://johnsonschowalter.info/kristofer,1.0000000000,237.223.135.138,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n354,1,36,2017-03-27 04:07:06,http://stokeskutch.org/kolby,2.0000000000,240.182.153.72,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n355,1,36,2016-12-24 07:50:31,http://klockoferry.co/theresia_hauck,2.0000000000,64.152.24.50,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n356,1,36,2017-02-10 08:22:23,http://hermanrath.co/wilton,0.0000000000,194.55.8.74,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n357,1,36,2017-06-10 12:04:13,http://murphy.com/ricardo,0.0000000000,85.121.215.213,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n358,1,36,2017-02-23 20:03:19,http://johns.info/denis_koch,1.0000000000,115.37.239.81,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n359,1,36,2016-12-24 18:24:43,http://kirlingreenfelder.biz/madaline.ritchie,1.0000000000,180.191.169.118,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n360,1,36,2017-04-23 06:40:34,http://oberbrunner.info/daniela_beer,0.0000000000,248.203.213.119,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n361,1,36,2017-06-10 20:30:29,http://gottlieb.com/lon,1.0000000000,25.251.205.155,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n362,1,36,2017-04-26 16:08:00,http://lemkecasper.name/aisha_ferry,0.0000000000,194.55.8.74,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n363,1,36,2017-03-26 21:59:37,http://oberbrunner.com/martina,2.0000000000,26.57.15.169,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n364,1,36,2017-01-29 15:24:26,http://yundt.co/lynn_koelpin,2.0000000000,158.72.146.86,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n365,1,36,2017-02-09 14:25:54,http://bayervandervort.biz/floy.wolff,2.0000000000,237.251.225.144,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n366,1,36,2016-12-27 19:58:51,http://maggio.info/myriam_doyle,2.0000000000,248.203.213.119,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n367,1,36,2017-03-29 15:48:28,http://gorczany.org/sylvan,0.0000000000,60.50.110.155,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n368,1,36,2017-02-16 02:01:05,http://pfeffer.biz/mayra,0.0000000000,234.199.239.10,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n369,1,36,2017-05-18 08:30:53,http://schummullrich.name/claude,0.0000000000,5.219.171.61,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n370,1,37,2016-12-19 12:51:40,http://fadeldamore.net/piper,0.0000000000,186.109.222.26,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n371,1,37,2017-01-06 21:56:47,http://bradtke.net/haylee,2.0000000000,233.2.156.103,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n372,1,37,2017-02-01 18:09:21,http://moriette.com/alford_watsica,3.0000000000,206.68.69.216,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n373,1,37,2017-06-12 08:45:05,http://swaniawski.io/dangelo.kiehn,3.0000000000,234.52.151.227,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n374,1,37,2017-02-07 04:12:13,http://hudson.net/jennifer,0.0000000000,167.40.30.133,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n375,1,37,2016-12-18 11:24:08,http://oreilly.org/elmira_kertzmann,2.0000000000,128.124.105.187,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n376,1,37,2016-12-25 05:48:09,http://blandarenner.biz/jolie.reichert,1.0000000000,3.159.228.202,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n377,1,38,2017-05-12 05:27:38,http://dickenshoppe.info/marisa,0.0000000000,113.214.152.158,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n378,1,39,2017-03-06 20:54:44,http://moriettegleichner.net/noelia.heaney,1.0000000000,60.150.72.124,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n379,1,39,2016-12-26 09:15:02,http://beierchristiansen.biz/trevion,1.0000000000,96.85.73.213,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n380,1,39,2017-02-24 16:39:22,http://wisozk.com/rylan,1.0000000000,228.194.188.82,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n381,1,39,2017-02-23 01:04:17,http://connelly.info/drew,1.0000000000,174.66.71.52,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n382,1,39,2017-06-06 09:47:16,http://feeneyaufderhar.name/blaise,1.0000000000,81.52.192.165,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n383,1,39,2017-04-18 23:55:23,http://wilkinson.co/hershel,1.0000000000,197.160.115.195,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n384,1,39,2017-05-19 03:57:43,http://leuschke.co/vada,0.0000000000,49.222.204.13,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n385,1,39,2017-03-26 09:10:56,http://darehilpert.info/trycia,0.0000000000,252.13.179.103,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n386,1,39,2017-02-14 07:33:20,http://jakubowski.biz/cordelia.hoppe,1.0000000000,34.127.16.132,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n387,1,39,2017-03-07 19:29:37,http://brakus.info/loyal,0.0000000000,91.50.70.140,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n388,1,39,2017-05-14 01:24:23,http://schulistboehm.name/shawn,1.0000000000,174.66.71.52,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n389,1,39,2017-02-18 16:59:30,http://rempel.info/nikita,0.0000000000,15.57.28.86,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n390,1,39,2017-01-18 09:46:09,http://batz.info/kay,0.0000000000,96.85.73.213,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n391,1,39,2017-04-25 00:45:36,http://bruenebert.biz/jacinthe_mertz,1.0000000000,252.13.179.103,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n392,1,39,2017-04-02 12:11:42,http://kelergutmann.org/cary_dooley,0.0000000000,127.110.237.58,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n393,1,39,2017-05-16 06:55:14,http://oreilly.com/beverly,0.0000000000,119.109.217.133,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n394,1,39,2017-03-11 10:20:00,http://will.info/antonetta,0.0000000000,181.52.126.101,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n395,1,39,2017-03-07 12:50:56,http://luettgen.net/rosemary,0.0000000000,253.8.79.81,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n396,1,40,2016-12-15 09:33:34,http://williamsonoconnell.io/felicity_gerlach,2.0000000000,44.76.228.249,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n397,1,40,2017-03-02 06:11:42,http://heller.io/floyd.anderson,0.0000000000,200.109.87.172,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n398,1,41,2017-04-28 04:57:31,http://cronin.info/deion_bartoletti,1.0000000000,148.178.76.76,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n399,1,41,2017-02-25 01:30:35,http://kris.net/eveline_rippin,2.0000000000,211.218.92.248,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n400,1,41,2017-04-18 23:35:28,http://gislason.name/jerel,2.0000000000,242.13.82.58,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n401,1,42,2017-02-19 09:47:02,http://herzog.biz/chance.kuhn,0.0000000000,174.199.4.142,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n402,1,42,2016-12-22 11:40:10,http://haley.info/ona_zulauf,1.0000000000,140.249.60.242,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n403,1,42,2017-05-12 05:36:59,http://tremblay.info/jolie.sporer,0.0000000000,93.63.207.120,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n404,1,42,2017-04-23 12:19:00,http://gaylord.biz/jamison.turner,1.0000000000,162.206.47.14,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n405,1,42,2017-05-29 08:09:35,http://howell.biz/aniyah,0.0000000000,139.122.54.11,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n406,1,42,2017-03-29 20:00:50,http://labadie.biz/alex.weinat,1.0000000000,237.50.15.127,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n407,1,42,2017-06-04 14:18:32,http://gibsondach.net/bell.pouros,1.0000000000,243.54.10.53,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n408,1,42,2017-01-14 20:42:45,http://pfefferpowlowski.org/allie,1.0000000000,35.137.90.72,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n409,1,42,2017-03-06 08:19:45,http://graham.com/garry_parisian,1.0000000000,19.121.56.122,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n410,1,42,2017-02-07 07:08:46,http://bartoletti.net/joel_sporer,1.0000000000,75.197.230.219,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n411,1,42,2017-04-06 07:35:35,http://eichmann.io/demond_powlowski,0.0000000000,9.133.138.50,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n412,1,42,2017-04-09 17:31:43,http://sawayn.org/mattie,0.0000000000,164.153.178.188,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n413,1,42,2017-03-18 16:25:08,http://marvinbeahan.io/torey,0.0000000000,40.245.155.197,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n414,1,42,2017-05-27 04:04:50,http://andersonyost.biz/felicia_botsford,1.0000000000,146.13.186.192,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n415,1,42,2017-01-07 10:32:27,http://kuphalswaniawski.org/jamar_nicolas,1.0000000000,202.204.68.250,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n416,1,42,2017-05-20 01:35:14,http://lakinwuckert.co/oliver,1.0000000000,126.140.153.86,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n417,1,42,2017-03-19 09:28:44,http://champlinhand.org/jermey_mraz,0.0000000000,143.240.218.162,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n418,1,42,2017-02-27 18:58:01,http://brekke.co/keyon,0.0000000000,126.140.153.86,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n419,1,42,2017-03-25 08:15:07,http://hansen.biz/antonio,1.0000000000,124.44.208.166,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n420,1,43,2017-05-12 23:57:04,http://beahanwunsch.info/carmelo_auer,0.0000000000,150.189.10.12,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n421,1,43,2017-01-07 18:20:46,http://emmerichhaley.org/velda_jerde,0.0000000000,151.189.159.101,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n422,1,43,2017-04-23 05:27:57,http://yost.biz/germaine.hagenes,0.0000000000,217.13.145.150,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n423,1,43,2017-03-03 06:27:12,http://bartoletti.com/colton.cain,0.0000000000,20.47.164.202,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n424,1,43,2017-01-22 02:22:52,http://conroy.co/caleigh_swift,0.0000000000,151.189.159.101,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n425,1,43,2017-01-25 05:36:11,http://gerlach.biz/ryder,0.0000000000,171.239.212.6,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n426,1,43,2017-03-10 20:19:27,http://bahringer.name/alize_abshire,0.0000000000,151.189.159.101,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n427,1,43,2017-04-24 23:10:11,http://mrazledner.info/curt,0.0000000000,5.121.151.172,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n428,1,43,2016-12-22 16:49:12,http://reichel.org/stone,0.0000000000,143.3.169.189,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n429,1,43,2017-02-06 14:23:49,http://considinejohnston.name/chanel_kihn,0.0000000000,231.210.250.32,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n430,1,43,2017-03-18 02:56:32,http://okon.net/shany,0.0000000000,22.12.155.110,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n431,1,43,2017-06-02 17:18:40,http://bergepredovic.name/marion,0.0000000000,5.121.151.172,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n432,1,43,2017-03-28 01:28:42,http://lowe.io/alexie.kozey,0.0000000000,252.104.87.25,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n433,1,43,2017-03-23 07:35:31,http://schusterglover.info/linwood_breitenberg,0.0000000000,217.13.145.150,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n434,1,44,2017-05-07 02:00:15,http://collins.org/darwin.dicki,0.0000000000,210.235.156.78,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n435,1,44,2017-01-25 14:35:24,http://doylestehr.io/sydni,1.0000000000,183.245.173.220,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n436,1,44,2017-01-18 20:29:19,http://heathcote.biz/miracle_nitzsche,1.0000000000,72.166.120.241,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n437,1,44,2017-03-13 21:16:50,http://gaylordwhite.co/mona,2.0000000000,7.59.32.185,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n438,1,44,2017-03-01 19:01:08,http://runolfonweinat.com/dena,2.0000000000,122.119.37.198,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n439,1,44,2017-03-17 16:52:15,http://gerhold.org/felipa_pagac,0.0000000000,129.117.3.120,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n440,1,44,2017-01-18 06:31:21,http://walker.net/braeden,1.0000000000,234.40.19.160,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n441,1,44,2017-04-22 22:22:42,http://ondricka.io/kyra,2.0000000000,21.26.53.45,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n442,1,45,2017-03-02 14:23:37,http://hayesdoyle.info/estefania.mcglynn,1.0000000000,15.89.103.157,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n443,1,45,2017-03-05 04:37:42,http://dubuque.io/crystal,0.0000000000,152.107.172.120,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n444,1,45,2017-05-13 23:17:15,http://herman.name/leopold.weimann,0.0000000000,80.96.163.145,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n445,1,45,2017-04-09 19:24:31,http://greentreutel.info/lavada,2.0000000000,72.155.240.217,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n446,1,45,2017-01-14 21:09:45,http://schaden.biz/laura_keebler,0.0000000000,39.23.44.142,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n447,1,45,2017-05-28 09:14:34,http://hoppe.org/blanche_wuckert,0.0000000000,220.205.192.6,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n448,1,45,2017-04-24 19:58:01,http://hills.io/vince.labadie,0.0000000000,111.192.110.72,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n449,1,45,2017-02-02 06:04:29,http://wiegand.name/romaine,0.0000000000,39.23.44.142,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n450,1,45,2017-05-23 00:47:02,http://schimmel.org/sabina_donnelly,1.0000000000,28.251.174.219,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n451,1,45,2017-06-05 21:23:38,http://cremin.io/jaylon.cartwright,0.0000000000,50.242.196.128,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n452,1,45,2017-06-12 13:00:32,http://beatty.co/murray_feeney,3.0000000000,73.12.79.101,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n453,1,45,2017-05-15 11:05:44,http://baileynader.co/earl,3.0000000000,134.65.51.101,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n454,1,45,2016-12-14 07:54:32,http://shanahan.info/mabelle,1.0000000000,164.241.75.15,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n455,1,45,2017-05-22 22:50:46,http://brekkedietrich.net/isac.macejkovic,1.0000000000,98.156.168.195,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n456,1,45,2017-04-06 05:28:45,http://bodeupton.net/hillard.konopelski,0.0000000000,72.155.240.217,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n457,1,46,2017-03-28 09:42:54,http://stammgrady.net/xavier.ratke,0.0000000000,22.247.15.145,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n458,1,46,2017-05-10 00:46:12,http://hagenesroob.com/arno,0.0000000000,242.36.59.99,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n459,1,46,2017-05-30 17:24:58,http://keebler.io/efren.beatty,0.0000000000,101.45.9.175,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n460,1,46,2017-06-11 12:23:46,http://kuphal.com/annamae,0.0000000000,47.79.126.156,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n461,1,46,2017-06-06 09:13:39,http://vonsauer.net/sim_crist,0.0000000000,244.105.239.47,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n462,1,46,2017-04-24 16:50:14,http://ryan.info/aleia,0.0000000000,254.209.239.47,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n463,1,46,2017-01-11 00:13:08,http://graham.name/camille.langworth,0.0000000000,245.143.218.131,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n464,1,46,2017-05-29 10:45:53,http://lockman.co/arne,0.0000000000,235.49.207.188,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n465,1,46,2017-02-21 01:32:50,http://baumbach.biz/wendell_jakubowski,0.0000000000,110.56.99.166,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n466,1,46,2017-02-02 02:17:18,http://runolfon.info/melya,0.0000000000,45.89.180.220,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n467,1,46,2017-06-11 11:06:22,http://sawayn.biz/javier.moriette,0.0000000000,245.143.218.131,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n468,1,46,2017-01-27 12:01:49,http://kohlerstiedemann.co/francis.kuphal,0.0000000000,22.131.41.39,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n469,1,46,2017-06-05 22:56:18,http://larkindamore.biz/keegan.medhurst,0.0000000000,245.143.218.131,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n470,1,46,2017-02-18 04:45:00,http://hauck.net/milan,0.0000000000,175.235.76.151,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n471,1,46,2017-04-29 17:36:11,http://goyetteokeefe.io/lyric,0.0000000000,68.187.177.175,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n472,1,46,2017-04-20 12:24:29,http://morar.name/lorna,0.0000000000,216.192.109.140,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n473,1,46,2017-01-23 06:08:36,http://cummerataratke.org/alva.schulist,0.0000000000,28.225.210.94,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n474,1,46,2017-05-25 13:03:07,http://witting.net/emile,0.0000000000,72.233.9.17,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n475,1,46,2017-05-28 15:12:46,http://jacobsondietrich.org/marianna_ohara,0.0000000000,41.140.108.64,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n476,1,46,2017-02-08 09:11:02,http://cain.io/bette,0.0000000000,242.36.59.99,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n477,1,47,2017-03-04 14:23:15,http://marvinmueller.name/daphne.hills,0.0000000000,71.45.221.159,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n478,1,48,2017-05-02 23:51:05,http://stroman.info/ahmad,1.0000000000,108.20.202.152,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n479,1,48,2017-03-11 12:10:37,http://auer.biz/estrella_olson,1.0000000000,44.148.211.113,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n480,1,48,2017-04-29 13:01:31,http://turner.co/kenna,1.0000000000,228.141.57.79,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n481,1,48,2017-05-26 21:13:29,http://macgyver.io/terrill_dickinson,0.0000000000,178.146.96.253,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n482,1,48,2017-02-05 00:41:53,http://krajcikschinner.biz/ima,0.0000000000,172.74.244.32,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n483,1,48,2017-03-07 08:34:51,http://deckow.info/yolanda_jacobson,0.0000000000,172.74.244.32,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n484,1,48,2017-04-07 09:49:16,http://schiller.co/horace.wolf,0.0000000000,189.26.134.53,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n485,1,48,2017-06-08 01:46:56,http://hermiston.net/toy,1.0000000000,148.13.88.206,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n486,1,48,2016-12-17 05:56:27,http://little.biz/stone,1.0000000000,228.141.57.79,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n487,1,48,2017-04-26 13:07:43,http://heller.io/amiya.wunsch,0.0000000000,195.142.199.210,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n488,1,48,2017-01-08 10:56:01,http://leuschke.com/tamara,0.0000000000,133.39.231.42,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n489,1,48,2017-03-21 18:54:08,http://hammes.info/rosalia,1.0000000000,2.188.128.203,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n490,1,48,2017-05-14 15:31:10,http://danieltrantow.name/raphael,1.0000000000,44.148.211.113,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n491,1,48,2017-04-01 17:33:39,http://parkerbuckridge.biz/perry.volkman,0.0000000000,133.39.231.42,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n492,1,49,2017-02-06 19:36:33,http://schneiderrunte.info/rahul,1.0000000000,196.102.251.120,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n493,1,49,2016-12-17 03:04:30,http://harris.name/billy,1.0000000000,84.27.149.171,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n494,1,49,2017-03-08 18:33:11,http://powlowskigislason.net/sandy.christiansen,1.0000000000,79.51.94.198,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n495,1,49,2017-02-04 09:21:10,http://adamsrunolfon.info/jamaal,1.0000000000,105.120.52.68,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n496,1,49,2016-12-14 08:12:14,http://lowe.co/josh.schoen,1.0000000000,184.24.238.15,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n497,1,49,2017-02-18 06:03:43,http://damore.co/ubaldo_smitham,2.0000000000,131.110.43.20,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n498,1,49,2017-04-26 20:52:19,http://dickinsonstiedemann.info/carolyn.ullrich,0.0000000000,217.173.252.118,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n499,1,49,2017-03-11 12:51:50,http://fadel.co/bernadette,0.0000000000,217.173.252.118,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n500,1,49,2017-05-06 16:03:47,http://stehrwill.net/mervin.kuhn,1.0000000000,2.82.162.233,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n501,1,49,2017-03-21 20:56:10,http://gusikowskischmitt.info/candace_schmitt,0.0000000000,103.105.163.101,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n502,1,50,2017-01-10 16:09:15,http://gaylordwilliamson.info/emily,3.0000000000,133.216.251.27,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n503,1,50,2017-02-21 01:47:11,http://feestfeil.biz/easter,3.0000000000,121.237.90.228,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n504,1,50,2017-05-26 11:28:50,http://baumbachstiedemann.info/michele,2.0000000000,159.23.141.243,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n505,1,50,2017-01-22 16:54:51,http://rauhyatt.net/ransom.wilderman,2.0000000000,203.251.254.161,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n506,1,50,2016-12-22 13:10:55,http://morarjenkins.io/dereck.brekke,1.0000000000,172.69.195.245,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n507,1,51,2017-02-07 07:39:18,http://kovacek.io/meda_gleason,3.0000000000,159.78.11.64,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n508,1,51,2017-04-22 22:40:24,http://borerschroeder.net/rosetta_vandervort,2.0000000000,244.231.121.250,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n2794,5,263,2017-01-11 11:09:43,http://marks.biz/loma,,6.155.218.162,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n2795,5,263,2017-01-03 15:42:55,http://stoltenbergschowalter.name/clovis,,206.129.103.236,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n2796,5,263,2017-03-04 22:05:17,http://bruenrobel.co/america,,191.162.136.31,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n2797,5,263,2017-02-20 03:31:04,http://schaefer.net/ardith_erdman,,6.155.218.162,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n2798,5,263,2017-03-17 07:51:13,http://hermann.info/dorcas,,72.126.67.66,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n2799,5,263,2017-01-30 17:45:00,http://swaniawskitremblay.biz/derrick.schuster,,167.149.114.171,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n2800,5,263,2017-03-31 04:13:26,http://hilpert.biz/gus_beer,,87.227.33.129,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n2801,5,264,2017-05-28 00:53:26,http://brown.net/marge.roob,,14.178.243.6,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n2802,5,264,2017-04-05 15:12:21,http://mclaughlinko.biz/marc_oberbrunner,,23.114.236.213,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n2803,5,264,2017-05-30 04:36:29,http://franecki.com/alejandrin_carroll,,40.185.6.234,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n2804,5,264,2017-06-13 05:41:43,http://spinka.co/linnie.okuneva,,70.28.161.218,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n2805,5,264,2017-02-11 04:03:09,http://mclaughlin.com/wilber,,149.76.60.11,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n2806,5,264,2017-01-12 20:53:38,http://wisokyparker.org/adriel_king,,224.142.76.83,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n2807,5,264,2017-03-07 17:22:53,http://kshlerinkutch.org/myrtle,,165.251.22.166,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n2808,5,264,2017-01-11 08:51:03,http://medhurstkuphal.biz/june,,8.212.9.27,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n2809,5,264,2017-01-05 12:28:04,http://handskiles.co/ruel.bayer,,70.86.169.17,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n2810,5,264,2017-02-22 22:38:30,http://stromanhermann.co/derrick,,77.63.173.180,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n2811,5,264,2017-04-16 10:02:41,http://klockogreenholt.name/kavon,,242.35.119.54,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n2812,5,264,2017-06-13 14:27:21,http://gutmannflatley.org/sam_fay,,60.173.24.178,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n2813,5,264,2017-05-31 17:20:49,http://lowedaniel.org/damon.schuster,,165.251.22.166,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n2814,5,264,2017-06-03 19:23:56,http://goldner.info/madalyn_collins,,165.251.22.166,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n2815,5,264,2017-05-31 14:02:37,http://gutkowskirippin.com/daniella,,60.173.24.178,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n2816,5,265,2017-04-07 18:18:25,http://mccullough.biz/santa.wunsch,,156.178.99.112,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n2817,5,265,2017-06-03 23:13:58,http://oconnerpouros.org/destiney.ward,,88.53.3.32,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n2818,5,265,2017-06-05 16:54:16,http://kshlerindaugherty.info/elinore,,33.71.216.109,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n2819,5,265,2017-05-26 11:16:21,http://emard.name/weston,,156.178.99.112,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n2820,5,265,2017-01-06 15:42:41,http://okeefe.io/magdalena,,241.195.93.72,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n2821,5,265,2017-03-29 09:55:16,http://kochdach.io/rocio,,120.109.185.19,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n2822,5,265,2017-04-01 13:27:46,http://rowe.biz/mayra,,71.218.16.77,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n2823,5,265,2017-06-03 13:14:56,http://maggiodaugherty.net/darren_leffler,,33.71.216.109,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n2824,5,265,2017-04-22 23:49:18,http://pouroschristiansen.com/aiyana,,71.218.16.77,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n2825,5,265,2017-05-17 08:47:01,http://schuppe.com/torrance.cartwright,,89.146.186.107,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n2826,5,265,2017-01-22 04:09:19,http://gradywisoky.com/alfreda,,152.48.109.120,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n2827,5,265,2017-02-25 02:57:57,http://mannwintheiser.org/erna.bernhard,,189.87.216.55,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n2828,5,265,2017-01-11 03:04:17,http://fritsch.org/milan.wilkinson,,88.53.3.32,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n2829,5,265,2017-04-23 08:03:15,http://braunkrajcik.name/gail.schmidt,,126.246.183.11,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n2830,5,265,2017-01-07 18:07:16,http://hamill.com/fanny,,168.59.186.35,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n2831,5,265,2017-01-12 17:49:21,http://rathweimann.io/dora.wiegand,,78.33.99.52,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n2832,5,265,2017-05-10 17:51:42,http://green.net/brown_bernhard,,93.83.249.37,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n2833,5,265,2017-03-07 10:32:20,http://champlin.name/hunter,,106.77.219.204,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n2834,5,266,2017-01-31 14:31:16,http://becker.co/reyes.pouros,,71.41.213.81,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n2835,5,266,2017-05-05 04:51:34,http://luettgensawayn.info/rollin,,145.27.239.118,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n2836,5,266,2017-03-19 08:55:08,http://metzruecker.co/marjory.borer,,42.173.5.212,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n2837,5,266,2017-05-22 13:47:47,http://cartwrightkiehn.co/cydney,,205.218.20.118,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n2838,5,266,2016-12-22 19:55:57,http://littlewiegand.org/carolina,,245.249.188.101,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n2839,5,266,2017-04-21 13:10:53,http://bartellmcglynn.co/virginie,,23.194.56.72,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n2840,5,266,2017-05-17 10:32:31,http://erdmanvon.io/jeffrey.franecki,,248.38.79.91,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n2841,5,266,2017-01-26 02:36:55,http://ruecker.info/deanna,,196.135.94.71,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n2842,5,266,2017-03-06 09:55:19,http://dubuquewunsch.co/saige,,82.94.196.146,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n2843,5,266,2016-12-27 22:49:31,http://botsford.info/brody,,102.168.210.52,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n2844,5,266,2017-01-03 02:13:43,http://kshlerinberge.co/arlene.gulgowski,,145.130.57.251,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n2845,5,266,2017-01-29 10:26:26,http://kuhnwalker.org/aubree.little,,82.91.128.159,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n2846,5,266,2017-03-24 19:22:09,http://macejkovic.name/bailee_runolfon,,46.147.21.116,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n2847,5,266,2016-12-19 06:01:22,http://stehr.co/myrtice_cole,,185.33.211.161,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n2848,5,266,2017-01-19 15:01:50,http://danielhegmann.com/kristina,,53.134.183.87,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n2849,5,266,2017-01-31 11:21:58,http://cormier.info/lisa.walter,,191.153.239.203,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n2850,5,266,2017-06-05 08:59:14,http://kutchswift.com/nash.batz,,71.41.213.81,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n2851,5,266,2017-04-21 08:20:17,http://nitzsche.name/adan.funk,,248.38.79.91,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n2852,5,266,2017-01-17 11:19:02,http://cormier.name/lisa.schimmel,,196.120.226.78,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n2853,5,266,2017-05-11 13:15:43,http://fisher.io/cristobal,,46.147.21.116,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n2854,5,267,2017-03-11 12:57:03,http://murazikstrosin.biz/diego,,154.32.27.198,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n2855,5,267,2017-04-24 02:55:26,http://legros.info/ephraim,,74.171.125.104,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n2856,5,267,2017-04-12 04:03:33,http://runolfsdottir.net/alexandria,,124.10.213.9,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n2857,5,267,2017-02-15 23:19:14,http://watsica.info/godfrey,,76.239.27.202,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n2858,5,267,2017-02-13 07:04:47,http://abshire.com/verna,,63.221.218.5,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n2859,5,267,2017-01-11 04:03:36,http://millchmitt.com/garrett_ward,,124.10.213.9,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n2860,5,267,2017-02-16 07:28:34,http://lueilwitzreichert.com/christelle_fahey,,102.186.233.219,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n2861,5,267,2017-05-07 01:07:48,http://lehner.net/deonte_oreilly,,21.188.188.183,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n2862,5,267,2017-04-16 13:28:08,http://koelpin.info/bridget,,3.13.50.113,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n2863,5,267,2017-03-05 08:00:04,http://grahamleuschke.biz/river_ruel,,159.69.221.135,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n2864,5,267,2017-03-15 17:38:01,http://hettinger.io/henriette.stamm,,146.109.146.104,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n2865,5,267,2017-06-03 15:31:35,http://quitzon.biz/giovanny_rosenbaum,,63.221.218.5,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n2866,5,267,2016-12-17 16:08:29,http://turner.io/meggie.keeling,,101.186.77.9,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n2867,5,267,2016-12-24 10:32:38,http://bailey.info/merl,,241.196.40.160,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n2868,5,267,2017-04-10 19:56:12,http://kaulke.info/orie_cain,,63.221.218.5,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n2869,5,267,2017-01-21 04:55:22,http://gradyabshire.org/ford,,173.229.124.123,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n2870,5,267,2017-02-06 21:20:24,http://stoltenberg.name/elmer.yundt,,131.57.97.68,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n2871,5,267,2017-04-11 19:34:39,http://jacobi.io/mona,,43.213.198.8,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n2872,5,268,2017-04-29 05:12:18,http://walsh.com/rebeka_kunde,,163.107.166.14,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n2873,5,268,2017-03-26 17:08:04,http://eichmann.net/frieda,,87.70.168.129,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n2874,5,268,2017-02-12 06:47:12,http://adamsheaney.com/jeffrey_romaguera,,127.142.118.201,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n2875,5,268,2017-01-24 16:02:19,http://cruickshankschroeder.com/flo_raynor,,15.112.231.73,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n2876,5,268,2017-02-03 10:51:46,http://fisherbotsford.biz/woodrow.mcglynn,,87.70.168.129,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n2877,5,268,2017-04-04 21:19:53,http://kuvalis.name/heber,,69.42.150.203,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n2878,5,268,2017-05-15 04:44:59,http://wittingschmidt.name/sam,,224.74.10.112,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n2879,5,268,2017-03-26 18:01:47,http://stehr.biz/shanie_harris,,116.154.218.71,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n2880,5,268,2017-04-12 20:07:43,http://gutmann.name/stefan.goodwin,,166.172.58.116,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n2881,5,268,2017-03-28 16:38:25,http://romaguera.net/leila,,15.209.191.43,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n2882,5,268,2017-01-20 09:20:09,http://ohara.co/dion_lebsack,,238.78.93.113,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n2883,5,268,2017-01-22 03:20:47,http://feest.net/martin,,238.78.93.113,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n2884,5,268,2017-04-11 06:18:38,http://grantnolan.co/priscilla.quitzon,,229.212.213.55,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n2885,5,268,2017-03-10 14:36:13,http://bahringerhaley.io/alvis_gerhold,,127.142.118.201,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n2886,5,268,2017-03-21 13:56:30,http://reichelmitchell.biz/gage.abshire,,24.140.127.63,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n2887,5,268,2017-03-29 17:01:27,http://schoenbarton.name/lizeth,,83.165.9.127,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n2888,5,268,2017-03-17 18:16:08,http://shields.info/kaci_cormier,,137.12.21.86,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n2889,5,268,2017-03-06 20:28:32,http://fadeldaniel.io/teie,,24.140.127.63,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n2890,5,269,2017-04-22 16:18:07,http://creminwilkinson.info/branson,,235.226.98.225,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n2891,5,269,2017-04-20 07:41:26,http://hyatt.info/ivy.mohr,,99.35.226.59,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n2892,5,269,2017-01-12 03:19:16,http://anderson.info/berenice_oconnell,,215.198.35.11,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n2893,5,269,2017-02-27 06:22:48,http://williamson.com/beryl.schulist,,192.238.199.150,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n2894,5,269,2017-01-01 10:41:33,http://beer.name/savion,,101.243.115.11,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n2895,5,269,2017-02-23 21:15:34,http://deckow.org/thaddeus,,29.253.101.128,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n2896,5,269,2017-04-03 22:13:55,http://beier.io/agnes,,25.143.50.128,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n2897,5,269,2017-05-13 20:02:14,http://walter.biz/gust.kris,,29.253.101.128,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n2898,5,269,2017-01-27 03:54:10,http://nienowstark.org/malcolm,,220.181.231.72,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n2899,5,269,2017-05-15 13:39:54,http://windler.io/junior,,193.198.155.100,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n2900,5,269,2017-01-13 18:24:59,http://crona.biz/carson,,50.67.30.126,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n2901,5,269,2017-02-27 06:33:09,http://gradyhirthe.co/enid.witting,,71.81.205.96,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n2902,5,269,2017-05-05 14:05:18,http://rath.net/terrence,,235.226.98.225,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n2903,5,269,2017-04-25 19:51:03,http://mcglynn.com/brenna,,238.57.203.153,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n2904,5,269,2017-02-23 22:15:58,http://huel.com/roderick_collier,,38.152.50.205,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n2905,5,269,2017-03-28 01:05:54,http://osinskinienow.org/dante_nitzsche,,72.196.133.76,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n2906,5,269,2017-01-04 17:43:54,http://paucekwehner.org/khalil,,161.9.133.164,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n2907,5,269,2017-01-29 04:48:28,http://corkeryzemlak.net/rhianna,,38.152.50.205,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n2908,5,270,2017-05-07 00:13:10,http://beattylynch.co/jamison,,66.102.174.43,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n2909,5,270,2017-01-28 10:52:50,http://sawaynokon.io/hal,,95.155.168.112,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n2910,5,270,2017-03-14 19:46:27,http://feestkuhlman.com/ruel.nader,,124.138.142.92,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n2911,5,270,2017-04-05 20:23:36,http://carterschmeler.co/jennings.champlin,,164.141.144.152,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n2912,5,270,2017-01-28 08:57:49,http://jast.name/anabelle_bartell,,71.167.240.38,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n2913,5,270,2017-06-07 23:12:04,http://mayert.io/tomasa,,78.34.23.74,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n2914,5,270,2017-03-19 00:29:50,http://littel.net/jamison.klocko,,95.155.168.112,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n2915,5,270,2017-01-02 13:34:38,http://keelingwiza.co/mellie.prosacco,,99.86.245.182,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n2916,5,270,2017-02-09 18:25:13,http://zboncak.info/deron,,19.37.177.140,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n2917,5,270,2017-06-05 19:06:21,http://ryanhayes.name/dangelo,,130.185.101.87,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n2918,5,270,2017-05-19 11:53:16,http://corwin.com/anya.hodkiewicz,,149.37.223.19,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n2919,5,270,2017-04-19 02:49:56,http://kuhn.com/demario_parker,,124.138.142.92,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n2920,5,270,2017-04-18 18:03:02,http://wehner.name/irving,,157.10.80.246,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n2921,5,270,2017-04-12 09:06:32,http://gottliebeichmann.io/mackenzie.hudson,,68.139.213.70,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n2922,5,271,2016-12-31 08:16:02,http://pfefferkutch.net/marisa,,68.247.177.215,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n2923,5,271,2017-02-05 11:03:34,http://franecki.name/susana.walter,,32.38.198.224,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n2924,5,271,2017-01-22 22:44:19,http://corkery.biz/gabriel,,139.93.208.204,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n2925,5,271,2017-05-28 03:54:52,http://kihn.com/kaylie,,77.30.59.138,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n2926,5,271,2017-02-06 09:02:16,http://corkery.net/elia,,21.247.62.123,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n2927,5,271,2017-06-11 19:38:43,http://gleasonheidenreich.info/paula_ryan,,102.177.96.148,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n2928,5,271,2017-01-06 04:55:50,http://gorczany.biz/maye.medhurst,,206.17.195.99,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n2929,5,271,2017-05-17 15:01:39,http://cristwest.com/kirsten.pollich,,204.145.129.117,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n2930,5,271,2017-01-03 09:20:21,http://watsica.co/andre.hodkiewicz,,93.41.73.124,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n2931,5,271,2017-04-10 12:39:24,http://schoenstehr.co/gust.langworth,,93.41.73.124,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n2932,5,271,2017-01-15 13:20:39,http://daniel.info/brent_schiller,,126.115.246.73,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n2933,5,271,2017-02-09 17:31:11,http://ohara.co/augusta,,206.17.195.99,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n2934,5,272,2017-05-30 23:55:41,http://schaeferwitting.com/jarrett_pouros,,95.52.75.143,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n2935,5,272,2016-12-31 14:02:05,http://hudson.com/rosemarie,,118.168.130.80,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n2936,5,272,2017-04-30 07:04:09,http://tromp.biz/theo.moore,,196.144.198.15,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n2937,5,272,2017-02-24 21:33:59,http://marquardt.io/annette.mann,,188.155.44.250,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n2938,5,272,2017-01-30 04:11:35,http://medhurstfeeney.org/shea_wolf,,14.112.244.125,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n2939,5,272,2017-02-12 09:04:19,http://hills.co/reinhold_ryan,,247.73.150.51,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n2940,5,272,2017-06-11 22:56:44,http://joneshuels.biz/nigel,,215.51.15.37,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n2941,5,272,2017-04-03 08:14:15,http://little.co/chad_ko,,247.73.150.51,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n2942,5,272,2017-03-28 08:06:11,http://streichblock.biz/garry,,167.37.95.54,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n2943,5,273,2017-03-26 00:34:54,http://grimes.io/roscoe.price,,202.137.232.251,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n2944,5,273,2017-01-26 08:27:13,http://kertzmannmurphy.com/rocio.bauch,,38.19.69.145,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n2945,5,273,2017-05-11 07:26:38,http://lockman.net/kennedy_bode,,234.211.126.100,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n2946,5,273,2017-01-24 03:59:04,http://lowe.net/bennett,,130.145.175.184,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n2947,5,273,2016-12-17 16:54:43,http://schiller.net/alene.kulas,,125.129.41.4,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n2948,5,273,2017-03-02 10:15:51,http://kohlerprice.info/cory,,101.95.137.101,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n2949,5,273,2017-04-15 15:54:20,http://krisblock.biz/lily.mcclure,,128.49.219.148,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n2950,5,273,2017-02-14 08:56:11,http://schmidt.io/giovanni.williamson,,202.137.232.251,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n2951,5,273,2017-01-13 07:19:24,http://schowalterherman.name/katlyn.keeling,,128.49.219.148,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n2952,5,273,2017-01-23 05:55:10,http://vonrueden.com/earlene,,130.145.175.184,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n2953,5,273,2017-01-24 23:39:41,http://romagueraklein.org/emile.kuhic,,102.144.41.244,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n2954,5,273,2016-12-21 14:24:57,http://larsonwaelchi.io/orville,,32.146.231.144,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n2955,5,273,2017-03-10 17:48:22,http://moriette.com/mozelle_dooley,,101.95.137.101,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n2956,5,273,2017-05-04 03:27:22,http://ohara.com/ollie,,130.145.175.184,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n2957,5,273,2017-04-06 02:36:35,http://rohan.net/angie,,94.241.248.233,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n2958,5,273,2017-05-05 09:26:30,http://renner.co/ronny.kuvalis,,13.23.119.23,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n2959,5,273,2017-05-31 22:28:12,http://marvin.org/ashlynn,,222.243.219.127,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n2960,5,274,2017-04-06 15:16:50,http://reillyjacobson.com/jayce,,235.139.211.201,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n2961,5,274,2017-03-19 05:30:47,http://fritschcrona.name/ally,,249.161.9.23,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n2962,5,274,2017-04-24 15:03:42,http://kirlin.info/payton,,171.163.199.18,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n2963,5,274,2017-03-19 04:28:04,http://erdman.com/loren,,72.135.55.12,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n2964,5,274,2017-01-08 19:53:57,http://quitzon.net/trycia,,137.232.143.73,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n2965,5,274,2017-01-09 22:51:17,http://schmeler.io/vicenta,,219.66.231.107,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n2966,5,274,2017-03-05 05:25:27,http://dubuque.org/abbigail.mills,,72.135.55.12,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n2967,5,274,2017-03-11 23:26:18,http://huel.com/axel,,95.56.222.222,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n2968,5,274,2017-05-13 14:11:33,http://rosenbaummarquardt.net/yasmine,,105.56.178.152,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n2969,5,274,2017-03-20 21:01:54,http://kunde.com/patricia_bins,,50.65.24.236,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n2970,5,274,2016-12-30 05:57:26,http://herzoglindgren.biz/lawson,,137.232.143.73,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n2971,5,274,2017-03-31 06:03:36,http://rempel.org/marietta_feest,,212.247.162.115,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n2972,5,274,2017-05-31 07:47:26,http://strosin.org/gudrun_zulauf,,72.135.55.12,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n2973,5,274,2017-04-06 17:20:48,http://lubowitz.biz/sallie.bartoletti,,219.66.231.107,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n2974,5,274,2017-01-21 16:15:30,http://marquardt.info/joanie,,22.101.187.92,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n2975,5,274,2017-01-18 17:25:01,http://kemmer.name/adolphus,,105.56.178.152,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n2976,5,274,2017-01-01 22:09:06,http://turnerprice.org/jimmy_kautzer,,235.139.211.201,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n2977,5,274,2017-01-18 11:23:44,http://kuvalisbraun.org/francisco,,26.110.97.213,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n2978,5,274,2017-01-29 19:45:58,http://dietrich.org/meghan.welch,,159.251.72.204,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n2979,5,274,2017-04-01 16:49:01,http://zieme.co/rupert,,50.65.24.236,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n2980,5,275,2017-02-21 16:34:20,http://toy.io/ima,,211.42.187.89,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n2981,5,275,2017-05-14 00:35:42,http://beatty.com/ardith_keler,,38.62.136.78,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n2982,5,275,2017-05-11 12:44:57,http://weber.info/caandra.monahan,,38.62.136.78,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n2983,5,275,2017-02-27 22:37:14,http://olson.io/terrell.dickens,,195.64.10.59,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n2984,5,275,2017-01-07 01:25:11,http://parisianromaguera.info/erich,,131.77.27.240,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n2985,5,275,2017-04-01 15:19:35,http://lubowitz.info/rae,,150.136.21.39,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n2986,5,275,2017-04-11 05:04:33,http://wisoky.org/salvador.leuschke,,134.207.8.76,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n2987,5,275,2017-01-25 00:57:17,http://corkery.org/max,,195.64.10.59,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n2988,5,275,2017-03-24 16:10:50,http://ward.org/will_mosciski,,180.251.2.246,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n2989,5,276,2016-12-17 09:04:24,http://adams.io/nichole_kirlin,,239.39.141.104,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n2990,5,276,2017-02-08 01:18:02,http://mosciski.name/layla_steuber,,141.7.176.167,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n2991,5,276,2017-05-24 16:00:22,http://crona.org/jennings,,65.250.233.20,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n2992,5,276,2017-01-26 02:53:37,http://johnsoconner.name/sarai,,72.47.128.72,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n2993,5,276,2017-01-04 08:35:51,http://schummbashirian.info/kitty,,34.5.253.114,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n2994,5,276,2017-04-24 08:49:25,http://howell.net/dillon_greenfelder,,83.8.126.207,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n2995,5,276,2016-12-29 17:51:53,http://walker.net/kallie_cain,,174.95.6.228,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n2996,5,276,2017-03-20 22:16:19,http://koelpin.biz/garnet,,44.21.122.109,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n2997,5,276,2017-01-25 05:39:49,http://dareschamberger.org/lazaro.kuphal,,48.152.146.10,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n2998,5,277,2017-03-10 04:19:12,http://kirlinwiegand.net/taryn.daugherty,,53.193.228.191,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n2999,5,277,2017-01-03 16:11:35,http://miller.co/joannie.ritchie,,211.131.196.132,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n3000,5,277,2017-02-24 10:27:07,http://crooks.biz/kale.lockman,,55.160.84.241,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n3001,5,277,2017-01-20 10:10:08,http://sawayn.io/kristy_king,,95.224.22.18,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n3002,5,277,2017-04-13 19:47:44,http://joneskreiger.org/clemens,,44.66.119.63,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n3003,5,277,2017-03-20 02:41:35,http://pfeffer.co/mallory_heathcote,,229.51.113.84,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n3004,5,277,2017-04-05 15:39:37,http://leuschkehammes.co/telly,,211.131.196.132,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n3005,5,277,2017-04-15 17:55:28,http://damore.org/katarina,,130.90.194.202,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n3006,5,277,2017-04-10 17:10:28,http://buckridge.net/mandy_prosacco,,206.219.70.243,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n3007,5,277,2017-05-31 21:02:29,http://wisozkwaters.name/alicia,,242.19.25.219,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n3008,5,277,2017-02-08 16:36:15,http://jacobs.biz/connor_kozey,,82.94.109.75,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n3009,5,277,2017-04-03 15:10:30,http://miller.name/danika,,95.224.22.18,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n3010,5,277,2016-12-24 21:38:18,http://wiza.name/alize,,206.29.80.132,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n3011,5,277,2017-02-25 03:29:21,http://hackett.biz/johann.toy,,55.160.84.241,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n3012,5,277,2017-05-02 11:23:12,http://beatty.co/ebba,,193.120.147.15,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n3013,5,277,2016-12-19 03:08:09,http://price.info/annamae,,226.54.21.49,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n3014,5,277,2017-06-02 01:11:51,http://spencer.net/candida,,229.51.113.84,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n3015,5,278,2017-05-04 00:10:19,http://rohanerdman.name/clementine,,28.34.213.153,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n3016,5,278,2017-02-06 17:48:19,http://huels.info/dominic,,81.251.23.109,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n3017,5,278,2017-02-12 07:17:59,http://bins.com/alexie.tillman,,109.109.135.182,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n3018,5,278,2017-01-08 05:43:47,http://bode.com/dax_hintz,,117.169.141.12,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n3019,5,278,2017-02-22 21:18:43,http://bernier.name/uriel.collins,,182.205.196.250,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n3020,5,278,2017-01-11 07:00:46,http://cartwright.com/ray,,12.37.184.178,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n3021,5,278,2017-01-01 04:51:49,http://muller.io/enrico.nienow,,246.251.143.42,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n3022,5,278,2016-12-27 01:53:34,http://hagenes.io/heloise_buckridge,,122.195.77.72,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n3023,5,278,2016-12-19 12:04:07,http://bailey.io/garland_schuster,,131.65.86.101,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n3024,5,278,2017-04-26 23:50:09,http://bergstrom.com/darrell.mccullough,,175.159.169.159,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n3025,5,278,2017-01-14 19:13:26,http://waters.io/garett,,12.37.184.178,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n3026,5,278,2017-02-03 21:12:48,http://torp.net/ethelyn,,121.230.19.226,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n3027,5,278,2017-03-01 15:54:36,http://hamill.com/wilma,,177.56.220.222,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n3028,5,278,2017-04-17 01:12:05,http://fadel.io/donato_rodriguez,,95.26.152.2,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n3029,5,278,2017-04-05 21:52:45,http://pfannerstill.biz/mckenzie.sauer,,146.10.183.220,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n3030,5,278,2017-03-26 21:55:19,http://beierschimmel.co/mozell,,12.37.184.178,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n3031,5,278,2016-12-23 20:43:27,http://crona.biz/brandi.waters,,28.34.213.153,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n3032,5,278,2017-05-17 11:58:59,http://schoenortiz.co/bailee,,63.104.137.168,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n3033,5,279,2017-03-28 12:55:53,http://wiegand.io/kirk_hamill,0.0000000000,60.98.123.200,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n3034,5,279,2017-03-22 03:44:15,http://hodkiewiczemard.net/benny,0.0000000000,82.91.227.158,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n3035,5,279,2017-02-08 13:44:18,http://kerluke.org/marcelle.jenkins,0.0000000000,96.135.96.100,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n3036,5,279,2017-06-05 11:04:07,http://nienow.io/eli.friesen,0.0000000000,176.68.147.103,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n3037,5,279,2017-04-20 19:46:37,http://lehner.info/marvin,0.0000000000,176.8.46.228,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n3038,5,279,2017-06-10 15:33:05,http://gulgowski.io/emiliano,0.0000000000,243.163.30.42,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n3039,5,279,2016-12-17 10:34:10,http://cummings.co/maryse,0.0000000000,228.118.191.225,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n3040,5,279,2017-01-05 02:49:17,http://cainschumm.name/citlalli.schneider,0.0000000000,80.23.113.32,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n3041,5,279,2016-12-21 18:29:08,http://kuhlmanabbott.info/nicholas,0.0000000000,99.187.63.223,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n3042,5,280,2017-05-14 08:58:48,http://oreilly.net/deshawn,2.0000000000,5.79.102.34,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n3043,5,280,2017-04-04 21:14:58,http://pacocha.io/berta_parisian,2.0000000000,227.249.155.170,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n3044,5,280,2017-03-19 02:14:54,http://vonrueden.co/elian,1.0000000000,64.184.136.34,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n3045,5,280,2017-03-24 08:48:00,http://dach.co/rashawn,1.0000000000,171.217.125.91,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n3046,5,280,2017-03-30 05:24:28,http://eichmann.net/lucio,1.0000000000,227.249.155.170,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n3047,5,280,2017-01-08 13:54:21,http://carrollwyman.name/abdiel,0.0000000000,227.249.155.170,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n3048,5,280,2017-03-29 05:53:05,http://denesik.net/maynard.morar,2.0000000000,111.235.217.176,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n3049,5,280,2017-01-29 10:17:36,http://hermann.com/imelda,2.0000000000,60.164.242.47,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n3050,5,280,2017-02-10 06:10:25,http://stanton.biz/boris_harris,0.0000000000,181.231.111.41,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n3051,5,280,2016-12-24 21:11:52,http://runolfonjohnson.org/emerson_kunde,3.0000000000,218.177.174.36,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n3052,5,280,2017-04-21 23:38:09,http://okon.net/jamel,0.0000000000,163.226.246.54,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n3053,5,280,2016-12-21 02:05:55,http://raynor.name/nova,3.0000000000,218.53.150.5,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n3054,5,281,2017-03-26 06:22:03,http://toy.info/easter.lynch,1.0000000000,111.41.230.229,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n3055,5,281,2017-05-30 11:40:58,http://casperhilpert.org/maximillia.greenfelder,2.0000000000,134.5.192.109,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n3056,5,281,2017-04-22 18:25:03,http://marks.co/kathlyn.ziemann,2.0000000000,111.41.230.229,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n3057,5,281,2017-03-04 09:08:58,http://raulangosh.co/devon.wilkinson,0.0000000000,134.5.192.109,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n3058,5,281,2017-03-13 08:39:00,http://leschdubuque.net/darrin,0.0000000000,121.228.123.252,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n3059,5,281,2017-06-09 22:21:18,http://purdycollins.net/elijah_runolfon,2.0000000000,231.77.84.67,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n3060,5,281,2017-03-03 02:17:11,http://kiehnbaumbach.name/brennon.dach,2.0000000000,134.5.192.109,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n3061,5,281,2017-02-13 06:04:05,http://grady.name/frieda,3.0000000000,164.123.171.152,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n3062,5,281,2017-03-02 23:04:54,http://boehm.net/perry_breitenberg,3.0000000000,254.203.236.245,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n3063,5,281,2017-05-16 13:05:05,http://lemke.io/elroy.reichel,2.0000000000,216.120.118.50,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n3064,5,282,2017-01-19 09:30:42,http://gutmann.biz/sandrine_ledner,1.0000000000,203.207.214.143,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n3065,5,282,2016-12-17 10:28:23,http://kohler.io/aniyah,1.0000000000,161.224.203.16,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n3066,5,282,2017-02-05 18:29:36,http://robelbalistreri.org/neil.bauch,2.0000000000,137.176.82.202,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n3067,5,282,2017-01-03 21:06:47,http://thompson.net/lucy_spinka,0.0000000000,169.148.187.213,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n3068,5,282,2017-01-09 18:36:37,http://cormier.name/erna_leannon,1.0000000000,57.28.194.234,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n3069,5,282,2017-05-14 10:41:40,http://hudson.co/joan,0.0000000000,203.207.214.143,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n3070,5,282,2017-05-01 14:23:35,http://stiedemann.com/izaiah.boyle,0.0000000000,241.199.39.5,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n3071,5,282,2017-04-12 23:52:01,http://wilderman.io/gracie_tillman,0.0000000000,166.100.133.65,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n3072,5,282,2017-02-28 13:04:15,http://wilkinsonabernathy.name/hallie,1.0000000000,166.100.133.65,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n3073,5,282,2017-03-14 02:55:26,http://hahnmetz.io/isabell.weinat,0.0000000000,220.193.145.128,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n3074,5,282,2017-05-25 14:20:45,http://jaskolskiklocko.org/cristal,0.0000000000,254.78.210.48,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n3075,5,283,2017-02-28 04:00:16,http://roberts.co/brown,1.0000000000,9.59.160.216,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n3076,5,283,2017-05-10 13:36:19,http://jakubowski.org/hadley_torp,1.0000000000,56.132.157.83,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n3077,5,283,2017-01-08 17:42:52,http://gaylord.com/danyka,1.0000000000,156.128.206.209,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n3078,5,283,2017-06-12 06:40:39,http://murazik.co/sincere_kulas,0.0000000000,199.90.150.138,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n3079,5,283,2016-12-17 07:37:24,http://gorczany.info/ansel,0.0000000000,78.87.44.139,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n3080,5,283,2017-06-13 02:32:51,http://flatley.io/irving,0.0000000000,44.16.234.13,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n3081,5,283,2017-05-04 22:37:22,http://powlowskihowell.biz/randall,0.0000000000,104.20.43.197,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n3082,5,283,2017-01-25 21:09:27,http://okunevagorczany.name/elvie.mitchell,0.0000000000,3.197.215.104,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n3083,5,283,2017-03-25 02:25:53,http://fay.name/buford,0.0000000000,231.26.193.67,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n3084,5,283,2016-12-28 06:50:49,http://quitzon.co/alison,1.0000000000,132.68.78.153,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n3085,5,283,2016-12-27 21:48:15,http://batzweinat.org/eunice.mccullough,1.0000000000,12.151.195.196,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n3086,5,283,2017-02-02 18:53:38,http://stiedemann.info/dorris.corwin,1.0000000000,89.32.150.92,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n3087,5,283,2017-02-12 06:39:46,http://wilkinson.info/percy.howell,0.0000000000,12.151.195.196,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n3088,5,283,2017-01-21 11:54:14,http://farrell.name/damian.lowe,1.0000000000,44.16.234.13,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n3089,5,284,2017-01-22 03:50:46,http://mertz.io/gust_harris,1.0000000000,209.81.247.43,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n3090,5,284,2017-04-19 23:54:10,http://creminbotsford.org/ona,1.0000000000,254.179.149.164,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n3091,5,284,2017-02-11 02:23:51,http://breitenberg.com/colby_treutel,1.0000000000,180.239.187.156,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n3092,5,284,2017-01-30 16:48:32,http://zboncak.org/sheila,0.0000000000,168.164.190.77,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n3093,5,285,2017-04-09 03:13:07,http://goodwin.com/rylan.ferry,3.0000000000,95.232.121.161,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n3094,5,285,2017-05-18 00:12:25,http://heaney.info/cristina,2.0000000000,129.200.46.203,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n3095,5,285,2016-12-19 16:18:54,http://larson.biz/marcelina.braun,3.0000000000,156.243.215.26,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n3096,5,285,2017-01-23 09:14:25,http://abernathy.name/zachariah_bernhard,0.0000000000,100.90.200.253,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n3097,5,285,2017-06-08 17:32:50,http://whiterunte.net/alysa,1.0000000000,238.221.135.205,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n3098,5,285,2017-04-16 15:31:37,http://gusikowski.com/lambert.bins,3.0000000000,129.31.92.208,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n3099,5,285,2017-02-13 18:50:46,http://hettingerhermann.com/christian.gusikowski,2.0000000000,19.93.150.105,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n3100,5,285,2017-02-28 10:43:18,http://boscobeer.net/christa.herman,0.0000000000,80.186.156.140,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n3101,5,285,2017-04-07 21:05:42,http://ondricka.name/stephanie_upton,1.0000000000,129.31.92.208,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n3102,5,285,2017-06-12 19:14:58,http://flatley.org/josie.rodriguez,2.0000000000,197.138.45.83,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n3103,5,285,2017-05-22 06:20:05,http://kuhlman.biz/kelli.bartoletti,1.0000000000,148.156.111.158,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n3104,5,285,2017-02-21 11:29:15,http://mcdermottromaguera.info/marquis.gislason,0.0000000000,246.247.188.185,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n3105,5,285,2017-02-28 16:50:28,http://sawaynrowe.net/nelda_veum,2.0000000000,192.167.72.153,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n3106,5,285,2017-04-23 02:19:41,http://fritsch.name/eldora_kertzmann,0.0000000000,100.90.200.253,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n3107,5,285,2017-05-31 05:37:29,http://gorczanykunde.name/darryl,2.0000000000,238.221.135.205,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n3108,5,285,2017-06-09 09:35:05,http://moriette.io/ladarius,3.0000000000,24.234.80.157,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n3109,5,285,2017-02-22 16:32:59,http://corwin.net/wendell.cummerata,3.0000000000,192.167.72.153,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n3110,5,285,2017-03-12 09:43:00,http://rice.net/houston,1.0000000000,197.138.45.83,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n3111,5,285,2017-01-02 21:24:59,http://shanahanglover.net/bryon,1.0000000000,12.178.198.24,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n3112,5,285,2017-01-02 15:24:33,http://wunsch.net/clark,3.0000000000,21.72.181.40,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n3113,5,286,2017-02-08 17:56:26,http://barton.co/alverta,1.0000000000,190.76.86.8,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n3114,5,286,2017-01-18 11:21:19,http://halvorson.org/orpha,1.0000000000,126.220.183.132,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n3115,5,286,2017-02-11 20:00:51,http://doyle.net/norberto,0.0000000000,61.7.17.163,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n3116,5,286,2017-05-13 07:42:02,http://hudson.net/kasey.lang,0.0000000000,200.191.34.127,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n3117,5,286,2017-03-07 01:31:27,http://kshlerin.com/amparo_keeling,0.0000000000,58.72.47.44,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n3118,5,286,2017-03-10 23:57:08,http://legros.biz/clinton.kulas,0.0000000000,82.66.163.94,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n3119,5,287,2017-01-07 14:01:20,http://hartmannkerluke.org/elna,1.0000000000,167.169.45.70,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n3120,5,287,2017-02-18 23:47:41,http://bins.info/pete.abernathy,1.0000000000,167.169.45.70,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n3121,5,287,2017-01-23 06:57:25,http://kaulke.biz/austyn.ryan,2.0000000000,50.130.174.129,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n3122,5,288,2017-06-11 09:21:39,http://haley.com/ophelia,,174.63.17.95,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n3123,5,288,2017-03-11 04:32:30,http://franecki.io/marjory.moore,,44.250.113.125,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n3124,5,288,2017-04-21 16:06:55,http://walterbrakus.biz/taryn_goldner,,170.72.207.199,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n3125,5,288,2017-04-01 04:11:33,http://purdy.com/percy_pfeffer,,170.72.207.199,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n3126,5,288,2017-06-04 00:28:32,http://jones.name/brannon_welch,,174.63.17.95,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n3127,5,288,2017-02-05 04:45:04,http://herzog.net/gavin.gibson,,100.142.27.116,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n3128,5,288,2016-12-30 01:11:34,http://shieldshickle.co/reid,,27.154.71.32,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n3129,5,288,2017-01-01 17:01:11,http://schoen.co/adrianna,,109.63.98.158,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n3130,5,288,2016-12-17 03:56:50,http://jacobs.com/keely_murphy,,119.52.214.167,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n3131,5,288,2017-02-05 17:55:35,http://lesch.co/berneice,,105.181.169.129,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n3132,5,288,2017-02-28 14:06:03,http://ziemann.org/chris,,83.79.21.19,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n3133,5,288,2016-12-28 22:26:42,http://fisher.name/kiara.ko,,119.52.214.167,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n3134,5,288,2017-01-29 10:46:20,http://gleason.com/ines_hane,,146.10.241.14,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n3135,5,288,2017-03-30 21:09:55,http://bartonwaters.name/mary,,105.17.77.86,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n3136,5,288,2017-02-06 14:12:37,http://stehrvonrueden.com/adele,,17.129.97.248,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n3137,5,289,2016-12-31 11:06:49,http://collins.info/sister.jerde,,28.145.201.223,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n3138,5,289,2017-01-17 14:25:40,http://roberts.net/eladio,,58.73.244.7,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n3139,5,289,2017-04-06 22:05:06,http://harris.com/gillian.reichert,,28.70.136.96,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n3140,5,289,2017-03-13 15:39:41,http://schmelerpfeffer.com/clemmie,,160.127.174.7,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n3141,5,289,2017-01-24 16:51:50,http://bayer.co/megane.fadel,,12.148.102.227,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n3142,5,289,2017-02-03 07:54:55,http://weber.name/hosea_schuster,,44.206.232.19,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n3143,5,289,2017-03-03 00:52:39,http://grimes.org/dakota.batz,,173.91.151.16,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n3144,5,289,2017-04-02 23:08:06,http://hilpert.net/jamil.damore,,34.145.244.196,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n3145,5,289,2017-02-14 19:20:31,http://mckenzie.org/moie,,138.229.120.99,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n3146,5,289,2017-03-30 20:18:42,http://quigley.name/gerda,,173.231.24.48,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n3147,5,289,2017-05-12 21:25:46,http://borer.co/mikayla,,107.140.161.117,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n3148,5,289,2017-04-18 13:46:01,http://raynorrippin.co/geovanny,,96.123.158.170,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n3149,5,289,2017-03-01 16:56:11,http://legroskuphal.name/ernestine.weinat,,50.155.99.238,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n3150,5,289,2017-06-02 07:24:13,http://hermann.co/deron,,107.236.24.58,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n3151,5,289,2017-05-23 00:48:31,http://krajcik.co/adele_hegmann,,12.148.102.227,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n3152,5,289,2017-01-28 06:29:26,http://dickinson.name/spencer.prohaska,,252.28.136.77,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n3153,5,290,2017-05-16 14:52:02,http://schadenspencer.name/ashton_wintheiser,,242.149.248.251,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n3154,5,290,2017-02-12 11:24:32,http://labadiewintheiser.co/annabelle_rutherford,,244.96.214.5,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n3155,5,290,2017-03-16 03:56:00,http://donnellyschiller.co/cole,,68.56.225.48,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n3156,5,290,2017-06-10 09:44:11,http://torp.info/ania,,173.197.73.230,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n3157,5,290,2017-04-08 04:23:27,http://lakinhahn.org/linwood,,244.244.32.253,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n3158,5,290,2017-02-11 18:40:11,http://trantow.name/brenda.wunsch,,190.243.88.181,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n3159,5,290,2017-05-27 22:38:18,http://rosenbaum.co/tremaine,,143.242.33.158,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n3160,5,290,2017-02-27 07:48:24,http://doyle.info/abdiel.hodkiewicz,,156.217.240.44,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n3161,5,290,2017-02-09 05:01:44,http://hamill.biz/benedict_zieme,,244.96.214.5,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n3162,5,290,2017-05-30 14:01:24,http://jakubowski.net/everett_mckenzie,,217.72.83.73,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n3163,5,290,2017-02-02 11:20:42,http://bartell.co/bernadine_mayert,,217.72.83.73,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n3164,5,290,2017-01-22 08:51:16,http://hoppe.io/eloise,,156.217.240.44,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n3165,5,290,2017-01-26 11:36:46,http://wilderman.org/ora,,244.244.32.253,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n3166,5,290,2017-05-01 05:04:49,http://ankunding.io/francisca,,224.90.243.143,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n3167,5,290,2017-02-26 05:01:33,http://considine.io/kole.parisian,,224.90.243.143,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n3168,5,290,2017-01-12 05:40:53,http://harberchamplin.name/hipolito,,217.72.83.73,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n3169,5,290,2017-04-20 21:06:53,http://paucekjaskolski.com/clifton.turcotte,,167.209.138.234,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n3170,5,290,2017-06-09 14:50:21,http://emard.io/lolita,,244.244.32.253,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n3171,5,290,2017-06-12 10:37:48,http://blick.io/juston_hartmann,,143.242.33.158,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n3172,5,290,2017-01-02 03:45:57,http://dibbert.co/fabian,,244.244.32.253,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n3173,5,291,2017-05-18 00:19:21,http://marvin.name/odie,,195.122.235.106,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n3174,5,291,2016-12-20 01:13:40,http://reinger.co/marty,,134.45.57.187,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n3175,5,291,2017-05-09 22:39:51,http://hartmanngulgowski.com/mireya,,141.68.20.248,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n3176,5,291,2017-02-20 23:53:52,http://oharaschneider.info/laron,,248.115.111.243,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n3177,5,291,2017-01-25 03:51:47,http://jasttreutel.com/caleb,,170.59.45.150,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n3178,5,291,2017-06-11 10:18:54,http://bosco.info/buster,,115.206.160.244,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n3179,5,291,2017-03-15 20:33:28,http://predovic.io/allie,,73.241.207.190,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n3180,5,291,2017-02-12 10:16:16,http://baumbach.biz/ro_gleichner,,221.111.173.139,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n3181,5,291,2017-02-14 22:26:46,http://fahey.name/terence.mcdermott,,115.206.160.244,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n3182,5,291,2017-01-04 23:35:35,http://gorczany.info/baron,,149.53.20.197,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n3183,5,292,2016-12-18 09:08:35,http://feeney.io/marty,,75.188.235.6,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n3184,5,292,2017-03-15 17:31:41,http://barton.org/ashly.bauch,,97.42.245.27,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n3185,5,292,2017-04-25 06:05:54,http://bauch.co/aida,,250.60.82.214,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n3186,5,292,2017-05-23 13:55:25,http://ziemannfranecki.info/idell.swift,,105.166.194.111,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n3187,5,292,2017-05-22 23:17:10,http://emmerichgreen.net/kenya.williamson,,71.80.232.252,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n3188,5,292,2017-02-09 07:49:41,http://thompson.com/glennie,,25.235.71.189,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n3189,5,292,2017-03-05 00:38:20,http://kunde.com/fay,,197.23.182.254,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n3301,5,304,2017-02-08 08:03:05,http://mann.name/luz,0.0000000000,161.66.146.85,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n3190,5,292,2017-04-14 07:11:27,http://bashirian.org/maddison,,7.78.111.93,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n3191,5,292,2017-03-05 02:27:20,http://mitchell.org/luciano_abshire,,21.72.97.213,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n3192,5,292,2017-05-22 04:12:48,http://kiehn.com/gregg.padberg,,100.56.166.220,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n3193,5,292,2017-04-16 01:40:25,http://rolfsonfahey.name/trenton_klein,,68.103.238.184,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n3194,5,292,2016-12-23 16:07:56,http://raynor.name/caterina.bergstrom,,245.117.206.3,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n3195,5,292,2016-12-19 22:00:22,http://oconnell.biz/leone.mosciski,,188.169.92.181,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n3196,5,292,2017-02-08 13:39:15,http://mohrrosenbaum.org/roman,,106.145.147.229,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n3197,5,293,2017-06-11 20:57:01,http://lubowitzwatsica.biz/katelyn.bins,,100.143.224.236,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n3198,5,293,2017-05-15 07:30:23,http://lesch.name/aaron,,108.248.85.147,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n3199,5,293,2017-01-23 16:11:15,http://spencergoldner.biz/darlene,,252.165.244.200,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n3200,5,293,2017-05-21 18:27:17,http://towne.info/gianni,,78.24.244.164,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n3201,5,293,2017-02-13 15:04:14,http://swaniawski.co/delia,,199.250.222.102,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n3202,5,293,2017-01-29 14:45:19,http://bartoletti.com/esta_crist,,100.143.224.236,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n3203,5,294,2017-02-20 09:07:14,http://donnelly.net/alayna,,230.212.214.129,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n3204,5,294,2017-01-12 02:26:45,http://kleinvandervort.name/mafalda_gottlieb,,250.172.221.126,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n3205,5,294,2016-12-15 19:10:46,http://weber.io/doyle,,250.155.133.59,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n3206,5,295,2017-03-31 07:39:04,http://pouros.name/alexis,2.0000000000,192.35.15.96,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n3207,5,295,2017-04-24 13:12:04,http://roobquitzon.name/garland,2.0000000000,116.119.172.221,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n3208,5,295,2017-01-12 02:39:20,http://runolfon.co/xzavier_harber,1.0000000000,113.202.114.115,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n3209,5,295,2017-05-15 14:49:09,http://torphy.info/mark.wiegand,1.0000000000,94.84.142.215,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n3210,5,295,2017-02-13 17:11:52,http://senger.io/nicolas,1.0000000000,9.26.183.188,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n3211,5,295,2017-01-21 23:26:10,http://ziemannrunolfsdottir.io/cameron_nitzsche,0.0000000000,9.219.126.149,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n3212,5,295,2017-02-22 19:30:58,http://conn.net/mitchell,0.0000000000,95.106.37.172,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n3213,5,295,2017-01-05 09:58:23,http://rice.name/tyree_pacocha,2.0000000000,116.84.12.251,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n3214,5,295,2017-01-20 17:37:38,http://schulist.co/colten,1.0000000000,183.239.185.104,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n3215,5,295,2017-03-07 05:41:37,http://bailey.io/mathias,0.0000000000,222.173.244.21,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n3216,5,295,2017-06-02 22:44:27,http://baileygreen.biz/charles,1.0000000000,241.221.93.23,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n3217,5,295,2017-02-03 21:42:43,http://funkkirlin.name/garfield,2.0000000000,94.84.142.215,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n3218,5,295,2017-03-31 04:56:28,http://sawayn.info/jerome_hauck,1.0000000000,24.148.116.113,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n3219,5,295,2017-04-21 15:37:32,http://donnellylakin.net/vaughn.daniel,0.0000000000,183.239.185.104,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n3220,5,295,2017-05-04 04:37:27,http://mayerhirthe.biz/amparo_prohaska,0.0000000000,178.211.35.164,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n3221,5,295,2017-05-29 04:51:40,http://rathkuhn.com/maud,2.0000000000,222.173.244.21,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n3222,5,295,2017-02-16 22:20:05,http://parker.org/astrid.thiel,2.0000000000,192.35.15.96,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n3223,5,295,2017-01-17 14:05:08,http://lednerreichel.co/elmo_walker,1.0000000000,72.172.8.6,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n3224,5,295,2017-02-28 15:30:33,http://bergstrom.info/jana_wolff,1.0000000000,178.211.35.164,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n3225,5,295,2017-01-29 16:29:45,http://kaulke.com/chasity.greenholt,2.0000000000,38.44.81.86,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n3226,5,296,2016-12-16 01:59:48,http://moriette.biz/layla,2.0000000000,21.142.120.228,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n3227,5,296,2017-01-15 01:00:54,http://bednar.com/wilford_mills,2.0000000000,106.53.18.68,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n3228,5,296,2017-01-03 20:32:59,http://kautzer.name/haven,2.0000000000,120.244.152.76,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n3229,5,296,2017-04-07 14:13:37,http://cainzemlak.biz/herminia,1.0000000000,126.157.201.64,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n3230,5,296,2017-05-11 00:39:01,http://wolff.org/stan,1.0000000000,72.218.111.159,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n3231,5,296,2017-04-27 11:32:57,http://kulas.io/concepcion.cremin,1.0000000000,105.190.248.175,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n3232,5,296,2017-02-07 23:26:50,http://nitzsche.io/rubye,0.0000000000,232.250.41.85,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n3233,5,297,2017-02-24 23:41:58,http://beahanbernier.com/leonie,2.0000000000,210.185.143.108,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n3234,5,297,2017-04-02 15:07:32,http://hagenes.org/nicolas,1.0000000000,116.191.2.177,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n3235,5,297,2017-01-31 20:02:23,http://zemlak.name/kobe.mclaughlin,2.0000000000,117.47.209.90,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n3236,5,297,2017-06-14 02:21:21,http://rohanpouros.org/lawrence,0.0000000000,62.132.245.171,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n3237,5,297,2017-05-31 07:44:56,http://emardwiegand.name/trevor,0.0000000000,252.198.83.7,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n3238,5,297,2017-01-06 08:55:01,http://frami.io/major,1.0000000000,47.150.217.203,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n3239,5,297,2017-01-31 14:13:51,http://kubschimmel.io/vincent,2.0000000000,242.136.58.116,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n3240,5,297,2017-04-08 10:08:01,http://oconner.info/may.bartoletti,2.0000000000,47.150.217.203,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n3241,5,297,2017-01-19 01:20:01,http://gibsonheaney.info/kaia_schumm,3.0000000000,210.185.143.108,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n3242,5,297,2017-04-27 10:52:35,http://schuppekuhlman.org/gavin,0.0000000000,219.204.37.55,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n3243,5,297,2017-02-09 04:03:29,http://sauerdoyle.io/lew_johns,2.0000000000,210.185.143.108,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n3244,5,297,2017-03-15 09:25:07,http://gibson.net/shane,0.0000000000,116.191.2.177,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n3245,5,298,2017-06-01 18:14:16,http://gibson.co/gracie.abshire,0.0000000000,54.39.37.101,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n3246,5,299,2017-03-07 20:58:35,http://wunschjerde.biz/kelli,2.0000000000,148.216.81.254,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n3247,5,299,2017-04-17 22:58:37,http://turcotte.co/eileen.robel,1.0000000000,248.9.149.56,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n3248,5,299,2017-03-10 06:21:05,http://bergehartmann.info/macie,0.0000000000,123.215.90.26,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n3249,5,299,2017-05-12 07:47:12,http://johns.com/jalen.terry,0.0000000000,212.243.170.229,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n3250,5,299,2017-05-30 15:07:29,http://osinski.net/raphaelle,0.0000000000,93.211.177.187,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n3251,5,299,2016-12-30 02:21:02,http://bergnaum.co/hipolito_collier,2.0000000000,93.211.177.187,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n3252,5,299,2017-04-11 22:33:23,http://leannon.info/oda,0.0000000000,148.216.81.254,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n3253,5,299,2016-12-24 15:31:25,http://casper.biz/timmy.cole,1.0000000000,58.168.204.203,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n3254,5,299,2017-03-08 13:43:31,http://zboncak.com/elenor,0.0000000000,218.78.254.136,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n3255,5,299,2017-02-08 08:40:58,http://brekkebernhard.info/kamryn.dibbert,1.0000000000,212.243.170.229,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n3256,5,299,2017-02-22 15:44:29,http://kohler.io/friedrich,1.0000000000,84.13.127.115,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n3257,5,299,2017-03-08 21:11:03,http://fisherblock.net/forrest,2.0000000000,77.80.22.226,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n3258,5,299,2017-02-23 08:26:03,http://okonoberbrunner.info/leone,0.0000000000,8.177.90.237,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n3259,5,299,2017-03-18 18:51:21,http://grady.info/velda_wyman,0.0000000000,128.239.117.117,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n3260,5,299,2017-03-09 12:04:52,http://borerking.biz/vena,0.0000000000,13.66.203.3,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n3261,5,299,2017-01-15 01:43:47,http://jakubowskischaefer.org/karina,1.0000000000,123.215.90.26,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n3262,5,299,2017-04-19 22:14:55,http://spinkarolfson.io/cara,2.0000000000,8.177.90.237,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n3263,5,299,2017-03-16 02:44:40,http://abshireosinski.biz/fausto,1.0000000000,164.36.238.52,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n3264,5,299,2017-05-04 12:59:42,http://swaniawski.co/bill,0.0000000000,123.215.90.26,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n3265,5,300,2017-06-08 17:34:43,http://batz.co/taylor,0.0000000000,183.174.159.65,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n3266,5,300,2016-12-31 12:32:09,http://gradyheidenreich.info/kiana_sporer,2.0000000000,171.128.25.193,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n3267,5,300,2017-06-09 10:54:12,http://raynorgraham.biz/madalyn.robel,1.0000000000,237.68.253.55,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n3268,5,300,2017-02-01 21:16:08,http://will.net/jerry,1.0000000000,22.122.49.159,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n3269,5,300,2017-05-14 11:51:48,http://jacobi.net/stephon.schuppe,2.0000000000,46.96.133.141,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n3270,5,300,2017-04-08 15:59:28,http://stehr.com/elian,1.0000000000,68.8.220.4,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n3271,5,300,2016-12-14 10:36:58,http://prohaska.org/dusty,1.0000000000,82.91.188.47,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n3272,5,300,2017-06-13 02:12:47,http://considine.net/marshall_kozey,0.0000000000,189.114.137.118,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n3273,5,300,2017-01-08 20:26:27,http://mcdermott.com/lela_luettgen,0.0000000000,118.174.113.248,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n3274,5,300,2017-02-09 21:47:16,http://thompson.com/paxton.rowe,0.0000000000,4.114.127.112,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n3275,5,300,2017-03-08 10:45:30,http://bernier.net/blair.doyle,2.0000000000,111.114.141.80,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n3276,5,300,2017-05-15 06:30:29,http://ullrich.info/rebekah_oreilly,0.0000000000,216.17.47.157,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n3277,5,300,2017-04-20 02:00:25,http://robel.io/marianna_donnelly,2.0000000000,101.55.142.175,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n3278,5,300,2017-04-20 09:53:32,http://stoltenbergheidenreich.net/velma,2.0000000000,245.179.16.9,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n3279,5,300,2017-06-12 06:58:30,http://stamm.biz/melya,1.0000000000,4.114.127.112,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n3280,5,300,2017-06-03 19:14:29,http://koelpinbergnaum.co/hubert,1.0000000000,146.216.178.144,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n3281,5,300,2017-01-17 23:25:05,http://hammes.org/beverly_beier,1.0000000000,183.174.159.65,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n3282,5,300,2017-03-28 05:28:25,http://parisianmayert.org/valentina,2.0000000000,93.123.216.46,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n3283,5,300,2017-01-24 23:16:05,http://konopelskistanton.info/cary.cummerata,1.0000000000,118.174.113.248,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n3284,5,300,2017-02-08 04:55:07,http://becker.org/bette_dickens,1.0000000000,245.179.16.9,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n3285,5,301,2017-05-10 21:53:57,http://goldner.org/gianni,0.0000000000,73.88.183.19,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n3286,5,301,2017-05-06 12:48:47,http://koelpingrant.net/keagan.schuppe,0.0000000000,31.94.30.11,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n3287,5,301,2017-01-23 10:47:39,http://larkinschuster.biz/alize,0.0000000000,73.88.183.19,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n3288,5,301,2017-01-09 16:23:44,http://monahan.org/keeley.cole,0.0000000000,67.161.31.249,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n3289,5,301,2017-01-19 09:26:51,http://veum.net/dolly.moen,0.0000000000,24.83.53.102,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n3290,5,302,2017-03-10 14:57:21,http://jakubowski.com/sadye_homenick,0.0000000000,68.156.189.107,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n3291,5,302,2017-03-07 00:19:31,http://streichwilkinson.com/solon_dickens,3.0000000000,106.189.146.206,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n3292,5,302,2017-02-23 10:48:52,http://armstrong.io/magnus,1.0000000000,161.120.20.37,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n3293,5,302,2017-02-17 01:57:32,http://schuppe.com/isidro.sporer,1.0000000000,225.32.204.191,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n3294,5,303,2017-04-06 07:40:25,http://nitzsche.name/forest_dietrich,1.0000000000,142.39.246.161,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n3295,5,303,2017-04-10 10:21:04,http://kodooley.org/vinnie,1.0000000000,145.2.63.170,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n3296,5,304,2017-05-28 14:29:50,http://ankundingcorkery.net/dwight_schaden,0.0000000000,101.153.214.157,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n3297,5,304,2017-06-12 16:51:29,http://rohanwalsh.net/mina,0.0000000000,188.65.162.58,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n3298,5,304,2017-02-17 16:07:42,http://schummschultz.io/susana,0.0000000000,64.61.241.81,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n3299,5,304,2017-06-12 00:30:44,http://nader.co/jany,0.0000000000,11.9.116.213,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n3300,5,304,2017-01-05 12:21:04,http://halvorsonkohler.com/pedro,0.0000000000,64.61.241.81,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n3302,5,304,2017-06-10 11:32:41,http://metz.info/daphne,0.0000000000,142.156.185.218,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n3303,5,304,2016-12-16 02:20:26,http://bashirian.net/scot.hoppe,0.0000000000,11.9.116.213,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n3304,5,304,2017-03-07 08:14:42,http://smitham.name/dallas,0.0000000000,184.202.245.135,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n3305,5,304,2017-05-28 21:44:19,http://kutch.net/lindsey,0.0000000000,188.65.162.58,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n3306,5,304,2017-04-17 13:00:34,http://daugherty.biz/cristian.stroman,0.0000000000,34.230.78.35,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n3307,5,304,2016-12-16 08:37:14,http://littelvandervort.io/glen.nicolas,0.0000000000,218.32.46.141,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n3308,5,304,2017-03-19 00:03:04,http://effertz.io/sean,0.0000000000,64.61.241.81,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n3309,5,304,2017-04-01 00:59:36,http://brakus.org/odell_reichert,0.0000000000,64.61.241.81,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n3310,5,304,2017-04-08 22:05:19,http://jakubowski.com/noemi,0.0000000000,101.153.214.157,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n3311,5,304,2017-01-03 18:44:10,http://hagenesbailey.info/aimee,0.0000000000,246.191.60.228,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n3312,5,304,2017-05-11 19:10:45,http://pollich.io/corrine,0.0000000000,237.137.80.136,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n3313,5,304,2017-05-25 13:19:58,http://jacobchmitt.co/tillman,0.0000000000,213.80.60.137,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n3314,5,304,2017-01-20 15:40:53,http://heaneycollier.info/willa_larkin,0.0000000000,184.202.245.135,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n3315,5,304,2017-01-29 16:09:35,http://towne.name/shad_yundt,0.0000000000,101.162.148.197,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n3316,5,305,2017-05-12 17:26:54,http://feest.co/caitlyn_aufderhar,0.0000000000,47.233.43.59,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n3317,5,305,2017-06-04 07:45:05,http://oberbrunnerschaden.io/allison,0.0000000000,104.191.162.248,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n3318,5,305,2017-03-12 04:34:13,http://kuhn.biz/karolann,0.0000000000,253.136.40.228,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n3319,5,305,2017-02-22 08:43:58,http://gibsonthiel.net/malinda_reilly,0.0000000000,149.62.50.236,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n3320,5,305,2017-01-10 00:33:21,http://nicolaswuckert.com/heloise,0.0000000000,211.220.134.254,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n3321,5,305,2017-04-04 18:31:41,http://schulist.io/tanya_macejkovic,0.0000000000,30.155.10.163,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n3322,5,305,2017-05-19 02:54:38,http://kutch.org/kaycee_hettinger,0.0000000000,104.191.162.248,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n3323,5,305,2017-03-25 10:10:58,http://mcdermott.biz/barrett_bradtke,0.0000000000,168.162.91.28,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n3324,5,305,2017-05-13 12:02:55,http://beahan.com/delores,0.0000000000,93.204.193.142,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n3325,5,305,2017-04-13 04:04:03,http://boscomoriette.name/judah,0.0000000000,247.215.201.114,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n3326,5,305,2017-03-20 08:43:25,http://altenwerth.info/alejandrin.white,0.0000000000,219.12.25.143,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n3327,5,305,2017-05-21 18:03:04,http://schowalter.co/annie.quigley,0.0000000000,30.155.10.163,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n3328,5,305,2017-04-03 23:54:28,http://sporer.io/jovany,0.0000000000,43.97.73.171,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n3329,5,305,2017-01-17 23:03:46,http://stehrhaley.info/reinhold,0.0000000000,160.136.222.206,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n3330,5,305,2017-05-22 17:41:18,http://fritsch.net/dalton_wiegand,0.0000000000,149.62.50.236,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n3331,5,305,2017-03-10 04:52:21,http://mayert.com/destini,0.0000000000,211.220.134.254,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n3332,5,305,2017-01-19 01:23:13,http://schiller.info/maymie,0.0000000000,158.15.137.153,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n3333,5,305,2017-05-31 22:08:06,http://kuhlmanlittle.org/jedidiah.jast,0.0000000000,247.215.201.114,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n3334,5,305,2017-02-02 17:04:44,http://torpkiehn.biz/catherine.miller,0.0000000000,208.40.239.144,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n3335,5,306,2017-05-13 21:29:16,http://osinski.name/ruthe.cronin,2.0000000000,174.164.91.183,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n3336,5,306,2017-02-05 17:27:09,http://smithamgoyette.org/van,1.0000000000,228.240.120.86,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n3337,5,306,2017-01-16 09:56:14,http://torphybartoletti.org/daisy,0.0000000000,25.146.250.101,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n3338,5,306,2017-03-08 10:29:31,http://swiftwaelchi.name/elsa,0.0000000000,174.164.91.183,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n3339,5,306,2017-02-27 17:46:01,http://wuckert.io/kayla,0.0000000000,181.46.209.17,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n3340,5,306,2017-03-07 04:44:32,http://williamson.name/xander,1.0000000000,40.24.36.133,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n3341,5,306,2017-05-24 09:43:19,http://kunze.com/stanley_upton,0.0000000000,109.187.30.240,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n3342,5,306,2017-03-06 04:05:35,http://barrowsbednar.com/jennifer,1.0000000000,25.146.250.101,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n3343,5,306,2017-03-04 21:53:56,http://kuhn.org/merle_stanton,2.0000000000,127.95.203.24,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n3344,5,306,2017-06-12 07:09:14,http://walsh.net/margaretta,0.0000000000,2.211.150.59,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n3345,5,306,2017-02-08 19:37:40,http://erdman.info/maude,2.0000000000,104.242.137.169,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n3346,5,306,2017-04-15 11:59:21,http://kulasborer.name/keon,2.0000000000,130.213.115.198,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n3347,5,306,2017-02-07 05:41:59,http://pollichledner.name/milo,0.0000000000,181.46.209.17,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n3348,5,306,2017-05-23 17:12:18,http://gulgowski.name/marguerite,2.0000000000,164.139.252.151,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n3349,5,306,2016-12-17 22:08:27,http://funkmcglynn.com/zachariah,1.0000000000,2.211.150.59,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n3350,5,306,2017-04-17 06:33:24,http://zemlak.info/therese,1.0000000000,2.211.150.59,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n3351,5,306,2016-12-24 12:46:31,http://huelmoen.io/harvey.farrell,1.0000000000,181.46.209.17,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n3352,5,307,2017-06-13 06:48:03,http://moriettestroman.biz/nichole,0.0000000000,113.17.225.153,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n3353,5,307,2017-01-04 08:49:50,http://donnelly.info/kristina.bayer,0.0000000000,93.89.193.243,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n3354,5,307,2017-02-18 16:56:37,http://stoltenberg.com/bernard.gottlieb,0.0000000000,72.46.29.169,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n3355,5,307,2017-02-26 17:36:34,http://thiel.net/rickey_goyette,0.0000000000,134.49.93.203,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n3356,5,307,2017-01-15 03:57:15,http://kochullrich.com/jayson.brakus,0.0000000000,155.66.94.57,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n3357,5,307,2017-02-05 18:53:31,http://schuster.com/joshua,0.0000000000,107.93.173.70,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n3358,5,307,2017-01-20 12:27:10,http://ritchie.co/delaney.gorczany,0.0000000000,247.19.75.205,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n3359,5,307,2017-01-16 09:15:33,http://mayert.com/simone,0.0000000000,22.102.127.253,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n3360,5,307,2017-05-28 09:44:14,http://muller.biz/jacey.johns,0.0000000000,133.77.147.34,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n3361,5,307,2017-03-21 18:40:48,http://jakubowski.name/sister,0.0000000000,110.5.192.241,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n3362,5,307,2017-01-09 05:31:10,http://schuppe.biz/gertrude_bruen,0.0000000000,45.25.183.83,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n3363,5,307,2017-06-03 23:54:37,http://howell.info/rusty,0.0000000000,134.49.93.203,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n3364,5,307,2017-02-06 08:16:16,http://kohler.com/lupe.roberts,0.0000000000,159.240.113.78,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n3365,5,307,2016-12-22 13:32:48,http://schinner.io/ryann,0.0000000000,232.185.47.39,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n3366,5,307,2017-05-30 21:20:01,http://schumm.biz/lilian_nienow,0.0000000000,134.49.93.203,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n3367,5,307,2017-03-25 07:40:31,http://schaefer.name/harley,0.0000000000,114.141.228.158,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n3368,5,307,2017-01-04 12:30:56,http://stoltenberghickle.name/daphnee_mayer,0.0000000000,36.56.188.201,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n3369,5,308,2017-05-14 22:34:34,http://wolf.co/chaim,0.0000000000,148.24.51.124,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n3370,5,308,2017-05-08 12:52:28,http://lang.org/doris_farrell,0.0000000000,254.105.44.38,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n3371,5,308,2017-03-01 21:35:43,http://bashirian.org/deron,0.0000000000,223.79.155.232,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n3372,5,308,2017-06-08 02:48:22,http://reichertcain.info/cordelia,0.0000000000,152.202.49.34,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n3373,5,308,2017-03-20 08:52:09,http://renner.co/eleazar.okeefe,0.0000000000,158.176.35.222,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n3374,5,308,2017-06-05 19:38:24,http://medhurstgreen.io/montana,0.0000000000,227.248.110.53,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n3375,5,308,2017-04-30 17:49:24,http://okunevagutmann.org/colt,0.0000000000,206.141.43.196,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n3376,5,308,2016-12-24 16:24:18,http://beierlesch.co/elvie,0.0000000000,219.90.113.55,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n3377,5,308,2017-05-21 05:26:20,http://whiteschimmel.info/dewitt.greenholt,0.0000000000,204.204.35.248,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n3378,5,308,2017-02-09 02:13:24,http://kirlin.io/dasia,0.0000000000,225.100.252.128,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n3379,5,308,2017-02-03 08:25:35,http://bergstrom.name/maudie.kirlin,0.0000000000,29.227.13.109,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n3380,5,308,2017-03-23 14:14:29,http://tromp.name/jadon_schultz,0.0000000000,220.114.164.237,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n3381,5,308,2017-04-16 05:59:36,http://schultz.co/sophie,0.0000000000,180.83.128.230,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n3382,5,309,2017-03-30 08:14:46,http://olsonjast.org/nannie.oreilly,0.0000000000,7.2.97.100,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n3383,5,310,2016-12-20 07:47:57,http://mullergrant.com/jayda,0.0000000000,227.213.134.43,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n3384,5,310,2017-05-24 21:33:43,http://gloverskiles.org/ciara,0.0000000000,11.161.42.201,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n3385,5,310,2016-12-20 03:11:11,http://treutel.name/nolan_gerlach,0.0000000000,136.22.110.68,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n3386,5,310,2017-04-13 13:00:34,http://muellerbailey.io/jimmie_kuphal,0.0000000000,227.101.43.131,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n3387,5,310,2017-04-02 08:10:54,http://bartellkonopelski.name/rogers,0.0000000000,45.31.8.15,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n3388,5,310,2017-03-28 17:19:50,http://krajcikko.io/nick.lemke,0.0000000000,227.101.43.131,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n3389,5,310,2017-03-03 14:48:32,http://dubuque.com/alphonso,0.0000000000,42.155.137.230,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n3390,5,310,2017-05-01 09:51:37,http://dietrichswift.net/margie,0.0000000000,141.142.252.93,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n3391,5,310,2016-12-28 00:04:07,http://schamberger.info/penelope,0.0000000000,52.169.55.212,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n3392,5,310,2017-03-22 17:57:35,http://fadel.org/shemar_feil,0.0000000000,243.236.167.116,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n3393,5,310,2017-04-17 05:40:16,http://weinatbode.com/parker_walsh,0.0000000000,185.228.193.157,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n3394,5,310,2017-03-28 15:15:11,http://beckerokeefe.co/mariana,0.0000000000,99.106.25.162,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n3395,5,310,2017-01-11 10:35:02,http://stoltenbergboyer.com/kaylah.mosciski,0.0000000000,45.56.150.52,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n3396,5,310,2017-01-09 20:57:53,http://hettinger.net/tommie_zulauf,0.0000000000,91.5.242.158,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n3397,5,310,2017-05-26 20:04:28,http://pagac.info/rachelle.schimmel,0.0000000000,227.101.43.131,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n3398,5,310,2017-06-06 18:13:12,http://treutellesch.com/gaylord.green,0.0000000000,99.106.25.162,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n3399,5,311,2017-01-20 22:01:51,http://barton.org/nettie_collier,0.0000000000,210.198.231.42,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n3400,5,311,2017-05-21 00:41:27,http://abbott.name/georgianna,0.0000000000,74.214.64.66,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n3401,5,311,2017-03-19 22:25:41,http://dietrich.io/laurie,0.0000000000,150.65.136.97,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n3402,5,312,2017-05-22 09:30:27,http://schamberger.info/terrell,0.0000000000,215.53.222.204,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n3403,5,312,2017-01-11 00:41:28,http://rippin.co/clovis.legros,0.0000000000,159.189.66.122,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n3404,5,312,2017-03-13 20:51:57,http://jones.net/shad_reichel,0.0000000000,148.117.17.109,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n3405,5,312,2017-01-13 05:08:15,http://dach.org/haie.nolan,0.0000000000,201.116.228.245,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n3406,5,312,2017-02-08 03:56:32,http://cummerata.co/penelope_stark,0.0000000000,5.84.89.45,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n3407,5,312,2017-03-12 06:04:00,http://hamill.io/edythe_jacobs,0.0000000000,232.87.160.149,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n3408,5,312,2017-01-19 01:40:09,http://heidenreichbahringer.info/gilbert,0.0000000000,167.164.54.165,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n3409,5,312,2017-06-13 09:48:25,http://bashirianritchie.org/allene,0.0000000000,180.141.207.73,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n3410,5,312,2017-03-28 07:05:22,http://feest.info/kenyon,0.0000000000,24.228.214.17,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n3411,5,313,2017-06-04 23:11:02,http://morar.co/dejah.swift,1.0000000000,251.148.250.17,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n3412,5,313,2017-02-02 20:53:54,http://oconnell.biz/marques,1.0000000000,114.126.99.3,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n3413,5,313,2017-04-09 05:32:43,http://lockman.org/concepcion_leannon,1.0000000000,89.21.221.233,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n3414,5,313,2017-04-11 07:39:47,http://thompson.net/broderick.hoppe,0.0000000000,153.207.2.207,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n3415,5,313,2017-05-06 06:04:50,http://beier.org/nadia,0.0000000000,120.99.143.40,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n3416,5,313,2017-03-26 18:08:36,http://strosincrona.com/raven,1.0000000000,11.215.86.248,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n3417,5,313,2017-02-23 08:46:16,http://yundt.com/lenora_ferry,1.0000000000,7.46.251.158,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n3418,5,313,2017-01-14 03:03:24,http://jaskolski.biz/graham.walker,0.0000000000,151.236.105.104,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n3419,5,313,2017-03-14 17:55:58,http://christiansensteuber.info/florida,0.0000000000,168.248.197.4,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n3420,5,313,2017-01-20 22:53:54,http://keeling.name/matilde.rau,1.0000000000,83.74.88.254,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n3421,5,313,2017-02-20 19:15:23,http://schroeder.io/terrence,0.0000000000,165.112.226.194,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n3422,5,313,2017-01-09 06:13:19,http://medhurstdonnelly.name/merl_waelchi,1.0000000000,198.151.165.211,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n3423,5,313,2017-02-07 18:10:36,http://rosenbaum.co/davin,1.0000000000,124.211.252.187,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n3424,5,313,2017-03-01 18:36:20,http://powlowskigoldner.com/cindy.jacobi,0.0000000000,89.21.221.233,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n3425,5,313,2017-03-26 01:23:08,http://pagac.org/angelina.roberts,0.0000000000,91.101.65.54,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n3426,5,313,2017-04-12 19:40:41,http://kerlukerippin.io/deja,1.0000000000,9.188.200.241,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n3427,5,313,2017-04-06 08:36:48,http://lowe.co/coby.balistreri,1.0000000000,174.62.79.230,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n3428,5,313,2017-01-27 03:31:37,http://thiel.net/quinten_willms,0.0000000000,214.108.223.144,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n3429,5,313,2017-04-27 13:35:56,http://stoltenberg.io/michele.romaguera,0.0000000000,153.207.2.207,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n3430,5,314,2016-12-21 22:34:18,http://botsford.com/duane.turcotte,1.0000000000,190.47.176.97,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n3431,5,314,2016-12-18 01:32:05,http://kuphal.info/edwin,1.0000000000,8.20.6.148,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n3432,5,314,2017-03-04 12:51:45,http://hegmannhahn.com/halle,1.0000000000,72.150.130.150,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n3433,5,314,2017-03-27 18:00:39,http://schmeler.co/walker.becker,2.0000000000,13.108.251.64,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n3434,5,314,2017-02-11 11:28:30,http://hilpert.biz/narciso,0.0000000000,13.108.251.64,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n3435,5,314,2017-02-27 23:30:09,http://hilll.io/naomie,1.0000000000,13.108.251.64,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n3436,5,314,2017-01-09 15:45:49,http://millsblanda.com/kade,3.0000000000,147.218.59.65,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n3437,5,314,2017-03-21 08:45:03,http://weimannbruen.info/rashad,3.0000000000,139.60.201.217,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n3438,5,314,2017-03-15 20:44:55,http://kovacekkuhlman.org/romaine,2.0000000000,136.213.27.58,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n3439,5,314,2017-06-03 22:47:25,http://ornupton.com/zora_marvin,3.0000000000,59.107.153.121,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n3440,5,314,2017-05-29 07:00:22,http://schultzrobel.biz/lia_hagenes,2.0000000000,249.167.83.42,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n3441,5,314,2017-02-24 04:54:00,http://beckerlakin.info/bret,1.0000000000,239.19.132.245,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n3442,5,314,2017-05-16 04:43:06,http://tromp.net/malinda.kuhlman,0.0000000000,166.14.40.136,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n3443,5,314,2017-05-29 08:34:22,http://cummerataweber.info/ola,1.0000000000,101.217.133.204,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n3444,5,314,2017-05-21 13:53:31,http://murray.name/danial,3.0000000000,41.176.249.59,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n3445,5,314,2017-02-27 06:17:24,http://schaefer.info/clint,2.0000000000,8.20.6.148,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n3446,5,314,2017-05-12 05:24:08,http://christiansenfeil.org/pascale,3.0000000000,121.115.171.224,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n3447,5,315,2017-03-30 21:07:23,http://sipeszieme.io/pedro_koepp,0.0000000000,230.254.10.203,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n3448,5,315,2017-02-12 19:45:32,http://hudsondamore.info/lilyan,3.0000000000,96.43.98.162,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n3449,5,315,2017-06-01 09:11:26,http://marquardtlowe.io/elsie,3.0000000000,197.45.140.235,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n3450,5,315,2017-03-18 20:22:11,http://homenick.biz/nikolas_champlin,3.0000000000,185.135.147.33,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n3451,5,315,2017-02-25 21:42:22,http://adams.co/lennie_moen,1.0000000000,226.163.156.168,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n3452,5,315,2017-04-18 14:08:26,http://torphy.org/shany,3.0000000000,242.20.250.229,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n3453,5,315,2017-01-10 15:09:34,http://turcotteherman.biz/cedrick,3.0000000000,63.9.169.102,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n3454,5,315,2017-06-05 03:25:12,http://cronin.name/malika,3.0000000000,172.119.40.158,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n3455,5,315,2017-04-09 02:08:36,http://bergstrom.info/burley,3.0000000000,226.163.156.168,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n3456,5,315,2017-05-27 13:11:03,http://goyettemoen.io/nicholas_connelly,3.0000000000,110.68.18.230,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n3457,5,315,2017-01-08 11:01:51,http://jaskolski.info/randi_king,1.0000000000,235.28.86.7,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n3458,5,315,2017-01-14 23:13:18,http://lowe.info/oceane.adams,0.0000000000,235.28.86.7,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n3459,5,315,2017-03-28 12:01:23,http://hamill.net/stefan_miller,0.0000000000,54.142.12.188,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n3460,5,316,2017-05-25 09:09:02,http://rohan.net/elia,0.0000000000,36.122.153.102,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n3461,5,316,2017-05-23 13:58:28,http://bosco.net/guy_klein,0.0000000000,218.145.100.126,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n3462,5,316,2017-05-14 09:11:10,http://hageneswindler.com/jevon.heel,0.0000000000,197.45.159.235,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n3463,5,316,2017-02-20 09:09:24,http://mcglynn.net/destin_nikolaus,0.0000000000,213.91.174.54,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n3464,5,316,2017-04-17 06:49:07,http://howe.co/oral,0.0000000000,70.156.24.65,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n3465,5,316,2017-01-08 06:36:35,http://daniel.co/zoe.denesik,1.0000000000,117.105.28.138,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n3466,5,316,2017-01-20 11:50:15,http://feil.name/maynard,0.0000000000,238.13.57.49,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n3467,5,316,2017-04-04 23:12:46,http://gottliebferry.biz/letha_homenick,1.0000000000,212.166.19.135,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n3468,5,316,2017-05-03 02:44:26,http://walterrippin.com/sydnee,1.0000000000,32.102.174.241,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n3469,5,316,2017-05-11 13:18:12,http://adams.com/ro,0.0000000000,225.76.245.47,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n3470,5,316,2017-01-17 01:22:40,http://renner.co/ardith_gerlach,0.0000000000,52.227.91.7,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n3471,5,316,2017-04-19 23:51:46,http://considine.com/hope.eichmann,1.0000000000,52.227.91.7,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n3472,5,316,2017-01-13 15:19:14,http://morar.io/israel_homenick,1.0000000000,142.30.243.30,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n3473,5,316,2016-12-23 09:11:39,http://conroy.co/retta.gleason,1.0000000000,62.94.180.175,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n3474,5,317,2017-03-03 15:44:53,http://funkrau.biz/edna_lakin,2.0000000000,158.211.84.60,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n3475,5,317,2017-02-10 08:14:22,http://smithambrown.com/tania_vandervort,0.0000000000,163.152.26.220,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n3476,5,317,2016-12-24 03:29:10,http://swaniawski.biz/aidan,3.0000000000,157.197.251.207,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n3477,5,317,2017-04-25 13:59:21,http://padberggoodwin.info/emmet,1.0000000000,116.173.103.71,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n3478,5,317,2017-01-04 09:31:53,http://ohara.org/manuela_lowe,1.0000000000,167.135.111.144,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n3479,5,317,2016-12-31 06:37:43,http://rogahn.com/jacey,1.0000000000,83.97.127.45,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n3480,5,317,2017-05-08 23:11:27,http://wilderman.biz/hermina.towne,3.0000000000,97.226.188.198,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n3481,5,317,2017-03-07 14:05:15,http://smitham.info/melyna,3.0000000000,167.135.111.144,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n3482,5,317,2017-01-30 17:18:30,http://collintamm.com/estella_cummerata,1.0000000000,73.40.107.124,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n3483,5,317,2017-01-08 16:46:36,http://macejkovicschimmel.org/angelo_hammes,2.0000000000,163.152.26.220,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n3484,5,317,2017-03-16 11:14:30,http://goyetteoreilly.com/dedrick,1.0000000000,97.226.188.198,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n3485,5,317,2016-12-25 16:08:11,http://kihn.co/jody.terry,1.0000000000,204.199.164.38,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n3486,5,317,2017-04-23 04:21:05,http://shanahan.co/zoila,1.0000000000,194.243.123.33,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n3487,5,317,2017-04-21 05:11:56,http://schowalterkshlerin.info/lee.orn,1.0000000000,5.29.64.238,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n3488,5,317,2017-01-21 22:21:03,http://aufderhar.org/marquise_johns,0.0000000000,182.39.69.14,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n3489,5,318,2017-06-10 05:39:13,http://breitenbergmayer.biz/ricardo_gleichner,1.0000000000,71.96.82.115,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n3490,5,318,2017-03-25 05:17:18,http://bruenward.io/rowena_greenfelder,0.0000000000,136.151.76.102,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n3491,5,318,2017-02-12 02:09:52,http://jaskolski.io/rebeca,1.0000000000,52.38.161.120,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n3492,5,318,2017-06-03 09:51:41,http://schumm.co/everette,2.0000000000,51.237.144.179,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n3493,5,318,2017-01-19 14:18:56,http://nienow.biz/lucinda,3.0000000000,30.133.76.110,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n3494,5,318,2017-01-22 23:27:50,http://quitzon.biz/louvenia.williamson,2.0000000000,10.66.124.168,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n3495,5,318,2017-02-28 14:20:12,http://schaden.name/lenore.pfeffer,3.0000000000,230.17.191.75,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n3496,5,318,2017-01-03 03:52:17,http://moensatterfield.net/eli_hansen,2.0000000000,103.79.6.118,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n3497,5,318,2017-03-13 04:10:08,http://cartwright.io/jakob,1.0000000000,196.222.242.5,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n3498,5,318,2017-06-12 01:08:09,http://abshire.org/rudy.predovic,1.0000000000,10.66.124.168,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n3499,5,318,2017-02-01 06:27:14,http://oconnerkihn.name/lelah,3.0000000000,204.4.158.143,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n3500,5,318,2017-03-29 10:11:59,http://gutmann.co/donato_crist,1.0000000000,10.66.124.168,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n3501,5,318,2017-03-12 03:25:51,http://ruel.org/vaughn.corwin,0.0000000000,154.81.253.105,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n3502,5,318,2016-12-24 02:10:32,http://christiansen.org/annabel_morar,3.0000000000,72.90.192.14,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n3503,5,318,2017-02-01 05:33:57,http://schimmelskiles.io/ola,2.0000000000,32.78.26.117,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n3504,5,318,2017-05-14 00:49:45,http://fisher.name/orrin,3.0000000000,103.79.6.118,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n3505,5,319,2017-01-30 11:51:43,http://erdman.io/johnathon,2.0000000000,171.108.16.138,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n3506,5,319,2017-03-25 07:07:47,http://zieme.io/warren,2.0000000000,95.148.123.98,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n3507,5,319,2017-01-06 04:20:03,http://friesenlockman.io/sedrick,0.0000000000,222.46.137.219,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n3508,5,319,2017-04-16 14:26:25,http://oconnell.biz/sofia,0.0000000000,192.159.12.61,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n3509,5,319,2016-12-30 18:44:44,http://okeefe.biz/idella_leannon,1.0000000000,205.231.253.17,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n3510,5,319,2017-02-08 12:14:07,http://willmccullough.net/emelia.leffler,0.0000000000,97.247.97.40,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n3511,5,319,2017-04-20 17:19:24,http://ankundingkuhlman.biz/hilbert,1.0000000000,171.108.16.138,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n3512,5,319,2017-03-19 05:01:43,http://lubowitz.info/jacynthe_schulist,2.0000000000,205.231.253.17,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n3513,5,320,2017-05-03 20:45:16,http://doyle.com/omer,2.0000000000,25.52.234.72,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n3514,5,320,2017-06-02 04:27:21,http://okunevaziemann.info/ned,0.0000000000,47.241.221.45,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n3515,5,320,2017-05-03 15:41:51,http://krajcik.biz/violet_tremblay,2.0000000000,45.126.231.7,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n3516,5,320,2016-12-18 08:24:14,http://parisianryan.io/domingo.mckenzie,1.0000000000,151.159.73.50,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n3517,5,320,2017-04-13 04:13:06,http://jacobi.co/kristian,2.0000000000,17.144.98.113,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n3518,5,320,2017-05-23 15:07:52,http://lueilwitzwunsch.io/jefferey.mueller,2.0000000000,47.241.221.45,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n3519,5,320,2017-05-17 19:18:06,http://herzog.io/domenick_wunsch,2.0000000000,116.59.242.226,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n3520,5,320,2017-06-06 16:54:55,http://schuppe.org/kadin,2.0000000000,200.156.216.27,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n3521,5,320,2017-01-26 06:38:07,http://funk.io/peyton,0.0000000000,8.91.110.94,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n3522,5,320,2017-03-09 20:17:20,http://herzog.biz/javier,0.0000000000,240.6.157.11,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n3523,5,320,2017-04-02 06:07:38,http://harris.name/reymundo,0.0000000000,89.11.248.232,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n3524,5,320,2017-01-03 23:18:42,http://coleschulist.com/eugene,2.0000000000,28.16.17.127,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n3525,5,320,2017-01-09 07:07:27,http://feil.com/pink,0.0000000000,28.16.17.127,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n3526,5,320,2017-03-30 05:47:51,http://ohara.info/moriah.mayer,2.0000000000,35.56.243.154,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n3527,5,320,2017-04-08 15:25:21,http://kihn.io/jonatan,2.0000000000,121.153.172.240,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n3528,5,320,2017-01-23 18:49:08,http://haley.name/demetrius_sporer,1.0000000000,75.194.196.139,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n3529,5,320,2016-12-17 23:52:47,http://schumm.net/bridgette,2.0000000000,125.37.235.183,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n3530,5,320,2017-02-26 00:26:46,http://kemmer.biz/gustave.turner,1.0000000000,246.243.181.204,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n3531,5,321,2017-05-01 03:56:41,http://thiel.info/emanuel_price,1.0000000000,191.24.184.80,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n3532,5,321,2017-01-28 14:59:17,http://hamill.org/fay_stanton,0.0000000000,105.156.91.206,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n3533,5,321,2017-05-11 23:51:56,http://shieldskrajcik.info/kristian.harris,2.0000000000,148.90.189.30,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n3534,5,321,2016-12-20 00:23:36,http://hoppe.io/earl_langworth,0.0000000000,169.150.235.18,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n3535,5,322,2017-02-16 01:56:12,http://greenholt.org/candida_harber,2.0000000000,197.171.26.224,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n3536,5,323,2017-03-04 19:00:37,http://hermiston.biz/marlon.bruen,0.0000000000,82.17.217.232,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n3537,5,323,2017-05-03 22:29:13,http://terry.info/adonis,1.0000000000,7.196.153.22,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n3538,5,323,2017-01-22 03:15:27,http://johns.co/alize_blanda,1.0000000000,143.185.13.169,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n3539,5,323,2017-02-03 09:51:06,http://herman.name/murphy.thompson,1.0000000000,173.177.63.224,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n3540,5,323,2017-02-17 06:51:33,http://gusikowskieichmann.co/angie_weimann,2.0000000000,216.174.122.210,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n3541,5,324,2017-04-05 15:59:57,http://dietrichschamberger.info/earnestine.mayert,1.0000000000,94.23.114.132,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n3542,5,324,2017-01-31 08:03:26,http://maggio.info/alysa,0.0000000000,104.36.108.185,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n3543,5,324,2016-12-28 00:15:53,http://bergstromkeeling.net/meredith,0.0000000000,77.145.35.85,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n3544,5,324,2017-04-16 04:44:07,http://pouros.biz/mckayla,0.0000000000,243.6.72.201,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n3545,5,324,2017-01-10 19:32:56,http://von.com/gaetano_fahey,0.0000000000,216.126.118.30,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n3546,5,324,2017-02-05 21:42:33,http://sawayn.co/erich_zemlak,1.0000000000,40.197.166.140,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n3547,5,324,2017-05-13 04:56:43,http://farrell.name/easton_johns,0.0000000000,110.51.42.217,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n3548,5,324,2017-01-31 05:54:57,http://pagachammes.io/mya_damore,1.0000000000,94.23.114.132,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n3549,5,324,2017-06-12 12:49:49,http://abbott.io/vida.koch,1.0000000000,251.93.157.251,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n3550,5,324,2017-01-22 15:09:44,http://sporerquitzon.com/kaley.rolfson,0.0000000000,46.177.14.15,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n3551,5,325,2017-06-12 14:32:04,http://mrazprosacco.co/crystel,2.0000000000,230.120.91.128,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n3552,5,325,2016-12-14 00:03:59,http://lebsackpouros.io/isadore.schultz,1.0000000000,31.11.57.59,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n3553,5,325,2016-12-16 06:39:14,http://gorczany.info/esther.rowe,2.0000000000,187.169.33.62,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n3554,5,325,2017-04-11 07:34:50,http://lowe.name/robin,1.0000000000,194.137.88.108,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n3555,5,325,2017-06-11 14:56:01,http://pollich.name/arnoldo_raynor,2.0000000000,102.127.58.156,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n3556,5,325,2017-03-21 08:45:45,http://johnston.co/izabella_grady,0.0000000000,45.147.168.211,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n3557,5,325,2017-04-29 20:14:33,http://ratkemckenzie.io/dee,0.0000000000,87.30.57.103,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n3558,5,326,2016-12-19 13:25:07,http://mante.com/lyla,1.0000000000,181.181.104.72,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n3559,5,326,2017-04-12 04:15:00,http://schneider.info/arden,0.0000000000,58.159.119.232,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n3560,5,326,2017-01-13 20:55:40,http://mckenziereichel.info/jeromy_rutherford,2.0000000000,217.237.169.29,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n3561,5,326,2017-05-12 01:22:51,http://conn.co/grayson.stiedemann,2.0000000000,27.77.134.103,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n3562,5,326,2017-04-23 19:45:34,http://kirlinstrosin.biz/thaddeus.wiza,2.0000000000,30.121.94.125,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n3563,5,326,2017-03-31 09:16:43,http://feeney.net/hellen,2.0000000000,239.178.218.171,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n3564,5,326,2017-01-21 03:59:44,http://weimannrolfson.org/brandyn,2.0000000000,27.77.134.103,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n3565,5,326,2017-05-29 17:50:45,http://gulgowski.com/enrique,1.0000000000,181.181.104.72,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n3566,5,326,2017-06-07 11:47:30,http://weinatschimmel.org/dovie,1.0000000000,63.206.18.140,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n3567,5,326,2017-01-09 18:08:56,http://kirlin.net/wilfrid.lindgren,3.0000000000,58.159.119.232,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n3568,5,326,2017-02-13 12:04:48,http://willms.org/mattie_jacobs,2.0000000000,95.203.126.55,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n3569,5,326,2017-04-30 06:46:13,http://balistreri.io/tevin,3.0000000000,19.112.7.211,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n3570,5,326,2017-01-15 09:30:58,http://schmitt.biz/lavada_morar,1.0000000000,228.147.115.203,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n3571,5,327,2017-01-28 17:40:54,http://friesenrunte.net/leann,,133.180.9.101,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n3572,5,327,2017-01-11 06:24:47,http://weber.org/charlotte_parisian,,19.199.36.244,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n3573,5,327,2017-02-08 18:08:45,http://medhurstcrooks.biz/marjory,,69.123.134.76,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n3574,5,328,2017-04-07 05:37:35,http://kuvalichaefer.biz/willow.barrows,,151.104.240.45,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n3575,5,328,2017-02-03 19:58:16,http://murray.io/deie_carter,,244.41.31.51,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n3576,5,328,2017-05-26 00:18:40,http://hills.net/vivien,,213.139.140.231,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n3577,5,328,2017-04-04 01:59:22,http://parker.org/riley.hartmann,,166.125.82.133,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n3578,5,328,2017-03-18 17:06:20,http://wymanjacobson.co/luigi_crist,,169.71.105.103,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n3579,5,328,2016-12-26 21:17:15,http://lakin.info/wilton,,165.143.28.172,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n3580,5,328,2017-05-01 00:14:10,http://hansen.org/meghan,,220.32.50.133,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n3581,5,328,2017-01-05 13:12:18,http://schuster.biz/enoch_franecki,,88.135.48.85,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n3582,5,328,2017-04-03 10:28:39,http://schinner.name/nicolette,,169.71.105.103,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n3583,5,328,2017-05-07 03:54:50,http://rosenbaum.info/anabel_koelpin,,188.248.163.143,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n3584,5,328,2017-05-17 22:53:03,http://turnerpowlowski.co/alycia,,2.172.63.126,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n3585,5,328,2016-12-18 20:45:07,http://tremblay.io/kariane,,27.197.2.81,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n3586,5,328,2017-02-22 00:11:11,http://heller.co/chad,,5.244.61.52,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n3587,5,328,2017-02-03 09:23:37,http://purdy.org/eldora,,240.163.25.105,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n3588,5,328,2017-03-12 01:49:55,http://ruecker.info/jamarcus_kohler,,103.80.196.225,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n3589,5,328,2017-01-04 08:32:40,http://kuhic.net/cleo.cormier,,17.27.86.250,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n3590,5,328,2017-04-10 08:40:32,http://schulist.net/patrick.sawayn,,145.195.143.146,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n3591,5,328,2017-03-21 23:20:05,http://ryan.info/oswaldo.heller,,27.197.2.81,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n3592,5,328,2017-05-01 04:49:08,http://hilpert.io/gwen.mayer,,215.103.163.53,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n3593,5,328,2016-12-17 10:09:32,http://heaneydickens.info/cathy,,17.27.86.250,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n3594,5,329,2017-06-08 02:48:22,http://bodebrakus.com/casimer,,121.33.164.155,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n3595,5,329,2017-05-05 21:59:42,http://mills.net/joan_langworth,,210.214.206.202,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n3596,5,329,2016-12-23 15:28:02,http://gerlach.biz/ada,,36.226.23.80,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n3597,5,329,2017-03-19 10:46:45,http://boscostiedemann.name/rafael,,121.199.233.125,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n3598,5,329,2017-03-06 23:40:22,http://wisokybarrows.info/janie.bogisich,,113.120.216.11,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n3599,5,329,2017-02-04 11:15:22,http://feestmcclure.io/myrtis.medhurst,,89.91.186.114,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n3600,5,329,2017-04-08 06:42:08,http://wehnerokuneva.net/maybelle,,230.57.19.208,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n3601,5,329,2017-03-12 05:17:44,http://padberg.name/brian,,199.118.30.123,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n3602,5,329,2017-04-04 00:38:30,http://sipesgutkowski.biz/karolann,,202.189.70.149,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n3603,5,329,2016-12-31 00:58:22,http://ledner.org/rolando,,148.169.154.186,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n3604,5,330,2017-05-30 17:09:18,http://keebler.org/gaston,,213.199.174.78,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n3605,5,330,2017-02-07 12:27:33,http://smith.biz/libby,,230.215.104.2,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n3606,5,330,2017-06-01 15:58:22,http://boehm.biz/tyler,,213.199.174.78,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n3607,5,330,2017-03-02 12:51:19,http://gaylordgibson.info/jonathan,,213.199.174.78,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n3608,5,330,2017-02-19 01:44:25,http://reichel.name/ulises,,165.253.55.228,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n3609,5,330,2017-05-18 12:17:02,http://blanda.biz/geo.stark,,230.215.104.2,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n3610,5,330,2017-04-22 22:03:54,http://gradyklein.name/iac,,97.190.105.241,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n3611,5,330,2017-03-09 05:25:52,http://block.io/lila,,165.253.55.228,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n3612,5,330,2017-03-05 11:00:18,http://brown.name/rudy_hettinger,,18.149.156.121,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n3613,5,331,2017-04-16 00:28:16,http://senger.com/candido_walker,,217.229.57.148,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n3614,5,331,2017-04-01 08:44:09,http://ebert.io/chance_kohler,,56.13.46.20,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n3615,5,331,2017-06-04 03:30:30,http://willms.info/ayden,,205.49.160.216,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n3616,5,331,2017-04-15 06:54:54,http://kub.info/gilda_beier,,9.2.253.172,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n3617,5,331,2017-03-11 09:51:00,http://bailey.info/gustave,,157.253.189.113,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n3618,5,331,2017-03-11 11:48:32,http://schmidtlind.com/rosalinda_ortiz,,219.99.161.207,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n3619,5,331,2017-05-19 11:48:37,http://simonisokeefe.info/larue_schumm,,189.166.182.178,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n3620,5,331,2017-03-25 04:40:40,http://lemke.co/tiara_swaniawski,,62.120.90.73,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n3621,5,331,2017-01-26 07:30:41,http://heel.biz/brook,,47.3.74.83,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n3622,5,331,2017-05-10 04:01:00,http://sporer.co/vallie.keler,,56.69.238.83,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n3623,5,331,2017-04-26 17:58:45,http://brekke.biz/katelin,,184.8.211.169,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n3624,5,332,2017-06-04 03:37:55,http://padberg.org/isai_rosenbaum,,233.184.101.15,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n3625,5,332,2017-02-05 09:26:40,http://gottlieb.co/lyric.metz,,35.144.79.195,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n3626,5,332,2016-12-15 00:02:30,http://witting.net/clifton,,212.125.77.251,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n3627,5,332,2017-01-19 21:11:31,http://okongrady.net/citlalli,,51.23.169.170,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n3628,5,332,2017-06-10 19:49:34,http://veum.info/ara_bode,,2.127.65.248,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n3629,5,332,2017-03-16 17:33:30,http://koch.net/adella,,2.127.65.248,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n3630,5,332,2017-03-14 15:06:14,http://kutch.biz/justus_lueilwitz,,159.43.123.182,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n3631,5,333,2017-06-05 20:41:48,http://marquardt.com/alford.nolan,,16.171.192.72,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n3632,5,333,2017-06-12 12:52:24,http://schaeferbednar.org/elenor_moore,,209.133.109.169,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n3633,5,333,2016-12-30 06:37:38,http://rutherford.org/jeffery,,163.84.95.34,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n3634,5,333,2017-01-12 23:56:01,http://stiedemannstrosin.name/fred.sanford,,45.193.206.106,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n3635,5,333,2017-02-26 01:31:54,http://emmerichspinka.com/kyler,,191.35.104.19,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n3636,5,333,2017-05-29 20:00:01,http://satterfield.name/adolf,,139.44.2.22,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n3637,5,333,2017-04-01 16:23:10,http://cormierhickle.io/earl.lubowitz,,96.173.131.44,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n3638,5,333,2016-12-23 21:58:05,http://gerlach.biz/noe,,50.229.229.20,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n3639,5,334,2017-02-19 05:51:03,http://ledner.co/josh.ohara,,215.191.90.86,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n3640,5,334,2016-12-28 16:58:47,http://mckenzie.io/haven.mueller,,168.203.249.241,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n3641,5,334,2016-12-22 23:35:39,http://friesencrooks.org/destiny.rice,,195.30.231.237,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n3642,5,334,2017-01-03 23:30:06,http://wyman.co/estel,,43.189.95.77,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n3643,5,334,2017-05-10 02:12:25,http://gibsonhammes.info/emmett,,167.137.220.218,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n3644,5,334,2017-02-05 16:47:03,http://robertsolson.com/amely,,99.207.66.134,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n3645,5,334,2017-03-12 04:14:27,http://rolfsonemard.co/elias_watsica,,195.30.231.237,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n3646,5,334,2017-02-08 07:09:57,http://croninolson.co/caterina,,248.216.44.124,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n3647,5,334,2017-01-24 06:35:04,http://keeling.org/marcella,,99.207.66.134,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n3648,5,334,2017-01-20 16:23:54,http://conn.io/autumn_wintheiser,,163.184.161.23,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n3649,5,334,2017-01-13 00:56:50,http://bashirian.io/shanelle.carroll,,195.30.231.237,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n3650,5,334,2017-03-18 12:21:14,http://weber.biz/victoria,,19.184.230.27,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n3651,5,334,2017-05-18 15:33:11,http://schinnerlynch.co/fatima_armstrong,,180.75.132.57,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n3652,5,334,2017-03-31 11:58:31,http://casper.name/ilene_hintz,,61.123.204.241,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n3653,5,334,2016-12-14 10:27:50,http://greenkiehn.name/nils_maggio,,167.137.220.218,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n3654,5,334,2017-04-19 22:51:24,http://stoltenbergcasper.org/roel_johns,,167.137.220.218,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n3655,5,334,2016-12-13 17:02:52,http://quitzonherman.name/carmella.hudson,,163.184.161.23,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n3656,5,334,2017-06-08 19:12:50,http://heel.biz/emely,,11.85.204.213,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n3657,5,334,2017-04-05 23:27:56,http://manncarroll.info/danielle_borer,,149.102.182.30,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n3658,5,335,2017-03-21 14:40:43,http://mooreko.name/sibyl,,28.237.40.186,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n3659,5,335,2017-05-13 22:07:21,http://hirthe.io/krystal_wuckert,,218.37.157.171,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n3660,5,335,2017-04-13 05:00:54,http://abbottfeil.info/brett,,140.99.244.85,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n3661,5,335,2017-04-06 06:35:31,http://anderson.io/retha,,226.39.16.98,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n3662,5,335,2017-03-09 19:15:20,http://yostspencer.net/curtis_schultz,,140.99.244.85,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n3663,5,335,2017-02-06 12:00:04,http://klockowolf.net/mattie,,226.39.16.98,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n3664,5,335,2016-12-26 13:07:08,http://jakubowskiherman.net/aliyah_stark,,109.247.32.66,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n3665,5,335,2017-05-12 10:19:52,http://adams.info/justen,,134.147.49.114,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n3666,5,335,2017-05-26 13:02:25,http://borerfunk.org/macy_rempel,,70.71.2.114,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n3667,5,335,2017-06-03 15:05:42,http://dareadams.biz/camren,,134.147.49.114,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n3668,5,335,2017-06-11 21:35:53,http://keebler.biz/leda.metz,,49.171.90.166,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n3669,5,335,2017-03-29 00:32:49,http://labadie.com/eldon,,134.147.49.114,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n3670,5,335,2017-03-01 11:14:23,http://tillman.com/roberto_schroeder,,73.161.192.153,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n3671,5,335,2017-04-28 23:41:00,http://shields.co/mariah,,213.124.78.111,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n2084,4,195,2017-04-28 20:26:38,http://bins.org/velma.mcdermott,,238.14.207.110,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n2085,4,195,2017-02-09 08:28:27,http://heelwillms.biz/alvera,,88.233.168.141,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n2086,4,195,2017-03-13 05:28:12,http://colebeer.name/lulu_roob,,188.195.187.121,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n2087,4,195,2017-03-12 04:29:01,http://swaniawski.org/liliana,,92.62.116.82,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n2088,4,195,2017-05-16 12:36:26,http://cummings.info/sonia,,57.116.161.131,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n2089,4,195,2017-03-26 02:54:36,http://vandervort.biz/cameron,,37.112.254.131,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n2090,4,195,2017-01-07 14:54:59,http://fisher.org/walker_kilback,,250.247.83.99,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n2091,4,195,2017-05-17 10:29:36,http://moore.org/carmel.hand,,165.159.3.148,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n2092,4,195,2016-12-31 10:49:37,http://cormier.co/casimir_cremin,,243.246.226.152,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n2093,4,196,2017-02-16 00:41:56,http://kaulke.name/zoe_huels,,213.234.252.61,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n2094,4,196,2017-04-29 07:05:33,http://padbergheel.com/newton_bartoletti,,31.17.96.120,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n2095,4,196,2017-06-07 07:38:48,http://rempellowe.co/devan,,145.31.59.226,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n2096,4,196,2017-02-06 08:27:37,http://murphypurdy.name/margaretta_borer,,31.17.96.120,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n2097,4,197,2017-06-05 20:38:51,http://durgan.biz/harry_sawayn,,206.221.57.82,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n2098,4,197,2017-02-15 01:09:14,http://connelly.co/noemie.cummerata,,241.61.109.196,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n2099,4,197,2017-04-01 19:30:04,http://streich.io/nasir,,103.222.165.178,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n2100,4,197,2017-01-30 21:14:11,http://hickle.io/brooke,,230.254.236.236,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n2101,4,197,2017-04-21 01:54:53,http://balistreri.org/kendall,,228.245.225.43,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n2102,4,197,2017-01-01 12:31:44,http://murray.io/caroline,,33.149.53.106,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n2103,4,198,2017-04-12 05:51:24,http://klein.net/jordan_bechtelar,,91.200.210.215,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n2104,4,198,2017-05-31 13:09:38,http://shields.biz/wilson,,217.56.198.79,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n2105,4,198,2016-12-21 03:29:20,http://mills.org/winnifred,,146.189.109.114,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n2106,4,198,2017-04-08 07:39:56,http://macejkovic.biz/forest,,241.13.64.198,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n2107,4,198,2017-06-13 04:09:32,http://botsford.com/dameon.schuster,,64.112.110.232,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n2108,4,198,2017-05-05 19:38:27,http://roob.biz/bernice_beer,,9.246.5.210,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n2109,4,198,2016-12-30 05:23:24,http://vonrueden.info/libbie.sawayn,,46.147.247.254,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n2110,4,198,2017-06-04 23:56:06,http://murphy.co/agustina,,143.246.188.216,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n2111,4,198,2017-02-03 01:58:14,http://schiller.biz/curtis.lakin,,222.156.240.16,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n2112,4,198,2016-12-25 16:01:00,http://murray.info/laney_ortiz,,202.132.44.211,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n2113,4,198,2017-03-22 05:31:27,http://bruen.io/meggie,,132.70.114.73,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n2114,4,198,2017-05-07 11:56:48,http://kundefeil.net/otis_cronin,,218.96.245.187,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n2115,4,198,2017-01-02 03:07:36,http://johnston.name/jaden.dach,,217.56.198.79,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n2116,4,198,2017-01-25 10:58:27,http://hoppe.co/gordon,,51.164.133.145,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n2117,4,199,2017-02-04 11:21:00,http://stehr.com/verdie_quitzon,,68.38.78.199,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n2118,4,199,2017-03-29 08:46:25,http://parker.com/chester,,120.133.67.103,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n2119,4,199,2017-04-16 02:01:22,http://durganwaelchi.biz/emma_feil,,46.33.72.25,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n2120,4,199,2017-03-05 06:23:37,http://koeppbogisich.name/ferne,,160.182.241.62,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n2121,4,199,2017-01-16 21:18:40,http://effertz.net/gail,,105.12.97.69,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n2122,4,199,2017-04-05 18:12:36,http://torphy.info/coty,,68.145.103.219,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n2123,4,199,2017-04-11 17:51:12,http://lubowitzjones.co/zena.grady,,77.181.56.188,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n2124,4,199,2017-04-20 15:14:09,http://maggioratke.info/isadore,,174.55.87.141,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n2125,4,199,2017-06-10 01:05:04,http://witting.com/luna,,86.135.45.14,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n2126,4,199,2017-01-21 00:35:23,http://waelchigoodwin.biz/kennedy,,247.41.63.144,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n2127,4,199,2016-12-22 06:10:23,http://schaefer.name/javier,,174.55.87.141,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n2128,4,199,2017-05-09 19:54:21,http://nitzschecasper.info/dallas_mcglynn,,174.55.87.141,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n2129,4,199,2016-12-21 05:26:00,http://rolfson.io/rey,,218.14.232.187,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n2130,4,199,2017-02-11 03:40:10,http://murphylebsack.io/edythe_lueilwitz,,246.28.87.203,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n2131,4,199,2017-05-17 03:19:31,http://lueilwitz.io/bette,,66.143.187.56,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n2132,4,200,2017-02-02 21:46:58,http://padberg.biz/orlando.borer,,9.42.162.59,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n2133,4,200,2017-04-01 21:06:23,http://torp.name/addison_kerluke,,30.193.74.133,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n2134,4,200,2017-03-27 20:04:12,http://oberbrunner.org/hector.cormier,,106.13.251.95,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n2135,4,200,2017-03-17 23:20:29,http://borergleichner.co/ari.denesik,,206.60.110.60,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n2136,4,201,2017-01-28 06:50:30,http://huel.info/meda_predovic,,228.215.116.150,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n2137,4,201,2017-01-19 02:15:19,http://corwinparisian.org/leanna_hermiston,,207.164.114.203,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n2138,4,201,2017-01-06 06:52:51,http://ebert.org/vanea.reichel,,179.211.41.200,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n2139,4,201,2017-05-18 19:44:57,http://deckowabshire.co/michaela,,124.133.40.247,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n2140,4,201,2017-04-04 09:10:23,http://steuber.co/antone.bosco,,47.123.238.76,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n2141,4,201,2017-04-12 11:48:25,http://hettinger.biz/earlene_upton,,245.168.56.77,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n2142,4,201,2017-01-26 22:57:36,http://wymanebert.com/kenna,,51.19.65.215,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n2143,4,201,2017-05-16 07:24:47,http://morietterosenbaum.biz/nedra.dibbert,,252.154.122.236,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n2144,4,201,2016-12-31 19:34:25,http://lockman.org/dwight,,215.112.21.228,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n2145,4,201,2017-03-04 12:06:31,http://baileyshanahan.info/rosemary_murray,,252.154.122.236,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n2146,4,201,2016-12-24 23:39:34,http://schaden.io/elmore_runolfsdottir,,99.73.153.187,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n2147,4,202,2017-04-28 02:59:32,http://yundt.org/anabelle,,24.71.70.149,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n2148,4,202,2017-02-26 04:01:10,http://weber.co/josiane_sporer,,151.84.82.96,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n2149,4,202,2016-12-25 17:34:22,http://marquardt.biz/cyrus.kiehn,,209.34.115.234,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n2150,4,202,2016-12-20 10:03:02,http://mannrunolfsdottir.info/columbus,,68.134.104.32,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n2151,4,202,2017-01-21 13:41:04,http://hauck.info/garett,,22.192.134.146,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n2152,4,202,2017-05-18 19:27:13,http://kuphal.org/darrion_goodwin,,109.238.197.52,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n2153,4,202,2016-12-20 10:34:18,http://bahringer.info/suzanne.mertz,,201.11.237.191,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n2154,4,202,2017-04-18 16:01:52,http://zemlak.net/seamus.hamill,,37.164.144.118,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n2155,4,202,2017-05-20 19:24:49,http://nader.com/bailee_boyer,,112.5.72.68,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n2156,4,202,2016-12-20 05:08:39,http://bahringerkoch.co/treie,,77.232.63.43,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n2157,4,202,2017-02-08 21:31:38,http://jacobi.com/jaiden,,116.219.76.223,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n2158,4,202,2017-03-03 23:43:27,http://pacochaokeefe.io/marjory,,167.166.83.232,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n2159,4,202,2017-06-01 11:04:03,http://littel.name/savanna,,136.105.209.153,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n2160,4,202,2017-01-11 14:30:52,http://weinat.biz/madie,,152.38.142.162,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n2161,4,202,2017-05-16 17:48:32,http://davis.co/kaitlin,,215.196.126.164,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n2162,4,202,2017-03-03 18:02:29,http://bins.co/mabel,,22.192.134.146,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n2163,4,202,2017-01-12 19:19:33,http://howewisozk.biz/martina.mitchell,,70.133.69.213,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n2164,4,203,2017-06-05 19:32:17,http://heelskiles.com/piper,,159.40.126.234,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n2165,4,203,2016-12-16 08:16:40,http://aufderhar.info/marcelo,,211.234.58.206,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n2166,4,203,2017-05-27 23:32:15,http://upton.net/hubert,,198.67.60.180,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n2167,4,203,2017-04-30 01:06:21,http://rice.com/clifton,,61.155.4.103,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n2168,4,203,2017-02-04 08:12:40,http://zboncak.biz/elbert.feil,,80.124.123.50,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n2169,4,203,2017-01-18 17:24:31,http://vonrueden.biz/boris_ryan,,196.116.253.85,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n2170,4,203,2017-06-12 13:51:33,http://bradtke.name/nina.mosciski,,55.114.87.35,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n2171,4,203,2017-05-27 03:37:16,http://nolan.info/julius,,197.167.52.253,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n2172,4,203,2017-04-01 16:02:42,http://kutch.biz/granville_cruickshank,,96.61.169.31,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n2173,4,203,2016-12-14 04:47:01,http://zboncak.co/stephania,,213.104.46.181,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n2174,4,203,2017-03-08 08:30:52,http://satterfield.org/ila_satterfield,,249.150.237.139,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n2175,4,203,2016-12-20 20:56:50,http://kiehn.info/harmon.ruecker,,189.78.239.163,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n2176,4,203,2017-02-11 23:45:46,http://swaniawskikaulke.info/eugene,,142.117.231.16,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n2177,4,203,2017-04-15 23:44:23,http://king.co/cecile,,40.50.204.51,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n2178,4,203,2017-04-19 06:28:09,http://cummerata.org/dante,,126.151.12.17,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n2179,4,203,2017-05-08 21:18:14,http://gulgowskifeil.io/bethel,,59.52.156.28,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n2180,4,203,2017-06-06 21:46:14,http://millerwillms.co/benny,,254.74.27.27,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n2181,4,204,2017-04-23 06:40:41,http://murphy.co/dorris.dietrich,,247.132.216.127,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n2182,4,204,2017-04-21 08:54:44,http://crooksdurgan.net/jovanny,,60.104.164.253,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n2183,4,204,2017-01-18 09:09:42,http://rippin.org/eudora_marvin,,160.180.191.214,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n2184,4,204,2017-05-24 21:56:20,http://marksosinski.net/david_bahringer,,40.214.137.148,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n2185,4,204,2017-04-19 07:13:52,http://casper.name/ara.franecki,,30.122.208.222,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n2186,4,204,2017-06-09 16:13:27,http://cremin.io/janie,,109.73.20.220,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n2187,4,204,2017-03-18 16:11:41,http://brekke.io/jovany,,180.253.223.29,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n2188,4,204,2017-01-24 20:10:25,http://yostkub.org/garret.toy,,140.113.215.34,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n2189,4,204,2017-04-06 03:38:27,http://hermistongerlach.co/jorge,,154.145.129.85,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n2190,4,204,2017-03-03 12:48:21,http://fritschsmith.name/marion.price,,140.113.215.34,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n2191,4,204,2017-01-02 22:18:34,http://vandervortkertzmann.io/letitia,,33.136.169.253,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n2192,4,204,2017-04-13 13:44:10,http://dietrichlang.io/shakira,,134.9.55.74,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n2193,4,205,2017-05-23 10:07:07,http://wardbeahan.info/rhea.heel,,105.231.142.219,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n2194,4,205,2017-06-06 04:02:28,http://tillman.co/danny,,226.46.222.104,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n2195,4,205,2016-12-17 23:34:14,http://schulistlegros.co/domenico.wyman,,237.95.120.142,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n2196,4,205,2017-03-29 02:04:26,http://oconnell.net/jefferey_miller,,82.130.187.195,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n2197,4,205,2017-02-24 14:40:16,http://prohaskagleason.info/frederique,,40.43.220.201,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n2198,4,205,2017-03-23 18:15:48,http://nicolas.biz/garrick_wehner,,98.204.29.122,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n2199,4,205,2017-02-28 20:02:23,http://rohan.com/kenyon,,243.117.179.33,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n2200,4,206,2017-02-17 13:55:07,http://schulist.co/monte,,18.58.60.23,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n2201,4,206,2017-04-15 12:43:50,http://hoeger.net/chaim,,167.144.49.13,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n2202,4,206,2017-05-20 06:33:22,http://rogahndickens.name/clarabelle_howe,,113.128.85.13,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n2203,4,206,2017-03-21 20:34:23,http://hilll.io/elwin,,18.58.60.23,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n2204,4,206,2017-06-08 12:05:24,http://shanahan.net/iva.volkman,,252.87.153.156,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n2205,4,206,2017-01-07 21:18:18,http://adamskutch.co/rachael,,114.222.238.8,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n2206,4,206,2017-06-13 10:41:44,http://brownlarkin.io/colleen.gutmann,,219.68.72.45,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n2207,4,206,2017-01-18 16:53:08,http://osinski.org/nona,,18.58.60.23,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n2208,4,206,2017-03-16 08:42:58,http://carroll.net/jeica,,225.195.167.216,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n2209,4,206,2017-06-03 10:27:03,http://hyatt.net/jan_bahringer,,74.201.243.117,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n2210,4,206,2017-02-26 23:16:57,http://hodkiewicz.com/virgil.renner,,25.52.186.215,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n2211,4,206,2017-01-02 04:13:19,http://hackett.io/mariam,,58.51.227.200,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n2212,4,206,2017-04-16 09:31:31,http://damore.com/junior_farrell,,137.198.146.116,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n2213,4,206,2016-12-15 22:04:59,http://damorerenner.org/alfred,,137.198.146.116,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n2214,4,207,2017-03-27 04:35:33,http://zemlakkoelpin.info/jazmin.mclaughlin,,131.182.203.249,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n2215,4,207,2017-02-01 21:10:54,http://mcglynn.co/zack.graham,,137.201.114.46,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n2216,4,207,2017-01-13 20:29:17,http://runolfon.net/virgie,,246.164.117.245,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n2217,4,207,2017-04-21 15:22:30,http://hills.org/destiny.oconner,,212.50.230.230,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n2218,4,207,2017-05-20 02:41:23,http://fritsch.biz/richard.larkin,,137.201.114.46,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n2219,4,207,2017-02-05 13:03:28,http://uptonemard.info/mara,,212.50.230.230,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n2220,4,207,2017-05-24 06:38:17,http://goyette.co/amari.roob,,45.179.239.10,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n2221,4,207,2017-02-04 02:09:54,http://cremingreenholt.name/trace.marvin,,63.134.92.186,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n2222,4,207,2016-12-14 20:35:32,http://hegmanndare.name/evangeline,,45.179.239.10,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n2223,4,208,2017-04-27 09:54:39,http://gislasonkozey.org/imelda.bednar,,183.13.183.196,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n2224,4,208,2017-06-05 08:08:58,http://mayertgrant.org/benny,,105.50.207.112,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n2225,4,208,2017-06-13 03:04:40,http://willms.co/beryl.abshire,,224.31.216.135,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n2226,4,208,2016-12-13 23:09:46,http://shieldsdooley.net/lew,,204.94.15.23,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n2227,4,208,2017-01-19 19:37:44,http://bechtelar.io/maximillian,,25.140.118.103,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n2228,4,208,2016-12-20 17:07:06,http://lynch.org/garfield.schowalter,,108.57.239.78,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n2229,4,208,2017-02-24 22:23:13,http://williamson.co/devante_stehr,,61.36.82.158,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n2230,4,208,2017-01-07 20:16:31,http://bednarrempel.com/evert,,125.220.90.233,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n2231,4,208,2017-03-31 16:52:01,http://stokeshodkiewicz.biz/aurore.schimmel,,252.47.127.198,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n2232,4,208,2017-01-08 15:02:47,http://hanebeer.com/kenyon,,204.94.15.23,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n2233,4,209,2017-04-19 09:06:22,http://connelly.name/isabelle,,223.56.89.18,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n2234,4,209,2017-05-24 07:47:49,http://mueller.biz/toby,,34.4.226.25,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n2235,4,209,2017-02-25 20:15:03,http://mayer.co/patricia.feil,,7.9.99.173,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n2236,4,209,2017-05-25 20:37:43,http://bartonwiegand.biz/lizeth,,34.4.226.25,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n2237,4,209,2017-04-26 14:14:31,http://turnernikolaus.biz/arnoldo,,83.232.84.97,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n2238,4,210,2017-05-27 23:05:38,http://hayes.net/roma.heaney,,145.122.75.58,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n2239,4,210,2017-05-29 05:47:27,http://crooks.biz/jaquelin,,246.88.79.129,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n2240,4,210,2017-05-09 13:34:25,http://sawayn.info/jerome,,202.127.99.170,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n2241,4,210,2017-05-10 02:08:00,http://gibson.net/sylvia_turner,,205.247.13.144,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n2242,4,210,2017-05-14 22:43:06,http://stantonhahn.com/spencer,,222.192.133.201,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n2243,4,210,2017-01-31 17:39:26,http://pourosweber.org/josue_kaulke,,222.192.133.201,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n2244,4,210,2017-05-23 14:35:39,http://swiftgrady.biz/gaston,,83.142.12.254,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n2245,4,210,2017-05-24 17:22:33,http://hoppe.co/yolanda,,52.252.7.158,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n2246,4,210,2017-05-05 23:56:40,http://parker.co/reanna.nienow,,52.169.124.88,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n2247,4,210,2017-03-27 21:57:33,http://walsh.com/estella_heidenreich,,147.35.43.46,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n2248,4,211,2017-06-09 15:12:26,http://trompsimonis.biz/cecil.wiegand,,63.181.235.66,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n2249,4,211,2017-01-24 17:27:24,http://collier.org/merlin.willms,,218.49.172.193,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n2250,4,211,2017-05-14 04:46:16,http://schaeferbashirian.biz/anabelle,,194.142.86.134,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n2251,4,211,2017-03-26 07:42:30,http://dach.io/esperanza,,173.60.76.34,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n2252,4,211,2017-05-10 16:27:12,http://howellboyle.net/rebekah.metz,,86.198.10.91,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n2253,4,211,2017-01-06 20:41:10,http://greenschuster.co/emmett_hyatt,,74.165.206.30,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n2254,4,211,2017-02-13 14:07:05,http://reillykihn.com/mercedes,,12.51.42.24,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n2255,4,211,2017-06-06 10:33:03,http://wildermanwitting.info/ines.kihn,,247.228.45.45,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n2256,4,211,2017-03-05 21:57:03,http://oberbrunnerreynolds.biz/franz,,214.58.229.244,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n2257,4,211,2017-03-18 18:12:46,http://kovacekdurgan.biz/rosendo,,92.112.89.227,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n2258,4,211,2017-02-10 21:24:18,http://will.com/ruell,,138.143.114.43,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n2259,4,211,2016-12-24 06:33:24,http://terry.io/brandyn_schumm,,9.65.116.125,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n2260,4,211,2017-01-18 01:15:00,http://gusikowskiskiles.com/flavio,,194.142.86.134,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n2261,4,211,2017-02-18 15:45:12,http://bergnaum.net/makenzie.schaefer,,205.93.226.17,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n2262,4,211,2017-04-17 01:50:25,http://trompsmith.biz/haskell_fahey,,151.45.200.112,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n2263,4,211,2017-04-30 16:00:17,http://purdy.org/bernita,,63.84.148.7,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n2264,4,211,2017-02-20 22:27:45,http://connfunk.io/andre,,87.114.64.240,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n2265,4,211,2016-12-26 00:31:02,http://haley.org/camila_stracke,,182.252.170.233,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n2266,4,211,2016-12-26 19:33:48,http://abshire.info/rafaela_dach,,167.111.252.179,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n2267,4,211,2017-04-20 13:10:13,http://bergstromglover.com/kayden,,63.84.148.7,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n2268,4,212,2017-03-11 17:52:52,http://kovacek.net/karley,0.0000000000,89.111.166.114,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n2269,4,212,2017-06-12 13:15:12,http://cummeratarohan.info/cleora.pagac,0.0000000000,219.216.26.193,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n2270,4,212,2017-05-10 04:25:18,http://gutkowski.net/donnie.davis,0.0000000000,41.123.124.18,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n2271,4,212,2017-01-18 14:01:01,http://weinat.net/baron,0.0000000000,216.201.24.159,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n2272,4,212,2017-01-04 17:44:23,http://gerholdkaulke.com/scarlett,0.0000000000,168.110.80.196,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n2273,4,212,2017-05-19 12:47:12,http://nicolas.org/icie,0.0000000000,66.73.29.100,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n2274,4,212,2017-05-09 01:39:21,http://renner.org/elnora.ruecker,0.0000000000,243.209.69.128,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n2275,4,213,2017-04-15 18:25:18,http://rosenbaum.net/lorenzo.moore,1.0000000000,202.11.115.198,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n2276,4,213,2017-04-28 10:58:36,http://larkin.com/edwin_mccullough,1.0000000000,23.127.42.184,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n2277,4,213,2017-03-02 00:08:30,http://schaefer.info/kianna.damore,1.0000000000,207.5.84.31,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n2278,4,213,2017-04-09 18:40:57,http://orn.name/theron_sawayn,1.0000000000,35.195.203.140,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n2279,4,213,2016-12-15 18:32:57,http://roberts.net/flavie.langosh,0.0000000000,58.178.48.181,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n2280,4,213,2016-12-31 11:21:05,http://green.org/nick,1.0000000000,244.17.163.200,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n2281,4,213,2017-04-05 16:30:18,http://williamson.biz/deshawn,0.0000000000,161.51.16.46,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n2282,4,214,2017-05-05 11:17:27,http://barrowsfeil.org/taryn,1.0000000000,189.181.113.169,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n2283,4,214,2017-01-21 11:39:56,http://reichel.com/audrey,0.0000000000,49.81.143.132,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n2284,4,214,2017-04-22 13:27:08,http://blanda.biz/alexandra,0.0000000000,38.203.43.22,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n2285,4,214,2017-01-03 12:50:52,http://darehauck.co/alec,0.0000000000,29.207.133.27,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n2286,4,214,2017-01-30 17:14:16,http://schumm.info/suzanne_hermiston,0.0000000000,38.203.43.22,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n2287,4,214,2017-04-09 15:59:02,http://baumbach.name/connor,1.0000000000,49.81.143.132,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n2288,4,214,2016-12-13 11:38:31,http://whitespencer.biz/billie,0.0000000000,138.10.143.15,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n2289,4,214,2017-03-22 19:27:28,http://deckow.net/bria.kunze,0.0000000000,238.175.158.187,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n2290,4,214,2017-03-26 05:48:49,http://reilly.net/ezekiel.wolf,1.0000000000,49.81.143.132,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n2291,4,214,2017-02-16 04:52:14,http://heathcote.org/prince,1.0000000000,68.56.104.2,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n2292,4,214,2017-02-14 07:43:12,http://schuppegerhold.info/nadia_hauck,1.0000000000,49.81.143.132,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n2293,4,214,2017-03-21 07:20:12,http://bartoletti.net/kara,0.0000000000,138.10.143.15,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n2294,4,214,2017-03-02 11:08:16,http://daniel.info/gregg.sauer,1.0000000000,91.177.191.144,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n2295,4,214,2016-12-28 12:16:20,http://friesenvolkman.name/juston,0.0000000000,49.81.143.132,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n2296,4,214,2017-03-03 11:28:20,http://schneider.io/lucas_toy,1.0000000000,51.29.209.47,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n2297,4,214,2017-04-30 13:12:19,http://sawayn.info/jayce_monahan,1.0000000000,252.47.76.166,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n2298,4,214,2017-05-21 06:19:44,http://schroeder.com/bernadine_sauer,0.0000000000,87.137.71.80,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n2299,4,215,2017-03-11 21:34:50,http://ferry.co/ralph.cole,1.0000000000,116.9.119.127,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n2300,4,215,2017-04-28 02:05:43,http://gerlachpfannerstill.info/valentine,2.0000000000,162.115.248.24,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n2301,4,215,2017-01-06 19:49:11,http://lueilwitzprosacco.com/freeman,1.0000000000,184.190.125.125,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n2302,4,215,2017-02-04 08:30:28,http://lindgren.org/ayana.weimann,2.0000000000,251.204.39.99,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n2303,4,215,2017-04-18 03:26:46,http://luettgengusikowski.com/ulises.jenkins,0.0000000000,225.15.44.249,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n2304,4,215,2017-05-18 08:18:10,http://kuhnflatley.io/alberto.kling,0.0000000000,208.81.79.152,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n2305,4,215,2017-05-09 15:40:45,http://corwingleason.org/alf,2.0000000000,149.60.22.154,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n2306,4,215,2017-03-14 15:05:26,http://christiansen.co/torrance,1.0000000000,41.154.55.36,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n2307,4,215,2017-06-11 06:18:58,http://hackett.com/lauren,1.0000000000,176.222.113.206,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n2308,4,215,2017-04-20 04:45:16,http://balistreri.biz/elton,1.0000000000,53.31.12.88,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n2309,4,215,2017-04-10 03:24:00,http://ritchie.io/christy,0.0000000000,116.9.119.127,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n2310,4,215,2017-02-19 22:51:07,http://flatley.com/hertha,1.0000000000,53.31.12.88,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n2311,4,215,2017-01-14 08:21:56,http://legros.com/gabe,0.0000000000,87.22.109.19,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n2312,4,215,2017-02-04 03:35:20,http://blick.com/frederick.schimmel,0.0000000000,199.223.103.65,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n2313,4,215,2016-12-18 11:37:13,http://bechtelar.biz/maximillia,2.0000000000,225.3.50.122,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n2314,4,215,2017-04-19 03:46:15,http://leffler.info/lisandro.kautzer,2.0000000000,84.204.125.112,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n2315,4,216,2016-12-25 12:24:28,http://greenholt.org/dangelo_borer,1.0000000000,13.251.77.22,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n2316,4,216,2017-03-16 15:48:36,http://schroeder.info/edyth_schmidt,3.0000000000,78.101.26.168,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n2317,4,216,2016-12-30 01:10:40,http://blick.org/francis,2.0000000000,13.88.46.99,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n2318,4,216,2017-02-19 16:58:58,http://schimmel.name/lesley.lockman,2.0000000000,215.47.100.221,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n2319,4,216,2017-03-22 12:29:31,http://veum.io/frieda_reichel,2.0000000000,111.224.62.253,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n2320,4,216,2017-02-18 08:15:01,http://barrows.org/liliana_greenfelder,2.0000000000,55.167.246.241,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n2321,4,216,2017-01-13 13:42:57,http://carter.name/lelia,3.0000000000,81.155.212.204,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n2322,4,217,2017-01-23 20:08:45,http://harrisgreen.co/lorenzo_kling,2.0000000000,160.118.41.61,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n2323,4,217,2017-01-25 08:32:31,http://predovic.info/gerda.borer,0.0000000000,129.122.187.140,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n2324,4,217,2017-01-17 17:16:54,http://gulgowski.org/una,1.0000000000,240.88.223.177,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n2325,4,217,2017-03-16 12:43:38,http://buckridge.co/moriah.kovacek,1.0000000000,57.210.119.150,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n2326,4,217,2017-01-07 03:48:03,http://grady.info/clotilde.kovacek,0.0000000000,218.233.77.163,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n2327,4,217,2017-01-28 13:34:20,http://christiansen.name/darrion_senger,1.0000000000,5.24.128.253,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n2328,4,217,2017-04-02 11:45:34,http://wehner.com/mohammed_schultz,2.0000000000,194.179.68.197,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n2329,4,217,2016-12-25 13:11:37,http://simonis.com/damien_boyle,0.0000000000,145.227.194.224,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n2330,4,217,2017-01-17 01:14:25,http://shieldsbednar.co/raoul_stroman,2.0000000000,41.222.57.51,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n2331,4,218,2017-02-22 14:49:21,http://naderdenesik.net/jannie_auer,1.0000000000,196.51.164.53,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n2332,4,218,2017-02-22 02:38:23,http://pouros.org/allen,0.0000000000,74.54.65.176,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n2333,4,218,2017-06-09 20:59:43,http://windler.biz/romaine.howell,0.0000000000,66.136.74.113,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n2334,4,218,2017-04-04 11:10:50,http://stamm.co/camylle.rodriguez,2.0000000000,30.112.236.197,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n2335,4,218,2017-03-31 00:32:03,http://lynch.co/ida,1.0000000000,232.87.36.208,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n2336,4,218,2017-05-30 05:56:50,http://jerde.com/maybell_dubuque,2.0000000000,138.237.13.241,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n2337,4,218,2017-03-19 03:24:56,http://stromangusikowski.io/danial,2.0000000000,202.205.78.105,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n2338,4,218,2017-04-23 16:36:35,http://leschborer.org/amani,0.0000000000,74.54.65.176,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n2339,4,218,2017-02-20 03:22:01,http://oconner.org/rhett,0.0000000000,72.17.87.177,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n2340,4,218,2017-01-16 20:24:49,http://leffler.net/mohamed.predovic,2.0000000000,120.66.103.103,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n2341,4,218,2017-02-04 07:31:10,http://trantowkerluke.org/loma,1.0000000000,82.123.194.73,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n2342,4,218,2017-05-25 03:38:25,http://sanford.co/jolie,2.0000000000,196.51.164.53,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n2343,4,218,2017-04-30 06:13:38,http://crist.net/manuela_cruickshank,0.0000000000,128.107.31.184,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n2344,4,218,2017-03-13 14:51:57,http://weber.info/anya,1.0000000000,63.159.211.208,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n2345,4,218,2017-05-26 17:11:25,http://mcclure.net/lamont_armstrong,1.0000000000,72.17.87.177,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n2346,4,218,2017-06-13 18:41:11,http://berge.name/asa,1.0000000000,30.112.236.197,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n2347,4,218,2017-04-15 06:55:28,http://stokes.net/dayton.homenick,2.0000000000,203.48.193.226,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n2348,4,219,2017-04-21 02:25:34,http://cartwrighthilll.co/keyon,0.0000000000,177.74.17.14,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n2349,4,219,2017-03-21 18:00:34,http://walkerleannon.co/alycia,0.0000000000,208.137.247.4,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n2350,4,219,2017-04-15 22:35:07,http://rennerbatz.biz/vernon,0.0000000000,162.236.80.172,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n2351,4,219,2017-04-03 15:34:09,http://goldner.name/stephanie,0.0000000000,76.196.161.249,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n2352,4,219,2017-03-23 22:28:33,http://ruel.com/candida_rosenbaum,0.0000000000,249.165.104.26,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n2353,4,219,2017-02-15 14:46:36,http://carter.name/tristian,0.0000000000,162.236.80.172,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n2354,4,219,2017-05-23 00:27:58,http://connelly.com/phoebe_blanda,0.0000000000,254.134.20.12,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n2355,4,219,2017-05-23 23:53:35,http://legroscormier.info/mathew.pfannerstill,0.0000000000,86.76.161.175,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n2356,4,219,2017-06-08 02:07:29,http://lynchdickinson.net/reese,0.0000000000,193.171.16.81,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n2357,4,219,2017-01-04 01:41:08,http://bartellmcglynn.net/ramiro_considine,0.0000000000,233.247.128.87,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n2358,4,219,2017-02-21 15:18:32,http://welch.org/lauren,0.0000000000,13.71.148.214,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n2359,4,219,2017-03-02 12:07:30,http://shanahanhansen.info/muriel.turner,0.0000000000,86.76.161.175,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n2360,4,219,2017-03-30 06:28:28,http://kautzer.co/candelario,0.0000000000,90.245.133.181,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n2361,4,219,2017-05-15 00:07:26,http://turner.io/shanna,0.0000000000,19.193.4.175,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n2362,4,219,2017-01-04 20:33:32,http://satterfieldkutch.io/emmitt_cummings,0.0000000000,203.84.214.156,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n2363,4,219,2016-12-25 10:33:25,http://windler.org/clark,0.0000000000,207.118.20.154,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n2364,4,220,2017-01-23 07:48:30,http://adams.biz/athena.olson,0.0000000000,87.182.47.244,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n2365,4,221,2017-05-30 12:11:18,http://leffler.com/amelia.denesik,,64.189.128.171,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n2366,4,221,2016-12-22 16:47:39,http://klocko.co/rubye,,247.44.138.18,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n2367,4,221,2017-06-06 20:04:32,http://kuvalis.co/omari,,194.122.31.144,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n2368,4,221,2017-02-16 07:30:37,http://stamm.info/mae_schneider,,46.207.39.66,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n2369,4,221,2017-04-12 01:09:51,http://dietrich.com/crystel_glover,,244.25.34.97,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n2370,4,221,2016-12-19 19:16:59,http://ward.biz/gregg,,240.169.18.162,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n2371,4,222,2017-03-13 04:05:07,http://runolfoncollier.info/octavia,,170.78.156.208,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n2372,4,222,2017-05-06 00:18:12,http://leffler.io/trenton,,170.78.156.208,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n2373,4,222,2017-06-04 22:42:07,http://ruel.com/cleta,,250.119.252.2,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n2374,4,223,2017-05-03 05:03:44,http://windlergreenfelder.name/donny.dare,,41.171.106.92,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n2375,4,223,2017-01-01 05:06:15,http://rogahn.info/ludwig,,147.212.182.23,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n2376,4,223,2017-03-06 23:11:40,http://skileslittle.name/trevor.kuhic,,118.168.135.138,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n2377,4,223,2017-04-06 17:56:40,http://gaylord.co/trever,,113.16.230.252,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n2378,4,223,2017-05-24 11:33:01,http://wymanboyer.com/jasen_wolff,,110.103.42.180,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n2379,4,223,2017-02-09 22:27:23,http://kilback.name/jacinthe_legros,,244.70.156.109,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n2380,4,223,2017-04-12 01:27:40,http://treutel.com/stanford_jacobi,,102.157.85.223,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n2381,4,223,2017-03-14 10:21:41,http://watsicabednar.org/johnpaul.leffler,,16.244.77.96,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n2382,4,223,2016-12-13 09:08:15,http://conroy.com/madelynn,,196.60.176.22,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n2383,4,223,2017-01-15 13:44:18,http://mitchellmarvin.name/chelsie_rice,,147.114.192.59,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n2384,4,223,2016-12-27 17:56:48,http://keeblerhermiston.name/andrew_jast,,226.219.139.117,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n2385,4,223,2017-03-29 08:46:34,http://schoen.net/mariah,,170.167.181.24,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n2386,4,224,2017-02-21 03:16:54,http://goldnerwest.io/marisa_nader,,148.109.110.112,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n2387,4,224,2017-04-25 07:20:26,http://botsford.org/telly,,77.22.107.118,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n2388,4,224,2017-01-07 00:54:03,http://yundt.io/dulce,,168.224.90.232,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n2389,4,224,2017-02-26 17:55:35,http://wardwilkinson.name/keara.doyle,,77.22.107.118,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n2390,4,225,2017-06-06 12:28:08,http://purdy.biz/marvin,,147.163.45.139,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n2391,4,225,2016-12-19 07:26:26,http://glover.biz/nickolas_haley,,47.225.162.87,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n2392,4,225,2017-04-14 00:28:02,http://jacobi.biz/rick_johnson,,128.214.219.78,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n2393,4,225,2017-05-28 00:01:34,http://lemke.name/eladio,,47.225.162.87,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n2394,4,225,2017-02-03 12:31:12,http://christiansenschulist.net/jaiden,,25.226.250.94,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n2395,4,225,2017-04-21 13:23:08,http://fay.net/mac,,109.252.215.28,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n2396,4,225,2017-04-27 04:15:36,http://morar.com/madyson.kertzmann,,3.153.18.135,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n2397,4,225,2017-05-06 00:43:51,http://turner.net/rhianna_rohan,,243.133.33.144,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n2398,4,225,2017-05-06 22:25:50,http://thiel.io/effie,,6.100.99.33,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n2399,4,225,2017-05-12 14:59:43,http://gutkowskiwisoky.io/houston.bauch,,196.143.119.199,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n2400,4,225,2017-05-23 06:11:30,http://cummerata.net/haley,,194.213.156.176,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n2401,4,225,2017-03-28 02:03:04,http://robelbins.name/loraine,,95.242.60.145,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n2402,4,225,2017-01-28 02:02:07,http://ruecker.org/maureen.hyatt,,66.85.250.254,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n2403,4,225,2017-05-17 10:07:18,http://cruickshank.net/anya,,109.252.215.28,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n2404,4,225,2017-01-12 03:13:20,http://orn.org/jamir_renner,,225.199.50.127,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n2405,4,226,2017-04-04 01:08:24,http://erdmaneffertz.biz/rashawn.davis,,126.173.180.19,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n2406,4,226,2017-06-10 14:52:26,http://kovacek.biz/brandon,,48.107.226.5,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n2407,4,226,2017-05-24 15:34:34,http://jast.biz/ashleigh.heaney,,184.124.59.236,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n2408,4,226,2017-03-07 18:19:43,http://wardjacobs.net/modesta_hauck,,206.208.143.37,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n2409,4,226,2017-01-15 07:39:04,http://reynolds.com/trevion,,49.8.148.227,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n2410,4,226,2017-05-11 00:12:15,http://goodwinmacejkovic.info/crawford_luettgen,,242.129.254.204,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n2411,4,226,2017-02-04 14:55:28,http://larsonwaters.biz/keagan,,48.107.226.5,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n2412,4,226,2017-04-28 21:46:58,http://kris.io/federico,,50.252.49.178,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n2413,4,226,2017-05-11 08:06:26,http://dickinsonkautzer.name/noe.koch,,126.173.180.19,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n2414,4,226,2016-12-23 02:00:48,http://gutkowski.name/hazel.weimann,,242.129.254.204,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n2415,4,226,2017-06-10 02:31:10,http://prohaskakulas.co/jailyn.bogan,,50.252.49.178,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n2416,4,226,2017-05-21 00:37:00,http://connrohan.name/libbie,,151.78.107.195,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n2417,4,226,2017-01-05 23:41:06,http://hoppe.com/tabitha,,59.132.144.74,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n2418,4,226,2017-01-31 02:59:03,http://murphy.co/tomas,,183.94.30.97,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n2419,4,226,2017-05-21 03:15:53,http://thompson.org/alana_stiedemann,,48.107.226.5,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n2420,4,226,2017-05-12 05:03:54,http://mckenziepfannerstill.com/kattie,,126.173.180.19,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n2421,4,226,2017-02-20 00:46:32,http://schneider.io/kaylah.hackett,,165.39.79.87,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n2422,4,227,2017-04-03 05:51:39,http://tremblay.biz/letitia,,229.172.214.86,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n2423,4,227,2017-05-03 22:39:02,http://medhurst.co/antonio.crist,,246.63.8.85,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n2424,4,227,2017-01-24 19:12:43,http://kutch.info/lester,,183.250.107.64,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n2425,4,227,2017-01-21 05:12:30,http://olsongrimes.info/vilma_daniel,,71.242.165.69,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n2426,4,227,2017-01-26 02:58:43,http://raustanton.info/walker,,77.176.235.3,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n2427,4,227,2017-04-08 12:54:34,http://aufderharbecker.com/adela_romaguera,,46.184.215.189,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n2428,4,227,2017-02-12 07:35:05,http://connelly.io/mattie.herman,,22.139.247.42,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n2429,4,227,2016-12-19 14:43:31,http://hyatt.info/malvina.keeling,,245.114.243.230,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n2430,4,227,2017-06-09 12:45:02,http://schimmeldicki.com/vince,,77.176.235.3,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n2431,4,227,2017-03-01 16:22:01,http://streichwiegand.name/reilly,,245.246.103.142,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n2432,4,228,2017-05-30 23:42:13,http://hayes.net/mac,,102.163.59.43,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n2433,4,228,2017-05-25 17:16:14,http://maggio.io/annalise,,150.187.42.142,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n2434,4,228,2017-03-16 01:24:36,http://haag.net/olen_labadie,,111.211.84.190,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n2435,4,228,2017-05-02 23:18:36,http://monahan.com/jadyn,,152.201.210.190,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n2436,4,228,2017-01-27 02:01:10,http://nienowschinner.io/lorine,,150.187.42.142,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n2437,4,228,2017-05-01 12:20:02,http://trantow.com/shanelle,,10.134.51.58,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n2438,4,228,2017-04-07 00:24:14,http://roobbogisich.net/reinhold.hirthe,,95.92.39.178,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n2439,4,228,2017-01-02 09:34:53,http://krajcik.biz/joshua_heathcote,,249.161.218.225,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n2440,4,228,2017-04-23 02:11:35,http://willms.name/delpha.goldner,,246.182.162.33,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n2441,4,228,2017-05-05 20:11:28,http://howe.name/idella.rodriguez,,251.44.46.110,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n2442,4,228,2017-04-11 03:39:56,http://lockmancarroll.org/orie,,53.145.162.254,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n2443,4,228,2017-05-03 07:59:26,http://murphy.com/berniece.hudson,,233.185.78.206,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n2444,4,228,2017-05-07 18:34:27,http://satterfield.com/glennie.windler,,131.101.120.43,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n2445,4,228,2017-01-07 23:37:02,http://marks.co/hannah,,150.187.42.142,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n2446,4,228,2017-04-06 10:22:23,http://eichmann.org/jovani,,48.111.121.64,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n2447,4,228,2017-03-15 08:43:21,http://hermiston.biz/ryan_kuhic,,246.182.162.33,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n2448,4,228,2017-04-18 01:03:42,http://gleason.io/marisa_turcotte,,251.44.46.110,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n2449,4,228,2017-04-18 15:02:44,http://braun.net/shyann,,24.9.56.162,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n2450,4,228,2016-12-20 15:15:11,http://deckow.name/thad_okon,,185.56.162.184,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n2451,4,229,2017-06-13 17:14:22,http://kuphalhagenes.net/whitney,,134.98.190.242,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n2452,4,229,2017-05-08 10:08:45,http://torphy.biz/germaine_lang,,31.119.228.223,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n2453,4,229,2017-04-08 20:05:53,http://erdman.co/ewell,,147.2.241.125,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n2454,4,229,2017-03-17 08:55:12,http://spencer.org/elisha,,204.124.177.41,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n2455,4,229,2017-06-02 16:46:47,http://hahn.org/charlene,,156.202.35.163,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n2456,4,229,2017-04-29 09:02:29,http://wolfkunde.io/marcos.padberg,,46.19.254.9,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n2457,4,229,2017-01-17 18:02:45,http://altenwerth.name/leie,,173.46.94.108,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n2458,4,229,2017-01-22 21:09:15,http://wyman.net/miguel,,5.40.155.163,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n2459,4,229,2017-03-10 23:10:49,http://ryan.org/liza,,136.222.20.139,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n2460,4,229,2016-12-25 21:34:45,http://maggio.name/moriah_romaguera,,150.178.251.183,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n2461,4,229,2017-04-17 22:55:53,http://schultz.info/eduardo_stamm,,103.63.22.112,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n2462,4,229,2017-06-04 23:55:29,http://treutelemard.com/vivian.kunde,,120.2.15.157,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n2463,4,229,2017-05-11 08:27:16,http://mullerkoch.org/dimitri.denesik,,222.45.21.236,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n2464,4,229,2017-02-27 08:56:07,http://mantejaskolski.org/eula_bogisich,,66.68.174.125,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n2465,4,229,2017-01-19 15:04:08,http://balistreri.net/stone,,204.124.177.41,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n2466,4,229,2017-02-13 21:55:15,http://blanda.co/felix.graham,,235.215.198.129,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n2467,4,230,2017-04-08 21:19:04,http://reichel.org/fausto.keler,,254.67.12.95,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n2468,4,230,2017-03-26 08:26:36,http://moore.com/waldo,,4.94.207.219,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n2469,4,230,2017-06-05 15:46:35,http://vandervort.net/florencio.sipes,,12.213.154.195,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n2470,4,230,2017-04-18 21:47:20,http://mann.com/robb_kerluke,,74.20.103.28,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n2471,4,230,2017-04-24 13:52:02,http://lowe.io/myles_beatty,,250.116.210.220,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n2472,4,230,2017-01-10 02:35:42,http://rath.io/jaida,,12.213.154.195,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n2473,4,230,2017-05-07 05:59:24,http://mcglynn.info/kallie_feeney,,162.115.223.126,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n2474,4,230,2016-12-18 00:43:03,http://schamberger.org/andy_mraz,,127.100.141.72,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n2475,4,230,2017-05-29 13:11:39,http://quigley.info/colby.nienow,,242.166.239.65,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n2476,4,230,2017-03-09 13:15:27,http://lubowitzbahringer.co/desiree,,117.186.222.52,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n2477,4,230,2017-05-03 01:22:44,http://lehner.info/lois,,162.115.223.126,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n2478,4,230,2017-04-02 16:52:32,http://feestfriesen.name/vincenzo,,85.235.200.5,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n2479,4,230,2017-01-18 13:06:20,http://orn.com/lukas.rogahn,,113.2.154.190,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n2480,4,230,2017-03-22 23:12:02,http://gibson.io/jerad,,97.20.112.91,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n2481,4,230,2017-05-04 23:32:22,http://greenholt.co/dasia_wisoky,,42.144.172.62,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n2482,4,230,2017-03-21 23:46:37,http://quigleystoltenberg.io/anderson_padberg,,4.94.207.219,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n2483,4,230,2017-02-15 02:15:40,http://hoegereichmann.co/katelynn,,160.143.224.50,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n2484,4,230,2017-06-11 10:17:02,http://emard.com/efrain.breitenberg,,197.197.70.46,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n2485,4,230,2017-05-23 06:01:47,http://buckridge.com/shanny,,127.100.141.72,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n2486,4,231,2016-12-27 06:00:54,http://schmitt.co/lurline.prosacco,,67.234.183.196,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n2487,4,231,2017-03-09 16:51:12,http://gloverhuel.name/teie.reichert,,63.245.221.231,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n2488,4,231,2017-05-13 20:36:30,http://roobroob.info/hilton,,35.150.108.209,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n2489,4,231,2017-02-05 03:20:17,http://abernathychamplin.org/lamar.feest,,35.150.108.209,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n2490,4,231,2017-02-19 01:02:40,http://bergstromokon.info/tyrell_bernier,,36.81.11.69,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n2491,4,231,2017-04-03 04:27:25,http://macgyver.io/kara,,84.73.126.185,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n2492,4,231,2017-05-30 06:37:29,http://rogahnfranecki.com/jazmin,,234.115.69.241,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n2493,4,231,2017-03-02 19:17:18,http://upton.net/flo.ohara,,84.73.126.185,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n2494,4,231,2017-05-07 07:25:29,http://hudsonmcglynn.info/dale.bernhard,,112.127.81.170,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n2495,4,231,2016-12-20 17:40:30,http://kub.biz/oswaldo,,144.43.100.129,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n2496,4,231,2017-01-05 00:32:46,http://webergulgowski.biz/filiberto.runolfon,,64.156.153.54,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n2497,4,231,2017-01-05 08:18:17,http://lakin.io/edmund_klein,,179.48.56.20,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n2498,4,232,2017-03-20 03:40:46,http://steuberhills.io/lilian_sporer,,190.109.227.162,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n2499,4,232,2017-04-30 02:04:43,http://kub.biz/quentin.baumbach,,72.156.122.52,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n2500,4,232,2017-06-02 11:14:55,http://kreiger.com/ignacio.strosin,,127.195.248.179,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n2501,4,232,2017-02-02 05:59:25,http://dietrich.net/keara.schroeder,,14.68.124.42,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n2502,4,232,2017-04-01 20:58:10,http://klein.biz/everette,,190.109.227.162,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n2503,4,232,2017-02-05 19:19:55,http://white.org/ceasar,,190.109.227.162,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n2504,4,232,2017-03-20 23:02:38,http://bradtke.info/jacey,,38.92.131.122,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n2505,4,232,2017-05-06 12:27:28,http://ziemann.name/delmer,,230.237.191.118,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n2506,4,232,2017-01-27 20:16:28,http://krisbins.info/marjorie,,210.34.74.19,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n2507,4,232,2017-03-31 21:12:07,http://rogahn.com/pasquale_bernier,,38.92.131.122,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n2508,4,232,2017-04-09 20:18:53,http://haagpfannerstill.co/imani,,156.14.220.246,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n2509,4,232,2017-01-24 10:15:51,http://schiller.biz/jaycee_kuhn,,116.218.67.69,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n2510,4,233,2017-04-21 19:11:39,http://halvorson.name/paxton.beahan,,33.226.56.126,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n2511,4,233,2017-04-28 12:39:18,http://klockohickle.co/fermin_champlin,,244.135.113.8,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n2512,4,233,2017-01-23 09:47:10,http://bartellmccullough.io/lucinda,,18.5.127.184,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n2513,4,233,2017-05-28 19:10:47,http://schimmel.co/mateo,,27.55.144.248,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n2514,4,233,2017-04-09 10:31:55,http://wisozk.com/vivianne,,48.60.144.104,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n2515,4,233,2017-05-03 21:25:45,http://okeefecummings.com/dee.rempel,,62.148.139.173,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n2516,4,233,2017-02-21 18:50:50,http://okuneva.io/viva.feil,,28.108.73.63,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n2517,4,233,2017-03-30 08:55:58,http://ankundingparisian.biz/sister_wisozk,,121.223.105.159,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n2518,4,233,2017-06-08 12:08:51,http://bartonhowe.com/graciela.kshlerin,,145.142.190.60,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n1337,3,126,2017-06-11 16:46:29,http://gleichneraltenwerth.net/karolann_lindgren,0.0000000000,250.206.28.76,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n1338,3,126,2017-04-18 19:59:06,http://kohlertoy.name/aliza.hilll,0.0000000000,18.97.64.164,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n1339,3,126,2016-12-20 22:56:28,http://batzlowe.com/cesar,1.0000000000,25.14.201.92,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n1340,3,126,2017-03-17 08:16:53,http://abernathy.com/edwin,1.0000000000,194.238.112.173,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n1341,3,126,2017-02-18 17:33:26,http://osinski.com/kitty,0.0000000000,68.18.167.224,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n1342,3,126,2016-12-24 08:46:50,http://osinski.biz/christop_torphy,1.0000000000,7.77.133.61,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n1343,3,126,2017-04-24 13:23:50,http://greenholt.net/tristian_cronin,0.0000000000,98.192.86.187,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n1344,3,126,2017-04-29 00:37:57,http://cummings.com/devan,0.0000000000,98.192.86.187,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n1345,3,126,2017-01-22 07:59:18,http://jast.co/letha_tremblay,1.0000000000,188.232.224.103,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n1346,3,126,2017-03-27 00:28:21,http://gottlieb.biz/brooks_gislason,0.0000000000,217.67.28.93,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n1347,3,126,2017-02-21 18:50:36,http://bailey.com/marquise,1.0000000000,142.43.172.147,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n1348,3,126,2017-06-06 12:12:49,http://jerde.net/samir,0.0000000000,7.77.133.61,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n1349,3,126,2017-04-28 10:05:30,http://quitzon.biz/green,1.0000000000,52.2.218.176,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n1350,3,126,2017-04-19 15:25:44,http://gloverhalvorson.co/trea_leannon,1.0000000000,20.140.219.112,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n1351,3,126,2017-02-04 13:33:41,http://trantow.com/jimmy_barrows,0.0000000000,52.153.156.78,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n1352,3,126,2017-02-21 04:24:02,http://bernier.info/yesenia.olson,1.0000000000,52.153.156.78,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n1353,3,126,2017-01-20 07:30:33,http://altenwerth.name/hoyt,1.0000000000,111.219.40.19,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n1354,3,126,2017-03-15 02:15:42,http://bartoletti.biz/karlie.schamberger,1.0000000000,247.254.202.254,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n1355,3,126,2017-04-24 15:32:47,http://mckenziefahey.info/erica.cruickshank,0.0000000000,250.206.28.76,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n1356,3,127,2017-01-01 10:51:17,http://maggiocarroll.name/ludie.conroy,3.0000000000,174.254.240.70,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n1357,3,127,2017-03-13 17:23:54,http://schoenwest.info/christina.thompson,3.0000000000,62.175.124.96,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n1358,3,127,2017-05-29 10:47:35,http://towne.org/kaden,0.0000000000,130.251.188.145,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n1359,3,127,2017-05-09 19:38:17,http://kshlerin.io/ola_berge,3.0000000000,246.250.199.6,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n1360,3,127,2017-04-09 09:25:20,http://keeling.org/glenna,1.0000000000,111.209.128.75,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n1361,3,127,2017-04-18 02:09:17,http://price.com/chaya.macgyver,3.0000000000,202.174.202.86,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n1362,3,128,2016-12-16 00:32:19,http://thiel.org/dulce,0.0000000000,187.8.177.13,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n1363,3,128,2016-12-28 10:51:18,http://krajcikjaskolski.co/marielle.stanton,3.0000000000,145.169.253.252,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n1364,3,128,2017-04-23 03:58:21,http://dach.name/sven.glover,3.0000000000,115.193.43.30,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n1365,3,128,2016-12-23 14:39:42,http://schmidtebert.biz/sonia.kris,1.0000000000,108.241.47.103,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n1366,3,128,2017-05-11 14:40:15,http://fahey.biz/korey,3.0000000000,176.100.50.231,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n1367,3,128,2017-01-15 10:27:20,http://braun.com/halle_ernser,0.0000000000,163.57.126.241,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n1368,3,128,2017-05-19 13:53:08,http://lang.net/laney,3.0000000000,98.13.92.24,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n1369,3,128,2017-02-17 21:35:39,http://wymanlebsack.name/jaclyn,3.0000000000,19.45.61.239,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n1370,3,128,2017-01-10 15:08:47,http://nitzsche.info/rashawn,0.0000000000,203.237.152.66,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n1371,3,128,2017-02-10 08:17:40,http://spinkabaumbach.com/pinkie.little,0.0000000000,169.6.103.14,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n1372,3,128,2017-01-18 23:22:17,http://considinedooley.net/rosalind,2.0000000000,85.181.39.92,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n1373,3,129,2017-03-30 09:43:04,http://mcdermott.com/shaina,0.0000000000,113.250.78.227,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n1374,3,129,2017-05-27 21:40:42,http://balistreri.net/enrico.glover,0.0000000000,24.138.154.4,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n1375,3,129,2017-02-26 22:47:34,http://jacobsjast.io/bertha,0.0000000000,14.126.56.193,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n1376,3,129,2017-05-05 21:09:03,http://bailey.org/tod.pagac,0.0000000000,63.10.23.107,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n1377,3,129,2016-12-28 10:03:27,http://balistreri.co/caesar.leannon,0.0000000000,215.203.184.70,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n1378,3,129,2017-04-05 06:45:41,http://lang.net/jayde.harris,0.0000000000,78.94.13.117,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n1379,3,129,2017-03-31 06:41:28,http://eichmann.org/simeon,0.0000000000,197.53.209.107,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n1380,3,129,2017-05-08 14:18:28,http://klocko.co/lori.carroll,0.0000000000,78.190.208.75,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n1381,3,129,2016-12-21 22:03:46,http://conroyboyer.org/maureen_abernathy,0.0000000000,140.31.60.102,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n1382,3,129,2017-01-15 15:22:19,http://eichmann.org/anne_gutmann,0.0000000000,165.198.5.172,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n1383,3,129,2017-03-16 02:35:55,http://beattygraham.com/rasheed,0.0000000000,143.3.135.235,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n1384,3,129,2017-04-20 00:32:53,http://franecki.net/cornelius_sanford,0.0000000000,131.54.76.24,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n1385,3,129,2016-12-31 12:43:40,http://zieme.net/zackery.doyle,0.0000000000,27.235.235.131,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n2519,4,233,2017-03-04 15:24:54,http://cronin.name/kay.paucek,,60.33.84.162,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n2520,4,233,2016-12-14 23:41:31,http://gaylord.com/betty,,27.55.144.248,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n2521,4,233,2017-02-23 16:38:07,http://roberts.info/sabryna,,85.55.40.230,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n2522,4,233,2017-01-22 15:58:37,http://wyman.org/lavern_nicolas,,145.82.54.97,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n2523,4,233,2016-12-29 21:29:24,http://kutch.net/lydia,,33.226.56.126,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n2524,4,233,2017-03-06 11:14:45,http://conn.info/luciano_stanton,,48.141.147.8,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n2525,4,234,2017-02-03 10:30:49,http://skiles.info/alivia_anderson,,171.206.135.227,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n2526,4,234,2017-04-08 12:45:38,http://kertzmann.biz/cameron.simonis,,233.94.20.173,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n2527,4,234,2017-01-31 09:36:46,http://kleinmedhurst.io/caria_corkery,,240.40.43.191,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n2528,4,234,2017-06-02 06:26:44,http://dickens.org/alisha.roberts,,31.49.30.143,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n2529,4,234,2017-01-27 19:11:13,http://pfefferjacobs.com/torrance,,11.217.42.229,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n2530,4,234,2017-01-07 07:14:28,http://larson.net/ozzie_gleason,,149.169.115.190,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n2531,4,235,2017-03-26 00:36:58,http://corwin.io/carol,,174.72.201.138,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n2532,4,235,2017-05-10 08:43:30,http://boyle.name/bobbie_okuneva,,64.125.162.102,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n2533,4,235,2016-12-14 17:01:56,http://hellerconroy.com/major,,64.125.162.102,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n2534,4,235,2017-02-19 15:00:32,http://gutkowski.io/wallace.rath,,20.241.156.163,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n2535,4,235,2016-12-31 20:43:53,http://paucekheathcote.name/christy,,107.144.39.193,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n2536,4,235,2016-12-26 23:18:49,http://flatleykoepp.net/edwin_ondricka,,74.32.185.166,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n2537,4,235,2017-01-06 12:48:57,http://rice.biz/adelle,,63.71.98.67,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n2538,4,235,2016-12-19 20:02:31,http://balistreriterry.io/rocio.wuckert,,40.7.158.85,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n2539,4,236,2017-04-11 22:48:04,http://predovicrice.name/harmon,,59.170.6.150,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n2540,4,236,2017-03-16 13:44:54,http://gutkowski.name/christop_orn,,234.20.224.247,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n2541,4,236,2017-05-16 17:09:09,http://ward.info/isom,,107.249.200.220,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n2542,4,236,2017-02-09 02:29:17,http://heller.name/alvah_rolfson,,222.171.193.205,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n2543,4,236,2016-12-25 16:20:09,http://cummings.com/hunter_hackett,,200.203.135.164,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n2544,4,236,2017-02-18 07:04:31,http://blandamedhurst.biz/arlie,,27.51.186.72,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n2545,4,236,2017-03-07 11:12:53,http://tremblayschowalter.info/hortense,,118.180.197.210,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n2546,4,236,2017-02-03 07:28:01,http://thompsonsanford.io/jamaal,,234.20.224.247,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n2547,4,236,2017-03-31 19:31:21,http://ratke.info/lucienne,,216.61.151.27,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n2548,4,236,2017-03-19 02:58:43,http://shanahan.net/maria_wisoky,,147.162.155.188,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n2549,4,236,2017-01-14 14:08:57,http://hettinger.net/ivah.murazik,,245.242.225.5,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n2550,4,236,2017-03-30 07:49:37,http://yundt.info/lucile_grady,,64.120.197.228,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n2551,4,236,2016-12-21 22:25:48,http://howe.com/jolie.bruen,,107.249.200.220,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n2552,4,236,2016-12-22 05:03:32,http://murphy.info/tevin,,175.51.241.162,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n2553,4,236,2017-04-27 20:25:44,http://predovicwhite.com/ansel_huels,,109.109.27.169,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n2554,4,236,2017-04-15 04:40:40,http://wilkinson.io/danny.wilkinson,,92.77.115.111,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n2555,4,236,2017-05-19 17:08:54,http://yost.name/denis,,144.6.189.169,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n2556,4,237,2016-12-26 19:59:24,http://kuhn.info/aleia_frami,,76.203.228.34,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n2557,4,237,2017-01-04 22:17:28,http://littlecruickshank.net/layla,,24.87.25.103,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n2558,4,237,2017-03-22 09:50:08,http://rau.org/leonard.kuhic,,228.157.247.103,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n2559,4,237,2017-01-08 15:43:32,http://wyman.biz/marion_west,,194.83.95.133,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n2560,4,237,2017-04-25 11:47:58,http://hane.biz/dale,,113.173.8.6,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n2561,4,238,2017-04-09 22:07:41,http://luettgen.io/kacey.reynolds,2.0000000000,180.241.27.97,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n2562,4,238,2017-03-25 15:52:14,http://heel.io/nicholaus_kuphal,2.0000000000,144.236.7.79,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n2563,4,238,2017-02-25 09:36:09,http://walsh.name/hugh.pfannerstill,1.0000000000,183.128.67.113,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n2564,4,238,2017-02-27 14:13:39,http://bednar.name/rick.bashirian,0.0000000000,171.70.63.217,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n2565,4,238,2016-12-24 02:11:15,http://sawayn.info/francesca.halvorson,2.0000000000,213.173.204.154,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n2566,4,238,2017-02-28 12:35:20,http://schuster.biz/monique,1.0000000000,100.58.122.219,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n2567,4,238,2017-05-29 08:45:33,http://rohan.com/aaron,2.0000000000,228.62.99.73,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n2568,4,238,2017-01-20 16:25:38,http://ankunding.com/selena,2.0000000000,41.102.81.148,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n2569,4,238,2017-01-21 08:30:17,http://casper.info/edmund.berge,0.0000000000,135.175.106.238,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n2570,4,238,2017-05-17 07:01:31,http://mills.io/hilma.rice,0.0000000000,206.161.86.238,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n2571,4,238,2017-03-18 05:19:38,http://marks.co/kacie,1.0000000000,206.161.86.238,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n2572,4,238,2017-02-01 00:55:52,http://koepp.info/jakob,2.0000000000,147.217.116.92,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n1386,3,129,2017-03-24 02:53:34,http://runte.com/alberta,0.0000000000,190.229.212.181,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n1387,3,130,2017-05-08 03:33:49,http://gusikowski.io/vella_greenfelder,0.0000000000,48.30.229.215,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n1388,3,130,2017-05-21 04:17:14,http://eichmannhammes.io/glenna.gleichner,0.0000000000,178.222.46.111,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n1389,3,130,2017-04-05 09:33:11,http://little.com/jordi_kunze,0.0000000000,48.30.229.215,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n1390,3,130,2017-01-07 06:20:05,http://vonmueller.co/forrest,1.0000000000,207.183.44.217,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n1391,3,130,2017-05-19 06:04:44,http://ruel.co/malcolm,0.0000000000,219.243.74.128,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n1392,3,130,2017-03-04 21:06:19,http://keeling.io/hellen.beahan,1.0000000000,142.115.219.164,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n1393,3,130,2017-02-01 01:35:56,http://towne.net/charlene,0.0000000000,174.241.253.18,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n1394,3,130,2017-04-12 02:52:10,http://kling.com/dangelo.corwin,1.0000000000,48.30.229.215,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n1395,3,130,2017-04-11 20:01:23,http://mohr.net/rebeka_trantow,1.0000000000,159.126.57.66,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n1396,3,130,2017-02-05 18:47:22,http://kunde.net/shawna,1.0000000000,218.205.249.198,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n1397,3,130,2017-06-08 23:54:21,http://roobosinski.name/kamren.gottlieb,0.0000000000,217.36.85.81,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n1398,3,130,2017-01-24 23:59:51,http://mohr.co/maia,1.0000000000,221.29.144.166,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n1399,3,130,2017-06-09 10:58:48,http://turner.net/kenya.swaniawski,1.0000000000,164.69.201.39,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n1400,3,130,2016-12-24 01:55:28,http://schmidt.info/cameron.klein,0.0000000000,115.204.176.89,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n1401,3,131,2017-06-09 00:13:56,http://bergnaum.net/wiley,3.0000000000,216.238.223.10,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n1402,3,131,2017-02-26 03:44:44,http://damore.org/kristina,1.0000000000,73.50.23.80,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n1403,3,131,2017-02-14 11:26:07,http://moen.net/carroll_swaniawski,0.0000000000,87.68.195.35,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n1404,3,131,2016-12-23 06:10:45,http://gibson.net/milford.schiller,2.0000000000,43.87.66.46,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n1405,3,131,2017-05-13 00:02:23,http://hettingerwisozk.co/aliza_boyle,2.0000000000,216.238.223.10,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n1406,3,131,2017-04-22 11:08:54,http://funk.biz/adelle.rath,1.0000000000,109.12.47.27,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n1407,3,131,2017-06-01 17:28:34,http://ziemann.com/kayli.oreilly,1.0000000000,23.86.247.148,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n1408,3,131,2017-06-03 09:45:03,http://andersonromaguera.net/vernie_schmitt,2.0000000000,149.133.43.209,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n1409,3,131,2017-03-11 22:41:06,http://krajcikwalter.com/reinhold.kling,1.0000000000,197.94.159.119,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n1410,3,131,2017-04-10 05:04:49,http://harveywaters.net/jefferey_kunde,1.0000000000,43.87.66.46,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n1411,3,131,2017-01-22 13:39:31,http://gleason.org/deshawn,0.0000000000,109.12.47.27,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n1412,3,131,2017-03-18 04:57:00,http://stoltenberg.io/gillian,2.0000000000,150.200.120.68,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n1413,3,131,2017-06-08 07:02:59,http://lubowitz.org/virgil.white,2.0000000000,150.231.205.225,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n1414,3,131,2017-04-16 00:24:57,http://cummings.co/quentin,1.0000000000,73.50.23.80,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n1415,3,131,2017-06-11 16:57:12,http://beahan.biz/toni,0.0000000000,149.44.193.213,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n1416,3,131,2017-05-24 05:49:29,http://ko.com/hattie_keler,3.0000000000,190.111.202.213,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n1417,3,131,2017-05-14 05:19:05,http://yundt.biz/jose_ward,2.0000000000,149.133.43.209,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n1418,3,131,2017-05-03 01:04:36,http://dubuque.name/clair.nolan,2.0000000000,150.200.120.68,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n1419,3,132,2016-12-13 10:56:16,http://fritschrodriguez.org/anahi,0.0000000000,152.118.62.27,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n1420,3,132,2017-05-23 08:56:53,http://collier.org/eldridge,0.0000000000,45.252.254.246,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n1421,3,132,2017-06-05 10:00:58,http://altenwerthwunsch.net/bryana.zulauf,0.0000000000,170.28.127.189,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n1422,3,132,2017-05-25 20:55:58,http://padberglebsack.org/myrtice_konopelski,0.0000000000,208.19.231.234,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n1423,3,132,2017-01-07 01:01:18,http://durgancrona.co/ottis,0.0000000000,208.19.231.234,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n1424,3,132,2017-03-06 21:58:27,http://wisozkkling.com/xander_pollich,0.0000000000,169.248.240.41,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n1425,3,132,2017-02-07 14:27:47,http://pollichledner.net/josianne.sporer,0.0000000000,10.143.72.213,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n1426,3,132,2017-06-02 13:18:45,http://macejkovic.com/treie.terry,0.0000000000,234.61.199.61,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n1427,3,132,2017-06-04 06:14:19,http://haagwolff.com/arnoldo_torphy,0.0000000000,87.68.10.254,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n1428,3,132,2017-04-12 22:54:30,http://anderson.net/alycia,0.0000000000,166.53.86.25,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n1429,3,133,2017-05-29 14:10:28,http://armstrongherman.co/jazlyn,0.0000000000,12.249.173.224,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n1430,3,133,2017-05-14 07:57:28,http://barton.name/terence,2.0000000000,199.36.136.61,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n1431,3,134,2017-04-15 18:00:59,http://schmitt.io/judy_bosco,3.0000000000,144.25.165.86,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n1432,3,134,2017-02-07 23:40:53,http://bogisichpowlowski.name/antwan.effertz,0.0000000000,46.43.193.176,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n1433,3,134,2017-03-18 18:51:57,http://watsica.com/jewel,1.0000000000,142.45.15.54,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n1434,3,134,2017-03-07 22:22:10,http://fay.org/francesca,0.0000000000,76.214.60.18,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n1435,3,134,2017-05-24 15:56:32,http://beahan.com/ryann.johns,3.0000000000,26.72.87.214,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n1436,3,134,2017-06-01 06:39:12,http://reichel.com/reyna,3.0000000000,147.31.204.47,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n1437,3,134,2016-12-17 00:29:34,http://hilll.co/alanna.hamill,0.0000000000,22.122.7.161,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n2573,4,238,2017-03-26 02:00:30,http://beer.name/marco,1.0000000000,213.173.204.154,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n2574,4,238,2017-05-23 16:39:42,http://kleinzboncak.com/roel,2.0000000000,192.210.208.147,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n2575,4,239,2017-04-29 22:28:29,http://kuvalis.info/tavares,2.0000000000,75.205.40.131,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n2576,4,239,2017-03-22 20:09:07,http://heathcote.io/hyman.tremblay,2.0000000000,142.211.2.213,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n2577,4,239,2017-05-21 16:41:35,http://upton.co/reymundo.ebert,1.0000000000,17.173.152.38,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n2578,4,239,2017-06-08 07:02:53,http://kuhnhegmann.com/candido.rippin,2.0000000000,22.212.2.38,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n2579,4,239,2017-03-28 06:53:07,http://runtepfannerstill.biz/taryn.padberg,0.0000000000,203.243.114.60,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n2580,4,239,2017-06-08 14:34:08,http://stoltenbergheathcote.com/eldon,0.0000000000,83.190.3.127,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n2581,4,239,2017-06-06 22:31:22,http://mills.com/lera,3.0000000000,83.190.3.127,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n2582,4,239,2017-05-03 04:25:02,http://larson.name/alphonso,0.0000000000,107.110.55.195,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n2583,4,239,2017-02-28 08:51:55,http://homenick.co/cullen.greenfelder,0.0000000000,137.196.16.251,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n2584,4,239,2017-05-28 21:47:30,http://ullrich.com/aurelia.gleason,0.0000000000,22.18.56.141,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n2585,4,239,2017-02-23 22:15:05,http://halvorsonko.info/cade,1.0000000000,225.218.9.173,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n2586,4,239,2017-05-14 06:53:25,http://gorczany.name/alberta,1.0000000000,214.103.183.130,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n2587,4,240,2017-03-01 07:47:31,http://roobstrosin.biz/reyna.thompson,0.0000000000,19.201.89.107,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n2588,4,240,2017-05-22 06:34:40,http://mohr.org/jarrett.stokes,1.0000000000,160.57.196.163,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n2589,4,240,2017-06-04 15:20:22,http://daniel.org/emmanuel.stracke,0.0000000000,93.166.110.162,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n2590,4,241,2017-05-21 15:48:50,http://murphy.net/aunta_gottlieb,0.0000000000,126.245.140.197,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n2591,4,241,2017-01-25 08:55:50,http://abernathy.io/sandrine_mante,0.0000000000,18.171.7.177,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n2592,4,241,2017-06-01 04:11:55,http://ferry.com/devonte_mclaughlin,0.0000000000,7.132.42.172,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n2593,4,241,2016-12-23 04:59:53,http://bednaremmerich.biz/brent,1.0000000000,183.191.114.202,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n2594,4,241,2017-06-02 19:52:14,http://kihnfisher.name/floy,2.0000000000,208.217.126.246,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n2595,4,241,2017-06-02 10:37:23,http://goodwin.biz/ethelyn,3.0000000000,194.243.73.245,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n2596,4,241,2017-01-30 04:38:00,http://ferry.io/joesph.kuhlman,3.0000000000,253.98.183.20,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n2597,4,241,2017-01-06 08:41:25,http://stoltenberg.org/taryn_fahey,0.0000000000,18.171.7.177,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n2598,4,241,2016-12-28 07:52:59,http://walter.com/rocky,0.0000000000,132.165.74.139,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n2599,4,241,2017-02-27 01:25:55,http://will.biz/alana_gutkowski,0.0000000000,223.34.84.244,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n2600,4,241,2017-02-03 16:40:17,http://carterwisozk.co/javonte.mclaughlin,2.0000000000,119.82.45.120,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n2601,4,241,2017-03-25 13:58:30,http://gleichner.info/eriberto,0.0000000000,66.10.94.107,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n2602,4,241,2017-02-06 00:47:08,http://tromp.co/francis_stamm,3.0000000000,116.67.33.224,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n2603,4,242,2017-05-04 15:31:41,http://runtebuckridge.name/beie,0.0000000000,124.231.249.197,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n2604,4,242,2016-12-25 10:05:02,http://aufderhar.com/isabelle,1.0000000000,204.69.46.138,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n2605,4,242,2017-02-02 06:05:28,http://rosenbaumframi.com/alvera_bailey,0.0000000000,223.22.182.16,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n2606,4,242,2017-03-04 09:51:06,http://metz.name/jackie,0.0000000000,138.175.254.207,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n2607,4,242,2017-04-25 08:21:18,http://deckowkuphal.name/edmund_reilly,0.0000000000,144.141.213.141,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n2608,4,242,2017-03-22 07:36:41,http://hintzwintheiser.info/dangelo_mcdermott,0.0000000000,220.31.18.130,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n2609,4,242,2017-03-30 03:09:55,http://greenfelder.net/vincenza,0.0000000000,165.170.225.4,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n2610,4,242,2017-05-19 07:39:06,http://trompsipes.biz/devante_predovic,1.0000000000,242.2.216.29,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n2611,4,242,2017-02-23 00:04:35,http://hayes.com/emelie,1.0000000000,68.187.181.13,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n2612,4,242,2017-03-05 05:36:50,http://bechtelar.org/allene.mann,0.0000000000,204.69.46.138,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n2613,4,243,2017-04-06 22:06:08,http://mcculloughpouros.net/elise,3.0000000000,230.105.148.155,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n2614,4,243,2017-05-01 21:53:20,http://greenholt.net/morris.bashirian,3.0000000000,193.231.143.170,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n2615,4,243,2017-04-27 00:30:28,http://grimesroob.com/antwan_batz,0.0000000000,28.56.119.28,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n2616,4,243,2017-03-25 08:23:45,http://bradtke.info/hipolito.miller,0.0000000000,28.56.119.28,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n2617,4,243,2017-03-16 01:34:14,http://dietrich.io/grace,2.0000000000,124.199.26.247,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n2618,4,244,2017-04-28 13:23:24,http://hansen.org/gail_reilly,0.0000000000,182.34.249.82,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n2619,4,244,2017-04-05 09:19:38,http://mayert.io/marshall.zboncak,1.0000000000,79.222.214.242,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n2620,4,244,2017-03-13 18:26:03,http://mosciskigleason.org/rashad,0.0000000000,98.147.61.116,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n2621,4,245,2017-04-12 08:03:04,http://pfannerstill.net/amaya_kiehn,1.0000000000,175.59.52.63,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n2622,4,245,2017-01-12 17:34:27,http://treutel.org/lera,1.0000000000,175.59.52.63,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n2623,4,245,2017-03-11 22:30:38,http://pagac.org/maurine_rohan,1.0000000000,224.163.203.58,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n2624,4,245,2017-01-25 13:44:50,http://johnston.info/joan_fadel,1.0000000000,173.31.9.140,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n1438,3,134,2017-02-17 09:15:48,http://cartwrightrau.co/tatum.kemmer,1.0000000000,82.218.105.28,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n1439,3,134,2017-03-26 21:22:09,http://mraz.org/richmond.schinner,2.0000000000,72.124.59.45,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n1440,3,134,2017-01-05 09:05:01,http://bayerbartell.com/reyna,2.0000000000,26.72.87.214,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n1441,3,134,2017-06-12 06:46:07,http://langworth.biz/faye_anderson,2.0000000000,183.46.42.136,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n1442,3,134,2017-04-15 16:07:40,http://auer.org/earnest,3.0000000000,138.238.61.210,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n1443,3,134,2017-05-13 06:07:38,http://harvey.org/maxine.botsford,0.0000000000,72.124.59.45,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n1444,3,134,2017-06-07 08:48:49,http://blick.info/stanford,0.0000000000,110.119.144.137,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n1445,3,134,2017-02-18 21:59:29,http://white.info/johnson_thiel,3.0000000000,118.192.159.250,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n1446,3,134,2017-04-21 06:15:54,http://rice.biz/pietro.leuschke,1.0000000000,124.189.71.202,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n1447,3,134,2017-03-17 10:21:11,http://lindgrenhickle.io/eleanora,2.0000000000,161.107.204.3,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n1448,3,134,2017-05-29 17:11:37,http://kshlerin.biz/wilhelmine,0.0000000000,147.31.204.47,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n1449,3,135,2017-04-20 18:17:55,http://zemlak.info/madilyn_batz,3.0000000000,241.86.229.87,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n1450,3,135,2017-01-31 17:02:07,http://dickens.info/andres.schuster,2.0000000000,28.61.26.192,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n1451,3,135,2017-03-07 01:51:40,http://boehm.biz/carley.corkery,0.0000000000,32.163.161.14,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n1452,3,135,2017-03-19 12:59:30,http://littelupton.io/destinee,2.0000000000,28.61.26.192,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n1453,3,135,2017-02-10 10:35:12,http://nicolas.io/celestino,2.0000000000,8.226.40.193,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n1454,3,135,2017-01-16 11:45:03,http://goldner.io/emanuel_langosh,1.0000000000,133.129.245.237,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n1455,3,135,2017-02-24 17:40:45,http://corkery.co/sylvia,1.0000000000,181.222.93.101,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n1456,3,135,2017-04-15 23:54:51,http://nienow.com/nasir,2.0000000000,15.206.99.104,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n1457,3,135,2017-01-10 00:13:11,http://nolansanford.co/green.carter,0.0000000000,75.194.71.113,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n1458,3,135,2017-03-16 12:18:02,http://altenwerthbeer.io/devon_kovacek,0.0000000000,97.172.15.81,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n1459,3,135,2017-05-25 00:18:01,http://zemlak.biz/doyle,3.0000000000,58.208.184.97,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n1460,3,135,2017-04-13 16:46:25,http://kovacek.info/brenden_kuhic,3.0000000000,5.213.129.40,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n1461,3,135,2016-12-27 06:32:04,http://graham.io/rupert,0.0000000000,230.104.104.239,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n1462,3,135,2017-03-16 02:03:43,http://padbergmcdermott.info/hellen.hintz,1.0000000000,53.101.140.128,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n1463,3,136,2017-03-27 07:13:53,http://gleichner.info/ashly_bode,,170.118.170.86,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n1464,3,136,2017-02-26 21:34:41,http://kerluke.co/idell_leuschke,,217.158.167.186,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n1465,3,136,2017-03-03 04:52:07,http://lesch.co/rusty_goodwin,,176.92.17.250,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n1466,3,136,2017-05-31 20:39:03,http://lebsackjaskolski.com/jefferey,,22.37.134.195,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n1467,3,136,2017-03-03 01:54:42,http://gorczany.co/dana.franecki,,27.98.183.95,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n1468,3,136,2016-12-19 02:14:57,http://kemmertowne.com/evalyn,,176.92.17.250,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n1469,3,136,2017-01-12 02:58:05,http://towne.info/amya,,165.143.73.226,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n1470,3,136,2017-01-01 21:44:30,http://mclaughlindonnelly.info/gladyce.ward,,40.129.46.136,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n1471,3,136,2017-01-21 01:05:18,http://barton.com/manuel,,198.25.4.91,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n1472,3,136,2017-03-08 19:54:50,http://kleinshanahan.co/rupert_wehner,,222.39.85.46,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n1473,3,136,2017-04-29 23:31:47,http://considineferry.com/woodrow_moore,,170.118.170.86,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n1474,3,136,2017-02-16 04:26:47,http://lockman.net/otho.grimes,,198.25.4.91,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n1475,3,136,2017-05-28 05:30:18,http://funk.co/geovanny_pagac,,43.198.114.153,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n1476,3,136,2017-01-04 18:49:54,http://powlowski.net/annamae_mueller,,89.20.13.104,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n1477,3,136,2017-03-29 17:51:57,http://collierpurdy.com/edgar,,170.118.170.86,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n1478,3,136,2017-05-20 15:40:04,http://thiel.name/yasmin_braun,,11.138.106.168,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n1479,3,136,2017-01-05 23:49:46,http://willms.info/alia,,232.96.201.173,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n1480,3,136,2016-12-22 10:49:55,http://okeefetromp.org/camila_kunze,,97.83.164.244,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n1481,3,136,2017-03-27 15:10:14,http://okeefe.biz/calista,,170.118.170.86,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n1482,3,137,2017-05-26 07:50:40,http://treutel.com/heidi_abernathy,,170.112.63.150,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n1483,3,137,2017-04-29 07:23:37,http://senger.org/yesenia_ebert,,191.188.108.136,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n1484,3,137,2017-03-11 04:11:08,http://jacobi.net/bridget_farrell,,83.176.18.153,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n1485,3,137,2017-03-26 09:59:03,http://schuppewilliamson.co/jamil_donnelly,,66.248.240.114,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n1486,3,137,2017-02-11 20:59:13,http://kautzer.co/allan.huels,,252.147.120.169,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n1487,3,137,2017-04-07 12:35:30,http://renner.io/jodie,,56.79.186.246,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n1488,3,137,2017-02-04 21:50:53,http://satterfield.info/jasper,,70.36.170.76,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n1489,3,137,2017-04-20 08:34:27,http://rolfson.co/bertrand_rempel,,69.213.114.123,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n1490,3,137,2017-04-17 23:05:09,http://terry.org/cleta.schiller,,124.137.231.49,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n2625,4,245,2017-05-06 06:52:31,http://spinkashields.info/cheyanne.kunze,2.0000000000,56.53.163.114,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n2626,4,245,2017-01-02 09:28:53,http://cruickshank.name/wyman_nicolas,0.0000000000,5.95.163.74,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n2627,4,245,2017-04-06 23:26:11,http://treutelrice.net/wanda,1.0000000000,100.151.222.153,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n2628,4,245,2017-01-12 07:29:23,http://larkinkovacek.co/leila.sipes,2.0000000000,150.116.181.126,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n2629,4,245,2017-05-23 05:28:56,http://lind.biz/freida,0.0000000000,108.216.253.160,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n2630,4,245,2017-03-10 13:41:07,http://nienow.biz/erwin,2.0000000000,8.152.186.247,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n2631,4,245,2017-03-17 01:11:29,http://sengerjaskolski.org/elroy.abbott,0.0000000000,104.56.40.224,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n2632,4,245,2016-12-29 05:36:04,http://bechtelar.name/noemie_pacocha,1.0000000000,171.138.152.67,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n2633,4,245,2017-03-03 01:56:20,http://flatley.io/santino_bradtke,0.0000000000,142.113.2.82,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n2634,4,245,2017-04-16 20:45:36,http://heaneystiedemann.biz/simone.baumbach,0.0000000000,108.216.253.160,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n2635,4,246,2017-01-07 05:07:22,http://robel.biz/merl,2.0000000000,61.226.131.19,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n2636,4,246,2017-01-20 03:49:52,http://monahanhuels.co/sim,0.0000000000,14.5.125.162,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n2637,4,246,2017-04-08 22:54:02,http://ritchie.io/bryana,0.0000000000,101.220.226.220,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n2638,4,246,2017-01-26 16:38:21,http://stroman.co/zander_gutmann,0.0000000000,252.60.12.251,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n2639,4,246,2017-04-18 21:35:18,http://swift.name/rashawn,2.0000000000,14.119.186.243,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n2640,4,246,2017-03-16 10:52:05,http://mclaughlin.org/clemmie.torphy,0.0000000000,93.36.29.82,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n2641,4,246,2017-05-09 15:38:06,http://schmeler.io/talon,2.0000000000,57.235.205.189,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n2642,4,246,2017-02-10 01:13:53,http://stanton.biz/esmeralda.rau,0.0000000000,46.246.183.58,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n2643,4,246,2017-04-08 16:22:32,http://lowe.org/angelina.dare,2.0000000000,174.38.20.197,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n2644,4,246,2017-06-13 06:47:35,http://ruel.name/garett_halvorson,2.0000000000,136.189.75.12,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n2645,4,246,2017-02-08 19:22:48,http://kemmer.io/gayle.walter,2.0000000000,140.181.79.20,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n2646,4,247,2017-02-03 02:42:53,http://schoen.io/jerome.senger,1.0000000000,14.85.43.242,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n2647,4,247,2017-05-05 07:40:19,http://murraysmith.biz/alia,2.0000000000,92.134.110.179,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n2648,4,247,2017-02-18 20:24:32,http://murazik.net/retha,0.0000000000,113.216.32.162,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n2649,4,247,2017-06-09 07:09:40,http://jastfahey.biz/melia_west,1.0000000000,18.140.125.194,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n2650,4,247,2017-04-11 23:57:11,http://faymacejkovic.co/hellen,1.0000000000,213.115.219.208,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n2651,4,247,2017-05-22 00:32:09,http://harber.name/sherwood,2.0000000000,32.84.172.220,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n2652,4,247,2017-04-14 20:36:08,http://schneider.io/vincenza,3.0000000000,236.15.49.212,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n2653,4,247,2016-12-19 13:49:52,http://crookanford.name/raven_jerde,2.0000000000,36.78.32.240,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n2654,4,247,2017-03-06 22:05:33,http://beer.co/eriberto,1.0000000000,204.111.60.3,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n2655,4,248,2017-05-09 03:32:01,http://abshirebednar.io/bradly,2.0000000000,152.202.200.40,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n2656,4,248,2016-12-31 11:37:01,http://bauch.info/davonte_flatley,2.0000000000,114.203.226.44,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n2657,4,248,2017-02-10 11:12:10,http://schimmelabernathy.io/casey.hegmann,3.0000000000,152.127.133.21,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n2658,4,248,2017-03-02 00:49:01,http://greenfeldercrooks.biz/lila,2.0000000000,66.203.111.86,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n2659,4,248,2017-05-20 05:29:19,http://ferryheel.io/jaydon,3.0000000000,66.203.111.86,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n2660,4,248,2017-03-08 17:47:02,http://mcdermott.com/krystal.pouros,1.0000000000,97.216.131.116,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n2661,4,248,2017-01-22 09:06:51,http://senger.net/gretchen,1.0000000000,22.149.177.7,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n2662,4,248,2017-02-20 13:59:26,http://douglas.net/modesto,1.0000000000,243.154.126.57,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n2663,4,248,2017-01-25 08:22:59,http://rohan.org/trenton,3.0000000000,233.46.160.139,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n2664,4,248,2017-03-11 05:59:27,http://kohler.info/rylan.johnson,1.0000000000,40.224.254.212,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n2665,4,248,2017-01-20 06:30:11,http://stoltenberg.org/claria.kshlerin,0.0000000000,96.227.172.253,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n2666,4,248,2017-05-21 06:08:15,http://kutch.biz/bette_haley,3.0000000000,73.36.202.234,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n2667,4,249,2017-03-25 15:52:30,http://jastnitzsche.net/santiago,0.0000000000,49.23.163.128,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n2668,4,249,2017-03-16 09:55:04,http://gaylordparker.biz/eliezer,0.0000000000,248.182.87.119,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n2669,4,250,2016-12-25 07:31:49,http://kertzmannzieme.info/mckayla.watsica,2.0000000000,50.170.121.182,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n2670,4,250,2017-02-16 22:08:04,http://bosco.info/alvis_nader,1.0000000000,14.90.12.160,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n2671,4,250,2017-05-16 13:04:30,http://kochklocko.name/teresa_franecki,0.0000000000,67.185.163.155,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n2672,4,250,2017-05-12 14:33:30,http://denesikdoyle.com/hudson,1.0000000000,81.45.185.144,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n2673,4,251,2017-05-10 15:49:12,http://waltergleason.net/samanta,3.0000000000,213.33.106.9,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n2674,4,251,2017-05-29 19:39:01,http://armstrongbednar.org/jackeline,3.0000000000,68.238.79.20,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n2675,4,251,2017-05-22 01:36:17,http://schaefer.biz/kade_casper,1.0000000000,197.160.168.47,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n1491,3,137,2017-03-11 16:11:26,http://franecki.name/agustin_boehm,,133.55.116.145,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n1492,3,137,2016-12-15 00:13:32,http://hermiston.net/elijah,,202.86.130.173,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n1493,3,137,2017-03-02 17:31:48,http://hansen.biz/alphonso,,29.2.189.70,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n1494,3,137,2017-05-30 09:37:24,http://oreillymetz.io/kade_berge,,191.188.108.136,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n1495,3,137,2017-05-12 19:14:10,http://kuhn.co/vita,,104.196.82.62,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n1496,3,137,2017-03-27 10:23:53,http://stanton.net/jayda,,188.68.183.101,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n1497,3,137,2016-12-31 14:30:16,http://willms.info/dameon,,137.234.71.24,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n1498,3,137,2017-04-30 09:15:57,http://ko.biz/gabrielle.schneider,,133.55.116.145,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n1499,3,137,2017-06-07 11:56:33,http://klockobosco.name/cecile,,161.45.112.132,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n1500,3,138,2016-12-29 12:29:28,http://haleymacgyver.io/greta,,26.81.188.149,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n1501,3,138,2016-12-20 10:17:01,http://wolf.net/savanna,,92.221.21.97,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n1502,3,138,2017-01-11 18:06:43,http://huel.biz/eloisa.erdman,,49.243.85.121,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n1503,3,138,2017-03-07 01:15:48,http://heaney.io/creola.smith,,26.81.188.149,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n1504,3,138,2017-05-08 04:40:15,http://kuvalis.com/kaylie,,3.252.78.199,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n1505,3,138,2016-12-20 05:34:40,http://rippin.com/flo,,250.147.200.102,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n1506,3,138,2016-12-14 08:15:45,http://green.com/fritz,,26.147.241.94,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n1507,3,138,2017-05-23 23:00:43,http://roberts.info/zoila,,6.246.90.42,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n1508,3,138,2017-03-16 14:42:53,http://pricerice.net/mandy,,66.24.223.143,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n1509,3,138,2017-02-16 11:40:05,http://gorczanyweinat.io/katherine,,6.246.90.42,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n1510,3,138,2017-02-03 19:43:35,http://jaskolski.info/lily.medhurst,,218.93.158.104,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n1511,3,138,2016-12-16 18:45:05,http://predoviccronin.net/leopoldo_lebsack,,237.171.32.116,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n1512,3,138,2017-01-20 18:58:36,http://gibson.biz/elmore,,173.233.106.253,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n1513,3,139,2017-06-10 10:50:17,http://runolfsdottirmurray.org/nakia.raynor,,227.153.58.120,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n1514,3,139,2017-04-11 14:55:22,http://hand.net/anabel_smitham,,181.104.56.250,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n1515,3,139,2017-04-20 11:56:37,http://kuhncormier.net/darwin_hermann,,214.164.229.134,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n1516,3,139,2017-03-09 21:45:40,http://jerde.net/fermin,,162.93.236.100,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n1517,3,139,2017-01-17 23:18:00,http://veum.net/norval.gulgowski,,186.124.152.28,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n1518,3,139,2017-06-10 21:11:53,http://pacocha.org/guido_donnelly,,101.85.228.20,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n1519,3,140,2017-01-21 01:31:48,http://becker.info/javier,,128.241.168.21,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n1520,3,140,2017-02-12 09:09:51,http://borerkozey.org/laurie.prohaska,,145.220.60.248,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n1521,3,140,2017-05-01 10:32:47,http://daniel.org/rae_white,,65.130.85.36,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n1522,3,140,2017-03-13 10:20:44,http://koepphyatt.name/domenic,,128.241.168.21,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n1523,3,140,2017-05-27 20:15:31,http://mertzkerluke.org/clementina.dach,,56.59.220.7,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n1524,3,140,2017-04-15 07:02:07,http://goodwinmurray.io/turner,,237.140.171.225,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n1525,3,140,2017-05-16 02:41:06,http://hartmann.co/maymie_douglas,,168.36.254.21,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n1526,3,140,2016-12-28 05:33:41,http://vonrueden.biz/myrtle,,47.3.209.151,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n1527,3,140,2017-06-03 21:26:56,http://botsfordheller.info/jacquelyn,,237.189.193.242,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n1528,3,140,2017-01-28 20:02:47,http://ferry.co/velva,,66.45.117.224,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n1529,3,140,2017-02-11 08:57:23,http://helleraufderhar.com/porter,,240.235.134.138,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n1530,3,140,2017-05-10 20:57:09,http://hauck.net/idell,,22.24.177.25,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n1531,3,141,2016-12-17 03:46:00,http://kemmer.net/pascale,,148.242.141.89,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n1532,3,141,2017-04-12 12:34:46,http://grady.biz/halle,,60.193.83.165,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n1533,3,141,2017-01-24 05:26:35,http://effertzko.co/lucious.dare,,45.164.167.192,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n1534,3,141,2017-05-12 22:51:54,http://swift.info/earnest,,204.171.34.189,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n1535,3,141,2017-05-06 17:19:47,http://schumm.io/lenna,,235.174.13.211,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n1536,3,141,2017-02-22 19:58:41,http://ratke.name/estelle_bergnaum,,61.19.8.222,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n1537,3,141,2017-01-21 14:29:02,http://volkman.org/annetta.rolfson,,100.184.180.4,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n1538,3,141,2016-12-20 19:06:26,http://reichelraynor.io/aleandra.borer,,248.180.108.194,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n1539,3,141,2017-01-02 18:28:57,http://rauhilll.org/simeon.monahan,,221.191.43.72,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n1540,3,141,2017-03-09 00:21:07,http://breitenberg.io/esperanza,,21.52.81.158,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n1541,3,141,2017-05-28 05:22:08,http://wisozk.com/doug_kunde,,56.247.139.240,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n1542,3,141,2017-05-25 06:54:39,http://turcotte.com/moshe,,241.59.183.181,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n1543,3,141,2017-02-07 02:41:09,http://schaeferrosenbaum.co/maximus,,47.16.199.73,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n1544,3,141,2017-03-03 19:12:38,http://dubuqueblock.co/henderson.steuber,,230.162.153.96,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n1545,3,141,2017-03-14 17:58:34,http://kunde.biz/joan,,235.174.13.211,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n1546,3,141,2017-01-30 20:28:43,http://okon.io/nathanial.wyman,,186.244.68.68,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n2676,4,251,2017-01-19 03:12:08,http://marquardtjacobs.net/kieran,1.0000000000,31.107.120.98,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n2677,4,251,2017-04-06 13:02:20,http://osinskidouglas.io/freda,1.0000000000,92.72.100.136,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n2678,4,251,2017-05-10 19:42:11,http://feest.io/johnny,1.0000000000,31.107.120.98,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n2679,4,251,2017-02-27 04:54:43,http://jacobs.co/orval_cronin,0.0000000000,49.211.128.33,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n2680,4,251,2017-04-30 13:36:55,http://heidenreichjast.info/bernhard,0.0000000000,16.115.121.242,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n2681,4,251,2016-12-15 01:14:19,http://dietrich.name/deangelo_gleason,0.0000000000,96.124.98.230,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n2682,4,251,2017-06-01 08:59:50,http://wiegand.io/travon_bins,2.0000000000,61.130.239.89,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n2683,4,251,2017-06-12 06:37:26,http://donnelly.org/jennie,0.0000000000,203.252.127.25,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n2684,4,251,2017-05-10 08:22:53,http://luettgen.com/walton.cruickshank,3.0000000000,63.156.167.124,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n2685,4,251,2017-01-01 08:34:22,http://zulauf.name/sadye_monahan,2.0000000000,168.133.63.148,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n2686,4,251,2017-05-22 10:47:14,http://kuhn.biz/yadira,0.0000000000,30.151.93.247,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n2687,4,251,2017-01-22 15:09:30,http://prohaska.info/ezekiel.steuber,3.0000000000,232.199.62.15,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n2688,4,251,2017-06-05 20:41:49,http://schroeder.org/alena.keeling,1.0000000000,116.9.37.217,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n2689,4,251,2017-03-24 09:59:26,http://collins.name/antonio,3.0000000000,206.173.231.215,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n2690,4,252,2016-12-31 19:54:43,http://homenickhettinger.info/leanne,0.0000000000,28.174.140.215,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n2691,4,252,2017-05-12 14:15:48,http://gerhold.biz/eudora.rolfson,0.0000000000,50.100.13.131,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n2692,4,252,2017-03-20 19:13:40,http://kaulke.org/francisca,0.0000000000,200.156.187.68,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n2693,4,252,2016-12-29 08:11:05,http://powlowskikerluke.io/brittany,0.0000000000,11.124.188.78,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n2694,4,252,2017-03-05 10:27:46,http://kulasgleichner.biz/aimee,0.0000000000,70.199.102.13,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n2695,4,252,2017-02-08 13:42:25,http://pacocha.org/bud,0.0000000000,133.207.81.168,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n2696,4,252,2017-01-10 23:41:58,http://lakin.net/cale_kirlin,0.0000000000,104.75.41.44,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n2697,4,252,2017-02-23 11:49:42,http://moore.org/reagan,0.0000000000,30.34.68.137,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n2698,4,252,2017-04-11 15:09:16,http://gislason.io/dustin.mohr,0.0000000000,51.119.125.130,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n2699,4,252,2017-04-14 14:38:55,http://stracke.biz/tiffany.keeling,0.0000000000,157.203.194.77,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n2700,4,253,2017-05-08 14:06:33,http://schultz.name/marlin,2.0000000000,137.146.143.247,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n2701,4,253,2017-06-02 00:25:03,http://krajcikhane.net/cathrine_ko,1.0000000000,47.191.112.217,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n2702,4,253,2017-01-19 06:06:26,http://fisher.co/zoila_oreilly,0.0000000000,26.220.132.238,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n2703,4,253,2017-06-04 06:32:43,http://casper.co/oral_zboncak,0.0000000000,234.26.209.20,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n2704,4,253,2017-01-16 08:52:14,http://schuppe.com/danika,0.0000000000,52.28.170.44,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n2705,4,254,2017-03-18 12:59:48,http://kihnfisher.info/wilber_zboncak,2.0000000000,67.178.60.163,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n2706,4,254,2017-03-29 17:55:36,http://wilderman.io/ruell,2.0000000000,198.75.177.79,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n2707,4,254,2017-04-04 23:46:48,http://borer.org/sabryna_goodwin,0.0000000000,59.56.146.178,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n2708,4,254,2017-02-02 23:55:30,http://paucek.com/beryl,0.0000000000,130.226.55.130,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n2709,4,254,2017-02-14 09:05:08,http://cole.com/bria_ruecker,1.0000000000,57.94.145.253,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n2710,4,254,2017-01-26 15:07:57,http://jones.name/ernestina.cremin,1.0000000000,101.161.146.14,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n2711,4,254,2016-12-22 16:14:55,http://goyettecormier.biz/ashleigh,2.0000000000,239.100.57.26,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n2712,4,254,2017-05-18 03:03:41,http://wolf.co/sage,2.0000000000,59.56.146.178,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n2713,4,254,2017-01-18 07:08:54,http://reynolds.com/brandt.bailey,2.0000000000,104.181.127.13,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n2714,4,254,2017-03-03 09:24:28,http://windlerschaden.biz/genesis,0.0000000000,101.161.146.14,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n2715,4,255,2017-05-08 09:47:55,http://schmidt.io/keeley_fay,0.0000000000,202.116.24.188,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n2716,4,255,2017-01-14 12:12:17,http://jacobson.org/hillard,0.0000000000,108.15.92.133,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n2717,4,255,2017-02-12 09:29:42,http://vandervort.org/nakia_weimann,1.0000000000,100.221.70.117,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n2718,4,255,2016-12-25 00:24:04,http://wisoky.org/darron,1.0000000000,108.15.92.133,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n2719,4,255,2017-01-15 02:05:29,http://cristmante.net/julian,0.0000000000,62.81.209.163,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n2720,4,255,2017-01-01 16:44:01,http://becker.org/manuel.bahringer,1.0000000000,247.185.34.132,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n2721,4,255,2017-05-18 21:04:07,http://denesikpouros.io/lura_heel,0.0000000000,42.251.178.114,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n2722,4,255,2017-05-15 23:10:41,http://oreillyzulauf.co/jevon.weber,0.0000000000,209.191.154.209,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n2723,4,255,2016-12-16 15:55:30,http://senger.biz/candice,1.0000000000,238.3.138.73,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n2724,4,255,2016-12-18 02:38:42,http://homenick.com/florida.buckridge,0.0000000000,92.140.247.192,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n2725,4,255,2017-01-23 05:47:13,http://dickinsoncremin.net/malika_harber,0.0000000000,34.180.4.211,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n2726,4,255,2017-06-08 03:56:11,http://dare.com/trudie,2.0000000000,179.148.102.246,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n1547,3,141,2017-04-13 15:39:29,http://boehm.biz/elenor.abbott,,34.161.17.72,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n1548,3,141,2017-01-03 07:21:40,http://raynor.biz/kory.nienow,,172.241.137.191,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n1549,3,141,2017-02-04 17:45:31,http://wisoky.com/jeromy_mills,,198.219.229.235,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n1550,3,142,2017-05-12 01:15:40,http://cormier.co/charlotte_farrell,,203.10.249.16,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n1551,3,142,2017-01-03 07:20:40,http://kertzmann.com/kiera,,116.78.115.248,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n1552,3,142,2017-02-21 13:18:18,http://beer.net/brionna,,67.202.84.251,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n1553,3,142,2017-03-12 17:10:50,http://trantow.io/emie,,223.116.24.71,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n1554,3,142,2017-06-06 13:25:04,http://windler.org/nelson,,47.66.119.45,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n1555,3,143,2017-02-26 09:29:39,http://anderson.name/camylle,,170.46.183.52,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n1556,3,143,2017-01-12 14:41:45,http://sauerframi.io/taylor_spinka,,200.19.25.102,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n1557,3,143,2017-06-09 23:13:39,http://vonrueden.com/rodrigo_hamill,,200.19.25.102,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n1558,3,143,2017-01-11 20:55:06,http://herman.name/madaline,,185.26.172.39,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n1559,3,143,2017-03-27 16:34:27,http://kshlerin.name/diamond,,241.73.9.188,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n1560,3,143,2016-12-28 06:56:16,http://bechtelarwill.info/lolita.johnson,,224.77.100.220,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n1561,3,143,2017-04-30 13:40:42,http://ortizjerde.org/dion,,126.155.135.150,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n1562,3,143,2017-01-29 04:26:31,http://pollichhaag.org/grant,,151.117.106.249,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n1563,3,143,2017-04-24 23:03:42,http://labadie.net/jane.aufderhar,,241.73.9.188,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n1564,3,143,2017-03-15 23:29:55,http://kutch.info/elsa,,218.18.52.179,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n1565,3,143,2017-02-16 00:58:01,http://purdy.biz/amya,,177.91.63.75,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n1566,3,144,2017-01-16 19:58:41,http://bode.co/orin,,230.141.47.235,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n1567,3,144,2017-02-15 06:49:11,http://mueller.biz/ralph,,2.153.161.54,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n1568,3,144,2017-02-05 06:45:16,http://bernhard.info/amara_sauer,,48.223.77.21,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n1569,3,145,2017-04-28 20:17:50,http://boehmwhite.io/danika,,154.116.95.164,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n1570,3,145,2017-04-18 14:11:30,http://koelpin.co/elinor,,17.56.199.174,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n1571,3,145,2016-12-26 20:21:08,http://hoppe.net/demetris,,234.49.209.78,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n1572,3,145,2017-01-19 10:15:16,http://larkin.info/sigurd,,251.110.79.249,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n1573,3,145,2017-03-15 23:18:18,http://treutelkeeling.biz/justen_conn,,106.49.243.37,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n1574,3,145,2017-02-07 20:47:57,http://hansen.co/cristian_prohaska,,251.110.79.249,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n1575,3,145,2017-03-14 12:18:00,http://pouros.biz/jodie.feest,,106.49.243.37,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n1576,3,145,2017-04-09 16:12:05,http://jakubowskiwolf.org/blanche_huel,,133.37.233.236,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n1577,3,145,2017-01-09 10:22:47,http://murazikmetz.net/nick.mosciski,,137.138.74.186,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n1578,3,145,2017-03-08 02:27:52,http://armstrong.biz/ilene,,234.49.209.78,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n1579,3,145,2017-04-27 04:09:36,http://thompson.biz/dewayne.pfeffer,,127.177.20.5,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n1580,3,145,2017-04-18 14:19:45,http://ornklocko.name/eladio.kautzer,,122.113.96.42,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n1581,3,145,2017-03-06 19:15:18,http://lind.com/isabella,,190.96.163.188,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n1582,3,145,2017-01-03 17:01:19,http://brown.biz/toney_mante,,81.22.125.253,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n1583,3,145,2017-02-18 16:16:44,http://lemke.name/haskell.cruickshank,,131.60.227.228,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n1584,3,145,2017-05-11 07:19:40,http://terry.name/jacques_emard,,127.177.20.5,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n1585,3,145,2017-05-22 00:00:46,http://grimes.name/marlee.abshire,,133.37.233.236,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n1586,3,145,2017-02-22 14:36:17,http://rueckerbogisich.io/cyril,,214.167.237.39,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n1587,3,145,2017-02-19 02:35:06,http://farrell.io/melvin,,17.56.199.174,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n1588,3,146,2017-02-15 19:21:07,http://altenwerth.name/laurence,0.0000000000,185.64.126.37,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n1589,3,146,2017-03-22 05:42:26,http://torpromaguera.com/lloyd,0.0000000000,45.37.55.45,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n1590,3,146,2016-12-13 13:06:03,http://osinski.io/winnifred,0.0000000000,225.207.5.162,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n1591,3,146,2017-01-06 16:04:26,http://hayes.com/anderson,0.0000000000,33.50.161.158,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n1592,3,146,2017-05-04 13:01:46,http://ratkeweimann.io/tyson.turner,0.0000000000,185.64.126.37,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n1593,3,147,2017-02-09 06:41:43,http://ullrichgottlieb.name/kristian,1.0000000000,127.73.173.173,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n1594,3,147,2017-04-21 09:46:33,http://ferrylakin.io/aiden,0.0000000000,110.244.225.61,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n1595,3,147,2017-01-01 19:43:24,http://littelsimonis.com/margaret.oberbrunner,0.0000000000,174.2.172.149,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n1596,3,147,2016-12-24 01:39:34,http://quitzonyost.com/clovis_bergstrom,0.0000000000,230.167.68.47,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n1597,3,148,2017-03-26 20:36:27,http://gislason.info/kenny_willms,0.0000000000,22.108.121.156,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n1598,3,148,2017-03-18 02:19:28,http://gleasonbeier.com/zack.wunsch,0.0000000000,233.162.94.143,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n1599,3,148,2017-05-06 10:03:42,http://wolf.biz/una.morar,0.0000000000,193.51.20.129,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n1600,3,148,2017-06-07 22:30:19,http://johnstonjenkins.io/kieran_nitzsche,0.0000000000,47.127.220.253,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n1601,3,148,2017-02-04 05:19:09,http://cummeratawilliamson.biz/oral_paucek,0.0000000000,193.51.20.129,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n2727,4,256,2017-05-25 17:23:54,http://cronin.info/elza_hackett,0.0000000000,54.64.128.8,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n2728,4,256,2017-04-13 06:00:05,http://little.info/austen,0.0000000000,99.150.241.122,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n2729,4,256,2017-02-15 15:44:27,http://fay.net/ara_borer,0.0000000000,125.13.42.136,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n2730,4,256,2017-04-06 22:54:53,http://moore.net/laura,0.0000000000,56.8.81.97,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n2731,4,256,2017-05-22 14:33:07,http://herman.org/hermann.waelchi,0.0000000000,218.120.173.33,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n2732,4,256,2016-12-25 10:13:17,http://rippin.org/stephania,0.0000000000,78.217.2.117,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n2733,4,256,2017-02-24 04:05:14,http://nienow.org/claudia,0.0000000000,143.229.95.192,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n2734,4,256,2017-03-13 23:46:04,http://parisian.io/kirstin,0.0000000000,151.130.161.150,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n2735,4,256,2017-03-06 12:39:39,http://mayer.org/birdie,0.0000000000,143.229.95.192,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n2736,4,256,2016-12-17 11:36:27,http://kohler.name/casandra_gorczany,0.0000000000,125.13.42.136,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n2737,4,256,2017-03-02 13:51:14,http://ruecker.co/durward,0.0000000000,172.168.189.222,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n2738,4,256,2017-03-31 11:45:22,http://osinski.co/cooper.cormier,0.0000000000,238.230.52.80,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n2739,4,256,2017-05-12 12:57:44,http://hettinger.name/will,0.0000000000,106.122.143.63,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n2740,4,256,2017-01-10 08:04:15,http://wunsch.org/enrique,0.0000000000,69.188.176.69,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n2741,4,256,2017-03-04 12:01:59,http://yundt.co/rhianna,0.0000000000,177.200.201.39,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n2742,4,256,2017-05-17 00:10:39,http://macgyver.org/winfield,0.0000000000,56.8.81.97,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n2743,4,256,2017-04-18 10:59:29,http://keler.name/jaden,0.0000000000,109.8.149.246,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n2744,4,256,2017-02-15 09:08:23,http://quitzonlarkin.co/domenic_sporer,0.0000000000,218.120.173.33,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n2745,4,257,2017-01-31 08:33:24,http://krajcikjakubowski.com/colby,,187.119.115.130,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n2746,4,257,2017-02-16 15:27:59,http://moriettereynolds.info/novella_johnston,,74.144.32.213,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n2747,4,257,2017-01-10 06:01:44,http://armstrong.co/jamel,,233.145.81.211,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n2748,4,257,2017-06-13 12:03:00,http://watsicaking.org/kathleen.streich,,80.95.53.184,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n2749,4,257,2017-02-01 09:45:26,http://metz.biz/chelsea.bayer,,252.198.110.23,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n2750,4,257,2017-03-04 00:48:43,http://kutchsawayn.com/estrella,,216.43.233.205,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n2751,4,257,2017-02-06 22:55:00,http://ritchie.co/idell,,116.245.7.183,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n2752,4,257,2017-05-21 12:37:05,http://gleichner.info/wiley.heidenreich,,166.216.54.242,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n2753,4,257,2017-02-22 18:54:31,http://jacobiwisoky.info/sunny,,18.124.18.195,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n2754,4,257,2017-02-23 15:50:09,http://hettingerbrown.info/edward,,51.136.240.25,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n2755,4,257,2017-05-22 11:39:19,http://bogisich.net/jamey_oconner,,252.198.110.23,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n2756,4,257,2017-05-24 22:54:28,http://rogahn.net/morton.kuhn,,68.79.137.162,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n2757,4,258,2016-12-19 20:38:49,http://ruelkuvalis.org/erik,,179.39.206.171,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n2758,4,258,2017-05-07 08:54:16,http://kuhlman.biz/kenyon.nader,,229.160.20.161,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n2759,4,258,2017-06-02 22:36:12,http://abbott.co/karianne,,33.8.179.51,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n2760,4,258,2017-03-09 07:20:47,http://pollichdare.net/nathanial_schuster,,102.220.121.247,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n2761,4,259,2017-02-07 00:37:59,http://rohan.co/beaulah_bashirian,,223.5.23.118,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n2762,4,259,2017-06-12 19:52:20,http://durganfay.name/nickolas.hand,,215.216.233.169,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n2763,4,259,2017-03-12 11:48:31,http://carroll.biz/savanah,,98.179.241.82,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n2764,4,259,2017-04-08 19:36:07,http://cremin.net/jamison.funk,,218.95.204.105,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n2765,4,259,2017-01-09 19:49:59,http://grahamgusikowski.net/esperanza_zieme,,218.95.204.105,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n2766,4,259,2017-04-29 18:32:55,http://kulas.net/lera,,5.221.111.165,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n2767,4,259,2017-01-24 00:50:23,http://kihn.io/erwin_mante,,5.221.111.165,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n2768,4,259,2016-12-29 20:19:31,http://prohaska.org/nella,,196.144.96.234,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n2769,4,259,2017-02-18 03:14:14,http://beahanmiller.org/blanca,,185.219.144.53,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n2770,4,259,2017-01-22 19:12:25,http://pollich.info/savanah_romaguera,,98.175.232.164,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n2771,4,259,2017-02-03 15:22:51,http://quitzonhintz.io/ivah.walker,,98.179.241.82,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n2772,4,260,2017-04-04 18:22:37,http://cormier.info/alexandre_kohler,,12.147.228.167,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n2773,4,260,2016-12-23 10:20:16,http://schadenjones.biz/maureen_price,,216.93.33.134,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n2774,4,260,2017-03-09 15:09:05,http://wardfeil.co/erica_hintz,,73.243.131.254,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n2775,4,260,2017-05-26 20:16:41,http://hickle.biz/micaela,,240.188.88.249,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n2776,4,260,2017-01-08 19:48:50,http://botsfordbotsford.name/krystina_oreilly,,172.215.15.107,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n2777,4,260,2016-12-18 01:26:40,http://romaguera.info/albertha,,240.188.88.249,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n2778,4,260,2017-04-30 07:55:41,http://kreiger.biz/simone,,66.90.203.59,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n2779,4,260,2017-02-20 13:24:22,http://schneider.com/marcia,,137.190.251.195,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n2780,4,260,2017-03-21 05:04:56,http://baumbach.biz/jennifer,,156.215.229.211,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n2781,4,260,2017-02-11 20:17:12,http://emmerich.org/mariela.raynor,,40.38.189.208,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n1602,3,148,2017-05-28 09:28:04,http://kunde.net/floyd.fisher,0.0000000000,22.108.121.156,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n1603,3,148,2017-04-25 09:47:44,http://brown.name/jayson,0.0000000000,131.232.24.32,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n1604,3,148,2017-02-02 19:36:34,http://bashirian.info/emmett,0.0000000000,76.102.30.212,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n1605,3,148,2017-05-15 15:38:43,http://boylejerde.co/morton,0.0000000000,135.95.233.79,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n1606,3,148,2017-03-04 10:30:10,http://ward.info/dariana,0.0000000000,131.232.24.32,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n1607,3,148,2017-01-16 03:57:44,http://hagenes.com/natasha,0.0000000000,238.204.144.63,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n1608,3,148,2017-04-25 20:35:59,http://willms.net/electa,0.0000000000,47.127.220.253,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n1609,3,148,2017-05-17 06:14:51,http://friesen.net/te,0.0000000000,171.225.232.132,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n1610,3,148,2017-02-14 06:02:41,http://beierbruen.name/mohammad,0.0000000000,204.12.250.211,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n1611,3,149,2017-03-19 16:16:20,http://mertzmaggio.net/kayden,2.0000000000,147.21.105.83,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n1612,3,149,2017-05-04 17:45:00,http://gaylordconsidine.net/allison_rutherford,1.0000000000,10.192.17.149,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n1613,3,149,2017-03-31 08:16:39,http://sporergreenfelder.biz/amber,0.0000000000,113.6.74.3,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n1614,3,149,2017-01-10 00:48:15,http://lynchdamore.io/alba,2.0000000000,200.213.239.170,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n1615,3,149,2017-02-28 20:18:47,http://bahringer.net/marquis.bergnaum,1.0000000000,221.242.98.102,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n1616,3,149,2017-01-24 12:33:20,http://stiedemann.io/humberto,3.0000000000,190.41.77.148,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n1617,3,149,2017-05-27 09:56:55,http://hammes.com/jeanie_romaguera,3.0000000000,39.19.70.113,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n1618,3,149,2017-05-06 23:29:01,http://hane.com/valentina,3.0000000000,39.19.70.113,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n1619,3,149,2017-02-13 15:13:38,http://cronaraynor.name/miouri,1.0000000000,189.66.65.55,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n1620,3,149,2017-05-27 12:04:01,http://mohr.co/pearline,1.0000000000,91.246.6.246,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n1621,3,149,2016-12-29 06:33:08,http://pfeffer.com/johan_beatty,3.0000000000,189.66.65.55,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n1622,3,150,2017-02-06 11:20:26,http://vonrueden.io/kieran,2.0000000000,182.89.210.50,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n1623,3,150,2017-03-23 23:44:05,http://bartoletti.io/aurelie.dubuque,2.0000000000,231.221.136.217,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n1624,3,150,2017-03-15 09:27:24,http://predovicbednar.name/dustin,0.0000000000,148.242.132.119,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n1625,3,150,2017-04-28 16:14:39,http://schamberger.co/rhoda.terry,1.0000000000,179.223.68.220,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n1626,3,150,2017-01-13 22:49:01,http://cummerata.net/herta.langosh,0.0000000000,230.188.98.105,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n1627,3,150,2017-04-09 05:34:23,http://emard.biz/eloy,0.0000000000,248.75.80.68,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n1628,3,150,2017-06-02 00:04:28,http://kutch.io/myah,1.0000000000,230.188.98.105,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n1629,3,150,2017-05-14 12:36:58,http://tillman.info/hope,2.0000000000,114.231.176.161,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n1630,3,150,2017-05-20 19:58:13,http://keeblershields.name/josiah.hickle,0.0000000000,52.48.194.115,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n1631,3,150,2017-05-31 13:46:41,http://balistreriabshire.co/jonatan,1.0000000000,163.204.165.183,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n1632,3,150,2017-02-10 11:11:43,http://zulauf.org/raegan.wolff,1.0000000000,148.242.132.119,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n1633,3,150,2017-02-25 06:31:17,http://huelsokuneva.io/mason_hermiston,1.0000000000,202.196.134.178,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n1634,3,150,2017-04-29 18:31:26,http://doyle.name/dovie,2.0000000000,222.32.67.65,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n1635,3,150,2017-04-09 04:51:22,http://volkmanjakubowski.org/gavin.green,1.0000000000,250.155.8.46,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n1636,3,150,2017-03-08 17:48:01,http://mrazschmidt.biz/flavio,2.0000000000,200.77.18.194,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n1637,3,150,2017-01-22 12:40:25,http://wuckert.io/aisha,2.0000000000,52.48.194.115,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n1638,3,150,2017-01-04 19:58:51,http://schamberger.net/fredy,0.0000000000,130.243.234.14,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n1639,3,150,2017-05-11 04:22:40,http://casper.net/marcus,0.0000000000,174.150.106.7,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n1640,3,150,2017-02-21 00:38:00,http://batzwilderman.biz/dawn.bernhard,0.0000000000,202.196.134.178,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n1641,3,150,2017-02-16 16:21:38,http://bradtke.info/heaven,0.0000000000,227.228.122.187,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n1642,3,151,2017-04-27 03:26:50,http://veumreichel.com/jacquelyn.mueller,0.0000000000,111.101.40.92,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n1643,3,151,2017-03-11 21:13:58,http://roob.net/britney.conroy,1.0000000000,148.254.14.220,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n1644,3,151,2017-03-20 04:49:12,http://west.co/brayan.emmerich,1.0000000000,22.115.73.77,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n1645,3,151,2017-04-27 07:07:16,http://brekke.co/meghan.mcglynn,1.0000000000,189.120.93.243,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n1646,3,151,2017-03-01 09:06:42,http://schmidt.info/dorcas_grady,0.0000000000,156.37.254.110,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n1647,3,151,2017-05-24 03:47:30,http://swift.biz/abagail,1.0000000000,183.111.130.148,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n1648,3,151,2017-05-31 21:04:53,http://hicklearmstrong.info/stevie,1.0000000000,86.250.147.55,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n1649,3,151,2017-03-08 00:13:31,http://kuhickozey.io/malvina_glover,0.0000000000,212.157.53.80,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n1650,3,151,2017-05-14 20:45:00,http://blanda.org/allen,0.0000000000,25.144.202.211,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n1651,3,151,2017-04-21 01:43:52,http://sawaynbeier.biz/ruby,0.0000000000,139.215.121.63,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n1652,3,151,2017-01-22 23:21:26,http://lueilwitz.co/stanford,1.0000000000,167.195.191.167,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n2782,4,261,2017-02-22 12:05:03,http://herzog.name/gerardo_schmitt,,97.243.27.252,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n2783,4,261,2017-01-16 07:47:42,http://williamson.org/carroll.hilpert,,140.7.233.63,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n2784,4,261,2016-12-20 04:47:49,http://pfannerstill.org/jennings,,162.83.151.157,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n2785,4,261,2017-01-24 10:52:46,http://schumm.biz/lillie_keler,,97.243.27.252,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n2786,4,261,2017-01-28 07:53:35,http://kunzekutch.biz/destinee,,248.253.16.35,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n2787,4,262,2017-06-12 21:27:33,http://kaulkewyman.biz/grayson,,240.82.203.202,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n2788,4,262,2017-03-04 06:28:20,http://kuhlman.net/matilde.hermann,,37.20.75.113,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n2789,4,262,2017-03-17 08:29:42,http://dibbert.org/joe,,209.156.199.230,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n2790,4,262,2016-12-27 18:56:08,http://beatty.org/gust,,16.253.82.176,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n2791,4,262,2017-02-08 03:44:49,http://lehnerschulist.co/macy,,99.67.35.187,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n2792,4,262,2017-03-24 19:04:00,http://lubowitz.biz/kaitlyn,,41.108.62.230,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n2793,4,262,2017-01-13 19:24:16,http://harriskertzmann.com/olen_gaylord,,149.211.69.118,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n4321,7,403,2017-01-07 15:04:40,http://schuppe.net/tony,3.0000000000,238.79.126.118,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n4322,7,403,2017-05-25 03:54:11,http://miller.com/dawson,3.0000000000,159.132.175.52,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n4323,7,403,2017-03-11 13:52:15,http://pfeffer.com/jaleel,0.0000000000,115.25.242.19,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n4324,7,403,2017-02-09 11:58:50,http://skiles.co/gail_jast,0.0000000000,168.116.52.24,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n4325,7,403,2016-12-21 02:02:03,http://turner.name/robbie,3.0000000000,181.24.220.109,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n4326,7,403,2017-04-25 07:15:35,http://huels.org/brady.reichert,2.0000000000,46.176.32.50,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n4327,7,403,2017-02-19 18:02:11,http://thompsonschimmel.io/earline,3.0000000000,192.253.184.168,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n4328,7,403,2017-03-14 07:39:59,http://rohanhirthe.name/eudora_mosciski,1.0000000000,40.148.209.206,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n4329,7,403,2017-03-13 16:26:41,http://hagenes.org/rowan_zieme,3.0000000000,131.41.214.75,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n4330,7,403,2017-06-12 15:26:22,http://steuber.name/cale.welch,2.0000000000,78.45.133.126,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n4331,7,403,2017-01-20 15:23:34,http://champlin.info/carolyne,1.0000000000,115.25.242.19,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n4332,7,403,2017-05-23 01:19:18,http://hegmann.info/turner.schuppe,3.0000000000,54.128.34.174,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n4333,7,403,2016-12-13 10:48:53,http://marquardtlarson.info/christelle,3.0000000000,155.230.246.40,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n4334,7,403,2017-04-23 02:50:30,http://oreillycartwright.co/velda.windler,1.0000000000,70.216.196.161,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n4335,7,403,2017-05-18 21:42:01,http://macgyvergottlieb.net/dameon,1.0000000000,76.40.132.13,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n4336,7,403,2017-02-05 01:27:47,http://strackeroob.name/randy,3.0000000000,54.128.34.174,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n4337,7,403,2017-05-25 21:32:56,http://zboncaklubowitz.info/bianka,1.0000000000,23.74.38.30,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n4338,7,403,2016-12-26 03:07:40,http://wilkinson.org/austen.rath,3.0000000000,176.219.208.171,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n4339,7,403,2017-01-22 02:36:43,http://lang.name/stanley_bernier,1.0000000000,183.3.121.210,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n4340,7,403,2017-02-19 15:20:31,http://hermann.io/ora_kautzer,3.0000000000,186.5.89.84,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n4341,7,404,2017-04-15 15:47:29,http://mann.net/monica_herman,0.0000000000,13.79.40.84,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n4342,7,404,2017-02-01 20:23:38,http://connankunding.net/stella_yundt,2.0000000000,182.82.177.224,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n4343,7,404,2017-03-08 12:08:59,http://gleichner.info/cedrick,0.0000000000,10.93.242.239,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n4344,7,404,2017-04-14 04:38:16,http://stoltenberg.org/donnell_leffler,0.0000000000,10.93.242.239,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n4345,7,404,2016-12-23 03:24:13,http://stammskiles.biz/marcelino.hand,2.0000000000,52.186.141.52,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n4346,7,405,2016-12-23 02:54:11,http://satterfield.co/camylle,2.0000000000,250.21.142.60,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n4347,7,405,2017-04-11 14:51:38,http://powlowski.org/geovanny,2.0000000000,250.21.142.60,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n4348,7,405,2017-03-04 13:17:40,http://funkmiller.biz/tiara,1.0000000000,9.73.244.104,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n4349,7,406,2017-04-15 04:26:18,http://mcclure.info/santiago,1.0000000000,103.120.221.150,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n4350,7,406,2016-12-18 15:10:03,http://tillman.org/ettie,0.0000000000,97.43.143.189,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n4351,7,406,2017-05-24 18:16:28,http://cummings.org/armand,0.0000000000,216.34.141.27,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n4352,7,407,2017-04-07 00:32:00,http://towne.name/carmela,1.0000000000,242.3.58.204,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n4353,7,407,2017-05-27 17:48:58,http://borerkutch.info/marjolaine,0.0000000000,123.179.228.183,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n4354,7,407,2017-01-08 02:37:49,http://ferry.biz/deshawn,1.0000000000,82.10.134.192,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n4355,7,407,2017-02-25 00:07:21,http://ullrich.net/bianka,1.0000000000,138.49.145.235,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n4356,7,407,2017-01-15 13:37:45,http://block.net/tracey,0.0000000000,183.243.67.234,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n4357,7,407,2017-04-12 15:02:41,http://hammesjerde.com/jada.hills,0.0000000000,248.67.23.174,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n4358,7,407,2017-01-29 18:07:38,http://bauch.org/raina_bogan,1.0000000000,187.138.48.43,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n4359,7,407,2017-06-07 04:48:39,http://schamberger.info/kameron.padberg,0.0000000000,9.58.152.212,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n4360,7,407,2017-03-24 04:45:57,http://harber.io/adelle,0.0000000000,187.138.48.43,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n1653,3,151,2017-06-01 06:30:59,http://bradtkerohan.name/jaime,0.0000000000,221.120.162.48,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n1654,3,151,2017-04-16 22:48:05,http://lubowitz.name/noemie,0.0000000000,148.254.14.220,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n1655,3,152,2017-04-23 08:49:20,http://barton.name/ilene,1.0000000000,15.238.96.175,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n1656,3,152,2016-12-22 02:03:06,http://dubuque.org/carlo_reinger,1.0000000000,179.195.168.149,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n1657,3,152,2017-02-18 19:01:37,http://quigley.info/irma,0.0000000000,201.101.168.4,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n1658,3,152,2017-03-10 14:34:01,http://fay.net/joe,1.0000000000,170.251.126.98,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n1659,3,152,2017-03-21 22:17:18,http://hilllglover.com/braeden_botsford,0.0000000000,163.60.121.195,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n1660,3,152,2017-06-09 03:15:57,http://langosh.com/joana.goyette,0.0000000000,82.154.179.169,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n1661,3,152,2017-02-20 03:49:15,http://stark.io/john_schmidt,0.0000000000,24.113.203.65,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n1662,3,152,2017-01-01 04:23:23,http://turcottecasper.com/myrl,1.0000000000,84.105.36.87,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n1663,3,152,2017-01-24 13:54:51,http://schowalter.org/jaime_bradtke,1.0000000000,29.232.98.162,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n1664,3,152,2017-05-12 12:23:21,http://kozey.name/carolyne_funk,1.0000000000,24.113.203.65,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n1665,3,152,2017-05-23 03:31:33,http://hansen.name/emanuel.heidenreich,0.0000000000,246.66.6.8,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n1666,3,152,2017-04-10 13:06:21,http://howell.net/geovany_fahey,1.0000000000,104.115.217.83,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n1667,3,152,2017-01-12 06:51:35,http://larkin.net/blake,0.0000000000,15.70.183.109,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n1668,3,152,2017-06-12 00:06:16,http://hilpert.io/meredith.hoeger,1.0000000000,92.83.129.48,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n1669,3,152,2017-01-01 00:43:51,http://reinger.name/aglae_pouros,1.0000000000,163.60.121.195,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n1670,3,153,2017-04-27 18:54:01,http://upton.org/lauryn,0.0000000000,6.202.48.23,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n1671,3,153,2017-05-20 18:38:09,http://ward.org/erick,3.0000000000,137.246.95.23,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n1672,3,153,2017-02-08 10:47:37,http://macejkovic.info/kobe,2.0000000000,231.75.84.206,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n1673,3,154,2017-04-16 21:23:55,http://lind.org/everardo.ferry,0.0000000000,45.31.134.130,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n1674,3,154,2017-05-03 11:11:15,http://bruen.co/vanea_sawayn,0.0000000000,206.243.44.99,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n1675,3,154,2017-06-05 04:19:25,http://mann.io/bernie.kunde,0.0000000000,36.43.123.187,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n1676,3,154,2017-01-08 17:21:13,http://watsica.co/timmothy,0.0000000000,110.253.18.210,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n1677,3,154,2016-12-20 18:55:35,http://prohaskakovacek.net/rafaela.wilkinson,0.0000000000,6.159.169.196,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n1678,3,154,2017-03-16 11:44:39,http://stokes.name/tyshawn,0.0000000000,69.66.93.12,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n1679,3,155,2017-02-17 22:03:35,http://cain.org/ezra,0.0000000000,2.57.161.73,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n1680,3,155,2016-12-20 14:17:53,http://willms.name/sedrick,1.0000000000,11.205.230.155,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n1681,3,155,2017-06-03 03:03:53,http://wisoky.co/aileen,1.0000000000,86.110.236.217,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n1682,3,155,2017-04-20 00:44:00,http://towne.biz/noemie,0.0000000000,143.220.194.91,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n1683,3,155,2016-12-22 20:03:41,http://effertzfranecki.info/gennaro_volkman,1.0000000000,196.74.108.219,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n1684,3,156,2017-02-15 13:37:19,http://zieme.org/holden,,178.197.31.87,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n1685,3,156,2017-01-10 11:33:49,http://lowebeahan.net/norwood.walker,,241.253.46.200,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n1686,3,156,2016-12-27 10:15:25,http://hicklemacejkovic.org/hillary,,155.53.24.179,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n1687,3,156,2017-06-01 03:06:05,http://heller.co/diego,,84.200.94.200,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n1688,3,156,2017-01-05 07:57:17,http://bahringer.co/imani,,149.93.36.108,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n1689,3,156,2017-05-31 00:57:29,http://price.io/eloise_ohara,,152.146.215.47,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n1690,3,156,2017-05-15 17:25:42,http://macejkovicfranecki.io/stephan_roberts,,55.72.83.242,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n1691,3,156,2017-04-27 06:41:12,http://langosh.net/rose,,152.71.16.54,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n1692,3,156,2017-03-21 18:28:56,http://fritschmorar.com/elton,,246.51.236.43,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n1693,3,156,2017-05-01 14:11:40,http://walkerwilderman.net/manley_abbott,,201.230.70.102,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n1694,3,156,2017-01-25 09:47:58,http://hermanhodkiewicz.net/cristal_effertz,,249.62.162.122,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n1695,3,157,2017-06-13 16:55:30,http://lowe.name/oscar,,194.95.100.100,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n1696,3,157,2017-04-25 06:39:46,http://williamson.biz/frederic,,65.130.186.183,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n1697,3,157,2017-06-06 01:19:37,http://rogahngusikowski.net/joelle,,88.244.35.187,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n1698,3,157,2017-03-14 07:43:29,http://hoppe.co/jaden_ebert,,165.12.116.6,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n1699,3,157,2017-06-09 15:36:08,http://zulaufwilderman.org/marcelo_parisian,,4.49.48.55,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n1700,3,157,2017-04-08 00:24:24,http://lemkegleason.net/virgie,,98.154.194.120,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n1701,3,157,2017-03-20 11:39:04,http://berge.co/addison.bailey,,4.49.48.55,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n1702,3,157,2017-01-28 13:49:40,http://lowe.io/kaley.macgyver,,23.139.119.35,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n1703,3,157,2017-05-11 20:37:24,http://aufderhar.org/heloise_graham,,206.39.33.102,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n1704,3,157,2017-04-19 18:17:11,http://turcottehudson.com/malachi,,88.244.35.187,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n1705,3,157,2017-05-17 07:20:41,http://bechtelar.com/shea.okuneva,,88.244.35.187,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n1706,3,157,2017-01-31 00:25:24,http://koelpinklein.biz/misty,,90.62.72.176,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n4361,7,407,2017-03-13 17:29:01,http://ko.name/aleen,0.0000000000,139.101.229.179,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n4362,7,407,2016-12-23 03:01:47,http://homenick.com/jewell_emard,0.0000000000,187.138.48.43,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n4363,7,407,2017-05-23 03:53:00,http://okeefebotsford.name/clementine.rohan,0.0000000000,199.94.165.130,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n4364,7,407,2017-02-07 18:12:54,http://bogan.io/dolly_howell,1.0000000000,185.106.84.72,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n4365,7,407,2016-12-16 23:42:30,http://keelinglebsack.info/angie.bogan,0.0000000000,139.101.229.179,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n4366,7,407,2017-02-04 17:31:19,http://wintheiser.com/macey,1.0000000000,187.138.48.43,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n4367,7,407,2017-04-20 18:36:26,http://treutelcruickshank.io/zetta,1.0000000000,250.137.218.215,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n4368,7,407,2017-03-20 20:01:43,http://quitzon.io/trea,1.0000000000,132.111.203.177,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n4369,7,407,2017-01-30 01:45:26,http://osinski.biz/arvid_braun,0.0000000000,185.106.84.72,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n4370,7,407,2017-03-23 10:25:51,http://becker.co/laila,0.0000000000,2.136.191.137,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n4371,7,408,2017-01-05 10:42:12,http://hilll.org/blaze.treutel,3.0000000000,252.49.103.206,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n4372,7,408,2017-03-10 18:20:11,http://romaguera.biz/antwan,3.0000000000,198.10.93.42,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n4373,7,408,2017-01-17 00:04:50,http://konopelski.co/kamren.kris,0.0000000000,70.249.236.73,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n4374,7,408,2017-04-04 09:03:55,http://zulauf.biz/sam_kunze,2.0000000000,228.234.152.126,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n4375,7,408,2017-01-31 02:49:37,http://schmitt.co/dannie,1.0000000000,199.12.17.119,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n4376,7,408,2017-05-31 06:37:54,http://reinger.co/donato_upton,2.0000000000,41.241.20.106,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n4377,7,408,2017-03-12 23:47:38,http://little.io/aurore_willms,3.0000000000,199.12.17.119,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n4378,7,408,2017-03-28 07:44:45,http://bartell.net/chester,1.0000000000,73.230.4.135,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n4379,7,408,2017-01-17 06:46:50,http://mayert.co/jarrod,3.0000000000,245.128.243.140,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n4380,7,408,2017-03-15 17:51:32,http://auerkulas.org/lucius_wolff,3.0000000000,73.230.4.135,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n4381,7,408,2017-01-14 21:21:41,http://hickle.org/alden_farrell,2.0000000000,87.100.69.116,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n4382,7,408,2017-05-05 05:48:13,http://keebler.io/tyrique,3.0000000000,88.11.41.6,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n4383,7,408,2017-04-29 12:37:28,http://hermanruecker.biz/mckayla,0.0000000000,252.49.103.206,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n4384,7,408,2017-04-15 20:30:24,http://kuhlman.com/jaqueline,0.0000000000,199.12.17.119,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n4385,7,408,2016-12-20 11:47:11,http://quigley.io/salvatore.leffler,0.0000000000,87.100.69.116,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n4386,7,409,2017-04-01 06:07:01,http://dickinson.io/franz.cummerata,0.0000000000,90.4.13.225,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n4387,7,409,2017-03-24 19:12:20,http://stehr.biz/chris,0.0000000000,94.43.243.113,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n4388,7,409,2017-01-07 06:17:41,http://hauck.net/jackson_mckenzie,0.0000000000,168.237.163.15,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n4389,7,409,2017-01-16 20:19:17,http://willms.io/johan.torphy,1.0000000000,176.68.91.130,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n4390,7,409,2017-05-29 11:50:55,http://balistreri.net/gerda,1.0000000000,247.162.57.135,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n4391,7,409,2017-01-21 13:31:14,http://wuckert.io/zena_jast,1.0000000000,206.67.67.190,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n4392,7,409,2017-01-08 23:36:15,http://stehr.co/oda,0.0000000000,168.237.163.15,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n4393,7,409,2017-03-27 01:04:20,http://jacobs.name/tomasa,1.0000000000,217.129.230.215,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n4394,7,410,2016-12-19 22:40:32,http://murphywisoky.net/alva.davis,0.0000000000,147.177.161.209,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n4395,7,410,2017-03-22 08:03:06,http://schmidtbeier.com/rosemary.mayert,1.0000000000,147.177.161.209,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n4396,7,410,2017-02-10 12:55:44,http://feil.io/obie.barton,1.0000000000,211.112.82.98,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n4397,7,410,2017-04-26 00:10:27,http://satterfieldschowalter.name/muhammad_marquardt,0.0000000000,16.172.245.102,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n4398,7,410,2017-05-29 15:56:43,http://schusterframi.info/jeie,1.0000000000,63.125.133.192,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n4399,7,410,2017-05-21 19:47:59,http://hermannschulist.co/leola_heller,1.0000000000,221.199.147.216,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n4400,7,410,2017-03-16 19:01:13,http://smith.co/elisabeth.lynch,1.0000000000,172.23.177.218,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n4401,7,410,2017-01-28 19:54:41,http://oconner.io/brett_streich,1.0000000000,122.92.144.34,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n4402,7,410,2017-01-27 20:32:36,http://runolfon.name/madie.sanford,1.0000000000,16.172.245.102,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n4403,7,410,2017-02-17 20:21:05,http://thiel.name/tina,0.0000000000,90.250.225.145,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n4404,7,410,2017-03-23 11:29:19,http://leffler.co/macie,0.0000000000,128.22.149.238,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n4405,7,410,2017-01-31 14:16:48,http://brekkerodriguez.net/erin,1.0000000000,206.27.219.30,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n4406,7,410,2017-05-28 12:18:52,http://wildermanhermann.com/gerda,0.0000000000,175.23.112.67,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n4407,7,410,2016-12-17 23:20:11,http://wildermanmayer.org/charley,0.0000000000,68.153.104.170,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n4408,7,410,2016-12-14 01:16:21,http://rempelsatterfield.biz/gudrun,1.0000000000,206.27.219.30,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n4409,7,410,2017-04-18 14:24:32,http://barrows.info/chesley,0.0000000000,211.160.210.187,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n4410,7,410,2017-02-26 04:07:04,http://glover.co/hildegard,0.0000000000,161.15.20.8,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n4411,7,410,2017-01-12 19:33:19,http://conroy.org/pete_ortiz,0.0000000000,122.92.144.34,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n4412,7,410,2017-02-11 16:03:36,http://roob.net/abbie_moriette,1.0000000000,211.160.210.187,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n1707,3,157,2017-01-07 08:44:31,http://adams.net/jaden,,251.102.64.231,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n1708,3,157,2017-02-12 14:31:51,http://veumkemmer.info/tanya.ritchie,,23.139.119.35,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n1709,3,157,2017-01-16 17:56:32,http://hudson.name/adrian_mitchell,,194.95.100.100,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n1710,3,157,2017-01-27 07:19:29,http://kulas.co/kristopher_cummings,,23.139.119.35,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n1711,3,157,2017-04-22 21:48:37,http://okuneva.org/alysha,,43.103.222.200,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n1712,3,157,2016-12-14 07:32:28,http://oberbrunnerlangosh.biz/ava_williamson,,42.213.211.116,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n1713,3,157,2017-03-24 00:26:31,http://ferryauer.name/brent,,4.49.48.55,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n1714,3,157,2017-04-30 00:43:12,http://larkinvon.net/gardner,,176.177.160.109,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n1715,3,158,2017-01-07 19:44:00,http://stammmarquardt.org/dwight,,158.81.223.15,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n1716,3,158,2017-03-08 13:56:30,http://sawaynlabadie.com/athena,,228.220.91.80,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n1717,3,158,2017-04-23 20:45:13,http://oberbrunner.io/marcus,,55.4.145.65,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n1718,3,158,2016-12-14 04:12:53,http://bartolettiweimann.biz/jovan_kertzmann,,117.138.164.147,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n1719,3,158,2017-03-28 19:50:23,http://flatley.io/julio.bruen,,45.151.93.62,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n1720,3,158,2017-05-05 12:21:28,http://feeneywalsh.biz/waino,,195.149.150.85,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n1721,3,158,2017-05-29 02:53:00,http://christiansen.biz/joannie_hettinger,,216.103.188.13,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n1722,3,158,2017-02-05 01:09:37,http://simonis.io/margaretta,,80.54.198.157,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n1723,3,158,2017-03-16 10:56:38,http://roberts.com/emmy_cartwright,,158.81.223.15,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n1724,3,158,2017-03-09 22:58:13,http://runolfon.com/katelynn.ondricka,,88.201.217.167,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n1725,3,158,2017-01-29 22:12:54,http://mcglynn.io/earl_jacobs,,84.101.88.27,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n1726,3,158,2017-04-11 08:53:45,http://lowefeeney.io/elsa_oconner,,72.46.79.24,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n1727,3,158,2017-02-09 04:28:26,http://gutmann.co/sylvan,,117.138.164.147,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n1728,3,158,2017-01-07 15:08:59,http://pacocha.info/juliet,,119.100.42.23,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n1729,3,158,2017-03-17 17:47:57,http://cartwright.biz/ruel.jenkins,,121.31.16.249,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n1730,3,158,2017-02-24 15:06:24,http://adams.net/lucile,,121.102.204.182,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n1731,3,158,2017-05-29 05:14:01,http://hirthe.name/alden,,179.60.152.250,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n1732,3,158,2017-05-08 01:21:48,http://turner.info/anais,,80.54.198.157,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n1733,3,158,2016-12-21 14:25:02,http://swaniawskipfeffer.org/brock_fahey,,113.58.93.42,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n1734,3,158,2017-01-08 00:01:18,http://cremincremin.io/erin,,117.138.164.147,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n1735,3,159,2017-03-06 04:35:20,http://berniermueller.io/houston_spinka,,230.45.87.235,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n1736,3,159,2017-03-22 06:12:30,http://mayert.io/paul,,165.215.98.227,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n1737,3,159,2017-01-03 05:11:57,http://boyle.name/ludwig.mclaughlin,,219.2.229.222,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n1738,3,160,2017-04-29 07:26:34,http://abshireschoen.co/orville,,86.28.213.59,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n1739,3,160,2017-04-30 10:49:54,http://hills.info/maximo_senger,,133.244.13.12,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n1740,3,160,2017-01-06 00:24:22,http://hansenkovacek.org/kenyatta_littel,,66.51.28.173,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n1741,3,160,2017-05-27 12:10:54,http://reinger.io/ena,,86.28.213.59,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n1742,3,161,2017-01-03 12:36:56,http://deckow.co/ulises,,127.51.194.62,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n1743,3,161,2017-06-03 23:35:08,http://quigley.org/ally,,81.199.12.157,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n1744,3,161,2017-03-13 03:54:05,http://hilperttoy.org/elinore_lesch,,3.205.225.243,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n1745,3,162,2017-03-27 06:06:07,http://vandervort.biz/sally_hahn,0.0000000000,91.70.192.95,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n1746,3,163,2016-12-24 06:35:08,http://stoltenberg.org/felicia,1.0000000000,75.195.242.86,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n1747,3,163,2017-01-08 07:35:17,http://haley.name/aurelio,1.0000000000,72.58.35.112,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n1748,3,163,2016-12-17 08:51:32,http://koepp.name/jazmyne.kunde,1.0000000000,62.110.170.69,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n1749,3,163,2017-05-09 14:53:27,http://stark.io/annette.monahan,0.0000000000,87.193.175.243,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n1750,3,163,2017-04-12 01:17:53,http://altenwerth.io/caitlyn.doyle,0.0000000000,72.58.35.112,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n1751,3,164,2017-06-09 15:11:21,http://collinsprohaska.co/darian,0.0000000000,75.127.129.85,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n1752,3,164,2017-05-02 19:17:20,http://wisozkfranecki.org/mikayla_predovic,0.0000000000,99.172.128.31,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n1753,3,164,2017-05-29 18:19:22,http://braunwunsch.io/neoma,0.0000000000,12.119.54.120,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n1754,3,164,2017-01-19 15:28:53,http://powlowski.com/javier.wunsch,0.0000000000,238.179.37.42,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n1755,3,164,2017-03-14 00:10:46,http://jakubowskimiller.io/morton,0.0000000000,146.36.41.85,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n1756,3,164,2017-02-14 15:08:13,http://stanton.org/madelyn,0.0000000000,253.251.177.33,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n1757,3,164,2017-04-11 15:11:14,http://labadie.com/abraham,0.0000000000,179.213.223.57,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n1758,3,164,2016-12-15 09:49:51,http://medhurstruel.io/icie_rolfson,0.0000000000,16.89.34.116,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n1759,3,164,2017-03-11 20:21:48,http://hahn.com/lawrence_osinski,0.0000000000,69.35.157.93,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n1760,3,164,2016-12-15 07:35:41,http://considine.biz/clint.douglas,0.0000000000,238.230.164.233,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n4413,7,411,2017-05-10 21:17:54,http://koepp.info/salvador,1.0000000000,162.53.77.92,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n4414,7,411,2017-04-27 12:16:25,http://shanahan.co/madaline,0.0000000000,10.30.138.71,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n4415,7,411,2017-04-03 17:59:58,http://parkerkoch.biz/eric.hintz,1.0000000000,129.119.140.146,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n4416,7,411,2017-03-03 03:49:39,http://runolfsdottir.biz/sherman,1.0000000000,224.100.213.172,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n4417,7,411,2017-03-18 23:47:58,http://wildermankirlin.co/elliot.crona,2.0000000000,140.152.217.97,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n4418,7,411,2017-06-12 15:09:48,http://feestquitzon.info/kristopher,2.0000000000,235.130.63.53,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n4419,7,411,2017-06-13 00:25:23,http://prosacco.info/edmund,1.0000000000,237.117.128.155,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n4420,7,412,2017-04-09 19:56:42,http://johnsgrady.info/destinee_stanton,,184.220.93.59,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n4421,7,412,2017-04-06 15:59:56,http://monahan.net/roel_ward,,55.126.218.64,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n4422,7,412,2017-02-22 01:01:41,http://kozey.biz/ubaldo_mohr,,30.71.237.191,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n4423,7,412,2017-03-18 13:50:12,http://dibbert.name/eulalia,,184.220.93.59,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n4424,7,412,2017-02-16 18:33:25,http://casper.io/dante,,37.108.200.64,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n4425,7,412,2017-04-15 09:46:09,http://koepp.io/christian,,13.58.229.173,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n4426,7,412,2017-02-12 18:09:49,http://mcglynnnader.net/judd.gleason,,190.34.65.112,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n4427,7,413,2017-06-10 11:46:09,http://windler.biz/aric,,49.220.24.253,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n4428,7,413,2017-02-10 07:35:44,http://marquardtrowe.org/kattie_pfannerstill,,44.210.148.194,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n4429,7,413,2017-01-31 19:35:35,http://flatley.info/kareem_borer,,33.233.50.200,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n4430,7,413,2017-02-15 03:28:03,http://hills.name/jaqueline.kovacek,,50.72.186.216,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n4431,7,413,2016-12-24 20:25:26,http://carter.org/darwin,,52.79.228.206,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n4432,7,414,2017-02-28 15:30:57,http://goodwin.name/darryl,,17.169.156.199,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n4433,7,414,2017-04-05 20:14:34,http://hodkiewiczdickens.com/milford,,145.54.129.28,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n4434,7,414,2017-06-10 14:24:55,http://bartell.name/mathias_barton,,53.50.107.85,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4435,7,414,2017-03-05 17:40:12,http://mraz.co/charity,,241.249.222.134,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n4436,7,414,2017-01-15 13:10:47,http://monahangerhold.biz/vernice,,44.214.86.249,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n4437,7,414,2016-12-17 16:09:22,http://quitzon.net/dana,,88.8.20.116,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n4438,7,414,2017-01-08 13:24:36,http://bartonnicolas.biz/gerda,,83.175.210.203,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n4439,7,414,2017-05-23 05:42:06,http://bahringer.name/angelina.kling,,145.6.174.237,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n4440,7,414,2017-05-30 04:05:58,http://lang.name/jazmyne,,225.94.136.242,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n4441,7,414,2017-05-27 03:37:20,http://steuber.io/marcelina,,54.200.21.151,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n4442,7,414,2017-02-05 21:22:04,http://sanford.co/nelle,,38.251.220.20,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n4443,7,414,2017-05-19 14:11:07,http://brekke.info/myles,,167.91.46.135,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n4444,7,414,2017-06-01 10:57:15,http://cummingswisozk.org/halie,,70.175.48.223,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n4445,7,414,2017-02-11 00:30:15,http://torpschoen.org/lucienne_kling,,135.174.3.128,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n4446,7,414,2017-01-31 12:19:13,http://lynch.org/wendell,,235.248.252.213,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n4447,7,414,2017-03-28 12:47:06,http://ruel.biz/everette,,21.139.197.106,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n4448,7,414,2017-05-22 17:36:29,http://larsonschmidt.net/jermey.bartell,,188.25.233.169,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n4449,7,414,2017-01-12 09:27:52,http://bruenpollich.biz/madonna_von,,12.35.248.179,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n4450,7,415,2017-02-07 01:03:56,http://hahnschmeler.name/corrine_ondricka,,149.121.27.77,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n4451,7,415,2017-02-17 07:33:21,http://bayer.com/christine.thompson,,15.49.38.106,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n4452,7,415,2017-01-31 20:44:43,http://bogisichglover.org/dena.lehner,,149.121.27.77,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n4453,7,416,2017-04-03 20:09:59,http://padberg.info/alanna.gerlach,,83.226.129.208,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n4454,7,416,2017-01-21 03:11:14,http://davis.net/jerome_jacobson,,100.220.71.66,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n4455,7,416,2016-12-28 06:26:55,http://ullrich.biz/melvin,,123.73.175.42,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n4456,7,416,2017-03-20 10:45:49,http://klockomayer.net/lonie,,152.73.236.145,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n4457,7,416,2017-01-15 05:53:46,http://oconnelldoyle.info/alan,,6.141.207.185,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n4458,7,416,2017-01-04 12:19:58,http://crona.name/ayden,,225.145.247.109,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n4459,7,416,2017-03-05 15:49:14,http://rempel.com/tracy,,243.135.52.203,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n4460,7,416,2017-03-06 23:50:50,http://pacochakunde.co/jailyn.cronin,,212.194.182.135,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n4461,7,416,2017-05-04 15:50:17,http://bradtkegerhold.com/adolph_west,,95.216.150.22,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n4462,7,416,2017-05-11 11:12:36,http://johnston.io/ruel,,79.139.203.30,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n4463,7,416,2017-01-12 20:51:52,http://rogahnfay.info/lavonne.rau,,22.142.58.62,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n4464,7,416,2016-12-25 17:30:31,http://marvin.io/sidney.ryan,,97.177.237.69,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n4465,7,416,2017-01-19 15:47:43,http://littleschaefer.net/isabelle.schamberger,,39.93.126.242,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n4466,7,416,2017-06-07 03:17:51,http://jenkins.org/flavio_koelpin,,8.131.71.87,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n4467,7,416,2017-04-05 12:35:47,http://lindgrenrutherford.co/nina.ko,,140.69.68.180,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n1761,3,164,2017-04-12 02:33:42,http://kingnader.biz/jeyca,0.0000000000,37.80.142.147,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n1762,3,164,2017-03-31 03:58:58,http://kuhic.com/dell.macgyver,0.0000000000,109.64.172.103,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n1763,3,164,2017-04-04 08:21:33,http://becker.io/jordi,0.0000000000,199.121.60.179,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n1764,3,164,2017-05-03 19:56:16,http://cummerata.org/nathaniel,0.0000000000,195.160.220.132,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n1765,3,165,2017-05-21 15:39:28,http://kshlerinmarks.info/caden,1.0000000000,188.233.90.228,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n1766,3,165,2017-03-23 06:00:29,http://bartonhintz.com/elyse.volkman,2.0000000000,56.12.154.166,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n1767,3,165,2017-06-05 15:11:53,http://okon.name/stevie,0.0000000000,244.102.176.246,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n1768,3,165,2017-01-27 14:07:34,http://oconnerpacocha.info/sallie,0.0000000000,164.185.21.232,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n1769,3,165,2017-04-19 18:50:58,http://rohan.io/rhianna,1.0000000000,66.57.115.208,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n1770,3,165,2017-01-24 12:16:21,http://gusikowskihoeger.io/chet,0.0000000000,134.83.129.211,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n1771,3,165,2017-03-12 23:20:53,http://wyman.com/ryder,1.0000000000,178.112.224.240,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n1772,3,165,2017-01-20 00:53:28,http://jacobson.co/liliana,0.0000000000,250.143.250.186,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n1773,3,165,2017-05-14 16:45:43,http://grimes.biz/gino,0.0000000000,114.42.156.7,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n1774,3,165,2017-01-04 18:40:20,http://marks.net/sterling,0.0000000000,185.21.168.171,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n1775,3,165,2017-04-05 21:39:07,http://rodriguez.com/makenna_rice,0.0000000000,229.150.50.36,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n1776,3,166,2017-05-20 18:01:53,http://mclaughlin.org/gilbert.farrell,0.0000000000,175.13.235.72,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n1777,3,166,2017-02-28 06:58:14,http://vandervort.co/tyrese,2.0000000000,68.216.126.88,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n1778,3,166,2017-01-13 12:46:22,http://moendach.name/jimmy,2.0000000000,215.45.128.193,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n1779,3,166,2017-03-25 23:06:58,http://cremin.name/gwen_mitchell,2.0000000000,30.229.213.70,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n1780,3,166,2017-05-20 00:02:43,http://heel.biz/irwin,0.0000000000,114.101.173.37,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n1781,3,166,2016-12-25 04:40:28,http://bernier.com/meggie,0.0000000000,185.13.133.198,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n1782,3,167,2017-05-16 17:14:28,http://smitham.net/camren_wiegand,1.0000000000,20.138.13.140,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n1783,3,167,2017-06-11 06:13:21,http://rowecormier.biz/sienna,1.0000000000,240.68.185.58,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n1784,3,167,2017-01-04 14:34:03,http://wintheiser.com/berta_johnson,1.0000000000,93.174.219.193,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n1785,3,167,2017-01-15 14:04:33,http://lefflerwalker.com/mozell_konopelski,0.0000000000,240.68.185.58,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n1786,3,167,2017-06-03 09:06:09,http://jones.name/elenor_leuschke,0.0000000000,159.8.116.11,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n1787,3,167,2016-12-28 23:50:59,http://buckridge.org/josephine.jones,0.0000000000,85.165.172.9,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n1788,3,167,2017-01-23 01:10:47,http://fritsch.io/ila,1.0000000000,183.20.61.70,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n1789,3,167,2017-04-10 19:48:19,http://kutch.biz/serena,1.0000000000,136.93.29.97,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n1790,3,167,2017-01-05 08:46:54,http://oharamayert.net/dora_altenwerth,0.0000000000,47.107.105.164,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n1791,3,167,2017-01-19 18:26:30,http://townehaley.biz/isobel.kuhn,1.0000000000,93.160.89.195,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n1792,3,167,2017-05-03 12:01:53,http://fritsch.com/rahsaan,0.0000000000,157.141.240.185,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n1793,3,167,2017-06-07 12:06:50,http://shieldporer.net/kylee.ohara,0.0000000000,254.235.69.78,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n1794,3,167,2017-04-24 08:55:54,http://marvin.co/brittany_funk,1.0000000000,252.42.29.165,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n1795,3,167,2017-01-16 11:55:36,http://kuhlmanbauch.co/hope,0.0000000000,117.230.202.130,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n1796,3,168,2017-01-10 07:47:32,http://mills.org/javonte_erdman,2.0000000000,215.151.203.178,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n1797,3,168,2017-05-09 21:08:00,http://aufderhar.com/kiel_simonis,2.0000000000,207.61.100.178,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n1798,3,168,2017-01-25 08:59:45,http://adamsdoyle.com/guie,1.0000000000,96.57.32.124,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n1799,3,168,2017-02-24 14:01:20,http://oreillyprice.biz/brett,1.0000000000,84.171.241.138,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n1800,3,168,2017-03-08 05:19:43,http://veum.net/sven,2.0000000000,52.237.89.142,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n1801,3,168,2017-03-02 01:02:32,http://rodriguezemmerich.co/gabrielle.jenkins,2.0000000000,205.33.231.167,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n1802,3,168,2017-01-02 07:28:29,http://dachthiel.name/bettie,0.0000000000,17.163.115.204,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n1803,3,168,2017-05-29 00:26:14,http://hermistonbrakus.io/aniya.rolfson,0.0000000000,103.116.139.95,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n1804,3,168,2017-01-12 22:44:46,http://parisian.biz/brad,1.0000000000,205.33.231.167,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n1805,3,168,2017-04-03 11:55:09,http://stracke.io/oswaldo,0.0000000000,131.211.124.244,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n1806,3,168,2017-03-21 17:55:28,http://daughertyziemann.net/maudie.bernier,0.0000000000,215.151.203.178,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n1807,3,168,2017-02-03 08:49:39,http://price.co/ima,0.0000000000,205.33.231.167,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n1808,3,168,2017-05-09 20:42:09,http://sporer.info/sidney.powlowski,1.0000000000,178.113.109.208,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n1809,3,168,2017-05-13 13:20:28,http://dickens.org/bennett,0.0000000000,249.13.182.34,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n1810,3,168,2017-06-02 11:11:43,http://heidenreichbatz.co/elton_olson,0.0000000000,52.237.89.142,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n1811,3,168,2017-03-08 09:13:24,http://kreiger.biz/breanne,0.0000000000,2.49.22.175,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n1812,3,168,2017-02-04 05:51:57,http://emmerich.name/marlene,1.0000000000,169.135.85.242,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n4468,7,416,2017-05-02 03:59:28,http://mohr.co/jefferey_grady,,79.139.203.30,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n4469,7,416,2017-06-13 10:15:51,http://simonis.info/roel_schmidt,,201.123.101.43,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n4470,7,416,2017-03-17 05:47:51,http://hilll.name/annabel,,22.186.158.99,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n4471,7,416,2016-12-16 14:11:03,http://hills.net/elna,,79.139.203.30,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n4472,7,417,2017-03-23 10:50:35,http://robel.com/gerda,,2.32.148.250,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n4473,7,417,2017-04-26 06:34:30,http://muellerborer.io/camylle,,210.241.232.73,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n4474,7,417,2017-06-09 04:17:47,http://bradtke.io/alec_donnelly,,6.34.18.2,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n4475,7,417,2017-01-15 20:06:12,http://hudson.org/theresia_kerluke,,127.101.115.75,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n4476,7,417,2016-12-27 00:12:40,http://reingerorn.name/selmer.donnelly,,2.32.148.250,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n4477,7,417,2017-05-15 06:47:06,http://jacobskrajcik.com/doug,,60.64.167.17,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n4478,7,417,2017-03-17 22:33:13,http://ferry.biz/denis.hoppe,,77.242.33.236,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n4479,7,417,2017-01-12 08:58:39,http://turcotte.co/eda,,177.25.10.229,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n4480,7,417,2017-02-23 20:10:24,http://gorczany.net/milo,,248.72.218.169,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n4481,7,417,2016-12-16 08:15:26,http://predovic.io/milo_zemlak,,74.95.112.22,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n4482,7,417,2017-02-21 10:21:33,http://trompmayert.info/amos.macejkovic,,147.188.96.50,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n4483,7,417,2017-01-27 00:21:43,http://hermiston.biz/davion.rodriguez,,81.122.171.186,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n4484,7,417,2016-12-27 16:44:16,http://halvorson.info/nathaniel,,127.101.115.75,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n4485,7,417,2017-03-05 09:44:08,http://jones.info/nora_hirthe,,178.39.123.108,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n4486,7,417,2017-05-21 12:54:37,http://runolfsdottir.biz/creola.mcdermott,,161.40.219.230,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n4487,7,417,2017-02-23 05:38:47,http://schaefer.org/mohamed.runolfon,,248.223.246.220,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n4488,7,417,2017-02-16 01:03:35,http://little.info/shane,,81.122.171.186,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n4489,7,417,2017-05-27 08:27:42,http://jerdejast.net/maximillia.hauck,,189.227.14.156,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n4490,7,418,2017-04-24 06:31:37,http://kerluke.co/janelle.runte,,197.221.9.214,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n4491,7,418,2017-03-05 01:18:22,http://keler.net/aurelio.denesik,,243.148.4.25,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n4492,7,419,2017-03-22 12:31:52,http://deckow.io/kasandra_beer,,143.110.22.213,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n4493,7,419,2017-02-03 03:35:36,http://greenholtbernhard.io/isaiah,,45.47.91.79,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n4494,7,419,2017-04-08 00:07:36,http://kihn.name/palma,,139.165.242.169,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n4495,7,419,2017-02-25 04:06:56,http://senger.com/dejah_harber,,116.22.118.15,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n4496,7,419,2017-05-10 07:26:00,http://hahnberge.biz/leopoldo,,139.114.44.157,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n4497,7,419,2017-01-01 14:10:41,http://morietteziemann.io/davonte,,139.118.236.124,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n4498,7,419,2017-04-26 22:18:03,http://bauchconroy.biz/nellie.jakubowski,,218.33.181.135,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n4499,7,419,2016-12-27 16:40:27,http://huels.name/guie,,194.40.74.91,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n4500,7,419,2017-03-23 06:03:24,http://mraz.biz/ramon.feeney,,184.28.240.198,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n4501,7,419,2017-05-19 21:58:07,http://bergnaum.co/otis,,115.94.164.240,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n4502,7,419,2017-01-07 17:54:13,http://little.org/jadon.bruen,,194.40.74.91,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n4503,7,419,2016-12-22 15:35:52,http://jenkins.io/lyric.ruecker,,121.24.117.119,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n4504,7,419,2017-01-06 06:52:02,http://satterfield.com/rocky.gislason,,164.16.55.55,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n4505,7,419,2017-02-28 05:45:22,http://howemacgyver.com/eduardo,,234.245.190.191,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n4506,7,419,2017-05-17 05:05:02,http://legros.info/kelli,,174.244.205.177,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n4507,7,419,2017-03-25 23:29:24,http://trantowbosco.biz/dannie,,194.115.219.73,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n4508,7,419,2017-01-24 20:07:07,http://pollich.org/samanta.sauer,,45.47.91.79,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n4509,7,419,2017-03-01 20:49:13,http://quigleywyman.info/olga,,45.47.91.79,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n4510,7,420,2016-12-31 10:00:35,http://harriskovacek.co/darrick_hand,,181.156.34.107,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n4511,7,420,2017-05-14 14:20:36,http://mckenzie.name/pietro,,145.106.154.177,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n4512,7,420,2017-04-11 09:24:00,http://pacocha.co/elfrieda_marvin,,151.56.100.231,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n4513,7,420,2017-02-16 14:04:30,http://welchmaggio.info/louie,,26.55.148.60,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n4514,7,420,2017-01-16 06:41:28,http://conn.com/billy_mclaughlin,,75.127.167.100,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n4515,7,420,2017-01-31 19:08:47,http://jacobsonschroeder.com/roxane,,209.225.228.21,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n4516,7,420,2017-02-04 14:57:50,http://gutmannkovacek.org/lee,,62.133.168.180,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n4517,7,420,2017-04-09 08:38:34,http://hillsmosciski.org/christopher,,26.55.148.60,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n4518,7,421,2017-02-09 12:25:07,http://langworth.info/quinten_schuppe,,88.74.171.226,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4519,7,421,2017-02-12 13:33:25,http://gibson.net/bernadine,,253.214.66.135,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n4520,7,421,2016-12-30 16:06:40,http://mcclure.name/ariane.huels,,253.214.66.135,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n4521,7,421,2016-12-31 13:48:20,http://sporer.biz/sonya.gottlieb,,39.83.151.245,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n4522,7,421,2016-12-28 07:25:45,http://schroeder.net/eduardo,,230.56.197.43,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n1813,3,168,2017-01-27 04:36:07,http://hickle.net/jacynthe_murphy,2.0000000000,51.4.89.107,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n1814,3,168,2017-03-24 04:19:19,http://kobins.com/aisha.swift,0.0000000000,53.123.124.236,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n1815,3,169,2017-03-29 17:06:19,http://kris.io/gordon.beatty,1.0000000000,205.31.25.123,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n1816,3,169,2017-06-02 13:37:30,http://considine.info/modesto,1.0000000000,141.248.114.82,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n1817,3,169,2017-06-04 22:01:53,http://abernathy.name/gregg,1.0000000000,93.243.184.91,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n1818,3,169,2017-05-29 23:01:43,http://cremin.io/elza,1.0000000000,135.232.193.209,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n1819,3,169,2017-03-27 03:15:52,http://kuhn.name/gage,0.0000000000,141.152.184.15,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n1820,3,169,2017-05-21 07:19:57,http://ortizmante.org/alvis.barrows,0.0000000000,234.167.187.70,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n1821,3,169,2017-06-10 02:01:50,http://olson.com/destiny,1.0000000000,201.158.49.46,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n1822,3,169,2017-05-30 16:56:43,http://tremblay.biz/mariah,0.0000000000,135.232.193.209,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n1823,3,169,2016-12-23 01:32:00,http://parker.io/vivianne,1.0000000000,7.164.243.4,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n1824,3,170,2017-03-04 04:16:11,http://pagachamill.name/sasha_braun,2.0000000000,32.191.224.142,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n1825,3,170,2017-01-25 20:01:35,http://feil.com/jaylen,0.0000000000,84.9.231.6,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n1826,3,170,2017-05-19 00:34:19,http://mccullough.net/ken,0.0000000000,146.54.49.3,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n1827,3,170,2017-04-21 23:53:09,http://herzog.io/sid,2.0000000000,189.143.166.202,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n1828,3,170,2017-04-13 11:48:13,http://langoshgutmann.org/brent,0.0000000000,27.211.206.153,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n1829,3,170,2017-04-11 19:57:36,http://langosh.co/ruthie.welch,1.0000000000,11.214.149.8,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n1830,3,170,2017-01-19 07:35:14,http://quitzon.name/kaci,1.0000000000,32.191.224.142,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n1831,3,171,2017-03-04 02:53:50,http://shields.co/nyah.sipes,2.0000000000,181.235.41.245,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n1832,3,171,2017-05-25 22:17:09,http://beatty.org/leonard_schmidt,3.0000000000,120.86.147.38,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n1833,3,171,2017-04-30 09:45:57,http://jacobson.biz/roberta,2.0000000000,60.177.67.67,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n1834,3,171,2017-01-08 20:45:26,http://rodriguez.biz/rex_schuster,2.0000000000,123.37.244.138,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n1835,3,171,2017-01-26 17:09:32,http://greenwalter.org/jillian_tromp,2.0000000000,251.149.155.4,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n1836,3,171,2017-01-21 05:03:02,http://borer.co/demarco,3.0000000000,95.49.216.169,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n1837,3,171,2017-04-24 17:41:37,http://spinka.info/anjali.nolan,1.0000000000,60.177.67.67,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n1838,3,171,2017-03-20 06:33:45,http://doyleleffler.com/laury.graham,2.0000000000,175.101.249.28,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n1839,3,171,2016-12-17 22:27:13,http://ebert.biz/euna,0.0000000000,163.44.76.179,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n1840,3,171,2017-01-24 04:41:20,http://homenick.co/finn.emard,0.0000000000,123.37.244.138,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n1841,3,171,2017-03-07 21:00:15,http://okeefe.org/doris,2.0000000000,181.97.49.244,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n1842,3,171,2017-02-02 20:18:57,http://langosh.co/roosevelt,0.0000000000,215.163.214.208,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n1843,3,171,2017-04-28 05:10:26,http://dietrichgutkowski.info/dennis_hauck,3.0000000000,123.37.244.138,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n1844,3,171,2016-12-19 07:21:45,http://stiedemann.co/jude_smith,0.0000000000,95.49.216.169,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n1845,3,171,2017-04-10 04:11:06,http://schumm.org/markus,2.0000000000,120.86.147.38,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n1846,3,172,2017-04-09 06:03:01,http://donnelly.org/leone_simonis,,226.38.207.137,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n1847,3,172,2017-01-18 20:17:59,http://eichmannmraz.io/precious.gaylord,,136.3.64.145,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n1848,3,172,2017-02-19 21:04:25,http://kihn.co/webster.okeefe,,143.23.113.166,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n1849,3,172,2017-03-02 20:31:15,http://hammes.biz/kelley.spinka,,244.127.63.226,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n1850,3,172,2017-04-19 21:53:59,http://upton.info/vella.schultz,,202.18.244.244,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n1851,3,172,2016-12-13 10:41:49,http://mraznader.com/reggie_schiller,,150.77.23.190,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n1852,3,172,2017-03-18 03:44:57,http://schamberger.co/sammy,,160.145.67.18,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n1853,3,172,2017-01-03 14:30:43,http://balistrerimraz.name/rubye_corkery,,199.13.242.55,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n1854,3,172,2017-03-20 11:48:34,http://luettgenjakubowski.biz/kendra_maggio,,158.118.245.201,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n1855,3,172,2017-05-13 12:13:02,http://ullrich.info/glennie,,118.43.83.138,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n1856,3,172,2017-04-23 06:49:31,http://leffler.info/shayne.gerlach,,231.69.251.32,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n1857,3,172,2017-04-09 11:07:45,http://volkmanwisozk.info/kurtis_farrell,,231.69.251.32,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n1858,3,172,2017-02-04 10:41:09,http://konopelski.net/paul_roob,,161.103.20.231,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n1859,3,172,2016-12-21 18:32:37,http://greenholtmorar.net/mittie,,113.114.83.247,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n1860,3,172,2016-12-24 02:04:23,http://langworth.com/preston,,41.147.38.230,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n1861,3,172,2017-02-04 11:57:31,http://raynor.net/newell,,231.69.251.32,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n1862,3,173,2017-05-24 02:29:49,http://stokesbartoletti.com/maegan.turcotte,,114.80.90.81,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n1863,3,173,2017-06-08 03:43:51,http://kuphalherman.io/unique,,48.227.90.218,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n1864,3,173,2017-02-09 23:25:34,http://lind.name/braulio_kunze,,203.20.186.214,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n1865,3,173,2016-12-26 10:05:11,http://ko.info/dario_lebsack,,126.136.40.134,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n4523,7,421,2017-02-15 09:22:47,http://goyette.com/thurman_larson,,240.71.193.226,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n4524,7,421,2017-03-09 23:53:13,http://gorczany.info/luciano_ruecker,,183.153.195.153,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n4525,7,421,2017-04-14 23:31:22,http://corkery.org/alejandra_kub,,88.74.171.226,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n4526,7,421,2016-12-19 18:50:19,http://fay.biz/kaci,,240.71.193.226,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n4527,7,421,2017-04-06 20:05:00,http://ferry.io/levi,,228.122.111.103,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n4528,7,421,2017-05-07 23:58:14,http://braun.biz/annabel_hudson,,233.113.146.61,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n4529,7,422,2017-03-22 19:35:06,http://mcdermottbogisich.info/kay,,29.108.163.134,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n4530,7,422,2017-01-18 22:52:13,http://toy.info/magdalena,,175.131.173.136,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n4531,7,422,2017-05-29 09:39:15,http://cartwrightstoltenberg.com/norma.hickle,,124.228.244.139,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n4532,7,422,2017-04-07 19:52:24,http://monahan.name/jeramy,,224.220.122.194,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n4533,7,422,2017-01-09 05:30:43,http://reingeradams.com/emie_cummings,,61.110.200.130,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n4534,7,422,2017-01-26 14:11:26,http://gulgowskibernier.io/sabrina.olson,,249.92.31.210,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n4535,7,422,2017-01-03 18:55:51,http://corkery.net/flavie.gerlach,,48.12.25.71,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n4536,7,422,2016-12-31 01:29:00,http://prohaska.org/arden,,156.31.154.227,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n4537,7,423,2017-03-21 00:04:02,http://jenkins.net/ethel_cummings,,253.18.184.100,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n4538,7,423,2017-02-19 12:00:59,http://gerholdtreutel.co/emma,,34.229.144.194,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n4539,7,423,2017-04-04 01:53:21,http://oharalueilwitz.com/friedrich,,214.119.36.130,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n4540,7,423,2017-05-07 08:17:43,http://abernathy.info/oma.hilll,,23.65.181.76,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n4541,7,423,2016-12-23 07:23:43,http://stantonprosacco.org/tyree,,26.155.133.236,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n4542,7,423,2017-02-14 13:24:08,http://emard.name/nakia,,26.155.133.236,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n4543,7,423,2016-12-30 20:25:14,http://zemlak.net/shane_heaney,,69.70.244.149,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n4544,7,423,2017-04-26 07:35:12,http://cruickshank.com/adam,,94.164.60.44,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n4545,7,423,2017-06-08 01:52:05,http://kihnruel.biz/filomena_bartell,,149.89.214.146,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n4546,7,424,2017-04-03 07:38:15,http://cummings.info/chadrick,,160.137.192.108,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n4547,7,424,2017-01-03 22:12:00,http://hayes.org/dave,,173.63.211.162,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n4548,7,424,2017-04-21 10:55:45,http://gerhold.com/jimmie_bailey,,121.217.38.246,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n4549,7,424,2017-05-26 02:01:51,http://johnson.biz/joshua_hoppe,,210.37.191.178,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n4550,7,424,2017-03-16 21:58:26,http://bergstrom.org/jeff,,18.180.96.78,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n4551,7,424,2016-12-14 08:36:23,http://moenoberbrunner.com/carolyne.walker,,231.184.210.226,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n4552,7,424,2017-03-15 17:21:56,http://vandervort.info/dion.kilback,,173.63.211.162,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n4553,7,424,2017-01-02 09:47:47,http://frami.biz/katherine,,64.104.70.131,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n4554,7,424,2017-05-01 05:37:15,http://reinger.io/ken,,74.210.94.9,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n4555,7,424,2016-12-26 09:04:16,http://walterbins.biz/domenic.breitenberg,,210.37.191.178,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n4556,7,424,2017-02-03 00:08:25,http://ritchie.org/teagan.boyer,,48.244.116.212,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n4557,7,424,2017-02-25 05:15:40,http://stanton.io/evie_dubuque,,187.205.8.90,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n4558,7,424,2017-03-15 15:04:33,http://beer.com/kailey,,121.217.38.246,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n4559,7,424,2017-03-18 22:18:53,http://heelhilll.biz/jackeline,,131.234.29.227,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n4560,7,424,2017-01-02 19:31:43,http://skiles.org/akeem.waelchi,,130.127.38.18,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n4561,7,425,2017-04-18 15:33:03,http://haag.org/rhianna,,97.163.56.35,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n4562,7,425,2017-03-03 10:33:55,http://auerkonopelski.org/hipolito_brown,,161.190.65.146,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n4563,7,425,2017-03-24 22:55:25,http://vonrueden.com/dale_morar,,120.85.29.159,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n4564,7,425,2017-01-24 14:19:09,http://swaniawski.org/hope_wintheiser,,24.12.74.226,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n4565,7,425,2017-06-04 14:03:22,http://abbottkaulke.co/fanny.mills,,185.203.49.132,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n4566,7,425,2016-12-18 03:51:52,http://schmitt.com/mike,,226.227.212.57,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n4567,7,425,2017-02-25 04:15:14,http://dibbert.name/raymundo,,97.163.56.35,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n4568,7,425,2017-01-31 20:43:08,http://streichschultz.biz/modesto,,195.26.6.45,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n4569,7,425,2017-05-09 15:20:31,http://moriette.net/zelma.mitchell,,41.70.47.37,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n4570,7,425,2017-03-12 11:18:53,http://cartwright.biz/deangelo,,89.106.9.149,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n4571,7,425,2017-03-21 09:25:31,http://hayetokes.biz/zaria_yundt,,222.26.244.87,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n4572,7,425,2017-01-16 17:19:39,http://metz.io/hal,,166.19.229.38,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n4573,7,425,2017-04-21 17:38:56,http://roberts.name/elenora_sporer,,64.118.207.78,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n4574,7,425,2017-02-07 03:06:35,http://stracke.info/jeramie,,135.150.24.147,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n4575,7,425,2017-01-29 10:43:46,http://hermiston.io/curt.erdman,,38.109.185.45,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n4576,7,425,2017-04-30 12:45:44,http://ortiz.info/keanu,,60.204.163.71,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n4577,7,426,2017-05-29 08:37:48,http://koch.com/erich,,116.243.191.73,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n4578,7,427,2016-12-27 02:30:35,http://funkreichel.io/devonte,,242.176.119.226,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n1866,3,173,2017-06-07 14:45:59,http://stark.biz/abel_pacocha,,191.83.226.228,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n1867,3,174,2017-03-05 17:09:50,http://heathcote.com/rasheed,,197.141.244.39,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n1868,3,174,2017-05-26 14:25:07,http://collins.info/odie_kub,,22.198.131.82,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n1869,3,174,2017-04-26 19:02:35,http://dibbertmraz.name/guido,,153.190.139.140,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n1870,3,174,2017-05-14 14:07:27,http://rodriguez.org/earl_breitenberg,,33.44.42.254,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n1871,3,174,2017-04-27 23:54:50,http://haagjenkins.net/myriam.price,,22.198.131.82,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n1872,3,174,2017-03-11 03:46:10,http://danielturcotte.info/anabel.dibbert,,80.52.237.78,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n1873,3,174,2017-03-20 02:04:12,http://ohararau.info/levi,,129.123.94.198,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n1874,3,174,2017-02-18 07:05:53,http://robertserdman.io/myah,,238.102.115.248,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n1875,3,175,2017-04-02 07:19:06,http://hettinger.io/roger.legros,,217.125.64.197,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n1876,3,175,2017-03-08 21:54:52,http://altenwerthblock.org/mozelle,,62.29.23.214,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n1877,3,175,2017-03-05 02:03:34,http://bins.io/conrad,,138.223.173.84,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n1878,3,176,2017-05-12 01:16:31,http://grimes.org/angus,,29.116.32.144,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n1879,3,176,2017-05-03 09:26:03,http://donnelly.info/tremayne,,145.47.126.102,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n1880,3,176,2017-03-25 18:58:20,http://oconnell.org/joesph,,153.101.35.206,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n1881,3,176,2017-04-08 07:03:32,http://mante.info/christian_fahey,,41.148.12.75,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n1882,3,176,2016-12-16 09:26:03,http://schiller.org/jamal,,187.132.16.224,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n1883,3,176,2016-12-22 16:07:28,http://schumm.net/cali,,52.81.229.164,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n1884,3,176,2016-12-23 17:42:59,http://mills.com/amparo_romaguera,,187.132.16.224,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n1885,3,176,2017-01-28 14:53:14,http://breitenberg.com/vernie,,153.101.35.206,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n1886,3,176,2017-04-17 08:22:08,http://rath.io/christop,,250.223.15.215,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n1887,3,176,2016-12-14 11:41:00,http://ernser.io/augustus,,106.195.199.145,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n1888,3,176,2017-01-27 12:38:16,http://rogahnankunding.co/lula,,90.243.14.23,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n1889,3,176,2017-01-29 10:55:47,http://reynoldsmacgyver.biz/karlee_rodriguez,,70.188.75.108,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n1890,3,176,2017-01-25 18:36:38,http://schummtorphy.net/retta,,195.181.193.147,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n1891,3,176,2017-05-12 04:26:58,http://konopelski.name/tillman_prohaska,,152.217.34.46,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n1892,3,176,2017-05-31 17:31:23,http://tromp.co/madalyn,,209.168.41.244,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n1893,3,176,2017-03-15 03:26:42,http://wilkinsonwolf.name/bertha_wyman,,187.132.16.224,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n1894,3,176,2017-06-03 22:17:43,http://gibson.io/scot.toy,,243.216.143.95,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n1895,3,176,2017-04-03 00:12:02,http://pfannerstill.name/burnice_pfeffer,,249.136.15.48,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n1896,3,177,2017-01-23 22:32:18,http://williamsonmosciski.co/mertie,,211.209.232.100,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n1897,3,177,2016-12-15 04:06:22,http://thompson.io/quentin,,190.176.70.107,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n1898,3,177,2017-02-13 08:43:41,http://reillyauer.info/amir,,137.55.229.238,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n1899,3,178,2017-03-16 00:45:00,http://reichertbecker.biz/franco,,216.208.125.128,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n1900,3,178,2017-01-19 03:20:25,http://douglas.net/agustina,,227.37.114.91,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n1901,3,178,2017-06-01 08:12:21,http://sipes.io/pearline,,83.117.73.154,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n1902,3,178,2017-01-12 18:19:31,http://smithamwaters.org/jaquan,,208.35.237.81,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n1903,3,178,2017-02-28 08:01:07,http://hammes.com/grover,,231.30.202.237,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n1904,3,178,2017-01-03 15:35:38,http://mosciski.net/sigmund,,75.195.26.87,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n1905,3,178,2016-12-17 02:16:21,http://kaulke.name/hank_schimmel,,216.237.27.134,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n1906,3,178,2017-04-01 18:47:58,http://murray.name/hal,,244.62.96.220,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n1907,3,178,2017-01-18 20:35:14,http://kuhn.net/johnpaul.wintheiser,,68.104.67.230,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n1908,3,178,2017-05-31 18:15:25,http://harveyrippin.co/ahmed,,157.81.223.139,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n1909,3,178,2017-04-11 15:51:03,http://wolfluettgen.net/riley.torphy,,24.248.47.39,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n1910,3,178,2017-05-04 23:32:32,http://wardhirthe.io/alisha.kuphal,,253.89.115.251,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n1911,3,178,2017-06-10 04:31:52,http://lang.name/mavis,,75.195.26.87,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n1912,3,178,2017-03-24 23:38:00,http://schultzluettgen.co/shakira,,203.12.71.156,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n1913,3,179,2017-04-10 02:38:25,http://dietrichreinger.info/myron,,70.252.178.31,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n1914,3,179,2017-04-11 00:51:51,http://oconnellbergnaum.net/karianne.terry,,102.69.153.25,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n1915,3,179,2017-02-22 05:39:55,http://willbogisich.name/jade,,48.80.3.149,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n1916,3,179,2017-06-12 11:18:34,http://lockmanlangworth.info/alvah,,62.122.106.196,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n1917,3,179,2017-01-30 18:26:57,http://schamberger.com/mariane_rodriguez,,71.29.82.113,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n1918,3,180,2017-05-26 02:54:32,http://altenwerth.biz/andy,,96.92.33.43,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n1919,3,180,2016-12-19 05:19:15,http://leannon.com/adeline,,50.243.24.191,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n1920,3,180,2017-06-08 12:09:14,http://lebsack.info/wilma.nitzsche,,76.159.60.49,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n1921,3,180,2017-05-04 17:50:54,http://senger.io/zena.goyette,,158.250.29.41,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n4579,7,428,2017-04-22 19:15:00,http://connellykulas.org/nicolette_hodkiewicz,,206.250.107.94,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n4580,7,428,2017-04-10 00:09:33,http://nienow.com/angel_kunde,,90.52.186.168,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n4581,7,428,2017-02-19 15:12:36,http://brownfahey.com/leanne_cain,,233.32.190.202,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n4582,7,429,2017-02-26 13:49:40,http://sporer.co/muriel,,57.55.57.242,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n4583,7,429,2017-02-22 00:05:47,http://romaguerabailey.co/shemar,,170.212.190.166,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n4584,7,429,2017-01-01 08:20:46,http://kautzerhansen.com/johathan_ernser,,89.159.147.227,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n4585,7,429,2017-06-05 08:20:54,http://stark.info/amber,,120.219.181.106,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n4586,7,430,2017-01-27 06:52:43,http://wuckert.org/craig_fahey,,90.26.234.72,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n4587,7,430,2017-05-19 21:12:16,http://senger.io/janae,,201.142.201.96,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n4588,7,430,2017-05-20 02:38:29,http://danielko.info/lenna,,135.47.32.71,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n4589,7,430,2017-01-21 06:39:37,http://wilkinsoncarter.co/annette_pagac,,156.121.29.145,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n4590,7,430,2017-03-02 20:23:47,http://okon.io/raegan_hermann,,93.136.75.60,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n4591,7,430,2017-04-26 15:17:05,http://heaney.co/thea.kohler,,93.136.75.60,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n4592,7,430,2016-12-28 15:32:44,http://smithrogahn.org/jasmin.swift,,16.130.176.189,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n4593,7,430,2017-04-20 21:29:37,http://predovicerdman.com/ariel,,246.127.18.205,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n4594,7,430,2017-05-25 23:18:36,http://langworth.net/brent,,230.168.142.153,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n4595,7,430,2016-12-14 16:01:34,http://schusterweimann.com/hellen,,201.142.201.96,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n4596,7,430,2017-02-10 23:25:04,http://adamsfriesen.io/kayley.schoen,,80.22.76.105,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n4597,7,431,2017-01-30 10:17:56,http://romaguerahalvorson.info/dorthy,,24.184.195.38,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n4598,7,431,2017-04-20 21:58:03,http://jenkinskirlin.biz/eloy,,191.75.33.200,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n4599,7,431,2017-05-25 12:41:19,http://feeney.name/june,,191.75.33.200,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n4600,7,431,2017-03-26 07:49:46,http://grantcorwin.info/flo,,90.252.233.46,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n4601,7,431,2017-04-26 13:32:12,http://brekke.io/glenda.ziemann,,181.172.13.246,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n4602,7,431,2017-02-02 06:11:35,http://sporer.biz/vladimir.bashirian,,239.82.225.47,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n4603,7,431,2017-04-26 23:37:17,http://maggio.name/kay.gusikowski,,28.88.38.188,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n4604,7,431,2017-01-09 19:01:04,http://hoppe.biz/tianna_kiehn,,216.110.113.224,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n4605,7,431,2016-12-25 02:00:31,http://rodriguez.org/jovanny_kris,,195.47.44.38,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n4606,7,431,2017-01-07 17:25:32,http://bartell.biz/fanny,,18.136.59.74,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n4607,7,431,2017-04-29 10:12:23,http://beier.name/tyreek,,138.12.234.163,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n4608,7,431,2017-02-15 06:46:50,http://abshire.io/erick,,104.208.102.131,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n4609,7,431,2017-01-17 22:13:09,http://cronin.info/caitlyn,,21.170.196.238,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n4610,7,431,2017-03-17 03:56:14,http://muellerquigley.io/estel,,111.152.218.206,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n4611,7,431,2017-04-11 08:23:51,http://walsh.org/grady.morar,,187.202.62.225,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n4612,7,431,2017-01-19 13:51:08,http://zboncakwisozk.co/janick,,191.75.33.200,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n4613,7,431,2017-05-28 22:49:17,http://greenholt.com/leie.parker,,179.161.141.200,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n4614,7,431,2017-01-13 09:45:24,http://bogisichbechtelar.info/irma.moore,,183.61.79.211,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n4615,7,431,2017-04-11 18:33:34,http://reichert.com/marquis,,148.105.206.150,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n4616,7,432,2017-02-12 08:58:54,http://block.io/zakary,,87.56.76.116,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n4617,7,433,2016-12-30 05:01:55,http://mohr.com/angeline,,49.27.9.247,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n4618,7,433,2017-01-11 09:51:24,http://mueller.name/hank.eichmann,,185.153.2.51,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n4619,7,433,2017-05-16 10:45:43,http://weber.info/kristin,,197.132.77.129,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n4620,7,433,2017-01-07 11:02:57,http://corkery.biz/antwon.hane,,10.188.10.36,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n4621,7,433,2017-03-13 21:07:45,http://cole.com/lamont,,154.237.210.111,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n4622,7,433,2017-01-31 10:22:50,http://rauharvey.co/rebecca,,197.132.77.129,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n4623,7,433,2017-01-19 10:54:55,http://greenholt.com/myles,,197.132.77.129,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n4624,7,433,2017-02-20 12:00:23,http://prosacco.io/eve.hettinger,,10.188.10.36,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n4625,7,433,2017-02-24 21:18:51,http://senger.info/micheal_schiller,,182.230.141.79,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n4626,7,433,2016-12-22 18:41:21,http://aufderharmonahan.co/naomi_jerde,,244.168.87.81,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n4627,7,433,2017-01-09 22:44:20,http://goyette.net/filiberto.schultz,,249.104.225.210,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n4628,7,433,2017-01-30 23:31:11,http://bauch.net/vince.yundt,,67.37.16.120,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4629,7,433,2017-06-02 14:36:55,http://mullerturcotte.org/meta,,205.36.198.39,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n4630,7,434,2017-03-24 19:52:44,http://kulas.org/josephine,,120.171.34.47,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n4631,7,434,2017-02-24 17:23:46,http://hansenruecker.name/savannah,,175.35.155.227,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n4632,7,434,2017-06-07 11:13:23,http://miller.co/alyon,,195.51.198.33,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n4633,7,434,2017-05-27 21:10:37,http://oberbrunner.co/floy_macgyver,,39.236.5.194,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n4634,7,434,2017-02-25 19:23:28,http://kohler.io/wilhelm,,108.123.110.175,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n1922,3,180,2017-05-13 17:50:19,http://hackett.co/hannah.crooks,,242.126.154.88,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n1923,3,180,2017-05-09 07:07:51,http://dicki.io/olaf.hahn,,193.136.184.11,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n1924,3,181,2017-02-26 01:39:23,http://doyle.biz/ramiro,,97.90.32.78,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n1925,3,181,2017-06-09 06:38:10,http://metz.info/maximo,,244.116.9.235,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n1926,3,181,2017-03-27 21:44:28,http://greenfelder.biz/roger,,111.142.223.84,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n1927,3,181,2017-01-15 04:11:50,http://lind.net/rosetta,,120.153.176.227,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n1928,3,181,2016-12-15 20:12:17,http://stehrhamill.io/jeromy,,230.245.7.38,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n1929,3,181,2017-04-16 23:58:12,http://hayes.info/chasity.keebler,,67.127.128.240,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n1930,3,181,2017-04-30 13:34:35,http://tremblay.org/corine_beatty,,120.153.176.227,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n1931,3,181,2017-06-13 22:57:07,http://tremblay.com/roy_kovacek,,118.48.81.179,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n1932,3,181,2017-03-04 01:08:09,http://berge.io/summer_kuvalis,,22.168.101.31,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n1933,3,181,2017-01-14 18:04:43,http://shanahan.net/iliana.gusikowski,,22.168.101.31,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n1934,3,182,2017-04-05 20:12:36,http://jerde.biz/julianne,,232.9.210.116,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n1935,3,182,2017-04-04 03:21:37,http://prohaska.net/skyla_green,,202.202.9.117,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n1936,3,182,2017-02-26 20:50:50,http://wunschnienow.info/luigi.weimann,,41.167.185.5,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n1937,3,182,2017-05-27 11:14:01,http://swaniawski.biz/chaz_harber,,202.202.9.117,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n1938,3,182,2017-05-26 20:23:05,http://schimmelmarks.co/giovanna,,32.74.127.21,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n1939,3,182,2017-06-07 19:10:55,http://terry.com/hildegard.mcglynn,,64.22.192.142,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n1940,3,182,2017-06-11 19:21:19,http://west.biz/dashawn.runolfsdottir,,163.206.171.216,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n1941,3,182,2017-05-28 03:02:34,http://deckow.com/adah,,21.141.141.76,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n1942,3,182,2017-03-16 12:08:39,http://mohr.name/ernest_jones,,161.138.57.117,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n1943,3,182,2017-05-29 12:44:13,http://weinat.biz/hellen.schowalter,,91.59.129.8,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n1944,3,182,2017-02-09 08:33:20,http://simonis.com/mattie,,114.46.231.125,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n1945,3,182,2017-03-15 00:52:39,http://paucek.info/holly.schuppe,,208.49.8.5,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n1946,3,182,2017-01-20 07:43:54,http://rogahn.com/van,,91.59.129.8,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n1947,3,183,2017-04-16 18:07:27,http://robertsdibbert.com/joelle,,132.58.27.89,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n1948,3,183,2017-03-29 06:10:37,http://stamm.info/bella_effertz,,162.81.235.124,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n1949,3,183,2017-05-14 13:38:42,http://vandervort.name/shayne_gleason,,142.253.16.83,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n1950,3,183,2017-05-20 01:41:01,http://kertzmann.io/josefa,,126.214.141.239,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n1951,3,183,2017-04-11 00:44:01,http://dietrich.org/amina.lynch,,57.111.46.95,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n1952,3,183,2017-02-12 03:02:24,http://kihn.net/paige,,243.232.183.61,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n1953,3,183,2017-03-30 17:14:37,http://hartmann.co/ludwig,,2.21.48.235,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n1954,3,183,2016-12-28 19:49:05,http://gleichner.co/grover.tremblay,,149.212.50.31,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n1955,3,183,2017-03-03 15:25:44,http://mckenziehackett.com/august_keeling,,204.247.135.39,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n1956,3,183,2017-06-13 22:35:21,http://rau.net/hadley_murazik,,185.157.96.10,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n1957,3,183,2017-05-14 06:13:05,http://haley.co/lilly,,254.188.137.215,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n1958,3,183,2017-05-13 22:40:06,http://mohr.biz/camron_rogahn,,21.129.194.194,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n1959,3,183,2017-02-07 18:15:10,http://ritchiecrooks.name/kianna_wilkinson,,251.150.89.113,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n1960,3,183,2017-06-03 19:04:46,http://dooley.name/eliza,,132.58.27.89,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n1961,3,183,2017-02-26 10:39:24,http://fritschrunolfsdottir.com/joany,,4.102.146.57,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n1962,3,183,2017-01-07 18:40:35,http://gibson.net/guiseppe.ortiz,,215.143.77.74,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n1963,3,184,2017-06-01 02:21:21,http://orn.info/anabel,,210.89.86.100,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n1964,3,184,2016-12-14 00:39:21,http://fisher.info/gardner,,119.12.150.23,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n1965,3,184,2017-03-18 06:29:03,http://spencer.info/ashtyn,,37.92.143.50,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n1966,3,184,2017-01-23 12:36:12,http://howellpagac.biz/billie_larkin,,79.224.6.114,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n1967,3,185,2017-06-06 07:56:51,http://friesen.name/miller.hettinger,1.0000000000,204.161.83.125,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n1968,3,185,2016-12-24 22:40:51,http://wilkinsonolson.com/zachariah,1.0000000000,166.41.212.144,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n1969,3,185,2017-03-03 19:16:06,http://hodkiewiczoconnell.info/daisha.crooks,1.0000000000,52.18.167.24,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n1970,3,185,2016-12-21 10:40:12,http://kunde.com/dolores_romaguera,0.0000000000,194.199.204.225,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n1971,3,185,2017-04-04 12:20:17,http://pfannerstillveum.io/wanda_volkman,1.0000000000,191.165.179.231,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n1972,3,185,2017-01-17 04:16:59,http://rowehartmann.org/bettye_thiel,0.0000000000,22.175.25.12,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n1973,3,185,2017-05-18 06:41:57,http://waelchi.io/noemi.jacobson,0.0000000000,210.251.44.245,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n1974,3,185,2017-03-17 11:58:41,http://stracke.name/gerry_walker,1.0000000000,166.41.212.144,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n1975,3,185,2016-12-14 02:08:04,http://rogahn.info/nicklaus,0.0000000000,190.36.116.76,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n4635,7,435,2017-03-08 20:06:14,http://mueller.info/alanna.kuhlman,,163.161.44.146,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n4636,7,435,2017-03-23 16:35:35,http://ratkevandervort.org/anita_okeefe,,210.45.92.156,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n4637,7,435,2017-05-11 23:26:51,http://haleypagac.net/susie_oconner,,235.195.250.23,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n4638,7,435,2017-05-28 03:04:27,http://mannschuster.name/austyn,,163.161.44.146,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n4639,7,435,2017-03-21 00:38:58,http://nitzsche.name/agustina,,227.230.42.124,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n4640,7,435,2017-01-27 13:54:39,http://gerholdhalvorson.net/scotty.wolf,,7.145.18.113,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n4641,7,435,2017-05-24 02:55:37,http://schaefer.co/astrid,,227.230.42.124,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n4642,7,435,2017-03-07 14:25:56,http://koch.com/jennings.rutherford,,241.212.190.57,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n4643,7,435,2017-05-01 19:25:17,http://mcclurewolf.biz/joelle.beier,,157.141.85.23,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n4644,7,435,2017-02-14 15:22:52,http://tremblay.biz/elroy.barrows,,222.15.9.50,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n4645,7,436,2016-12-21 01:32:25,http://swaniawskibradtke.net/teie_lowe,,195.229.246.38,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n4646,7,436,2017-05-17 21:24:09,http://rosenbaumreichel.org/lew,,59.19.248.161,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n4647,7,436,2017-04-10 07:05:53,http://ullrich.name/bradly,,110.223.82.214,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n4648,7,437,2017-06-12 14:33:06,http://kris.biz/dawn,,235.33.238.110,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n4649,7,437,2017-03-12 03:31:01,http://emmerich.org/dee_robel,,20.149.196.234,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n4650,7,437,2017-04-27 03:14:31,http://nienow.net/odie_stanton,,29.6.112.247,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n4651,7,437,2017-04-17 16:54:41,http://sauer.com/frankie,,174.223.128.77,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n4652,7,437,2017-04-07 13:17:47,http://kulasdurgan.com/aurelia,,102.75.217.148,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n4653,7,437,2017-05-25 09:44:55,http://lynchjohnston.net/josephine_kuhn,,3.137.121.179,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n4654,7,437,2017-05-15 10:17:22,http://murraydubuque.com/sierra.rosenbaum,,217.248.57.88,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n4655,7,437,2017-05-31 02:52:00,http://bauchlockman.com/tierra,,145.206.124.176,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n4656,7,437,2017-01-18 09:10:44,http://emmerich.com/jules_corkery,,235.33.238.110,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n4657,7,437,2017-05-06 07:23:34,http://marquardt.org/thora_bechtelar,,70.56.135.157,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n4658,7,437,2016-12-16 15:13:16,http://kiehn.name/vergie,,245.77.116.195,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n4659,7,437,2017-02-20 11:57:05,http://purdy.biz/river,,154.145.53.191,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n4660,7,437,2017-01-18 19:06:35,http://heaneyhammes.name/frederique,,70.56.135.157,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n4661,7,437,2017-04-23 23:56:09,http://ankunding.name/tania,,18.19.143.129,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n4662,7,437,2017-02-14 14:04:50,http://wisokyvandervort.name/shana.wyman,,88.173.33.78,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n4663,7,437,2016-12-30 17:18:10,http://lednerheathcote.org/ludie.miller,,240.158.206.127,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n4664,7,437,2017-01-03 19:45:17,http://ohara.org/mae.ryan,,43.117.12.97,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n4665,7,437,2017-05-04 13:48:36,http://grady.net/jacey,,245.77.116.195,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n4666,7,438,2017-05-18 02:00:29,http://gutmann.org/bertha,,140.141.117.141,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n4667,7,438,2017-02-07 19:36:37,http://hyatt.io/chesley,,61.167.7.92,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n4668,7,438,2017-05-14 11:52:10,http://hyatthilpert.name/luther.auer,,210.187.92.59,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n4669,7,438,2017-02-11 22:23:48,http://gutmann.biz/alvina,,44.105.205.181,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n4670,7,438,2017-01-27 08:44:40,http://wuckerthoeger.org/danny.gulgowski,,113.210.153.54,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n4671,7,439,2017-04-25 12:19:38,http://brekkeharber.org/millie,,2.226.169.138,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n4672,7,439,2017-03-03 17:54:12,http://borer.name/nola_schuppe,,200.38.169.156,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n4673,7,439,2017-01-28 17:28:26,http://simoniskunze.info/larue.harber,,79.38.133.76,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n4674,7,439,2017-03-31 18:52:34,http://yundt.io/claudia_weinat,,119.52.63.38,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n4675,7,439,2017-06-04 05:43:53,http://conroy.com/elia.schimmel,,94.160.201.19,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n4676,7,439,2017-04-18 16:33:17,http://mcdermottrohan.info/jonathon,,162.133.162.110,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n4677,7,439,2017-01-23 00:46:00,http://price.info/leonora_schinner,,13.127.120.195,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n4678,7,440,2017-02-28 12:46:30,http://kolynch.info/fleta,,90.142.142.54,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n4679,7,440,2017-03-10 09:42:05,http://conroycartwright.biz/nick.wiegand,,186.10.242.234,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n4680,7,440,2016-12-27 07:59:31,http://quitzon.co/dandre,,206.51.133.225,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n4681,7,440,2017-02-10 14:35:52,http://blick.info/donnie.ullrich,,90.142.142.54,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n4682,7,440,2017-03-02 19:55:40,http://gulgowski.com/alivia_mills,,19.210.52.177,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n4683,7,440,2017-02-21 04:48:34,http://reichel.co/efrain_bechtelar,,46.16.67.82,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n4684,7,440,2017-02-27 09:08:44,http://mcdermott.org/fredrick.mitchell,,126.115.176.56,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n4685,7,440,2017-05-08 02:12:55,http://bins.info/brendan,,254.70.60.222,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n4686,7,440,2017-02-07 21:53:40,http://schuppe.info/ulises,,192.66.126.228,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n4687,7,440,2016-12-24 10:08:47,http://manndeckow.info/oceane_jones,,110.154.146.193,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n4688,7,440,2017-03-27 18:07:00,http://cain.name/elia_collins,,28.159.193.222,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n4689,7,440,2017-03-31 14:11:39,http://quigley.net/citlalli,,135.165.130.108,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n1976,3,185,2016-12-19 21:18:33,http://deckow.net/carol,0.0000000000,215.89.210.165,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n1977,3,185,2017-06-12 04:45:58,http://bruen.name/alize,1.0000000000,52.18.167.24,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n1978,3,185,2017-03-13 21:59:23,http://batz.org/maverick_treutel,1.0000000000,26.66.118.123,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n1979,3,185,2017-06-03 11:07:21,http://cainstehr.org/frankie.schulist,1.0000000000,176.218.219.222,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n1980,3,185,2017-04-01 00:02:15,http://danieldooley.net/adrien,1.0000000000,18.176.153.136,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n1981,3,185,2017-01-15 15:35:32,http://friesen.org/hannah,0.0000000000,26.66.118.123,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n1982,3,185,2017-01-20 14:14:00,http://connelly.com/vickie_cronin,1.0000000000,204.66.140.187,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n1983,3,186,2017-01-16 15:08:01,http://simonis.org/rosella,0.0000000000,194.230.130.110,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n1984,3,186,2017-04-07 13:57:29,http://konopelskimarquardt.org/geraldine.becker,1.0000000000,254.99.118.243,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n1985,3,186,2017-03-23 20:01:50,http://langworth.name/newton.glover,3.0000000000,41.138.177.6,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n1986,3,186,2017-04-30 19:01:27,http://hauck.biz/brooklyn,2.0000000000,55.79.11.203,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n1987,3,186,2017-04-15 06:01:09,http://graham.co/sven,2.0000000000,23.28.74.68,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n1988,3,186,2017-05-13 14:15:22,http://kreiger.biz/joanie.lockman,0.0000000000,183.104.81.108,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n1989,3,186,2017-02-07 20:05:31,http://rippinkohler.net/amira,0.0000000000,25.40.112.109,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n1990,3,186,2017-01-13 03:56:49,http://wizakoepp.net/hector_conn,0.0000000000,114.35.37.106,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n1991,3,186,2017-01-29 06:17:18,http://hoppe.name/arlo_carter,2.0000000000,23.28.74.68,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n1992,3,186,2017-01-30 13:22:46,http://schroeder.com/noemi_hackett,1.0000000000,202.87.236.216,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n1993,3,186,2017-03-15 20:49:02,http://millerlebsack.com/noelia,2.0000000000,67.128.157.145,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n1994,3,186,2016-12-18 18:52:15,http://pollichquitzon.net/edgardo,2.0000000000,194.230.130.110,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n1995,3,186,2017-05-18 06:18:09,http://howell.info/madonna_stroman,0.0000000000,25.40.112.109,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n1996,3,186,2017-02-08 03:49:40,http://gleasonwill.com/eldon,3.0000000000,183.104.81.108,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n1997,3,186,2017-04-11 19:49:24,http://heaneydaugherty.io/jamison.davis,0.0000000000,194.230.130.110,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n1998,3,186,2017-02-06 00:48:00,http://grant.info/antwan,3.0000000000,59.13.216.153,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n1999,3,186,2017-02-12 01:39:08,http://koch.biz/dangelo_littel,2.0000000000,141.171.126.232,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n2000,3,186,2017-03-06 06:07:12,http://vonrueden.info/brice.mraz,1.0000000000,46.7.117.248,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n2001,3,186,2017-03-02 07:09:34,http://marvin.io/dina,2.0000000000,183.104.81.108,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n2002,3,187,2017-03-04 15:48:46,http://murphyreichel.org/jacey,1.0000000000,60.235.147.157,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n2003,3,187,2017-05-21 19:13:45,http://brekke.io/rickey,0.0000000000,87.48.234.69,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n2004,3,187,2017-04-30 20:29:30,http://hoppewiegand.biz/theron,0.0000000000,54.187.136.187,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n2005,3,187,2016-12-31 22:54:12,http://emmerich.name/juliana,3.0000000000,198.92.230.78,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n2006,3,187,2016-12-31 10:10:50,http://johnson.org/jovan,3.0000000000,105.5.113.175,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n2007,3,187,2017-02-22 01:06:00,http://dare.biz/crystel.brakus,1.0000000000,155.16.87.48,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n2008,3,187,2017-03-01 11:33:03,http://hahn.io/paul_halvorson,0.0000000000,4.131.201.25,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n2009,3,187,2017-05-29 13:57:05,http://dickinson.com/stefanie,0.0000000000,190.26.163.225,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n2010,3,187,2017-04-25 04:39:37,http://kuvalis.io/conner,3.0000000000,21.132.243.146,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n2011,3,188,2017-05-19 15:53:03,http://bahringerbayer.co/ceasar_bradtke,3.0000000000,65.27.5.82,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n2012,3,189,2017-05-31 21:29:54,http://jaskolski.biz/mohammad.wehner,1.0000000000,106.193.27.120,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n2013,3,189,2017-04-30 12:56:11,http://will.name/roman.daugherty,0.0000000000,249.86.66.79,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n2014,3,189,2017-03-27 07:15:33,http://padberg.co/stevie,1.0000000000,21.108.116.37,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n2015,3,189,2017-06-13 02:39:59,http://skiles.net/lucinda.gislason,1.0000000000,174.128.189.115,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n2016,3,189,2017-05-02 20:06:32,http://leannon.co/amara_jacobson,0.0000000000,174.128.189.115,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n2017,3,189,2017-01-23 13:13:44,http://little.net/danika,2.0000000000,19.41.229.37,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n2018,3,189,2017-05-16 14:36:01,http://jakubowski.name/mckayla_legros,1.0000000000,125.116.176.125,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n2019,3,189,2017-05-31 04:48:47,http://hahnnienow.co/jayden,1.0000000000,174.128.189.115,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n2020,3,189,2017-04-23 18:09:14,http://langsporer.org/seamus_zemlak,2.0000000000,250.77.42.66,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n2021,3,189,2017-02-10 07:46:17,http://murazik.io/berneice.steuber,1.0000000000,188.191.203.189,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n2022,3,189,2017-02-07 16:20:10,http://dubuquebeahan.org/moses,0.0000000000,167.79.244.178,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n2023,3,190,2017-03-12 10:19:40,http://bayer.biz/neha_doyle,3.0000000000,129.222.235.173,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n2024,3,190,2017-03-07 16:55:57,http://jakubowskihegmann.net/jasmin,0.0000000000,37.101.21.89,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n2025,3,190,2017-01-12 22:21:13,http://dooley.info/colten_kilback,3.0000000000,64.189.155.78,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n2026,3,190,2017-03-17 01:07:57,http://cristbergnaum.io/guillermo_rowe,3.0000000000,226.89.140.188,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n2027,3,190,2016-12-27 15:42:10,http://bode.org/macey_anderson,0.0000000000,136.107.159.116,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n4690,7,440,2017-04-18 13:35:28,http://danielkunde.org/kaleigh,,110.154.146.193,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n4691,7,440,2017-01-28 21:30:03,http://gulgowski.org/shanon_west,,122.51.13.2,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n4692,7,440,2017-02-27 13:39:01,http://mohr.com/vance_mcdermott,,206.51.133.225,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n4693,7,441,2017-03-06 23:31:46,http://cummeratawunsch.name/mark,,113.209.172.182,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n4694,7,441,2017-05-06 13:17:39,http://nicolas.net/myrtie.yost,,132.215.234.54,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n4695,7,441,2017-01-17 00:35:49,http://witting.name/verona,,213.154.233.165,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n4696,7,441,2017-03-30 10:27:51,http://hueldaugherty.net/name,,135.129.122.175,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n4697,7,442,2017-02-13 01:32:22,http://roberts.net/lea_moriette,,234.182.178.71,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n4698,7,442,2017-02-16 03:17:15,http://runolfon.biz/carmen,,159.108.17.208,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n4699,7,442,2017-03-23 13:05:01,http://okuneva.info/tyrell,,95.235.90.20,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n4700,7,442,2017-06-13 02:51:48,http://waters.info/joel,,18.120.188.247,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n4701,7,442,2016-12-17 01:01:10,http://christiansen.biz/kiana,,207.85.133.18,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n4702,7,442,2017-03-28 19:59:03,http://raynor.com/kattie_spinka,,56.54.157.221,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n4703,7,443,2017-01-31 19:32:46,http://cartwright.info/jerrold.hudson,,162.242.233.211,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n4704,7,443,2017-04-02 22:17:26,http://ruel.biz/jensen,,161.171.191.246,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n4705,7,443,2017-06-02 18:37:58,http://reichert.info/jeanette,,224.52.230.42,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n4706,7,443,2017-04-28 16:01:17,http://lindgren.io/carey,,117.224.111.20,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n4707,7,443,2017-03-04 05:07:41,http://kulas.org/darrell,,168.168.24.164,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n4708,7,443,2017-02-17 21:16:38,http://bogan.co/ima.gusikowski,,195.111.155.142,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n4709,7,443,2017-02-22 04:06:54,http://lehnergreenholt.io/karelle_schuster,,190.46.193.20,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n4710,7,443,2017-04-19 18:00:43,http://wiegandprohaska.net/america,,233.232.180.152,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n4711,7,444,2017-05-15 06:08:00,http://heathcote.info/kenneth.zboncak,,106.28.52.207,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n4712,7,444,2017-05-03 13:25:00,http://kuhlmanlueilwitz.org/gregg,,118.162.201.5,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n4713,7,444,2017-03-08 10:00:26,http://pfannerstill.io/gerry,,204.19.242.138,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n4714,7,444,2017-06-13 01:52:11,http://uptonbraun.info/braden.farrell,,246.112.130.187,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n4715,7,444,2016-12-22 00:22:58,http://connelly.io/baylee,,233.154.231.76,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n4716,7,444,2017-02-19 11:34:51,http://lindgren.co/nikki,,203.165.169.33,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n4717,7,444,2017-02-09 01:51:36,http://yundtgrimes.org/nelda.schuster,,188.61.178.42,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n4718,7,444,2017-02-12 07:51:56,http://kris.com/ryley,,204.19.242.138,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n4719,7,444,2017-03-24 00:03:13,http://kreiger.info/bulah,,106.28.52.207,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n4720,7,444,2017-04-15 10:39:37,http://hermann.com/anais,,131.139.37.67,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n4721,7,445,2017-03-31 03:53:01,http://schowalter.net/genesis.smitham,,71.229.22.234,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n4722,7,445,2017-04-12 21:14:26,http://johnston.org/troy.franecki,,198.133.88.32,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n4723,7,445,2017-04-03 07:16:22,http://hoppe.info/josianne_damore,,19.37.183.148,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n4724,7,445,2016-12-19 14:20:54,http://parker.com/mabel,,121.71.97.214,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n4725,7,445,2017-04-05 19:27:20,http://bernierreynolds.biz/thurman_bartell,,48.90.251.169,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n4726,7,445,2017-03-12 16:43:45,http://labadieschroeder.biz/kennedy_haag,,247.107.142.162,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n4727,7,445,2017-05-01 00:36:42,http://windlerschmidt.com/obie,,100.2.186.95,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n4728,7,446,2017-02-13 07:17:11,http://hermiston.org/ottilie,,147.228.41.201,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n4729,7,446,2017-01-10 21:30:07,http://cole.name/stan_emard,,128.241.231.28,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n4730,7,446,2017-05-23 13:34:33,http://collier.name/yazmin.wintheiser,,254.3.234.220,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n4731,7,446,2017-02-12 17:38:38,http://boscomcglynn.net/clifford,,228.197.201.81,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n4732,7,446,2017-04-14 17:12:57,http://durgan.com/freeman,,71.19.106.211,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n4733,7,446,2017-01-15 17:06:01,http://strackewaters.biz/sheldon,,164.97.15.102,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n4734,7,446,2017-03-05 09:22:12,http://spinka.com/arch,,133.218.163.19,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n4735,7,446,2017-03-28 10:55:59,http://kirlinmcclure.name/bettie.wisozk,,228.19.222.60,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n4736,7,446,2017-05-03 12:50:25,http://trantowbode.net/lucie.collier,,90.102.241.179,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n4737,7,446,2017-01-05 20:25:35,http://mohr.name/marcia_kuhlman,,170.165.211.96,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n4738,7,446,2017-03-24 16:02:04,http://bergnaum.info/raheem_becker,,101.191.250.189,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n4739,7,447,2017-06-11 02:23:31,http://schmidt.com/modesto_will,,204.66.243.246,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n4740,7,447,2017-02-13 11:45:05,http://sanfordgrimes.name/maribel_wiza,,193.169.73.200,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n4741,7,447,2016-12-30 05:52:56,http://kunde.com/cordie,,216.185.40.241,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n4742,7,447,2017-03-07 02:42:29,http://langosh.biz/kathryne.greenholt,,216.185.40.241,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n4743,7,447,2017-02-04 03:10:48,http://crooks.info/jewell_swift,,110.214.183.142,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n4744,7,447,2017-05-31 20:51:12,http://morarankunding.name/billy.blanda,,83.81.14.168,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n2028,3,190,2017-01-26 13:15:18,http://volkmankozey.org/rosalind_homenick,3.0000000000,93.238.6.177,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n2029,3,190,2017-01-09 07:39:03,http://bednar.info/bobby,2.0000000000,129.222.235.173,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n2030,3,191,2017-03-22 01:43:42,http://reynolds.com/imani_cummerata,0.0000000000,217.80.168.68,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n2031,3,191,2017-01-26 23:35:15,http://harvey.net/judah_koepp,0.0000000000,169.139.198.68,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n2032,3,191,2017-02-02 07:48:39,http://sporer.biz/andy.nikolaus,3.0000000000,101.19.25.199,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n2033,3,191,2017-04-30 05:54:40,http://sauermills.name/antonio.toy,3.0000000000,154.242.211.48,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n2034,3,191,2017-05-29 23:46:59,http://herman.biz/emilia,3.0000000000,56.167.37.6,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n2035,3,191,2017-06-03 08:19:18,http://kaulke.io/saige,1.0000000000,32.137.9.40,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n2036,3,191,2017-03-19 13:23:07,http://legroshoppe.com/gregorio,2.0000000000,61.140.5.92,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n2037,3,191,2017-01-12 00:25:06,http://schimmel.net/samson.wintheiser,3.0000000000,228.9.79.167,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n2038,3,191,2017-02-20 14:47:11,http://heathcote.biz/raheem,2.0000000000,4.4.182.34,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n2039,3,191,2017-04-11 08:27:08,http://ohara.co/rose_huels,3.0000000000,101.19.25.199,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n2040,3,191,2016-12-23 16:35:17,http://tremblay.net/jayden,3.0000000000,217.80.168.68,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n2041,3,191,2017-03-07 02:00:04,http://schroeder.com/emely,2.0000000000,158.109.166.17,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n2042,3,191,2017-04-26 01:23:39,http://monahan.co/wilburn,2.0000000000,144.107.12.137,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n2043,3,191,2017-04-06 20:37:40,http://auerrippin.name/destin,0.0000000000,115.152.118.138,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n2044,3,191,2017-05-22 04:50:54,http://king.com/kayley_hahn,1.0000000000,160.226.102.96,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n2045,3,192,2017-03-29 13:43:10,http://gutmanntrantow.co/brock,0.0000000000,96.77.89.166,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n2046,3,192,2017-02-23 11:19:35,http://gerlach.com/lauriane,0.0000000000,246.231.204.127,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n2047,3,192,2017-06-09 20:34:37,http://koch.co/valentine,2.0000000000,173.193.194.188,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n2048,3,192,2017-03-18 19:16:52,http://kunze.org/nicola,0.0000000000,237.204.99.2,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n2049,3,192,2017-04-19 09:59:56,http://cummings.com/henderson,1.0000000000,252.13.175.209,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n2050,3,192,2017-02-12 04:45:09,http://beattykeebler.org/leonie_purdy,1.0000000000,77.157.104.92,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n2051,3,192,2017-06-08 08:37:40,http://prohaska.net/brayan_kemmer,0.0000000000,95.10.107.54,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n2052,3,192,2017-02-26 13:01:08,http://ebert.org/mathilde.huel,1.0000000000,96.77.89.166,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n2053,3,192,2017-02-03 21:31:52,http://vandervortcasper.info/murray,2.0000000000,252.13.175.209,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n2054,3,192,2017-02-05 22:39:46,http://roobdietrich.io/leland,0.0000000000,251.85.5.178,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n2055,3,193,2017-01-10 14:50:16,http://ferryerdman.net/alison.hoppe,0.0000000000,172.105.228.61,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n2056,3,193,2017-01-24 05:26:32,http://altenwerth.com/jorge,1.0000000000,18.175.244.198,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n2057,3,193,2017-03-02 01:25:44,http://stracke.name/elmira_murazik,0.0000000000,38.129.235.92,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n2058,3,193,2016-12-22 14:15:38,http://reynoldshuels.name/brando.lockman,1.0000000000,44.166.240.204,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n2059,3,193,2017-04-12 21:14:58,http://miller.com/reinhold,1.0000000000,58.111.129.120,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n2060,3,193,2017-04-10 07:23:46,http://townekling.biz/hellen,2.0000000000,105.106.132.40,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n2061,3,193,2017-02-19 20:52:14,http://wunschfranecki.info/zelda,3.0000000000,189.244.196.14,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n2062,3,193,2016-12-22 01:31:19,http://pricecasper.name/emilio.terry,2.0000000000,189.244.196.14,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n2063,3,193,2017-04-23 10:33:37,http://berge.net/daphnee_bosco,2.0000000000,113.30.199.122,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n2064,3,193,2017-02-07 02:34:08,http://schulist.co/jannie,2.0000000000,238.79.70.28,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n2065,3,193,2017-04-26 12:32:56,http://altenwerthgreenfelder.co/michele,3.0000000000,165.176.73.62,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n2066,3,193,2017-03-13 17:57:46,http://hauckrath.name/barrett.jacobs,3.0000000000,189.244.196.14,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n2067,3,193,2017-01-07 18:11:28,http://hills.com/stefan_pagac,0.0000000000,189.244.196.14,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n2068,3,193,2016-12-26 20:32:29,http://macejkovic.io/reuben,3.0000000000,58.111.129.120,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n2069,3,193,2017-05-29 16:33:15,http://bernhard.org/arnulfo.steuber,3.0000000000,35.170.85.206,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n2070,3,193,2016-12-21 09:33:49,http://hermann.io/roderick_ruel,2.0000000000,119.119.219.176,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n2071,3,194,2017-02-24 12:55:31,http://pagac.net/mortimer,0.0000000000,49.107.73.14,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n2072,3,194,2017-03-28 18:51:21,http://heel.biz/samanta,0.0000000000,203.38.71.77,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n2073,3,194,2017-05-28 23:54:56,http://waelchiquitzon.org/jamil,0.0000000000,156.26.214.55,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n2074,3,194,2017-03-29 01:53:41,http://beer.net/dane,0.0000000000,124.108.77.57,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n2075,3,194,2017-02-14 14:21:11,http://kulas.net/kristopher,0.0000000000,44.178.162.197,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n2076,3,194,2017-01-08 00:36:57,http://kuhn.biz/joel,0.0000000000,88.26.98.128,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n2077,3,194,2017-02-04 15:21:01,http://schuster.biz/jonathon,0.0000000000,173.219.136.211,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n2078,3,194,2016-12-26 05:32:36,http://blandaherman.io/helen,0.0000000000,4.51.96.198,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n2079,3,194,2017-04-30 23:49:59,http://klocko.co/kameron,0.0000000000,117.253.95.202,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n4745,7,448,2017-01-11 19:54:58,http://dubuque.com/jaylan.feest,,44.223.226.149,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n4746,7,448,2016-12-19 01:56:06,http://labadie.name/ramiro_stanton,,43.202.192.104,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n4747,7,448,2017-03-14 07:58:54,http://hyatt.org/jordyn,,130.231.197.171,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n4748,7,448,2017-04-20 16:38:25,http://pfeffer.info/adelbert.jacobson,,140.29.249.132,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n4749,7,448,2017-06-03 00:38:40,http://hahn.co/bertha,,196.213.66.158,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n4750,7,448,2017-02-25 17:04:44,http://towne.info/daisha,,17.32.209.216,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n4751,7,448,2017-05-08 18:33:31,http://gibson.net/quinn,,10.200.193.16,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n4752,7,448,2017-03-20 03:03:02,http://jakubowskiwilliamson.name/zelda.mayert,,4.71.68.76,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n4753,7,449,2017-02-11 16:04:15,http://ernser.info/orlo_boyle,,20.238.97.96,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n4754,7,449,2017-03-28 06:06:45,http://bauch.org/guadalupe,,27.14.235.66,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n4755,7,449,2017-02-14 20:04:07,http://kozey.info/tyler_borer,,247.14.78.42,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n4756,7,450,2017-02-21 09:40:07,http://hoeger.co/harley,,206.193.41.44,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n4757,7,450,2017-01-08 17:30:31,http://doyleokeefe.co/burdette_funk,,238.110.174.75,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n4758,7,451,2017-06-07 05:19:05,http://johnson.name/keaton,,251.200.138.3,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n4759,7,451,2016-12-28 19:57:27,http://bruen.org/adrienne,,177.169.138.211,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n4760,7,451,2017-03-16 19:33:43,http://runolfsdottir.io/nella.batz,,110.40.212.149,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n4761,7,452,2017-01-09 04:52:47,http://kling.org/ardella,,242.38.184.102,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n4762,7,452,2017-06-10 12:03:28,http://kihnbruen.info/cleora.lockman,,17.107.66.137,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4763,7,452,2017-06-06 20:13:21,http://mayer.io/deborah,,246.130.252.188,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n4764,7,452,2017-04-27 11:00:20,http://prohaska.com/sophia,,228.241.195.148,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n4765,7,452,2017-02-01 10:56:09,http://armstrongterry.net/louisa,,212.155.250.138,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n4766,7,452,2017-04-06 03:13:51,http://dibbertdavis.net/horace_hodkiewicz,,251.144.218.67,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n4767,7,452,2017-02-06 10:02:33,http://bergstrom.net/gunner.schumm,,25.70.8.162,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n4768,7,452,2017-02-11 05:11:32,http://hansenorn.name/je.stracke,,228.241.195.148,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n4769,7,452,2017-04-26 22:38:30,http://bahringer.io/duncan_greenfelder,,242.38.184.102,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n4770,7,452,2017-04-09 18:31:44,http://christiansenmonahan.io/newell,,209.84.251.163,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n4771,7,452,2017-02-03 14:09:03,http://abshire.net/norwood.altenwerth,,63.215.204.217,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n4772,7,452,2017-03-08 20:37:48,http://kub.info/abbey.thompson,,52.136.243.56,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n4773,7,452,2017-04-26 22:29:24,http://kshlerin.org/jadon.homenick,,17.107.66.137,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n4774,7,452,2017-01-04 12:11:53,http://reichertgleason.io/madelyn.welch,,231.134.58.105,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n4775,7,452,2017-06-10 01:19:26,http://sporer.co/tyreek.graham,,144.8.152.117,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n4776,7,453,2017-03-18 02:24:27,http://quitzonsteuber.com/mckenzie.cruickshank,,87.60.182.218,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n4777,7,453,2017-02-08 11:56:41,http://abernathy.co/roman,,124.212.45.22,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n4778,7,453,2017-02-15 10:05:27,http://skiles.net/keon,,222.245.175.181,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n4779,7,453,2016-12-31 22:25:48,http://gleichner.com/johnathan,,37.16.219.77,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n4780,7,453,2017-03-28 17:01:42,http://okeefe.info/nico,,87.60.182.218,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n4781,7,453,2017-01-19 05:03:30,http://ledner.net/carole,,161.113.123.39,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n4782,7,453,2017-05-20 15:02:09,http://daniel.co/valentin,,175.172.25.172,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n4783,7,453,2017-03-13 13:16:46,http://runolfsdottir.co/roy,,103.237.246.95,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n4784,7,453,2017-05-28 07:59:07,http://stiedemann.io/cletus,,175.172.25.172,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n4785,7,453,2017-02-04 00:45:02,http://treutel.org/elenora,,161.113.123.39,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n4786,7,454,2017-03-06 23:19:12,http://tromp.com/eve,,62.192.181.112,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n4787,7,454,2017-03-04 01:39:32,http://kuhicgoyette.name/jerrold,,154.222.232.66,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n4788,7,454,2017-05-15 04:22:04,http://stracke.org/teagan,,87.161.181.70,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n4789,7,454,2017-04-01 05:47:09,http://millerstiedemann.org/wellington,,175.23.149.200,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n4790,7,455,2016-12-20 02:39:51,http://williamson.info/octavia.sanford,,121.6.191.84,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n4791,7,455,2016-12-15 03:59:36,http://williamson.co/selena,,71.153.117.200,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n4792,7,455,2017-02-13 10:43:54,http://mclaughlin.info/bridget_oreilly,,29.252.55.236,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n4793,7,455,2016-12-17 06:49:33,http://crona.name/hildegard,,189.198.97.19,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n4794,7,455,2017-02-16 03:05:46,http://block.biz/jany.runolfon,,92.37.137.230,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n4795,7,455,2017-01-28 19:21:46,http://fahey.org/brandy,,216.236.51.133,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n4796,7,455,2017-01-19 19:22:01,http://abernathy.co/glennie,,126.141.127.72,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n4797,7,455,2017-05-16 11:14:35,http://predoviccollier.name/noelia_mcclure,,221.75.173.160,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n4798,7,455,2017-02-04 16:11:24,http://feest.com/alicia_bashirian,,222.76.46.163,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n4799,7,455,2017-01-22 02:19:49,http://johnsongislason.net/geovany.beier,,71.153.117.200,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n4800,7,455,2017-01-25 21:50:42,http://fadel.org/maverick.schmidt,,70.163.75.129,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n2080,3,194,2017-05-22 18:09:27,http://gutkowskitremblay.com/filomena,0.0000000000,41.13.228.96,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n2081,3,194,2017-01-26 05:00:48,http://spencer.co/paige.kirlin,0.0000000000,88.26.98.128,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n2082,3,194,2017-03-17 18:06:59,http://sauer.name/richie.kemmer,0.0000000000,172.250.191.101,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n2083,3,194,2017-01-26 21:25:08,http://kemmer.info/eloisa_feest,0.0000000000,4.51.96.198,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n4801,7,455,2017-02-12 20:32:43,http://tremblay.biz/beth.yundt,,92.37.137.230,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n4802,7,455,2017-04-07 05:14:14,http://rolfson.co/devonte,,17.104.175.12,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n4803,7,455,2017-06-11 19:00:45,http://williamson.biz/ransom_fritsch,,228.14.192.215,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n4804,7,455,2017-03-23 15:04:26,http://ratke.org/dewayne,,186.117.211.248,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n4805,7,455,2017-05-01 16:39:24,http://luettgen.co/janice_friesen,,178.161.41.37,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n4806,7,455,2017-02-23 21:16:32,http://zboncakkoch.com/deja,,221.75.173.160,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n4807,7,456,2017-04-18 16:57:57,http://cremin.net/hallie.marvin,,81.170.227.31,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n4808,7,456,2017-03-02 08:50:07,http://bradtke.com/hope.homenick,,12.34.42.229,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n4809,7,456,2017-03-28 01:09:46,http://rodriguezemard.net/emmy,,86.21.175.7,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n4810,7,456,2016-12-24 10:25:36,http://gaylordroberts.info/tristian,,141.156.97.23,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n4811,7,456,2017-02-01 02:15:13,http://conroy.net/heloise,,66.59.146.239,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n4812,7,456,2017-03-10 21:17:22,http://trantow.net/selina_bernier,,11.27.240.148,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n4813,7,456,2016-12-30 19:32:40,http://halvorson.net/chaya_armstrong,,141.186.254.129,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n4814,7,457,2016-12-27 15:03:29,http://labadie.info/camille,,42.2.222.69,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n4815,7,457,2017-03-27 03:15:02,http://pagac.net/dane,,162.65.240.229,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n4816,7,457,2017-03-07 08:50:51,http://gorczanyrolfson.net/berniece_casper,,224.7.103.189,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n4817,7,457,2017-01-17 11:18:00,http://waters.info/jarret.dicki,,229.54.234.30,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n4818,7,457,2017-03-02 18:27:12,http://schusterdach.io/abbie.pollich,,106.152.135.147,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n4819,7,457,2017-05-30 20:07:52,http://gusikowski.biz/shayne.treutel,,119.75.219.19,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n4820,7,457,2017-04-18 19:39:35,http://herzog.io/isidro,,208.82.75.160,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n4821,7,457,2017-01-06 13:06:41,http://emard.name/bailee.bernier,,57.82.127.253,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n4822,7,457,2017-03-30 01:03:55,http://ebertfriesen.info/maybell,,57.82.127.253,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n4823,7,458,2017-02-05 09:19:33,http://zemlak.io/mallory,,242.185.33.67,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n4824,7,458,2017-04-08 11:23:18,http://stehr.com/etha,,16.250.65.85,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n4825,7,458,2017-03-01 20:05:19,http://weimann.org/katelynn_balistreri,,179.156.11.32,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n4826,7,458,2017-01-17 20:40:53,http://sengerboyer.net/jan,,185.103.248.77,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n4827,7,458,2017-04-02 16:41:24,http://bruen.org/jeramie,,12.26.105.154,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n4828,7,458,2017-04-28 13:13:04,http://croninraynor.com/quinton_stiedemann,,64.57.56.21,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n4829,7,458,2017-03-23 21:15:16,http://gerlach.name/jermain.fisher,,64.57.56.21,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n4830,7,459,2017-03-29 08:05:36,http://predovic.org/jee_daniel,,65.209.63.246,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n4831,7,459,2017-04-29 07:42:17,http://langoshkoelpin.org/aron,,107.125.48.86,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n4832,7,459,2017-01-08 11:31:23,http://lubowitzbeier.name/emelia,,224.39.114.31,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n4833,7,459,2017-03-11 05:55:53,http://kirlin.io/delta,,69.20.76.124,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n4834,7,459,2017-06-07 04:15:50,http://ondrickaharris.com/everette,,160.154.13.144,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n4835,7,459,2017-04-06 21:28:54,http://feiladams.biz/lila,,6.104.73.53,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n4836,7,459,2017-01-13 16:58:31,http://marvin.biz/alf_spencer,,212.226.166.175,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n4837,7,459,2017-05-02 13:05:36,http://gislasonleuschke.io/alek.bernhard,,32.3.190.19,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n4838,7,459,2017-03-05 13:38:39,http://reichert.info/orland,,78.212.245.56,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n4839,7,460,2017-05-24 22:47:31,http://quigley.io/myron,,236.52.166.112,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n4840,7,460,2017-01-13 02:29:23,http://hilll.net/xander_wiza,,114.18.166.142,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n4841,7,460,2017-05-01 18:14:52,http://bartellpagac.org/jed,,170.22.199.211,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n4842,7,461,2017-02-20 17:05:31,http://pacocha.info/chelsea.fritsch,,153.126.193.51,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n4843,7,461,2017-04-17 12:22:53,http://koelpin.name/tre_jacobson,,214.143.167.215,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n4844,7,461,2017-05-21 21:55:53,http://blandarice.org/elbert,,148.191.58.228,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n4845,7,461,2017-03-19 05:29:08,http://daugherty.io/lexie,,136.220.178.41,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n4846,7,461,2017-02-10 08:05:28,http://kunze.io/brannon.daniel,,5.98.143.76,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n4847,7,461,2017-06-08 00:28:00,http://gleichner.name/brionna.smith,,244.219.231.195,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n4848,7,461,2017-01-29 14:55:54,http://powlowskileffler.co/gerda,,205.73.69.146,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n4849,7,461,2017-01-09 09:01:31,http://ebert.net/ed_shields,,206.57.162.9,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n4850,7,461,2017-02-25 09:27:48,http://quigley.net/ozzie.dickinson,,214.196.181.238,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n4851,7,461,2017-04-27 15:37:59,http://ruel.co/valentin,,131.155.122.121,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n4852,7,461,2016-12-19 12:56:30,http://tillman.io/leie.bergstrom,,113.231.99.143,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n4853,7,461,2017-05-20 05:52:04,http://kelercormier.biz/marguerite_schowalter,,148.191.58.228,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n4854,7,461,2017-03-22 04:37:44,http://heaney.net/otto,,219.117.166.241,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n4855,7,461,2017-01-03 11:02:43,http://romaguera.io/vida_green,,231.204.2.21,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n4856,7,461,2017-02-16 13:33:01,http://purdy.info/alverta.schaefer,,223.66.84.155,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n4857,7,461,2017-01-15 17:47:53,http://mcdermott.io/margarete,,105.210.148.25,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n4858,7,461,2017-01-27 04:10:46,http://rice.io/jameson,,247.157.7.247,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n4859,7,461,2017-06-04 08:58:51,http://steuber.io/paul.bode,,78.77.13.13,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n4860,7,461,2017-01-25 02:04:56,http://lesch.com/andreanne,,246.228.50.2,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n4861,7,462,2017-04-11 10:15:13,http://schimmelbuckridge.org/cecil.hartmann,,123.63.148.123,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n4862,7,462,2017-01-09 12:22:36,http://abernathyerdman.name/lennie,,33.154.234.164,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4863,7,462,2017-04-05 12:35:13,http://feest.com/lennie_gerlach,,240.20.123.253,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n4864,7,462,2017-05-31 21:27:38,http://keeling.name/emie,,132.219.58.211,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n4865,7,463,2017-04-23 17:49:50,http://weimannvon.info/will,,199.71.170.182,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n4866,7,463,2017-01-05 23:17:01,http://medhurst.biz/lloyd,,75.185.73.111,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n4867,7,463,2017-05-14 11:42:47,http://rippintreutel.info/lenny,,104.8.119.230,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n4868,7,463,2017-01-09 00:43:07,http://marvin.biz/iliana.blanda,,126.188.215.106,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n4869,7,463,2017-02-05 15:41:34,http://dicki.net/lucile_reichel,,86.142.225.39,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n4870,7,463,2017-01-07 12:21:41,http://carrolldooley.org/elnora_jenkins,,140.163.173.211,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n4871,7,463,2017-01-23 04:48:08,http://farrellmcdermott.com/pauline,,41.132.15.179,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n4872,7,463,2017-06-13 16:23:19,http://durgan.org/susanna,,188.46.162.14,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n4873,7,463,2017-05-16 03:10:08,http://bahringer.co/brittany,,135.164.191.148,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n4874,7,463,2017-05-11 05:24:26,http://oberbrunner.co/clovis,,98.189.175.63,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n4875,7,463,2017-05-10 18:39:30,http://white.net/nayeli,,199.71.170.182,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n4876,7,463,2016-12-25 17:54:45,http://sporer.co/triston_mohr,,4.87.27.183,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n4877,7,463,2017-01-17 17:14:50,http://reichert.co/reba.white,,245.54.159.66,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n4878,7,463,2017-04-20 21:04:32,http://nicolas.name/karli,,208.66.83.212,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n4879,7,463,2017-05-28 10:38:16,http://mitchell.net/pete,,179.19.116.47,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n4880,7,463,2017-02-09 12:27:28,http://ebertrice.name/armando.little,,164.185.161.38,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n4881,7,463,2017-04-28 09:12:55,http://toykerluke.org/lisa,,104.8.119.230,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n4882,7,463,2017-04-27 07:50:03,http://sporer.io/jennyfer,,55.46.46.200,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n4883,7,463,2017-01-20 23:46:59,http://bruen.name/ena,,190.111.112.148,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n4884,7,463,2017-06-12 22:23:54,http://jonesblanda.org/georgette,,228.194.69.150,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n4885,7,464,2017-01-31 22:26:08,http://harvey.co/opal,,99.14.172.56,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n4886,7,464,2017-04-21 12:23:45,http://ratkekling.biz/judge,,180.189.118.154,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n4887,7,464,2017-05-30 18:51:40,http://becker.org/dora,,182.141.153.61,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n4888,7,464,2017-01-19 21:21:57,http://waelchi.info/theresia_kerluke,,197.180.183.41,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n4889,7,464,2017-02-14 18:27:32,http://kuhic.co/ima_haley,,177.38.12.91,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n4890,7,464,2017-03-10 19:37:43,http://hermanluettgen.io/sammy.gibson,,180.189.118.154,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n4891,7,464,2017-05-27 17:07:52,http://collins.org/clifford.tremblay,,112.163.158.236,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n4892,7,464,2017-01-20 18:34:21,http://strosincollier.name/lucio.pacocha,,133.193.173.210,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n4893,7,465,2017-03-26 19:38:41,http://hahnpagac.org/destiney.greenholt,,117.251.108.222,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n4894,7,465,2017-04-18 22:36:26,http://leannon.name/fae.hirthe,,49.50.184.57,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n4895,7,465,2017-02-07 18:22:41,http://gutmannosinski.name/finn,,218.55.94.78,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n4896,7,465,2017-06-04 23:04:12,http://lednerchamplin.co/marguerite,,194.51.59.247,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n4897,7,465,2017-06-03 22:46:23,http://bartoletti.io/leopoldo_durgan,,49.50.184.57,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n4898,7,465,2017-04-13 06:46:38,http://emmerich.org/alize,,208.173.21.118,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n4899,7,465,2017-04-14 11:43:16,http://runolfon.biz/andrew.fritsch,,157.89.200.132,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n4900,7,465,2017-04-03 00:08:36,http://schimmel.io/oma,,157.37.46.11,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n4901,7,465,2017-02-04 20:41:22,http://ruelosinski.net/ona,,157.37.46.11,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n4902,7,465,2017-01-22 18:03:14,http://schneidermarvin.io/quentin.kulas,,55.117.225.44,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n4903,7,466,2017-01-01 11:50:51,http://ankundinglabadie.info/domenic.jones,1.0000000000,3.201.213.83,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n4904,7,466,2017-03-11 12:53:46,http://goyette.com/antonetta,3.0000000000,211.39.235.248,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n4905,7,466,2017-01-15 21:33:14,http://connelly.com/ashton,1.0000000000,182.163.33.68,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n4906,7,466,2017-05-17 07:49:39,http://keler.net/savanah.kohler,0.0000000000,166.190.83.80,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n4907,7,467,2017-02-25 12:18:07,http://goldner.com/oma,1.0000000000,26.165.44.241,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n4908,7,467,2017-02-26 08:07:41,http://stracke.com/jaydon.thiel,1.0000000000,105.225.97.91,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n4909,7,467,2017-01-05 21:22:36,http://corwinberge.com/jalyn,1.0000000000,117.61.134.112,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n4910,7,467,2017-02-28 11:40:18,http://schiller.net/emely_schneider,0.0000000000,124.148.69.25,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n4911,7,467,2017-01-07 07:32:57,http://turcotte.name/dylan_glover,1.0000000000,117.253.51.74,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4912,7,468,2017-01-19 00:47:30,http://bauch.net/tanner,0.0000000000,210.55.219.187,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n4913,7,468,2017-03-26 07:15:35,http://ebert.co/teagan,0.0000000000,113.191.15.208,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n4914,7,468,2017-05-30 00:03:55,http://wehner.name/helga_cole,1.0000000000,26.218.115.48,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n4915,7,468,2017-04-28 02:40:28,http://hilll.info/ciara.glover,1.0000000000,37.207.254.43,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n4916,7,468,2017-02-28 09:55:09,http://yostraynor.biz/harley.ryan,2.0000000000,37.207.254.43,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n4917,7,468,2017-02-18 13:04:54,http://haag.net/julianne.harvey,0.0000000000,222.152.6.148,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n4918,7,468,2017-02-23 06:20:26,http://lang.co/emie.doyle,0.0000000000,175.55.148.101,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n4919,7,469,2017-01-27 16:47:57,http://anderson.name/bennett_koch,0.0000000000,39.135.41.76,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n4920,7,469,2017-06-12 13:49:30,http://satterfield.com/samanta.stokes,0.0000000000,208.183.68.105,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n4921,7,469,2017-03-13 19:13:22,http://mcglynn.io/tremayne,0.0000000000,193.143.183.124,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n4922,7,469,2017-03-19 18:30:04,http://hyatt.biz/annamarie_okeefe,0.0000000000,193.143.183.124,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n4923,7,469,2017-05-13 23:24:31,http://lind.net/shakira,0.0000000000,161.67.78.175,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n4924,7,470,2017-02-13 12:46:25,http://toy.co/sydni_emard,2.0000000000,203.9.207.79,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n4925,7,470,2017-01-27 09:56:15,http://boylelang.name/steve.gusikowski,2.0000000000,203.9.207.79,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n4926,7,470,2017-01-27 18:40:04,http://powlowski.co/shany,1.0000000000,230.81.165.195,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n4927,7,470,2017-03-04 06:45:13,http://lockman.org/hazle,3.0000000000,211.17.130.70,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n4928,7,470,2017-05-29 03:25:17,http://croninkoch.io/reymundo_fritsch,1.0000000000,225.130.193.102,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n4929,7,470,2017-05-11 14:57:01,http://smithamrempel.co/tobin.witting,3.0000000000,211.17.130.70,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n4930,7,470,2017-04-25 18:22:17,http://aufderhar.info/aileen,2.0000000000,190.109.89.48,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n4931,7,470,2017-05-01 22:27:16,http://wilkinson.com/chanel_sawayn,0.0000000000,106.32.236.177,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n4932,7,470,2017-04-14 05:06:45,http://champlin.net/lea,1.0000000000,241.161.167.245,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n4933,7,470,2017-04-19 09:09:03,http://rogahnhickle.info/garnett_walter,2.0000000000,150.56.155.125,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n4934,7,470,2017-03-19 05:51:01,http://konopelskiullrich.co/gerry_braun,2.0000000000,150.56.155.125,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n4935,7,470,2017-04-29 16:37:38,http://parker.org/jamie,3.0000000000,86.74.112.248,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n4936,7,470,2017-02-25 09:25:15,http://hahnwindler.org/zachary_skiles,0.0000000000,199.106.89.147,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n4937,7,470,2017-05-24 13:33:59,http://grantbosco.org/maryse,1.0000000000,94.115.223.78,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n4938,7,470,2017-05-28 05:03:34,http://stanton.co/frederic.krajcik,3.0000000000,225.141.8.61,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n4939,7,470,2017-03-31 02:04:53,http://blick.biz/river,3.0000000000,3.78.72.78,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n4940,7,470,2017-02-15 11:32:27,http://bartell.info/eudora.jacobi,0.0000000000,203.9.207.79,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n4941,7,470,2017-06-05 09:40:08,http://gusikowski.com/cali,3.0000000000,63.123.174.144,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n4942,7,471,2017-04-29 23:56:33,http://douglasullrich.net/lila_schowalter,0.0000000000,24.223.19.152,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n4943,7,471,2017-03-09 01:14:43,http://shieldsyundt.biz/georgiana,0.0000000000,224.24.234.227,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n4944,7,471,2017-03-07 03:34:34,http://schneider.org/eduardo.lehner,0.0000000000,53.49.185.125,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n4945,7,471,2017-05-30 12:41:52,http://treutelanderson.io/royce,2.0000000000,158.11.249.172,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n4946,7,471,2017-01-12 01:21:11,http://dickinson.net/christine.ruel,2.0000000000,205.197.206.145,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n4947,7,471,2017-02-12 09:41:52,http://gaylordleannon.biz/alanna.koepp,2.0000000000,135.176.50.96,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n4948,7,471,2017-05-10 00:17:14,http://schillerquigley.co/hallie.walker,2.0000000000,158.11.249.172,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n4949,7,471,2017-02-03 16:29:13,http://hageneskuhic.net/rosina,0.0000000000,236.116.160.221,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n4950,7,471,2016-12-29 02:28:42,http://mcdermott.com/helen,0.0000000000,135.176.50.96,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n4951,7,471,2017-02-13 12:09:43,http://altenwerth.com/marlee,2.0000000000,162.213.235.212,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n4952,7,471,2017-02-04 04:39:53,http://daniel.com/quincy,2.0000000000,94.221.83.143,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n4953,7,471,2017-02-17 18:05:21,http://smith.info/krystal_jakubowski,1.0000000000,150.181.135.232,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n4954,7,471,2017-03-09 18:08:50,http://denesikcartwright.info/frederik_blick,0.0000000000,162.213.235.212,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n4955,7,471,2017-01-21 23:35:47,http://parker.biz/aleandro,0.0000000000,4.34.144.221,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n4956,7,471,2017-01-14 00:13:58,http://block.info/mohammed.greenfelder,2.0000000000,71.69.169.54,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n4957,7,471,2017-01-09 12:49:08,http://torp.net/skyla_padberg,2.0000000000,170.49.130.156,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4958,7,471,2017-01-07 17:08:15,http://ziemelubowitz.net/chase.farrell,0.0000000000,162.213.235.212,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n4959,7,471,2016-12-29 23:20:49,http://boehm.info/patrick,2.0000000000,24.223.19.152,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n4960,7,471,2017-06-11 12:21:40,http://satterfield.name/caleigh.schinner,2.0000000000,128.14.20.206,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n4961,7,471,2017-04-14 20:49:00,http://sporerkozey.info/jacquelyn.hammes,1.0000000000,147.119.70.50,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n4962,7,472,2017-02-26 11:16:41,http://okonherzog.org/arianna,1.0000000000,159.99.103.146,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n4963,7,472,2017-03-03 10:30:05,http://aufderhar.info/adonis_okuneva,0.0000000000,110.213.146.44,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n4964,7,472,2017-01-21 18:19:49,http://smitham.info/jaydon,1.0000000000,181.59.190.173,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n4965,7,472,2017-03-07 19:04:42,http://beier.org/angelo,0.0000000000,207.83.215.249,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n4966,7,472,2017-02-25 17:19:17,http://heathcote.org/rigoberto_bauch,1.0000000000,181.59.190.173,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n4967,7,472,2017-04-20 16:20:42,http://williamson.name/stanley_bode,1.0000000000,235.196.94.164,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n4968,7,472,2017-05-22 03:52:59,http://sawayn.com/cyril_glover,1.0000000000,46.186.187.169,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n4969,7,472,2017-06-03 21:15:36,http://walkerjaskolski.name/vella,1.0000000000,28.118.221.96,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n4970,7,472,2017-04-20 06:41:14,http://koelpin.info/mara,0.0000000000,149.55.233.193,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n4971,7,472,2017-04-29 20:45:36,http://wymanfritsch.name/victor.baumbach,0.0000000000,46.186.187.169,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n4972,7,472,2017-02-28 19:07:14,http://harvey.net/andreanne_macgyver,0.0000000000,152.60.42.238,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n4973,7,472,2017-05-09 08:36:58,http://nicolas.name/delbert.bogisich,0.0000000000,81.37.251.142,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n4974,7,472,2017-05-06 12:52:25,http://gerhold.biz/meredith,1.0000000000,89.34.204.152,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n4975,7,473,2016-12-27 20:49:39,http://hettinger.io/graham.jacobi,0.0000000000,148.164.200.48,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n4976,7,473,2017-03-05 03:54:30,http://sanford.org/casimer.robel,1.0000000000,29.126.254.21,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n4977,7,473,2017-01-31 22:13:45,http://denesik.biz/mary_casper,1.0000000000,154.153.219.22,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n4978,7,473,2017-05-25 18:19:24,http://ledner.name/chaz_zulauf,1.0000000000,173.46.214.118,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n4979,7,473,2016-12-31 04:27:51,http://oconnellblanda.name/jeie_connelly,0.0000000000,137.151.68.82,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n4980,7,473,2017-04-20 05:22:09,http://thompsonbahringer.info/reva,0.0000000000,55.34.238.71,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n4981,7,473,2017-06-09 08:05:07,http://brown.org/porter_schmitt,0.0000000000,164.22.252.99,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n4982,7,473,2017-01-16 22:02:15,http://leffler.biz/gardner_hickle,0.0000000000,46.164.229.23,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n4983,7,473,2017-01-28 05:24:31,http://veum.org/wanda,1.0000000000,55.34.238.71,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n4984,7,473,2017-01-25 05:13:45,http://heathcote.net/colin_hayes,0.0000000000,29.126.254.21,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n4985,7,473,2017-05-21 11:03:09,http://jerde.co/amara,0.0000000000,43.40.201.248,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n4986,7,473,2017-02-07 04:42:40,http://feeney.name/christ_miller,0.0000000000,154.153.219.22,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n4987,7,473,2017-03-17 15:59:37,http://turner.info/gunner,1.0000000000,38.136.185.67,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n4988,7,473,2017-04-30 19:35:28,http://gislason.biz/earlene_murray,1.0000000000,129.252.140.176,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n4989,7,473,2017-01-29 10:59:40,http://volkmandaugherty.org/anika_leuschke,1.0000000000,24.134.234.121,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n4990,7,473,2017-06-13 17:57:37,http://powlowskihoeger.com/darrel,1.0000000000,173.46.214.118,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n4991,7,473,2017-01-25 13:35:52,http://mclaughlinparisian.org/santina.monahan,1.0000000000,99.131.147.82,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n4992,7,473,2017-04-15 21:18:00,http://thompson.net/maude_beahan,1.0000000000,34.92.172.92,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n4993,7,473,2017-01-13 19:45:20,http://dubuquewisoky.org/caleb,0.0000000000,38.136.185.67,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4994,7,473,2016-12-29 02:33:41,http://fisherjacobi.org/skyla,1.0000000000,88.240.104.13,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n509,2,52,2017-03-26 01:47:37,http://macejkovicbernier.info/elena.terry,,136.114.18.117,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n510,2,52,2016-12-30 06:10:02,http://gutmann.org/conor,,246.22.72.58,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n511,2,52,2017-03-26 15:26:00,http://jacobi.name/shaylee,,40.51.228.228,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n512,2,52,2017-01-01 07:06:21,http://funkcole.io/rahul_schiller,,243.24.5.206,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n513,2,52,2017-01-31 12:48:12,http://lowebartoletti.io/telly,,18.168.220.134,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n514,2,52,2017-05-05 19:47:25,http://gutkowski.biz/milo,,113.204.192.139,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n515,2,52,2017-04-20 23:25:49,http://kshlerin.com/carmel_kulas,,40.51.228.228,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n516,2,52,2017-02-23 21:47:18,http://keeling.io/lorenzo,,116.234.223.235,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n517,2,53,2017-02-03 10:07:47,http://jenkins.net/karen_bradtke,,8.130.223.123,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n518,2,53,2017-03-12 20:39:48,http://pfannerstill.net/bernhard.west,,128.163.186.212,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n519,2,53,2017-05-22 11:19:37,http://dietrich.co/loyce,,155.100.133.85,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n520,2,53,2017-05-02 22:54:30,http://langosh.name/zack,,154.155.250.88,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n521,2,53,2017-03-09 02:22:55,http://lehner.name/reid,,167.185.68.138,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n522,2,53,2017-04-09 23:36:15,http://cremin.io/elinore,,34.42.155.254,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n523,2,53,2017-02-07 12:56:24,http://lind.io/orrin.gleason,,108.243.121.244,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n524,2,53,2017-04-25 12:47:32,http://anderson.co/blaise,,124.95.214.23,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n525,2,53,2017-06-08 10:39:51,http://stantonmurray.biz/maxwell,,212.119.250.114,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n526,2,53,2017-04-14 01:04:21,http://jakubowski.info/sierra,,130.65.140.114,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n527,2,53,2017-02-21 19:59:22,http://wolff.org/elmo_carroll,,158.195.56.201,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n528,2,53,2016-12-19 04:06:34,http://stoltenbergshanahan.biz/tiffany_wiza,,158.195.56.201,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n529,2,53,2017-03-25 11:44:14,http://treutelaltenwerth.info/wade,,60.234.145.107,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n530,2,53,2016-12-29 09:30:56,http://kiehn.name/levi.braun,,141.27.31.37,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n531,2,53,2017-05-11 06:02:12,http://mcculloughschmeler.com/laisha,,3.220.131.177,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n532,2,53,2017-01-30 06:29:12,http://carroll.org/milford,,170.65.49.23,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n533,2,53,2017-03-20 01:51:00,http://mohr.name/melisa,,189.37.198.112,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n534,2,53,2017-04-03 10:40:31,http://strosinschamberger.io/maxine.zemlak,,51.128.118.193,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n535,2,53,2017-05-28 04:09:32,http://willmsweimann.info/laurence_fay,,102.185.160.154,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n536,2,54,2017-01-27 00:20:29,http://smitham.io/freeda_gottlieb,,112.40.248.83,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n537,2,54,2017-01-18 02:10:07,http://jakubowskihyatt.co/mitchell.grimes,,140.74.58.119,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n538,2,54,2016-12-19 08:23:22,http://nienow.name/broderick,,151.243.92.203,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n539,2,54,2017-03-06 22:19:29,http://cummeratakeebler.com/rubie.beer,,126.161.4.228,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n540,2,54,2016-12-22 18:47:20,http://mraz.name/kadin.weber,,245.194.234.143,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n541,2,54,2017-05-28 16:38:25,http://hilpert.info/marlen,,110.54.126.83,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n542,2,54,2017-04-15 05:46:15,http://parisiangrant.net/mariane,,176.165.162.38,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n543,2,54,2017-03-14 18:27:14,http://mayert.com/polly_heller,,9.35.68.134,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n544,2,54,2017-01-01 01:53:02,http://dare.com/theron_hirthe,,121.67.38.77,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n545,2,54,2017-05-23 11:16:36,http://muller.biz/amya,,149.34.122.94,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n546,2,54,2017-05-21 23:33:24,http://kuhlman.org/loyal_terry,,199.34.17.239,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n547,2,54,2017-03-20 12:49:48,http://stroman.name/trever,,110.54.126.83,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n548,2,54,2017-05-21 09:58:17,http://carroll.co/ed,,231.154.31.89,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n549,2,54,2017-06-09 07:15:53,http://spencer.name/verner_schmidt,,191.201.245.36,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n550,2,54,2017-02-01 10:39:53,http://greenholt.com/deon.reichert,,213.51.147.155,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n551,2,54,2017-04-10 23:38:30,http://stark.name/katarina_mertz,,14.179.65.191,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n552,2,55,2017-04-15 21:52:20,http://ratke.io/krista,,173.223.150.174,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n553,2,55,2017-01-18 10:05:26,http://yundt.name/eleanore,,216.9.4.237,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n554,2,55,2017-05-23 12:31:37,http://doylestroman.name/asha_kilback,,46.191.180.85,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n555,2,55,2017-04-16 07:28:56,http://toy.co/german,,179.192.21.80,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n556,2,55,2016-12-18 21:02:26,http://windler.net/carleton_mohr,,235.205.45.56,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n557,2,55,2017-03-06 04:40:09,http://mertz.name/gino_pagac,,79.83.80.125,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n558,2,55,2017-03-04 09:24:12,http://terry.org/nikolas.wintheiser,,21.203.253.252,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n559,2,55,2017-04-03 03:48:09,http://emmerichdubuque.io/cierra,,17.106.125.161,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n560,2,55,2016-12-19 13:32:10,http://schadensauer.info/hollie,,21.203.253.252,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n561,2,55,2017-03-16 15:43:25,http://denesik.com/lenna,,35.107.151.36,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n562,2,55,2017-01-15 06:04:31,http://gleichnerfritsch.net/myra,,71.81.245.244,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n3672,6,336,2017-02-15 03:54:54,http://goldnerbotsford.com/reilly,1.0000000000,134.119.158.66,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n3673,6,336,2017-05-11 01:33:23,http://jenkinscarroll.org/royce_schmitt,2.0000000000,29.56.188.198,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n3674,6,336,2017-01-14 13:12:53,http://lindgren.name/robbie.rogahn,1.0000000000,147.124.128.139,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n3675,6,336,2017-02-12 01:01:13,http://schmittborer.com/martina_balistreri,2.0000000000,14.129.6.193,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n3676,6,336,2017-01-09 23:38:58,http://torphy.io/dejon.bradtke,0.0000000000,105.242.129.53,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n3677,6,336,2017-01-28 22:33:12,http://brownreichert.name/jamir,0.0000000000,203.121.66.30,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n3678,6,336,2017-05-11 14:19:32,http://dickensullrich.net/jackeline.berge,2.0000000000,232.84.16.228,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n3679,6,336,2017-03-02 02:55:16,http://waelchi.name/logan.kovacek,0.0000000000,54.95.178.82,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n3680,6,336,2017-01-25 11:17:26,http://becker.biz/stefanie.bauch,0.0000000000,190.7.39.130,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n3681,6,336,2017-06-05 10:44:47,http://borer.net/hugh,1.0000000000,55.163.221.208,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n3682,6,336,2017-03-09 03:27:50,http://beckerlehner.co/brielle,0.0000000000,222.162.136.158,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n3683,6,336,2017-05-31 22:05:23,http://koeppsipes.net/gunner,1.0000000000,179.155.67.59,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n3684,6,336,2017-03-03 16:22:30,http://rippin.net/elfrieda_cummings,1.0000000000,14.129.6.193,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n3685,6,336,2017-04-25 09:43:17,http://medhurst.info/marcus_langworth,2.0000000000,73.215.40.65,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n3686,6,337,2017-02-12 20:33:06,http://willms.name/myrl,0.0000000000,3.177.96.178,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n3687,6,337,2017-05-04 10:38:05,http://marquardt.net/jamar.schmidt,0.0000000000,181.149.95.247,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n3688,6,337,2017-06-10 08:49:56,http://streich.biz/adrienne.grant,3.0000000000,220.111.167.51,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n3689,6,337,2017-05-31 07:20:46,http://hermann.info/susie_haley,2.0000000000,37.98.139.198,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n3690,6,337,2017-05-06 22:42:49,http://mayer.io/kyler,3.0000000000,31.130.7.87,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n3691,6,337,2017-05-27 22:09:20,http://jacobimurphy.name/reed_hauck,1.0000000000,60.142.121.14,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n3692,6,337,2017-02-20 15:34:01,http://toy.com/joannie,2.0000000000,183.173.230.194,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n3693,6,337,2017-05-07 11:28:39,http://mannreichert.io/kiel_runte,2.0000000000,48.251.51.225,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n3694,6,337,2017-01-07 19:56:00,http://stiedemannauer.info/lafayette_ryan,2.0000000000,54.128.236.96,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n3695,6,337,2016-12-27 20:36:47,http://kautzerpagac.org/mathilde,1.0000000000,58.75.96.120,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n3696,6,337,2017-02-13 11:06:42,http://rogahn.co/ervin_doyle,3.0000000000,130.218.3.247,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n3697,6,337,2017-01-20 05:18:11,http://towne.org/elody,2.0000000000,205.156.188.36,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n3698,6,337,2017-03-15 19:04:19,http://fritschsmith.net/amber.murphy,1.0000000000,60.142.121.14,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n3699,6,337,2017-01-29 23:07:30,http://cruickshankeffertz.io/mikel,1.0000000000,138.243.222.160,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n3700,6,337,2017-04-22 20:09:00,http://lockman.info/fern,1.0000000000,54.128.236.96,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n3701,6,337,2017-04-29 03:12:07,http://konopelski.info/landen,1.0000000000,124.71.99.130,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n3702,6,338,2017-02-01 19:43:09,http://koepp.io/omari,1.0000000000,76.51.193.17,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n3703,6,338,2016-12-22 11:45:35,http://jerdekunde.io/tristin,1.0000000000,239.114.192.4,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n3704,6,338,2017-01-21 06:05:20,http://farrellhackett.org/rosalyn_gerhold,1.0000000000,76.51.193.17,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n3705,6,338,2017-04-26 15:29:03,http://kulaswalsh.co/damaris,0.0000000000,238.78.253.135,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n3706,6,338,2017-03-27 15:29:25,http://vandervortpadberg.info/tatyana_gulgowski,0.0000000000,37.79.215.75,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n3707,6,338,2017-05-01 03:48:04,http://corwinschultz.biz/ryann,1.0000000000,196.106.178.189,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n3708,6,338,2017-02-02 01:33:21,http://nienowvolkman.name/alfonso,0.0000000000,29.55.175.207,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n3709,6,338,2017-04-27 07:32:35,http://leffler.org/chauncey,0.0000000000,92.151.149.17,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n3710,6,338,2017-01-13 21:29:14,http://yost.name/saul,0.0000000000,229.167.185.30,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n3711,6,338,2017-03-01 09:53:53,http://mcglynn.name/trystan,1.0000000000,84.90.175.173,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n3712,6,338,2017-03-10 20:31:23,http://cruickshank.info/nyah,0.0000000000,83.247.236.89,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n3713,6,338,2017-04-04 01:30:42,http://kundejohns.io/lauriane_pfannerstill,0.0000000000,84.90.175.173,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n3714,6,339,2016-12-26 12:05:43,http://shieldscarroll.name/bertha.welch,0.0000000000,120.98.214.109,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n3715,6,339,2017-02-22 01:46:49,http://herzog.io/susie,0.0000000000,91.134.108.172,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n3716,6,339,2017-02-22 15:01:04,http://boscoko.info/wilford_leuschke,0.0000000000,171.141.53.240,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n3717,6,339,2017-02-14 21:20:05,http://gibson.io/dorothy,0.0000000000,218.35.62.250,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n3718,6,339,2017-01-22 18:21:59,http://barrows.co/carolyn.klein,0.0000000000,240.143.14.156,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n3719,6,339,2017-05-27 16:33:55,http://runolfsdottir.net/zachariah.rolfson,0.0000000000,81.36.125.167,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n3720,6,339,2017-02-08 20:10:05,http://pollich.info/chauncey_feil,0.0000000000,113.74.91.192,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n563,2,55,2017-05-23 12:40:44,http://jaskolskireynolds.info/emerson,,99.229.130.181,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n564,2,56,2017-05-30 17:03:02,http://macejkovic.io/raleigh,,209.205.172.191,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n565,2,56,2017-05-23 12:17:47,http://ferrygoldner.com/eugenia,,236.33.10.218,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n566,2,56,2017-05-30 12:23:08,http://reinger.info/hilma,,212.224.19.102,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n567,2,56,2017-06-09 18:56:42,http://gutkowskicronin.io/baylee,,97.92.146.190,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n568,2,56,2017-05-12 20:19:15,http://reilly.org/alice_carroll,,15.110.67.28,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n569,2,56,2017-04-08 23:54:43,http://schmidtdicki.com/shannon,,27.151.60.110,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n570,2,56,2017-05-29 09:57:06,http://witting.biz/aunta,,145.216.246.154,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n571,2,56,2017-02-21 09:04:49,http://gislason.org/ivy_willms,,209.205.172.191,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n572,2,56,2017-02-01 13:16:10,http://bernhard.co/allison.macgyver,,145.216.246.154,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n573,2,56,2017-05-14 02:03:15,http://casper.name/enid.huel,,108.25.72.163,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n574,2,56,2017-05-03 07:59:27,http://goodwin.com/myrl,,204.81.240.251,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n575,2,56,2017-05-02 06:40:27,http://emmerich.name/rocio,,234.114.161.249,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n576,2,56,2017-05-24 19:09:30,http://murphy.io/arianna_jerde,,204.81.240.251,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n577,2,56,2017-03-16 20:35:16,http://mertz.net/genevieve,,145.216.246.154,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n578,2,56,2017-04-24 06:57:34,http://ornhintz.org/mollie.sawayn,,225.249.29.250,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n579,2,56,2017-03-16 08:35:56,http://turnerklein.info/jewel,,38.42.224.180,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n580,2,56,2017-03-14 22:23:30,http://ondricka.biz/cara.cruickshank,,107.236.142.203,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n581,2,56,2017-05-23 01:01:22,http://bartonferry.co/ozella.conroy,,169.163.4.89,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n582,2,56,2017-03-14 08:05:12,http://luettgen.com/desiree,,212.224.19.102,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n583,2,57,2017-04-26 09:51:08,http://gottliebbarton.biz/edmond_schiller,,12.6.165.74,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n584,2,57,2017-04-17 02:00:01,http://hahnauer.info/justyn.schmidt,,171.78.150.213,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n585,2,57,2016-12-18 18:54:35,http://goldner.biz/howard.fahey,,219.206.157.41,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n586,2,57,2017-01-10 03:08:52,http://berge.biz/haskell.lind,,251.149.181.216,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n587,2,57,2017-02-01 12:56:55,http://gerholdblock.info/esmeralda.moen,,251.149.181.216,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n588,2,57,2016-12-31 00:34:19,http://rutherford.co/buford,,206.68.55.89,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n589,2,57,2017-01-05 08:28:33,http://champlinmiller.com/rocio,,106.121.171.40,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n590,2,57,2017-04-02 19:56:54,http://armstrong.co/josiah,,113.197.235.122,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n591,2,57,2017-03-03 19:24:55,http://yundtpagac.biz/gino.jacobs,,190.233.15.235,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n592,2,57,2017-02-25 19:35:56,http://powlowski.net/wilton,,113.197.235.122,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n593,2,57,2017-06-12 21:09:33,http://kertzmannokuneva.name/savannah,,126.212.145.230,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n594,2,57,2017-04-02 15:51:12,http://will.name/anabelle,,117.128.207.122,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n595,2,57,2017-03-03 23:11:35,http://bergnaum.net/luciano,,187.85.2.166,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n596,2,57,2016-12-30 04:19:10,http://smith.co/annetta,,65.229.169.90,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n597,2,57,2017-01-21 01:29:17,http://ritchiehauck.com/loren.ziemann,,126.212.145.230,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n598,2,57,2017-05-03 19:54:58,http://reynolds.com/alfonzo_murray,,187.85.2.166,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n599,2,57,2017-05-10 06:03:29,http://breitenberghyatt.com/garrett,,12.167.241.67,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n600,2,57,2017-04-17 07:04:25,http://kuphal.io/adriel,,251.149.181.216,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n601,2,57,2017-04-13 17:39:56,http://schaefer.com/grace,,56.35.72.163,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n602,2,57,2017-06-13 18:54:23,http://wisozkcrooks.net/vicky_yost,,165.92.188.6,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n603,2,58,2017-01-15 10:06:50,http://farrell.name/martina,0.0000000000,242.207.134.237,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n604,2,58,2017-04-23 02:12:27,http://greenmacgyver.info/addison,0.0000000000,16.42.179.107,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n605,2,58,2017-04-14 02:42:49,http://hermannrath.io/claude_rutherford,1.0000000000,111.218.156.72,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n606,2,58,2016-12-31 04:34:11,http://jerde.name/effie.runte,0.0000000000,64.5.129.44,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n607,2,58,2016-12-19 22:42:12,http://reynoldscummings.name/tristian_tillman,0.0000000000,35.92.64.230,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n608,2,58,2017-04-20 07:00:19,http://kiehn.biz/justice_rice,1.0000000000,111.218.156.72,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n609,2,58,2017-01-07 01:19:30,http://rohan.biz/brandi,0.0000000000,245.101.143.202,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n610,2,58,2017-03-14 09:33:05,http://bauch.info/geoffrey,1.0000000000,147.153.234.70,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n611,2,58,2016-12-19 02:12:36,http://wisoky.io/kailey_ruecker,1.0000000000,144.144.178.215,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n612,2,58,2016-12-29 10:46:39,http://oberbrunner.net/katelin,1.0000000000,229.194.39.21,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n613,2,58,2017-03-05 03:15:54,http://goldner.com/vena,0.0000000000,118.86.227.27,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n614,2,58,2017-06-01 03:36:54,http://hackettdooley.io/layla_murazik,0.0000000000,83.90.119.171,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n615,2,58,2017-02-10 00:34:34,http://mertzgaylord.info/herta_simonis,0.0000000000,225.10.238.111,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n616,2,58,2017-05-16 22:08:10,http://dare.biz/olin_kuhn,1.0000000000,87.140.122.185,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n617,2,58,2017-06-02 21:28:19,http://mertz.com/jackeline,0.0000000000,196.204.157.4,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n3721,6,339,2017-01-09 10:32:12,http://howellziemann.net/jermaine,0.0000000000,202.69.45.238,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n3722,6,339,2016-12-19 17:44:41,http://lindgrady.org/cristal.hahn,0.0000000000,204.201.243.201,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n3723,6,339,2017-03-13 16:10:14,http://harbermurazik.com/granville,0.0000000000,39.136.149.120,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n3724,6,339,2017-02-03 12:00:05,http://cummerata.net/loma,0.0000000000,59.43.79.20,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n3725,6,339,2016-12-24 01:27:47,http://oberbrunner.info/levi,0.0000000000,171.141.53.240,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n3726,6,339,2017-01-19 05:21:07,http://price.net/gerardo_grady,0.0000000000,174.12.58.141,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n3727,6,339,2016-12-15 11:27:43,http://breitenberg.org/margot,0.0000000000,41.137.170.185,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n3728,6,339,2017-02-21 23:30:49,http://schimmel.co/odea.koepp,0.0000000000,40.174.201.133,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n3729,6,339,2016-12-29 16:20:45,http://ruecker.name/beie_lemke,0.0000000000,120.98.214.109,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n3730,6,340,2017-05-03 21:37:12,http://dare.name/cristal,0.0000000000,97.133.161.136,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n3731,6,340,2017-02-20 13:21:55,http://sawayn.org/wade.pfannerstill,0.0000000000,76.19.80.126,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n3732,6,341,2017-03-08 05:47:05,http://bogan.co/corene,0.0000000000,7.194.57.104,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n3733,6,341,2017-05-12 22:05:24,http://haley.biz/sydni,2.0000000000,120.185.159.218,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n3734,6,341,2017-04-21 06:38:33,http://schuppe.org/kristian_jacobi,0.0000000000,129.183.172.93,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n3735,6,341,2017-03-12 20:44:29,http://mitchell.com/alphonso.moore,1.0000000000,194.114.126.236,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n3736,6,341,2017-01-12 20:25:32,http://beier.net/dameon_ortiz,0.0000000000,51.90.230.89,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n3737,6,341,2017-03-19 10:25:57,http://pfannerstill.net/kiarra,2.0000000000,44.44.249.197,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n3738,6,341,2017-04-29 19:52:56,http://heidenreich.net/hilma.tromp,0.0000000000,230.224.152.216,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n3739,6,341,2016-12-31 20:21:59,http://connelly.com/ashlee,1.0000000000,93.18.205.221,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n3740,6,341,2017-01-18 10:51:23,http://mraz.biz/deshawn,2.0000000000,42.36.164.25,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n3741,6,341,2017-01-20 21:04:41,http://ebert.co/wendell,0.0000000000,169.118.182.220,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n3742,6,341,2017-02-23 21:40:13,http://sanford.net/dwight,1.0000000000,42.36.164.25,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n3743,6,341,2016-12-28 05:36:46,http://simonis.biz/ruthe,2.0000000000,166.99.243.200,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n3744,6,341,2016-12-28 00:27:45,http://gutkowski.org/kaylie,2.0000000000,65.188.53.110,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n3745,6,341,2017-04-14 13:22:58,http://prohaska.net/reva,0.0000000000,44.44.249.197,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n3746,6,341,2017-05-20 14:26:31,http://abernathy.name/velma_bruen,0.0000000000,166.99.243.200,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n3747,6,341,2017-02-12 18:07:22,http://purdy.name/anthony.lemke,0.0000000000,120.185.159.218,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n3748,6,341,2016-12-13 06:05:35,http://king.info/maryse.boyer,1.0000000000,230.224.152.216,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n3749,6,341,2017-05-22 11:16:57,http://witting.io/robin,2.0000000000,42.36.164.25,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n3750,6,341,2016-12-19 04:38:36,http://conn.biz/aurelio,2.0000000000,21.77.184.141,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n3751,6,342,2017-01-02 12:35:14,http://pacocha.name/amaya.osinski,0.0000000000,220.142.130.138,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n3752,6,342,2017-05-31 17:35:01,http://hartmannschumm.net/elva_kerluke,0.0000000000,188.219.2.23,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n3753,6,343,2017-05-24 12:15:12,http://donnelly.org/mina,3.0000000000,132.54.169.115,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n3754,6,343,2017-03-19 07:21:51,http://kreiger.info/keith,3.0000000000,132.54.169.115,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n3755,6,343,2017-02-12 02:01:27,http://turnerbartoletti.co/raleigh_luettgen,2.0000000000,160.155.140.244,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n3756,6,343,2017-02-10 01:07:24,http://barton.name/linnie,0.0000000000,246.132.133.210,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n3757,6,343,2017-04-20 17:09:54,http://halvorson.biz/elisabeth,1.0000000000,50.126.41.36,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n3758,6,343,2017-01-27 14:23:28,http://lang.name/virgie,1.0000000000,254.246.142.182,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n3759,6,344,2017-01-31 06:50:28,http://murazik.co/anastasia.wolff,0.0000000000,240.99.62.164,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n3760,6,344,2017-05-19 10:53:27,http://lebsack.name/reid,1.0000000000,141.130.113.240,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n3761,6,344,2016-12-13 19:42:37,http://rodriguezking.co/eve,0.0000000000,243.53.204.55,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n3762,6,344,2017-01-14 04:06:15,http://schoen.org/corene,0.0000000000,84.171.219.189,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n3763,6,344,2017-04-05 18:57:05,http://kris.io/hallie,0.0000000000,84.160.109.203,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n3764,6,344,2017-04-02 06:25:46,http://welch.io/kitty_batz,0.0000000000,58.162.149.22,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n3765,6,344,2017-05-20 08:17:27,http://lindgren.biz/isom_shields,1.0000000000,44.168.99.26,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n3766,6,344,2017-01-25 23:19:56,http://murphywiegand.org/zaria_bechtelar,0.0000000000,243.53.204.55,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n3767,6,344,2017-02-22 06:20:57,http://kuhn.info/muhammad,1.0000000000,99.14.73.155,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n3768,6,344,2017-02-07 19:12:46,http://rosenbaumkreiger.net/harold,1.0000000000,8.69.9.172,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n3769,6,344,2017-06-11 17:41:40,http://littlebarrows.biz/lenore_mayert,0.0000000000,43.28.224.119,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n3770,6,344,2017-03-08 05:35:25,http://cronin.info/larry,0.0000000000,47.185.82.141,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n3771,6,344,2017-04-29 12:17:10,http://schinnerrau.io/nakia,0.0000000000,213.225.97.172,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n3772,6,344,2017-05-29 20:19:08,http://smitham.info/benedict_boyle,0.0000000000,15.254.24.23,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n618,2,58,2017-01-20 05:33:22,http://halvorson.co/harry,0.0000000000,22.140.173.127,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n619,2,58,2017-04-02 08:52:33,http://bahringer.com/patrick,1.0000000000,10.31.92.156,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n620,2,59,2017-05-21 05:42:40,http://rosenbaum.biz/teie,2.0000000000,59.130.205.221,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n621,2,59,2017-02-28 03:01:39,http://vandervort.name/viviane.lindgren,1.0000000000,121.22.189.85,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n1292,2,123,2017-05-02 08:23:03,http://kuphal.org/brown,,195.245.131.144,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n622,2,59,2017-05-27 00:11:38,http://feeney.info/arjun,0.0000000000,207.137.90.249,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n623,2,59,2017-01-24 06:21:29,http://murazik.org/jadon.wehner,0.0000000000,24.86.213.81,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n624,2,60,2017-05-19 08:13:57,http://frami.com/jerrell,0.0000000000,149.115.183.169,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n625,2,60,2017-06-09 04:20:14,http://emmerich.io/florine.nicolas,0.0000000000,209.66.63.27,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n626,2,60,2017-04-01 13:04:07,http://wyman.com/ida,0.0000000000,3.246.82.71,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n627,2,60,2017-02-03 08:17:14,http://renneremmerich.net/noah,0.0000000000,14.48.201.18,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n628,2,60,2017-05-04 02:56:44,http://crooks.org/darlene,0.0000000000,92.178.81.132,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n629,2,60,2017-05-25 13:03:13,http://gislason.co/cary_lesch,0.0000000000,246.243.203.99,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n630,2,60,2017-04-08 07:36:26,http://schmidt.name/greta.keler,0.0000000000,246.243.203.99,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n631,2,60,2017-05-22 13:02:05,http://streich.net/adelia,0.0000000000,158.34.113.242,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n632,2,60,2016-12-24 09:03:11,http://rennerschowalter.co/travis,0.0000000000,96.19.40.83,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n633,2,60,2017-01-25 03:09:23,http://bodehackett.com/brook,0.0000000000,224.174.203.199,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n634,2,60,2017-03-28 23:30:57,http://gleichner.name/mabelle_emmerich,0.0000000000,214.111.179.2,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n635,2,60,2017-01-05 20:30:39,http://howehills.io/raheem,0.0000000000,7.98.147.74,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n636,2,61,2017-01-24 11:09:52,http://hilpert.com/lacy.little,1.0000000000,228.119.145.31,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n637,2,61,2017-01-26 06:57:04,http://miller.co/hortense,1.0000000000,155.157.69.198,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n638,2,61,2017-01-03 13:14:03,http://johnson.com/jaquan,0.0000000000,28.224.240.211,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n639,2,61,2017-02-01 14:01:38,http://gleasonbechtelar.io/rebeca,1.0000000000,186.254.44.16,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n640,2,62,2017-04-07 05:22:51,http://runolfon.com/cristal,0.0000000000,236.184.6.169,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n641,2,62,2016-12-16 20:45:59,http://steuberpfannerstill.biz/laury.crist,0.0000000000,40.203.231.253,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n642,2,62,2017-01-21 18:03:32,http://buckridgebruen.info/bette,0.0000000000,131.51.63.53,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n643,2,62,2017-01-02 16:15:08,http://leffler.biz/emilia,0.0000000000,217.197.136.180,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n644,2,62,2017-01-27 15:23:21,http://kozey.name/demond,0.0000000000,217.197.136.180,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n645,2,62,2017-03-07 07:07:48,http://herzogyost.org/scarlett.shanahan,0.0000000000,131.51.63.53,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n646,2,62,2017-05-29 07:10:36,http://dooley.biz/rosendo,0.0000000000,122.208.117.82,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n647,2,62,2017-05-11 16:01:19,http://pollich.com/darrell.schaefer,0.0000000000,18.237.6.24,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n648,2,62,2017-05-25 04:38:11,http://mckenzie.co/cruz,0.0000000000,39.216.55.243,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n649,2,62,2017-01-11 21:21:06,http://leannonfritsch.biz/emmet,0.0000000000,42.118.248.238,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n650,2,62,2017-02-01 20:01:16,http://olsonhirthe.org/kole_nicolas,0.0000000000,103.86.224.168,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n651,2,63,2017-01-20 18:51:19,http://raynor.org/raleigh.bode,1.0000000000,150.18.21.66,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n652,2,63,2016-12-14 21:06:02,http://moenhamill.io/kurt_walsh,1.0000000000,95.242.164.113,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n653,2,63,2017-01-01 19:16:00,http://aufderharlueilwitz.org/mozell.abbott,3.0000000000,35.171.116.38,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n654,2,63,2017-03-19 20:17:50,http://schulistkulas.biz/nikita,3.0000000000,172.56.141.44,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n655,2,63,2017-03-06 18:49:25,http://yostdonnelly.info/jameson,1.0000000000,11.208.13.63,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n656,2,63,2017-05-14 15:03:57,http://stehrluettgen.org/jordan,0.0000000000,95.242.164.113,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n657,2,63,2017-01-21 20:02:59,http://ortiz.org/francis,0.0000000000,75.76.113.194,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n658,2,63,2017-04-03 09:08:07,http://mayert.net/kenton_ritchie,3.0000000000,63.112.155.212,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n659,2,63,2017-02-07 00:02:55,http://morarkozey.net/adam_wyman,2.0000000000,133.147.230.215,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n660,2,63,2016-12-14 01:00:59,http://mohr.biz/dayana.moore,3.0000000000,168.130.238.203,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n661,2,63,2017-03-04 10:18:15,http://treutel.info/maximillian.bergstrom,2.0000000000,150.18.21.66,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n662,2,63,2017-05-31 17:29:55,http://trantowsmitham.biz/ova_reichel,3.0000000000,205.228.242.109,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n663,2,63,2017-04-09 03:31:33,http://cruickshankmorar.com/stephan,0.0000000000,146.49.105.154,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n664,2,63,2017-04-02 16:25:10,http://walker.info/erica_volkman,3.0000000000,41.52.203.28,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n665,2,63,2017-04-22 05:18:21,http://lindgrenrutherford.name/name,2.0000000000,84.82.22.124,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n666,2,63,2017-02-06 00:41:23,http://hilpert.name/sarina,1.0000000000,172.56.141.44,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n667,2,63,2017-05-06 01:14:54,http://ryan.org/jazmyn,0.0000000000,99.50.111.133,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n668,2,63,2017-01-07 13:21:53,http://goodwin.io/carley,2.0000000000,168.130.238.203,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n669,2,63,2017-03-07 08:19:11,http://cummings.io/tremaine.gaylord,3.0000000000,172.56.141.44,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n3773,6,344,2017-06-03 02:06:21,http://kilback.info/jarret,0.0000000000,108.32.5.196,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n3774,6,344,2017-04-28 22:44:26,http://williamsonkemmer.co/rickie,0.0000000000,160.131.66.36,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n3775,6,344,2017-02-23 08:43:52,http://bruenprohaska.biz/dedric_dubuque,1.0000000000,160.131.66.36,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n3776,6,344,2017-03-14 21:41:03,http://metz.biz/jewel,1.0000000000,105.91.51.3,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n3777,6,344,2017-03-15 03:39:23,http://jacobsmcglynn.name/nikki,0.0000000000,250.15.107.36,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n3778,6,344,2017-04-22 17:34:52,http://heelruel.net/hilbert_strosin,1.0000000000,243.53.204.55,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n3779,6,345,2017-01-06 07:18:56,http://bruen.net/eino,,62.54.75.185,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n3780,6,345,2017-04-04 03:14:51,http://berge.com/keyshawn,,128.108.28.56,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n3781,6,345,2017-06-06 06:07:38,http://durgan.info/orrin_schneider,,62.54.75.185,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n3782,6,345,2017-03-06 17:34:14,http://schroederschmitt.biz/andres,,94.241.138.212,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n3783,6,345,2017-04-06 08:30:24,http://wolff.info/amber,,114.237.191.214,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n3784,6,345,2017-05-26 17:05:55,http://oberbrunner.com/ed.kulas,,151.202.152.39,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n3785,6,345,2017-01-06 09:28:16,http://ferry.name/nella_block,,114.237.191.214,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n3786,6,345,2017-04-25 04:51:32,http://bosco.info/patrick.leuschke,,151.202.152.39,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n3787,6,346,2017-03-05 08:17:44,http://cummings.info/macey_kunze,,107.179.169.168,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n3788,6,346,2017-01-12 03:13:16,http://zboncak.com/harmony,,241.194.72.86,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n3789,6,346,2017-03-31 23:34:31,http://reingerhilll.name/chance_schuppe,,4.202.27.246,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n3790,6,346,2017-02-23 18:31:09,http://hills.org/skyla,,76.127.124.83,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n3791,6,347,2017-01-22 20:27:29,http://zemlak.io/ciara,,41.92.198.26,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n3792,6,348,2017-01-24 21:49:13,http://hane.io/drew_skiles,,87.23.114.79,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n3793,6,348,2017-02-02 02:28:43,http://bernierhartmann.io/ulices.spencer,,165.113.185.41,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n3794,6,348,2017-05-16 19:49:58,http://naderstehr.net/maida_sporer,,142.106.95.24,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n3795,6,348,2017-04-16 15:13:43,http://eichmanngerhold.info/rosalind,,81.161.194.233,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n3796,6,348,2017-02-20 18:23:58,http://johnston.com/mateo,,243.184.204.37,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n3797,6,348,2017-06-05 18:47:36,http://toy.biz/filomena.boyle,,69.49.70.28,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n3798,6,348,2017-05-20 06:45:57,http://kautzer.co/christiana.harvey,,193.120.155.208,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n3799,6,348,2017-04-01 23:36:23,http://langworthklocko.name/tiffany,,211.15.109.232,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n3800,6,348,2016-12-28 00:26:08,http://wilderman.io/perry_bogan,,37.224.46.26,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n3801,6,349,2017-05-22 13:39:09,http://trantow.com/denis.nikolaus,,181.104.108.139,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n3802,6,349,2017-05-02 23:33:47,http://mills.co/janae,,57.142.19.90,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n3803,6,349,2017-02-26 23:31:12,http://cormier.biz/ebba_willms,,135.124.28.180,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n3804,6,349,2017-02-02 11:45:57,http://goyette.name/lindsey,,248.251.107.180,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n3805,6,349,2017-03-24 07:13:42,http://schowaltermaggio.name/ada_nader,,204.112.150.36,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n3806,6,349,2017-04-08 10:23:18,http://ziemann.co/holden,,121.69.235.203,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n3807,6,349,2017-03-29 05:18:41,http://yost.com/gaston_mosciski,,5.159.121.63,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n3808,6,349,2016-12-20 05:12:35,http://runolfsdottirrunte.org/marcelo,,224.8.100.63,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n3809,6,349,2017-03-14 00:57:04,http://goldner.biz/reie.feest,,116.54.64.97,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n3810,6,349,2017-05-19 03:11:22,http://tremblay.co/carmen,,250.208.4.101,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n3811,6,349,2017-06-03 06:13:12,http://greenholt.info/kaycee,,223.217.55.166,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n3812,6,350,2017-01-18 04:41:36,http://kozeyschuppe.io/lydia.mcglynn,,179.78.232.172,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n3813,6,350,2016-12-25 09:01:44,http://jaskolski.com/shea,,23.87.149.216,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n3814,6,350,2017-03-21 03:54:00,http://parisian.io/daphne,,205.13.95.42,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n3815,6,350,2017-05-01 21:05:24,http://runolfon.co/darrin_sipes,,63.184.150.98,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n3816,6,350,2017-02-14 15:16:05,http://brakusbrakus.io/jensen,,129.89.3.209,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n3817,6,350,2017-05-05 18:05:27,http://wiegand.biz/mikel.dietrich,,179.78.232.172,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n3818,6,350,2017-06-04 23:18:55,http://strosin.org/dean,,68.16.181.120,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n3819,6,350,2016-12-29 18:12:44,http://bailey.com/jadyn,,23.87.149.216,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n3820,6,350,2017-03-13 14:30:42,http://kerluke.org/colten.gorczany,,160.53.31.165,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n3821,6,350,2017-03-11 10:12:53,http://vandervort.io/estel,,63.184.150.98,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n3822,6,351,2017-02-02 13:06:48,http://ortiz.io/emie_lakin,0.0000000000,220.250.70.88,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n3823,6,351,2017-05-29 05:20:06,http://lindgren.biz/carol,3.0000000000,182.5.132.26,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n3824,6,352,2017-04-16 12:00:23,http://bogisichfahey.net/emilie,2.0000000000,82.41.221.49,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n3825,6,352,2017-02-27 06:39:26,http://boyle.net/monserrate,0.0000000000,125.2.8.27,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n3826,6,352,2017-04-08 06:08:54,http://lockman.co/valerie,2.0000000000,188.61.63.58,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n3827,6,352,2017-06-10 22:44:51,http://borermueller.net/zane_grimes,2.0000000000,183.32.196.171,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n670,2,63,2017-01-29 16:13:31,http://schmidt.name/gerson.strosin,0.0000000000,90.157.196.215,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n671,2,64,2017-04-19 11:03:45,http://eichmann.name/adam,,72.37.223.252,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n672,2,64,2017-06-09 11:38:04,http://volkman.info/elta.howell,,189.58.231.216,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n673,2,64,2017-02-08 13:18:35,http://mantethiel.info/gayle.ratke,,250.114.7.204,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n674,2,64,2017-02-07 12:59:58,http://pouros.co/wilber,,165.162.213.186,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n675,2,64,2017-03-28 23:56:12,http://kling.info/mafalda_hudson,,129.108.166.80,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n676,2,64,2016-12-30 11:14:06,http://baileyerdman.info/thea,,16.145.181.55,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n677,2,64,2017-06-10 03:17:15,http://schiller.net/amiya,,198.89.119.146,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n678,2,64,2017-03-16 06:42:59,http://lebsack.org/arch.fisher,,2.9.181.44,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n679,2,64,2017-04-26 16:01:14,http://tillmanabshire.co/skye_hamill,,115.208.79.242,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n680,2,64,2016-12-25 00:46:35,http://greenholthodkiewicz.co/millie,,16.204.208.214,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n681,2,64,2017-02-10 17:44:52,http://mann.co/alfonzo.rath,,129.62.119.213,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n682,2,64,2017-03-27 08:49:45,http://emard.org/wiley.davis,,129.62.119.213,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n683,2,64,2017-04-19 09:54:06,http://ornkling.io/kavon.stark,,69.133.147.85,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n684,2,64,2017-01-18 00:16:22,http://adamsdouglas.net/leanne_emmerich,,230.98.180.68,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n685,2,65,2017-05-11 13:36:33,http://kerlukemayert.biz/casey_howell,,227.184.101.246,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n686,2,65,2017-01-30 17:14:03,http://vandervortsmitham.biz/karli_mertz,,184.214.125.122,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n687,2,65,2016-12-19 00:40:11,http://daugherty.net/alex,,66.252.143.149,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n688,2,65,2017-05-30 06:55:37,http://rice.io/ubaldo,,247.24.25.108,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n689,2,65,2017-05-03 06:04:18,http://kertzmann.biz/casey,,113.36.100.73,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n690,2,65,2017-04-24 21:24:26,http://lebsack.co/oswaldo.pollich,,54.3.235.205,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n691,2,65,2017-05-10 07:42:02,http://kuhic.io/aaliyah,,147.241.227.130,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n692,2,65,2017-03-05 06:03:45,http://rosenbaum.biz/rosamond.gerlach,,31.7.89.67,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n693,2,65,2017-06-02 07:53:31,http://orn.info/name.predovic,,162.19.98.187,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n694,2,65,2017-05-13 13:09:43,http://carter.biz/enrique_nikolaus,,31.7.89.67,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n695,2,65,2017-04-04 15:46:24,http://lindgren.info/jerome_bartoletti,,69.116.65.45,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n696,2,65,2016-12-27 12:59:19,http://boyle.co/elias_hartmann,,224.92.149.187,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n697,2,65,2017-02-22 21:42:58,http://hegmann.name/fern_mcdermott,,31.98.148.11,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n698,2,66,2017-01-27 10:54:28,http://cain.io/erica,,118.116.21.133,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n699,2,66,2017-04-04 11:33:43,http://nader.net/oswaldo,,213.94.223.147,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n700,2,66,2017-01-24 20:34:20,http://vonruedenlehner.com/raleigh,,29.147.115.131,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n701,2,66,2017-02-03 05:01:28,http://welch.com/christian_kling,,251.115.229.91,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n702,2,67,2017-02-07 16:24:39,http://torphy.io/vergie,,183.77.25.246,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n703,2,67,2017-04-29 03:09:10,http://konopelski.io/bruce,,135.152.82.60,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n704,2,67,2017-05-20 22:05:47,http://rodriguez.co/april.corkery,,242.240.82.43,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n705,2,67,2017-05-26 01:59:39,http://steuber.biz/stephany,,144.148.223.18,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n706,2,67,2017-05-22 14:00:22,http://stanton.name/ian.conn,,86.59.142.135,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n707,2,67,2017-02-08 04:44:16,http://wildermanlakin.org/scotty,,177.15.254.124,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n708,2,67,2017-05-17 06:50:45,http://ritchie.org/donavon.lowe,,104.223.204.123,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n709,2,67,2017-01-16 23:40:54,http://bosco.com/makenna_corwin,,44.16.202.177,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n710,2,67,2017-06-07 21:24:35,http://simonis.net/una,,219.61.171.236,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n711,2,68,2017-05-15 02:33:48,http://renner.name/milton,,240.32.102.200,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n712,2,68,2017-01-20 17:55:02,http://kling.com/lauren,,147.213.122.245,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n713,2,68,2017-02-25 15:19:21,http://kuhic.info/rocky.friesen,,221.11.132.24,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n714,2,68,2017-03-14 02:09:06,http://gradyjerde.org/genevieve.okon,,165.206.27.33,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n715,2,68,2017-03-17 00:37:40,http://powlowskidickens.net/emmet,,49.241.168.221,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n716,2,69,2017-01-01 09:46:48,http://parker.com/yasmine,,186.204.52.137,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n717,2,69,2017-04-18 04:49:05,http://dach.biz/marilyne_upton,,143.103.225.98,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n718,2,69,2017-05-09 15:42:44,http://ernserschaefer.org/athena_stiedemann,,33.72.161.166,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n719,2,69,2017-02-04 01:10:08,http://jakubowski.info/clyde,,109.44.215.139,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n720,2,69,2017-05-27 04:59:49,http://roberts.com/janie_frami,,22.163.39.128,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n721,2,69,2017-06-07 16:28:22,http://dach.info/jalyn_kulas,,132.187.19.90,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n722,2,69,2016-12-26 04:15:51,http://schumm.info/meda_casper,,235.68.212.180,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n723,2,69,2017-06-08 22:20:18,http://wintheiserkeeling.name/gabriel,,15.24.196.54,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n724,2,69,2017-04-16 18:26:09,http://franecki.co/juwan,,220.197.155.202,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n725,2,69,2017-02-24 17:20:42,http://kutchgreenholt.org/angelo.mante,,15.24.196.54,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n3828,6,352,2017-02-06 10:49:49,http://altenwerthberge.info/virgil,2.0000000000,241.131.137.60,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n3829,6,352,2017-01-24 10:22:31,http://homenick.org/tatyana_kreiger,1.0000000000,253.71.131.50,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n3830,6,352,2017-04-05 12:43:16,http://hermistondamore.biz/faye.hudson,2.0000000000,213.67.228.221,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n3831,6,352,2017-01-07 21:36:16,http://murray.info/abner,2.0000000000,253.71.131.50,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n3832,6,352,2017-06-03 21:51:11,http://murphytowne.io/janiya_mcclure,0.0000000000,241.131.137.60,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n3833,6,352,2017-03-02 19:35:51,http://greenfelder.org/devan,2.0000000000,153.170.167.221,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n3834,6,352,2017-03-02 03:00:04,http://schaeferabbott.com/fleta.bernier,0.0000000000,47.137.58.230,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n3835,6,352,2017-01-25 13:40:05,http://ruel.biz/elliot.ledner,2.0000000000,238.156.179.135,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n3836,6,352,2016-12-15 20:22:36,http://farrell.name/colby,0.0000000000,178.20.86.119,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n3837,6,352,2017-05-21 04:03:17,http://parisian.com/adonis,2.0000000000,8.6.173.37,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n3838,6,352,2017-01-03 05:10:11,http://nikolauslynch.org/maria_leuschke,1.0000000000,47.96.115.132,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n3839,6,352,2017-02-16 20:59:28,http://gradycasper.info/efren.terry,0.0000000000,86.253.233.107,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n3840,6,352,2017-04-08 02:39:32,http://lebsack.co/jeramie.dooley,2.0000000000,31.88.68.86,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n3841,6,352,2017-04-08 02:20:01,http://runolfsdottir.org/tanya_wuckert,0.0000000000,84.32.77.20,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n3842,6,352,2017-02-08 05:08:23,http://wisozknienow.net/heaven,0.0000000000,47.137.58.230,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n3843,6,353,2017-04-19 15:03:52,http://jenkinstorphy.net/salvatore,2.0000000000,186.197.46.47,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n3844,6,353,2017-06-03 02:54:39,http://hansensenger.biz/abe.yundt,0.0000000000,106.100.10.163,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n3845,6,354,2017-04-26 07:32:52,http://adamchaefer.co/carmine,1.0000000000,90.88.87.48,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n3846,6,354,2017-01-21 11:47:03,http://schimmel.com/hayden.reichel,0.0000000000,158.196.251.49,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n3847,6,354,2017-04-14 12:23:36,http://lowerolfson.org/westley.hodkiewicz,1.0000000000,135.149.214.156,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n3848,6,354,2017-05-17 18:26:01,http://hirthe.org/keeley,0.0000000000,49.199.186.207,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n3849,6,354,2017-02-14 12:15:40,http://heel.com/buford_dietrich,1.0000000000,150.202.94.20,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n3850,6,354,2017-02-18 06:42:34,http://krajcik.biz/tiffany,1.0000000000,141.96.210.243,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n3851,6,354,2017-02-05 00:35:16,http://bergstrom.co/mitchell_yundt,1.0000000000,143.48.10.190,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n3852,6,354,2017-05-10 09:47:38,http://morar.info/vidal,0.0000000000,115.237.185.209,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n3853,6,354,2016-12-30 19:39:51,http://dicki.name/rudolph.windler,1.0000000000,22.35.189.232,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n3854,6,354,2017-01-18 15:45:22,http://schowalter.io/juliana_bernhard,0.0000000000,71.85.15.39,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n3855,6,354,2017-03-30 02:09:03,http://king.co/green,0.0000000000,49.199.186.207,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n3856,6,355,2017-04-26 03:01:33,http://walkersatterfield.name/victor_buckridge,0.0000000000,166.90.45.203,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n3857,6,355,2017-03-20 05:19:46,http://west.io/giovanna_tillman,0.0000000000,187.51.14.191,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n3858,6,355,2017-01-26 23:50:01,http://ohara.com/gust_ledner,0.0000000000,107.169.230.172,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n3859,6,355,2017-04-18 16:10:22,http://pricetorphy.com/keagan,0.0000000000,251.99.139.54,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n3860,6,355,2017-02-24 06:55:51,http://miller.info/mathew,0.0000000000,109.69.242.210,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n3861,6,355,2016-12-16 08:01:15,http://kunde.co/fay_corkery,0.0000000000,6.197.99.129,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n3862,6,355,2017-04-13 09:03:53,http://legros.net/kendrick.quitzon,0.0000000000,217.205.215.61,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n3863,6,355,2017-01-10 04:21:19,http://hegmannbernhard.info/dashawn,0.0000000000,42.150.58.145,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n3864,6,355,2017-05-22 03:14:56,http://howellquitzon.io/deondre_dubuque,0.0000000000,201.125.143.5,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n3865,6,355,2017-05-20 12:34:46,http://lesch.com/hank.swift,0.0000000000,216.44.115.200,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n3866,6,355,2017-02-03 21:03:14,http://bednarkoelpin.com/cletus,0.0000000000,6.197.99.129,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n3867,6,355,2017-04-22 16:44:44,http://abshire.net/ricky,0.0000000000,112.79.129.252,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n3868,6,355,2017-02-18 12:45:59,http://lowe.org/constantin,0.0000000000,112.79.129.252,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n3869,6,355,2017-03-06 17:49:11,http://ritchie.org/braulio_rogahn,0.0000000000,113.24.223.92,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n3870,6,355,2016-12-28 18:48:19,http://larkintillman.org/christian,0.0000000000,209.99.242.152,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n3871,6,355,2017-04-22 20:36:10,http://johnston.com/anika_murray,0.0000000000,142.220.42.13,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n3872,6,355,2016-12-29 23:43:41,http://mraz.biz/mike_hilll,0.0000000000,107.169.230.172,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n3873,6,356,2017-03-12 08:48:53,http://davis.io/kirstin,1.0000000000,71.74.246.192,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n3874,6,356,2017-05-27 21:29:55,http://torphy.io/jalen.nicolas,0.0000000000,151.109.253.110,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n3875,6,356,2017-04-18 18:44:46,http://stoltenberg.biz/june,0.0000000000,248.157.100.104,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n3876,6,356,2017-01-12 13:56:22,http://daughertyfadel.biz/alysha,0.0000000000,138.150.187.129,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n3877,6,357,2016-12-26 15:25:59,http://durgan.biz/amalia,1.0000000000,205.20.117.45,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n3878,6,357,2017-05-01 05:57:54,http://jacobsmraz.net/rasheed.jenkins,0.0000000000,32.85.12.192,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n726,2,69,2017-04-15 06:56:55,http://littlestoltenberg.net/ted,,152.151.43.91,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n727,2,70,2017-05-08 19:57:00,http://jonesnienow.net/shannon_franecki,,245.132.194.115,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n728,2,70,2017-01-25 19:21:26,http://altenwerth.net/eric.streich,,71.200.107.59,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n729,2,70,2017-02-23 16:25:09,http://schoenweinat.co/lula.okuneva,,189.134.57.14,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n730,2,70,2017-06-02 16:03:01,http://tromp.org/keagan_wehner,,132.24.183.209,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n731,2,70,2017-01-01 09:36:27,http://watsica.org/jordon,,188.86.81.231,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n732,2,70,2017-05-18 01:49:24,http://daugherty.info/margarete.friesen,,157.221.235.125,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n733,2,70,2017-05-22 08:21:31,http://waelchichristiansen.com/carol,,65.4.54.208,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n734,2,70,2017-02-01 16:53:10,http://mosciski.net/miller,,77.145.165.197,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n735,2,70,2016-12-28 03:51:35,http://johnson.io/hattie_connelly,,127.238.105.202,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n736,2,70,2017-05-11 11:54:14,http://bins.info/bailee,,28.188.144.199,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n737,2,70,2017-01-12 21:21:30,http://vonrueden.biz/america_sipes,,128.76.254.135,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n738,2,70,2016-12-13 10:44:56,http://nikolaus.io/bert_rosenbaum,,233.154.134.69,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n739,2,70,2017-05-18 11:11:33,http://volkman.net/ansel,,189.134.57.14,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n740,2,70,2016-12-13 06:53:02,http://connelly.co/wilhelmine_welch,,160.173.211.112,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n741,2,70,2017-06-13 17:50:22,http://emmerichjacobson.info/dejon_yost,,150.100.82.111,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n742,2,70,2017-05-30 08:30:25,http://gerhold.co/esther.lind,,65.4.54.208,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n743,2,70,2017-04-30 01:36:51,http://colezemlak.io/devante.cartwright,,240.239.78.233,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n744,2,71,2016-12-22 20:42:45,http://howell.io/ed,,190.203.67.98,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n745,2,71,2017-05-10 08:44:30,http://batzbuckridge.co/brain,,96.16.109.58,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n746,2,71,2017-01-19 07:01:56,http://purdybeahan.info/zita,,69.55.203.19,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n747,2,72,2017-01-28 01:57:19,http://wunsch.com/jerald.yundt,,14.29.229.209,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n748,2,72,2017-04-11 08:09:40,http://herzogstreich.net/thelma_beatty,,83.181.47.51,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n749,2,72,2017-04-17 14:36:21,http://cronabarton.biz/cale_shanahan,,169.244.74.103,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n750,2,72,2017-04-18 21:28:02,http://nienow.com/lewis.bashirian,,180.223.78.82,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n751,2,72,2017-02-04 23:09:21,http://bechtelar.net/edd_berge,,14.29.229.209,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n752,2,72,2017-01-18 15:17:23,http://kutch.name/evert_okeefe,,151.53.226.48,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n753,2,72,2017-05-02 11:11:45,http://harber.com/dave_green,,151.53.226.48,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n754,2,72,2017-02-08 07:55:16,http://murphy.biz/rogers,,171.15.188.220,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n755,2,72,2017-04-13 02:43:39,http://kunde.co/marlin.swaniawski,,167.7.40.183,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n756,2,72,2016-12-25 10:24:54,http://wunschlittle.org/trevion_kshlerin,,171.15.188.220,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n757,2,72,2017-03-11 15:05:38,http://blick.org/nicole_king,,180.223.78.82,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n758,2,73,2017-02-04 17:31:19,http://price.io/filiberto_mills,,186.190.179.110,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n759,2,73,2017-05-23 08:34:16,http://heller.net/teagan.lehner,,119.77.12.204,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n760,2,73,2017-04-21 11:18:46,http://armstrongheaney.info/thea,,32.179.175.95,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n761,2,73,2017-01-02 12:42:06,http://satterfieldcarter.io/oscar.collier,,23.228.236.126,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n762,2,73,2017-05-02 07:01:53,http://nikolauskunde.com/judson_bernhard,,201.189.136.201,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n763,2,73,2017-05-24 22:21:00,http://jast.name/jamir,,204.22.203.208,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n764,2,73,2017-06-09 08:18:28,http://bosco.net/caandre_bruen,,103.94.247.167,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n765,2,73,2017-01-25 11:25:19,http://schambergeradams.com/gina,,201.189.136.201,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n766,2,73,2017-06-06 03:44:35,http://rath.net/ofelia_kiehn,,114.44.50.224,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n767,2,73,2017-03-23 03:50:55,http://schuster.org/arlie,,48.131.138.226,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n768,2,73,2017-05-09 09:02:47,http://hodkiewicz.co/octavia,,239.211.230.200,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n769,2,73,2017-06-07 06:09:31,http://hirthe.name/felicia,,107.137.127.179,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n770,2,73,2017-03-17 20:25:25,http://leannon.co/dolly.beier,,200.75.60.70,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n771,2,73,2017-01-30 09:56:07,http://millsrodriguez.org/tiana,,84.57.92.120,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n772,2,73,2017-03-14 22:17:42,http://lowemiller.io/grant,,6.131.100.251,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n773,2,73,2016-12-16 18:44:58,http://sanfordweimann.co/lydia,,239.211.230.200,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n774,2,74,2016-12-14 23:20:13,http://schroederwhite.org/halle,,3.150.124.36,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n775,2,74,2017-03-22 01:24:33,http://reingerhowe.org/dejuan_parisian,,124.62.249.97,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n776,2,74,2017-05-20 07:22:15,http://cormierhaag.net/jesus_klocko,,221.173.46.105,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n777,2,74,2017-05-20 20:53:21,http://hermiston.co/isabell_paucek,,95.214.40.90,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n778,2,74,2017-04-10 13:17:27,http://larson.io/elton,,3.150.124.36,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n779,2,74,2017-05-28 15:05:18,http://friesenzieme.org/norwood.orn,,119.247.161.123,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n780,2,74,2016-12-21 06:09:26,http://reinger.net/mylene_gleichner,,177.61.32.107,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n781,2,74,2017-04-30 06:12:30,http://cremin.co/amina,,112.58.59.207,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n3879,6,357,2017-05-05 16:40:27,http://hartmanncollier.biz/marge,1.0000000000,201.212.10.254,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n3880,6,357,2017-03-08 11:20:32,http://koch.name/shea_wiza,0.0000000000,65.203.234.226,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n3881,6,357,2017-01-23 07:44:52,http://harber.net/selena.nitzsche,2.0000000000,149.237.66.14,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n3882,6,357,2017-01-29 12:33:59,http://ruelbailey.org/nedra,2.0000000000,153.48.54.234,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n3883,6,357,2016-12-15 02:07:17,http://connellywaelchi.com/nathanael,1.0000000000,195.114.174.18,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n3884,6,357,2017-03-09 04:12:23,http://schimmel.name/larry.waelchi,2.0000000000,28.113.123.24,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n3885,6,357,2017-01-24 19:50:49,http://schmelerconnelly.biz/stacy.conn,2.0000000000,105.191.204.52,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n3886,6,357,2017-01-21 16:11:33,http://bartell.name/jennyfer,1.0000000000,192.120.183.37,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n3887,6,357,2017-03-24 14:19:35,http://satterfield.biz/joesph,2.0000000000,195.114.174.18,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n3888,6,357,2017-04-10 23:25:29,http://powlowski.org/cleta,0.0000000000,111.34.217.211,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n3889,6,357,2017-02-08 10:49:09,http://renner.biz/geraldine_bernhard,0.0000000000,192.120.183.37,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n3890,6,357,2017-03-27 13:39:53,http://kiehn.org/gonzalo_considine,2.0000000000,214.34.91.115,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n3891,6,357,2017-01-29 18:03:59,http://veumhansen.biz/claude,0.0000000000,61.213.239.33,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n3892,6,358,2017-05-30 02:41:37,http://rennerosinski.org/katherine,3.0000000000,235.85.225.137,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n3893,6,358,2017-03-16 10:20:27,http://rolfson.co/enola,2.0000000000,176.20.240.216,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n3894,6,358,2017-04-30 21:13:03,http://hilpert.org/vallie,0.0000000000,108.207.85.139,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n3895,6,358,2017-04-30 00:37:38,http://pacocha.biz/jeanette.gibson,0.0000000000,235.85.225.137,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n3896,6,358,2017-04-17 09:38:26,http://weimann.org/michael_batz,1.0000000000,213.232.26.245,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n3897,6,358,2017-06-02 13:06:51,http://okeefebednar.org/vance_mann,0.0000000000,252.53.183.52,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n3898,6,358,2017-04-30 12:15:22,http://larson.info/karianne_boyer,2.0000000000,176.20.240.216,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n3899,6,359,2016-12-26 05:38:27,http://ruecker.net/cordelia,3.0000000000,114.187.179.174,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n3900,6,360,2017-01-27 03:46:25,http://wiza.net/sonya.mayer,2.0000000000,85.116.149.16,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n3901,6,360,2016-12-31 03:11:14,http://lindgrendavis.com/felton,1.0000000000,85.116.149.16,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n3902,6,360,2017-05-01 16:24:04,http://daughertyeichmann.co/shad,1.0000000000,102.44.196.132,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n3903,6,360,2016-12-22 12:52:53,http://streich.info/odie,0.0000000000,111.98.79.129,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n3904,6,360,2017-04-30 22:31:23,http://lubowitzrenner.info/rusty.olson,1.0000000000,248.194.192.127,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n3905,6,360,2017-01-10 18:35:10,http://runte.com/lewis,0.0000000000,248.194.192.127,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n3906,6,361,2017-06-10 17:20:10,http://wuckert.co/edmund,0.0000000000,54.188.235.36,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n3907,6,361,2016-12-25 05:45:39,http://dicki.net/lizzie_bogisich,1.0000000000,123.49.27.110,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n3908,6,361,2017-05-03 18:34:14,http://white.org/payton.lubowitz,2.0000000000,14.179.128.90,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n3909,6,361,2017-02-03 21:29:24,http://wisokywintheiser.io/alfonso_dooley,1.0000000000,120.181.159.198,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n3910,6,361,2017-02-18 03:22:20,http://fritsch.io/ewald,2.0000000000,233.134.209.182,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n3911,6,361,2017-03-22 03:02:58,http://rosenbaumfahey.biz/ricardo_swaniawski,0.0000000000,244.133.70.218,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n3912,6,361,2017-01-27 09:47:24,http://howe.net/nathan,0.0000000000,123.49.27.110,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n3913,6,361,2016-12-26 12:26:09,http://okuneva.net/rubye,0.0000000000,36.111.218.159,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n3914,6,361,2017-01-20 12:04:54,http://trompkeler.net/raegan.johnson,0.0000000000,175.145.161.162,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n3915,6,361,2017-03-18 14:43:05,http://adams.info/estrella,0.0000000000,242.195.35.51,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n3916,6,362,2017-03-21 11:20:05,http://bergedenesik.io/jee,1.0000000000,127.248.215.175,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n3917,6,362,2016-12-18 11:15:34,http://swaniawski.org/hilma_turner,2.0000000000,62.82.184.3,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n3918,6,362,2016-12-15 14:33:10,http://murray.info/clementina_heidenreich,2.0000000000,79.123.241.58,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n3919,6,363,2017-05-07 19:44:26,http://stoltenberg.org/quinn_marvin,0.0000000000,145.20.107.44,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n3920,6,363,2017-03-23 21:09:40,http://borertowne.info/julius,0.0000000000,42.96.151.136,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n3921,6,363,2016-12-14 21:53:09,http://mcdermott.info/morris,0.0000000000,29.175.119.115,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n3922,6,363,2017-04-12 12:40:30,http://sanfordlangworth.co/keely.wehner,0.0000000000,74.38.34.190,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n3923,6,363,2017-01-15 09:46:22,http://graham.org/uriah.huels,0.0000000000,134.8.110.230,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n3924,6,363,2017-01-21 21:30:30,http://koch.biz/lizzie.heathcote,0.0000000000,109.64.133.53,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n3925,6,363,2017-02-19 05:00:44,http://skiles.net/darron,0.0000000000,152.210.235.21,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n3926,6,363,2017-04-06 22:54:39,http://senger.io/lisette.gutmann,0.0000000000,18.185.175.133,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n3927,6,363,2017-02-23 11:46:17,http://macejkovicgerhold.io/casey_wisoky,0.0000000000,74.38.34.190,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n3928,6,363,2017-04-30 23:42:47,http://abernathy.org/belle_stehr,0.0000000000,177.41.161.59,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n3929,6,363,2017-01-10 07:39:28,http://hills.co/cameron.boehm,0.0000000000,4.172.224.221,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n3930,6,363,2017-04-16 19:33:05,http://kertzmann.net/pasquale,0.0000000000,209.66.113.241,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n782,2,74,2017-01-16 13:10:32,http://littelkeeling.co/jerod_rolfson,,32.219.151.162,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n783,2,74,2016-12-15 02:45:23,http://walker.info/maximillian.rosenbaum,,95.214.40.90,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n784,2,74,2017-01-25 02:41:24,http://roberts.io/keith.dickinson,,119.247.161.123,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n785,2,74,2016-12-13 08:00:04,http://wisozk.biz/delphia,,61.26.6.126,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n786,2,74,2017-01-16 17:51:05,http://yundtwilliamson.io/diego_koch,,124.62.249.97,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n787,2,74,2017-03-30 03:46:51,http://mraz.biz/fiona,,221.173.46.105,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n788,2,74,2017-03-12 13:41:51,http://littelbayer.co/alexane.blanda,,108.107.121.161,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n789,2,74,2017-06-10 13:35:17,http://boehm.co/kelton,,95.214.40.90,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n790,2,75,2017-06-08 18:18:27,http://mcglynn.org/francesco.wisozk,,134.83.37.40,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n791,2,75,2017-02-22 18:14:11,http://sipetokes.info/aracely_hudson,,48.210.93.176,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n792,2,75,2017-02-18 19:30:14,http://littel.io/newell.marquardt,,87.137.232.101,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n793,2,75,2017-05-18 13:03:54,http://herzog.com/margarette,,192.29.43.24,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n794,2,75,2017-01-06 01:25:17,http://mueller.biz/tianna,,47.101.67.125,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n795,2,75,2017-02-21 16:53:25,http://ankunding.biz/bertha,,21.239.210.51,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n796,2,75,2016-12-22 04:36:16,http://kshlerinfeest.com/gunner,,6.19.183.245,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n797,2,75,2017-03-15 13:04:46,http://hauck.org/velma,,110.134.245.74,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n798,2,76,2017-03-01 01:19:31,http://lemke.io/kirstin.morar,,183.3.215.229,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n799,2,76,2017-04-01 18:09:53,http://runtecain.io/sierra_nienow,,179.2.142.51,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n800,2,76,2017-03-04 17:17:47,http://leffler.net/ransom,,137.6.143.20,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n801,2,76,2017-01-02 11:30:24,http://langworthbreitenberg.info/demarcus.daniel,,243.224.149.185,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n802,2,76,2017-01-02 11:33:17,http://dickensmitchell.info/ellis,,208.195.59.250,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n803,2,76,2017-05-19 04:42:27,http://smith.io/rhoda.mills,,5.90.248.133,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n804,2,76,2017-05-06 23:27:38,http://ko.io/jett_kovacek,,153.111.253.194,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n805,2,76,2017-01-27 01:01:30,http://schaefer.co/alene_champlin,,142.113.5.240,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n806,2,76,2017-05-05 02:03:56,http://millchroeder.com/colton_grant,,208.195.59.250,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n807,2,76,2017-06-02 11:27:50,http://lubowitzsteuber.net/manuela.grant,,137.6.143.20,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n808,2,76,2017-05-02 12:29:46,http://donnelly.net/kiarra_marvin,,205.157.141.233,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n809,2,76,2017-06-11 05:27:52,http://beer.net/haley_kuvalis,,201.46.216.193,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n810,2,76,2017-02-10 15:54:47,http://langosh.biz/oswaldo_cartwright,,107.195.136.143,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n811,2,76,2016-12-25 19:15:55,http://wymangoldner.biz/raquel_bins,,183.3.215.229,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n812,2,76,2016-12-22 13:33:13,http://spinka.co/pierre.kris,,107.195.136.143,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n813,2,77,2017-06-01 14:16:06,http://spinkacruickshank.co/sincere.hoeger,,205.40.30.246,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n814,2,77,2017-01-18 15:21:06,http://beatty.com/izaiah.wolff,,157.199.53.135,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n815,2,77,2017-05-17 00:52:10,http://kozeypredovic.info/erin.runolfon,,28.227.225.155,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n816,2,77,2017-02-20 07:42:54,http://heaney.biz/larue,,198.67.77.138,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n817,2,77,2017-05-06 07:06:18,http://gottliebschimmel.com/rebekah,,23.240.244.244,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n818,2,77,2017-06-04 19:57:55,http://schulist.info/bertrand.boyer,,227.112.35.97,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n819,2,77,2017-04-22 05:56:30,http://hahn.com/verdie,,222.217.196.181,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n820,2,78,2017-04-14 16:02:27,http://skiles.com/felipe_johns,,117.10.186.121,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n821,2,78,2016-12-23 00:37:51,http://koeppgrady.io/sibyl.hoppe,,218.83.38.201,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n822,2,78,2017-04-15 17:06:01,http://emmerich.info/kayla_cruickshank,,234.123.37.216,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n823,2,78,2017-06-04 03:22:24,http://glover.com/misty,,39.207.194.120,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n824,2,78,2017-02-12 14:03:49,http://rodriguez.com/ethan,,30.183.44.48,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n825,2,78,2017-03-08 17:31:22,http://reinger.com/justyn_hayes,,30.183.44.48,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n826,2,78,2017-06-01 04:54:09,http://bruen.co/aylin.little,,146.252.155.201,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n827,2,78,2017-05-16 12:15:12,http://grady.net/syble,,60.219.185.33,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n828,2,78,2017-04-11 12:55:21,http://boganprice.name/mohammed,,218.83.38.201,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n829,2,78,2017-01-13 17:33:53,http://olsonokeefe.org/adriana.wolff,,234.123.37.216,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n830,2,78,2017-03-09 14:50:44,http://wehner.co/armand_heller,,117.10.186.121,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n831,2,78,2017-02-08 12:44:00,http://rowe.biz/mittie.grant,,117.10.186.121,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n832,2,79,2017-03-14 16:37:19,http://batz.net/moses,,176.76.241.79,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n833,2,79,2017-05-30 17:48:32,http://hodkiewicz.biz/uriel.schaden,,63.238.24.176,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n834,2,79,2017-02-26 00:34:56,http://donnellyharber.biz/wilbert.hodkiewicz,,167.77.167.229,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n835,2,80,2017-01-26 00:00:36,http://wuckertbosco.org/rose_konopelski,,31.185.243.211,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n836,2,80,2017-03-14 15:07:12,http://auer.org/claud,,14.48.181.30,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n837,2,80,2017-05-11 19:50:19,http://stehrkuhic.name/kiarra,,67.245.90.212,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n3931,6,363,2017-01-15 00:23:37,http://white.biz/tyra.kuvalis,0.0000000000,4.172.224.221,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n3932,6,363,2016-12-23 12:40:28,http://jacobsbosco.name/genoveva,0.0000000000,65.12.166.72,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n3933,6,363,2017-01-15 22:13:18,http://harberhomenick.name/stacey,0.0000000000,90.219.235.249,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n3934,6,363,2017-02-03 14:33:40,http://rolfson.org/nicklaus,0.0000000000,199.191.169.77,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n3935,6,363,2017-04-25 06:50:58,http://lemke.net/ibrahim_miller,0.0000000000,145.20.107.44,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n3936,6,363,2017-04-02 21:02:29,http://schneider.io/shad.lueilwitz,0.0000000000,116.193.180.201,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n3937,6,363,2017-04-12 12:57:03,http://feil.org/gaylord,0.0000000000,99.85.112.98,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n3938,6,364,2017-02-07 14:39:29,http://donnelly.org/aurore,3.0000000000,55.9.135.106,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n3939,6,364,2017-05-03 02:54:02,http://leannon.biz/lola,0.0000000000,183.171.103.52,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n3940,6,364,2017-05-21 07:43:23,http://zulauf.co/elsie.feeney,3.0000000000,30.251.10.66,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n3941,6,364,2017-04-08 17:47:55,http://weinatdubuque.org/kayli_mccullough,2.0000000000,244.65.159.248,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n3942,6,364,2017-01-23 12:02:54,http://crona.info/jeanne,2.0000000000,101.14.86.95,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n3943,6,364,2017-02-20 18:16:56,http://lakin.info/tod,3.0000000000,190.243.72.46,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n3944,6,364,2017-04-30 11:17:02,http://armstrong.com/hallie.bogisich,2.0000000000,220.218.130.3,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n3945,6,364,2017-04-03 03:33:41,http://jast.biz/kathleen,2.0000000000,3.91.225.165,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n3946,6,364,2017-04-30 19:45:46,http://haley.io/retta.mayer,1.0000000000,216.110.55.154,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n3947,6,364,2017-05-08 12:39:52,http://bins.org/bernita,0.0000000000,56.176.124.91,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n3948,6,364,2017-03-17 17:53:27,http://rodriguez.io/gretchen.pagac,1.0000000000,49.176.236.126,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n3949,6,364,2017-05-26 22:49:26,http://monahanfahey.com/dayna,2.0000000000,209.109.230.161,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n3950,6,364,2017-01-18 00:57:49,http://dicki.net/velma,1.0000000000,209.109.230.161,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n3951,6,364,2017-01-08 03:02:04,http://wildermanbernhard.name/jaime,0.0000000000,56.176.124.91,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n3952,6,364,2017-05-27 08:56:39,http://carroll.name/mark,3.0000000000,88.246.53.133,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n3953,6,364,2017-01-10 00:03:40,http://franecki.biz/chandler,1.0000000000,200.74.75.208,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n3954,6,365,2017-05-31 18:47:12,http://veum.co/milford_will,2.0000000000,18.164.162.8,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n3955,6,365,2017-02-21 03:12:41,http://kiehn.name/jovani.howell,2.0000000000,37.254.51.13,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n3956,6,365,2017-02-03 07:25:19,http://nader.info/hailey,0.0000000000,83.86.222.238,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n3957,6,365,2017-03-29 11:08:50,http://dietrich.net/ardella,2.0000000000,179.46.188.252,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n3958,6,365,2017-03-04 22:07:05,http://ziemann.net/tremaine,0.0000000000,218.125.53.176,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n3959,6,366,2016-12-15 19:32:45,http://feeneyleuschke.info/golden.ohara,3.0000000000,195.48.66.144,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n3960,6,366,2017-03-19 18:07:42,http://stokes.biz/amiya.hilpert,3.0000000000,31.38.39.35,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n3961,6,366,2016-12-31 21:07:50,http://franecki.name/jackeline,1.0000000000,73.22.57.77,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n3962,6,366,2017-03-17 10:24:46,http://ruelsmitham.org/jarret,0.0000000000,107.215.232.15,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n3963,6,366,2017-03-28 23:27:26,http://kreiger.co/eulalia_kovacek,0.0000000000,31.38.39.35,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n3964,6,367,2017-03-04 08:41:10,http://price.biz/marilie,,229.203.73.132,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n3965,6,367,2017-02-26 19:22:11,http://stantongreenfelder.com/ryann,,53.96.23.40,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n3966,6,367,2017-05-18 18:35:11,http://mosciski.info/delaney.ullrich,,29.158.244.71,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n3967,6,367,2017-05-31 05:01:13,http://spinkamcclure.info/kolby,,241.39.246.7,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n3968,6,367,2017-01-13 15:59:24,http://wunsch.org/erling.heaney,,225.150.43.192,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n3969,6,367,2017-05-24 17:39:40,http://ferry.org/paolo.rogahn,,161.177.132.29,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n3970,6,367,2017-02-01 14:40:05,http://dietrich.info/hildegard,,79.176.47.13,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n3971,6,367,2017-04-23 18:21:21,http://okeefe.net/ethan,,2.216.162.28,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n3972,6,367,2017-04-07 20:07:17,http://bradtkeoconnell.io/kathryn,,145.25.23.221,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n3973,6,368,2017-04-21 00:43:31,http://mckenziedickens.org/anabelle,,95.159.156.158,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n3974,6,368,2016-12-18 05:16:45,http://huels.co/kayla.torphy,,63.242.161.113,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n3975,6,368,2017-04-05 18:45:30,http://dietrich.co/maggie.klocko,,109.117.98.56,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n3976,6,368,2017-05-05 20:20:37,http://daniel.info/rozella,,63.242.161.113,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n3977,6,368,2017-03-14 03:10:01,http://blick.name/retha.smith,,143.181.3.95,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n3978,6,369,2017-01-12 17:02:47,http://bins.org/maggie,,32.26.184.232,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n3979,6,369,2017-01-09 14:05:59,http://stracke.com/ashtyn,,21.210.239.237,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n3980,6,369,2017-02-21 22:15:47,http://mooreblanda.io/lorenz,,150.48.41.74,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n3981,6,369,2017-01-05 08:28:30,http://buckridge.com/athena,,254.153.64.167,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n3982,6,370,2017-03-07 23:58:50,http://paucekshields.co/jenifer.king,,8.114.52.81,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n3983,6,370,2017-06-12 10:22:12,http://tromp.info/carmel,,119.115.170.115,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n838,2,80,2017-06-10 19:58:59,http://connelly.name/avery_schimmel,,67.245.90.212,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n839,2,80,2017-01-16 11:53:57,http://parker.org/demetrius,,103.126.177.58,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n840,2,80,2017-02-28 16:03:28,http://upton.co/leif.bernier,,234.228.81.84,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n841,2,80,2017-02-11 13:31:25,http://stark.io/madaline,,136.204.143.202,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n842,2,80,2017-06-05 19:37:46,http://reynolds.info/roberto_rau,,136.204.143.202,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n843,2,80,2016-12-20 12:54:17,http://stracke.biz/emory.schmidt,,165.117.60.32,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n844,2,80,2017-02-03 16:56:53,http://walker.com/jed,,234.228.81.84,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n845,2,80,2016-12-15 10:05:55,http://wisozk.org/dario.buckridge,,97.36.171.192,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n846,2,81,2017-05-27 20:15:40,http://runolfon.io/shanelle_roberts,,185.233.7.47,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n847,2,81,2017-02-18 01:38:49,http://osinski.com/misty,,34.124.53.111,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n848,2,81,2017-03-08 11:12:33,http://tromp.io/dario_schmeler,,81.147.112.87,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n849,2,81,2016-12-31 06:12:17,http://feilaltenwerth.info/sim.fisher,,193.90.163.180,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n850,2,81,2017-02-12 11:43:16,http://christiansen.co/sherman,,6.14.46.107,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n851,2,81,2017-02-02 11:42:35,http://wolf.info/una.rice,,99.100.90.179,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n852,2,81,2017-02-02 02:04:52,http://larkin.co/priscilla_goodwin,,94.204.114.194,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n853,2,81,2017-03-22 04:52:06,http://heel.info/sophie_aufderhar,,244.13.11.157,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n854,2,81,2017-03-10 13:58:23,http://donnelly.net/giovanny.larkin,,34.124.53.111,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n855,2,81,2016-12-31 10:33:41,http://cruickshank.biz/waldo_bruen,,92.214.73.156,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n856,2,81,2017-02-24 16:00:28,http://watsica.biz/ethel,,197.98.30.207,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n857,2,81,2017-02-09 00:11:41,http://stehrhoppe.biz/michael_mayert,,244.13.11.157,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n858,2,81,2017-03-10 07:04:34,http://kuphalrippin.co/marley,,81.147.112.87,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n859,2,82,2017-01-18 08:54:41,http://caspercole.net/oma,,41.152.243.37,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n860,2,82,2017-03-05 17:33:00,http://labadie.info/tyreek_turner,,46.162.244.21,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n861,2,82,2017-06-12 22:22:42,http://zulauf.biz/madilyn_beer,,129.226.19.84,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n862,2,82,2017-01-28 19:50:59,http://gradyhane.org/karli,,81.46.107.133,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n863,2,82,2017-01-22 14:13:44,http://schillergulgowski.info/lysanne_pacocha,,57.13.107.77,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n864,2,83,2017-05-17 02:32:40,http://kshlerin.org/bonita,,216.148.228.159,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n865,2,83,2016-12-22 06:24:04,http://gorczanyprice.biz/bettye,,152.125.108.240,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n866,2,84,2017-02-10 03:03:52,http://donnelly.org/bria.kreiger,,58.190.103.240,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n867,2,84,2017-01-08 23:03:58,http://kunde.biz/theresa,,96.104.241.149,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n868,2,84,2017-03-09 14:46:11,http://farrell.name/delores.langworth,,168.138.93.245,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n869,2,84,2017-03-14 16:06:15,http://price.org/samanta.bashirian,,62.221.145.130,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n870,2,84,2017-01-25 07:05:57,http://anderson.name/conrad,,150.96.187.2,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n871,2,84,2017-05-26 11:18:37,http://swaniawskiwillms.info/lavinia,,30.242.253.84,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n872,2,84,2017-02-04 22:59:57,http://gaylord.org/roie_mayer,,96.104.241.149,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n873,2,84,2017-02-19 02:30:49,http://wilderman.com/miller.mayert,,3.215.19.95,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n874,2,84,2017-04-05 21:27:21,http://cain.com/furman_fay,,96.104.241.149,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n875,2,84,2017-04-19 20:38:10,http://bruen.co/devon,,178.167.248.96,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n876,2,84,2017-04-18 10:44:19,http://paucek.info/helen_grant,,166.214.34.203,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n877,2,84,2017-03-14 23:18:30,http://walsh.io/jesus.mohr,,164.78.106.242,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n878,2,84,2017-01-28 03:32:57,http://zboncak.biz/broderick,,168.72.54.45,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n879,2,84,2017-01-22 00:30:58,http://champlin.info/yadira,,166.214.34.203,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n880,2,84,2017-05-17 11:10:52,http://schroeder.io/dale,,231.215.221.2,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n881,2,85,2017-04-17 08:55:42,http://kihn.info/jana,,61.103.181.167,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n882,2,85,2017-03-14 18:50:44,http://haley.biz/ignacio.emard,,19.87.198.44,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n883,2,85,2017-04-20 23:10:22,http://ebert.info/elenora.swift,,107.41.113.247,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n884,2,86,2017-05-29 21:56:21,http://heelvolkman.info/favian,0.0000000000,84.19.249.202,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n885,2,86,2017-05-26 03:18:06,http://hintzhammes.com/meta,0.0000000000,34.122.77.249,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n886,2,86,2017-06-09 07:47:00,http://faybailey.com/roslyn_walsh,0.0000000000,253.77.104.48,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n887,2,86,2017-04-20 13:22:24,http://johnsonmarvin.org/caie.blick,0.0000000000,173.212.122.73,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n888,2,86,2017-02-13 22:01:05,http://ernser.biz/fernando_braun,0.0000000000,196.8.150.24,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n889,2,86,2017-02-07 11:43:23,http://schinner.co/weston,0.0000000000,198.121.124.151,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n890,2,86,2017-04-14 05:50:39,http://mckenzieparisian.name/bill,0.0000000000,10.159.57.209,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n891,2,86,2017-03-03 18:10:14,http://sipes.com/lyric,0.0000000000,184.120.220.219,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n892,2,86,2017-02-21 21:16:44,http://donnelly.org/german,0.0000000000,181.136.101.166,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n893,2,86,2017-02-14 00:28:35,http://strackelangosh.name/marge.oconner,0.0000000000,80.198.145.118,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n3984,6,370,2017-03-21 23:16:09,http://kautzer.info/chelsea,,88.160.122.184,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n3985,6,370,2017-03-04 02:30:02,http://prohaska.org/ruthe,,7.130.55.106,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n3986,6,370,2017-05-26 19:21:57,http://weinatherman.biz/mikayla,,241.150.61.44,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n3987,6,370,2016-12-21 20:12:31,http://schmidt.io/delia,,174.144.133.13,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n3988,6,370,2017-02-17 03:12:45,http://lebsack.name/rubye.thompson,,231.7.250.179,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n3989,6,370,2017-03-15 16:44:31,http://toy.net/koby,,83.95.104.249,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n3990,6,370,2016-12-24 23:54:30,http://harber.info/earnestine_armstrong,,195.103.234.224,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n3991,6,371,2017-04-23 15:01:51,http://parisian.com/boyd.donnelly,,137.107.180.153,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n3992,6,371,2016-12-24 12:27:23,http://hayesreichel.info/keira.kohler,,137.107.180.153,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n3993,6,371,2017-05-17 09:49:54,http://abshire.co/shea_hermiston,,42.252.157.93,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n3994,6,371,2017-01-16 00:27:34,http://hudson.co/sallie,,143.236.24.183,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n3995,6,371,2017-06-03 07:51:05,http://hahn.name/lonie,,119.7.226.11,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n3996,6,371,2017-05-30 21:13:27,http://rutherford.name/rafael_oreilly,,119.7.226.11,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n3997,6,371,2017-04-16 12:51:05,http://ratkekozey.info/annabell,,66.134.190.138,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n3998,6,371,2017-05-31 17:03:49,http://dickens.net/manuel,,167.223.38.128,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n3999,6,371,2017-04-23 23:21:45,http://littleeichmann.com/eda.quitzon,,4.72.34.163,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n4000,6,371,2017-02-12 03:39:13,http://mcglynn.co/reina,,170.201.248.109,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n4001,6,371,2017-05-16 16:47:59,http://lubowitz.io/carlotta,,4.72.34.163,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n4002,6,371,2017-05-12 06:47:51,http://boganspinka.name/haan_lindgren,,66.134.190.138,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n4003,6,371,2017-03-20 04:37:12,http://turcottebotsford.co/garland,,208.43.216.250,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n4004,6,371,2017-04-08 15:23:16,http://heidenreich.biz/cornell,,39.136.242.86,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n4005,6,371,2016-12-21 19:54:51,http://keeling.org/durward.bashirian,,77.168.254.23,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n4006,6,371,2017-05-29 19:09:19,http://hansen.org/antonette,,241.141.229.81,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n4007,6,371,2017-04-14 15:36:19,http://durgan.name/rafaela_tremblay,,26.16.22.152,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n4008,6,371,2017-05-26 06:38:07,http://gutmannzemlak.io/joana.cummerata,,141.180.240.169,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n4009,6,372,2017-01-15 04:56:43,http://prosacco.com/elvie.schiller,,254.48.59.33,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n4010,6,372,2017-03-25 15:13:04,http://okeefe.net/brielle,,116.37.229.156,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n4011,6,372,2017-04-08 18:53:50,http://deckow.info/kristina.corwin,,119.25.96.133,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n4012,6,372,2016-12-17 07:15:17,http://murrayreinger.com/eileen,,34.223.92.68,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n4013,6,372,2017-01-05 04:56:40,http://schamberger.info/nora,,57.85.4.220,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n4014,6,372,2017-01-22 13:15:40,http://moriettethiel.info/erik,,103.28.116.22,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n4015,6,372,2017-02-15 05:53:19,http://mcclure.biz/savanah_christiansen,,103.7.149.107,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n4016,6,372,2017-05-09 04:33:52,http://bruen.com/emile,,140.112.133.106,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n4017,6,372,2017-04-26 18:02:19,http://walkerboehm.net/vernon_wisoky,,48.77.211.133,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n4018,6,372,2017-05-03 12:01:52,http://toyrutherford.co/jaydon_huel,,88.60.28.181,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n4019,6,372,2016-12-20 03:29:45,http://cole.biz/kaleigh.okuneva,,236.211.33.136,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n4020,6,372,2017-02-09 06:46:15,http://vonrueden.biz/elmore,,28.141.136.35,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n4021,6,372,2016-12-18 07:01:57,http://torphydouglas.name/bridget,,254.48.59.33,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n4022,6,373,2017-03-10 06:32:18,http://rutherfordlubowitz.biz/nash,,61.219.209.200,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n4023,6,373,2017-06-09 14:18:51,http://krajcik.net/kyra,,161.10.210.85,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n4024,6,373,2017-01-03 02:32:36,http://zemlakflatley.com/levi,,253.134.126.231,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n4025,6,373,2017-01-14 08:56:39,http://larkin.name/jaylan,,222.216.220.76,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n4026,6,373,2017-04-14 13:42:17,http://sporer.com/jordy,,74.160.71.71,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n4027,6,373,2017-03-01 04:32:10,http://stromanokuneva.io/myrtice,,177.53.93.40,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n4028,6,373,2017-05-17 22:57:49,http://schaden.biz/clay_kirlin,,202.225.174.118,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n4029,6,373,2017-02-16 08:16:54,http://crist.info/cecilia_flatley,,86.254.205.75,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n4030,6,373,2017-01-16 12:03:11,http://kub.info/bernard.ledner,,193.26.213.185,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n4031,6,373,2017-04-12 07:17:13,http://feil.info/vincenza,,158.9.61.87,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n4032,6,373,2017-01-15 08:36:34,http://herzog.info/hosea,,222.216.220.76,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n4033,6,373,2017-03-26 16:44:44,http://fadelchristiansen.com/austen,,225.145.155.239,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n4034,6,373,2017-03-11 02:06:29,http://crooks.name/maynard,,78.45.164.164,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n4035,6,373,2017-02-28 06:44:52,http://gottlieblittel.net/pamela,,159.75.33.117,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n4036,6,373,2016-12-15 20:47:14,http://kertzmannlangworth.org/delfina,,98.179.220.253,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n4037,6,374,2017-05-30 10:25:24,http://willmskoelpin.io/stefanie,,50.146.250.117,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n4038,6,374,2017-03-12 12:02:48,http://nikolaus.biz/jarvis.miller,,190.249.246.60,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n4039,6,374,2017-02-21 15:06:04,http://johnston.com/sincere_denesik,,112.122.48.61,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n894,2,86,2017-02-28 06:49:31,http://beerweinat.biz/gerard,0.0000000000,82.245.199.148,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n895,2,87,2016-12-19 12:01:49,http://bartell.biz/carey,0.0000000000,168.37.120.85,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n896,2,87,2017-04-15 05:35:56,http://boscocorwin.co/janice,0.0000000000,211.241.95.191,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n897,2,87,2017-05-16 10:38:22,http://millerwatsica.net/william_hermann,0.0000000000,130.66.186.222,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n898,2,87,2017-06-02 01:00:34,http://terry.name/enrico.mcglynn,0.0000000000,238.183.14.155,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n899,2,87,2017-03-10 11:57:45,http://senger.com/jaqueline,0.0000000000,22.238.207.105,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n900,2,87,2017-03-08 22:44:53,http://satterfieldondricka.info/joey.murray,0.0000000000,211.241.95.191,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n901,2,87,2017-06-10 08:12:15,http://kris.org/rickey,0.0000000000,179.85.80.137,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n902,2,87,2016-12-31 12:00:01,http://kihn.com/genesis,0.0000000000,10.139.233.123,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n903,2,87,2017-06-06 20:11:12,http://walsh.co/troy,0.0000000000,70.164.94.149,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n904,2,87,2017-02-09 15:23:07,http://rogahn.org/deshawn_thiel,0.0000000000,174.144.164.52,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n905,2,87,2017-05-25 11:15:54,http://schuppe.name/chelsey,0.0000000000,174.144.164.52,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n906,2,88,2017-03-07 05:56:29,http://ferry.biz/lucinda_waters,2.0000000000,89.159.225.34,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n907,2,88,2017-02-12 19:45:28,http://bergnaummoore.com/quinn,3.0000000000,26.167.49.106,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n908,2,88,2017-01-14 12:35:21,http://upton.info/gerda.moore,3.0000000000,86.187.55.244,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n909,2,88,2017-02-18 18:09:55,http://collierschinner.biz/opal.douglas,2.0000000000,239.56.63.78,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n910,2,88,2017-01-04 12:07:09,http://morarkuphal.org/joy,3.0000000000,138.2.175.22,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n911,2,88,2017-01-15 00:50:19,http://nitzsche.info/nora.bode,3.0000000000,186.223.134.157,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n912,2,88,2017-04-04 09:10:27,http://cruickshankspinka.org/johnpaul,3.0000000000,236.23.214.136,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n913,2,88,2017-04-11 12:07:34,http://orn.co/reymundo_reichert,1.0000000000,155.244.57.179,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n914,2,88,2017-01-07 11:28:57,http://grahamstark.name/jazmyn,2.0000000000,239.56.63.78,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n915,2,88,2017-02-19 20:55:19,http://smithamschultz.org/santino.schimmel,3.0000000000,189.232.98.188,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n916,2,88,2017-01-21 19:58:11,http://kunde.info/kadin,3.0000000000,200.74.236.8,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n917,2,88,2017-02-02 02:13:41,http://johnson.info/chanelle.von,1.0000000000,254.194.189.12,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n918,2,88,2017-01-18 08:08:14,http://weinat.net/luz_kris,1.0000000000,214.250.168.89,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n919,2,88,2017-03-23 03:15:44,http://kozeyhills.co/bryce.kihn,3.0000000000,19.204.208.228,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n920,2,88,2017-05-16 19:27:41,http://stamm.net/skye,2.0000000000,144.8.239.4,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n921,2,88,2017-05-09 18:41:19,http://grimes.co/alfredo_doyle,1.0000000000,186.223.134.157,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n922,2,88,2017-01-27 10:01:53,http://lebsack.io/roscoe_schamberger,2.0000000000,214.250.168.89,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n923,2,88,2017-03-27 03:08:29,http://murray.biz/antone,1.0000000000,166.54.154.216,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n924,2,89,2017-02-19 01:13:20,http://robelfritsch.io/zachary_cremin,1.0000000000,170.93.42.95,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n925,2,89,2017-01-15 21:16:55,http://mcdermott.name/dora_hickle,2.0000000000,26.246.150.114,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n926,2,89,2016-12-26 06:59:26,http://gorczanyhauck.io/yvonne,1.0000000000,53.241.25.155,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n927,2,89,2017-05-26 14:32:49,http://reichel.info/jerrod_casper,2.0000000000,118.18.39.87,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n928,2,89,2017-02-03 00:54:16,http://bogan.co/eldon,1.0000000000,115.242.87.74,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n929,2,89,2017-02-23 17:11:19,http://franecki.biz/jeanie.towne,0.0000000000,115.71.51.194,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n930,2,89,2017-03-03 14:55:35,http://watsica.io/bernadine.pfeffer,1.0000000000,8.111.204.89,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n931,2,89,2017-01-07 16:32:00,http://monahanberge.net/filomena_ward,1.0000000000,148.94.83.48,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n932,2,89,2017-02-18 20:59:54,http://ziemann.co/daren,2.0000000000,184.221.126.98,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n933,2,89,2017-02-12 21:15:21,http://durgan.name/kylee,1.0000000000,182.130.134.114,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n934,2,89,2017-03-10 14:52:58,http://lind.io/shaun,0.0000000000,8.111.204.89,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n935,2,89,2017-04-21 13:06:05,http://little.org/javier,2.0000000000,139.47.157.59,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n936,2,89,2017-03-16 13:01:47,http://jaskolski.org/sabryna_gottlieb,2.0000000000,254.186.94.37,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n937,2,89,2017-01-20 20:22:09,http://crist.info/jasmin,0.0000000000,47.208.33.91,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n938,2,89,2017-05-23 01:39:38,http://kemmer.org/zachariah,2.0000000000,186.196.104.128,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n939,2,89,2017-01-10 21:15:02,http://lebsack.info/minnie,1.0000000000,17.124.90.231,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n940,2,89,2017-01-20 02:31:58,http://quigley.com/major.hilll,2.0000000000,231.49.65.15,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n941,2,89,2017-06-08 21:24:01,http://armstrong.info/halie,2.0000000000,186.217.227.50,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n942,2,89,2017-03-08 11:36:52,http://macejkovicprice.org/dion,2.0000000000,139.47.157.59,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n943,2,89,2017-02-08 13:14:59,http://spencer.net/terrance,2.0000000000,148.94.83.48,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n944,2,90,2017-04-18 19:46:28,http://johnsonrogahn.co/adolphus_harvey,1.0000000000,167.254.172.198,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n945,2,90,2017-02-02 15:18:19,http://collins.co/brenda,1.0000000000,164.152.196.138,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n4040,6,374,2017-05-12 17:34:50,http://wiza.net/alfreda_jaskolski,,242.58.193.148,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n4041,6,374,2017-04-03 17:21:53,http://kiehnvonrueden.info/precious_keeling,,74.75.235.156,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n4042,6,374,2017-04-16 22:21:48,http://roberts.info/casandra_ryan,,147.89.104.121,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n4043,6,374,2017-02-13 05:49:03,http://gerholdhammes.com/berta_predovic,,65.177.90.215,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n4044,6,374,2017-04-20 23:12:22,http://goldner.com/mohammed,,176.228.97.200,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n4045,6,374,2017-06-03 06:54:08,http://botsford.co/bettie,,251.144.75.232,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n4046,6,374,2017-02-02 05:21:03,http://herzog.name/jan,,50.146.250.117,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n4047,6,374,2017-01-05 15:23:04,http://dachhalvorson.org/gina,,177.62.14.78,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n4048,6,374,2017-04-24 09:01:13,http://litteltoy.io/okey.herman,,112.122.48.61,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n4049,6,374,2017-05-06 20:54:02,http://marvin.com/michaela,,6.38.134.150,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n4050,6,374,2017-04-05 19:30:54,http://schumm.co/golda_wehner,,178.104.32.117,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n4051,6,374,2016-12-18 23:17:27,http://greenweinat.biz/aimee_mitchell,,119.107.173.207,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n4052,6,374,2017-05-06 11:34:05,http://kirlin.co/june,,13.143.254.38,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n4053,6,374,2017-05-06 11:05:50,http://pollich.io/otilia,,125.213.150.214,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n4054,6,374,2017-03-25 09:20:08,http://hartmann.io/jermain_rosenbaum,,147.89.104.121,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n4055,6,375,2017-02-23 23:35:17,http://rutherfordabshire.co/walter_heathcote,,252.54.210.190,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n4056,6,375,2017-01-28 17:57:11,http://predovic.com/odie.strosin,,45.195.177.173,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n4057,6,375,2017-04-24 20:55:58,http://kulasgusikowski.io/jennings.lueilwitz,,22.230.157.37,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n4058,6,375,2017-04-26 18:47:16,http://sawayn.co/gretchen,,107.235.56.88,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n4059,6,375,2017-02-18 11:58:02,http://swift.co/serena,,25.64.179.226,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n4060,6,375,2017-01-18 04:07:46,http://heaneywolff.name/aleandra,,153.151.84.207,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n4061,6,375,2017-01-20 09:28:54,http://kris.com/makenna,,208.175.62.97,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n4062,6,375,2017-03-28 09:53:29,http://wunsch.biz/milton_kub,,67.92.181.179,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n4063,6,375,2016-12-23 09:02:03,http://mertz.io/maybelle.botsford,,121.48.239.26,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n4064,6,375,2017-02-15 19:30:36,http://kirlin.co/milton,,47.11.11.244,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n4065,6,375,2017-02-15 20:53:11,http://fay.org/destin_turcotte,,206.164.246.143,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n4066,6,375,2017-05-07 18:58:39,http://streich.name/freeman.adams,,78.48.161.217,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n4067,6,375,2016-12-29 10:06:29,http://cormierkshlerin.net/ethel,,208.175.62.97,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n4068,6,375,2017-04-10 11:26:11,http://oreillystamm.biz/makayla.volkman,,78.48.161.217,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n4069,6,375,2017-03-30 05:28:28,http://boyergreenholt.name/tad,,121.226.95.251,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n4070,6,375,2017-02-16 03:34:27,http://bradtke.io/romaine.considine,,25.64.179.226,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n4071,6,375,2017-02-04 18:00:08,http://paucek.info/geovanni,,208.175.62.97,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n4072,6,375,2017-04-16 04:29:25,http://blanda.net/laisha,,22.230.157.37,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n4073,6,375,2017-01-09 13:02:34,http://wolff.co/urban,,244.188.36.115,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n4074,6,376,2017-05-29 02:00:13,http://feilzboncak.biz/antonia.eichmann,,205.37.58.23,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n4075,6,376,2016-12-19 00:31:40,http://roob.com/eliza,,215.131.208.181,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n4076,6,376,2017-02-07 07:17:57,http://sporer.info/destinee.dare,,164.52.141.199,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n4077,6,376,2017-03-11 05:41:16,http://hoeger.com/elbert.cain,,87.57.214.97,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n4078,6,377,2016-12-19 12:31:25,http://bartell.net/abagail,1.0000000000,72.192.24.163,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n4079,6,377,2017-01-03 05:13:55,http://mitchellgorczany.com/dallas.hermann,1.0000000000,76.178.6.134,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n4080,6,377,2017-05-19 16:12:24,http://wunsch.info/montana,0.0000000000,240.149.116.106,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n4081,6,378,2017-03-29 08:53:17,http://jacobson.info/shyann_white,0.0000000000,253.120.147.129,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n4082,6,378,2017-03-30 00:03:24,http://murazik.name/kaya.waelchi,0.0000000000,55.252.95.214,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n4083,6,378,2017-03-20 07:11:01,http://corkery.net/lisa.lehner,0.0000000000,91.239.5.247,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n4084,6,378,2017-01-01 00:01:14,http://lockmanterry.com/jamarcus.ernser,0.0000000000,205.162.28.167,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n4085,6,378,2017-01-12 03:58:11,http://vonbeer.info/christelle,0.0000000000,128.218.66.233,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n4086,6,378,2017-06-03 13:33:02,http://leannonbernhard.biz/annabelle,1.0000000000,196.180.19.159,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n4087,6,378,2017-02-21 22:28:34,http://ziemann.io/adaline,0.0000000000,135.142.50.101,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n4088,6,379,2017-05-27 00:11:25,http://hackett.net/isai,1.0000000000,102.214.68.89,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n4089,6,379,2016-12-28 16:24:44,http://cronaconn.io/frieda_schulist,3.0000000000,81.159.9.49,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n4090,6,379,2017-02-20 01:07:39,http://hirthe.name/jarod,3.0000000000,79.191.16.63,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n4091,6,379,2017-01-25 22:43:53,http://parisianfeil.biz/marilie,1.0000000000,202.141.228.78,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n4092,6,379,2017-04-19 16:54:50,http://kihn.net/stanton,2.0000000000,171.47.14.181,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n4093,6,379,2016-12-14 17:20:55,http://green.com/rosie,2.0000000000,193.169.41.213,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n946,2,90,2017-04-13 18:05:16,http://hane.co/ernie.little,1.0000000000,4.70.159.55,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n947,2,90,2017-05-13 03:51:09,http://armstrong.name/hailey_denesik,0.0000000000,4.70.159.55,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n948,2,90,2017-04-27 13:05:34,http://nolan.io/liza,3.0000000000,134.209.179.253,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n949,2,90,2017-05-18 04:39:36,http://barton.com/jane.medhurst,1.0000000000,3.179.179.118,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n950,2,90,2017-03-29 11:38:44,http://pollich.co/josh,2.0000000000,152.33.71.251,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n951,2,90,2017-05-10 23:50:25,http://kuhn.biz/eleanore,0.0000000000,207.157.213.208,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n952,2,90,2017-05-16 04:34:37,http://gutkowski.net/christiana,2.0000000000,109.44.141.196,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n953,2,90,2017-01-21 14:28:59,http://darefahey.net/candido_bashirian,1.0000000000,131.253.170.169,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n954,2,90,2017-04-17 13:08:21,http://bradtkeprohaska.biz/tracy,2.0000000000,117.151.9.61,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n955,2,90,2017-04-04 01:51:22,http://flatley.io/howell_shanahan,0.0000000000,152.33.71.251,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n956,2,90,2017-04-21 01:29:04,http://pagac.org/johann_goyette,2.0000000000,101.127.239.213,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n957,2,90,2017-03-08 20:01:17,http://damorewolff.io/beau,2.0000000000,129.66.68.4,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n958,2,90,2016-12-23 18:33:09,http://heaneyhudson.org/jan.jaskolski,0.0000000000,3.179.179.118,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n959,2,91,2017-03-28 22:07:49,http://gibson.co/savannah_batz,3.0000000000,193.19.158.18,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n960,2,91,2017-05-20 10:14:36,http://mckenzie.name/kristian,3.0000000000,70.44.115.250,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n961,2,91,2017-03-06 07:28:39,http://hammes.net/gregoria,0.0000000000,2.83.218.113,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n962,2,91,2016-12-24 17:45:05,http://goldner.name/elya,0.0000000000,19.163.88.229,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n963,2,91,2017-01-15 15:20:39,http://mertzschowalter.net/vernie,0.0000000000,44.233.246.114,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n964,2,91,2017-05-25 15:20:46,http://price.name/vincenzo,2.0000000000,165.135.115.15,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n965,2,92,2017-05-13 12:02:14,http://pouros.biz/floy,3.0000000000,248.58.23.41,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n966,2,92,2016-12-17 10:38:59,http://nader.name/jalen,2.0000000000,79.137.214.68,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n967,2,92,2017-03-06 04:30:40,http://sipeslebsack.org/jaclyn,1.0000000000,12.29.12.37,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n968,2,92,2017-04-11 00:28:42,http://littlegraham.info/craig,0.0000000000,46.159.139.120,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n969,2,92,2017-01-12 20:41:13,http://gorczanysatterfield.com/marisa,1.0000000000,97.22.10.241,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n970,2,92,2017-03-11 06:09:09,http://lindgrenmarquardt.info/roscoe_gutkowski,1.0000000000,46.159.139.120,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n971,2,92,2017-01-17 21:17:23,http://veum.org/cecilia_mann,0.0000000000,185.186.207.161,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n972,2,92,2017-02-05 20:01:04,http://bruenfriesen.info/trenton.sanford,2.0000000000,10.148.166.114,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n973,2,92,2017-04-22 13:06:30,http://lueilwitz.org/guillermo.yost,1.0000000000,145.154.91.130,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n974,2,92,2016-12-31 20:13:54,http://macejkovic.org/alex,2.0000000000,246.17.41.5,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n975,2,92,2017-04-02 01:38:45,http://kowelch.org/keshaun,2.0000000000,10.70.18.244,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n976,2,92,2017-03-08 05:31:35,http://batz.org/delphine_predovic,2.0000000000,85.119.156.149,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n977,2,92,2017-06-12 05:59:34,http://braun.net/silas,2.0000000000,82.216.199.5,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n978,2,93,2016-12-16 21:44:04,http://mann.net/naomi.collins,3.0000000000,200.163.123.109,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n979,2,93,2016-12-15 07:33:20,http://hegmannhermiston.net/nyah,2.0000000000,70.79.35.203,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n980,2,93,2017-03-24 11:35:31,http://turcotte.io/quincy,0.0000000000,98.128.220.245,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n981,2,93,2017-06-09 12:21:27,http://schamberger.name/kenneth_fadel,1.0000000000,170.172.239.109,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n982,2,93,2017-05-14 22:32:04,http://jaskolski.biz/myron,1.0000000000,200.163.123.109,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n983,2,93,2017-04-28 19:13:29,http://wilkinson.org/dylan_sawayn,0.0000000000,14.113.90.40,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n984,2,93,2017-03-20 01:57:45,http://carroll.info/ocie,1.0000000000,168.50.33.115,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n985,2,93,2017-04-30 03:20:08,http://dach.co/raymond,0.0000000000,179.105.210.164,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n986,2,93,2017-05-16 20:18:08,http://jasthermiston.io/chyna,3.0000000000,44.79.36.39,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n987,2,93,2017-06-14 01:06:36,http://wisoky.com/izaiah.schmeler,1.0000000000,213.186.189.228,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n988,2,93,2017-03-27 13:50:33,http://olson.org/litzy,1.0000000000,129.139.57.128,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n989,2,93,2016-12-21 00:10:39,http://durgankaulke.com/chance,0.0000000000,20.68.196.75,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n990,2,93,2016-12-16 12:26:55,http://schroeder.biz/kacey,3.0000000000,20.68.196.75,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n991,2,93,2017-05-17 07:08:44,http://tromp.name/hyman.rolfson,1.0000000000,168.50.33.115,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n992,2,93,2017-05-25 07:53:30,http://wymanspinka.info/fred,0.0000000000,70.181.167.176,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n993,2,93,2017-04-09 12:09:52,http://yundthauck.com/brad_bergstrom,3.0000000000,170.172.239.109,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n994,2,93,2017-03-21 00:17:15,http://huel.name/terry.mclaughlin,2.0000000000,83.251.124.232,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n995,2,93,2017-04-10 20:30:35,http://collins.io/wava.rohan,0.0000000000,142.183.176.156,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n996,2,93,2017-01-31 20:07:17,http://ondrickawalker.biz/florine,2.0000000000,177.142.16.21,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n997,2,93,2017-03-16 05:10:02,http://klingstiedemann.org/shirley,1.0000000000,235.65.183.253,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n998,2,94,2017-05-16 05:58:40,http://bernhardtreutel.info/haie_lynch,0.0000000000,166.251.207.11,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n4094,6,379,2017-01-04 05:59:30,http://moen.net/jarrell,3.0000000000,193.169.41.213,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n4095,6,379,2017-06-10 14:27:57,http://toy.net/winnifred,0.0000000000,121.198.196.112,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n4096,6,379,2017-03-24 22:13:07,http://rohan.io/lea,0.0000000000,89.192.216.14,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n4097,6,379,2017-05-29 21:34:33,http://ward.org/rolando,2.0000000000,171.126.139.159,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n4098,6,379,2017-01-31 07:06:52,http://kuhn.net/sigurd,0.0000000000,136.104.82.93,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n4099,6,379,2017-05-09 17:34:58,http://shanahantoy.name/delaney.graham,0.0000000000,18.5.228.92,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n4100,6,379,2017-05-06 19:04:26,http://kub.com/may.lang,2.0000000000,89.192.216.14,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n4101,6,380,2017-05-17 02:06:29,http://howell.net/alena,1.0000000000,12.22.82.105,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n4102,6,380,2017-03-20 14:39:10,http://graham.co/carolina,1.0000000000,59.198.139.140,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n4103,6,380,2017-04-11 06:51:28,http://champlin.name/gaylord.goyette,2.0000000000,25.212.195.241,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n4104,6,380,2017-04-15 21:25:50,http://schimmel.info/robyn,1.0000000000,170.179.9.21,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n4105,6,380,2017-05-21 05:09:38,http://hudson.net/anjali_price,2.0000000000,101.162.98.37,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n4106,6,380,2017-02-03 17:23:09,http://paucekdubuque.biz/rhianna_von,0.0000000000,78.176.22.238,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n4107,6,380,2017-05-16 15:50:25,http://mosciskiweinat.name/orrin_hagenes,1.0000000000,123.150.112.102,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n4108,6,380,2017-05-12 06:21:37,http://batz.info/george,0.0000000000,12.22.82.105,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n4109,6,380,2017-06-12 02:49:19,http://mueller.co/krista,1.0000000000,161.36.66.187,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n4110,6,380,2017-06-06 07:40:06,http://beer.net/helen.hilll,2.0000000000,78.176.22.238,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n4111,6,380,2017-04-28 02:14:49,http://emmerich.info/braden_parisian,0.0000000000,73.137.177.250,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n4112,6,381,2017-06-09 14:39:31,http://carter.net/sigmund,0.0000000000,56.141.152.134,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n4113,6,381,2017-04-10 13:32:01,http://mertzdeckow.io/alyson,1.0000000000,140.180.254.223,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4114,6,381,2017-04-05 15:32:02,http://kogleichner.biz/esta,1.0000000000,27.148.146.69,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n4115,6,381,2017-04-03 10:47:00,http://botsford.org/armando.hartmann,1.0000000000,240.208.231.59,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n4116,6,382,2017-01-31 03:06:19,http://trantow.info/dale.eichmann,0.0000000000,49.230.127.71,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n4117,6,382,2017-03-29 01:05:56,http://bauch.org/jeanette_schulist,0.0000000000,245.110.112.195,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n4118,6,382,2017-05-09 05:43:36,http://tremblaymclaughlin.biz/cathrine,0.0000000000,248.113.46.35,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n4119,6,382,2017-04-17 01:52:35,http://gaylord.org/dorothea.ohara,0.0000000000,64.206.121.83,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n4120,6,382,2017-06-06 11:57:52,http://quigley.co/austin.miller,0.0000000000,190.21.60.193,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n4121,6,382,2017-01-22 23:15:41,http://naderbruen.org/nigel,0.0000000000,104.167.146.220,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n4122,6,382,2017-05-16 22:53:59,http://parisian.net/lia,0.0000000000,49.230.127.71,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n4123,6,382,2016-12-31 02:45:32,http://ziemannprice.net/macey,0.0000000000,169.220.219.73,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n4124,6,382,2017-02-22 21:03:58,http://west.net/ramona.ortiz,0.0000000000,129.161.192.114,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n4125,6,382,2016-12-13 06:13:08,http://mitchell.co/chanelle,0.0000000000,157.247.38.20,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n4126,6,382,2017-02-21 06:58:45,http://bosco.name/gaetano_schultz,0.0000000000,33.149.77.90,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n4127,6,382,2017-01-18 09:40:07,http://wyman.org/lupe_franecki,0.0000000000,98.207.157.23,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n4128,6,382,2017-05-02 15:42:16,http://tremblayblick.com/karolann_crona,0.0000000000,116.34.46.213,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n4129,6,382,2017-02-24 09:55:37,http://wilderman.io/lula.heathcote,0.0000000000,169.220.219.73,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n4130,6,382,2017-03-02 03:38:00,http://pfefferlarson.name/chris,0.0000000000,57.225.199.201,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n4131,6,383,2016-12-21 13:55:42,http://sengerthiel.io/heath,2.0000000000,178.118.219.145,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n4132,6,383,2017-02-14 00:37:26,http://cremin.info/brain,0.0000000000,123.119.221.223,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n4133,6,383,2017-05-26 20:09:17,http://okon.org/valerie_shanahan,2.0000000000,60.237.195.179,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n4134,6,383,2017-01-25 02:09:40,http://mosciski.co/rozella_heidenreich,2.0000000000,211.253.58.232,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n4135,6,383,2017-02-23 19:13:23,http://batz.name/aryanna_howe,0.0000000000,72.151.24.161,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n4136,6,383,2017-02-14 20:40:34,http://langworthkilback.net/samantha,1.0000000000,185.192.212.152,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n4137,6,383,2017-04-05 15:56:06,http://homenickwiegand.info/lauryn_moore,1.0000000000,121.202.167.152,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n4138,6,384,2017-04-30 19:14:42,http://gerhold.co/gina,1.0000000000,109.78.66.42,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n4139,6,384,2017-02-25 20:44:07,http://kovacek.name/bradly,2.0000000000,226.51.41.7,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n4140,6,384,2017-01-15 21:16:13,http://streich.io/victoria.carter,1.0000000000,163.21.123.25,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n4141,6,384,2017-04-16 11:21:45,http://ohara.io/alberto_klocko,0.0000000000,33.245.212.28,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n4142,6,384,2017-02-03 17:19:32,http://pfannerstill.info/brady.kshlerin,2.0000000000,240.57.217.217,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n4143,6,384,2017-05-24 20:30:48,http://kuhlman.co/mertie,1.0000000000,225.176.247.61,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n4144,6,384,2017-03-04 09:32:36,http://dach.org/mitchell,1.0000000000,99.128.151.235,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n4145,6,384,2017-02-01 19:40:31,http://bechtelar.biz/kiara.rutherford,1.0000000000,99.128.151.235,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n999,2,94,2017-01-21 01:15:46,http://nicolas.com/markus.jenkins,0.0000000000,51.4.148.50,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n1000,2,94,2017-03-18 13:46:11,http://spinka.com/josue,0.0000000000,40.8.58.181,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n1001,2,94,2017-03-09 17:25:41,http://zboncak.co/macey,2.0000000000,57.134.198.183,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n1002,2,94,2017-04-20 12:47:53,http://schiller.info/chyna_bosco,0.0000000000,107.78.226.182,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n1003,2,94,2017-04-19 21:13:52,http://jakubowski.com/maverick_gleichner,2.0000000000,40.8.58.181,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n1004,2,94,2017-05-31 11:31:13,http://hellerhackett.com/roslyn,2.0000000000,254.190.88.174,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n1005,2,94,2017-06-03 17:54:00,http://kuphal.com/genesis,2.0000000000,202.247.88.24,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n1006,2,94,2017-01-16 12:22:52,http://littel.org/judd_hyatt,1.0000000000,40.8.58.181,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n1007,2,94,2017-04-20 20:54:21,http://windlerbartoletti.co/mose.schulist,1.0000000000,52.155.164.169,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n1008,2,94,2017-01-13 18:54:24,http://marks.com/fritz_koepp,2.0000000000,68.134.168.157,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n1009,2,94,2017-02-16 21:42:08,http://murray.io/kim.durgan,3.0000000000,202.247.88.24,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n1010,2,94,2017-01-21 15:51:03,http://erdman.net/moriah.macejkovic,0.0000000000,254.190.88.174,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n1011,2,94,2017-04-07 04:06:42,http://bergstromhowe.com/vivianne.mertz,1.0000000000,145.77.134.115,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n1012,2,94,2017-06-01 15:20:25,http://brekkebeahan.org/addie.eichmann,2.0000000000,211.154.216.172,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n1013,2,95,2016-12-16 02:53:51,http://trantowbatz.name/enola_reichert,0.0000000000,10.231.45.227,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n1014,2,95,2017-01-28 05:36:37,http://paucek.io/irving,1.0000000000,10.231.45.227,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n1015,2,95,2017-05-12 02:33:46,http://lesch.name/armand,2.0000000000,248.127.77.252,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n1016,2,95,2017-04-30 21:04:35,http://rosenbaum.info/obie.rowe,2.0000000000,239.39.29.102,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n1017,2,96,2017-04-21 10:53:52,http://purdy.com/hilton_upton,0.0000000000,94.182.43.254,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n1018,2,96,2017-04-12 10:27:01,http://naderprosacco.biz/oleta,0.0000000000,96.247.237.123,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n1019,2,96,2017-05-14 10:12:26,http://daugherty.name/mortimer,0.0000000000,62.234.207.134,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n1020,2,96,2017-04-04 16:18:33,http://davisdooley.org/javonte_bosco,0.0000000000,197.115.29.10,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n1021,2,96,2017-04-13 20:33:51,http://damore.info/noe.conn,0.0000000000,40.223.243.94,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n1022,2,96,2016-12-21 22:59:59,http://cummerata.org/berneice,0.0000000000,254.156.115.206,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n1023,2,96,2017-04-29 22:35:12,http://jasthane.net/mathias_larson,0.0000000000,154.73.250.103,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n1024,2,96,2017-05-25 22:09:37,http://okonbuckridge.co/cleora_hahn,0.0000000000,197.115.29.10,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n1025,2,96,2017-05-05 01:47:33,http://wyman.biz/aleen.botsford,0.0000000000,242.72.205.242,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n1026,2,96,2017-01-30 21:53:15,http://hermiston.name/peter,0.0000000000,134.249.7.66,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n1027,2,96,2017-05-11 13:01:12,http://hegmann.co/terrance,0.0000000000,5.33.127.247,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n1028,2,96,2017-05-10 20:45:59,http://lang.io/muhammad.hudson,0.0000000000,242.72.205.242,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n1029,2,96,2017-02-15 18:25:20,http://hermiston.org/parker,0.0000000000,197.115.29.10,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n1030,2,96,2017-04-02 15:58:05,http://carter.com/cody_runolfsdottir,0.0000000000,153.117.199.153,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n1031,2,96,2017-04-17 03:06:39,http://waelchi.net/carlee.mayert,0.0000000000,79.196.131.229,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n1032,2,96,2017-02-19 05:30:26,http://grimesabernathy.io/sim,0.0000000000,154.73.250.103,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n1033,2,96,2017-05-24 13:21:22,http://bergnaumkunde.co/sibyl.mclaughlin,0.0000000000,206.184.148.87,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n1034,2,96,2017-03-25 09:06:49,http://lebsackrodriguez.com/dwight.lueilwitz,0.0000000000,90.191.183.94,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n1035,2,96,2017-06-06 01:08:44,http://skiles.co/zelda_flatley,0.0000000000,90.191.183.94,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n1036,2,96,2017-05-22 05:20:38,http://gottlieb.net/ian,0.0000000000,49.138.113.173,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n1037,2,97,2017-02-14 20:30:00,http://corwin.io/erich,1.0000000000,99.19.55.183,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n1038,2,97,2017-01-15 03:24:34,http://stoltenbergwhite.io/tito,2.0000000000,20.92.83.154,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n1039,2,97,2016-12-27 12:29:06,http://hirthe.org/sister.moen,1.0000000000,27.75.199.186,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n1040,2,97,2017-04-13 08:54:51,http://framithompson.info/dallin.breitenberg,3.0000000000,114.110.242.4,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n1041,2,97,2017-03-10 09:38:53,http://schmittfahey.com/elliott,3.0000000000,95.157.203.178,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n1042,2,97,2017-04-18 13:26:00,http://halvorson.org/georgiana,1.0000000000,236.127.180.143,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n1043,2,97,2017-03-18 00:36:03,http://nikolaus.net/vincenzo.lebsack,2.0000000000,185.90.196.249,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n1044,2,97,2017-04-17 13:18:43,http://runolfsdottir.io/rhiannon.klein,3.0000000000,188.53.17.73,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n1045,2,97,2017-03-03 06:55:01,http://goldner.net/bennett_friesen,3.0000000000,65.188.48.174,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n1046,2,97,2017-02-04 13:19:55,http://lang.co/dasia.toy,0.0000000000,84.5.239.162,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n1047,2,97,2017-02-11 13:49:02,http://kaulketillman.co/raoul,3.0000000000,185.81.106.202,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n1048,2,98,2016-12-23 18:40:29,http://hills.biz/van,1.0000000000,129.85.129.59,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n1049,2,98,2017-05-30 10:24:35,http://murray.biz/antonina.koch,1.0000000000,165.103.196.6,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n4146,6,384,2017-02-08 13:10:48,http://connelly.com/lilly_bosco,2.0000000000,99.128.151.235,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n4147,6,384,2017-04-28 18:00:10,http://spinka.org/margarette_goldner,1.0000000000,95.111.65.21,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n4148,6,384,2017-01-17 11:15:09,http://stammwalker.biz/diamond,1.0000000000,197.213.155.164,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n4149,6,384,2017-03-05 15:13:44,http://graham.com/ernest,0.0000000000,33.245.212.28,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n4150,6,384,2017-04-17 13:26:03,http://prosaccorenner.co/cicero,1.0000000000,39.92.92.86,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n4151,6,384,2017-02-18 10:03:08,http://trantowstreich.com/tre,0.0000000000,198.151.108.141,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n4152,6,384,2016-12-31 12:20:24,http://medhurst.name/fausto,1.0000000000,113.6.29.235,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n4153,6,384,2017-03-21 15:28:37,http://jakubowski.io/lori,2.0000000000,200.126.218.178,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n4154,6,384,2017-05-01 23:49:58,http://bashirian.com/caandre_bailey,0.0000000000,209.94.18.120,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n4155,6,384,2017-02-26 17:25:50,http://nienow.com/general_bruen,0.0000000000,225.176.247.61,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n4156,6,385,2017-04-09 00:52:08,http://mantefritsch.co/reyna_gislason,2.0000000000,244.139.245.7,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n4157,6,385,2017-03-05 02:30:35,http://koeppcarroll.org/millie_kertzmann,1.0000000000,123.41.129.83,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n4158,6,385,2017-02-01 20:24:04,http://swiftbatz.net/jerad,3.0000000000,174.252.25.58,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n4159,6,385,2016-12-19 07:44:34,http://gradyoberbrunner.com/marge_deckow,3.0000000000,69.42.192.172,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n4160,6,385,2017-03-05 12:25:45,http://abbottreinger.net/beatrice,3.0000000000,48.123.125.55,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n4161,6,385,2017-03-24 15:17:36,http://krajcik.biz/orion_mills,3.0000000000,153.45.143.73,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n4162,6,385,2017-01-24 08:09:17,http://satterfield.net/terrell.franecki,3.0000000000,169.147.131.98,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n4163,6,385,2017-01-04 23:00:30,http://kshlerin.name/dion,1.0000000000,182.106.131.46,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n4164,6,385,2016-12-31 16:26:49,http://welchheller.name/nash,0.0000000000,224.185.219.216,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n4165,6,385,2017-01-21 21:13:48,http://sanford.name/wilma_beahan,0.0000000000,251.30.154.237,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4166,6,385,2017-04-26 16:37:25,http://schulistnitzsche.net/dasia.stiedemann,3.0000000000,45.136.101.22,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n4167,6,385,2017-03-26 17:25:36,http://schmeler.name/judson_gleason,1.0000000000,114.187.113.223,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n4168,6,385,2017-01-13 02:50:38,http://wilderman.biz/zachariah.lubowitz,2.0000000000,224.185.219.216,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n4169,6,386,2017-03-20 05:31:57,http://keelingkohler.io/estelle_bosco,2.0000000000,208.7.175.140,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n4170,6,386,2017-03-30 00:34:19,http://jacobson.net/stella.shanahan,0.0000000000,218.26.147.62,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n4171,6,386,2017-05-16 00:01:12,http://heidenreich.info/myrtle,2.0000000000,9.248.14.79,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n4172,6,386,2017-06-04 01:20:33,http://eichmann.io/coby,1.0000000000,248.212.236.179,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n4173,6,386,2017-02-06 19:34:23,http://cruickshanktoy.org/ceasar,0.0000000000,236.248.15.206,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n4174,6,386,2016-12-26 17:46:59,http://gleichnerblick.biz/cletus.halvorson,1.0000000000,109.35.170.241,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n4175,6,386,2017-01-15 07:27:48,http://harberhowell.com/lindsay,2.0000000000,230.75.42.18,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n4176,6,386,2016-12-29 06:45:26,http://corwin.biz/katelynn_barrows,0.0000000000,36.94.140.169,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n4177,6,386,2017-06-10 22:37:02,http://schoen.info/ezra.johnson,0.0000000000,160.230.207.86,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n4178,6,386,2017-04-03 18:16:02,http://treutelmcclure.biz/berneice,1.0000000000,9.248.14.79,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n4179,6,386,2017-02-23 00:47:40,http://gleichnerlang.io/derek,0.0000000000,80.138.102.19,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n4180,6,386,2017-06-11 16:10:31,http://conn.biz/keeley.hills,1.0000000000,93.14.40.175,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n4181,6,386,2017-03-03 18:36:11,http://hermiston.net/cristopher,1.0000000000,72.212.227.151,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n4182,6,386,2017-01-27 03:46:55,http://ruecker.name/myrna_keeling,0.0000000000,200.106.190.35,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n4183,6,386,2017-03-24 15:08:44,http://erdman.net/hellen_kihn,2.0000000000,177.214.16.189,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n4184,6,386,2017-05-11 03:47:02,http://feil.net/eulalia,0.0000000000,93.14.40.175,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n4185,6,386,2017-03-14 01:06:57,http://miller.org/franco,0.0000000000,36.94.140.169,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n4186,6,387,2017-01-26 17:49:03,http://ohara.biz/trycia,1.0000000000,147.172.244.207,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n4187,6,387,2017-02-22 21:28:25,http://crona.io/al.streich,0.0000000000,77.160.80.35,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n4188,6,387,2016-12-16 03:48:00,http://gleasonklein.com/lilian,1.0000000000,200.113.80.239,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n4189,6,387,2016-12-27 03:19:39,http://klein.info/matilda_runolfon,0.0000000000,200.113.80.239,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n4190,6,388,2017-05-20 00:57:21,http://considine.org/adella.kiehn,1.0000000000,195.143.4.171,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n4191,6,388,2016-12-18 18:20:52,http://greenfelderwitting.net/ayden,1.0000000000,206.25.216.127,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n4192,6,388,2017-01-05 13:11:21,http://kuhlmanjast.org/bryon,1.0000000000,40.213.147.154,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n4193,6,388,2017-05-31 09:59:51,http://feil.org/meta_wiegand,0.0000000000,84.13.211.172,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n4194,6,388,2017-05-02 20:39:58,http://kutch.org/rhianna,0.0000000000,90.95.82.158,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n4195,6,388,2017-05-17 05:18:13,http://ondrickagaylord.info/lenore,1.0000000000,7.57.169.183,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n4196,6,388,2017-05-30 05:22:20,http://lueilwitz.name/aurelia,1.0000000000,163.64.5.4,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n1050,2,98,2017-06-07 20:51:55,http://kautzer.io/johnnie,1.0000000000,63.155.185.207,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n1051,2,98,2017-05-25 21:13:37,http://rogahnbarrows.info/theresa_huel,1.0000000000,28.123.59.27,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n1052,2,98,2017-01-15 04:47:39,http://okuneva.info/caesar,0.0000000000,56.71.107.209,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n1053,2,98,2017-03-14 05:33:09,http://ankunding.name/henri.daugherty,0.0000000000,30.140.43.59,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n1054,2,99,2017-02-10 22:45:10,http://windler.com/ebba_price,0.0000000000,74.43.60.150,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n1055,2,99,2017-02-04 01:37:29,http://ortiz.co/dolores,0.0000000000,240.145.6.250,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n1056,2,99,2017-03-29 14:39:32,http://lesch.biz/rosalind,1.0000000000,103.160.232.163,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n1057,2,99,2017-05-19 22:42:46,http://buckridge.co/victoria,1.0000000000,219.245.130.191,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n1058,2,99,2017-04-13 03:05:59,http://wisozk.net/angelica,3.0000000000,56.93.157.16,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n1059,2,99,2017-04-24 08:24:21,http://rau.net/jedediah_metz,3.0000000000,175.88.125.50,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n1060,2,99,2017-01-08 04:45:58,http://steubertorphy.com/terry,0.0000000000,88.194.65.245,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n1061,2,99,2017-03-28 20:46:34,http://beermcdermott.org/anna_olson,1.0000000000,225.98.152.231,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n1062,2,99,2017-01-09 15:34:05,http://fisher.info/luz,3.0000000000,127.248.237.241,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n1063,2,100,2017-03-29 22:07:21,http://cainrodriguez.com/te_schowalter,1.0000000000,236.222.115.118,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n1064,2,100,2017-03-19 05:08:36,http://shields.name/agnes,3.0000000000,125.64.185.157,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n1065,2,100,2017-05-03 16:07:11,http://runolfon.name/karine,1.0000000000,81.125.184.120,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n1066,2,100,2017-05-04 08:52:32,http://larkin.name/johathan_gleason,1.0000000000,119.80.218.10,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n1067,2,100,2017-02-23 01:21:30,http://braunturcotte.net/jude.haag,0.0000000000,74.131.229.207,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n1068,2,100,2017-02-18 23:45:39,http://moriettewisozk.com/raoul,3.0000000000,93.70.150.79,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n1069,2,100,2017-05-22 22:36:15,http://kubbrekke.info/conrad_balistreri,2.0000000000,172.248.142.213,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n1070,2,100,2016-12-20 10:12:41,http://tillman.com/sherwood,2.0000000000,222.187.21.159,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n1071,2,100,2017-03-27 16:01:17,http://bernhard.info/presley,3.0000000000,81.125.184.120,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n1072,2,100,2017-05-14 16:03:46,http://pouros.co/mikayla.johnston,3.0000000000,125.64.185.157,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n1073,2,100,2017-01-23 04:57:15,http://baumbachmoriette.io/mireille,0.0000000000,49.35.135.225,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n1074,2,100,2017-06-03 10:55:08,http://dachlittle.io/kaleigh,1.0000000000,70.236.185.228,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n1075,2,100,2016-12-31 09:21:38,http://smitham.info/colin,0.0000000000,72.139.147.193,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n1076,2,100,2016-12-31 11:43:58,http://rowemann.co/abigale,2.0000000000,93.70.150.79,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n1077,2,100,2017-06-13 01:52:39,http://yost.name/wilbert,2.0000000000,84.156.245.33,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n1078,2,101,2017-02-27 20:05:02,http://schimmel.net/mike,1.0000000000,100.243.5.194,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n1079,2,101,2017-01-14 02:24:26,http://kuhlmanlittle.io/emiliano_schneider,0.0000000000,138.159.38.233,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n1080,2,101,2017-03-03 13:46:27,http://schroeder.com/anjali_beahan,1.0000000000,37.163.208.78,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n1081,2,101,2017-02-13 08:40:13,http://bashirianemard.net/alayna_lueilwitz,1.0000000000,219.20.93.50,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n1082,2,101,2016-12-20 22:12:28,http://mayer.name/mattie.bashirian,1.0000000000,100.4.49.192,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n1083,2,101,2017-02-14 10:54:09,http://monahanmraz.co/brianne,0.0000000000,219.20.93.50,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n1084,2,101,2017-01-10 10:43:12,http://morar.net/ova,1.0000000000,100.4.49.192,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n1085,2,101,2017-06-02 21:11:37,http://hagenes.biz/christopher_stark,0.0000000000,206.66.105.66,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n1086,2,101,2016-12-21 09:44:49,http://hagenes.biz/mario.blanda,1.0000000000,205.150.12.159,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n1087,2,101,2017-01-03 23:23:00,http://greenfelder.net/mariano_donnelly,0.0000000000,219.20.93.50,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n1088,2,101,2017-04-05 09:07:00,http://leuschkecrist.net/damaris,0.0000000000,206.66.105.66,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n1089,2,101,2017-04-27 04:52:19,http://rogahn.info/kenyon,0.0000000000,78.76.126.64,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n1090,2,101,2017-06-09 11:26:53,http://fay.net/giuseppe.volkman,0.0000000000,48.92.122.126,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n1091,2,101,2017-01-09 17:54:49,http://herzogrobel.org/leanna.dare,1.0000000000,96.233.23.219,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n1092,2,101,2017-01-07 01:42:13,http://gislasongraham.name/leie,0.0000000000,100.243.5.194,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n1093,2,101,2017-01-30 16:18:28,http://franeckitreutel.info/dee_gaylord,1.0000000000,37.23.89.112,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n1094,2,101,2017-03-15 14:03:51,http://hilll.net/kyleigh,1.0000000000,103.34.227.221,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n1095,2,101,2017-01-16 12:20:52,http://ankunding.biz/rolando,1.0000000000,120.156.225.18,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n1096,2,101,2017-05-18 08:15:57,http://mertz.biz/lyda_runolfsdottir,0.0000000000,206.66.105.66,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n1097,2,101,2017-05-31 19:36:44,http://kertzmann.co/harmon.roberts,1.0000000000,63.158.79.67,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n1098,2,102,2017-06-08 00:28:18,http://jacobs.org/brisa,1.0000000000,100.227.45.71,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n1099,2,102,2017-01-05 21:01:04,http://schultz.info/rudolph_gerlach,1.0000000000,24.55.203.124,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n1100,2,102,2017-02-09 16:18:02,http://lang.co/ottis_gibson,2.0000000000,7.248.109.244,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n1101,2,102,2017-02-05 10:03:17,http://rosenbaum.name/miles,1.0000000000,104.201.28.103,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n4197,6,388,2017-01-14 07:33:49,http://jacobs.co/loraine,0.0000000000,195.143.4.171,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n4198,6,388,2017-06-12 10:57:40,http://goldner.co/meda.rogahn,0.0000000000,206.25.216.127,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n4199,6,388,2016-12-29 10:39:08,http://kihn.org/jaime,1.0000000000,248.60.119.157,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n4200,6,388,2017-01-03 22:57:27,http://nitzsche.io/gaston.wisoky,1.0000000000,230.157.31.242,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n4201,6,388,2017-03-21 02:41:38,http://hamillkeeling.org/abbie,0.0000000000,70.246.158.56,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n4202,6,389,2017-02-18 13:48:31,http://sauerwiza.biz/shyann,2.0000000000,136.64.234.72,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n4203,6,389,2017-04-20 18:02:34,http://langworthspinka.io/adan,1.0000000000,193.229.240.88,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4204,6,390,2017-04-15 06:27:24,http://hammes.co/alf_strosin,1.0000000000,5.35.236.33,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n4205,6,390,2017-05-08 00:06:35,http://litteldouglas.org/cory,0.0000000000,106.74.105.63,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n4206,6,390,2017-04-03 01:50:10,http://klein.net/darius.schmeler,0.0000000000,104.77.146.219,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n4207,6,390,2017-04-28 09:51:37,http://schroeder.biz/josephine_lindgren,1.0000000000,51.143.219.104,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n4208,6,390,2017-01-07 15:46:34,http://robel.info/austyn,0.0000000000,112.139.212.193,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n4209,6,390,2017-05-20 11:02:22,http://cruickshankmayer.name/daniela_glover,1.0000000000,206.215.21.55,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n4210,6,390,2017-05-18 11:19:15,http://mclaughlin.info/laury.doyle,1.0000000000,104.77.146.219,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n4211,6,390,2017-02-06 16:08:47,http://prosaccoabbott.net/kailee.konopelski,0.0000000000,15.194.237.229,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n4212,6,390,2017-01-09 21:58:14,http://mertz.org/peggie,0.0000000000,21.147.74.226,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n4213,6,390,2016-12-17 16:51:18,http://raynor.biz/orie,0.0000000000,106.74.105.63,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n4214,6,391,2017-05-08 05:42:52,http://rogahn.net/erik,1.0000000000,186.130.175.236,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n4215,6,391,2017-05-18 12:45:16,http://ullrichhintz.org/lilla,0.0000000000,87.83.247.248,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n4216,6,391,2017-03-16 10:11:42,http://lakin.com/katrina_beer,0.0000000000,10.225.225.143,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n4217,6,391,2017-04-10 17:43:05,http://hagenes.io/jermain.parisian,1.0000000000,239.177.253.149,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n4218,6,391,2017-01-25 10:02:25,http://farrell.info/jeremie,1.0000000000,207.163.49.122,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n4219,6,391,2017-03-06 05:41:02,http://bernierlakin.net/nikolas,1.0000000000,16.221.132.104,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n4220,6,391,2017-06-10 18:50:55,http://schneider.io/litzy_mosciski,0.0000000000,183.86.222.89,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n4221,6,391,2017-04-02 19:06:14,http://keeling.com/cyrus_goyette,1.0000000000,103.250.56.171,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n4222,6,391,2017-01-24 00:31:13,http://rowe.biz/anna.smith,0.0000000000,84.122.214.100,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n4223,6,392,2017-04-01 19:01:24,http://ledner.io/katrina.schamberger,0.0000000000,82.211.213.81,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n4224,6,392,2017-01-09 01:45:57,http://greenfelder.net/aron,0.0000000000,33.228.50.45,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n4225,6,392,2017-01-13 21:33:56,http://dubuquereilly.name/betsy,0.0000000000,245.25.18.252,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n4226,6,392,2017-02-05 06:13:09,http://glovercollier.biz/jaylin.keeling,1.0000000000,189.153.130.133,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n4227,6,393,2017-05-30 04:20:36,http://brekke.com/jeyca,0.0000000000,147.169.88.2,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n4228,6,393,2017-05-13 21:30:42,http://erdman.co/lavinia_hansen,0.0000000000,120.41.13.234,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n4229,6,393,2017-01-31 12:57:12,http://bergnaum.name/carli.kautzer,0.0000000000,91.218.14.56,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n4230,6,393,2017-03-20 22:32:30,http://nitzschekoch.net/dewayne.klein,0.0000000000,147.7.193.84,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n4231,6,394,2017-05-30 19:34:54,http://towne.name/destany_rau,0.0000000000,18.114.128.147,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n4232,6,394,2016-12-25 13:32:13,http://bartoletti.org/kattie.stoltenberg,2.0000000000,41.165.243.185,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n4233,6,394,2017-05-13 14:12:32,http://gislason.org/bernadine,3.0000000000,138.208.198.208,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n4234,6,395,2017-02-19 11:53:41,http://beahanklocko.io/emelie_dach,1.0000000000,96.178.215.81,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n4235,6,395,2017-02-10 21:05:21,http://nicolas.info/malachi,0.0000000000,148.64.216.180,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n4236,6,395,2016-12-17 13:28:31,http://johnston.co/tianna,2.0000000000,82.31.178.240,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n4237,6,395,2017-02-15 05:59:48,http://padberg.name/eliezer_little,2.0000000000,153.74.48.31,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n4238,6,395,2017-04-25 00:32:03,http://reinger.com/reese,1.0000000000,153.74.48.31,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n4239,6,395,2017-03-28 03:13:55,http://armstrong.name/orlando,1.0000000000,28.2.146.208,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n4240,6,395,2016-12-21 07:11:36,http://zieme.info/breana,2.0000000000,181.184.36.101,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n4241,6,395,2017-06-08 07:22:40,http://labadie.co/irma,1.0000000000,148.64.216.180,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n4242,6,395,2016-12-17 07:09:58,http://larsonohara.io/natasha,1.0000000000,112.146.203.125,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n4243,6,395,2017-03-19 10:02:46,http://mckenzieyundt.biz/irma,2.0000000000,38.52.84.15,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n4244,6,395,2017-03-19 13:18:15,http://nitzsche.io/gianni,2.0000000000,79.253.79.88,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n4245,6,395,2017-05-12 06:48:37,http://jacobsonwiza.info/cheyenne.skiles,2.0000000000,13.169.212.31,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n4246,6,395,2017-04-19 09:57:02,http://pouros.org/leila.oberbrunner,1.0000000000,33.23.248.224,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4247,6,395,2017-05-11 11:36:58,http://williamson.info/susanna_conn,1.0000000000,224.39.40.247,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n4248,6,395,2017-05-04 14:32:05,http://mraz.co/cyrus.schinner,2.0000000000,153.74.48.31,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n1102,2,102,2017-03-01 03:04:24,http://zboncak.net/dovie_pfannerstill,3.0000000000,43.177.73.69,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n1103,2,102,2016-12-24 22:05:23,http://kuhlman.net/josh,0.0000000000,79.42.153.123,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n1104,2,102,2017-02-22 13:51:03,http://cummerata.com/herbert.crona,2.0000000000,246.246.42.192,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n1105,2,102,2017-06-10 19:43:27,http://bartell.info/delilah.waters,1.0000000000,143.212.133.154,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n1106,2,102,2017-04-18 15:02:36,http://pfannerstill.net/tristin,0.0000000000,55.229.122.65,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n1107,2,102,2016-12-17 12:21:28,http://mayertbruen.biz/deion,3.0000000000,104.201.28.103,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n1108,2,102,2016-12-30 21:57:55,http://cormier.biz/christelle_purdy,3.0000000000,134.103.51.208,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n1109,2,102,2017-02-08 09:23:43,http://schmidt.info/elise,3.0000000000,248.62.120.57,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n1110,2,102,2017-01-11 22:28:03,http://nolan.com/joyce,0.0000000000,233.161.35.210,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n1111,2,102,2017-05-12 03:44:58,http://kuphalhirthe.net/flavie,0.0000000000,208.138.71.208,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n1112,2,102,2017-03-03 09:39:29,http://cartwright.biz/jayme.brakus,2.0000000000,163.19.227.163,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n1113,2,103,2017-01-10 10:30:07,http://veumgleichner.io/laurence.lowe,2.0000000000,175.74.236.203,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1114,2,103,2017-05-14 08:38:23,http://rowemarquardt.biz/wilmer.murray,0.0000000000,246.53.192.144,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n1115,2,103,2017-04-10 09:13:56,http://harvey.io/marley_gutkowski,0.0000000000,5.119.110.42,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n1116,2,103,2017-04-27 06:04:56,http://quigley.com/jett.grimes,0.0000000000,66.151.162.227,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n1117,2,103,2017-05-30 04:22:08,http://treutel.name/elna,1.0000000000,36.13.50.152,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n1118,2,103,2017-05-13 06:36:41,http://raynormurphy.co/baylee,1.0000000000,211.161.21.56,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n1119,2,103,2017-06-06 06:26:42,http://wisoky.biz/monty,0.0000000000,211.161.21.56,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n1120,2,103,2017-06-05 21:34:59,http://conn.info/freida_bartell,2.0000000000,246.148.55.15,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n1121,2,103,2017-04-13 01:53:18,http://reichertchristiansen.name/aglae_roberts,2.0000000000,231.199.31.88,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n1122,2,103,2017-04-26 16:40:35,http://yundtswift.co/janiya,1.0000000000,35.81.86.95,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n1123,2,103,2017-02-05 22:53:50,http://goldner.org/garnett_wehner,1.0000000000,222.206.217.252,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n1124,2,103,2017-03-03 17:28:00,http://kuhic.name/arvid,1.0000000000,109.184.67.235,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n1125,2,103,2017-03-15 09:28:02,http://nicolas.info/lue.thompson,1.0000000000,231.199.31.88,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n1126,2,103,2017-04-28 03:09:08,http://mcclure.info/walter,2.0000000000,96.228.118.116,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n1127,2,104,2017-01-29 11:15:39,http://abshire.com/jerrell_kautzer,1.0000000000,22.15.63.248,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n1128,2,104,2017-03-22 08:32:30,http://deckow.info/angelo_howell,1.0000000000,248.156.251.90,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n1129,2,104,2017-05-27 23:34:49,http://schultz.name/alejandra_nolan,1.0000000000,242.61.123.84,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n1130,2,104,2017-05-15 11:20:42,http://tillmanbarton.biz/nolan.ratke,2.0000000000,128.50.91.51,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n1131,2,104,2016-12-22 11:58:25,http://shanahanshields.biz/isidro,0.0000000000,128.196.54.78,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n1132,2,104,2017-03-08 22:33:30,http://rice.biz/royal,0.0000000000,18.226.164.119,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n1133,2,104,2017-06-08 11:59:19,http://schulist.biz/catharine_kreiger,1.0000000000,132.73.4.193,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n1134,2,104,2017-01-27 09:40:24,http://king.net/amani.altenwerth,0.0000000000,248.156.251.90,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n1135,2,104,2017-04-19 05:30:05,http://marquardt.io/dorthy_lang,2.0000000000,180.62.248.147,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n1136,2,104,2016-12-24 08:03:40,http://schumm.biz/deondre.brekke,2.0000000000,180.62.248.147,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n1137,2,104,2017-05-30 06:45:55,http://waltergreenfelder.io/iliana,1.0000000000,178.210.92.74,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n1138,2,105,2017-01-21 16:10:16,http://powlowskimueller.name/ole,3.0000000000,38.22.118.117,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n1139,2,105,2017-01-19 09:57:09,http://kuhlman.com/vada_maggio,0.0000000000,9.105.174.39,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n1140,2,105,2017-05-31 17:42:13,http://lindgren.org/vivian.davis,1.0000000000,98.89.173.231,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n1141,2,105,2017-04-27 22:49:51,http://ornlittle.com/ilene.gutkowski,0.0000000000,178.65.108.16,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n1142,2,105,2017-01-23 21:54:23,http://heidenreich.info/rashawn.gaylord,0.0000000000,170.50.24.250,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n1143,2,105,2017-05-10 17:21:56,http://macgyver.name/luisa,3.0000000000,16.44.10.195,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n1144,2,105,2017-03-11 22:25:05,http://schultz.co/terry.heel,0.0000000000,178.65.108.16,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n1145,2,105,2017-06-10 13:28:55,http://walter.com/neoma_fay,1.0000000000,165.111.80.205,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n1146,2,105,2017-01-01 17:32:13,http://gorczanylabadie.biz/winnifred,0.0000000000,98.89.173.231,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n1147,2,105,2017-03-10 17:52:25,http://hammesrosenbaum.org/josephine.rohan,2.0000000000,100.56.36.195,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n1148,2,105,2017-01-18 17:40:12,http://collier.net/glennie,3.0000000000,73.82.203.73,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n1149,2,105,2017-01-24 02:01:34,http://jenkins.io/stewart,0.0000000000,9.242.164.166,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n1150,2,105,2016-12-30 23:00:26,http://beatty.io/laria,0.0000000000,178.65.108.16,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n1151,2,105,2017-06-10 10:09:15,http://gleason.co/maddison,0.0000000000,67.94.88.217,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n1152,2,105,2017-01-24 12:16:45,http://emmerich.net/berry,1.0000000000,16.55.132.222,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n4249,6,396,2016-12-25 16:44:47,http://denesik.co/marcelina,,231.196.211.121,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n4250,6,396,2017-05-30 02:07:03,http://deckowcrist.info/eloy,,96.115.195.74,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n4251,6,396,2017-04-30 18:17:35,http://wolf.com/shany_nitzsche,,230.100.237.89,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n4252,6,396,2017-01-29 23:16:04,http://beerraynor.info/alexandre.baumbach,,39.72.93.43,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n4253,6,396,2017-02-19 13:05:52,http://welch.biz/merl.kohler,,147.114.234.122,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n4254,6,396,2017-05-18 18:48:59,http://kemmernicolas.io/dagmar,,204.166.123.67,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n4255,6,396,2017-02-14 00:36:56,http://macejkovic.org/abel,,103.109.207.52,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n4256,6,396,2017-03-20 20:22:48,http://crona.co/karson,,176.131.191.108,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n4257,6,396,2017-02-15 02:48:03,http://beattykautzer.biz/leta,,179.117.95.192,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n4258,6,396,2017-01-26 20:18:31,http://purdy.io/kelvin.lindgren,,179.117.95.192,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n4259,6,396,2017-02-02 00:34:34,http://ratke.net/cole.konopelski,,59.133.252.234,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n4260,6,396,2017-04-30 08:05:04,http://schaden.co/benjamin,,20.95.83.137,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n4261,6,396,2017-03-02 19:49:35,http://schoenbernier.org/kali_metz,,229.97.54.190,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n4262,6,396,2017-01-24 02:24:12,http://bailey.com/annetta,,40.211.177.154,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n4263,6,396,2017-06-09 23:52:57,http://streichhirthe.com/chauncey,,57.186.74.80,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n4264,6,397,2017-01-11 01:48:50,http://gusikowski.co/breanne.ondricka,,230.135.47.177,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n4265,6,397,2017-05-29 08:46:00,http://quigley.com/rosalee,,176.105.100.179,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n4266,6,397,2016-12-20 17:59:34,http://kuhic.info/heaven,,86.58.244.67,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n4267,6,397,2017-03-23 03:39:52,http://nolan.io/hester_wuckert,,60.58.254.196,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n4268,6,397,2017-02-23 20:26:44,http://hackettconn.io/nicholas,,64.225.23.210,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n4269,6,398,2017-01-22 01:40:01,http://bahringer.com/christine,,115.133.247.160,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n4270,6,398,2017-02-17 11:30:41,http://westfay.com/lillian_walsh,,121.133.166.147,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n4271,6,398,2016-12-31 08:42:46,http://maggio.org/edmond,,35.208.58.239,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n4272,6,398,2017-05-30 03:31:58,http://legros.biz/virginie,,11.231.231.34,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n4273,6,398,2017-04-02 00:45:23,http://friesen.net/mafalda,,37.181.122.228,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n4274,6,398,2017-06-07 06:38:57,http://hellerfeil.io/wilber_bosco,,152.37.35.78,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n4275,6,398,2017-02-04 16:18:12,http://gerhold.info/erika,,83.166.55.203,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n4276,6,398,2017-03-12 22:20:08,http://ankunding.name/ramon,,189.152.140.26,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n4277,6,398,2017-01-21 05:17:52,http://schulist.io/florencio_hettinger,,35.208.58.239,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n4278,6,398,2017-04-27 10:18:36,http://lowe.net/genesis,,152.37.35.78,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n4279,6,398,2017-04-04 05:58:38,http://doyle.info/annetta.johnston,,44.41.223.150,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n4280,6,398,2017-03-02 04:28:34,http://braun.org/te,,150.149.142.169,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n4281,6,398,2017-04-07 20:31:56,http://zemlakrice.name/miller,,61.172.225.104,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n4282,6,398,2017-03-18 01:55:42,http://hansenjacobson.co/caie.miller,,145.168.3.191,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n4283,6,398,2017-03-18 16:31:28,http://macgyver.info/clare,,83.166.55.203,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n4284,6,398,2017-01-18 23:44:31,http://kling.io/gwen,,37.181.122.228,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n4285,6,398,2017-03-28 07:02:21,http://gutmann.io/alverta,,123.8.250.153,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n4286,6,398,2017-06-07 21:50:53,http://prosacco.org/esther_lueilwitz,,79.118.183.112,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n4287,6,399,2017-03-11 15:42:31,http://littel.org/zora,,145.175.242.151,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n4288,6,399,2016-12-26 10:11:57,http://krajcikwunsch.com/zula_torp,,117.164.100.63,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n4289,6,399,2017-04-04 06:45:13,http://sanforddaniel.info/aisha,,225.91.79.75,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n4290,6,399,2017-05-11 02:46:01,http://jacobson.co/lyla,,136.209.169.75,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n4291,6,399,2017-02-13 11:54:57,http://macejkovic.biz/natalia.strosin,,136.209.169.75,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n4292,6,399,2017-02-12 17:47:56,http://reichertcrona.name/kendra_dibbert,,127.86.101.186,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n4293,6,399,2017-01-30 04:58:35,http://cormier.biz/elisha,,85.18.83.58,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n4294,6,399,2017-03-19 12:15:43,http://handhansen.net/leilani,,145.175.242.151,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n4295,6,399,2017-01-18 19:01:54,http://buckridge.io/sherman_quigley,,85.18.83.58,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n4296,6,399,2017-03-28 23:12:23,http://brekkehowe.info/allan,,238.189.184.40,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n4297,6,400,2017-01-10 01:37:19,http://legrosgorczany.name/grayson,,128.176.35.221,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n4298,6,400,2017-04-07 00:51:57,http://little.biz/ryley,,128.176.35.221,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n4299,6,400,2017-05-23 03:49:32,http://wittingtreutel.info/leonie.crona,,233.112.8.139,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n4300,6,400,2017-01-08 11:35:59,http://kovacek.io/garrick,,129.126.44.67,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n4301,6,400,2017-05-21 14:16:07,http://marquardtconn.info/ola_stehr,,233.112.8.139,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n4302,6,400,2017-03-20 16:51:10,http://erdman.co/maud,,244.59.12.15,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n4303,6,401,2017-04-28 15:19:29,http://marvinauer.org/lily,,184.231.34.196,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n4304,6,401,2016-12-19 16:26:02,http://kuphal.biz/isaias.von,,2.131.189.190,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n1153,2,105,2017-04-22 19:55:42,http://reynolds.net/ola_little,0.0000000000,109.134.234.146,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n1154,2,106,2017-05-11 23:21:11,http://moen.com/elza,1.0000000000,12.46.167.65,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n1155,2,106,2017-01-28 09:27:26,http://pricegulgowski.biz/milan.graham,2.0000000000,206.63.199.140,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1156,2,107,2017-04-26 16:56:04,http://wilkinson.name/thora.smith,2.0000000000,38.34.52.38,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n1157,2,107,2017-02-25 20:40:52,http://schaefer.biz/thora,2.0000000000,109.13.72.71,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n1158,2,107,2017-03-23 21:36:24,http://gutkowski.biz/demetrius.wiegand,0.0000000000,62.153.191.47,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n1159,2,107,2017-02-17 10:31:05,http://bayer.com/hyman,0.0000000000,71.45.115.132,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n1160,2,107,2017-03-15 04:50:54,http://erdman.org/mekhi,0.0000000000,86.250.31.238,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n1161,2,107,2017-01-29 17:19:59,http://cormier.info/norwood,1.0000000000,184.220.73.3,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n1162,2,107,2017-01-23 16:11:32,http://schuster.name/amara,2.0000000000,96.120.136.47,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n1163,2,107,2017-02-09 11:37:36,http://mante.net/maybelle.oconner,0.0000000000,86.250.31.238,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n1164,2,107,2017-01-07 15:30:52,http://cormierstroman.io/kelly.johnson,1.0000000000,189.205.251.150,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n1165,2,107,2017-05-04 02:14:22,http://marksmccullough.com/alexandrine_bogan,1.0000000000,221.198.247.242,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n1166,2,107,2017-01-05 14:30:58,http://wyman.name/ashtyn_olson,2.0000000000,9.238.220.73,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n1167,2,108,2017-02-18 21:04:31,http://yundthegmann.biz/titus_schumm,2.0000000000,233.13.239.67,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n1168,2,109,2017-01-08 16:41:27,http://flatley.net/quinten,0.0000000000,221.33.219.79,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n1169,2,109,2017-01-20 19:23:48,http://pagac.info/cheyanne_heidenreich,0.0000000000,172.202.28.41,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n1170,2,109,2017-06-06 22:35:47,http://klein.biz/general.stiedemann,0.0000000000,15.179.85.126,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n1171,2,109,2017-06-10 09:40:20,http://balistrerireichert.co/theresia,0.0000000000,207.128.15.13,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n1172,2,109,2016-12-22 05:09:45,http://tremblaysauer.co/crystel,0.0000000000,102.171.12.159,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n1173,2,109,2017-05-22 05:53:17,http://batzwalker.io/gerard_walter,0.0000000000,155.158.55.212,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n1174,2,109,2017-04-11 02:33:02,http://feeney.info/pablo_rowe,0.0000000000,123.254.92.198,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n1175,2,109,2016-12-14 07:29:52,http://funk.com/heaven,0.0000000000,221.168.40.20,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n1176,2,109,2017-02-11 05:52:04,http://rau.name/kip,0.0000000000,61.18.136.96,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n1177,2,109,2017-05-29 00:41:00,http://bosco.name/cortez_heaney,0.0000000000,233.147.78.99,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n1178,2,109,2017-06-08 21:34:13,http://okuneva.net/eudora_nolan,0.0000000000,167.124.247.178,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n1179,2,110,2017-03-31 23:49:59,http://graham.biz/magnus,1.0000000000,202.131.110.33,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n1180,2,110,2017-04-17 17:38:39,http://jerde.com/lorenzo.leuschke,0.0000000000,119.91.95.155,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n1181,2,110,2017-05-05 17:37:22,http://zieme.net/erika,0.0000000000,22.110.67.173,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n1182,2,110,2017-04-26 18:01:39,http://yostmayert.co/kathryne.prohaska,0.0000000000,166.108.231.88,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n1183,2,110,2017-03-06 21:34:57,http://legros.biz/christa.stehr,1.0000000000,105.37.107.23,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n1184,2,110,2017-03-28 12:21:26,http://mitchell.info/cordell,1.0000000000,22.110.67.173,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n1185,2,110,2017-04-30 20:17:17,http://oreilly.com/tracey,2.0000000000,166.108.231.88,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n1186,2,111,2017-03-01 05:58:23,http://wintheiser.net/beverly,0.0000000000,120.83.216.245,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n1187,2,111,2017-02-01 08:39:30,http://wiza.co/bernie,0.0000000000,118.78.144.196,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n1188,2,111,2017-01-11 17:34:10,http://legros.net/blair_crist,0.0000000000,152.148.147.191,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n1189,2,111,2017-04-03 17:20:39,http://quitzon.name/pink_herman,0.0000000000,48.214.173.19,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n1190,2,111,2017-03-18 05:55:02,http://haucklarson.io/gabriel,0.0000000000,184.222.170.68,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n1191,2,111,2017-04-06 20:57:32,http://jacobs.name/wellington,0.0000000000,8.105.129.12,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n1192,2,111,2017-02-07 20:13:21,http://hand.org/deion_breitenberg,0.0000000000,219.135.151.147,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n1193,2,111,2017-04-07 16:30:20,http://braunrodriguez.net/iva,0.0000000000,182.60.183.104,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n1194,2,112,2017-06-09 04:48:59,http://leuschke.biz/marcus_murazik,0.0000000000,222.218.217.92,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n1195,2,112,2017-06-03 04:55:58,http://wildermanroob.co/keenan,0.0000000000,191.118.39.136,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n1196,2,112,2017-05-05 11:00:53,http://collins.co/nova_deckow,0.0000000000,180.144.94.38,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n1197,2,112,2017-05-18 16:07:33,http://feest.info/erick,0.0000000000,191.118.39.136,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n1198,2,112,2017-02-27 03:49:40,http://kutch.org/hailey_cormier,0.0000000000,170.135.44.123,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n1199,2,112,2017-04-21 00:29:09,http://durgancruickshank.info/michael.donnelly,0.0000000000,222.218.217.92,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n1200,2,112,2017-05-12 23:41:54,http://kozey.io/adolf.reinger,0.0000000000,230.240.68.145,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n1201,2,112,2017-05-17 02:13:17,http://ortiz.name/carlo,0.0000000000,253.44.209.73,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n1202,2,112,2017-05-23 05:54:40,http://friesen.co/novella,0.0000000000,5.76.3.209,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n1203,2,112,2017-01-10 16:26:10,http://gleichner.com/aniyah_zemlak,0.0000000000,180.144.94.38,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n1204,2,112,2017-04-10 11:29:08,http://effertz.info/korey_powlowski,0.0000000000,106.125.190.162,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n4305,6,401,2017-04-28 22:20:21,http://schillersimonis.io/carli,,5.185.83.189,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n4306,6,401,2017-04-29 13:02:43,http://hudson.io/adah,,113.133.96.131,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n4307,6,401,2017-01-21 16:59:27,http://kemmerhansen.net/jillian.collier,,108.219.66.203,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n4308,6,401,2017-05-17 04:42:40,http://grant.com/beverly,,121.221.29.48,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n4309,6,401,2017-03-30 15:53:06,http://nader.com/stefanie_hegmann,,113.133.96.131,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n4310,6,401,2017-02-11 11:50:18,http://bechtelar.io/martine.fadel,,139.233.234.201,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n4311,6,401,2017-04-06 01:22:28,http://mcdermottkoepp.com/lysanne.kohler,,78.170.75.215,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n4312,6,402,2016-12-23 16:28:28,http://wolfgleason.org/darryl,,9.227.62.196,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n4313,6,402,2017-05-20 06:40:53,http://howellhyatt.io/diego,,95.61.151.30,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n4314,6,402,2017-01-09 21:02:29,http://hackett.io/taryn.trantow,,27.11.66.24,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n4315,6,402,2017-02-21 01:20:49,http://rohan.co/cristal.bradtke,,27.52.87.199,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n4316,6,402,2017-06-06 05:24:26,http://herman.net/helga,,185.89.175.138,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n4317,6,402,2017-02-07 06:25:33,http://gusikowskiklein.biz/loy,,9.227.62.196,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n4318,6,402,2017-02-18 17:07:55,http://starkcollier.com/emmie.lockman,,13.5.15.158,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4319,6,402,2017-01-22 20:45:10,http://purdy.io/hallie.weimann,,117.47.16.6,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n4320,6,402,2017-04-18 18:49:46,http://nikolausgorczany.com/virgil,,136.77.194.227,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n1205,2,112,2017-05-03 11:45:24,http://huels.name/hunter,0.0000000000,41.251.202.168,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n1206,2,112,2017-02-28 22:25:55,http://rowe.net/nya,0.0000000000,180.144.94.38,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n1207,2,112,2017-01-11 18:23:28,http://cruickshank.io/katherine,0.0000000000,59.36.133.199,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n1208,2,112,2017-04-14 01:16:27,http://runte.org/sincere.graham,0.0000000000,81.214.228.105,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n1209,2,112,2017-01-30 11:20:06,http://greenfelderosinski.net/willy,0.0000000000,137.90.241.214,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n1210,2,112,2017-04-28 08:35:15,http://bergstrom.org/terrill_bradtke,0.0000000000,105.246.104.218,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n1211,2,113,2017-05-17 09:59:57,http://pfannerstill.info/kian.runolfon,0.0000000000,216.65.188.184,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n1212,2,113,2017-01-16 22:54:24,http://hegmannnienow.info/kailyn,0.0000000000,91.93.115.91,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n1213,2,114,2017-02-25 17:11:30,http://ricequitzon.co/alicia.considine,1.0000000000,40.122.238.149,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n1214,2,114,2017-03-04 21:16:33,http://osinski.co/lazaro_beier,2.0000000000,122.22.250.105,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n1215,2,114,2017-03-03 14:25:49,http://harveyflatley.co/gunnar.durgan,1.0000000000,4.71.149.103,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n1216,2,114,2017-06-13 22:34:31,http://bergnaumdooley.name/edwina.marvin,0.0000000000,246.64.144.97,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n1217,2,114,2017-03-22 16:04:57,http://wisoky.io/ali_mueller,0.0000000000,145.195.175.116,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n1218,2,114,2017-04-10 02:10:00,http://langworth.net/nelda.kling,1.0000000000,148.84.96.56,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n1219,2,114,2017-02-27 22:36:40,http://schoencarroll.net/dortha_barton,2.0000000000,18.88.187.77,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n1220,2,114,2017-06-02 00:31:45,http://wiegand.net/kennith_schaefer,2.0000000000,128.43.233.144,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n1221,2,114,2017-05-08 10:31:58,http://reillygottlieb.net/vito,1.0000000000,42.120.4.95,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n1222,2,114,2016-12-24 07:50:40,http://stamm.name/mellie_stiedemann,0.0000000000,36.68.69.175,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n1223,2,114,2017-01-14 15:06:47,http://grady.io/lempi_fadel,2.0000000000,148.84.96.56,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n1224,2,115,2017-05-07 12:40:39,http://effertzschultz.io/hobart_morar,0.0000000000,53.95.80.197,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n1225,2,115,2017-04-06 05:12:13,http://hintz.net/krystal_jast,1.0000000000,134.208.69.114,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n1226,2,115,2017-03-17 04:44:11,http://sauerokuneva.info/katelin,1.0000000000,40.204.74.114,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n1227,2,115,2017-05-29 11:28:00,http://johnston.org/oma.carroll,1.0000000000,235.142.203.141,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n1228,2,115,2017-05-13 00:49:51,http://dare.net/clay.kozey,1.0000000000,169.60.138.15,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n1229,2,115,2017-04-08 21:51:52,http://halvorsonrowe.info/chanelle,0.0000000000,237.150.99.156,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n1230,2,116,2016-12-31 21:14:38,http://renner.net/destiney,0.0000000000,195.135.65.212,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n1231,2,116,2017-03-09 22:24:29,http://millshagenes.info/shane,0.0000000000,86.94.88.134,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n1232,2,116,2017-01-26 14:37:55,http://welch.io/donato_weimann,0.0000000000,67.42.118.8,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n1233,2,116,2017-05-02 22:35:44,http://runte.biz/leilani,0.0000000000,32.166.3.201,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n1234,2,116,2017-03-10 22:28:39,http://eichmann.name/charlie.goyette,0.0000000000,64.103.2.236,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n1235,2,117,2017-06-06 07:20:28,http://pouros.info/ambrose_mckenzie,1.0000000000,47.132.145.213,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n1236,2,117,2017-04-11 15:20:26,http://schowalter.info/sigurd_armstrong,0.0000000000,146.243.38.198,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n1237,2,117,2017-04-15 03:28:45,http://macgyverdaniel.io/kacie,0.0000000000,41.176.120.16,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n1238,2,118,2017-02-22 08:43:29,http://thompson.co/adeline_bogisich,0.0000000000,152.254.228.73,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n1239,2,118,2017-06-01 02:08:23,http://becker.com/lilyan_jacobs,3.0000000000,169.115.176.219,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n1240,2,118,2017-05-17 05:34:34,http://simonis.co/carlos.reynolds,0.0000000000,179.32.231.69,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n1241,2,118,2017-04-03 12:24:16,http://gibson.biz/angus.deckow,3.0000000000,78.35.191.111,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n1242,2,118,2017-02-24 13:08:24,http://krajcikcronin.info/eleanora,2.0000000000,114.240.223.133,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n1243,2,118,2017-03-04 20:51:17,http://graham.net/alexandrea.quigley,3.0000000000,152.254.228.73,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n1244,2,118,2017-03-24 07:11:08,http://bogisich.com/nikita.ko,2.0000000000,25.48.154.83,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n1245,2,118,2017-02-05 09:44:56,http://gerholdaufderhar.com/mohammed.lueilwitz,2.0000000000,136.207.184.21,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n1246,2,118,2017-03-27 18:42:12,http://little.co/ashleigh_kilback,3.0000000000,125.12.218.11,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n1247,2,118,2017-05-01 23:27:59,http://marvin.net/della,2.0000000000,228.81.124.158,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n1248,2,118,2016-12-26 22:24:03,http://kovacekbrown.com/durward,2.0000000000,136.51.55.250,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n1249,2,118,2017-03-12 04:45:30,http://hagenes.co/hermann_wisozk,1.0000000000,75.216.205.204,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n1250,2,118,2017-02-08 07:26:02,http://mertz.name/dariana,1.0000000000,152.254.228.73,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n1251,2,118,2017-02-11 10:17:35,http://gusikowskigulgowski.name/bo,1.0000000000,246.5.207.242,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n1252,2,119,2017-05-19 03:29:40,http://terrybeatty.com/madge.hagenes,,156.6.202.29,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n1253,2,119,2017-02-28 06:13:31,http://kunde.com/leie,,5.219.187.130,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n1254,2,120,2017-04-02 23:19:31,http://schroeder.org/aidan.bergstrom,,239.85.56.16,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n1255,2,120,2017-05-29 18:16:26,http://wunschwilderman.name/jazlyn,,168.245.53.4,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n1256,2,120,2017-03-09 16:02:48,http://kaulkecrist.net/maximillia.von,,176.242.68.251,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n1257,2,120,2017-04-10 18:44:57,http://carrolllittle.co/yolanda_nicolas,,206.141.126.157,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n1258,2,120,2017-01-18 11:01:17,http://homenick.net/hilario,,128.92.114.191,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n1259,2,120,2017-02-07 09:02:06,http://ebert.io/cali,,90.140.85.175,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n1260,2,120,2017-06-08 15:13:43,http://goyette.name/rhett_heel,,41.184.169.213,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n1261,2,120,2017-01-23 09:49:56,http://grimes.name/verna.jerde,,130.17.113.22,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n1262,2,121,2017-04-20 00:27:35,http://denesikebert.biz/helmer,,236.22.122.238,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n1263,2,122,2017-04-03 08:35:40,http://ritchie.info/laurel.crooks,,41.237.87.138,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n1264,2,122,2017-04-20 21:23:51,http://mitchell.com/stephania,,93.223.101.131,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n1265,2,122,2016-12-14 07:21:39,http://mante.info/alexandre_stokes,,41.237.87.138,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n1266,2,122,2017-01-31 22:15:33,http://fay.name/lazaro,,113.11.182.230,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n1267,2,122,2017-05-28 16:10:48,http://heathcote.io/mohammed.predovic,,4.196.238.40,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n1268,2,122,2017-03-27 12:32:56,http://bartoletti.org/hoyt.hills,,78.253.211.228,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n1269,2,122,2017-04-14 09:50:25,http://batz.io/andre.christiansen,,207.219.102.3,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n1270,2,122,2017-05-03 11:57:24,http://dicki.net/carmelo_mann,,97.27.152.3,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n1271,2,122,2017-06-08 03:04:00,http://johnsdach.biz/murphy_schowalter,,93.223.101.131,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n1272,2,122,2017-04-29 07:02:20,http://goodwin.net/guadalupe,,207.219.102.3,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n1273,2,122,2017-03-29 08:50:21,http://moorehoppe.name/raymundo,,78.253.211.228,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n1274,2,122,2017-04-03 16:04:57,http://johnsonorn.net/hilbert,,54.102.28.51,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n1275,2,122,2017-03-25 08:50:51,http://heathcote.org/emmett_thiel,,180.92.34.211,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n1276,2,122,2017-03-13 22:55:07,http://bruen.com/joannie,,4.196.238.40,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n1277,2,122,2017-04-02 21:48:47,http://hirthe.biz/joany,,9.153.147.254,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n1278,2,122,2017-03-02 11:12:30,http://brakusberge.org/prince_beahan,,41.237.87.138,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n1279,2,123,2017-04-09 01:08:59,http://mayerdoyle.org/carole,,11.31.30.4,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n1280,2,123,2017-02-08 12:16:02,http://jacobi.io/kelvin.botsford,,38.93.174.201,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n1281,2,123,2017-05-04 21:39:43,http://schowalter.co/taylor.moriette,,115.200.63.80,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n1282,2,123,2017-01-15 03:28:15,http://dibbert.io/walter,,151.71.167.149,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n1283,2,123,2017-02-22 06:38:23,http://brown.name/jace,,194.232.136.118,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n1284,2,123,2017-01-26 18:42:38,http://schulist.info/armando,,52.226.14.230,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n1285,2,123,2017-02-12 06:10:25,http://millshilll.name/katheryn,,116.153.115.35,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n1286,2,123,2017-04-19 01:02:19,http://west.info/brice.zulauf,,184.177.238.72,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n1287,2,123,2017-02-23 04:56:14,http://bosco.org/destiney,,151.71.167.149,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n1288,2,123,2017-05-08 01:28:37,http://boyle.org/garnett_muller,,209.165.149.48,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n1289,2,123,2017-05-15 17:59:53,http://cartwrighttorphy.org/davin_reilly,,63.38.22.15,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n1290,2,123,2017-03-23 12:00:10,http://weimann.name/robert.kertzmann,,4.46.228.211,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n1291,2,123,2017-01-07 01:45:38,http://kinghudson.org/owen,,105.75.128.238,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n1293,2,123,2017-04-15 07:18:36,http://armstrong.info/susanna.dare,,195.245.131.144,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n1294,2,123,2017-02-16 06:37:22,http://bechtelar.name/jabari_emmerich,,209.165.149.48,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n1295,2,123,2017-03-14 06:11:46,http://sipes.io/savanah,,193.132.233.14,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n1296,2,123,2017-06-06 19:13:08,http://thiel.name/felipe.simonis,,225.182.184.149,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n1297,2,123,2017-05-30 10:22:04,http://marquardt.info/mikel,,209.165.149.48,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n1298,2,124,2017-03-11 20:33:03,http://champlin.net/elvera_damore,,166.165.59.167,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n1299,2,124,2017-04-15 15:13:58,http://uptonhilll.org/bobbie,,184.3.202.217,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n1300,2,124,2017-01-15 20:11:21,http://jacobi.info/arlene_ernser,,184.3.202.217,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n1301,2,124,2017-01-05 00:48:34,http://rogahnnader.biz/dawson.waelchi,,252.98.229.152,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n1302,2,124,2017-02-26 04:34:31,http://legros.org/llewellyn,,227.74.211.72,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n1303,2,124,2017-01-09 23:52:52,http://deckow.io/lucio,,203.98.228.115,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n1304,2,124,2017-03-16 07:34:20,http://marquardt.net/rozella.sauer,,192.185.69.234,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n1305,2,124,2017-02-14 14:57:49,http://altenwerth.com/edmond,,66.49.38.129,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n1306,2,124,2017-04-12 23:58:36,http://batz.net/ena_oconnell,,197.40.226.228,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n1307,2,124,2017-03-25 05:44:09,http://bahringer.info/rylee_crona,,88.239.142.245,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n1308,2,124,2017-02-26 12:18:31,http://schinnerconsidine.org/quinton.gulgowski,,53.118.162.128,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n1309,2,124,2017-05-17 23:35:16,http://leannon.net/reese,,239.33.243.139,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n1310,2,124,2017-01-16 06:18:52,http://gutmannstreich.io/oceane,,53.118.162.128,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n1311,2,124,2017-01-09 10:10:24,http://rutherfordblock.info/myrtis,,197.40.226.228,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n1312,2,124,2017-02-24 18:11:14,http://crist.io/miller,,25.98.49.80,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n1313,2,124,2017-02-14 15:11:21,http://westweimann.info/leonardo.leffler,,162.163.247.122,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n1314,2,124,2016-12-16 10:56:25,http://upton.name/retta,,162.163.247.122,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n1315,2,124,2016-12-28 08:42:29,http://crist.co/zita,,100.210.219.233,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n1316,2,124,2017-05-16 14:34:13,http://rogahnlindgren.biz/estevan.koch,,227.74.211.72,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n1317,2,125,2017-02-16 05:35:29,http://mraz.com/tyree,,58.162.43.14,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n1318,2,125,2017-06-05 00:33:03,http://hoegerschinner.info/ava_gorczany,,10.11.165.225,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n1319,2,125,2017-01-15 14:34:26,http://smitham.name/odie,,102.9.170.186,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n1320,2,125,2017-05-04 21:29:06,http://cummerata.net/derek,,102.9.170.186,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n1321,2,125,2017-05-15 15:04:43,http://conroy.net/vladimir,,13.204.94.211,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n1322,2,125,2017-01-15 18:07:40,http://cummings.net/elmira.schmeler,,139.20.184.101,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n1323,2,125,2017-01-01 08:28:09,http://walsh.io/zaria,,159.67.71.59,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n1324,2,125,2017-03-28 20:20:45,http://champlinwalker.net/erling,,29.228.229.96,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n1325,2,125,2016-12-14 04:05:39,http://koch.co/kristopher,,160.253.61.116,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n1326,2,125,2017-01-06 09:41:01,http://colliergoodwin.net/mya,,251.31.125.33,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n1327,2,125,2016-12-25 03:58:36,http://rodriguezskiles.info/valerie,,10.11.165.225,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n1328,2,125,2017-03-29 14:46:14,http://rau.io/lowell,,34.101.113.246,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n1329,2,125,2017-03-13 15:43:17,http://hand.co/casandra,,18.115.126.112,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n1330,2,125,2017-01-08 04:39:11,http://ondrickahuels.name/bert_muller,,160.253.61.116,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n1331,2,125,2017-05-03 22:32:58,http://marvinprice.io/jayme.beer,,189.197.201.142,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n1332,2,125,2017-03-26 08:42:09,http://hills.info/earl_larson,,31.14.152.202,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n1333,2,125,2017-05-22 15:53:12,http://haag.com/amya_conroy,,212.9.109.59,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n1334,2,125,2017-05-21 02:13:51,http://steuber.co/jose,,12.52.177.103,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n1335,2,125,2017-04-14 13:39:34,http://hamill.biz/cydney.kutch,,12.52.177.103,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n1336,2,125,2016-12-30 14:46:51,http://tillman.name/elva,,243.67.151.103,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n"
  },
  {
    "path": "src/test/regress/data/companies.csv",
    "content": "8,Luna Rogahn,https://robohash.org/idfugavero.png?size=300x300&set=set1,2017-06-13 16:42:34.405636,2017-06-13 16:42:34.405636\n1,Dulce Corkery,https://robohash.org/cumqueautquia.png?size=300x300&set=set1,2017-06-13 16:41:52.460952,2017-06-13 16:41:52.460952\n5,Lindsey Durgan,https://robohash.org/sintexfacilis.png?size=300x300&set=set1,2017-06-13 16:42:15.946372,2017-06-13 16:42:15.946372\n4,Ms. Morton Stehr,https://robohash.org/optionamquis.png?size=300x300&set=set1,2017-06-13 16:42:09.712621,2017-06-13 16:42:09.712621\n7,Sienna Collier,https://robohash.org/autemquiamolestiae.png?size=300x300&set=set1,2017-06-13 16:42:28.41381,2017-06-13 16:42:28.41381\n3,Mrs. Nels Jacobi,https://robohash.org/quiapraesentiumeos.png?size=300x300&set=set1,2017-06-13 16:42:03.620678,2017-06-13 16:42:03.620678\n6,Maximo Hegmann,https://robohash.org/aliquamreprehenderitdolor.png?size=300x300&set=set1,2017-06-13 16:42:22.35326,2017-06-13 16:42:22.35326\n2,Jamir Larkin,https://robohash.org/teneturiureducimus.png?size=300x300&set=set1,2017-06-13 16:41:57.057374,2017-06-13 16:41:57.057374\n"
  },
  {
    "path": "src/test/regress/data/contestants.1.csv",
    "content": "a,1990-01-10,2090,97.1,XA ,{a}\nb,1990-11-01,2203,98.1,XA ,\"{a,b}\"\nc,1988-11-01,2907,99.4,XB ,\"{w,y}\"\nd,1985-05-05,2314,98.3,XB ,{}\ne,1995-05-05,2236,98.2,XC ,{a}\n"
  },
  {
    "path": "src/test/regress/data/contestants.2.csv",
    "content": "f,1983-04-02,3090,99.6,XD ,\"{a,b,c,y}\"\ng,1991-12-13,1803,85.1,XD ,\"{a,c}\"\nh,1987-10-26,2112,95.4,XD ,\"{w,a}\"\n"
  },
  {
    "path": "src/test/regress/data/customer-1-10.data",
    "content": "1|Customer#000000001|IVhzIApeRb ot,c,E|15|25-989-741-2988|711.56|BUILDING|to the even, regular platelets. regular, ironic epitaphs nag e\n2|Customer#000000002|XSTf4,NCwDVaWNe6tEgvwfmRchLXak|13|23-768-687-3665|121.65|AUTOMOBILE|l accounts. blithely ironic theodolites integrate boldly: caref\n3|Customer#000000003|MG9kdTD2WBHm|1|11-719-748-3364|7498.12|AUTOMOBILE| deposits eat slyly ironic, even instructions. express foxes detect slyly. blithely even accounts abov\n4|Customer#000000004|XxVSJsLAGtn|4|14-128-190-5944|2866.83|MACHINERY| requests. final, regular ideas sleep final accou\n5|Customer#000000005|KvpyuHCplrB84WgAiGV6sYpZq7Tj|3|13-750-942-6364|794.47|HOUSEHOLD|n accounts will have to unwind. foxes cajole accor\n6|Customer#000000006|sKZz0CsnMD7mp4Xd0YrBvx,LREYKUWAh yVn|20|30-114-968-4951|7638.57|AUTOMOBILE|tions. even deposits boost according to the slyly bold packages. final accounts cajole requests. furious\n7|Customer#000000007|TcGe5gaZNgVePxU5kRrvXBfkasDTea|18|28-190-982-9759|9561.95|AUTOMOBILE|ainst the ironic, express theodolites. express, even pinto beans among the exp\n8|Customer#000000008|I0B10bB0AymmC, 0PrRYBCP1yGJ8xcBPmWhl5|17|27-147-574-9335|6819.74|BUILDING|among the slyly regular theodolites kindle blithely courts. carefully even theodolites haggle slyly along the ide\n9|Customer#000000009|xKiAFTjUsCuxfeleNqefumTrjS|8|18-338-906-3675|8324.07|FURNITURE|r theodolites according to the requests wake thinly excuses: pending requests haggle furiousl\n10|Customer#000000010|6LrEaV6KR6PLVcgl2ArL Q3rqzLzcT1 v2|5|15-741-346-9870|2753.54|HOUSEHOLD|es regular deposits haggle. fur\n"
  },
  {
    "path": "src/test/regress/data/customer-1-15.data",
    "content": "1|Customer#000000001|IVhzIApeRb ot,c,E|15|25-989-741-2988|711.56|BUILDING|to the even, regular platelets. regular, ironic epitaphs nag e\n2|Customer#000000002|XSTf4,NCwDVaWNe6tEgvwfmRchLXak|13|23-768-687-3665|121.65|AUTOMOBILE|l accounts. blithely ironic theodolites integrate boldly: caref\n3|Customer#000000003|MG9kdTD2WBHm|1|11-719-748-3364|7498.12|AUTOMOBILE| deposits eat slyly ironic, even instructions. express foxes detect slyly. blithely even accounts abov\n4|Customer#000000004|XxVSJsLAGtn|4|14-128-190-5944|2866.83|MACHINERY| requests. final, regular ideas sleep final accou\n5|Customer#000000005|KvpyuHCplrB84WgAiGV6sYpZq7Tj|3|13-750-942-6364|794.47|HOUSEHOLD|n accounts will have to unwind. foxes cajole accor\n6|Customer#000000006|sKZz0CsnMD7mp4Xd0YrBvx,LREYKUWAh yVn|20|30-114-968-4951|7638.57|AUTOMOBILE|tions. even deposits boost according to the slyly bold packages. final accounts cajole requests. furious\n7|Customer#000000007|TcGe5gaZNgVePxU5kRrvXBfkasDTea|18|28-190-982-9759|9561.95|AUTOMOBILE|ainst the ironic, express theodolites. express, even pinto beans among the exp\n8|Customer#000000008|I0B10bB0AymmC, 0PrRYBCP1yGJ8xcBPmWhl5|17|27-147-574-9335|6819.74|BUILDING|among the slyly regular theodolites kindle blithely courts. carefully even theodolites haggle slyly along the ide\n9|Customer#000000009|xKiAFTjUsCuxfeleNqefumTrjS|8|18-338-906-3675|8324.07|FURNITURE|r theodolites according to the requests wake thinly excuses: pending requests haggle furiousl\n10|Customer#000000010|6LrEaV6KR6PLVcgl2ArL Q3rqzLzcT1 v2|5|15-741-346-9870|2753.54|HOUSEHOLD|es regular deposits haggle. fur\n11|Customer#000000011|PkWS 3HlXqwTuzrKg633BEi|23|33-464-151-3439|-272.60|BUILDING|ckages. requests sleep slyly. quickly even pinto beans promise above the slyly regular pinto beans. \n12|Customer#000000012|9PWKuhzT4Zr1Q|13|23-791-276-1263|3396.49|HOUSEHOLD| to the carefully final braids. blithely regular requests nag. ironic theodolites boost quickly along\n13|Customer#000000013|nsXQu0oVjD7PM659uC3SRSp|3|13-761-547-5974|3857.34|BUILDING|ounts sleep carefully after the close frays. carefully bold notornis use ironic requests. blithely\n14|Customer#000000014|KXkletMlL2JQEA |1|11-845-129-3851|5266.30|FURNITURE|, ironic packages across the unus\n15|Customer#000000015|YtWggXoOLdwdo7b0y,BZaGUQMLJMX1Y,EC,6Dn|23|33-687-542-7601|2788.52|HOUSEHOLD| platelets. regular deposits detect asymptotes. blithely unusual packages nag slyly at the fluf\n"
  },
  {
    "path": "src/test/regress/data/customer-1-20.data",
    "content": "1|Customer#000000001|IVhzIApeRb ot,c,E|15|25-989-741-2988|711.56|BUILDING|to the even, regular platelets. regular, ironic epitaphs nag e\n2|Customer#000000002|XSTf4,NCwDVaWNe6tEgvwfmRchLXak|13|23-768-687-3665|121.65|AUTOMOBILE|l accounts. blithely ironic theodolites integrate boldly: caref\n3|Customer#000000003|MG9kdTD2WBHm|1|11-719-748-3364|7498.12|AUTOMOBILE| deposits eat slyly ironic, even instructions. express foxes detect slyly. blithely even accounts abov\n4|Customer#000000004|XxVSJsLAGtn|4|14-128-190-5944|2866.83|MACHINERY| requests. final, regular ideas sleep final accou\n5|Customer#000000005|KvpyuHCplrB84WgAiGV6sYpZq7Tj|3|13-750-942-6364|794.47|HOUSEHOLD|n accounts will have to unwind. foxes cajole accor\n6|Customer#000000006|sKZz0CsnMD7mp4Xd0YrBvx,LREYKUWAh yVn|20|30-114-968-4951|7638.57|AUTOMOBILE|tions. even deposits boost according to the slyly bold packages. final accounts cajole requests. furious\n7|Customer#000000007|TcGe5gaZNgVePxU5kRrvXBfkasDTea|18|28-190-982-9759|9561.95|AUTOMOBILE|ainst the ironic, express theodolites. express, even pinto beans among the exp\n8|Customer#000000008|I0B10bB0AymmC, 0PrRYBCP1yGJ8xcBPmWhl5|17|27-147-574-9335|6819.74|BUILDING|among the slyly regular theodolites kindle blithely courts. carefully even theodolites haggle slyly along the ide\n9|Customer#000000009|xKiAFTjUsCuxfeleNqefumTrjS|8|18-338-906-3675|8324.07|FURNITURE|r theodolites according to the requests wake thinly excuses: pending requests haggle furiousl\n10|Customer#000000010|6LrEaV6KR6PLVcgl2ArL Q3rqzLzcT1 v2|5|15-741-346-9870|2753.54|HOUSEHOLD|es regular deposits haggle. fur\n11|Customer#000000011|PkWS 3HlXqwTuzrKg633BEi|23|33-464-151-3439|-272.60|BUILDING|ckages. requests sleep slyly. quickly even pinto beans promise above the slyly regular pinto beans. \n12|Customer#000000012|9PWKuhzT4Zr1Q|13|23-791-276-1263|3396.49|HOUSEHOLD| to the carefully final braids. blithely regular requests nag. ironic theodolites boost quickly along\n13|Customer#000000013|nsXQu0oVjD7PM659uC3SRSp|3|13-761-547-5974|3857.34|BUILDING|ounts sleep carefully after the close frays. carefully bold notornis use ironic requests. blithely\n14|Customer#000000014|KXkletMlL2JQEA |1|11-845-129-3851|5266.30|FURNITURE|, ironic packages across the unus\n15|Customer#000000015|YtWggXoOLdwdo7b0y,BZaGUQMLJMX1Y,EC,6Dn|23|33-687-542-7601|2788.52|HOUSEHOLD| platelets. regular deposits detect asymptotes. blithely unusual packages nag slyly at the fluf\n15|Customer#000000015|YtWggXoOLdwdo7b0y,BZaGUQMLJMX1Y,EC,6Dn|23|33-687-542-7601|2788.52|HOUSEHOLD| platelets. regular deposits detect asymptotes. blithely unusual packages nag slyly at the fluf\n16|Customer#000000016|cYiaeMLZSMAOQ2 d0W,|10|20-781-609-3107|4681.03|FURNITURE|kly silent courts. thinly regular theodolites sleep fluffily after \n17|Customer#000000017|izrh 6jdqtp2eqdtbkswDD8SG4SzXruMfIXyR7|2|12-970-682-3487|6.34|AUTOMOBILE|packages wake! blithely even pint\n18|Customer#000000018|3txGO AiuFux3zT0Z9NYaFRnZt|6|16-155-215-1315|5494.43|BUILDING|s sleep. carefully even instructions nag furiously alongside of t\n19|Customer#000000019|uc,3bHIx84H,wdrmLOjVsiqXCq2tr|18|28-396-526-5053|8914.71|HOUSEHOLD| nag. furiously careful packages are slyly at the accounts. furiously regular in\n20|Customer#000000020|JrPk8Pqplj4Ne|22|32-957-234-8742|7603.40|FURNITURE|g alongside of the special excuses-- fluffily enticing packages wake \n"
  },
  {
    "path": "src/test/regress/data/customer-1-30.data",
    "content": "1|Customer#000000001|IVhzIApeRb ot,c,E|15|25-989-741-2988|711.56|BUILDING|to the even, regular platelets. regular, ironic epitaphs nag e\n2|Customer#000000002|XSTf4,NCwDVaWNe6tEgvwfmRchLXak|13|23-768-687-3665|121.65|AUTOMOBILE|l accounts. blithely ironic theodolites integrate boldly: caref\n3|Customer#000000003|MG9kdTD2WBHm|1|11-719-748-3364|7498.12|AUTOMOBILE| deposits eat slyly ironic, even instructions. express foxes detect slyly. blithely even accounts abov\n4|Customer#000000004|XxVSJsLAGtn|4|14-128-190-5944|2866.83|MACHINERY| requests. final, regular ideas sleep final accou\n5|Customer#000000005|KvpyuHCplrB84WgAiGV6sYpZq7Tj|3|13-750-942-6364|794.47|HOUSEHOLD|n accounts will have to unwind. foxes cajole accor\n6|Customer#000000006|sKZz0CsnMD7mp4Xd0YrBvx,LREYKUWAh yVn|20|30-114-968-4951|7638.57|AUTOMOBILE|tions. even deposits boost according to the slyly bold packages. final accounts cajole requests. furious\n7|Customer#000000007|TcGe5gaZNgVePxU5kRrvXBfkasDTea|18|28-190-982-9759|9561.95|AUTOMOBILE|ainst the ironic, express theodolites. express, even pinto beans among the exp\n8|Customer#000000008|I0B10bB0AymmC, 0PrRYBCP1yGJ8xcBPmWhl5|17|27-147-574-9335|6819.74|BUILDING|among the slyly regular theodolites kindle blithely courts. carefully even theodolites haggle slyly along the ide\n9|Customer#000000009|xKiAFTjUsCuxfeleNqefumTrjS|8|18-338-906-3675|8324.07|FURNITURE|r theodolites according to the requests wake thinly excuses: pending requests haggle furiousl\n10|Customer#000000010|6LrEaV6KR6PLVcgl2ArL Q3rqzLzcT1 v2|5|15-741-346-9870|2753.54|HOUSEHOLD|es regular deposits haggle. fur\n11|Customer#000000011|PkWS 3HlXqwTuzrKg633BEi|23|33-464-151-3439|-272.60|BUILDING|ckages. requests sleep slyly. quickly even pinto beans promise above the slyly regular pinto beans. \n12|Customer#000000012|9PWKuhzT4Zr1Q|13|23-791-276-1263|3396.49|HOUSEHOLD| to the carefully final braids. blithely regular requests nag. ironic theodolites boost quickly along\n13|Customer#000000013|nsXQu0oVjD7PM659uC3SRSp|3|13-761-547-5974|3857.34|BUILDING|ounts sleep carefully after the close frays. carefully bold notornis use ironic requests. blithely\n14|Customer#000000014|KXkletMlL2JQEA |1|11-845-129-3851|5266.30|FURNITURE|, ironic packages across the unus\n15|Customer#000000015|YtWggXoOLdwdo7b0y,BZaGUQMLJMX1Y,EC,6Dn|23|33-687-542-7601|2788.52|HOUSEHOLD| platelets. regular deposits detect asymptotes. blithely unusual packages nag slyly at the fluf\n16|Customer#000000016|cYiaeMLZSMAOQ2 d0W,|10|20-781-609-3107|4681.03|FURNITURE|kly silent courts. thinly regular theodolites sleep fluffily after \n17|Customer#000000017|izrh 6jdqtp2eqdtbkswDD8SG4SzXruMfIXyR7|2|12-970-682-3487|6.34|AUTOMOBILE|packages wake! blithely even pint\n18|Customer#000000018|3txGO AiuFux3zT0Z9NYaFRnZt|6|16-155-215-1315|5494.43|BUILDING|s sleep. carefully even instructions nag furiously alongside of t\n19|Customer#000000019|uc,3bHIx84H,wdrmLOjVsiqXCq2tr|18|28-396-526-5053|8914.71|HOUSEHOLD| nag. furiously careful packages are slyly at the accounts. furiously regular in\n20|Customer#000000020|JrPk8Pqplj4Ne|22|32-957-234-8742|7603.40|FURNITURE|g alongside of the special excuses-- fluffily enticing packages wake \n21|Customer#000000021|XYmVpr9yAHDEn|8|18-902-614-8344|1428.25|MACHINERY| quickly final accounts integrate blithely furiously u\n22|Customer#000000022|QI6p41,FNs5k7RZoCCVPUTkUdYpB|3|13-806-545-9701|591.98|MACHINERY|s nod furiously above the furiously ironic ideas. \n23|Customer#000000023|OdY W13N7Be3OC5MpgfmcYss0Wn6TKT|3|13-312-472-8245|3332.02|HOUSEHOLD|deposits. special deposits cajole slyly. fluffily special deposits about the furiously \n24|Customer#000000024|HXAFgIAyjxtdqwimt13Y3OZO 4xeLe7U8PqG|13|23-127-851-8031|9255.67|MACHINERY|into beans. fluffily final ideas haggle fluffily\n25|Customer#000000025|Hp8GyFQgGHFYSilH5tBfe|12|22-603-468-3533|7133.70|FURNITURE|y. accounts sleep ruthlessly according to the regular theodolites. unusual instructions sleep. ironic, final\n26|Customer#000000026|8ljrc5ZeMl7UciP|22|32-363-455-4837|5182.05|AUTOMOBILE|c requests use furiously ironic requests. slyly ironic dependencies us\n27|Customer#000000027|IS8GIyxpBrLpMT0u7|3|13-137-193-2709|5679.84|BUILDING| about the carefully ironic pinto beans. accoun\n28|Customer#000000028|iVyg0daQ,Tha8x2WPWA9m2529m|8|18-774-241-1462|1007.18|FURNITURE| along the regular deposits. furiously final pac\n29|Customer#000000029|sJ5adtfyAkCK63df2,vF25zyQMVYE34uh|0|10-773-203-7342|7618.27|FURNITURE|its after the carefully final platelets x-ray against \n30|Customer#000000030|nJDsELGAavU63Jl0c5NKsKfL8rIJQQkQnYL2QJY|1|11-764-165-5076|9321.01|BUILDING|lithely final requests. furiously unusual account\n"
  },
  {
    "path": "src/test/regress/data/customer-11-20.data",
    "content": "11|Customer#000000011|PkWS 3HlXqwTuzrKg633BEi|23|33-464-151-3439|-272.60|BUILDING|ckages. requests sleep slyly. quickly even pinto beans promise above the slyly regular pinto beans. \n12|Customer#000000012|9PWKuhzT4Zr1Q|13|23-791-276-1263|3396.49|HOUSEHOLD| to the carefully final braids. blithely regular requests nag. ironic theodolites boost quickly along\n13|Customer#000000013|nsXQu0oVjD7PM659uC3SRSp|3|13-761-547-5974|3857.34|BUILDING|ounts sleep carefully after the close frays. carefully bold notornis use ironic requests. blithely\n14|Customer#000000014|KXkletMlL2JQEA |1|11-845-129-3851|5266.30|FURNITURE|, ironic packages across the unus\n15|Customer#000000015|YtWggXoOLdwdo7b0y,BZaGUQMLJMX1Y,EC,6Dn|23|33-687-542-7601|2788.52|HOUSEHOLD| platelets. regular deposits detect asymptotes. blithely unusual packages nag slyly at the fluf\n16|Customer#000000016|cYiaeMLZSMAOQ2 d0W,|10|20-781-609-3107|4681.03|FURNITURE|kly silent courts. thinly regular theodolites sleep fluffily after \n17|Customer#000000017|izrh 6jdqtp2eqdtbkswDD8SG4SzXruMfIXyR7|2|12-970-682-3487|6.34|AUTOMOBILE|packages wake! blithely even pint\n18|Customer#000000018|3txGO AiuFux3zT0Z9NYaFRnZt|6|16-155-215-1315|5494.43|BUILDING|s sleep. carefully even instructions nag furiously alongside of t\n19|Customer#000000019|uc,3bHIx84H,wdrmLOjVsiqXCq2tr|18|28-396-526-5053|8914.71|HOUSEHOLD| nag. furiously careful packages are slyly at the accounts. furiously regular in\n20|Customer#000000020|JrPk8Pqplj4Ne|22|32-957-234-8742|7603.40|FURNITURE|g alongside of the special excuses-- fluffily enticing packages wake \n"
  },
  {
    "path": "src/test/regress/data/customer-21-30.data",
    "content": "21|Customer#000000021|XYmVpr9yAHDEn|8|18-902-614-8344|1428.25|MACHINERY| quickly final accounts integrate blithely furiously u\n22|Customer#000000022|QI6p41,FNs5k7RZoCCVPUTkUdYpB|3|13-806-545-9701|591.98|MACHINERY|s nod furiously above the furiously ironic ideas. \n23|Customer#000000023|OdY W13N7Be3OC5MpgfmcYss0Wn6TKT|3|13-312-472-8245|3332.02|HOUSEHOLD|deposits. special deposits cajole slyly. fluffily special deposits about the furiously \n24|Customer#000000024|HXAFgIAyjxtdqwimt13Y3OZO 4xeLe7U8PqG|13|23-127-851-8031|9255.67|MACHINERY|into beans. fluffily final ideas haggle fluffily\n25|Customer#000000025|Hp8GyFQgGHFYSilH5tBfe|12|22-603-468-3533|7133.70|FURNITURE|y. accounts sleep ruthlessly according to the regular theodolites. unusual instructions sleep. ironic, final\n26|Customer#000000026|8ljrc5ZeMl7UciP|22|32-363-455-4837|5182.05|AUTOMOBILE|c requests use furiously ironic requests. slyly ironic dependencies us\n27|Customer#000000027|IS8GIyxpBrLpMT0u7|3|13-137-193-2709|5679.84|BUILDING| about the carefully ironic pinto beans. accoun\n28|Customer#000000028|iVyg0daQ,Tha8x2WPWA9m2529m|8|18-774-241-1462|1007.18|FURNITURE| along the regular deposits. furiously final pac\n29|Customer#000000029|sJ5adtfyAkCK63df2,vF25zyQMVYE34uh|0|10-773-203-7342|7618.27|FURNITURE|its after the carefully final platelets x-ray against \n30|Customer#000000030|nJDsELGAavU63Jl0c5NKsKfL8rIJQQkQnYL2QJY|1|11-764-165-5076|9321.01|BUILDING|lithely final requests. furiously unusual account\n"
  },
  {
    "path": "src/test/regress/data/customer-subset-11-20.data",
    "content": "11|Customer#000000011|PkWS 3HlXqwTuzrKg633BEi|23|33-464-151-3439|-272.60|BUILDING|ckages. requests sleep slyly. quickly even pinto beans promise above the slyly regular pinto beans. \n12|Customer#000000012|9PWKuhzT4Zr1Q|13|23-791-276-1263|3396.49|HOUSEHOLD| to the carefully final braids. blithely regular requests nag. ironic theodolites boost quickly along\n14|Customer#000000014|KXkletMlL2JQEA |1|11-845-129-3851|5266.30|FURNITURE|, ironic packages across the unus\n16|Customer#000000016|cYiaeMLZSMAOQ2 d0W,|10|20-781-609-3107|4681.03|FURNITURE|kly silent courts. thinly regular theodolites sleep fluffily after \n17|Customer#000000017|izrh 6jdqtp2eqdtbkswDD8SG4SzXruMfIXyR7|2|12-970-682-3487|6.34|AUTOMOBILE|packages wake! blithely even pint\n18|Customer#000000018|3txGO AiuFux3zT0Z9NYaFRnZt|6|16-155-215-1315|5494.43|BUILDING|s sleep. carefully even instructions nag furiously alongside of t\n20|Customer#000000020|JrPk8Pqplj4Ne|22|32-957-234-8742|7603.40|FURNITURE|g alongside of the special excuses-- fluffily enticing packages wake \n"
  },
  {
    "path": "src/test/regress/data/customer-subset-21-30.data",
    "content": "21|Customer#000000021|XYmVpr9yAHDEn|8|18-902-614-8344|1428.25|MACHINERY| quickly final accounts integrate blithely furiously u\n22|Customer#000000022|QI6p41,FNs5k7RZoCCVPUTkUdYpB|3|13-806-545-9701|591.98|MACHINERY|s nod furiously above the furiously ironic ideas. \n24|Customer#000000024|HXAFgIAyjxtdqwimt13Y3OZO 4xeLe7U8PqG|13|23-127-851-8031|9255.67|MACHINERY|into beans. fluffily final ideas haggle fluffily\n26|Customer#000000026|8ljrc5ZeMl7UciP|22|32-363-455-4837|5182.05|AUTOMOBILE|c requests use furiously ironic requests. slyly ironic dependencies us\n27|Customer#000000027|IS8GIyxpBrLpMT0u7|3|13-137-193-2709|5679.84|BUILDING| about the carefully ironic pinto beans. accoun\n28|Customer#000000028|iVyg0daQ,Tha8x2WPWA9m2529m|8|18-774-241-1462|1007.18|FURNITURE| along the regular deposits. furiously final pac\n30|Customer#000000030|nJDsELGAavU63Jl0c5NKsKfL8rIJQQkQnYL2QJY|1|11-764-165-5076|9321.01|BUILDING|lithely final requests. furiously unusual account\n"
  },
  {
    "path": "src/test/regress/data/customer.1.data",
    "content": "1|Customer#000000001|IVhzIApeRb ot,c,E|15|25-989-741-2988|711.56|BUILDING|to the even, regular platelets. regular, ironic epitaphs nag e\n2|Customer#000000002|XSTf4,NCwDVaWNe6tEgvwfmRchLXak|13|23-768-687-3665|121.65|AUTOMOBILE|l accounts. blithely ironic theodolites integrate boldly: caref\n3|Customer#000000003|MG9kdTD2WBHm|1|11-719-748-3364|7498.12|AUTOMOBILE| deposits eat slyly ironic, even instructions. express foxes detect slyly. blithely even accounts abov\n4|Customer#000000004|XxVSJsLAGtn|4|14-128-190-5944|2866.83|MACHINERY| requests. final, regular ideas sleep final accou\n5|Customer#000000005|KvpyuHCplrB84WgAiGV6sYpZq7Tj|3|13-750-942-6364|794.47|HOUSEHOLD|n accounts will have to unwind. foxes cajole accor\n6|Customer#000000006|sKZz0CsnMD7mp4Xd0YrBvx,LREYKUWAh yVn|20|30-114-968-4951|7638.57|AUTOMOBILE|tions. even deposits boost according to the slyly bold packages. final accounts cajole requests. furious\n7|Customer#000000007|TcGe5gaZNgVePxU5kRrvXBfkasDTea|18|28-190-982-9759|9561.95|AUTOMOBILE|ainst the ironic, express theodolites. express, even pinto beans among the exp\n8|Customer#000000008|I0B10bB0AymmC, 0PrRYBCP1yGJ8xcBPmWhl5|17|27-147-574-9335|6819.74|BUILDING|among the slyly regular theodolites kindle blithely courts. carefully even theodolites haggle slyly along the ide\n9|Customer#000000009|xKiAFTjUsCuxfeleNqefumTrjS|8|18-338-906-3675|8324.07|FURNITURE|r theodolites according to the requests wake thinly excuses: pending requests haggle furiousl\n10|Customer#000000010|6LrEaV6KR6PLVcgl2ArL Q3rqzLzcT1 v2|5|15-741-346-9870|2753.54|HOUSEHOLD|es regular deposits haggle. fur\n11|Customer#000000011|PkWS 3HlXqwTuzrKg633BEi|23|33-464-151-3439|-272.60|BUILDING|ckages. requests sleep slyly. quickly even pinto beans promise above the slyly regular pinto beans. \n12|Customer#000000012|9PWKuhzT4Zr1Q|13|23-791-276-1263|3396.49|HOUSEHOLD| to the carefully final braids. blithely regular requests nag. ironic theodolites boost quickly along\n13|Customer#000000013|nsXQu0oVjD7PM659uC3SRSp|3|13-761-547-5974|3857.34|BUILDING|ounts sleep carefully after the close frays. carefully bold notornis use ironic requests. blithely\n14|Customer#000000014|KXkletMlL2JQEA |1|11-845-129-3851|5266.30|FURNITURE|, ironic packages across the unus\n15|Customer#000000015|YtWggXoOLdwdo7b0y,BZaGUQMLJMX1Y,EC,6Dn|23|33-687-542-7601|2788.52|HOUSEHOLD| platelets. regular deposits detect asymptotes. blithely unusual packages nag slyly at the fluf\n16|Customer#000000016|cYiaeMLZSMAOQ2 d0W,|10|20-781-609-3107|4681.03|FURNITURE|kly silent courts. thinly regular theodolites sleep fluffily after \n17|Customer#000000017|izrh 6jdqtp2eqdtbkswDD8SG4SzXruMfIXyR7|2|12-970-682-3487|6.34|AUTOMOBILE|packages wake! blithely even pint\n18|Customer#000000018|3txGO AiuFux3zT0Z9NYaFRnZt|6|16-155-215-1315|5494.43|BUILDING|s sleep. carefully even instructions nag furiously alongside of t\n19|Customer#000000019|uc,3bHIx84H,wdrmLOjVsiqXCq2tr|18|28-396-526-5053|8914.71|HOUSEHOLD| nag. furiously careful packages are slyly at the accounts. furiously regular in\n20|Customer#000000020|JrPk8Pqplj4Ne|22|32-957-234-8742|7603.40|FURNITURE|g alongside of the special excuses-- fluffily enticing packages wake \n21|Customer#000000021|XYmVpr9yAHDEn|8|18-902-614-8344|1428.25|MACHINERY| quickly final accounts integrate blithely furiously u\n22|Customer#000000022|QI6p41,FNs5k7RZoCCVPUTkUdYpB|3|13-806-545-9701|591.98|MACHINERY|s nod furiously above the furiously ironic ideas. \n23|Customer#000000023|OdY W13N7Be3OC5MpgfmcYss0Wn6TKT|3|13-312-472-8245|3332.02|HOUSEHOLD|deposits. special deposits cajole slyly. fluffily special deposits about the furiously \n24|Customer#000000024|HXAFgIAyjxtdqwimt13Y3OZO 4xeLe7U8PqG|13|23-127-851-8031|9255.67|MACHINERY|into beans. fluffily final ideas haggle fluffily\n25|Customer#000000025|Hp8GyFQgGHFYSilH5tBfe|12|22-603-468-3533|7133.70|FURNITURE|y. accounts sleep ruthlessly according to the regular theodolites. unusual instructions sleep. ironic, final\n26|Customer#000000026|8ljrc5ZeMl7UciP|22|32-363-455-4837|5182.05|AUTOMOBILE|c requests use furiously ironic requests. slyly ironic dependencies us\n27|Customer#000000027|IS8GIyxpBrLpMT0u7|3|13-137-193-2709|5679.84|BUILDING| about the carefully ironic pinto beans. accoun\n28|Customer#000000028|iVyg0daQ,Tha8x2WPWA9m2529m|8|18-774-241-1462|1007.18|FURNITURE| along the regular deposits. furiously final pac\n29|Customer#000000029|sJ5adtfyAkCK63df2,vF25zyQMVYE34uh|0|10-773-203-7342|7618.27|FURNITURE|its after the carefully final platelets x-ray against \n30|Customer#000000030|nJDsELGAavU63Jl0c5NKsKfL8rIJQQkQnYL2QJY|1|11-764-165-5076|9321.01|BUILDING|lithely final requests. furiously unusual account\n31|Customer#000000031|LUACbO0viaAv6eXOAebryDB xjVst|23|33-197-837-7094|5236.89|HOUSEHOLD|s use among the blithely pending depo\n32|Customer#000000032|jD2xZzi UmId,DCtNBLXKj9q0Tlp2iQ6ZcO3J|15|25-430-914-2194|3471.53|BUILDING|cial ideas. final, furious requests across the e\n33|Customer#000000033|qFSlMuLucBmx9xnn5ib2csWUweg D|17|27-375-391-1280|-78.56|AUTOMOBILE|s. slyly regular accounts are furiously. carefully pending requests\n34|Customer#000000034|Q6G9wZ6dnczmtOx509xgE,M2KV|15|25-344-968-5422|8589.70|HOUSEHOLD|nder against the even, pending accounts. even\n35|Customer#000000035|TEjWGE4nBzJL2|17|27-566-888-7431|1228.24|HOUSEHOLD|requests. special, express requests nag slyly furiousl\n36|Customer#000000036|3TvCzjuPzpJ0,DdJ8kW5U|21|31-704-669-5769|4987.27|BUILDING|haggle. enticing, quiet platelets grow quickly bold sheaves. carefully regular acc\n37|Customer#000000037|7EV4Pwh,3SboctTWt|8|18-385-235-7162|-917.75|FURNITURE|ilent packages are carefully among the deposits. furiousl\n38|Customer#000000038|a5Ee5e9568R8RLP 2ap7|12|22-306-880-7212|6345.11|HOUSEHOLD|lar excuses. closely even asymptotes cajole blithely excuses. carefully silent pinto beans sleep carefully fin\n39|Customer#000000039|nnbRg,Pvy33dfkorYE FdeZ60|2|12-387-467-6509|6264.31|AUTOMOBILE|tions. slyly silent excuses slee\n40|Customer#000000040|gOnGWAyhSV1ofv|3|13-652-915-8939|1335.30|BUILDING|rges impress after the slyly ironic courts. foxes are. blithely \n41|Customer#000000041|IM9mzmyoxeBmvNw8lA7G3Ydska2nkZF|10|20-917-711-4011|270.95|HOUSEHOLD|ly regular accounts hang bold, silent packages. unusual foxes haggle slyly above the special, final depo\n42|Customer#000000042|ziSrvyyBke|5|15-416-330-4175|8727.01|BUILDING|ssly according to the pinto beans: carefully special requests across the even, pending accounts wake special\n43|Customer#000000043|ouSbjHk8lh5fKX3zGso3ZSIj9Aa3PoaFd|19|29-316-665-2897|9904.28|MACHINERY|ial requests: carefully pending foxes detect quickly. carefully final courts cajole quickly. carefully\n44|Customer#000000044|Oi,dOSPwDu4jo4x,,P85E0dmhZGvNtBwi|16|26-190-260-5375|7315.94|AUTOMOBILE|r requests around the unusual, bold a\n45|Customer#000000045|4v3OcpFgoOmMG,CbnF,4mdC|9|19-715-298-9917|9983.38|AUTOMOBILE|nto beans haggle slyly alongside of t\n46|Customer#000000046|eaTXWWm10L9|6|16-357-681-2007|5744.59|AUTOMOBILE|ctions. accounts sleep furiously even requests. regular, regular accounts cajole blithely around the final pa\n47|Customer#000000047|b0UgocSqEW5 gdVbhNT|2|12-427-271-9466|274.58|BUILDING|ions. express, ironic instructions sleep furiously ironic ideas. furi\n48|Customer#000000048|0UU iPhBupFvemNB|0|10-508-348-5882|3792.50|BUILDING|re fluffily pending foxes. pending, bold platelets sleep slyly. even platelets cajo\n49|Customer#000000049|cNgAeX7Fqrdf7HQN9EwjUa4nxT,68L FKAxzl|10|20-908-631-4424|4573.94|FURNITURE|nusual foxes! fluffily pending packages maintain to the regular \n50|Customer#000000050|9SzDYlkzxByyJ1QeTI o|6|16-658-112-3221|4266.13|MACHINERY|ts. furiously ironic accounts cajole furiously slyly ironic dinos.\n51|Customer#000000051|uR,wEaiTvo4|12|22-344-885-4251|855.87|FURNITURE|eposits. furiously regular requests integrate carefully packages. furious\n52|Customer#000000052|7 QOqGqqSy9jfV51BC71jcHJSD0|11|21-186-284-5998|5630.28|HOUSEHOLD|ic platelets use evenly even accounts. stealthy theodolites cajole furiou\n53|Customer#000000053|HnaxHzTfFTZs8MuCpJyTbZ47Cm4wFOOgib|15|25-168-852-5363|4113.64|HOUSEHOLD|ar accounts are. even foxes are blithely. fluffily pending deposits boost\n54|Customer#000000054|,k4vf 5vECGWFy,hosTE,|4|14-776-370-4745|868.90|AUTOMOBILE|sual, silent accounts. furiously express accounts cajole special deposits. final, final accounts use furi\n55|Customer#000000055|zIRBR4KNEl HzaiV3a i9n6elrxzDEh8r8pDom|10|20-180-440-8525|4572.11|MACHINERY|ully unusual packages wake bravely bold packages. unusual requests boost deposits! blithely ironic packages ab\n56|Customer#000000056|BJYZYJQk4yD5B|10|20-895-685-6920|6530.86|FURNITURE|. notornis wake carefully. carefully fluffy requests are furiously even accounts. slyly expre\n57|Customer#000000057|97XYbsuOPRXPWU|21|31-835-306-1650|4151.93|AUTOMOBILE|ove the carefully special packages. even, unusual deposits sleep slyly pend\n58|Customer#000000058|g9ap7Dk1Sv9fcXEWjpMYpBZIRUohi T|13|23-244-493-2508|6478.46|HOUSEHOLD|ideas. ironic ideas affix furiously express, final instructions. regular excuses use quickly e\n59|Customer#000000059|zLOCP0wh92OtBihgspOGl4|1|11-355-584-3112|3458.60|MACHINERY|ously final packages haggle blithely after the express deposits. furiou\n60|Customer#000000060|FyodhjwMChsZmUz7Jz0H|12|22-480-575-5866|2741.87|MACHINERY|latelets. blithely unusual courts boost furiously about the packages. blithely final instruct\n61|Customer#000000061|9kndve4EAJxhg3veF BfXr7AqOsT39o gtqjaYE|17|27-626-559-8599|1536.24|FURNITURE|egular packages shall have to impress along the \n62|Customer#000000062|upJK2Dnw13,|7|17-361-978-7059|595.61|MACHINERY|kly special dolphins. pinto beans are slyly. quickly regular accounts are furiously a\n63|Customer#000000063|IXRSpVWWZraKII|21|31-952-552-9584|9331.13|AUTOMOBILE|ithely even accounts detect slyly above the fluffily ir\n64|Customer#000000064|MbCeGY20kaKK3oalJD,OT|3|13-558-731-7204|-646.64|BUILDING|structions after the quietly ironic theodolites cajole be\n65|Customer#000000065|RGT yzQ0y4l0H90P783LG4U95bXQFDRXbWa1sl,X|23|33-733-623-5267|8795.16|AUTOMOBILE|y final foxes serve carefully. theodolites are carefully. pending i\n66|Customer#000000066|XbsEqXH1ETbJYYtA1A|22|32-213-373-5094|242.77|HOUSEHOLD|le slyly accounts. carefully silent packages benea\n67|Customer#000000067|rfG0cOgtr5W8 xILkwp9fpCS8|9|19-403-114-4356|8166.59|MACHINERY|indle furiously final, even theodo\n68|Customer#000000068|o8AibcCRkXvQFh8hF,7o|12|22-918-832-2411|6853.37|HOUSEHOLD| pending pinto beans impress realms. final dependencies \n69|Customer#000000069|Ltx17nO9Wwhtdbe9QZVxNgP98V7xW97uvSH1prEw|9|19-225-978-5670|1709.28|HOUSEHOLD|thely final ideas around the quickly final dependencies affix carefully quickly final theodolites. final accounts c\n70|Customer#000000070|mFowIuhnHjp2GjCiYYavkW kUwOjIaTCQ|22|32-828-107-2832|4867.52|FURNITURE|fter the special asymptotes. ideas after the unusual frets cajole quickly regular pinto be\n71|Customer#000000071|TlGalgdXWBmMV,6agLyWYDyIz9MKzcY8gl,w6t1B|7|17-710-812-5403|-611.19|HOUSEHOLD|g courts across the regular, final pinto beans are blithely pending ac\n72|Customer#000000072|putjlmskxE,zs,HqeIA9Wqu7dhgH5BVCwDwHHcf|2|12-759-144-9689|-362.86|FURNITURE|ithely final foxes sleep always quickly bold accounts. final wat\n73|Customer#000000073|8IhIxreu4Ug6tt5mog4|0|10-473-439-3214|4288.50|BUILDING|usual, unusual packages sleep busily along the furiou\n74|Customer#000000074|IkJHCA3ZThF7qL7VKcrU nRLl,kylf |4|14-199-862-7209|2764.43|MACHINERY|onic accounts. blithely slow packages would haggle carefully. qui\n75|Customer#000000075|Dh 6jZ,cwxWLKQfRKkiGrzv6pm|18|28-247-803-9025|6684.10|AUTOMOBILE| instructions cajole even, even deposits. finally bold deposits use above the even pains. slyl\n76|Customer#000000076|m3sbCvjMOHyaOofH,e UkGPtqc4|0|10-349-718-3044|5745.33|FURNITURE|pecial deposits. ironic ideas boost blithely according to the closely ironic theodolites! furiously final deposits n\n77|Customer#000000077|4tAE5KdMFGD4byHtXF92vx|17|27-269-357-4674|1738.87|BUILDING|uffily silent requests. carefully ironic asymptotes among the ironic hockey players are carefully bli\n78|Customer#000000078|HBOta,ZNqpg3U2cSL0kbrftkPwzX|9|19-960-700-9191|7136.97|FURNITURE|ests. blithely bold pinto beans h\n79|Customer#000000079|n5hH2ftkVRwW8idtD,BmM2|15|25-147-850-4166|5121.28|MACHINERY|es. packages haggle furiously. regular, special requests poach after the quickly express ideas. blithely pending re\n80|Customer#000000080|K,vtXp8qYB |0|10-267-172-7101|7383.53|FURNITURE|tect among the dependencies. bold accounts engage closely even pinto beans. ca\n81|Customer#000000081|SH6lPA7JiiNC6dNTrR|20|30-165-277-3269|2023.71|BUILDING|r packages. fluffily ironic requests cajole fluffily. ironically regular theodolit\n82|Customer#000000082|zhG3EZbap4c992Gj3bK,3Ne,Xn|18|28-159-442-5305|9468.34|AUTOMOBILE|s wake. bravely regular accounts are furiously. regula\n83|Customer#000000083|HnhTNB5xpnSF20JBH4Ycs6psVnkC3RDf|22|32-817-154-4122|6463.51|BUILDING|ccording to the quickly bold warhorses. final, regular foxes integrate carefully. bold packages nag blithely ev\n84|Customer#000000084|lpXz6Fwr9945rnbtMc8PlueilS1WmASr CB|11|21-546-818-3802|5174.71|FURNITURE|ly blithe foxes. special asymptotes haggle blithely against the furiously regular depo\n85|Customer#000000085|siRerlDwiolhYR 8FgksoezycLj|5|15-745-585-8219|3386.64|FURNITURE|ronic ideas use above the slowly pendin\n86|Customer#000000086|US6EGGHXbTTXPL9SBsxQJsuvy|0|10-677-951-2353|3306.32|HOUSEHOLD|quests. pending dugouts are carefully aroun\n87|Customer#000000087|hgGhHVSWQl 6jZ6Ev|23|33-869-884-7053|6327.54|FURNITURE|hely ironic requests integrate according to the ironic accounts. slyly regular pla\n88|Customer#000000088|wtkjBN9eyrFuENSMmMFlJ3e7jE5KXcg|16|26-516-273-2566|8031.44|AUTOMOBILE|s are quickly above the quickly ironic instructions; even requests about the carefully final deposi\n89|Customer#000000089|dtR, y9JQWUO6FoJExyp8whOU|14|24-394-451-5404|1530.76|FURNITURE|counts are slyly beyond the slyly final accounts. quickly final ideas wake. r\n90|Customer#000000090|QxCzH7VxxYUWwfL7|16|26-603-491-1238|7354.23|BUILDING|sly across the furiously even \n91|Customer#000000091|S8OMYFrpHwoNHaGBeuS6E 6zhHGZiprw1b7 q|8|18-239-400-3677|4643.14|AUTOMOBILE|onic accounts. fluffily silent pinto beans boost blithely according to the fluffily exp\n92|Customer#000000092|obP PULk2LH LqNF,K9hcbNqnLAkJVsl5xqSrY,|2|12-446-416-8471|1182.91|MACHINERY|. pinto beans hang slyly final deposits. ac\n93|Customer#000000093|EHXBr2QGdh|7|17-359-388-5266|2182.52|MACHINERY|press deposits. carefully regular platelets r\n94|Customer#000000094|IfVNIN9KtkScJ9dUjK3Pg5gY1aFeaXewwf|9|19-953-499-8833|5500.11|HOUSEHOLD|latelets across the bold, final requests sleep according to the fluffily bold accounts. unusual deposits amon\n95|Customer#000000095|EU0xvmWvOmUUn5J,2z85DQyG7QCJ9Xq7|15|25-923-255-2929|5327.38|MACHINERY|ithely. ruthlessly final requests wake slyly alongside of the furiously silent pinto beans. even the\n96|Customer#000000096|vWLOrmXhRR|8|18-422-845-1202|6323.92|AUTOMOBILE|press requests believe furiously. carefully final instructions snooze carefully. \n97|Customer#000000097|OApyejbhJG,0Iw3j rd1M|17|27-588-919-5638|2164.48|AUTOMOBILE|haggle slyly. bold, special ideas are blithely above the thinly bold theo\n98|Customer#000000098|7yiheXNSpuEAwbswDW|12|22-885-845-6889|-551.37|BUILDING|ages. furiously pending accounts are quickly carefully final foxes: busily pe\n99|Customer#000000099|szsrOiPtCHVS97Lt|15|25-515-237-9232|4088.65|HOUSEHOLD|cajole slyly about the regular theodolites! furiously bold requests nag along the pending, regular packages. somas\n100|Customer#000000100|fptUABXcmkC5Wx|20|30-749-445-4907|9889.89|FURNITURE|was furiously fluffily quiet deposits. silent, pending requests boost against \n101|Customer#000000101|sMmL2rNeHDltovSm Y|2|12-514-298-3699|7470.96|MACHINERY| sleep. pending packages detect slyly ironic pack\n102|Customer#000000102|UAtflJ06 fn9zBfKjInkQZlWtqaA|19|29-324-978-8538|8462.17|BUILDING|ously regular dependencies nag among the furiously express dinos. blithely final\n103|Customer#000000103|8KIsQX4LJ7QMsj6DrtFtXu0nUEdV,8a|9|19-216-107-2107|2757.45|BUILDING|furiously pending notornis boost slyly around the blithely ironic ideas? final, even instructions cajole fl\n104|Customer#000000104|9mcCK L7rt0SwiYtrbO88DiZS7U d7M|10|20-966-284-8065|-588.38|FURNITURE|rate carefully slyly special pla\n105|Customer#000000105|4iSJe4L SPjg7kJj98Yz3z0B|10|20-793-553-6417|9091.82|MACHINERY|l pains cajole even accounts. quietly final instructi\n106|Customer#000000106|xGCOEAUjUNG|1|11-751-989-4627|3288.42|MACHINERY|lose slyly. ironic accounts along the evenly regular theodolites wake about the special, final gifts. \n107|Customer#000000107|Zwg64UZ,q7GRqo3zm7P1tZIRshBDz|15|25-336-529-9919|2514.15|AUTOMOBILE|counts cajole slyly. regular requests wake. furiously regular deposits about the blithely final fo\n108|Customer#000000108|GPoeEvpKo1|5|15-908-619-7526|2259.38|BUILDING|refully ironic deposits sleep. regular, unusual requests wake slyly\n109|Customer#000000109|OOOkYBgCMzgMQXUmkocoLb56rfrdWp2NE2c|16|26-992-422-8153|-716.10|BUILDING|es. fluffily final dependencies sleep along the blithely even pinto beans. final deposits haggle furiously furiou\n110|Customer#000000110|mymPfgphaYXNYtk|10|20-893-536-2069|7462.99|AUTOMOBILE|nto beans cajole around the even, final deposits. quickly bold packages according to the furiously regular dept\n111|Customer#000000111|CBSbPyOWRorloj2TBvrK9qp9tHBs|22|32-582-283-7528|6505.26|MACHINERY|ly unusual instructions detect fluffily special deposits-- theodolites nag carefully during the ironic dependencies\n112|Customer#000000112|RcfgG3bO7QeCnfjqJT1|19|29-233-262-8382|2953.35|FURNITURE|rmanently unusual multipliers. blithely ruthless deposits are furiously along the\n113|Customer#000000113|eaOl5UBXIvdY57rglaIzqvfPD,MYfK|12|22-302-930-4756|2912.00|BUILDING|usly regular theodolites boost furiously doggedly pending instructio\n114|Customer#000000114|xAt 5f5AlFIU|14|24-805-212-7646|1027.46|FURNITURE|der the carefully express theodolites are after the packages. packages are. bli\n115|Customer#000000115|0WFt1IXENmUT2BgbsB0ShVKJZt0HCBCbFl0aHc|8|18-971-699-1843|7508.92|HOUSEHOLD|sits haggle above the carefully ironic theodolite\n116|Customer#000000116|yCuVxIgsZ3,qyK2rloThy3u|16|26-632-309-5792|8403.99|BUILDING|as. quickly final sauternes haggle slyly carefully even packages. brave, ironic pinto beans are above the furious\n117|Customer#000000117|uNhM,PzsRA3S,5Y Ge5Npuhi|24|34-403-631-3505|3950.83|FURNITURE|affix. instructions are furiously sl\n118|Customer#000000118|OVnFuHygK9wx3xpg8|18|28-639-943-7051|3582.37|AUTOMOBILE|uick packages alongside of the furiously final deposits haggle above the fluffily even foxes. blithely dogged dep\n119|Customer#000000119|M1ETOIecuvH8DtM0Y0nryXfW|7|17-697-919-8406|3930.35|FURNITURE|express ideas. blithely ironic foxes thrash. special acco\n120|Customer#000000120|zBNna00AEInqyO1|12|22-291-534-1571|363.75|MACHINERY| quickly. slyly ironic requests cajole blithely furiously final dependen\n121|Customer#000000121|tv nCR2YKupGN73mQudO|17|27-411-990-2959|6428.32|BUILDING|uriously stealthy ideas. carefully final courts use carefully\n122|Customer#000000122|yp5slqoNd26lAENZW3a67wSfXA6hTF|3|13-702-694-4520|7865.46|HOUSEHOLD| the special packages hinder blithely around the permanent requests. bold depos\n123|Customer#000000123|YsOnaaER8MkvK5cpf4VSlq|5|15-817-151-1168|5897.83|BUILDING|ependencies. regular, ironic requests are fluffily regu\n124|Customer#000000124|aTbyVAW5tCd,v09O|18|28-183-750-7809|1842.49|AUTOMOBILE|le fluffily even dependencies. quietly s\n125|Customer#000000125|,wSZXdVR xxIIfm9s8ITyLl3kgjT6UC07GY0Y|19|29-261-996-3120|-234.12|FURNITURE|x-ray finally after the packages? regular requests c\n126|Customer#000000126|ha4EHmbx3kg DYCsP6DFeUOmavtQlHhcfaqr|22|32-755-914-7592|1001.39|HOUSEHOLD|s about the even instructions boost carefully furiously ironic pearls. ruthless, \n127|Customer#000000127|Xyge4DX2rXKxXyye1Z47LeLVEYMLf4Bfcj|21|31-101-672-2951|9280.71|MACHINERY|ic, unusual theodolites nod silently after the final, ironic instructions: pending r\n128|Customer#000000128|AmKUMlJf2NRHcKGmKjLS|4|14-280-874-8044|-986.96|HOUSEHOLD|ing packages integrate across the slyly unusual dugouts. blithely silent ideas sublate carefully. blithely expr\n129|Customer#000000129|q7m7rbMM0BpaCdmxloCgBDRCleXsXkdD8kf|7|17-415-148-7416|9127.27|HOUSEHOLD| unusual deposits boost carefully furiously silent ideas. pending accounts cajole slyly across\n130|Customer#000000130|RKPx2OfZy0Vn 8wGWZ7F2EAvmMORl1k8iH|9|19-190-993-9281|5073.58|HOUSEHOLD|ix slowly. express packages along the furiously ironic requests integrate daringly deposits. fur\n131|Customer#000000131|jyN6lAjb1FtH10rMC,XzlWyCBrg75|11|21-840-210-3572|8595.53|HOUSEHOLD|jole special packages. furiously final dependencies about the furiously speci\n132|Customer#000000132|QM5YabAsTLp9|4|14-692-150-9717|162.57|HOUSEHOLD|uickly carefully special theodolites. carefully regular requests against the blithely unusual instructions \n133|Customer#000000133|IMCuXdpIvdkYO92kgDGuyHgojcUs88p|17|27-408-997-8430|2314.67|AUTOMOBILE|t packages. express pinto beans are blithely along the unusual, even theodolites. silent packages use fu\n134|Customer#000000134|sUiZ78QCkTQPICKpA9OBzkUp2FM|11|21-200-159-5932|4608.90|BUILDING|yly fluffy foxes boost final ideas. b\n135|Customer#000000135|oZK,oC0 fdEpqUML|19|29-399-293-6241|8732.91|FURNITURE| the slyly final accounts. deposits cajole carefully. carefully sly packag\n136|Customer#000000136|QoLsJ0v5C1IQbh,DS1|7|17-501-210-4726|-842.39|FURNITURE|ackages sleep ironic, final courts. even requests above the blithely bold requests g\n137|Customer#000000137|cdW91p92rlAEHgJafqYyxf1Q|16|26-777-409-5654|7838.30|HOUSEHOLD|carefully regular theodolites use. silent dolphins cajo\n138|Customer#000000138|5uyLAeY7HIGZqtu66Yn08f|5|15-394-860-4589|430.59|MACHINERY|ts doze on the busy ideas. regular\n139|Customer#000000139|3ElvBwudHKL02732YexGVFVt |9|19-140-352-1403|7897.78|MACHINERY|nstructions. quickly ironic ideas are carefully. bold, \n140|Customer#000000140|XRqEPiKgcETII,iOLDZp5jA|4|14-273-885-6505|9963.15|MACHINERY|ies detect slyly ironic accounts. slyly ironic theodolites hag\n141|Customer#000000141|5IW,WROVnikc3l7DwiUDGQNGsLBGOL6Dc0|1|11-936-295-6204|6706.14|FURNITURE|packages nag furiously. carefully unusual accounts snooze according to the fluffily regular pinto beans. slyly spec\n142|Customer#000000142|AnJ5lxtLjioClr2khl9pb8NLxG2,|9|19-407-425-2584|2209.81|AUTOMOBILE|. even, express theodolites upo\n143|Customer#000000143|681r22uL452zqk 8By7I9o9enQfx0|16|26-314-406-7725|2186.50|MACHINERY|across the blithely unusual requests haggle theodo\n144|Customer#000000144|VxYZ3ebhgbltnetaGjNC8qCccjYU05 fePLOno8y|1|11-717-379-4478|6417.31|MACHINERY|ges. slyly regular accounts are slyly. bold, idle reque\n145|Customer#000000145|kQjHmt2kcec cy3hfMh969u|13|23-562-444-8454|9748.93|HOUSEHOLD|ests? express, express instructions use. blithely fina\n146|Customer#000000146|GdxkdXG9u7iyI1,,y5tq4ZyrcEy|3|13-835-723-3223|3328.68|FURNITURE|ffily regular dinos are slyly unusual requests. slyly specia\n147|Customer#000000147|6VvIwbVdmcsMzuu,C84GtBWPaipGfi7DV|18|28-803-187-4335|8071.40|AUTOMOBILE|ress packages above the blithely regular packages sleep fluffily blithely ironic accounts. \n148|Customer#000000148|BhSPlEWGvIJyT9swk vCWE|11|21-562-498-6636|2135.60|HOUSEHOLD|ing to the carefully ironic requests. carefully regular dependencies about the theodolites wake furious\n149|Customer#000000149|3byTHCp2mNLPigUrrq|19|29-797-439-6760|8959.65|AUTOMOBILE|al instructions haggle against the slyly bold w\n150|Customer#000000150|zeoGShTjCwGPplOWFkLURrh41O0AZ8dwNEEN4 |18|28-328-564-7630|3849.48|MACHINERY|ole blithely among the furiously pending packages. furiously bold ideas wake fluffily ironic idea\n151|Customer#000000151|LlyEtNEXT6kkZ,kGP46H|19|29-433-197-6339|5187.02|HOUSEHOLD|regular dugouts: blithely even dolphins cajole furiously carefull\n152|Customer#000000152|PDrllSkScKLh4lr19gmUZnK|8|18-585-850-3926|1215.18|BUILDING|ously ironic accounts. furiously even accounts accord\n153|Customer#000000153|kDzx11sIjjWJm1|6|16-342-316-2815|5454.26|HOUSEHOLD|promise carefully. unusual deposits x-ray. carefully regular tithes u\n154|Customer#000000154|2LAlU fDHkOqbXjHHDqw1mJQNC|19|29-522-835-6914|4695.12|FURNITURE|nic packages haggle blithely across the\n155|Customer#000000155|l,sSphiStMgdrxpxi|0|10-566-282-8705|5902.85|AUTOMOBILE| sleep ironic, bold requests. regular packages on the quiet dependencies\n156|Customer#000000156|5OS0edX2Y6B1cf9wJNuOQWgrrZccXk9|9|19-723-913-3943|9302.95|AUTOMOBILE| regular foxes above the theodolites haggle \n157|Customer#000000157|HGEouzCcFrNd nBAdsCRjsMxKOvYZdbwA7he5w9v|15|25-207-442-1556|9768.73|BUILDING| pinto beans against the carefully bold requests wake quickly alongside of the final accounts. accounts\n158|Customer#000000158|2HaYxi0J1620aoI1CdFyrW,rWOy|10|20-383-680-1329|6160.95|AUTOMOBILE|ecoys. fluffily quick requests use flu\n159|Customer#000000159|KotsdDO6EHnysVu922s6pjZpG,vlT|10|20-888-668-2668|2060.06|HOUSEHOLD|cingly express somas haggle above the theodolites. pinto beans use special theodolites. theodolites sleep \n160|Customer#000000160|5soVQ3dOCRBWBS|13|23-428-666-4806|4363.17|MACHINERY|olites. silently ironic accounts cajole furious\n161|Customer#000000161|2oRkx,NtjFUh|7|17-805-718-2449|3714.06|MACHINERY|ptotes nag carefully instructions. silent accounts are. furiously even accounts alongside\n162|Customer#000000162|JE398sXZt2QuKXfJd7poNpyQFLFtth|8|18-131-101-2267|6268.99|MACHINERY|accounts along the doggedly special asymptotes boost blithely during the quickly regular theodolites. slyly \n163|Customer#000000163|OgrGcOnm4whd0f|21|31-863-349-4121|2948.61|FURNITURE| nag furiously furiously final requests. slyly s\n164|Customer#000000164|YDW51PBWLXLnbQlKC|4|14-565-638-9768|208.45|HOUSEHOLD|ironic, special pinto beans. ironic\n165|Customer#000000165|8pc6kwBmwBdEnfVP53aqL9DM4LymC4|0|10-927-209-5601|3349.92|HOUSEHOLD| requests. final ideas cajole quickly at the special, ironic acco\n166|Customer#000000166|15HWGtwoP77EJfd95HxtMSTZUelV8NOKne2|10|20-320-530-5920|2042.21|FURNITURE|the packages. blithely final packages are furiously unusual asymptotes. regular frets promise carefully u\n167|Customer#000000167|QNc2eOlRIzL6jpthwgDuB866uCIUPiOX|5|15-288-395-5501|1468.09|AUTOMOBILE|espite the ironic excuses. furiously final deposits wake slyly. slyly ex\n168|Customer#000000168|GDcL5qU86P8,oaTwVBCLE6asM8rlxpE,211uziU|12|22-354-984-5361|-808.56|FURNITURE|blithely final accounts sleep quickly along the regular ideas. furiously sly foxes nag across the\n169|Customer#000000169|NjhmHa7xrcjE|18|28-362-499-3728|4483.83|FURNITURE|fully unusual pinto beans. blithely express asymptotes lose carefully regular instructions? accounts b\n170|Customer#000000170|5QmxmYubNhn6HAgLwTvphevM3OmpZTGsM|15|25-879-984-9818|7687.89|BUILDING| regular requests. carefully regu\n171|Customer#000000171|RIhjJCrth89EU7xRSvN|7|17-513-603-7451|2379.91|MACHINERY|ly furiously final requests. slyly final requests wake silently pending, silent accounts. exp\n172|Customer#000000172|KwgdKUL1G2WacsMNF50yX|22|32-178-964-1847|1134.40|MACHINERY|losely regular, unusual instructions. \n173|Customer#000000173|Aue7KVz,FinSHpov Vk5ed,wSQ2BRSioJ0|9|19-443-196-8008|845.84|BUILDING|s pinto beans use thinly slyly regular packages. instructions print along the s\n174|Customer#000000174|R5 fCPMSeDXtUpp5Ax|23|33-845-455-8799|1944.73|FURNITURE|oldly even requests haggle quickly blithely ironic accounts. idly final foxes doze slyly pending dep\n175|Customer#000000175|8YK1ZyTqoY3wMWnExl4itPMLL793GpEZb6T|10|20-427-617-9922|1975.35|FURNITURE|ly final platelets are final pinto b\n176|Customer#000000176|9hBepY2uz88HlCqToOLgeU770u81FeL|13|23-432-942-8830|-375.76|FURNITURE|uriously. final requests sleep ironic packages. quickly\n177|Customer#000000177|6wzEKPyZE9dmBCJZ8e7x7fiiK,k|1|11-917-786-9955|7457.50|BUILDING|nal dolphins: blithely bold gifts wake slyly afte\n178|Customer#000000178|p HUSDg8Cgan4Fj8Drvcdz4gi4dSqV0a7n 0ag|21|31-436-268-6327|2272.50|FURNITURE|unts. blithely regular dependencies kindle pending deposits. quietly express deposits wake above the Tiresias-- ex\n179|Customer#000000179|djez3CWg0nnCiu60jsF|4|14-703-953-2987|-43.08|MACHINERY|st furiously. idly regular instructions wake fluffily slyl\n180|Customer#000000180|DSGW3RFoYJE opVw,Y3wGCGcNULZi|13|23-678-802-2105|-92.58|FURNITURE|lar accounts sublate above the slyly final\n181|Customer#000000181|YNviWd WrRkZvSw1OxIewBq|9|19-653-305-8440|3929.96|FURNITURE|final requests cajole furiously acro\n182|Customer#000000182|tdwvgepG316CCTHtMaF8Q|3|13-199-211-9023|4810.22|AUTOMOBILE|quickly against the blithely even deposits; epitaphs unwind quickly along the carefully regular excuses. furio\n183|Customer#000000183|aMAB2QSb8 86MAx|22|32-771-279-8154|4419.89|HOUSEHOLD|sual accounts across the slyl\n184|Customer#000000184|uoOpBuRr42f1WIqnVYAhxbAA9bkK6HUGpOt|21|31-739-340-5476|170.46|AUTOMOBILE|hely according to the furiously unusual accounts. furiously bold platele\n185|Customer#000000185|iHXzQgienOQ|5|15-760-572-8760|2788.76|BUILDING|t the ironic accounts. fluffily regular requests wake slyly ironic pinto beans. slyly unusu\n186|Customer#000000186|BeVr6MzaobBENXRBC8pmOmkByMJI|3|13-518-743-2576|8737.50|HOUSEHOLD|e slyly final dependencies. unusual instructions against the carefully pending instructions boost quickly\n187|Customer#000000187|OIlgR6oIRXV5g63q5YGudCjRD8kpod2p|4|14-716-294-6674|-774.22|FURNITURE|r deposits. carefully silent packages after the fluffily even instructio\n188|Customer#000000188|58Srs6gEEoD3ZfwgXDM1OayRiaSY6K9YsveWwV|5|15-613-528-7811|9533.37|BUILDING|st slyly special platelets. bold,\n189|Customer#000000189|r51HSq Rg8wQgF1CBfG1Vbye3GK|22|32-980-348-1114|-594.05|MACHINERY|sly express patterns. ideas on the regular d\n190|Customer#000000190|F2X,GhSqLz8k u0gWsirsraFaEDEo6vIGtOTaO1T|11|21-730-373-8193|1657.46|AUTOMOBILE|uickly-- fluffily pending instructions boo\n191|Customer#000000191|P1eCXsPWkv2y6ENQv|16|26-811-707-6869|2945.16|BUILDING|o beans hinder slyly bold accounts.\n192|Customer#000000192|rDmB2c9d1BJQ y6R9jTx86YI77D|10|20-750-712-2481|8239.96|MACHINERY|ely unusual packages are fluffily \n193|Customer#000000193|dUT4dtsPTZ6ZpkWLc,KGJCHY6JDJgPFH4|23|33-182-978-6287|8024.55|MACHINERY|y even theodolites. final foxes print along the final pinto beans. theodoli\n194|Customer#000000194|mksKhdWuQ1pjbc4yffHp8rRmLOMcJ|16|26-597-636-3003|6696.49|HOUSEHOLD|quickly across the fluffily dogged requests. regular platelets around the ironic, even requests cajole quickl\n195|Customer#000000195|WiqQD8hscyKekjMcSBA7AX 0AbxvBV|22|32-757-684-6845|4873.91|AUTOMOBILE| should detect blithely. quickly even packages above the deposits wak\n196|Customer#000000196|68RstNo6a2B|18|28-135-177-2472|7760.33|FURNITURE|accounts wake. express instructions according to the s\n197|Customer#000000197|UeVqssepNuXmtZ38D|1|11-107-312-6585|9860.22|AUTOMOBILE|ickly final accounts cajole. furiously re\n198|Customer#000000198|,7fcZHIUn,fUaQtK8U,Q8|1|11-237-758-6141|3824.76|AUTOMOBILE|tions. slyly ironic waters wa\n199|Customer#000000199|lBU3xll,a7e9TYm3 UyjDPCVMvnHKpq,9HW1X|4|14-136-924-5232|7654.31|FURNITURE|fully busy pinto beans. packages cajole around the express, bold packages! quickly ironic tithes\n200|Customer#000000200|x1 H5c66DUgH2pgNTJhw6eZKgrAz|16|26-472-302-4189|9967.60|BUILDING|e after the ironic, even realms. fluffily regular packages doze-- courts haggle carefully! blithely\n201|Customer#000000201|yWLtmd5usyjsCvyL1QJsBorC|2|12-759-183-9859|4614.40|MACHINERY| blithely even packages sleep carefully bold, unus\n202|Customer#000000202|Q0uJ1frCbi9yvu|7|17-905-805-4635|2237.64|AUTOMOBILE|fully along the carefully pending Tiresias; special packages along the carefully special deposits try to\n203|Customer#000000203|2fRlubh lWRinCs1nimADdn|1|11-886-563-6149|7960.63|MACHINERY| packages are. requests integrate regularly across th\n204|Customer#000000204|7U7u2KryFP|6|16-761-837-4820|-627.76|BUILDING|ages. accounts wake slyly. dolphins nag blithely. final, regular requests haggle blithely furiously even \n205|Customer#000000205|jOTQBGb nhfBMu3,LIN62WogLDBO0w|12|22-356-437-1311|7161.52|BUILDING| furiously pending accounts. ideas along the slyly final deposits cajole blithel\n206|Customer#000000206|xsg,ehRHS5OKqyBR5YtoPm8myz|9|19-976-832-3312|-274.79|AUTOMOBILE| the carefully regular foxes. regular accounts wake furiously braids. bold ideas are carefu\n207|Customer#000000207|ewz5JNnxJPmPGY|21|31-562-675-6475|-439.98|AUTOMOBILE|n theodolites against the evenly even requests boost carefully pinto beans! fi\n208|Customer#000000208|Abye1MwcNfY0KO6yqv,Wwe|19|29-859-139-6234|6239.89|MACHINERY|le carefully according to the quickly silent packages. quickly ironic packages affix according to the ruthles\n209|Customer#000000209|iBvmxOZV3qXMYQW3W4Oo7YFhdV|16|26-207-121-7721|8873.46|FURNITURE|deposits. furiously regular ideas across the quietly regular accounts cajole about the express packages. quickly reg\n210|Customer#000000210|13cFL9sG1nrGERURN9WZI0|20|30-876-248-9750|7250.14|HOUSEHOLD|nusual instructions sleep regular acc\n211|Customer#000000211|URhlVPzz4FqXem|13|23-965-335-9471|4198.72|BUILDING|furiously regular foxes boost fluffily special ideas. carefully regular dependencies are. slyly ironic \n212|Customer#000000212|19U0iZ3GtDdrsn|7|17-382-405-4333|957.58|BUILDING|symptotes are blithely special pinto beans. blithely ironic \n213|Customer#000000213|NpqMYBhBcWk8mnEta|24|34-768-700-9764|9987.71|HOUSEHOLD|al deposits. final instructions boost carefully. even deposits sleep quickly. furiously regul\n214|Customer#000000214|MpCwhcLrbcIM7AeKS9tRM09by|8|18-180-678-6165|1526.59|MACHINERY|grow. fluffily regular pinto beans according to the regular accounts affix quickly pe\n215|Customer#000000215|8H76xbBhde HY70BrYqGEFmVPXqlm8pgjjxh|9|19-564-446-4758|3379.20|FURNITURE|al pinto beans. ironic foxes serve. i\n216|Customer#000000216|LXH7wSv4I6GG6TAkLOyLcMh559a8Y|21|31-296-111-5448|-776.08|FURNITURE|hely at the pending warhorses; blithe\n217|Customer#000000217|YIy05RMdthrXqdfnNKud|23|33-159-298-3849|378.33|AUTOMOBILE|ven frays wake according to the carefully \n218|Customer#000000218| V1FCIeSseuyNGYfHS Rx0,sc4IsBfReV|4|14-480-931-8567|9541.19|MACHINERY|lar courts. furiously pending dependencies cajole blithely? fluffily regular deposits cajol\n219|Customer#000000219|eTjiL01eyoKiAe2WQoz3EpPg2lvSLeOu2X2wyxK|11|21-159-138-6090|9858.57|AUTOMOBILE|ckly multipliers. carefully eve\n220|Customer#000000220|TbUHVhkttz|16|26-201-301-7371|9131.64|BUILDING| even, even accounts are. ironic \n221|Customer#000000221|ripNyyPOewg8AahnZlsM|16|26-351-738-1001|1609.39|BUILDING| instructions above the regular requests cajole packages. pending, even \n222|Customer#000000222|gAPkFjwxX1Zq 2Yq6 FIfLdJ4yUOt4Al7DL18Ou|1|11-722-672-5418|8893.76|BUILDING|regular accounts haggle furiously around the c\n223|Customer#000000223|ftau6Pk,brboMyEl,,kFm|20|30-193-643-1517|7476.20|BUILDING|al, regular requests run furiously blithely silent packages. blithely ironic accounts across the furious\n224|Customer#000000224|4tCJvf30WagGfacqcAqmfCptu2cbMVcj2M7Y0W|15|25-224-867-3668|8465.15|BUILDING|counts. bold packages doubt according to the furiously pending packages. bold, regular pinto beans \n225|Customer#000000225|2HFk1E0fmqs|13|23-979-183-7021|8893.20|AUTOMOBILE|ages boost among the special foxes. quiet, final foxes lose carefully about the furiously unusual th\n226|Customer#000000226|ToEmqB90fM TkLqyEgX8MJ8T8NkK|3|13-452-318-7709|9008.61|AUTOMOBILE|ic packages. ideas cajole furiously slyly special theodolites: carefully express pinto beans acco\n227|Customer#000000227|7wlpEBswtXBPNODASgCUt8OZQ|13|23-951-816-2439|1808.23|MACHINERY|lar, ironic pinto beans use! quickly regular theodolites maintain slyly pending pac\n228|Customer#000000228|A1Zvuxjdpt8TZP6i41H3fn9csGqOJUm5x0NIS1LA|20|30-435-915-1603|6868.12|FURNITURE| blithely ironic theodolites \n229|Customer#000000229|Sbvjxgmwy4u6Ks1FH7lxo7toMmeU5dG|1|11-243-298-4029|7568.07|BUILDING|bold accounts haggle furiously even deposits. regular instruct\n230|Customer#000000230|CnR8xt3MYqID0tiHwYh|21|31-744-950-8047|1682.83|MACHINERY|c decoys impress even deposits. thinly final asymptotes \n231|Customer#000000231|WFOhG9Z9ohRdsyuYnPvBSv|10|20-825-880-1065|283.55|BUILDING|ly final deposits. fluffily ironic requests wake carefully carefully regular accounts. quickly sp\n232|Customer#000000232|oA9o,3YOXu2rzKONdd,cxpqCFXUv5kuxBYKp|22|32-283-563-2674|554.71|HOUSEHOLD|ges sleep. final, bold theodolites are quickly final packages. furiously ironic packages are slyly fi\n233|Customer#000000233|mFm45wZ7rV4VIbEE1F4|3|13-574-104-3221|3998.24|FURNITURE|st the special instructions. theodolites detect blithely according \n234|Customer#000000234|ILyuJbixVmrNEVxsfQOMFxByySs|18|28-243-424-1393|8383.51|AUTOMOBILE| fluffily regular ideas play s\n235|Customer#000000235|bp0rIBMh4fMdQnHBmMnB|3|13-350-790-6416|754.41|AUTOMOBILE|hely ruthless instructions again\n236|Customer#000000236|kcW,mM0rhIstAcVaol1,6DVkS FPKlhY|14|24-808-967-4503|5384.59|AUTOMOBILE|te slyly along the requests. carefully final requests sleep slyly blithe frets. furiously ruthless dep\n237|Customer#000000237|R dtznB5ocPPo|19|29-414-970-5238|-160.02|HOUSEHOLD|regular pinto beans sleep furiously ironically silent theodolites. quickly ironic courts after the deposits sleep f\n238|Customer#000000238|tE0lVKK3tz5AG2 Hal2XHwE485g5MX7|16|26-307-925-1236|3482.32|HOUSEHOLD|uffily ironic theodolites are. regular, regular ideas cajole according to the blithely pending epitaphs. slyly \n239|Customer#000000239|w8eRmMOmUTjVOkucbfcGDh2AqCixTOC|9|19-699-117-6988|5398.77|FURNITURE|uctions. furiously even dolphins haggle fluffily according to the furiously regular dep\n240|Customer#000000240|SXfeEOwRZsXArtY3C5UWqXgLcJBAMmaynaTJs8|9|19-756-548-7835|7139.68|MACHINERY|al accounts about the slyly pending p\n241|Customer#000000241|FBuwHkPR450PvnZnAezcaeMaS,hX3Ifdk|9|19-344-614-2207|6569.34|AUTOMOBILE| across the enticingly even requests. blithely iro\n242|Customer#000000242|apgzK3HWAjKHFteJ16Tg3OERViesqBbx|3|13-324-350-3564|1975.41|MACHINERY|riously ironic pinto beans cajole silently. regular foxes wake slyly. bravely \n243|Customer#000000243|te2FOn8xJzJinZc|7|17-297-684-7972|620.73|AUTOMOBILE|nic deposits. evenly pending deposits boost fluffily careful\n244|Customer#000000244|FBVbCpEVaFaP8KogqQO2VuXeVx|15|25-621-225-8173|2506.38|HOUSEHOLD|encies. requests nag carefully. regularly final accounts h\n245|Customer#000000245|IseFIO7jTGPTzAdZPoO2X4VX48Hy|12|22-952-232-2729|3720.15|MACHINERY|s. regular foxes against the s\n246|Customer#000000246|WrRUR0ds6iypmopww9y9t0NJBRbke78qJm|15|25-608-618-2590|9584.96|AUTOMOBILE| requests shall have to integrate furiously pending courts. sil\n247|Customer#000000247|N8q4W4QQG2mHY47Dg6|20|30-151-905-3513|8495.92|HOUSEHOLD|es affix furiously regular deposits. blithely ironic asymptotes after the blithely e\n248|Customer#000000248|mgT15r8asLyaED|10|20-447-727-8914|8908.35|FURNITURE|s detect blithely. blithely pending dolphins along the fluffily final accounts haggle fu\n249|Customer#000000249|0XE2fhX7j2uivaHBrFuRl1NoJnaTSIQT|3|13-226-455-7727|-234.01|MACHINERY|its are after the special deposits. ironic, final deposits against the slyl\n250|Customer#000000250|9hif3yif6z8w8pW88F755PU7uz|16|26-464-852-1461|2869.97|FURNITURE|s. slyly unusual instructions cajole quickly carefully bold dep\n251|Customer#000000251|Z9fdQmv07C3k hxwt9nchhuQiqC4wox85se8EW7L|13|23-975-623-5949|9585.32|HOUSEHOLD|fully blithely regular requests. fluffily even dugouts detect furiously final ideas. sometimes ironic depos\n252|Customer#000000252|db1bPFF xUkJYzvE3cBtqYeDn2 u|16|26-330-347-9201|3561.74|FURNITURE|ngside of the pending foxes. furiously ironic requests wake. blithely ironic acco\n253|Customer#000000253|naGyIRPFPH E|15|25-461-140-9884|9139.52|AUTOMOBILE| regular deposits sleep against the accounts. foxes cajole carefully special \n254|Customer#000000254|vQ,pEzMQaFgJzK4TJ2eA|1|11-451-622-6325|1915.35|MACHINERY|equests. carefully ironic deposits detect carefully abo\n255|Customer#000000255|I8Wz9sJBZTnEFG08lhcbfTZq3S|3|13-924-679-8287|3196.07|BUILDING|ges mold. bold, regular courts boost furiously at the \n256|Customer#000000256|eJ6AggYh80JMEzZNwYK4CIC2flT|10|20-229-271-4429|1299.92|HOUSEHOLD|ld boost about the carefully ironic foxes. slyly special packages cajole alongside of the slyly final accounts. q\n257|Customer#000000257|LyIa26EXYaSU|7|17-816-687-2155|-339.85|AUTOMOBILE|s cajole quickly along the ironic pinto beans: even, regular foxes are \n258|Customer#000000258|7VbADek8qYezQYotxNUmnNI|12|22-278-425-9944|6022.27|MACHINERY|about the regular, bold accounts; pending packages use furiously stealthy warhorses. bold accounts sleep fur\n259|Customer#000000259|kwh9i86Wj1Zq14nwTdhxapIkLEI|5|15-907-674-2046|3335.29|HOUSEHOLD|furiously unusual instructions. s\n260|Customer#000000260|CrHY2zx4vner4|1|11-708-529-9446|9196.11|MACHINERY|carefully. furiously bold accounts nag furiously carefully regular accounts-- final decoys prin\n261|Customer#000000261|dXkVi8qahjP|12|22-494-898-7855|7094.22|AUTOMOBILE|he special instructions integrate carefully final request\n262|Customer#000000262|DcUOAFBxMu8oGKvIqbDx7xgeZ|4|14-698-169-5201|1561.80|AUTOMOBILE|ress packages above the ironic accounts are against the ironic pinto beans. carefully final accoun\n263|Customer#000000263|Y2pxeGWkTyaq,0RCzIbZ3|1|11-276-906-3193|1162.03|FURNITURE|usly ironic theodolites cajole furiously. final ep\n264|Customer#000000264|24Akixb4hqpRD|11|21-881-683-3829|3195.83|MACHINERY|ular packages cajole blithely a\n265|Customer#000000265|sthiqpj6CPAKbD7BBSz9ulRuF9d,ebfaiTc|17|27-716-734-2046|8275.80|MACHINERY|lar, ironic platelets. furiously unu\n266|Customer#000000266|VSIEruiMdDvjDaTQxkuK60Yw3AGxO|0|10-474-243-3974|5481.00|HOUSEHOLD|ccounts. quickly ironic excuses after the regular foxes wake along the ironic, fina\n267|Customer#000000267|el7 bYzj1USp6T5i3KpfZ43jKegbdO,Jd69|15|25-402-954-8909|3166.94|AUTOMOBILE| detect slyly alongside of the foxes. closely regular pinto beans nag quickly of the blithely bold r\n268|Customer#000000268|tkSLQoOpfOa601itad05EcN0UmhjZXdyKRc0r|3|13-720-469-5207|6821.01|MACHINERY|press ideas print quickly. fluffily unusual deposits use blithely eve\n269|Customer#000000269|J7kLF9iPOQA 7CVwAmQRpwfZPDJ2q5Seu2Vj1gh|14|24-570-874-6232|7667.35|MACHINERY| close packages-- quickly regular instructions sleep. carefully \n270|Customer#000000270|,rdHVwNKXKAgREU|7|17-241-806-3530|9192.50|AUTOMOBILE|ldly final instructions mold carefully along the ironic accounts.\n271|Customer#000000271|O,HceV3 XE77Yx|6|16-621-282-5689|1490.35|MACHINERY|ly pending deposits cajole slyly sl\n272|Customer#000000272| YDjKpjXEe0A6rDE|2|12-324-877-9650|-746.03|MACHINERY|he regular requests. slyly special \n273|Customer#000000273|sOA,alhAw1juArjRLOd|2|12-197-772-5736|-675.05|FURNITURE|ng frets sleep. slyly express dolphins doubt ironically ironic accounts. final de\n274|Customer#000000274|adesXwNumnPqsKgsE1groEAwdKNgZ|19|29-330-389-1442|4425.42|FURNITURE|gular dependencies. ironic foxes haggle du\n275|Customer#000000275|M1UCTKrZLOgSyr|22|32-194-864-6861|5067.31|AUTOMOBILE|y regular deposits. fluffily ironic packages cajole along the \n276|Customer#000000276|iSWxETEMKe5cF|16|26-716-357-3851|2292.67|AUTOMOBILE|eans. even, ironic accounts affix sl\n277|Customer#000000277|BWGsQevHk0BfRJV3RRB ElFc|23|33-696-831-5394|8876.10|BUILDING|phins; bold, final accounts print. carefully silent \n278|Customer#000000278|4jqLjG 2aeYMFEJi|20|30-445-570-5841|7621.56|BUILDING| pending, express requests cajole carefully special packages. blithely pending accounts affix furiously. fluffily \n279|Customer#000000279|9t2Wo1jK1TYnDGg6ODSMGf1W9hRT3F3VK5zxJOC|9|19-220-605-9025|9663.23|AUTOMOBILE|l platelets sleep fluffily against the fluffily enticing excuses. blithely special requests wake somet\n280|Customer#000000280|3fDiGmN64En0ei|11|21-537-461-3965|3952.84|BUILDING|accounts. quiet deposits sleep. slyly even instructions detect about the blithely bold instru\n281|Customer#000000281|x5gJ8IGm2Fo9Jigt|6|16-809-382-6446|4361.70|BUILDING|fully quiet ideas detect quickly even packages. regular instructions accor\n282|Customer#000000282|wcCc, y1996DnOwnXu1i|18|28-251-599-2415|1125.45|HOUSEHOLD|ole daringly against the carefully ir\n283|Customer#000000283|jysHDuTKUKYEjdBhtI LqTttTp 7i2kvx1 O3A|7|17-111-303-1282|4450.03|AUTOMOBILE|y alongside of the accounts. slyly express dependencies wake quickly above the carefully ironic package\n284|Customer#000000284|2ZgAkaBgb6aigORfIfUd3kHbPi42|6|16-161-235-2690|593.52|AUTOMOBILE|lar gifts. carefully even deposits boost! furiously even braids use afte\n285|Customer#000000285|ApUL7bgFMUXGXewpoQyQOSnLeL9Vc1rrkW |20|30-235-130-1313|7276.72|FURNITURE|dolphins after the slyly ironic packages boost furiously among the furiously pending theodolites. bl\n286|Customer#000000286|7 7uVDrpkWuozyEd|22|32-274-308-4633|-109.73|HOUSEHOLD|ly special accounts haggle slyly slyly fluffy req\n287|Customer#000000287|KTsaTAJRC0eMYkyFm7EK3eeamHs7s|4|14-330-840-6321|1734.18|MACHINERY|requests. bold, silent depths lose f\n288|Customer#000000288|eEs5rwc9AOJaKhvV|2|12-674-136-5397|5339.43|HOUSEHOLD| furiously about the carefully ironic packages. express reques\n289|Customer#000000289|NUilehg0nVOkK3K1SW0,BAHCeST2JqKzuTMoGS|10|20-456-773-7693|-215.75|AUTOMOBILE|ending foxes across the carefully\n290|Customer#000000290|8OlPT9G 8UqVXmVZNbmxVTPO8|4|14-458-625-5633|1811.35|MACHINERY|sts. blithely pending requests sleep fluffily on the regular excuses. carefully expre\n291|Customer#000000291|ZlLNbGxnQYMubQ9K|8|18-657-656-2318|4261.68|HOUSEHOLD|e slyly silent deposits. bold deposits haggle slyly special packages. furiously bold requests cajole carefully abo\n292|Customer#000000292|hCXh3vxC4uje9|11|21-457-910-2923|2975.43|HOUSEHOLD|usly regular, ironic accounts. blithely regular platelets are carefully. blithely unusual ideas affi\n293|Customer#000000293|7ynwX7lZ3o2cmAWSkKAc3edKa 8yT|2|12-887-984-5485|-43.79|MACHINERY|ironic foxes are final packages. requests about the furiousl\n294|Customer#000000294|hSaNqI1P2IyEFHY0r0PsPkMqt|18|28-187-946-4260|-994.79|BUILDING|bold packages. regular, final asymptotes use quickly fluffily even waters. blithely express requests wake into th\n295|Customer#000000295|mk649IH6njR14woTVZ1cxtlNs URxBHD5o5z2|0|10-340-773-4322|9497.89|HOUSEHOLD|play according to the quickly ironic instructions-- unusual, bol\n296|Customer#000000296|4eyqk2zpg4m V JGEtgwNmCq3c|15|25-875-178-1959|8081.52|BUILDING|es need to affix furiously. ironic, final foxes are against the regular instructions: pinto beans haggle q\n297|Customer#000000297|hzg,409pj0|15|25-915-518-8800|7096.32|HOUSEHOLD|de of the regular asymptotes detect slyly ironic theod\n298|Customer#000000298|jFKF3w 8aegECg7mP,qtuR9IsTSYQlEXq|21|31-542-157-4074|3812.84|BUILDING|sleep slyly. stealthy, bold pinto beans sleep blit\n299|Customer#000000299|3F3Q0fTkjIv1UfJbcN7|4|14-948-474-7353|5380.50|HOUSEHOLD|tes sleep fluffily. furiously regular requests boost fluffily evenly even asympt\n300|Customer#000000300|I0fJfo60DRqQ|7|17-165-193-5964|8084.92|AUTOMOBILE|p fluffily among the slyly express grouches. furiously express instruct\n301|Customer#000000301|FtFq9Cgg5UAzUL|7|17-265-345-9265|9305.05|HOUSEHOLD|ular, regular notornis sleep along the furiously pending foxes\n302|Customer#000000302|cJ3cHoAjAiaxTU2t87EJM|4|14-152-594-2967|1107.42|MACHINERY|dolphins haggle fluffily across the final requests. regularly unusual sentiments detect fluffily requests. regular\n303|Customer#000000303|5pSw0OIoNRcpyTEEI1gZ6zRMyJ0UGhJdD|3|13-184-254-6407|9339.57|AUTOMOBILE|mise ironically against the unusual foxes. deposits cajole asymptotes. ironic ideas shall have to sleep\n304|Customer#000000304|Cilvb3k8ghDX4|0|10-321-698-7663|9217.55|MACHINERY|s integrate at the carefully ironic instructions. fin\n305|Customer#000000305|x8kcl,R4Wk|11|21-250-654-3339|4356.59|FURNITURE|nts. even, regular courts nag. dugouts use blithely a\n306|Customer#000000306|ADoOEIr5aQcLIoGJM6nCvPEP 91|10|20-109-305-9629|3268.01|AUTOMOBILE|ill have to are. final, express deposits hag\n307|Customer#000000307|xvkJ13gs7GH|13|23-836-934-5394|346.59|FURNITURE| ironic platelets nag against the bold pinto \n308|Customer#000000308|c9WuNBiEYmGxeBmZaELg WWb|9|19-992-128-2013|4150.76|HOUSEHOLD|ilent accounts haggle carefully unusual dolphins. carefully regular requests wake along the \n309|Customer#000000309|6Jg4ECVS2u7i,E|21|31-231-377-9535|8824.78|FURNITURE|lyly. furiously enticing instructions haggle. carefull\n310|Customer#000000310|QZnc5mkLIPh6JGrzcHmRzCiL0AmdE92vyM|1|11-838-647-9285|3186.57|FURNITURE|mise fluffily blithely ironic courts\n311|Customer#000000311|dvpNARle3mR19GD4s2gpEbkL2mZV3uvV6P|23|33-919-292-8822|6589.50|AUTOMOBILE|essly even escapades. blithely regular Tiresias cajole blithely furiously close packages. furiously ironic pi\n312|Customer#000000312|cH6XucXV0V|6|16-316-482-2555|-178.84|AUTOMOBILE|e slyly. furiously regular pinto beans wake slyly according to the fluffily even excuses. ca\n313|Customer#000000313|Ay52vCrTXsSmp7TmQ1kujvuItfLGx|0|10-401-786-6040|6115.81|HOUSEHOLD|g to the even dependencies. accoun\n314|Customer#000000314|8,tdTVYGYoYRaAKwG 6aDJna4Cfjt,F9DDCC2|13|23-366-243-4713|2394.92|MACHINERY|ets alongside of the slyly pending pinto bean\n315|Customer#000000315|pXaKKTCTyc UI3tglBaWRimosymG6ZyOCyb6Vb3M|7|17-442-286-3594|348.58|FURNITURE|s. slyly regular sentiments are carefully. slyly ironic asymptot\n316|Customer#000000316|zE dN3aqjaG|8|18-171-394-5011|4571.78|MACHINERY|egular ideas cajole around the ironic, pending deposits. furiously pending dolphins serve blithely regular \n317|Customer#000000317|uOeuL8DG1j|19|29-615-537-8871|956.88|HOUSEHOLD|ages. hockey players are. dependencie\n318|Customer#000000318|PtJQn0IjYtShb1f2uYTYBnnmUeGNiwcALU|0|10-229-548-7118|9149.98|HOUSEHOLD|nding requests. special, bold instruction\n319|Customer#000000319| UQ5mF3sdoZT2|6|16-734-928-1642|1834.36|FURNITURE| packages use slyly always ironic deposits. unusual, even notornis above\n320|Customer#000000320|pO8rWSwK j|12|22-358-857-3698|6082.74|MACHINERY|ing requests. furiously regular accounts hinder slyly. final, regular theodolites against the slyly quiet requests\n321|Customer#000000321|g3,8g XHACSvjZtJuiNk5BYiyPFnIxg|20|30-114-675-9153|7718.77|FURNITURE|special requests! express dugouts can affix furiously blithely regular platelets. fu\n322|Customer#000000322|bWRyCyjH5OfGX|20|30-660-202-7517|4489.98|HOUSEHOLD|usual sauternes are among the slyly even instructions! thinly regular \n323|Customer#000000323|ZLnVZ CXRi2,QDrlo|18|28-347-223-6024|1137.67|AUTOMOBILE|ely special foxes. express, final excuses across the packages are quickly amon\n324|Customer#000000324|fiW1n6dLSVRkXj7kU1768UI2w1vMxEde5a |2|12-722-560-7023|806.59|FURNITURE|, regular requests kindle slyly furio\n325|Customer#000000325|Z I43vl3ta3iYmjXNaSM d6Pe24ibjhdvPSi|15|25-823-702-9630|2377.34|HOUSEHOLD|nal foxes alongside of the always bold\n326|Customer#000000326|nWDOTh6X019pfBtV3ikACYZiMjGykLrFnuyAo2|2|12-447-614-7494|1906.52|HOUSEHOLD|ckey players. carefully ironic a\n327|Customer#000000327|UyKulwfNnX4l4ba1vQtwCWw8WNP50U8DCU|8|18-606-718-3062|8762.16|MACHINERY| unusual braids. daringly final ideas are quickly c\n328|Customer#000000328|9pu j2HoEf1uhiY3jxE9l9fCRfjoVU|5|15-817-180-1487|6709.90|BUILDING|y about the daring accounts. furiously thin escapades integrate furiously against the furiously ironi\n329|Customer#000000329|67r6XnIxUVgAc3pRX8tmGOw|11|21-106-357-8302|-651.91|BUILDING|ans. fluffily unusual instructions haggle about the slyly ironic platelets. never regular pinto beans sleep fl\n330|Customer#000000330|UfNb7T9CTCnsfN3b|20|30-476-852-2371|8244.73|MACHINERY|en pinto beans. quickly final excuses haggle furiously. slyly pendin\n331|Customer#000000331|Ug e2IBbl,LJuqjNz5XeQV|5|15-411-430-7917|170.27|AUTOMOBILE|r the silent ideas. carefully ironic deposits was carefully above the furiously even excuses. evenly regu\n332|Customer#000000332|Jfosq,G6ziag7M04IvCx7SMRafyYvSI,Do|22|32-767-972-2596|-267.09|HOUSEHOLD| around the pinto beans. final theodolites haggle\n333|Customer#000000333|heiloGYs Yey7NKhEFoiNhUBb,QFbjtn5wt|11|21-908-534-7709|8018.89|AUTOMOBILE|uriously close theodolites! slyly express foxes cajole-- final pinto beans boost blithely along the ironic\n334|Customer#000000334|OPN1N7t4aQ23TnCpc|4|14-947-291-5002|-405.91|BUILDING|fully busily special ideas. carefully final excuses lose slyly carefully express accounts. even, ironic platelets ar\n335|Customer#000000335|d2JCYLr2F9tC1AZMIvbIYPDQA|21|31-772-165-3138|6837.46|HOUSEHOLD| requests haggle carefully about the quickly special escapades. regular a\n336|Customer#000000336|yC zy1i6AGrnykrV McJyjg|2|12-345-190-9898|9241.49|AUTOMOBILE|es. dependencies lose carefully blithely regular deposits. t\n337|Customer#000000337|EluRTlO4pE7u0XSKKyvKvVyt4sADWFRLZuiyn|0|10-337-165-1106|-270.59|MACHINERY|ld requests sleep quickly. carefully express tithes wake carefully ac\n338|Customer#000000338| aiYAeWgI0okGSJv7OgvKqMvPLhxF3blT8josX|23|33-302-620-7535|4092.49|FURNITURE|ckages nag blithely regular requests: carefully final packages between the slyly regular instructions sleep \n339|Customer#000000339|jUs1Im28boIduGhp5vbKK50gM5ov7xH9G|24|34-992-529-2023|8438.07|HOUSEHOLD|ix. ironic, special tithes detect dog\n340|Customer#000000340|WRnPrKQmAmoMQgHQERoVOhyTklcHMajJlc|2|12-730-681-4571|4667.12|BUILDING|es sleep according to the even, unusual Tiresias. carefully bold packages haggle. furiously pending s\n341|Customer#000000341|4,zQfld2YV9TSeNgCSOvqlxhJvVW8WD|9|19-870-813-8585|8247.11|FURNITURE|low, special platelets alongside of the even, bold theodolites are carefully \n342|Customer#000000342|SpDDdUfraEAfCULAuGLE|18|28-690-119-9571|7186.74|AUTOMOBILE|luffily final ideas. finally unusual requests boost slyly above the furio\n343|Customer#000000343|ejvvSNHIkJVm8I1zpQINNn5yyJbA|3|13-877-910-5134|5521.36|HOUSEHOLD| unusual requests cajole blithely about the carefully express ideas. blithely even excuses above the pint\n344|Customer#000000344|Zasc8,E0VVY|2|12-810-788-6699|-544.95|FURNITURE|le according to the regular instruction\n345|Customer#000000345|dGFK ICPKxnsAzlX4UYOUf,n200yyEWhIeG|9|19-209-576-4513|1936.77|AUTOMOBILE|en pinto beans nag along the slyly regular deposits. slyly ir\n346|Customer#000000346|K61SvIue3Emcwfel,7f9tO5WyJ58MbT7k3iS|2|12-100-890-4659|238.14|FURNITURE|ickly even pinto beans affix across the bravel\n347|Customer#000000347|qRT7WRrnykLDfTc5Ei|1|11-519-832-9913|7348.92|BUILDING|ts use blithely blithely regular theodolites. even requests after the\n348|Customer#000000348|ciP7BWkhOe1IbbVGlqJePBI6ZwqENkS|13|23-986-141-5327|3310.49|HOUSEHOLD|al foxes are on the carefully final excuses. careful dependen\n349|Customer#000000349|vjJBjxjW9uoRZP02nS p6XY5wU6Ic,6xHpxUKA|23|33-818-229-3473|-565.35|BUILDING|y. bold, ironic instructions after the theodolites sleep blithely ironic packages. ideas c\n350|Customer#000000350|G vBMGVmIOHl7tc4HeNMiMkKY|15|25-960-809-3690|19.31|BUILDING|tions. quietly unusual accounts sleep blithely afte\n351|Customer#000000351|De35Hx1QiyS0uy|7|17-873-420-4342|3419.54|AUTOMOBILE|telets haggle blithely against the ironic\n352|Customer#000000352|HqhIE5GRTK0dFtWpJUQENU4aa1bwdsUBEWtzUw|9|19-906-158-8420|6257.88|HOUSEHOLD|ts are. blithely special requests wake. furiously bold packages among the blithely eve\n353|Customer#000000353|eftGCmL4b5rAKdvUe9biJXzAH|10|20-733-644-2244|3199.03|BUILDING|nal theodolites nag carefully. requests wake. slyly ironic ideas according to the blithely pe\n354|Customer#000000354|sV3WgvJA06WngO4|2|12-545-101-2447|7095.95|BUILDING|. regular, final requests cajole fluffily. express attainments wake slyly until the even acco\n355|Customer#000000355|205r3Xg9ZWjPZNX1z|14|24-656-787-6091|8727.90|FURNITURE|ly bold requests detect furiously. unusual instructions sleep aft\n356|Customer#000000356|9RfNXUJivKTonL2bp1eG5IT|10|20-415-457-4421|2934.06|FURNITURE|al packages haggle always. daringly bold inst\n357|Customer#000000357|l2C0Xkdib4t4 qKFUcRDOhRQMK7U0|18|28-452-965-8560|8747.36|AUTOMOBILE|ress platelets cajole fluffily final accounts: slyly ironic foxes s\n358|Customer#000000358|F  z jplpUKWz1Hn7p3ez2qTsiIh|5|15-457-255-3822|-44.66|MACHINERY|e furiously pending requests. slyly bold requests wake deposits. furiously express\n359|Customer#000000359|z4lUH9ssc3K2w0UjRIuNRrdqw|14|24-608-547-4751|6375.23|FURNITURE|ifts wake fluffily ironic ideas. slyly ironic deposits above the \n360|Customer#000000360|S,6ajyDFO3WUQ0Qr|17|27-604-646-1645|6542.83|FURNITURE|engage. quickly final platelets about the fluffily unusual accounts wake \n361|Customer#000000361|l0F8jMJVe63cb|20|30-164-267-4590|7451.84|BUILDING|fully busy ideas. regular foxes cajole \n362|Customer#000000362|UscV00TNrNTDddxF7BTk|17|27-651-653-4122|6149.01|AUTOMOBILE|ut the fluffily ironic platelets. ironi\n363|Customer#000000363|2Koh mYARhsVcFn0U2Abt35qIyedAr1TxP|17|27-460-529-3937|-573.86|HOUSEHOLD|s. carefully unusual deposits are foxes. furiously even foxes nag carefully according to the furiously express \n364|Customer#000000364|SQ3b5Q5OtrmmZjJ87tq,o1TiXKVJQ0M7ZOuud|23|33-492-647-4972|32.24|HOUSEHOLD| dependencies? pending requests use carefull\n365|Customer#000000365|QiZRz y1xU|24|34-708-696-5226|737.03|HOUSEHOLD|counts. unusual packages are blithely foxes. unusual dinos\n366|Customer#000000366|pPQektSfn55AC7s9SRFkj07I2yXqakvCa|3|13-915-531-6826|-729.74|MACHINERY|nos wake quickly. regular, regula\n367|Customer#000000367|yZaDoEZCqt2VMTVKoZUkf6gJ4yj|13|23-939-319-4691|9108.65|HOUSEHOLD|eodolites under the ironic, stealthy requests affix furiously among the unusual tit\n368|Customer#000000368|9p ReFA4fseKWYUaUHi|22|32-552-596-4994|84.72|MACHINERY|ic asymptotes. quickly special packages along the bravely bold depos\n369|Customer#000000369|ge1XhgI3ADIkvLr5GPMqpup,hzlTVv|8|18-333-644-9832|2881.06|FURNITURE| theodolites? quickly quick foxes are fluffily slyly regular instructions. fluffily\n370|Customer#000000370|oyAPndV IN|12|22-524-280-8721|8982.79|FURNITURE|ges. final packages haggle quickly. slyly bold \n371|Customer#000000371|dnxjCYwhuSHx 9KX38nV0R16fG|22|32-119-346-2028|7789.14|AUTOMOBILE|equests shall boost furiously special pinto beans. express, ironic ideas sleep across the ironi\n372|Customer#000000372|aKPMNZfbgV0neVIBo|19|29-226-339-6392|-921.91|MACHINERY|. furiously even foxes sleep at the forges. bold accounts sleep after the ironic theodolites. ironi\n373|Customer#000000373|2hrQ wHkbaNlJCzY,mVkugMIE 8ryNlaA3JHDTjJ|20|30-883-170-4010|2354.06|MACHINERY|requests wake blithely even packages. slyly ironic deposits haggle blithely \n374|Customer#000000374|fg4eklU1,UaFOan|22|32-282-723-3627|6718.78|AUTOMOBILE|ges are carefully. slyly ironic deposits about the fin\n375|Customer#000000375|e53JADEeGvM1ikhN7aa|15|25-575-273-9756|5562.22|HOUSEHOLD|st the pending accounts. final courts above the pending pinto beans use furiously ironic requests. dolphins \n376|Customer#000000376|4NwsvFQU T4mSgzvU1Rx2ZtHOGyaNyhe|16|26-437-952-8986|4231.45|AUTOMOBILE|gs cajole quickly. bold asymptotes wake regularly along the quickly \n377|Customer#000000377|PA4levhyD,Rvr0JHQ4QNOqJ9gW YXE|23|33-260-610-4079|1043.72|MACHINERY|. slyly regular ideas cajole blithely. slyly ironic foxes are carefully after the thinly special accou\n378|Customer#000000378|133stqM9 LT,a2BSlbm49 nXreFggaZgW6P6J|22|32-147-793-4825|5718.05|BUILDING|ackages haggle fluffily ironic packages.\n379|Customer#000000379|t3QzCf,q1NbshmjOIUY|7|17-228-550-9246|5348.11|AUTOMOBILE|l deposits cajole blithely blithely final deposits. express, even foxes grow carefully about the sile\n380|Customer#000000380|n2w3Jd1bipwICbOVgrELzcNRexmWSklo|21|31-538-493-4229|2755.46|BUILDING|riously special accounts. slyly final accounts sleep; blithely special requests integrate carefully slyly en\n381|Customer#000000381|w3zVseYDbjBbzLld|5|15-860-208-7093|9931.71|BUILDING|t regular, bold accounts. carefully quick packages haggle. care\n382|Customer#000000382|UdgAMamK5JnSykA,ZPfR5W5zRFatDUye|8|18-942-650-6657|6269.42|AUTOMOBILE|. blithely express notornis according to the blithely even requests are never fina\n383|Customer#000000383|iBIHYgXvVDpu6qq7FlqXVcAIDAzv4qs|2|12-868-920-9034|-849.44|MACHINERY|slyly express ideas haggle blithely unusual dugouts. ironic pinto beans are ironic ideas.\n384|Customer#000000384|kDDMb3ML nUSu2Sn7CUHyZVedAFUx9|9|19-271-453-8361|-614.30|HOUSEHOLD|olites. express, unusual dolphins cajole carefully about the \n385|Customer#000000385|zJvPI24TSPpiFzYfu3RvTKQ9|3|13-741-675-6890|2457.09|AUTOMOBILE|rs. blithely ironic deposits nag furiously across the furiously ironic accounts. bold deposits sleep express\n386|Customer#000000386|DeQxsCxixT8RQ7JV6mddRYGDGQ2WM94|24|34-193-143-1425|232.01|BUILDING|counts. blithely permanent deposits wake slyly! unusual, even theodolites u\n387|Customer#000000387|Yj 9g1mNu00rKRkc1ovOmptsPI|18|28-694-363-3673|3404.23|HOUSEHOLD|oach. blithely regular instructions sublate across the quickly regular ideas. qui\n388|Customer#000000388|dV4lqEufXkF8R|7|17-856-814-6352|1938.05|HOUSEHOLD| carefully bold deposits: final pinto beans sleep slyly idl\n389|Customer#000000389|ij8KNM0,HRvIvnvY w8jQK4zvr1EOO9YM|9|19-264-943-1253|-307.61|AUTOMOBILE|o beans affix fluffily. slyly ironic notornis wake \n390|Customer#000000390|Nsc3VZZnVsw0mLAnqqzVz,|4|14-812-253-6693|8862.18|HOUSEHOLD| final packages promise quickly. pending theodolites haggle quickly above the doggedly ironic\n391|Customer#000000391|q10SV05KB1038lzUR8P|11|21-604-451-4462|4801.30|HOUSEHOLD|le blithely final forges. furiously even deposits cajole fluffily even patterns. furious\n392|Customer#000000392|H7M6JObndO|17|27-601-793-2507|8492.33|BUILDING|efully bold ideas. bold requests sleep carefully blithe instructions. carefully final accounts are blithely quickly \n393|Customer#000000393|RSELskV44I3LFA9VLGY2Qe|20|30-749-949-5915|3593.57|FURNITURE|ake furiously express notornis. pending accounts hang slyly slyly blithe theod\n394|Customer#000000394|nxW1jt,MQvImdr z72gAt1bslnfEipCh,bKZN|23|33-422-600-6936|5200.96|MACHINERY| instructions. carefully special ideas after the fluffily unusual r\n395|Customer#000000395|b06rg6Cl5W|15|25-804-388-6600|4582.28|HOUSEHOLD|s mold blithely regular platelets. slyly silent instructions use slowly slyly specia\n396|Customer#000000396|miE7JrCdGpQkF4zYJ27tBdSu IYhQ HXx0 |22|32-902-936-4845|1433.50|BUILDING|xcuses. regular pains wake slyly across the ruthlessly ironic dependencies. e\n397|Customer#000000397|EzR2BKJ85SmBDS|7|17-103-357-8777|709.46|FURNITURE|al theodolites. regular accounts are regular, silent foxes. unusual asymptotes above t\n398|Customer#000000398|cq9NmtIT4b6JB8L79iLzljlHs4 3|15|25-110-215-3747|8865.61|HOUSEHOLD|l deposits breach slyly ironic asymptotes. carefully pend\n399|Customer#000000399|ZBvzMa6N1wdCGaPmG13xVusIxdjSiA94jTXN|8|18-882-664-5454|7358.53|BUILDING|yly even excuses. ironic theodolites wake furiously. blithely regular pinto beans cajole. fin\n400|Customer#000000400|U23zy17EPxqmJn7neVc|14|24-522-746-1247|-98.46|BUILDING|fully bold accounts cajole bravel\n401|Customer#000000401|aKALIG526OK4veQfUh2KmKcE,oRyg|19|29-667-766-5291|4146.43|BUILDING|l instructions wake. slyly express deposits us\n402|Customer#000000402|8Cw4p1m1gKYVUgomkAq,es1ZtrnmHaO|6|16-950-729-1638|2106.67|AUTOMOBILE|dolites. furiously regular theodolites integrate furiously. bravely bold requests are. furiously\n403|Customer#000000403|9,BVYegfkFLsEMDkeVW|14|24-753-433-1769|6693.36|HOUSEHOLD|al hockey players; ironic dependencies after t\n404|Customer#000000404|2orgvLJ05jOvM292mhkS7iJmHG0jk|22|32-840-785-1776|7408.73|BUILDING|uickly brave requests haggle furiously carefully special idea\n405|Customer#000000405|mCQNH1rJtqjjQ9Piauc2bZr4pRFydscZtbD9d|10|20-509-301-7901|7519.14|MACHINERY|nts. pending, express foxes sleep? ironic, pending instructions haggle. ironic, pending theodolites detect slyly. bl\n406|Customer#000000406|j1fOG9WsIr2JI6Yi9jgJ M|9|19-426-693-4043|4286.94|FURNITURE|nal foxes. unusual pinto beans wake. special excuses cajole ironic \n407|Customer#000000407|cfCP9bE3HnI|1|11-975-454-8499|9537.08|MACHINERY|ect among the carefully regular theodolites. regular dep\n408|Customer#000000408|TBjb3m,3aea4JtP833HD4VDk7STz2Y9FB|10|20-177-807-5661|6825.37|BUILDING|unts. furiously ironic depths among the instructions wake carefully along the blithely ironi\n409|Customer#000000409|mtrMiDvQxNsy1Cj0cU4ITEW5wGKLPQ2IPHNE9r4|11|21-466-412-4731|3969.86|FURNITURE|fily pending courts. express, regular packages are furiously along the quickly regular packages.\n410|Customer#000000410|nYak2u Q9,gYUiLfh1N|7|17-576-345-5940|4349.27|BUILDING| sublate across the pending, express asymptotes. quickly \n411|Customer#000000411|V3e,FX5x50scsQDzt5,ESxfOQBt4OzjHRoTZxF|18|28-483-924-1955|1209.32|HOUSEHOLD|refully. slyly even packages above the evenly regular asymptotes are blithely ironic dependencies. deposi\n412|Customer#000000412|5IN2Y,QrhDJ2YBVGKiDbMpzi2hk1fmozIy2zQ|22|32-940-318-3191|6044.02|BUILDING|ithely silent notornis haggle. regular requests haggle according to the ironic deposits. blithely final dep\n413|Customer#000000413|,4Jm5N0ruhJCB7cBR6Kw|6|16-158-285-7336|5817.90|FURNITURE|ular packages integrate furiously fluffily final accounts. carefully regular \n414|Customer#000000414|i49DWI61AFb 45vb1RMH|19|29-552-380-2475|527.78|AUTOMOBILE|sily silent, even accounts. careful, final ideas boost fluffily. slyly final pinto be\n415|Customer#000000415|334jCRiUb,gx3|23|33-346-876-2972|2317.93|FURNITURE|egular deposits. blithely ironic inst\n416|Customer#000000416|fm7H7k6sYhKfXttOT|12|22-651-146-4780|4365.28|MACHINERY|p the pending pinto beans. furiously express reques\n417|Customer#000000417|X3LMSpIn4FgjgJxldHVUlUvKzyX|11|21-794-364-5100|6187.73|BUILDING|lent multipliers. quickly express theodolites kindle blithely. ironic re\n418|Customer#000000418|,e0q82drO rgVHXHrJRQ0GDrRoUOl|5|15-826-508-1218|1211.39|FURNITURE|d foxes against the furiously special packages snooze blithely quickly \n419|Customer#000000419|gvbZNJ4UVBAo5yOZ2UOWcvV9TeTj|16|26-338-447-2399|7786.69|BUILDING|ideas affix alongside of the final accounts. quickly ironic deposits abo\n420|Customer#000000420|HV0YB82MWw93 9K|20|30-776-366-5869|1999.35|BUILDING|ideas wake. fluffily ironic packages hang furiously above the regular, even platelets; packages haggle slyly \n421|Customer#000000421|it3mUlkZAe9J8gmy|13|23-918-228-2560|7073.17|FURNITURE|lithely final deposits haggle furiously above the\n422|Customer#000000422|AyNzZBvmIDo42JtjP9xzaK3pnvkh Qc0o08ssnvq|9|19-299-247-2444|-272.14|HOUSEHOLD|eposits; furiously ironic packages accordi\n423|Customer#000000423|Y2B EbOg39GpFLS0n|13|23-201-501-7824|95.79|BUILDING|ts cajole after the silent, pending instructions. ironic, even asymptotes use carefully. furi\n424|Customer#000000424|i4cf3kmRE9IJr,cu,1|19|29-891-311-6778|1866.42|HOUSEHOLD|bove the express, final deposits wake furiously furiou\n425|Customer#000000425|lp3aCRBK11qFY|16|26-756-407-4828|5824.88|HOUSEHOLD|ajole even, pending accounts. carefully brave accounts\n426|Customer#000000426|GjFjM4zjbyhNrV6XlE|19|29-768-330-6311|7818.25|HOUSEHOLD|ar instructions are against the ironic platelets. slyly final acc\n427|Customer#000000427|LHzXa71U2AGqfbqj1yYYqw2MEXq99dWmY|2|12-124-309-3821|4376.80|MACHINERY|y even multipliers according to the regu\n428|Customer#000000428|TCVjlzbX7x,kWcHN33LRdEjO38mAGmPR|21|31-587-557-8211|1952.36|BUILDING|furiously quick accounts. slyly bold dependencies cajole carefully. quickly even requests int\n429|Customer#000000429|kZBtY,LQAFu4iaSagjfIk8Q8dzgmT|15|25-989-936-1954|9247.21|FURNITURE|ly regular requests haggle enticing excuses. carefully ironic requests on th\n430|Customer#000000430|s2yfPEGGOqHfgkVSs5Rs6 qh,SuVmR|3|13-406-611-4228|7905.17|BUILDING|ly slyly ironic attainments. slyly special instructions until the deposits nag quickly whithout the bo\n431|Customer#000000431|RNfSXbUJkgUlBBPn|6|16-326-904-6643|2273.50|HOUSEHOLD|e quickly. final, even excuses against the even accounts sleep agai\n432|Customer#000000432|FDConiq g20GI9dH QTM ZNX4OB9KU|23|33-307-912-9016|5715.64|BUILDING|wake carefully close, special deposits. regu\n433|Customer#000000433|7XFuE4 euQR0w|20|30-659-445-3595|8746.23|FURNITURE|sual ideas affix carefully always regular accou\n434|Customer#000000434|6LGAf2hv4MB5MJhfvNsg|3|13-325-443-1474|2940.46|MACHINERY|lly final Tiresias. blithely regular ideas nag stealthily about the furiously \n435|Customer#000000435|diwjNQSb3wLYLy WfCDATo5rc1I3 s|2|12-741-309-6377|6217.46|MACHINERY|quickly excuses. blithely express theodolites poach slyly along the theodolites. slyly reg\n436|Customer#000000436|4DCNzAT842cVYTcaUS94kR0QXHSRM5oco0D6Z|19|29-927-687-6390|5896.87|FURNITURE|olites engage carefully. slyly ironic asymptotes about the ironi\n437|Customer#000000437|0PM1xuHd0q2ElcJp 77F2MykOVBSQnZR8u3jkn|4|14-364-492-8498|7760.52|AUTOMOBILE| foxes sleep across the slyly unusual pack\n438|Customer#000000438|eqo9A9oaE2CA7 7,L|23|33-394-388-4375|2131.13|MACHINERY|al deposits mold alongside of the fluffily brave requests. \n439|Customer#000000439|3deBblz2syRv8yMf0yAVKkE4mDH20uDRj4tJVHUm|14|24-873-368-6801|-61.29|BUILDING|ions may impress thinly for the deposits? even packages towa\n440|Customer#000000440|w4fKMgiBuGmV,nLn7NgJl1DoUWwNQMV8z  5,R|3|13-244-480-5751|1809.04|MACHINERY| even theodolites: fluffily final requests cajole about the quickly regular\n441|Customer#000000441|gjYpcBx6MP8GvDa6|23|33-438-355-3491|9451.84|HOUSEHOLD|r requests wake theodolites. quickly final ideas haggle fluffily. blithely f\n442|Customer#000000442|rvgayfJFLO2cjzMA|1|11-240-523-8711|4157.00|FURNITURE|lets would affix fluffily. regular, regular ideas ought to haggle carefully blit\n443|Customer#000000443|UdyNGZ6GSz5aNpMO5N2|3|13-241-131-1632|3726.22|FURNITURE|t the special, final platelets. bold req\n444|Customer#000000444|D8l4G8i9aZ7KRbqp6ajvR8h1wjr|1|11-402-300-1949|1505.27|HOUSEHOLD| express accounts along the pending deposits lose carefully above the furiously regular requests. pen\n445|Customer#000000445|MX1UA0KUJzIGyWM p2hbLg5dCpVLws8KNcwEsP|20|30-849-846-6070|8018.81|FURNITURE|e ironic, special accounts. quickly regular packages integrate fluffily slyly\n446|Customer#000000446|mJOJwYfch izLCuw70,qhlJSmH|24|34-321-168-5681|9225.60|FURNITURE|ending instructions. boldly ironic foxes across the regularly ironic pains sleep along the carefully final deposits.\n447|Customer#000000447|hVZBzP8Pii|3|13-438-344-7007|7665.98|HOUSEHOLD|telets around the furiously unusual foxes detect carefully against the \n448|Customer#000000448|BH4vtnDpabk0NgoGNJWu4OUXnidfJ|24|34-985-422-6009|8117.27|BUILDING|unts. final pinto beans boost carefully. furiously even foxes according to the express, regular pa\n449|Customer#000000449|DiUXazp8EYcJFsX2a7nciEpo9W5BRB4iqdb9HWL|4|14-893-381-6454|3001.94|MACHINERY|posits boost slyly carefully regular requests. final, bold fo\n450|Customer#000000450|KVpuYa4dDW8lZZVBttyK614C2qdS|9|19-782-397-9006|5544.42|HOUSEHOLD|gular decoys nod slyly express requests. slyly bold theodolites are along the regular\n451|Customer#000000451|ZJKTC1Ck,B01fYZ xdN2|20|30-939-275-3248|2110.59|HOUSEHOLD|quests grow furiously final deposits. ironic, even pi\n452|Customer#000000452|,TI7FdTc gCXUMi09qD|6|16-335-974-9174|6633.70|BUILDING|aggle quickly. unusual instructions i\n453|Customer#000000453|PZ4mmWL7R,El0MtLWMfLXp120lo0,itmO|8|18-209-381-8571|5678.18|HOUSEHOLD|sts. slyly even dolphins across the bold, regular foxes haggle blithely\n454|Customer#000000454|d9oQCm3onNsFlIoteVjFcQDv|7|17-818-915-9400|6134.40|AUTOMOBILE|ions print slyly platelets. carefully regular packages according to the fluffy, even foxes wake carefu\n455|Customer#000000455|sssuscPJ,ZYQ8viO|6|16-863-225-9454|6860.34|BUILDING|l wake. blithely final instructions integrate furiously above the final, regular req\n456|Customer#000000456|IgUSuulguDJ5|0|10-784-971-7777|8815.78|FURNITURE|ly even warhorses. quickly even requests wake slyly. \n457|Customer#000000457|eaAWe Vqr0x17Uwj1uzQRb wQpXxZVDWS3Wg|20|30-543-684-2857|5867.61|FURNITURE|the foxes. carefully pending instructions integrate fluffily blithely pending packages. careful\n458|Customer#000000458|iIKwI3HrgNlD9|4|14-651-706-4016|-38.42|BUILDING|ng. final, express requests are furious\n459|Customer#000000459|CkGH34iK 9vAHXeY7 wAQIzJa1cmA8DAEA7m|6|16-927-662-8584|1207.97|MACHINERY|ronic, regular dependencies use above the ironic deposits. carefully express packages use car\n460|Customer#000000460|Gbx5Hnw,ctlI7|11|21-643-955-6555|5222.83|FURNITURE|old dependencies mold slyly above the foxes. dogged, express ins\n461|Customer#000000461|5vxNLzSASzkbrUr8CRf5|21|31-533-226-4307|9177.63|AUTOMOBILE|sits breach blithely. slyly regular ideas haggle fluffily; special ideas cajole q\n462|Customer#000000462|MSqsCvNEkowp7FnscRXP6OUWm|21|31-157-561-4106|4522.60|HOUSEHOLD|ly special accounts? ideas engage regular dependencies. fluffily even pinto beans x-ray blith\n463|Customer#000000463|LV7MN7Tkm2NSo4Q3lwvjxGQyRJjRZRf,M|8|18-167-214-5805|-654.50|HOUSEHOLD| quickly along the final ideas. slyly regular accounts are iro\n464|Customer#000000464|kAALP9gEt3,G9XtxCXjv38HjKBEP|9|19-269-971-9738|8730.85|AUTOMOBILE|efully express accounts play. special requests use carefully. regular courts sle\n465|Customer#000000465|gngnTNn7azjgQlQJnakTZto|2|12-137-838-1346|8432.74|FURNITURE|es. quick asymptotes integrate carefully alongside of the ideas. even requests believe slyly even ac\n466|Customer#000000466|ZI1c8,ZanegEu5CEQxNf5,bkuYPwn7H7JIK7|12|22-280-738-3240|3168.41|MACHINERY|foxes. express, ironic accounts boost? carefully silent deposits engage. accou\n467|Customer#000000467|amwRkh0nDQ6r6MU|11|21-449-581-5158|9398.51|MACHINERY|manently special warthogs. final ideas a\n468|Customer#000000468|IcbihAtOVWcnswfyE|10|20-489-960-5023|9834.19|FURNITURE| accounts cajole quickly above the blithely final packages. even, express package\n469|Customer#000000469|JWOULMa5Qtt|12|22-406-988-6460|6343.64|BUILDING|cajole carefully slyly regular packages.\n470|Customer#000000470|v9 gWSuP4WrOjNJRgyJtjbNCChQME|20|30-507-458-4433|3597.53|HOUSEHOLD|ilent excuses. never ironic requests sleep furiously. daringly f\n471|Customer#000000471|tGr0DtrK 91IgzfeZrSPpPIia3|4|14-574-118-1005|5716.90|FURNITURE|es. unusual accounts try to solve ca\n472|Customer#000000472|hWgfnsmTAEOx9Mqp87YwztGrgLLqNkjMPh4|12|22-940-478-1933|7929.90|MACHINERY|deas sleep slyly blithely final foxes. slyly final e\n473|Customer#000000473|zO3W9pYj PvlsQGe|9|19-209-647-5704|-202.22|HOUSEHOLD|ter the quickly pending requests sleep above the carefully iron\n474|Customer#000000474|mvEKw,6zT0V8Yb2yTG hu990UX|21|31-247-536-6143|9165.47|MACHINERY|ns integrate against the quickly special courts. slyly\n475|Customer#000000475|JJMbj6myLUzMlbUmg63hNtFv4pWL8nq|14|24-485-422-9361|9043.55|BUILDING|egular requests. ironic requests detect furiously; deposits ha\n476|Customer#000000476|68r87HCBbQkVYaVfes8mgKs|2|12-996-628-9902|5973.10|BUILDING|sly. carefully quick instructions sleep carefully deposits. final, pending pinto beans use closely fluffily final in\n477|Customer#000000477|5aW5WHphNgFdIS1Qdp2cIJXG8ER8|23|33-845-877-6997|1836.61|AUTOMOBILE|totes are blithely among the furiously final foxes. slyly \n478|Customer#000000478|clyq458DIkXXt4qLyHlbe,n JueoniF|1|11-655-291-2694|-210.40|BUILDING|o the foxes. ironic requests sleep. c\n479|Customer#000000479|RdIiG8NbwYtamReRwhR|18|28-336-406-1631|3653.64|AUTOMOBILE|ages. bravely even foxes detect careful\n480|Customer#000000480|XyQSPswCeO WPD37K3 mYZ4hnCMJO5p|7|17-231-147-5851|2750.71|FURNITURE|posits. slyly ironic theodolites nag carefully about the quickly final accounts. s\n481|Customer#000000481|o4xa7J20NqHM8E0ykH,NKe1gPz04OqIn|21|31-363-392-6461|7157.21|FURNITURE|s can nag slyly instructions. regular, regular asymptotes haggle sly\n482|Customer#000000482|389RgNCsmVUKiRskmrQQm90xx JiIxOM0|13|23-732-448-1610|4333.37|HOUSEHOLD|carefully bold instructions. carefully final instructions wake carefully accounts. accounts cajole slyly ironic acc\n483|Customer#000000483|Yv1QV 1JsV 9sVbNufRvdnprt0grx52|11|21-799-189-1135|8877.20|MACHINERY|pecial ideas. furiously final i\n484|Customer#000000484|ismzlUzrqRMRGWmCEUUjkBsi|20|30-777-953-8902|4245.00|BUILDING|y against the express, even packages. blithely pending pearls haggle furiously above the fur\n485|Customer#000000485|XeFbvXCQ,J|19|29-434-961-1623|8695.45|MACHINERY|ecial pinto beans. instructions ought to cajole even\n486|Customer#000000486|2cXXa6MSx9CGU|21|31-787-534-8723|7487.40|AUTOMOBILE|nstructions. unusual, special pinto beans sleep about the slyly pending requests. fu\n487|Customer#000000487|oTc,l9dAf8O0qOOMP4P0WFTuGS|2|12-111-401-4259|9749.37|AUTOMOBILE|as. excuses use carefully carefully pending i\n488|Customer#000000488|bBcMjFPTysSTaTdHcoO|3|13-513-778-1881|-275.58|AUTOMOBILE|thely above the carefully ironic accounts. excuse\n489|Customer#000000489|GIdW4IVgeqWMBXnNFZGHS8kmhw|4|14-916-241-6195|8255.83|AUTOMOBILE|lar accounts. finally pending dependencies solve fluffily \n490|Customer#000000490| 66fG3Fyb946cVQsH9Z3VMNzR,yfHMKIEB|22|32-268-147-7824|-213.85|FURNITURE|ash carefully never bold instructions. regular, bold asymptotes cajole regularly. quickly bold foxes wak\n491|Customer#000000491|r3zPOuenxHl0oqInxWlEyLP1ZH|0|10-856-259-7548|785.37|AUTOMOBILE| slyly special requests hang dogged, express epitaphs.\n492|Customer#000000492|JexAgMLuUHoElYFaKx,hJcAP1b1GknYoYHQLyx|8|18-686-244-1077|8635.18|AUTOMOBILE|gle furiously furiously final packages. carefully bold pinto beans promise quickly alongside of the close\n493|Customer#000000493|G dRBjxmBBug1 xRSa6VwRchFDtU5b|16|26-514-558-7246|6582.04|MACHINERY|er the furiously express excuses use above the regular accounts. regular instructions after the \n494|Customer#000000494|GKgTjHFlQrDZWcketSqhZCopBhmChknI|10|20-330-453-6579|6295.47|FURNITURE|al courts. regular, ironic requests serve furiously. pending\n495|Customer#000000495|QhFbEv6KbQIwfZs 1krt1eACKI31v3iyM|7|17-400-405-6060|7997.81|BUILDING| dependencies. silent accounts cajole quickly furiously pendin\n496|Customer#000000496|Y8oYLlHme6Z4fEzkTu|12|22-173-644-7922|8174.82|MACHINERY| quickly bold packages. decoys among the blithely pending accounts lose according to the deposits.\n497|Customer#000000497|0 qRRXAxUbo1J KDwDMjFde5fXDwn |23|33-937-724-3506|2191.59|BUILDING|fluffy ideas detect carefully \n498|Customer#000000498|1Wnja9i7KAC3HxS5yATK,In8Q6AHcEUr0f5Tp|19|29-210-810-1479|3945.64|BUILDING|yly pending requests according to the slyly special asymptotes sleep carefully against the slyly even pack\n499|Customer#000000499|m1hO3VXQVbwTbJ99Hw|14|24-387-817-9149|4293.76|HOUSEHOLD|old sentiments cajole carefully among the blithely unusual requests. final packages nag careful\n500|Customer#000000500|fy7qx5fHLhcbFL93duj9|4|14-194-736-4233|3300.82|AUTOMOBILE|s boost furiously. slyly special deposits sleep quickly above the furiously i\n501|Customer#000000501|lzkYA5C6wa,wX|13|23-867-672-1331|1909.35|FURNITURE|ual deposits wake. quickly ironic platelets along the careful deposits haggle\n502|Customer#000000502|nouAF6kednGsWEhQYyVpSnnPt|11|21-405-590-9919|1378.67|HOUSEHOLD|even asymptotes haggle. final, unusual theodolites haggle. carefully bo\n503|Customer#000000503|7xCLYGLCpFU,toJBIPIrJbLIuLok81h IxK ae5Z|20|30-441-755-3094|3213.66|MACHINERY| even deposits haggle. packages i\n504|Customer#000000504|2GuRx4pOLEQWU7fJOa, DYiK8IuMsXRLO5D 0|10|20-916-264-7594|0.51|FURNITURE|slyly final theodolites are across the carefully \n505|Customer#000000505|MAUkwAyEvg61RlCMomspMs0WzYa,Ns|2|12-530-647-8313|6557.51|HOUSEHOLD|mptotes haggle around the theodolites. furiously bold accounts detect quickly packages. special pinto beans \n506|Customer#000000506|dT kFaJww1B|13|23-895-781-8227|1179.85|HOUSEHOLD| idle instructions impress blithely along the carefully unusual notornis. furiously even packages\n507|Customer#000000507|QlA0Fc 6e,r67ugESzq|14|24-158-185-4455|5727.00|MACHINERY|nst the furiously even deposits cajole slyly among the furiously ironic requests. blithely unusual depo\n508|Customer#000000508|q9Vq9 nTrUvx|18|28-344-250-3166|1685.90|BUILDING|uses dazzle since the carefully regular accounts. patterns around the furiously even accounts wake blithely abov\n509|Customer#000000509|LHLR0IKQJHVF1 0UvBNPLq0|4|14-115-338-1002|7885.50|FURNITURE|ily! requests cajole fluffily. slyly regular waters na\n510|Customer#000000510|r6f34uxtNID YBuAXpO94BKyqjkM0qmT5n0Rmd9L|5|15-846-260-5139|1572.48|HOUSEHOLD|symptotes. furiously careful re\n511|Customer#000000511|lQC9KfW W77IYtJjAgSZguNzxjY rYk3t6lcxfSh|13|23-247-728-9743|4571.31|FURNITURE|he slyly close deposits. special, ironic ideas detect furiously carefull\n512|Customer#000000512|e5 kymvjf6Vja7tNsL 3dfiK|2|12-144-416-6035|3937.58|BUILDING|packages are slyly after the slyly express packages. bold d\n513|Customer#000000513|sbWV6FIPas6C0puqgnKUI|1|11-861-303-6887|955.37|HOUSEHOLD|press along the quickly regular instructions. regular requests against the carefully ironic s\n514|Customer#000000514|0qD6Nwp3tG3QqCq9qvRAzT6N8L|23|33-194-775-6756|5840.97|BUILDING|carefully final ideas. quickly final packages are. requests haggle slyly. blithely pending sauternes lose bl\n515|Customer#000000515|oXxHtgXP5pXYTh|15|25-204-592-4731|3225.07|BUILDING|ackages cajole furiously special, ironic deposits. carefully even Tiresias according to \n516|Customer#000000516|EJwOQMTQnFwvd8r Y7f9i5POy6ZlNkIYxCL hg8t|6|16-947-309-2690|4768.96|MACHINERY|final requests after the furiously \n517|Customer#000000517|mSo5eI8F4E6Kgl63nWtU84vfyQjOBg4y|10|20-475-741-4234|3959.71|FURNITURE|al, ironic foxes. packages wake according to the pending\n518|Customer#000000518|EsCrt4chk,3IRIzwMHTu 6VQWrfh|17|27-651-256-7682|9871.66|BUILDING|as. quickly regular requests are carefully above th\n519|Customer#000000519|Z6ke6Y9J2pYuPBp7jE|5|15-452-860-5592|9074.45|BUILDING|es. fluffily regular accounts should have to sleep quickly against the carefully ironic foxes. furiously daring\n520|Customer#000000520|yaOGc9Ve92Bi4F6e0GcheU2MmEOXJE0zqyDT sEA|3|13-612-111-7765|8315.09|HOUSEHOLD| haggle across the even, bold instructions. final, even ideas might wake blithely against the \n521|Customer#000000521|MUEAEA1ZuvRofNY453Ckr4Apqk1GlOe|2|12-539-480-8897|5830.69|MACHINERY|ackages. stealthily even attainments sleep carefull\n522|Customer#000000522|gPz4FuAGpjvaU4YB9J,fGSnLBr9scEovGO1KkTx|12|22-771-454-9561|6358.46|BUILDING|instructions. doggedly express requests doze blithely. regular theodolites hagg\n523|Customer#000000523|sHeOSgsSnJi6pwYSr0v5ugiGhgnx7ZB|10|20-638-320-5977|-275.73|BUILDING| fluffily deposits. slyly regular instructions sleep e\n524|Customer#000000524|bpsO77xiAmjwOxqIgAszRu4Y|9|19-844-888-9800|5706.19|HOUSEHOLD|ending pinto beans unwind slyly. slyly final theodolites above the quickly ironic pinto beans haggle ev\n525|Customer#000000525|w0pOG5FhH45aYg7mKtHQhAWQKe|19|29-365-641-8287|3931.68|AUTOMOBILE| blithely bold accounts about the quietl\n526|Customer#000000526|0oAVPhh1I4JdrDafVG2Z8|1|11-170-679-3115|705.93|HOUSEHOLD|ctions cajole after the furiously unusual ideas. ironic packages among the instructions are carefully carefully iro\n527|Customer#000000527|giJAUjnTtxX,HXIy0adwwvg,uu5Y3RVP|13|23-139-567-9286|4429.81|HOUSEHOLD|ending, ironic instructions. blithely regular deposits about the deposits wake pinto beans. closely silent \n528|Customer#000000528|SRYjG5Wgp8ZG8GyDFhRIR5ep8yNs3nrCmYa|15|25-985-381-5453|1802.50|AUTOMOBILE| the slyly even instructions. carefully idle packages sleep about the platelets. bol\n529|Customer#000000529|oGKgweC odpyORKPJ9oxTqzzdlYyFOwXm2F97C|15|25-383-240-7326|9647.58|FURNITURE| deposits after the fluffily special foxes integrate carefully blithely dogged dolphins. enticingly bold d\n530|Customer#000000530|wG6AC7G6Y0DRuzJiroWCByzbrkqeySQDvRXzH|13|23-614-884-1055|4990.92|BUILDING|uctions cajole blithely across the ironic packages. slyly regular deposits wa\n531|Customer#000000531|ceI1iHfAaZ4DVVcm6GU370dAuIEmUW1wxG|19|29-151-567-1296|5342.82|HOUSEHOLD|e the brave, pending accounts. pending pinto beans above the \n532|Customer#000000532|xwWO3lWjgVJTZwhnltyH6zj5ddkzgH8RbF|15|25-875-978-2232|1725.68|MACHINERY|usly regular deposits kindle. quickly even depos\n533|Customer#000000533|mSt8Gj4JqXXeDScn2CB PIrlnhvqxY,w6Ohku|15|25-525-957-4486|5432.77|HOUSEHOLD|even dolphins boost furiously among the theodo\n534|Customer#000000534|3PI4ZATXq8yaHFt,sZOQccGl  Fc1TA3Y 2|1|11-137-389-2888|6520.97|AUTOMOBILE|deas. blithely regular foxes use carefully bold accounts-- ruth\n535|Customer#000000535|,2Y kklprPasEp6DcthUibs|2|12-787-866-1808|2912.80|BUILDING|even dinos breach. fluffily ironic\n536|Customer#000000536|jf8PSOQDvqQj4uF8|12|22-521-348-9030|3342.75|MACHINERY|tes? blithely enticing theodolites wake. braids sleep. sly\n537|Customer#000000537|wyXvxD,4jc|10|20-337-488-6765|2614.79|FURNITURE|e carefully blithely pending platelets. furiously final packages dazzle. ironic foxes wake f\n538|Customer#000000538|u9jYEMPoKwrH5wXivkSebbxAx1PU|19|29-632-471-2852|-303.95|MACHINERY|uffily special requests nag around the quickly stealthy\n539|Customer#000000539|FoGcDu9llpFiB LELF3rdjaiw RQe1S|6|16-166-785-8571|4390.33|HOUSEHOLD|ent instructions. pending patter\n540|Customer#000000540|YkaXu3o1X8|16|26-933-117-7482|9195.77|MACHINERY| enticingly express excuses. quickly regular notornis cajole near th\n541|Customer#000000541|,Cris88wkHw4Q0XlCLLYVOAJfkxw|0|10-362-308-9442|1295.54|FURNITURE|according to the final platelets. final, busy requests wake blithely across th\n542|Customer#000000542|XU2ffxnW3TQasrfF0u2KwKWmMarPyY4q7Q|16|26-674-545-2517|3109.96|BUILDING|r forges! requests alongside of the bold, final deposits\n543|Customer#000000543|JvbSKX7RG3xuqiKQ93C|17|27-972-408-3265|6089.13|AUTOMOBILE|l, even theodolites. carefully bold accounts sleep about the sly\n544|Customer#000000544|Jv7vcm,oE,HEyxekXKia1V5H1up23|5|15-572-651-1323|4974.68|AUTOMOBILE|bout the packages integrate above the regular instructions. regular ideas hinder s\n545|Customer#000000545|AsYw6k,nDUQcMOpEws|10|20-849-123-8918|7505.33|AUTOMOBILE| carefully final deposits. slyly unusual pinto beans may wake bold requests. unusual courts alongside \n546|Customer#000000546|GZtBXX3OaqFLbC9JNi1hmF1JFLbmRs9|19|29-936-444-8248|3116.50|MACHINERY|ly fluffy braids. blithely special theodolites use express deposits-- slyly regular attainments\n547|Customer#000000547|4h SK3dVkE1tQ0NCh|22|32-696-724-2981|6058.08|BUILDING|y express deposits. slyly ironic deposits nod slyly slyly ironic instructions. carefully quick idea\n548|Customer#000000548|98nP31ToAGK tCCkYm7HqBZt0dLjy0JzlMMRCmkj|4|14-787-370-8722|90.45|BUILDING|c pinto beans. quickly even requests haggle against the blithel\n549|Customer#000000549|v5uqfeHLiL1IELejUDnagWqP5pKWa9LtoemziGV|24|34-825-998-8579|91.53|BUILDING|n asymptotes grow blithely. blithely fluffy deposits boost furiously. busily fu\n550|Customer#000000550|q5 gKwc7PBQOyd,H|17|27-938-997-6262|7270.82|FURNITURE|ully regular deposits. slyly ironic requests wake along the depos\n551|Customer#000000551|holp1DkjYzznatSwjG|15|25-209-544-4006|-334.89|MACHINERY|y special ideas. slyly ironic foxes wake. regular packages alongside of the deposit\n552|Customer#000000552|EbjtaRaiok7eqbQ5VJi7q|2|12-669-784-2191|1353.24|FURNITURE|ickly final accounts cajole fluffily according to the bold, regular accounts. \n553|Customer#000000553|8tTlavJ sT|4|14-454-146-3094|4804.57|BUILDING|ully regular requests are blithely about the express, bold platelets. slyly permanent deposits across the\n554|Customer#000000554|RluaguNRAJhYXmn,CWxcOC,Ly7|2|12-938-503-7317|8395.57|HOUSEHOLD|jole along the blithely bold \n555|Customer#000000555|chm8jY6TfQ8CEnsvpuL6azNZzkqGcZcO8|15|25-548-367-9974|5486.52|BUILDING|lites are blithely ironic ideas. blithely special pinto beans dazzl\n556|Customer#000000556|UMHllVkuyQUQ3aLXCVRxrXatsyd0AL6Xw|1|11-934-412-5846|7944.22|MACHINERY|nt excuses! carefully final requests solve quick\n557|Customer#000000557|Nt6FUuDR7v|15|25-390-153-6699|9559.04|BUILDING|furiously pending dolphins use. carefully unusual ideas must have to are carefully. express instructions a\n558|Customer#000000558|PB1ZV4kQnRHiC|17|27-866-273-7672|1912.23|HOUSEHOLD|ly final requests. regular requests hag\n559|Customer#000000559|A3ACFoVbP,gPe xknVJMWC,wmRxb Nmg fWFS,UP|7|17-395-429-6655|5872.94|AUTOMOBILE|al accounts cajole carefully across the accounts. furiously pending pinto beans across the \n560|Customer#000000560|gU5FQf0WM0sxTYQ|19|29-618-467-8489|1469.59|BUILDING|sly pending packages boost slyly-- fluffily ironic ideas bel\n561|Customer#000000561|Z1kPCTbeTqGfdly2Ab9KEdE,jIKW|18|28-286-185-3047|2323.45|FURNITURE|across the furiously ironic theodolites. final requests cajole. slowly unusual foxes haggle carefully\n562|Customer#000000562|04xjB,zuffnhVyEY0 PeiJPtdjh 0ji|15|25-271-465-6971|9234.50|BUILDING|accounts. ideas cajole. quick\n563|Customer#000000563|2RSC1g7cVd,j23HusdkhdCGmiiE|12|22-544-152-1215|3231.71|FURNITURE| pinto beans believe fluffily. excuses wake blithely silent requests. b\n564|Customer#000000564|qPQOo94iVl|4|14-865-332-8571|6307.59|MACHINERY|onic patterns about the furiously pending\n565|Customer#000000565|HCBXAou,1eP6Z3IynHFI7XmEBgu27Sx|4|14-798-211-2891|2688.88|FURNITURE|e. carefully bold deposits sleep regu\n566|Customer#000000566|5NmdMIwTpF8tj7O92363ycA6EL5Yh,vW|24|34-443-780-3708|1928.10|FURNITURE|ke express, ironic requests. regularly even sauternes detect de\n567|Customer#000000567|KNE6mpW69IgTjVN|21|31-389-883-3371|8475.17|BUILDING|blithe, even ideas. fluffily special requests wake. c\n568|Customer#000000568|ZddVCnzeABTTBgV3GvkvNtw9,KOHHpME2GELhz|13|23-603-795-8611|1317.56|BUILDING|gular decoys haggle slyly. blithely special packages slee\n569|Customer#000000569|Kk20Q5HiysjcPpMlL6pNUZXXuE|2|12-648-567-6776|-795.23|MACHINERY|sh. blithely special excuses sleep. blithely ironic accounts slee\n570|Customer#000000570|0Zo0P6m,sie 1,VXacPX2ccDIyWFolj6R|15|25-264-442-3057|8480.87|AUTOMOBILE|gular instructions unwind bold escapades. special asymptotes snooze according to the\n571|Customer#000000571|hCrDDrMzGhsa6,5K4rGXQ|2|12-115-414-4819|8993.23|HOUSEHOLD|le fluffily. ironic, pending accounts poach quickly iron\n572|Customer#000000572|Nf4Yqb49BqGkzmmtf6|11|21-425-209-5033|7252.65|AUTOMOBILE|leep. pending requests affix blithely. ironic theodol\n573|Customer#000000573|BEluH7it7jUcWqb tNLbMIKjU9hrnL7K|4|14-354-826-9743|2333.96|HOUSEHOLD|as. furiously even packages sleep quickly final excu\n574|Customer#000000574|ratVLdmp070|8|18-676-218-1058|9787.56|FURNITURE|al pinto beans. carefully ironic foxes cajole idly finally express theodolites. fluff\n575|Customer#000000575|4K6h0pYH,bg2FS5cYL,qqejhvp7EfTlBjRjeVPkq|1|11-980-134-7627|3652.29|BUILDING| final requests cajole after the ironic, bold instructio\n576|Customer#000000576|JI7ZI3BRrkt40uuUmg oyZC3pQ2lS65SnSGL|1|11-777-499-8213|2091.63|HOUSEHOLD|sual platelets. furiously final theodolit\n577|Customer#000000577|a73SSq2cip7C8nSzdmmscpZyLCZ7KL|14|24-662-826-1317|7059.15|FURNITURE|int furiously. slyly express pin\n578|Customer#000000578|nxUZ BCBO1 HAymUcopl2NtyWMuWVnE3bqPVDB|14|24-278-860-9263|6181.23|FURNITURE|ly. carefully pending packages cajole among the carefu\n579|Customer#000000579|9ST2x,snyY3s|0|10-374-175-6181|1924.96|MACHINERY|ndencies detect slyly fluffil\n580|Customer#000000580|wpvPbaPtx5QN|11|21-444-589-3830|-181.63|BUILDING|dependencies. final asymptotes haggle among the bold packages. slyly silent \n581|Customer#000000581|s9SoN9XeVuCri|24|34-415-978-2518|3242.10|MACHINERY|ns. quickly regular pinto beans must sleep fluffily \n582|Customer#000000582|KqH6uOztVK55zDxLA9kvdtny i5OYXt|3|13-484-591-9280|4879.55|MACHINERY|carefully against the quickly s\n583|Customer#000000583|V3i6Gu9,LZtvdnNppXnI2eKQFx0b36WvL,F |13|23-234-625-4041|3686.07|HOUSEHOLD| haggle. regular, regular accounts hinder carefully i\n584|Customer#000000584|jebKvptmHtS9,YE1qOjl2AOw38P,8skngJZh|15|25-352-778-1041|8825.71|MACHINERY|ages boost regular deposits. blithely stealthy depo\n585|Customer#000000585|OAnZOqr6A,,77WC001ck8BAqvJTW6,dRGoRdX|16|26-397-693-4170|7820.26|MACHINERY|ickly ironic requests sleep regularly pending requ\n586|Customer#000000586|vGaA9XBtn,hlswFhSjLIXGlLEDD2flE8UXwj|11|21-239-369-7791|5134.35|AUTOMOBILE|above the blithely express ideas. slyly r\n587|Customer#000000587|J2UwoJEQzAOTtuBrxGVag9iWSUPTp|6|16-585-233-5906|7077.79|AUTOMOBILE|ve the final asymptotes. carefully final deposits wake fu\n588|Customer#000000588|ex9SkK7K uM,ki1dsO7PgZLlIuQFKJUQZpD2oS|17|27-988-546-2598|483.89|FURNITURE|ic requests haggle quickly across the deposits. regular, express ideas along the\n589|Customer#000000589|TvdYNogIzDfr 1UyJE4b9RTENPmffmIoH|19|29-479-316-3576|1647.05|FURNITURE|s; blithely ironic theodolites sleep-- accounts haggle around the furiously silent ideas. silent, final packages in\n590|Customer#000000590|4sHhhAZWHYRxJVz0KRgjW9IlKu,55IuT|8|18-734-215-6394|3993.54|MACHINERY|es. regular dependencies cajole furiously blithely regular ideas. regular dependencies cajole carefully a\n591|Customer#000000591|wGE7AnEtiX7cmCkYA|20|30-584-309-7885|6344.66|MACHINERY| regular requests after the deposits cajole blithely ironic pinto beans. platelets about the regular, sp\n592|Customer#000000592|srNO5Hu10z1Ru4rRPU,QpXzFwY8759wqZ|24|34-832-574-7217|9712.75|BUILDING|lithely final requests use slyly. special theodolites nag carefully-- carefully pending deposits cajol\n593|Customer#000000593|SYyEL2nytJXBbFemMseCiivA32USVEDbvGzZS|9|19-621-217-1535|233.51|AUTOMOBILE|ve the regular, ironic deposits. requests along the special, regular theodolites lose furi\n594|Customer#000000594|sbcKWltfCAnXrc Z27ZYDzsH1ztd,ZhgaD9xIMsh|9|19-286-925-8440|6518.42|HOUSEHOLD|. fluffily final instructions are slyly toward the slyly \n595|Customer#000000595|7Q17BacxM,liY2AwhnHGR0Pjf1180sMz1U|19|29-554-215-7805|4177.17|HOUSEHOLD|gular accounts x-ray carefully against the slyl\n596|Customer#000000596|hoByQV2JchlIWfzPFW8I0nCI|5|15-484-811-5482|1722.88|MACHINERY|ecial deposits after the slyly regular packages dazzle furiously across the courts. accounts wake. reg\n597|Customer#000000597|Dbv,XVGzl4X|15|25-687-952-9485|2443.52|AUTOMOBILE|es across the slyly brave packages maintain quickly quickly dogged excuses\n598|Customer#000000598|9ICLFWFZa6|9|19-113-384-3847|3244.78|FURNITURE|es. furiously pending packages haggle fluffily carefully silent foxes. carefully unusual dependencies boost fu\n599|Customer#000000599|fIvpza0tlXAVjOAPkWN5,DiU0DO4e5NkfgOlXpDI|4|14-916-825-6916|6004.52|HOUSEHOLD|thely even requests wake carefully regular theodolites. instructions haggle alongside of the f\n600|Customer#000000600|LOtVjPC,Eu,0I2BRCqWf,K|12|22-675-907-7888|2003.44|HOUSEHOLD|nstructions sleep among the final, even pinto beans. fluffily pending theodolites according to the \n601|Customer#000000601|P3 Dv,6yllTNmL9yt6NUZZPZjvM2coWJd|1|11-104-635-9839|9768.21|BUILDING|ly according to the unusual foxes. carefully ironic accounts haggle accounts-- regular dolphins will integ\n602|Customer#000000602|NCryKIpG3W,FDV2|13|23-434-900-7213|8404.90|BUILDING|nstructions. asymptotes above the forges are against the carefully \n603|Customer#000000603|DFAIWiyqZ0GzuF6AWCZX3DcDxyICb3EWxEw|19|29-629-573-6194|8161.13|AUTOMOBILE|n packages wake carefully. special requests haggle slyly carefully bold deposits. furiously regular pinto b\n604|Customer#000000604|qCQsFELZ3W hlmi,zOHBcZGo0PZl3jbFu1jsijqE|21|31-757-951-9827|3195.96|HOUSEHOLD|le furiously express instructions. ideas hag\n605|Customer#000000605|QAxZ0IXgCzUfNjseQCLfh95HEi|23|33-269-948-8039|-549.73|HOUSEHOLD|ly regular foxes are quickly \n606|Customer#000000606|vBIUd7LjRJ5rZXSzITHIvpZwBCClyt4Hjr Tlnf,|1|11-284-540-8460|9676.98|AUTOMOBILE|, bold packages. regular, final theodolites haggle slyly carefully final accounts. silently specia\n607|Customer#000000607|m61hvYPASIGmNJx7Tu|24|34-601-151-4029|4038.45|FURNITURE|ymptotes. blithely bold requests shall are about the furiously final platelets.\n608|Customer#000000608|luMI1JpfrrILCEeTgz8k98z|16|26-767-193-8671|2256.36|BUILDING|ld packages. special requests along the accounts are after the carefully unusual deposits. blithely quiet excuses c\n609|Customer#000000609|dSpUFl8IR8Gh|21|31-869-580-1707|3651.06|FURNITURE|pending, express platelets poach furiously after th\n610|Customer#000000610|Fo8RfPq1kgzD 0|11|21-782-663-7023|3374.92|BUILDING|uriously final deposits against the deposits detect alongsi\n611|Customer#000000611|E1dtWGHE7NrLfnSKLPFU|19|29-924-242-5243|4272.43|HOUSEHOLD|inst the slyly final accounts. final packages wake after the even pinto beans. carefully\n612|Customer#000000612|oNFqorGhq3a3woEp5q8xVDX|14|24-818-339-9984|7669.16|HOUSEHOLD|ns wake quickly quickly ironic accounts. regular accounts toward the \n613|Customer#000000613|AJT,26RbanTdEHOBgTWg|4|14-275-416-1669|6679.75|AUTOMOBILE|ironic, pending deposits: quickl\n614|Customer#000000614|YKweqHJfVok|18|28-698-510-6194|9630.24|FURNITURE|y even multipliers. pinto beans nag busily after the busily ironic reques\n615|Customer#000000615|6aITapYMqM1fJQKuJD05Yb,6FhjrW|2|12-639-391-3956|-482.48|BUILDING|yly. blithely even accounts sleep blithely unusual in\n616|Customer#000000616|yvUE7Qy3Ub6uGhPkuEJeOI|1|11-275-121-4443|6898.65|FURNITURE|ts. blithely bold packages sleep \n617|Customer#000000617|Ifjxbt3Y4mGu|14|24-527-532-7752|3625.93|HOUSEHOLD|deas sleep slyly? final, even gifts about the furiously regular\n618|Customer#000000618|9O4fhgteQdyFvCkrFm|0|10-675-573-1877|-932.38|HOUSEHOLD|uickly even ideas sleep slyly pending foxes. final, pending foxes nag slyly. permanent instructio\n619|Customer#000000619|6bxrNxQA oes7cMa23R 5lDmIOIRThvd|24|34-245-618-6317|2336.99|FURNITURE|ts breach slyly after the slyly regu\n620|Customer#000000620|3ztw9KQqKGNsiMM,I1 6g2f,u2Pm5LhlSEe8ZK1k|8|18-466-916-8135|5795.15|AUTOMOBILE|ructions boost furiously among the slyly final dolphins. regular, regular ideas w\n621|Customer#000000621|IpFo6e22CRink74PUEPthY9DJJnSeORmQJ4|17|27-667-987-3718|3164.28|HOUSEHOLD|nstructions! final dependencies s\n622|Customer#000000622|qdRHTTnVf9O2iFMG1sDm2GSnlM24tKWK|13|23-925-151-9771|5974.11|FURNITURE| asymptotes. slyly ironic excus\n623|Customer#000000623|HXiFb9oWlgqZXrJPUCEJ6zZIPxAM4m6|9|19-113-202-7085|7887.60|BUILDING| requests. dolphins above the busily regular dependencies cajole after\n624|Customer#000000624|L1hGsNrx4BiN5DIZGk7WMsB90T4ag|4|14-558-935-8773|3907.11|MACHINERY|le carefully. sly asymptotes sleep fluffily unusual packages. final deposi\n625|Customer#000000625|uvgDE6eQ2bJp4BkHyVdpYYC8|13|23-789-801-2873|5744.89|FURNITURE|, pending deposits. sly theodolites along the carefully unusual\n626|Customer#000000626|PDeE61VY2Q96efuewIZ|5|15-540-121-5663|5447.12|FURNITURE|t brave foxes. slyly pending packages wake furiously along the deposits. carefully pendin\n627|Customer#000000627|uOFz ,iMYi02Ksr13Q2nBCETCpSp|15|25-811-790-3533|5826.68|FURNITURE|ages. regular ideas sleep. bold foxes affix. regular instructions haggle. bravely unusual requests haggl\n628|Customer#000000628|Vzraru5KbgcC3V|17|27-367-742-4090|4954.25|FURNITURE|l dependencies. pending warhorses haggle. fluffily final accounts slee\n629|Customer#000000629|LeXGhXX1mFQ0Cq,7taW ruvRHTpG3q,KkW|12|22-260-205-9116|5100.77|AUTOMOBILE|ic attainments. stealthily pending pinto beans affix carefully ironic theodolites. fluffily final deposits sleep da\n630|Customer#000000630|XAw3WrAa mt0DnOuycb16LG9zbUv04DXsS|24|34-396-743-8684|3649.05|HOUSEHOLD|ely across the blithely stealthy ac\n631|Customer#000000631|By LIK3TbJ67sJLlpaoOCXZheuX|19|29-864-813-2575|2603.00|MACHINERY|bold platelets haggle. slyly even pearls can solve slyly among the final foxes. slyly unusual r\n632|Customer#000000632|sUlni97rSK6DIL|3|13-310-645-6928|-487.92|MACHINERY|ld dependencies sleep slyly along the special requests. furiously regular asymptotes use furiously accounts.\n633|Customer#000000633|0pgCxndi1coDLkAV,UJJDMV0wtVVahCQaQap 0M|2|12-450-116-1239|3385.52|HOUSEHOLD|ully. even, final requests use slyly. blithely special packages wake carefully furiously busy\n634|Customer#000000634|O09TejHJ6UszNfmqTR cmal8zcs|20|30-997-704-1110|6397.58|FURNITURE|e above the regular deposits. slyly even requests integrate slyly blithely express forges. regular platele\n635|Customer#000000635|Ftqi0UYvzz56Ov,J6,ySp5WE4vJ2rtY|2|12-399-186-7550|8216.79|AUTOMOBILE|efully bold deposits doubt above the clos\n636|Customer#000000636|7urmO1zY77WBPOWn7pXA OqCm3upL9gOtL4V|7|17-420-712-5063|3660.47|FURNITURE|ions boost furiously at the final dolphins. blithe\n637|Customer#000000637|Ey7g4q2oH Q1vs|7|17-983-923-8985|7511.17|HOUSEHOLD|ly furiously even accounts: final, unusual Tiresias do snooze theodolites. slyly regular dependenci\n638|Customer#000000638|yyRRorZ HHzU1yJwNJwF72dvUVJ nMlzpKAXEb|24|34-102-347-8343|2258.40|HOUSEHOLD|nts x-ray fluffily across the theodolites. carefully final pearls cajole ruthl\n639|Customer#000000639|8OiPHefIPoalRjUoCIwbXz|15|25-221-133-2233|4899.15|MACHINERY|ly bold asymptotes. ironic, even d\n640|Customer#000000640|j3vjr0 n,pJFG4gIOtC|2|12-702-315-6637|3025.84|HOUSEHOLD|lly. furiously quick deposits haggle quickly regular packages. pinto \n641|Customer#000000641|gbIvFlCygHjj5NG7U|24|34-761-987-7777|339.49|AUTOMOBILE|uriously final requests boost regular, final ideas. fluffily busy packages promise? requests along the bold, s\n642|Customer#000000642|6Y1gEH0gMLh0yzlipNR|22|32-925-597-9911|5684.01|AUTOMOBILE| requests cajole blithely quick\n643|Customer#000000643|9T 2avhfyF PQ|0|10-978-597-2747|5184.70|FURNITURE|fily along the quickly ironic ideas. final, final\n644|Customer#000000644|bfZrdZE0QHtMc,ksudO|18|28-489-845-4801|6183.15|HOUSEHOLD|ke slyly alongside of the silent, even req\n645|Customer#000000645|I,Kso,IZ,AL2rK4HhIB1wRWWrx3 yoaZlFs|11|21-242-974-5799|1146.49|BUILDING|. special packages haggle ruthles\n646|Customer#000000646|gogI8kweD 2H6U,01lbIp0UWUwljSue,KRcC|2|12-177-329-4316|6074.42|HOUSEHOLD|ptotes. silent requests cajole fluffily along the slyly permanent ideas. pending instr\n647|Customer#000000647|2Bx7,7i87h5cagC,ZBz49lyiziLqQoD|1|11-873-931-2886|-132.97|BUILDING|. excuses use alongside of the furiously final deposits. blithely express foxes wake furiously. blithely \n648|Customer#000000648|pYCT1OLD5Y7rBjjAKnf0Lqd 41RC,0n,nT3oNWz9|7|17-473-731-8352|7029.24|FURNITURE| wake carefully outside the ironic, bold packages. blithely special dependencies boost slyly even ideas. careful\n649|Customer#000000649|EntXL7MF4lU|11|21-656-678-1337|9442.55|BUILDING|unusual deposits engage along the carefully pen\n650|Customer#000000650|1hqwYdlDhaqnkRrovbulo0rrDhJZgUz|5|15-842-586-1263|2086.91|AUTOMOBILE|g to the final deposits. quickly regular requests dazzle along the slyly express courts.\n651|Customer#000000651|K7rCTwTb3UX9wAO6ihvYxwBOuJNx51|9|19-610-541-4787|4716.31|HOUSEHOLD| fluffily quick dependencies cajole slyly along the blithely final dependencies. carefully pen\n652|Customer#000000652|0WlNKsoRqdjU9,5 Qz,bgm5swI,i0Kg|20|30-254-389-5987|-919.65|AUTOMOBILE|p carefully blithely bold platelets. furiously unusual deposits wake. sl\n653|Customer#000000653|la,ROBPJ4I2YNzQw,RpbB0sTOjyv3F ZBeWRiQ|11|21-715-573-6928|7307.55|BUILDING|aggle bravely deposits. even du\n654|Customer#000000654|Ip,OhnmOeHu9PezZBvw7AHJcBDOWVoVUJKsJyA|16|26-416-434-3449|1067.04|AUTOMOBILE| nag slyly. final deposits sleep along the ironic, bold i\n655|Customer#000000655|SceikyyffYL5OUI8rFnAgrT5E|14|24-916-820-5158|1724.68|FURNITURE|e accounts. unusual, pending platelets are quickly. req\n656|Customer#000000656|DeZPec2K1U1fCBR2Ul9mnBNjKuXPcRcm|5|15-379-993-4446|-390.09|AUTOMOBILE|uests cajole quickly. final, pending pinto beans wake furiously among the requests. ironic foxes with the forg\n657|Customer#000000657|BpXQ3sbx8bCU0|19|29-952-164-8914|8729.97|FURNITURE|ies boost slyly bravely express instructions. fluffily even theodolites integrate s\n658|Customer#000000658|9SboxGtXimmJg49IyT3Zt|1|11-675-750-9832|4716.68|HOUSEHOLD|r ideas about the slyly ironic theodolites integrate according to \n659|Customer#000000659|ThR9miOedPuwVEZyz 3MMjHPwB|0|10-834-287-1466|5297.68|HOUSEHOLD|s cajole final, regular dependencies. final accounts sleep! furiously regular requests wake slyly silent\n660|Customer#000000660|ZD4fphyxb5pyE|0|10-987-901-3986|2110.30|MACHINERY|ding excuses haggle furiously regular foxes. finally\n661|Customer#000000661|1MqWuuRVM5y5NlT1kakwke|4|14-983-203-6472|1735.44|HOUSEHOLD| regular theodolites. brave platelets\n662|Customer#000000662|ss AgOrB5VFubLk bsmTgbw2ddJD|0|10-728-355-4532|4517.33|BUILDING|ckages haggle: quickly regular ideas are after the furi\n663|Customer#000000663| fqLQWxHWWC40GUOgVvb4idYDbE1Gxc6|9|19-613-882-5677|9698.21|AUTOMOBILE|r the slyly final packages. slyly bold attainments detect blithely acco\n664|Customer#000000664|viRe1P6HiyL4LXpU7HPwu|15|25-329-655-5068|8878.22|BUILDING|yly ironic requests cajole pending pla\n665|Customer#000000665|Fo9QgQsDOP28D3zR|22|32-759-858-2984|-616.79|BUILDING|y along the special requests. blithely regular waters thrash abou\n666|Customer#000000666|dD32Q8kL6KW|2|12-594-508-9621|4538.54|AUTOMOBILE| special packages boost across the even accounts. ironic accounts serv\n667|Customer#000000667|oQqeEC,OD9XC1JXyOsHqcpv0fPUdP9ek5KKb70tQ|6|16-917-453-2490|3288.76|AUTOMOBILE|lithely about the furiously sil\n668|Customer#000000668|PCmw0r6KkLjXZsljablJ|15|25-582-501-2346|8184.21|FURNITURE|uffy excuses are carefully regular, ironic pinto\n669|Customer#000000669|axdO3iaVyYXSxnqnwC0p2Dx6Mn3lDDMp|10|20-471-280-4789|6019.20|MACHINERY|ronic decoys. blithely ironic accoun\n670|Customer#000000670|RJtcLv6Tjpx|2|12-839-426-4266|6738.93|BUILDING|y unusual deposits are carefully regular foxes. packages dazzle carefully. furiously unusual d\n671|Customer#000000671|ic6qGrt0giB,HDEiBK,,FYGHXQpc|21|31-593-213-9388|3227.87|FURNITURE|bold ideas above the ironic packages affix blithely about the furiou\n672|Customer#000000672|Rsq8zHIbqRRB8VlgldFlU56j,0SN |18|28-774-971-2700|7778.95|BUILDING|ake. regular requests about the idle asymptotes haggle slyly final, silent packages. carefully special\n673|Customer#000000673|Q2S9DoW6mQN5iQ0A8DxD6UxNmPZky|4|14-769-230-8609|3942.58|MACHINERY|ons will cajole blithely. always even packages dazzle blithely across the regular, unusual \n674|Customer#000000674|GLZCUQrtiNTrPKdK 0O86ZF|5|15-543-819-4391|7889.64|BUILDING|ites cajole ironic accounts. regular, ironic gifts nag furiously against the blithely express deposits; unusual, \n675|Customer#000000675|canew8kOzr8RDTmenqJOn|20|30-299-640-9565|5295.26|HOUSEHOLD|riously even requests boost slowly. ex\n676|Customer#000000676|JzJnD kA3KTjTYl|24|34-710-820-8362|5527.61|HOUSEHOLD|es. slyly even theodolites across the slyly ironic accounts nag from the slyly even accounts. accounts are. \n677|Customer#000000677|,wTtWOOr  wqX2sL,h79myT6nxG1EgY|3|13-398-309-9122|5582.63|AUTOMOBILE|ly brave ideas haggle slyly blithe acc\n678|Customer#000000678|BiQyhSp oiDp,,MFVxyrOwMT810qiQlISEPye|9|19-508-735-4910|6302.93|AUTOMOBILE|gainst the carefully pending requests-- furiously special requests haggle? not\n679|Customer#000000679|IJf1FlZL9I9m,rvofcoKy5pRUOjUQV|10|20-146-696-9508|1394.44|AUTOMOBILE|ely pending frays boost carefully\n680|Customer#000000680|TuONtFVII8TR2QtJyl1lS5f0iagaWWsBm1IQo|12|22-593-663-2200|4366.23|AUTOMOBILE|kages: final deposits believe unusual deposits: b\n681|Customer#000000681|x85ncT W41KEcUQCtxE,LD iMNO6j4Hh4x|20|30-809-878-5822|235.53|MACHINERY|ate carefully. accounts hang carefully along the blithely final theodolites. bravely sile\n682|Customer#000000682|y5J0HJVI6Mb k4rXTAPbxE9aw|15|25-233-924-7389|7420.60|AUTOMOBILE|he bold, even accounts breach\n683|Customer#000000683|G0, q8c6vBykpiLvcuSJLYvqE|6|16-566-251-5446|9120.93|MACHINERY| somas use-- slyly express foxes wake quickly blithely iro\n684|Customer#000000684|5kSJ3qMS,7YKdfmwBFzQN4y8EgPiH,8ln19|15|25-456-286-6398|8545.90|HOUSEHOLD| of the bold requests. blithely regular accounts nag alongside of the carefully silent dolphins. carefully \n685|Customer#000000685|eRvmYOdl5v|17|27-269-224-9902|2039.84|MACHINERY|nusual ideas. pending packages use carefully. slyly special sentiments cajole after the blithely even accounts. blit\n686|Customer#000000686|1j C80VWHe ITCVCV|6|16-682-293-3599|5503.36|HOUSEHOLD| even deposits print quickly. foxes wake. furiously ironic asymptotes across the bold foxes\n687|Customer#000000687|UJTC3 WtzvoD39r1GuoCP|21|31-149-119-1456|4999.31|FURNITURE|special packages haggle carefully slyly ironic pinto beans. sl\n688|Customer#000000688|2QBxK8WIryWMZTsDM JS7GxWCB6Y71Swa,f1EUxk|8|18-210-546-2836|3354.47|HOUSEHOLD| use alongside of the ironic, pending foxes. final packages use. ironic, unusual dependencies haggle\n689|Customer#000000689|Gcie9Q,Wc6J0QvKcefqflajjOtOVchnxqBn9|24|34-325-146-3591|1481.24|MACHINERY|ions. furiously unusual platelets cajole blithely. caref\n690|Customer#000000690|xH61m,Si5X4REvi|3|13-489-760-5455|103.45|HOUSEHOLD|nt deposits. final, unusual requests use blithely. slyly unusual packages against the carefully bol\n691|Customer#000000691|0RkDX6OLU1hISYCLmdLD C|16|26-741-688-4189|9566.15|MACHINERY|p. ironic, regular foxes against the ironic, special theodolites nod carefully quietly unusu\n692|Customer#000000692|d6XE9sg,  wINvIz8aZS b8n XuhAQU5|19|29-804-421-1703|276.71|AUTOMOBILE|cuses. slyly even asymptotes doubt quickly. fluffily thin theodolites boost. ca\n693|Customer#000000693|r5gn5SUW0tsfkOw42x84|1|11-391-870-8153|6577.21|AUTOMOBILE|onic packages. carefully final sauternes across the even, express deposits h\n694|Customer#000000694|3ToF2HqyF9mEcP1QgW NMN,6,KsFq6x4u14MS|10|20-646-375-1939|2222.65|BUILDING|efully final requests detect blithely. furiously ironic p\n695|Customer#000000695|fbT9dQCc,su7JboB5FWI6|8|18-131-151-7466|9126.54|FURNITURE|ly express deposits according to the fina\n696|Customer#000000696|dYyj4azN0EE GqeZxv|22|32-923-916-5643|3778.97|HOUSEHOLD| ironic ideas among the carefully bold foxes na\n697|Customer#000000697|LFwdGCTUYDenZGoA9|4|14-613-651-2263|8121.84|AUTOMOBILE|he regular pinto beans. courts cajole carefully according to the blithely even theodolites. quickly\n698|Customer#000000698|FLZ2NG5pKHpPtAh|23|33-282-178-6799|2894.22|BUILDING| sauternes. gifts cajole. slyly pending platelets cajole at the iron\n699|Customer#000000699|4R6pspbuk5Sp,036VraUEKPBzs|4|14-865-140-8680|-809.22|HOUSEHOLD|. finally even ideas wake slyly pending dolphins. slyly even instructions wake whithout the p\n700|Customer#000000700|zyWvi,SGc,tXTls|0|10-351-119-7514|4367.53|MACHINERY| bold excuses. furiously even asymptotes across the carefully regular dugouts cajole fur\n701|Customer#000000701|yQU8LcjocNHkk1hse30M0U1f46MrU6dB|2|12-323-784-5793|148.98|FURNITURE|c packages. blithely final instructions according to the regular pinto beans\n702|Customer#000000702|BDKtDAva8rBuCWXT6jXb2JJY7YoRS|3|13-549-296-5659|4782.05|FURNITURE| silent accounts. regular, regul\n703|Customer#000000703|ge1GEYt4ewGUiSeqBA4rNB5JhyQ92uKF|6|16-741-513-6919|3998.42|AUTOMOBILE|arefully final accounts cajole quickly blith\n704|Customer#000000704|41s9yU0ossOKgBvjPPSTZqhfciy|9|19-998-886-1551|441.44|AUTOMOBILE|ously ironic instructions believe about the pending, regular deposits; final accounts cajole alongside of t\n705|Customer#000000705|YKdZRFEGcclF6rmdoiNCY8|18|28-991-405-7914|3199.00|BUILDING|s pinto beans sleep busily. ironic, bold theo\n706|Customer#000000706|ycxysGDuWHN98FS4lZ2obT9ZPNRWjJTsHbQXvi|19|29-468-275-6045|6496.08|AUTOMOBILE|ses cajole furiously after the ironic requests. pinto beans sleep busi\n707|Customer#000000707|DT0nzkijELHyI|21|31-796-903-4461|2619.52|FURNITURE| the deposits. fluffily ironic pinto beans wake quickly. slyly even foxes sleep slyly quickly final deposits. ev\n708|Customer#000000708|9iJJ868sXAdFgZyo0V8cfPRTRaUc3d|17|27-796-490-6026|2297.33|BUILDING|p along the carefully ironic pinto beans. even pinto beans are fluffily against the furiously quick dolphins. s\n709|Customer#000000709|drDnC6YXTJgcdfJkpbhrQ7z7XmCsoym2t22jwg|18|28-117-812-5493|-867.94|BUILDING|g the blithely ironic multipliers sleep a\n710|Customer#000000710|OCLSZuXw1AEK NLvlofMkuK,YNe,bJD40a|22|32-459-427-9559|7412.12|FURNITURE|ges integrate express, even ideas\n711|Customer#000000711|EmACeG3r2Y9bxf7KLLgX,ZdQlATjGaafINO|15|25-306-725-3622|9591.51|MACHINERY|ackages promise. theodolites haggle along the express dolphins. special, special deposits affix slyly final pac\n712|Customer#000000712| 8w2pIiA4wWAhtjAdXR|6|16-843-486-5087|8667.09|BUILDING| express dependencies use fluffily final platelets. furiously regular accounts wake furiously. carefully careful cou\n713|Customer#000000713|ov fZJgESFHP P|10|20-595-832-3185|3829.03|MACHINERY|lites sleep slyly furiously express requests. even, even notornis affix furious, ironic p\n714|Customer#000000714|2,ARep1aMyhgNZqrkc,toQ3XY6FOiCZqNW|9|19-209-782-4244|2438.10|BUILDING|cross the bold pains. bold instructions haggle. even accounts wake slowly. carefully busy ideas need to x-ray sly\n715|Customer#000000715|9qLvF42uxUarKl4I 2pEKOMNJmo8Ro5EK|19|29-500-408-6392|85.05|AUTOMOBILE|hins boost quickly. quickly regular epitaphs haggle fluffily quickly bold pinto beans. regular\n716|Customer#000000716|jbXpMEcV9j,6ciftBXEMXDjE|12|22-501-849-6084|8364.99|AUTOMOBILE|as. even accounts about the slyly pending excuses lose bl\n717|Customer#000000717|TqWi1c aNhdExPcDD7KQ18W|12|22-660-518-8009|8510.94|MACHINERY|ns poach slyly. carefully express pinto beans ab\n718|Customer#000000718|w,GXCSSI4NEHAFPKG|20|30-605-635-8197|8438.40|HOUSEHOLD| regular packages-- pinto beans detect ironically. furiously even accounts detect unusual, pendin\n719|Customer#000000719|wry Gj3xd8QX ylUCulG|15|25-498-699-7824|-774.47|HOUSEHOLD|gly express pinto beans sleep furiously around the quick foxes.\n720|Customer#000000720|8shFEL7J9sq1NJXR8dixBqaTO,kbSx|22|32-575-838-4260|5357.32|HOUSEHOLD|furiously above the furiously ironic c\n721|Customer#000000721|N6hr4gV9EkPBuE3Ayu |21|31-174-552-2949|3420.64|AUTOMOBILE|ar instructions. packages haggle stealthily ironic deposits. even platelets detect quickly. even sheaves along\n722|Customer#000000722|aymiuXFyrALTRIzfbLDvtaj37ydq|20|30-633-109-9587|2724.80|BUILDING|leep sometimes express accounts. regular theodolites wake quickly ab\n723|Customer#000000723|5tKvCvmVB0yxyAF|21|31-542-178-2520|738.57|BUILDING|e quickly against the blithely final requests. regular pinto beans nag fluffily fur\n724|Customer#000000724|dF80enl1y5MfccEMqYz0JSY|5|15-316-638-4703|3035.29|AUTOMOBILE|s. bold accounts about the carefully final packages detect slyly pending platele\n725|Customer#000000725|fm8t3X05Wm,PQxPSPHZy,rHI,wUBLTFO5S |16|26-923-317-3870|5030.24|FURNITURE|ts. accounts after the even pinto beans boost across the ironic, silent accounts. reques\n726|Customer#000000726|4w7DOLtN9Hy,xzZMR|3|13-168-233-7513|6253.81|BUILDING|wake after the regular, final instructions. final epitaphs mold fluffily blithely ironic id\n727|Customer#000000727|wLIX8hKnk0qjUAAGqm|18|28-815-316-3525|807.37|FURNITURE| requests-- quickly regular pinto beans need to detect \n728|Customer#000000728|uACufK5vIMlsq,v9d4U5ZWi,|24|34-195-384-1105|4349.73|BUILDING|re stealthily furiously bold requests. carefully final pinto beans lose slyly\n729|Customer#000000729|YZxBLBAqBmW53g2ia6s4u,MlJ4WhW|20|30-900-572-2883|8947.26|MACHINERY| packages about the furiously even foxes sleep furiously furiously pending ideas. slyly ironic epitaphs\n730|Customer#000000730|ORP6ofUwyD1|2|12-513-973-2702|4718.23|AUTOMOBILE|foxes would cajole furiously slyly special forges; slyly regular pinto beans inste\n731|Customer#000000731|D0ceqTVRO3EctrC|7|17-731-915-9753|9311.17|MACHINERY|lar dolphins are carefully slyly final theodolites. unusual accounts integrate across the regular, final deposit\n732|Customer#000000732|tQ,r4voHok7oeKw9msDh4ORGje|16|26-154-285-2634|4830.51|BUILDING|structions are after the even deposits\n733|Customer#000000733|Ks7Ed2g7zDP905tLGdGcFtomDNchNdaPK2cd|13|23-110-948-6754|4351.09|HOUSEHOLD| furiously. pending, quiet packages nag slyly. stealthy pinto beans haggle \n734|Customer#000000734|xULRxhbfzPT5nBh|18|28-880-175-5173|9750.71|AUTOMOBILE|tions against the ideas haggle furiously quickly regular forges. carefully final requests solve even,\n735|Customer#000000735|8Gn WOTY4cgGaoHwezrZ,JN,Px8e sr|5|15-959-576-4327|293.88|BUILDING|ites. furiously unusual frets boost among the slyly ironic theodolites. packages cajole\n736|Customer#000000736|zQNs5e2aFcVY8MigFQEBtoCaQ9e|1|11-617-726-2039|9114.68|MACHINERY| the deposits. blithely regular asymptotes boost q\n737|Customer#000000737|NdjG1k243iCLSoy1lYqMIrpvuH1Uf75|18|28-658-938-1102|2501.74|AUTOMOBILE|ding to the final platelets. regular packages against the carefully final ideas hag\n738|Customer#000000738|iecb89zRRNeN3KywZSaPXEWMjz|10|20-722-370-5220|2734.95|MACHINERY| carefully slow instructions. furiously express dependencies alongside of the ca\n739|Customer#000000739|pAROUfTi3wCEHi1PXu|14|24-309-302-6776|6344.18|HOUSEHOLD|ly regular accounts. blithely final theodolites sleep blithely. furiously regular decoys cajole blithely. bold de\n740|Customer#000000740|FCerGpsfsWAsBrQTyqdzMxUQnbQembHKGg|10|20-215-156-3727|1733.76|BUILDING|ets. final, blithe theodolites abou\n741|Customer#000000741|jiPAOQuJ5gIauMfvcbE8lKUVPMp|12|22-560-854-2068|9195.41|HOUSEHOLD|ve the fluffily regular accounts wake carefully ironic grouches. slyly bold theodo\n742|Customer#000000742|2qRObRkFktME6SsNV0Pa3L8txbA0AFtXuWsKrkW|12|22-610-582-8610|6381.24|HOUSEHOLD|eodolites alongside of the daringly ironic deposi\n743|Customer#000000743|WJ  lVLsFSgZJCHHLqwRmvCjWvYlCs0c2TvO|20|30-743-559-7934|8450.37|HOUSEHOLD|ptotes. furiously even accounts haggle slyly. ironic foxes sl\n744|Customer#000000744| nYSoGuQkf|7|17-121-555-4268|2458.81|HOUSEHOLD|n packages. furiously silent foxes sleep regular, regular accounts. slyly ironic patterns cajole fluffily\n745|Customer#000000745|vjuHvDKdaomsivy l|18|28-913-438-9403|7115.14|FURNITURE|o beans. bold, regular theodolites haggle carefully about the quickl\n746|Customer#000000746|JOKj8N2QKUm8Gi,F4qX0fLVy|20|30-154-354-9928|1164.46|MACHINERY| final theodolites. final pinto beans haggle-- furiously \n747|Customer#000000747|uuichgTY7NjlZaaRZ6S7KzvapZWvAiCUWAQr|8|18-793-504-2931|67.95|FURNITURE|eodolites. blithely regular pinto bea\n748|Customer#000000748|1 nkl3GMSnweulTNAMPeB8Sa5aSIk|23|33-403-226-2580|6959.09|AUTOMOBILE|nts. special excuses thrash blithe\n749|Customer#000000749|U1Dvu0r793a|24|34-158-697-9591|7491.42|MACHINERY|accounts was. final, final requests wake. theodolites was slyly. blithely even foxes wake carefully ac\n750|Customer#000000750|5OyNRajjgjjbaXtI rkxvB2lX4c6u|8|18-235-587-1274|269.90|BUILDING|s. regular, regular deposits sleep carefully blithely bol\n751|Customer#000000751|e OSrreG6sx7l1t3wAg8u11DWk D 9|0|10-658-550-2257|2130.98|FURNITURE|ges sleep furiously bold deposits. furiously regular requests cajole slyly. unusual accounts nag unusual ide\n752|Customer#000000752|KtdEacPUecPdPLt99kwZrnH9oIxUxpw|8|18-924-993-6038|8363.66|MACHINERY|mong the ironic, final waters. regular deposits above the fluffily ironic instructions\n753|Customer#000000753|9k2PLlDRbMq4oSvW5Hh7Ak5iRDH|17|27-817-126-3646|8114.44|HOUSEHOLD|cies. deposits snooze. final, regular excuses wake furiously about the furiously final foxes. dependencies \n754|Customer#000000754|8r5wwhhlL9MkAxOhRK|0|10-646-595-5871|-566.86|BUILDING|er regular accounts against the furiously unusual somas sleep carefull\n755|Customer#000000755|F2YYbRT2EV|16|26-395-247-2207|7631.94|HOUSEHOLD|xpress instructions breach; pending request\n756|Customer#000000756|Lv7cG by4Wyd8Hzmumwp8hSIZg9|14|24-267-298-7503|8116.99|AUTOMOBILE|ly unusual deposits. fluffily express deposits nag blithely above the silent, even instructions. expr\n757|Customer#000000757|VFnouow3LhLvEDy|3|13-704-408-2991|9334.82|AUTOMOBILE|riously furiously unusual asymptotes. slyly\n758|Customer#000000758|8fJLXfS5Zup0GQ3xBKL3eAC Q|17|27-175-799-9168|6352.14|HOUSEHOLD|eposits. blithely unusual deposits affix care\n759|Customer#000000759|IX1uj4NFhOmu0V xDtiYzHVzWfi8bl,5EHtJ|1|11-731-806-1019|3477.59|FURNITURE|above the quickly pending requests nag final, ex\n760|Customer#000000760|jp8DYJ7GPQSDQC|2|12-176-116-3113|2883.24|BUILDING|uriously alongside of the ironic deposits. slyly thin pinto beans a\n761|Customer#000000761|oObRVLlulGS5xikRk8La|19|29-835-631-4258|1525.96|BUILDING|ress Tiresias haggle across the never ironic ideas. blithely pending theodolites cajole ironically\n762|Customer#000000762|n5QsjD,gTSrdNRoRpvWqS|11|21-757-455-7898|3764.90|FURNITURE| sublate carefully carefully regular deposits. bold foxes along the gifts nag \n763|Customer#000000763|ACMrkbcf3a2J3aobVEmU5hGnHuc|1|11-232-719-3610|2650.27|BUILDING| ironic asymptotes are. slyly bold realms alongside of the fluffily specia\n764|Customer#000000764|F8WBznjtO2bX2knXl4ghnsp ixWylkf|16|26-714-322-4800|2061.45|MACHINERY|tructions. quick theodolites kindle\n765|Customer#000000765|b7w2edOUZNe0QX 3Ab3y5RFlMZX|20|30-544-657-7473|5683.33|AUTOMOBILE|ter the blithely regular foxes. slyly bold packages use bl\n766|Customer#000000766|zGTH6uWKoQxIE|16|26-283-847-8946|4677.55|BUILDING|ly special deposits boost blithely above the slyly ruthless ideas. carefully bold asymptotes unwind about the e\n767|Customer#000000767|9f3 XsYXdvP0E39ZBi7r7oRCns3PTx,H|17|27-526-143-3959|7647.09|BUILDING|ngly express dolphins boost carefu\n768|Customer#000000768| ,cIZ,06Kg|18|28-851-493-8588|9618.84|BUILDING|to cajole blithely express asymptotes. foxes use quickly. carefully special ideas could have to are bravely bl\n769|Customer#000000769|0eGzsjpDFsN0|5|15-102-635-4835|5950.86|HOUSEHOLD|ggle slyly pinto beans. furiously even excuses above the \n770|Customer#000000770|IjwJR6TjBJZbKIeouH2d|8|18-856-112-5677|2066.79|AUTOMOBILE|dolites haggle express, express requests. furiou\n771|Customer#000000771|J9UMiYJznHVHZDuCG,ErV0iiMStETS|23|33-761-371-4753|7461.74|AUTOMOBILE|arefully unusual accounts shall boost unusual pinto beans. ironic, final ideas boost. slyly i\n772|Customer#000000772|TBxlR0AAHeSYl0vyK 8joF|20|30-405-614-4887|7555.16|MACHINERY|ular requests. final ideas sleep. regular, even platelets could haggle blithely bol\n773|Customer#000000773|NyRSeog kIkD7YOb0EuSfSGxfptN5nkX26Mk6|8|18-456-377-3723|4578.24|FURNITURE|hely silent ideas could doubt carefully al\n774|Customer#000000774|95 O8gd08tdtmJwM0ebHUnDc|11|21-463-652-6686|-506.37|HOUSEHOLD|nd the bold platelets affix furiously doggedly express accounts. carefully unusual deposits serve furiousl\n775|Customer#000000775|Cg3M4gTXeIY7llMN2puop7D2|17|27-903-936-7924|1376.67|BUILDING|ts. blithely regular requests upon the ironic, final courts haggle sly, regular deposits. final requests \n776|Customer#000000776|rzhIStRHsiWoc6K,7yv3YMqVdrz|15|25-941-650-8313|2669.01|AUTOMOBILE| express accounts. furiously ironic theodolites serve blithely. blithely thin packages are among the eve\n777|Customer#000000777|27adTXaVp7araW|20|30-765-163-9750|9097.52|AUTOMOBILE|pinto beans; furiously special platelets haggle quickly against the slyly unusual foxes. \n778|Customer#000000778|tCuRA2W9y5iiGrcT7a4TzK|23|33-702-179-3134|52.43|MACHINERY|hely ironic instructions. regular, ironic requests affix along the carefully \n779|Customer#000000779|2cTZiS4ulZ74edT,RmDnh4ZaCrphMMh Ff2|5|15-940-483-5702|-902.48|HOUSEHOLD|old dependencies. pains haggle fluffily carefull\n780|Customer#000000780|CMxcdzgEUkCWP1|8|18-844-576-7345|9874.12|FURNITURE|, final packages use slyly regular deposits. slyly ironic instructions nag careful\n781|Customer#000000781|FQCAkyfV0 kL3,FNA1OlBjABak|18|28-478-388-5881|6403.62|MACHINERY|ake blithely blithely final foxes. blithely silent pinto beans haggle furiously. fluffily bold acco\n782|Customer#000000782|HFuyemzqz0g QhkL|19|29-850-576-7450|-326.32|AUTOMOBILE|usly bold deposits-- furiously ironic accoun\n783|Customer#000000783|01bR7OOM6zPqo29DpAq|1|11-920-256-8525|2436.32|FURNITURE| slyly carefully pending packages; doggedly bold theodolites boost slyly slyly dogged excuses: slyly expr\n784|Customer#000000784|evcGXqbosO6,qhx|14|24-975-574-2063|3170.47|BUILDING|cajole quickly. ironic dependencies wake quickly. silent ideas use furiously \n785|Customer#000000785|gEkI8kSq8RYgO6tiTA0AB7urZX8s2w03JGwtMi9|18|28-808-670-2983|108.14|BUILDING|nal theodolites. dogged, ironic deposits wake carefully. slyly regular requests after the furiously ironic \n786|Customer#000000786|viZtkiJ gbMcPrINM3Ez,33vOJW|13|23-413-365-2022|400.03|MACHINERY|ccording to the regular deposits. carefully final packages run. slyly final deposits h\n787|Customer#000000787|wwPe2vMZZ1n1Mm2z0qzDfS43FPj2Ndn|10|20-210-617-3870|212.16|AUTOMOBILE|e. ironic theodolites serve regular foxes. pending requests haggle slyly busy instruct\n788|Customer#000000788|LbFHoDpNUSu3AyDS7KLgjoQBJV|16|26-388-689-9272|-330.69|FURNITURE|. unusual packages against the \n789|Customer#000000789|DW0NMV Ci5V2bnsX0Al98plG1J0QZqNwcjEVW|9|19-176-517-4263|6038.60|BUILDING|as. quickly special courts integrate slowly final accounts. ironic packages are slyly about th\n790|Customer#000000790|CR bzmYYVP|0|10-368-832-9671|2724.98|BUILDING|riously final requests haggle to the blithely special requests. blithely pending gifts after the carefully silent id\n791|Customer#000000791|3ZWQ5xexnnLDEmxpmbg|13|23-575-775-4059|3694.81|HOUSEHOLD|posits cajole carefully along the slyly final packages\n792|Customer#000000792|icVt7HjGs,p3YL3nr1MHgaQIY5Gmzej57nB,b|7|17-392-500-4370|1672.46|AUTOMOBILE|latelets along the carefully even packages cajole blithely packages-- even pinto beans haggle \n793|Customer#000000793| SltK1IMp2Xvwb,A0x3Co1uhcwr|0|10-404-953-9048|2072.99|BUILDING|uickly silent foxes use after the\n794|Customer#000000794|RMY8 LyGnJ67NGc5cxPYiIDSF|23|33-633-470-5945|1709.50|FURNITURE|gular patterns cajole slyly blithely final ideas? furiously unusual courts wake among \n795|Customer#000000795|droXvSIcNEElsEYS|14|24-973-990-6608|8443.18|HOUSEHOLD|packages cajole furiously since the slyly\n796|Customer#000000796|79bj5Rk3jJj9ked7M|3|13-554-411-6773|2584.23|HOUSEHOLD|re slyly even deposits. pending pac\n797|Customer#000000797|bdGkzA5duas6LZ1ywB96K6Av3x 99q95h|0|10-994-609-7082|3063.11|HOUSEHOLD|iously regular packages mold fluffily. express, idle accounts nag furiously across the carefully ironic req\n798|Customer#000000798|wW2OgnHj6dBz tO9OXFqCLm|4|14-670-423-7529|-391.13|HOUSEHOLD|sleep slyly ironic, express ideas. slyly special packag\n799|Customer#000000799|LVk8ljWeIYTQQFMKCmxEeRpWQT|12|22-909-693-7833|2263.25|MACHINERY|ets haggle. busily final packages nag carefully after the bo\n800|Customer#000000800|mpI6pkdnWLZsBbQi4,uUC5Y3TcM9vmRIgZelrQ|14|24-555-630-2261|9443.39|AUTOMOBILE|ly alongside of the carefully ironic deposits. blithely ironic packages are blithely agains\n801|Customer#000000801|UQ67hfDJlxgX68hiFPmDuHav12Vx|16|26-439-495-8236|5207.32|FURNITURE|sits wake blithely according to the slyly un\n802|Customer#000000802|ZDk4Suvi8gMp2LLAOW6nFA 3u|0|10-606-236-5778|1377.52|BUILDING|the carefully even pinto beans. carefully unusual pinto beans against the asymptotes cajol\n803|Customer#000000803|zm7Xs6RJJJfZ|18|28-855-429-9109|6003.09|BUILDING|. blithely special instructions\n804|Customer#000000804|u6xYwCLD,Vd4ODt8|24|34-861-760-4796|3.43|AUTOMOBILE|usly. final accounts integrate slyly above the furiously sly pinto beans. furiously unusual ideas \n805|Customer#000000805|wCKx5zcHvwpSffyc9qfi9dvqcm9LT,cLAG|10|20-732-989-5653|511.69|BUILDING|busy sentiments. pending packages haggle among the express requests-- slyly regular excuses above the slyl\n806|Customer#000000806|FTM62Dujm2BoblKFY w|12|22-888-883-2475|6859.28|FURNITURE|the carefully regular accounts breach above \n807|Customer#000000807|zKjtrn5FGIs|17|27-333-779-5333|1929.89|MACHINERY|y final patterns? carefully pendi\n808|Customer#000000808|S2WkSKCGtnbhcFOp6MWcuB3rzFlFemVNrg |19|29-531-319-7726|5561.93|BUILDING| unusual deposits. furiously even packages against the furiously even ac\n809|Customer#000000809|eApaa5hW3Mqp11ZuOP|16|26-776-223-5427|-594.23|HOUSEHOLD|ackages. quickly unusual dugouts c\n810|Customer#000000810|3J1wwVhwzVT7XM1v3Rzx90a8c4|9|19-997-929-2765|9632.77|FURNITURE|ep pending deposits. quickly bold accounts are carefully instructions. ruth\n811|Customer#000000811|Rau3ADOLCNXM42D6fjQTFuueQce2BHw9r|15|25-575-731-2159|9010.02|BUILDING|kly pending foxes dazzle fluffily after the furiously bold packages. fluffy\n812|Customer#000000812|u9uI3BE68quHja6k0,UXRYFgvLHl3jKhn|17|27-714-618-2239|3428.67|BUILDING|ar packages. packages thrash amo\n813|Customer#000000813|VBNrCYm67O|9|19-585-173-1514|5673.47|HOUSEHOLD|ts cajole silently bold pinto be\n814|Customer#000000814|KmMC1nd5iOXTRlhzMu13Wtx|15|25-342-269-3824|4011.18|BUILDING|yly at the blithely regular excuses. carefully thin pinto beans boost slyly furiously regu\n815|Customer#000000815|TmpTFwt3b0 WQUmljE1LKbg50dtQszn67qEog|12|22-941-606-7227|5841.33|HOUSEHOLD|unusual accounts. slyly ironic pinto beans nag against the regular, regular requests. slyly p\n816|Customer#000000816|k2dnnQZowe|24|34-457-910-7430|9239.32|BUILDING|accounts cajole slyly quickly express platelets. carefully silent theodolites wake carefully. pe\n817|Customer#000000817|jDJt0Wfk0,Wcx5HOI5itS XqUJmMpjzJ|0|10-566-341-4382|7297.64|MACHINERY|y against the carefully special patterns. unusual accounts sleep quickly final instructions. \n818|Customer#000000818|CvQvKClYoh9lPjMngrjbCoxqqnp2QFiTe8eF|20|30-943-141-5174|-574.39|BUILDING|yly. blithely final platelets haggle regular, regular ideas: furiously r\n819|Customer#000000819|cug3zDy qHUaZMQNEtYlWK3R,mGrid4Of|12|22-424-990-1743|2152.05|MACHINERY|y final accounts sleep slyly express ideas. carefully express foxes sleep. carefully stealthy pin\n820|Customer#000000820|xsYy3Nu7RhNYyBqL8dD0fONh|4|14-993-368-5047|-614.28|AUTOMOBILE|g, final ideas. slyly ironic packages dazz\n821|Customer#000000821|PVdTfF7cfPueJS0MBncE5v4bKfPV64Zg|5|15-901-460-2033|2691.42|FURNITURE|wake carefully. slyly express deposits wake furiously among the fluffily final deposits. pa\n822|Customer#000000822|ZoRr18ZiDTtkRduo6PO60gNRg,b46zOe QdS8B|13|23-410-801-1644|736.47|BUILDING|ding dependencies hang alongs\n823|Customer#000000823|HuoOuJLxx7S8YDQhexfmIX|0|10-109-430-5638|738.79|AUTOMOBILE|l requests are against the slyly ironic pinto beans. blithely unusual packages wake alongsid\n824|Customer#000000824|qHxq5m g6Ug,SDq7R17|18|28-454-124-5859|6840.29|AUTOMOBILE|ffily unusual packages. furiously final ideas \n825|Customer#000000825|L8P98o,xq9E78kmuadw,Z7Rwy|10|20-492-863-1129|1809.01|HOUSEHOLD| busy deposits. careful, bold foxes eat furiously according to the final as\n826|Customer#000000826|f5Y UZoE nR|13|23-762-328-7631|8938.14|BUILDING|usly final dinos. final depths wake slyly across the final, bold requests. bl\n827|Customer#000000827|fmmKD8aBlF9bFndkYf402cHVrUj8FhGZtHeL|14|24-196-343-6537|5411.20|HOUSEHOLD|om the carefully even requests are slowly along the regular \n828|Customer#000000828|0PkG ELwBIgU4AsNcjDc5Q,9Gj|23|33-108-680-4317|7059.68|BUILDING|cial instructions. accounts nod special accounts. slowly even deposits belie\n829|Customer#000000829|4oht3f64ZWA1,ACbBP9DNxjjG1CbIqOCK|8|18-404-707-6095|4086.56|MACHINERY|deposits cajole slyly ironic, express packages. asymptotes cajole ruthlessly \n830|Customer#000000830|4fNmWCmfys1jUI|23|33-408-548-6806|7775.65|AUTOMOBILE|ironic packages integrate alo\n831|Customer#000000831|1DyQwHuUncB7BSnQUiW8ksexb|20|30-495-591-5384|3401.49|AUTOMOBILE|c instructions. fluffily bold requests are furiously quickly ironic deposits. carefully \n832|Customer#000000832|eNIy9PatAWlh0|19|29-264-864-8387|-201.49|HOUSEHOLD|the special ideas. finally ironic accounts doubt. slyly regular ideas\n833|Customer#000000833|t3qDCo,Yh MZcJFV6PibeY,MUunz|6|16-624-307-4875|-526.14|FURNITURE|uickly final orbits across the blithely express accounts integrate furiously among the final sheaves. blithe\n834|Customer#000000834|y4mA, TY6DW4gAY|14|24-637-803-7812|-976.25|MACHINERY|regular dolphins. furiously careful ideas eat beyond the bravely regular packages. slyly unusual deposi\n835|Customer#000000835|Sjqs42jh111|21|31-700-242-6347|2106.81|BUILDING|gside of the furiously final packages wake quiet, permanent accounts. unusual packages into the unusual, ironic req\n836|Customer#000000836|rayz3tXswDbL4hyIr5SMrEIOTqvmok48e|12|22-181-313-6281|9184.72|FURNITURE|press deposits-- fluffily express accounts cajole carefully according to the daringly even deposits-- furi\n837|Customer#000000837|mMNC8wjT5aC655e3|9|19-572-730-8324|2701.29|BUILDING|he bold, pending theodolites. special, bold accounts haggle. blithely regular ideas nag fluffily regular packages\n838|Customer#000000838|BxDlz44b56kXjsTpl|5|15-919-192-7197|1605.76|AUTOMOBILE|elets. never express accounts haggle slyly. depths along the requests haggle bl\n839|Customer#000000839|Ch8 wZk5UKK45BSq1uZLJL0Z A8UUoms|22|32-435-825-7973|2924.78|HOUSEHOLD| close accounts. unusual pinto beans sleep quickly slyly bold instructions. blithely unusu\n840|Customer#000000840|9wA4l70okCEW3GnYQvlIAXetDgD50l|22|32-802-156-1748|6799.00|AUTOMOBILE|usly according to the slyly regular pinto beans. blithely regula\n841|Customer#000000841|pSISKRxFIgVasW71fLyaEuODHZQLBfd6E c7jo|9|19-556-695-9964|884.20|FURNITURE|ly. slyly special instructions wake. carefully even accounts sleep quickly slyly silent deposits. s\n842|Customer#000000842|stRBRwdFCkdN|0|10-272-126-1413|8635.49|FURNITURE| silent pinto beans. bold, pending platelets sleep furiously express theodolites. fluffily express reque\n843|Customer#000000843|OEJ7AElvHgEtDpjcrXgLK|14|24-979-584-7928|675.73|HOUSEHOLD|ronic somas. accounts was among the carefully unusual requests. unusual pinto beans are blithely regular depths. s\n844|Customer#000000844|1nUzjsH9HS1sPAGLwDIom9IESivLeEh1BvyynjU|11|21-285-410-4046|2954.90|BUILDING|ymptotes. ironic, unusual notornis wake after the ironic, special deposits. blithely fina\n845|Customer#000000845|fIq5p0GpDtw6FIsPMdbqNYgfSw3gL9ep|13|23-125-871-9246|6898.89|AUTOMOBILE|deposits above the deposits wake bold instructions. special accounts cajole. ideas along the regu\n846|Customer#000000846|EWQcGkx0CU5ZGUNcTV9bBCQ4qnuKsHC|18|28-766-714-9136|7650.73|MACHINERY|s. quickly thin requests against the ironic, bold requests haggle above the final foxe\n847|Customer#000000847|oR9VbMI LkR2GUv4MRmylhb|23|33-645-447-3944|4822.08|HOUSEHOLD|haggle carefully. pending, bold foxes play carefully ironic frets: slyly unusual ins\n848|Customer#000000848|a nIm5Bk7 RMqMZ6|15|25-655-714-7125|5685.59|BUILDING|st furiously blithely pending packages. deposits across the \n849|Customer#000000849|kqRGW2JQtTM,a6 DzJMgcU9U|24|34-718-798-7751|9670.64|MACHINERY|ly silent ideas. fluffily even packages boost carefully fur\n850|Customer#000000850|GdhPVh9rkPqSt v17ZuxIlx8c1N8G|22|32-546-203-4000|7576.55|FURNITURE|ly ironic accounts whithout the regular waters are blithely abo\n851|Customer#000000851|H9HRTaOz4yI9elrUUiS|9|19-678-843-9850|1144.23|FURNITURE|cross the final packages. blithely ironic excuses serve slyly about the final accounts. fluffily\n852|Customer#000000852|fR1Cq8d6m,zJwS3FFrOBV7u|5|15-252-941-6247|1098.98|AUTOMOBILE|unts thrash quickly through the express, regular theodolites.\n853|Customer#000000853|U0 9PrwAgWK8AE0GHmnCGtH9BTexWWv87k|2|12-869-161-3468|-444.73|MACHINERY|yly special deposits wake alongside of\n854|Customer#000000854|flSR,SlEXwxrDcm3uedfK1Oiq,c9mZI|0|10-544-967-2382|3393.86|AUTOMOBILE|t slyly after the furiously even escapades. carefully regular pa\n855|Customer#000000855|i8mS 0Plk2tI8HG1Mnzj8v5RIl5JqmwTSW2Wq|1|11-379-392-2701|3550.49|BUILDING|s. carefully final deposits detect furiously slyly even packages. final, special accounts eat along \n856|Customer#000000856|X4U7LH4YtDzephie|15|25-336-316-9641|6988.55|FURNITURE|y bold pinto beans according to the pending foxes \n857|Customer#000000857|TFCCMlSEyrItYvEZy068NhrUxJz|5|15-856-649-3113|7537.16|AUTOMOBILE|g to the deposits cajole furiously final deposits. furiously silent dependencies agains\n858|Customer#000000858|8qSqHq,Fc378KIPPbTNmaL 0bpAwO|8|18-509-453-8977|-829.37|BUILDING|, special accounts. ironic, pending requests might cajole flu\n859|Customer#000000859|yp6dlWnpbzQboP5Xj8W|17|27-379-135-4463|6737.18|BUILDING|uffy foxes boost about the eve\n860|Customer#000000860|t0GtEsh39KvInVMH7CDn0xi|12|22-895-319-1388|6836.89|BUILDING| deposits above the blithe requests maintain furiously slyly even packages. slyly regular requests boost. blith\n861|Customer#000000861|GvlCZ4fJYbHTOTYczE1iH2k6K9edUyNxWZsl6x|14|24-939-734-5650|-336.80|BUILDING|arefully regular requests unwind slyly \n862|Customer#000000862|zkaZ,iOp8t9MBK9T,JIPGL5hmBmK,xjkjHEP|2|12-479-479-6941|2338.92|MACHINERY|yly regular foxes sleep. quickly ironic foxes use furiously express deposits! carefully regular\n863|Customer#000000863| oaBBxuQLKPawG1yqOc7cyVhOezCy|3|13-801-392-5922|5274.52|AUTOMOBILE|d accounts detect. bold pinto beans against the slyly ironic\n864|Customer#000000864|lKcUKtfu0myNF5msATCFVHHXfMg3,cdb|12|22-897-966-6672|8932.76|AUTOMOBILE| instructions detect slyly. never pending dependencies use slyly enticing accounts. slowl\n865|Customer#000000865|UIv4t5cbA7j1ftOSatj2EKah3p|9|19-245-153-1471|3016.38|BUILDING|nic deposits print blithely slyly final accounts. special, express accounts are fluffily a\n866|Customer#000000866|u,b0GdP7dZg|20|30-436-285-7224|6265.88|FURNITURE|ts. bravely express pinto beans after the blithely bold requests x-ray busy orbits. furiously pending \n867|Customer#000000867|KmET9DxfPVs15pIUUWQ|14|24-522-194-4543|5680.23|FURNITURE|t according to the careful packages. regular \n868|Customer#000000868|cFG8Fa5h1e uvHJ9pgZAO|22|32-850-421-3334|7616.48|FURNITURE|the requests. regular dinos at the even requests use furiously around the sheaves. slyly final theodolites bo\n869|Customer#000000869|PDGU7BXDNXAo0vmo7QtDm,yCMVcD|2|12-379-344-7132|1228.06|HOUSEHOLD|. final packages wake slyly? blithely even deposits haggle carefully regular packages. unusual requests\n870|Customer#000000870|6wGPZX1SbXLGtweqx8jK|2|12-953-532-5903|1970.76|BUILDING|es cajole slyly. furiously even asymptotes are furiously regular packages. special, final ideas s\n871|Customer#000000871|KcLmBKitbx7NvU7bpu9clIyccxWG|20|30-933-714-8982|-395.89|HOUSEHOLD|ts. blithely silent courts doze. regular atta\n872|Customer#000000872|vLP7iNZBK4B,HANFTKabVI3AO Y9O8H|17|27-357-139-7164|-858.61|BUILDING| detect. packages wake slyly express foxes. even deposits ru\n873|Customer#000000873|XFnr9C2bANXL|6|16-375-385-5712|-797.38|AUTOMOBILE|lithely ironic, silent forges. furious\n874|Customer#000000874|rdo knkGhtvpH6dbLkQon8QsrK1z4LFUpaVDTOn2|0|10-886-494-4217|5391.74|AUTOMOBILE|st the deposits. furiously even requests cajole slyly. bol\n875|Customer#000000875|8pQ4YUYox0d|3|13-146-810-5423|-949.28|FURNITURE|ar theodolites snooze slyly. furiously express packages cajole blithely around the carefully r\n876|Customer#000000876|NMzgtdV zCuRIMK0vV,DP9ynDd6Z9X3T|20|30-320-481-3076|4367.63|MACHINERY|ronic requests haggle blithely. slowly ironic ideas against the fluffily \n877|Customer#000000877|uYO2BKogrHcOcHEgzjlmZAa1QYyR45i8|16|26-400-912-7812|1755.33|MACHINERY|packages cajole carefully. slyly regular pinto beans bo\n878|Customer#000000878|hUCH2juGwk4OtThyY8p35Hi0,IfOGA|15|25-465-180-9022|8781.53|FURNITURE|the quickly pending deposits cajole care\n879|Customer#000000879|EJcG18hFrS0SPT0yvl1b|2|12-878-466-6505|2235.20|HOUSEHOLD|ntegrate by the carefully special requests. dolphins sleep e\n880|Customer#000000880|ogwHmUUFa1QB69pAoYAAoB0rjbdsVpAQ552e5Q,|8|18-763-990-8618|-77.63|FURNITURE|regular requests. regular deposits ar\n881|Customer#000000881|XJ94RTR2oXI omeh|4|14-127-261-7876|2141.71|FURNITURE|pinto beans. asymptotes about the slowly even theodolites are pending requests\n882|Customer#000000882|hsKaXwHCLD|2|12-437-842-6799|1650.12|HOUSEHOLD|ts. quickly regular packages alongside of the furiously silent theodolites nag slyly after\n883|Customer#000000883|qVQ8rWNU5KZYDcS|3|13-526-239-6950|479.96|FURNITURE|uctions are carefully across the regular, regular asymptote\n884|Customer#000000884|5KyisO0Tv9ZtlJhzyI7vAe88|21|31-483-489-6172|1601.60|FURNITURE| among the quickly express theodolites. accounts sleep furiously along the special, pending depende\n885|Customer#000000885|nNUbC73nPBCKLg0|5|15-874-471-4903|-959.94|HOUSEHOLD|sits impress regular deposits. slyly silent excuses grow \n886|Customer#000000886|QOTGbGPJjNPD7IrfAILA1da|12|22-771-691-7229|1194.33|AUTOMOBILE|slyly even foxes according to the pending, special accounts use carefully against the courts. regular,\n887|Customer#000000887|CoInl1fmf5MjYn15AdA|1|11-136-651-8293|7009.42|MACHINERY|ording to the fluffily regular foxes nag fluffily instructions. thinly careful accounts around the furio\n888|Customer#000000888|3vlJp0W8cniEXV|12|22-855-455-1154|6358.04|HOUSEHOLD|sleep carefully quick pinto beans. packages hinder beneath the instructions? ironic, unusual theodoli\n889|Customer#000000889|pLvfd7drswfAcH8oh2seEct|13|23-625-369-6714|3635.35|FURNITURE|inal ideas. slowly pending frays are. fluff\n890|Customer#000000890|rvsLCrRX9z,IPaaF9kqwvLLxueobbPiH4pz|4|14-938-708-4678|3329.21|BUILDING|ironic accounts cajole ruthlessly above the carefully pending accounts. quickly regular theodolites cajole. iro\n891|Customer#000000891|AW0m6YSpe,BNPHvBj|11|21-439-958-7518|6032.18|FURNITURE|sits. final foxes run. quickly pendin\n892|Customer#000000892|N KwiRAwIk6KL9WJ6vt0G|9|19-589-784-1249|4799.98|FURNITURE|ses are carefully. quickly regular theodolites cajole. carefully express accounts wake sly\n893|Customer#000000893|W6m7LofOZoik72ku|13|23-827-724-6816|8250.87|BUILDING|ets. ironic instructions nag even, regular courts. slyly iro\n894|Customer#000000894|5y7m8Ts4kDf|16|26-844-679-1540|4483.42|AUTOMOBILE|carefully regular accounts sleep carefully slyly ironic dependencies. blithely ironic accounts wa\n895|Customer#000000895|MDaJr8ekGTS79bS7CH8f1WgWPU|0|10-933-819-2037|904.43|AUTOMOBILE|ggle final packages. slyly regular instructions affix fur\n896|Customer#000000896|Tu1ZBNgiSEL9Ns|0|10-425-565-3199|7659.72|AUTOMOBILE|affix carefully unusual requests. furiously fin\n897|Customer#000000897|nW1X1Hl9uWycuBEu3F3|6|16-988-776-4568|1999.42|MACHINERY|riously regular ideas sleep into the final, unusual\n898|Customer#000000898|JrvrIEzAre,VJzJCi3SEmib1T2,YVXVvOGxaVZwR|3|13-265-738-4361|8137.24|AUTOMOBILE|s haggle around the special dependencies. slyly regular requests are according to the idly sly ideas! fluffily final\n899|Customer#000000899|Th5XO5ImeCe9nHFQfQMCkNcmf5WHSeYQaR5TJ|2|12-594-534-9654|8605.53|FURNITURE|rding to the furiously unusual accounts. express, express accounts nag furiously. ide\n900|Customer#000000900|kEhE1Y,OoZTDv,Auh d5G ALINN0rND|1|11-422-328-1612|3195.39|BUILDING| packages sleep slyly around the quickly special packages. final accounts are furiously. bol\n901|Customer#000000901|QUyXt94YM6Ou6rDqK|12|22-202-667-4372|938.35|HOUSEHOLD| foxes maintain. theodolites sleep above the regular deposits. slyly bold excuses boost careful\n902|Customer#000000902|A1hnMyYPSkXf7QgOPD2H|4|14-209-883-5797|5858.48|AUTOMOBILE| sometimes regular epitaphs. furiously regular gifts against\n903|Customer#000000903|URTiQupkhObWG39,kZ3CfU|7|17-706-779-2078|509.23|FURNITURE|kly doggedly even instructions. regular, regular accounts along the even, bold packa\n904|Customer#000000904|YdJEbNygDU6DrgWXQY6orasq|5|15-940-929-4572|9562.82|BUILDING|nic dolphins alongside of the slyly final ideas run\n905|Customer#000000905|f iyVEgCU2lZZPCebx5bGp5|3|13-803-156-2231|-600.73|BUILDING|slyly closely ironic dolphins. blithely ironic asymptotes haggle carefully ironic theodolites. furiously\n906|Customer#000000906|1Uavkms1A5z|6|16-594-569-6627|-613.45|HOUSEHOLD| accounts. furiously silent ins\n907|Customer#000000907|UeVLwnnpccsG1pbQmN7pzD|10|20-501-816-7673|5751.31|HOUSEHOLD|sits haggle quickly above the excuses. slyly ironic packages print furiously. carefully pending reques\n908|Customer#000000908|Fa5bchMKUMsaNKOXAiu9pX  ME|12|22-814-669-9320|3215.96|AUTOMOBILE|quickly express packages. blithely fina\n909|Customer#000000909|b 2X284A5AGpt8skuYwMvTyK68srMKikPst6X|24|34-717-350-9722|5565.58|MACHINERY|se carefully around the special, regular requests. ironic theodolites cajole quickly theodolites. regular, ev\n910|Customer#000000910|Qg8TJTCT1mJ9H|9|19-899-463-4292|5794.69|BUILDING|uctions. silent requests after the regular theodolites haggle furiously across the ca\n911|Customer#000000911|VS0fia,lJ RvUf68 l4Unv,Vx|13|23-121-746-7339|6364.60|BUILDING|gular foxes! permanently regular packages wake. quickly regular deposits detect blithely. carefully express\n912|Customer#000000912|dQA12NEPQK1A5mvD|14|24-348-437-3105|3861.36|FURNITURE|sits use slyly carefully final dependen\n913|Customer#000000913|aohNRUjsMbBNE1Ax|14|24-500-946-3315|6935.16|FURNITURE|y blithely final dugouts. accounts wake accounts. asymptotes above the even \n914|Customer#000000914| LErnJFeOuDeMgvVzPKHS|5|15-915-758-7313|1230.79|MACHINERY|accounts. slyly final attainments boost slyly express, pen\n915|Customer#000000915|mtGezp1BRzcfPVl,1,G8Wl|0|10-452-398-2445|3776.53|AUTOMOBILE|r ideas. final dependencies haggle fluffily. express ideas behin\n916|Customer#000000916|9Zo7nkmzJla4Q4PE5mbw|23|33-511-587-8754|130.59|MACHINERY|sual ideas cajole carefully ironic excuses. final platelets use slyly. quickly special instructions nag. bold escapa\n917|Customer#000000917|KZ TS0omSFmUQkIOmzlXhwQS,OcF3wzz5|1|11-100-917-5264|3679.65|HOUSEHOLD|arefully fluffy pinto beans. enticingly silent requests affix furiously busily regular ide\n918|Customer#000000918|WSptkDdGQQyJ6|19|29-992-318-6425|-155.06|MACHINERY|s boost furiously slyly final dependencies! fluffily regular sheaves nag fluffily. slyly even courts sleep sl\n919|Customer#000000919|cHGtsqVvXRiK|9|19-537-180-2200|9774.97|MACHINERY|lar instructions. blithely final deposits haggle furiously bold pinto bean\n920|Customer#000000920|oDBFWKIP6M6OlYRPmqCBkVpVSj6uFa|2|12-905-464-3299|990.58|HOUSEHOLD|. ironic, pending frets haggle carefully. ironic,\n921|Customer#000000921|XYBVDdDifSYrW gUeDPhITqMjpjtbnc|8|18-765-936-2316|3651.09|HOUSEHOLD|g the furiously unusual theodolites are carefully accounts. slyl\n922|Customer#000000922|Az9RFaut7NkPnc5zSD2PwHgVwr4jRzq|7|17-945-916-9648|3869.25|BUILDING|luffily fluffy deposits. packages c\n923|Customer#000000923|ckBLWkfYtn2VZXWWqUGbDgbP|11|21-476-142-5086|7462.20|HOUSEHOLD|s. furiously express depths boost. regular requests boost furiously. even, unusual deposits cajole blithely. expre\n924|Customer#000000924|yKEtokQYXiuSSh8ZP5|15|25-518-232-9865|4212.53|BUILDING|ake slyly furiously even deposits. express pinto beans are carefully quic\n925|Customer#000000925|jn Razhw70hWtHN4iRBWsf1UmrFUPn36Ni562ex|10|20-753-609-6699|1939.39|AUTOMOBILE|ironic accounts cajole carefully. even, expre\n926|Customer#000000926| 3b8K2YhfbGDJOpSAUrvq82MnkhHBdwL|16|26-783-803-1329|274.50|AUTOMOBILE|ly quickly unusual foxes. fluffily regular ideas among the regular plat\n927|Customer#000000927|Uy7xvOwo4Ndha1tSxDKrQ gXUTdS ,YDqwE2YSO|17|27-332-891-1391|2417.65|MACHINERY|es affix quickly carefully regular platelets. slyly special theodolites would sleep furiously after the special, \n928|Customer#000000928|A9 UduEb48ffOe27FxMXF|21|31-508-509-6393|8330.70|HOUSEHOLD| instructions cajole fluffily alongside of the blithely regular re\n929|Customer#000000929|c dPfaAmmoLjR3m|23|33-487-459-1026|4079.18|MACHINERY|ously silent foxes cajole slyly pe\n930|Customer#000000930|84jHYR8u2XvhyT|19|29-562-904-5451|4787.20|FURNITURE|sual ideas according to the furiously regul\n931|Customer#000000931|M,lWVafqdRIO, WnAyLLt|0|10-349-498-1720|2409.69|BUILDING|s the carefully regular packages: pending\n932|Customer#000000932|HN9Ap0NsJG7Mb8O|13|23-300-708-7927|6553.37|BUILDING|packages boost slyly along the furiously express foxes. ev\n933|Customer#000000933|V0SPv2VbrNo7Pj|14|24-623-803-8018|8541.16|MACHINERY| accounts haggle quickly against the blithely even accounts. deposits sleep blithely quickly p\n934|Customer#000000934|UMAFCPYfCxn LhawyoEYoU9GZC7TORCX|12|22-119-576-7222|-592.69|AUTOMOBILE|fluffily requests. carefully even ideas snooze above the accounts. blithely bold platelets cajole\n935|Customer#000000935|XkVT4jvetY4JV76IAkd91sSp9CqsICE|0|10-724-445-8323|2531.25|MACHINERY| furiously pending ideas. daringly \n936|Customer#000000936|hwJIFpxofea6CLEbWZFsWUxNrGFLANp|11|21-100-538-9635|3650.90|MACHINERY| furiously enticingly final foxes. pending requests wake quickly according to the slyly regu\n937|Customer#000000937|usrG6ohdPROyd98c9|23|33-869-990-3946|668.51|AUTOMOBILE|ickly alongside of the express, express ideas. sly\n938|Customer#000000938|wrq9S3rEW8zXUVCXpa7uKi|12|22-157-321-7590|2584.52|BUILDING| the quickly special accounts are regular patt\n939|Customer#000000939|jYaDdfxAlL1aVKPfN|19|29-627-844-1293|8059.51|FURNITURE|ages integrate carefully. sometimes even sheaves wake sometimes unusual ac\n940|Customer#000000940|T7ROXBXdajS,vkwy3VuC8wNvA|19|29-958-573-1004|253.59|AUTOMOBILE|ent accounts. slyly even accounts breach across the dolphins. quickly regular pains dazzle carefully. sl\n941|Customer#000000941|297w97UgOfpV3pv2QniJUWBKq0BRpcawOfpj|19|29-745-875-1061|2990.18|FURNITURE|e furiously along the slyly bold pains. ironic, ironic foxes affix quickly even deposits. packages caj\n942|Customer#000000942|y0OKxFyfXeYuklJDY9RwujlNIC2ETXo9HxZCVhg1|18|28-560-449-7675|5898.17|HOUSEHOLD|lar depths! carefully regular pinto beans after the deposits wake about the packages. final, final instruc\n943|Customer#000000943|74dBRGOKLFEQEqCgH2x8WGL9tubtgJAbHu|5|15-483-251-8603|7541.05|FURNITURE| quickly along the silent, express braids. quickly pending packages boost fluffily furiously regular foxes? a\n944|Customer#000000944|8lO9F4WK6PKWXiocyE,ojIfPNfVY8|4|14-485-139-5142|9454.78|BUILDING|ilent pinto beans are according to the regular, final somas. idle, bold foxes was. som\n945|Customer#000000945|300zKNJ9lg|15|25-542-662-1673|9615.39|BUILDING|. ironic deposits haggle among the carefully regular excuse\n946|Customer#000000946|ufHQOmRhQoLSiyfQ|21|31-152-357-9762|2990.73|FURNITURE|ly after the furiously regular asymptotes. foxes play quickly ironic packages. dinos along the silently expre\n947|Customer#000000947|JnzDRxqCwjRPyeq70wrxzKWLXI|22|32-838-393-6825|4092.24|BUILDING| blithely final instructions grow furiously ironic requests. furiously special theodolites sleep after \n948|Customer#000000948|yxBr7nLGxxwECEk|17|27-125-968-3750|4346.90|AUTOMOBILE|he final, even deposits. furiously special pl\n949|Customer#000000949|tOBbBIfhWM lNI3YxTYH8Or8Ki|2|12-391-316-1861|5340.67|AUTOMOBILE|pecial, pending dolphins. slyly final ideas boost quickly. carefull\n950|Customer#000000950|mi3o6cp47mo8Miqh9d R1XWStjxatcQqHQZW|15|25-849-760-2017|9609.77|FURNITURE|s wake quickly after the carefully brave multipliers. regular excuses wake care\n951|Customer#000000951|PnC4Xlds,v|0|10-813-916-8297|7499.47|FURNITURE|ts. permanently special accounts mold quickly. requests boost slyly bo\n952|Customer#000000952|jg0YsHARdoULvVtP2vGHLVoAfWKFNz6QdTeAi|24|34-527-524-9172|3710.14|AUTOMOBILE|slyly regular ideas. even theodolite\n953|Customer#000000953|5HJQ8UzSSl1PJv28MpZqWvNwUm|22|32-829-961-2870|6292.06|BUILDING| according to the carefully bold dependencies use boldly slyly express deposits. car\n954|Customer#000000954|rr0Gz2iuYNuLgrIcLCdi5Zr2SnY8,wpzD9A|19|29-639-437-3775|5740.00|FURNITURE| haggle furiously. furiously ironic \n955|Customer#000000955|FIis0dJhR5DwVCLy|0|10-918-863-8880|138.31|AUTOMOBILE|ts cajole quickly according to the pending, unusual dolphins. special, ironic c\n956|Customer#000000956|aI12bsLSd1Y4dIx2Me5BLbGDCZPn |20|30-627-947-8311|1587.14|HOUSEHOLD|eans. regular, regular foxes wake furiously carefully even pinto beans. furiously r\n957|Customer#000000957|9F8p,XsLLxyiZ3b8NN|19|29-941-553-8245|9076.68|FURNITURE|al excuses boost to the ideas. unusual requests are across the slyly\n958|Customer#000000958|OrOUBBV7NlzVFXtuSOECmQFOkw8r|5|15-455-954-2914|1791.65|FURNITURE|s deposits. blithely even packages sleep carefully ironic deposits. quickly ironic accou\n959|Customer#000000959|O FdrkZxCx PK|4|14-546-329-6898|3266.14|FURNITURE|press accounts wake busily after the furiously final theodolites: fluffily final deposits above the carefully iro\n960|Customer#000000960|meekxaMlz5c1uE3wV7a,u h WcU,1OJz|4|14-664-604-8633|1932.59|HOUSEHOLD|s are carefully after the permanent foxes. fluffily \n961|Customer#000000961|5,81YDLFuRR47KKzv8GXdmi3zyP37PlPn|12|22-989-463-6089|6963.68|MACHINERY|e final requests: busily final accounts believe a\n962|Customer#000000962|lDp572JGdrL34kB YOQuC|4|14-792-232-1645|7557.00|BUILDING|. carefully brave foxes wake furiously final orbits. furiously pending theodolites along the bold the\n963|Customer#000000963|40EdWkddaWhQyiQ6FfUo8VOZwgb MetJ2jV,QPL|13|23-921-332-7635|2557.49|HOUSEHOLD|ns snooze slyly daringly pending instructions. regular requests above the regular, even requests sleep blithely fin\n964|Customer#000000964|ZnBNwMqvW7y3FSn6025pwkzgTDfsG2A|12|22-974-772-2802|4756.58|FURNITURE|al accounts haggle blithely! regular platelets haggle blit\n965|Customer#000000965|UigBc,9d1iLtQAVatnWACSDc9mNx0mYl|2|12-893-735-6415|4768.80|MACHINERY|lly enticing pinto beans haggle requests. evenly express grouch\n966|Customer#000000966|V9c8SR8WK7wEd|8|18-539-933-5176|1283.26|MACHINERY|ts. regular accounts cajole about the ideas. slyly unusual idea\n967|Customer#000000967|xKdAl6HSWvAmptzHgQHX3cMmxZDhfyrMqx|23|33-687-917-3598|5710.41|BUILDING|iously quickly silent ideas. blithely pending pinto beans except t\n968|Customer#000000968|eu 5FA1WHs9jq0pcdlVVA|0|10-470-740-2657|8921.97|BUILDING|ic foxes haggle slyly according to the dependencies. even, regular acc\n969|Customer#000000969|N9NSGc0Bj6FlSw3d9k GI7VAd1jW|8|18-148-790-2039|8601.63|BUILDING|lithely according to the fluffily silent patterns. furiously fin\n970|Customer#000000970|DXEgz7JHSFW401|14|24-266-486-1615|3623.60|MACHINERY|ronic requests sleep slyly at the slyl\n971|Customer#000000971|z29DUY Utsi6mWKI|1|11-256-718-6928|3914.88|AUTOMOBILE|ular theodolites haggle carefully: f\n972|Customer#000000972|ImKvHrrNc3rfWejksbCPyIQ|4|14-405-229-6174|4453.46|MACHINERY|deposits: slyly regular deposits among the furiously bold asymptotes are furiously along the slyly even \n973|Customer#000000973|FT4jTOdVCpmYW|0|10-749-928-5415|3229.18|FURNITURE|sly special requests integrate carefully along the special foxes. regular, silent\n974|Customer#000000974|7RcY6fOjTMbbOnVaFV,,6Dk5FIiHGrCpwXJNI|20|30-473-948-7149|7826.10|HOUSEHOLD|uickly. regular, ironic waters sleep blithely. blithely regular foxes are blithel\n975|Customer#000000975|qPFceGMB0xDjY6BhTGdIxe2Z F4MVuKIXHqQ1|16|26-428-220-2070|4364.06|FURNITURE|furiously express packages. even, bold sheaves haggle fluffily. slyly ironic accounts wake slyly across the quickl\n976|Customer#000000976|I78UJ2ks3sbcd0c2NQ7aH|19|29-436-660-4732|7772.85|BUILDING|special requests wake carefully regular somas. special theodolites wake regular, unusual pinto beans. furi\n977|Customer#000000977|JcKxPwHPM7akg5IiCs,ZVAfO73KE3|2|12-602-807-5055|311.00|FURNITURE|lly against the busily unusual requests! busily even requests haggle blit\n978|Customer#000000978|zpvQ6LYE89Inl40Yz,7NJ|24|34-261-243-2624|-50.51|BUILDING|ely unusual packages nag fluffily above the quickly regular requests. regular accounts run. blithely\n979|Customer#000000979|DvzUxD35ohKtUnalLGO9kDsCzZxtfcjO|18|28-113-574-4962|7055.13|MACHINERY|. accounts wake carefully special accounts. de\n980|Customer#000000980|UsrigSqZBnmbXhXNR6ibloq60qHBUj42kwX|1|11-572-281-8212|4586.33|AUTOMOBILE|st have to integrate above the regularly regular accounts. regular, final waters breach blithely \n981|Customer#000000981|pM4DXkl6Y,7S6a6jlJf8dZogp9QOdv|18|28-202-962-8429|3383.26|MACHINERY|ts doze quickly. platelets are quickly agains\n982|Customer#000000982|EN9aD5Xgh2q|23|33-460-986-9418|1437.55|FURNITURE|lithely express requests along the carefully pendi\n983|Customer#000000983|9jgCxRufEbwbGwW0PmG1RDIOwCUYlHs8z|7|17-283-610-6143|2902.95|BUILDING|mas cajole furiously across the fluffily special deposits. pending foxes sleep regular, silent packages. ironic pla\n984|Customer#000000984|fgAELFO9RS 6q9|11|21-247-588-5181|2811.97|AUTOMOBILE|t daringly against the even foxes. furiously silent forges sleep furiously busily ruthless requests. exp\n985|Customer#000000985|0uAMe1ICB,wts4STD4eLL|23|33-408-194-5161|2701.21|HOUSEHOLD|gular deposits among the thin instructions haggle since the furiously final packages. ideas use. regular i\n986|Customer#000000986|Cei2QidV0GC3OQWfJTNHLYPd|1|11-537-225-3800|178.73|BUILDING|cies impress blithely furiously final package\n987|Customer#000000987|SO 0UTuH26eduKI|8|18-887-394-2506|9850.64|AUTOMOBILE|eposits. even packages cajole boldly bold ideas. even, regular accounts haggle. packages among \n988|Customer#000000988|3BNYKEUyMbzfb40SEr 8OTb|3|13-862-722-3298|7746.97|FURNITURE|he courts. carefully silent courts impress. carefully close ideas run slyly pinto beans. even, ironic accounts are a\n989|Customer#000000989|pKuixWbH6XZFJY0uZjGi0oRaH1Xl|10|20-646-819-6827|5453.74|AUTOMOBILE| accounts. fluffily regular requests use carefully after the furiously special instructions! slyly\n990|Customer#000000990|uF idg4bq8Ij7ghxJ5KuTnU8w|0|10-403-137-1064|6988.49|BUILDING|dolites for the fluffily bold\n991|Customer#000000991|dK1Gzw1glT|21|31-977-971-6175|6533.53|AUTOMOBILE|n somas. slyly ironic instructions solve quickly at the final dolphins. requests cajole carefully\n992|Customer#000000992|Vbi1NGfPeKw,XU|5|15-262-535-3924|5027.75|MACHINERY| across the regular, pending requests. slyly ironic accounts wake furiously about the pending, regular \n993|Customer#000000993|56K JjC bMcgbXlJA4KI Icu uggsRoviMQm,F|7|17-494-757-5759|8421.87|FURNITURE| according to the slyly bold accounts. \n994|Customer#000000994|sZjdeW4LT9EKopmlv3M Xbnbe3gXQ9JkoxPv |16|26-638-159-5836|7461.27|HOUSEHOLD|ake furiously across the quickly idl\n995|Customer#000000995|5tCSAsm4qL5OvHdRZsiwSlVTdqPZws3f|13|23-272-700-1002|-341.79|BUILDING|wake slyly fluffily unusual requests. stealthily regular pinto beans are along the slyly final dugouts. slyly \n996|Customer#000000996|yjrSjcG z0Rm5PYrVMFTrU pFRMw|22|32-902-625-1946|6450.78|AUTOMOBILE|hely against the final, brave asymptotes. final ideas haggle slyly bold pinto beans. slyly unusu\n997|Customer#000000997|85KMCT2D2RIGayG99ozpk85ppHE6i9gJE|17|27-218-645-5219|367.03|FURNITURE|ven asymptotes. carefully regular packages are blithely. special requests according to the care\n998|Customer#000000998|fHRMFCGphazw9KvR1,EmNOUBG|17|27-951-935-6514|6679.20|MACHINERY|ular courts nag quickly unusual, sly pinto beans. special foxes thrash blithely up the foxes. \n999|Customer#000000999|r2SFEmfqrRu3M7ouE4zvI2ApOAtD|16|26-876-956-1302|403.89|BUILDING|riously special instructions ac\n1000|Customer#000001000|hzM1shTwWlLuk|22|32-730-275-2976|-881.70|BUILDING| closely against the slyly special deposits. regular, ironic p\n"
  },
  {
    "path": "src/test/regress/data/customer.2.data",
    "content": "6001|Customer#000006001|e,jhgkgWnN|3|13-247-356-9056|9139.66|FURNITURE|eans haggle blithely. requests hinder furiously alongside of the slyly final foxes. carefully even instructions wake\n6002|Customer#000006002|ZnHuM0Y9nONIKR5TFrHGuJnmxq9GLULVLitL8|20|30-330-985-9161|4400.25|HOUSEHOLD|final instructions. quickly regular epitaphs are according to the sl\n6003|Customer#000006003|TD5JS9ULaDBUHIy5J7FfT|16|26-402-596-3552|-30.04|MACHINERY|yly furiously regular instructions. requests sleep about the carefully special accounts.\n6004|Customer#000006004|Oo0mrAFH,KrRuF0eCxbklLZC|4|14-523-907-2485|-987.86|FURNITURE|the regular theodolites. carefully silent deposits hagg\n6005|Customer#000006005|Qhudh0ioaEafuJ Rfr6DAUqtKkZ33nby|0|10-930-477-2232|4911.63|MACHINERY|p fluffily fluffily pending notornis. requests ser\n6006|Customer#000006006|5VfR3EWqbrt0zdkyy8 |8|18-760-236-6029|3751.22|FURNITURE| instructions wake about the slyly ironic deposits! regular excuses wake. furiously pendin\n6007|Customer#000006007|eFTUQxFkePYCDkt2YiTlP1oUNww1lUPA|16|26-238-936-7519|3752.79|MACHINERY|quickly special pinto beans serve carefully. blithely ironic requests cajole \n6008|Customer#000006008|S1yv Nmjxkhb2yQU7sEX ,poB5f0ijkeRWsY|2|12-448-135-5947|8049.94|FURNITURE|uses boost carefully throughout the blithely express theodolites. furiously i\n6009|Customer#000006009|tJ3M102q9VHZ0jX|15|25-821-240-1667|4357.29|AUTOMOBILE|endencies. carefully silent attainments cajole according to the ironic multipliers. final dependencies integr\n6010|Customer#000006010|Oyw3CVbhnakZby zVlRd9jZPUygik65nK6UCg|23|33-702-784-8705|3887.84|HOUSEHOLD|uickly final pinto beans kindle instructions. slyly regular instructions sleep carefully. slyly ironic\n6011|Customer#000006011|wP2fviXCIC9E6kZ2StshJetAa9x0vNMBZ0I|5|15-600-545-9353|895.26|HOUSEHOLD|en requests wake against the fur\n6012|Customer#000006012|mSI9TkywG,fJgIV,mr24o,JupCD36mw0|14|24-197-638-5900|7639.44|FURNITURE|ourts against the slyly final ideas sleep quickly pint\n6013|Customer#000006013|NOTH5 kyVefl4bJA|20|30-137-646-6576|2051.61|MACHINERY|nal packages are quickly regular, even instructions. even, ironic deposits sleep; slyly \n6014|Customer#000006014|ZB9wS3fmWrQi8cZil6mzcKYo0PQLXrKw2ZM Rv5|24|34-570-696-6422|5302.25|BUILDING|y blithely express accounts. furiousl\n6015|Customer#000006015|AAc,PFYt20otDa85nT6wDwOVJ|21|31-376-506-1388|1950.33|FURNITURE|riously theodolites; fluffily enticing\n6016|Customer#000006016|Of93IVKgMknchAkEf16|22|32-695-578-4478|8103.98|BUILDING|requests cajole packages: quickly express accoun\n6017|Customer#000006017|013MNSbWeJML ohf|23|33-579-732-1955|-402.46|FURNITURE|iously. furiously bold theodolites cajole blithely regular \n6018|Customer#000006018|XNLYh8cfpNGERz1O|0|10-261-311-5038|9806.55|AUTOMOBILE|ng the blithely pending platelets detect s\n6019|Customer#000006019|5,lbJvlCV l8V|18|28-475-771-1730|3695.26|MACHINERY|carefully. furiously pending p\n6020|Customer#000006020|SIdmPkG3HQKTwU4p5hGrJttj,km|16|26-990-181-4165|9256.73|BUILDING|cial deposits x-ray after the blithely regular requests. final requests \n6021|Customer#000006021|r5k7syDoG41,Uhtpoii9Hp,oxJKKME9|21|31-358-419-1438|3002.73|AUTOMOBILE|ly pending accounts about the blithely ironic package\n6022|Customer#000006022|z9Gbi2AkLS0s6HNIDyerHNGE7V98G|10|20-331-281-6929|7892.25|BUILDING|tructions detect among the bold, regular accounts. ironic ideas inte\n6023|Customer#000006023|uz9mw3nZBoJ0j2zkMueLynANFVN|21|31-825-465-7964|5150.58|AUTOMOBILE| must have to sleep furiously at the pending, regular accounts. fluffily unusual deposits nag along the carefull\n6024|Customer#000006024|LZ80EMXjRz7JULb75V5n3qg|3|13-664-584-4042|4732.68|AUTOMOBILE|cajole blithely about the furiously pending theodolit\n6025|Customer#000006025|cawvuhdgRy KaqlhXcWab y31A37F8IPT|14|24-233-488-3262|-815.07|BUILDING| final packages boost slyly above the blithely regular requests. slyly ir\n6026|Customer#000006026|ius6eByivZ4BVYaESvB9p0bWsZ|12|22-822-409-2653|4993.97|AUTOMOBILE|xpress requests. carefully unusual accounts cajole blithely even ideas. carefully regular deposits are quickly iro\n6027|Customer#000006027|hST46enQLI8TzdOmvA8J|7|17-178-716-9690|1657.83|HOUSEHOLD|deposits sleep slyly regular deposits. furiously ironic accounts boost carefully according to the carefully regular \n6028|Customer#000006028|mL,IJFVI1MA9|0|10-424-273-2141|9173.51|MACHINERY|ckly ironic theodolites cajole after the quickly regular ide\n6029|Customer#000006029|Bhz6SV,wpp|2|12-230-673-1285|1247.91|FURNITURE|ites wake blithely final, unusual accounts? quietly final foxes hinder. furiously express theodoli\n6030|Customer#000006030|thigRH2AJJ5ay1akT2MoIWgGS3iH|0|10-385-880-2306|5585.25|BUILDING|uickly final deposits: even packages after the deposits wake regularly under the blithely ironic theodolites. furio\n6031|Customer#000006031|ovto GrnDjTqwxdA0L|24|34-257-441-6562|6268.52|BUILDING|oss the carefully even deposits. furiously ironic deposits haggle furiously furio\n6032|Customer#000006032|U9Y1LGOIyhU1r|24|34-124-269-1297|6098.48|AUTOMOBILE|its. pending excuses detect among the carefully ironic packages. carefully final gifts are. ironic in\n6033|Customer#000006033|CY9eVCYfqrI1WlamyV,2h2fAuwRof4Vi|20|30-194-447-2847|8203.11|HOUSEHOLD| regular deposits wake carefully final accounts. regular, thin \n6034|Customer#000006034|9C1trxxcizUyNm,rADOUt5UB|15|25-369-506-8757|4765.57|HOUSEHOLD|ding, even gifts about the regular, final requests sleep fluffily by the carefully silent dolphins. even, s\n6035|Customer#000006035|pzz5CQ3wWzqmawu811Zfm|2|12-929-596-2859|1907.14|FURNITURE|es sleep fluffily besides the furiously qui\n6036|Customer#000006036|oArQJruOuhJxRvLWjeDX3 h6CspeJwV7U6by|0|10-960-488-8572|8282.11|HOUSEHOLD| after the carefully even theodolites. furiously unusual pinto beans among the ironic theodolites k\n6037|Customer#000006037|SnqQMacIw1UlhWOZib7iEU|6|16-600-315-6012|1463.98|AUTOMOBILE|encies cajole carefully according to the final, pending packages. regular, ironic packages b\n6038|Customer#000006038| bIKiOVO1JVZiEtTF4T7HxjimXgL5|9|19-485-680-7672|7087.20|AUTOMOBILE|quests are furiously besides the blithely ironic requests. slow asymptotes cajole about the furiously \n6039|Customer#000006039|a5rSHRtBDeEFAd8f,suDA8ve|5|15-453-414-1942|4616.61|HOUSEHOLD| pending instructions must have to sleep carefully \n6040|Customer#000006040|al9P9X5hZsq4GVzXaf45nmYGre S|18|28-602-749-7354|6309.48|BUILDING|nstructions along the blithely final foxes solve according to t\n6041|Customer#000006041|BFIr9VI8QcoMd4mjXtXH|22|32-422-646-4800|1445.52|BUILDING|ests boost furiously blithely ironic pinto beans. daringly ironic pac\n6042|Customer#000006042|5HJbh0QShvTfZretlUSjRk|0|10-526-919-1776|4839.54|FURNITURE| express requests boost carefully. deposits boost blithely quickly ironic deposits. ironic accounts x\n6043|Customer#000006043|04Ln0amBet|9|19-335-750-5425|2143.97|AUTOMOBILE| asymptotes haggle above the final gifts. ironic accounts sleep. pending, regu\n6044|Customer#000006044|DOpmBtl1j5gVB36A57vtsI6Z|10|20-311-967-3855|6649.41|HOUSEHOLD|thely special packages. express pinto beans sleep about the final, unu\n6045|Customer#000006045|FH8AwQ54fTohmWWZEFluKjNP|0|10-579-571-4767|5587.35|HOUSEHOLD| instructions. special accounts hinder quickly unusual depend\n6046|Customer#000006046|3pqu0wwA NYnUQAx|1|11-901-156-9794|8566.39|FURNITURE|telets sleep around the accounts. busily special theodolites along the fluffily silent deposits use fu\n6047|Customer#000006047|WJIOtP1XJSsFYl7yCfPo2|4|14-601-562-8081|5439.08|FURNITURE|y express, ironic deposits. final deposits nag slyly slyly bold excuses; blithely regular accounts affix slyly. ir\n6048|Customer#000006048|DNtLkGdov3MnFfAGa40Pi6c6Y1r5j NF|24|34-996-904-3634|2634.46|AUTOMOBILE| sleep blithely across the regular deposits. accounts wake. slyly ironic gifts above th\n6049|Customer#000006049|b1Wlcr2IYgYvlZWW3V,JNkpDg,Msiwmm9|23|33-822-276-9791|357.21|AUTOMOBILE| instructions. instructions boost fu\n6050|Customer#000006050|orhXeGjD4RRH5QU|0|10-472-892-7595|6612.70|AUTOMOBILE|ts cajole quickly furiously express packages. evenly final theodolites use quickly \n6051|Customer#000006051|k7txuddD z|4|14-674-349-1580|4706.51|MACHINERY|platelets. slyly bold packages sleep permanently along the c\n6052|Customer#000006052|5R8 L5TJey,G7Ta7YGFnezUXaI4eIZu|21|31-792-184-4320|-624.49|FURNITURE|gular, final dolphins. sauternes against the slyly final dependencies cajol\n6053|Customer#000006053|Yzq6dtXjEfbY PEVyUkJ|15|25-363-543-1931|7102.91|MACHINERY|r, ironic foxes. packages wake\n6054|Customer#000006054|bXUQWTwB29ox1ganSx,QcxemZ|6|16-472-362-3917|4601.79|HOUSEHOLD|uriously after the silent deposits. blithely express instructions integrate carefully alongsi\n6055|Customer#000006055|FnwMVA8yGuU SgVGARNz22st,8|9|19-663-939-5531|-631.98|FURNITURE|g to the final, regular requests haggle furiously quick foxes. regular requests p\n6056|Customer#000006056|nyg076hIRk|2|12-687-122-1788|2218.75|AUTOMOBILE|se accounts. ironic pearls are\n6057|Customer#000006057|R4Bt0gWYUrIshQjPi7UaKDrFs|20|30-448-516-3117|9526.94|FURNITURE|ly regular ideas haggle furiously enticingly final packages. regular, bold dolphins cajole slyly\n6058|Customer#000006058|noUsJ1sGwBlm9KfvZ|21|31-818-798-9205|1401.74|MACHINERY|y ironic requests. silent deposits grow fluffily. regular, ironic pearls among the dolphins integrate even senti\n6059|Customer#000006059|hbYI6RCnsefLu,WWFcArgxqy5xQdygs8tv9|15|25-133-925-3453|6051.11|BUILDING|s detect slyly. even pinto beans across the quickly ironic forges use furiously slyly regular courts. furiou\n6060|Customer#000006060|G ziIQMNWzroTjGIHiWM 0pG|15|25-174-729-5653|4030.26|BUILDING|express, regular multipliers haggle after the fluffily final requests. regular excuses ar\n6061|Customer#000006061|FRN1 emJhjvn9yjnKN9HjoNG,X aW3kdDmBA|15|25-551-122-4107|7532.06|MACHINERY|during the blithely express requests. quickly ironic frays across the blithe\n6062|Customer#000006062|UtdDwozzDvfMtgj3W|3|13-756-700-4918|1370.35|FURNITURE|ourts-- regular packages hang furious\n6063|Customer#000006063|P8McFZy0XZk2tO0fd6e|13|23-185-189-2252|6385.75|BUILDING|, special foxes. regular asymptotes sleep slyly\n6064|Customer#000006064|,izgs4ldAZyamftqvB|20|30-205-718-3890|7335.40|AUTOMOBILE|theodolites. carefully pending packages sleep fluffily blithe\n6065|Customer#000006065|Gb72LtD9HVv9slwDCWiufPxBYW6qVgnfe|16|26-150-752-2624|7191.82|HOUSEHOLD| alongside of the furiously unusual instr\n6066|Customer#000006066|s,Txrg7qYwMSykuMvxhA26sOs1KNm8t|6|16-166-390-9922|2967.12|BUILDING|arefully blithely regular courts. furiously express request\n6067|Customer#000006067|wktXPx5LSL5ZSP0shdmYDauSiNMRiAHaaKVJ|4|14-386-119-4898|3407.75|AUTOMOBILE|inal dependencies nag blithely car\n6068|Customer#000006068|RCTOrn8 qmIVd6qGMMLmjWCvz7|4|14-138-821-9164|6028.37|FURNITURE|arefully even instructions sleep slyly regular, silent pinto b\n6069|Customer#000006069|lP56RbvJxUsPWR7AJ3nGdcXjPYhDzw|8|18-520-115-6373|6775.83|FURNITURE|le furiously carefully brave ideas. f\n6070|Customer#000006070|0Pycomq4a6KgwcZYyaH5g6t5hdLgsKF|14|24-195-934-6766|7430.40|AUTOMOBILE|even asymptotes wake about the furiously express accounts. special platelets between\n6071|Customer#000006071|bSImC9SNAZBpJfS|3|13-976-555-2947|8764.04|AUTOMOBILE|after the dependencies eat carefully against the blithely regular pinto beans.\n6072|Customer#000006072|dJTs qvHOtIuFDNIsyGkzkuz|16|26-754-599-4979|5312.01|MACHINERY|theodolites nag evenly among the bold excuses. ironic accounts sleep along\n6073|Customer#000006073|pfb,24MPwsE8,0LUyO|9|19-689-677-5011|1045.91|HOUSEHOLD|. furiously regular ideas are regularly ironic theodolites. slyly even packages sleep slyly fluffily express p\n6074|Customer#000006074|nrcHwNPzOQ x|8|18-837-412-1792|9486.43|AUTOMOBILE|olve furiously about the furiously bold deposits. slyl\n6075|Customer#000006075|DOsAAUjRKdmOtvgEMibSALCKR M5Nj|1|11-593-566-6284|2455.27|FURNITURE|s the furiously ironic requests a\n6076|Customer#000006076|At1D9HHVnICLHSzLkebTdUFubbpiizOsfsnT1|7|17-850-828-6128|2626.39|FURNITURE| pending deposits solve. slowly bold ideas haggle fluffily packages. blithely\n6077|Customer#000006077|uhQEW4hX BiYzeK vM4p5nmxPwa|9|19-473-215-4783|7755.29|MACHINERY|e the pinto beans. final somas affix slyly: fluffily special instructions are careful\n6078|Customer#000006078|SBe5gejYZc lZ|9|19-127-604-4037|2143.99|MACHINERY|o beans. quickly ironic accounts\n6079|Customer#000006079|JrsyZ3aOo7pyfy1Nfcu7wv2y9MssX9Fl2j|24|34-956-792-4754|7035.43|AUTOMOBILE|pinto beans cajole carefully special pinto beans. bold packages was across the carefully ironic ideas. even, regular\n6080|Customer#000006080|TO8wL2kHE7DAZOlyh7U4aW|2|12-853-465-2990|3557.21|FURNITURE|as sleep carefully along the ironic deposits. furiously dogged deposits among the fluffily regular frets sleep blit\n6081|Customer#000006081|g9sWPKJZrAa2yRmoTwz|14|24-898-630-6492|-956.69|MACHINERY| courts affix around the furiously stealthy deposits. packages wake evenly\n6082|Customer#000006082|A VQviGoD71daDZOZv|11|21-155-663-3196|898.19|BUILDING|usual pinto beans after the carefully pending fox\n6083|Customer#000006083|Zk4jDSMrlCCH,MIpgre hsHn8XZJn62|11|21-852-430-5106|1845.68|AUTOMOBILE|ial accounts wake slyly. furiously unusual depths cajole blithely furiously \n6084|Customer#000006084|E0NXucb5MGwfV5BnCW1qPSpeMoqf|15|25-403-768-3899|9300.11|AUTOMOBILE|s are carefully. doggedly pending foxes boost carefully pending, ironic accounts. closely bold\n6085|Customer#000006085|N280OwVf0BbPajbB89YmFrHEihif|12|22-247-826-9697|6041.35|MACHINERY|slyly permanent courts was blithely packages. furiously b\n6086|Customer#000006086|kBBqMZHke10dGuoDBvzv|20|30-613-894-3303|5029.81|MACHINERY|ays? dugouts sleep carefully around the slyly regular attainments. iron\n6087|Customer#000006087|HZvlNvQ41HBORsewqAXWc1i5z,V|17|27-176-985-1293|9991.42|FURNITURE|fluffily. furiously final deposits believe about the furiously final excuses. slyly ironic accoun\n6088|Customer#000006088|yxbYU1ogtMNgiSUAFNsA5 aYG|24|34-944-696-5888|8895.30|MACHINERY|thely. packages at the final packages use carefully around the regular, even deposits.\n6089|Customer#000006089|1xUlqn0cOHlIKlRHeBz8Mfn|18|28-312-896-5088|458.27|HOUSEHOLD|es. accounts cajole slyly ironic c\n6090|Customer#000006090|NqM,WhTKaDb4uzuNsmHPiSvWFsEGHJA|8|18-417-809-3338|61.15|HOUSEHOLD| special sheaves. quickly final requests are. quickly fina\n6091|Customer#000006091|xqRatn5FncOIxHo9oZ9lBbPrJQ0e aoISrI|1|11-922-360-2419|2753.48|MACHINERY|quests. furiously ironic requests are above the carefully special packages. \n6092|Customer#000006092|Z,XTnww1rI6aWDjmfk7w0p8kxLcqCIfD7h8|5|15-941-440-2662|9761.72|FURNITURE| foxes are above the ironic excuses. special instructions dazzle blithely about the regular asymptotes. fur\n6093|Customer#000006093|BOuFVeQZ,E|8|18-806-815-4057|9343.42|AUTOMOBILE|gedly regular pinto beans. express pinto beans nag slyly. regular ideas wake thi\n6094|Customer#000006094|X7kFGROKaQpglhpv20A|12|22-421-694-6690|7983.44|FURNITURE|to beans boost. carefully special pinto beans wake alongside of the slyly regular accounts. furiously regular pinto\n6095|Customer#000006095|qP9OXihxntW60ybk|17|27-151-511-8515|8802.47|BUILDING|thely. furiously ironic request\n6096|Customer#000006096|wy UjJzsFOKGCMfqtaHK5NI|1|11-517-930-3964|2237.40|HOUSEHOLD|silent excuses detect slyly silent accounts. slyly final instructions nag furiously. fur\n6097|Customer#000006097|KCW2zgHZRm36j90QVmQ9b5Ml|2|12-742-944-8759|3963.90|MACHINERY|ding to the carefully silent accounts. special excuses wake. furiously final foxes about t\n6098|Customer#000006098|c Bp7dnCuklVVNOM7Gc11k2gCRv|3|13-223-820-6932|3312.28|HOUSEHOLD|ts! carefully final foxes haggle slyly: slyly express tithes wake furiously about the final, final deposits.\n6099|Customer#000006099|Zw,1lluCeZSlbwLKZo2i37|23|33-797-420-9100|7413.61|HOUSEHOLD| deposits are slyly pinto beans. carefully express packages wake furiously. even deposits\n6100|Customer#000006100|BFnRAdDK0EMZAr2zS3js6,Jsh|14|24-425-432-3048|1569.09|AUTOMOBILE|olphins after the requests wake furiously silent requests. quickly ironic dependencies are furiously around the reg\n6101|Customer#000006101|KI6M7iuiqLHAb|1|11-152-108-8875|2651.06|AUTOMOBILE|efully even requests. ironic, unusual deposits wake furiously. blithely pending req\n6102|Customer#000006102|s5ViRaDEjv2nKox7c6Y|3|13-533-564-6439|4257.41|HOUSEHOLD|cial asymptotes are slyly regular sauternes-- slyly ironic deposits integrate regular, final theodolites-- furi\n6103|Customer#000006103|YYS9 3AI8tkKqUqMtWssXA44M|4|14-405-808-5807|8364.07|HOUSEHOLD| pinto beans sleep. carefully regular sentiments sleep blithely. final packages cajole \n6104|Customer#000006104|jcKuAbr5WFuVxd2xt,4KqyvQ7kz|15|25-250-924-1873|4010.86|MACHINERY| final packages sleep above the slyly bold\n6105|Customer#000006105|EfQPHmIraBqSvNY15deoIPsc|4|14-506-543-9059|506.68|BUILDING|he unusual deposits. furiously even a\n6106|Customer#000006106|nfroWgkspby66r6BkrvQCMX,f,i4Myrykv3k pa|7|17-151-647-7940|-337.05|FURNITURE| special excuses cajole. carefully bold req\n6107|Customer#000006107|,4I3WYJQN9ZAWX5I5J54dJna|19|29-717-849-8757|9064.29|FURNITURE|long the excuses. slyly special instructions unwind slyly beyond the regular, final pinto beans. requests use blit\n6108|Customer#000006108|0SPg7CHlEjunN0dHuKF932w|10|20-564-384-5926|2570.78|BUILDING|asymptotes sleep carefully. fluffily regular pinto \n6109|Customer#000006109|gpgBDs5krmN|7|17-896-737-8894|8151.07|HOUSEHOLD|uriously pending asymptotes. fluffily bold pinto beans are above the\n6110|Customer#000006110|FAxMqfHBG36oB|8|18-793-720-8574|-888.62|BUILDING|iously final ideas. furiously regular packages nag. quickly regular pinto beans above the regular theo\n6111|Customer#000006111|CLSQ2jkX5hmEPyKFJkKxemAH|18|28-252-831-5956|857.85|HOUSEHOLD|ly final deposits. slyly special accounts against the furious\n6112|Customer#000006112|wPzFnLHJ Eg3EzH |24|34-654-913-4569|5894.96|HOUSEHOLD| final, dogged packages wake furiously slyly regular platelets. fluffily silent pinto bea\n6113|Customer#000006113|YV9LsSwnYEjuQENdPBp8Sfl|18|28-599-409-3070|8646.86|MACHINERY| blithely at the blithely ironic deposits. carefully final ideas cajole fluffily. unusual pa\n6114|Customer#000006114|87W0S9L9zjjL,lPnNxpXEJKSjaOY|12|22-957-665-4649|4860.40|HOUSEHOLD|haggle slyly above the slyly express deposits. slyly even ideas sleep fluffily above the slyly ironic accoun\n6115|Customer#000006115|r3MSiTaTNNKpIXe1x,RDRqPx1s|21|31-465-527-1774|821.94|MACHINERY|er the slyly express platelets affix carefully above the careful requests. pending\n6116|Customer#000006116|Q1HHnkKguo6sSYNWgNEk2tTeEe6db3s6osO|4|14-743-932-4071|7234.12|BUILDING|he blithely express requests haggle furiously regular, final excuses; carefully regular pinto beans unwind ca\n6117|Customer#000006117|4CHkWzZ3fT|24|34-380-887-8865|9826.19|FURNITURE|ly final packages. fluffily bold escapades sleep fluffily after the carefully express\n6118|Customer#000006118|GZJ6Dctbr0USrtx|11|21-581-376-6339|3810.27|HOUSEHOLD|e fluffily special packages integrate quickly final dependencies. regular \n6119|Customer#000006119|p8I8iRWN3HIQZPoMD5y1qogRuycR7VnAiEyOZff9|1|11-137-505-5251|6246.11|FURNITURE|wake blithely fluffily ironic theodolites. bold deposits cajole about the blithely fin\n6120|Customer#000006120|ZTjNH4Si5g9pTrul2fRGaHEB|6|16-141-186-1813|3351.67|HOUSEHOLD| excuses nag fluffily ironic requests. slyly \n6121|Customer#000006121|uAXqrguis17T1SGF9Od0sJ|13|23-689-704-1607|9531.41|FURNITURE|ss packages are furiously final depths-- final, ev\n6122|Customer#000006122|48JzmrY5BZiS2C5Ts,wgJkRR7z5SOkJhg7|18|28-417-497-5317|-485.36|BUILDING|yly express accounts. slyly regular f\n6123|Customer#000006123|rriyD1ssl4dg,ur1WRPBG|19|29-912-115-6013|2397.49|BUILDING|ogged deposits breach blithely. blithely express theodolites use. packages about the fina\n6124|Customer#000006124|1FAv28GTpQRD2Nw ULiQG3qCi6PTGFXC|7|17-962-756-6916|-383.24|MACHINERY|final courts. furiously ironic deposits run blithely furiously express instructions. slyly special deposits slee\n6125|Customer#000006125|q4dT2taZKQmIDI8,V|11|21-143-295-8049|2811.99|MACHINERY| ideas. slyly even packages are fluffily instead of t\n6126|Customer#000006126|7NRolRPvqN3QIrcKU|23|33-988-127-6540|3047.89|BUILDING|hely. quickly even braids haggle slyly. quickly even theodolites \n6127|Customer#000006127|4xg9rQlpgsZ7NJl4k9IL47ODv3E26jIRCvnlgd|23|33-236-693-8622|7908.12|BUILDING|le carefully. even requests grow carefully carefully bold theodolites. furiously regul\n6128|Customer#000006128|c88JZp1so3TfKB6o5|16|26-586-949-7031|7899.60|FURNITURE|efully enticing braids according to the furiously ironic accounts detect furiously around the fluffi\n6129|Customer#000006129|MVydnT2OcBdbSLF|22|32-368-121-9588|7571.87|HOUSEHOLD|ously after the regularly pending excuses. accounts boost slyly final ideas. special account\n6130|Customer#000006130|uzgA6cyeuysYLVKMv|6|16-405-646-2657|-555.94|BUILDING|efully regular deposits about the regular packages believe blithely alongside of the ideas. bl\n6131|Customer#000006131|EwoT1zKxHYj8|13|23-237-602-7871|4223.57|BUILDING|e doggedly permanent platelets slee\n6132|Customer#000006132|LMFK0bKFaBIX8tW74Yoxb8,bw9XS|1|11-890-884-8734|468.90|HOUSEHOLD|ly close courts among the express, even packages believe doggedly among the fluffily regular ins\n6133|Customer#000006133|FwwrG68tR4k|13|23-849-670-9143|2698.12|HOUSEHOLD|final ideas detect furiously permanent accounts. slyly regular courts boost final instructions. theodolite\n6134|Customer#000006134| DMIOsEg5VDDKmWzjjShHILJhBrFVea|8|18-349-804-2162|3214.77|FURNITURE|kages sleep. furiously thin requests affix ac\n6135|Customer#000006135|IED4rGsufuEnT4NrO7KvhQfFfcwGzvMHgQiUnoP|1|11-117-608-4110|7552.48|MACHINERY| deposits. deposits along the final packages haggle furiously ironi\n6136|Customer#000006136|pDoECZ7k3AwOSp9wDO|19|29-974-254-4381|3649.73|BUILDING|ely ironic tithes sleep finally after the \n6137|Customer#000006137|Uv1p49Ppo7lO9zpW05z|5|15-958-508-2113|3037.82|BUILDING|riously express, regular hockey players. quickly final pinto beans use packages. carefully e\n6138|Customer#000006138|Dm6,p6hst9ub9FiYivgUm3FapKRiEYPG|15|25-872-607-4602|3638.97|FURNITURE|snooze slyly quickly ironic packages. furiously final deposits sleep quickly specia\n6139|Customer#000006139|3r3Uc,7NMqKxby7w641xEV3su|17|27-704-480-4139|9119.98|AUTOMOBILE|s cajole blithely regular packages. furiously ironic deposits are final, regular fo\n6140|Customer#000006140|QPlaw,8zEWbcabsVf,wRBMC1hvedP0|11|21-263-388-7344|3593.93|AUTOMOBILE|ke furiously slyly bold warhors\n6141|Customer#000006141|4QBCP6mT5dep|10|20-961-227-7319|247.76|BUILDING| alongside of the patterns. pending ideas doze \n6142|Customer#000006142|6rJlz 6aiuufZqN8Ldf9|7|17-856-660-5846|4045.08|BUILDING|the carefully bold deposits-- requests haggle slyly \n6143|Customer#000006143|zuh77l1EZ6xUn6H,sZIYq|1|11-253-915-7383|3706.32|FURNITURE| nag slyly. final, pending dependencies haggle slyly about the furiously ironic o\n6144|Customer#000006144|zPKzjSPczgTN9Yb3ybnDmhplv6ymvG|1|11-306-913-9602|8057.08|FURNITURE|equests cajole carefully pending, even packages. regular platelets\n6145|Customer#000006145|CybtvIEF1DXOFCddFGr|21|31-879-282-9414|3423.05|HOUSEHOLD|sits are slyly even requests. regular packages across t\n6146|Customer#000006146|RNKo8PgfKqgW6hGWTJMp|19|29-723-257-4271|-832.84|FURNITURE|ld requests alongside of the regular packages wake fluff\n6147|Customer#000006147|fr6iYAVpls5Zy8UF1qn|21|31-729-268-4470|3418.83|FURNITURE|accounts. silent, ironic orbits believe furiously escapades. slyly regular packag\n6148|Customer#000006148|OiRtO4N,szTYNMjqyl,|14|24-345-723-7631|5246.25|AUTOMOBILE|s poach carefully after the furiously ironic packages. even pinto beans are fluffily. blithely unusual pinto beans\n6149|Customer#000006149|Dv0FCsfLC8tg I,NGqcnL3uO|9|19-981-118-5032|6737.15|BUILDING|mong the blithely final requests sleep according to the b\n6150|Customer#000006150|aX47rrqgyvzy74tFd0o Wn1wr5XK|2|12-895-769-4268|6249.24|BUILDING|o beans cajole doggedly above the slyly unusual deposits. enticingly regular theodolites snooze c\n6151|Customer#000006151|ojCmk5FfGZm4Oset4R|1|11-178-162-9290|1016.84|BUILDING|inos should have to use furiously among the quickly express somas. ironic requests cajole furiously after the slyly\n6152|Customer#000006152|Bqxwa70bCy,M8Vs6cWn|19|29-492-966-5351|4957.70|MACHINERY|y final foxes. express packages wake across the ruthlessly regular instructions. express \n6153|Customer#000006153|,oEYlRJs0L76hU5MtnT17H5qULk|6|16-303-738-6232|8070.72|MACHINERY|ctions use slyly above the blithely express deposits. \n6154|Customer#000006154|FI1U42PTLrAG n3a5JxzHabcH|10|20-372-167-3439|1571.98|AUTOMOBILE|tithes nag regular, final theodolites. deposits sleep along the ironic theodolites. carefully \n6155|Customer#000006155|03gJRZx3TGwScCd8d1 MhitTEHY i|20|30-511-190-6866|7246.91|HOUSEHOLD|ess accounts. carefully even theodolites are silent requests. slyly unusual packages \n6156|Customer#000006156|cVhwUHk5,b74V|12|22-119-245-3413|4873.83|HOUSEHOLD|ly daring, express deposits. special, regular packages haggle furiously ironic package\n6157|Customer#000006157|3hTpojm17PUW3OvoLxeqQtz2|11|21-578-529-7649|6444.10|HOUSEHOLD|are slyly sometimes final deposits. special, final dependencies cajole quickly. slyly e\n6158|Customer#000006158|GmqBG39QiPPqq1YA|10|20-393-733-8825|8966.55|FURNITURE| even asymptotes. fluffily pending requests against the quickly silent platelets cajo\n6159|Customer#000006159|naaCoV9ztjZ2YVP4hmQAtTnFsb,DAWFoAJp5|6|16-192-111-5212|9877.27|FURNITURE|g the silent, ironic theodolite\n6160|Customer#000006160|NrxXWav6LihuuSlWGszrc|13|23-830-408-5650|4333.77|FURNITURE|pending orbits sleep quickly. slyly unusual requests cajole. furiously regular requests \n6161|Customer#000006161|pbRxOFTDqY0lPBucGOSzCy|18|28-703-556-9515|4706.39|FURNITURE|ing deposits? furiously regular accounts alongside of the fluffily even requests are blithely above the\n6162|Customer#000006162|wFK59S80D3m iXYc96LznPltGTpDcB|3|13-525-195-7035|8277.40|HOUSEHOLD|ackages. requests use blithely according to the final, regular requests. even, special accounts\n6163|Customer#000006163|tmxBpVae0jKAe9vXikmLaCRGPAoyiDVGz|21|31-674-263-4416|-678.87|AUTOMOBILE|requests sleep. quickly final depths affix quickly unusual excuses-- final packages af\n6164|Customer#000006164|PSgfAO1LnbP8 5aNt2lWBD|14|24-782-729-4216|6171.41|FURNITURE| ironic courts solve closely according to the carefully ironic requests; special,\n6165|Customer#000006165|31aZoMFnTFf|12|22-414-194-1058|1982.00|AUTOMOBILE| the even, unusual excuses. slyly special foxes nag quickly special, expres\n6166|Customer#000006166|df2AQz9BJgmAj XoIOiyoUd|14|24-891-533-8945|1444.98|HOUSEHOLD|ng packages affix even, final pi\n6167|Customer#000006167|oPGRaBt5IuSxtQhwgztJyv1miL|7|17-798-361-3738|6729.32|FURNITURE|ly final requests grow alongside of t\n6168|Customer#000006168|TfeEEZ9Ds8x3eQljQUYaNzpu6tE1ap,JGRn2gZm|24|34-877-467-2443|6633.98|MACHINERY|ily. special platelets about the slyly \n6169|Customer#000006169|UDeLABnhksFK7tfInLsDPRtynoWxesfiJ|7|17-656-209-2093|-681.05|HOUSEHOLD|y final pinto beans. slyly even dependencies boost\n6170|Customer#000006170|PLJi9aPgRlkkIUsc8LGRVLOGrz1IBd|19|29-558-582-4733|2570.49|BUILDING|t requests cajole slyly. slyly ironic courts ca\n6171|Customer#000006171|SNyHcZsmA5EydGjLu0MT7Y|19|29-222-112-6966|3125.83|MACHINERY|ts nag fluffily even instructions. furiously even pint\n6172|Customer#000006172|uafeB6k3L MWgR6k8Wokrv0gtSwTLmWRW8,UVhv|5|15-750-174-4232|8749.91|BUILDING|ound the slyly regular theodolites wake slyly across the platelets. carefu\n6173|Customer#000006173|jd3so7 leJO5Y0SF1YFrMJ|20|30-743-439-3998|9725.31|MACHINERY|es boost above the carefully regular\n6174|Customer#000006174|UOkeiQv5WK1OBw5CjtHQDh84JAV|11|21-570-543-7869|2314.46|FURNITURE|ly bold foxes-- final ideas x-ray among the regular theodo\n6175|Customer#000006175|4fRoxmxFa4n|23|33-387-822-4617|1065.86|MACHINERY|ress pinto beans. fluffily final ideas dazzle closely slyly ironic packages. furiousl\n6176|Customer#000006176|kXM67uOjA5sj|24|34-919-259-2224|3918.54|FURNITURE|nts wake after the even packages. ironic requests alongside of the slyly regular excuses boost slyly regular reque\n6177|Customer#000006177|pZZ8D,yKhfsFatXwk|24|34-161-190-1931|-149.78|MACHINERY|are across the slyly ironic requests; carefully dogged accounts run blithely bli\n6178|Customer#000006178|ejgXNsz sI0Dl3F,FVziTAF4mPWXczkmXlu|9|19-567-173-3123|-369.66|FURNITURE| furiously alongside of the pending, ironic theodolites. ideas among the silent pinto beans dazzle silently alongsi\n6179|Customer#000006179|0MU9 AVKw SeY6kbL5VJm|6|16-699-827-5744|38.38|AUTOMOBILE|l accounts lose furiously slyly final tithes. quickly stealthy attainm\n6180|Customer#000006180|4csqeJ8yw1y r 6Bzi49uv|11|21-527-929-7958|4979.33|AUTOMOBILE|ymptotes solve beside the ironic packages. carefully special deposits ar\n6181|Customer#000006181|cZ9B3p5D4poouVdTvh0Sol7ODKuWa|2|12-716-858-1804|8630.17|MACHINERY|es. unusual instructions among the slyly regular deposits affix carefully after the furiously ironic platelets. \n6182|Customer#000006182|8U367QGaD8IUUdHyHtwSj3pmJoeLcVrMccGMZ5J|6|16-291-418-8009|-745.50|AUTOMOBILE|es serve blithely alongside of the regular instructions: unusual deposits cajole ruthlessly alo\n6183|Customer#000006183|iDx,aeynoLw|20|30-808-423-5478|8330.40|MACHINERY|gular, express accounts. requests use. furiously pending instructions detect within the quickly\n6184|Customer#000006184|dkynJLYBPBCkx 1paCFtwxmixcoPoqaFVyGQ|19|29-373-987-6278|-960.96|MACHINERY|hely blithely special deposits. furiously pending requests boost carefully instructions. excuses nag \n6185|Customer#000006185|4qtODzt Kxhkagjgtc5U 6l|24|34-493-651-6114|6109.32|HOUSEHOLD|ests are final ideas-- ironic, pe\n6186|Customer#000006186|0dwkPKvOkPIniv3,Fahd1rq9nwc|16|26-982-329-6333|3360.22|AUTOMOBILE|sly bold grouches. ironic pinto beans sleep furiously furiously regular depths. platelets breach t\n6187|Customer#000006187|LJJpyf,OZivsv6IQBJG3gEisgc7d QC7oKRuXOBj|15|25-545-311-4634|-838.33|AUTOMOBILE| requests. slyly regular requests after the accounts hagg\n6188|Customer#000006188|Z85HZ6fRUEl3|15|25-648-100-5980|-895.88|AUTOMOBILE|gle carefully. bold theodolites x-ray fluffily. bold gifts alongside of the sent\n6189|Customer#000006189|X6edGVb,Osa emoLHSaQKn|14|24-556-862-5258|-213.89|AUTOMOBILE|ly regular deposits across the regular theodolites serve blithely express \n6190|Customer#000006190|mV9CzSEQr,nE3CC, xJ1EsQOw|2|12-212-128-8305|33.63|MACHINERY| finally regular theodolites wa\n6191|Customer#000006191|7XGJ0ugPk dVdCm1nJQ|22|32-229-609-5050|2167.56|BUILDING|s dugouts. carefully bold pinto beans \n6192|Customer#000006192|oI8CXoK1w9PnZDUEbvj|0|10-276-595-2077|7179.63|BUILDING|ly bold decoys are slyly. slyly even epitaphs around the regular requests cajole at the blit\n6193|Customer#000006193|RnHj1jACEqFgLpCQfzgsZtgoZu1Jck|16|26-200-646-6714|7033.00|MACHINERY|dependencies. furiously special foxes cajole\n6194|Customer#000006194|9GsTKrC4NgB2bqd4ui9kuijhjxDlw IU1|9|19-886-790-6122|6639.12|AUTOMOBILE|ts boost after the carefully ex\n6195|Customer#000006195|eVLaPYm6NRhqQzuMx3vk|0|10-891-840-4980|8707.05|FURNITURE|y. quickly pending accounts against the blithely thin ins\n6196|Customer#000006196|KNWdwsj7hGyO0lrvqr6G1o|15|25-200-325-2383|4462.11|BUILDING|c courts. final instructions a\n6197|Customer#000006197|Ce9LTBhp7GkCqZy|11|21-395-292-5975|5631.85|FURNITURE| unusual instructions wake always above the regular excuses\n6198|Customer#000006198|OglD6pbHC9ovv2mfZ rDO iay cVjX5SRng2|23|33-381-544-1422|8515.39|FURNITURE|y unusual platelets are slyly across the carefully final pack\n6199|Customer#000006199|3LH72AxUTOqvuazpB6dk5i80YVo3,H2YZiyir|22|32-514-462-3884|6178.78|MACHINERY|ideas wake fluffily. pending, pending p\n6200|Customer#000006200|oGCR8cSGI,rHCpRMi2|3|13-609-502-2266|4767.80|FURNITURE|l dolphins. sometimes unusual instructi\n6201|Customer#000006201|oK3Q7pkcEZaGXxOeNB4okAaAxNDbZB8K1y|0|10-385-198-4441|6397.29|HOUSEHOLD|ly. regular, slow packages along the carefully express foxes doze to the blithely even foxes-- furiou\n6202|Customer#000006202|E73qW mbEQINh gPymXB,ed4O  nKl|15|25-477-944-7482|3330.24|FURNITURE|l excuses. fluffily ironic packages use. quickly final Tiresias a\n6203|Customer#000006203|xVMuglbV53zSLL4wb7Mxb,pkD8MrP6 R,CO ehMc|19|29-735-432-9939|1280.15|BUILDING| ideas atop the even, unusual excuses sleep against the furiously careful pearls. closely express\n6204|Customer#000006204|6MBV7BG qK9BOmGQny|16|26-983-171-4809|8460.54|FURNITURE|eful requests. quickly ironic instructions use b\n6205|Customer#000006205|Bd2A0KLWCYN1WZ1XJc,N |18|28-920-423-1966|8416.88|FURNITURE|osits over the pending instructions wa\n6206|Customer#000006206|EZ6yws0GmtHJXSHiV|4|14-448-256-3507|486.36|HOUSEHOLD|nding platelets are after the pending foxes. unusual, regula\n6207|Customer#000006207|Ba2gZAYGD 74QLT8T7,uRQwIi0rqbJ9|7|17-896-188-3890|-740.28|FURNITURE|foxes. blithely pending platelets haggle quickly bold, silent asymptotes\n6208|Customer#000006208|m,PxS2pByk43RfabxsV6in7n|18|28-322-291-1770|8045.41|FURNITURE|deposits wake slyly. slyly even ideas use blithely. furiously iro\n6209|Customer#000006209|C1Ls6INP7D9jHTugpjUUWD9kS9cpKclB|2|12-685-144-7597|5241.88|BUILDING|ges. final accounts breach slyly. furiously regular deposits according to the express instructions wake after \n6210|Customer#000006210|d5fRx4ruNET9kj6LqGhVxCYwT|17|27-245-729-5781|-791.51|AUTOMOBILE|special pinto beans poach quickly careful theodolites. fluffily pending instructions cajo\n6211|Customer#000006211|,bXG5MlIamKtG8mMbce|2|12-200-723-4029|5045.01|BUILDING| furiously special requests. carefully final deposits wake even requests. carefully final accounts are alongside o\n6212|Customer#000006212|ArifsT45MN,N2HR,CoiDLwG0|5|15-251-873-7969|1570.87|AUTOMOBILE|n foxes. ironic, final foxes boost blithely bold instructions. furiously dogged deposi\n6213|Customer#000006213|jpXKO9LktOMMIPpfE xyGA7uurVPqSzOn|5|15-600-409-7048|-445.21|AUTOMOBILE|nts use carefully above the b\n6214|Customer#000006214| ,MAKZxDGF3QKwDERiLKFCaCOcIOlN|22|32-811-917-7230|6202.90|HOUSEHOLD|. carefully express theodolites sleep furiously even requests. quickly express instructions lose slyly silent idea\n6215|Customer#000006215|fAfdqCTURbOu,|5|15-792-734-1509|3206.02|BUILDING|deposits. furiously even hockey players sleep carefully. quickly bold requests across the slyly spe\n6216|Customer#000006216|bGTXGAHg72BEDM09QZEFI|0|10-585-359-5566|3548.03|MACHINERY|ickly. slyly final theodolites integrate fluffily quickly ironic asymptotes. \n6217|Customer#000006217|k9NiqQlFJVv6|9|19-612-407-3150|9647.49|MACHINERY|tions wake quickly slyly regular pinto beans. ironic, final asymptotes affix blithely around the hockey players. \n6218|Customer#000006218|4,z5xJL2IWWO5LMrF36cZISGaq77Q6 7DvAh5|16|26-501-652-8685|3504.23|HOUSEHOLD|slyly final instructions. final e\n6219|Customer#000006219|lM 6tdVkyERY,wQ6n7ZHD11,G|8|18-384-857-8254|-443.88|FURNITURE|lyly regular theodolites doze furiously bold ac\n6220|Customer#000006220|je,Ssek0XNsaWRGsiKF 0,hDjGVOaSNsN7TkZz|7|17-755-898-9664|6694.27|BUILDING|ffily express deposits. carefully ironic packages haggle furiously final excuses. pending dolphins af\n6221|Customer#000006221|J2Zx,5YWGMpbyd9yupa1PMuDhhHCFtDqmtzx0CE|6|16-641-636-3853|712.36|HOUSEHOLD| the furiously even braids. instructions use blithely\n6222|Customer#000006222|8p2FbclgqcvoFR29P,OwwCkWR|8|18-981-975-1436|2589.32|FURNITURE|usual pinto beans. slyly special deposits breach about the slyly i\n6223|Customer#000006223|kOimz7buzOsZP8DPRQfB pa8a7bWyA0Axx|7|17-805-445-2530|644.72|BUILDING|each slyly quickly even requests. quickly regular instructions cajole blithely. deposits wake along \n6224|Customer#000006224|LFoSAlF,JOEn4gVU0qGhIpu|5|15-952-723-9945|9794.49|FURNITURE|ly blithe dependencies use across the slow requests.\n6225|Customer#000006225|cqOhtNVujJSTrsZlLLvt1k|21|31-221-435-4954|-847.96|BUILDING|; slyly unusual deposits cajole pinto beans. fluffily regular accounts above the accounts lose slyly \n6226|Customer#000006226|8gPu8,NPGkfyQQ0hcIYUGPIBWc,ybP5g,|23|33-657-701-3391|2230.09|BUILDING|ending platelets along the express deposits cajole carefully final \n6227|Customer#000006227|hQpDSUJLnjcvDZ4WbiVrWDSYBjCou1kJ|11|21-159-594-1232|2062.51|FURNITURE|uffily stealthy deposits haggle quickly carefully final request\n6228|Customer#000006228|Zb2Vj1EhkIivE CSYkb936,JYTQaWYT0a|5|15-417-317-1397|1524.63|FURNITURE|ts. furiously regular requests wake carefully regular packages. furiously regular packages about\n6229|Customer#000006229|6jBRlUNs3Q,XQZsgUuaWybaSY|17|27-996-380-7890|3480.92|HOUSEHOLD| packages. even asymptotes nag ironic excuses. slyly ironic deposits cajole. express requests\n6230|Customer#000006230|MGXvPZQ6UMzc4PbZcSUl8kGfew|18|28-226-342-3356|3763.78|MACHINERY|ously pending platelets; busily special theodolites sleep carefully! express platelets sleep slyly acros\n6231|Customer#000006231|DssbpsUtrcZVi81wG|11|21-183-229-4023|5805.58|HOUSEHOLD|ding accounts thrash carefully alongside of the even, regular ex\n6232|Customer#000006232|T0JFOylApn8YQr,|18|28-847-729-1271|2455.95|HOUSEHOLD|ly pending packages are. regularly express accounts past the fluffily regular p\n6233|Customer#000006233|4ZbcmGRlrqQ5|22|32-482-650-3134|3186.31|FURNITURE| instructions along the regular packages maintain above th\n6234|Customer#000006234|lQaP7fCR8lVTQY7pkMi1BLv|23|33-658-619-9537|2250.65|BUILDING|dle packages boost slyly. quickly special theodolites wake furiou\n6235|Customer#000006235|YpIuvasSAUFQ027rL|11|21-845-791-5239|6718.32|AUTOMOBILE|e the ironic, final instructions. carefully unusual somas haggle quickly quickly ironic requests. quickly eve\n6236|Customer#000006236|zQjCvBPfacwUT0nrJ2uF,4FtSAC0ldB|17|27-557-761-6785|8836.84|MACHINERY|uriously bold accounts boost blithely ironic theodolites. quickly even packages mold along the \n6237|Customer#000006237|jgXVEi0rAQCxBmRGM1vE1|0|10-312-679-5009|7806.93|AUTOMOBILE|oze slyly. thin, unusual depths integrate quickly alo\n6238|Customer#000006238|AckKNmINwCbVw kC5bZ0u3Hh7C3,7Rdva0|15|25-749-568-5891|1052.60|HOUSEHOLD|hely special deposits. regular requests solve un\n6239|Customer#000006239|PklZCqNbiyA9|22|32-305-456-4164|1007.44|AUTOMOBILE|ular packages. final, even ideas print slowly along the pending acco\n6240|Customer#000006240|kxWW41iCbmQJFT3GxGtmHakhcHZ07nW0diOTG|8|18-432-692-3478|939.14|BUILDING| even dependencies. pending, regular packages wake carefully above the deposits.\n6241|Customer#000006241|ucocla,JAAW amojIyO Ow |7|17-623-128-3126|8018.46|FURNITURE|tructions wake fluffily. enticingly regular requests sleep furiously silent packages. slyly special \n6242|Customer#000006242|1J2Yz5f1lOLcsSkMTme|14|24-293-990-7292|3379.23|FURNITURE|uickly ironic packages sleep. fluffily regular accounts wake qui\n6243|Customer#000006243|JotibBD2zs87K8|19|29-356-403-8790|-377.14|AUTOMOBILE|ly even accounts. final instructions haggle. silent, silent platelets are around the carefully even accounts. caref\n6244|Customer#000006244|5XQ1QzJOWRsj2dkV0WnG2lFantHSML9hToTT2tw|21|31-123-516-6719|6385.54|FURNITURE| furiously even requests sleep quickly under the pinto beans. ironic warthogs thrash sp\n6245|Customer#000006245|okJFjGfCET8m41TwZdswM4nqi2,wx8qP6tpM8GE|19|29-589-374-8352|3766.89|BUILDING|ounts cajole. deposits are. quickly final ideas across the carefully ironic notor\n6246|Customer#000006246|5l,sIIFQVnUUtzs1|10|20-124-894-8392|3090.40|FURNITURE|ckly regular theodolites use. furiously regular instructions against the regular dolphins are sl\n6247|Customer#000006247|22fOQj6uLxyE6L,ev4yMzPv8FuBvstp9JJAmez|3|13-961-966-3385|7408.81|HOUSEHOLD|s should have to haggle furiously even requests. furiously bold d\n6248|Customer#000006248|F3NgALjt9qjhFKEDz|10|20-237-638-2221|8834.94|AUTOMOBILE|ckly according to the theodolites. foxes along the ironic, bold accou\n6249|Customer#000006249|I3e rogGC4PkngfpXmtiw|15|25-311-257-8481|8946.03|MACHINERY|efully across the fluffily bold tithes. fluffily regular frays nag blithely along the sometimes eve\n6250|Customer#000006250|swZXFAFQ2O|17|27-861-967-3557|5926.27|FURNITURE|after the furiously ironic asymptotes. pinto beans nag. fluffily regular ideas pro\n6251|Customer#000006251|GWig6svLrx4Lcr0I tMBGMgd7HuyLGjbDJnFkGTl|8|18-245-505-4627|6920.15|AUTOMOBILE| unusual dependencies wake furiously even foxes. regular pinto beans mold furiously spe\n6252|Customer#000006252|iGu23V6R50fBd4,WCMeGmcF9QaffCWOZiiY|22|32-311-215-9551|1928.88|AUTOMOBILE|packages cajole. carefully ironic packages nod along the even excuses. foxes wake against the packages.\n6253|Customer#000006253|dHtDAyg0dAsmNZUJ6yonI6|9|19-583-938-5958|-356.48|BUILDING|ffix blithely. special, special packages believe. slyly regular platel\n6254|Customer#000006254|XpXxuBjXYzFBXCvTp8sO4 0zwCiWD9ggiF|12|22-251-775-8243|1538.38|FURNITURE|. blithely final courts are quickly across the ironic deposit\n6255|Customer#000006255|l6XtT3yvhZ,VzKzrXl2vQiIgwcrFB3qL7fuARSYk|5|15-472-424-3063|3495.68|BUILDING|even excuses. fluffily regular pa\n6256|Customer#000006256|,fpJxiGODuy,EqJmiD9qM1DN|23|33-628-309-9349|9846.83|FURNITURE|lithely ironic, bold deposits. blithely bold\n6257|Customer#000006257|etreRcLjc7uC|17|27-604-361-9761|9836.21|AUTOMOBILE| asymptotes wake slyly. bold excuses are furiously carefully furious accounts. slyly even packa\n6258|Customer#000006258|CzJGapE7fxQx3x9eN A7O|18|28-429-156-3365|7221.20|FURNITURE| foxes haggle slyly slyly final asymptotes. slyly even packages wake furiously along the \n6259|Customer#000006259|fpEAMihvplhOKyor ZRcf2bEUOwQgGz6SkilLk|16|26-449-972-6429|1723.42|AUTOMOBILE|instructions are evenly unusual ideas. furiously regular pinto beans among the furiously bold deposits wake\n6260|Customer#000006260|oJLJxevvYZqj,n6Dq,L5V6C|4|14-214-869-6336|405.59|FURNITURE| haggle above the slyly regular platelets. pending theodolites wake carefully even \n6261|Customer#000006261|rbTkXWn,HeQxxR8SLRuBS3m,LK|12|22-636-489-9192|5612.90|AUTOMOBILE|ructions. final, unusual pinto beans are furiously specia\n6262|Customer#000006262|HlUg CpG1hqLgHf|14|24-761-458-3272|8970.62|FURNITURE| ironic requests believe quickly after the furious dependencies. express, fin\n6263|Customer#000006263|nTwk5ECJ6elmDX8zLW7Fta9u9PlmTaqqRNja7|14|24-936-988-1040|1314.66|FURNITURE|ckages use furiously slyly final accounts. bravely bold dependencies about the carefully ironic asymptotes are \n6264|Customer#000006264|pV82CJ4rNyOcMzCXNmdy|17|27-583-887-6592|5392.22|AUTOMOBILE|al, careful instructions affix blithely. ironic \n6265|Customer#000006265|,8NdehNjF5ojMMEKikadmc2ng|23|33-192-952-7496|6977.71|BUILDING|y after the quickly pending requests. unusual\n6266|Customer#000006266|9OKHx1,rXIAV0pq6Vj,uERU44LaT|22|32-443-596-1740|1468.66|BUILDING|g blithely. ironic foxes cajole blithely around the packages. \n6267|Customer#000006267|Hxi,BwRbqRQUkum7Ts3R ugk4w58Ozmpp|20|30-657-865-4960|8650.75|HOUSEHOLD|l platelets sleep blithely. quickly pe\n6268|Customer#000006268|cG,c4luyALcY|5|15-764-581-5523|4236.85|BUILDING|special hockey players wake fluffily express, bold pinto bean\n6269|Customer#000006269|t3tcDR3QxuXh1Q5eHbzBls8jxxc4eCZSKKu Rh|16|26-790-547-6046|3793.75|FURNITURE|ccording to the furiously ironic deposits. final decoys wake along the patterns. blithely even\n6270|Customer#000006270|PGn,pJmM gsA1tDtDbbuiiGra57c4FL|18|28-717-120-2144|3485.91|AUTOMOBILE|deposits. pending braids cajole fluffily pinto beans. carefully ir\n6271|Customer#000006271|C61IFNXGXjOgzUAf8drHHKFksk,dASWbIXele|15|25-214-187-2123|-342.40|AUTOMOBILE|ptotes haggle slyly regular accounts-- regular pinto beans use carefully against the blithely reg\n6272|Customer#000006272|HnzXtYtwH8Jco2wa,L|2|12-494-911-3342|7209.51|FURNITURE|ajole slyly. furiously final packages affix silent, final theodolites. even asymptotes x-ray bravely. reg\n6273|Customer#000006273|ZD7bJedn3FdCd3p1SLGq4rZGqBCMlic|0|10-717-770-2411|3278.98|AUTOMOBILE|uffily among the fluffily regular accounts. even asymptotes haggle finally \n6274|Customer#000006274|SgRPgKV3mB1oPuGe1ccFvjhDBRYiopGwIsWTNuOL|24|34-845-579-7944|8778.79|AUTOMOBILE|uriously regular requests. slyly regular instructions haggle. in\n6275|Customer#000006275|vFGTLUjxiQu4HEiY16P1jSBrn380WinK,|0|10-194-385-3660|187.26|AUTOMOBILE| poach above the furiously unusual sauternes. accounts poach slyly. blithe\n6276|Customer#000006276|qrFgLgA0RCdrQioauSbVb8g|11|21-790-836-6047|-777.89|AUTOMOBILE|uriously regular deposits. even deposits alongside of the slyly\n6277|Customer#000006277|0EKQ4D5RMYQ6,NHCtnq14Es7OIWwNDPRtBEswyFP|0|10-237-523-6848|2907.96|BUILDING|s boost furiously final, special r\n6278|Customer#000006278|F kMurdAhFU0C2KEiojmsS5gWlgxPZ5Q49iZl|5|15-299-327-8860|9996.76|AUTOMOBILE|detect slyly unusual hockey players. regular requests after the final \n6279|Customer#000006279|Ft4nZfY7lsZ ws|5|15-206-922-2248|-319.90|FURNITURE|ial, unusual accounts. final asymptot\n6280|Customer#000006280|eA0MCPdIfSK06GY JDS,GKrDGrr3e7ZqkB|19|29-971-689-4133|407.63|AUTOMOBILE|cuses. ironic deposits wake above the permanently ironic deposits. final ideas are slyly ca\n6281|Customer#000006281|yORAuTtjrCJF9lOKTJtS9|4|14-318-809-5732|7055.03|BUILDING|refully even frays cajole during the carefully even acc\n6282|Customer#000006282|fCqZiNSOZ46KUCaRvVFPG60DMq|16|26-750-142-4294|2969.47|AUTOMOBILE| furiously regular deposits. slyly ironic packages\n6283|Customer#000006283|tmVqD0BrhOBHlA|14|24-539-196-5846|9860.06|HOUSEHOLD|regular, silent requests. blithely ironic theodolites sleep. blithely even deposits nag r\n6284|Customer#000006284|zEh7eHXMGoSNP9h7Bk7G8axMfEkBVkj,NADgz9|13|23-583-198-6369|1178.44|BUILDING|fily alongside of the fluffily speci\n6285|Customer#000006285|nf 4a5KU8QuLSMPZWmEkyVq6UAD065pgsgI|3|13-353-741-3596|3913.54|MACHINERY|rmanent deposits print furiously among the slyly e\n6286|Customer#000006286|WbndcI8V39JL1oxVYtRHMvESj1|16|26-227-468-4312|3895.62|AUTOMOBILE|le blithely ironic packages. furiously bold instructions wake blithely ruthless, ironic pinto beans. even\n6287|Customer#000006287|b1WalykIgCZUEIk,KPnxg7ytSfEtEHsVRCX98H5|20|30-755-248-6558|-296.05|BUILDING| beans wake slowly according to the regular, ironic deposits. furiously regular requests cajole. express\n6288|Customer#000006288|iN4rgnOJ5RH8M5r6fvF75YNLBiT4loi|11|21-622-156-3974|5184.42|HOUSEHOLD|unts sleep carefully quickly even foxes. fluffily bold requests about the silent theodolites breach quickly above \n6289|Customer#000006289|MuOF83xgQwBnj42OUkVScHj7RKGR3U7NkdzVdLc|17|27-437-457-4918|1509.92|MACHINERY|efully furiously pending pinto beans. pinto beans after the blithely express pinto beans haggle carefully packa\n6290|Customer#000006290|Vi6,QwAcedleabbr0SEv6LeHEU9SluHi57,|3|13-927-921-7780|9223.20|HOUSEHOLD|ual, express deposits wake carefully final instructions. slow, ironic requests across the furiously regular package\n6291|Customer#000006291|JwSPtW9LtBALXgfhtQ3H|15|25-657-320-9686|313.68|HOUSEHOLD|ess instructions wake slyly by the\n6292|Customer#000006292|xR0ShtQF06IrULp,|20|30-313-386-9424|910.80|FURNITURE|ely regular requests cajole blithely regular, unusual accounts. ironic, ironic packages about the slyl\n6293|Customer#000006293|40Q UY9xqRxuXTIF3Kh58iCsPTn6g6FXAU|3|13-828-651-7919|4785.81|BUILDING|uickly final excuses was slyly silent, express requests. carefully regular requests g\n6294|Customer#000006294|TEYcUTvYiWvxYjLqLx1a7dI7nqlcLDSG6S732 |10|20-377-548-8347|1065.94|AUTOMOBILE|uriously. furiously bold platelets detect theodolites. quickly final accounts can cajole according to the bli\n6295|Customer#000006295|YEDvY2dxIZA5AFzrYqM2R,Qu0BWeRzqZ|11|21-326-272-7171|5134.99|BUILDING|sts boost final, final packages. unusual pinto beans against the carefully final re\n6296|Customer#000006296|igCv4BEwY9,779Tix1Jw|21|31-763-742-7377|6768.63|BUILDING|could sleep blithely inside the regular accounts. furiously ironic accounts slee\n6297|Customer#000006297|DX,A9MX7Xpum|22|32-968-252-6956|7365.56|BUILDING|ctions haggle furiously carefully final pinto beans. pending deposits dazzle regular, regul\n6298|Customer#000006298|v5JDDFsvutMitkNO|10|20-805-968-6774|8028.38|FURNITURE| furiously fluffily pending packages. silent pinto beans across the final foxes nag perm\n6299|Customer#000006299|,4uQAq3HIX7qNb2tA yA|4|14-120-953-8397|4991.04|HOUSEHOLD|refully regular ideas wake carefully dependencies. slyly regular excuses serve. fur\n6300|Customer#000006300|iFuTBsELWUD|14|24-935-162-6227|7456.50|AUTOMOBILE|bold ideas. depths hang ideas. slyly final requests boost fluffily above the quickly bold escapades\n6301|Customer#000006301|JdPIarVVF5vd0laNpjFh|18|28-940-337-3490|7399.19|AUTOMOBILE|nal instructions eat slyly. pendin\n6302|Customer#000006302|Pmy5pzuh1YeOHkpciY0bCwMtDvMEPy2816MEBz|11|21-127-281-7462|1273.35|AUTOMOBILE|o beans. bold requests promise carefully slyly regular pains-- boldly ironic accounts are blithely pending \n6303|Customer#000006303|YlPFtsXInnJ9pylCP3WVnzHDGX,RbchWeoZ|21|31-241-146-4709|4079.40|HOUSEHOLD|ans run quickly slyly special deposits. carefully\n6304|Customer#000006304|Zg6sBmlhMs9XVZeDS3D|13|23-243-641-3155|8426.13|FURNITURE|ccording to the furiously express instructions. asymptotes affix slyly carefully even do\n6305|Customer#000006305|26EjrYGIc38wIM,rkf nwGVNIaWPfmsEpjmyyfP|0|10-141-833-6715|2088.12|FURNITURE|dencies. blithely final packages integrate furiously. pending platelets use across the quickly bold de\n6306|Customer#000006306|Bp0L3gbYTOCVi9N3Tq6CtAMW6jFuqWTmOEB|7|17-595-455-5504|3048.38|BUILDING|luffily across the special requests. quickly special packages among\n6307|Customer#000006307|FJlRZXhd4LA,uWwwa78AoylhQkOOJrNZ|19|29-453-201-8768|5047.86|AUTOMOBILE|eat. quickly pending courts lose furiously against the slyly special platelets. carefully special\n6308|Customer#000006308|FmSPcC6tPeT6M6|10|20-499-286-6565|5329.24|BUILDING|s. stealthy, silent packages print. deposits are slyly even, pending accounts. blithely even\n6309|Customer#000006309|pHdznY21xwitGpZp|7|17-525-121-8608|4575.56|AUTOMOBILE|deas. quickly quiet instructions can are. express accounts use after the\n6310|Customer#000006310|n2yStHsmbyEE6P|6|16-432-150-5510|9468.61|AUTOMOBILE|e final packages haggle blithely above the qui\n6311|Customer#000006311|wSwN,5PelFjtbAIv,SzZXy05,GTmiXBsXSTfI|17|27-553-115-9891|-71.60|HOUSEHOLD|inal, ironic accounts above the excuses use evenly among the bold pinto beans. carefully silent foxes above the r\n6312|Customer#000006312|QMhVQC0PiraO8oEHoMJ14b8Gxf9gK4h5ATkCn|4|14-870-343-5163|4562.08|AUTOMOBILE|ily silent foxes above the ironically unusual packages cajole quickl\n6313|Customer#000006313|g66sNXSi5LC9tvZ|6|16-911-713-4691|2933.72|FURNITURE|beans. boldly final deposits wake blithely. slyly ironic packag\n6314|Customer#000006314|lqbO7daGUg0T0QppRJTLXXan2PJ4YiZUtA|24|34-155-178-2373|2996.03|HOUSEHOLD|uickly regular packages alongside of the slow, special requests wake slyly against the furiously regular accounts.\n6315|Customer#000006315|H3lTGfGTxl|6|16-965-207-3063|5307.12|BUILDING|packages. packages cajole notornis. closely express accounts according to the id\n6316|Customer#000006316|jbMHTjobBPcepRl|14|24-427-805-3364|6714.53|HOUSEHOLD|l, bold requests. fluffily unusual accounts s\n6317|Customer#000006317|1i8b72gMGW4MBizzmt2G2j9FiUhxKUV2xcJ|20|30-302-599-4639|154.99|MACHINERY| with the silent accounts? theodolites affix. ironic asymptotes across the idle, express requests h\n6318|Customer#000006318|iYPP2u47,ZVs pK,|21|31-463-681-6877|6923.15|BUILDING|quick packages wake slyly unusual \n6319|Customer#000006319|j1p9Rogz9sN1u9g0cyHBNsV5Uj0sT,mYh2Z5|14|24-464-821-9726|8487.59|BUILDING|ic deposits integrate slyly across the quickly ironic pinto beans. furiously re\n6320|Customer#000006320|utTqGM30xwZPpmVURyoh7jWE4emjju6JHYuUAWFs|3|13-381-184-9600|937.20|MACHINERY|ly ironic requests detect blithely inside the sentiments\n6321|Customer#000006321|QQ3MOdoHCo9I6SvghQ10xY|19|29-754-952-3500|8433.20|AUTOMOBILE|odolites. even, final requests through \n6322|Customer#000006322|NK2pKqhhwp LJnExfiTmLeCZm6bhLkXWRIfxBPQj|9|19-998-647-9970|-95.54|FURNITURE|counts wake quickly pending packages. silent instructions above the even, r\n6323|Customer#000006323|CSFI2KxIkCJ7O,KVsI9rtPZZi1cYypJbtN|23|33-716-180-9533|9240.58|MACHINERY|to cajole about the slyly ironic foxes. i\n6324|Customer#000006324|o7FUm0oOy,5Cz|24|34-225-267-1395|6470.97|HOUSEHOLD|furiously idle accounts wake among the closely bold foxes. requests grow fluffily abo\n6325|Customer#000006325|eI IZBMs2Neixixf|14|24-113-347-2651|9653.04|MACHINERY|he quickly regular sentiments. ironic, ironic theodolites doze. final deposits run blithely ironi\n6326|Customer#000006326|Vvne n,VXT ykC6eBZ202wy4ev360a7jWQugfmC|9|19-653-669-5014|8701.02|MACHINERY|yly. furiously express foxes sublate slyly. pinto beans grow. slyly final deposits cajole quickly f\n6327|Customer#000006327|zfiwjLhIm3ykcc0PExS1enEBsWkf|16|26-366-282-8221|2114.22|BUILDING|gle. accounts might nag silent deposits. pending packages use fl\n6328|Customer#000006328|wibapP1Bq,wY|23|33-499-740-7766|1651.24|FURNITURE|eposits wake carefully regular, final accounts. pending pinto beans after the unusu\n6329|Customer#000006329|GgzjDBrJgBnzfhNzdcbe7XSOs5a9CVHoO|22|32-532-430-2910|8415.80|AUTOMOBILE|ully regular accounts. carefully unusual grouches are. slyly even requests boost b\n6330|Customer#000006330|6c1MemjbKFOa41b5CGI4rox|16|26-223-365-1109|4490.78|HOUSEHOLD|counts haggle alongside of the careful requests. fluffily regular deposits are. ideas grow: slow braids sleep \n6331|Customer#000006331|7,qAyD7LhheRuOcwIJEzmPI|9|19-824-332-5078|3583.47|MACHINERY| between the pinto beans cajole quickly among the slyly special foxes. furio\n6332|Customer#000006332|s7FTXH37X2fnlxS|6|16-913-396-4738|436.62|MACHINERY|yly ironic theodolites cajole fu\n6333|Customer#000006333|IExKkHfdJck,eoVnei8NhqREtBsDfpFKG6otE|4|14-571-939-5220|-856.26|HOUSEHOLD|hinder carefully. deposits cajole \n6334|Customer#000006334|RwSGFTlf,AMKTl2|24|34-336-748-8274|5795.61|HOUSEHOLD|to haggle. furiously final packages haggle. fluffily final pinto beans a\n6335|Customer#000006335|4V2jDP,swx9N|18|28-547-289-9779|-197.98|AUTOMOBILE|xes across the ideas wake above the requests. carefully unusual theodolites according to the slyly fina\n6336|Customer#000006336|IZ8GS3783y5 K6zMxFP,wa9cvcmzVIe4noGXa|14|24-979-385-1940|3438.49|MACHINERY| foxes sleep alongside of the ironic, express gifts. de\n6337|Customer#000006337|PiZCe6IVnzD4lUtquLcVYM0eH|22|32-187-888-4292|5610.32|BUILDING|s. carefully final requests use bli\n6338|Customer#000006338|PUuGLhCv0G0OYIeBs|21|31-679-556-1647|9653.55|FURNITURE|ully regular accounts haggle. furiously ironic sheaves affix. furio\n6339|Customer#000006339|tk70NFG92XanAXo,NySdfkSiQcpyXHW|12|22-301-127-4981|6226.35|BUILDING| pending packages. slyly ironic accounts wake across the furiously even instructions. pinto beans \n6340|Customer#000006340|oxxavhy2E4A No|2|12-958-657-2432|1279.15|HOUSEHOLD|inal dinos use. ironic, regular theodolites sleep quickly theodolites. even pinto beans wake accord\n6341|Customer#000006341|JPBISK7sJEEmLqyjH8gwtLCqIgByZEh|16|26-657-318-9584|2573.55|AUTOMOBILE|ites. special ideas are across the reg\n6342|Customer#000006342|70kmTAuDuG9pOnUUXeUp en555nKm3lyh|11|21-100-162-4466|-326.28|BUILDING|n, regular ideas haggle blithely. bold, regular packages will boost blithely. car\n6343|Customer#000006343|RxSzYVBobmzOfG7NTC3JnFIvUoIcgSo|20|30-972-622-2287|3104.63|HOUSEHOLD|ngside of the close accounts are final, e\n6344|Customer#000006344|ad,0JVklm5JfxVGyc LkIojMtdbWpZnXT2a1aKSX|7|17-693-904-1827|4576.81|AUTOMOBILE|uffily special dependencies boost bl\n6345|Customer#000006345|es2go,e,Lr4TRItOoUAQKzu1OJIS,8cq50Yb|19|29-627-959-8977|8513.41|BUILDING|he carefully final excuses. final pinto beans sleep\n6346|Customer#000006346|50RfbxMaJICGgfSOXGA|6|16-291-905-7678|5910.74|FURNITURE| requests. ideas nag fluffily alongside of the slyly pending dependencies. quickly\n6347|Customer#000006347|70IEncoPD8K5Vin9BRkBxdndJkIraSM|23|33-977-252-8569|9804.85|MACHINERY|egular requests sleep furiously fluffily thin somas. special instructions after th\n6348|Customer#000006348|1W8CqVR0os|22|32-400-947-4328|8251.10|AUTOMOBILE|sits after the unusual, even pinto beans are sl\n6349|Customer#000006349|mb,ZNqwU0WkZFYDX6hw|8|18-762-580-1517|8319.51|MACHINERY|mong the ironic dependencies. fluffily special excuses solve blithely. regul\n6350|Customer#000006350|9PHlaqUJG84BxDkb8fe|9|19-525-320-9404|3019.75|FURNITURE|efully according to the quickly final requests. regular packages wake furiously after the quickly furious f\n6351|Customer#000006351|mXVsAYZWVd8rduL2Ndnd20a bFxcZ39umb|22|32-148-461-6773|7852.79|BUILDING|uickly regular accounts cajole slyly around the fluffily ironic courts. unusual theodolites a\n6352|Customer#000006352|RoiheUBTOtjUEm  L2kUWHswZBOmhW|23|33-416-643-4777|1902.93|MACHINERY|beans detect ironic, express requests. furiously final asymptotes boost furiously. express acc\n6353|Customer#000006353|TiJAQNquw5b57kjKHrf6RLX|19|29-104-330-1710|3465.89|AUTOMOBILE|equests. asymptotes are along\n6354|Customer#000006354|NUj4uSOE6ZlEKd5dhc|1|11-652-847-7151|4231.75|HOUSEHOLD|ons wake quickly above the busily ironic deposit\n6355|Customer#000006355|0gR84cQxuKr0m6V3z6YnGms2Kqj5 FT FHE5YRwg|3|13-206-334-2025|6066.98|HOUSEHOLD|ven, final deposits are carefully carefully regular excuses. quickly unusual excuses\n6356|Customer#000006356|6xjYndpkg 0HArUyB56Xqv7EyDD7JrEi|4|14-568-481-2395|6800.46|AUTOMOBILE|ronic ideas integrate furiously careful platelets. blithely regular packages sleep carefully. \n6357|Customer#000006357|,HVhNgTVe,T|22|32-202-348-8130|3312.51|HOUSEHOLD|e carefully final deposits. carefully regular deposits run\n6358|Customer#000006358|aynGZ5F8MJIzxoEJc kCqh7J9vj|16|26-600-100-6775|297.48|AUTOMOBILE|ages sleep slyly decoys. sometimes bold accounts nag quickly despite the\n6359|Customer#000006359|fhA9rzJfqM686ozX RLctv|22|32-890-744-3388|3722.61|MACHINERY|oost carefully. quickly ironic deposits eat carefully. ironic requests boost furiou\n6360|Customer#000006360|WYPts48L0tn7iuLS58Cw8JUY2GUY0enx|13|23-508-750-7646|6814.61|AUTOMOBILE|boost slyly blithely bold accounts. careful\n6361|Customer#000006361|VzLr6guzIzrZpZfDZjndwix,|23|33-598-626-9499|-780.01|AUTOMOBILE|ggle furiously regular requests. regular, special requests use. fluffily express asymptotes are slyly alon\n6362|Customer#000006362|0H7VGv7MTGlMxF8igvaoXhElF8S bqj|15|25-667-257-1612|236.71|HOUSEHOLD|riously regular theodolites-- quickly unusual packages are fluffily after the quickly silent packages: \n6363|Customer#000006363|M,pjP 1RfkcWiPc0wMlUmiaV4cmlz57JMV1BI,|15|25-466-653-4756|5382.91|AUTOMOBILE|carefully ironic requests cajole carefully across the carefully regular asymptotes. quickly pending packages slee\n6364|Customer#000006364|P7n15pH0vDHA|3|13-693-752-4345|4585.65|AUTOMOBILE|according to the carefully final accounts maintain carefully among the even packages. ironic, i\n6365|Customer#000006365|q9FqvI49NhkzJH6lwSPbM,8sD|23|33-576-256-7432|2994.15|HOUSEHOLD| use blithely pending courts. even, ironic deposits about the quickly special dep\n6366|Customer#000006366|EAiyyzQp,q7GUp0wQzThB4OasmaTEnE4z|12|22-771-559-9705|6622.87|FURNITURE|s are according to the fluffily ironic instructions. quickly ironic packages haggle slyly among the furiously\n6367|Customer#000006367|hGP9UlKdD2BN3LePLnOJ|2|12-744-594-5061|-340.83|MACHINERY|y final foxes believe ironically. carefully pending theodolites haggle. carefully final pinto beans haggle care\n6368|Customer#000006368|9wlLx9qDuskAA5Lg3CgbgK2,RYX3|7|17-527-847-1825|591.04|HOUSEHOLD|arefully regular ideas use furiously after the special, special waters: accounts alongside of t\n6369|Customer#000006369|2GoiGrH9dEHSKHX3Y8fbA|3|13-817-557-7983|9563.67|HOUSEHOLD|e carefully final accounts. quickly final d\n6370|Customer#000006370|mpRkYWqUoJQncfc7Q3VEEgI3eyoGyKFzPJ|3|13-586-788-3167|2734.72|HOUSEHOLD|wake furiously carefully regular theodolites. unusual foxes haggle slyly among the regular depos\n6371|Customer#000006371|sqDry7KOh6ztkEJrGoX Y7NPIBW3|16|26-326-418-1698|2443.10|MACHINERY|onic packages dazzle furiously. ironic, final foxes wake slyly. re\n6372|Customer#000006372|cN5aX8yXPYruA4rITmdh8e1QaOEyjEeub|7|17-585-758-4417|7197.10|MACHINERY|ithely special requests are alongside of the quickly final requests? furiously regular instructions lose furi\n6373|Customer#000006373|Ap2nqlR9SntNoWkk5DFSON84r|22|32-608-822-4087|3700.15|MACHINERY|he packages. unusual asymptotes haggle alongside of the final, \n6374|Customer#000006374|6U,tnx3EQ6ymmLLtrRYD8FwwaQEiwvEqA2pL|20|30-879-852-8442|3388.38|AUTOMOBILE|s at the ironic, final pinto beans nag quickly after the packages. slyly even instructions wake furiously \n6375|Customer#000006375|,f00Mk7z1TQ4lHuZQA|17|27-709-238-4692|-510.18|HOUSEHOLD|nal excuses. quickly bold packages across the express requests sleep slyly about the slyly final foxes. ironic de\n6376|Customer#000006376|9xX7j8zvAb3rY,y2N6rakYVhkcNShJZ|6|16-888-795-8432|3376.54|AUTOMOBILE|lent foxes affix against the even deposits. fin\n6377|Customer#000006377|fb5rLUH6Hn|18|28-929-173-8781|9538.01|HOUSEHOLD|lly. instructions are. regular realms after the deposits integra\n6378|Customer#000006378|jk4,Yt5J6YsUnLKs3Fj8PWF8y|21|31-227-241-6464|330.88|BUILDING|. express pinto beans doubt quickly. silently bold platelets haggle. furiously regular packages sleep alongside \n6379|Customer#000006379|fJOnWmoLfEKa8FDt9T6foVT2njtBKSEnnsQlV0|1|11-989-205-6094|4057.27|MACHINERY|lyly regular deposits. foxes cajole blithely regular, ironic accounts. reg\n6380|Customer#000006380|A3CZEoXr2U3M9TJq|14|24-601-213-9207|-453.65|AUTOMOBILE|its nag quickly. furiously unusual platelets x-ray. car\n6381|Customer#000006381|qgLG,UnydWWKKYvXEe6g,|7|17-877-502-9214|7346.88|HOUSEHOLD|onic instructions wake furiously according to the carefully express instructions; pending theodolites\n6382|Customer#000006382|iT3z1RZJfuFhTVQWn gUQQ30J59FLhUtNa1y|5|15-884-325-7498|2101.57|MACHINERY|ely pending requests haggle s\n6383|Customer#000006383|pZMLOQVPjfqg3JvDp|8|18-910-470-6748|9130.34|MACHINERY|its. blithely express asymptote\n6384|Customer#000006384|eRUjfmK9XOnuIxULs,H3g2jasWFLc|23|33-767-423-3297|1756.89|FURNITURE|sleep alongside of the slyly idle packages. slyly si\n6385|Customer#000006385|feb0lDE33xT7COEBl4CL|15|25-446-991-1683|1948.61|AUTOMOBILE|eposits haggle blithely regular, pending requests. carefully even\n6386|Customer#000006386| ,6di,DsCLRoei7glq03Dv03xA|24|34-991-678-6036|1252.97|HOUSEHOLD|sts sleep carefully blithely bold packages-- the\n6387|Customer#000006387|YU5Ai0APMax|10|20-581-220-3948|2536.86|MACHINERY|ckages use slyly regular, regular deposits. special, regular deposits\n6388|Customer#000006388|p8xZE4LxJw,BhxqdqhnokSPs7TIAZyieKyyzse|14|24-525-264-8052|9925.28|HOUSEHOLD| according to the regular requests cajole carefully above the even depend\n6389|Customer#000006389|h1DyF2DxWF0|10|20-749-986-1120|8270.04|BUILDING|slyly express packages affix. carefully ironic requests use among the bold foxes? blithely regular hockey p\n6390|Customer#000006390|bn2EoZThG0 s3CAOW0V|17|27-231-144-4566|7571.48|AUTOMOBILE| bold asymptotes use fluffily special packages! slyly final theodolites unwind slyly about th\n6391|Customer#000006391|tJFRcLobeDbMdgzr1EfSw7PtH8Uqx97Pd|10|20-447-685-8271|3923.68|HOUSEHOLD|yly thin braids haggle quickly unusual, pending deposits. ca\n6392|Customer#000006392| Nn14Ugte47JXBLpdJj4NAXbnsmyk2ykL8v0B|6|16-236-622-1653|9277.66|FURNITURE|s use. bold accounts affix about the ironic theodolites. ironic, ironic packages according to the special pack\n6393|Customer#000006393|LrSfeTLkBD8iJmD4,mkCP0awj6UnAxhohA|10|20-600-941-3910|2762.42|BUILDING|ial sentiments cajole quickly. slyly unusual accounts boost blithely regular \n6394|Customer#000006394|Qsb,p1hXnullljxxWg7svdXJSXRT58Tjxm2|2|12-653-895-4516|5972.35|HOUSEHOLD|rs doubt even requests. slyly express requests haggle ironic packages. pending accounts nag fluffily after \n6395|Customer#000006395|g qNyxByuAIIKgSdK8ye9mw B1ujFXueIu9|17|27-165-881-6729|379.03|MACHINERY|are furiously. carefully silent accounts snooze quickly along the bold, final depos\n6396|Customer#000006396|OkUGWXg2r42,|21|31-720-445-5419|-695.45|HOUSEHOLD| even theodolites nag furiously. busy instructions affix slyly after the careful accounts. doggedl\n6397|Customer#000006397|OupmJTZAYWzow1H3zbv,v|3|13-204-711-1078|2415.67|AUTOMOBILE|ly against the furiously express notorn\n6398|Customer#000006398|nE3eEiDxvFRyoT|5|15-144-311-5947|8155.12|AUTOMOBILE|ly special requests. bold, ironic accounts nag furiously sly\n6399|Customer#000006399|dQbyCKR,g2,MXLYPiFE1NlLxzL|20|30-783-132-6452|1103.67|MACHINERY|tes. pending dolphins nag blith\n6400|Customer#000006400|B7M0M7FZy06iyPxs VmGJaRJeYgSiL|13|23-177-650-5024|1362.29|AUTOMOBILE|ctions are above the permanently ironic packages. \n6401|Customer#000006401|yveLKpTUIx7K6WtnhXAcz5|23|33-945-761-8571|2891.62|HOUSEHOLD|yly final foxes integrate across the carefully expres\n6402|Customer#000006402|GLZyBHPsfH,y|9|19-260-224-4337|-594.88|HOUSEHOLD|uctions cajole slyly according to the f\n6403|Customer#000006403|9eX1E4GyIXptoHrL5db b7wqVYaUB55u|24|34-376-305-4671|9717.24|FURNITURE|ly unusual accounts cajole slyly even platelets. carefully even package\n6404|Customer#000006404|s,kUf5HrSnq,g8un,UAcz7HvRQ MV|19|29-917-546-1913|9678.44|FURNITURE| furiously bold deposits eat ironic ideas. slyly regular theodolites across the even i\n6405|Customer#000006405|I0BmHPY,nxSz0vA8a|24|34-382-195-4386|6618.54|AUTOMOBILE|sly pending braids. slyly even platelets sleep fluffily final packages. express patterns integrate finally pen\n6406|Customer#000006406|lL3dzJv7lVqtqz,DiuUHcvH1EcpM9X8jba|21|31-342-422-4630|3830.68|AUTOMOBILE|es would haggle after the final pinto beans. slyly ironic deposits sleep quickly. asymptotes h\n6407|Customer#000006407|BtK4gy01cd|6|16-862-127-3910|8151.95|MACHINERY| ironic platelets. carefully bold excuses mold slyly above the pending pinto beans. asympt\n6408|Customer#000006408|oa9bZUw3jwpkK,Gd|9|19-912-624-3972|2836.65|AUTOMOBILE|boost against the quickly final requests. daring\n6409|Customer#000006409|T0HCEe4pnQrvw|17|27-890-763-2774|-451.44|BUILDING|its about the accounts nag even foxes! slyly i\n6410|Customer#000006410|GGGN,NDpImMdzCHOjMg6D,35uOrTr3yJ71fW|9|19-197-885-8569|530.65|FURNITURE|side of the special pinto beans. dependencies are. regular, ironic foxes cajole\n6411|Customer#000006411|z3ej9PD1gmKm1uaQySQhjFndp0kz062lnUVt|0|10-759-591-4295|6576.83|FURNITURE|y bold foxes. foxes haggle slyly about the silent, special ideas. slyly regular requests haggle across the pendi\n6412|Customer#000006412|wJVfQt,87Ckyca4rS4yh8PPGbzQyVxCXqil9ex|24|34-744-473-7572|6753.76|AUTOMOBILE| furious accounts after the furiously regular dependenci\n6413|Customer#000006413|okFK1CC8ibk3ml7X4ZhRl|10|20-949-158-4934|-563.78|AUTOMOBILE|ades. frays wake slyly. carefully final realms sleep. qui\n6414|Customer#000006414|3mdeAURniRPufSi6dz2|24|34-799-456-8937|4444.30|AUTOMOBILE| final pinto beans. furiously bold pinto beans nag. blithely fina\n6415|Customer#000006415|0SOe8iGHkEtlpwV7 e|10|20-971-763-1702|3396.66|HOUSEHOLD|ependencies. quickly ironic pack\n6416|Customer#000006416|WGVJj9TyQ5ac3qL7RHW8jSOYQH6XZV|2|12-415-551-2448|6601.37|FURNITURE|lar theodolites. accounts use sometimes fluffily ironic packages\n6417|Customer#000006417|06dHuGAxyFTfEygMp6ZU|23|33-360-140-2353|1256.36|MACHINERY|c sauternes. carefully final fox\n6418|Customer#000006418| ydC4OuGkly|1|11-186-687-6620|4592.96|AUTOMOBILE|sts. unusual deposits haggle against the regular, \n6419|Customer#000006419|JisGDcZRiDHg4yLNKSc2HPy32KfTIL,|12|22-243-778-7315|4838.38|FURNITURE|ound the fluffily furious accounts. courts haggle furiously about the quickly final packages. carefully eve\n6420|Customer#000006420|ROdVxbHD0,GVCY,9NwYfDO|22|32-469-891-2051|4658.51|BUILDING|sts cajole according to the dependencies. final requests cajole carefully. final courts are fluffily express, slow\n6421|Customer#000006421|fv4AI3OzYxdrM3I4cuDUoxMfOubOlO1m6Oxmv|3|13-450-314-2988|6650.59|FURNITURE| according to the slyly bold accounts are fu\n6422|Customer#000006422|ZltvyXcMiO|2|12-501-888-2104|3415.78|HOUSEHOLD|nto beans. fluffily unusual deposits boost slyly. quickly \n6423|Customer#000006423|7nudsRA5wmNNjs4,FThBletUyOIq|19|29-421-996-4033|9077.24|FURNITURE|inal, special accounts. furiously final pinto beans a\n6424|Customer#000006424|ieZS lxnQBTqJO8BOfz7mcPbUsnlS|6|16-525-665-8732|258.29|MACHINERY|lly even accounts sleep slyly alongside of the blithely final asymptotes. bl\n6425|Customer#000006425|y4kyZJUM4AOCGcaIjuj8as6XMgEb,tZyXaWTY2yS|9|19-638-440-4991|5006.99|AUTOMOBILE|fully even requests integrate quickly final pinto beans. carefully silent ideas cajole \n6426|Customer#000006426|MBbafnBIIVTjNxVB8,yZcLEMlp5FbyMCFozSR|16|26-200-900-4409|529.42|MACHINERY| bold requests wake slyly. even instructions are. slyly express accounts nag blithely \n6427|Customer#000006427|Xa17u3du45DiLnp8SEaIgKaS8l|5|15-852-433-6135|7907.55|HOUSEHOLD|s integrate even, regular foxes. blith\n6428|Customer#000006428|jiZdycWOv3UcTXmeQYadg7xOV6LRX|20|30-758-610-5037|9217.56|HOUSEHOLD|kages boost slyly carefully pending packages. b\n6429|Customer#000006429|723rhJstUlnqt  siNCyOV67ZVvTEXcJ0PqJ1uE|14|24-314-971-8033|4648.01|AUTOMOBILE|thely pending theodolites haggle furiously express requests. fluffily even requests are furiously pending requests.\n6430|Customer#000006430|xXFJR6GUfqeCyN5PiAvrw7HV2U|11|21-549-125-1175|6077.54|BUILDING|quickly among the special accounts: even, special packages according to the quickly sp\n6431|Customer#000006431|alr2AvVdnwHuR6MYiNcZvdfHfN seFOpZMS|4|14-847-121-5795|8310.76|MACHINERY|inal orbits about the final deposits wake furiously blithe instruc\n6432|Customer#000006432|4PGhIJsN8,hNjTsk6|22|32-173-374-5893|9988.42|HOUSEHOLD|encies. special requests after the regular, regular packages use\n6433|Customer#000006433|a3pPw8Sauu6hhR4k5uL7wg1H95kiZ64Tk|19|29-909-421-8085|2412.87|FURNITURE|ickly final deposits use carefully. blithely\n6434|Customer#000006434|XEo,6jjEzKR0aOUcozaEu8 gisw|2|12-500-275-2120|5131.87|BUILDING|e slyly bold pinto beans. final decoys use against the special ideas. furiously ironic courts against t\n6435|Customer#000006435|Bg7iFjWjLVQKT0AEclRbHPBTDpvlrEXKAD9K2|21|31-382-296-1618|9295.79|AUTOMOBILE| bold deposits cajole furiously theodolit\n6436|Customer#000006436|yfj8DtQgQrb52|13|23-860-371-1102|-651.11|AUTOMOBILE|ular dependencies cajole evenly final the\n6437|Customer#000006437|pZ639dzBsf14zfwauL0jOSYTGQ870YRctiD|23|33-994-170-5321|-282.52|AUTOMOBILE| about the final accounts. slyly bold instructions are iro\n6438|Customer#000006438|RyVaFQyhkoASdbPjYYBI|3|13-147-903-2434|1684.90|BUILDING|lithely unusual instructions nag according to the slyly special theodolites. express, r\n6439|Customer#000006439|JoIRrAaO8XlKsPF35eCO8Vcy0o5L|13|23-346-902-8254|2048.44|MACHINERY|cuses. final, special ideas boost furiously. furiously close dependencies above the daring deposits wake according \n6440|Customer#000006440|EhPeQVf4268eEE81yT7L3kHeBFC1nsvYjF|22|32-963-990-1859|7040.39|MACHINERY|aggle quickly excuses. regular packages nag ruthlessly regular, idle sentiments. bold account\n6441|Customer#000006441|XG8rZ2j868y,WH1VUBZ|19|29-490-926-8692|9951.53|BUILDING|es affix carefully. slyly regul\n6442|Customer#000006442|KHfAHw,EYDgYxunkuoCi5UZb 0nZL6i868CET|19|29-922-394-2085|9442.52|AUTOMOBILE|osits haggle quickly slowly final requests. even, fina\n6443|Customer#000006443|pHv34YqZSMUDlEyxk Mlvf6Ub,|6|16-852-419-2587|1478.34|BUILDING|nic courts haggle slyly blithely regular theodolites-- special excuses use slyly. ironic dolphins are slyly f\n6444|Customer#000006444|7wOibQuMiSsum0zAfieMfvToucnh|15|25-268-268-9763|6469.44|HOUSEHOLD|sits use among the fluffily even foxes. furiously regular dependenc\n6445|Customer#000006445|J8HlYAyAUaCQpMoj4cTlS,TDmGK8gcKnrDxmUPG,|5|15-192-490-3051|5966.09|BUILDING|, regular theodolites according to the quickly even pa\n6446|Customer#000006446|V0Dd,KY57jzAlKGMwRdstqlBUgAj12janY O8|2|12-139-856-4622|5131.95|FURNITURE|bove the regular, even instruc\n6447|Customer#000006447|2iQk,R9iPSQKkb3e7oQi |23|33-450-821-7164|-380.57|MACHINERY|after the ironic requests thrash slyly carefully special\n6448|Customer#000006448|13Wg2FUg2Ffki TBsY517Ztb4fytGvRe8peQtJ0|0|10-486-606-3968|3677.31|MACHINERY|ing requests. ironic, ironic deposits cajole above the carefully bold deposits. pending i\n6449|Customer#000006449|pE4YcxWffV,7kwV8BX4XyOY4S7|1|11-503-687-8564|4467.60|MACHINERY|ptotes sleep against the slyly regular deposits. express requests wake ironically across the blithely r\n6450|Customer#000006450|Gc,ojHknX6MUSgmXpv8EmEhKPkU|24|34-272-661-2143|9888.45|FURNITURE|riously slyly silent courts. ironic, final excuses sleep after the regula\n6451|Customer#000006451|E,ekihaEI,uN4kGRUwQ csQjx3hVRvUDh,|12|22-457-204-2046|5307.46|AUTOMOBILE|ts integrate carefully carefully pending theodolites. quic\n6452|Customer#000006452|Qrin8a98Frzmr0kMzVeX9h6|18|28-426-597-9924|1512.79|HOUSEHOLD|ironic theodolites. requests detect carefully silent courts. slyly\n6453|Customer#000006453|pZ0jI IY sh07aKPdmQ7uv3ta8eU,jCXFbL1|22|32-372-439-2517|2372.25|BUILDING|into beans sleep at the blithely brave accounts. unusual deposits wake\n6454|Customer#000006454|ypyPdN8tQvcYERecN,3Oufizu|1|11-276-495-7172|4437.76|FURNITURE|ly special packages cajole ruthlessly around t\n6455|Customer#000006455|unZZGWq2HBJ4EKR3UlbpcORxMlO|2|12-239-754-9487|3482.98|MACHINERY|c pinto beans sleep blithely carefully express accounts\n6456|Customer#000006456|XF8NeBd23gN|23|33-589-473-6210|5405.47|FURNITURE|dependencies. final accounts are furiously. bold instructions use quickly. express, br\n6457|Customer#000006457|RWwZBhG0WF8ixJJ9csB|2|12-292-531-2808|9754.94|MACHINERY|olites cajole blithely. blithely bold foxes wake blithely. regular deposits sleep blit\n6458|Customer#000006458|9hveScYbQY iKmqJuNs9PetcC|9|19-701-840-4110|5425.87|MACHINERY|pending asymptotes are alongside of the ironic, pending dependencies. blithe\n6459|Customer#000006459|zd0TdwWz,IhDrPeawC3G9SHV2Tr0r|16|26-264-700-6190|633.59|AUTOMOBILE|carefully regular requests are furiously about the packages. furiously special accou\n6460|Customer#000006460|Pr5bp2A90JjbrjE1XN5HM1|12|22-504-912-3129|9685.49|BUILDING|ending ideas. regular platelets use quickly final theodolites. foxes affix above the regular epi\n6461|Customer#000006461|vgpOxXZRI,,Y92 1NO7N98jKwnOY7|5|15-362-557-8104|4166.62|AUTOMOBILE| blithe packages boost slyly. blit\n6462|Customer#000006462|3njV5Ft7NhNxW50neyjlryC0Ylq0fI0n,P|18|28-848-982-8505|433.66|AUTOMOBILE|l requests play carefully across the instructions. blithely\n6463|Customer#000006463|M9Oy,Xx9CbN GSl3L6khL0LTRgCi8MgX0gTzVbCQ|23|33-939-251-7239|4447.04|HOUSEHOLD|final foxes. accounts should nod against the unusual, even instructions. blithely regular theodolites hag\n6464|Customer#000006464|eF9E6ScHCw9,z8nF0py9 ySlB0 iHTIEEZRWl6H|1|11-870-572-9943|5468.53|FURNITURE|telets could are quickly regular packages. fluffily iro\n6465|Customer#000006465|hh2iSvQ3ixAtfId4QsSzJOMORcy4t|24|34-206-617-3619|2544.19|FURNITURE|express packages. ironic requests wake carefully\n6466|Customer#000006466|nxPwv4px0 1LW05BtXaT7m,b2a63Dq8s25b09|20|30-314-197-3871|1131.19|MACHINERY|believe furiously. unusual, pending deposits wake. quickly pen\n6467|Customer#000006467|VKsGXyxDaRzjYny fn 1zOOjPmkA9ZDp1C6lu|11|21-298-482-2423|1798.41|BUILDING| slyly special instructions boost carefully. final packages \n6468|Customer#000006468|NTWZxBhUwmLUSwoXnrTwKdFs2|8|18-741-953-3789|6840.45|MACHINERY|y ironic deposits are along the furiously ironic requests. theodolites can are. accounts use after the f\n6469|Customer#000006469|kDCpN,X8RAKe6EHXL6zsygq57|7|17-884-283-2518|4429.67|MACHINERY|osits hinder slowly along the regular accounts. even, unusual foxes wi\n6470|Customer#000006470|BHgwAOTI0WReuiU9Ub1y|24|34-790-284-2739|-501.95|HOUSEHOLD|c platelets. carefully regular platelets sleep carefully. blithely unusual dependencies haggle. packages use furio\n6471|Customer#000006471|z64U8Zsf 1ItZlk|1|11-284-691-1178|9608.94|AUTOMOBILE|bove the slyly even requests haggle even ideas. slyly r\n6472|Customer#000006472|sLyBV0ZtMvAH1QSOnxg6koHGsKzzjm7RWuEMRWQV|20|30-220-128-1332|4515.01|HOUSEHOLD|onic pinto beans. furiously final platelets promise never. fluffily ironic instructions p\n6473|Customer#000006473|Ee0nC9W1Y9j6e4V|9|19-156-916-4569|3728.77|AUTOMOBILE|al theodolites boost across the even dependencies. express theodolites sleep enticingly fluffily ironic i\n6474|Customer#000006474|AqmYPBgr9r vxl8sMzecS5ak3JJ4ByqN5 |9|19-500-356-4558|478.16|BUILDING|e blithely pending dolphins. packages\n6475|Customer#000006475|itSIjP3CZMWKp3MjgSuPFim0l6T|8|18-427-310-9935|4338.27|FURNITURE|pades. blithe attainments impress quickly ab\n6476|Customer#000006476|NbYct5LvnQfrajINpQm34t3yxD32UQED5bk5dNPG|16|26-531-511-8701|3327.81|AUTOMOBILE|side of the deposits are slyly around the ideas. quickly final accounts wake furiously. bol\n6477|Customer#000006477|qylnoy8Yoou7au1IjGNmAR,amKok|1|11-336-397-1897|4554.64|MACHINERY|ly permanently final warhorses. deposits ca\n6478|Customer#000006478|E1ZXZROw4YBwc4HhN2M|6|16-483-594-1126|9874.64|MACHINERY|lar accounts are quickly fluffily pending packages. special pinto beans above the slyly bold excuses hag\n6479|Customer#000006479|r,YZ8Bqo9gHbHFfnEITjeU4riKFay4mvovDTmc|11|21-143-392-9914|4160.96|FURNITURE| quickly express accounts. slyly furio\n6480|Customer#000006480|jZCx1qKm8AJtV|14|24-732-299-9650|4276.33|BUILDING|ending realms cajole at the caref\n6481|Customer#000006481|QM0JCcwqe 2OSh5LpTjoQVL0dIbCJ3unJO5V|9|19-708-287-2759|7278.40|BUILDING|pecial packages are busily according to the ironic escapades. slyly\n6482|Customer#000006482|dOVykuZo,txNWSMQncRnWc4Goi9sslnW5zv|2|12-270-253-5013|6111.54|MACHINERY|ncies. slyly unusual platelets haggle slyly above the even, express deposi\n6483|Customer#000006483|ZT8TwDuABR77|11|21-679-759-6880|7606.73|FURNITURE| are. carefully bold instructions by the foxes are along the slyly express theodolites. eve\n6484|Customer#000006484|VbH7IC5F ZR1CDcyyjxX|3|13-467-401-7503|2438.45|MACHINERY|ress whithout the blithely regular deposit\n6485|Customer#000006485|sCWkSZkKOpsnFhyNUIccdfvFvipe|1|11-541-386-1396|5926.60|HOUSEHOLD|ly express foxes integrate after \n6486|Customer#000006486|huOvXcWg648ez69Yyebn8|11|21-909-289-7137|1380.84|MACHINERY|l theodolites. slyly regular ideas sleep carefully slyly idle asymptotes. fluffily unusual ideas \n6487|Customer#000006487|1JfLyCcXKGeHiEV|12|22-967-924-9865|6789.80|HOUSEHOLD|uests across the packages could have to nod furiously silent dependencies. final or\n6488|Customer#000006488|fEUg4BCUhNIcaNVKNdkadz2N2lIDWybFm,quvpnO|8|18-915-822-5617|144.97|MACHINERY|final deposits serve carefully about the slyly ironic or\n6489|Customer#000006489|z6IrxkffxNyMYDRx4qR,PQnTDYSMXG3bsdBD8mx|8|18-713-226-2794|3494.92|HOUSEHOLD|s haggle around the carefully unusual deposits. carefully even theodolites are enticingly ironic dep\n6490|Customer#000006490|VjpZnXeSD2oIK0U,CNLNfRxZF1NDFadS W|1|11-122-897-9952|8180.23|FURNITURE|n instructions lose carefully alongside of the fu\n6491|Customer#000006491|J3,jSVfCkxq91fHjycRrqx1mQ,sGEFBT7iCtgu|6|16-977-273-4547|5239.92|AUTOMOBILE|al foxes sleep fluffily along the\n6492|Customer#000006492|nPlHrfRAlpx|0|10-440-853-6387|-723.71|FURNITURE|ruthlessly regular packages. pending platelets near the slyly ironic packages haggle quickly against the blith\n6493|Customer#000006493|ZT2dynxWMfOTM|19|29-295-246-8539|500.11|MACHINERY|ending decoys. furiously thin ideas cajole according to the f\n6494|Customer#000006494|Vu5i1odOtPFZVNY53su76yFnal |12|22-121-359-7339|-795.34|HOUSEHOLD|ts across the final packages are slyly ideas. blithely unusual ac\n6495|Customer#000006495|Hs,w9KO2RR At|18|28-465-937-3117|6679.86|AUTOMOBILE|deposits. theodolites hang blithely after the carefully ironic\n6496|Customer#000006496|tdAcewkJ,9IL7tYdP6xFSlR4sxhzqYx|14|24-755-676-4374|517.43|BUILDING|cies wake slyly according to the blithely ironic accounts. slyly even packages across the slyly ironic a\n6497|Customer#000006497|0m1WM8YSt2E1jhOAohr3Zs9NJuVlgORZFF85yp|21|31-417-915-1172|4355.91|MACHINERY|refully pending packages. even pinto beans haggle across the pinto beans. final instructions after the foxes \n6498|Customer#000006498|M,,F70BZbI7wFy Eij7bct0rFLaJkqFN5DS8vS8|3|13-779-226-9489|2765.60|MACHINERY|side of the pinto beans. slyly express theodolites sleep regular, special platelets. slyly bold depos\n6499|Customer#000006499|zXN1H4vSEbL VEvBO5lT8IICF|22|32-496-645-5422|4390.89|FURNITURE|tegrate alongside of the final, final theodolite\n6500|Customer#000006500|SQLIuXRcQ3Q7G38eUgMXV4ybIKPQxKS0|21|31-954-475-8701|7764.33|HOUSEHOLD|ggle against the silent excuses. frays doubt quickly. carefully pending deposits sleep quickly final\n6501|Customer#000006501|9v8gAkVB KcQoBRKbQ2rykps4fKqYX|24|34-433-352-7689|184.55|FURNITURE|pliers. platelets eat slyly against the even accounts. fluffily express requests are furiously unusua\n6502|Customer#000006502|mbRY4j5rudW|11|21-562-896-1594|8710.50|BUILDING|eep among the fluffily ironic req\n6503|Customer#000006503|4p3W4XHBSKbswkOpK3jehDgW|11|21-476-545-9987|7492.32|FURNITURE|st the quickly careful instructions wake quickly according to\n6504|Customer#000006504|,XoJJFtPqo6Hk7GxI4agFf9r5|8|18-223-523-7723|4395.75|HOUSEHOLD|al requests. carefully unusual ideas en\n6505|Customer#000006505|,35tS 0nsQ,|10|20-116-997-7935|1320.29|MACHINERY|al theodolites: carefully bold dependencies haggle pending accou\n6506|Customer#000006506|Mol5UoqoOCHvOfBHz3s07NcF7HoU2rIWZGlkcI|7|17-831-994-7711|1146.56|HOUSEHOLD|wake blithely after the idle, even packages. final pains c\n6507|Customer#000006507|ewNKMjvFSXSHZRYQnPbMCYqB|21|31-762-523-7126|7134.11|FURNITURE|ar foxes around the carefully even deposits haggle quickly quickly final i\n6508|Customer#000006508|Swm,c vbMg36c1z5,4fdBedBNG1OrfhsTPco|18|28-986-386-9035|952.00|FURNITURE|ters are above the express, ironic dependencies. express deposits according to the furiously furious packages pri\n6509|Customer#000006509|70iy4ZLmA6,OjYW7tTtxlnxkJf N5|24|34-658-604-7430|4234.26|FURNITURE|ly final theodolites affix fl\n6510|Customer#000006510|mHOy9Hl0Qj|0|10-992-585-3630|4211.77|MACHINERY|express accounts according to the quickly specia\n6511|Customer#000006511|Hqpb ssH,HNi|0|10-987-779-8094|238.09|FURNITURE|ost slyly carefully even packages. slyly final theo\n6512|Customer#000006512| 33vAzHrrXIgd68KBr4mD|7|17-511-867-8977|6571.18|BUILDING| after the final requests. carefully ironic packages nag slyly. fluffily eve\n6513|Customer#000006513|0yWTa31spHkfkccVy,mRh|4|14-344-180-9079|-125.66|MACHINERY|ites doubt fluffily. furiously regular deposits \n6514|Customer#000006514|F8eUA3o2KlPxQoOP42h,cNYShHe|10|20-816-198-3141|8911.35|AUTOMOBILE| sly deposits alongside of the requests are carefully carefully unusual a\n6515|Customer#000006515|knMzD7lwT9|16|26-565-114-8675|6121.09|AUTOMOBILE|t ideas. excuses across the furiously fluffy accounts use quickly about the express, \n6516|Customer#000006516|zL3M2qRYhxxz|4|14-657-773-6508|3208.09|HOUSEHOLD|ole furiously among the slyly express requests. foxes sleep blithely blithely bold ide\n6517|Customer#000006517|tjhz2CB9lXX0TcmPRX|17|27-923-897-4118|5407.70|FURNITURE|the furiously ironic packages haggle blithely pending req\n6518|Customer#000006518|1gxNOhqNUnOgcA8pfdSuYB1ROX|13|23-117-625-2924|3187.34|HOUSEHOLD|inal requests above the furiously final asymptotes are bold, \n6519|Customer#000006519|NomK60Y3wXy06J7YEMGp 3,XRpg0j9IxcDtVEAM|21|31-885-482-7206|8593.58|MACHINERY|pinto beans. furiously unusual platelets integrate. furiously special packages are. fluffily express fox\n6520|Customer#000006520|GgmUKbGO4YqccuU|2|12-135-446-1603|326.88|FURNITURE|p slyly about the silent requests. qui\n6521|Customer#000006521|ocqjyIQB3nrDvcPfNK6YdWLJRV,aNNfYjBl|3|13-856-838-6626|2762.22|AUTOMOBILE|ironic braids above the regular r\n6522|Customer#000006522|9lIS6iU8xfx2UB77M,cRbhUYe7WaawYE|2|12-106-450-9122|2611.92|MACHINERY|ress notornis integrate blithely inside the slyly thin deposits; ironic, regular platelets hinder\n6523|Customer#000006523|ppBY8l3kJDLcOjqD0mF5H|21|31-855-623-3767|6472.78|BUILDING|counts wake blithely. carefully pending requests haggle carefully. slyly ruthless asymptotes wake furiou\n6524|Customer#000006524|3NMnX6Cbi83z3Cul C|14|24-198-901-2901|7024.93|BUILDING|rges. even packages sleep across the furiously \n6525|Customer#000006525|9TeyC0eRLeHUiJYR9EA8fimqlu6biVgxdxVx|18|28-268-185-9919|2973.93|MACHINERY|s promise furiously across the final grouches. furiously final accounts according to the ironically final re\n6526|Customer#000006526|j2e,TUUVZxaFSuiJauFDS2eo|5|15-609-391-3715|7755.32|BUILDING| courts cajole blithely above the fluffily bol\n6527|Customer#000006527|R0sIPtavn8fgJTfsLCVTfSuuFHkTVFh0ZscC6Rub|13|23-215-568-8678|2722.00|FURNITURE|furiously: furiously unusual packages cajole. pinto beans are furiously across the unusual accou\n6528|Customer#000006528|5F4Zd7t2UCw COOvRxsTqSFYbNuT3LNWqAbE|15|25-123-454-7331|8627.24|FURNITURE|l requests boost carefully across the furiously regular dependencies\n6529|Customer#000006529|z MsqjmOtVW8ynDudk6XiUsAurx57YfL9|6|16-605-167-6202|5032.68|BUILDING| orbits are? unusual, ironic accounts are slyly. unusual requests sleep after the ironic, ironic ex\n6530|Customer#000006530|MV,sirXvlYHdNS|17|27-993-759-6289|3190.15|FURNITURE|e requests; quickly regular excuses nag\n6531|Customer#000006531|mS8cUs YGyXTFQ6,raiT IVNEprioVqOkf02J|2|12-613-384-6941|889.74|MACHINERY|ng warthogs print furiously i\n6532|Customer#000006532|TvhgzSH6z6mBFPH4PVuV8WYBdwaL|1|11-390-945-2159|2857.43|AUTOMOBILE|xes. quickly bold accounts detect accordi\n6533|Customer#000006533|nlQQZE8X8Gcs0DTc4FbikdL|0|10-292-966-8076|7733.20|AUTOMOBILE|ions. requests wake slyly alongside of the even, even depths. always regular accounts after the blithely exp\n6534|Customer#000006534|VxsPQuMbLvQ|14|24-272-292-9916|4916.15|HOUSEHOLD|ly alongside of the furiously unusual packages. special, ironic orbits \n6535|Customer#000006535|x nBq56OShrH,akf9CsjKBDGZXYjPPD0JClsM|22|32-183-174-5882|2786.57|HOUSEHOLD|e blithely express requests. packages breach along the packages\n6536|Customer#000006536|LCjRJ4bGqSQQ|7|17-209-711-6334|4800.56|HOUSEHOLD|ate slyly slyly regular accounts. furiously speci\n6537|Customer#000006537|crEEScryYpiugrKGzY2vjFvO22Bq,wCnTwIzpYN|10|20-987-261-6336|7057.74|BUILDING|ymptotes. fluffily final packages above the packages cajole carefully blithely regular excuses. packag\n6538|Customer#000006538|zKfgcSybmZ8|19|29-312-894-5415|4435.01|FURNITURE|nal, regular packages. furiou\n6539|Customer#000006539|7Il6KjzRIE xHg8B2wsoIP5y 5t|19|29-262-259-8994|1175.83|AUTOMOBILE| with the carefully even requests. even, bo\n6540|Customer#000006540|I8kQ2XzSVZ3sXkl|16|26-663-746-1303|4042.13|HOUSEHOLD|instructions integrate carefully. final requests sleep. quickly special de\n6541|Customer#000006541|3ua8FZTnQ5aC|18|28-614-720-7659|9117.32|HOUSEHOLD|al dependencies. carefully final instructions cajole account\n6542|Customer#000006542|0r NUZcHVeN5ZImNTuc2Gjf3st|4|14-681-824-2073|2821.51|FURNITURE|r packages. carefully even accounts cajole carefully along the regular, ironi\n6543|Customer#000006543|vv4vWlUAksCLLGzokFhUypa0d67QFAE2pxiH,|18|28-439-480-5859|9050.06|FURNITURE|express requests wake slyly even dependencies. close requests around the car\n6544|Customer#000006544|blMRmjCIk9qZDmu|8|18-430-406-1141|5344.32|BUILDING|totes sleep fluffily. ruthless, special accounts alongside of the carefully\n6545|Customer#000006545|EVlv0HyoPBatXo6VRaH0uSmnfd0YQZggZBaeHrB0|3|13-174-449-5530|5016.87|BUILDING| thin pinto beans boost furiously ironic instructi\n6546|Customer#000006546|zYQtqlAP5jGhQOtMELGmOF QYmKwL,|22|32-365-893-3285|1530.52|AUTOMOBILE|e carefully against the pending requ\n6547|Customer#000006547|3zLebAX1KgiP7|7|17-710-138-4406|3321.43|HOUSEHOLD|ide of the slyly unusual requests. blithely ironic packages haggle. quickly silent pinto bea\n6548|Customer#000006548|32TjRFavvtwh M|1|11-397-769-2069|7325.51|HOUSEHOLD| have to are always pending deposits. slyly silent requests wake carefully regular accoun\n6549|Customer#000006549|qAhHr41OY7bbo|7|17-522-198-9893|5850.07|FURNITURE|encies are blithely. special theodolites above the slyly regular realms cajole fluffily foxes. unusual requests sl\n6550|Customer#000006550|3bUibIk2 e0g9upYw7f|21|31-679-244-3796|2107.94|BUILDING|ending, bold dependencies boost. regular, silent foxes haggle. deposits h\n6551|Customer#000006551|CFd12CDJGcvLDzKaWj SOIjHwSGuIjnqclcJwqJh|11|21-528-245-9647|6066.94|MACHINERY|ans? furiously ironic foxes hinder blithely pending, pending deposits. blith\n6552|Customer#000006552|Nf6QCAunWDenuiZJmxzANjkcjzjl|20|30-889-967-1134|6162.01|AUTOMOBILE|. furiously express foxes according to the slyly idle accounts mold ruthlessly slyly slow packages. special, p\n6553|Customer#000006553| ocLpu754,ol|16|26-166-724-4677|8985.90|HOUSEHOLD|refully regular courts. requests sleep: special, final accounts sle\n6554|Customer#000006554|A9l1P,V5p9431yso381EbxGEBIrjzbQqe4 Hn|8|18-845-570-6654|5037.71|AUTOMOBILE|e along the warhorses. fluffily fina\n6555|Customer#000006555|UMegOBlfpGA0IaM|21|31-923-419-2629|-120.55|AUTOMOBILE|alongside of the furiously special\n6556|Customer#000006556|p8DBE,GLPulEItM,G,YMkdQ|10|20-224-737-2850|6794.45|AUTOMOBILE|lites. blithely special accounts use furiously deposits. blithely regular accounts use evenly pending accounts.\n6557|Customer#000006557|wUknF8m7MjQL,,6nUI1gB LWN|0|10-666-886-5603|1405.40|MACHINERY|even attainments are carefully pending asymptotes. special deposits could have\n6558|Customer#000006558|Kzbfegyh P0YRTxW9aCqgoNrx3jYa8j6j|13|23-111-200-2537|1525.10|MACHINERY|ages after the slyly regular r\n6559|Customer#000006559|j4vrCbCwWy4ZdMUF, bHX58wklRdI0|0|10-180-831-4219|314.77|FURNITURE|ts. carefully bold packages cajole permanently besides the quickly final foxes. bol\n6560|Customer#000006560|nicoCxzD22IH|3|13-393-157-4401|8302.04|AUTOMOBILE|silent deposits sleep quickly. platelets sleep furiously furiously bold ideas. carefully unusua\n6561|Customer#000006561|U B1notuUA|24|34-800-483-7728|6486.47|BUILDING|nstructions. regular packages haggle against the bold packages. deposits cajole. blithely unusual pinto beans\n6562|Customer#000006562|6XINODN,YblT3W5FrWSo2voo7MeU5kv8hTni|14|24-485-841-2292|6057.40|BUILDING|s accounts. regular, ironic theodolites haggle\n6563|Customer#000006563|ckpsWGe2Xt2QnI05rzcbreoFdTEK,OwotWDbccxm|11|21-535-565-6266|-250.80|BUILDING|bold deposits after the ironic, regular asymptotes are regular dolphins. quickly regular requests are bra\n6564|Customer#000006564|pqkVXBhs6SV,fGXfelR1l29,yit1uOZ|13|23-518-531-7793|6761.96|MACHINERY|can cajole blithely even foxes. dogged instructions wake after the bold requests. furiously regular pin\n6565|Customer#000006565|hqYF09xT hXv3RHhuSgCCfHnZr7Sz, 2Zuy5XfRN|22|32-398-565-3295|5309.19|AUTOMOBILE|furiously express deposits. furiously even asymptotes cajole quickly. final dependencies sleep slyly. \n6566|Customer#000006566|i9Aw4WrBV7UKH3JXXjsdz5 W5UKD1t,pSOo0v7r|11|21-404-346-3746|8548.38|FURNITURE|ages haggle furiously regular p\n6567|Customer#000006567|R8iEyHwfq7JefvXy7woKlcnqbVN 0TYcPZu|5|15-714-627-6738|-379.87|FURNITURE|l ideas nag furiously bold hockey players. furiously unusual requests brea\n6568|Customer#000006568|7KgPG F0nEcyBKpQJqL|22|32-338-335-4860|4514.95|FURNITURE|ly. regular, silent dependencies affix about the idly special excuses. co\n6569|Customer#000006569|67iACh32SK|1|11-178-911-3792|2712.91|MACHINERY|al dependencies wake blithely against the ironic pinto beans. final\n6570|Customer#000006570|Xvm7kfDpAmTyhISPbrqibCUopcCq1qqNCOE1pzlO|0|10-475-471-1307|9873.25|AUTOMOBILE|wake bravely against the blithely express theodolites. furiously silent deposits a\n6571|Customer#000006571|fj4IX5Zk4vvfUuEIC|14|24-892-309-1142|2767.45|FURNITURE| pending accounts sleep above the blithely regul\n6572|Customer#000006572|ar ADMGk0y2|19|29-771-561-9164|2540.69|MACHINERY|telets use blithely accounts. pending platelets integrate fur\n6573|Customer#000006573|N58H8Hoy0XD216MOSnWysRXUlIsVqAUR6GZ1LTlF|18|28-312-121-4734|8367.22|FURNITURE|lyly slyly pending foxes. blithely final requests cajole slyly after the even, pending deposits. slyly i\n6574|Customer#000006574|nzW785SZCqoQLHUqxecq,xzU0EyIxa,bwZ|17|27-392-453-6805|1972.26|AUTOMOBILE|ts above the carefully ironic packages cajole alongside of the sile\n6575|Customer#000006575|GARTfwst7rRbB5|22|32-486-660-6159|2769.78|MACHINERY|uriously ironic accounts haggle blithely. ironic pinto beans use carefully furiously final multipliers. fl\n6576|Customer#000006576|cWpLaNr2DIuZanI3i|12|22-167-100-5796|8636.08|HOUSEHOLD|ual packages. ironic, special deposits thrash slyl\n6577|Customer#000006577|uEQPgw rPzHldPtHfUqU1r4K5|20|30-983-783-3040|-395.07|HOUSEHOLD|egular packages. furiously ironic fo\n6578|Customer#000006578|   5L06W67,Mw8G|2|12-946-562-5905|1973.65|AUTOMOBILE| silent accounts haggle blithely blithe ideas. carefully special request\n6579|Customer#000006579|tpu9XN6JdsLCO6nnqauXv3|21|31-593-816-5830|4037.08|FURNITURE|about the blithely silent foxes need to sleep ironic, fin\n6580|Customer#000006580|AaTSoiFwZUkdYNegqMCCs|6|16-285-854-7551|1193.35|HOUSEHOLD| sleep requests. slyly even asymptotes sleep; sentiments affix according to the waters! final, final fra\n6581|Customer#000006581|m7AxxAwDpU173tVX8AryB4bRTv|0|10-473-250-2099|1624.68|FURNITURE|l, special requests after the quickly regular requests are fluffily pending id\n6582|Customer#000006582|roC81vpXtYqj6w2ofenW|3|13-543-506-5912|1920.90|BUILDING|st the slyly regular foxes. even \n6583|Customer#000006583|xm9DySSRKsU04Oru|15|25-719-747-8483|8469.57|HOUSEHOLD|thely dependencies: quickly ironic tithes wake fluffily special, special requests. regular, spec\n6584|Customer#000006584|KVHnlcQNk3RAGL9llr|7|17-892-368-3465|6059.87|MACHINERY|lyly special theodolites cajole fluffily even requests. even pinto beans among the iro\n6585|Customer#000006585|FSuVskGB021iRHHJpNyQiBYx2S2eS R1g9|24|34-421-647-5744|8304.87|HOUSEHOLD|ronic packages haggle blithely packages. unusual excuses sleep busily. slyly careful dep\n6586|Customer#000006586|rmyIMRGGnIp84hB9APjbpN3l2J4 lDcogPRb|13|23-442-685-1204|9955.66|HOUSEHOLD|oss the slyly regular requests. furiously even requests believe. quickly ex\n6587|Customer#000006587|U2vx2k5HvCCh MNEYbIRD3BwkR|17|27-495-708-8832|1888.45|MACHINERY|the ironic, unusual accounts \n6588|Customer#000006588|q4ECgmz0iqJlZeKE0U|1|11-899-895-2340|2153.86|FURNITURE|ans. even instructions wake at the slyly even packag\n6589|Customer#000006589|OPaay XGwsQ5FPbgRupMO5|2|12-462-422-9223|7820.61|HOUSEHOLD|e ironic pinto beans should boost ironically furiously ironic asymptotes. blithely regular accounts despite the furi\n6590|Customer#000006590|aMW6NjpCUVPZxoLFEQ3V75cZ5eVfmeGc|14|24-797-771-6036|106.60|BUILDING|y final requests are. foxes mold carefully regular depos\n6591|Customer#000006591|5f8amVgHTYIC9LNg,oJ2358|10|20-937-427-5966|7511.62|FURNITURE|lly ironic deposits integrate regular ideas. packages are quickly. fu\n6592|Customer#000006592|ICcURs4cxBOwwDlSm1N0Q3o2gBIhX|21|31-112-360-4445|9860.40|FURNITURE|cuses. furiously bold requests wake above the furiously unusual dinos. ironi\n6593|Customer#000006593|bXigSMqStoMDk4bZGp7sSpBFr,KfbgZzZkU6x|19|29-550-277-9067|6893.52|MACHINERY|ake among the fluffily ironic foxes. dependencies sleep orbits\n6594|Customer#000006594|2cDc15tGdriYteAK75|18|28-438-658-3673|5410.27|HOUSEHOLD|fully ruthless accounts sleep across the slow instructio\n6595|Customer#000006595|uVa27rCZ,a|19|29-292-466-2278|2348.08|FURNITURE|s among the unusual forges integr\n6596|Customer#000006596|v05Csj41kqY8c Z|3|13-153-530-7399|5083.03|AUTOMOBILE|ronic decoys. slyly special packages above the slyly regular deposits cajole past the regular, final platelets. \n6597|Customer#000006597|xan0fBW83D27pugrU|2|12-820-261-1596|2468.03|BUILDING|y unusual packages. blithely express deposits run slyly. si\n6598|Customer#000006598|gbSYPPXD xhYTY|13|23-340-258-3248|8171.34|HOUSEHOLD| blithely final braids; furiously express ideas cajole c\n6599|Customer#000006599|zZJjOj,Fl38qicLtaaRFZmXBPrsOPu6K|0|10-297-776-2902|9848.52|HOUSEHOLD|sly ironic ideas sleep fluffily regular instructions. special, pending pinto b\n6600|Customer#000006600|m3pLs7ZW2DQGLirHs2KrrsVG|3|13-922-914-9708|5090.20|HOUSEHOLD|ut the furiously ironic realms hang furiously final packages. slyly unusual instructions nod furiously furiously i\n6601|Customer#000006601|8CksnofGDe3,eOr yd G1NJvNV8g|3|13-158-904-5841|1921.72|HOUSEHOLD|y against the slyly bold deposits. carefully regular sauternes above the furiously silent deposits\n6602|Customer#000006602|xYS0xzAVCHivnaBFSkuHzezVfozYTop|3|13-468-842-3174|252.68|BUILDING|structions. instructions sleep blith\n6603|Customer#000006603|G0VkNa06eg5whaAH6XUH|17|27-100-185-6322|9705.48|MACHINERY|ronic, idle foxes. bold, pending warhorses are: dependencies use boldly ironic requests. quickl\n6604|Customer#000006604|kmwPS7a1rYHG3d2KI12OKOegpaHNwQitCvRbb|5|15-116-310-7342|-850.55|HOUSEHOLD| use above the final asymptotes. ironic, ironic instructio\n6605|Customer#000006605|bJpsYu5HBIgwd3bPpcfTMme|5|15-797-674-4556|2748.15|BUILDING|o beans are after the packages. even pinto beans boost according to the final excuses. fluffil\n6606|Customer#000006606|veGv5 O g1eNuMCQ8lbO0X0|18|28-322-896-9125|8130.69|FURNITURE|y special asymptotes. even, even requests above the enticing \n6607|Customer#000006607|pm9R99glhFCWZzIEllz428 TqMQQmX|17|27-924-798-8911|8585.44|BUILDING|regular deposits. foxes are blithely carefull\n6608|Customer#000006608|WZYAo7ClTq8j|1|11-275-467-1847|6611.72|FURNITURE|s pinto beans. furiously regular platelets according to the furiously regular hockey players slee\n6609|Customer#000006609|9XLzC3FZ8xeSgZX2PiZ2JTpZP7uW,KsvvqY|15|25-264-475-4079|-773.01|HOUSEHOLD|theodolites nag quickly idly ironic accounts. ironically even foxes are. blithely ironic platelets c\n6610|Customer#000006610| QFFI2olnw1MmJfAqwsm0iw4oNDjU6kITjOa53h|8|18-120-297-4174|7884.20|HOUSEHOLD|ently even packages sleep furiously. carefully even theodolites haggle quickly exc\n6611|Customer#000006611|aQKv,MgJ9FK3NjvADMZ3rhl|3|13-452-843-5081|1801.07|HOUSEHOLD|carefully thin, express requests. carefully re\n6612|Customer#000006612|gnqac7Ybh2kSoqKa3ASVAHvypm|17|27-106-926-4405|7620.71|AUTOMOBILE|ld asymptotes. carefully final requests haggle fluffily regular, regular platelets\n6613|Customer#000006613|zPS5aK 66ca8nD|4|14-453-488-2934|6296.69|AUTOMOBILE|unts. ironic, even instructions cajole blithely a\n6614|Customer#000006614|ICoJpB8v3 QU8AfZ|11|21-180-351-9946|6559.73|MACHINERY|eep blithely slyly pending platelets. ironic, express ideas cajole according to the regular ideas. slyly r\n6615|Customer#000006615|GkKngc3qcaXlUc0oUGBVh,Ah|22|32-821-576-6664|5407.17|HOUSEHOLD|kindle. always regular accounts thrash furiously ab\n6616|Customer#000006616|8ssRl7vqBcxVrQWh xQdt5U1zX34R5ga  Txw,|11|21-804-310-8614|5341.58|HOUSEHOLD| silent packages ought to use\n6617|Customer#000006617|gkz18C,mxfkSot1U zHcc8E|18|28-955-200-3871|3008.53|MACHINERY|ainst the blithely regular deposits boost carefully carefully unusual ideas. quickly u\n6618|Customer#000006618|jUw3FhXzO0qfzz zXxWOhKwfmI3r|11|21-610-354-6624|6411.46|HOUSEHOLD|nal requests affix quickly pending packages. close pint\n6619|Customer#000006619|clyImCIDigUVv1edDwAG34tr7MWfI|20|30-409-954-6902|-549.38|AUTOMOBILE|hely final instructions nag; carefully bold accounts maintain after the even requests. carefully unusual saut\n6620|Customer#000006620|,paCQ5qIt,Ylr,iREwAefW8ys5k|13|23-766-421-3496|4441.83|FURNITURE| ironic accounts doze carefully according to the blithely regular deposits. slyly silent reques\n6621|Customer#000006621|EuR3TpdBnWKm6OMxOW9yAuiT|8|18-801-593-9685|5852.43|MACHINERY|ymptotes sleep quickly regular platelets. furiously ironic fox\n6622|Customer#000006622|fidCV6mKheF|16|26-334-959-1721|8847.45|MACHINERY|wake regularly final instructions. deposits are slyly. furiously pending pinto beans boost. care\n6623|Customer#000006623|Xjw9Hy5h4Pgb3zcUEWm8|24|34-304-460-3284|-872.31|BUILDING|g the slyly final foxes. even, pending requests boost about the requests. r\n6624|Customer#000006624|5VX6OjpQ Bro52jdZej8FlK0OFFtf7R3ESIJPK|1|11-660-742-2374|-7.37|BUILDING|inder blithely? regular platelets about the packages believe instructions. regular fox\n6625|Customer#000006625|R3hdYck8WB  F4No|0|10-367-922-8280|6076.36|HOUSEHOLD|ly regular requests cajole; blithely pending instructions haggle al\n6626|Customer#000006626|ZdfCZEerdNphvEz5|8|18-208-761-1975|-595.84|HOUSEHOLD|have to doze permanent pinto beans. special requests sleep across the furio\n6627|Customer#000006627| lMYWGolCqdPZwftuYfusc7pMOGgLUZdrI|6|16-598-408-8591|4678.15|FURNITURE|ckages. ironic pinto beans use carefully-- carefully eve\n6628|Customer#000006628|xP MMDAoXhUc,s8N|9|19-364-386-8197|6607.71|HOUSEHOLD|s are. furiously express platelets boost. regular, regular accounts cajole. depo\n6629|Customer#000006629|aLIf6koYS8OSSnvRGqhRFqIRnX|6|16-310-265-2013|8761.81|AUTOMOBILE|onic deposits hang slyly enticing packages: slyly regular courts was blithel\n6630|Customer#000006630|mrADj6gHX6kvN8H9hV2TTZD|10|20-993-605-4157|844.42|FURNITURE|across the blithely special excuses. express accounts are about the packages. ironic re\n6631|Customer#000006631|7iOXTPto,B5|12|22-688-971-9796|89.69|BUILDING|the ironic deposits serve slyly slow, regu\n6632|Customer#000006632|WFDE,gGvSeFejvfMg|2|12-979-552-7277|9324.80|MACHINERY|onic theodolites haggle carefully carefully pending excuses! packages boost against the furiously ironic p\n6633|Customer#000006633|MXIkYoyLJbpxhc|4|14-157-941-2531|2911.60|FURNITURE|deposits cajole even instructions! slyly final requests integrate enticingly furiously special\n6634|Customer#000006634|v40njkdojYnRdVGwAW|5|15-916-327-8851|5261.30|BUILDING|lar instructions. accounts haggle permanently according to th\n6635|Customer#000006635|dPKoh,uNPUKABQlR,WDK3pTzCp|24|34-785-680-5961|6618.78|AUTOMOBILE|packages sleep. slyly silent deposits x-ray furiously deposits: bold, e\n6636|Customer#000006636|0yf ,8IEF6Ym5JFeZb1HfyYJG CDxU|24|34-625-369-8340|7912.37|BUILDING|ously ironic ideas integrate furiously ironic instructions. careful\n6637|Customer#000006637|5RhMx2tf5k8u|13|23-455-236-7135|2317.68|BUILDING|e blithely. furiously even dependencies integrate slyly. car\n6638|Customer#000006638|Mm2JAYCCHstTE|16|26-315-337-6748|191.55|FURNITURE|s packages sleep quickly. final ideas boost carefully\n6639|Customer#000006639|auyhpm1qOcoflRfR4S35,7nPTFyM ZG04eGAMb,U|13|23-364-243-5030|5269.15|FURNITURE|urts cajole bold, even requests. furiously ironic exc\n6640|Customer#000006640|LraQsOeV6d|7|17-233-212-8020|6693.51|AUTOMOBILE|ate after the special packages. carefully regular excuses are quickly quickly even decoys. ironic foxes detect bo\n6641|Customer#000006641|gw3LD4q9DzXioZ37chuNxB2|8|18-212-860-9043|9799.19|BUILDING|e furiously according to the theodolites. carefully bold ideas wake fina\n6642|Customer#000006642|LoE,WKpwd79h4TGVAJDgTYwzSwDmBfd|2|12-278-337-5916|1026.55|HOUSEHOLD|s. slyly bold excuses play above the furiously ironic requests. furiousl\n6643|Customer#000006643|Iuv NTvS6dJ2io6Q76FiZeJI|23|33-173-903-7175|3109.73|HOUSEHOLD|are carefully blithe accounts. carefully unusual pi\n6644|Customer#000006644|mf2MRu7b37tyk|21|31-369-661-7739|2257.43|AUTOMOBILE|bout the unusual requests. special, unusual epitaphs haggle furiously final deposits: even requests agains\n6645|Customer#000006645|9wuMUJea1LYIsA0MDzoFxKARmIJvMpI|22|32-339-429-1573|-347.93|BUILDING|according to the permanently special packages. pen\n6646|Customer#000006646|GioIRa7f673rsFRHt2e|9|19-556-383-9646|2206.85|MACHINERY| furiously special packages haggle busily. s\n6647|Customer#000006647|gLBSZ8P6yXMpyGwCirn9HfN,|6|16-349-915-4350|7515.71|AUTOMOBILE|ncies are against the furiously express instructions. deposits above the bold theodolit\n6648|Customer#000006648|0HJ,ghgxYfPYPC eCh6nDUgWLalajrwBRY4p|0|10-142-681-9531|1611.85|AUTOMOBILE|y unusual packages. express ideas sleep furiously along the final frets. furiousl\n6649|Customer#000006649|q7et5s1SAXMlokVdGW3ZYueHV|20|30-484-404-2575|6300.33|BUILDING|nts. packages integrate blithely around t\n6650|Customer#000006650|NxtKIJDfOaBn|16|26-568-235-1339|1611.97|FURNITURE|pite the ironic packages. silent requests across the regular theodolites solve care\n6651|Customer#000006651|cAtLrZCfDjtH5vIpaqg,qBs6J|9|19-396-649-7221|8251.81|AUTOMOBILE|al, final pearls wake furiously regular grouches. packages wake bl\n6652|Customer#000006652|4mz9d8cQw7Hh,KZYOWhHrH5NpLkhQL|14|24-741-346-4893|9296.36|FURNITURE| above the regular, unusual dependencies. final pinto beans dazzle fluffily. pending\n6653|Customer#000006653|4Q4ARPm8n2f|10|20-532-650-2293|8927.69|MACHINERY|riously final packages integrate. ironic, thin accounts cajole among the carefully pending\n6654|Customer#000006654|AHYF0lz1LiG,wC1WMH9L9pCe3PdUaO4Q|7|17-933-462-3572|5789.45|MACHINERY|s. blithely regular accounts engage slowly carefully final se\n6655|Customer#000006655|FgGQl7KxO2rmmwE0rndJ|24|34-277-845-9539|6372.81|AUTOMOBILE|ermanently regular packages do are carefully express requests. pending accounts sleep. pac\n6656|Customer#000006656|jgLvAdS6UQcyaUCSb|17|27-416-436-5518|9824.64|FURNITURE|eas serve furiously pending theodolites. f\n6657|Customer#000006657|fDo1gqlFFrkkqjwSb9 9RD7DNbuPI59zt|3|13-538-227-3972|-146.22|BUILDING|ly. blithely stealthy asymptotes alongside of the fu\n6658|Customer#000006658|qOYY,NL6MESHOBu1r8jx|21|31-302-918-2979|4384.07|MACHINERY|riously blithe accounts cajole slyly ideas\n6659|Customer#000006659|WKPkuTEjDJc|10|20-393-281-4388|3053.98|BUILDING|around the blithely express dependencies. careful\n6660|Customer#000006660|Mhf8lV21 BapxeXn9lz9k7b6tBd|23|33-164-539-3410|188.29|BUILDING|s. quickly close requests are quickly. asymptotes are against the carefully slow requests. pending foxes caj\n6661|Customer#000006661|bB6EP7Tf3mVskZJ7tLHKQSGDifygpVm2|16|26-168-633-7265|5529.99|AUTOMOBILE|t requests nag. blithely regular courts wake quickly. thinly regular deposits according \n6662|Customer#000006662|oZ6a dkzIqrldlL|10|20-378-737-1289|1621.83|AUTOMOBILE|tes use pending, ironic dependencies. even, ironic dependencies boost according to the deposits. regular\n6663|Customer#000006663|tFHYxL YrhKpX|13|23-250-528-2581|9077.11|AUTOMOBILE|ckages after the pending, regular dolphins use quickly \n6664|Customer#000006664|580X3yq552pkRg5sQwDbltI6XxFJb6c6rb|24|34-426-291-1081|9108.44|BUILDING|unusual dependencies sleep quickly accounts. ironic ideas sleep along the carefully bold accounts: furiousl\n6665|Customer#000006665|,Y,EsUyItpokyz9XVi9jf3L7JuOwrQjNE3c1Mlij|0|10-163-117-8909|7628.89|FURNITURE|ickly regular theodolites use blithely among t\n6666|Customer#000006666|9zJqKbGbRjPS|18|28-557-833-2670|792.26|AUTOMOBILE|lithely regular requests after t\n6667|Customer#000006667|SXoMfAHkfO4b44Yr1Qz|19|29-738-728-6617|3385.51|MACHINERY|ual accounts. final excuses cajole car\n6668|Customer#000006668|t,qsLMTcPSMSc I3,7LYW W0EwfqtOu,pmLc,|5|15-364-427-1235|6319.59|FURNITURE|unts boost. final deposits serve furiously according to the carefully regular accounts. b\n6669|Customer#000006669|lT6gKsLMZJaBBXCtnGWroOoUkdbqwFyjMU Q|9|19-723-887-5480|6311.20|AUTOMOBILE|carefully even instructions haggle furiously. c\n6670|Customer#000006670|naiDNB2uULTBT,321CFY9HYG0jFelpYg|16|26-694-136-5425|8297.83|HOUSEHOLD|gular pinto beans solve quickly carefully bold accounts. express pinto beans promise slyly acr\n6671|Customer#000006671|WaqMrlZBfcDiT3n5KvOWt14jgw1m5ZARzqI85fY|18|28-145-184-9679|1603.96|MACHINERY|ans run fluffily blithely unusual dependencies. bold, ironic packages above the quickly pending foxes believe caref\n6672|Customer#000006672|TAk1jzQy60fSpBRSLShvpTZae1797Bdve|10|20-897-841-4188|5700.11|AUTOMOBILE|unusual sheaves haggle quickly according to the carefully final som\n6673|Customer#000006673|heXKQ2V3L0uxVhdWxCvr42|23|33-166-738-4873|751.90|MACHINERY|packages sleep fluffily final grouches? fur\n6674|Customer#000006674|WfEiHyBjekCdDYT2Gbb0infBTvADc27M9R3 BWZ|9|19-629-336-3947|7179.20|BUILDING|y even deposits. accounts cajole furiously about th\n6675|Customer#000006675|HAnz4SbD7 1s,9fqBkN8A4L,m8UZ|5|15-317-619-9609|-226.28|MACHINERY|ess accounts. even packages above the quickly regular requests mold a\n6676|Customer#000006676|VtWsbzZD4qe8Z T02uSPbSuKiz|10|20-569-115-2865|816.03|FURNITURE|he ironic, bold courts are ac\n6677|Customer#000006677|Wh55,5rAIfHnBZbN|0|10-283-641-1486|7035.29|BUILDING|ns cajole furiously beneath the carefully ironic waters. regular requests are according to the furi\n6678|Customer#000006678|QDpY,eRLnl5,HsjFuCEmufwZadvV4|3|13-605-424-6382|1201.45|MACHINERY|warthogs. blithely ironic asymptotes \n6679|Customer#000006679|VqLS2XSCc0GFgWN5 Ol|19|29-169-326-9045|5642.93|MACHINERY|, even deposits use fluffily regular pi\n6680|Customer#000006680|8TEDCB7fdAUhgYRUU7ZfV1Ld3mB00gDQhtNB,oS|17|27-962-923-6320|7673.78|BUILDING|s cajole. fluffily express ideas wake furiously about the ironic\n6681|Customer#000006681|th39LRNpVorKObh7TB|6|16-926-344-3556|7321.37|AUTOMOBILE|egrate furiously. slyly ironi\n6682|Customer#000006682|47B7Wsx8E7991Y2|19|29-904-610-3132|2230.59|AUTOMOBILE|bout the boldly final requests. slyly special ideas h\n6683|Customer#000006683|snrDJsmCFVQ4O3dveQpw5JIDvmtsZXdRtzNmP4O,|10|20-812-694-7415|-549.56|HOUSEHOLD|gular accounts. fluffily express \n6684|Customer#000006684|qdqKwrzOb2|5|15-109-442-3321|1412.15|HOUSEHOLD|ily express excuses wake blithely. carefully bold packages sleep above the even, regular pinto beans.\n6685|Customer#000006685|BkWlHQusdN1kzNDHvqnOV1SJ9|17|27-406-584-3296|4996.19|BUILDING|ly regular foxes boost blithely bold foxes. e\n6686|Customer#000006686|DvvWufVBbpdB3nSVazM7|4|14-799-782-6111|5959.97|HOUSEHOLD|y unusual deposits play final requests. ironi\n6687|Customer#000006687|Q0K48 G0mj|17|27-869-237-1381|1157.82|FURNITURE|ress asymptotes. quickly regular deposits haggle. final, close packages snooze fluffily according\n6688|Customer#000006688|h61o1Amg8a2wnZYMAqd5gJM16PPB|15|25-369-725-3205|9401.81|AUTOMOBILE|fully regular platelets. ironic ideas hagg\n6689|Customer#000006689|x5j,b7K9Irw9KnJmeCur|23|33-787-189-8082|-652.17|MACHINERY|blithely regular packages across the packages poach across the bold platelets. even accounts nag furiously against t\n6690|Customer#000006690|B1nB5mFxDsSJQvm5lxn3nkyMl|16|26-671-890-1848|3868.69|FURNITURE|eas try to are fluffily. pending, t\n6691|Customer#000006691|NvkXKfvzF9oErd6MzanMySOlVPNUCQ7tcV|5|15-503-903-6718|8999.87|FURNITURE|luffily. unusual dinos doubt slyly. accounts sleep fluffily after \n6692|Customer#000006692|HkPGTwsyUV|9|19-145-218-4626|8817.45|AUTOMOBILE|boost permanently final, regular deposits. blithe, ironic requests sleep furiously express dep\n6693|Customer#000006693|Bd7U3RV0lbRer WQY|24|34-882-944-4437|925.49|HOUSEHOLD|silent deposits. special foxes nag sometimes permanent, bold deposits. carefully special pinto beans sleep fluffily\n6694|Customer#000006694|XyOYqBJqGspMZSdqQq5DvaX9CD|4|14-959-393-1619|9588.19|HOUSEHOLD| against the slyly ironic deposits cajole blithely even pinto beans. final\n6695|Customer#000006695|XGxqrdbm4jLBmrqTRmd5dLwIR|18|28-628-769-5605|7779.06|BUILDING|y even dolphins. requests wake. fluffily express asymptotes sleep. slyly final acc\n6696|Customer#000006696|xhoBDOYsLR89|21|31-975-638-1125|5667.73|MACHINERY|es. slyly regular accounts integrate! fluffily ruthless accounts wake furiously! quiet accounts\n6697|Customer#000006697|0NTbi10hHKSxo|24|34-307-871-2967|6632.75|HOUSEHOLD|re carefully quickly final asymptotes. blithely pending packages cajole quickly along th\n6698|Customer#000006698|0QyCW1acGdoAo59FdWV 3pZ|19|29-862-853-4688|620.84|MACHINERY|ges wake blithely. quickly pending braids sleep furiously: carefully regular pinto beans mold after th\n6699|Customer#000006699|jGDulmO0cw9lFBN1jlR64OLiBqCJc|2|12-708-798-7379|4317.54|BUILDING|unts-- carefully regular instructions maintain s\n6700|Customer#000006700|KUpYErT7tXcOM04gpSlDW4566SCbvBT dA6l|8|18-304-498-5307|6932.01|FURNITURE|structions. blithely final ideas affix. blithely unusual requests haggle slyly fluffily even instructions\n6701|Customer#000006701|CVKLM V7ST0Nx,jr0e0gHcduEj5|9|19-659-528-9243|3257.42|AUTOMOBILE|lly final foxes. ironic requests boost carefully du\n6702|Customer#000006702|Wi DrmUzjVKPCSBNG6Wok7io9QxVX7kN7Jd|7|17-216-230-3435|9392.14|AUTOMOBILE|t slyly according to the furiously fluffy theodolites. carefully regular requests are carefully int\n6703|Customer#000006703|03PTXkGYGGxCThBmmgGy|21|31-804-154-5053|1836.91|BUILDING|. unusual asymptotes cajole carefully against the ironic packages. \n6704|Customer#000006704|B1B1Ms3HDpnvU8cDBoMP3T4PkW,mHSWd|19|29-206-376-9599|4862.96|MACHINERY|rmanently even ideas haggle carefully. carefully unusual courts according to the c\n6705|Customer#000006705|aDX0WTflMQRyU2JMFFlW|3|13-434-897-7312|-159.03|BUILDING| foxes. furiously silent pinto beans integrate slyly across the grouches. slyly special pinto beans in\n6706|Customer#000006706|TBcc48ZL2vZv7afiGoSEhjbM|3|13-134-428-9567|9100.37|AUTOMOBILE| special, even requests are fluffily-- regular accounts a\n6707|Customer#000006707|o6Nm3V8 Zj2tt4j3sMiTJ4|6|16-466-364-3185|3977.38|FURNITURE|uriously against the regular, ironic requests. regular packages wake never. quietly pending instructions believe. \n6708|Customer#000006708|mUhDV6NCOCJghkAjEX0Jo|12|22-281-780-5651|9900.11|BUILDING|ly. ironic accounts wake carefully at the final pinto beans. slyly regular requests slee\n6709|Customer#000006709|BXNfIHT5cfG6DtLlBKePFXUQbBeB4|8|18-207-447-8319|3199.11|BUILDING|ronic, even requests. slyly unusual realms sleep carefully. ironic asymptote\n6710|Customer#000006710|CeyxEnNCzAuXmAId1vHI4kN5YuwNvV0rH8NhYz99|12|22-412-672-8270|8494.05|HOUSEHOLD|usly even requests. accounts boost after the bold deposits. slyl\n6711|Customer#000006711|z9trQmIO3Y5z60O5ozSv877GQILvo|13|23-886-250-5460|-361.61|AUTOMOBILE|ounts! thinly even theodolites cajole furiousl\n6712|Customer#000006712|T QGDjZA2gcd92poV ,1rQrRu9ZB7EzZ|17|27-884-283-7355|3268.82|FURNITURE|ke blithely bold deposits. final requests sleep furiously? blithely specia\n6713|Customer#000006713|ynNA7HFYdk0KjYuVxo9R9NK8qw8lXJxDFhA|9|19-945-198-9475|3006.93|FURNITURE|gular asymptotes nag. sometimes final accounts integrate blithely blithely ironic ideas. caref\n6714|Customer#000006714|m8zfcfvyi7w PulZPp,g|21|31-407-424-6606|1494.63|MACHINERY| blithely over the furiously regular \n6715|Customer#000006715|wEEcGy ed7V nIyY|8|18-303-744-7177|5244.28|BUILDING|ep slyly-- fluffily regular pinto beans use silent accounts: sometimes regular foxes are along the regular, regular \n6716|Customer#000006716|qBJ6Dx1ASZOZLrQ7FTreiGRWX|4|14-339-999-9145|6593.08|MACHINERY|ons. blithely bold instructions instead of the blithely even dependencies haggle according t\n6717|Customer#000006717|A3TpI60MfBQNedRV 3DcYhf3GUWL|23|33-731-150-6538|4844.87|HOUSEHOLD|ependencies wake except the reques\n6718|Customer#000006718|8m61NcNrEZzFo,NmKaBzQXjeX Va|12|22-820-610-1027|3658.95|BUILDING|ely. notornis try to nag. blithely pending package\n6719|Customer#000006719|,SJoHSlIYiQZ3ebJw SpwZ8lg|7|17-221-621-5826|3965.07|BUILDING|ronic warthogs after the fluff\n6720|Customer#000006720|luGQxDrBGnnftUVgjF|6|16-457-628-5807|843.36|FURNITURE|odolites cajole. ironic packages haggle regular instructions. slyly special \n6721|Customer#000006721|OkHnnK98UeBILbX2bjAPwvOdKMBidiW|4|14-701-354-8135|4277.74|AUTOMOBILE|l requests are. furiously quick dependencies cajole. deposits cajole blithely after th\n6722|Customer#000006722|1jnPUXu2iBwB7|1|11-451-400-5785|8919.68|MACHINERY|ronic asymptotes use. furiously pending packages boost furiously-- slyly regular deposits wake\n6723|Customer#000006723|gAfgW13,GB|20|30-582-567-3761|3070.48|FURNITURE|sts. tithes wake quickly. carefully regular theodolites integrate. carefully final asymptotes \n6724|Customer#000006724|bcpvOK,8gUO1|12|22-598-908-4189|2471.21|AUTOMOBILE|s. slyly final pains wake carefully. express, ironic deposits among the furiously final dinos ar\n6725|Customer#000006725|if8lplIxBWNs6u2fgJKJLKDrRlfhEjwQcJVooOH|7|17-685-688-6979|6523.60|FURNITURE|n accounts poach silently after the regular\n6726|Customer#000006726|hssVbd6,2x8YN7nJDem092|6|16-514-909-5495|3108.77|MACHINERY|es against the carefully regular courts are quickly furiously even th\n6727|Customer#000006727|APG0BfGWOYnDkXgbcKdhJXzzJgLtQXYKCoXMPxJA|23|33-373-151-6436|7993.12|BUILDING|riously close foxes. fluffily final accounts haggle quickly across the accounts. bold theodolites engage furiously a\n6728|Customer#000006728|v9cVptqbc0tKb9ZT q|8|18-216-645-1385|6449.93|MACHINERY|about the bravely even ideas are carefully ironic accounts. thinly even dependencies haggle\n6729|Customer#000006729| NrrDioTuXtkJCh|13|23-575-217-4725|8051.37|AUTOMOBILE| furiously. quickly regular packages above the reg\n6730|Customer#000006730|EmtyBtMxazeTZFx1zqBuNju5VmzsqOi930BE|11|21-223-976-8367|6413.26|MACHINERY| carefully final accounts. closely final deposits integrate carefully according to the dogged, regular ideas; platel\n6731|Customer#000006731|HpEXXgDmsqIPyVb FXeWbadti|2|12-283-284-3677|7639.55|MACHINERY| foxes. carefully final asymptotes print excuses. furiously ironic waters engage. c\n6732|Customer#000006732|sHH4w G8QMTtCVTjbkiEHTvL8bRr5Tu|16|26-779-843-5056|4018.93|FURNITURE|ions haggle quickly against the slyly regular accounts-- bli\n6733|Customer#000006733|Kik6R2t1WP|11|21-569-230-8251|4219.13|AUTOMOBILE| boldly across the carefully ironic dugouts; slyly bold pinto beans haggle. \n6734|Customer#000006734|yRF8my1jIZM1QuEGXxSuXT0P83w ljVko |14|24-157-448-3086|2971.38|FURNITURE|ly final deposits. excuses along the carefully special packages promise regular theodolites. ca\n6735|Customer#000006735|eG5Pt6wlZpFUt0140cHxYox3roKuQU,V|9|19-117-148-6129|8904.94|HOUSEHOLD|onic, express packages wake carefully final asy\n6736|Customer#000006736|TwL2M9L6iblvaG,93Efw2HP Glnm8jsH4EQP|17|27-421-117-6858|8363.54|AUTOMOBILE|inal dependencies nag. regular theodolites sleep. blithely unusual foxes boost slyly. final packages nag slyl\n6737|Customer#000006737|TnIV3SSmxJFDBYUexZHw2w2m3vIAFgv38lwDY7Rb|4|14-372-941-6618|6015.13|FURNITURE|iously ironic theodolites nag slyly according to the even requests! slow, regular platelets doubt. \n6738|Customer#000006738|eAylAy5pHWBnUK6q3v2cXNODweF4|3|13-699-399-9154|4394.53|BUILDING|ending accounts. slyly special pin\n6739|Customer#000006739|3lsA2Q8pNR|5|15-493-613-7300|2838.35|BUILDING|uests nag furiously according to the quickly even dinos. ironic, bold requests\n6740|Customer#000006740|VZIi3wMuEDjnmgAq4YwDGnf5BK e7Fs2|11|21-751-155-2705|6098.86|MACHINERY|lphins affix quickly alongside of the deposits. slyly pending pearls wake regular, regu\n6741|Customer#000006741|ogMwwJa0CY9MZsdv7nCQi5HCOAwuE|7|17-567-242-2914|3514.90|BUILDING| are over the foxes. furiously even requests after the closely r\n6742|Customer#000006742|u5lVPzNeS1z2TcfehzgZFHXtHyxNJHU|1|11-643-477-4053|2834.80|BUILDING|bold deposits wake. special deposits about the regular excuses boost furiously furiously final packages. slyly r\n6743|Customer#000006743|uXzHSYlqjnoKSFFTrUjwPa4g9oIMRp4D,S,cNM|20|30-195-288-9509|1464.96|FURNITURE| unusual accounts wake slyly about the carefully regular packages: carefully pending dol\n6744|Customer#000006744|PGmBpCi8fCF9caRqw|9|19-551-917-8841|1605.91|MACHINERY|ly. quickly special accounts dazzle around the ideas. blithely regular requests sleep quickly carefully regular \n6745|Customer#000006745|F4AQlOnBqEtwLi85Fm0RzN3ZmaMdl3|18|28-602-954-3388|5454.54|FURNITURE| final pinto beans sleep pending, ironic deposits. busi\n6746|Customer#000006746|M0 KJuw4qc9UBliw9 oD42VXakTKG0,6|18|28-204-291-9356|9415.95|MACHINERY|ges. slyly ironic patterns haggle slowly carefully regular requests. quickly regular t\n6747|Customer#000006747|3ntn2pmdvucSKFlb|3|13-243-582-8819|5970.51|AUTOMOBILE|round the slyly bold deposits. slyly unusual dolphins boost bli\n6748|Customer#000006748|a8b2paF9T94UrLTiSzHSE38ZmxHL|17|27-580-967-4556|2410.88|AUTOMOBILE| furiously ironic deposits haggle across the slyly silent instructions. quietly ironic \n6749|Customer#000006749|sU3,BsMtoVz xNJJX1emkZJxZbOIRc|0|10-774-652-4046|4693.71|AUTOMOBILE|ts. unusual accounts are slyly furiously final accounts! ironic, even pains cajole furiously. \n6750|Customer#000006750|V7UkwofaAeXZ7 0rXuHekMbN3mXZLK7koxDO66Tl|4|14-457-948-4761|4227.49|HOUSEHOLD|ding dependencies. carefully regular accounts cajole carefully about the ironic deposits. package\n6751|Customer#000006751|Tdxt4GKZYz|7|17-895-936-3672|365.42|FURNITURE|ly for the fluffily silent foxes? slow deposits are. packages doze carefully. quickl\n6752|Customer#000006752|80HtfjhjeYU|17|27-487-194-4578|1487.49|AUTOMOBILE|regular accounts. pinto beans sleep express foxes. special instructions wake. quickl\n6753|Customer#000006753|yNkYKPtxuhgzqBBm7s6|8|18-860-208-5739|6195.82|HOUSEHOLD|s. bold, ironic packages wake c\n6754|Customer#000006754|B6i9QUe1MogaHVLxdQy4QnH3nkb UaY0|7|17-897-648-1827|5143.81|BUILDING|ar ideas doze furiously final\n6755|Customer#000006755|aJU5CHd5UZzrd80j1|6|16-237-199-3241|964.67|AUTOMOBILE|nic asymptotes. blithely ironic foxes are. blithely ev\n6756|Customer#000006756|sRflkvpvQnc6q8wXS|7|17-105-972-5722|8127.58|HOUSEHOLD|ing accounts. unusual theodolites hag\n6757|Customer#000006757|Ot2eNxssE43x0Ivhlzy 2|13|23-725-154-2356|227.89|BUILDING|breach blithely; furiously regular deposits according to the quickly regular packages nag slyly fluffily regular p\n6758|Customer#000006758|YhRgjpezuIfxnPM3docKnH9Dr6Zs9dlMPmI r5X|18|28-730-164-7975|15.13|HOUSEHOLD|dolites hang carefully. slyly special accounts nod slyly express theodolites. pending depths according to the regula\n6759|Customer#000006759|CyomHt7D6FLzXgN5Rff0fo0a |19|29-276-479-4436|-773.12|FURNITURE|ut the slowly final accounts sleep blithely slyly special foxes. quickly final ac\n6760|Customer#000006760|ZXjHellHGmKAh|18|28-609-664-1095|6035.85|FURNITURE|. furiously unusual packages sleep quickly. accounts nod sly\n6761|Customer#000006761|v1sTMXm97sEpzFo|1|11-894-413-9156|521.65|BUILDING|tructions cajole carefully against the ironic deposits. blithely final asymptotes wake furiously ironic ac\n6762|Customer#000006762|Vha,VM8y w5TD0q7|18|28-388-163-7393|9311.76|BUILDING|against the final requests. p\n6763|Customer#000006763|laxf,Ybtd4d2FGE9RCG|18|28-654-592-3290|3895.04|AUTOMOBILE|ts. busy pinto beans cajole carefully since the slow accounts. final pinto beans cajo\n6764|Customer#000006764|Ehu6TNlFTGdkY|16|26-720-544-9325|921.28|AUTOMOBILE| forges snooze. carefully pending warhorses cajole carefully upon the carefully regular packages. thinly unusu\n6765|Customer#000006765|K8S00oGkfyhG7,A08NTyZTfOItXThIZ|14|24-562-459-8122|4816.14|AUTOMOBILE| regular instructions haggle slyly accor\n6766|Customer#000006766|KbP0 fFP7iJCWl4E|4|14-369-503-4420|4814.57|FURNITURE|express, final accounts. furiously u\n6767|Customer#000006767|yEB,5rAwZ1Vi,u|21|31-217-839-6340|528.35|BUILDING| ironic requests cajole quickly slyly even foxes. fluffily pending accounts are according to the regul\n6768|Customer#000006768|h Kgr0xrW9MkORlqUgwFsGgQZG3,Jks|9|19-647-976-4923|979.30|AUTOMOBILE|furiously idle packages are slyly fluffily quiet dinos. quickly even instructions about the regular reque\n6769|Customer#000006769|v5TrRZTAAiD4i1eyVTLNA|20|30-186-784-8328|1029.38|AUTOMOBILE| slyly regular packages nag carefully. furiously bold theodolites along the\n6770|Customer#000006770|fhS8YZLcFyBIeZSp2|12|22-300-803-6439|6756.19|BUILDING| express ideas sleep slyly ironic dolphins. u\n6771|Customer#000006771|MMWc6i48BJhKrgu9ko Io|24|34-589-527-3110|7287.84|BUILDING|iously alongside of the theodolites! even realms above the furiously special orbits are slyly final ideas. car\n6772|Customer#000006772|ggL8d6JSSQUkE9zg6F3|10|20-387-573-8593|-238.33|MACHINERY|eas: carefully regular packages about the furiously silent pains\n6773|Customer#000006773|WO 4bouYuu |7|17-627-272-8854|7232.34|BUILDING|tes. furiously bold pains wake care\n6774|Customer#000006774|tufe2Xi42Fdq6R4hf7drAFRCg3Rz4i|2|12-800-430-6064|1958.28|HOUSEHOLD|ounts. slyly even theodolites sleep slyly accounts. ironic foxes sleep carefully. blithely pend\n6775|Customer#000006775|bzDrDnIGQLCgiExwO6VwqlW|15|25-386-188-6886|9781.70|BUILDING|old, ironic pinto beans wake fluffily. even, regu\n6776|Customer#000006776|JXoJqh4JpcdvugREShRwNHCee4ltrxrb3OM1,E7l|18|28-994-492-7101|4054.12|BUILDING|hely among the ideas. slyly unu\n6777|Customer#000006777|uT2JCbpTRH9Inj0wgbRuv,|16|26-447-185-5798|1597.51|AUTOMOBILE|cajole. slyly express asymptotes sleep above the final, bold realms. slyly special realms boost on the quickly re\n6778|Customer#000006778|ACpbkEbCPYy|4|14-172-893-4202|7333.47|MACHINERY|r platelets affix. carefully ironic accounts arou\n6779|Customer#000006779|UeGNbDKhSDW1MkcE,GnxRAiyjHHe0itTSj|18|28-974-892-3856|7630.58|MACHINERY|xcuses might sleep slyly silent, final deposits. regular pinto beans integra\n6780|Customer#000006780|R20cDLZCvC,XeXxr3JVgS63kH9IYW41ql9|4|14-601-747-4629|7181.83|MACHINERY|blithely even escapades. even deposits are carefully pending accou\n6781|Customer#000006781|hjgOEQCNYiCKQTwECBEJ6pMsKqhNJH1oRj|21|31-556-362-4055|-21.79|AUTOMOBILE|packages nag along the quickly express packages. ironic deposits are along the\n6782|Customer#000006782|QfjRd7YtTJRio2K70XUp7w,WY3a9xlo,|18|28-152-315-2630|5711.06|MACHINERY|ss realms. blithely even ideas haggle furiously afte\n6783|Customer#000006783|VcFpiOQxpgr2Q5w0,d|4|14-418-290-9278|8724.64|MACHINERY|. blithely regular ideas haggle fluffily against the ironic requests. final deposits sleep. even, regu\n6784|Customer#000006784|t7bVQGTfY,PwEmIshCKmrWiNMj|10|20-926-379-4893|3026.77|FURNITURE|ful pinto beans nag final, even accounts. requests \n6785|Customer#000006785|Zeb0QWx561VbdmAnNJwFX|5|15-334-822-1897|4849.38|FURNITURE|equests. blithely ironic dependencies about the slyly ironic accounts wake furiously iron\n6786|Customer#000006786|y JAZymUDAm19ImpwXrDBhrJ2tkCXImzEU84NXe|14|24-639-124-4027|2564.13|MACHINERY| forges: fluffily bold packages boost about the sometimes final excuses. ironic deposit\n6787|Customer#000006787|x3R8xwciGpAldSQtfrfQjKPVA5MK|4|14-221-614-7132|6276.35|BUILDING|ularly silent ideas along the regular, even asymptotes boost fluffily bold theodolites. blithely express packages \n6788|Customer#000006788|tkzbRySfDjHBZuJU8xa9XXx4EeZ6L EmX|12|22-769-485-6232|5623.43|MACHINERY|ackages. carefully express platelets haggle blithely\n6789|Customer#000006789|wQUHbVSc8YanGHPCDbK0,njoByEX7ThcX7|13|23-254-104-3764|-913.53|MACHINERY|yly: carefully ironic asymptotes are furiously; pending do\n6790|Customer#000006790|DCD1tDMXhoUIaKhQPnCUVUKxiLdcGsNK|18|28-476-389-8594|222.98|AUTOMOBILE|rave deposits. regular, even instructions us\n6791|Customer#000006791|w7qvbNTA3AUnviiYrUHQ3rrvxg|24|34-796-717-7454|5488.16|BUILDING|g the blithely express packages. u\n6792|Customer#000006792|1eyzmigbKdS,uPvd,gTfV82Q0s|7|17-399-754-8075|2503.03|AUTOMOBILE|thely ironic pinto beans. accoun\n6793|Customer#000006793|6iyy1xAWokEzS7vzwKebMn8uc9rnD|14|24-645-815-1178|2415.53|AUTOMOBILE|ies sleep slyly along the blithely bold deposits. sly\n6794|Customer#000006794|eW4D8D nAkkGhhx|21|31-846-103-1877|5798.75|MACHINERY| integrate about the quickly dogged requests. furiously ironic pinto beans haggle above the never final \n6795|Customer#000006795|uHQSwrVKQCUmmUjgTjuUo oR65yPIOzNuZ5j|11|21-520-971-6013|8574.87|MACHINERY|counts. pending packages haggle quickly alongside of the pending requests. ironic fo\n6796|Customer#000006796|ry4xotfFJkV|21|31-204-934-2930|4956.27|FURNITURE|beans! furiously even deposits doubt fluffily carefully express pinto beans. furiously pending\n6797|Customer#000006797|CbAgm9paksZYcQNM|2|12-443-241-4031|6051.25|FURNITURE|s ideas are carefully across the fur\n6798|Customer#000006798|DRAvKxwGdh8qBsv6DlFfvsilS7,QuSOQfO4D|21|31-784-264-5102|6312.50|MACHINERY|kly ironic pinto beans. fluffily final escapades beyond the reg\n6799|Customer#000006799|OmuxHQ,MFaA6IKDRJpI7rOq0 2|19|29-861-675-1912|8266.46|MACHINERY|tes. slyly permanent foxes sleep against the carefully regular deposits. ironic, ev\n6800|Customer#000006800|dyQqJ sBw9RZiggYPODCddm2|24|34-746-736-6270|2438.35|HOUSEHOLD|al accounts sleep carefully accord\n6801|Customer#000006801|h4zMf8BMKsgOf964TpkBtFenTIiHb|7|17-563-914-8922|4350.72|AUTOMOBILE| the furiously express accounts. even foxes sleep fluffily ironic ideas?\n6802|Customer#000006802|XwZM0 CSn4C Pe|24|34-621-826-1804|3589.51|MACHINERY|nstructions wake quickly along the bold accounts. ironically ironic tithes sleep blithely unusual\n6803|Customer#000006803|6xmvFf,9ifUkUXDtls|6|16-508-741-8182|2880.15|MACHINERY|ully slow asymptotes. special pinto beans use after the final ideas. furiously even ideas hinder. qui\n6804|Customer#000006804|y3KkEdC7h4oc6BGM8r xsYpi V7ivtz9|16|26-988-381-6875|4647.66|MACHINERY|ccounts sleep slyly among the regular packages. quickly final ideas sleep furiousl\n6805|Customer#000006805|kTi3eJA0j3wmK,PDjWGed8tzM|24|34-956-659-4290|147.84|MACHINERY|eep quickly carefully even courts.\n6806|Customer#000006806|63GYGgLROgXoSKxwRPVwKdjdTFx|3|13-535-116-9961|7653.27|HOUSEHOLD|press theodolites. blithely pending accounts according to the quickly regular accounts nag alongside of the unusua\n6807|Customer#000006807|zfbTyBPugePY1ea3MbBFWuXrhsT|0|10-318-291-1534|3571.22|BUILDING| blithely ironic deposits wake slyly about the fluffily pending dependencies-- bold, unusual theodolites above the \n6808|Customer#000006808|roBxgLQjBCcu7 Yls8BnCT3Oum241ON43b|7|17-840-939-1028|3471.22|MACHINERY|gle idly. ironic instructions sleep carefully accounts. bold, even sentiments haggle. furiously\n6809|Customer#000006809|D2OPM0RO7wjQB,y5lQJIhGKRAK|19|29-455-199-9534|5772.30|MACHINERY|efully according to the fluffily silent packages. carefully silent packages use furiously above the carefully even\n6810|Customer#000006810|Vav VVRBQ3WYqKNtNTi39x7,kXBcfblF3|13|23-459-864-2903|4116.60|AUTOMOBILE|instructions cajole permanently furiously bold grouc\n6811|Customer#000006811|OcI9j59VXpHJy8akRgo9QazL2FBJzr|2|12-527-163-1718|6619.05|FURNITURE|ans besides the carefully ironic pinto beans sleep slyly fluffily pending \n6812|Customer#000006812|AhoTwIbKJQn1FD4f59Og02|12|22-522-190-4711|1433.83|HOUSEHOLD| slyly ironic instructions! pending\n6813|Customer#000006813|7sU37tY2xghEmgmrmGFw|6|16-378-417-2821|6328.64|FURNITURE|ges. pinto beans are fluffily. blithely\n6814|Customer#000006814|xKxM4j61kF6WrHJSYTo|0|10-143-621-7307|4498.28|AUTOMOBILE|ructions. carefully bold Tiresias haggle furiousl\n6815|Customer#000006815|SwpQn8U2,7FIzgYG|23|33-553-747-6097|8623.37|BUILDING|dolites sleep. regular theodolites after the express ideas are a\n6816|Customer#000006816|l9iMew1ckL2nrx5YiUnd0Bs7Z96,A|23|33-435-484-3984|5981.91|HOUSEHOLD|to beans are alongside of the final\n6817|Customer#000006817|0GuFB2pIst2i5ku761SGYJF0YKbemV3oGBxuY|11|21-463-437-2941|6900.32|BUILDING| beans. carefully final theodolites haggle furiously unusual foxes. pin\n6818|Customer#000006818|NsWBjqO16kujv3WRtKhhLH28|18|28-865-256-2071|9642.22|HOUSEHOLD|pecial packages sleep carefully furiously regular deposits. even, regular pinto beans cajole slyly about the blith\n6819|Customer#000006819|BxmHawhcf5E|19|29-909-925-3216|2833.96|HOUSEHOLD|ts above the regular theodolites cajole at the pending, bold requests! slyly pending asymptotes would haggle. ir\n6820|Customer#000006820|i1RJGpP9HqI4s6151jR1z8ZWpXdOU|10|20-589-342-9128|9377.01|AUTOMOBILE|ironic requests against the regular warthogs nag across the fluffily regular deposits: \n6821|Customer#000006821|GpFN4sJSskru0JEsjNZLg,dcTKNsIgDO|14|24-956-317-4494|431.91|FURNITURE|l platelets. fluffily regular accounts across the silent pinto beans p\n6822|Customer#000006822|bo tbD14X1LH254lRO|18|28-422-199-2677|8027.05|FURNITURE|sly regular ideas wake. fluffily do\n6823|Customer#000006823|NxR5mcTtNqZad4WGlWudh PLP1itX0Y,xI4f|5|15-158-466-1756|4742.19|MACHINERY|onic deposits. carefully brave decoys along the blithely ironic asymptotes detect c\n6824|Customer#000006824|ots Xj,zI6Lt,5CSMqtm0aoz7UeBs|14|24-918-571-1016|4911.60|HOUSEHOLD|encies cajole at the fluffily even pinto beans. express ep\n6825|Customer#000006825|iCyuTflSjsWcDC|12|22-228-819-1874|3300.02|AUTOMOBILE|accounts. furiously unusual ideas are daringly alongside of the carefully unusu\n6826|Customer#000006826|i6Gp3F X4bJxVZ,IOFWyvKHBzEWDK7Ao4B|16|26-414-821-5076|-532.24|BUILDING| slyly pending asymptotes! pending foxes nag. slyl\n6827|Customer#000006827|pkyUJaeoz7jLGIrQ,kYUVF5loZj|5|15-181-335-1276|6647.51|AUTOMOBILE|press packages cajole carefully blithely bold pinto be\n6828|Customer#000006828|i07aofApQ6Sg,gMjYzl ycOlqa sSi|11|21-232-607-3504|6718.53|FURNITURE|ickly fluffily permanent asymptotes. fluffily regular deposits use slyly carefu\n6829|Customer#000006829|hLxbhkw8dFbPqPuELUIn39hrnisrNUfJv4F|20|30-413-747-6182|1323.84|BUILDING| the packages. regular dolphins s\n6830|Customer#000006830|eVnzLRiUrpOj2zHTaq|6|16-527-593-5356|5679.21|AUTOMOBILE|endencies are slyly against the final, r\n6831|Customer#000006831|s ZzU  g4nFiJrvNg pE7cN1UGL7z3THVaXiuuY|16|26-690-485-7073|1470.27|MACHINERY|around the accounts integrate slyly according to the special accounts. blithely express deposits wak\n6832|Customer#000006832|AggcVEoM1Dw2WfE|10|20-653-330-2367|2821.81|BUILDING|o beans use slyly ironic theodolites. ironic platelets boost carefully into the fina\n6833|Customer#000006833|xpXLjnef5qfH2xlHXF9oMMzIjIsuex,F0uw|13|23-153-556-1341|3185.16|FURNITURE| quickly bold instructions. even somas wake blithely. blithely furious theodo\n6834|Customer#000006834|2 OGzQbTM4PhDp6|12|22-607-237-9927|4970.18|FURNITURE|y after the notornis. fluffily regu\n6835|Customer#000006835| 1Vwc1DHAl|18|28-336-747-3054|8825.71|FURNITURE|ss asymptotes about the bold ideas \n6836|Customer#000006836|IPAnUQ7OdyUJ9HlXzSKYEp|9|19-944-807-7519|7724.27|HOUSEHOLD|e regular requests sublate regular platelets. ironic dolphins integrate carefully. slyly even \n6837|Customer#000006837|kcS0yon,tgPVfGO7hrxyv5C,Su,gM|4|14-101-991-2132|8823.38|BUILDING|s. pending, express pinto beans w\n6838|Customer#000006838|BITaY3dbOQVUe3i5g7,ewHd|23|33-156-718-4716|1578.52|FURNITURE| courts are after the fluffily express requests; c\n6839|Customer#000006839|oYnqEjWQ9dZezLjJtHPNIXI8HoxtqANG Z7z zj1|19|29-332-980-9867|7140.01|HOUSEHOLD|tes sleep slyly furiously special requests. carefully even dolphins nod blith\n6840|Customer#000006840|8enbm51YZ1P8 9WL56McRjcRLxC|18|28-421-550-7131|1256.89|HOUSEHOLD|ly blithely bold ideas. blithely regular excuses nag slyly even requests. slyly regular forges are packages. pendin\n6841|Customer#000006841|g  NzADDwQKOQedcgxkP3 vDTJ0OThsh0sqI,vb|9|19-631-468-3008|2459.12|MACHINERY| busily final packages across\n6842|Customer#000006842|1xXUXh723lgE0wfY1d9vuOTXELqhEPFo5t|9|19-676-771-4155|1342.45|MACHINERY| above the regular theodolites wake ironically against the bold, regular foxes. unusual requ\n6843|Customer#000006843|M5z1DoiXAJCQVVIHKjMbntcT1A3Lfc4XSkXVRY|19|29-764-509-2918|-563.00|FURNITURE|ove the carefully regular instructions. carefully r\n6844|Customer#000006844|hgGc5KCVN3QDk0Lci|6|16-763-561-5338|6663.09|AUTOMOBILE|ng to the silently unusual deposits. slyly ironic excuses use around the slyly regular escapades. regul\n6845|Customer#000006845|VzpxA2uz,A BwWkAvIJnXqZHGDlhXK8n3IX8|19|29-371-479-7196|4510.06|MACHINERY|g the slyly pending packages wake along the carefully final accounts. regular pin\n6846|Customer#000006846|ggBnTO6OZXBMWuflbpz4yVEDFUa9n|20|30-223-579-2199|8524.19|AUTOMOBILE| carefully ironic requests. slyly ironic deposits sleep along the carefully special dep\n6847|Customer#000006847|FgCB,v15gQzq6|16|26-489-731-4993|81.08|AUTOMOBILE|across the quickly final theodolites. slyly ironic d\n6848|Customer#000006848|XETSPsMa,,KHVAAu|8|18-993-692-5687|7653.55|MACHINERY|. theodolites run carefully quietly even packages. furiously even pinto \n6849|Customer#000006849|zwnB9JnyCV0B2O Ue6Jn78KBarQRx2m|19|29-921-924-3641|8217.84|HOUSEHOLD|requests. ironic instructions use blithely blithely special ideas. bold theodolites haggle a\n6850|Customer#000006850|A0qvm6saCh|4|14-519-545-3578|-702.43|BUILDING| special ideas. final, regular platelets affix regularly according to the slyly bol\n6851|Customer#000006851|hvFJsItQnymk8pxkrjnenWCJ9GAjSNh |12|22-860-161-7304|6230.56|FURNITURE| foxes. blithely enticing accounts above the furiously re\n6852|Customer#000006852|C hC7N6xSqjDVmEQzEZ01NYj4SBl5nhu|24|34-681-524-9480|6121.02|HOUSEHOLD|ily silent pinto beans. even, ironic dependenc\n6853|Customer#000006853|AbpZYqtK6c5EfpZtpF|10|20-422-853-4017|1986.65|HOUSEHOLD|bout the requests. pending deposits cajole carefully above the regular deposits. requ\n6854|Customer#000006854|Ka5iBA43bmKVAjKqFya2HfFkurzVx3pbFn,|5|15-430-821-6024|2568.18|FURNITURE|ts cajole carefully across the regular deposits. furi\n6855|Customer#000006855|7yJhlD4Ziy9 IFz4VFzAURNoVWzFj5zyYdKBKT|6|16-876-384-6010|8375.91|BUILDING|s across the slyly unusual requests cajole alongside of\n6856|Customer#000006856|CQMnbODOwhI9toZ75vf|23|33-529-574-6366|4984.79|AUTOMOBILE|. blithely ruthless warthogs against the slyly final deposits wake along the furiously fi\n6857|Customer#000006857|qnTjfF57SI5LrBzO|23|33-446-990-4978|1409.67|BUILDING|. slyly final packages poach bli\n6858|Customer#000006858|pd6Motczl51ondujEyQ7367tYCT4NC6|0|10-799-377-4331|7239.92|BUILDING|olites cajole carefully requests. special, blithe gifts pr\n6859|Customer#000006859|1zy,R99p9Dg|17|27-368-450-7892|8452.75|MACHINERY|ular requests. blithely regular requests nag! quickly unusual instructions cajole. carefully ironic ac\n6860|Customer#000006860|j0c5d8lwjd X7fYicMKeDycv|4|14-580-483-9140|9414.14|AUTOMOBILE|ithely unusual pinto beans unwind special packages: quickly pending deposits cajo\n6861|Customer#000006861|um,8dEPxPOIu4uCO5oF9C os|19|29-269-205-4055|8593.46|BUILDING|ole fluffily against the carefully unusual excuses. special, final accounts \n6862|Customer#000006862|WHjtQl UwzncNIkejao3a5W|14|24-682-159-5973|9323.04|FURNITURE|heodolites wake idly against the packages. ironic theodolites c\n6863|Customer#000006863|C7xp7Euj8Zatj|1|11-147-882-1449|6353.09|AUTOMOBILE|luffily above the ironic, express requests. sly, careful foxes are about the pending, fl\n6864|Customer#000006864|sdyPu0LQ3RkTdoFy|23|33-998-134-8314|8447.64|HOUSEHOLD|ickly even theodolites cajole slyly blithely special pinto beans? slyly unusual depos\n6865|Customer#000006865|5JPh7HJJGpVizXF|24|34-198-293-9623|527.61|AUTOMOBILE|l courts. slyly ironic foxes b\n6866|Customer#000006866|P7lfQDiROc4qhR3Khflxr|6|16-366-687-4124|-567.46|MACHINERY|ly. slyly regular ideas integrate instructions: dependencies haggle furiously regular, regular dolp\n6867|Customer#000006867|t1gpHaWFZqRKhV,raEaxdUJ,MU|14|24-146-780-7698|8539.69|MACHINERY|es doze furiously express accounts. slyly silent requests sleep fluffily fluf\n6868|Customer#000006868|R13,6EjlPmdsl|23|33-647-349-2675|7639.96|MACHINERY|al packages. ideas boost slyly above the final requests. regular deposits haggle fur\n6869|Customer#000006869|6ZYHNoZX5,Me|14|24-606-630-9319|-84.65|MACHINERY| pinto beans. carefully ironic accounts haggle caref\n6870|Customer#000006870|i3GhNxcD0,NDHWYXq778CcKI4Rok57Rk4I50R|16|26-714-569-7040|5292.68|AUTOMOBILE|boost furiously. furiously unusual ideas nag along the ideas. requests cajole carefully among the sly\n6871|Customer#000006871|4Aawh3u9VlvfLcp,2|4|14-497-499-6031|6002.37|AUTOMOBILE| regular requests. even, close deposits are. permanen\n6872|Customer#000006872|seuInq8wsBrKZU6lBlb ro1lt1gMtWkI L6LEQS|14|24-644-733-1264|9852.24|AUTOMOBILE|y ironic packages after the quickly final accounts haggle foxes. ironic e\n6873|Customer#000006873|KXt6OTXQyCYz46Kw5Ynz|0|10-948-162-7136|1599.50|BUILDING|ts boost requests. slyly quiet requests are slyly across th\n6874|Customer#000006874|VbQ7fM46whu0cr31adJ|19|29-695-872-3712|7814.06|FURNITURE|en requests mold furiously express, final multipliers. car\n6875|Customer#000006875|TjO3DIyAnZ5kX3 KOWj7m7d,3jC1dbZZNy9MoNVg|10|20-934-535-5633|28.31|AUTOMOBILE|lly regular instructions haggle fluffily accounts. carefully regular dependencies wake furiously.\n6876|Customer#000006876|AzTnyu945JeYf4PLbjV7cSL3v|21|31-326-600-5503|812.97|MACHINERY|ccounts should nag until the ironic, express packages. ironic forges \n6877|Customer#000006877|QR2VDDA94zJHPMmac86RN1Lg3SPxPzIt,u|16|26-767-249-9337|-532.13|BUILDING|rding to the quickly bold instructions? careful ideas cajole furiously against the final packages-- fluffily busy a\n6878|Customer#000006878|OyrOaYhd68cPx9maycfwDGq R22|14|24-648-517-8694|8396.86|HOUSEHOLD|ic packages boost around the blithely final deposits. permanent,\n6879|Customer#000006879|qGAxDp5CIcJWPNt zGFerq4kFZWQiX|12|22-942-165-1099|5044.93|FURNITURE| to the special foxes? fluffily special theodolites \n6880|Customer#000006880|t3um3YPoJ17XQUXC8V3VBIFnoz1D|24|34-961-846-5811|225.45|BUILDING|ts print. unusual, special deposits against the blithely regular accounts serve fluffily after the deposits. even\n6881|Customer#000006881|6VfVWZMuCYB8i4b 6dRhgUPFMnWADADr407sLL|18|28-406-320-1387|3156.70|AUTOMOBILE|ets across the ironic pinto beans should boost ironically furiously ironic asymptotes. blithely regular accounts des\n6882|Customer#000006882|3lBEzGcwg1vKtSkqtvzMVQwqvwfGBGn|2|12-151-684-6027|-396.82|HOUSEHOLD|he unusual pinto beans. slyly regular accounts haggle furiously express excuses? slyly pending warh\n6883|Customer#000006883|,OGuJ9Zt3XpAEONbIpIpal Ux3|11|21-873-538-3754|5579.14|FURNITURE|s frets nod blithely alongside of the deposits. quickly even theodol\n6884|Customer#000006884|yRIvbIG72RGuz|11|21-789-993-5944|2597.78|BUILDING|uickly. quickly express deposits sleep slyly? q\n6885|Customer#000006885|zgFxaDEtwdOvsa8eku1KI5f0Hq|17|27-266-722-7657|-188.30|AUTOMOBILE|sits wake carefully. ideas are carefully. final, furious packages impress slyly unusual requests. slyly re\n6886|Customer#000006886|e1kh m79wZ2sAUFXAzmTAsYsG6bpc2SlJ|5|15-103-327-5461|1158.35|FURNITURE|lly. fluffily regular foxes alo\n6887|Customer#000006887|Ts6FP r64sVYHPT|0|10-744-644-3355|7280.37|MACHINERY|lly even accounts. unusual, final foxes cajole express waters. carefully special \n6888|Customer#000006888| O40aa4,Ip frcWNEh6XbZL5kktj,QFqRe|5|15-967-617-5600|6207.74|AUTOMOBILE|to beans. regular packages accor\n6889|Customer#000006889|SvUWryqsTeYMUI8MoGwe23MxCJcgnk|15|25-961-943-9801|7526.00|FURNITURE|according to the accounts. foxes cajole carefully. ironic packages haggle blithely\n6890|Customer#000006890|9h9 InEMYJ5P|7|17-911-310-5010|9514.17|MACHINERY|en packages. express, even inst\n6891|Customer#000006891|6KWm7ap8xnKV2zYC7ejZifLwE|12|22-373-997-5030|6815.03|BUILDING| use carefully regular packages. blithely \n6892|Customer#000006892|mNuuOriPB9 YdILhmvINOGd38jGBZ|13|23-151-643-6412|6203.78|BUILDING|s sleep carefully foxes. slyly pending requests sleep requests. requests nag furiously carefully \n6893|Customer#000006893|IQKKtRNXWAsdbqynSPQ7P5Pd8X uF|3|13-947-844-8526|6929.65|AUTOMOBILE|y special foxes are quickly carefully final requests. unusual packages affix express, regular requests. quickly\n6894|Customer#000006894|qP,e3crppnJlKzejVH b2D,|12|22-247-482-7189|7747.33|AUTOMOBILE|al accounts according to the pinto beans doubt among the final braids. blithely fina\n6895|Customer#000006895|eYt JN3SRFOg,PHtmhhfqN|20|30-525-577-9213|361.67|BUILDING|kly even theodolites. packages integrate across the slyly pending accou\n6896|Customer#000006896|MvVnnxEF29nDVdr  WvzbHWkm|24|34-850-657-1872|4576.31|AUTOMOBILE| final, regular waters. final platelets wake furiously silent deposits. regular \n6897|Customer#000006897|2y42Nhr2PYA54EHyciLjYpnVHJStojcN|0|10-920-317-5942|101.13|BUILDING| the express dependencies. boldly ironic foxes nag slyly express requests. final \n6898|Customer#000006898|Iu VC1aV16HlqfvkfNd8snf4|20|30-234-165-4904|3546.09|MACHINERY|. carefully regular ideas cajole carefully brave, final asymptotes. regular, regular \n6899|Customer#000006899|fI2lc9kOUESmGia2kXSJNNI, 5MMioi3cbGg1M97|0|10-535-357-3310|-909.78|BUILDING|e carefully regular ideas. pending warhorses use quickly according to the blithely even accounts. blithely regular \n6900|Customer#000006900|XxzSLyP jdrxrFf,iyxf|5|15-836-566-6221|8277.05|HOUSEHOLD|ully final requests sleep carefully enticing, even theodolites. excuses cajole! f\n6901|Customer#000006901|MF1Yh6821MDYo|17|27-691-188-7577|4462.87|AUTOMOBILE|sleep quickly even theodolites. furi\n6902|Customer#000006902|bDR25t GnLkoK|6|16-378-380-3108|7453.93|HOUSEHOLD|e fluffily above the blithely even dept\n6903|Customer#000006903|AtYnMz1ydb2y2yeKlQ6df1txYM1Ibs5u|9|19-471-505-9852|8241.38|MACHINERY|gouts cajole instructions? quickly unusual ideas caj\n6904|Customer#000006904|Pm8 tKCreWStvS|22|32-451-759-6848|9979.02|AUTOMOBILE|lyly ironic asymptotes: fluffily ironic accounts cajole blithely pending \n6905|Customer#000006905|R,hn e5kStdmzVDhYooDC7Z1tJ7|11|21-390-139-1440|9534.63|MACHINERY|ng requests. carefully silent theodolites \n6906|Customer#000006906|NtpZ4oXphusYXOHppCd,DNX g65hon1kdIAGs233|6|16-847-442-4850|9552.15|AUTOMOBILE|inal theodolites wake slyly carefully dogged packages. reque\n6907|Customer#000006907|xVxf5 SbjMNLGhAFzmW6|6|16-105-262-6169|7106.46|HOUSEHOLD|ccounts integrate against the even packages; blithely even pac\n6908|Customer#000006908|PSFN752zobO,NnrXueh68DsNliR|18|28-352-804-7306|9321.69|HOUSEHOLD|y packages; carefully even theodolites boost carefully after the furiously ironic gifts. foxes kindle slyly. sly\n6909|Customer#000006909|wTMW41vDDJ,rDvLBtcP2lAGPortmxNP18h|10|20-480-370-7421|5745.76|AUTOMOBILE|s about the ironic deposits detect blithely bravely ironic orbits. pending packages above the blithely iron\n6910|Customer#000006910|wHUTQtKiSMGbGqKcC526p9Pfebuc7r|5|15-722-787-9962|8945.89|FURNITURE|the express, regular escapades. carefully unusual theodolites do wake quickly. quickly\n6911|Customer#000006911|lx95MUKc7,CsN WHXRWUoVV|6|16-440-937-2210|3682.25|FURNITURE|riously pending accounts? carefully unusual accounts according to the quickly unusual re\n6912|Customer#000006912|eZArhHGNgAZTIIs6m8JEFgvhhsybD77LXI8ScXH|8|18-789-906-8424|-383.69|AUTOMOBILE|quickly against the regular deposits. blithely regu\n6913|Customer#000006913|jH27L,gZj9dws4sVD9pjQWjNM9Z3hjJhNEAHd|7|17-424-926-7617|6237.26|HOUSEHOLD|ses nag blithely slyly silent accounts. slyly ironic pinto beans alongside of the \n6914|Customer#000006914|6ZnxYihMt7HmIQqiItpeVDq81omFb9S4mul|21|31-622-699-4865|8725.78|MACHINERY|. regular, ironic accounts among the furiously even pe\n6915|Customer#000006915| VQo,H2BRkjoa0dYXLAA01mqwUytbBM|19|29-651-328-4337|319.47|BUILDING|lets after the carefully slow packages cajole blithely asy\n6916|Customer#000006916|KGSD2t2VRVQ,n3EW4TiQH8i4L8VBRP1P|5|15-509-409-1957|-795.98|AUTOMOBILE|lites. regular pearls solve quickly. carefully regular sheaves against the always\n6917|Customer#000006917|SwW3qHYL1ZuBlx17gCKpvcP|6|16-544-374-5520|6932.21|FURNITURE|d sleep. blithely special ideas haggle along the special pinto beans. regular, special warhorses are alongside o\n6918|Customer#000006918|0HHnMHiko6CFQTroq5lGeFg0JDtZm6PUhIWU|3|13-658-888-3933|6671.40|HOUSEHOLD| fluffily carefully quick accounts: fluffily regular pinto beans cajole quickly according to the q\n6919|Customer#000006919|hUBFeaV6cn41fFHMpvd7nuAMC1Q4|10|20-964-269-3393|1279.94|HOUSEHOLD|deas are carefully. regular, permanent instructions boost slyly. blithely unusual deposits caj\n6920|Customer#000006920|DmTeHxRVLSe32KfxJp38lE|0|10-384-129-2868|4968.13|BUILDING|ounts are blithely above the furiously special sauternes-- quickly special \n6921|Customer#000006921|nGFGmYctkA9IM0vxf2Y4GBPnT|12|22-642-260-2620|7388.91|HOUSEHOLD|ly unusual requests boost above the final pinto beans. careful\n6922|Customer#000006922|3bCCt0S2wd6RQeq|4|14-163-216-8977|4434.20|HOUSEHOLD|kindle carefully. carefully unusual packages d\n6923|Customer#000006923|v6BO5dXHqLFmOfPck0|7|17-733-662-9017|9600.79|FURNITURE|t about the pending requests. final, regular theodol\n6924|Customer#000006924|z,OREBfNfUkRsj|16|26-289-764-7597|-408.30|BUILDING| the unusual packages. ironic, final requ\n6925|Customer#000006925|2Gg4xMqjRAV0nAn|8|18-878-944-5385|-448.55|HOUSEHOLD|sits about the slyly regular accounts x-ray carefully over the unusual, final theodolites. blithel\n6926|Customer#000006926|KOx lB nFGsdak3 Sc5zCjiRFoWq1mx5t|22|32-894-701-8844|5160.08|HOUSEHOLD|its haggle quickly. fluffily even packages sleep slyly along the theodolites. express, pending deposits above\n6927|Customer#000006927|Ra0BshyuR,4uTcWjvs1PIqswCbIC6U l7A|6|16-135-283-6467|-509.64|FURNITURE|efully even depths. requests should have to w\n6928|Customer#000006928|SOf,2mOUgK5PpW,C9US8oH61eNDG4|2|12-944-721-3451|1380.62|AUTOMOBILE|fully after the slyly dogged depos\n6929|Customer#000006929|sQEFEgufg0ZT|12|22-151-910-6027|3000.98|HOUSEHOLD|lites haggle final platelets. express, special theodolites wake slyl\n6930|Customer#000006930|RpPHMhjHQLhRR|11|21-832-212-8391|386.35|AUTOMOBILE|e blithely even accounts. ironic, even accounts about the furiously final\n6931|Customer#000006931|DJcGXg0BrP1ibpV7UCxEaK5OXy yCckOACzF|24|34-282-643-3571|1402.40|HOUSEHOLD|nusual pinto beans across the ironic deposits are blithely alongside of the carefully brave foxes. quic\n6932|Customer#000006932|XjlmI08 R3CXJ8JPYGwSihQUDir|19|29-930-104-7406|6064.87|AUTOMOBILE| courts cajole ironic, final ideas. ironic multipliers cajole carefully iron\n6933|Customer#000006933|w0D2wXhqohW3rtxjXxIQvcxH75Fh1XAWZ3O|20|30-564-778-5516|4292.61|FURNITURE| accounts above the quickly final warhorses print blithely \n6934|Customer#000006934|oVWWov tNz1fn|14|24-121-895-8013|5944.59|MACHINERY|efully express ideas cajole furiously slyly brave packages. closely bold requests haggle\n6935|Customer#000006935|eqVuF6zTXrqK4BhmNUBAZQghbjY8uy0co|3|13-497-759-6170|6730.94|FURNITURE|ronic, regular requests after the ca\n6936|Customer#000006936|XgPD s4bQqx5|9|19-923-718-1041|1006.58|HOUSEHOLD|he ironically even packages. regular dolphins are slyly by the pending excuses. slyly final requests haggle careful\n6937|Customer#000006937|BlUCF0,i2jMXs70wxl3AWvd9frn|24|34-883-594-7672|8496.83|HOUSEHOLD|lly slyly ironic deposits. quickly special excus\n6938|Customer#000006938|2ud1eHT7TvCAPub2UZ3bkcKT2Pq8M11eSpd|15|25-974-463-8064|2339.57|HOUSEHOLD|realms are busily carefully express requests. furiously pending foxes try to cajole blithe\n6939|Customer#000006939|e2UhEWWPopLOyQ6G|10|20-425-357-4024|5165.17|BUILDING|ut the ironic ideas. regular courts according to the final instructions detect blithely theodolites. even packages\n6940|Customer#000006940|37YCc8T1xgurTO|11|21-485-600-2482|8105.71|HOUSEHOLD|y along the carefully regular gifts. final accounts cajole. quickly ironic deposits kindle carefully f\n6941|Customer#000006941|63Z3Qe6urA|4|14-210-435-4058|6757.19|MACHINERY|ular instructions cajole above the bold, ironic accounts. ironic ideas about the blithely even re\n6942|Customer#000006942|N31XA0a7VOdIiIxuKpbYwE0l|10|20-899-337-6472|2059.91|HOUSEHOLD|ep blithely about the quickly regular dependencies. ironic \n6943|Customer#000006943|qjFXg1Ia9FuM|24|34-704-691-5442|1872.41|HOUSEHOLD| carefully regular excuses. ruthlessly unusual instructions was ironic theodolites. pinto beans cajole carefully\n6944|Customer#000006944|PlOgCXn qIsC96sqwi7jMKVcv|8|18-677-417-1382|7549.91|AUTOMOBILE|nding accounts. bold dolphins detect bold packages. reques\n6945|Customer#000006945|X6nI1uOqfKBA,7V7iEb9PWg6hbM794B1ZmSsgk|10|20-763-130-4896|4363.99|BUILDING|nt accounts sleep quickly. theodol\n6946|Customer#000006946|O142a46LKzb75,ggUsnA7yY MHZ7rxNi|24|34-108-937-2099|6626.82|HOUSEHOLD| final packages boost above the carefully bold pinto beans. pending sheaves sleep beneath the sp\n6947|Customer#000006947|2q61zt4rUQY6JOVeKxh FcRNRqj|24|34-935-489-1820|-30.39|AUTOMOBILE| packages. furiously close deposits sublate across the final packages. regular, bold requests breach. car\n6948|Customer#000006948|GWfdOFWXism9l,PQp8azQJO UPfcdMm2cb,rqU5|3|13-636-530-5983|4150.95|HOUSEHOLD| blithely unusual forges run quickly furiously final accounts. blithely regular accoun\n6949|Customer#000006949|88E o,rjHZJVp|0|10-920-156-2564|999.02|AUTOMOBILE| even instructions. asymptotes use. blithe theodolites\n6950|Customer#000006950|D7PZ9 FXDeoG9jzsIqiVm8i V|16|26-567-707-8729|2414.22|BUILDING|g to the express platelets. even account\n6951|Customer#000006951|dswooF8dKb|21|31-661-344-4562|5751.69|MACHINERY|regular requests. carefully silent theodolites promise quickly regular accounts. platelets sleep among the fina\n6952|Customer#000006952|tedolX6bU7a07kS,HSV1Kh2VartW|1|11-964-186-1638|9600.30|HOUSEHOLD|ckages play pending ideas. pending dependencies wake blithely along the unusual accounts. eve\n6953|Customer#000006953|T6GNNKYnmp3KhK2a,n|4|14-200-221-1070|2384.73|HOUSEHOLD|ar dependencies boost across the regular, bold \n6954|Customer#000006954|rGHTeIqYsbkLG4yOmrZWyvY17zxvWjCgvj|19|29-846-280-8291|5080.67|BUILDING|ross the ironic pinto beans affix after the packages. ironic, express theodolites after the furious\n6955|Customer#000006955|7Pl77 AAMou0F56ufvqSGYm2dFZrsBSf|23|33-734-299-2759|6751.07|HOUSEHOLD|is against the carefully bold theodoli\n6956|Customer#000006956|k 4M1dYnBY0s9tDIjnpZQ7QSRAsqv,dP4qip CBY|19|29-765-731-1061|9202.51|HOUSEHOLD|ial requests detect carefully express instruct\n6957|Customer#000006957|sAt10Pog00qXxf3|1|11-448-447-2941|4672.45|HOUSEHOLD|idly pending pinto beans. carefully pending deposits cajole quickly across the unu\n6958|Customer#000006958|pyyjzooPiwi2FUIz|5|15-554-805-5336|9829.41|AUTOMOBILE|ly final pinto beans. carefully express ideas about the furiously unusual pinto beans ha\n6959|Customer#000006959|rorENMWClttRXEp|13|23-233-789-3757|2990.45|HOUSEHOLD|gular packages are slyly across the asymptotes? escapades boost blit\n6960|Customer#000006960|OUKTB cNG030,aFLzyB|19|29-674-628-3972|-505.65|BUILDING|blithely final waters detect according to the even pinto beans. express \n6961|Customer#000006961|T6oeN XNst4bY6QOIxFQAj,WN|16|26-346-690-5410|2474.82|MACHINERY|its use carefully blithely even pinto beans. carefully even acco\n6962|Customer#000006962|4G9HL28bwq|5|15-357-936-1344|1281.15|MACHINERY|equests. regular deposits nag. foxes sleep quickly-- carefully regular requests across the deposits wake quick\n6963|Customer#000006963|CW0iwpVyNqVJiJf0roU5OAoX|16|26-430-431-1058|3161.07|AUTOMOBILE|le quickly ironic excuses. slyly close ideas wake quickly wit\n6964|Customer#000006964|IWYQGMU6rEz4GMdjsQAKCsnQT5|13|23-185-648-1303|7140.70|HOUSEHOLD|lly bold requests use carefully carefully bold\n6965|Customer#000006965|d6gerbM9AWmOdDpp|15|25-939-405-4493|1815.68|AUTOMOBILE|ular accounts. carefully close accounts doze. final requests solve furiously furiously ironic requests. special asym\n6966|Customer#000006966|Q3yAE1yoj,TKafWnhyfcR4WL322ME Pv9bkNR,FW|22|32-842-357-9534|144.58|FURNITURE|theodolites use. finally special excuses hagg\n6967|Customer#000006967|uMPce8nER9v3PCIcsZmNlSrCKcau6tJd4qe|13|23-816-949-8373|7865.21|MACHINERY|r pinto beans. regular multipliers detect carefully. carefully final instructions affix quickly. packages boost af\n6968|Customer#000006968|Ez1Rhj5Qi2,10Nug38BsPiacwskhzpT|5|15-858-442-4792|1651.44|AUTOMOBILE|deposits are blithely unusual foxes-- even requests do are to the blit\n6969|Customer#000006969|HKsXzhiJwn0oWqic7outvp6ek5|24|34-608-122-1503|727.09|HOUSEHOLD|s. even foxes wake. requests haggle slyly carefully final deposits. even f\n6970|Customer#000006970|PEtGiJUdTje1Iag6unPFdev|7|17-244-333-8174|8208.18|MACHINERY| regular accounts grow carefully \n6971|Customer#000006971|OMyW3Rc1F9r9ixU|15|25-797-318-1684|1906.06|MACHINERY|ts. carefully even excuses mold blithely carefully final foxes. furiously unusual accounts sleep carefully above \n6972|Customer#000006972|MjqRGeXURtvVOEY5u30KbgffSKBvJ2X2OI,Hm6Tj|10|20-340-512-1672|1096.88|FURNITURE|ges wake up the fluffily pending deposits: slow deposits cajole before the slyly regular frets. regular theodolites\n6973|Customer#000006973|2kXQXsfJMUjQn|21|31-948-302-3253|8232.51|MACHINERY| the regular, final asymptotes ca\n6974|Customer#000006974|TVPV8QjdvgxuewOviKUj8IrMvta yJ|23|33-695-299-2927|3894.63|FURNITURE|ounts above the blithely bold requests sleep furiously across the regular instructions. quickly\n6975|Customer#000006975|bSjO03DW yuKgBg2ewRK6e47ixGfAYRuFT,GR9|23|33-652-640-1772|5049.82|BUILDING|ld accounts above the furiously \n6976|Customer#000006976|Oj Pipp9GFv8FLDelp82C|9|19-115-502-9616|5348.14|HOUSEHOLD|structions wake carefully. furiously even requests detect blithely. fi\n6977|Customer#000006977|IAhFYl42MDQOWl  FBdXT8M o|8|18-423-650-1806|3164.56|FURNITURE|usly alongside of the bold accounts. packages haggle after the blithely ironic accounts. ironic pinto beans dete\n6978|Customer#000006978| TOk2qS85CJpdrRpGsszF|12|22-707-108-2535|-325.02|MACHINERY|xpress patterns engage fluffily after the deposits. blithely final theodolites nag abo\n6979|Customer#000006979|K1aueKP9rhN,OLMh6NB9MF9JC|3|13-179-679-2432|2270.82|BUILDING|ously regular pinto beans wake: ironic requests wak\n6980|Customer#000006980|IzbhQ7AaNNSNkTjIGPsH1bZe53WXDupcQm|11|21-352-950-4327|4710.25|HOUSEHOLD|ing instructions: furiously bold dolphins about the furiously ironic instructions haggle furi\n6981|Customer#000006981|BDM83,inyQX8VjjAAY8hrSlA8uu9h6zusf|22|32-465-600-2595|7194.73|MACHINERY|le quickly express deposits. final theodolites haggle quickly permanently even pac\n6982|Customer#000006982|dTif54yxBO1KSK90YnlVp,j53YYC4D|8|18-481-928-6409|7891.67|BUILDING| alongside of the furiously regular accounts. quick\n6983|Customer#000006983|E0AqrX3aIw4ZcINSIWDXD,7qxDykEj5odh8|22|32-796-697-5842|6348.19|BUILDING|ackages. special instructions wake alo\n6984|Customer#000006984|ESRBQnnbjKMRydlNyoXn,|2|12-423-137-1791|3020.71|AUTOMOBILE|ckages at the blithely regular ideas wake blithely amon\n6985|Customer#000006985|J9qvTE,Ic0Oxv2cbFwAynNxzDW5KY96qVpOQ3q|13|23-652-721-2985|2119.77|BUILDING|efully carefully final deposits: quickly pending dependencies wake al\n6986|Customer#000006986|EBY0Sg3HMtzo8CzNhzaUtjiR1bAL|23|33-582-389-4321|6959.44|AUTOMOBILE|nts sleep slyly above the regular packages. slyly unusual packages na\n6987|Customer#000006987|lGJOaGFNBmiIy3AIqwf5Az0aGvrXnhfakZBOQawR|17|27-320-420-9595|2282.79|AUTOMOBILE|latelets haggle quickly slyly regular ac\n6988|Customer#000006988|PuokzRJ2EY5|3|13-830-329-5880|7733.90|BUILDING|inal platelets affix furiously. blithely regular deposits promise special, unusual requests. fluf\n6989|Customer#000006989|VAdDFdOq9BAyUsOP8CaFBvTG8G2K0|23|33-283-282-3404|5712.90|AUTOMOBILE|ular asymptotes use even, regu\n6990|Customer#000006990|z3zQwqsS8eg61oVV4e8UgdFDt|7|17-437-544-6391|6746.93|MACHINERY|ackages. express, unusual accounts cajole furiously toward the fluffily final acc\n6991|Customer#000006991|xMyl,tcSuNEP L6z5VlZXB69GuDoBz1 FeQxHnb|0|10-948-457-2049|5747.55|AUTOMOBILE|s around the slyly bold deposits wake carefully among the bravely pending asymptotes. quick\n6992|Customer#000006992|HvnRKHV1kJ|23|33-830-883-1031|6070.79|MACHINERY|ithely quick foxes cajole fluffily. even accounts \n6993|Customer#000006993|VgyZ4hR3o2DDrqX2p9f1ao8GLn7Y el0|11|21-996-879-2346|4754.10|FURNITURE|l pinto beans boost silent pinto beans. carefully even accounts integrate evenly. d\n6994|Customer#000006994|rV05MzBGNrLz|0|10-962-702-6979|7109.05|BUILDING|ctions unwind. bold foxes cajole quickly above the regular, i\n6995|Customer#000006995|h1Is0,kANMXbCDqVHJav|16|26-594-133-2656|8768.51|HOUSEHOLD|slyly ironic, even accounts. furiou\n6996|Customer#000006996|FFH0V1HOhdgfDs,kJV|20|30-658-795-5594|3335.98|MACHINERY|egular asymptotes cajole quickly regular requests. unusual courts use quietly unusual, pending packages. package\n6997|Customer#000006997|twNLY6rDvngJvLOEiF|18|28-278-657-2446|9728.39|BUILDING|ep furiously along the grouches. regular, slow accounts use blithely regular, bold foxes. slyly slo\n6998|Customer#000006998|R4GseYn1PbPAQkah9p5Bdqfe|15|25-779-557-3473|-870.34|AUTOMOBILE|after the blithely bold deposits impress quickly idle instructions. slyly silent instructions among the bra\n6999|Customer#000006999|1T9GzwkkJp,giqi KBNPEXhslG|9|19-345-535-4019|93.74|BUILDING| are above the slowly pending platelets. \n7000|Customer#000007000|GabU4EArz2WOSxBImz79QitlufnV,kWbFSrQD|16|26-901-104-2112|469.82|MACHINERY|after the carefully ironic accounts boost slyly regular accounts. express, regular a\n"
  },
  {
    "path": "src/test/regress/data/customer.3.data",
    "content": "1001|Customer#000001001|KbWTzGB3ZUymu nNCIuG5eCueaqu|21|31-389-986-4741|7140.81|MACHINERY|ever. fluffily special requests are. slyly final asymptotes are carefully quickly reg\n1002|Customer#000001002|98bKmyr3jZWRLEY9WBtyUWOodVd|10|20-973-622-6579|3699.76|MACHINERY|ns. deposits along the ironic, regular packages wake furiously according to the carefully even excuses. slyly qui\n1003|Customer#000001003|lE07lPMzVzMhG9CUC54uPwGw3BWO|21|31-716-397-1854|7894.00|AUTOMOBILE|quests sublate blithely blithely special dependencies. excuses use busily express pinto bea\n1004|Customer#000001004|mBaNGEJoY2tgXD60V2DEO ajjoM3Zd,Jp|8|18-676-152-4849|1512.46|MACHINERY|ainst the ideas nag fluffily according to \n1005|Customer#000001005|cTWPLcTvotjgrrcN3j|13|23-149-373-9093|7790.94|AUTOMOBILE|. furiously ironic accounts affix careful\n1006|Customer#000001006|Q46palcsa4KwAMhPS|12|22-364-780-5932|7447.99|BUILDING|equests. regular pinto beans sleep furiously express, ironic accounts. special,\n1007|Customer#000001007|PfH0lw8GzD7o|9|19-790-843-5283|7347.90|MACHINERY| theodolites. ironic requests wake. thinly silent \n1008|Customer#000001008|AfP6tFNz1Eu4buoUd,HrZAld340 xz2wbQ2|2|12-115-571-7897|8191.74|HOUSEHOLD|press orbits affix furiously pending packages. courts alongsi\n1009|Customer#000001009|cWONXs2Vx30bkgYoCkx7LrJH,E|12|22-132-906-1117|594.50|BUILDING|ng to the stealthy, final courts cajole carefully alongside of the gifts? regular ideas above the furiously express \n1010|Customer#000001010|uasIK CZZ5|5|15-221-463-3776|1652.78|AUTOMOBILE|ing ideas doze carefully accounts. slyly regular theodolites poach. carefully pending de\n1011|Customer#000001011|6m8KP FxT4nnHgoc4CN70TVLW1X5Q|5|15-736-809-3168|1188.94|BUILDING|uriously express asymptotes. u\n1012|Customer#000001012|5Zsp rqM6oCmgqqFe|4|14-535-551-6255|4422.45|AUTOMOBILE|s above the carefully express r\n1013|Customer#000001013|k5rfeOtchP1  w|15|25-725-599-1183|-951.53|BUILDING|idle packages cajole regular asymptotes. carefully express forg\n1014|Customer#000001014|ZsiaboMOOV,aGwWUpfE|11|21-553-425-9152|-392.84|AUTOMOBILE|telets. ironic platelets cajole carefully; bold, special instructions unwind blithely regular somas. carefully \n1015|Customer#000001015|RDJWEmcAk4GC8OT8WCsXB|10|20-134-926-5391|6392.00|HOUSEHOLD|g courts nag daringly brave pains. blithely special deposits use blithely carefully c\n1016|Customer#000001016|8tzkhPXMFHKgmz|11|21-683-368-2994|2357.54|AUTOMOBILE|ular deposits. special foxes solve quickly idly special ideas. never final asymptotes nag. furiously even deposi\n1017|Customer#000001017|OoVPZGR5hUp8oo|16|26-593-941-5690|-913.70|MACHINERY|integrate furiously furiously even pinto beans. ironically pending packages cajole quickly furiously special \n1018|Customer#000001018|yldxLZOgQwzrXh3t4yktykZZV8v,vK2c6pVr|18|28-450-764-4871|8341.71|FURNITURE|e quickly around the quick, regular pinto beans. regular, regul\n1019|Customer#000001019|VMFs38VlBt01g30PzPyliiAoGHazC4HG74JJ|21|31-502-683-3413|2114.53|FURNITURE|quickly special ideas about the courts use of the pending instructions. furiously final accounts c\n1020|Customer#000001020|DHom,LSHKfYSwLSZv39AooYQHlvbaeztefjwR|3|13-692-286-8158|6914.87|MACHINERY|es. blithely unusual asymptotes sleep ironic accounts. fluffily express sheaves haggle fl\n1021|Customer#000001021|m h2wQbujQnQOrcf109reW0 o|6|16-469-554-5196|1286.76|MACHINERY|courts could hang quickly express epitaphs. foxes haggle above the carefully unusual requ\n1022|Customer#000001022|lP,9H6e6mQwLsWYYr2Y|8|18-733-553-2195|9605.83|AUTOMOBILE|ending packages. dependencies along the slyly pending dependencies wake carefu\n1023|Customer#000001023|w8 oxHcOTUiF8dOr,ktZ05pO7qcHZ8ZeH7|17|27-960-306-5136|7188.35|AUTOMOBILE| express requests. slyly ironic asymptotes throughout the ironic, final packages nag careful\n1024|Customer#000001024|9wLrRS78uOPy7CHW|11|21-508-779-7822|-425.09|FURNITURE| carefully regular instructions. furiously final deposits across the carefully special ideas cajole furi\n1025|Customer#000001025|3T2A1uo8mCqTeO LTW8atjLBLO12nh6lyl|8|18-588-456-4616|3363.46|AUTOMOBILE|to beans sleep according to the fluffily regular instructions. \n1026|Customer#000001026|ktKcS9tV2OC8T42KVqMem NjkNO 4pkXmu|17|27-169-221-8173|9699.28|MACHINERY|totes against the stealthy deposits haggle fluffily after the regular, regular deposi\n1027|Customer#000001027|GNaw4RXXMr|2|12-278-154-5262|4946.21|BUILDING| final requests haggle. final, even sheaves maintain carefully above the even ac\n1028|Customer#000001028|NxmOhIN,w45aogQ1hZSvqoz0 8nrbdkaiZOe|10|20-582-119-3249|1915.53|AUTOMOBILE|ly unusual, even packages. fluffily special foxes across the furiously final asymptotes main\n1029|Customer#000001029|D3TLK5s,gc|15|25-602-810-8723|6252.18|BUILDING|arefully furiously final pinto beans. daringly express deposits \n1030|Customer#000001030|Xpt1BiB5h9o|8|18-759-877-1870|6359.27|HOUSEHOLD|ding to the slyly unusual accounts. even requests among the evenly\n1031|Customer#000001031|dwCYOftUgV5,EwGJc|21|31-946-641-1853|2226.80|AUTOMOBILE| theodolites. even theodolites sleep slyly. special, express excuses cajole among th\n1032|Customer#000001032|6yoIzDrw5zLBO|18|28-449-227-3528|1853.64|FURNITURE| sleep quickly even somas. permanently regular grouches cajole blithely furiously ironic ide\n1033|Customer#000001033|WOozPuOF8UdYMwjF5|8|18-470-380-2978|81.06|BUILDING| to the quickly final packages. carefully slow accounts use blithely slowly permanent requests. un\n1034|Customer#000001034|Fn5qqb64TSKuJWz4f8GpPkF,c3WY3yqjsV,GgHu|5|15-370-179-6631|7349.82|HOUSEHOLD|bout the ironic requests-- packages wake. requests haggle silent, \n1035|Customer#000001035|7yTbQ665G3Bi,6BK0EmQPw,Gc7bZOPk4ncXpo|10|20-376-345-3729|7499.36|AUTOMOBILE|ound the fluffily enticing foxes detect slyly at the furiously final deposits. quickly final instru\n1036|Customer#000001036|fxujgj8DOFO6oKrH|8|18-791-577-7691|1766.23|BUILDING|ins sleep. slyly express platelets cajol\n1037|Customer#000001037|dwgDZPKR5ZuU3HO2sDOS7Ym0oeC8c6Xm|23|33-855-960-2989|4936.25|BUILDING|lly final pinto beans. pending instructions boost careful\n1038|Customer#000001038|yQCza56pNgcF9sxDR HCed22GeEq|17|27-511-101-1611|-509.92|AUTOMOBILE|uriously express accounts. even pinto beans wake. slyly regular requests according t\n1039|Customer#000001039|F602TgKjElSWrZ|10|20-871-886-9220|7618.54|FURNITURE|nding packages use. blithely regular sheaves doze blithely. fluffi\n1040|Customer#000001040|vbJmdHe6U9Pl|11|21-756-109-1482|2860.71|BUILDING|old requests wake slyly! slyly special deposits cajole above the unusua\n1041|Customer#000001041|189f n2lA4|11|21-314-290-3052|7993.98|MACHINERY|uctions are busily along the furiously ironic instructions. blithely thin waters cajole slyly. \n1042|Customer#000001042|S1sh9gyFn21m4zkb4J95GD5|5|15-215-652-3459|9849.87|FURNITURE|ly bold dugouts! pending asymptotes are blithel\n1043|Customer#000001043|HJMn12xn4bl vWC7iVuTRsErYEzlyCO|7|17-266-334-8613|5847.76|HOUSEHOLD|iously ironic deposits cajole slyly busily final account\n1044|Customer#000001044|Eh2e8gLyStrLE7A|0|10-451-459-9620|7291.30|BUILDING|ly across the slyly ironic accounts. even requests \n1045|Customer#000001045|clvGUnQPLbzX 23hemPp24WS1MEtS4z|20|30-120-992-2121|2942.19|HOUSEHOLD|r deposits cajole blithely along the quickly silent pattern\n1046|Customer#000001046|umgqzlyUW3AYz2C39YMhIgf|10|20-890-161-8958|2311.00|BUILDING| accounts. carefully regular theodolites run fluffily carefully e\n1047|Customer#000001047|h5iBRMsym,y6LLSQU2DzNftiET qZ|9|19-146-399-4251|8918.99|BUILDING|lar packages haggle theodolites. thinly express deposits \n1048|Customer#000001048|Mk0ebiw9SaFBTwoib|19|29-757-642-3735|2583.91|BUILDING|s are slyly regular foxes. slyly final pinto beans wake quickly among the regular, spe\n1049|Customer#000001049|bZ1OcFhHaIZ5gMiH|9|19-499-258-2851|8747.99|MACHINERY|uriously according to the furiously silent packages\n1050|Customer#000001050|KgVnjN7Y4HCN5f97HEUp7kYNNTrE3 O|11|21-448-313-4374|-517.65|FURNITURE|ely ironic packages. blithely regular foxes sublate furiously. special requests boost furiously agains\n1051|Customer#000001051|iHS,UFudVOOe|2|12-869-221-1428|9776.39|HOUSEHOLD|cuses boost furiously silent deposits. quickly silent requests integrate quickly bold asymptotes; slyly regular ide\n1052|Customer#000001052|OcXtKS,1Hvf2D0 rPvhw4qXViYOudQ3|13|23-496-475-9040|2837.96|BUILDING|s accounts haggle against the furiously final asymptotes. ironically regular accounts boost. furiously fina\n1053|Customer#000001053|wDJTteyausmZswQAFQot|16|26-400-312-6496|-473.85|MACHINERY|efully enticing pinto beans. final pack\n1054|Customer#000001054|Xgj6QVy2I9FVoSiIbgLf9LIE8XpWI2RtmbGUx|21|31-915-292-9727|8844.27|HOUSEHOLD|y pending ideas. dogged dependenci\n1055|Customer#000001055|Z3AggyEMPME2hqqTfbMC76O0z|7|17-802-131-7180|639.93|HOUSEHOLD|dolphins: furiously ironic pinto beans above the carefully regular foxes nag slyly across th\n1056|Customer#000001056|8u1rnDOcvU109|5|15-325-285-5215|6287.12|HOUSEHOLD|leep except the foxes. packages eat enticingly along the requests. even \n1057|Customer#000001057|xyV8 FbW4xS,JhkxC0dY527tzcMKxM|24|34-750-735-1314|-377.11|AUTOMOBILE|s. furiously ironic deposits against the carefully bold accounts wake carefully even deposits. fluffily even\n1058|Customer#000001058|R0NIEcSVDQ4rNUcCevDrap|19|29-818-620-9637|6807.55|MACHINERY|uctions. slyly express pinto beans are furiously. bold theodolites according to the fur\n1059|Customer#000001059|OHwYMiDjmgeIQXhLlNW,8LIwIEr|23|33-683-418-9460|1547.50|HOUSEHOLD| wake carefully. carefully quick excuses cajole ruthlessly among the ideas. bold, ironic braids are \n1060|Customer#000001060|aWJkU6JJJOvgaKPOAJJc|8|18-290-794-6133|2840.59|HOUSEHOLD|ter the bold, regular ideas! deposits eat. daringly unusual theodolites sleep alongside of the regular, fina\n1061|Customer#000001061|CqLhg io1CpQKhrVHHDhWg1Omrx1hLcpKB6h|4|14-909-417-8324|-258.77|BUILDING|ticing packages maintain doggedly carefully regular instructio\n1062|Customer#000001062|3OYrGEJC1YUa9DP|22|32-207-600-8684|4709.92|HOUSEHOLD|ag. carefully regular asymptotes a\n1063|Customer#000001063|yHVWD7y1Oe1P|21|31-277-349-9036|1663.28|MACHINERY|ress attainments. furiously regular excu\n1064|Customer#000001064|VmFhpV9 aIqPysMHRIWZl|15|25-391-998-4106|1666.07|MACHINERY|gular accounts. thin platelets promise fluffily. carefully express accounts haggle quickly qu\n1065|Customer#000001065|qGBa7X0dOMsKLuYBpShpJVwGyU9rh|22|32-605-226-2449|4663.41|HOUSEHOLD|nts. quickly quick dolphins run\n1066|Customer#000001066|2Ge 0Nk29FlBs1GuBiY84sLvn38mEkAKnM|0|10-333-463-4472|949.68|MACHINERY|requests. slyly final instructions sleep. fluffily even packages cajole pending, final \n1067|Customer#000001067|g25CH,fhra|23|33-764-123-9568|9153.84|FURNITURE|ackages are furiously carefully even dependencies. idly final\n1068|Customer#000001068|ElWdGnnKpmo0sA1Au teWwomSVgG,me|18|28-485-984-7299|737.40|AUTOMOBILE|nto beans. dinos sleep fluffily carefully regular sentiments. final, special packages wake blithely. ironic \n1069|Customer#000001069|PdWrPGSArhnqWQ km65e|21|31-927-711-6278|5465.29|BUILDING|. ironic excuses after the special hockey pl\n1070|Customer#000001070|m0sYmeYs5wLydSS qw542Et32|15|25-894-843-9171|3160.23|FURNITURE| carefully across the express foxes. carefully special accounts ca\n1071|Customer#000001071|PgCAYL2LEwE7v7Pk4dYpRe Nn7MN8wVzYbA2qtj|16|26-350-231-6183|4033.56|MACHINERY|ar accounts. quickly regular packages sleep bold ideas. slyly pending asymptotes a\n1072|Customer#000001072|HpCr1tM88WoELSld708ByJ|4|14-432-882-6163|7979.48|HOUSEHOLD|ic pinto beans are blithely across the grouches. furiously pending platelets sleep pe\n1073|Customer#000001073|KEyFI2gYMZrSVbMMMIf|10|20-774-197-6595|8217.23|BUILDING|fter the blithely regular pinto beans. express asymptotes sleep special pa\n1074|Customer#000001074|nG,eR,gjPr|10|20-176-839-1649|122.67|BUILDING|ly final courts haggle quickly boldly express excuses. dependencies eat. slyly even requests boost blithely \n1075|Customer#000001075|hTIc2AUg pqhYh2W0yMUTQtrZV1KUutysIb6,nxb|21|31-724-234-4181|2714.50|BUILDING|regular patterns. unusual platelets try to are unusual theod\n1076|Customer#000001076|C1gf0FyiU H88P0cpv4UOcdgaPRpVA|10|20-405-710-1902|3509.35|AUTOMOBILE|ainst the silent, silent ideas. sly theodolites use carefully express accounts. regular foxes boost carefully agai\n1077|Customer#000001077|sjk1DTHWVMX53kG8AbTtTh1EcMvWeDO8gFDdpQOK|21|31-367-294-4048|8581.78|AUTOMOBILE|lithely regular deposits. carefully pending deposits sleep. quickl\n1078|Customer#000001078|ZjRzAz8QbEeIkJxrUI,b|19|29-729-692-6790|70.93|MACHINERY|onic requests wake slyly furiously final attainments. ironic, even accounts cajo\n1079|Customer#000001079|cOyd7wsHIQq2LNN|19|29-699-930-2250|2135.91|AUTOMOBILE|ter the express foxes nod slyly excuses. slyly speci\n1080|Customer#000001080|Yux,gs14NpneiZEy9Rz|12|22-806-885-5347|3267.19|MACHINERY|ng dolphins cajole across the carefu\n1081|Customer#000001081|eGGRjZex7YANvD1jfnPMcBK2JbM|12|22-866-942-1021|8647.42|MACHINERY|pendencies haggle after the quickly special instructions. furio\n1082|Customer#000001082|vMX52A1zqDbGNzjfSzgsxSVU0GU6iFmrgiUE|5|15-646-384-2302|3247.92|BUILDING|ly. blithely final packages wake silent ideas. express, special theodo\n1083|Customer#000001083|tnrpYmWGxwyaFmJy2Oq0Z|7|17-159-499-3318|3847.29|FURNITURE|luffily. slyly unusual accounts cajole furiously against the ironic asymptotes. slyly reg\n1084|Customer#000001084|E091r836A8TPqn5|2|12-378-899-7136|1416.75|FURNITURE|nstructions. fluffily pending pinto beans affix slyly; carefully pending requ\n1085|Customer#000001085|pZgtHRGIkUVwiEJLWZXs3KUNi6wLnQzJU1|21|31-831-702-3157|5275.88|BUILDING|he carefully regular courts use special excuses. ironic deposits along the blithely even sauternes nag slyly\n1086|Customer#000001086|ECMZrONto2nI2TBv,k|8|18-399-482-6815|9726.83|FURNITURE|ymptotes cajole enticingly furiously silent ideas. furiously pending packages are al\n1087|Customer#000001087|ETOH68urIxK839xmKEmfkjc|21|31-334-391-6403|5878.21|MACHINERY|s haggle above the slyly express requests. quickly regular packages after the quickly silent accoun\n1088|Customer#000001088|YjXQtOJoM0nhClEy0,WFdNxvJ1g6xpn kL2ommEv|22|32-324-225-2635|2098.62|BUILDING|ly special ideas. slyly unusual requests haggle\n1089|Customer#000001089|OO77  pLjaOe7bam1WnH9gtcZNCUlUPI|18|28-164-765-7462|3429.95|BUILDING|o beans affix carefully regular accounts. quickly even ideas sleep. pinto beans haggle fluffy courts. slyly regu\n1090|Customer#000001090|P2JDHFVxjU|15|25-711-934-6343|5212.43|AUTOMOBILE|ffily even packages wake quickly \n1091|Customer#000001091|4ye7wJ3gU92RZCpwTtDi8Ws,|17|27-336-955-4882|-710.53|BUILDING|y. carefully ironic excuses sleep quickly fluffily even requests. fi\n1092|Customer#000001092|,oAq2L60hjb8|15|25-766-175-4580|2004.15|HOUSEHOLD|carefully silent somas can wake carefully aft\n1093|Customer#000001093|LO,9qCPIjSXriBqQsAOXLrQKedQ8UO6gb|24|34-931-911-6156|-273.96|HOUSEHOLD|p furiously carefully bold packages. regular escapades breach. blithely unusual ideas integrate across t\n1094|Customer#000001094|OFz0eedTmPmXk2 3XM9v9Mcp13NVC0PK|2|12-234-721-9871|2544.49|MACHINERY|tes serve blithely quickly pending foxes. express, quick accounts\n1095|Customer#000001095|JtyQvLlCI ZPYQ6ygv,5q|9|19-881-259-2391|6221.26|MACHINERY|foxes. ironic, daring requests sleep regularly across the blithely\n1096|Customer#000001096|ldbo6AfnCRjFW8rZnvG6UxbX6o7ISAJRDD7|4|14-368-827-9896|3687.37|FURNITURE|lyly even asymptotes cajole furiously. regular, ironic theodolite\n1097|Customer#000001097|a wMc0lQutcHs6cRomoMCGjvM0MwEk4uyrxKI3|6|16-604-758-5574|8651.87|MACHINERY|p carefully. carefully special excuses haggle carefully about th\n1098|Customer#000001098|XVJb1HxQeLu9x|22|32-206-732-5183|1009.22|FURNITURE|evenly unusual deposits. slyly even ideas according\n1099|Customer#000001099|2ZiU64au LN0 GUxY8|1|11-128-186-5241|8990.07|AUTOMOBILE|ckages: blithely ironic theodolites cajole furiously. f\n1100|Customer#000001100|PGXj,,vjAfMNLzd|12|22-880-206-7392|9189.75|BUILDING|ideas. furiously final sheaves integrate. pinto beans haggle slyly according to the furiously ironi\n1101|Customer#000001101|h,UOEyoi1ZG4|3|13-528-469-6051|-842.72|MACHINERY|o beans; quickly express accounts slee\n1102|Customer#000001102|F9fxZhJJhaR0P4Rgd7SE2PA58x|24|34-103-353-4822|2369.01|HOUSEHOLD|elets. regular requests sleep quickly. express ideas haggle. bold, regular ideas haggle. quickly regular accoun\n1103|Customer#000001103|kbYrf d uR|16|26-307-423-8860|4878.10|AUTOMOBILE|n accounts cajole across the even pinto beans. quickly express pat\n1104|Customer#000001104|,t,d8FlnmiECPa|8|18-644-507-8095|1230.47|AUTOMOBILE|ages haggle. slyly ironic foxes are idly among the furiously final pearls. slyly unusual reques\n1105|Customer#000001105|cZhhOUzv6,Vbaa2bFT|22|32-885-298-6750|9491.46|FURNITURE|y final packages. furiously ironic packages was. fluffily ironic instructions integrate\n1106|Customer#000001106|WZEExIU9g2smcowcinj|21|31-214-739-2409|9977.62|HOUSEHOLD|requests nag. fluffily regular packages haggle q\n1107|Customer#000001107|yQBP1SLK2uzN4dzgaQ|1|11-720-869-9052|7961.62|AUTOMOBILE| along the final deposits. carefully express ideas wake? quickly regular instructions are furious\n1108|Customer#000001108|9sPt6a66R0eCRVYh9QrF8zjxNWFFk8KU|7|17-408-450-8891|4997.35|BUILDING|rding to the final instructions. carefully final accounts wake along the carefully careful pinto beans. re\n1109|Customer#000001109|BJCfTYEV9eCDraeyO3v|22|32-194-697-1794|3387.22|FURNITURE|r accounts. bold, final pinto beans wake carefully even Tiresias. quickly busy frays above the blithely ironic de\n1110|Customer#000001110|BRnTy8RZ,1oHOg9ly8SsJLIyiuvhv|10|20-777-225-9349|2041.65|HOUSEHOLD|usual platelets along the quickly regul\n1111|Customer#000001111|gavpg6eW5lEML|6|16-824-312-3537|2892.21|MACHINERY|s are slyly quiet requests. darin\n1112|Customer#000001112|wFf 0nSvdJyk2GqRsqJrcr9 UPr0C3OT5zT|20|30-401-424-6458|9314.59|MACHINERY|fily quickly unusual theodolite\n1113|Customer#000001113|jLtKZ0bRJyYL1k|12|22-412-216-1933|7392.30|HOUSEHOLD|ages among the furiously pending packages detect across the blithely unusual accounts. furiously ironic requests sh\n1114|Customer#000001114|f7 he8eByBFy6z7vcOajC1gaUKqmRN|14|24-630-988-3843|6446.83|BUILDING|ularly ironic platelets. pinto beans along the slyly express packages wake unusual packa\n1115|Customer#000001115|Elvb2a3FinAzxw |5|15-356-145-6356|-178.52|BUILDING|ending instructions thrash blithel\n1116|Customer#000001116|aWuLgbu,8HZMbkI|14|24-116-214-4051|592.60|AUTOMOBILE|tes-- final, regular excuses sleep. sly\n1117|Customer#000001117|80NfzBRWj5tUUaRdnsFE7Eg|23|33-461-439-5684|2829.07|FURNITURE| ironic deposits need to haggle furiously. furiously bold deposits use among the carefully ironic\n1118|Customer#000001118|QHg,DNvEVXaYoCdrywazjAJ|11|21-583-715-8627|4130.18|HOUSEHOLD|y regular requests above the blithely ironic accounts use slyly bold packages: regular pinto beans eat carefully spe\n1119|Customer#000001119|ER5vABifV766q5f0FN7l2eN7MIg2lO|20|30-789-716-6850|3971.65|AUTOMOBILE| pinto beans maintain slyly even instructions. regular acc\n1120|Customer#000001120|UAG90slCmJS7JOP AhlV12tYD3yUiyB1p2hxZ|2|12-938-579-7156|1543.64|AUTOMOBILE|r theodolites boost. slyly final pinto beans sleep blithely unusual accounts. fluffily even multipliers \n1121|Customer#000001121|o2uc3AHYz,m 3vYg8YxBwI0XuG|20|30-197-936-4724|3942.11|MACHINERY|usly? final theodolites are carefully \n1122|Customer#000001122|9lxNEW0Rei4DFaT4vX,T551AwBzrZoOXsRNOm|0|10-257-957-3327|45.21|BUILDING|egular, regular instructions are slyly regular requests. deposits despite the regular, pendi\n1123|Customer#000001123|pO80QGjK7S0Kmoh46dViD K4OSEVDyiJ53CN|16|26-983-192-5480|9786.36|MACHINERY|s carefully ironic packages. accounts boost boldly fluffily even gifts. slyly final fo\n1124|Customer#000001124|EQNw9dNy63,|1|11-709-582-2006|5512.73|BUILDING|ctions wake. packages haggle furiously. express\n1125|Customer#000001125|DrHkeaX6wshtuZOI2nLrME|3|13-807-542-3923|8427.55|MACHINERY|counts according to the carefully silent grouches haggl\n1126|Customer#000001126|8J bzLWboPqySAWPgHrl4IK4roBvb|8|18-898-994-6389|3905.97|AUTOMOBILE|se carefully asymptotes. unusual accounts use slyly deposits; slyly regular pi\n1127|Customer#000001127|nq1w3VhKie4I3ZquEIZuz1 5CWn|10|20-830-875-6204|8631.35|AUTOMOBILE|endencies. express instructions wake about th\n1128|Customer#000001128|72XUL0qb4,NLmfyrtzyJlR0eP|0|10-392-200-8982|8123.99|BUILDING|odolites according to the regular courts detect quickly furiously pending foxes? unusual theodolites use p\n1129|Customer#000001129|OMEqYv,hhyBAObDjIkoPL03BvuSRw02AuDPVoe|8|18-313-585-9420|6020.02|HOUSEHOLD|pades affix realms. pending courts haggle slowly fluffily final requests. quickly silent deposits are. iro\n1130|Customer#000001130|60zzrBpFXjvHzyv0WObH3h8LhYbOaRID58e|22|32-503-721-8203|9519.36|HOUSEHOLD|s requests nag silently carefully special warhorses. special accounts hinder slyly. fluffily enticing\n1131|Customer#000001131|KVAvB1lwuN qHWDDPNckenmRGULDFduxYRSBXv|20|30-644-540-9044|6019.10|MACHINERY|er the carefully dogged courts m\n1132|Customer#000001132|6dcMOh60XVGcGYyEP|22|32-953-419-6880|4962.12|AUTOMOBILE|ges. final, special requests nag carefully carefully bold deposits. ironic requests boost slyly through th\n1133|Customer#000001133|FfA0o cMP02Ylzxtmbq8DCOq|14|24-858-762-2348|5335.36|MACHINERY|g to the pending, ironic pinto beans. furiously blithe packages are fina\n1134|Customer#000001134|79TYt94ty a|9|19-832-924-7391|8458.26|HOUSEHOLD|riously across the bold instructions. quickly \n1135|Customer#000001135|cONv9cxslXOefPzhUQbGnMeRNKL1x,m2zlVOj|11|21-517-852-3282|3061.78|FURNITURE|regular frays about the bold, regular requests use quickly even pin\n1136|Customer#000001136|GHCEiSK0TKsOncuJT3,2zSvlZW4Pz|24|34-440-798-1100|-723.49|FURNITURE|ular pinto beans. slyly special deposits according to the slyly ironic requests maintain quickly \n1137|Customer#000001137|LJ3J3i0BlPLrhKi6VabXxNrtpLAGH|16|26-598-565-1269|4210.15|AUTOMOBILE|usly quickly unusual attainments. stealthily unusual requests cajole ironic reques\n1138|Customer#000001138|8 9P,dIGWnrrDiVs0S|22|32-236-817-2959|6035.44|BUILDING| instructions cajole thinly ironic requests. regular packages affix. ironic, final pinto beans ac\n1139|Customer#000001139|UDGG69rYgUGayNk 9vFytd5q3nZdeRZQNSfL6|22|32-182-662-9475|4604.83|BUILDING|y pending pinto beans haggle blit\n1140|Customer#000001140|leG5nToZpjmWNeaOsVv|20|30-331-754-7359|6319.21|AUTOMOBILE| pinto beans. blithely regular packages sleep carefully blithely ironic requests. requests eat blithely aga\n1141|Customer#000001141|A6uzuXpgRPp19ek8K8zd5O|22|32-330-618-9020|0.97|MACHINERY| accounts. furiously pending deposits cajole. c\n1142|Customer#000001142|b7ytiiX7E9|16|26-191-682-8920|3273.08|AUTOMOBILE|doze slyly. furiously pending deposits cajole fluffily carefully pending packages. boldly regular\n1143|Customer#000001143|9tfTGdYHyZXtXbbeboPIXwCT|4|14-568-471-9747|8655.98|AUTOMOBILE|e carefully final packages integrate against the furiously express platelets. ironic ideas wake above the e\n1144|Customer#000001144|DGLUWG9evYLNbYhOXVzqZ LdfIMVfBjDf|1|11-336-453-4489|4189.04|BUILDING| ideas. even, regular excuses after the ironic requests cajole blithe\n1145|Customer#000001145|6R rPD6SDQPpFuYxxwh,Dv1PeusmP,C6cNcI|2|12-270-756-2968|3249.25|HOUSEHOLD|e. asymptotes sleep fluffily quiet requests. even theodolites among the fluffily regular pinto \n1146|Customer#000001146|DRBYvF0iBGsDC3iPNFsPHq3FkU,jCK8LJPX4W|12|22-720-237-1751|4204.36|FURNITURE|final, pending asymptotes. regular requests was\n1147|Customer#000001147|AVjlczwVwL CT jO3sgWn|15|25-754-809-7107|7734.64|HOUSEHOLD|eposits. quickly express accounts are idly. slyly final platelets wak\n1148|Customer#000001148|7PslyqtS1K2Pabjht 4qgaZ1BbSNFfz6QiK4K|19|29-393-445-2761|7129.84|AUTOMOBILE|c, even deposits. accounts do use. regular accounts haggle blithely special, express courts. blithely\n1149|Customer#000001149|5JOAwCy8MD70TUZJDyxgEBMe|3|13-254-242-3889|6287.79|MACHINERY|ress requests haggle carefully across the fluffily regula\n1150|Customer#000001150|fUJqzdkQg1|21|31-236-665-8430|-117.31|MACHINERY|usly final dolphins. fluffily bold platelets sleep. slyly unusual attainments lo\n1151|Customer#000001151|ratQBQ4rYv TfhWfHe|7|17-948-135-2667|6354.89|BUILDING|l requests. furiously bold orbits after the furiously ironic excuses sleep\n1152|Customer#000001152|QRmFl9ZkoBDQ7|12|22-471-341-5516|5680.15|HOUSEHOLD|oost along the quiet, bold foxes. ironic dinos nag fluffily final pinto beans. blithely regular deposit\n1153|Customer#000001153|SYG3KMj1fMh7GwvIZ,pY7mGLR1NT6EmNjE|3|13-319-420-5160|6244.03|HOUSEHOLD|s. even packages use fluffily always express packages. regular, even asymptotes about the furiou\n1154|Customer#000001154|7RqtNwcSPbaUKaC|19|29-797-132-6916|1498.46|BUILDING|thely. furiously regular accounts above the ironic platelets wake slyly blithely bold pint\n1155|Customer#000001155|kEDBn1IQWyHyYjgGGs6FiXfm3|8|18-864-953-3058|3510.25|MACHINERY|ages? fluffily even accounts shall have to boost furiously alongside of the furiously pendin\n1156|Customer#000001156|3ShFbt9dTbLOG4lUBvc1AZp0Tam0BNjYS qwTZ|14|24-637-724-1410|1799.67|HOUSEHOLD|ns. carefully regular foxes are quickly. furiously careful accounts accord\n1157|Customer#000001157|3rchTZwilGpffMz1MfpnkFfWBtOIdgmvvS1E7sJj|20|30-741-794-9826|6013.09|HOUSEHOLD|equests. deposits cajole quickly slyly spe\n1158|Customer#000001158|btAl2dQdvNV9cEzTwVRloTb08sLYKDopV2cK,p|10|20-487-747-8857|3081.79|MACHINERY| theodolites use stealthy asymptotes. frets integrate even instructions. car\n1159|Customer#000001159|IAnWq4YFKs7|2|12-269-807-3861|5553.75|HOUSEHOLD|ages sleep fluffily. packages after the carefully express packages nag slyl\n1160|Customer#000001160|v65g1aRCGA76ZHySoOBffL31n4vJ0nm,tK,UEA|24|34-103-942-4634|4976.24|AUTOMOBILE| pending, special packages against the blithely unusual packages eat quic\n1161|Customer#000001161| QD7s2P6QpCC6g9t2aVzKg7y|19|29-213-663-3342|591.31|MACHINERY|ly alongside of the quickly blithe ideas. quickly ironic accounts haggle regul\n1162|Customer#000001162|b5N12h9D6yJemoVx6OQf0uL|2|12-887-115-9986|3139.71|AUTOMOBILE|refully furious packages. furiously ironic ideas against the carefull\n1163|Customer#000001163|54fBdElRYOjEH8S|2|12-204-803-1483|90.22|BUILDING|inments. carefully regular instructions haggle carefully slow packages. slyly even packages kindle blithely special\n1164|Customer#000001164|XWfNRnO2S5KAW0VodNwaBDixCEtv nKzt2LVFiwm|0|10-828-178-5049|7341.35|HOUSEHOLD| ideas use. unusual packages sleep\n1165|Customer#000001165|h7KTXGSqsn0|9|19-766-409-6769|8177.33|MACHINERY|jole slyly beside the quickly final accounts. silent, even requests are stealthily ironic, re\n1166|Customer#000001166|W4FAGNPKcJFebzldtNp8SehhH3|17|27-869-223-7506|507.26|MACHINERY| before the platelets! carefully bold ideas lose carefully\n1167|Customer#000001167|gNYGOcGkJu3ooSmsCh|19|29-721-479-1548|9510.87|FURNITURE|lly regular ideas grow furiously regular accounts. regular, special requests sleep blithely. slyly bold pla\n1168|Customer#000001168|gmAnpwPPg0LX4|17|27-608-883-2632|6194.65|FURNITURE|ses run according to the regular instructions. slyly regular foxes around the furiously ironic theodolites use fl\n1169|Customer#000001169|04YQNIYyRRFxUnJsTP36da|4|14-975-169-9356|7503.30|MACHINERY|into beans doubt about the slyly ironic multipliers. carefully regular requests breach theodolites. special packages\n1170|Customer#000001170|BNhssVcV36vshEHUAc aPFJ|8|18-670-628-8499|2070.73|HOUSEHOLD|ronic instructions. express pinto beans poach blithely. quickly ironic accounts n\n1171|Customer#000001171|GatOC LsLU9MkgyaNMYH|8|18-457-394-2863|7658.97|HOUSEHOLD|c dolphins. accounts are slyl\n1172|Customer#000001172|r dreL8m8cRaiIz IqZ83oMo,AVxe2PbsHQzK|14|24-249-588-1969|420.97|FURNITURE|express asymptotes haggle furiously. fluffily special deposits haggle quietly even, special tithes. ironic foxes alo\n1173|Customer#000001173|6Abj72jR5Z0GYQMZKBmiQxW|18|28-409-365-6392|182.59|FURNITURE| ironic accounts above the ironic excuses haggle fluffily furiously regular packages-- slyly regular gi\n1174|Customer#000001174| b9zecNS,J97qi7Qk5|4|14-131-930-7154|8798.96|AUTOMOBILE| ironic packages. furiously regular excuses sleep about the fluffily unusual pinto beans? regular foxes kind\n1175|Customer#000001175|olj7nLYgBZ526MNBg9CV7w LYo6F1uD,Hm54|4|14-756-259-6379|9207.16|FURNITURE| haggle pending requests. carefully regular ideas nag. ruthlessly final packages a\n1176|Customer#000001176|V0xc0tXNMmObuUJ0rGARp6YIw4fo84CD|10|20-141-903-5936|5827.59|BUILDING|ven accounts boost after the accounts. slyly silent accounts use fluffily amon\n1177|Customer#000001177|hZPNQ8a9QRM ,SYdTdoW9hw|14|24-200-701-8606|9281.72|FURNITURE|ng the quickly bold theodolites cajole carefully around the deposits. furi\n1178|Customer#000001178|W,03Nl2iWQ94xYyCo3R8CTMNFhu|9|19-717-739-3103|4966.58|HOUSEHOLD| even requests cajole furiously after the quickly ironic accounts. even re\n1179|Customer#000001179|JLtS3n1xDqnNFS5MZc5uZHOjDctAJEl|19|29-311-833-9211|3336.25|AUTOMOBILE|ress, special accounts sleep slyly about the carefully express packages. f\n1180|Customer#000001180|jI4QtviiCs0,LOgUPH4aONMnoNt|13|23-188-767-6645|3367.36|BUILDING|uests poach carefully carefully final deposits: ironic, regular deposits are slyly busy excuses. regular\n1181|Customer#000001181|ZFFYipzTg0vSjOhPbcBUgPK9se|10|20-330-305-8843|9180.50|AUTOMOBILE|gle about the busily special theodolites. furiously ironic deposits haggle beside the furiously special accounts. \n1182|Customer#000001182|pLrF7F1,uoyGaU|6|16-229-473-7194|8814.39|AUTOMOBILE|jole carefully. furiously final pinto beans detect. f\n1183|Customer#000001183|qdIqRUfpmvtWo0NGsyi4qyjkwzlImP9,NrSC|1|11-968-244-9275|4455.76|BUILDING|arefully regular dependencies. quick\n1184|Customer#000001184|M0dd3R30k0YjIr4GVeo|11|21-661-875-1923|9032.89|BUILDING| excuses nag carefully even accounts. unusual platelets detect carefully bold acco\n1185|Customer#000001185|z,dN83fETWpkJkoR|14|24-860-751-6688|2913.52|BUILDING|ndencies against the carefully even accounts cajole carefully quickly regular packages. even fox\n1186|Customer#000001186|cj5EeLbJJ6MPdynzposq,Apbj9 2Jm|23|33-500-965-3385|4466.30|BUILDING|ding realms cajole after the even foxes\n1187|Customer#000001187|W1GdUKr3vQMVAZIU|10|20-543-260-5157|-932.96|AUTOMOBILE| blithely unusual theodolites detect doggedly. bold dolphins was blithely. pinto beans use carefully at the furiou\n1188|Customer#000001188|PtwoF3jNQ9r6 GbPIelt GvbNBuDH|15|25-108-989-8154|3698.86|MACHINERY|ts. quickly unusual ideas affix aft\n1189|Customer#000001189|rbnZCbJSTE3qWLl|10|20-746-804-1553|3798.28|HOUSEHOLD|enticingly express platelets wake. regular requests boost even, regular instructions. express dependencies a\n1190|Customer#000001190|JwzW9OtxFRXDnVo5hXl8 2A5VxH12|15|25-538-604-9042|2743.63|MACHINERY| regular deposits according to the pending packages wake blithely among the silent inst\n1191|Customer#000001191|K9J7dhIXDB2kgubtIVdRC6RP0aF,GQXin|19|29-587-244-2901|9088.54|AUTOMOBILE|ts wake. waters detect fluffily carefully regu\n1192|Customer#000001192|8DbtM3KloBZ4OO1dRrF99|20|30-904-936-4914|3231.33|BUILDING|efully final packages use. slyly pend\n1193|Customer#000001193|gdKqrIp,yaMaQSFerrGGzc6Kpy|8|18-524-487-2547|-17.10|AUTOMOBILE|accounts sleep carefully. regular accounts wake slyly. carefully regular requests along the quickly pend\n1194|Customer#000001194|NzWKbiPK1oFd7PNz|21|31-155-275-3981|7582.29|FURNITURE|lve quickly according to the carefully regu\n1195|Customer#000001195|71mmXvaWKf|4|14-355-801-7486|9621.49|FURNITURE|l, regular gifts should have to x-ray blithely fluffily ironic\n1196|Customer#000001196|S3sw3q SDWVuUoEFvwv9M|20|30-615-967-7758|6378.67|BUILDING| carefully alongside of the blithely even theodolites. carefully ironic instructions wake after the spec\n1197|Customer#000001197|9A1LTDf0KbR|0|10-254-891-7835|9261.05|FURNITURE|ording to the slyly ironic accounts. carefully final instructions haggle. special, unusual foxes haggle enticing\n1198|Customer#000001198|r0liwpMwaIBQ9 zQjojGylXkJuKUL|18|28-278-515-1034|9593.35|AUTOMOBILE|intain fluffily ironic instructions. regular requests nag fluffily carefully unu\n1199|Customer#000001199|sQJZJRAgYrZY0gPo9fJp6KAbY|16|26-367-212-1737|6503.35|AUTOMOBILE|es. quickly slow foxes boost \n1200|Customer#000001200|2PFysvL4pk80l|22|32-890-210-4199|3765.05|HOUSEHOLD|nent frets. blithely bold pearls thrash across the r\n1201|Customer#000001201|LfCSVKWozyWOGDW02g9UX,XgH5YU2o5ql1zBrN|10|20-825-400-1187|5165.39|BUILDING|lyly pending packages. special requests sleep-- platelets use blithely after the instructions. sometimes even id\n1202|Customer#000001202|xThQDiKdG,0sU IduCCPAgHJfx1PDJwtUQvfU|0|10-788-256-6117|702.73|BUILDING|accounts. fluffily regular requests are. packages among the final deposits haggle carefully arou\n1203|Customer#000001203|9pTq4gggfKoSqQetn0yJR|16|26-370-660-6154|5787.69|MACHINERY|osits nag furiously final accounts. silent pack\n1204|Customer#000001204|QxpCVhq2x0PwW,zgZ AEuFkgb50ryGM|20|30-117-472-8751|9777.19|HOUSEHOLD|ily final instructions. pending foxes detect doggedly accor\n1205|Customer#000001205|1ALD7GN4Iw0Vl5toeM|8|18-185-307-2678|5390.34|HOUSEHOLD|ptotes. silent deposits above the bold warhorses boost\n1206|Customer#000001206|dxzjW0gykcG2kJ gN8hZV02q6U5T6uVNfP|20|30-716-117-6066|8437.76|HOUSEHOLD|ng the ironic accounts. regular requests across the quickly bold deposits wake carefully across the\n1207|Customer#000001207|tDZe2FlIxGjrf9x,n6N1tu0DbWyUkSSu|3|13-572-474-7362|-556.05|AUTOMOBILE|uriously by the slyly regular packages. fluffily final deposits across the quickly express epitaphs us\n1208|Customer#000001208|M uLSFG6IrQkKQxrH5vCbPjglIpB3JC|5|15-789-973-6601|2426.52|BUILDING| blithely bold dependencies detect slyly. carefully silent platelets haggle along the pinto beans\n1209|Customer#000001209|PW00geNpQOiug6dftXfBkpwdAfsmRYsve,b44uR8|4|14-664-771-9006|3551.21|HOUSEHOLD|unts. regular dolphins integrate slyly. regular, pending accounts sleep b\n1210|Customer#000001210|bUTLW1KIHzzQkuOEwUMwEGCQfTQM7aBmUM0|16|26-202-315-9048|8137.66|AUTOMOBILE|luffily ironic accounts haggle about the regular theod\n1211|Customer#000001211|HCACb3Al89h6NqHUJ8qIjhfGFyA4S0c2|18|28-280-785-7324|4723.37|HOUSEHOLD|posits. packages affix carefully after the carefully\n1212|Customer#000001212|kjiVLfadsq6sU3A6MYwySu XZnWzgkiWSa9v3K 6|22|32-462-274-7707|7736.03|HOUSEHOLD|e quickly unusual pinto beans. packages need to sleep furiously. regular asymptotes are furiously. final packages \n1213|Customer#000001213|4ATLYSTcqLfgAJLxL7U|7|17-548-151-7684|8555.12|HOUSEHOLD|ong the deposits. quickly express deposit\n1214|Customer#000001214|EATpN6m rGunAAkWFNSpsqy|4|14-281-851-2904|2935.31|BUILDING|carefully across the carefully ironic asympto\n1215|Customer#000001215|oAvLu8VcRg9FNS9sNmoqU9|16|26-405-743-5405|7795.87|MACHINERY|special packages against the slyly final pinto beans wake slyly furiously final \n1216|Customer#000001216|CSf1BbFhJhucmvftOwYLQACMEqgXj|3|13-673-633-4561|2155.06|AUTOMOBILE|eposits. slyly ironic dependencies haggle quickly. slyly close orbits above\n1217|Customer#000001217|ddk4YC7lmTM,Z3LbX|12|22-191-580-2887|6019.32|FURNITURE|ar instructions. furiously pending \n1218|Customer#000001218|JYNvUpFG0dDy7aJNhl2zLyIxUGqZZ35ncUe|22|32-299-871-1751|8801.73|AUTOMOBILE|packages hang against the unusual, unusual accounts\n1219|Customer#000001219|kP1xK5be a8tW5JRSq0nwJWgKbO|4|14-364-661-8744|774.23|FURNITURE| the quickly even packages wake fur\n1220|Customer#000001220|tbyect2HMX47TzsKy5 ho5|18|28-379-869-1009|8429.33|FURNITURE|sual multipliers. furiously unusual theodolites are. \n1221|Customer#000001221|4mmeiymVdRmz|22|32-185-876-3586|816.50|FURNITURE|express, bold pinto beans. packages would detect alongside of the quickly bold \n1222|Customer#000001222|hn6SzlP4Dq8F89iOXH0tjIgsz0uBCiBM|11|21-709-519-4959|3883.18|AUTOMOBILE|riously special theodolites nag slyly. slyly special ideas sublate quickly across the slyly un\n1223|Customer#000001223|,I0bRfCGE5ssaX4V3|11|21-659-745-8411|-413.03|BUILDING|oxes. bold foxes detect always furiously special platelets. fluffily bold depende\n1224|Customer#000001224|PWOwgZKsBoFJQ7py4HJpdJoSO2,|8|18-794-312-9970|8124.15|HOUSEHOLD| furiously regular accounts. slyly regular \n1225|Customer#000001225|CgaNokxe s|11|21-839-103-4411|8634.92|MACHINERY|elets. bold packages use blithely special foxes. quick\n1226|Customer#000001226|HKR1zog fXW|0|10-251-221-9440|2135.92|FURNITURE|ns. furiously pending packages hinder special accounts. sl\n1227|Customer#000001227|GiT5IrOJ1zJTZErbFt1Jy6Gj|23|33-468-642-3107|3335.72|FURNITURE|fily atop the bold, unusual theo\n1228|Customer#000001228|fV,ZM6pj1nivvbnfseVaWRkB0UYwKgvv|12|22-778-955-6105|5392.30|AUTOMOBILE|s according to the carefully final ideas ha\n1229|Customer#000001229|csvrfGKxtX|9|19-313-452-6076|8328.12|FURNITURE| instructions sleep. carefully regular accounts use furiously. ironic, even foxes wake: busily even deposits caj\n1230|Customer#000001230|Pr7yxcRne6NiloD1oR,d28rwVFRnOoTWeYq9|23|33-786-129-3407|4787.85|MACHINERY|ackages cajole furiously quickly pending packages. ironic foxes\n1231|Customer#000001231|qJWtxdKmKWcR5XgMDn|9|19-316-348-3289|2326.68|AUTOMOBILE|uickly regular foxes are after t\n1232|Customer#000001232|yYXdTto04oLlk04SM|18|28-518-320-7417|8482.51|MACHINERY|. even deposits lose above the even, regular\n1233|Customer#000001233|KdmXav1IIIo|15|25-522-912-6255|3649.49|MACHINERY|ymptotes according to the ironic deposits use around the reg\n1234|Customer#000001234|B3OhbH0MRJE,F0Lc7Jq0Ttv3|1|11-742-434-6436|-982.32|FURNITURE|y ironic instructions are quickly about the slyly silent pinto beans. quickly final dependenci\n1235|Customer#000001235|q 1E JKZqhvUzj48|24|34-549-333-8551|-982.05|BUILDING|ckly. furiously quick dependencie\n1236|Customer#000001236|pTEEPYlnQBzi558CN7LJ5UTdvO|11|21-699-526-9355|3600.95|MACHINERY|, pending excuses wake slyly pending accounts. asymptotes wake fluffily against the ironic, bold pack\n1237|Customer#000001237| CQEeqR ,cVU7Bby|20|30-415-666-7691|8156.62|BUILDING|the bold accounts wake blithely across the deposits\n1238|Customer#000001238|HGCJI27,RIIQcS20,DcJbMQuUmN3Vhzdm|6|16-302-171-7578|4299.22|BUILDING|ly special requests. unusual, special asymptotes according to the blithely express pinto beans wake en\n1239|Customer#000001239|,K7wNII9jhC ,|20|30-786-518-4678|6936.72|FURNITURE| are alongside of the requests. s\n1240|Customer#000001240|XbvhyAXRkuujtESRmxeD9eQpYSkiCa|4|14-650-555-5310|5439.44|FURNITURE|ans above the slyly regular ideas cajole furiously across the regular, regular decoys. furiously final asymptotes s\n1241|Customer#000001241|74mW8ipfvoVPR3PS|3|13-902-876-1609|8654.56|HOUSEHOLD|ructions affix against the evenly ironic packages. slyly regular accounts run carefully. accounts accor\n1242|Customer#000001242|mA8bUqB6WqNEe2nsQXlaHqMqaACj|2|12-521-364-1211|2276.15|BUILDING| regular ideas cajole! blithely express excuses b\n1243|Customer#000001243|g,qSvyYkgjDcCL cxx5qy8hAwhomRq9cYJRvXZ6|14|24-445-165-9851|6271.38|MACHINERY|s, regular packages through the carefully ruthless theodolites promise quickly blithely final pinto be\n1244|Customer#000001244|I3DrbiKwP3dxs1jF0iDwXh|5|15-881-433-2257|-942.80|BUILDING|old deposits alongside of the packages are \n1245|Customer#000001245| xLnSgzY70qTKPF753|4|14-500-764-3702|3196.66|FURNITURE|haggle slyly at the carefully pending excuses. slyly pending theodolites use re\n1246|Customer#000001246|acguUq5BISzqjHB7Bt|4|14-882-141-9354|260.71|BUILDING|haggle furiously. blithely regular patterns sleep quickly slyly even\n1247|Customer#000001247|q5,Og3ezW3jSUtbwK 6qLJPqPwCwdL|0|10-386-173-3167|1696.95|MACHINERY|s against the quickly unusual ideas are blithely packages. accounts sleep quickly. regular\n1248|Customer#000001248|f0X68bItSl|8|18-692-669-1536|6539.15|AUTOMOBILE|theodolites for the unusual deposits cajole fluffily final patterns. caref\n1249|Customer#000001249|x9ukZnNiUM5pBPXyE3epagewVQBPZzxGYD6sMH|7|17-866-269-1165|448.49|BUILDING| the ironic packages sleep carefully express theodolites. even, final deposits across the even deposits nag after t\n1250|Customer#000001250|LBPszo9EVA88sbbdYl7E,MVm7UvoBjmjr|9|19-509-608-4350|780.05|AUTOMOBILE|und the fluffily bold requests. silent, final theodolites solve furiou\n1251|Customer#000001251|4AjU4c4D4AMLwQx,lAJGwBIgmT7oSZwYUv0es3J|24|34-741-256-6399|7267.76|MACHINERY|ckly regular accounts affix slyly carefully unusual \n1252|Customer#000001252|u2OUDBxaayX4WhrftcM,|12|22-604-782-7617|3279.84|MACHINERY|eans might impress about the bold requests. bl\n1253|Customer#000001253|2rEfA2LR6LkUXjoMxIsv58YSHPMjlqr1YXhHSX|1|11-900-587-2067|1222.21|BUILDING|efully regular deposits. bravely ironi\n1254|Customer#000001254|wdGz5Cm DrSdF|18|28-832-851-4673|2676.06|HOUSEHOLD|wind fluffily blithely regular pac\n1255|Customer#000001255|UC6I32JjBU62t4WgDe e2pDYbuM3VAt4MPM|14|24-359-633-2713|6487.71|FURNITURE|s deposits sleep. blithely ironic dependencies wake. blithely even theodolites sleep. blithely \n1256|Customer#000001256|sNx4HcJ35paZik,IN02G7p|15|25-306-342-4782|5012.07|MACHINERY|integrate carefully. blithely e\n1257|Customer#000001257|kX6yufw5dfKrgwQPVwWE7|2|12-824-451-8526|8810.83|HOUSEHOLD|hins. furiously unusual foxes about the regular foxes wake blithel\n1258|Customer#000001258|zK3TKgKVuFCBdjt|3|13-727-588-7092|-301.75|AUTOMOBILE|ove the carefully ironic asymptotes. quickly final Tiresias wake regular packages. s\n1259|Customer#000001259| YQc2RRQJV7kl1zxWg4OiUVU 5GlpB|20|30-930-620-7210|8353.00|HOUSEHOLD|ages sleep blithely regular, final\n1260|Customer#000001260|npdrgr5Yqp0znvQt,Cw07j4BS22RNIANcb3t|2|12-742-408-2980|4991.59|MACHINERY|kly express theodolites sleep blithely across the doggedly regular packages. final, unusual instruc\n1261|Customer#000001261|mWs6m9QwmTOZ|20|30-372-895-4261|5579.81|BUILDING|uffily final pinto beans. ironic deposits according to th\n1262|Customer#000001262|u39WRGDI6AKU|3|13-444-583-3984|2840.36|MACHINERY| final accounts sleep slyly Tiresias. packages are furiously idle platelets. slyly silent requests are acr\n1263|Customer#000001263|MXA4v0xQ9Kt |9|19-690-614-5736|6975.90|MACHINERY|ress dependencies are carefully theodolites. blithely ironic foxes among the slyly bold packages sleep blith\n1264|Customer#000001264|vC1Yg5q O9  Tt5SM7OF|16|26-617-707-6647|3959.28|MACHINERY|y, regular requests above the fluffily special deposits engage around the furiously\n1265|Customer#000001265|CTbTIB ZTYyKUSY42Ksz F33fxKsSG|24|34-945-256-3226|2653.53|MACHINERY|yly pending deposits about the regular accounts pri\n1266|Customer#000001266|LW7shrnoCLUjJKQI8EF7SIFofvvIUmiJzpdS|3|13-832-768-3873|1877.05|MACHINERY|egular ideas. blithely regular requests above the deposits unwind on the slyly dogged pinto \n1267|Customer#000001267|o3dtauyIeWwFRok2whWam0MLjmOdlG1H|24|34-329-328-2500|8616.12|BUILDING| accounts nag fluffily blithely ironic pinto beans. carefully pending deposits dazzle along\n1268|Customer#000001268|SHn6HpO2VXBw3RJFPxjQFGanrsndRwRR2LWdm|24|34-973-735-5374|5152.42|BUILDING|y pending accounts! blithely unusual ideas wake alongside of the regular, f\n1269|Customer#000001269|j2hwJHCMprK8HQdK7DpeUx5SG8j4dfuNR|10|20-818-485-8060|2659.68|BUILDING|promise slyly against the carefully ironic deposits. fluffily unusual foxes cajole carefully \n1270|Customer#000001270|HPcuKCEtUzP3np7 oDR|13|23-473-283-1422|6170.06|MACHINERY|oost along the unusual, permanent pinto beans. even packages integrate slyly according to the pendin\n1271|Customer#000001271|S7fmHdkot3JAv|14|24-698-342-2768|1209.37|HOUSEHOLD|efully after the slyly regular accounts. carefully ironic theo\n1272|Customer#000001272|hzMO9cmypW|21|31-659-617-1632|6865.14|FURNITURE|symptotes. carefully regular requests after the pending ideas affix fu\n1273|Customer#000001273|6RglRQdIV9mF8Tn6ABFmSQl|12|22-594-567-9307|1499.56|AUTOMOBILE|t final packages. furiously unus\n1274|Customer#000001274|eHJnE7ytBm|24|34-152-721-6307|126.97|AUTOMOBILE|nts are enticingly above the furiously\n1275|Customer#000001275|KUtV3oFy2Kyuzs4zT DB,S|20|30-410-174-2034|8972.46|MACHINERY|uternes. blithely express accounts detect around the fluffily even theodolites. even, spe\n1276|Customer#000001276|c5UAVe71MPvmerPafNlpvTBWCewT|16|26-809-582-2064|-761.70|AUTOMOBILE| the blithely regular packages boost blithely regular excuses. final dep\n1277|Customer#000001277|2ETOoQWtvxqp|14|24-502-746-4128|-52.35|BUILDING|oss the packages. packages doze car\n1278|Customer#000001278|OB JBXz5fghXsYEaClW8PZpDmxMVZct|5|15-253-270-5149|9038.43|HOUSEHOLD|foxes. even, special theodolites boost. furiously silent packages haggle? furiously \n1279|Customer#000001279|fkrzLacsqCnwUwgjjttKmY|14|24-742-587-6985|7915.06|BUILDING|tes are. ideas above the carefully ironic d\n1280|Customer#000001280|3AmBFWaqOYt7F|16|26-725-573-7255|3419.66|HOUSEHOLD|eodolites sleep according to the theodolites. slyly pending dolphins among the pending, express platel\n1281|Customer#000001281|pekyJqzeIQKGO8TeLvXgH8HR|11|21-124-963-7614|8182.42|AUTOMOBILE|the furiously quick deposits. slyly regul\n1282|Customer#000001282|qeYHABkf21,5C5OC5it6q|14|24-750-627-7414|8998.82|FURNITURE| even deposits sleep quickly regular\n1283|Customer#000001283|6JeLWEtDERPB,0KzWB,I6Xs8rJXAC8ryFulW5NPC|0|10-203-771-2219|2222.71|AUTOMOBILE| blithely daringly final theodolites. foxes ha\n1284|Customer#000001284|sdj PCsILD6mOJfEuIIbrN52hOHTYWwUUPT|18|28-750-346-1442|-911.40|BUILDING|pending packages cajole carefully! furiously final packages wake. special requests sleep along the caref\n1285|Customer#000001285|5Hy,ajDzJPtZFeJedRSeLN7XGOJtyUy2FI93|19|29-424-835-1463|3061.58|HOUSEHOLD|ly bold ideas affix blithely about the slyly even pinto beans. slyly regular multip\n1286|Customer#000001286|FP3aFvhRMSKfCz3l0h|12|22-374-932-9860|6906.08|MACHINERY|quests. quickly even packages wak\n1287|Customer#000001287|8CaksGsCJOK3oUm1kUsQ|15|25-493-734-3918|7461.69|MACHINERY|e quickly silent courts. furiously even packages among the ironic ideas integrate slyl\n1288|Customer#000001288|wQDTTCkSGxic2d66|3|13-533-256-9320|6603.43|HOUSEHOLD|equests detect atop the ironic deposits. final requests according to the blithely sp\n1289|Customer#000001289|OGb4YMkool8QMr|24|34-409-591-4324|2925.52|MACHINERY|deas haggle carefully alongside of the always even ideas. never unusual as\n1290|Customer#000001290|0Q9URl0Y3rJWt9GYZF|24|34-837-161-2672|8108.42|MACHINERY|s use across the express requests-- carefully bold foxes cajole slyly slyly express pinto beans. ironic request\n1291|Customer#000001291|dg3hkdHiI9zqk7l3242Q28OLLFy,1vZ,|7|17-693-294-2656|8227.37|BUILDING|, final requests-- furiously careful ideas wake busily ironic, even a\n1292|Customer#000001292|QVr2XTDOMzWcLKHtNgrLK|21|31-966-407-1575|5509.11|BUILDING|haggle. special foxes sleep blithely\n1293|Customer#000001293|E79dBMCNl5xXBwtnSsjuBLa16VgrLsKz|12|22-517-223-6566|2565.67|HOUSEHOLD|heodolites boost blithely ironic packages. special, even ideas above the asymptotes wake quickly accordin\n1294|Customer#000001294|EZVIKislr4L0PrBP8LShL|23|33-506-204-7796|-808.13|AUTOMOBILE|deas boost bravely final ideas. slyly even pearls are furiously \n1295|Customer#000001295|kded3b,5e5|24|34-259-484-2624|753.62|BUILDING|slyly final accounts detect blithely regular, bold requests-- blithely final foxes wake blithel\n1296|Customer#000001296|cLAyTJcfD3T4hKW52lIU9yk|5|15-130-485-4234|3034.69|MACHINERY|e slyly ironic requests. final requests\n1297|Customer#000001297|4QnYEe0KXOP3yridKldXROs7jQdMu9tE|21|31-579-682-9907|6074.01|HOUSEHOLD| pinto beans! furiously regular courts ea\n1298|Customer#000001298|ujAPYPBrLW,oIxGpuWmxoTDscSXFOP Tjk|15|25-765-244-1549|3903.54|AUTOMOBILE|er furiously despite the ironic, even ideas. slyly silent ideas boost\n1299|Customer#000001299|vm2THnXrMKrn5xvPL88EMT9QntU|11|21-150-179-4763|808.39|HOUSEHOLD|sheaves promise furiously alongside of the slyly pending platelets. pending dolphins at the furi\n1300|Customer#000001300|VmW1dNLVaQY0ud6Csa5WHWuV8|23|33-581-399-6027|-370.44|BUILDING| x-ray furiously regular deposits. final, silent theodolites are slyly pending ideas. final dependencies\n1301|Customer#000001301|oR0kHfL6GWhF VPD,mM1Jxsd9l3nZEkfDn|10|20-339-347-9046|8966.63|MACHINERY|egular asymptotes along the even, express packages sleep express realms; carefully final packages haggle quickly\n1302|Customer#000001302|vyImQ4AVgv,Rn|9|19-316-212-9313|197.90|MACHINERY| platelets engage carefully! furiously express ideas shall have to use. regular \n1303|Customer#000001303|MarfB1lCCs2MZ8CWdWqCfb|5|15-658-234-7985|2020.15|MACHINERY|express deposits haggle slyly after the carefully unusual packages-- silently si\n1304|Customer#000001304|1sXtodRtFvBd449a2aJ|11|21-638-815-3982|4548.46|AUTOMOBILE|orses boost blithely platelets. fur\n1305|Customer#000001305|xHgwqc1p0eLf5F8JkE7zvYXPHhIOP5IgLRJgR|23|33-396-634-9150|4900.66|MACHINERY|efully. furiously ironic packages cajole slyly bold requests. quickly ruthless requests alongside of the iro\n1306|Customer#000001306|0YRFIqAc5imIKGi9cEYtn6L|12|22-923-551-9639|6464.77|MACHINERY|le. quickly pending accounts detect furiously. packages \n1307|Customer#000001307|L OAVSFQauP87kLdHouM8|3|13-970-299-8199|4344.52|MACHINERY|ts. brave, express packages boost. even, pending instructions nag blithely regular theodolite\n1308|Customer#000001308|Ndovi7D9gJ u1gjQwYOkIARup6VzhQFCHHmSMw|18|28-560-833-2066|9290.53|MACHINERY|uickly even dependencies. unus\n1309|Customer#000001309|xaOhk73bjekYrVc5zZ36c,GuZUxsMHjo7WH9WVe|10|20-821-905-5952|-922.69|AUTOMOBILE|nusual excuses. ironic deposits are furiously ironic frays. blithely ironic platelets are evenly regular package\n1310|Customer#000001310|bN, XpseFnbjZRh3fryWogaudZB|17|27-538-338-3378|204.84|HOUSEHOLD|unts. silently bold ideas against the blithely regular deposits use furiously ironic fo\n1311|Customer#000001311|rcff2L75vK5EOUaPK DiDz6atB|13|23-647-279-5735|8713.24|FURNITURE|nd the regularly unusual foxes. regular asympto\n1312|Customer#000001312|f5zgMB4MHLMSHaX0tDduHAmVd4|3|13-153-492-9898|9459.50|BUILDING|odolites wake always packages. slyly slow orbits lose. regular depo\n1313|Customer#000001313|Ax4TI4jbHvaYUaaFuEUQTiMWQvrjez G|23|33-623-834-3089|889.11|MACHINERY|ely. carefully pending foxes was furiously special, special \n1314|Customer#000001314|auN4t99aykk1AlmJl|1|11-290-301-2722|3218.33|HOUSEHOLD|e special theodolites haggle furiously along the even deposits. final accounts haggle. furious\n1315|Customer#000001315|5J941XxxkE|10|20-941-614-6433|1447.84|AUTOMOBILE|refully bold packages. final, regular pa\n1316|Customer#000001316|nmbpR1rqOdlUDvT6C HXUhm2|5|15-642-801-1329|-158.39|BUILDING| might wake. sometimes unusual requests cajole carefully about the excuses. stealthily final requests wake quick\n1317|Customer#000001317|a6M1wdC44LW|14|24-518-294-8197|8925.08|AUTOMOBILE|s haggle furiously slyly final accounts. slyly bold pac\n1318|Customer#000001318|yrASJAqw67PQxFYVAVsGU|14|24-524-279-4270|5812.93|BUILDING|lyly blithely final depths. regularly even accounts haggle across the carefu\n1319|Customer#000001319|c5M1KcH60UZPYsa|9|19-573-345-3305|4910.48|MACHINERY|se across the dependencies. express, \n1320|Customer#000001320|8gman6hzpuKUsX7mKU9katXpP1ia|15|25-116-108-3791|6407.13|MACHINERY|lent, final accounts cajole fluffily special requests. deposits around the fluffily even packages \n1321|Customer#000001321|dWd3MhPQY3|10|20-571-787-3958|3589.49|FURNITURE|express foxes are quickly blithely bold \n1322|Customer#000001322|35jI39rgIHCI4Pwvpy1beKgL0|13|23-207-256-7245|2621.71|FURNITURE|tes cajole. blithely express request\n1323|Customer#000001323|r9R6okXwQID,|23|33-476-768-7390|6006.81|HOUSEHOLD|uffily even packages. dependencies are. excuses cajole furiously regular foxes. special dep\n1324|Customer#000001324|6qS1ZDpAYr9aED9Yh ggf8ACJcPi7sp|7|17-415-957-9976|7548.88|AUTOMOBILE|t the busy courts sleep quietly above the ideas. final accounts after the regular, ironic pl\n1325|Customer#000001325|Agu uZvi6Xv77 nE7W8|7|17-687-303-1074|9108.61|HOUSEHOLD|n foxes integrate furiously ironic requests. furiously even theodolites use daringly pending deposits; even pl\n1326|Customer#000001326|naLuK8XKUP72msE0e|21|31-373-307-4091|-468.49|HOUSEHOLD| pending accounts impress. regular, express accounts cajole ironically express deposits. slyly regular accoun\n1327|Customer#000001327|LBVMBxjllZpTQd|12|22-920-576-6295|0.97|MACHINERY| are furiously according to the multipliers. pinto beans are thinly. special deposits haggle quickly express Ti\n1328|Customer#000001328|fjKlKFyxTQRJjLeT1Md|10|20-305-428-9878|3264.99|AUTOMOBILE| deposits haggle ironic, bold packages. quickly unusual packages print furiously care\n1329|Customer#000001329|Q3 pefFAcrEYPQ6J AC|17|27-945-826-8003|4645.91|BUILDING|quickly silent requests affix blithely slyly bold instructions. furiously even packages dazzle whith\n1330|Customer#000001330|MGY4P7QIy3|1|11-353-524-1234|3893.14|BUILDING|to beans doze along the furiously final pinto beans. req\n1331|Customer#000001331|mjaArHGsPWg|8|18-140-389-1328|2005.02|BUILDING|ic excuses. requests would promise according to the furiously ironic accounts. slyly final deposi\n1332|Customer#000001332|1JMz4nbClfcxmzPyWyJK|18|28-560-351-6594|3323.04|FURNITURE|ing forges. foxes haggle fluffily. express, final excuses sleep slyly blithely express dependencies. \n1333|Customer#000001333|o9o6lky2KYgFZ2cSx5lyFQYufM1i1d|2|12-154-975-6824|1330.85|AUTOMOBILE|beans integrate fluffily. carefully final pinto beans wake furiously even pint\n1334|Customer#000001334|gZkxQY2Aa3o D6f1O 7nsPdg6BJ3|5|15-493-800-1041|2485.71|BUILDING| unusual dependencies cajole regular, r\n1335|Customer#000001335|VeQAJlVqZgl0adTxSpZ6P2ZVIC0kWokJ|10|20-744-779-7057|8341.67|BUILDING|t slyly accounts. slyly express pinto beans nag. \n1336|Customer#000001336|E4MeTLnSTIOlWkLDwmG7QPf 9Dq|16|26-350-110-5043|1490.21|AUTOMOBILE|ts after the deposits are quickly deposits! blithely regular theodolites integrate above the slyly pending \n1337|Customer#000001337|ACAMJe2Xdw2BCgHrGMd0BX|22|32-528-594-1931|7882.44|MACHINERY|n, express gifts. express, fluffy pinto beans sleep. regula\n1338|Customer#000001338|8Nx5v3cKF MK3ejHdMUgcY,FNZZs1|20|30-763-866-5779|5139.00|BUILDING|te quickly above the regular packages. thinly re\n1339|Customer#000001339|QGiiQ1iMDmLKLAHsZa L68gZFyPXX18a38IS|4|14-904-963-2452|8167.50|MACHINERY|neath the carefully unusual plat\n1340|Customer#000001340|dYRQ2tz0OdH|21|31-872-435-1900|280.29|HOUSEHOLD|against the final theodolites. slyly regular dependencies after the bo\n1341|Customer#000001341|n5dnBrBUHnNEnaglCr9jNvONhG tMPb|18|28-701-221-9569|762.69|MACHINERY|nt requests. ironic, even excuse\n1342|Customer#000001342|FD6UNqfsYMKkf3ZFZdI4EaYMZ|16|26-340-733-2096|1520.34|MACHINERY|y around the final, special foxes. \n1343|Customer#000001343|WtCLJBdycxFOsHyv|18|28-393-594-5247|8303.09|AUTOMOBILE|accounts. blithely pending foxes wake among the carefully express forges. quickly ironic realms wake bl\n1344|Customer#000001344|95XSwEZD22AZln3RB|5|15-307-682-9911|2113.32|AUTOMOBILE|after the furiously ironic foxes cajole slyly unusual, pending reques\n1345|Customer#000001345|31zcobEB,6Li4YDZbnNX|9|19-913-651-7783|8593.83|BUILDING|of the express, express packages. final requests detect to the regular accou\n1346|Customer#000001346|cuwz2Yvj VKYEXjZzfL|10|20-502-685-6183|4524.45|FURNITURE|heodolites after the quickly bold deposits wake according to the regular platelets. ca\n1347|Customer#000001347|7oXery7shMx|24|34-956-232-6103|8476.43|HOUSEHOLD|ular accounts. furiously final t\n1348|Customer#000001348|CgtcDDYMnvsgI1uozRj|23|33-360-732-3579|459.22|MACHINERY|s cajole furiously among the ironic deposits. carefully bold pinto beans wake carefully against the carefully\n1349|Customer#000001349|HvlnFsKOdm39Ge4VPgzE,UN|18|28-950-527-8728|4967.24|AUTOMOBILE|ges. final ideas nag furiously against the fluffily express accounts. \n1350|Customer#000001350|fc,TCo2zqB9T3C5IbaGkfV3,hLqLr|3|13-486-903-2349|3339.51|AUTOMOBILE| regular, ironic ideas are carefully against the silent packages. careful, \n1351|Customer#000001351|NYMFfkNlCGoTeaDrNO9nn|1|11-916-210-6616|3106.00|FURNITURE| accounts after the final deposits sleep fluffily ironic accoun\n1352|Customer#000001352|XW4X8ComPo5mlyrgLn|20|30-631-606-4317|5570.69|FURNITURE|en escapades after the furiously special accounts use slyly regular grouches. fluffily final pinto bean\n1353|Customer#000001353|CzscM6Q8vW6|1|11-109-274-1421|3666.51|MACHINERY| quickly ironic packages. regular, bold asymptotes about the foxes haggle carefully regular pa\n1354|Customer#000001354|rvGErAt5suIqpuxwtL QPAgN7n7Tyv|13|23-969-619-1363|-897.04|MACHINERY| blithely blithely pending packages. furiously pending accounts use slyly. bl\n1355|Customer#000001355|c1r6G98ixzLQkvUV2KphsFwhYvpDo18oToGB|20|30-918-883-1662|2351.10|AUTOMOBILE|anent dependencies are blithely above the quickly silent escapades. requests sleep. final foxes sleep slyl\n1356|Customer#000001356|3SLzAiW4PihnFUE243 AHKkwtL1PCj|5|15-656-712-5740|927.39|HOUSEHOLD|fully pending deposits. carefully unusual accounts\n1357|Customer#000001357|S1bDHNFkDEi,Gbsc3|15|25-242-146-4223|8627.90|BUILDING|osits boost pending packages. slyly pending deposits along the requ\n1358|Customer#000001358|t23gsl4TdVXqTZha DioEHIq5w7y|3|13-264-253-1258|5149.23|BUILDING|sy excuses. slyly express requests detect slyly quic\n1359|Customer#000001359|F5XtTR5KeZ,wAL|11|21-124-833-5784|4069.82|FURNITURE|eposits sleep quickly. enticing packages sleep ironic, ironic accounts. daring, regular t\n1360|Customer#000001360|xeaT6W6D569UKCKU86iK9b6aUanlra|19|29-574-552-4018|1422.57|MACHINERY|nt packages affix quickly furiously regular foxes; quickly \n1361|Customer#000001361|OAHRbO5RS8,yFt16e7glYM4oVEZpf8BefK5DA,7|13|23-104-975-7608|4128.86|HOUSEHOLD| need to promise furiously quickly bold packages. finally express pinto beans alongside of the\n1362|Customer#000001362|FKywgbtf4ib|7|17-801-385-5904|3718.92|MACHINERY|gouts. quickly silent foxes affix after the ironic, special accounts. carefully bold d\n1363|Customer#000001363|mYa,yAtLmW2mCglfc7cZ8LrPuP0|13|23-964-365-7781|-112.46|HOUSEHOLD|silent packages. blithely regular instructions haggle carefully slyly ironic forges? thin, br\n1364|Customer#000001364|INrMv02tUJWFSRMEbBl0oUTtCjry8qUcI8T|19|29-992-959-9626|-181.69|BUILDING|e furiously according to the slyly final ideas. blithely silent excuses cajole s\n1365|Customer#000001365|DOifjgJKjlSgnpPJ3cHLl2yi EseDZbg3|17|27-358-301-5393|2207.81|MACHINERY|ironic requests use blithely according to the slyly ironic patterns. carefully regular excuses about the c\n1366|Customer#000001366|v3YAa1hq4Qc7FdpLg4Jh0b7xo0soyvq1w,Yrb|20|30-193-707-6924|1634.70|HOUSEHOLD|the furiously final foxes. furiously bold depos\n1367|Customer#000001367|gN803k703pZ1YizV5fp6S8|22|32-462-328-6604|5420.32|HOUSEHOLD|riously theodolites. slyly bold excuses thrash slyly final pinto beans. instructions use\n1368|Customer#000001368|4PxJqZUIML EhegD7RXkLY8|15|25-801-622-7438|6376.18|BUILDING|iously regular packages wake according to the slyly final deposits. carefully even packages cajole. carefully\n1369|Customer#000001369|rXTwOzU0a2ak4Nj5L5b1aLij|10|20-232-617-7418|498.77|AUTOMOBILE|ong the ironic ideas haggle slyly above the courts. packages engage blithely. pend\n1370|Customer#000001370|WN7onCgcC,,Lt4dC4C f7SCgnHWSjeTUp|18|28-575-379-5893|9802.04|BUILDING|y across the regular dependencies. fur\n1371|Customer#000001371|H,U3MSp1OTLGIQuW2|7|17-492-673-8157|4943.58|BUILDING|es are after the carefully ironic deposits. silent requests alongside of the furiously even dependencies\n1372|Customer#000001372|WiWQk7DyBtI,hfP0CIZ|23|33-563-510-6488|1796.09|HOUSEHOLD|l theodolites. regular ideas are around the furiously iron\n1373|Customer#000001373|fAfmAacTlPc|13|23-959-476-7310|909.84|FURNITURE|ckages cajole slyly even requests. express \n1374|Customer#000001374|vRPteZtcyV|19|29-869-316-1166|-411.43|AUTOMOBILE|ckly permanent accounts wake fluffily regular packages. quickly express foxes cajole. carefully ironic packa\n1375|Customer#000001375|lpKhW7g QK7Y13sxKlRvRYI7SItbTbcBxae|14|24-620-497-1489|2011.11|BUILDING|requests! even excuses are furiously express deposits: fluffily ironic \n1376|Customer#000001376|VushDntQeYmYLT22vW09rlg5j06B|12|22-972-150-2900|6761.52|HOUSEHOLD|iously unusual ideas. ironic ideas use carefully about the foxes. slyly unusual pinto be\n1377|Customer#000001377|P7aUKm47hbe14nVZSrwZ|17|27-398-963-9520|8839.15|MACHINERY|s sublate carefully alongside of the slyly express theodolites. furiously special instructions haggle\n1378|Customer#000001378|zDULZOX6KrHF6aL1AMsIg0Ivv4Crz|17|27-806-173-2824|2675.73|HOUSEHOLD|ges haggle slyly alongside of the furiously final excuses. carefully regular foxes boost across the regular, ex\n1379|Customer#000001379|rqYSBCMywMKnfcp2DwotVqI|6|16-695-982-9623|1008.26|MACHINERY|sly ironic requests cajole fluffi\n1380|Customer#000001380|a,q fKSYFADxRtRQWSppP8YKp|17|27-641-565-1036|3723.53|HOUSEHOLD|lar instructions boost quickly. blithely regular\n1381|Customer#000001381|HqKfFUD6Ib9yoFM5cIgMxjXaqdJAyKSN5w Od|22|32-418-900-6494|367.82|BUILDING|foxes thrash slyly express foxes. even th\n1382|Customer#000001382|uiTMgqzTPqAPoKQwbnv|10|20-962-576-3853|8898.67|AUTOMOBILE| wake furiously through the pending platelets. furiously pending deposit\n1383|Customer#000001383|bSLtrtrAaAky9GZuKhlQqp8BB|15|25-267-778-1591|2092.61|AUTOMOBILE|s. fluffily unusual accounts against the special theodolites print around the special the\n1384|Customer#000001384|bQI5haTy6PHM8MyRtKSlvU4ixAUg|8|18-788-299-9227|1534.38|HOUSEHOLD|tes. regularly pending theodolites cajole even sheaves. stealthy, ironic ideas are furiously above the even p\n1385|Customer#000001385|4jAtwsWIITPzhTIx7jblhjp9aAzejEGnu|3|13-693-138-5884|2326.75|BUILDING|ithely daring ideas? regular requests wake slyly ag\n1386|Customer#000001386|uByG5EoybI5dNNLzU5uD4Ba|11|21-450-191-9064|9643.87|BUILDING|ages above the busily final packages grow blithely alongside of the blithely even f\n1387|Customer#000001387|GQhAzCMyKiDoel3|19|29-444-890-8990|8541.87|BUILDING|foxes haggle furiously according to the stealthy ideas: slyly special accounts about the requests use caref\n1388|Customer#000001388|WaKdgWEru70hsL8nyLeEkneHyM59Lboo5zfWv|13|23-185-747-9502|291.44|FURNITURE|longside of the ruthlessly regular dugouts. slyly ironic pinto beans wake. dogged de\n1389|Customer#000001389|ORf,IQyXsXJ1svlQ,5U|19|29-865-304-6982|1111.02|FURNITURE|posits. accounts are carefully. carefully express deposits cajole-- slyly bold dugouts wake according to \n1390|Customer#000001390|fQm,RnwO4Tt PMQIB|11|21-978-977-8988|3931.31|HOUSEHOLD|ress platelets poach carefully above the slyl\n1391|Customer#000001391|7MqN5yFijW6Yua7LVU6i7QMjjiyJ2KTZEaQ|23|33-558-545-3053|5510.28|FURNITURE|ges haggle slyly across the carefully pending accounts. slyly regular accounts should sleep. slyly express packages\n1392|Customer#000001392|iXmNoe7IBgjc|22|32-561-640-4912|249.82|HOUSEHOLD| sleep fluffily across the final, pendi\n1393|Customer#000001393|zVp5Hbhro,9rTwCYys1HUk|24|34-953-819-7858|5672.05|FURNITURE|escapades. fluffily ironic packages nag among the slyly regular dolphins. special asymptot\n1394|Customer#000001394|eE8wv lYYKLXB|3|13-580-581-6470|2233.10|MACHINERY|regular accounts cajole never above the even, final instructions. furiously regular foxes wake unusual requests. ca\n1395|Customer#000001395|XJoxiYIaBYgEE|23|33-291-909-3901|8733.39|MACHINERY|tions sleep carefully. furiously final requests about the regular excuses a\n1396|Customer#000001396|M4dHuyrttFfeBr|3|13-523-516-9742|7149.43|BUILDING| the even theodolites integrate fluffily regular dolphins. blithely \n1397|Customer#000001397|1bk KBemIEsKhD3VyXa6IRLx 4GH u|8|18-294-992-6523|5466.83|AUTOMOBILE|onic packages across the bold, regular dolphins boost furiously furious multipliers; furiously specia\n1398|Customer#000001398|K1rQq6exc3WcVCcgIjA4SaeqxtK2,HG1|8|18-377-181-4654|7004.90|HOUSEHOLD|the blithely silent dinos. even, special hockey pla\n1399|Customer#000001399|FOuY,endAFj|0|10-775-919-7154|7352.14|AUTOMOBILE|foxes across the silent platelets haggle fluffily special requests. unusu\n1400|Customer#000001400|BuouRkR7J f|0|10-217-180-5310|2432.73|BUILDING|etect fluffily final courts. carefully special instructions\n1401|Customer#000001401|C4vlB8ENikVmaMizX3nH3zgds6|9|19-339-404-7859|8908.63|BUILDING|accounts use furiously unusual pinto bean\n1402|Customer#000001402|F7 m0JwiCABmbJLPQpCJ2|6|16-713-144-2780|4396.25|AUTOMOBILE|g the carefully express dolphins: special, pending packages affix after the packa\n1403|Customer#000001403|,ql804gtMc3uxTfP,lt4yRBWQ|12|22-458-624-2509|9782.34|HOUSEHOLD|tes are blithely carefully bold pac\n1404|Customer#000001404|pIO5i3yjeODChGMHoVvrX,Ctpdj|12|22-320-701-5582|3828.46|MACHINERY|ven platelets use quickly pending requests. busily busy asymptotes sleep slyly across the\n1405|Customer#000001405|i9khsGcg17kWI4q5LKTcc8U3aFojf403|16|26-285-488-6682|3987.39|BUILDING|haggle slyly; regular, final excuses are blithe\n1406|Customer#000001406|g1xS4snd0fzl4R,JmPHfEzRD|5|15-767-155-6419|2023.59|HOUSEHOLD|uests are furiously carefully express packages. slyly permanen\n1407|Customer#000001407|zZsTZ3nI1rG5X|14|24-529-300-1554|7424.99|BUILDING|nding, ironic instructions promise across the quickly regular r\n1408|Customer#000001408|NMIb3p1DyU,Z4XOFUS0B,|11|21-901-381-6344|5920.09|HOUSEHOLD|express requests believe. pending, brave deposits sleep furiously. carefully regular deposit\n1409|Customer#000001409|jzfaCksWUNlI|11|21-667-401-3780|1959.17|MACHINERY|ously ironic ideas are. unusual packages kindle along the dugouts. unus\n1410|Customer#000001410|yEUlreh6mkGmg8SIwKZooUOJ42kuZwAptaR4HAJ|22|32-358-270-1819|2716.95|HOUSEHOLD|lar, express packages cajole bravely permanently final packages. blithely even dolphins nag finally special theo\n1411|Customer#000001411|4iLVKtSmtJpU|21|31-898-640-7625|772.14|AUTOMOBILE|sly final ideas. carefully bold pinto beans wake: slyly regular packages must sleep. final platelets inte\n1412|Customer#000001412|gfsI6WU i7kYypv09gGIqUFrUod9uhb|10|20-715-510-2804|6368.38|BUILDING|fully regular accounts print ironic, regular platelets. deposits promise slyly. express, ironic ideas \n1413|Customer#000001413|9Yh cGpbCbrXZytNfH,dAEwX|15|25-624-816-9010|1387.83|AUTOMOBILE|onic foxes. quickly final dolphins are fluffily. quickly unusual ideas wake carefully. furiously\n1414|Customer#000001414|2 HBoqTD0qCyMKtcBPVHbNna|20|30-323-797-7514|3136.15|BUILDING|ackages. blithely unusual accounts a\n1415|Customer#000001415|x,hzUUAZ9w7ndksLyH0,fEpMfU|21|31-295-601-1598|6252.12|BUILDING|heodolites. furiously pending requests are above th\n1416|Customer#000001416|ovOZcFGL31uxmA2ifIYudX6OuwNDz,B|7|17-306-898-9363|5348.40|MACHINERY|unts wake slyly excuses. bravely even pinto beans across the furiously final de\n1417|Customer#000001417|1BDU8AvljnLmkM|1|11-242-612-1339|7543.01|FURNITURE|ag slyly-- furiously final accounts are ironic instruct\n1418|Customer#000001418|S5uBtE hDxHcHunowPDXKSxP3csMFnhYt|17|27-773-818-3164|9359.01|AUTOMOBILE|ggle quickly blithely thin excuses. final, even accounts integrate slyly. carefully pending account\n1419|Customer#000001419|JM4NV2pq4Ps0xJNRtUtlmQ8uuDvKx|4|14-533-796-5446|5912.72|FURNITURE|. idly express pinto beans sleep above the deposits. excuses use: furiously bold accounts cajole slyly across the ca\n1420|Customer#000001420|mjkRUOEzdCWpNdpp5PKOObMmhpufeNGnO1VFdbpK|9|19-433-305-7356|-932.09|AUTOMOBILE|the instructions cajole carefully. slyly final requests nag carefully ac\n1421|Customer#000001421|Qx9tZ9yiMo|12|22-139-990-1907|7292.93|MACHINERY|kly even ideas cajole carefully quickly ironic \n1422|Customer#000001422|mzXw44ExYC8DAdeKBakiWy0II|17|27-270-833-4320|8389.50|MACHINERY|mong the evenly express asymptotes integrate slyly brave\n1423|Customer#000001423|9BcCj8CLsqylKxRj0,lm|21|31-624-875-9135|2411.69|FURNITURE|he carefully express courts. regular instructions haggle. special, express accounts believe? packages cajole slyly.\n1424|Customer#000001424| 3QsPgbVLZ|22|32-542-134-6212|7207.70|FURNITURE| instructions against the furiously express accounts doze fluffily unusual d\n1425|Customer#000001425|I2UBZAPPdnA9oFKKJTGxaSQZb5QzNzTR4vN6d P|1|11-697-824-4418|5814.72|FURNITURE|uests boost carefully even foxes. accounts along the slyly ironic requests cajole express, final deposits. sl\n1426|Customer#000001426|d1Tyzg,0ArKPuBln8CDH, 1Xsukm2nXVl|15|25-629-292-9022|1965.38|HOUSEHOLD|s are slyly according to the express, spe\n1427|Customer#000001427|dDsmiig0T4oFKaf9ttFeh1etLvSIc9aV1xF2H|11|21-941-208-2485|8136.53|AUTOMOBILE|es detect accounts. slyly bold theodolites wake ironic, special accounts. instructions cajo\n1428|Customer#000001428|3SdWi3lKPXk00UYT,hL|15|25-859-663-3690|7703.84|FURNITURE|iously final platelets sleep slyly ironic instructions. furious\n1429|Customer#000001429|K5sID 6zGPrYdfoADUq4kidlPgF3|3|13-119-903-3814|6444.82|AUTOMOBILE|even requests among the blithely regular pinto beans use across the ironic accounts. slyly ironic\n1430|Customer#000001430|mv 9MEDwd8yPeQj7N|0|10-209-317-6929|-920.40|BUILDING|nic deposits. bold, even accounts cajole blithely\n1431|Customer#000001431|l3LM2d2T1n c7yI4sOfpEbbd540qO66A4MARk|13|23-640-395-7009|5805.81|AUTOMOBILE|eposits up the carefully bold requests mo\n1432|Customer#000001432|,pbQM2fi642oAuel|21|31-831-635-9758|6314.25|MACHINERY|ts sleep. regular frets sleep carefully ironically special dolphins. carefully bold pinto beans\n1433|Customer#000001433|gK7D76v78U iRA2YI2kxeKLlm4LZMH13,|17|27-558-375-8169|4605.87|BUILDING|ly bold deposits. furiously silent braids alongside \n1434|Customer#000001434|V15TQAhSLp7YC3KdjuMwSV3cwg0lp|18|28-749-743-6583|1851.63|MACHINERY|lieve carefully ironic hockey players. special ide\n1435|Customer#000001435|s0fqxkVVqLWR  IaqibwwOf|20|30-309-437-9265|-729.09|AUTOMOBILE|usly final orbits are? unusual, ironic accounts are slyly. unusual requests sleep after the ironic,\n1436|Customer#000001436|kV5m0jkgFEto,|10|20-918-593-1860|9158.91|MACHINERY|theodolites. final Tiresias after the quickly final packages dazzle carefully blithely bold \n1437|Customer#000001437|DjHgTwtlzCmcQo|17|27-805-486-5768|8839.32|BUILDING|y. bold pinto beans affix carefully unusual pinto beans. carefully bold Tiresias mold \n1438|Customer#000001438|Gz Aey8gzHxIIxtpJaG0lAqd82T|17|27-688-787-7928|5436.81|FURNITURE|odolites cajole slyly. furiously unusual requests boost furiously along the fluffily pending in\n1439|Customer#000001439|IGGK4SXvT5ioAeT2fbVYDemsTTqqhsQu6|21|31-433-694-1822|8487.60|AUTOMOBILE|ely after the carefully bold accounts. carefully ironic packages are silent packages. blithely ironic pinto beans ar\n1440|Customer#000001440|k3LXBO5QJrG94TBG77adB1HjqQkleDyUf2c|7|17-619-730-9883|1236.36|BUILDING|xpress, even accounts integrate. ironic, special requests doze. carefully express instructions doze furio\n1441|Customer#000001441|u0YYZb46w,pwKo5H9vz d6B9zK4BOHhG jx|23|33-681-334-4499|9465.15|BUILDING|nts haggle quietly quickly final accounts. slyly regular accounts among the sl\n1442|Customer#000001442|2fTQpX7N2kp31U|16|26-677-746-7145|7917.90|MACHINERY|ess theodolites. furiously express pinto beans alongside of the\n1443|Customer#000001443|qtBPSM2NvmJXNePBT Ap3M6UqIZTvaF|10|20-959-383-4792|7141.87|HOUSEHOLD|ickly about the sly foxes. furiously bold patterns sleep regularly across\n1444|Customer#000001444|8WcsyfQU5svH9miWvYbSTH9|7|17-107-228-8125|-501.37|HOUSEHOLD|inal platelets. quickly ironic requests do are carefully carefu\n1445|Customer#000001445|5y7gtM75FOfTSBKx9gs9c9MkqJt|21|31-151-251-1931|8367.94|HOUSEHOLD|carefully regular accounts after the b\n1446|Customer#000001446|p94EVXQW,Q3bhXDyhG1Gp96b5zbaW|23|33-873-120-5388|2981.48|BUILDING|gle ruthlessly. furiously express dolp\n1447|Customer#000001447|pHkyNkViDja,dZVNg4bEEbicpoHIVDZvtQi8RPl|17|27-452-251-2941|2718.02|BUILDING|ously around the accounts. packages haggle blithely ironic, idle ac\n1448|Customer#000001448|a45QD J55bo35zA4qR3v|24|34-969-612-1458|7756.35|AUTOMOBILE|y ironic instructions? slyly pending platelets hang quickly slyly ironic ideas. blithely ironic instructions a\n1449|Customer#000001449|lNFczqF3TjlSO9BuO3jqXY,b|22|32-827-813-3340|9051.75|BUILDING|ructions wake slyly ironic notornis? slyly express courts wake along the slowly \n1450|Customer#000001450|z7Pl iXBEstivMNf|3|13-443-688-6724|857.70|MACHINERY|ains; daringly dogged deposits across the furiously regular instructions breach furiously foxes. carefully unusual a\n1451|Customer#000001451|Yt69m0Aw1LWZhisHJxL4iGEEzx6y,ehspkes|2|12-590-121-9328|3274.30|FURNITURE|ously regular packages. furiously final deposits boost. slyl\n1452|Customer#000001452|51mhHAvPHZACedHYXVU5HXoDIQtBK9,pxuOIlJ|7|17-581-575-4538|7086.97|FURNITURE|silent theodolites. fluffily special\n1453|Customer#000001453|FTfWkW1 8jVgOIIR9sMm2HpohiuR1v2278|0|10-852-397-3642|662.67|BUILDING| to wake above the blithely regular foxes. d\n1454|Customer#000001454| wwPhUG35PiIVasu88,RvDA|24|34-478-555-5955|3366.61|FURNITURE|ss the blithely ironic deposits. regular deposits after t\n1455|Customer#000001455|MrDN0cvhoLZ ioRLZCR hPcY4WvILz2|3|13-839-360-1866|7591.90|MACHINERY|ironic instructions: ironic pinto beans acros\n1456|Customer#000001456|zKDB5elqlAQoUQp|13|23-171-834-8997|6123.69|AUTOMOBILE|unusual accounts wake. even, ironic packages wake carefully. regular p\n1457|Customer#000001457|qmpteVs7H9WjRow7FDut9a77oFKRDOXxq0JmG|23|33-660-953-7656|2873.49|BUILDING|onic accounts nag blithely among the regular, regular pinto beans. carefully regular\n1458|Customer#000001458|vsGifZH3fNgJjlgF6jJbmkSqGc|3|13-392-503-9207|2716.80|AUTOMOBILE|ests sleep bravely ironic accounts. quickly regular accounts cajole \n1459|Customer#000001459|2sPwjFNEFf9dN4az|11|21-424-586-6295|9270.88|AUTOMOBILE|ounts use blithely. blithely pending packages use ironic deposits. final accounts boost slyly. care\n1460|Customer#000001460|AEgBZGLmuMqe7Gqh1|20|30-151-388-7118|9680.51|BUILDING|accounts. ironic packages cajole furiously; quickly pending requests lose quickly carefully bold deposits. \n1461|Customer#000001461|MMmT5l0zAilFCb2ZMqsUm3TXlRH|3|13-393-444-1533|8460.48|AUTOMOBILE|ress, unusual packages affix carefully carefully final ideas. blithely special instructions nag even deposits? f\n1462|Customer#000001462|b9Ed,6BCKn5v37q1|17|27-153-195-4457|7305.88|FURNITURE|ously slyly express requests. spec\n1463|Customer#000001463|WD3OuRpJ0NVj2qslrTkUPmeJqVx5|7|17-980-394-5868|6039.64|MACHINERY|s. blithely even courts wake quickly: quickly silent pains doubt slyly. slyl\n1464|Customer#000001464|5kOAfK9s6goOZabgSzNLgD,CILowRxqC2OLnV|14|24-133-117-1577|9306.20|AUTOMOBILE|iously furiously regular tithes. boldly final requests use carefully at the f\n1465|Customer#000001465|tDRaTC7UgFbBX7VF6cVXYQA0|8|18-807-487-1074|9365.93|FURNITURE|s lose blithely ironic, regular packages. regular, final foxes haggle c\n1466|Customer#000001466|Fdm3uYarZ0Tnnh9R|17|27-360-496-5041|1268.69|FURNITURE|nts along the blithely bold instructions boost carefully after the unusual depos\n1467|Customer#000001467|GE,jQi5oLlkzh4jIUct7r 3C5G|24|34-941-824-8063|2857.19|FURNITURE|. final, bold deposits sleep furiously. unusual instructions are final requests. quickly final sentiments \n1468|Customer#000001468|APEd1ssFxDC9fhwosxxeQUul5EhwBczX|12|22-901-280-1023|3826.52|BUILDING|s. slyly regular theodolites aft\n1469|Customer#000001469|yLW8qLv25wuMsibRd,1qJe9|7|17-961-583-4658|4329.98|FURNITURE|y even dependencies wake against the regular, final excuses. packages haggle slyly a\n1470|Customer#000001470|8ufZxZ5IgwGrUM2CWfxYoRHuBi Vj8rY|17|27-350-836-5521|7033.49|HOUSEHOLD| excuses are slyly after the carefully bold accounts. unusual pinto beans boost. final accounts wak\n1471|Customer#000001471|lbRP,tSo,eQT6rDDNNIBx|5|15-230-827-4758|3872.86|AUTOMOBILE|thely according to the carefully ironic foxes; packages according to the quickly special deposits wake fur\n1472|Customer#000001472| Eayx9GAqjJEwrGy1Er5 ffNtLL|5|15-464-411-8342|2168.61|MACHINERY| haggle against the carefully bold theodolites. quietly regular ideas haggle. pending pinto beans engage sl\n1473|Customer#000001473|UPkONG9dy4VYyGNJGHG|0|10-891-555-7734|2796.93|MACHINERY|uriously. quickly pending multipliers maintain slyly silent excuses. regular requests cajole qui\n1474|Customer#000001474|KB83CaaM8DRjvAmEMg1fw|16|26-609-226-4269|2961.79|HOUSEHOLD|kages above the requests sleep furiously packages-- deposits detect fluffily. pending th\n1475|Customer#000001475|4tUf4SaYTFV2H7ji|0|10-932-794-2009|1820.28|BUILDING|uctions sleep blithely bold packages. pending, silent deposits after the fluffily final pinto beans ar\n1476|Customer#000001476|nsPnedR1dhWK,|16|26-621-638-1459|409.72|BUILDING|across the fluffily final requests. regular forges haggle furiously r\n1477|Customer#000001477|nUT6kGEr7tmgpJaPgfFtXY|6|16-407-756-8079|9103.33|MACHINERY|ites nag blithely alongside of the ironic accounts. accounts use. carefully silent deposits\n1478|Customer#000001478|x7HDvJDDpR3MqZ5vg2CanfQ1hF0j4|7|17-420-484-5959|9701.54|AUTOMOBILE|ng the furiously bold foxes. even notornis above the unusual \n1479|Customer#000001479|KDZMMuMVSWQPkGpoTUE0G 1vXHd3mS4c,A,kFR|16|26-203-849-3685|9793.29|BUILDING|arefully final ideas. unusual accounts sleep. final packages wake. fluffily bold dependencies hang slyly. bl\n1480|Customer#000001480|Hzjh65ZXBFSzflrjQgECkrp35gDha,2|7|17-573-775-8796|876.02|HOUSEHOLD|uriously pending courts are. deposits serve quickly blithely final excuses. slyly reg\n1481|Customer#000001481|Vp7Um1Vy7MNVJvP 5cqUrz8scGtcaLJB3f5bZDW|12|22-674-694-9039|3204.67|AUTOMOBILE|lithely. idly ruthless packages wake above the bold, quick pinto beans. regular ex\n1482|Customer#000001482|kTcr5JgkjFeLKIRcmtnCvOFr1feN59chP7|19|29-452-962-5934|2930.53|FURNITURE|are. slyly regular deposits mold carefully above the blithely regular ideas. carefully r\n1483|Customer#000001483|ZjY1C b6cOnY3|7|17-202-113-4814|4409.70|BUILDING|nts sleep around the carefully express theodolites. requests nag \n1484|Customer#000001484|WcOint654aJStnQWSgAAtI|18|28-987-505-1842|4883.17|FURNITURE|s against the furiously special packag\n1485|Customer#000001485|oR6sZslMa7bPLxtHFhqdJt|24|34-329-123-7678|9412.02|BUILDING|pliers. ironic requests boost slyly carefully express ideas. blithely ironic foxes af\n1486|Customer#000001486|7A2MhrNtsA|24|34-559-605-2237|5859.97|BUILDING|lithely ironic dependencies haggle quickly b\n1487|Customer#000001487|AJXUi2qFVKfypmmpTEbkmjmz0gPKQ2|17|27-197-562-5547|3589.16|FURNITURE|y final instructions. regular, regular packages boost alongside of the b\n1488|Customer#000001488|DtF2uJI8td2wqrumD|4|14-892-461-5341|7929.51|HOUSEHOLD|sits boost quickly fluffily even pinto beans. slyly e\n1489|Customer#000001489|yM8biIU5IFKHODCGTCwdkUf|9|19-906-669-4354|4389.66|MACHINERY|ckages play carefully? permanently regular pinto beans\n1490|Customer#000001490|vBUkY7eCyWP|20|30-326-598-2437|8997.60|MACHINERY| bold orbits boost slyly according to the carefully ironic accounts. slyly special packages whithout the \n1491|Customer#000001491|GjZIP4Fv5lqDt|4|14-931-281-5631|3739.82|HOUSEHOLD|efully silent tithes. even deposits according to the unusual, even platelets haggle furiously a\n1492|Customer#000001492|2QNz4Zy0UjjI|1|11-527-949-4092|-875.17|HOUSEHOLD| blithely even accounts. furiously final instructions across the decoys cajo\n1493|Customer#000001493|FbV 8Ug9GkSfMde5b|24|34-947-154-7032|7014.12|MACHINERY|carefully quiet requests lose slyly. quickly final pinto beans haggle bli\n1494|Customer#000001494|4V71P ku3jrqBfQp|11|21-248-166-9549|8292.21|MACHINERY|arefully furiously special ideas. pending deposits above the blithely regular excuses wake slyly car\n1495|Customer#000001495|78w5H7VJSo0Ps,jqeoCWS4Kay17ygM4RtIH|10|20-416-910-7075|6227.55|FURNITURE|osely blithe, ironic foxes. regular dependencies use blithely about \n1496|Customer#000001496|ZOyMxutVHpJy|3|13-802-978-9538|-496.49|AUTOMOBILE|counts wake slyly above the instructi\n1497|Customer#000001497| D8e2U3gYd57H4grcOr,02|14|24-506-574-8552|2449.57|AUTOMOBILE|gular packages boost foxes. blithely bold escapades wake slyly special pack\n1498|Customer#000001498|x XToT5oFi7oIsRG2mgIL3ncvYJoWBsufsQ7N,z|19|29-676-227-6356|5810.56|AUTOMOBILE|ackages are slyly unusual req\n1499|Customer#000001499|4,6jWOEqfnuXkwhB7gs0M9TcWJlaJNv4bt|3|13-273-527-9609|9128.69|AUTOMOBILE|ole blithely permanent instructions. carefully even packages\n1500|Customer#000001500|4zaoUzuWUTNFiNPbmu43|5|15-200-872-4790|6910.79|MACHINERY|s boost blithely above the fluffily ironic dolphins! ironic accounts\n1501|Customer#000001501|tLJmtj5OgXCQM|10|20-489-284-9686|9734.53|BUILDING|longside of the furiously ruthless deposits slee\n1502|Customer#000001502|FIsFVFApqxzRHQrRjAlODHWTDZc35,BD0c7CuyVy|13|23-873-733-3833|3361.88|FURNITURE| carefully express requests. quickly even in\n1503|Customer#000001503|9fFMPuIIatxmXEDe4XCu4PRea9|2|12-957-226-3187|5164.52|FURNITURE|odolites. express notornis detect blithely unusual, regular d\n1504|Customer#000001504|suueZs7bAberaafllLS|0|10-462-929-6039|8151.61|MACHINERY|ess theodolites. blithely speci\n1505|Customer#000001505|SFczFxAak1xX,CmWAE|21|31-344-990-8260|8207.39|AUTOMOBILE|yly above the accounts. even deposi\n1506|Customer#000001506|RUScjIPOHpz3it|1|11-381-308-9658|-373.24|MACHINERY|s. furiously unusual excuses sleep slyly. blithely unusual packages about the slyly regular accounts are accou\n1507|Customer#000001507|KtVNuytlncvuV44YzpoB|10|20-694-294-7077|5801.76|BUILDING|symptotes around the blithely ironic requests may boost blithely during the bold, final ideas. ironic pinto beans sl\n1508|Customer#000001508|E7fRkt7uXJHIR8akfmor42eTm5kZH|4|14-740-990-2746|4213.74|AUTOMOBILE| fluffily pending asymptotes. blithely even foxes nag slyly slyly pending platelets. carefully regular req\n1509|Customer#000001509|LQY2i,MHY8czRV2Ize|9|19-226-262-5083|328.44|BUILDING|tipliers serve quickly furiously express excuses. furiously unusual deposits slee\n1510|Customer#000001510|BVNRoS0TPt7yBxD|1|11-138-490-8934|7100.18|MACHINERY|ng the quickly bold deposits. regular, even deposits use silently. special, regular packages mold slyly regula\n1511|Customer#000001511|Lh9VKgOjqeJ5P5veH6NKZG3We|4|14-230-666-6671|2757.29|MACHINERY|xes. ironic, special dolphins against the regular accounts haggle carefully across the f\n1512|Customer#000001512| FhwT 40,zugIGQPtYUDkjvXct070xNX4Lze|16|26-502-737-9941|7729.48|HOUSEHOLD|l accounts. final deposits use carefully slyly regular deposit\n1513|Customer#000001513|CkEgq3Yvj9kGkHvVeUELT1UP9HBnHwiEIFzRWNTA|10|20-670-367-4252|8434.13|FURNITURE|y regular accounts cajole blithely\n1514|Customer#000001514|2dVI195Lf,EUjr1CY37GWxCxb0uUjEa|8|18-602-992-7324|4566.48|FURNITURE|ngage slyly alongside of the f\n1515|Customer#000001515|QS USHJ02MP2yd7TIcCNGMyXjyQug0EIShDlUM|12|22-852-688-4287|-179.31|MACHINERY|ily: regular packages cajole furiously at the carefully dogged foxes. final grouches must are. silently daring de\n1516|Customer#000001516|VFbEMU7LSQZPCZ3m73dNP2WH0ywr5loATV4r|14|24-797-943-8908|9263.27|FURNITURE|tructions integrate above the regular, regular somas. blithely ironic asymptote\n1517|Customer#000001517|hJBcIv8Yc9ukY9Erz96RRKNR8upJ8IBJxgePjf9|23|33-993-734-9681|2875.95|FURNITURE|tes. carefully final packages against the p\n1518|Customer#000001518|ulllJKhRl VkFwAIhlb |8|18-242-415-7477|37.80|MACHINERY|l, pending packages boost ironic, final theodolites. fluffy requests are carefully. ironic, regular theodoli\n1519|Customer#000001519|ersLKVkITqd,b7yCM1td5h9Y1tQv|14|24-663-396-2927|6172.47|BUILDING|ost from the close accounts. r\n1520|Customer#000001520|WuEf6uxQmSgTA1efbl24QhQ60WJoh2166RzzOiV|24|34-364-590-2076|8678.58|HOUSEHOLD|ges after the requests integrate slyly according to the ironic requ\n1521|Customer#000001521| UAwhhVG066cebuZN6Wk7s|13|23-168-973-9213|9983.09|AUTOMOBILE|special deposits use quickly according to the furiously express packages. slyly unu\n1522|Customer#000001522|aMbkFCcpuqN8YFzn8ctAhm skIIfd|4|14-712-410-2710|948.00|AUTOMOBILE|packages hang slyly alongside of the slyly silent foxes. closely express accounts alongside of the even Tir\n1523|Customer#000001523|  udI60hnJb0IMvB67xQFfkgamLpP2Bwynf5P|7|17-405-744-1455|2921.13|MACHINERY|oxes. slyly bold ideas snooze blithely. accounts sleep slyly agains\n1524|Customer#000001524|nW8RCuzryVNcYMCvE ZKJC7apmhen|22|32-834-498-4224|425.06|BUILDING|ses are regular packages. even asymptotes believe. furiously regular platelets sleep carefully according to t\n1525|Customer#000001525|NtS KugGxV4GMBxNAwZdR6wwq02 fd y5,M|0|10-178-851-9228|2915.69|AUTOMOBILE|cial dependencies. ironic accounts integrate regularly across the permanent accounts. quickly \n1526|Customer#000001526|pAC6Yj2c5lyOIr5 IQpM0|8|18-679-265-7392|8012.73|MACHINERY|ructions. furiously ironic packages after the evenly express pinto beans \n1527|Customer#000001527|486JIEEHa |24|34-219-462-2180|6025.59|FURNITURE|old excuses sleep furiously ironic packages-- final pinto beans cajol\n1528|Customer#000001528|fa,9WdvoEW06FtLQ6bpXBYGORjOWt,w|21|31-594-709-9605|4114.07|AUTOMOBILE| bold deposits sleep slyly about the blithely express accounts. final accounts haggle furiously at the\n1529|Customer#000001529|NYQrlaZMT2rOQadTbfSpAdPPTQwpWQEEWD|2|12-170-370-8690|8221.42|HOUSEHOLD|each blithely. ironic, even packages sleep slyly alongside of the furiousl\n1530|Customer#000001530|KVYYmaQ7fGwFnhgBnot1zTnFa|2|12-845-483-5866|4404.87|MACHINERY|ckly furious deposits. furiously special instructions sleep furiously according to the regula\n1531|Customer#000001531|OomxCS69ZBbyC99b6YHXYGvw1Fs|21|31-735-863-6916|728.83|AUTOMOBILE|. deposits use furiously. ironic accounts affix\n1532|Customer#000001532|VHjtEO1OwfCkrTIj|1|11-301-550-1539|4311.62|BUILDING|ependencies. fluffily even instruction\n1533|Customer#000001533|jVCPod3Ysz|7|17-511-289-3953|6323.48|HOUSEHOLD|cross the ironic, regular ideas. even, regular deposits haggle according to the blithely regular dep\n1534|Customer#000001534|EJ1gh5MYQ7R xKH6RfqPU96So94cMHKHgnEVTgy|17|27-975-211-1327|5760.99|FURNITURE|ly final requests; blithely regular pinto beans haggle slyly along the quickly bold notornis. sentiments cajole c\n1535|Customer#000001535|2l8xLuwaicTTg5RNA7mwyHhz|23|33-371-530-1740|1041.79|FURNITURE|g the blithely final accounts haggle carefully above the sly, express platelets. excuses eat slyly across the c\n1536|Customer#000001536|HRUhB3D7LC6V ydQigaOZ10Y9Be1jN31|13|23-357-877-4041|6388.82|HOUSEHOLD|uriously against the slyly regular ideas. blithely ironic dependencies solve nev\n1537|Customer#000001537|Fx1vbSLG90yTE3KF2VGDMOeny|15|25-482-334-6480|4000.55|HOUSEHOLD| courts. quickly regular instructions are blithely silent excuses. slyly special theodolites use finally\n1538|Customer#000001538|ohSUJgfMxt2Hq9f0tv,MZaRsombSl,MU1d6,|2|12-766-442-8988|3245.69|FURNITURE| blithely carefully even instructions. blithe\n1539|Customer#000001539|EFgodQ9F0u SUYZQcJCNzjDlte5 br0klLU|17|27-544-403-7594|-702.43|MACHINERY|e blithely express pinto beans. carefully regular pack\n1540|Customer#000001540|c1kVCV43v2RpUwCoZJ2LBHWYt2BT7|17|27-352-357-7209|6102.98|HOUSEHOLD|ious foxes against the slyly regular deposits boost ideas. regular waters sleep slyly: unusual deposits\n1541|Customer#000001541|3HbD4JaolktsAYU,OgPrar2|15|25-786-474-5957|6792.00|AUTOMOBILE|blithely. packages wake final accounts. carefully enticing asymptotes run quietly despite the slyl\n1542|Customer#000001542|4whsFeeVSBH7Eq WSu gF5JCsJc|14|24-754-425-8980|4108.68|BUILDING|sual, regular deposits haggle blithely alongside of the quickly ironic foxes. slyly bold pinto beans det\n1543|Customer#000001543|IKgaPQRsONfY1vAsPP|18|28-327-662-8527|5653.73|MACHINERY|ckages haggle. idly even deposits according to the regularly even ideas haggle blithely re\n1544|Customer#000001544|R,hoHFlkusJ,1Kts,0QEixg|23|33-132-882-2925|2204.41|AUTOMOBILE|ular deposits. final, regular accounts nag carefully quickly regular pinto beans. unusual, unusual theodol\n1545|Customer#000001545|08TtvYMUYuq6Hgi1T4IsV2fr1G90cnb D|1|11-287-870-3637|-487.86|BUILDING|ial requests wake. sometimes regular sentiments are. pinto beans use car\n1546|Customer#000001546|kFu hXaTK2Vk|20|30-788-120-7833|4488.33|AUTOMOBILE|sts haggle furiously. even, regular packages sleep. idly idle somas affix furiousl\n1547|Customer#000001547|RgRcB,v0ZS|4|14-683-809-4484|6387.31|AUTOMOBILE|ts after the accounts are above the fina\n1548|Customer#000001548|0uaAwzhbw,1VFL|23|33-610-656-3668|562.75|AUTOMOBILE|eep slyly regular, final instructions. final dependencies engage a\n1549|Customer#000001549|Bm8PVyaAYfS0IFPhkXiVGL|24|34-263-284-6757|7050.56|FURNITURE|sts about the quickly unusual dolphins integrate quickly slyly silent platelets. quickly final instructions\n1550|Customer#000001550|NgbaaI8wjR|20|30-722-982-9755|4742.59|BUILDING|sits. regular requests cajole boldly after the slyly special ideas. quick t\n1551|Customer#000001551|GSs9E1btXLKkSgkCAyLohk1bOLuJ6|14|24-667-589-4141|797.35|BUILDING|ely ironic dependencies. quickly iro\n1552|Customer#000001552|eR6My q7YdhYeBH jVxHHC mYpeNFNBDuG10|21|31-902-185-9642|956.02|AUTOMOBILE|regular accounts eat furiously slyly slow ideas. carefully final accounts mold furiously after \n1553|Customer#000001553|zS2t71h5ssFkRFiB4EvNtWPqjexC1FaO1MeNutf|1|11-879-323-7032|5853.10|AUTOMOBILE|he slyly unusual packages cajole slyly ab\n1554|Customer#000001554|axGq6Zieq8sy|7|17-462-295-6567|8996.02|AUTOMOBILE|latelets cajole furiously final, regular packages. furiously even accounts cajole according to the even \n1555|Customer#000001555|7V1UD h0 oKL04nnKVzu7UCmFSL56|5|15-722-660-7220|-740.02|BUILDING|ng deposits alongside of the express, bold deposits cajole blithely deposits. even packa\n1556|Customer#000001556|0KThJm1X9rQH3Me,EI2QW8HzrUKnsU,gvw9BwzN|11|21-170-549-6376|1462.44|BUILDING|coys wake slyly along the ironic pinto beans. evenly ironic requests use quickly. final packages are slyly. dolph\n1557|Customer#000001557|cbF7Kpmtk4w1vCoqB,3Ev3XNnr|19|29-927-226-6896|3144.14|BUILDING| according to the final, pending account\n1558|Customer#000001558|hHKBdZXfRUbMjnlX i8sGWu6|9|19-532-314-9903|8473.41|AUTOMOBILE| thin packages against the even ideas sleep slyly according to the pending instructions: \n1559|Customer#000001559|0rOzDCEPki4zpeqXx5nW3ajIGdLN15XHeS|0|10-700-486-1040|4630.52|FURNITURE|ronic sentiments doubt carefully slyly even deposits. e\n1560|Customer#000001560|yNFoAP4UcMlluwL1uNYvUmCgrn7GfDiTo3H3mzV|15|25-187-156-3225|9146.01|MACHINERY|furiously ironic requests alongside of the deposits impre\n1561|Customer#000001561|11hKNqixtqQsCgZKu3DYu0VEx28g04|13|23-445-875-1233|1083.49|AUTOMOBILE|slyly about the quickly expres\n1562|Customer#000001562|Rj0aTQUqnb1u4qOvWzb3|13|23-883-927-3910|3102.27|FURNITURE|eposits cajole. final instructions alongsid\n1563|Customer#000001563|cb7 vuR7o4Z5KQqgd5yllan5Evum5|4|14-146-791-5866|8838.33|MACHINERY|ourts-- slyly regular packages sleep car\n1564|Customer#000001564| kQ06G,BN4KWou6DYH|5|15-898-126-9264|-184.33|BUILDING|ual foxes wake: theodolites sleep bravely after the furiously \n1565|Customer#000001565|EWQO5Ck,nMuHVQimqL8dLrixRP6QKveXcz9QgorW|2|12-402-178-2007|1820.03|AUTOMOBILE|ously regular accounts wake slyly ironic idea\n1566|Customer#000001566|NfBldfDRJyOWXbZ47UJP2hGn6HF1zOZGJaOa|23|33-480-441-5244|7256.21|FURNITURE| final frets are carefully against th\n1567|Customer#000001567|D XMRaJOpRqLttO8yiMZ4tYU1L2nUr|24|34-146-945-2364|7209.94|BUILDING|thely ironic ideas. ruthlessly pending sauternes are furiously enticingly regular pinto\n1568|Customer#000001568|uOMsOfJ0raeSGqW9PMPs1sL5D pcO,fUaYsY6|22|32-780-340-3819|-576.58|HOUSEHOLD|ts sublate carefully ironic orbits. final, even accounts sleep toward\n1569|Customer#000001569|4vO9w7ixKJ 5od18LqLvr,|6|16-108-793-2841|9416.38|HOUSEHOLD| the unusual packages. even excuses against the fluffily regular idea\n1570|Customer#000001570|RMBVeVOCt002J1|13|23-319-685-6601|2106.52|FURNITURE|unusual deposits unwind among the courts. silent packages \n1571|Customer#000001571|akbtXy3o6igP3n8C|6|16-661-716-6605|4250.73|BUILDING|silent deposits sleep. silent foxes acro\n1572|Customer#000001572|wS4p6kZ8dz8WyKfAbhXeBUO3QJj|5|15-262-124-6233|6070.44|FURNITURE|instructions affix furiously slyly regular foxe\n1573|Customer#000001573|pcC2rrIA2bwtSXkcBy8X5eoQBrfoGb7gT|10|20-170-955-7287|9831.27|AUTOMOBILE|ests use of the brave accounts. excuses are \n1574|Customer#000001574|hgovcHRlq4 y|8|18-753-101-5745|1204.82|HOUSEHOLD|e regular courts affix after the thinly ironic reque\n1575|Customer#000001575|Ntyf,WOVz9hrnESfXT6gBxej1eZjbwgdSEVvmRw|0|10-455-580-7646|7283.99|AUTOMOBILE|dugouts. slyly even deposits about the quickly reg\n1576|Customer#000001576|ec9dOjmCD0iicQEc4iIff88zX4kFGHPZUPYX1sBg|22|32-430-540-7796|1006.44|MACHINERY|y bold requests about the ironic, regular theodolites haggle slyly instructions: ironic courts wake carefully.\n1577|Customer#000001577|eaMmfsWJ7  USPFwMH|22|32-267-732-1345|6204.00|MACHINERY|dolites boost slyly even deposits. furiously ironic asymptotes ha\n1578|Customer#000001578|KGMw1t3in68W4|17|27-348-227-6667|-365.45|AUTOMOBILE|elets. special, express excuses after the accounts promise carefully requests. care\n1579|Customer#000001579|TI4GCerFzw2UgqQdzJ94|3|13-255-948-9257|4763.17|AUTOMOBILE|efully across the quickly express deposits. slyly regular deposits sleep above the blithely ironic theodoli\n1580|Customer#000001580|Uc5lBMkU8F1zW56P Bo,8fbVlyCKs|13|23-651-166-3240|5587.12|BUILDING|ts before the ironic packages sleep furiously regular, bold dependencies: dolphins \n1581|Customer#000001581|fCDyGbFmnkclr,031ny|14|24-603-456-1171|4669.01|AUTOMOBILE|lithely final deposits. quickly express platelets unwind \n1582|Customer#000001582|Tw 9wNgPjMmsy1brAYW0|11|21-998-418-6615|7119.80|BUILDING|even accounts. quickly brave deposits haggle.\n1583|Customer#000001583|og6OTS,QKN2BidNDSZd0yB,Tn8ls6TGnKUz |13|23-136-310-3804|2540.51|BUILDING|bout the pinto beans. bold, e\n1584|Customer#000001584|BWzLMEnPG7tsF54M8kdGVd7zQCxiXniOP|21|31-675-590-3473|5305.86|HOUSEHOLD|. quickly busy deposits haggle carefully under the even, unusual foxes. carefully blithe foxes snooze fluffily? qui\n1585|Customer#000001585|kMDzNCvICH1j7sLp8g0CFB8cO12tCS70VTp5wM7|22|32-232-514-3624|7651.05|AUTOMOBILE| carefully regular packages are about the carefully silent foxes. fluffily regular Tiresias wake furiously across th\n1586|Customer#000001586|I76G9G7dkkigm162L|2|12-221-668-7869|-808.05|FURNITURE|rs cajole silently. ideas doze furiously! spec\n1587|Customer#000001587|ztyGKSLXBi6r,uNDAxxDeWuWWUdfR1WL4maTC|17|27-437-149-3006|2050.48|HOUSEHOLD|closely alongside of the furiously pending foxes. furiously final requests wake about the ironic, ironic depend\n1588|Customer#000001588|TOCHdXfBa1nhv26OP|1|11-700-437-5542|8372.34|HOUSEHOLD|onic asymptotes sleep carefully-- furiously regular accounts against the quickly pending pinto beans mold boldl\n1589|Customer#000001589|As9UC67KvgdnJcZWfdz,|13|23-189-857-8090|-101.68|HOUSEHOLD|s boost final excuses. slyly ironic deposits wake quickly blithely silent requests. car\n1590|Customer#000001590|c9ykZTFqi2xpKpNedlJ5,v03aqCbT|19|29-736-744-4365|5065.00|AUTOMOBILE|nusual instructions sleep fluffily furiously bold realms. regular, quick platelets wake slyly. final asymptote\n1591|Customer#000001591|ZLJNTInWmiv9a1|9|19-142-875-7741|7470.70|FURNITURE|yly final foxes should have to use carefully. even, bol\n1592|Customer#000001592|Bf Y0y,RTCY4z|9|19-565-127-5247|4042.57|HOUSEHOLD|n foxes. foxes cajole daringly silent deposits. sentiments sleep flu\n1593|Customer#000001593|IAhXngV2KlKAbAQh4y6S7Vd|7|17-767-583-7374|5447.72|AUTOMOBILE|he special pinto beans. silent accounts sleep furiously final packages;\n1594|Customer#000001594|8No1IYGij7|13|23-416-484-3099|4796.94|FURNITURE| final packages wake idly. quickly regular pack\n1595|Customer#000001595|bJ6tl8L3gexrf9rdD,Nn9ojzg92|3|13-153-638-7545|1151.78|MACHINERY|hely final ideas. regularly daring requests sleep. silent excuses s\n1596|Customer#000001596|fpSMWvE3a |20|30-259-884-2046|6900.08|AUTOMOBILE|foxes integrate thinly. furious, special packages sleep furiously about the asymptotes. final accounts s\n1597|Customer#000001597|6pS2oH twoOdcRPVMT13YQQA YIFu|22|32-621-493-2342|5728.91|HOUSEHOLD|s foxes eat furiously final foxes. slyly unusual packages are never against the deposits\n1598|Customer#000001598|bz91jr1NNiJ|9|19-439-414-8308|835.29|AUTOMOBILE|onic packages about the ironic, bold ideas are packages. care\n1599|Customer#000001599|DbZoJYdsMvL8hELLlgjAvUZ|11|21-556-967-2607|626.23|MACHINERY|ly unusual gifts. even, regular foxes use slyly along the ironic, regular dependencies. \n1600|Customer#000001600|GFgAlTCNWGZU4Gyk9glu8uX2vZ|20|30-563-390-7858|7027.54|BUILDING| sleep blithely along the slyly ironic deposits. blithely permanent accounts nag. fluffily ironic accounts\n1601|Customer#000001601|jiy,cXiM41u9yIb1Vy|9|19-152-934-8225|2884.08|MACHINERY|ly ironic, even accounts. special dependencies detect. c\n1602|Customer#000001602|Lum76wozwPDwPGgk7yFzLnG|19|29-236-186-6698|4645.67|BUILDING|. blithely even requests use slyly. unusual platelets snooze carefully r\n1603|Customer#000001603|JHju hD17jZDMXprwVfC|11|21-880-121-2298|-149.21|HOUSEHOLD|tect slyly quickly regular accounts. daringly bold deposits are blithely blithe\n1604|Customer#000001604|DXn5Lr8KjjMebZznHhSsX3n7T6J8UkWdYw|6|16-960-140-9357|9079.75|HOUSEHOLD|odolites. final frets need to grow according to \n1605|Customer#000001605|PLOEPrgnofqWl3|15|25-483-103-9669|9396.10|MACHINERY|sly unusual requests. furiously final theodolites are boldly unusual instructions. ironic, permanent acc\n1606|Customer#000001606|rIhuh0JIXA caaG|19|29-275-181-3687|2244.46|FURNITURE|y slyly unusual accounts. even foxes print furiously daringly even waters. blithely unusual deposits boo\n1607|Customer#000001607|JrtvTEYpFdqK68WSabydH6dz9Opj6X5orhrIYeHY|23|33-529-928-4089|1543.75|FURNITURE|ites sleep furiously quickly pending somas. fluffily special\n1608|Customer#000001608|jGjdmzMbF05pXU5STryOYpL9orgJ6F|6|16-897-134-9884|5827.14|BUILDING|oys haggle never. regular ideas solve. express dependencies cajole furiously bold ac\n1609|Customer#000001609|YuJ96cGZd lzZ5jo0HI6OAi,7b12GYDC,|20|30-784-105-5546|1710.71|HOUSEHOLD|unts alongside of the regular packages haggle carefull\n1610|Customer#000001610|8T,m b4Gwjs9j|18|28-219-755-5479|6861.85|FURNITURE|ainst the slyly ironic instructions. blithely final deposits \n1611|Customer#000001611|QX3yB3eqWVsGuy7WetBKk6U6s  CXl|18|28-997-908-7044|1148.91|BUILDING|ests. furiously express instructions wake slyly! fluffily express frets haggle. quickly even instructions do\n1612|Customer#000001612|oRmhlGYt71UyFdgI4KvPxF|16|26-493-547-6969|2613.29|AUTOMOBILE|oost blithely about the blithely express packages. reg\n1613|Customer#000001613|grC4vU,xdQCWgrPJzj|19|29-636-508-4398|7539.75|HOUSEHOLD|onic excuses. regularly unusual deposits sleep. slyly ironic accounts nag carefully. furiousl\n1614|Customer#000001614|7BofGHd,3lr2wda7i|1|11-986-549-9647|9609.43|HOUSEHOLD|to beans use blithely. unusual, regular waters along the slyly bold pinto beans wake sly\n1615|Customer#000001615|AWqpPsmhK,yirQmha|18|28-449-655-8989|1764.25|MACHINERY|egular packages. carefully express ideas wake fluffily fluffily idle deposits. furiously final dinos n\n1616|Customer#000001616|hZ7KvTsImg5hRUWmHXpkZGvhFe|20|30-752-506-2492|4635.15|FURNITURE| pending tithes. furiously blithe instructions boost. thinly pending ideas use careful\n1617|Customer#000001617|SfX,PYtDB3h2gdmDD1JMN,gKIIVqo|2|12-677-936-3084|9911.51|AUTOMOBILE|y ironic requests above the regular requests haggle silent asymptotes. daringly special instructions detec\n1618|Customer#000001618|efEU9gOnX05FfeJAyNMup|12|22-326-603-9101|7795.47|AUTOMOBILE|refully pending accounts haggle furiously dependencies. \n1619|Customer#000001619|qM0OslnGfoXRS4YhYUDaUd6cXDDCPc5Ppke8CU|13|23-371-869-2433|6511.83|BUILDING|bout the slyly silent accounts. fluffily ironic deposits integrate carefully against the ironic, express pinto bea\n1620|Customer#000001620|p2BIMDiWvXUWlb FXxIukQZI|5|15-151-404-4005|3324.77|FURNITURE|regular courts. carefully brave deposits\n1621|Customer#000001621|5I2xwuWad5n73M5zM,Dj|16|26-615-141-1919|8358.51|AUTOMOBILE|efully silent deposits shall nag blithely a\n1622|Customer#000001622|HEfEM41ad6Tar1EH526Q9cxe3Pi|5|15-120-999-7103|9617.34|HOUSEHOLD|e requests-- carefully bold packages are. \n1623|Customer#000001623|azlsfbL,uqLb2T1wCn5yQ33YK5KvJ8Fo|23|33-467-523-3238|2746.27|HOUSEHOLD|ress carefully quickly special depend\n1624|Customer#000001624|A8VfM,awG8VPydormLPcaw|17|27-822-330-4723|9566.20|AUTOMOBILE|accounts among the brave packages haggle finally careful\n1625|Customer#000001625|qQ53P2z9Mnocb2HG9u|9|19-868-381-8072|1189.78|HOUSEHOLD|nts use furiously regular pinto beans. unusual, regular dependencies detect blithely\n1626|Customer#000001626|Qqvd9BwVQQ133oxNXb8N1i6V3l9z7eu3A|22|32-751-259-8740|7564.80|FURNITURE|ar accounts haggle never. quietly regular instructions are carefu\n1627|Customer#000001627|RV5yXOOv0tjeqxIoRtIw9lKU3UK|10|20-566-949-4093|1673.17|AUTOMOBILE|fully after the blithely regular warhorses-- quickly even\n1628|Customer#000001628|xOOuECIqRpweZwxZRgQpb2guNYVE|18|28-241-420-5429|3002.83|MACHINERY|s use fluffily. slyly silent foxes\n1629|Customer#000001629|eGVew4ADiILjquTPiTeVS9|18|28-413-295-5895|9601.48|HOUSEHOLD|sly express dependencies abou\n1630|Customer#000001630|mFqtTXCA4QaCqP7yXsTlk|24|34-375-163-1478|214.28|FURNITURE|furiously final excuses. accounts cajole blithely ironic pinto \n1631|Customer#000001631|TEZnHT8B6dqZw9,OoyNrOJs PlT2QfZKk|13|23-875-411-6115|2401.42|BUILDING|y ironic packages. fluffily express deposits sleep by the slyly ironic requests. regular depos\n1632|Customer#000001632|9SoJbgMR23pwWXyE|24|34-183-653-4603|-378.16|AUTOMOBILE|p across the ironic foxes. furiously close dependencies det\n1633|Customer#000001633|kO5Tq2Y W,NklARS,|11|21-575-247-9010|162.02|MACHINERY| ruthless pains alongside of the even platele\n1634|Customer#000001634|mTRMQ9143TTe5kHsD2FdNE7proZ|24|34-186-980-9064|4030.37|FURNITURE|s are thinly fluffily ironic requests. pinto beans cajole blithely before the regular \n1635|Customer#000001635|HjISwY7cr50HVcC81T7MnYJ7byRMXrMgB7RjsV|23|33-974-490-5943|9435.42|HOUSEHOLD| the blithely final dependencies. slyly ironic instructions are furiou\n1636|Customer#000001636|t1VhiA5gssjGA4,o5b2e8WJHsaBAmCfm4G|20|30-559-573-5410|3228.88|FURNITURE|to beans are quickly carefully regular dolphins. regular, ironic dolphins de\n1637|Customer#000001637|dqGWqAXF4JuL7FcYH7r9dnH3MiT0S08VS7KgD|15|25-177-814-7863|2844.51|HOUSEHOLD|the quickly ironic instructions. packages poach blithely express instructions. even packages haggle sl\n1638|Customer#000001638|Gxgb1kyTwOAoVu0fqaRQk4KCyWzkULGTy6tkpdOx|17|27-548-377-6273|-411.15|FURNITURE|lly regular accounts! express attainments maintain carefully. furiously pending pi\n1639|Customer#000001639|QWbeop69wQqRFQbySM7WqPGTSd7fW6QMFYIjL|1|11-304-833-1391|6651.19|AUTOMOBILE|nt deposits cajole slyly blithe pinto bea\n1640|Customer#000001640|lGeZbMEg03r24lUuK|8|18-270-772-6060|2622.28|BUILDING| regular packages are slyly among th\n1641|Customer#000001641|XT5DXFdGy4kjb|12|22-791-967-1788|4611.37|HOUSEHOLD|unts through the slyly pending platelets integrate carefully dependencies? blithely ironic\n1642|Customer#000001642|UgLWC4Pw,XZX52b8hcEixGxk,J|9|19-134-303-4344|6248.64|FURNITURE|uick requests. furiously ironi\n1643|Customer#000001643|,vdC1qp8aweR4z4 sTdnhujyZn,|13|23-553-752-7340|1982.44|BUILDING|ites. slyly pending accounts across the dogged excuses can are according to the bold packag\n1644|Customer#000001644|la3oZuddBtIVanskRXO8|8|18-235-782-9940|8808.00|MACHINERY|thely final dependencies above the deposits boost fluffily above the ironic, express theodolites. ironic theodol\n1645|Customer#000001645|2gNNcbkeFHKEgl4WSW7G8XpXL0VW,6MtTc0G|16|26-174-526-8279|7085.79|MACHINERY|ic theodolites. furiously regular theodolites nag furiously carefully pending pinto beans. blithely unusual de\n1646|Customer#000001646|RQ,TryFh5loGPxszvCgRncdO5kM daRcgON|24|34-268-537-8282|2854.29|MACHINERY|re according to the ironic, ironic requests. ironic packages wake after the special requests. \n1647|Customer#000001647|aLfdvxbHzfKz2CAdiOgKiJ|16|26-784-323-3431|9987.11|HOUSEHOLD| final deposits nag. sometimes final dependencies d\n1648|Customer#000001648|e3oTXQ7OOTzwcRFXr|23|33-718-723-5373|2389.14|AUTOMOBILE|d sentiments eat carefully unusual instructions. unusual war\n1649|Customer#000001649|7n8CvxEE4tthklLyRNZIMQds5rruRPiQLLdV|4|14-308-532-5953|2271.45|BUILDING|te across the blithely ironic packages. blithely final accounts among the quietl\n1650|Customer#000001650|6mpXm8FQzetQ7wA1pzEmuYVcVp5 fnDk|24|34-295-469-8581|4183.71|FURNITURE|express ideas along the regular requests sleep furiously alongside of the fluffily ironic courts. idly final\n1651|Customer#000001651|whCw6gMwEuls sCrsaB,DQI0,|3|13-593-198-5028|1614.35|AUTOMOBILE|eans. blithely final requests according to \n1652|Customer#000001652|uDJ6cxL10W sEd4,O7,rdoxst2Sp1Ij72Jb1|7|17-670-200-2924|4335.18|MACHINERY|ainst the close pinto beans. furiously final asymptotes across the even deposits \n1653|Customer#000001653|PQFMr5tmEgBCF7rww29Vc yMrHY9HJk|8|18-150-322-1853|6338.28|BUILDING|ic packages. platelets along the slyly pending dependencies sleep slyly alongside of the slyly\n1654|Customer#000001654|igHSnmh 6yMC3vF|5|15-299-167-7023|1522.54|AUTOMOBILE|thely unusual requests boost slyly special, final requests. quickly exp\n1655|Customer#000001655|DjCE7uReh1B,,ikdShz9W3PCfqkJow|5|15-306-607-3769|1214.81|BUILDING|s. special, bold requests haggle fluffily. carefull\n1656|Customer#000001656|m3BvBNeQ1O eV1Bnn3y,MkEx7Io8GkfQ|19|29-904-708-2645|-664.93|AUTOMOBILE|furiously pending packages cajole regularly requests. fluffily even foxes wake blithely. furiously bold \n1657|Customer#000001657|MSSbpflkYXCciBa|12|22-113-887-8875|9376.24|FURNITURE|ckages nag according to the regular\n1658|Customer#000001658|5ZAXRv0hnyOcjObHR1ScVOZ77ncI,0|21|31-949-978-6932|9525.19|BUILDING|press instructions. carefully express requests are against the blithely final\n1659|Customer#000001659|6,g4PcDD8cCUdAKNbhAmwyG3lqEKuUbq|11|21-545-972-9730|4982.96|MACHINERY|ake. regular, unusual instructions sleep quickly carefully even foxes. regular sauternes acco\n1660|Customer#000001660|ClPcSJym47fEQW78Kt4|21|31-870-788-5315|3581.59|AUTOMOBILE| accounts breach slyly. permanent deposits are furi\n1661|Customer#000001661|IRXXgB,YgRc078y2i1C,87 1wZ|0|10-582-676-4365|1760.90|HOUSEHOLD|s. ideas play slyly slyly bold theodolites. furiously ironic packages na\n1662|Customer#000001662|fImm0 WZ JU39aNmhsh5WKcnCqXW|13|23-691-593-1242|3333.02|MACHINERY|nic accounts sleep carefully across the fluffily unusual pinto b\n1663|Customer#000001663|7PGRXPj1HXVQUbcL S2|16|26-507-387-2886|4085.85|HOUSEHOLD| slyly ironic deposits. daringly regular requests haggle slyly. ironic dependencies cajole furiously \n1664|Customer#000001664|LWrtr,G ifu9pwmSc2HknzWQS,o0FOMGucsq7Rdh|12|22-597-130-8584|6912.53|MACHINERY|thely ironic requests haggle slyly. quickly ruthless dolphins are slyly ironic requests. carefully special\n1665|Customer#000001665|26NoCK4dbtU7jmEhrXSuq9rtQWM042UYODGFm,|9|19-542-708-2762|5869.13|HOUSEHOLD| wake furiously carefully even id\n1666|Customer#000001666|AFCMGLIrCORZavTw7YX1dAVlJIk,aYlH|22|32-587-573-6083|3562.78|MACHINERY|fluffily regular ideas nag furiously. even accounts haggle carefully among the furiously final\n1667|Customer#000001667|aEGS4v41BVwqZylqNvPj|16|26-528-257-9769|4649.03|AUTOMOBILE|nments; unusual deposits wake according to the regularly unusual deposits-- fluffily express requests sl\n1668|Customer#000001668|CS067JF7eX,ax,vrx8wx|23|33-184-926-5421|1305.72|MACHINERY| ironic platelets must wake fur\n1669|Customer#000001669|i38,,EDjrqVpk1UKXsl9cCdAwS,HpcFqPS|6|16-172-628-3560|9180.72|HOUSEHOLD|quickly unusual packages are. furiously express ins\n1670|Customer#000001670|YP A2c1cFpn|18|28-571-377-3401|1472.96|AUTOMOBILE|ge silently. packages haggle quickly final packages! ruthless dependencies cajole ruthlessly q\n1671|Customer#000001671|6yWHFFBO5YDpHN,YmYEpxulL|2|12-269-842-9419|4030.97|HOUSEHOLD|otes run slyly ironic deposits. carefully silent requests use slyly carefully ironic epita\n1672|Customer#000001672|ZqEat15B3nCQI4MaRoxdfhN3WIH96vWpUs80z4|23|33-169-930-6985|8586.04|FURNITURE|sts. thinly final accounts are about the blithely regular foxes. blithely even dolphins promise blithely speci\n1673|Customer#000001673|uQi2r9akwS4LNd7XQEa|3|13-713-161-3704|5714.73|HOUSEHOLD| slyly. dolphins wake carefully even deposits. quickly even dolphins grow slyly. express, regular deposits \n1674|Customer#000001674|VDSnyhnkkFA1CKYDjdMx4Hpt1QaZ4g,1cy9Fnr|3|13-396-137-7834|4568.47|BUILDING|equests use furiously at the carefully ironic requests. unusual deposits boost slyly carefully even fox\n1675|Customer#000001675|YvuT73pnh06wLlgAyTO1ZyZ4w,2e5Wk MGnbFO|24|34-888-827-2907|261.00|AUTOMOBILE|eep carefully. even deposits cajole furiously around the boldly final deposits. dep\n1676|Customer#000001676|WgQKmlxIcGQz86n5sQMbWUu8cg7UG7W3r|5|15-612-997-6342|6504.47|AUTOMOBILE|s. even asymptotes boost blithely\n1677|Customer#000001677|7PgxZfn 6hX3gSjJzRq|5|15-121-397-5027|650.89|MACHINERY|inal asymptotes haggle carefully packages. slyly ironic req\n1678|Customer#000001678|xq R0 eIkV019MjY8yRdj r,Gfd|4|14-624-973-2343|3396.57|MACHINERY|sits. fluffily even instructions are again\n1679|Customer#000001679|EBr12ymXS5u3,9Bh6Cd8VCsmJ9cOR8nuS|18|28-730-907-3502|5172.53|HOUSEHOLD|eep quickly. furiously express excuses haggl\n1680|Customer#000001680|jYhr0a6R8XTw8RR3XJQ1kToU5H|18|28-465-621-9214|-203.01|HOUSEHOLD|ithely unusual patterns above the unusual ideas cajole blithely according to the fluffily expre\n1681|Customer#000001681|QxfVn4jW30|16|26-375-137-8121|6996.10|HOUSEHOLD|tions among the ironically final accounts haggle according to the carefully unusual instructions. fluffi\n1682|Customer#000001682|8TtqhjtXrYlzMxQ17N|17|27-438-398-2565|2459.75|MACHINERY|. blithely bold courts sleep carefully except the furiously unusual platelet\n1683|Customer#000001683|ecOqgCbaUID9JwYZuvSrFxXH9dIDaV|22|32-209-661-3831|942.28|AUTOMOBILE| carefully unusual foxes doze according to the platelets. bo\n1684|Customer#000001684|7t,Vo69PIG3t,ncWkzoLCJ8A,V28nMkK|6|16-197-588-1571|5928.82|AUTOMOBILE|d cajole about the ironic theodolites. fluffily special deposits about the enticingly blithe\n1685|Customer#000001685|3Mg0g4AXNPa|8|18-694-638-1767|5621.33|MACHINERY| sheaves. regular packages nag slyly afte\n1686|Customer#000001686|EYR2WxcOKG 4rIlcO9wbkAtID7PJOVkcPaC|12|22-241-190-8777|7782.48|HOUSEHOLD|osits sleep furiously about the closely ironic pac\n1687|Customer#000001687|lNxhAZMB,t1bbxFz7UXI0gFWhw|23|33-345-542-8289|8151.36|AUTOMOBILE| boost blithely at the furiously special requests. ironic, unusual requests \n1688|Customer#000001688|KE2TYjMt08|3|13-420-827-4701|3929.06|BUILDING|ckages use slyly. pinto beans haggle regular instructions. blithely even theodolites cajo\n1689|Customer#000001689|oYgoEWSydzBD81VB3q20DEx8TgSUX6qio,zpL83p|23|33-974-102-7427|705.64|MACHINERY|y silent requests according to the slyly final deposits poach ac\n1690|Customer#000001690|v2dVbJH3RxbBj5Wk5btJdzv9K35jXAoxhRYVthO|1|11-869-223-3212|-381.24|MACHINERY|riously throughout the slyly express accounts. slyl\n1691|Customer#000001691|BvajZGLJDqzvJfZKsuVjdwaixCO|12|22-649-185-6921|3367.22|AUTOMOBILE|side of the ironic packages. fluffily special asymptotes among the slyly unusual foxes haggle slyly b\n1692|Customer#000001692|C3n33KUNrCXK|7|17-625-330-9211|6921.50|BUILDING|. quickly unusual courts use furiously carefully ironic grouches. blithely unusual accounts sleep carefu\n1693|Customer#000001693|k9j7wuuKPs8gE|12|22-402-777-3279|2747.22|MACHINERY|carefully special packages cajole. blithely bold \n1694|Customer#000001694|jCLu0ZDrLdq7wFEJb|4|14-609-696-9902|3535.57|AUTOMOBILE|the pending, special Tiresias. fluffily even deposits wake carefully theo\n1695|Customer#000001695|ihGFlPO39VGgr7xRcR7AM1BFKn9pDq3C|16|26-263-151-5237|9258.82|AUTOMOBILE|press, express packages nag. slyly special asymptotes sublate. regular, even\n1696|Customer#000001696|HIJoLNtvjJbh5H0PturTaOBtSAQ3T,j7GSqq|16|26-115-967-9585|4978.11|BUILDING|eans across the even attainments haggle carefully above the furiously express req\n1697|Customer#000001697|TQj18iiC1gziLOnTileoy|24|34-288-313-5272|-913.24|FURNITURE|nding dependencies wake alongside of the sly\n1698|Customer#000001698|Xzkyij4D5OOWYsaWsucYV0|22|32-926-560-9683|5109.01|HOUSEHOLD|uriously ironic packages cajole slyly! regular deposits alon\n1699|Customer#000001699|lGzEu5g4oOROn4QvjK8fEd Z,Y9Vn7IV7EnlE5|1|11-655-675-5843|-813.01|HOUSEHOLD|ngage ruthlessly alongside of the carefully \n1700|Customer#000001700|DK3nJU9doE2BtBjQTFApwnLxOCSD |21|31-868-665-9539|6683.70|MACHINERY|ound the furiously final hockey pla\n1701|Customer#000001701|IbyUBDvH,eRszYTbnEDHGu16B4UsJSbQaA7F |3|13-397-730-2856|9986.13|FURNITURE|gular ideas. deposits about the busily unusual deposits are furiously ironic theodolites. quickly e\n1702|Customer#000001702|ZUf5SwR,j3HdY TBel7Mk|19|29-110-823-3729|8048.90|HOUSEHOLD|e quickly pending accounts-- carefully special deposi\n1703|Customer#000001703|7qJL pH9GSS4BZ Nc31|19|29-687-882-9664|8889.69|BUILDING|the ironic, final accounts. qui\n1704|Customer#000001704|G4lZ0VRWfLKldLDFR3,bA|16|26-425-543-6950|5145.40|HOUSEHOLD|fully express requests about the carefully regular deposits use carefully\n1705|Customer#000001705|lvZ9qSNhUMiE0LTOzmU7,NgjBo6VvcGrs|11|21-470-157-6516|5688.31|AUTOMOBILE|s sleep carefully. fluffily regular accounts haggle furious\n1706|Customer#000001706|FBx04exFTAFFRA3G,UR9Q2XSM1c8Uopaal2rEFv|2|12-442-364-1024|455.15|HOUSEHOLD| beans after the ironically pending accounts affix furiously quick pla\n1707|Customer#000001707|MjHpj4aS20ftMyK5EMEk87p|11|21-859-412-6010|2714.86|AUTOMOBILE|. carefully even requests nag fluffily after the sometimes regular instructions. quickly bold foxes among the idly\n1708|Customer#000001708|BdT2freRSXKa31JLnSBliCXmhi8J|2|12-857-766-2851|9702.83|AUTOMOBILE| always. requests are furiously packages. slyly regular tithes c\n1709|Customer#000001709|x0 eZbj1iyKLgjF8qImD|11|21-877-394-8667|9588.88|AUTOMOBILE| deposits. blithely regular frets\n1710|Customer#000001710|6kwF3TSVbf,JAh3PAjc0NSHHvdEGWjxVf2us|10|20-393-759-9313|8409.91|HOUSEHOLD|gular asymptotes. furiously silent excuses sleep never-- blith\n1711|Customer#000001711|Mhg8c9IAFb8G|15|25-302-946-6337|4421.61|MACHINERY|gle carefully. final, even deposi\n1712|Customer#000001712|Qdv0r7aA5tmEZw JkozgH|18|28-959-477-6941|7013.72|FURNITURE|le blithely. regular, regular dependencies integrate slowly a\n1713|Customer#000001713|saqFezmCXyr2f2sGR3WexM8MSf03S|15|25-566-819-3545|1627.04|AUTOMOBILE|cial deposits. requests alongside of the \n1714|Customer#000001714|6xIlLyed lGGxdneig8xdrvHZYHuLldIGeJu|23|33-251-747-7039|8569.50|BUILDING|ar packages. carefully final accounts wake carefully. slyly even ideas above the closely special reque\n1715|Customer#000001715|TNwfsu3dIIti|12|22-283-174-5611|2587.36|BUILDING|slyly regular accounts. quickly furious accounts sleep blithely alongside of the carefully special packages. ironic \n1716|Customer#000001716|FmPtChYDv7s7KX5Zi8Ug9SGMajKrQYRuv|20|30-299-232-8737|779.07|BUILDING|. final accounts use. furiously ironic asymptotes d\n1717|Customer#000001717|HufIAAW0Xbs3qoYpAVxk4KKvm,F|3|13-450-115-4347|1715.40|MACHINERY|s doubt. blithely silent accounts try to haggle: blithely ironic pinto beans boost furiously. carefully unusual a\n1718|Customer#000001718|z2SLViCW5QBh8FDiy3|24|34-842-694-7686|8697.88|FURNITURE|s sleep furiously about the carefully pending asymptotes.\n1719|Customer#000001719|eOix0jv6gCP34oQBO2i2z1UugzE1hwWx28n7Uog0|9|19-261-141-3893|4257.24|BUILDING|ully furiously ironic courts. even asymptotes sleep! regular, ironic dependencies are. carefully silent theodolit\n1720|Customer#000001720|CIRvBtD2pSJ2b2hoqOxhj|7|17-681-485-3576|5496.84|BUILDING|sly even accounts sleep carefully-- pending excuses sublate slyly. slyly unusual foxes al\n1721|Customer#000001721|qRNwOW8G9E5fOvG,,W7HDgv|9|19-288-344-3668|5484.48|FURNITURE| furiously. foxes cajole. instructions after the bold instructions\n1722|Customer#000001722|XqgWxT4hlg2EjS0HaV6XQwclVGN09kfwUJa iX|1|11-252-319-1249|6718.86|BUILDING|posits. furiously regular requests are fluffily ac\n1723|Customer#000001723|riRitYAsJ0nPLmAnd5JX,8QpE2F9iZIKXAg|22|32-609-708-8185|6893.86|AUTOMOBILE|e the carefully unusual theodolites. bold, even deposits alongside of the pending packages s\n1724|Customer#000001724|oCZhgKIkixlfOYiqSXqzeiUAV9ctBQSRsW55G6n|11|21-327-332-4432|123.03|AUTOMOBILE|ven packages. fluffily final deposits nag. carefully ironic dependencies unwind b\n1725|Customer#000001725|deDnuDKB3fZczOWTxwxutP|6|16-227-800-7867|8778.07|FURNITURE|he furiously stealthy deposits. deposits haggle quickly among the final packages. carefully expres\n1726|Customer#000001726|Gi5q77BDziKnHUbMcbOrja22sv|13|23-199-630-2326|-879.29|BUILDING|lithely final foxes around the ev\n1727|Customer#000001727|g7iJ1HSEmxE1sd4lWLV XcE64HA2JHYqPEJBA|23|33-952-217-5029|3628.03|AUTOMOBILE|e regular excuses detect carefully spe\n1728|Customer#000001728|ZbokjPCGrcUuysUjYtzQRK9gQSSNK CkFo3rh8i1|16|26-560-950-6812|1223.09|AUTOMOBILE|regular pinto beans; bold packages sleep. blithely ironic requests boost carefully furiously fin\n1729|Customer#000001729|qc3FBSbJHHDIEkku69mWsz,KO|5|15-361-394-6195|6479.07|AUTOMOBILE|y even depths sleep silent accounts. evenly final epita\n1730|Customer#000001730|p8wSGXOVmXO6rJBW3jgEjcFYRKW0jVY|1|11-216-159-3328|2743.27|HOUSEHOLD|efully final somas hang slyly. dependencies over the blithely regular requests haggle carefull\n1731|Customer#000001731|YOLyjZXp0eenovcSOSR4cShVAvSLDG84VTtB7|2|12-671-588-4662|3075.42|MACHINERY|sual gifts above the blithely bold packages haggle slyly pending attainmen\n1732|Customer#000001732|2ZHJonGizgo0QYzPxNF1PubW|2|12-505-455-1597|8512.55|BUILDING|arefully even deposits would cajole about the sometimes final requests. slyly silent ideas accordi\n1733|Customer#000001733|RO7fzBGXbovRqHEQln,fPJza9UAmIOe,Q|21|31-150-385-3780|2568.57|BUILDING| pinto beans nag! furiously pending deposits grow blithely even tithes? pending, ironic requests impress. regular do\n1734|Customer#000001734|Hj4hmdNc2sRBgG5YO4mz5q 9|8|18-264-978-2762|3888.89|AUTOMOBILE|inal asymptotes along the final platelets nag above the quickly regular requests. slyly final request\n1735|Customer#000001735|59,bPx BNSZ1YJ9thet9N|10|20-343-415-9202|7495.94|HOUSEHOLD|efully along the special realms. ruthlessly brave courts cajole carefully carefully silent reques\n1736|Customer#000001736|t3 1Dv36zNjBXauc2HKcSLJk,QRu4t|7|17-572-741-5408|-686.49|HOUSEHOLD|s! slyly sly requests above the expr\n1737|Customer#000001737|Mdj2vXS04lbiu1hlBPSpA6XliT0yq,XL|5|15-973-473-2767|-917.33|MACHINERY|wake furiously even deposits. busy, bold grouches integrate q\n1738|Customer#000001738|dKNfooI8lr6G0yb19Oug|8|18-263-208-3553|2290.69|AUTOMOBILE|mptotes. courts about the ideas nag requests. quickly unusual multipliers print after the pen\n1739|Customer#000001739|lhga8XGNRXPfJTotgUTn5qc4ush|12|22-216-512-8595|8526.70|FURNITURE|ut the fluffily regular foxes. regular accounts haggle. special deposits affix carefully. even, final\n1740|Customer#000001740|7SEO3ug3RMgpphEQ6Ozn2N22VGruDJA|19|29-148-722-9708|9323.31|FURNITURE|ronic foxes sleep carefully forges. regular, ironic pinto beans promise carefully across t\n1741|Customer#000001741|W9G SogT0038gQBdoLtFsexbHqNl|20|30-605-828-6050|-0.28|FURNITURE|sits wake carefully final Tiresias. carefully unusual requests sleep \n1742|Customer#000001742|t3PI5OUN02V0BSuoWRvpEcxhY6qX 3IxBOq|20|30-777-207-9522|4273.99|AUTOMOBILE| regular theodolites. carefully ironic packages are. even, express foxes h\n1743|Customer#000001743|F 5AwQdqqG4K q Ra2AZ0DIsKLNwhtIgHVxIr|16|26-867-792-6806|985.87|HOUSEHOLD|riously regular attainments. furiously unusual pinto beans cajole slyly daringly r\n1744|Customer#000001744|cUBf1 YMJEgbt2XDeQWD4WinTu4iFIF|17|27-864-312-2867|1436.96|FURNITURE|egularly bold, ironic packages. even theodolites nag. unusual theodolites would sleep furiously express\n1745|Customer#000001745|pAo6p8Q,xr4Y|22|32-624-467-3275|3907.29|BUILDING|final instructions affix fluffily alongside of the blithely unusual pinto be\n1746|Customer#000001746|f8Ku1TqpFJ 4wU,s6clle8G|17|27-110-285-2511|8774.31|HOUSEHOLD|ions cajole ironic theodolites. slyly even requests wake carefully. blithely regular frets integrate furiously agai\n1747|Customer#000001747|xSJopUGOWIGwa2QJA9mbIzcLAA8OM|15|25-493-381-4100|1962.62|MACHINERY|furiously furiously ironic theodolites. stealthily reg\n1748|Customer#000001748|4MaVm9Yox5xeu|13|23-496-856-9633|5779.19|AUTOMOBILE|y pending ideas. furiously regular instructions are. quickly ironic depo\n1749|Customer#000001749|lgY9SnA8QnkA7x2LAje7MGsVYB|4|14-247-462-3766|9788.66|AUTOMOBILE| carefully ironic packages. d\n1750|Customer#000001750|PBWOGiTcFiFucs8kN2h ,ACNS NC|16|26-538-454-1606|110.26|HOUSEHOLD|packages are stealthily final frets. regu\n1751|Customer#000001751|8KIBa2IJPXquMkWYLAccUT|7|17-460-999-8173|3151.94|HOUSEHOLD|ithely regular, even accounts. unusual packages lose furiously; asymptotes nag si\n1752|Customer#000001752|CZzZOOncqNdRX|5|15-542-683-9661|7585.99|MACHINERY|uriously unusual accounts. carefully unusual dependencies shall have to play sly\n1753|Customer#000001753|XlL3uXUfcHDfQ 4unoq pbTPbUzO|11|21-214-570-7769|5787.81|MACHINERY| carefully regular asymptotes a\n1754|Customer#000001754|wXsAA1ZgkeofaPGeZaIe|0|10-154-971-3056|759.00|BUILDING|requests. stealthily pending requ\n1755|Customer#000001755|emfcQnwjix1Ul0eKbHP|3|13-333-888-8618|5389.05|MACHINERY|detect blithely about the fluffily regular foxes. slyly regular requests wake quickly about the final foxes\n1756|Customer#000001756|q8WlGuUrrao7fWFp4pZE|19|29-962-368-3523|8817.44|MACHINERY|press pinto beans. accounts could are blithely carefully express dolphins. blithely bold deposits detect. eve\n1757|Customer#000001757|jY0jJh ww3hLh8eLmmJvKhIL47ka2j1uAys|6|16-868-947-1151|830.36|HOUSEHOLD|, bold instructions integrate slyly final accounts! carefully final accounts caj\n1758|Customer#000001758|xvGuRiXuKNuXABjdfERW8jl7b5FdFNo|16|26-263-427-8796|5746.38|FURNITURE|old bravely. ironic dugouts are slyly. carefull\n1759|Customer#000001759|2hzwTzhbdHJlHbT0jWD6lKTGA|20|30-176-851-5565|8396.37|MACHINERY|tect furiously. permanently even requests ac\n1760|Customer#000001760|HcnL7r33sIo1SAwc5|9|19-539-811-6674|7775.38|BUILDING|ts are after the regular theodolites. dar\n1761|Customer#000001761|ypll07IxByF9atO nGkJo7R8Tds8Wq |21|31-376-476-3692|9846.97|BUILDING|en requests. accounts sleep quickly around the carefully e\n1762|Customer#000001762|LD,Y0Xj io6wJYOZJI|15|25-319-745-1408|1171.92|AUTOMOBILE|ng accounts. thinly regular packages across the furiously ironic accounts wake fluffily accounts\n1763|Customer#000001763|Co2ATzqcl61Cd|1|11-738-302-2342|4490.23|AUTOMOBILE|manent accounts use slyly regular requests. carefully special packages after the pending \n1764|Customer#000001764|rlpLd5W4dwxpO7aAVKa7YlZHnwOQCziuByOrK|13|23-308-606-7773|5331.37|HOUSEHOLD|sits sleep slyly. even, ironic instructions breach permanently regular foxes. furiously silent platelets abo\n1765|Customer#000001765|YYXRIFQJ4V9paDLFCd13|9|19-538-728-6104|7409.60|AUTOMOBILE|final, special deposits sleep carefully furiously special deposits. evenly ironic p\n1766|Customer#000001766|n4uulup7FP1ZLXvSRHveY,TXBOTxs0pj67|15|25-155-396-7616|200.00|BUILDING|lly express foxes. blithely final deposits cajole doggedly regular theodol\n1767|Customer#000001767|ddqHp2,Ylt8vN8Pf|12|22-421-403-9852|4369.15|AUTOMOBILE|e carefully atop the even theodolites. pending, ironic accounts hinder among the carefully regular foxes. c\n1768|Customer#000001768|kId4M 0RG9dW9Po|17|27-746-769-7890|5356.96|AUTOMOBILE|s. carefully even requests are. spe\n1769|Customer#000001769|mrche55tm5KGuJb6lGWqCeKKIlZ rj4|10|20-708-644-8998|8457.18|AUTOMOBILE|furiously silent packages. bold, bold theodolites sleep quickly against the quickly ironic theodolites. slyl\n1770|Customer#000001770|IonoL5JlAeGYQFKq2k1sKH0QscvwaAYdrpFSxHV|18|28-976-259-9388|6876.37|BUILDING|ole. furiously regular pinto beans affix blithely inside \n1771|Customer#000001771|evmQypmt DbfynZ4bXvm0KUNtyvynyDp3zjcXX|11|21-345-763-5234|3151.21|BUILDING|tes wake carefully according to the unusual accounts. fluffily regular theodolites na\n1772|Customer#000001772|AQla93nCHVF6jkq,J|13|23-887-525-9315|6394.26|MACHINERY|e slyly ironic requests. furiously bold courts thrash blithely bold foxes. slyly express foxes \n1773|Customer#000001773|StUAItIdmWQdpF6Gz|19|29-729-630-8987|7378.35|HOUSEHOLD|are furiously after the carefully unusual dependencies. ideas sleep furiously. slyly bold packages\n1774|Customer#000001774|5pstbh4XaxP91 ,wNQFbR|8|18-753-365-9994|2922.61|AUTOMOBILE|furiously theodolites. blithely unusual ideas main\n1775|Customer#000001775|RUY1tS8uCKV1DrB|2|12-544-332-4550|3221.15|MACHINERY| the furiously silent packages. pending, final accounts breach. instructions according to the quick\n1776|Customer#000001776|,iyGlh4 Wrn2|22|32-425-716-3547|4801.42|BUILDING|l packages. slyly regular requests wake ruthlessly above the blithely bold dependencies. unusual, ironic in\n1777|Customer#000001777|54GpDEcWWIDVMRjP3|24|34-469-776-4539|-535.08|MACHINERY|ar packages? blithely regular instructions along the fluffily ironic theodolites believe ironically aro\n1778|Customer#000001778|XxR5jsS8OA1xjtocU,KS6F0Pte4Go5|4|14-504-368-5987|2772.31|BUILDING|eans. regular, pending packages haggle blithely regular p\n1779|Customer#000001779|3iow2GjE85s8GnxfNO,fnr9T|23|33-986-427-8764|7233.18|AUTOMOBILE|ly pending instructions: carefully regular excuse\n1780|Customer#000001780|ZIeOfVh8umRYig|17|27-213-387-3335|5137.48|FURNITURE|patterns use carefully about the slyly even foxes. furiously regular excuses about the furiously careful packages af\n1781|Customer#000001781|JYtJY4OTZQUaEQlfDEeVkK4mtO|8|18-350-885-2317|4686.31|AUTOMOBILE|s cajole fluffily. platelets believe quickly. furiously even accounts wa\n1782|Customer#000001782|ehV3 VRXVZ9SR3|21|31-246-927-6074|839.66|AUTOMOBILE|beans. requests was about the blithely regular foxes. unusual, final platelets sublate accounts. \n1783|Customer#000001783|ey2RVFXAj5c1qisLEFJA43S2|3|13-239-528-2710|8131.81|BUILDING|dolites. ironic excuses shall wake blithely slyly even theodolites. quickly ironic asympt\n1784|Customer#000001784|Zs8QpbcHZfcVJ6oujM8g69J|15|25-605-903-3007|5458.37|BUILDING|n frays. blithely ironic theodolites haggle carefully. blithel\n1785|Customer#000001785|nnCUQ01AgIgYBDsdHteH4u0na6WiXvBv|23|33-475-488-7723|7805.55|FURNITURE|r packages was. permanent requests are special deposit\n1786|Customer#000001786|pZAHd2LxDUbgcS1WQBN4vvoR5BeNpckOkl7DhG9|6|16-436-900-4501|-51.99|AUTOMOBILE|nts sleep among the quickly regular excuses. slyly even \n1787|Customer#000001787|l9Rin,i89mx8LxSK0cC0BPi3OBcM3BXp|20|30-258-449-6408|4120.78|HOUSEHOLD|ts above the quickly regular instructions cajole about the attainments. carefully\n1788|Customer#000001788|4,Lldd8YNe,DOiCq2dSW4JL0uqjdVpI2yW4fZMDI|24|34-826-793-6480|-45.96|BUILDING|tes promise. carefully final requests use above the blithely express packages. carefully even theodolites na\n1789|Customer#000001789|lfHQVe9IPEKad|0|10-582-797-3122|6407.46|FURNITURE|he slyly express requests. silently daring packages sleep express courts! blithely pending foxes w\n1790|Customer#000001790|z IQ1wwki0OLw7biMFHxPKNjp1JBOs7|5|15-805-568-6053|9177.60|MACHINERY| sleep along the special, bold requests. even accounts against the dogge\n1791|Customer#000001791|nEuCQJ1,wiJUq7k05FED7j6EjZP0QdzriHsUWZ|8|18-937-127-3890|3917.82|HOUSEHOLD|he furiously final instructions sleep regular deposits. blithely ironic accounts boost among the ac\n1792|Customer#000001792|IPqTE3D5cvxDmpveKD6WwW7Pb9ymeAgKs,rT|1|11-561-103-5122|9762.91|BUILDING|to the final instructions will wake quickly carefully special \n1793|Customer#000001793|B13zlM7kKBKt|19|29-393-128-9130|7329.58|HOUSEHOLD|ringly furiously bold requests.\n1794|Customer#000001794|col5dSe1MO8MyVdQ1f09bhAFsVjYwNH8R4I|7|17-670-112-5044|8243.80|HOUSEHOLD| instructions around the furious\n1795|Customer#000001795|EaQbFX VR89kRgd6svC3NK8MSivUK8DZ3y|21|31-853-266-7057|6580.13|MACHINERY|; requests wake slyly after the furiously pending pa\n1796|Customer#000001796|HGRGXJA8AwhfBNSyDNn8j3JMvbIwPUKjPTcvKBLs|10|20-905-459-3952|7327.39|BUILDING|ironic, regular accounts haggle care\n1797|Customer#000001797|5v4QJxOVHNQ3J6NORJE2edRftfg8 HiGu|19|29-805-690-1846|4461.48|FURNITURE|long the accounts are furiously courts. quickly regular exc\n1798|Customer#000001798|fOQAhX8wjDFg8tpeOa L ZdgFlOC69bvmZE|7|17-422-203-8428|6072.64|HOUSEHOLD|hely about the carefully bold deposits. quickly ruthless warthogs w\n1799|Customer#000001799|07tCCGrmdFwcXYnolQabgAZW9yq|20|30-643-275-8135|2967.77|BUILDING|requests of the slyly ironic instructions believe slyly regular \n1800|Customer#000001800|Cc 1QYWD8JeDlRuyLOEffaanH|16|26-566-785-6289|3323.37|FURNITURE|regular pearls. quickly even escapades alon\n1801|Customer#000001801|8ZC3HFVDQGf23cjelZL0wa|3|13-994-265-8339|6806.86|MACHINERY|es sleep carefully along the quickly regular accounts. furiously unusual packages must are permanent accou\n1802|Customer#000001802|fGUhRVo61nZfOPxAxzZLrp4z|23|33-720-109-4385|897.62|MACHINERY|c requests. slyly final deposits cajole bold packages. ironic deposits wake quickly around the ironic, special i\n1803|Customer#000001803|D7E  PteFioyOXeI 422,yZZIsEk|0|10-993-780-6774|3318.35|FURNITURE|its eat quickly around the slyly \n1804|Customer#000001804|,TDgHBX9y5eC5ycEwVKbO3gJjXChWmj|15|25-332-547-7897|-516.28|HOUSEHOLD|quests cajole fluffily after the pending, special pinto beans. packages try to haggle. braids haggle fina\n1805|Customer#000001805|ZERs4Cu5lQTYD|9|19-679-706-1096|-274.75|AUTOMOBILE|ding tithes. slyly ironic packages boost. final, stealthy requests wake. final, re\n1806|Customer#000001806|BB6Vr7W,rSIpWKp|9|19-872-322-3433|254.17|MACHINERY|usly blithely regular instructions. slyly regular dugouts sleep carefully. \n1807|Customer#000001807|jlGhIS6zaYIfu9tWFHAyDKVQpOvIluJ1RunV3X|6|16-707-959-9348|1720.94|MACHINERY| slyly final pinto beans sleep furious\n1808|Customer#000001808|Z Losr9UXEWwm3RgetdFLr6Q|22|32-507-149-7712|2776.30|HOUSEHOLD|oost carefully ironic requests. fluffily regular foxes boost blithely blithely express ideas. car\n1809|Customer#000001809|GqhON8SNyRv|20|30-390-140-3365|8306.29|MACHINERY|ld theodolites eat carefully special pinto \n1810|Customer#000001810|RI0cwmW3gNVJiSnIGooUzA|0|10-274-496-6960|923.32|MACHINERY| the slyly special ideas. regular platelet\n1811|Customer#000001811|GAuB2XYNF6YxAJgUQO1VcXal|14|24-892-235-8707|6104.69|MACHINERY|dazzle blithely. ironic asymptotes breach; carefully iron\n1812|Customer#000001812|UqextI Ph0pve58|0|10-876-771-7164|2515.86|BUILDING| final orbits promise fluffily even waters. blithely \n1813|Customer#000001813|xEoR4tsV2Bse527UyeFO8aFhmZ|3|13-604-485-5526|9074.92|FURNITURE|luffily ironic requests doze carefully after the express packages. slyly regular excuses sleep slyly carefu\n1814|Customer#000001814|wrzLhEh9DAAHPrh19AGxqCxBWQjO52j4qA1fmqw6|13|23-588-705-2608|5215.32|AUTOMOBILE|ully special packages on the bold accounts sleep among the furiously ironic requests. final requests detect quickl\n1815|Customer#000001815|wXx75IZkzX3hsqx5H,PBqxf1CB,cjS|13|23-954-607-5326|3940.94|BUILDING|, bold requests. ironically regular accounts sleep blithely carefully\n1816|Customer#000001816|WemZ1IDB0sBS9yyqszdSxqRm8YtGGDme6 BW|4|14-307-306-8692|2328.09|BUILDING|e deposits maintain about the furiously thin theodolites. carefully iron\n1817|Customer#000001817|,pNXvEI4pkygH 4wjAy,hPxNikXwej29WH8|7|17-246-120-5754|-862.51|FURNITURE|egular packages. carefully even Tiresias haggle. asymptotes haggle furiously across the quickly pending reque\n1818|Customer#000001818|wSYuvOMT8YVZqRDS1YCGrALb|22|32-897-843-2620|-331.97|BUILDING|about the blithely regular requests. ironic requests sleep. blithely pending accounts are according to th\n1819|Customer#000001819|pfaH03EbQcSE64yOEnKz7mwHHn|18|28-832-601-3029|6435.85|AUTOMOBILE|y even gifts haggle since the slyly bold warthogs.\n1820|Customer#000001820|RsTONzasImDok,RBqc2J09pa29w8gSf6JDIuCBx|6|16-496-603-4437|2282.18|BUILDING|ymptotes affix fluffily doggedly express packages. ex\n1821|Customer#000001821|QlxYI,DDZRUAyyVfdaF4q nurCpm,3FltjM|11|21-278-374-9593|8593.01|FURNITURE|tegrate slyly carefully final requests. ironic, regular package\n1822|Customer#000001822|H79GEJ2,C0JTus4zVXNjEyMqEFe,7RmzxIGvU5|14|24-524-173-1344|1728.52|AUTOMOBILE|regular forges. ironic packages sleep furiously. slyly fina\n1823|Customer#000001823|ci7lCqnRGp|19|29-787-260-1556|9240.38|FURNITURE| furiously. blithely final foxes integrate alongside of the instructions. quickly regular \n1824|Customer#000001824|aN7aUMe22hd2LnIqVf7TnJLzSI8uggv,YpldbC|10|20-684-209-1084|3177.51|AUTOMOBILE|luffily regular, pending instructions. slyly pending foxes\n1825|Customer#000001825|,E8l78G7k,u0eGXu,sGU1fta2Lg|8|18-969-538-4715|8447.18|FURNITURE|ing to the slyly silent deposits. \n1826|Customer#000001826|tbWBFvYUZjBY,BH5r5CQsA71GJIQJNx|3|13-389-673-9030|3891.76|BUILDING|e. bold deposits among the slyly regular depths detect slyly a\n1827|Customer#000001827|icXm8xlUo0Ca,T9MoUjkWej3|24|34-815-928-1369|800.31|BUILDING|se carefully alongside of the quickly special packages. regular requests run. pinto bean\n1828|Customer#000001828|djbEIhlvmo1i8ZUgTNLFT2f1P|13|23-937-592-5811|6673.46|AUTOMOBILE|into beans alongside of the unusual foxes breach blithely above the quickly dog\n1829|Customer#000001829|9TWqvF0jyqs5eJ O0OPAprl0chk8WGTf,|0|10-582-390-6176|2886.86|BUILDING|ccounts. boldly bold foxes use slyly even requests. never regular dolphins at th\n1830|Customer#000001830|d6ZvjlxbHL6Kq5y,|17|27-148-316-6372|7363.56|AUTOMOBILE|around the fluffily regular notornis integrate above the final deposits. pending requests nag carefully packages\n1831|Customer#000001831|DcW02etJU0N2SmSs3UgkmkPRvao7A|13|23-215-897-5399|7448.78|FURNITURE| thin ideas. furiously quiet pinto beans w\n1832|Customer#000001832|VffJ90poBdj 9fq7Hpp0NvcEeeuHg |10|20-337-677-6483|9640.29|BUILDING|. furiously express ideas wake furiously. unusual, bold requests nag quickly. slyly regular\n1833|Customer#000001833|KluDIF0rSvGj8mjBA vo7db1bA|3|13-425-435-3864|3418.04|BUILDING|y quickly slow requests? ironic, final pinto beans eat furiously. slyly regular deposits sleep\n1834|Customer#000001834| oWU4qpGp8v5pjfHDGwIAh7z|24|34-976-916-8306|2985.37|HOUSEHOLD|slyly unusual packages play. unusual, ironic ideas integrate always according to the thin requests. ste\n1835|Customer#000001835|XwMDWDz0zgJT7CpiEZ7Mak8kB6m|7|17-285-512-9067|2081.99|AUTOMOBILE|s. carefully unusual packages along the carefully idle pinto beans haggle carefull\n1836|Customer#000001836|0oV7OBdPNyXRqIesxfDU0Ol4So|7|17-927-450-7081|9925.39|FURNITURE|ate slyly idle depths. silent, even theodolites are furiously ironic \n1837|Customer#000001837|sWO0VNEY4GzuBq L0vJ5p2|14|24-908-704-5003|-886.94|AUTOMOBILE|unts after the bold, regular dependencies breach a\n1838|Customer#000001838|4nF9mi2bj3s82Sv8KPgIsS|21|31-194-756-6301|7142.14|AUTOMOBILE| deposits. regular requests nag slyly-- regular accounts are furiously acr\n1839|Customer#000001839|QKjHd6wOQJaxJCOqH2E5P n2tZb3Y|5|15-944-305-1436|3909.79|AUTOMOBILE|haggle furiously after the regular packages. ironic, regular foxes haggle\n1840|Customer#000001840|cMNMPjKib0Ez0Bl,cesLvkOl39p4oiUH |3|13-833-461-5939|6879.93|BUILDING|the courts sleep blithely according to the slyly permanent ideas. \n1841|Customer#000001841|xJLMIrO4OB|10|20-566-527-9390|8053.62|BUILDING|ts snooze furiously final waters. furiousl\n1842|Customer#000001842|SYMcJ0iGluYT2pe6NR5jyErQBODPK|10|20-148-955-7830|233.23|BUILDING|y regular theodolites. packages sleep blithely according to the busy, final instructi\n1843|Customer#000001843|5Op16OGLtq5hXrYvKBKPPu,B1pB|1|11-977-879-2346|1821.61|HOUSEHOLD| requests after the never bold ideas hang carefully bold sheaves. carefully pending deposits\n1844|Customer#000001844|1BOo5no2lQ8Fa CLDWFaRC8j|18|28-705-205-8653|798.99|HOUSEHOLD| about the slyly final foxes. furiously pending asymptotes acco\n1845|Customer#000001845|xHga IhnB2a,|10|20-777-704-2095|6567.69|MACHINERY| furiously requests. even instructions haggle. instructions haggle fluffily. requests along the carefu\n1846|Customer#000001846|v0hJC6rlPBHdWAUGg7xm7SEUhpI3wWIO2|16|26-860-238-2408|7115.80|BUILDING|riously regular deposits wake regularly above the slyly unusual requests: unusual packages are furiously furiousl\n1847|Customer#000001847|g12l6sqk5YNADO,NEnDg, prphdZp|16|26-677-971-4905|1240.90|MACHINERY|. furiously express packages wake quickly. sl\n1848|Customer#000001848|g xIZPvP9AkmbUQa6e|4|14-161-419-6384|8684.90|HOUSEHOLD|as. final, regular asymptotes haggle in place of the slyly ironi\n1849|Customer#000001849|vRc6mWLRoRf2ElUvz9byqx2Vt|22|32-730-179-7629|6259.04|BUILDING|ckages. bold epitaphs maintain slyly alongside of the carefully exp\n1850|Customer#000001850|yY0Ihs9rn4Jt9qzowqGCKcGYUYVqrOpfKVArQK|10|20-451-644-7838|722.28|BUILDING|lly regular accounts. carefully regula\n1851|Customer#000001851|n873H6Cz5A9uHamGSt iIs3|3|13-311-225-1271|4290.76|BUILDING|e slyly regular pains wake slyly final requests. quickly \n1852|Customer#000001852|LCTu83UaCBLeatTuc|14|24-811-458-3601|7717.57|AUTOMOBILE|ar, final accounts. fluffily bold deposits cajole. ironic deposits above t\n1853|Customer#000001853| z1i M6vmUfw|5|15-442-128-2785|6244.70|FURNITURE|ly pending instructions. blithely special pinto beans sleep carefully quickly final packages\n1854|Customer#000001854|CYDzQ3P8qyP,o0ZCHt oAxFNfmlkY18|14|24-654-947-6633|1661.15|BUILDING| the fluffily silent deposits. quickly\n1855|Customer#000001855|REHQRPduxffKW5vE8Laf|13|23-239-487-1955|9936.71|MACHINERY|nto beans cajole. pending packages nag quickly according to the packages. s\n1856|Customer#000001856|FuvHERUFZy4lUPSGX9WzC6rM3dLy4DRPv21scFOR|21|31-242-270-7219|2381.48|AUTOMOBILE|ic requests. furiously silent requests wake slyly according to the never pending packages. slyly iro\n1857|Customer#000001857|4BOgUDmgtH,RyMRb4jUc TDYqFB|12|22-974-598-1668|5485.71|HOUSEHOLD|sits doze among the ironic packages. carefully express requests wake blithely alongside of the theodolites. ca\n1858|Customer#000001858|jtVS,lDzoPG7|18|28-940-298-3762|5263.13|MACHINERY| unusual theodolites affix according to the silent instructions. furiously darin\n1859|Customer#000001859|MV0YXkg6XVwBzeXMljCiLp|1|11-147-516-2776|4508.50|AUTOMOBILE|t ideas. bold deposits across the quickly slow excuses haggle slyly unusual packages. furiously final ideas caj\n1860|Customer#000001860|4,u2YoI2nnY|15|25-432-186-2226|4383.03|HOUSEHOLD|ackages wake on the pending sheaves. bold foxes nag slyly slyl\n1861|Customer#000001861|E XEUZYUxRnh0Z1MKcU19Ff2kj7|15|25-966-583-8374|7584.92|HOUSEHOLD|blithely regular instructions detect fluffily abou\n1862|Customer#000001862|jDYsot4wXtwiJ9W 7XqR,C|7|17-972-634-9470|9848.27|MACHINERY|s are after the blithely even instructions. slyly regular\n1863|Customer#000001863|kfPxNgJute862KvopjfTItHJVC9Hu|16|26-777-313-6429|1024.85|FURNITURE|fily final accounts cajole furiously furiously pending accounts. slyly final requests acco\n1864|Customer#000001864|ZTAtP8aj97X246rzbsglEXqogjhEcYybWE3W|22|32-582-138-8697|7669.96|FURNITURE|uriously. final packages detect slyly blithely r\n1865|Customer#000001865|0wViSaeE08WI09xFxqr58|14|24-208-401-3922|9112.90|BUILDING|luffily regular dolphins. quickly regul\n1866|Customer#000001866|bdPJelrGfDMOilIhvlZNmb3|6|16-520-541-7567|5563.66|HOUSEHOLD|ent accounts about the blithely unusual dolphins are carefully along the special deposits! final excuses u\n1867|Customer#000001867|72gC1ZTlzFNQy9CvFosWzE|1|11-951-119-6203|6470.11|MACHINERY|final instructions are according to the carefully final accounts. caref\n1868|Customer#000001868|kD u32wkAhHa FvvBM,y6oJVpfZQDcuDBQ|2|12-347-925-5872|6150.48|MACHINERY|onic, final ideas haggle blithely excuses. slyly regular ideas across the carefully bold dolphins cajole sly\n1869|Customer#000001869|hOzuX9JGbYqPv677zUdWtUN3,LHdGYcEwqva|6|16-750-945-2045|2181.16|BUILDING|nts. special, unusual pinto beans haggle slyly \n1870|Customer#000001870|iAG9OC,akOL06jR|1|11-950-597-6395|4739.19|BUILDING| epitaphs according to the slyly regular foxe\n1871|Customer#000001871|,B2hG X848Trk9SZ|15|25-329-168-1291|9528.08|AUTOMOBILE|y express excuses. furiously regular warthogs integrate quickly after the regular packag\n1872|Customer#000001872|CTHfC,m5kcnglbJawTVx5x1cJx3lynOTa7|22|32-985-585-5168|9479.72|FURNITURE|kages wake carefully after the ironic, special requests: regular foxes from the special asymptote\n1873|Customer#000001873|fvkv57 kYzsIjzXH9,csoT7smnh pObla4,|3|13-581-396-9191|-201.93|BUILDING|yly. fluffily unusual theodolites among the ironic ac\n1874|Customer#000001874|DVHIWiilJuI|17|27-133-279-5869|3121.93|FURNITURE|s. carefully final requests unwind furiously among the \n1875|Customer#000001875|CiwSrmNO0dw3Bvf, v2NL,|10|20-222-437-7307|9319.39|MACHINERY|equests cajole above the slyly final requests. blithely special excuses nag blithely bold dependencies. blithely exp\n1876|Customer#000001876|pUsteCJxfoVJ|7|17-965-399-4552|90.46|FURNITURE|s. ironic theodolites around the specia\n1877|Customer#000001877|a1Rycy,Cni7sr7VUyLR|23|33-986-738-5501|341.10|MACHINERY|ss instructions. fluffily ironic packages boost alongside of the blithely final patterns. s\n1878|Customer#000001878|EJqsZwacKoF5mzgYOzYM5Tt7tcuJ|6|16-201-771-9025|-120.65|MACHINERY|xes. furiously bold platelets sleep quickly among the slyly bold theodolites. special deposi\n1879|Customer#000001879|DsH91sbfSoNl3Ohor9NQGBO94rGJeGRkc4|17|27-652-714-8086|5163.22|HOUSEHOLD|theodolites could cajole quickly about the ironically final requests. blithely even asymptotes use theodolit\n1880|Customer#000001880|LRh,W2K0FBWZWB|16|26-331-549-2024|8247.29|HOUSEHOLD|aves are ironic, final accounts. regular asymptotes promise pending epitaphs-- ideas\n1881|Customer#000001881|eo82SRJdBgn25wzgDGaYETk70Wj Y3NKEat|18|28-135-437-2467|-780.99|HOUSEHOLD|to beans haggle even, unusual packages. fluffily regular de\n1882|Customer#000001882|U3c UftfresD4TjzRl8F|3|13-454-520-9987|5785.07|HOUSEHOLD|uickly regular, final accounts. furiously special theodolites cajole \n1883|Customer#000001883|vyFP6B54fLxqmV3RkbkgNbb|18|28-359-399-4989|9692.28|MACHINERY|kly express packages haggle slyly according to the furiously even orbits. \n1884|Customer#000001884|TFjb8jysqh0cMWSt36NLtlP|0|10-570-500-9765|8261.18|HOUSEHOLD|s over the ironic requests sleep fluffily carefully bold dependencies. \n1885|Customer#000001885|WceTHrKwpwDq4AbQiFFO03R88q2|8|18-986-991-1839|1642.14|MACHINERY|. pinto beans wake along the theodolites. caref\n1886|Customer#000001886|rEn7jolYiPkRQDuLU8lSbn|13|23-315-177-8462|9333.99|FURNITURE|ar pinto beans sleep even deposits. slyly special requests are. express platelets snooze blithely \n1887|Customer#000001887|F9k42yQM7WJEwLS|10|20-704-266-2935|3370.69|AUTOMOBILE|s wake carefully against the carefully ironic deposits. finally regular attainments\n1888|Customer#000001888|3psgNE6QfX,Iw2xTWauq9yL9EkSiD1fAUMXfXu8k|5|15-757-359-6861|-861.72|AUTOMOBILE|ntain ironic foxes. unusual accounts sleep quickly. quickly regular accounts are blithely. fin\n1889|Customer#000001889|am4T 9xjY,|24|34-941-762-4541|1977.38|FURNITURE| ideas detect blithely. accounts haggle fluffily against the ironic, ironic accounts. slyly p\n1890|Customer#000001890|QvxzOMYHMQUUfnfY3Fhydb|9|19-980-717-9488|704.54|BUILDING|nic sauternes. even pinto beans are carefully express requests. carefully express packages across the blithe\n1891|Customer#000001891|xdhHnr2OrFVgZSEY190|9|19-323-409-2180|3069.79|HOUSEHOLD|ording to the blithely final requests. requests sleep quickly according to the ironic, even deposits. blithely ir\n1892|Customer#000001892|Mm4BlT6Q10AlsxwZBAoJE,rXVDtArlKo3b,Ysk|14|24-677-485-8489|1917.06|AUTOMOBILE|luffily around the slyly ironic platelets. express deposits hang furiously among the r\n1893|Customer#000001893|iFCRrPifE A|1|11-796-419-9244|9971.58|HOUSEHOLD|ccounts among the fluffily bold pinto beans \n1894|Customer#000001894|X,KINka,nciwGbK|12|22-832-993-2803|5415.55|BUILDING| final deposits. regular frays was blithely carefully regular platelets. even dolphins run among the \n1895|Customer#000001895|PAv4VeG04NXcldWKmT|12|22-350-120-2910|3216.87|HOUSEHOLD|onic pinto beans sleep quietly regular Tiresias: slyly regular excuses ha\n1896|Customer#000001896|4QX6hX9fgTooTWKoy6jmMOEpNZHoNJ|16|26-210-412-7721|9849.67|MACHINERY|re among the slyly express packages. dolphins affix furiously across t\n1897|Customer#000001897|bizbUJTTu896QHClLsGIeQmcIF5K,7pyN|8|18-397-783-2293|2415.01|BUILDING| fluffily final theodolites. blithely regular requests\n1898|Customer#000001898|yK530x63pGQpzYZbGjsmt0INzw2f9oKfocxs0b|16|26-214-695-2065|7975.89|MACHINERY|ar requests. slyly pending accounts haggle blithely. busy account\n1899|Customer#000001899|XuQb0moba ,XyN|10|20-243-672-4974|2869.40|MACHINERY| even, idle deposits about the busily final foxes\n1900|Customer#000001900|lf78mUY8AHVrPld7M8Ysotn9WoXwKoN|21|31-565-138-3230|-57.96|MACHINERY|kly regular accounts. requests use furiously alongside of the carefu\n1901|Customer#000001901|TJVv IWFTUUp ihC 7mzH,ru5s0J9Xb|24|34-736-824-5989|2749.93|FURNITURE|s use. express platelets beside\n1902|Customer#000001902|03 VVAFfgEADO1Ert upreETF9E9i1QUgy53i|3|13-815-127-1409|5019.36|BUILDING|after the blithely regular packages. carefully regular gi\n1903|Customer#000001903|oNu,vFSWInYzj5wIB5khmgObNMx61UfUap|15|25-489-905-9207|-679.30|FURNITURE|slyly regular requests sleep. blithely ironic packages c\n1904|Customer#000001904|qtgYwanpIR3LfvbOPexVyJ8XHDWNn p95|24|34-565-694-9621|9913.42|BUILDING|. furiously special requests haggle finally carefully \n1905|Customer#000001905|P xPYefatHsYxGtFYbzin3GqmqPWvR31YlXlOt|19|29-697-760-7269|6862.64|BUILDING|ajole about the pending, bold requests. furiously regular platelets above the q\n1906|Customer#000001906|pByvzLPeSKa8Zlh5ncJwAuYK2U|5|15-956-730-8661|3448.10|MACHINERY| the fluffily special foxes. carefully pending deposits by the special, final accounts wake care\n1907|Customer#000001907|Wjpx2yEVYfTUHPc9TgXzD64PBqW x|24|34-755-117-7537|2145.93|HOUSEHOLD|carefully silent platelets wake fluffily express deposits. forges against the ironic platelets are silent requests. \n1908|Customer#000001908|eT6lmLbi11KA|17|27-275-292-5975|6551.65|HOUSEHOLD|ly unusual requests engage carefully blithely express depos\n1909|Customer#000001909|wrQOWC 6kE|19|29-108-892-4321|1605.61|HOUSEHOLD| the regular, unusual accounts. fluffily final theodolites d\n1910|Customer#000001910|LQ9x01e0v2qFuNr|5|15-112-377-2843|475.44|FURNITURE|. quickly even deposits sleep f\n1911|Customer#000001911|F9kOxmOLLFyC|1|11-369-744-8780|2601.29|HOUSEHOLD|y idle requests poach across the quickly final theodolites. slyly ironic re\n1912|Customer#000001912|1AHyJO5HDpo5AiczS 8zP9P9x7PUuoU5wwrTLEb|20|30-784-527-9935|3886.95|FURNITURE|uriously above the ironic requests. blithely careful packages use daringly furiously regular tithes. \n1913|Customer#000001913|3Ya7Xzjzd,JayN,jN|8|18-831-659-1744|8031.55|BUILDING|y express platelets. quickly ironic requests hinder quickly against the carefully bold deposits. acc\n1914|Customer#000001914|JqCaLg2nQOwrdP1DIHNQ9b,Wk|6|16-458-421-7729|3308.34|FURNITURE|ss pinto beans. quickly unusual packages doze carefully furiously final\n1915|Customer#000001915|FMtQbAxpu5v3,0NVxEJwU24sV2TgF|16|26-683-446-1665|7268.98|AUTOMOBILE|s. furiously ironic ideas boost furiously among the ironic pinto beans. accounts thrash slyly about the\n1916|Customer#000001916|jFGCdeXJVKzv7YDL|5|15-662-459-4512|1768.76|HOUSEHOLD|into beans. even deposits among the fluffily even platelets cajole ab\n1917|Customer#000001917|gZW6epQmK,ISulI9 Qt IWytnY|7|17-940-989-5599|3470.08|AUTOMOBILE|silent packages boost quickly. regular, regular instructions nod. idle dependencies are slyly among the slyly i\n1918|Customer#000001918|CRBet8MbYBwTgtdPOzX,ipUvTiOrp0vUQjnr|9|19-987-388-4413|8539.61|MACHINERY| ideas alongside of the blithely regular\n1919|Customer#000001919|lnfRcSyQXSI|18|28-664-832-4585|6262.18|HOUSEHOLD|as outside the furiously expre\n1920|Customer#000001920|FvwmRwSmKgzj7u4disg0ahJOtEfo|8|18-810-300-8541|9476.97|MACHINERY|uctions believe. furiously ironic accounts use fluffily. regula\n1921|Customer#000001921|9AE6JuRlhkABh1m8ABQD4AsBk6x5rXlCQ5I|18|28-644-770-2274|8509.33|BUILDING|lar platelets was furiously above the slyly exp\n1922|Customer#000001922|qMXL1bzgCtSj1GLMI3AMi1CNV,f, 0iCXt|9|19-532-924-9245|3398.55|MACHINERY|ake carefully even packages. ironic requests against the express, even asymptotes affi\n1923|Customer#000001923|m6AVOkvy swPbt,pZ7b9MBfr8zo9vFUvappyyWjv|23|33-781-817-1440|5376.70|HOUSEHOLD|waters detect fluffily carefully regular pinto\n1924|Customer#000001924|inc0YxJOJYunXhf|4|14-472-427-4447|-851.49|AUTOMOBILE|gular instructions boost among the blithely s\n1925|Customer#000001925|qZ8m0grVeRJN9h6Bn|0|10-814-492-1424|8891.65|HOUSEHOLD|its sleep among the regular packages. fluffily final depende\n1926|Customer#000001926|EMRuUDTA2m0|7|17-203-685-7821|4968.09|AUTOMOBILE|ve the even packages. blithely \n1927|Customer#000001927|V4zstahq,OaYcbh2IeSkJfiRO7wtMPXOoV|1|11-876-774-5282|6756.49|AUTOMOBILE|side of the furiously final ideas are at the bold patterns. quickly pending requests thrash fluffily\n1928|Customer#000001928|Ce02vQsU97|12|22-812-639-3782|1391.69|MACHINERY|t the carefully final instructions wake fluffily f\n1929|Customer#000001929|imHqCzuePilpP5h UA2fwTIJMWVdj|20|30-949-561-2278|2157.13|AUTOMOBILE|osits are slyly across the even requests! busily special platelets cajole carefully \n1930|Customer#000001930|kJD6LiynLuutDPKOcgIBUIdUQIWb14oGE jb|18|28-793-928-3242|7909.13|HOUSEHOLD|ular packages snooze blithely carefully express packages. quickly regular dolphins sleep about the slyl\n1931|Customer#000001931|DUbNQry9jewVHI6GLNIbX0aIBGoWwsqWabbuXPy|19|29-893-435-8218|2762.06|BUILDING|heodolites. final ideas detect slyly furiously regular dugouts. even pinto beans haggle. slyly express foxes na\n1932|Customer#000001932|VhBhkEXBrpSzYgxu1jmIJ3 tVomaDEr|15|25-252-625-7296|-55.92|FURNITURE|fully regular theodolites. quickly express requests may sleep carefully among the furio\n1933|Customer#000001933|JGeZTkGT1uwbQa1C,sj|0|10-484-847-9516|4003.10|HOUSEHOLD|es the accounts. close, ironic dolphins wake quickly furiously ir\n1934|Customer#000001934|REnXv9yHCBrQTv2i7j645dr5iSY5|10|20-656-890-6862|2038.31|HOUSEHOLD|ons. bold foxes haggle furiously blithely ironic ideas. blithely ironic deposits integrat\n1935|Customer#000001935|2SwVYEBhbhWTTWCBsGow0Wu|9|19-674-562-9631|1775.61|HOUSEHOLD|ilent epitaphs wake slyly according to the slyly furious deposits. silent, special instructions past t\n1936|Customer#000001936|PGu2Wen188BC|0|10-714-881-1338|8592.20|HOUSEHOLD|y regular packages. blithely even accounts are blithely. carefully regular accounts cajole blithely. quickly bol\n1937|Customer#000001937|mXJvkSoAwpg1wVW45|3|13-166-456-2309|-866.47|FURNITURE|equests. final, ironic packages haggle: carefully regular packages mold fluffily accord\n1938|Customer#000001938|IqaZSFbDxA,8a,xxnV,gHVOT|10|20-789-331-1347|-894.49|AUTOMOBILE|osits affix slow accounts. carefully final ideas according\n1939|Customer#000001939|SBEYKDkrUBAt2RNh1z0dtG|0|10-417-450-5341|1306.86|HOUSEHOLD|usly unusual theodolites haggle slyly around the fluffily ironic asymptotes. express instructions cajo\n1940|Customer#000001940|DIL GvUsP25WSgsshTdjULCPxy7l|24|34-475-738-8227|6310.63|BUILDING|e the furiously bold dependencies. slyly silent accounts grow carefully according to the\n1941|Customer#000001941|,51rsqq9XV|0|10-365-133-1400|9710.06|FURNITURE|fluffily carefully regular foxes. fin\n1942|Customer#000001942|5 eTPX2yTp49JPctgc1EeYMUSWm|3|13-500-263-4736|-947.24|MACHINERY| sauternes. quickly express accounts cajole carefully above the regular accounts. pinto beans\n1943|Customer#000001943|sH4nc83Lh,6A2xe9ApH6WD|3|13-737-218-4651|5599.71|AUTOMOBILE|ckages above the final, final pinto beans haggle against t\n1944|Customer#000001944|LbrsZ4CFBqJZ|14|24-492-922-6990|7279.93|HOUSEHOLD|s play boldly about the slyly iro\n1945|Customer#000001945|ghlnLGw i,LV,MAuts8Ii7XE4WaoExoa|10|20-470-267-5365|9880.10|BUILDING|iously bold pinto beans cajole slyly warthogs. silent, special accounts detect evenly across the ironic pl\n1946|Customer#000001946|Z3ceD0uA1BJsDmFl4botusq2SBPCJqIqzr9bHQM|20|30-764-188-4293|7851.87|FURNITURE| requests boost fluffily. ideas detect finally. quickly final deposits nag carefully. quick\n1947|Customer#000001947|s 1fCYZ4AqqTJ,ZvJsLi5N|20|30-174-661-7603|8385.53|MACHINERY| ought to are furiously alongside of the blithel\n1948|Customer#000001948|4aCCe9W1s4,VdYswJWigEOJ2sW6Thh|5|15-227-107-9153|1704.12|HOUSEHOLD|s about the blithely regular deposits cajole blithely \n1949|Customer#000001949|fwIfk5BDREkV uapHbKzYrOCWUz|19|29-227-849-7731|6025.80|FURNITURE|nal platelets cajole whithout the silently regular ideas. furiously bold asympto\n1950|Customer#000001950|sRX9I79UagRC3 sV9hO0H|6|16-524-582-1728|7698.52|HOUSEHOLD|nal requests. slyly pending pains according \n1951|Customer#000001951|NOXYVa52HyF5lmpj9,WR0|0|10-334-921-5346|5002.24|MACHINERY|thely unusual accounts. furiously unusual deposits sleep along the blithely final packages-- f\n1952|Customer#000001952|sZvM,CF54wYyQGhSuFVe|18|28-102-243-6615|8620.01|MACHINERY|y express pinto beans sleep carefully carefully regular pinto beans. fluffily final accounts \n1953|Customer#000001953|vd2GjYHxhaYoyE0dU46shW|8|18-155-378-1656|4654.64|MACHINERY|ly slow foxes x-ray quickly even pinto beans. slyly permanent packages nag furiously. carefully\n1954|Customer#000001954|ec2Cv6JlL3MFSnbBVWrE vljCiPiM6gFr4WE5PW8|22|32-872-341-1949|7575.28|BUILDING|ep furiously about the fluffily regular accounts.\n1955|Customer#000001955|INxAk4G6dbQSijuSR,0t|7|17-649-408-5594|1754.99|BUILDING|usly. final packages doze carefully excuses. packages wake slyly according to the slyly ironic pi\n1956|Customer#000001956|sOgO,FYO1KNY3Q45sj asSLH33rFk|21|31-426-823-2422|3007.04|HOUSEHOLD|ess platelets. express pinto beans wake furiously according to \n1957|Customer#000001957|Hl2JPoWaDAeyGd|21|31-861-837-2140|3366.66|HOUSEHOLD|al platelets. sometimes final accounts\n1958|Customer#000001958|P8lsQKRVNyrMTYEgXU7W|4|14-218-444-2269|8425.51|HOUSEHOLD|quests across the accounts need to haggle carefully regular deposits. careful accounts boost carefully fin\n1959|Customer#000001959|NMDtn0GA,CLrZYGaMDmwb|7|17-992-958-9639|2608.12|AUTOMOBILE|ronic deposits: quickly bold packages along the fluffily regular depths sleep furiously thinly regular deposits\n1960|Customer#000001960|DFG0YX 4bZQ40vdnfLc8zJfgdWY4iARmum|1|11-645-941-2149|8663.17|BUILDING|arefully carefully regular pains. carefully unusual ideas at the furio\n1961|Customer#000001961|j7 XCftSKhgKscMoPW|23|33-428-682-4988|3918.30|MACHINERY|ut the fluffily regular theodolites. dependencies among the\n1962|Customer#000001962|VxLrW,XnARNIdQgq1J |4|14-913-275-9532|6907.64|FURNITURE|os shall have to eat bold theodolites. regular dependencies sublate blithely alongside of the express, \n1963|Customer#000001963|WFokQKiIqXjIiH|2|12-406-701-6501|680.33|BUILDING|ously fluffily express theodolites. ironic, final dolphins snooze regular ac\n1964|Customer#000001964|xEoAWLcXGm|11|21-651-389-8060|3323.69|HOUSEHOLD|osits. furiously express ideas integrate \n1965|Customer#000001965|Zf89ewXnl,RNe36J86yVxppUfr6VXEeKGYUa|15|25-361-492-1713|1223.48|BUILDING|ronic, even waters use along the final ideas. ruthlessly\n1966|Customer#000001966|jPv1 UHra5JLALR5Isci5u0636RoAu7t vH|0|10-973-269-8886|1937.72|BUILDING|the blithely even accounts. final deposits cajole around the blithely final packages. \n1967|Customer#000001967|F1wAzk2iYcb|14|24-936-670-3499|5250.63|BUILDING|ut the slyly ironic pearls. re\n1968|Customer#000001968|dUAE71eduW|24|34-192-617-7717|3322.72|HOUSEHOLD|uriously regular deposits use car\n1969|Customer#000001969|F7fcrNnwmFLr2fXyi58|11|21-978-965-1419|6864.72|FURNITURE|ckages sleep carefully. never bold instructions are even, final ideas. fluffily even id\n1970|Customer#000001970|V9aSisZGbj8fo|14|24-949-639-9364|5469.10|MACHINERY|grate furiously ironic orbits? final, pending deposits affix carefully express ideas. special, express pack\n1971|Customer#000001971|DZsft5O035OEx6ql2BKC3CHI3R5ZtlFgKsMs XZM|12|22-293-654-8027|1205.73|AUTOMOBILE|arefully. express packages are above the slyl\n1972|Customer#000001972|0XaaQuagZ1K9E63qlSw5SBk|7|17-991-532-5136|689.12|AUTOMOBILE|ckly. regular, pending pinto beans solve around the blithely exp\n1973|Customer#000001973|J8,WbeY81R4j4nAv5CEczBfn3XPh2dIcxnhLtdf|4|14-828-797-8149|7990.18|HOUSEHOLD|dolites affix bold, final requests. blithely special accounts integrate. bold, iron\n1974|Customer#000001974|e4NzvGNJLIOkXfof8RF21qDH1O|24|34-740-820-4310|1007.21|AUTOMOBILE| sleep against the quickly ironic sentiments; ironic packages affix above the carefully final \n1975|Customer#000001975|XzyjsmoapfpiWPo fn|5|15-174-926-1370|8125.51|MACHINERY|re slyly against the unusual dependencies. slyly\n1976|Customer#000001976|M2WWTo3je0NgpIFOe55SvtJwnyxY8jp8Ikms4|15|25-703-881-1012|9548.13|MACHINERY| slyly regular deposits. fluffily fina\n1977|Customer#000001977|TMAohw9bEg3eDJH|2|12-534-854-9706|5529.03|BUILDING| carefully bold requests up the final, speci\n1978|Customer#000001978|e4sYh5qmltXfpKhnO fQF3E9dZuWXu,C0E|13|23-513-770-1875|7503.87|MACHINERY|ully regular dolphins. blithely express requests are furiously slyly regular accounts-- caref\n1979|Customer#000001979|HSGMB5gHS1ieJ58hBBImFA9OxE|14|24-690-108-2317|631.63|MACHINERY|ns alongside of the bold, silent requests x-ray after the blithely silent p\n1980|Customer#000001980|rhVLwo9PmcGj8oWJSAIYmcbPf 2lCekjg6V6|6|16-721-559-9819|9772.49|AUTOMOBILE|ckly regular packages. ironic packages after the special instructions breach fluffil\n1981|Customer#000001981|wctyYavWwznYKAs4|13|23-867-283-2614|3283.90|FURNITURE|ding to the blithely bold deposits. ironic packages detect blithely deposits. regular, specia\n1982|Customer#000001982|mpS3z3Av8c89RsaJyiet|12|22-891-773-1026|3567.30|HOUSEHOLD|e blithely courts. even, express re\n1983|Customer#000001983|KFNKCUFOyS|0|10-305-434-9284|3640.90|FURNITURE|es sleep blithely regular instructions. regular instructions cajole carefully. slyly regular accounts\n1984|Customer#000001984|MAqwYLxOBbMoyAWwvjEZK9QYgRMbhtFkdHbiR|13|23-768-636-1831|8661.08|HOUSEHOLD|y unusual requests. furiously ironic deposits haggle quickly a\n1985|Customer#000001985|Cy7CMgihIn6lemMjU2d3SKiOmHXi0TrRZK|0|10-248-352-6397|1800.96|HOUSEHOLD| theodolites wake slyly fluffily regular pinto beans. furiously ironic packages according to t\n1986|Customer#000001986|3ugRcAUZuXms2oHREuEsprYLg2u|0|10-971-309-8075|5749.20|FURNITURE|fter the slyly even deposits cajole pending accounts.\n1987|Customer#000001987|lWwjammTGaKVKq9npAtfs3|19|29-745-306-2348|796.96|BUILDING|s. blithely even pinto beans boost always about the doggedly bold requests. regular requests use. \n1988|Customer#000001988|MdIopEKT9C1|9|19-927-922-5628|5414.64|AUTOMOBILE|ve to are furiously bold theodolites. qu\n1989|Customer#000001989|,WMIpzbXk5YrV7ZlH23grs5TeGhEtF|10|20-648-105-6440|9859.77|MACHINERY| unusual foxes wake slyly fluffily pending gifts. bold packages across the slyly \n1990|Customer#000001990|M9VbFPNnktfGtPgnf3t1ptNYOzS3eCwlKpte|14|24-543-274-3227|7244.87|HOUSEHOLD|usual asymptotes around the packages haggle \n1991|Customer#000001991|REzgRDc,2RF4ayaV3b3gZhIdMti9J6b8b90p|9|19-351-767-3188|3517.27|FURNITURE|nag blithely slyly special deposits? express accounts about the dolphins woul\n1992|Customer#000001992|FNQZbT88wzGKhU9|1|11-760-815-8444|9679.43|BUILDING|e fluffily regular ideas haggle past the even requests. regular pinto beans nod acc\n1993|Customer#000001993|Yi8QRnZDlVqZfW8SHbw,drKwsPBd6qXjavl|0|10-928-142-7350|1209.74|FURNITURE|regular pinto beans. furiously ironic epitaphs after the \n1994|Customer#000001994|TRsalXgbwGxicJe1Ogt4th3BwgmrADqgILXd|1|11-384-723-1000|1971.60|HOUSEHOLD|taphs along the furiously regular dependencies must have to run blithely idle dependencies. slyly fi\n1995|Customer#000001995|aIGN3hurONjCDOvfE JLx,5pmtqCPnXvgqV7VNy|2|12-339-453-3201|2674.92|BUILDING|ilent instructions sleep quickly quickly bold gifts-- final deposits grow after the final asymptotes. care\n1996|Customer#000001996|SkYj5PWHdXD9|17|27-626-208-5273|9379.16|BUILDING|lyly final accounts are blithely final courts! even \n1997|Customer#000001997|D7SUlJ,KUdYu|24|34-724-701-3880|3610.05|FURNITURE|accounts wake furiously. silently quick epitaphs are carefully slyly final pinto bea\n1998|Customer#000001998|RpAD0CJxRxC2kjR|14|24-529-154-9925|7080.37|FURNITURE|p slyly alongside of the pains.\n1999|Customer#000001999|y8mRnn6pJ0V|2|12-967-439-5391|-117.85|AUTOMOBILE|heodolites. furiously ironic excuses boost along the carefully f\n2000|Customer#000002000|NabVtyaWao1l9Ss|13|23-122-829-3487|8176.83|AUTOMOBILE|s shall have to sleep quickly\n"
  },
  {
    "path": "src/test/regress/data/datetime_types.csv",
    "content": "2000-01-02 04:05:06,1999-01-08 14:05:06+02,2000-01-02,04:05:06,04:00:00\n1970-01-01 00:00:00,infinity,-infinity,00:00:00,00:00:00\n"
  },
  {
    "path": "src/test/regress/data/enum_and_composite_types.csv",
    "content": "a,\"(2,b)\"\nb,\"(3,c)\"\n"
  },
  {
    "path": "src/test/regress/data/events_table.data",
    "content": "2,2017-11-23 12:32:57.851419,3,3,4,\n2,2017-11-23 03:35:04.321504,1,0,2,\n5,2017-11-22 21:24:22.849224,5,4,1,\n6,2017-11-23 14:00:13.20013,3,3,3,\n4,2017-11-23 16:20:33.264457,0,0,3,\n4,2017-11-23 04:01:12.29256,4,4,3,\n2,2017-11-23 17:26:14.563216,1,5,3,\n2,2017-11-22 20:16:16.614779,0,1,1,\n3,2017-11-22 21:12:24.542921,1,2,5,\n3,2017-11-23 02:03:45.966467,4,1,0,\n2,2017-11-23 13:27:37.441959,5,2,4,\n4,2017-11-23 18:10:21.338399,1,2,4,\n5,2017-11-23 09:20:13.303927,3,0,3,\n3,2017-11-22 22:05:38.409323,3,2,2,\n5,2017-11-23 14:28:51.833214,3,0,1,\n3,2017-11-23 07:11:12.987954,3,1,3,\n4,2017-11-23 03:20:23.803407,2,3,3,\n3,2017-11-22 20:23:46.906523,3,3,2,\n3,2017-11-23 03:02:35.021717,1,0,2,\n4,2017-11-23 03:29:32.271967,3,2,4,\n6,2017-11-22 20:36:09.106561,3,2,1,\n5,2017-11-23 16:11:02.929469,4,2,0,\n3,2017-11-23 18:08:26.550729,2,4,3,\n2,2017-11-22 22:23:25.40611,3,4,4,\n1,2017-11-22 23:22:09.957743,1,1,1,\n4,2017-11-23 14:19:25.765876,3,1,1,\n3,2017-11-22 21:26:21.185134,1,5,3,\n3,2017-11-23 14:00:12.884702,3,1,1,\n4,2017-11-23 07:19:09.381749,1,0,2,\n5,2017-11-22 22:45:17.377228,3,2,0,\n3,2017-11-23 04:45:23.548548,1,2,3,\n4,2017-11-22 19:00:10.396739,2,1,1,\n4,2017-11-23 08:36:53.871919,2,4,3,\n6,2017-11-23 00:45:41.784391,2,3,2,\n4,2017-11-22 21:05:25.194441,5,4,1,\n3,2017-11-23 09:38:45.338008,2,5,0,\n3,2017-11-23 01:17:49.040685,2,3,4,\n1,2017-11-22 21:06:57.457147,4,3,2,\n6,2017-11-22 21:17:09.549341,5,2,5,\n4,2017-11-23 00:40:52.517603,1,5,4,\n4,2017-11-23 08:14:18.231273,4,3,2,\n1,2017-11-22 19:07:03.846437,1,2,5,\n2,2017-11-23 14:02:47.738901,1,3,2,\n1,2017-11-23 09:33:16.992454,3,4,1,\n5,2017-11-23 14:40:40.467511,1,4,1,\n2,2017-11-23 01:08:57.24208,2,3,1,\n2,2017-11-22 22:56:47.673504,4,1,4,\n2,2017-11-22 22:50:33.855696,4,2,3,\n5,2017-11-23 06:56:03.019565,2,1,1,\n6,2017-11-23 00:01:48.155345,4,2,1,\n6,2017-11-23 02:06:53.132461,5,1,1,\n2,2017-11-23 05:42:25.859537,2,3,5,\n4,2017-11-23 12:58:37.035024,1,2,4,\n1,2017-11-23 11:09:38.074595,0,5,1,\n5,2017-11-23 14:23:09.889786,3,1,0,\n5,2017-11-22 20:45:35.99031,1,2,3,\n3,2017-11-23 03:53:44.925122,4,2,3,\n1,2017-11-23 05:19:55.213875,0,2,0,\n1,2017-11-22 19:03:01.772353,4,1,2,\n5,2017-11-23 02:27:02.996588,1,4,1,\n2,2017-11-23 07:09:08.06934,1,5,4,\n2,2017-11-23 10:14:51.816339,4,2,1,\n3,2017-11-23 13:30:04.643655,3,2,1,\n2,2017-11-23 02:06:28.579787,3,1,4,\n4,2017-11-23 02:59:28.548578,4,2,3,\n2,2017-11-22 22:06:12.107108,0,2,5,\n2,2017-11-23 10:46:34.177173,3,2,3,\n2,2017-11-23 08:26:23.045769,1,1,0,\n3,2017-11-23 06:44:50.887182,4,0,4,\n6,2017-11-22 22:44:48.458334,1,3,2,\n6,2017-11-22 23:15:15.875499,4,3,3,\n3,2017-11-23 16:44:41.903713,4,2,2,\n5,2017-11-23 13:02:31.055159,0,2,1,\n4,2017-11-23 07:32:45.521278,4,5,3,\n5,2017-11-23 03:23:09.888244,1,3,2,\n1,2017-11-23 09:23:30.994345,3,1,1,\n1,2017-11-22 20:56:21.122638,2,4,4,\n1,2017-11-23 02:59:23.620864,4,5,4,\n2,2017-11-23 10:59:25.787633,3,1,0,\n5,2017-11-23 13:26:45.571108,3,3,4,\n2,2017-11-23 06:23:43.305403,1,2,1,\n4,2017-11-23 04:32:21.986825,4,1,3,\n1,2017-11-22 21:47:04.188168,4,2,0,\n4,2017-11-23 06:34:15.714083,2,2,2,\n2,2017-11-23 04:05:16.217731,1,4,3,\n2,2017-11-23 12:17:48.482544,2,3,2,\n2,2017-11-23 15:58:49.273421,5,1,2,\n2,2017-11-23 12:06:39.464165,4,5,4,\n3,2017-11-23 16:31:56.219594,5,1,2,\n1,2017-11-23 10:23:27.617726,4,2,5,\n3,2017-11-23 10:24:02.530471,2,2,3,\n6,2017-11-23 07:27:32.822068,2,2,1,\n5,2017-11-23 04:16:29.402814,1,3,3,\n3,2017-11-23 13:19:47.138294,2,1,4,\n2,2017-11-23 08:07:58.727599,3,1,3,\n1,2017-11-23 00:42:37.237615,2,4,3,\n3,2017-11-22 22:54:59.938341,2,4,3,\n6,2017-11-23 11:16:13.106691,1,1,0,\n3,2017-11-22 18:36:16.372893,2,3,4,\n1,2017-11-22 18:49:42.327403,3,2,1,\n1,2017-11-23 21:54:46.924477,6,4,5,\n"
  },
  {
    "path": "src/test/regress/data/impressions.csv",
    "content": "21048,8,474,2017-05-14 16:54:53,http://larkinjacobs.info/nathanael.steuber,,8.215.89.118,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n21049,8,474,2017-01-11 19:52:23,http://murraygulgowski.info/kay,,215.126.168.159,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n21050,8,474,2017-01-12 03:38:55,http://bernier.info/alene,,192.183.170.81,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n21051,8,474,2017-01-15 01:55:54,http://jast.net/clarabelle,,246.77.195.227,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n21052,8,474,2017-03-19 17:17:10,http://becker.com/carolanne_tillman,,241.245.251.129,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n21053,8,474,2017-03-11 21:38:30,http://mcdermott.name/monserrat,,58.95.22.196,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n21054,8,474,2017-01-05 00:21:05,http://feil.net/natasha,,243.212.193.204,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n21055,8,474,2017-03-24 18:36:28,http://jonesmann.io/jace_christiansen,,75.67.156.107,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n21056,8,474,2017-02-18 06:27:00,http://faheyhane.info/abbie_eichmann,,115.164.165.4,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n21057,8,474,2017-01-15 18:28:03,http://klockopollich.name/arlie.ritchie,,215.38.238.168,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n21058,8,474,2017-01-22 23:41:02,http://kautzercain.biz/brenden_donnelly,,201.221.188.203,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n21059,8,474,2017-01-06 22:33:16,http://halvorson.io/dasia,,202.156.170.209,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n21060,8,474,2017-02-22 16:34:21,http://mertz.org/buck_lueilwitz,,190.180.45.74,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n21061,8,474,2017-01-15 06:31:09,http://ohara.net/kendall,,167.75.123.42,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n21062,8,474,2017-05-08 08:07:21,http://shanahan.com/kendra,,66.136.16.94,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n21063,8,474,2017-02-01 15:17:13,http://mueller.io/vincenza,,20.151.168.170,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n21064,8,474,2017-02-27 17:05:13,http://purdy.biz/tanner_glover,,228.191.66.240,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n21065,8,474,2017-04-14 16:03:10,http://collins.co/irma,,26.60.167.220,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n21066,8,474,2017-03-15 18:31:01,http://willmsboyle.biz/karl.stamm,,58.61.67.24,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n21067,8,474,2017-05-21 16:59:52,http://jacobson.org/maritza.kuhn,,138.84.165.89,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n21068,8,474,2017-06-06 00:36:36,http://kihn.net/joaquin.dickens,,89.148.71.17,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n21069,8,474,2017-02-11 15:58:53,http://crist.net/dean_cronin,,7.104.218.146,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n21070,8,474,2017-03-27 10:04:38,http://wehner.com/grayce,,183.3.86.138,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n21071,8,474,2017-05-04 06:08:52,http://boyerhilpert.biz/sid,,175.16.225.99,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n21072,8,474,2017-03-18 04:04:36,http://stark.com/hazel.nikolaus,,254.85.152.70,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n21073,8,474,2017-04-29 16:20:24,http://cronin.biz/earl_reichert,,224.161.170.198,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n21074,8,474,2017-04-01 09:38:53,http://altenwerthweimann.biz/ruben,,22.178.2.131,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n21075,8,474,2017-05-22 13:31:57,http://gaylord.info/vicky,,205.16.171.137,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n21076,8,474,2017-04-04 15:44:32,http://pacochajacobi.com/alvina_hettinger,,241.111.144.230,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n21077,8,474,2016-12-31 21:35:28,http://runolfsdottir.com/queen.king,,73.125.121.41,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n21078,8,474,2017-04-29 01:44:05,http://carroll.biz/daniella,,115.63.11.199,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n21079,8,474,2016-12-19 02:18:42,http://schmitt.org/otto_waelchi,,196.70.30.165,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n21080,8,474,2017-01-22 02:09:47,http://walter.co/rey.jacobs,,209.52.141.190,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n21081,8,474,2017-04-21 13:34:56,http://huelsfritsch.net/braeden.bogan,,253.216.207.242,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n21082,8,474,2017-05-15 21:28:33,http://bosconader.info/junius,,159.116.91.225,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n21083,8,474,2017-02-06 08:32:36,http://stoltenbergweber.org/kristina_gerlach,,34.85.47.54,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n21084,8,474,2017-06-02 00:34:18,http://leschgrady.com/stephania.howe,,80.216.156.239,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n21085,8,474,2016-12-17 17:22:30,http://gaylordstanton.name/theresia.borer,,47.11.177.24,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n21086,8,474,2017-03-18 03:24:02,http://stracke.info/jamar_kulas,,165.248.36.140,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n21087,8,474,2017-04-11 18:45:25,http://ferry.co/moie.herman,,62.66.215.251,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n21088,8,474,2017-03-12 15:35:39,http://johnson.com/krista,,211.17.120.135,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n21089,8,474,2017-03-07 08:29:48,http://grady.info/tyler,,60.43.237.139,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n21090,8,474,2017-01-06 02:07:20,http://koeppspencer.com/elizabeth_pacocha,,185.157.242.205,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n21091,8,474,2017-02-08 03:40:45,http://sanfordwyman.io/golden,,120.19.63.84,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n21092,8,474,2017-02-05 12:28:32,http://bergemccullough.co/deven_schuppe,,103.6.226.229,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n21093,8,474,2017-01-20 18:09:10,http://blanda.org/jettie_frami,,47.156.44.66,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n21094,8,474,2017-05-21 08:21:28,http://hills.net/leland,,93.163.167.143,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n21095,8,474,2016-12-29 16:33:39,http://macejkovic.biz/mallory.denesik,,25.103.198.65,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n21096,8,474,2017-02-25 02:36:31,http://johnstonosinski.name/josiane.schamberger,,90.235.249.185,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n21097,8,474,2017-02-01 23:15:11,http://ruel.org/lelah_mccullough,,146.91.118.53,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n21098,8,474,2017-06-12 00:01:53,http://zulaufbalistreri.org/dimitri_windler,,30.30.52.108,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n21099,8,474,2017-05-05 16:40:11,http://kelerbahringer.biz/rory,,120.214.169.120,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n21100,8,475,2017-02-14 18:06:39,http://trantow.io/frances_schmidt,,206.187.13.76,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n1,1,1,2016-12-28 15:41:12,http://wisozk.org/sonya.herzog,0.3889986856,26.188.235.197,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n2,1,1,2017-05-10 07:13:45,http://batzwest.org/reed,0.0638372400,32.158.161.111,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n3,1,1,2017-06-01 22:41:32,http://howe.biz/priscilla,0.6467528056,17.104.94.47,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n4,1,1,2017-01-10 16:39:34,http://auer.name/dave.johnston,0.7427506486,243.186.9.121,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n5,1,1,2017-03-06 22:29:08,http://halvorsonrutherford.name/imani_fahey,0.4283587081,161.226.163.83,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n6,1,1,2017-03-16 10:51:52,http://quigley.biz/howard,0.1766760636,102.154.56.47,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n7,1,1,2017-02-05 07:34:31,http://jacobsonklocko.net/willow,0.1532288024,170.152.94.48,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n8,1,1,2017-06-04 23:04:25,http://ruel.name/marquise.mertz,0.0606385498,34.205.150.206,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n9,1,1,2017-02-04 07:46:27,http://kaulke.net/jeremy,0.4834670365,109.61.30.120,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n10,1,1,2017-03-10 11:48:58,http://larkin.biz/sean.hamill,0.7836783542,55.189.154.209,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n11,1,1,2017-02-16 01:12:55,http://stokes.biz/deja,0.0527221629,94.109.124.93,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n12,1,1,2017-02-18 16:26:17,http://kulas.net/ervin,0.4500981266,75.214.183.240,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n13,1,1,2017-02-20 01:28:24,http://schuster.io/christophe.adams,0.8720638012,85.182.62.188,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n14,1,1,2017-01-09 23:41:28,http://bruen.info/fausto.hansen,0.3775360146,162.214.214.66,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n15,1,1,2017-01-11 19:27:10,http://zemlak.info/ocie,0.7637572025,229.102.154.44,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n16,1,1,2017-06-08 12:14:07,http://runolfon.com/zechariah,0.0295705185,177.233.153.80,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n17,1,1,2017-01-31 02:26:17,http://hoeger.co/marcellus,0.5192454333,136.239.43.229,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n18,1,1,2017-04-17 06:57:41,http://hellerwiegand.biz/darlene.konopelski,0.6917053221,130.70.206.43,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n19,1,1,2017-01-17 17:37:17,http://sporer.org/joey,0.4197002462,50.142.252.130,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n20,1,1,2017-05-17 17:31:48,http://rau.name/vincenzo,0.8694303306,38.254.5.203,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n21,1,1,2017-03-09 19:22:06,http://runolfontromp.co/elisabeth,0.6498390032,43.229.91.171,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n22,1,1,2017-02-08 12:13:05,http://erdmanlubowitz.co/german_wolf,0.4904417670,176.239.100.21,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n23,1,1,2017-03-23 08:54:50,http://daniel.name/tony,0.8588046862,74.215.183.204,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n24,1,1,2017-01-21 12:15:50,http://marquardt.net/ignatius,0.3364057220,24.248.181.46,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n25,1,1,2016-12-15 13:53:30,http://hammes.io/rudy.keeling,0.7934070793,179.145.245.93,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n26,1,1,2017-04-23 19:43:58,http://walker.co/martine_treutel,0.1573282271,132.27.160.44,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n27,1,2,2017-01-17 06:01:02,http://krajcik.name/tia_steuber,0.0079417672,140.121.198.44,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n28,1,2,2017-04-20 08:22:00,http://hayes.co/hester,0.5333644539,22.162.27.226,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n29,1,2,2017-05-06 23:21:33,http://yost.info/kenneth,0.3397297211,64.59.55.97,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n30,1,2,2017-02-03 06:17:48,http://thiel.name/sheila,0.3091657443,79.249.221.62,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n31,1,2,2017-04-30 21:18:24,http://sipes.name/golden_harvey,0.2503317316,94.67.39.29,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n32,1,2,2017-06-07 06:02:51,http://kilbackbrekke.org/adele.runolfon,0.4153793646,143.109.158.192,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n33,1,2,2017-04-11 07:52:48,http://rowe.com/chase,0.6382217585,128.195.119.48,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n34,1,2,2017-03-19 03:32:31,http://howeledner.org/abelardo.kertzmann,0.5798386320,111.125.240.232,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n35,1,2,2017-03-01 14:28:50,http://considine.org/eloisa,0.5738457552,84.254.178.198,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n36,1,2,2017-05-24 05:51:13,http://gutkowski.net/delphia_grant,0.9937431682,85.89.85.236,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n37,1,2,2017-05-10 22:40:20,http://hermanntoy.io/reyes_feil,0.9662424061,171.155.55.77,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n38,1,2,2017-01-17 14:58:50,http://hartmannbechtelar.net/marcelle,0.0501072762,142.249.144.160,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n39,1,2,2017-02-24 16:00:05,http://torp.biz/adaline.feest,0.7056395420,68.150.202.54,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n40,1,2,2017-04-25 15:44:05,http://sanford.io/brent,0.7666997789,214.139.63.172,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n41,1,2,2017-05-13 16:32:13,http://mcclure.io/anika_ferry,0.7244277388,52.43.18.91,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n42,1,2,2017-04-05 02:09:36,http://fayweimann.io/ernie.bergnaum,0.4478818614,136.239.79.72,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n43,1,2,2017-05-10 16:34:47,http://bailey.com/santino.swift,0.1799102658,171.55.242.248,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n44,1,2,2017-01-30 08:22:08,http://casperhills.info/reynold.moriette,0.7838694881,222.74.140.227,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n45,1,2,2017-01-24 01:16:16,http://rutherford.co/alaina,0.1350277039,40.110.63.158,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n46,1,2,2017-02-11 16:56:10,http://connelly.org/clementine.herzog,0.7663320163,64.40.133.96,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n47,1,2,2017-03-08 20:26:32,http://mannmckenzie.com/chanelle,0.1063036380,166.148.96.180,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n48,1,2,2017-04-21 05:18:56,http://dubuque.io/lelia.steuber,0.9020465502,133.18.180.254,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n49,1,2,2017-06-12 04:39:41,http://rippineichmann.co/colleen_howe,0.9121157540,236.83.222.116,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n50,1,2,2017-01-03 15:57:54,http://lehner.biz/crystal_bergstrom,0.0969568597,135.90.22.83,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n51,1,2,2017-05-26 03:48:58,http://schinner.co/loma,0.7837701570,236.21.11.70,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n21101,8,475,2017-05-18 15:20:44,http://marksrempel.net/breanna,,174.182.197.192,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n21102,8,475,2017-04-10 04:19:03,http://will.name/rosemarie_armstrong,,179.154.137.83,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n21103,8,475,2017-05-13 23:28:52,http://smitham.info/evelyn,,53.56.169.5,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n21104,8,475,2017-06-05 08:17:06,http://weinat.io/branson,,88.227.32.76,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n21105,8,475,2017-05-13 01:11:07,http://smith.co/verna,,58.82.148.146,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n21106,8,475,2016-12-22 02:35:50,http://gutmannbins.com/daphney,,253.38.108.124,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n21107,8,475,2017-03-01 15:43:39,http://powlowskiharris.co/alexander,,39.127.45.87,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n21108,8,475,2017-03-28 12:09:01,http://hackett.name/briana,,22.184.41.162,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n21109,8,475,2017-04-05 10:46:40,http://aufderharmclaughlin.com/precious,,166.199.146.250,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n21110,8,475,2017-05-31 16:29:56,http://hilpertruel.org/karianne.will,,148.105.96.126,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n21111,8,475,2017-03-19 18:00:15,http://metz.org/karen_lueilwitz,,102.175.201.88,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n21112,8,475,2017-05-04 16:31:55,http://donnelly.com/della.hackett,,114.133.75.130,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n21113,8,475,2017-03-30 11:29:04,http://wolf.io/deanna,,113.249.156.42,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n21114,8,475,2017-06-11 14:19:29,http://brakus.name/quentin_okuneva,,53.170.230.9,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n21115,8,475,2017-05-23 05:39:58,http://conn.info/karianne,,162.150.191.222,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n21116,8,475,2017-02-19 18:31:16,http://greenholt.com/chasity.marks,,143.52.163.5,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n21117,8,475,2017-06-06 17:47:52,http://witting.info/tavares.lebsack,,133.212.71.139,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n21118,8,475,2017-04-20 15:38:46,http://weber.co/mitchel.powlowski,,18.66.152.170,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n21119,8,475,2017-05-12 20:35:09,http://turnerwalker.io/soledad_kunze,,138.15.129.103,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n21120,8,475,2017-04-06 07:03:45,http://wintheiser.biz/gunner.witting,,70.209.101.240,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n21121,8,475,2017-06-03 04:26:41,http://hirthe.biz/allan.legros,,168.205.223.223,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n21122,8,475,2017-06-07 06:25:06,http://prosacco.net/helen,,159.227.94.130,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n21123,8,475,2017-03-09 17:42:22,http://klein.co/callie,,118.119.218.176,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n21124,8,475,2017-03-15 07:33:28,http://mohrdubuque.biz/adrianna,,140.171.144.91,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n21125,8,475,2017-03-18 12:40:02,http://kozey.io/prudence.murray,,200.53.155.78,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n21126,8,475,2017-01-17 14:19:52,http://doyle.net/lexus.rau,,115.42.5.101,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n21127,8,475,2017-04-24 16:37:05,http://lemke.name/keyon,,232.67.218.242,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n21128,8,475,2017-02-26 05:50:19,http://balistrerimorar.org/alysa,,21.141.212.222,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n21129,8,475,2017-02-10 00:51:47,http://wiegand.name/tina,,5.215.6.112,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n21130,8,475,2017-03-11 18:08:56,http://windlerkub.biz/nathanael,,122.194.144.161,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n21131,8,475,2017-04-21 18:21:40,http://wolffnienow.info/madalyn,,28.132.189.18,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n21132,8,475,2017-03-24 23:59:39,http://hoegercrist.org/alvis.mills,,36.53.15.155,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n21133,8,475,2017-04-02 21:28:15,http://danielthompson.org/easter.corwin,,124.90.69.200,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n21134,8,475,2017-01-09 02:52:36,http://hilpert.io/ralph.gutkowski,,53.64.102.201,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n21135,8,475,2016-12-21 00:14:03,http://farrellstoltenberg.net/tony_volkman,,86.164.27.64,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n21136,8,475,2017-02-21 14:45:30,http://hahn.org/kyla_reynolds,,189.111.122.90,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n21137,8,475,2016-12-25 17:40:37,http://gaylord.io/shyanne.zboncak,,17.216.118.190,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n21138,8,475,2017-01-13 04:36:31,http://davis.biz/ulises_wiza,,99.112.243.102,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n21139,8,475,2017-03-22 09:43:18,http://hegmann.org/hilario,,215.84.141.149,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n21140,8,475,2017-01-22 17:20:08,http://eichmann.biz/esperanza.carter,,90.30.26.158,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n21141,8,475,2017-02-16 12:54:48,http://westparker.biz/monte,,82.53.210.17,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n21142,8,475,2017-04-12 04:19:41,http://thompson.biz/lonzo.kreiger,,50.68.216.75,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n21143,8,475,2017-05-14 21:39:08,http://smitham.io/rex.ziemann,,161.145.20.150,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n21144,8,475,2017-02-25 00:12:08,http://conroy.net/xavier,,140.110.118.231,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n21145,8,475,2017-04-15 04:55:09,http://macgyver.org/jovani,,229.50.233.233,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n21146,8,475,2017-02-16 13:10:28,http://wolffmcclure.co/autumn,,187.54.90.82,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n21147,8,475,2017-02-03 11:03:47,http://ebert.biz/oral_cronin,,152.190.72.19,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n21148,8,475,2016-12-20 12:12:45,http://bradtke.biz/ana.sipes,,85.173.7.67,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n21149,8,475,2017-06-04 12:13:31,http://hoeger.com/aliyah.kunze,,131.177.117.207,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n21150,8,475,2016-12-15 12:48:16,http://nienow.co/kattie.zulauf,,123.174.180.110,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n21151,8,475,2016-12-29 09:43:51,http://mclaughlin.net/peggie,,238.107.185.241,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n21152,8,475,2017-05-02 22:10:46,http://macejkovichoppe.io/kavon,,16.9.86.95,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n21153,8,475,2017-04-25 09:07:41,http://donnelly.io/shaniya,,17.118.154.52,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n21154,8,475,2017-01-18 14:04:08,http://rohan.net/lorenza,,176.77.82.33,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n21155,8,475,2017-05-20 00:35:21,http://jast.org/elva,,142.208.216.94,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n52,1,2,2017-06-13 04:55:12,http://tillman.biz/hortense,0.2756487119,170.33.171.155,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n53,1,2,2017-02-21 13:00:33,http://marquardt.org/amber,0.9261337557,13.143.25.68,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n54,1,2,2017-03-21 04:45:57,http://denesikondricka.name/jailyn_mraz,0.6055894759,148.127.195.70,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n55,1,2,2016-12-25 11:50:14,http://carter.com/janice.farrell,0.6343735216,154.91.126.136,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n56,1,2,2017-05-06 11:34:17,http://rath.info/earlene,0.0701133420,139.246.186.154,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n57,1,2,2017-03-31 20:02:35,http://hammes.biz/dane.macejkovic,0.5892573428,21.159.213.109,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n58,1,2,2017-01-22 10:21:05,http://heel.name/shawna_sauer,0.4535145446,182.70.56.55,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n59,1,2,2017-04-10 16:34:32,http://reilly.com/pat,0.3653598724,110.200.199.101,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n60,1,2,2017-05-27 08:32:17,http://gloverprice.info/jedidiah_simonis,0.3568039311,141.144.138.131,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n61,1,2,2017-03-26 03:36:26,http://ernser.info/robbie,0.5785665848,143.32.230.134,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n62,1,2,2017-03-04 07:45:20,http://ullrich.biz/travon.fadel,0.5472223873,36.157.40.86,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n63,1,2,2017-03-29 16:50:44,http://hamill.co/mackenzie,0.6083412824,184.244.192.150,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n64,1,2,2017-04-02 21:06:39,http://kunzekohler.biz/myrtle,0.8276967649,161.192.194.241,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n65,1,2,2017-06-09 22:13:22,http://paucek.com/randy,0.4086336508,137.150.186.54,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n66,1,3,2017-02-12 07:57:24,http://hahn.biz/fidel_waters,0.3889022593,4.230.51.14,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n67,1,3,2017-01-12 11:45:12,http://lowegrimes.com/jana,0.0761463508,218.4.220.11,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n68,1,3,2017-03-15 04:36:24,http://mcdermottbuckridge.info/gretchen_kerluke,0.9956589291,3.232.171.243,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n69,1,3,2017-02-02 20:42:19,http://gleason.co/rigoberto_schneider,0.1498752570,197.29.133.181,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n70,1,3,2017-02-19 03:53:09,http://connellyparker.org/samson_aufderhar,0.1322172271,2.153.179.122,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n71,1,3,2017-05-16 13:51:11,http://block.co/hank.marks,0.0656715468,219.11.70.126,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n72,1,3,2017-03-17 17:17:41,http://towne.co/gunnar.larkin,0.0259777458,182.57.71.22,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n73,1,3,2016-12-14 09:31:57,http://lefflerkiehn.co/kacey,0.7224987538,72.137.101.165,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n74,1,3,2017-04-03 20:11:07,http://reichel.org/donny.donnelly,0.3498653809,146.132.39.211,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n75,1,3,2016-12-21 03:40:01,http://lang.name/neoma,0.4265966274,67.161.17.228,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n76,1,3,2017-03-28 04:43:32,http://greenholt.net/laney.tremblay,0.1198275621,224.86.119.31,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n77,1,3,2017-03-18 07:51:48,http://metz.name/kenyon.zemlak,0.7292641463,215.238.163.22,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n78,1,3,2017-03-22 08:50:23,http://reynoldsferry.com/april_sipes,0.8745492405,184.181.183.61,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n79,1,3,2016-12-23 11:55:26,http://schmitt.co/mckenna,0.8740530481,57.211.221.37,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n80,1,3,2017-05-21 14:19:51,http://miller.info/thaddeus_crist,0.1973919184,47.122.82.55,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n81,1,3,2017-03-07 00:23:47,http://franecki.net/enoch.parker,0.1691501644,188.50.189.129,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n82,1,3,2017-02-12 10:17:00,http://muller.io/genoveva.donnelly,0.8380742106,81.174.84.52,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n83,1,3,2017-03-13 20:58:31,http://halvorson.biz/arvel_pfeffer,0.4324168422,73.112.169.169,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n84,1,3,2017-05-17 03:32:16,http://gradyreichel.io/oceane.herman,0.7048880959,149.247.135.184,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n85,1,3,2017-04-05 15:43:40,http://huelsenger.biz/timothy.oconner,0.5912624312,50.145.163.237,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n86,1,3,2017-04-17 06:39:24,http://hodkiewiczthompson.info/audra.paucek,0.8031535367,111.93.176.232,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n87,1,3,2017-05-31 06:29:28,http://fay.biz/henderson_ortiz,0.9611721198,17.135.51.21,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n88,1,3,2017-01-31 09:53:45,http://feestmorar.name/oceane,0.5488106716,185.64.195.249,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n89,1,3,2016-12-20 12:21:28,http://macgyverboyer.net/dorian,0.3399680484,138.123.60.174,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n90,1,3,2017-05-17 01:10:02,http://gislason.com/ron,0.8305261708,50.101.163.25,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n91,1,3,2017-05-08 20:49:13,http://feeney.org/claudine.gorczany,0.3117818379,132.202.110.37,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n92,1,3,2017-01-18 17:27:48,http://king.info/daisha,0.9503684113,21.53.145.74,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n93,1,3,2017-04-24 18:21:58,http://stehr.net/ruthe,0.6267859364,139.155.214.115,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n94,1,3,2017-01-14 21:41:43,http://pagac.name/alaina,0.4251381545,113.104.8.31,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n95,1,3,2017-03-31 04:15:12,http://manntillman.name/linda,0.1438333256,42.45.97.97,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n96,1,3,2017-05-26 18:23:56,http://toylittle.name/sigrid.rippin,0.2939488363,46.105.84.208,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n97,1,3,2017-01-05 11:10:56,http://mitchellmarks.co/ardith_schoen,0.3229934275,86.203.137.112,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n98,1,3,2017-03-15 05:55:12,http://kertzmann.info/carol.kuphal,0.1190198799,137.164.170.240,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n99,1,3,2016-12-19 22:13:39,http://ward.co/nelson.quitzon,0.7167377663,212.21.132.231,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n100,1,3,2016-12-18 22:36:28,http://veum.io/nestor.lakin,0.1516013059,181.197.250.177,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n101,1,3,2017-04-15 01:10:44,http://raumills.co/lukas_friesen,0.8996811047,86.138.241.252,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n102,1,3,2017-05-05 22:47:16,http://dubuque.info/chandler_pacocha,0.3647552999,241.17.11.202,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n103,1,3,2017-05-24 19:41:25,http://wiegandrice.io/kellen.hackett,0.3783671295,79.251.206.155,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n21156,8,475,2017-04-23 05:12:56,http://sanfordrunte.name/korey,,177.64.44.228,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n21157,8,475,2017-03-03 13:12:41,http://jenkins.com/johathan.hauck,,241.249.250.141,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n21158,8,475,2017-02-25 00:17:19,http://torp.name/bernadette_bauch,,173.158.19.64,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n21159,8,475,2017-01-02 17:38:00,http://bergnaum.net/tania_auer,,106.188.169.172,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n21160,8,475,2017-03-15 23:47:39,http://hahn.co/jefferey_hagenes,,222.160.17.44,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n21161,8,475,2017-01-13 09:05:19,http://schultzfahey.biz/euna,,82.137.159.204,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n21162,8,475,2016-12-25 04:07:11,http://bins.info/seamus.beer,,220.182.249.42,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n21163,8,476,2017-04-16 04:13:45,http://mcclure.info/dimitri,,202.13.77.86,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n21164,8,476,2017-01-02 13:17:51,http://brown.io/walter,,69.156.6.164,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n21165,8,476,2017-03-10 17:38:47,http://shieldsabbott.net/tillman,,23.154.52.135,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n21166,8,476,2017-03-14 17:07:37,http://gerlach.info/gunnar,,45.204.126.119,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n21167,8,476,2017-01-10 12:57:13,http://rogahn.io/alta,,98.213.150.74,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n21168,8,476,2017-04-07 22:44:00,http://kuvalis.org/julia.strosin,,79.48.56.120,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n21169,8,476,2017-05-14 18:22:32,http://miller.org/te.rutherford,,176.49.189.200,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n21170,8,476,2017-05-29 18:55:02,http://turcotte.biz/janae,,146.160.2.66,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n21171,8,476,2017-05-24 06:42:55,http://dickinson.org/malachi_wolff,,201.129.45.41,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n21172,8,476,2017-03-05 16:17:39,http://kaulke.net/jaunita,,3.137.239.80,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n21173,8,476,2017-05-27 11:16:01,http://davis.io/gerald,,204.246.119.117,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n21174,8,476,2017-05-27 13:08:30,http://walshwilderman.name/sierra.reichel,,197.239.245.27,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n21175,8,476,2017-04-23 11:49:01,http://grady.net/albin,,27.174.162.11,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n21176,8,476,2017-04-01 01:41:55,http://bernier.com/kelsi.braun,,223.49.106.205,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n21177,8,476,2017-05-21 10:46:50,http://weinat.com/garland,,128.68.45.113,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n21178,8,476,2017-01-24 20:40:53,http://gutkowskiernser.info/jeremy_kozey,,210.14.30.161,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n21179,8,476,2017-01-03 13:12:19,http://baumbach.org/mike.hartmann,,177.81.185.83,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n21180,8,476,2017-04-13 13:09:09,http://schaefer.io/yesenia.collier,,202.95.16.64,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n21181,8,476,2017-05-22 22:19:05,http://buckridge.com/daisy,,34.138.253.129,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n21182,8,476,2017-04-28 15:26:40,http://considine.co/yolanda,,224.91.205.183,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n21183,8,476,2017-03-06 17:58:42,http://rogahn.net/patrick.rempel,,21.173.187.17,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n21184,8,476,2017-05-08 05:01:37,http://jakubowski.io/myrtie_pacocha,,140.163.183.243,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n21185,8,476,2017-05-20 16:02:55,http://dooleyoreilly.biz/elya,,110.194.68.89,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n21186,8,476,2017-02-11 20:52:58,http://johnson.info/dave,,226.204.234.155,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n21187,8,476,2017-01-06 00:46:55,http://okunevawaters.io/albertha,,169.49.49.126,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n21188,8,476,2017-05-14 06:49:21,http://raynor.info/pietro,,180.193.87.240,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n21189,8,476,2017-06-14 03:29:49,http://ebert.info/vern,,64.182.151.45,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n21190,8,476,2017-01-30 16:54:44,http://lakin.com/tevin,,231.163.165.99,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n21191,8,476,2017-05-29 06:33:41,http://hammesharvey.org/ignatius,,214.11.74.162,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n21192,8,477,2017-06-03 21:49:12,http://breitenberg.info/alfredo,,94.173.155.90,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n21193,8,477,2017-02-16 07:26:35,http://funk.name/delfina,,12.180.196.78,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n21194,8,477,2017-04-02 15:09:45,http://hoegerconsidine.name/aurelio_howell,,172.40.227.12,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n21195,8,477,2017-01-06 20:30:20,http://gusikowskilittel.com/rupert_feest,,103.125.27.164,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n21196,8,477,2017-02-07 15:39:00,http://kaulke.io/ubaldo,,144.61.249.95,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n21197,8,477,2017-04-23 23:57:13,http://jast.info/keaton_rutherford,,179.12.253.110,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n21198,8,477,2017-01-12 20:49:43,http://ankundingarmstrong.io/willow,,42.216.200.136,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n21199,8,477,2017-05-21 13:23:12,http://terry.co/ben,,238.97.190.84,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n21200,8,477,2017-01-07 16:36:23,http://wymanmoore.info/roscoe_bechtelar,,155.207.200.227,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n21201,8,477,2017-03-15 03:13:19,http://morarbraun.co/hayley,,79.82.181.211,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n21202,8,477,2017-03-18 08:14:37,http://brekkeblock.name/tad,,98.233.189.77,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n21203,8,477,2017-04-02 06:48:43,http://eichmann.org/elliott.gutkowski,,57.158.23.106,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n21204,8,477,2016-12-19 00:54:26,http://okunevablock.org/aurore_lind,,49.111.34.241,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n21205,8,477,2017-03-31 07:21:05,http://trompreichel.biz/carmela_berge,,121.63.170.93,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n21206,8,477,2017-02-24 01:55:15,http://krajcik.co/jany,,73.7.157.220,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n21207,8,477,2017-01-27 07:46:47,http://ratkehintz.name/dasia,,93.234.91.159,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n21208,8,477,2017-01-01 12:21:34,http://cole.io/brandy.steuber,,18.18.238.105,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n21209,8,477,2017-04-28 18:08:51,http://christiansen.com/chanelle.boyle,,227.63.248.253,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n21210,8,477,2017-01-27 07:26:06,http://auer.name/angus_schulist,,147.57.149.240,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n21211,8,477,2017-01-13 14:58:29,http://vandervortweber.biz/christopher.kreiger,,222.189.78.121,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n104,1,3,2017-02-14 19:36:52,http://thompsonwitting.info/penelope,0.7603798157,42.193.156.225,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n105,1,3,2017-06-11 07:27:13,http://pollichconroy.name/lennie,0.3796789796,51.144.40.180,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n106,1,3,2017-05-16 09:32:44,http://wintheiser.org/billie,0.5223689934,241.18.20.26,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n107,1,3,2017-03-17 13:52:55,http://schumm.name/jayce,0.2390509085,223.37.51.205,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n439,1,11,2017-05-15 17:51:19,http://ullrich.io/haven,,115.119.20.15,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n108,1,3,2017-03-28 09:51:50,http://dooley.biz/albin_adams,0.9916325338,60.147.21.252,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n109,1,3,2017-04-13 23:34:18,http://daughertykemmer.name/jillian,0.7219787891,31.64.221.240,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n110,1,3,2017-03-10 02:04:42,http://sanford.info/wellington_hyatt,0.5442953252,83.48.10.205,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n111,1,3,2017-05-01 05:29:23,http://heller.info/layla,0.1994001399,63.29.178.139,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n112,1,3,2016-12-31 21:51:34,http://hills.info/bailey_smith,0.8265723189,51.5.183.215,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n113,1,3,2017-02-16 22:22:21,http://jaskolski.org/kathryn_osinski,0.5635848485,36.234.7.112,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n114,1,3,2017-05-25 23:27:43,http://haagmitchell.co/kailee_heller,0.0746966372,139.184.234.253,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n115,1,3,2017-01-09 02:20:39,http://effertz.net/melody,0.0752629214,223.175.26.20,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n116,1,3,2017-01-10 23:01:37,http://sporer.org/carlie_heidenreich,0.1780834954,95.129.13.105,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n117,1,3,2017-04-16 10:09:48,http://schimmelfeeney.net/mariela_lakin,0.3433570122,203.68.235.10,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n118,1,3,2017-04-28 23:43:24,http://stehr.biz/lorine,0.5872423368,84.242.65.72,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n119,1,3,2017-02-26 09:36:28,http://beahan.com/marlen,0.0646358215,82.22.103.213,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n120,1,4,2017-01-09 12:47:51,http://schambergertreutel.co/jason_stracke,0.4565702586,133.157.245.228,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n121,1,4,2017-04-13 23:10:04,http://kautzerdietrich.io/boris,0.6660390786,90.200.195.135,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n122,1,4,2017-03-06 21:28:21,http://fay.name/dante_smitham,0.3988895140,86.251.33.174,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n123,1,4,2017-01-16 07:08:05,http://moen.io/katheryn.treutel,0.8964232561,120.173.32.33,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n124,1,4,2017-03-13 05:44:36,http://nolan.name/mohammad,0.2224835571,190.63.7.97,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n125,1,4,2017-02-09 12:59:01,http://feest.com/kenna,0.8122121901,126.97.42.86,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n126,1,4,2017-02-24 22:24:23,http://hanewisoky.net/cale,0.3658620474,129.248.247.117,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n127,1,4,2017-05-22 22:55:07,http://trantow.name/kip_stanton,0.1241019774,4.45.90.244,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n128,1,4,2017-05-10 19:22:46,http://schoen.co/scottie,0.0049093082,171.63.44.91,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n129,1,4,2017-02-13 00:05:35,http://townenienow.biz/laila_bailey,0.5457511619,200.143.119.84,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n130,1,4,2017-01-24 09:40:48,http://trantowkiehn.net/nya,0.5969854166,144.4.96.9,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n131,1,4,2017-01-08 08:56:08,http://mantepredovic.io/mabel_baumbach,0.0456555790,114.93.106.199,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n132,1,4,2017-03-04 08:31:16,http://hintz.info/emilia.walter,0.8934218479,224.186.192.228,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n133,1,4,2017-04-09 20:00:27,http://marvinjakubowski.co/laurine.aufderhar,0.0084132576,123.108.32.214,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n134,1,4,2017-05-09 04:46:30,http://lockman.co/ezekiel,0.0443884515,56.166.226.70,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n135,1,4,2017-01-14 01:40:52,http://wunsch.com/xzavier,0.8979338484,19.37.189.101,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n136,1,4,2017-01-30 04:58:59,http://runolfsdottir.co/lynn,0.2143528786,84.123.247.153,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n137,1,4,2017-06-11 22:00:03,http://goldner.net/john,0.7062060899,243.201.19.68,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n138,1,4,2017-04-02 05:56:05,http://mitchell.biz/jazmyn,0.7162014740,249.86.34.119,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n139,1,4,2017-02-10 00:39:02,http://jacobistrosin.info/elise,0.4246218770,33.159.103.29,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n140,1,4,2017-01-12 03:53:57,http://west.co/roslyn,0.3011598837,124.161.145.47,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n141,1,5,2017-01-28 13:35:52,http://barton.biz/kenyon.upton,0.1468870249,87.92.164.74,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n142,1,5,2017-06-04 15:01:03,http://rippin.com/dino,0.9960105385,183.195.153.237,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n143,1,5,2017-05-05 13:50:11,http://treutel.info/eryn,0.7802999330,227.169.64.170,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n144,1,5,2017-04-05 17:21:12,http://reillybailey.co/hazel_veum,0.0237870641,67.158.116.28,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n145,1,5,2016-12-19 19:43:52,http://carter.info/blaise_casper,0.5853350326,84.94.163.46,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n146,1,5,2017-01-10 17:26:43,http://faheycrooks.com/janis.grady,0.2354234610,9.35.183.96,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n147,1,5,2017-05-11 07:44:31,http://farrell.co/frieda,0.7590308840,120.32.60.102,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n148,1,5,2016-12-28 12:27:23,http://ebert.io/jolie_carroll,0.3956142731,223.28.209.136,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n149,1,5,2017-04-24 19:15:35,http://braun.org/cheyanne,0.1069149872,198.127.91.180,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n150,1,5,2017-04-17 00:27:22,http://murray.info/tanner_kertzmann,0.2232039493,126.83.36.114,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n151,1,5,2017-06-13 22:51:31,http://weimanndamore.name/aiyana,0.1105747224,248.160.88.37,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n152,1,5,2017-03-30 16:29:53,http://lynchcrooks.io/tyrese,0.4941153356,82.103.8.234,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n153,1,5,2017-01-03 09:46:42,http://windler.info/king_schuppe,0.3765764061,124.177.220.104,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n154,1,5,2017-05-16 16:04:30,http://monahan.org/emma,0.0055531358,133.77.132.127,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n155,1,5,2017-02-05 18:07:14,http://thompson.biz/oswaldo,0.5665000194,218.247.157.189,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n21212,8,477,2017-01-12 23:47:27,http://murphykirlin.com/jade_brown,,149.212.194.251,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n21213,8,477,2017-03-15 11:47:40,http://howell.com/jace.moriette,,62.92.91.94,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n21214,8,477,2017-05-05 05:27:48,http://hettinger.info/dayton_rosenbaum,,43.165.227.233,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n21215,8,477,2017-05-11 13:20:08,http://cronin.org/jeffrey_renner,,218.93.65.10,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n21216,8,477,2017-04-16 14:06:42,http://heidenreichhauck.name/billy_homenick,,180.177.216.102,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n21217,8,477,2017-04-23 14:01:37,http://barrows.info/jan,,217.184.81.237,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n21218,8,477,2017-03-09 14:21:44,http://zboncaktoy.io/norval_wyman,,196.30.65.147,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n21219,8,477,2017-04-15 11:35:24,http://mcdermott.name/conor_gaylord,,235.57.94.220,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n21220,8,477,2017-05-27 00:12:12,http://macgyverpredovic.com/yadira,,2.179.79.111,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n21221,8,477,2017-04-14 12:15:05,http://howe.name/martina,,210.127.78.110,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n21222,8,477,2017-04-04 08:42:26,http://mcdermott.co/carli_homenick,,154.141.98.129,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n21223,8,477,2017-03-10 07:15:42,http://reillylindgren.org/andre,,166.100.70.136,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n21224,8,477,2017-04-15 02:51:30,http://streich.name/donna,,4.61.119.41,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n21225,8,477,2017-04-06 22:05:40,http://hirthemills.org/jordi,,87.245.205.182,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n21226,8,477,2016-12-22 21:18:06,http://cruickshank.co/ruby.feil,,98.135.245.39,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n21227,8,477,2017-04-03 19:30:57,http://conroy.com/ottis.emmerich,,110.226.170.46,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n21228,8,477,2017-02-13 21:33:46,http://runte.io/reva,,84.140.55.60,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n21229,8,477,2017-01-06 10:52:02,http://robel.biz/titus,,46.217.50.64,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n21230,8,477,2017-01-23 14:19:09,http://metzokuneva.info/eliezer,,102.154.235.9,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n21231,8,477,2017-03-29 10:54:05,http://dietrichlebsack.name/bernice_okeefe,,69.88.217.123,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n21232,8,477,2017-05-21 09:54:09,http://mueller.biz/selmer.hettinger,,27.214.246.87,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n21233,8,477,2017-05-21 19:11:57,http://deckow.org/aimee_frami,,211.181.104.106,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n21234,8,477,2017-06-12 08:44:37,http://bernier.io/rogers_shanahan,,154.122.150.4,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n21235,8,477,2017-05-22 23:56:49,http://mcclure.co/monique,,16.218.59.75,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n21236,8,477,2017-01-19 06:18:03,http://reichel.co/juana.connelly,,169.143.108.81,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n21237,8,477,2017-01-28 01:59:49,http://kovacek.net/jack.borer,,221.160.153.169,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n21238,8,477,2016-12-23 22:46:17,http://farrellwitting.name/joel,,143.66.12.12,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n21239,8,477,2017-03-21 07:03:47,http://framireichel.com/alex.heel,,206.160.108.143,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n21240,8,477,2017-04-28 04:16:58,http://stark.io/augusta,,240.195.92.102,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n21241,8,477,2017-05-31 05:06:58,http://hahnsimonis.name/esperanza,,107.116.51.177,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n21242,8,477,2016-12-28 13:45:44,http://maggio.co/alta,,42.209.140.171,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n21243,8,477,2017-01-07 00:35:38,http://conroy.co/otha_hammes,,56.87.134.220,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n21244,8,477,2017-01-10 01:26:06,http://sipes.co/soledad,,215.236.34.149,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n21245,8,477,2017-03-08 21:36:48,http://eichmann.info/marguerite,,43.134.197.239,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n21246,8,477,2017-02-24 01:08:40,http://mcdermott.name/terrell,,228.172.130.129,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n21247,8,477,2017-05-10 02:15:44,http://torpkautzer.biz/summer,,49.219.212.27,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n21248,8,477,2017-05-24 00:45:41,http://bayer.net/keenan_connelly,,217.77.157.145,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n21249,8,477,2017-05-30 17:09:33,http://funk.org/jamie,,25.139.98.193,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n21250,8,477,2016-12-23 17:43:55,http://rohanpacocha.com/miracle,,87.39.44.253,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n21251,8,477,2017-01-27 10:13:15,http://wiza.biz/neoma.hilll,,113.114.218.120,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n21252,8,477,2017-05-16 09:32:06,http://gibson.co/tom,,157.242.109.30,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n21253,8,477,2017-03-23 04:44:42,http://glover.co/clinton_brekke,,136.65.220.182,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n21254,8,477,2016-12-15 08:38:08,http://white.io/candido,,150.27.228.172,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n21255,8,477,2017-06-02 11:59:08,http://cain.name/kory.oconnell,,103.211.8.205,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n21256,8,477,2017-03-12 15:19:19,http://watershyatt.info/kieran_heel,,196.14.194.73,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n21257,8,477,2017-01-29 23:24:35,http://thompson.org/vernie.brakus,,194.140.199.184,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n21258,8,477,2017-03-08 11:58:21,http://boehmhintz.co/eugenia.durgan,,22.192.23.84,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n21259,8,477,2017-03-26 02:30:29,http://aufderhar.name/tanya.bednar,,89.111.191.11,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n21260,8,477,2017-03-01 04:45:11,http://waltersanford.net/gina_conroy,,142.71.188.161,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n21261,8,477,2017-01-01 23:34:34,http://auer.io/thea.roob,,48.130.211.130,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n21262,8,478,2017-02-02 05:57:48,http://kuhlman.com/daisha,,43.204.36.154,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n21263,8,478,2017-03-12 12:00:50,http://haley.com/kareem,,158.65.71.134,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n21264,8,478,2016-12-19 07:36:32,http://kohlermayer.io/demario,,121.33.224.242,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n21265,8,478,2017-05-01 12:45:10,http://emmerichquigley.org/landen_kuhic,,127.9.189.61,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n21266,8,478,2017-05-12 00:22:18,http://mohrbraun.name/odell_abshire,,245.217.31.155,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n156,1,5,2017-02-28 14:01:01,http://farrell.io/adeline,0.0973006459,140.92.208.155,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n157,1,5,2017-01-29 06:03:19,http://ortiz.net/emelia.gaylord,0.7117211981,245.10.13.153,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n158,1,5,2017-05-24 07:32:09,http://roob.io/sigmund_hyatt,0.7668921151,214.18.177.74,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n159,1,5,2017-05-19 10:37:13,http://volkmanswaniawski.name/sophia,0.9076940109,46.164.41.83,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n160,1,5,2016-12-14 20:30:03,http://roob.co/katarina,0.1184546810,182.225.238.154,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n161,1,5,2017-01-21 21:05:45,http://dibbert.io/taurean.kuhn,0.1564920005,86.81.68.98,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n162,1,5,2017-04-29 01:42:08,http://vonrueden.com/tommie.rogahn,0.7513364421,37.222.252.119,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n163,1,5,2017-03-10 12:35:06,http://stark.info/afton,0.3932017194,222.252.41.76,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n164,1,5,2016-12-16 14:48:39,http://marvin.com/aniya.nikolaus,0.2803402145,107.94.231.160,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n165,1,5,2017-04-03 10:13:39,http://danielschuster.biz/haleigh,0.0419757803,58.113.118.10,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n166,1,5,2017-02-12 19:38:36,http://schmidt.net/alfonzo_upton,0.3713954095,21.199.217.190,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n167,1,5,2017-04-03 18:18:14,http://toy.name/silas_cronin,0.6393181017,71.191.200.33,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n168,1,5,2017-03-26 07:58:41,http://boyer.org/octavia_ruel,0.4022229836,50.202.169.211,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n169,1,5,2017-02-04 07:11:19,http://klingmedhurst.org/roie,0.8675705676,195.53.8.185,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n170,1,5,2017-01-31 12:30:57,http://conroykuphal.biz/mona_kub,0.6259723123,203.105.14.87,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n171,1,5,2017-03-07 01:13:25,http://boehm.name/caitlyn.fahey,0.7856185636,211.168.83.69,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n172,1,5,2016-12-21 01:31:31,http://torp.io/dawn,0.5999685263,207.201.119.79,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n173,1,5,2017-01-19 01:41:44,http://kuphal.io/abdullah_smith,0.7368600212,108.36.194.181,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n174,1,5,2017-06-05 20:32:23,http://lynch.co/helga.bayer,0.5199178973,139.164.108.79,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n175,1,5,2017-03-05 07:26:27,http://moen.org/khalil,0.9734379114,56.50.239.44,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n176,1,5,2017-05-13 07:14:19,http://abbott.io/adelbert,0.5609720819,221.91.92.84,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n177,1,5,2017-02-04 20:36:12,http://schultzpfeffer.biz/allen,0.9632023669,233.81.180.130,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n178,1,5,2016-12-17 22:13:20,http://balistreri.info/camryn,0.3752039402,230.175.93.46,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n179,1,5,2017-05-10 23:13:02,http://gleason.name/nicholaus,0.2571087249,153.251.99.218,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n180,1,5,2017-05-17 01:43:25,http://leannonrunolfsdottir.name/melany,0.3954058751,247.232.194.24,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n181,1,5,2017-01-21 17:56:42,http://hodkiewiczlueilwitz.io/destany,0.0586740145,9.239.207.82,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n182,1,5,2017-03-15 15:13:04,http://gutkowskihansen.info/preston,0.1595139939,178.191.171.208,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n183,1,5,2017-01-01 15:50:44,http://kemmerschowalter.io/camilla_hirthe,0.1647224080,229.202.70.147,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n184,1,5,2017-05-28 04:12:32,http://grimes.name/daniela,0.5542240788,215.89.232.207,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n185,1,5,2017-05-25 16:51:06,http://dooley.com/ted_harvey,0.1095130625,199.147.84.107,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n186,1,5,2017-03-16 21:04:02,http://beier.com/austen.mosciski,0.9352864344,80.205.229.216,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n187,1,5,2017-02-25 11:04:02,http://roobconsidine.biz/mark,0.1912498207,5.16.99.162,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n188,1,5,2017-03-24 17:55:52,http://balistrerikozey.biz/elise,0.8558493466,90.82.193.37,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n189,1,5,2017-01-27 13:48:59,http://bins.info/melisa_lang,0.1717571191,172.15.39.109,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n190,1,5,2017-04-04 13:50:23,http://kuphal.info/hunter,0.9204948910,243.121.84.137,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n191,1,5,2017-01-23 14:39:15,http://wilkinson.net/cathryn.fisher,0.6264778865,8.8.70.158,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n192,1,5,2017-02-16 09:08:06,http://walshweber.co/eldora.gutkowski,0.6352322788,224.208.123.62,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n193,1,5,2017-01-18 10:12:20,http://hellerherman.name/rubye,0.8023360292,168.81.107.63,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n194,1,5,2017-01-31 21:24:32,http://schimmel.com/kade,0.0309756614,163.203.138.182,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n195,1,5,2017-02-28 12:50:40,http://hoppe.biz/nicholas,0.3865859501,127.116.151.208,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n196,1,6,2017-04-17 13:50:42,http://gutmann.net/cydney.mueller,0.5155177076,253.20.183.162,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n197,1,6,2017-04-12 20:39:22,http://willstehr.net/kyra,0.0286517080,32.185.126.197,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n198,1,6,2017-06-05 13:58:45,http://franecki.name/daisy,0.4765253859,189.87.70.59,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n199,1,6,2017-01-30 02:27:13,http://boehm.io/deonte_bruen,0.6783055797,181.5.178.191,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n200,1,6,2017-01-26 13:33:08,http://raynor.net/candida_prohaska,0.5607241009,133.88.178.149,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n201,1,6,2016-12-15 05:07:46,http://christiansenhermann.co/aleen_lesch,0.9177731078,32.88.25.183,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n202,1,6,2016-12-20 21:03:30,http://heidenreichgreen.co/dock,0.1383376981,244.137.113.126,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n203,1,6,2017-04-24 19:49:55,http://kingcasper.info/scot.johnson,0.1853921377,213.187.85.93,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n204,1,6,2016-12-16 09:44:43,http://jenkins.biz/baby.bradtke,0.7916128486,105.135.38.101,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n205,1,6,2017-06-04 21:28:16,http://ankundingsanford.name/zetta,0.9896897814,88.87.178.197,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n206,1,6,2017-01-27 18:30:14,http://dietrich.name/letha,0.0860835674,218.79.225.144,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n207,1,6,2017-04-13 01:02:00,http://jaskolskismith.io/deon,0.5650486488,199.215.37.113,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n208,1,6,2017-06-11 12:09:17,http://wisozkking.com/wava_schimmel,0.6031023198,151.66.200.253,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n21267,8,478,2016-12-28 16:51:44,http://gutmann.co/bernard,,13.6.182.63,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n21268,8,478,2017-05-15 17:06:35,http://predovic.com/nikita,,163.14.145.192,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n21269,8,478,2016-12-28 12:09:43,http://walkerharris.io/bradly_langosh,,78.21.194.211,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n21270,8,478,2017-03-16 09:48:49,http://kozey.org/jackson,,25.84.226.215,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n21271,8,478,2017-05-07 04:21:25,http://abbott.co/kraig,,209.179.145.8,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n21272,8,478,2016-12-17 21:05:13,http://metz.co/ronaldo.fisher,,150.211.179.244,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n21273,8,478,2017-03-04 17:07:00,http://ortiz.org/carolyn.will,,78.74.147.109,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n21274,8,478,2017-05-06 16:25:04,http://jaskolski.info/wendell.glover,,38.98.92.241,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n21275,8,478,2017-03-13 03:54:12,http://kulas.org/orie.kautzer,,150.199.220.23,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n21276,8,478,2017-05-15 19:09:30,http://ohara.info/estrella,,96.237.242.104,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n21277,8,478,2017-05-20 22:13:23,http://senger.info/bartholome.gerlach,,46.81.135.97,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n21278,8,478,2017-06-13 15:01:29,http://walter.com/henry.kuhlman,,191.232.195.220,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n21279,8,478,2017-03-24 08:14:52,http://durgannikolaus.biz/sigrid.hauck,,249.206.51.98,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n21280,8,478,2017-04-30 08:12:55,http://wizaberge.com/robb,,90.226.20.23,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n21281,8,478,2017-04-14 17:05:56,http://reinger.io/floy,,36.94.122.69,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n21282,8,478,2017-05-23 17:02:37,http://beierebert.org/bill,,56.177.71.80,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n21283,8,478,2017-03-12 00:42:45,http://wilderman.org/stefanie,,125.194.250.198,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n21284,8,478,2017-01-02 18:31:08,http://corkery.io/elisabeth.veum,,102.80.96.27,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n21285,8,478,2017-05-12 11:54:03,http://lebsack.io/humberto,,94.215.183.139,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n21286,8,478,2017-06-10 03:34:21,http://koepp.com/jo,,25.66.43.252,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n21287,8,478,2017-06-12 05:29:21,http://hayes.info/einar,,74.154.211.121,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n21288,8,478,2017-03-09 22:21:07,http://willmchulist.info/ro,,253.90.16.69,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n21289,8,478,2017-05-03 11:10:51,http://hammesward.net/martine_cronin,,167.94.24.72,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n21290,8,478,2017-06-06 20:45:31,http://smith.name/kailey,,155.73.27.242,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n21291,8,478,2017-04-15 14:43:20,http://jakubowskicorwin.org/pinkie,,22.204.95.37,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n21292,8,478,2017-01-29 15:51:32,http://sawayn.com/xander,,86.46.129.110,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n21293,8,478,2017-03-18 00:57:43,http://wardlind.biz/jamie_zieme,,197.185.179.127,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n21294,8,479,2017-02-20 16:47:28,http://botsford.info/adrian.schuppe,,6.129.79.27,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n21295,8,479,2017-02-13 00:49:55,http://aufderharlehner.biz/julia_ryan,,247.211.152.196,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n21296,8,479,2017-05-09 18:58:25,http://dach.org/ardella,,117.219.55.16,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n21297,8,479,2017-04-11 07:46:37,http://von.info/ella,,46.83.48.83,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n21298,8,479,2017-04-22 20:48:56,http://reingerrosenbaum.net/janet_pfeffer,,48.249.229.174,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n21299,8,479,2017-04-09 11:01:48,http://thiel.biz/alexandro,,75.35.50.27,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n21300,8,479,2017-03-17 01:51:12,http://dare.io/nikki_altenwerth,,48.232.12.135,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n21301,8,479,2017-06-11 12:17:01,http://monahan.net/rogelio_feil,,191.101.111.117,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n21302,8,479,2017-02-05 17:02:33,http://abernathy.co/lisette.purdy,,128.24.59.150,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n21303,8,479,2017-04-23 08:28:39,http://collierhayes.io/sarina,,232.233.145.104,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n21304,8,479,2017-06-14 00:20:39,http://beatty.net/kayley.dibbert,,124.53.229.143,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n21305,8,479,2017-05-25 19:22:47,http://millersipes.com/shana,,247.131.221.149,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n21306,8,479,2017-05-05 09:12:07,http://littelframi.co/linnie_batz,,231.178.99.86,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n21307,8,479,2017-03-06 09:50:21,http://ankunding.info/marilie_littel,,227.77.160.223,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n21308,8,479,2016-12-18 23:14:23,http://krisgulgowski.co/peter,,120.243.123.208,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n21309,8,479,2017-03-17 18:56:10,http://spencertorp.io/bonita_donnelly,,239.121.254.35,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n21310,8,479,2017-03-30 01:27:59,http://kertzmann.io/jaleel.abshire,,240.124.109.138,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n21311,8,479,2017-05-29 22:38:23,http://carroll.name/retta.walsh,,80.126.62.203,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n21312,8,479,2017-01-19 21:58:47,http://dubuque.info/darryl,,126.218.143.193,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n21313,8,479,2017-04-02 20:22:26,http://swaniawskihilpert.info/arnoldo_boyer,,109.235.104.62,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n21314,8,479,2017-01-22 01:35:03,http://altenwerth.info/donnell,,13.160.72.133,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n21315,8,479,2017-04-05 22:53:30,http://kertzmannschroeder.net/luis.cummerata,,117.121.220.44,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n21316,8,479,2016-12-23 19:56:33,http://lindgren.name/baron,,151.122.79.95,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n21317,8,479,2017-03-10 22:21:30,http://johns.org/edd,,3.12.27.210,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n21318,8,479,2017-03-05 23:44:04,http://jacobsongerhold.io/allie,,191.252.74.88,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n21319,8,479,2017-01-13 16:51:52,http://bins.biz/alda,,230.40.61.172,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n21320,8,479,2017-01-01 20:05:39,http://rowe.com/theodore,,187.232.246.117,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n21321,8,479,2017-03-09 21:37:45,http://douglas.com/julio_gaylord,,35.69.134.41,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n21322,8,479,2017-02-25 03:25:51,http://ricemacejkovic.org/bertha,,208.149.61.29,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n209,1,6,2017-03-18 17:01:21,http://raynordouglas.name/reagan_tromp,0.7501246284,19.68.69.99,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n210,1,6,2017-04-16 16:31:07,http://schmelerraynor.com/monserrat_gibson,0.5465721721,228.111.37.47,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n211,1,6,2017-03-26 20:04:55,http://wolffbuckridge.co/shanelle,0.5066755219,186.177.147.162,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n212,1,6,2017-03-26 09:42:03,http://colekozey.co/camila,0.9769205887,156.44.156.135,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n213,1,6,2017-03-30 00:21:52,http://boehmconn.name/angelita_pagac,0.8910228658,153.142.61.93,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n214,1,6,2017-04-20 15:38:23,http://graham.io/jefferey,0.3867140401,27.157.201.85,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n1048,1,24,2017-03-26 10:51:38,http://terry.com/lucio,,147.89.43.44,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n215,1,6,2017-04-23 09:14:56,http://labadie.com/karl.rodriguez,0.0944978064,50.41.53.48,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n216,1,6,2017-04-18 17:15:49,http://stantonraynor.biz/verdie_harris,0.7748436150,100.239.196.216,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n217,1,6,2017-03-31 19:17:51,http://okon.name/myriam,0.6210429025,85.212.209.69,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n218,1,6,2017-04-09 21:57:11,http://schaefer.biz/alexis.metz,0.1912995859,242.149.190.55,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n219,1,6,2017-05-30 16:31:08,http://strosin.org/athena,0.5384474205,214.116.22.251,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n220,1,6,2017-03-16 19:27:05,http://bernhardstokes.net/heath,0.7268181081,101.119.111.159,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n221,1,6,2017-06-05 07:43:11,http://jacobi.io/robert_steuber,0.4607575021,51.56.141.203,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n222,1,6,2017-01-29 12:10:25,http://mooreflatley.net/anastasia.feil,0.9680427466,145.249.163.64,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n223,1,6,2017-04-23 21:40:42,http://brakuseffertz.net/lucinda,0.0078797077,97.77.185.183,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n224,1,6,2017-01-20 09:36:48,http://windler.org/audra,0.1746856190,135.225.205.59,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n225,1,6,2017-06-05 02:21:24,http://emmerichcartwright.biz/calista.mckenzie,0.8087271972,230.149.249.229,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n226,1,6,2017-02-27 23:11:06,http://raynor.net/zella,0.9490212741,38.197.253.115,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n227,1,6,2017-01-04 14:10:05,http://goyette.biz/major.grant,0.5452318489,18.10.222.243,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n228,1,6,2017-01-24 02:04:32,http://ryan.info/vernice,0.9190438041,182.243.172.193,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n229,1,6,2017-03-16 12:24:40,http://brekke.com/loma_jerde,0.7985769383,121.26.155.40,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n230,1,6,2017-05-26 07:50:22,http://nader.com/mohammed,0.3194378282,121.145.19.247,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n231,1,6,2017-05-19 18:32:41,http://botsford.biz/jaylon.stiedemann,0.6751005354,182.150.74.70,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n232,1,6,2017-02-12 13:09:21,http://stracke.net/lolita,0.7501822518,188.219.180.248,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n233,1,6,2017-05-30 08:51:52,http://leuschke.co/felix.rowe,0.4612989301,68.29.181.197,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n234,1,6,2017-02-04 07:11:37,http://hilll.info/jazmyn,0.8515188437,195.55.59.163,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n235,1,6,2017-03-04 00:32:13,http://effertzhilpert.org/adriel,0.6796913962,39.88.236.28,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n236,1,6,2017-03-19 20:00:35,http://adams.info/dawn,0.2202506216,250.180.63.182,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n237,1,6,2017-02-07 13:56:41,http://hintz.io/abe,0.9430528767,81.172.109.71,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n238,1,6,2017-03-11 14:31:25,http://bins.info/elody_hackett,0.7404865153,220.221.95.245,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n239,1,6,2017-01-08 03:53:16,http://reinger.org/catharine.hahn,0.5051925980,217.170.134.37,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n240,1,6,2017-01-10 22:10:27,http://williamson.info/macie_emmerich,0.1841981852,62.128.62.121,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n241,1,6,2017-03-08 00:27:14,http://waters.io/suzanne,0.3225056989,64.179.45.203,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n242,1,6,2017-03-04 05:52:23,http://yost.com/alison_dicki,0.2189924544,241.47.88.240,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n243,1,6,2017-06-09 23:40:52,http://schulistbosco.org/amanda_kozey,0.7364554407,200.205.105.34,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n244,1,6,2017-04-29 15:13:02,http://mueller.org/earnestine,0.8886049839,137.51.214.39,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n245,1,6,2017-05-30 09:17:10,http://hansen.com/ryan,0.6890976754,126.112.247.243,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n246,1,6,2017-03-21 09:10:46,http://bergstrom.com/shana_kautzer,0.1192768589,160.154.45.137,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n247,1,6,2017-02-28 04:14:54,http://hudsonquigley.org/adrain,0.2851109589,63.200.15.158,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n248,1,6,2017-02-08 13:48:55,http://von.net/erica_gulgowski,0.5384702868,77.235.89.203,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n249,1,6,2017-02-02 11:22:39,http://jones.info/kadin_hyatt,0.3827914721,234.218.36.154,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n250,1,6,2017-05-10 15:48:16,http://baileybaumbach.co/jodie.brakus,0.3793901749,43.237.135.95,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n251,1,6,2017-05-19 03:08:38,http://jacobsonmoore.net/gloria_heathcote,0.9626236445,219.63.22.60,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n252,1,6,2017-02-16 15:24:40,http://altenwerthkonopelski.name/willis,0.7496539756,23.39.176.58,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n253,1,6,2016-12-16 20:03:23,http://jakubowskibeier.io/emmanuel_wyman,0.6223168743,239.151.223.207,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n254,1,6,2017-02-28 23:08:42,http://littel.biz/trisha.koepp,0.4587851717,155.73.42.24,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n255,1,6,2017-01-06 18:52:48,http://hermiston.co/joanie,0.8136161872,131.122.81.235,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n256,1,6,2017-01-29 05:23:43,http://hackett.name/uriel,0.9420652736,51.118.217.30,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n257,1,6,2017-01-18 01:27:01,http://skilesromaguera.name/maximo_murazik,0.0886841203,24.92.149.123,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n258,1,6,2017-03-31 20:18:43,http://ullrichkub.net/mason,0.4903908107,134.203.215.223,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n259,1,6,2016-12-27 06:48:18,http://stokes.io/verla,0.3702651128,149.160.217.153,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n21323,8,479,2017-05-24 12:48:33,http://kochmertz.com/rachel,,3.220.12.202,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n21324,8,479,2016-12-24 11:53:21,http://zboncak.name/myrna,,137.78.18.181,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n21325,8,479,2017-02-01 18:21:39,http://predovicherman.biz/karson,,28.162.133.101,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n21326,8,479,2017-01-09 02:45:43,http://moriette.org/westley_gleichner,,177.68.249.139,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n21327,8,479,2017-05-25 17:12:49,http://marks.name/brice,,59.223.56.201,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n21328,8,479,2017-02-06 14:44:35,http://heller.name/josue,,246.74.124.247,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n21329,8,479,2017-03-31 08:50:35,http://cruickshank.co/louisa_deckow,,62.6.240.190,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n21330,8,479,2017-02-03 20:30:09,http://jastblock.co/mallie,,198.191.141.146,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n21331,8,479,2017-05-10 11:36:44,http://moendickens.info/ted,,109.111.67.238,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n21332,8,479,2017-01-01 14:36:04,http://lindgren.io/raphael.dach,,163.74.100.64,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n21333,8,479,2017-01-28 23:45:44,http://johnston.info/earl,,236.138.22.149,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n21334,8,479,2017-01-06 22:53:30,http://toy.net/madelynn,,242.218.26.164,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n21335,8,479,2017-03-10 23:44:30,http://champlin.org/ken,,210.157.215.166,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n21336,8,479,2017-05-17 02:20:53,http://larson.biz/una,,44.136.94.107,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n21337,8,479,2017-04-07 16:29:39,http://ullrich.org/isabel_padberg,,200.145.112.62,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n21338,8,479,2017-05-08 15:56:23,http://mullerharber.net/kim,,142.85.113.67,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n21339,8,479,2017-02-24 23:05:41,http://williamson.info/koby.stroman,,161.233.58.89,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n21340,8,479,2017-06-10 12:19:40,http://hoeger.co/trevor.leffler,,227.2.59.3,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n21341,8,479,2017-02-01 16:30:15,http://tremblay.org/madeline_schaden,,222.88.57.202,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n21342,8,479,2017-05-03 09:03:26,http://welch.biz/tanya.schuppe,,92.141.135.242,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n21343,8,479,2017-06-12 05:06:50,http://volkman.org/edd_cartwright,,237.121.30.5,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n21344,8,479,2016-12-15 23:35:20,http://oberbrunnerbradtke.co/jack_konopelski,,210.56.16.230,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n21345,8,479,2017-04-21 23:13:39,http://jenkins.org/demarco,,238.198.45.217,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n21346,8,479,2017-03-20 12:39:52,http://mclaughlinlemke.com/maximo.dickens,,152.16.186.84,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n21347,8,479,2017-05-02 16:00:07,http://doyle.com/tomas,,40.101.226.188,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n21348,8,479,2017-05-28 03:04:37,http://lakin.info/tristin.huel,,37.100.236.178,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n21349,8,479,2017-04-20 00:43:12,http://eichmann.net/mona_wunsch,,217.133.182.65,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n21350,8,479,2017-06-03 10:23:05,http://beier.info/everardo.torp,,7.82.160.129,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n21351,8,479,2017-03-06 16:30:08,http://bartolettibernier.biz/alisa_fritsch,,165.229.10.146,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n21352,8,479,2016-12-29 08:24:11,http://johnson.biz/antonio.fadel,,198.138.194.103,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n21353,8,479,2017-01-24 11:01:55,http://hyatthaag.biz/novella,,229.216.76.98,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n21354,8,479,2017-03-27 10:15:22,http://wintheiser.net/rowena_towne,,218.223.167.96,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n21355,8,479,2017-06-05 01:56:31,http://wintheiser.org/tyreek,,158.3.7.124,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n21356,8,479,2017-04-09 21:12:05,http://watsica.name/britney_brown,,75.218.224.150,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n21357,8,479,2017-05-29 17:40:58,http://greenfeldergoyette.io/koby.kirlin,,162.5.47.218,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n21358,8,479,2016-12-16 17:18:53,http://leschschuster.biz/keshaun_leuschke,,78.101.173.128,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n21359,8,479,2017-03-21 23:06:48,http://gislasonhackett.net/joe,,156.15.171.88,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n21360,8,479,2017-02-09 06:24:12,http://schoencartwright.name/maci,,75.133.115.146,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n21361,8,479,2017-02-09 22:04:02,http://kuhn.io/angela.heathcote,,165.131.167.110,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n21362,8,479,2017-01-01 18:23:07,http://purdy.co/brain,,13.164.126.97,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n21363,8,480,2017-03-13 20:12:08,http://jones.name/ruell.hayes,,17.78.76.97,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n21364,8,480,2017-05-21 00:58:20,http://barton.info/edison,,157.169.151.145,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n21365,8,480,2017-01-28 08:02:19,http://harveysanford.info/gilbert,,58.185.207.95,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n21366,8,480,2017-03-10 15:19:05,http://gerhold.info/max,,66.165.18.86,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n21367,8,480,2017-06-07 12:03:12,http://crooks.io/deron.herzog,,129.206.136.167,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n21368,8,480,2017-03-27 07:18:19,http://sanford.io/jadyn_kaulke,,204.208.247.42,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n21369,8,480,2017-02-25 10:25:59,http://balistreri.biz/conrad,,55.26.240.155,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n21370,8,480,2017-03-21 16:27:11,http://kiehn.net/emmet,,95.146.228.29,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n21371,8,480,2017-01-11 20:37:22,http://mcdermott.name/junius.rau,,31.82.170.164,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n21372,8,480,2017-03-27 23:30:12,http://starkmoen.biz/shaylee.schuster,,169.23.227.192,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n21373,8,480,2017-01-03 04:39:45,http://luettgen.com/tierra,,58.171.169.81,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n21374,8,480,2017-05-19 12:25:06,http://willmsfranecki.co/don,,246.221.130.31,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n21375,8,480,2017-03-16 11:21:03,http://torp.name/beie.conroy,,97.228.188.234,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n21376,8,480,2017-05-10 07:10:58,http://wisokykozey.com/imogene.brown,,81.129.115.27,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n21377,8,480,2016-12-30 01:23:33,http://heidenreich.net/miller.quigley,,112.73.160.217,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n260,1,6,2017-04-03 09:43:30,http://larson.org/oscar,0.2864307208,106.187.40.128,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n261,1,6,2017-01-03 10:09:12,http://kozey.org/genevieve.hansen,0.7212253501,29.84.70.245,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n262,1,6,2017-01-04 19:16:01,http://ryan.io/je,0.2960264203,62.100.252.64,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n263,1,6,2017-05-13 12:12:08,http://romaguera.net/crystal.kulas,0.3073693934,12.89.127.107,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n264,1,6,2017-01-25 12:16:14,http://heathcote.org/jerad_smith,0.7063256905,171.151.250.61,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n265,1,6,2017-05-02 02:41:10,http://lynch.biz/agnes_fay,0.1151494915,242.55.15.141,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n266,1,7,2017-03-15 17:00:41,http://buckridge.biz/eriberto_konopelski,0.7619650945,194.216.49.11,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n267,1,7,2017-05-03 13:28:47,http://cristpadberg.io/lia.trantow,0.8814378079,246.198.17.158,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n268,1,7,2017-04-22 23:58:00,http://rodrigueztrantow.org/jamey_fay,0.5239271210,190.210.172.134,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n269,1,7,2017-02-18 13:58:27,http://hackettbergstrom.io/ryley_franecki,0.5350009438,55.249.104.107,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n270,1,7,2017-06-12 00:43:54,http://smithambrown.name/felicity,0.3516943662,39.40.180.181,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n271,1,7,2017-03-23 23:50:35,http://luettgencorwin.biz/samson_hegmann,0.3452650771,82.40.130.131,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n272,1,7,2017-03-23 16:35:19,http://dietrichzboncak.biz/emil.price,0.2741194903,250.236.187.52,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n273,1,7,2016-12-17 23:57:21,http://bruen.biz/parker.kuhn,0.2805371257,204.49.49.216,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n274,1,7,2017-02-20 07:27:41,http://douglas.io/haleigh.botsford,0.8144940696,239.194.17.254,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n275,1,7,2016-12-31 09:54:26,http://creminmoore.com/magnus,0.9784108809,243.193.31.49,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n276,1,7,2017-01-28 01:00:26,http://bogisich.io/maurice_mayert,0.7996932547,201.163.104.192,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n277,1,7,2017-03-31 22:34:10,http://ledner.org/eula,0.9983680004,68.223.168.47,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n278,1,7,2016-12-15 12:51:46,http://becker.co/rachel_hirthe,0.5205821927,100.45.224.158,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n279,1,7,2017-01-19 10:46:42,http://kozey.net/donna_crooks,0.9312369087,3.172.252.233,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n280,1,7,2017-03-19 05:00:47,http://keler.co/serenity.gottlieb,0.0045982871,134.247.219.94,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n281,1,7,2017-02-05 01:24:38,http://armstrong.net/jillian,0.2932671090,91.11.189.30,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n282,1,7,2017-04-30 02:08:25,http://rogahn.com/cortez.oconner,0.7806050391,137.27.224.89,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n283,1,7,2017-03-05 23:55:34,http://trantow.name/je_zboncak,0.3618442111,211.240.227.251,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n284,1,7,2017-03-02 22:02:19,http://damore.name/pietro_rice,0.7685661566,242.200.40.75,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n285,1,7,2017-05-05 17:31:28,http://steuber.biz/don,0.7519503138,86.198.75.31,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n286,1,7,2017-01-01 02:16:45,http://uptonwillms.io/lisandro.konopelski,0.5728517451,109.231.38.130,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n287,1,7,2017-03-14 00:46:54,http://brekke.co/belle.lynch,0.8065999620,186.184.245.233,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n288,1,7,2017-04-13 14:02:01,http://gleasonleffler.io/elroy,0.9291218574,10.83.80.43,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n289,1,7,2017-01-02 19:08:54,http://stokeskris.co/antonetta_vonrueden,0.3180651822,229.37.123.85,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n290,1,7,2017-02-26 16:41:08,http://nicolaslubowitz.info/marcella,0.9861733782,9.231.210.104,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n291,1,7,2017-01-10 11:02:15,http://macejkovicheidenreich.org/keanu,0.6432571478,12.179.41.223,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n292,1,7,2017-04-08 07:23:12,http://greenokeefe.net/salma_baumbach,0.4071050860,166.106.42.91,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n293,1,7,2016-12-24 09:50:23,http://hand.org/allene.leuschke,0.4093570870,96.170.204.167,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n294,1,7,2017-04-25 21:01:10,http://osinskijohnston.io/kaley.lubowitz,0.3421712197,172.246.70.245,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n295,1,7,2017-01-16 14:39:14,http://harrichuppe.io/marcos,0.9523782445,29.244.43.243,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n296,1,7,2016-12-26 13:14:56,http://koch.name/richard_swift,0.2935921328,177.184.46.13,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n297,1,7,2016-12-28 20:20:22,http://bailey.org/willard_robel,0.1899216399,159.170.35.26,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n298,1,7,2017-05-03 16:34:21,http://collinscruickshank.info/jayne,0.6219826289,176.194.184.2,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n299,1,7,2017-04-16 01:16:31,http://kuvalis.net/emerson_barton,0.6746315878,177.26.106.161,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n300,1,7,2016-12-21 11:47:38,http://hintz.net/delphia,0.9954208743,113.50.173.188,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n301,1,7,2017-03-29 16:48:17,http://kirlin.com/aryanna,0.2875143554,153.190.154.47,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n302,1,8,2016-12-16 18:15:47,http://weimann.net/ronaldo_haley,,242.140.207.225,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n303,1,8,2016-12-13 12:23:57,http://cruickshankschmidt.info/lori_beahan,,243.88.26.72,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n304,1,8,2016-12-27 10:32:30,http://franeckiquigley.org/rick_koepp,,34.215.100.8,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n305,1,8,2017-01-17 09:38:01,http://lang.info/bianka,,150.150.105.101,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n306,1,8,2017-02-16 13:39:00,http://harris.org/dimitri.heaney,,68.155.157.138,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n307,1,8,2017-01-19 10:28:13,http://bruen.org/dashawn.smitham,,14.95.222.47,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n308,1,8,2017-05-27 19:51:31,http://nikolaus.io/cora,,184.47.164.254,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n309,1,8,2017-05-03 06:01:58,http://olson.com/kathlyn,,151.115.36.207,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n310,1,8,2017-01-19 21:14:13,http://cormier.info/elisabeth,,171.82.150.188,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n311,1,8,2016-12-14 06:40:45,http://boehm.com/kurt.schamberger,,148.141.70.170,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n312,1,8,2017-02-23 21:10:55,http://macgyverwilkinson.com/lina.damore,,153.220.141.226,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n21378,8,480,2017-01-17 12:51:41,http://mann.co/ebba,,119.17.120.115,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n21379,8,480,2017-05-23 09:54:48,http://litteldach.biz/nicholas_eichmann,,117.110.100.53,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n21380,8,480,2017-01-09 00:35:17,http://bailey.biz/nikko_keler,,233.101.79.99,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n21381,8,480,2017-06-04 04:53:46,http://wymandicki.name/billy_pouros,,203.250.3.132,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n21382,8,480,2017-03-05 21:28:14,http://bechtelar.biz/leonardo,,201.21.221.42,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n21383,8,480,2016-12-22 02:40:33,http://pagacwelch.io/ceasar.bernhard,,67.112.121.201,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n21384,8,480,2017-03-15 14:24:14,http://hamill.info/elva_bogisich,,34.54.14.154,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n21385,8,480,2017-01-08 10:49:24,http://schummrunolfsdottir.net/aunta.rowe,,15.176.19.12,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n21386,8,480,2017-01-07 10:23:42,http://dubuque.net/maye,,89.157.157.235,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n21387,8,480,2017-05-04 15:37:33,http://bernier.com/madelyn.bednar,,96.245.126.227,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n21388,8,480,2017-02-24 16:52:25,http://reichert.co/toni_hansen,,179.121.83.145,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n21389,8,480,2016-12-15 17:26:33,http://oconnell.co/trevion,,208.168.29.4,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n21390,8,480,2017-04-30 00:28:49,http://prohaskaebert.net/ila.crooks,,54.85.156.11,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n21391,8,480,2017-02-08 10:31:42,http://sipes.com/wyatt,,28.87.37.186,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n21392,8,480,2017-02-27 03:29:00,http://stracke.biz/jalen.runolfsdottir,,242.112.200.156,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n21393,8,480,2017-05-24 00:52:00,http://schambergerrice.info/katherine.dickinson,,155.122.127.119,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n21394,8,480,2017-04-27 08:57:08,http://bashirian.io/wilmer.ondricka,,151.234.37.76,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n21395,8,480,2017-05-23 22:23:39,http://morar.co/jordi_eichmann,,205.41.243.230,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n21396,8,480,2017-01-03 05:03:59,http://murray.biz/rasheed,,126.137.101.155,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n21397,8,480,2016-12-28 09:03:14,http://thiel.org/viviane,,13.56.26.14,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n21398,8,480,2017-03-16 01:24:41,http://powlowski.org/lew.crist,,92.171.155.221,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n21399,8,480,2017-02-01 00:35:56,http://monahan.org/laura,,135.81.92.53,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n21400,8,480,2017-02-22 18:57:35,http://schmidt.io/benjamin,,64.215.148.145,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n21401,8,480,2017-01-16 10:30:47,http://keebler.info/else,,17.179.12.99,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n21402,8,480,2017-02-13 01:44:24,http://beierlakin.name/vern,,70.15.140.225,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n21403,8,480,2017-05-18 23:51:07,http://funk.com/olin,,236.149.96.50,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n21404,8,480,2017-02-14 03:29:40,http://strosin.info/orlando,,213.175.106.74,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n21405,8,480,2016-12-27 01:40:15,http://raynordoyle.name/martina,,17.92.55.108,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n21406,8,480,2017-01-23 01:23:16,http://stokesvon.org/adolfo.howell,,29.120.77.52,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n21407,8,480,2017-02-08 07:46:56,http://pfeffer.org/alice,,175.143.203.182,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n21408,8,480,2017-05-30 20:09:36,http://weimann.com/meggie,,135.172.244.213,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n21409,8,480,2017-04-19 05:09:55,http://okeefe.co/alex.dach,,22.174.2.171,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n21410,8,480,2017-03-25 08:08:13,http://robel.com/jettie,,142.208.114.223,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n21411,8,480,2017-01-30 22:08:57,http://hermanmayer.name/delaney,,44.87.203.91,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n21412,8,480,2016-12-23 22:50:29,http://steuber.com/adonis.adams,,112.157.126.28,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n21413,8,480,2017-04-04 12:24:14,http://ko.com/evangeline,,37.244.92.34,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n21414,8,480,2016-12-19 14:28:37,http://bernhard.org/jaylin,,84.90.251.208,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n21415,8,481,2017-03-27 04:33:02,http://price.net/mortimer_smith,0.7051708670,171.254.26.164,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n21416,8,481,2017-05-11 23:14:45,http://shields.net/braeden,0.2964600864,128.185.162.83,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n21417,8,481,2017-03-10 06:56:24,http://spencer.biz/leonor.walter,0.6856056816,197.129.119.150,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n21418,8,481,2017-04-25 04:58:47,http://colefadel.net/claudie_turcotte,0.4595871251,59.140.183.157,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n21419,8,481,2017-01-13 02:02:33,http://bednar.com/orlo,0.5000198945,54.39.79.168,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n21420,8,481,2017-05-17 20:31:56,http://konopelski.name/dudley,0.1520285004,69.10.60.191,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n21421,8,481,2017-03-12 15:47:00,http://andersongulgowski.net/nestor,0.4910319367,67.142.20.145,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n21422,8,481,2017-03-02 14:09:10,http://blick.biz/carolina_schneider,0.1628165717,198.74.196.158,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n21423,8,481,2017-05-05 19:58:59,http://wilkinson.net/augusta,0.6445749264,100.241.243.235,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n21424,8,481,2017-01-10 15:03:05,http://kirlindickens.io/dan_parisian,0.2058874908,172.126.171.44,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n21425,8,481,2017-02-26 16:09:41,http://ko.co/shyanne.green,0.0177032475,232.119.131.29,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n21426,8,481,2017-04-09 04:53:57,http://renner.net/jaime_dooley,0.6240295143,130.119.199.248,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n21427,8,481,2017-04-14 20:25:32,http://davisbogan.name/darren,0.1598364699,85.193.226.34,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n21428,8,481,2017-02-14 17:12:34,http://schowaltermoriette.biz/kadin_smitham,0.1867757225,38.253.155.132,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n21429,8,481,2017-05-28 04:04:19,http://beahan.com/ellie,0.6610382410,218.83.147.14,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n21430,8,481,2017-03-31 17:23:04,http://kuvalis.com/marquise,0.1173893796,73.24.151.182,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n21431,8,481,2017-06-13 20:43:41,http://moen.name/myra,0.1079982039,85.65.238.157,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n313,1,8,2017-05-10 00:48:15,http://damore.info/lyric_ernser,,30.36.60.121,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n314,1,8,2017-03-19 03:32:49,http://quitzon.co/nat_ohara,,159.195.68.147,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n315,1,8,2016-12-25 08:29:35,http://cruickshank.info/darwin,,110.77.15.188,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n316,1,8,2017-04-18 18:23:30,http://bernier.info/tom_stoltenberg,,74.184.192.76,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n317,1,8,2017-04-10 04:08:02,http://cummeratalesch.org/abdul,,100.46.246.37,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n318,1,8,2017-05-22 13:47:17,http://crooks.name/clay,,92.49.186.30,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n319,1,8,2017-06-12 16:56:22,http://weber.io/icie.ledner,,9.234.14.36,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n320,1,8,2017-04-25 17:26:33,http://schmelerterry.net/bonnie.hirthe,,193.231.31.133,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n321,1,8,2016-12-23 18:10:58,http://wittingheller.org/remington,,12.120.98.161,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n322,1,8,2017-03-11 19:07:33,http://boscopfannerstill.io/jacques,,153.120.103.128,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n323,1,8,2017-06-11 04:24:43,http://mcdermott.name/ayden.orn,,110.208.27.149,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n324,1,8,2017-01-12 11:15:18,http://hammesheathcote.com/alfonzo_yost,,231.69.190.214,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n325,1,8,2017-05-04 20:45:40,http://mayer.biz/fritz,,232.215.103.28,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n326,1,8,2017-03-11 13:34:56,http://grimesgusikowski.info/claud,,181.98.80.139,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n327,1,8,2017-03-19 00:45:17,http://auer.name/name,,108.22.241.163,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n328,1,8,2017-03-18 16:24:07,http://schillergreenfelder.info/valentin_sawayn,,166.82.27.22,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n329,1,8,2017-01-31 10:03:06,http://paucek.org/emmie,,192.46.253.176,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n330,1,8,2016-12-24 10:36:23,http://ferrywilliamson.info/cornell,,228.196.155.251,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n331,1,8,2017-04-16 08:42:32,http://lockman.net/rodolfo_rohan,,156.96.140.159,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n332,1,8,2017-02-09 18:15:36,http://witting.com/vergie.block,,103.28.212.205,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n333,1,8,2017-05-04 11:20:49,http://simonis.org/lexi.kihn,,69.232.104.59,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n334,1,8,2017-04-10 21:01:42,http://gerlach.name/miracle,,216.68.204.66,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n335,1,8,2017-03-02 11:11:59,http://moore.io/reie_mcglynn,,105.100.130.236,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n336,1,8,2017-03-04 18:08:37,http://wilkinsonhettinger.io/elroy,,6.178.181.212,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n337,1,8,2017-05-23 05:40:04,http://gutkowski.name/kenneth,,252.129.190.3,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n338,1,8,2017-06-11 14:19:08,http://konopelskimarks.co/arielle,,16.16.227.45,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n339,1,8,2017-02-14 21:18:56,http://turcotte.net/lilliana,,81.147.64.13,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n340,1,8,2017-03-24 10:39:09,http://bernhard.org/moshe,,206.167.216.203,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n341,1,9,2017-04-10 05:28:10,http://mcglynn.org/benny.armstrong,,137.65.174.79,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n342,1,9,2017-04-24 09:18:06,http://aufderhar.co/lori,,135.201.3.229,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n343,1,9,2017-05-20 19:11:19,http://schoenwunsch.co/elisabeth,,189.151.166.242,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n344,1,9,2017-03-20 05:37:05,http://danielrowe.org/lupe,,112.8.164.165,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n345,1,9,2017-05-14 22:42:41,http://mcculloughwalter.info/evan_hauck,,32.165.179.57,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n346,1,9,2017-01-19 00:12:46,http://cole.com/kelton_klocko,,57.116.30.178,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n347,1,9,2017-01-27 20:36:01,http://nienow.net/jadon_langworth,,106.241.129.86,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n348,1,9,2017-04-28 12:20:53,http://grimetreich.net/jordi,,120.49.22.122,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n349,1,9,2017-05-12 12:33:20,http://walter.info/prince,,37.82.182.78,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n350,1,9,2017-05-03 12:26:19,http://ruel.info/janiya.adams,,95.5.14.122,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n351,1,9,2017-04-01 16:56:20,http://waelchicremin.net/lisette_kuphal,,209.43.122.70,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n352,1,9,2017-02-16 03:45:03,http://bergeveum.co/travon,,161.130.165.111,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n353,1,9,2017-02-06 08:40:49,http://kirlin.biz/mertie,,21.92.38.138,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n354,1,9,2017-06-03 11:41:42,http://murphy.info/waylon,,134.150.30.248,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n355,1,9,2017-05-18 20:26:49,http://dibbertmcclure.info/myrl,,67.119.243.225,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n356,1,9,2017-01-13 22:31:32,http://pollichconnelly.biz/shad.kuhlman,,155.237.17.84,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n357,1,9,2017-04-21 17:39:40,http://reinger.biz/tre,,202.81.250.62,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n358,1,9,2017-06-05 03:18:27,http://breitenberglittle.co/randi.cronin,,218.116.97.120,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n359,1,9,2017-04-07 22:52:45,http://hahngleason.com/kellie.mayer,,203.55.86.88,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n360,1,9,2017-03-20 17:31:52,http://mills.co/floy.murray,,254.41.109.161,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n361,1,9,2017-04-16 20:32:35,http://steuber.biz/vincenza_sanford,,109.219.211.88,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n362,1,9,2017-03-19 08:26:27,http://cain.org/abagail,,234.8.35.114,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n363,1,9,2017-04-28 17:57:54,http://murphy.com/herminio.upton,,39.30.116.29,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n364,1,9,2017-03-11 02:05:05,http://medhurst.org/tyrel_luettgen,,253.48.216.99,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n365,1,9,2017-04-10 23:09:21,http://mclaughlingreenholt.co/tamara.dooley,,217.60.75.15,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n366,1,9,2017-01-03 02:06:50,http://mcclure.co/fannie,,36.128.62.29,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n367,1,9,2017-01-15 06:07:08,http://koch.io/waino,,212.79.141.113,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n368,1,9,2017-03-21 11:51:55,http://reynolds.com/antonia.heathcote,,121.241.196.125,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n369,1,9,2017-05-26 14:20:13,http://zboncak.io/marilou,,157.28.57.81,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n21432,8,481,2017-06-12 23:14:12,http://wilkinson.name/marlene,0.9886218830,184.102.230.81,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n21433,8,481,2017-01-14 07:08:56,http://bradtkehills.com/kevon_hammes,0.7291856400,141.14.61.93,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n21434,8,481,2017-05-26 09:41:25,http://uptonhuels.org/destin.wiegand,0.6188809043,252.213.232.3,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n21435,8,481,2017-05-08 20:07:01,http://okeefe.io/kelsi_farrell,0.7639908095,186.130.88.81,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n21436,8,481,2017-02-04 17:04:41,http://miller.biz/meda,0.9347658989,147.187.29.37,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n21437,8,481,2017-04-26 03:56:12,http://douglas.com/wilson.bergstrom,0.7043313229,116.162.65.132,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n21438,8,481,2017-04-03 11:46:10,http://schimmelgrant.com/tillman,0.3221038526,87.133.175.145,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n21439,8,481,2017-03-08 07:33:30,http://eichmann.name/abe,0.3734542282,45.123.219.183,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n21440,8,481,2017-02-24 05:29:55,http://ondricka.info/guadalupe.reichert,0.3015158155,168.253.232.144,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n21441,8,481,2017-02-25 04:09:21,http://murraycrona.info/toy,0.5603720194,98.235.175.161,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n21442,8,481,2017-02-19 02:26:50,http://schimmel.org/jeremy,0.0732232134,129.167.7.147,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n21443,8,481,2017-02-15 08:08:23,http://roberts.com/deja_ruel,0.2408559357,35.149.3.211,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n21444,8,481,2017-04-22 05:54:57,http://gutmann.net/anastasia,0.3571706817,148.27.13.64,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n21445,8,481,2017-01-02 16:23:58,http://eichmann.info/andre.cummerata,0.2993549872,152.252.129.36,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n21446,8,481,2017-03-30 03:27:42,http://brown.net/kimberly,0.5150267908,239.185.86.71,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n21447,8,481,2017-03-02 07:35:41,http://keelinghills.net/adelle.friesen,0.1981348953,172.176.121.188,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n21448,8,481,2017-04-27 08:17:50,http://schuster.com/ignacio,0.4544798659,14.161.180.45,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n21449,8,481,2017-01-03 07:09:09,http://moore.io/elise_dare,0.1179635025,221.238.51.202,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n21450,8,481,2016-12-21 16:31:14,http://will.co/elfrieda,0.1849310077,69.157.99.240,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n21451,8,481,2016-12-22 22:07:07,http://considine.net/hayley,0.2721319269,174.9.25.48,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n21452,8,481,2017-03-11 09:59:56,http://will.name/golda,0.7751345135,160.37.119.147,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n21453,8,481,2017-01-31 11:41:12,http://wardthiel.io/daron_boyle,0.7514174666,178.98.49.155,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n21454,8,481,2017-03-19 14:11:10,http://gusikowski.biz/felicity_wintheiser,0.6742491810,98.133.237.176,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n21455,8,481,2017-06-06 00:38:33,http://macejkovic.com/georgianna,0.7163235018,188.149.80.237,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n21456,8,481,2017-06-05 18:47:00,http://cain.info/arturo_streich,0.6110775519,191.209.228.129,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n21457,8,481,2017-04-18 22:05:47,http://maggiokozey.biz/henriette,0.3905458048,87.221.11.69,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n21458,8,481,2017-05-15 05:22:14,http://mitchell.info/chaim,0.1174803264,3.234.84.54,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n21459,8,481,2017-01-27 00:30:55,http://turner.co/wellington.bernhard,0.6511589697,252.48.7.197,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n21460,8,481,2017-02-21 03:55:26,http://prohaska.io/carlie,0.2442224826,228.35.169.167,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n21461,8,481,2017-03-14 01:22:15,http://millsleffler.co/floie,0.1840687797,15.149.185.43,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n21462,8,481,2017-02-21 07:06:07,http://berge.net/gertrude,0.8733437662,46.156.211.145,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n21463,8,481,2016-12-19 20:09:19,http://dickicummings.info/dawn,0.6164390918,14.36.86.249,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n21464,8,481,2016-12-15 19:44:29,http://bogisich.co/ola,0.3511605423,189.64.202.181,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n21465,8,481,2017-05-07 14:47:03,http://jenkins.name/odell_kris,0.1667912614,227.143.221.38,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n21466,8,481,2017-02-07 00:31:20,http://yundt.org/bennie.beer,0.3253178400,150.78.152.174,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n21467,8,481,2017-03-31 15:57:28,http://morar.co/vincenzo,0.5652852570,48.86.49.95,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n21468,8,481,2016-12-13 11:53:58,http://hintzwilderman.name/grant.boehm,0.6908976528,221.250.121.199,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n21469,8,481,2017-01-08 02:40:51,http://yosttorp.name/marisa.ritchie,0.0426148595,146.85.159.244,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n21470,8,481,2017-03-20 05:43:03,http://langworth.org/georgiana,0.0647868743,149.232.132.57,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n21471,8,481,2017-04-02 07:15:05,http://gutkowskibeahan.biz/gloria,0.9133948881,33.13.133.166,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n21472,8,481,2017-04-22 02:41:02,http://hane.org/yasmin_ward,0.7600258831,218.52.93.14,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n21473,8,481,2017-03-24 07:56:37,http://torphy.com/patsy_nitzsche,0.2884882990,27.227.120.92,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n21474,8,481,2017-03-22 21:54:50,http://rodriguez.biz/javier,0.3855151322,197.199.225.201,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n21475,8,481,2017-02-04 22:58:35,http://funk.net/gideon.frami,0.3410855172,147.57.234.135,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n21476,8,481,2016-12-27 16:58:16,http://huelskovacek.org/matt,0.7142886923,149.48.247.98,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n21477,8,481,2017-01-19 01:39:11,http://johns.org/janiya.bauch,0.5388145250,229.150.10.235,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n21478,8,481,2017-02-14 06:18:30,http://adams.biz/emilio,0.9663540736,36.113.42.69,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n21479,8,481,2017-05-26 18:24:26,http://strosindubuque.com/adelbert,0.0753806490,15.254.59.75,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n21480,8,481,2017-05-09 11:01:49,http://daugherty.net/kyle,0.7264118568,17.178.107.53,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n21481,8,481,2016-12-14 19:04:24,http://tremblaystehr.net/janick,0.8361220957,174.30.49.241,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n21482,8,482,2017-03-06 02:51:14,http://kunze.co/candelario.ko,0.4896124765,92.46.58.110,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n21483,8,482,2017-03-04 14:30:29,http://haley.org/vickie,0.9298252150,45.58.47.33,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n370,1,9,2017-03-09 04:23:48,http://ebert.biz/emil_harris,,42.62.58.11,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n371,1,9,2017-01-03 19:34:19,http://kuhiccummings.io/ashlee.quitzon,,111.245.85.209,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n372,1,9,2017-06-07 14:48:18,http://schiller.net/ayla,,156.189.55.192,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n373,1,9,2016-12-17 11:48:21,http://spencer.biz/rodrick,,158.176.208.80,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n374,1,9,2017-04-12 14:30:07,http://wizaheathcote.name/maximus,,10.218.34.162,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n375,1,9,2016-12-26 03:26:57,http://bogisichwilliamson.info/kellie.nienow,,213.135.227.69,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n376,1,9,2017-05-21 18:16:17,http://keebler.co/lula,,189.42.82.216,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n377,1,9,2017-02-26 07:54:58,http://maggio.info/wilhelm,,13.104.3.223,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n378,1,9,2017-03-09 06:33:00,http://paucek.io/aisha,,129.102.217.171,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n379,1,9,2017-03-31 12:57:54,http://lubowitzhaley.com/lottie,,152.40.31.36,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n380,1,9,2016-12-14 01:52:12,http://stokes.biz/caidy.bernier,,48.219.137.149,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n381,1,9,2017-02-02 17:42:17,http://wehner.net/taya.adams,,168.239.219.55,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n382,1,9,2017-05-19 05:01:22,http://schamberger.org/albin.johnson,,12.60.7.60,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n383,1,9,2017-04-17 16:09:37,http://schuppe.org/robb,,165.219.209.120,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n384,1,9,2017-05-24 08:09:48,http://kulas.co/deonte,,196.225.67.94,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n385,1,9,2017-01-06 16:53:35,http://gottlieb.co/tony,,96.90.31.154,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n386,1,9,2017-01-19 11:40:32,http://hintz.net/ettie,,77.195.241.218,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n387,1,9,2017-04-01 18:31:52,http://boehm.net/lacey_pollich,,166.191.163.242,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n388,1,9,2017-06-04 08:17:11,http://considine.co/dangelo,,99.131.121.248,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n389,1,9,2017-02-10 19:33:15,http://bashirianjast.net/joel_jast,,72.118.5.192,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n390,1,9,2017-01-31 03:26:15,http://pagac.co/darrion_damore,,249.48.119.77,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n391,1,9,2017-02-24 15:17:00,http://kling.biz/laura,,137.112.49.151,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n392,1,9,2017-06-10 15:41:13,http://heathcote.co/vivien,,181.186.120.73,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n393,1,9,2016-12-13 06:27:25,http://altenwerth.org/abbey.botsford,,211.194.44.172,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n394,1,9,2017-05-01 04:35:15,http://altenwerth.biz/layla.jerde,,165.79.104.156,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n395,1,9,2017-03-18 05:59:54,http://watsicablanda.biz/eulah,,184.89.172.77,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n396,1,9,2017-01-22 13:40:34,http://wolf.io/ronny.swift,,35.53.248.124,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n397,1,9,2017-01-23 15:11:50,http://boyle.name/orlando_sauer,,166.8.163.27,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n398,1,9,2017-06-04 06:42:40,http://hansen.org/summer.kaulke,,197.91.193.64,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n399,1,9,2017-03-20 10:04:55,http://cain.com/anastacio_heathcote,,65.110.40.100,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n400,1,10,2017-02-28 12:56:30,http://weber.net/nasir,,36.196.102.87,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n401,1,10,2017-02-16 11:44:02,http://ferrycormier.info/kaya.harris,,68.145.84.240,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n402,1,10,2016-12-21 21:34:26,http://emard.org/aglae.jenkins,,200.223.114.241,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n403,1,10,2017-05-13 08:11:37,http://bergstrom.name/yeenia,,195.200.132.244,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n404,1,10,2017-02-23 03:52:21,http://king.net/edwina,,124.152.162.119,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n405,1,10,2017-05-11 21:43:14,http://daredickens.io/lina.bode,,57.155.245.154,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n406,1,10,2017-03-09 04:02:02,http://mueller.io/benton,,147.19.223.94,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n407,1,10,2017-05-30 16:13:17,http://gaylordconn.info/mitchell,,125.144.122.228,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n408,1,10,2017-05-03 07:23:29,http://toy.net/carroll,,187.137.136.251,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n409,1,10,2016-12-17 05:25:27,http://bechtelar.net/iva_ferry,,249.133.137.221,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n410,1,10,2017-03-20 01:25:34,http://herzog.com/rosemary,,178.168.16.121,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n411,1,10,2017-02-14 15:02:28,http://cummerata.biz/caandra_schuster,,81.11.12.35,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n412,1,10,2017-05-19 18:52:45,http://nienowbergnaum.co/burley,,50.60.179.168,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n413,1,10,2017-02-06 22:06:32,http://lebsack.info/armando_bergnaum,,82.122.182.164,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n414,1,10,2017-03-07 13:12:55,http://rogahnlang.info/zella,,169.100.165.128,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n415,1,10,2017-02-05 20:38:28,http://schinner.name/andy,,16.143.30.224,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n416,1,10,2017-06-08 19:14:28,http://barton.name/larry,,147.116.206.189,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n417,1,10,2017-05-13 04:01:44,http://damoredach.io/hayley,,29.167.81.226,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n418,1,10,2017-04-05 22:11:38,http://torphyokon.biz/amiya.strosin,,86.94.104.115,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n419,1,10,2017-03-26 12:51:11,http://zemlak.net/amir,,11.130.113.197,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n420,1,10,2017-01-18 01:09:39,http://hills.net/araceli.dicki,,12.176.42.101,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n421,1,10,2017-05-04 06:00:14,http://vandervortmoriette.io/shaylee,,192.193.37.57,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n422,1,10,2017-03-01 14:13:46,http://kulas.io/paula_damore,,22.92.235.108,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n423,1,10,2017-02-26 16:38:45,http://hilll.name/wyman,,254.67.166.26,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n424,1,11,2017-05-02 23:57:24,http://kreiger.org/maribel,,37.62.198.213,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n425,1,11,2017-05-05 07:00:31,http://mrazgrady.info/maribel,,202.219.166.111,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n426,1,11,2016-12-23 03:00:41,http://corkery.io/taurean_feeney,,136.188.124.12,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n21484,8,482,2017-01-30 06:53:27,http://hettinger.co/kaden,0.7178791824,88.23.249.218,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n21485,8,482,2017-01-31 12:14:27,http://lehner.name/morton,0.6719325213,27.78.48.248,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n21486,8,482,2017-04-28 02:23:58,http://west.biz/cleve.champlin,0.7535044646,186.127.228.108,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n21487,8,482,2017-04-19 17:55:53,http://gibsonmraz.co/felton.ko,0.4696008498,205.85.121.163,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n21488,8,482,2017-01-15 14:54:16,http://beahan.com/laisha_harber,0.9719216658,45.251.26.249,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n21489,8,482,2017-02-27 08:54:03,http://crooks.biz/ines,0.9033331923,135.72.23.210,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n21490,8,482,2017-04-23 05:25:46,http://schuster.org/earnest.larson,0.9344914871,184.58.195.139,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n21491,8,482,2017-03-30 23:26:03,http://stark.com/janick_stiedemann,0.0454663942,142.60.60.79,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n21492,8,482,2017-01-12 20:28:25,http://medhurst.name/adella.bahringer,0.0176105087,144.233.252.168,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n21493,8,482,2017-03-30 13:37:41,http://reynolds.com/luisa_legros,0.9266801049,165.150.48.110,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n21494,8,482,2017-02-12 06:22:18,http://krajcikvandervort.name/stanton_glover,0.1943651596,134.140.40.19,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n21495,8,482,2016-12-27 03:10:39,http://becker.com/patrick,0.6387942446,27.254.153.252,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n21496,8,482,2016-12-29 20:39:09,http://hand.net/kira_nolan,0.3213341194,66.236.239.52,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n21497,8,482,2017-06-13 18:47:11,http://conn.com/jeanette,0.2766003865,158.20.151.238,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n21498,8,482,2017-06-01 06:29:30,http://barrows.org/kathleen,0.2376837547,230.153.40.134,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n21499,8,482,2017-02-20 12:57:06,http://donnelly.co/dangelo,0.2366547163,52.87.141.153,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n21500,8,482,2017-05-10 07:50:32,http://haleymayer.co/craig_labadie,0.7644014664,139.253.72.122,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n21501,8,482,2017-06-04 21:26:19,http://haagbartoletti.info/lourdes,0.8036379858,90.168.50.195,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n21502,8,482,2017-03-31 05:38:48,http://fahey.net/christopher,0.4014651886,173.71.151.152,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n21503,8,482,2017-04-05 09:29:15,http://gloverbode.com/theresa_kulas,0.5257911523,182.87.40.212,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n21504,8,482,2017-06-02 17:42:58,http://quigley.org/giuseppe,0.7543192362,130.110.178.56,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n21505,8,482,2017-04-09 05:19:35,http://gutmannturner.info/jorge.padberg,0.6610327839,230.229.146.240,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n21506,8,483,2017-02-18 08:04:36,http://collierkemmer.io/toney.steuber,0.9974149933,110.36.249.189,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n21507,8,483,2017-01-25 00:33:21,http://tromp.name/marielle_heathcote,0.2672210832,71.212.219.106,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n21508,8,483,2017-03-11 05:03:42,http://sawaynhomenick.name/jayde_mitchell,0.3625100543,187.121.213.136,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n21509,8,483,2016-12-25 13:08:50,http://flatley.io/cleveland,0.2215867950,131.139.37.208,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n21510,8,483,2017-02-04 03:09:42,http://cristdonnelly.com/amie_aufderhar,0.1286229530,14.198.65.205,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n21511,8,483,2017-01-26 22:42:10,http://boylezulauf.biz/wilhelm.kozey,0.2615617623,220.16.164.243,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n21512,8,483,2017-03-27 14:30:32,http://kilback.org/amani_ko,0.9461744758,6.237.39.59,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n21513,8,483,2017-01-04 04:32:40,http://hettinger.co/annabell.wiegand,0.8596591504,59.135.184.31,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n21514,8,483,2017-02-24 06:29:23,http://little.biz/patricia,0.4528267387,108.21.99.155,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n21515,8,483,2017-06-11 22:08:13,http://keler.biz/sydney,0.3765745296,171.94.26.6,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n21516,8,483,2017-03-27 15:58:16,http://hermanswaniawski.com/aaliyah,0.4955941811,32.168.126.39,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n21517,8,483,2017-05-04 13:41:14,http://reynolds.biz/alena,0.5016938180,150.248.76.163,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n21518,8,483,2017-04-07 12:56:02,http://parisian.info/gina.oreilly,0.1779087053,199.196.122.217,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n21519,8,483,2017-01-09 00:03:03,http://goldner.net/lazaro_feil,0.8788739966,2.107.252.207,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n21520,8,483,2017-03-10 16:05:17,http://walsh.com/berneice,0.0699266115,252.94.82.151,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n21521,8,483,2017-05-26 11:16:40,http://volkman.info/tiana,0.6661051746,130.110.13.218,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n21522,8,483,2017-02-25 18:13:27,http://crona.com/johnnie.nolan,0.1481655402,16.68.145.97,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n21523,8,483,2017-03-05 09:35:43,http://spinkaheller.co/lenora_gibson,0.0180225109,194.190.28.160,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n21524,8,483,2017-04-16 14:02:06,http://gerlach.io/amari,0.3516609888,119.79.183.138,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n21525,8,483,2017-06-06 20:46:01,http://little.com/eliezer,0.2874764068,213.27.189.243,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n21526,8,483,2017-03-06 07:09:22,http://stehrzboncak.net/denis,0.0676005767,58.38.35.70,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n21527,8,483,2017-03-16 04:44:15,http://halvorsonhuels.com/roel,0.8203649640,43.144.254.189,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n21528,8,483,2017-03-28 09:57:01,http://zboncak.info/maverick,0.5161826924,184.47.97.86,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n21529,8,483,2017-06-12 09:40:37,http://howell.io/lea,0.2005356822,14.177.134.42,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n21530,8,483,2017-06-03 06:56:08,http://connelly.net/odell_hills,0.0896680381,126.35.17.150,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n21531,8,483,2017-01-07 03:23:33,http://walter.org/leora,0.7944767684,83.169.207.2,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n21532,8,483,2017-01-03 02:38:48,http://bernier.co/jeyca.dare,0.1029929261,30.85.116.103,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n21533,8,483,2016-12-28 17:50:04,http://lynch.io/vernon,0.2413923266,248.77.35.70,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n21534,8,483,2017-02-07 06:49:39,http://marvinkunde.io/judd,0.8286400413,245.152.44.251,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n427,1,11,2017-01-25 04:46:47,http://halvorson.co/lavonne_feest,,160.130.186.212,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n428,1,11,2017-02-21 20:17:34,http://feest.info/alycia.schuster,,236.31.219.124,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n429,1,11,2017-06-02 22:10:17,http://medhurst.net/abagail.gerhold,,65.99.122.171,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n430,1,11,2017-05-31 06:32:47,http://reilly.name/floyd,,162.220.218.200,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n431,1,11,2017-03-27 14:59:54,http://swift.co/magdalena_cronin,,82.201.250.32,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n432,1,11,2016-12-29 07:49:14,http://morar.com/hal.towne,,32.189.130.187,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n433,1,11,2017-04-24 16:16:11,http://runolfon.io/theresa,,93.129.128.39,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n434,1,11,2017-01-05 10:14:34,http://heidenreich.com/vanea,,163.154.107.214,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n435,1,11,2017-05-27 16:07:12,http://cruickshankglover.info/arvilla.schaden,,249.226.33.211,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n436,1,11,2017-01-04 03:33:43,http://wuckertkling.name/sally,,220.114.174.192,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n437,1,11,2017-01-11 04:32:49,http://gottlieb.net/della_willms,,124.64.197.182,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n438,1,11,2017-04-30 08:27:57,http://roberts.com/micaela_cronin,,240.235.251.182,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n440,1,11,2017-03-17 08:22:40,http://ritchie.name/cornell,,178.106.71.42,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n441,1,11,2017-02-22 05:18:20,http://larkinebert.name/elian,,237.248.211.187,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n442,1,11,2016-12-25 10:22:31,http://reinger.name/geoffrey.ankunding,,45.99.124.85,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n443,1,11,2017-02-03 14:18:10,http://carroll.org/aleia,,76.84.230.187,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n444,1,11,2017-04-26 23:27:40,http://priceerdman.net/camille_dach,,190.240.176.67,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n445,1,11,2017-06-05 21:00:50,http://carter.info/zander.raynor,,49.207.60.124,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n446,1,11,2017-06-01 02:11:02,http://lueilwitzbernier.net/berta.welch,,222.200.162.129,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n447,1,11,2017-02-09 01:14:29,http://trantow.com/danial,,226.104.35.83,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n448,1,11,2017-03-19 07:49:40,http://brown.co/braeden.robel,,243.239.49.214,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n449,1,11,2017-01-25 04:03:56,http://shields.co/dandre.powlowski,,206.94.236.158,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n450,1,11,2017-02-19 04:38:59,http://hettinger.com/angel,,202.117.45.38,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n451,1,11,2017-03-28 17:51:23,http://predovic.com/jeffry,,46.172.72.131,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n452,1,11,2017-02-22 09:04:40,http://jonesgulgowski.org/soledad_dickinson,,87.194.5.119,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n453,1,11,2017-06-05 19:59:50,http://romaguerahaley.co/fabiola_thompson,,206.24.113.74,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n454,1,11,2017-01-20 23:11:51,http://gulgowski.com/lincoln.simonis,,158.148.19.68,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n455,1,11,2017-05-11 11:56:54,http://gottlieb.co/jett.willms,,194.157.188.209,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n456,1,11,2017-03-27 10:59:22,http://krajcik.com/daniella.altenwerth,,76.108.148.112,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n457,1,11,2017-01-10 07:21:49,http://hintzjaskolski.io/kevin,,234.67.164.82,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n458,1,11,2017-03-20 13:36:46,http://schoenzieme.com/roselyn,,72.211.4.71,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n459,1,11,2017-01-07 20:15:50,http://sanford.co/mattie_kiehn,,74.119.182.242,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n460,1,11,2017-05-27 14:14:42,http://goyette.com/naomie,,50.229.211.128,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n461,1,11,2017-05-20 07:23:32,http://hintz.net/orlando,,121.19.115.28,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n462,1,11,2017-05-13 06:33:17,http://murrayparisian.co/keshaun,,155.52.68.217,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n463,1,11,2017-01-12 14:30:21,http://schmeler.org/ania_purdy,,65.183.102.88,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n464,1,11,2017-06-06 14:32:33,http://zulauf.co/kaleigh,,36.104.237.195,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n465,1,11,2017-02-06 07:26:46,http://rodriguez.biz/andreane,,235.157.40.79,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n466,1,11,2017-02-21 08:05:58,http://schulist.biz/victor,,99.6.217.197,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n467,1,12,2017-03-13 15:21:17,http://pollich.biz/jasen,,76.161.212.173,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n468,1,12,2017-02-19 14:23:31,http://bayererdman.io/carol_leannon,,146.115.120.184,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n469,1,12,2017-01-16 09:37:44,http://hettinger.org/leonie,,123.169.110.75,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n470,1,12,2017-02-10 06:13:02,http://wunsch.org/savanah_purdy,,221.7.250.37,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n471,1,12,2017-05-16 21:09:36,http://tromp.biz/sophie.pfeffer,,182.87.99.224,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n472,1,12,2017-05-18 00:11:36,http://rice.net/elmira_gorczany,,220.209.214.124,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n473,1,12,2017-05-26 09:32:47,http://larkin.net/ludwig_lesch,,105.109.93.203,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n474,1,12,2016-12-17 11:07:55,http://muller.org/susanna,,15.126.71.232,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n475,1,12,2017-04-29 16:58:49,http://hermiston.com/brent.haag,,103.73.204.105,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n476,1,12,2017-04-01 05:29:13,http://jast.biz/alexzander,,212.200.19.29,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n477,1,12,2016-12-29 20:52:54,http://medhurst.net/otto.gorczany,,234.81.30.230,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n478,1,12,2017-05-10 15:16:50,http://prosacco.com/sean,,132.252.152.28,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n479,1,12,2017-04-05 15:06:12,http://mueller.com/skylar,,171.186.66.159,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n480,1,12,2016-12-22 06:03:27,http://towneko.org/godfrey,,147.116.215.112,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n481,1,12,2017-02-03 20:43:43,http://gradyweinat.biz/juanita_ernser,,172.103.23.125,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n482,1,12,2017-04-22 12:23:00,http://voneichmann.net/chloe,,212.122.53.48,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n483,1,12,2017-05-15 18:03:14,http://damore.net/hershel.kunze,,181.68.238.82,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n21535,8,483,2017-02-17 11:28:26,http://mohr.com/maci,0.3094113115,46.57.234.58,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n21536,8,483,2017-04-09 00:44:53,http://rowe.net/matilde,0.3833575420,57.61.227.222,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n21537,8,483,2017-01-25 13:42:07,http://waelchi.com/henderson.fahey,0.2663939183,231.126.110.64,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n21538,8,483,2016-12-17 23:59:13,http://shields.net/timmothy_cronin,0.0878990993,97.61.37.186,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n21539,8,483,2017-05-08 18:56:41,http://schneiderjenkins.biz/sigurd_kuphal,0.1059749590,53.80.206.225,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n21540,8,483,2017-05-19 09:09:17,http://mohr.biz/dulce,0.0815258681,131.123.28.145,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n21541,8,483,2017-05-12 17:37:32,http://littledonnelly.info/estelle.hermann,0.6343283852,69.44.128.112,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n21542,8,483,2017-01-18 17:25:29,http://bergnaumlemke.io/aurelie,0.5026484950,116.124.197.81,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n21543,8,483,2017-03-03 14:03:17,http://bogan.io/eunice_corkery,0.4796485543,2.116.204.19,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n21544,8,483,2017-04-13 01:32:52,http://muller.co/colt_schmidt,0.7390592173,22.106.23.42,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n21545,8,483,2017-01-02 09:08:08,http://lockman.com/blair,0.3209146559,7.229.142.195,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n21546,8,483,2017-03-17 01:43:14,http://gibson.com/estel_ullrich,0.6509608651,56.169.13.116,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n21547,8,483,2017-01-31 15:00:02,http://upton.org/ford,0.2963979536,198.137.173.251,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n21548,8,483,2017-03-30 19:18:22,http://vandervorthuels.biz/pierce_ruecker,0.4869329224,210.213.158.66,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n21549,8,483,2017-06-07 16:53:40,http://gaylord.com/chaz,0.1573323542,97.192.165.188,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n21550,8,483,2017-02-09 12:22:30,http://mooreankunding.io/katelynn.schuster,0.5803675401,244.181.17.231,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n21551,8,483,2017-02-05 01:50:16,http://kingfisher.com/jerald.ernser,0.2264575109,216.163.212.78,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n21552,8,483,2017-03-28 14:31:04,http://mertz.info/liam_nikolaus,0.9501513137,14.248.16.134,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n21553,8,483,2017-01-27 14:03:46,http://crist.co/doug,0.6245208895,85.224.144.91,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n21554,8,483,2017-06-09 04:57:46,http://nicolas.org/ena,0.8245218897,74.125.33.224,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n21555,8,483,2017-03-20 05:58:54,http://turcottebernhard.name/velva,0.0176142334,11.139.79.174,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n21556,8,483,2017-03-13 02:52:33,http://adams.io/damion,0.9458715898,225.51.96.53,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n21557,8,483,2017-03-08 19:37:14,http://connelly.co/davin.swift,0.8194350362,15.177.53.90,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n21558,8,483,2017-03-03 05:34:11,http://gleichner.org/tillman.deckow,0.9939032162,201.60.111.171,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n21559,8,483,2017-05-26 12:47:09,http://waelchismitham.biz/zelma_beier,0.2913414492,100.67.35.135,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n21560,8,483,2017-05-25 10:10:55,http://huels.biz/clemens,0.4868187719,79.6.122.204,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n21561,8,483,2017-06-01 10:30:35,http://runte.com/merlin.yundt,0.4837693222,110.132.132.99,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n21562,8,483,2017-05-05 01:58:28,http://franecki.com/felton,0.3558535016,86.210.254.115,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n21563,8,483,2016-12-30 08:40:51,http://starkgreenholt.biz/kiara_connelly,0.7149379372,97.63.168.166,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n21564,8,483,2017-01-14 21:35:05,http://schroeder.com/aleia.schaden,0.0954321972,217.163.112.236,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n21565,8,483,2017-04-29 15:26:38,http://hirthe.co/nico,0.0882193261,207.180.195.146,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n21566,8,483,2017-06-12 16:14:34,http://mitchell.info/emilio,0.3811874693,57.190.204.217,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n21567,8,483,2017-01-15 02:00:02,http://adams.biz/jude,0.1078667972,81.13.168.109,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n21568,8,484,2017-02-15 09:59:44,http://west.net/deja,0.0188688845,104.188.185.43,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n21569,8,484,2017-01-26 16:38:12,http://naderhuels.org/salma,0.8667917530,196.106.185.93,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n21570,8,484,2017-04-13 11:38:51,http://yost.info/mathias,0.5805389507,194.223.148.51,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n21571,8,484,2017-03-12 08:22:39,http://funktrantow.info/river_thompson,0.6420174041,227.15.6.103,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n21572,8,484,2017-06-05 01:51:47,http://trompdibbert.co/anastasia,0.5762105746,76.186.181.159,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n21573,8,484,2017-03-15 00:03:41,http://lebsack.net/santino.jast,0.6704369060,117.59.188.84,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n21574,8,484,2016-12-31 01:39:58,http://wehnerlebsack.com/alison_gleason,0.8877749046,64.204.178.136,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n21575,8,484,2017-05-19 04:24:51,http://jastschuster.info/ricky.miller,0.0282905050,87.9.171.159,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n21576,8,484,2017-06-01 09:06:00,http://morar.info/annabelle,0.5676427342,39.37.223.28,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n21577,8,484,2017-03-14 09:44:28,http://feest.name/zella,0.9756496154,241.205.39.163,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n21578,8,484,2017-02-27 04:18:56,http://hand.biz/daron,0.3420753223,160.195.72.172,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n21579,8,484,2017-01-17 11:37:07,http://christiansen.info/santa_cummings,0.7848428843,154.241.167.151,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n21580,8,484,2017-01-30 18:58:03,http://bartell.name/cale,0.1974509826,202.238.129.187,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n21581,8,484,2017-03-01 22:48:25,http://ankundingstamm.name/madison_batz,0.0665106420,248.137.87.103,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n21582,8,484,2017-03-25 00:18:01,http://bruen.name/lysanne.king,0.8685346822,5.47.229.224,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n21583,8,484,2017-04-18 19:28:47,http://dickinsonvolkman.io/ardith,0.9301972329,202.81.68.250,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n21584,8,484,2016-12-15 16:33:09,http://prosacco.info/te_dickinson,0.4374744069,196.55.79.221,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n21585,8,484,2017-01-22 23:39:52,http://mertz.biz/dasia.nitzsche,0.3650212279,4.74.124.44,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n484,1,12,2016-12-14 15:01:06,http://stark.name/drew_senger,,157.2.253.233,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n485,1,12,2017-02-09 19:14:12,http://swaniawski.biz/penelope.metz,,128.154.193.94,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n486,1,12,2017-05-29 01:14:44,http://funk.name/casandra.dickens,,237.57.241.226,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n487,1,12,2017-02-16 04:01:42,http://strackerogahn.io/alec,,239.196.63.184,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n488,1,12,2017-05-14 21:11:17,http://monahan.info/emery_batz,,65.236.231.138,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n489,1,12,2017-02-20 11:36:15,http://west.name/destiny,,117.83.166.38,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n490,1,12,2017-06-04 23:05:33,http://raynor.co/arch_keeling,,91.245.107.189,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n491,1,12,2016-12-29 06:40:13,http://block.info/kaylin.nolan,,163.244.205.143,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n492,1,12,2016-12-27 09:36:00,http://kihn.name/alexandro,,43.157.137.143,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n493,1,12,2017-05-31 16:18:13,http://cole.name/greg,,252.214.116.251,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n494,1,12,2017-05-16 03:12:36,http://parisian.biz/reginald,,89.249.168.95,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n495,1,12,2017-01-15 18:29:18,http://kulaspacocha.net/rosalyn.ko,,72.252.86.197,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n496,1,12,2017-03-05 08:51:54,http://reillyeichmann.biz/will,,217.216.188.161,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n497,1,12,2017-01-15 16:19:39,http://abbott.io/columbus,,233.148.42.6,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n498,1,12,2017-06-07 08:57:07,http://hand.biz/luigi_dare,,117.187.7.13,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n499,1,12,2016-12-22 16:38:31,http://tillman.co/valerie.jacobson,,80.133.105.45,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n500,1,12,2017-01-12 18:14:32,http://mcclure.biz/therese.greenfelder,,4.169.197.203,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n501,1,12,2017-03-08 21:53:57,http://zboncakhowell.biz/deron_erdman,,165.174.110.138,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n502,1,12,2017-02-07 13:57:39,http://kuhlman.io/nicolas,,14.186.159.254,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n503,1,12,2017-02-28 23:26:17,http://lynchaufderhar.com/jayden,,31.9.121.66,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n504,1,12,2017-05-02 14:41:36,http://maggio.biz/salvador,,135.55.200.21,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n505,1,12,2017-01-17 04:51:03,http://ledner.net/leo,,219.145.104.94,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n506,1,12,2017-01-23 20:51:51,http://kunde.com/frida.herman,,150.105.187.40,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n507,1,12,2017-02-18 12:37:47,http://kihnpagac.info/michelle,,49.246.185.62,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n508,1,12,2017-01-19 07:11:26,http://jacobi.co/jorge.walter,,34.131.97.72,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n509,1,12,2017-03-27 00:44:48,http://predovicboyer.info/deanna.lueilwitz,,39.250.150.14,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n510,1,12,2016-12-17 18:30:41,http://hermannpurdy.co/deonte_schneider,,90.94.179.17,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n511,1,12,2017-01-27 17:59:33,http://mayertveum.net/joel,,180.226.152.59,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n512,1,12,2017-03-28 05:36:38,http://breitenberg.name/sonny_toy,,75.111.71.42,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n513,1,13,2017-04-08 11:07:46,http://bogisichupton.io/jalyn_becker,,223.92.113.163,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n514,1,13,2016-12-20 20:37:05,http://beerharvey.net/otho.green,,136.131.56.188,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n515,1,13,2017-01-05 05:46:08,http://reynolds.net/kirk_roberts,,235.231.120.52,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n516,1,13,2017-01-05 06:17:48,http://uptonschamberger.net/ines_tromp,,72.246.34.173,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n517,1,13,2017-05-02 02:01:27,http://barrows.info/meggie.cummings,,248.127.144.103,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n518,1,13,2017-05-11 09:33:27,http://davisrowe.biz/myah.okon,,14.221.34.85,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n519,1,13,2017-03-26 07:54:06,http://schambergerhilll.biz/anabel_wuckert,,155.2.138.41,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n520,1,13,2017-05-02 22:28:42,http://ohara.name/thalia,,34.215.94.233,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n521,1,13,2017-01-28 13:21:19,http://kerluke.com/khalid,,196.226.101.185,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n522,1,13,2017-03-30 00:23:15,http://eichmanncartwright.name/claud.wunsch,,114.3.165.140,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n523,1,13,2017-02-19 20:59:55,http://ziemebode.org/pierce_schoen,,19.240.114.57,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n524,1,13,2017-01-14 09:31:29,http://lueilwitz.org/kenny,,40.218.151.83,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n525,1,13,2016-12-13 15:02:44,http://willfadel.com/elyse,,49.145.31.127,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n526,1,13,2017-05-27 15:39:29,http://moriettekautzer.biz/sister,,2.170.154.231,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n527,1,13,2017-05-17 01:02:51,http://lubowitz.com/cleta.raynor,,63.116.202.101,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n528,1,13,2017-02-19 03:46:51,http://gislasonhand.co/twila,,64.144.116.213,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n529,1,13,2017-03-21 02:42:17,http://barrows.biz/chelsea,,241.119.225.38,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n530,1,13,2016-12-16 03:26:15,http://fisherfunk.net/pansy,,176.140.76.181,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n531,1,13,2017-04-24 08:19:32,http://littel.io/asa,,111.117.12.99,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n532,1,13,2017-03-05 04:56:51,http://mcclure.name/kaleb,,130.203.254.128,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n533,1,13,2017-01-25 05:45:23,http://kunde.net/quincy_collier,,163.134.206.253,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n534,1,13,2017-02-21 03:17:24,http://gradywiza.co/heath,,223.233.177.73,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n535,1,13,2017-02-02 18:49:14,http://heaney.org/alene,,203.158.111.152,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n536,1,13,2017-05-22 23:23:53,http://boehmbode.com/raegan,,102.249.20.91,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n537,1,13,2017-06-12 22:39:56,http://bartolettibotsford.com/wilhelmine_mante,,160.141.210.240,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n538,1,13,2017-04-17 00:16:07,http://hane.info/darrell.doyle,,165.219.39.78,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n539,1,13,2017-01-10 05:23:05,http://parisian.co/bert_tremblay,,21.58.253.96,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n540,1,13,2017-04-10 08:15:00,http://harris.co/reina_ward,,10.86.153.198,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n21586,8,484,2017-02-20 12:06:59,http://white.io/gay.lockman,0.2734629959,171.169.219.110,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n21587,8,484,2017-03-06 11:04:52,http://white.io/jerry,0.0779409816,189.227.205.225,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n21588,8,484,2017-03-17 16:17:37,http://durgan.name/maybell,0.7921279125,239.178.146.73,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n21589,8,484,2017-03-19 04:13:24,http://pfeffer.info/lazaro_koch,0.0467860414,19.251.168.102,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n21590,8,484,2017-01-05 00:39:41,http://swift.io/richard,0.7760720083,246.176.65.79,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n21591,8,484,2017-03-13 05:36:59,http://predovic.info/antonio,0.5004459570,22.60.44.24,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n21592,8,484,2017-03-01 03:57:25,http://keeling.com/laurianne.gibson,0.0362908190,142.2.105.194,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n21593,8,484,2017-03-21 06:42:02,http://mante.co/angelo,0.7315154722,101.231.122.42,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n21594,8,484,2017-06-02 03:29:00,http://schowalter.info/estelle.bayer,0.6463994151,233.173.182.123,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n21595,8,484,2017-03-13 14:23:59,http://waelchi.name/alivia.lueilwitz,0.4544171579,47.148.138.207,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n21596,8,484,2017-02-07 05:46:38,http://schowalter.co/chloe.monahan,0.6220413188,122.25.244.192,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n21597,8,484,2017-03-05 10:26:09,http://haleylind.info/marisol_macejkovic,0.5646170562,239.128.232.101,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n21598,8,484,2016-12-18 22:16:16,http://tillman.org/amara,0.9690606315,23.45.137.11,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n21599,8,484,2017-04-13 18:49:14,http://hermann.info/johan.torp,0.0483498652,98.135.40.197,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n21600,8,484,2017-04-20 08:25:27,http://prohaska.net/stephania,0.5975067518,116.83.106.83,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n21601,8,484,2017-04-23 22:29:52,http://stehrcasper.co/natasha_baumbach,0.0716983431,68.134.239.18,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n21602,8,484,2016-12-14 16:58:36,http://kovacek.io/dan,0.1845611253,31.207.110.12,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n21603,8,484,2017-02-25 06:10:37,http://weimann.biz/virgil,0.8175192608,130.155.127.58,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n21604,8,484,2016-12-16 02:16:46,http://harris.info/magdalena,0.6847217847,51.38.220.193,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n21605,8,484,2017-03-24 16:29:28,http://sawaynwolf.com/jewell,0.9826445921,120.234.242.222,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n21606,8,484,2016-12-18 16:09:18,http://mosciski.org/theodora_pagac,0.4696616243,42.108.204.214,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n21607,8,484,2017-06-06 22:21:15,http://mueller.name/loyal,0.4831435599,234.17.57.162,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n21608,8,484,2017-04-15 13:12:54,http://mosciski.com/oda.lubowitz,0.5449272392,13.90.224.37,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n21609,8,484,2017-02-12 14:29:32,http://mann.biz/joanne_johns,0.0019654533,94.233.136.165,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n21610,8,484,2017-03-18 19:24:48,http://ferry.org/gonzalo,0.5114070107,144.164.59.136,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n21611,8,484,2017-03-17 11:03:29,http://boyer.biz/robb,0.7450941602,97.142.108.234,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n21612,8,484,2017-04-01 18:05:26,http://nitzsche.info/estefania.lueilwitz,0.2849088718,127.6.163.64,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n21613,8,484,2017-03-26 08:15:01,http://tillman.com/ashton_bashirian,0.3251828854,89.54.182.225,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n21614,8,484,2017-01-18 22:02:01,http://hamill.com/ellie_armstrong,0.5517774293,192.60.176.122,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n21615,8,484,2016-12-28 05:25:44,http://gusikowski.io/ara.price,0.2914705297,103.49.240.19,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n21616,8,484,2017-06-05 23:05:45,http://tillmanolson.biz/marcia_koelpin,0.4992658872,25.126.229.69,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n21617,8,484,2017-05-11 23:16:30,http://west.info/michale.hackett,0.8208222337,168.214.174.152,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n21618,8,484,2017-01-29 21:25:33,http://breitenbergmaggio.info/jeika,0.8046817819,74.68.54.51,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n21619,8,484,2017-04-26 22:51:52,http://heaney.info/ayla,0.6185983150,251.151.138.213,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n21620,8,484,2017-06-05 14:01:01,http://bednar.com/bert,0.7613567955,223.71.5.206,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n21621,8,484,2017-02-25 12:23:48,http://halvorson.io/rachelle,0.5487564928,23.39.156.198,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n21622,8,484,2017-02-14 23:30:10,http://muller.co/abel.johnson,0.2036564900,239.194.174.33,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n21623,8,484,2016-12-30 04:15:57,http://ortiz.net/arielle_heaney,0.2305168868,79.181.143.226,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n21624,8,484,2017-04-10 04:37:35,http://schustercrona.biz/shany,0.0110272785,74.122.185.238,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n21625,8,484,2017-04-25 08:54:21,http://white.info/lysanne,0.7857482371,42.122.185.2,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n21626,8,484,2017-02-09 13:38:24,http://rath.name/barry,0.8395812454,20.252.251.247,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n21627,8,484,2017-02-14 09:41:50,http://krisbeier.org/rickie,0.6164655833,86.204.194.241,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n21628,8,484,2017-01-13 10:49:22,http://cartwright.net/margarete,0.0375393362,120.119.22.207,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n21629,8,484,2017-03-02 12:58:22,http://ledner.co/loren_block,0.3831794422,104.222.84.232,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n21630,8,484,2017-03-05 23:22:37,http://haleyrippin.biz/liza,0.4363591519,91.236.55.95,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n21631,8,484,2016-12-29 01:57:49,http://okon.net/tania,0.6806868852,12.222.57.91,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n21632,8,484,2017-06-06 10:49:26,http://gerlachwatsica.net/maribel_huels,0.5217159783,205.177.182.6,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n21633,8,484,2017-04-19 17:57:35,http://kerlukeschuster.io/anabel.gusikowski,0.5252526615,218.99.47.132,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n21634,8,484,2017-02-10 22:11:30,http://smith.biz/zena.buckridge,0.5564113947,134.165.23.109,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n21635,8,484,2017-06-13 18:33:41,http://feeneyruecker.io/shayne.nikolaus,0.8393092014,147.95.247.189,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n21636,8,484,2017-04-20 22:40:55,http://skiles.io/roie_predovic,0.0420090142,156.170.232.167,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n541,1,13,2017-04-30 18:41:27,http://veum.io/vidal,,226.6.126.75,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n542,1,13,2017-02-05 19:36:36,http://wunsch.com/anais,,167.179.41.159,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n543,1,13,2017-02-13 14:11:43,http://littlemarvin.name/lexus,,135.131.4.218,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n544,1,13,2017-04-06 17:52:13,http://zulaufhilll.com/rupert_schoen,,169.42.154.193,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n545,1,13,2017-03-15 13:20:41,http://dietrichmoriette.com/domenic,,135.8.175.119,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n546,1,13,2017-01-10 16:42:34,http://zulaufkuhlman.io/abdul_hickle,,97.15.210.10,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n547,1,13,2017-04-30 16:24:31,http://markscrist.info/lea_cormier,,93.250.29.156,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n548,1,13,2017-03-01 17:49:47,http://keler.info/sid,,236.94.217.74,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n549,1,13,2017-03-29 06:11:42,http://cruickshankwehner.co/donavon.west,,209.136.120.80,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n550,1,13,2017-06-05 20:47:52,http://stanton.name/rigoberto,,183.239.46.159,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n551,1,13,2017-02-03 14:07:22,http://roob.info/bertram,,104.236.80.111,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n552,1,13,2017-01-15 07:52:38,http://powlowski.org/floy,,171.131.43.196,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n553,1,13,2017-02-07 06:11:53,http://windlermoore.info/roselyn,,203.32.168.121,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n554,1,13,2017-03-27 04:54:31,http://kelerheidenreich.io/pinkie,,86.101.189.107,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n555,1,13,2016-12-19 05:26:24,http://wardlegros.com/cayla,,100.2.206.179,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n556,1,13,2017-04-01 02:55:38,http://considinegerlach.name/josie,,92.29.37.200,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n557,1,13,2017-03-17 23:09:47,http://schimmel.net/olga_gleason,,53.194.85.149,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n558,1,13,2017-05-30 12:16:49,http://connelly.io/adolfo,,125.203.157.34,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n559,1,13,2017-05-17 00:34:06,http://millchoen.com/yvonne.bahringer,,64.108.234.232,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n560,1,13,2017-01-17 06:51:01,http://kemmer.com/santos_rempel,,85.227.50.187,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n561,1,13,2017-05-21 03:52:19,http://goldner.com/augusta,,175.113.113.181,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n562,1,13,2017-02-07 23:17:34,http://lockman.co/nicole,,65.211.177.182,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n563,1,13,2017-03-20 20:17:05,http://kautzer.info/addison,,238.83.136.31,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n564,1,13,2016-12-22 18:15:42,http://rippinankunding.co/keshawn,,191.51.172.248,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n565,1,13,2017-01-09 19:21:00,http://barrowsdickinson.com/lue.spinka,,215.175.48.161,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n566,1,13,2017-03-19 09:26:27,http://schaefer.io/hiram.bergnaum,,236.228.148.223,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n567,1,13,2017-04-22 15:18:37,http://zieme.biz/jayson_fay,,169.131.8.159,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n568,1,13,2017-05-15 00:03:51,http://pollich.info/deontae,,48.167.63.144,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n569,1,13,2017-01-07 11:50:23,http://quitzon.biz/edna,,122.111.204.246,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n570,1,13,2017-05-30 06:45:52,http://okeefe.org/mikayla,,24.119.66.59,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n571,1,14,2017-03-09 15:07:33,http://fay.info/miracle_berge,0.4406991709,83.144.75.221,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n572,1,14,2017-03-18 15:40:30,http://ruelgoodwin.biz/andrew.doyle,0.6641132977,92.63.41.202,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n573,1,14,2017-02-11 12:04:01,http://feeneyhansen.biz/whitney.wunsch,0.3176705880,191.2.191.33,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n574,1,14,2017-04-17 01:06:19,http://keler.org/brett,0.7011144680,90.72.166.13,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n575,1,14,2017-05-16 09:07:02,http://hodkiewiczblanda.co/fausto,0.4158055199,241.231.252.113,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n576,1,14,2017-01-08 15:34:36,http://cruickshank.info/bobbie_funk,0.8136470929,141.169.70.192,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n577,1,14,2017-05-16 09:26:56,http://raynor.co/rhiannon.gleichner,0.8325518538,82.88.163.189,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n578,1,14,2017-03-29 04:55:10,http://bogisich.name/kirk.raynor,0.6731242729,66.69.28.142,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n579,1,14,2017-05-02 01:45:19,http://wisoky.org/marjolaine_denesik,0.5551845092,4.16.78.229,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n580,1,14,2017-03-25 07:10:36,http://wuckert.co/marcella,0.8756977898,56.120.69.245,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n581,1,14,2017-06-03 09:53:41,http://gerlach.com/jaida,0.8285406228,216.239.249.207,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n582,1,14,2017-03-10 22:08:15,http://feest.biz/makayla,0.4697051394,207.135.241.165,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n583,1,14,2017-01-31 11:00:42,http://ko.co/jalen.prohaska,0.2959144758,200.56.121.138,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n584,1,14,2017-03-04 01:18:02,http://cremin.net/marcelo,0.6709620928,85.227.30.3,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n585,1,14,2017-03-06 17:34:56,http://kutch.org/rachel.cummerata,0.0095729652,215.21.216.125,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n586,1,14,2017-05-23 22:54:21,http://beckerosinski.info/hailee_fisher,0.4893956152,19.131.170.85,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n587,1,14,2016-12-22 00:08:41,http://will.biz/wendy,0.9942129581,112.219.73.105,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n588,1,14,2017-03-08 23:56:28,http://armstrong.name/hettie_mcclure,0.9834721037,242.242.180.33,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n589,1,14,2017-01-16 14:24:17,http://littel.com/hellen.keebler,0.7415430626,41.182.248.128,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n590,1,14,2017-02-13 19:56:12,http://langworth.biz/emmy_kemmer,0.7283673021,250.96.40.44,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n591,1,14,2016-12-24 18:41:17,http://kiehn.org/herta,0.6063709788,45.196.145.124,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n592,1,14,2017-01-04 13:25:56,http://doyle.org/kacie.king,0.9666986137,58.237.171.200,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n593,1,14,2017-06-04 23:12:01,http://smith.biz/evert,0.4362683140,147.212.227.15,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n594,1,14,2017-05-17 13:29:23,http://crona.org/onie_mante,0.9682104593,163.173.151.22,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n21637,8,484,2017-01-27 14:00:30,http://hettingerlegros.info/vivianne,0.7803642182,81.78.145.202,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n21638,8,485,2017-03-29 16:11:13,http://nolankihn.com/kaden_botsford,0.1790925509,235.137.76.4,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n21639,8,485,2017-06-10 12:21:14,http://conroy.name/constance_jenkins,0.8762094793,135.195.160.218,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n21640,8,485,2017-05-09 22:28:57,http://thiel.name/giovanny,0.4316531289,28.170.58.79,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n21641,8,485,2017-02-25 18:41:11,http://ullrich.com/jaron,0.0691293342,6.103.81.39,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n21642,8,485,2017-01-21 03:55:10,http://gleichner.info/jovani.buckridge,0.0137420230,78.59.85.158,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n21643,8,485,2016-12-26 16:28:19,http://glover.info/cortney.volkman,0.0777112150,222.174.196.237,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n21644,8,485,2017-04-09 15:26:12,http://stehr.co/bernhard,0.2242591705,160.149.42.136,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n21645,8,485,2017-04-18 13:38:30,http://veumkunze.biz/lucienne,0.8452676347,121.136.76.22,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n21646,8,485,2016-12-24 02:14:23,http://weinat.co/bobbie.haag,0.8515977160,210.63.158.187,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n21647,8,485,2017-03-13 07:11:33,http://treutel.org/raymundo_hilll,0.6630023985,217.59.191.230,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n21648,8,485,2017-05-01 09:18:46,http://hermanmohr.co/friedrich.trantow,0.6076692544,164.27.34.213,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n21649,8,485,2017-04-23 06:13:31,http://mcglynn.com/ulices,0.2639555470,250.24.144.75,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n21650,8,485,2017-02-05 17:56:21,http://wehner.org/stephon,0.4962845743,196.213.227.233,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n21651,8,485,2016-12-13 08:04:15,http://barton.biz/merritt,0.7308054826,99.97.183.11,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n21652,8,485,2016-12-13 19:58:59,http://kochlangworth.name/elmo,0.8461257829,160.149.68.213,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n21653,8,485,2017-01-03 20:08:08,http://lang.com/wayne.corkery,0.2042719624,125.37.190.162,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n21654,8,485,2017-05-24 18:50:00,http://flatleywolff.com/florencio.ferry,0.4976165759,14.33.186.83,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n21655,8,485,2017-01-25 23:31:56,http://bartoletti.com/hosea,0.8144539890,49.9.217.152,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n21656,8,485,2017-05-25 12:27:58,http://veum.io/quinn_treutel,0.5515213132,233.63.37.19,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n21657,8,485,2017-01-07 18:52:47,http://harber.com/tania_reilly,0.1521649732,227.44.4.9,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n21658,8,485,2017-04-19 09:46:40,http://gutmannlakin.biz/ruell,0.0467074287,27.27.75.130,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n21659,8,485,2017-03-31 01:38:54,http://baileyerdman.io/amelie.kiehn,0.1626992429,253.25.107.105,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n21660,8,485,2017-05-23 00:44:44,http://kohler.name/martin,0.3047636488,172.126.3.5,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n21661,8,485,2017-02-04 07:24:02,http://kutchhickle.net/kayley,0.2550184922,61.163.248.55,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n21662,8,485,2017-05-14 06:13:34,http://smith.biz/giles_green,0.7228108334,85.250.88.109,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n21663,8,485,2017-02-25 10:46:28,http://ohara.org/torrey_spinka,0.4689887177,107.23.10.100,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n21664,8,485,2017-01-31 20:50:06,http://jaskolskischaefer.com/corine,0.0530013791,226.199.2.182,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n21665,8,485,2017-02-20 04:36:42,http://moriette.name/blanca.torp,0.9712469809,123.72.133.64,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n21666,8,485,2017-03-19 14:32:08,http://conroy.org/jeica.daugherty,0.6042761325,118.222.157.78,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n21667,8,485,2017-05-03 12:52:17,http://kunde.biz/linwood,0.7425004077,79.157.119.171,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n21668,8,485,2017-02-02 19:29:26,http://feest.info/winnifred_schumm,0.6118465582,195.124.158.201,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n21669,8,485,2017-01-31 00:42:44,http://douglas.io/alysa,0.4281151116,210.148.42.171,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n21670,8,485,2016-12-31 12:11:10,http://feest.net/dawn_runte,0.1168304491,93.117.69.182,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n21671,8,485,2017-05-14 05:22:50,http://block.biz/durward_koelpin,0.4183887169,160.247.117.233,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n21672,8,485,2017-01-25 07:55:44,http://rosenbaum.org/stephon,0.4861377900,184.150.80.14,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n21673,8,485,2017-04-14 06:29:42,http://stroman.name/mittie,0.2668798450,15.254.3.232,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n21674,8,485,2017-02-21 23:26:05,http://okonlubowitz.org/alford,0.1269332366,169.208.57.97,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n21675,8,485,2017-01-18 02:39:24,http://pollich.name/nora_koepp,0.4920038918,175.42.55.71,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n21676,8,485,2017-02-07 16:02:14,http://weinat.org/roman.skiles,0.1075989216,223.230.195.78,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n21677,8,485,2017-04-30 06:33:40,http://emard.info/trenton,0.2717064282,73.81.81.48,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n21678,8,485,2017-05-23 05:31:59,http://collierjerde.info/kelley,0.0536243873,82.175.31.9,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n21679,8,485,2017-01-08 19:04:59,http://pfannerstillpfannerstill.org/scarlett,0.9791798050,202.107.172.54,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n21680,8,485,2016-12-15 13:56:36,http://fay.name/ayla_stiedemann,0.5324430676,162.34.146.210,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n21681,8,485,2017-05-06 14:02:24,http://ratke.name/leon,0.9916864938,14.201.188.4,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n21682,8,485,2016-12-29 00:43:18,http://westwalker.co/mariane,0.9314809213,87.9.135.42,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n21683,8,485,2017-01-12 01:20:54,http://homenickjohns.org/burdette,0.5441678117,180.22.172.27,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n21684,8,485,2017-02-23 08:33:29,http://hartmannlesch.name/robb_mante,0.6640685971,197.19.140.105,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n21685,8,485,2016-12-28 07:29:03,http://dare.name/allie,0.7063734397,250.79.148.43,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n21686,8,485,2017-04-02 17:37:19,http://heidenreichhyatt.io/rolando.upton,0.6384392639,15.37.168.67,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n21687,8,485,2017-05-08 14:34:00,http://dicki.info/braxton.mayert,0.2745501242,56.160.132.48,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n21688,8,485,2017-04-10 21:39:44,http://robel.info/trace_sporer,0.2962148749,111.226.166.118,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n595,1,14,2017-01-16 19:19:57,http://hoegerlegros.org/tyrel,0.5432075141,91.144.186.14,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n596,1,14,2017-02-19 07:17:47,http://altenwerth.name/wendell,0.0659388037,220.187.145.23,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n597,1,14,2016-12-27 04:14:54,http://lindgren.name/kendra,0.5089340492,94.165.227.46,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n598,1,14,2016-12-25 01:57:41,http://keebler.info/caden.gaylord,0.9146963969,230.86.125.68,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n599,1,14,2017-03-14 03:20:49,http://becker.net/americo_hegmann,0.0093533833,249.142.134.86,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n600,1,14,2017-01-30 18:01:58,http://rolfsonferry.org/seamus_feil,0.2775745424,166.244.207.39,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n601,1,14,2017-01-24 01:53:12,http://rowe.biz/lavina,0.1462301167,200.144.52.114,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n602,1,14,2017-01-09 17:07:09,http://muellerokon.io/laura_leannon,0.7330493598,44.130.254.214,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n603,1,14,2017-04-10 23:00:03,http://rauwilkinson.org/charlotte,0.3105306891,77.215.102.213,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n604,1,14,2017-06-13 07:32:29,http://jaskolski.co/bernadette.zboncak,0.1769557479,165.208.12.113,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n605,1,14,2017-04-16 09:01:51,http://littel.co/hipolito,0.9811855012,49.74.184.165,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n606,1,14,2017-05-26 16:54:37,http://reillygoyette.org/sasha_windler,0.8339317455,230.118.80.7,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n607,1,14,2017-06-05 08:11:39,http://hyatt.name/deonte,0.0918650207,121.243.80.27,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n608,1,14,2016-12-16 02:27:48,http://torpdach.info/lue,0.6739489847,61.125.123.56,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n609,1,14,2016-12-23 23:34:55,http://reichel.co/elia,0.8384398069,76.2.48.134,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n610,1,14,2017-04-10 20:10:17,http://kshlerin.net/lilian,0.0310620762,47.75.54.236,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n611,1,14,2017-03-21 08:55:09,http://blick.net/ara_wiegand,0.2728816972,150.164.243.184,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n612,1,14,2017-05-18 13:54:20,http://haagrowe.net/vernie,0.6654161345,236.203.111.3,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n613,1,14,2017-02-01 18:09:55,http://schaefer.org/giovanni_cruickshank,0.2167761636,120.204.39.225,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n614,1,14,2017-03-21 00:35:49,http://aufderhar.biz/rosella_bins,0.5570723880,233.62.8.73,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n615,1,14,2017-06-09 05:25:49,http://dooley.co/ozella,0.4895660094,128.240.63.237,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n616,1,14,2017-04-08 05:57:13,http://mckenzie.info/halle,0.4807887459,200.165.10.157,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n617,1,14,2016-12-29 20:07:29,http://boehm.io/margaret_hagenes,0.2275306982,16.18.173.172,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n618,1,14,2017-01-04 16:25:02,http://tremblay.io/jordy,0.1394232350,140.212.50.67,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n619,1,14,2017-05-23 09:27:28,http://rath.net/vincenza_lakin,0.8313897174,94.206.247.47,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n620,1,14,2017-03-04 03:50:57,http://keebler.org/braulio_nader,0.2681330050,180.69.135.140,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n621,1,14,2017-04-15 11:03:32,http://osinskiryan.io/ethan,0.4487611654,117.50.194.69,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n622,1,14,2016-12-19 04:19:36,http://ziemekunze.biz/roger_vonrueden,0.9174269971,11.62.214.205,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n623,1,14,2017-05-26 08:58:00,http://hettinger.io/penelope_hickle,0.5637041815,51.178.143.91,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n624,1,14,2017-04-11 17:19:16,http://ondrickarice.co/aidan_padberg,0.6963666691,218.144.154.122,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n625,1,14,2017-03-28 22:21:38,http://torptowne.net/emerson_ohara,0.7768064036,239.140.116.240,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n626,1,15,2017-06-11 10:31:39,http://denesik.co/kade.wisoky,0.5051103131,21.115.154.250,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n627,1,15,2017-01-21 10:46:59,http://abbott.name/lizzie,0.1422019063,101.66.100.220,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n628,1,15,2017-03-25 07:37:21,http://schamberger.org/marcos_fritsch,0.3663524713,178.109.238.51,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n629,1,15,2017-01-30 01:48:29,http://kshlerinhackett.biz/kyleigh.moriette,0.7853134503,25.21.127.119,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n630,1,15,2017-03-08 12:33:53,http://mohr.co/hank,0.8183540816,223.222.220.239,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n631,1,15,2017-02-27 08:47:55,http://kreigerlueilwitz.org/kelli,0.2487063478,242.97.161.94,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n632,1,15,2017-05-08 08:20:20,http://wymanlindgren.com/jammie.spinka,0.3412591434,68.3.160.12,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n633,1,15,2017-02-17 10:53:19,http://kuphalhagenes.name/maymie.buckridge,0.3996874567,161.84.223.200,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n634,1,15,2016-12-26 06:30:24,http://cummeratahuels.io/katheryn.schuster,0.6776608313,203.166.123.231,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n635,1,15,2017-03-26 04:49:29,http://flatleygerlach.co/deie_brekke,0.7207512377,254.183.199.157,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n636,1,15,2017-01-21 15:44:17,http://hackett.org/muriel_hayes,0.4471580046,141.99.108.10,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n637,1,15,2017-01-07 20:17:15,http://brownlueilwitz.biz/earlene_barton,0.9472549191,72.100.22.118,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n638,1,15,2017-06-09 12:16:52,http://quigley.com/evelyn,0.6397288046,207.64.164.201,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n639,1,15,2017-06-05 10:58:31,http://littlemayert.name/tristin,0.8791812429,227.26.183.26,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n640,1,15,2017-05-18 01:01:24,http://stracke.co/esta,0.3325711879,138.85.254.54,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n641,1,15,2017-04-04 10:53:14,http://langworth.co/damon,0.1069602064,244.106.106.236,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n642,1,15,2017-04-05 09:39:17,http://rath.co/jonathan,0.0542174405,251.217.158.103,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n643,1,15,2017-01-28 23:14:42,http://davislynch.name/carolyn_runolfsdottir,0.1115052349,87.160.127.187,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n644,1,15,2017-01-07 09:37:40,http://bauchlittle.biz/donald_keebler,0.7477258304,210.30.115.194,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n645,1,15,2017-06-08 10:17:13,http://bergstrom.net/mortimer.oreilly,0.8940896011,211.178.80.176,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n646,1,15,2016-12-17 19:08:41,http://mitchellschuster.net/jennyfer,0.5404107081,221.233.25.115,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n21689,8,485,2017-02-15 09:41:18,http://littel.name/michelle_metz,0.4264450645,171.230.146.2,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n21690,8,485,2017-05-09 17:32:22,http://barton.info/abelardo,0.1581679387,85.235.224.121,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n21691,8,485,2017-04-18 07:08:42,http://doylewilkinson.biz/charlotte_stamm,0.1443394564,18.57.231.210,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n21692,8,486,2017-05-13 06:59:50,http://homenickdeckow.biz/xavier,0.8921992667,48.20.198.159,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n21693,8,486,2017-02-25 05:49:35,http://moenjast.co/olin,0.4779121900,157.7.220.14,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n21694,8,486,2017-06-01 12:17:02,http://leannon.name/earlene,0.1818201272,90.131.9.242,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n21695,8,486,2017-05-03 21:49:06,http://beahan.com/enola.pouros,0.3547822867,47.110.180.217,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n21696,8,486,2017-03-24 23:24:55,http://kleinsenger.io/lenny,0.0116410119,126.108.67.27,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n21697,8,486,2016-12-28 12:31:29,http://bogisich.net/cheyenne.crist,0.0816924364,158.5.248.197,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n21698,8,486,2017-02-15 14:27:53,http://beerkovacek.biz/izabella_lang,0.8279827583,94.187.243.75,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n21699,8,486,2017-01-22 12:54:48,http://fisher.name/cullen_osinski,0.7246660858,239.217.223.2,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n21700,8,486,2017-05-07 23:47:15,http://runolfon.co/dangelo,0.4690782416,156.23.75.123,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n21701,8,486,2017-06-12 16:57:49,http://gleichner.biz/janae.auer,0.4191748897,120.87.30.222,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n21702,8,486,2017-05-21 22:24:13,http://raynorjohnson.io/shemar.marks,0.4141988553,236.116.188.38,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n21703,8,486,2017-04-25 05:00:07,http://runolfsdottir.org/kade,0.6409177871,186.166.81.215,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n21704,8,486,2017-02-08 14:29:45,http://bogan.com/brionna,0.7102126445,68.115.62.5,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n21705,8,486,2017-02-13 13:54:28,http://bartoletti.io/reese_dare,0.0634150170,144.194.201.103,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n21706,8,486,2017-04-29 10:01:50,http://barton.name/cecile.ledner,0.5832031016,174.63.90.195,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n21707,8,486,2017-03-06 06:30:18,http://cruickshankzboncak.com/adolf.ward,0.6110112727,55.4.93.118,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n21708,8,486,2017-03-29 23:02:01,http://rodriguez.info/silas,0.5068219802,229.72.21.107,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n21709,8,486,2017-01-21 17:26:18,http://rippin.biz/toy_deckow,0.1523969342,89.144.165.135,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n21710,8,486,2017-03-13 12:38:22,http://nitzsche.com/aubree,0.6397100353,155.60.103.132,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n21711,8,486,2017-03-06 17:14:43,http://bauchfritsch.org/darian.ryan,0.3322842238,16.212.136.104,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n21712,8,486,2017-04-11 17:35:58,http://rempel.co/velva.steuber,0.1943471364,94.246.208.105,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n21713,8,486,2017-05-19 00:06:14,http://sauer.com/genoveva.casper,0.2891295916,191.204.173.17,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n21714,8,486,2017-03-19 09:47:47,http://weimann.com/linnie,0.5607517309,45.27.73.7,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n21715,8,486,2016-12-29 09:25:04,http://sauer.net/trever,0.8734058034,166.97.171.170,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n21716,8,486,2017-04-16 00:33:23,http://cormier.io/garrick.hickle,0.6809616187,128.13.43.244,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n21717,8,486,2017-01-09 15:18:13,http://harber.com/arturo,0.4932686915,45.223.123.122,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n21718,8,486,2017-02-21 19:03:59,http://hayescole.info/ila_sporer,0.1537280711,132.46.203.249,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n21719,8,486,2017-04-18 08:34:06,http://emmerichstehr.org/rubie,0.9424516850,81.239.185.12,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n21720,8,486,2017-04-19 15:23:19,http://grady.info/amos,0.5807246455,174.2.30.125,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n21721,8,486,2017-01-30 01:25:15,http://metz.io/antonietta,0.4564609492,84.88.174.230,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n21722,8,486,2017-03-15 03:31:55,http://ritchie.biz/ford,0.8200843336,213.204.170.104,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n21723,8,486,2017-01-24 16:34:55,http://von.com/aleandra_quitzon,0.2542551902,114.169.202.150,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n21724,8,486,2017-06-04 23:54:56,http://kub.info/bert_robel,0.6004908216,133.77.134.217,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n21725,8,486,2016-12-17 19:26:58,http://satterfield.biz/jaleel,0.5027998777,161.121.84.183,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n21726,8,486,2017-03-08 16:29:28,http://frami.co/marlon.runte,0.0274555892,36.3.38.46,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n21727,8,486,2017-04-24 19:04:18,http://sanford.org/miller,0.6122300013,44.75.73.232,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n21728,8,486,2017-02-27 03:05:00,http://buckridgebeier.co/wyatt_cremin,0.6015390341,14.250.45.44,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n21729,8,486,2017-01-13 09:26:54,http://kovacekmann.name/jee,0.2899621249,50.49.223.196,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n21730,8,486,2017-02-28 22:11:18,http://lebsackbergstrom.info/jalyn,0.5359494010,194.2.176.35,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n21731,8,486,2017-01-30 08:56:53,http://fayschimmel.biz/kyleigh,0.6414176020,157.108.156.25,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n21732,8,486,2016-12-14 05:06:09,http://windlerharber.io/meda,0.8105419091,220.74.83.110,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n21733,8,486,2017-06-08 16:18:42,http://towne.net/axel_kuhn,0.4903286079,192.110.135.56,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n21734,8,486,2017-04-24 11:55:25,http://kuhicschroeder.biz/tod,0.0542611465,234.122.140.185,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n21735,8,486,2017-03-02 23:44:29,http://metz.co/leda,0.0947482238,134.27.223.82,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n21736,8,486,2017-05-10 01:38:55,http://kundefahey.net/leonardo,0.1213394031,227.70.177.43,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n21737,8,486,2017-02-28 05:01:00,http://thiel.name/magdalen_carter,0.1892735298,124.248.243.170,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n21738,8,486,2017-06-13 04:11:12,http://ryanmitchell.biz/candido.kuhlman,0.3307187210,211.149.9.27,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n21739,8,486,2017-04-05 18:25:28,http://friesen.com/benjamin_nolan,0.2306927392,175.216.69.180,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n647,1,15,2016-12-27 01:13:20,http://conroy.co/fannie_jones,0.0580697490,144.141.166.77,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n648,1,15,2017-05-18 06:05:38,http://keeblerkaulke.name/eula.schroeder,0.6325928777,169.154.222.79,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n649,1,15,2017-02-19 05:58:36,http://pollich.info/mathew_macgyver,0.6876000269,197.209.250.16,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n650,1,15,2017-06-03 00:02:28,http://kihn.co/tyrel,0.4427050526,92.34.252.21,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n651,1,16,2017-04-02 11:20:57,http://howegutkowski.info/garett,0.6930005556,46.174.128.227,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n652,1,16,2017-04-03 01:42:09,http://monahandouglas.org/althea,0.5576192339,65.64.24.12,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n653,1,16,2017-02-26 19:09:42,http://hammesdicki.co/josefa,0.9612586901,92.189.186.128,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n654,1,16,2017-05-13 13:19:27,http://shanahan.net/dasia,0.5038018738,87.237.35.205,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n655,1,16,2017-04-15 08:25:35,http://beatty.name/cordie_baumbach,0.7196666585,99.222.163.122,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n656,1,16,2017-04-30 08:19:29,http://halvorson.net/daniela,0.3338070786,111.159.232.223,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n657,1,16,2017-03-14 07:52:02,http://dicki.net/noemi_rau,0.1288609802,146.137.129.205,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n658,1,16,2017-02-06 16:09:20,http://weinat.info/zaria.predovic,0.7722979702,254.41.155.178,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n659,1,16,2017-06-13 23:50:45,http://dickens.biz/pansy_rempel,0.7336186170,115.231.82.120,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n660,1,16,2017-06-08 21:17:36,http://murazikpowlowski.net/juana,0.6797698953,46.245.224.45,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n661,1,16,2017-01-01 01:44:31,http://kuvalis.io/randy,0.9008435504,162.240.70.16,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n662,1,16,2017-01-05 13:30:31,http://muellergoldner.org/icie,0.3427249122,239.166.25.34,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n663,1,16,2017-05-06 07:51:18,http://marquardt.org/keely,0.0212565228,180.211.239.105,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n664,1,16,2017-06-03 13:34:02,http://botsford.org/vidal.hayes,0.0674131129,239.160.115.136,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n665,1,16,2017-03-23 03:50:35,http://harvey.name/margarette,0.4216591311,206.247.121.125,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n666,1,16,2017-03-23 05:25:27,http://zieme.com/nickolas_gorczany,0.7138523167,246.18.64.49,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n667,1,16,2016-12-20 05:54:22,http://hermann.name/ethyl.hahn,0.3606970978,241.121.25.102,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n668,1,16,2016-12-17 21:39:01,http://hilpert.biz/cale,0.1903379742,158.185.230.215,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n669,1,16,2017-03-22 19:04:25,http://pouros.net/donny.kuphal,0.8576155915,89.68.156.143,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n670,1,16,2017-02-25 22:15:06,http://connellyjacobi.name/ima_hodkiewicz,0.3946469785,28.227.57.197,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n671,1,16,2017-02-02 18:38:07,http://daniel.net/adolf,0.6712166817,208.239.95.189,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n672,1,16,2017-01-12 07:21:55,http://ziemannspinka.name/foster,0.8757948990,207.192.202.121,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n673,1,16,2017-02-06 16:31:12,http://gutkowskigoyette.info/glennie,0.0849041091,146.24.94.204,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n674,1,16,2016-12-20 12:56:24,http://boehm.biz/barbara,0.5807929292,114.217.201.152,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n675,1,16,2017-03-20 16:44:34,http://mclaughlin.com/brandon,0.4377246245,137.254.40.244,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n676,1,16,2017-02-18 07:08:52,http://kaulke.name/ruell,0.2871905270,115.210.117.95,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n677,1,16,2017-04-17 05:31:52,http://trantowkunde.co/wilfrid_sanford,0.1551226698,62.79.235.130,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n678,1,16,2017-04-04 04:14:52,http://reilly.co/eleonore.kohler,0.1741163822,200.251.12.43,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n679,1,16,2017-03-05 16:29:21,http://klocko.org/sim,0.5320342371,160.86.178.11,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n680,1,16,2017-02-14 12:34:52,http://quigleycormier.info/leonard.barrows,0.9918399512,18.4.135.176,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n681,1,16,2017-03-30 05:13:04,http://huels.co/jada.schmitt,0.3819120076,91.44.8.15,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n682,1,16,2017-04-09 01:37:21,http://carroll.org/adolf,0.6355591001,251.123.77.182,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n683,1,16,2017-01-10 21:38:56,http://bednarkunde.net/alejandra,0.2563387908,172.245.58.254,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n684,1,16,2017-02-10 12:10:17,http://wintheiserhowell.net/elaina,0.4509225291,164.78.228.42,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n685,1,16,2017-01-09 09:36:46,http://ryan.info/raphael,0.2556251134,217.94.118.186,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n686,1,16,2017-02-17 04:21:41,http://schmidt.name/ella_pfeffer,0.1819068287,196.226.154.33,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n687,1,17,2017-03-11 09:01:17,http://schamberger.biz/cielo,0.6388316833,44.242.142.153,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n688,1,17,2017-05-02 02:56:57,http://blick.net/asa_mitchell,0.4358244047,194.72.231.243,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n689,1,17,2017-05-13 15:04:01,http://schaden.biz/giuseppe,0.5506703269,198.132.202.132,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n690,1,17,2017-05-26 21:44:48,http://halvorson.com/janis.fahey,0.5896872540,240.192.149.160,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n691,1,17,2017-05-17 14:36:31,http://volkmanmurray.co/jadyn,0.2184602954,151.219.145.97,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n692,1,17,2017-04-26 19:13:26,http://heller.biz/jamar_halvorson,0.4983878910,16.187.217.144,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n693,1,17,2017-02-04 14:59:22,http://mclaughlindickinson.com/ayana_nikolaus,0.2993980338,40.252.186.230,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n694,1,17,2017-03-05 07:22:39,http://schroederwaters.name/lillie,0.9536559892,140.64.204.253,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n695,1,17,2017-04-22 07:40:49,http://franecki.net/keagan.nolan,0.3976513512,176.37.254.64,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n696,1,17,2017-01-03 07:51:14,http://hamill.com/gerda.abbott,0.0613937381,3.68.20.223,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n697,1,17,2016-12-29 14:02:20,http://abernathy.com/antonio,0.5070615077,192.92.225.49,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n698,1,17,2017-01-01 17:35:56,http://ankunding.net/oral.nikolaus,0.3136747089,236.18.205.144,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n21740,8,486,2016-12-17 13:11:52,http://oreilly.io/sincere,0.9601836176,182.248.29.81,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n21741,8,486,2017-05-28 20:10:46,http://harberweber.io/violette,0.6283354924,246.54.227.81,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n21742,8,486,2017-01-03 07:21:33,http://durgan.info/reyna,0.9898902314,85.67.205.170,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n21743,8,486,2017-03-14 14:07:48,http://batz.org/evert.reichel,0.1224614534,85.218.180.83,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n21744,8,486,2017-04-05 21:32:22,http://christiansen.org/brent.johns,0.3630375768,209.236.156.35,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n21745,8,486,2017-02-27 11:58:29,http://damore.org/gillian_moriette,0.9030868842,206.175.146.183,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n21746,8,486,2017-05-16 22:13:16,http://hahnstracke.info/abigale_vonrueden,0.3755267174,121.30.105.142,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n21747,8,486,2017-05-25 08:13:10,http://schmidt.name/katrina_stanton,0.3675710071,21.80.33.253,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n21748,8,486,2017-01-10 03:35:30,http://schowalterherzog.io/christy_rice,0.1645404389,78.150.100.36,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n21749,8,486,2017-03-02 23:00:55,http://trantowmraz.com/milton_marvin,0.2279152498,180.10.194.21,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n21750,8,487,2017-04-12 09:57:07,http://friesen.biz/otha,0.0195390988,148.131.181.80,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n21751,8,487,2017-02-27 12:22:38,http://carroll.net/diego,0.9348165243,231.174.39.64,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n21752,8,487,2016-12-24 01:24:58,http://bogisichprice.org/kenya,0.2022475982,107.163.12.62,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n21753,8,487,2017-05-14 04:53:26,http://zulauf.biz/mollie,0.5045467364,144.128.69.117,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n21754,8,487,2017-05-01 01:57:33,http://schamberger.org/kaela_kemmer,0.7384094839,188.224.198.161,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n21755,8,487,2017-04-04 14:23:42,http://prohaskacartwright.name/rosalinda_von,0.8736868329,134.115.247.156,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n21756,8,487,2017-05-18 14:28:13,http://dibbert.com/jason,0.9938585716,4.51.36.51,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n21757,8,487,2017-04-11 04:09:05,http://rutherfordwalter.biz/annamae,0.1503358846,115.209.72.11,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n21758,8,487,2017-01-10 05:32:26,http://williamson.io/ayden,0.0983903965,158.223.154.120,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n21759,8,487,2017-01-18 13:28:36,http://simonis.io/walter_wiegand,0.7478961176,236.161.233.131,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n21760,8,487,2017-02-13 19:09:41,http://towne.org/destin,0.6458293824,26.227.164.249,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n21761,8,487,2017-03-10 14:04:13,http://okeefetremblay.net/george_luettgen,0.4349112527,210.107.226.200,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n21762,8,487,2016-12-20 22:31:04,http://renner.biz/adella_farrell,0.8303364331,147.151.197.119,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n21763,8,487,2017-03-12 12:08:59,http://lindgrendouglas.com/carlo,0.1560037753,49.189.202.195,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n21764,8,487,2017-06-01 17:43:45,http://reingerdubuque.org/laney.steuber,0.7804121735,36.11.206.141,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n21765,8,487,2017-02-22 09:03:11,http://mertz.co/eliseo_greenfelder,0.8473871245,84.21.49.224,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n21766,8,487,2017-03-18 18:22:31,http://vonruedenwatsica.com/devante,0.3634518885,162.242.130.120,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n21767,8,487,2017-05-27 21:51:27,http://kohler.com/annetta,0.5641414134,155.249.180.38,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n21768,8,487,2017-01-30 14:39:07,http://murazik.org/verla,0.4443445397,227.229.232.188,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n21769,8,487,2017-05-20 16:36:56,http://kaulke.net/hudson,0.0459895908,104.157.94.128,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n21770,8,487,2017-06-13 09:58:57,http://upton.name/mohammad,0.9326342900,69.108.223.44,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n21771,8,487,2017-04-20 18:40:24,http://gerhold.info/reggie.bauch,0.0818195463,59.83.109.239,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n21772,8,487,2017-03-26 00:48:25,http://zemlak.net/aurelio,0.5274810200,202.54.238.19,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n21773,8,487,2017-01-08 04:34:20,http://abernathy.com/marcelina,0.4078646495,95.143.43.229,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n21774,8,487,2017-02-07 00:07:46,http://macgyvermclaughlin.org/santino_huels,0.4235228598,50.101.87.119,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n21775,8,487,2017-04-02 04:30:11,http://cummingskovacek.net/caie,0.4842856028,147.59.205.130,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n21776,8,487,2017-04-26 21:53:02,http://gradypurdy.name/tyrel.little,0.9612433927,233.70.205.115,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n21777,8,487,2017-05-09 16:31:37,http://cronin.io/novella,0.6964588772,200.32.178.56,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n21778,8,487,2017-03-25 14:31:25,http://leuschkelueilwitz.com/gregorio,0.0567256574,57.32.14.143,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n21779,8,487,2017-01-08 20:43:20,http://dickinsonbogisich.net/kimberly.rutherford,0.2784065390,108.108.213.63,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n21780,8,487,2016-12-16 02:09:06,http://mullerreynolds.io/taya,0.1778741308,229.8.167.173,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n21781,8,487,2017-01-03 21:31:07,http://dooley.biz/marguerite,0.4039199337,215.132.27.109,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n21782,8,487,2017-01-13 08:35:36,http://pagac.com/bruce_gislason,0.1257830008,249.115.243.135,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n21783,8,487,2017-04-20 02:22:50,http://wiza.io/hans.jacobi,0.4783631664,10.139.56.8,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n21784,8,487,2016-12-13 19:27:32,http://macejkovic.co/zechariah,0.0063537839,140.217.157.96,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n21785,8,487,2017-02-19 23:10:47,http://pfeffer.net/wilfredo,0.9502524338,197.178.21.250,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n21786,8,487,2017-03-16 00:25:11,http://kiehnemard.info/maggie,0.4513554925,50.59.60.247,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n21787,8,487,2017-05-18 23:57:00,http://jacobi.org/erna_kutch,0.5031997690,51.116.171.120,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n21788,8,488,2016-12-21 22:16:34,http://upton.org/alexanne_ferry,0.1581584970,104.212.183.169,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n21789,8,488,2017-01-16 06:40:53,http://bergnaum.info/germaine_wilderman,0.4465439206,215.114.142.168,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n21790,8,488,2016-12-19 08:37:32,http://mosciski.biz/mathew,0.6820212252,9.236.61.19,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n699,1,17,2017-01-06 12:04:52,http://mayertgislason.com/nola,0.6659007426,250.32.221.225,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n700,1,17,2016-12-26 18:53:11,http://schambergerbarton.co/felicita.batz,0.3285112133,237.194.207.155,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n701,1,17,2017-01-31 04:10:00,http://conroyrowe.name/tina,0.1983847355,114.92.40.163,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n702,1,17,2017-05-01 19:38:03,http://ondrickalockman.io/tania,0.5046378015,217.120.35.226,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n703,1,17,2017-04-17 11:02:13,http://gutmann.io/claudine_parker,0.1635388210,176.182.188.241,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n704,1,17,2017-02-24 15:47:48,http://lindgren.org/luna,0.4657619213,211.132.206.71,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n705,1,17,2017-01-21 02:10:58,http://schaefer.co/mariela_terry,0.8209161171,224.105.29.6,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n706,1,17,2016-12-19 21:32:14,http://tromp.net/dortha.wyman,0.3949535681,219.24.27.158,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n707,1,17,2017-03-22 14:13:59,http://bogan.co/uriel.hauck,0.2967328537,31.22.121.246,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n708,1,17,2017-04-26 21:57:28,http://cartwright.co/winona_prosacco,0.9120943701,40.94.205.53,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n709,1,17,2017-05-12 11:38:00,http://greenholthagenes.io/ronny,0.2699627694,13.23.182.126,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n710,1,17,2017-05-08 04:36:33,http://cole.com/zander,0.0264346793,83.172.13.223,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n711,1,17,2017-03-14 22:22:01,http://mertz.biz/adelbert.cormier,0.6527820019,178.48.69.34,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n712,1,17,2017-04-27 23:12:00,http://wuckert.org/oswaldo,0.6761603585,58.104.253.225,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n713,1,17,2017-06-11 22:04:40,http://rolfsonwintheiser.net/sammie,0.5112922934,57.253.177.169,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n714,1,17,2017-02-03 18:54:03,http://haag.co/alexis,0.3490664837,30.181.160.211,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n715,1,17,2017-06-02 06:25:51,http://kemmer.com/polly_price,0.6373698062,106.66.243.209,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n716,1,17,2017-03-07 11:34:56,http://greenholt.net/verna,0.6727093527,71.46.205.144,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n717,1,17,2017-04-11 06:13:29,http://harveypaucek.co/brooks.okon,0.7390430651,138.119.218.120,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n718,1,17,2017-06-02 08:17:16,http://hodkiewiczcarroll.biz/thea_moriette,0.5702686033,11.127.105.64,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n719,1,17,2017-02-09 15:05:42,http://blanda.org/bo,0.9986694066,64.142.118.61,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n720,1,17,2017-01-08 04:15:36,http://schimmel.info/urban,0.5970538167,205.138.226.161,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n721,1,17,2017-05-07 09:44:28,http://swaniawski.info/edwina,0.6319560771,153.107.248.78,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n722,1,17,2017-01-30 16:45:56,http://rauschaefer.name/sarina,0.5419149162,129.192.50.219,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n723,1,17,2017-05-21 00:15:37,http://conroyhamill.net/florida.hand,0.3416174456,238.245.225.92,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n724,1,17,2017-01-06 15:57:03,http://dooleywiegand.org/hazel,0.7137576964,36.95.64.207,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n725,1,17,2017-04-07 19:07:52,http://spinka.net/zoila,0.0180465335,177.101.164.145,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n726,1,17,2017-02-23 21:02:27,http://hegmann.co/bell,0.8903189511,163.80.93.17,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n727,1,17,2017-01-28 06:43:36,http://kemmerbuckridge.info/brenda.von,0.6481964600,240.224.133.151,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n728,1,17,2017-02-23 04:59:21,http://bogisichlabadie.org/adelia.gleichner,0.6935115142,15.102.7.201,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n729,1,17,2017-05-24 01:40:32,http://gleason.biz/letha,0.5630973309,51.159.204.118,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n730,1,17,2017-04-19 21:36:21,http://powlowski.name/alek.runte,0.4717640153,242.40.44.152,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n731,1,17,2017-02-11 23:42:57,http://hettingerwaelchi.biz/liliana.kuvalis,0.4403855103,208.85.192.75,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n732,1,17,2017-02-17 01:14:45,http://zieme.com/antonietta,0.6635081513,29.219.17.177,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n733,1,17,2017-05-08 04:42:11,http://medhurstdeckow.net/zakary,0.0353995083,170.32.69.135,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n734,1,18,2017-02-11 12:31:55,http://harris.net/kay.rutherford,0.3248948260,91.90.51.238,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n735,1,18,2017-03-11 07:44:58,http://batz.biz/albertha,0.6930348274,47.223.80.54,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n736,1,18,2017-05-09 00:11:36,http://naderondricka.biz/magdalena_bartell,0.1055769758,97.148.9.70,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n737,1,18,2017-01-24 02:51:09,http://mcclurehagenes.org/jackeline,0.4425122865,60.204.149.64,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n738,1,18,2017-02-22 05:20:54,http://prohaska.org/moriah,0.1943760653,246.205.23.181,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n739,1,18,2017-03-26 18:14:21,http://romaguera.com/billy,0.0734000258,163.175.75.164,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n740,1,18,2017-01-13 15:21:44,http://mcdermott.co/dayne.bergnaum,0.2581587922,213.130.58.10,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n741,1,18,2017-03-23 16:26:02,http://kilback.name/tyrese,0.3479709415,210.166.246.190,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n742,1,18,2017-02-03 09:03:46,http://romaguerakiehn.biz/ken.dietrich,0.4404779250,23.99.153.111,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n743,1,18,2017-06-11 05:51:07,http://connelly.co/rosanna_gibson,0.9291207293,85.197.253.55,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n744,1,18,2017-05-09 12:53:15,http://romaguera.biz/malika.kuhn,0.8868278426,106.238.22.198,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n745,1,18,2017-02-25 00:45:57,http://breitenberg.biz/harry.kulas,0.5667186124,170.254.38.24,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n746,1,18,2017-04-24 11:32:39,http://oconner.name/betsy.miller,0.0302597362,140.177.145.66,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n747,1,18,2017-04-04 23:56:23,http://nader.biz/stephan,0.1393401838,160.185.103.118,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n748,1,18,2017-06-04 12:23:01,http://hermistonpaucek.info/bryce.daniel,0.2202493412,52.26.90.142,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n749,1,18,2017-03-31 13:00:40,http://gleichnerrohan.io/cameron.watsica,0.1981494054,16.125.216.202,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n750,1,18,2017-04-07 14:20:09,http://dickensvon.info/nicolette.reinger,0.1913264392,50.242.162.178,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n21791,8,488,2017-01-16 12:33:28,http://ratke.name/mathias.grimes,0.6418457741,181.197.185.219,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n21792,8,488,2017-01-13 17:24:10,http://nicolas.co/andy.botsford,0.4402524021,73.156.26.52,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n21793,8,488,2017-02-17 11:04:02,http://lockman.com/mireille.wilderman,0.1332917525,221.144.248.240,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n21794,8,488,2017-02-22 12:41:55,http://terrybailey.io/florine,0.3227951404,180.92.209.162,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n21795,8,488,2017-03-08 13:32:42,http://kemmer.name/rodrigo_swift,0.2444938758,186.243.16.218,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n21796,8,488,2017-02-27 16:57:55,http://runolfsdottir.org/rylee_johnson,0.2834792672,115.248.193.24,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n21797,8,488,2017-03-22 11:58:57,http://dibbertwiegand.io/keira,0.7332519670,138.24.141.39,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n21798,8,488,2017-05-24 18:59:34,http://daugherty.info/foster,0.2185154888,184.156.152.193,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n21799,8,488,2017-03-27 07:02:00,http://okuneva.info/daniela,0.9419276221,87.160.54.29,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n21800,8,488,2017-01-16 14:15:21,http://greenfeldertreutel.co/emilie_bernier,0.1394425954,41.215.28.116,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n21801,8,488,2017-03-02 10:18:38,http://medhurst.biz/carleton_marks,0.8278235593,82.134.17.167,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n21802,8,488,2017-05-08 16:34:00,http://weimann.name/leland,0.3856929690,191.146.19.241,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n21803,8,488,2017-01-12 05:23:42,http://lubowitz.org/jaime.metz,0.5373475400,70.165.40.134,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n21804,8,488,2017-05-19 17:38:05,http://boyer.io/josie,0.9876154052,8.206.113.212,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n21805,8,488,2017-04-29 20:27:53,http://block.biz/marshall,0.3348429259,30.251.83.117,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n21806,8,488,2017-03-11 10:27:44,http://corkerykeler.com/josianne,0.6720744750,49.244.237.220,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n21807,8,488,2017-01-05 14:13:57,http://breitenbergdibbert.info/judah,0.7766031453,68.41.60.87,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n21808,8,488,2017-01-04 11:04:13,http://bashirian.io/pierre,0.1786664967,146.217.194.82,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n21809,8,488,2017-01-09 23:53:16,http://bernhard.org/velva_legros,0.7089982213,227.221.51.229,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n21810,8,488,2017-03-26 02:45:43,http://gulgowskigleichner.name/mafalda_schumm,0.2023853675,36.21.241.247,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n21811,8,488,2017-05-18 15:50:23,http://kirlin.biz/douglas_klocko,0.3023890230,208.28.75.86,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n21812,8,488,2017-01-25 21:11:20,http://rodriguez.name/clare,0.4892635181,252.4.13.13,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n21813,8,488,2017-02-13 09:48:47,http://schinnerlabadie.biz/jayson,0.1221528004,218.187.110.85,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n21814,8,488,2017-06-08 23:22:06,http://heathcote.net/dimitri_flatley,0.6490576647,11.139.170.56,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n21815,8,488,2017-03-19 21:17:21,http://bartonheel.name/austyn,0.4020316482,91.218.91.24,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n21816,8,488,2017-02-01 23:39:13,http://marvin.name/piper_rippin,0.9101164925,250.221.67.133,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n21817,8,488,2017-02-14 08:38:51,http://klocko.biz/stefan,0.0822747185,41.85.169.133,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n21818,8,488,2017-04-12 18:31:36,http://lehnerkoch.net/arnoldo_witting,0.4666952118,105.92.206.73,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n21819,8,488,2017-06-04 22:21:32,http://gaylord.biz/kaley.flatley,0.4092632984,131.79.148.212,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n21820,8,488,2016-12-26 01:31:17,http://spinkaebert.net/matilda,0.8126084634,193.160.218.110,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n21821,8,488,2017-02-07 06:53:01,http://barton.biz/griffin_kub,0.9745024520,186.41.162.19,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n21822,8,488,2017-03-31 05:41:50,http://schulist.com/lacy,0.2727397450,97.50.68.95,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n21823,8,488,2017-01-04 15:48:53,http://waterslangworth.org/madelynn.klocko,0.4687375992,56.26.20.45,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n21824,8,488,2017-01-18 20:39:45,http://hahn.name/kenton.dach,0.7693017071,61.241.56.65,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n21825,8,488,2017-03-26 22:21:37,http://waelchibayer.co/ottis,0.9488593833,132.226.14.163,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n21826,8,488,2017-02-04 13:27:52,http://muller.info/edgar_miller,0.5950464935,92.220.201.205,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n21827,8,488,2017-06-02 17:54:39,http://wolff.io/nickolas,0.6191351012,30.109.115.43,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n21828,8,488,2017-01-23 06:20:10,http://dubuque.info/rylee,0.4682555364,101.134.67.60,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n21829,8,488,2017-02-13 09:52:19,http://mueller.co/cordelia,0.5922319079,39.241.82.122,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n21830,8,488,2017-04-04 20:37:07,http://kochswift.net/vernice,0.5752751676,10.153.47.146,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n21831,8,488,2017-03-04 01:46:41,http://schaeferdonnelly.io/nyah.hoeger,0.4088153361,247.247.47.231,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n21832,8,488,2017-04-24 01:53:55,http://osinskirogahn.org/delilah,0.2937071459,153.177.132.225,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n21833,8,488,2017-02-28 00:55:04,http://veum.net/christophe.goyette,0.2027813896,25.223.182.214,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n21834,8,488,2017-02-07 15:58:00,http://mitchellhayes.com/sarina,0.0135614644,137.82.243.32,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n21835,8,488,2017-03-04 23:31:38,http://moriette.org/jaunita.keler,0.7363914827,146.136.28.41,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n21836,8,488,2017-03-10 07:29:47,http://kuphalcronin.name/van_becker,0.3450583617,4.96.68.22,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n21837,8,488,2017-05-11 01:04:08,http://kutchkoch.com/emmanuelle.christiansen,0.6879781996,232.106.15.166,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n21838,8,488,2017-04-23 19:23:58,http://ruel.org/joan,0.3817558213,108.89.12.177,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n21839,8,488,2017-03-19 23:06:42,http://pfeffer.info/eduardo,0.4446939354,98.194.131.45,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n21840,8,488,2016-12-14 12:28:54,http://okuneva.co/royce_baumbach,0.3246995558,183.228.151.185,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n21841,8,488,2017-05-29 18:38:12,http://gutkowskideckow.io/gregory_gibson,0.2705500712,194.120.140.97,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n751,1,18,2017-02-20 00:04:01,http://wyman.net/eusebio,0.0451662661,81.96.130.197,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n752,1,18,2017-01-01 21:16:09,http://gutmann.name/margaret,0.6256827212,208.81.138.12,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n753,1,18,2017-02-07 16:22:50,http://rolfson.co/frederick,0.1635584264,242.152.197.217,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n754,1,18,2017-02-05 13:34:54,http://oberbrunnermiller.org/reina,0.2417983787,223.13.50.68,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n755,1,18,2017-03-02 22:27:07,http://mayer.co/thomas.christiansen,0.5537420216,215.35.195.153,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n756,1,18,2017-03-06 06:44:31,http://thompsonwalsh.io/nelle,0.2420303189,210.159.76.243,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n757,1,18,2017-01-21 18:16:20,http://nader.info/camylle,0.5334830759,51.92.70.151,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n758,1,18,2017-01-15 22:58:15,http://kirlin.com/afton,0.5876386675,50.110.23.192,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n759,1,18,2017-04-17 22:54:09,http://nitzsche.co/corrine,0.1335697103,209.42.140.249,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n760,1,18,2016-12-31 21:10:02,http://ritchiemante.com/jayme,0.6446955130,70.11.51.93,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n761,1,18,2017-01-13 00:45:58,http://toy.org/arlo_nienow,0.6315342365,176.218.59.240,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n762,1,18,2017-03-16 21:08:29,http://beatty.co/rachel.koepp,0.8466019604,136.68.23.53,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n763,1,18,2017-04-21 01:19:46,http://breitenbergokuneva.name/kavon.mosciski,0.6582684567,149.220.54.102,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n764,1,18,2017-01-28 00:48:43,http://upton.io/bruce.lynch,0.1104383495,153.142.16.172,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n765,1,18,2017-03-13 10:53:14,http://emmerich.name/dagmar_hansen,0.0804609547,28.205.158.179,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n766,1,18,2017-02-27 15:50:10,http://jacobs.name/abbigail.connelly,0.8983033512,24.185.14.98,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n767,1,18,2017-03-06 08:37:19,http://treutel.com/turner,0.9228699549,91.140.183.49,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n768,1,18,2017-03-28 00:10:51,http://gibson.com/jaqueline,0.1091751829,6.248.9.100,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n769,1,18,2017-04-27 11:47:15,http://runolfon.org/mike_konopelski,0.4336912520,150.234.214.34,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n770,1,18,2017-05-13 23:04:41,http://harris.com/linwood.schmitt,0.0522468381,104.70.200.72,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n771,1,18,2017-04-22 09:32:25,http://dibbert.co/hillard.mosciski,0.6153340357,246.166.88.200,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n772,1,18,2017-03-13 21:48:31,http://king.co/lilliana,0.1118251662,159.186.19.58,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n773,1,18,2017-02-28 10:11:23,http://orn.net/antonetta_yost,0.4741321172,120.120.8.15,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n774,1,18,2017-01-03 17:45:36,http://witting.org/edwina,0.2653390624,107.96.175.173,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n775,1,18,2017-05-02 14:10:57,http://kovacek.name/matilde_wilkinson,0.0737117138,65.180.246.210,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n776,1,18,2017-01-28 17:57:53,http://hahn.info/caandra_haley,0.8208549595,243.52.222.170,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n777,1,18,2016-12-18 01:16:50,http://glover.info/makayla,0.5830523350,174.11.7.157,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n778,1,18,2017-05-04 01:51:52,http://batzdubuque.com/merritt,0.6155353767,126.215.74.135,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n779,1,18,2017-02-27 15:32:52,http://schadensteuber.com/caie,0.3121925003,57.4.214.213,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n780,1,18,2016-12-29 02:15:05,http://naderkohler.net/jermey,0.9501712048,130.151.254.147,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n781,1,18,2017-04-05 00:42:59,http://davis.name/annette_gulgowski,0.6905620332,176.52.103.120,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n782,1,18,2016-12-30 23:57:57,http://hahnmckenzie.co/tom,0.3936842107,34.25.77.151,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n783,1,18,2017-04-01 05:57:25,http://hermannbalistreri.io/sarina,0.1366392708,228.45.251.38,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n784,1,18,2017-02-22 18:10:52,http://steuber.info/coleman,0.9944250901,186.28.6.108,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n785,1,18,2017-03-22 23:59:52,http://feil.name/garnett_connelly,0.1955121309,129.231.100.200,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n786,1,18,2017-06-03 04:56:51,http://hicklelindgren.org/aimee,0.0729385607,159.180.41.98,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n787,1,18,2017-02-01 15:15:00,http://huels.org/breanne.volkman,0.7968237949,43.207.221.234,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n788,1,18,2017-03-20 00:11:18,http://gibson.net/keely.blanda,0.0582424962,17.241.247.189,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n789,1,18,2016-12-24 08:09:40,http://hayes.io/beulah,0.8458180782,159.46.64.251,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n790,1,18,2017-03-03 10:37:37,http://dooley.org/craig_gottlieb,0.8960295729,56.82.97.194,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n791,1,18,2017-05-13 00:43:56,http://rueckerdaugherty.com/alexandre,0.7177347752,27.22.143.190,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n792,1,18,2017-03-01 18:16:11,http://haley.name/kirk.hamill,0.2563875139,188.216.5.102,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n793,1,18,2017-01-23 13:27:35,http://hudsonlittel.io/eric.hackett,0.4302024840,187.93.33.141,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n794,1,18,2017-01-13 22:21:53,http://deckow.info/wallace,0.9975462799,102.14.210.204,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n795,1,18,2017-02-10 16:18:57,http://trantow.info/wilbert_douglas,0.0970885986,230.195.169.207,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n796,1,18,2017-04-24 04:20:48,http://ortiz.net/torrance_emard,0.5967184660,139.169.160.40,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n797,1,18,2016-12-21 18:20:38,http://johnson.io/darrell,0.6137416592,170.64.228.189,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n798,1,18,2017-03-12 08:54:22,http://kilback.io/felicita,0.6944138265,197.90.235.94,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n799,1,18,2017-05-13 20:14:49,http://lowe.io/shany,0.3431985301,198.206.153.210,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n800,1,18,2017-04-23 10:50:59,http://schummfeest.co/eleazar,0.0548789691,159.204.152.129,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n801,1,19,2017-06-08 23:34:36,http://kingbrekke.biz/juliet,0.3934337329,247.78.212.77,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n802,1,19,2017-01-25 17:00:09,http://stokes.info/madilyn,0.7738837079,226.139.2.167,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n21842,8,488,2017-02-22 13:19:50,http://walter.co/arne,0.4080410826,3.117.203.170,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n21843,8,488,2017-04-20 09:30:33,http://dickinson.biz/curtis_beier,0.4579956626,116.58.137.218,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n21844,8,488,2017-04-12 10:06:18,http://marks.net/vincenza_rice,0.3318336670,38.153.211.113,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n21845,8,488,2017-03-20 14:16:55,http://cole.net/mireya,0.8093959832,24.76.49.45,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n21846,8,488,2017-04-07 09:13:43,http://walter.org/lindsey_heaney,0.9339443366,105.191.18.58,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n21847,8,488,2017-04-06 19:09:20,http://schimmel.info/bianka.bednar,0.0055641447,207.238.22.60,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n21848,8,488,2017-03-04 01:47:29,http://hermann.info/rose,0.7948881050,15.69.28.69,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n21849,8,488,2017-05-10 08:12:11,http://smitham.com/roxane.prosacco,0.8427825736,101.209.132.21,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n21850,8,488,2016-12-28 03:04:55,http://thompson.org/perry.windler,0.6330362242,128.218.211.202,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n21851,8,488,2017-02-17 14:01:24,http://block.name/mya_herman,0.3508962615,47.19.82.198,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n21852,8,488,2017-01-23 14:49:02,http://oberbrunner.name/keanu,0.4701324920,182.180.153.121,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n21853,8,488,2016-12-22 22:28:47,http://lowe.com/marcus_mills,0.3329101170,217.196.245.53,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n21854,8,488,2017-04-06 19:04:51,http://toy.info/hulda.franecki,0.7587740847,117.208.245.249,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n21855,8,488,2017-06-02 17:57:36,http://daughertygrant.co/amparo.conn,0.8407182363,202.239.46.178,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n21856,8,489,2017-01-25 06:22:57,http://moriette.biz/kelli,0.0140220877,114.241.86.194,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n21857,8,489,2016-12-18 01:25:52,http://westcummerata.info/sheila,0.7498768515,17.233.72.217,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n21858,8,489,2017-05-19 23:49:36,http://bartonzemlak.biz/gunnar.schroeder,0.5592423119,167.66.97.162,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n21859,8,489,2017-01-20 04:46:57,http://mckenzie.io/breanne_haag,0.0710995074,240.202.54.137,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n21860,8,489,2017-04-08 01:31:31,http://mcdermottkreiger.biz/alvis,0.8268573056,36.215.118.59,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n21861,8,489,2017-04-01 18:56:54,http://ritchie.info/quinn.baumbach,0.0980700460,190.78.224.205,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n21862,8,489,2017-04-12 08:33:17,http://pouros.org/emelia.romaguera,0.4240368179,225.232.21.107,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n21863,8,489,2017-04-29 05:23:49,http://stiedemannbahringer.net/audrey,0.6737351576,101.204.195.109,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n21864,8,489,2017-05-22 05:35:39,http://aufderhardicki.info/eusebio,0.7029897514,240.103.44.109,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n21865,8,489,2017-04-11 00:22:29,http://blanda.info/devon,0.1195803972,227.54.221.87,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n21866,8,489,2017-04-19 10:59:11,http://considine.io/morton.kulas,0.0301423237,39.24.83.179,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n21867,8,489,2017-03-23 07:48:17,http://pacocha.biz/aleandro.conroy,0.9878631766,155.150.103.193,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n21868,8,489,2017-04-08 21:57:27,http://schamberger.io/alanis.mills,0.8929679166,47.32.83.147,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n21869,8,489,2017-01-30 09:06:16,http://fisher.io/taylor,0.3969409985,101.225.158.22,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n21870,8,489,2017-02-13 07:02:24,http://daugherty.biz/furman,0.4926757208,91.99.210.251,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n21871,8,489,2017-06-02 11:58:15,http://rath.name/barrett.johns,0.2556695858,239.23.190.63,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n21872,8,489,2017-04-20 22:09:59,http://breitenberg.biz/adolf_kreiger,0.7862818294,220.251.97.111,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n21873,8,489,2017-04-16 09:56:52,http://shields.io/roscoe,0.9282357653,38.53.139.64,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n21874,8,489,2017-05-25 18:18:52,http://denesik.io/clement_fahey,0.2463799169,45.86.181.5,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n21875,8,489,2016-12-17 11:30:26,http://boylelangosh.name/wyatt.zboncak,0.1406781292,242.178.94.91,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n21876,8,489,2017-05-18 15:42:14,http://hilll.co/bell,0.9663702052,92.59.55.95,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n21877,8,489,2017-01-29 13:13:18,http://ohara.io/melya_schoen,0.8049172951,32.29.147.142,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n21989,8,492,2017-01-31 20:48:33,http://bauch.name/percy,,240.206.93.248,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n21878,8,489,2017-03-29 17:19:27,http://labadie.com/annie_huels,0.7538627358,109.208.147.203,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n21879,8,489,2017-02-11 03:09:37,http://rice.biz/tommie,0.9471123250,56.54.82.25,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n21880,8,489,2017-03-27 04:38:05,http://hintz.io/paris,0.7737793713,70.59.213.235,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n21881,8,489,2017-03-24 19:44:18,http://swaniawskihaley.name/regan_upton,0.6596115147,30.199.28.122,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n21882,8,489,2017-02-13 22:38:53,http://ernsercrona.io/kasandra.glover,0.5885219095,56.231.114.229,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n21883,8,489,2017-03-06 05:21:08,http://torphyconn.biz/robb_mccullough,0.9161326618,129.191.243.99,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n21884,8,489,2017-02-17 09:20:54,http://pacocha.name/eli.stark,0.6195179867,157.149.235.107,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n21885,8,489,2017-05-03 01:47:03,http://labadieherzog.co/carolina.abshire,0.1682292038,95.82.94.59,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n21886,8,489,2017-01-03 02:48:31,http://corkeryauer.biz/edgardo_murazik,0.3720517635,144.67.57.163,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n21887,8,489,2017-02-21 21:36:19,http://ondrickafeeney.org/cecelia,0.1215024428,68.194.196.190,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n21888,8,489,2017-02-03 03:13:54,http://batz.name/connie,0.5185656878,50.235.133.179,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n21889,8,489,2017-05-19 03:44:40,http://robel.net/melya.kutch,0.5430641385,89.95.12.111,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n21890,8,489,2017-03-19 09:22:09,http://ratke.co/viola.oconner,0.1777644443,164.177.182.194,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n21891,8,489,2017-01-15 16:03:31,http://haag.info/alana,0.9844566038,8.41.58.123,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n803,1,19,2017-04-13 12:20:15,http://wisozkfarrell.org/eileen,0.1171376036,208.162.29.248,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n804,1,19,2016-12-30 08:06:53,http://brownhodkiewicz.com/orie,0.0038368401,105.105.194.185,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n805,1,19,2017-04-02 13:21:45,http://wiegand.net/kari.crona,0.4246903807,141.2.183.67,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n806,1,19,2017-04-06 02:34:07,http://mcclure.net/ova.dach,0.1150460794,48.242.111.233,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n807,1,19,2017-01-27 19:42:09,http://hamill.net/matteo,0.9221379035,127.224.84.183,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n808,1,19,2017-04-26 06:36:25,http://damorebode.biz/lelia,0.1675794789,57.198.52.133,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n809,1,19,2017-05-25 08:40:48,http://marksmacejkovic.org/eloise,0.6201594615,103.78.135.172,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n810,1,19,2017-02-11 21:33:08,http://kuhic.info/ephraim.lesch,0.9769909551,146.143.15.65,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n811,1,19,2017-05-14 15:16:08,http://heathcote.io/abbigail,0.7874059671,181.162.252.55,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n812,1,19,2017-03-21 16:45:20,http://white.name/chet.welch,0.3103426479,30.244.232.219,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n813,1,19,2016-12-16 08:29:06,http://renner.biz/jerrod_kulas,0.4851849961,160.171.98.152,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n814,1,19,2016-12-26 12:38:56,http://bauchlakin.net/brook.schinner,0.9100687141,121.135.52.251,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n815,1,19,2017-04-17 18:10:29,http://gerhold.biz/jasmin,0.2489739847,195.156.69.134,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n816,1,19,2017-05-25 10:18:50,http://murazik.biz/shyann.raynor,0.5148751574,245.6.187.26,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n817,1,19,2017-03-25 06:12:38,http://fritschpfannerstill.biz/dashawn.runte,0.1385088885,190.118.15.105,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n818,1,19,2017-05-09 18:33:42,http://mclaughlinterry.info/beth_champlin,0.3268102818,236.157.108.222,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n819,1,19,2016-12-16 22:39:08,http://spencer.co/sam,0.8004854322,230.227.156.95,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n820,1,19,2017-02-24 02:55:15,http://kautzer.name/kailey.wintheiser,0.4009802227,46.70.111.40,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n821,1,19,2017-02-11 03:56:21,http://marquardt.com/abelardo,0.0467420544,228.239.191.40,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n822,1,19,2016-12-28 13:46:31,http://walshgrant.net/duane_hudson,0.8670390488,226.252.246.221,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n823,1,19,2017-04-10 00:50:50,http://dooleyfeest.com/mohamed.schroeder,0.8467371604,226.193.71.137,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n824,1,20,2017-05-22 16:18:52,http://linderdman.name/narciso,0.2097181402,116.90.57.177,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n825,1,20,2017-02-02 11:57:20,http://stamm.biz/keaton,0.0187961612,80.15.233.95,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n826,1,20,2016-12-29 04:23:43,http://mccullough.org/flavio.jones,0.1148091326,90.136.10.47,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n827,1,20,2017-02-19 18:06:55,http://lesch.com/lucie,0.5494479207,245.112.117.49,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n828,1,20,2017-03-23 21:20:33,http://romaguera.net/modesta_gerhold,0.2888483720,236.139.144.223,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n829,1,20,2017-03-30 13:00:15,http://stehrbeahan.name/astrid_pfannerstill,0.8905790955,58.24.249.144,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n830,1,20,2016-12-13 23:09:47,http://rice.info/vesta,0.4011229174,75.84.251.235,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n831,1,20,2017-02-15 13:23:45,http://kertzmann.info/beverly.lebsack,0.4227755444,14.36.229.219,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n832,1,20,2017-01-22 00:18:04,http://fritsch.org/devan_crona,0.1649725299,113.240.242.97,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n833,1,20,2017-05-25 15:49:31,http://larson.org/maegan_cain,0.2348967356,84.96.199.21,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n834,1,20,2017-01-08 15:57:12,http://fadel.info/kurt.kemmer,0.0386258288,64.100.244.8,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n835,1,20,2017-04-05 16:39:35,http://roberts.io/ryleigh,0.4347350402,249.77.216.225,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n836,1,20,2017-04-14 10:55:19,http://ornerdman.io/clemens,0.8306348925,164.155.175.111,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n837,1,20,2017-02-27 10:54:49,http://nolanritchie.name/dixie,0.3079787572,84.9.127.128,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n838,1,20,2017-02-23 12:53:22,http://boscowaelchi.com/mireya_streich,0.2183921473,92.62.213.126,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n839,1,20,2017-01-22 23:43:28,http://rau.com/abdullah.wolf,0.3966417210,66.203.244.64,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n840,1,20,2017-02-03 02:58:22,http://kaulke.com/adelle,0.9115346182,219.30.59.67,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n841,1,20,2017-02-28 21:00:01,http://price.co/josue,0.7539135725,76.110.79.197,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n842,1,20,2017-06-09 18:52:13,http://mcglynnmoen.org/zackary.zieme,0.6834245460,84.72.155.230,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n843,1,20,2017-05-31 20:15:42,http://kulas.biz/drake.abshire,0.5185080765,179.92.167.152,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n844,1,20,2017-05-01 02:02:48,http://swaniawskicole.info/yeenia,0.8779527512,28.85.72.84,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n845,1,20,2017-05-28 21:00:35,http://kertzmann.com/eugene.wehner,0.1402232253,223.111.17.130,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n846,1,20,2017-05-07 21:47:14,http://green.net/marc_casper,0.8514443337,164.114.247.117,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n847,1,20,2017-01-03 17:34:28,http://kozey.name/chasity.zulauf,0.3450031712,81.28.120.162,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n848,1,20,2017-01-04 15:30:56,http://kaulke.org/walton_okon,0.6617639823,94.229.142.156,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n849,1,20,2017-04-20 09:01:07,http://olson.com/velma,0.7203800481,62.249.135.202,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n850,1,20,2016-12-22 19:05:27,http://labadie.biz/allene.will,0.0024943608,225.106.68.173,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n851,1,20,2017-04-16 05:08:10,http://ward.biz/cindy,0.6024942474,166.124.159.5,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n852,1,20,2017-05-19 09:22:25,http://olson.com/carmel,0.1488134424,52.166.125.151,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n853,1,20,2017-05-25 21:38:48,http://vandervortrippin.org/ernestine.howe,0.2650837840,214.64.194.46,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n854,1,20,2017-05-25 18:20:16,http://stiedemann.biz/mustafa,0.0263490493,209.151.221.78,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n21892,8,489,2016-12-17 06:30:08,http://ortiz.com/alda,0.6023445224,58.233.49.18,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n21893,8,489,2017-02-23 19:58:32,http://goyette.co/geovanny,0.2007577124,247.227.121.48,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n21894,8,489,2017-05-07 20:27:42,http://jaskolski.info/remington.murray,0.8427672193,216.120.140.230,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n21895,8,489,2016-12-14 16:15:44,http://pagac.net/chance_stamm,0.5350730661,145.66.152.177,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n21896,8,489,2017-03-05 23:11:21,http://grimes.name/stan_mayer,0.4073511901,209.13.85.126,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n21897,8,489,2017-04-17 01:42:27,http://hauckhills.io/makenna.parisian,0.7167090787,5.133.87.153,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n21898,8,489,2017-02-22 03:19:32,http://auer.biz/shawn.carter,0.4184733219,157.96.77.129,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n21899,8,489,2017-02-24 16:32:08,http://mcdermott.net/alycia_witting,0.9910156809,152.58.187.54,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n21900,8,489,2017-01-12 23:43:53,http://bogan.info/jadon,0.3330589334,52.235.96.235,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n21901,8,489,2017-02-06 21:42:25,http://huels.co/evie.steuber,0.3816273574,224.48.30.177,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n21902,8,489,2016-12-27 23:27:22,http://wisozk.org/cortney.mckenzie,0.5390909976,38.162.67.196,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n21903,8,489,2017-05-10 06:12:22,http://huel.name/frieda_schmeler,0.8055094695,37.243.129.95,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n21904,8,489,2017-02-19 18:18:10,http://tremblay.com/rosalia,0.0770557598,37.98.139.230,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n21905,8,489,2017-03-20 05:48:54,http://gleichnerrohan.biz/tyrell,0.0105190866,163.94.82.60,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n21906,8,489,2017-01-18 22:27:03,http://hand.com/annamae_padberg,0.0083998587,228.139.223.102,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n21907,8,489,2017-06-12 03:08:48,http://hane.name/jarvis,0.2548298667,141.237.19.173,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n21908,8,489,2017-01-11 20:09:50,http://williamsonsteuber.net/darrick,0.1037917908,177.105.138.239,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n21909,8,489,2017-01-18 14:07:40,http://lubowitz.info/markus_reichel,0.2059291003,237.145.214.151,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n21910,8,489,2017-04-20 12:55:00,http://koelpin.org/carolyne_hayes,0.0381646134,121.96.157.132,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n21911,8,489,2017-04-06 23:29:15,http://zemlakhirthe.co/amanda,0.7584330896,114.219.142.97,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n21912,8,489,2016-12-13 11:17:15,http://grimeskilback.biz/selmer.will,0.0357965345,160.224.220.223,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n21913,8,490,2016-12-27 21:21:14,http://bode.io/sunny.langworth,0.3109579430,11.19.6.197,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n21914,8,490,2017-02-18 11:21:18,http://greenfelder.net/vicente.kilback,0.6957801000,109.46.5.30,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n21915,8,490,2017-03-04 15:29:55,http://larkin.net/rubie_ernser,0.3807046908,60.179.26.13,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n21916,8,490,2017-01-07 11:43:12,http://murphy.name/bennie_bednar,0.5598230178,132.98.39.221,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n21917,8,490,2017-01-03 05:21:52,http://damore.co/opal_bogan,0.0406417262,188.109.125.54,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n21918,8,490,2017-01-06 15:13:25,http://rogahnwisoky.io/dell_okon,0.7292600498,2.88.140.238,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n21919,8,490,2017-04-29 22:42:59,http://torphy.com/tiara_graham,0.8622101786,205.91.89.76,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n21920,8,490,2017-02-26 06:39:19,http://ullrich.net/kurtis,0.7291428433,78.202.19.171,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n21921,8,490,2017-03-19 00:15:19,http://schmitt.co/toney,0.3567097994,151.101.195.146,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n21922,8,490,2017-04-26 15:37:57,http://stoltenbergkohler.biz/orland.crona,0.7370917642,158.36.48.59,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n21923,8,490,2017-05-27 17:23:56,http://harvey.co/sherwood,0.2529324571,80.28.183.176,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n21924,8,490,2017-02-10 19:26:19,http://harber.biz/irving,0.0035304287,142.83.111.54,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n21925,8,490,2017-02-10 01:27:57,http://schuppe.name/mara_reichert,0.6799832781,231.41.200.41,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n21926,8,490,2017-01-04 13:40:54,http://jacobi.io/narciso.gibson,0.2341808101,102.175.191.121,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n21927,8,490,2016-12-13 19:14:40,http://greenholt.biz/murphy.lind,0.1048775930,208.176.155.69,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n21928,8,490,2016-12-31 08:06:01,http://wuckertwiegand.co/trever,0.1052124652,181.34.126.222,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n21929,8,490,2017-03-25 09:27:24,http://macejkovictreutel.co/tia_moriette,0.6995864642,61.82.126.23,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n21930,8,490,2017-04-15 16:45:22,http://jast.com/gonzalo,0.9048051221,238.85.151.180,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n21931,8,490,2017-03-06 05:23:43,http://smith.name/keeley,0.3379483722,2.171.16.120,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n21932,8,490,2016-12-14 00:50:04,http://emard.biz/rosanna_armstrong,0.7517466397,159.199.166.21,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n21933,8,490,2017-02-21 15:55:50,http://reynolds.com/mikel.kovacek,0.4558595844,253.158.68.238,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n21934,8,490,2017-04-23 00:28:49,http://fadel.net/alene,0.9781206776,61.99.159.15,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n21935,8,490,2017-04-25 03:40:24,http://balistreri.org/ferne,0.8138392533,136.212.201.102,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n21936,8,490,2017-05-14 03:59:18,http://steuber.com/burdette,0.2134963163,201.228.116.68,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n21937,8,490,2017-01-17 16:29:58,http://smith.co/hilma,0.8588099612,151.55.132.197,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n21938,8,490,2017-03-01 04:40:06,http://wiza.info/corbin,0.9284762098,64.61.36.16,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n21939,8,490,2017-04-05 04:38:03,http://stantonmetz.name/anibal,0.6200608947,115.40.239.252,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n21940,8,490,2017-02-13 09:48:05,http://purdy.org/korey,0.5614479491,76.185.162.10,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n21941,8,490,2017-02-17 20:11:38,http://shields.info/sophia,0.6259998419,203.19.244.226,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n21942,8,490,2017-02-09 19:30:12,http://boyerkerluke.name/tyrese_mayer,0.8148552727,254.208.112.78,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n855,1,20,2017-04-24 07:18:39,http://kautzerborer.co/vaughn,0.3189389165,198.21.149.180,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n856,1,20,2016-12-27 01:38:13,http://welch.net/jeff_heaney,0.9219788862,144.150.139.14,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n857,1,20,2017-06-05 20:20:29,http://terry.io/jada_little,0.6553629451,72.242.165.2,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n858,1,20,2016-12-23 12:45:31,http://lubowitz.info/destinee.pacocha,0.6747019281,148.231.191.83,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n859,1,20,2017-06-04 05:43:00,http://kerluke.com/olen,0.1600807104,205.55.12.116,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n860,1,20,2017-03-22 14:18:40,http://leannon.biz/sammie.stoltenberg,0.5504225740,89.157.24.40,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n861,1,20,2017-03-14 15:40:45,http://sengermarvin.com/roxane,0.7807496842,115.188.252.28,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n862,1,20,2017-03-22 15:43:22,http://andersonhoeger.org/darwin_rippin,0.5197891442,100.6.191.47,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n863,1,20,2017-05-03 13:38:30,http://ziemann.org/adela_murazik,0.3687506406,236.127.245.147,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n864,1,20,2017-01-20 05:42:22,http://medhursthaley.biz/joanie,0.5862176499,61.175.181.96,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n865,1,20,2017-01-11 03:08:34,http://lubowitzoconnell.name/alexis_cummings,0.1274407045,223.59.245.128,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n866,1,20,2017-06-06 22:36:57,http://vonruedensanford.io/markus,0.7750341961,168.74.156.71,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n867,1,20,2017-05-07 07:50:48,http://bergstrom.biz/dahlia.lehner,0.6143930161,189.187.141.254,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n868,1,20,2017-02-12 21:32:37,http://roob.org/joany.hagenes,0.4636330655,136.225.253.135,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n869,1,20,2016-12-25 12:16:45,http://pfannerstillmertz.name/marguerite,0.5750866905,107.226.151.230,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n870,1,20,2017-06-06 16:56:53,http://jacobi.co/waino,0.2855380353,37.229.91.190,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n871,1,20,2017-05-06 16:39:22,http://hodkiewicz.io/addison,0.5566513299,78.89.10.249,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n872,1,20,2017-04-28 07:49:51,http://oconnellprosacco.biz/gia,0.1959820837,162.193.169.182,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n873,1,20,2017-03-07 17:38:47,http://wilderman.name/citlalli_turner,0.8670163239,72.175.161.3,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n874,1,20,2017-01-09 07:47:53,http://kunde.biz/gabriel.volkman,0.8305926303,26.119.129.24,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n875,1,20,2017-02-22 16:51:06,http://reinger.co/ari.block,0.9663057902,28.220.159.216,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n876,1,20,2017-03-25 14:22:59,http://keebler.io/jarret,0.3390688536,203.102.23.158,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n877,1,20,2017-03-12 00:21:54,http://wiza.biz/jaren.zulauf,0.9209901300,169.172.176.27,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n878,1,20,2017-02-06 17:42:33,http://yoststamm.biz/coby,0.8376356693,57.130.9.179,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n879,1,20,2017-05-23 23:31:22,http://bernhard.co/ashton,0.7039780780,251.123.36.95,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n880,1,20,2017-05-19 22:21:58,http://cain.org/tania.nienow,0.5293478831,21.24.240.245,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n881,1,20,2017-01-27 10:29:49,http://nienow.org/rosalee.jacobs,0.4032274376,136.180.98.14,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n882,1,20,2017-01-04 21:49:06,http://sauer.net/cathryn.berge,0.8179447191,128.227.45.100,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n883,1,20,2017-01-21 11:33:22,http://fritsch.net/zelda,0.6736928933,88.221.30.250,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n884,1,21,2017-01-30 00:05:30,http://bednar.com/andreanne,,245.227.7.244,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n885,1,21,2017-02-17 07:30:35,http://vonrueden.org/alvera,,5.134.42.97,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n886,1,21,2017-02-02 13:49:25,http://sipesking.co/paul_emmerich,,225.168.159.171,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n887,1,21,2017-03-20 23:01:47,http://murrayzulauf.org/michelle_jaskolski,,91.217.45.213,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n888,1,21,2017-01-18 17:03:50,http://roobwillms.com/marion.nikolaus,,19.19.78.170,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n889,1,21,2017-02-24 05:29:22,http://grant.net/meda_romaguera,,147.206.10.180,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n890,1,21,2017-02-23 06:17:47,http://ruel.biz/johnny_carroll,,148.87.172.183,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n891,1,21,2017-04-13 03:08:36,http://kaulke.net/genevieve,,224.127.60.22,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n892,1,21,2017-01-20 21:49:48,http://runte.net/jerry_bechtelar,,138.74.139.5,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n893,1,21,2017-01-07 10:18:29,http://auer.info/lucius.breitenberg,,206.176.71.168,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n894,1,21,2016-12-20 17:44:34,http://borer.org/vincenzo_thiel,,244.165.137.186,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n895,1,21,2017-01-04 20:44:54,http://feest.co/natasha_mcdermott,,107.178.183.138,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n896,1,21,2017-05-04 21:47:25,http://schimmel.net/zakary,,204.109.147.66,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n897,1,21,2017-02-18 10:02:10,http://effertz.biz/joy,,197.217.230.96,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n898,1,21,2017-03-20 12:30:59,http://waelchi.io/frederic,,222.141.161.25,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n899,1,21,2017-03-28 03:39:59,http://nicolaswolf.biz/maximus_spencer,,184.180.61.80,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n900,1,21,2017-04-29 01:18:09,http://skilesbosco.net/rosetta_kiehn,,102.151.221.141,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n901,1,21,2016-12-27 19:10:41,http://cruickshank.biz/carole.little,,144.111.102.123,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n902,1,21,2017-05-01 15:58:32,http://framikeler.org/jayce.zulauf,,48.131.78.160,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n903,1,21,2017-05-27 10:06:35,http://lubowitz.org/odell.sauer,,136.226.141.190,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n904,1,21,2017-05-26 12:11:55,http://cormier.info/oran.wilderman,,176.182.15.22,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n905,1,21,2017-02-20 11:25:10,http://adams.co/daisy,,116.85.107.14,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n906,1,21,2017-01-27 16:57:04,http://towne.io/eldred,,127.134.204.109,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n907,1,21,2017-03-05 19:47:31,http://rodriguezluettgen.co/otilia.erdman,,92.63.244.180,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n908,1,21,2017-05-23 11:13:38,http://renner.net/melvin.bins,,103.5.249.148,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n21943,8,490,2017-04-30 13:14:37,http://heller.io/reva_leuschke,0.6230696998,245.250.106.74,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n21944,8,490,2017-03-10 13:46:09,http://abshirecummings.net/breana,0.8880883751,201.173.104.84,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n21945,8,490,2017-05-12 22:36:21,http://prosaccohegmann.org/jeremie.reynolds,0.8660166605,18.52.109.187,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n21946,8,490,2017-04-20 02:38:32,http://johns.info/jeika,0.7113250017,81.61.139.203,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n21947,8,490,2017-03-27 15:02:14,http://kilback.biz/jaycee.moen,0.3421443151,211.223.91.223,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n21948,8,490,2017-01-24 10:18:37,http://raynorhegmann.org/andy,0.1405840203,182.202.228.145,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n21949,8,490,2017-03-30 01:16:43,http://vandervort.org/jeffry_strosin,0.9350835244,90.195.161.188,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n21950,8,491,2017-03-02 19:15:09,http://champlin.org/deie,,234.8.246.49,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n21951,8,491,2017-01-07 12:49:51,http://hand.info/hildegard,,68.125.71.247,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n21952,8,491,2017-01-20 23:42:23,http://bashirian.com/trent_batz,,119.154.206.69,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n21953,8,491,2016-12-16 19:36:00,http://binstromp.biz/arvid,,115.119.55.236,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n21954,8,491,2017-02-03 22:50:08,http://sawayncruickshank.net/bria,,221.179.170.60,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n21955,8,491,2016-12-17 01:05:51,http://schadenmann.com/coralie,,38.86.118.172,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n21956,8,491,2017-05-05 21:36:02,http://reichert.org/anabel.spencer,,89.77.173.5,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n21957,8,491,2016-12-13 09:28:09,http://donnellylabadie.com/antonietta,,181.6.59.27,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n21958,8,491,2017-01-31 10:39:09,http://adamsking.com/abraham_wyman,,19.190.115.115,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n21959,8,491,2017-03-21 06:23:19,http://prohaska.biz/vincenza.gutkowski,,54.112.138.141,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n21960,8,491,2016-12-15 23:05:37,http://hettinger.com/harrison.hodkiewicz,,36.99.142.163,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n21961,8,491,2016-12-20 15:00:00,http://kozey.net/mandy.prosacco,,32.167.84.95,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n21962,8,491,2017-03-19 02:49:38,http://cremin.name/nannie.konopelski,,151.214.16.82,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n21963,8,491,2016-12-28 22:29:23,http://bahringer.info/palma,,133.94.148.190,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n21964,8,491,2017-05-29 21:22:47,http://blick.org/graciela_green,,47.195.57.154,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n21965,8,491,2017-03-10 01:46:06,http://jakubowski.net/carley,,146.20.27.20,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n21966,8,491,2017-04-05 10:42:48,http://bartoletti.biz/giovanni_buckridge,,254.77.2.32,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n21967,8,491,2017-02-12 14:31:54,http://grimesboyle.biz/dorothy,,89.124.68.64,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n21968,8,491,2016-12-25 21:19:27,http://jastroberts.info/lonzo,,49.106.162.10,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n21969,8,491,2017-04-15 19:14:36,http://casper.name/esther.morar,,210.164.116.167,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n21970,8,491,2017-03-02 19:28:50,http://thompson.biz/laurine,,222.124.33.186,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n21971,8,491,2017-02-05 03:27:21,http://ernser.biz/pearlie_hyatt,,171.25.119.156,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n21972,8,491,2017-05-01 21:56:48,http://hintz.org/stacy.oconner,,250.227.210.90,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n21973,8,491,2017-03-10 08:19:15,http://oreilly.co/esta,,228.18.5.215,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n21974,8,491,2017-04-19 15:03:34,http://hanebeahan.net/xavier,,107.232.112.244,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n21975,8,491,2016-12-17 20:28:19,http://hegmann.biz/jarrod.abbott,,50.13.114.123,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n21976,8,491,2016-12-21 18:51:17,http://thiel.biz/norma_schmitt,,106.155.103.236,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n21977,8,491,2017-04-29 00:53:28,http://bosco.net/maryam,,103.28.239.126,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n21978,8,491,2017-02-06 13:27:52,http://klein.io/wendell,,118.210.40.222,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n21979,8,491,2017-02-12 22:51:54,http://leannon.co/laurie,,120.232.60.19,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n21980,8,491,2017-03-12 20:57:41,http://mohrlarson.info/henry.emmerich,,38.242.154.219,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n21981,8,491,2017-01-19 07:42:06,http://littel.com/kaley,,38.176.207.118,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n21982,8,491,2017-06-11 02:16:09,http://balistreri.org/estrella,,101.60.159.103,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n21983,8,491,2017-03-01 03:47:17,http://streich.org/arlo.schmitt,,27.103.205.92,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n21984,8,491,2017-05-05 09:15:20,http://runolfon.biz/deborah,,171.136.111.246,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n21985,8,491,2017-03-31 02:54:54,http://haley.biz/matt_hermann,,64.206.251.151,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n21986,8,492,2017-01-11 00:51:48,http://monahan.co/nat,,243.155.198.230,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n21987,8,492,2017-02-01 10:52:23,http://streichreichel.co/marvin.donnelly,,209.3.113.85,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n21988,8,492,2016-12-31 11:16:37,http://purdy.net/jamal,,172.159.253.45,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n21990,8,492,2017-04-30 00:26:16,http://gerlach.biz/silas.heathcote,,171.85.254.136,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n21991,8,492,2017-01-12 02:59:11,http://marks.com/roderick_bernhard,,226.186.156.92,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n21992,8,492,2016-12-22 22:57:40,http://leuschke.co/sasha,,112.111.95.177,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n21993,8,492,2017-02-09 23:30:26,http://kerlukebrown.org/yvonne_white,,77.201.183.178,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n21994,8,492,2016-12-14 07:19:07,http://hammesblanda.co/neal.king,,177.54.237.98,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n21995,8,492,2017-01-24 11:01:48,http://boyle.info/winnifred.schultz,,2.16.168.30,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n21996,8,492,2017-03-02 14:00:31,http://mohr.biz/kyle.koch,,191.86.44.17,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n21997,8,492,2016-12-22 23:49:15,http://jenkinsmuller.io/leilani_skiles,,157.53.197.53,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n21998,8,492,2017-06-08 03:28:27,http://borerkreiger.org/golden,,105.63.17.157,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n909,1,21,2017-03-25 18:19:29,http://thiel.co/aliya,,246.207.196.120,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n910,1,21,2017-01-29 18:23:39,http://labadie.info/lavonne,,195.192.253.246,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n911,1,21,2017-01-29 19:38:23,http://mayer.io/courtney_batz,,109.45.12.8,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n912,1,21,2017-02-17 18:11:04,http://kerlukezemlak.info/jakob.swaniawski,,8.208.25.34,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n913,1,21,2017-03-13 01:59:33,http://torphy.org/keon.hills,,67.159.164.144,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n914,1,21,2017-02-26 20:33:12,http://glover.com/lois_welch,,62.174.187.200,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n915,1,21,2017-06-11 07:38:50,http://von.org/torrey,,229.221.121.156,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n916,1,21,2017-03-17 12:40:53,http://thompson.com/jaydon_crist,,37.16.73.69,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n917,1,21,2017-05-13 18:45:33,http://funk.biz/kaycee_rolfson,,127.172.41.139,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n918,1,21,2017-06-10 02:40:40,http://gerlach.biz/albina_durgan,,208.178.215.93,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n919,1,21,2016-12-14 08:52:14,http://halvorsonkoepp.name/nova,,171.231.245.235,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n920,1,21,2017-03-30 14:37:36,http://doylekautzer.biz/adrien.harvey,,241.55.235.183,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n921,1,21,2017-04-03 18:02:36,http://stromansenger.info/freddy_williamson,,79.242.82.122,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n922,1,21,2017-05-21 17:55:24,http://ratke.com/edmond,,188.106.77.223,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n923,1,21,2017-05-15 05:35:59,http://medhurst.org/ruel.robel,,237.34.84.211,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n924,1,22,2017-01-11 18:02:14,http://dickinson.biz/geoffrey,,26.169.12.130,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n925,1,22,2017-03-18 08:26:23,http://keebler.co/violet,,16.193.193.97,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n926,1,22,2017-04-24 02:56:16,http://schaefer.name/vernice.jones,,229.150.65.14,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n927,1,22,2017-01-16 13:30:10,http://roberts.org/eloy,,53.179.121.21,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n928,1,22,2017-04-16 14:12:55,http://runolfsdottirstehr.io/pansy.lakin,,149.33.246.110,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n929,1,22,2017-01-20 22:03:09,http://kreigerfisher.net/sven.lakin,,149.30.232.202,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n930,1,22,2017-05-20 08:33:22,http://heller.co/pablo,,100.224.241.216,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n931,1,22,2017-02-18 05:56:40,http://ritchie.biz/bernita_hintz,,195.22.12.6,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n932,1,22,2017-03-05 00:17:47,http://bruen.com/columbus_schmeler,,95.75.144.22,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n933,1,22,2017-03-24 22:58:40,http://wolff.info/claude,,146.154.42.183,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n934,1,22,2017-03-10 10:36:11,http://trompkirlin.org/jeffry,,230.151.197.172,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n935,1,22,2017-05-19 23:37:48,http://hodkiewicz.name/jo,,229.46.99.4,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n936,1,22,2017-04-29 18:49:33,http://hermiston.com/kadin.deckow,,12.29.17.12,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n937,1,22,2016-12-31 16:01:59,http://ornnolan.name/antone.ondricka,,167.244.212.68,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n938,1,22,2017-05-24 20:59:45,http://ernser.com/amelie.mosciski,,28.232.152.138,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n939,1,22,2017-04-09 17:28:15,http://lemke.net/sheila_bogisich,,28.23.97.124,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n940,1,22,2017-02-15 19:44:26,http://stokes.org/olaf,,233.248.190.222,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n941,1,22,2017-06-08 06:48:05,http://fritsch.biz/natalie,,199.140.68.62,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n942,1,22,2017-04-23 21:54:02,http://wyman.net/patrick_lind,,95.170.190.50,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n943,1,22,2017-02-03 05:48:09,http://rohan.com/evans,,213.61.68.163,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n944,1,22,2017-01-15 18:30:46,http://schinnermcdermott.com/dina,,107.92.242.229,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n945,1,22,2017-01-21 11:29:47,http://yundt.biz/okey.nader,,232.130.176.75,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n946,1,22,2017-03-25 15:25:42,http://crona.info/gerhard,,38.215.67.157,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n947,1,22,2016-12-16 01:43:52,http://schowalter.co/jayde,,65.94.90.72,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n948,1,22,2017-04-12 00:52:47,http://dickenskiehn.io/lisette,,43.211.71.223,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n949,1,22,2017-04-21 08:31:50,http://homenick.net/darius,,59.193.153.48,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n950,1,22,2017-06-14 01:27:26,http://mertz.net/johnnie,,45.16.135.53,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n951,1,22,2017-05-21 05:32:20,http://leuschke.biz/torrey.crist,,17.133.156.126,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n952,1,23,2017-03-12 11:04:57,http://hoppewhite.info/santino,,218.184.196.22,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n953,1,23,2016-12-30 21:35:41,http://kihn.com/novella.simonis,,44.11.84.53,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n954,1,23,2017-01-21 19:27:20,http://faheyturcotte.name/ericka,,144.78.75.75,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n955,1,23,2017-06-01 15:29:48,http://grimesmarquardt.org/orlando,,94.190.148.175,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n956,1,23,2017-02-23 02:41:52,http://thompsontillman.org/marcelina,,133.220.148.154,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n957,1,23,2017-03-12 20:41:28,http://vonrueden.org/morris.ledner,,187.28.71.118,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n958,1,23,2017-05-05 08:03:24,http://nitzsche.net/jakob,,253.121.59.225,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n959,1,23,2017-03-24 23:02:06,http://veum.name/priscilla,,211.40.30.248,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n960,1,23,2017-02-17 03:29:04,http://adams.io/bernita,,87.142.233.21,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n961,1,23,2017-04-20 16:06:13,http://erdmanpowlowski.co/paula_little,,105.194.92.204,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n962,1,23,2017-02-06 13:14:33,http://emard.com/paris,,103.16.114.113,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n963,1,23,2017-04-20 07:32:30,http://macgyverchamplin.biz/cielo,,218.62.30.246,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n964,1,23,2017-05-13 21:00:59,http://fisher.co/gunnar_fritsch,,33.245.152.92,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n965,1,23,2017-02-16 06:39:39,http://weimann.co/raoul,,149.174.153.95,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n21999,8,492,2017-06-06 11:38:54,http://bechtelar.com/reanna.powlowski,,247.62.67.113,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n22000,8,492,2017-01-27 15:49:36,http://ondricka.biz/gabriel,,35.136.67.33,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n22001,8,492,2017-03-30 15:13:45,http://rau.name/adriana,,164.4.67.37,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n22002,8,492,2017-04-14 07:24:11,http://marks.info/helmer,,186.22.41.30,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n22003,8,492,2017-04-25 16:40:19,http://boehmmcdermott.name/theresia.kertzmann,,121.81.248.24,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n22004,8,492,2017-04-02 02:16:56,http://frami.org/ahmed_mcglynn,,240.254.239.236,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n22005,8,492,2017-01-08 06:52:46,http://kutch.biz/deion_moore,,168.207.235.116,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n22006,8,492,2017-03-24 04:42:43,http://jerdeyundt.org/cletus,,232.73.63.137,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n22007,8,492,2017-04-07 15:58:39,http://parkereffertz.com/roslyn,,252.167.21.174,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n22008,8,492,2017-06-06 02:10:06,http://pacocha.info/easter.zulauf,,200.126.213.154,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n22009,8,492,2016-12-29 07:27:57,http://gleichner.io/kelly,,65.170.185.214,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n22010,8,492,2017-05-14 17:03:41,http://simonis.io/giles_farrell,,103.205.212.172,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n22011,8,492,2017-04-09 19:57:51,http://luettgen.org/harmony,,84.226.91.111,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n22012,8,492,2017-02-11 11:26:52,http://weinat.co/gaetano_okon,,97.149.115.79,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n22013,8,492,2017-02-17 08:03:46,http://deckowkunze.name/leilani,,45.120.152.125,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n22014,8,492,2017-05-15 07:55:24,http://legros.com/jarrell.hoppe,,32.167.82.157,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n22015,8,492,2016-12-24 17:16:41,http://murazik.net/kenyon,,205.188.73.202,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n22016,8,492,2017-04-03 11:40:46,http://connelly.com/sofia.crona,,114.166.47.204,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n22017,8,492,2017-04-16 06:03:17,http://gottlieb.info/roslyn,,249.178.103.196,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n22018,8,492,2017-04-27 21:30:09,http://turnertrantow.name/sister_kertzmann,,161.132.22.220,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n22019,8,492,2017-04-16 02:04:50,http://hegmann.co/luisa,,38.18.62.64,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n22020,8,492,2016-12-31 09:53:08,http://schulist.info/mackenzie_hermann,,218.193.121.32,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n22021,8,493,2017-03-30 13:57:37,http://abbottnicolas.org/jovanny_fritsch,,116.237.233.225,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n22022,8,493,2016-12-21 04:18:21,http://toy.net/dedrick,,167.60.43.229,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n22023,8,493,2017-04-16 08:18:39,http://funk.io/emmalee,,219.163.49.45,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n22024,8,493,2017-01-29 16:03:42,http://dickimosciski.com/tyshawn.denesik,,186.155.110.97,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n22025,8,493,2017-05-29 11:17:37,http://adams.info/marcelle,,37.188.174.192,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n22026,8,493,2017-06-12 22:11:35,http://langworth.co/annabel.murazik,,29.112.194.98,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n22027,8,493,2017-02-14 02:54:34,http://quitzongerlach.co/lazaro,,111.158.102.106,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n22028,8,493,2017-03-23 11:53:05,http://lehnerhuels.net/rosie_senger,,13.57.137.29,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n22029,8,493,2017-06-06 03:30:16,http://gerlach.name/raphaelle_herman,,158.221.125.212,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n22030,8,493,2017-06-10 03:26:42,http://waelchi.name/efrain_cronin,,222.12.97.124,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n22031,8,493,2017-03-04 04:42:15,http://swift.io/raven_dooley,,189.204.49.14,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n22032,8,493,2016-12-30 07:04:16,http://hicklebayer.info/clemmie_metz,,49.248.226.107,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n22033,8,493,2017-04-03 23:44:54,http://schoen.io/alyce,,135.251.186.23,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n22034,8,493,2017-02-07 20:37:04,http://christiansen.co/oswald_powlowski,,144.46.13.142,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n22035,8,493,2017-01-28 19:50:57,http://schimmel.name/aric.hayes,,206.50.193.114,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n22036,8,493,2017-04-16 15:52:39,http://halvorsongoyette.org/adolf_will,,105.216.211.43,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n22037,8,493,2017-02-28 21:43:48,http://rolfsonruecker.io/serena,,84.160.224.53,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n22038,8,493,2017-02-22 02:26:09,http://nikolauwaniawski.name/fabiola,,41.162.226.178,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n22039,8,493,2017-03-25 11:13:14,http://lemke.co/jimmy.olson,,174.54.171.4,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n22040,8,493,2017-02-07 05:52:00,http://mcdermott.net/wendy,,58.117.53.214,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n22041,8,493,2017-05-21 16:11:43,http://greenbernhard.info/alaina_bailey,,144.153.229.244,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n22042,8,493,2017-05-15 03:08:54,http://emmerich.name/ruell_weber,,99.33.62.55,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n22043,8,493,2016-12-17 15:30:58,http://wintheiser.name/frederick,,48.26.139.25,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n22044,8,493,2017-02-16 11:02:33,http://weimann.org/anais,,19.234.134.247,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n22045,8,493,2017-02-17 01:16:23,http://lakin.org/joyce,,201.19.61.10,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n22046,8,493,2017-05-10 11:27:58,http://denesik.info/ottis,,151.45.8.74,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n22047,8,493,2017-04-30 16:29:23,http://gottlieb.name/eulalia,,127.65.97.95,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n22048,8,493,2017-02-19 08:03:56,http://donnelly.biz/flavio,,180.220.98.82,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n22049,8,493,2017-05-17 00:44:10,http://collins.org/rylee,,79.182.78.127,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n22050,8,493,2017-01-17 02:34:05,http://bruen.co/davonte_streich,,154.180.132.174,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n22051,8,493,2017-03-07 23:04:13,http://welch.com/blake_romaguera,,187.129.71.140,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n22052,8,493,2017-04-26 10:33:47,http://kautzergreen.net/patsy_hoeger,,158.30.59.226,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n22053,8,493,2017-03-22 19:08:58,http://ullrichgreenholt.org/wendell,,215.253.125.219,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n966,1,23,2016-12-28 00:17:57,http://granthermiston.biz/augustine.hauck,,158.80.65.30,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n967,1,23,2017-01-21 04:25:22,http://collier.net/beverly_vonrueden,,160.122.117.59,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n968,1,23,2017-03-15 10:42:17,http://bauchcormier.co/morris,,115.24.123.250,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n969,1,23,2017-01-21 13:13:13,http://veum.net/alta,,3.174.190.99,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n970,1,23,2017-05-24 23:20:58,http://corwin.biz/alayna_fahey,,210.126.89.200,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n971,1,23,2017-06-13 22:49:37,http://mcdermott.biz/sandrine,,24.190.88.153,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n972,1,23,2017-01-08 09:58:17,http://lockman.org/hans_ullrich,,82.182.187.175,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n973,1,23,2017-02-08 16:43:44,http://bartolettikshlerin.net/mavis.steuber,,39.44.143.75,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n974,1,23,2016-12-27 05:36:27,http://erdman.info/linwood.mraz,,59.242.87.107,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n975,1,23,2016-12-20 14:15:08,http://douglas.name/ashlynn_abbott,,205.254.50.101,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n976,1,23,2017-05-08 01:06:32,http://feil.net/ava,,202.110.184.181,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n977,1,23,2017-02-20 00:29:59,http://reichel.biz/clemens,,209.9.110.159,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n978,1,23,2017-04-05 15:59:00,http://greenfeldertremblay.io/kara,,177.14.178.11,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n979,1,23,2017-05-11 08:29:49,http://nikolaus.net/colleen,,31.34.33.182,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n980,1,23,2017-05-21 06:07:25,http://gradyjerde.biz/bartholome.bayer,,177.225.19.77,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n981,1,23,2017-05-26 10:04:48,http://tillmanjohnson.io/murray,,84.132.209.189,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n982,1,23,2017-03-31 21:59:58,http://marvin.info/eliezer_schamberger,,20.188.168.136,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n983,1,23,2017-03-30 08:22:39,http://mayert.org/reuben,,213.249.150.53,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n984,1,23,2017-05-02 16:22:06,http://romaguerahintz.io/ethel,,232.211.243.199,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n985,1,23,2017-01-23 22:56:48,http://terryrempel.org/arvid.heel,,206.50.202.66,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n986,1,24,2017-03-12 19:59:56,http://rueckerreichert.biz/cecelia,,202.224.133.252,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n987,1,24,2017-06-09 12:35:44,http://nienowzieme.net/aiden.lemke,,70.111.109.173,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n988,1,24,2017-05-14 06:46:03,http://gleason.info/violette.mayer,,121.83.83.231,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n989,1,24,2017-05-30 02:42:50,http://larkincummings.io/ettie,,119.54.36.9,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n990,1,24,2017-04-06 02:21:36,http://lakin.name/lela,,49.20.122.61,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n991,1,24,2017-04-03 06:32:53,http://okuneva.io/katherine_steuber,,107.212.42.119,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n992,1,24,2017-03-02 18:27:17,http://balistreri.net/ray,,136.31.238.136,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n993,1,24,2017-05-25 18:01:22,http://ward.name/jacques,,58.127.63.118,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n994,1,24,2016-12-31 06:01:41,http://lockmantromp.biz/euna.lang,,152.36.134.85,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n995,1,24,2017-02-25 00:57:50,http://shanahan.name/major.cartwright,,104.127.221.154,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n996,1,24,2017-01-25 19:02:29,http://terry.name/grayce,,56.216.7.77,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n997,1,24,2017-02-03 21:27:29,http://boyerbatz.co/yolanda,,5.109.158.123,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n998,1,24,2017-02-23 22:55:57,http://luettgencrist.com/colby_collins,,241.40.6.49,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n999,1,24,2017-05-23 13:24:17,http://schneider.io/alta,,172.184.32.195,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n1000,1,24,2017-03-16 00:19:30,http://schamberger.co/nikolas.hand,,83.186.216.244,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n1001,1,24,2017-05-14 07:30:36,http://pagacmiller.net/cydney.ankunding,,24.252.249.160,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n1002,1,24,2017-05-11 12:51:55,http://jacobs.info/braulio.block,,21.63.5.119,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n1003,1,24,2016-12-16 23:32:23,http://terry.biz/linwood.bergnaum,,174.210.138.206,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n1004,1,24,2017-04-14 16:31:44,http://schmeler.biz/elmira_gaylord,,210.61.12.88,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n1005,1,24,2017-03-30 20:11:26,http://mertz.name/mozelle,,49.72.126.82,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n1006,1,24,2017-04-08 19:47:17,http://gerhold.biz/graham.ebert,,228.8.176.237,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n1007,1,24,2017-02-23 09:00:15,http://greenfelder.net/paolo.sauer,,106.177.249.40,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n1008,1,24,2017-04-10 04:26:29,http://willms.net/ismael_hauck,,102.240.56.15,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n1009,1,24,2017-04-07 23:06:39,http://prosaccoaltenwerth.org/joana.boyle,,19.51.218.41,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n1010,1,24,2017-04-27 21:41:19,http://stammfranecki.biz/eloy.cole,,207.177.59.198,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n1011,1,24,2017-01-14 16:14:44,http://white.io/jorge.bauch,,50.162.169.33,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1012,1,24,2016-12-16 05:44:49,http://reillydoyle.com/owen,,202.157.4.188,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n1013,1,24,2017-05-26 12:09:48,http://effertz.co/robin.williamson,,8.246.81.65,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n1014,1,24,2016-12-30 20:22:54,http://kuphal.com/flavio,,152.125.47.179,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n1015,1,24,2017-05-20 09:03:17,http://wilkinson.co/delbert.johns,,42.113.205.169,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n1016,1,24,2017-01-16 11:31:39,http://macejkovic.biz/minerva_moore,,191.15.111.212,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n1017,1,24,2017-02-10 15:07:31,http://steuberschumm.info/chasity,,253.253.13.64,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n1018,1,24,2017-04-25 19:01:06,http://satterfield.com/forrest,,226.157.148.191,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n1019,1,24,2017-05-22 20:43:00,http://kirlin.info/ayden,,96.56.158.67,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n1020,1,24,2017-04-10 06:25:45,http://kocherdman.com/kennedi,,224.233.19.130,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n1021,1,24,2017-02-09 06:22:07,http://feest.org/tyra_wolff,,174.104.67.8,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n22054,8,493,2017-04-09 18:09:33,http://cummeratamuller.net/katherine_hilpert,,133.116.177.155,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n22055,8,493,2016-12-27 12:20:53,http://lubowitz.name/lelah,,123.67.83.25,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n22056,8,493,2017-01-08 04:09:28,http://beer.org/camila,,47.85.220.57,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n22057,8,493,2017-03-26 17:15:37,http://quigley.io/walton,,45.215.17.114,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n22058,8,493,2017-04-17 17:08:06,http://thompson.net/treie.cormier,,141.148.39.31,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n22059,8,493,2017-03-10 00:29:00,http://hodkiewiczkulas.net/derrick,,140.28.118.101,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n22060,8,493,2017-02-18 16:08:32,http://stokes.org/ofelia_jacobson,,177.119.110.86,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n22061,8,493,2017-03-29 18:34:27,http://langlowe.biz/brennan,,4.89.154.207,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n22062,8,493,2017-04-24 23:38:42,http://stamm.biz/marlin,,204.10.65.127,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n22063,8,494,2017-06-09 11:16:40,http://rau.com/jonathon_king,,125.27.45.109,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n22064,8,494,2017-04-18 13:08:19,http://walker.com/evangeline,,200.109.172.166,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n22065,8,494,2017-05-16 22:07:35,http://welch.name/pinkie_pfannerstill,,248.144.129.126,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n22066,8,494,2017-05-22 14:57:54,http://langosh.org/gonzalo,,153.98.128.98,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n22067,8,494,2017-03-23 01:18:22,http://thompson.name/lora_okeefe,,28.100.230.78,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n22068,8,494,2016-12-31 07:44:18,http://mitchell.biz/ena.ruecker,,23.126.91.188,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n22069,8,494,2017-03-06 21:23:19,http://pagacnitzsche.net/kody,,73.102.136.228,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n22070,8,494,2017-02-20 08:03:40,http://mckenzie.org/buford.bednar,,232.127.243.103,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n22071,8,494,2017-01-02 06:39:55,http://abshirewhite.org/thora,,136.72.126.15,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n22072,8,494,2017-05-17 21:59:28,http://waters.info/granville,,248.230.204.116,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n22073,8,494,2016-12-18 15:47:43,http://mante.com/nash.wilderman,,174.33.48.72,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n22074,8,494,2017-05-05 04:29:58,http://wilderman.io/tyrell,,136.193.230.240,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n22075,8,494,2017-02-15 12:02:27,http://jerde.io/hayden,,180.211.252.58,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n22076,8,494,2017-02-13 10:52:09,http://orn.info/patricia_purdy,,185.161.82.118,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n22077,8,494,2017-05-09 12:26:50,http://harvey.io/oleta,,224.214.234.239,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n22078,8,494,2017-01-24 21:42:13,http://ernsercasper.co/theron_leffler,,94.239.155.149,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n22079,8,494,2017-03-08 18:24:15,http://stanton.net/christina,,92.190.147.111,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n22080,8,494,2017-02-22 23:55:23,http://gerlach.net/davon_hilpert,,101.97.10.116,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n22081,8,494,2017-04-18 01:58:28,http://wehnerrutherford.org/claudie.conroy,,156.176.3.63,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n22082,8,494,2017-04-20 15:47:17,http://crooks.name/manley,,134.23.41.57,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n22083,8,494,2017-05-11 17:29:37,http://konopelski.co/eliseo,,244.142.71.50,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n22084,8,494,2017-03-30 12:06:49,http://haagvolkman.info/brooklyn,,226.3.81.21,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n22085,8,494,2017-03-29 16:33:17,http://heaneywelch.co/zola,,199.48.126.55,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n22086,8,494,2017-05-07 12:19:13,http://yundt.org/jose,,200.160.27.153,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n22087,8,495,2017-01-30 17:38:20,http://abbott.io/kattie.borer,,180.80.103.197,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n22088,8,495,2017-02-26 02:19:13,http://ryan.org/sigrid,,71.37.35.178,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n22089,8,495,2017-01-17 08:05:42,http://pfeffer.org/lula_towne,,163.156.65.55,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n22090,8,495,2017-05-24 08:50:08,http://witting.net/terrell,,113.196.197.220,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n22091,8,495,2017-04-03 08:45:20,http://pagac.io/gianni.prohaska,,53.13.26.57,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n22092,8,495,2016-12-20 14:16:21,http://dooleymohr.net/faustino,,205.92.168.88,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n22093,8,495,2017-05-24 16:35:22,http://konopelskikuhn.info/reba.mann,,50.148.15.52,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n22094,8,495,2017-01-07 15:04:58,http://nikolaus.com/loyce,,2.159.121.233,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n22095,8,495,2017-01-13 01:19:49,http://reilly.org/harley,,152.152.206.193,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n22096,8,495,2017-01-18 11:19:50,http://rosenbaumherman.co/electa,,14.49.48.188,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n22097,8,495,2017-02-15 14:48:40,http://gutmann.com/kali,,225.214.27.204,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n22098,8,495,2016-12-23 05:30:26,http://murphy.io/bartholome_frami,,184.66.214.13,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n22099,8,495,2017-04-20 08:33:19,http://cremin.biz/maeve.dicki,,158.23.123.7,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n22100,8,495,2017-01-13 20:40:34,http://cartertoy.io/oran.gleason,,2.111.193.126,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n22101,8,495,2017-01-16 14:53:45,http://flatley.name/sonya_crist,,220.27.231.203,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n22102,8,495,2017-02-24 13:06:42,http://bednar.info/pietro_jacobi,,91.63.25.60,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n22103,8,495,2017-05-14 02:11:58,http://komoen.org/isadore,,202.147.206.115,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n22104,8,495,2016-12-17 10:18:43,http://balistreri.co/eda,,124.141.229.125,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n22105,8,495,2017-06-06 04:36:11,http://yundt.org/flavie,,104.12.54.27,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n22106,8,495,2017-02-06 08:14:35,http://borer.biz/eliseo_stiedemann,,233.184.247.233,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n22107,8,495,2017-05-10 08:52:24,http://sanford.info/micaela.ankunding,,132.180.43.83,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n22108,8,495,2017-01-09 21:21:14,http://mayert.biz/kevon,,147.10.225.235,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n22109,8,495,2017-01-31 21:59:21,http://bahringer.info/catharine,,18.201.69.184,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n1022,1,24,2017-06-05 07:52:56,http://waters.info/emmanuelle,,170.8.171.20,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n1023,1,24,2017-01-05 17:06:18,http://keelingmccullough.biz/leone_robel,,183.96.146.199,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n1024,1,24,2017-02-12 00:39:33,http://pollich.co/ara_collier,,249.227.219.149,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n1025,1,24,2017-03-25 16:12:16,http://moriette.info/aglae_kshlerin,,243.156.17.196,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n1026,1,24,2017-03-16 22:06:50,http://kemmerleannon.net/cordia_rosenbaum,,31.44.171.174,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n1027,1,24,2017-05-10 20:24:27,http://ziemewilliamson.name/orville,,37.110.174.148,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n1028,1,24,2017-01-05 18:48:47,http://zboncak.net/hector.ebert,,78.174.166.144,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n1029,1,24,2017-05-03 18:11:52,http://zulaufupton.io/roel,,229.47.55.240,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n1030,1,24,2017-04-17 01:43:06,http://schinner.io/tobin,,127.137.114.75,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n1031,1,24,2017-06-07 08:10:36,http://bayer.co/nadia.collins,,40.81.112.178,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1032,1,24,2017-05-14 00:25:30,http://ondricka.org/kelton.treutel,,249.18.157.249,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n1033,1,24,2017-03-08 16:43:09,http://moore.info/camille,,237.20.4.228,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n1034,1,24,2017-05-24 03:00:54,http://ornokuneva.info/john,,191.171.213.168,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n1035,1,24,2017-06-08 04:38:07,http://lynchmuller.info/stefan.bechtelar,,227.53.254.2,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n1036,1,24,2017-05-10 16:56:30,http://vandervortgibson.info/eula,,124.5.51.33,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n1037,1,24,2017-01-08 07:22:37,http://aufderhar.org/clemmie,,224.47.147.31,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n1038,1,24,2016-12-21 07:28:50,http://schroeder.com/vincenzo,,142.152.43.81,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n1039,1,24,2017-05-10 03:46:23,http://nolanbernhard.io/carson_balistreri,,155.117.81.47,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n1040,1,24,2017-03-07 13:14:35,http://bosco.co/derick,,240.101.40.157,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n1041,1,24,2017-05-21 20:22:52,http://kochlesch.biz/kenny.armstrong,,90.183.251.193,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n1042,1,24,2017-02-19 02:21:12,http://schneider.name/emmet.spencer,,131.48.157.169,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n1043,1,24,2017-05-31 19:33:17,http://krajcik.net/eryn.grant,,238.163.78.138,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n1044,1,24,2017-01-03 17:14:04,http://botsford.info/nathanial,,57.150.64.12,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n1045,1,24,2017-03-05 22:08:23,http://conroy.net/ari_torphy,,220.120.113.158,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n1046,1,24,2017-04-20 19:34:23,http://weinat.com/jonathon_bartell,,247.215.136.116,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n1047,1,24,2017-03-23 16:45:46,http://wisoky.net/natalie_beatty,,23.40.252.204,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n1049,1,24,2016-12-17 00:44:41,http://jacobs.biz/isaias.rau,,226.80.88.161,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n1050,1,24,2017-05-07 11:33:21,http://rowe.org/francisco.halvorson,,57.118.31.98,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n1051,1,24,2017-05-17 12:19:43,http://halvorsonbrown.net/antonio,,24.214.117.177,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n1052,1,24,2017-03-27 18:54:08,http://welchjast.biz/tommie,,182.225.138.39,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n1053,1,24,2017-03-02 00:55:40,http://howe.co/saul,,190.24.213.254,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n1054,1,25,2017-02-14 22:14:42,http://conroy.com/jada,,224.27.231.43,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n1055,1,25,2017-01-24 13:27:36,http://hellerbergstrom.info/thomas.turner,,233.69.218.52,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n1056,1,25,2017-04-27 06:49:35,http://kutch.co/jordon,,139.46.201.117,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n1057,1,25,2017-04-16 21:03:42,http://rice.io/layne.satterfield,,173.198.85.48,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n1058,1,25,2017-02-14 23:40:21,http://kingstark.info/albina.pouros,,61.68.220.177,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n1059,1,25,2016-12-16 08:51:59,http://pouros.io/mina,,40.96.42.71,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n1060,1,25,2017-05-29 09:34:13,http://aufderhar.co/aleandro_corkery,,215.82.122.123,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n1061,1,25,2016-12-30 13:23:44,http://okunevastokes.biz/justyn.pollich,,197.137.40.144,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n1062,1,25,2017-01-04 15:34:01,http://stroman.org/evie,,80.24.87.252,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n1063,1,25,2017-02-20 10:28:35,http://labadie.biz/milan.mckenzie,,164.233.81.226,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n1064,1,25,2017-03-02 08:54:01,http://zemlaksauer.info/bailee.kovacek,,8.91.29.216,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n1065,1,25,2017-03-08 14:21:44,http://parisian.io/ines_schmitt,,90.64.200.170,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n1066,1,25,2017-04-24 09:24:34,http://langworth.com/ray.marks,,184.86.57.157,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n1067,1,25,2017-05-23 10:58:39,http://welchpacocha.name/nedra,,189.173.223.128,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n1068,1,25,2017-05-06 15:23:51,http://vonsteuber.name/frieda,,106.43.236.8,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n1069,1,25,2016-12-25 20:58:40,http://mcdermottrutherford.org/wellington,,85.175.44.175,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n1070,1,25,2017-03-04 05:36:30,http://schmidt.name/estella_boehm,,114.6.217.245,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n1071,1,25,2016-12-15 10:25:08,http://kunze.net/percy,,93.103.163.169,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n1072,1,25,2017-03-04 16:31:35,http://volkmanauer.org/amani,,226.150.133.70,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n1073,1,25,2017-04-07 20:01:38,http://wilkinson.name/eduardo,,52.221.47.70,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n1074,1,25,2016-12-14 20:17:31,http://lynchemmerich.name/richmond,,236.73.231.238,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n1075,1,25,2017-05-18 02:44:26,http://cain.com/javier_hoppe,,179.156.39.189,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n1076,1,25,2017-01-24 12:48:45,http://bernhard.info/amelie,,102.226.221.140,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n1077,1,25,2017-03-12 12:47:15,http://osinski.io/zackary.jones,,87.42.77.229,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n1078,1,25,2016-12-28 19:30:59,http://boylekeler.biz/furman.lind,,73.83.177.118,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n22110,8,495,2017-05-15 12:51:36,http://buckridgetorphy.name/javier.gibson,,231.229.104.11,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n22111,8,495,2017-03-02 15:42:23,http://predovic.net/ricardo_wilderman,,119.32.137.126,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n22112,8,495,2017-04-23 16:25:56,http://marks.com/haan.hammes,,112.227.223.206,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n22113,8,495,2017-02-20 17:45:14,http://klocko.io/taurean,,12.183.199.191,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n22114,8,495,2016-12-13 09:08:24,http://bechtelarkling.io/keenan,,225.81.53.3,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n22115,8,495,2017-02-14 01:06:46,http://rodriguezbogan.net/aleia.torp,,226.102.109.106,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n22116,8,495,2017-01-06 18:04:52,http://balistrerijohnston.name/conner_steuber,,140.183.145.186,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n22117,8,495,2017-01-05 23:27:28,http://maggiolynch.biz/milton,,154.5.46.29,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n22118,8,495,2017-01-26 01:02:31,http://kuvaliskertzmann.name/camron_wilkinson,,123.143.88.86,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n22119,8,495,2017-01-18 17:28:34,http://grimes.co/nathan,,118.95.243.63,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n22120,8,495,2017-05-03 09:26:28,http://wilkinson.name/jamey_cremin,,6.13.11.27,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n22121,8,495,2017-06-01 00:48:09,http://windler.net/okey_langosh,,37.72.219.225,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n22122,8,495,2017-01-03 00:25:35,http://koch.co/jacques,,10.251.199.216,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n22123,8,495,2017-03-01 11:33:55,http://blick.name/devon.aufderhar,,218.120.174.50,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n22124,8,495,2017-05-21 05:47:51,http://kovacekhaag.io/jayce.wehner,,81.89.235.61,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n22125,8,495,2017-03-21 04:02:14,http://nolanwitting.net/trycia.feeney,,81.210.71.7,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n22126,8,495,2017-01-12 01:31:03,http://stroman.com/edward.tillman,,235.67.170.47,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n22127,8,495,2017-01-23 08:49:52,http://mannbeatty.co/sandy.kunde,,181.232.212.78,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n22128,8,495,2017-03-10 00:34:35,http://wieganddickinson.co/gene.trantow,,178.161.204.244,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n22129,8,495,2017-06-01 18:48:11,http://barrows.org/carole,,36.45.243.203,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n22130,8,496,2017-01-01 18:44:09,http://jones.info/marcellus,,151.133.163.88,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n22131,8,496,2017-02-01 14:24:37,http://zieme.name/freddie,,203.32.75.137,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n22132,8,496,2017-01-23 05:33:33,http://lindoreilly.info/eldred.hartmann,,205.215.102.176,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n22133,8,496,2017-01-30 21:22:33,http://schinner.net/iliana,,161.17.155.131,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n22134,8,496,2017-01-03 15:49:29,http://turcottejohnston.com/rachael,,190.86.84.55,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n22135,8,496,2016-12-30 14:21:15,http://wardjohnson.biz/donny_weinat,,188.143.242.135,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n22136,8,496,2017-03-31 16:33:03,http://schaeferdach.name/heidi,,8.196.41.169,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n22137,8,496,2017-01-06 07:35:12,http://kunze.name/lily,,7.49.154.121,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n22138,8,496,2017-04-06 12:05:02,http://rice.biz/ansel.watsica,,189.183.132.85,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n22139,8,496,2017-01-08 17:06:27,http://koepp.biz/sam,,78.81.125.120,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n22140,8,496,2017-04-15 09:27:33,http://dicki.co/veda,,115.55.102.18,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n22141,8,496,2017-03-09 06:38:40,http://sauer.biz/guy.dietrich,,30.247.46.66,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n22142,8,496,2017-03-10 05:46:49,http://wittinggoodwin.net/favian,,155.105.178.213,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n22143,8,496,2017-03-25 20:13:57,http://durganritchie.info/kaci_bogisich,,14.27.132.213,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n22144,8,496,2017-06-02 10:59:20,http://bergnaumkshlerin.org/marlee,,225.164.126.31,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n22145,8,496,2017-03-23 14:14:18,http://osinskihansen.net/kayley,,252.232.227.61,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n22146,8,496,2017-06-02 16:51:27,http://hermann.biz/stephon_satterfield,,16.128.19.30,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n22147,8,496,2017-05-09 10:52:27,http://botsfordnitzsche.net/kaandra_kuhn,,205.192.251.205,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n22148,8,496,2017-05-18 15:11:42,http://stromanthiel.io/tyrel_kub,,217.36.96.190,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n22149,8,496,2017-05-06 06:02:10,http://powlowski.com/dillon_torphy,,185.196.173.237,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n22150,8,496,2017-04-15 17:48:19,http://jast.co/wellington_spencer,,135.175.216.69,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n22151,8,496,2017-04-26 08:40:32,http://monahankulas.org/adrian_anderson,,183.251.123.80,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n22152,8,496,2017-03-30 05:39:09,http://willmskirlin.net/bernardo_stracke,,92.213.107.2,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n22153,8,496,2016-12-18 09:07:16,http://gislason.net/hertha,,150.167.243.22,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n22154,8,496,2017-04-23 23:11:40,http://gaylord.com/erna.satterfield,,229.163.77.17,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n22155,8,496,2017-06-05 15:40:16,http://bauch.co/patience,,222.116.19.150,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n22156,8,496,2017-04-19 04:44:46,http://schultz.org/jeie,,68.76.212.85,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n22157,8,496,2017-04-10 12:27:14,http://runolfon.co/ocie.leffler,,59.179.154.40,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n22158,8,496,2017-01-16 03:29:09,http://weberschroeder.info/annamae,,21.106.134.245,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n22159,8,496,2017-06-09 14:12:59,http://okeefe.info/hailie.gibson,,88.55.253.127,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n22160,8,496,2017-04-08 08:18:09,http://monahan.org/haie_powlowski,,2.221.159.126,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n22161,8,496,2017-03-13 15:44:24,http://bahringer.co/davin,,95.224.47.241,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n22162,8,496,2017-04-14 10:47:18,http://von.io/jerrod,,163.111.186.158,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n22163,8,496,2017-03-30 23:50:38,http://wuckertgibson.net/florence,,134.66.216.195,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n22164,8,496,2017-01-03 04:28:33,http://ziemann.biz/marilou,,5.71.243.94,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n1079,1,25,2017-03-01 07:44:09,http://lakinwalter.co/maxine.swaniawski,,197.12.249.27,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n1080,1,25,2017-04-29 06:19:02,http://gleichnerokuneva.name/carole,,68.69.166.152,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n1081,1,25,2017-01-04 03:02:07,http://kovacek.name/jey,,195.232.106.119,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n1082,1,25,2017-03-17 16:46:46,http://rogahndonnelly.org/marlee,,156.201.46.80,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n1083,1,25,2017-02-19 01:31:41,http://halvorson.com/olaf_schaefer,,226.101.232.100,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n1084,1,25,2017-02-09 04:58:36,http://sengervandervort.com/nannie.marks,,16.16.253.108,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n1085,1,25,2017-03-17 15:18:32,http://schowalter.com/yvonne_rohan,,134.94.10.173,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n1086,1,25,2017-03-01 11:31:07,http://parisian.net/ariane,,37.155.178.171,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n1087,1,25,2017-04-09 19:12:59,http://beattygislason.biz/erica,,61.35.36.172,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n1088,1,25,2017-01-09 01:46:56,http://nolan.net/dewitt.beahan,,166.11.125.91,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n1089,1,25,2017-01-23 20:07:46,http://littlesauer.info/sydney.boehm,,253.158.102.3,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n1090,1,25,2017-03-06 08:26:00,http://weimannmayert.io/margarette_runte,,17.30.86.76,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n1091,1,25,2017-02-18 16:50:13,http://runolfsdottir.io/oswaldo.cremin,,73.228.73.124,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n1092,1,25,2017-01-07 12:17:51,http://schmeler.name/amir.welch,,69.32.156.26,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n1093,1,25,2017-01-13 07:44:17,http://steuber.net/buford.bahringer,,192.59.220.139,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n1094,1,25,2017-03-24 19:10:43,http://miller.co/celestine_treutel,,215.70.175.52,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n1095,1,26,2017-02-21 20:58:43,http://gleason.net/nathen.parisian,,193.128.177.24,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n1096,1,26,2017-04-25 06:36:56,http://brown.biz/lois,,45.14.134.67,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n1097,1,26,2017-01-20 02:35:52,http://hayesgottlieb.info/maynard.feeney,,167.188.77.108,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n1098,1,26,2017-04-03 23:47:47,http://aufderhar.info/muhammad,,235.180.98.160,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n1099,1,26,2017-03-06 09:56:16,http://schoen.info/gerard,,144.98.82.192,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n1100,1,26,2017-01-21 18:53:29,http://boganrowe.net/merritt,,18.218.175.123,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1101,1,26,2017-06-10 13:47:26,http://schiller.net/shanny,,31.193.88.152,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n1102,1,26,2017-02-16 10:28:30,http://quitzon.info/lester,,179.142.252.238,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n1103,1,26,2017-04-13 05:02:20,http://ziemannstanton.net/juwan,,178.194.40.165,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n1104,1,26,2017-03-08 16:00:20,http://feilhilll.info/sven,,253.9.200.58,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n1105,1,26,2017-05-13 03:33:29,http://botsford.info/lacy_corwin,,181.130.152.223,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n1106,1,26,2017-06-09 00:00:14,http://hellermcclure.org/kendall,,120.229.175.173,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n1107,1,26,2017-05-29 22:14:05,http://becker.biz/laverna_reilly,,94.211.243.83,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n1108,1,26,2017-05-26 19:23:46,http://greenfelder.org/delia,,187.233.127.122,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n1109,1,26,2017-01-20 01:14:08,http://murazik.biz/warren_mueller,,94.94.33.124,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n1110,1,26,2017-04-25 07:47:15,http://gutmannschneider.io/wilson_ondricka,,112.252.251.13,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n1111,1,26,2017-04-03 06:46:32,http://gleichner.info/guillermo,,59.112.58.252,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n1112,1,26,2017-04-24 10:40:30,http://medhurstkovacek.io/hyman_lang,,118.40.175.42,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n1113,1,26,2016-12-13 12:40:45,http://bruen.info/kamron,,156.60.231.181,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n1114,1,26,2017-01-26 12:16:28,http://shields.net/estefania_zemlak,,90.251.103.22,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n1115,1,26,2017-04-05 14:52:25,http://weimann.co/tate,,117.79.37.81,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n1116,1,26,2017-05-21 20:46:58,http://rodriguez.com/kenna,,3.42.182.95,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n1117,1,27,2017-03-18 12:00:29,http://hillslind.com/marguerite,,51.116.178.157,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n1118,1,27,2017-05-10 02:30:30,http://koeppkihn.net/janick,,164.95.139.75,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n1119,1,27,2017-01-17 10:33:57,http://oberbrunnerpagac.name/mara,,156.30.147.165,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1120,1,27,2016-12-20 23:31:04,http://rosenbaum.org/sandra,,93.215.215.120,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n1121,1,27,2017-01-18 08:52:38,http://hartmann.info/davon,,57.155.209.253,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n1122,1,27,2017-04-26 11:02:59,http://lesch.info/jewell,,109.151.115.86,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n1123,1,27,2017-02-23 06:16:29,http://kuvalis.biz/gladyce,,122.94.29.235,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n1124,1,27,2016-12-22 07:27:22,http://nolanschiller.org/miles,,254.77.16.189,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n1125,1,27,2017-02-14 12:50:27,http://powlowski.co/andy,,12.62.240.21,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n1126,1,27,2017-03-15 10:06:43,http://mraz.biz/bridgette,,30.5.138.212,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n1127,1,27,2017-02-04 07:03:16,http://ricestoltenberg.info/salma.kshlerin,,76.204.53.87,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n1128,1,27,2017-04-14 04:26:26,http://gleason.info/roxanne_walsh,,134.94.37.213,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n1129,1,27,2017-05-03 16:50:59,http://dooleyhand.info/amara,,229.235.225.154,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n1130,1,27,2017-02-15 12:26:51,http://stantonkerluke.info/randi,,133.242.185.40,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n1131,1,27,2017-03-27 16:45:43,http://hanechristiansen.io/jeica,,61.156.223.202,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n1132,1,27,2016-12-26 07:39:19,http://kihncasper.com/mckayla.zboncak,,149.109.195.155,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n1133,1,27,2017-04-25 16:16:08,http://olson.com/alverta_ziemann,,114.112.213.172,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n1134,1,27,2017-06-06 09:44:13,http://feeney.org/lyla,,51.146.109.132,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n22165,8,496,2017-01-04 12:29:29,http://langworth.info/jee.kozey,,89.174.203.75,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n22166,8,496,2017-02-23 10:31:24,http://ward.net/avis,,35.47.170.76,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n22167,8,496,2016-12-13 09:36:46,http://spencerdare.io/delores,,220.14.9.97,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n22168,8,496,2017-02-01 04:04:52,http://larson.name/ruben,,97.23.180.208,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n22169,8,496,2017-03-31 20:51:09,http://kozey.biz/ila.effertz,,197.58.186.217,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n22170,8,496,2017-03-08 03:46:26,http://wiegand.name/robin_wunsch,,219.76.131.76,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n22171,8,496,2017-03-27 16:02:08,http://kochrosenbaum.info/kyla,,106.221.13.95,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n22172,8,496,2016-12-16 15:19:52,http://windler.name/antone,,206.238.231.191,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n22173,8,496,2017-01-20 08:00:29,http://turner.org/rozella_howell,,131.52.152.74,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n22174,8,496,2017-03-09 00:20:49,http://mannfeeney.com/marcia.lebsack,,225.219.39.129,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n22175,8,496,2017-03-01 14:30:43,http://nolan.io/caroline_lebsack,,103.27.160.227,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n22176,8,496,2017-02-11 23:56:12,http://auereichmann.co/don,,92.85.87.154,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n22177,8,496,2017-06-05 20:51:33,http://marvinabbott.name/lorena_hilll,,204.43.235.108,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n22178,8,497,2017-03-08 03:22:02,http://kshlerin.info/godfrey.nitzsche,,3.246.99.180,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n22179,8,497,2017-01-22 05:58:32,http://hegmannmayer.info/hope_mosciski,,77.115.39.58,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n22180,8,497,2017-04-17 01:19:13,http://gloverarmstrong.net/tevin.hilll,,248.126.131.210,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n22181,8,497,2017-03-12 05:42:26,http://mayert.biz/antonetta_buckridge,,69.201.116.22,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n22182,8,497,2017-05-25 04:14:34,http://reichel.co/kaci,,93.44.70.144,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n22183,8,497,2017-04-07 14:51:06,http://lind.org/vince,,9.33.63.212,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n22184,8,497,2017-01-02 20:03:29,http://spencer.name/alexandre,,44.201.234.214,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n22185,8,497,2017-06-01 22:06:10,http://dare.co/broderick,,239.125.204.25,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n22186,8,497,2017-04-10 23:10:54,http://lynch.info/violette,,8.5.206.65,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n22187,8,497,2017-01-21 15:57:46,http://bogan.org/alison_rau,,164.168.2.49,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n22188,8,497,2016-12-25 02:06:27,http://glover.net/bernard.kreiger,,133.225.62.45,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n22189,8,497,2016-12-14 13:15:57,http://hamillhaag.net/viva_schiller,,209.184.143.56,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n22190,8,497,2017-03-12 10:31:36,http://kunde.co/jadon_spinka,,180.96.105.99,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n22191,8,497,2017-05-06 08:49:57,http://white.info/sven_murray,,131.76.3.71,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n22192,8,497,2017-05-19 18:56:23,http://batz.com/lenny,,4.11.63.54,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n22193,8,497,2017-06-03 16:09:03,http://carrollmacejkovic.biz/donald,,44.108.145.51,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n22194,8,497,2017-05-17 13:42:58,http://legrosjohnson.net/rudolph.champlin,,233.172.28.108,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n22195,8,497,2017-01-07 11:30:17,http://effertz.org/aida.von,,190.99.115.56,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n22196,8,497,2017-02-01 21:07:20,http://rutherford.io/leola,,186.106.83.31,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n22197,8,497,2017-05-20 11:47:46,http://rosenbaumbogisich.net/dashawn,,159.44.222.106,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n22198,8,497,2017-01-13 19:58:09,http://reichel.co/shanel,,56.174.69.148,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n22199,8,497,2017-01-18 17:02:37,http://ondrickasatterfield.net/jeff,,210.193.46.164,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n22200,8,497,2017-05-15 18:56:23,http://bailey.info/johanna,,35.176.199.233,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n22201,8,497,2017-01-19 01:18:35,http://mohrdibbert.biz/leta,,146.156.212.234,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n22202,8,497,2017-04-21 17:03:21,http://hoegerjaskolski.io/charlie,,80.181.212.170,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n22203,8,497,2017-03-04 15:21:57,http://langosh.io/orpha,,217.83.95.236,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n22204,8,497,2017-02-16 15:18:39,http://torp.com/matilde,,72.89.35.26,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n22205,8,497,2017-02-06 23:57:37,http://gerlach.co/jayce,,169.198.78.222,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n22206,8,497,2017-03-13 09:21:07,http://lehnerupton.com/rosella,,132.10.59.172,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n22207,8,497,2017-06-11 22:19:03,http://mcdermott.biz/gertrude_fay,,67.143.35.3,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n22208,8,497,2017-05-29 09:07:29,http://rath.name/elliot_raynor,,102.252.130.43,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n22209,8,497,2017-01-19 14:38:39,http://terryblanda.net/madie,,105.238.242.61,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n22210,8,497,2017-02-27 15:08:07,http://mcdermott.net/green.gerhold,,94.160.166.245,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n22211,8,497,2017-05-18 18:12:23,http://ziemann.org/alberto.block,,103.156.146.50,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n22212,8,497,2017-04-27 03:19:28,http://jerde.biz/maximo,,202.107.42.65,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n22213,8,497,2017-01-08 22:37:38,http://fahey.biz/enrique_brekke,,16.39.113.108,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n22214,8,497,2017-05-22 13:26:54,http://heller.co/destinee_grimes,,218.64.13.242,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n22215,8,497,2017-05-11 14:04:59,http://tillmanbartoletti.io/cordie.kulas,,29.120.136.66,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n22216,8,497,2017-05-12 01:45:44,http://gleason.name/arlene,,99.160.192.86,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n22217,8,497,2016-12-18 02:22:38,http://haag.io/dayana,,132.144.185.125,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n22218,8,497,2016-12-17 02:19:08,http://stantonkirlin.name/anika_bartoletti,,78.142.111.206,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n22448,8,503,2017-05-23 17:50:31,http://mayert.info/sydni,,252.136.82.196,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n1135,1,27,2017-03-01 08:52:55,http://emmerich.org/garland.kutch,,189.29.200.198,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n1136,1,27,2017-01-05 20:20:25,http://moore.io/peyton.collier,,183.107.225.13,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n1137,1,27,2017-04-24 19:31:31,http://treutelabbott.io/craig_gerhold,,67.11.196.61,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n1138,1,27,2017-04-26 14:54:59,http://greenblanda.net/xzavier.rolfson,,222.231.147.63,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n1139,1,27,2017-02-05 16:29:54,http://jacobsonmayert.biz/milan.nader,,158.160.245.169,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n1140,1,27,2017-01-31 18:15:17,http://kunze.name/kayden_sawayn,,209.185.45.209,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n1141,1,27,2017-01-03 01:04:32,http://sawayn.info/brice.kreiger,,190.140.48.208,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n1142,1,27,2017-04-15 01:36:49,http://gutmann.info/neal,,247.144.78.25,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n1143,1,27,2017-03-03 14:34:35,http://ortiz.name/abigayle_wolff,,144.70.164.114,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n1144,1,27,2017-01-11 14:20:03,http://monahan.net/jefferey,,154.172.134.4,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n1145,1,27,2017-05-08 17:03:43,http://stoltenberg.net/brycen,,177.243.135.4,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n1146,1,27,2017-04-29 02:30:06,http://hellerfunk.net/brandy,,219.23.174.109,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n1147,1,27,2017-02-08 23:15:42,http://mcclure.com/ozzie,,86.235.161.124,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n1148,1,27,2017-04-28 02:46:35,http://schiller.com/faye.okeefe,,91.165.67.180,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n1149,1,27,2017-01-03 07:38:01,http://moen.io/mervin,,83.187.178.148,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n1150,1,27,2017-04-20 02:47:11,http://walsh.net/brionna.mann,,127.61.221.16,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n1151,1,27,2017-05-22 18:15:59,http://vonhahn.co/lincoln_huel,,97.114.133.203,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n1152,1,27,2017-01-30 14:00:10,http://mantevandervort.com/krystina_lueilwitz,,209.20.246.47,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n1153,1,27,2017-01-21 10:36:02,http://okon.net/gideon_weinat,,41.133.232.253,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n1154,1,27,2017-05-03 15:04:06,http://heller.org/justine,,200.143.192.68,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n1155,1,27,2017-02-04 22:45:25,http://rooboreilly.name/jonas,,72.180.80.254,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n1156,1,27,2017-01-19 02:37:24,http://barton.name/gina.jast,,160.129.77.114,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n1157,1,27,2017-03-21 16:31:26,http://littel.com/dawn,,187.177.133.86,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n1158,1,27,2016-12-27 00:51:32,http://doyle.net/clement,,179.41.202.104,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n1159,1,27,2017-02-07 04:26:17,http://johnson.net/vance_boyer,,241.21.167.17,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n1160,1,27,2017-02-15 04:26:07,http://botsford.org/corrine.abernathy,,161.227.178.169,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n1161,1,27,2016-12-30 23:37:29,http://mante.co/noel.schmeler,,48.27.101.2,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n1162,1,27,2017-01-30 19:53:32,http://reichert.io/grace.larson,,95.216.171.70,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n1848,1,41,2017-05-14 15:56:19,http://mann.name/carroll,,30.180.112.155,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n1163,1,27,2017-01-09 04:23:18,http://reynoldswilliamson.net/polly,,98.128.19.230,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n1164,1,27,2017-03-02 23:23:24,http://klocko.net/german,,156.47.8.191,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n1165,1,27,2017-05-20 04:18:27,http://willms.org/asa,,227.8.150.87,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n1166,1,27,2017-01-20 00:47:18,http://ledner.biz/victoria,,108.133.82.9,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n1167,1,27,2017-04-23 18:23:16,http://beer.info/susana,,214.176.240.247,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n1168,1,27,2017-04-03 20:14:23,http://pagac.name/jailyn_abshire,,188.204.95.4,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n1169,1,27,2017-03-02 11:23:17,http://pollichdibbert.biz/carey,,247.45.150.148,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n1170,1,27,2017-03-28 16:12:46,http://ledner.io/marianna_schmeler,,156.103.157.5,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n1171,1,27,2016-12-30 11:52:17,http://franecki.com/kathryne,,207.185.94.141,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n1172,1,27,2017-04-23 14:25:14,http://quigley.name/kris_robel,,121.85.202.159,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n1173,1,27,2017-05-21 15:26:38,http://jacobs.co/zora,,200.121.7.38,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n1174,1,27,2017-01-05 12:58:28,http://wisozk.info/ania,,32.129.163.87,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n1175,1,27,2017-03-15 08:27:44,http://jaskolski.co/alvena_keler,,35.54.222.168,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n1176,1,28,2017-04-04 17:38:17,http://millerrath.name/estrella,,243.229.91.212,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n1177,1,28,2017-01-27 06:07:56,http://emard.name/chase,,165.52.4.239,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n1178,1,28,2017-03-24 05:06:28,http://anderson.biz/arlene,,220.96.182.181,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n1179,1,28,2017-01-15 15:04:30,http://effertz.net/elbert_homenick,,113.29.227.210,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n1180,1,28,2016-12-20 05:30:59,http://larson.info/tyreek,,23.187.94.90,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n1181,1,28,2017-05-08 05:40:36,http://roweolson.name/ena,,224.61.225.20,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n1182,1,28,2017-05-23 02:15:25,http://rath.biz/ryleigh,,43.193.105.58,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n1183,1,28,2017-04-29 00:45:42,http://darehermiston.co/ricky,,102.159.136.211,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n1184,1,28,2017-02-06 00:05:57,http://paucek.biz/danika,,62.12.148.28,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n1185,1,28,2017-01-07 01:34:33,http://batz.biz/liana.volkman,,25.40.226.161,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n1186,1,28,2017-03-22 23:30:26,http://wolf.com/hailey,,134.82.133.17,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n1187,1,28,2017-02-07 22:43:44,http://schiller.name/annie,,148.87.9.3,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n1188,1,28,2017-04-28 10:46:12,http://boyer.biz/mariela.dach,,10.70.167.198,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n1189,1,28,2017-04-06 00:52:44,http://pacocha.biz/keegan,,101.33.83.152,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n22219,8,498,2017-03-06 23:16:19,http://lemke.name/davonte,,180.96.146.109,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n22220,8,498,2017-02-10 11:01:00,http://ruel.info/phoebe,,80.209.110.39,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n22221,8,498,2016-12-18 22:22:59,http://grantsawayn.com/aiyana,,68.85.179.176,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n22222,8,498,2017-04-04 08:24:39,http://schusteremmerich.name/clifford,,101.206.36.193,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n22223,8,498,2017-04-29 03:30:32,http://bogan.name/brandy_glover,,227.7.48.60,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n22224,8,498,2017-01-15 00:58:41,http://durganheidenreich.biz/neva,,190.220.201.63,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n22225,8,498,2017-01-04 14:10:45,http://schultztremblay.io/kelli,,174.49.197.252,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n22226,8,498,2017-05-17 10:10:28,http://sauerhickle.org/rudy_nienow,,62.73.253.25,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n22227,8,498,2017-06-02 13:17:34,http://windlergulgowski.biz/doug,,152.135.25.163,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n22228,8,498,2017-03-17 08:46:21,http://renner.info/ashtyn.torphy,,89.115.186.75,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n22229,8,498,2016-12-27 12:09:21,http://toy.net/ruby,,201.9.73.58,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n22230,8,498,2016-12-26 07:08:33,http://cummings.org/claudine.schinner,,81.100.108.136,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n22231,8,498,2017-02-04 20:58:00,http://franecki.co/clifford,,227.41.228.104,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n22232,8,498,2017-02-03 03:32:46,http://denesik.name/darrel,,226.151.168.25,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n22233,8,498,2017-06-09 08:45:27,http://ryanboyer.co/axel,,250.29.14.36,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n22234,8,498,2017-03-17 12:19:00,http://mayertfunk.info/cayla,,203.33.26.88,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n22235,8,498,2017-05-09 12:00:51,http://yundt.co/america,,238.144.240.188,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n22236,8,498,2016-12-31 10:37:04,http://runteortiz.name/osvaldo_homenick,,251.217.101.246,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n22237,8,498,2017-02-21 16:34:27,http://gutkowskicole.net/eusebio,,194.74.153.160,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n22238,8,498,2017-01-29 03:10:40,http://volkman.co/ramona_kerluke,,201.45.25.163,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n22239,8,498,2016-12-15 05:11:57,http://robel.io/alisa,,175.162.104.35,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n22240,8,498,2017-03-12 20:38:00,http://ondrickarolfson.net/odell_quitzon,,109.201.99.209,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n22241,8,498,2017-05-01 12:12:41,http://jastweinat.io/maria_bailey,,44.249.30.194,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n22242,8,498,2017-05-13 18:45:24,http://schuster.io/jimmie,,83.194.176.3,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n22243,8,498,2017-05-04 01:00:41,http://hayes.co/emanuel_predovic,,10.92.91.13,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n22244,8,498,2017-04-07 03:12:55,http://cristkutch.com/jerad,,111.24.49.202,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n22245,8,498,2017-05-28 07:00:47,http://cartermcdermott.info/brady.green,,95.159.175.124,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n22246,8,498,2017-02-12 20:34:56,http://moriettehilll.co/collin_christiansen,,150.77.190.238,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n22247,8,498,2017-02-01 18:37:07,http://johnston.info/marisa.wolff,,148.25.82.24,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n22248,8,498,2017-05-21 11:50:16,http://emard.biz/jake,,253.213.224.186,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n22249,8,498,2017-01-07 03:20:28,http://cronin.name/domenic_morar,,239.138.98.156,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n22250,8,498,2017-03-24 01:18:10,http://heaney.name/isac.gulgowski,,72.143.244.9,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n22251,8,498,2017-03-25 14:21:11,http://runte.net/lillian.yost,,165.79.165.123,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n22252,8,498,2017-05-16 07:00:43,http://tillman.io/amos.cronin,,67.53.27.124,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n22253,8,498,2017-01-11 07:08:31,http://fadel.co/terrence,,34.14.236.196,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n22254,8,498,2016-12-22 07:24:30,http://ricerice.info/keven,,146.72.44.181,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n22255,8,498,2017-05-30 00:07:20,http://friesen.info/seamus_deckow,,87.87.150.89,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n22256,8,498,2017-01-09 14:47:23,http://fisherswaniawski.biz/amari,,194.16.124.206,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n22257,8,498,2017-06-08 06:12:05,http://dooley.co/kameron,,34.18.87.195,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n22258,8,498,2017-02-16 20:47:10,http://feeney.name/abdiel.gleichner,,90.92.147.228,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n22259,8,498,2017-01-27 22:36:00,http://volkmantremblay.co/deborah,,221.123.138.137,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n22260,8,498,2017-01-14 22:31:55,http://sauer.net/nash,,62.180.159.142,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n22261,8,498,2017-02-13 12:46:29,http://roob.org/francisco,,4.159.120.159,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n22262,8,498,2017-05-16 10:15:21,http://walsh.biz/alyson_boyer,,42.168.113.171,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n22263,8,498,2017-03-03 05:29:47,http://kilback.co/misty_miller,,68.127.146.69,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n22264,8,498,2017-05-15 05:21:29,http://wisozk.org/angelo,,253.97.208.115,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n22265,8,498,2017-06-04 09:52:37,http://muller.biz/lurline,,147.108.181.194,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n22266,8,498,2017-04-07 05:23:31,http://darerath.biz/ivory_raynor,,69.43.203.250,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n22267,8,498,2017-05-28 15:54:36,http://lefflerfahey.org/florida_spinka,,221.35.111.129,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n22268,8,498,2017-05-23 01:46:14,http://johnston.name/julianne,,221.132.16.196,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n22269,8,498,2017-03-18 04:20:46,http://rutherfordaltenwerth.co/melvina_zieme,,141.148.210.167,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n22270,8,498,2017-05-17 11:36:22,http://heller.co/keven_hyatt,,3.81.14.119,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n22271,8,498,2016-12-22 01:11:57,http://mooreklein.info/taurean.herzog,,128.189.159.118,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n22272,8,498,2017-04-26 05:29:44,http://thiel.com/jovanny.schumm,,106.52.179.97,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n22273,8,498,2017-06-01 19:33:16,http://haag.io/jazmin_ondricka,,50.230.233.98,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n22274,8,498,2016-12-21 16:32:59,http://hegmannmertz.info/reid,,215.172.85.51,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n1190,1,28,2017-06-04 01:46:14,http://champlin.com/dell.williamson,,176.221.215.144,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n1191,1,28,2017-03-02 21:41:16,http://wunschschneider.net/norma.spencer,,91.220.71.182,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n1192,1,28,2017-05-18 16:19:16,http://okuneva.biz/marilou,,49.164.92.24,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n1193,1,28,2017-02-11 22:15:32,http://toybins.com/dewitt.fisher,,26.38.105.239,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n1194,1,28,2017-01-26 02:11:38,http://feil.com/mariane.howe,,64.13.177.132,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n1195,1,28,2017-04-03 09:18:17,http://kingreichel.com/alyson.lakin,,224.208.95.87,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n1196,1,28,2017-01-08 00:16:15,http://cremin.info/elinor_schmidt,,133.110.140.80,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n1197,1,28,2017-01-25 18:15:10,http://torphy.net/emery,,128.75.239.167,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n1198,1,28,2016-12-17 05:22:26,http://hintz.name/melia,,150.190.130.215,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n1199,1,28,2017-05-24 18:42:42,http://runolfon.name/june.price,,139.203.155.122,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n1200,1,28,2017-05-18 13:29:30,http://boehm.com/murray,,124.117.174.170,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n1201,1,28,2017-04-04 22:05:28,http://willeichmann.org/barrett_yost,,50.204.14.194,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n1202,1,28,2017-06-13 14:44:39,http://lockman.net/lottie_ratke,,166.49.53.174,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n1203,1,28,2017-03-09 22:50:23,http://schamberger.info/aliza_torphy,,55.159.89.17,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n1204,1,28,2017-04-11 05:24:28,http://schinnerweber.name/paxton,,219.125.8.28,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n1205,1,28,2017-02-23 04:57:07,http://nicolas.co/claudie.padberg,,10.84.61.153,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n1206,1,28,2017-02-12 06:41:48,http://wisoky.io/bernardo_cummerata,,143.122.44.85,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n1207,1,28,2017-04-24 12:37:34,http://kovacek.co/melia.rice,,147.248.183.26,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n1208,1,28,2017-05-08 13:55:45,http://hamill.info/buck.hermann,,57.222.179.220,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n1209,1,28,2017-02-02 10:22:12,http://heathcotewisozk.co/jerome_strosin,,124.133.10.145,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n1210,1,28,2017-01-26 01:25:17,http://wolfharris.com/austyn.rohan,,49.248.140.206,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n1211,1,28,2017-02-12 15:35:58,http://lueilwitz.co/ruby,,150.247.4.216,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n1212,1,28,2017-01-05 10:40:34,http://cole.io/orval_schmitt,,176.142.193.117,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n1213,1,28,2017-01-18 05:34:14,http://grimesrunolfon.io/nia.larson,,231.47.40.47,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n1214,1,28,2017-02-14 14:37:06,http://christiansensatterfield.org/wilson,,94.79.175.221,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n1215,1,28,2017-03-27 12:18:25,http://bogan.co/effie.erdman,,194.23.177.199,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n1216,1,28,2017-04-25 04:59:34,http://olsonward.info/brando,,55.124.216.30,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n1217,1,28,2016-12-19 14:37:16,http://bartolettilarson.co/emmalee.miller,,142.247.220.32,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n1218,1,28,2017-04-17 04:50:26,http://bradtke.info/candido,,236.195.190.135,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n1219,1,28,2017-03-06 12:55:55,http://windlerdouglas.net/taryn.lebsack,,254.64.57.168,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n2020,1,45,2017-03-29 21:49:25,http://torp.co/uriah,,30.166.27.103,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n1220,1,28,2017-01-27 19:12:24,http://weimannokuneva.biz/jermaine,,93.236.134.190,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n1221,1,28,2017-02-11 04:59:19,http://littledenesik.name/carmen,,24.247.44.172,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n1222,1,28,2017-03-17 18:24:01,http://sporer.info/emmitt,,131.233.240.210,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n1223,1,28,2017-05-28 14:33:22,http://millsvandervort.co/carli,,34.241.167.250,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n1224,1,28,2017-05-12 07:28:37,http://batz.info/andres_sipes,,172.23.139.169,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n1225,1,28,2017-03-22 18:10:21,http://grant.org/jettie_strosin,,191.232.38.9,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n1226,1,28,2017-02-13 16:33:53,http://frami.org/marcia_gulgowski,,245.73.125.245,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n1227,1,28,2017-02-19 17:59:25,http://sawaynlockman.info/mafalda.cummerata,,228.179.121.23,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n1228,1,28,2017-05-07 14:26:02,http://cremin.co/telly.pacocha,,129.58.96.161,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n1229,1,28,2016-12-29 17:23:43,http://schuppebrekke.info/maegan,,53.151.219.245,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n1230,1,28,2017-03-16 09:25:48,http://crist.com/carrie,,71.105.238.129,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n1231,1,28,2017-01-11 23:54:11,http://schultz.biz/dorcas.wolf,,105.58.166.56,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n1232,1,28,2017-04-26 22:47:03,http://krishuels.net/karlie_tremblay,,221.177.179.17,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n1233,1,28,2017-04-08 22:38:30,http://west.org/wanda.fritsch,,197.138.25.179,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n1234,1,28,2017-04-16 17:59:33,http://prohaska.io/imelda_grady,,224.73.235.159,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n1235,1,28,2016-12-30 21:23:59,http://hageneswyman.biz/tito.wilkinson,,6.223.202.165,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n1236,1,28,2017-03-27 14:42:24,http://jenkins.name/allie,,233.223.245.18,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n1237,1,28,2017-03-05 16:27:54,http://braunbernier.name/brigitte,,71.209.50.134,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n1238,1,28,2017-03-19 12:19:30,http://jaskolski.io/ivy,,198.156.71.25,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n1239,1,28,2017-06-07 08:33:04,http://renner.io/jaleel,,182.139.11.212,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n1240,1,28,2017-05-22 02:55:26,http://bruen.com/garett_abshire,,220.175.156.92,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n1241,1,28,2017-02-19 15:18:41,http://smithcrooks.name/merl,,161.235.159.158,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n1242,1,28,2017-02-27 17:22:20,http://rutherford.org/jey,,207.125.189.19,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n1243,1,28,2017-03-06 15:47:46,http://stracke.io/alfred,,113.183.117.236,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n1244,1,28,2016-12-19 01:46:43,http://johnston.io/casper_pfeffer,,156.104.141.214,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n22275,8,498,2017-01-25 12:04:30,http://cummerata.org/damaris_heidenreich,,137.140.72.118,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n22276,8,498,2017-06-13 03:49:36,http://heel.com/kareem_feeney,,51.128.208.13,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n22277,8,498,2016-12-27 02:51:54,http://homenick.co/wallace_barton,,174.86.15.42,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n22278,8,499,2016-12-22 19:01:18,http://mraz.co/vicente_beer,,28.22.93.50,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n22279,8,499,2017-02-10 04:41:01,http://lubowitz.net/otto,,39.179.28.218,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n22280,8,499,2017-03-30 13:16:57,http://predovic.biz/angeline_mcglynn,,107.112.138.132,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n22281,8,499,2017-03-21 22:07:09,http://jacobiprosacco.io/je.smith,,173.3.76.5,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n22282,8,499,2017-02-08 15:53:44,http://feil.co/doris_brekke,,47.38.55.10,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n22283,8,499,2017-01-18 11:56:29,http://howearmstrong.name/dangelo,,72.8.176.141,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n22284,8,499,2017-05-16 07:42:40,http://balistrerifriesen.info/marge.mohr,,140.12.147.54,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n22285,8,499,2017-05-21 11:26:52,http://turnersatterfield.com/hiram,,25.51.111.82,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n22286,8,499,2017-06-13 17:46:36,http://stromanconsidine.name/mariela_stiedemann,,44.21.20.24,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n22287,8,499,2017-04-11 12:31:43,http://friesenrowe.name/gay,,230.166.52.253,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n22288,8,499,2017-01-22 12:26:32,http://steuber.com/isaiah,,121.114.78.34,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n22289,8,499,2017-05-25 05:53:08,http://stamm.org/freda.damore,,28.211.16.120,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n22290,8,499,2017-04-30 00:43:40,http://oberbrunnermayert.io/keaton,,44.174.222.194,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n22291,8,499,2017-04-03 12:53:57,http://keler.biz/noe_farrell,,105.135.49.203,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n22292,8,499,2017-05-08 20:11:18,http://beerboyle.info/jeff_konopelski,,202.93.42.79,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n22293,8,499,2017-05-14 22:10:08,http://beatty.com/althea_schaefer,,150.176.60.78,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n22294,8,499,2017-03-12 08:06:51,http://kaulkebotsford.biz/odell.murray,,215.108.199.94,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n22295,8,499,2017-01-22 15:31:11,http://kuvalis.org/uriah,,7.244.88.34,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n22296,8,499,2017-02-09 08:44:07,http://ankundingjenkins.info/dannie,,61.12.179.59,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n22297,8,499,2017-03-18 19:11:19,http://altenwerth.biz/kip,,118.237.87.8,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n22298,8,499,2017-05-06 05:42:10,http://goldnermarquardt.co/brice,,49.77.147.238,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n22299,8,499,2017-06-06 00:13:13,http://howell.name/violette,,32.90.117.35,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n22300,8,499,2017-04-16 06:00:11,http://gibson.name/beatrice,,237.232.88.241,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n22301,8,499,2017-03-29 21:51:32,http://hauckjacobs.org/alicia_dicki,,23.133.106.27,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n22302,8,499,2017-03-28 01:22:47,http://colewillms.com/armand,,99.129.193.186,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n22303,8,500,2017-05-23 11:16:52,http://moriettelind.name/modesto,,221.162.224.141,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n22304,8,500,2017-03-24 16:16:58,http://nitzsche.io/cyril_friesen,,156.90.88.188,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n22305,8,500,2016-12-29 13:32:09,http://pacocha.biz/meghan.hudson,,35.47.7.85,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n22306,8,500,2017-01-01 14:36:51,http://schoen.biz/justina,,138.186.206.213,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n22307,8,500,2017-03-06 22:59:45,http://marvin.info/orlando_ferry,,193.4.4.92,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n22308,8,500,2017-01-18 09:31:09,http://johnstonheller.name/lourdes_skiles,,232.175.153.129,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n22309,8,500,2017-06-10 06:22:37,http://carroll.co/nyah,,109.145.192.101,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n22310,8,500,2017-05-25 14:47:57,http://veum.info/margie,,5.221.125.117,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n22311,8,500,2016-12-28 06:08:47,http://hartmann.org/brendon,,2.47.51.155,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n22312,8,500,2017-06-08 00:39:20,http://casper.net/adelle,,60.67.87.198,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n22313,8,500,2017-01-10 03:05:40,http://willweinat.biz/skylar_watsica,,182.161.206.247,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n22314,8,500,2017-04-13 15:06:05,http://leuschkeko.co/gillian,,46.81.58.21,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n22315,8,500,2017-02-02 04:23:36,http://kemmer.name/bradly,,212.101.68.253,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n22316,8,500,2016-12-31 14:53:42,http://schaden.name/napoleon_brekke,,156.49.166.31,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n22317,8,500,2017-04-07 06:16:50,http://white.name/tamia,,49.68.161.238,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n22318,8,500,2017-02-12 01:56:08,http://batz.io/araceli,,26.165.131.163,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n22319,8,500,2017-06-05 18:43:47,http://gibson.co/zachariah.donnelly,,108.242.205.194,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n22320,8,500,2017-03-06 01:44:51,http://hermann.biz/kenya,,13.150.27.243,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n22321,8,500,2017-05-10 13:30:14,http://stroman.net/florian,,161.53.74.27,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n22322,8,500,2017-04-03 04:19:55,http://hilpert.io/kale,,171.28.121.19,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n22323,8,500,2017-04-26 19:00:44,http://mclaughlin.com/berniece_rodriguez,,220.124.197.209,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n22324,8,500,2017-01-12 07:50:02,http://weber.name/kellie_mckenzie,,217.183.147.241,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n22325,8,500,2017-02-02 06:46:29,http://schowalterconroy.biz/freddie,,107.34.54.148,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n22326,8,500,2017-01-28 22:29:56,http://gusikowski.co/jarred,,116.69.131.253,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n22327,8,500,2017-05-30 03:41:07,http://adams.io/jairo.rosenbaum,,5.116.111.52,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n22328,8,500,2017-01-29 02:49:57,http://lubowitz.co/amber_murray,,149.128.85.53,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n22329,8,500,2017-03-07 03:04:33,http://ziemezemlak.org/claudia,,56.93.151.67,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n1245,1,28,2017-03-26 00:58:13,http://hudsonoreilly.co/tyshawn,,32.43.141.48,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n1246,1,29,2017-01-25 14:08:04,http://ullrich.net/jey.klein,,134.140.169.82,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n1247,1,29,2017-04-23 16:06:34,http://schroeder.co/pearline,,72.8.93.22,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n1248,1,29,2017-01-18 00:01:52,http://west.name/mauricio,,138.186.247.130,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n1249,1,29,2017-03-25 23:07:47,http://mcglynn.co/floyd_schmeler,,251.80.146.45,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n1250,1,29,2017-05-19 03:57:44,http://legros.co/iva_mann,,180.115.38.18,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n1251,1,29,2017-05-20 22:10:33,http://rippin.co/estrella,,100.133.243.42,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n1252,1,29,2017-03-28 19:37:37,http://murazikkautzer.com/christopher,,211.51.69.190,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n1253,1,29,2017-06-09 13:49:26,http://maggio.org/leopoldo_auer,,60.224.111.123,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n1254,1,29,2017-02-09 04:22:20,http://yost.io/lucy,,77.145.75.184,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n1255,1,29,2017-03-10 01:56:02,http://kautzer.org/kiana,,34.85.171.165,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n1256,1,29,2017-02-12 09:58:54,http://wunsch.info/treva.toy,,136.178.108.27,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n1257,1,29,2017-03-08 01:45:19,http://lockman.info/betsy.rolfson,,138.77.216.163,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n1258,1,29,2017-02-16 07:23:11,http://hickle.name/amely,,18.188.161.28,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1259,1,29,2017-01-07 12:59:45,http://nienow.info/jaclyn.ferry,,228.202.191.33,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n1260,1,29,2017-02-11 03:21:45,http://schulist.io/ofelia,,106.95.229.5,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n1261,1,29,2017-02-10 16:36:18,http://jerde.info/shaylee_gibson,,167.78.31.61,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n1262,1,29,2017-02-12 09:15:42,http://schroedertremblay.info/bradly,,117.207.50.111,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n1263,1,29,2017-01-02 14:32:37,http://hermistonrau.io/sadye.barrows,,95.164.109.118,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n1264,1,29,2017-03-18 11:07:43,http://kuphalherman.biz/ernestine,,27.99.147.44,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1265,1,29,2017-02-22 17:00:10,http://strosin.io/johnson.stiedemann,,245.38.130.246,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n1266,1,29,2017-05-12 22:24:18,http://goodwinhuels.org/merle,,72.234.226.191,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n1267,1,29,2017-01-23 21:45:45,http://boganweimann.net/eric,,151.122.12.247,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n1268,1,29,2017-03-16 10:37:46,http://boyer.biz/zella.ortiz,,207.170.151.108,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n1269,1,29,2017-01-21 13:29:38,http://pollich.com/merritt_kunze,,178.110.155.124,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n1270,1,29,2016-12-20 17:13:24,http://hegmann.net/eloisa,,165.140.81.119,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n1271,1,29,2017-02-19 12:17:45,http://bayer.io/torrey,,151.13.53.59,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n1272,1,29,2017-04-13 12:02:56,http://schneider.org/rubye.strosin,,55.66.181.77,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n1273,1,29,2017-01-31 11:43:30,http://oconnell.info/cathryn.mayert,,221.115.95.86,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n1274,1,29,2017-01-28 22:05:19,http://cummeratacole.name/cary_johns,,122.186.23.127,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n1275,1,29,2017-02-24 20:04:31,http://upton.com/bethany,,186.28.111.145,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n1276,1,29,2017-01-31 13:10:44,http://rowe.name/howell,,148.250.53.232,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n2136,1,47,2016-12-23 16:54:09,http://toy.net/celine,,158.67.223.96,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n1277,1,29,2017-03-05 09:49:07,http://mohr.co/maia.brown,,54.43.33.125,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n1278,1,29,2017-05-31 15:13:59,http://carterbalistreri.info/dwight,,224.245.95.149,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n1279,1,29,2017-03-16 01:08:25,http://gottliebwilliamson.io/mireya,,199.171.229.42,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n1280,1,29,2017-06-03 02:14:15,http://kilback.org/aileen,,110.49.51.222,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n1281,1,29,2017-05-13 12:13:31,http://daugherty.org/ed_stracke,,66.181.246.81,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n1282,1,29,2016-12-31 18:53:28,http://leffler.co/jason_sporer,,148.102.86.198,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n1283,1,29,2016-12-22 11:54:56,http://terry.com/hal.grant,,153.229.202.34,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n1284,1,29,2017-01-06 18:20:17,http://hamill.io/ernest,,203.29.226.90,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n1285,1,29,2016-12-15 15:20:38,http://ebertstark.net/bonnie.gusikowski,,166.2.85.71,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n1286,1,30,2017-02-08 02:23:53,http://gusikowskicruickshank.net/eula,,50.43.62.62,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n1287,1,30,2017-03-18 06:29:50,http://beer.biz/otto.leuschke,,190.13.147.126,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n1288,1,30,2017-05-28 12:44:01,http://yundtgerlach.net/meta,,154.230.128.46,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n1289,1,30,2017-01-30 05:23:18,http://cummerata.biz/braden_rolfson,,47.247.117.193,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n1290,1,30,2017-06-01 07:50:37,http://reilly.co/brenda.becker,,207.250.159.205,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n1291,1,30,2017-06-14 00:12:10,http://gerlachthiel.org/roel,,125.246.138.12,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n1292,1,30,2017-04-17 09:04:52,http://kuhlmankonopelski.net/rolando,,109.103.130.121,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n1293,1,30,2017-06-07 00:28:11,http://goldner.com/kathleen,,192.99.18.166,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n1294,1,30,2016-12-15 10:58:08,http://kuvalis.io/kiara.schuppe,,215.91.116.164,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n1295,1,30,2017-04-24 02:21:26,http://ankunding.name/tyreek.weber,,19.37.180.218,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n1296,1,30,2017-02-03 02:00:07,http://millsdubuque.org/francesco,,97.163.62.139,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n1297,1,30,2017-01-23 00:04:45,http://dickens.biz/tomasa,,153.93.13.248,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n1298,1,30,2017-05-24 05:48:42,http://windlerziemann.org/mateo,,213.195.40.94,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n1299,1,30,2017-02-11 10:03:37,http://volkmanmuller.io/bailee_schuster,,242.76.28.245,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n22330,8,500,2017-05-02 20:45:51,http://wiza.io/destini,,87.56.79.239,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n22331,8,500,2017-02-21 07:06:15,http://stantonbashirian.info/aleandro_ruecker,,70.4.225.11,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n22332,8,500,2017-01-27 19:50:46,http://green.com/karianne,,125.135.97.48,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n22333,8,500,2017-01-06 01:44:00,http://hermanabshire.com/jamey_will,,145.125.14.43,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n22334,8,501,2017-05-24 16:40:15,http://greenfelder.io/gerhard,,57.202.16.55,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n22335,8,501,2017-04-19 15:24:37,http://heller.net/keagan.christiansen,,68.218.167.58,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n22336,8,501,2017-04-04 02:22:39,http://rueckerdenesik.org/eula_dickens,,19.37.222.251,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n22337,8,501,2017-03-31 10:49:44,http://kunde.com/ilene,,190.126.214.240,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n22338,8,501,2016-12-25 11:28:33,http://mayercollier.net/alexie,,205.167.180.36,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n22339,8,501,2017-01-10 03:59:27,http://bergstrom.co/aleen.leannon,,114.4.171.72,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n22340,8,501,2017-01-10 15:50:34,http://walsh.org/wilson.king,,217.129.209.185,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n22341,8,501,2017-01-23 22:40:20,http://bauchrohan.biz/delmer,,83.188.129.224,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n22342,8,501,2017-02-06 01:52:07,http://marks.org/shyanne.metz,,38.84.121.152,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n22343,8,501,2017-04-07 00:33:36,http://witting.biz/valentine_mclaughlin,,243.87.227.177,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n22344,8,501,2017-01-05 16:34:31,http://shields.net/mayra.huel,,16.49.212.67,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n22345,8,501,2017-05-18 15:00:50,http://kuhn.com/larry_jerde,,188.23.253.18,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n22346,8,501,2017-03-16 04:22:36,http://stoltenbergdach.info/jeyca,,30.35.124.38,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n22347,8,501,2017-05-05 23:34:24,http://corkery.biz/jermey,,195.96.13.44,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n22348,8,501,2017-04-15 07:39:52,http://paucek.org/treva.spencer,,26.54.145.98,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n22349,8,501,2017-01-05 06:20:41,http://beier.info/price_nikolaus,,85.86.158.15,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n22350,8,501,2017-03-31 06:19:41,http://shields.name/nichole_crist,,212.26.149.191,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n22351,8,501,2017-03-10 09:07:44,http://stehr.io/hilario,,109.62.56.22,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n22352,8,501,2017-02-07 16:00:40,http://blockrempel.net/judson.reichel,,40.206.204.51,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n22353,8,501,2017-04-26 17:43:19,http://kilback.name/percy,,181.243.83.174,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n22354,8,501,2017-01-17 18:45:58,http://goodwin.io/hilton_thompson,,212.208.106.83,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n22355,8,501,2017-04-05 04:55:03,http://gottlieb.io/susie.adams,,112.202.105.251,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n22356,8,501,2016-12-18 05:08:13,http://abernathy.io/rosetta,,158.103.42.87,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n22357,8,501,2017-03-11 08:54:15,http://creminzboncak.org/rafael,,170.110.203.31,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n22358,8,501,2017-02-28 23:54:34,http://mcdermott.com/tomas.weinat,,111.3.235.28,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n22359,8,501,2017-04-02 12:45:52,http://prohaska.com/lazaro,,222.45.38.98,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n22360,8,501,2017-02-21 23:37:22,http://klocko.biz/sonny,,89.61.36.12,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n22361,8,501,2017-05-14 00:23:12,http://romaguerathompson.com/aunta.sawayn,,91.93.4.127,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n22362,8,501,2017-05-27 02:57:46,http://walter.name/erna_flatley,,102.240.83.106,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n22363,8,501,2016-12-16 02:58:26,http://deckowarmstrong.io/kelvin,,188.239.36.198,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n22364,8,501,2017-05-05 14:04:37,http://corkery.co/caidy,,29.157.164.227,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n22365,8,501,2017-01-18 06:07:38,http://vandervort.name/johnnie,,173.106.121.103,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n22366,8,501,2017-01-05 20:47:23,http://watsicapacocha.net/hailee.reilly,,205.127.51.193,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n22367,8,501,2016-12-13 11:06:50,http://dickimcdermott.info/gabe_kaulke,,60.216.116.216,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n22368,8,501,2017-05-31 23:38:34,http://goodwin.com/napoleon,,209.148.231.195,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n22369,8,501,2016-12-25 11:58:28,http://ruecker.io/anastacio_altenwerth,,14.40.145.114,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n22370,8,501,2017-01-11 09:17:02,http://okuneva.io/guillermo_haley,,185.96.108.33,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n22371,8,501,2017-02-04 11:06:08,http://barrows.co/brayan_fay,,8.79.145.110,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n22372,8,501,2017-04-16 19:34:45,http://waelchi.co/edmond_donnelly,,101.22.159.74,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n22373,8,501,2017-05-07 00:53:51,http://rowe.info/quincy_kulas,,112.24.21.173,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n22374,8,501,2017-05-21 03:11:39,http://beier.net/myrtis.stark,,219.96.81.51,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n22375,8,501,2017-02-24 22:10:21,http://huels.io/olga,,22.94.165.240,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n22376,8,501,2017-04-23 23:02:06,http://schimmel.net/skyla_altenwerth,,153.136.231.87,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n22377,8,501,2016-12-24 23:37:37,http://oberbrunner.io/maritza.eichmann,,82.139.149.64,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n22378,8,501,2017-02-08 18:29:07,http://cummerataglover.name/alvah.hintz,,228.46.142.103,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n22379,8,501,2017-02-17 00:52:23,http://macgyverhoeger.net/jordy_paucek,,92.139.51.191,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n22380,8,501,2017-01-18 13:56:39,http://considinegraham.org/aleandra.armstrong,,66.100.91.108,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n22381,8,501,2017-04-24 01:44:44,http://boylepollich.com/daphney_kuhic,,40.4.67.236,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n22382,8,502,2016-12-25 21:21:12,http://reichert.info/ronny,,253.124.40.119,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n22383,8,502,2017-01-29 00:15:47,http://roberts.io/lyric,,69.244.238.38,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n22384,8,502,2017-06-13 16:12:12,http://cruickshank.name/otho,,250.224.130.84,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n1300,1,30,2017-03-14 12:07:44,http://bogan.org/gilberto.okeefe,,59.198.69.140,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n1301,1,30,2017-02-24 14:19:48,http://cole.name/isidro_bauch,,101.109.62.59,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n1302,1,30,2017-03-07 02:10:29,http://schowalterkoch.biz/mandy_hayes,,83.172.190.76,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n1303,1,30,2017-01-26 12:29:20,http://romaguera.name/fabiola,,111.36.12.238,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n1304,1,30,2017-03-12 12:50:22,http://carter.net/kelley.spencer,,3.93.178.87,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n1305,1,30,2017-03-23 20:52:22,http://fahey.com/dan_hermann,,162.24.201.77,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n1306,1,30,2017-02-15 20:44:17,http://goldnerhand.io/rachael,,68.219.202.82,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n1307,1,30,2017-05-20 17:58:43,http://mckenzie.org/laurianne,,68.17.173.171,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n1308,1,30,2017-05-29 10:57:33,http://conroy.info/katelin,,201.71.208.114,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n1309,1,30,2017-02-07 08:30:27,http://ernser.io/marques,,150.58.64.180,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n1310,1,30,2016-12-21 05:47:10,http://faheyfadel.io/kip,,25.147.67.91,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n1311,1,30,2017-02-06 03:15:24,http://corkery.info/geovany,,168.194.154.31,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n1312,1,30,2017-05-27 10:49:39,http://collier.co/gardner_skiles,,112.232.179.65,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n1313,1,30,2017-04-02 03:11:08,http://mckenzie.com/liana_hauck,,71.212.189.254,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n1314,1,30,2017-02-23 14:25:44,http://barton.co/robb,,92.221.170.184,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n1315,1,30,2017-01-12 16:38:40,http://swaniawskidouglas.io/annamae,,230.236.77.86,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n1316,1,30,2017-05-05 07:07:44,http://marquardt.io/mary,,93.10.94.76,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n1317,1,30,2017-05-03 23:37:29,http://vonrueden.biz/caandra_ferry,,129.232.240.171,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n1318,1,30,2017-02-23 23:12:34,http://huels.biz/maximillian,,199.239.81.207,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n1319,1,30,2017-06-02 07:55:56,http://wunsch.name/sophie,,151.216.94.209,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n1320,1,30,2017-04-04 01:00:55,http://pfannerstillebert.biz/ted,,175.215.206.114,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n1321,1,30,2017-05-16 03:41:40,http://harrisaltenwerth.name/kristopher,,26.13.246.244,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n1322,1,30,2017-06-06 02:05:52,http://bartell.info/erna.powlowski,,22.228.134.116,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n1323,1,30,2017-01-20 14:07:53,http://corkerykeler.io/aliza.erdman,,22.126.169.61,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n1324,1,30,2017-05-09 07:06:14,http://kirlin.co/rigoberto,,59.83.44.91,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n1325,1,30,2017-03-16 00:42:12,http://gutkowski.name/rubye,,86.169.136.43,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n1326,1,30,2017-02-09 04:40:25,http://leschstrosin.org/ethelyn,,178.4.191.27,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n1327,1,30,2017-06-08 22:32:12,http://lebsack.co/tia,,155.236.15.168,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n1328,1,31,2017-03-10 20:57:07,http://boyle.info/rigoberto_heel,,83.7.164.104,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n1329,1,31,2017-05-16 00:35:24,http://boyer.name/rita.kunde,,66.114.82.210,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n1330,1,31,2017-03-08 17:56:01,http://satterfieldkuhic.io/mack_adams,,130.61.20.55,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n1331,1,31,2017-01-26 11:46:35,http://oconnell.biz/sherman,,155.159.129.149,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n1332,1,31,2017-01-10 20:21:20,http://roberts.info/guido_gorczany,,227.86.15.230,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n1333,1,31,2017-03-22 00:41:46,http://tillman.biz/lucile_bode,,179.127.165.179,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n1334,1,31,2017-01-15 17:01:48,http://zboncak.biz/heaven.bergnaum,,140.145.4.121,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n1335,1,31,2016-12-24 04:30:44,http://walterkuvalis.co/ayden_rice,,92.218.87.31,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n1336,1,31,2017-02-17 07:44:30,http://kuvalis.com/erwin,,173.183.191.184,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n1337,1,31,2017-04-17 16:52:37,http://vonruedenlehner.biz/keely,,43.181.36.245,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n1338,1,31,2016-12-22 20:22:13,http://greenholt.name/maudie.borer,,27.140.46.68,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n1339,1,31,2017-05-24 03:01:01,http://metz.io/michele,,110.114.184.251,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n1340,1,31,2017-05-02 12:05:25,http://bogisich.io/audra,,166.104.70.24,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n1341,1,31,2016-12-26 14:38:24,http://schumm.com/favian_larson,,33.26.244.205,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n1342,1,31,2017-04-21 15:01:27,http://dibbert.co/isai,,206.47.229.124,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n1343,1,31,2017-03-01 08:21:35,http://ward.com/carter,,49.134.70.222,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n1344,1,31,2017-04-17 09:12:47,http://okon.net/alden_thompson,,175.198.246.114,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n1345,1,31,2016-12-29 12:27:49,http://bahringer.io/bridie.wilderman,,178.172.207.186,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n1346,1,31,2017-01-07 15:46:42,http://krajcik.info/elinore_trantow,,184.247.82.85,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n1347,1,31,2017-03-23 22:06:27,http://hammes.co/owen,,240.160.128.69,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n1348,1,31,2017-02-08 18:59:23,http://johnston.name/saul_sipes,,100.136.151.101,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n1349,1,31,2017-06-02 17:02:35,http://kaulkemayer.net/carmelo_casper,,39.83.56.45,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n1350,1,31,2017-03-15 11:44:12,http://kovacek.biz/precious.davis,,162.2.73.90,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n1351,1,31,2017-03-13 20:47:11,http://homenickfarrell.org/isac,,38.161.92.191,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n1352,1,31,2017-02-17 12:11:09,http://schillermills.biz/melisa,,194.239.129.140,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n1353,1,31,2017-04-25 10:09:47,http://welch.com/karley,,120.202.239.96,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n1354,1,31,2017-06-04 23:59:08,http://gerholdschmitt.com/maxime.lowe,,71.52.145.60,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n1355,1,31,2017-02-22 11:20:28,http://hansenlowe.info/glen,,128.28.22.198,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n1356,1,31,2017-02-22 22:44:36,http://strosindietrich.biz/catalina,,162.93.159.166,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n22385,8,502,2017-02-21 19:23:20,http://luettgencorwin.org/pete,,216.31.51.190,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n22386,8,502,2017-03-22 05:35:48,http://johnston.org/anderson,,153.82.132.22,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n22387,8,502,2017-01-23 05:37:27,http://kemmer.name/malachi.dicki,,201.139.12.224,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n22388,8,502,2017-02-17 17:22:40,http://connelly.info/meagan.reichert,,145.232.233.79,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n22389,8,502,2017-03-09 15:00:28,http://feil.co/delta_denesik,,115.11.28.82,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n22390,8,502,2017-06-12 02:31:32,http://stracke.net/montana_senger,,239.228.248.21,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n22391,8,502,2017-02-02 15:05:47,http://bins.info/laurie,,33.115.171.236,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n22392,8,502,2017-05-29 00:57:54,http://swift.name/jalon,,29.141.133.193,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n22393,8,502,2017-06-03 17:45:59,http://cummings.io/emil.gleichner,,197.62.201.212,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n22394,8,502,2017-03-18 16:10:32,http://simoniscorwin.biz/bette,,84.219.198.239,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n22395,8,502,2017-04-14 08:39:10,http://goldner.org/alphonso,,57.92.235.109,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n22396,8,502,2017-02-15 18:07:00,http://kling.io/arnoldo,,174.66.134.25,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n22397,8,502,2016-12-23 02:24:05,http://croninbergstrom.io/ashleigh_funk,,150.203.234.232,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n22398,8,502,2017-03-02 13:59:25,http://cummeratawunsch.biz/christelle,,220.131.205.251,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n22399,8,502,2017-03-16 19:55:16,http://howe.name/osborne,,48.235.28.19,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n22400,8,502,2017-05-11 06:01:50,http://lesch.io/marlin,,172.86.5.133,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n22401,8,502,2017-03-10 10:03:03,http://zemlakjast.org/tiffany_heidenreich,,183.64.51.38,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n22402,8,502,2017-03-31 13:29:47,http://okon.biz/lemuel,,87.105.71.27,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n22403,8,502,2017-03-24 08:21:11,http://schumm.name/vivianne.zulauf,,252.125.220.43,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n22404,8,502,2017-04-02 03:04:17,http://fadeldamore.io/matt,,8.222.6.140,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n22405,8,502,2017-01-13 18:56:33,http://kemmer.org/maegan,,41.48.73.119,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n22406,8,502,2017-02-07 15:32:36,http://legros.info/merritt,,125.222.106.79,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n22407,8,503,2016-12-20 21:15:04,http://marquardt.biz/reggie,,82.89.118.227,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n22408,8,503,2017-04-21 19:51:38,http://mitchell.io/darlene.stark,,25.21.90.244,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n22409,8,503,2017-04-21 10:28:10,http://gorczany.co/jaylan,,87.115.108.106,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n22410,8,503,2016-12-18 13:51:21,http://quitzonschaden.org/avery,,206.11.123.123,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n22411,8,503,2017-05-04 01:52:08,http://nolan.net/kellie.effertz,,150.35.92.59,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n22412,8,503,2017-01-15 08:26:42,http://lemkecrist.biz/austin,,104.88.240.60,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n22413,8,503,2016-12-21 20:22:24,http://gradygreenfelder.biz/hulda,,150.79.29.147,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n22414,8,503,2017-03-31 12:51:11,http://windlerrunolfon.com/loraine_rice,,246.160.80.126,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n22415,8,503,2017-03-17 04:03:57,http://jaskolski.biz/lily,,201.175.247.60,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n22416,8,503,2016-12-15 11:35:37,http://bayer.net/camila,,88.70.251.62,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n22417,8,503,2016-12-16 20:49:28,http://bradtke.org/gunnar_heller,,40.2.240.157,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n22418,8,503,2017-01-22 05:38:25,http://streich.co/adah,,203.190.37.161,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n22419,8,503,2017-02-14 01:14:04,http://greenwitting.biz/amie,,218.51.12.97,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n22420,8,503,2017-01-30 12:37:53,http://langosh.name/estel,,239.50.212.76,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n22421,8,503,2016-12-14 07:15:28,http://volkman.info/bo.macejkovic,,39.240.149.70,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n22422,8,503,2017-04-27 11:23:04,http://muller.com/brooke,,175.191.73.243,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n22423,8,503,2017-01-04 23:10:06,http://becker.biz/treie_ruecker,,156.133.33.151,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n22424,8,503,2016-12-27 16:16:10,http://howellgottlieb.net/jarvis,,124.178.3.191,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n22425,8,503,2017-03-23 06:38:33,http://hahn.org/estell_mcdermott,,65.143.16.248,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n22426,8,503,2017-05-07 20:41:47,http://corwinhansen.co/tremayne.halvorson,,180.95.110.122,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n22427,8,503,2017-05-13 07:23:14,http://tremblay.co/betsy,,250.132.214.191,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n22428,8,503,2017-06-03 21:19:17,http://cronin.name/aryanna_gerlach,,218.3.166.102,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n22429,8,503,2017-03-30 21:59:47,http://cain.biz/karl_stroman,,26.127.228.35,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n22430,8,503,2017-01-15 14:25:33,http://cole.org/reid_dietrich,,14.253.125.60,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n22431,8,503,2017-04-22 08:34:16,http://hermiston.com/bradley_hermiston,,128.105.29.122,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n22432,8,503,2017-03-16 16:43:09,http://borerkertzmann.org/patsy,,212.55.202.24,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n22433,8,503,2017-02-03 01:52:20,http://leffler.co/jannie,,53.139.220.13,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n22434,8,503,2017-04-13 01:33:57,http://weimann.org/weston.heidenreich,,192.59.89.254,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n22435,8,503,2017-05-22 17:35:46,http://lindgrengutkowski.org/geo.frami,,25.150.90.157,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n22436,8,503,2017-04-28 13:08:14,http://satterfield.io/aliza_walker,,105.83.102.199,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n22437,8,503,2017-05-22 15:09:50,http://prosaccobauch.co/kimberly_fahey,,210.35.39.189,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n22438,8,503,2017-05-29 12:24:15,http://huels.biz/greta,,246.199.47.185,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n22439,8,503,2017-05-25 00:09:00,http://padberg.co/rebeka,,239.158.39.115,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n22440,8,503,2017-01-06 15:56:40,http://bahringer.com/winifred,,251.248.245.191,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n1357,1,31,2017-04-20 01:05:31,http://cremin.biz/ewell.price,,206.23.43.118,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n1358,1,31,2017-04-24 14:41:23,http://jenkinskunde.org/hardy.batz,,211.96.214.86,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n1359,1,31,2017-02-01 00:55:27,http://quitzonschaden.org/godfrey_block,,130.29.22.242,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n1360,1,31,2017-01-01 20:58:45,http://harber.name/kamron_spinka,,194.98.183.247,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n1361,1,31,2017-06-04 07:37:53,http://deckow.co/amely_lindgren,,79.227.136.48,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n1362,1,31,2017-06-05 05:33:14,http://goyette.info/kaitlin.brekke,,169.121.11.85,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n1363,1,31,2017-03-17 19:47:11,http://pagac.com/nona,,102.141.231.15,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n1364,1,31,2016-12-24 00:40:44,http://wolffkovacek.com/judah.brakus,,235.219.7.200,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n1365,1,31,2017-03-21 06:00:31,http://jerde.co/johnnie_moriette,,65.172.58.170,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n1366,1,31,2017-02-01 22:01:03,http://bednar.net/marianna_marvin,,52.21.13.48,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n1367,1,31,2017-01-18 02:14:46,http://ohara.org/veronica,,64.32.247.171,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n1368,1,31,2017-03-20 03:52:23,http://auer.co/earl,,171.197.88.39,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n1369,1,31,2016-12-29 13:25:25,http://simonis.com/immanuel.hirthe,,14.174.196.47,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n1370,1,31,2017-03-01 07:55:38,http://okeefe.co/tara.schinner,,212.177.203.49,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n1371,1,31,2016-12-26 04:44:47,http://langbailey.biz/chelsey_macgyver,,17.159.83.239,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n1372,1,31,2017-01-07 08:24:11,http://osinskispencer.org/dulce,,134.17.250.131,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n1373,1,31,2017-05-29 03:24:37,http://hermann.biz/keshawn_graham,,136.125.159.147,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n1374,1,31,2017-02-16 01:35:30,http://bergstrom.name/jerrold_cole,,105.186.28.200,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n1375,1,31,2017-01-22 01:51:48,http://windlerromaguera.io/lambert.jones,,168.60.230.132,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n1376,1,31,2017-03-18 13:36:21,http://shanahan.biz/leonora_bosco,,165.177.179.169,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n1377,1,32,2017-02-03 10:35:24,http://bradtke.io/quincy,,134.236.50.140,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n1378,1,32,2017-04-05 03:03:29,http://auer.name/bulah,,118.174.96.212,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n1379,1,32,2017-05-30 19:33:50,http://friesen.biz/daphne,,28.53.104.225,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n1380,1,32,2017-02-23 16:18:42,http://dibbert.name/heloise_breitenberg,,228.195.75.204,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n1381,1,32,2017-04-03 15:13:09,http://ryanklocko.io/augustus.beier,,145.113.64.126,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n1382,1,32,2017-02-04 10:41:57,http://schadenmedhurst.net/frederique,,144.152.69.220,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n1383,1,32,2017-04-08 00:13:12,http://rau.org/damaris_gleichner,,126.141.48.90,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n1384,1,32,2017-05-18 03:53:51,http://lynch.biz/ottilie.jakubowski,,53.138.205.6,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n1385,1,32,2017-02-17 09:38:32,http://pauceksenger.biz/cody,,141.227.254.7,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n1386,1,32,2017-04-08 10:41:37,http://johnsonosinski.net/benedict.padberg,,233.142.95.125,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n1387,1,32,2017-05-08 14:13:28,http://schoen.biz/katelynn_jast,,199.80.210.50,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n1388,1,32,2016-12-19 01:07:46,http://torphy.net/camila,,107.124.94.75,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n1389,1,32,2017-05-12 04:34:12,http://greenfelder.io/maegan_schmitt,,173.30.182.126,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n1390,1,32,2017-02-25 11:50:13,http://kaulke.net/ezequiel,,181.23.74.127,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n1391,1,32,2017-06-11 15:52:25,http://brown.name/verlie_kunze,,114.49.140.83,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n1392,1,32,2017-05-20 10:26:13,http://kovacekkeler.co/elvie_oconnell,,248.14.251.188,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n1393,1,32,2017-03-07 00:01:46,http://mertzmurray.io/alexandria_beer,,147.86.154.248,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n1394,1,32,2017-04-11 07:23:12,http://langoshhills.co/idella,,113.58.165.151,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n1395,1,32,2017-02-03 02:34:25,http://sanford.io/presley,,11.200.134.64,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n1396,1,32,2017-02-22 09:33:50,http://streich.io/samir,,229.250.195.180,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n1397,1,32,2017-03-09 22:05:01,http://bauch.org/roselyn.maggio,,102.58.57.204,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n1398,1,32,2017-02-13 03:54:20,http://franecki.io/kendrick_schneider,,63.41.90.139,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n1399,1,33,2017-03-17 08:03:25,http://weberrunolfon.info/cordelia,,38.158.171.248,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n1400,1,33,2016-12-26 11:36:46,http://cummerata.com/aimee.smith,,167.57.67.145,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n1401,1,33,2017-03-23 09:42:14,http://wuckert.com/tania,,214.230.233.180,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n1402,1,33,2017-04-25 17:13:55,http://dubuque.biz/kayleigh.bergnaum,,186.185.73.29,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n1403,1,33,2017-03-08 20:36:34,http://bodeward.info/charity,,174.97.58.44,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n1404,1,33,2017-02-23 08:39:13,http://raugleason.com/charley,,226.132.94.73,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n1405,1,33,2017-01-23 18:52:38,http://farrell.biz/darrion,,249.70.231.71,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n1406,1,33,2017-04-22 16:45:11,http://rice.net/bertram_batz,,197.152.251.64,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n1407,1,33,2017-01-27 09:58:21,http://dibbert.biz/waldo.kuhn,,137.230.90.181,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n1408,1,33,2017-02-13 22:18:49,http://runte.io/kayley,,129.40.240.171,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n1409,1,33,2017-01-31 07:38:49,http://beattyledner.biz/anika,,20.34.171.93,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n1410,1,33,2017-04-26 15:34:45,http://damore.name/sam,,26.171.30.115,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n1411,1,33,2017-02-20 08:01:31,http://kuhic.info/gavin.batz,,84.41.50.23,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n1412,1,33,2017-03-04 13:59:11,http://hamillhintz.org/curt,,65.8.49.106,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n22441,8,503,2017-02-14 16:12:32,http://emard.com/jane.muller,,40.180.25.181,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n22442,8,503,2017-01-05 08:16:31,http://jacobson.name/verdie,,85.56.238.200,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n22443,8,503,2017-03-08 00:05:40,http://welchschroeder.io/jordyn_heidenreich,,117.62.167.113,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n22444,8,503,2017-03-25 13:51:49,http://kohlerfarrell.name/velma,,154.118.199.112,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n22445,8,503,2017-01-24 04:38:03,http://ohara.net/celia.davis,,132.153.143.205,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n22446,8,503,2017-01-30 21:28:40,http://mertz.co/ellis,,120.204.92.235,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n22447,8,503,2017-02-24 03:14:50,http://hane.name/melya,,225.197.66.224,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n22449,8,503,2017-03-24 16:19:27,http://lemke.co/easter,,2.118.81.162,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n22450,8,504,2017-04-11 10:01:27,http://rau.info/jee,,12.181.158.137,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n22451,8,504,2017-02-17 01:34:34,http://torp.net/chase,,24.194.253.49,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n22452,8,504,2017-01-27 21:02:16,http://oconnell.net/lukas_pfannerstill,,59.39.49.228,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n22453,8,504,2017-01-07 11:17:53,http://haag.net/michel_mertz,,231.216.240.92,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n22454,8,504,2017-02-02 10:58:14,http://hodkiewiczmetz.info/jairo_considine,,205.220.64.63,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n22455,8,504,2016-12-19 09:34:52,http://ullrichking.com/ned.becker,,169.65.36.19,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n22456,8,504,2017-03-05 08:17:31,http://erdman.info/earnestine_walter,,78.95.121.30,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n22457,8,504,2017-05-22 11:57:52,http://toy.info/lavinia_ruel,,74.72.71.50,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n22458,8,504,2017-05-04 03:46:28,http://walter.io/ike.bauch,,133.17.58.29,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n22459,8,504,2016-12-26 11:43:09,http://hartmanndach.net/blair.huel,,86.63.23.45,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n22460,8,504,2017-05-17 23:57:47,http://macgyver.name/glenda,,232.214.87.247,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n22461,8,504,2017-05-03 12:59:12,http://littelstiedemann.com/newell,,77.62.103.85,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n22462,8,504,2017-04-13 17:04:51,http://breitenberg.com/osborne,,177.64.101.153,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n22463,8,504,2016-12-20 14:04:29,http://rowe.info/winona_wilkinson,,187.194.91.107,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n22464,8,504,2016-12-31 15:48:36,http://schoen.co/wilfrid.adams,,6.179.177.119,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n22465,8,504,2016-12-20 11:23:59,http://koelpin.io/bertha,,126.145.167.36,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n22466,8,504,2016-12-21 12:23:28,http://koch.com/demarco_bailey,,87.238.190.57,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n22467,8,504,2016-12-25 14:28:32,http://ornoberbrunner.com/ferne.reynolds,,72.57.194.73,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n22468,8,504,2017-05-29 06:03:30,http://davis.io/woodrow.eichmann,,104.129.206.47,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n22469,8,504,2017-05-02 02:15:39,http://volkman.net/tatyana.stark,,41.103.93.196,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n22470,8,504,2017-03-20 09:03:51,http://wiegand.net/terrill,,139.80.104.44,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n22471,8,504,2017-04-10 01:45:53,http://raynor.co/wyman,,130.253.54.245,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n22472,8,504,2017-01-27 17:02:56,http://rohan.info/demario.legros,,10.186.194.140,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n22473,8,504,2017-05-03 21:12:41,http://bernhardbailey.net/hildegard_reilly,,245.7.14.64,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n22474,8,504,2017-03-10 18:00:29,http://daniel.name/amya_kiehn,,80.17.246.39,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n22475,8,504,2017-04-08 05:13:09,http://robel.info/franz,,48.113.74.188,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n22476,8,504,2017-05-11 23:38:03,http://harberschmeler.com/ellie,,66.139.246.238,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n22477,8,504,2016-12-21 08:54:28,http://gerholdschneider.biz/glenna.goyette,,237.103.46.232,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n22478,8,504,2017-06-13 05:18:57,http://bauch.com/kaley,,187.41.200.173,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n22479,8,504,2017-01-26 14:25:44,http://rolfsonlangworth.com/josue,,171.108.72.121,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n22480,8,504,2017-05-01 23:16:34,http://haag.io/darius_rogahn,,202.54.239.65,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n22481,8,504,2017-02-04 19:13:05,http://lemke.com/meredith.prohaska,,160.200.65.115,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n22482,8,504,2017-02-08 15:06:12,http://kirlinnolan.co/cortney_waelchi,,38.28.192.164,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n22483,8,504,2017-04-06 21:13:30,http://hegmannshanahan.name/wilber,,235.160.213.208,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n22484,8,504,2017-02-15 16:03:11,http://purdy.org/keira.adams,,11.195.217.210,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n22485,8,504,2017-02-17 11:37:32,http://smith.name/emmanuel,,60.134.45.63,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n22486,8,504,2017-01-05 13:17:26,http://gerhold.io/ted,,117.68.23.220,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n22487,8,504,2017-05-24 12:33:26,http://dooleysmitham.info/taylor,,87.20.136.26,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n22488,8,504,2017-02-24 12:44:40,http://grady.co/madisyn.hoppe,,21.74.144.174,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n22489,8,504,2016-12-21 04:46:43,http://andersonbednar.info/esther.wehner,,114.36.92.103,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n22490,8,504,2017-04-03 22:08:53,http://oharaterry.name/kenneth_dibbert,,161.58.103.230,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n22491,8,504,2017-06-12 19:04:24,http://gulgowskischuster.org/ford.windler,,94.100.72.157,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n22492,8,504,2017-06-08 07:31:30,http://cain.com/audra.cartwright,,164.144.30.9,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n22493,8,504,2017-06-13 13:29:56,http://maggiokeeling.info/sabina.lynch,,200.169.186.167,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n22494,8,504,2017-01-09 23:57:05,http://cummerata.biz/zula,,97.197.178.250,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n22495,8,504,2017-01-19 16:50:08,http://ortizstark.org/syble.osinski,,156.21.239.180,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n22496,8,504,2017-06-06 12:21:01,http://hackett.com/bret,,72.93.124.208,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n1413,1,33,2017-02-25 07:32:27,http://heidenreichwatsica.io/robb.heller,,20.238.209.72,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n1414,1,33,2017-02-15 17:47:21,http://gutmann.info/zoey.abbott,,237.197.199.108,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n1415,1,33,2017-04-26 04:44:00,http://bahringer.com/gunner.howell,,224.172.67.65,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n1416,1,33,2017-03-19 11:18:50,http://morarruecker.co/schuyler,,183.92.82.238,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n1417,1,33,2017-03-22 18:22:01,http://funk.name/alvah,,20.50.171.57,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n1418,1,33,2017-02-23 23:05:32,http://daugherty.name/nathan,,102.11.214.137,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n1419,1,33,2017-03-15 23:15:22,http://dubuque.com/gracie,,242.41.36.136,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n1420,1,33,2017-06-09 12:19:59,http://nicolas.info/saul.king,,213.184.223.78,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n1421,1,33,2017-02-03 01:13:12,http://graham.biz/maurine.dickinson,,17.77.158.166,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n1422,1,33,2017-03-01 03:31:59,http://wizafeil.biz/luisa,,214.100.37.152,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1423,1,33,2017-05-17 17:38:40,http://hettinger.org/chet,,93.204.194.204,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n1424,1,33,2016-12-31 06:00:30,http://reichel.biz/myles,,100.199.206.163,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n1425,1,33,2017-04-27 08:22:55,http://nienow.biz/name,,185.250.65.140,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n1426,1,33,2017-04-29 20:00:37,http://schuppe.io/cheyenne,,182.104.156.48,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n1427,1,33,2016-12-14 07:26:49,http://spinka.info/lorenz,,142.3.214.240,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n1428,1,33,2017-04-22 18:36:40,http://crist.org/easter,,217.176.103.127,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n1429,1,33,2017-02-11 19:21:58,http://howe.name/fannie,,57.4.56.71,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n1430,1,33,2017-01-30 20:30:54,http://king.name/pascale,,242.18.166.5,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n1431,1,33,2017-01-22 18:06:35,http://wilderman.name/tatum,,185.200.107.62,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n1432,1,33,2016-12-15 00:20:47,http://okunevanolan.co/jensen.lueilwitz,,196.148.102.109,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n1433,1,33,2016-12-16 15:12:06,http://beier.co/hilton,,15.246.148.181,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n1434,1,33,2017-06-08 03:13:27,http://gislason.io/bonnie,,172.107.39.232,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n1435,1,33,2016-12-30 22:59:12,http://dibbert.info/beie,,220.251.125.233,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n1436,1,33,2017-02-14 01:02:06,http://roobkrajcik.org/jarret_erdman,,93.41.207.99,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n1437,1,33,2017-03-14 17:09:55,http://swift.io/ayden,,30.66.113.200,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n1438,1,33,2017-02-02 06:44:35,http://fay.name/emilie.heidenreich,,75.199.208.248,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n1439,1,33,2017-05-04 08:03:59,http://prosaccoheidenreich.com/libby,,250.57.118.155,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n1440,1,33,2017-04-12 01:51:51,http://hoegerswaniawski.org/rebekah,,25.218.6.179,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n1441,1,33,2017-05-30 04:59:35,http://prosacco.net/kiera.breitenberg,,232.112.15.46,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n1442,1,33,2017-05-06 06:42:46,http://ruel.com/wilfredo,,243.62.142.102,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n1443,1,33,2017-01-20 06:11:45,http://klingbahringer.org/hailie_willms,,103.37.48.119,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n1444,1,33,2017-02-21 09:05:13,http://balistreri.info/jovani,,155.144.162.32,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n1445,1,33,2017-01-09 23:57:36,http://shieldskreiger.io/judd,,70.169.233.73,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n1446,1,33,2017-03-18 03:01:39,http://hartmannsmitham.co/emily,,84.110.191.115,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n1447,1,33,2017-02-28 16:09:46,http://mayer.net/alford,,33.59.73.15,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n1448,1,33,2017-06-05 23:56:20,http://wilkinson.name/delta.brown,,95.17.231.156,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n1449,1,33,2017-05-18 09:15:09,http://upton.com/vidal.mcglynn,,225.163.182.69,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n1450,1,33,2017-04-09 18:53:56,http://schmidtokon.biz/michale_swift,,123.157.129.112,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n1451,1,33,2017-05-06 11:29:45,http://damore.name/pierre,,119.197.56.110,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n1452,1,33,2017-01-04 04:47:25,http://thompson.net/lula.littel,,38.230.23.42,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n1453,1,33,2017-05-06 15:18:01,http://mcdermott.info/albin.bogan,,114.40.180.171,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n1454,1,33,2017-02-12 15:35:15,http://toy.biz/everett,,164.185.53.64,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n1455,1,33,2017-03-18 06:22:52,http://hauck.io/ava_treutel,,63.56.20.77,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n1456,1,33,2017-01-03 19:25:06,http://reingershields.biz/gaylord.schuppe,,216.230.179.184,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n1457,1,33,2017-05-12 12:41:49,http://hickledaniel.net/mallory_grady,,163.60.88.62,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n1458,1,33,2017-03-14 05:18:13,http://hellerharvey.name/esta_crooks,,236.121.189.13,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n1459,1,33,2017-02-02 00:35:37,http://goodwinziemann.net/berenice_white,,162.98.49.33,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n1460,1,33,2017-03-22 22:26:11,http://gorczany.info/johnny,,7.68.103.8,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n1461,1,33,2017-04-09 11:36:10,http://kuvalis.io/willis.ratke,,144.220.157.121,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n1462,1,33,2017-01-08 23:23:10,http://howell.biz/jensen,,4.88.150.176,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n1463,1,33,2016-12-24 06:12:49,http://greenholtgulgowski.biz/elsa.leuschke,,30.212.30.100,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n1464,1,33,2017-05-18 09:44:50,http://flatleylang.io/ruben,,82.36.17.199,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n1465,1,33,2017-04-18 06:18:17,http://willms.biz/theron.crona,,203.18.20.8,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n1466,1,33,2017-01-26 11:28:07,http://steuberspinka.name/delphine,,149.51.104.181,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n1467,1,33,2017-06-03 10:49:38,http://jakubowski.net/diamond_tremblay,,201.19.214.183,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n1468,1,34,2017-03-16 14:42:11,http://borer.info/mavis_legros,,24.167.182.197,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n22497,8,504,2017-04-06 17:06:07,http://abbott.name/shanelle,,226.84.167.188,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n22498,8,504,2017-02-07 15:01:58,http://bartell.co/cesar,,43.69.203.245,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n22499,8,504,2017-02-18 11:44:10,http://cummingtrosin.biz/damion,,56.221.94.164,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n22500,8,504,2017-02-02 09:18:36,http://cartwrightking.io/dante,,23.127.19.52,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n22501,8,504,2017-03-30 13:09:10,http://parkerebert.com/royce_moen,,40.196.114.41,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n22502,8,504,2017-05-21 09:46:58,http://morarkuhlman.info/baylee,,179.147.104.34,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n22503,8,504,2017-01-11 03:46:36,http://kutch.name/alberto.mcglynn,,97.15.105.90,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n22504,8,504,2017-02-21 12:44:42,http://grantbradtke.io/soledad,,112.64.31.152,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n22505,8,504,2017-03-05 02:38:28,http://oconner.biz/ebony_jacobi,,36.139.5.162,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n22506,8,504,2017-01-07 09:40:56,http://auerflatley.name/odea_ruel,,155.86.59.112,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n22507,8,504,2017-03-29 03:08:08,http://powlowski.org/kailee.haag,,39.30.188.139,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n22508,8,505,2017-03-07 06:39:04,http://bernierstracke.org/allan_baumbach,,128.50.138.165,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n22509,8,505,2017-06-01 06:11:30,http://torpprohaska.biz/alanna,,132.209.245.7,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n22510,8,505,2017-01-30 21:29:13,http://schmidt.com/valentina,,8.183.77.145,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n22511,8,505,2017-04-01 15:16:50,http://yundt.org/evie,,165.84.221.153,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n22512,8,505,2017-04-12 08:43:00,http://fayhammes.org/jose.walker,,7.95.10.121,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n22513,8,505,2017-01-02 09:05:15,http://cronastoltenberg.biz/scottie,,228.70.208.88,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n22514,8,505,2017-04-01 02:26:37,http://roberts.info/jerry_nader,,40.247.41.148,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n22515,8,505,2017-02-25 02:08:54,http://harris.org/benny.marvin,,45.209.243.200,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n22516,8,505,2017-05-22 21:33:25,http://schulist.info/destin,,23.193.27.34,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n22517,8,505,2017-03-05 02:15:05,http://donnellyullrich.co/coty,,228.117.188.3,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n22518,8,505,2017-01-15 15:39:28,http://rodriguez.co/gia.adams,,103.192.192.170,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n22519,8,505,2017-01-31 10:05:20,http://beer.net/antoinette,,208.148.5.201,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n22520,8,505,2017-02-24 12:25:32,http://kling.info/noel.hudson,,60.224.137.248,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n22521,8,505,2017-05-17 23:33:08,http://jaskolski.biz/kaia,,181.210.55.214,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n22522,8,505,2017-02-27 00:25:48,http://kunde.org/margarette.franecki,,46.134.167.246,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n22523,8,505,2017-01-18 11:15:34,http://huels.info/friedrich.ohara,,228.248.23.207,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n22524,8,505,2017-03-21 01:41:23,http://friesen.biz/angus,,34.223.4.58,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n22525,8,505,2017-01-09 18:37:17,http://dickinson.co/edwin,,28.184.74.102,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n22526,8,505,2017-02-03 06:15:25,http://block.com/hunter,,162.78.174.133,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n22527,8,505,2017-05-25 19:28:47,http://tromp.net/nestor,,207.237.12.122,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n22528,8,506,2017-01-01 14:47:45,http://reichert.co/valentine.terry,,231.66.221.4,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n22529,8,506,2017-06-01 23:04:53,http://pauceklangworth.co/susanna.weimann,,143.251.35.180,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n22530,8,506,2017-03-13 18:40:22,http://abbott.io/noelia.larson,,245.159.235.116,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n22531,8,506,2017-01-05 02:54:07,http://farrellgutkowski.info/mikel,,168.150.197.249,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n22532,8,506,2017-05-17 22:21:26,http://leannonzboncak.com/fletcher.pacocha,,115.216.224.43,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n22533,8,506,2017-04-08 07:01:25,http://bayer.name/mose.ward,,42.100.126.145,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n22534,8,506,2016-12-28 21:05:53,http://hegmannemard.net/reese.greenfelder,,101.115.152.43,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n22535,8,506,2017-02-13 14:56:42,http://runolfsdottir.info/britney.herman,,175.71.145.244,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n22536,8,506,2017-05-09 08:46:01,http://mertznitzsche.io/raheem_stokes,,54.167.226.30,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n22537,8,506,2017-05-22 07:06:25,http://keelingbergnaum.com/kareem,,63.92.148.44,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n22538,8,506,2017-03-04 15:49:40,http://welch.com/lilian,,22.238.27.40,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n22539,8,506,2017-05-23 14:58:47,http://oreillyhirthe.biz/veda,,155.111.169.43,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n22540,8,506,2017-05-26 12:37:01,http://fahey.com/haan.herman,,16.42.129.190,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n22541,8,506,2017-01-19 21:37:31,http://connmckenzie.co/amari,,56.99.39.128,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n22542,8,506,2017-04-09 11:18:01,http://schinner.com/aurore.borer,,97.109.173.56,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n22543,8,506,2017-06-01 06:13:06,http://beahan.info/eulalia,,190.81.28.166,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n22544,8,506,2017-04-13 14:49:51,http://armstrongcronin.name/kari.ruecker,,188.27.198.120,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n22545,8,506,2017-06-12 14:35:04,http://lind.name/izaiah_west,,15.227.215.159,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n22546,8,506,2017-02-20 23:45:44,http://ward.org/deborah,,39.125.61.134,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n22547,8,506,2017-01-07 12:32:59,http://smithammoore.com/keeley_okuneva,,31.14.121.73,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n22548,8,506,2017-06-01 05:50:10,http://wuckert.org/kristofer,,100.153.104.82,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n22549,8,506,2017-05-16 01:08:26,http://carterreichel.com/stan.aufderhar,,112.12.46.168,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n22550,8,506,2017-04-24 17:39:24,http://cainjohnson.org/alexander,,81.140.215.254,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n22551,8,506,2017-01-01 13:08:45,http://hane.com/maegan,,60.225.87.70,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n1469,1,34,2017-04-19 02:26:53,http://romaguera.com/karson,,91.10.113.168,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n1470,1,34,2017-03-02 13:23:51,http://shields.info/frederique_wyman,,250.135.29.35,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n1471,1,34,2017-02-02 11:15:52,http://lemke.io/kellie,,120.182.131.56,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n1472,1,34,2017-01-16 07:20:14,http://ferry.info/norwood_hartmann,,58.203.32.244,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n1473,1,34,2017-03-15 07:00:34,http://davis.com/eve,,41.171.223.37,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n1474,1,34,2017-02-23 22:59:58,http://kub.name/saul,,186.91.19.22,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n1475,1,34,2017-01-05 02:10:41,http://rodrigueznikolaus.name/mariah_fay,,210.125.100.109,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n1476,1,34,2017-01-16 20:43:20,http://koelpinwunsch.co/angela_smitham,,103.169.127.143,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n1477,1,34,2016-12-16 21:09:27,http://ebert.com/abby,,219.15.243.224,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n1478,1,34,2017-06-01 13:30:30,http://murazik.biz/reinhold,,34.32.100.22,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n1479,1,34,2017-05-23 15:31:45,http://barton.io/brendon,,241.95.24.143,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n1480,1,34,2017-01-23 09:58:52,http://kundehaley.net/sarah,,148.70.250.49,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n1481,1,34,2017-01-12 03:54:04,http://mcclure.net/gilda_baumbach,,128.77.11.21,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n1482,1,34,2017-02-15 00:34:49,http://rodriguezluettgen.name/sienna,,238.232.229.17,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n1483,1,34,2017-03-25 06:10:33,http://franecki.io/may,,87.209.168.181,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n1484,1,34,2017-04-18 14:22:42,http://dachjenkins.biz/matilda,,4.116.189.3,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n1485,1,34,2017-01-16 22:37:12,http://rolfson.info/susanna,,149.232.190.144,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n1486,1,34,2016-12-29 19:45:12,http://jaskolski.co/lazaro,,119.211.251.221,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n1487,1,34,2017-04-04 14:11:46,http://effertzdooley.biz/kathryn_weinat,,51.246.130.100,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n1488,1,34,2017-05-09 20:37:24,http://runolfsdottir.org/meaghan.mosciski,,227.203.120.251,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n1489,1,34,2017-03-18 19:39:35,http://rice.name/nelle,,202.12.123.84,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n1490,1,34,2017-04-29 10:11:00,http://greenholtvolkman.io/manuel,,137.107.37.200,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n1491,1,34,2017-03-07 04:20:36,http://leannon.info/george,,95.152.139.55,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n1492,1,34,2017-01-11 12:10:28,http://watsica.name/rashad.shanahan,,203.184.153.242,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n1493,1,34,2017-05-14 03:14:36,http://bernierschimmel.com/arvilla_altenwerth,,31.183.225.20,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n1494,1,34,2017-01-21 02:11:38,http://champlin.info/riley,,56.194.191.194,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n1495,1,34,2017-06-02 14:38:26,http://balistrerischulist.net/jimmie,,13.160.33.212,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n1496,1,34,2017-02-22 11:17:08,http://morar.com/oral,,89.240.47.230,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n1497,1,34,2017-01-06 00:37:38,http://oconnell.net/cecilia,,10.248.130.230,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n1498,1,34,2017-04-30 14:06:45,http://corwin.org/chance,,98.189.166.127,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n1499,1,34,2017-02-12 12:30:03,http://cronin.com/darien.smith,,181.9.60.102,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n1500,1,34,2017-03-21 08:19:28,http://boehm.name/maybell,,67.4.175.61,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n1501,1,34,2017-05-26 13:06:19,http://labadiecarter.co/santa.sauer,,237.33.90.154,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n1502,1,34,2016-12-22 10:51:01,http://kohler.biz/vena.greenholt,,131.232.30.138,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n1503,1,34,2016-12-29 03:45:27,http://oreillylehner.biz/abigayle.bode,,36.240.177.220,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n1504,1,34,2017-02-16 23:50:48,http://hansen.co/kaylah,,50.222.59.171,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n1505,1,35,2017-03-17 17:58:59,http://emardking.net/vada_bergstrom,,39.84.81.231,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n1506,1,35,2017-05-15 19:11:43,http://nolangutmann.name/donny_turner,,99.153.96.83,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n1507,1,35,2017-01-13 09:33:23,http://strackebergnaum.name/precious,,251.168.134.141,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n1508,1,35,2017-01-10 00:42:51,http://kleinherman.io/precious_schimmel,,68.71.141.115,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n1509,1,35,2017-02-21 17:09:50,http://christiansenreinger.io/jasen,,143.210.138.177,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n1510,1,35,2017-01-01 01:36:35,http://gerhold.net/pasquale,,216.236.132.252,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n1511,1,35,2017-05-23 18:08:12,http://mitchell.io/arnoldo.ko,,249.159.173.104,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n1512,1,35,2017-02-24 18:26:50,http://wittingschimmel.info/reynold,,174.36.113.3,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n1513,1,35,2017-01-25 00:13:37,http://buckridge.info/raphaelle_hermann,,102.82.46.181,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n1514,1,35,2017-05-23 08:34:52,http://ortiz.name/ubaldo,,179.35.48.142,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n1515,1,35,2017-02-14 19:53:39,http://sawayn.com/zaria,,46.7.180.229,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n1516,1,35,2017-06-09 14:53:56,http://stark.io/julie,,244.184.42.65,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n1517,1,35,2017-02-09 17:54:52,http://beahanbosco.io/alfonso,,102.172.29.239,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n1518,1,35,2017-03-19 19:12:40,http://weinattrantow.co/kobe,,246.77.30.185,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n1519,1,35,2017-03-17 05:07:37,http://stammmckenzie.co/hilma,,85.248.193.179,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n1520,1,35,2017-05-18 04:40:08,http://bechtelarsimonis.io/landen.hagenes,,63.167.80.73,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n1521,1,35,2016-12-13 20:24:42,http://towne.info/jailyn,,238.241.195.187,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n1522,1,35,2017-04-16 16:50:11,http://wolf.co/perry_balistreri,,99.118.40.160,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n1523,1,35,2016-12-20 10:03:58,http://mosciski.name/ransom.corwin,,176.34.147.135,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n1524,1,35,2017-05-07 00:57:22,http://dicki.com/mike,,36.196.227.90,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n22552,8,506,2017-02-03 11:44:27,http://farrellgleichner.co/vern.hand,,157.20.199.91,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n22553,8,506,2017-03-13 08:16:21,http://nolan.name/xander_nolan,,85.140.253.70,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n22554,8,506,2017-04-08 07:13:17,http://pacochasporer.net/kathryn.kemmer,,46.106.181.218,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n22555,8,506,2017-05-13 05:20:47,http://yundt.info/robert,,14.63.135.209,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n22556,8,506,2017-04-11 08:44:49,http://swaniawskifeil.biz/raegan.larkin,,236.143.131.215,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n22557,8,506,2017-06-07 18:25:10,http://turcotte.org/lisa_yundt,,169.81.94.52,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n22558,8,506,2017-01-13 17:51:02,http://crona.info/kaela.morar,,174.21.5.161,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n22559,8,506,2017-03-30 17:31:33,http://corwinmertz.info/marjory.little,,107.72.194.31,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n22560,8,506,2016-12-16 22:06:08,http://greenwisoky.biz/mitchel,,96.156.8.12,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n22561,8,506,2017-03-13 21:24:05,http://vonrueden.io/dean,,79.225.185.127,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n22562,8,506,2017-03-14 15:57:00,http://okon.info/electa.kautzer,,102.222.189.236,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n22563,8,506,2017-03-31 07:15:37,http://murphyzieme.com/bernadine,,203.159.186.8,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n22564,8,506,2017-02-20 10:48:23,http://schaefer.net/columbus,,230.177.254.10,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n22565,8,506,2017-05-12 00:35:15,http://damore.co/brian_mosciski,,158.90.189.144,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n22566,8,506,2017-01-13 08:35:16,http://baumbachberge.info/sammy,,223.170.104.114,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n22567,8,506,2017-06-11 16:35:38,http://batz.com/armani,,31.160.31.211,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n22568,8,507,2016-12-25 02:34:52,http://lubowitz.name/rachelle,,89.218.139.92,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n22569,8,507,2017-01-20 04:10:00,http://schuster.info/alvera,,27.200.80.234,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n22570,8,507,2017-05-21 13:47:04,http://kihn.name/annetta,,217.41.193.16,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n22571,8,507,2017-03-03 03:03:48,http://dickenswillms.net/rudy_toy,,222.173.40.128,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n22572,8,507,2017-04-16 08:24:49,http://carter.biz/fannie,,86.181.239.144,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n22573,8,507,2017-04-18 11:36:43,http://buckridge.biz/ena_veum,,227.23.89.193,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n22574,8,507,2016-12-19 14:57:14,http://murrayebert.co/bonita,,217.208.104.148,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n22575,8,507,2016-12-20 03:48:01,http://greenbode.biz/pinkie_fahey,,137.26.53.220,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n22576,8,507,2017-02-24 23:00:40,http://schmittcorkery.org/clinton,,243.162.79.100,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n22577,8,507,2017-03-21 05:24:14,http://harber.name/milan,,130.60.27.188,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n22578,8,507,2017-06-07 05:18:29,http://lesch.org/lizeth,,182.18.121.191,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n22579,8,507,2017-06-01 02:41:08,http://shields.org/kelsie_kunde,,160.195.230.82,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n22580,8,507,2017-06-12 04:20:53,http://prosacco.com/eddie.cole,,132.241.217.118,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n22581,8,507,2017-01-02 05:52:42,http://stokes.info/dee.orn,,8.46.98.170,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n22582,8,507,2016-12-17 04:29:26,http://hilll.info/taurean_gleason,,166.220.188.139,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n22583,8,507,2016-12-25 01:25:32,http://effertz.name/ora_ko,,3.244.111.19,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n22584,8,507,2017-06-05 02:06:44,http://stokes.org/markus,,141.203.12.242,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n22585,8,507,2017-02-11 00:54:50,http://zboncak.org/dayne,,14.23.61.213,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n22586,8,507,2017-04-16 00:42:39,http://jastkertzmann.co/tanner,,151.18.124.141,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n22587,8,507,2017-01-23 03:02:54,http://pfannerstillhermann.org/rodger,,92.143.45.72,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n22588,8,507,2017-04-03 20:57:46,http://wisozk.io/rita.hartmann,,44.17.83.177,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n22589,8,507,2017-04-30 09:09:52,http://lubowitz.com/cody,,103.133.128.65,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n22590,8,507,2017-05-20 23:15:13,http://bode.net/vida,,51.136.130.83,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n22591,8,507,2017-06-06 06:37:33,http://buckridge.info/dejah.simonis,,104.135.94.253,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n22592,8,507,2017-04-22 11:46:00,http://millchaden.com/ludwig_metz,,211.240.105.187,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n22593,8,507,2017-01-14 22:25:28,http://mitchellbreitenberg.biz/faustino.corkery,,153.13.8.186,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n22594,8,507,2017-04-11 06:05:16,http://bednar.biz/bettye,,200.244.225.34,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n22595,8,507,2017-06-10 07:30:40,http://predovicdooley.info/lura_carroll,,45.143.76.179,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n22596,8,507,2017-02-21 18:06:38,http://swiftsatterfield.org/ruthe_zulauf,,33.136.132.45,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n22597,8,507,2017-04-26 22:31:34,http://welch.co/cathryn_schiller,,242.72.241.178,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n22598,8,507,2017-02-21 04:20:47,http://spinkasmitham.com/dante,,165.67.161.193,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n22599,8,507,2017-03-04 14:58:40,http://reinger.info/quinten,,59.230.31.31,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n22600,8,507,2017-04-25 02:48:30,http://beier.net/lyric_borer,,189.27.96.109,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n22601,8,507,2017-03-01 18:54:25,http://mcglynnblanda.net/bridgette.aufderhar,,126.225.16.217,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n22602,8,507,2017-04-08 18:17:44,http://mann.info/chester.hettinger,,161.102.121.155,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n22603,8,507,2017-04-02 06:13:51,http://krishansen.info/lavinia,,27.199.35.11,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n22604,8,507,2017-04-21 06:29:11,http://osinski.org/angela,,31.125.175.198,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n22605,8,507,2017-02-10 09:35:38,http://zulauf.io/liam,,42.84.231.207,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n22606,8,507,2017-01-20 02:20:46,http://lynch.org/elda_weimann,,44.69.118.160,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n1525,1,35,2017-01-14 19:31:35,http://lakin.name/ardith_eichmann,,38.6.122.139,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n1526,1,35,2017-02-22 23:08:43,http://ledner.co/sasha,,25.175.35.192,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n1527,1,35,2017-04-12 15:48:58,http://schmitt.name/keira,,231.165.172.14,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n1528,1,35,2016-12-25 21:00:58,http://cain.co/arthur_brekke,,80.82.62.121,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n1529,1,35,2017-01-24 17:44:44,http://huels.co/noemi.durgan,,172.45.230.123,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n1530,1,35,2017-01-25 09:33:09,http://brown.name/ed,,244.60.158.144,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n1531,1,35,2017-04-21 09:47:48,http://feeney.org/malinda_borer,,169.10.91.161,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n1532,1,35,2017-03-08 11:57:52,http://wisozk.io/ottis_stroman,,31.207.75.56,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n1533,1,35,2017-01-24 12:07:16,http://mcdermott.com/adolf,,13.227.143.104,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n1534,1,35,2017-03-08 16:28:58,http://hoppe.info/marlee,,32.168.107.16,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n1535,1,35,2017-02-13 06:07:44,http://feeney.com/delfina,,105.139.238.92,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n1536,1,35,2016-12-20 10:17:38,http://balistreri.io/rodrigo_kunze,,215.166.57.77,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n1537,1,35,2017-04-24 03:06:40,http://vonhyatt.name/adelle,,164.10.254.12,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n1538,1,35,2017-05-28 23:41:11,http://rippin.co/elliot,,106.149.104.121,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n1539,1,35,2017-03-01 14:53:26,http://oreilly.info/hailey.emmerich,,196.60.106.150,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n1540,1,35,2017-01-25 17:23:30,http://morar.io/rosella.waters,,134.98.158.210,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n1541,1,35,2017-05-20 03:41:55,http://breitenberggulgowski.org/wade.keeling,,226.181.92.151,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n1542,1,35,2017-01-31 14:44:08,http://botsford.net/anna,,88.222.229.250,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n1543,1,35,2017-03-04 02:46:22,http://oreillyprosacco.org/tristin,,173.140.171.156,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n1544,1,35,2017-05-30 05:16:38,http://toy.info/nicolas,,65.82.110.40,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n1545,1,35,2017-03-25 12:21:43,http://mcclure.info/jaylon_gerlach,,156.208.33.73,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n1546,1,35,2017-05-25 02:02:54,http://reichertturcotte.org/isabell_ziemann,,9.146.140.5,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n1547,1,35,2017-01-06 07:51:28,http://gibson.com/camille,,132.208.163.89,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n1548,1,35,2017-06-06 15:50:16,http://davis.io/tara.lemke,,23.23.163.7,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n1549,1,35,2016-12-31 16:32:07,http://zboncak.name/jermey,,54.138.214.199,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n1550,1,35,2017-01-21 07:26:27,http://oconnellboehm.io/roger_nienow,,159.178.234.152,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n1551,1,35,2017-02-24 17:42:48,http://sipes.name/kaitlin,,167.7.78.91,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n1552,1,35,2016-12-22 22:23:46,http://crooks.com/keely.larkin,,75.64.153.33,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n1553,1,35,2017-02-23 19:30:22,http://nicolas.co/kayley,,179.85.2.123,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n1554,1,35,2016-12-29 08:59:41,http://bergstrom.info/gaston.denesik,,246.198.249.104,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n1555,1,35,2017-06-06 13:14:22,http://huelswitting.co/thalia,,2.77.57.153,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n1556,1,35,2017-01-04 22:21:21,http://mraz.info/haie.okuneva,,12.184.41.134,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n1557,1,35,2017-03-12 01:56:42,http://casper.name/lane,,84.78.118.133,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n1558,1,35,2017-06-13 10:00:17,http://metzcartwright.info/carter,,144.184.167.41,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n1559,1,35,2016-12-17 13:42:05,http://spinka.name/olga,,170.104.13.85,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n1560,1,35,2017-04-13 06:25:17,http://stoltenbergmoen.co/mallory_christiansen,,109.64.114.155,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n1561,1,35,2017-04-19 13:49:43,http://bechtelarschmidt.info/aditya.ruel,,131.166.218.154,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n1562,1,35,2017-01-08 15:11:11,http://schroedermraz.com/amelie,,153.181.135.183,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n1563,1,35,2017-02-28 09:06:05,http://wiza.biz/tommie_kunde,,185.46.170.156,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n1564,1,35,2017-04-21 10:00:15,http://ruecker.name/katarina.terry,,115.12.245.186,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n1565,1,35,2017-01-06 03:54:31,http://grimes.biz/kenny,,12.181.173.112,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n1566,1,36,2017-05-22 18:47:15,http://glovergaylord.biz/madaline,,98.122.152.188,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n1567,1,36,2017-02-18 21:09:35,http://runte.info/bennett,,89.12.21.48,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n1568,1,36,2017-04-14 05:48:51,http://osinski.biz/jeanne_wiza,,35.128.175.41,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n1569,1,36,2017-05-29 19:39:44,http://medhurstharvey.name/may_cormier,,167.101.31.248,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n1570,1,36,2017-01-29 16:52:38,http://mertz.com/birdie_glover,,249.50.93.179,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n1571,1,36,2017-01-29 13:27:41,http://hickleratke.io/minerva_murazik,,180.191.169.118,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n1572,1,36,2017-05-21 11:46:31,http://mckenzie.biz/rylee_kihn,,248.203.213.119,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n1573,1,36,2017-01-21 06:27:06,http://huelscorwin.info/marc.labadie,,202.111.180.93,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n1574,1,36,2017-02-08 00:56:36,http://herzog.info/gerry.schuppe,,30.233.56.144,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n1575,1,36,2017-04-30 19:51:59,http://markschamplin.info/cielo.hickle,,141.94.47.121,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n1576,1,36,2017-01-16 17:49:07,http://kaulke.co/aurelia,,63.192.126.114,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n1577,1,36,2017-02-25 10:36:12,http://faheyjohns.com/wiley,,240.155.78.191,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n1578,1,36,2017-02-01 15:13:27,http://dare.com/therese,,79.149.238.56,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n1579,1,36,2017-05-07 19:50:52,http://huels.co/ceasar,,39.234.233.164,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n1580,1,36,2017-06-03 11:28:49,http://hayes.net/ayla.gutmann,,158.72.146.86,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n22607,8,507,2017-05-26 03:48:14,http://farrell.info/ariel.weber,,172.6.231.34,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n22608,8,507,2017-01-18 23:04:13,http://feest.com/telly,,9.104.152.83,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n22609,8,507,2017-03-10 05:09:17,http://hamill.name/brandi_weimann,,13.8.58.130,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n22610,8,507,2017-04-12 03:37:34,http://swaniawski.name/haylee_brekke,,209.75.62.185,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n22611,8,507,2017-01-10 21:33:00,http://jastmohr.co/anastasia_bailey,,231.228.122.123,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n22612,8,507,2016-12-15 20:00:15,http://wiegandwaters.name/jermey_rogahn,,129.234.67.168,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n22613,8,507,2017-04-11 00:04:57,http://frami.io/alaina.flatley,,115.245.183.81,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n22614,8,507,2017-01-28 10:31:32,http://wuckertwill.net/buster.barrows,,239.209.219.246,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n22615,8,507,2017-04-18 22:08:57,http://gislason.biz/peggie_wolf,,243.55.88.86,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n22616,8,507,2017-04-15 08:59:53,http://hilll.org/ulices_morar,,37.105.206.134,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n22617,8,508,2017-03-15 13:52:38,http://harber.co/zelma.orn,,52.157.180.241,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n22618,8,508,2017-03-03 17:12:45,http://blick.name/clinton,,59.149.243.74,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n22619,8,508,2017-06-06 04:35:01,http://deckowbarrows.org/abigayle,,2.199.42.207,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n22620,8,508,2017-02-08 12:25:39,http://flatleyquitzon.name/colten,,110.248.157.63,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n22621,8,508,2017-04-22 06:20:33,http://altenwerthjerde.name/jan,,174.180.129.200,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n22622,8,508,2017-03-21 05:03:05,http://strosintreutel.info/dejon.keebler,,68.210.118.62,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n22623,8,508,2017-01-19 16:59:33,http://ankundingbalistreri.co/frankie_hegmann,,201.54.95.234,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n22624,8,508,2016-12-21 14:08:03,http://connellyhuel.com/jovany,,38.230.11.103,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n22625,8,508,2017-06-10 14:19:11,http://hyattdurgan.info/celestino.zboncak,,74.4.130.34,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n22626,8,508,2017-01-12 20:35:41,http://wuckert.com/abagail,,81.153.98.138,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n22627,8,508,2017-01-18 20:16:31,http://kolangosh.co/twila,,7.52.100.23,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n22628,8,508,2017-02-20 13:11:56,http://hodkiewicz.info/florencio,,231.214.41.92,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n22629,8,508,2017-03-07 03:00:08,http://pagac.io/johann.kovacek,,115.117.124.54,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n22630,8,508,2017-02-08 18:28:48,http://borer.co/harmony_jakubowski,,180.72.132.35,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n22631,8,508,2017-03-22 14:22:17,http://mckenzie.biz/laurianne_kuvalis,,40.49.34.225,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n22632,8,508,2017-05-02 13:22:46,http://rutherford.org/jakob,,153.61.220.103,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n22633,8,508,2017-05-08 02:04:23,http://douglas.info/kaycee.cremin,,230.4.51.36,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n22634,8,508,2017-04-22 00:31:51,http://sporer.com/arnold,,213.56.182.198,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n22635,8,508,2016-12-14 01:04:54,http://kirlinschimmel.io/lew.pouros,,48.110.238.104,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n22636,8,508,2016-12-19 15:52:47,http://corkery.io/coralie.collins,,74.144.107.2,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n22637,8,508,2017-04-10 08:42:46,http://barton.co/marguerite,,217.42.181.225,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n22638,8,508,2017-01-25 15:28:56,http://grahamwehner.info/genoveva_pfannerstill,,130.224.119.145,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n22639,8,508,2016-12-23 21:52:21,http://cartwright.com/deangelo.crona,,21.85.166.245,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n22640,8,508,2017-05-17 12:41:43,http://jacobsreichel.io/lurline.armstrong,,169.154.199.148,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n22641,8,508,2016-12-20 12:15:51,http://douglasoconnell.com/chaim,,215.244.88.108,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n22642,8,508,2017-03-23 05:58:23,http://jacobsonrosenbaum.biz/wilford_dietrich,,78.57.246.246,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n22643,8,508,2017-05-28 07:55:42,http://bradtke.net/guadalupe_welch,,143.23.155.73,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n22644,8,508,2016-12-24 09:49:38,http://greendickens.info/kennedi_wyman,,207.31.96.195,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n22645,8,508,2017-02-15 12:55:30,http://bins.org/linda.ritchie,,142.100.198.220,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n22646,8,508,2017-05-02 22:14:22,http://douglas.co/dante,,84.186.131.235,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n22647,8,508,2017-01-19 03:43:53,http://wolffroob.biz/aniya_kozey,,162.51.121.180,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n22648,8,508,2017-01-20 23:23:30,http://beer.com/marcelle.zieme,,113.210.246.44,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n22649,8,508,2017-05-31 03:18:18,http://keeblerbogisich.net/trenton,,94.92.179.195,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n22650,8,508,2017-05-02 22:03:15,http://bayerzulauf.org/filomena_lubowitz,,180.161.190.22,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n22651,8,508,2017-06-08 07:50:43,http://oberbrunner.co/giles.casper,,236.18.107.12,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n22652,8,508,2016-12-21 01:49:42,http://franeckijakubowski.biz/elsa_klocko,,109.68.236.159,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n22653,8,508,2017-01-06 05:58:31,http://nolan.com/davon,,101.168.55.116,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n22654,8,508,2016-12-24 15:39:12,http://mertzdavis.name/lance_romaguera,,48.33.116.180,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n22655,8,508,2017-03-11 16:11:53,http://abbott.org/cecil_hamill,,55.58.193.115,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n22656,8,508,2017-03-04 00:42:05,http://reillynolan.org/leanna,,203.126.4.192,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n22657,8,508,2017-05-01 17:34:57,http://toy.org/jaleel.kerluke,,138.166.68.53,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n22658,8,508,2017-03-16 08:34:29,http://terry.name/hubert_maggio,,117.122.24.92,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n22659,8,508,2017-02-26 06:40:41,http://bernierbeer.com/guie.braun,,39.86.47.114,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n22660,8,508,2017-04-30 19:21:45,http://sanford.co/melya_hoppe,,219.84.236.155,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n22661,8,508,2017-03-31 16:35:20,http://beer.org/neal,,88.195.196.251,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n1581,1,36,2017-04-30 08:47:22,http://hodkiewicz.org/maximillia_schuster,,85.121.215.213,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n1582,1,36,2017-04-13 05:36:59,http://bartellchristiansen.net/maverick.fay,,64.152.24.50,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n1583,1,36,2017-04-01 03:54:48,http://franeckigrady.biz/delilah,,58.45.21.207,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n1584,1,36,2017-01-12 05:36:13,http://batz.co/sonya,,114.194.148.69,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n1585,1,36,2017-05-23 07:03:03,http://huel.co/fabian,,220.115.126.67,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n1586,1,36,2017-02-20 12:40:25,http://huels.com/demond_schowalter,,240.34.139.215,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n1587,1,36,2017-05-30 08:13:43,http://predovic.org/dexter.ziemann,,237.223.135.138,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n1588,1,36,2017-02-21 09:31:48,http://ledner.biz/fritz.beahan,,249.132.86.10,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n1589,1,36,2017-03-15 20:13:53,http://cummerata.info/noemie_ledner,,114.221.34.202,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n1590,1,36,2017-05-27 19:21:42,http://dach.name/margot,,39.240.94.9,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n1591,1,36,2017-05-19 14:07:37,http://ondricka.com/shanel.lebsack,,121.137.251.47,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n1592,1,36,2017-01-17 15:11:17,http://sipescremin.biz/stuart,,173.216.168.73,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n1593,1,36,2017-04-05 12:23:29,http://grantbauch.name/madilyn,,226.178.229.219,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n1594,1,36,2017-03-31 10:28:31,http://mckenzie.net/mable_crist,,130.72.99.26,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n1595,1,36,2017-03-06 18:36:25,http://schmidt.com/margie_welch,,29.36.15.186,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n1596,1,36,2017-05-20 19:12:36,http://walkerlegros.name/ernesto,,5.219.171.61,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n1597,1,36,2017-06-04 04:37:27,http://paucek.io/floy,,140.168.68.241,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n1598,1,36,2017-01-13 06:17:35,http://bartolettiledner.biz/dayna.bailey,,249.191.28.23,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n1599,1,36,2016-12-15 08:43:40,http://terry.info/alvina_gulgowski,,17.55.250.249,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n1600,1,36,2017-01-01 05:29:55,http://hermiston.net/rosetta_hermiston,,240.182.153.72,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n1601,1,36,2017-01-15 14:27:16,http://bartell.biz/jamal.quigley,,60.50.110.155,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n1602,1,36,2017-03-14 07:07:12,http://crist.info/amalia.frami,,178.33.130.165,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n1603,1,36,2017-06-13 19:04:47,http://boehm.net/horace,,237.251.225.144,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1604,1,36,2017-04-17 13:19:51,http://stehr.name/oleta_pagac,,232.211.218.181,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n1605,1,36,2017-03-28 22:58:05,http://pollich.org/cleo_emard,,194.55.8.74,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n1606,1,36,2017-04-13 17:24:14,http://mcglynn.com/cecelia.jast,,249.115.7.173,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n1607,1,36,2017-01-18 21:02:32,http://ernser.io/blanche,,26.57.15.169,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n1608,1,36,2017-01-13 18:46:55,http://kuhnharris.io/alayna,,156.132.49.194,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n1609,1,36,2017-04-24 02:13:47,http://fritsch.org/jayne.schulist,,159.20.150.203,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n1610,1,36,2017-02-14 06:07:48,http://kling.biz/tyree_white,,153.215.149.248,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n1611,1,36,2017-03-08 15:27:10,http://nitzscheharber.name/keara.feil,,115.37.239.81,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n1612,1,36,2017-01-31 14:41:33,http://considine.info/nikolas_cruickshank,,193.170.26.184,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n1613,1,36,2017-04-20 02:14:38,http://rowemante.co/tony_pacocha,,25.251.205.155,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n1614,1,36,2017-06-01 22:17:18,http://langosh.org/miouri_ko,,234.199.239.10,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n1615,1,36,2017-04-18 09:35:50,http://haleyferry.biz/kian,,227.204.36.7,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n1616,1,36,2017-06-02 08:30:07,http://rempel.io/abigale_deckow,,160.194.58.164,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n1617,1,36,2017-02-28 07:06:44,http://terry.info/lillie,,167.56.225.32,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n1618,1,37,2017-03-05 00:32:55,http://swaniawski.biz/gilbert,,26.212.89.25,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n1619,1,37,2017-01-01 01:54:12,http://keler.net/camryn_hermann,,234.171.151.219,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n1620,1,37,2017-01-22 17:23:04,http://okuneva.io/agustin_williamson,,216.141.12.173,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n1621,1,37,2017-04-25 01:56:56,http://murazik.io/fanny.kunze,,10.211.29.57,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n1622,1,37,2017-06-06 17:36:01,http://pollich.co/jan_kohler,,188.167.254.19,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n1623,1,37,2017-04-30 00:22:43,http://keeling.biz/ila,,232.99.92.5,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n1624,1,37,2017-03-04 12:57:34,http://boyleschroeder.biz/diamond.deckow,,56.49.231.110,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n1625,1,37,2017-05-28 09:36:50,http://pagac.biz/lelah_bruen,,41.17.250.50,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n1626,1,37,2017-05-02 00:25:21,http://kerluke.net/heidi,,74.202.143.227,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n1627,1,37,2017-04-24 09:26:48,http://herzogwelch.name/guy_parisian,,201.166.75.157,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n1628,1,37,2017-01-07 11:37:30,http://stanton.net/bo_smitham,,252.15.14.127,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n1629,1,37,2017-05-15 15:17:08,http://kunze.net/bud,,21.174.145.178,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n1630,1,37,2017-01-12 07:50:02,http://shanahan.co/ettie_gleason,,186.245.134.61,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n1631,1,37,2017-03-05 11:46:56,http://baumbachbogisich.io/olga,,33.12.117.48,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n1632,1,37,2017-02-09 11:39:41,http://faheyharber.com/delbert_schulist,,234.166.230.252,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n1633,1,37,2017-03-09 16:16:42,http://kub.org/gielle,,206.68.69.216,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n1634,1,37,2017-02-09 19:22:28,http://willmsrau.info/alphonso.mraz,,220.118.95.7,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n1635,1,37,2017-04-08 21:15:45,http://kertzmannsatterfield.name/ashtyn,,82.167.23.184,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n1636,1,37,2017-05-31 00:40:53,http://okonernser.co/kaandra_rutherford,,167.40.30.133,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n22662,8,508,2017-04-09 22:16:48,http://maggio.biz/darron,,82.61.130.9,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n22663,8,508,2017-05-12 20:44:56,http://watsicaleannon.io/gianni,,192.156.227.238,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n22664,8,508,2017-04-27 10:31:01,http://jaskolski.io/deontae.jones,,58.195.206.119,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n22665,8,508,2017-05-18 11:52:40,http://veum.co/je_mohr,,152.37.71.77,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n22666,8,509,2017-03-26 11:50:50,http://brekke.net/elian,,156.171.87.54,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n22667,8,509,2017-03-14 00:16:52,http://gaylordshields.info/marquise.wehner,,119.164.118.105,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n22668,8,509,2017-03-20 18:26:01,http://howe.info/aurelie.quigley,,206.62.188.110,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n22669,8,509,2017-03-12 08:25:55,http://rowe.name/stevie.hackett,,206.238.79.143,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n22670,8,509,2017-03-16 20:34:48,http://dickens.co/kattie,,210.186.197.143,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n22671,8,509,2017-01-29 05:13:24,http://daugherty.io/lauryn.moore,,24.155.195.118,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n22672,8,509,2017-05-23 20:38:37,http://gulgowskihegmann.io/antonetta,,192.15.26.2,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n22673,8,509,2017-05-02 05:54:33,http://stokes.info/karianne,,33.50.22.113,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n22674,8,509,2017-03-02 09:22:42,http://wolff.io/boris.hermiston,,121.230.162.93,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n22675,8,509,2017-02-22 04:43:18,http://wildermanhahn.biz/josephine,,125.185.117.169,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n23018,8,516,2016-12-15 15:43:06,http://stracke.name/noe,,153.150.32.32,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n22676,8,509,2017-02-14 14:20:56,http://reynoldsgleichner.io/kip_klein,,120.84.146.170,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n22677,8,509,2017-05-21 13:14:01,http://white.org/keira,,163.65.70.68,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n22678,8,509,2017-03-27 10:06:42,http://quigleykeeling.net/cielo.mitchell,,99.232.219.18,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n22679,8,509,2017-05-09 02:32:36,http://carterstrosin.net/anne,,227.132.51.224,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n22680,8,509,2017-03-03 14:58:52,http://beatty.co/brennan,,27.120.204.102,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n22681,8,509,2016-12-26 05:30:08,http://schimmel.info/clyde,,241.154.83.152,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n22682,8,509,2017-05-31 02:04:50,http://heaneycruickshank.name/hilario,,105.39.150.128,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n22683,8,509,2017-04-25 00:55:37,http://bednar.co/leilani_durgan,,170.29.133.76,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n22684,8,509,2017-02-08 10:15:58,http://wisozkjerde.co/kieran_turcotte,,236.239.104.216,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n22685,8,509,2017-03-30 21:33:41,http://von.name/laurence,,88.40.187.153,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n22686,8,509,2017-02-09 06:16:10,http://wintheiser.biz/maryse.leannon,,254.144.210.100,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n22687,8,509,2017-01-08 08:56:02,http://hamill.com/rachelle,,231.204.88.249,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n22688,8,509,2016-12-24 22:48:47,http://muller.io/demond.weimann,,159.120.119.97,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n22689,8,509,2017-01-13 04:34:38,http://gutkowski.info/julianne_mertz,,82.131.224.192,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n22690,8,509,2016-12-26 05:55:27,http://nicolas.co/ryan_stracke,,157.145.196.154,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n22691,8,509,2017-05-22 22:17:50,http://hansen.com/emely,,155.158.130.126,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n22692,8,509,2017-04-27 01:48:44,http://bernhard.co/westley,,50.11.235.171,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n22693,8,509,2017-04-22 02:46:52,http://douglas.org/mateo_murphy,,54.254.68.220,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n22694,8,509,2017-06-08 22:12:38,http://bayer.net/gregory,,185.78.162.198,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n22695,8,509,2017-03-04 12:47:28,http://koelpin.info/elisha,,201.40.208.243,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n22696,8,509,2017-02-28 19:33:35,http://swaniawskistamm.com/corrine.kuphal,,170.144.120.147,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n22697,8,509,2016-12-18 02:54:33,http://walker.co/blanca_boehm,,64.18.196.54,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n22698,8,509,2017-02-09 11:23:30,http://toyfisher.org/ruthie,,157.178.32.92,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n22699,8,509,2017-05-08 00:42:28,http://walsh.io/jordon_conn,,18.145.175.193,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n22700,8,509,2017-04-11 00:35:32,http://koelpin.org/dorothea,,182.145.4.235,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n22701,8,509,2017-05-18 06:27:58,http://goyettecollins.co/dave,,160.130.107.6,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n22702,8,509,2017-06-01 10:53:10,http://sporer.co/elise,,166.64.42.69,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n22703,8,509,2016-12-17 07:59:48,http://windlermaggio.biz/kiarra_daugherty,,105.231.135.187,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n22704,8,509,2017-02-19 09:22:46,http://toy.name/zella,,104.179.64.163,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n22705,8,509,2017-01-24 11:12:08,http://ondricka.org/noble_dubuque,,54.187.239.84,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n22706,8,509,2017-04-16 03:36:20,http://terry.info/levi,,104.125.199.231,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n22707,8,509,2017-02-08 08:11:45,http://anderson.info/tristin.hamill,,59.254.238.82,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n22708,8,509,2017-06-09 02:05:55,http://greenholt.com/nia,,128.54.32.152,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n22709,8,509,2017-01-14 22:19:21,http://simonis.com/jodie,,223.224.42.170,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n22710,8,509,2017-03-06 04:51:20,http://runolfsdottir.info/jakob,,166.160.24.180,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n22711,8,509,2016-12-13 15:55:23,http://quitzonswaniawski.name/quinton_murray,,224.9.209.183,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n22712,8,509,2017-05-29 06:50:34,http://nicolashagenes.com/fidel_yundt,,93.18.230.133,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n22713,8,509,2017-02-26 17:40:12,http://buckridgefeest.name/julius,,177.164.210.234,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n22714,8,509,2017-03-27 17:10:50,http://yostschmeler.info/keenan.kris,,42.102.188.97,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n22715,8,509,2017-02-05 01:22:04,http://carroll.info/corrine,,130.18.240.112,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n1637,1,37,2017-01-23 15:36:56,http://blick.name/aiden_kuhlman,,252.211.46.206,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n1638,1,37,2016-12-29 18:38:32,http://ruecker.io/rolando,,128.124.105.187,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n1639,1,37,2017-02-23 11:34:46,http://feeney.biz/jamar,,120.216.249.120,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n1640,1,37,2017-05-30 06:20:26,http://heaney.co/maxwell,,220.254.79.181,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n1641,1,37,2017-02-02 03:03:12,http://ornosinski.net/je,,36.62.120.142,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n1642,1,37,2017-04-02 18:29:27,http://schmitt.io/sandra,,39.139.172.248,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n1643,1,37,2017-03-12 21:51:45,http://okeefe.io/wilfredo.emmerich,,155.154.130.200,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n1644,1,37,2017-03-15 07:03:12,http://weimann.co/orville_kertzmann,,6.213.52.55,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n1645,1,37,2017-02-21 18:08:52,http://rice.io/mandy_erdman,,151.127.128.203,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n1646,1,37,2017-04-14 14:45:35,http://quitzon.info/je,,238.138.229.86,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n1647,1,37,2017-05-21 18:11:34,http://bednar.net/electa,,160.120.96.162,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n1648,1,37,2017-02-12 00:43:12,http://ko.name/amy,,61.202.4.235,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n1649,1,37,2017-05-24 20:28:07,http://moore.io/camren,,138.155.133.229,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n1650,1,37,2017-04-24 12:37:22,http://spinka.name/jose,,234.52.151.227,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n1651,1,37,2017-04-06 12:52:39,http://gottlieb.org/vincenzo,,152.26.241.60,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n1652,1,37,2017-06-12 05:44:46,http://sporerbrekke.name/gust.hansen,,116.117.160.141,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n1653,1,37,2017-03-03 14:00:15,http://gorczanyhansen.io/carmel,,95.242.94.65,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n1654,1,37,2016-12-18 20:40:54,http://faheywalsh.net/alva,,246.123.199.185,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n1655,1,37,2017-04-11 22:01:39,http://powlowskiohara.org/kari.nader,,215.124.163.195,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n1656,1,37,2017-04-09 21:40:35,http://bauch.net/adonis_hermiston,,186.109.222.26,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n1657,1,37,2017-03-22 16:59:59,http://connstroman.info/magnolia,,233.2.156.103,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n1658,1,37,2017-04-03 09:26:28,http://quitzonlebsack.name/joy,,195.237.251.231,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n1659,1,37,2016-12-30 12:00:31,http://turnernader.com/tevin_ullrich,,101.126.139.178,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n1660,1,37,2016-12-23 18:03:57,http://hudsonlegros.info/raleigh,,3.159.228.202,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n1661,1,37,2017-01-21 12:06:56,http://kunze.info/cody_hudson,,90.226.190.118,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n1662,1,37,2017-03-22 01:10:41,http://kozey.name/astrid.wisoky,,208.147.117.99,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n1663,1,37,2016-12-20 09:09:39,http://cole.co/noemi,,182.222.145.182,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n1664,1,37,2016-12-29 15:50:37,http://cummings.org/martine,,182.224.132.125,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n1665,1,37,2017-02-10 04:38:36,http://schmitt.info/geoffrey,,111.9.12.233,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n1666,1,37,2017-04-03 17:27:20,http://moriettejacobs.name/joanny.jones,,226.168.192.144,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n1667,1,37,2017-03-01 04:23:12,http://crookslangosh.info/eduardo_douglas,,168.142.80.16,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n1668,1,37,2017-01-02 05:44:20,http://ziemann.org/houston,,148.174.237.154,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n1669,1,37,2017-01-08 15:53:40,http://vandervort.co/talia_smith,,31.123.33.178,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1670,1,37,2017-04-04 16:13:03,http://kunde.net/adriana_batz,,114.89.163.241,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n1671,1,37,2017-05-13 08:13:32,http://lynch.com/mozelle.rowe,,44.170.192.101,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n1672,1,37,2017-06-14 02:30:53,http://lehnerkautzer.info/isabel,,23.242.41.92,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n1673,1,37,2017-02-20 22:31:50,http://mayerleuschke.co/shane.damore,,11.207.89.170,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n1674,1,37,2017-03-04 05:03:49,http://lynchheidenreich.co/keely.dare,,176.44.174.166,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n1675,1,37,2017-05-30 09:06:50,http://goldner.info/zechariah.abernathy,,174.17.69.226,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n1676,1,37,2017-04-14 20:08:58,http://wolfadams.info/chester_connelly,,34.189.155.19,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n2194,1,49,2017-02-15 12:20:31,http://oconnell.io/glen,,54.179.196.206,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n1677,1,37,2016-12-17 05:08:44,http://pfefferhowe.name/zula.lockman,,196.91.105.198,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n1678,1,37,2017-01-22 10:24:28,http://eichmann.org/birdie.hilpert,,79.70.16.122,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n1679,1,38,2017-05-06 09:46:44,http://conroy.com/alexis_haag,,15.115.69.239,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n1680,1,38,2016-12-13 06:41:28,http://jones.info/sophia.bradtke,,177.19.141.108,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n1681,1,38,2017-04-23 01:39:20,http://schuppeheel.biz/lambert_feest,,166.96.234.171,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n1682,1,38,2017-04-18 18:57:30,http://bradtke.io/vesta,,106.238.29.62,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n1683,1,38,2017-03-09 14:19:14,http://vonrueden.co/eryn,,171.219.151.17,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n1684,1,38,2017-02-05 22:27:46,http://hoeger.io/gabe,,63.240.153.165,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n1685,1,38,2017-04-27 18:07:31,http://schroeder.io/ayden.barton,,202.214.134.60,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n1686,1,38,2017-05-01 10:45:53,http://spencer.net/elmira.king,,34.18.85.99,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n1687,1,38,2017-04-24 16:37:55,http://sipeskertzmann.info/savanna,,12.80.104.197,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n1688,1,38,2017-01-16 03:45:27,http://mohr.info/lawrence,,152.28.230.227,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n1689,1,38,2017-02-10 00:14:06,http://ward.info/jaydon,,30.180.206.96,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n1690,1,38,2017-05-25 03:28:21,http://westdibbert.biz/elinore,,252.220.104.2,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n1691,1,38,2016-12-26 21:02:17,http://bartell.info/marianna,,138.70.254.156,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n1692,1,38,2017-02-13 10:33:39,http://okeefe.io/evan_ebert,,113.161.151.223,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n22716,8,509,2017-03-02 07:21:22,http://keebler.info/karson,,200.42.157.40,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n22717,8,509,2017-03-10 17:19:20,http://olson.net/rogers,,201.38.143.97,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n22718,8,509,2017-02-03 02:55:27,http://shanahankaulke.name/kianna.walter,,127.79.28.32,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n22719,8,509,2017-05-10 14:40:07,http://binsnienow.info/shea,,146.170.196.197,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n22720,8,509,2016-12-31 17:15:43,http://reilly.org/jeromy,,193.35.92.138,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n22721,8,510,2017-01-17 09:52:08,http://feil.name/coy_schaden,,143.228.38.33,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n22722,8,510,2017-01-24 17:19:14,http://kovacek.name/loyce_mcclure,,195.172.227.194,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n22723,8,510,2017-04-20 03:51:29,http://breitenberg.net/viviane,,193.242.39.249,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n22724,8,510,2017-05-18 07:01:08,http://macgyver.biz/angeline,,70.65.132.21,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n22725,8,510,2017-05-17 13:15:29,http://mayertromaguera.co/jonathon_leffler,,110.183.187.53,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n22726,8,510,2017-02-07 02:57:15,http://dickenslarson.info/reina,,99.204.235.81,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n22727,8,510,2017-03-02 22:25:32,http://torphybartoletti.io/merlin,,209.34.215.245,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n22728,8,510,2017-05-28 21:35:41,http://bartoletti.net/darrion.rice,,110.129.16.120,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n22729,8,510,2017-06-05 19:23:51,http://murazikmckenzie.co/tiara,,69.79.79.117,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n22730,8,510,2017-01-06 08:38:37,http://kulasmcglynn.org/ethan,,121.157.60.9,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n22731,8,510,2017-01-12 02:02:14,http://doyle.io/kenton_heel,,9.153.186.160,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n22732,8,510,2017-01-22 14:39:35,http://larkinhammes.com/desmond,,3.72.175.29,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n22733,8,510,2017-05-24 03:06:47,http://sauer.com/schuyler.hartmann,,132.86.6.42,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n22734,8,510,2016-12-24 21:43:43,http://reichert.org/ashlee.fadel,,202.126.2.97,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n22735,8,510,2017-03-02 20:18:58,http://dickens.org/aliya,,128.80.57.154,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n22736,8,510,2017-03-30 10:27:47,http://smitham.net/jalyn_green,,9.65.73.234,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n22737,8,510,2017-05-06 22:41:47,http://sporerreynolds.io/chaim,,66.129.121.40,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n22738,8,510,2017-04-03 08:53:04,http://daniel.io/mackenzie.lakin,,32.92.65.58,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n22739,8,510,2017-02-26 00:41:02,http://mrazemmerich.net/darian_hills,,26.132.144.225,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n22740,8,510,2017-01-11 07:53:10,http://nitzsche.biz/isobel.emmerich,,239.119.228.198,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n22741,8,510,2017-02-10 12:56:04,http://lueilwitz.name/vernie.torphy,,169.126.195.34,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n22742,8,510,2016-12-23 08:56:26,http://kovacek.biz/brielle_mcdermott,,188.71.204.42,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n22743,8,510,2017-01-01 10:38:56,http://moen.co/claudia,,229.250.73.115,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n22744,8,510,2017-01-09 00:29:28,http://schimmel.co/aliza_donnelly,,165.172.91.34,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n22745,8,510,2017-03-13 20:50:39,http://stoltenberg.co/stan,,19.138.242.34,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n22746,8,510,2017-01-14 08:22:46,http://eichmann.biz/audra,,166.245.55.127,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n22747,8,510,2017-05-19 08:56:38,http://von.co/guy_schaefer,,77.46.158.126,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n22748,8,510,2017-05-21 20:51:05,http://glover.biz/layne.kihn,,254.150.95.100,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n22749,8,510,2016-12-27 16:34:09,http://dare.com/kaia,,43.230.185.98,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n22750,8,510,2017-03-28 05:23:00,http://ferry.io/abdullah,,86.65.30.194,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n22751,8,510,2017-01-22 03:29:16,http://gerlachwhite.com/samantha.mcclure,,47.239.211.15,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n22752,8,510,2017-01-27 07:13:15,http://greenfelder.info/august,,215.239.198.204,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n22753,8,510,2017-05-11 21:32:36,http://hahngrant.info/margarette,,4.192.136.23,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n22754,8,510,2017-03-26 06:02:18,http://mcclurenicolas.biz/gerard.greenfelder,,40.211.215.132,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n22755,8,510,2017-06-01 17:29:32,http://daughertydare.org/reinhold.ondricka,,131.168.62.32,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n22756,8,510,2017-04-28 18:17:31,http://gerholdupton.net/mckenna,,244.179.223.109,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n22757,8,510,2016-12-24 05:43:19,http://price.name/nikita,,33.137.235.152,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n22758,8,510,2016-12-13 18:55:19,http://romaguera.info/barry,,27.134.243.121,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n22759,8,510,2017-01-17 11:57:36,http://mccullough.io/renee_luettgen,,11.11.124.190,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n22760,8,510,2016-12-19 08:35:03,http://jaskolskiquitzon.biz/brad,,202.180.129.4,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n22761,8,510,2017-01-03 20:20:12,http://grimes.name/izaiah,,247.101.98.148,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n22762,8,510,2017-01-25 18:55:39,http://koelpinhowell.name/jerome,,92.252.38.3,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n22763,8,510,2017-01-08 04:27:27,http://stanton.com/erna,,202.105.102.90,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n22764,8,510,2017-03-31 04:59:03,http://wolff.info/christopher,,157.103.117.149,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n22765,8,510,2017-05-09 15:00:35,http://kuhlman.net/neal,,132.202.118.38,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n22766,8,510,2017-06-14 01:58:32,http://stamm.org/fredy_prosacco,,89.183.251.251,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n22767,8,510,2017-02-19 18:23:30,http://bartolettiemmerich.co/berta,,112.41.228.119,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n22768,8,510,2017-02-24 21:39:38,http://davis.io/meta,,124.104.55.226,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n22769,8,510,2017-04-12 15:00:04,http://gottliebschmidt.io/zita_deckow,,156.154.42.98,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n22770,8,510,2016-12-28 04:29:35,http://runolfsdottir.io/katarina,,209.13.116.199,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n1693,1,38,2017-02-05 16:21:18,http://mclaughlin.com/joseph.mclaughlin,,250.149.219.44,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n1694,1,38,2017-02-22 20:07:58,http://mckenzieruel.name/hazle,,48.5.169.248,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n1695,1,38,2017-02-06 09:59:24,http://ziemann.org/alanna,,113.214.152.158,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n1696,1,38,2016-12-16 09:21:17,http://wintheiserkozey.biz/gustave.hintz,,187.65.209.4,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n1697,1,38,2017-01-27 15:56:04,http://halvorson.io/zella_pfannerstill,,74.151.69.61,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n1698,1,38,2017-05-16 02:10:07,http://krajcik.io/erick,,239.83.99.228,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n1699,1,38,2016-12-22 18:09:18,http://walter.org/myrtis,,45.226.216.61,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n1700,1,38,2017-05-16 19:50:04,http://ritchie.com/isabella.beer,,162.171.203.235,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n1701,1,38,2017-02-13 11:38:57,http://shields.biz/winfield,,86.133.191.205,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n1702,1,38,2017-05-15 18:04:42,http://johnson.org/eliane,,113.90.66.27,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n1703,1,38,2017-03-01 20:46:42,http://abshire.io/bernita.bergnaum,,107.165.58.51,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n1704,1,38,2017-05-07 21:35:59,http://pacocha.biz/rubie,,59.108.206.188,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n1705,1,38,2017-03-25 17:36:17,http://wolff.biz/foster.abernathy,,112.62.173.165,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n1706,1,38,2017-03-30 16:35:11,http://spencer.co/eden.gislason,,161.245.79.42,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n1707,1,38,2017-02-08 06:36:23,http://koch.com/chasity_fay,,244.50.225.37,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n1708,1,38,2017-06-01 03:15:52,http://baumbach.io/candace_towne,,57.87.102.78,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n1709,1,38,2017-01-23 17:13:39,http://yundtledner.biz/mason,,44.245.131.40,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n1710,1,38,2017-03-06 00:44:36,http://bogisich.co/kurt,,109.87.233.76,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n1711,1,38,2016-12-17 01:30:30,http://watsicalesch.org/alexandria,,134.152.3.2,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n1712,1,38,2017-02-21 18:03:08,http://sporer.biz/adelia.langworth,,167.167.150.5,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n1713,1,38,2017-02-07 09:19:43,http://beier.info/emerson,,252.87.50.11,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n1714,1,38,2017-06-13 23:16:23,http://auer.co/kiel_mcglynn,,19.20.34.76,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n1715,1,38,2017-05-18 10:42:58,http://cronanitzsche.name/dangelo_heidenreich,,5.132.209.196,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n1716,1,38,2017-03-11 19:39:07,http://weinatpfannerstill.biz/opal,,158.227.10.220,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n1717,1,38,2017-01-25 17:41:35,http://carter.biz/reggie,,65.206.165.53,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n1718,1,38,2017-02-16 02:58:13,http://green.net/crystel_durgan,,12.221.20.21,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n1719,1,38,2017-03-26 02:52:39,http://bergnaumhowe.biz/mathilde,,11.236.155.231,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n1720,1,38,2017-04-29 12:49:22,http://mills.info/daija,,61.85.194.30,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n1721,1,39,2017-02-20 21:09:49,http://crooks.net/roderick_hermann,,228.194.188.82,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n1722,1,39,2017-02-13 21:07:29,http://douglas.co/jarod,,247.205.97.160,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n1723,1,39,2017-03-06 23:46:09,http://botsfordgoldner.info/abner_luettgen,,113.10.129.189,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n1724,1,39,2017-04-12 01:13:29,http://rodriguezorn.net/violette_larkin,,68.174.129.211,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n1725,1,39,2017-05-01 23:51:32,http://wisoky.name/shaylee,,162.110.174.3,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n1726,1,39,2017-05-24 07:45:25,http://hauck.info/constance,,42.221.227.216,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n1727,1,39,2017-01-08 06:41:23,http://hahn.name/maxine,,117.138.246.76,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n1728,1,39,2017-04-25 01:17:23,http://larkinturcotte.io/donny.glover,,21.183.171.80,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n1729,1,39,2017-01-22 09:41:38,http://franecki.org/abbey.ratke,,60.150.72.124,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n1730,1,39,2017-04-12 16:22:05,http://haley.biz/kiarra,,181.52.126.101,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n1731,1,39,2016-12-26 12:30:29,http://bednardavis.co/christ,,47.253.43.240,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n1732,1,39,2016-12-16 17:37:01,http://abshire.co/brittany.pfeffer,,91.50.70.140,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n1733,1,39,2017-02-22 23:39:26,http://boscosteuber.name/rasheed_heidenreich,,34.2.67.252,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n1734,1,39,2017-02-09 09:25:45,http://schamberger.co/selina,,127.110.237.58,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n1735,1,39,2017-05-03 01:37:59,http://wilkinson.co/lia.blick,,174.150.29.86,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n1736,1,39,2017-01-02 17:34:47,http://spinka.org/geovanny,,39.208.79.68,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n1737,1,39,2017-01-13 10:49:04,http://dickinson.name/glennie,,174.66.71.52,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n1738,1,39,2017-03-27 10:37:53,http://oreilly.com/otilia.cruickshank,,36.57.44.59,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n1739,1,39,2017-04-13 01:26:42,http://rath.info/theresa,,47.225.120.84,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n1740,1,39,2017-01-01 15:20:35,http://kunze.info/chris,,218.60.19.33,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n1741,1,39,2017-05-07 22:19:18,http://hoppe.biz/electa.yost,,252.13.179.103,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n1742,1,39,2017-06-08 04:29:08,http://padberg.biz/trea.kertzmann,,147.200.23.57,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n1743,1,39,2017-05-08 12:22:05,http://stoltenberg.info/magdalena.satterfield,,253.233.19.253,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n1744,1,39,2017-01-04 06:35:13,http://schamberger.org/frederique.parker,,166.177.188.141,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n1745,1,39,2017-03-29 15:25:41,http://farrell.co/mandy_damore,,12.157.113.232,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n1746,1,39,2016-12-28 14:56:25,http://bruen.co/stacey,,92.227.208.137,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n1747,1,39,2017-02-24 07:51:57,http://stokesheel.io/elias,,2.243.15.174,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n1748,1,39,2017-04-17 06:44:35,http://olson.com/tyler_steuber,,252.26.182.51,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n22771,8,510,2017-03-06 03:43:50,http://toy.io/manley_bruen,,69.196.231.172,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n22772,8,510,2017-04-25 22:35:50,http://ebert.name/josefina.pollich,,123.66.216.247,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n22773,8,510,2017-05-22 14:32:32,http://cristzieme.com/heath_mccullough,,89.240.169.16,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n22774,8,510,2017-01-26 09:49:22,http://wilkinson.info/antwan.franecki,,94.112.9.139,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n22775,8,510,2017-03-10 02:06:05,http://bechtelar.co/jacynthe_schimmel,,227.173.141.106,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n22776,8,510,2017-05-27 14:25:01,http://rathmarvin.co/allison,,42.244.198.57,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n22777,8,510,2017-06-06 20:32:20,http://gaylord.io/franco_tremblay,,228.185.173.31,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n22778,8,510,2017-05-30 02:02:36,http://konopelski.co/davin_friesen,,103.197.251.11,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n22779,8,510,2017-04-02 11:44:37,http://gerhold.org/hollie,,251.38.254.200,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n22780,8,510,2016-12-21 00:12:48,http://franecki.co/fay.heel,,54.180.117.115,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n22781,8,510,2016-12-13 07:50:30,http://gerhold.com/jedidiah_pfannerstill,,249.105.15.78,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n22782,8,510,2016-12-31 14:03:07,http://walker.name/garfield_mayert,,51.235.110.190,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n22783,8,510,2017-06-05 10:03:40,http://parker.info/gia_macejkovic,,222.213.125.249,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n22784,8,510,2017-02-13 15:45:37,http://cummings.net/hertha_schuppe,,251.223.78.96,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n22785,8,510,2017-04-27 19:56:25,http://wolfschamberger.name/alda.mcclure,,150.19.247.22,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n22786,8,510,2017-01-05 06:35:23,http://herzog.co/electa.schultz,,43.24.55.228,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n22787,8,510,2016-12-22 11:21:35,http://hoppe.name/magdalen,,235.178.36.105,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n22788,8,510,2017-05-09 13:26:58,http://bauch.org/stone_huels,,212.214.154.57,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n22789,8,510,2017-03-20 02:22:20,http://schuster.name/zoila,,249.208.8.59,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n22790,8,511,2017-04-07 18:48:21,http://hackett.org/pascale_watsica,,24.243.195.154,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n22791,8,511,2017-04-13 23:49:21,http://mosciski.co/chaz_dickinson,,26.80.109.112,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n22792,8,511,2016-12-17 03:22:37,http://macgyver.org/elwin,,22.201.147.221,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n22793,8,511,2017-01-01 11:48:04,http://kovacek.biz/creola,,37.187.179.54,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n22794,8,511,2017-02-27 14:09:02,http://dickenswiegand.com/emerald_funk,,78.175.97.57,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n22795,8,511,2017-01-20 16:32:19,http://hegmann.info/obie_stamm,,163.218.202.120,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n22796,8,511,2017-02-20 01:34:17,http://lind.org/veda,,226.122.143.88,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n22797,8,511,2017-05-30 17:28:47,http://ondricka.net/katheryn,,47.133.200.234,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n22798,8,511,2017-06-01 11:41:15,http://terrypowlowski.io/gerard,,245.119.140.198,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n22799,8,511,2017-02-22 03:04:10,http://parisianwolff.io/cortney.sauer,,31.44.151.165,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n22800,8,511,2017-01-15 20:32:09,http://bosco.co/francisca.feeney,,7.74.130.75,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n22801,8,511,2017-06-12 13:04:15,http://johnsglover.name/maritza,,154.164.173.120,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n22802,8,511,2017-05-05 23:40:24,http://mcculloughgreenholt.com/ila,,28.16.71.211,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n22803,8,511,2017-05-12 12:14:53,http://shanahan.name/kirsten,,144.157.152.250,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n22804,8,511,2017-03-26 23:38:56,http://haag.io/wade_turcotte,,24.54.57.9,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n22805,8,511,2017-02-23 07:33:43,http://yostbayer.net/hazel_marks,,73.162.84.75,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n22806,8,511,2017-04-19 11:15:54,http://hand.net/preston,,172.185.89.125,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n22807,8,511,2017-01-22 19:59:02,http://quitzonbernier.org/zoila,,67.71.55.242,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n22808,8,511,2017-03-17 00:16:57,http://bahringer.org/adam,,19.36.64.197,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n22809,8,511,2017-04-11 06:05:38,http://wolff.com/hattie,,152.239.172.48,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n22810,8,511,2017-03-09 12:55:00,http://spencer.co/jaeden.ortiz,,21.182.77.220,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n22811,8,511,2016-12-20 05:52:40,http://schroederschimmel.name/makayla,,29.172.202.188,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n22812,8,511,2016-12-26 07:05:18,http://ohara.co/elaina,,81.173.245.167,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n22813,8,511,2017-02-15 16:24:32,http://orn.org/kathryne_pacocha,,52.65.13.48,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n22814,8,511,2017-01-16 18:44:10,http://johnston.info/eleanora.durgan,,70.118.81.151,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n22815,8,511,2016-12-15 23:08:02,http://jacobi.biz/paige,,158.172.98.19,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n22816,8,511,2017-05-01 12:08:13,http://borerwillms.com/blanca.schiller,,123.199.60.188,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n22817,8,511,2017-03-09 18:48:02,http://schaden.name/davion_marvin,,228.56.177.198,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n22818,8,511,2017-02-25 16:56:41,http://gislasonborer.com/kailyn_hermiston,,61.45.235.112,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n22819,8,511,2017-03-05 10:50:44,http://kovacekwillms.name/favian_wehner,,56.189.223.156,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n22820,8,511,2017-03-06 02:32:35,http://wolffschroeder.net/gwendolyn_marquardt,,211.125.234.128,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n22821,8,511,2017-02-16 00:12:03,http://mills.co/vesta,,141.172.56.32,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n22822,8,511,2017-02-01 01:03:41,http://vonrueden.net/akeem,,195.145.245.134,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n22823,8,511,2017-01-26 11:54:16,http://torphy.name/dayton_okon,,54.167.226.79,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n22824,8,511,2017-05-17 10:14:45,http://gleichner.info/nakia_vonrueden,,14.213.25.13,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n22825,8,511,2017-04-27 01:45:41,http://volkman.info/genevieve.rohan,,46.218.83.95,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n1749,1,39,2017-04-01 11:20:22,http://casper.org/vernice_kaulke,,237.149.207.239,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n1750,1,39,2017-03-04 07:52:29,http://marks.name/bertrand.oberbrunner,,96.85.73.213,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n1751,1,39,2017-05-08 01:19:25,http://toybraun.org/deie.zulauf,,81.52.192.165,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n1752,1,39,2017-04-20 07:01:46,http://hansen.io/guy.ryan,,66.119.167.53,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n1753,1,39,2017-02-13 22:19:27,http://kohlerstark.biz/giovani_rosenbaum,,162.199.32.177,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n1754,1,39,2017-04-15 18:21:34,http://lehner.biz/florencio_waelchi,,233.116.13.174,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n1755,1,39,2017-04-01 02:41:44,http://kemmer.net/kian,,119.109.217.133,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n1756,1,39,2017-04-04 07:47:51,http://lakinpowlowski.net/edmond.gulgowski,,47.223.20.100,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n1757,1,39,2017-05-06 17:59:10,http://schmidtjaskolski.info/dorian,,170.138.176.134,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1758,1,39,2017-01-04 21:47:35,http://darekohler.info/burnice,,193.145.247.209,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n1759,1,39,2017-05-03 20:55:57,http://daugherty.io/elvis_goodwin,,197.160.115.195,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n1760,1,39,2017-06-07 11:16:46,http://king.name/oda,,80.224.146.110,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n1761,1,39,2017-05-20 01:33:20,http://kuhnquigley.name/clare,,49.222.204.13,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n1762,1,39,2017-02-08 08:23:07,http://howezulauf.info/breana_hayes,,15.57.28.86,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n1763,1,39,2017-01-16 01:59:44,http://reilly.biz/oleta.schneider,,111.89.198.226,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n1764,1,39,2017-01-19 16:31:07,http://shields.io/maximillian,,254.168.250.26,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n1765,1,39,2017-05-07 16:16:56,http://fadelreilly.io/anderson,,31.52.164.60,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n1766,1,39,2017-05-30 09:58:49,http://wiza.name/karolann,,34.127.16.132,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n1767,1,39,2017-04-04 19:58:31,http://balistreriwindler.org/gladys,,69.133.66.2,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n1768,1,39,2017-02-23 12:36:42,http://okonterry.info/annetta.casper,,209.152.46.186,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n1769,1,39,2017-01-30 07:19:27,http://stamm.org/marjorie.kerluke,,118.201.90.182,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n1770,1,39,2017-06-03 22:53:51,http://effertz.io/bertha_haley,,227.201.212.243,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n1771,1,39,2017-04-06 09:52:02,http://strosin.io/bette_marquardt,,147.2.229.8,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n1772,1,39,2017-05-06 03:57:24,http://hamillwitting.info/antonia.halvorson,,192.216.194.228,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n1773,1,39,2017-01-31 04:25:23,http://reilly.co/terry,,198.75.11.239,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n1774,1,39,2017-05-22 08:12:08,http://barrows.co/clint,,39.106.67.170,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n1775,1,39,2017-02-10 02:38:45,http://cole.io/wilburn.haley,,253.8.79.81,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n1776,1,39,2017-05-11 10:00:09,http://gulgowski.name/clifton.cummings,,21.244.140.148,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n1777,1,39,2017-01-15 22:50:48,http://breitenberg.co/carlie,,183.246.46.144,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n1778,1,39,2017-02-04 06:35:03,http://volkman.co/elwin,,20.123.171.129,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n1779,1,40,2017-02-03 01:46:56,http://hettinger.com/emmett.hyatt,,30.137.215.216,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n1780,1,40,2017-02-04 07:17:11,http://hayes.info/coby.labadie,,67.103.41.185,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n1781,1,40,2017-01-31 06:01:10,http://fritschparker.name/liliane,,182.223.196.96,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n1782,1,40,2017-01-17 18:32:57,http://casper.biz/jeramy,,121.129.14.142,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n1783,1,40,2017-01-14 19:07:40,http://mills.com/christelle.gibson,,9.54.162.142,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n1784,1,40,2017-04-25 20:46:41,http://williamson.name/gertrude,,49.219.207.89,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n1785,1,40,2017-02-17 04:47:01,http://nienowoconnell.org/katlyn.roberts,,232.20.173.136,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n1786,1,40,2017-03-07 06:05:34,http://waters.org/wallace.damore,,88.38.94.71,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n1787,1,40,2017-05-05 19:48:00,http://spencer.com/cheyenne.simonis,,197.82.252.197,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n1788,1,40,2017-03-18 07:56:55,http://kinggutkowski.org/pietro_okuneva,,52.121.203.32,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n1789,1,40,2017-04-12 21:38:19,http://rippin.net/corene,,101.201.248.66,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n1790,1,40,2017-03-28 02:37:10,http://croninmayert.co/hilma,,48.128.169.240,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n1791,1,40,2017-04-30 16:17:35,http://marquardtpagac.net/kiera,,51.225.69.228,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n1792,1,40,2017-02-09 22:23:45,http://sporer.io/timmothy,,110.250.8.32,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n1793,1,40,2017-05-25 11:20:15,http://hoppegutmann.name/destany_kautzer,,31.104.220.240,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n1794,1,40,2017-04-16 17:29:17,http://gulgowskismith.com/lillie.volkman,,121.227.133.94,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n1795,1,40,2017-01-22 16:46:31,http://daughertyschowalter.org/carmela,,172.142.33.55,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n1796,1,40,2017-04-30 01:54:54,http://berge.name/salma,,111.82.211.76,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n1797,1,40,2017-03-09 22:36:54,http://schultz.com/gertrude,,44.167.249.27,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n1798,1,40,2017-06-06 08:33:05,http://effertzkeeling.org/braden_will,,26.143.105.6,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n1799,1,40,2016-12-31 23:52:26,http://kreiger.net/ebony,,154.2.167.176,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n1800,1,40,2017-03-12 05:45:43,http://strosinfeeney.co/loren.dooley,,160.122.32.112,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n1801,1,40,2017-04-09 16:33:06,http://damoreoberbrunner.name/braden,,26.18.246.114,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n1802,1,40,2017-05-20 10:42:07,http://koeppsimonis.net/frieda,,60.46.31.106,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n1803,1,40,2016-12-29 17:53:58,http://denesik.biz/stephanie,,142.173.86.11,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n22826,8,512,2017-04-29 11:35:57,http://batztorp.name/maymie,,151.164.248.221,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n22827,8,512,2017-03-14 08:44:51,http://mayerbotsford.info/dexter.heathcote,,249.183.159.193,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n22828,8,512,2017-02-26 12:42:09,http://metz.co/gilberto_pouros,,163.115.226.245,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n22829,8,512,2016-12-23 13:48:31,http://hills.info/augustine,,97.184.49.244,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n22830,8,512,2017-01-30 21:09:19,http://legros.org/trinity,,215.113.115.227,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n22831,8,512,2017-06-03 11:00:23,http://greenfeldertowne.org/jay.kozey,,132.52.96.115,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n22832,8,512,2017-06-06 10:45:04,http://cronin.com/malinda,,248.54.45.21,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n22833,8,512,2017-01-17 13:17:42,http://stroman.com/mortimer,,238.164.250.53,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n22834,8,512,2016-12-17 17:45:58,http://okon.com/bernadine.moen,,223.231.138.91,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n22835,8,512,2017-01-01 04:16:01,http://satterfieldschneider.net/jamal.dietrich,,39.59.162.155,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n22836,8,512,2017-04-30 05:35:18,http://mclaughlin.org/abigale,,215.147.111.160,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n22837,8,512,2017-04-14 11:33:46,http://schummstiedemann.info/lia,,35.211.111.113,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n22838,8,512,2017-03-06 16:49:31,http://boehm.org/kyra,,218.23.247.79,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n22839,8,512,2016-12-13 06:29:54,http://yundt.name/domingo,,20.175.210.139,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n22840,8,512,2017-02-28 12:28:18,http://swiftkrajcik.net/hudson.wiegand,,184.59.248.58,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n22841,8,512,2017-03-24 06:42:57,http://schmidtrau.biz/sabrina,,172.50.119.105,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n22842,8,512,2017-02-12 10:26:41,http://davis.io/hosea,,129.248.183.230,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n22843,8,512,2017-05-21 17:27:39,http://reichel.net/lawrence_carroll,,199.45.168.149,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n22844,8,512,2017-01-15 09:14:53,http://bayer.co/lexus.beer,,179.156.226.18,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n22845,8,512,2017-06-08 09:23:00,http://senger.name/jeanne_oberbrunner,,248.152.32.115,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n22846,8,512,2017-02-18 06:27:15,http://barrows.name/dominic.tromp,,232.88.107.183,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n22847,8,512,2017-02-04 21:02:13,http://anderson.io/clarabelle,,199.189.131.135,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n22848,8,512,2016-12-16 03:14:29,http://parisian.org/tobin_keeling,,161.87.104.73,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n22849,8,512,2017-04-08 08:25:41,http://nicolas.biz/brandyn,,62.70.214.228,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n22850,8,512,2017-05-10 06:42:30,http://turnerabshire.org/penelope,,202.162.48.94,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n22851,8,512,2017-01-15 13:20:58,http://ullrich.org/viva,,204.51.217.33,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n22852,8,512,2017-02-25 05:17:56,http://wunschstracke.net/vladimir.hoeger,,64.127.216.31,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n22853,8,512,2017-06-05 20:05:03,http://greenholt.net/jaquan_fahey,,202.150.249.114,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n22854,8,512,2016-12-20 11:14:40,http://rueckerkuphal.name/furman,,48.5.209.159,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n22855,8,512,2017-01-14 18:15:12,http://huels.net/yeenia,,89.138.40.22,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n22856,8,512,2017-06-10 04:54:44,http://robertsquigley.com/jamal_luettgen,,237.99.130.154,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n22857,8,512,2017-04-26 21:30:18,http://kovacek.biz/davion.cronin,,8.125.64.24,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n22858,8,512,2017-05-07 03:48:37,http://damore.biz/wilfred_bergstrom,,237.186.66.71,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n22859,8,512,2016-12-14 08:30:12,http://glover.com/jamar,,5.126.85.253,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n22860,8,512,2017-05-19 23:35:46,http://schowalter.io/talon_ullrich,,13.162.125.248,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n22861,8,512,2017-06-02 17:34:02,http://lednercrist.com/gordon_keebler,,137.213.121.48,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n22862,8,512,2017-03-24 16:04:12,http://homenick.co/greta,,180.15.230.163,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n22863,8,512,2017-04-01 00:54:14,http://swaniawski.co/taylor,,167.81.87.6,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n22864,8,513,2017-04-07 08:31:14,http://homenicklangworth.info/zoey_schultz,,96.33.181.204,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n22865,8,513,2017-01-03 01:32:19,http://abernathy.com/jarred.paucek,,242.187.226.14,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n22866,8,513,2017-01-13 15:16:28,http://funk.org/richie,,215.107.28.111,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n22867,8,513,2017-01-15 18:19:59,http://greenfelder.biz/orlo,,16.158.48.90,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n22868,8,513,2017-01-30 17:48:15,http://ryanlangosh.io/jaron,,147.194.23.27,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n22869,8,513,2017-01-25 12:59:25,http://leuschke.info/nolan_durgan,,177.69.105.76,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n22870,8,513,2017-03-02 23:27:54,http://hamillrutherford.io/zachariah,,241.222.181.241,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n22871,8,513,2017-01-13 05:45:17,http://doyle.co/karen.cremin,,70.125.133.238,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n22872,8,513,2017-05-11 17:14:33,http://weber.net/isidro,,87.167.87.229,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n22873,8,513,2016-12-31 04:20:37,http://stracke.net/elbert_ratke,,95.72.172.97,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n22874,8,513,2017-06-08 14:40:19,http://kuhicwalker.io/juston,,59.247.44.99,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n22875,8,513,2017-01-04 15:23:03,http://willgulgowski.name/furman,,13.207.73.76,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n22876,8,513,2017-02-21 00:56:03,http://huels.co/elinor_walker,,117.84.139.30,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n22877,8,513,2017-04-05 03:45:03,http://mayer.biz/keith.bradtke,,16.197.66.192,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n22878,8,513,2017-04-10 08:34:17,http://ratke.info/amber,,136.100.4.153,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n22879,8,513,2017-01-05 20:22:19,http://stanton.net/noemie,,208.84.129.84,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n22880,8,513,2017-04-10 10:06:25,http://eichmann.com/stanton,,239.53.249.168,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n22881,8,513,2017-03-21 08:49:48,http://streich.io/jovani,,111.247.110.127,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n1804,1,40,2017-04-12 10:26:21,http://keler.co/harley.lueilwitz,,44.76.228.249,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n1805,1,40,2017-02-16 07:38:45,http://von.io/dane_toy,,86.52.213.41,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n1806,1,40,2016-12-23 06:14:22,http://mcglynn.org/turner,,181.58.226.169,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n1807,1,40,2017-04-29 11:42:01,http://wiza.co/jarvis,,4.109.94.164,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n1808,1,40,2017-03-28 17:23:37,http://dicki.name/kelley.breitenberg,,200.109.87.172,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n1809,1,40,2017-03-30 21:14:58,http://bergebuckridge.net/donna,,153.219.200.173,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n1810,1,40,2017-04-19 21:49:35,http://pouros.biz/lucy,,210.200.160.154,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n1811,1,40,2017-01-03 03:09:24,http://fadel.name/claria_borer,,33.54.28.219,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n1812,1,40,2017-01-02 02:52:49,http://jacobs.net/sylvan,,184.66.77.233,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n1813,1,40,2017-05-05 14:51:46,http://medhurstrolfson.biz/jabari_trantow,,144.3.73.60,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n1814,1,40,2017-05-01 03:34:49,http://hagenes.com/nettie.beier,,45.159.227.230,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n1815,1,40,2017-01-25 22:31:54,http://rolfson.co/bridgette,,198.46.173.105,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n1816,1,40,2017-03-02 08:26:39,http://bergnaum.co/anais,,225.110.57.203,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n1817,1,40,2017-04-10 10:36:12,http://gaylordrunolfon.biz/tiara_runolfsdottir,,136.198.252.218,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n1818,1,40,2017-01-25 23:18:48,http://corwin.info/danielle,,168.135.31.4,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n1819,1,40,2017-03-21 04:03:13,http://flatley.io/vito,,215.223.159.56,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n1820,1,40,2017-01-27 15:15:43,http://mcdermottgislason.info/verlie,,59.21.138.91,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n1821,1,40,2017-03-03 15:15:03,http://cain.co/mara,,214.173.198.15,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n1822,1,40,2016-12-24 12:23:19,http://anderson.biz/estell,,58.235.157.179,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n1823,1,40,2017-01-14 06:23:17,http://ortizerdman.info/hardy,,32.168.154.112,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n1824,1,40,2016-12-14 17:51:31,http://bartell.com/kaia,,168.56.27.198,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n1825,1,40,2017-06-13 02:42:33,http://beahan.io/elya.wisoky,,26.121.117.125,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n1826,1,40,2017-02-24 05:53:03,http://funk.co/ima.considine,,163.240.134.178,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n1827,1,40,2016-12-29 20:28:36,http://hackettmacejkovic.info/bernice_reinger,,94.93.106.109,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n1828,1,40,2017-05-13 22:04:27,http://cruickshank.com/lonny_walker,,101.178.234.126,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n1829,1,40,2017-04-14 05:36:37,http://nienow.info/gilberto,,13.251.241.167,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n1830,1,40,2017-05-23 02:33:30,http://kohler.io/conrad.luettgen,,212.173.27.232,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n1831,1,40,2017-03-06 14:49:20,http://shields.net/lisette,,112.202.19.166,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n1832,1,40,2017-02-11 13:30:32,http://millerstiedemann.org/lucio,,72.90.105.209,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n1833,1,41,2017-02-23 02:46:23,http://dickinsonrobel.name/althea,,231.101.125.119,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n1834,1,41,2017-02-12 10:24:04,http://gradybechtelar.org/bianka,,95.146.242.87,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n1835,1,41,2017-03-24 16:59:34,http://okuneva.org/krystel,,32.184.194.61,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n1836,1,41,2017-02-22 03:18:01,http://turner.info/jade_buckridge,,207.74.227.24,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n1837,1,41,2016-12-23 10:30:59,http://bosco.io/vada,,139.242.246.98,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n1838,1,41,2017-06-11 04:31:39,http://pouros.io/scottie_keeling,,183.126.97.177,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n1839,1,41,2017-06-08 04:47:42,http://turner.io/jordan,,242.13.82.58,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n1840,1,41,2017-04-11 06:56:16,http://larkinpadberg.biz/dannie_wuckert,,205.116.68.43,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n1841,1,41,2017-04-19 19:09:35,http://turnervandervort.com/filiberto,,7.106.87.71,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n1842,1,41,2017-01-15 23:26:16,http://mcculloughmiller.org/morton.osinski,,184.21.173.157,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n1843,1,41,2017-04-16 03:01:47,http://nitzscheauer.com/diana,,26.45.17.193,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n1844,1,41,2017-03-14 14:31:59,http://gusikowski.io/edna_will,,131.12.157.247,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n1845,1,41,2017-05-13 09:51:45,http://mohrhilll.info/derek.mcglynn,,87.63.111.202,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n1846,1,41,2016-12-17 12:52:45,http://gottlieb.net/marilyne_mayert,,96.223.133.191,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n1847,1,41,2017-01-16 11:23:36,http://zemlak.name/august_gibson,,46.90.215.23,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n1849,1,41,2017-02-02 22:23:25,http://grimes.io/carli_mcglynn,,211.218.92.248,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n1850,1,41,2017-02-23 04:19:53,http://murazikmurphy.co/shawna,,78.216.178.44,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n1851,1,41,2017-01-15 23:32:05,http://pacochaschamberger.org/berry,,145.54.172.165,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n1852,1,41,2017-04-06 05:31:59,http://hermann.biz/horacio,,128.90.254.98,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n1853,1,41,2017-04-07 01:02:39,http://ryan.co/trent,,220.29.84.139,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n1854,1,41,2017-02-17 15:34:37,http://stracke.com/christophe,,23.101.170.232,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n1855,1,41,2017-04-17 13:18:23,http://ortiz.com/keith,,56.108.34.197,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n1856,1,41,2017-02-10 08:14:30,http://hermann.info/reuben,,65.186.154.109,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n1857,1,41,2017-04-21 02:48:14,http://dietrichking.info/naomie_doyle,,71.164.70.176,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n1858,1,41,2017-01-15 13:08:31,http://hansen.name/manuela_okon,,247.149.120.50,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n1859,1,41,2017-04-05 10:01:59,http://franecki.io/dayne,,194.29.36.164,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n1860,1,41,2016-12-28 10:53:05,http://littlewaelchi.info/teagan.stiedemann,,30.242.73.115,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n22882,8,513,2017-03-31 19:03:08,http://strosin.co/monica.parisian,,95.9.215.49,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n22883,8,513,2017-02-11 09:23:36,http://larsonspinka.biz/jailyn,,12.243.11.234,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n22884,8,513,2016-12-20 10:48:45,http://yostwolf.com/gielle,,103.154.5.162,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n22885,8,513,2017-05-07 15:34:08,http://heelpfeffer.com/jeie_luettgen,,143.224.189.42,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n22886,8,513,2017-05-16 01:26:42,http://mitchell.info/clotilde,,89.15.32.166,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n22887,8,513,2016-12-13 10:42:52,http://harber.co/candelario_hettinger,,218.95.13.43,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n22888,8,513,2017-05-28 13:18:54,http://stromanfahey.org/claude.dickens,,182.227.103.16,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n22889,8,513,2017-03-28 03:58:03,http://goyette.biz/ania,,212.56.197.211,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n22890,8,513,2017-06-01 14:21:26,http://beer.name/tito,,50.164.151.38,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n22891,8,513,2017-05-22 21:59:40,http://johnson.co/judson_gaylord,,155.96.33.232,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n22892,8,513,2017-02-28 17:21:01,http://adams.co/laurie,,190.15.201.169,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n22893,8,513,2017-04-05 17:13:11,http://dachabbott.co/irma_zulauf,,216.26.69.148,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n22894,8,513,2017-04-03 01:03:51,http://murraystoltenberg.com/kavon,,94.63.2.189,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n22895,8,513,2017-02-08 19:25:10,http://lakinbergnaum.name/cary_franecki,,134.120.113.148,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n22896,8,513,2017-04-08 15:16:36,http://townehaley.info/laura_brown,,66.158.250.205,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n22897,8,513,2017-03-26 05:05:28,http://trompdooley.name/hilma_orn,,94.112.236.230,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n22898,8,513,2017-04-02 22:44:30,http://prohaskakertzmann.com/marcella.lemke,,20.183.215.115,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n22899,8,513,2017-01-25 17:57:36,http://bernhardokeefe.info/evert_brekke,,111.154.176.242,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n22900,8,513,2017-06-10 03:06:08,http://hand.name/gladyce_turcotte,,208.232.116.74,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n22901,8,514,2017-04-19 02:07:47,http://donnelly.info/reta_hintz,,75.222.242.128,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n22902,8,514,2017-02-21 08:15:51,http://borer.com/mozell,,164.104.140.137,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n22903,8,514,2016-12-25 06:57:44,http://mckenzie.info/herminia.nitzsche,,85.170.17.202,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n22904,8,514,2017-04-20 08:05:00,http://macejkovic.org/josefa.hyatt,,195.246.116.251,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n22905,8,514,2017-03-18 04:38:58,http://altenwerth.net/hettie,,251.41.177.114,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n22906,8,514,2017-03-27 20:18:16,http://schuppe.org/dortha,,115.184.64.41,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n22907,8,514,2017-02-02 14:06:42,http://koch.com/ruel,,224.186.108.60,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n22908,8,514,2017-03-20 05:30:46,http://kundewitting.co/patrick.hegmann,,37.64.79.10,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n22909,8,514,2016-12-31 20:11:25,http://ullrich.co/friedrich_moriette,,19.158.134.237,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n22910,8,514,2017-06-03 10:54:19,http://zulauffay.biz/erling,,40.150.40.68,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n22911,8,514,2017-02-16 03:03:05,http://braun.biz/lilla,,170.200.81.94,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n22912,8,514,2017-04-02 03:22:49,http://borer.com/micah,,124.176.27.49,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n22913,8,514,2017-04-06 20:25:34,http://olson.io/orion.hahn,,103.122.43.174,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n22914,8,514,2017-04-09 16:09:54,http://mayer.info/blanca_schiller,,41.9.37.197,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n22915,8,514,2017-06-11 17:21:57,http://cristmueller.biz/george,,38.215.49.114,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n22916,8,514,2017-03-08 19:44:47,http://koelpinshanahan.info/axel_parisian,,33.70.215.48,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n22917,8,514,2017-06-01 16:03:30,http://hamill.io/maxwell.tillman,,248.112.67.30,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n22918,8,514,2017-05-14 13:04:40,http://dickens.io/annetta,,137.207.5.241,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n22919,8,514,2017-05-22 20:06:30,http://kshlerincorwin.co/antonia.shields,,82.70.24.237,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n22920,8,514,2017-03-27 11:38:55,http://hermann.org/maci.mayert,,38.248.186.116,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n22921,8,514,2017-01-10 07:54:41,http://ziemannhilpert.com/jade_upton,,184.236.113.239,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n22922,8,514,2017-05-16 21:36:19,http://mitchell.org/nikolas,,157.6.48.124,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n22923,8,514,2017-01-01 12:29:37,http://bergstrombarton.co/jeanne,,245.67.213.92,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n22924,8,514,2017-02-26 04:41:59,http://eichmann.net/alda,,196.79.7.30,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n22925,8,514,2017-06-11 00:31:59,http://murphy.info/ray,,51.218.89.51,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n22926,8,514,2017-05-21 14:25:02,http://towne.biz/jarret.huel,,252.240.229.100,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n22927,8,514,2017-05-29 04:28:36,http://willmmith.net/jason,,42.103.103.187,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n22928,8,514,2017-05-30 06:02:58,http://kris.biz/deie.pfannerstill,,239.170.174.78,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n22929,8,514,2017-03-28 07:10:50,http://spencer.name/daisha,,94.170.182.250,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n22930,8,514,2017-02-28 22:02:11,http://hahnbarton.com/michel.haley,,139.75.82.166,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n22931,8,515,2017-05-02 22:21:40,http://hermannrippin.name/libbie.stoltenberg,,198.88.21.187,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n22932,8,515,2016-12-24 17:22:25,http://quigleydietrich.info/dasia,,18.138.126.84,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n22933,8,515,2017-05-07 03:10:06,http://king.co/deja.hintz,,25.200.119.190,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n22934,8,515,2017-01-30 10:59:20,http://stracke.biz/ezequiel.waters,,250.211.35.174,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n22935,8,515,2017-05-04 03:06:27,http://walkerhuels.biz/luciano,,114.128.124.152,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n22936,8,515,2017-03-06 03:24:33,http://bradtke.io/aaron,,50.200.159.214,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n1861,1,41,2017-05-13 23:47:14,http://wisoky.biz/emmy_durgan,,188.241.7.151,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n1862,1,41,2017-04-03 02:43:57,http://nader.io/clara.schmitt,,223.104.63.131,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n1863,1,41,2017-04-15 13:18:11,http://greensipes.com/trycia.wolff,,133.114.194.137,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n1864,1,41,2017-04-24 06:21:21,http://browngreen.biz/savanah,,136.145.190.206,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n1865,1,41,2017-06-12 10:46:08,http://moriette.net/norene,,129.36.12.213,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n1866,1,41,2017-05-24 07:48:57,http://ondricka.info/shawna,,217.97.129.16,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n1867,1,41,2016-12-25 17:33:33,http://treutelbosco.biz/nash,,252.68.9.244,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n1868,1,41,2016-12-16 09:00:34,http://spinkaconnelly.co/shaina.moriette,,185.225.30.205,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n1869,1,41,2017-02-15 17:50:40,http://von.org/adolf,,135.213.175.42,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n1870,1,41,2017-06-02 12:15:21,http://feil.net/betsy.block,,176.93.119.205,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n1871,1,41,2017-03-24 08:46:20,http://homenick.info/annalise_murray,,219.206.75.9,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n1872,1,41,2017-06-05 22:40:40,http://emard.org/evans.huel,,183.30.46.80,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n1873,1,41,2017-01-11 17:07:01,http://townekaulke.info/delbert,,163.75.125.76,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n1874,1,41,2017-05-17 08:12:53,http://bergewelch.net/april.becker,,152.157.216.39,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n1875,1,41,2017-05-09 03:26:40,http://little.net/lizzie_zemlak,,244.72.11.128,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n1876,1,41,2017-04-19 15:41:20,http://gleasonsatterfield.org/maximus_metz,,222.204.110.248,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n1877,1,41,2017-01-30 09:17:40,http://welchcarroll.co/ruby,,148.178.76.76,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n1878,1,41,2017-03-18 07:52:43,http://powlowski.info/raphael,,159.208.62.116,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n1879,1,41,2017-01-23 13:44:36,http://homenick.biz/kaci,,94.170.12.216,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n1880,1,41,2017-05-28 21:11:15,http://hartmann.name/eveline.shanahan,,125.135.192.150,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n1881,1,41,2017-04-28 14:15:28,http://bashirian.biz/allene_jenkins,,114.197.165.79,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n1882,1,42,2017-05-19 00:10:36,http://larkin.org/sandrine.mckenzie,,93.63.207.120,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n1883,1,42,2017-01-06 00:01:33,http://oconnell.name/kadin,,3.137.201.248,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n1884,1,42,2016-12-30 14:37:46,http://lindgren.io/eusebio,,84.105.217.197,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n1885,1,42,2017-05-14 23:10:57,http://heel.io/amparo_wyman,,87.225.152.248,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n1886,1,42,2016-12-15 15:31:51,http://moen.biz/mohamed,,174.199.4.142,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n1887,1,42,2017-03-15 04:23:04,http://caspermetz.io/kendall,,105.200.94.86,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n1888,1,42,2017-01-07 12:08:40,http://mohrkohler.io/torrey_barton,,40.245.155.197,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n1889,1,42,2017-02-23 15:24:54,http://feeney.info/blake.jast,,228.198.42.131,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n1890,1,42,2017-04-08 05:50:27,http://heaney.com/dorothy.braun,,88.178.177.89,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n1891,1,42,2017-03-27 06:39:38,http://monahan.net/zane_sauer,,172.109.78.180,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n1892,1,42,2016-12-21 17:22:11,http://wildermanbruen.org/emanuel.nolan,,140.249.60.242,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n1893,1,42,2017-03-29 04:46:54,http://lesch.info/ardella,,188.167.100.12,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n1894,1,42,2017-01-02 06:20:33,http://lindgrenhackett.co/tanya,,202.245.197.237,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n1895,1,42,2017-01-12 23:56:35,http://pauceklegros.co/emelia,,139.122.54.11,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n1896,1,42,2017-01-26 01:20:52,http://dach.org/valentine_goldner,,135.93.243.224,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n1897,1,42,2017-03-29 23:37:28,http://wolff.name/henri,,143.240.218.162,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n1898,1,42,2017-05-23 21:30:54,http://altenwerthconn.com/kenneth,,250.164.69.92,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n1899,1,42,2017-03-17 23:17:16,http://paucek.name/scotty,,137.2.120.35,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n1900,1,42,2017-03-24 05:56:18,http://fadelmiller.io/moriah_ratke,,75.197.230.219,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n1901,1,42,2017-04-18 12:06:34,http://luettgen.org/berniece,,149.155.175.144,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n1902,1,42,2017-05-28 08:27:53,http://ortiz.info/jennings.douglas,,10.175.194.225,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n1903,1,42,2016-12-22 14:53:53,http://will.name/alphonso.stoltenberg,,56.112.40.181,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n1904,1,42,2016-12-16 03:39:43,http://rodriguez.info/winfield,,151.117.61.206,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n1905,1,42,2017-02-20 15:36:26,http://smitham.info/devin.hayes,,207.127.20.136,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n1906,1,42,2017-03-06 09:59:48,http://keelingluettgen.org/marquise_satterfield,,202.204.68.250,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n1907,1,42,2017-05-20 12:28:00,http://weber.com/elvie.schamberger,,68.26.44.142,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n1908,1,42,2017-04-04 08:51:36,http://cummings.info/amber.aufderhar,,162.206.47.14,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n1909,1,42,2017-03-09 16:11:49,http://hackettwehner.com/viva,,155.63.197.231,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n1910,1,42,2016-12-13 17:34:46,http://weimannschuster.net/valentin,,59.50.152.73,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n1911,1,42,2017-05-06 17:31:18,http://ryanbergstrom.info/litzy,,19.121.56.122,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n1912,1,42,2017-01-05 04:19:12,http://wolff.co/vergie,,149.49.154.13,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n1913,1,42,2017-03-15 00:07:58,http://reichel.name/verner,,237.239.192.70,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n1914,1,42,2017-03-11 11:06:21,http://wunsch.co/janae,,29.145.26.79,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n1915,1,42,2017-03-26 01:13:04,http://gusikowskijohns.io/houston_bernhard,,177.206.2.140,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n1916,1,42,2017-03-30 06:45:13,http://marks.org/ashleigh.kub,,210.52.18.111,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n22937,8,515,2017-03-06 17:27:10,http://jones.org/danny,,160.133.36.98,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n22938,8,515,2017-03-06 05:26:16,http://upton.org/myrtie,,123.228.13.84,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n22939,8,515,2017-01-09 21:54:26,http://strosinsauer.com/elizabeth.schultz,,103.244.213.81,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n22940,8,515,2017-05-06 02:45:59,http://hilllglover.biz/lelah_emmerich,,68.74.33.69,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n22941,8,515,2017-05-20 17:30:14,http://christiansen.name/bettye,,124.204.209.57,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n22942,8,515,2017-02-23 01:04:08,http://kohler.info/haylee.strosin,,77.119.56.86,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n22943,8,515,2017-02-13 04:45:27,http://padberg.name/dee.ruecker,,181.125.243.61,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n22944,8,515,2016-12-28 16:53:48,http://reinger.co/lon.beier,,219.249.114.77,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n22945,8,515,2017-01-19 08:58:59,http://kreiger.biz/karley.schaefer,,134.149.41.34,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n22946,8,515,2017-01-08 15:48:29,http://gusikowskihettinger.io/emmitt,,46.61.175.170,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n22947,8,515,2017-03-12 18:02:14,http://brown.name/mireille_stehr,,249.196.146.85,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n22948,8,515,2017-01-22 20:11:23,http://gusikowskihomenick.info/asha,,17.70.183.172,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n22949,8,515,2017-01-08 11:30:30,http://kozeyohara.co/rusty,,56.209.24.155,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n22950,8,515,2017-01-27 12:48:53,http://olson.net/tyreek.king,,89.234.9.219,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n22951,8,515,2017-03-20 12:00:29,http://hermiston.com/irwin.murphy,,213.198.159.158,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n22952,8,515,2017-04-29 06:16:05,http://bechtelarwaelchi.co/bernita,,114.195.49.188,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n22953,8,515,2016-12-18 15:28:52,http://bartell.co/kathryn,,237.39.47.158,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n22954,8,515,2017-02-21 05:05:56,http://schinner.net/emiliano_schultz,,85.246.76.138,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n22955,8,515,2017-03-20 01:56:01,http://bashirianullrich.net/alexys,,112.61.33.248,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n22956,8,515,2017-03-21 01:15:16,http://hamill.com/josh,,126.143.167.122,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n22957,8,515,2017-01-18 21:38:33,http://schambergermann.net/jayda,,215.174.225.164,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n22958,8,515,2017-04-25 09:00:00,http://schoenbotsford.org/jasmin,,23.35.195.156,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n22959,8,515,2017-05-07 10:50:02,http://flatley.name/gardner_keler,,185.88.9.153,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n22960,8,515,2017-05-30 23:01:21,http://goyette.io/ashly,,168.109.190.124,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n22961,8,515,2017-04-09 13:49:16,http://toy.net/robbie.adams,,83.76.99.203,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n22962,8,515,2017-06-08 10:19:59,http://conroyswaniawski.co/skyla,,232.95.193.11,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n22963,8,515,2017-02-17 12:07:10,http://grimes.name/keanu_feeney,,223.240.95.176,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n22964,8,515,2017-01-27 01:08:33,http://leannon.org/rosemary,,57.190.29.135,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n22965,8,515,2017-05-01 07:17:49,http://schowalter.name/buddy,,99.195.28.156,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n22966,8,515,2017-03-07 01:21:16,http://legrosbauch.org/camryn,,26.78.191.106,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n22967,8,515,2017-05-27 02:40:14,http://wiegand.com/deshaun,,46.235.144.251,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n22968,8,515,2016-12-22 03:43:55,http://parisianshields.io/lazaro.mayert,,216.158.121.162,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n22969,8,515,2017-03-03 00:03:58,http://nienow.net/adriana_ward,,86.207.170.211,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n22970,8,515,2017-03-10 12:15:06,http://kundemcglynn.net/ruth,,119.232.138.87,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n22971,8,515,2017-03-29 12:45:02,http://simonis.co/darryl_welch,,233.64.81.102,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n22972,8,515,2017-01-16 07:51:06,http://stehr.com/mandy.marquardt,,37.224.153.133,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n22973,8,515,2017-01-15 12:15:32,http://corkery.com/rodrick_turner,,111.71.128.163,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n22974,8,515,2017-04-24 09:38:34,http://reichert.biz/ila,,23.227.55.154,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n22975,8,515,2017-04-19 01:26:52,http://bashirian.net/britney,,248.213.187.190,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n22976,8,515,2016-12-13 13:38:06,http://gulgowski.name/paula,,153.230.167.162,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n22977,8,515,2016-12-22 02:57:06,http://ratke.biz/amiya,,108.233.204.95,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n22978,8,515,2017-03-15 10:27:57,http://hoeger.biz/iliana_swaniawski,,35.147.18.14,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n22979,8,515,2017-01-12 23:58:00,http://jaskolski.co/kristin,,243.155.231.110,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n22980,8,515,2017-04-14 12:06:19,http://mayer.net/sherwood,,161.218.110.48,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n22981,8,515,2017-01-13 09:57:58,http://schummborer.com/herminio_upton,,46.153.76.237,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n22982,8,515,2017-01-07 21:25:31,http://greenholt.co/hattie,,105.42.172.116,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n22983,8,516,2017-04-17 07:53:40,http://huelkoepp.net/rhea.runolfon,,134.133.218.172,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n22984,8,516,2017-03-15 14:17:37,http://swaniawski.net/dale.schamberger,,42.114.246.249,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n22985,8,516,2017-05-07 11:26:26,http://schmidt.co/margret,,86.180.4.123,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n22986,8,516,2017-06-12 07:48:57,http://langosh.org/abraham,,201.174.228.176,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n22987,8,516,2017-06-02 10:18:33,http://ledner.net/zaria,,37.99.217.118,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n22988,8,516,2017-06-10 22:48:42,http://green.biz/elody_koch,,203.28.140.229,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n22989,8,516,2017-04-14 02:55:13,http://willmsfeil.co/desiree,,202.228.22.110,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n22990,8,516,2017-05-30 23:24:38,http://langworth.io/robbie.jakubowski,,121.250.109.224,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n22991,8,516,2017-02-04 07:29:22,http://medhurst.io/emmanuel.yundt,,111.107.214.250,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n1917,1,42,2017-06-13 21:27:02,http://ullrich.info/vickie.roberts,,188.230.166.163,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n1918,1,42,2017-01-01 22:42:44,http://lebsackcollins.co/wellington,,164.153.178.188,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n1919,1,42,2017-02-18 18:30:22,http://luettgen.co/belle_deckow,,126.103.46.135,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n1920,1,42,2017-03-18 03:45:09,http://mraz.net/clyde,,126.140.153.86,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n1921,1,42,2017-06-09 09:18:15,http://macgyver.name/hulda,,124.44.208.166,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n1922,1,42,2017-01-03 17:42:59,http://gorczany.info/henderson.williamson,,65.36.113.97,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n1923,1,42,2017-03-17 15:22:34,http://strosin.io/oliver_smith,,226.90.19.47,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n1924,1,42,2017-06-04 08:52:36,http://mosciskiwatsica.org/diamond,,237.50.15.127,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n1925,1,42,2017-03-01 15:28:23,http://beer.org/tyrese_boyer,,35.137.90.72,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n1926,1,42,2017-06-11 20:35:45,http://douglasherzog.io/augustine_larson,,94.225.180.162,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n1927,1,42,2017-02-21 09:15:56,http://deckow.info/anya.romaguera,,171.79.239.208,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n1928,1,42,2017-03-08 19:09:35,http://weimann.io/ambrose,,9.133.138.50,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n1929,1,42,2017-01-25 23:47:47,http://raynorzboncak.net/zack_goodwin,,123.184.109.149,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n1930,1,42,2017-03-24 06:25:33,http://littel.info/toy,,243.54.10.53,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n1931,1,42,2017-04-03 10:03:06,http://wuckerttreutel.io/cornell_crist,,25.246.96.139,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n1932,1,42,2017-05-25 09:57:07,http://hegmann.name/hosea,,69.8.236.80,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n1933,1,42,2017-02-06 12:46:50,http://mills.org/emmitt.lind,,92.222.50.199,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n1934,1,42,2017-06-05 08:47:53,http://ernser.co/kaela,,253.112.100.14,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n1935,1,42,2017-04-14 01:59:19,http://bergebradtke.net/bernice,,146.13.186.192,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n1936,1,42,2017-01-13 02:26:36,http://ryan.com/willie,,15.140.161.249,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n1937,1,42,2017-01-09 02:01:09,http://beahan.co/rickie.nitzsche,,196.29.136.199,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n1938,1,43,2016-12-27 03:57:53,http://predovic.com/rae_monahan,,126.85.73.47,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n1939,1,43,2017-01-24 09:59:32,http://corwin.co/calista_harris,,5.137.54.118,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n1940,1,43,2017-01-13 13:35:47,http://corwin.net/federico,,9.117.217.177,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n1941,1,43,2017-06-02 14:23:09,http://adams.com/alvina_jones,,246.183.199.117,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n1942,1,43,2017-02-25 18:22:09,http://marquardtgrant.net/elvis,,151.189.159.101,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n1943,1,43,2017-05-06 15:23:20,http://mann.org/wayne,,153.2.21.19,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n1944,1,43,2017-01-09 14:23:30,http://ratkecummerata.name/gene_jerde,,119.233.173.26,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n1945,1,43,2017-03-07 15:20:56,http://rohan.biz/monserrate,,252.104.87.25,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n1946,1,43,2017-01-04 13:52:57,http://cartercrona.name/alden_kling,,2.93.184.215,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n1947,1,43,2017-02-03 09:07:53,http://gottlieb.com/bethel,,231.73.190.41,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n1948,1,43,2017-04-21 22:00:32,http://waelchi.name/isac,,65.183.19.8,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n1949,1,43,2017-06-08 06:52:03,http://bechtelar.co/clifford,,215.214.220.23,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n1950,1,43,2017-01-18 20:08:50,http://kirlin.org/leopold,,202.6.167.140,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n1951,1,43,2017-03-02 19:34:50,http://bartoletti.org/stewart_ko,,171.239.212.6,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n1952,1,43,2017-01-07 16:40:44,http://pollich.io/kaleb.davis,,22.12.155.110,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n1953,1,43,2017-02-12 06:19:14,http://swaniawski.org/lulu_shanahan,,20.47.164.202,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n1954,1,43,2017-04-25 12:37:50,http://vandervort.net/alberta,,217.13.145.150,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n1955,1,43,2017-01-29 12:14:45,http://brown.biz/pablo,,25.48.125.126,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n1956,1,43,2017-01-10 23:47:16,http://kuhn.net/hank_schneider,,151.32.201.177,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n1957,1,43,2017-05-23 22:48:30,http://bernhard.com/king,,231.210.250.32,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n1958,1,43,2016-12-21 16:30:51,http://pagacwalter.info/moises_nitzsche,,124.58.150.18,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n1959,1,43,2016-12-29 15:14:28,http://eichmann.net/madalyn,,159.179.243.220,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n1960,1,43,2017-01-02 22:14:23,http://zemlakjakubowski.io/horace,,103.213.239.60,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n1961,1,43,2017-01-07 20:00:58,http://terryrath.info/norval_lebsack,,234.6.213.166,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n1962,1,43,2017-03-11 16:18:21,http://kleinstrosin.net/caitlyn_thompson,,5.121.151.172,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n1963,1,43,2017-04-12 15:56:27,http://balistreri.net/willard_kaulke,,150.189.10.12,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n1964,1,43,2017-01-12 02:30:49,http://emardschiller.org/lois,,12.204.6.109,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n1965,1,43,2017-01-19 14:07:24,http://yundt.name/nelson,,143.3.169.189,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n1966,1,43,2017-02-01 10:47:00,http://aufderhar.name/odie,,80.39.15.222,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n1967,1,44,2017-02-02 00:25:27,http://mante.io/saul,,219.200.251.254,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n1968,1,44,2017-01-10 20:35:20,http://murphy.co/christina,,7.2.37.203,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n1969,1,44,2016-12-26 21:37:05,http://will.name/vergie,,220.98.114.218,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n1970,1,44,2016-12-28 06:18:17,http://kovacekgaylord.co/violette,,215.89.149.198,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n1971,1,44,2017-03-11 12:05:41,http://beatty.net/yesenia,,234.40.19.160,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n1972,1,44,2017-04-19 18:28:57,http://mckenziefadel.name/coby,,145.94.166.181,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n1973,1,44,2017-01-21 11:32:30,http://wisozkcrooks.io/julien_corkery,,252.175.221.18,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n22992,8,516,2017-05-15 19:37:28,http://conroy.io/serena.steuber,,20.50.39.25,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n22993,8,516,2017-01-22 07:58:28,http://hodkiewicz.org/rey_cole,,173.139.113.126,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n22994,8,516,2016-12-24 23:25:31,http://hudsonhartmann.io/lemuel.stehr,,193.238.143.68,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n22995,8,516,2017-03-18 06:55:11,http://weinat.info/bonita,,7.222.172.223,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n22996,8,516,2017-05-18 00:02:32,http://grant.com/lila,,210.178.231.66,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n22997,8,516,2017-01-19 07:27:40,http://monahan.net/shemar,,56.64.142.138,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n22998,8,516,2017-02-20 15:19:56,http://considine.io/tevin,,149.150.242.45,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n22999,8,516,2016-12-20 00:09:34,http://mosciski.biz/rosella_littel,,200.96.180.237,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n23000,8,516,2017-05-12 14:55:17,http://smith.biz/marcella,,185.146.59.101,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n23001,8,516,2017-05-17 00:27:18,http://swiftdicki.com/aiden_west,,145.46.40.193,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n23002,8,516,2017-03-21 04:30:37,http://kuhlman.name/madaline,,254.156.221.77,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n23003,8,516,2017-03-13 12:39:43,http://deckowsanford.name/toney,,27.226.114.134,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n23004,8,516,2017-05-02 06:40:16,http://reichel.com/deonte.swaniawski,,219.157.156.28,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n23005,8,516,2017-01-27 19:02:40,http://fay.biz/johnson,,234.134.175.231,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n23006,8,516,2017-04-30 06:20:43,http://gerhold.info/donny,,200.123.146.113,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n23007,8,516,2017-03-31 02:57:11,http://roob.net/alexie,,98.144.154.144,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n23008,8,516,2017-03-25 05:43:38,http://bartell.name/edwin_greenfelder,,235.16.232.71,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n23009,8,516,2017-01-20 01:11:09,http://mayer.io/jamaal,,157.110.245.225,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n23010,8,516,2017-02-14 22:05:23,http://crist.name/sandrine_trantow,,26.176.138.128,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n23011,8,516,2016-12-27 16:49:04,http://rau.name/harold.hettinger,,199.170.54.250,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n23012,8,516,2017-05-25 17:16:20,http://kilbacklang.co/pearline,,252.38.213.230,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n23013,8,516,2017-04-26 01:09:52,http://rauframi.info/river_upton,,126.178.138.159,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n23014,8,516,2017-02-02 18:08:16,http://bahringer.biz/luis,,189.208.194.177,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n23015,8,516,2017-05-26 08:45:05,http://reynolds.info/isobel,,39.40.37.124,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n23016,8,516,2017-03-24 19:08:25,http://predovic.com/rachel_stamm,,135.240.208.22,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n23017,8,516,2017-04-11 19:51:03,http://mcdermottwill.biz/caleb_mccullough,,140.127.197.14,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n23019,8,516,2017-04-23 20:14:10,http://casper.co/pinkie,,251.126.92.153,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n23020,8,516,2017-02-23 18:03:54,http://wildermanrosenbaum.co/effie,,62.2.141.199,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n23021,8,516,2017-03-08 02:07:17,http://ullrich.net/beryl.bergstrom,,33.246.121.161,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n23022,8,516,2017-06-07 16:13:47,http://kozey.co/maurine_shields,,237.178.141.13,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n23023,8,516,2017-04-09 18:08:11,http://volkman.name/sherman_macejkovic,,182.57.226.21,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n23024,8,516,2016-12-23 12:03:17,http://monahan.io/mafalda,,250.71.64.42,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n23025,8,516,2017-02-12 23:00:41,http://steuberchristiansen.biz/jordy,,144.158.34.52,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n23026,8,516,2016-12-14 10:01:59,http://franecki.co/sadie.leannon,,28.101.114.173,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n23027,8,516,2016-12-16 13:52:07,http://champlinmraz.io/harley_simonis,,68.60.182.243,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n23028,8,517,2017-02-14 13:16:30,http://koelpinsipes.biz/geovanni.eichmann,,252.175.214.144,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n23029,8,517,2017-02-15 19:19:11,http://wehnerbogan.co/louie,,141.173.69.59,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n23030,8,517,2017-02-04 00:45:02,http://oreilly.com/turner,,51.176.238.12,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n23031,8,517,2017-03-07 18:52:58,http://reingermcglynn.info/rickie_mayer,,69.84.36.156,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n23032,8,517,2017-05-14 01:19:24,http://lind.info/hank.sanford,,252.199.25.201,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n23033,8,517,2017-03-25 12:21:28,http://lynch.co/lonzo,,146.224.87.36,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n23034,8,517,2017-03-15 23:20:14,http://cormierweber.co/zoey,,170.60.105.205,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n23035,8,517,2017-05-22 16:30:33,http://christiansencollier.com/rahul.terry,,202.128.107.213,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n23036,8,517,2017-04-28 04:12:50,http://fay.org/alf,,218.177.246.177,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n23037,8,517,2017-05-08 23:28:23,http://okeefe.biz/maxine_keebler,,21.137.234.226,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n23038,8,517,2016-12-27 20:08:48,http://gusikowski.biz/tamara.deckow,,73.163.60.198,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n23039,8,517,2017-01-28 13:40:36,http://weimannhudson.com/jeromy,,141.116.47.59,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n23040,8,517,2017-03-24 20:09:10,http://kihn.net/estelle.kuhn,,6.132.64.231,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n23041,8,517,2017-05-10 01:06:28,http://schuster.name/jacques.bosco,,162.134.106.85,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n23042,8,517,2017-04-09 21:37:37,http://runteoconnell.info/beryl,,160.146.224.207,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n23043,8,517,2017-03-24 19:08:24,http://green.name/darian,,177.87.36.75,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n23044,8,517,2017-03-17 13:45:46,http://feest.org/lucile_hilpert,,39.114.39.139,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n23045,8,517,2017-01-10 00:15:03,http://white.org/billy,,149.7.54.5,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n23046,8,517,2017-04-30 02:48:39,http://littel.net/aleen,,196.44.135.226,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n23047,8,517,2017-03-11 06:12:02,http://gleichnerjerde.com/haley,,240.141.36.245,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n23048,8,517,2017-02-08 10:07:21,http://hilll.org/madaline.kling,,206.33.87.196,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n1974,1,44,2017-01-27 01:52:14,http://faybosco.name/leonardo.schoen,,131.64.9.245,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n1975,1,44,2017-03-23 22:47:35,http://hilpert.name/estelle_lakin,,250.218.161.86,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n1976,1,44,2017-02-10 11:32:17,http://larkin.net/francesco,,59.170.76.193,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n1977,1,44,2017-01-17 15:22:56,http://beerwehner.biz/maybell.spinka,,91.118.140.162,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n1978,1,44,2017-01-16 01:51:28,http://reichertlubowitz.org/mckayla,,150.180.98.209,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n1979,1,44,2017-05-18 22:22:44,http://pfannerstill.co/agustina,,129.117.3.120,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n1980,1,44,2017-05-25 23:29:14,http://hane.io/melvin,,76.188.74.31,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n1981,1,44,2017-05-28 13:21:38,http://balistrerimayer.info/lowell_abbott,,27.76.138.248,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n1982,1,44,2017-03-19 06:16:46,http://quitzon.co/trevion,,179.232.32.244,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n1983,1,44,2017-02-13 18:44:04,http://framigibson.name/jamil,,179.172.206.46,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n1984,1,44,2017-04-08 23:52:16,http://tromp.org/noelia_wolff,,21.26.53.45,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n1985,1,44,2017-06-13 06:27:28,http://hane.com/ahmad,,232.177.200.115,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n1986,1,44,2017-05-07 10:06:11,http://bergnaumbaumbach.co/haylee.toy,,13.29.123.63,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n1987,1,44,2017-03-26 14:57:28,http://maggiomosciski.com/itzel_glover,,183.245.173.220,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n1988,1,44,2017-04-14 03:53:13,http://johns.info/martin,,244.240.228.67,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n1989,1,44,2017-05-29 04:13:16,http://harberframi.com/brock.hills,,225.117.26.112,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n1990,1,44,2017-05-16 13:02:59,http://crona.org/ada_schowalter,,72.166.120.241,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n1991,1,44,2017-01-20 03:11:43,http://kubhauck.io/reina,,229.28.139.242,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n1992,1,44,2017-03-03 09:59:33,http://harber.net/brock.kutch,,160.200.78.19,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n1993,1,44,2017-04-19 16:16:56,http://towneemard.net/faustino_grant,,7.59.32.185,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n1994,1,44,2017-03-26 13:58:13,http://millerkozey.io/justyn,,210.235.156.78,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n1995,1,44,2017-03-18 02:07:18,http://schneider.info/susie,,110.41.200.20,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n1996,1,44,2017-04-16 03:59:09,http://wilkinson.com/mortimer,,33.52.130.118,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n1997,1,44,2017-01-26 23:54:36,http://schimmelschimmel.com/bonita,,122.119.37.198,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n1998,1,44,2016-12-23 17:29:58,http://purdybeier.io/daryl_hintz,,242.169.31.198,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n1999,1,45,2017-05-15 05:32:53,http://koch.io/ebony,,168.35.49.194,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n2000,1,45,2017-04-07 08:24:14,http://mclaughlin.biz/connor_mertz,,50.242.196.128,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n2001,1,45,2017-03-25 07:49:28,http://gerhold.net/domenic,,67.156.123.73,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n2002,1,45,2017-05-02 20:24:14,http://hegmannkohler.name/john.kautzer,,187.22.126.51,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n2003,1,45,2017-03-16 19:56:26,http://watsica.org/burnice_cole,,147.41.156.123,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n2004,1,45,2017-03-22 09:03:37,http://douglas.co/lupe_yundt,,15.89.103.157,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n2005,1,45,2017-05-28 19:11:05,http://ricechristiansen.org/alvis_bednar,,111.159.71.184,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n2006,1,45,2017-04-11 07:24:05,http://walsh.name/chandler_pagac,,15.49.128.21,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n2007,1,45,2017-04-12 08:01:15,http://prosacco.io/torrey,,134.65.51.101,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n2008,1,45,2017-04-23 23:36:23,http://armstrong.info/johnson_auer,,80.96.163.145,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n2009,1,45,2017-01-06 16:23:13,http://pollich.com/norbert,,27.85.215.23,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n2010,1,45,2017-03-17 14:42:17,http://schambergerreilly.io/zachary,,205.125.77.136,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n2011,1,45,2017-03-15 20:40:24,http://legroscorwin.net/fleta,,163.16.118.211,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n2012,1,45,2017-01-08 00:26:13,http://quigley.info/joaquin,,22.4.216.11,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n2013,1,45,2017-03-02 07:18:48,http://cain.io/gilda,,28.251.174.219,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n2014,1,45,2017-02-26 00:25:28,http://keebler.co/maximo.flatley,,63.161.204.198,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n2015,1,45,2017-03-06 00:40:27,http://breitenberg.net/miguel_fay,,152.107.172.120,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n2016,1,45,2017-04-06 10:08:12,http://dickinsonkreiger.name/emilia,,220.205.192.6,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n2017,1,45,2017-01-30 00:29:06,http://pfannerstillrippin.name/grover.smitham,,72.155.240.217,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n2018,1,45,2017-01-03 10:57:16,http://kozeydenesik.net/jerry.hermann,,147.4.16.90,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n2019,1,45,2017-01-09 13:23:54,http://ruelaltenwerth.co/silas,,163.31.170.30,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n2021,1,45,2017-05-27 09:15:40,http://heidenreich.com/curtis,,52.80.152.139,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n2022,1,45,2017-04-28 10:20:52,http://harvey.com/garrett_boyle,,58.12.41.87,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n2023,1,45,2017-03-17 06:13:22,http://schuppe.co/scottie,,98.156.168.195,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n2024,1,45,2017-05-14 13:01:23,http://johns.org/raegan,,164.241.75.15,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n2025,1,45,2017-02-19 17:28:37,http://gottlieb.io/susie,,217.70.183.95,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n2026,1,45,2017-01-29 20:26:02,http://oreilly.io/janae_welch,,39.23.44.142,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n2027,1,45,2017-01-28 10:10:53,http://white.name/jeramie.will,,94.41.7.28,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n2028,1,45,2017-02-28 07:53:02,http://hyatt.com/carleton_ritchie,,111.192.110.72,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n2029,1,45,2017-03-10 13:30:11,http://lebsack.name/henri,,145.254.109.159,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n2030,1,45,2017-05-23 07:21:04,http://johnsonbechtelar.io/annamarie.jenkins,,47.37.132.204,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n23049,8,517,2017-02-27 20:30:43,http://mccullough.org/may.lehner,,72.219.131.115,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n23050,8,517,2017-01-06 09:47:19,http://sawayn.net/louie_heel,,50.35.154.93,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n23051,8,517,2017-02-21 05:21:17,http://stanton.io/ivy.maggio,,170.196.100.105,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n23052,8,517,2017-03-31 20:21:13,http://tremblay.net/lila,,201.159.32.108,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n23053,8,517,2017-05-25 03:29:07,http://ferry.info/alexandria,,45.167.163.254,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n23054,8,517,2017-04-28 15:32:26,http://haag.io/corine.kulas,,109.217.108.14,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n23055,8,517,2017-04-28 08:23:41,http://wizakunze.name/dorian_hudson,,205.8.163.70,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n23056,8,517,2017-01-26 18:15:36,http://little.io/aurelie_kulas,,73.207.190.109,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n23057,8,517,2017-01-13 18:45:30,http://collier.io/margarett,,243.60.167.132,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n23058,8,517,2016-12-30 07:32:08,http://vandervortweimann.biz/jazmyne,,139.58.140.142,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n23059,8,517,2017-01-28 06:17:16,http://rempelsanford.co/lorenz,,151.170.219.5,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n23060,8,517,2017-02-25 18:34:19,http://macgyver.co/wilfrid.jacobi,,222.75.42.163,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n23061,8,517,2016-12-31 10:19:10,http://moen.io/macie_hagenes,,204.252.122.219,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n23062,8,517,2017-03-29 19:24:04,http://gutkowski.com/etha.bauch,,154.196.142.87,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n23063,8,517,2017-02-07 22:06:19,http://ritchiecarter.info/alejandrin,,53.240.156.251,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n23064,8,517,2017-01-08 14:05:20,http://von.co/eli,,163.72.158.20,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n23065,8,517,2016-12-18 01:09:54,http://kunze.com/maryam.romaguera,,3.242.233.40,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n23066,8,517,2017-06-02 16:09:58,http://blick.com/lorenzo,,218.113.161.40,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n23067,8,517,2017-04-19 15:32:34,http://boyerhilpert.name/kaylee.miller,,221.227.14.213,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n23068,8,517,2017-03-17 18:00:43,http://stromanstamm.biz/meaghan_ferry,,249.20.199.202,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n23069,8,517,2017-02-25 22:36:12,http://kuvalis.co/shaun_parker,,57.78.168.108,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n23070,8,517,2017-01-03 10:22:52,http://homenick.net/rachel_hauck,,203.108.200.214,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n23071,8,517,2016-12-30 08:30:58,http://beckerhackett.info/ofelia,,154.17.208.27,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n23072,8,517,2017-02-06 01:47:34,http://wuckert.org/max_cole,,27.190.37.96,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n23073,8,517,2017-04-20 10:32:28,http://ondricka.com/danial,,37.17.238.179,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n23074,8,517,2017-04-01 21:59:06,http://barrowscronin.biz/lucile.frami,,30.125.90.45,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n23075,8,517,2017-04-08 17:16:05,http://dubuque.net/darren,,104.226.24.26,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n23076,8,517,2017-04-25 04:57:36,http://hodkiewicz.info/adolph,,147.192.15.54,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n23077,8,517,2017-03-20 19:31:06,http://hodkiewiczkaulke.net/jacynthe.gibson,,93.119.23.8,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n23078,8,517,2016-12-19 01:19:11,http://abshirejacobs.org/dexter_hintz,,34.195.129.101,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n23079,8,517,2017-01-04 23:25:20,http://gottlieb.name/anabelle.denesik,,57.152.64.49,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n23080,8,517,2017-05-13 19:34:20,http://okon.io/rosie_emmerich,,147.194.225.190,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n23081,8,517,2017-02-04 14:57:47,http://greendickinson.io/jade,,242.42.100.132,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n23082,8,517,2017-05-02 23:20:29,http://stehrhayes.com/thaddeus,,76.129.41.252,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n23083,8,517,2017-03-15 09:08:03,http://zboncakhauck.io/horacio.rolfson,,49.248.197.209,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n23084,8,517,2017-02-24 02:55:10,http://prosacco.com/rosie.kihn,,48.86.132.153,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n23085,8,517,2017-05-08 00:01:56,http://herman.biz/fannie_reichert,,52.239.105.222,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n23086,8,517,2017-04-06 04:10:32,http://predovic.io/orlo_abshire,,198.186.181.74,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n23087,8,517,2017-05-06 18:51:10,http://mohr.biz/cruz,,87.142.212.85,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n23088,8,517,2016-12-25 14:57:20,http://stroman.biz/oda,,220.83.176.126,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n23089,8,517,2017-03-25 09:51:18,http://ferry.info/skye_kovacek,,238.203.62.222,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n23090,8,517,2017-05-11 01:44:23,http://hartmann.net/kenna,,30.204.31.207,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n23091,8,517,2016-12-23 12:32:19,http://harvey.io/modesta,,108.239.91.107,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n23092,8,517,2017-04-05 19:38:46,http://heathcote.net/green.schultz,,253.47.7.25,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n23093,8,517,2017-05-05 01:33:21,http://cronin.biz/imani,,127.232.122.71,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n23094,8,517,2017-02-22 21:30:45,http://reillyrosenbaum.com/isidro.legros,,156.216.219.110,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n23095,8,517,2017-02-21 18:47:14,http://wisoky.io/rosemary.ferry,,158.215.167.88,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n23096,8,518,2017-05-04 02:12:47,http://bergnaumkuvalis.info/grady_mcglynn,,172.117.220.99,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n23097,8,518,2017-04-28 10:24:08,http://kleinjohnston.info/kaylah,,56.203.118.231,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n23098,8,518,2017-02-09 21:26:54,http://larsonrunolfsdottir.info/curt,,148.223.201.124,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n23099,8,518,2017-04-13 16:56:49,http://powlowski.com/dillan_zemlak,,18.154.163.112,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n23100,8,518,2017-01-07 01:27:37,http://luettgen.net/elna.bogisich,,165.10.204.214,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n23101,8,518,2017-06-09 08:14:55,http://ortiz.info/alice_raynor,,185.130.74.92,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n23102,8,518,2017-04-15 11:53:13,http://hartmann.info/johann,,189.66.110.233,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n23103,8,518,2017-03-22 07:48:59,http://hintz.com/miouri,,25.135.139.35,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n2031,1,45,2016-12-15 23:12:13,http://dickidibbert.com/coleman,,29.137.83.69,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n2032,1,45,2016-12-15 12:30:40,http://deckow.org/damon,,229.48.127.178,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n2033,1,45,2017-01-30 06:06:04,http://vandervort.org/roberto_macejkovic,,73.12.79.101,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n2034,1,45,2017-02-04 14:21:57,http://braun.name/forest_zemlak,,232.222.230.31,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n2035,1,46,2016-12-16 08:54:04,http://walsh.net/annamarie.fay,,36.241.75.95,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n2036,1,46,2017-06-11 18:42:24,http://homenickhodkiewicz.info/olin.konopelski,,16.47.209.64,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n2037,1,46,2017-01-31 17:55:52,http://haley.name/terrill.lemke,,53.125.188.61,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n2038,1,46,2017-05-01 10:42:01,http://vandervortstreich.co/raegan.barton,,82.245.95.132,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n2039,1,46,2017-01-31 10:20:08,http://walter.org/rosendo.koepp,,169.253.237.198,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n2040,1,46,2016-12-30 15:00:04,http://quigley.co/nadia_sauer,,254.209.239.47,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n2041,1,46,2017-02-01 12:27:30,http://satterfield.biz/jaleel.kreiger,,225.249.166.47,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n2042,1,46,2016-12-20 23:36:14,http://friesen.co/arvel_mclaughlin,,32.186.8.184,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n2043,1,46,2017-02-26 17:48:15,http://gutkowskirice.io/shaniya,,77.158.221.104,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n2044,1,46,2017-03-31 16:17:46,http://ullrich.io/donny_ohara,,28.225.210.94,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n2045,1,46,2017-03-09 02:38:21,http://raynorbartell.com/anthony,,217.70.215.91,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n2046,1,46,2017-04-21 19:51:16,http://buckridge.com/anabelle_bergnaum,,22.131.41.39,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n2047,1,46,2017-05-10 12:47:13,http://schroeder.name/ari,,235.49.207.188,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n2048,1,46,2016-12-27 14:40:08,http://dareabshire.name/robert,,199.183.199.166,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n2049,1,46,2017-02-03 08:43:57,http://osinski.org/luisa.bode,,108.102.64.78,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n2050,1,46,2017-03-07 20:05:21,http://smitham.io/malika,,2.239.41.138,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n2051,1,46,2017-04-16 12:47:39,http://cummings.net/enoch.oreilly,,97.31.4.88,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n2052,1,46,2017-04-08 07:31:02,http://dickens.name/brady_schumm,,239.21.93.52,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n2053,1,46,2017-03-18 16:55:09,http://smitham.net/pablo,,41.140.108.64,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n2054,1,46,2017-01-10 16:03:42,http://fayhilpert.net/rusty.morar,,72.233.9.17,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n2055,1,46,2017-02-02 02:24:44,http://gerlach.biz/kaitlin,,110.56.99.166,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n2056,1,46,2017-03-24 05:58:51,http://vonruedengoyette.info/nathaniel,,45.89.180.220,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n2057,1,46,2017-02-20 21:20:45,http://nicolas.co/ricky_nikolaus,,149.14.39.157,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n2058,1,46,2017-05-20 06:38:46,http://rosenbaum.name/abraham,,71.99.48.98,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n2059,1,46,2017-01-20 17:52:22,http://altenwerthgreenfelder.io/garrett,,149.187.205.167,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n2060,1,46,2017-03-09 10:17:55,http://homenickward.name/arvilla,,137.158.20.217,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n2061,1,46,2017-05-17 03:19:56,http://marksdoyle.org/pierre_will,,142.64.7.186,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n2062,1,46,2017-01-19 18:08:37,http://haag.biz/frida_kilback,,239.190.41.136,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n2063,1,46,2017-01-18 19:10:19,http://kris.biz/ken.leffler,,101.45.9.175,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n2064,1,46,2017-04-27 14:26:41,http://nicolas.name/velma,,115.178.90.142,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n2065,1,46,2016-12-28 22:31:00,http://conn.net/vivianne,,61.71.217.229,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n2066,1,46,2017-02-06 20:03:17,http://goodwin.com/mohamed_lowe,,186.180.76.152,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n2067,1,46,2017-03-29 13:16:24,http://skilesbergstrom.co/casimir,,253.73.27.194,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n2068,1,46,2016-12-20 20:18:07,http://hartmann.co/bette.mclaughlin,,244.105.239.47,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n2069,1,46,2017-04-24 00:15:38,http://weimannshields.com/megane.grimes,,47.79.126.156,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n2070,1,46,2017-03-09 15:44:20,http://hoppestark.io/ozzie,,222.167.152.44,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n2071,1,46,2017-04-02 20:55:37,http://smitham.org/sienna_kling,,181.93.223.42,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n2072,1,46,2017-02-23 22:24:04,http://harris.info/trinity,,13.73.92.22,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n2073,1,46,2016-12-23 23:58:59,http://jenkins.name/braxton.tillman,,217.169.84.77,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n2074,1,46,2017-01-01 07:09:13,http://wiegandlockman.name/juliet,,22.247.15.145,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n2075,1,46,2017-03-22 23:36:03,http://bradtke.com/bradly.beer,,26.204.77.123,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n2076,1,46,2017-02-16 11:59:16,http://ratke.info/adonis.carter,,102.203.230.178,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n2077,1,46,2017-03-25 12:17:14,http://spencer.com/william,,89.202.107.154,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n2078,1,46,2017-05-03 01:01:34,http://jastpaucek.io/myrtle.kemmer,,169.197.38.244,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n2079,1,46,2017-03-11 02:39:48,http://lockman.net/emile,,117.21.111.67,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n2080,1,46,2017-02-17 18:06:11,http://hudson.biz/aiyana,,121.222.127.94,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n2081,1,46,2017-02-26 10:58:09,http://sipes.org/laney,,144.32.110.162,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n2082,1,46,2017-02-01 06:58:17,http://gradyherzog.com/toney,,108.51.80.7,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n2083,1,46,2017-02-20 22:31:29,http://littel.co/talon_hirthe,,107.116.246.23,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n2084,1,46,2017-06-11 06:56:28,http://lubowitz.com/elza,,242.36.59.99,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n2085,1,46,2017-02-12 04:56:44,http://kreiger.info/agustin.murazik,,109.14.140.39,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n2086,1,46,2017-05-21 15:13:47,http://buckridgerempel.org/orland,,68.187.177.175,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n23104,8,518,2017-03-05 18:40:45,http://greenholt.biz/terrill,,133.93.192.241,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n23105,8,518,2017-04-15 03:06:54,http://nienow.biz/emmett,,52.137.228.85,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n23106,8,518,2017-04-25 07:35:01,http://labadie.info/monte,,2.102.108.125,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n23107,8,518,2017-02-01 02:06:21,http://weinat.io/willa,,186.3.78.70,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n23108,8,518,2017-05-10 04:27:07,http://labadie.org/andres.konopelski,,108.93.183.185,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n23109,8,518,2016-12-26 14:38:46,http://herman.com/whitney,,220.31.147.9,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n23110,8,518,2017-06-04 19:50:25,http://gislason.biz/stanton.kshlerin,,224.248.44.79,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n23111,8,518,2017-02-05 18:28:36,http://weinat.info/waldo.rodriguez,,93.234.90.62,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n23112,8,518,2017-05-11 18:03:45,http://kaulkekeeling.net/brett_ernser,,170.165.20.17,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n23113,8,518,2017-02-07 12:59:00,http://tremblaymills.io/providenci,,28.75.64.118,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n23114,8,518,2017-02-10 21:03:18,http://botsfordprohaska.info/cleora,,143.43.238.43,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n23115,8,518,2017-03-14 19:47:28,http://turnerfisher.org/kaandra,,94.227.207.165,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n23116,8,518,2017-05-03 23:21:41,http://feil.biz/dayna_mohr,,183.124.96.242,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n23117,8,518,2017-01-05 15:15:03,http://christiansenwelch.net/kade.larkin,,134.228.29.134,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n23118,8,518,2016-12-27 09:19:13,http://parker.biz/maye,,109.187.66.58,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n23119,8,518,2017-06-12 19:57:35,http://hand.name/conor_raynor,,153.153.111.22,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n23120,8,518,2017-03-16 16:57:56,http://wildermanlittel.net/daija,,180.14.175.133,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n23121,8,518,2017-06-06 23:07:57,http://walternikolaus.com/zelda,,56.251.219.82,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n23122,8,518,2017-05-16 10:23:48,http://hettingergoodwin.info/napoleon,,16.213.51.116,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n23123,8,518,2016-12-27 16:31:43,http://bartoletti.io/kaela.schmitt,,66.23.234.68,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n23124,8,518,2017-05-02 19:19:25,http://bahringer.org/walker,,61.28.148.247,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n23125,8,518,2017-04-18 01:03:46,http://rippinrice.name/lamont.oconner,,102.197.6.91,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n23126,8,518,2017-04-10 07:24:05,http://dachokon.co/rico.kaulke,,39.114.227.186,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n23127,8,518,2017-02-04 02:55:12,http://glovercorkery.io/viola,,117.221.173.205,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n23128,8,518,2017-03-08 13:59:50,http://kiehn.com/milton.schaefer,,50.234.203.127,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n23129,8,518,2016-12-29 05:15:46,http://reilly.info/kara,,205.93.229.162,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n23130,8,518,2017-01-23 04:20:23,http://stiedemann.biz/bridget,,207.16.222.156,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n23131,8,518,2016-12-20 01:46:27,http://crona.name/darlene,,200.251.208.217,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n23132,8,518,2017-05-03 13:56:27,http://schoen.info/sunny_cain,,135.167.61.142,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n23133,8,518,2017-03-19 04:29:16,http://spinkahowell.info/ansley,,84.39.80.227,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n23134,8,518,2017-02-02 11:40:56,http://okon.com/fletcher,,54.23.217.181,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n23135,8,518,2017-01-26 02:54:55,http://vandervort.com/gavin,,84.53.243.112,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n23136,8,518,2017-02-17 04:05:32,http://schmidt.biz/kaylie.larkin,,225.238.38.163,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n23137,8,518,2017-02-28 06:42:54,http://eichmann.info/claire,,234.61.111.170,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n23138,8,518,2017-01-15 02:01:58,http://leuschke.net/citlalli.legros,,82.207.15.65,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n23139,8,518,2017-06-13 17:14:00,http://wolff.org/margarett.altenwerth,,114.130.239.128,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n23140,8,518,2017-04-08 02:15:44,http://conroy.io/moses.strosin,,135.203.174.108,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n23141,8,518,2017-04-23 02:32:48,http://stehr.info/royce,,22.98.94.58,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n23142,8,518,2017-03-17 19:49:28,http://abernathy.biz/clare,,74.127.145.24,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n23143,8,518,2017-05-04 02:43:17,http://hintzlittel.net/gielle,,127.238.61.15,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n23144,8,518,2017-01-26 11:19:52,http://fahey.info/katarina,,16.51.137.49,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n23145,8,518,2017-03-28 05:49:14,http://macgyverframi.com/eugenia.von,,103.215.78.137,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n23146,8,518,2017-04-21 08:42:50,http://fadel.info/leopold.sawayn,,189.45.213.142,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n23147,8,518,2017-05-02 00:32:26,http://friesen.net/libby.hayes,,102.39.170.37,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n23148,8,519,2017-05-06 18:13:38,http://caspermedhurst.name/evert.sanford,,208.179.61.210,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n23149,8,519,2017-02-26 16:24:17,http://hyatt.name/caandra.hahn,,88.189.114.114,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n23150,8,519,2016-12-19 04:12:47,http://hintz.co/kurt,,212.119.22.175,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n23151,8,519,2017-03-02 14:16:48,http://gradytillman.org/violette_robel,,73.161.118.180,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n23152,8,519,2017-01-04 06:53:05,http://reichel.net/amari_johns,,238.123.47.53,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n23153,8,519,2017-02-25 13:10:21,http://hintz.info/juliana.gutkowski,,61.250.147.53,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n23154,8,519,2017-02-12 06:07:05,http://wymanjohnson.biz/judson,,122.77.78.119,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n23155,8,519,2017-04-25 20:53:39,http://schusterkub.name/angel_shields,,127.197.105.202,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n23156,8,519,2017-01-31 14:44:45,http://hickle.name/dorcas,,11.168.219.220,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n23157,8,519,2017-05-21 07:45:38,http://armstrongbogisich.io/guie.king,,241.72.246.112,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n23158,8,519,2017-01-13 11:12:52,http://denesik.org/holden.koelpin,,216.80.107.160,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n2087,1,46,2017-04-03 13:58:11,http://shieldsquigley.biz/porter,,27.66.96.39,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n2088,1,46,2017-02-05 19:13:22,http://ricegrimes.info/fannie,,85.65.152.97,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n2089,1,46,2017-04-11 11:32:52,http://tromp.info/leilani,,174.60.119.30,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n2090,1,46,2017-05-06 00:14:19,http://bernierfarrell.io/letha_walsh,,216.192.109.140,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n2091,1,46,2017-02-20 17:59:24,http://hauckluettgen.co/lavonne,,97.144.75.108,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n2092,1,46,2017-04-16 18:29:34,http://windler.io/julio,,63.46.160.112,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n2093,1,46,2017-01-13 04:59:37,http://stamm.name/tanya,,235.253.194.103,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n2094,1,46,2017-01-19 05:04:53,http://tremblay.info/augusta,,45.4.23.241,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n2095,1,46,2017-02-26 10:34:51,http://wildermanleannon.com/karley,,76.192.137.63,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n2096,1,46,2016-12-16 22:08:19,http://lowe.org/jules_gulgowski,,196.76.254.201,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n2097,1,46,2017-03-05 08:17:51,http://johnston.io/althea_reinger,,34.118.4.28,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n2098,1,46,2017-02-20 04:24:30,http://haag.info/adam,,245.143.218.131,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n2099,1,46,2017-05-15 07:10:57,http://farrell.biz/ona_herman,,215.222.243.242,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n2100,1,46,2016-12-18 16:32:08,http://gradyhackett.co/sabryna,,208.179.239.61,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n2101,1,46,2017-03-10 10:04:02,http://sauermoen.info/lawrence,,175.235.76.151,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n2102,1,46,2017-01-07 12:01:18,http://gerlach.com/colleen,,108.212.161.210,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n2103,1,46,2017-02-22 14:41:36,http://ferryhodkiewicz.info/katheryn,,195.6.90.229,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n2104,1,47,2017-04-12 01:36:42,http://crooks.net/kolby,,211.116.101.159,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n2105,1,47,2017-03-07 00:00:48,http://mcculloughschuppe.info/icie,,223.216.55.230,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n2106,1,47,2017-01-10 18:09:47,http://walter.io/paula,,106.29.11.141,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n2107,1,47,2017-03-06 08:07:29,http://orn.name/marianna,,108.138.171.147,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n2108,1,47,2017-04-12 02:35:44,http://williamsonmacejkovic.io/hayley_ohara,,210.173.9.112,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n2109,1,47,2016-12-20 21:07:25,http://kemmer.net/rhea,,112.173.147.187,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n2110,1,47,2017-03-15 04:42:45,http://oconner.org/herbert,,33.57.85.102,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n2111,1,47,2017-05-25 19:54:12,http://beatty.name/hailie,,139.236.34.83,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n2112,1,47,2017-05-05 23:38:54,http://batz.info/rhiannon.wyman,,107.60.236.134,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n2113,1,47,2017-02-10 07:03:02,http://bergnaum.co/belle,,111.158.82.102,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n2114,1,47,2017-04-26 06:25:29,http://harristowne.biz/myah,,203.226.193.39,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n2115,1,47,2017-05-06 17:30:11,http://torp.biz/leann,,9.180.156.17,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n2116,1,47,2016-12-13 09:10:10,http://barrows.biz/delphia.pagac,,242.69.124.236,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n2117,1,47,2017-02-22 15:46:48,http://stehr.name/pierre,,61.26.136.125,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n2118,1,47,2017-05-27 14:30:54,http://berge.org/shanel.rolfson,,210.80.4.33,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n2119,1,47,2017-03-23 21:24:59,http://beier.co/brice,,151.239.191.6,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n2120,1,47,2017-04-14 14:07:02,http://dubuquewindler.io/ali.lockman,,119.136.117.172,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n2121,1,47,2017-04-22 11:18:47,http://jakubowskifisher.com/jon_erdman,,232.210.207.210,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n2122,1,47,2017-04-10 07:31:50,http://zulauf.name/philip,,45.111.200.250,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n2123,1,47,2017-04-10 01:22:07,http://walker.net/hailie,,32.78.17.74,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n2124,1,47,2017-04-03 12:29:10,http://macejkovic.biz/ewell.mccullough,,91.192.131.103,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n2125,1,47,2017-02-28 11:13:37,http://lakin.net/melisa,,2.65.4.127,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n2126,1,47,2017-05-30 23:03:06,http://bins.info/lupe,,202.141.227.28,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n2127,1,47,2017-04-17 03:25:44,http://schowalter.net/bella_wehner,,43.113.251.128,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n2128,1,47,2016-12-18 08:22:34,http://beertillman.co/kenna,,124.231.85.155,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n2129,1,47,2017-04-27 03:06:49,http://kshlerin.org/sonya,,10.95.109.113,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n2130,1,47,2017-01-30 02:57:25,http://hudsonhahn.name/jalyn,,16.89.50.142,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n2131,1,47,2017-02-25 07:26:52,http://schoen.info/frieda.schowalter,,4.187.140.162,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n2132,1,47,2017-05-31 15:46:18,http://murray.info/garnet_rogahn,,90.6.94.190,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n2133,1,47,2017-04-06 08:39:31,http://paucek.com/johan_moen,,182.54.36.148,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n2134,1,47,2016-12-17 03:38:47,http://graham.co/ashley,,33.83.36.150,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n2135,1,47,2017-04-30 10:06:38,http://rosenbaum.info/kaitlin,,6.29.157.116,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n2137,1,47,2017-04-17 11:55:06,http://stanton.co/milan.harvey,,58.77.6.241,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n2138,1,47,2016-12-21 00:14:10,http://turcottebartell.co/rene,,71.45.221.159,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n2139,1,47,2017-06-04 11:38:49,http://konopelski.org/bud,,123.182.198.128,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n2140,1,48,2017-05-08 00:17:37,http://dooleycarroll.co/lester,,108.20.202.152,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n2141,1,48,2017-02-11 10:57:24,http://bernier.com/lee,,223.71.167.12,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n2142,1,48,2017-03-24 21:33:54,http://simonis.org/chet.towne,,142.248.150.53,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n2143,1,48,2017-05-15 09:53:35,http://greenholt.com/loren.heel,,19.203.46.182,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n23159,8,519,2017-04-23 05:03:31,http://mertzdoyle.org/jovani_ward,,121.95.253.61,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n23160,8,519,2017-03-01 10:14:39,http://hodkiewiczwintheiser.co/terrell_jenkins,,148.88.216.16,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n23161,8,519,2017-04-13 14:08:55,http://cain.org/virginia,,83.85.211.54,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n23162,8,519,2017-04-15 17:47:20,http://mohr.info/hershel_mcclure,,167.222.246.35,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n23163,8,519,2017-01-03 17:36:27,http://davis.net/kane_mann,,229.146.160.40,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n23164,8,519,2017-01-22 14:10:39,http://binswiza.biz/deontae.harris,,253.127.232.38,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n23165,8,519,2016-12-17 18:04:44,http://gerhold.net/alta,,247.151.26.47,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n23166,8,519,2017-01-17 21:38:27,http://deckow.name/mackenzie,,199.92.225.99,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n23167,8,519,2017-06-13 17:32:33,http://weber.co/demetrius,,139.6.4.59,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n23168,8,519,2017-06-01 17:37:51,http://macgyverhoppe.io/viva_balistreri,,104.85.190.132,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n23169,8,519,2017-04-02 00:01:53,http://roweconroy.co/brandyn,,237.177.206.127,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n23170,8,519,2017-03-06 20:48:45,http://pourosko.org/gunnar.donnelly,,108.73.57.254,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n23171,8,520,2017-01-09 13:03:35,http://padberg.biz/brown.maggio,,140.19.214.145,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n23172,8,520,2017-02-13 19:00:19,http://pfeffer.io/waldo,,191.27.144.174,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n23173,8,520,2017-06-10 21:16:30,http://pollichwyman.org/janice_rohan,,160.55.113.28,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n23174,8,520,2017-05-13 13:47:43,http://kerluke.co/margie,,135.140.133.25,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n23175,8,520,2017-03-16 23:10:18,http://flatley.co/lynn,,178.99.85.175,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n23176,8,520,2017-02-22 09:00:12,http://heidenreichrunolfon.org/tevin.goodwin,,224.27.174.248,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n23177,8,520,2017-02-21 12:14:21,http://lindgren.biz/kaylin.haley,,211.253.39.18,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n23178,8,520,2017-03-13 13:54:38,http://sauer.co/jada,,50.253.244.194,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n23179,8,520,2017-02-28 01:39:10,http://reynolds.name/dudley,,252.170.102.79,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n23180,8,520,2017-02-12 21:30:56,http://huels.info/lera.johnston,,56.86.72.163,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n23181,8,520,2017-01-08 02:51:04,http://walsh.biz/kolby,,254.57.60.209,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n23182,8,520,2017-04-10 17:36:53,http://cristwalker.net/berneice_larson,,238.59.122.33,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n23183,8,520,2017-05-13 17:57:33,http://kautzer.name/kenny,,21.64.110.127,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n23184,8,520,2017-04-03 20:29:39,http://kochkoch.info/onie,,189.88.182.39,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n23185,8,520,2017-02-19 07:52:55,http://quitzon.info/reilly,,70.24.33.193,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n23186,8,520,2017-05-02 20:21:24,http://howell.co/vergie,,212.55.138.226,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n23187,8,520,2017-05-19 16:30:07,http://kuhlmanmarquardt.com/ernie_schmidt,,181.246.155.132,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n23188,8,520,2017-04-12 15:40:22,http://zemlakspinka.io/wayne,,4.170.250.119,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n23189,8,520,2017-03-09 11:00:49,http://sauer.net/alicia,,172.237.162.43,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n23190,8,520,2017-05-04 04:45:18,http://davis.info/melody.lind,,130.169.225.5,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n23191,8,520,2017-01-29 17:53:11,http://lesch.name/katarina,,201.235.81.26,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n23192,8,520,2016-12-27 18:25:57,http://mante.net/keon_hintz,,215.19.102.8,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n23193,8,520,2017-04-10 04:06:33,http://bauch.info/mavis,,164.31.6.177,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n23194,8,520,2017-02-21 12:44:50,http://ryanbahringer.net/adolfo,,168.167.90.58,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n23195,8,521,2017-03-27 00:28:45,http://heathcote.net/ivy.paucek,,104.249.227.121,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n23196,8,521,2016-12-29 03:10:45,http://gerlach.io/garett.lakin,,94.35.220.128,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n23197,8,521,2017-04-25 12:36:03,http://botsford.name/seamus,,127.180.148.138,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n23198,8,521,2017-01-09 16:02:01,http://heidenreich.name/tabitha,,167.178.159.254,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n23199,8,521,2017-02-19 23:25:46,http://towne.co/sasha_ernser,,26.46.200.18,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n23200,8,521,2017-03-03 00:38:51,http://schinner.name/janice.lynch,,215.38.174.41,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n23201,8,521,2017-06-09 03:29:42,http://hoeger.biz/elliot,,157.4.157.185,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n23202,8,521,2017-03-30 04:15:04,http://littel.info/adela.tromp,,112.143.156.30,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n23203,8,521,2017-05-09 23:44:39,http://yundt.co/augusta,,94.40.149.237,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n23204,8,521,2017-01-03 07:16:54,http://pfannerstillrohan.net/kirk,,214.135.175.118,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n23205,8,521,2017-05-16 22:18:59,http://vandervortwisozk.com/maeve_lockman,,199.83.16.81,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n23206,8,521,2017-03-11 03:39:30,http://denesikdonnelly.net/curtis,,35.251.37.107,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n23207,8,521,2017-05-21 09:40:12,http://pollichwalsh.net/angie,,150.30.83.102,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n23208,8,521,2017-01-29 02:59:02,http://koch.name/beatrice,,67.121.248.175,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n23209,8,521,2017-06-10 09:18:17,http://murphybreitenberg.name/deontae,,218.240.22.159,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n23210,8,521,2017-04-19 07:22:05,http://stokesreinger.biz/brenda_reilly,,150.112.216.138,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n23211,8,521,2017-06-04 15:56:42,http://barton.net/breanna.dickinson,,66.240.3.109,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n23212,8,521,2016-12-29 00:40:23,http://waelchischmitt.biz/baylee,,254.60.83.210,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n23213,8,521,2016-12-24 13:48:26,http://parker.co/elna,,173.42.196.106,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n2144,1,48,2017-05-04 22:35:41,http://yundt.info/lorenzo,,210.78.2.46,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n2145,1,48,2017-03-10 18:21:47,http://oconner.net/justyn,,39.62.104.234,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n2146,1,48,2017-03-12 04:40:41,http://gutmannspencer.info/federico_dickens,,214.65.2.249,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n2147,1,48,2017-06-08 09:45:40,http://toy.com/janelle.bosco,,153.247.92.146,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n2148,1,48,2017-03-07 06:19:27,http://mueller.info/tiffany.sporer,,69.33.142.106,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n2149,1,48,2017-04-20 11:52:45,http://kuhnwisoky.biz/jedediah,,100.182.156.253,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n2150,1,48,2017-05-09 22:24:54,http://reichel.co/vladimir,,185.216.27.188,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n2151,1,48,2017-03-11 10:15:21,http://sengernolan.biz/wayne,,153.14.123.14,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n2152,1,48,2017-05-06 07:11:15,http://stehr.com/lesly.connelly,,133.39.231.42,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n2153,1,48,2017-04-18 21:42:28,http://olsonterry.info/roger_swaniawski,,241.5.202.237,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n2154,1,48,2017-02-27 04:09:44,http://terry.com/keenan_berge,,27.206.37.129,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n2155,1,48,2016-12-22 18:24:33,http://satterfield.com/einar.runolfon,,189.26.134.53,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n2156,1,48,2017-04-10 17:21:32,http://abbott.co/myles,,15.38.141.242,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n2157,1,48,2017-02-05 08:18:32,http://aufderhar.org/lamont.mayert,,243.147.79.178,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n2158,1,48,2017-05-15 13:04:23,http://schmidtko.com/pietro,,175.132.147.49,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n2159,1,48,2017-05-27 16:24:32,http://schulistcorkery.co/darron.lakin,,195.142.199.210,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n2160,1,48,2017-03-24 19:25:27,http://sipesquitzon.net/agustina_nader,,148.13.88.206,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n2161,1,48,2017-03-02 03:08:15,http://stokes.info/keaton_jones,,162.247.71.193,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n2162,1,48,2017-02-10 06:27:56,http://douglas.co/hilton.paucek,,85.28.100.120,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n2163,1,48,2017-03-19 20:50:18,http://haleyschroeder.co/kirsten,,44.148.211.113,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n2164,1,48,2016-12-23 13:26:11,http://haag.name/soledad_lemke,,2.188.128.203,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n2165,1,48,2017-06-02 22:14:52,http://wymancollins.io/taurean.stoltenberg,,178.146.96.253,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n2166,1,48,2017-02-02 21:31:17,http://schroeder.co/nicole_lowe,,228.141.57.79,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n2167,1,48,2017-04-26 16:51:18,http://dooley.net/ottis_spencer,,135.253.202.163,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n2168,1,48,2017-02-04 09:06:47,http://will.name/elenora,,226.215.110.65,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n2169,1,48,2017-04-27 10:49:22,http://lehner.biz/bella_bosco,,50.64.10.129,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n2170,1,48,2017-02-07 07:13:17,http://thompson.biz/oleta_olson,,172.74.244.32,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n2171,1,48,2017-06-13 14:13:14,http://schmitt.com/sofia.hauck,,65.220.179.31,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n2172,1,48,2017-05-28 02:08:19,http://rippinsimonis.io/coty,,167.120.190.240,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n2173,1,49,2017-01-24 12:32:31,http://halvorsonohara.net/nya,,65.206.201.211,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n2174,1,49,2017-02-19 03:07:20,http://roobbernhard.com/lew,,75.98.107.48,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n2175,1,49,2017-01-05 03:04:16,http://stiedemann.io/grover_mann,,236.91.112.202,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n2176,1,49,2017-05-13 22:46:42,http://kozey.org/jaqueline_berge,,217.173.252.118,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n2177,1,49,2017-03-01 08:07:30,http://cummings.io/candace,,31.224.5.50,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n2178,1,49,2017-04-24 15:27:06,http://mullerromaguera.info/lauryn,,79.51.94.198,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n2179,1,49,2017-03-13 09:15:15,http://bernier.io/river.weinat,,84.27.149.171,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n2180,1,49,2017-03-05 23:48:38,http://ferry.co/franco.krajcik,,161.128.115.231,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n2181,1,49,2017-01-21 04:04:50,http://schoenlindgren.info/anjali.grady,,5.56.3.132,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n2182,1,49,2017-04-18 09:04:17,http://wehner.biz/abbey,,131.133.65.142,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n2183,1,49,2017-03-09 00:53:20,http://price.org/rosalia,,237.73.4.189,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n2184,1,49,2017-03-02 02:59:16,http://walsh.biz/norwood.wiza,,19.130.237.13,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n2185,1,49,2017-05-29 18:48:57,http://mullerdickinson.net/audra.kuhn,,119.177.103.227,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n2186,1,49,2017-04-08 01:02:08,http://halvorson.net/hollie,,116.220.91.155,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n2187,1,49,2017-06-01 18:07:35,http://gleason.biz/frances_shields,,196.102.251.120,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n2188,1,49,2017-04-30 14:30:50,http://kuhicmitchell.biz/mireya_tremblay,,93.144.74.239,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n2189,1,49,2017-04-14 07:31:55,http://gutkowskibayer.net/maymie_becker,,131.110.43.20,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n2190,1,49,2017-01-03 12:44:12,http://mcclureswaniawski.info/victor_dooley,,130.236.239.30,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n2191,1,49,2017-03-06 03:55:35,http://feil.io/claudie.rath,,187.85.241.170,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n2192,1,49,2017-03-13 11:21:50,http://hyatt.io/friedrich.durgan,,122.98.64.136,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n2193,1,49,2017-01-16 13:53:47,http://murraybosco.org/izabella.macejkovic,,251.113.159.10,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n2195,1,49,2017-02-08 14:37:39,http://pouros.biz/mackenzie,,30.35.243.145,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n2196,1,49,2016-12-19 08:57:42,http://weimann.info/cyril,,184.24.238.15,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n2197,1,49,2017-01-30 16:59:50,http://cummerata.name/mina.tromp,,87.214.248.81,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n2198,1,49,2017-05-20 03:00:12,http://mosciskipaucek.name/amber_beahan,,39.115.23.224,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n2199,1,49,2017-03-21 14:49:28,http://hellerfunk.co/kenyatta.stoltenberg,,146.17.145.183,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n2200,1,49,2017-03-31 07:44:05,http://homenick.info/felipe.keler,,103.105.163.101,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n23214,8,521,2017-03-01 07:50:46,http://spinka.io/london,,177.114.95.198,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n23215,8,521,2017-01-20 19:22:29,http://ernser.co/waino_carroll,,151.45.228.141,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n23216,8,521,2017-06-04 09:57:29,http://hauckfunk.com/garrison,,57.161.13.3,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n23217,8,521,2017-05-02 01:16:18,http://vonrueden.co/tyson,,245.132.147.222,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n23218,8,521,2017-02-15 12:06:28,http://stiedemann.org/rylee,,113.252.139.69,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n23219,8,521,2017-06-12 12:44:07,http://tromp.com/jovani,,112.28.174.151,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n23220,8,521,2017-01-06 15:59:20,http://lehner.biz/elvera.blick,,67.36.34.241,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n23221,8,521,2017-03-19 11:59:52,http://blickmayert.name/leon,,186.243.42.129,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n23222,8,521,2017-04-18 07:20:39,http://ratke.co/sabryna,,195.96.78.227,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n23223,8,521,2017-05-12 14:19:13,http://keeling.io/susie_senger,,28.127.185.193,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n23224,8,521,2017-04-24 17:50:39,http://kovacek.info/meredith.bechtelar,,20.73.172.106,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n23225,8,521,2017-01-26 15:45:08,http://balistrerimccullough.biz/sage,,173.190.12.187,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n23226,8,521,2017-05-12 08:24:17,http://goodwin.net/eloy,,41.49.176.156,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n23227,8,521,2017-05-22 03:32:57,http://wilkinson.io/alexa_glover,,65.69.103.99,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n23228,8,521,2017-05-19 20:03:05,http://haleyledner.biz/casimir,,112.25.153.20,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n23229,8,522,2017-01-25 19:09:02,http://stantonwitting.name/dorthy,,139.156.167.59,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n23230,8,522,2016-12-13 18:24:52,http://keeblerruel.org/imelda_spinka,,206.79.92.28,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n23231,8,522,2017-02-18 09:31:55,http://feilmoen.net/renee,,165.43.140.213,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n23232,8,522,2017-03-29 02:15:26,http://wehner.net/palma_medhurst,,120.228.42.188,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n23233,8,522,2016-12-22 20:57:13,http://littleconsidine.io/hal,,30.231.40.211,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n23234,8,522,2017-02-12 12:34:48,http://vonrueden.net/keanu,,20.105.143.105,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n23235,8,522,2017-05-10 04:13:46,http://lynch.net/coby,,67.180.137.157,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n23236,8,522,2017-05-21 20:32:16,http://lindgren.info/mellie,,155.224.153.4,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n23237,8,522,2017-05-28 17:38:49,http://cremin.com/ahmed.monahan,,78.189.68.62,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n23238,8,522,2017-05-22 02:00:22,http://romagueradaniel.biz/francis,,8.154.34.87,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n23239,8,522,2017-02-14 18:33:19,http://hirthe.net/claud_berge,,168.172.24.124,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n23240,8,522,2017-05-05 02:17:56,http://lebsack.io/colleen,,159.90.253.62,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n23241,8,522,2017-03-10 13:22:29,http://lind.com/markus_wilkinson,,60.71.131.224,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n23242,8,522,2017-04-14 01:57:21,http://kuhn.name/kelton,,38.103.16.149,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n23243,8,522,2017-03-12 08:28:48,http://ernser.org/dane_bartell,,3.230.40.41,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n23244,8,522,2017-01-13 09:04:37,http://schimmelmcdermott.com/benjamin,,47.46.206.57,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n23245,8,522,2017-01-18 21:35:11,http://koepp.org/aleandro.haag,,175.76.107.30,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n23246,8,522,2017-04-10 09:39:20,http://davismohr.io/lurline.medhurst,,204.224.173.195,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n23247,8,522,2016-12-30 12:21:43,http://jakubowski.name/florine,,126.109.229.81,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n23248,8,522,2017-01-16 18:52:14,http://heidenreichstiedemann.com/gudrun,,198.62.31.88,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n23249,8,522,2017-01-09 15:40:01,http://oharastroman.biz/arnulfo_konopelski,,99.245.217.209,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n23250,8,522,2017-02-06 17:18:24,http://morar.org/rosa.stoltenberg,,201.241.244.223,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n23251,8,522,2017-03-18 07:49:59,http://macejkoviclarkin.com/kane_harris,,239.208.109.56,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n23252,8,522,2016-12-16 03:45:11,http://waters.net/edison,,13.99.240.125,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n23253,8,522,2017-05-06 05:56:07,http://effertzwolff.io/price,,123.64.75.133,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n23254,8,522,2017-03-20 17:51:59,http://volkman.info/cleora,,38.197.224.103,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n23255,8,522,2017-03-23 16:54:51,http://feeney.net/leta_jones,,134.18.32.116,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n23256,8,522,2017-01-13 13:54:28,http://beahanmckenzie.org/kenny_marvin,,71.172.75.240,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n23257,8,522,2017-04-05 15:04:24,http://schroeder.info/athena.satterfield,,208.31.160.41,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n23258,8,522,2016-12-31 16:46:58,http://oconner.com/nickolas.hills,,11.29.123.149,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n23259,8,522,2017-01-13 21:53:15,http://jacobson.name/santiago,,43.169.198.35,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n23260,8,522,2017-05-10 01:19:06,http://zemlak.name/dock,,66.11.218.115,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n23261,8,522,2017-02-20 18:14:39,http://sporer.net/kylee,,207.38.197.159,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n23262,8,522,2017-02-19 14:49:24,http://shanahan.com/otis,,10.246.159.22,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n23263,8,522,2017-04-13 23:09:58,http://hirtheheller.name/marjory,,37.64.86.197,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n23264,8,522,2017-03-10 10:04:51,http://beiermills.info/chaya,,194.165.229.5,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n23265,8,522,2017-04-11 18:49:16,http://botsfordhammes.biz/gayle.funk,,174.11.189.16,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n23266,8,522,2017-04-26 15:32:46,http://smitham.info/caandra.mills,,166.3.84.165,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n23267,8,522,2017-02-23 14:10:01,http://okonwitting.org/haleigh_mraz,,27.113.167.114,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n23268,8,523,2017-03-19 00:20:57,http://toy.name/bell,,16.148.120.113,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n23269,8,523,2017-05-09 09:43:25,http://crona.net/raymundo_bartoletti,,17.32.209.95,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n2201,1,49,2017-05-05 08:37:14,http://larkinroob.biz/macy,,97.48.174.141,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n2202,1,49,2017-04-28 16:05:01,http://stehr.biz/lambert,,50.31.57.246,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n2203,1,49,2017-04-06 13:10:12,http://gleichnerstehr.info/estrella.ledner,,2.82.162.233,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n2204,1,49,2017-01-01 11:02:08,http://walkerkuhic.net/dexter.kuphal,,75.154.69.230,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n2205,1,49,2017-05-29 09:12:03,http://rempel.net/sidney_block,,93.95.162.181,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n2206,1,49,2017-04-28 23:10:16,http://trompbraun.com/hank.moore,,159.90.92.254,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n2207,1,49,2017-04-14 23:56:40,http://feil.name/ike,,105.120.52.68,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n2208,1,49,2017-02-20 14:30:01,http://blick.name/joany,,73.158.223.8,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n2209,1,49,2017-04-17 04:36:11,http://christiansenosinski.co/aniyah,,23.112.109.132,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n2210,1,50,2017-05-28 14:29:08,http://sauer.info/natasha,,50.230.142.31,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n2211,1,50,2017-04-03 15:30:10,http://johnsonframi.com/marielle.rohan,,159.23.141.243,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n2212,1,50,2017-06-11 22:04:13,http://tromp.co/drew_wisozk,,117.59.12.156,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n2213,1,50,2017-05-08 19:38:21,http://mclaughlinpacocha.io/wilton,,158.188.228.242,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n2214,1,50,2017-05-28 07:25:19,http://toygrimes.com/brandon,,194.128.166.252,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n2215,1,50,2017-06-11 03:39:20,http://bruen.biz/theodora,,63.230.229.171,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n2216,1,50,2017-03-28 00:39:53,http://hettingerstreich.biz/freda_wilkinson,,40.153.184.107,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n2217,1,50,2017-05-13 20:46:57,http://sawayn.net/rosa,,213.73.93.132,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n2218,1,50,2017-04-16 11:26:55,http://kowalker.biz/breanna,,65.246.43.178,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n2219,1,50,2017-01-01 03:03:33,http://rodriguez.co/delmer,,126.177.144.38,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n2220,1,50,2016-12-20 01:50:10,http://ledner.com/clifton,,254.96.41.204,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n2221,1,50,2017-03-15 12:29:17,http://bechtelar.info/ellsworth,,121.245.121.85,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n2222,1,50,2016-12-16 15:46:54,http://leschwalker.io/armand,,99.190.62.92,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n2223,1,50,2017-04-26 21:11:22,http://haaggusikowski.co/brigitte.ebert,,92.158.93.53,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n2224,1,50,2017-04-28 09:15:23,http://oberbrunner.co/caesar,,172.69.195.245,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n2225,1,50,2017-04-18 10:27:18,http://mcglynnoreilly.com/korey,,48.119.247.78,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n2226,1,50,2017-02-20 22:39:24,http://gusikowskiweber.org/tiara_dibbert,,3.118.180.184,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n2227,1,50,2017-04-07 21:58:03,http://prohaskadubuque.name/alivia.halvorson,,78.209.141.104,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n2228,1,50,2017-02-05 23:59:28,http://naderveum.io/rashawn,,40.72.244.253,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n2229,1,50,2017-03-26 04:21:32,http://powlowski.info/vada,,121.237.90.228,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n2230,1,50,2017-02-13 23:34:47,http://damorehegmann.org/laron,,203.251.254.161,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n2231,1,50,2017-01-22 10:34:50,http://mooreyost.io/hardy_maggio,,143.162.59.16,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n2232,1,50,2017-05-16 08:37:02,http://brownshanahan.io/dena.grady,,133.216.251.27,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n2233,1,50,2017-01-31 18:23:01,http://klockodach.info/gregoria,,12.80.7.164,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n2234,1,50,2017-02-06 20:26:25,http://turner.net/hal_baumbach,,220.143.13.133,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n2235,1,50,2017-05-19 19:12:56,http://nienowhansen.name/kayley,,90.140.238.94,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n2236,1,50,2017-01-17 23:57:40,http://gutkowski.org/felicita,,210.2.188.8,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n2237,1,50,2017-06-05 23:27:45,http://stracke.biz/lizeth_murphy,,84.189.179.242,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n2238,1,50,2017-06-12 15:33:24,http://fadelsporer.io/aidan,,107.15.48.31,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n2239,1,50,2017-04-07 20:43:43,http://ruel.com/amara.wuckert,,94.23.28.11,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n2240,1,50,2017-04-03 17:13:02,http://balistreri.name/gloria,,6.191.134.63,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n2241,1,50,2017-01-23 11:28:28,http://emmerich.com/sam_rempel,,185.141.101.46,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n2242,1,50,2017-02-19 04:14:25,http://hermiston.net/fleta.hane,,113.20.127.102,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n2243,1,50,2017-01-17 18:16:04,http://crooksherzog.co/albina_okon,,123.252.146.234,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n2244,1,50,2017-04-15 13:08:49,http://oharareynolds.io/eugene,,102.170.88.73,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n2245,1,50,2017-03-15 18:50:21,http://ryan.io/jarod_jones,,197.223.148.124,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n2246,1,50,2017-06-01 16:50:13,http://kilback.name/kyler.wiegand,,199.104.24.86,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n2247,1,50,2017-01-29 17:58:52,http://mante.info/delphia.homenick,,221.232.93.151,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n2248,1,50,2017-05-17 04:43:40,http://hartmannzulauf.net/velda_ritchie,,180.26.208.183,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n2249,1,50,2017-05-30 02:54:02,http://shields.net/maximo.heller,,9.177.70.2,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n2250,1,51,2017-05-27 00:31:31,http://legros.name/raegan,,121.67.189.190,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n2251,1,51,2017-03-28 10:12:00,http://willmstromp.net/brett,,129.70.79.141,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n2252,1,51,2017-01-31 23:38:08,http://bailey.io/natalie,,120.174.100.15,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n2253,1,51,2017-02-10 11:31:17,http://johnson.info/ryder,,242.233.190.211,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n2254,1,51,2017-05-20 16:22:32,http://ullrich.net/mekhi,,34.149.108.120,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n2255,1,51,2017-03-03 12:47:27,http://wolf.name/phyllis.pacocha,,147.163.6.188,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n2256,1,51,2017-05-14 02:16:22,http://swift.com/shayna_moen,,91.180.67.239,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n23270,8,523,2017-04-29 10:25:49,http://rogahnheidenreich.net/sylvia_krajcik,,216.46.254.175,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n23271,8,523,2017-04-25 20:25:48,http://rutherford.info/stuart,,129.3.207.134,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n23272,8,523,2017-05-26 06:34:33,http://schinnerziemann.biz/danny_upton,,85.72.31.23,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n23273,8,523,2017-03-27 08:30:38,http://heathcote.com/drake.graham,,56.44.228.44,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n23274,8,523,2016-12-27 14:23:04,http://renner.org/walker,,56.207.54.88,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n23275,8,523,2017-01-05 18:28:15,http://runolfonnikolaus.info/maiya,,200.132.58.26,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n23276,8,523,2017-01-17 02:09:17,http://bartelllarson.info/buck_corkery,,82.105.29.196,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n23277,8,523,2016-12-21 19:16:14,http://raynor.io/abdullah,,206.95.187.53,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n23278,8,523,2017-02-19 21:10:12,http://heaney.net/elvie,,198.190.238.131,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n23279,8,523,2017-05-20 09:08:14,http://reichert.net/leora,,243.136.88.60,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n23280,8,523,2017-04-28 22:36:33,http://kuhlman.com/alex_upton,,184.240.153.114,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n23281,8,523,2017-03-16 04:45:29,http://mann.com/adela,,78.16.254.234,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n23282,8,523,2017-03-21 07:58:51,http://halvorsoncummerata.name/jeff,,162.12.236.144,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n23283,8,523,2017-01-19 19:51:04,http://roweokon.org/johnny,,6.191.211.121,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n23284,8,523,2017-03-18 03:43:03,http://armstrong.io/duane.hirthe,,203.128.170.222,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n23285,8,523,2017-03-16 16:29:14,http://rutherford.com/eveline_rau,,32.209.188.34,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n23286,8,523,2017-03-09 17:15:48,http://kuphalkshlerin.com/terence,,150.236.110.18,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n23287,8,523,2017-05-23 13:29:17,http://gerhold.org/kenton_kling,,39.238.93.215,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n23288,8,523,2017-02-22 06:29:23,http://kerluke.io/dejuan,,28.98.68.157,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n23289,8,523,2017-03-05 13:22:44,http://schamberger.net/deven,,31.61.75.201,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n23290,8,523,2017-04-29 16:28:43,http://morar.biz/eriberto_kautzer,,145.175.209.224,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n23291,8,523,2017-04-27 09:00:46,http://croninlangworth.name/erick.waters,,7.57.32.246,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n23292,8,523,2017-06-09 07:31:57,http://bartoletti.info/trinity_jacobson,,164.12.207.122,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n23293,8,523,2017-05-12 03:56:27,http://yundt.io/carmela_gislason,,241.159.195.79,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n23294,8,523,2017-04-27 13:53:24,http://thiel.net/deja,,70.182.196.176,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n23295,8,523,2017-01-24 05:01:39,http://wilkinsonbeier.net/susana_klein,,188.65.2.211,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n23296,8,523,2017-03-05 01:08:35,http://greenfelder.info/wilmer,,231.52.232.169,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n23297,8,523,2017-06-09 22:28:09,http://batzcummings.com/madison,,42.135.75.252,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n23298,8,523,2017-04-30 14:42:14,http://howell.com/elenora,,99.36.48.29,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n23299,8,523,2017-04-20 04:18:03,http://brakus.org/major,,66.56.237.93,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n23300,8,523,2017-05-18 01:49:32,http://cruickshank.name/bailey.ruel,,16.134.14.192,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n23301,8,523,2017-05-10 12:20:55,http://runolfsdottir.org/adrianna.dooley,,231.204.203.207,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n23302,8,523,2017-02-13 11:05:03,http://bauch.com/lora,,78.153.72.186,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n23303,8,523,2017-04-26 08:26:48,http://murazikstanton.co/pete,,16.243.64.189,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n23304,8,523,2017-05-06 03:26:27,http://bechtelar.co/kayley.hermiston,,191.148.160.120,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n23305,8,523,2017-01-31 13:58:25,http://bayer.net/jaden.oberbrunner,,110.48.152.205,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n23306,8,523,2017-01-17 12:35:43,http://grant.co/mohammed.kshlerin,,220.203.26.89,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n23307,8,523,2017-02-06 16:07:55,http://sawayntrantow.io/zetta.erdman,,201.68.6.219,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n23308,8,523,2017-02-10 03:57:16,http://osinski.co/allan.bradtke,,81.189.105.49,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n23309,8,523,2016-12-20 18:36:11,http://stamm.name/marietta,,67.138.53.26,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n23310,8,523,2017-03-07 15:03:10,http://effertzharvey.info/trey,,200.192.220.153,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n23311,8,523,2017-02-28 03:59:38,http://weimann.io/makenzie,,31.187.189.60,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n23312,8,523,2017-02-04 15:25:13,http://haley.com/miles,,197.4.26.3,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n23313,8,523,2017-02-22 09:41:38,http://gorczany.name/jennings,,251.29.70.58,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n23314,8,523,2017-04-12 00:39:52,http://dach.co/nathan.macgyver,,192.243.92.143,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n23315,8,523,2017-01-17 08:45:37,http://boehm.biz/tracy.murray,,204.19.172.245,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n23316,8,523,2017-01-16 10:38:20,http://hamill.net/yazmin,,81.66.219.242,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n23317,8,523,2017-05-07 00:08:12,http://beatty.org/nasir,,252.22.80.89,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n23318,8,523,2017-06-07 09:53:28,http://bayer.biz/wilfrid.bayer,,116.140.95.123,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n23319,8,523,2017-05-28 07:15:13,http://zemlak.org/erik_schimmel,,30.168.245.207,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n23320,8,523,2017-04-10 13:56:42,http://farrellhackett.net/casandra,,94.194.2.81,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n23321,8,523,2017-05-26 20:26:28,http://trantow.info/dayton_shanahan,,160.128.53.211,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n23322,8,523,2016-12-30 04:43:25,http://kerluke.co/lizzie.rosenbaum,,222.202.132.35,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n23323,8,523,2017-05-04 00:17:03,http://klocko.io/ryann,,253.135.82.41,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n23324,8,524,2017-03-01 01:00:39,http://feilkemmer.io/joshuah,,206.9.16.34,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n2257,1,51,2017-05-05 16:31:19,http://spinka.info/ashlee,,199.122.58.194,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n2258,1,51,2017-03-07 05:17:16,http://hellerwilliamson.net/emmet,,40.149.134.142,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n2259,1,51,2017-05-15 23:08:29,http://kuhlman.info/rodger_wilderman,,253.197.214.204,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n2260,1,51,2016-12-18 11:50:03,http://wiza.biz/troy_moen,,51.69.14.19,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n2261,1,51,2017-03-16 23:17:55,http://huel.io/jacinto,,247.82.230.66,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n2262,1,51,2017-06-12 19:30:09,http://goldner.info/roxanne,,158.117.156.85,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n2263,1,51,2017-04-08 23:05:38,http://kohler.co/pamela.nader,,72.72.82.183,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n2264,1,51,2017-03-01 00:58:48,http://jones.co/owen,,166.97.131.115,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n2265,1,51,2017-03-26 16:57:53,http://torphy.name/kaia,,225.195.71.71,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n2266,1,51,2017-03-15 02:07:48,http://gleason.net/haskell.price,,57.161.84.200,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n2267,1,51,2017-04-20 17:37:41,http://ortiz.com/jaycee_raynor,,2.183.83.75,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n2268,1,51,2017-06-11 11:51:07,http://dach.org/jenifer,,24.203.187.225,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n2269,1,51,2017-04-25 00:33:50,http://oberbrunner.io/karson_marks,,150.95.216.116,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n2270,1,51,2016-12-29 23:50:11,http://jerde.io/hailee,,30.51.7.189,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n2271,1,51,2017-02-27 16:15:12,http://von.org/elisa,,139.157.8.195,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n2272,1,51,2016-12-29 13:12:31,http://goyettebruen.name/arlie_prosacco,,153.26.6.83,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n2273,1,51,2017-05-11 20:19:10,http://mills.com/paula.mante,,176.21.59.91,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n2274,1,51,2017-01-02 09:45:07,http://stehr.info/isaiah.hoppe,,168.88.252.15,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n2275,1,51,2017-02-20 08:00:31,http://rosenbaum.io/santa,,94.65.69.195,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n2276,1,51,2017-01-26 00:25:45,http://stiedemann.com/randal.bode,,57.189.233.113,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n2277,1,51,2017-03-29 20:03:11,http://sawaynryan.net/drake,,37.120.93.155,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n2278,1,51,2017-02-13 22:50:00,http://larsonhalvorson.biz/eliza.bernhard,,248.77.218.220,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n2279,1,51,2017-06-04 21:27:00,http://mills.com/lucy.wiegand,,124.146.182.197,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n2280,1,51,2017-05-22 04:08:18,http://kautzer.org/elliot.spinka,,233.46.236.169,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n2281,1,51,2017-04-14 07:54:05,http://wilderman.info/hector,,137.238.73.181,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n2282,1,51,2017-03-28 16:34:45,http://satterfield.info/halie.hane,,106.49.189.153,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n2283,1,51,2017-06-08 07:56:38,http://dickensbeahan.name/tate,,175.51.80.186,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n2284,1,51,2017-01-08 11:43:02,http://buckridge.net/letitia_ward,,51.220.74.131,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n2285,1,51,2017-02-01 04:54:00,http://welchmayert.net/keely,,193.225.26.2,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n2286,1,51,2017-02-14 14:26:40,http://langworthstokes.co/reid,,197.246.249.59,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n2287,1,51,2017-01-30 22:16:53,http://cain.com/hosea,,232.186.107.5,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n2288,1,51,2017-04-07 09:20:35,http://grahammiller.io/citlalli_lubowitz,,100.3.144.164,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n2289,1,51,2016-12-13 12:34:56,http://gerlach.co/haskell,,143.92.242.126,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n2290,1,51,2017-03-26 23:19:12,http://kunde.com/libbie.treutel,,239.236.57.224,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n2291,1,51,2017-06-08 03:47:07,http://kub.com/emelie.fahey,,176.193.129.250,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n2292,1,51,2017-04-05 09:02:48,http://farrellleuschke.org/fatima_hegmann,,190.126.74.58,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n2293,1,51,2017-03-06 03:43:23,http://wiegandkeebler.biz/garry_leffler,,182.41.107.72,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n2294,1,51,2017-02-18 19:51:08,http://ledner.co/alan,,245.222.82.39,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n2295,1,51,2017-02-19 05:16:51,http://bosco.com/filiberto_kris,,14.215.65.198,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n2296,1,51,2017-04-30 20:39:46,http://von.com/sebastian,,163.159.82.173,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n2297,1,51,2017-04-22 08:38:41,http://purdy.org/chelsea,,108.124.109.172,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n2298,1,51,2017-03-17 04:06:24,http://schinner.net/dorothea_swaniawski,,139.32.147.156,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n2299,1,51,2016-12-31 01:27:38,http://littel.co/arielle,,67.228.162.194,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n2300,1,51,2016-12-31 18:01:29,http://bailey.co/oceane_beer,,113.242.214.133,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n2301,1,51,2017-01-27 08:04:31,http://wisokyluettgen.name/madelyn,,194.27.248.41,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n2302,1,51,2017-02-25 05:55:41,http://gaylordrice.info/tatum_strosin,,220.209.99.105,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n2303,1,51,2017-04-10 03:12:59,http://trantow.biz/delphia,,251.225.176.253,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n2304,1,51,2017-02-24 00:45:49,http://stiedemann.org/guiseppe,,116.64.107.43,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n2305,1,51,2017-03-22 17:21:07,http://wuckert.biz/cicero_hauck,,28.39.159.223,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n2306,1,51,2017-03-20 14:25:36,http://robertscruickshank.biz/minerva_dicki,,6.23.119.119,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n2307,1,51,2017-04-02 14:14:07,http://rempel.org/josue_conroy,,244.231.121.250,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n2308,1,51,2017-05-01 18:14:12,http://legroslarkin.io/nash_bins,,234.250.89.249,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n2309,1,51,2017-02-11 08:29:37,http://baumbach.io/thea_reichel,,159.78.11.64,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n2310,1,51,2017-06-04 19:09:57,http://hayes.net/aiden_ziemann,,198.114.252.62,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n2311,1,51,2017-03-24 10:47:47,http://roob.name/nyah,,190.6.52.191,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n2312,1,51,2017-05-01 19:47:03,http://lakin.name/chasity.zieme,,208.42.15.4,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n23325,8,524,2017-04-15 02:54:03,http://beier.com/karson,,125.224.197.142,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n23326,8,524,2017-03-04 08:30:48,http://boehmoreilly.io/lesley_fritsch,,123.116.57.212,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n23327,8,524,2017-06-10 18:44:38,http://waelchi.co/reese,,26.199.118.88,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n23328,8,524,2016-12-19 02:22:50,http://prohaskawhite.org/esther,,136.119.21.245,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n23329,8,524,2017-03-28 18:29:27,http://hegmannhoeger.info/lyla,,240.4.88.7,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n23330,8,524,2017-03-08 15:17:07,http://rutherfordschuster.org/abbigail,,190.217.76.170,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n23331,8,524,2017-05-15 20:04:38,http://ziememclaughlin.net/alene,,202.183.212.51,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n23332,8,524,2017-06-01 08:31:11,http://krichimmel.name/garfield.wehner,,133.72.113.20,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n23333,8,524,2017-04-18 01:40:33,http://kshlerinjacobson.info/christelle,,136.26.76.119,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n23334,8,524,2017-05-26 10:19:55,http://thiel.co/mallie_ernser,,87.28.175.56,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n23335,8,524,2017-06-10 19:20:14,http://rath.name/elisabeth,,61.32.102.199,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n23336,8,524,2017-01-17 06:46:09,http://brakus.info/retta,,106.88.91.79,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n23337,8,524,2017-06-08 15:15:10,http://kling.io/noah,,138.224.40.130,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n23338,8,524,2017-05-19 07:45:45,http://treutelleannon.net/vallie,,45.19.95.236,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n23339,8,524,2017-01-15 20:51:01,http://breitenberg.biz/keshaun.lesch,,243.213.68.197,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n23340,8,524,2016-12-29 22:42:36,http://heidenreichrowe.io/mariane.klocko,,153.8.120.28,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n23341,8,524,2017-01-27 12:51:47,http://zulauf.net/benedict.daniel,,24.181.190.147,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n23342,8,524,2017-02-15 04:43:55,http://romaguera.io/darien_windler,,122.149.77.244,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n23343,8,524,2017-01-05 06:24:21,http://flatleyfarrell.io/charity,,70.131.79.131,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n23344,8,524,2017-04-03 17:37:02,http://heathcotekeler.net/camilla,,158.234.240.15,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n23345,8,524,2017-05-09 23:19:51,http://denesiklowe.io/zoe,,230.97.254.22,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n23346,8,524,2017-03-27 06:46:34,http://rempel.biz/polly_bergstrom,,190.11.176.20,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n23347,8,524,2017-01-09 15:01:32,http://kling.co/cordia,,87.250.146.56,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n23348,8,524,2016-12-25 21:20:17,http://kirlin.co/marcella,,38.193.185.126,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n23349,8,524,2017-03-13 01:55:38,http://brown.biz/abe,,29.234.235.120,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n23350,8,524,2017-06-13 04:46:05,http://rodriguezdaniel.name/savanah,,58.251.198.183,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n23351,8,524,2016-12-31 16:53:33,http://wintheiserhegmann.io/linwood,,183.120.116.149,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n23352,8,524,2016-12-29 11:52:26,http://mccluremurphy.io/dorris.trantow,,211.28.34.68,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n23353,8,524,2017-03-09 20:08:17,http://flatleybartoletti.org/catharine,,125.7.31.28,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n23354,8,524,2017-04-26 15:49:29,http://langhudson.name/abraham,,119.114.64.221,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n23355,8,524,2017-06-01 08:37:00,http://goldnerhilll.io/kayla.cormier,,17.173.35.122,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n23356,8,524,2017-03-04 14:16:31,http://hauck.io/adele.schoen,,191.73.238.3,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n23357,8,524,2017-03-12 13:42:59,http://wiza.com/athena.rutherford,,83.220.229.8,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n23358,8,524,2017-01-14 23:52:02,http://kilback.biz/bella.kaulke,,221.148.111.125,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n23359,8,524,2017-03-29 21:52:35,http://halvorsonhansen.com/domenica,,20.156.33.217,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n23360,8,524,2017-04-19 13:16:20,http://welch.name/arnold.mcglynn,,81.74.143.164,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n23361,8,524,2017-06-07 05:12:28,http://volkman.net/tyreek_nienow,,159.157.253.62,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n23362,8,524,2017-05-05 19:54:53,http://ebertstreich.co/regan_olson,,48.122.176.111,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n23363,8,524,2017-02-21 13:29:08,http://robel.io/maeve_maggio,,208.27.148.121,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n23364,8,524,2017-05-09 11:22:42,http://auerwuckert.io/pearline,,65.227.240.150,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n23365,8,524,2017-04-16 19:30:56,http://schmidt.io/jeie,,11.132.48.249,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n23366,8,524,2017-01-02 10:59:18,http://schultzweinat.org/kitty,,37.125.208.186,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n23367,8,524,2017-01-23 01:17:41,http://prosaccovandervort.co/winnifred,,131.94.131.28,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n23368,8,524,2017-03-15 04:11:59,http://moriette.info/scot_streich,,187.26.16.84,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n23369,8,524,2017-01-21 21:59:22,http://kunze.io/providenci,,179.19.22.68,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n23370,8,524,2017-03-16 01:20:42,http://terry.io/bridget,,35.26.169.124,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n23371,8,524,2017-04-15 07:37:42,http://boehmhuel.co/libbie_stamm,,106.95.17.168,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n23372,8,524,2017-03-23 21:15:16,http://durgan.biz/justen,,87.90.185.8,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n23373,8,524,2016-12-22 20:19:35,http://mayert.info/albert,,61.33.222.198,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n23374,8,524,2017-01-27 18:58:03,http://okuneva.io/cara,,193.242.155.29,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n23375,8,524,2016-12-22 03:37:56,http://willms.biz/corrine,,34.45.86.45,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n23376,8,524,2017-02-12 23:40:29,http://weinatschiller.io/baby_moen,,92.207.193.52,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n23377,8,524,2017-04-02 11:13:35,http://mann.org/mittie,,104.53.79.102,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n23378,8,524,2017-01-23 03:52:20,http://ruecker.net/ignatius,,54.81.28.204,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n23379,8,524,2017-02-21 07:10:21,http://oberbrunnerwilliamson.name/maxine.pollich,,244.48.199.27,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n23380,8,524,2017-02-27 13:32:20,http://macgyver.net/beau_shanahan,,252.152.61.65,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n2313,1,51,2017-05-07 09:35:22,http://strosin.co/krystal_hettinger,,102.165.139.208,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n2314,1,51,2017-04-12 19:46:15,http://labadie.name/laurel_feest,,45.6.114.18,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n23381,8,524,2017-01-13 13:38:42,http://casper.info/wilson.jacobson,,53.141.103.49,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n23382,8,524,2017-01-25 07:23:13,http://bailey.biz/roberta_schinner,,243.61.96.168,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n23383,8,524,2017-06-07 06:39:04,http://pfeffer.io/hilbert,,196.11.5.11,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n23384,8,524,2017-03-05 11:26:04,http://feeneysteuber.biz/janea.leffler,,47.42.109.179,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n23385,8,524,2017-04-29 00:09:51,http://morar.com/lue,,10.177.7.87,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n23386,8,524,2017-03-16 22:37:47,http://kling.info/teagan.flatley,,204.54.186.123,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n23387,8,524,2017-04-14 04:54:16,http://larson.io/bradly_mertz,,190.216.84.246,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n23388,8,524,2017-01-10 15:22:57,http://powlowski.info/rudolph.schulist,,97.5.3.247,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n23389,8,524,2016-12-29 12:08:15,http://schmelerwatsica.net/gaylord_beahan,,205.101.60.49,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n23390,8,525,2017-05-24 01:05:40,http://hayes.info/lorenza,,5.87.127.204,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n23391,8,525,2017-06-09 06:38:51,http://beier.biz/mallie,,178.83.232.95,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n23392,8,525,2017-04-28 09:25:14,http://towne.io/tiana,,236.238.186.183,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n23393,8,525,2017-04-11 15:39:03,http://goldnerstark.com/heath.schumm,,202.213.244.3,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n23394,8,525,2017-01-11 08:30:32,http://stracke.biz/myrtice,,15.139.143.124,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n23395,8,525,2017-03-09 03:27:04,http://sauerwaelchi.co/kaelyn,,142.8.167.50,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n23396,8,525,2017-06-09 07:34:35,http://romaguera.name/reina.mcdermott,,88.201.24.118,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n23397,8,525,2017-04-05 03:34:17,http://kiehn.biz/jaquan_borer,,96.235.40.50,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n23398,8,525,2017-01-27 22:11:48,http://larkin.biz/leora,,69.244.233.186,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n23399,8,525,2017-04-08 18:55:58,http://gaylordhirthe.net/kody,,92.71.95.187,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n23400,8,525,2017-03-16 11:06:34,http://rogahn.net/leland_erdman,,209.52.177.100,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n23401,8,525,2017-01-18 05:05:47,http://homenick.biz/cruz,,31.246.112.67,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n23402,8,525,2017-05-05 20:17:46,http://ratkemcdermott.io/declan_rowe,,99.114.110.242,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n23403,8,525,2017-03-09 02:17:46,http://king.name/shaniya_bayer,,254.120.144.54,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n23404,8,525,2017-03-26 02:15:19,http://cremin.org/shaun.zemlak,,230.19.126.143,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n23405,8,525,2017-05-26 22:56:52,http://wyman.net/lorenza.fadel,,72.95.20.59,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n23406,8,525,2017-06-09 02:20:24,http://gleichner.com/weston.bailey,,16.147.70.245,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n23407,8,525,2017-01-04 12:09:10,http://hansenankunding.co/clara_pollich,,28.120.233.113,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n23408,8,525,2017-05-06 05:33:26,http://quigley.io/trey_renner,,129.240.107.243,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n23409,8,525,2017-05-28 19:12:08,http://predovic.name/sage.kunze,,58.220.56.115,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n23410,8,525,2017-03-28 19:13:53,http://wisokybernhard.org/krystal.kiehn,,80.128.15.208,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n23411,8,525,2017-05-16 10:35:48,http://beer.info/marcelle,,44.199.136.70,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n23412,8,525,2017-05-31 06:55:27,http://goldnerromaguera.net/armani_schiller,,99.238.214.4,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n23413,8,525,2017-04-30 07:56:14,http://windler.net/lina,,102.169.14.222,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n23414,8,525,2017-05-06 01:30:50,http://king.name/darrion_muller,,155.65.92.216,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n23415,8,525,2017-03-02 15:22:51,http://kuhn.biz/javonte,,122.27.215.14,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n23416,8,525,2017-05-12 09:27:26,http://langosh.co/riley.heaney,,173.151.26.146,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n23417,8,525,2017-01-22 13:36:36,http://mcglynn.org/autumn,,29.177.244.60,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n23418,8,525,2017-04-03 14:45:04,http://hammes.org/aimee.bednar,,157.85.81.83,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n23419,8,525,2016-12-30 14:44:55,http://kubrau.io/monserrate,,28.121.96.231,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n23420,8,525,2017-03-10 03:14:58,http://hahn.org/desiree_ko,,114.92.218.12,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n23421,8,525,2017-04-07 17:36:11,http://padbergtillman.org/rosalia,,158.66.141.195,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n23422,8,525,2017-06-04 13:45:18,http://farrellerdman.io/claude,,11.80.77.223,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n23423,8,525,2017-01-27 10:06:15,http://lind.com/orpha,,112.57.92.35,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n23424,8,525,2017-03-01 14:41:38,http://hilll.name/dewayne.pfannerstill,,169.157.187.50,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n23425,8,525,2017-03-11 07:16:43,http://naderbeer.name/henry,,196.111.229.92,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n23426,8,525,2017-02-23 20:56:28,http://rippin.biz/samara,,58.126.57.250,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n23427,8,525,2017-02-09 13:14:03,http://gusikowskicruickshank.info/harrison,,203.76.189.145,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n23428,8,525,2017-01-01 20:26:53,http://zemlakwisozk.co/marie,,151.246.24.32,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n23429,8,525,2017-05-12 20:13:48,http://schmeler.co/pascale,,11.236.179.154,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n23430,8,525,2017-03-11 20:06:56,http://borerpouros.org/thaddeus,,175.137.20.32,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n23431,8,525,2016-12-30 16:45:57,http://schimmelweber.biz/kevin,,195.136.52.22,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n23432,8,525,2017-06-04 02:34:44,http://larson.io/keaton.lesch,,165.156.9.200,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n23433,8,525,2017-03-08 01:43:15,http://stroman.biz/ibrahim_tillman,,58.161.174.52,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n23434,8,525,2017-03-14 13:56:06,http://waelchipollich.org/keanu.reinger,,209.243.141.250,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n23435,8,525,2016-12-23 05:29:32,http://hettingerlueilwitz.co/idell,,240.86.238.206,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n23436,8,525,2017-04-02 20:16:11,http://murazikcollier.info/pattie_heathcote,,63.221.249.5,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n23437,8,525,2017-02-21 00:33:14,http://rice.net/brennan,,167.89.69.206,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n23438,8,525,2017-01-11 17:23:52,http://ziemann.co/mohammed.heidenreich,,187.103.85.37,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n23439,8,525,2017-02-05 05:02:27,http://hansen.org/aglae_sawayn,,110.22.215.110,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n23440,8,525,2017-06-04 09:59:24,http://kingwillms.co/westley_olson,,158.31.123.246,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n23441,8,525,2017-03-24 18:50:42,http://kozeymccullough.net/margarita,,244.200.101.176,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n23442,8,525,2017-04-30 15:48:29,http://stokes.name/halle_brakus,,101.21.135.126,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n23443,8,525,2017-01-13 01:14:56,http://thompson.com/briana.schmidt,,68.134.254.162,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n23444,8,525,2017-05-17 12:52:31,http://miller.net/garth.braun,,79.8.18.104,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n23445,8,525,2017-01-19 00:33:22,http://smitham.com/heidi_smitham,,30.173.211.164,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n23446,8,525,2017-01-22 15:11:00,http://haley.co/larue.greenfelder,,201.206.145.36,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n23447,8,525,2017-05-14 08:53:59,http://friesenkuhic.name/tyrique_kulas,,213.81.133.114,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n23448,8,525,2017-03-29 02:31:15,http://rohantillman.biz/elnora.ernser,,85.51.162.10,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n8683,4,195,2017-06-03 21:19:16,http://kelerlarson.org/everardo,0.7837852015,57.116.161.131,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n8684,4,195,2017-02-11 22:45:01,http://ward.co/julia,0.6341351753,13.103.93.167,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n8685,4,195,2017-04-14 02:15:19,http://dietrichquitzon.io/paxton.erdman,0.4488990600,165.159.3.148,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n8686,4,195,2017-05-08 03:26:40,http://hand.com/xzavier,0.1773083712,254.198.137.54,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n8687,4,195,2016-12-14 20:19:49,http://king.io/dayana,0.5574280453,7.223.122.147,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n8688,4,195,2017-05-15 22:26:32,http://abbott.biz/elta,0.0224180187,249.118.198.27,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n8689,4,195,2017-06-12 15:01:39,http://treutelpadberg.co/may,0.6318311955,188.195.187.121,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n8690,4,195,2017-05-11 14:35:42,http://auer.biz/jude,0.3261076292,217.171.48.109,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n8691,4,195,2017-03-19 15:51:52,http://brakus.biz/te_cremin,0.5271661267,37.112.254.131,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n8692,4,195,2017-02-03 17:41:06,http://funkkuhic.io/jaren_stoltenberg,0.9053255429,238.14.207.110,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n8693,4,195,2017-05-12 20:29:42,http://purdy.co/jon,0.5588179133,92.62.116.82,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n8694,4,195,2016-12-17 12:13:26,http://abshirekuvalis.co/shayna,0.5505128310,211.101.168.143,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n8695,4,195,2017-04-21 06:31:21,http://rowe.net/broderick_jacobson,0.9659758026,58.198.251.128,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n8696,4,195,2017-05-23 09:15:35,http://aufderhargibson.io/mariane,0.7967229161,243.246.226.152,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n8697,4,195,2017-01-25 22:30:28,http://rippinleuschke.com/emilie.bashirian,0.6919455416,34.113.66.191,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n8698,4,195,2017-05-09 03:55:34,http://miller.name/sammy_hamill,0.3681866896,88.233.168.141,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n8699,4,195,2017-01-02 11:49:43,http://yundtschoen.net/walker_walter,0.9930442368,95.209.77.90,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n8700,4,195,2016-12-24 05:08:52,http://conroy.biz/raymond_witting,0.7068929068,166.121.146.37,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n8701,4,195,2017-04-12 08:33:19,http://king.name/markus_corkery,0.9497535706,167.190.69.96,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n8702,4,195,2017-05-25 01:39:50,http://dicki.co/keith_effertz,0.5056025923,199.236.182.189,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n8703,4,195,2017-05-10 19:21:03,http://ziemeullrich.info/emile,0.9636015368,131.98.129.195,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n8704,4,195,2017-05-13 09:22:22,http://terry.co/murphy,0.7034442891,39.215.202.176,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n8705,4,195,2017-05-17 02:24:11,http://mayert.biz/cristopher_dickinson,0.6471544390,161.169.146.228,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n8706,4,195,2017-03-09 09:53:19,http://cole.co/wade,0.0215397393,254.89.248.128,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n8707,4,195,2017-03-27 18:11:47,http://hahnkilback.com/everardo,0.2530588511,138.89.229.175,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n8708,4,195,2017-02-21 02:21:40,http://wiza.org/logan.hamill,0.5923070116,125.32.249.100,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n8709,4,195,2017-06-09 22:28:51,http://daugherty.name/roscoe_cremin,0.0084498931,164.89.129.229,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n8710,4,195,2017-05-01 23:07:04,http://greenpredovic.co/jillian.nikolaus,0.4677807676,80.208.188.88,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n8711,4,195,2017-06-11 14:08:42,http://wisoky.net/bell_schimmel,0.4752186679,250.247.83.99,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n8712,4,195,2016-12-22 12:46:03,http://nader.info/vern,0.8469564257,142.114.234.228,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n8713,4,196,2017-04-10 18:34:40,http://ankunding.co/stone,0.7167377991,3.12.254.189,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n8714,4,196,2016-12-30 17:12:41,http://mcclurethompson.co/lamar.keebler,0.3186774939,101.143.159.85,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n8715,4,196,2017-02-09 09:48:54,http://robel.biz/gracie.toy,0.2396363835,102.83.150.184,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n8716,4,196,2017-04-18 06:50:17,http://hicklecummerata.io/norwood.mann,0.0151560679,8.65.160.100,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n8717,4,196,2017-02-24 16:11:59,http://schowalterbauch.org/nyasia_hintz,0.5030379481,125.124.207.74,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n8718,4,196,2017-05-11 06:49:51,http://schroeder.com/claudine,0.5015744150,79.178.212.146,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n8719,4,196,2017-01-12 11:05:18,http://harberlind.info/maxwell.mckenzie,0.9985890050,4.44.212.37,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n8720,4,196,2017-03-30 14:46:22,http://williamson.org/shanna.davis,0.2034361840,117.116.248.134,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n8721,4,196,2016-12-26 16:43:02,http://walker.io/adam.goyette,0.9304590185,201.120.97.36,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n8722,4,196,2017-01-15 02:25:00,http://brekke.name/nikita_kuvalis,0.8621046820,164.206.112.162,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n8723,4,196,2017-05-23 20:13:35,http://moenwisoky.info/vernon.buckridge,0.8134010853,245.92.213.112,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n8724,4,196,2017-02-28 05:29:25,http://blick.net/kailey.walsh,0.0537161785,151.229.248.6,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n8725,4,196,2017-06-10 12:48:14,http://gerlach.info/emery_nicolas,0.2196112943,124.168.227.143,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n8726,4,196,2017-04-30 15:44:43,http://cain.biz/lupe.kiehn,0.1688795558,240.168.178.83,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n8727,4,196,2017-03-28 12:14:43,http://howecruickshank.com/kyler,0.4800721541,89.14.30.56,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n8728,4,196,2017-02-04 03:12:50,http://andersonkling.com/elmore.nolan,0.2465674377,26.180.250.3,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n8729,4,196,2017-06-13 21:31:39,http://gaylord.org/sydney_sanford,0.4774269048,31.17.96.120,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n8730,4,196,2017-01-08 19:02:05,http://rippin.net/paige_halvorson,0.9619041212,241.186.27.105,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n8731,4,196,2017-03-01 19:34:53,http://hyatt.io/claudine,0.2889963249,211.199.198.206,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n8732,4,196,2017-03-21 00:07:31,http://dickinsonleuschke.net/wilhelm,0.2895888913,5.132.186.63,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n8733,4,196,2017-03-22 00:39:38,http://barton.net/geraldine.lynch,0.5521089322,228.184.233.221,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n8734,4,196,2017-02-28 06:32:45,http://kohler.co/shanna,0.8735604275,55.39.184.171,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n8735,4,196,2017-01-28 07:27:15,http://stammpurdy.net/esther_kuhlman,0.6144055588,109.115.200.191,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n8736,4,196,2017-05-21 13:54:35,http://terrymraz.com/rogelio,0.1453577152,35.191.107.147,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n8737,4,196,2017-01-10 02:53:55,http://schusterschultz.biz/susie,0.2226834152,32.209.103.6,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n8738,4,196,2017-05-17 07:04:52,http://abshire.biz/nikolas,0.7368007009,60.68.72.96,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n8739,4,196,2016-12-16 03:50:21,http://reilly.biz/dulce,0.5882471274,191.150.166.252,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n8740,4,196,2016-12-21 21:50:16,http://schinner.co/orlo,0.5687910592,105.177.106.188,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n8741,4,196,2017-04-22 05:06:02,http://will.net/emmett,0.3029667457,162.235.68.99,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n8742,4,196,2017-03-26 22:05:53,http://nikolaus.name/wanda_veum,0.2171931378,169.11.136.242,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n8743,4,196,2017-03-10 03:13:30,http://shanahanmcglynn.net/manuel_upton,0.8908734344,239.156.154.196,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n8744,4,196,2017-06-02 01:19:20,http://friesenwehner.net/frieda,0.9800004458,106.94.189.223,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n8745,4,196,2017-04-05 11:33:00,http://franecki.info/salvatore,0.7239601332,234.173.181.106,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n8746,4,196,2017-06-04 19:13:00,http://muller.co/dylan,0.3236419281,241.170.80.216,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n8747,4,196,2017-03-30 01:10:10,http://marquardt.com/kade,0.4982056566,37.20.206.34,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n8748,4,196,2017-03-13 13:28:04,http://berge.biz/kyra.blick,0.0645575607,198.205.228.229,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n8749,4,196,2017-04-29 02:49:22,http://dooleyconn.com/juwan,0.1440836982,56.65.32.218,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n8750,4,196,2016-12-30 02:10:06,http://deckow.info/franz,0.5056517775,100.210.213.240,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n8751,4,196,2017-04-23 00:27:26,http://bauchconsidine.info/ruth.trantow,0.9829378082,238.45.96.191,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n8752,4,196,2017-04-08 21:01:30,http://zieme.com/waldo,0.0115969148,176.9.213.94,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n8753,4,196,2017-04-04 17:24:52,http://roberts.com/kiel,0.2218761841,50.91.64.55,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n8754,4,196,2017-05-14 22:13:36,http://tillmanfisher.biz/sydni.cartwright,0.3414799995,110.139.219.90,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n8755,4,196,2017-06-05 07:49:00,http://leannonvonrueden.co/london_mitchell,0.5397942421,50.209.84.58,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n8756,4,196,2017-01-03 03:03:19,http://uptonsmith.io/stevie.hudson,0.3326452450,139.174.152.110,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n8757,4,196,2017-05-20 04:12:33,http://terry.net/clara_mann,0.3054421766,137.46.162.198,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n8758,4,196,2016-12-24 04:58:09,http://hageneswaters.net/lorna,0.3017815319,213.234.252.61,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n8759,4,196,2017-03-23 18:12:53,http://blandasatterfield.com/cali,0.6014862279,125.119.128.8,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n8760,4,196,2016-12-16 02:47:22,http://morar.io/marcia.volkman,0.9932661765,60.52.43.234,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n8761,4,196,2017-02-03 01:02:22,http://cartwright.biz/jerald,0.2602474415,189.192.148.254,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n8762,4,196,2017-03-24 23:00:04,http://padbergnader.org/wade,0.8908535753,198.110.181.41,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n8763,4,196,2017-01-16 17:07:09,http://welch.net/skye_spencer,0.9444863735,79.248.127.190,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n8764,4,196,2017-03-10 04:04:37,http://weimann.co/thora.ledner,0.2645013143,14.237.197.30,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n8765,4,196,2017-05-05 15:13:47,http://terry.co/gustave.hettinger,0.1946649441,145.31.59.226,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n8766,4,197,2017-02-24 20:56:31,http://schiller.co/tiffany,0.8525530358,196.37.102.196,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n8767,4,197,2017-03-25 16:16:46,http://durgan.com/saul.kunze,0.8548617241,191.184.199.225,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n8768,4,197,2017-03-29 03:28:51,http://turnerhagenes.info/adelia_west,0.9370212503,182.102.244.75,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n8769,4,197,2017-03-06 15:21:31,http://harveydaniel.name/casimir.renner,0.8567609227,22.120.49.68,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n8770,4,197,2016-12-23 19:49:25,http://reichertflatley.org/joel.hermann,0.9432064212,238.211.137.29,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n8771,4,197,2017-04-07 20:18:12,http://harveybashirian.biz/bernita.gutkowski,0.1023805070,228.245.225.43,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n8772,4,197,2017-06-08 20:11:59,http://johnstonrippin.name/rogelio.grady,0.1207089154,230.254.236.236,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n8773,4,197,2017-04-15 23:25:24,http://white.com/anika.anderson,0.2536446078,207.226.132.49,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n8774,4,197,2017-02-19 05:59:26,http://ullrich.net/pete_mante,0.8754261180,234.213.106.79,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n8775,4,197,2017-02-02 04:41:54,http://deckow.net/anika_borer,0.6959540940,40.61.18.182,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n8776,4,197,2017-04-04 17:44:21,http://gusikowski.biz/nakia_lang,0.6876525379,79.35.93.167,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n8777,4,197,2017-03-11 14:09:45,http://koelpinprohaska.co/danny_cremin,0.1630662334,195.113.91.191,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n8778,4,197,2016-12-15 18:53:18,http://koch.com/london,0.8343991080,206.221.57.82,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n8779,4,197,2017-02-27 19:21:16,http://howe.biz/emerald,0.9553694777,241.61.109.196,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n8780,4,197,2017-02-11 23:46:18,http://ratke.name/keyon,0.9723278236,33.149.53.106,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n8781,4,197,2017-02-25 23:02:28,http://beier.co/hector,0.0198749615,179.6.180.6,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n8782,4,197,2017-03-10 01:19:09,http://leschwiza.org/donavon,0.6151547357,176.200.133.227,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n8783,4,197,2017-04-25 00:49:43,http://harveyhaag.com/frances,0.0186454341,73.177.3.38,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n8784,4,197,2017-05-27 02:37:09,http://sanford.co/hosea,0.8863437358,103.222.165.178,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n8785,4,197,2016-12-30 23:07:13,http://greenfelder.biz/anabelle,0.7799739026,21.223.45.122,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n8786,4,197,2017-02-20 20:06:34,http://koelpin.io/zora,0.7092584126,163.101.102.150,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n8787,4,198,2017-04-24 21:55:05,http://champlin.name/lia.bashirian,0.1610006191,241.47.31.114,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n8788,4,198,2017-05-01 20:31:21,http://willms.com/cody,0.8698253476,108.179.170.239,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n8789,4,198,2017-04-26 11:36:09,http://little.biz/dortha,0.8525511280,53.178.15.131,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n8790,4,198,2017-03-19 19:52:09,http://strosin.info/claude,0.2872581408,199.151.107.74,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n8791,4,198,2017-01-31 20:24:01,http://labadie.biz/maryam,0.9370233241,185.200.145.13,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n8792,4,198,2017-05-18 00:00:46,http://kutch.com/vivienne.monahan,0.4895155120,132.70.114.73,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n8793,4,198,2017-04-23 16:09:53,http://schmelerbruen.name/dianna,0.7047241343,107.113.74.15,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n8794,4,198,2017-02-07 01:21:52,http://corwinfriesen.com/ryleigh,0.0568916640,222.156.240.16,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n8795,4,198,2017-01-24 09:34:33,http://frami.biz/joseph_keeling,0.0306896389,132.115.163.188,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n8796,4,198,2017-01-23 15:26:43,http://wintheiser.info/rosalinda.harber,0.9774626407,186.217.229.44,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n8797,4,198,2017-05-03 03:29:16,http://weinat.info/joey.collins,0.1712909351,48.80.18.38,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n8798,4,198,2017-04-04 08:02:42,http://stammgleason.org/donnell.abernathy,0.5596023177,202.132.44.211,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n8799,4,198,2017-03-27 07:16:16,http://grimes.name/eloise_frami,0.8900954596,102.32.3.213,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n8800,4,198,2017-04-22 03:08:08,http://weber.biz/max,0.1198896721,143.246.188.216,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n8801,4,198,2017-03-23 23:26:27,http://naderdeckow.com/emmanuelle.mcglynn,0.2920895247,119.90.46.113,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n8802,4,198,2017-02-04 13:54:46,http://mohr.org/shanel,0.4336557161,71.81.48.150,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n8803,4,198,2017-05-16 12:46:08,http://gerlach.io/elena,0.8421889151,191.109.191.241,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n8804,4,198,2017-02-03 12:21:21,http://leannonhyatt.net/natalie_kshlerin,0.0409433998,132.194.206.142,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n8805,4,198,2017-02-13 02:01:29,http://kuhic.name/charlene_mcclure,0.4034373868,58.7.240.130,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n8806,4,198,2017-04-08 09:10:44,http://hintz.net/humberto,0.5279324083,96.22.177.61,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n8807,4,198,2017-04-22 04:48:28,http://emmerichfarrell.biz/furman_gislason,0.8604471129,9.246.5.210,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n8808,4,198,2017-05-31 20:12:08,http://blanda.name/heather,0.6547761520,78.65.196.192,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n8809,4,198,2016-12-17 15:30:38,http://kuvalis.org/herminio,0.7237387998,199.107.71.86,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n8810,4,198,2017-01-07 21:09:28,http://pouros.io/vinnie_moen,0.5282389066,91.200.210.215,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n8811,4,198,2017-02-08 14:20:49,http://pouros.com/troy,0.9972482239,210.61.183.157,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n8812,4,198,2017-02-13 00:06:04,http://bernhardlebsack.org/emilio.jerde,0.4443593866,15.245.216.224,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n8813,4,198,2017-02-21 22:57:08,http://torp.com/marilou_lehner,0.1664114066,93.117.48.177,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n8814,4,198,2017-05-26 22:46:51,http://mannfarrell.name/rebecca.dach,0.6492696285,177.245.202.81,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n8815,4,198,2017-04-07 23:36:18,http://mosciski.biz/brown.fadel,0.5192736514,95.44.107.25,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n8816,4,198,2017-01-03 18:15:42,http://cruickshankpredovic.biz/lina,0.0932487451,162.104.85.245,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n8817,4,198,2017-06-05 23:56:45,http://dooley.info/cletus_wilderman,0.2304722827,64.112.110.232,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n8818,4,198,2017-06-05 11:02:51,http://kozeywolf.biz/esteban_bashirian,0.8628739886,65.79.41.167,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n8819,4,198,2017-03-12 00:43:11,http://effertz.com/filiberto,0.5978230981,75.85.100.180,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n8820,4,198,2017-05-28 13:46:58,http://fadel.com/jamar.gibson,0.4717492853,213.5.217.192,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n8821,4,198,2017-04-30 17:52:45,http://feeney.biz/pink,0.0020606172,218.84.219.79,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n8822,4,198,2017-03-26 08:26:31,http://tremblay.io/jany,0.9786546798,142.134.200.223,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n8823,4,198,2017-03-22 15:14:20,http://windlerframi.info/aliza,0.4910358123,218.96.245.187,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n8824,4,198,2017-01-16 17:03:20,http://keeling.name/akeem.walker,0.6586889301,146.189.109.114,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n8825,4,198,2017-05-04 11:00:50,http://hammes.com/sylvester.cormier,0.6127640742,81.164.253.15,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n8826,4,198,2017-04-05 04:29:26,http://schuppe.net/jamal_walker,0.8486946242,16.241.119.214,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n8827,4,198,2017-04-16 16:25:23,http://kuphalziemann.net/orion,0.7549960793,243.217.134.67,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n8828,4,198,2017-04-19 17:39:08,http://boyle.org/neoma_renner,0.0623859320,191.57.121.162,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n8829,4,198,2017-06-12 17:16:51,http://jones.com/mitchel,0.0947111947,37.213.241.95,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n8830,4,198,2017-03-03 13:01:16,http://treutel.io/nettie_murphy,0.5876643815,197.219.33.13,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n8831,4,198,2017-04-06 12:56:06,http://bodecole.name/idella.wiza,0.1300575205,137.136.178.232,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n8832,4,198,2017-06-04 01:43:43,http://zulauf.io/kris.medhurst,0.2224550573,89.117.76.206,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n8833,4,198,2017-06-11 10:07:42,http://ziemannleuschke.biz/savion,0.3792597564,108.73.190.232,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n8834,4,198,2017-04-08 10:50:08,http://pagac.com/jack,0.1507565899,77.32.202.169,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n5761,3,126,2017-02-11 01:37:39,http://spinkaritchie.info/jaquan.schuppe,,19.241.180.52,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n5762,3,126,2017-05-19 07:49:02,http://roob.name/jarvis,,137.150.246.101,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n5763,3,126,2017-04-02 05:54:42,http://graham.io/uriel,,68.18.167.224,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n5764,3,126,2017-01-30 07:01:16,http://willspencer.co/dane,,250.215.10.145,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n5765,3,126,2017-02-07 08:05:07,http://little.co/ethel_kirlin,,52.153.156.78,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n5766,3,126,2017-04-18 02:05:43,http://leschbednar.com/elva_schaefer,,130.73.251.200,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n5767,3,126,2017-04-02 15:26:15,http://kihnsimonis.io/ted,,230.245.96.46,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n5768,3,126,2017-03-29 22:16:14,http://bayer.com/myah,,151.63.44.212,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n5769,3,126,2016-12-16 06:52:23,http://kovacek.info/waldo,,111.219.40.19,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n5770,3,126,2017-04-20 10:47:47,http://franecki.io/priscilla_zboncak,,250.206.28.76,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n5771,3,126,2017-05-24 01:36:28,http://schultzerdman.biz/sydni,,9.46.197.157,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n5772,3,126,2017-06-04 08:16:00,http://mcglynn.io/juana_kutch,,235.26.46.82,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n5773,3,126,2017-03-31 11:45:03,http://mcdermottmcglynn.org/edward,,217.67.28.93,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n5774,3,126,2017-05-26 01:02:43,http://effertz.co/charity,,203.50.150.86,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n5775,3,126,2017-03-22 05:32:49,http://breitenberg.name/neoma.graham,,101.45.6.114,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n5776,3,126,2017-01-16 20:01:42,http://macgyver.com/sheila,,243.115.6.81,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n5777,3,126,2017-03-10 08:10:21,http://jacobsheidenreich.info/mateo.olson,,216.191.47.120,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n5778,3,126,2017-02-16 22:00:30,http://wilkinson.com/tania_nienow,,233.233.31.156,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n5779,3,126,2017-05-30 18:17:50,http://bogisich.info/mustafa,,188.232.224.103,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n5780,3,126,2017-05-07 01:31:09,http://nitzsche.io/ara_kemmer,,18.97.64.164,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n5781,3,126,2016-12-28 05:29:02,http://hintzhodkiewicz.org/rhiannon,,195.60.79.164,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n5782,3,126,2017-03-08 12:58:42,http://kreiger.com/leann_balistreri,,20.140.219.112,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n5783,3,126,2017-04-30 01:07:30,http://turcotte.io/enoch,,95.54.125.220,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n5784,3,126,2017-01-13 04:15:00,http://schambergerhettinger.biz/izaiah,,200.32.60.207,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n5785,3,126,2017-05-24 03:33:32,http://sanfordrohan.net/arnulfo.vandervort,,247.254.202.254,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n5786,3,126,2017-02-22 15:27:05,http://mitchell.io/libby,,64.61.36.182,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n5787,3,126,2017-05-18 08:59:04,http://ullrichferry.co/sasha,,131.125.219.48,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n5788,3,126,2017-01-05 05:36:52,http://schusterkautzer.net/kathryne.jacobi,,175.117.79.20,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n5789,3,126,2017-05-31 00:53:08,http://nitzscherunolfsdottir.net/margarete.metz,,52.2.218.176,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n5790,3,126,2017-04-18 10:36:00,http://carrolljerde.org/roie_schuppe,,166.134.156.162,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n5791,3,126,2017-04-08 15:05:29,http://nikolauskerluke.info/malinda,,98.192.86.187,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n5792,3,126,2017-05-29 00:14:09,http://leannondaugherty.net/tanya,,177.179.132.132,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n5793,3,126,2017-03-18 23:16:20,http://swiftschultz.net/kurt,,185.75.187.200,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n5794,3,126,2017-04-13 08:25:15,http://rueckerdurgan.io/aglae,,69.40.47.79,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n5795,3,126,2017-02-01 23:03:35,http://stoltenbergblanda.net/luisa.bailey,,25.14.201.92,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n5796,3,126,2017-02-22 18:16:41,http://millercronin.info/tabitha,,34.161.44.60,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n5797,3,126,2017-04-11 01:06:19,http://hilpertgoyette.net/abdul,,9.141.48.95,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n5798,3,126,2017-03-14 07:47:56,http://brekkedietrich.com/ramona,,195.195.73.210,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n5799,3,126,2017-03-08 09:02:25,http://satterfield.name/mable_bechtelar,,65.2.206.237,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n5800,3,126,2017-05-06 15:28:37,http://franecki.com/madyson.kub,,182.2.159.252,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n5801,3,126,2017-05-17 03:00:56,http://roob.co/ivy,,101.203.244.98,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n5802,3,126,2017-05-12 04:02:40,http://wunsch.name/cristina,,140.125.81.247,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n5803,3,126,2017-02-14 09:39:51,http://gerlach.io/pete,,141.93.138.232,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n5804,3,126,2017-05-12 07:06:24,http://bergejohns.biz/brian,,194.238.112.173,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n5805,3,126,2017-02-02 02:45:58,http://buckridgemertz.info/roscoe_waelchi,,7.77.133.61,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n5806,3,126,2017-03-01 02:16:29,http://walsh.com/isidro,,191.102.236.76,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n5807,3,126,2017-06-08 01:47:23,http://stracke.name/allison,,94.230.23.13,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n5808,3,126,2016-12-18 05:10:10,http://gerhold.biz/concepcion,,253.181.17.28,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n5809,3,126,2016-12-22 23:29:19,http://strosinsimonis.biz/maximillian_lemke,,33.237.59.193,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n5810,3,126,2017-02-20 09:43:06,http://kertzmann.net/alyce,,214.81.8.20,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n5811,3,126,2017-03-17 09:28:45,http://willsteuber.name/queen,,10.25.5.44,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n5812,3,126,2017-01-24 19:24:54,http://bodeskiles.info/domenic_krajcik,,115.91.253.61,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n5813,3,126,2017-01-20 14:50:27,http://skiles.org/jee,,142.43.172.147,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n5814,3,126,2017-03-27 22:17:50,http://conn.net/raleigh,,4.211.141.25,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n8835,4,198,2016-12-20 22:41:15,http://ryan.co/cara,0.5597857971,123.73.11.206,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n8836,4,198,2016-12-22 11:02:10,http://hodkiewicz.name/agnes,0.4273521017,241.13.64.198,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n8837,4,198,2017-01-25 02:17:22,http://hand.net/ursula_altenwerth,0.7412839932,217.56.198.79,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n8838,4,198,2017-03-03 18:18:28,http://legrosrunte.co/agustin.purdy,0.2312579653,107.184.213.54,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n8839,4,198,2017-05-19 04:56:08,http://grant.net/kara.buckridge,0.1595464850,78.47.100.123,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n8840,4,198,2017-06-13 23:11:15,http://dach.co/bette.harvey,0.8751103728,199.181.57.136,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n8841,4,198,2017-04-30 08:57:49,http://cruickshank.com/ima.little,0.1409160647,30.80.207.158,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n8842,4,198,2017-05-24 22:14:15,http://jacobs.com/yasmeen,0.2400374820,51.164.133.145,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n8843,4,198,2017-04-13 19:09:19,http://schumm.biz/ima,0.7108763836,33.248.213.4,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n8844,4,198,2017-03-24 19:15:50,http://rogahn.co/jaydon.schmitt,0.5650419068,25.16.34.79,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n8845,4,198,2017-02-23 08:30:11,http://boyle.com/makenna,0.3737284898,59.190.213.74,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n8846,4,198,2017-03-09 02:47:01,http://mraz.co/eleanora,0.9467390478,46.147.247.254,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n8847,4,199,2017-01-17 13:15:21,http://reillygusikowski.com/norene_gulgowski,0.9757982228,116.120.227.21,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n8848,4,199,2016-12-31 02:00:48,http://becker.com/jo_lubowitz,0.5733575374,202.103.11.2,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n8849,4,199,2017-04-08 05:00:24,http://reilly.org/santiago_murray,0.8998182259,113.108.127.189,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n8850,4,199,2017-01-28 10:38:24,http://block.biz/carter,0.6884710172,245.172.199.155,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n8851,4,199,2017-04-04 17:40:43,http://leuschke.net/juston,0.0950574058,68.38.78.199,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n8852,4,199,2017-06-06 11:37:06,http://lang.info/willard,0.6666275966,196.152.57.120,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n8853,4,199,2017-05-13 20:32:19,http://deckow.org/ashton.donnelly,0.3069954647,165.103.51.65,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n8854,4,199,2017-01-14 19:52:59,http://durgan.name/magnolia_gleason,0.5743377291,39.120.76.185,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n8855,4,199,2017-06-13 06:58:46,http://runolfon.co/marcella,0.8904960201,233.10.142.125,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n8856,4,199,2017-03-22 05:50:58,http://nadersanford.co/paige,0.2439292823,83.184.222.153,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n8857,4,199,2017-01-28 23:04:51,http://oconner.io/gregg.heel,0.3512053496,147.77.56.158,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n8858,4,199,2017-04-13 09:31:03,http://ernserryan.info/orrin_heidenreich,0.4265410236,232.15.22.205,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n8859,4,199,2017-02-21 18:42:37,http://priceparker.name/miguel,0.7252021458,247.79.225.110,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n8860,4,199,2017-03-25 16:58:58,http://rogahn.net/ada,0.1964984097,127.190.222.154,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n8861,4,199,2017-03-13 13:52:29,http://wisozkrunolfon.org/gladys,0.2452635266,103.15.69.175,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n8862,4,199,2017-05-27 23:16:35,http://sanfordweimann.net/neha.gerlach,0.3878320428,154.191.213.74,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n8863,4,199,2017-05-07 14:35:55,http://marksherman.net/alek,0.3495561763,105.12.97.69,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n8864,4,199,2017-04-01 07:45:23,http://carroll.io/dion.green,0.7871974859,46.33.72.25,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n8865,4,199,2017-01-17 05:02:06,http://yundt.name/anais_reichel,0.5399770214,157.7.119.219,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n8866,4,199,2017-01-06 23:48:03,http://konopelski.name/marion,0.7501745993,41.103.127.87,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n8867,4,199,2017-05-24 06:43:00,http://okon.com/toni,0.7998795361,104.70.37.183,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n8868,4,199,2017-04-30 13:48:44,http://hilpert.io/alycia,0.8425533516,144.110.218.89,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n8869,4,199,2017-02-10 15:11:46,http://krisreichert.io/catalina.barrows,0.5081939500,117.46.49.110,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n8870,4,199,2016-12-27 03:16:03,http://kshlerinlockman.info/melyna_upton,0.0952862521,220.209.133.38,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n8871,4,199,2017-06-13 04:10:23,http://hudsonthiel.info/kaylee_thiel,0.7483013854,144.65.57.204,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n8872,4,199,2017-05-06 15:41:22,http://okonmarks.org/hans.ondricka,0.3289508272,204.183.95.137,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n8873,4,199,2017-03-03 04:02:50,http://ruecker.co/alford.schamberger,0.4900606503,57.216.89.30,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n8874,4,199,2017-04-16 11:35:37,http://mcdermottwuckert.net/ocie.rowe,0.2898629585,164.52.200.150,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n8875,4,199,2017-03-12 14:42:26,http://daughertyhirthe.com/narciso,0.8320872702,86.135.45.14,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n8876,4,199,2017-02-11 08:48:15,http://lang.co/dustin_mcdermott,0.4426876779,8.5.137.78,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n8877,4,199,2017-02-19 06:45:49,http://cruickshank.biz/april,0.8704595244,149.225.225.21,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n8878,4,199,2017-01-19 06:23:10,http://stokes.co/jolie,0.9187108653,122.55.182.222,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n8879,4,199,2017-04-19 08:24:02,http://miller.biz/merlin,0.1996435795,84.231.14.79,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n8880,4,199,2017-04-06 22:14:06,http://runtehintz.name/kenton_jerde,0.7733691640,160.182.241.62,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n8881,4,199,2017-03-07 08:09:38,http://hilpert.biz/tate,0.4247030224,162.203.127.132,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n8882,4,199,2017-05-04 05:07:46,http://hartmann.name/ezra,0.8284693998,120.133.67.103,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n8883,4,199,2016-12-15 14:31:15,http://jerderowe.info/jordon_reynolds,0.2388870643,191.70.97.49,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n8884,4,199,2017-01-25 07:44:03,http://hauck.biz/ryann,0.6717933339,73.39.10.121,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n8885,4,199,2017-04-15 06:06:05,http://mckenzie.name/orin,0.6378697137,80.97.138.232,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n8886,4,199,2017-02-13 01:11:45,http://stanton.com/bette_towne,0.2023286210,94.250.126.20,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n5815,3,126,2017-02-08 16:26:02,http://satterfield.co/holly_ohara,,134.152.103.122,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n5816,3,126,2017-03-28 17:31:32,http://torphyauer.biz/helmer.heller,,49.86.57.56,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n5817,3,126,2017-02-20 21:48:45,http://cummerata.com/nels,,101.189.149.200,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n5818,3,126,2017-01-16 10:46:05,http://thompson.io/melia,,65.24.189.52,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n5819,3,127,2017-02-26 12:06:13,http://feil.info/floyd_aufderhar,,62.175.124.96,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n5820,3,127,2017-04-09 22:42:32,http://bergstromabbott.com/kaia,,61.224.132.213,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n5821,3,127,2017-01-27 00:45:24,http://pricespencer.info/eli.moore,,85.89.146.10,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n5822,3,127,2017-04-17 16:14:16,http://gaylordherzog.name/ryleigh.kunze,,75.89.4.17,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n5823,3,127,2017-04-14 13:30:08,http://brown.com/maximilian.stanton,,202.174.202.86,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n5824,3,127,2017-02-15 03:41:36,http://stoltenbergnader.io/hannah,,172.206.48.97,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n5825,3,127,2016-12-26 00:47:49,http://kub.net/alek_ernser,,166.206.100.123,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n5826,3,127,2016-12-28 18:37:24,http://adams.name/alanis,,116.220.245.254,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n5827,3,127,2016-12-28 11:00:48,http://mcglynn.biz/elsa_carroll,,144.33.5.234,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n5828,3,127,2017-02-17 01:46:00,http://hettinger.net/leonardo,,122.127.173.190,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n5829,3,127,2016-12-15 22:31:07,http://waters.org/margaretta.jacobson,,246.250.199.6,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n5830,3,127,2017-02-25 03:37:25,http://frami.com/jaunita.kuphal,,94.122.28.209,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n5831,3,127,2017-05-07 23:11:24,http://kihnfranecki.co/omari_altenwerth,,56.16.52.212,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n5832,3,127,2017-02-14 23:53:40,http://brownpowlowski.net/dedrick.tillman,,78.88.95.87,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n5833,3,127,2017-04-17 12:48:19,http://grady.org/buddy,,27.7.210.206,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n5834,3,127,2017-05-08 13:14:20,http://carrollgutmann.co/antonetta.cartwright,,249.69.73.186,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n5835,3,127,2017-06-06 09:46:28,http://dachmante.com/rachel_hauck,,81.186.139.160,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n5836,3,127,2017-01-02 05:08:39,http://ohara.co/shayne,,130.251.188.145,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n5837,3,127,2017-06-06 10:09:21,http://rowe.io/ford,,111.209.128.75,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n5838,3,127,2016-12-23 11:46:29,http://littel.io/aniya.casper,,174.254.240.70,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n5839,3,127,2017-04-21 16:04:32,http://homenicksauer.io/dillan.rippin,,173.239.52.75,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n5840,3,127,2017-01-08 11:16:19,http://erdmanolson.name/eino_turner,,16.85.248.223,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n5841,3,128,2017-06-08 15:21:48,http://reichelarmstrong.co/carter,,72.207.42.42,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n5842,3,128,2017-02-12 03:53:30,http://kunde.org/lucas_pollich,,106.207.6.9,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n5843,3,128,2016-12-30 20:21:24,http://hahntorphy.io/leon,,169.6.103.14,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n5844,3,128,2016-12-26 05:15:14,http://hayesflatley.name/rico,,172.221.172.252,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n5845,3,128,2017-01-16 02:15:05,http://sengerlang.com/cletus_beatty,,195.239.32.166,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n5846,3,128,2017-01-10 05:10:02,http://hand.info/daphne,,108.127.192.85,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n5847,3,128,2017-01-12 08:51:11,http://casper.name/freeman,,229.47.145.227,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n5848,3,128,2017-01-09 16:35:25,http://schimmel.io/fritz.schuppe,,157.79.200.222,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n5849,3,128,2017-05-23 04:18:38,http://lindgrenaltenwerth.co/george.ullrich,,106.235.120.160,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n5850,3,128,2017-01-07 06:19:31,http://jones.org/alejandra_okuneva,,25.160.219.138,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n5851,3,128,2017-03-17 05:22:59,http://bartell.org/andreane,,20.149.122.52,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n5852,3,128,2016-12-17 04:54:48,http://hackett.net/anthony,,131.238.49.210,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n5853,3,128,2017-03-21 18:43:03,http://townehintz.biz/amelie_wiegand,,85.70.210.56,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n5854,3,128,2017-04-13 01:48:34,http://swaniawski.info/marilie.adams,,51.3.62.226,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n5855,3,128,2016-12-19 07:26:44,http://veum.net/brant,,63.133.153.111,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n5856,3,128,2017-03-11 04:22:17,http://brauncormier.io/braulio_king,,159.44.162.224,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n5857,3,128,2017-06-01 00:03:56,http://kundehaag.name/nina,,145.169.253.252,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n5858,3,128,2017-05-10 15:32:34,http://green.net/claudia,,21.125.20.220,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n5859,3,128,2017-01-03 15:30:00,http://cormier.biz/elmore,,19.45.61.239,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n5860,3,128,2017-01-05 21:24:18,http://stiedemann.io/gail,,240.228.74.226,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n5861,3,128,2016-12-17 13:27:07,http://rowe.info/francisca,,163.148.75.75,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n5862,3,128,2017-06-07 08:12:45,http://lehner.io/ed,,102.46.128.223,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n5863,3,128,2017-02-16 16:44:13,http://welch.net/alvera.schaden,,203.237.152.66,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n5864,3,128,2017-05-01 06:42:37,http://turner.biz/rita,,17.136.117.214,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n5865,3,128,2017-03-23 02:43:08,http://reingerwalter.com/constantin,,46.205.141.143,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n5866,3,128,2017-01-31 11:30:27,http://rath.net/shania,,139.248.152.86,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n5867,3,128,2016-12-31 00:21:17,http://jaskolski.com/rickey,,98.13.92.24,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n5868,3,128,2017-06-08 13:28:33,http://bechtelargoodwin.biz/deondre.prosacco,,210.82.194.231,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n5869,3,128,2017-02-10 16:53:16,http://collier.biz/tre_wehner,,40.75.59.173,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n11753,5,263,2017-06-11 19:06:30,http://bahringer.biz/rae,0.2091751182,60.114.254.10,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n11754,5,263,2017-02-11 07:21:11,http://renner.name/otilia,0.6408695181,30.106.42.26,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n11755,5,263,2016-12-24 22:38:09,http://danieltowne.co/angie_wintheiser,0.1623754006,209.80.103.24,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n11756,5,263,2016-12-22 15:57:10,http://walshnader.co/billy,0.3004525220,74.2.168.151,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n11757,5,263,2017-02-10 07:03:12,http://konopelskinienow.com/peggie_heel,0.2372708509,233.225.189.146,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n11758,5,263,2017-01-14 03:38:01,http://shanahankoch.net/wade.connelly,0.8361292759,237.150.180.22,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n11759,5,263,2017-04-04 06:32:06,http://wiza.name/lynn,0.4176487362,6.155.218.162,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n11760,5,263,2016-12-18 20:08:28,http://reilly.com/samanta,0.1374549331,161.171.184.95,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n11761,5,263,2017-03-26 14:11:30,http://runtemoore.co/rodolfo,0.3678270642,191.162.136.31,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n11762,5,263,2017-05-16 22:47:36,http://lehner.net/felicia.jacobi,0.8765909621,81.33.244.45,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n11763,5,263,2017-04-11 21:43:42,http://jacobson.net/devante,0.6588732520,250.9.18.245,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n11764,5,263,2017-02-04 03:04:15,http://breitenbergweber.name/charley_wisoky,0.2655681962,71.93.221.238,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n11765,5,263,2016-12-20 02:24:07,http://rolfson.biz/jarrod,0.9641338458,72.126.67.66,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n11766,5,263,2016-12-15 07:26:19,http://harris.biz/juliet.kuhlman,0.6301629881,167.149.114.171,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n11767,5,263,2017-04-15 19:37:19,http://mraz.net/shana_hackett,0.9526687514,144.216.117.206,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n11768,5,263,2017-04-19 05:04:49,http://homenick.com/kim,0.1186251824,209.207.26.239,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n11769,5,263,2017-02-06 10:28:57,http://ferry.io/celia,0.2869380626,93.133.66.251,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n11770,5,263,2017-06-04 14:46:20,http://ward.biz/dovie_heathcote,0.2719403218,206.129.103.236,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n11771,5,263,2017-02-21 07:58:11,http://lemke.info/amanda_gibson,0.9896862550,225.204.43.217,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n11772,5,263,2017-03-21 15:36:11,http://will.com/samara,0.2176258570,87.227.33.129,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n11773,5,263,2017-04-18 21:39:03,http://schowalter.name/zaria_eichmann,0.5786358946,86.36.218.36,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n11774,5,264,2017-04-20 07:36:20,http://flatley.org/sienna_erdman,0.3083172227,163.225.201.157,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n11775,5,264,2017-02-20 16:25:05,http://rauconnelly.info/margaretta,0.9783480849,161.220.78.78,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n11776,5,264,2017-05-28 17:43:59,http://creminstoltenberg.co/brendan_rowe,0.7458118886,198.219.252.123,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n11777,5,264,2017-04-20 10:58:04,http://hegmann.biz/rene_hansen,0.7922647543,38.119.71.221,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n11778,5,264,2017-04-14 19:11:04,http://kuhn.io/edmund,0.3975225880,100.54.155.9,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n11779,5,264,2017-04-06 21:44:49,http://balistreri.info/hershel_gerhold,0.5550865955,67.183.26.175,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n11780,5,264,2017-05-09 14:59:19,http://flatley.com/lewis,0.4312329361,165.251.22.166,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n11781,5,264,2017-04-19 16:11:28,http://gleason.co/max,0.8814850552,8.212.9.27,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n11782,5,264,2017-03-17 04:17:22,http://lynch.io/aiden.corkery,0.0660631373,14.178.243.6,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n11783,5,264,2017-05-19 23:08:02,http://hoeger.name/opal,0.5941297618,44.46.221.46,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n11784,5,264,2017-06-07 18:19:47,http://ryan.co/susanna,0.4880950590,27.28.215.36,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n11785,5,264,2017-06-02 02:02:24,http://gutkowskistanton.net/brady.king,0.9731355309,228.132.38.128,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n11786,5,264,2016-12-24 11:38:35,http://stracke.net/selina,0.8405589635,247.164.239.194,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n11787,5,264,2017-06-05 00:50:21,http://bogisichterry.co/ray,0.2937510124,107.186.66.115,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n11788,5,264,2016-12-14 07:03:18,http://sawayn.info/margie_kling,0.8357591555,23.180.121.89,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n11789,5,264,2017-04-14 02:58:20,http://green.org/emory_mann,0.2629312041,143.125.220.36,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n11790,5,264,2017-03-10 14:55:24,http://schumm.name/mia.pagac,0.5566922607,131.24.20.215,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n11791,5,264,2017-03-16 09:39:23,http://mraz.io/lucienne,0.9698309033,115.4.109.246,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n11792,5,264,2017-04-14 23:26:25,http://oconner.name/hershel_mccullough,0.6218460271,173.169.207.82,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n11793,5,264,2016-12-19 18:31:04,http://bednardibbert.org/rhea.barton,0.5442219433,19.231.145.123,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n11794,5,264,2017-02-14 17:07:03,http://wiegand.org/aurelie,0.2730209832,118.87.122.94,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n11795,5,264,2017-04-08 01:44:44,http://terry.biz/geovanni,0.1498978854,77.63.173.180,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n11796,5,264,2017-02-25 18:38:39,http://moenaltenwerth.name/sarai.grady,0.0525185271,32.188.243.216,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n11797,5,264,2017-02-15 00:59:53,http://ratke.biz/carley,0.9792004753,207.61.54.2,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n11798,5,264,2017-04-13 18:52:20,http://hacketttillman.info/megane,0.2000283080,169.220.12.34,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n11799,5,264,2017-04-15 16:19:25,http://johns.biz/abbigail.bruen,0.7005317652,233.223.123.246,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n11800,5,264,2016-12-22 18:35:27,http://yostgusikowski.info/steve,0.7034506332,40.185.6.234,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n11801,5,264,2017-02-02 07:10:02,http://reynolds.info/pedro,0.0604570209,3.79.237.102,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n11802,5,264,2017-04-26 04:47:54,http://boehm.co/virgil,0.6700351048,70.86.169.17,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n8887,4,199,2017-01-06 10:54:33,http://collierkohler.com/eduardo.jakubowski,0.5452140762,246.28.87.203,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n8888,4,199,2017-05-10 11:08:56,http://johnsongaylord.name/hilario,0.4697952740,66.143.187.56,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n8889,4,199,2016-12-28 17:19:47,http://yundtgreenholt.io/marianna,0.3223297646,77.181.56.188,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n8890,4,199,2017-01-06 18:16:24,http://kris.org/al,0.4301509114,180.15.213.245,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n8891,4,199,2017-04-07 00:42:48,http://oconnellsteuber.biz/richie_skiles,0.8376457942,24.110.195.135,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n8892,4,199,2016-12-25 11:12:27,http://trantow.name/erika.will,0.7093578694,120.113.129.207,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n8893,4,199,2017-02-02 09:59:44,http://erdmanlang.co/roel_borer,0.1034211088,147.77.59.23,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n8894,4,199,2017-01-25 21:58:39,http://shields.org/gregory,0.6686169953,227.105.88.72,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n8895,4,199,2017-01-15 00:19:20,http://trantow.com/palma_roberts,0.0251613110,171.246.183.67,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n8896,4,199,2017-06-08 17:27:34,http://donnellydavis.com/hattie,0.5330141072,218.226.76.251,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n8897,4,199,2017-06-09 21:47:36,http://smithamabshire.io/danika.kaulke,0.1851461362,218.14.232.187,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n8898,4,199,2017-03-01 23:28:33,http://wizaspinka.name/roma_grimes,0.8940611531,139.177.33.126,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n8899,4,199,2017-01-31 11:44:24,http://pollich.org/cyril,0.2417822554,183.159.67.75,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n8900,4,199,2017-05-05 19:09:12,http://stantonnolan.co/cara,0.6459126273,108.23.223.139,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n8901,4,199,2017-04-12 11:51:13,http://smitham.net/josh_hills,0.6931682517,68.145.103.219,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n8902,4,199,2016-12-15 03:15:43,http://maggioschneider.org/consuelo_dooley,0.0036287370,174.55.87.141,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n8903,4,199,2016-12-27 20:08:35,http://dachnikolaus.net/roy,0.7517413810,208.191.88.60,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n8904,4,199,2017-03-25 04:17:08,http://crist.info/willis,0.3230633267,53.144.51.195,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n8905,4,199,2017-04-16 12:51:14,http://labadie.com/augustus,0.8987353695,77.201.33.166,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n8906,4,199,2016-12-27 23:57:19,http://luettgen.io/elizabeth,0.9752222880,108.86.52.112,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n8907,4,199,2017-02-22 08:00:18,http://emmerichankunding.io/colleen,0.1672225502,159.134.205.223,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n8908,4,199,2016-12-17 10:01:24,http://lind.biz/lavinia_franecki,0.9052647685,247.112.186.173,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n8909,4,199,2017-04-17 09:56:14,http://king.info/krystina_fay,0.9400359907,196.143.235.33,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n8910,4,199,2017-04-26 14:15:42,http://schuppe.net/jeanette,0.4429472720,247.41.63.144,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n8911,4,199,2017-04-12 04:20:34,http://lefflermohr.biz/dallas,0.0758749258,100.89.16.241,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n8912,4,199,2017-03-03 11:29:47,http://sauer.org/claude,0.5007537769,188.134.250.193,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n8913,4,200,2017-02-11 11:37:40,http://krisfunk.com/chyna,0.9868017792,192.159.111.249,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n8914,4,200,2017-01-08 16:22:43,http://considine.biz/newell,0.8540858332,108.30.221.95,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n8915,4,200,2016-12-29 04:05:09,http://wintheiser.net/mary_stiedemann,0.2027538603,127.82.134.118,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n8916,4,200,2017-04-18 04:14:02,http://corwin.com/eliezer,0.4859105104,98.195.48.220,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n8917,4,200,2017-04-24 14:26:51,http://dickenshintz.net/aniyah,0.8364392966,219.24.251.168,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n8918,4,200,2017-03-28 18:07:17,http://cremin.name/noemi,0.5397364043,220.50.231.4,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n8919,4,200,2017-01-28 06:09:26,http://dach.co/catharine.tillman,0.7892810243,30.193.74.133,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n8920,4,200,2017-04-30 05:14:36,http://raukuhlman.com/trenton_ko,0.3466791421,33.141.66.180,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n8921,4,200,2017-05-16 13:42:28,http://mcglynn.net/natalie_pacocha,0.1242810546,152.5.64.235,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n8922,4,200,2017-05-05 07:27:09,http://hudson.org/julianne_rowe,0.4562617110,206.60.110.60,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n8923,4,200,2016-12-18 15:28:29,http://buckridge.info/vivien,0.0159612831,114.75.252.61,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n8924,4,200,2016-12-25 19:55:27,http://romaguera.biz/porter.casper,0.0103996868,120.41.67.251,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n8925,4,200,2017-03-29 22:20:21,http://rolfsonorn.biz/germaine_willms,0.2905568949,242.252.182.29,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n8926,4,200,2017-03-22 05:44:48,http://sanford.io/kolby.feil,0.9276146908,174.140.105.146,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n8927,4,200,2016-12-13 13:24:48,http://kiehngottlieb.name/alexis.greenholt,0.6472984636,231.193.250.6,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n8928,4,200,2017-02-26 14:39:46,http://muellertreutel.co/meagan_feeney,0.7247556074,231.164.141.225,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n8929,4,200,2017-05-10 13:44:49,http://hegmann.info/lila.nader,0.6366875602,225.235.102.239,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n8930,4,200,2017-04-20 05:52:08,http://mannreynolds.org/eve,0.8859466110,154.131.187.176,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n8931,4,200,2017-02-26 13:17:28,http://von.co/lulu.altenwerth,0.2840902850,71.188.176.77,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n8932,4,200,2017-02-01 04:31:33,http://johnson.co/derick_kunde,0.0076819992,213.117.4.22,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n8933,4,200,2017-03-02 01:29:02,http://muller.name/krystel,0.6000433657,207.171.234.228,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n8934,4,200,2017-01-06 15:50:41,http://hellerhowell.info/rickey,0.1538772415,219.12.167.150,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n8935,4,200,2017-05-24 00:38:01,http://jerdequigley.io/oda,0.4239317426,106.13.251.95,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n8936,4,200,2017-04-28 03:29:50,http://kozey.io/carmine_lesch,0.2452819805,9.42.162.59,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n8937,4,200,2017-02-11 17:39:05,http://wunschoconnell.com/pearline_cartwright,0.1373539715,141.231.11.96,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n5870,3,128,2017-06-13 10:47:14,http://kuvalitrosin.com/chadrick_murphy,,108.241.47.103,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n5871,3,128,2016-12-17 18:06:56,http://heidenreichgulgowski.co/alta,,193.168.113.226,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n5872,3,128,2017-06-11 08:45:01,http://greenfelder.biz/kathryn,,94.77.246.234,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n5873,3,128,2017-06-04 15:35:29,http://predovic.biz/gielle.romaguera,,45.3.203.95,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n5874,3,128,2017-06-06 17:25:08,http://treutelprosacco.net/devin,,134.91.23.110,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n5875,3,128,2017-04-09 09:51:12,http://schamberger.co/wilhelmine.wuckert,,72.156.224.97,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n5876,3,128,2017-05-30 23:08:00,http://mayertlueilwitz.name/domenico,,85.42.140.121,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n5877,3,128,2017-02-26 19:43:10,http://gutmann.co/joy_balistreri,,112.95.130.243,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n5878,3,128,2017-05-31 00:19:23,http://hilll.io/zena,,84.13.126.226,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n5879,3,128,2017-02-06 20:37:46,http://schultzmcdermott.info/kaleigh,,116.110.252.226,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n5880,3,128,2017-05-25 18:23:13,http://hermistongrimes.info/noelia,,205.190.199.233,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n5881,3,128,2017-06-09 13:16:59,http://greenfeldercartwright.io/loyal_goldner,,212.29.184.225,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n5882,3,128,2017-01-18 20:48:36,http://sporer.co/granville,,115.193.43.30,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n5883,3,128,2017-03-25 23:40:06,http://schuppe.com/rusty.hirthe,,29.151.81.45,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n5884,3,128,2016-12-30 01:24:32,http://kautzer.name/wava_hudson,,183.193.41.143,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n5885,3,128,2017-05-25 13:31:31,http://blockmarvin.biz/suzanne_beatty,,83.151.250.236,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n5886,3,128,2017-04-24 21:33:42,http://mrazvandervort.net/raphaelle,,175.202.240.56,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n5887,3,128,2017-05-29 00:43:22,http://kiehn.io/elta,,110.31.172.95,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n5888,3,128,2017-05-24 15:31:21,http://hansenpredovic.co/jordan.jacobs,,75.68.168.130,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n5889,3,128,2017-05-17 06:42:21,http://powlowskiblock.org/don,,223.150.237.97,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n5890,3,128,2017-05-12 23:52:25,http://mante.info/kaandra,,181.104.73.80,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n5891,3,128,2017-03-27 09:00:25,http://schoenmetz.net/elinor,,185.10.237.250,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n5892,3,128,2017-05-14 01:26:53,http://dibbertjohnston.net/te,,51.29.254.165,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n5893,3,128,2017-05-21 22:38:08,http://torp.io/kayden,,11.81.193.58,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n5894,3,128,2016-12-23 13:28:28,http://ruel.name/bonnie,,19.9.173.178,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n5895,3,128,2017-06-11 11:57:16,http://gibson.biz/arianna_huel,,77.121.122.153,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n5896,3,128,2017-02-24 06:35:50,http://macejkovic.name/malcolm_koch,,14.110.130.206,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n5897,3,128,2017-01-20 03:43:18,http://pfannerstill.org/coleman,,152.100.138.79,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n5898,3,128,2017-02-22 11:59:42,http://witting.org/vernie_denesik,,141.170.22.82,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n5899,3,128,2017-01-23 12:25:31,http://bechtelar.co/wilhelmine_haag,,243.224.167.41,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n5900,3,128,2017-05-27 17:15:42,http://bergstrom.name/ally,,163.57.126.241,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n5901,3,128,2017-05-19 04:16:53,http://reichertgoyette.net/cory,,229.174.124.122,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n5902,3,128,2017-02-19 02:18:41,http://homenick.net/mabel.batz,,20.90.216.209,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n5903,3,128,2017-01-14 19:23:26,http://quitzon.org/lucinda,,187.8.177.13,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n5904,3,128,2017-01-22 10:46:50,http://marvin.info/moses,,85.181.39.92,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n5905,3,128,2016-12-14 23:34:01,http://oreilly.org/emelie,,194.119.193.228,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n5906,3,128,2017-04-07 21:29:52,http://breitenberg.com/savanah,,176.100.50.231,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n5907,3,128,2017-05-03 09:09:41,http://crist.io/evangeline,,174.162.17.6,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n5908,3,128,2017-04-27 19:28:05,http://kuvalis.com/tanya,,122.166.148.207,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n5909,3,128,2017-04-18 10:29:02,http://abshire.net/miles,,177.5.74.58,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n5910,3,128,2017-02-09 19:34:23,http://dubuque.org/dawson,,201.193.212.93,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n5911,3,129,2017-03-16 21:28:21,http://kunde.co/jacques,,250.86.25.38,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n5912,3,129,2017-02-15 05:22:18,http://gibson.org/verlie_crooks,,134.107.127.32,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n5913,3,129,2017-04-12 09:57:18,http://gislasonleuschke.co/dayna,,112.52.111.246,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n5914,3,129,2017-05-09 13:09:07,http://satterfield.net/briana.metz,,145.178.27.26,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n5915,3,129,2017-01-02 23:13:15,http://emmerich.org/arlo_rodriguez,,190.229.212.181,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n5916,3,129,2017-03-19 02:26:18,http://gerhold.name/declan.hilpert,,113.85.38.56,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n5917,3,129,2017-01-10 12:20:06,http://kutch.name/dianna,,63.10.23.107,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n5918,3,129,2017-03-09 23:21:38,http://gulgowskidurgan.co/madeline,,149.190.130.23,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n5919,3,129,2017-04-20 12:19:31,http://welchhansen.name/dannie,,14.126.56.193,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n5920,3,129,2017-04-15 00:23:19,http://lesch.info/leola.wiegand,,53.111.2.40,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n5921,3,129,2017-04-05 19:52:53,http://stark.name/consuelo.fadel,,140.31.60.102,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n5922,3,129,2017-04-04 10:26:54,http://weber.com/abigail,,219.44.117.88,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n5923,3,129,2017-06-02 02:10:40,http://schambergermueller.net/zora.roob,,49.179.12.51,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n5924,3,129,2017-05-01 09:42:41,http://spencer.biz/mya,,178.73.190.134,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n5925,3,129,2017-02-06 06:32:14,http://jakubowski.name/drew,,73.237.205.159,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n11803,5,264,2017-04-07 01:15:52,http://collins.org/marta,0.5092112511,170.109.126.166,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n11804,5,264,2017-04-09 17:07:13,http://daniel.net/rodolfo,0.4705072877,149.76.60.11,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n11805,5,264,2017-05-31 04:12:51,http://carter.org/queen,0.8897180847,118.12.139.105,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n11806,5,264,2016-12-25 00:47:05,http://hackettokon.info/easter.pfannerstill,0.7276331323,109.91.82.84,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n11807,5,264,2017-01-24 17:21:29,http://moenfriesen.com/twila.turcotte,0.6990072158,157.62.29.240,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n11808,5,264,2016-12-21 21:19:53,http://feest.biz/sasha_rosenbaum,0.9242724067,150.140.204.252,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n11809,5,264,2017-06-14 02:07:37,http://fritsch.io/bernie,0.3992487994,60.173.24.178,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n11810,5,264,2017-03-05 19:10:33,http://oconner.io/myah,0.3722569298,18.218.41.139,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n11811,5,264,2017-04-26 18:02:56,http://ebert.org/van,0.2569907847,224.142.76.83,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n11812,5,264,2017-02-18 01:01:37,http://mckenzie.co/magdalena_halvorson,0.4464662152,2.147.211.206,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n11813,5,264,2017-01-17 06:19:29,http://balistreridietrich.com/ernest,0.1178537319,244.70.160.134,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n11814,5,264,2017-05-02 19:21:31,http://gibson.io/karl.conroy,0.5524511255,133.137.114.114,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n11815,5,264,2017-03-03 03:56:30,http://mcclure.net/toni_abernathy,0.9636599314,144.135.145.156,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n11816,5,264,2017-05-13 03:55:02,http://ratke.org/terrence,0.9665444428,50.169.5.126,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n11817,5,264,2017-04-20 18:38:49,http://huelrosenbaum.com/matteo_herzog,0.3740746129,33.40.134.16,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n11818,5,264,2017-05-17 18:34:43,http://mayerjohnson.com/pierre.schneider,0.0888158790,111.4.230.141,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n11819,5,264,2017-05-17 09:19:56,http://yostfeil.name/vesta,0.3206051294,55.218.132.231,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n11820,5,264,2016-12-28 16:27:22,http://marquardt.io/jarrell,0.9625616449,90.251.190.148,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n11821,5,264,2017-06-08 19:55:38,http://mayer.net/cecelia.turner,0.4121764044,185.194.43.148,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n11822,5,264,2016-12-14 15:52:30,http://dickinsongutkowski.io/burnice_kulas,0.9288447098,210.100.159.19,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n11823,5,264,2017-02-10 11:00:39,http://farrellbechtelar.name/hipolito_cain,0.6572692414,70.43.250.102,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n11824,5,264,2017-01-13 03:01:45,http://bradtkerippin.org/general.kuhlman,0.0499739102,70.28.161.218,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n11825,5,264,2017-03-10 00:47:42,http://oconnercummerata.org/shirley.predovic,0.3286439957,217.53.167.46,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n11826,5,264,2017-05-14 06:08:05,http://kohler.co/callie,0.5543270117,44.232.50.34,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n11827,5,264,2017-04-24 22:37:18,http://jacobi.com/paxton.shields,0.3844232620,24.24.199.209,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n11828,5,264,2017-02-11 01:37:56,http://pouroswindler.io/elmore.reichel,0.9084581637,67.155.2.48,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n11829,5,264,2017-04-12 09:22:50,http://mitchellcollins.info/rudolph_roberts,0.8678939228,72.110.68.102,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n11830,5,264,2017-04-09 16:57:53,http://crona.io/kale_breitenberg,0.7337803856,23.114.236.213,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n11831,5,264,2017-01-07 17:14:40,http://bogisich.com/jerald,0.6105300849,228.26.122.51,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n11832,5,264,2016-12-14 23:42:50,http://carrollstroman.org/dasia,0.1750003859,110.162.166.184,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n11833,5,264,2017-02-09 20:35:02,http://nicolasankunding.biz/dudley_larson,0.3962373414,97.129.202.7,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n11834,5,264,2017-01-03 09:50:42,http://dach.io/adelle,0.5130153850,235.31.153.237,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n11835,5,264,2017-01-01 18:13:44,http://grady.info/marcia.walsh,0.3271548493,83.66.140.53,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n11836,5,264,2017-02-26 06:05:39,http://hettinger.io/orland,0.6142208273,250.165.63.96,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n11837,5,264,2017-03-12 05:52:31,http://reichel.biz/magnolia.hackett,0.8056922867,76.236.74.235,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n11838,5,264,2017-05-24 01:47:56,http://veum.co/darby,0.2873974556,10.151.64.229,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n11839,5,264,2017-03-08 20:14:12,http://herzog.net/alexa_weinat,0.7614394208,189.232.130.11,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n11840,5,264,2017-01-28 10:48:43,http://boscookuneva.biz/terry,0.0771578786,242.35.119.54,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n11841,5,264,2017-03-30 05:06:33,http://gaylordkohler.org/pascale_predovic,0.2769083246,17.99.247.192,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n11842,5,265,2017-04-12 16:02:35,http://gerhold.net/dejuan.anderson,0.8444568189,69.146.179.63,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n11843,5,265,2017-02-01 12:45:02,http://connheel.info/hazle,0.0430238197,102.93.51.251,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n11844,5,265,2017-03-27 00:22:55,http://robertslockman.io/matteo.heller,0.8495660591,67.45.139.87,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n11845,5,265,2016-12-28 21:00:57,http://larkinborer.org/ladarius,0.1428910138,168.59.186.35,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n11846,5,265,2017-03-09 09:35:40,http://rowe.name/nichole_weber,0.2415414949,184.108.93.53,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n11847,5,265,2017-02-20 23:02:03,http://ornhand.co/laron,0.0977885918,152.48.109.120,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n11848,5,265,2017-06-13 01:58:42,http://schuster.org/patricia_schimmel,0.8064989147,189.87.216.55,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n11849,5,265,2017-01-17 13:05:19,http://reichel.name/morton_quigley,0.1882777781,250.74.246.155,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n11850,5,265,2017-04-22 00:00:02,http://wisoky.io/rudolph_smitham,0.1331254577,106.77.219.204,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n11851,5,265,2017-05-06 21:18:27,http://gleason.info/francis_morar,0.4049434841,93.83.249.37,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n11852,5,265,2017-01-24 07:14:44,http://torp.com/thad,0.5691784122,241.195.93.72,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n8938,4,200,2017-06-09 22:53:12,http://okeefe.biz/fritz_stiedemann,0.9042114293,105.68.181.199,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n8939,4,200,2017-04-18 11:43:50,http://wolf.co/rowan.damore,0.4099718946,159.74.22.68,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n8940,4,201,2017-02-21 20:17:31,http://koepp.co/junius,0.3361907255,223.242.146.98,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n8941,4,201,2017-02-01 21:25:31,http://mann.co/keely_huels,0.2578809864,245.168.56.77,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n8942,4,201,2017-04-01 03:35:37,http://lakin.name/alfreda,0.4026363870,160.55.82.42,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n8943,4,201,2017-01-19 21:16:47,http://sauer.biz/lazaro,0.7132542221,99.73.153.187,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n8944,4,201,2017-03-16 06:01:21,http://hanewitting.info/lizeth.orn,0.6391945721,207.164.114.203,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n8945,4,201,2016-12-23 14:30:13,http://howellhermiston.net/alexandre,0.9959124371,47.123.238.76,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n8946,4,201,2017-01-17 13:28:38,http://johnston.co/akeem,0.5668473804,48.174.74.249,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n8947,4,201,2017-04-09 21:00:46,http://kshlerin.net/timmy,0.9359064478,236.67.185.69,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n8948,4,201,2017-03-14 11:00:59,http://turner.net/donato.hamill,0.6776376900,149.30.19.195,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n8949,4,201,2017-01-16 22:28:33,http://hauck.biz/lon.funk,0.6269355054,131.193.32.179,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n8950,4,201,2017-03-15 07:36:33,http://fritsch.name/deron_green,0.9810399711,163.178.126.180,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n8951,4,201,2017-05-30 06:43:21,http://zboncak.com/ernie_hane,0.6465781846,179.211.41.200,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n8952,4,201,2017-02-23 15:33:02,http://hoeger.co/mohammad_adams,0.2439910650,124.133.40.247,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n8953,4,201,2017-05-05 05:08:10,http://langworthjohnston.biz/sheldon,0.5329562055,98.62.193.252,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n8954,4,201,2016-12-25 20:54:16,http://mcclurekuphal.info/aracely_smitham,0.8850597086,16.143.76.246,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n8955,4,201,2017-03-09 03:52:20,http://langosh.com/jedidiah_carter,0.3057327534,232.246.85.10,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n8956,4,201,2017-01-09 19:31:31,http://thiel.net/brent,0.9988888548,228.215.116.150,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n8957,4,201,2017-02-19 00:34:39,http://klingbrekke.co/aleen_kiehn,0.4870008507,31.83.139.103,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n8958,4,201,2017-01-22 17:17:16,http://satterfield.info/antonetta.stroman,0.3101512536,221.223.70.108,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n8959,4,201,2017-04-01 07:19:22,http://lesch.info/kale_spinka,0.9587893650,73.234.233.211,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n8960,4,201,2017-02-11 09:38:56,http://ward.com/kelli_stehr,0.6901307704,135.122.226.127,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n8961,4,201,2017-03-18 09:03:36,http://gerholdcasper.net/ethan_ruecker,0.9397766450,252.154.122.236,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n8962,4,201,2017-01-12 14:13:12,http://predovic.net/oleta,0.8902798469,215.112.21.228,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n8963,4,201,2017-02-22 15:42:07,http://marvin.name/tony_franecki,0.0130136231,51.19.65.215,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n8964,4,201,2017-05-16 04:38:23,http://blick.info/jabari,0.9294544341,233.151.143.221,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n8965,4,201,2017-05-19 14:48:42,http://shanahan.org/imani.willms,0.4976632911,166.65.241.232,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n8966,4,201,2017-05-18 09:56:53,http://parisian.name/stephania.hayes,0.9032666234,146.145.15.135,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n8967,4,202,2017-06-05 23:02:19,http://berge.biz/amanda,0.9302212510,252.24.157.213,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n8968,4,202,2017-02-20 17:18:08,http://gleichner.org/bryce,0.3455450368,77.232.63.43,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n8969,4,202,2017-06-11 04:09:56,http://marks.name/justen,0.7528374472,113.8.75.187,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n8970,4,202,2017-06-12 02:15:53,http://nikolaus.name/devonte,0.6114518275,38.139.205.5,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n8971,4,202,2017-02-06 05:19:25,http://bosco.info/germaine,0.8981378032,135.176.11.67,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n8972,4,202,2017-05-23 11:56:37,http://waelchi.name/bertrand,0.5610399436,182.136.215.53,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n8973,4,202,2017-05-25 01:43:11,http://sanford.name/jaylin_mraz,0.8082886509,71.97.225.156,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n8974,4,202,2017-03-07 14:03:52,http://koelpin.co/susanna.gaylord,0.4098374336,243.148.78.208,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n8975,4,202,2017-02-13 10:58:54,http://abbott.co/israel_klein,0.2053049629,116.219.76.223,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n8976,4,202,2017-05-05 18:21:38,http://abernathy.biz/araceli,0.0174805890,51.208.122.154,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n8977,4,202,2017-05-17 14:12:14,http://lindcorkery.info/rusty,0.3171729814,209.34.115.234,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n8978,4,202,2017-02-17 03:33:11,http://farrellwintheiser.co/santino_ledner,0.2093625735,150.127.36.235,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n8979,4,202,2017-04-19 23:04:52,http://abshire.org/lucio_blanda,0.6023417124,11.9.88.139,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n8980,4,202,2017-05-08 12:59:16,http://lehnerwaelchi.name/heidi,0.9199542922,227.128.142.98,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n8981,4,202,2017-04-18 12:50:44,http://connelly.name/leopold_smitham,0.5440987643,9.74.45.159,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n8982,4,202,2017-02-01 04:07:07,http://yostnitzsche.io/kamille,0.3938259813,113.40.175.251,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n8983,4,202,2017-02-07 21:49:45,http://jacobson.com/trystan.wiegand,0.9542054127,231.175.167.138,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n8984,4,202,2017-01-11 02:35:30,http://barton.biz/yvonne.dooley,0.2493037519,169.76.160.254,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n8985,4,202,2017-03-17 10:11:01,http://hicklekemmer.net/deshawn,0.6294770458,152.38.142.162,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n8986,4,202,2016-12-15 16:39:09,http://caspercollier.com/nicholaus_gorczany,0.2120287345,141.52.9.223,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n8987,4,202,2017-04-04 06:40:19,http://stark.name/damien,0.1271790707,252.161.143.225,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n8988,4,202,2017-02-04 11:19:35,http://macgyver.name/elroy,0.0521492665,74.222.229.69,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n8989,4,202,2017-04-13 05:06:24,http://lakin.net/frank_emard,0.6224848020,246.179.166.135,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n5926,3,129,2017-06-03 06:10:05,http://brakus.biz/keven,,198.2.7.95,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n5927,3,129,2017-05-15 12:32:25,http://oberbrunner.name/abigail_kunze,,197.53.209.107,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n5928,3,129,2017-01-09 12:56:44,http://dooley.org/austyn,,158.168.104.53,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n5929,3,129,2017-03-13 11:03:43,http://rice.co/anastasia,,78.190.208.75,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n5930,3,129,2017-01-29 20:43:59,http://gutkowskiupton.net/hayden_hermiston,,98.245.165.244,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n5931,3,129,2017-03-08 23:38:30,http://tromp.org/lew,,100.48.125.72,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n5932,3,129,2017-04-22 17:38:39,http://stiedemannkling.io/candido.kunde,,61.212.99.144,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n5933,3,129,2017-03-05 06:08:02,http://armstrong.io/nicholas_cremin,,152.191.40.195,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n5934,3,129,2017-01-30 05:07:18,http://bernierruel.io/fredrick,,144.142.204.169,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n5935,3,129,2017-06-01 06:07:41,http://kirlin.org/winfield,,172.232.148.208,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n5936,3,129,2017-04-15 09:20:35,http://cainharris.io/cali.miller,,38.33.26.96,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n5937,3,129,2017-02-12 18:17:59,http://carroll.biz/mitchell,,131.54.76.24,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n5938,3,129,2016-12-30 21:51:19,http://hoeger.net/markus,,24.138.154.4,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n5939,3,129,2017-04-08 05:19:45,http://labadieklocko.org/ubaldo.nader,,149.230.191.118,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n5940,3,129,2017-04-22 13:05:49,http://price.name/giovanni_buckridge,,231.217.5.229,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n5941,3,129,2017-01-12 22:49:55,http://baumbachcorwin.co/alfonzo,,32.179.33.4,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n5942,3,129,2017-01-24 23:36:34,http://boyer.name/kamren.zieme,,215.203.184.70,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n5943,3,129,2017-03-03 21:11:06,http://hickle.org/bria,,62.73.42.210,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n5944,3,129,2017-06-01 20:53:39,http://kleinschimmel.io/hugh.gusikowski,,165.213.138.97,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n5945,3,129,2017-04-15 23:53:20,http://sawayn.biz/hans_medhurst,,142.236.151.91,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n5946,3,129,2017-01-08 21:40:02,http://lindgren.com/cayla,,254.123.30.132,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n5947,3,129,2017-01-19 02:37:03,http://schadendoyle.io/wilmer.wilkinson,,75.29.4.229,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n5948,3,129,2017-06-08 12:41:25,http://larkin.com/max_sauer,,78.94.13.117,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n5949,3,129,2017-03-15 13:12:10,http://champlinabernathy.biz/arthur,,60.249.150.31,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n5950,3,129,2017-04-17 22:24:07,http://ortiz.net/idella,,2.50.57.212,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n5951,3,129,2017-04-22 12:00:42,http://kohler.info/golda,,238.211.80.219,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n5952,3,129,2017-03-17 14:03:47,http://barton.io/jeanie,,239.215.176.125,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n5953,3,129,2017-03-28 16:20:22,http://smith.name/ozella,,27.219.165.15,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n5954,3,129,2017-05-23 18:24:29,http://adams.io/gerry,,72.83.41.29,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n5955,3,129,2017-05-29 06:55:40,http://cristokuneva.name/kiara,,125.246.196.90,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n5956,3,129,2017-03-17 06:26:04,http://berge.info/delia,,160.126.4.21,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n5957,3,129,2017-01-05 13:37:41,http://torp.com/rodolfo.dicki,,81.237.52.84,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n5958,3,129,2017-06-05 07:49:10,http://braun.biz/scot_hane,,63.105.24.208,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n5959,3,129,2016-12-13 08:22:14,http://kuhn.io/jovanny_konopelski,,143.3.135.235,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n5960,3,129,2017-05-05 03:51:47,http://morar.biz/angeline,,51.184.6.138,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n5961,3,129,2017-01-11 17:36:29,http://windler.co/arden.oberbrunner,,14.171.59.219,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n5962,3,129,2017-06-01 20:56:14,http://kirlindach.name/jarret_sipes,,17.223.39.132,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n5963,3,129,2016-12-16 04:23:42,http://jacobi.org/leora,,47.114.178.124,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n5964,3,129,2017-03-14 16:35:21,http://pagacgreen.biz/jaydon.boyer,,243.130.209.108,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n5965,3,129,2017-06-13 10:49:52,http://wolff.net/emmet,,183.53.168.11,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n5966,3,129,2017-04-23 01:35:34,http://gibsonhermiston.co/marquise_corwin,,226.67.77.219,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n5967,3,129,2017-02-25 03:25:14,http://west.net/naomie,,239.228.79.152,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n5968,3,129,2017-02-28 07:35:11,http://rippin.co/demond,,113.250.78.227,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n5969,3,129,2017-05-03 11:34:25,http://wintheiser.name/chris_kilback,,85.233.184.237,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n5970,3,129,2017-05-29 13:08:43,http://koepp.net/cora,,146.168.191.142,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n5971,3,129,2017-03-03 21:16:53,http://kaulkewill.com/jerel,,156.17.19.231,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n5972,3,129,2017-05-11 09:55:05,http://gottlieb.info/christy,,205.98.206.237,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n5973,3,129,2017-05-26 16:15:51,http://fisherorn.io/kevon.little,,103.199.48.8,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n5974,3,129,2017-01-22 01:53:11,http://mante.net/christian_reinger,,119.41.194.44,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n5975,3,129,2017-03-19 09:04:02,http://dietrich.info/cornell_lesch,,152.253.178.190,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n5976,3,129,2017-02-02 06:39:54,http://hansen.name/raphael,,165.198.5.172,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n5977,3,129,2017-05-22 20:48:30,http://hermistongulgowski.biz/sebastian,,27.235.235.131,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n5978,3,129,2017-05-01 01:27:45,http://maggiolehner.org/tyrese,,214.72.36.110,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n5979,3,130,2017-03-21 21:25:39,http://harris.org/lucienne.skiles,,88.152.130.202,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n5980,3,130,2017-02-28 05:59:19,http://mertzpfannerstill.io/michele,,41.159.202.35,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n5981,3,130,2017-02-09 08:12:06,http://wolfrath.net/joel_kuvalis,,164.69.201.39,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n11853,5,265,2017-02-19 09:50:27,http://pacochawaelchi.info/briana.durgan,0.5202608552,124.94.41.14,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n11854,5,265,2017-03-04 19:17:08,http://mannferry.info/boyd_leffler,0.1254577793,210.101.180.90,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n11855,5,265,2017-02-03 09:47:31,http://bradtkewelch.org/sherwood_ohara,0.4146630130,88.53.3.32,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n11856,5,265,2017-03-17 22:23:01,http://heaney.biz/nannie.bashirian,0.8082636923,89.146.186.107,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n11857,5,265,2017-05-16 03:39:50,http://trompjohns.biz/evie,0.7063900754,206.207.38.226,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n11858,5,265,2017-01-04 09:14:34,http://reilly.biz/benton,0.2344035831,71.218.16.77,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n11859,5,265,2017-04-03 17:18:09,http://pfannerstillcruickshank.name/monserrat_larson,0.8732617190,171.237.41.61,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n11860,5,265,2016-12-25 21:07:54,http://gerhold.net/ola,0.3029357011,235.134.252.46,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n11861,5,265,2016-12-25 16:21:38,http://kiehn.biz/bernadine,0.5869571235,161.233.38.14,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n11862,5,265,2017-04-14 07:01:29,http://kuvaliskertzmann.net/aryanna,0.2781810805,33.71.216.109,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n11863,5,265,2017-01-18 02:03:12,http://lindreichel.co/jaida,0.3332937234,54.110.216.11,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n11864,5,265,2017-01-16 23:19:54,http://kris.com/madalyn,0.0184447562,156.178.99.112,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n11865,5,265,2017-05-08 07:59:01,http://rathbahringer.co/dayton,0.5825414017,120.109.185.19,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n11866,5,265,2017-03-06 04:11:09,http://runolfon.org/bridget,0.2113803616,249.34.95.56,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n11867,5,265,2017-01-17 07:17:16,http://kunze.net/rebekah.zulauf,0.0540986531,147.184.38.206,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n11868,5,265,2017-06-04 18:38:20,http://stiedemann.org/ivah_hintz,0.5909273565,79.92.202.124,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n11869,5,265,2017-01-16 22:20:12,http://macejkovic.info/adelia,0.2741205832,78.33.99.52,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n11870,5,265,2017-02-21 04:16:13,http://weimann.name/blaze,0.3272367807,126.246.183.11,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n11871,5,265,2017-03-25 09:47:37,http://casperschmitt.org/asha_schultz,0.1036787605,202.222.54.153,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n11872,5,266,2017-05-24 04:55:09,http://bechtelar.net/jalon,0.3533988179,54.100.79.148,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n11873,5,266,2016-12-24 15:22:32,http://hintz.co/kamille,0.5197254803,205.218.20.118,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n11874,5,266,2017-05-17 09:05:22,http://spencerwalsh.info/maximillian,0.9826143939,102.168.210.52,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n11875,5,266,2017-04-26 01:07:01,http://tremblay.co/chadd,0.1106894504,109.231.171.166,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n11876,5,266,2017-03-07 05:20:57,http://simonis.io/yoshiko,0.0842173598,245.242.9.195,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n11877,5,266,2017-01-28 09:31:53,http://auer.org/anais_moore,0.1652019584,177.243.231.136,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n11878,5,266,2017-01-20 13:59:31,http://bins.co/libby,0.9688937562,197.143.127.108,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n11879,5,266,2017-03-07 10:17:09,http://bruen.info/pinkie,0.9117557075,24.145.147.50,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n11880,5,266,2017-01-30 04:00:59,http://fay.net/bonita,0.1626124429,230.206.230.241,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n11881,5,266,2017-04-24 08:40:05,http://blickrodriguez.co/andrew,0.2173005821,185.33.211.161,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n11882,5,266,2017-01-27 19:22:15,http://hintz.org/danielle.wolf,0.6722116571,203.234.173.82,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n11883,5,266,2017-04-29 17:12:31,http://ruecker.co/amos_shanahan,0.5494990482,196.135.94.71,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n11884,5,266,2017-01-20 12:09:57,http://volkmanmurphy.co/merritt,0.2760339899,135.106.101.25,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n11885,5,266,2017-01-29 19:16:52,http://lebsack.com/douglas,0.5645589625,42.173.5.212,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n11886,5,266,2017-01-15 03:15:04,http://schamberger.net/karli,0.2529970953,71.41.213.81,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n11887,5,266,2017-05-06 02:33:13,http://hackett.info/magdalen,0.9760795290,146.31.33.247,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n11888,5,266,2017-05-31 04:07:38,http://cartwright.net/shaun,0.4154398525,248.38.79.91,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n11889,5,266,2016-12-18 10:31:51,http://bernierpagac.com/lenny,0.6750198749,14.17.205.72,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n11890,5,266,2017-02-25 08:27:55,http://labadiemaggio.biz/tom_dibbert,0.2625525453,53.134.183.87,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n11891,5,266,2017-04-14 16:20:17,http://ziemannkoch.biz/shad_gusikowski,0.9207830607,82.91.128.159,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n11892,5,266,2017-01-25 07:08:18,http://littleskiles.name/paris,0.3513102021,169.60.254.52,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n11893,5,266,2017-06-01 13:30:14,http://beahan.net/pablo_stehr,0.1118792376,63.110.183.74,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n11894,5,266,2017-04-13 13:59:48,http://harbergerlach.org/tanya,0.7964352896,254.12.31.69,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n11895,5,266,2017-06-13 00:16:47,http://mcdermott.net/taurean.jacobi,0.3393810357,203.205.207.228,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n11896,5,266,2017-05-12 05:23:22,http://pollich.name/jermaine.bosco,0.3619880089,46.147.21.116,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n11897,5,266,2017-06-10 21:56:45,http://emmerich.org/dejon,0.8222048029,191.153.239.203,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n11898,5,266,2016-12-25 11:35:55,http://armstrong.com/eden,0.7952813383,82.94.196.146,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n11899,5,266,2017-06-01 13:51:30,http://cainwisozk.info/mireya.koelpin,0.1580573980,196.120.226.78,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n11900,5,266,2016-12-26 07:33:07,http://schaefer.co/bernie,0.3730441288,118.72.240.110,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n11901,5,266,2017-05-10 16:50:06,http://franecki.info/shanny,0.3363766351,4.240.102.26,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n11902,5,266,2017-02-14 03:53:27,http://kilback.co/jeramy.fadel,0.5191199105,57.240.224.120,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n11903,5,266,2017-05-01 17:19:32,http://macgyver.info/dasia_boyle,0.9510133266,81.208.5.237,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n11904,5,266,2017-03-27 22:54:17,http://leuschkezemlak.org/arjun,0.8991342255,72.99.228.35,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n8990,4,202,2017-06-05 05:56:59,http://willmsratke.name/roslyn,0.0858122439,120.193.145.38,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n8991,4,202,2017-02-03 11:23:05,http://prosaccokreiger.com/carmine,0.6440021014,37.164.144.118,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n8992,4,202,2016-12-16 20:38:38,http://williamsonflatley.biz/syble,0.4136741186,55.41.79.194,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n8993,4,202,2017-06-05 12:49:55,http://abernathy.com/lela,0.5590893278,20.209.176.179,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n8994,4,202,2016-12-25 14:17:50,http://parisianhintz.com/jaylan.hackett,0.7871529418,36.124.65.42,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n8995,4,202,2017-03-04 17:42:18,http://hamilltreutel.com/ahmad,0.2901478944,201.11.237.191,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n8996,4,202,2017-06-05 08:59:12,http://veum.com/reid,0.0919036992,8.22.66.158,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n8997,4,202,2016-12-15 04:58:57,http://kerlukeprosacco.io/erika_vandervort,0.7384921110,68.134.104.32,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n8998,4,202,2017-03-05 19:29:26,http://treutelmills.net/caie_purdy,0.2232696100,151.84.82.96,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n8999,4,202,2017-05-12 11:28:06,http://goodwin.net/asia.herman,0.0418368089,254.122.53.200,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n9000,4,202,2017-01-18 08:22:30,http://bechtelarlockman.io/maximillia,0.6891395862,136.105.209.153,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n9001,4,202,2017-04-08 07:54:55,http://strosin.co/modesto,0.0051938671,195.179.227.238,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n9002,4,202,2017-02-09 16:48:55,http://oreilly.co/ebony.dare,0.6468116459,157.122.6.156,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n9003,4,202,2017-01-05 19:39:36,http://spinka.name/rubye,0.9334324632,215.196.126.164,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n9004,4,202,2016-12-20 08:49:46,http://sporerfeeney.name/margot.fritsch,0.8036648590,167.166.83.232,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n9005,4,202,2017-04-08 16:16:05,http://larson.info/sadie.conroy,0.0298865630,199.209.162.73,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n9549,4,214,2017-02-17 13:18:14,http://boehm.info/cortez,,91.177.191.144,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n9006,4,202,2017-04-22 09:01:24,http://howebreitenberg.com/helga,0.1746734646,50.162.125.177,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n9007,4,202,2017-05-27 04:17:17,http://medhurstadams.net/tamara.kemmer,0.6822859470,119.35.170.116,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n9008,4,202,2017-02-06 13:25:16,http://mclaughlin.io/myrtis.legros,0.2875759054,77.115.5.240,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n9009,4,202,2017-01-09 21:35:13,http://oreilly.name/akeem.oconner,0.1857670898,194.81.146.60,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n9010,4,202,2017-06-12 15:46:07,http://zemlakhoppe.co/stephany,0.3621109135,70.133.69.213,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n9011,4,202,2017-04-30 07:28:53,http://reynolds.com/virgil.streich,0.4175791383,112.5.72.68,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n9012,4,202,2016-12-13 12:06:52,http://conn.info/cristal,0.3976183111,31.253.54.32,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n9013,4,202,2016-12-18 00:17:51,http://grant.biz/tyrese,0.5287466133,122.148.66.164,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n9014,4,202,2017-06-11 20:59:03,http://oberbrunner.io/garret_bins,0.9446460917,25.125.125.54,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n9015,4,202,2017-03-28 16:28:47,http://wunsch.io/iva.reichert,0.4994488150,86.49.144.137,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n9016,4,202,2017-05-13 02:38:00,http://rosenbaumstamm.io/aliya,0.2171280144,109.238.197.52,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n9017,4,202,2017-03-24 03:13:26,http://bauch.biz/mary_schroeder,0.2750063718,115.83.8.124,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n9018,4,202,2016-12-17 02:48:08,http://mertz.io/durward,0.9037435748,226.23.244.9,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n9019,4,202,2017-02-19 12:59:30,http://kirlincartwright.com/alta,0.6815282558,8.85.62.57,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n9020,4,202,2017-06-08 18:01:51,http://mertz.io/felicia,0.3851121654,68.123.243.250,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n9021,4,202,2017-04-20 07:44:27,http://hahn.biz/modesto,0.6118891850,66.221.244.132,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n9022,4,202,2017-01-04 10:26:45,http://ortiz.info/tanner,0.9417757011,63.247.223.146,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n9023,4,202,2017-01-09 09:41:11,http://trompkemmer.biz/margarita_barton,0.7242031121,22.192.134.146,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n9024,4,202,2017-04-23 05:25:57,http://wilderman.name/hope.yost,0.2887292067,210.45.105.128,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n9025,4,202,2017-01-27 23:41:17,http://willmsbayer.io/noe_witting,0.8025460902,111.204.222.63,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n9026,4,202,2017-06-01 00:21:20,http://upton.info/gerard_mcclure,0.4922519929,163.16.42.209,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n9027,4,202,2017-04-08 14:01:38,http://wilkinsonshanahan.org/jerrell,0.9900998673,197.147.220.50,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n9028,4,202,2017-01-16 07:19:45,http://pacochatorphy.biz/stephon,0.1649935122,205.73.160.229,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n9029,4,202,2016-12-19 19:38:22,http://ondricka.org/benedict_grant,0.7482612622,24.71.70.149,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n9030,4,202,2017-01-28 14:21:10,http://haley.org/idell_kuhlman,0.6853021437,222.232.104.202,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n9031,4,202,2016-12-27 01:38:28,http://zieme.name/rozella,0.6932023612,198.99.85.131,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n9032,4,203,2017-05-16 08:04:05,http://veum.io/ryann.rau,0.0167704913,195.94.217.217,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n9033,4,203,2017-02-09 01:49:00,http://leuschke.io/obie_rutherford,0.2399798459,75.35.151.64,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n9034,4,203,2017-01-31 00:03:57,http://strosin.biz/jaron,0.1635135243,190.103.31.203,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n9035,4,203,2016-12-29 23:56:33,http://schaefer.info/jaqueline,0.8630221451,196.116.253.85,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n9036,4,203,2017-02-04 01:54:32,http://gulgowski.org/aurelie.harris,0.1782974629,156.9.64.18,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n9037,4,203,2017-01-06 20:04:49,http://stamm.io/joanne_bins,0.8916993249,125.192.192.22,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n9038,4,203,2017-06-08 02:36:22,http://bednar.com/rylee_dubuque,0.1266266732,249.150.237.139,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n9039,4,203,2017-02-19 02:31:04,http://wehner.io/coty,0.7731078093,69.36.104.219,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n5982,3,130,2017-05-09 22:58:19,http://braun.info/elmer,,48.30.229.215,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n5983,3,130,2017-05-30 20:14:40,http://bahringer.net/ayden,,158.131.49.93,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n5984,3,130,2017-01-02 14:50:35,http://wiegandstokes.name/alejandra,,177.54.112.219,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n5985,3,130,2017-02-25 03:44:53,http://monahan.net/lillie.flatley,,80.54.26.152,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n5986,3,130,2017-05-11 13:39:27,http://dibbertdaugherty.biz/nathanael,,232.176.156.206,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n5987,3,130,2017-01-19 20:52:07,http://howellyost.name/roselyn.greenholt,,213.158.140.209,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n5988,3,130,2017-03-19 15:14:38,http://moorebode.io/bobbie,,159.225.19.24,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n5989,3,130,2017-01-18 22:19:59,http://zemlakweber.info/houston,,216.34.229.173,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n5990,3,130,2017-04-27 08:39:22,http://strosin.info/dominique,,254.173.171.74,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n5991,3,130,2017-04-03 21:43:01,http://goyetteruel.biz/broderick,,115.204.176.89,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n5992,3,130,2017-02-16 10:07:24,http://bergstromschmeler.io/van,,118.200.46.133,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n5993,3,130,2017-02-19 17:49:21,http://roberts.info/bert,,198.224.5.151,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n5994,3,130,2017-01-11 12:44:12,http://harrishyatt.name/wyman,,211.114.109.56,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n5995,3,130,2017-05-11 10:07:47,http://wittingdamore.name/bud_rath,,207.183.44.217,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n5996,3,130,2016-12-28 03:26:08,http://schaden.io/reanna.renner,,242.52.221.244,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n5997,3,130,2017-01-24 08:55:52,http://mckenziewintheiser.net/euna.grant,,41.141.26.249,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n5998,3,130,2017-06-06 17:55:49,http://schuppekozey.info/hollie.kling,,178.222.46.111,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n5999,3,130,2017-05-01 12:23:57,http://wuckert.co/phoebe,,219.243.74.128,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n6000,3,130,2017-05-08 02:17:16,http://barrowhields.name/avis,,210.218.224.93,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n6001,3,130,2017-03-24 23:30:36,http://cummingsroob.co/mireya.rodriguez,,196.107.162.232,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n6002,3,130,2017-06-06 19:32:38,http://whitekeler.biz/estevan.block,,174.241.253.18,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n6003,3,130,2017-03-06 19:33:10,http://predovicgottlieb.biz/claude.king,,221.29.144.166,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n6004,3,130,2017-02-24 12:22:52,http://leuschke.biz/reese,,34.145.27.166,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n6005,3,130,2017-05-10 07:27:24,http://wehner.org/samara_kris,,132.140.148.97,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n6006,3,130,2017-04-04 12:22:50,http://hicklevolkman.biz/ottis,,218.205.249.198,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n6007,3,130,2017-03-24 22:51:25,http://upton.io/leonie.gerlach,,142.115.219.164,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n6008,3,130,2017-04-26 23:32:40,http://hoppe.org/dolores,,36.102.37.137,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n6009,3,130,2017-05-21 07:10:55,http://dicki.name/emmie_beahan,,98.231.68.157,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n6010,3,130,2017-03-17 21:39:27,http://turner.io/angelica,,159.126.57.66,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n6011,3,130,2017-01-22 12:08:48,http://harvey.name/roger,,173.39.137.85,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n6012,3,130,2017-02-03 21:27:36,http://boscomurray.biz/moshe,,79.129.36.170,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n6013,3,130,2017-04-20 01:50:35,http://bechtelarwalker.org/velda,,217.36.85.81,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n6014,3,130,2017-03-30 18:38:31,http://dach.com/grace,,239.188.218.193,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n6015,3,131,2017-03-11 05:11:49,http://treutelkoch.biz/adolphus,,112.209.69.63,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n6016,3,131,2017-01-29 03:50:35,http://abernathy.io/billie,,43.87.66.46,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n6017,3,131,2017-05-11 19:43:19,http://lind.biz/bradley,,181.92.136.30,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n6018,3,131,2017-05-14 14:43:12,http://faydicki.name/jodie.davis,,23.6.196.63,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n6019,3,131,2017-01-28 07:14:51,http://harvey.org/cristina,,149.44.193.213,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n6020,3,131,2017-03-26 21:25:13,http://boehm.io/quinn_frami,,190.111.202.213,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n6021,3,131,2017-03-15 08:14:18,http://stiedemannhomenick.net/guy,,56.67.3.98,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n6022,3,131,2017-02-02 21:08:48,http://marks.com/alanis,,98.229.159.211,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n6023,3,131,2017-05-08 02:22:09,http://labadie.net/jaylin.pacocha,,197.94.159.119,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n6024,3,131,2017-03-13 07:13:49,http://donnelly.io/tyrese,,102.47.143.39,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n6025,3,131,2017-05-10 21:04:05,http://turcotte.name/tamara_koch,,23.86.247.148,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n6026,3,131,2017-04-24 05:12:19,http://will.info/arlene,,239.12.118.148,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n6027,3,131,2017-01-11 22:19:55,http://hickleanderson.co/abigail.kreiger,,73.50.23.80,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n6028,3,131,2017-06-12 02:18:28,http://cremin.com/celestino,,181.185.154.6,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n6029,3,131,2017-06-05 14:57:21,http://johnson.info/tyshawn.watsica,,87.68.195.35,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n6030,3,131,2017-05-04 23:38:55,http://price.co/henri_emard,,249.141.117.124,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n6031,3,131,2017-03-08 18:18:44,http://hand.net/katherine_gaylord,,149.133.43.209,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n6032,3,131,2017-04-17 06:23:25,http://nienow.net/archibald.altenwerth,,150.231.205.225,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n6033,3,131,2017-05-31 22:18:56,http://senger.net/hadley_carroll,,167.225.155.74,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n6034,3,131,2017-05-06 20:13:51,http://balistrerilockman.net/ruby,,161.155.3.236,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n6035,3,131,2017-05-08 13:19:44,http://schmelerconn.co/buddy.prohaska,,113.97.29.2,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n6036,3,131,2017-02-17 00:05:54,http://kuhn.net/emory.oreilly,,15.212.43.21,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n6037,3,131,2017-01-09 21:26:42,http://tremblay.biz/herbert,,109.12.47.27,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n11905,5,266,2017-02-04 05:14:26,http://stroman.biz/tyree,0.4045663378,51.161.154.182,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n11906,5,266,2017-04-13 11:27:43,http://oreilly.co/claude,0.7786988546,232.221.160.235,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n11907,5,266,2017-05-18 16:09:00,http://dibbert.biz/aliza,0.9521876302,51.175.69.208,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n11908,5,266,2017-04-24 18:41:39,http://doyle.com/norval,0.7110095699,95.198.42.52,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n11909,5,266,2017-03-29 05:05:07,http://prosacco.co/levi_ledner,0.6947770379,37.145.96.48,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n11910,5,266,2017-04-01 11:00:16,http://smith.co/josue,0.5399709413,74.205.48.10,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n11911,5,266,2017-02-20 14:43:17,http://lindgrenskiles.org/murphy.keeling,0.6076513774,244.79.250.44,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n11912,5,266,2017-05-14 01:09:25,http://collier.co/rachelle,0.1157078916,71.57.237.135,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n11913,5,266,2017-04-30 08:02:06,http://kris.name/westley.spencer,0.7217221606,145.27.239.118,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n11914,5,266,2017-06-12 07:11:46,http://rice.biz/elton,0.0920499357,232.28.151.86,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n11915,5,266,2017-06-05 00:07:28,http://kiehn.net/kadin.ritchie,0.9591546487,138.234.191.248,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n11916,5,266,2017-04-17 16:16:16,http://raynor.name/francesco,0.4782681370,199.165.46.2,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n11917,5,266,2017-03-23 02:04:59,http://dach.org/brycen,0.1930651800,207.197.140.212,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n11918,5,266,2017-01-22 18:52:34,http://conroylueilwitz.name/chase.reilly,0.9098380054,196.64.129.124,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n11919,5,266,2016-12-15 19:39:02,http://cummerata.net/idell_blick,0.4035263418,244.33.73.219,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n11920,5,266,2017-03-02 20:40:24,http://kautzer.net/margarita.moen,0.6065635639,160.243.58.252,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n11921,5,266,2017-04-21 18:33:25,http://kris.io/marguerite,0.9405963569,44.10.109.34,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n11922,5,266,2017-02-21 13:46:43,http://buckridge.io/myron.herman,0.7893794467,253.244.254.239,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n11923,5,266,2017-03-31 08:17:59,http://hudson.name/aunta_moore,0.4768288601,245.249.188.101,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n11924,5,266,2017-01-26 02:55:43,http://hyatt.biz/sage_prosacco,0.5445600675,145.130.57.251,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n11925,5,266,2017-04-08 06:51:32,http://okondickens.io/mauricio,0.1569363952,135.228.131.182,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n11926,5,266,2017-05-09 00:00:11,http://mills.name/jeramie_stroman,0.0789664822,23.194.56.72,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n11927,5,266,2017-02-16 10:18:43,http://jones.org/yoshiko_jacobi,0.1742015917,203.11.183.77,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n11928,5,267,2017-02-03 05:09:13,http://halvorsonspencer.co/leonora.yost,0.8921496120,239.229.238.147,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n11929,5,267,2017-04-13 10:08:13,http://runtelebsack.co/geraldine_hintz,0.4763687591,76.239.27.202,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n11930,5,267,2017-04-28 14:11:20,http://prosacco.org/roxane,0.9412549963,173.229.124.123,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n11931,5,267,2017-05-17 05:46:17,http://treutelheel.net/teagan_heaney,0.4586043847,71.32.183.177,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n11932,5,267,2017-03-13 13:30:25,http://ratke.com/craig,0.4269398300,142.152.216.151,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n11933,5,267,2016-12-23 23:04:42,http://sipes.org/madisen,0.7973064512,174.8.178.55,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n11934,5,267,2017-05-06 14:09:35,http://hauck.org/arvid.shields,0.6349922132,3.13.50.113,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n11935,5,267,2017-03-05 14:41:37,http://friesen.io/krystel.hoppe,0.8471922139,102.186.233.219,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n11936,5,267,2017-04-07 03:30:22,http://conroy.co/caandra,0.1791867072,101.186.77.9,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n11937,5,267,2017-06-03 20:14:08,http://wisoky.io/anastacio.stroman,0.9789593226,10.196.137.58,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n11938,5,267,2017-04-06 15:33:19,http://turnerlowe.org/braeden,0.7245668430,63.221.218.5,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n11939,5,267,2017-02-26 02:46:18,http://anderson.com/litzy_johnson,0.1997374733,151.137.144.58,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n11940,5,267,2016-12-22 00:00:59,http://ritchie.info/alayna.block,0.5915255548,39.207.185.84,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n11941,5,267,2017-02-28 10:14:52,http://reilly.net/efren.marvin,0.2072807494,241.196.40.160,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n11942,5,267,2017-05-15 15:30:07,http://mayert.co/elliott,0.3824977492,124.10.213.9,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n11943,5,267,2017-01-22 02:01:29,http://rueckerherzog.co/gregoria,0.6320496105,43.213.198.8,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n11944,5,267,2017-02-13 08:48:27,http://beier.name/marlin_lakin,0.7347539524,153.184.7.68,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n11945,5,267,2017-03-28 00:57:24,http://stromanschumm.biz/lois,0.9074942253,199.173.49.102,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n11946,5,267,2017-01-08 00:42:08,http://moen.net/brandt.jacobson,0.5533901845,187.171.13.52,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n11947,5,267,2017-01-18 10:09:04,http://gaylord.biz/evans_howe,0.1409655073,242.222.132.144,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n11948,5,267,2017-05-17 15:24:29,http://romaguera.co/shane,0.0164909587,142.57.163.122,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n11949,5,267,2017-01-08 12:03:23,http://bartell.io/audie,0.1887320540,123.180.44.101,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n11950,5,267,2017-03-26 00:29:36,http://gibson.info/faye,0.9753343094,228.49.157.27,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n11951,5,267,2017-03-28 02:40:17,http://turcotte.com/shawn.kreiger,0.9961716671,78.32.121.92,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n11952,5,267,2017-03-13 07:16:44,http://jaskolski.com/jacques.johnson,0.7470105142,169.186.189.126,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n11953,5,267,2017-04-07 23:33:08,http://deckow.io/irwin.renner,0.2890123246,11.10.197.244,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n11954,5,267,2017-01-26 08:54:59,http://gibson.io/eldridge,0.8031064807,202.213.19.154,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n11955,5,267,2017-06-04 09:58:22,http://jaskolski.co/brianne,0.8054397129,72.131.222.133,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n14889,6,336,2017-04-15 13:26:32,http://stanton.biz/boris.bartell,,105.242.129.53,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n14890,6,336,2017-05-30 12:27:10,http://beerhaley.co/jermaine.goldner,,82.155.185.170,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n14891,6,336,2017-01-09 04:15:55,http://mcclure.io/merle.metz,,34.112.197.238,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n14892,6,336,2017-06-10 06:45:28,http://cummerata.org/lloyd_zemlak,,190.7.39.130,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n14893,6,336,2016-12-13 19:54:04,http://lind.org/mariela.kirlin,,5.17.96.252,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n14894,6,336,2017-02-06 02:01:25,http://damoreklocko.com/quinn,,193.159.77.30,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n14895,6,336,2017-03-17 13:33:31,http://murazik.io/nikolas.fay,,82.86.120.243,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n14896,6,336,2016-12-21 19:50:51,http://sipes.biz/cyrus.ferry,,232.84.16.228,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n14897,6,336,2016-12-29 09:14:50,http://stroman.io/rita.corkery,,137.116.120.224,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n14898,6,336,2017-03-11 13:40:07,http://bartolettijohnston.com/anibal,,106.75.204.236,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n14899,6,336,2017-05-02 08:25:53,http://turcotte.info/darien_green,,238.200.70.151,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n14900,6,336,2017-04-09 08:45:09,http://medhurst.org/maya,,10.58.48.207,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n14901,6,336,2017-02-03 00:56:44,http://johnsonheller.com/leann.olson,,69.158.190.142,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n14902,6,336,2017-03-03 06:08:22,http://lueilwitz.biz/jaren.ritchie,,90.169.173.150,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n14903,6,336,2017-01-10 12:16:02,http://corkery.com/jayden_bradtke,,246.175.41.133,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n14904,6,336,2017-04-29 15:28:32,http://jakubowski.com/jeramy_schinner,,73.215.40.65,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n14905,6,336,2017-01-10 11:16:08,http://rogahn.io/eunice,,28.156.35.132,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n14906,6,336,2017-03-22 22:41:55,http://ferry.net/rosamond_kohler,,136.84.110.233,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n14907,6,336,2017-01-19 15:03:22,http://eichmann.net/houston_murphy,,10.66.14.141,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n14908,6,336,2017-02-20 04:05:16,http://metz.name/leland_lind,,15.107.229.107,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n14909,6,336,2017-02-07 12:27:35,http://collinsking.net/willard,,246.105.249.175,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n14910,6,336,2017-02-07 22:02:47,http://greenholtbruen.co/jovany,,160.44.92.179,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n14911,6,336,2017-03-28 16:30:48,http://leuschke.com/obie,,86.144.147.211,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n14912,6,336,2017-02-13 12:42:25,http://borer.org/joany,,200.199.31.82,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n14913,6,336,2017-05-08 20:22:47,http://hahn.io/rahsaan,,179.155.67.59,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n14914,6,336,2017-01-27 16:05:09,http://lowe.biz/dominique.langworth,,54.95.178.82,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n14915,6,336,2017-02-27 08:57:44,http://lehner.name/mack_goodwin,,203.121.66.30,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n14916,6,336,2017-04-23 15:53:08,http://rathdickinson.biz/ethelyn_conn,,20.202.82.220,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n14917,6,336,2017-02-17 00:48:25,http://kochrempel.net/earnestine_brown,,201.139.191.5,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n14918,6,336,2017-06-13 14:18:25,http://ankunding.biz/chris,,108.22.217.15,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n14919,6,336,2017-02-14 11:53:35,http://gutmann.co/rosemary.parker,,147.124.128.139,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n14920,6,336,2017-01-29 12:27:46,http://balistreri.com/dorian,,55.163.221.208,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n14921,6,336,2017-01-05 00:46:38,http://strosinhickle.org/tyree.homenick,,25.201.118.141,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n14922,6,336,2017-01-11 14:42:14,http://jenkinscremin.name/geo_marvin,,148.95.178.231,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n14923,6,336,2017-03-06 22:47:38,http://howe.org/rosalinda,,91.115.59.17,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n14924,6,336,2016-12-28 06:14:20,http://stromanhartmann.org/sanford,,16.17.98.76,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n14925,6,336,2017-05-31 23:15:30,http://leuschke.net/dayne_jakubowski,,134.119.158.66,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n14926,6,336,2017-03-15 14:30:54,http://kris.io/durward_ullrich,,103.9.6.105,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n14927,6,336,2017-02-07 17:03:10,http://howelind.org/francis_kreiger,,165.24.177.62,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n14928,6,336,2017-01-19 20:10:42,http://dubuquemayert.co/chris,,2.74.115.161,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n14929,6,336,2017-02-20 23:44:26,http://bins.net/kay_kshlerin,,65.82.158.198,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n14930,6,336,2017-01-20 00:30:59,http://flatley.co/torrance,,34.69.125.148,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n14931,6,336,2017-05-30 12:33:14,http://gerlach.biz/adrain_reichert,,48.177.20.228,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n14932,6,336,2017-04-11 15:59:41,http://keelingrippin.org/katlynn.auer,,13.145.2.214,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n14933,6,336,2017-06-07 14:32:51,http://rolfson.net/reie,,208.88.82.249,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n14934,6,336,2017-04-04 05:12:16,http://bradtke.org/beaulah,,222.162.136.158,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n14935,6,336,2017-05-24 13:53:53,http://okonvon.biz/wyatt.bruen,,12.102.135.160,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n14936,6,336,2017-01-23 16:37:38,http://homenickroob.co/bryon,,29.70.32.71,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n14937,6,336,2017-02-02 14:06:46,http://stokekiles.name/claudia.sauer,,102.141.217.79,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n14938,6,336,2017-04-26 12:40:10,http://hansen.io/chaim.ortiz,,14.129.6.193,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n14939,6,336,2017-04-26 07:56:32,http://steuber.org/kiera_weber,,112.166.246.56,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n14940,6,336,2017-01-20 16:13:32,http://kaulke.info/josephine,,74.69.141.245,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n14941,6,336,2017-02-20 09:13:16,http://vonhermiston.co/johann.purdy,,83.101.112.141,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n9040,4,203,2017-05-09 01:04:26,http://altenwerthwindler.info/mark,0.8181794576,215.30.139.66,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n9041,4,203,2017-02-16 17:22:18,http://feest.co/viviane,0.0166207917,197.167.52.253,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n9042,4,203,2017-03-05 13:29:16,http://osinski.co/kaylah,0.4091500876,118.8.62.45,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n9043,4,203,2017-01-13 08:45:25,http://kshlerinstracke.com/taya_considine,0.6496999597,211.234.58.206,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n9044,4,203,2017-05-29 23:07:17,http://strosin.info/leora,0.9612903181,120.249.175.183,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n9045,4,203,2016-12-31 01:02:55,http://kozey.info/jacinto.christiansen,0.9578217278,198.67.60.180,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n9046,4,203,2017-01-07 00:27:36,http://ankunding.com/fredrick,0.5998710009,94.159.242.20,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n9047,4,203,2017-05-19 05:55:22,http://simonis.net/ansel,0.3120550466,140.87.192.190,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n9048,4,203,2016-12-28 07:20:55,http://pagac.io/lionel,0.0089949262,193.144.24.48,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n9049,4,203,2017-02-24 17:20:52,http://larsontoy.com/mya_cormier,0.7929195627,241.54.248.154,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n9050,4,203,2017-01-20 21:25:36,http://howell.io/rubye,0.0307491492,222.155.82.151,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n9051,4,203,2017-03-23 16:46:41,http://wisozkfritsch.com/eda.hodkiewicz,0.0580375398,96.61.169.31,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n9052,4,203,2017-05-27 17:23:53,http://cruickshankcormier.net/timothy,0.4655233226,55.114.87.35,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n9053,4,203,2017-03-21 04:47:04,http://zboncak.com/ellen.walsh,0.3254719706,69.29.36.212,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n9054,4,203,2017-04-24 03:50:11,http://predovic.name/lucile.reichel,0.8369299046,80.124.123.50,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n9055,4,203,2017-01-25 05:31:54,http://bruen.info/lorena_jacobson,0.7872795308,147.68.87.110,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n9056,4,203,2017-06-08 12:47:28,http://yundt.com/laurine.hackett,0.4236863199,40.50.204.51,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n9057,4,203,2017-02-24 22:33:56,http://torphystark.io/jamison.rau,0.2140555521,162.183.155.88,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n9058,4,203,2017-02-27 16:27:24,http://ryan.name/hershel.cole,0.7994463846,55.95.142.237,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n9059,4,203,2017-03-31 01:00:07,http://wilderman.info/lexi_rogahn,0.0580235824,125.177.16.114,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n9060,4,203,2016-12-17 07:51:16,http://feestleuschke.name/eleazar,0.1139732523,189.78.239.163,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n9061,4,203,2017-06-05 01:24:05,http://padberg.biz/kiarra_beahan,0.4975870937,15.220.82.24,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n9062,4,203,2017-04-06 12:16:52,http://feeney.org/roberta.bogisich,0.8732157700,71.197.209.133,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n9063,4,203,2017-01-12 23:42:49,http://kuhnkris.com/dudley,0.1320846054,80.42.164.251,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n9064,4,203,2017-04-21 20:19:37,http://donnelly.org/julianne,0.2578789095,142.117.231.16,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n9065,4,203,2017-01-20 08:00:36,http://renner.org/theresa,0.2933961033,213.104.46.181,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n9066,4,203,2017-04-02 18:35:05,http://ratke.co/mariam.block,0.8631010956,46.53.37.127,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n9067,4,203,2017-02-04 15:05:46,http://wymanschmeler.io/cleveland_vonrueden,0.0334704511,59.52.156.28,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n9068,4,203,2017-05-03 20:20:03,http://hettingercollins.name/myra,0.3985607251,61.155.4.103,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n9069,4,203,2017-03-23 03:59:48,http://murray.co/lura,0.9012938095,169.105.99.206,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n9070,4,203,2017-05-05 00:26:56,http://oconnellschmeler.co/lafayette,0.7466290768,126.151.12.17,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n9071,4,203,2017-03-09 08:00:09,http://weber.biz/mckenna,0.8483811166,76.72.20.29,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n9072,4,203,2017-02-22 05:48:32,http://daniel.info/verona.anderson,0.0900736265,159.40.126.234,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n9073,4,203,2017-03-28 04:41:46,http://steuber.biz/valentine.hintz,0.6159139134,254.74.27.27,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n9074,4,204,2017-04-15 01:36:13,http://bernier.info/kendra,0.2860127345,10.117.67.181,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n9075,4,204,2016-12-17 13:07:11,http://lemke.io/dusty.larson,0.7302770880,69.93.61.85,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n9076,4,204,2017-01-17 07:16:28,http://breitenbergswaniawski.net/freda,0.2200828297,134.4.200.238,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n9077,4,204,2017-05-24 01:18:12,http://simonis.com/tamara_volkman,0.5736650801,203.156.247.238,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n9078,4,204,2017-02-14 07:06:36,http://schinner.biz/wallace,0.9625829253,179.73.62.129,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n9079,4,204,2017-01-02 06:31:54,http://parker.info/delores.smitham,0.3940273666,166.218.187.169,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n9080,4,204,2017-06-06 06:10:07,http://lesch.net/jensen,0.8243936713,60.104.164.253,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n9081,4,204,2017-06-07 22:11:25,http://oconnellmante.io/angelica,0.7041920274,91.39.171.16,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n9082,4,204,2017-01-28 02:11:08,http://braunbotsford.biz/carter_reichel,0.5084487523,239.44.141.144,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n9083,4,204,2017-05-19 19:05:08,http://olson.net/nadia_keeling,0.5601176911,33.204.199.252,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n9084,4,204,2017-03-20 16:20:40,http://wildermanlowe.io/erna,0.0486222039,211.166.75.178,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n9085,4,204,2017-05-25 17:33:26,http://buckridge.co/burdette.ruel,0.2434911752,38.110.26.169,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n9086,4,204,2017-04-24 05:00:13,http://torplehner.info/glen_hagenes,0.5138480049,136.215.154.172,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n9087,4,204,2017-01-02 12:13:37,http://miller.name/lenore_tromp,0.6522095470,174.168.48.115,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n9088,4,204,2017-03-20 07:14:28,http://wuckert.org/oran.hoeger,0.0765314606,217.210.59.117,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n9089,4,204,2017-02-05 10:30:47,http://keeling.io/ava,0.1361255966,88.161.204.85,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n9090,4,204,2017-05-08 05:45:01,http://jenkins.com/joy_bradtke,0.5579061913,40.214.137.148,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n9091,4,204,2017-01-22 02:51:47,http://hudson.net/irving.gulgowski,0.7225069755,164.191.186.213,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n6038,3,131,2017-01-29 22:12:26,http://homenick.net/gunner,,116.12.85.150,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n6039,3,131,2016-12-31 09:43:51,http://ritchie.biz/della,,216.238.223.10,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n6040,3,131,2017-04-10 00:17:53,http://batzhuels.io/kirsten,,251.21.5.33,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n6041,3,131,2017-02-06 23:53:42,http://walker.name/modesto,,150.200.120.68,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n6042,3,131,2017-05-01 09:21:07,http://friesen.net/willy,,180.62.163.87,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n6043,3,131,2016-12-27 18:16:49,http://mohr.com/jose.brekke,,129.130.226.79,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n6044,3,131,2017-03-04 15:23:19,http://schoen.name/oran,,241.197.116.25,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n6045,3,132,2017-01-22 22:23:31,http://lehnertorphy.com/kip_wintheiser,,15.90.144.147,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n6046,3,132,2017-04-06 23:15:26,http://terryfisher.info/marge.hills,,147.125.135.121,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n6933,3,152,2017-05-27 12:47:08,http://reichert.io/tyson,,179.120.56.49,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n6047,3,132,2017-05-15 12:33:43,http://wintheisercorwin.com/elinore.kunze,,19.174.147.206,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n6048,3,132,2017-05-13 17:11:36,http://davis.info/taryn,,13.172.230.117,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n6049,3,132,2017-03-20 06:06:34,http://schusterdibbert.co/dock,,147.5.185.166,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n6050,3,132,2017-05-14 10:20:58,http://durgannienow.net/althea.schaefer,,170.28.127.189,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n6051,3,132,2017-03-31 07:04:53,http://lindgrenstoltenberg.io/estel,,244.20.228.10,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n6052,3,132,2017-03-17 17:49:30,http://wilkinson.net/gerald_hegmann,,230.197.111.98,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n6053,3,132,2017-04-20 02:37:48,http://kris.info/vince,,226.21.103.158,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n6054,3,132,2017-04-06 23:36:12,http://reichert.io/ernestine.kuphal,,152.124.87.219,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n6055,3,132,2017-01-31 17:50:23,http://oberbrunner.info/nick.littel,,65.210.70.30,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n6056,3,132,2017-04-25 13:42:38,http://mante.info/hardy,,244.87.104.61,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n6057,3,132,2017-04-02 13:14:39,http://schinnerhaag.co/georgianna.tillman,,154.241.193.238,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n6058,3,132,2017-02-08 08:18:53,http://hand.com/abbey.lang,,203.81.229.155,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n6059,3,132,2017-06-05 11:14:41,http://ritchie.io/vernon_gibson,,28.121.43.56,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n6060,3,132,2017-01-22 17:18:21,http://kihnmills.org/cathryn,,14.22.143.75,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n6061,3,132,2017-03-19 16:54:57,http://wuckert.com/carlee,,223.12.197.32,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n6062,3,132,2017-06-11 19:55:44,http://cain.org/kamron,,242.244.43.243,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n6063,3,132,2017-03-24 20:48:58,http://jacobshirthe.org/kaylin,,156.231.203.92,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n6064,3,132,2016-12-28 05:01:18,http://lockman.org/dimitri,,122.65.27.233,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n6065,3,132,2017-02-09 10:19:49,http://hintz.io/gwendolyn_donnelly,,195.232.235.149,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n6066,3,132,2017-05-22 12:47:57,http://schroeder.net/susanna,,118.142.125.236,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n6067,3,132,2017-02-28 01:53:22,http://ziemann.org/dedric_hilll,,254.31.43.238,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n6068,3,132,2017-01-12 07:20:02,http://metz.net/prudence_kozey,,167.120.210.65,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n6069,3,132,2016-12-18 16:09:17,http://wehner.co/roberta,,10.143.72.213,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n6070,3,132,2017-06-10 07:04:06,http://reilly.org/madeline,,58.151.111.93,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n6071,3,132,2017-05-18 14:40:07,http://wilkinson.org/jewell,,60.175.124.234,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n6072,3,132,2017-05-25 16:55:49,http://lemke.info/flo,,160.146.184.69,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n6073,3,132,2017-05-20 16:22:08,http://romaguera.org/kayli,,219.240.174.202,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n6074,3,132,2017-05-24 17:18:45,http://franecki.com/jannie,,121.3.65.168,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n6075,3,132,2017-03-10 14:13:59,http://steuber.net/verona.kirlin,,29.58.13.131,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n6076,3,132,2017-01-19 18:15:32,http://douglas.net/trycia,,10.190.144.20,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n6077,3,132,2017-04-21 01:20:20,http://eichmannauer.biz/mckenna,,195.153.22.202,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n6078,3,132,2017-01-16 10:19:15,http://homenick.io/cristobal,,34.232.224.27,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n6079,3,132,2017-01-18 06:38:13,http://nicolas.io/mireya,,35.221.116.46,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n6080,3,132,2017-02-10 02:42:22,http://weinat.co/lenore.lesch,,60.153.67.233,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n6081,3,132,2017-04-04 15:09:49,http://king.biz/david_leuschke,,214.2.167.47,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n6082,3,132,2017-03-23 04:43:24,http://ankundinggutkowski.biz/otis_schinner,,169.248.240.41,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n6083,3,132,2017-01-20 16:41:21,http://pagaclynch.org/jermaine.reinger,,172.189.129.42,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n6084,3,132,2017-05-22 15:46:14,http://simonis.com/dahlia,,133.99.155.25,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n6085,3,132,2017-02-19 12:55:52,http://hickle.name/thea,,200.118.97.11,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n6086,3,132,2016-12-19 20:21:23,http://torpvonrueden.org/paul_blick,,8.99.40.208,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n6087,3,132,2017-01-31 03:35:20,http://kunze.name/macey.frami,,230.214.121.244,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n6088,3,132,2017-03-25 03:25:42,http://purdy.io/idell,,75.178.150.198,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n6089,3,132,2017-01-06 08:01:16,http://bartell.biz/dianna.wilderman,,190.125.253.253,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n6090,3,132,2017-04-01 20:14:04,http://bartoletti.biz/brady,,48.90.5.92,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n6091,3,132,2017-02-05 09:33:57,http://funk.name/lelia,,88.16.180.119,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n6092,3,132,2016-12-29 03:16:05,http://conroy.co/dan.stanton,,189.27.148.16,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n11956,5,267,2017-01-22 01:47:58,http://dooley.co/matilde,0.9826193419,21.188.188.183,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n11957,5,267,2017-02-26 13:07:20,http://schaden.org/adella_macgyver,0.8226360078,174.141.211.178,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n11958,5,267,2017-04-14 05:39:40,http://cronatillman.info/richard.hermiston,0.4415385242,159.69.221.135,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n11959,5,267,2017-02-08 16:03:03,http://borerkemmer.io/rozella.larkin,0.1989662219,154.32.27.198,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n11960,5,267,2017-01-10 02:51:17,http://abshire.io/beie,0.4832993235,131.57.97.68,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n11961,5,267,2017-03-19 11:38:14,http://mohrkautzer.org/mallory,0.7266332543,146.109.146.104,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n11962,5,267,2016-12-16 05:40:47,http://pfannerstillpouros.net/lane,0.6262636080,74.171.125.104,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n11963,5,267,2017-02-09 22:36:51,http://kreigerturner.co/kelsie_haag,0.9632597957,28.67.98.100,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n11964,5,267,2017-01-24 18:30:42,http://gulgowski.info/sandy,0.6272052952,127.3.129.243,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n11965,5,267,2017-03-19 12:36:21,http://krajcik.name/sandrine,0.6771677990,117.113.49.230,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n11966,5,267,2016-12-16 06:28:50,http://luettgen.biz/marty,0.9445689779,218.112.150.211,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n11967,5,267,2017-03-19 10:48:27,http://cronin.com/jamison_abshire,0.7348573267,176.81.48.70,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n11968,5,267,2017-03-30 00:07:26,http://cummings.io/nolan_mraz,0.1786904787,176.59.246.112,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n11969,5,268,2017-03-17 10:23:09,http://wolfbartell.com/elmer,0.3978016248,69.42.150.203,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n11970,5,268,2017-03-18 12:02:00,http://bartonleffler.io/tyra_kihn,0.7274953699,87.70.168.129,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n11971,5,268,2017-02-04 10:01:44,http://romaguera.io/aidan,0.4182211296,202.95.114.14,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n11972,5,268,2017-05-31 14:08:33,http://robel.com/xzavier_kling,0.6691326127,246.159.81.165,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n11973,5,268,2017-05-08 11:01:42,http://thiel.com/marilou,0.8612565932,25.44.125.143,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n11974,5,268,2017-04-25 06:05:19,http://yostmacgyver.net/franz,0.0550424418,35.21.171.83,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n11975,5,268,2017-05-11 02:37:40,http://towne.com/candelario.mohr,0.1853853084,238.78.93.113,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n11976,5,268,2017-03-18 17:13:09,http://pacocha.name/guiseppe_casper,0.9686103812,253.49.5.160,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n11977,5,268,2017-02-09 01:53:14,http://douglas.io/marta_okon,0.5742725811,75.225.68.38,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n11978,5,268,2017-05-24 09:56:51,http://feest.net/baron,0.5179085506,83.165.9.127,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n11979,5,268,2017-05-30 20:43:29,http://tromp.com/dawson,0.2441047687,241.190.86.236,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n11980,5,268,2017-06-08 06:35:13,http://beerkonopelski.io/aidan_oconner,0.4846495398,24.140.127.63,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n11981,5,268,2017-02-17 14:46:58,http://crona.io/craig_lakin,0.6916640257,12.194.22.124,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n11982,5,268,2017-02-13 23:17:50,http://wolff.io/helene,0.2431830556,228.103.253.176,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n11983,5,268,2017-04-14 05:08:31,http://lemkebeer.io/helena_lehner,0.5978222649,137.12.21.86,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n11984,5,268,2017-01-29 00:55:00,http://bauchconroy.io/shayna,0.6175000440,219.141.196.205,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n11985,5,268,2017-06-01 16:50:37,http://lubowitz.com/madelynn_stamm,0.0692788105,12.240.209.35,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n11986,5,268,2017-05-19 18:59:46,http://kertzmann.name/elnora.bergstrom,0.7415936805,15.209.191.43,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n11987,5,268,2017-04-23 13:05:15,http://goodwinstanton.net/ali,0.8436591364,127.142.118.201,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n11988,5,268,2017-04-22 16:29:56,http://markslueilwitz.co/imogene,0.6398817647,163.107.166.14,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n11989,5,268,2017-02-24 10:25:52,http://will.co/aaliyah,0.7705110494,79.85.74.33,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n11990,5,268,2017-01-16 09:02:40,http://shanahan.biz/haie,0.9210062106,229.212.213.55,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n11991,5,268,2016-12-18 18:12:23,http://gerhold.info/amina,0.5868686893,224.74.10.112,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n11992,5,268,2017-05-17 18:51:31,http://hettinger.biz/jerrod_mann,0.0383565053,67.141.69.50,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n11993,5,268,2016-12-23 22:10:36,http://wehner.name/felipe,0.1440324429,15.112.231.73,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n11994,5,268,2017-04-26 22:40:42,http://rosenbaum.name/davion,0.9336001123,166.172.58.116,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n11995,5,268,2016-12-30 02:12:21,http://bruenglover.net/allene,0.9291949150,68.92.33.234,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n11996,5,268,2017-01-22 05:03:15,http://mraz.io/lon,0.7699133113,116.154.218.71,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n11997,5,268,2017-02-18 13:28:16,http://littel.info/michael,0.9195926877,25.208.128.40,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n11998,5,269,2017-01-17 17:02:31,http://runolfsdottir.info/milo,0.7529212630,23.225.230.54,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n11999,5,269,2016-12-22 12:46:58,http://langoshruecker.org/lester.langworth,0.8221729192,195.125.10.242,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n12000,5,269,2017-06-08 19:31:49,http://fisher.info/billy,0.8688481259,173.132.56.241,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n12001,5,269,2017-06-04 19:32:17,http://lesch.org/dahlia_kunze,0.8372441708,218.74.22.48,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n12002,5,269,2017-01-14 17:53:48,http://johns.com/matilde,0.9238419313,72.196.133.76,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n12003,5,269,2017-01-10 22:55:18,http://mertz.io/ramona.beatty,0.4049512991,4.179.59.177,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n12004,5,269,2017-04-26 23:02:48,http://schoenmcglynn.co/jamil,0.2602687884,119.2.186.141,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n12005,5,269,2017-05-15 11:56:15,http://heller.io/ozzie,0.5490753550,202.6.96.125,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n12006,5,269,2017-06-05 08:03:18,http://hegmann.biz/jermaine,0.7685844344,156.92.65.122,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n12007,5,269,2017-03-27 01:07:32,http://ferry.name/orpha,0.2710002316,53.236.106.39,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n14942,6,336,2016-12-17 00:13:29,http://moendicki.biz/jodie_skiles,,76.225.15.123,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n14943,6,336,2017-05-11 15:23:20,http://mayertwiza.name/donavon_crist,,217.114.94.64,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n14944,6,336,2017-01-30 12:44:57,http://collins.name/llewellyn.legros,,66.94.137.90,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n14945,6,336,2016-12-21 14:37:19,http://osinski.info/reid_blanda,,29.56.188.198,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n14946,6,337,2017-02-17 23:39:17,http://sporer.co/rudolph,,22.9.194.150,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n14947,6,337,2017-05-01 10:45:07,http://vandervort.name/edwardo_brakus,,107.22.147.15,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n14948,6,337,2017-01-15 10:31:42,http://treutel.org/jacinthe.barton,,104.29.148.136,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n14949,6,337,2017-05-10 13:00:08,http://bauch.org/leonora.sipes,,183.173.230.194,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n14950,6,337,2017-02-11 01:53:58,http://walter.com/geoffrey_wisozk,,119.200.102.113,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n14951,6,337,2017-03-04 21:59:28,http://schuppe.name/deon.rau,,139.226.167.94,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n14952,6,337,2017-06-13 18:46:23,http://casperkohler.com/laurie,,68.5.154.218,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n14953,6,337,2017-01-29 10:41:36,http://kozey.org/laron,,24.225.111.96,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n14954,6,337,2017-04-27 04:56:10,http://champlin.org/garland,,208.43.141.165,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n14955,6,337,2017-04-04 15:23:11,http://wiegand.com/lindsay.feest,,159.113.188.86,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n14956,6,337,2017-03-27 04:41:47,http://tremblayhaag.com/armand,,60.127.85.236,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n14957,6,337,2017-01-27 16:18:26,http://jast.biz/darlene,,64.253.200.7,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n14958,6,337,2017-01-18 02:03:58,http://beahanhowe.co/laron,,51.144.41.155,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n14959,6,337,2017-04-27 10:07:35,http://robel.biz/drake,,48.251.51.225,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n14960,6,337,2017-04-29 00:13:59,http://beahandubuque.com/stan,,11.72.68.150,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n14961,6,337,2017-02-12 19:56:30,http://bode.net/rosario.nolan,,81.239.245.6,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n14962,6,337,2017-02-09 19:58:39,http://douglaskertzmann.org/marielle,,37.98.139.198,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n14963,6,337,2017-05-06 11:51:24,http://cole.co/kylee,,69.44.127.33,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n14964,6,337,2017-03-16 08:23:33,http://reingertillman.co/kristoffer.barrows,,98.115.3.171,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n14965,6,337,2017-05-16 14:57:23,http://luettgen.com/lucio.crooks,,143.99.128.124,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n14966,6,337,2017-03-02 05:40:08,http://hammes.name/uriah_langworth,,101.116.157.65,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n14967,6,337,2017-06-03 08:39:35,http://cummerata.name/trea,,233.149.215.39,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n14968,6,337,2017-05-31 21:52:15,http://brekke.net/garett_volkman,,77.225.204.163,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n14969,6,337,2017-01-21 19:25:25,http://christiansenokuneva.io/markus,,124.71.99.130,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n14970,6,337,2017-02-21 03:16:14,http://ko.co/demarco,,152.160.127.138,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n14971,6,337,2017-02-13 13:44:37,http://bauchkshlerin.info/aiyana,,54.128.236.96,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n14972,6,337,2017-05-09 01:19:47,http://stehrfahey.com/cecil,,20.139.215.121,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n14973,6,337,2017-02-28 14:47:22,http://corkery.org/kirk,,199.47.122.159,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n14974,6,337,2016-12-20 10:08:49,http://windler.net/rusty_gleichner,,155.165.194.105,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n14975,6,337,2017-03-24 14:51:37,http://walter.info/nya_tillman,,225.160.161.240,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n14976,6,337,2017-05-28 13:53:08,http://vonrueden.net/alaina,,45.244.117.161,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n14977,6,337,2017-02-18 19:17:07,http://kuhic.com/kyler,,172.79.241.100,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n14978,6,337,2017-01-16 02:54:31,http://harberhermiston.info/wilbert,,247.50.171.193,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n14979,6,337,2017-03-03 23:12:56,http://bernhardchristiansen.io/corene,,132.200.224.252,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n14980,6,337,2017-03-16 00:46:04,http://kshlerinbarton.com/travon_crooks,,82.7.42.227,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n14981,6,337,2017-02-26 07:04:53,http://wilderman.io/araceli.koepp,,194.224.206.197,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n14982,6,337,2017-05-04 15:26:45,http://braun.biz/erin,,205.156.188.36,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n14983,6,337,2017-02-16 11:09:59,http://wehner.biz/hollis.schmidt,,231.239.222.163,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n14984,6,337,2017-04-28 02:38:53,http://rutherford.biz/maiya,,122.100.157.197,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n14985,6,337,2017-02-27 12:54:33,http://larkinlindgren.biz/rita,,130.218.3.247,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n14986,6,337,2016-12-16 15:43:58,http://gislason.com/haven,,31.130.7.87,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n14987,6,337,2017-02-23 01:35:48,http://wisozkfadel.biz/claire_kunze,,54.182.66.107,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n14988,6,337,2017-02-10 00:56:38,http://kaulkekirlin.net/kitty_buckridge,,119.130.21.153,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n14989,6,337,2016-12-20 04:00:33,http://gislasonroberts.net/kaleigh,,220.111.167.51,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n14990,6,337,2017-04-09 18:42:14,http://davis.com/carmen,,47.70.106.42,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n14991,6,337,2017-03-27 04:15:45,http://altenwerthrenner.net/blair.price,,137.157.196.202,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n14992,6,337,2016-12-19 23:46:29,http://ortiz.biz/lizzie,,36.223.183.130,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n14993,6,337,2017-01-19 14:33:40,http://stoltenberg.biz/napoleon.flatley,,58.75.96.120,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n14994,6,337,2017-01-08 03:03:42,http://pouros.co/kacey,,3.177.96.178,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n14995,6,337,2017-05-18 19:35:16,http://shanahantremblay.name/domingo.lowe,,52.237.13.100,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n14996,6,337,2017-05-04 20:41:25,http://barton.info/raymundo_bergnaum,,181.149.95.247,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n9092,4,204,2017-05-19 11:15:12,http://goyettemaggio.info/wyatt,0.7328989314,47.236.19.172,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n9093,4,204,2017-01-15 13:19:49,http://jaskolski.biz/verdie,0.9902895891,2.48.241.59,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n9094,4,204,2017-03-16 05:01:00,http://wyman.co/donato,0.6453332985,194.117.227.253,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n9095,4,204,2017-05-26 20:42:21,http://rolfsonkuhic.io/aleandra.spinka,0.0745380998,55.56.204.83,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n9096,4,204,2017-05-28 01:59:40,http://rueckertrantow.io/cicero,0.3649383382,206.33.100.79,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n9097,4,204,2017-01-24 23:52:11,http://cronajast.biz/price_cummerata,0.6995824441,154.145.129.85,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n9098,4,204,2017-03-10 00:25:37,http://schuster.io/dortha.halvorson,0.5355483849,52.166.185.20,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n9099,4,204,2017-01-10 05:28:28,http://nicolas.name/dane,0.1501250828,40.9.56.68,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n9100,4,204,2016-12-20 06:22:07,http://grimes.name/joanne_runolfon,0.2873279056,176.155.114.104,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n9101,4,204,2017-06-13 06:13:07,http://medhurst.co/reagan,0.8320412793,139.211.192.242,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n9102,4,204,2016-12-19 16:22:27,http://friesenmarquardt.com/daniela.nitzsche,0.9209833617,143.231.142.64,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n9103,4,204,2017-02-19 18:28:29,http://thiel.biz/noemy,0.6742106266,218.46.216.232,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n9104,4,204,2017-04-27 01:01:50,http://oreilly.biz/ellsworth.ferry,0.2251240793,52.195.234.77,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n9105,4,204,2017-04-09 17:05:07,http://doyle.info/conor.hayes,0.8176178279,143.220.44.158,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n9106,4,204,2016-12-28 05:32:18,http://quitzonokeefe.net/emmanuel,0.1876087604,45.35.5.200,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n9107,4,204,2017-06-11 15:12:57,http://schoen.co/douglas,0.1378070040,207.84.226.223,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n9108,4,204,2017-04-08 16:06:47,http://steuberfeeney.info/glenna,0.8528889640,93.248.14.183,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n9109,4,204,2017-01-11 17:00:35,http://willms.net/haley,0.6451649344,204.79.79.27,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n9110,4,204,2016-12-19 15:26:01,http://turcotteconsidine.biz/casimer.veum,0.6253840699,28.229.244.45,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n9111,4,204,2016-12-31 12:08:17,http://kozey.io/crystel_hermiston,0.5209262356,84.155.43.138,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n9112,4,204,2016-12-30 05:03:54,http://weimann.info/isadore,0.4748274417,143.96.52.223,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n10759,4,241,2017-05-11 16:46:25,http://boehm.co/hillary,,26.241.152.218,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n9113,4,204,2017-04-06 13:48:11,http://leannonjenkins.co/korey,0.2447610148,180.253.223.29,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n9114,4,204,2017-05-30 18:43:06,http://goldnerreinger.org/tate.boehm,0.8376861122,56.157.116.143,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n9115,4,204,2017-01-21 21:10:15,http://bednargraham.info/janea_hettinger,0.3473730567,30.122.208.222,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n9116,4,204,2017-03-09 11:26:48,http://grahamthompson.co/ally,0.2243270024,247.132.216.127,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n9117,4,204,2017-05-04 05:14:42,http://beatty.net/shyann,0.0799632838,251.126.113.82,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n9118,4,204,2016-12-22 23:12:14,http://hilll.net/horace_lockman,0.5890702029,131.76.104.121,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n9119,4,204,2017-05-06 16:33:13,http://lind.io/deondre,0.4124258251,223.123.60.135,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n9120,4,204,2017-05-22 06:19:13,http://mcculloughschuppe.org/ursula,0.2139264179,163.44.4.55,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n9121,4,204,2017-04-03 08:39:50,http://mcglynn.co/cole,0.8164851359,34.117.113.83,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n9122,4,204,2017-01-29 07:26:22,http://mosciski.name/chelsea.kuphal,0.0146069227,33.136.169.253,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n9123,4,204,2017-01-09 08:36:17,http://eichmann.net/howell,0.3949973363,250.203.152.38,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n9124,4,204,2017-01-27 17:00:34,http://hicklejerde.co/delphine,0.3867595613,53.207.124.240,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n9125,4,204,2017-06-07 13:09:40,http://harvey.info/harley_durgan,0.6820768489,13.213.188.189,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n9126,4,204,2017-02-13 14:08:57,http://spinka.net/taryn,0.6623359844,216.82.141.238,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n9127,4,204,2017-05-27 09:07:26,http://kuhnbahringer.co/elia,0.5511253862,197.4.46.55,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n9128,4,204,2017-06-02 03:38:17,http://kochlowe.biz/sterling,0.1485038484,23.213.114.203,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n9129,4,204,2017-05-23 20:48:41,http://hilll.org/weldon,0.7037941575,143.63.77.121,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n9130,4,204,2017-05-27 05:24:51,http://wuckert.biz/jerome,0.6226746770,200.30.108.167,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n9131,4,204,2017-03-08 11:10:52,http://senger.biz/kelley,0.8783279862,83.35.232.21,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n9132,4,204,2017-04-01 08:27:48,http://dickirogahn.org/kariane.prohaska,0.4187568201,67.116.84.95,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n9133,4,204,2016-12-18 01:31:54,http://medhurst.co/micheal.donnelly,0.8638965760,160.180.191.214,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n9134,4,204,2017-02-17 23:23:08,http://hayes.biz/rosina,0.9905281770,134.9.55.74,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n9135,4,204,2017-02-12 22:51:33,http://rice.org/afton.ruecker,0.2215904570,109.73.20.220,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n9136,4,204,2017-05-05 13:32:33,http://gulgowskicollins.org/aleandro_legros,0.3413409497,151.222.108.12,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n9137,4,204,2017-06-14 04:00:23,http://kozey.io/uriel,0.5283781357,85.183.101.105,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n9138,4,204,2017-03-13 08:20:29,http://huelsmurray.biz/fernando_senger,0.4423401693,140.113.215.34,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n9139,4,204,2016-12-20 16:45:45,http://schoen.org/leone_torphy,0.4349613211,32.203.42.227,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n9140,4,205,2017-05-15 09:46:02,http://green.info/bettye.terry,0.8928266414,82.130.187.195,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n9141,4,205,2017-06-11 01:12:03,http://hintz.info/april,0.5060247482,153.113.118.140,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n9142,4,205,2017-03-06 07:39:48,http://ernser.co/lina,0.0040075619,243.117.179.33,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n6093,3,132,2017-05-16 08:12:34,http://blandakilback.name/ephraim,,114.80.71.42,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n6094,3,132,2017-04-28 00:42:32,http://kaulkeko.info/abby,,45.252.254.246,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n6095,3,132,2017-05-17 04:31:04,http://olsonmcclure.biz/gwendolyn_ruel,,35.198.64.144,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n6096,3,132,2017-05-03 16:44:29,http://ferry.com/jude_ondricka,,152.118.62.27,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n6097,3,132,2017-01-05 16:21:45,http://hilpertmarvin.org/alena_emmerich,,166.53.86.25,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n6098,3,132,2017-02-14 06:03:57,http://dietrichjerde.org/lane.stroman,,87.68.10.254,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n6099,3,132,2017-03-01 19:55:16,http://kris.co/maudie,,117.62.93.218,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n6100,3,132,2017-06-13 20:50:58,http://heathcote.com/adrianna_feeney,,215.131.78.117,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n6101,3,132,2017-04-19 23:28:00,http://runolfsdottir.io/joel,,208.19.231.234,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n6102,3,132,2017-06-08 05:52:54,http://oharanicolas.net/deon_cormier,,234.61.199.61,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n6103,3,132,2017-05-27 20:15:41,http://weberbrown.org/cathrine_fay,,47.156.195.251,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n6104,3,132,2017-05-26 12:33:28,http://gislasonauer.name/jenifer,,133.250.164.234,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n6105,3,132,2017-05-28 06:25:24,http://ratke.info/jadon,,241.188.163.245,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n6106,3,132,2017-01-28 14:29:53,http://schultzpaucek.name/florence.kutch,,157.44.206.94,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n6107,3,132,2017-05-19 21:26:31,http://heel.com/odell.kunze,,98.184.77.50,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n6108,3,132,2017-03-02 17:00:22,http://connellyyost.info/colt_moriette,,183.113.73.101,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n6109,3,133,2017-05-01 12:18:16,http://hartmannfeest.name/oswaldo_carter,,226.38.228.210,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n6110,3,133,2017-02-01 03:41:43,http://hyattgoodwin.org/aylin_langworth,,173.10.158.75,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n6111,3,133,2017-04-22 21:33:47,http://homenickkovacek.io/coleman.abernathy,,235.193.87.49,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n6112,3,133,2017-06-13 15:13:41,http://rauboehm.co/kailee,,25.42.223.85,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n6113,3,133,2016-12-17 22:53:32,http://bode.info/norwood_gulgowski,,161.127.175.132,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n6114,3,133,2017-01-05 19:02:45,http://walsh.com/earnestine,,234.7.156.33,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n6115,3,133,2016-12-30 15:06:35,http://markswalker.io/larry_hauck,,244.149.84.120,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n6116,3,133,2017-04-09 11:38:17,http://dubuque.biz/emely_macejkovic,,112.229.95.220,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n6117,3,133,2017-05-07 09:19:24,http://wiza.biz/andre.predovic,,135.214.189.148,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n6118,3,133,2017-02-09 13:36:31,http://sporer.name/derrick,,30.240.175.164,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n6119,3,133,2017-04-09 23:23:20,http://strosin.co/delphia,,146.51.178.26,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n6120,3,133,2017-05-13 14:51:32,http://macejkovic.io/stone,,186.142.82.112,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n6121,3,133,2017-02-02 15:56:31,http://faheykozey.org/naomie,,199.36.136.61,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n6122,3,133,2017-02-07 13:54:50,http://klein.org/vance,,51.92.210.113,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n6123,3,133,2016-12-17 04:13:00,http://braunbauch.name/walton,,12.249.173.224,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n6124,3,133,2017-01-10 21:43:17,http://schoen.net/jensen,,176.155.137.9,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n6125,3,133,2017-06-10 10:04:57,http://hansenfranecki.io/bridgette_franecki,,165.45.150.245,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n6126,3,133,2017-04-01 20:56:54,http://hickle.net/horacio.littel,,88.242.103.199,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n6127,3,133,2017-01-01 19:12:20,http://koelpin.info/elwin,,8.124.44.22,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n6128,3,133,2017-05-06 04:32:30,http://durgan.co/audra,,241.19.231.187,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n6129,3,133,2017-02-18 03:21:26,http://torp.net/gene_farrell,,92.97.189.7,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n6130,3,133,2017-03-13 09:36:51,http://homenick.io/kay.yost,,171.161.91.160,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n6131,3,133,2017-01-06 19:26:53,http://runteheaney.co/hilda.hahn,,233.99.173.23,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n6132,3,133,2016-12-26 12:05:00,http://hudson.io/maurine_greenfelder,,19.66.162.11,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n6133,3,133,2017-05-28 07:33:32,http://block.info/josephine_murazik,,62.103.137.223,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n6134,3,133,2016-12-21 16:32:05,http://weinat.com/shane,,139.216.229.150,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n6135,3,133,2017-02-28 22:06:51,http://upton.name/nels_koelpin,,233.134.114.66,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n6136,3,133,2017-05-26 14:52:37,http://jast.info/bennett,,92.49.204.226,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n6137,3,133,2017-03-26 19:29:35,http://lubowitz.org/angelita_murazik,,103.80.106.182,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n6138,3,133,2017-04-28 07:45:21,http://rau.net/harold_pacocha,,196.154.160.243,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n6139,3,133,2017-03-19 03:11:42,http://gusikowski.co/triston_witting,,243.253.15.156,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n6140,3,133,2017-05-20 16:36:23,http://smith.org/saul.glover,,219.239.243.109,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n6141,3,133,2017-02-21 11:42:12,http://ward.info/al_balistreri,,22.39.239.144,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n6142,3,133,2017-01-30 03:51:17,http://blick.org/charlotte_kunde,,60.6.175.130,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n6143,3,133,2016-12-16 21:49:25,http://kertzmann.co/jewell_mayer,,6.50.44.114,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n6144,3,133,2017-02-12 23:46:31,http://boyle.info/deja,,2.66.252.22,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n6145,3,134,2017-06-13 20:38:15,http://rodriguez.info/lorenza_botsford,,76.214.60.18,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n6146,3,134,2017-01-12 07:52:08,http://rosenbaum.name/leopold.dietrich,,72.124.59.45,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n6147,3,134,2017-02-05 18:55:11,http://kerlukesauer.com/roselyn,,161.107.204.3,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n12008,5,269,2017-02-11 15:57:11,http://kiehn.com/moshe_ritchie,0.2543979137,131.200.17.140,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n12009,5,269,2017-01-18 21:59:39,http://tillman.com/scotty.wilderman,0.5149678327,122.206.98.226,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n12010,5,269,2017-06-08 18:19:06,http://heidenreich.name/rosa,0.1490686367,10.28.193.13,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n12011,5,269,2017-05-21 02:43:15,http://ryan.io/zora_marquardt,0.1009912442,29.253.101.128,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n12012,5,269,2017-03-09 02:18:22,http://kunzeschaden.biz/jaclyn.schaefer,0.5978221576,249.189.197.31,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n12013,5,269,2017-02-21 13:54:13,http://daniel.org/gia_strosin,0.5639718745,31.41.210.140,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n12014,5,269,2017-05-05 06:45:00,http://cummingsmayer.io/paula,0.3270419902,214.160.97.230,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n12015,5,269,2017-04-13 11:33:23,http://cainwisoky.co/nova,0.0981890187,126.176.212.65,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n12016,5,269,2016-12-23 00:26:30,http://zboncak.biz/jennyfer_bergnaum,0.0487299878,45.242.230.126,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n12017,5,269,2017-02-01 08:19:28,http://lockman.co/hector.corkery,0.0078371218,239.159.232.181,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n12018,5,269,2017-05-13 04:06:16,http://murazik.com/kennedy,0.8326550255,77.58.47.73,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n12019,5,269,2017-03-23 17:47:37,http://cain.io/demarcus,0.2960890938,98.31.9.45,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n12020,5,269,2017-03-12 09:19:12,http://kub.info/nicola.kozey,0.6201079113,71.81.205.96,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n12021,5,269,2017-05-14 03:02:28,http://jast.name/macey,0.4924928829,35.241.171.12,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n12022,5,269,2017-02-14 01:21:05,http://feeney.net/jaclyn,0.1797621070,253.128.217.182,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n12023,5,269,2017-03-12 21:36:08,http://runolfsdottir.info/deangelo,0.1952823653,252.205.153.190,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n12024,5,269,2017-02-11 06:48:15,http://tremblay.org/katheryn,0.5578453583,129.84.35.213,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n12025,5,269,2017-04-16 20:12:53,http://damore.net/paxton_rempel,0.8417161320,38.152.50.205,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n12026,5,269,2017-01-12 12:04:19,http://harris.net/eldred_schmidt,0.8647438520,99.35.226.59,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n12027,5,269,2017-06-12 09:21:44,http://donnelly.co/delores,0.5918832636,150.77.205.227,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n12028,5,269,2017-02-11 20:57:32,http://macgyver.name/brayan,0.0232875349,238.57.203.153,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n12029,5,269,2017-05-20 04:55:28,http://mckenzie.org/enrique,0.3235847542,198.109.200.199,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n12030,5,269,2017-01-19 22:08:17,http://mueller.biz/beth.denesik,0.6415754576,232.142.36.63,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n12031,5,269,2017-04-18 11:36:52,http://nienow.co/charles.wehner,0.4728163508,249.18.30.32,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n12032,5,269,2017-03-01 19:30:52,http://gerlach.com/jairo,0.2542180712,250.105.132.237,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n12033,5,269,2017-03-01 09:06:47,http://heidenreich.info/bradford,0.1404943559,193.198.155.100,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n12034,5,269,2017-01-03 12:00:29,http://stiedemann.biz/delmer,0.7344922997,225.41.132.162,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n12035,5,269,2017-01-03 04:06:27,http://rowe.net/britney,0.9992014842,123.29.203.90,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n12036,5,269,2017-01-02 06:47:44,http://rutherford.name/crystal_dubuque,0.2552458318,220.181.231.72,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n12037,5,269,2017-04-30 21:44:52,http://bernhard.net/roy,0.9728514541,192.238.199.150,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n12038,5,269,2017-03-03 15:11:03,http://jerdevandervort.biz/providenci,0.7962400217,164.211.90.32,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n12039,5,269,2017-02-23 07:41:47,http://wilkinson.org/ellen_yost,0.7300726250,104.127.48.11,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n12040,5,269,2017-04-16 09:04:14,http://zemlakstokes.com/trisha,0.4945305453,174.216.141.60,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n12041,5,269,2017-01-20 18:57:08,http://stehr.org/amanda.vandervort,0.8990267437,101.243.115.11,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n12042,5,269,2017-02-04 08:12:20,http://white.co/kamron,0.7592601812,34.59.181.170,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n12043,5,269,2017-02-10 18:02:09,http://flatley.org/clotilde,0.7576953982,60.59.114.168,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n12044,5,269,2017-06-05 11:59:13,http://krajcik.org/lysanne,0.0515529037,25.143.50.128,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n12045,5,269,2017-04-04 17:23:14,http://ward.name/tad.okuneva,0.3121969329,38.9.220.8,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n12046,5,269,2017-01-01 12:32:49,http://stracke.name/freida_bayer,0.6963837850,37.115.199.108,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n12047,5,269,2017-03-21 13:22:31,http://ondrickareilly.org/caria_schoen,0.8335077512,81.98.214.147,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n12048,5,269,2017-05-13 19:14:05,http://west.info/sibyl_cummerata,0.5105976112,50.67.30.126,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n12049,5,269,2017-01-06 09:14:13,http://brakuspfeffer.name/lavada.graham,0.9282154430,215.198.35.11,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n12050,5,269,2017-01-06 05:57:19,http://spinka.net/earline,0.5674510161,161.9.133.164,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n12051,5,269,2017-01-01 20:09:50,http://hoegerlakin.biz/damion.kuhlman,0.0548341439,140.223.161.238,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n12052,5,269,2017-03-28 20:01:53,http://vonrueden.net/rosalind_bernier,0.6713182914,60.128.216.210,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n12053,5,269,2017-02-17 09:01:39,http://stark.net/savion,0.3978664781,235.226.98.225,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n12054,5,269,2017-03-11 09:21:10,http://keebler.name/estell,0.4396447672,171.35.172.248,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n12055,5,269,2017-02-10 23:04:58,http://mclaughlinquigley.biz/breanna.larson,0.8226971841,212.250.16.152,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n12056,5,269,2017-01-04 21:25:52,http://yost.com/kacie,0.6667122349,34.128.104.106,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n12057,5,269,2017-04-24 17:22:36,http://mills.com/emerson,0.4601424190,120.194.97.90,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n12058,5,269,2017-04-04 18:23:08,http://ward.org/catherine_dach,0.7773580455,60.199.118.135,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n14997,6,337,2016-12-30 00:51:17,http://daniel.com/waylon,,138.243.222.160,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n14998,6,337,2017-02-19 17:29:09,http://walter.net/lesley,,23.189.98.129,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n14999,6,337,2017-05-28 01:31:29,http://zboncak.name/toby_dickinson,,214.138.103.32,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n15000,6,337,2017-04-25 03:28:10,http://boyer.com/parker_hintz,,24.82.150.179,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n15001,6,337,2017-04-25 09:28:42,http://schowalter.info/ole,,60.142.121.14,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n15002,6,337,2017-01-17 19:58:48,http://abshire.biz/antonietta,,195.161.66.124,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n15003,6,338,2017-05-14 09:47:47,http://weber.org/rita,,215.125.245.40,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n15004,6,338,2017-04-08 10:40:08,http://hilpertgoyette.org/estelle.heller,,83.247.236.89,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n15005,6,338,2017-03-26 05:32:03,http://bogisich.org/gilda,,116.133.126.113,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n15006,6,338,2016-12-19 18:36:32,http://hauckhickle.co/zoe,,66.219.202.240,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n15007,6,338,2017-01-24 02:57:37,http://eichmann.name/zaria,,173.134.88.170,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n15008,6,338,2017-06-06 16:28:05,http://davis.org/tiana,,249.110.176.162,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n15009,6,338,2017-03-16 05:52:08,http://nienow.biz/carlie.cummerata,,107.23.69.223,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n15010,6,338,2017-02-09 12:21:28,http://handgoodwin.biz/allie,,224.168.32.19,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n15011,6,338,2017-01-05 15:42:22,http://robel.co/delilah,,28.178.118.74,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n15012,6,338,2017-04-04 19:02:46,http://feest.com/haan_white,,238.78.253.135,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n15013,6,338,2017-01-05 11:45:56,http://rolfson.com/meda.lakin,,92.151.149.17,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n15014,6,338,2017-05-26 08:51:38,http://reynolds.com/tyler,,45.56.195.83,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n15015,6,338,2017-01-14 17:25:20,http://klocko.org/thora_rogahn,,4.87.145.205,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n15016,6,338,2017-04-08 23:56:14,http://baumbachbernier.biz/valentina,,57.59.88.123,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n15017,6,338,2017-01-11 12:18:10,http://roob.name/tiara,,222.86.110.184,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n15018,6,338,2017-05-14 16:09:02,http://streich.biz/charlotte.vonrueden,,84.90.175.173,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n15019,6,338,2017-04-03 05:19:46,http://gleason.co/shawna,,207.92.131.115,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n15020,6,338,2017-03-27 16:40:27,http://stracketowne.info/emerald_hilll,,94.238.131.59,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n15021,6,338,2017-01-28 20:18:39,http://welchweber.name/jonatan,,224.203.231.120,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n15022,6,338,2017-01-18 09:08:26,http://hagenes.org/kurt,,239.114.192.4,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n15023,6,338,2017-03-06 12:01:27,http://beahanferry.org/kayla,,53.53.30.37,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n15024,6,338,2017-03-07 19:01:00,http://windler.io/lucienne_kerluke,,168.193.107.244,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n15025,6,338,2017-03-12 01:06:23,http://waelchi.name/efrain,,212.104.95.156,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n15026,6,338,2017-04-20 16:57:59,http://connelly.biz/chesley_dibbert,,235.146.251.250,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n15027,6,338,2017-04-23 22:01:26,http://runolfonrodriguez.name/uriah,,72.109.165.202,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n15028,6,338,2017-04-23 08:22:48,http://mcglynn.io/lorenzo,,69.106.73.91,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n15029,6,338,2017-02-13 20:01:54,http://sipes.io/shawn,,238.68.118.192,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n15030,6,338,2017-02-18 13:19:24,http://gottlieb.name/otho,,22.217.184.147,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n15031,6,338,2017-04-16 22:46:05,http://marquardtcain.io/trycia_flatley,,156.122.132.173,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n15032,6,338,2017-03-17 21:16:51,http://braunterry.io/kasey.reilly,,124.3.57.132,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n15033,6,338,2017-03-13 13:16:57,http://corwin.org/axel_hayes,,76.51.193.17,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n15034,6,338,2017-04-08 08:40:46,http://vandervort.biz/hallie,,46.70.129.74,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n15035,6,338,2017-01-13 14:30:26,http://murazikstoltenberg.com/gertrude_crona,,250.192.216.91,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n15036,6,338,2017-01-20 17:41:05,http://feestfeeney.name/aylin.yost,,76.35.199.180,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n15037,6,338,2017-04-02 14:36:16,http://champlinschowalter.org/gabe,,167.212.161.36,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n15038,6,338,2016-12-31 18:09:59,http://bergnaumdicki.com/hardy.jaskolski,,96.41.181.214,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n15039,6,338,2017-04-08 12:29:34,http://willms.org/jolie,,29.55.175.207,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n15040,6,338,2017-04-06 19:21:49,http://ruel.com/leonard,,80.34.224.191,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n15041,6,338,2017-05-04 03:59:33,http://lehner.biz/reymundo,,219.129.191.19,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n15042,6,338,2016-12-30 04:33:49,http://lang.co/amanda.blick,,37.79.215.75,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n15043,6,338,2017-02-06 14:36:22,http://strosin.biz/roberto_gorczany,,155.52.227.214,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n15044,6,338,2016-12-17 16:17:22,http://satterfield.info/dolores_mckenzie,,68.49.46.180,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n15045,6,338,2017-04-23 05:02:54,http://adamsbruen.io/roma.block,,216.36.32.89,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n15046,6,338,2017-01-18 04:13:39,http://blanda.net/dereck,,161.238.93.222,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n15047,6,338,2017-04-19 09:15:51,http://botsford.org/selina.gutkowski,,49.253.56.215,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n15048,6,338,2017-03-30 09:02:20,http://colesipes.org/effie,,211.212.159.250,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n15049,6,338,2017-02-20 20:49:29,http://jaskolskitillman.name/tianna,,161.3.127.35,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n15050,6,338,2017-05-28 07:18:46,http://hegmann.info/joanie_auer,,117.129.152.32,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n15051,6,338,2017-02-25 22:10:22,http://hyatt.name/esteban,,150.133.11.84,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n15052,6,338,2017-05-29 01:25:39,http://keelingcremin.io/elsie_prosacco,,191.244.190.189,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n9143,4,205,2017-04-22 06:40:29,http://bailey.name/reta,0.2256821006,102.165.138.69,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n9144,4,205,2017-03-23 22:28:59,http://boehmbins.org/angela,0.9112830698,180.151.97.85,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n9145,4,205,2017-05-08 20:40:47,http://goodwinhackett.name/christina.hills,0.4902191709,143.133.119.33,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n9146,4,205,2017-01-21 01:07:52,http://kozey.name/alexa,0.5379756666,217.84.110.196,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n9147,4,205,2017-04-06 10:43:38,http://grantcummerata.biz/river.stracke,0.0355948960,223.120.106.205,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n9148,4,205,2016-12-22 21:29:10,http://klein.io/roma,0.7196241445,227.63.22.113,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n9149,4,205,2017-01-29 15:45:40,http://dare.info/nikita,0.3442478471,173.196.195.187,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n9150,4,205,2016-12-16 06:11:47,http://dickinson.name/viviane,0.8937007900,226.210.217.85,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n9151,4,205,2017-03-06 14:13:00,http://hegmannfisher.com/mya.rowe,0.2616255212,252.60.199.198,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n9152,4,205,2017-05-18 05:17:08,http://wisokysimonis.io/taryn_wolf,0.2010558656,214.209.16.46,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n9153,4,205,2017-03-31 13:09:06,http://skileswuckert.io/trycia,0.9094021604,168.229.34.19,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n9154,4,205,2017-05-18 23:28:38,http://stanton.net/roie,0.3947653885,73.10.89.249,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n9155,4,205,2017-04-04 11:43:15,http://homenicklynch.info/berneice_barton,0.5035305484,24.232.142.238,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n9156,4,205,2017-01-18 11:27:24,http://satterfield.net/stefanie_nitzsche,0.5346291665,40.43.220.201,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n9157,4,205,2017-01-12 21:38:16,http://crona.org/mike_west,0.5673669563,2.61.36.2,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n9158,4,205,2017-02-14 17:02:28,http://oreilly.info/luigi_ebert,0.9677572493,226.195.218.215,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n9159,4,205,2017-01-14 16:16:40,http://white.name/delbert,0.4220793030,111.253.221.113,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n9160,4,205,2017-05-13 02:35:26,http://west.com/emmanuelle,0.7299551041,230.6.126.36,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n9161,4,205,2017-03-10 16:58:40,http://pfeffersimonis.co/weston,0.5072743161,190.93.100.228,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n9162,4,205,2017-05-26 10:04:43,http://okunevabeer.name/roie,0.8913576936,2.126.180.63,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n9163,4,205,2017-05-14 07:26:14,http://welchheathcote.com/celestine,0.6944279246,131.77.152.139,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n9164,4,205,2017-01-28 19:39:38,http://glover.biz/gabriel,0.2268501227,163.193.78.32,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n9165,4,205,2017-03-09 06:59:01,http://franecki.io/vena.torp,0.7127921445,4.162.234.111,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n9166,4,205,2017-05-11 14:06:26,http://powlowski.net/meagan,0.3337514132,58.9.14.89,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n9167,4,205,2017-03-09 11:13:08,http://swaniawski.co/leola,0.2640261639,181.222.176.91,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n9168,4,205,2017-03-01 00:31:04,http://feest.name/amya_jast,0.4031073256,169.170.182.250,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n9169,4,205,2017-02-10 14:28:05,http://damore.co/marilou,0.6047272861,226.46.222.104,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n9170,4,205,2017-05-09 08:51:40,http://dubuque.org/tristian_strosin,0.3059447626,245.93.4.238,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n9171,4,205,2017-06-06 04:52:05,http://jacobi.org/antonetta_ankunding,0.9318954889,93.254.55.34,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n9172,4,205,2017-04-04 00:53:15,http://swaniawski.co/gaylord,0.1223763208,127.252.37.161,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n9173,4,205,2017-05-08 11:05:13,http://schuppe.net/addison.bayer,0.8534238626,163.51.39.46,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n9174,4,205,2017-04-20 02:33:01,http://murphy.name/felton.torp,0.2017467171,93.241.235.18,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n9175,4,205,2017-05-22 23:32:52,http://feil.net/elisabeth,0.1549648509,170.80.119.84,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n9176,4,205,2017-04-10 04:31:06,http://pfannerstill.name/lila_waters,0.6935064244,208.251.201.201,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n9177,4,205,2017-05-29 21:21:16,http://purdynicolas.info/reece,0.0111927293,104.130.95.44,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n9178,4,205,2017-03-13 14:47:00,http://bayer.org/mark,0.4648782469,237.95.120.142,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n9179,4,205,2017-05-12 03:40:15,http://fahey.io/clovis,0.8989951313,124.97.233.109,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n9180,4,205,2017-03-12 05:18:33,http://eichmannhalvorson.info/ike,0.8817087621,98.204.29.122,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n9181,4,205,2017-01-18 17:25:55,http://jaskolski.io/wilford,0.8523479727,187.62.42.46,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n9182,4,205,2017-04-26 22:53:25,http://hagenes.net/lisandro_ferry,0.0084174318,105.231.142.219,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n9183,4,205,2017-05-03 02:40:17,http://glover.name/annabel,0.5708733287,100.227.204.83,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n9184,4,205,2017-04-29 11:24:08,http://schuster.org/corine,0.7316064770,218.70.232.69,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n9185,4,205,2017-05-30 03:55:54,http://wuckert.org/bria_glover,0.5574463229,191.111.102.57,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n9186,4,205,2017-02-19 23:35:53,http://kirlincruickshank.io/misael_schmidt,0.9549867210,79.128.120.213,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n9187,4,205,2017-03-25 04:47:56,http://moore.name/ottilie_kuphal,0.3397509773,168.250.142.125,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n9188,4,205,2017-01-14 03:24:01,http://tillman.co/susanna,0.3418296875,243.105.135.35,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n9189,4,205,2017-04-26 00:07:29,http://buckridge.info/lonny,0.8326977532,151.220.80.145,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n9190,4,205,2017-01-03 06:56:01,http://torp.org/jadon.monahan,0.5547797549,62.23.74.99,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n9191,4,205,2017-02-08 02:24:35,http://schoen.io/demond,0.3486094667,121.68.40.233,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n9192,4,205,2017-02-27 12:04:21,http://mclaughlin.net/johnathon_franecki,0.8613185552,43.13.196.73,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n9193,4,205,2017-05-22 02:31:57,http://balistreri.io/jamil,0.3936402323,244.21.210.121,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n6148,3,134,2017-04-18 10:56:49,http://leffler.net/eryn,,251.193.74.234,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n6149,3,134,2017-04-10 23:29:42,http://abshire.info/furman.harris,,150.93.166.161,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n6150,3,134,2017-04-09 00:52:19,http://hartmannweimann.biz/maryjane,,35.68.76.169,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n6151,3,134,2017-02-13 22:22:41,http://keeblerhowe.org/lavada.trantow,,26.72.87.214,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n6152,3,134,2017-04-05 00:44:44,http://king.io/carmine.lang,,72.196.31.196,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n6153,3,134,2017-03-15 12:37:22,http://kunde.co/verla,,36.76.93.58,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n6154,3,134,2017-02-07 01:27:11,http://connjaskolski.com/amiya.fahey,,77.244.174.21,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n6155,3,134,2016-12-27 06:01:08,http://nicolasbashirian.name/telly,,82.102.185.136,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n6156,3,134,2017-03-01 19:38:05,http://fisherfadel.co/nathen_koch,,251.135.88.120,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n6157,3,134,2017-06-06 14:01:47,http://schuppe.net/linwood_gottlieb,,25.149.251.252,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n6158,3,134,2017-01-17 02:19:58,http://yost.io/addie,,46.43.193.176,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n6159,3,134,2017-04-26 12:08:35,http://heathcote.io/irwin.schroeder,,76.226.101.50,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n6160,3,134,2017-04-28 05:16:51,http://hicklethompson.name/hattie,,144.25.165.86,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n6161,3,134,2017-02-02 12:23:54,http://spinka.biz/waylon.heller,,183.46.42.136,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n6162,3,134,2017-05-23 15:51:29,http://ortiz.org/brandyn.vonrueden,,147.31.204.47,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n6163,3,134,2017-01-10 03:56:47,http://runolfon.io/bette.emmerich,,22.122.7.161,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n6164,3,134,2017-04-19 00:21:15,http://spinkafadel.net/tyra_prohaska,,149.27.84.61,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n6165,3,134,2017-01-16 22:13:02,http://thompson.io/ted_halvorson,,110.119.144.137,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n6166,3,134,2017-05-20 18:55:13,http://wilkinson.name/maiya.bosco,,124.189.71.202,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n6167,3,134,2017-05-14 00:52:56,http://brekkewintheiser.com/colby.torphy,,63.5.69.149,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n6168,3,134,2016-12-16 07:34:33,http://medhurstbartell.com/michael,,82.218.105.28,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n6169,3,134,2017-04-13 13:29:06,http://dicki.biz/isom,,92.189.185.184,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n6170,3,134,2017-01-31 10:06:04,http://moriette.com/jennings,,118.192.159.250,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n6171,3,134,2016-12-15 03:45:52,http://weinat.name/eliza_bailey,,149.140.3.63,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n6172,3,134,2017-02-01 10:59:04,http://oreillyoreilly.name/dale,,138.238.61.210,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n6173,3,134,2017-02-01 18:00:49,http://heller.org/antonio.kunze,,142.45.15.54,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n6174,3,135,2017-01-23 11:22:50,http://wolff.info/patricia_mcglynn,,179.177.143.216,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n6175,3,135,2017-04-06 13:04:42,http://thompson.info/elian.wiegand,,156.45.134.146,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n6176,3,135,2017-01-01 18:13:00,http://vandervorthaley.net/geoffrey,,72.213.67.28,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n6177,3,135,2016-12-30 15:12:25,http://damoreheller.name/wilton,,97.172.15.81,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n6178,3,135,2017-05-17 14:35:31,http://mills.co/aleen,,133.129.245.237,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n6179,3,135,2017-02-14 01:33:19,http://padbergruecker.info/celestino.runolfon,,15.206.99.104,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n6180,3,135,2017-03-15 00:00:12,http://balistrerigrant.net/rylan_kirlin,,231.33.184.169,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n6181,3,135,2017-03-20 21:30:40,http://robel.co/sylvia,,16.194.79.116,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n6182,3,135,2017-03-29 13:14:11,http://hickle.net/rocio.dickens,,113.133.35.134,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n6183,3,135,2017-03-18 22:11:27,http://borersanford.io/francesco,,58.208.184.97,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n6184,3,135,2016-12-14 03:44:59,http://kautzerboyle.com/elda,,40.99.85.7,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n6185,3,135,2017-06-03 10:14:26,http://jakubowskiwilkinson.net/tania,,205.25.49.49,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n6186,3,135,2017-05-20 01:40:27,http://block.info/joana.gislason,,60.42.165.209,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n6187,3,135,2017-02-06 23:25:19,http://volkman.com/barrett,,163.200.208.151,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n6188,3,135,2017-04-06 08:39:18,http://schummmckenzie.io/rickie,,251.48.109.66,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n6189,3,135,2017-01-10 00:46:01,http://okonfunk.name/jazmyne,,28.61.26.192,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n6190,3,135,2017-01-21 06:33:44,http://hegmann.com/granville.stanton,,75.194.71.113,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n6191,3,135,2017-06-01 05:51:31,http://abbott.com/weston_bauch,,53.101.140.128,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n6192,3,135,2017-01-13 17:42:40,http://gerlachwalter.biz/waino.bogan,,137.131.135.188,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n6193,3,135,2017-05-09 08:19:43,http://roberts.biz/jovanny_cummerata,,37.44.113.30,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n6194,3,135,2017-04-09 03:28:17,http://kemmerjohns.name/orin,,5.213.129.40,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n6195,3,135,2017-02-15 18:50:07,http://wiza.org/desiree_grant,,45.80.89.195,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n6196,3,135,2017-04-27 22:19:48,http://keeblerhammes.net/jadyn,,235.64.171.198,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n6197,3,135,2017-02-26 16:38:25,http://nader.name/theron,,230.104.104.239,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n6198,3,135,2016-12-26 10:21:50,http://gerholdberge.org/emile,,65.246.132.136,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n6199,3,135,2017-05-09 09:39:02,http://goodwin.com/marilou,,68.153.28.220,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n6200,3,135,2017-03-15 23:09:15,http://dubuque.info/enola,,241.86.229.87,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n6201,3,135,2016-12-26 19:51:05,http://schneider.info/leone,,202.142.114.130,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n6202,3,135,2017-01-04 03:59:52,http://grantrau.org/wellington.padberg,,232.211.117.41,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n6203,3,135,2016-12-19 13:35:12,http://marquardthauck.co/viva,,28.157.162.171,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n12059,5,269,2017-06-02 05:30:13,http://aufderhar.io/kaitlyn,0.4445752241,55.55.83.6,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n12060,5,269,2017-01-09 02:33:22,http://sporer.co/alana_bashirian,0.9942746574,102.116.37.77,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n12061,5,269,2017-04-13 10:10:26,http://kautzer.org/vance.predovic,0.1076890855,173.143.204.228,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n12062,5,270,2017-04-03 22:31:21,http://rodriguez.co/kimberly.welch,0.6231263565,114.8.90.61,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n12063,5,270,2017-02-03 16:21:46,http://swaniawski.biz/karolann.jast,0.6740172015,232.252.212.26,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n12064,5,270,2017-04-16 11:52:32,http://feil.net/alexandro.schaden,0.4500720812,247.76.27.171,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n12065,5,270,2017-06-12 22:57:25,http://kautzer.org/hermann_strosin,0.3812269844,206.243.245.4,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n12066,5,270,2017-06-14 01:41:01,http://luettgen.name/amaya,0.8312571527,138.46.61.216,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n12067,5,270,2017-02-14 12:57:44,http://boehmwunsch.io/herbert,0.9890807699,29.124.229.155,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n12068,5,270,2017-06-07 10:33:46,http://price.com/emelia_welch,0.4842501111,79.160.41.151,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n12069,5,270,2017-06-10 23:57:01,http://beattypurdy.com/clementine_ward,0.8696854050,90.179.241.239,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n12070,5,270,2017-04-28 01:09:01,http://haagkiehn.org/winona,0.1514734737,68.139.213.70,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n12071,5,270,2017-02-18 14:40:07,http://lind.name/elia.kertzmann,0.9314016705,195.164.135.231,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n12072,5,270,2017-01-07 05:59:38,http://schumm.net/pattie,0.2848337342,149.37.223.19,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n12073,5,270,2017-02-07 08:19:05,http://braun.com/marjory,0.5272372853,231.235.122.107,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n12074,5,270,2017-03-02 01:47:27,http://hilll.co/charles.braun,0.9322564333,109.9.41.132,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n12075,5,270,2017-04-16 01:40:11,http://dach.net/magnolia_koelpin,0.3298946993,13.11.14.232,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n12076,5,270,2017-04-24 03:48:33,http://bogan.com/jadon.ernser,0.8375385266,91.30.35.67,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n12077,5,270,2017-05-16 02:03:07,http://williamson.biz/minerva_bailey,0.9760023623,121.194.147.186,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n12078,5,270,2017-02-17 11:56:32,http://stromanhamill.io/kelly,0.2857649178,233.222.241.135,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n12079,5,270,2017-04-12 18:19:40,http://nienow.name/ashley,0.8513482430,162.111.74.31,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n12080,5,270,2017-01-18 06:29:33,http://pagacmetz.info/jasmin_kris,0.1452375785,66.102.174.43,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n12081,5,270,2017-01-17 23:08:52,http://hansen.net/lafayette_rice,0.2495088824,26.191.140.134,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n12082,5,270,2017-03-21 05:15:13,http://stokesondricka.co/karlie,0.2121652510,151.142.195.50,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n12083,5,270,2017-01-30 02:56:30,http://daniel.info/reva,0.0470262413,173.103.7.68,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n12084,5,270,2016-12-27 18:25:42,http://nikolauskeler.com/zakary,0.2862411638,47.11.169.121,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n12085,5,270,2017-03-20 01:36:57,http://rosenbaum.name/adela_grant,0.1434473061,157.10.80.246,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n12086,5,270,2017-05-18 08:05:50,http://herman.name/ivory_wuckert,0.5275954853,185.24.10.8,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n12087,5,270,2017-01-10 15:34:33,http://gerhold.info/kitty_gusikowski,0.9524461317,186.206.209.48,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n12088,5,270,2016-12-29 10:10:09,http://flatleymoen.name/janelle,0.2466624080,6.3.79.182,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n12089,5,270,2017-02-27 12:17:18,http://koepp.name/rolando,0.5116147936,254.170.172.160,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n12090,5,270,2017-01-21 14:58:28,http://damore.co/conor_monahan,0.8557456064,185.190.207.243,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n12091,5,270,2017-04-12 14:04:15,http://stiedemanngrady.org/markus.jast,0.3681156658,95.155.168.112,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n12092,5,270,2017-05-01 13:13:43,http://greenfelderjacobson.org/carole.will,0.1190346987,85.149.138.212,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n12093,5,270,2017-02-20 17:27:18,http://mcdermottmurphy.org/rachelle_kihn,0.8878591510,71.167.240.38,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n12094,5,270,2017-02-27 00:23:22,http://konopelskigleichner.net/kaelyn.grant,0.1105416168,148.18.4.191,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n12095,5,270,2017-05-31 03:48:49,http://swifthamill.io/lora.howell,0.1640670758,19.37.177.140,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n12096,5,270,2017-02-16 23:44:06,http://gutmann.org/elva_white,0.0988592039,78.34.23.74,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n12097,5,270,2017-03-23 13:31:51,http://kreigervolkman.name/monica,0.4454310415,145.202.212.70,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n12098,5,270,2017-03-04 00:31:40,http://buckridge.io/floy_predovic,0.5559864616,246.219.250.80,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n12099,5,270,2017-02-17 15:10:24,http://daugherty.io/landen,0.4714608421,165.215.104.161,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n12100,5,270,2017-04-07 05:35:44,http://stehrmcdermott.io/keyon,0.1595940489,239.45.129.234,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n12101,5,270,2017-01-04 12:56:44,http://rutherford.com/halie,0.2240296337,219.191.51.208,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n12102,5,270,2017-01-21 23:22:47,http://hahn.io/gretchen,0.6105054904,95.221.96.47,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n12103,5,270,2017-04-11 18:44:08,http://mueller.io/jordan_walter,0.7324863717,49.140.209.131,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n12104,5,270,2017-02-26 16:51:23,http://beiernicolas.io/kailey,0.2604616583,187.122.171.233,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n12105,5,270,2017-05-26 16:56:43,http://lehnerankunding.io/johann,0.2765160865,93.187.241.71,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n12106,5,270,2017-05-22 07:21:59,http://lehner.io/florida,0.9421040274,199.48.220.63,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n12107,5,270,2017-01-05 23:47:57,http://haag.net/triston,0.7969915163,99.86.245.182,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n12108,5,270,2017-03-16 23:40:26,http://goyette.info/vincent,0.4964221716,188.108.13.17,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n12109,5,270,2017-04-07 17:56:45,http://sauer.co/candelario_mohr,0.3437343330,204.207.133.239,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n15053,6,338,2017-05-12 21:28:39,http://beahan.org/odie.kub,,176.208.75.184,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n15054,6,338,2017-04-28 16:22:13,http://kuhn.io/juanita,,27.190.63.173,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n15055,6,338,2017-05-12 11:43:17,http://mcclure.co/mabel_hudson,,201.189.225.16,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n15056,6,338,2017-06-12 00:11:21,http://moriette.net/frederick_huel,,200.78.99.221,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n15057,6,338,2017-03-31 16:41:50,http://douglas.co/talia_ward,,100.21.4.201,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n15058,6,338,2017-03-23 03:08:40,http://creminwatsica.co/darion_ruel,,146.167.138.108,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n15059,6,338,2017-02-23 18:01:51,http://schaden.biz/margarett,,6.231.103.121,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n15060,6,338,2017-03-26 00:21:23,http://kovacekkeler.name/manuel_yundt,,106.233.50.244,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n15061,6,338,2017-03-25 11:17:03,http://heidenreich.biz/rubye_moore,,229.167.185.30,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n15062,6,338,2017-04-06 23:20:41,http://swifthagenes.org/bonita_dooley,,196.106.178.189,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n15063,6,338,2017-03-19 18:19:00,http://collinsmacejkovic.biz/glenda.klein,,65.76.121.41,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n15064,6,338,2017-02-26 00:52:41,http://spinka.co/kariane.lubowitz,,195.4.114.220,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n15065,6,338,2017-01-22 18:36:16,http://oberbrunnerabernathy.info/garland_heathcote,,121.225.185.96,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n15066,6,338,2017-05-21 23:47:53,http://mueller.org/rachael_bayer,,55.18.165.205,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n15067,6,338,2017-05-11 14:07:19,http://roberts.net/sid.roberts,,86.25.84.63,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n15068,6,338,2017-02-11 16:00:32,http://oreilly.info/aurelie_keler,,192.101.49.49,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n15069,6,338,2017-03-24 16:09:35,http://nadercartwright.name/kathlyn,,99.87.164.159,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n15070,6,338,2017-03-28 15:07:12,http://hicklelangworth.org/marcus_halvorson,,39.147.133.203,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n15071,6,338,2017-03-11 16:31:52,http://ortizankunding.biz/fannie,,68.39.248.236,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n15072,6,339,2017-04-06 18:15:17,http://waelchibecker.net/kitty_brown,,217.89.104.96,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n15073,6,339,2017-02-16 14:09:10,http://schimmel.info/jarrod.halvorson,,62.181.9.205,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n15074,6,339,2017-04-30 03:23:13,http://jerde.io/clemmie,,221.253.169.162,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n15075,6,339,2017-03-14 18:16:22,http://green.io/rosa_wuckert,,22.175.78.239,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n15076,6,339,2017-06-02 18:13:52,http://schamberger.net/ivory.schiller,,81.36.125.167,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n15077,6,339,2017-03-17 21:17:13,http://abshire.net/sadie,,47.122.216.239,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n15078,6,339,2017-01-10 18:29:31,http://funk.name/bernard.fadel,,41.137.170.185,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n15079,6,339,2017-02-06 10:35:09,http://erdman.org/wallace,,110.136.59.136,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n15080,6,339,2017-05-08 04:53:58,http://williamson.com/lelia,,74.114.162.167,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n15081,6,339,2017-05-28 01:31:21,http://kub.org/harry_wyman,,174.12.58.141,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n15082,6,339,2017-02-03 04:11:13,http://ruel.name/coty.streich,,120.98.214.109,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n15083,6,339,2016-12-20 04:41:04,http://ratke.net/louvenia.kuhlman,,59.43.79.20,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n15084,6,339,2017-03-19 03:30:39,http://keler.co/hector,,63.121.195.204,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n15085,6,339,2016-12-24 13:31:10,http://rolfson.co/miguel,,21.219.214.74,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n15086,6,339,2017-03-22 22:14:51,http://flatley.io/elisabeth,,202.69.45.238,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n15087,6,339,2017-01-19 15:52:42,http://veum.net/leta,,132.174.18.48,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n15088,6,339,2017-03-16 20:30:21,http://schmidt.io/theresa,,93.184.237.166,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n15089,6,339,2016-12-17 03:23:55,http://eichmann.com/landen,,91.134.108.172,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n15090,6,339,2017-03-03 19:27:18,http://davis.biz/freddy,,180.80.125.27,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n15091,6,339,2017-02-24 16:24:17,http://torpbuckridge.name/sigmund,,50.131.195.111,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n15092,6,339,2017-06-11 14:32:13,http://sanford.biz/americo,,247.75.145.197,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n15093,6,339,2017-02-03 15:14:30,http://balistreribatz.name/mable,,204.201.243.201,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n15094,6,339,2017-02-17 01:09:34,http://lubowitz.com/litzy_fritsch,,173.180.171.212,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n15095,6,339,2017-02-08 10:55:50,http://abshireshanahan.co/jameson,,171.141.53.240,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n15096,6,339,2017-05-14 02:46:53,http://keebler.co/evalyn_lemke,,232.124.77.217,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n15097,6,339,2017-06-07 06:59:58,http://willms.com/irwin,,224.232.131.105,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n15098,6,339,2017-04-04 10:00:45,http://flatleyhamill.net/jaren,,40.174.201.133,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n15099,6,339,2017-01-22 21:41:36,http://parisian.org/mabelle,,199.37.86.85,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n15100,6,339,2017-01-01 15:50:26,http://blockkling.info/jayne,,102.217.29.43,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n15101,6,339,2017-04-14 17:47:23,http://brakus.info/dorthy,,240.143.14.156,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n15102,6,339,2017-01-19 00:04:43,http://corkery.info/fredy.white,,70.38.88.146,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n15103,6,339,2017-03-22 07:53:01,http://mayerlarkin.com/cristina.weimann,,107.30.246.84,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n15104,6,339,2017-02-18 11:15:40,http://watsica.name/bo,,210.146.209.100,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n15105,6,339,2017-02-12 15:59:37,http://pfeffer.io/miouri,,218.35.62.250,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n15106,6,339,2017-02-14 02:11:30,http://greenholtbatz.biz/edwardo.hickle,,197.70.193.243,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n15107,6,339,2017-01-29 14:45:39,http://mann.org/demond,,189.128.228.13,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n9194,4,205,2017-05-24 17:59:15,http://hillsjenkins.co/nickolas.stark,0.1486469762,129.164.77.136,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n9195,4,205,2017-01-12 20:34:57,http://kingrunolfon.biz/ewald_bins,0.8858589040,158.181.107.35,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n9196,4,205,2017-01-31 16:44:54,http://mitchellschamberger.info/elna.gutmann,0.1441584937,91.209.231.155,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n9197,4,205,2017-05-11 16:07:53,http://gleichner.co/jovanny_ullrich,0.2194563581,69.35.34.102,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n9198,4,205,2017-03-26 17:08:58,http://rolfson.io/jacklyn,0.5587352141,194.207.193.41,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n9199,4,205,2017-04-22 07:44:10,http://erdman.io/bart,0.6339333540,57.96.64.216,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n9200,4,205,2017-02-07 02:00:53,http://ritchietillman.info/noelia.lebsack,0.2251079585,95.107.43.205,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n9201,4,206,2016-12-16 14:59:52,http://miller.io/aracely,0.9100050953,167.144.49.13,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n9202,4,206,2017-03-19 08:11:05,http://cartwrightwitting.io/harmon,0.0799805408,21.254.99.67,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n9203,4,206,2017-05-13 22:38:02,http://macgyver.info/josue.kling,0.8319751394,48.176.96.15,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n9204,4,206,2017-03-05 17:54:06,http://kris.biz/caitlyn,0.4852922526,58.51.227.200,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n9205,4,206,2016-12-13 18:22:00,http://steuber.co/meagan.stracke,0.3655498089,225.195.167.216,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n9206,4,206,2017-02-20 04:13:30,http://yundt.co/derick,0.9014575402,233.33.79.154,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n9207,4,206,2017-01-24 18:52:54,http://greenfelderkoch.net/hermann.homenick,0.0755470803,93.80.57.237,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n9208,4,206,2017-05-29 03:50:30,http://beckermonahan.biz/andreane,0.7576713509,114.222.238.8,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n9209,4,206,2017-05-15 21:48:36,http://cole.info/jany,0.3436693031,35.53.190.5,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n9210,4,206,2017-01-12 00:09:34,http://priceadams.co/miles,0.9425489426,238.54.171.200,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n9211,4,206,2017-01-28 16:31:05,http://kreigersmith.net/miles,0.6706872392,113.128.85.13,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n9212,4,206,2017-04-23 05:13:32,http://tremblayhudson.info/modesta,0.2026104572,219.68.72.45,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n9213,4,206,2016-12-28 15:56:16,http://volkman.org/avis,0.6246514592,107.212.137.51,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n9214,4,206,2017-05-28 02:00:24,http://collierrenner.info/nathan.torp,0.2951615887,248.89.213.31,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n9215,4,206,2017-06-07 17:23:24,http://wittingblick.name/sonny,0.7895017671,32.165.243.221,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n9216,4,206,2017-04-21 09:33:03,http://kuvalis.com/bud,0.4437604608,63.226.154.52,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n9217,4,206,2017-06-03 17:31:56,http://johnhields.org/gunnar_thompson,0.6667069416,133.254.227.186,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n9218,4,206,2017-01-24 14:45:20,http://bogisich.co/kaycee.will,0.5524306225,25.52.186.215,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n9219,4,206,2016-12-15 21:54:15,http://zieme.net/mckenna.ward,0.9415597634,118.55.84.164,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n9220,4,206,2017-05-23 12:25:39,http://hartmannheaney.co/isaias,0.4321246880,137.198.146.116,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n9221,4,206,2017-05-09 16:02:50,http://jacobson.co/name.kihn,0.7730106533,172.178.22.171,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n9222,4,206,2017-05-27 03:00:36,http://mayert.biz/kara.zemlak,0.5256156880,212.207.242.163,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n9223,4,206,2017-05-23 00:45:59,http://shanahan.co/shakira,0.7948530899,159.195.199.84,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n9224,4,206,2017-03-25 13:58:58,http://feest.co/marjolaine.kuphal,0.9568539712,65.124.44.219,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n9225,4,206,2017-05-11 01:03:05,http://hodkiewiczconsidine.info/antwon,0.3871411351,252.156.34.33,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n9226,4,206,2016-12-23 21:07:01,http://crookskertzmann.biz/giovanny_friesen,0.1711776252,18.58.60.23,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n9227,4,206,2016-12-23 03:44:46,http://dickidicki.com/maximo,0.3413620981,125.68.4.92,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n9228,4,206,2017-01-01 23:20:32,http://harber.org/adan_king,0.1525092676,64.112.151.213,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n9229,4,206,2017-05-18 07:47:22,http://hudson.biz/shane,0.0600363110,10.9.61.18,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n9230,4,206,2017-01-05 09:45:43,http://waters.io/valentine,0.3482018189,250.106.177.14,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n9231,4,206,2017-03-03 05:10:43,http://tillman.com/naomie.gaylord,0.1910742377,134.108.47.219,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n9232,4,206,2017-02-05 22:38:26,http://pfannerstill.io/francisca,0.8297523765,74.201.243.117,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n9233,4,206,2017-01-30 18:12:31,http://gorczany.org/luis.pollich,0.1865280105,252.87.153.156,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n9234,4,206,2017-02-21 14:48:23,http://okon.org/alexander.turcotte,0.2085916382,5.193.174.36,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n9235,4,207,2017-04-28 11:23:58,http://gerlach.org/charity,0.1862029814,137.201.114.46,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n9236,4,207,2017-05-15 00:08:11,http://harrislindgren.com/matt,0.4754738060,246.39.107.39,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n9237,4,207,2017-04-14 19:38:55,http://orn.biz/ophelia_boyle,0.3337471096,189.194.193.154,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n9238,4,207,2017-03-30 22:40:57,http://raynor.net/ines,0.1749852478,117.101.78.218,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n9239,4,207,2016-12-31 12:41:49,http://marvin.io/adolfo_rice,0.6309963848,178.19.239.187,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n9240,4,207,2017-03-22 19:03:34,http://feest.io/emmy,0.2347868019,80.77.138.242,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n9241,4,207,2017-03-26 16:32:33,http://strosin.info/alfred,0.6264832332,45.179.239.10,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n9242,4,207,2017-01-13 13:22:46,http://lowe.com/eleanora.donnelly,0.3862410861,233.182.138.89,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n9243,4,207,2017-05-12 10:54:07,http://jakubowskidach.org/esperanza,0.1933427601,198.145.11.235,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n9244,4,207,2017-02-28 09:16:13,http://howe.co/ambrose,0.1973236241,216.20.9.235,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n9245,4,207,2017-01-23 04:20:06,http://effertzhermann.io/alexanne,0.4733063123,131.182.203.249,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n6204,3,135,2017-02-10 09:13:06,http://rempellangworth.name/angelina,,32.163.161.14,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n6205,3,135,2017-01-16 20:24:16,http://haneblock.com/loy_spinka,,26.140.124.189,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n6206,3,135,2017-03-09 23:50:54,http://mckenzie.io/rocky.dicki,,209.185.139.80,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n6207,3,135,2017-02-10 11:00:58,http://stammcrooks.info/don.douglas,,86.160.216.76,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n6208,3,135,2016-12-26 17:47:57,http://rowe.co/raquel,,203.235.140.142,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n6209,3,135,2017-05-26 14:22:39,http://haag.io/beulah_turcotte,,181.222.93.101,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n6210,3,135,2017-03-12 13:49:53,http://von.org/marcia,,2.153.247.217,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n6211,3,135,2016-12-27 13:42:39,http://grimes.info/bridie.connelly,,68.217.236.97,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n6212,3,135,2017-04-25 15:50:00,http://simonis.net/brendon,,8.226.40.193,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n6213,3,136,2017-05-30 04:57:41,http://tremblay.info/gillian_carter,0.1304169010,223.243.221.129,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n6214,3,136,2017-05-03 02:38:40,http://koelpin.org/rosetta,0.6987075191,41.153.232.8,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n6215,3,136,2017-03-25 20:14:59,http://beatty.com/josephine,0.3103418293,132.83.204.12,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n6216,3,136,2017-02-07 04:18:05,http://altenwerthcummerata.name/magdalena.wyman,0.9120022161,110.214.11.140,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n6217,3,136,2017-03-20 12:53:59,http://hills.org/cale_gerlach,0.8935410738,170.118.170.86,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n6218,3,136,2017-05-23 21:04:30,http://buckridge.co/dora_botsford,0.9321871171,89.20.13.104,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n6219,3,136,2017-05-18 17:44:50,http://hamill.co/gilda,0.6287615392,217.158.167.186,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n6220,3,136,2017-04-24 18:38:02,http://auer.io/gabriel.kutch,0.7846586075,176.92.17.250,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n6221,3,136,2017-03-20 06:05:10,http://ko.com/holden.quitzon,0.6074397445,118.153.251.55,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n6222,3,136,2017-02-02 19:14:22,http://lednerswift.com/jasper,0.6236198541,205.99.98.166,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n6223,3,136,2017-01-20 14:09:37,http://stiedemannflatley.net/magnolia_boehm,0.9124009477,232.96.201.173,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n6224,3,136,2017-05-09 13:58:39,http://shields.org/natalie,0.3432271085,165.143.73.226,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n6225,3,136,2017-01-22 11:01:07,http://grant.org/trudie,0.2218677671,198.25.4.91,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n6226,3,136,2017-02-19 05:52:05,http://kuhlman.com/name_green,0.9436802366,40.129.46.136,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n6227,3,136,2017-05-20 20:55:41,http://gorczanymoore.net/lonzo,0.5418630111,65.59.91.16,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n6228,3,136,2016-12-22 05:47:51,http://batzprosacco.biz/timothy,0.8773651602,22.37.134.195,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n6229,3,136,2016-12-23 04:50:41,http://cummerata.co/fredy.kaulke,0.3287255280,238.171.200.238,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n6230,3,136,2017-02-28 21:14:39,http://gutmann.net/bianka_goodwin,0.5555421206,28.197.106.63,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n6231,3,136,2017-06-11 15:28:58,http://runolfsdottir.name/buddy_botsford,0.1932598364,222.39.85.46,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n6232,3,136,2017-01-14 21:39:43,http://carrollflatley.com/blanche.kuhic,0.8936742240,41.98.104.223,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n6233,3,136,2016-12-23 09:42:16,http://bashirian.org/rosina,0.1217784851,43.198.114.153,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n6234,3,136,2017-02-26 06:24:53,http://huel.com/edgardo,0.5919622926,27.98.183.95,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n6235,3,136,2017-04-03 02:47:36,http://hudson.co/loyal,0.4696667372,11.138.106.168,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n6236,3,136,2017-01-22 22:20:49,http://abernathy.name/rickie_maggio,0.6517122502,104.70.38.174,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n6237,3,136,2017-05-02 11:16:04,http://graham.biz/jada,0.5078953375,97.83.164.244,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n6238,3,136,2017-05-14 00:07:05,http://olsonhudson.info/earline,0.4456315306,44.230.21.127,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n6239,3,137,2017-03-14 21:28:05,http://langworthbatz.biz/jacinthe,0.6026301897,179.252.234.224,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n6240,3,137,2017-03-22 15:55:39,http://sawaynmuller.net/letha,0.9239881055,3.18.37.204,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n6241,3,137,2017-01-19 07:59:58,http://graham.info/aric,0.4572211326,93.181.103.195,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n6242,3,137,2017-03-18 08:55:31,http://larson.biz/cameron.schaefer,0.8049743126,31.235.136.141,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n6243,3,137,2017-04-28 10:18:36,http://gleasonbayer.com/stacy.auer,0.4727663590,49.101.199.203,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n6244,3,137,2017-05-06 20:26:51,http://steuber.org/estel.labadie,0.3439952014,241.245.168.212,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n6245,3,137,2017-02-08 19:03:51,http://dibbert.net/michel,0.6141984872,188.68.183.101,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n6246,3,137,2017-05-15 21:30:20,http://hillljerde.co/wade_hintz,0.8529427565,59.21.195.94,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n6247,3,137,2017-03-10 10:53:59,http://roobgoodwin.biz/antwan,0.6093570111,228.233.212.125,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n6248,3,137,2017-02-02 06:08:42,http://parisian.name/dax,0.1382939850,228.183.39.172,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n6249,3,137,2017-05-24 03:42:48,http://monahan.co/isabella,0.8997006660,170.112.63.150,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n6250,3,137,2017-03-05 16:14:59,http://kaulkegottlieb.name/dorothy_casper,0.0167946557,103.245.8.82,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n6251,3,137,2017-06-08 18:01:14,http://howe.info/ruel_koch,0.6079230112,83.176.18.153,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n6252,3,137,2017-05-16 07:17:12,http://gusikowski.name/eino,0.9678693641,178.42.171.45,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n6253,3,137,2017-05-04 09:22:12,http://mclaughlinprice.net/samson,0.3165578042,215.155.114.161,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n6254,3,137,2017-04-17 18:05:19,http://stokes.io/ryley,0.5300459882,202.86.130.173,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n6255,3,137,2017-05-12 18:49:21,http://douglas.co/meggie.hintz,0.3575868941,244.60.96.4,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n12110,5,270,2017-06-09 03:58:30,http://swaniawski.biz/reie,0.5112924218,218.63.36.171,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n12111,5,270,2017-04-26 17:17:41,http://eichmann.biz/ludwig,0.9980377398,118.249.37.6,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n12112,5,270,2017-01-23 16:38:40,http://shanahan.co/lavonne.dibbert,0.7424350835,29.247.244.117,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n12113,5,270,2017-06-01 19:39:54,http://goodwin.net/lori,0.2069524489,231.239.72.131,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n12114,5,270,2017-04-10 06:23:47,http://rohan.net/merritt_blick,0.5913165126,189.152.70.164,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n12115,5,270,2017-01-19 20:01:19,http://baumbachfisher.com/janea,0.0198536664,225.209.162.142,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n12116,5,270,2017-05-27 14:04:48,http://jerdewyman.org/selmer.hermann,0.2244732613,39.228.71.132,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n12117,5,270,2017-05-02 06:49:24,http://reichel.org/mireya,0.3948823616,71.221.78.187,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n12118,5,270,2017-04-25 06:47:30,http://littel.info/estelle_nikolaus,0.6883469080,130.185.101.87,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n12119,5,270,2016-12-27 17:54:04,http://langwilliamson.co/alfred,0.1089695199,223.173.200.161,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n12120,5,270,2017-04-27 00:54:32,http://hand.co/alene,0.5766178798,98.163.46.103,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n12121,5,270,2017-04-16 08:34:21,http://gusikowski.info/okey_carroll,0.8552397876,69.150.86.120,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n12122,5,270,2017-05-15 21:08:20,http://carter.com/rhiannon,0.7649971136,132.19.6.34,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n12123,5,270,2017-02-17 13:25:38,http://beahan.org/everett,0.1261063826,78.172.69.164,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n12124,5,270,2017-04-09 16:58:29,http://johnson.com/keanu,0.1705002015,236.70.28.115,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n12125,5,270,2017-06-08 00:50:48,http://okuneva.name/bernhard.hoppe,0.8756187518,164.141.144.152,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n12126,5,270,2017-05-18 15:36:08,http://windler.info/camylle,0.7560108979,141.90.227.202,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n12127,5,270,2017-04-29 15:29:26,http://walsh.com/brant,0.5252656327,3.192.78.41,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n12128,5,270,2017-04-20 07:11:54,http://auerzboncak.io/kenya_kilback,0.3878928500,8.71.168.188,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n12129,5,270,2017-04-22 04:18:29,http://braun.net/abby_flatley,0.1338790860,108.118.26.190,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n12130,5,270,2017-05-24 19:50:50,http://altenwerth.biz/eunice,0.4391788519,78.155.74.210,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n12131,5,270,2017-01-13 05:39:27,http://larson.net/chaz,0.7577919437,124.138.142.92,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n12132,5,271,2017-01-01 22:56:31,http://pfefferklein.biz/berneice_gusikowski,0.1528351116,119.135.236.215,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n12133,5,271,2016-12-22 10:37:31,http://boscolittel.net/diamond_goodwin,0.8140434586,68.247.177.215,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n12134,5,271,2017-01-09 03:16:09,http://cummings.co/marion.connelly,0.8805606478,145.231.100.117,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n12135,5,271,2017-04-30 12:31:02,http://skiles.io/winston,0.0958300270,244.200.152.138,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n12136,5,271,2017-05-27 00:45:44,http://jacobs.co/kellen,0.0501203629,70.72.206.58,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n12137,5,271,2017-02-16 10:05:57,http://langosh.biz/vince,0.7827857300,44.61.231.129,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n12138,5,271,2017-03-27 21:00:32,http://sanford.com/marjory.mraz,0.7837173731,4.126.205.55,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n12139,5,271,2017-04-17 08:13:26,http://rempel.name/vella.gibson,0.0916990792,197.217.162.175,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n12140,5,271,2017-04-24 20:22:58,http://ankunding.co/meagan,0.1272529807,102.177.96.148,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n12141,5,271,2017-04-24 03:33:35,http://oconnell.info/alexander.stoltenberg,0.4186582783,82.102.176.35,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n12142,5,271,2016-12-23 22:15:01,http://mcglynn.info/colt_dubuque,0.5228071822,223.143.53.205,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n12143,5,271,2017-04-01 07:52:41,http://mcculloughhudson.com/wilhelmine_prohaska,0.7683406035,64.34.85.172,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n12144,5,271,2017-03-01 14:26:58,http://goyette.net/nikita_leffler,0.6256323693,204.145.129.117,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n12145,5,271,2017-04-13 18:29:23,http://jacobson.co/julie,0.3090947700,17.26.27.78,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n12146,5,271,2017-04-19 09:46:44,http://oconnerdaniel.io/alice.lesch,0.3026562252,190.123.230.177,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n12147,5,271,2017-01-23 05:40:18,http://ernser.io/alek.wehner,0.3374379186,12.236.206.196,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n12148,5,271,2017-05-22 06:07:05,http://nicolas.co/alvera.fadel,0.2390870799,54.202.126.47,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n12149,5,271,2017-04-30 16:08:32,http://rutherford.net/bo,0.5254076580,95.194.234.39,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n12150,5,271,2017-03-23 15:30:20,http://lockman.com/natalie,0.6999005549,141.197.79.200,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n12151,5,271,2017-05-02 21:58:40,http://lebsack.io/rogers_abernathy,0.3167431577,206.17.195.99,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n12152,5,271,2017-01-25 13:06:19,http://veumlehner.co/yasmeen,0.5760805995,239.9.174.168,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n12153,5,271,2017-01-03 15:21:00,http://lockman.net/lonny_price,0.8140926397,139.93.208.204,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n12154,5,271,2017-04-11 21:04:38,http://strosinhettinger.co/cory_pagac,0.6472540787,21.247.62.123,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n12155,5,271,2017-06-09 15:02:39,http://emard.com/ashlee,0.1136928866,208.137.16.102,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n12156,5,271,2017-05-28 04:47:28,http://denesikberge.com/mac,0.6682052332,98.31.178.107,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n12157,5,271,2017-01-20 01:44:09,http://heller.org/darian,0.5804666592,93.41.73.124,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n12158,5,271,2017-02-05 04:00:57,http://mullerdicki.info/hazel,0.9811452595,20.156.140.101,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n12159,5,271,2017-04-17 14:25:28,http://trantow.name/stephania,0.7461452680,28.190.150.226,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n12160,5,271,2017-01-12 09:06:37,http://goyettemetz.io/hermina,0.1317994816,20.161.82.117,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n12161,5,271,2017-03-16 09:57:17,http://weimann.org/montana,0.8721054668,124.230.70.87,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n15108,6,339,2017-06-13 02:13:15,http://quitzonschimmel.io/rowland,,146.199.33.183,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n15109,6,339,2017-05-22 05:27:12,http://leschwisozk.biz/ericka,,39.136.149.120,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n15110,6,339,2017-04-30 09:55:48,http://kirlin.co/nina,,28.12.44.105,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n15111,6,339,2017-05-11 05:00:54,http://monahanweinat.com/demarco,,113.74.91.192,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n15112,6,339,2017-06-09 00:06:59,http://nicolashudson.info/vinnie_heaney,,181.125.226.16,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n15113,6,340,2017-05-18 23:27:46,http://bernier.biz/kolby_mertz,,97.133.161.136,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n15114,6,340,2016-12-31 20:21:57,http://yundt.info/estell,,135.192.108.85,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n15115,6,340,2017-02-06 01:23:19,http://lowe.io/kirsten_schoen,,138.63.173.4,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n15116,6,340,2017-03-22 05:42:59,http://ziemann.io/korbin,,115.148.95.14,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n15117,6,340,2017-02-03 01:03:35,http://lockman.co/erin,,210.171.222.157,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n15118,6,340,2017-03-30 16:23:43,http://lang.com/liliana,,224.55.72.32,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n15119,6,340,2016-12-30 15:38:43,http://weberwilliamson.biz/cleo_kaulke,,90.178.59.232,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n15120,6,340,2017-02-18 17:30:08,http://whitekuvalis.com/mariano.kemmer,,163.70.211.154,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n15121,6,340,2017-05-19 22:51:38,http://schowalter.io/damon.sauer,,219.19.152.111,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n15122,6,340,2017-04-22 20:41:24,http://larkin.co/brycen_okon,,175.76.12.59,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n15123,6,340,2017-01-06 20:25:58,http://shields.co/norval,,81.23.211.183,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n15124,6,340,2017-03-19 17:41:09,http://gusikowskiframi.biz/terrill,,176.84.63.22,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n15125,6,340,2017-06-10 10:55:49,http://vandervort.org/ransom.oconnell,,99.140.192.94,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n15126,6,340,2017-01-19 05:02:28,http://legrosklein.info/marjory_crona,,3.104.49.86,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n15127,6,340,2017-05-13 04:16:42,http://pfannerstill.info/electa,,176.249.205.19,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n15128,6,340,2017-04-26 04:35:03,http://heelabbott.io/annabelle,,103.248.9.169,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n15129,6,340,2017-04-25 08:34:33,http://braunkeler.biz/kacey.farrell,,70.50.196.188,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n15130,6,340,2017-02-14 16:53:10,http://dubuquebruen.biz/mertie,,139.161.188.119,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n15131,6,340,2017-03-02 09:29:25,http://oconnerbailey.co/darrin,,21.152.34.176,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n15132,6,340,2017-01-02 09:36:11,http://stracke.io/lorna,,91.124.202.160,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n15133,6,340,2016-12-14 07:41:02,http://spinka.biz/kenyon,,13.68.238.204,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n15134,6,340,2017-01-11 16:12:28,http://gaylordmills.net/colton.eichmann,,76.19.80.126,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n15135,6,340,2016-12-22 09:29:56,http://ricewilkinson.org/norris,,239.184.204.152,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n15136,6,340,2017-04-06 17:15:12,http://stracke.biz/braulio.okeefe,,12.192.6.161,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n15137,6,340,2017-05-12 14:48:56,http://greenfelder.net/savanna.schultz,,23.173.41.147,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n15138,6,340,2017-05-10 19:03:31,http://kiehnerdman.net/pablo,,140.126.228.70,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n15139,6,340,2017-03-08 07:03:19,http://jast.net/asia,,229.30.51.56,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n15140,6,340,2017-02-27 16:58:46,http://schuster.biz/carson,,246.223.23.129,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n15141,6,340,2017-01-15 20:37:54,http://vonruedenhane.com/angie,,165.79.69.159,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n15142,6,340,2016-12-20 22:25:07,http://hanemccullough.name/stacy,,118.62.236.50,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n15143,6,340,2017-05-06 20:12:41,http://rennerheaney.io/sydni.bins,,107.35.89.161,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n15144,6,340,2017-04-11 04:19:14,http://lesch.info/amos,,96.218.220.60,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n15145,6,340,2017-04-25 10:24:45,http://bergnaum.info/lynn.aufderhar,,170.204.134.103,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n15146,6,340,2017-04-27 23:47:24,http://schumm.name/arely,,190.41.93.33,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n15147,6,340,2017-04-22 18:45:06,http://okon.info/danielle,,136.191.169.39,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n15148,6,340,2017-04-30 13:18:17,http://pacocha.co/darrin,,216.240.62.253,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n15149,6,340,2017-04-14 23:00:33,http://schimmel.info/madisyn,,212.217.109.12,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n15150,6,340,2017-02-22 18:59:23,http://berge.org/kadin,,71.187.206.241,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n15151,6,340,2016-12-21 00:44:38,http://lehnermoore.net/savannah.schroeder,,90.249.144.90,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n15152,6,340,2017-06-10 00:56:07,http://schowalter.com/penelope.wiza,,83.5.172.29,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n15153,6,340,2017-04-29 10:53:11,http://bradtke.org/roxanne,,246.233.96.143,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n15154,6,340,2017-05-25 10:43:29,http://heidenreich.org/tyler_barrows,,81.41.111.37,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n15155,6,340,2017-01-30 17:31:52,http://jerde.info/beau.toy,,7.183.18.90,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n15156,6,340,2017-03-10 11:08:23,http://smith.info/verona_emmerich,,62.9.23.16,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n15157,6,340,2017-03-12 11:40:22,http://mulleryost.biz/iliana,,202.177.213.74,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n15158,6,340,2017-04-11 00:33:37,http://hartmanncarter.org/ernestina.ratke,,211.137.153.6,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n15159,6,340,2017-01-07 04:16:55,http://daniel.com/asa,,222.174.254.194,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n15160,6,341,2017-04-02 23:34:09,http://pagac.io/aric_lindgren,,169.118.182.220,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n15161,6,341,2017-03-15 00:01:13,http://barrowsmarquardt.org/idella,,50.77.220.152,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n15162,6,341,2017-06-11 01:01:21,http://grimes.biz/samara.von,,194.114.126.236,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n15163,6,341,2016-12-29 14:53:43,http://block.org/cristina_schiller,,43.143.64.78,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n2315,2,52,2017-03-28 03:00:59,http://bechtelarcain.info/annetta,0.8117562816,94.27.121.20,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n2316,2,52,2017-03-22 15:11:20,http://larson.info/lolita.blanda,0.0864011113,166.9.120.174,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n2317,2,52,2017-04-09 16:21:28,http://runte.com/lucile,0.5552759115,136.114.18.117,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n2318,2,52,2017-03-28 11:19:40,http://huels.co/tamia_reilly,0.4273676662,206.193.206.127,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n2319,2,52,2016-12-20 12:47:52,http://boehm.co/bradly,0.4063026458,31.151.2.100,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n2320,2,52,2016-12-24 10:38:34,http://grantkoepp.com/peggie.fahey,0.7660398481,40.51.228.228,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n2321,2,52,2017-04-15 19:51:51,http://krisbecker.info/roxane.fay,0.0729742021,120.82.183.116,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n2322,2,52,2017-05-10 19:22:39,http://gusikowski.org/evangeline,0.0457805507,142.244.229.31,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n2323,2,52,2017-02-18 21:54:40,http://marquardt.biz/alisha,0.6443649442,105.179.115.94,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n2324,2,52,2016-12-26 14:57:22,http://ortiz.name/hank,0.2442663746,248.29.27.188,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n2325,2,52,2016-12-30 15:36:54,http://goldner.org/reilly_maggio,0.8042181519,181.199.189.153,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n2326,2,52,2017-02-17 12:59:58,http://casper.net/kristopher,0.2699786469,23.138.31.248,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n2327,2,52,2017-02-10 02:42:45,http://watsicaschuppe.com/edd,0.2284343090,64.101.58.210,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n2328,2,52,2017-01-07 22:10:25,http://gutkowski.biz/amanda,0.0311637941,221.128.65.186,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n2329,2,52,2017-02-08 19:01:58,http://johns.name/piper.schmitt,0.0032607542,74.147.205.245,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n2330,2,52,2016-12-25 19:53:41,http://ruecker.net/alana.effertz,0.1777038242,152.5.39.152,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n2331,2,52,2017-04-21 11:32:28,http://klein.biz/reyes,0.8044369694,37.5.108.165,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n2332,2,52,2016-12-28 05:56:18,http://lynchmurray.co/alexane,0.2049284918,113.204.192.139,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n2333,2,52,2016-12-25 17:56:08,http://kuphal.biz/aurelia,0.1870730105,156.83.117.89,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n2334,2,52,2017-01-26 22:26:05,http://daregreen.net/kelley.beahan,0.5246625391,89.44.42.42,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n2335,2,52,2017-05-02 19:26:11,http://kovacek.info/maximillian_lang,0.1232737121,243.24.5.206,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n2336,2,52,2017-01-25 22:42:35,http://zulaufmills.biz/chloe,0.4194587873,29.242.81.115,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n2337,2,52,2017-03-24 07:54:29,http://kertzmann.com/andreanne_witting,0.3723470282,149.29.115.136,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n2338,2,52,2017-03-29 05:27:05,http://walter.info/nona_weber,0.9891523491,153.64.109.208,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n2339,2,52,2017-02-17 15:54:26,http://bergnaummraz.co/devon.wyman,0.0708524469,142.122.34.62,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n2340,2,52,2016-12-28 23:26:58,http://langworth.net/shaniya,0.6008142440,98.24.131.218,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n2341,2,52,2016-12-14 18:33:06,http://kertzmann.co/zita_tillman,0.5359397516,99.191.68.193,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n2342,2,52,2017-05-10 02:56:30,http://grimes.info/alyce,0.4276145713,211.106.96.176,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n2343,2,52,2017-02-25 06:00:35,http://davis.info/trea,0.4742620606,18.168.220.134,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n2344,2,52,2017-03-31 18:13:52,http://conroy.name/denis,0.8156568666,200.66.223.134,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n2345,2,52,2017-01-04 21:58:20,http://becker.io/jalen,0.0056497214,12.66.135.32,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n2346,2,52,2017-06-06 22:56:03,http://wehner.biz/noe,0.7771645561,102.229.136.44,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n2347,2,52,2017-05-27 19:44:33,http://haley.name/jed,0.2199990683,246.22.72.58,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n2348,2,52,2017-03-10 14:53:14,http://effertz.io/hortense.okon,0.3712002844,116.234.223.235,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n2349,2,52,2017-01-11 21:42:01,http://gottlieb.net/lucy_champlin,0.3727873244,150.99.219.56,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n2350,2,53,2017-03-25 19:08:20,http://ruecker.net/saige_glover,0.5164576592,52.19.130.63,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n2351,2,53,2017-05-02 11:34:12,http://armstrong.io/mellie,0.5096708646,27.100.193.245,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n2352,2,53,2017-04-26 03:59:20,http://ankunding.biz/kody.brakus,0.8617573774,170.31.17.118,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n2353,2,53,2017-03-15 13:08:17,http://kub.org/henry.ratke,0.3484772765,189.37.198.112,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n2354,2,53,2017-05-18 19:33:14,http://marquardtrunte.co/adriana,0.1508065710,178.54.214.115,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n2355,2,53,2017-01-16 15:30:56,http://ruelmarquardt.biz/kaylin_hyatt,0.7883620728,162.21.226.246,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n2356,2,53,2016-12-21 02:57:42,http://ernserfadel.org/kailey,0.3706178185,212.119.250.114,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n2357,2,53,2017-01-15 02:16:52,http://hansenschultz.org/lloyd_rosenbaum,0.0349892231,60.234.145.107,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n2358,2,53,2017-03-01 07:25:31,http://veumstrosin.com/vernice_hartmann,0.1261609038,68.23.54.201,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n2359,2,53,2017-04-15 07:59:47,http://murazikstehr.co/timmy,0.8189687834,163.208.25.209,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n2360,2,53,2017-01-13 21:50:51,http://conn.net/elta.white,0.4690406864,126.105.241.108,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n2361,2,53,2017-03-17 07:48:54,http://oberbrunnermuller.name/libbie,0.7521856388,167.185.68.138,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n2362,2,53,2017-03-24 23:03:03,http://ziemeemard.io/addie,0.6090183262,80.138.96.214,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n2363,2,53,2017-02-09 09:10:21,http://hettinger.co/shanon_shields,0.5964716013,248.83.118.80,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n2364,2,53,2017-05-02 18:27:10,http://graham.biz/drew,0.5118672575,215.91.251.12,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n9246,4,207,2017-06-14 04:47:00,http://bins.info/cleta_dietrich,0.2631021989,189.187.76.41,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n9247,4,207,2016-12-30 17:27:27,http://muller.net/westley,0.5409273462,105.238.222.248,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n9248,4,207,2017-02-15 19:05:50,http://faheywyman.com/telly_predovic,0.5960631414,57.27.41.129,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n9249,4,207,2017-05-30 18:40:55,http://croninarmstrong.info/bartholome_rath,0.8393735103,37.124.63.219,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n9250,4,207,2017-03-22 13:36:42,http://mccullough.org/elody.lebsack,0.7581915253,246.164.117.245,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n9251,4,207,2017-02-02 16:37:55,http://hammes.info/anastasia_conn,0.5398227014,186.192.110.250,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n9252,4,207,2017-05-12 22:36:31,http://leannon.io/austen,0.2575185081,132.16.25.211,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n9253,4,207,2017-03-30 14:53:47,http://roberts.net/beverly,0.0689722918,179.115.250.70,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n9254,4,207,2017-04-30 15:56:32,http://willms.com/retha_moore,0.9440775669,248.67.129.253,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n9255,4,207,2016-12-16 07:48:03,http://steuber.info/elfrieda,0.7630692993,215.46.50.189,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n9256,4,207,2016-12-21 18:31:48,http://champlinschaefer.org/ebba_wuckert,0.0005517334,63.134.92.186,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n9257,4,207,2017-03-28 18:10:23,http://turner.name/andrew.schimmel,0.9356970245,41.96.86.233,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n9258,4,207,2017-01-04 11:10:24,http://dicki.org/darlene_strosin,0.5237157390,212.50.230.230,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n9259,4,207,2017-02-16 19:48:08,http://beatty.biz/jaquelin,0.6996001955,110.220.132.148,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n9260,4,207,2017-05-26 22:59:56,http://greenfelder.co/dena.nader,0.8238089952,109.55.35.18,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n9261,4,207,2017-05-18 19:54:40,http://herman.net/reanna,0.7237817657,227.23.150.53,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n9262,4,207,2017-01-31 02:04:01,http://blandamayert.biz/dario.ko,0.7458458958,212.17.221.38,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n9263,4,207,2017-04-25 21:14:35,http://turner.co/josefina.nikolaus,0.2416807078,138.196.203.219,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n9264,4,207,2017-05-27 05:10:44,http://dietrich.com/tyreek.bauch,0.4698139780,186.67.198.89,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n9265,4,207,2017-04-25 15:32:03,http://rosenbaum.name/rene_koepp,0.6125750907,119.99.124.112,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n9266,4,207,2017-05-31 09:37:58,http://beatty.info/crystal,0.1021023838,124.122.99.151,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n9267,4,208,2017-05-03 00:48:13,http://buckridgevonrueden.org/mercedes_hayes,0.1183813167,249.238.28.243,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n9268,4,208,2017-03-07 11:54:50,http://kohlerwunsch.name/marcos,0.7167534405,29.215.249.127,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n9269,4,208,2017-04-16 05:58:18,http://jerde.org/bartholome_gerlach,0.2646265080,58.84.198.97,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n9270,4,208,2017-02-09 17:39:16,http://wiza.net/cleora,0.3902235589,105.50.207.112,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n9271,4,208,2017-03-02 20:24:57,http://nicolas.name/paula,0.4779355366,242.99.155.6,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n9272,4,208,2017-06-03 18:52:03,http://considine.name/geraldine.rogahn,0.5257825220,208.215.227.234,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n9273,4,208,2017-03-08 02:55:13,http://veum.name/rigoberto_buckridge,0.0987337405,232.180.204.222,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n9274,4,208,2017-05-23 07:10:33,http://moen.info/annette,0.3740418999,32.108.189.123,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n9275,4,208,2017-01-26 06:19:22,http://abshire.net/carolina,0.7237835254,18.22.152.92,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n9276,4,208,2017-04-29 04:28:20,http://runtehalvorson.com/creola,0.0785254861,190.64.16.191,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n9277,4,208,2017-05-21 06:41:55,http://walker.net/vivianne,0.4224005545,25.140.118.103,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n9278,4,208,2017-04-11 21:12:32,http://harber.com/nathaniel.larkin,0.3894485898,243.106.225.186,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n9279,4,208,2017-04-04 17:07:04,http://leuschke.co/jennyfer.weimann,0.2077013213,10.203.221.132,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n9280,4,208,2017-02-13 16:49:02,http://rippin.info/otilia,0.4329094838,107.121.46.49,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n9281,4,208,2017-01-12 20:28:23,http://nicolas.net/sally_dicki,0.7407448081,252.47.127.198,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n9282,4,208,2017-05-30 07:46:11,http://hanefeest.info/darrick.schuppe,0.6651645634,15.183.77.93,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n9283,4,208,2017-03-05 02:42:43,http://boganmayert.net/leon,0.9191018167,118.89.229.131,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n9284,4,208,2016-12-14 02:32:41,http://armstrong.name/lucienne.rowe,0.5926753287,125.220.90.233,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n9285,4,208,2017-05-02 09:04:33,http://boylejaskolski.com/gregg.terry,0.7420721660,108.57.239.78,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n9286,4,208,2017-05-29 19:42:16,http://green.com/jamir,0.1356057665,174.21.207.58,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n9287,4,208,2017-02-21 17:55:01,http://zieme.info/zetta_bruen,0.1907284576,224.31.216.135,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n9288,4,208,2017-03-07 07:38:36,http://schadenglover.io/stevie,0.6452982011,171.60.41.143,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n9289,4,208,2017-05-14 01:51:53,http://purdy.biz/providenci,0.4299172987,204.94.15.23,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n9290,4,208,2017-01-15 07:58:29,http://kuhlmanjacobson.org/brandt,0.6836529775,233.242.49.177,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n9291,4,208,2017-04-01 00:37:05,http://smith.name/johnnie,0.2417132898,219.150.93.145,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n9292,4,208,2017-05-31 11:58:12,http://schultzwilkinson.name/estefania,0.2319053558,228.102.183.26,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n9293,4,208,2017-04-22 07:12:06,http://hettinger.biz/foster,0.7954642393,149.232.31.71,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n9294,4,208,2017-05-06 14:26:41,http://johnsonhand.name/aurelia,0.5564617573,128.99.246.92,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n9295,4,208,2017-01-08 12:47:27,http://veum.com/rozella_schimmel,0.2940808970,27.13.15.206,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n9296,4,208,2017-05-17 15:24:07,http://zemlak.info/christine,0.0023034185,164.163.167.147,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n6256,3,137,2017-04-17 08:15:30,http://schillergleichner.com/cierra_davis,0.3444077548,161.45.112.132,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n6257,3,137,2017-03-30 20:21:27,http://faheyhaag.net/ruthe.macejkovic,0.3291536209,29.2.189.70,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n6258,3,137,2017-05-09 18:23:39,http://emardraynor.co/greg.langworth,0.3854922903,191.188.108.136,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n6259,3,137,2017-06-11 14:33:03,http://funk.com/sophie,0.9211509777,137.234.71.24,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n6260,3,137,2017-02-08 10:50:26,http://dibbertcollier.net/ludie_monahan,0.0002704200,56.79.186.246,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n6261,3,137,2017-04-17 14:08:04,http://turnerkuvalis.name/haven,0.9274661383,68.71.125.223,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n6262,3,137,2017-03-12 22:23:09,http://dickischuppe.org/aryanna,0.0865578861,108.104.94.84,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n6263,3,137,2017-03-04 17:10:58,http://cronacrooks.name/elias_spencer,0.1299530524,7.185.163.158,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n6264,3,137,2017-04-23 01:16:07,http://pagac.com/mariela,0.3814399773,158.114.28.213,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n6265,3,137,2017-01-23 19:35:39,http://rohanthiel.info/edward_cormier,0.5069687449,169.164.161.190,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n6266,3,137,2017-02-20 14:28:28,http://konopelski.name/delmer,0.6576658097,247.60.209.112,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n6267,3,137,2016-12-22 11:16:04,http://stracke.org/nicolette.wintheiser,0.5536527877,177.101.26.33,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n6268,3,137,2017-02-17 05:45:28,http://ullrich.org/otis,0.5446096613,129.220.162.148,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n6269,3,137,2017-05-17 17:52:59,http://beattyschmitt.co/jeremie_torphy,0.6997734032,139.179.80.164,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n6270,3,137,2017-04-16 19:12:47,http://ziemann.org/jamison_langosh,0.3558893123,196.178.48.201,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n6271,3,137,2017-05-10 20:02:26,http://ankunding.biz/malinda,0.4119439340,18.241.41.194,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n6272,3,137,2017-01-28 23:05:09,http://satterfield.net/marcos.marvin,0.5904091713,66.248.240.114,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n6273,3,137,2017-03-12 17:04:40,http://jaskolskiwhite.biz/mabelle.kihn,0.8506620112,135.153.151.69,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n6274,3,137,2017-03-20 05:37:50,http://heelromaguera.co/brenna.terry,0.1043294138,133.55.116.145,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n6275,3,137,2017-01-17 17:36:53,http://coledouglas.info/noah,0.5002019950,222.128.20.73,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n6276,3,137,2016-12-17 00:57:44,http://yundtlehner.com/katlyn,0.5099815493,58.17.63.129,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n6277,3,137,2017-03-10 03:20:43,http://schumm.org/kayden.collins,0.9535713041,225.241.103.55,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n6278,3,137,2017-03-25 09:02:52,http://stamm.org/hobart.emard,0.3547567871,70.36.170.76,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n6279,3,137,2017-05-07 04:34:14,http://stokes.com/bret_langworth,0.2248224318,98.10.143.153,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n6280,3,137,2017-06-02 01:11:44,http://colebechtelar.name/zane.kozey,0.1504423568,229.2.94.68,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n6281,3,137,2017-03-26 09:55:57,http://grahammertz.co/katharina,0.9658975774,187.130.225.65,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n6282,3,137,2017-03-01 01:03:45,http://heller.co/maritza,0.8618922175,214.213.189.161,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n6283,3,137,2017-06-09 01:20:25,http://reillybruen.io/oceane_ritchie,0.4929228842,77.180.228.211,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n6284,3,137,2017-06-06 18:48:56,http://millskulas.io/willow.koelpin,0.3434163902,4.212.16.167,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n6285,3,137,2016-12-23 00:58:44,http://little.name/lawrence,0.7815114377,10.238.244.4,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n6286,3,137,2017-02-16 12:47:15,http://schillerkonopelski.org/myrtis,0.2057812266,13.12.171.114,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n6287,3,137,2017-03-02 18:57:09,http://maggiostokes.name/calista,0.9175527536,23.242.122.55,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n6288,3,137,2017-05-08 21:06:57,http://volkman.info/freda.bauch,0.0659148056,252.147.120.169,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n6289,3,137,2017-05-29 08:51:33,http://hartmannrenner.name/melyna_fahey,0.4559406338,104.196.82.62,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n6290,3,137,2017-05-25 17:21:18,http://mueller.info/alfreda.rogahn,0.2921272151,102.99.4.242,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n6291,3,137,2017-05-18 22:21:18,http://collinporer.info/paul.gutmann,0.7008044921,124.137.231.49,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n6292,3,137,2017-04-26 16:53:33,http://gislason.net/retta,0.8635893812,69.213.114.123,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n6293,3,137,2017-05-14 22:05:24,http://olson.net/alfreda,0.9498455135,58.83.100.247,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n6294,3,137,2017-04-16 00:13:26,http://wolf.info/stefanie_kirlin,0.5415256470,111.71.111.41,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n6295,3,137,2017-05-21 07:39:04,http://okuneva.info/imani,0.1476843409,5.17.212.54,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n6296,3,137,2017-04-25 23:52:50,http://bogisich.net/jerome,0.0893724673,12.141.130.212,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n6297,3,137,2017-02-01 06:50:01,http://douglas.com/jensen.fritsch,0.8859816902,57.198.62.233,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n6298,3,137,2017-01-16 23:36:15,http://bechtelar.org/kameron_douglas,0.0955677983,153.211.78.123,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n6299,3,137,2017-03-29 22:01:01,http://dickensrogahn.biz/lolita,0.5846314723,163.252.195.73,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n6300,3,138,2017-06-12 09:09:17,http://gerhold.com/meredith,0.6334303105,162.137.185.77,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n6301,3,138,2017-03-16 00:38:45,http://hettinger.io/madyson.boehm,0.5764337266,66.253.78.69,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n6302,3,138,2017-01-15 10:39:20,http://wittingschiller.info/dorian,0.6731242524,49.243.85.121,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n6303,3,138,2017-03-20 19:21:39,http://gerlachwisoky.org/destin,0.8032572660,73.243.212.38,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n6304,3,138,2017-01-05 15:24:50,http://rowedouglas.info/verna,0.1295793538,129.74.9.24,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n6305,3,138,2016-12-27 09:58:26,http://doyle.io/magdalen.damore,0.2432063415,220.168.187.49,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n6306,3,138,2017-04-14 12:23:51,http://schmidt.co/glennie_sipes,0.9389140290,92.13.111.249,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n12162,5,271,2017-01-23 17:31:21,http://halvorson.biz/eden.romaguera,0.9152243315,154.169.230.5,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n12163,5,271,2017-03-24 03:11:13,http://keler.net/miracle,0.4523198428,77.30.59.138,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n12164,5,271,2017-03-25 13:44:55,http://lindgrencormier.name/talon,0.2941577176,18.138.174.22,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n12165,5,271,2016-12-26 22:38:21,http://collins.org/herbert.sauer,0.3437208237,32.38.198.224,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n12166,5,271,2017-04-25 10:05:12,http://runolfoneffertz.com/gardner,0.5347472070,126.115.246.73,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n12167,5,271,2017-03-10 09:13:23,http://prohaska.org/aiden,0.0395803779,47.154.100.116,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n12168,5,271,2017-05-21 21:24:56,http://little.name/katlynn,0.6627255584,165.58.71.202,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n12169,5,271,2017-05-23 18:28:10,http://wuckert.com/keira.mayer,0.3581529267,33.215.107.112,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n12170,5,271,2017-04-11 07:47:30,http://langworthmurazik.co/agustin.kunze,0.5502703345,104.49.202.144,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n12171,5,271,2017-03-26 15:17:48,http://considine.net/noemi.orn,0.5803195671,208.108.180.235,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n12172,5,271,2017-03-30 06:42:38,http://gaylordbarton.net/dora.ullrich,0.3028631099,49.45.48.45,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n12173,5,272,2017-01-06 02:59:11,http://smithamdurgan.co/della,0.7392904654,92.160.227.167,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n12174,5,272,2017-01-28 01:04:41,http://osinski.io/zoey,0.3231424598,19.84.135.166,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n12175,5,272,2017-04-21 06:53:01,http://turner.biz/lina_kub,0.4225825896,224.252.189.112,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n12176,5,272,2017-04-27 06:30:10,http://huel.co/alvah.smitham,0.5780021262,14.112.244.125,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n12177,5,272,2016-12-15 11:58:40,http://rogahn.name/erna,0.1400310900,164.123.133.170,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n12178,5,272,2017-01-22 12:17:02,http://cummerata.name/estell,0.5946274022,113.233.12.196,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n12179,5,272,2016-12-27 15:47:05,http://hueltehr.name/beaulah.kemmer,0.5386903027,76.43.98.199,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n12180,5,272,2016-12-31 07:57:57,http://nader.co/haie,0.3698294525,105.162.19.160,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n12181,5,272,2016-12-22 18:24:23,http://macgyver.co/adonis,0.6230459711,26.179.106.237,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n12182,5,272,2017-02-24 13:27:02,http://schneider.org/ania,0.3245742819,167.123.205.173,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n12183,5,272,2017-03-30 18:53:55,http://lubowitzlind.co/mavis,0.1589947128,92.123.61.63,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n12184,5,272,2017-06-06 13:08:33,http://gleasonhegmann.co/torey_lehner,0.7753999823,247.73.150.51,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n12185,5,272,2017-01-28 21:43:57,http://reinger.info/felicita_kunze,0.1524308345,131.79.57.93,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n12186,5,272,2017-03-25 03:31:02,http://halvorson.org/vivien,0.3292610349,34.66.95.108,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n12187,5,272,2017-02-09 08:25:51,http://casper.co/nathanial,0.1307309404,118.168.130.80,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n12188,5,272,2017-03-23 09:03:58,http://greenfelder.net/beau,0.1135911565,95.52.75.143,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n12189,5,272,2017-02-18 20:59:15,http://wolf.org/alize.balistreri,0.7754545229,112.196.145.147,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n12190,5,272,2017-01-02 16:46:18,http://kerluketreutel.org/enrique_mertz,0.1628890943,59.88.41.158,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n12191,5,272,2017-01-07 01:59:28,http://quitzon.co/libbie.hartmann,0.5713274612,198.119.226.196,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n12192,5,272,2016-12-28 01:03:02,http://walter.com/mohammed,0.5828578729,196.144.198.15,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n12193,5,272,2017-05-28 05:31:27,http://huels.co/myah,0.5868853343,202.5.5.129,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n12194,5,272,2017-01-29 06:49:26,http://nicolas.net/pansy,0.9304664227,110.187.190.55,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n12195,5,272,2017-03-25 01:50:27,http://maggio.net/jaydon.conn,0.0498080181,63.73.80.48,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n12196,5,272,2017-02-21 03:32:45,http://donnelly.biz/keyshawn,0.0115978437,52.46.109.141,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n12197,5,272,2017-04-05 03:42:15,http://lueilwitz.org/gardner_prosacco,0.0345924742,100.247.119.222,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n12198,5,272,2017-01-02 15:14:56,http://beerschulist.name/milo.jacobi,0.5278339549,3.249.91.192,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n12199,5,272,2017-04-25 14:02:35,http://hilpertondricka.co/jennyfer.brekke,0.9814302872,167.37.95.54,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n12200,5,272,2017-04-21 09:08:04,http://homenick.biz/norris,0.4516604227,195.35.251.138,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n12201,5,272,2017-03-30 03:06:04,http://ritchie.com/elroy_batz,0.9243214607,23.239.37.68,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n12202,5,272,2017-05-21 05:58:34,http://cummerata.net/frederic,0.8955865897,188.155.44.250,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n12203,5,272,2017-04-07 19:29:53,http://ruel.info/hudson,0.8549933199,199.167.132.32,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n12204,5,272,2017-05-15 11:59:43,http://kautzer.biz/sebastian,0.5383722068,249.125.93.21,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n12205,5,272,2017-06-05 04:22:06,http://senger.net/nyah,0.7066919282,215.51.15.37,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n12206,5,272,2017-06-03 19:16:41,http://bechtelar.net/jakob_predovic,0.8195985815,30.155.143.140,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n12207,5,272,2017-03-09 01:49:14,http://mayer.org/kasey,0.9179704231,201.106.133.69,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n12208,5,272,2017-06-04 04:43:17,http://hermannwaters.name/novella,0.8506033850,54.2.104.130,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n12209,5,272,2017-02-14 23:47:34,http://kautzer.biz/aniya,0.1529949030,144.138.86.136,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n12210,5,273,2017-05-12 23:16:02,http://borershields.org/ryan,0.7492205087,191.128.143.44,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n12211,5,273,2016-12-27 09:49:37,http://okonlynch.biz/teresa,0.3576184174,14.13.142.170,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n12212,5,273,2016-12-20 11:04:06,http://gutkowskikonopelski.com/reina,0.8463747150,125.129.41.4,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n15164,6,341,2017-03-18 12:52:44,http://kshlerin.co/nikolas.ebert,,166.131.154.212,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n15165,6,341,2017-01-31 07:56:25,http://christiansendaugherty.info/maxine.padberg,,7.194.57.104,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n15166,6,341,2017-03-22 21:49:07,http://mueller.biz/evalyn_wehner,,97.54.208.220,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n15167,6,341,2017-01-23 00:10:45,http://herman.io/eddie_little,,166.99.243.200,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n15168,6,341,2017-06-09 18:32:49,http://becker.net/fidel,,143.160.25.202,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n15169,6,341,2017-05-26 16:44:06,http://bechtelar.net/alivia,,86.86.70.8,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n15170,6,341,2017-01-11 01:35:58,http://bernhard.co/jose,,65.151.119.65,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n15171,6,341,2017-01-30 14:23:42,http://weber.net/chesley.yost,,77.57.231.165,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n15172,6,341,2017-06-13 14:46:19,http://emard.name/ruthie,,186.42.186.169,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n15173,6,341,2017-05-05 20:45:14,http://howe.co/rachelle,,44.44.249.197,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n15288,6,344,2017-04-12 09:25:44,http://flatley.org/ahmed,,27.41.53.187,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n15174,6,341,2017-02-06 16:52:00,http://lockman.name/jarvis.lakin,,230.224.152.216,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n15175,6,341,2017-06-11 17:29:49,http://medhurst.co/dora,,120.185.159.218,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n15176,6,341,2016-12-17 18:34:32,http://homenick.biz/ben.howell,,93.18.205.221,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n15177,6,341,2017-04-17 00:23:58,http://feil.com/montana.mayer,,21.77.184.141,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n15178,6,341,2017-02-15 20:48:22,http://brownlang.io/otto,,148.52.47.128,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n15179,6,341,2017-06-02 20:56:11,http://schneiderglover.name/millie.hartmann,,51.90.230.89,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n15180,6,341,2017-05-23 17:37:49,http://moriette.co/megane,,242.121.218.183,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n15181,6,341,2017-03-06 09:49:25,http://conroy.biz/josephine,,22.210.218.206,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n15182,6,341,2017-02-09 00:19:18,http://wuckert.biz/norwood_ledner,,27.250.6.93,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n15183,6,341,2017-04-24 01:59:28,http://adams.biz/elenor,,79.191.134.89,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n15184,6,341,2017-01-10 19:22:45,http://schiller.com/raina.langosh,,45.49.141.61,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n15185,6,341,2017-03-27 08:35:27,http://ziemann.org/carol.walsh,,164.86.156.89,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n15186,6,341,2017-01-03 11:08:37,http://blanda.co/rudolph.kemmer,,65.188.53.110,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n15187,6,341,2017-01-30 17:17:12,http://rosenbaum.com/cynthia.windler,,204.134.197.214,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n15188,6,341,2017-03-25 11:36:36,http://collierpollich.org/virgie.boehm,,241.20.229.176,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n15189,6,341,2017-03-02 00:32:43,http://oconner.name/zita.sanford,,129.183.172.93,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n15190,6,341,2017-02-18 14:57:36,http://aufderharlarson.org/nikki.nicolas,,42.36.164.25,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n15191,6,342,2016-12-31 10:07:01,http://rippin.org/riley,,77.162.191.36,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n15192,6,342,2017-02-09 17:44:10,http://pagac.name/margarita_walker,,246.170.185.24,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n15193,6,342,2017-02-28 11:17:34,http://ruel.com/gay_balistreri,,88.196.190.139,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n15194,6,342,2017-05-23 12:28:19,http://robertshodkiewicz.org/lou,,66.213.167.93,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n15195,6,342,2017-05-01 09:11:29,http://koelpin.biz/freeman.wolf,,67.37.79.217,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n15196,6,342,2017-03-22 04:13:42,http://cormier.io/consuelo.littel,,170.72.46.154,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n15197,6,342,2017-04-01 17:08:53,http://muellermante.co/lamar_farrell,,207.120.236.19,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n15198,6,342,2017-05-17 06:35:16,http://bashirian.biz/dean,,117.38.154.98,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n15199,6,342,2017-02-15 23:33:46,http://romaguera.com/melvin_littel,,252.165.249.12,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n15200,6,342,2017-05-21 05:33:36,http://oconner.org/gerald_schultz,,24.179.128.211,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n15201,6,342,2017-04-19 03:57:05,http://leannon.biz/london,,250.79.130.13,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n15202,6,342,2016-12-25 22:23:43,http://schuppe.org/madonna,,126.87.242.73,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n15203,6,342,2017-01-28 21:02:12,http://kovacek.biz/zion.rippin,,112.141.244.249,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n15204,6,342,2017-01-12 19:42:05,http://beckerarmstrong.info/lloyd_steuber,,49.18.31.254,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n15205,6,342,2017-04-03 08:45:01,http://morarernser.com/lottie,,28.92.44.213,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n15206,6,342,2017-05-18 07:32:45,http://little.io/marilyne,,167.35.249.35,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n15207,6,342,2016-12-16 22:24:13,http://williamson.io/jany,,85.179.173.152,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n15208,6,342,2017-05-25 02:31:01,http://schumm.name/janea,,27.8.253.51,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n15209,6,342,2017-04-05 02:59:56,http://mann.net/kale,,201.26.66.182,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n15210,6,342,2017-03-05 04:39:07,http://murray.info/emmy,,227.177.2.177,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n15211,6,342,2017-02-14 23:19:16,http://okeefekirlin.co/daphney,,223.96.179.218,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n15212,6,342,2016-12-22 05:13:59,http://ebertosinski.com/dino_connelly,,117.4.224.47,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n15213,6,342,2017-04-28 17:54:46,http://yostoreilly.info/mark,,107.78.190.252,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n15214,6,342,2017-03-26 17:51:01,http://swaniawski.org/sim.bogan,,94.214.52.117,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n15215,6,342,2017-01-23 09:09:44,http://osinskigottlieb.co/deja,,34.210.21.97,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n15216,6,342,2017-04-26 06:49:49,http://brakusjenkins.com/jadon,,2.219.119.7,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n15217,6,342,2017-01-07 09:15:04,http://abshire.io/colt,,138.97.219.174,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n2365,2,53,2017-03-20 17:37:27,http://hane.com/gerson.schinner,0.1946556893,154.155.250.88,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n2366,2,53,2017-04-20 12:57:07,http://feeney.org/fatima_flatley,0.8662892300,61.151.212.88,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n2367,2,53,2016-12-13 22:16:03,http://yostfarrell.info/arvid,0.4864209150,79.249.176.54,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n2368,2,53,2017-04-24 14:23:18,http://blick.co/susan.kautzer,0.8258063698,36.221.240.119,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n2369,2,53,2017-05-14 04:06:05,http://koelpinarmstrong.io/pamela,0.5978810584,9.29.198.173,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n2370,2,53,2017-05-22 17:27:43,http://senger.com/lucy_berge,0.0268709428,22.180.145.161,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n2371,2,53,2017-02-27 07:25:56,http://zboncak.net/ricky_torp,0.3816027039,137.57.47.138,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n2372,2,53,2016-12-30 15:40:30,http://nikolaus.io/kitty_damore,0.6807434403,27.9.80.207,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n2373,2,53,2017-03-13 00:58:42,http://wehnermarks.biz/laury_stroman,0.9948558469,104.17.7.100,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n2374,2,53,2017-02-26 21:35:49,http://hilll.info/donnell,0.2329052533,182.206.139.166,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n2375,2,53,2017-02-23 14:40:44,http://reichel.name/bernard,0.8797106997,91.17.226.131,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n2376,2,53,2017-01-11 14:59:17,http://welch.biz/paolo_reichert,0.5211365624,8.130.223.123,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n2377,2,53,2017-05-26 22:25:36,http://schimmel.org/danny,0.6611775519,148.4.226.136,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n2378,2,53,2017-03-06 07:55:47,http://mueller.net/tremayne,0.9946802045,108.243.121.244,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n2379,2,53,2017-03-28 17:39:45,http://gleichner.biz/baron,0.1557417745,158.195.56.201,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n2380,2,53,2017-05-10 23:18:45,http://wyman.org/ibrahim.tremblay,0.5578915306,3.220.131.177,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n2381,2,53,2017-03-28 11:20:18,http://ziemeryan.net/layne_zboncak,0.6882189772,86.139.152.246,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n2382,2,53,2017-05-21 08:04:45,http://yost.net/concepcion_padberg,0.0076692451,117.25.178.30,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n2383,2,53,2017-03-04 09:59:51,http://ruel.com/cecelia,0.9087857899,219.148.100.14,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n2384,2,53,2016-12-28 22:53:27,http://emmerich.org/avis,0.6129427694,130.65.140.114,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n2385,2,53,2017-02-26 11:13:28,http://cormier.io/evans.ziemann,0.3169762101,118.209.12.178,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n2386,2,53,2017-03-31 16:07:49,http://hermann.co/damien,0.5544336640,234.54.110.28,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n2387,2,53,2017-04-25 01:18:15,http://oconnellhomenick.biz/sasha,0.3456076971,153.82.156.109,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n2388,2,53,2017-04-03 23:02:26,http://oconner.io/avis,0.1863634178,52.191.156.122,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n2389,2,53,2017-05-20 15:52:53,http://bechtelar.net/reginald.pouros,0.5096195793,47.147.141.157,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n2390,2,53,2017-04-25 23:37:27,http://johnsongraham.io/gabe_kunde,0.1904917103,236.108.58.85,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n2391,2,53,2017-05-05 03:09:36,http://brakusblanda.name/jovanny,0.0045222434,124.95.214.23,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n2392,2,53,2017-03-19 19:42:05,http://von.com/trace,0.9574226105,124.18.236.164,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n2393,2,53,2017-03-12 08:58:00,http://vonkoepp.info/aletha.pagac,0.5147524947,155.100.133.85,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n2394,2,53,2017-01-13 00:51:26,http://hegmannorn.co/cielo,0.5546058194,102.185.160.154,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n2395,2,53,2017-03-18 09:38:17,http://frami.com/domenico_kuhic,0.2898686436,169.242.229.225,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n2396,2,53,2017-01-06 06:10:44,http://hamilllindgren.biz/janae,0.1807431239,49.123.88.218,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n2397,2,53,2017-06-01 21:03:21,http://wisozk.com/mozell.marks,0.0922068186,139.20.27.172,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n2398,2,53,2017-01-02 02:27:10,http://beer.io/paige,0.4546792421,10.164.35.24,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n2399,2,53,2016-12-23 14:20:56,http://deckow.name/dean_thiel,0.2863342202,155.244.137.252,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n2400,2,53,2017-01-03 22:52:58,http://schowaltercummings.name/marilie.carter,0.6989122034,170.65.49.23,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n2401,2,53,2017-02-24 15:11:45,http://cronin.io/rusty,0.7269303713,141.27.31.37,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n2402,2,53,2017-01-18 14:47:00,http://bogisichschuster.org/marianne,0.7551204529,29.211.12.178,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n2403,2,53,2017-02-26 19:53:05,http://weber.com/cullen,0.3934441122,66.5.98.60,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n2404,2,53,2017-01-23 04:10:55,http://herman.name/gabrielle_ledner,0.8301137780,34.42.155.254,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n2405,2,53,2017-03-12 07:14:44,http://schuster.co/julius,0.0751106836,6.55.229.147,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n2406,2,53,2017-02-27 21:23:43,http://jast.info/donna,0.9917694026,128.163.186.212,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n2407,2,53,2017-04-07 06:42:30,http://langosh.org/pinkie,0.7839362987,51.128.118.193,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n2408,2,53,2017-04-04 20:47:42,http://predovic.io/nikki_denesik,0.1674089481,200.243.237.22,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n2409,2,53,2017-01-26 19:29:05,http://zieme.co/arno.zemlak,0.2630875453,214.120.203.223,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n2410,2,53,2017-06-03 18:27:32,http://schulistbotsford.name/thelma.rosenbaum,0.4146018123,30.133.46.15,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n2411,2,53,2017-02-07 15:54:41,http://ledner.net/erika,0.9836448789,203.102.45.104,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n2412,2,54,2017-05-26 12:08:06,http://friesenleannon.com/lee,0.7718756286,61.163.241.193,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n2413,2,54,2017-03-21 02:48:04,http://schaefer.info/pat,0.7506664376,205.251.105.189,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n2414,2,54,2017-04-30 07:54:07,http://mueller.biz/brendan.walker,0.3487401390,207.35.54.121,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n2415,2,54,2017-01-24 16:21:38,http://walker.org/adrain_schuster,0.4100866152,151.243.92.203,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n2416,2,54,2017-04-03 13:50:24,http://wolfwilliamson.co/janea,0.2243629478,87.119.30.141,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n9297,4,208,2017-04-28 22:17:59,http://herzog.name/amiya,0.3617453118,145.3.236.50,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n9298,4,208,2017-04-12 19:14:20,http://boehm.com/adrien,0.4922574993,58.17.35.195,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n9299,4,208,2016-12-29 01:09:38,http://kreigerpadberg.info/rosanna,0.8732172657,61.36.82.158,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n9300,4,208,2016-12-16 14:25:49,http://bergeromaguera.org/ro,0.4023517801,68.40.224.132,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n9301,4,208,2017-03-08 09:50:03,http://pricerobel.biz/elisabeth.rice,0.6523231493,32.58.52.77,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n9302,4,208,2017-03-12 09:01:37,http://hilpertlangosh.org/corine,0.0097227823,183.13.183.196,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n9303,4,208,2017-03-03 15:17:07,http://beer.com/halle,0.8441910445,229.28.67.249,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n9304,4,208,2016-12-14 11:53:25,http://mcclure.org/noemy,0.5593226362,59.40.4.88,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n9305,4,208,2017-02-11 01:23:32,http://bruen.name/harold.mitchell,0.3115333649,223.75.34.147,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n9306,4,209,2017-01-10 03:19:38,http://kirlin.info/braeden_nikolaus,0.2939818517,94.17.198.95,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n9307,4,209,2017-04-23 23:07:06,http://powlowski.net/desmond.wiegand,0.6426840429,204.167.193.56,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n9308,4,209,2016-12-17 08:05:53,http://lubowitz.org/claudia,0.4463073494,34.4.226.25,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n9309,4,209,2017-02-11 01:16:41,http://kohlerschneider.co/cecilia,0.1965727258,2.133.54.169,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n9310,4,209,2017-01-30 04:22:22,http://bechtelar.com/allie_ohara,0.2089018490,13.214.30.3,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n9311,4,209,2017-01-22 04:29:30,http://hilpertbaumbach.io/gunner,0.1994833758,69.173.235.242,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n9312,4,209,2017-06-13 05:56:34,http://lueilwitzjacobi.io/ru.goyette,0.5203954536,59.25.127.231,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n9313,4,209,2017-06-01 03:38:06,http://rath.info/macey,0.7931764981,253.130.237.228,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n9314,4,209,2017-03-30 09:20:18,http://weinat.net/mackenzie,0.5799655389,182.226.108.182,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n9315,4,209,2017-05-03 02:45:24,http://kundeswaniawski.net/korbin,0.9251474172,247.140.191.181,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n9316,4,209,2017-02-25 19:43:37,http://gislason.org/oral.lockman,0.4853284240,90.195.162.224,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n9317,4,209,2017-05-07 00:30:29,http://bechtelar.io/erling_bode,0.9852079326,70.149.226.165,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n9318,4,209,2017-04-30 19:23:25,http://gottlieb.com/daphnee,0.5883836057,93.37.178.189,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n9319,4,209,2017-03-23 09:59:44,http://jerde.com/conor,0.0716072786,108.178.58.121,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n9320,4,209,2017-01-30 18:28:23,http://hamill.co/monroe_waelchi,0.4827818183,83.232.84.97,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n9321,4,209,2017-04-08 14:25:56,http://schambergerbeahan.biz/luisa_donnelly,0.1195958839,31.187.218.221,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n9322,4,209,2017-02-21 08:22:13,http://schinner.net/maynard.glover,0.1641035738,26.246.158.97,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n9323,4,209,2017-03-10 12:37:51,http://swift.org/donato_halvorson,0.3624143793,200.134.46.97,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n9324,4,209,2017-05-21 10:19:30,http://sauer.io/ephraim,0.9675188699,235.8.205.107,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n9325,4,209,2016-12-28 08:21:53,http://rohan.net/dulce.brown,0.3339167234,180.157.101.148,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n9326,4,209,2016-12-24 00:36:00,http://schinner.com/simone,0.0418594864,20.194.69.79,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n9327,4,209,2017-02-16 21:14:36,http://lindgrenbecker.info/broderick_hintz,0.9286739805,253.177.28.189,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n9328,4,209,2017-02-23 23:18:47,http://kautzerrobel.io/althea,0.7082975171,223.56.89.18,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n9329,4,209,2017-05-12 10:17:36,http://torphy.com/kade_nikolaus,0.6471889543,7.9.99.173,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n9330,4,210,2016-12-24 06:32:20,http://auerkuhn.co/wilfrid_schaefer,0.6992155072,147.35.43.46,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n9331,4,210,2017-02-20 17:23:09,http://koelpin.co/brenda_padberg,0.6829044983,25.232.254.65,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n9332,4,210,2017-04-29 08:12:28,http://boyer.info/velda,0.3222011743,5.233.27.3,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n9333,4,210,2017-03-27 13:46:45,http://bergstrom.info/tom.jacobs,0.4206463208,52.252.7.158,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n9334,4,210,2017-05-08 06:01:11,http://zemlak.org/toni.weinat,0.4906737024,21.134.224.149,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n9335,4,210,2017-03-24 08:57:40,http://simonispurdy.io/vickie,0.5801079878,63.61.178.8,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n9336,4,210,2017-01-10 05:19:47,http://thompsonvandervort.biz/haylie.schultz,0.0322686404,228.40.141.183,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n9337,4,210,2017-05-16 08:44:53,http://batz.co/stanford.keler,0.1113232106,83.142.12.254,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n9338,4,210,2017-05-19 19:33:05,http://johnston.co/esmeralda,0.8470147087,84.8.145.78,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n9339,4,210,2017-03-10 08:58:56,http://watersfriesen.com/alia.heathcote,0.1324550507,190.174.3.158,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n9340,4,210,2017-02-14 01:37:17,http://collins.org/gust,0.1706196846,222.193.41.58,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n9341,4,210,2017-05-03 01:10:20,http://murray.name/logan_bayer,0.4009189854,145.122.75.58,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n9342,4,210,2017-01-31 14:54:30,http://dickens.info/raina.kunde,0.4930651234,204.152.212.28,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n9343,4,210,2016-12-15 12:41:39,http://ledner.com/zakary.schumm,0.6423810855,2.81.45.238,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n9344,4,210,2017-01-27 19:46:06,http://weinat.com/giles_greenfelder,0.0084849894,192.178.175.80,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n9345,4,210,2017-05-13 22:53:26,http://goodwin.net/javon_bode,0.0155760676,246.88.79.129,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n9346,4,210,2017-03-10 15:20:22,http://reichel.org/lilyan,0.2880157081,202.127.99.170,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n9347,4,210,2017-05-20 14:30:03,http://gleichner.info/noble,0.4924746802,205.247.13.144,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n9348,4,210,2017-01-25 05:14:24,http://casperveum.net/tyrique.olson,0.9891424539,194.125.76.134,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n6307,3,138,2017-05-27 11:45:26,http://hills.name/elroy.marvin,0.5359448729,167.73.142.11,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n6308,3,138,2017-05-22 11:14:05,http://mayer.io/kayley,0.1844877744,224.64.46.225,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n6309,3,138,2017-03-11 11:16:21,http://schowalterbeatty.io/jakob.dickens,0.3248997970,27.64.240.246,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n6310,3,138,2017-05-16 21:16:15,http://stoltenberg.name/devante_vandervort,0.4393021806,211.194.88.75,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n6311,3,138,2017-02-07 08:26:41,http://colegreenholt.com/robin,0.5496566502,101.56.227.209,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n6312,3,138,2017-06-13 06:58:47,http://rowe.co/alexandre,0.0341347219,147.158.103.22,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n6313,3,138,2017-05-16 08:15:21,http://marvin.name/john,0.8582179729,237.171.32.116,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n6314,3,138,2016-12-18 05:14:21,http://mannbeatty.net/elsie,0.6494612987,66.24.223.143,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n6315,3,138,2017-02-22 03:13:57,http://langledner.info/delilah,0.9809692158,73.15.155.179,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n6316,3,138,2017-05-09 18:32:51,http://schulist.info/ephraim,0.9823130473,151.37.178.222,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n6317,3,138,2017-04-06 23:51:58,http://marquardt.info/omari.haley,0.5644039664,218.93.158.104,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n6318,3,138,2017-02-13 23:02:28,http://nolan.co/darryl,0.1583780770,3.252.78.199,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n6319,3,138,2016-12-15 17:05:23,http://muller.name/deie.kovacek,0.6887579739,190.248.162.127,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n6320,3,138,2017-04-06 04:17:04,http://terry.co/keaton,0.7026601684,3.62.27.129,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n6321,3,138,2017-06-11 10:29:29,http://ondricka.info/verdie,0.7295113699,44.236.11.221,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n6322,3,138,2017-03-28 04:59:30,http://labadie.biz/shawn,0.0193518133,224.25.128.155,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n6323,3,138,2017-03-13 19:39:44,http://bernhard.info/melia.abbott,0.2836191943,18.50.9.106,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n6324,3,138,2017-01-16 03:40:11,http://howewalsh.info/nikolas.gaylord,0.5835706050,26.81.188.149,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n6325,3,138,2017-04-09 11:09:03,http://ryan.io/enrique,0.8467580809,40.71.216.180,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n6326,3,138,2016-12-14 22:38:53,http://jakubowski.org/ines_greenholt,0.6760293597,182.31.192.224,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n6327,3,138,2016-12-13 06:10:06,http://beatty.net/rachael_harris,0.1114272091,130.145.167.132,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n6328,3,138,2017-02-23 22:01:42,http://grant.io/marisa_stokes,0.7931276438,23.130.47.165,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n6329,3,138,2017-03-29 10:05:36,http://hackett.name/gilbert,0.0164761156,35.108.225.78,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n6330,3,138,2017-01-25 03:36:41,http://reilly.info/bridgette,0.4730522526,206.43.189.16,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n6331,3,138,2016-12-21 21:24:06,http://flatley.name/brandi_gislason,0.4247980046,63.95.75.66,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n6332,3,138,2017-04-29 22:02:36,http://strackerunolfon.biz/nels.gerhold,0.0135598763,25.153.162.172,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n6333,3,138,2017-01-11 02:57:50,http://warddenesik.io/harmon,0.0751959541,173.233.106.253,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n6334,3,138,2017-04-07 22:04:20,http://parker.name/monica.lindgren,0.4560840691,26.147.241.94,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n6335,3,138,2017-05-07 14:24:29,http://thiel.co/dianna,0.9697225762,236.144.35.23,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n6336,3,138,2017-05-25 15:49:05,http://starkglover.net/sandra,0.2329547709,24.137.80.224,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n6337,3,138,2017-03-14 20:23:29,http://ziemannblanda.com/sedrick.dickens,0.2927034680,214.180.42.251,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n6338,3,138,2017-01-02 20:31:34,http://reilly.co/ana,0.8134123507,56.83.29.48,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n6339,3,138,2017-03-19 07:17:14,http://okeefestanton.net/lora.schoen,0.7080689063,6.246.90.42,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n6340,3,138,2017-02-16 23:34:55,http://boyle.info/shaina,0.9300706167,246.82.168.85,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n6341,3,138,2017-01-29 15:11:01,http://abshirelubowitz.net/aliza,0.7728521100,196.138.204.131,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n6342,3,138,2017-05-09 18:53:51,http://schimmelframi.org/shakira,0.6390740627,110.72.46.100,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n6343,3,138,2017-02-26 09:04:08,http://douglas.co/nona_fisher,0.4234561343,98.200.165.225,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n6344,3,138,2017-04-22 02:00:24,http://swaniawskicain.name/charley,0.1832450485,6.129.125.182,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n6345,3,138,2017-05-20 03:16:16,http://effertz.com/celia_zboncak,0.9990616036,147.214.112.102,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n6346,3,138,2017-01-03 04:50:25,http://flatley.co/trey.reinger,0.0878801283,146.130.106.149,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n6347,3,138,2016-12-14 23:15:32,http://tremblay.info/davon,0.3495742930,189.153.123.118,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n6348,3,138,2017-03-23 18:12:03,http://abbott.co/vergie.oconnell,0.3958848621,145.137.48.220,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n6349,3,138,2017-02-01 17:28:34,http://weimannmurazik.com/damien,0.8566588608,92.221.21.97,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n6350,3,138,2017-05-23 00:20:25,http://adams.co/roma_pagac,0.5051341258,250.147.200.102,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n6351,3,138,2017-05-08 17:16:55,http://bernier.io/peggie,0.3125895216,47.171.129.83,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n6352,3,138,2017-04-08 20:54:06,http://turcotte.io/mittie,0.8960375233,187.237.216.121,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n6353,3,138,2017-05-18 05:15:38,http://crist.com/myriam,0.3233817283,244.118.23.10,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n6354,3,138,2017-01-22 06:03:43,http://robel.biz/tobin.rodriguez,0.2764347278,253.210.93.56,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n6355,3,138,2017-04-26 12:27:18,http://torphy.co/solon,0.6585900637,185.179.5.62,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n6356,3,138,2017-01-08 10:12:15,http://padberg.net/billie.bayer,0.9378437829,60.209.174.84,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n6357,3,138,2017-04-08 22:11:08,http://macejkovic.net/aleen,0.2020010888,30.80.17.206,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n6358,3,139,2017-03-01 07:42:35,http://emard.net/beulah,0.3430519153,181.104.56.250,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n12213,5,273,2017-05-19 09:45:16,http://murazikernser.name/wendell,0.0667729723,32.146.231.144,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n12214,5,273,2016-12-17 19:01:33,http://streichcole.name/kamron,0.4978375530,253.160.71.45,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n12215,5,273,2017-03-30 05:01:04,http://zemlak.net/rebecca_borer,0.2682215873,102.144.41.244,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n12216,5,273,2017-01-25 04:15:58,http://hoppe.com/adalberto.sauer,0.5538525469,163.129.218.37,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n12217,5,273,2017-04-27 10:47:01,http://marvin.info/florian,0.8302553606,218.82.232.155,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n12218,5,273,2017-02-05 16:02:49,http://jacobimonis.org/jovani,0.4353532766,173.50.82.173,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n12219,5,273,2017-01-06 18:51:43,http://kilbackkoch.co/janiya,0.0939441661,51.221.201.188,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n12220,5,273,2017-03-22 09:38:05,http://rutherfordledner.io/kay,0.6416748851,207.54.3.169,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n12221,5,273,2017-01-10 09:58:33,http://mertz.co/bradford.schneider,0.7443481067,234.211.126.100,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n12222,5,273,2017-01-01 04:55:09,http://schillermitchell.biz/leora_altenwerth,0.6451414231,137.186.117.235,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n12223,5,273,2017-02-25 18:54:13,http://weinat.biz/manuela,0.9408462506,94.241.248.233,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n12224,5,273,2017-03-29 21:23:29,http://carter.name/robb.gusikowski,0.6550517656,202.137.232.251,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n12225,5,273,2017-02-05 03:48:02,http://kutchcummings.biz/beulah,0.1212898404,13.23.119.23,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n12226,5,273,2016-12-28 15:11:46,http://nader.biz/guadalupe_wuckert,0.4177440442,8.169.131.82,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n12227,5,273,2017-06-11 23:23:27,http://maggiostoltenberg.net/verna,0.0554142642,101.95.137.101,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n12228,5,273,2017-03-19 06:18:12,http://ebert.com/ollie.hayes,0.7879363452,47.162.122.132,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n12229,5,273,2017-03-11 03:08:39,http://corkery.org/bernadette,0.6405496132,222.243.219.127,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n12230,5,273,2017-04-28 14:56:44,http://okuneva.biz/josiah_ruel,0.5900461251,130.145.175.184,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n12231,5,273,2016-12-20 12:44:41,http://ryan.io/beau_little,0.7445352551,38.19.69.145,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n12232,5,273,2017-02-28 15:15:12,http://bernhardkub.name/aimee,0.1403402443,66.240.50.168,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n12233,5,273,2016-12-31 06:27:28,http://larkinkunze.co/amber,0.9130957596,170.71.141.120,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n12234,5,273,2017-04-18 02:43:57,http://jacobs.org/cordie.schimmel,0.5193229192,23.95.77.175,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n12235,5,273,2017-01-23 06:28:20,http://durgan.name/erica.hickle,0.7790974268,128.49.219.148,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n12236,5,273,2017-03-29 16:15:52,http://roweheidenreich.biz/michele,0.1157401907,50.43.79.16,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n12237,5,273,2016-12-16 06:42:06,http://dietrich.biz/van,0.6211244260,69.168.161.114,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n12238,5,273,2017-04-02 14:13:23,http://goodwin.name/kenny_goyette,0.5072768580,191.198.137.226,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n12239,5,273,2017-01-06 02:15:02,http://lynch.co/taryn.becker,0.1229846940,185.169.197.236,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n12240,5,273,2017-03-11 17:30:22,http://luettgenkuhn.com/arne.zulauf,0.7777745309,40.153.108.93,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n12241,5,274,2016-12-29 07:22:05,http://lowe.biz/coty_bins,0.9970221510,235.139.211.201,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n12242,5,274,2017-06-14 01:19:29,http://pollich.co/colten,0.8202675679,50.65.24.236,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n12243,5,274,2016-12-23 23:10:35,http://hirthefadel.com/iva_corkery,0.9102462320,72.135.55.12,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n12244,5,274,2016-12-15 16:55:57,http://goyettehudson.net/marcel,0.3482114405,212.247.162.115,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n12245,5,274,2017-01-10 02:21:07,http://mayerhodkiewicz.name/bertrand.baumbach,0.3057380597,222.21.10.40,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n12246,5,274,2017-03-14 00:31:12,http://keebler.net/jeramie.runolfon,0.5478349799,235.5.229.191,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n12247,5,274,2017-06-10 16:18:20,http://kertzmann.com/velda,0.7072329976,34.201.195.185,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n12248,5,274,2017-03-04 09:01:32,http://fahey.co/lea,0.0219728267,171.163.199.18,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n12249,5,274,2017-03-01 07:52:39,http://goodwin.biz/kobe,0.5877336945,137.232.143.73,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n12250,5,274,2017-03-19 23:22:10,http://williamson.org/maeve.heathcote,0.6796373995,95.56.222.222,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n12251,5,274,2017-06-10 11:10:57,http://kuhlman.co/pinkie.homenick,0.5762023369,162.207.132.125,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n12252,5,274,2017-04-16 01:25:36,http://hintz.biz/harrison_hintz,0.5260598576,22.101.187.92,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n12253,5,274,2017-01-02 00:49:10,http://wilderman.name/joaquin_reichert,0.8620059332,28.26.135.23,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n12254,5,274,2017-01-21 01:30:08,http://murazik.info/valentina_koepp,0.3642075661,190.23.65.136,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n12255,5,274,2017-01-30 08:40:40,http://spinkaosinski.org/frida,0.0344494613,159.251.72.204,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n12256,5,274,2017-05-17 09:10:48,http://schillerbrakus.io/ruth,0.9663932457,26.110.97.213,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n12257,5,274,2017-04-14 17:44:11,http://cummeratabins.io/jewel,0.4274322182,219.66.231.107,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n12258,5,274,2016-12-25 11:28:29,http://schustertorphy.io/ashton_labadie,0.0143100111,145.168.199.52,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n12259,5,274,2017-02-20 14:54:14,http://murphy.name/melvin,0.1290073591,249.161.9.23,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n12260,5,274,2017-06-12 04:30:20,http://framischmitt.info/autumn,0.2499451737,22.241.214.179,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n12261,5,274,2017-03-16 07:25:41,http://moen.name/floy,0.4307568928,122.64.118.125,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n12262,5,274,2017-04-13 22:55:45,http://corkery.name/octavia,0.1715488252,105.56.178.152,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n12263,5,275,2017-04-11 09:55:50,http://westcummings.com/evie_barton,0.7185387225,193.129.105.68,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n15218,6,342,2017-04-18 22:17:21,http://langworth.info/hoyt.reilly,,6.212.158.214,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n15219,6,342,2017-05-14 02:33:17,http://wilderman.org/ted,,92.26.166.132,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n15220,6,342,2017-04-28 00:09:59,http://conroy.co/zula,,85.168.18.161,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n15221,6,342,2017-01-15 19:07:25,http://hand.info/valentina,,157.116.232.52,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n15222,6,342,2017-01-23 14:46:45,http://vandervort.name/leta.harber,,51.27.231.86,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n15223,6,342,2017-04-22 00:15:15,http://oberbrunner.org/titus.morar,,106.165.159.41,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n15224,6,342,2017-02-13 02:41:00,http://kuhnthiel.name/madyson.grant,,17.9.54.202,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n15225,6,342,2017-03-02 16:03:28,http://schoennolan.io/emmanuelle,,68.158.111.161,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n15226,6,342,2017-01-06 16:55:43,http://breitenberglakin.name/ara_wilderman,,177.237.22.74,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n15227,6,342,2017-01-22 07:42:52,http://stroman.name/keenan,,84.248.76.7,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n15228,6,342,2017-04-29 09:42:02,http://mertzvon.info/angie,,218.124.208.67,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n15229,6,342,2017-04-01 15:04:01,http://hoppe.co/bernadine_emmerich,,11.140.84.138,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n15230,6,342,2017-02-22 11:33:17,http://franeckicrist.io/johann.kilback,,71.15.233.186,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n15231,6,342,2017-02-19 13:02:58,http://westschmidt.name/rebeka_beer,,137.149.210.28,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n15232,6,342,2017-05-17 06:14:37,http://lyncholson.co/isaiah_lubowitz,,77.45.118.89,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n15233,6,342,2017-01-14 13:23:36,http://wittingwindler.org/gerard,,185.254.74.44,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n15234,6,342,2017-02-17 09:49:33,http://rice.io/donny_okeefe,,86.129.188.27,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n15235,6,342,2017-01-08 21:26:16,http://pouros.info/randy.morar,,229.33.151.71,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n15236,6,342,2017-03-19 12:58:55,http://schillerkertzmann.net/raphael,,212.104.153.167,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n15237,6,342,2017-04-19 08:01:04,http://jenkins.co/pedro_friesen,,220.142.130.138,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n15238,6,342,2017-03-11 14:40:41,http://lynchrowe.org/jeffery_strosin,,114.138.38.112,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n15239,6,342,2017-01-29 02:39:22,http://morar.name/pascale,,11.33.45.198,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n15240,6,342,2017-01-20 21:23:45,http://osinskibogisich.co/mabel_breitenberg,,188.219.2.23,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n15241,6,342,2017-03-29 21:35:59,http://mcclure.net/dominic_johns,,178.87.232.164,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n15242,6,342,2017-03-30 08:37:11,http://brown.net/naomi,,176.235.123.59,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n15243,6,342,2017-01-18 06:20:20,http://corwin.io/wilton,,123.93.88.222,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n15244,6,342,2017-04-08 22:55:32,http://schmidt.biz/dante,,85.211.141.58,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n15245,6,342,2016-12-17 18:37:19,http://mills.net/esta.jacobson,,101.205.244.168,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n15246,6,342,2017-04-24 15:36:51,http://parker.net/jackie,,235.115.28.76,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n15247,6,342,2017-02-16 05:17:09,http://vandervortmedhurst.net/nicolas_dickens,,224.40.96.34,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n15248,6,342,2017-05-11 04:53:58,http://schowalter.biz/khalil,,214.73.183.60,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n15249,6,342,2017-05-09 03:28:48,http://mckenzie.net/jevon.daniel,,247.225.30.182,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n15250,6,342,2017-03-07 06:22:22,http://boehmvandervort.name/jailyn,,75.21.36.112,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n15251,6,342,2017-03-06 13:35:00,http://stark.info/caterina,,155.217.36.202,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n15252,6,342,2017-05-30 03:47:51,http://smithcain.io/kameron,,162.152.116.58,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n15253,6,342,2017-04-21 09:32:16,http://bahringer.name/will,,77.233.188.225,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n15254,6,343,2016-12-28 20:18:52,http://rogahn.net/leo,,114.231.252.231,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n15255,6,343,2017-05-04 21:09:25,http://legros.com/skyla_romaguera,,231.95.133.117,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n15256,6,343,2016-12-27 04:58:59,http://bahringer.com/jonathon_pouros,,46.32.128.121,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n15257,6,343,2017-03-08 08:45:11,http://goodwin.net/ben.kunze,,6.4.207.46,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n15258,6,343,2017-01-18 08:28:45,http://greenholtgerlach.io/savanna,,16.118.136.145,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n15259,6,343,2017-03-02 08:48:26,http://kleingerhold.name/aurelia.barton,,193.100.164.16,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n15260,6,343,2016-12-15 06:10:08,http://hoegerfahey.name/ryleigh_dickinson,,213.8.96.150,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n15261,6,343,2017-02-26 03:02:53,http://parisianrau.org/rosalind.baumbach,,246.132.133.210,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n15262,6,343,2017-02-06 01:44:16,http://kulas.org/santiago.schamberger,,135.228.135.202,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n15263,6,343,2017-04-05 14:33:46,http://emmerich.io/collin,,13.24.63.165,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n15264,6,343,2017-06-14 00:48:15,http://hyatt.info/verner.rosenbaum,,50.126.41.36,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n15265,6,343,2017-01-09 21:56:30,http://medhurst.biz/alene_rau,,43.238.52.108,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n15266,6,343,2016-12-19 15:58:50,http://douglachuppe.name/luther,,239.195.143.62,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n15267,6,343,2016-12-30 12:25:13,http://larson.com/theron_lehner,,58.167.29.138,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n15268,6,343,2017-05-30 12:40:34,http://baumbach.info/genesis_farrell,,99.78.189.126,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n15269,6,343,2017-05-25 07:20:44,http://feeney.co/annamarie,,234.59.90.103,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n15270,6,343,2017-05-22 10:35:59,http://treuteljaskolski.co/keith,,132.54.169.115,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n15271,6,343,2017-02-24 10:50:29,http://watsicastroman.net/marty,,254.246.142.182,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n15272,6,343,2017-04-04 16:45:35,http://bahringer.biz/amara.mohr,,151.81.251.17,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n2417,2,54,2017-04-18 09:00:09,http://gorczanyoconnell.co/noah_bechtelar,0.4291460190,9.35.68.134,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n2418,2,54,2017-01-20 20:22:58,http://wiegand.biz/graciela.block,0.3967234432,14.179.65.191,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n2419,2,54,2017-01-28 08:43:28,http://wunsch.com/julio_runolfsdottir,0.3607383094,2.215.162.124,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n2420,2,54,2017-01-11 23:25:13,http://blick.biz/vicente_durgan,0.4973748376,239.92.19.204,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n2421,2,54,2017-05-22 09:34:15,http://hagenechuppe.name/cierra,0.1136271324,121.67.38.77,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n2422,2,54,2017-01-22 09:14:28,http://satterfield.com/reinhold,0.2485791683,163.203.181.245,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n2423,2,54,2017-01-20 03:39:19,http://abbottkoepp.name/rebeca_ohara,0.6832795418,187.117.100.140,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n2424,2,54,2017-02-20 04:33:42,http://moenmclaughlin.biz/beth,0.5520260967,215.229.164.166,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n2425,2,54,2017-04-24 12:42:55,http://kertzmannconn.org/isabella_heel,0.8859386931,149.34.122.94,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n2426,2,54,2017-06-04 22:14:01,http://ruel.biz/christopher_yost,0.4832150396,155.193.72.123,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n2427,2,54,2017-02-02 11:50:37,http://swift.net/natasha_moriette,0.2342283745,55.149.77.197,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n2428,2,54,2017-01-20 05:24:09,http://robel.io/andreanne,0.3969909273,126.161.4.228,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n2429,2,54,2017-04-08 16:38:54,http://shanahan.com/mathew,0.9098223635,233.179.133.134,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n2430,2,54,2017-04-19 08:08:34,http://kaulkeconnelly.name/rey,0.6410266447,245.194.234.143,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n2431,2,54,2017-02-05 09:04:57,http://oberbrunner.com/felicity.parisian,0.8120353815,155.57.185.212,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n2432,2,54,2017-06-02 23:01:00,http://bayer.net/jerad.lind,0.6635199745,111.144.2.124,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n2433,2,54,2017-01-22 15:09:26,http://bruen.com/ted_macejkovic,0.0360239262,226.10.181.228,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n2434,2,54,2017-04-01 11:29:30,http://wilkinson.info/ally.conn,0.1461150533,16.201.211.205,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n2435,2,54,2017-03-11 21:30:42,http://gulgowski.info/raul_sporer,0.9735253309,231.154.31.89,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n2436,2,54,2016-12-15 17:55:42,http://lednerkunze.net/ardella_hagenes,0.5632065474,10.46.149.223,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n2437,2,54,2017-02-25 16:17:48,http://baumbachkirlin.name/danny,0.3288653827,81.212.43.93,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n2438,2,54,2017-03-23 15:58:05,http://halvorson.name/johann_jacobs,0.1610952090,122.39.244.212,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n2439,2,54,2017-05-04 20:52:26,http://kochfriesen.name/efren,0.3005845556,97.161.44.52,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n2440,2,54,2017-01-27 23:58:10,http://greenholtlesch.net/karley,0.7330898391,237.234.72.222,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n2441,2,54,2017-04-30 16:01:59,http://hilpert.info/cindy.abbott,0.6901110667,176.165.162.38,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n2442,2,54,2017-02-27 08:45:27,http://upton.info/kim_lindgren,0.0489630406,227.210.70.4,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n2443,2,54,2017-05-08 06:17:09,http://braun.info/scarlett.sporer,0.5954730654,182.64.229.61,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n2444,2,54,2017-02-27 10:32:53,http://kub.info/paige.gislason,0.3080395874,226.141.241.3,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n2445,2,54,2017-03-16 17:52:02,http://bernhard.com/liza.nader,0.6442912473,8.130.239.219,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n2446,2,54,2017-03-24 19:58:39,http://paucekokeefe.info/loren,0.7926050422,140.74.58.119,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n2447,2,54,2017-03-28 19:52:57,http://williamson.co/lilyan.kunze,0.4062501810,225.89.126.31,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n2448,2,54,2017-01-28 05:49:40,http://blandahagenes.co/grady,0.8910244725,139.182.176.39,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n2449,2,54,2017-04-25 23:40:20,http://walter.org/halie.conroy,0.0115670398,69.114.18.194,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n2450,2,54,2017-03-30 07:17:29,http://pfeffer.io/nels.lindgren,0.0414388291,160.183.60.239,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n2451,2,54,2017-03-20 22:30:30,http://hilpert.biz/estevan,0.2504798341,145.41.149.184,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n2452,2,54,2017-05-03 08:26:52,http://koelpinwilderman.biz/edd_krajcik,0.2432213460,139.117.48.145,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n2453,2,54,2017-03-06 06:18:11,http://walker.com/tod.hermiston,0.9057980964,76.192.16.71,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n2454,2,54,2017-02-02 15:00:36,http://keeling.co/stefanie_bernhard,0.7444520398,97.126.3.108,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n2455,2,54,2017-03-26 22:06:48,http://blick.net/tierra_will,0.0564215964,191.201.245.36,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n2456,2,54,2016-12-18 00:47:22,http://steuber.net/kris,0.1182580799,54.98.68.8,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n2457,2,54,2017-02-24 23:35:50,http://rohan.io/darian_beatty,0.2912406688,200.210.13.245,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n2458,2,54,2017-01-06 02:48:59,http://collier.io/tomasa,0.1305194071,172.59.237.208,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n2459,2,54,2017-05-04 10:13:55,http://kuphalgulgowski.name/dannie.glover,0.1544406108,205.111.111.43,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n2460,2,54,2017-04-26 03:03:43,http://oharadooley.co/emmie.schulist,0.6623398932,13.221.60.141,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n2461,2,54,2017-06-09 02:57:49,http://quigley.com/nolan,0.1161044528,234.12.65.85,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n2462,2,54,2017-01-29 08:49:07,http://doyle.com/elnora,0.2221848784,69.131.41.238,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n2463,2,54,2017-03-01 15:14:45,http://blockparisian.org/lauren.renner,0.5798114963,48.101.219.211,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n2464,2,54,2017-06-05 18:54:49,http://kerlukejones.info/danial.kirlin,0.7999860184,235.77.87.53,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n2465,2,54,2017-03-30 01:43:17,http://donnelly.co/dulce.mills,0.3761852577,110.54.126.83,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n2466,2,54,2017-03-09 18:01:28,http://abshire.org/raleigh_hammes,0.1358647694,191.26.204.232,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n2467,2,54,2017-02-17 03:29:58,http://mcclure.info/milan.weinat,0.5421756206,212.166.50.244,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n2468,2,54,2016-12-28 21:10:16,http://cummingswintheiser.biz/anne,0.9552554912,199.34.17.239,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n9349,4,210,2016-12-30 08:19:10,http://reilly.info/zoie_parisian,0.4409377672,253.177.140.112,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n9350,4,210,2017-01-29 06:23:43,http://williamsonpurdy.com/rachel.corwin,0.9660761513,222.192.133.201,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n9351,4,210,2017-01-14 16:00:15,http://dubuque.com/genoveva_mills,0.9669760605,52.169.124.88,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n9352,4,210,2017-03-23 06:42:41,http://skiles.org/ariane,0.5235652846,50.44.174.147,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n9353,4,210,2017-04-07 03:59:36,http://adams.org/camden,0.9939310365,21.193.46.199,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n9354,4,210,2017-01-18 10:19:45,http://harrisvolkman.net/tremayne,0.6777456391,23.237.37.128,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n9355,4,210,2017-05-25 20:02:10,http://aufderhar.name/toney,0.7059451732,20.6.129.51,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n9356,4,210,2017-02-11 22:44:29,http://gerholdcartwright.com/john,0.1662921222,180.15.73.104,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n9357,4,211,2017-01-26 11:30:19,http://marks.co/taurean_franecki,0.6213545286,32.84.66.200,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n9358,4,211,2017-05-06 12:36:29,http://leuschkehaag.com/hertha,0.5273060323,205.93.226.17,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n9359,4,211,2017-05-07 06:28:11,http://spencer.org/tamia.maggio,0.0476171740,70.116.235.68,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n9360,4,211,2017-03-10 11:35:48,http://simonisgorczany.name/estella_harris,0.9435498028,214.58.229.244,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n9361,4,211,2016-12-19 10:09:25,http://gerhold.co/jayden,0.4168374282,236.84.68.14,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n9362,4,211,2017-05-22 10:39:06,http://volkman.biz/katharina.considine,0.5264327981,91.108.95.114,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n9363,4,211,2017-03-17 14:19:07,http://hirthe.name/ari,0.7665950570,194.142.86.134,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n9364,4,211,2017-01-24 06:32:50,http://botsford.name/daphnee_stamm,0.4336679465,134.45.218.204,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n9365,4,211,2017-01-20 19:13:24,http://collins.org/gideon,0.5647984649,85.110.181.180,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n9366,4,211,2017-05-15 00:00:26,http://schmitt.co/cheyanne,0.6328360450,169.5.177.5,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n9367,4,211,2017-03-07 01:13:12,http://ferrybergnaum.net/glenna_aufderhar,0.6856979457,210.210.198.48,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n9368,4,211,2017-02-03 17:33:34,http://kirlintreutel.name/josiane,0.2965349912,167.111.252.179,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n9369,4,211,2017-05-09 17:57:19,http://leannon.com/rubie,0.3151328549,92.112.89.227,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n9370,4,211,2017-04-08 20:29:00,http://tremblaylubowitz.biz/frieda.wisoky,0.3998291300,87.114.64.240,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n9371,4,211,2017-01-05 21:17:00,http://oconnell.com/arielle,0.5437939266,212.85.159.102,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n9372,4,211,2016-12-27 07:18:10,http://waelchi.biz/jerald,0.5419435361,216.42.168.13,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n9373,4,211,2017-03-30 21:41:10,http://friesen.com/arely,0.7077259468,44.226.17.197,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n9374,4,211,2017-01-15 15:51:54,http://wiza.net/kamryn.wintheiser,0.6271060189,51.22.62.82,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n9375,4,211,2017-03-17 08:03:16,http://schamberger.com/faye,0.0573184025,35.174.8.227,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n9376,4,211,2017-03-03 11:02:38,http://gerhold.co/gerry_kozey,0.3933131091,133.31.205.36,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n9377,4,211,2017-03-11 20:07:04,http://kuhn.com/tyra_stanton,0.5513489342,162.210.149.220,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n9378,4,211,2017-05-11 13:37:22,http://heidenreichwatsica.org/giuseppe_sipes,0.3053096320,63.181.235.66,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n9379,4,211,2017-05-16 10:11:10,http://green.biz/isabell_pouros,0.6090670802,74.165.206.30,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n9380,4,211,2017-01-16 15:30:07,http://abshire.net/stefanie.raynor,0.3667866503,182.252.170.233,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n11500,4,256,2017-06-10 04:48:54,http://kihn.biz/suzanne,,109.106.250.197,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n9381,4,211,2017-05-04 08:14:45,http://herman.info/arvel.crona,0.3171763558,111.12.123.212,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n9382,4,211,2016-12-17 08:38:11,http://moore.io/raven.labadie,0.4885376574,136.85.232.116,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n9383,4,211,2017-02-19 06:18:27,http://watsicabaumbach.info/amina,0.9446527603,86.198.10.91,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n9384,4,211,2017-03-13 21:09:10,http://stokes.net/jasmin,0.5844571943,184.178.53.136,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n9385,4,211,2017-01-03 17:05:03,http://collier.io/reina.price,0.6239919180,138.143.114.43,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n9386,4,211,2017-03-16 22:38:11,http://mohr.name/darron_wiza,0.3485517382,189.23.47.127,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n9387,4,211,2017-03-10 11:41:42,http://koepp.com/sylvia.aufderhar,0.9589477348,228.240.218.137,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n9388,4,211,2016-12-22 10:33:56,http://hilllschamberger.net/elton,0.8173880504,188.146.243.39,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n9389,4,211,2017-03-02 20:33:47,http://oreilly.info/heaven.ebert,0.1248879651,63.84.148.7,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n9390,4,211,2017-05-11 07:27:19,http://rippingoldner.net/maritza,0.9731227528,2.62.187.28,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n9391,4,211,2017-02-16 12:35:24,http://labadie.biz/sage.crona,0.2524034989,248.80.17.81,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n9392,4,211,2017-02-17 04:35:47,http://kautzer.co/nicolas,0.7585233979,130.188.61.193,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n9393,4,211,2017-04-16 08:13:35,http://kovacek.io/reggie_leuschke,0.2940170536,215.101.116.54,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n9394,4,211,2017-04-17 01:41:26,http://schroeder.co/leta,0.0014175598,247.228.45.45,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n9395,4,211,2017-05-16 10:15:50,http://schmitt.net/maybell.veum,0.1812880349,91.132.213.216,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n9396,4,211,2017-01-15 02:04:44,http://nienowtromp.io/jerald.zemlak,0.9661257015,163.212.164.191,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n9397,4,211,2017-01-21 00:16:57,http://rolfson.biz/abagail,0.1012646651,114.133.99.126,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n9398,4,211,2017-05-22 04:56:14,http://okeefewilliamson.io/alberta,0.0298791773,151.45.200.112,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n6359,3,139,2017-06-13 16:07:05,http://krajcik.org/judge_beier,0.9768025919,76.26.50.137,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n6360,3,139,2017-06-12 09:58:06,http://bartell.co/josue.konopelski,0.5886286776,114.253.39.71,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n6361,3,139,2017-01-29 11:41:17,http://trantow.co/kaylah.kihn,0.8445051948,115.92.205.12,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n6362,3,139,2017-01-10 07:00:04,http://kub.org/buddy.klocko,0.8088140049,177.213.198.197,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n6363,3,139,2017-06-04 23:08:17,http://damorerobel.net/karlie.schmitt,0.4369161297,97.112.227.249,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n6364,3,139,2017-05-30 23:16:31,http://bergstrom.name/wilfred_runolfon,0.5525087658,141.81.10.60,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n6365,3,139,2016-12-26 18:27:03,http://borer.org/keegan,0.6675013230,227.153.58.120,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n6366,3,139,2017-03-09 01:21:49,http://wyman.co/dolly,0.1512464473,26.216.72.33,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n6367,3,139,2017-06-13 11:08:34,http://collins.co/dominic,0.5157626616,105.100.108.156,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n6368,3,139,2017-05-31 07:22:49,http://morar.org/demetris,0.3118847648,108.49.52.182,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n6369,3,139,2017-01-26 17:56:58,http://dickinsonbogan.co/laurianne,0.2343032712,67.247.201.127,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n6370,3,139,2017-02-15 00:55:25,http://veum.info/marie_huel,0.8789436790,55.253.23.56,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n6371,3,139,2017-06-01 05:03:28,http://bechtelar.io/arely_feest,0.6073036038,2.92.33.173,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n6372,3,139,2017-04-29 03:01:04,http://johns.io/brisa_pagac,0.6490538059,138.21.173.250,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n6373,3,139,2017-03-18 10:29:32,http://kozey.org/adriana_hoeger,0.6041005561,61.58.80.107,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n6374,3,139,2017-01-04 03:07:41,http://hand.info/renee.boyle,0.4767194682,122.18.30.234,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n6375,3,139,2017-03-28 01:49:41,http://rogahn.co/ambrose.renner,0.2625564565,40.84.39.99,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n6376,3,139,2016-12-23 21:41:06,http://reinger.co/pauline.willms,0.1704846240,186.124.152.28,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n6377,3,139,2017-02-23 09:26:44,http://okeefecummings.info/arturo.cremin,0.9487255376,141.7.200.126,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n6378,3,139,2017-06-06 21:21:01,http://reingermraz.net/laney,0.2618966039,31.221.48.247,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n6379,3,139,2017-03-01 16:30:58,http://abshirelehner.biz/alexane.hamill,0.4346032247,162.93.236.100,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n6380,3,139,2017-05-30 02:09:31,http://zboncak.com/forrest_yundt,0.1013567634,114.175.223.80,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n6381,3,139,2017-06-13 22:26:40,http://donnelly.com/cale_greenholt,0.0338284174,187.205.183.252,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n6382,3,139,2017-02-14 19:09:08,http://ledner.com/price_jakubowski,0.5872349034,47.184.221.2,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n6383,3,139,2016-12-28 11:43:16,http://gutmann.net/willie.erdman,0.7881227786,53.30.253.142,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n6384,3,139,2017-01-13 15:21:34,http://rosenbaum.co/alysha,0.6484327361,194.32.186.242,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n6385,3,139,2017-03-28 11:12:05,http://olsonboyer.io/lavada,0.1197242961,157.209.189.47,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n6386,3,139,2017-02-12 05:46:38,http://schmittbogisich.co/dusty,0.4827288270,101.85.228.20,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n6387,3,139,2017-01-24 00:05:30,http://weinat.co/javier_ruecker,0.2149831602,153.54.102.84,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n6388,3,139,2017-05-21 07:26:08,http://beatty.org/maximilian,0.4786258747,12.82.33.156,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n6389,3,139,2017-01-17 01:56:47,http://johns.name/vita,0.8447178769,112.80.237.101,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n6390,3,139,2017-03-06 18:55:50,http://mcglynnbaumbach.co/marilyne,0.2000496615,162.246.70.24,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n6391,3,139,2016-12-24 04:36:58,http://lubowitz.co/alexandra,0.5054985245,82.68.210.127,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n6392,3,139,2017-06-12 18:51:56,http://grimes.co/logan.kuhn,0.5562429488,214.164.229.134,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n6393,3,139,2017-05-11 22:16:02,http://weimannveum.co/aileen,0.9535645480,162.248.230.161,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n6394,3,140,2017-01-24 05:26:35,http://mcglynn.net/cruz,0.0545569090,145.220.60.248,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n6395,3,140,2017-04-16 12:28:23,http://collins.com/charlene,0.8821985081,237.140.171.225,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n6396,3,140,2017-01-29 06:33:29,http://homenickkuvalis.name/hector.bechtelar,0.4011843730,119.198.56.114,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n6397,3,140,2017-03-07 13:37:08,http://barton.info/sherman,0.8800024164,168.36.254.21,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n6398,3,140,2017-02-21 06:11:32,http://bergnaum.net/geovany.miller,0.4137851761,62.195.110.220,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n6399,3,140,2017-01-28 23:36:01,http://mosciski.com/gunnar,0.6189960182,33.83.57.243,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n6400,3,140,2017-05-06 00:04:20,http://lindgrenjerde.org/unique,0.9859982223,66.45.117.224,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n6401,3,140,2017-06-03 18:28:50,http://grahamkiehn.org/stefanie,0.2531287665,230.13.248.159,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n6402,3,140,2017-06-11 14:22:55,http://herman.name/hildegard.robel,0.4580445433,65.69.88.102,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n6403,3,140,2017-06-11 20:45:44,http://tremblay.co/nicole.blanda,0.2308204977,241.137.235.157,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n6404,3,140,2017-04-28 09:11:08,http://shanahanhane.net/rhoda_zieme,0.1290331321,142.55.162.227,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n6405,3,140,2017-05-26 13:11:52,http://gleichner.biz/verla,0.6662069960,217.175.183.37,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n6406,3,140,2017-02-23 13:42:58,http://wunschlockman.net/blanche_lueilwitz,0.6493628477,45.191.221.26,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n6407,3,140,2017-05-31 01:10:24,http://mosciski.io/hoyt,0.8283926030,21.144.64.99,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n6408,3,140,2017-03-24 13:01:27,http://beier.com/pamela,0.8381995955,176.72.90.225,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n6409,3,140,2017-01-25 18:58:27,http://blickondricka.biz/chelsea,0.1076236514,65.130.85.36,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n12264,5,275,2017-01-21 03:02:53,http://abbottquitzon.info/remington.corkery,0.3360508046,138.245.131.247,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n12265,5,275,2017-03-08 19:28:31,http://dickinson.co/lurline.lehner,0.5566131469,49.163.153.27,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n12266,5,275,2017-03-24 19:24:33,http://hoeger.biz/dax,0.0368716295,133.58.212.40,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n12267,5,275,2016-12-13 12:21:56,http://kertzmann.net/roberto,0.7619531571,58.241.52.253,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n12268,5,275,2016-12-25 05:28:47,http://wisoky.info/adela,0.9074700084,106.132.9.70,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n12269,5,275,2016-12-22 00:41:12,http://bergstrom.net/isabel_keler,0.3521403076,203.178.229.58,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n12270,5,275,2017-04-20 13:51:47,http://hermannwitting.biz/wellington,0.0844487688,158.135.128.109,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n12271,5,275,2017-03-06 21:47:04,http://walter.biz/david_maggio,0.7075108705,77.164.248.60,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n12272,5,275,2017-03-10 03:36:10,http://moriettelind.io/william_ernser,0.9417105766,211.42.187.89,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n12273,5,275,2017-04-19 21:43:47,http://prosacco.com/hanna.crist,0.4546798121,142.218.128.69,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n12274,5,275,2017-06-07 14:59:56,http://macejkovic.biz/levi,0.9619941203,170.156.59.49,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n12275,5,275,2017-05-15 09:10:02,http://connelly.com/terrance_murazik,0.3199102955,153.37.42.185,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n12276,5,275,2016-12-28 13:07:57,http://smithtillman.net/flavie,0.1938164742,58.156.82.158,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n12277,5,275,2017-05-20 14:44:32,http://blanda.co/myrna_ledner,0.0319988476,153.113.79.70,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n12278,5,275,2017-04-29 00:38:22,http://roberts.co/timmy.trantow,0.9341448052,38.62.136.78,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n12279,5,275,2017-03-05 11:41:19,http://rath.net/edna,0.3080957963,154.56.178.137,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n12280,5,275,2017-01-25 21:52:24,http://fay.co/lina.rau,0.6199755258,160.124.175.163,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n12281,5,275,2017-01-19 02:59:56,http://monahan.co/trisha,0.2159740112,189.70.249.202,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n12282,5,275,2017-02-05 06:51:00,http://kingzieme.org/aiden.rolfson,0.7594094241,132.233.50.220,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n12283,5,275,2017-05-30 09:20:43,http://tremblaycummings.biz/carley_ryan,0.8662728946,86.224.154.166,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n12284,5,275,2017-03-09 22:04:09,http://uptonboyer.co/rex,0.1611521137,195.64.10.59,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n12285,5,275,2017-04-15 03:30:50,http://balistreri.name/mattie_rolfson,0.3323063187,2.222.63.163,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n12286,5,275,2017-02-11 00:05:31,http://jacobs.com/vicky,0.0885012252,191.120.248.64,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n12287,5,275,2017-05-30 22:11:47,http://gulgowski.net/hermann.oconner,0.6808919251,150.136.21.39,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n12288,5,275,2017-04-02 15:29:01,http://kilbackmoore.co/frederick,0.2369582049,15.224.71.71,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n12289,5,275,2017-01-01 19:00:11,http://monahan.co/derek,0.7570727199,250.59.85.100,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n12290,5,275,2017-04-23 11:02:18,http://bosco.biz/shakira.torphy,0.0993611696,26.77.105.238,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n12291,5,275,2017-06-11 05:34:02,http://homenick.com/anais.feil,0.8647500981,128.46.151.159,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n12292,5,275,2017-02-10 03:14:50,http://stammschimmel.com/margaretta_kilback,0.6482379851,177.18.93.38,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n12293,5,275,2017-03-17 20:37:03,http://padberg.com/noemie,0.6394385123,3.113.251.249,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n12294,5,275,2017-05-21 20:39:23,http://kozey.com/eddie,0.4267610022,131.77.27.240,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n12295,5,275,2017-01-09 00:00:31,http://cremin.com/alden_stoltenberg,0.5668973249,11.219.175.94,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n12296,5,275,2017-02-09 05:43:41,http://beattyhirthe.com/denis,0.5835324289,180.95.226.137,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n12297,5,275,2017-01-27 03:56:14,http://johnsonfarrell.name/sydnee,0.9460944867,237.126.220.154,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n12298,5,275,2017-02-24 05:16:51,http://thiel.name/wellington,0.7201660010,78.217.254.23,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n12299,5,275,2017-05-03 02:03:12,http://hagenes.io/wilma,0.6527421520,134.207.8.76,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n12300,5,275,2016-12-31 11:01:57,http://lubowitz.biz/mallie,0.9505579418,108.114.45.30,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n12301,5,275,2016-12-23 19:22:35,http://mcglynn.com/margarett,0.2703175220,242.158.48.122,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n12302,5,275,2017-06-04 04:53:18,http://christiansendare.com/helmer.ferry,0.3395245536,215.160.50.7,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n12303,5,275,2017-03-29 06:37:56,http://hoppe.com/lennie_lemke,0.7517829154,70.123.214.102,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n12304,5,275,2017-03-23 15:39:44,http://lemkepacocha.org/rickie,0.7810242527,155.35.4.29,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n12305,5,275,2016-12-24 03:17:50,http://johnson.com/mckayla.schmitt,0.8823610745,84.75.39.253,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n12306,5,275,2017-03-07 20:45:31,http://konopelski.info/mohammad_jacobi,0.7539223855,124.220.169.62,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n12307,5,275,2017-02-03 12:34:11,http://macejkovicfriesen.co/gladys_emard,0.9932231861,65.36.182.161,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n12308,5,275,2017-02-21 12:41:03,http://barton.biz/antonina,0.5258819225,177.58.212.30,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n12309,5,275,2017-05-28 03:22:22,http://wintheiser.co/cleve_nienow,0.7332386759,180.251.2.246,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n12310,5,275,2017-05-01 04:01:00,http://denesikhauck.io/stephanie,0.5985991975,162.205.111.14,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n12311,5,275,2017-05-07 17:25:14,http://reynolds.info/tomas.rempel,0.2183152721,50.231.87.72,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n12312,5,276,2017-01-05 07:16:06,http://zboncak.name/michale,0.2001611054,81.142.184.236,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n12313,5,276,2017-03-29 11:52:50,http://fadel.io/ron.altenwerth,0.7816472955,139.205.87.162,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n12314,5,276,2016-12-16 06:12:20,http://padberg.biz/maida,0.4919462451,213.216.244.111,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n15273,6,343,2017-01-15 15:41:02,http://murray.info/elmer_huel,,201.197.242.127,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n15274,6,343,2017-06-09 22:48:47,http://mccullough.com/clay,,160.155.140.244,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n15275,6,343,2017-02-13 02:11:39,http://wisokyupton.net/tevin_hickle,,124.125.58.226,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n15276,6,343,2016-12-31 09:35:02,http://dietrichjacobi.com/david,,62.52.102.9,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n15277,6,343,2017-02-07 10:23:44,http://littel.org/woodrow,,114.60.183.85,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n15278,6,343,2017-05-01 06:07:36,http://reynolds.co/tyler_haag,,116.141.65.21,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n15279,6,343,2017-03-30 21:42:00,http://dach.info/guiseppe,,249.246.204.35,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n15280,6,343,2017-04-04 04:10:47,http://donnelly.biz/rhoda.bernhard,,100.236.49.92,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n15281,6,344,2017-02-28 04:31:42,http://kubbode.org/ebba_jakubowski,,44.168.99.26,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n15282,6,344,2017-04-08 20:00:52,http://harvey.net/clifford,,231.157.13.17,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n15283,6,344,2017-01-01 16:21:08,http://cummings.info/eleazar,,88.2.247.68,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n15284,6,344,2017-03-19 21:53:17,http://renner.org/dawn,,243.53.204.55,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n15285,6,344,2017-02-24 17:15:18,http://hermannolan.com/robyn_howe,,43.211.179.210,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n15286,6,344,2017-04-01 14:17:51,http://stammlangworth.biz/mohammad,,153.231.102.192,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n15287,6,344,2017-03-01 08:45:35,http://emmerich.co/haley.blick,,240.99.62.164,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n15289,6,344,2017-01-18 17:24:29,http://hammesheathcote.com/landen,,137.221.154.122,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n15290,6,344,2017-02-11 14:12:50,http://eichmann.io/hayley,,53.252.70.148,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n15291,6,344,2017-06-06 18:24:30,http://wiza.biz/shakira_volkman,,58.162.149.22,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n15292,6,344,2017-02-18 20:36:54,http://schuppevon.co/tyrique.rohan,,206.45.144.15,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n15293,6,344,2017-05-23 01:35:03,http://rolfson.biz/nat,,160.131.66.36,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n15294,6,344,2016-12-15 02:21:43,http://rodriguez.com/tania,,8.69.9.172,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n15295,6,344,2017-02-18 01:11:14,http://kaulkehowell.name/amelia_osinski,,107.91.196.222,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n15296,6,344,2017-01-16 09:10:12,http://abbott.org/marielle,,99.14.73.155,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n15297,6,344,2017-03-12 22:36:07,http://legros.org/triston.runolfsdottir,,63.189.73.225,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n15298,6,344,2017-01-08 06:19:07,http://kling.co/serenity.oreilly,,210.128.42.223,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n15299,6,344,2016-12-18 10:20:07,http://barrowswunsch.org/hanna_bernhard,,5.187.11.100,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n15300,6,344,2017-06-01 22:05:18,http://greenfelderdickinson.info/jordon.heathcote,,15.254.24.23,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n15301,6,344,2017-04-11 07:21:31,http://keler.info/hilda,,182.113.119.84,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n15302,6,344,2017-04-24 12:24:33,http://wildermanfeest.co/harvey,,191.78.190.180,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n15303,6,344,2017-02-17 16:38:34,http://ledner.io/jadyn,,84.160.109.203,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n15304,6,344,2017-02-22 00:23:36,http://kohler.com/hillard.hudson,,24.43.129.186,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n15305,6,344,2017-04-20 13:06:30,http://stroman.name/trea.moriette,,105.91.51.3,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n15306,6,344,2017-03-15 04:26:44,http://parker.net/toy,,152.113.3.51,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n15307,6,344,2017-04-28 20:31:52,http://howe.io/teagan.crona,,201.9.207.3,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n15308,6,344,2017-05-17 16:48:45,http://jacobsonharvey.co/janelle,,110.74.152.236,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n15309,6,344,2017-04-13 14:24:21,http://harriauer.org/lyda.daniel,,108.32.5.196,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n15310,6,344,2017-04-06 06:35:42,http://emard.name/briana_shields,,133.112.128.54,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n15311,6,344,2017-02-03 00:11:31,http://walker.org/jefferey,,119.117.229.85,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n15312,6,344,2017-02-28 20:40:02,http://jacobi.io/casey,,47.185.82.141,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n15313,6,344,2017-03-25 08:06:59,http://schuppe.io/narciso,,39.207.238.18,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n15314,6,344,2017-05-01 15:52:07,http://wintheiser.info/germaine,,64.34.186.176,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n15315,6,344,2017-05-24 02:51:09,http://danielschowalter.net/jada,,84.171.219.189,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n15316,6,344,2017-05-26 02:03:30,http://davisheller.com/genoveva_littel,,24.143.174.247,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n15317,6,344,2017-04-02 23:05:13,http://moriette.co/aubrey,,199.79.254.202,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n15318,6,344,2017-06-11 20:57:37,http://veumlangworth.name/emelie_price,,171.176.221.244,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n15319,6,344,2017-02-01 12:25:13,http://moen.org/marietta,,163.244.44.104,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n15320,6,344,2017-03-26 14:42:32,http://lynchkshlerin.name/brooklyn,,43.28.224.119,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n15321,6,344,2017-01-10 11:57:30,http://damorefay.net/vance_dickinson,,15.152.50.151,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n15322,6,344,2017-05-17 15:58:27,http://abbottweinat.biz/owen,,25.105.175.119,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n15323,6,344,2017-04-21 21:14:30,http://leannonbrown.info/morton.gusikowski,,63.185.137.129,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n15324,6,344,2016-12-16 15:18:59,http://mueller.org/dario,,141.130.113.240,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n15325,6,344,2017-05-08 21:02:37,http://lang.biz/marques.kovacek,,103.42.188.248,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n15326,6,344,2017-06-08 21:48:21,http://murray.biz/norene,,250.15.107.36,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n15327,6,344,2017-01-13 15:59:07,http://mueller.info/angelina_kunde,,29.238.101.129,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n15328,6,344,2017-03-18 00:37:43,http://shields.name/lindsey_kertzmann,,50.129.179.169,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n2469,2,54,2017-06-13 16:45:47,http://roobpowlowski.co/keshaun_pacocha,0.1376652059,159.80.152.126,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n2470,2,54,2016-12-16 22:19:54,http://steuber.biz/daisha,0.6553158244,166.212.44.94,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n2471,2,54,2017-05-28 19:03:41,http://hermann.name/dean,0.0998805735,120.112.191.40,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n2472,2,54,2017-02-25 11:13:27,http://rogahnanderson.info/annette,0.1487252361,213.51.147.155,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n2473,2,54,2017-04-01 23:05:16,http://murray.co/iliana.ryan,0.8899319728,74.49.252.55,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n2474,2,54,2017-02-23 14:20:12,http://crona.io/garret_yost,0.4080043172,77.103.12.113,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n2475,2,54,2017-03-15 19:26:06,http://ledner.net/kelsie.daugherty,0.4644809780,112.40.248.83,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n2476,2,54,2017-01-19 16:00:33,http://rau.io/ro.vonrueden,0.0434729480,128.28.242.53,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n2477,2,54,2017-05-30 03:07:26,http://bins.biz/jakayla_jacobson,0.5296388744,78.234.87.148,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n2478,2,54,2017-01-23 01:12:46,http://hauck.info/amir,0.1001439741,175.38.218.73,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n2479,2,54,2017-01-02 00:16:12,http://friesen.biz/kristina,0.6982916759,202.26.170.92,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n2480,2,55,2017-01-29 05:00:01,http://beer.biz/alphonso.wiza,0.2933637260,71.81.245.244,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n2481,2,55,2017-05-02 05:51:55,http://gutkowski.net/nathaniel.upton,0.6982740163,35.107.151.36,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n2482,2,55,2017-05-30 18:56:30,http://gottliebpollich.info/marlen.gleichner,0.7685743326,79.83.80.125,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n2483,2,55,2017-04-30 12:46:49,http://mcclurepowlowski.com/adella,0.0784691170,2.224.118.87,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n2484,2,55,2017-04-24 17:40:17,http://crona.io/lucinda,0.6413251726,115.81.85.62,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n2485,2,55,2017-03-27 03:11:10,http://andersonfay.co/kaylie_kihn,0.7402888559,21.203.253.252,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n2486,2,55,2017-02-28 13:11:39,http://mertz.com/ena,0.8391321516,179.192.21.80,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n2487,2,55,2017-06-06 02:34:48,http://volkman.name/cora.ankunding,0.9616650348,19.233.159.95,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n2488,2,55,2017-02-06 09:38:03,http://lang.co/katlynn_kirlin,0.6222331055,102.219.13.73,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n2489,2,55,2017-05-15 13:11:04,http://schaden.co/darien.heaney,0.7065338815,113.233.184.148,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n2490,2,55,2017-01-16 13:22:00,http://westdaugherty.com/destany,0.0898102048,136.122.235.101,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n2491,2,55,2017-04-02 13:12:13,http://oharapurdy.com/evalyn,0.8124353259,13.203.60.147,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n2492,2,55,2017-03-26 07:27:50,http://mcdermott.co/marlen,0.4265812321,235.205.45.56,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n2493,2,55,2017-03-02 18:54:42,http://halvorson.com/christop,0.3876591823,228.32.155.102,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n2494,2,55,2016-12-20 12:36:13,http://johnsbrekke.biz/tyra,0.6168654309,96.49.233.114,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n2495,2,55,2017-05-31 14:47:00,http://farrell.net/nathanial.ernser,0.7017816544,62.202.141.168,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n2496,2,55,2017-02-10 07:49:41,http://beerabbott.name/elia,0.2959021392,40.29.50.212,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n2497,2,55,2017-03-21 09:32:34,http://wintheiserprice.name/johnny.wintheiser,0.2247293141,99.229.130.181,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n2498,2,55,2017-03-04 05:50:57,http://fisheroconner.co/mae,0.1329079173,121.31.213.165,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n2499,2,55,2017-05-02 21:43:09,http://collierschaden.biz/elisa.schaden,0.4299326549,130.242.45.204,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n2500,2,55,2016-12-21 10:12:51,http://konopelskiborer.io/carson.schulist,0.8249208574,53.183.133.28,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n2501,2,55,2017-01-31 05:50:26,http://kunze.info/tyrese,0.6774383913,116.46.237.244,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n2502,2,55,2017-03-14 03:36:51,http://bechtelarsimonis.com/jenifer,0.4854301452,23.28.156.234,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n2503,2,55,2017-05-13 18:13:46,http://bailey.co/geovanny_hansen,0.6224936895,9.77.4.131,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n2504,2,55,2017-06-12 19:40:27,http://kihn.io/aurore.sauer,0.4651812668,165.3.152.168,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n2505,2,55,2017-03-21 11:46:17,http://jaskolskimurphy.org/zakary,0.4325778088,81.173.210.4,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n2506,2,55,2017-02-06 17:40:10,http://morar.co/paul,0.5537675962,75.84.233.211,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n2507,2,55,2016-12-25 04:43:50,http://moen.com/leanna,0.8428602755,68.236.8.72,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n2508,2,55,2017-06-05 13:57:27,http://macejkovic.name/josianne.mueller,0.5623264176,97.100.174.40,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n2509,2,55,2016-12-27 01:53:08,http://kris.info/isabell_thompson,0.8242909863,173.223.150.174,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n2510,2,55,2017-04-11 01:16:23,http://heidenreich.com/shayne,0.5579806403,46.191.180.85,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n2511,2,55,2017-03-02 09:35:56,http://hand.io/kelvin,0.0933199378,80.239.4.33,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n2512,2,55,2017-04-29 06:01:52,http://kuphalmckenzie.com/nelson_cartwright,0.2074446034,164.116.62.102,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n2513,2,55,2017-04-12 01:54:17,http://waters.co/dandre,0.8209743581,138.70.54.232,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n2514,2,55,2017-01-07 04:29:37,http://daniel.com/richmond,0.2543039085,216.9.4.237,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n2515,2,55,2016-12-15 04:30:31,http://osinski.biz/brody,0.6562397367,183.79.115.81,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n2516,2,55,2017-05-07 09:29:16,http://spencer.io/alf.kub,0.6931936520,195.49.154.47,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n2517,2,55,2017-02-10 12:21:05,http://thompsondicki.org/marcia.keler,0.3465529883,16.111.192.151,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n2518,2,55,2017-02-06 22:33:40,http://erdman.io/birdie_parisian,0.0569547118,163.247.190.103,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n2519,2,55,2017-05-26 19:36:18,http://shanahan.name/oleta.streich,0.5992936409,13.240.44.214,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n9399,4,211,2017-06-11 18:32:24,http://christiansenpfeffer.org/kaylin_haag,0.7329149630,150.102.172.170,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n9400,4,211,2017-06-12 17:55:25,http://marquardt.com/isabelle,0.2733610399,66.227.9.196,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n9401,4,211,2017-06-08 16:24:11,http://haag.io/jeramie.ward,0.0692280223,95.215.140.202,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n9402,4,211,2017-04-02 15:24:35,http://damore.net/herman_champlin,0.4622915705,134.156.118.183,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n9403,4,211,2017-01-17 01:21:06,http://leuschkeswaniawski.info/bernardo,0.6154881653,12.51.42.24,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n9404,4,211,2017-01-02 21:58:18,http://reichertcormier.info/isabelle_ferry,0.2269357814,218.213.15.188,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n9405,4,211,2017-01-08 12:32:40,http://jacobs.name/giuseppe,0.4705715981,180.39.210.35,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n9406,4,211,2017-06-10 18:31:53,http://stanton.info/caterina.pagac,0.6715041217,59.199.36.112,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n9407,4,211,2017-01-31 17:27:42,http://hahn.info/kurtis_osinski,0.7132647084,12.149.25.183,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n9408,4,211,2017-03-31 02:42:59,http://konopelski.com/herbert,0.2071476949,9.65.116.125,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n9409,4,211,2017-05-10 03:32:14,http://mclaughlin.org/mariah,0.1170145298,115.167.202.165,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n9410,4,211,2017-01-28 05:26:24,http://marquardtkozey.io/douglas_spencer,0.5444746912,225.27.38.197,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n9411,4,211,2017-03-15 13:53:03,http://greenholt.biz/garry,0.5884721635,198.60.237.130,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n9412,4,211,2017-05-02 18:16:04,http://hegmann.name/henry.doyle,0.6682490290,125.98.105.192,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n9413,4,211,2017-06-14 03:11:40,http://parisian.io/connie,0.7446083749,249.25.118.174,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n9414,4,211,2017-01-01 10:54:18,http://lockmannitzsche.org/tina_romaguera,0.0208218114,250.12.230.165,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n9415,4,211,2017-03-20 15:24:34,http://lemke.org/shanelle_braun,0.7212142886,225.195.28.138,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n9416,4,211,2017-03-06 15:18:45,http://jakubowski.com/jaleel,0.9687462391,31.140.119.12,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n9417,4,211,2017-05-05 03:17:16,http://klingreilly.biz/lauriane.legros,0.7197915302,218.49.172.193,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n9418,4,211,2017-05-23 12:28:52,http://mayer.org/junior.farrell,0.2983501204,130.74.195.217,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n9419,4,211,2017-01-05 18:38:34,http://hanecollier.com/sigmund,0.7322762465,56.151.236.17,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n9420,4,211,2017-05-30 22:33:19,http://rodriguez.co/arno.pouros,0.0204592383,173.60.76.34,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n9421,4,211,2017-01-02 16:35:17,http://nolan.co/sister_jacobson,0.1546756897,109.121.228.164,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n9422,4,211,2017-02-12 20:20:57,http://rutherford.biz/lloyd,0.9095044179,193.125.41.234,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n9423,4,211,2017-03-17 21:03:49,http://kerluke.org/lawrence,0.2883967101,216.236.114.141,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n9424,4,211,2017-03-21 16:13:52,http://reinger.net/laron,0.9805945881,214.191.146.200,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n9425,4,212,2017-01-13 16:51:26,http://klein.biz/cornelius.mcdermott,,103.66.254.112,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n9426,4,212,2017-03-22 18:24:38,http://kshlerin.net/verdie,,135.8.87.8,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n9427,4,212,2017-01-12 17:03:07,http://franecki.biz/dashawn,,122.170.79.8,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n9428,4,212,2017-01-24 10:02:50,http://kautzerosinski.com/nels,,117.11.177.112,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n9429,4,212,2017-06-12 19:09:09,http://mohrmetz.org/korbin.swaniawski,,84.69.102.32,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n9430,4,212,2017-03-28 00:49:03,http://trompbode.net/thora,,199.103.143.101,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n9431,4,212,2017-06-07 06:51:45,http://davis.biz/marlin,,251.199.32.241,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n9432,4,212,2017-05-25 22:09:37,http://goldneryost.org/jedediah.erdman,,50.198.119.113,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n9433,4,212,2017-01-10 05:05:27,http://nitzsche.info/lora.stracke,,97.38.166.248,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n9434,4,212,2017-04-11 20:23:02,http://bartonschulist.com/ralph_anderson,,85.84.254.247,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n9435,4,212,2017-03-31 12:50:20,http://jacobi.org/jailyn_casper,,159.164.199.157,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n9436,4,212,2017-04-18 16:35:33,http://hudsonconsidine.info/adeline,,124.175.214.104,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n9437,4,212,2016-12-13 11:31:40,http://witting.io/sanford_huels,,16.84.207.169,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n9438,4,212,2017-02-26 16:16:52,http://kautzerhilll.info/mona_bins,,142.133.25.149,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n9439,4,212,2017-04-18 03:27:09,http://skilesquigley.info/kale,,41.123.124.18,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n9440,4,212,2017-05-29 14:08:00,http://schowalter.biz/chad.braun,,115.3.222.2,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n9441,4,212,2017-02-22 02:43:39,http://mullermiller.com/jarrett_wiza,,168.110.80.196,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n9442,4,212,2017-04-16 20:35:17,http://tillman.org/rahsaan,,127.31.200.157,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n9443,4,212,2017-04-11 08:48:11,http://moriette.org/obie.altenwerth,,88.117.235.163,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n9444,4,212,2017-03-06 07:28:00,http://oreilly.name/liam_zboncak,,89.111.166.114,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n9445,4,212,2017-04-02 00:40:17,http://adamchmidt.info/danyka,,254.87.210.193,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n9446,4,212,2017-01-07 11:51:48,http://shanahan.biz/tanner,,66.73.29.100,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n9447,4,212,2017-02-05 02:19:37,http://kris.co/lucile,,153.2.254.61,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n9448,4,212,2017-05-04 01:12:55,http://rodriguez.com/evan,,228.122.241.141,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n9449,4,212,2017-06-10 23:15:22,http://durgan.io/nathanael.abernathy,,45.145.234.214,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n9450,4,212,2017-01-15 08:50:36,http://medhurst.info/alene_emmerich,,18.8.251.184,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n9451,4,212,2017-05-08 06:12:34,http://pfefferpacocha.info/amara,,196.156.91.231,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n6410,3,140,2017-02-07 18:52:26,http://mclaughlin.biz/shanon_pollich,0.6068526101,193.176.106.211,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n6411,3,140,2017-04-06 11:17:03,http://fay.org/greyson,0.4311413296,234.141.113.132,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n6412,3,140,2017-06-06 15:26:54,http://abshire.net/elliot,0.1775242058,110.241.101.65,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n6413,3,140,2017-04-17 13:46:11,http://torphyschaden.net/loyal,0.6346625091,236.20.167.68,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n6414,3,140,2017-02-09 21:40:17,http://heaneybode.io/tremaine_herzog,0.4352150545,241.77.122.199,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n6415,3,140,2017-01-08 07:17:11,http://wilkinson.co/milford,0.1880575295,240.235.134.138,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n6416,3,140,2017-06-12 23:36:17,http://windler.org/drew_cummings,0.5167461771,147.76.247.214,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n6417,3,140,2017-05-16 01:29:02,http://abshireyost.com/ken,0.3493025812,130.10.24.120,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n6418,3,140,2017-04-07 06:46:44,http://satterfield.info/jana,0.6257156864,22.24.177.25,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n6419,3,140,2017-03-07 14:24:36,http://bednar.com/jabari,0.5950558764,119.252.39.219,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n6420,3,140,2017-05-10 21:36:21,http://weber.org/johnnie.mcglynn,0.9555044164,237.189.193.242,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n6421,3,140,2017-04-08 13:33:06,http://oconnellparker.info/roderick,0.0705178582,74.177.96.65,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n6422,3,140,2017-03-02 04:45:42,http://barrowshirthe.info/cooper,0.1732032916,57.248.13.251,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n6423,3,140,2017-03-18 09:53:22,http://oharasmith.net/fay,0.2861797084,90.134.130.96,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n6424,3,140,2017-06-14 04:55:58,http://sipes.net/leif,0.4671150186,56.59.220.7,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n6425,3,140,2016-12-22 20:47:00,http://gorczany.info/francesca,0.5067994423,197.97.220.141,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n6426,3,140,2017-06-05 14:42:45,http://nader.co/immanuel,0.2523675524,210.19.42.193,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n6427,3,140,2016-12-20 19:33:18,http://conroyhahn.com/johann,0.0484292042,210.239.105.68,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n6428,3,140,2017-03-14 16:41:05,http://cruickshank.io/kaandra_padberg,0.7218668952,254.180.106.158,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n6429,3,140,2016-12-21 13:29:50,http://schmeler.net/jaquan,0.1601703674,120.84.176.39,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n6430,3,140,2016-12-28 03:58:44,http://schmittzulauf.biz/myriam.lueilwitz,0.1908061177,46.197.65.48,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n6431,3,140,2017-01-31 16:40:41,http://dare.name/dino.vonrueden,0.2058627319,102.251.52.3,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n6432,3,140,2017-02-20 08:36:30,http://kubkuhn.name/norwood,0.7202211069,224.250.106.186,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n6433,3,140,2017-04-13 13:16:25,http://witting.name/justyn.mann,0.2010426384,90.59.213.244,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n6434,3,140,2017-02-19 01:22:15,http://ziemann.info/travis,0.1295173813,172.126.212.23,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n6435,3,140,2017-03-12 08:11:00,http://ebert.biz/ludie_cronin,0.1332296773,2.109.77.79,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n6436,3,140,2017-03-04 19:28:01,http://gutmann.io/jordon.dickens,0.7937100156,128.241.168.21,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n6437,3,140,2017-01-10 05:50:45,http://dietrich.com/rosella,0.9592443277,47.3.209.151,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n6438,3,140,2016-12-28 10:29:46,http://kutchking.net/effie.bailey,0.7082703601,143.13.183.246,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n6439,3,140,2017-05-14 22:36:11,http://corwinherman.name/minnie.cain,0.8023136747,88.157.112.226,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n6440,3,141,2017-04-22 11:39:14,http://spinka.biz/nathanial.brown,0.7834279349,198.219.229.235,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n6441,3,141,2017-04-06 23:16:45,http://kertzmann.biz/ericka,0.1179760095,182.8.123.112,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n6442,3,141,2017-04-20 03:31:17,http://hermanglover.net/rico,0.5147940181,247.232.117.173,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n6443,3,141,2016-12-14 10:46:52,http://beerwindler.io/micah,0.7163490741,160.182.6.66,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n6444,3,141,2017-02-24 23:23:01,http://bogisichdonnelly.info/cornell,0.4190616994,73.72.251.19,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n6445,3,141,2017-03-02 05:12:04,http://wintheiserdickinson.io/christy,0.8244214012,12.237.182.244,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n6446,3,141,2017-01-12 23:59:24,http://moriette.info/jeyca,0.9629920400,22.33.159.96,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n6447,3,141,2017-03-24 05:05:43,http://cronin.io/weldon_hamill,0.1303221086,240.27.12.111,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n6448,3,141,2016-12-19 17:39:11,http://lynch.name/kareem.abbott,0.7937626992,85.59.183.164,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n6449,3,141,2017-03-11 02:01:15,http://kertzmannlegros.name/lukas.gerhold,0.4414455521,156.69.164.236,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n6450,3,141,2017-05-01 05:47:27,http://luettgenbernhard.biz/lori,0.2162075829,85.115.106.71,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n6451,3,141,2017-01-18 04:59:59,http://heaneyjaskolski.net/alba_fahey,0.4086008375,137.217.5.121,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n6452,3,141,2016-12-28 01:36:24,http://hintzondricka.org/elroy,0.4204390014,16.53.30.99,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n6453,3,141,2017-05-03 03:58:19,http://zieme.net/tyler,0.4510168509,165.195.138.120,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n6454,3,141,2017-04-17 05:05:29,http://ledner.co/leonard_terry,0.5904854657,11.172.201.132,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n6455,3,141,2017-04-23 01:56:39,http://weimann.org/dashawn,0.2565429494,230.231.166.186,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n6456,3,141,2017-05-16 12:09:30,http://harvey.biz/elbert,0.2563860021,112.106.39.108,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n6457,3,141,2017-05-10 09:34:06,http://cormier.biz/emmett_gislason,0.2863691887,158.147.149.125,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n6458,3,141,2017-06-11 00:59:40,http://hegmannkiehn.net/ola,0.8514715575,100.184.180.4,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n6459,3,141,2017-01-31 09:59:15,http://feeney.info/sheila.stroman,0.8037566920,23.197.86.155,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n6460,3,141,2017-04-05 16:33:24,http://christiansen.org/rick,0.5610079912,112.134.55.242,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n6461,3,141,2017-05-07 10:53:26,http://dickens.com/citlalli,0.4747050192,221.191.43.72,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n12315,5,276,2017-04-04 05:17:39,http://thompson.net/luz,0.1245938421,211.230.108.182,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n12316,5,276,2017-02-25 04:06:24,http://cronin.net/jamil,0.8229863779,143.125.56.99,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n12317,5,276,2017-03-03 09:25:56,http://creminkling.co/krystal,0.0154400923,45.64.218.155,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n12318,5,276,2016-12-14 18:04:55,http://willmsrunte.io/mable.kling,0.2340428431,179.86.60.63,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n12319,5,276,2017-05-07 01:38:33,http://turcottegorczany.co/megane_kirlin,0.6282171495,27.25.210.93,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n12320,5,276,2017-05-29 13:03:59,http://pagac.io/ramiro.mueller,0.4997321873,56.30.17.27,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n12321,5,276,2017-03-10 20:49:15,http://ruecker.com/helmer_wunsch,0.9977485969,115.253.68.239,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n12322,5,276,2017-03-31 11:26:57,http://lindgrenwest.info/wiley_wolf,0.6368795697,34.5.253.114,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n12323,5,276,2017-06-11 22:35:05,http://rice.name/elliott,0.9047116281,197.152.220.219,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n12324,5,276,2017-02-10 17:21:42,http://wyman.biz/dustin.hansen,0.8834770029,142.15.167.57,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n12325,5,276,2017-04-16 17:10:04,http://littleemard.org/humberto,0.7759275370,243.249.207.30,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n12326,5,276,2017-06-10 11:44:20,http://gleason.name/alvena_volkman,0.1433168133,240.156.78.150,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n12327,5,276,2017-03-24 05:39:07,http://friesengibson.name/garland,0.5872954834,180.170.236.243,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n12328,5,276,2017-03-31 04:42:13,http://grimes.com/jermain,0.9996232106,169.63.238.123,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n12329,5,276,2017-04-26 03:14:02,http://goldner.name/sydnee_botsford,0.8585202820,174.95.6.228,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n12330,5,276,2017-05-23 16:33:56,http://fritsch.io/floyd_fay,0.7111542601,80.74.8.143,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n12331,5,276,2017-05-15 05:21:18,http://funkaltenwerth.info/giovanny,0.9565265199,44.21.122.109,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n12332,5,276,2017-01-27 15:03:14,http://ward.info/jena.bergnaum,0.6214597970,70.93.232.41,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n12333,5,276,2017-02-13 23:47:14,http://boyerdach.net/kaylah_halvorson,0.3935029746,214.179.117.215,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n12334,5,276,2017-01-28 00:17:01,http://raynor.net/johann_kertzmann,0.3017178698,208.253.95.175,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n12335,5,276,2017-01-09 11:46:19,http://kreigerhand.io/meda,0.7318144963,168.154.53.119,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n12336,5,276,2017-01-16 09:21:16,http://goldner.info/damion,0.1878417901,184.16.41.44,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n12337,5,276,2017-01-17 07:02:54,http://nicolas.name/leone,0.4983207905,83.8.126.207,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n12338,5,276,2017-05-11 22:54:06,http://dachgottlieb.info/bart,0.7617898608,228.187.33.173,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n12339,5,276,2017-05-21 15:34:11,http://klockowehner.com/pierce,0.1420916514,65.250.233.20,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n12340,5,276,2016-12-27 16:42:27,http://paucek.co/jee.ledner,0.0966197770,35.219.67.86,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n12341,5,276,2017-01-28 23:44:36,http://loweschmitt.net/francesco,0.5049618842,121.174.27.166,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n12342,5,276,2017-05-16 20:38:43,http://kihnmckenzie.com/ari,0.0246283131,238.219.106.239,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n12343,5,276,2017-03-15 12:43:53,http://deckowframi.io/ismael,0.5620613241,239.39.141.104,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n12344,5,276,2017-03-01 05:44:11,http://bailey.info/jeffery_mckenzie,0.8964999629,102.84.32.116,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n12345,5,276,2017-04-26 15:37:30,http://bernhard.org/coby,0.8076975884,48.152.146.10,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n12346,5,276,2016-12-21 17:22:20,http://ziemann.org/aileen_bailey,0.5939816179,113.138.196.82,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n12347,5,276,2017-01-21 01:13:58,http://beahanschuster.biz/karley,0.2953783356,141.7.176.167,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n12348,5,276,2016-12-16 12:15:30,http://pagacgrady.name/urban,0.8142533469,26.250.96.115,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n12349,5,276,2017-02-23 23:07:39,http://whiteratke.name/antwon,0.7234452439,2.131.100.247,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n12350,5,276,2016-12-23 06:35:53,http://johnson.info/marques_littel,0.8627129725,241.103.22.220,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n12351,5,276,2017-02-27 14:30:39,http://rempelcrist.info/mauricio,0.3729186422,121.126.252.250,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n12352,5,276,2016-12-30 16:40:40,http://weimann.com/jayden,0.8062340566,247.229.37.25,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n12353,5,276,2017-05-26 21:44:59,http://weimann.biz/kolby_osinski,0.5062131192,72.47.128.72,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n12354,5,276,2016-12-23 04:01:02,http://mcclure.co/ahmad,0.7069956173,199.30.74.218,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n12355,5,276,2017-05-16 18:46:16,http://kozeylarkin.co/burnice_osinski,0.4435905820,18.25.44.27,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n12356,5,276,2017-03-10 02:14:11,http://feest.co/burley,0.3399967225,163.128.54.47,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n12357,5,276,2017-05-09 11:38:19,http://balistreri.net/barney_keler,0.7357131973,216.15.108.19,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n12358,5,276,2017-01-20 13:50:47,http://oreillyharris.info/veda.langosh,0.8694034082,150.168.122.206,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n12359,5,276,2017-05-10 06:17:43,http://thompsonhills.biz/gianni,0.8675569459,19.64.167.76,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n12360,5,276,2017-05-20 08:39:33,http://mcclure.biz/sibyl,0.1562443413,187.174.119.12,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n12361,5,276,2017-01-16 18:50:17,http://lynch.com/cortney,0.1901213851,26.37.97.179,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n12362,5,276,2017-05-05 04:34:35,http://schuppe.net/marley_baumbach,0.5467797189,110.34.122.187,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n12363,5,276,2017-03-11 14:10:00,http://bogisich.io/addie,0.1624575752,204.53.17.190,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n12364,5,276,2017-04-03 09:50:53,http://mann.com/derek.abernathy,0.6455735569,121.129.174.75,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n12365,5,276,2017-02-13 20:58:32,http://zemlakblick.com/mireya,0.0773085477,238.124.142.171,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n12366,5,276,2016-12-22 18:11:26,http://rolfsonwitting.org/annabel,0.7385035228,99.133.27.73,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n15329,6,344,2017-02-22 20:21:51,http://heathcotehilll.org/ellis,,132.131.124.229,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n15330,6,344,2017-03-08 16:55:14,http://mertzvonrueden.com/tavares.lubowitz,,216.62.239.204,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n15331,6,344,2017-02-06 11:43:23,http://grahamcole.io/suzanne,,135.20.14.63,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n15332,6,344,2017-05-27 12:33:17,http://franecki.co/yesenia,,213.225.97.172,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n15333,6,345,2017-01-02 17:59:01,http://wilkinson.biz/valentine_hudson,0.5516121172,183.59.239.200,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n15334,6,345,2017-02-09 10:25:31,http://hammesokuneva.co/ludwig_mills,0.0569908049,128.108.28.56,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n15335,6,345,2016-12-24 14:55:05,http://koelpinspencer.name/cordell,0.7006015060,250.189.130.101,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n15336,6,345,2017-05-21 17:42:59,http://pfannerstillwisozk.co/francesco_yost,0.8640271524,56.201.171.216,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n15337,6,345,2017-01-23 14:07:53,http://brakus.info/danielle_huel,0.9618580883,46.95.94.127,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n15338,6,345,2017-05-17 12:33:31,http://damorebreitenberg.io/arvid,0.8638478389,39.51.165.36,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n15339,6,345,2017-06-05 09:14:38,http://kingking.biz/camden.thiel,0.2538111727,199.59.97.148,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n15340,6,345,2017-04-04 04:53:28,http://lueilwitzwitting.name/cornelius.daugherty,0.0196035505,61.98.133.143,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n15341,6,345,2017-02-02 21:45:44,http://turcotte.net/nettie,0.3667576158,228.97.82.50,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n15342,6,345,2017-03-07 16:46:21,http://quigley.biz/maureen.herman,0.5492664271,114.237.191.214,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n15343,6,345,2017-02-03 08:11:21,http://parker.io/ona,0.7313932587,197.247.21.91,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n15344,6,345,2017-05-15 19:15:57,http://douglas.com/hillard_jones,0.1290171924,12.44.35.147,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n15345,6,345,2017-03-13 00:46:29,http://hayesgreenholt.io/nathanial,0.6558660680,20.178.74.132,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n15346,6,345,2017-04-18 01:41:05,http://schinnerklocko.org/judah.schuster,0.5318601960,3.26.125.153,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n15347,6,345,2017-02-27 04:35:27,http://oberbrunnerhaley.co/june_powlowski,0.0912056261,122.50.144.169,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n15348,6,345,2017-01-21 15:10:49,http://larkin.org/arielle.schroeder,0.2103725543,151.202.152.39,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n15349,6,345,2017-03-10 09:59:44,http://millwift.biz/adrain,0.7126190731,151.252.121.87,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n15350,6,345,2017-05-18 04:25:48,http://roberts.biz/maurine.wolff,0.5071947403,244.120.245.232,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n15351,6,345,2017-05-30 15:12:07,http://leffler.co/rodolfo.goldner,0.1196561373,136.221.217.150,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n15352,6,345,2017-05-05 19:27:50,http://hagenesbrekke.name/elwyn,0.2832094646,174.10.29.71,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n15353,6,345,2017-03-18 00:42:00,http://mclaughlingoldner.io/yvonne_jakubowski,0.7204790107,80.141.252.148,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n15354,6,345,2017-02-14 04:56:51,http://trantow.net/mafalda,0.8854696204,223.71.53.4,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n15355,6,345,2016-12-20 07:13:02,http://dooley.info/trevion,0.9176201221,106.187.77.31,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n15356,6,345,2017-01-07 10:40:35,http://sawayn.com/davin,0.0476147458,94.241.138.212,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n15357,6,345,2017-02-17 19:05:22,http://witting.info/charlene,0.6609751449,41.147.128.148,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n15358,6,345,2017-03-29 17:00:55,http://trantow.net/sam.bernier,0.6469723282,241.50.165.194,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n15359,6,345,2017-06-06 21:03:44,http://fadeljohns.com/marco,0.9677392148,196.208.143.198,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n15360,6,345,2017-02-11 04:34:59,http://zboncakgleichner.io/obie.beier,0.9958544291,51.88.61.5,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n15361,6,345,2017-02-21 02:00:32,http://leffler.biz/dax,0.8906229433,149.163.134.63,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n15362,6,345,2017-05-31 14:23:06,http://gleichner.org/dillon,0.8558024150,20.105.72.170,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n15363,6,345,2017-05-01 07:15:03,http://bartell.name/linnea_sawayn,0.9206186141,87.53.138.68,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n15364,6,345,2017-04-06 02:58:13,http://jenkinsnolan.co/vada.anderson,0.5602940669,237.241.60.201,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n15365,6,345,2017-05-02 11:32:00,http://framigraham.biz/ezekiel,0.4765118850,148.24.41.228,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n15366,6,345,2017-04-17 00:59:37,http://homenick.name/marilou,0.4158873247,152.206.11.77,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n15367,6,345,2017-03-22 05:37:10,http://daughertycruickshank.name/abagail_willms,0.8896438446,3.240.136.72,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n15368,6,345,2016-12-23 20:51:11,http://kihn.name/adan,0.4425875974,13.46.244.12,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n15369,6,345,2017-03-16 07:57:28,http://gulgowski.co/fred.schinner,0.0440594323,173.24.86.224,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n15370,6,345,2017-02-06 03:42:48,http://rowe.com/emerald.zieme,0.7183463469,232.20.155.70,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n15371,6,345,2016-12-30 03:09:34,http://cain.info/meggie_greenfelder,0.9139643588,59.107.129.98,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n15372,6,345,2016-12-27 05:33:02,http://reilly.co/maurine,0.4107003480,62.54.75.185,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n15373,6,346,2017-03-11 02:15:53,http://cruickshank.org/leda.thiel,0.7479382715,6.172.13.127,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n15374,6,346,2017-05-28 07:53:33,http://bartoletti.net/lysanne_gusikowski,0.1830214792,253.169.108.223,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n15375,6,346,2017-05-25 14:40:34,http://block.co/garrison,0.1138116066,221.19.95.227,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n15376,6,346,2017-05-17 06:54:19,http://pacocharempel.net/kali,0.0526155784,82.76.165.194,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n15377,6,346,2017-05-07 17:05:51,http://davis.com/geraldine,0.3205749386,26.250.100.216,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n15378,6,346,2017-03-08 13:24:23,http://batz.co/kraig,0.2796871906,20.86.145.50,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n15379,6,346,2017-01-21 08:44:19,http://stiedemann.net/nigel.little,0.5489254835,80.112.160.98,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n2520,2,55,2017-03-04 19:46:05,http://carter.name/sarina_zboncak,0.2406291522,17.106.125.161,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n2521,2,56,2017-05-21 00:00:04,http://fay.io/millie_franecki,0.5040639485,148.94.90.128,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n2522,2,56,2017-01-18 10:58:28,http://leannon.org/mariam_bednar,0.1610338434,236.33.10.218,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n2523,2,56,2017-01-06 21:03:14,http://leuschke.org/tillman,0.5081438527,5.145.156.239,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n2524,2,56,2017-04-03 01:09:50,http://senger.name/gennaro_williamson,0.8301891924,15.110.67.28,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n2525,2,56,2017-01-23 09:09:24,http://schinner.name/diamond,0.7062934890,225.249.29.250,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n2526,2,56,2017-01-10 08:54:32,http://rowe.biz/parker,0.5997955306,92.60.162.210,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n2527,2,56,2017-04-30 20:44:03,http://langworth.org/sallie,0.9525906620,49.210.91.197,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n2528,2,56,2016-12-25 12:50:17,http://hansen.com/marcia.willms,0.0808373436,87.230.51.63,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n2529,2,56,2017-02-26 05:48:43,http://spinkaratke.net/damon_armstrong,0.7515305557,145.216.246.154,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n2530,2,56,2017-03-24 16:50:42,http://anderson.net/lora.kunze,0.5269732887,212.224.19.102,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n2531,2,56,2016-12-30 05:54:16,http://moriette.co/reagan,0.5713368906,112.166.165.120,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n2532,2,56,2016-12-29 17:19:31,http://mitchell.co/sienna,0.6557021565,140.127.13.81,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n2533,2,56,2017-05-05 14:52:05,http://wehner.net/bertha_goodwin,0.2720637580,204.81.240.251,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n2534,2,56,2017-03-16 13:16:41,http://rath.com/rachael,0.9533892620,38.42.224.180,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n2535,2,56,2017-01-25 06:50:16,http://ritchie.net/julian,0.5826187889,107.236.142.203,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n2536,2,56,2016-12-25 20:24:19,http://bauch.info/antwan,0.0924447667,204.183.167.167,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n2537,2,56,2017-01-15 20:47:44,http://hackett.org/celestino_bosco,0.3328585183,97.92.146.190,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n2538,2,56,2016-12-29 03:24:27,http://ohara.com/fernando_bogisich,0.2182006607,27.151.60.110,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n2539,2,56,2017-02-10 03:09:17,http://moencrooks.biz/eliza.flatley,0.5927647505,87.176.125.140,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n2540,2,56,2017-02-14 02:06:18,http://block.org/delia.lakin,0.3161621762,234.114.161.249,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n2541,2,56,2017-03-24 20:06:27,http://medhurst.com/guadalupe,0.6531447096,209.205.172.191,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n2542,2,56,2017-02-26 17:45:33,http://walsh.com/sam_reichel,0.5056337570,108.25.72.163,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n2543,2,56,2017-03-12 10:19:41,http://leffler.name/adeline_feeney,0.5546199902,169.163.4.89,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n2544,2,56,2017-02-23 01:24:32,http://pollich.biz/thelma_stoltenberg,0.8758554759,250.74.227.36,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n2545,2,57,2017-01-12 00:03:25,http://bashirian.biz/kane_macejkovic,0.6843203102,136.138.120.247,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n2546,2,57,2017-04-26 17:41:19,http://halvorson.biz/alisha_waelchi,0.2114965996,187.85.2.166,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n2547,2,57,2017-03-01 08:33:46,http://corwin.org/elvie,0.0119174423,124.202.6.207,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n2548,2,57,2017-02-04 04:28:00,http://rowepurdy.org/shaina,0.5256406987,236.119.141.212,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n2549,2,57,2016-12-26 03:08:50,http://bradtke.name/hudson_mcclure,0.7114100948,220.7.222.82,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n2550,2,57,2017-04-12 03:26:03,http://wintheisermorar.net/fatima,0.1141829077,71.206.157.248,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n2551,2,57,2017-05-10 10:42:11,http://kreigerschmeler.net/ava.langworth,0.6146830864,12.6.165.74,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n2552,2,57,2017-04-29 12:50:27,http://bauch.com/jeika.pouros,0.7990823296,219.206.157.41,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n2553,2,57,2017-02-20 02:14:32,http://windlerhettinger.biz/stevie_mueller,0.8656687665,106.121.171.40,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n2554,2,57,2017-06-03 04:26:13,http://blick.net/donnie_baumbach,0.7949682684,212.187.163.5,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n2555,2,57,2017-02-19 02:25:12,http://pourosbauch.com/cayla,0.3666714402,178.183.84.241,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n2556,2,57,2017-06-06 13:33:58,http://trantow.co/uriah,0.9949590361,93.221.169.234,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n2557,2,57,2017-03-15 04:20:55,http://baumbach.biz/minnie.kozey,0.6661542148,165.92.188.6,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n2558,2,57,2017-05-02 18:56:49,http://hagenesbernhard.name/karianne,0.0095368078,251.149.181.216,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n2559,2,57,2017-04-08 12:25:04,http://adams.io/justice,0.3288718439,220.247.64.234,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n2560,2,57,2017-02-11 13:14:06,http://senger.name/major_jones,0.9550624501,5.86.125.201,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n2561,2,57,2017-06-08 04:00:29,http://willms.name/magnolia_bartell,0.3234512572,32.190.229.192,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n2562,2,57,2017-04-07 10:33:08,http://beahan.com/marisol,0.4165447750,140.199.32.176,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n2563,2,57,2017-05-02 02:07:33,http://armstrong.net/peggie,0.8441234924,56.35.72.163,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n2564,2,57,2017-04-29 11:45:44,http://altenwerth.name/rashad,0.2448623564,117.128.207.122,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n2565,2,57,2017-02-11 13:49:13,http://greenholt.name/pinkie.kiehn,0.5522003463,117.167.189.119,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n2566,2,57,2017-04-23 02:42:54,http://johnson.co/tyler.cormier,0.4777561568,65.229.169.90,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n2567,2,57,2017-03-30 07:07:25,http://watsicaconsidine.name/breana,0.5501082580,12.167.241.67,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n2568,2,57,2017-01-26 16:16:54,http://bartell.com/omari.langworth,0.2932564554,125.53.222.178,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n2569,2,57,2017-05-19 12:01:05,http://erdman.io/efrain,0.1168755859,206.68.55.89,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n2570,2,57,2017-03-07 12:46:26,http://bayerhuels.net/scarlett,0.9181115474,143.176.3.11,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n2571,2,57,2016-12-14 03:14:03,http://streich.net/jan_frami,0.7667705961,16.233.198.180,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n9452,4,212,2017-02-26 07:22:21,http://emmerichflatley.info/sallie,,41.31.155.236,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n9453,4,212,2017-04-08 04:10:18,http://langworthstroman.org/lavonne.beier,,210.98.27.97,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n9454,4,212,2016-12-28 02:13:50,http://mclaughlin.info/eladio_dietrich,,219.216.26.193,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n9455,4,212,2017-06-04 10:42:17,http://beerhuels.name/colten,,8.6.85.145,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n9456,4,212,2017-05-01 10:23:55,http://rosenbaum.com/mercedes.gerlach,,8.18.230.45,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n9457,4,212,2017-04-11 11:57:32,http://willms.com/winona.parker,,33.142.251.111,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n9458,4,212,2017-02-24 00:00:10,http://parisian.biz/deondre_klein,,248.90.202.29,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n9459,4,212,2017-02-11 10:41:53,http://marquardt.net/betty_bins,,242.47.112.130,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n9460,4,212,2017-06-11 14:44:20,http://macgyveranderson.com/deven.ziemann,,230.168.186.38,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n9461,4,212,2017-04-09 11:23:20,http://townestreich.biz/raul,,49.110.34.116,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n9462,4,212,2016-12-25 00:09:23,http://weinatbins.net/marjolaine.barrows,,154.161.242.65,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n9463,4,212,2017-02-09 00:58:02,http://paucekkovacek.biz/arlo.brekke,,197.236.77.212,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n9464,4,212,2017-02-13 19:21:32,http://cartwrighthudson.io/scot,,155.209.48.171,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n9465,4,212,2017-04-12 02:37:42,http://swift.co/marcelino,,127.144.83.222,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n9466,4,212,2017-01-09 05:25:55,http://price.co/annette_kertzmann,,248.82.12.48,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n9467,4,212,2017-06-09 08:12:31,http://harvey.io/skyla,,243.209.69.128,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n9468,4,212,2017-05-27 04:30:45,http://ondrickaleannon.com/jalen_torphy,,36.48.213.180,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n9469,4,212,2017-01-13 09:56:27,http://hudson.org/ardella.hintz,,87.132.130.115,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n9470,4,212,2017-03-29 21:28:42,http://pouros.org/constantin,,178.157.33.253,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n9471,4,212,2017-02-25 00:26:36,http://parkerprohaska.info/jasmin_oreilly,,106.200.221.74,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n9472,4,212,2017-05-23 17:18:14,http://witting.io/avis,,67.70.216.64,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n9473,4,212,2017-01-25 00:49:23,http://conroy.org/osbaldo.waters,,216.201.24.159,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n9474,4,212,2017-06-11 23:15:08,http://stark.org/ariane.robel,,209.93.33.141,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n9475,4,212,2017-05-15 15:51:56,http://daugherty.net/antoinette,,41.49.196.32,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n9476,4,212,2017-05-25 17:52:29,http://larkin.net/elouise.carroll,,215.112.38.182,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n9477,4,212,2017-02-12 03:10:23,http://kreigerwalsh.com/gladys,,198.119.95.2,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n9478,4,212,2017-01-12 23:43:33,http://hilpertdamore.com/violet,,89.16.184.158,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n9479,4,212,2016-12-23 05:21:38,http://kerlukebartell.com/jerry,,204.162.126.26,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n9480,4,212,2017-02-04 03:06:15,http://anderson.org/damian,,222.198.143.105,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n9481,4,212,2017-01-14 08:02:19,http://schumm.com/makenna.feil,,183.185.199.193,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n9482,4,212,2017-03-30 19:28:18,http://koepp.org/bianka.langworth,,110.36.180.213,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n9483,4,212,2017-05-10 17:59:12,http://bahringer.co/erica_stracke,,217.211.219.9,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n9484,4,212,2017-01-07 00:33:57,http://champlin.co/wava,,200.213.183.179,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n9485,4,212,2017-05-01 20:48:51,http://donnelly.com/stanford_kreiger,,219.111.118.42,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n9486,4,213,2017-01-01 19:34:46,http://heidenreich.name/giovani,,6.84.139.104,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n9487,4,213,2017-01-29 19:10:06,http://herman.info/rosemary.medhurst,,231.205.60.155,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n9488,4,213,2017-06-07 14:46:48,http://zboncak.biz/jamaal,,37.139.160.114,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n9489,4,213,2017-02-22 07:50:28,http://bauchjacobs.co/larue,,246.53.115.195,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n9490,4,213,2016-12-18 23:49:38,http://turner.biz/jeffry.farrell,,6.179.198.119,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n9491,4,213,2017-05-08 02:09:32,http://muellersawayn.biz/boris_ortiz,,172.10.131.15,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n9492,4,213,2017-04-11 17:49:55,http://aufderhar.name/tyrell_heller,,232.219.155.126,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n9493,4,213,2016-12-29 08:04:26,http://oberbrunnerschaden.info/marina_wuckert,,154.161.115.38,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n9494,4,213,2017-05-18 13:52:44,http://windler.com/curt_gaylord,,9.175.66.252,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n9495,4,213,2017-01-05 10:01:53,http://toy.biz/gudrun_streich,,58.178.48.181,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n9496,4,213,2017-04-13 17:21:24,http://purdy.net/juwan.stehr,,149.206.236.119,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n9497,4,213,2017-03-08 12:21:52,http://framigislason.com/chance.greenholt,,54.127.147.84,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n9498,4,213,2017-03-11 19:32:29,http://schinner.biz/willis.reichel,,20.32.216.62,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n9499,4,213,2017-01-18 20:49:41,http://mcglynn.com/deontae_collins,,202.11.115.198,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n9500,4,213,2017-02-19 00:13:57,http://lebsackluettgen.com/makenzie.gottlieb,,161.51.16.46,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n9501,4,213,2017-03-17 06:39:05,http://cormier.com/orie,,35.195.203.140,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n9502,4,213,2017-01-16 04:05:11,http://feeneybreitenberg.com/cletus.boyer,,244.17.163.200,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n9503,4,213,2017-03-29 10:54:12,http://fadelankunding.org/myrtie.sanford,,23.70.102.210,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n9504,4,213,2017-06-02 00:20:17,http://borer.biz/austin_larson,,207.5.84.31,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n9505,4,213,2017-01-15 20:51:31,http://hagenesrogahn.net/weldon,,196.177.194.63,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n9506,4,213,2017-03-05 06:27:21,http://wyman.net/brian_oconner,,88.109.168.167,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n6462,3,141,2017-03-09 12:47:52,http://rath.org/mitchell_macgyver,0.0095553997,188.90.94.100,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n6463,3,141,2016-12-26 19:06:10,http://fisher.info/jane,0.3421866398,61.19.8.222,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n6464,3,141,2017-04-01 16:18:55,http://eichmann.co/jace.wilkinson,0.0595269944,18.81.110.253,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n6465,3,141,2017-04-15 18:03:50,http://gloverdickinson.com/myah,0.1733263447,13.190.57.32,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n6466,3,141,2017-03-19 23:43:49,http://romaguera.biz/matilda,0.2075737801,21.52.81.158,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n6467,3,141,2017-04-05 23:26:59,http://west.org/alva.langosh,0.8352090228,60.193.83.165,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n6468,3,141,2017-05-17 14:25:04,http://aufderhar.biz/chasity.hintz,0.4904130174,203.136.168.28,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n6469,3,141,2017-04-30 11:40:50,http://langworth.io/lauryn_nikolaus,0.6055070704,122.236.149.196,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n6470,3,141,2017-03-08 14:34:35,http://fritsch.info/celestine_johnston,0.3173265371,45.164.167.192,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n6471,3,141,2016-12-13 16:49:41,http://kreigerfeeney.org/patrick,0.5738068434,186.244.68.68,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n6472,3,141,2017-02-17 16:36:33,http://mann.biz/violet,0.7873676103,106.95.98.186,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n6473,3,141,2017-06-05 20:52:39,http://corwin.name/enos.reynolds,0.4819027215,56.247.139.240,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n6474,3,141,2017-03-21 20:50:41,http://wisozkparker.co/lorena_lueilwitz,0.5594959506,4.82.252.5,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n6475,3,141,2017-01-08 02:13:27,http://ondrickawolf.co/sigurd.collins,0.4235609464,211.248.201.229,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n6476,3,141,2017-05-05 16:07:28,http://lockmanlegros.org/jeremy,0.3236024355,235.174.13.211,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n6477,3,141,2017-02-28 07:19:50,http://rolfson.io/kennith,0.1691977830,230.162.153.96,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n6478,3,141,2017-04-06 14:12:24,http://homenick.biz/lyda.grady,0.4023058753,172.241.137.191,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n6479,3,141,2017-04-16 23:03:05,http://adams.co/olin,0.8211132361,47.16.199.73,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n6480,3,141,2017-03-01 11:21:53,http://adams.org/kitty_rohan,0.4220758821,4.13.2.19,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n6481,3,141,2016-12-22 11:55:17,http://lind.com/rae_wehner,0.8677715908,177.147.69.205,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n6482,3,141,2016-12-27 06:44:02,http://millsmcclure.info/zola_thiel,0.4847160222,248.180.108.194,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n6483,3,141,2017-05-13 01:28:12,http://lang.name/joseph_hansen,0.6752448703,213.246.63.143,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n6484,3,141,2017-02-27 02:51:17,http://considinespinka.com/maude.ullrich,0.1137719418,188.233.22.3,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n6485,3,141,2017-02-15 22:16:34,http://mitchell.co/elta.bayer,0.2850086517,89.55.46.54,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n6486,3,141,2017-05-13 14:04:30,http://hickledavis.info/hollis.dicki,0.2623858482,179.184.163.169,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n6487,3,141,2017-03-25 09:58:49,http://herman.biz/brittany_muller,0.4291851335,247.227.168.35,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n6488,3,141,2017-02-05 15:30:00,http://darebode.org/keara.grimes,0.4131658600,110.252.160.126,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n6489,3,141,2017-02-22 21:35:02,http://schowalter.info/conrad_lemke,0.7694219596,123.19.97.99,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n6490,3,141,2017-02-15 21:24:50,http://predovic.co/bailey,0.2324889811,204.171.34.189,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n6491,3,141,2017-05-12 15:31:30,http://vandervort.io/berry_deckow,0.1739193478,171.7.83.248,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n6492,3,141,2017-04-09 22:06:17,http://kirlinmohr.net/hettie_hagenes,0.9987492282,34.161.17.72,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n6493,3,141,2017-04-27 07:55:01,http://murray.info/katrina.rosenbaum,0.8562649399,176.196.64.168,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n6494,3,141,2017-05-18 09:43:34,http://dare.biz/mona,0.1221599869,194.60.109.242,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n6495,3,141,2017-04-18 17:58:12,http://jenkins.name/finn_kreiger,0.4611103864,170.164.249.104,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n6496,3,141,2017-06-08 12:34:15,http://botsfordquitzon.info/aglae.nikolaus,0.1212544032,148.242.141.89,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n6497,3,141,2017-04-19 14:25:36,http://dickens.name/dedric_marks,0.4090285530,191.108.231.124,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n6498,3,141,2017-06-05 19:36:01,http://jaskolskischiller.name/name_spencer,0.8432471125,233.230.95.45,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n6499,3,141,2017-02-24 03:10:31,http://heaney.io/reva.kunde,0.3502500201,92.63.103.77,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n6500,3,141,2017-02-22 00:52:54,http://shanahangislason.biz/nia_hintz,0.1567743189,23.167.199.102,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n6501,3,141,2017-02-13 02:15:51,http://heller.com/armando_quigley,0.6989783624,231.30.245.113,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n6502,3,141,2017-04-11 23:09:11,http://erdmanstark.org/pablo.ratke,0.1061977172,173.218.9.7,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n6503,3,141,2017-04-30 04:27:44,http://lockmancormier.biz/clovis,0.8674163079,67.88.170.174,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n6504,3,141,2017-05-31 10:11:49,http://vonaltenwerth.info/deja,0.5013648602,241.59.183.181,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n6505,3,142,2016-12-15 23:23:24,http://sporer.biz/talon,0.0545928940,243.93.219.161,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n6506,3,142,2017-01-23 23:02:01,http://yundt.co/kayla.schamberger,0.1530779717,242.58.242.14,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n6507,3,142,2017-03-01 14:16:26,http://johns.io/jesus_wiegand,0.6581167082,13.144.217.154,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n6508,3,142,2017-01-28 16:07:46,http://swift.name/adalberto.boyer,0.8407273584,153.91.130.68,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n6509,3,142,2017-05-16 19:13:55,http://farrell.org/dayne,0.7228775913,126.165.160.59,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n6510,3,142,2017-03-10 00:15:32,http://bayer.net/wilfredo,0.8568567877,117.250.177.204,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n6511,3,142,2017-02-23 15:52:39,http://prosacco.co/hugh,0.4623412759,53.97.212.167,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n6512,3,142,2017-02-22 05:34:06,http://smith.com/bernadine,0.0751614975,201.242.194.117,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n12367,5,277,2017-03-14 10:23:09,http://deckow.info/heber_ward,0.6188413720,53.193.228.191,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n12368,5,277,2016-12-13 16:44:22,http://goldner.net/stephany.cain,0.5797786736,206.29.80.132,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n12369,5,277,2017-01-08 17:57:19,http://mueller.net/richmond_spinka,0.6344151885,62.55.72.138,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n12370,5,277,2017-02-07 06:59:17,http://hillsgraham.com/jensen,0.8250679292,193.120.147.15,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n12371,5,277,2017-04-15 23:29:53,http://gottlieb.biz/benedict_sawayn,0.2179542885,211.131.196.132,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n12372,5,277,2017-04-02 16:33:07,http://cremincrooks.net/myrtis,0.3742482103,44.66.119.63,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n12373,5,277,2017-01-14 14:07:15,http://hillsroob.net/whitney,0.2279878559,42.199.115.226,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n12374,5,277,2017-05-11 05:40:42,http://hellerlabadie.co/tremaine_weimann,0.2458975885,79.193.229.149,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n12375,5,277,2017-06-06 22:44:05,http://ruecker.biz/alvera.okon,0.2035548425,236.124.166.149,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n12376,5,277,2017-03-31 22:57:04,http://ferry.name/trisha_runolfsdottir,0.1658654415,229.51.113.84,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n12377,5,277,2017-04-08 01:55:56,http://murazik.net/carolyn,0.0817002070,146.125.220.155,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n12378,5,277,2017-03-13 09:33:34,http://adams.info/lonnie_kuvalis,0.8794112754,242.19.25.219,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n12379,5,277,2017-04-24 22:24:51,http://leuschke.info/vivienne_howe,0.0215698207,221.116.75.165,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n12380,5,277,2017-04-28 03:26:51,http://wilderman.biz/georgiana_reynolds,0.4427542452,171.223.103.21,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n12381,5,277,2017-05-01 10:20:55,http://marks.info/susana,0.5237403277,130.90.194.202,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n12382,5,277,2017-01-22 15:33:14,http://torphy.org/dallin.lehner,0.5063891223,55.160.84.241,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n12383,5,277,2017-04-26 11:43:07,http://wildermanschoen.biz/delta,0.1625101596,226.54.21.49,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n12384,5,277,2017-05-17 12:39:30,http://bergstrom.name/angel,0.3329893455,206.14.196.180,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n12385,5,277,2017-02-15 02:52:47,http://rogahnmarvin.io/sterling.block,0.7461423786,206.219.70.243,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n12386,5,277,2017-03-05 12:18:01,http://boyermaggio.io/reyna_muller,0.6897209806,209.178.191.2,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n12387,5,277,2017-03-12 09:08:54,http://boylebeatty.org/aliyah,0.0638395781,94.124.145.239,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n12388,5,277,2017-02-25 06:18:51,http://wisoky.org/alverta,0.1786807373,48.54.39.178,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n12389,5,277,2017-02-16 11:39:50,http://sawayn.org/jayden_heidenreich,0.1825154538,95.224.22.18,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n12390,5,277,2017-01-24 10:12:44,http://gottliebzulauf.info/karli,0.4067333206,82.94.109.75,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n12391,5,278,2017-06-01 06:45:18,http://durganleannon.org/casandra,0.6029347181,247.78.66.42,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n12392,5,278,2017-02-06 00:32:17,http://pagacgreenholt.io/hilda_hermiston,0.8725936540,18.53.220.172,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n12393,5,278,2017-01-01 18:10:58,http://jerdedoyle.net/iliana.homenick,0.3992647544,115.72.224.226,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n12394,5,278,2017-03-18 20:04:40,http://harvey.co/zakary_bernier,0.2877629809,23.211.92.100,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n12395,5,278,2017-01-24 22:21:30,http://gerholdturner.net/haven.nikolaus,0.9788648616,246.251.143.42,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n12396,5,278,2016-12-22 00:44:19,http://ward.name/daisha.schultz,0.8518152583,146.10.183.220,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n12397,5,278,2017-01-20 16:41:18,http://schaden.org/emmanuel_okuneva,0.8783758018,122.195.77.72,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n12398,5,278,2017-05-20 13:22:39,http://paucek.biz/rae,0.0850013473,28.34.213.153,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n12399,5,278,2017-03-06 15:31:35,http://vandervort.info/gerald,0.5727095515,182.205.196.250,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n12400,5,278,2017-05-14 12:19:02,http://pagacdibbert.net/jamison,0.2397249686,62.248.48.67,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n12401,5,278,2017-03-30 16:19:36,http://conroykovacek.io/antwon,0.5661140597,49.213.170.169,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n12402,5,278,2016-12-25 23:36:54,http://schmitt.co/ava,0.4114977835,81.251.23.109,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n12403,5,278,2017-02-15 05:50:21,http://rodriguez.info/reed,0.6145236708,65.40.27.7,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n12404,5,278,2017-05-20 08:30:22,http://stokes.io/claudine_blick,0.4919722412,175.159.169.159,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n12405,5,278,2016-12-19 21:15:36,http://wolf.io/daniela.hoeger,0.9437493702,109.109.135.182,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n12406,5,278,2017-03-10 18:51:21,http://collier.biz/tommie,0.1773666446,97.181.46.245,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n12407,5,278,2016-12-21 03:20:05,http://schaden.biz/savannah_herman,0.2686811719,21.110.79.39,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n12408,5,278,2017-01-23 15:28:00,http://krajcik.info/delta.becker,0.9050136013,95.26.152.2,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n12409,5,278,2017-03-23 06:06:09,http://shanahan.net/lillian_sanford,0.6091156172,113.135.190.237,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n12410,5,278,2017-02-23 08:37:39,http://lebsack.name/felipe,0.6284852772,175.159.20.77,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n12411,5,278,2017-02-28 21:28:22,http://gleason.net/isac_cremin,0.5171061003,250.87.116.220,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n12412,5,278,2016-12-27 14:18:18,http://wardcorkery.io/connor,0.3560251774,12.86.209.203,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n12413,5,278,2017-03-30 14:53:48,http://blanda.biz/beaulah,0.1012625911,121.230.19.226,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n12414,5,278,2017-02-12 03:14:35,http://langosh.co/garry,0.4003607200,158.94.206.14,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n12415,5,278,2017-02-16 07:10:05,http://brakuserdman.net/ashly,0.5833876146,117.169.141.12,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n12416,5,278,2017-02-25 01:54:32,http://brakus.co/zakary_runte,0.8340396410,63.104.137.168,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n12417,5,278,2017-02-10 13:41:13,http://okon.name/lorenz_abshire,0.2329338354,3.190.88.38,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n15380,6,346,2016-12-25 04:20:01,http://effertz.co/german.marks,0.2075568221,107.179.169.168,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n15381,6,346,2017-04-23 20:21:15,http://price.com/rodrick_ferry,0.1066343061,70.188.3.186,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n15382,6,346,2017-01-24 03:05:16,http://cummings.name/ana,0.0518414579,24.52.93.245,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n15383,6,346,2017-04-28 17:59:03,http://kunde.co/taya_tromp,0.7840980486,91.184.177.129,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n15384,6,346,2016-12-26 14:19:21,http://corkeryhalvorson.info/malcolm,0.8465100123,131.91.84.47,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n15385,6,346,2017-05-31 12:52:08,http://whiteveum.net/romaine,0.4042632890,3.198.195.203,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n15386,6,346,2017-04-07 15:09:50,http://grimeshomenick.info/antone,0.4636847374,114.180.188.68,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n15387,6,346,2017-02-27 12:08:54,http://handledner.co/lucy_gislason,0.3377969347,4.202.27.246,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n15388,6,346,2017-06-05 06:15:59,http://botsford.org/ericka,0.7114482597,11.193.148.164,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n15389,6,346,2016-12-29 00:45:32,http://legros.com/daisy.quitzon,0.7964926146,76.127.124.83,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n15390,6,346,2017-04-04 06:33:49,http://rohan.biz/clifford.feeney,0.2549817971,241.194.72.86,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n15391,6,346,2017-02-06 21:02:53,http://gorczany.io/tod.jacobi,0.3015180241,138.112.16.199,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n15392,6,346,2017-04-18 14:55:45,http://parker.net/magnolia.frami,0.7522752114,122.67.223.56,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n15393,6,346,2017-01-31 03:45:02,http://haagharber.org/kaleigh.wolff,0.4615015731,118.4.15.119,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n15394,6,346,2016-12-20 12:08:26,http://nikolaus.co/efrain,0.1885623482,228.95.40.62,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n15395,6,347,2017-06-01 23:53:29,http://baumbach.org/kaylie.nienow,0.9524400845,12.176.96.249,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n15396,6,347,2017-02-24 13:16:39,http://kihntrantow.com/aileen,0.5500079118,45.236.44.173,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n15397,6,347,2017-02-09 17:14:01,http://muller.net/ava,0.4314798103,20.114.23.242,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n15615,6,351,2017-05-24 03:07:10,http://conn.com/vivianne,,219.90.248.254,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n15398,6,347,2017-05-09 12:38:02,http://dickinson.net/macey_okuneva,0.4434698849,244.124.233.31,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n15399,6,347,2017-03-12 14:06:42,http://zboncak.com/giles.emmerich,0.3320638596,26.171.242.147,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n15400,6,347,2017-05-06 03:03:28,http://torphy.net/orlando,0.0574609239,190.88.184.70,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n15401,6,347,2017-04-12 22:28:20,http://strosinwisoky.info/zora.purdy,0.8903193009,105.73.95.10,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n15402,6,347,2017-05-08 07:15:35,http://pouroshudson.info/jerad,0.8666881874,220.212.55.80,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n15403,6,347,2017-01-25 00:02:29,http://carrolllebsack.org/mauricio,0.8899328170,5.16.207.251,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n15404,6,347,2017-05-01 17:22:54,http://homenickabshire.com/allison.king,0.5856855933,202.58.25.39,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n15405,6,347,2016-12-28 16:15:53,http://tillman.biz/andreanne.jacobi,0.2250158381,148.100.243.197,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n15406,6,347,2017-03-09 23:51:11,http://mraz.name/alison,0.6570918192,33.90.203.88,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n15407,6,347,2017-04-06 04:28:06,http://mosciskihuels.com/dustin,0.4694390979,176.209.195.131,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n15408,6,347,2017-03-05 23:15:38,http://haley.net/bria_terry,0.8606799023,212.253.194.103,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n15409,6,347,2017-03-24 22:17:37,http://wymanemard.com/jerod,0.2646811268,10.27.169.36,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n15410,6,347,2017-01-09 13:44:57,http://thiel.io/deven,0.3180440044,134.253.98.147,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n15411,6,347,2017-04-10 03:06:28,http://abernathy.io/cordie,0.1369883552,148.195.108.189,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n15412,6,347,2017-01-20 03:43:44,http://muller.co/marietta,0.8754454408,230.205.237.179,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n15413,6,347,2017-03-16 14:11:51,http://swaniawski.org/birdie,0.9140335871,233.146.192.57,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n15414,6,347,2017-03-31 04:07:28,http://bosco.co/brianne.davis,0.6841297308,105.224.210.61,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n15415,6,347,2017-03-04 18:37:22,http://willms.biz/demetrius_king,0.7694036109,212.169.57.211,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n15416,6,347,2017-06-01 04:37:01,http://feestvolkman.io/carlos,0.3082332581,159.74.226.187,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n15417,6,347,2017-05-13 16:18:48,http://trantow.name/alta,0.8011993262,163.166.35.110,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n15418,6,347,2017-03-01 09:52:55,http://dickinson.co/jazmyn.senger,0.6873833191,223.138.15.52,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n15419,6,347,2017-02-02 22:59:50,http://oconnell.name/patsy_dooley,0.1413283964,159.24.11.245,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n15420,6,347,2017-04-28 11:46:31,http://watsica.org/jordy,0.2280335048,181.212.201.113,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n15421,6,347,2016-12-13 14:51:57,http://weimannschulist.io/brayan.turner,0.6103585661,76.117.206.129,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n15422,6,347,2017-05-30 01:28:17,http://koelpin.co/harley,0.0418357533,17.192.163.141,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n15423,6,347,2017-02-08 18:24:42,http://braunmckenzie.com/augustine.turner,0.8849326377,186.202.48.224,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n15424,6,347,2017-05-13 00:19:35,http://von.org/duncan_mertz,0.8476366035,225.85.96.113,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n15425,6,347,2017-06-08 04:16:07,http://kozey.name/eden,0.4933382473,34.101.122.97,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n15426,6,347,2017-05-31 18:59:38,http://leannon.io/ernesto,0.9114987561,97.74.53.3,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n15427,6,347,2016-12-28 04:57:06,http://kaulke.io/deshawn,0.6611986901,73.141.243.249,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n15428,6,347,2017-01-17 16:34:49,http://brakus.net/luigi_morar,0.8426907128,55.234.99.10,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n15429,6,347,2017-02-03 22:40:46,http://lockmanlakin.io/mohammed,0.8712739595,130.18.238.123,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n15430,6,347,2017-02-02 11:22:47,http://rutherford.org/barton,0.9585057088,125.161.205.69,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n2572,2,57,2017-04-05 00:59:48,http://lakinwiegand.name/teie,0.5224903377,190.233.15.235,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n2573,2,57,2017-01-05 16:48:10,http://west.com/torrance.moore,0.6885023240,217.137.34.209,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n2574,2,57,2017-04-22 03:22:11,http://watsicahane.io/agnes.beier,0.4387159604,171.78.150.213,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n2575,2,57,2017-05-30 08:23:57,http://hegmann.co/jakayla,0.8247869480,181.60.199.93,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n2576,2,57,2017-05-19 17:06:53,http://wilderman.com/jayce,0.5582098526,126.212.145.230,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n2577,2,57,2017-04-15 05:57:16,http://volkman.net/kenton,0.2564190876,113.197.235.122,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n2578,2,57,2017-06-04 19:34:08,http://durgan.co/monte.stanton,0.8444813335,32.84.103.142,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n2579,2,57,2017-01-26 19:34:01,http://boehmgaylord.biz/kaandra_prosacco,0.8554819541,241.238.39.110,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n2580,2,57,2017-02-10 20:31:43,http://macejkovic.net/jairo_franecki,0.4007724653,31.214.162.229,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n2581,2,57,2017-02-04 05:38:47,http://feiljohns.com/skye.steuber,0.8868847022,209.205.21.214,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n2582,2,58,2017-01-25 10:50:05,http://schaden.co/allie_quitzon,,94.156.106.213,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n2583,2,58,2017-02-20 05:39:29,http://cronakunde.net/alva.willms,,179.33.220.180,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n2584,2,58,2017-05-09 09:23:30,http://bogisich.biz/amalia,,246.161.45.49,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n2585,2,58,2017-05-26 12:01:07,http://blanda.co/brice,,214.146.125.106,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n2586,2,58,2017-04-04 04:34:38,http://medhurstkeebler.name/stella,,167.246.217.149,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n2587,2,58,2017-03-20 07:07:28,http://hermiston.biz/asa.kautzer,,196.204.157.4,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n2588,2,58,2017-05-11 15:17:24,http://ferry.biz/alvina,,226.53.232.85,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n2589,2,58,2017-01-07 04:51:05,http://mayerupton.co/maynard_legros,,144.144.178.215,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n2590,2,58,2017-04-30 09:44:48,http://flatley.biz/fae,,199.37.24.78,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n2591,2,58,2017-02-17 15:09:15,http://nitzsche.org/randal,,22.140.173.127,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n2592,2,58,2016-12-22 17:06:07,http://olson.co/eliane,,64.5.129.44,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n2593,2,58,2017-05-05 15:01:12,http://kovacektorphy.com/yasmeen,,245.101.143.202,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n2594,2,58,2017-03-15 01:56:26,http://mosciski.co/brenden_ohara,,169.71.77.27,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n2595,2,58,2016-12-31 01:52:50,http://volkman.com/keara.frami,,229.96.209.147,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n2596,2,58,2017-02-04 01:26:34,http://dicki.co/deron,,12.27.205.41,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n2597,2,58,2017-05-01 20:05:27,http://kelerquigley.com/vernon,,101.135.11.241,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n2598,2,58,2016-12-22 17:38:50,http://koepp.name/robyn,,184.197.231.99,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n2599,2,58,2017-05-18 10:40:57,http://west.biz/nikki_schmeler,,86.52.144.33,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n2600,2,58,2017-02-12 02:52:03,http://rempel.org/jovani.wiza,,160.151.161.122,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n2601,2,58,2017-03-17 07:54:51,http://carter.com/reese_casper,,84.16.233.225,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n2602,2,58,2017-06-01 01:57:20,http://larsonprosacco.com/josefina,,175.227.49.187,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n2603,2,58,2017-05-29 11:33:56,http://smith.com/andreane,,16.42.179.107,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n2604,2,58,2017-06-13 14:22:09,http://satterfield.net/arno.tremblay,,77.167.238.156,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n2605,2,58,2017-01-27 19:33:48,http://hayes.biz/clemens,,165.54.181.100,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n2606,2,58,2017-01-02 03:59:25,http://mclaughlin.io/hailee,,10.31.92.156,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n2607,2,58,2017-01-28 19:04:52,http://bergstromjast.info/tristin,,112.66.129.2,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n2608,2,58,2017-02-07 09:21:03,http://terry.net/orpha.bartoletti,,105.20.204.115,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n2609,2,58,2017-01-05 13:53:57,http://robel.biz/alia,,142.56.92.238,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n2610,2,58,2017-05-07 05:38:30,http://emmerich.com/keon,,168.251.213.116,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n2611,2,58,2017-02-15 21:56:27,http://mitchellbergstrom.org/twila,,229.194.39.21,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n2612,2,58,2017-02-12 20:33:33,http://fahey.io/lexus_hammes,,219.44.50.195,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n2613,2,58,2017-03-21 14:23:34,http://lowe.com/mittie,,245.106.12.211,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n2614,2,58,2017-05-03 13:00:57,http://lindgrenabernathy.net/lowell,,125.235.41.194,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n2615,2,58,2017-02-22 16:33:13,http://kuphalcremin.org/dahlia,,225.10.238.111,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n2616,2,58,2017-01-05 10:29:14,http://thompsonabshire.org/harmon,,10.225.39.38,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n2617,2,58,2017-05-23 00:38:36,http://parisian.org/jana,,158.181.23.159,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n2618,2,58,2017-02-27 12:28:42,http://kovacekschumm.net/vicente.hermiston,,206.217.158.24,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n2619,2,58,2017-05-14 08:35:41,http://medhurst.com/dalton,,163.236.146.112,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n2620,2,58,2017-03-06 04:28:28,http://swaniawskidach.info/velma,,111.132.22.114,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n2621,2,58,2017-06-12 03:26:50,http://bailey.io/ewell_king,,86.102.228.198,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n2622,2,58,2017-02-15 23:09:44,http://yundt.name/pedro.renner,,119.171.110.112,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n2623,2,58,2017-05-20 12:57:19,http://daugherty.biz/karine_flatley,,155.12.135.97,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n2624,2,58,2017-03-06 09:21:42,http://paucekarmstrong.org/dario,,118.86.227.27,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n2625,2,58,2017-04-26 05:53:45,http://kuhlman.com/leonel,,39.51.35.245,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n2626,2,58,2017-02-16 23:24:56,http://jacobi.co/kay,,200.45.206.98,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n2627,2,58,2017-01-29 07:27:22,http://mertz.io/wallace.abbott,,99.137.36.65,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n9507,4,213,2017-01-05 20:45:59,http://dubuquehickle.net/willy,,211.41.38.98,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n9508,4,213,2017-01-30 02:58:01,http://ziemann.co/alan,,7.77.19.202,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n9509,4,213,2017-03-09 21:19:07,http://douglasgusikowski.com/jaylon,,48.87.204.37,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n9510,4,213,2017-01-04 14:14:09,http://gorczany.info/dedric_schuppe,,246.179.239.249,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n9511,4,213,2017-02-20 00:44:46,http://hilpert.name/krystel_jakubowski,,128.48.113.116,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n9512,4,213,2017-02-12 06:48:26,http://rosenbaumhamill.net/ocie_nolan,,55.252.89.11,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n9513,4,213,2017-04-13 09:46:15,http://orn.info/mose,,140.50.8.76,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n9514,4,213,2017-03-05 02:42:34,http://oconner.info/ismael,,66.63.18.222,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n9515,4,213,2017-02-06 12:25:12,http://lednercormier.info/melisa,,121.130.223.77,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n9516,4,213,2017-04-12 17:33:22,http://herzogreinger.com/curtis_oconnell,,156.112.44.165,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n9517,4,213,2017-01-29 16:06:31,http://spinka.org/arne.smitham,,139.9.164.202,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n9518,4,213,2017-03-12 02:21:29,http://aufderhar.io/jake.mckenzie,,206.12.154.142,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n9519,4,213,2017-03-21 21:12:27,http://predoviccain.net/cara.hansen,,118.64.29.194,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n9520,4,213,2017-03-01 15:21:27,http://kling.co/maverick,,248.108.26.68,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n9521,4,213,2017-03-06 15:51:28,http://powlowskistracke.co/isadore.skiles,,109.175.7.48,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n9522,4,213,2017-06-03 05:22:13,http://gleason.name/julien,,181.112.160.171,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n9523,4,213,2017-01-05 01:30:36,http://daugherty.io/dixie,,242.87.102.216,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n9524,4,213,2017-03-19 10:33:44,http://connellymcclure.co/antonetta,,159.187.164.66,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n9525,4,213,2017-05-27 12:46:21,http://raynor.com/thalia,,149.21.148.172,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n9526,4,213,2017-06-12 08:17:34,http://kovacekhagenes.org/gwen,,30.175.12.78,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n9527,4,213,2016-12-20 07:49:08,http://bogan.co/mozelle,,20.165.76.72,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n9528,4,213,2017-05-14 11:05:16,http://simonitroman.io/jeica,,23.127.42.184,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n9529,4,213,2017-03-29 20:18:29,http://corwin.biz/alanna_wolff,,33.59.24.71,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n9530,4,213,2017-06-11 17:27:27,http://jacobsonbins.name/allene,,11.145.79.39,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n9531,4,213,2017-02-18 00:37:08,http://yost.org/brook_white,,230.10.176.179,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n9532,4,213,2016-12-21 08:59:34,http://veum.io/theodore,,173.155.252.67,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n9533,4,213,2017-01-27 10:00:29,http://walsh.name/benny,,238.176.122.110,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n9534,4,213,2017-03-08 19:48:31,http://okunevadeckow.io/ophelia_beer,,250.214.253.157,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n9535,4,213,2017-04-16 15:22:18,http://rempel.name/mozelle_upton,,127.61.189.108,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n9536,4,213,2017-03-29 15:17:50,http://osinskikeeling.info/jorge,,226.235.226.174,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n9537,4,213,2016-12-19 02:05:01,http://sanford.net/brandon,,240.87.27.101,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n9538,4,213,2017-05-02 13:39:06,http://schuster.io/taylor.rohan,,69.74.90.23,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n9539,4,213,2017-06-10 03:15:09,http://wintheiser.com/leann.crooks,,39.68.228.176,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n9540,4,214,2017-05-25 15:06:03,http://bergstrom.org/armando,,31.237.61.15,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n9541,4,214,2017-05-06 18:40:16,http://balistreri.biz/alexandrea.armstrong,,38.203.43.22,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n9542,4,214,2017-01-24 18:27:39,http://mueller.com/miracle.robel,,138.10.143.15,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n9543,4,214,2017-01-08 07:43:37,http://dickens.biz/abby.hoppe,,75.98.219.76,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n9544,4,214,2017-04-07 05:50:13,http://gusikowski.io/cheyenne.lakin,,129.47.189.48,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n9545,4,214,2016-12-24 04:38:36,http://brakus.com/zelma_bernier,,122.90.127.171,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n9546,4,214,2016-12-13 14:04:05,http://legrosconroy.biz/darrick.anderson,,14.110.53.57,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n9547,4,214,2017-04-12 22:41:10,http://considinehirthe.name/royce,,87.137.71.80,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n9548,4,214,2017-06-04 10:38:53,http://hyattcrist.net/larue.bruen,,221.170.106.83,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n9550,4,214,2017-05-18 00:10:10,http://brown.info/major.ebert,,29.238.195.29,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n9551,4,214,2017-05-18 21:01:35,http://strosin.com/muhammad,,247.223.105.55,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n9552,4,214,2017-03-20 11:10:14,http://boylehansen.name/morris,,29.207.133.27,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n9553,4,214,2017-05-03 18:42:52,http://carrollwuckert.co/astrid.hagenes,,189.181.113.169,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n9554,4,214,2017-04-15 02:24:48,http://zboncak.co/marjory,,51.29.209.47,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n9555,4,214,2017-02-05 13:03:00,http://gleasonschuster.net/vernon,,239.73.183.60,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n9556,4,214,2017-05-22 20:34:41,http://walter.org/deangelo,,175.139.158.28,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n9557,4,214,2017-06-09 20:35:44,http://caspertreutel.name/lula_rau,,200.54.2.39,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n9558,4,214,2017-06-13 20:08:42,http://kubhaley.biz/merritt_harris,,252.47.76.166,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n9559,4,214,2017-04-26 17:23:38,http://parisian.net/erin,,147.39.230.217,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n9560,4,214,2017-03-18 15:37:26,http://baumbach.biz/treva,,238.175.158.187,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n9561,4,214,2017-06-09 04:45:56,http://conroy.org/mattie,,116.61.181.68,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n9562,4,214,2017-06-08 16:53:17,http://fay.org/solon.welch,,68.56.104.2,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n9563,4,214,2017-05-11 17:13:12,http://murazik.biz/tamara.connelly,,243.160.250.138,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n6513,3,142,2017-04-09 16:23:15,http://greenreilly.name/nicola_bogan,0.9800229940,206.113.84.254,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n6514,3,142,2017-01-01 01:27:19,http://considine.org/randi_bogan,0.0272615037,67.47.44.98,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n6515,3,142,2017-01-08 10:56:07,http://stoltenberg.com/reie,0.4633431105,116.78.115.248,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n6516,3,142,2017-02-06 21:49:23,http://pacocha.org/brandy.harvey,0.7075524987,202.199.203.119,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n6517,3,142,2017-03-09 08:46:27,http://braun.com/darrion.gerlach,0.8931621040,120.246.124.59,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n6518,3,142,2017-03-06 14:21:52,http://macejkovicparker.biz/boris.luettgen,0.8714782226,250.158.35.221,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n6519,3,142,2017-01-27 05:14:15,http://swaniawski.biz/efrain_leffler,0.1795823392,68.211.166.220,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n6520,3,142,2017-01-30 11:13:39,http://nader.org/cleora,0.2562371143,199.175.252.98,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n6521,3,142,2017-04-27 00:54:33,http://jacobson.biz/hal_stark,0.3378514436,144.172.144.38,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n6522,3,142,2016-12-22 01:55:14,http://boylewest.co/penelope_crooks,0.9434379410,210.115.211.192,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n6523,3,142,2017-05-08 02:58:25,http://breitenbergauer.net/evie.klein,0.1019453860,90.3.125.9,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n6524,3,142,2017-03-28 11:40:16,http://emmerich.org/oral.bernhard,0.5372194847,67.202.84.251,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n6525,3,142,2017-02-15 14:52:04,http://yundt.net/ivah,0.3488151095,47.66.119.45,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n6526,3,142,2017-04-18 16:18:43,http://runolfsdottirvandervort.io/marlon,0.3527407710,231.194.119.146,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n6527,3,142,2017-03-03 17:07:03,http://wisoky.com/pattie,0.2138910501,199.143.239.74,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n6528,3,142,2016-12-28 04:18:56,http://langosh.biz/ashleigh,0.4420210850,190.232.3.154,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n6529,3,142,2016-12-14 11:01:51,http://framibechtelar.org/jaylan,0.7946070682,43.33.161.220,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n6530,3,142,2017-04-03 01:46:01,http://schuster.name/beth.paucek,0.8305308010,35.35.198.93,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n6531,3,142,2016-12-14 15:07:11,http://bauch.net/bryce,0.2291212002,136.32.52.88,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n6532,3,142,2017-02-11 23:14:59,http://schroeder.biz/destany.hand,0.8156219987,139.129.88.156,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n6533,3,142,2017-01-23 08:37:14,http://purdy.net/johnathon.kautzer,0.5492407052,149.106.233.227,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n6534,3,142,2017-01-06 01:41:16,http://sengerthiel.name/patricia,0.8300098828,57.186.67.143,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n6535,3,142,2017-04-05 01:31:34,http://labadie.co/alfredo,0.0948036718,155.159.125.123,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n6536,3,142,2017-01-13 06:20:47,http://parisian.biz/berenice.price,0.0054255541,5.179.225.204,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n6537,3,142,2017-05-01 19:15:46,http://barrows.info/maybell_wisozk,0.3841912727,27.240.143.70,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n6538,3,142,2017-03-22 14:06:53,http://schuppe.biz/ryley,0.7006116772,155.174.69.232,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n6539,3,142,2017-02-25 09:32:30,http://walsh.biz/green,0.0783315551,119.120.148.227,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n6540,3,142,2017-04-24 15:53:06,http://grimes.co/madelyn,0.0572643655,138.62.115.78,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n6541,3,142,2016-12-23 04:02:03,http://cole.name/clare_bednar,0.6269816588,134.72.108.216,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n6542,3,142,2017-04-13 08:18:03,http://schoen.co/sadie,0.2992819606,194.212.13.105,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n6543,3,142,2017-04-04 08:10:29,http://gislasonmacgyver.com/felix,0.8682777494,109.158.172.173,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n6544,3,142,2016-12-30 05:41:07,http://ortiz.biz/vance,0.3302972464,79.158.210.228,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n6545,3,142,2016-12-23 23:59:08,http://satterfieldgusikowski.info/carole.lehner,0.8281299488,45.56.157.240,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n6546,3,142,2017-03-14 19:02:23,http://romaguera.com/marques.howell,0.9602289271,142.166.63.24,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n6547,3,142,2017-06-08 06:31:15,http://aufderhar.org/franz_rolfson,0.0221862535,157.78.5.179,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n6548,3,142,2016-12-23 06:03:20,http://kuhic.com/hope,0.7752699546,143.142.11.192,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n6549,3,142,2016-12-23 21:18:56,http://jacobson.com/noemie_medhurst,0.7916418967,210.167.202.35,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n6550,3,142,2017-03-20 00:07:15,http://waelchiklocko.name/willard,0.7811822657,155.225.113.27,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n6551,3,142,2017-05-09 15:07:50,http://beatty.name/charley_hartmann,0.3814397295,98.204.2.208,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n6552,3,142,2017-02-12 11:04:14,http://walsh.org/rachael,0.4716689416,86.156.199.20,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n6553,3,142,2016-12-28 21:36:42,http://stokes.com/napoleon,0.2396320103,203.10.249.16,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n6554,3,142,2017-05-14 01:33:28,http://hudsonbeier.org/gregoria.cronin,0.7430821017,97.93.151.51,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n6555,3,142,2017-02-22 21:33:28,http://stracke.com/lydia,0.2831647543,223.116.24.71,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n6556,3,142,2017-05-07 19:41:01,http://welch.org/israel.auer,0.9938440036,14.226.22.154,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n6557,3,142,2017-01-23 05:05:47,http://hyatt.io/haylee,0.1815453485,243.16.29.75,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n6558,3,142,2017-01-18 04:11:55,http://wiegand.io/marcelo,0.7679505077,214.197.190.12,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n6559,3,142,2017-03-25 03:32:31,http://thiel.io/estefania,0.2516111779,173.141.239.77,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n6560,3,142,2017-01-27 13:28:33,http://wildermanabbott.com/braeden_orn,0.0974296592,227.177.74.52,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n6561,3,142,2017-02-10 14:51:01,http://bernierrippin.io/eliezer,0.9832251748,90.9.167.50,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n6562,3,142,2017-02-28 02:20:37,http://cummingsrenner.co/carlie,0.6679053674,40.35.71.121,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n6563,3,142,2017-05-13 19:25:26,http://mcclure.com/cleo,0.3370384441,127.66.228.142,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n12418,5,278,2017-04-16 20:40:24,http://keebler.biz/baylee.glover,0.2350792117,79.211.13.90,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n12419,5,278,2017-04-03 00:41:49,http://zulauf.io/silas.shields,0.6827203720,200.149.28.132,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n12420,5,278,2017-05-18 18:11:52,http://runtejohnston.com/kitty.bechtelar,0.4103684471,104.27.155.13,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n12421,5,278,2016-12-22 09:26:46,http://stehrwitting.co/ottilie_jenkins,0.5364121831,177.56.220.222,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n12422,5,278,2017-02-25 22:36:24,http://cainrodriguez.info/carmen.gorczany,0.4315975547,12.37.184.178,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n12423,5,278,2017-04-16 04:58:56,http://bauch.io/merlin_spinka,0.9014341070,252.252.243.125,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n12424,5,278,2017-01-12 06:39:35,http://wilkinsonhaag.net/yazmin,0.5812948276,131.65.86.101,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n12425,5,278,2017-04-05 15:04:44,http://trantow.co/brooks,0.5032742786,14.38.170.5,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n12426,5,278,2017-03-31 00:13:48,http://klein.net/sylvan_bosco,0.9513140021,90.159.215.114,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n12427,5,279,2017-01-07 05:23:54,http://schumm.co/judy.watsica,,9.33.219.40,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n12428,5,279,2017-05-10 23:56:58,http://murray.co/charlie,,173.41.54.59,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n12429,5,279,2017-01-21 12:00:00,http://zulaufchristiansen.biz/angelita,,26.62.188.134,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n12430,5,279,2017-04-08 09:29:16,http://ohara.biz/savanah,,96.135.96.100,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n12431,5,279,2017-04-16 22:32:00,http://stiedemannherzog.com/alexandro.stark,,82.91.227.158,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n12432,5,279,2017-06-05 07:08:26,http://stiedemannsenger.name/madonna_murphy,,71.198.120.188,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n12433,5,279,2017-05-29 09:17:41,http://rutherford.name/moshe.wuckert,,254.35.161.238,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n12434,5,279,2017-03-29 21:30:15,http://lind.info/alford_krajcik,,54.2.152.160,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n12435,5,279,2017-05-01 20:18:25,http://larkinlesch.org/natalia,,18.67.167.8,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n12436,5,279,2017-03-13 06:01:30,http://pourosharvey.biz/carlos_hoppe,,28.4.164.209,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n12437,5,279,2017-04-09 14:52:41,http://casper.net/torrey,,203.90.224.57,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n12438,5,279,2017-03-15 02:43:43,http://weber.info/jamal_carroll,,172.108.115.199,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n12439,5,279,2016-12-20 13:20:48,http://harris.com/lucas.ritchie,,4.148.103.230,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n12440,5,279,2017-05-14 18:22:35,http://hane.name/eva,,224.181.199.226,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n12441,5,279,2016-12-27 02:06:40,http://connellyfeeney.info/emma_durgan,,131.242.134.225,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n12442,5,279,2017-05-14 02:22:06,http://altenwerth.io/oda,,177.82.213.141,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n12443,5,279,2017-02-11 10:20:27,http://zieme.name/norris,,157.94.107.30,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n12444,5,279,2017-06-10 11:54:35,http://macejkovickeebler.co/keely,,230.56.107.187,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n12445,5,279,2017-03-26 03:28:30,http://lebsack.name/saul,,176.8.46.228,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n12446,5,279,2017-05-25 01:26:04,http://hayes.info/kay,,187.24.52.167,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n12447,5,279,2017-04-04 01:04:13,http://damore.name/sarah.volkman,,222.65.138.203,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n12448,5,279,2017-03-27 01:01:56,http://schaefer.org/lucius_rempel,,237.90.53.241,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n12449,5,279,2017-03-04 15:30:38,http://ankunding.biz/una.schowalter,,155.46.102.132,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n12450,5,279,2017-01-24 04:45:08,http://weimann.info/murray_schmeler,,181.217.2.242,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n12451,5,279,2017-04-30 02:53:07,http://okon.name/maddison_sipes,,92.54.245.36,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n12452,5,279,2017-06-14 00:28:54,http://volkman.net/clair,,69.52.82.86,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n12453,5,279,2017-02-08 19:19:34,http://hartmannkozey.net/bridgette,,195.115.230.30,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n12454,5,279,2016-12-19 11:25:53,http://kleinlabadie.biz/eric,,176.68.147.103,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n12455,5,279,2017-04-28 23:31:17,http://stroman.com/alva.bailey,,154.180.216.50,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n12456,5,279,2017-01-04 22:23:13,http://gleichner.io/vivianne.casper,,163.69.130.238,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n12457,5,279,2016-12-24 04:51:36,http://jacobson.name/dedrick,,60.98.123.200,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n12458,5,279,2017-03-11 04:08:31,http://kemmerondricka.io/eldora.prosacco,,243.163.30.42,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n12459,5,279,2017-02-11 18:46:04,http://cain.name/matilda,,82.195.168.12,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n12460,5,279,2017-02-27 05:53:17,http://auervolkman.com/eli_goyette,,188.124.17.148,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n12461,5,279,2017-03-12 08:30:41,http://hayesmitchell.info/aleia,,155.109.80.41,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n12462,5,279,2017-05-30 13:51:56,http://block.co/eriberto_grady,,174.235.115.207,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n12463,5,279,2017-01-22 08:27:23,http://kuhicpadberg.co/claudia,,209.38.236.44,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n12464,5,279,2017-03-12 08:26:11,http://hand.name/fay.schneider,,39.14.168.159,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n12465,5,279,2017-03-27 14:29:02,http://leffler.org/katelin_erdman,,222.184.105.59,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n12466,5,279,2017-05-20 06:06:49,http://koepp.biz/elouise,,195.65.170.162,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n12467,5,279,2017-03-19 10:35:57,http://reichel.info/keegan.gorczany,,204.128.195.47,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n12468,5,279,2017-04-07 15:11:06,http://lindgrenkoepp.co/deon,,210.69.115.23,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n12469,5,279,2017-03-08 09:41:06,http://davis.biz/nestor.homenick,,99.72.147.242,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n12470,5,279,2017-01-25 08:14:53,http://erdmanbeier.name/delores,,153.244.128.33,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n12471,5,279,2017-05-01 00:21:55,http://greenholtdenesik.com/jayson,,142.130.41.209,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n15431,6,347,2017-04-19 03:52:41,http://beahanbechtelar.name/amelie,0.1752103028,48.36.85.217,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n15432,6,347,2017-05-31 02:35:54,http://rice.net/earline,0.8706524028,96.202.9.248,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n15433,6,347,2017-06-05 03:02:55,http://bergnaumohara.io/madelyn,0.6730532335,41.92.198.26,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n15434,6,347,2017-03-28 17:01:02,http://reichert.name/cornell,0.9219355011,28.162.137.55,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n15435,6,347,2016-12-31 18:48:27,http://collinszboncak.name/maximilian,0.4653461043,77.35.71.171,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n15436,6,348,2017-05-31 19:29:35,http://gerhold.net/freddie_bednar,0.2338442227,211.15.109.232,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n15437,6,348,2017-05-01 06:23:15,http://crooks.biz/korey.reinger,0.0041134345,127.93.32.243,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n15438,6,348,2017-05-08 23:11:51,http://hamill.io/dana_ritchie,0.5323271545,46.130.163.103,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n15439,6,348,2017-03-01 21:03:35,http://colethompson.io/oceane_kub,0.7161216476,95.72.112.81,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n15440,6,348,2017-04-05 01:21:55,http://macejkovic.net/milford.runte,0.1053956183,142.106.95.24,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n15441,6,348,2017-03-23 22:31:59,http://kihn.io/heber_bradtke,0.7954978758,61.9.193.197,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n15442,6,348,2017-04-08 12:34:53,http://spencer.biz/oran,0.5587005181,97.132.101.108,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n15443,6,348,2017-02-22 12:27:41,http://hintz.io/brendan,0.3890339005,13.6.8.201,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n15444,6,348,2017-04-08 02:54:32,http://schneider.org/lucie,0.1802019941,13.121.53.84,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n15445,6,348,2017-05-27 20:01:21,http://kirlinwaelchi.com/dovie.altenwerth,0.5754985492,16.204.65.86,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n15446,6,348,2017-01-18 06:24:13,http://jerde.biz/briana,0.0192508262,117.109.23.227,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n15447,6,348,2017-03-27 20:02:09,http://dooley.com/chelsea.bernhard,0.2175125282,175.242.27.194,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n15448,6,348,2017-04-15 19:16:53,http://hayes.io/elisabeth,0.6257551232,30.15.192.40,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n15449,6,348,2016-12-22 03:33:18,http://bartoletti.co/kellen,0.9404426152,228.64.243.52,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n15450,6,348,2017-05-15 15:18:04,http://millsrowe.biz/jaqueline_simonis,0.7940012030,239.114.18.131,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n15451,6,348,2017-05-12 09:34:14,http://goyetteblock.org/cole,0.4810776967,77.70.232.108,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n15452,6,348,2017-06-07 15:34:25,http://kertzmann.com/jarred_waters,0.3483627498,126.242.39.182,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n15453,6,348,2017-01-15 20:21:53,http://blanda.org/trinity_bosco,0.5829822641,69.49.70.28,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n15454,6,348,2017-06-02 17:59:14,http://gleichner.biz/michelle,0.9370479837,77.75.146.69,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n15455,6,348,2017-04-24 06:38:38,http://keebler.info/vince_bode,0.1047879880,116.162.72.114,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n15456,6,348,2017-03-05 11:09:35,http://rodriguezreynolds.biz/royce,0.0700696186,202.9.134.132,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n15457,6,348,2017-05-25 11:25:39,http://macejkovic.net/marlon.jerde,0.4649247807,192.181.61.171,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n15458,6,348,2017-05-04 15:24:19,http://mitchellgorczany.com/rolando_shanahan,0.8467351890,172.149.232.28,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n15459,6,348,2017-04-16 14:29:05,http://lueilwitz.net/leonardo_glover,0.8562019759,199.199.100.80,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n15460,6,348,2017-01-19 13:28:53,http://senger.name/vinnie_koch,0.4966059169,93.16.92.120,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n15461,6,348,2017-05-29 10:36:44,http://beer.io/dee_ankunding,0.8180170168,72.137.209.182,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n15462,6,348,2017-01-09 07:21:00,http://green.com/lori,0.1209700815,243.184.204.37,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n15463,6,348,2017-05-22 05:58:12,http://sawayn.info/amani,0.4909453677,37.224.46.26,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n15464,6,348,2017-03-13 08:01:54,http://metzcain.info/chance,0.1141870467,52.119.130.85,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n15465,6,348,2017-02-14 05:39:14,http://hermiston.biz/kira,0.9183425449,245.25.7.193,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n15466,6,348,2017-04-30 07:22:32,http://schoen.net/ramona,0.7874701979,80.245.120.158,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n15467,6,348,2017-03-26 13:10:43,http://zulaufdickinson.co/gilda,0.5391390816,123.42.171.145,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n15468,6,348,2017-01-05 21:49:12,http://jaskolski.info/ellen_senger,0.0252048218,2.198.15.101,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n15469,6,348,2016-12-14 15:50:28,http://bartell.name/ottilie_bode,0.7395370324,227.110.81.92,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n15470,6,348,2017-06-05 12:14:46,http://dickinson.co/lula,0.8599456534,220.37.54.60,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n15471,6,348,2017-02-28 01:24:08,http://schaden.info/emmie.block,0.6411947264,235.105.237.71,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n15472,6,348,2017-02-12 17:36:00,http://feeneyweber.co/savannah,0.1452810262,43.215.60.60,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n15473,6,348,2017-04-29 02:34:26,http://wisoky.io/nicole.ziemann,0.0106055210,35.175.107.184,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n15474,6,348,2017-05-30 19:31:08,http://bodeabshire.com/brielle,0.4151619287,155.231.72.214,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n15475,6,348,2017-03-30 16:34:12,http://lesch.com/dagmar_mccullough,0.3635830278,165.113.185.41,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n15476,6,348,2017-01-29 01:09:23,http://zemlak.com/marge.moen,0.9724559593,225.37.141.228,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n15477,6,348,2017-04-20 06:32:27,http://considine.org/daron_greenholt,0.5054400552,33.221.110.209,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n15478,6,348,2017-02-09 15:47:07,http://schneidertillman.name/morris_keler,0.1047567810,207.33.66.96,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n15479,6,348,2016-12-15 12:17:31,http://smithamkunze.org/lucas,0.4375805357,21.198.20.28,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n15480,6,348,2017-05-05 04:17:27,http://reilly.name/keegan,0.9134892282,41.201.136.133,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n15481,6,348,2017-06-11 01:35:15,http://volkman.org/eldora,0.6044429743,71.89.129.158,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n2628,2,58,2016-12-26 06:22:54,http://hirthe.co/westley,,35.92.64.230,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n2629,2,58,2016-12-21 00:25:24,http://hand.io/wava_adams,,147.153.234.70,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n2630,2,58,2017-03-03 00:09:05,http://davis.name/sydnee_roob,,83.90.119.171,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n2631,2,58,2017-02-09 12:58:32,http://schaeferdubuque.name/cecilia,,126.31.149.124,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n2632,2,58,2017-01-03 19:01:39,http://cruickshank.info/shirley.stark,,53.100.118.198,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n2633,2,58,2017-03-22 20:58:47,http://bartonjacobs.name/kelsi,,133.75.15.204,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n2634,2,58,2017-01-08 14:29:24,http://lockman.info/conner,,81.215.4.206,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n2635,2,58,2017-04-29 13:34:18,http://glover.com/rosalyn_goodwin,,27.45.88.246,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n2636,2,58,2017-01-01 08:41:02,http://sawaynbatz.name/amelia,,150.74.229.45,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n2637,2,58,2017-02-05 07:53:43,http://tillman.io/alene,,201.240.196.145,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n2638,2,58,2017-03-21 13:14:08,http://kub.org/dorian,,96.19.177.18,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n2639,2,58,2017-03-25 20:01:30,http://willms.biz/nona_jenkins,,13.120.163.207,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n2640,2,58,2017-01-25 21:45:17,http://dubuque.net/sherwood,,201.55.22.240,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n2641,2,58,2017-05-18 10:39:50,http://macgyver.net/irma_tillman,,139.67.94.50,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n2642,2,58,2017-05-13 01:48:25,http://block.com/gino,,242.207.134.237,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n2643,2,58,2017-06-07 15:37:25,http://bosco.io/jedidiah_yost,,111.218.156.72,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n2644,2,58,2017-03-16 17:30:43,http://littel.org/euna_bernhard,,72.171.81.230,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n2645,2,58,2017-04-12 04:45:45,http://boehmwillms.io/rebecca,,62.108.215.205,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n2646,2,58,2017-03-05 18:15:50,http://kertzmann.org/layne,,87.140.122.185,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n2647,2,58,2017-02-10 04:19:06,http://durgan.net/herbert_bahringer,,29.249.33.119,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n2648,2,58,2017-05-02 15:23:03,http://kuhic.io/fern.kilback,,124.219.172.225,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n2649,2,59,2017-02-18 06:43:07,http://ferry.io/keven,,153.152.76.145,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n2650,2,59,2017-01-04 12:16:40,http://kreigercrooks.info/van,,22.84.143.61,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n2651,2,59,2017-04-16 18:31:32,http://bahringer.info/laverne.hills,,199.166.115.191,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n2652,2,59,2017-03-07 14:46:16,http://schuppekirlin.name/julio.hirthe,,28.31.190.212,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n2653,2,59,2017-05-01 08:27:23,http://ward.name/destiny_dibbert,,244.200.82.145,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n2654,2,59,2017-02-06 14:02:09,http://reichert.info/jocelyn.hahn,,138.100.61.74,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n2655,2,59,2017-01-16 12:17:57,http://koelpineichmann.name/casper_abernathy,,138.202.238.93,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n2656,2,59,2017-05-28 08:33:46,http://cartwright.name/aiyana.swaniawski,,221.72.94.93,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n2657,2,59,2016-12-27 16:09:53,http://gutkowski.biz/retha,,116.150.39.104,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n2658,2,59,2017-03-11 13:09:03,http://farrell.co/samantha,,152.204.208.147,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n2659,2,59,2017-03-25 15:07:47,http://cole.com/keon,,92.223.190.118,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n2660,2,59,2017-02-08 07:08:24,http://cruickshank.com/harmon.wilderman,,16.156.186.150,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n2661,2,59,2017-04-11 12:44:49,http://wuckertleannon.com/taryn.gleichner,,84.25.202.195,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n2662,2,59,2017-04-14 03:01:35,http://hartmann.co/percy,,154.121.12.174,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n2663,2,59,2016-12-30 04:02:49,http://schuppe.biz/kyla,,232.114.84.90,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n2664,2,59,2017-01-08 14:37:19,http://runolfsdottir.net/jace,,55.220.179.97,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n2665,2,59,2017-01-17 21:37:41,http://witting.com/davion,,186.244.28.18,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n2666,2,59,2017-05-26 02:53:00,http://torphy.info/lacey_fadel,,159.12.195.89,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n2667,2,59,2017-05-14 05:14:26,http://fahey.net/hans,,93.120.166.215,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n2668,2,59,2017-06-05 02:31:52,http://quigley.info/curtis_bernhard,,151.69.37.49,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n2669,2,59,2017-05-05 15:39:35,http://haley.co/leo,,23.102.40.250,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n2670,2,59,2017-03-21 16:59:24,http://altenwerth.io/esther,,59.130.205.221,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n2671,2,59,2017-02-05 06:52:37,http://purdy.net/carrie,,121.22.189.85,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n2672,2,59,2017-05-08 20:54:09,http://bahringer.io/alanna,,172.163.104.227,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n2673,2,59,2017-04-17 15:29:16,http://hillskuhic.name/augustine,,101.251.122.149,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n2674,2,59,2017-02-20 00:46:14,http://schaefer.com/luz,,104.254.5.188,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n2675,2,59,2017-04-01 07:50:10,http://torphyjast.co/shyanne_block,,41.41.207.79,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n2676,2,59,2017-01-17 00:02:12,http://aufderhar.name/freeman_bechtelar,,63.129.199.223,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n2677,2,59,2017-01-25 10:55:54,http://gradydenesik.co/brennon,,219.190.90.95,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n2678,2,59,2017-04-11 07:24:52,http://effertz.co/gabe_mann,,225.102.94.62,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n2679,2,59,2017-05-10 16:46:26,http://ziemann.info/weston.medhurst,,192.79.239.69,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n2680,2,59,2017-04-05 02:33:45,http://gleichnerkemmer.biz/roman,,198.16.210.216,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n2681,2,59,2016-12-30 18:51:01,http://huels.net/emmie.welch,,71.214.115.155,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n2682,2,59,2017-01-26 11:47:42,http://hayes.org/lilly,,27.116.96.130,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n2683,2,59,2016-12-25 20:01:39,http://toy.name/chaz.eichmann,,211.26.148.153,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n9564,4,214,2017-01-28 15:12:47,http://paucek.net/emelia_schulist,,86.74.84.147,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n9565,4,214,2017-03-17 03:23:01,http://haley.info/simeon.lemke,,49.81.143.132,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n9566,4,214,2017-04-22 04:54:43,http://lebsackjohnson.info/justus.price,,67.119.5.99,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n9567,4,215,2016-12-21 14:11:00,http://spinka.biz/tyson.steuber,,252.88.249.48,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n9568,4,215,2017-01-17 08:05:45,http://emard.com/quentin,,208.81.79.152,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n9569,4,215,2017-04-19 18:14:24,http://manndibbert.net/alize,,38.212.251.239,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n9570,4,215,2017-03-13 16:54:47,http://murazikconnelly.biz/nora,,71.216.225.242,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n9571,4,215,2017-06-10 21:56:15,http://roberts.net/elliott,,251.204.39.99,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n9572,4,215,2017-05-12 18:22:40,http://deckowbeatty.name/jamey_thompson,,148.252.87.139,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n9573,4,215,2017-02-25 17:13:00,http://goyettecruickshank.org/ramiro_pouros,,162.115.248.24,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n9574,4,215,2017-01-30 20:46:28,http://abernathygleason.org/meredith,,116.9.119.127,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n9575,4,215,2017-06-09 10:45:38,http://uptonhammes.co/efrain_denesik,,98.237.191.216,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n9576,4,215,2017-04-26 08:31:12,http://larkin.net/lisette,,176.222.113.206,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n9577,4,215,2017-05-22 14:44:01,http://casper.com/alyson,,219.94.81.251,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n9578,4,215,2017-01-01 15:59:48,http://huel.info/mozelle,,129.138.84.124,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n9579,4,215,2017-02-04 02:53:38,http://lowe.io/nina_lakin,,177.183.198.109,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n9580,4,215,2017-05-11 06:13:45,http://kilbackblick.biz/maudie_johnston,,50.30.52.57,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n9581,4,215,2017-05-30 06:12:45,http://schinner.com/ewell.mccullough,,139.248.244.45,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n9582,4,215,2017-02-27 19:31:05,http://sawayn.co/peggie,,27.30.235.42,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n9583,4,215,2017-02-13 17:33:22,http://ritchie.biz/georgianna,,249.195.92.247,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n9584,4,215,2017-03-26 22:04:39,http://beer.io/kaitlyn,,89.39.110.11,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n9585,4,215,2017-06-01 16:34:13,http://larson.net/ruell.ratke,,87.22.109.19,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n9586,4,215,2016-12-28 12:29:20,http://douglas.org/nicolas,,170.248.204.149,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n9587,4,215,2017-03-04 23:49:41,http://hintz.biz/faustino,,161.220.165.191,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n9588,4,215,2017-03-04 00:56:49,http://mayerstrosin.com/fletcher_wyman,,245.214.227.200,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n9589,4,215,2017-04-12 11:04:51,http://fahey.net/jaylan_boehm,,149.60.22.154,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n9590,4,215,2017-02-09 06:11:41,http://west.io/celia,,163.165.170.106,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n9591,4,215,2017-02-19 09:30:53,http://pouros.info/kavon,,9.101.128.103,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n9592,4,215,2017-02-06 17:25:55,http://corkery.io/brice,,56.79.215.231,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n9593,4,215,2017-04-28 22:19:59,http://klein.name/gladyce,,251.194.109.108,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n9594,4,215,2017-05-01 10:03:29,http://price.biz/norma,,139.153.99.67,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n9595,4,215,2017-01-14 22:10:58,http://beer.io/jamar.nolan,,35.32.162.195,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n9596,4,215,2016-12-16 14:44:51,http://bergstrom.org/carolyne.turcotte,,166.118.86.108,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n9597,4,215,2017-04-05 23:04:57,http://hudson.org/ava_mann,,199.223.103.65,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n9598,4,215,2017-06-03 17:51:55,http://runolfon.co/della,,225.3.50.122,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n9599,4,215,2017-01-05 12:59:46,http://kozey.net/jovanny,,60.45.202.222,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n9600,4,215,2016-12-27 04:55:17,http://macejkovicsipes.net/lorena,,68.251.69.122,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n9601,4,215,2016-12-23 04:44:08,http://millchulist.io/cindy,,84.204.125.112,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n9602,4,215,2017-04-14 05:32:15,http://legros.net/cristian.kilback,,44.111.45.109,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n9603,4,215,2017-01-09 22:03:59,http://swiftjohnston.name/wilfrid,,73.31.205.199,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n9604,4,215,2017-02-09 08:25:09,http://torphy.co/moses,,224.251.13.104,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n9605,4,215,2017-05-10 12:09:37,http://okeefe.biz/victor,,95.23.154.240,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n9606,4,215,2017-01-09 02:00:47,http://berge.biz/cole.rolfson,,98.83.99.151,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n9607,4,215,2017-04-11 23:22:45,http://casper.org/tyree,,53.31.12.88,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n9608,4,215,2017-05-15 07:01:44,http://spinkaking.co/jakayla,,129.232.247.17,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n9609,4,215,2017-02-03 23:50:45,http://goyette.biz/rosemarie.bernhard,,184.190.125.125,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n9610,4,215,2016-12-14 21:48:44,http://lindgren.org/christian,,41.154.55.36,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n9611,4,215,2016-12-15 14:42:22,http://kreiger.net/arvel_mcdermott,,17.123.134.207,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n9612,4,215,2017-02-22 10:18:21,http://green.biz/laurine,,148.96.10.218,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n9613,4,215,2017-05-30 04:25:42,http://mraz.name/zetta_casper,,162.13.130.18,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n9614,4,215,2017-03-08 11:31:33,http://hilpert.com/aleandro.dickinson,,31.28.125.154,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n9615,4,215,2017-06-11 04:33:25,http://konopelski.biz/carmella,,222.211.43.162,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n9616,4,215,2017-05-28 21:23:43,http://heathcote.biz/marcus.kiehn,,225.15.44.249,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n9617,4,215,2017-04-11 05:29:50,http://townehuel.biz/nathanial_white,,224.105.234.73,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n9618,4,215,2017-05-16 16:02:23,http://aufderharschmeler.org/jamil,,115.195.139.117,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n9619,4,216,2017-04-15 08:59:10,http://rutherford.biz/neva_goldner,,55.167.246.241,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n6564,3,142,2017-06-11 13:01:20,http://wilderman.com/elmore_mckenzie,0.6741451587,78.81.157.196,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n6565,3,142,2017-03-17 22:19:41,http://kulas.org/alexie.bergnaum,0.3194787899,223.86.151.193,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n6566,3,142,2017-02-27 13:42:36,http://kaulke.biz/willa,0.5028555408,224.133.163.156,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n6567,3,142,2017-02-16 04:38:25,http://hand.co/adolfo.lowe,0.2608082344,63.37.38.249,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n6568,3,142,2017-01-06 20:51:48,http://zulauf.info/marie.oconnell,0.6214164336,36.87.162.118,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n6569,3,142,2017-01-26 06:31:21,http://dickihartmann.info/sydnie,0.0373839159,9.119.251.136,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n6570,3,143,2017-02-25 21:02:20,http://berge.com/leonie,0.7542596844,17.145.64.72,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n6571,3,143,2017-01-22 23:58:18,http://oreilly.com/adolph_kutch,0.6013033900,47.227.100.177,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n6572,3,143,2017-04-26 10:30:10,http://torphy.com/woodrow.ruel,0.7849814094,242.114.185.226,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n6573,3,143,2017-03-20 23:11:15,http://ledner.co/kaylin,0.1842973642,241.73.9.188,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n6574,3,143,2017-04-06 02:46:39,http://schmeler.co/berneice_hegmann,0.6438856256,59.106.143.7,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n6575,3,143,2017-02-12 01:48:16,http://abbott.name/lazaro.boyer,0.2504664185,131.59.77.29,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n6576,3,143,2017-01-20 07:05:52,http://collins.info/carmella,0.3013626199,2.37.212.219,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n6577,3,143,2017-05-25 21:05:04,http://wisozk.co/olaf,0.4341056705,223.206.210.61,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n6578,3,143,2017-03-12 21:40:20,http://jacobson.co/neva_lowe,0.1885951323,87.238.11.250,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n6579,3,143,2017-04-23 05:05:03,http://bruen.co/curt_pagac,0.7979621105,228.220.44.193,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n6580,3,143,2017-01-05 22:24:44,http://bruenhahn.name/brennan.waelchi,0.3908039371,118.145.122.145,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n6581,3,143,2017-04-06 20:23:04,http://hoeger.com/hardy,0.8173891216,177.91.63.75,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n6582,3,143,2017-03-29 17:55:46,http://rath.net/margarett_hamill,0.5522251673,180.38.225.235,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n6583,3,143,2017-01-24 19:06:04,http://wolff.io/diamond,0.3938183283,102.175.47.172,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n6584,3,143,2017-03-13 06:47:40,http://von.io/addison,0.4439737765,224.77.100.220,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n6585,3,143,2017-04-05 20:56:08,http://schneiderhettinger.org/cary_dach,0.6230306827,195.120.149.215,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n6586,3,143,2017-05-11 03:25:56,http://lynch.biz/shirley.mills,0.8004484277,203.184.138.175,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n6587,3,143,2017-04-14 13:43:44,http://conn.name/kathleen_hermiston,0.2647540543,93.177.3.106,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n6588,3,143,2017-05-14 10:12:21,http://stromantrantow.biz/abe_cole,0.0670506782,106.142.69.168,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n6589,3,143,2017-02-03 07:31:25,http://okon.org/kenyon_smitham,0.0739444786,111.201.62.171,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n6590,3,143,2016-12-15 17:45:48,http://marvincartwright.org/tillman,0.7437269897,203.72.136.83,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n6591,3,143,2017-04-19 08:06:28,http://orn.net/lonny.pollich,0.7110749188,19.130.188.23,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n6592,3,143,2017-05-05 16:25:42,http://leffler.biz/avery,0.5219298087,224.2.226.197,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n6593,3,143,2017-04-18 09:37:07,http://little.net/ova,0.7026690496,85.141.11.253,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n6594,3,143,2017-03-04 03:04:38,http://ricefunk.net/ronaldo,0.9495107167,186.129.190.156,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n6595,3,143,2017-05-01 12:34:16,http://marks.info/julianne,0.6705335646,147.116.185.10,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n6596,3,143,2017-01-27 15:45:11,http://reichert.com/gregoria.hirthe,0.0653634066,218.18.52.179,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n6597,3,143,2017-01-12 23:55:58,http://kulachowalter.org/elwin.rempel,0.5565461945,107.175.34.173,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n6598,3,143,2017-02-20 01:56:16,http://bayer.info/jordon,0.2778506371,36.16.53.210,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n6599,3,143,2017-01-11 20:44:21,http://pacochayost.info/naomie.zemlak,0.9841250917,205.77.139.152,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n6600,3,143,2017-05-29 08:42:39,http://jacobs.name/macy,0.0114181093,170.46.183.52,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n6601,3,143,2017-01-28 20:31:45,http://grant.name/lillie_dooley,0.9710361587,164.243.152.132,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n6602,3,143,2017-04-05 02:56:58,http://bruen.info/harrison_lang,0.0529181649,78.55.34.107,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n6603,3,143,2017-04-13 12:05:52,http://aufderharmacejkovic.net/rubie.bergnaum,0.6704035988,12.83.179.7,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n6604,3,143,2017-05-31 18:43:48,http://considine.info/kyle.barton,0.9186188554,216.75.228.76,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n6605,3,143,2017-05-26 12:11:34,http://windler.io/david.mckenzie,0.1889592017,215.89.43.23,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n6606,3,143,2017-04-09 20:33:09,http://grady.biz/kaitlin.rempel,0.6907391628,137.129.91.160,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n6607,3,143,2016-12-28 18:23:37,http://baumbachward.io/jorge,0.8896273114,153.163.195.26,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n6608,3,143,2017-06-09 21:09:21,http://brekke.info/jayson,0.1261721288,30.38.194.55,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n6609,3,143,2017-01-01 09:27:16,http://osinskirippin.biz/orval,0.7091148550,254.102.177.250,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n6610,3,143,2017-01-12 01:14:15,http://kuvalisdavis.info/calista,0.7565339066,185.26.172.39,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n6611,3,143,2017-01-04 01:27:18,http://cain.co/jarod,0.2100995224,151.117.106.249,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n6612,3,143,2017-02-02 15:27:14,http://mosciski.com/cordie,0.3837210121,139.162.228.139,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n6613,3,143,2016-12-22 17:33:12,http://labadie.biz/renee.doyle,0.0981821118,126.155.135.150,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n6614,3,143,2017-06-07 07:02:35,http://stamm.io/carolyne,0.4166966130,124.142.52.86,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n6615,3,143,2017-04-16 06:42:32,http://hansen.net/samir_schamberger,0.3377165885,72.10.19.35,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n12472,5,279,2017-02-11 00:26:43,http://brown.net/miracle,,143.180.56.207,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n12473,5,279,2017-03-11 09:18:56,http://batzstiedemann.org/lia,,61.225.124.35,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n12474,5,279,2017-05-04 06:29:14,http://hyatt.org/deven.rice,,80.23.113.32,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n12475,5,279,2017-02-04 13:49:24,http://halvorson.biz/reilly,,133.250.57.130,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n12476,5,279,2016-12-13 13:04:30,http://kunze.net/brianne.gerlach,,228.118.191.225,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n12477,5,279,2017-02-28 21:08:22,http://balistrerikertzmann.co/josue,,225.171.244.148,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n12478,5,279,2017-04-22 22:54:13,http://strosin.net/cynthia,,199.251.184.30,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n12479,5,279,2017-06-06 17:21:59,http://hermistonbreitenberg.biz/laura,,99.187.63.223,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n12480,5,279,2017-04-15 13:29:06,http://hettingerruel.biz/athena,,146.101.190.112,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n12481,5,279,2017-02-27 20:40:26,http://purdyko.org/ron,,102.157.97.78,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n12482,5,279,2017-03-15 16:39:58,http://oconnell.com/misty,,94.23.72.44,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n12483,5,279,2017-06-10 10:04:50,http://powlowskiwolff.info/marjory_hermann,,20.240.131.119,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n12484,5,279,2017-05-09 00:12:48,http://kunzebalistreri.org/bruce,,142.12.249.77,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n12485,5,279,2016-12-17 05:26:53,http://franecki.org/marian,,217.173.149.17,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n12486,5,279,2017-03-26 02:51:04,http://langosh.info/leanna,,178.8.182.166,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n12487,5,279,2017-03-22 02:45:43,http://effertzbayer.com/jason,,153.112.182.190,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n12488,5,279,2017-03-20 01:32:04,http://runte.io/ambrose,,56.168.185.113,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n12489,5,280,2017-01-13 13:18:36,http://schulist.io/lea.beatty,,82.130.150.59,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n12490,5,280,2017-06-01 11:20:27,http://koconn.io/alena_wilderman,,170.217.57.93,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n12491,5,280,2017-04-01 09:47:29,http://kirlinveum.org/floy_kling,,38.161.163.190,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n12492,5,280,2017-05-10 21:35:25,http://gleason.net/reece,,171.217.125.91,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n12493,5,280,2017-05-03 00:51:47,http://johnson.co/lilian_friesen,,227.249.155.170,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n12494,5,280,2017-01-29 08:09:42,http://bahringer.net/gilbert,,187.160.248.28,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n12495,5,280,2016-12-20 21:24:45,http://ricequitzon.info/ramona_parker,,113.147.131.198,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n12496,5,280,2017-04-10 16:17:25,http://cartwright.io/cicero,,245.144.119.56,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n12497,5,280,2016-12-17 07:14:37,http://metz.biz/dena_cole,,56.235.224.35,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n12498,5,280,2017-04-14 05:05:02,http://wisoky.co/maya,,24.105.84.48,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n12499,5,280,2017-03-14 12:14:48,http://rolfson.net/hayley,,64.184.136.34,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n12500,5,280,2017-05-10 22:44:54,http://mraz.net/horace.huels,,206.175.200.218,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n12501,5,280,2016-12-31 12:53:37,http://toyjaskolski.co/isabell,,178.187.23.20,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n12502,5,280,2017-01-24 12:38:21,http://mitchell.com/camille_lehner,,186.71.251.67,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n12503,5,280,2017-03-02 18:40:42,http://skiles.info/terrill,,204.135.100.31,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n12504,5,280,2017-03-22 05:28:18,http://strackemann.net/sienna,,218.53.150.5,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n12505,5,280,2017-01-20 09:23:39,http://schambergerwolff.net/camden,,63.122.38.204,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n12506,5,280,2017-03-08 18:56:24,http://osinskihaley.name/andrew,,48.231.185.204,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n12507,5,280,2017-04-09 00:08:35,http://hansen.name/maggie_auer,,225.236.114.8,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n12508,5,280,2017-02-01 18:33:24,http://monahan.info/granville.weimann,,62.189.191.144,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n12509,5,280,2017-06-09 14:50:02,http://considinekris.org/bertha,,170.191.154.203,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n12510,5,280,2016-12-14 09:31:57,http://considine.com/avis.mclaughlin,,43.242.19.27,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n12511,5,280,2016-12-20 07:58:14,http://wintheiserrutherford.name/brennon,,143.234.139.111,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n12512,5,280,2017-01-31 17:50:09,http://oconnell.org/aleia_hudson,,173.95.193.173,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n12513,5,280,2017-03-18 17:43:56,http://wilderman.org/jarrett_crooks,,140.87.235.80,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n12514,5,280,2017-06-12 12:52:02,http://schmidtbruen.com/nat_doyle,,229.43.231.209,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n12515,5,280,2017-02-11 07:18:27,http://macejkovicschultz.io/ike,,252.70.232.225,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n12516,5,280,2017-03-20 04:51:55,http://marksberge.com/mona,,75.11.33.22,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n12517,5,280,2017-02-14 17:35:52,http://anderson.biz/tyrel,,50.239.224.75,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n12518,5,280,2017-01-22 23:57:10,http://barrows.name/adaline,,60.164.242.47,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n12519,5,280,2017-05-09 21:42:16,http://langworth.io/immanuel_stanton,,111.235.217.176,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n12520,5,280,2017-02-04 11:25:42,http://stracke.co/jaydon,,159.47.104.75,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n12521,5,280,2017-05-06 20:19:44,http://nitzsche.biz/maude_kreiger,,218.177.174.36,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n12522,5,280,2017-06-14 04:39:09,http://sengerwalker.com/noemi.kunde,,163.226.246.54,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n12523,5,280,2017-06-11 12:17:50,http://nolan.io/casimir,,11.139.95.36,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n12524,5,280,2017-03-31 20:58:35,http://cormier.org/emery,,206.118.177.199,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n12525,5,280,2017-03-14 03:20:20,http://kunde.name/marisol_dietrich,,3.128.186.157,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n12526,5,280,2017-02-12 20:33:57,http://hillsmiller.name/cheyenne_collier,,250.246.112.215,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n15482,6,348,2017-03-19 18:16:19,http://reichel.co/aimee.tillman,0.2963933829,96.184.40.51,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n15483,6,348,2017-02-10 03:48:28,http://huels.biz/ozella,0.3568982700,136.25.114.54,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n15484,6,348,2017-04-26 05:41:19,http://casper.org/jamar,0.1206179609,237.54.195.64,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n15485,6,348,2017-04-03 16:11:43,http://windler.io/wyatt_hand,0.3555207366,81.161.194.233,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n15486,6,348,2017-06-01 11:15:26,http://kris.name/emilie,0.9901904373,179.57.157.84,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n15487,6,348,2017-03-06 21:31:53,http://bartoletti.io/aurore,0.2588076915,176.31.194.105,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n15488,6,348,2017-02-01 21:16:22,http://konopelskigibson.info/ian,0.1924671808,123.169.76.98,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n15489,6,348,2017-06-03 08:57:15,http://haag.com/eladio,0.3921073801,49.138.212.30,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n15490,6,348,2017-06-02 09:58:47,http://kunzelynch.org/miguel,0.5793877021,131.219.171.196,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n15491,6,348,2017-01-08 07:41:59,http://moen.biz/tamara.mosciski,0.1917166811,23.205.139.201,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n15492,6,348,2017-02-26 23:39:26,http://marvinadams.com/eve.dubuque,0.6821636183,89.5.219.108,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n15493,6,348,2017-01-07 03:29:10,http://bergstrom.org/edgar,0.9302279036,227.216.82.202,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n15494,6,348,2017-06-07 07:21:02,http://adams.biz/ocie_nolan,0.2561259714,87.23.114.79,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n15495,6,348,2017-06-01 19:10:09,http://hills.biz/ayla,0.7588906462,246.241.164.122,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n15496,6,348,2017-05-30 14:48:32,http://powlowski.net/theodore,0.3802896132,16.175.238.153,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n15497,6,348,2017-03-12 10:47:53,http://runte.info/bennett,0.3968527314,126.173.168.14,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n15498,6,348,2017-04-07 20:26:32,http://littel.co/malachi_connelly,0.9147388075,148.148.96.164,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n15499,6,348,2017-05-09 17:32:17,http://mantestamm.org/german,0.7817669277,13.157.103.107,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n15500,6,348,2017-03-29 23:35:51,http://champlin.info/anibal_fay,0.6789675870,245.53.34.155,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n15501,6,348,2017-03-16 12:55:16,http://hermiston.info/maybell,0.3151012880,14.18.34.238,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n15502,6,348,2016-12-15 06:58:33,http://beier.co/roxane_harber,0.3844250160,185.79.40.136,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n15503,6,348,2017-04-05 23:42:47,http://gulgowski.com/cordia_paucek,0.4762554555,191.63.157.82,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n15504,6,348,2017-03-16 05:21:55,http://pfannerstill.com/rigoberto_hodkiewicz,0.1221494372,193.120.155.208,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n15505,6,348,2017-05-31 12:52:28,http://sauer.io/mallory,0.4546239966,138.72.197.138,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n15506,6,349,2017-01-03 10:51:17,http://altenwerth.org/dereck.predovic,0.5998374718,236.215.150.112,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n15507,6,349,2016-12-24 18:57:43,http://yost.io/elisa,0.1465831770,97.108.38.150,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n15508,6,349,2017-05-15 01:33:05,http://spencergreenholt.io/keon,0.3065167272,208.5.105.197,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n15509,6,349,2017-04-07 11:16:30,http://mckenziemiller.info/reggie,0.9745659576,64.209.191.246,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n15510,6,349,2017-04-16 09:44:30,http://auerkoelpin.com/frederic_hudson,0.1880741817,237.196.42.65,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n15511,6,349,2017-03-26 15:37:01,http://hilllbashirian.name/kolby,0.8120521941,133.3.241.188,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n15512,6,349,2017-05-01 03:00:08,http://rau.name/josue,0.8628040379,241.107.17.35,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n15513,6,349,2017-01-03 18:49:05,http://lubowitz.net/mozell_legros,0.9040671911,79.89.70.245,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n15514,6,349,2017-01-28 06:56:54,http://nader.net/rahsaan.king,0.4733137609,252.157.62.19,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n15515,6,349,2016-12-17 22:33:12,http://schuppe.co/albin,0.9247782407,169.223.39.246,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n15516,6,349,2017-05-22 21:05:23,http://emardwatsica.org/nannie,0.7651647468,159.184.24.130,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n15517,6,349,2017-04-11 19:45:21,http://blanda.info/demarco_hoeger,0.7323298720,23.70.78.97,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n15518,6,349,2017-02-19 10:01:28,http://ritchiegaylord.info/liana.von,0.5610327624,135.124.28.180,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n15519,6,349,2017-04-19 12:29:38,http://rau.com/shirley.frami,0.5829079430,81.40.10.45,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n15520,6,349,2017-04-21 21:05:55,http://schuppe.org/ludwig,0.8381578576,221.231.224.130,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n15521,6,349,2017-03-21 19:17:38,http://rueljohnston.io/ricky,0.6688429986,87.3.102.228,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n15522,6,349,2017-05-01 09:39:13,http://zieme.io/orion,0.6314982464,220.91.43.96,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n15523,6,349,2017-04-11 07:46:32,http://anderson.co/marie,0.7680719609,43.150.72.16,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n15524,6,349,2017-03-24 06:47:31,http://cruickshankmarks.info/jarvis_ruel,0.1418163929,165.240.74.183,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n15525,6,349,2017-04-17 00:02:32,http://ruel.net/noemy,0.1379164449,22.213.243.54,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n15526,6,349,2016-12-15 13:15:10,http://kuhn.io/stacey_ondricka,0.5209680561,19.87.116.197,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n15527,6,349,2017-01-23 22:01:59,http://reinger.info/devon,0.8173415017,37.234.45.167,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n15528,6,349,2017-01-05 10:18:38,http://bartolettipaucek.io/dallin,0.0328396956,181.104.108.139,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n15529,6,349,2017-04-20 22:09:39,http://hilll.org/abelardo.towne,0.7992551162,121.69.235.203,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n15530,6,349,2017-02-10 23:58:58,http://hettinger.co/thora_gottlieb,0.5002487361,175.6.97.187,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n15531,6,349,2016-12-21 07:57:28,http://prohaska.name/vladimir,0.2856866049,204.112.150.36,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n15532,6,349,2017-03-21 19:51:43,http://hermann.com/kaden_jakubowski,0.4003162190,248.251.107.180,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n15533,6,349,2017-03-19 10:53:57,http://hane.com/bernadette.hermiston,0.6000633312,5.159.121.63,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n2684,2,59,2016-12-20 04:08:26,http://doyle.co/deanna_roberts,,173.30.250.192,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n2685,2,59,2017-03-01 15:09:11,http://pacochadare.org/robb,,24.86.213.81,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n2686,2,59,2016-12-18 10:41:50,http://gleason.org/deanna.carter,,83.239.91.219,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n2687,2,59,2017-02-24 18:21:30,http://harris.com/zula.kerluke,,194.86.46.220,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n2688,2,59,2016-12-25 19:34:36,http://hermiston.name/carolina_haag,,207.137.90.249,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n2689,2,59,2016-12-23 17:58:27,http://padbergschuster.info/golda,,49.112.63.229,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n2690,2,59,2017-04-29 15:56:15,http://watsicajacobs.info/garth,,210.23.187.150,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n2691,2,59,2016-12-30 09:27:00,http://raynor.com/golda.heathcote,,250.222.89.231,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n2692,2,60,2017-04-25 11:47:08,http://deckowstiedemann.biz/alford_emmerich,,92.178.81.132,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n2693,2,60,2017-02-05 23:16:21,http://hettinger.io/rosa_kunde,,119.128.203.134,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n2694,2,60,2017-02-08 16:42:49,http://rogahnkerluke.info/immanuel_durgan,,147.75.101.58,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n2695,2,60,2017-02-21 18:46:51,http://strackestroman.info/don,,44.15.247.143,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n2696,2,60,2017-04-14 18:14:38,http://murphy.info/margret,,69.115.177.41,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n2697,2,60,2017-01-15 15:52:15,http://stiedemann.name/judah,,224.174.203.199,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n2698,2,60,2017-01-19 18:23:52,http://okeefe.co/daisy,,246.243.203.99,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n2699,2,60,2017-03-03 05:47:58,http://gerlach.net/macey,,184.30.137.212,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n2700,2,60,2017-04-01 09:55:43,http://handortiz.info/flavio_west,,90.72.196.73,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n2701,2,60,2017-06-13 07:33:44,http://senger.name/fanny,,61.54.202.148,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n2702,2,60,2017-05-01 02:42:37,http://halvorsonfeil.io/yasmeen_kling,,119.159.28.252,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n2703,2,60,2017-02-23 06:39:46,http://ernserabernathy.biz/ilene_howe,,89.141.187.61,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n2704,2,60,2017-01-19 00:21:11,http://quigley.info/wilbert.wintheiser,,180.39.162.145,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n2705,2,60,2017-02-14 22:30:39,http://denesik.info/alphonso,,233.89.180.76,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n2706,2,60,2017-03-01 17:57:57,http://volkmannader.io/mathew,,77.195.222.55,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n2707,2,60,2016-12-16 17:28:15,http://king.org/lizeth,,76.240.156.165,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n2708,2,60,2016-12-16 21:38:41,http://raynor.name/alford,,134.131.157.137,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n2709,2,60,2017-05-07 21:09:07,http://toygoyette.com/christa.schiller,,174.40.54.65,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n2710,2,60,2017-03-19 03:46:44,http://kaulke.biz/manuel_bernier,,7.98.147.74,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n2711,2,60,2017-04-24 05:27:04,http://goyettesenger.org/sylvia,,67.238.182.159,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n2712,2,60,2016-12-25 18:03:01,http://krajcikjakubowski.org/sigrid_spencer,,173.247.199.65,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n2713,2,60,2017-06-05 12:35:03,http://wiza.co/karen_walker,,57.47.220.20,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n2714,2,60,2016-12-26 20:16:45,http://langworthcruickshank.org/loren.herman,,234.127.243.184,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n2715,2,60,2017-05-11 12:28:08,http://swiftcarroll.biz/adelbert_keler,,152.186.29.247,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n2716,2,60,2017-04-24 22:13:23,http://dach.co/deshawn,,214.111.179.2,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n2717,2,60,2017-01-24 04:45:43,http://wyman.io/ena_batz,,52.29.8.188,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n2718,2,60,2017-03-02 22:38:57,http://rippin.name/jana.brown,,3.114.63.183,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n2719,2,60,2017-01-29 17:56:03,http://mclaughlin.com/isaiah.hartmann,,69.26.205.226,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n2720,2,60,2017-06-04 15:35:49,http://zulauf.name/lavern,,59.208.251.112,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n2721,2,60,2017-01-25 07:34:01,http://funkmitchell.name/reed_hammes,,10.106.237.234,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n2722,2,60,2017-01-01 22:06:37,http://heaney.com/cierra_schuster,,149.115.183.169,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n2723,2,60,2017-01-11 13:08:32,http://lang.org/jacinto,,108.14.141.49,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n2724,2,60,2017-05-10 21:16:46,http://langosh.info/colton,,220.92.197.207,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n2725,2,60,2017-03-05 09:12:03,http://oreillymuller.org/kelvin,,63.46.28.44,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n2726,2,60,2017-04-08 04:45:04,http://pouros.name/jaclyn,,14.48.201.18,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n2727,2,60,2017-04-13 03:07:33,http://will.io/gail_cole,,96.19.40.83,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n2728,2,60,2017-04-05 00:50:32,http://blick.net/bryce,,158.34.113.242,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n2729,2,60,2017-03-10 22:21:58,http://champlin.io/dexter,,193.245.123.127,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n2730,2,60,2017-04-16 18:19:25,http://johnston.org/sasha.kemmer,,68.204.71.174,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n2731,2,60,2016-12-21 05:09:31,http://leuschke.io/norene,,209.66.63.27,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n2732,2,60,2017-01-21 01:04:16,http://hudson.com/preston,,3.246.82.71,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n2733,2,60,2017-02-07 01:47:02,http://osinski.name/darwin.brekke,,127.89.214.17,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n2734,2,60,2017-02-28 23:14:16,http://okunevaabernathy.org/maximillia_collins,,23.153.223.179,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n2735,2,60,2017-03-03 13:22:28,http://kohler.info/leda.legros,,7.74.129.183,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n2736,2,60,2017-03-15 01:47:57,http://mccullough.io/vallie_hermiston,,170.30.236.163,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n2737,2,60,2017-05-31 03:37:41,http://blocksenger.co/travon,,146.49.72.101,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n2738,2,60,2017-05-30 11:23:20,http://zieme.org/eliseo_herman,,154.120.174.62,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n2739,2,60,2017-04-14 04:24:49,http://crona.biz/ida_kub,,239.63.203.83,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n9620,4,216,2017-06-13 05:25:32,http://pfannerstill.io/kelvin,,13.88.46.99,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n9621,4,216,2016-12-13 20:59:24,http://baumbachmedhurst.biz/kaandra,,157.175.167.109,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n9622,4,216,2016-12-29 14:04:35,http://connelly.com/delpha,,78.101.26.168,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n9623,4,216,2017-02-24 21:05:26,http://walsh.co/emerson.rippin,,13.251.77.22,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n9624,4,216,2017-03-07 07:05:17,http://zulaufhills.com/shayne_wyman,,160.174.110.156,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n9625,4,216,2017-04-05 16:50:51,http://jerdeziemann.io/colt.toy,,27.75.127.232,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n9626,4,216,2017-03-24 18:38:08,http://friesen.com/tracey.mante,,81.155.212.204,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n9627,4,216,2017-03-20 05:39:48,http://sanford.io/davon,,37.10.115.136,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n9628,4,216,2017-04-25 09:28:48,http://harvey.info/brenna,,126.217.21.129,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n9629,4,216,2017-02-27 17:53:20,http://hilll.co/marianne_marks,,74.207.13.134,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n9630,4,216,2016-12-14 09:45:50,http://kubpfannerstill.io/rozella.langworth,,173.104.252.234,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n9631,4,216,2017-01-16 02:19:29,http://haley.org/nya,,105.166.206.121,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n9632,4,216,2017-04-22 12:36:33,http://gaylord.org/kayden,,189.205.60.140,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n9633,4,216,2017-05-19 15:19:27,http://leannon.com/liana.mills,,254.92.217.51,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n9634,4,216,2017-05-21 17:44:47,http://cain.net/forest,,54.23.124.170,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n9635,4,216,2017-01-21 04:59:19,http://dach.org/ena.blick,,188.63.109.193,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n9636,4,216,2016-12-16 04:27:07,http://rogahn.biz/nichole_jast,,215.47.100.221,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n9637,4,216,2017-04-01 10:49:25,http://schillergusikowski.biz/tillman,,111.224.62.253,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n9638,4,216,2016-12-17 21:56:26,http://kautzergusikowski.biz/elya.klocko,,190.50.70.146,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n9639,4,216,2016-12-23 10:29:47,http://lemke.io/julian,,50.155.198.12,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n9640,4,216,2016-12-29 05:09:31,http://brakusraynor.net/amara_weimann,,156.92.135.61,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n9641,4,216,2017-02-14 23:50:39,http://cain.biz/cristian,,147.118.133.165,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n9642,4,216,2017-04-03 19:13:34,http://leffler.io/eulah,,189.61.253.56,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n9643,4,216,2017-01-14 16:45:10,http://carternienow.co/lelia_bergstrom,,121.172.136.252,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n9644,4,216,2017-01-09 04:04:00,http://lubowitz.net/price.carter,,238.253.134.173,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n9645,4,216,2017-05-06 12:28:00,http://reillygreenfelder.co/merlin,,60.122.162.101,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n9646,4,216,2017-02-26 01:47:16,http://feil.biz/rowena.friesen,,131.33.151.65,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n9647,4,216,2017-05-12 12:34:19,http://dietrich.io/maybell_corkery,,182.93.87.61,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n9648,4,216,2017-04-24 05:02:34,http://yundt.name/antonina,,56.241.217.82,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n9649,4,216,2017-02-18 15:20:37,http://olson.biz/rolando,,36.237.195.72,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n9650,4,216,2017-01-29 05:27:50,http://hartmann.info/lois.bradtke,,77.69.80.70,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n9651,4,217,2017-06-01 06:54:43,http://howell.name/mercedes_upton,,250.185.179.123,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n9652,4,217,2017-04-27 03:19:22,http://paucek.io/ambrose,,41.207.75.156,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n9653,4,217,2017-03-18 01:31:12,http://cain.com/kole,,162.60.55.213,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n9654,4,217,2017-03-22 16:44:01,http://gislasonmaggio.biz/kayla,,156.30.46.8,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n9655,4,217,2017-01-27 13:54:06,http://schoenshanahan.name/joana_botsford,,145.227.194.224,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n9656,4,217,2017-04-19 12:11:15,http://vandervortskiles.co/providenci.senger,,208.120.8.149,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n9657,4,217,2017-04-17 10:44:34,http://reichertberge.org/julianne_ritchie,,197.60.178.150,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n9658,4,217,2017-06-07 10:54:40,http://borer.co/bernadine_willms,,41.222.57.51,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n9659,4,217,2017-01-12 15:28:36,http://dietrich.name/jeff,,218.233.77.163,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n9660,4,217,2017-04-10 23:25:42,http://huels.co/holly,,16.165.139.168,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n9661,4,217,2017-03-09 23:10:17,http://welchquigley.net/mavis_cole,,240.88.223.177,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n9662,4,217,2017-04-30 09:46:07,http://bruen.biz/gilda_hand,,233.131.210.26,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n9663,4,217,2017-04-01 18:19:04,http://hettinger.info/eloise.mclaughlin,,134.214.230.205,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n9664,4,217,2016-12-14 01:05:42,http://metz.com/thalia,,31.87.228.153,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n9665,4,217,2017-04-28 19:54:38,http://runtebrown.info/maya.haag,,210.181.213.99,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n9666,4,217,2017-04-12 16:13:31,http://bechtelar.name/celestine_hintz,,148.35.88.121,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n9667,4,217,2017-05-11 19:57:07,http://williamson.name/herbert_reilly,,98.164.111.225,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n9668,4,217,2017-01-17 02:57:31,http://mitchellconsidine.info/wallace,,57.210.119.150,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n9669,4,217,2017-01-15 08:15:59,http://moennicolas.org/miguel_konopelski,,127.137.202.140,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n9670,4,217,2016-12-29 18:18:59,http://koch.io/arturo.gottlieb,,247.207.56.237,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n9671,4,217,2017-01-27 09:14:33,http://waters.com/rosetta.rohan,,64.195.192.53,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n9672,4,217,2017-02-03 01:21:47,http://wardgreenfelder.org/caria,,194.179.68.197,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n9673,4,217,2017-03-30 05:05:51,http://schroedergottlieb.org/ulises,,188.29.35.27,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n9674,4,217,2017-05-24 07:43:51,http://bergnaum.info/keely.leffler,,129.122.187.140,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n6616,3,143,2016-12-23 08:11:25,http://koelpin.info/nia,0.3844630263,60.46.248.244,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n6617,3,143,2017-03-07 17:13:39,http://torp.org/morgan,0.9385344047,168.11.250.221,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n6618,3,143,2017-04-17 21:20:16,http://carter.name/cristal_strosin,0.1928578701,203.58.154.118,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n6619,3,143,2017-01-15 06:38:53,http://walkerlarson.com/gertrude,0.2845837043,200.19.25.102,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n6620,3,143,2017-04-28 18:52:07,http://turcotte.biz/johnnie_sawayn,0.0165610671,195.5.26.71,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n6621,3,144,2017-05-13 09:10:40,http://ritchie.net/angel,0.1618870814,58.119.240.164,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n6622,3,144,2017-04-29 10:39:24,http://kertzmannlebsack.name/felton.purdy,0.0445990740,3.165.19.19,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n6623,3,144,2017-02-04 22:44:38,http://harberheaney.net/erica,0.8706823444,198.10.71.41,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n6624,3,144,2016-12-14 06:07:26,http://lang.org/alexandria,0.6867206713,220.239.71.129,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n6625,3,144,2017-02-25 04:51:52,http://cronin.info/maryam_oconner,0.2750111440,182.158.61.49,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n6626,3,144,2017-02-12 11:43:00,http://crona.io/sydnie,0.7335374390,86.39.214.16,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n6627,3,144,2017-04-01 15:03:46,http://dickens.name/brittany,0.9224148443,195.73.152.202,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n6628,3,144,2017-04-19 17:15:42,http://kihn.org/blanche,0.2203803436,104.85.57.71,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n6629,3,144,2017-05-14 06:33:37,http://kelerkunze.co/lola_larkin,0.6038576567,230.141.47.235,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n6630,3,144,2017-04-21 01:16:12,http://macejkovicgreenholt.net/santino,0.6924042411,35.79.81.26,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n6631,3,144,2017-02-02 16:58:14,http://zboncakondricka.net/zack,0.1599982152,108.49.165.57,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n6632,3,144,2017-06-02 14:55:19,http://schoen.com/nathanial,0.5161636441,48.223.77.21,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n6633,3,144,2017-04-09 08:22:06,http://manncole.com/efrain,0.3037949951,59.154.106.34,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n6634,3,144,2017-01-19 10:35:15,http://kshlerin.info/carmel,0.5467559139,72.228.125.199,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n6635,3,144,2017-05-23 04:23:18,http://doyle.com/sydni,0.9311953274,113.101.65.244,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n6636,3,144,2017-04-14 10:13:21,http://oconnell.io/matilde_jenkins,0.6690599839,205.155.177.227,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n6637,3,144,2017-01-17 14:55:14,http://mccullough.name/marielle_breitenberg,0.1094189471,12.217.129.67,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n6638,3,144,2017-04-17 06:48:51,http://mcglynn.com/earline,0.0708567131,176.187.27.46,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n6639,3,144,2017-02-24 18:49:19,http://darebauch.io/destiny.baumbach,0.2542724202,56.230.163.86,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n6640,3,144,2017-01-17 20:50:57,http://kutchkuhn.org/elza,0.4079738422,41.191.191.174,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n6641,3,144,2017-01-11 13:24:47,http://watsica.org/torrance,0.9512958894,189.68.60.103,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n6642,3,144,2017-04-26 19:33:50,http://walterpredovic.biz/dangelo,0.9411036089,2.153.161.54,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n6643,3,144,2017-04-27 08:24:46,http://schowalterdonnelly.co/tyrel,0.8244558683,206.183.106.187,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n6644,3,144,2017-01-22 03:36:17,http://connelly.com/aliza_dietrich,0.8745942777,51.126.100.120,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n6645,3,145,2017-05-04 14:41:36,http://harbernicolas.biz/easton,0.0387648209,137.138.74.186,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n6646,3,145,2017-01-21 10:51:47,http://hermannschmeler.biz/tristin,0.0654599151,65.111.173.221,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n6647,3,145,2016-12-25 06:36:54,http://okonruecker.name/helena,0.2489352568,127.177.20.5,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n6648,3,145,2017-05-06 04:21:39,http://dickens.com/maia.emmerich,0.8166643130,200.62.233.200,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n6649,3,145,2017-04-08 07:50:30,http://lindconnelly.info/ezequiel.nader,0.5778004120,197.116.6.248,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n6650,3,145,2017-02-10 02:27:17,http://kemmer.org/celestino,0.4232851047,234.49.209.78,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n6651,3,145,2017-02-13 22:52:56,http://crooks.io/maritza,0.6641849413,190.96.163.188,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n6652,3,145,2017-06-03 11:53:56,http://goldner.co/bethel.krajcik,0.8628365967,101.222.73.108,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n6653,3,145,2017-03-27 08:37:44,http://ziemeoconner.org/johan,0.8574671927,81.22.125.253,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n6654,3,145,2017-03-17 11:53:30,http://douglaswisoky.co/gavin.hane,0.2849749160,32.251.191.74,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n6655,3,145,2017-05-22 17:43:52,http://abshiremurray.net/francesca,0.2225947285,204.209.181.47,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n6656,3,145,2017-05-15 22:28:10,http://cronin.name/buck,0.0953272161,214.167.237.39,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n6657,3,145,2017-01-22 01:33:49,http://hegmann.biz/horace.oconnell,0.5125026500,133.37.233.236,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n6658,3,145,2017-03-13 16:00:53,http://haag.com/fannie.cruickshank,0.5726887119,251.110.79.249,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n6659,3,145,2017-01-17 16:40:28,http://williamsonhintz.org/leila_hane,0.6097342789,106.49.243.37,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n6660,3,145,2017-05-03 01:16:13,http://schroeder.com/jayne,0.6067090805,213.15.10.84,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n6661,3,145,2017-04-16 22:32:58,http://batzmiller.info/peyton,0.1481871247,232.145.165.225,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n6662,3,145,2017-03-28 02:59:51,http://weimann.info/jackeline_rogahn,0.8464249725,131.60.227.228,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n6663,3,145,2017-02-15 01:45:15,http://beahanmaggio.info/erwin.orn,0.9434261224,134.152.59.173,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n6664,3,145,2017-05-22 17:39:49,http://homenickconn.org/bradly,0.3085766595,182.56.21.128,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n6665,3,145,2017-03-12 21:11:48,http://powlowski.com/ernie_mills,0.2776174995,122.113.96.42,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n6666,3,145,2017-04-10 16:54:24,http://marks.co/wilson_west,0.2983725679,145.147.153.103,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n6667,3,145,2017-01-14 05:25:23,http://crist.net/lurline,0.5795780862,218.177.141.241,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n12527,5,280,2016-12-29 10:42:51,http://konopelski.info/reta,,226.97.86.66,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n12528,5,280,2017-03-26 23:35:49,http://steuber.com/tanya.balistreri,,181.231.111.41,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n12529,5,280,2017-01-11 03:38:11,http://gradyfisher.info/abigayle,,5.79.102.34,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n12530,5,280,2017-05-26 15:33:49,http://daniel.net/sarah,,79.21.181.192,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n12531,5,280,2017-05-12 18:09:23,http://mcclureheidenreich.info/bart_schamberger,,124.109.148.203,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n12532,5,280,2016-12-22 15:25:27,http://pfannerstill.name/ardella.mueller,,52.127.181.221,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n12533,5,280,2017-02-19 18:38:54,http://shields.net/tobin,,134.24.206.31,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n12534,5,280,2017-01-29 20:41:32,http://collins.co/theo_johnson,,252.203.191.117,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n12535,5,281,2017-01-24 10:48:03,http://stracke.org/cathrine.mayer,,143.99.228.203,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n12536,5,281,2017-04-03 21:09:52,http://frami.io/ron.predovic,,49.10.205.15,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n12537,5,281,2017-02-06 13:44:34,http://danielpfannerstill.biz/morton,,67.3.70.115,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n12538,5,281,2017-01-24 20:34:46,http://wintheiser.name/arielle,,29.73.29.240,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n12539,5,281,2017-05-30 00:25:53,http://kilback.io/emerald.schoen,,35.179.16.138,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n12540,5,281,2017-03-02 17:41:04,http://yundtmcglynn.co/kelli,,199.92.77.107,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n12541,5,281,2017-03-24 07:20:19,http://quigley.io/marcella,,231.77.84.67,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n12542,5,281,2017-01-19 16:09:18,http://raynor.net/ivah_waters,,94.132.156.170,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n12543,5,281,2017-04-10 11:05:24,http://white.io/ben,,6.58.143.99,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n12544,5,281,2017-01-09 07:40:38,http://rath.io/elda_jacobs,,166.184.61.214,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n12545,5,281,2016-12-17 06:05:10,http://gerlachdooley.info/dagmar.romaguera,,99.20.17.69,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n12546,5,281,2017-01-01 01:25:18,http://langoshbradtke.name/magnus,,238.174.98.16,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n12547,5,281,2017-01-18 21:37:43,http://rau.org/asha.crona,,98.14.63.13,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n12548,5,281,2017-02-26 06:18:18,http://keeling.io/gudrun.heidenreich,,23.117.139.94,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n12549,5,281,2017-03-10 22:27:22,http://weber.com/lew.powlowski,,130.198.128.98,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n12550,5,281,2017-05-06 10:46:41,http://roberts.org/ethyl,,134.5.192.109,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n12551,5,281,2017-04-17 19:16:14,http://douglas.org/steve.schumm,,111.171.82.195,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n12552,5,281,2017-04-08 11:25:54,http://yundt.org/clint,,111.41.230.229,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n12553,5,281,2017-01-09 08:45:53,http://koelpinhauck.biz/darrel,,160.133.247.12,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n12554,5,281,2017-02-20 04:59:58,http://cremin.biz/rosario_bode,,23.72.43.61,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n12555,5,281,2017-02-23 02:31:16,http://blockbosco.co/monique,,241.143.86.194,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n12556,5,281,2017-03-31 01:34:31,http://kuhic.com/shyann,,216.120.118.50,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n12557,5,281,2017-01-18 07:15:01,http://ondricka.net/ettie,,165.240.158.76,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n12558,5,281,2017-06-07 01:55:58,http://okuneva.org/aliya,,250.181.116.96,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n12559,5,281,2017-04-27 18:47:44,http://schultzbauch.info/shayna,,100.141.11.238,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n12560,5,281,2017-06-03 15:33:56,http://mosciski.name/marcella.hand,,189.248.20.179,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n12561,5,281,2016-12-15 09:43:53,http://gleason.info/al,,48.200.52.178,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n12562,5,281,2017-06-11 22:02:24,http://langoshwillms.net/madeline.balistreri,,122.2.4.102,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n12563,5,281,2017-03-17 05:40:07,http://nienow.com/kitty_koepp,,192.3.80.205,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n12564,5,281,2017-02-13 06:56:30,http://ferry.co/shad.nienow,,38.125.211.247,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n12565,5,281,2017-01-02 10:39:21,http://blickjohns.co/javonte_fadel,,59.23.228.251,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n12566,5,281,2017-06-08 20:50:06,http://buckridgepfannerstill.io/dereck_bode,,121.228.123.252,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n12567,5,281,2017-04-03 11:01:48,http://mcclurebrakus.net/gudrun,,10.207.123.253,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n12568,5,281,2017-05-20 03:20:25,http://hermiston.co/morris,,56.150.184.212,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n12569,5,281,2017-05-17 02:58:44,http://jakubowski.biz/brandy,,118.146.210.201,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n12570,5,281,2017-02-10 16:09:11,http://grimetracke.biz/kobe,,132.68.214.248,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n12571,5,281,2017-01-28 00:20:57,http://ziemanncollins.name/hester_howell,,144.223.8.198,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n12572,5,281,2017-02-03 09:21:17,http://konopelskikirlin.net/alphonso.kaulke,,249.232.145.84,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n12573,5,281,2017-06-02 17:40:01,http://powlowski.io/emory_streich,,46.227.175.89,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n12574,5,281,2016-12-18 19:34:14,http://langosh.info/amari.brekke,,5.2.128.94,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n12575,5,281,2017-02-03 10:22:40,http://schuppe.info/casey_fay,,18.245.124.217,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n12576,5,281,2016-12-17 06:15:31,http://hermanemmerich.net/annamarie_hagenes,,254.203.236.245,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n12577,5,281,2017-05-25 15:32:46,http://spencer.biz/jett,,25.246.245.55,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n12578,5,281,2017-01-06 18:28:34,http://legros.net/maiya_doyle,,226.33.83.105,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n12579,5,281,2017-03-22 15:49:10,http://hirthe.io/reed.schaden,,126.112.164.57,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n12580,5,281,2017-05-28 04:47:57,http://price.io/viva,,164.123.171.152,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n12581,5,281,2017-02-10 03:12:01,http://romagueragrant.info/arlo_crona,,124.189.230.148,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n12582,5,281,2017-03-17 05:46:30,http://hirtheschmidt.biz/madison,,180.245.72.72,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n15534,6,349,2017-04-05 21:46:19,http://klein.co/gennaro,0.5580615877,140.6.31.213,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n15535,6,349,2017-01-10 09:18:47,http://kuhic.name/amelia_blanda,0.7318310513,84.13.85.219,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n15536,6,349,2017-01-01 15:47:33,http://hand.com/myrtle,0.6863038324,224.172.56.218,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n15537,6,349,2017-02-10 08:55:24,http://walterlynch.info/felicia.jacobson,0.2771948797,20.82.74.240,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n15538,6,349,2017-01-02 09:29:59,http://torphy.io/darian,0.4637163134,133.139.17.163,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n15539,6,349,2017-05-14 00:24:17,http://rau.net/leslie,0.3436317685,125.159.82.214,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n15540,6,349,2016-12-24 09:46:42,http://colezieme.io/kayli.stracke,0.2245251956,11.152.181.10,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n15541,6,349,2017-05-13 21:16:56,http://schuster.info/dante.conn,0.1849811381,67.244.213.151,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n15542,6,349,2017-02-20 23:20:37,http://jacobswilliamson.biz/cheyanne_braun,0.4461908160,129.21.102.206,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n15543,6,349,2017-04-12 09:34:51,http://heaney.com/jerad,0.5561262621,36.142.109.165,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n15544,6,349,2017-02-05 06:50:54,http://yost.net/gay,0.5009781839,30.134.170.19,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n15545,6,349,2017-04-30 10:05:02,http://waters.net/domenick_hane,0.0214952193,46.107.6.134,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n15546,6,349,2016-12-14 03:29:33,http://bruen.com/jordan.hilpert,0.4299121915,13.250.161.230,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n15547,6,349,2017-06-01 19:14:53,http://erdmanwalter.co/ottilie_braun,0.3788584718,128.50.172.111,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n15548,6,349,2017-01-02 11:02:14,http://damore.net/junior,0.4588030545,223.217.55.166,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n15549,6,349,2017-05-30 21:39:29,http://mohrwiza.info/jayda,0.1796893586,116.54.64.97,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n15550,6,349,2017-04-10 14:50:57,http://schuppe.biz/georgette,0.6812837887,168.50.112.244,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n15551,6,349,2016-12-28 05:10:13,http://stoltenberg.name/mauricio.pagac,0.9919936520,190.98.4.211,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n15552,6,349,2017-05-03 12:50:06,http://bayer.name/kris_rau,0.8629439953,57.142.19.90,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n15553,6,349,2017-01-05 05:30:16,http://willmhields.name/mckenzie,0.3806188393,10.82.28.101,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n15554,6,349,2017-01-08 15:47:39,http://rolfson.net/ezra,0.7136891866,224.8.100.63,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n15555,6,349,2017-06-01 05:33:37,http://oconnellkoch.name/ubaldo.macgyver,0.8908210155,228.177.78.130,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n15556,6,349,2017-02-16 23:48:35,http://damore.org/clementine,0.9220386329,38.172.230.110,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n15557,6,349,2017-03-18 01:05:43,http://weinatschinner.org/petra,0.7874413588,250.208.4.101,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n15558,6,350,2017-05-12 14:01:48,http://boscometz.com/jaunita,0.9961574866,199.212.123.95,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n15559,6,350,2017-02-09 11:28:59,http://schillerfisher.io/elna,0.4830711816,205.13.95.42,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n15560,6,350,2017-03-13 04:03:51,http://ryan.org/lois_prohaska,0.3426683149,176.111.151.52,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n15561,6,350,2017-01-04 05:21:04,http://weimann.biz/alexis_blanda,0.7671297124,179.78.232.172,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n15562,6,350,2017-02-16 16:44:58,http://mcglynn.com/spencer.schneider,0.2825364054,23.87.149.216,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n15563,6,350,2017-06-06 08:40:49,http://lakin.org/elta_legros,0.0589057194,101.129.119.221,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n15564,6,350,2017-02-03 09:27:04,http://balistrerikozey.com/name,0.5543355010,146.134.42.11,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n15565,6,350,2017-05-27 10:27:20,http://keelingbrown.name/llewellyn.legros,0.5455010757,95.121.234.220,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n15566,6,350,2017-03-02 10:14:42,http://reilly.net/tianna,0.3311596295,151.15.248.149,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n15567,6,350,2017-02-18 05:27:01,http://homenicksipes.io/marina,0.4047344522,226.137.29.37,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n15568,6,350,2017-02-18 04:43:30,http://okuneva.io/naomie.lakin,0.7082532615,160.53.31.165,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n15569,6,350,2017-02-28 06:33:29,http://strosin.name/pierce_borer,0.0650436409,145.123.169.227,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n15570,6,350,2017-06-11 15:25:15,http://jerdeklein.name/darrel,0.8759895258,71.119.82.21,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n15571,6,350,2017-01-26 19:19:03,http://dibbert.info/garfield.dooley,0.8143404711,129.89.3.209,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n15572,6,350,2017-05-13 15:17:54,http://johnson.org/brody,0.0707917550,124.123.64.153,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n15573,6,350,2017-01-09 00:07:15,http://hackett.io/emmet_reilly,0.0487651569,68.16.181.120,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n15574,6,350,2017-03-02 04:04:58,http://emmerich.co/dominique_johns,0.1236202720,139.150.100.157,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n15575,6,350,2017-01-01 21:08:53,http://hoppe.name/mariana.hilll,0.0461736478,127.63.130.77,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n15576,6,350,2017-06-03 08:47:04,http://prohaska.info/zoey,0.4754812287,95.4.184.2,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n15577,6,350,2017-02-26 08:49:45,http://kilback.com/jerry,0.3968286383,58.110.106.190,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n15578,6,350,2017-03-29 20:29:47,http://brekkestreich.org/hailee,0.6179057588,190.165.53.217,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n15579,6,350,2017-02-23 02:33:32,http://huel.biz/izabella,0.8130352320,40.115.95.189,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n15580,6,350,2017-01-24 20:35:01,http://ferry.info/adonis.wunsch,0.9979639497,64.224.242.215,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n15581,6,350,2017-05-01 06:40:06,http://harvey.info/payton,0.6961932886,197.87.169.233,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n15582,6,350,2017-06-13 15:42:59,http://larkin.co/deron_wolf,0.5145331095,209.235.61.210,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n15583,6,350,2017-04-07 03:04:34,http://schamberger.net/furman,0.3325312333,166.121.117.222,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n15584,6,350,2017-02-09 02:05:21,http://hahn.info/janea,0.4238055755,126.222.42.27,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n2740,2,60,2017-02-15 09:25:39,http://balistreri.co/efrain.weber,,240.69.237.77,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n2741,2,60,2017-02-06 16:54:40,http://hackett.io/adrien,,254.28.227.205,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n2742,2,60,2017-03-12 13:46:28,http://renner.net/candido.raynor,,82.8.227.48,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n2743,2,60,2017-01-24 15:01:58,http://nadernicolas.io/michale,,49.10.200.6,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n2744,2,60,2016-12-14 09:50:32,http://kiehnshields.name/alvah,,112.237.199.112,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n2745,2,60,2017-03-19 01:45:20,http://zboncakstanton.org/muriel,,120.52.202.141,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n2746,2,61,2017-02-09 03:35:14,http://rippin.org/lavina_stanton,,155.213.4.84,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n2747,2,61,2017-04-02 11:06:47,http://leuschke.io/gustave,,250.250.253.117,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n2748,2,61,2017-03-27 10:44:38,http://collins.info/melvina,,177.100.105.235,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n2749,2,61,2016-12-27 16:45:39,http://gulgowski.co/herminia,,155.157.69.198,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n2750,2,61,2016-12-22 12:25:45,http://lakin.com/wyatt.lindgren,,94.106.236.199,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n2751,2,61,2017-03-08 04:29:25,http://turcottekemmer.net/garnett.wunsch,,28.224.240.211,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n2752,2,61,2017-03-08 13:51:50,http://mann.net/kenyon,,180.30.86.113,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n2753,2,61,2016-12-22 19:15:53,http://becker.org/orie,,158.81.9.76,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n2754,2,61,2017-02-22 07:58:06,http://hills.net/nicklaus_wolff,,91.203.193.144,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n2755,2,61,2017-05-12 06:51:14,http://gleasonhodkiewicz.biz/clement,,99.158.37.81,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n2756,2,61,2017-03-30 12:30:35,http://smith.name/rosalee,,79.2.100.161,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n2757,2,61,2017-03-26 19:23:47,http://mitchell.net/heather_hickle,,72.224.151.246,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n2758,2,61,2017-04-02 11:26:20,http://lemke.biz/marjolaine_hand,,12.39.11.185,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n2759,2,61,2017-04-17 06:52:11,http://jacobizboncak.com/kenny_bailey,,223.246.197.171,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n2760,2,61,2017-03-11 09:39:35,http://hermannschaden.com/benton_upton,,91.78.37.177,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n2761,2,61,2017-03-26 14:09:21,http://lockman.biz/hardy_lehner,,131.100.35.199,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n2762,2,61,2017-05-15 00:53:02,http://mclaughlinrogahn.com/merle,,142.57.34.201,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n2763,2,61,2017-06-05 06:42:10,http://hettinger.io/jeffry,,163.93.212.151,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n2764,2,61,2017-03-11 00:20:40,http://kris.co/elna,,94.136.128.61,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n2765,2,61,2017-04-29 12:38:55,http://abbottboyle.biz/marcelina,,203.114.254.17,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n2766,2,61,2017-03-31 20:33:05,http://erdman.io/raleigh,,46.162.112.217,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n2767,2,61,2016-12-31 03:58:31,http://ortiz.name/elizabeth_larson,,79.100.195.200,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n2768,2,61,2017-01-29 12:56:42,http://will.info/bill_terry,,79.22.164.163,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n2769,2,61,2017-01-23 13:47:31,http://handhalvorson.io/gunnar,,156.98.167.25,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n2770,2,61,2017-02-24 05:24:37,http://predovicdurgan.co/janet.heidenreich,,230.52.68.2,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n2771,2,61,2017-05-24 14:34:03,http://abshireberge.com/gino.tillman,,53.76.16.191,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n2772,2,61,2017-04-23 10:59:12,http://wisozkreinger.org/theresia.balistreri,,186.254.44.16,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n2773,2,61,2017-05-21 20:01:57,http://batzgerlach.net/jairo_kunze,,38.108.20.216,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n2774,2,61,2017-04-27 23:56:08,http://little.co/tremaine,,173.156.110.200,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n2775,2,61,2017-03-15 07:30:05,http://waters.info/evalyn.wiza,,9.191.58.206,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n2776,2,61,2017-01-14 22:35:04,http://fritschwolf.info/watson.jakubowski,,25.206.38.9,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n2777,2,61,2017-02-24 01:00:01,http://medhurst.info/brayan,,24.253.90.187,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n2778,2,61,2017-04-03 14:44:04,http://bechtelar.co/norberto_luettgen,,103.144.240.51,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n2779,2,61,2017-03-19 09:07:04,http://keebler.org/abraham,,65.12.35.25,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n2780,2,61,2017-05-24 18:48:23,http://oconnell.biz/justen,,69.81.176.194,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n2781,2,61,2017-06-03 14:21:16,http://wilkinson.net/aron,,156.120.221.155,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n2782,2,61,2017-03-28 23:34:40,http://labadie.com/titus.schinner,,122.63.103.244,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n2783,2,61,2017-04-28 09:05:55,http://douglas.net/ed.roberts,,101.204.59.163,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n2784,2,61,2017-02-23 06:36:13,http://zboncak.biz/cristina,,83.250.41.8,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n2785,2,61,2017-06-03 12:21:03,http://mccullough.com/rosendo_nikolaus,,53.161.200.88,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n2786,2,61,2017-01-08 20:24:08,http://mayer.name/ronaldo_quitzon,,182.9.234.123,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n2787,2,61,2017-06-04 19:49:34,http://pfannerstill.co/sammie_parker,,201.70.19.128,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n2788,2,61,2017-03-21 04:39:26,http://franecki.name/ethel,,48.80.145.180,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n2789,2,61,2017-03-21 12:52:00,http://kuhic.org/buford,,171.54.17.247,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n2790,2,61,2017-02-11 01:44:56,http://bogisichbauch.info/perry,,192.217.235.63,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n2791,2,61,2017-01-06 06:05:12,http://hartmann.net/monica.abshire,,22.212.54.75,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n2792,2,61,2016-12-20 02:10:09,http://nienow.io/duncan_altenwerth,,234.252.146.35,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n2793,2,61,2017-01-25 05:21:20,http://beahan.info/jamar.gaylord,,228.119.145.31,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n2794,2,61,2017-01-01 18:19:48,http://rennerrolfson.org/emmie.green,,8.165.11.63,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n2795,2,61,2017-01-11 06:38:58,http://feeney.org/adonis,,64.141.74.181,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n9675,4,217,2017-04-19 16:54:25,http://volkmanerdman.net/iva_klein,,160.118.41.61,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n9676,4,217,2016-12-17 07:40:32,http://feilkohler.net/gaetano_ankunding,,45.7.105.133,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n9677,4,217,2017-02-01 09:10:13,http://will.net/coralie_franecki,,130.221.57.134,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n9678,4,217,2016-12-20 21:55:13,http://lehnerweimann.net/jena,,140.107.202.98,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n9679,4,217,2016-12-16 07:52:57,http://zboncakwunsch.biz/emelie_braun,,99.231.11.136,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n9680,4,217,2016-12-23 05:38:47,http://huelerdman.co/theresia,,251.89.164.244,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n9681,4,217,2017-03-11 01:38:00,http://donnellykub.co/ruell.bins,,252.199.37.105,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n9682,4,217,2017-05-15 10:53:58,http://altenwerth.net/angeline.ferry,,158.136.176.207,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n9683,4,217,2017-01-01 22:59:53,http://bashirian.co/nathaniel.lesch,,116.237.55.241,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n9684,4,217,2017-01-11 15:06:36,http://hand.co/jovan,,136.5.79.121,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n9685,4,217,2017-01-19 07:56:33,http://bayer.io/alberta,,102.151.213.111,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n9686,4,217,2017-05-09 10:00:40,http://thiel.io/janiya.towne,,127.25.243.165,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n9687,4,217,2017-05-18 22:36:13,http://reynolds.com/hettie,,166.59.27.23,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n9688,4,217,2017-03-10 01:25:16,http://brekke.com/henderson_daugherty,,49.81.41.33,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n9689,4,217,2017-05-27 03:57:38,http://quigley.biz/avis.klocko,,141.134.13.187,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n9690,4,217,2016-12-19 15:56:52,http://leschnolan.biz/maud,,148.127.95.113,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n9691,4,217,2017-03-27 07:23:35,http://carroll.co/santos,,163.50.229.108,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n9692,4,217,2016-12-23 23:12:31,http://ernserklocko.name/montana_conroy,,142.8.220.203,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n9693,4,217,2017-04-13 16:35:08,http://paucek.net/ashley_howe,,87.242.73.145,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n9694,4,217,2016-12-29 15:31:47,http://turcotte.biz/ashly_goodwin,,103.174.26.198,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n9695,4,217,2016-12-25 22:47:34,http://runolfon.net/mallory_hettinger,,143.228.30.6,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n9696,4,217,2017-04-13 04:28:56,http://mertz.biz/maureen,,5.24.128.253,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n9697,4,217,2017-02-05 03:22:28,http://bahringer.name/alysha,,254.60.80.91,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n9698,4,217,2017-03-24 20:15:34,http://predovicprice.org/philip,,19.136.161.22,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n9699,4,217,2016-12-27 05:18:03,http://schimmelmclaughlin.com/jamie_treutel,,219.62.83.55,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n9700,4,217,2016-12-27 01:27:56,http://carrollshanahan.com/gardner,,70.150.203.237,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n9701,4,217,2017-01-25 16:38:02,http://fay.com/brock,,173.3.16.168,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n9702,4,217,2017-04-03 16:10:04,http://dibbert.io/kyle_willms,,75.68.222.104,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n9703,4,217,2017-04-24 07:45:47,http://bayer.com/finn,,37.18.191.135,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n9704,4,217,2017-05-23 00:00:54,http://considine.name/nora,,164.26.212.58,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n9705,4,217,2017-04-29 09:26:04,http://hilllbechtelar.net/dominic_crona,,69.233.20.45,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n9706,4,217,2017-03-20 12:23:51,http://lakin.org/brice_carter,,151.229.77.177,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n9707,4,217,2017-05-11 04:34:28,http://larkin.net/annie_leannon,,141.135.168.206,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n9708,4,217,2016-12-17 08:27:13,http://beer.com/dario,,41.77.60.118,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n9709,4,217,2017-01-10 19:35:46,http://strackeschaden.name/shaina,,29.120.204.72,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n9710,4,217,2017-03-17 01:42:10,http://rosenbaum.name/tania,,166.87.69.68,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n9711,4,218,2017-01-03 23:49:56,http://uptonjohnston.info/rollin,,203.173.14.127,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n9712,4,218,2017-04-27 11:02:25,http://kohler.biz/billie,,72.17.87.177,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n9713,4,218,2017-01-16 16:18:29,http://nolan.io/travis,,121.89.238.220,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n9714,4,218,2017-01-02 06:14:25,http://carter.net/bobbie_kuhlman,,232.87.36.208,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n9715,4,218,2017-03-20 03:00:11,http://walsh.biz/drake.nienow,,82.123.194.73,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n9716,4,218,2016-12-15 15:31:07,http://rohanwilkinson.info/stacey.torphy,,65.227.39.119,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n9717,4,218,2017-01-24 21:35:23,http://heaney.name/trystan,,120.207.179.210,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n9718,4,218,2017-02-21 05:08:54,http://carter.co/helen.beier,,60.150.108.54,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n9719,4,218,2016-12-19 09:48:01,http://herman.biz/alexandria_ryan,,249.30.31.239,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n9720,4,218,2017-04-01 16:41:56,http://wolfhickle.org/genoveva,,215.80.28.183,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n9721,4,218,2017-01-12 13:03:12,http://collier.com/holden.mueller,,180.228.58.53,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n9722,4,218,2016-12-29 22:01:58,http://gleichner.name/abbie,,98.71.108.108,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n9723,4,218,2017-01-15 23:23:07,http://hermiston.name/judy_friesen,,189.201.133.188,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n9724,4,218,2017-05-26 10:34:00,http://funk.name/katharina.dickinson,,63.159.211.208,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n9725,4,218,2017-05-16 23:04:45,http://leschjones.io/maurine_rau,,205.30.208.95,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n9726,4,218,2016-12-29 14:08:14,http://krajciklabadie.com/nasir,,118.108.138.79,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n9727,4,218,2017-01-05 23:05:03,http://marquardt.com/ila_baumbach,,30.112.236.197,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n9728,4,218,2017-06-08 04:15:29,http://baumbachlebsack.io/rachel,,148.193.229.224,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n9729,4,218,2016-12-21 08:41:02,http://funk.name/dangelo.runolfsdottir,,176.200.33.242,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n9730,4,218,2017-04-29 23:58:28,http://jastschultz.name/lillie_leannon,,74.54.65.176,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n6668,3,145,2017-02-28 09:55:35,http://rueckerabernathy.info/noe.mills,0.4560321096,154.116.95.164,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n6669,3,145,2016-12-22 00:09:26,http://hudson.biz/benedict,0.6738025853,209.175.238.219,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n6670,3,145,2017-06-04 08:02:12,http://bergnaum.biz/elroy,0.2338821147,157.98.99.169,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n6671,3,145,2017-01-12 06:29:12,http://dicki.io/jamie.blick,0.6181160679,17.56.199.174,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n6672,3,146,2016-12-16 14:31:59,http://kreiger.io/catalina,,193.173.244.166,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n6673,3,146,2017-05-27 00:01:06,http://gleichner.net/trystan.dickinson,,11.244.249.59,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n6674,3,146,2017-01-27 20:47:58,http://robel.info/ransom,,161.17.247.92,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n6675,3,146,2017-02-18 13:57:12,http://hoppe.org/bailey,,36.54.185.14,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n6676,3,146,2017-04-17 15:55:30,http://hansenmayert.co/julianne,,195.181.131.253,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n6677,3,146,2017-02-06 14:17:24,http://oconnerflatley.name/brittany.jakubowski,,157.34.111.46,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n6678,3,146,2017-02-16 05:22:54,http://beahanhoeger.name/larry.bernier,,33.50.161.158,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n6679,3,146,2017-01-21 04:09:48,http://mertzconnelly.net/danika,,45.37.55.45,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n6680,3,146,2017-05-21 22:53:11,http://koepp.net/laurine,,214.154.12.135,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n6681,3,146,2017-03-25 06:03:47,http://hettingerratke.biz/kellie_rolfson,,131.112.7.107,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n6682,3,146,2017-04-04 03:55:07,http://thielcrist.com/frederik.cormier,,177.167.80.18,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n6683,3,146,2017-06-10 06:05:55,http://lakin.net/raheem,,102.237.37.50,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n6684,3,146,2017-01-23 23:32:43,http://kozey.org/berniece_lubowitz,,56.209.13.7,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n6685,3,146,2017-01-08 11:05:32,http://weimann.co/tanya.sipes,,81.251.139.107,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n6686,3,146,2017-01-23 14:34:37,http://adams.net/ethel,,225.207.5.162,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n6687,3,146,2017-01-13 15:30:58,http://wolf.net/alvis,,249.143.153.247,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n6688,3,146,2016-12-26 01:38:16,http://boyerkuvalis.net/mallory,,130.54.216.161,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n6689,3,146,2017-04-11 10:42:24,http://schmidt.co/myrtie.hoeger,,70.196.126.205,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n6690,3,146,2017-03-17 01:58:38,http://vonhahn.com/marianne_hills,,185.64.126.37,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n6691,3,146,2017-01-16 21:40:28,http://runteward.io/robb,,71.141.37.234,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n6692,3,146,2017-04-05 00:46:46,http://hudson.co/kamryn,,18.16.230.208,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n6693,3,146,2017-05-08 00:26:02,http://smitham.net/leann_leuschke,,135.174.163.189,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n6694,3,147,2017-03-10 16:15:03,http://macejkovic.biz/nils,,32.164.129.36,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n6695,3,147,2017-01-03 22:24:13,http://frami.co/america,,228.2.127.67,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n6696,3,147,2017-05-15 23:52:59,http://haley.co/beau,,164.99.219.8,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n6697,3,147,2017-05-27 10:21:30,http://thompsonmurray.name/sophia,,201.48.128.142,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n6698,3,147,2017-05-09 20:37:23,http://hansenward.info/mya,,110.244.225.61,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n6699,3,147,2017-05-14 17:19:50,http://parkerhoppe.biz/keyshawn,,124.113.42.5,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n6700,3,147,2017-06-08 12:01:35,http://willruel.co/madilyn,,105.11.57.94,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n6701,3,147,2016-12-21 12:38:49,http://kuvalis.name/brice,,15.157.134.140,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n6702,3,147,2017-06-12 20:43:34,http://bogan.net/itzel,,70.3.239.205,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n6703,3,147,2016-12-18 17:46:39,http://schustergorczany.info/hettie.thiel,,151.208.176.224,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n6704,3,147,2017-04-02 12:16:35,http://schroederflatley.net/bianka_considine,,127.73.173.173,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n6705,3,147,2017-04-02 23:29:15,http://lowe.org/danial.hills,,20.58.36.150,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n6706,3,147,2017-03-10 19:03:47,http://langmcdermott.biz/benny_champlin,,18.27.85.200,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n6707,3,147,2017-01-03 15:26:07,http://wisoky.info/tommie.bergstrom,,55.12.14.140,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n6708,3,147,2017-03-23 06:54:17,http://lebsackkutch.biz/blaise_ward,,156.21.243.252,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n6709,3,147,2017-01-14 20:20:17,http://deckow.net/nels,,152.206.105.41,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n6710,3,147,2017-05-13 23:50:33,http://berge.io/owen_homenick,,181.251.186.125,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n6711,3,147,2017-03-14 10:59:54,http://lubowitz.info/delia,,155.236.132.222,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n6712,3,147,2017-02-03 16:26:11,http://weinatcarter.name/annamarie,,174.2.172.149,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n6713,3,147,2017-05-18 14:28:41,http://daugherty.info/delores,,14.113.87.18,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n6714,3,147,2017-04-20 12:17:06,http://marvin.co/vernice_klocko,,108.242.67.67,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n6715,3,147,2017-01-23 22:18:29,http://ruelwalker.info/matteo_harvey,,25.66.241.87,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n6716,3,147,2017-04-28 09:16:59,http://littel.io/kira.west,,224.176.19.105,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n6717,3,147,2017-05-28 04:51:47,http://moore.org/muriel,,88.130.214.81,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n6718,3,147,2016-12-23 15:53:01,http://ferry.io/carolyne_beier,,216.125.86.63,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n6719,3,147,2017-01-30 09:51:14,http://mayert.name/conner,,100.45.73.112,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n6720,3,147,2017-06-08 18:55:02,http://johnsmayert.name/magdalena.miller,,72.197.188.126,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n6721,3,147,2017-06-03 10:53:23,http://shanahan.org/mackenzie.skiles,,230.167.68.47,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n6722,3,147,2017-01-20 02:45:07,http://schaefer.com/ethan_murphy,,3.193.91.104,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n12583,5,281,2017-04-09 21:30:44,http://sanford.com/berenice_lakin,,148.85.57.76,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n12584,5,281,2017-05-26 06:53:26,http://harvey.io/america.cremin,,61.227.125.222,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n12585,5,281,2017-03-03 19:00:23,http://pollich.org/laurianne,,223.239.140.178,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n12586,5,281,2017-04-01 15:22:20,http://lednerdaniel.org/clyde.will,,189.72.228.177,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n12587,5,281,2017-01-07 01:42:42,http://murray.com/burley,,100.121.92.12,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n12588,5,281,2017-05-04 23:01:42,http://grady.org/mitchell,,56.242.110.59,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n12589,5,281,2017-04-26 10:15:15,http://homenick.info/maryam,,248.55.221.114,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n12590,5,281,2017-03-26 21:03:27,http://fay.biz/elwyn,,213.108.113.199,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n12591,5,281,2017-03-03 12:24:10,http://west.biz/joelle_murray,,18.224.73.248,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n12592,5,281,2017-01-23 20:18:44,http://pacocha.co/elyse.howe,,78.107.17.250,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n12593,5,281,2017-02-17 12:46:07,http://okeefe.co/haley.corkery,,167.145.39.10,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n12594,5,282,2017-02-04 19:02:37,http://fadel.io/frances,,249.220.233.162,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n12595,5,282,2017-01-13 05:57:31,http://cormierboehm.com/august,,149.87.21.238,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n12596,5,282,2017-04-20 06:24:02,http://willpowlowski.io/rosanna,,52.31.99.195,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n12597,5,282,2017-03-07 23:09:25,http://schultz.net/bailee.ryan,,173.88.171.198,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n12598,5,282,2017-01-03 04:29:22,http://bernhard.name/sid_hamill,,176.13.223.219,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n12599,5,282,2017-03-24 04:05:56,http://walterwehner.info/davion,,123.201.114.135,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n12600,5,282,2017-04-09 01:06:20,http://corwin.co/arnold_bergstrom,,254.22.129.167,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n12601,5,282,2017-03-11 00:21:33,http://littel.com/leilani,,246.98.20.200,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n12602,5,282,2016-12-29 14:57:31,http://mohrheller.biz/tommie.spencer,,82.60.243.207,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n12603,5,282,2017-04-04 08:01:11,http://huel.net/marcos,,107.16.130.216,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n12604,5,282,2017-03-27 01:00:05,http://gutmannharvey.info/miguel,,46.193.34.91,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n12605,5,282,2017-02-11 13:38:42,http://dooley.org/denis.becker,,229.156.139.180,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n12606,5,282,2017-04-16 23:06:56,http://hegmann.com/izaiah,,193.184.242.177,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n12607,5,282,2017-01-23 08:42:53,http://zulauf.io/geo,,57.28.194.234,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n12608,5,282,2017-01-21 18:13:25,http://legros.org/verner_rempel,,161.224.203.16,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n12609,5,282,2017-02-04 05:32:01,http://ernser.co/philip.dicki,,160.76.232.94,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n12610,5,282,2017-02-27 21:53:30,http://hayescronin.net/gonzalo_borer,,172.236.22.32,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n12611,5,282,2017-06-09 16:24:32,http://schimmel.info/garret.walsh,,108.246.52.89,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n12612,5,282,2017-04-04 03:48:42,http://turnerwalsh.io/albert,,18.122.100.181,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n12613,5,282,2017-04-20 17:27:16,http://murphy.io/josefina_rohan,,165.207.238.29,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n12614,5,282,2017-02-07 16:37:49,http://moen.net/griffin,,241.199.39.5,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n12615,5,282,2017-05-03 23:33:52,http://rippin.name/christa,,2.4.188.175,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n12616,5,282,2017-01-03 21:22:37,http://parisianwaelchi.org/willard_greenholt,,109.67.57.144,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n12617,5,282,2017-05-13 13:02:56,http://schneider.io/margot_lynch,,216.15.213.97,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n12618,5,282,2017-01-16 23:07:00,http://conroyherzog.io/margarita_bogan,,8.219.8.192,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n12619,5,282,2017-05-02 08:15:50,http://ebert.biz/marco.stiedemann,,71.149.78.205,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n12620,5,282,2017-01-20 22:43:38,http://haagsanford.net/bobbie.lubowitz,,169.148.187.213,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n12621,5,282,2017-05-10 07:30:43,http://reynoldskiehn.io/amparo,,222.51.58.142,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n12622,5,282,2017-03-30 00:45:23,http://runolfsdottir.org/salma.volkman,,49.83.118.33,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n12623,5,282,2017-03-31 12:33:11,http://romaguera.com/patsy,,28.143.189.196,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n12624,5,282,2017-02-14 06:13:24,http://williamson.biz/talia.hyatt,,146.135.225.236,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n12625,5,282,2017-02-11 00:47:26,http://aufderhar.com/ernestine,,75.233.40.126,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n12626,5,282,2017-06-03 18:52:39,http://torp.co/lonnie,,38.89.34.210,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n12627,5,282,2017-01-28 20:00:59,http://hoppe.biz/darrion,,98.129.170.155,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n12628,5,282,2017-01-30 00:48:44,http://dare.co/yoshiko,,98.144.160.86,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n12629,5,282,2017-02-16 05:59:48,http://hartmann.net/jazlyn.kilback,,203.207.214.143,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n12630,5,282,2017-03-26 23:50:33,http://heidenreich.biz/virgil,,187.114.18.112,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n12631,5,282,2017-05-24 14:00:09,http://raynor.biz/antwon.stroman,,146.228.161.200,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n12632,5,282,2016-12-18 20:17:55,http://durgan.io/henry_dare,,72.209.114.16,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n12633,5,282,2017-02-21 04:44:58,http://mayert.info/fannie_gutkowski,,124.148.11.238,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n12634,5,282,2017-02-25 22:17:47,http://williamsonziemann.co/keven.rowe,,117.221.204.186,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n12635,5,282,2016-12-19 01:36:33,http://price.name/evan,,125.142.138.74,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n12636,5,282,2017-04-16 06:09:24,http://maggio.biz/myriam,,220.193.145.128,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n12637,5,282,2017-03-07 12:43:52,http://nienowharber.io/vincenzo,,30.249.237.103,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n15585,6,350,2016-12-16 20:26:20,http://connsimonis.biz/dolly.marquardt,0.5958027443,75.37.100.169,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n15586,6,350,2017-02-09 21:06:00,http://wolff.org/kayden,0.3545751719,63.184.150.98,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n15587,6,350,2017-06-04 22:14:09,http://adamsheller.io/julie_rippin,0.9632084924,63.69.119.110,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n15588,6,350,2017-03-30 19:06:06,http://sipesbreitenberg.name/carlie,0.1117152522,223.77.99.93,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n15589,6,350,2017-01-05 13:56:29,http://champlinkertzmann.org/laron,0.6917430925,68.249.199.33,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n15590,6,350,2017-03-04 15:39:38,http://kilback.net/vesta.jerde,0.8532566458,104.161.107.85,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n15591,6,350,2016-12-19 15:53:50,http://farrelldubuque.name/florine_lang,0.7479824168,227.231.46.96,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n15592,6,350,2017-01-26 11:44:08,http://roberts.name/nikita_labadie,0.5523686205,131.82.33.85,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n15593,6,350,2017-01-05 05:48:01,http://schulistleannon.net/dangelo_murray,0.7505460364,202.168.14.27,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n15594,6,350,2017-05-16 06:35:41,http://heidenreichcremin.org/charley,0.5069874346,85.124.177.43,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n15595,6,350,2017-05-14 04:04:59,http://skilesruecker.net/leola,0.9897049271,88.251.253.120,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n15596,6,350,2017-05-06 01:24:29,http://rauwolf.name/ora_emard,0.3174065626,45.167.160.30,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n15597,6,351,2017-02-26 22:35:28,http://becker.biz/dariana_lueilwitz,,71.198.14.54,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n15598,6,351,2017-05-18 04:20:01,http://cummerata.org/nicholaus,,67.178.230.251,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n15599,6,351,2017-02-28 05:45:55,http://kuphalebert.co/dewitt,,231.67.170.244,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n15600,6,351,2017-04-01 01:24:39,http://lesch.name/karlie_hansen,,108.116.5.223,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n15601,6,351,2017-04-13 07:58:49,http://bode.info/chyna.hamill,,149.86.219.17,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n15602,6,351,2017-01-08 08:12:53,http://beatty.info/skye_langosh,,220.151.211.12,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n15603,6,351,2017-05-08 02:36:55,http://connpaucek.io/ulises_hegmann,,160.135.132.228,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n15604,6,351,2017-02-27 00:08:25,http://bergnaum.info/winston.cruickshank,,24.252.121.85,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n15605,6,351,2017-06-11 14:57:46,http://leannon.org/lori,,125.228.177.176,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n15606,6,351,2017-03-12 13:58:31,http://ratke.io/mireille_cummings,,207.95.206.103,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n15607,6,351,2017-05-11 08:52:07,http://cronin.name/asha,,220.250.70.88,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n15608,6,351,2017-03-28 02:16:17,http://dickensmacejkovic.biz/leonardo,,166.95.195.114,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n15609,6,351,2016-12-15 13:23:08,http://bartoletti.io/tito_oreilly,,124.126.215.54,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n15610,6,351,2017-02-20 06:32:58,http://stiedemann.name/shane,,252.41.65.181,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n15611,6,351,2017-03-25 22:23:56,http://davis.co/harrison,,141.193.103.232,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n15612,6,351,2017-05-31 12:45:35,http://kozey.co/morris,,67.74.122.61,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n15613,6,351,2017-03-02 14:44:52,http://quigley.com/lorena,,236.50.24.17,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n15614,6,351,2017-04-20 19:47:31,http://orn.com/leonora_hand,,182.5.132.26,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n15616,6,351,2017-05-05 22:44:42,http://buckridge.net/deshaun.lindgren,,199.184.254.233,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n15617,6,351,2017-01-18 15:39:42,http://ruel.co/dale.waelchi,,159.226.80.132,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n15618,6,351,2017-06-07 13:24:00,http://dickinson.com/abbey,,23.62.9.20,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n15619,6,351,2016-12-26 23:29:14,http://watsica.net/armand_west,,61.36.246.124,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n15620,6,351,2016-12-31 07:49:55,http://simonis.info/sabryna,,107.231.236.15,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n15621,6,352,2017-03-18 23:16:48,http://bechtelar.org/jordi_wisozk,,192.32.64.108,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n15622,6,352,2017-05-26 03:56:08,http://kulascormier.name/cleta,,122.61.207.160,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n15623,6,352,2017-01-30 14:04:07,http://bosco.biz/joanne,,237.216.15.95,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n15624,6,352,2017-04-09 16:43:35,http://quitzon.co/kristofer_frami,,133.52.224.17,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n15625,6,352,2017-04-26 02:18:32,http://jastgrimes.co/vincenzo.kirlin,,47.96.115.132,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n15626,6,352,2017-05-23 12:15:59,http://brownstoltenberg.com/maud,,64.116.29.38,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n15627,6,352,2017-03-24 22:36:12,http://wisokykonopelski.io/aisha.brakus,,92.139.250.123,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n15628,6,352,2017-04-04 19:52:16,http://erdman.name/gaston,,153.170.167.221,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n15629,6,352,2017-06-13 14:21:29,http://osinski.biz/cletus,,227.85.87.41,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n15630,6,352,2016-12-15 07:12:34,http://wisoky.co/cleta.rodriguez,,169.209.26.112,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n15631,6,352,2017-02-14 06:20:18,http://emard.info/adonis.fahey,,61.9.22.49,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n15632,6,352,2017-03-01 09:51:44,http://sporer.info/eusebio,,96.41.122.209,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n15633,6,352,2017-03-04 16:35:29,http://faheyfunk.net/carol_hauck,,8.6.173.37,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n15634,6,352,2016-12-20 06:29:21,http://hansenschimmel.biz/shanna,,24.235.129.178,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n15635,6,352,2017-06-09 01:34:37,http://kihn.io/gwendolyn.gislason,,144.196.138.46,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n15636,6,352,2017-04-13 16:29:59,http://ernser.com/forrest.zemlak,,31.88.68.86,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n15637,6,352,2017-02-01 15:37:46,http://veumparisian.io/lemuel,,14.202.165.74,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n15638,6,352,2017-04-29 02:02:33,http://roob.org/nicholas.toy,,231.165.14.223,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n15639,6,352,2017-02-27 22:13:23,http://langshanahan.org/ellen_stiedemann,,100.145.172.71,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n15640,6,352,2017-06-03 23:29:48,http://ward.biz/emilia,,241.240.230.78,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n2796,2,61,2017-04-23 21:29:46,http://rippinkuhn.com/christelle,,233.77.35.106,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n2797,2,61,2017-03-25 08:41:17,http://lindschuppe.org/ellie,,19.110.87.176,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n2798,2,61,2017-02-21 07:19:37,http://rodriguezborer.org/kaleigh.wyman,,87.50.189.21,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n2799,2,61,2017-05-20 09:52:36,http://beier.name/lupe.hauck,,188.228.37.149,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n2800,2,61,2017-02-19 02:22:40,http://cummings.com/jordan_ebert,,174.251.252.109,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n2801,2,61,2016-12-13 22:53:20,http://durgan.name/matt,,22.216.243.81,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n2802,2,61,2017-05-05 08:51:05,http://walsh.name/dina,,31.91.111.210,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n2803,2,61,2017-02-19 06:16:07,http://cole.biz/forrest_monahan,,173.195.224.251,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n2804,2,61,2017-06-13 18:47:50,http://lehner.org/tara,,92.228.108.198,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n2805,2,61,2017-05-06 02:07:41,http://bradtke.net/theodore,,45.132.239.87,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n2806,2,61,2016-12-14 04:10:36,http://nolan.io/stephen,,104.133.94.89,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n2807,2,61,2017-02-05 09:05:38,http://hintz.info/aylin,,8.23.147.198,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n2808,2,61,2017-01-15 23:05:09,http://spencerabshire.co/gudrun,,7.52.185.108,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n2809,2,61,2017-04-10 12:01:54,http://kutch.io/dahlia,,121.210.92.67,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n2810,2,61,2017-05-24 21:27:58,http://borerstehr.net/eileen,,163.249.173.29,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n2811,2,62,2017-03-23 12:09:59,http://lang.com/odea,,155.120.25.93,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n2812,2,62,2017-03-28 01:38:04,http://nadermccullough.io/rafael.murphy,,184.231.65.140,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n2813,2,62,2016-12-14 12:00:51,http://adams.org/markus,,248.87.115.35,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n2814,2,62,2017-02-04 01:07:26,http://ruellegros.com/nora_roob,,39.216.55.243,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n2815,2,62,2017-02-27 15:41:26,http://volkman.biz/lauriane,,243.227.233.16,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n2816,2,62,2017-03-22 23:12:20,http://prohaskakonopelski.biz/zachariah_gutkowski,,42.118.248.238,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n2817,2,62,2017-01-30 08:25:31,http://brekke.info/reagan_macgyver,,24.34.9.200,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n2818,2,62,2017-05-12 04:52:01,http://kozeypouros.info/zakary,,214.179.65.36,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n2819,2,62,2017-04-12 10:21:40,http://white.name/cade,,21.42.67.127,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n2820,2,62,2017-03-09 21:32:38,http://rodriguez.org/adan_kirlin,,18.237.6.24,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n2821,2,62,2017-02-07 17:43:50,http://rempel.co/annabelle,,103.86.224.168,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n2822,2,62,2017-03-03 16:58:28,http://champlinkrajcik.info/robb_bode,,188.165.18.25,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n2823,2,62,2017-01-18 10:44:31,http://runolfsdottir.co/mario.turcotte,,122.208.117.82,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n2824,2,62,2017-06-05 17:59:00,http://oreillydouglas.io/crystel.stehr,,244.115.21.195,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n2825,2,62,2017-02-02 07:50:39,http://erdman.name/manuela,,116.193.40.175,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n2826,2,62,2017-01-01 07:31:27,http://zboncak.name/effie,,178.140.120.50,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n2827,2,62,2017-02-27 00:22:45,http://smithklein.com/clement_haley,,51.81.101.193,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n2828,2,62,2017-02-01 00:54:06,http://ziemelehner.name/heidi_little,,181.228.6.50,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n2829,2,62,2017-04-02 07:50:38,http://trompschamberger.io/evert,,75.47.13.180,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n2830,2,62,2017-05-14 02:39:33,http://marquardtkuvalis.biz/cayla,,131.51.63.53,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n2831,2,62,2017-01-25 20:12:52,http://roob.name/ezra.bergstrom,,227.244.64.46,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n2832,2,62,2017-02-06 09:41:44,http://witting.info/jordy.carroll,,181.68.200.91,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n2833,2,62,2017-01-08 08:35:13,http://wolff.net/laisha_lindgren,,24.34.24.210,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n2834,2,62,2017-03-11 03:30:54,http://zieme.co/violet_schuster,,236.184.6.169,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n2835,2,62,2017-04-13 02:24:45,http://emard.biz/omari.kilback,,113.117.86.5,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n2836,2,62,2017-04-20 10:38:19,http://schambergerchristiansen.co/dylan_sporer,,217.197.136.180,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n2837,2,62,2016-12-26 11:07:17,http://stehr.co/anibal,,70.176.204.226,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n2838,2,62,2017-05-31 22:42:55,http://graham.info/victor_kirlin,,150.227.210.227,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n2839,2,62,2016-12-21 09:13:40,http://mayertko.io/kane,,40.203.231.253,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n2840,2,62,2017-02-13 03:30:53,http://faheywolff.org/asa,,36.99.3.21,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n2841,2,62,2017-05-01 06:20:59,http://hintz.biz/una.cremin,,238.104.50.63,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n2842,2,63,2017-03-23 23:47:16,http://hettingercrona.biz/hanna,,172.56.141.44,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n2843,2,63,2017-03-09 15:35:43,http://torphy.org/jackeline.ledner,,84.82.22.124,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n2844,2,63,2017-04-09 12:31:37,http://mcdermott.io/maeve_stanton,,186.17.116.177,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n2845,2,63,2017-03-02 01:11:49,http://hermiston.io/harrison_wisozk,,241.192.199.92,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n2846,2,63,2017-01-29 14:11:53,http://simonis.net/ryder,,15.109.252.126,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n2847,2,63,2016-12-23 12:41:35,http://durganrunolfsdottir.name/winifred_graham,,175.189.90.107,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n2848,2,63,2017-06-03 19:06:25,http://berge.com/roselyn.crooks,,251.125.157.62,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n2849,2,63,2017-01-18 17:15:05,http://macgyverbechtelar.io/keeley,,90.157.196.215,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n2850,2,63,2017-06-09 08:04:21,http://kling.com/laurence_mclaughlin,,67.121.6.233,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n2851,2,63,2017-01-02 02:18:55,http://jastbahringer.info/lisette.gulgowski,,202.35.200.231,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n9731,4,218,2017-02-20 15:20:35,http://kelerquitzon.io/kayli,,249.7.45.131,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n9732,4,218,2017-05-28 20:53:30,http://oconnell.com/kathryne,,81.107.182.147,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n9733,4,218,2017-04-13 00:09:10,http://hansen.net/shaina,,20.149.24.126,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n9734,4,218,2017-01-23 12:50:49,http://swaniawski.io/jackson,,203.48.193.226,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n9735,4,218,2017-03-16 08:52:56,http://hirthestracke.org/floyd.dicki,,128.107.31.184,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n9736,4,218,2016-12-22 04:43:35,http://raynor.co/sydni,,92.218.108.81,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n9737,4,218,2017-02-19 23:30:08,http://hettinger.co/aglae,,19.247.198.94,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n9738,4,218,2017-05-25 04:43:04,http://champlin.net/eladio.kilback,,43.27.196.25,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n9739,4,218,2017-01-09 18:19:19,http://weberoconnell.co/simone.schmeler,,24.34.178.172,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n9740,4,218,2017-05-25 05:39:56,http://kirlin.info/otis_ko,,66.136.74.113,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n9741,4,218,2017-02-24 05:59:25,http://mcglynnrowe.com/andy.strosin,,31.65.217.107,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n9742,4,218,2017-01-06 16:16:40,http://dubuque.io/melany.wolf,,202.205.78.105,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n9743,4,218,2017-03-17 09:34:30,http://lebsack.info/maritza,,57.215.91.163,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n9744,4,218,2017-04-19 20:07:55,http://johnson.info/allie,,189.58.26.216,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n9745,4,218,2016-12-17 13:55:57,http://pfeffer.net/jaquan,,120.66.103.103,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n9746,4,218,2017-01-19 04:23:39,http://kunde.name/milan,,196.51.164.53,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n9747,4,218,2017-05-03 18:56:28,http://nienowturcotte.info/erna,,9.128.185.50,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n9748,4,218,2016-12-15 08:08:24,http://huel.io/shanon.gulgowski,,81.8.139.66,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n9749,4,218,2017-02-17 16:19:30,http://wilkinson.name/andre.emard,,141.134.111.19,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n9750,4,218,2017-04-01 19:31:36,http://yostspinka.io/earnestine.sanford,,113.79.136.7,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n9751,4,218,2017-05-31 18:40:01,http://hilpert.info/rachelle.bogisich,,247.15.205.41,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n9752,4,218,2016-12-17 19:21:41,http://gorczany.name/marjolaine.howe,,100.39.190.196,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n9753,4,218,2017-03-09 08:17:42,http://becker.com/jaydon.paucek,,155.6.2.107,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n9754,4,218,2017-03-19 05:44:44,http://hoeger.co/caleigh,,138.237.13.241,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n9755,4,219,2017-02-03 14:23:07,http://kohlerwalter.co/chanelle,,76.196.161.249,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n9756,4,219,2017-05-14 01:43:40,http://moriettehirthe.name/raleigh.strosin,,19.193.4.175,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n9757,4,219,2017-03-03 06:43:26,http://darewolff.info/dylan,,203.84.214.156,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n9758,4,219,2017-05-19 00:05:10,http://miller.com/leila.kihn,,11.193.75.77,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n9759,4,219,2017-05-24 09:30:11,http://hane.io/oscar,,118.86.33.244,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n9760,4,219,2017-04-04 04:13:34,http://cristschmeler.org/savannah_schoen,,208.137.247.4,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n9761,4,219,2017-06-02 10:14:37,http://marquardt.biz/lia_schowalter,,2.214.214.169,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n9762,4,219,2017-06-02 02:52:58,http://simonisgoldner.biz/annabelle,,237.139.208.193,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n9763,4,219,2017-02-17 20:57:51,http://parisiankunde.biz/amie_nitzsche,,86.76.161.175,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n9764,4,219,2016-12-22 08:47:38,http://schmidt.net/trent.mohr,,190.209.99.249,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n9765,4,219,2017-02-22 03:47:15,http://goldner.io/gerald,,90.245.133.181,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n9766,4,219,2017-04-21 07:17:25,http://king.co/jackeline,,188.212.229.61,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n9767,4,219,2017-02-13 01:56:29,http://gorczanysporer.name/dena.jast,,177.74.17.14,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n9768,4,219,2017-01-03 00:26:54,http://dickinson.org/mateo.reynolds,,50.92.4.247,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n9769,4,219,2017-03-20 20:58:53,http://jast.name/herbert_kreiger,,155.142.70.158,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n9770,4,219,2017-03-14 11:20:58,http://vonrueden.net/sister_jacobi,,34.107.41.203,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n9771,4,219,2017-04-30 02:30:33,http://schmitt.io/randy,,247.178.124.222,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n9772,4,219,2017-02-16 06:19:33,http://bednarnolan.io/linnie,,162.236.80.172,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n9773,4,219,2017-03-05 13:20:52,http://boehm.org/susanna,,11.8.182.191,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n9774,4,219,2017-06-06 14:18:05,http://mosciski.name/constantin.lang,,88.194.247.242,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n9775,4,219,2017-06-11 02:06:09,http://mante.info/ebba.quigley,,249.165.104.26,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n9776,4,219,2016-12-26 04:07:35,http://reynolds.name/stephan,,204.179.220.164,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n9777,4,219,2017-01-25 07:39:06,http://medhurst.name/andres_schimmel,,207.118.20.154,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n9778,4,219,2017-05-26 15:55:03,http://brekke.org/walton,,145.69.75.200,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n9779,4,219,2017-06-03 23:22:40,http://andersonkreiger.co/eden,,118.107.132.48,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n9780,4,219,2017-04-19 20:16:50,http://crona.com/pasquale_padberg,,233.247.128.87,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n9781,4,219,2017-01-02 17:51:32,http://bergstrom.biz/gregoria,,13.71.148.214,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n9782,4,219,2017-05-08 14:38:23,http://nicolas.net/cedrick,,99.10.4.164,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n9783,4,219,2017-02-14 12:12:11,http://tremblay.info/tom_becker,,56.108.233.49,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n9784,4,219,2017-05-31 15:22:00,http://johnsonheller.com/katrina,,97.73.19.224,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n9785,4,219,2017-05-07 12:15:46,http://bauch.org/therese,,111.11.247.57,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n9786,4,219,2017-06-08 09:41:46,http://hermann.com/odea,,16.122.216.230,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n6723,3,147,2017-05-02 20:30:54,http://marquardt.co/elta_wilderman,,100.5.6.39,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n6724,3,147,2017-01-27 23:43:00,http://swaniawskikreiger.co/colt,,120.10.214.240,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n6725,3,147,2017-06-08 10:34:30,http://ritchie.net/frederik_goyette,,6.56.116.172,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n6726,3,147,2017-06-01 22:52:46,http://daniel.org/mallory_howell,,169.132.94.199,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n6727,3,147,2017-04-17 20:20:09,http://schoen.info/joe,,99.14.71.238,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n6728,3,147,2017-02-21 13:00:41,http://ratkefarrell.org/esteban.green,,146.51.46.172,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n6729,3,147,2017-03-18 08:26:36,http://blandadurgan.co/kyleigh.mueller,,107.48.176.117,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n6730,3,147,2016-12-29 08:33:57,http://mcglynn.biz/una,,87.187.56.190,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n6731,3,147,2017-01-07 08:49:08,http://swaniawski.name/pascale.hickle,,75.17.204.225,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n6732,3,147,2017-04-03 23:27:38,http://bruen.info/angeline,,157.199.101.185,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n6733,3,147,2017-05-25 10:30:45,http://kulas.io/ervin,,251.169.204.70,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n6734,3,147,2017-01-02 13:36:05,http://rau.com/aunta_fisher,,186.165.123.59,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n6735,3,147,2017-01-09 16:56:27,http://vonruedenrunolfon.co/lilly,,98.95.114.242,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n6736,3,147,2017-03-01 21:17:11,http://prohaskawiegand.io/tia_dibbert,,112.137.155.197,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n6737,3,147,2017-05-11 14:48:46,http://mann.biz/rosendo.zulauf,,51.229.191.121,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n6738,3,147,2017-01-18 15:22:14,http://wittinghaley.co/lauriane,,120.49.182.187,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n6739,3,147,2017-04-14 07:51:58,http://eichmann.biz/trystan,,95.185.83.16,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n6740,3,147,2017-05-11 14:48:57,http://carterbechtelar.net/clementine,,247.177.236.63,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n6741,3,147,2017-02-01 05:19:24,http://conn.name/wanda_oreilly,,52.46.63.254,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n6742,3,147,2017-03-13 17:04:23,http://vandervort.net/cicero_murazik,,252.42.193.221,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n6743,3,148,2017-05-26 15:38:57,http://beer.co/roma,,72.38.185.225,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n6744,3,148,2017-06-09 23:55:58,http://stoltenberg.biz/jedediah,,76.102.30.212,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n6745,3,148,2017-02-24 16:10:47,http://yundtzboncak.co/quentin_jast,,68.149.191.13,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n6746,3,148,2016-12-18 10:53:03,http://morar.info/margarette,,204.12.250.211,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n6747,3,148,2017-05-05 03:35:35,http://roobherzog.org/jacinthe_skiles,,116.229.20.177,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n6748,3,148,2017-03-29 08:58:09,http://oreilly.co/elmira,,199.199.149.8,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n6749,3,148,2017-03-20 03:33:08,http://harber.biz/merle,,135.95.233.79,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n6750,3,148,2017-01-22 09:49:13,http://dietrichjaskolski.info/aniyah,,131.232.24.32,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n6751,3,148,2017-04-01 07:41:05,http://mannankunding.info/geovany,,168.231.7.48,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n6752,3,148,2017-03-21 12:47:30,http://kuhlmandoyle.name/uriel,,43.37.125.232,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n6753,3,148,2017-01-03 22:43:38,http://hintz.com/emerson,,222.104.239.61,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n6754,3,148,2017-01-27 20:36:01,http://mckenzietorphy.biz/ellis_nicolas,,233.162.94.143,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n6755,3,148,2017-03-27 11:28:52,http://bogisich.biz/providenci.mosciski,,108.193.82.226,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n6756,3,148,2017-04-21 17:14:20,http://gutmann.io/alf.hyatt,,47.127.220.253,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n6757,3,148,2017-05-08 04:45:33,http://bosco.biz/reanna,,171.225.232.132,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n6758,3,148,2017-03-03 10:37:45,http://feest.com/martine,,214.11.26.12,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n6759,3,148,2017-01-03 10:33:26,http://lednerkoepp.com/william,,200.151.193.199,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n6760,3,148,2017-05-13 16:37:57,http://grant.biz/ivah,,187.135.90.64,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n6761,3,148,2017-03-06 17:00:26,http://kovacek.biz/kory.reinger,,99.105.53.174,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n6762,3,148,2017-03-26 01:21:36,http://mcculloughschuppe.biz/jordane,,22.108.121.156,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n6763,3,148,2016-12-22 23:29:16,http://oconnell.info/kallie.kling,,238.204.144.63,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n6764,3,148,2017-03-02 09:17:57,http://reichel.com/gage_hackett,,87.135.193.55,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n6765,3,148,2017-04-09 07:03:25,http://west.co/elya,,193.51.20.129,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n6766,3,148,2017-03-28 01:16:40,http://eichmann.info/jeremie,,52.224.116.32,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n6767,3,148,2017-03-31 17:43:50,http://haagbradtke.com/amelia.little,,214.90.190.56,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n6768,3,148,2017-01-11 14:13:30,http://wunschreichel.org/bennett_kling,,97.249.138.92,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n6769,3,148,2017-03-15 10:19:52,http://rosenbaumanderson.org/mckenna.price,,241.133.119.119,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n6770,3,148,2017-02-06 00:43:13,http://rempel.biz/noemi.cartwright,,5.170.191.33,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n6771,3,148,2017-04-04 15:09:27,http://sauer.io/eleonore.hyatt,,113.178.136.243,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n6772,3,148,2017-05-09 11:50:36,http://hickle.co/enid,,62.94.241.136,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n6773,3,148,2017-01-03 02:46:33,http://farrelltorp.com/twila,,93.207.172.158,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n6774,3,149,2017-05-23 13:55:29,http://spencer.name/whitney.hayes,,93.179.68.107,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n6775,3,149,2017-02-25 10:30:08,http://smith.com/claire_kunde,,176.115.213.182,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n6776,3,149,2017-02-25 03:56:55,http://yundt.info/tracy,,58.226.242.141,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n6777,3,149,2017-05-01 20:58:22,http://block.co/anthony,,126.203.182.209,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n6778,3,149,2017-02-25 21:29:58,http://fadelhamill.co/greta,,50.60.81.24,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n12638,5,282,2017-04-21 03:05:51,http://beerharvey.com/quentin,,60.60.26.234,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n12639,5,282,2017-05-23 06:14:50,http://donnellyshanahan.com/roscoe_nitzsche,,151.60.190.123,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n12640,5,282,2017-03-06 12:06:48,http://will.io/emilio.berge,,158.106.77.204,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n12641,5,282,2017-03-21 12:02:28,http://klocko.net/daniella,,155.30.101.205,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n12642,5,282,2017-04-26 05:09:00,http://osinskiratke.com/christa,,23.183.82.67,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n12643,5,282,2017-03-01 23:49:44,http://kshlerin.org/abner,,22.120.95.2,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n12644,5,282,2017-02-16 19:53:40,http://willms.io/garrison,,94.198.58.175,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n12645,5,282,2017-03-01 10:51:08,http://lind.net/josie,,254.78.210.48,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n12646,5,282,2017-05-10 08:33:03,http://wisoky.biz/dalton.kuvalis,,27.140.115.93,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n12647,5,282,2017-03-15 14:23:33,http://reinger.net/lenna_mann,,233.82.92.26,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n12648,5,282,2016-12-18 12:45:22,http://ryan.info/selina,,221.108.41.20,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n12649,5,282,2017-04-11 02:02:19,http://okeefe.io/vito.rosenbaum,,46.89.78.144,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n12650,5,282,2017-04-07 22:15:18,http://blanda.info/zoie_dickinson,,137.176.82.202,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n12651,5,282,2017-01-05 02:31:03,http://macgyver.co/otho_olson,,229.225.163.190,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n12652,5,282,2017-01-29 15:22:42,http://kotreutel.org/alford.zieme,,8.45.8.117,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n12653,5,282,2017-04-01 06:15:14,http://rowemcclure.org/leonard,,211.238.236.157,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n12654,5,282,2017-01-02 18:24:25,http://weimann.name/westley_hahn,,105.203.42.5,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n12655,5,282,2017-01-09 15:37:34,http://reillyhagenes.com/chelsey,,204.244.110.169,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n12656,5,282,2017-06-13 19:18:39,http://hermiston.info/enos,,17.42.233.119,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n12657,5,282,2017-04-12 22:51:25,http://auerdibbert.biz/kris,,166.100.133.65,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n12658,5,283,2017-05-22 14:45:52,http://sanford.biz/itzel_abernathy,,98.80.16.74,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n12659,5,283,2017-01-05 07:58:12,http://schulist.biz/ezequiel,,137.20.123.216,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n12660,5,283,2017-05-14 07:27:05,http://jaskolski.biz/jaylon,,48.77.203.18,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n12661,5,283,2017-03-28 10:18:06,http://rosenbaum.name/reyna,,12.151.195.196,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n12662,5,283,2017-06-05 10:45:40,http://cremin.info/bernadette,,208.148.198.189,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n12663,5,283,2017-01-13 11:02:45,http://batz.com/payton_herzog,,193.6.151.153,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n12664,5,283,2017-06-12 08:20:21,http://cummings.info/aliyah.ohara,,95.2.166.158,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n12665,5,283,2017-04-03 18:55:41,http://koepp.com/emory,,202.198.162.108,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n12666,5,283,2017-04-16 05:24:51,http://schmeler.net/velda,,195.231.187.59,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n12667,5,283,2017-05-15 14:44:25,http://turnerstokes.biz/elfrieda,,168.186.153.194,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n12668,5,283,2017-06-08 15:51:59,http://mckenzie.com/valentina_hettinger,,209.149.121.82,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n12669,5,283,2017-02-06 10:40:12,http://simonisreynolds.net/margarete_frami,,104.20.43.197,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n12670,5,283,2016-12-16 15:49:16,http://bruen.info/alice_hauck,,94.203.37.149,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n12671,5,283,2017-02-13 15:21:59,http://blockberge.io/jordi,,156.128.206.209,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n12672,5,283,2017-06-10 19:01:19,http://lehner.com/jewel,,118.76.109.3,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n12673,5,283,2017-06-05 10:33:02,http://koepp.name/jamey,,132.68.78.153,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n12674,5,283,2017-05-12 18:25:45,http://olsonbechtelar.org/jennings,,33.213.140.97,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n12675,5,283,2016-12-28 20:15:03,http://green.biz/keven.hodkiewicz,,145.101.241.28,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n12676,5,283,2017-05-31 12:28:05,http://gutmann.com/jonatan_hyatt,,67.117.132.176,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n12677,5,283,2017-02-16 07:09:34,http://cremin.org/christop,,185.44.78.142,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n12678,5,283,2017-01-25 04:14:44,http://grimes.info/maude,,213.211.65.222,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n12679,5,283,2017-05-28 18:02:45,http://roob.net/gregorio.nolan,,44.16.234.13,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n12680,5,283,2017-04-22 18:50:30,http://ryanschiller.info/guiseppe,,9.59.160.216,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n12681,5,283,2017-03-30 10:58:20,http://kemmerbosco.biz/layla.jerde,,199.90.150.138,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n12682,5,283,2017-02-16 02:23:24,http://wunsch.biz/cary_kilback,,231.26.193.67,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n12683,5,283,2017-01-01 17:42:00,http://okeefe.biz/milo,,181.175.110.237,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n12684,5,283,2017-05-23 13:08:14,http://lehner.biz/susanna.leuschke,,58.191.91.176,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n12685,5,283,2017-05-17 15:38:04,http://dietrich.co/lenore.macgyver,,56.132.157.83,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n12686,5,283,2017-06-12 05:03:55,http://smitham.org/barton.homenick,,119.167.138.171,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n12687,5,283,2017-05-31 04:14:03,http://morar.info/jeramy,,181.226.56.58,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n12688,5,283,2017-06-06 10:10:34,http://johnson.io/darian_fay,,54.68.129.242,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n12689,5,283,2017-02-08 05:32:48,http://heller.net/stephan,,89.32.150.92,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n12690,5,283,2016-12-25 03:32:46,http://mckenzie.co/merle.moore,,162.252.223.108,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n12691,5,283,2016-12-28 06:33:48,http://lind.name/casimir_anderson,,137.232.61.205,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n12692,5,283,2017-05-13 05:03:19,http://mraz.io/sigmund,,3.197.215.104,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n12693,5,283,2017-04-04 06:45:58,http://nolanrutherford.com/jazmyne,,137.12.182.206,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n15641,6,352,2017-03-22 10:26:14,http://pollich.net/pat,,188.61.63.58,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n15642,6,352,2017-04-03 01:36:56,http://thiel.net/reid,,82.41.221.49,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n15643,6,352,2017-01-07 09:02:14,http://tillmansawayn.com/marisol_herman,,61.138.6.2,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n15644,6,352,2017-05-06 06:38:37,http://dickens.com/ashton,,238.156.179.135,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n15645,6,352,2017-05-03 08:23:11,http://greenholt.biz/burley_conroy,,183.32.196.171,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n15646,6,352,2017-04-22 10:27:56,http://bernhard.biz/duane.schmeler,,35.146.182.251,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n15647,6,352,2017-01-31 14:55:50,http://gulgowski.io/sibyl.hagenes,,81.149.244.71,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n15648,6,352,2017-01-07 05:57:35,http://will.net/anabelle_turner,,86.253.233.107,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n15649,6,352,2017-02-01 22:59:19,http://oconner.org/loy,,84.32.77.20,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n15650,6,352,2017-01-20 17:20:24,http://koelpinullrich.org/jaylin,,214.101.176.117,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n15651,6,352,2017-01-17 20:14:21,http://bradtke.biz/jaron,,164.62.73.241,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n15652,6,352,2017-03-15 18:48:10,http://reilly.org/trenton,,139.23.59.244,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n15653,6,352,2017-04-08 05:51:56,http://wunsch.name/amie.pfannerstill,,66.159.139.208,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n15654,6,352,2017-05-05 15:52:06,http://mertz.info/vincenza.greenholt,,253.71.131.50,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n15655,6,352,2017-01-08 08:53:09,http://andersonstamm.net/kaitlyn,,3.132.182.72,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n15656,6,352,2017-04-03 07:11:55,http://kulas.io/kailee,,115.173.180.185,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n15657,6,352,2017-01-17 00:54:22,http://bergstrom.name/justyn,,201.28.119.175,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n15658,6,352,2017-01-15 23:49:10,http://hintzratke.net/liza,,54.112.118.94,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n15659,6,352,2017-04-16 17:58:27,http://kunze.io/colby,,192.26.52.234,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n15660,6,352,2017-03-31 23:08:13,http://lind.biz/annabelle,,241.131.137.60,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n15661,6,352,2017-01-25 02:19:19,http://kling.name/carlos.cruickshank,,241.50.16.106,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n15662,6,352,2017-01-31 12:16:46,http://lueilwitz.biz/cullen.runolfon,,47.137.58.230,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n15663,6,352,2017-05-23 16:26:24,http://trantowmoore.name/nathanial,,213.67.228.221,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n15664,6,352,2017-01-18 18:28:50,http://kozeybeer.name/birdie,,178.20.86.119,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n15665,6,352,2017-05-12 00:56:49,http://kaulke.com/rahsaan,,125.2.8.27,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n15666,6,352,2017-02-24 05:55:42,http://kirlin.net/elvis.marquardt,,186.165.101.115,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n15667,6,352,2017-02-03 05:04:27,http://fahey.name/johnny.mante,,10.240.151.250,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n15668,6,352,2017-02-14 16:24:24,http://hauck.net/eleanore,,34.229.111.226,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n15669,6,352,2017-05-14 00:45:14,http://johnsonkuhic.name/raina.koch,,85.104.57.7,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n15670,6,352,2017-04-18 02:16:53,http://senger.io/ezequiel.schmitt,,91.101.183.20,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n15671,6,352,2017-03-21 15:45:42,http://stromanschmeler.info/tyshawn.kemmer,,48.14.11.177,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n15672,6,353,2017-03-27 03:13:57,http://williamson.co/porter,,232.168.138.89,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n15673,6,353,2017-01-23 03:28:11,http://purdyveum.co/luz_buckridge,,6.191.233.7,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n15674,6,353,2017-04-02 23:28:36,http://erdman.co/elizabeth,,189.132.15.229,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n15675,6,353,2017-03-31 13:17:52,http://roobrohan.info/nels_kertzmann,,174.58.222.85,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n15676,6,353,2016-12-25 17:02:46,http://bogisichrippin.biz/vita,,174.11.252.227,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n15677,6,353,2017-02-27 09:32:12,http://rodriguez.org/arlo_schulist,,44.249.250.113,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n15678,6,353,2017-03-09 14:21:28,http://block.co/leie_dickinson,,75.34.253.21,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n15679,6,353,2017-05-11 23:38:26,http://bernhardsporer.org/vance,,6.54.240.224,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n15680,6,353,2017-04-21 07:27:57,http://smitham.info/lilly_king,,184.129.215.151,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n15681,6,353,2017-01-28 00:24:16,http://mcglynn.co/mariam,,216.10.233.29,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n15682,6,353,2017-05-27 20:05:52,http://bogan.io/yeenia_abshire,,158.156.106.161,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n15683,6,353,2017-04-21 10:31:14,http://schaden.info/joel,,133.107.127.198,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n15684,6,353,2017-04-15 04:28:18,http://brakuskuvalis.org/verna.larson,,184.123.37.237,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n15685,6,353,2017-05-06 13:12:22,http://johnson.io/sincere_dickinson,,147.64.57.3,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n15686,6,353,2017-03-16 18:54:02,http://lueilwitz.com/viola.kshlerin,,242.76.140.154,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n15687,6,353,2017-03-14 00:51:03,http://mckenzie.org/destiny,,206.4.201.72,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n15688,6,353,2017-03-22 00:51:49,http://johnston.info/delmer.abshire,,86.210.119.199,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n15689,6,353,2017-01-04 10:27:58,http://ohara.co/shannon,,155.80.197.166,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n15690,6,353,2017-03-21 13:14:39,http://larkin.info/marquise,,36.219.71.30,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n15691,6,353,2017-03-05 21:21:28,http://olsontreutel.info/katelin_blick,,191.186.170.228,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n15692,6,353,2017-04-03 02:44:55,http://romaguera.io/lucile,,16.114.194.185,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n15693,6,353,2016-12-21 18:25:54,http://crooks.net/marlee.wolff,,251.140.116.66,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n15694,6,353,2017-03-08 16:50:10,http://collier.co/zoe.kris,,186.197.46.47,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n15695,6,353,2017-02-27 17:22:45,http://schuster.org/kolby_ortiz,,104.254.189.238,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n2852,2,63,2017-04-01 13:54:04,http://franecki.com/katarina_ernser,,168.130.238.203,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n2853,2,63,2017-03-30 17:42:07,http://schumm.net/keira,,99.50.111.133,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n2854,2,63,2017-04-26 01:24:46,http://quigley.com/lorine_kuvalis,,35.171.116.38,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n2855,2,63,2017-04-17 13:46:59,http://murphyturner.com/danielle_mohr,,205.228.242.109,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n2856,2,63,2017-01-29 16:16:18,http://strosin.name/zoey,,41.52.203.28,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n2857,2,63,2017-05-11 21:41:32,http://stroman.io/kira_gislason,,11.208.13.63,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n2858,2,63,2017-05-30 15:58:53,http://weimann.co/lisette_christiansen,,49.67.189.15,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n2859,2,63,2017-02-02 20:15:28,http://champlinferry.org/allie,,144.254.120.150,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n2860,2,63,2017-04-10 11:01:19,http://runolfon.biz/karlee_rohan,,42.221.164.55,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n2861,2,63,2017-05-11 22:07:51,http://mccullough.com/nicolette_armstrong,,95.242.164.113,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n2862,2,63,2016-12-29 19:01:10,http://wolf.io/lewis,,146.49.105.154,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n2863,2,63,2017-04-02 02:58:03,http://baumbach.info/haley.graham,,43.169.61.202,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n2864,2,63,2017-01-09 09:22:03,http://pfeffer.name/fleta,,75.76.113.194,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n2865,2,63,2017-06-02 02:22:22,http://gislason.info/isom,,234.140.250.140,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n2866,2,63,2017-01-19 09:57:54,http://trantowgleason.com/harold,,150.18.21.66,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n2867,2,63,2016-12-25 15:59:57,http://emmerich.org/thaddeus.hilpert,,117.50.4.91,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n2868,2,63,2016-12-30 08:43:02,http://volkman.io/mack,,79.170.25.174,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n2869,2,63,2017-01-11 10:53:44,http://dickens.co/ben,,68.69.218.19,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n2870,2,63,2016-12-19 08:02:03,http://jacobson.name/tomas,,63.112.155.212,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n2871,2,63,2017-03-05 20:05:31,http://danieltillman.org/rasheed.ferry,,133.147.230.215,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n2872,2,63,2017-04-29 22:50:08,http://ullrich.io/ayana.ruecker,,156.155.170.145,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n2873,2,63,2017-06-01 11:31:21,http://hilpert.com/lewis.wolff,,86.245.54.89,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n2874,2,63,2017-04-03 20:12:37,http://lockmanmayer.net/josiah,,91.184.146.232,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n2875,2,64,2017-03-18 19:41:57,http://beatty.com/dawn.cronin,0.9511796056,183.134.179.232,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n2876,2,64,2017-06-10 08:46:59,http://aufderhar.io/myriam,0.6346225665,209.22.192.110,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n2877,2,64,2017-04-26 01:14:04,http://wisozk.info/timmothy.kunze,0.1928551209,2.9.181.44,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n2878,2,64,2017-03-18 07:22:24,http://mitchell.org/alayna,0.6019735012,129.108.166.80,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n2879,2,64,2017-04-19 03:40:44,http://murazik.net/henderson_toy,0.2634694294,34.67.13.63,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n2880,2,64,2017-05-06 07:46:28,http://fisher.biz/selina,0.7121637419,16.204.208.214,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n2881,2,64,2017-02-07 19:24:15,http://jacobs.name/krystel.kovacek,0.8676960906,184.71.50.153,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n2882,2,64,2016-12-27 06:03:20,http://deckowreynolds.name/celine.deckow,0.8177789743,179.166.30.183,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n2883,2,64,2017-04-06 01:19:21,http://schulist.net/earnest,0.5083965950,83.230.236.96,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n2884,2,64,2017-05-03 19:36:02,http://swaniawskiheel.com/valentin.williamson,0.9581201732,228.41.244.207,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n2885,2,64,2017-03-04 18:21:57,http://eichmann.com/chyna.veum,0.9601646946,198.89.119.146,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n2886,2,64,2017-06-14 01:01:00,http://bogisichrippin.name/kaya_flatley,0.5277688758,190.78.123.52,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n2887,2,64,2017-02-28 15:50:23,http://trantow.info/ruel,0.0960232109,244.25.47.214,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n2888,2,64,2017-03-07 14:12:18,http://carter.io/seth_watsica,0.7066932917,126.198.162.216,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n2889,2,64,2017-04-04 10:44:36,http://rippin.name/emmitt.quigley,0.0534928588,249.141.63.22,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n2890,2,64,2017-04-13 04:33:25,http://zulauf.io/joseph,0.7075664303,98.44.114.157,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n2891,2,64,2017-03-04 16:06:25,http://fisherbogisich.co/helga_grant,0.7161153806,91.104.240.81,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n2892,2,64,2017-01-09 00:40:36,http://handrutherford.net/mireya_wilderman,0.5680583550,150.87.105.193,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n2893,2,64,2017-05-17 20:59:28,http://swaniawski.io/jacques_corwin,0.2410868668,230.98.180.68,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n2894,2,64,2017-01-20 03:54:49,http://kuhn.org/alexandro,0.1077098089,129.62.119.213,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n2895,2,64,2017-05-08 05:03:21,http://conroy.io/kyla_sporer,0.2012890936,215.16.67.251,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n2896,2,64,2017-05-28 00:19:27,http://leannon.com/laverne,0.9760099187,16.145.181.55,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n2897,2,64,2017-01-22 05:00:05,http://heel.com/kay_strosin,0.0065256895,115.208.79.242,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n2898,2,64,2017-02-12 04:37:47,http://zboncakkunze.info/sedrick.brekke,0.6712320237,72.37.223.252,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n2899,2,64,2017-03-10 18:43:41,http://stehr.net/darius.volkman,0.6126397169,114.221.170.35,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n2900,2,64,2017-03-07 12:48:02,http://purdy.io/alf,0.0183792302,209.122.170.47,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n2901,2,64,2017-04-17 14:53:17,http://ziemanndibbert.net/mariam.denesik,0.3137354145,228.199.64.234,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n2902,2,64,2017-05-27 01:44:39,http://purdyhoppe.co/nathan.emard,0.7654613637,189.58.231.216,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n2903,2,64,2017-04-22 00:20:02,http://rolfson.io/juana,0.7987053689,125.250.53.68,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n2904,2,64,2017-03-09 02:18:53,http://weinatweber.org/darion,0.1256875556,19.231.13.113,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n2905,2,64,2017-04-24 07:44:05,http://nader.net/amelie_bauch,0.3906543467,229.28.63.116,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n9787,4,219,2017-05-21 11:05:17,http://ankunding.info/bethel.volkman,,121.253.224.147,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n9788,4,219,2017-02-21 20:07:39,http://kohler.org/ada,,180.151.36.119,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n9789,4,219,2017-02-28 17:12:04,http://kihnhayes.name/nannie,,130.19.138.30,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n9790,4,219,2017-02-05 15:16:45,http://fishermetz.org/guy.kunze,,193.171.16.81,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n9791,4,219,2017-01-06 04:27:06,http://armstrong.org/ona.lubowitz,,252.23.176.149,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n9792,4,219,2017-05-23 20:33:24,http://mayertbosco.org/rebeka.gutkowski,,254.134.20.12,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n9793,4,219,2017-04-13 10:23:16,http://erdman.io/aditya.spinka,,129.194.170.96,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n9794,4,220,2017-03-10 01:37:56,http://torp.com/geovany_pfeffer,,178.100.230.142,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n9795,4,220,2016-12-14 10:13:40,http://schneiderkemmer.com/adella,,72.193.27.60,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n9796,4,220,2017-03-11 19:29:42,http://veumsauer.io/estel_kohler,,120.73.169.23,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n9797,4,220,2017-01-25 04:26:57,http://wyman.info/barrett_denesik,,147.223.45.241,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n9798,4,220,2017-05-22 00:24:35,http://von.net/alexane_mante,,210.170.123.249,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n9799,4,220,2017-02-21 04:28:49,http://kiehn.org/kelley,,247.92.243.251,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n9800,4,220,2017-04-11 21:33:15,http://schiller.org/rodrigo,,116.251.11.162,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n9801,4,220,2016-12-14 20:25:04,http://dare.biz/wilhelm.green,,48.104.161.205,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n9802,4,220,2017-04-10 17:24:54,http://starkmorar.info/lupe_kling,,117.4.174.49,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n9803,4,220,2017-06-02 04:02:36,http://altenwerthgislason.name/ramon,,149.164.60.180,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n9804,4,220,2017-05-21 09:43:25,http://larson.info/austin.armstrong,,151.7.149.149,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n9805,4,220,2017-03-22 16:52:57,http://purdy.co/bernie,,56.29.166.119,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n9806,4,220,2017-05-30 02:47:06,http://hickle.biz/sammy,,170.9.100.241,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n9807,4,220,2017-06-05 23:41:37,http://ziemann.name/bernadine,,123.87.61.148,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n9808,4,220,2017-02-07 20:57:06,http://schaeferweinat.com/everette,,54.21.199.171,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n9809,4,220,2017-03-04 04:15:29,http://schuster.info/leta,,116.167.146.100,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n9810,4,220,2017-01-22 08:04:42,http://hintz.co/sabrina,,217.162.223.130,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n9811,4,220,2017-06-05 22:56:46,http://will.biz/meda,,50.178.28.120,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n9812,4,220,2017-01-03 12:07:18,http://schaefer.info/cornelius,,232.180.229.135,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n9813,4,220,2017-03-29 06:21:49,http://terrystreich.co/madyson,,87.182.47.244,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n9814,4,220,2017-05-21 13:09:38,http://stoltenberg.net/marlee.bashirian,,154.218.202.208,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n9815,4,220,2016-12-26 00:58:22,http://dubuque.biz/ottilie_hamill,,141.34.196.98,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n9816,4,220,2017-05-11 17:22:49,http://wiegand.biz/nash,,83.40.33.72,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n9817,4,220,2017-03-23 05:00:04,http://damore.info/lionel,,95.137.20.200,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n9818,4,220,2017-04-10 11:21:03,http://lindgrentromp.name/rosemarie.effertz,,190.114.214.165,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n9819,4,220,2017-04-04 13:30:18,http://schillercummings.co/velda.wolff,,85.64.25.180,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n9820,4,220,2017-05-25 14:54:12,http://conroybuckridge.com/fabian,,85.78.138.82,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n9821,4,220,2017-06-14 00:48:45,http://buckridge.name/minnie_olson,,200.90.28.64,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n9822,4,220,2017-04-27 17:32:07,http://kohaag.co/montana,,133.42.115.179,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n9823,4,220,2017-01-01 07:38:52,http://langoshhermiston.biz/kailee,,109.207.74.6,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n9824,4,220,2017-05-27 23:25:37,http://torphy.info/leopoldo,,35.81.229.70,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n9825,4,220,2017-05-29 20:58:43,http://senger.info/jordane,,196.216.145.156,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n9826,4,220,2017-04-29 17:57:04,http://torphy.name/cyrus,,132.42.25.179,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n9827,4,220,2017-02-17 04:52:13,http://harber.co/aliya.wuckert,,98.159.169.140,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n9828,4,220,2017-04-14 00:16:11,http://sporer.org/johnson_kutch,,7.109.126.162,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n9829,4,220,2017-04-05 19:13:46,http://skileshintz.net/casper.ritchie,,164.86.75.232,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n9830,4,220,2017-05-19 08:58:36,http://baumbach.co/bret,,54.102.182.23,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n9831,4,220,2017-05-03 22:46:13,http://pacocha.org/sam.reilly,,7.248.42.64,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n9832,4,220,2016-12-20 16:08:24,http://volkman.net/darwin_adams,,171.158.76.147,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n9833,4,220,2017-04-30 19:32:42,http://gerlach.name/percy,,237.242.8.87,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n9834,4,220,2017-06-01 21:10:40,http://gerhold.com/dahlia_hermiston,,110.225.61.246,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n9835,4,221,2017-04-13 06:43:46,http://farrell.org/shad_stiedemann,0.8544780865,28.32.117.170,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n9836,4,221,2017-05-26 17:46:58,http://streich.name/reid,0.9731786518,116.97.8.178,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n9837,4,221,2017-05-30 23:53:03,http://weimanngrimes.com/jillian,0.7283078522,48.89.110.11,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n9838,4,221,2017-01-14 01:13:26,http://kunzeveum.org/deie,0.8985458574,87.247.67.242,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n9839,4,221,2016-12-27 22:39:04,http://sanfordpowlowski.biz/rafaela.gleason,0.0682227551,71.50.65.10,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n9840,4,221,2017-05-19 00:45:41,http://lesch.biz/leone_hilpert,0.9640156136,134.82.91.203,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n9841,4,221,2017-01-17 20:15:10,http://hilpertjakubowski.com/karina.boyle,0.4458298816,229.32.247.114,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n6779,3,149,2017-06-10 20:33:17,http://rathbartoletti.net/hershel_lubowitz,,167.210.66.181,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n6780,3,149,2017-04-09 20:27:54,http://raynor.net/roger_dickens,,173.209.5.186,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n6781,3,149,2017-01-24 01:04:56,http://upton.info/emory,,218.210.103.222,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n6782,3,149,2016-12-30 18:21:42,http://rutherford.com/helena_zulauf,,93.5.156.22,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n6783,3,149,2017-04-17 20:41:54,http://haneshanahan.name/lew,,35.75.11.17,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n6784,3,149,2017-01-29 14:22:08,http://lind.org/burnice_hintz,,124.241.129.145,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n6785,3,149,2017-04-17 19:06:40,http://kshlerin.name/katrina.corkery,,10.192.17.149,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n6786,3,149,2017-06-06 06:19:57,http://kleinokon.name/ewell_kertzmann,,200.213.239.170,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n6787,3,149,2017-04-05 08:25:00,http://reilly.info/abner,,26.109.83.89,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n6788,3,149,2017-01-29 13:04:50,http://hoeger.org/beryl.johnston,,113.6.74.3,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n6789,3,149,2017-02-01 20:23:29,http://littleortiz.info/dangelo.torphy,,242.166.82.223,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n6790,3,149,2017-01-06 10:02:09,http://schuster.name/herminia_quitzon,,190.41.77.148,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n6791,3,149,2017-02-10 12:01:38,http://kubryan.io/melya_crona,,147.21.105.83,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n6792,3,149,2017-04-01 00:46:37,http://schadenhane.biz/federico.grimes,,133.24.148.53,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n6793,3,149,2017-01-27 14:40:23,http://gerlach.info/tanner,,30.43.179.196,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n6794,3,149,2017-05-23 19:37:33,http://johnston.org/audrey,,235.114.253.124,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n6795,3,149,2017-04-09 08:18:05,http://kohler.biz/heath,,39.19.70.113,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n6796,3,149,2017-01-17 08:17:13,http://trantowjones.net/hiram,,247.87.97.102,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n6797,3,149,2017-05-15 15:17:23,http://lueilwitz.org/ashleigh.botsford,,159.26.188.243,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n6798,3,149,2017-06-12 19:16:15,http://gutmann.org/khalid.lehner,,222.84.136.244,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n6799,3,149,2016-12-29 12:53:47,http://bartolettifahey.biz/chance.stoltenberg,,237.253.192.101,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n6800,3,149,2017-02-10 08:57:42,http://ziemannlakin.info/lavina,,2.74.223.192,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n6801,3,149,2017-06-07 01:28:10,http://predovic.org/nya,,221.242.98.102,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n6802,3,149,2017-04-25 16:02:26,http://nienow.net/lempi,,245.250.96.180,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n6803,3,149,2017-04-24 17:48:54,http://friesenwest.org/tabitha.haag,,64.144.38.230,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n6804,3,149,2017-01-08 08:41:15,http://jacobi.biz/samantha,,135.250.246.99,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n6805,3,149,2017-02-28 10:04:27,http://shanahangrant.org/nathanial.borer,,189.66.65.55,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n6806,3,149,2017-05-30 19:14:52,http://renner.co/ali,,91.246.6.246,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n6807,3,149,2017-01-10 20:29:59,http://kiehnwalker.org/jaunita_jacobson,,177.94.77.168,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n6808,3,149,2017-01-11 22:14:17,http://rau.org/isobel.conroy,,49.34.164.18,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n6809,3,150,2017-02-11 10:06:06,http://legros.org/ardith,,76.57.126.14,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n6810,3,150,2017-05-08 01:09:33,http://lemkekovacek.biz/zackary.gleichner,,179.223.68.220,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n6811,3,150,2017-03-27 17:34:44,http://bosco.name/dean.padberg,,83.32.58.228,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n6812,3,150,2017-02-16 16:31:15,http://walkerbruen.org/montana.dooley,,148.242.132.119,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n6813,3,150,2017-03-20 02:42:20,http://kiehn.com/luigi,,165.135.17.17,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n6814,3,150,2017-04-15 18:35:33,http://rohan.io/angel,,202.196.134.178,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n6815,3,150,2017-02-18 02:37:01,http://rennerstanton.name/maymie_boehm,,168.166.177.214,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n6816,3,150,2017-06-13 15:30:26,http://dickinson.net/lizeth,,248.75.80.68,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n6817,3,150,2017-04-01 15:28:41,http://lynch.org/sarah,,183.245.226.127,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n6818,3,150,2017-05-16 01:46:51,http://wuckert.name/rozella,,130.243.234.14,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n6819,3,150,2016-12-28 10:33:38,http://harbermccullough.biz/bailee,,229.99.29.28,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n6820,3,150,2017-04-03 10:45:45,http://daniel.name/kane_greenfelder,,117.106.195.135,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n6821,3,150,2017-05-19 09:24:16,http://goyette.biz/maia,,114.231.176.161,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n6822,3,150,2017-04-24 09:09:53,http://nitzsche.net/vickie,,43.235.34.28,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n6823,3,150,2017-05-17 21:42:57,http://stiedemann.org/henri_tremblay,,163.204.165.183,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n6824,3,150,2016-12-24 11:19:52,http://farrell.biz/victoria_hilll,,186.168.204.160,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n6825,3,150,2017-02-01 07:43:20,http://batzcrona.net/billie,,86.222.40.88,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n6826,3,150,2017-01-16 09:49:49,http://larsonoberbrunner.org/cielo_mraz,,230.188.98.105,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n6827,3,150,2016-12-26 18:51:51,http://leannon.co/mylene_heidenreich,,175.77.116.66,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n6828,3,150,2017-01-12 22:09:32,http://klockofisher.com/lew.durgan,,15.92.158.38,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n6829,3,150,2017-03-06 04:53:19,http://feest.co/theodora_murray,,182.89.210.50,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n6830,3,150,2017-01-07 21:36:49,http://ratkethompson.name/raymond,,170.147.127.38,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n6831,3,150,2017-05-01 09:10:52,http://spinka.info/delilah,,195.86.51.203,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n6832,3,150,2017-03-27 12:19:29,http://moen.io/candida.barrows,,174.150.106.7,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n6833,3,150,2017-05-03 15:44:09,http://herzog.info/caie,,251.237.120.131,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n12694,5,283,2017-04-07 06:00:38,http://jerdenikolaus.com/robyn.vonrueden,,78.87.44.139,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n12695,5,283,2017-01-01 01:26:37,http://gaylord.net/rosie.schaefer,,41.48.247.92,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n12696,5,283,2017-01-07 12:44:46,http://nitzsche.name/meredith,,45.58.131.230,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n12697,5,284,2017-06-03 23:04:04,http://rueckertorphy.biz/arvel,,180.239.187.156,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n12698,5,284,2017-03-07 01:10:22,http://wolf.org/herminia_durgan,,79.57.57.101,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n12699,5,284,2017-04-24 19:24:06,http://emard.io/fannie,,110.93.210.109,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n12700,5,284,2017-01-16 17:45:54,http://stark.name/leslie_pollich,,182.132.2.48,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n12701,5,284,2017-01-10 05:15:40,http://pfannerstill.org/jonatan_effertz,,204.10.215.194,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n12702,5,284,2017-04-09 23:52:37,http://hermanbaumbach.name/christelle_borer,,81.189.156.237,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n12703,5,284,2017-05-27 10:29:51,http://rogahn.co/ubaldo_king,,65.117.210.236,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n12704,5,284,2017-03-25 08:24:01,http://crona.name/madilyn_cremin,,145.248.86.77,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n12705,5,284,2016-12-26 08:35:35,http://metz.name/jaleel,,241.25.101.119,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n12706,5,284,2017-01-04 21:40:17,http://lakinstoltenberg.io/amir_leuschke,,116.87.43.78,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n12707,5,284,2017-05-05 16:04:29,http://buckridge.name/christina.weimann,,209.81.247.43,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n12708,5,284,2017-05-07 16:08:35,http://runolfon.io/ania_armstrong,,4.91.230.57,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n12709,5,284,2017-05-09 05:20:01,http://hettingerconsidine.info/cornell,,80.214.27.179,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n12710,5,284,2017-04-27 14:37:06,http://keeling.io/flo.wintheiser,,62.33.109.158,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n12711,5,284,2017-03-22 05:16:17,http://bergstromhegmann.org/evelyn.koch,,252.219.12.217,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n12712,5,284,2017-01-15 19:11:31,http://hermanmertz.info/vesta,,178.200.77.234,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n12713,5,284,2017-04-04 00:46:19,http://kutch.co/teresa_stehr,,210.195.16.193,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n12714,5,284,2017-03-01 23:57:12,http://stark.net/luther,,251.38.54.58,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n12715,5,284,2017-04-11 23:03:23,http://barrows.net/garrison,,107.29.25.5,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n12716,5,284,2017-05-27 19:19:12,http://ryanwitting.co/gina.quigley,,216.240.118.135,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n12717,5,284,2017-03-16 21:49:55,http://durgan.org/tyrique,,53.134.246.2,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n12718,5,284,2017-02-07 03:38:32,http://kub.org/nolan_blick,,181.68.115.26,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n12719,5,284,2017-02-23 03:30:21,http://gleichner.biz/kathleen,,39.168.111.15,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n12720,5,284,2017-05-27 08:59:10,http://emmerich.net/braxton,,190.65.246.24,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n12721,5,284,2017-01-22 12:46:55,http://connelly.co/karl.ebert,,98.48.141.142,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n12722,5,284,2016-12-21 14:58:26,http://ledner.name/arne,,196.130.249.163,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n12723,5,284,2017-01-25 03:30:21,http://reichel.info/april,,194.115.239.249,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n12724,5,284,2016-12-17 07:25:05,http://olson.name/lon_krajcik,,132.14.193.216,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n12725,5,284,2017-01-18 05:17:10,http://jacobson.io/baron_abernathy,,59.77.38.54,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n12726,5,284,2017-05-05 09:15:12,http://lueilwitz.name/zena,,147.145.179.195,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n12727,5,284,2017-03-23 15:38:23,http://ryan.info/kaylin_lubowitz,,114.46.5.45,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n12728,5,284,2017-06-07 06:53:15,http://conn.io/alize_wisozk,,227.105.28.41,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n12729,5,284,2017-02-09 01:39:01,http://hagenes.org/joesph_toy,,168.164.190.77,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n12730,5,284,2017-05-22 22:14:30,http://frami.info/salvatore.predovic,,75.66.157.8,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n12731,5,284,2017-01-30 08:22:05,http://reichel.io/faye.ebert,,185.66.78.50,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n12732,5,284,2017-06-04 09:20:57,http://olsonleffler.com/lula,,157.20.108.119,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n12733,5,284,2017-05-25 02:44:13,http://grahamgreenholt.com/erick.rice,,227.26.87.145,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n12734,5,284,2017-05-29 19:19:21,http://herzogcole.info/mya,,210.204.225.63,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n12735,5,284,2017-02-06 02:42:56,http://cormier.info/destany,,16.217.63.45,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n12736,5,284,2017-03-12 21:40:36,http://abernathy.co/conor,,254.179.149.164,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n12737,5,284,2017-05-13 14:00:41,http://muller.org/jared,,153.178.149.120,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n12738,5,284,2017-03-31 02:18:06,http://king.biz/casimir_prosacco,,205.108.222.189,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n12739,5,284,2017-05-28 15:04:46,http://lynch.biz/norene,,146.60.252.228,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n12740,5,284,2016-12-25 20:42:20,http://mante.io/telly.beahan,,252.100.196.37,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n12741,5,284,2017-05-30 03:33:21,http://johnston.info/asha.conroy,,226.223.48.219,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n12742,5,284,2017-01-05 18:49:47,http://leannon.org/zelda,,148.121.224.57,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n12743,5,284,2017-03-18 15:29:35,http://crooks.org/laney,,188.40.130.234,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n12744,5,284,2016-12-21 09:50:43,http://wolff.co/ed_schiller,,233.43.146.190,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n12745,5,284,2017-01-30 07:09:28,http://rutherfordkemmer.net/josue_morar,,10.140.184.122,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n12746,5,284,2017-04-09 09:09:43,http://funk.name/rollin.willms,,122.231.56.70,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n12747,5,284,2017-05-06 06:47:06,http://schowalterjacobson.info/isaiah_kuhlman,,64.69.193.202,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n12748,5,284,2017-03-24 06:38:22,http://stroman.biz/lizeth,,178.163.200.122,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n15696,6,353,2017-05-22 10:47:22,http://baumbachheel.net/eliza,,127.100.30.220,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n15697,6,353,2017-05-14 03:05:51,http://greenholt.info/charlotte.moriette,,105.184.220.42,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n15698,6,353,2017-05-02 06:18:28,http://zboncakrodriguez.biz/jefferey.zulauf,,113.85.182.19,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n15699,6,353,2017-01-04 19:24:37,http://feeney.org/janie_krajcik,,117.19.200.153,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n15700,6,353,2017-04-02 03:08:33,http://dickens.io/helga,,106.100.10.163,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n15701,6,353,2017-05-07 16:30:32,http://donnelly.biz/lila.marvin,,162.222.181.231,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n15702,6,353,2017-01-06 07:49:09,http://reichel.biz/broderick,,157.182.38.225,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n15703,6,353,2017-04-15 14:02:57,http://bode.name/violette_breitenberg,,23.78.188.86,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n15704,6,353,2016-12-19 09:49:10,http://glover.co/nikko.koepp,,49.175.186.58,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n15705,6,353,2017-02-20 17:16:30,http://morarcorkery.org/garth_kunde,,176.121.115.254,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n15706,6,353,2017-03-04 19:44:17,http://macgyverhodkiewicz.name/daniela_predovic,,47.16.143.51,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n15707,6,353,2017-06-13 07:08:37,http://collins.org/dovie,,129.186.128.250,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n15708,6,353,2017-01-15 12:11:37,http://cruickshankheaney.com/harrison,,162.125.123.52,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n15709,6,353,2017-04-13 00:51:31,http://mrazbergnaum.com/karlee,,242.92.58.103,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n15710,6,353,2017-02-03 19:28:00,http://croninhyatt.io/stephon.brakus,,215.175.179.222,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n15711,6,353,2017-02-09 15:37:29,http://hayes.name/zane_mckenzie,,238.27.43.92,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n15712,6,353,2017-03-20 00:40:04,http://gorczanycole.com/hattie.predovic,,228.44.116.14,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n15713,6,353,2017-01-11 06:31:18,http://kihn.org/jaylin.koch,,44.75.221.36,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n15714,6,353,2017-03-18 10:34:22,http://lynch.biz/carlos,,47.12.2.107,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n15715,6,353,2017-05-21 20:44:40,http://brownyost.org/jaden,,153.28.219.46,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n15716,6,354,2017-04-26 03:53:38,http://smithamvolkman.info/darlene,,114.249.14.190,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n15717,6,354,2017-05-10 00:43:56,http://hilpert.info/eduardo.huel,,250.246.189.176,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n15718,6,354,2017-01-25 17:36:32,http://deckow.name/gregory_dach,,143.48.10.190,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n15719,6,354,2017-01-15 17:54:10,http://beckersimonis.info/kaitlyn.price,,24.238.240.203,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n15720,6,354,2017-05-23 05:29:45,http://murphygraham.io/christ,,34.162.31.235,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n15721,6,354,2017-04-10 10:59:31,http://moenpfannerstill.co/estella,,139.154.41.40,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n15722,6,354,2017-03-18 07:10:52,http://zemlak.name/mekhi_jacobson,,141.96.210.243,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n15723,6,354,2016-12-28 13:10:41,http://parker.name/kolby.bartoletti,,91.49.32.195,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n15724,6,354,2017-04-29 04:36:11,http://ferry.biz/kareem,,192.48.31.130,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n15725,6,354,2017-01-22 14:40:20,http://cronincronin.biz/carlo,,243.170.77.201,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n15726,6,354,2017-03-18 17:02:12,http://grant.com/kristy_zulauf,,7.74.228.158,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n15727,6,354,2017-02-08 10:54:55,http://hickle.biz/vivien_moore,,120.106.219.207,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n15728,6,354,2017-03-25 22:18:08,http://halvorson.biz/rocky,,135.149.214.156,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n15729,6,354,2017-02-28 23:27:14,http://boyle.co/dianna,,229.184.45.244,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n15730,6,354,2017-04-15 01:32:19,http://funk.com/veda,,115.237.185.209,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n15731,6,354,2017-01-23 21:29:21,http://conn.co/blaze_rowe,,124.230.186.48,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n15732,6,354,2017-01-01 08:10:15,http://rohan.info/emilie,,215.203.46.89,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n15733,6,354,2017-03-02 17:49:48,http://quigleymcdermott.io/jamil.rath,,166.149.126.24,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n15734,6,354,2017-03-16 22:10:07,http://greenholtkemmer.name/sedrick,,25.133.174.149,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n15735,6,354,2017-06-08 19:48:34,http://denesiknolan.net/sammie_mueller,,13.93.40.40,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n15736,6,354,2017-06-05 05:37:32,http://lefflerbrakus.io/donny_stiedemann,,78.192.24.69,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n15737,6,354,2017-03-19 17:52:30,http://jenkins.biz/jaquan.hauck,,111.2.165.153,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n15738,6,354,2017-06-06 06:55:26,http://bergnaum.io/diamond,,228.216.182.122,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n15739,6,354,2016-12-19 06:09:57,http://legros.co/josiane_runte,,183.22.101.159,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n15740,6,354,2017-03-20 03:00:41,http://williamson.com/wilburn,,52.187.18.176,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n15741,6,354,2017-01-12 06:44:05,http://okuneva.org/manuela,,100.145.243.123,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n15742,6,354,2017-05-09 17:41:47,http://stracke.info/mateo,,239.106.231.25,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n15743,6,354,2017-04-01 16:13:12,http://heathcotekeeling.io/felipe,,83.151.102.119,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n15744,6,354,2017-03-19 11:21:03,http://kiehn.co/sandra_kihn,,127.83.249.103,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n15745,6,354,2017-05-23 10:44:45,http://homenick.co/heaven_ko,,149.92.15.183,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n15746,6,354,2017-02-01 13:19:02,http://bernier.org/mazie.kozey,,235.221.31.64,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n15747,6,354,2017-03-20 03:36:02,http://mcclure.net/herminia_bergnaum,,44.41.188.198,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n15748,6,354,2017-01-04 18:56:23,http://blanda.name/fermin,,82.210.228.141,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n15749,6,354,2016-12-13 19:15:53,http://kulas.name/lavern_turner,,72.131.7.147,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n15750,6,354,2017-01-02 07:48:37,http://kelerankunding.com/candace_tremblay,,49.199.186.207,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n2906,2,64,2017-05-27 12:49:43,http://denesik.net/stacey.aufderhar,0.1648262502,165.162.213.186,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n2907,2,64,2017-01-18 07:55:05,http://strackejohns.co/emile,0.3817178047,153.217.88.142,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n2908,2,64,2017-05-02 00:59:34,http://king.org/josiane,0.6800251921,183.201.252.57,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n2909,2,64,2017-04-01 16:09:29,http://witting.net/deshawn_doyle,0.1010081191,92.58.199.27,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n2910,2,64,2017-06-02 05:16:41,http://mohr.net/casimir.fahey,0.9212890942,47.207.70.235,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n2911,2,64,2017-06-07 00:57:06,http://crooktoltenberg.co/bradford,0.6480223567,56.80.219.35,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n2912,2,64,2017-04-01 19:47:45,http://wehnerpollich.io/adrain,0.5353433482,53.117.205.71,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n2913,2,64,2017-01-05 17:29:35,http://heel.com/greta,0.4274482932,250.114.7.204,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n2914,2,64,2017-02-24 21:51:58,http://reichert.io/ronny.wiza,0.8570592901,186.118.57.169,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n2915,2,64,2017-03-31 01:18:24,http://leannonstanton.co/angelica,0.0976372904,150.69.7.114,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n2916,2,64,2017-01-04 23:17:57,http://schowalter.com/lenora_wyman,0.3583426950,35.3.69.120,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n2917,2,64,2017-04-27 20:06:22,http://farrell.biz/rupert,0.6116453719,58.221.209.218,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n2918,2,64,2017-04-13 01:25:43,http://halvorsonchamplin.co/kali,0.6340286841,16.182.225.78,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n2919,2,64,2017-03-21 15:48:35,http://breitenberg.com/porter,0.1676519262,99.121.35.16,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n2920,2,64,2017-01-11 18:58:10,http://walter.io/reed.vandervort,0.5001945653,221.178.231.120,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n2921,2,64,2017-05-23 19:39:49,http://douglas.co/precious,0.5072863712,69.133.147.85,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n2922,2,64,2017-03-24 16:09:12,http://glover.com/waino_kutch,0.8574326949,116.147.221.157,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n2923,2,65,2017-02-19 14:34:43,http://jenkins.co/florence.moriette,0.8109989174,188.56.226.240,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n2924,2,65,2017-01-09 18:21:24,http://rathhagenes.info/aurore,0.5023311389,31.7.89.67,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n2925,2,65,2017-02-17 10:42:51,http://kovacek.net/ezekiel,0.7814605669,162.19.98.187,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n2926,2,65,2017-03-01 18:25:59,http://murphy.org/roderick,0.6242767281,202.26.141.123,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n2927,2,65,2016-12-19 02:44:46,http://hettinger.co/arturo,0.8853867871,24.23.11.130,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n2928,2,65,2016-12-16 09:35:47,http://kerluke.net/jacynthe.oberbrunner,0.3334095711,241.219.223.146,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n2929,2,65,2017-04-13 18:36:46,http://stamm.co/anabel,0.3163032951,246.149.177.204,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n2930,2,65,2017-01-06 19:28:58,http://hills.biz/braulio.hintz,0.6874250717,8.61.147.252,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n2931,2,65,2016-12-24 06:39:37,http://schiller.io/kaden.zboncak,0.4237815431,184.214.125.122,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n2932,2,65,2017-01-14 15:06:46,http://morarhowell.net/isaias,0.5509876293,135.243.235.239,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n2933,2,65,2017-01-23 04:02:44,http://zulaufschmeler.org/korbin_smitham,0.5605076506,227.196.246.229,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n2934,2,65,2017-03-05 12:01:19,http://windler.org/cortney_kemmer,0.9480535579,159.170.15.131,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n2935,2,65,2017-01-07 00:10:36,http://spencerkemmer.name/dakota.swaniawski,0.4326836503,125.112.231.143,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n2936,2,65,2017-01-09 20:53:06,http://kulas.org/harmon.weinat,0.2532339087,54.3.235.205,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n2937,2,65,2017-04-01 08:00:28,http://beahan.com/christophe,0.9496827760,120.252.253.231,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n2938,2,65,2017-01-04 21:02:23,http://krajcik.biz/rebeka_donnelly,0.9560487469,253.66.186.198,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n2939,2,65,2017-05-12 02:40:02,http://bode.co/camille,0.0153785099,113.36.100.73,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n2940,2,65,2017-01-21 09:27:48,http://jacobson.biz/celia_schultz,0.9784070527,247.243.15.189,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n2941,2,65,2017-04-26 10:35:21,http://quitzon.com/quincy.schumm,0.1667851298,100.147.136.125,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n2942,2,65,2017-05-23 08:20:29,http://gaylord.info/caandre,0.7223754919,63.226.170.93,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n2943,2,65,2017-01-31 20:09:58,http://hudson.co/reina_ernser,0.5436318485,191.147.59.61,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n2944,2,65,2017-05-17 15:46:36,http://powlowskicain.biz/velda_zemlak,0.6176269495,164.238.63.36,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n2945,2,65,2017-02-02 15:18:22,http://lind.name/stanley_ondricka,0.7626547180,147.241.227.130,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n2946,2,65,2017-03-08 12:32:21,http://littlenikolaus.net/haskell.bartell,0.4908110678,119.180.104.123,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n2947,2,65,2017-03-12 21:21:25,http://kirlinbarrows.info/gerda,0.8842974958,11.224.199.237,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n2948,2,65,2017-04-26 19:21:44,http://sipes.com/gerardo_daugherty,0.3192392049,69.116.65.45,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n2949,2,65,2017-03-07 20:21:44,http://eichmann.net/nelda_heller,0.4568917456,5.73.8.68,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n2950,2,65,2017-02-22 07:48:00,http://romaguera.com/seamus_zulauf,0.2964098804,184.101.74.37,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n2951,2,65,2017-06-13 18:40:49,http://gaylordharvey.io/duane_larkin,0.9359039759,227.184.101.246,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n2952,2,65,2017-02-17 20:52:17,http://wilderman.info/sam,0.7266130418,66.252.143.149,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n2953,2,65,2017-04-12 17:29:10,http://walshsmitham.net/braeden,0.5946515427,31.98.148.11,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n2954,2,65,2016-12-21 16:29:01,http://mayert.com/mathias,0.4921715151,210.202.17.77,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n2955,2,65,2016-12-30 07:25:36,http://westcummings.net/susanna,0.9634033114,247.24.25.108,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n2956,2,65,2017-03-09 12:18:13,http://pollich.info/malvina,0.5346158652,205.234.129.115,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n9842,4,221,2017-02-07 14:17:14,http://ricekaulke.biz/kolby_macgyver,0.3742259453,247.44.138.18,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n9843,4,221,2017-02-06 12:09:25,http://walker.biz/allie.fadel,0.0099313869,87.213.67.151,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n9844,4,221,2017-04-02 13:58:15,http://walter.biz/ivory_hamill,0.8696352059,19.232.160.215,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n9845,4,221,2017-03-17 07:10:39,http://reinger.net/astrid,0.6127495328,228.127.160.254,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n9846,4,221,2017-05-02 08:11:02,http://willms.org/armani_dare,0.0552043017,65.217.165.79,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n9847,4,221,2017-05-15 04:35:25,http://oreilly.io/brain,0.1222123055,11.36.131.26,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n9848,4,221,2017-03-05 21:52:12,http://buckridge.io/susanna,0.2736269425,107.15.181.2,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n9849,4,221,2017-04-25 21:13:17,http://walsh.co/hayden.kshlerin,0.7117761415,166.101.113.238,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n9850,4,221,2017-05-08 06:15:18,http://keler.biz/lisa_ullrich,0.2012082672,83.251.41.160,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n9851,4,221,2017-02-03 05:42:38,http://jerde.com/adrienne,0.3289415038,111.177.236.201,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n9852,4,221,2017-02-17 08:44:41,http://greenfelder.name/olaf.hagenes,0.8169530901,16.176.13.54,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n9853,4,221,2017-05-17 12:01:29,http://quitzon.org/jordi,0.3274574223,20.186.155.67,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n9854,4,221,2017-03-18 19:41:20,http://wyman.name/triston,0.6037084992,141.62.129.115,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n9855,4,221,2017-06-08 19:14:54,http://daughertyschneider.info/milford.schinner,0.7066142558,189.242.60.141,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n9856,4,221,2016-12-21 22:49:29,http://gibson.name/rosalia.keler,0.2682220915,240.169.18.162,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n9857,4,221,2017-06-02 21:47:58,http://homenick.org/muriel,0.7829910711,116.196.75.217,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n9858,4,221,2017-02-15 07:23:02,http://torpkoch.name/jeanette,0.3655956294,16.60.185.210,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n9859,4,221,2017-02-18 06:29:29,http://auer.co/wyman.tillman,0.1419698842,60.225.141.186,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n9860,4,221,2017-04-19 11:49:09,http://gusikowskihermann.info/darius_lemke,0.6759285914,217.181.134.102,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n9861,4,221,2017-01-28 03:26:28,http://sanforddietrich.com/anjali_botsford,0.3710781372,244.25.34.97,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n9862,4,221,2017-02-04 05:35:47,http://beiergleason.co/santiago.schowalter,0.2602600645,99.146.18.244,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n9863,4,221,2017-03-26 02:53:35,http://blick.org/jerrell,0.5067720743,55.190.136.253,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n9864,4,221,2017-05-17 20:59:47,http://predovic.info/ryder,0.9431469738,120.168.53.249,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n9865,4,221,2017-05-23 14:09:55,http://brown.co/damon,0.4317209422,224.39.206.167,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n9866,4,221,2016-12-23 17:07:29,http://volkman.org/clifton.ebert,0.1331418719,177.219.95.69,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n9867,4,221,2017-01-19 10:20:30,http://bechtelar.info/ebony.stoltenberg,0.7268938428,88.221.106.40,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n9868,4,221,2017-03-09 17:41:46,http://croninschmeler.co/felicia,0.9097963626,110.150.38.31,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n9869,4,221,2017-04-08 19:44:20,http://schoen.info/michel_mclaughlin,0.6658248494,52.87.90.107,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n9870,4,221,2017-02-26 12:24:37,http://hodkiewicz.org/nannie.kovacek,0.4858070880,84.248.117.24,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n9871,4,221,2017-01-09 01:32:07,http://stanton.biz/adolfo.gaylord,0.2497390271,89.26.206.100,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n9872,4,221,2017-05-11 20:28:33,http://romaguera.biz/brandt,0.0138754435,31.14.106.187,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n9873,4,221,2017-01-09 21:35:48,http://ernser.co/pascale,0.7032806041,147.174.140.234,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n9874,4,221,2017-05-05 15:25:05,http://vonruedenhintz.io/caidy.sporer,0.4515276316,183.157.137.231,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n9875,4,221,2017-04-16 16:35:40,http://schoen.io/ryder.prohaska,0.9535643159,141.210.9.183,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n9876,4,221,2017-04-14 20:57:00,http://dickinson.com/agnes_wiza,0.5705275428,203.217.175.38,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n9877,4,221,2017-04-16 16:16:59,http://gusikowskijast.io/albina.renner,0.5441168734,247.58.85.31,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n9878,4,221,2017-04-22 03:01:22,http://moen.name/judah_davis,0.7314554402,6.135.178.12,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n9879,4,221,2017-04-04 13:50:19,http://lefflerkling.name/maya,0.6689526769,194.122.31.144,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n9880,4,221,2017-03-23 04:08:03,http://bins.name/norval,0.5403204964,93.34.77.183,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n9881,4,221,2017-02-13 14:13:39,http://kling.co/abagail.farrell,0.6046073060,120.197.180.67,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n9882,4,221,2017-02-02 04:53:43,http://kilbackmueller.biz/kenyon_borer,0.7485072967,40.95.215.150,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n9883,4,221,2017-04-26 01:56:39,http://rodriguezsipes.org/adele,0.1634595831,85.170.45.197,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n9884,4,221,2017-03-17 07:39:54,http://kuvalis.com/margie,0.9623555284,29.2.162.201,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n9885,4,221,2016-12-14 18:04:00,http://larkin.name/hester_stamm,0.4952940447,50.24.78.75,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n9886,4,221,2017-01-11 09:52:22,http://kovacek.biz/nicholaus.hills,0.7957641336,193.68.62.252,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n9887,4,221,2017-02-22 08:39:25,http://zboncak.info/gertrude_rolfson,0.1720415080,74.43.148.141,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n9888,4,221,2017-05-09 19:25:12,http://bauch.co/liliana,0.9288063303,46.207.39.66,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n9889,4,221,2017-03-26 22:59:36,http://torp.name/bridie,0.5942013457,113.246.130.170,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n9890,4,221,2017-01-12 08:53:11,http://gerlach.biz/sasha,0.3079783255,78.99.80.212,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n9891,4,221,2017-05-31 13:32:33,http://nikolausokuneva.name/aditya,0.0728848938,220.222.106.70,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n9892,4,221,2017-02-09 19:25:02,http://flatley.org/laurence.oconnell,0.7800609424,235.102.60.161,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n6834,3,150,2017-01-03 00:14:17,http://watsica.org/ernestine,,52.48.194.115,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n6835,3,150,2016-12-25 01:23:01,http://hickle.biz/chester,,125.180.51.200,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n6836,3,150,2017-02-15 15:42:33,http://littel.com/jason,,213.18.90.77,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n6837,3,150,2017-05-13 17:00:59,http://schamberger.com/mohammad,,200.77.18.194,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n6838,3,150,2017-06-11 17:41:58,http://prohaska.org/tyshawn_effertz,,222.32.67.65,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n6839,3,150,2017-04-28 12:07:16,http://senger.biz/jeie,,138.152.246.78,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n6840,3,150,2017-05-25 18:32:43,http://krajcik.name/toy_reinger,,227.228.122.187,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n6841,3,150,2017-04-02 21:23:23,http://brekkefeil.com/eugenia.greenfelder,,250.155.8.46,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n6842,3,150,2017-05-08 02:45:10,http://gaylord.info/kamron,,26.209.130.247,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n6843,3,150,2017-02-22 00:10:41,http://dietrich.com/kasey,,166.252.36.44,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n6844,3,150,2017-01-10 00:59:24,http://mayertmohr.biz/isidro,,215.214.146.49,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n6845,3,150,2017-03-01 14:33:31,http://greenfelderkulas.biz/brennan,,231.221.136.217,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n6846,3,151,2017-05-30 15:47:26,http://kerluke.info/yoshiko,,132.46.129.145,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n6847,3,151,2017-04-01 12:36:07,http://swift.org/leann,,177.71.237.133,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n6848,3,151,2017-04-04 05:58:29,http://okeefe.info/leon,,116.30.114.127,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n6849,3,151,2017-03-07 14:25:22,http://rowe.name/willa,,34.162.136.144,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n6850,3,151,2017-01-21 02:40:03,http://schowalter.co/lea.goodwin,,182.239.102.207,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n6851,3,151,2017-02-06 21:32:52,http://moriette.biz/ettie_weber,,22.115.73.77,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n6852,3,151,2017-02-15 20:49:52,http://stehrhayes.info/jazmyn,,209.178.246.219,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n6853,3,151,2017-01-23 21:57:21,http://waters.net/cristal_kovacek,,142.5.190.203,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n6854,3,151,2016-12-24 14:54:36,http://lakinkovacek.info/donnell,,25.144.202.211,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n6855,3,151,2017-01-12 10:10:38,http://bednar.co/melisa_gerlach,,248.48.157.68,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n6856,3,151,2017-03-12 02:54:28,http://schuppe.net/isaias,,86.250.147.55,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n6857,3,151,2017-04-17 08:44:44,http://braun.com/mateo_mosciski,,9.95.33.187,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n6858,3,151,2016-12-25 13:44:44,http://torpgraham.info/allison.treutel,,189.120.93.243,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n6859,3,151,2017-04-11 08:13:36,http://larkin.org/rylan,,219.217.113.27,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n6860,3,151,2017-05-13 14:45:00,http://hirthekoch.org/francesca_goyette,,228.137.19.75,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n6861,3,151,2017-05-25 15:55:31,http://ferry.net/kyla,,120.31.213.91,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n6862,3,151,2016-12-21 04:52:08,http://larson.name/fatima,,39.11.220.2,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n6863,3,151,2017-03-08 14:39:12,http://jacobi.net/shirley_dicki,,82.155.184.149,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n6864,3,151,2017-04-05 21:48:43,http://jakubowskideckow.name/ronaldo.hermiston,,254.106.164.79,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n6865,3,151,2017-04-27 03:20:28,http://hagenesmorar.com/vincent_hayes,,183.111.130.148,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n6866,3,151,2017-01-24 15:43:00,http://vonrueden.co/tanner.barrows,,228.11.33.253,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n6867,3,151,2017-02-10 20:23:40,http://morar.io/demetris_dickinson,,237.176.148.136,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n6868,3,151,2017-04-16 18:10:13,http://stark.biz/ethyl_wintheiser,,236.14.50.200,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n6869,3,151,2017-02-16 01:04:04,http://schamberger.name/dayna,,156.37.254.110,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n6870,3,151,2017-03-05 11:08:36,http://howe.name/cecile_dare,,5.72.233.21,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n6871,3,151,2017-04-24 18:34:12,http://kozey.io/savanah,,64.216.61.247,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n6872,3,151,2017-03-15 02:19:11,http://jakubowskiokeefe.info/krystina_blick,,175.97.90.246,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n6873,3,151,2017-04-08 11:01:49,http://gibson.com/catherine_mclaughlin,,139.215.121.63,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n6874,3,151,2016-12-23 05:04:57,http://walterrenner.co/micheal,,23.221.86.17,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n6875,3,151,2017-03-26 18:35:26,http://ward.co/sam,,111.101.40.92,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n6876,3,151,2017-05-06 05:05:50,http://johnston.name/emerson,,10.100.21.225,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n6877,3,151,2017-06-02 10:02:25,http://lebsackmckenzie.io/dariana,,210.180.29.174,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n6878,3,151,2017-02-04 07:13:07,http://stiedemannsatterfield.org/wilfred,,212.157.53.80,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n6879,3,151,2017-03-04 10:01:02,http://tromp.com/raphael_mcglynn,,167.195.191.167,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n6880,3,151,2017-05-29 09:22:10,http://feeney.name/virgil.dooley,,40.98.75.211,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n6881,3,151,2016-12-28 14:17:17,http://ohara.com/eulah.ward,,221.36.219.129,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n6882,3,151,2017-01-17 13:44:41,http://heller.name/hermina.kuhic,,72.163.211.194,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n6883,3,151,2017-04-13 20:04:57,http://schiller.io/katelin,,148.254.14.220,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n6884,3,151,2017-02-04 19:39:51,http://ernser.com/aiyana_yundt,,220.72.218.194,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n6885,3,151,2017-01-20 01:20:04,http://kutch.name/bryce.becker,,59.3.121.185,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n6886,3,151,2017-02-08 14:45:28,http://brekkeschiller.net/bennett,,108.100.237.46,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n6887,3,151,2017-03-12 08:55:00,http://marvin.net/sydnee_schuster,,149.226.212.13,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n6888,3,151,2017-01-29 06:26:00,http://durganhowe.co/keenan,,230.131.158.155,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n6889,3,151,2017-05-10 12:28:01,http://goyette.biz/josue,,68.68.189.118,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n12749,5,285,2017-03-15 05:23:42,http://upton.biz/montana,,230.250.33.186,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n12750,5,285,2017-02-16 00:28:51,http://schoen.io/savanna,,101.97.208.80,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n12751,5,285,2017-05-13 06:06:28,http://berge.info/jamil,,150.101.194.117,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n12752,5,285,2017-04-12 14:08:24,http://kemmer.co/lillie,,129.31.92.208,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n12753,5,285,2017-01-12 03:54:57,http://auer.net/damian_abernathy,,195.230.37.176,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n12754,5,285,2017-03-11 18:06:54,http://schiller.info/hobart,,52.148.159.65,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n12755,5,285,2017-03-01 19:39:13,http://emmerichlesch.info/fabian.nikolaus,,12.178.198.24,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n12756,5,285,2017-02-09 08:30:33,http://kshlerinbecker.name/everardo,,149.227.36.107,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n12757,5,285,2016-12-17 17:17:49,http://macejkovic.biz/gabrielle,,80.186.156.140,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n12758,5,285,2017-06-11 02:04:43,http://walshullrich.org/pansy.crona,,194.27.2.106,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n12759,5,285,2017-05-12 00:06:38,http://gerlachschmidt.info/retha_luettgen,,211.44.230.230,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n12760,5,285,2017-05-18 13:33:11,http://larkin.com/vella_orn,,126.186.8.94,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n12761,5,285,2017-05-19 21:38:12,http://keeling.io/nikki.champlin,,148.156.111.158,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n12762,5,285,2017-01-02 05:19:56,http://ondrickaziemann.co/raina.murphy,,61.51.62.212,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n12763,5,285,2017-02-27 15:34:34,http://jaskolskileuschke.net/justice_johns,,238.221.135.205,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n12764,5,285,2017-04-16 17:08:24,http://satterfield.biz/caroline,,19.93.150.105,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n12765,5,285,2017-05-20 17:41:02,http://hills.co/gene,,88.132.210.72,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n12766,5,285,2017-01-31 04:37:54,http://durgan.co/brandon,,156.243.215.26,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n12767,5,285,2017-03-14 09:44:06,http://west.org/rico_jacobson,,222.149.47.172,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n12768,5,285,2017-06-12 07:53:43,http://hand.com/lucienne.crist,,96.68.43.200,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n12769,5,285,2017-03-17 09:33:37,http://schultz.biz/theresia,,52.229.93.242,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n12770,5,285,2017-01-03 15:28:32,http://oreilly.io/jaylan,,95.232.121.161,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n12771,5,285,2017-01-17 01:18:41,http://tillman.co/krystal,,37.216.128.176,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n12772,5,285,2017-06-04 21:33:38,http://beahan.co/joel_conn,,26.167.74.20,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n12773,5,285,2017-06-07 08:12:17,http://macejkovic.co/dangelo_metz,,58.72.157.222,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n12774,5,285,2017-04-10 01:00:01,http://schumm.net/elian,,192.167.72.153,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n12775,5,285,2017-03-10 23:47:31,http://torphyfriesen.name/therese_huel,,123.9.155.43,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n12776,5,285,2017-04-26 01:01:41,http://abbott.net/donato.oconner,,225.165.173.76,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n12777,5,285,2017-03-06 18:18:03,http://littelkeeling.org/makenzie,,35.81.38.126,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n12778,5,285,2017-01-07 10:54:08,http://wolfdicki.co/patricia_prosacco,,78.7.254.239,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n12779,5,285,2017-03-07 08:16:12,http://mante.net/janiya,,104.20.168.93,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n12780,5,285,2016-12-17 17:50:10,http://brown.io/harry_kihn,,100.90.200.253,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n12781,5,285,2017-05-09 08:41:52,http://colekirlin.info/obie,,171.171.155.151,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n12782,5,285,2017-02-02 04:29:30,http://leuschkegrimes.org/alize.kuphal,,21.72.181.40,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n12783,5,285,2017-02-09 19:05:29,http://moriette.com/nat_fadel,,165.110.206.114,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n12784,5,285,2017-06-10 19:58:37,http://hand.co/danyka,,246.247.188.185,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n12785,5,285,2017-05-19 22:10:54,http://bednar.info/ariel,,50.208.166.135,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n12786,5,285,2017-01-06 16:14:22,http://kreiger.net/suzanne_kozey,,31.62.17.192,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n12787,5,285,2016-12-13 09:45:27,http://jaskolski.net/juvenal,,164.134.61.80,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n12788,5,285,2017-06-13 14:59:36,http://graham.info/reid,,123.130.243.176,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n12789,5,285,2017-03-24 13:30:44,http://vandervort.net/elisha.dibbert,,24.234.80.157,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n12790,5,285,2017-02-20 08:33:49,http://muller.co/pattie,,129.200.46.203,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n12791,5,285,2017-04-11 09:45:11,http://jacobi.co/consuelo,,231.7.59.93,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n12792,5,285,2017-01-31 08:56:53,http://schroeder.co/malachi_carroll,,141.99.2.227,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n12793,5,285,2017-01-17 23:45:50,http://kuphal.io/ismael.mayert,,41.217.211.182,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n12794,5,285,2017-06-10 05:40:47,http://larson.com/dudley.daugherty,,5.129.6.6,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n12795,5,285,2017-05-01 00:03:20,http://heel.co/antwan,,179.142.111.3,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n12796,5,285,2017-02-21 21:20:47,http://dickens.io/sid.cummings,,206.156.12.6,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n12797,5,285,2017-04-20 04:24:02,http://wiegand.info/janie,,11.76.150.65,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n12798,5,285,2016-12-24 19:59:20,http://macejkovic.net/deshawn.rutherford,,74.152.223.128,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n12799,5,285,2017-01-26 02:02:25,http://brownjacobs.io/abagail,,197.138.45.83,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n12800,5,285,2017-04-18 13:40:36,http://torptorphy.net/frederic,,12.138.41.85,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n12801,5,285,2017-04-21 13:27:05,http://zboncak.io/frances,,2.78.159.187,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n12802,5,286,2017-05-22 13:05:53,http://ohara.com/rhiannon,,199.147.153.205,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n12803,5,286,2017-05-10 17:29:40,http://torp.org/darlene.king,,68.222.173.147,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n12804,5,286,2017-05-10 23:43:16,http://marksfranecki.org/stacy.predovic,,134.112.187.41,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n15751,6,354,2017-05-14 01:38:14,http://bartell.biz/katlyn,,203.72.96.171,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n15752,6,354,2017-05-29 21:57:09,http://predovickrajcik.io/kelsi,,150.202.94.20,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n15753,6,354,2017-02-02 17:42:59,http://kertzmannwindler.org/daren,,228.221.80.187,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n15754,6,354,2017-01-23 08:25:20,http://hermiston.info/demarco_jacobi,,84.193.26.213,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n15755,6,354,2017-04-26 14:38:42,http://huelbins.biz/carmel_koch,,161.45.146.122,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n15756,6,354,2016-12-25 19:34:30,http://bartolettischowalter.biz/quinn.reynolds,,76.169.67.207,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n15757,6,354,2017-03-26 15:16:54,http://rutherford.org/stephon,,23.61.236.247,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n15758,6,354,2017-06-10 03:52:37,http://walter.com/guie_kris,,197.39.165.190,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n15759,6,354,2017-01-05 22:45:27,http://wuckert.org/eloisa.kreiger,,59.37.124.103,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n15760,6,354,2017-03-19 20:56:23,http://kiehn.io/jay_conroy,,37.61.132.219,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n15761,6,354,2017-03-26 02:40:52,http://andersondenesik.name/myles_ernser,,105.28.247.200,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n15762,6,354,2017-06-06 01:01:05,http://nicolas.biz/raymond_eichmann,,22.35.189.232,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n15763,6,354,2017-02-16 02:36:50,http://hilpert.name/jaida.sawayn,,117.174.127.178,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n15764,6,354,2017-06-13 21:07:58,http://wisozkgreenfelder.info/orland,,158.196.251.49,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n15765,6,354,2017-01-05 06:17:53,http://starkokuneva.io/sonya.rutherford,,55.152.124.60,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n15766,6,354,2017-02-07 14:28:03,http://blanda.co/hilbert,,71.85.15.39,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n15767,6,354,2017-04-18 17:19:45,http://kautzercole.info/elnora.cormier,,144.188.165.128,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n15768,6,354,2017-02-05 23:30:51,http://glover.org/noemi,,236.98.101.204,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n15769,6,354,2017-06-06 22:59:58,http://gleason.org/lupe_schuster,,107.74.114.160,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n15770,6,354,2017-05-09 21:04:32,http://boyer.name/freida.hackett,,71.106.140.79,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n15771,6,354,2016-12-29 03:42:42,http://block.name/milan_ledner,,90.88.87.48,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n15772,6,355,2017-03-29 07:03:23,http://collier.biz/shanie,,113.24.223.92,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n15773,6,355,2017-02-27 13:07:20,http://kub.info/sienna,,78.157.219.9,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n15774,6,355,2017-04-08 00:24:35,http://toylemke.net/pedro,,166.90.45.203,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n15775,6,355,2017-06-10 15:27:16,http://rogahn.com/nona,,222.245.242.119,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n15776,6,355,2017-01-01 09:24:21,http://quitzon.org/oswald,,107.169.230.172,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n15777,6,355,2017-02-13 11:35:21,http://boyer.name/zelda,,201.125.143.5,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n15778,6,355,2017-02-07 02:22:46,http://wolf.co/quincy_predovic,,84.177.162.219,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n15779,6,355,2017-04-12 02:29:59,http://kling.info/warren.schmeler,,182.52.38.34,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n15780,6,355,2017-05-26 20:11:50,http://thielkunze.net/vella,,103.132.253.49,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n15781,6,355,2017-04-07 19:30:39,http://connelly.info/aryanna.satterfield,,112.79.129.252,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n15782,6,355,2016-12-31 22:26:29,http://raynorhackett.biz/kaelyn,,16.214.75.241,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n15783,6,355,2017-06-06 05:32:40,http://hermanspinka.name/monica_cruickshank,,96.96.188.233,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n15784,6,355,2017-01-29 16:39:43,http://nicolas.biz/herman_mante,,124.73.179.221,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n15785,6,355,2017-01-07 18:48:57,http://dickipollich.biz/maryam,,177.125.76.234,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n15786,6,355,2017-04-20 15:58:13,http://oconner.net/teresa.dooley,,221.147.18.16,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n15787,6,355,2017-03-08 13:42:20,http://littel.info/cicero,,6.197.99.129,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n15788,6,355,2017-01-01 12:06:30,http://tromp.io/frieda.dach,,35.54.157.86,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n15789,6,355,2017-02-01 23:54:37,http://schmidt.info/titus,,187.51.14.191,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n15790,6,355,2017-05-15 17:50:14,http://runolfon.biz/mabel_moen,,195.90.34.116,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n15791,6,355,2017-03-09 19:25:56,http://prosacco.io/alia.mccullough,,38.77.168.21,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n15792,6,355,2017-06-13 03:55:26,http://barrows.com/marcos,,181.159.83.74,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n15793,6,355,2017-01-08 17:47:23,http://towne.name/rose_glover,,177.14.77.246,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n15794,6,355,2017-01-28 06:08:01,http://klingmayer.com/jake,,42.150.58.145,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n15795,6,355,2017-05-26 17:38:16,http://sipesrunte.com/albertha.morar,,72.231.184.123,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n15796,6,355,2017-05-29 22:33:30,http://wittinggreenholt.biz/abdul,,209.99.242.152,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n15797,6,355,2017-04-12 02:21:15,http://hills.biz/dawn,,251.99.139.54,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n15798,6,355,2017-06-11 11:01:08,http://bahringerrutherford.com/martina_anderson,,129.97.212.115,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n15799,6,355,2017-05-08 02:51:06,http://boscoryan.info/larue,,18.142.69.216,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n15800,6,355,2017-05-30 07:48:22,http://veumrobel.info/hyman,,37.25.57.126,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n15801,6,355,2017-02-16 01:38:19,http://stanton.info/newell.nienow,,32.102.165.207,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n15802,6,355,2017-02-14 22:23:25,http://sanford.biz/jayme.gusikowski,,142.220.42.13,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n15803,6,355,2017-02-28 19:16:03,http://wolfadams.org/adan_halvorson,,109.69.242.210,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n15804,6,355,2017-03-11 02:08:14,http://ondrickavon.name/araceli.smith,,217.205.215.61,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n15805,6,355,2017-01-06 19:59:42,http://cartwrightmetz.net/juliet_keebler,,201.106.40.35,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n2957,2,65,2017-05-10 14:25:29,http://funk.biz/ryan.oconner,0.4598884545,39.39.181.3,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n2958,2,65,2017-06-09 22:51:34,http://murray.name/thora_mccullough,0.9545603204,224.92.149.187,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n2959,2,65,2016-12-25 02:10:14,http://rolfson.net/oma.boehm,0.7263601725,206.191.214.130,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n2960,2,65,2017-03-16 12:49:41,http://stoltenberg.info/dejuan,0.9851600068,61.194.244.186,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n2961,2,65,2016-12-21 06:37:22,http://schaeferorn.co/shawna,0.8438723357,142.52.5.133,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n2962,2,65,2017-05-28 08:05:35,http://strosin.info/vito.kris,0.7085063828,125.86.54.175,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n2963,2,66,2017-03-22 22:46:01,http://rogahn.info/elouise,0.9048305324,65.210.62.23,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n2964,2,66,2016-12-25 03:15:38,http://von.net/kelsie_mclaughlin,0.1989095692,2.95.231.51,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n2965,2,66,2017-06-06 14:25:48,http://bernhard.com/fernando.doyle,0.6860244887,244.118.230.56,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n2966,2,66,2016-12-18 19:59:17,http://hane.com/alf,0.9810337320,213.94.223.147,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n2967,2,66,2017-04-22 20:43:42,http://bechtelar.name/zena.nitzsche,0.8785353873,29.147.253.112,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n2968,2,66,2017-05-31 15:20:19,http://padberg.info/johnpaul,0.3705462125,29.147.115.131,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n2969,2,66,2017-03-12 07:40:15,http://mueller.biz/bernard,0.1321717799,251.115.229.91,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n2970,2,66,2017-04-15 11:56:14,http://berge.biz/madelyn,0.6632042283,192.120.175.167,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n2971,2,66,2017-06-11 05:31:31,http://gusikowskipowlowski.info/dan,0.8257899771,77.83.85.72,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n2972,2,66,2017-04-11 18:01:11,http://damore.org/green,0.4497904166,152.231.57.202,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n2973,2,66,2017-02-07 17:50:37,http://strackebecker.info/hertha.fritsch,0.2365714273,30.242.101.215,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n2974,2,66,2017-02-13 10:12:12,http://schinner.org/eloy,0.9443732849,198.159.6.235,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n2975,2,66,2017-04-21 05:59:29,http://nikolausterry.info/elenora.kerluke,0.2105318936,149.145.55.215,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n2976,2,66,2017-02-11 10:03:11,http://prohaska.com/amelia_kuphal,0.7121725580,119.194.184.32,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n3946,2,87,2017-01-09 13:42:45,http://larkin.biz/bruce,,236.17.229.221,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n2977,2,66,2017-02-06 15:22:03,http://weimann.net/josiane_lubowitz,0.9060446351,71.22.90.168,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n2978,2,66,2017-01-25 02:40:05,http://hamilllubowitz.com/lambert.turner,0.6888431909,11.29.95.60,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n2979,2,66,2017-02-12 13:20:57,http://naderkshlerin.name/joany_schulist,0.9559052130,146.211.117.92,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n2980,2,66,2017-04-13 17:39:17,http://little.org/freeda.berge,0.4453407911,202.137.198.167,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n2981,2,66,2017-02-06 11:03:53,http://mckenzie.co/providenci,0.3175380580,133.33.158.61,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n2982,2,66,2017-03-24 06:53:40,http://sauer.com/jacinto,0.6318277114,254.235.133.73,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n2983,2,66,2017-02-01 03:24:03,http://oberbrunnerkonopelski.net/alfredo,0.9653703150,142.74.218.61,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n2984,2,66,2017-03-20 22:46:05,http://moen.biz/darrin_white,0.2553838715,153.16.135.189,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n2985,2,66,2017-01-25 19:01:46,http://moore.name/bernita_hoppe,0.1828467353,179.234.124.35,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n2986,2,66,2017-05-30 11:25:09,http://morarrowe.net/mina,0.2323580328,116.145.52.152,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n2987,2,66,2017-02-06 11:17:07,http://larsonfeeney.io/hertha_jerde,0.7669827216,66.143.193.213,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n2988,2,66,2017-05-13 06:28:43,http://boehmturcotte.info/karen,0.1697935864,251.151.128.147,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n2989,2,66,2017-01-04 21:37:10,http://gibsonrowe.biz/omer,0.9791108276,26.248.249.185,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n2990,2,66,2017-06-03 15:15:20,http://altenwerth.name/filiberto_hegmann,0.7634864120,36.88.99.83,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n2991,2,66,2017-04-12 05:01:30,http://erdman.info/desmond_leuschke,0.7755646847,228.110.223.107,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n2992,2,66,2017-01-02 09:53:41,http://denesik.biz/kathryn.champlin,0.9921455659,94.6.133.44,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n2993,2,66,2017-03-22 02:32:10,http://klinglubowitz.com/augustus_rempel,0.9848990854,248.221.106.235,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n2994,2,66,2017-02-21 13:54:11,http://kubframi.name/wilton_turcotte,0.0825520141,58.198.245.22,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n2995,2,66,2017-04-10 04:37:39,http://jenkins.co/jermain,0.5959374033,252.21.248.109,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n2996,2,66,2017-06-13 07:57:54,http://krajcikkshlerin.net/lysanne,0.7657235355,176.236.165.140,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n2997,2,66,2017-03-20 19:11:14,http://skiles.net/kareem_rogahn,0.9188463793,44.237.204.210,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n2998,2,66,2017-05-05 12:13:50,http://simonis.info/enos,0.5870025946,155.3.223.104,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n2999,2,66,2016-12-19 22:02:29,http://feest.biz/brain,0.3884351561,56.244.6.107,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n3000,2,66,2017-05-22 01:28:18,http://ryanreichel.name/laurianne.durgan,0.3051801624,77.219.129.58,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n3001,2,66,2016-12-29 19:59:57,http://lockman.biz/lamont,0.4292839274,89.120.122.249,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n3002,2,66,2017-01-25 22:27:48,http://abshire.net/amely.blanda,0.0660953820,85.82.85.112,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n3003,2,66,2017-01-30 21:03:48,http://binshartmann.org/providenci.kunze,0.2334200402,94.139.245.40,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n3004,2,66,2017-04-25 16:15:18,http://torphy.co/arjun,0.3727160113,137.233.151.138,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n3005,2,66,2017-04-16 05:43:20,http://labadie.co/bertram_moriette,0.1648455782,122.158.147.236,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n3006,2,66,2017-06-10 02:27:16,http://beahan.co/jaeden,0.6726252622,122.33.165.51,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n3007,2,66,2017-03-12 03:45:25,http://jones.co/chanel,0.0763117607,40.69.10.136,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n9893,4,221,2017-06-09 14:06:30,http://howe.name/elva_littel,0.9960789505,68.155.14.95,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n9894,4,221,2017-05-27 04:42:40,http://stehr.com/vickie,0.2653502529,43.199.219.141,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n9895,4,221,2017-04-24 00:14:23,http://keler.org/caterina_prohaska,0.8508095690,64.189.128.171,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n9896,4,221,2017-06-14 01:04:30,http://gerlach.name/barton,0.3212696929,84.4.173.119,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n9897,4,221,2017-04-28 04:14:43,http://moore.io/camilla,0.6867684723,135.249.86.31,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n9898,4,221,2017-01-19 11:18:09,http://okeefe.com/jaunita.von,0.8567044926,249.109.214.30,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n9899,4,221,2017-03-06 06:46:41,http://darehyatt.org/nora.moriette,0.7092761715,152.172.120.197,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n9900,4,221,2017-06-01 11:07:14,http://kundecasper.net/ernie,0.7294184274,117.222.154.17,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n9901,4,221,2016-12-16 10:09:22,http://daniel.info/anna,0.5186599459,116.16.243.80,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n9902,4,222,2017-03-24 03:07:58,http://heaney.biz/diego,0.8655071324,221.192.161.229,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n9903,4,222,2017-04-02 10:28:29,http://ryan.info/easton,0.0102671841,209.49.218.82,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n9904,4,222,2017-06-10 16:43:02,http://deckow.info/maynard,0.4657222618,28.44.31.215,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n9905,4,222,2016-12-19 15:08:15,http://bartellborer.co/ruben,0.5136799851,220.111.248.216,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n9906,4,222,2017-03-08 03:37:12,http://murphy.info/kaden,0.1278335126,238.211.202.211,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n9907,4,222,2017-03-11 09:44:03,http://gleason.org/marian.pagac,0.1298642076,103.247.6.215,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n9908,4,222,2017-05-31 16:10:38,http://kelerjacobi.io/geoffrey.rodriguez,0.0282220485,184.95.104.134,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n9909,4,222,2017-03-02 06:23:48,http://reillyernser.org/guy_becker,0.7981495528,83.207.95.86,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n9910,4,222,2017-04-14 12:48:28,http://wolffullrich.com/deshaun_goodwin,0.4252387221,154.184.95.229,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n9911,4,222,2017-02-23 20:04:24,http://langworth.co/carolyn.kohler,0.6644933621,76.50.179.226,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n9912,4,222,2017-06-05 05:55:21,http://douglasroob.info/frank,0.8580511222,234.116.153.6,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n9913,4,222,2017-01-14 15:16:20,http://gottliebreilly.info/edison.buckridge,0.9873040120,69.181.6.230,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n9914,4,222,2016-12-14 00:52:26,http://lynch.org/stone_becker,0.6274415243,54.51.187.136,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n9915,4,222,2017-02-10 15:39:15,http://ferry.info/kenya,0.6103079753,205.15.73.109,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n9916,4,222,2017-01-26 15:00:04,http://kertzmann.net/joelle,0.1879752067,77.216.217.185,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n9917,4,222,2017-02-11 09:40:42,http://gutmanntrantow.org/yesenia,0.8999576168,164.25.201.105,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n9918,4,222,2017-02-11 07:33:38,http://spinkabarton.co/lexus,0.0972669064,105.190.70.160,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n9919,4,222,2017-05-02 06:43:15,http://adams.com/julio.mills,0.4950991137,98.63.222.43,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n9920,4,222,2017-05-12 04:11:09,http://ziemestark.net/mohamed,0.2543832217,170.78.156.208,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n9921,4,222,2017-02-03 20:19:30,http://simonis.co/stevie,0.4043849745,254.159.254.107,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n9922,4,222,2017-02-11 09:50:25,http://ankunding.co/wyatt.rempel,0.2679693583,44.36.123.40,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n9923,4,222,2017-01-30 01:00:42,http://kirlin.co/daisha,0.7674543857,187.120.193.63,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n9924,4,222,2017-02-23 03:24:45,http://bode.biz/garrett.mayer,0.2497501477,128.22.29.2,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n9925,4,222,2016-12-20 09:15:59,http://friesenkuhlman.name/millie,0.6171785043,225.91.191.149,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n9926,4,222,2017-04-30 01:47:26,http://champlinlang.com/dedrick,0.3992283042,203.81.44.2,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n9927,4,222,2017-04-13 13:06:26,http://lehnerkuphal.info/keegan_sanford,0.8585245644,104.234.159.8,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n9928,4,222,2017-02-20 19:54:59,http://herzogquitzon.biz/valentin,0.1572329922,105.65.99.96,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n9929,4,222,2017-04-16 19:56:56,http://heathcote.net/doug,0.5846206944,252.194.173.209,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n9930,4,222,2016-12-22 11:47:03,http://kuhiccole.co/jettie_von,0.1279437551,250.119.252.2,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n9931,4,222,2017-04-17 10:24:47,http://schmitt.info/giovanna_mclaughlin,0.4299213108,33.48.139.151,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n9932,4,222,2017-06-03 01:53:44,http://keebler.io/leola,0.9049610680,146.30.172.177,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n9933,4,222,2017-03-05 00:33:29,http://rolfsonthiel.info/cayla.nolan,0.6333016458,66.13.252.63,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n9934,4,222,2017-06-12 01:49:38,http://beahan.com/alberta.carroll,0.7913128933,96.14.5.129,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n9935,4,222,2017-02-13 18:14:00,http://johnsko.info/robert_feeney,0.1717907512,2.60.238.102,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n9936,4,222,2016-12-23 01:21:25,http://oberbrunnerhermann.io/ernesto,0.6817795429,118.222.176.193,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n9937,4,222,2017-02-28 11:30:26,http://goyette.co/eugene.leannon,0.2698759457,147.124.4.230,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n9938,4,222,2017-05-18 12:23:47,http://brakus.biz/vidal,0.1500199384,124.136.225.37,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n9939,4,222,2017-03-08 00:19:50,http://gerlach.name/keegan.okuneva,0.3891908024,23.33.58.74,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n9940,4,222,2017-04-19 23:09:24,http://pagac.info/jabari_ratke,0.2221759482,246.87.133.187,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n9941,4,222,2017-06-12 23:42:54,http://dietrichgleichner.biz/hans_bednar,0.6254960501,49.73.140.252,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n9942,4,223,2017-03-24 06:15:53,http://hand.biz/aleandra.shanahan,0.1325028515,209.137.5.210,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n9943,4,223,2017-01-12 23:08:52,http://keebler.co/lora_schmitt,0.7249031819,171.91.64.210,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n9944,4,223,2017-05-02 13:56:37,http://nitzsche.name/selena,0.4627372954,147.212.182.23,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n6890,3,151,2017-01-28 22:40:08,http://dach.io/olen.mills,,187.69.35.112,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n6891,3,151,2017-03-26 09:06:10,http://lednerjerde.name/urban,,221.120.162.48,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n6892,3,152,2017-05-28 21:49:50,http://maggio.com/jace.jones,,84.105.36.87,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n6893,3,152,2017-03-17 19:12:26,http://gulgowski.net/ludie,,110.13.70.91,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n6894,3,152,2017-01-31 16:41:25,http://fay.net/nolan.gleichner,,226.83.253.204,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n6895,3,152,2017-03-01 11:33:12,http://sawayn.biz/bryce.howe,,47.150.160.185,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n6896,3,152,2017-03-31 03:34:21,http://runolfsdottirharvey.org/jayne_corkery,,28.37.209.92,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n6897,3,152,2017-05-25 10:26:30,http://wehnerbogisich.info/keeley.runolfsdottir,,59.55.70.145,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n6898,3,152,2017-02-01 09:34:16,http://walker.co/alvah,,94.121.167.207,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n6899,3,152,2017-04-06 12:18:07,http://rauzboncak.biz/elna,,179.195.168.149,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n6900,3,152,2017-01-02 06:08:00,http://heelmoriette.info/kasandra.dietrich,,67.2.173.101,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n6901,3,152,2017-04-04 14:17:31,http://lemke.co/kiera.prosacco,,216.147.251.11,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n6902,3,152,2017-03-11 18:45:27,http://douglas.com/kamryn,,94.125.79.142,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n6903,3,152,2016-12-29 08:49:11,http://mullerdare.net/toby.feeney,,93.117.252.212,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n6904,3,152,2017-04-23 06:28:59,http://goyettemarks.net/calista.daniel,,9.240.152.89,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n6905,3,152,2017-05-21 12:50:35,http://hermannnicolas.com/crystal,,193.203.206.62,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n6906,3,152,2016-12-24 20:34:08,http://kreiger.org/annabell_adams,,190.193.26.231,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n6907,3,152,2017-05-27 23:38:55,http://bauch.io/lucile,,118.100.70.12,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n6908,3,152,2017-01-04 20:14:54,http://volkmanquitzon.org/jordan,,15.70.183.109,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n6909,3,152,2017-01-14 15:13:20,http://kuhn.org/deon,,246.66.6.8,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n6910,3,152,2017-06-13 05:15:13,http://abshire.co/christy.gerhold,,70.12.71.230,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n6911,3,152,2017-04-17 23:44:47,http://harbertromp.net/camila,,54.87.226.70,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n6912,3,152,2017-03-26 08:01:05,http://bechtelarhaag.info/gretchen.heel,,62.65.9.85,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n6913,3,152,2017-03-26 12:49:17,http://casper.info/belle_eichmann,,180.110.102.48,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n6914,3,152,2017-01-15 20:43:31,http://funk.net/eugene_cain,,87.46.189.63,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n6915,3,152,2017-01-17 23:13:46,http://doyleharris.com/anibal.sanford,,31.189.231.184,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n6916,3,152,2017-02-26 15:02:58,http://toy.biz/jalen_heel,,59.99.135.21,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n6917,3,152,2017-02-22 02:18:46,http://harveygoyette.io/autumn,,252.224.155.123,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n6918,3,152,2017-05-30 09:30:24,http://sipes.net/christiana_davis,,17.128.237.152,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n6919,3,152,2017-01-11 10:43:10,http://doyle.net/christiana,,92.83.129.48,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n6920,3,152,2017-01-09 09:03:17,http://okeefe.io/madie,,112.80.244.168,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n6921,3,152,2017-06-05 02:07:49,http://mannabernathy.biz/georgianna_rosenbaum,,104.115.217.83,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n6922,3,152,2017-04-26 23:20:40,http://swaniawski.com/granville_upton,,166.121.66.220,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n6923,3,152,2017-02-11 10:25:51,http://kling.info/chandler.dibbert,,196.224.116.200,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n6924,3,152,2017-03-12 07:23:38,http://pacochakoch.net/keon_osinski,,29.232.98.162,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n6925,3,152,2017-02-13 21:28:05,http://kuhn.info/estelle,,163.60.121.195,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n6926,3,152,2017-05-30 15:04:47,http://walshjast.io/francis,,219.135.220.118,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n6927,3,152,2017-04-25 02:28:00,http://hegmann.name/jaida,,59.61.135.141,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n6928,3,152,2016-12-28 22:25:16,http://rogahn.name/hank.gulgowski,,249.191.25.57,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n6929,3,152,2017-02-23 01:49:55,http://von.name/quinten.gutkowski,,201.101.168.4,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n6930,3,152,2017-01-01 16:29:43,http://murphy.name/barry,,138.43.205.91,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n6931,3,152,2017-01-21 20:38:28,http://kunde.co/francesca.heidenreich,,153.40.233.220,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n6932,3,152,2017-02-11 11:51:32,http://parisian.co/clare,,177.24.173.181,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n6934,3,152,2017-03-01 14:56:39,http://feeneybecker.info/bethany,,24.113.203.65,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n6935,3,152,2017-01-17 04:41:09,http://littel.co/viva,,36.252.127.127,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n6936,3,152,2017-05-17 10:27:49,http://boganbauch.net/jodie.nolan,,81.122.235.221,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n6937,3,152,2017-04-05 23:11:12,http://gottlieb.info/tiffany.tromp,,82.154.179.169,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n6938,3,152,2017-03-06 12:20:18,http://williamson.net/erick,,15.238.96.175,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n6939,3,152,2017-05-04 08:37:44,http://denesikharvey.io/casandra,,116.238.20.97,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n6940,3,152,2017-06-04 14:36:38,http://prohaskanienow.info/giuseppe,,67.102.112.75,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n6941,3,152,2017-01-12 05:08:55,http://jacobsoncummerata.io/valentine.gutmann,,219.82.181.181,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n6942,3,152,2016-12-19 01:55:38,http://kris.org/kattie,,159.122.97.227,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n6943,3,152,2017-02-12 03:10:21,http://okuneva.org/kayli,,170.251.126.98,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n6944,3,152,2017-04-17 09:15:07,http://walker.io/joshua_rohan,,166.68.19.112,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n6945,3,152,2017-04-21 12:59:13,http://buckridge.io/eulah,,237.159.111.94,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n6946,3,153,2017-06-04 19:14:24,http://welchkaulke.biz/elliot,,252.251.126.218,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n12805,5,286,2017-01-03 17:16:53,http://bergstrom.net/cali.murphy,,172.114.9.108,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n12806,5,286,2017-03-21 12:03:44,http://murray.net/dulce,,82.66.163.94,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n12807,5,286,2017-02-19 10:08:16,http://muller.biz/jordy,,47.227.130.25,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n12808,5,286,2016-12-14 01:10:59,http://satterfieldhackett.org/ivy,,110.92.177.150,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n12809,5,286,2017-01-08 18:13:27,http://koelpinruecker.name/lorena,,71.111.201.164,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n12810,5,286,2017-01-20 22:59:55,http://walkermante.io/elliott,,106.242.80.105,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n12811,5,286,2017-04-16 15:10:54,http://block.name/tierra,,61.20.96.210,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n12812,5,286,2017-04-30 05:42:18,http://hartmannaufderhar.info/alvera.ruecker,,10.35.155.236,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n12813,5,286,2017-04-06 07:59:32,http://runolfsdottir.net/brice_streich,,38.64.228.179,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n12814,5,286,2017-01-20 09:12:26,http://dickens.com/clara,,218.159.55.151,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n12815,5,286,2016-12-16 07:16:22,http://von.biz/ludie_ohara,,26.209.79.94,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n12816,5,286,2017-02-09 19:39:56,http://kling.biz/alexie,,190.76.86.8,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n12817,5,286,2017-05-05 16:17:00,http://keler.net/thurman_runolfon,,200.191.34.127,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n12818,5,286,2017-05-07 21:47:52,http://granthamill.biz/ewald,,181.201.48.72,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n12819,5,286,2017-03-31 07:23:38,http://ortizruecker.io/forest.purdy,,126.220.183.132,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n12820,5,286,2017-06-04 23:16:01,http://jaskolskizieme.biz/mireille_armstrong,,127.165.31.103,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n12821,5,286,2017-06-11 23:46:36,http://labadie.org/floy_dare,,221.238.153.37,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n12822,5,286,2017-01-24 14:54:51,http://hills.name/carter.koelpin,,61.7.17.163,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n12823,5,286,2017-05-25 12:35:42,http://hartmann.co/terrance_weber,,136.232.187.203,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n12824,5,286,2017-03-16 19:55:57,http://spencer.com/hettie,,116.226.240.199,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n12825,5,286,2017-04-19 13:02:40,http://bayer.info/jeffery,,201.246.224.5,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n12826,5,286,2017-01-28 19:48:40,http://kingshanahan.name/josianne,,112.253.181.164,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n12827,5,286,2017-02-20 13:36:52,http://kohlermante.io/lloyd.west,,214.228.48.49,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n12828,5,286,2017-05-15 21:34:21,http://abshire.com/marcelina,,58.72.47.44,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n12829,5,286,2017-04-18 20:00:02,http://jerdecain.info/vicky,,227.111.149.147,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n12830,5,286,2017-06-13 06:39:00,http://terry.biz/ethan,,118.61.50.152,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n12831,5,286,2016-12-28 07:37:58,http://funk.com/catharine,,91.98.182.83,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n12832,5,286,2017-03-31 13:43:07,http://harber.com/alex_durgan,,133.20.26.212,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n12833,5,286,2017-04-04 01:42:05,http://litteljast.co/ruben,,20.189.26.160,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n12834,5,286,2017-01-27 00:10:18,http://mueller.io/idell_daniel,,237.244.109.236,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n12835,5,286,2017-06-08 17:35:34,http://kertzmannhand.com/amanda,,50.125.19.114,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n12836,5,286,2017-05-08 05:17:53,http://wintheisercrist.org/elody,,194.232.36.216,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n12837,5,286,2017-05-26 02:10:53,http://torp.info/abbigail,,234.208.236.207,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n12838,5,286,2016-12-18 21:18:58,http://wilkinson.com/berenice.hagenes,,244.26.133.105,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n12839,5,286,2017-05-01 06:30:00,http://hamill.biz/edyth.aufderhar,,169.175.161.20,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n12840,5,286,2017-03-06 18:13:27,http://mcdermottskiles.biz/mariane.steuber,,56.156.192.142,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n12841,5,286,2017-01-09 08:02:00,http://kreiger.org/mina,,35.163.31.79,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n12842,5,286,2017-05-07 00:49:08,http://koch.co/ella,,110.143.21.230,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n12843,5,286,2016-12-18 07:54:00,http://huels.biz/griffin,,237.15.242.136,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n12844,5,286,2017-02-11 13:10:57,http://herzog.biz/bell,,100.192.115.44,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n12845,5,287,2017-02-24 16:59:19,http://zemlak.name/victoria,,44.62.2.232,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n12846,5,287,2017-06-01 01:43:04,http://jerdezemlak.info/lacy,,62.82.128.55,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n12847,5,287,2017-01-18 09:07:19,http://bauchrolfson.net/benjamin.quigley,,223.202.227.95,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n12848,5,287,2017-05-21 01:47:02,http://turner.co/amani_johnston,,50.130.174.129,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n12849,5,287,2017-01-21 15:19:25,http://ondricka.biz/berry.lind,,126.242.159.40,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n12850,5,287,2017-04-18 03:07:07,http://schuster.org/queenie.barton,,188.245.250.54,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n12851,5,287,2017-05-10 17:36:10,http://gerlachstanton.io/jermain.kerluke,,253.95.51.108,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n12852,5,287,2017-05-05 22:36:23,http://cristtillman.net/gideon_nikolaus,,234.222.163.101,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n12853,5,287,2017-02-17 18:29:20,http://watsica.biz/hilma_wolf,,101.149.200.115,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n12854,5,287,2017-03-13 02:23:34,http://osinskiparisian.co/gunnar,,229.84.243.10,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n12855,5,287,2017-01-01 00:47:55,http://lehner.biz/august,,195.43.167.177,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n12856,5,287,2017-01-22 11:49:32,http://fahey.net/ophelia.schneider,,197.84.253.105,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n12857,5,287,2017-06-12 16:59:11,http://runolfon.org/alyson,,65.138.229.208,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n12858,5,287,2017-06-06 04:17:40,http://heathcotejohnston.co/amelia,,210.85.165.70,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n12859,5,287,2016-12-23 18:49:51,http://spinka.biz/donna,,19.52.66.25,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n15806,6,355,2016-12-25 04:08:29,http://mosciski.biz/ilene_rice,,208.129.24.189,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n15807,6,355,2017-03-13 23:08:26,http://bodestracke.net/nora,,216.44.115.200,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n15808,6,356,2017-02-28 09:16:53,http://welch.info/jillian,,110.168.68.218,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n15809,6,356,2017-05-20 17:46:06,http://rolfsonstamm.co/abdullah_bosco,,133.117.55.153,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n15810,6,356,2016-12-28 03:29:13,http://aufderhar.org/dashawn,,22.89.182.252,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n15811,6,356,2017-04-06 07:43:03,http://gleichnerko.co/jamel.hills,,29.31.227.37,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n15812,6,356,2017-02-20 17:55:44,http://toylegros.name/max.wuckert,,213.105.81.147,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n15813,6,356,2017-03-27 23:20:23,http://douglas.org/casandra,,187.137.116.109,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n15814,6,356,2017-06-09 11:39:03,http://leuschke.co/yeenia_hyatt,,112.7.174.40,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n15815,6,356,2017-02-17 02:52:12,http://kunde.org/israel.lindgren,,216.26.118.77,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n15816,6,356,2017-03-11 04:40:09,http://murazik.org/lindsay,,23.154.251.251,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n15817,6,356,2017-03-29 23:32:47,http://hagenes.co/eulah,,141.34.67.178,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n15818,6,356,2017-01-08 19:12:52,http://nader.io/robin.hagenes,,47.249.196.93,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n15819,6,356,2017-03-30 05:25:17,http://hand.name/deon,,166.136.132.185,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n15820,6,356,2017-04-07 04:22:06,http://bartoletti.biz/grayce_mante,,110.45.241.229,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n15821,6,356,2017-05-22 09:06:36,http://konopelskiframi.biz/liliana,,220.98.2.146,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n15822,6,356,2017-05-21 08:27:03,http://murphy.org/lucious,,31.248.90.149,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n15823,6,356,2017-05-06 17:29:55,http://krishand.info/kaycee_becker,,26.2.62.204,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n15824,6,356,2017-04-03 02:44:55,http://blick.org/geovanny_borer,,141.17.108.66,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n15825,6,356,2017-03-08 08:15:37,http://mayertgrimes.net/melia.altenwerth,,249.47.158.185,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n15826,6,356,2017-01-11 22:40:41,http://heathcotehowell.biz/terry_wiza,,122.124.179.166,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n15827,6,356,2017-03-23 19:54:40,http://abernathypagac.info/maximillian,,8.132.205.204,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n15828,6,356,2017-04-24 23:48:58,http://lowe.io/jimmy_walter,,78.100.18.96,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n15829,6,356,2017-01-09 16:44:39,http://boganhammes.co/khalil,,98.191.172.34,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n15830,6,356,2017-02-09 00:30:09,http://kohler.co/candace.hills,,233.188.146.34,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n15831,6,356,2017-01-14 15:44:56,http://lebsack.org/moises_schulist,,103.40.103.158,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n15832,6,356,2017-03-24 15:50:42,http://schoen.info/jarrell,,77.176.218.106,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n15833,6,356,2016-12-30 02:36:09,http://lowe.biz/abdullah,,234.154.210.211,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n15834,6,356,2016-12-20 16:06:03,http://terry.biz/amy,,75.211.88.47,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n15835,6,356,2017-05-11 07:23:12,http://schustermante.com/jovan_fisher,,178.210.221.181,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n15836,6,356,2016-12-17 02:29:18,http://vandervort.net/stanton,,64.31.36.126,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n15837,6,356,2017-05-25 13:41:43,http://terry.biz/benjamin_mccullough,,54.83.19.126,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n15838,6,356,2017-04-03 02:27:03,http://miller.co/chadrick,,248.157.100.104,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n15839,6,356,2017-05-06 11:04:20,http://gloverortiz.net/alysha_harris,,178.70.112.50,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n15840,6,356,2017-02-01 23:49:16,http://flatley.com/felipe.crooks,,75.160.34.182,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n15841,6,356,2017-03-28 16:40:04,http://purdyblock.biz/lula_gaylord,,44.43.47.123,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n15842,6,356,2017-02-22 21:01:56,http://kihn.co/joanne,,113.157.142.16,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n15843,6,356,2017-06-02 21:04:29,http://prohaska.org/sedrick_cain,,71.74.246.192,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n15844,6,356,2017-04-29 19:28:22,http://roobschinner.net/gerson.schaden,,254.217.247.187,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n15845,6,356,2017-05-04 07:21:35,http://wintheiser.name/francisco,,201.164.148.133,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n15846,6,356,2017-04-12 23:33:39,http://parker.name/forest_feil,,151.109.253.110,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n15847,6,356,2017-02-06 19:31:17,http://prosaccoaufderhar.com/elza.effertz,,225.25.158.87,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n15848,6,356,2017-01-23 07:58:32,http://emmerichdaugherty.com/janelle,,19.129.230.152,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n15849,6,356,2017-05-29 03:32:05,http://ernserbosco.biz/filomena,,66.199.32.228,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n15850,6,356,2017-03-10 13:28:45,http://beatty.net/willa,,176.150.106.231,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n15851,6,356,2017-02-20 12:31:30,http://larson.net/corine_grant,,150.13.195.66,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n15852,6,356,2017-02-25 15:58:30,http://lueilwitz.io/ryan,,27.190.206.215,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n15853,6,356,2017-04-03 20:38:57,http://cummings.net/oma,,138.150.187.129,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n15854,6,356,2016-12-29 01:54:02,http://wolff.org/silas,,50.207.103.206,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n15855,6,356,2017-01-22 07:02:58,http://quigley.org/selina,,42.126.110.89,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n15856,6,356,2017-03-30 02:38:59,http://oberbrunner.co/natalia,,162.254.41.35,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n15857,6,356,2017-02-05 20:27:36,http://monahanmurphy.biz/dominique_bartell,,218.87.200.114,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n15858,6,356,2017-01-07 14:36:49,http://carroll.io/hailie_cronin,,210.103.94.162,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n15859,6,356,2017-02-18 20:46:45,http://erdmanrau.name/alvina,,63.178.80.51,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n15860,6,356,2016-12-26 00:35:29,http://haleyhilll.co/jada.veum,,237.119.140.87,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n3008,2,66,2017-03-30 12:03:09,http://sporerhickle.net/america.dare,0.3283648343,226.74.219.121,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n3009,2,66,2017-06-03 22:00:53,http://macgyver.co/frederick_kuvalis,0.9103569328,176.134.139.198,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n3010,2,66,2017-03-25 13:20:31,http://bogan.biz/aric,0.4612352990,117.96.213.96,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n3011,2,66,2017-04-25 04:56:04,http://pagac.name/adella,0.5499951986,214.209.201.142,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n3012,2,66,2017-03-22 10:57:58,http://upton.org/lysanne,0.4207174868,249.165.157.59,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n3013,2,66,2017-02-05 13:55:34,http://orn.co/myrtle,0.2625692102,118.116.21.133,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n3014,2,66,2017-05-18 22:38:22,http://bernier.co/piper_effertz,0.4949108400,144.61.245.183,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n3015,2,66,2017-02-07 01:06:16,http://wardbins.com/reilly,0.8417543620,135.45.156.144,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n3016,2,66,2017-06-06 13:16:40,http://framidaniel.info/kraig,0.4584310559,200.238.173.8,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n3017,2,66,2017-01-23 22:07:45,http://windler.info/elza,0.9858953043,230.39.63.31,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n3018,2,66,2017-04-03 17:55:28,http://erdman.name/alex.gutkowski,0.1428229556,57.253.194.30,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n3019,2,66,2017-01-02 22:48:13,http://hintz.com/estel_bogisich,0.9664657952,156.246.37.181,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n3020,2,66,2017-05-18 15:37:57,http://stantonpacocha.net/carlotta,0.7941341497,229.245.85.80,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n3021,2,66,2017-03-16 03:48:26,http://mannbrakus.biz/otto,0.5628489351,81.148.186.70,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n3022,2,66,2017-01-15 09:10:34,http://berge.io/margaretta.rodriguez,0.7975393241,236.63.140.167,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n3023,2,66,2017-03-23 14:41:44,http://erdmankreiger.org/thea_graham,0.0511527373,197.21.220.182,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n3024,2,66,2017-03-09 12:42:59,http://lindgreenfelder.io/graham.steuber,0.5863194511,37.29.217.224,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n3025,2,66,2017-01-28 22:05:34,http://fisher.co/armand_lebsack,0.6187797718,206.166.82.132,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n3026,2,66,2017-04-26 01:47:14,http://prosaccoherzog.info/wava.roberts,0.1244857225,219.99.30.4,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n3027,2,66,2017-05-21 15:41:34,http://bosco.biz/abner,0.3120541704,249.212.95.250,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n3028,2,66,2017-02-18 09:06:51,http://anderson.info/celestine.marquardt,0.3742896222,68.116.83.120,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n3029,2,66,2017-03-30 11:12:47,http://zemlakmarks.info/catharine,0.3660466952,68.83.220.89,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n3030,2,66,2017-03-11 18:03:26,http://skiles.com/kaleb,0.1424076583,230.143.84.11,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n3031,2,66,2017-03-25 16:20:28,http://dibbert.biz/alfonso.gutkowski,0.5790093665,178.230.246.143,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n3032,2,67,2016-12-26 16:11:08,http://hartmann.co/keshaun,0.3731311886,8.122.124.190,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n3033,2,67,2016-12-21 02:06:24,http://rippingerlach.co/ned.huel,0.5341077110,102.45.227.30,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n3034,2,67,2016-12-24 22:11:16,http://lowebeatty.org/frida,0.8777716334,176.234.178.137,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n3035,2,67,2017-02-17 03:55:22,http://raynorjenkins.name/gladys_braun,0.0803604485,156.196.9.19,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n3036,2,67,2017-03-20 21:33:37,http://feeney.io/edmond_cummerata,0.9128735293,252.63.65.230,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n3037,2,67,2016-12-16 13:46:46,http://kreiger.name/pinkie,0.4912933073,183.77.25.246,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n3038,2,67,2017-01-14 11:09:35,http://kemmer.org/jakob_tremblay,0.3391633804,107.91.99.119,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n3039,2,67,2017-04-21 05:00:33,http://schmeler.org/odea,0.0857790696,177.15.254.124,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n3040,2,67,2017-06-07 16:37:16,http://haley.biz/lorenzo,0.7017768962,82.5.123.131,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n3041,2,67,2017-02-21 23:14:16,http://douglaskris.io/ashleigh,0.8891327685,246.88.81.74,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n3042,2,67,2017-01-04 10:01:03,http://mckenziesipes.info/roberta.hermann,0.7302447964,54.73.235.92,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n3043,2,67,2017-01-03 11:04:04,http://harber.name/ryleigh,0.3016545887,159.52.28.219,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n3044,2,67,2017-03-08 20:07:48,http://schuppe.net/susan_durgan,0.8988463304,244.87.97.239,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n3045,2,67,2017-04-09 05:07:06,http://weinat.io/kayla,0.9491274007,173.236.51.134,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n3046,2,67,2017-05-23 07:07:50,http://quitzonkunde.co/glenna_dietrich,0.0074697193,94.152.80.191,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n3047,2,67,2017-03-07 11:32:02,http://cormierprice.name/boris,0.8103363758,161.72.201.87,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n3048,2,67,2016-12-24 17:15:38,http://fay.biz/wade,0.6269527414,159.25.67.90,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n3049,2,67,2017-06-03 12:52:34,http://walter.name/gerda_predovic,0.1856226982,181.65.210.206,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n3050,2,67,2017-05-05 21:07:19,http://considine.io/kamron.spinka,0.7511434170,10.83.203.13,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n3051,2,67,2017-01-14 21:25:26,http://ruel.co/micaela_mccullough,0.9971675376,221.92.206.5,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n3052,2,67,2017-01-29 03:16:34,http://kirlincorwin.net/halie.hegmann,0.6613248013,194.230.14.145,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n3053,2,67,2017-04-13 18:32:27,http://trantow.com/ahmad.wehner,0.6868047757,100.53.13.67,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n3054,2,67,2017-03-03 02:19:38,http://jenkinsmacejkovic.info/leon,0.3702052801,110.115.161.22,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n3055,2,67,2017-01-28 13:17:35,http://dibbert.net/eve.legros,0.4927245752,94.182.201.101,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n3056,2,67,2017-05-21 16:57:28,http://boscocrona.co/eli_aufderhar,0.8121811029,44.16.202.177,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n3057,2,67,2017-05-15 04:26:29,http://thompson.info/rachael.rodriguez,0.2974642401,114.76.98.56,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n3058,2,67,2017-05-16 23:14:35,http://gulgowski.info/mafalda,0.2550798173,246.111.228.247,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n3059,2,67,2017-05-20 03:01:37,http://daugherty.info/christop,0.5640715361,245.110.26.134,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n9945,4,223,2016-12-28 17:56:33,http://bartonluettgen.net/linwood_erdman,0.6676393242,110.103.42.180,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n9946,4,223,2017-05-13 16:19:06,http://vonruedenoberbrunner.org/domingo,0.0312599221,143.90.107.160,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n9947,4,223,2017-04-16 10:02:57,http://kemmer.biz/wyatt,0.9541460641,60.144.64.198,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n9948,4,223,2017-03-29 01:55:50,http://hegmann.net/lou.kuphal,0.4039689280,151.242.30.163,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n9949,4,223,2017-03-04 09:43:30,http://feeneycarroll.org/murphy.kutch,0.9157664444,189.238.147.184,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n9950,4,223,2017-01-29 17:17:23,http://okuneva.net/margret.lubowitz,0.6254031044,244.70.156.109,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n9951,4,223,2016-12-23 21:10:39,http://nikolaus.org/lia,0.6835859605,91.225.166.203,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n9952,4,223,2017-02-10 17:40:40,http://smithweinat.co/tina_gutkowski,0.5343808740,8.195.157.154,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n9953,4,223,2017-01-09 22:01:26,http://wilderman.io/queen,0.4040836962,238.90.3.214,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n9954,4,223,2017-01-11 20:11:58,http://treutelgoodwin.net/cordie,0.2247655053,55.124.23.219,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n9955,4,223,2017-02-25 13:54:34,http://schinnerwest.io/tamara,0.4195395606,147.114.192.59,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n9956,4,223,2016-12-23 20:41:02,http://zulauf.com/brenna_goldner,0.3522003279,209.29.221.107,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n9957,4,223,2017-02-15 04:01:03,http://quigleykonopelski.co/caandre,0.2052398580,226.220.92.246,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n9958,4,223,2017-05-11 16:39:58,http://stokes.org/keagan_howe,0.4636249267,144.229.236.137,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n9959,4,223,2017-04-28 15:18:19,http://rosenbaum.info/bonita,0.1770827803,178.154.92.128,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n9960,4,223,2017-04-12 01:19:30,http://bayer.co/imogene,0.3849046257,142.216.51.240,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n9961,4,223,2016-12-22 22:21:20,http://breitenbergroberts.org/justina.stokes,0.8753023837,67.85.40.13,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n9962,4,223,2017-06-07 23:16:29,http://berge.info/tierra_gleichner,0.4455484062,211.7.132.108,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n9963,4,223,2017-03-09 15:29:17,http://bernier.biz/shyann_mccullough,0.0260969728,28.165.40.76,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n9964,4,223,2017-01-31 11:10:31,http://corkery.com/sigmund_jerde,0.0856309933,20.121.112.186,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n9965,4,223,2017-01-27 09:53:49,http://millersawayn.name/kayden,0.6371018193,195.40.206.61,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n9966,4,223,2017-05-03 12:06:18,http://hickle.com/monty_schumm,0.2485320767,5.121.60.192,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n9967,4,223,2017-02-14 05:24:12,http://ebert.net/tyra_runte,0.0960862540,113.16.230.252,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n9968,4,223,2017-04-01 12:33:05,http://bednar.biz/eloy.gleason,0.9805692589,196.60.176.22,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n9969,4,223,2017-05-05 12:54:23,http://bruen.net/sidney,0.9282815899,115.239.158.223,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n9970,4,223,2017-04-07 10:47:16,http://rath.org/emie,0.9929037398,187.113.14.41,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n9971,4,223,2017-03-25 11:10:14,http://krajcik.info/aiyana,0.9301638389,180.179.119.203,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n9972,4,223,2017-01-19 12:32:38,http://farrell.io/linda,0.5165077297,97.229.172.252,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n9973,4,223,2017-02-24 23:14:06,http://fay.com/ellen,0.2160714421,102.157.85.223,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n9974,4,223,2017-01-26 10:15:19,http://beatty.info/nova.labadie,0.4601833123,169.112.208.218,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n9975,4,223,2017-05-20 17:07:33,http://upton.com/stephen,0.7412656474,71.153.111.220,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n9976,4,223,2017-02-28 02:54:07,http://okunevacarroll.name/damaris,0.5931668933,85.227.146.164,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n9977,4,223,2017-05-22 13:48:48,http://bashirian.name/drew,0.3627805645,21.81.210.142,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n9978,4,223,2017-01-26 10:37:46,http://braunjenkins.org/morton.thiel,0.3657094415,20.23.137.3,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n9979,4,223,2017-05-06 15:30:51,http://green.net/andre,0.8152582598,80.84.50.239,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n9980,4,223,2017-05-05 17:25:25,http://weimann.io/juliana.blanda,0.2129642866,13.68.242.188,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n9981,4,223,2017-04-02 19:37:02,http://ritchie.io/matt.ferry,0.2135305360,170.167.181.24,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n9982,4,223,2017-01-09 03:23:44,http://altenwerthgulgowski.info/ervin,0.5326790224,38.26.105.108,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n9983,4,223,2017-06-10 17:20:40,http://nienow.org/mallie,0.4930635926,48.188.204.40,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n9984,4,223,2017-05-20 00:57:06,http://kaulke.net/jamel,0.4598325005,204.40.52.225,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n9985,4,223,2017-02-10 05:08:36,http://kuhlman.com/gerardo.rippin,0.0906940136,149.81.223.211,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n9986,4,223,2017-05-30 13:34:51,http://oberbrunnerveum.biz/rosetta,0.3472847485,98.124.58.230,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n9987,4,223,2017-06-09 08:56:59,http://hettingerschowalter.name/sabina.bode,0.4082582901,117.190.179.76,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n9988,4,223,2017-04-30 19:25:37,http://kuvalismaggio.com/river,0.0139497046,124.174.246.38,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n9989,4,223,2016-12-23 04:25:38,http://champlin.info/clarabelle,0.9773780236,158.121.13.9,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n9990,4,223,2017-03-01 20:00:41,http://cruickshankjaskolski.com/otilia.kris,0.0025326056,118.168.135.138,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n9991,4,223,2017-05-13 20:08:11,http://walterlegros.io/valentin_lueilwitz,0.2092987679,249.194.244.99,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n9992,4,223,2017-04-30 11:28:59,http://rosenbaum.name/queen,0.7928851507,60.72.7.71,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n9993,4,223,2017-05-12 15:47:21,http://pouros.biz/adolph.lang,0.4385363771,127.21.233.84,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n9994,4,223,2017-01-25 02:48:19,http://kovacek.info/cheyenne_hermann,0.1651114554,54.152.97.206,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n9995,4,223,2017-01-25 01:39:45,http://mcdermott.info/breanne_bergnaum,0.3033566916,172.39.200.225,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n6947,3,153,2017-04-15 09:49:04,http://kuhn.com/reyes,,221.195.49.112,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n6948,3,153,2017-02-03 07:01:23,http://predovic.name/percival,,231.75.84.206,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n6949,3,153,2017-05-27 23:30:47,http://bayer.io/abagail.heidenreich,,166.169.160.232,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n6950,3,153,2017-01-08 14:48:30,http://sipes.com/samanta,,6.71.193.159,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n6951,3,153,2017-03-06 00:55:55,http://shieldsernser.org/ken.hauck,,24.85.73.123,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n6952,3,153,2017-02-27 16:30:01,http://zulauf.io/rhianna.hoppe,,12.215.33.155,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n6953,3,153,2017-05-24 10:54:52,http://quitzonquigley.info/ayden,,158.60.130.137,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n6954,3,153,2017-01-10 14:08:20,http://boscokoch.net/leopold.smitham,,77.185.225.192,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n6955,3,153,2017-06-02 03:51:54,http://jonesmiller.net/armando.hettinger,,254.36.84.241,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n6956,3,153,2017-02-17 14:58:53,http://jenkins.name/kirsten.cremin,,171.20.170.28,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n6957,3,153,2017-01-27 14:42:49,http://brekkesawayn.biz/jarod,,220.231.17.3,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n6958,3,153,2017-04-03 13:05:35,http://block.biz/icie_denesik,,128.77.34.65,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n6959,3,153,2017-01-17 20:25:24,http://turcotteschumm.com/jarrell.ko,,232.217.36.48,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n6960,3,153,2016-12-24 14:05:43,http://mitchell.info/gust,,117.39.198.118,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n6961,3,153,2017-03-31 07:29:22,http://metz.biz/tyrel,,250.33.166.196,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n6962,3,153,2017-03-31 13:33:59,http://mann.name/delia_deckow,,108.139.19.191,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n6963,3,153,2017-02-18 19:52:55,http://watersmckenzie.com/selena_rath,,211.118.64.157,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n6964,3,153,2017-01-21 15:41:54,http://cummeratabauch.info/monique.harber,,149.223.177.198,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n6965,3,153,2017-04-13 02:50:37,http://smitham.com/elia,,13.30.186.101,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n6966,3,153,2017-06-06 09:19:27,http://bartoletti.biz/haan.zieme,,143.201.146.131,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n6967,3,153,2017-03-30 07:19:41,http://runolfonbailey.info/sammy.terry,,34.252.231.220,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n6968,3,153,2016-12-13 06:33:03,http://quitzon.info/kenny.kiehn,,44.88.248.239,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n6969,3,153,2016-12-23 18:32:28,http://heaney.org/elijah,,137.246.95.23,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n6970,3,153,2017-01-18 20:08:30,http://osinski.biz/tremayne_cartwright,,153.171.131.152,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n6971,3,153,2016-12-26 10:33:28,http://heathcotetrantow.biz/krystal,,110.225.147.177,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n6972,3,153,2017-04-15 19:21:20,http://wymanmcdermott.co/juliana,,242.77.227.3,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n6973,3,153,2017-03-06 07:59:36,http://eichmann.info/rodrigo_turcotte,,4.27.69.43,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n6974,3,153,2017-04-13 22:27:37,http://walshupton.co/gonzalo,,189.250.82.2,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n6975,3,153,2017-04-11 23:40:53,http://hirthehuels.biz/floy.wyman,,6.202.48.23,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n6976,3,153,2016-12-20 05:35:31,http://veum.name/glenna,,145.192.26.181,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n6977,3,153,2017-03-23 09:50:26,http://jacobson.org/ryleigh,,252.65.60.195,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n6978,3,153,2017-06-12 07:43:24,http://schmidt.com/danika.towne,,55.52.20.216,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n6979,3,153,2017-02-22 00:36:37,http://rathmills.info/ruthie.fritsch,,105.57.42.183,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n6980,3,153,2017-05-27 09:39:09,http://anderson.io/norberto,,44.251.223.79,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n6981,3,154,2017-05-10 06:00:30,http://hilpertthompson.co/kayla.cremin,,182.5.45.92,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n6982,3,154,2017-02-18 14:51:09,http://wizahowe.net/kathryn.durgan,,16.44.105.10,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n6983,3,154,2017-02-07 05:28:44,http://schoenrenner.co/felipe_thompson,,51.103.233.65,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n6984,3,154,2017-01-26 14:37:23,http://carterbahringer.co/kody.beier,,134.112.214.202,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n6985,3,154,2017-05-30 19:42:29,http://zulauf.org/felipe.murray,,11.61.181.144,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n6986,3,154,2017-02-15 00:06:52,http://predovic.net/roie,,206.243.44.99,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n6987,3,154,2017-04-22 00:54:14,http://stammkuphal.biz/ken,,120.210.36.75,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n6988,3,154,2017-01-27 00:51:21,http://bashirian.io/gonzalo_gibson,,104.171.134.99,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n6989,3,154,2017-05-04 15:56:58,http://tremblay.co/litzy,,55.70.151.144,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n6990,3,154,2017-01-18 16:51:04,http://ziemann.net/cameron,,96.14.38.239,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n6991,3,154,2017-01-29 13:01:58,http://walker.biz/lucienne,,201.162.172.211,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n6992,3,154,2017-03-15 09:38:33,http://bahringer.io/marcella,,238.132.183.2,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n6993,3,154,2017-03-23 20:54:58,http://dubuque.info/alisa,,6.159.169.196,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n6994,3,154,2017-02-13 11:45:04,http://murazik.io/kristin.metz,,81.16.62.70,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n6995,3,154,2017-05-20 04:47:08,http://langworth.com/josh,,110.253.18.210,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n6996,3,154,2017-03-27 17:18:20,http://brekke.name/tia.runolfon,,109.238.103.60,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n6997,3,154,2017-02-16 13:23:11,http://johns.biz/garnet_moriette,,36.43.123.187,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n6998,3,154,2017-06-08 09:08:59,http://mclaughlinschuster.biz/sigrid_maggio,,13.121.160.133,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n6999,3,154,2017-04-23 18:08:34,http://heidenreich.io/austin_nikolaus,,177.215.76.193,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n7000,3,154,2017-05-26 21:11:52,http://walterhamill.name/kasey.pfannerstill,,69.66.93.12,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n7001,3,154,2016-12-31 00:55:47,http://gerlach.info/karelle,,36.254.227.43,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n12860,5,287,2017-05-07 05:00:43,http://murazikbraun.co/waylon.nicolas,,244.36.73.37,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n12861,5,287,2017-01-11 04:03:05,http://trantowgibson.name/aryanna_conn,,141.36.167.152,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n12862,5,287,2017-03-06 02:38:35,http://balistreri.co/braxton.collins,,245.252.23.220,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n12863,5,287,2017-03-09 13:09:48,http://bartell.com/julie,,55.41.33.237,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n12864,5,287,2017-03-13 04:41:33,http://dietrich.net/brady_huel,,171.124.16.63,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n12865,5,287,2017-04-13 09:09:37,http://streich.info/laurine_harber,,224.194.105.247,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n12866,5,287,2017-02-04 01:38:05,http://heidenreichcasper.biz/zelda,,174.236.208.162,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n12867,5,287,2016-12-20 13:21:29,http://wuckert.name/makenzie,,253.15.83.44,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n12868,5,287,2017-01-01 18:43:59,http://cartwrightbruen.com/fredy,,194.166.105.218,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n12869,5,287,2017-02-07 13:57:18,http://moen.name/kiel.rolfson,,128.250.166.176,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n12870,5,287,2016-12-25 22:20:09,http://herman.info/roger,,182.190.149.209,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n12871,5,287,2017-04-01 17:21:09,http://trantow.org/jeromy_toy,,202.28.151.93,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n12872,5,287,2017-02-24 10:47:09,http://hamillweber.biz/maci,,13.214.27.143,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n12873,5,287,2017-06-02 03:16:17,http://sauerpacocha.org/bertram,,167.169.45.70,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n12874,5,287,2017-02-22 22:40:09,http://torphycartwright.org/foster,,131.127.182.64,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n12875,5,287,2017-04-05 23:43:06,http://ryankeeling.co/emma_hyatt,,130.151.142.208,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n12876,5,287,2017-01-16 14:37:02,http://schmeler.name/geovanny,,7.240.44.44,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n12877,5,287,2017-02-03 16:24:56,http://hansen.biz/efrain,,251.3.93.86,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n12878,5,287,2017-01-09 05:05:37,http://greenholt.net/emie,,84.8.239.117,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n12879,5,287,2017-01-05 11:54:00,http://cummings.io/brandi.heidenreich,,48.182.42.112,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n12880,5,287,2017-03-03 01:16:10,http://schimmel.info/melia,,45.29.109.181,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n12881,5,288,2017-01-04 22:45:00,http://hermann.com/modesto,0.7787327495,120.76.49.124,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n12882,5,288,2017-05-03 01:05:50,http://rogahn.org/michael.bednar,0.7351705188,125.14.37.24,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n12883,5,288,2017-03-10 02:51:35,http://wolf.io/charlotte,0.1138953315,206.76.96.74,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n12884,5,288,2016-12-17 18:29:45,http://mcclurekeebler.co/belle_wilderman,0.4644237301,33.165.198.199,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n12885,5,288,2017-03-03 11:12:20,http://williamsongaylord.io/rubye,0.5695371739,44.250.113.125,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n12886,5,288,2016-12-23 02:16:07,http://champlinhaag.org/liana,0.6578666124,119.52.214.167,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n12887,5,288,2017-01-16 07:12:40,http://lehner.net/nico_walker,0.6491775219,105.181.169.129,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n12888,5,288,2017-05-08 13:26:48,http://schimmel.io/connie,0.4650732160,17.129.97.248,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n12889,5,288,2017-03-14 17:41:14,http://murray.info/fred_mckenzie,0.7114877466,100.142.27.116,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n12890,5,288,2017-06-11 05:29:21,http://collierschuppe.co/araceli,0.8883118933,93.172.40.146,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n12891,5,288,2017-05-11 02:33:04,http://nolansporer.com/jude,0.3452559839,146.10.241.14,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n12892,5,288,2017-02-23 07:09:55,http://beer.biz/erica,0.3027656393,135.21.18.105,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n12893,5,288,2017-03-27 18:53:37,http://brownkshlerin.org/rhoda,0.3978771191,83.79.21.19,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n12894,5,288,2017-06-07 21:41:01,http://wehner.co/jeramy,0.1194254282,109.63.98.158,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n12895,5,288,2017-01-26 22:46:19,http://hettinger.org/marilyne,0.0977744560,27.154.71.32,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n12896,5,288,2017-05-24 14:16:48,http://lakin.net/krystel.ferry,0.1668884006,97.21.182.248,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n12897,5,288,2017-01-21 23:03:33,http://schimmel.io/jacklyn.davis,0.4410425647,184.11.4.193,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n12898,5,288,2017-05-29 12:37:27,http://hellertreutel.io/dena.lowe,0.7313394271,174.63.17.95,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n12899,5,288,2017-06-11 19:51:13,http://howe.com/darren,0.0398333868,105.17.77.86,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n12900,5,288,2017-04-28 15:42:58,http://spencerwolff.info/vincent.wisozk,0.8039928458,170.72.207.199,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n12901,5,288,2017-01-19 22:52:37,http://shanahan.io/aurore,0.5763521189,84.241.8.126,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n12902,5,289,2017-03-09 16:41:31,http://parisian.biz/napoleon_cummings,0.5637876083,75.89.116.243,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n12903,5,289,2017-06-11 16:02:44,http://labadiegreen.com/seth,0.1790093412,9.209.222.245,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n12904,5,289,2017-06-01 07:32:47,http://boehmhintz.co/rogelio_rohan,0.4565257791,173.231.24.48,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n12905,5,289,2017-02-10 19:06:23,http://steuberpagac.name/addison,0.1377349129,205.221.199.227,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n12906,5,289,2016-12-31 06:00:35,http://stracke.org/destany,0.2053571853,50.155.99.238,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n12907,5,289,2017-05-26 23:20:04,http://bednarmccullough.info/moie.lehner,0.6865716784,72.135.123.24,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n12908,5,289,2017-05-11 20:01:34,http://zemlak.io/theodore,0.0289214345,8.116.198.235,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n12909,5,289,2017-05-18 15:33:39,http://ullrich.biz/dax,0.4620727060,203.227.93.228,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n12910,5,289,2016-12-24 01:06:47,http://weber.org/mae.erdman,0.8089092620,177.143.249.15,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n12911,5,289,2017-04-20 11:22:44,http://conn.biz/jerome,0.8191011852,132.239.184.152,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n12912,5,289,2017-03-27 18:52:54,http://fisher.io/akeem,0.1322306280,137.111.78.254,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n15861,6,356,2017-05-24 07:59:20,http://kihnlynch.com/afton,,107.44.203.29,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n15862,6,356,2017-04-24 23:05:44,http://kuhic.net/maxwell,,253.97.215.99,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n15863,6,356,2017-03-24 19:36:31,http://gutmann.io/cecil,,159.143.98.11,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n15864,6,356,2017-01-30 20:59:41,http://shanahanlueilwitz.co/rene.macgyver,,151.100.231.121,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n15865,6,356,2017-06-01 04:00:23,http://stromanbahringer.com/heloise,,163.244.203.204,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n15866,6,356,2017-03-25 02:46:18,http://schulistfahey.org/graham,,140.165.32.102,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n15867,6,356,2017-04-20 17:53:17,http://lang.net/george,,68.240.107.119,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n15868,6,356,2017-04-27 01:51:49,http://labadie.com/idella_gerhold,,209.157.32.14,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n15869,6,356,2016-12-13 06:46:53,http://gleichnerbins.com/kirsten_gleichner,,194.172.47.42,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n15870,6,356,2017-03-23 20:12:18,http://kiehnwatsica.name/carli_breitenberg,,137.218.200.53,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n15871,6,356,2017-03-27 11:36:37,http://huels.org/ezra,,186.63.237.33,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n15872,6,356,2017-03-07 05:15:34,http://carter.info/chase.stoltenberg,,166.194.26.61,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n15873,6,356,2017-01-11 02:53:14,http://dach.io/damien,,103.237.169.183,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n15874,6,356,2017-03-16 00:57:20,http://rolfson.org/lottie,,85.22.174.92,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n15875,6,356,2016-12-18 21:31:00,http://kertzmann.name/jedidiah,,157.202.214.73,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n15876,6,356,2017-05-28 13:22:07,http://goyetteconroy.info/mertie.boyle,,210.137.247.249,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n15877,6,356,2017-01-04 16:11:18,http://blick.com/april_kovacek,,241.66.22.188,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n15878,6,357,2017-03-27 17:13:54,http://gorczany.io/carey.koelpin,,119.161.38.176,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n15879,6,357,2017-04-29 05:39:11,http://jast.net/dereck_nitzsche,,35.27.131.178,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n15880,6,357,2017-03-04 05:24:30,http://auer.name/madelynn_metz,,181.19.216.209,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n15881,6,357,2017-02-13 07:19:51,http://ernser.com/jaydon.bednar,,192.120.183.37,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n15882,6,357,2017-05-14 01:07:09,http://hansen.co/alford,,9.250.149.58,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n15883,6,357,2016-12-14 01:45:11,http://mitchell.com/pierce_pollich,,6.165.125.133,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n15884,6,357,2017-03-30 15:24:04,http://schneider.biz/darian,,28.113.123.24,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n15885,6,357,2017-01-27 11:18:47,http://howe.info/grover,,149.237.66.14,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n15886,6,357,2017-02-28 11:55:24,http://gulgowskigraham.info/mckenzie,,193.75.183.57,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n15887,6,357,2017-05-30 13:55:22,http://hettinger.name/abby,,31.48.195.198,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n15888,6,357,2017-02-07 16:51:59,http://heaney.org/josephine_brekke,,58.108.96.216,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n15889,6,357,2017-02-22 09:54:58,http://rutherfordschuster.org/amanda.johnson,,61.213.239.33,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n15890,6,357,2017-02-27 17:01:38,http://funk.co/alexane.frami,,201.212.10.254,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n15891,6,357,2016-12-30 16:06:52,http://senger.co/jo,,65.203.234.226,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n15892,6,357,2016-12-21 00:11:28,http://wisoky.io/kaleigh.champlin,,105.191.204.52,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n15893,6,357,2017-01-12 10:23:03,http://hageneawayn.biz/janice,,32.85.12.192,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n15894,6,357,2017-04-28 14:54:13,http://donnellygutkowski.io/ashlynn.davis,,205.20.117.45,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n15895,6,357,2017-05-07 07:09:44,http://harber.io/sasha_halvorson,,25.161.234.35,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n15896,6,357,2017-04-25 02:41:18,http://oreillyfritsch.com/toy,,23.61.182.47,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n15897,6,357,2017-02-15 22:50:55,http://wuckert.name/federico_huels,,195.114.174.18,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n15898,6,357,2017-02-22 20:18:54,http://brakus.info/jaiden.mcdermott,,139.151.40.116,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n15899,6,357,2017-03-01 04:01:31,http://huelstoltenberg.net/salma.shanahan,,214.34.91.115,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n15900,6,357,2017-01-07 19:47:25,http://streich.biz/ivy,,7.26.170.57,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n15901,6,357,2017-06-12 04:56:09,http://kuhic.org/hobart,,68.42.139.79,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n15902,6,357,2017-01-14 19:05:49,http://schamberger.com/eudora,,91.13.86.170,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n15903,6,357,2017-04-26 22:12:21,http://lubowitz.net/te,,184.219.79.234,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n15904,6,357,2017-02-28 05:05:15,http://zieme.biz/elody,,111.34.217.211,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n15905,6,357,2017-01-07 19:33:44,http://rodriguezwiza.co/terrence.luettgen,,179.247.144.123,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n15906,6,357,2017-03-12 04:29:02,http://purdy.com/dominic.mann,,153.48.54.234,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n15907,6,358,2017-03-28 15:20:18,http://runolfonkoelpin.biz/mark.mohr,,23.66.82.177,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n15908,6,358,2017-01-29 20:12:05,http://macejkovic.biz/timmothy_windler,,166.159.227.210,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n15909,6,358,2017-02-16 09:04:21,http://mann.co/bettye_cain,,252.53.183.52,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n15910,6,358,2017-02-26 18:10:24,http://emmerichgreenholt.info/mara,,235.85.225.137,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n15911,6,358,2017-01-11 18:56:52,http://heidenreichkilback.co/ken,,111.192.151.229,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n15912,6,358,2016-12-21 08:04:51,http://schneider.com/troy,,209.162.235.57,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n15913,6,358,2017-01-24 06:43:23,http://lockmanbecker.com/martine,,213.232.26.245,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n15914,6,358,2017-01-04 10:55:15,http://sawaynbatz.co/desmond.keeling,,42.36.58.199,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n15915,6,358,2017-04-13 19:21:39,http://harberstoltenberg.co/merlin,,199.32.194.69,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n3060,2,67,2017-06-04 07:47:35,http://daniel.com/andreanne,0.5081365347,104.223.204.123,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n3061,2,67,2017-05-27 17:18:34,http://mertz.biz/patrick_leffler,0.6428793500,121.240.69.73,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n3062,2,67,2017-02-17 23:52:39,http://steuber.com/emelia.wintheiser,0.7345841760,92.254.7.121,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n3063,2,67,2016-12-14 22:16:58,http://walter.io/modesto.simonis,0.8903366154,71.15.55.197,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n3064,2,67,2016-12-17 11:54:24,http://labadie.org/grover.waters,0.4764158963,86.59.142.135,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n3065,2,67,2017-01-24 04:07:58,http://ferry.com/blaze,0.7393647915,105.241.26.140,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n3066,2,67,2017-02-18 21:22:03,http://wolff.org/junior,0.1373549526,71.77.175.140,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n3067,2,67,2017-02-17 05:42:22,http://rohanking.info/desmond.stroman,0.1648052082,242.240.82.43,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n3068,2,67,2017-03-19 01:44:47,http://weinat.com/lue_nader,0.9962787196,179.89.185.38,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n3069,2,67,2017-02-06 12:54:10,http://cormier.net/jordane,0.5503633857,116.65.94.19,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n3070,2,67,2017-06-10 13:05:05,http://wilkinsonsimonis.biz/marcellus.schiller,0.1873058066,144.148.223.18,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n3071,2,67,2017-04-13 03:27:52,http://bergnaumwill.info/laverna,0.6004717392,109.5.131.233,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n3072,2,67,2017-02-15 03:20:19,http://wintheiser.io/letitia.bednar,0.3552216278,106.76.177.183,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n3073,2,67,2017-02-10 21:07:43,http://runolfsdottir.info/lavinia,0.1145646945,222.220.31.158,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n3074,2,67,2017-05-11 05:43:21,http://kuhnwindler.org/rebeca_pacocha,0.2933389308,125.29.242.243,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n3075,2,67,2017-03-31 16:14:08,http://goodwin.net/america_lemke,0.4858740781,88.2.149.68,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n3076,2,67,2017-05-11 02:33:47,http://herzog.name/maude,0.0106117873,117.116.205.202,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n3077,2,67,2017-03-20 12:44:47,http://stehr.co/gardner,0.6639282114,125.130.47.156,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n3078,2,67,2017-01-31 22:26:43,http://pouros.co/alysha,0.0699426450,40.210.144.196,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n3079,2,67,2017-04-17 02:51:31,http://wilkinson.io/brenna,0.4833066038,135.152.82.60,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n3080,2,67,2017-05-28 09:50:00,http://herman.name/athena_keebler,0.1843461595,119.177.168.105,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n3081,2,67,2017-02-06 23:46:11,http://shanahan.net/lina_hauck,0.7073889898,39.113.105.115,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n3082,2,67,2017-04-01 14:31:37,http://hand.org/leola.friesen,0.6395754157,84.181.10.190,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n3083,2,67,2017-05-08 18:25:58,http://mosciski.org/ava_carter,0.5432194911,86.42.28.124,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n3084,2,67,2016-12-27 18:21:35,http://rodriguez.org/danika,0.0195413358,65.215.10.227,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n3085,2,67,2017-04-27 20:33:11,http://trompcorkery.biz/catharine_hammes,0.7785582599,99.201.77.119,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n3086,2,67,2017-04-03 20:41:54,http://klocko.org/laurie.hauck,0.4295465037,219.61.171.236,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n3087,2,67,2017-04-10 15:45:10,http://prosacco.io/jakob,0.9702963902,174.206.54.249,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n3088,2,67,2017-05-23 13:52:02,http://altenwerth.io/hayley,0.5522858634,120.27.168.75,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n3089,2,67,2017-05-16 09:17:54,http://lind.biz/carroll.hettinger,0.1695288634,131.4.38.150,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n3090,2,67,2017-01-08 02:13:33,http://lemke.name/einar.botsford,0.7323684522,22.82.38.73,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n3091,2,68,2017-02-08 04:02:21,http://okon.net/john,0.0228505400,30.135.190.47,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n3092,2,68,2017-05-19 02:53:14,http://wisozk.io/blair_pfannerstill,0.1881665641,229.218.106.143,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n3093,2,68,2017-04-05 03:53:39,http://wuckert.net/kathryn,0.8955266268,238.250.188.195,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n3094,2,68,2017-05-09 17:00:40,http://heaney.com/rachael,0.8634943041,240.32.102.200,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n3095,2,68,2017-05-17 11:28:08,http://heller.info/vince,0.8918104107,221.11.132.24,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n3096,2,68,2017-04-16 04:13:18,http://corwineichmann.org/shayna_mueller,0.0393817586,93.121.136.9,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n3097,2,68,2017-02-10 18:17:20,http://padberg.io/kyleigh,0.0130914409,128.249.116.15,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n3098,2,68,2017-01-31 16:23:52,http://parker.name/magdalena_price,0.8521141740,165.206.27.33,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n3099,2,68,2017-02-17 05:34:23,http://daniel.com/rebeka,0.9185710806,108.6.26.27,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n3100,2,68,2016-12-17 16:48:25,http://okeefe.biz/justen.kuphal,0.0015395372,67.158.191.197,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n3101,2,68,2017-05-29 14:58:25,http://bechtelar.io/helene,0.2010510408,59.67.157.18,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n3102,2,68,2016-12-29 13:46:09,http://funk.org/ervin,0.1494269200,165.92.59.199,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n3103,2,68,2017-05-15 01:06:45,http://kohler.biz/alayna,0.1601838411,202.56.15.120,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n3104,2,68,2017-04-18 03:56:28,http://west.io/francisca.boehm,0.8928508989,132.8.246.232,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n3105,2,68,2017-02-03 04:05:15,http://jones.info/trystan,0.7573981124,247.99.21.246,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n3106,2,68,2017-05-12 19:12:40,http://homenick.biz/jonathon_stark,0.2784887883,147.213.122.245,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n3107,2,68,2016-12-15 18:31:18,http://mitchell.biz/peyton,0.8696738815,230.224.124.99,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n3108,2,68,2017-05-06 23:29:35,http://dickinson.org/ivory,0.7421303781,176.135.115.90,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n3109,2,68,2017-04-16 17:15:08,http://breitenberg.biz/jerod,0.2523042135,49.241.168.221,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n3110,2,68,2017-05-28 20:49:30,http://oconnell.net/charles.bernhard,0.9487590323,92.102.143.131,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n3111,2,68,2017-03-01 05:43:41,http://weinatcollins.net/lexus.cole,0.8671190870,94.164.38.213,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n9996,4,223,2017-05-24 00:54:35,http://feil.net/zoey,0.0671630238,29.111.198.129,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n9997,4,223,2017-05-18 05:01:25,http://botsford.biz/helga.funk,0.4426849024,216.58.213.142,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n9998,4,223,2017-05-15 09:14:12,http://nitzsche.org/mitchel.lindgren,0.3630798485,111.109.233.70,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n9999,4,223,2017-01-31 14:03:52,http://schroeder.org/alexandria,0.6756549808,140.31.192.86,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n10000,4,223,2017-02-19 11:51:24,http://ledner.com/trevion,0.6515260766,211.94.7.49,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n10001,4,223,2017-01-24 10:09:06,http://leffler.io/melyna,0.5911099904,198.24.142.72,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n10002,4,223,2016-12-26 08:33:13,http://moriette.name/briana_moriette,0.2514496626,125.92.115.87,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n10003,4,223,2017-03-21 14:34:44,http://bartoletti.com/devon,0.7213022701,41.171.106.92,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n10004,4,223,2017-05-19 03:01:42,http://thompson.net/noah.sporer,0.1148729238,226.219.139.117,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n10005,4,223,2017-06-14 02:02:28,http://okonstrosin.org/petra.kirlin,0.2770214283,36.130.43.106,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n10006,4,223,2017-02-24 09:32:16,http://abshire.org/hermina.williamson,0.7333105519,16.244.77.96,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n10007,4,223,2017-02-02 16:45:52,http://cummingswaters.info/alvina,0.1406641602,227.134.146.8,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n10008,4,224,2017-04-03 23:26:57,http://armstrong.biz/erwin,0.8019035669,65.168.211.225,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n10009,4,224,2017-05-20 14:57:42,http://ullrich.org/alexandrine_cronin,0.1422289452,15.29.136.104,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n10010,4,224,2017-06-01 04:24:00,http://boyle.info/alexandre,0.0295243911,130.124.173.170,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n10011,4,224,2016-12-14 02:44:50,http://bayerpouros.biz/garett,0.3821132978,63.7.43.223,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n10012,4,224,2017-02-28 22:45:43,http://muller.com/julianne_mosciski,0.4137981111,148.109.110.112,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n10013,4,224,2017-04-15 10:06:01,http://cormier.biz/morgan,0.3683053537,77.22.107.118,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n10014,4,224,2017-04-14 09:08:57,http://gerholdgraham.name/guie.kozey,0.8481704464,168.224.90.232,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n10015,4,224,2017-01-09 13:42:49,http://okuneva.name/delaney_crist,0.0240244726,101.165.72.81,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n10016,4,224,2017-05-10 12:00:16,http://eberthilll.co/mark_roob,0.7175282874,160.246.220.164,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n10017,4,224,2017-05-01 14:49:38,http://conroymoriette.net/destiney,0.5453984019,49.99.14.141,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n10018,4,224,2016-12-26 20:00:57,http://lefflerkihn.io/cora_pollich,0.9887016783,62.79.53.147,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n10019,4,224,2017-04-14 20:26:30,http://schmeler.org/antonina,0.3667238933,95.185.2.152,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n10020,4,224,2017-05-13 08:41:03,http://sipes.org/ruby.mertz,0.1748238044,147.26.16.186,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n10021,4,224,2017-04-02 00:28:59,http://schambergersanford.net/avis_oconner,0.5084078587,218.5.243.169,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n10022,4,224,2017-03-24 08:28:29,http://langoshgutkowski.com/paula.welch,0.3080244138,185.73.8.217,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n10023,4,224,2017-03-08 13:41:42,http://hettingertorp.biz/duane,0.6301798479,104.188.159.90,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n10024,4,224,2017-03-05 05:48:55,http://ziemannhahn.io/annetta.vonrueden,0.0424738026,174.220.128.60,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n10025,4,224,2017-01-02 13:17:13,http://veumschiller.co/eric_sauer,0.1563862592,251.147.253.47,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n10026,4,224,2016-12-14 10:35:30,http://lang.co/lonzo,0.7270488260,64.12.38.28,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n10027,4,224,2017-01-30 12:56:05,http://jakubowskiwaelchi.com/jose,0.9428672359,166.131.46.80,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n10028,4,224,2017-05-03 00:12:16,http://keler.name/evert,0.5552977545,249.162.216.13,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n10029,4,224,2017-04-04 00:23:12,http://kemmerlangosh.com/sally,0.4814987407,169.254.190.222,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n10030,4,224,2017-04-07 20:03:26,http://fahey.net/richard_conroy,0.2796135147,27.231.241.88,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n10031,4,224,2017-06-05 09:30:17,http://marksgleichner.name/torrey_langworth,0.4976732834,191.105.162.159,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n10032,4,224,2017-02-24 11:47:48,http://kohler.org/carmella_yundt,0.9604905292,5.37.29.202,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n10033,4,224,2016-12-24 12:10:15,http://emmerich.org/carmine.reichert,0.5902192977,141.92.249.219,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n10034,4,224,2017-01-08 04:09:33,http://olson.name/royal,0.7248193964,224.183.27.18,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n10035,4,224,2017-05-06 15:39:10,http://harvey.co/eugenia,0.8089411981,123.208.61.68,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n10036,4,224,2017-01-29 12:20:25,http://stehr.com/juliana.turcotte,0.5435663782,164.213.232.135,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n10037,4,224,2017-01-21 17:04:07,http://kuvalis.io/samanta_schuppe,0.4193934774,54.48.69.180,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n10038,4,224,2017-01-03 09:03:49,http://ankundingrohan.co/maye_reinger,0.2258893031,29.34.43.114,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n10039,4,225,2017-05-06 03:45:19,http://jones.net/osborne,0.7582668016,243.133.33.144,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n10040,4,225,2017-05-13 15:52:59,http://bergebergnaum.net/ethyl,0.9929988940,55.110.223.125,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n10041,4,225,2017-02-22 16:56:30,http://gutkowskibailey.org/carolanne,0.7890077182,66.85.250.254,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n10042,4,225,2017-04-22 21:39:25,http://hintz.io/neal.reichert,0.8373338188,32.223.22.102,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n10043,4,225,2017-01-24 13:09:58,http://lindbotsford.com/mazie_will,0.4117967747,4.250.87.248,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n10044,4,225,2017-01-10 02:49:34,http://kertzmann.biz/loyal.koelpin,0.8807088268,172.84.6.80,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n10045,4,225,2017-03-05 04:09:18,http://parkerschmitt.io/sigrid.kulas,0.5817756627,27.189.74.50,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n10046,4,225,2017-04-07 08:36:57,http://mayert.org/melisa_ruel,0.1234101914,25.226.250.94,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n7002,3,154,2017-02-14 12:01:42,http://fahey.info/zachariah.swaniawski,,211.170.128.100,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n7003,3,154,2017-03-10 08:47:40,http://raynor.co/hal,,22.85.79.65,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n7004,3,154,2017-04-21 15:22:38,http://stehr.info/elbert,,97.128.190.41,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n7005,3,154,2016-12-19 14:15:47,http://ricekulas.co/daniela,,45.31.134.130,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n7006,3,154,2017-04-29 02:59:02,http://stracke.info/albert,,2.41.53.66,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n7007,3,154,2017-05-23 11:59:53,http://rutherford.co/camden,,217.211.109.38,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n7008,3,154,2016-12-26 07:05:42,http://orn.net/keara_dickens,,211.206.237.149,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n7009,3,154,2017-03-31 02:27:32,http://schaeferlakin.name/mervin_brekke,,134.161.159.135,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n7010,3,155,2017-06-03 21:42:47,http://cole.co/jaren_dare,,94.136.25.229,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n7011,3,155,2017-05-09 22:55:31,http://orn.name/phyllis,,187.142.243.111,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n7012,3,155,2017-01-18 18:23:53,http://lueilwitz.co/kathryn,,122.215.189.45,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n7013,3,155,2017-05-02 16:57:03,http://gloverhuels.com/jacklyn,,109.81.80.173,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n7014,3,155,2017-02-13 16:09:35,http://wolf.net/caleb.deckow,,10.14.152.236,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n7015,3,155,2017-06-06 15:00:49,http://ondricka.com/judson,,143.220.194.91,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n7016,3,155,2017-02-08 22:25:40,http://roberts.biz/shawn_baumbach,,227.55.162.132,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n7017,3,155,2017-02-13 13:12:33,http://wilkinsonkuphal.org/alycia_emard,,23.84.126.158,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n7018,3,155,2017-03-07 02:12:01,http://jerde.info/pattie.spinka,,232.7.60.76,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n7019,3,155,2017-05-06 12:09:13,http://franecki.com/clare,,61.136.149.40,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n7020,3,155,2017-01-06 22:26:16,http://weber.co/baylee,,44.17.111.129,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n7021,3,155,2017-03-24 10:51:43,http://metz.com/unique_kirlin,,86.110.236.217,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n7022,3,155,2017-01-04 23:09:12,http://kovacek.info/yazmin,,223.102.4.37,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n7023,3,155,2017-01-12 07:03:06,http://grady.net/marques,,19.130.46.202,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n7024,3,155,2017-04-26 07:29:32,http://loweokeefe.name/jeanne.langworth,,11.205.230.155,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n7025,3,155,2017-02-27 21:14:12,http://rutherfordhammes.net/noel_sipes,,23.235.70.35,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n7026,3,155,2017-04-18 17:37:43,http://andersonsmitham.co/frederic,,216.192.31.125,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n7027,3,155,2017-03-27 22:02:07,http://leannondoyle.name/vladimir.pfannerstill,,2.57.161.73,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n7028,3,155,2017-04-05 06:59:51,http://rutherford.com/kaia.herzog,,139.28.174.246,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n7029,3,155,2017-02-01 01:16:49,http://sanford.com/lester,,45.216.242.107,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n7030,3,155,2017-03-08 07:13:01,http://kozeykuhn.name/elza.willms,,196.74.108.219,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n7031,3,155,2016-12-21 08:10:53,http://hane.name/evans,,191.218.81.226,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n7032,3,155,2017-03-01 21:01:43,http://breitenberg.co/kadin,,117.132.181.154,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n7033,3,155,2017-04-12 12:57:56,http://schaefer.net/eladio,,109.134.13.18,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n7034,3,155,2017-05-30 23:57:46,http://farrell.name/vincenzo,,229.241.150.244,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n7035,3,155,2017-04-12 21:27:17,http://becker.biz/carlos.grimes,,8.150.199.87,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n7036,3,156,2017-04-05 11:40:54,http://skiletoltenberg.io/green.quigley,0.0218777881,78.193.43.33,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n7037,3,156,2017-04-26 07:45:22,http://bernier.biz/maribel,0.0711979253,70.171.31.171,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n7038,3,156,2017-04-29 15:29:54,http://murrayfisher.biz/trevor,0.0349001465,73.140.102.5,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n7039,3,156,2017-04-06 21:46:31,http://doyle.io/lela,0.7815179423,246.51.236.43,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n7040,3,156,2017-01-13 00:27:10,http://goodwinwiegand.info/tate_boyer,0.1959538141,241.253.46.200,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n7041,3,156,2017-01-07 18:54:28,http://beahan.org/daisha.weimann,0.0547582120,155.53.24.179,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n7042,3,156,2017-03-05 19:22:16,http://ricegibson.org/pamela_rath,0.1491594551,95.175.73.174,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n7043,3,156,2017-04-25 20:48:33,http://anderson.net/abelardo,0.2684333554,231.86.193.190,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n7044,3,156,2017-01-28 03:52:07,http://grantgrady.info/erica,0.4401895081,9.253.16.14,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n7045,3,156,2017-01-08 00:34:31,http://mitchellhills.info/seamus_reichert,0.1986207852,180.180.208.228,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n7046,3,156,2017-05-08 23:52:23,http://nicolasbrekke.com/brionna.vonrueden,0.2702300079,178.197.31.87,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n7047,3,156,2017-03-19 00:20:22,http://waelchimccullough.name/rosalee_hagenes,0.7767875117,249.62.162.122,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n7048,3,156,2017-03-24 08:00:42,http://ko.com/monserrat,0.7187589391,117.165.248.45,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n7049,3,156,2017-04-02 12:20:10,http://leffler.net/gracie,0.6450532900,84.200.94.200,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n7050,3,156,2017-03-01 07:22:16,http://bradtke.com/maggie,0.8463542005,204.12.70.135,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n7051,3,156,2017-01-20 09:41:54,http://heel.io/garrett_goldner,0.6585828317,149.93.36.108,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n7052,3,156,2017-05-11 13:51:35,http://frami.net/elta.stehr,0.7272995671,152.71.16.54,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n7053,3,156,2016-12-31 14:03:50,http://kilback.info/hillary_kunde,0.2482418354,55.72.83.242,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n7054,3,156,2017-06-05 04:06:10,http://collier.io/sonya,0.1024340705,201.230.70.102,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n7055,3,156,2017-04-20 02:45:36,http://goldner.net/vilma.bartoletti,0.7415573045,31.176.226.89,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n12913,5,289,2017-05-12 18:19:06,http://marquardtkling.io/garnet.marks,0.2114571526,64.128.154.207,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n12914,5,289,2017-03-09 06:17:24,http://welchhauck.name/hudson_oconnell,0.3579188433,107.140.161.117,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n12915,5,289,2017-04-05 14:51:42,http://bednar.info/chasity,0.7221942403,210.144.212.154,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n12916,5,289,2017-03-07 04:12:56,http://reichel.io/jamison.kling,0.3657567777,68.208.60.85,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n12917,5,289,2017-04-28 14:04:06,http://becker.com/donald,0.0535041910,246.119.99.113,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n12918,5,289,2017-06-07 18:37:33,http://greenholt.co/iva,0.5310092216,21.39.253.57,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n12919,5,289,2017-03-05 21:59:38,http://runolfsdottir.biz/cletus.hayes,0.4710642801,163.26.204.138,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n12920,5,289,2017-04-15 18:07:23,http://cronin.co/maybell,0.3359936474,180.212.94.163,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n12921,5,289,2017-05-27 15:50:18,http://fahey.com/lacey,0.9771584125,28.145.201.223,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n12922,5,289,2017-03-13 04:21:22,http://torp.info/isadore_herman,0.3819657892,15.201.30.205,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n12923,5,289,2017-03-18 12:12:23,http://rath.net/ruby.koepp,0.8512739068,34.145.244.196,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n12924,5,289,2017-02-21 16:44:32,http://champlin.name/myles_lang,0.0453148654,51.63.233.62,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n12925,5,289,2017-03-23 13:59:19,http://powlowski.org/jaylin_padberg,0.6919893587,177.183.171.117,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n12926,5,289,2017-06-09 01:03:12,http://kreigerbergnaum.biz/norberto,0.0742084578,12.148.102.227,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n12927,5,289,2017-02-27 17:04:11,http://hane.org/rae,0.6256220353,139.147.142.66,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n12928,5,289,2016-12-23 20:04:30,http://hintzhilll.org/cathrine,0.8752426985,138.229.120.99,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n12929,5,289,2017-04-29 12:07:45,http://lind.name/yasmin,0.9627893802,252.28.136.77,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n12930,5,289,2017-02-26 05:38:57,http://larson.net/markus_casper,0.6463302032,145.244.89.45,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n12931,5,289,2017-06-08 19:42:28,http://flatley.name/abe,0.2061320792,216.190.73.156,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n12932,5,289,2017-03-23 23:30:38,http://bogisich.biz/hayden,0.4216103311,44.206.232.19,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n12933,5,289,2017-05-17 22:55:54,http://dooley.info/monty,0.9298374874,227.123.154.22,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n12934,5,289,2017-02-16 01:44:35,http://reynolds.net/brant.stracke,0.2862261264,107.236.24.58,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n12935,5,289,2017-05-11 00:48:34,http://lowerosenbaum.biz/tania.lubowitz,0.1804552158,58.73.244.7,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n12936,5,289,2017-04-29 04:37:15,http://labadie.com/palma.metz,0.9864401177,167.88.98.68,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n12937,5,289,2017-01-13 02:19:58,http://spinkarobel.com/audrey,0.2958251714,69.18.3.21,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n12938,5,289,2017-02-24 12:14:52,http://hackett.info/ollie,0.9631013858,173.91.151.16,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n12939,5,289,2017-03-21 17:10:46,http://blanda.io/harmony_lesch,0.3545326717,54.52.51.136,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n12940,5,289,2017-04-27 07:08:17,http://kemmertillman.io/coty,0.2285528420,210.133.203.245,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n12941,5,289,2017-04-23 07:24:05,http://beahan.biz/dell,0.2815222932,217.50.92.125,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n12942,5,289,2017-03-13 17:32:55,http://schmittblick.org/norval_veum,0.6730696121,20.166.222.67,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n12943,5,289,2017-03-31 09:15:19,http://gaylord.net/jey_schaefer,0.0993606289,210.160.19.106,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n12944,5,289,2017-01-03 06:01:53,http://feeney.biz/ford_hand,0.2517836048,95.79.195.236,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n12945,5,289,2016-12-25 04:17:28,http://gutmann.biz/zoila,0.5404774759,39.162.126.181,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n12946,5,289,2017-04-27 09:15:20,http://kunze.name/jarrod_rosenbaum,0.7540004096,124.49.241.220,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n12947,5,289,2017-06-12 03:23:53,http://luettgen.com/lori_fahey,0.6781993300,96.123.158.170,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n12948,5,289,2016-12-20 13:16:04,http://kuhic.com/raquel,0.9492554153,160.127.174.7,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n12949,5,289,2017-03-30 12:44:21,http://lynchpollich.co/jensen,0.7626915552,42.226.115.229,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n12950,5,289,2017-04-19 07:58:36,http://wiza.com/lamont,0.2380596080,28.70.136.96,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n12951,5,290,2017-02-09 09:53:06,http://moriette.net/shane,0.5720359253,46.207.70.183,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n12952,5,290,2017-02-04 11:15:57,http://zemlak.name/obie,0.7425776544,167.209.138.234,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n12953,5,290,2017-06-01 14:35:32,http://price.io/domenico_monahan,0.6072363176,224.90.243.143,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n12954,5,290,2017-06-09 23:45:03,http://marvin.co/abigail,0.5257599185,156.217.240.44,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n12955,5,290,2017-01-10 00:26:11,http://wardwolff.net/chelsie_dickinson,0.4880739788,115.96.57.162,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n12956,5,290,2016-12-31 05:43:36,http://crookshuel.info/mollie_hammes,0.1974298713,109.186.10.23,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n12957,5,290,2017-04-07 19:06:29,http://west.io/mallory_mitchell,0.1910741705,11.223.58.33,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n12958,5,290,2017-03-22 07:32:20,http://carrolltillman.co/luisa,0.1249702950,244.244.32.253,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n12959,5,290,2017-02-28 04:16:13,http://reichel.biz/jacky,0.3684256531,217.72.83.73,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n12960,5,290,2017-06-13 06:45:31,http://bernhardklocko.org/delta_nolan,0.4919868023,172.15.243.206,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n12961,5,290,2017-02-22 05:07:59,http://nolanfisher.net/stefan_bartoletti,0.0055156304,242.149.248.251,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n12962,5,290,2017-04-20 16:28:14,http://leuschkecummings.net/margarette.reynolds,0.8307134822,173.197.73.230,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n12963,5,290,2017-05-15 04:33:27,http://mayert.org/elian,0.3434131551,244.96.214.5,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n15916,6,358,2017-04-06 19:34:07,http://witting.io/davin.wehner,,22.75.102.111,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n15917,6,358,2017-04-21 07:17:19,http://krajcik.biz/ada.leffler,,108.234.35.24,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n15918,6,358,2017-02-07 15:04:36,http://jaskolski.org/abraham,,122.103.94.178,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n15919,6,358,2017-01-05 18:01:50,http://stokes.biz/brian_beahan,,43.117.27.186,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n15920,6,358,2017-05-08 09:49:12,http://kreiger.info/katherine,,252.218.42.147,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n15921,6,358,2017-02-27 14:32:52,http://gorczany.info/cheyenne_schmidt,,203.166.131.110,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n15922,6,358,2017-05-12 17:26:59,http://simonis.io/esperanza,,182.228.182.53,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n15923,6,358,2016-12-15 03:24:19,http://feil.co/nya,,141.245.87.54,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n15924,6,358,2017-03-27 13:47:16,http://feeney.net/lexi_boyle,,108.207.85.139,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n15925,6,358,2017-02-21 22:54:59,http://beatty.info/vena.wehner,,193.99.65.214,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n15926,6,358,2017-01-01 20:49:42,http://waelchi.org/hillard,,176.20.240.216,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n15927,6,358,2017-03-25 09:37:18,http://ziemann.info/kathryn,,195.203.155.147,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n15928,6,358,2017-05-11 14:27:21,http://price.org/laron.brakus,,126.24.60.176,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n15929,6,358,2017-04-02 22:02:35,http://nicolas.co/melyna.zemlak,,230.131.61.44,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n15930,6,358,2017-01-22 10:51:49,http://mayerhintz.info/rylee,,59.94.207.175,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n15931,6,359,2017-02-02 12:52:05,http://dickens.net/cicero_paucek,,161.89.122.59,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n15932,6,359,2017-05-31 13:30:51,http://hansen.net/christian,,114.187.179.174,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n15933,6,359,2017-03-21 15:20:53,http://osinski.org/filomena,,233.70.95.120,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n15934,6,359,2017-03-13 22:46:15,http://kshlerin.name/alvah.sawayn,,52.253.213.211,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n15935,6,359,2017-01-31 07:35:52,http://larkin.co/giles_damore,,244.117.113.110,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n15936,6,359,2017-04-23 04:51:54,http://hettinger.name/beverly.kuhic,,125.245.159.127,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n15937,6,359,2017-05-08 07:56:08,http://bechtelar.info/lelia.stracke,,44.234.183.96,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n15938,6,359,2017-05-19 12:26:38,http://yundt.co/kaci_bosco,,97.190.65.99,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n15939,6,359,2016-12-15 12:56:49,http://lindbins.io/elwyn,,175.140.157.129,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n15940,6,359,2016-12-27 23:53:43,http://blockkilback.io/obie,,109.51.186.148,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n15941,6,359,2017-03-07 19:37:37,http://maggiojerde.info/rebeka,,195.242.236.6,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n15942,6,359,2017-01-27 08:17:39,http://dare.com/chris,,129.176.17.70,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n15943,6,359,2017-06-01 02:06:24,http://lowe.org/courtney,,230.200.168.23,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n15944,6,359,2016-12-20 13:06:46,http://hilll.co/vada,,236.80.224.225,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n15945,6,359,2016-12-31 01:18:09,http://hyattmayer.biz/brandt_jacobs,,241.153.73.249,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n15946,6,359,2017-04-07 04:40:42,http://mannschaefer.info/laury,,122.40.100.193,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n15947,6,359,2017-05-10 14:25:42,http://mueller.name/kali_nienow,,213.169.214.134,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n15948,6,359,2017-04-14 05:55:45,http://cormier.com/jarod_konopelski,,69.123.116.123,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n15949,6,359,2017-06-10 07:28:51,http://hahn.net/milo,,206.153.176.254,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n15950,6,359,2017-06-03 01:44:56,http://cronafisher.io/lempi_jacobi,,2.222.158.104,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n15951,6,359,2017-04-06 19:13:06,http://kunde.com/tate,,230.77.99.186,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n15952,6,359,2017-05-08 13:55:42,http://senger.name/brooke_sauer,,194.201.180.56,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n15953,6,359,2017-06-09 08:29:30,http://swift.io/carmen.stokes,,164.174.146.226,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n15954,6,359,2017-05-28 22:35:28,http://swaniawski.io/imani_paucek,,133.117.50.250,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n15955,6,359,2017-02-17 13:00:33,http://dietrich.info/estelle,,53.227.232.95,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n15956,6,359,2017-02-07 03:22:54,http://walsh.name/meredith,,201.101.180.194,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n15957,6,359,2017-04-29 03:33:08,http://greenfelder.biz/aida.zulauf,,83.152.129.93,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n17071,6,386,2017-01-09 03:31:15,http://spinka.org/cecile,,160.230.207.86,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n15958,6,359,2017-01-06 02:14:53,http://schimmelcrona.name/monserrat,,164.71.57.119,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n15959,6,359,2017-01-09 20:31:20,http://hackett.biz/carmelo.jast,,254.166.241.238,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n15960,6,359,2017-01-17 09:04:17,http://stamm.co/demond.schinner,,128.68.101.25,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n15961,6,359,2017-02-22 22:05:13,http://mohrnikolaus.net/stewart,,212.57.235.159,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n15962,6,359,2017-02-06 12:46:35,http://lemke.biz/beryl,,217.164.133.8,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n15963,6,359,2017-03-03 14:30:23,http://tillman.co/loyce,,81.170.33.137,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n15964,6,359,2017-01-07 14:04:56,http://cremin.com/tiffany,,67.2.188.11,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n15965,6,359,2017-03-16 17:58:40,http://kutch.biz/carole.yost,,229.57.133.124,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n15966,6,359,2017-02-07 21:22:17,http://hegmann.com/tamara,,56.184.178.76,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n15967,6,359,2017-04-17 02:02:23,http://breitenberg.org/tiara,,93.141.29.110,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n15968,6,359,2017-01-23 02:24:13,http://wyman.info/clara,,77.13.203.20,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n15969,6,359,2017-02-28 12:11:10,http://stiedemann.io/floyd,,171.17.187.179,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n15970,6,359,2017-01-27 18:00:32,http://stiedemann.net/jake,,139.163.27.73,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n3112,2,68,2017-04-12 17:13:19,http://denesik.info/kristopher.bogan,0.5974878075,203.21.50.243,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n3113,2,68,2017-04-23 10:02:43,http://breitenbergweinat.name/domenick,0.1911644382,53.237.245.250,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n3114,2,69,2017-03-25 15:13:24,http://hanerolfson.info/kaleb,0.9996665033,157.212.243.59,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n3115,2,69,2016-12-31 06:57:29,http://walkeroconner.net/aleandra,0.6722299459,184.12.169.26,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n3116,2,69,2017-03-04 09:46:26,http://reinger.co/johathan.yost,0.7854829853,148.151.165.11,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n3117,2,69,2017-02-03 16:50:50,http://hansen.io/carlie_hamill,0.0863582474,67.177.89.42,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n3118,2,69,2017-02-25 23:47:55,http://fay.info/jaylon_senger,0.5206267978,67.32.159.143,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n3119,2,69,2016-12-26 08:38:43,http://gleichnerlangworth.biz/tamara,0.3397541567,230.109.143.229,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n3120,2,69,2016-12-19 07:28:38,http://mantestiedemann.com/deja_kshlerin,0.4346794280,109.44.215.139,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n3121,2,69,2017-06-01 03:55:19,http://bradtke.biz/garry,0.5734775743,47.175.65.247,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n3122,2,69,2017-05-18 02:11:03,http://johnson.net/daren,0.4229125511,43.225.250.117,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n3123,2,69,2017-06-01 09:19:24,http://lakin.net/jasper,0.9772882885,220.197.155.202,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n3124,2,69,2017-03-24 19:10:38,http://hansenbrekke.org/boris,0.5008544086,2.157.174.200,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n3125,2,69,2017-02-14 13:08:00,http://macejkovic.io/johan_herman,0.2307843963,165.142.45.3,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n3126,2,69,2017-06-04 08:27:48,http://champlin.name/elroy_ernser,0.9662507808,22.163.39.128,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n3127,2,69,2017-02-17 05:02:39,http://spinkagrimes.biz/dillan,0.6342897693,186.204.52.137,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n3128,2,69,2017-02-25 02:25:36,http://mrazkoelpin.net/norene,0.2252231497,125.6.30.134,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n3129,2,69,2017-03-10 03:58:07,http://wisozk.info/hermina,0.5488085094,147.139.208.176,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n3130,2,69,2017-03-27 14:34:00,http://stroman.co/luciano,0.8465012278,132.187.19.90,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n3131,2,69,2017-05-27 21:36:05,http://huels.co/weston.swaniawski,0.9839823085,33.72.161.166,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n3132,2,69,2017-01-09 14:30:55,http://skilesbahringer.name/scarlett_lueilwitz,0.4501433862,224.189.14.6,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n3133,2,69,2016-12-20 15:39:33,http://klein.io/paris,0.8083949107,155.252.250.109,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n3134,2,69,2017-02-09 04:40:27,http://beier.com/nils_walsh,0.8604351129,34.93.24.185,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n3135,2,69,2017-02-15 11:01:15,http://kulasfranecki.info/lavon,0.3847863653,57.134.131.21,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n3136,2,69,2017-06-05 18:05:55,http://kihngerhold.com/margarete_strosin,0.7769132799,169.224.54.146,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n3137,2,69,2016-12-17 03:52:07,http://murazik.com/garnet,0.5502466655,52.174.40.82,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n3138,2,69,2016-12-30 14:35:54,http://deckow.org/kenny_gislason,0.7484658726,251.243.174.45,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n3139,2,69,2017-04-28 22:48:00,http://gerlach.biz/briana_cole,0.0539582506,11.81.87.54,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n3140,2,69,2017-02-04 05:53:35,http://purdy.net/paul,0.9650374810,166.70.119.217,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n3141,2,69,2017-03-10 18:51:51,http://mayermcdermott.co/kathlyn_bernier,0.8205558098,24.87.22.103,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n3142,2,69,2017-05-05 16:22:54,http://hermiston.co/myrtie,0.9125203508,183.120.139.243,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n3143,2,69,2017-02-08 13:53:45,http://hyatt.org/reece.pfeffer,0.3757934771,201.229.159.89,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n3144,2,69,2017-04-28 05:48:18,http://carter.com/eliza,0.6419280306,109.144.72.23,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n3145,2,69,2016-12-25 02:38:11,http://waelchi.io/robbie_aufderhar,0.8109047527,35.147.23.97,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n3146,2,69,2016-12-23 22:59:32,http://framiabshire.net/alfonzo,0.9589649718,170.164.231.237,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n3147,2,69,2017-05-21 03:25:40,http://harvey.name/cornell,0.0195763306,15.155.75.103,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n3148,2,69,2017-01-24 15:26:30,http://klocko.io/stephan_wolff,0.8668169186,108.15.249.17,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n3149,2,69,2016-12-22 19:04:25,http://hayes.io/gilbert.roberts,0.8626886040,235.68.212.180,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n3150,2,69,2017-01-16 06:48:25,http://damore.name/madie.legros,0.8404784869,243.191.251.89,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n3151,2,69,2017-05-18 21:40:57,http://hamill.info/george,0.3961158672,2.121.81.64,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n3152,2,69,2017-04-17 15:39:15,http://runteschuppe.biz/gretchen.roberts,0.5894416392,29.178.248.109,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n3153,2,69,2017-02-26 07:40:31,http://mills.com/myles,0.6909378060,143.103.225.98,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n3154,2,69,2017-03-15 12:35:55,http://feest.io/norbert,0.1219484846,101.250.60.181,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n3155,2,69,2016-12-27 04:03:09,http://sawayn.name/walker,0.5429478769,164.56.114.216,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n3156,2,69,2017-06-04 15:53:42,http://murray.io/reece_koepp,0.1305136170,152.151.43.91,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n3157,2,69,2017-01-10 20:11:58,http://ryan.io/colt_vandervort,0.5289830917,8.145.178.242,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n3158,2,69,2017-02-07 00:52:35,http://beahan.com/kareem,0.9830014039,106.125.96.44,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n3159,2,69,2017-03-26 14:33:01,http://cruickshank.info/wyatt,0.1436772183,15.24.196.54,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n3160,2,70,2017-02-24 03:19:59,http://kilback.info/wallace,0.0501336203,166.17.141.53,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n3161,2,70,2017-05-07 15:07:47,http://champlinwhite.name/maximillia,0.9703294405,157.221.235.125,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n3162,2,70,2017-01-08 04:38:40,http://runolfon.com/austen_johns,0.3816768533,168.104.140.225,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n3163,2,70,2017-01-22 03:23:42,http://kuphal.co/raheem,0.3158470806,223.251.55.233,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n10047,4,225,2017-05-02 16:06:59,http://armstrong.co/ephraim_kuphal,0.8963865325,55.29.160.210,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n10048,4,225,2017-04-22 03:13:51,http://schaden.com/jonathan_bayer,0.4939406247,47.225.162.87,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n10049,4,225,2017-03-28 12:10:03,http://morarpredovic.org/braeden,0.3607602004,20.254.56.59,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n10050,4,225,2017-05-24 02:09:54,http://purdy.io/lavonne,0.6901845225,89.210.76.176,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n10051,4,225,2017-04-11 15:36:49,http://brakus.biz/khalid,0.5376315138,120.122.178.58,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n10052,4,225,2017-03-27 11:39:24,http://dibbert.io/johan,0.4527326973,142.150.27.28,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n10053,4,225,2017-03-30 01:48:12,http://pouros.info/pete_hand,0.8423703205,196.143.119.199,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n10054,4,225,2017-05-07 08:17:13,http://schimmel.com/melisa_gorczany,0.1614912784,194.213.156.176,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n10055,4,225,2017-01-16 23:32:39,http://barrows.co/domenico.kunze,0.0590944245,3.153.18.135,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n10056,4,225,2017-01-02 04:04:14,http://lueilwitz.com/drake_von,0.9083508914,128.214.219.78,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n10057,4,225,2017-04-20 21:48:30,http://howell.co/eladio_trantow,0.4691959655,147.163.45.139,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n10058,4,225,2017-03-27 11:09:43,http://rohan.biz/kaylie,0.6507741491,225.199.50.127,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n10059,4,225,2017-05-10 01:35:40,http://swaniawski.biz/anya,0.0159485417,72.217.140.93,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n10060,4,225,2017-06-05 09:06:22,http://moore.org/deshawn,0.6421662535,95.242.60.145,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n10061,4,225,2017-02-14 21:06:35,http://aufderharwiegand.com/craig,0.1884342827,109.252.215.28,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n10062,4,225,2017-01-18 19:55:45,http://faheyanderson.biz/aleandro,0.9759867905,158.154.39.83,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n10063,4,225,2017-02-12 22:49:37,http://auer.name/maxwell,0.6447699111,6.100.99.33,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n10064,4,225,2017-04-01 06:51:53,http://rolfson.net/audie,0.3148405542,202.52.235.66,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n10065,4,225,2017-06-02 10:57:37,http://bernhard.org/bryon.casper,0.6136907033,153.38.140.20,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n10066,4,225,2017-03-18 16:19:24,http://cormier.io/adella.auer,0.5284071008,129.238.108.210,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n10067,4,225,2017-01-20 21:07:30,http://prohaskatowne.info/merlin.abernathy,0.1465674256,68.62.21.239,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n10068,4,225,2017-05-13 10:08:32,http://jenkinsdubuque.net/delfina,0.9093268155,128.95.59.90,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n10069,4,226,2017-02-21 21:56:36,http://gusikowski.org/caden,0.0927987991,206.208.143.37,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n10070,4,226,2017-01-21 15:55:02,http://goodwin.name/lillie_hintz,0.8965432268,81.105.163.248,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n10071,4,226,2017-01-09 20:12:35,http://barton.co/griffin,0.2903004413,151.78.107.195,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n10072,4,226,2017-02-24 05:02:18,http://hyattbogisich.biz/clotilde,0.8055224888,14.133.6.216,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n10073,4,226,2017-05-03 18:15:53,http://binshoppe.info/annabel,0.5319501797,214.57.87.129,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n10074,4,226,2017-01-26 14:44:14,http://torphyblock.info/urban.kohler,0.7789587729,30.79.141.97,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n10075,4,226,2017-05-29 10:11:00,http://cormier.name/ruby,0.0202851091,48.107.226.5,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n10076,4,226,2017-05-07 19:16:44,http://pollich.biz/gudrun,0.1514022032,165.39.79.87,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n10077,4,226,2017-02-26 01:13:50,http://bogisichgutkowski.org/jose,0.4710720278,184.124.59.236,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n10078,4,226,2017-01-25 02:22:23,http://mraz.name/sigmund,0.3581254307,49.8.148.227,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n10079,4,226,2016-12-23 03:41:56,http://hermiston.name/toni.runte,0.9604485637,246.82.150.158,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n10080,4,226,2017-03-21 11:17:38,http://bruenschuppe.biz/adah,0.1126660183,242.129.254.204,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n10081,4,226,2017-03-02 03:55:33,http://kelerfay.com/gerardo.herman,0.1366062928,50.252.49.178,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n10082,4,226,2017-03-18 07:20:19,http://langosh.io/camilla,0.6457041416,79.163.207.160,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n10083,4,226,2017-01-06 06:43:07,http://thompson.name/linnea_grady,0.1530135510,183.94.30.97,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n10084,4,226,2017-02-17 06:02:39,http://tillmandare.biz/tyson,0.1753918850,3.40.30.179,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n10085,4,226,2017-05-16 07:07:18,http://gibsonkuphal.name/sofia,0.8340643466,199.231.218.121,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n10086,4,226,2017-02-02 12:01:19,http://christiansenkutch.org/greyson.casper,0.1945193331,59.132.144.74,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n10087,4,226,2017-04-09 17:18:35,http://davis.org/tyler,0.4832460840,245.71.135.108,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n10088,4,226,2017-01-30 20:48:09,http://gulgowski.net/marguerite,0.2826123820,20.139.118.20,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n10089,4,226,2017-01-14 23:49:06,http://swaniawski.name/mina.johnson,0.6488654431,195.38.210.177,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n10090,4,226,2017-05-31 20:50:05,http://satterfield.name/tracey,0.6287612171,133.247.159.43,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n10091,4,226,2017-01-09 05:25:55,http://lowe.co/enid_graham,0.5150062857,148.129.167.159,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n10092,4,226,2017-02-02 04:48:26,http://cummings.org/carolanne,0.9387358654,230.199.31.232,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n10093,4,226,2017-02-25 20:45:04,http://terry.net/erna.mann,0.7581702070,126.173.180.19,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n10094,4,227,2016-12-22 01:14:28,http://cartwrightschroeder.net/meaghan_lynch,0.3109966382,108.74.88.239,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n10095,4,227,2017-05-10 07:51:25,http://mertz.net/rex,0.4515466504,32.76.133.90,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n10096,4,227,2017-02-09 20:19:43,http://bins.net/bonita,0.1717030152,197.20.225.164,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n10097,4,227,2017-02-08 10:56:21,http://gislason.info/claud,0.7552811053,67.155.122.181,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n7056,3,156,2017-05-09 16:04:03,http://ricebatz.com/flavie,0.5967866412,152.146.215.47,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n7057,3,156,2017-02-01 08:36:27,http://gleasonweinat.com/bernice.murphy,0.0333287868,16.172.80.162,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n7058,3,157,2017-05-25 23:48:55,http://walker.net/aleen,0.0567311750,251.102.64.231,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n7059,3,157,2017-04-27 09:44:07,http://considine.biz/anibal,0.9366841954,176.177.160.109,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n7060,3,157,2017-03-14 18:01:54,http://reichel.net/hilbert,0.1029688468,154.121.226.131,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n7061,3,157,2017-06-09 03:49:09,http://davis.biz/ansel,0.5609859823,187.250.139.67,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n7062,3,157,2016-12-28 02:19:02,http://kub.name/kaylin_strosin,0.0128193576,42.213.211.116,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n7063,3,157,2017-05-02 03:00:49,http://hoegerbeatty.net/briana,0.1550153762,12.86.16.136,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n7064,3,157,2017-01-27 12:48:48,http://powlowskiwitting.org/raleigh,0.4915111190,96.95.158.162,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n7065,3,157,2017-04-25 18:58:56,http://schulist.io/ardella,0.6182895478,113.241.166.113,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n7066,3,157,2017-01-01 18:14:19,http://weber.io/jermain.nolan,0.3777212918,160.135.157.73,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n7067,3,157,2016-12-30 22:15:33,http://lednersporer.biz/casper,0.8325869696,17.23.38.98,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n7068,3,157,2017-02-19 13:32:30,http://effertz.net/deangelo_pollich,0.5471904599,88.244.35.187,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n7069,3,157,2017-02-08 01:44:55,http://champlin.info/addie,0.9712519353,96.30.140.194,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n7070,3,157,2017-05-07 06:34:27,http://ward.biz/everardo,0.0230003880,142.234.112.130,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n7071,3,157,2017-04-04 08:44:04,http://frami.com/ottilie,0.8194871738,74.199.209.38,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n7072,3,157,2017-03-21 11:22:02,http://muellerhettinger.info/felton,0.1512753199,23.139.119.35,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n7073,3,157,2016-12-26 20:30:02,http://orn.info/clement,0.2130948479,165.12.116.6,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n7074,3,157,2017-04-16 10:10:56,http://collins.co/ron,0.0885804718,160.19.204.239,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n7075,3,157,2017-05-14 02:40:40,http://schultz.co/estella.osinski,0.6037884710,65.130.186.183,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n7076,3,157,2017-05-22 19:42:26,http://jast.name/maximilian_herzog,0.2378929184,206.39.33.102,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n7077,3,157,2017-03-04 09:45:03,http://veum.org/dannie.hilll,0.1994470863,25.168.12.106,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n7078,3,157,2017-03-19 16:42:14,http://mckenzie.net/madison,0.7149027762,223.158.100.241,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n7079,3,157,2017-04-15 20:45:08,http://goyette.io/bernhard.will,0.2188010941,98.154.194.120,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n7080,3,157,2017-05-25 06:24:36,http://hanewitting.biz/lew,0.4440808125,94.186.215.96,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n7081,3,157,2017-05-17 12:49:41,http://reynoldtark.com/breanna.lockman,0.5481628532,220.132.154.202,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n7082,3,157,2017-03-02 17:29:53,http://conroy.co/keira,0.4594788193,4.49.48.55,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n7083,3,157,2017-04-23 12:02:01,http://purdyokon.biz/ettie.hintz,0.2319666502,179.92.14.9,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n7084,3,157,2017-05-23 15:57:15,http://breitenberg.name/rebecca,0.5736828474,90.62.72.176,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n7085,3,157,2017-06-03 22:40:05,http://hodkiewiczstoltenberg.io/rod,0.9617909479,43.103.222.200,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n7086,3,157,2017-04-28 11:41:52,http://feeney.co/florencio,0.7898441842,41.63.9.58,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n7087,3,157,2017-01-18 07:52:22,http://ziemann.org/cory.langworth,0.7160168862,194.95.100.100,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n7088,3,158,2017-05-20 09:19:50,http://denesik.co/hiram,0.5076915781,94.56.147.59,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n7089,3,158,2017-03-13 12:42:50,http://kemmerhand.biz/damian,0.8946720664,126.49.115.48,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n7090,3,158,2017-02-22 16:26:33,http://gerlach.name/lexi.keebler,0.3129572937,7.92.185.93,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n7091,3,158,2017-02-08 11:00:30,http://fadel.io/linwood,0.1666701422,130.107.137.135,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n7092,3,158,2017-04-02 08:42:00,http://hansen.co/sophie_moriette,0.4384174488,122.12.115.139,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n7093,3,158,2017-02-11 07:57:51,http://daniel.info/zelda.schinner,0.7196864813,158.81.223.15,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n7094,3,158,2017-02-22 09:44:23,http://metz.biz/emelie,0.1879753913,114.241.6.76,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n7095,3,158,2017-03-29 00:19:18,http://zieme.name/howell_mitchell,0.3571206246,166.53.191.169,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n7096,3,158,2017-02-07 08:42:11,http://kilback.co/elody,0.1070852815,51.203.90.105,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n7097,3,158,2017-05-25 18:57:18,http://gutmann.com/may_donnelly,0.8081765585,112.229.215.173,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n7098,3,158,2017-01-21 18:46:27,http://doyle.org/vallie,0.2105464417,156.217.163.52,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n7099,3,158,2016-12-19 14:21:02,http://boehm.biz/rebekah,0.6513311080,179.60.152.250,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n7100,3,158,2017-05-22 18:18:58,http://jacobshoppe.net/hillard.koelpin,0.7822400916,216.103.188.13,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n7101,3,158,2017-06-10 11:44:28,http://ruel.info/muriel.runte,0.2390961175,72.46.79.24,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n7102,3,158,2017-03-02 02:24:51,http://brekke.biz/beth,0.8653283838,80.54.198.157,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n7103,3,158,2017-01-28 12:55:22,http://brown.biz/adrain,0.6063683983,103.183.250.184,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n7104,3,158,2017-06-02 07:14:18,http://reynoldsko.name/brandt.littel,0.3558468345,36.179.152.180,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n7105,3,158,2016-12-31 00:24:55,http://schiller.name/ahmed,0.6433073994,220.141.110.186,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n7106,3,158,2017-02-01 13:26:51,http://feil.com/carolina_hahn,0.0453928478,219.209.241.127,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n7107,3,158,2017-01-31 20:46:02,http://kohler.co/orion,0.0830145086,121.102.204.182,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n12964,5,290,2017-02-25 04:37:49,http://rempel.net/ernestine_tremblay,0.9370939059,86.3.77.69,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n12965,5,290,2017-06-12 04:56:05,http://okuneva.com/coy.nikolaus,0.2069425362,117.205.37.251,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n12966,5,290,2017-04-17 21:44:31,http://ryan.com/micah,0.8543146560,143.242.33.158,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n12967,5,290,2017-01-22 14:13:39,http://weinat.org/camden,0.7648103820,3.232.48.85,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n12968,5,290,2017-01-23 13:09:57,http://feil.org/hope,0.7705368696,80.98.128.128,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n12969,5,290,2017-04-08 07:05:00,http://hackettmueller.name/garland,0.1323472432,190.243.88.181,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n12970,5,290,2017-04-26 00:19:54,http://von.net/lilly,0.4373598389,152.174.36.181,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n12971,5,290,2017-04-26 08:23:49,http://hillanford.co/kennith_hilll,0.5403922576,68.56.225.48,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n12972,5,290,2017-02-06 13:32:38,http://erdmanhowell.io/elsie.anderson,0.0835851416,75.161.153.147,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n12973,5,291,2017-01-20 04:28:33,http://rolfson.info/bernardo,0.7182776696,82.211.7.6,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n12974,5,291,2017-03-04 17:57:52,http://bradtke.io/reece.ritchie,0.7306997775,115.206.160.244,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n12975,5,291,2016-12-29 21:09:22,http://volkmankulas.name/rhea,0.1278940541,177.110.69.212,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n12976,5,291,2017-04-07 08:07:05,http://mayert.net/emmalee,0.1709884575,248.115.111.243,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n12977,5,291,2017-06-06 09:57:58,http://dubuquegrant.io/jennings.kuhlman,0.2704914706,36.103.76.86,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n12978,5,291,2017-01-02 04:19:38,http://stoltenberg.org/sister,0.8889207650,54.89.191.229,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n12979,5,291,2016-12-30 17:16:44,http://murazik.name/archibald,0.5748712531,81.152.148.52,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n12980,5,291,2017-05-05 15:34:14,http://wisokybecker.com/cornell,0.6870723165,136.32.181.216,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n12981,5,291,2017-01-20 19:51:29,http://hammes.biz/kallie,0.7803801234,119.226.193.149,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n12982,5,291,2016-12-28 03:31:53,http://shields.name/easter,0.0275044990,73.241.207.190,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n12983,5,291,2017-04-15 18:00:20,http://prohaskapadberg.co/kaela_mosciski,0.6975186117,49.190.225.203,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n12984,5,291,2017-03-21 14:30:19,http://batz.com/marcella,0.3275682393,141.68.20.248,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n12985,5,291,2017-06-11 03:54:13,http://dooleybechtelar.com/kaden.pagac,0.2337586066,66.147.173.199,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n12986,5,291,2017-05-26 22:04:51,http://strosin.biz/judd,0.2687380016,240.131.156.147,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n12987,5,291,2016-12-15 05:39:03,http://jacobsziemann.com/katlyn,0.4534504027,113.101.64.252,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n12988,5,291,2017-02-26 12:11:18,http://miller.name/derrick.herzog,0.0577833451,221.111.173.139,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n12989,5,291,2017-02-08 01:27:18,http://graham.name/travon,0.2924272255,114.78.210.65,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n12990,5,291,2017-01-31 07:50:18,http://millshauck.io/mike.bailey,0.1967106309,195.122.235.106,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n12991,5,291,2017-01-10 20:07:54,http://runolfon.biz/kelton,0.2450870992,149.53.20.197,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n12992,5,291,2017-02-05 10:31:30,http://lemke.org/rowland.runolfsdottir,0.6484380855,137.112.210.47,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n12993,5,291,2017-02-25 14:02:26,http://cummings.net/lera,0.9569843695,134.45.57.187,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n12994,5,291,2017-05-02 21:57:36,http://handkunze.io/philip.ankunding,0.7866881664,170.59.45.150,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n12995,5,291,2016-12-28 03:52:34,http://bogisich.net/elyse,0.1908533126,177.247.240.36,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n12996,5,291,2017-05-05 22:12:02,http://adams.io/novella,0.9289694406,72.44.77.246,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n12997,5,291,2017-04-03 13:32:45,http://bosco.net/florida_adams,0.9398514625,242.80.225.224,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n12998,5,291,2016-12-13 08:57:37,http://nolanwalsh.org/shea.volkman,0.9808276364,142.240.122.74,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n12999,5,292,2017-01-12 14:45:50,http://torphykeeling.com/golden.champlin,0.0878710696,2.54.57.52,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n13000,5,292,2017-05-03 06:15:42,http://bartoletti.co/vickie_bauch,0.6314044983,89.142.146.102,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n13001,5,292,2017-02-16 08:05:39,http://heller.com/vivian,0.3405306538,227.160.175.94,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n13002,5,292,2017-05-11 19:53:39,http://lindgren.net/clair.nolan,0.4701617972,69.136.248.126,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n13003,5,292,2017-03-13 06:10:27,http://gerhold.info/hilton,0.4689550291,245.117.206.3,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n13004,5,292,2017-01-03 16:15:15,http://stiedemannkuvalis.org/kavon_farrell,0.2092244725,75.228.108.115,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n13005,5,292,2017-01-19 09:57:16,http://feest.net/dangelo,0.0946881106,101.188.23.49,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n13006,5,292,2017-01-26 05:19:14,http://mitchell.co/cara.graham,0.5972926003,68.103.238.184,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n13007,5,292,2017-04-16 16:03:56,http://hodkiewicz.com/lexie_gislason,0.9448041806,97.42.245.27,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n13008,5,292,2016-12-29 07:34:53,http://kilback.biz/lottie.sawayn,0.5688799455,235.218.92.43,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n13009,5,292,2017-02-21 09:07:03,http://okuneva.com/davin.anderson,0.0179929344,142.125.230.76,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n13010,5,292,2016-12-24 14:34:57,http://kovacek.co/jennie,0.8189158677,250.60.82.214,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n13011,5,292,2017-03-26 08:24:52,http://littelberge.name/yasmeen.ledner,0.9280171973,90.243.144.239,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n13012,5,292,2017-03-29 19:21:31,http://harris.biz/sylvester,0.2673627990,21.72.97.213,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n13013,5,292,2017-04-05 19:57:41,http://padbergcasper.org/jaqueline,0.5038247888,125.28.41.161,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n13014,5,292,2017-04-11 16:39:26,http://feest.io/armand,0.6134931194,106.145.147.229,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n13015,5,292,2017-04-27 23:42:14,http://conroy.io/corine.weinat,0.8841835339,132.114.29.92,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n15971,6,359,2017-04-25 23:22:05,http://damore.net/hudson_hahn,,205.240.209.108,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n15972,6,359,2017-01-09 16:49:27,http://luettgen.io/nathanial.west,,16.54.201.107,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n15973,6,359,2017-05-24 10:25:41,http://lebsackkris.name/laila.feest,,159.96.74.12,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n15974,6,359,2017-01-31 13:29:36,http://kihn.io/mallie_kuhlman,,78.190.119.43,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n15975,6,359,2017-01-23 00:51:52,http://ratkeweimann.info/esperanza,,41.28.238.239,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n15976,6,359,2016-12-22 06:32:21,http://beatty.biz/bobby,,87.32.75.75,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n15977,6,359,2017-01-10 00:55:21,http://tromp.com/remington.daniel,,80.213.30.149,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n15978,6,359,2017-01-29 11:31:16,http://corwinadams.name/randall.mckenzie,,53.95.49.170,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n15979,6,359,2017-02-01 06:20:34,http://reillybreitenberg.co/aric,,144.166.70.227,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n15980,6,359,2017-01-28 13:02:41,http://larson.co/edmond_herzog,,154.42.41.126,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n15981,6,359,2017-01-13 04:29:18,http://rice.biz/lilian,,31.82.173.106,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n15982,6,359,2017-06-11 09:46:57,http://graham.com/kenneth,,149.188.56.94,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n15983,6,359,2017-05-19 12:18:44,http://kuvalis.co/herman,,109.108.140.142,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n15984,6,359,2017-05-17 07:58:55,http://schneider.biz/winston_muller,,98.17.232.11,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n15985,6,360,2017-03-21 12:43:09,http://lowe.name/norbert_koelpin,,66.161.88.167,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n15986,6,360,2016-12-22 05:48:44,http://waters.io/citlalli,,232.3.90.160,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n15987,6,360,2017-03-22 20:59:30,http://mills.com/cecile,,105.178.172.157,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n15988,6,360,2016-12-29 10:19:50,http://yostgibson.com/clint_schaden,,164.11.232.164,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n15989,6,360,2017-04-11 02:38:15,http://collins.biz/lester_fadel,,241.181.61.146,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n15990,6,360,2017-02-21 03:09:59,http://bashirianblick.name/reynold.schmidt,,102.44.196.132,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n15991,6,360,2017-02-13 05:37:27,http://doylegislason.info/edgardo_kuhn,,123.72.18.64,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n15992,6,360,2017-02-06 14:00:38,http://robelkemmer.name/althea,,146.74.179.33,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n15993,6,360,2017-04-14 02:24:16,http://skiles.info/bobbie.raynor,,204.114.214.93,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n15994,6,360,2017-06-02 13:54:38,http://christiansengerlach.name/webster_luettgen,,248.194.192.127,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n15995,6,360,2017-04-22 10:03:09,http://gleichner.com/alycia,,193.98.37.209,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n15996,6,360,2017-04-21 03:54:09,http://boehm.io/erik_huel,,111.98.79.129,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n15997,6,360,2017-01-28 03:45:26,http://mitchell.biz/selena,,253.27.225.206,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n15998,6,360,2017-01-18 20:22:24,http://hintzkutch.org/selina,,37.206.229.95,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n15999,6,360,2017-02-16 04:37:55,http://veum.info/sydney.wilderman,,19.156.178.77,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n16000,6,360,2017-01-02 20:17:12,http://bailey.io/caterina,,149.154.177.101,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n16001,6,360,2017-02-07 08:49:25,http://rath.net/dexter_kozey,,88.30.62.175,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n16002,6,360,2017-01-18 16:05:30,http://emardkonopelski.com/angel_hoppe,,115.244.98.133,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n16003,6,360,2017-05-13 01:35:55,http://oreilly.info/mitchel,,235.184.4.115,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n16004,6,360,2017-05-12 10:16:25,http://wyman.info/betty,,7.116.195.118,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n16005,6,360,2017-02-23 09:18:24,http://hudsonjakubowski.org/brandon,,85.116.149.16,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n16006,6,360,2017-05-15 19:37:32,http://miller.info/holly,,140.55.200.40,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n16007,6,360,2017-03-12 13:53:28,http://pricegraham.org/stella,,17.248.63.10,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n16008,6,361,2017-01-17 13:06:45,http://weinat.biz/yasmin_champlin,,194.36.184.196,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n16009,6,361,2017-01-08 11:41:24,http://thiel.co/mack_lockman,,24.163.26.147,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n16010,6,361,2017-05-14 23:15:59,http://daniel.info/zita,,131.228.237.182,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n16011,6,361,2017-01-01 15:28:38,http://thompsonortiz.co/milan.torp,,101.170.243.21,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n16012,6,361,2017-01-21 05:11:51,http://vonrueden.biz/magdalena.buckridge,,159.193.225.83,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n16013,6,361,2017-02-15 19:58:42,http://lehner.co/cristobal.cremin,,51.105.118.172,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n16014,6,361,2017-06-07 05:30:36,http://kunzebashirian.co/jose,,56.71.98.136,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n16015,6,361,2017-05-18 22:03:35,http://buckridge.co/lucius,,242.195.35.51,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n16016,6,361,2017-02-18 23:54:43,http://stokesadams.net/adriana.kilback,,25.66.41.143,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n16017,6,361,2017-04-05 19:27:47,http://reynoldskautzer.com/lolita,,123.49.27.110,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n16018,6,361,2017-03-30 20:50:14,http://wunsch.com/edison.thiel,,120.181.159.198,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n16019,6,361,2017-01-08 14:01:29,http://towneebert.io/dayne,,54.188.235.36,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n16020,6,361,2017-05-12 08:35:36,http://dubuque.net/liliane,,151.198.36.114,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n16021,6,361,2017-05-14 05:53:55,http://sipespadberg.name/daniela.satterfield,,231.178.182.150,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n16022,6,361,2017-05-22 19:42:11,http://grahamweimann.org/frances,,175.145.161.162,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n16023,6,361,2016-12-29 02:30:04,http://ryanadams.info/telly,,130.18.105.155,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n16024,6,361,2017-01-10 23:17:20,http://runolfon.co/jefferey,,113.42.137.147,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n16025,6,361,2017-01-01 22:05:48,http://bartoletti.co/cleve_mcglynn,,103.219.217.232,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n3164,2,70,2017-04-26 12:18:50,http://langosh.com/dylan.satterfield,0.3995446815,172.210.245.150,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n3165,2,70,2016-12-14 19:51:12,http://leannonfeil.co/crawford,0.7269254292,188.231.166.31,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n3166,2,70,2017-01-11 06:20:16,http://herman.com/nadia,0.4981931865,123.213.225.198,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n3167,2,70,2017-04-26 10:36:11,http://okuneva.io/aisha.kuvalis,0.2036350992,207.146.228.114,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n3168,2,70,2017-06-04 11:40:42,http://wintheiser.name/ali,0.7791044149,233.154.134.69,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n3169,2,70,2017-06-02 14:16:16,http://windler.biz/enos,0.5576621281,65.4.54.208,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n3170,2,70,2017-01-19 05:40:15,http://kertzmann.co/precious_greenholt,0.2306446487,192.19.12.108,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n3171,2,70,2017-04-29 18:39:53,http://reichertconsidine.info/napoleon.klocko,0.8635961808,150.100.82.111,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n3172,2,70,2017-06-01 08:14:49,http://champlinlindgren.name/astrid,0.0891855067,132.24.183.209,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n3173,2,70,2017-04-10 15:56:48,http://koelpinbeahan.net/jamarcus,0.8422969521,89.131.49.226,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n3174,2,70,2017-02-18 20:46:50,http://bergstromeffertz.info/ova,0.6214309409,189.134.57.14,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n3175,2,70,2017-02-02 13:45:12,http://grantkunde.net/savanna,0.1519146681,130.80.86.90,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n3176,2,70,2016-12-31 17:17:19,http://zemlak.biz/blaise.smitham,0.6224488311,102.101.232.246,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n3177,2,70,2017-03-16 16:18:28,http://mcdermott.org/hilario,0.2703748848,96.197.109.219,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n3178,2,70,2017-03-03 18:02:20,http://effertzleuschke.co/adam,0.9819698129,73.114.16.85,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n3179,2,70,2017-03-29 01:04:49,http://paucek.name/horace,0.6134758915,185.70.176.48,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n3180,2,70,2017-02-09 19:42:28,http://schmitt.co/evan.becker,0.6190268694,76.112.171.178,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n3181,2,70,2017-03-09 10:02:36,http://smith.io/nelson,0.7606227337,180.95.190.165,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n3182,2,70,2017-01-05 00:18:40,http://crist.com/grayson_conn,0.6191406598,128.76.254.135,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n3183,2,70,2017-01-14 04:12:44,http://crooks.info/luisa,0.2900826882,153.209.189.137,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n3184,2,70,2017-05-06 02:29:34,http://mooremarquardt.org/patience,0.7598943188,101.131.189.15,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n3185,2,70,2017-05-09 16:42:10,http://hermanbogisich.name/carlo.hamill,0.4921300632,104.146.249.218,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n3186,2,70,2017-01-31 12:59:00,http://hicklefadel.io/jovani,0.6312898753,218.39.179.88,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n3187,2,70,2017-03-30 02:28:51,http://kulas.biz/magali.gaylord,0.2207516186,164.193.215.151,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n3188,2,70,2016-12-18 16:17:45,http://powlowski.co/sydni_kiehn,0.5645217244,166.48.231.4,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n3189,2,70,2017-02-05 21:55:21,http://macejkovic.net/annabel,0.2988220242,99.108.27.110,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n3190,2,70,2017-03-07 03:33:43,http://zboncaknicolas.io/johnnie,0.5294670252,212.120.106.73,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n3191,2,70,2017-01-06 23:06:39,http://kuhnokon.co/jettie.mante,0.5736509639,47.26.103.228,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n3192,2,70,2017-02-02 02:19:25,http://kutch.org/merlin.skiles,0.7365899188,122.165.144.251,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n3193,2,70,2017-01-26 20:59:51,http://franecki.name/mathew_considine,0.8382310223,245.132.194.115,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n3194,2,70,2017-01-31 13:07:13,http://heathcote.org/immanuel.dooley,0.0491573526,71.200.107.59,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n3195,2,70,2017-01-12 20:33:14,http://pricehuel.net/carolanne_homenick,0.4668266403,105.217.201.171,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n3196,2,70,2017-03-08 18:48:54,http://schimmel.info/bonnie,0.1052624965,5.4.136.143,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n3197,2,70,2017-03-01 02:55:57,http://kaulkeflatley.name/vince_sauer,0.4389680120,203.169.218.38,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n3198,2,70,2017-03-01 20:43:49,http://vonruedenbreitenberg.com/creola,0.4113556699,111.98.128.184,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n3199,2,70,2017-01-06 02:00:45,http://kilback.biz/dion,0.8085387191,29.253.170.54,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n3200,2,70,2017-06-12 00:33:54,http://keeling.info/kendrick,0.5736482370,16.253.231.236,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n3201,2,70,2017-02-15 11:35:22,http://sengermacgyver.org/casimir,0.8246168954,84.243.6.7,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n3202,2,70,2017-01-13 07:58:38,http://zieme.biz/jaqueline,0.0657853727,225.64.240.186,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n3203,2,70,2017-06-03 01:09:35,http://jerdebauch.net/reta.watsica,0.3588749459,223.26.150.99,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n3204,2,70,2016-12-21 13:32:24,http://abbott.com/jena,0.0143137107,240.239.78.233,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n3205,2,70,2017-06-13 01:36:31,http://runolfsdottir.biz/glenna,0.1816033279,160.173.211.112,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n3206,2,70,2017-05-03 09:55:05,http://reillyquitzon.net/jovany.deckow,0.1485986436,28.188.144.199,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n3207,2,70,2017-01-07 03:18:40,http://mraz.name/wayne.hermann,0.5027297235,77.145.165.197,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n3208,2,70,2017-02-20 23:21:56,http://kojakubowski.io/hilario,0.5652418826,79.77.43.82,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n3209,2,70,2017-05-05 05:13:35,http://hillsmills.org/gregory_schuster,0.1086386041,84.129.4.97,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n3210,2,70,2017-01-20 12:25:45,http://dickinson.com/benny,0.8201960793,21.233.79.43,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n3211,2,70,2017-05-22 04:32:36,http://larkinkrajcik.name/osbaldo_rempel,0.3163130446,153.239.53.108,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n3212,2,70,2016-12-19 04:40:27,http://cormiervonrueden.co/reie,0.2893209751,190.177.211.100,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n3213,2,70,2017-02-19 16:50:54,http://bosco.co/ona.predovic,0.7843253866,22.152.2.235,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n3214,2,70,2017-05-22 21:01:22,http://turcotte.biz/ansley_herman,0.2523834349,149.39.49.152,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n10098,4,227,2017-01-08 15:10:02,http://gerhold.name/jasmin.witting,0.2999711425,77.176.235.3,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n10099,4,227,2017-04-26 03:01:31,http://collier.io/alexander_cremin,0.5574552123,131.49.74.42,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n10100,4,227,2017-01-16 21:22:30,http://sauer.net/stefan_haag,0.7188778358,245.69.23.52,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n10101,4,227,2017-04-14 03:06:57,http://moen.info/maddison,0.2989466310,46.184.215.189,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n10102,4,227,2017-02-20 10:59:17,http://mertz.net/antonetta.bauch,0.7650339055,246.63.8.85,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n10103,4,227,2017-03-02 07:51:02,http://kuhn.org/arnulfo.wintheiser,0.3047712608,17.160.214.15,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n10104,4,227,2017-04-21 04:16:02,http://davis.com/joseph.lebsack,0.5922916891,126.91.56.141,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n10105,4,227,2017-03-13 07:19:30,http://will.net/brody.schneider,0.1235586767,180.23.189.98,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n10106,4,227,2017-01-30 15:49:17,http://nitzsche.co/ernesto,0.9741504274,51.206.97.121,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n10107,4,227,2017-02-05 16:33:31,http://stammdonnelly.name/santino.wuckert,0.1763606904,22.139.247.42,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n10108,4,227,2017-04-21 03:51:37,http://oberbrunnerjaskolski.co/rosie.christiansen,0.4362991037,198.48.137.124,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n10109,4,227,2017-02-12 00:23:27,http://balistreri.net/ronny,0.2229095207,44.33.102.47,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n10110,4,227,2017-03-25 17:26:04,http://koepp.info/amani,0.1154796555,245.114.243.230,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n10111,4,227,2017-05-10 08:46:29,http://hermannhahn.co/conner,0.0488825977,183.250.107.64,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n10112,4,227,2017-04-18 09:57:01,http://cormiermccullough.name/kristin.dooley,0.2531532267,158.226.222.7,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n10113,4,227,2017-01-27 06:18:44,http://witting.io/maritza,0.2873185384,50.147.198.27,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n10114,4,227,2016-12-22 21:20:16,http://ritchie.io/virginia,0.8764654319,47.222.223.132,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n10115,4,227,2017-01-24 10:28:50,http://bogisichbrekke.name/reinhold.jones,0.4474255691,245.246.103.142,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n10116,4,227,2017-05-04 01:32:33,http://mckenzie.com/meaghan_goyette,0.9005084192,148.215.128.77,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n10117,4,227,2017-04-14 14:30:34,http://gorczanybrown.io/eloy.brown,0.9527298232,74.96.135.57,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n10118,4,227,2017-01-09 00:50:40,http://lemketremblay.info/kelsi_mohr,0.3243366193,229.172.214.86,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n10119,4,227,2017-02-28 21:34:33,http://vonreilly.co/shanel_renner,0.4897581340,41.11.126.91,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n10120,4,227,2017-02-17 01:42:30,http://bradtke.info/dennis,0.4495669875,181.146.163.200,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n10121,4,227,2017-04-20 17:56:38,http://wehner.info/edwardo.grady,0.4919298034,220.238.115.187,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n10122,4,227,2017-01-20 17:31:11,http://doyle.co/jerrod.heaney,0.0245842196,81.116.152.27,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n10123,4,227,2017-06-02 05:50:34,http://conn.io/horacio.murphy,0.3301172545,34.5.50.248,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n10124,4,227,2017-05-25 18:59:57,http://fisher.org/nicolette,0.8309284190,71.242.165.69,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n10125,4,227,2017-06-10 18:42:17,http://wisokyschinner.info/carmelo.kreiger,0.5230115363,9.56.239.154,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n10126,4,228,2017-05-17 03:01:15,http://padberggleichner.name/mafalda_williamson,0.6538482406,163.250.79.221,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n10127,4,228,2016-12-15 22:49:26,http://huel.net/jordane_zemlak,0.8296645972,48.111.121.64,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n10128,4,228,2017-04-22 07:41:49,http://heidenreich.org/floy,0.2961226237,10.134.51.58,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n10129,4,228,2017-01-15 14:55:59,http://sipesblock.org/garett,0.3157907186,95.92.39.178,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n10130,4,228,2017-04-06 22:45:30,http://kautzermedhurst.io/ole.will,0.7712411500,58.160.48.245,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n10131,4,228,2017-04-05 02:46:55,http://doyleleffler.name/ramiro.flatley,0.2231594701,246.182.162.33,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n10132,4,228,2017-02-07 16:09:24,http://bechtelarbauch.info/terrill,0.4000776033,53.145.162.254,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n10133,4,228,2016-12-28 20:24:20,http://reynolds.info/clotilde.kohler,0.4283429046,251.44.46.110,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n10134,4,228,2017-05-28 14:09:51,http://sporer.io/yvette.cummerata,0.1270343523,110.242.169.98,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n10135,4,228,2017-05-18 23:46:48,http://corkerygoyette.org/lonny.rath,0.8190526142,222.120.98.114,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n10136,4,228,2017-03-23 17:42:03,http://donnellyarmstrong.co/isabell,0.0450598976,232.240.137.160,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n10137,4,228,2017-01-05 08:01:26,http://wittingstrosin.co/bert,0.9140051563,159.101.236.197,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n10138,4,228,2017-01-28 12:13:59,http://monahan.net/margarett_daniel,0.5072758715,150.187.42.142,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n10139,4,228,2017-02-18 07:49:01,http://dickensjohns.name/deven_rolfson,0.4814953619,141.203.61.168,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n10140,4,228,2017-01-10 09:31:41,http://effertzkunde.info/axel,0.4930035481,131.101.120.43,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n10141,4,228,2017-04-10 08:30:11,http://dach.info/arielle,0.6100998913,57.165.203.115,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n10142,4,228,2017-02-11 02:54:01,http://emmerich.name/delpha,0.3229747228,214.213.208.50,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n10143,4,228,2017-05-20 05:04:09,http://stiedemann.name/antonette.bradtke,0.0256426523,111.211.84.190,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n10144,4,228,2016-12-14 19:18:52,http://bartoletti.com/christelle,0.4099673203,245.103.95.41,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n10145,4,228,2017-03-20 04:49:36,http://paucek.io/clementine,0.6394117659,185.56.162.184,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n10146,4,228,2017-06-02 04:25:53,http://jenkinsmckenzie.biz/mateo_blick,0.7486199186,63.136.10.198,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n10147,4,228,2017-02-15 05:06:35,http://lind.name/kurt,0.1414203785,25.237.47.111,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n10148,4,228,2017-03-21 00:05:42,http://waelchikuhic.co/daron,0.7758615428,152.201.210.190,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n7108,3,158,2017-01-03 02:03:41,http://davisnitzsche.io/mavis,0.0259288807,228.220.91.80,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n7109,3,158,2017-05-19 14:21:07,http://conn.biz/perry,0.7184517048,182.95.162.11,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n7110,3,158,2017-03-20 06:05:31,http://jacobson.biz/hayley_waters,0.0165299534,151.232.106.203,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n7111,3,158,2017-05-14 10:44:15,http://klingruel.biz/stefan,0.4625819741,238.88.200.42,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n7112,3,158,2017-01-01 20:20:46,http://bednargerlach.info/hailee_mraz,0.5826191778,147.2.160.122,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n7113,3,158,2017-06-02 21:58:44,http://gottlieb.net/preston,0.6180902773,125.195.140.231,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n7114,3,158,2017-02-28 09:15:00,http://witting.info/janick,0.9757653506,206.56.146.88,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n7115,3,158,2017-03-28 21:11:04,http://rolfson.net/eugene,0.4090121515,78.13.179.34,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n7116,3,158,2017-05-28 08:47:14,http://rowecorwin.io/lavina,0.3948104941,195.149.150.85,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n7117,3,158,2016-12-20 06:00:09,http://ko.org/ivah.spinka,0.9299602623,117.138.164.147,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n7118,3,158,2017-05-14 03:03:34,http://haley.io/morris,0.3753767857,70.28.66.201,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n7119,3,158,2017-01-24 19:53:57,http://kerluke.biz/stuart,0.8332994322,5.206.33.96,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n7120,3,158,2017-05-16 05:26:43,http://blockhomenick.net/letitia,0.7670838278,121.31.16.249,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n7121,3,158,2017-01-23 19:07:54,http://kuhlman.info/virginia_mohr,0.4017290327,15.187.203.82,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n7122,3,158,2017-05-04 10:10:25,http://nitzsche.biz/dena,0.1333217604,189.74.159.65,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n7123,3,158,2017-05-29 06:35:05,http://brakuchmidt.name/angel_glover,0.1793286629,113.58.93.42,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n7124,3,158,2017-03-17 10:07:01,http://dickens.name/evan_hauck,0.6652268726,204.50.82.205,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n7125,3,158,2017-03-17 15:08:01,http://lebsackmcclure.co/ramona.west,0.2982106747,240.228.86.9,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n7126,3,158,2016-12-24 18:13:42,http://davis.net/candido,0.0137252945,140.222.21.197,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n7127,3,158,2016-12-23 01:33:33,http://feeney.com/ila.pfeffer,0.4194038406,88.201.217.167,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n7128,3,158,2017-03-25 10:12:43,http://hermistonstrosin.name/don,0.3467404801,55.4.145.65,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n7129,3,158,2017-05-24 04:46:52,http://batz.io/lilla_corwin,0.0958603401,170.200.18.214,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n7130,3,158,2016-12-27 01:30:47,http://ankundingheidenreich.info/wilber,0.6118997205,68.123.249.77,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n7131,3,158,2017-03-10 21:54:32,http://jacobi.co/haie_braun,0.2822770438,119.100.42.23,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n7132,3,158,2017-03-26 08:42:34,http://murazikkling.co/jordyn_kuhic,0.4926158024,28.205.104.28,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n7133,3,158,2017-03-31 17:56:33,http://pourosbogan.io/jerald,0.7046601528,68.201.83.41,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n7134,3,158,2017-01-19 01:17:14,http://keebler.biz/clarabelle,0.9689898710,45.151.93.62,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n7135,3,158,2017-02-17 00:49:08,http://ondricka.org/reanna_armstrong,0.9817095524,94.223.141.129,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n7136,3,158,2017-03-09 01:19:00,http://cainmitchell.com/pietro,0.7321598889,194.165.79.71,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n7137,3,158,2017-03-30 07:58:10,http://ziemeklocko.org/emmanuelle,0.2885898450,132.121.224.79,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n7138,3,158,2017-03-25 15:52:48,http://wyman.org/bill,0.8165699443,84.101.88.27,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n7139,3,159,2017-03-01 22:26:01,http://andersonzboncak.biz/ozella_konopelski,0.5507918025,209.192.169.208,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n7140,3,159,2017-04-28 17:00:43,http://wolfweber.info/chadrick,0.5450484023,45.42.135.151,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n7141,3,159,2017-04-19 03:59:06,http://funkrath.co/forest,0.5885048465,211.159.181.109,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n7142,3,159,2016-12-16 07:33:06,http://zemlak.com/furman.kerluke,0.5784893009,78.172.31.234,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n7143,3,159,2017-02-12 19:11:40,http://mayerstokes.co/augustus,0.5120345166,54.101.67.116,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n7144,3,159,2017-02-03 22:44:26,http://kozey.com/estell,0.1143227688,145.3.91.108,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n7145,3,159,2017-05-05 11:56:53,http://senger.com/jayda.stanton,0.0661511933,140.20.163.54,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n7146,3,159,2017-06-09 03:56:39,http://cartwright.biz/antone.bartell,0.2337101414,176.23.60.253,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n7147,3,159,2017-03-18 06:22:32,http://daugherty.com/asa_senger,0.5627915219,219.2.229.222,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n7148,3,159,2017-03-21 22:51:05,http://bradtke.com/layne,0.0160742247,110.63.88.147,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n7149,3,159,2017-01-12 17:11:41,http://walker.net/roma,0.7488417764,87.230.20.153,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n7150,3,159,2017-02-02 21:28:25,http://friesenkovacek.com/te,0.2926401315,230.45.87.235,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n7151,3,159,2017-03-15 06:47:39,http://boyerhowell.org/marty_kovacek,0.5767038461,95.62.75.194,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n7152,3,159,2017-06-02 06:42:29,http://schmeler.io/mabelle.price,0.5840588052,13.147.60.175,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n7153,3,159,2017-06-02 21:18:54,http://langworth.org/daron,0.1259956049,191.125.245.202,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n7154,3,159,2017-01-25 07:13:35,http://gradyschneider.info/kip,0.8104220538,34.147.183.234,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n7155,3,159,2016-12-20 08:23:30,http://cummingsrempel.name/reva,0.5176506628,213.153.168.176,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n7156,3,159,2017-02-19 15:48:23,http://heathcote.info/arch_douglas,0.7194358241,127.160.238.250,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n7157,3,159,2017-04-10 08:42:17,http://grant.org/osvaldo.white,0.2793506395,229.98.47.183,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n7158,3,159,2017-04-28 23:14:42,http://cremin.name/autumn,0.3433644069,181.24.71.166,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n7159,3,159,2017-04-30 19:27:53,http://nienow.io/zoe,0.6521688503,2.12.240.43,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n13016,5,292,2017-02-20 18:17:22,http://spencer.info/albin_bailey,0.2943082138,242.38.159.86,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n13017,5,292,2017-03-19 23:16:41,http://lowe.info/seth,0.8280911811,197.23.182.254,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n13018,5,292,2017-02-19 18:37:51,http://bernhardschulist.io/cielo_reichert,0.2072642508,7.78.111.93,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n13019,5,292,2017-05-09 10:20:57,http://haley.com/julien,0.3318970043,189.225.113.202,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n13020,5,292,2017-01-24 20:19:14,http://gaylordhayes.info/juwan_cain,0.1983777353,21.126.94.172,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n13021,5,292,2017-01-16 01:31:31,http://dicki.info/ilene,0.4271826278,169.74.237.163,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n13022,5,292,2017-02-17 13:05:29,http://williamson.com/margarett.pollich,0.0028463334,174.84.69.55,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n13023,5,292,2017-02-16 02:22:35,http://rippinbruen.io/damian,0.9648393564,100.56.166.220,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n13024,5,292,2017-01-25 04:02:21,http://fayboyer.io/rupert,0.3821288254,24.196.166.98,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n13025,5,292,2017-05-20 17:28:35,http://hansen.com/madisyn.shanahan,0.4364290279,232.210.198.224,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n13026,5,292,2017-04-21 20:31:30,http://kihn.biz/lance_kozey,0.5051495200,217.248.98.50,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n13027,5,292,2017-03-12 12:50:56,http://labadie.net/misty.halvorson,0.4331065695,112.113.236.163,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n13028,5,292,2017-03-25 03:27:35,http://quitzon.co/amos.hahn,0.2705957444,98.224.36.142,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n13029,5,292,2017-01-21 23:24:01,http://heller.name/rowan,0.9804042040,203.207.12.89,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n13030,5,292,2017-02-07 06:41:26,http://howeoberbrunner.org/linwood.paucek,0.7902376272,179.146.66.171,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n13031,5,292,2017-05-30 16:53:58,http://raynor.co/carolyn_runolfon,0.3923046594,213.195.110.211,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n13032,5,292,2016-12-24 06:06:31,http://boyle.name/barney,0.9038312169,88.73.184.227,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n13033,5,292,2017-05-04 21:23:22,http://walkerchamplin.info/abbey_hansen,0.6775122425,54.155.185.238,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n13034,5,292,2017-01-20 05:04:31,http://mertz.name/gregg,0.7409434311,18.8.133.214,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n13035,5,292,2017-04-01 05:01:09,http://bernhard.co/otis_harber,0.8469362508,178.103.139.31,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n13036,5,292,2017-04-10 08:06:41,http://schaden.co/darrin,0.9443850890,75.188.235.6,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n13037,5,292,2017-03-17 19:18:43,http://roobbeahan.net/beryl_friesen,0.7490845098,90.15.82.19,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n13038,5,292,2017-02-21 03:15:33,http://hartmannmckenzie.co/deron_glover,0.1000497029,164.7.77.213,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n13039,5,292,2017-05-23 22:28:11,http://schroeder.org/lizzie,0.2962046598,212.195.95.153,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n13040,5,292,2017-04-05 09:43:55,http://mayerteichmann.net/mark.conroy,0.5631642105,164.70.16.27,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n13041,5,292,2017-04-22 13:40:42,http://kohler.info/bailee_hegmann,0.1486229725,25.235.71.189,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n13042,5,292,2017-04-19 23:27:26,http://schultz.info/jonatan,0.2694945418,254.248.251.226,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n13043,5,292,2017-04-29 21:22:44,http://macejkovicbernier.org/genoveva,0.3296009372,78.66.80.130,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n13044,5,292,2017-03-15 18:35:24,http://hilll.net/jamaal.rohan,0.1817401536,227.210.146.214,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n13045,5,292,2017-06-07 15:31:00,http://wehner.name/nicklaus,0.1779788538,188.169.92.181,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n13046,5,292,2017-06-01 03:44:18,http://homenick.biz/bill,0.5606071360,73.68.78.122,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n13047,5,292,2017-04-27 10:01:42,http://kihn.co/judge_satterfield,0.5413843636,218.214.30.140,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n13048,5,292,2017-04-28 14:04:48,http://franecki.info/virginie,0.9511857796,42.58.41.21,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n13049,5,292,2017-06-04 15:44:37,http://bartoletti.io/michale.cormier,0.1938801298,51.57.159.193,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n13050,5,292,2017-05-21 21:36:45,http://haag.name/ola_hahn,0.0381020946,105.166.194.111,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n13051,5,292,2017-02-07 00:41:12,http://beatty.io/justine,0.3867543718,249.115.165.72,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n13052,5,292,2017-03-24 06:17:02,http://farrellreichert.com/caie.bartell,0.3193526202,181.116.158.232,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n13053,5,292,2017-05-29 22:44:51,http://hayes.biz/tina,0.2285871054,71.80.232.252,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n13054,5,292,2017-03-18 05:53:26,http://zulauf.co/misty,0.8093256506,168.74.127.49,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n13055,5,292,2017-04-18 15:45:34,http://feilhuels.com/gust,0.9804736886,123.243.222.121,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n13056,5,293,2017-02-17 22:51:59,http://heelwintheiser.io/stacey.rodriguez,0.4441187659,169.24.129.166,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n13057,5,293,2017-03-26 18:15:23,http://simonis.name/albert.oberbrunner,0.5918567905,46.109.171.211,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n13058,5,293,2017-03-18 15:18:46,http://cruickshank.biz/neoma,0.1064824851,70.200.246.5,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n13059,5,293,2017-02-06 20:51:12,http://considine.info/juwan,0.5526028717,89.83.155.162,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n13060,5,293,2017-05-25 01:53:21,http://upton.org/dangelo_casper,0.5829071748,227.100.110.219,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n13061,5,293,2017-01-27 06:25:46,http://parisian.name/jey,0.2481821642,18.179.200.114,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n13062,5,293,2017-04-21 21:05:53,http://roberts.net/chance_funk,0.1141835795,199.250.222.102,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n13063,5,293,2017-02-27 22:15:55,http://okeefe.name/kaitlin,0.4495180944,74.177.236.162,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n13064,5,293,2017-02-21 05:18:33,http://wehner.net/ramona_bechtelar,0.3925710094,124.52.68.180,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n13065,5,293,2017-03-30 05:45:47,http://mertz.info/don.williamson,0.8612276067,108.248.85.147,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n13066,5,293,2017-01-09 17:53:51,http://fay.io/glenna,0.2656969860,173.155.14.67,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n16026,6,361,2016-12-24 12:09:05,http://kertzmanncasper.net/arturo.harris,,68.254.58.157,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n16027,6,361,2017-02-26 20:15:56,http://mosciski.name/nelson,,161.131.116.199,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n16028,6,361,2017-04-25 18:12:21,http://wehner.org/carey.dare,,19.208.58.37,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n16029,6,361,2017-01-20 18:54:30,http://parisiangaylord.name/cordie,,212.42.107.135,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n16030,6,361,2017-01-19 11:11:06,http://ohara.io/paris,,42.24.50.112,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n16031,6,361,2017-02-18 01:35:02,http://conn.io/bill,,36.111.218.159,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n16032,6,361,2016-12-28 00:18:32,http://west.org/barrett_hilpert,,244.133.70.218,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n16033,6,361,2017-05-04 18:21:37,http://marquardtschaefer.com/madisen.hackett,,233.134.209.182,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n16034,6,361,2017-04-01 13:11:51,http://mueller.org/elwyn.gibson,,14.179.128.90,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n16035,6,362,2017-01-29 11:43:38,http://brakusmetz.com/coleman,,22.179.155.235,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n16036,6,362,2017-03-27 23:47:41,http://hodkiewicz.name/eriberto,,164.113.234.175,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n16037,6,362,2016-12-25 14:21:52,http://trantowgutkowski.org/denis.mann,,101.73.124.101,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n16038,6,362,2017-01-14 04:28:36,http://fahey.io/lourdes_goldner,,178.104.109.185,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n16039,6,362,2017-04-04 09:49:42,http://daniel.name/daisy_dickens,,95.146.188.205,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n16040,6,362,2017-01-09 13:12:36,http://dachschulist.biz/amara_jones,,61.99.151.30,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n16041,6,362,2017-03-02 07:31:44,http://greenfelder.name/thea.dibbert,,18.195.88.121,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n16042,6,362,2017-02-06 11:39:14,http://prohaska.io/monty.swaniawski,,79.123.241.58,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n16043,6,362,2017-05-26 19:04:35,http://witting.co/georgianna,,109.112.206.59,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n16044,6,362,2017-06-07 06:52:58,http://oconnellmcclure.org/kelsie_haag,,184.143.135.114,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n16045,6,362,2017-04-06 06:50:56,http://mcclure.com/ernest,,208.233.186.102,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n16046,6,362,2017-05-29 07:56:56,http://klocko.biz/sedrick_stracke,,187.138.58.219,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n16047,6,362,2016-12-16 23:21:53,http://lakinjast.org/prince_friesen,,192.161.241.153,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n16048,6,362,2017-06-13 17:21:41,http://hilll.io/geovanny.pacocha,,96.251.211.60,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n16049,6,362,2017-04-13 14:39:55,http://witting.net/haleigh_farrell,,209.149.148.250,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n16050,6,362,2017-02-28 06:24:03,http://reinger.io/kristy,,3.149.187.130,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n16051,6,362,2017-06-06 17:08:25,http://kuvalis.com/norwood,,228.233.113.6,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n16052,6,362,2016-12-22 17:12:01,http://ferry.net/annamae.renner,,62.82.184.3,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n16053,6,362,2017-02-03 10:42:05,http://rogahn.org/william,,10.84.202.177,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n16054,6,362,2017-04-10 15:47:57,http://boyle.com/marta,,127.248.215.175,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n16055,6,362,2016-12-27 12:05:10,http://borer.org/gerard.glover,,151.112.147.227,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n16056,6,362,2017-06-09 07:35:02,http://rosenbaum.com/lavern_gusikowski,,70.217.74.156,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n16057,6,363,2017-04-15 01:58:14,http://greenkuhn.co/tom,,134.8.110.230,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n16058,6,363,2016-12-15 10:19:37,http://mcdermott.com/floie_heaney,,145.20.107.44,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n16059,6,363,2017-02-25 17:43:48,http://ward.com/sydni_kub,,72.249.214.41,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n16060,6,363,2017-03-20 14:58:16,http://vonruedenabbott.info/lonny,,108.28.24.204,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n16061,6,363,2017-01-18 10:30:11,http://walker.co/eldridge,,199.191.169.77,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n16062,6,363,2017-05-18 23:23:58,http://mayer.net/heidi_wuckert,,65.12.166.72,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n16063,6,363,2017-03-06 12:37:07,http://cartwright.info/robb_olson,,117.33.147.202,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n16064,6,363,2016-12-21 08:17:49,http://strosin.co/caandre,,38.110.110.108,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n16065,6,363,2017-01-16 18:20:12,http://schamberger.io/adan,,8.9.215.61,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n16066,6,363,2017-05-25 18:10:24,http://schoen.co/zack_wunsch,,116.193.180.201,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n16067,6,363,2017-03-15 12:18:47,http://zulauf.io/dejuan_jacobi,,18.185.175.133,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n16068,6,363,2017-05-18 19:54:17,http://gleichner.org/eric,,207.18.157.43,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n16069,6,363,2017-03-04 16:04:29,http://damorekuhic.org/catharine_johnston,,37.155.155.243,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n16070,6,363,2017-04-20 15:36:59,http://fisher.co/darlene,,57.58.112.7,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n16071,6,363,2017-05-31 02:20:08,http://conroy.info/brenda.harvey,,100.63.206.87,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n16072,6,363,2017-05-16 12:41:36,http://swaniawskisanford.com/alexie,,177.41.161.59,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n16073,6,363,2016-12-15 23:51:11,http://macgyver.io/chanel.schimmel,,29.175.119.115,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n16074,6,363,2017-03-15 16:33:56,http://strosin.com/ruby.schinner,,36.35.38.128,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n16075,6,363,2017-06-10 09:03:32,http://leffler.net/alexandra.kulas,,99.85.112.98,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n16076,6,363,2017-04-11 20:16:08,http://cronin.io/thora,,152.210.235.21,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n16077,6,363,2017-03-21 02:50:36,http://quitzon.name/henri.keeling,,41.236.179.232,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n16078,6,363,2017-01-10 12:24:17,http://kilback.com/lottie.kozey,,41.228.94.148,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n16079,6,363,2016-12-21 13:33:23,http://padberg.com/eva,,209.66.113.241,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n16080,6,363,2017-06-05 07:38:27,http://kohler.com/christ_emard,,31.217.197.53,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n16081,6,363,2017-01-16 23:43:17,http://gusikowskishields.biz/alexzander.kohler,,4.172.224.221,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n3215,2,70,2017-01-26 09:23:10,http://caingorczany.io/lawson.kerluke,0.6710299662,168.233.129.212,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n3216,2,70,2017-02-23 11:03:43,http://schultz.biz/hattie_kuhn,0.1095510261,188.86.81.231,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n3217,2,70,2016-12-17 16:02:30,http://herzoggottlieb.io/jaiden,0.6279733399,30.213.78.148,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n3218,2,70,2017-05-05 15:42:37,http://rempel.net/malvina,0.5794572574,127.238.105.202,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n3219,2,70,2017-01-09 23:48:43,http://maggio.info/waylon,0.4083953369,252.158.185.237,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n3220,2,71,2016-12-27 23:51:13,http://sauerhackett.name/cody.hodkiewicz,0.5017354407,98.109.132.216,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n3221,2,71,2017-04-04 22:05:27,http://wilkinson.co/valentine.hane,0.6171185063,83.47.192.141,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n3222,2,71,2017-03-26 02:36:52,http://cremin.org/audrey.kirlin,0.7640025030,107.254.95.192,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n3223,2,71,2017-06-07 19:19:07,http://nadermayert.com/verla_marquardt,0.2587349674,71.235.222.120,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n3224,2,71,2016-12-31 03:33:16,http://ohara.com/gino,0.1150013208,190.203.67.98,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n3225,2,71,2017-03-23 23:52:48,http://vandervort.co/liana.waelchi,0.0635390583,238.232.237.79,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n3226,2,71,2017-04-08 18:11:47,http://stroman.name/marley_kunze,0.3694944091,118.86.196.186,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n3227,2,71,2017-04-04 08:06:42,http://halvorson.net/cynthia,0.8831656825,96.27.20.68,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n3228,2,71,2017-02-03 09:01:55,http://nikolaus.org/liam,0.4989752548,98.122.138.223,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n3229,2,71,2017-03-15 22:53:14,http://blandamonahan.io/orville,0.5240664809,190.75.24.135,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n3230,2,71,2017-05-26 15:24:40,http://johnston.com/walker_collier,0.7856001078,41.8.196.152,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n3231,2,71,2017-04-29 12:56:32,http://moen.io/herman.olson,0.2390327218,179.231.186.232,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n3232,2,71,2017-05-15 16:51:20,http://rutherfordwilliamson.io/sierra_runolfon,0.9108688192,172.240.203.42,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n3233,2,71,2017-04-30 19:10:03,http://berge.io/kirstin,0.5207004752,64.32.201.145,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n3234,2,71,2017-05-22 04:55:46,http://mcglynn.co/kathryne_oconner,0.9044288915,47.134.182.205,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n3235,2,71,2017-03-03 04:01:32,http://gottlieb.io/dashawn,0.5924953658,96.16.109.58,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n3236,2,71,2017-04-04 02:37:41,http://stokes.info/effie,0.1326918827,230.83.208.194,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n3237,2,71,2017-03-14 19:04:58,http://cainkoelpin.biz/omer,0.5205022193,216.60.151.110,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n3238,2,71,2017-02-06 03:00:57,http://kerluke.co/korbin,0.2115641982,126.49.144.57,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n3239,2,71,2017-01-10 08:06:56,http://mitchell.name/maxie,0.1299830812,150.102.58.84,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n3240,2,71,2017-05-27 11:26:23,http://oreilly.net/samir_bashirian,0.8362010460,226.160.65.59,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n3241,2,71,2017-01-19 00:15:17,http://okeefepurdy.org/anthony.eichmann,0.5766176293,197.23.219.26,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n3242,2,71,2017-03-27 15:48:20,http://lehnerhodkiewicz.com/forest,0.6647326079,4.196.209.70,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n3243,2,71,2017-03-27 00:30:57,http://crooksboehm.info/charlotte,0.3098527141,52.84.112.250,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n3244,2,71,2017-02-12 03:19:34,http://hermann.org/dallin,0.9309141605,85.141.240.236,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n4175,2,91,2017-02-25 15:42:48,http://volkman.biz/anahi,,99.119.209.241,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n3245,2,71,2017-06-01 21:57:20,http://beckerherman.co/josh.kuhn,0.4660185347,18.198.145.27,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n3246,2,71,2017-01-19 19:32:09,http://keeblerschulist.io/donavon.hoppe,0.2276658571,118.144.205.164,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n3247,2,71,2017-01-22 14:49:07,http://champlin.biz/caterina_okon,0.1620862099,81.39.43.128,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n3248,2,71,2017-04-15 00:37:42,http://considine.io/paige.eichmann,0.9456620378,156.68.107.19,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n3249,2,71,2017-05-25 23:06:00,http://rolfsonborer.co/alia,0.0477833516,99.74.204.105,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n3250,2,71,2017-02-15 09:02:28,http://auer.co/claudine,0.3661682972,133.50.220.71,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n3251,2,71,2017-01-14 11:09:19,http://johns.info/camren_schmeler,0.1504238984,162.223.193.42,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n3252,2,71,2017-04-21 07:18:37,http://feeney.org/trent.tremblay,0.1978312241,69.55.203.19,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n3253,2,71,2016-12-22 07:07:42,http://kuphal.net/pamela,0.2927983392,52.79.219.177,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n3254,2,71,2017-06-13 14:07:19,http://mayerbogisich.io/harvey,0.8264181827,254.108.128.176,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n3255,2,71,2017-03-11 18:04:27,http://howe.io/myriam.thiel,0.6233006005,187.77.145.16,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n3256,2,71,2017-06-04 17:31:23,http://schmidtokuneva.name/blair.cole,0.3115824973,80.136.32.35,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n3257,2,71,2017-03-10 18:50:08,http://adams.biz/ronny.lubowitz,0.8430832072,162.84.252.232,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n3258,2,71,2017-04-19 15:17:52,http://murray.net/mazie,0.9586781246,114.55.229.35,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n3259,2,71,2017-02-18 09:05:42,http://bogan.net/agnes,0.2396434865,120.112.133.63,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n3260,2,71,2017-06-08 09:02:51,http://hodkiewicz.name/clair,0.4347797367,211.158.26.121,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n3261,2,71,2017-02-17 19:30:41,http://nikolaus.net/lyla,0.0486440341,126.152.178.159,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n3262,2,71,2017-01-12 19:01:23,http://wymangrimes.io/janet_brown,0.5517823673,222.153.206.82,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n3263,2,71,2017-01-26 14:55:15,http://howe.name/mitchel,0.5127387826,90.17.223.189,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n3264,2,71,2017-03-10 03:22:56,http://robelfunk.io/chanel_wilkinson,0.7647385105,132.96.173.204,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n3265,2,71,2017-06-06 17:18:17,http://hirthe.name/deon_block,0.8125045726,71.197.76.182,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n10149,4,228,2017-01-03 18:34:04,http://welchwisozk.net/annabell.legros,0.7298383411,78.54.193.53,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n10150,4,228,2017-04-10 09:05:31,http://moen.net/cole.smitham,0.0279983687,19.167.40.193,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n10151,4,228,2017-02-12 23:29:27,http://bodebernier.name/robin,0.1175456608,177.90.253.110,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n10152,4,228,2017-06-08 06:53:24,http://conroy.co/ruth,0.2812274434,249.161.218.225,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n10153,4,228,2017-04-29 19:30:47,http://nitzsche.org/pietro.kohler,0.6247987616,24.9.56.162,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n10154,4,228,2017-04-10 06:49:44,http://hills.name/guiseppe,0.7074445237,190.111.201.46,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n10155,4,228,2016-12-31 02:20:50,http://friesencummerata.com/flavie,0.9729885963,188.14.83.232,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n10156,4,228,2017-01-12 20:30:55,http://marvinstark.co/orlo,0.9051712551,24.192.177.72,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n10157,4,228,2017-02-14 20:39:10,http://streichfeeney.biz/esmeralda_fahey,0.1902308829,102.163.59.43,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n10158,4,228,2017-05-20 00:45:16,http://littel.biz/marcos,0.0369681729,215.134.136.177,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n10159,4,228,2017-04-19 04:28:16,http://smitham.name/monty_heathcote,0.4633078601,191.23.250.248,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n10160,4,228,2017-03-05 10:35:19,http://bechtelar.io/roxane,0.5337310195,242.226.139.68,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n10161,4,228,2017-05-25 06:50:48,http://lakin.biz/maybell,0.4334823649,233.185.78.206,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n10162,4,229,2017-04-24 09:22:03,http://schmitt.name/elian_mccullough,0.8144799267,221.210.92.150,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n10163,4,229,2017-06-02 17:42:01,http://fay.org/marjolaine.cole,0.2334119532,126.82.110.195,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n10164,4,229,2017-04-20 07:34:49,http://senger.biz/grover,0.1988847070,98.16.192.34,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n10165,4,229,2017-04-25 15:19:51,http://wintheiser.name/haven,0.1697069636,41.170.25.227,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n10166,4,229,2017-04-18 01:08:30,http://runolfsdottirstamm.co/carter,0.5805796231,173.46.94.108,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n10167,4,229,2017-04-12 04:22:15,http://carroll.name/stephanie.kiehn,0.7707336185,43.137.171.195,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n10168,4,229,2017-01-04 00:38:15,http://handhaag.info/tavares.prosacco,0.0390443831,36.197.20.145,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n10169,4,229,2017-05-29 05:57:38,http://carroll.io/carolina.bartell,0.8175035244,116.41.45.142,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n10170,4,229,2017-01-10 06:49:28,http://runte.com/nella_kunde,0.0529022773,204.124.177.41,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n10171,4,229,2016-12-19 14:16:27,http://rutherford.co/sharon,0.1259322576,44.48.67.227,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n10172,4,229,2016-12-28 00:22:48,http://hyatt.io/nedra.ortiz,0.8455079937,152.19.60.222,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n10173,4,229,2017-01-19 13:07:50,http://howell.net/claude.brown,0.9856821626,140.61.77.121,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n10174,4,229,2017-03-21 14:42:25,http://riceking.net/murray.leannon,0.4212328349,183.250.112.84,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n10175,4,229,2016-12-25 13:18:11,http://schowalter.biz/kasandra_predovic,0.8581680472,203.165.52.42,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n10176,4,229,2017-03-16 00:52:14,http://gislason.co/octavia,0.1393364495,222.45.21.236,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n10177,4,229,2017-02-27 21:12:21,http://brakusmarquardt.info/tatum,0.8365316956,189.7.191.97,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n10178,4,229,2017-05-17 03:09:31,http://prosaccocummings.net/prudence,0.5655096047,48.178.230.192,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n10179,4,229,2017-04-01 20:46:50,http://wiegand.net/alvah.eichmann,0.8684992353,222.57.158.87,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n10180,4,229,2017-06-06 03:28:47,http://kohler.name/zoey,0.1839769661,46.19.254.9,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n10181,4,229,2017-04-11 03:33:54,http://gaylorddamore.name/maximillia,0.2770437907,145.5.157.27,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n10182,4,229,2017-06-10 09:11:21,http://goldner.info/muhammad_little,0.5261174075,194.206.135.247,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n10183,4,229,2017-03-08 20:53:04,http://will.org/ahmad,0.2028454119,146.63.12.246,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n10184,4,229,2017-05-22 00:11:37,http://okuneva.name/cordie.stoltenberg,0.6293621060,2.144.111.107,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n10185,4,229,2017-01-18 05:38:10,http://lindgrenhayes.co/chesley_reinger,0.4992063040,134.98.190.242,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n10186,4,229,2017-06-05 02:55:26,http://prosacco.name/nikki.padberg,0.3508857279,133.39.72.190,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n10187,4,229,2017-05-22 16:15:59,http://gleichner.org/garrison,0.3230640977,31.119.228.223,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n10188,4,229,2017-05-20 06:00:33,http://jastbotsford.name/elody,0.4988829771,136.222.20.139,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n10189,4,229,2016-12-26 07:59:31,http://ryan.com/angelica,0.8377964741,19.43.5.92,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n10190,4,229,2017-05-02 12:27:35,http://ebert.com/gianni.oconner,0.0714369048,132.15.42.200,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n10191,4,229,2017-05-26 09:40:22,http://mueller.io/doug_feeney,0.6918321415,120.2.15.157,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n10192,4,229,2017-03-23 16:22:28,http://farrell.biz/corene_medhurst,0.1550340937,147.2.241.125,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n10193,4,229,2017-06-08 08:21:46,http://carterblock.net/kurtis,0.7826291896,5.40.155.163,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n10194,4,229,2017-02-21 15:09:34,http://hamill.biz/cordia,0.2283176811,117.57.207.240,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n10195,4,229,2017-02-07 10:01:23,http://windlerschinner.biz/cordell_jerde,0.0195087870,61.229.252.83,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n10196,4,229,2017-03-17 14:54:37,http://rueckerfay.io/judd_mcclure,0.4042943187,235.215.198.129,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n10197,4,229,2017-03-09 03:47:46,http://reynolds.org/filomena_bode,0.2389422022,99.166.31.125,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n10198,4,229,2017-02-23 04:40:36,http://jakubowski.org/margret,0.3497965016,103.63.22.112,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n10199,4,229,2017-06-02 11:48:46,http://bogan.net/shany,0.7711693009,156.202.35.163,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n7160,3,159,2017-02-13 21:07:47,http://bruenhuel.name/leopoldo_windler,0.1097070402,180.231.118.221,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n7161,3,159,2017-05-06 07:10:06,http://wardtorp.co/pietro_glover,0.6763080544,95.90.80.81,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n7162,3,159,2017-03-06 11:47:08,http://ebertbeatty.net/macie,0.7291401707,37.97.40.195,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n7163,3,159,2017-02-11 23:26:15,http://rath.name/bridgette,0.4823116446,241.22.212.192,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n7164,3,159,2016-12-27 17:15:26,http://hillsheller.io/coby.watsica,0.9534398448,221.121.232.235,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n7165,3,159,2017-05-14 12:39:51,http://hilpert.co/christiana,0.6167481923,178.25.94.38,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n7166,3,159,2017-03-23 22:16:16,http://steubergoldner.org/jedidiah_wisozk,0.2700860470,221.162.58.50,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n7167,3,159,2017-06-02 10:31:46,http://legros.org/dillon,0.6922706699,162.162.39.6,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n7168,3,159,2017-02-21 22:53:15,http://bernier.net/leon,0.2497714046,80.3.239.112,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n7169,3,159,2017-01-03 02:53:16,http://hills.co/tabitha.ankunding,0.6055786532,121.41.208.49,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n7170,3,159,2017-04-06 16:35:25,http://dickinson.biz/annabell,0.8899835480,154.185.248.67,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n7171,3,159,2017-01-06 13:46:52,http://smith.org/moshe,0.6392432823,228.12.216.110,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n7172,3,159,2017-04-26 18:18:45,http://tillmanreichert.co/jeramie,0.2353707539,170.109.231.13,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n7173,3,159,2017-01-04 10:09:53,http://schulistromaguera.name/garland_wiza,0.5798975538,165.215.98.227,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n7174,3,159,2017-04-26 23:18:13,http://brownjohnston.info/talon,0.9461407825,4.46.180.16,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n7175,3,159,2017-05-15 07:17:20,http://reichert.co/hank,0.5445757128,228.163.73.67,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n7176,3,159,2017-03-27 08:03:26,http://huel.co/salvatore_oreilly,0.5898897901,104.24.167.187,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n7177,3,159,2017-05-04 23:11:12,http://nienow.org/marcel,0.8826730329,20.123.11.227,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n7178,3,159,2017-03-29 11:34:32,http://rodriguez.com/karina,0.1190058829,40.98.135.229,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n7179,3,159,2017-02-18 16:20:39,http://monahanmills.net/jeremie,0.5932551902,165.19.74.123,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n7180,3,159,2017-01-20 20:40:47,http://schmidt.name/johathan.zulauf,0.6676961550,216.235.52.17,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n7181,3,159,2017-05-14 20:02:00,http://borerkling.org/edgardo,0.3092586253,46.179.97.4,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n7182,3,159,2017-03-12 03:41:01,http://gislason.org/mateo,0.5062100076,90.245.97.92,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n7183,3,159,2017-05-09 20:34:26,http://grimes.co/lura.yundt,0.1372168524,144.15.204.224,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n7184,3,159,2017-03-18 22:35:48,http://langosh.io/lyda.schmitt,0.9423699509,134.223.88.177,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n7185,3,160,2017-05-11 22:04:13,http://wehner.com/anita,0.0859580585,129.198.79.158,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n7186,3,160,2017-03-03 08:02:34,http://rempeldietrich.info/carolyn,0.2671684347,92.14.23.15,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n7187,3,160,2017-05-19 16:04:38,http://kling.co/aida.hackett,0.2715481893,213.166.114.9,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n7188,3,160,2017-05-31 20:10:32,http://ankunding.net/jovani,0.9118092220,193.16.177.10,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n7189,3,160,2017-01-13 12:58:02,http://halvorsonrempel.net/abel,0.4098406340,206.224.109.175,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n7190,3,160,2016-12-25 15:56:07,http://crona.io/camilla,0.8173357970,125.109.209.76,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n7191,3,160,2017-03-17 11:08:52,http://kautzer.name/major.altenwerth,0.2484386938,189.243.247.56,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n7192,3,160,2017-05-22 13:44:44,http://nolanhaag.net/marianne.robel,0.8908008564,152.117.88.220,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n7193,3,160,2017-03-29 22:09:44,http://schimmel.org/caleb_crona,0.0477542464,100.144.184.32,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n7194,3,160,2017-04-03 21:11:10,http://lueilwitz.io/beaulah_weber,0.8721864004,69.26.196.182,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n7195,3,160,2017-03-08 22:34:53,http://bahringer.biz/rodger,0.5342937769,209.152.25.104,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n7196,3,160,2017-04-22 13:03:02,http://marvin.biz/dane_mann,0.7306232231,53.28.189.253,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n7197,3,160,2016-12-26 05:24:58,http://ferrybergnaum.name/jaeden,0.3370518861,75.58.247.29,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n7198,3,160,2017-03-27 01:53:10,http://mclaughlin.info/earnestine,0.5568099396,11.65.223.117,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n7199,3,160,2016-12-29 12:52:19,http://gloverdietrich.net/warren.lockman,0.5295855118,170.135.246.41,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n7200,3,160,2017-04-27 18:39:53,http://labadiecorwin.info/luella_herzog,0.6763142366,150.127.227.5,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n7201,3,160,2017-02-25 09:11:46,http://auer.com/selena,0.6596328654,56.15.33.20,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n7202,3,160,2017-05-28 14:08:46,http://lubowitzlangworth.com/coby,0.5063715352,157.151.207.254,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n7203,3,160,2017-04-08 10:47:45,http://simonis.org/moshe.kunze,0.4257943447,155.171.227.238,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n7204,3,160,2017-03-27 22:15:52,http://gleasonvon.info/maxwell,0.8763815474,99.86.84.22,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n7205,3,160,2017-02-25 00:37:59,http://ryanrodriguez.com/kenton_bode,0.4506237499,16.133.164.251,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n7206,3,160,2017-05-11 19:19:42,http://reichellowe.com/hellen_raynor,0.6598220828,173.151.206.205,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n7207,3,160,2017-03-30 01:05:09,http://bergnaum.com/lucio,0.9793735429,247.200.149.130,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n7208,3,160,2017-02-10 18:52:30,http://daniel.info/fidel_mitchell,0.8286475997,66.51.28.173,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n7209,3,160,2017-01-10 12:14:15,http://millawayn.info/ernie_maggio,0.0227986185,221.168.99.80,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n7210,3,160,2016-12-24 09:46:15,http://hirthe.net/alexys.torphy,0.1247856988,35.92.168.141,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n7211,3,160,2017-02-15 07:47:54,http://zboncakwalker.biz/margot,0.8092878106,118.198.76.78,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n13067,5,293,2017-02-13 09:55:19,http://harris.org/alex.blanda,0.0984958663,129.75.103.157,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n13068,5,293,2017-05-20 10:10:48,http://langworthfahey.org/treie,0.1884070382,33.92.169.122,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n13069,5,293,2017-04-24 02:26:46,http://krajciktorp.net/emma,0.7473057686,59.92.114.137,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n13070,5,293,2017-01-26 23:35:01,http://botsford.io/jonathon.wiegand,0.9215855254,152.220.149.82,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n13071,5,293,2017-06-11 20:22:51,http://legros.info/norbert,0.0387347448,245.219.64.215,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n13072,5,293,2017-05-06 08:11:24,http://hoppe.co/taryn_johns,0.2515641707,254.224.157.85,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n13073,5,293,2017-03-29 14:06:45,http://morarkshlerin.org/larue,0.1772712246,184.204.98.232,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n13074,5,293,2017-05-09 11:50:01,http://bruen.org/frieda,0.9339929151,252.165.244.200,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n13075,5,293,2017-03-20 17:03:46,http://casper.com/marcelo,0.9484389505,51.191.73.143,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n13076,5,293,2017-06-10 20:58:46,http://ratkekub.name/pasquale.roob,0.5509251266,223.107.107.16,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n13077,5,293,2017-05-05 17:50:42,http://lebsack.net/emilia_lubowitz,0.5796893394,42.52.107.46,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n13078,5,293,2017-05-28 16:19:15,http://hegmannrosenbaum.net/delpha,0.5868423358,100.143.224.236,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n13079,5,293,2017-04-05 04:49:10,http://zemlak.biz/alexandrine,0.3163519602,215.67.143.53,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n13080,5,293,2017-03-16 22:38:28,http://rosenbaum.name/melia_torp,0.5505986042,162.21.90.8,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n13081,5,293,2017-03-31 07:14:55,http://wiza.io/bridget.boyle,0.1493724429,110.144.181.149,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n13082,5,293,2017-03-13 01:29:54,http://muellerrolfson.co/arlene,0.8404533560,44.26.85.118,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n13083,5,293,2017-01-12 03:48:31,http://corwinkunde.biz/oscar,0.9629320205,249.32.210.19,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n13084,5,293,2017-03-24 08:52:34,http://greenholt.co/misael.stroman,0.4937035013,165.230.123.64,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n13085,5,293,2017-03-20 06:20:07,http://mayer.net/gina,0.9699452488,78.51.223.31,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n13086,5,293,2017-04-03 00:37:10,http://halvorsonmacejkovic.io/murray_kozey,0.3501627276,78.24.244.164,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n13087,5,293,2017-03-24 20:07:00,http://nolan.net/florencio,0.4262637829,181.167.95.58,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n13088,5,294,2017-03-20 05:55:58,http://stark.info/shanel,0.3105251338,236.82.50.21,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n13089,5,294,2017-04-18 00:15:54,http://schroeder.co/lindsey_keebler,0.8185599990,142.225.175.106,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n13090,5,294,2017-02-11 10:35:55,http://hamillraynor.org/phoebe_wolff,0.0049951172,50.95.213.184,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n13091,5,294,2017-04-03 15:45:47,http://goodwinledner.org/minerva,0.8630669141,211.67.57.21,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n13092,5,294,2017-04-13 13:46:57,http://swift.io/jaqueline,0.5878224067,181.193.198.91,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n13093,5,294,2017-01-29 15:43:06,http://tromp.org/aiyana.pfannerstill,0.3836138661,200.118.117.191,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n13094,5,294,2017-01-10 09:52:15,http://satterfieldtillman.io/kiara,0.7365877219,172.14.64.196,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n13095,5,294,2017-06-09 03:29:33,http://pouroshalvorson.biz/christina,0.3711972680,87.173.144.63,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n13096,5,294,2017-02-24 09:40:07,http://weimannschoen.info/wayne,0.9160825282,250.172.221.126,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n13097,5,294,2017-05-04 12:47:56,http://mayer.org/will_fahey,0.4709717897,224.215.61.186,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n13098,5,294,2017-03-19 20:09:34,http://dibbertblock.org/lolita.rath,0.3350012719,115.73.167.61,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n13099,5,294,2017-01-11 09:27:45,http://kiehn.com/giovanna,0.7916575896,174.71.138.126,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n13100,5,294,2017-03-05 02:46:52,http://hills.io/forest.kiehn,0.9407850408,154.145.141.76,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n13101,5,294,2017-03-16 15:30:18,http://hayes.org/laurie,0.3943755142,228.106.216.18,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n13102,5,294,2017-03-15 07:13:45,http://hansenrunolfon.name/anna,0.3905482201,68.113.202.8,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n13103,5,294,2017-02-12 02:06:24,http://boyer.name/hipolito.koepp,0.0481377739,43.163.48.25,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n13104,5,294,2016-12-26 03:12:45,http://frami.co/dario_bednar,0.1943778112,59.8.98.88,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n13105,5,294,2017-02-02 16:22:06,http://christiansen.com/alvah_king,0.6604624182,156.75.214.250,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n13106,5,294,2016-12-18 09:33:19,http://gorczanyhuel.com/emilio.champlin,0.9628269960,250.155.133.59,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n13107,5,294,2017-02-15 21:10:33,http://emmerichkoelpin.com/danyka_romaguera,0.6068931713,252.160.213.92,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n13108,5,294,2016-12-25 14:46:03,http://jonesmoriette.org/brandyn_barrows,0.6937975194,170.81.198.145,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n13109,5,294,2017-04-18 17:21:43,http://bailey.io/marisa.bins,0.9222917131,51.225.109.83,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n13110,5,294,2017-05-06 15:33:46,http://kutch.info/marcel_hauck,0.7630032412,100.100.30.254,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n13111,5,294,2017-05-09 14:32:53,http://klein.net/brooke_morar,0.1937616715,32.37.65.138,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n13112,5,294,2017-02-23 10:35:21,http://thiel.info/jace,0.4289262914,181.87.235.163,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n13113,5,294,2017-06-02 17:53:19,http://shieldsgrant.io/samara,0.0259831335,177.201.92.215,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n13114,5,294,2016-12-27 21:27:01,http://crona.com/jamel.hodkiewicz,0.7980229606,33.190.152.130,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n13115,5,294,2017-01-30 12:09:34,http://hellerpagac.org/tobin,0.9813192684,147.210.6.16,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n13116,5,294,2017-04-05 23:25:50,http://dibbert.name/carolina,0.8965045517,230.212.214.129,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n13117,5,294,2017-01-08 23:00:03,http://feest.io/iac_daugherty,0.5950305740,196.35.215.186,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n16082,6,363,2016-12-28 04:55:09,http://runte.com/jamey_reichert,,81.247.13.39,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n16083,6,363,2017-03-02 02:21:50,http://stammprohaska.org/jason_nikolaus,,39.140.54.240,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n16084,6,363,2017-03-23 06:39:29,http://kochwolff.co/francesco_beatty,,23.154.30.32,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n16085,6,363,2017-03-06 17:41:57,http://bahringer.biz/tristian,,153.157.229.85,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n16086,6,363,2017-06-05 13:43:47,http://simonis.biz/kellen.altenwerth,,74.38.34.190,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n16087,6,363,2017-05-19 02:40:24,http://gusikowski.co/jeramie.kling,,141.168.198.2,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n16088,6,363,2017-01-27 11:58:03,http://bogansipes.biz/treva_stokes,,109.64.133.53,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n16089,6,363,2016-12-13 11:02:27,http://webermacgyver.biz/ariel,,131.242.241.35,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n16090,6,363,2017-02-17 00:13:36,http://watsicasanford.name/everette.kertzmann,,42.96.151.136,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n16091,6,363,2017-01-26 18:15:40,http://murrayfeil.biz/marlin.spinka,,90.219.235.249,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n16092,6,364,2017-03-13 09:46:48,http://towne.biz/ryleigh,,219.3.30.186,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n16093,6,364,2017-06-03 06:43:48,http://harvey.net/rylee,,187.208.155.201,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n16094,6,364,2016-12-22 23:58:22,http://gutkowski.org/candace_lemke,,78.134.125.206,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n16095,6,364,2017-01-10 05:53:58,http://monahan.net/andre.rogahn,,103.111.119.196,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n16096,6,364,2017-05-09 14:51:43,http://luettgen.name/levi.runolfon,,3.91.225.165,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n16097,6,364,2017-01-08 15:00:28,http://shanahan.info/theodora_nicolas,,26.32.5.220,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n16098,6,364,2017-01-18 02:28:19,http://kutch.info/kaia,,67.252.76.9,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n16099,6,364,2017-04-07 08:14:23,http://rodriguez.io/erich,,190.243.72.46,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n16100,6,364,2016-12-17 06:37:32,http://mertzzieme.biz/lonnie_kris,,120.34.12.185,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n16101,6,364,2017-05-28 02:05:46,http://heidenreich.name/andre,,81.25.134.232,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n16102,6,364,2017-02-19 15:10:00,http://barrows.org/ahmed,,21.98.194.172,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n16103,6,364,2017-03-26 06:38:28,http://jones.co/tyreek,,44.213.127.23,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n16104,6,364,2017-01-04 21:50:43,http://swift.com/albertha,,101.14.86.95,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n16105,6,364,2017-04-27 21:44:42,http://heidenreichoconnell.io/daniella,,200.74.75.208,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n16106,6,364,2017-01-24 21:14:58,http://huel.co/brayan_torphy,,216.110.55.154,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n16107,6,364,2017-04-25 00:15:42,http://auer.info/dorcas.hagenes,,100.143.13.90,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n16108,6,364,2017-03-30 13:29:03,http://kshlerinwillms.net/erna_bechtelar,,105.27.173.123,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n16109,6,364,2017-02-10 09:07:22,http://mcglynn.info/jazlyn_lynch,,75.237.135.247,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n16110,6,364,2017-01-13 08:59:46,http://westfeest.info/quinten_kris,,14.81.42.56,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n16111,6,364,2017-04-23 20:21:13,http://pollich.name/kade_dietrich,,142.192.159.100,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n16112,6,364,2017-03-25 18:38:52,http://smitham.co/pink_bayer,,220.119.183.240,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n16113,6,364,2017-05-16 01:35:45,http://greenholtchristiansen.net/imogene_huels,,242.92.134.170,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n16114,6,364,2017-03-19 21:36:33,http://altenwerth.co/david_prosacco,,128.106.149.160,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n16115,6,364,2017-04-12 14:29:48,http://nicolaenger.info/justine_kautzer,,183.171.103.52,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n16116,6,364,2016-12-20 10:05:49,http://beier.co/dulce.adams,,69.94.231.114,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n16117,6,364,2017-02-06 00:54:09,http://rutherford.com/brendan,,218.240.214.90,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n16118,6,364,2017-04-14 23:30:11,http://rohan.io/elliott,,40.171.166.104,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n16119,6,364,2017-06-10 03:05:34,http://sporer.org/meagan.mitchell,,118.89.232.159,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n16120,6,364,2017-05-19 02:32:35,http://price.info/destinee_schuster,,23.160.156.106,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n16121,6,364,2016-12-23 14:39:34,http://swift.info/ethel_kiehn,,7.84.193.193,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n16122,6,364,2016-12-18 21:57:01,http://kertzmannhammes.io/alexzander,,209.109.230.161,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n16123,6,364,2017-01-16 21:47:20,http://moore.org/zion,,155.223.21.174,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n16124,6,364,2017-05-26 07:41:25,http://marquardt.name/addison,,55.130.143.188,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n16125,6,364,2017-06-02 06:25:33,http://kirlin.name/floy,,55.9.135.106,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n16126,6,364,2017-04-09 22:58:15,http://renner.org/sydnie.schamberger,,220.218.130.3,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n16127,6,364,2017-05-07 20:31:10,http://schuppeluettgen.io/donald,,26.74.145.131,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n16128,6,364,2017-04-15 14:41:12,http://coleterry.org/amelie,,25.238.224.137,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n16129,6,364,2017-01-05 12:35:46,http://dach.co/sheila.labadie,,143.178.179.209,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n16130,6,364,2017-05-27 10:00:07,http://huel.co/alexandro.auer,,184.158.128.75,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n16131,6,364,2017-01-02 02:05:02,http://metz.name/jamaal_emard,,162.68.37.75,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n16132,6,364,2017-03-21 10:32:03,http://murazik.info/jasper,,188.132.163.190,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n16133,6,364,2017-01-11 20:50:19,http://murazik.co/geraldine,,237.20.139.15,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n16134,6,364,2017-01-29 21:50:33,http://herzog.org/adriel.weinat,,56.176.124.91,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n16135,6,364,2016-12-15 21:02:00,http://kulas.org/pansy.mohr,,12.40.189.30,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n16136,6,364,2017-01-18 00:01:26,http://senger.io/carole.krajcik,,49.176.236.126,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n3266,2,71,2017-03-28 02:04:16,http://rempel.co/eunice.windler,0.6391566473,12.28.231.182,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n3267,2,71,2017-05-28 18:16:19,http://zieme.info/malcolm,0.0336052790,88.7.201.103,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n3268,2,71,2017-01-23 05:49:45,http://jenkins.org/tamia,0.1427517969,125.6.35.238,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n3269,2,71,2017-02-10 08:42:56,http://schoen.name/king,0.3924669421,9.155.84.49,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n3270,2,72,2017-01-18 04:43:34,http://schmitt.biz/magdalena.reynolds,0.5146584949,100.153.162.194,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n3271,2,72,2017-04-15 18:04:14,http://hauck.io/gus,0.0933673965,157.29.190.27,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n3272,2,72,2017-03-17 20:52:54,http://kuvalis.org/anna,0.4406808695,36.176.159.210,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n3273,2,72,2017-05-09 14:32:57,http://buckridge.org/gayle,0.6397725698,80.111.77.70,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n3274,2,72,2017-05-21 20:47:44,http://sawayn.io/elisabeth.schuster,0.7477722450,28.12.228.164,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n3275,2,72,2016-12-25 04:02:14,http://jacobson.name/ruth,0.0867302231,169.244.74.103,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n3276,2,72,2017-05-31 10:19:29,http://labadie.io/chelsie,0.5137322247,206.136.224.222,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n3277,2,72,2017-02-14 10:16:42,http://beahankulas.co/bradford,0.1128547083,175.51.62.198,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n3278,2,72,2017-03-07 04:14:30,http://hilll.net/ova.green,0.5244510176,188.176.130.171,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n3279,2,72,2016-12-15 08:00:35,http://barton.io/leila,0.9723739081,83.181.47.51,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n3280,2,72,2017-02-04 14:30:08,http://walshbruen.com/gaylord_bradtke,0.1836845997,146.211.71.174,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n3281,2,72,2017-01-10 20:20:02,http://harris.name/kyleigh,0.9938335481,171.15.188.220,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n3282,2,72,2017-02-10 13:15:11,http://ward.net/charlotte.bergstrom,0.3086160407,173.32.180.4,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n3283,2,72,2017-02-26 07:07:22,http://macgyvermurphy.org/darren,0.6509816301,151.53.226.48,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n3284,2,72,2017-01-31 12:45:19,http://kunde.org/garry_larson,0.4068417133,14.29.229.209,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n3285,2,72,2017-05-13 01:18:45,http://runolfon.biz/nicole,0.0522700384,98.24.249.54,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n3286,2,72,2017-04-09 02:09:58,http://haag.biz/kory_runolfsdottir,0.1137819421,180.223.78.82,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n3287,2,72,2017-06-07 07:03:42,http://bahringerkaulke.net/lucy,0.2120947589,55.69.120.12,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n3288,2,72,2017-01-14 06:01:46,http://trantow.co/julia,0.6252401982,89.187.209.82,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n3289,2,72,2017-01-28 22:10:23,http://vandervort.info/sincere,0.9594850765,167.7.40.183,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n3290,2,73,2017-02-22 13:45:02,http://bernier.io/margie,0.4402075526,127.62.141.89,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n3291,2,73,2017-04-27 03:33:20,http://lowe.net/cayla_boehm,0.5802552046,23.228.236.126,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n3292,2,73,2017-02-16 07:35:05,http://lubowitzmurazik.com/yolanda,0.7643475197,251.14.2.216,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n3293,2,73,2017-03-22 15:20:18,http://batz.co/xzavier.heathcote,0.9593511680,32.179.175.95,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n3294,2,73,2016-12-20 02:10:08,http://cummerata.biz/beth,0.2271449926,98.43.74.238,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n3295,2,73,2017-04-05 21:37:30,http://ferrykiehn.net/alisha,0.0178964756,103.94.247.167,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n3296,2,73,2017-01-27 18:43:03,http://turnercollier.org/casandra,0.0694885468,6.48.155.115,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n3297,2,73,2016-12-26 13:25:03,http://dietrichweinat.co/stephen_strosin,0.8349198175,91.143.97.106,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n3298,2,73,2017-02-07 21:56:48,http://feil.info/raphael,0.6614312058,247.72.7.165,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n3299,2,73,2016-12-28 18:27:18,http://orn.name/pietro.goodwin,0.4649633682,79.170.234.66,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n3300,2,73,2016-12-16 14:47:00,http://stroman.net/lula.wiza,0.3136502613,179.151.32.168,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n3301,2,73,2017-02-21 09:01:20,http://predovic.org/mekhi.parisian,0.8899581913,171.86.227.52,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n3302,2,73,2017-03-17 02:42:37,http://pfeffer.name/richie,0.0113097917,111.133.203.167,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n3303,2,73,2017-03-27 12:06:31,http://damoreadams.com/rowland.beier,0.8226942708,201.189.136.201,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n3304,2,73,2017-05-06 06:10:53,http://lemkeboyle.net/bettie,0.1657922807,200.75.60.70,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n3305,2,73,2017-05-02 00:44:01,http://johns.biz/adrien_olson,0.0178767661,239.211.230.200,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n3306,2,73,2016-12-17 21:12:24,http://wisozkwilkinson.name/abby_schaefer,0.1171075303,221.210.178.65,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n3307,2,73,2017-03-23 06:19:55,http://hammes.io/katelin,0.8455369558,6.131.100.251,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n3308,2,73,2017-05-02 11:17:45,http://swaniawski.io/shemar.veum,0.3896119467,48.131.138.226,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n3309,2,73,2017-01-12 00:04:16,http://schultzwelch.biz/madisen.armstrong,0.6011708012,117.194.101.39,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n3310,2,73,2017-02-04 16:52:51,http://reingerkemmer.com/kelley,0.8961612974,114.44.50.224,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n3311,2,73,2017-04-18 14:04:57,http://douglas.name/jillian,0.2516802338,105.127.178.98,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n3312,2,73,2016-12-26 09:31:37,http://boehm.org/cristian.pfeffer,0.2530844474,165.154.29.116,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n3313,2,73,2017-04-08 10:45:05,http://morar.name/hailee.rolfson,0.6116291765,119.77.12.204,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n3314,2,73,2017-06-03 02:47:07,http://hellerpacocha.com/antone.brakus,0.3337944704,107.137.127.179,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n3315,2,73,2017-03-09 15:39:35,http://zemlakschowalter.biz/brisa.pouros,0.4950607822,115.87.142.171,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n3316,2,73,2017-05-06 11:13:44,http://johnsoncarroll.info/rylee_altenwerth,0.2855759377,84.57.92.120,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n3317,2,73,2017-04-24 02:12:14,http://kleinzemlak.net/casandra,0.3156966336,147.197.80.69,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n10200,4,229,2017-01-26 06:32:18,http://jast.info/francisco,0.1741305127,66.68.174.125,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n10201,4,229,2017-01-20 09:24:43,http://kohler.name/tyshawn,0.2718992467,50.250.244.47,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n10202,4,229,2017-04-23 14:25:30,http://wiegand.io/julio,0.0430457183,138.95.218.34,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n10203,4,229,2017-04-07 19:17:38,http://lueilwitz.org/elliot.douglas,0.5340850875,105.23.3.145,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n10204,4,229,2017-04-24 23:43:53,http://farrell.io/arnoldo,0.7018283964,110.42.5.237,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n10205,4,229,2017-01-29 04:02:31,http://jacobson.co/anastacio_carter,0.5890158237,133.76.237.100,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n10206,4,229,2017-02-02 17:08:05,http://schummschulist.info/piper,0.2709407495,239.210.142.107,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n10207,4,229,2017-02-06 01:02:15,http://nicolaskris.name/savion,0.2522551951,58.207.9.251,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n10208,4,229,2017-06-06 05:39:13,http://bergnaum.biz/terrence.oconner,0.1134816208,23.33.211.21,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n10209,4,229,2017-03-12 22:53:59,http://binspacocha.net/beulah,0.6774651173,150.178.251.183,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n10210,4,230,2017-06-02 15:56:13,http://barrows.info/imogene_koelpin,0.7798749067,156.8.113.40,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n20872,7,469,2017-05-23 15:58:01,http://moen.org/vivian,,90.132.141.249,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n10211,4,230,2017-05-26 02:30:26,http://grant.net/jarrett_nikolaus,0.2230757995,197.162.190.93,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n10212,4,230,2017-02-18 09:40:45,http://windler.biz/elias,0.6824598870,127.100.141.72,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n10213,4,230,2017-04-22 23:08:24,http://bartell.net/zoey.fay,0.0381699580,211.160.65.125,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n10214,4,230,2017-06-07 04:39:12,http://kuhlman.biz/jayne,0.0193422758,155.133.194.136,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n10215,4,230,2017-03-27 00:32:21,http://ornwunsch.org/alvena,0.3715409956,97.20.112.91,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n10216,4,230,2016-12-28 23:22:34,http://lakin.org/addison,0.3622046372,89.231.226.131,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n10217,4,230,2017-03-12 09:44:40,http://stroman.biz/edmond,0.1784426963,173.84.203.156,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n10218,4,230,2017-04-16 07:26:16,http://kulasgulgowski.org/collin_gottlieb,0.6692173860,254.67.12.95,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n10219,4,230,2017-03-03 00:09:15,http://little.net/hipolito,0.8529434057,246.74.81.148,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n10220,4,230,2017-03-07 11:54:27,http://jacobson.net/lance_howell,0.4083775344,242.166.239.65,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n10221,4,230,2017-04-22 00:21:16,http://wyman.io/paige.kemmer,0.2905651718,85.235.200.5,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n10222,4,230,2017-06-09 03:59:02,http://murray.org/violet,0.8919752332,250.116.210.220,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n10223,4,230,2016-12-26 14:22:23,http://yundtharris.co/elda,0.9849185153,160.143.224.50,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n10224,4,230,2016-12-17 12:08:07,http://ruecker.info/alyce,0.3545076803,162.115.223.126,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n10225,4,230,2017-04-18 02:49:06,http://marks.net/clifford,0.6911274923,91.111.160.15,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n10226,4,230,2017-01-01 22:24:27,http://kuhnstark.com/katelynn,0.0012205319,156.43.161.215,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n10227,4,230,2017-04-17 00:46:52,http://borer.name/shad,0.7690708637,113.2.154.190,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n10228,4,230,2017-03-09 09:52:39,http://cummerataparker.biz/ella,0.2591016210,176.146.8.145,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n10229,4,230,2016-12-24 10:44:51,http://zemlakbins.name/johnathan,0.0490166439,249.45.19.68,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n10230,4,230,2017-03-09 20:35:37,http://breitenberg.io/ryann_kling,0.9360933206,197.197.70.46,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n10231,4,230,2017-01-23 15:15:42,http://bahringerlebsack.org/mazie,0.0210043523,4.94.207.219,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n10232,4,230,2017-03-31 12:19:34,http://welchbuckridge.info/tania,0.6583608785,79.248.64.250,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n10233,4,230,2017-04-08 21:39:23,http://schmelerpadberg.org/sherman,0.4406905517,140.199.78.225,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n10234,4,230,2017-05-31 18:17:59,http://lefflerrath.io/selena_huels,0.9065213498,42.144.172.62,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n10235,4,230,2017-02-19 05:18:48,http://pagac.biz/vickie_gleason,0.3308059198,147.192.118.125,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n10236,4,230,2017-04-05 10:54:20,http://bodeveum.org/sydney_dicki,0.8490904798,34.82.230.28,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n10237,4,230,2017-04-13 14:23:41,http://bednarlockman.info/kaycee,0.8028305025,28.130.178.233,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n10238,4,230,2017-02-23 09:44:22,http://swift.co/declan,0.6632006793,117.186.222.52,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n10239,4,230,2017-04-11 20:01:35,http://schulist.org/stephania_lynch,0.2528724275,12.213.154.195,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n10240,4,230,2017-06-12 05:00:07,http://raynorbernier.org/rebekah.runolfon,0.3122378582,250.66.35.113,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n10241,4,230,2017-03-02 09:35:36,http://conroy.co/kiera_sawayn,0.4890252655,74.20.103.28,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n10242,4,230,2017-05-30 02:35:02,http://cartermckenzie.co/alexandrine.kautzer,0.3295967331,129.251.141.187,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n10243,4,230,2017-02-15 04:24:13,http://hirthe.org/jacey,0.7885282345,2.190.89.157,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n10244,4,231,2017-02-24 05:12:24,http://feest.net/hoyt.kshlerin,0.2949218997,49.166.254.149,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n10245,4,231,2017-04-09 04:55:57,http://lednermills.name/dora,0.4072513167,64.156.153.54,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n10246,4,231,2017-03-14 18:41:58,http://wisozk.info/theodora,0.8454667686,38.118.119.90,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n10247,4,231,2017-04-23 21:57:15,http://schuppe.org/hettie_larson,0.6427273204,67.234.183.196,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n10248,4,231,2017-02-08 13:30:11,http://bins.info/skye,0.5986265650,176.58.240.168,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n10249,4,231,2017-01-04 05:29:25,http://wunschoconner.name/lacy,0.4839622595,135.150.195.208,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n7212,3,160,2017-05-09 15:30:35,http://jacobs.biz/maynard.pfeffer,0.3597311400,102.140.58.164,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n7213,3,160,2017-05-23 04:13:20,http://lang.com/dan,0.7050599590,199.139.118.41,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n7214,3,160,2017-03-24 10:46:32,http://mcglynn.biz/frieda,0.2764999604,225.196.97.25,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n7215,3,160,2017-02-21 23:58:31,http://rice.info/jason,0.2883737204,167.94.80.149,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n7216,3,160,2017-04-04 23:00:57,http://wisozk.biz/shawn,0.5024763017,87.172.193.137,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n7217,3,160,2017-06-07 20:21:09,http://grantweimann.name/pauline_okuneva,0.6485795140,237.73.179.200,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n7218,3,160,2017-06-05 02:49:38,http://roob.info/petra.dach,0.2186605065,110.88.208.230,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n7219,3,160,2017-05-28 13:06:54,http://kihn.org/felix_bernier,0.0719621123,95.243.206.87,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n7220,3,160,2017-01-29 05:00:56,http://skiles.org/mariane,0.0256635760,237.164.56.60,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n7221,3,160,2017-04-22 09:13:15,http://kriscormier.org/flavio,0.0322540759,128.84.206.175,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n7222,3,160,2017-03-03 12:32:00,http://osinski.info/aleen,0.2322720326,86.28.213.59,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n7223,3,160,2017-04-28 17:57:17,http://lebsack.org/jacey,0.9540180941,244.53.31.25,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n7224,3,160,2017-03-24 22:48:19,http://feeney.biz/martina.deckow,0.0153660401,202.163.172.238,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n7225,3,160,2017-04-29 07:13:14,http://purdy.co/providenci,0.4966021377,154.100.5.135,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n7226,3,160,2017-03-26 11:44:09,http://weinat.com/nat.fisher,0.1158819019,8.122.34.206,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n7227,3,160,2017-03-01 17:45:04,http://hayes.biz/nasir,0.8534083027,79.191.6.111,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n7228,3,160,2017-06-03 18:15:19,http://kozeygislason.name/elmer_auer,0.7647156352,94.68.116.131,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n7229,3,160,2017-04-27 14:20:27,http://grant.org/gaston,0.5205816062,249.124.41.231,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n7230,3,160,2017-05-17 04:05:47,http://oconnerbuckridge.co/forrest,0.0424716245,157.114.49.171,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n7231,3,160,2017-01-04 03:33:31,http://parker.co/talon.waters,0.8882526399,135.172.87.15,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n7232,3,160,2017-05-24 23:36:01,http://murraynienow.co/everardo_stanton,0.3773239432,242.125.171.195,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n7233,3,160,2016-12-29 23:57:43,http://hermann.com/ivory.crooks,0.7807960854,59.118.101.153,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n7234,3,160,2017-01-03 12:12:07,http://manteleuschke.io/isabelle.sipes,0.8652077085,175.5.204.173,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n7235,3,160,2017-02-02 14:07:25,http://tremblay.co/harrison,0.8091720675,81.133.85.234,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n7236,3,160,2017-01-13 09:59:58,http://gislasonconn.io/andy_strosin,0.6897593688,204.138.123.2,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n7237,3,160,2017-03-14 21:11:41,http://barrows.info/triston,0.5148206784,20.133.93.109,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n7238,3,160,2016-12-31 09:53:12,http://altenwerth.name/marty,0.4154112922,95.249.205.134,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n7239,3,160,2017-04-27 23:05:24,http://marks.biz/lizeth.lubowitz,0.2340357948,76.99.53.219,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n7240,3,160,2017-01-03 07:04:11,http://kerluke.biz/verner.brekke,0.4208857998,40.37.204.128,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n7241,3,160,2017-03-27 14:41:03,http://ebert.org/anais_mosciski,0.7532544265,15.56.250.213,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n7242,3,160,2017-01-10 05:13:55,http://waelchi.co/mathilde_raynor,0.5638954264,133.244.13.12,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n7243,3,160,2016-12-29 08:27:50,http://abshirepouros.biz/wendy,0.6920799070,10.130.54.19,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n7244,3,160,2017-04-14 05:16:19,http://andersonthompson.biz/cornell.kling,0.5368915854,80.49.122.161,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n7245,3,160,2017-05-09 12:41:05,http://stokes.net/peter,0.2712604449,115.133.121.56,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n7246,3,160,2017-02-12 12:47:20,http://tromprempel.biz/jesus,0.1979286348,158.201.152.122,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n7247,3,161,2017-06-05 01:08:07,http://borer.biz/nils.nitzsche,0.3749918622,33.58.58.4,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n7248,3,161,2017-04-23 00:52:05,http://goodwin.co/freeman,0.7390166648,57.209.43.186,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n7249,3,161,2017-05-29 22:34:30,http://yundtjast.biz/marisol_johnston,0.2106895314,44.3.77.104,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n7250,3,161,2017-01-20 04:25:03,http://wisoky.com/kevin,0.8543972732,74.102.125.152,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n7251,3,161,2017-02-22 15:40:30,http://kutch.com/kale,0.1853705488,74.145.69.62,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n7252,3,161,2017-05-21 07:43:06,http://sporer.co/cyrus_simonis,0.8633512922,29.199.210.102,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n7253,3,161,2017-02-03 23:50:04,http://sporermcdermott.info/peter,0.5044924462,24.208.238.205,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n7254,3,161,2017-02-01 22:23:11,http://daniel.biz/agustina,0.9450692001,195.217.192.58,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n7255,3,161,2017-03-23 14:00:33,http://grimes.com/van.wuckert,0.8101462776,191.94.224.152,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n7256,3,161,2017-04-29 20:51:18,http://ernserreichel.co/jeika,0.6061784273,72.193.16.219,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n7257,3,161,2017-05-26 06:24:58,http://lockman.net/sharon_collier,0.0252690386,127.51.194.62,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n7258,3,161,2017-05-16 20:56:01,http://strosinkling.com/brett_walker,0.2324779106,232.85.29.237,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n7259,3,161,2017-04-25 16:20:50,http://terry.io/hiram,0.3811084138,222.55.190.36,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n7260,3,161,2017-04-26 10:06:47,http://friesen.com/peggie_mcglynn,0.6045645532,81.199.12.157,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n7261,3,161,2017-02-08 07:53:59,http://leschcasper.co/sherwood.bartoletti,0.4746615700,86.154.186.168,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n7262,3,161,2017-04-04 03:52:16,http://harber.io/merritt_borer,0.3552821742,30.113.186.110,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n13118,5,294,2016-12-18 18:19:12,http://bins.net/lisette,0.1851633740,227.79.148.3,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n13119,5,294,2017-06-05 18:20:00,http://wizaframi.net/alisa.kilback,0.3033689630,114.144.71.173,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n13120,5,294,2017-01-08 03:00:46,http://fritsch.info/rashawn,0.9782695112,152.185.247.240,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n13121,5,294,2017-04-11 07:30:09,http://von.co/haskell.raynor,0.4098467227,252.42.67.183,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n13122,5,294,2017-03-15 18:22:58,http://crooks.co/jaylon,0.0258377265,159.126.54.107,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n13123,5,294,2017-05-18 18:20:57,http://maggio.co/augustus,0.1032491087,198.72.97.10,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n13124,5,294,2017-05-07 19:30:17,http://hartmann.name/oliver,0.5149978408,198.95.193.54,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n13125,5,294,2017-04-28 04:28:56,http://bartellhyatt.info/gage,0.7303327798,165.114.175.56,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n13126,5,295,2017-04-12 06:38:32,http://abbott.biz/carmelo.hane,,121.65.204.71,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n13127,5,295,2017-06-01 12:03:34,http://bauch.co/donavon_adams,,94.84.142.215,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n13128,5,295,2017-01-30 01:23:05,http://jacobilindgren.biz/chauncey_hyatt,,97.67.121.30,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n13129,5,295,2016-12-30 10:26:25,http://gerhold.name/austen.lubowitz,,9.26.183.188,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n13130,5,295,2017-02-04 21:19:58,http://keler.co/amparo_becker,,163.130.75.254,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n13131,5,295,2017-02-12 20:21:08,http://keeblerabbott.org/furman,,241.221.93.23,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n13132,5,295,2017-02-18 09:25:43,http://veumankunding.io/bennie,,6.132.90.68,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n13133,5,295,2017-03-15 21:58:26,http://krajcik.name/felicity,,38.44.81.86,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n13134,5,295,2017-01-21 08:03:35,http://torphy.biz/percival,,116.119.172.221,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n13135,5,295,2017-01-24 14:00:20,http://millergraham.co/akeem,,9.219.126.149,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n13136,5,295,2017-01-05 12:43:19,http://rathgerlach.name/barton,,118.82.249.44,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n13137,5,295,2017-04-02 20:15:25,http://gutkowskikris.net/graciela,,222.173.244.21,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n13138,5,295,2017-05-15 00:39:51,http://koeppspinka.info/jayne_heel,,113.202.114.115,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n13139,5,295,2017-06-11 04:08:50,http://schmeler.net/natalie.schumm,,95.106.37.172,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n13140,5,295,2017-05-04 02:18:29,http://nienow.biz/karl.bauch,,182.53.128.60,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n13141,5,295,2016-12-15 19:46:25,http://funk.biz/caria,,204.241.160.146,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n13142,5,295,2017-06-01 06:58:35,http://dietrich.name/lucas.windler,,116.196.119.192,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n13143,5,295,2017-05-31 16:12:18,http://hermiston.info/jolie,,178.162.117.88,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n13144,5,295,2017-02-01 17:00:57,http://bernier.com/stuart,,72.172.8.6,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n13145,5,295,2017-04-27 20:04:29,http://labadiehintz.org/mayra,,216.48.105.246,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n13146,5,295,2017-02-19 00:34:25,http://terrymraz.biz/casper_keler,,111.78.22.98,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n13147,5,295,2017-02-08 17:54:03,http://glover.io/jakayla_hand,,192.35.15.96,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n13148,5,295,2017-04-09 19:48:01,http://reichertdaniel.biz/josh.will,,181.15.99.237,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n13149,5,295,2017-05-30 19:19:28,http://ebert.net/dell.powlowski,,118.53.125.39,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n13150,5,295,2017-05-27 00:13:12,http://nolan.net/bartholome_abbott,,53.159.47.226,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n13151,5,295,2017-05-22 01:37:16,http://mcglynn.io/kelsi_schmitt,,27.98.58.234,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n13152,5,295,2017-04-11 07:40:57,http://ernser.org/milford,,228.143.171.78,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n13153,5,295,2017-04-17 06:43:45,http://breitenberg.io/gregg,,116.84.12.251,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n13154,5,295,2017-03-25 01:56:03,http://dicki.co/emiliano.zieme,,178.211.35.164,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n13155,5,295,2017-01-07 11:04:28,http://conroy.org/esther,,191.166.183.215,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n13156,5,295,2017-05-02 09:47:36,http://wisozk.name/sam,,183.239.185.104,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n13157,5,295,2017-02-20 03:39:37,http://lakin.org/schuyler_langworth,,190.170.241.79,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n13158,5,295,2017-06-11 17:05:44,http://goyettenolan.net/rickie.hayes,,24.148.116.113,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n13159,5,295,2017-03-10 21:35:36,http://moen.org/ayden.fay,,241.53.162.46,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n13160,5,296,2017-02-06 15:36:26,http://hartmannbins.net/max.hilll,,179.69.233.241,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n13161,5,296,2017-02-14 16:55:13,http://weber.co/evalyn,,20.161.92.229,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n13162,5,296,2017-04-23 01:00:57,http://boscodamore.io/agustina,,214.44.186.187,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n13163,5,296,2017-05-15 18:58:48,http://stammnikolaus.name/newton.oreilly,,72.218.111.159,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n13164,5,296,2017-02-24 22:05:40,http://runolfsdottir.com/kian.fahey,,244.21.233.152,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n13165,5,296,2017-04-23 20:44:19,http://boyerstroman.com/kristian,,126.157.201.64,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n13166,5,296,2017-01-04 18:26:00,http://harvey.name/dolly_anderson,,96.36.151.96,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n13167,5,296,2017-03-14 19:07:59,http://smithamkling.co/salvatore.douglas,,186.121.114.185,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n13168,5,296,2017-01-03 01:20:17,http://romaguera.io/hilma,,22.187.217.70,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n13169,5,296,2017-03-16 21:43:36,http://cummings.io/marlee,,128.189.197.59,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n13170,5,296,2016-12-27 00:07:49,http://pollich.co/lempi,,15.120.65.72,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n13171,5,296,2017-02-07 21:40:38,http://kovacek.com/lottie.medhurst,,31.110.237.224,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n13172,5,296,2017-04-03 11:28:39,http://skiles.info/elbert.ryan,,77.229.114.213,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n16137,6,364,2017-02-01 06:36:09,http://greenholtturner.io/ernest_wilkinson,,233.71.182.243,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n16138,6,364,2016-12-27 22:08:41,http://mayert.info/pat,,88.246.53.133,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n16139,6,364,2017-05-16 18:22:29,http://halvorsonroob.io/norwood.lebsack,,61.191.104.240,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n16140,6,364,2017-01-15 09:49:14,http://bosco.io/conner_bergnaum,,30.251.10.66,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n16141,6,364,2017-03-02 00:17:12,http://macgyverfarrell.com/laney_cole,,81.175.98.7,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n16142,6,364,2017-04-22 22:51:56,http://abshire.org/theo.dibbert,,130.117.67.238,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n16143,6,364,2017-02-28 06:21:16,http://douglasleffler.biz/lyda.hand,,190.81.89.96,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n16144,6,364,2016-12-16 22:56:10,http://kautzerzemlak.name/jaclyn,,147.53.244.58,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n16145,6,364,2017-03-18 17:37:13,http://douglas.biz/jasmin,,96.38.85.186,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n16146,6,364,2017-01-21 18:00:40,http://kaulke.com/ettie.ritchie,,150.134.130.54,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n16147,6,364,2017-06-02 09:46:06,http://graham.com/maya,,80.163.133.145,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n16148,6,364,2017-04-11 09:12:27,http://jacobi.biz/alberto_bartoletti,,199.85.39.224,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n16149,6,364,2017-06-09 21:39:42,http://schoen.info/laverna_wisoky,,244.65.159.248,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n16150,6,364,2017-05-27 05:32:38,http://nader.biz/delpha.haley,,150.93.28.18,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n16151,6,364,2017-02-25 18:19:40,http://cormier.org/neal_denesik,,5.245.85.144,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n16152,6,364,2017-04-06 12:42:42,http://spinka.info/janie,,101.24.42.174,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n16153,6,364,2017-02-07 17:10:46,http://little.io/elody,,121.204.99.230,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n16154,6,365,2016-12-18 14:36:12,http://breitenbergbahringer.net/gay.bartoletti,,84.94.210.235,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n16155,6,365,2017-05-24 11:25:00,http://lemkeeffertz.net/francis,,23.86.126.13,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n16156,6,365,2017-01-15 23:30:49,http://gutkowskikuphal.net/marcellus,,248.179.9.90,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n16157,6,365,2017-02-01 08:40:31,http://colewalsh.net/elvera,,13.6.229.148,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n16158,6,365,2017-05-21 15:23:57,http://olson.name/judah_hintz,,115.42.5.250,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n16159,6,365,2017-02-06 04:10:38,http://hackett.name/hermann.kerluke,,83.210.225.71,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n16160,6,365,2017-02-02 06:21:26,http://connellydouglas.info/crystel.sporer,,243.173.212.133,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n16161,6,365,2017-01-16 21:18:35,http://douglas.io/nella_jerde,,21.98.137.176,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n16162,6,365,2017-01-25 08:07:53,http://boyer.info/orrin_moen,,178.43.254.232,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n16163,6,365,2017-02-28 22:36:36,http://gislason.org/nicola.farrell,,218.125.53.176,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n16164,6,365,2017-01-28 22:44:13,http://rohanwisozk.org/claudine,,247.161.125.237,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n16165,6,365,2017-01-02 08:44:09,http://kuhlman.biz/porter,,253.154.172.233,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n16166,6,365,2017-05-08 10:46:43,http://king.com/alden,,66.29.29.220,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n16167,6,365,2017-04-24 23:20:34,http://harvey.info/julien,,82.40.131.219,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n16168,6,365,2017-01-18 11:13:31,http://murphy.biz/reyna_waters,,18.164.162.8,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n16169,6,365,2017-04-17 19:38:49,http://dickens.com/jaquelin_kautzer,,143.148.3.64,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n16170,6,365,2017-03-14 07:42:03,http://bradtke.name/amelie,,209.5.78.115,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n16171,6,365,2017-03-25 19:28:58,http://leffler.name/meaghan,,210.41.142.214,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n16172,6,365,2017-04-11 21:15:45,http://gottlieb.io/thea,,97.210.210.77,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n16173,6,365,2017-05-18 16:30:12,http://haag.info/eldred.stokes,,55.3.80.194,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n16174,6,365,2016-12-24 21:21:07,http://stiedemann.name/keshawn,,83.86.222.238,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n16175,6,365,2017-05-13 02:47:33,http://bernhardlarson.info/valentin_roob,,106.129.240.139,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n16176,6,365,2017-02-23 22:57:26,http://rowe.co/dorris.emard,,111.126.74.76,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n16177,6,365,2017-05-29 11:42:04,http://mclaughlinschmeler.org/jaycee,,57.112.53.86,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n16178,6,365,2016-12-21 05:55:04,http://langworth.net/micah,,37.254.51.13,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n16179,6,365,2016-12-26 03:23:16,http://millchiller.name/gielle,,142.148.163.137,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n16180,6,365,2017-02-06 06:02:40,http://fahey.com/emely_fay,,6.111.238.117,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n16181,6,365,2017-02-03 10:26:51,http://kiehnhoppe.com/billie,,179.46.188.252,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n16182,6,365,2017-01-23 12:25:55,http://quitzon.net/stanford.feeney,,43.177.22.84,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n16183,6,365,2017-04-07 23:16:26,http://abernathyhuel.name/letha,,173.239.52.89,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n16184,6,365,2016-12-16 22:35:09,http://halvorson.name/evalyn_feeney,,122.56.179.221,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n16185,6,365,2017-04-25 00:55:53,http://volkman.co/jaleel.beer,,83.74.102.236,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n16186,6,366,2017-04-20 05:46:19,http://mannleffler.co/obie.mckenzie,,146.22.20.245,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n16187,6,366,2017-05-15 08:58:03,http://torphy.name/claudie,,48.185.210.204,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n16188,6,366,2017-02-27 04:38:21,http://greenholt.com/clinton,,221.101.133.192,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n16189,6,366,2017-02-03 16:14:54,http://barrows.io/tierra,,75.73.154.217,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n16190,6,366,2017-02-21 15:55:54,http://collier.net/tyra.hickle,,248.55.146.26,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n16191,6,366,2017-06-08 02:58:10,http://haag.name/mozell.senger,,121.116.105.103,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n3318,2,73,2017-01-24 15:35:49,http://oberbrunnerkuvalis.info/rosie.breitenberg,0.7276529732,83.146.165.57,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n3319,2,73,2017-01-29 13:30:44,http://windlerrath.com/yvette_boyer,0.0833660566,140.137.206.238,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n3320,2,73,2017-01-08 08:31:52,http://cruickshankhegmann.biz/alexis_sauer,0.4260702140,143.111.162.235,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n3321,2,73,2017-01-03 10:18:36,http://huelsbartell.name/carolyne,0.4110839752,15.54.66.167,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n3322,2,73,2017-02-22 05:33:03,http://bechtelar.name/kristofer_hane,0.5930202235,220.181.36.176,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n3323,2,73,2017-02-01 22:35:10,http://torphy.name/sigmund,0.6040979851,204.22.203.208,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n3324,2,73,2017-04-04 07:48:51,http://feest.net/abel.west,0.1718269634,46.31.39.55,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n3325,2,73,2017-06-01 03:18:36,http://nolan.com/jovan_ohara,0.0427052714,104.43.217.89,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n3326,2,73,2017-04-10 17:54:45,http://okeefe.com/omer,0.5052237576,13.141.56.52,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n3327,2,73,2017-02-07 19:58:41,http://cronaklocko.biz/angeline,0.6725601288,56.49.220.151,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n3328,2,73,2016-12-16 10:12:47,http://ankunding.info/vilma.spencer,0.3950093244,29.123.226.64,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n3329,2,73,2017-02-17 09:07:07,http://cummeratagutmann.info/wyatt,0.2592343962,186.190.179.110,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n3330,2,73,2017-02-07 23:25:52,http://abbottrobel.co/kristina,0.8371929420,201.148.200.63,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n3331,2,73,2017-06-11 18:38:36,http://leannon.info/enrico,0.3770720906,15.112.196.68,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n3332,2,73,2017-01-01 05:37:02,http://flatley.biz/keith,0.9220624744,177.245.101.110,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n3333,2,73,2017-01-25 18:39:06,http://hauck.org/jocelyn.schneider,0.2473830123,105.203.17.116,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n3334,2,74,2016-12-24 15:07:07,http://fadel.info/juanita,0.6310982422,65.29.76.164,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n3335,2,74,2017-02-27 23:39:00,http://purdy.net/pearl.von,0.0911642441,88.151.67.5,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n3336,2,74,2017-05-21 06:52:33,http://sanford.com/dayne.dooley,0.0752830254,34.67.228.116,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n3337,2,74,2017-05-12 21:54:56,http://hackett.info/demarco_labadie,0.7975416159,119.247.161.123,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n3338,2,74,2017-06-07 19:29:31,http://ebert.info/jedediah,0.4155092593,108.107.121.161,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n3339,2,74,2017-03-23 14:36:25,http://balistreri.name/winfield,0.5599428936,95.214.40.90,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n3340,2,74,2017-03-18 20:13:44,http://mclaughlin.info/francisco_beahan,0.3199011332,109.253.252.77,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n3341,2,74,2017-04-07 17:20:41,http://gorczany.biz/milford_schneider,0.0468558707,191.248.198.234,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n3342,2,74,2017-04-18 22:17:43,http://corwin.net/darrin,0.2577090757,3.150.124.36,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n3343,2,74,2017-02-19 08:49:02,http://beckerheaney.net/henri,0.2544168691,146.97.55.119,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n3344,2,74,2017-01-01 23:52:15,http://haag.co/cicero,0.2682738094,193.207.10.220,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n3345,2,74,2017-03-09 14:25:15,http://keebler.name/patrick_kub,0.6952813459,154.87.3.92,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n3346,2,74,2016-12-28 16:53:20,http://bauchrodriguez.info/alfreda,0.8467694945,122.79.181.229,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n3347,2,74,2017-05-13 17:24:34,http://nolanhilll.com/jennie.haley,0.6307927771,21.69.149.160,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n3348,2,74,2017-06-06 06:36:24,http://schulistpadberg.co/ladarius,0.9144426528,193.17.143.99,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n3349,2,74,2017-02-12 08:35:08,http://jaskolski.com/chanel.goodwin,0.1054820153,124.62.249.97,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n3350,2,74,2017-03-15 01:57:43,http://senger.io/ashtyn,0.2515146309,174.101.246.178,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n3351,2,74,2017-02-18 17:55:34,http://abernathybeier.org/vivienne.kub,0.2434417855,177.61.32.107,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n3352,2,74,2017-05-13 19:21:06,http://mckenzie.info/haven_kuhlman,0.6673771423,73.226.38.69,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n3353,2,74,2017-02-23 10:52:29,http://boyerkonopelski.name/dexter,0.2088725613,77.23.61.206,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n3354,2,74,2017-04-23 18:47:50,http://volkman.biz/lexie,0.2429459894,221.173.46.105,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n3355,2,74,2017-04-10 05:49:43,http://larson.info/abbie,0.3743725848,61.26.6.126,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n3356,2,74,2017-03-14 10:22:54,http://kilbackdavis.info/kendall.walter,0.4478114516,163.88.116.128,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n3357,2,74,2017-03-14 06:27:46,http://koepp.io/florine,0.3409988534,72.238.66.31,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n3358,2,74,2017-04-16 20:07:08,http://rempelerdman.co/dan,0.9724749047,159.198.183.13,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n3359,2,74,2017-03-05 14:41:33,http://hagenesrohan.io/salvador,0.0299427016,112.58.59.207,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n3360,2,74,2017-05-30 02:53:54,http://torp.info/hilario_sawayn,0.3424102316,32.219.151.162,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n3361,2,75,2017-02-18 13:05:24,http://raynor.name/brooklyn,0.8552383741,153.67.229.59,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n3362,2,75,2017-01-06 03:13:55,http://hirthe.io/paolo_jerde,0.9585967615,74.114.154.97,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n3363,2,75,2017-03-10 02:46:33,http://macgyverbeahan.name/lucius,0.3031828772,110.134.245.74,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n3364,2,75,2017-04-11 18:54:01,http://glover.biz/bridgette.beier,0.4084725848,236.245.114.66,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n3365,2,75,2017-01-06 13:16:06,http://heidenreichboyer.co/mikayla.hane,0.3301250576,186.7.95.226,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n3366,2,75,2017-05-31 19:53:33,http://rogahn.com/eliza,0.0284332440,148.140.154.179,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n3367,2,75,2017-03-19 07:06:11,http://walker.name/vicenta,0.9498869898,128.130.182.6,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n3368,2,75,2017-04-03 05:54:16,http://cain.info/orlando,0.9675430658,158.134.185.169,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n3369,2,75,2017-01-22 15:36:48,http://andersonconsidine.co/rosella,0.7374516434,2.66.153.14,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n10250,4,231,2017-05-17 08:15:22,http://cainbradtke.com/chyna,0.2596368566,249.165.132.20,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n10251,4,231,2017-04-01 04:03:40,http://farrellwyman.org/marlin,0.5402574527,63.245.221.231,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n10252,4,231,2017-03-02 03:27:54,http://reynolds.org/waylon_wuckert,0.3275844421,166.96.93.244,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n10253,4,231,2017-03-18 22:11:01,http://gusikowski.com/kadin.streich,0.4105330944,36.81.11.69,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n10254,4,231,2016-12-24 06:03:38,http://huel.biz/consuelo.lemke,0.3097520921,52.213.69.7,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n10255,4,231,2017-02-23 21:59:36,http://mills.io/lilian,0.7972534040,215.105.17.233,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n10256,4,231,2017-02-18 19:21:13,http://cummerata.co/alvis,0.6382327641,30.100.83.152,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n10257,4,231,2017-05-10 16:27:56,http://rosenbaum.name/dayna.abernathy,0.1550157712,84.73.126.185,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n10258,4,231,2017-04-14 05:12:57,http://legrosryan.info/felicia.oberbrunner,0.3295082861,158.238.104.109,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n10259,4,231,2017-01-17 03:45:50,http://dooleymertz.net/damion,0.0535820789,137.48.219.212,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n10260,4,231,2017-01-24 11:30:40,http://kutch.info/jaida_kulas,0.1296176018,139.198.157.74,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n10261,4,231,2017-02-17 01:44:10,http://zulauf.com/hulda,0.3461819944,41.51.150.94,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n10262,4,231,2017-01-17 10:58:34,http://stanton.org/maximillia.barton,0.9334106981,126.91.187.68,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n10263,4,231,2017-02-28 03:28:25,http://gislason.name/virgie,0.4259577908,206.148.170.72,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n10264,4,231,2017-05-28 21:55:10,http://farrell.biz/cleora.vonrueden,0.6022779389,56.46.123.86,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n10265,4,231,2017-06-07 11:41:12,http://raynor.io/trey_langworth,0.4203812266,119.84.235.163,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n10266,4,231,2017-02-25 20:48:16,http://pouros.io/kory,0.5231721367,73.90.31.223,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n10267,4,231,2017-05-09 13:21:44,http://hilpertkeebler.io/elva,0.7347521751,247.227.178.171,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n10268,4,231,2017-01-14 14:01:48,http://robertswatsica.io/elian.tillman,0.0691424662,35.150.108.209,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n10269,4,231,2017-01-30 02:34:22,http://oharavon.org/leta.nikolaus,0.2877276154,180.242.75.171,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n10270,4,231,2017-05-11 20:52:22,http://rolfsonorn.name/jeica.zulauf,0.9192271474,141.43.140.136,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n10271,4,231,2017-01-13 21:31:17,http://kovaceksmitham.net/rylan,0.6832663659,140.130.154.240,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n10272,4,231,2017-01-29 08:34:01,http://west.biz/wallace_bechtelar,0.1841709577,211.104.97.117,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n10273,4,231,2017-04-11 10:58:50,http://connelly.org/krista,0.8574019595,47.105.102.19,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n10274,4,231,2017-03-20 16:21:44,http://murray.io/cecilia_anderson,0.9396774481,212.108.124.190,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n10275,4,231,2016-12-29 06:49:26,http://reichert.io/luciano,0.3817683029,187.113.132.77,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n10276,4,231,2016-12-18 04:23:48,http://robel.net/zella,0.3312385128,213.164.144.32,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n10277,4,231,2017-04-20 01:23:02,http://schamberger.co/rubie.ortiz,0.8441995049,234.115.69.241,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n10278,4,231,2017-04-17 14:38:35,http://olson.biz/josh_murphy,0.0807495815,90.190.55.195,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n10279,4,231,2017-04-22 15:41:48,http://schinner.io/lauretta,0.0201766096,112.127.81.170,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n10280,4,231,2017-02-07 23:18:48,http://robelheidenreich.name/emmanuelle,0.1476162038,52.182.72.78,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n10281,4,231,2017-01-15 07:31:02,http://schamberger.info/shaina,0.9339568098,21.101.57.24,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n10282,4,231,2017-02-16 01:17:18,http://morar.io/abbigail.schulist,0.2544911302,251.123.76.65,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n10283,4,231,2016-12-22 23:04:49,http://ruecker.com/sydnee_towne,0.8075260468,232.169.246.165,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n10284,4,231,2017-04-21 19:52:09,http://haag.info/garry.ullrich,0.8770736781,133.240.80.145,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n10285,4,231,2017-05-15 11:16:44,http://rippin.name/gabriel.von,0.9849806523,179.48.56.20,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n10286,4,231,2017-05-02 18:50:49,http://herzogmonahan.net/queen,0.3307911247,41.112.183.27,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n10287,4,231,2017-04-06 16:17:36,http://sauer.co/andrew.ritchie,0.4558812383,236.251.20.72,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n10288,4,231,2017-03-02 02:45:08,http://yost.io/toby.rolfson,0.8796943905,234.76.106.91,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n10289,4,231,2017-06-05 12:23:18,http://mosciski.co/celine_hettinger,0.8884712918,144.43.100.129,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n10290,4,231,2016-12-17 13:50:34,http://windler.biz/dorthy_yost,0.4624625495,96.236.242.136,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n10291,4,231,2017-05-26 02:20:49,http://shanahan.net/judge.harris,0.2885144078,30.71.153.132,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n10292,4,232,2017-05-14 07:07:45,http://johnston.org/shana,0.3728541019,159.193.225.113,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n10293,4,232,2017-03-16 16:59:56,http://hermistonpfannerstill.net/bryon,0.3670304748,190.109.227.162,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n10294,4,232,2017-06-10 08:36:39,http://cruickshank.io/vivienne,0.3600121407,120.209.17.102,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n10295,4,232,2016-12-31 06:32:25,http://erdman.name/lacey_brown,0.9979101792,14.68.124.42,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n10296,4,232,2017-04-01 00:34:05,http://hudsonkoch.net/hayley,0.5307143342,230.237.191.118,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n10297,4,232,2017-04-12 12:06:43,http://kovacek.name/keagan_lowe,0.5402884838,85.20.34.254,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n10298,4,232,2017-02-17 04:18:07,http://zulauf.io/mertie,0.0305559281,85.162.56.12,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n10299,4,232,2017-01-17 02:42:25,http://feeney.com/juston,0.2050076896,52.144.139.196,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n10300,4,232,2017-04-17 12:58:15,http://kemmer.net/heidi,0.5435829148,210.34.74.19,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n10301,4,232,2017-05-07 08:36:42,http://jakubowski.io/may,0.2368814972,224.216.127.176,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n7263,3,161,2017-03-16 12:38:39,http://berge.biz/josie.bosco,0.0601504283,161.16.55.248,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n7264,3,161,2017-01-17 23:24:11,http://boscohamill.info/major,0.0853269010,98.54.54.180,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n7265,3,161,2016-12-28 00:29:27,http://weinat.io/wilfred_beer,0.4554543793,126.187.134.229,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n7266,3,161,2017-04-18 10:12:45,http://yundt.biz/luis,0.8856571261,97.150.130.96,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n7267,3,161,2017-05-30 14:26:08,http://grant.co/brent_effertz,0.8008623843,50.227.82.148,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n7268,3,161,2017-02-18 22:11:00,http://bechtelarkuvalis.info/martin,0.0131400124,4.196.63.137,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n7269,3,161,2017-04-05 17:49:45,http://heel.net/daren,0.2872192993,3.205.225.243,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n7270,3,161,2017-01-08 14:06:54,http://sporer.net/giovanni,0.8565914423,53.238.175.80,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n7271,3,161,2017-01-14 19:44:07,http://johnston.co/elza.larson,0.2577679711,52.102.235.126,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n7272,3,161,2017-01-22 12:17:36,http://toy.name/hal_beer,0.7722237089,227.232.29.60,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n7273,3,161,2017-02-11 05:08:41,http://stroman.co/merritt,0.6641424277,172.86.169.97,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n7274,3,161,2017-04-12 10:56:16,http://schaefergoyette.io/colten,0.1726948151,188.96.101.219,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n7275,3,161,2017-02-25 13:20:42,http://gorczany.net/mallory.sipes,0.4555251542,45.65.120.164,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n7276,3,161,2017-03-28 09:32:32,http://weber.name/macy,0.7134317647,80.242.104.20,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n7277,3,161,2017-06-03 22:54:13,http://conn.co/roxane,0.3962254580,193.46.7.235,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n7278,3,161,2017-02-27 02:14:34,http://littel.net/daphne,0.9186295396,219.214.24.110,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n7279,3,161,2017-02-23 02:20:28,http://nader.net/viva.prosacco,0.9550941310,224.83.80.100,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n7280,3,161,2017-02-24 18:06:37,http://klocko.co/corrine,0.6801428928,75.101.23.44,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n7281,3,161,2017-06-06 11:04:36,http://millsfranecki.co/lavern.bogan,0.7730865065,75.225.65.168,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n7282,3,161,2017-03-16 06:34:16,http://johnson.io/adriel,0.0351813452,30.166.154.224,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n7283,3,161,2017-04-24 14:36:31,http://okuneva.name/alexis.langworth,0.5025736064,252.210.107.28,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n7284,3,161,2017-06-13 10:12:40,http://hintzschroeder.name/marion,0.1926216576,110.31.8.88,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n7285,3,161,2017-05-30 13:00:01,http://pollich.io/adrienne,0.8300253782,95.149.46.45,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n7286,3,161,2016-12-29 00:24:24,http://herman.net/abraham,0.6749699775,27.28.188.110,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n7287,3,162,2017-02-20 08:43:41,http://kreiger.net/raymond,,119.101.41.157,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n7288,3,162,2017-04-25 07:10:15,http://doyleharvey.net/derrick,,164.189.56.41,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n7289,3,162,2017-03-04 13:34:38,http://hahnhermann.io/shanna,,3.231.229.232,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n7290,3,162,2017-06-07 20:15:36,http://moriettehuel.net/earnestine,,147.221.226.58,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n7291,3,162,2017-02-15 02:02:04,http://klein.info/kelli.fisher,,252.114.159.180,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n7292,3,162,2017-02-07 09:13:45,http://bernhard.name/dillon.jerde,,205.203.70.213,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n7293,3,162,2017-01-06 22:38:25,http://mraz.biz/mckenna,,8.179.17.103,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n7294,3,162,2017-03-28 05:38:55,http://leuschke.org/consuelo.muller,,119.205.39.42,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n7295,3,162,2017-02-08 01:04:03,http://wunschbergstrom.io/grant,,190.47.76.88,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n7296,3,162,2017-01-28 14:41:23,http://morar.org/esta,,87.218.37.175,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n7297,3,162,2017-05-23 07:24:12,http://rogahnkilback.info/simeon,,188.215.68.85,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n7298,3,162,2017-03-25 18:56:36,http://gottliebbergstrom.com/adan_durgan,,145.227.242.116,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n7299,3,162,2017-05-11 19:08:49,http://bogisichratke.com/karlie.bernier,,167.75.92.132,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n7300,3,162,2017-03-20 01:38:01,http://moore.co/carmella_swaniawski,,34.153.222.176,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n7301,3,162,2017-06-13 16:13:19,http://dibbert.co/tina,,131.16.163.55,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n7302,3,162,2017-02-23 18:34:49,http://carter.co/benjamin_white,,168.67.199.230,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n7303,3,162,2017-05-08 05:19:36,http://schroeder.biz/zion_smitham,,130.70.160.100,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n7304,3,162,2017-05-11 05:37:28,http://gleason.io/austyn_mosciski,,184.53.196.208,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n7305,3,162,2017-03-25 14:52:21,http://breitenbergabbott.info/ansley_toy,,110.220.30.231,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n7306,3,162,2017-02-19 04:27:22,http://satterfieldklein.biz/izaiah,,202.34.201.253,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n7307,3,162,2017-01-25 22:54:41,http://beier.info/hayley,,77.209.46.23,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n7308,3,162,2017-04-30 19:49:29,http://krisgrady.name/adelle.schaefer,,214.166.228.197,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n7309,3,162,2017-03-13 18:21:29,http://zemlak.name/loyal,,52.78.238.173,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n7310,3,162,2017-05-28 14:54:26,http://ritchie.biz/helga,,233.64.115.87,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n7311,3,162,2017-01-26 22:05:05,http://borer.co/kasandra,,91.234.39.236,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n7312,3,162,2017-04-14 15:38:59,http://yundtbecker.biz/annabelle.hayes,,95.116.218.115,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n7313,3,162,2017-05-02 06:17:52,http://fahey.net/colton_wolff,,91.70.192.95,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n7314,3,162,2016-12-28 09:31:05,http://johnsjerde.info/meggie_hoeger,,92.208.165.37,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n7315,3,162,2017-06-14 04:47:18,http://grant.name/amber,,184.250.46.226,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n7316,3,162,2017-05-19 07:30:35,http://grady.io/porter_cummerata,,149.52.7.237,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n7317,3,163,2017-05-12 06:02:06,http://walkerkiehn.co/jacques.rogahn,,208.216.224.12,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n13173,5,296,2017-05-15 09:27:04,http://kilback.biz/ima_thiel,,226.32.56.149,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n13174,5,296,2017-05-11 21:04:52,http://schultzjacobs.net/eulah,,170.222.233.158,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n13175,5,296,2017-05-16 16:20:40,http://buckridge.biz/soledad,,146.15.131.187,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n13176,5,296,2017-04-19 03:24:39,http://toy.biz/duane_renner,,105.190.248.175,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n13177,5,296,2017-05-28 10:29:44,http://jenkins.net/electa,,28.50.195.158,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n13178,5,296,2017-01-07 13:56:02,http://farrell.net/terence,,6.216.45.212,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n13179,5,296,2017-05-19 17:10:48,http://zieme.name/antone,,14.27.148.250,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n13180,5,296,2017-03-10 09:35:06,http://ullrich.biz/vergie_pagac,,82.20.81.238,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n13181,5,296,2017-05-25 03:56:26,http://okon.org/alexane,,168.244.136.27,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n13182,5,296,2017-06-03 16:35:49,http://crona.com/courtney_conroy,,106.53.18.68,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n13183,5,296,2017-01-18 02:31:27,http://legros.name/amy_douglas,,232.250.41.85,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n13184,5,296,2017-01-20 08:31:47,http://mertz.org/favian.oconner,,235.185.233.26,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n13185,5,296,2017-03-16 21:32:41,http://okeefewaelchi.com/brandt,,120.244.152.76,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n13186,5,296,2017-03-07 04:29:19,http://howell.name/kim_lindgren,,21.142.120.228,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n13187,5,296,2017-01-10 06:28:06,http://osinskizemlak.io/oral.sporer,,96.173.171.205,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n13188,5,296,2017-01-14 19:24:27,http://ryan.biz/george_bashirian,,114.183.7.114,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n13189,5,296,2017-01-06 20:03:42,http://leffler.info/daphnee_willms,,80.119.198.142,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n13190,5,296,2017-03-13 02:06:31,http://considine.biz/halle.quitzon,,80.17.69.83,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n13191,5,297,2017-03-24 13:05:41,http://zieme.info/estel,,64.231.24.208,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n13192,5,297,2017-02-22 21:48:38,http://harvey.info/maria.adams,,252.198.83.7,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n13193,5,297,2017-05-07 11:55:34,http://cronin.name/bell_baumbach,,101.91.191.33,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n13194,5,297,2017-04-14 04:54:56,http://rath.net/osbaldo,,38.124.100.43,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n13195,5,297,2017-03-20 02:52:26,http://markskulas.org/chandler.abbott,,117.47.209.90,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n13196,5,297,2017-06-01 06:14:26,http://keebler.net/samanta.dicki,,7.13.247.206,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n13197,5,297,2017-03-11 11:27:34,http://pollich.com/alexis_morar,,130.127.28.156,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n13198,5,297,2017-05-16 02:21:46,http://carroll.name/camron.oreilly,,62.132.245.171,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n13199,5,297,2017-01-30 22:32:13,http://jenkins.io/ethel.jacobi,,90.13.56.198,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n13200,5,297,2017-02-27 06:38:17,http://westmcclure.biz/lea,,9.95.125.19,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n13201,5,297,2017-04-13 05:11:23,http://kris.org/dagmar,,169.67.61.23,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n13202,5,297,2017-04-22 12:49:54,http://hettingermcglynn.info/jorge,,12.3.148.9,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n13203,5,297,2017-05-28 22:38:38,http://zulaufledner.io/connie,,219.204.37.55,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n13204,5,297,2017-02-04 21:54:25,http://ziemeboyer.io/emile_cormier,,242.136.58.116,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n13205,5,297,2017-03-07 11:22:06,http://vandervort.info/patience,,196.217.253.83,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n13206,5,297,2017-03-20 14:38:31,http://parisianwaters.com/kiana.greenfelder,,18.234.216.135,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n13207,5,297,2017-03-15 05:44:26,http://block.name/euna.waters,,116.191.2.177,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n13208,5,297,2017-01-17 20:20:38,http://goyettehuels.name/cale,,210.185.143.108,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n13209,5,297,2017-03-14 19:32:39,http://kochtorp.name/al_bode,,7.253.93.145,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n13210,5,297,2017-02-21 11:15:40,http://mclaughlin.biz/karlee,,47.150.217.203,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n13211,5,298,2017-04-09 11:16:13,http://baumbachstehr.biz/ali_brakus,,134.96.33.185,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n13212,5,298,2017-02-19 23:08:23,http://johnsonwunsch.co/theo,,116.96.102.165,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n13213,5,298,2017-02-19 22:49:33,http://cormier.name/jamey.howe,,55.129.197.224,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n13214,5,298,2017-03-24 18:01:11,http://carroll.name/wiley,,12.211.168.56,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n13215,5,298,2017-03-07 23:28:36,http://kulas.com/chasity_mitchell,,98.118.79.235,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n13216,5,298,2017-01-12 20:38:07,http://howe.org/fatima.von,,80.173.2.25,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n13217,5,298,2017-01-15 12:39:07,http://hackett.net/felipe_hilll,,200.116.16.32,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n13218,5,298,2017-05-28 05:08:34,http://reichel.name/roxanne,,64.25.209.147,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n13219,5,298,2017-03-23 13:59:52,http://cormier.io/delpha_medhurst,,69.223.254.57,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n13220,5,298,2017-05-15 02:15:34,http://bauch.biz/frederic.cartwright,,129.47.209.228,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n13221,5,298,2017-05-10 19:00:44,http://jerdeschuppe.biz/esther_boyle,,20.205.43.63,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n13222,5,298,2017-06-02 09:45:34,http://davislarson.biz/bartholome,,175.58.13.106,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n13223,5,298,2017-04-05 09:07:52,http://halvorson.name/ahmad.stehr,,215.196.23.206,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n13224,5,298,2016-12-25 09:32:27,http://doylebrown.com/leopold,,21.168.108.87,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n13225,5,298,2017-02-02 16:07:25,http://collins.info/abel.berge,,26.250.121.45,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n13226,5,298,2017-01-05 06:52:08,http://champlinskiles.io/mark_predovic,,102.37.158.158,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n13227,5,298,2017-05-08 22:37:08,http://moriettenader.info/amely,,8.45.102.28,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n16192,6,366,2017-04-12 05:14:31,http://schulist.org/geovany_durgan,,2.60.154.3,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n16193,6,366,2017-02-08 01:50:11,http://ryancrooks.co/nathanial_prohaska,,38.135.84.133,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n16194,6,366,2017-02-12 17:37:11,http://ortiz.biz/dorris.blanda,,31.38.39.35,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n16195,6,366,2017-05-22 01:01:54,http://boehmlindgren.net/ashton_hettinger,,195.48.66.144,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n16196,6,366,2017-05-21 03:17:10,http://brown.org/jordan_weimann,,160.128.119.243,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n16197,6,366,2017-02-10 03:48:01,http://considinekunde.biz/amir,,240.40.135.193,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n16198,6,366,2016-12-29 05:02:30,http://macgyvergoodwin.io/aiyana,,165.68.96.20,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n16199,6,366,2017-04-25 15:34:03,http://harvey.io/kiley_lang,,106.202.41.84,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n16200,6,366,2017-05-07 13:49:52,http://will.name/timothy_kihn,,148.43.87.34,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n16201,6,366,2017-05-15 13:43:48,http://daniel.co/katherine,,47.209.161.88,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n16202,6,366,2017-01-01 10:43:50,http://williamson.org/izabella_kemmer,,161.63.170.43,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n16203,6,366,2017-05-24 17:03:58,http://pricetillman.net/cheyenne,,188.165.160.16,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n16204,6,366,2017-05-30 17:00:06,http://mayerthodkiewicz.name/mina,,32.140.40.4,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n16205,6,366,2017-02-16 14:00:42,http://cummings.info/alice_treutel,,41.253.244.176,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n16206,6,366,2017-04-10 16:38:35,http://legros.net/camilla,,107.215.232.15,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n16207,6,366,2017-02-13 10:24:34,http://kleinbradtke.biz/sophia,,240.64.165.79,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n16208,6,366,2017-06-03 21:32:42,http://cremin.org/abel_hermann,,63.32.28.218,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n16209,6,366,2017-03-25 20:46:48,http://langgutmann.biz/carolina,,9.94.207.92,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n16210,6,366,2017-04-27 02:41:52,http://aufderharhegmann.net/fatima.ruecker,,6.109.239.98,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n16211,6,366,2017-01-01 19:13:40,http://rodriguez.io/tiffany,,145.23.209.175,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n16212,6,366,2017-02-17 06:56:59,http://wilderman.info/gerson_schuppe,,73.22.57.77,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n16213,6,367,2016-12-31 00:42:38,http://hartmann.net/ellie,0.2692487466,79.176.47.13,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n16214,6,367,2017-01-11 14:31:10,http://kemmerauer.name/floie,0.5859551846,222.183.93.154,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n16215,6,367,2017-01-23 14:31:18,http://schultz.info/hailee,0.1031484475,156.249.79.12,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n16216,6,367,2017-05-23 02:53:32,http://hyattkuhn.io/kaela,0.6045939188,191.202.44.213,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n16217,6,367,2017-02-01 23:11:00,http://bradtkesteuber.biz/richard.medhurst,0.4428672649,172.83.17.37,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n16218,6,367,2017-02-01 22:21:53,http://franecki.co/nathan,0.6766504339,117.52.152.186,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n16219,6,367,2017-05-30 20:58:51,http://kubdonnelly.biz/maryse.keebler,0.8113703997,108.49.95.211,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n16220,6,367,2017-05-31 07:29:06,http://halvorson.biz/isabel,0.0903848355,229.203.73.132,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n16221,6,367,2017-04-16 14:05:31,http://emmerichweber.io/terrell,0.4802804429,53.96.23.40,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n16222,6,367,2017-04-08 14:48:23,http://bruenschumm.org/rory,0.9250272977,29.158.244.71,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n16223,6,367,2016-12-30 04:09:19,http://hartmann.net/jeanette_kuhic,0.2203697234,220.168.125.206,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n16224,6,367,2017-01-18 05:15:17,http://jenkins.com/dexter,0.1801383451,88.57.124.91,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n16225,6,367,2017-03-19 16:56:16,http://miller.org/humberto_heathcote,0.6116325775,161.177.132.29,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n16226,6,367,2017-06-12 07:07:19,http://welch.biz/coleman_denesik,0.0689314905,208.97.112.181,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n16227,6,367,2017-04-09 23:32:45,http://predovic.com/jameson_harris,0.8073426437,92.239.73.102,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n16228,6,367,2016-12-26 11:53:59,http://dickenserdman.net/nettie,0.9852326582,241.39.246.7,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n16229,6,367,2017-04-06 11:50:22,http://friesenupton.biz/chyna,0.6861870883,204.17.17.27,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n16230,6,367,2016-12-22 04:40:33,http://schulisthilpert.io/myrtle_reilly,0.3677931654,253.76.65.31,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n16231,6,367,2017-03-30 16:42:19,http://langworth.biz/eve.koepp,0.2350714299,225.150.43.192,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n16232,6,367,2017-02-11 14:27:29,http://bashirian.info/nathan,0.8061285148,83.141.117.26,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n16233,6,367,2016-12-26 12:58:29,http://jacobson.net/pascale,0.8695064620,2.216.162.28,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n16234,6,367,2017-02-07 12:16:27,http://ledner.com/hermann,0.6117577502,64.43.152.203,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n16235,6,367,2017-03-19 13:23:43,http://danielweinat.com/norberto,0.3989781794,98.233.243.154,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n16236,6,367,2017-05-14 08:47:22,http://pagac.name/darrion_leannon,0.1079601705,17.129.65.165,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n16237,6,367,2017-03-13 06:26:10,http://pagac.com/zachery,0.0097162892,43.233.157.245,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n16238,6,367,2017-02-15 21:05:50,http://dooleydaugherty.biz/merlin,0.1535962850,33.62.74.156,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n16239,6,367,2017-01-24 01:54:19,http://hane.com/lucy,0.4935618705,145.25.23.221,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n16240,6,368,2017-01-19 13:18:24,http://stanton.io/myron,0.0623187218,131.154.239.251,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n16241,6,368,2017-06-14 02:48:49,http://wisozk.name/selmer,0.9338420034,95.159.156.158,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n16242,6,368,2017-05-28 17:26:21,http://kuhic.io/burley,0.5808360322,123.229.69.201,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n16243,6,368,2017-05-31 06:01:46,http://kovacek.info/frida_ritchie,0.9116740531,143.181.3.95,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n16244,6,368,2017-03-19 20:39:12,http://oberbrunner.io/shanie,0.6303565479,3.167.186.94,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n3370,2,75,2017-04-17 13:08:10,http://deckowwehner.info/ebony,0.1469519343,8.45.253.98,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n3371,2,75,2017-03-21 20:02:18,http://hyatt.org/bobby,0.4744092855,233.87.213.218,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n3372,2,75,2017-04-19 17:03:47,http://schultz.co/mariah,0.9903107904,248.175.37.5,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n3373,2,75,2016-12-14 21:25:11,http://lynch.org/angelita,0.5126503763,228.11.20.148,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n3374,2,75,2017-01-02 19:16:26,http://hintzgrady.co/orion,0.0017227968,254.246.141.154,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n3375,2,75,2017-05-26 10:28:40,http://kerluke.co/brice,0.4613338446,132.211.35.180,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n3376,2,75,2017-05-11 14:04:13,http://weimann.io/hailee.goldner,0.5096386726,15.190.62.57,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n3377,2,75,2017-01-10 16:06:23,http://strosin.com/ulises.stracke,0.6477960298,105.248.164.10,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n3378,2,75,2017-03-20 00:06:28,http://white.com/rahul,0.3314935286,29.186.96.3,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n3379,2,75,2017-05-26 23:26:29,http://bahringer.net/mackenzie,0.3641569231,179.246.105.51,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n3380,2,75,2017-04-04 08:20:25,http://brown.info/cecilia_abshire,0.4929411665,114.150.141.108,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n3381,2,75,2017-01-06 18:38:36,http://blanda.name/ruth,0.9543421790,171.164.218.202,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n3382,2,75,2017-01-17 10:56:18,http://oreillypollich.co/jeica,0.6938371394,35.209.17.113,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n3383,2,75,2017-01-21 04:36:47,http://boyle.net/matteo_wintheiser,0.2772141060,192.29.43.24,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n3384,2,75,2016-12-28 10:17:08,http://gulgowskihuels.io/leonora,0.5667267704,15.137.160.63,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n3385,2,75,2017-02-09 17:01:38,http://turner.net/fritz,0.6711068506,247.211.93.5,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n3386,2,75,2017-05-07 05:46:18,http://luettgenbayer.org/dejuan,0.4758818277,252.128.254.173,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n3387,2,75,2017-01-14 23:23:36,http://bahringer.co/clint.hoppe,0.8600543533,6.155.191.169,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n3388,2,75,2017-04-07 05:31:38,http://wunsch.org/mauricio.aufderhar,0.3442423088,51.188.151.8,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n3389,2,75,2017-05-08 09:02:58,http://weberbayer.name/maxwell.rogahn,0.6128888803,201.190.134.26,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n3390,2,75,2017-04-26 08:51:34,http://kutch.info/brennan_bergstrom,0.5683008262,198.168.103.74,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n3391,2,75,2017-04-25 01:42:27,http://welch.net/lexi.schmitt,0.1952069273,87.49.90.152,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n3392,2,75,2017-03-08 19:18:59,http://cainparker.io/fannie,0.2908365463,87.137.232.101,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n3393,2,75,2017-04-03 16:12:41,http://erdman.io/lon.crist,0.7183839901,166.220.98.211,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n3394,2,75,2017-04-04 00:47:40,http://haleycruickshank.net/talia_jacobs,0.4320038066,54.158.50.99,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n3395,2,75,2017-01-19 04:45:38,http://kutch.biz/alek,0.1289284683,219.62.109.66,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n3396,2,75,2017-03-25 11:07:19,http://mann.com/marian,0.6163136485,43.242.52.177,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n3397,2,75,2017-01-02 08:44:39,http://schiller.biz/shanna,0.4999624490,128.43.151.249,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n3398,2,75,2017-01-21 00:02:31,http://rutherford.co/alfonzo_okeefe,0.8018986088,226.2.193.103,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n3399,2,75,2017-04-21 21:46:41,http://farrell.biz/eloy,0.8175547102,167.94.46.190,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n3400,2,75,2017-01-28 17:43:24,http://kilback.com/brandi,0.8880545793,165.205.135.224,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n3401,2,75,2017-05-01 19:36:52,http://terry.name/davonte,0.4886291504,126.137.95.80,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n3402,2,75,2017-01-08 20:40:45,http://reichert.com/lura,0.9979804867,230.175.226.193,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n3403,2,75,2017-05-18 19:47:27,http://marks.co/carli,0.4168628322,134.83.37.40,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n3404,2,75,2017-01-24 00:20:12,http://williamsonstehr.org/prudence,0.3603267049,149.206.197.82,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n3405,2,75,2017-04-26 01:02:17,http://bogisichveum.net/alex_gutkowski,0.3095471375,56.97.83.82,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n3406,2,75,2017-02-09 22:26:10,http://sauer.io/michelle.heidenreich,0.0042031375,118.131.210.58,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n3407,2,75,2017-04-14 22:39:03,http://oberbrunnerkuhlman.org/else,0.1513546666,27.201.189.83,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n3408,2,75,2017-05-19 17:07:58,http://king.co/jadyn,0.0951166032,224.105.166.230,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n3409,2,75,2017-02-19 07:38:18,http://grady.biz/itzel,0.9850280318,223.214.57.185,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n3410,2,75,2017-03-21 07:18:35,http://roberts.biz/avery,0.5407842795,188.37.175.109,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n3411,2,75,2017-02-20 14:50:31,http://beier.co/delmer.abbott,0.2605207416,48.210.93.176,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n3412,2,75,2017-03-14 23:16:25,http://jacobsgerhold.biz/nickolas.fadel,0.6147114645,124.103.55.48,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n3413,2,75,2017-03-16 06:27:37,http://schneider.com/freddie,0.6997591781,36.72.202.80,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n3414,2,75,2017-03-25 02:25:21,http://durgan.name/celine_anderson,0.6713381605,110.222.175.38,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n3415,2,75,2016-12-28 17:36:35,http://schroederwalker.org/rosie_king,0.1994014263,241.122.62.91,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n3416,2,75,2017-04-30 08:05:18,http://glover.io/beverly,0.9307711344,230.109.16.19,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n3417,2,75,2017-02-12 00:49:25,http://sipes.com/cary,0.3787116741,47.101.67.125,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n3418,2,75,2017-01-25 21:43:27,http://purdy.name/davon_kaulke,0.4997361024,253.188.172.157,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n3419,2,75,2017-02-05 03:04:35,http://hirthe.org/arnold,0.3729473352,155.98.202.102,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n3420,2,75,2017-03-24 02:31:26,http://simonisheaney.co/ashton.koepp,0.1093954466,175.55.133.105,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n3421,2,75,2017-01-12 05:55:28,http://monahanwest.co/giovanni_blick,0.2944820783,18.147.79.194,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n10302,4,232,2017-04-24 05:05:48,http://connelly.net/johnathan.pacocha,0.5766852254,116.218.67.69,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n10303,4,232,2017-02-06 09:14:27,http://wunsch.co/delpha_treutel,0.8754796255,141.105.242.77,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n10304,4,232,2017-04-17 04:57:29,http://ebert.info/giuseppe_marquardt,0.9666389303,62.151.196.222,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n10305,4,232,2017-02-14 17:43:34,http://waelchi.co/aliyah,0.5816607737,193.52.132.243,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n10306,4,232,2017-05-23 23:27:06,http://hellercain.org/terry.champlin,0.6977502523,130.133.164.54,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n10307,4,232,2017-05-24 15:27:36,http://boyer.co/trent_stracke,0.3737416558,124.89.95.119,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n10308,4,232,2017-04-09 14:24:38,http://pollichfeeney.name/reyna_maggio,0.5165004498,156.89.211.80,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n10309,4,232,2017-05-28 01:09:11,http://gibson.co/bailee,0.5418506277,211.13.140.192,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n10310,4,232,2016-12-26 09:08:46,http://ankundingaltenwerth.biz/ceasar.prohaska,0.1003636675,125.16.9.117,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n10311,4,232,2017-05-17 12:22:40,http://adams.biz/nathen.miller,0.2299884632,127.195.248.179,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n10312,4,232,2016-12-26 03:26:09,http://goldnerking.co/esperanza,0.7098713578,117.158.162.5,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n10313,4,232,2017-05-06 22:12:51,http://emmerich.net/isabel,0.4491685494,27.226.29.239,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n10314,4,232,2017-03-12 08:29:54,http://monahanhirthe.io/javonte.nolan,0.4315295130,72.156.122.52,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n10315,4,232,2017-03-10 15:43:33,http://lueilwitz.io/arlie.torphy,0.6968920862,38.92.131.122,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n10316,4,232,2017-05-09 08:42:08,http://champlin.org/ana_kirlin,0.6166241244,130.221.86.105,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n10317,4,232,2017-05-14 12:56:57,http://gusikowski.com/jorge,0.7884550821,156.14.220.246,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n10318,4,232,2017-05-24 05:30:14,http://windler.net/cesar,0.1915814112,84.68.221.81,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n10319,4,232,2017-04-22 23:51:58,http://mcglynn.biz/kaela_mitchell,0.7476739414,47.128.193.97,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n10320,4,233,2016-12-15 09:54:08,http://schimmel.biz/kaci,0.5313860183,145.142.190.60,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n10321,4,233,2017-06-13 21:07:41,http://wizabode.net/annamarie.rogahn,0.5830613367,160.113.28.151,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n10322,4,233,2017-06-12 17:39:35,http://oconnell.io/luna,0.9171928629,9.50.189.116,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n10323,4,233,2017-06-13 13:31:37,http://ebert.org/branson,0.4159324368,171.202.165.119,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n10324,4,233,2017-04-12 00:45:56,http://lang.org/christiana.gulgowski,0.4169997180,159.5.72.75,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n10325,4,233,2017-06-09 18:20:49,http://terry.io/wilhelm.hermiston,0.5714368566,138.8.47.238,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n10326,4,233,2017-04-13 05:08:48,http://hagenes.co/eladio_macejkovic,0.7727668863,144.17.154.234,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n10327,4,233,2017-01-08 19:39:11,http://metz.org/imani.jacobs,0.4223402053,208.229.222.223,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n10328,4,233,2017-02-23 13:42:39,http://beerkonopelski.biz/lawson_becker,0.9797869022,32.207.6.95,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n10329,4,233,2017-03-07 20:21:46,http://bauch.info/roxane.wyman,0.5430061440,109.15.115.241,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n10330,4,233,2017-04-12 21:39:13,http://murray.name/samson,0.7243304301,203.10.65.9,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n10331,4,233,2017-02-24 02:27:55,http://crist.co/zella.leffler,0.8001750229,18.5.127.184,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n10332,4,233,2017-03-02 07:06:01,http://schuster.org/david_collier,0.0699062022,28.108.73.63,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n10333,4,233,2017-02-11 17:31:54,http://hermistonoconnell.info/arno,0.6807600965,21.156.71.218,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n10334,4,233,2017-03-24 02:16:56,http://bosco.co/retta,0.0589956718,145.82.54.97,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n10335,4,233,2017-04-13 01:19:31,http://okon.info/emile,0.7686083101,150.39.40.42,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n10336,4,233,2017-06-12 12:12:50,http://ebertprohaska.name/myron_mcdermott,0.4785455068,44.191.139.5,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n10337,4,233,2017-06-06 05:10:53,http://koepp.org/sherwood.mosciski,0.4359782585,207.21.103.89,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n10338,4,233,2017-06-13 10:20:02,http://rath.info/nella.stamm,0.9933801499,85.55.40.230,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n10339,4,233,2017-02-23 13:37:29,http://feest.co/alexander,0.5582695376,60.33.84.162,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n10340,4,233,2017-04-16 18:57:08,http://hahn.name/bette_rodriguez,0.5256977540,99.198.60.40,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n10341,4,233,2017-04-03 02:17:15,http://croninframi.net/peter.johnson,0.6670274192,12.138.82.121,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n10342,4,233,2017-04-17 21:21:24,http://quitzon.biz/dana_rippin,0.9143234167,62.148.139.173,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n10343,4,233,2017-01-12 04:13:21,http://ebert.biz/bruce,0.8995920803,27.55.144.248,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n10344,4,233,2017-06-08 11:27:58,http://fadel.com/lura,0.7067520331,31.79.227.213,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n10345,4,233,2017-02-12 13:16:40,http://tromp.com/petra,0.3668838269,193.56.62.17,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n10346,4,233,2017-03-30 14:00:29,http://bruen.co/eli.hansen,0.5698399915,221.65.252.13,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n10347,4,233,2017-04-11 23:56:23,http://schiller.net/pierre,0.7834002952,173.57.246.85,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n10348,4,233,2017-03-08 11:18:01,http://willmskerluke.org/tremaine,0.0247925768,220.245.227.174,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n10349,4,233,2016-12-27 02:08:56,http://wuckert.name/alden,0.1347947862,61.122.32.131,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n10350,4,233,2016-12-28 01:28:06,http://skilescartwright.org/furman.cruickshank,0.0855490202,134.113.6.11,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n10351,4,233,2017-04-06 03:18:55,http://pouroslindgren.io/janis,0.0636159031,79.165.96.33,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n10352,4,233,2017-03-29 15:42:15,http://stiedemann.io/quinn,0.1727632817,121.223.105.159,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n7318,3,163,2017-05-16 19:06:40,http://gaylord.biz/audie,,33.70.52.131,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n7319,3,163,2017-02-27 15:00:25,http://jakubowski.biz/jolie_waters,,151.55.16.129,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n7320,3,163,2017-03-07 02:41:10,http://cartwrightschroeder.co/aleandra.parisian,,129.46.139.115,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n7321,3,163,2017-01-26 07:56:08,http://veumheidenreich.info/jeika,,34.4.63.123,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n7322,3,163,2017-05-26 03:07:27,http://kohler.net/rodger.ko,,169.123.41.3,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n7323,3,163,2017-02-15 11:11:21,http://stracke.info/treva,,42.154.126.55,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n7324,3,163,2017-04-20 15:01:12,http://hettinger.com/heath,,141.84.205.106,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n7325,3,163,2017-03-19 03:05:04,http://okeefeanderson.com/madeline,,75.195.242.86,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n7326,3,163,2017-04-02 11:56:04,http://treutel.name/tanya.walsh,,7.101.58.148,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n7327,3,163,2017-05-25 20:59:10,http://prosacco.info/evert,,128.59.209.129,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n7328,3,163,2017-04-06 23:11:59,http://greenholtlueilwitz.info/luna,,62.110.170.69,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n7329,3,163,2017-01-06 09:18:26,http://casper.org/myles_schmidt,,155.200.102.84,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n7330,3,163,2016-12-19 21:39:32,http://huelswolf.info/pascale_howell,,130.54.245.243,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n7331,3,163,2017-03-06 12:55:11,http://gorczany.org/hillard.harvey,,114.67.44.174,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n7332,3,163,2017-04-17 14:29:30,http://trantow.com/helena,,39.66.47.158,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n7333,3,163,2017-02-04 04:35:11,http://conn.org/delia_cummings,,190.92.184.27,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n7334,3,163,2017-03-22 03:41:37,http://boscoko.info/addison,,214.245.208.137,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n7335,3,163,2017-01-28 09:33:47,http://mayer.com/arden,,229.177.4.211,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n7336,3,163,2017-06-04 03:09:33,http://bernhard.com/isaias.champlin,,240.10.8.62,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n7337,3,163,2017-02-26 08:39:38,http://ondrickako.co/elenor_volkman,,170.79.145.43,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n7338,3,163,2017-03-06 19:43:12,http://breitenberg.co/immanuel,,237.65.167.244,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n7339,3,163,2017-01-09 14:24:11,http://mannconroy.info/jacques,,251.190.124.42,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n7340,3,163,2017-02-27 01:46:04,http://bartell.co/juana.jacobs,,126.24.135.108,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n7341,3,163,2017-03-11 09:22:56,http://streich.info/kendall,,253.176.227.153,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n7342,3,163,2017-01-09 00:09:31,http://smithdenesik.biz/tyra.bayer,,18.211.118.150,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n7343,3,163,2017-03-07 16:28:14,http://macgyver.io/maxime.emard,,78.29.208.186,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n7344,3,163,2017-04-27 14:46:13,http://schmitt.org/addie_wunsch,,87.193.175.243,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n7345,3,163,2017-02-27 09:24:48,http://johnsonmclaughlin.name/ezequiel,,33.11.63.116,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n7346,3,163,2017-04-11 10:24:16,http://gerhold.com/yeenia.sipes,,153.154.98.160,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n7347,3,163,2017-04-04 04:54:52,http://trantow.org/tyrese,,214.149.222.235,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n7348,3,163,2017-03-22 21:03:33,http://schowalter.org/kyla,,147.90.242.220,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n7349,3,163,2017-01-05 17:12:25,http://considine.name/jaime,,212.72.40.93,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n7350,3,163,2017-01-01 17:28:19,http://ferry.net/shirley,,206.83.251.237,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n7351,3,163,2017-06-03 19:53:50,http://mayermarks.io/edmund_altenwerth,,72.58.35.112,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n7352,3,163,2017-02-21 18:09:32,http://baileyfahey.io/jayne.heller,,7.96.10.46,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n7353,3,163,2017-03-22 11:18:47,http://hintz.name/joanne_casper,,44.83.191.9,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n7354,3,163,2017-02-19 08:01:27,http://prosacco.name/elwin.metz,,187.126.67.2,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n7355,3,163,2016-12-21 03:31:48,http://hermannkerluke.org/elias,,111.44.204.120,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n7356,3,163,2017-03-04 15:33:45,http://torp.net/caidy,,241.191.164.53,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n7357,3,163,2017-04-17 13:14:18,http://rosenbaum.co/lorine_schultz,,248.117.131.21,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n7358,3,163,2017-01-08 09:13:58,http://maggio.org/bettye,,65.162.87.226,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n7359,3,163,2017-04-19 03:04:14,http://simonis.name/keshawn,,165.151.107.87,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n7360,3,163,2017-06-11 06:24:51,http://hermannfranecki.name/myrtie_koelpin,,221.85.186.107,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n7361,3,163,2017-01-19 09:37:04,http://oberbrunnerbins.net/anais,,70.93.67.137,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n7362,3,163,2017-05-30 22:14:01,http://schowalter.co/elenora,,57.140.166.174,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n7363,3,163,2017-04-11 15:43:01,http://kleinwhite.net/muhammad,,150.247.16.119,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n7364,3,163,2017-03-25 19:54:06,http://howe.name/casandra.kling,,12.13.63.138,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n7365,3,163,2017-05-24 11:07:43,http://funk.name/evie,,159.179.127.151,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n7366,3,163,2017-02-09 00:04:25,http://conn.io/andres,,144.98.95.92,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n7367,3,163,2017-01-20 18:36:30,http://hansen.co/kaitlin_rempel,,212.94.11.215,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n7368,3,163,2017-04-07 10:48:45,http://klein.com/teresa,,251.207.254.166,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n7369,3,163,2017-03-02 10:19:54,http://schmeler.name/lorna,,72.175.102.16,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n7370,3,163,2017-02-16 03:49:20,http://steuberkonopelski.net/kareem,,209.125.167.89,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n7371,3,163,2017-01-12 11:41:28,http://nienowbosco.io/jasen.balistreri,,57.216.97.173,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n7372,3,163,2017-06-02 23:27:02,http://quigley.biz/declan,,159.42.25.103,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n13228,5,298,2017-04-08 00:08:11,http://moen.info/dorothea,,243.230.53.181,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n13229,5,298,2017-05-29 23:15:21,http://harber.biz/jakob.lynch,,223.71.101.19,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n13230,5,298,2017-02-24 08:57:55,http://schinner.biz/odea.crona,,99.236.248.233,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n13231,5,298,2017-04-04 14:04:03,http://metz.info/kyler.stokes,,111.17.65.104,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n13232,5,298,2017-01-21 17:20:19,http://barton.com/charles_harvey,,19.15.46.46,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n13233,5,298,2017-01-08 12:28:48,http://denesikgorczany.co/tevin.muller,,94.165.35.5,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n13234,5,298,2017-04-14 18:05:49,http://brekkeswaniawski.info/alyce,,124.230.83.23,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n13235,5,298,2017-05-05 19:27:13,http://stroman.org/casandra,,66.92.253.159,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n13236,5,298,2017-04-10 05:19:23,http://harrisking.co/norene.rempel,,191.175.249.246,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n13237,5,298,2017-03-14 05:32:36,http://okon.org/violette.bahringer,,215.107.121.149,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n13238,5,298,2017-02-19 13:05:06,http://harris.biz/lonzo.mertz,,163.247.161.134,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n13239,5,298,2017-03-13 23:44:28,http://crist.net/conner_gerhold,,227.192.221.23,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n13240,5,298,2017-02-05 12:11:07,http://osinski.co/barry,,93.23.237.129,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n13241,5,298,2017-03-01 04:01:20,http://powlowski.co/alverta,,8.220.70.253,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n13242,5,298,2017-04-14 02:21:34,http://kemmer.name/warren,,121.140.162.122,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n13243,5,298,2017-02-27 09:16:45,http://lueilwitz.info/paxton.wolff,,8.44.11.148,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n13244,5,298,2017-01-25 22:40:20,http://runolfsdottir.info/walker.murazik,,54.39.37.101,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n13245,5,298,2017-05-04 07:03:57,http://starkwalker.info/adriel,,105.212.94.223,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n13246,5,298,2017-02-25 14:06:47,http://kshlerinbruen.com/craig.mante,,195.149.125.111,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n13247,5,298,2016-12-20 23:08:12,http://kohler.com/boris,,143.58.114.143,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n13248,5,298,2017-03-07 17:01:17,http://hudson.co/tommie_renner,,82.154.205.164,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n13249,5,298,2017-01-04 20:39:29,http://romaguera.name/baron_powlowski,,190.42.157.162,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n13250,5,298,2017-01-31 19:38:21,http://boyer.net/deshawn,,247.28.182.217,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n13251,5,298,2017-04-30 12:23:13,http://greenholtwiegand.co/keagan,,94.201.129.23,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n13252,5,298,2017-04-10 07:58:13,http://spinkaconnelly.biz/brando.orn,,73.13.141.246,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n13253,5,298,2017-02-11 06:51:26,http://pfefferjenkins.com/johnathan.effertz,,69.229.228.59,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n13254,5,298,2017-03-08 06:33:14,http://dibbert.co/keenan_greenfelder,,166.207.42.169,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n13255,5,298,2017-04-18 17:58:31,http://stantonmertz.info/dock_nolan,,233.244.53.103,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n13256,5,298,2016-12-29 21:05:10,http://graham.name/rudolph,,20.151.225.193,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n13257,5,298,2017-04-27 14:05:04,http://moen.org/jorge_ko,,98.105.127.223,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n13258,5,298,2017-02-07 17:28:07,http://towne.biz/baylee,,10.151.108.162,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n13259,5,298,2017-05-22 09:43:22,http://emard.biz/zoe,,182.205.252.146,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n13260,5,298,2017-05-05 12:07:44,http://goyette.biz/theron,,50.29.163.62,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n13261,5,298,2017-02-23 08:17:47,http://kovacek.name/eric_halvorson,,123.122.237.66,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n13262,5,298,2017-06-07 14:30:07,http://farrell.name/garnet,,38.157.181.209,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n13263,5,298,2017-02-17 08:27:32,http://legros.net/bethany_funk,,96.32.30.21,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n13264,5,298,2017-05-08 19:18:57,http://bauch.biz/morris,,159.27.192.127,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n13265,5,298,2017-04-20 21:04:03,http://connpowlowski.org/nelle_keler,,167.131.98.27,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n13266,5,298,2017-02-21 13:57:54,http://waelchi.io/damien.wyman,,144.167.203.141,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n13267,5,298,2017-01-07 12:45:56,http://walsh.biz/hellen,,43.81.65.165,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n13268,5,298,2016-12-24 08:13:51,http://stammpfeffer.info/deron_gutmann,,216.136.174.49,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n13269,5,298,2017-05-25 04:09:31,http://nolan.com/jacynthe,,178.248.140.187,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n13270,5,298,2017-01-06 13:50:17,http://crist.com/georgiana_schimmel,,23.114.112.76,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n13271,5,299,2017-06-02 19:58:52,http://reichel.io/ralph.gislason,,212.243.170.229,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n13272,5,299,2017-02-03 18:23:53,http://hettingerschmeler.org/pamela.jerde,,206.165.235.136,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n13273,5,299,2017-05-26 23:48:46,http://bartoletti.org/kameron,,218.78.254.136,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n13274,5,299,2017-02-10 11:39:16,http://wolff.org/citlalli.west,,164.36.238.52,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n13275,5,299,2017-02-21 06:24:27,http://anderson.info/carson,,77.80.22.226,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n13276,5,299,2017-05-01 19:11:49,http://bailey.com/murray,,128.239.117.117,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n13277,5,299,2017-04-04 21:24:42,http://marquardtjones.name/melyna,,8.177.90.237,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n13278,5,299,2017-02-19 09:54:25,http://lehner.io/charity,,244.128.98.196,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n13279,5,299,2017-05-19 10:41:52,http://orn.com/frida,,84.13.127.115,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n13280,5,299,2017-05-25 03:22:55,http://jakubowskiquitzon.info/melisa,,93.211.177.187,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n13281,5,299,2017-05-15 07:10:57,http://pfeffer.name/lukas_little,,175.108.65.88,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n13282,5,299,2017-03-23 09:38:32,http://doyle.net/maci_spinka,,113.251.200.230,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n13283,5,299,2017-05-05 14:06:00,http://schultz.name/shanna,,247.88.152.171,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n16245,6,368,2017-01-15 13:30:44,http://okunevaebert.io/haan_block,0.9873102921,38.238.168.135,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n16246,6,368,2017-02-28 01:23:41,http://uptonlarson.org/aubrey_mitchell,0.9463942891,95.191.88.166,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n16247,6,368,2017-04-29 07:07:14,http://kris.net/ruel_gulgowski,0.9389584342,40.207.202.158,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n16248,6,368,2016-12-25 02:46:48,http://wymanmarks.name/cade_goodwin,0.8300843886,46.247.210.238,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n16249,6,368,2017-05-09 08:45:26,http://franecki.net/ford_king,0.2234202148,106.94.34.17,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n16250,6,368,2017-03-08 05:44:37,http://batzmohr.name/armando.hauck,0.9120070570,63.242.161.113,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n16251,6,368,2016-12-14 10:43:08,http://wolffsmitham.io/deja,0.9168017643,79.191.198.77,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n16252,6,368,2017-03-05 19:03:26,http://davisgerlach.co/travon,0.4698140909,17.18.210.241,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n16253,6,368,2017-06-13 12:02:37,http://durganabbott.io/santos.stark,0.4194204107,113.240.52.180,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n16254,6,368,2017-03-20 04:49:01,http://kreiger.io/myrtis_romaguera,0.3903464819,132.242.188.17,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n16255,6,368,2017-01-23 17:39:02,http://marquardtboyer.net/jewell,0.3145347043,14.199.221.217,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n16256,6,368,2017-04-03 16:01:41,http://pagac.biz/lester.farrell,0.1280220352,180.231.106.123,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n16257,6,368,2017-06-03 05:35:57,http://kirlin.co/foster,0.6597787251,109.117.98.56,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n16258,6,368,2017-02-01 03:55:06,http://johnston.name/maximus,0.0701680996,42.73.35.25,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n16259,6,368,2017-01-10 10:07:38,http://douglasbraun.biz/justyn_hills,0.6346064770,9.168.186.131,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n16260,6,369,2017-04-28 04:15:53,http://zieme.net/jonathon,0.5754439774,73.162.4.239,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n16261,6,369,2017-01-30 10:14:29,http://hettingergrant.io/ashly,0.0337277039,176.242.108.11,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n16262,6,369,2017-06-01 02:16:59,http://koch.name/lawrence,0.4724159794,151.96.71.161,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n16263,6,369,2017-04-09 12:08:26,http://rempelhammes.name/watson.rippin,0.3835698840,158.243.97.236,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n16264,6,369,2017-06-08 16:30:52,http://durgan.io/lyda.quitzon,0.2700187305,126.250.198.58,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n16265,6,369,2017-04-15 22:01:58,http://green.co/brain_aufderhar,0.0113180537,248.148.233.47,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n16266,6,369,2017-03-25 03:00:02,http://dietrich.biz/osborne_jast,0.1454255094,142.172.194.56,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n16267,6,369,2017-02-03 19:45:33,http://von.info/imogene_fay,0.4464987838,86.80.74.138,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n16268,6,369,2017-05-05 23:30:51,http://donnellyluettgen.biz/dedrick,0.5072231450,70.181.252.29,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n16269,6,369,2017-05-19 19:43:10,http://rohan.io/shannon.kulas,0.9281805122,32.26.184.232,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n16270,6,369,2017-04-14 14:56:17,http://gulgowski.co/miguel_stroman,0.4453845040,152.133.46.226,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n16271,6,369,2017-05-11 11:40:41,http://champlinherman.biz/alyce,0.6091485431,114.234.105.227,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n16272,6,369,2017-02-09 01:54:41,http://olsonschuster.net/jalen,0.0990943742,14.54.135.24,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n16273,6,369,2016-12-24 14:34:12,http://buckridge.biz/letitia,0.7071240623,216.55.139.160,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n16274,6,369,2017-05-18 11:43:56,http://sipes.co/celestino.dietrich,0.6597806414,21.210.239.237,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n16275,6,369,2017-05-16 16:28:44,http://buckridgedooley.name/rahsaan.damore,0.3605297457,64.160.110.192,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n16276,6,369,2017-01-14 22:10:42,http://schulist.com/duane.runolfsdottir,0.1473741185,69.246.12.226,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n16277,6,369,2017-05-19 15:57:08,http://leuschkemurazik.info/virgil_bosco,0.6482213889,87.153.64.178,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n16278,6,369,2017-01-10 18:53:04,http://mante.org/jennyfer.farrell,0.2032017631,193.43.187.146,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n16279,6,369,2016-12-27 21:25:47,http://douglas.name/kameron_connelly,0.1370963645,4.8.209.139,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n16280,6,369,2017-02-18 05:45:08,http://trantow.biz/octavia,0.3486724951,160.228.220.133,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n16281,6,369,2017-02-07 04:01:54,http://blockcarter.name/emmanuel_hilll,0.4935620530,26.120.214.46,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n16282,6,369,2017-04-03 10:55:34,http://osinski.biz/maximillian_bradtke,0.9567287730,219.170.102.57,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n16283,6,369,2017-02-20 10:42:36,http://bayer.biz/crawford,0.4517583520,83.102.37.189,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n16284,6,369,2017-04-16 02:16:22,http://klocko.biz/newton_moore,0.7263436141,5.114.134.155,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n16285,6,369,2017-06-06 22:58:32,http://hermanntorphy.com/sandy.erdman,0.3447338173,66.203.37.51,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n16286,6,369,2017-05-07 08:05:38,http://hamillnitzsche.info/aimee_goodwin,0.5688188248,180.239.29.43,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n16287,6,369,2017-04-26 14:54:32,http://blick.org/charley,0.1654159243,45.84.112.9,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n16288,6,369,2017-05-09 14:43:27,http://mckenzie.net/ena.cartwright,0.1180147275,160.170.252.134,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n16289,6,369,2017-04-15 12:18:07,http://fritsch.org/jacky,0.9635111083,248.167.159.21,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n16290,6,369,2017-05-04 04:21:45,http://ward.com/burley,0.3126057709,179.138.48.9,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n16291,6,369,2017-04-08 15:53:46,http://braun.org/lorenzo,0.0739755123,150.252.100.87,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n16292,6,369,2017-02-28 13:44:30,http://considine.name/reagan_watsica,0.3183597835,100.154.164.54,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n16293,6,369,2017-05-11 18:12:11,http://zboncak.info/chauncey,0.4118755984,70.224.122.93,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n16294,6,369,2017-01-02 13:22:30,http://connelly.io/franz_bernhard,0.9940413354,185.169.68.52,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n16295,6,369,2017-04-23 20:03:01,http://osinski.org/ford,0.1315041406,49.40.117.213,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n3422,2,75,2017-02-23 23:38:37,http://koepp.info/felipa_grant,0.3790825185,81.172.227.32,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n3423,2,75,2017-03-08 15:55:39,http://daniel.co/joanne,0.6407551796,42.127.115.130,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n3424,2,75,2016-12-14 23:25:15,http://schustersawayn.org/verda,0.3184199098,89.204.60.235,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n3425,2,75,2017-05-07 21:35:57,http://ferry.co/ned,0.2626383275,118.9.92.137,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n3426,2,75,2017-05-25 15:30:07,http://lindgren.info/alisa_kuhic,0.8839290630,159.192.142.217,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n3427,2,75,2017-02-06 05:19:30,http://pricedietrich.info/adolphus_mraz,0.9491762344,6.19.183.245,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n3428,2,75,2016-12-29 23:21:02,http://medhurst.io/imani.trantow,0.6055260922,21.239.210.51,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n3429,2,75,2017-02-28 15:07:15,http://rohan.io/mohamed.osinski,0.4424948208,245.251.195.55,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n3430,2,75,2017-03-23 21:28:23,http://runte.io/floie.windler,0.5805570890,200.92.243.37,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n3431,2,76,2017-02-22 08:09:47,http://hanesmitham.io/emily.reilly,0.9226101750,237.157.126.238,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n3432,2,76,2017-04-01 04:45:13,http://johns.org/viviane,0.7864294932,27.178.106.221,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n3433,2,76,2017-02-07 03:26:30,http://mckenzieritchie.info/sigurd,0.5753753195,227.250.120.216,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n3434,2,76,2017-04-28 18:31:45,http://wiegand.org/gertrude.schinner,0.6983172262,183.3.215.229,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n3435,2,76,2017-03-01 14:02:05,http://bailey.biz/verdie.brown,0.7047169300,205.157.141.233,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n3436,2,76,2017-04-02 09:38:07,http://tremblay.org/christine.mayert,0.3366553677,46.98.20.40,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n3437,2,76,2017-05-30 17:06:49,http://rippin.info/elnora_weber,0.9609882074,186.56.183.11,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n3438,2,76,2017-02-20 13:26:40,http://treutel.com/lawson,0.9559764240,208.195.59.250,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n3439,2,76,2017-05-23 02:32:28,http://cartertorphy.info/art.runolfsdottir,0.3098590675,252.130.45.113,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n3440,2,76,2017-04-07 16:36:58,http://raupredovic.info/zion,0.1779518690,148.231.120.51,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n3441,2,76,2017-04-19 16:27:27,http://konopelskischimmel.io/alisa,0.5441564397,34.91.3.29,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n3442,2,76,2017-01-26 00:02:40,http://wehner.org/wayne.wehner,0.9183848694,164.152.16.5,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n3443,2,76,2016-12-24 00:06:38,http://weinat.com/gertrude,0.2711227337,180.69.102.158,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n3444,2,76,2017-03-14 22:59:22,http://hickle.io/lambert,0.2294650904,243.89.235.64,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n3445,2,76,2017-05-25 03:18:19,http://kuphal.biz/kenyatta.frami,0.2359243707,201.46.216.193,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n3446,2,76,2017-03-13 17:39:55,http://lindfadel.name/bella,0.2325263309,153.111.253.194,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n3447,2,76,2017-04-02 04:53:17,http://shanahanlittel.co/claire.bergstrom,0.4329497801,61.138.31.215,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n3448,2,76,2017-04-11 13:36:02,http://ko.info/kurt,0.7719690706,75.39.25.148,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n3449,2,76,2017-03-04 18:45:25,http://wisoky.info/cordia,0.2241097954,137.6.143.20,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n3450,2,76,2017-04-18 13:24:03,http://mayercremin.com/fritz.lowe,0.3311455758,85.178.152.24,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n3451,2,76,2017-04-27 01:39:02,http://effertz.com/ivy.torp,0.4186087980,152.218.84.8,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n3452,2,76,2017-06-01 20:13:07,http://wardmcglynn.name/winnifred_kuhlman,0.0715451990,50.206.41.30,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n3453,2,76,2017-01-05 16:10:52,http://rohanlehner.name/shawn,0.7155945608,250.35.245.45,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n3454,2,76,2017-01-28 08:56:29,http://wintheiser.io/adrian,0.7955898943,78.175.231.8,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n3455,2,76,2017-01-21 16:55:22,http://larson.com/eliezer_pouros,0.2565055680,38.237.216.237,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n3456,2,76,2017-03-11 18:17:55,http://okuneva.biz/izabella.boehm,0.2585407048,97.149.213.222,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n3457,2,76,2017-04-25 17:06:41,http://weber.info/lon,0.0031570732,7.26.151.182,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n3458,2,76,2017-03-28 22:10:26,http://vonrueden.name/saige,0.9540906608,134.193.75.114,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n3459,2,76,2017-05-16 02:55:02,http://wolff.biz/dina_bradtke,0.2825102295,13.125.157.200,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n3460,2,76,2017-02-14 06:14:13,http://heathcote.io/mose,0.7278688947,191.218.247.240,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n3461,2,76,2017-06-03 23:52:21,http://olson.io/mariana,0.3593013493,243.224.149.185,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n3462,2,76,2017-06-03 15:06:46,http://heathcotedeckow.biz/autumn.runolfsdottir,0.5345384645,142.113.5.240,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n3463,2,76,2017-04-15 03:21:51,http://hellerstehr.io/nola.bergnaum,0.6920942458,102.212.47.146,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n3464,2,76,2017-02-19 08:41:54,http://kaulkehermann.co/flavio_parker,0.1038661566,95.99.240.153,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n3465,2,76,2017-04-28 09:04:24,http://reinger.name/watson_stamm,0.2810152866,169.69.93.64,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n3466,2,76,2017-04-21 18:35:42,http://shanahan.org/lorenz,0.9606444690,10.178.221.98,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n3467,2,76,2017-01-17 12:53:17,http://torp.info/vernie_hoeger,0.0187218003,156.115.90.141,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n3468,2,76,2017-03-14 08:42:20,http://bednarokeefe.biz/brendon,0.2241540253,25.56.62.8,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n3469,2,76,2017-01-14 03:08:49,http://pollichreichert.net/marley,0.9168778879,95.206.31.58,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n3470,2,76,2017-05-18 02:49:31,http://gerlachmurray.io/pascale,0.8558500037,5.90.248.133,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n3471,2,76,2017-05-22 19:22:08,http://cruickshank.io/giuseppe.weimann,0.7523806443,49.54.80.234,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n3472,2,76,2017-05-25 11:57:53,http://boyerjenkins.co/americo.sanford,0.6849335270,184.36.195.239,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n3473,2,76,2017-02-15 16:26:51,http://ondricka.net/astrid.padberg,0.8012557887,199.94.181.155,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n10353,4,233,2017-05-14 00:36:31,http://blanda.org/bethany,0.8891134476,16.225.191.208,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n10354,4,233,2016-12-29 17:37:34,http://hudson.name/hayley_herzog,0.1036093910,134.197.188.103,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n10355,4,233,2017-06-02 23:30:50,http://toy.co/laurie.kreiger,0.7091896805,69.187.234.146,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n10356,4,233,2017-04-03 18:38:20,http://wisozk.net/vivian,0.5835490360,68.188.126.182,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n10357,4,233,2017-03-25 02:44:38,http://bednar.io/marina,0.0754743037,242.20.131.50,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n10358,4,233,2017-02-11 10:46:49,http://armstrong.name/danyka,0.9823391762,100.253.33.212,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n10359,4,233,2017-05-28 08:25:02,http://spencer.co/aubrey.schowalter,0.4974512335,198.228.86.207,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n10360,4,233,2017-02-15 23:00:04,http://schulistdaugherty.com/frederic,0.2931384880,43.138.86.218,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n10361,4,233,2017-02-07 05:26:43,http://larkinkub.io/bernie.bahringer,0.6887152543,191.203.12.181,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n10362,4,233,2017-06-07 15:38:35,http://sawayn.io/eleanora_dickens,0.4463504250,33.197.111.5,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n10363,4,233,2016-12-21 16:20:54,http://schaefer.net/chase.lind,0.8341924702,252.183.180.125,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n10364,4,233,2017-03-01 06:56:06,http://gleason.com/devan_runolfon,0.1390004634,48.60.144.104,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n10365,4,233,2017-06-10 03:13:44,http://mohr.info/talon,0.4604288957,101.31.252.254,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n10366,4,233,2017-01-09 01:55:09,http://heller.co/elody_mcglynn,0.5798585361,200.163.24.254,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n10367,4,233,2017-03-30 11:21:39,http://baileycrist.net/rhea,0.4634556661,129.251.191.17,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n10368,4,233,2017-04-28 02:29:02,http://johnson.biz/willa,0.2977830818,33.226.56.126,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n10369,4,233,2017-02-04 18:22:59,http://robel.info/donavon.hamill,0.2965782686,120.68.73.233,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n10370,4,233,2017-02-18 21:00:12,http://hand.biz/americo,0.2108010811,244.135.113.8,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n10371,4,233,2017-04-17 21:00:44,http://jaskolski.name/tyrese.predovic,0.2390435823,29.28.122.170,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n10372,4,233,2017-03-03 13:52:45,http://hilpert.co/noel.lubowitz,0.2338709626,48.141.147.8,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n10373,4,233,2017-06-11 18:41:13,http://kuhic.info/rosalyn,0.2297831935,161.189.56.44,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n10374,4,233,2016-12-22 07:37:53,http://batz.info/hunter,0.5087692074,102.101.138.64,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n10375,4,233,2017-03-12 20:05:48,http://collier.biz/dedrick,0.8118078616,117.93.201.46,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n10376,4,233,2017-05-19 08:00:40,http://hansen.com/vida,0.6980505815,95.93.29.68,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n10377,4,233,2016-12-31 23:20:41,http://feestgerhold.co/tiara_rodriguez,0.9234647050,20.227.53.242,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n10378,4,233,2017-05-28 03:43:39,http://hermannupton.info/brittany,0.7765468099,214.187.94.68,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n10379,4,233,2017-02-19 20:13:46,http://hahn.org/susie_reynolds,0.9260299324,11.87.228.168,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n10380,4,233,2017-03-07 05:54:15,http://purdy.co/kamille_baumbach,0.8778112100,148.10.212.81,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n10381,4,233,2016-12-31 14:31:52,http://schroederchamplin.org/casper,0.7207356286,184.211.111.62,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n10382,4,233,2017-05-01 06:02:13,http://kozeyparisian.name/chandler.reichert,0.6722432179,112.231.130.111,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n10383,4,233,2017-04-06 05:24:58,http://hoppewyman.net/moie.torphy,0.6828329433,19.35.221.253,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n10384,4,233,2016-12-19 23:48:53,http://legros.com/mireille,0.2136537032,81.164.174.34,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n10385,4,233,2016-12-13 15:10:55,http://kuvalis.info/emmanuelle,0.9889925310,102.244.53.231,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n10386,4,233,2017-03-20 15:02:08,http://grimehields.com/nikolas.greenholt,0.1086702870,16.50.225.114,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n10387,4,234,2017-03-21 21:25:36,http://little.org/laurie.stamm,0.6573493538,127.194.72.51,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n10388,4,234,2017-05-05 08:38:55,http://orn.com/holden,0.6484413405,56.103.111.38,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n10389,4,234,2017-03-10 04:33:13,http://mueller.info/titus,0.2374538896,110.217.157.206,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n10390,4,234,2017-02-03 00:51:56,http://doyle.org/kayli.nader,0.8973808488,90.246.16.234,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n10391,4,234,2017-02-05 10:16:18,http://gislason.co/morton.mcdermott,0.8458858184,149.169.115.190,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n10392,4,234,2016-12-21 11:38:41,http://maggio.net/gregg,0.0284452825,85.78.17.219,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n10393,4,234,2017-03-02 00:09:21,http://vandervort.io/baylee,0.6817406640,31.49.30.143,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n10394,4,234,2017-04-22 23:38:03,http://franecki.name/tavares,0.4982464665,73.180.95.129,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n10395,4,234,2017-05-20 21:28:17,http://bailey.info/augustus,0.0717753045,71.161.155.96,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n10396,4,234,2017-04-15 01:00:26,http://konopelski.com/theresa_johnston,0.7155839449,73.17.47.28,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n10397,4,234,2017-04-17 11:37:11,http://hane.name/mittie_hackett,0.3430854160,107.191.172.133,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n10398,4,234,2017-01-04 10:36:49,http://jerde.io/cleve,0.7402261959,240.40.43.191,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n10399,4,234,2017-03-03 10:42:34,http://beatty.co/lizeth,0.5228685974,171.206.135.227,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n10400,4,234,2017-01-17 21:02:00,http://moen.biz/santina_anderson,0.1239439254,115.42.204.142,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n10401,4,234,2017-03-21 05:18:03,http://lebsack.co/treva_spencer,0.0171621802,11.217.42.229,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n10402,4,234,2017-04-02 11:13:27,http://vandervortmorar.com/mina.purdy,0.1553133322,60.233.152.92,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n10403,4,234,2017-04-02 19:54:50,http://vonruedenkrajcik.net/sarai,0.4341828697,187.222.40.9,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n7373,3,164,2017-06-05 16:35:06,http://rogahnmurray.info/rashawn_renner,,251.245.207.122,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n7374,3,164,2017-04-18 04:30:12,http://morar.info/vernie,,16.89.34.116,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n7375,3,164,2017-02-27 00:17:46,http://bergstrommayer.org/romaine.ruel,,99.172.128.31,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n7376,3,164,2017-04-25 03:28:11,http://kreiger.info/ethan,,215.132.105.25,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n7377,3,164,2017-06-05 02:58:15,http://waelchi.name/edison,,33.252.95.9,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n7378,3,164,2017-01-08 06:23:12,http://huelcain.co/rosemary_blanda,,69.35.157.93,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n7379,3,164,2017-01-14 16:40:38,http://hand.net/chaim.hermiston,,240.168.93.169,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n7380,3,164,2017-04-29 19:07:54,http://hoeger.biz/glenna,,238.179.37.42,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n7381,3,164,2017-04-02 18:52:21,http://ullrich.name/marge.barton,,238.230.164.233,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n7382,3,164,2017-03-31 14:21:24,http://graham.co/estelle.yost,,83.234.153.133,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n7383,3,164,2017-04-06 08:47:27,http://erdmanschmeler.com/opal_lowe,,195.160.220.132,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n7384,3,164,2017-02-27 20:28:21,http://goldnerprohaska.io/wyatt,,4.69.251.237,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n7385,3,164,2017-05-31 07:16:20,http://kunze.io/raleigh_kulas,,180.74.45.113,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n7386,3,164,2017-01-24 19:29:30,http://anderson.biz/oswaldo.gorczany,,39.85.131.21,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n7387,3,164,2017-06-05 07:37:45,http://gutmanndaniel.biz/vivien.schoen,,249.151.13.109,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n7388,3,164,2017-05-16 08:08:08,http://ebert.io/autumn_fahey,,253.251.177.33,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n7389,3,164,2017-06-01 19:23:32,http://murrayschmeler.com/queen.sauer,,178.201.244.103,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n7390,3,164,2017-03-11 03:40:35,http://smith.org/summer_beer,,120.45.4.114,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n7391,3,164,2017-02-01 08:17:58,http://greenholt.name/fernando,,15.123.110.132,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n7392,3,164,2017-02-28 22:57:59,http://frami.net/mark,,50.19.163.98,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n7393,3,164,2017-01-21 03:26:19,http://mcculloughmayert.biz/roxane_sanford,,138.27.196.40,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n7394,3,164,2017-03-10 23:23:33,http://donnellyernser.io/rylan.hoppe,,248.5.148.91,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n7395,3,164,2017-02-27 05:38:44,http://nolan.co/amalia,,212.202.158.111,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n7396,3,164,2017-01-29 00:45:41,http://barrows.co/evangeline_price,,231.116.243.147,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n7397,3,164,2017-05-12 14:05:09,http://cole.io/adalberto,,32.157.88.107,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n7398,3,164,2017-05-27 01:45:14,http://rath.name/lowell.terry,,109.64.172.103,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n7399,3,164,2017-03-30 16:52:08,http://gaylord.org/enola,,250.33.231.108,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n7400,3,164,2017-05-10 04:03:38,http://langworth.io/angel.bernhard,,75.127.129.85,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n7401,3,164,2017-04-04 15:09:40,http://corkery.net/deonte,,199.121.60.179,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n7402,3,164,2017-06-12 19:13:19,http://boehmlesch.net/torey,,35.146.35.23,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n7403,3,164,2017-06-10 21:16:09,http://dachkemmer.co/reagan,,70.126.125.26,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n7404,3,164,2017-04-15 03:07:31,http://eichmannlehner.biz/lilly.metz,,12.119.54.120,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n7405,3,164,2017-04-17 20:41:10,http://botsford.org/meagan.raynor,,82.200.24.112,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n7406,3,164,2017-03-25 22:31:57,http://ankundingbartell.org/erick,,129.194.217.48,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n7407,3,164,2017-01-23 02:16:08,http://altenwerth.net/ena,,179.213.223.57,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n7408,3,164,2017-05-28 01:31:35,http://schneiderkohler.net/ansel,,183.149.18.11,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n7409,3,164,2017-01-20 12:42:21,http://rogahnko.org/gideon,,38.82.141.82,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n7410,3,164,2017-06-08 13:45:21,http://balistrerisawayn.info/precious_lehner,,53.187.89.39,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n7411,3,164,2017-05-05 22:34:49,http://erdman.biz/kirsten,,40.237.71.145,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n7412,3,164,2017-02-03 08:49:25,http://mraz.org/gladyce_baumbach,,156.183.47.110,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n7413,3,164,2017-05-22 05:09:19,http://kutchgerhold.name/teie_ankunding,,221.115.143.167,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n7414,3,164,2017-02-27 11:29:10,http://dietrich.io/horace,,84.177.90.90,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n7415,3,164,2017-01-20 18:52:57,http://boderolfson.net/grace.kuhic,,37.80.142.147,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n7416,3,164,2016-12-25 18:21:53,http://friesen.biz/esperanza,,157.41.236.58,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n7417,3,164,2017-06-11 02:09:14,http://rolfson.co/van.armstrong,,146.36.41.85,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n7418,3,165,2017-04-09 12:51:11,http://ferry.com/adam,,80.117.47.28,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n7419,3,165,2017-01-12 02:30:01,http://sawayn.com/martin_schuster,,190.136.100.151,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n7420,3,165,2017-04-28 09:32:32,http://windler.net/isai,,185.21.168.171,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n7421,3,165,2017-01-19 12:28:27,http://gottliebparisian.net/caandre,,147.114.142.229,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n7422,3,165,2017-04-20 14:40:35,http://dare.net/clementine.maggio,,209.109.173.103,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n7423,3,165,2017-06-09 07:09:30,http://schuppe.name/marvin_renner,,199.224.104.7,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n7424,3,165,2017-02-10 01:56:52,http://ryan.com/bryce.gutkowski,,106.12.61.222,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n7425,3,165,2017-05-21 14:46:13,http://leschhintz.name/angela,,219.47.9.242,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n7426,3,165,2017-01-10 11:48:31,http://leannon.org/jonathon.hammes,,231.109.183.27,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n7427,3,165,2017-04-26 01:49:06,http://weber.org/lorenzo_kovacek,,138.99.80.196,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n7428,3,165,2017-04-21 04:36:48,http://hayescollier.info/walter_dare,,135.208.26.173,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n13284,5,299,2017-04-02 22:07:43,http://tillman.io/beryl,,236.61.90.107,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n13285,5,299,2016-12-17 20:35:11,http://corkeryritchie.com/darrick.willms,,59.254.79.114,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n13286,5,299,2017-04-06 05:38:05,http://mcclure.net/emmie.moore,,196.192.121.57,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n13287,5,299,2017-06-04 13:32:37,http://rau.biz/haan,,58.168.204.203,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n13288,5,299,2016-12-29 15:16:03,http://renner.com/marco.ankunding,,231.42.91.182,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n13289,5,299,2017-02-06 18:57:31,http://friesen.info/monica,,13.66.203.3,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n13290,5,299,2017-03-23 15:52:20,http://heathcoteschultz.io/jeffry,,105.239.37.97,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n13291,5,299,2017-02-14 15:18:02,http://whitemosciski.info/gene,,248.9.149.56,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n13292,5,299,2016-12-27 23:57:06,http://vandervort.info/pamela_dickens,,148.216.81.254,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n13293,5,299,2017-02-28 00:31:53,http://sawayn.org/verda_leuschke,,123.215.90.26,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n13294,5,300,2016-12-31 22:51:43,http://donnellytrantow.biz/audrey,,146.216.178.144,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n13295,5,300,2016-12-31 05:27:02,http://ruel.com/benjamin_watsica,,82.91.188.47,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n13296,5,300,2017-05-05 02:19:20,http://rippinbaumbach.name/heaven,,114.208.155.129,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n13297,5,300,2017-01-04 21:36:42,http://fritsch.io/gilda.hayes,,180.164.31.64,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n13298,5,300,2016-12-28 05:06:20,http://macejkovic.org/marietta_west,,34.14.24.165,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n13299,5,300,2017-04-05 22:13:44,http://trantowbrekke.com/dashawn.goyette,,213.33.18.163,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n13300,5,300,2017-05-31 10:25:17,http://toyzboncak.name/trey,,97.49.210.167,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n13301,5,300,2017-05-22 01:22:27,http://champlin.co/dock_morar,,220.123.95.114,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n13302,5,300,2017-05-12 13:37:43,http://ortiz.co/emory.bernhard,,59.164.14.191,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n13303,5,300,2017-04-10 14:46:41,http://collierbernier.io/yasmeen_hagenes,,171.128.25.193,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n13304,5,300,2017-06-01 13:51:08,http://rempel.co/toby,,32.183.82.95,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n13305,5,300,2017-05-08 15:31:10,http://aufderhar.info/amelie,,156.206.6.173,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n13306,5,300,2016-12-15 10:13:06,http://ruecker.info/dawn.champlin,,234.206.233.166,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n13307,5,300,2017-02-15 10:38:02,http://schmelerrempel.io/harley.deckow,,46.96.133.141,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n13308,5,300,2017-04-27 21:16:41,http://keeblerrowe.net/grady.hayes,,249.211.98.105,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n13309,5,300,2017-02-25 00:44:09,http://nitzsche.io/preston.beier,,101.55.142.175,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n13310,5,300,2017-02-11 04:33:01,http://labadie.biz/rafael,,50.165.48.5,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n13311,5,300,2017-04-11 21:45:54,http://schroeder.info/andre,,232.94.185.113,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n13312,5,300,2017-02-28 21:08:20,http://schoenhintz.biz/pearlie,,210.118.125.212,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n13313,5,300,2017-04-06 06:39:50,http://mann.com/karine.kirlin,,155.208.168.243,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n13314,5,300,2017-05-03 12:02:44,http://boehmcarter.biz/antonina.mohr,,183.174.159.65,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n13315,5,300,2017-01-26 18:53:00,http://beatty.org/elda.rohan,,22.122.49.159,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n13316,5,300,2016-12-23 18:25:57,http://kirlin.com/orion,,179.46.245.152,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n13317,5,300,2017-03-16 12:46:01,http://king.net/mina_crist,,184.227.85.254,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n13318,5,300,2017-05-09 04:19:31,http://dubuque.biz/jaron,,226.39.251.101,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n13319,5,300,2016-12-29 08:20:50,http://dooley.co/hilbert,,51.160.133.163,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n13320,5,300,2017-02-16 07:30:51,http://waelchi.org/kenton,,150.218.85.186,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n13321,5,300,2017-01-07 16:21:56,http://fisher.com/geoffrey,,111.114.141.80,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n13322,5,300,2017-05-19 11:32:54,http://gleichner.com/rocky,,4.114.127.112,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n13323,5,300,2017-02-06 05:19:34,http://ritchiesawayn.info/conrad_oreilly,,118.174.113.248,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n13324,5,300,2017-06-04 23:54:38,http://okeefe.net/yvonne.blanda,,223.204.123.248,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n13325,5,300,2017-01-19 11:18:19,http://paucek.info/zakary.boyer,,59.31.76.22,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n13326,5,300,2017-03-09 23:35:47,http://shieldsbayer.info/dayana.buckridge,,106.170.227.176,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n13327,5,300,2017-03-05 07:48:35,http://cain.net/jaylan,,179.93.178.207,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n13328,5,300,2017-05-13 13:14:19,http://osinski.org/koby.dickens,,21.156.89.215,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n13329,5,300,2017-06-13 02:25:39,http://yost.org/dana_cremin,,61.214.99.152,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n13330,5,300,2017-04-01 17:47:30,http://kunde.name/vilma_goodwin,,27.189.24.24,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n13331,5,300,2016-12-19 17:26:39,http://grant.info/megane.vonrueden,,93.123.216.46,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n13332,5,300,2016-12-17 07:23:33,http://cronin.name/alejandra,,75.46.177.44,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n13333,5,300,2017-02-13 07:00:26,http://leuschke.io/alison_zboncak,,35.90.178.90,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n13334,5,300,2017-06-13 04:43:31,http://armstrong.org/kennith,,216.17.47.157,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n13335,5,300,2017-02-11 06:20:36,http://okuneva.biz/winona,,139.87.209.140,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n13336,5,300,2017-05-29 05:16:47,http://wiegandbrown.io/johnny.tromp,,245.179.16.9,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n13337,5,300,2017-04-01 20:08:22,http://croninfeil.io/laurianne,,102.194.220.122,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n13338,5,300,2016-12-31 17:23:56,http://daugherty.co/lyda,,68.8.220.4,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n16296,6,369,2017-02-04 16:41:26,http://ondrickadubuque.name/ola.dubuque,0.1082320092,254.153.64.167,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n16297,6,369,2017-01-16 12:49:08,http://larkin.name/milo.torp,0.1235837877,150.48.41.74,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n16298,6,369,2017-01-10 09:52:58,http://heaney.com/janis,0.4792710186,118.25.175.170,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n16299,6,369,2017-04-11 21:29:28,http://klocko.org/nyasia_donnelly,0.0311419012,8.240.63.214,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n16300,6,369,2016-12-31 12:28:00,http://ruecker.biz/amelia.doyle,0.2831392465,74.185.171.46,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n16301,6,369,2017-02-25 04:57:04,http://boscowalter.com/reid,0.9391230313,168.180.127.181,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n16302,6,369,2016-12-27 18:01:59,http://lebsack.net/nora,0.9364728851,161.186.182.150,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n16303,6,370,2016-12-15 01:15:44,http://kreiger.biz/wallace_schulist,0.3823492868,129.199.101.124,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n16304,6,370,2017-03-26 03:52:40,http://daniel.name/gerson.wolff,0.8348129382,40.213.209.186,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n16305,6,370,2017-05-13 06:56:09,http://heaney.org/lila,0.0761149900,160.193.113.7,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n16306,6,370,2017-06-11 17:15:39,http://cartwright.org/deangelo_gerlach,0.3020162453,148.55.138.190,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n16307,6,370,2017-01-07 07:21:37,http://ondrickahegmann.co/dax.gutkowski,0.3639463700,136.91.137.92,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n16308,6,370,2017-04-18 08:50:50,http://funk.info/keyon,0.7261787769,252.248.83.59,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n16309,6,370,2017-02-17 10:16:44,http://tillmanblick.com/cynthia.frami,0.3151682452,91.133.146.118,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n16310,6,370,2017-05-21 23:32:55,http://jerde.io/priscilla,0.2985300502,241.150.61.44,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n16311,6,370,2017-02-25 20:39:59,http://mohr.io/zackery,0.2929681076,62.167.247.121,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n16312,6,370,2017-05-26 03:48:59,http://lueilwitz.io/dejah,0.8435892980,250.136.212.238,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n16313,6,370,2017-03-05 03:11:05,http://wisozkmertz.io/tomasa,0.2458996276,8.114.52.81,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n16314,6,370,2017-05-21 08:12:34,http://dickinsonbauch.name/earnest.bednar,0.4948641724,40.91.177.144,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n16315,6,370,2017-05-26 21:31:05,http://wiegand.com/andrew,0.2509022458,119.115.170.115,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n16316,6,370,2017-06-09 15:55:10,http://rodriguez.info/milton.parker,0.8104413653,195.103.234.224,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n16317,6,370,2017-05-06 07:34:58,http://jerde.net/nathanial,0.9148858402,81.109.183.91,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n16318,6,370,2017-03-13 22:30:11,http://schiller.io/jalyn,0.7003597290,92.42.136.108,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n16319,6,370,2017-02-15 01:45:07,http://goldnerhalvorson.biz/ivy_fadel,0.2055940152,77.69.57.14,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n16320,6,370,2017-03-11 09:45:25,http://mohr.com/vivianne_spencer,0.6310179374,130.41.116.197,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n16321,6,370,2017-01-22 15:29:50,http://powlowski.com/hayley_wiza,0.9230116362,231.7.250.179,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n16322,6,370,2017-01-21 01:55:39,http://conn.name/kaylee_runolfon,0.1749968831,240.107.126.73,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n16323,6,370,2016-12-21 13:35:47,http://parkerrosenbaum.com/ladarius.johns,0.4304615760,133.110.247.5,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n16324,6,370,2017-03-17 20:23:52,http://cain.biz/kristofer,0.4500653456,194.166.182.10,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n16325,6,370,2017-04-19 06:07:12,http://hahncummings.co/sincere,0.3971096422,134.231.239.142,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n16326,6,370,2017-02-21 22:15:42,http://bogan.io/dameon,0.3686123870,5.96.17.150,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n16327,6,370,2016-12-13 08:35:44,http://grady.info/august_gottlieb,0.6555042974,31.187.176.101,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n16328,6,370,2017-02-13 17:48:54,http://wuckertlegros.io/percival_rosenbaum,0.1657302147,174.144.133.13,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n16329,6,370,2017-05-06 15:00:21,http://larsonsmith.org/maxwell.bechtelar,0.8835049836,73.131.23.152,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n16330,6,370,2017-01-16 10:10:34,http://rippinziemann.net/wilfred_schneider,0.7559936338,66.158.68.81,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n16331,6,370,2016-12-17 00:13:37,http://sanfordpaucek.co/candido_herzog,0.7124732749,121.163.204.228,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n16332,6,370,2017-05-25 04:17:25,http://rohan.co/marie,0.7106250558,7.130.55.106,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n16333,6,370,2017-04-26 01:37:07,http://spencer.co/flavio,0.6823717638,237.65.98.138,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n16334,6,370,2017-02-05 07:52:21,http://hand.co/verner.anderson,0.9383441181,94.188.57.158,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n16335,6,370,2016-12-14 13:49:27,http://smithambednar.org/lila_hahn,0.5348912094,96.91.14.213,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n16336,6,370,2017-02-03 15:11:04,http://gorczanygottlieb.net/jose.heathcote,0.5761590696,243.70.116.161,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n16337,6,370,2017-04-05 16:32:23,http://adamsnikolaus.name/kurtis_lind,0.1596249044,116.125.229.32,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n16338,6,370,2017-02-06 02:38:36,http://pfefferblick.co/darron_bergnaum,0.7311297591,159.132.171.207,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n16339,6,370,2017-06-13 14:38:12,http://konopelski.name/lexi,0.2657541790,245.63.225.67,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n16340,6,370,2017-05-17 12:22:06,http://sauer.biz/herminia,0.6733656989,83.159.11.75,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n16341,6,370,2016-12-21 09:24:11,http://brekke.info/fidel,0.4242227500,241.106.43.138,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n16342,6,370,2017-04-01 01:17:03,http://ruecker.net/trudie,0.6467240818,153.13.103.227,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n16343,6,370,2017-02-19 22:19:07,http://labadiemaggio.co/meta_murray,0.5435278819,51.62.201.62,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n16344,6,370,2017-06-01 02:54:28,http://lemke.info/alda,0.9784477979,11.195.16.49,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n16345,6,370,2017-01-30 03:46:12,http://moriette.com/berniece.howell,0.7427948740,227.81.183.133,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n16346,6,370,2017-01-06 23:18:21,http://buckridge.name/lorena,0.5354354988,35.90.64.111,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n3474,2,76,2017-01-01 09:39:39,http://beahan.info/lexus,0.5073137971,125.88.117.251,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n3475,2,76,2017-02-07 19:40:51,http://rutherford.com/helen,0.9146112184,155.140.150.41,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n3476,2,76,2016-12-18 17:20:09,http://kerluke.com/cecilia,0.4010907309,180.236.57.183,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n3477,2,76,2017-04-22 22:44:20,http://kilback.io/hector,0.1334181834,29.37.251.159,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n3478,2,76,2017-02-28 02:25:25,http://schinnertorp.io/rocky.kling,0.6045549905,107.195.136.143,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n3479,2,76,2017-03-10 18:38:00,http://pfannerstill.name/oswald.hagenes,0.4082453107,183.238.115.188,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n3480,2,76,2017-05-22 09:21:58,http://boehm.name/imani.wiegand,0.4740647269,6.219.178.66,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n3481,2,76,2017-06-04 16:09:47,http://harber.info/immanuel,0.7213410551,228.145.130.158,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n3482,2,76,2017-04-15 08:54:35,http://littlemertz.co/johathan,0.6995928355,179.2.142.51,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n3483,2,76,2017-04-12 09:13:49,http://hammes.biz/maye,0.1710449997,174.227.110.134,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n3484,2,76,2017-01-21 09:26:04,http://ryanschowalter.info/alda_rolfson,0.4621735265,212.201.88.26,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n3485,2,76,2017-05-27 03:42:11,http://wiegand.biz/kendall.okuneva,0.2661528277,113.249.3.81,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n3486,2,77,2017-01-15 15:44:43,http://leuschkesmitham.org/kristopher.maggio,0.7607770825,7.179.197.8,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n3487,2,77,2017-04-14 21:30:31,http://oconner.co/verona_kreiger,0.8388445286,74.172.202.118,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n3488,2,77,2017-05-10 19:06:12,http://wuckert.biz/sigmund.christiansen,0.7766338310,9.244.228.30,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n3489,2,77,2017-02-07 07:45:08,http://ward.net/brayan,0.6086022330,65.152.45.229,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n3490,2,77,2017-01-05 21:54:46,http://connelly.com/alyce,0.6389652674,216.201.20.182,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n3491,2,77,2017-05-11 07:56:48,http://keebler.name/wellington.lebsack,0.3654820078,242.35.238.135,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n3492,2,77,2017-05-23 17:28:17,http://koleffler.net/sebastian_turcotte,0.8544604177,37.43.21.31,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n3493,2,77,2017-01-21 03:09:22,http://reichert.name/rickie,0.0783224145,78.89.104.213,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n3494,2,77,2016-12-15 10:42:55,http://wildermanpadberg.com/bettie,0.5106544425,109.239.31.146,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n3495,2,77,2017-01-21 18:40:08,http://zieme.co/ona_langworth,0.2503709905,177.107.31.32,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n3496,2,77,2017-01-02 15:38:24,http://rice.co/geovanni_herman,0.5178194873,224.134.48.131,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n3497,2,77,2017-01-18 14:13:42,http://vandervortlowe.org/annamarie,0.0138910584,120.95.245.239,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n3498,2,77,2017-05-01 07:23:02,http://baumbach.org/jeramie,0.5862554567,239.74.37.213,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n3499,2,77,2017-01-26 00:44:12,http://powlowskibogisich.io/liana.ebert,0.3963297344,227.112.35.97,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n3500,2,77,2017-05-26 15:30:42,http://fay.io/nelda,0.4356159077,214.189.54.210,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n3501,2,77,2017-02-20 17:40:36,http://davisjast.org/chandler_schaden,0.4592299522,250.215.205.112,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n3502,2,77,2017-02-02 22:35:16,http://davis.io/tierra,0.7544524170,222.217.196.181,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n3503,2,77,2017-06-08 15:30:21,http://collins.name/vincenza,0.9922112984,14.247.226.13,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n3504,2,77,2017-02-21 22:46:36,http://mclaughlin.biz/kayli.cronin,0.2454114312,179.17.134.144,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n3505,2,77,2017-06-13 15:28:20,http://hermann.com/tyson.hartmann,0.6165013451,3.181.224.198,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n3506,2,77,2017-05-14 03:28:16,http://bogan.com/isabella,0.1140352509,47.94.170.89,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n3507,2,77,2017-01-02 17:04:00,http://cremin.org/esta,0.5577890246,218.217.26.231,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n3508,2,77,2017-03-24 14:53:20,http://cain.io/burley,0.9701615874,205.40.30.246,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n3509,2,77,2016-12-30 00:21:20,http://lemke.biz/annamarie_gleichner,0.3712402170,64.158.60.235,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n3510,2,77,2017-05-11 23:40:33,http://purdystehr.net/isaias,0.0244238508,221.186.64.15,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n3511,2,77,2017-04-16 06:56:15,http://moriette.org/watson_gislason,0.0312024572,239.196.168.244,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n3512,2,77,2017-03-23 22:31:44,http://weinat.biz/cordelia_rippin,0.4258899193,157.199.53.135,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n3513,2,77,2017-05-08 09:41:01,http://ruecker.name/paul,0.7453022127,208.107.128.129,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n3514,2,77,2017-02-21 04:54:59,http://schmeler.name/kenny_lockman,0.9523867887,159.47.175.251,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n3515,2,77,2017-03-24 11:06:54,http://smithcummings.org/shanny,0.0989161825,28.227.225.155,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n3516,2,77,2017-03-15 10:07:18,http://jaskolskischumm.name/braeden,0.3120749362,160.179.212.168,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n3517,2,77,2017-03-26 23:12:20,http://skilesgulgowski.io/hubert_douglas,0.8290865725,157.79.194.14,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n3518,2,77,2017-05-12 03:30:55,http://cruickshankturner.co/nathanial.green,0.9614707468,207.136.59.5,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n3519,2,77,2017-01-28 04:05:52,http://hettinger.io/mayra.trantow,0.8469165715,41.239.99.15,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n3520,2,77,2017-03-25 18:15:37,http://ward.biz/stevie,0.1751824372,212.141.137.181,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n3521,2,77,2017-05-06 01:13:18,http://kuhlman.info/jenifer,0.9143773921,39.191.246.108,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n3522,2,77,2017-01-03 11:07:16,http://langoshbeahan.net/myrtie.smitham,0.7006306102,106.88.65.104,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n3523,2,77,2017-02-25 11:13:02,http://schinner.io/jada.hegmann,0.5656580614,190.144.244.207,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n3524,2,77,2017-02-02 10:13:13,http://mooreokon.name/waldo,0.8967128783,204.150.200.95,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n10404,4,234,2017-05-07 22:39:51,http://reicheljohns.info/jamel_oreilly,0.5688983177,233.94.20.173,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n10405,4,234,2017-06-02 08:32:39,http://stokes.org/ro,0.7911434364,230.67.50.111,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n10406,4,234,2017-05-13 19:00:32,http://turcottereichel.org/antonette_barton,0.9668983179,182.246.149.13,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n10407,4,234,2017-01-25 12:43:40,http://nienowdeckow.io/murphy.stanton,0.9821918100,16.81.104.185,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n10408,4,234,2017-04-09 14:54:02,http://wolff.org/paolo_batz,0.7092666670,5.208.16.230,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n10409,4,234,2017-05-27 07:50:21,http://bernhard.com/niko,0.9850529654,5.135.161.237,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n10410,4,234,2017-01-29 05:02:46,http://lind.io/domenick,0.0171860910,35.13.22.253,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n10411,4,234,2017-04-26 17:38:06,http://sipes.com/zaria.considine,0.7116760851,186.176.233.251,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n10412,4,234,2017-02-18 07:57:32,http://windlerhoeger.name/mac,0.1197273722,231.9.70.244,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n10413,4,234,2017-02-03 19:14:25,http://stamm.co/sydnee_hahn,0.0789239169,139.42.154.27,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n10414,4,234,2017-05-22 08:06:32,http://eichmannklein.co/alana_feest,0.7110976753,190.38.22.92,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n10415,4,234,2017-04-28 04:14:53,http://schuster.net/billie_emmerich,0.7180308734,88.89.2.31,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n10416,4,234,2017-06-02 02:38:13,http://skiles.net/danielle,0.2278176787,74.253.87.135,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n10417,4,234,2017-05-21 03:37:04,http://erdman.co/vena,0.3321244917,131.113.139.201,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n10418,4,234,2017-04-15 11:04:58,http://skilehanahan.io/devonte_parker,0.0023951782,177.230.104.184,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n10419,4,234,2017-03-03 01:47:54,http://mitchell.org/rogers,0.8425463117,174.208.240.55,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n10420,4,234,2017-06-09 01:24:45,http://murraylowe.com/melia_cartwright,0.2395018968,138.131.52.179,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n10421,4,234,2017-05-08 04:02:57,http://cainblanda.net/olen_wilderman,0.5524719408,110.183.168.3,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n10422,4,234,2017-02-12 06:40:26,http://lind.biz/kathryne,0.6743823322,239.158.182.177,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n10423,4,234,2017-05-09 07:56:25,http://abernathyhane.net/gonzalo_goyette,0.3590141637,17.213.86.165,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n10424,4,234,2017-04-10 18:21:34,http://ferry.co/bettie,0.2723317347,169.248.209.241,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n10425,4,234,2017-01-10 09:11:40,http://breitenberghilpert.biz/gerson,0.1667641882,242.251.2.248,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n10426,4,234,2017-01-16 14:24:26,http://dubuqueschroeder.com/antwan.collier,0.9669955526,97.141.210.160,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n10427,4,234,2017-03-25 01:16:09,http://monahan.info/monty,0.9547918394,182.248.80.8,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n10428,4,234,2017-02-01 18:55:30,http://haag.name/albert,0.1954546776,14.167.35.203,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n10429,4,235,2017-02-05 16:00:37,http://lynch.net/toni_oreilly,0.8463385411,149.127.132.51,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n10430,4,235,2017-04-21 13:31:32,http://murazik.net/krystel,0.3764827972,246.223.228.55,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n10431,4,235,2017-02-20 19:22:17,http://abshiregusikowski.com/alba_dicki,0.5848952225,121.214.66.253,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n10432,4,235,2016-12-30 14:41:00,http://pagac.org/nestor,0.8598227959,175.4.77.30,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n10433,4,235,2017-06-08 19:35:38,http://parker.name/norbert_collins,0.6612779253,231.119.36.223,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n10434,4,235,2017-01-15 17:26:38,http://jacobideckow.name/terry,0.0320799136,33.124.201.137,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n10435,4,235,2017-02-24 14:27:13,http://windler.co/jules,0.8004334986,221.80.235.66,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n10436,4,235,2017-02-24 13:06:04,http://hackett.net/hallie_fahey,0.6497129494,174.72.201.138,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n10437,4,235,2017-04-30 07:03:31,http://armstrong.com/whitney.bahringer,0.7932158146,115.40.70.171,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n10438,4,235,2017-05-14 12:12:04,http://treutel.co/mayra,0.2869364412,63.71.98.67,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n10439,4,235,2017-01-13 02:50:52,http://reilly.io/belle.mante,0.0458380195,19.158.14.62,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n10440,4,235,2016-12-25 07:14:48,http://robelschaden.net/josie,0.0107134376,83.123.29.211,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n10441,4,235,2017-04-27 03:31:01,http://mckenzie.info/bart,0.9334165177,86.43.43.164,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n10442,4,235,2016-12-20 19:19:14,http://kris.co/brad,0.1977877267,112.94.156.208,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n10443,4,235,2016-12-29 22:07:01,http://schimmel.info/gabriella,0.9680778849,51.225.56.243,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n10444,4,235,2017-04-14 02:18:48,http://collier.info/christina,0.4340239801,11.221.30.182,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n10445,4,235,2017-04-14 20:33:28,http://powlowski.co/miguel,0.7185863782,64.125.162.102,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n10446,4,235,2017-02-16 00:37:43,http://hamill.co/ellsworth_zulauf,0.7735244505,210.17.15.88,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n10447,4,235,2017-04-26 04:28:56,http://doyle.net/maegan,0.4558510314,26.186.216.47,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n10448,4,235,2017-01-18 03:45:14,http://johnson.info/justen,0.4837986418,62.46.125.242,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n10449,4,235,2017-04-18 23:42:37,http://gulgowski.com/ryann_hilpert,0.7050118694,74.32.185.166,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n10450,4,235,2017-03-10 19:46:57,http://nolan.info/gardner,0.8341803926,36.169.230.32,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n10451,4,235,2017-01-26 07:11:38,http://howebecker.org/laurianne,0.3446242048,249.99.159.51,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n10452,4,235,2017-02-28 08:33:06,http://wisozkfeest.org/hellen_rogahn,0.3186207922,195.21.99.160,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n10453,4,235,2016-12-14 00:37:22,http://cummingswalter.name/albert,0.0930160052,114.230.232.190,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n10454,4,235,2017-06-04 22:04:27,http://moriettegottlieb.name/rafaela.mcglynn,0.8687719195,253.232.58.110,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n7429,3,165,2017-01-19 00:23:25,http://boyerpfeffer.com/brooklyn.paucek,,147.129.221.72,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n7430,3,165,2017-03-27 02:50:20,http://goldner.name/stacy.bechtelar,,120.232.50.37,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n7431,3,165,2017-05-09 22:24:45,http://weinatrohan.io/grace,,66.57.115.208,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n7432,3,165,2017-03-01 15:22:16,http://shanahangibson.net/katherine,,251.248.128.42,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n7433,3,165,2017-01-05 06:49:48,http://deckow.com/waino,,171.182.144.239,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n7434,3,165,2017-03-24 23:53:23,http://konopelskiruecker.biz/anika,,84.75.228.221,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n7435,3,165,2017-03-26 11:51:41,http://greenholt.co/hosea.waters,,86.238.154.34,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n7436,3,165,2017-06-02 04:05:30,http://pacochakoepp.info/alejandra,,114.42.156.7,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n7437,3,165,2016-12-25 21:55:36,http://kleinschimmel.info/sabrina.sanford,,118.120.223.11,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n7438,3,165,2017-05-24 13:10:59,http://runolfsdottirokuneva.com/delphia,,229.150.50.36,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n7439,3,165,2017-06-05 13:51:17,http://lindgren.co/enoch,,104.11.151.6,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n7440,3,165,2017-05-24 21:57:06,http://streichherman.io/jimmy,,36.105.180.83,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n7441,3,165,2017-01-27 16:50:18,http://abbott.com/clint,,122.36.244.17,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n7442,3,165,2017-04-11 20:40:41,http://kertzmann.name/hipolito.runolfsdottir,,92.55.172.98,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n7443,3,165,2017-04-20 22:04:41,http://robertshettinger.biz/astrid.metz,,43.74.174.178,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n7444,3,165,2017-01-02 20:03:23,http://schimmel.biz/renee_stokes,,48.12.239.78,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n7445,3,165,2017-01-06 12:24:48,http://mitchelllang.org/philip_collier,,88.161.57.125,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n7446,3,165,2017-01-18 23:51:52,http://stiedemann.net/burnice_cormier,,99.204.121.117,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n7447,3,165,2017-03-02 20:41:57,http://hamill.info/emmalee,,41.150.251.28,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n7448,3,165,2017-03-18 20:58:55,http://emard.name/kendall_halvorson,,178.112.224.240,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n7449,3,165,2016-12-29 18:58:09,http://jerde.org/marlon,,179.161.147.217,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n7450,3,165,2017-05-19 08:44:01,http://mayer.com/maiya,,115.143.104.79,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n7451,3,165,2017-05-15 19:36:14,http://weinatschamberger.co/valentina,,181.147.47.238,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n7452,3,165,2017-05-30 07:16:10,http://trompbecker.com/madilyn_barrows,,134.83.129.211,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n7453,3,165,2016-12-29 11:30:51,http://rosenbaum.com/ahmed,,197.27.173.173,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n7454,3,165,2017-06-13 09:02:56,http://jacobiprohaska.info/kiarra,,72.77.63.192,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n7455,3,165,2017-01-24 13:11:57,http://lockman.co/ashley_erdman,,156.181.175.116,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n7456,3,165,2017-03-17 20:58:32,http://eichmanncrona.name/leanna.jacobi,,185.114.251.4,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n7457,3,165,2017-04-11 02:58:06,http://hoppe.name/rogers,,188.233.90.228,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n7458,3,165,2017-03-28 15:38:08,http://herzoghermann.name/carlo,,184.77.162.159,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n7459,3,165,2017-05-24 14:44:46,http://ritchie.org/rasheed,,165.222.202.175,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n7460,3,165,2017-06-11 12:24:47,http://friesenerdman.net/marianna,,196.83.253.183,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n7461,3,165,2017-01-04 07:11:17,http://prohaska.org/jabari_feeney,,57.119.34.239,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n7462,3,165,2016-12-14 21:01:22,http://kerlukestamm.info/adolf,,109.230.102.157,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n7463,3,165,2017-03-03 09:48:19,http://schroeder.org/colten,,78.131.156.52,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n7464,3,165,2017-05-25 03:53:49,http://spinkawilliamson.info/jed.rutherford,,161.119.238.233,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n7465,3,165,2017-03-25 11:44:52,http://barrows.co/wilber,,164.185.21.232,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n7466,3,165,2017-04-25 18:58:17,http://schinnerokon.biz/shanna,,244.102.176.246,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n7467,3,165,2017-03-31 21:42:35,http://hermannharvey.name/ervin_weber,,79.23.200.53,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n7468,3,165,2017-01-23 16:49:45,http://christiansen.com/kacie,,120.205.147.44,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n7469,3,165,2017-06-04 07:56:07,http://mcglynn.name/terrence,,37.7.185.151,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n7470,3,165,2017-05-06 22:26:02,http://haleyrobel.com/karen.rohan,,250.143.250.186,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n7471,3,165,2017-04-25 00:15:12,http://hirthe.org/perry_hauck,,162.151.173.241,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n7472,3,165,2017-02-05 16:01:25,http://grimes.io/bryana,,102.185.8.215,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n7473,3,165,2017-02-11 10:55:50,http://connsawayn.com/lamont.satterfield,,232.161.134.192,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n7474,3,165,2017-01-09 09:47:29,http://mayert.net/jimmie.stroman,,118.156.160.155,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n7475,3,165,2017-01-07 16:31:54,http://bednar.com/bonita,,56.12.154.166,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n7476,3,165,2016-12-31 17:51:52,http://kulaswelch.name/lucinda_mcglynn,,89.65.137.103,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n7477,3,165,2017-01-01 12:53:18,http://green.io/baby,,163.114.193.225,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n7478,3,165,2017-03-03 07:36:51,http://mohr.org/creola_pagac,,126.147.104.230,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n7479,3,165,2017-05-24 16:48:46,http://mohr.name/jordyn,,71.10.207.97,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n7480,3,165,2017-03-23 03:09:08,http://gerholdbeer.org/laverna,,130.77.216.212,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n7481,3,165,2017-01-13 11:17:31,http://watsicarau.net/deon,,222.76.167.147,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n7482,3,166,2017-01-28 00:11:44,http://parisian.com/reinhold,,147.187.162.115,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n7483,3,166,2017-05-13 20:39:05,http://prosacco.co/kattie,,86.147.250.116,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n13339,5,300,2017-02-26 16:16:44,http://metz.biz/kirsten,,17.200.91.209,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n13340,5,300,2017-04-20 18:15:47,http://hyattrodriguez.co/gayle,,237.68.253.55,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n13341,5,300,2017-03-03 22:27:23,http://ziemann.biz/kiarra,,136.231.20.114,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n13342,5,300,2017-03-09 16:53:12,http://klein.io/emelia,,55.52.166.132,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n13343,5,300,2017-02-13 21:16:13,http://pourosrunolfsdottir.io/dolly,,105.17.82.102,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n13344,5,300,2017-04-30 23:29:35,http://johnson.org/stella,,31.187.189.152,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n13345,5,300,2017-05-25 22:52:48,http://wardjohns.io/kamron,,33.122.9.81,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n13346,5,300,2016-12-19 20:27:44,http://barrows.name/alejandra_padberg,,189.114.137.118,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n13347,5,300,2017-01-12 15:05:16,http://renner.co/loraine,,112.23.124.112,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n13348,5,300,2017-06-05 20:20:42,http://carter.biz/jevon,,87.98.213.91,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n13349,5,300,2016-12-23 07:43:08,http://oconnelllarkin.name/peggie,,157.8.31.198,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n13350,5,300,2017-05-08 09:41:00,http://tromp.com/wendy_lockman,,121.253.38.13,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n13351,5,301,2017-02-04 06:47:09,http://keelinglind.biz/nikko,,152.238.51.36,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n13352,5,301,2017-03-12 19:45:06,http://auer.co/reece.roob,,125.98.169.120,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n13353,5,301,2017-02-26 08:50:42,http://schamberger.biz/leonard,,95.78.118.89,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n13354,5,301,2017-02-03 23:20:40,http://conroy.info/cortez.goodwin,,170.47.68.126,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n13355,5,301,2016-12-27 15:25:36,http://mills.net/lorenz,,241.198.125.180,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n13356,5,301,2017-02-05 00:36:29,http://fadelmorar.org/devonte,,46.120.105.35,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n13357,5,301,2017-01-02 17:00:22,http://ratke.biz/cleveland,,139.10.232.168,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n13358,5,301,2017-01-03 17:02:32,http://sipes.com/jacquelyn.klein,,189.58.226.122,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n13359,5,301,2017-01-11 21:48:30,http://brauntorphy.io/loren,,237.229.79.68,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n13360,5,301,2017-04-17 12:42:54,http://little.com/ernestine,,107.197.189.98,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n13361,5,301,2016-12-22 00:18:16,http://jerdedibbert.net/isaias,,236.142.57.245,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n13362,5,301,2017-03-27 05:45:37,http://nikolaus.co/marquise,,206.250.76.84,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n13363,5,301,2017-05-31 08:18:27,http://durgan.io/rowan_hirthe,,73.88.183.19,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n13364,5,301,2017-03-08 18:50:24,http://reichelyost.io/giovanny.little,,90.205.247.202,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n13365,5,301,2017-03-19 11:57:03,http://rosenbaumleffler.biz/olen,,218.223.55.129,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n13366,5,301,2017-03-30 12:19:37,http://weinat.info/reyna,,53.47.134.55,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n13367,5,301,2017-01-30 22:14:01,http://tillman.biz/karine,,52.64.160.222,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n13368,5,301,2017-02-17 13:28:54,http://boehmjacobs.io/yeenia.barrows,,2.190.195.54,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n13369,5,301,2017-04-25 11:09:28,http://turcottehilll.co/anika.rowe,,117.90.152.194,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n13370,5,301,2017-06-10 17:05:35,http://white.co/alfreda,,24.83.53.102,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n13371,5,301,2016-12-16 14:29:04,http://flatleychristiansen.io/jonathan,,65.203.102.168,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n13372,5,301,2016-12-31 18:20:42,http://stokesullrich.com/leanne,,240.150.35.229,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n13373,5,301,2017-05-21 15:50:58,http://rau.co/lawrence,,67.161.31.249,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n13374,5,301,2016-12-29 04:51:26,http://waters.co/americo,,171.8.67.243,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n13375,5,301,2017-01-23 01:50:28,http://streichkoch.co/chanel_pollich,,31.94.30.11,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n13376,5,301,2017-01-09 10:32:41,http://gulgowski.net/samson_lynch,,147.115.189.153,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n13377,5,301,2017-02-16 14:27:55,http://torphy.info/casey,,55.120.205.233,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n13378,5,301,2017-01-29 14:04:01,http://brownoberbrunner.org/mia,,150.189.17.149,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n13379,5,301,2017-01-22 04:22:58,http://rogahn.com/theodora_schowalter,,118.55.246.109,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n13380,5,301,2017-04-05 09:49:59,http://heathcote.co/lillian.beier,,199.71.97.222,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n13381,5,301,2017-01-24 08:39:21,http://greenholt.net/kathryn,,155.79.34.5,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n13382,5,301,2017-02-26 07:48:36,http://greenholtweimann.name/krista,,229.48.203.181,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n13383,5,301,2017-04-29 15:15:21,http://raustrosin.org/jordan_torphy,,14.9.219.148,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n13384,5,301,2017-05-31 11:39:14,http://wisozk.biz/benjamin,,88.56.173.205,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n13385,5,301,2017-02-09 01:55:06,http://ebert.com/raphaelle,,20.162.192.212,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n13386,5,301,2017-02-23 04:10:49,http://kundehyatt.com/nathanael_yost,,171.85.131.208,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n13387,5,301,2017-02-07 08:34:25,http://schinner.io/keith.smitham,,191.140.44.87,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n13388,5,301,2017-03-29 17:41:25,http://hermann.co/patience_bernhard,,104.85.118.11,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n13389,5,301,2017-01-05 22:00:48,http://colemraz.net/laverna.kuhn,,111.141.28.253,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n13390,5,301,2017-01-27 20:46:04,http://schusterconroy.name/fanny_rolfson,,6.45.212.205,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n13391,5,301,2017-03-07 17:00:44,http://mcdermott.name/rocio,,246.33.7.162,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n13392,5,301,2017-03-03 13:34:52,http://miller.net/leone,,128.128.135.49,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n13393,5,301,2017-06-08 04:47:36,http://berge.io/fritz,,164.204.232.52,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n13394,5,301,2017-05-16 21:10:12,http://pagac.co/milford,,238.242.69.62,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n16347,6,370,2016-12-29 22:12:41,http://cummings.co/alf,0.9392782598,83.95.104.249,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n16348,6,370,2017-01-23 05:30:55,http://connelly.name/rhiannon.fisher,0.8577447533,237.190.119.147,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n16349,6,370,2017-04-06 21:00:29,http://leschhane.io/saige,0.3867775915,88.160.122.184,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n16350,6,370,2017-04-05 22:57:27,http://marvintoy.io/kaandra,0.3779063708,163.125.126.141,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n16351,6,370,2017-02-22 07:37:09,http://johnsmoen.co/malcolm,0.2594580189,192.236.3.152,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n16352,6,370,2017-05-02 17:16:17,http://bartolettimaggio.net/kimberly,0.1229465548,51.110.66.27,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n16353,6,370,2017-05-06 14:49:00,http://starkrolfson.info/juwan,0.1936098527,102.109.148.43,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n16354,6,370,2017-06-12 19:36:51,http://harris.biz/korbin.kutch,0.3714085406,153.217.77.86,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n16355,6,370,2017-05-07 10:58:00,http://prosacco.name/jody,0.6422231779,124.96.94.150,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n16356,6,370,2017-03-08 19:10:50,http://langworth.name/abdul.gutmann,0.3179840192,212.235.39.90,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n16357,6,370,2017-02-01 07:51:14,http://simoniscummerata.info/philip,0.9756520859,16.127.126.28,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n16358,6,370,2017-01-11 16:36:36,http://kuhlman.net/guy,0.9505561941,10.126.249.212,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n16359,6,370,2017-05-08 16:29:44,http://pfannerstillschmitt.name/wilhelm_kuphal,0.2526571959,119.182.164.211,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n16360,6,371,2017-05-08 02:38:51,http://dach.com/quincy,0.6713239686,229.219.134.140,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n16361,6,371,2017-05-14 10:58:11,http://langworthhand.com/webster_kiehn,0.4714293771,96.21.118.17,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n16362,6,371,2017-05-23 08:23:27,http://weimann.name/guy,0.8522895659,71.233.115.130,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n16363,6,371,2017-04-28 02:01:38,http://bergnaum.com/alysha,0.9928476210,173.74.104.252,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n16364,6,371,2017-05-02 02:43:01,http://haag.net/mellie,0.8358821013,51.209.109.30,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n16365,6,371,2017-03-24 17:37:08,http://fay.co/daren,0.4846032874,141.180.240.169,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n16366,6,371,2017-02-23 02:35:17,http://schmitt.biz/dawn.langosh,0.4103010959,39.136.242.86,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n16367,6,371,2017-05-04 15:50:17,http://stiedemannchamplin.com/queenie.stokes,0.9944715661,163.102.206.234,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n16368,6,371,2017-05-19 19:47:21,http://monahan.io/nicholas_murazik,0.5991129038,4.72.34.163,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n16369,6,371,2017-02-17 13:38:28,http://mcglynn.name/heather.carroll,0.2994231871,90.41.103.251,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n16370,6,371,2017-04-24 12:04:46,http://berge.org/kimberly,0.6616318221,175.106.225.122,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n16371,6,371,2016-12-26 02:23:10,http://jenkinsmarquardt.biz/westley.wilderman,0.2240959061,59.91.18.4,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n16372,6,371,2017-03-26 03:43:21,http://bauch.info/mariano,0.3114070546,201.10.106.214,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n16373,6,371,2017-05-31 08:16:38,http://vonaltenwerth.co/luigi,0.9771082932,42.252.157.93,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n16374,6,371,2017-05-18 05:51:18,http://simonisbatz.org/avery.lang,0.7040879919,208.43.216.250,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n16375,6,371,2017-01-04 10:14:28,http://kshlerin.co/eldon.maggio,0.5649297556,77.168.254.23,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n16376,6,371,2017-01-29 23:30:25,http://grahambauch.com/camryn,0.3918487075,34.8.230.143,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n16377,6,371,2016-12-24 21:52:22,http://stiedemann.org/keaton.lubowitz,0.2864161980,241.141.229.81,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n16378,6,371,2017-05-08 11:49:06,http://spencer.biz/cara_wilderman,0.3497677859,35.181.187.137,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n16379,6,371,2017-03-06 03:19:11,http://kozey.org/cheyenne.schultz,0.4765532724,143.236.24.183,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n16380,6,371,2017-06-02 06:19:37,http://grahamfunk.co/horacio,0.0988377897,99.129.129.243,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n16381,6,371,2017-03-11 03:29:17,http://jerderolfson.name/verner,0.4666825641,180.52.148.248,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n16382,6,371,2017-02-01 02:39:37,http://welchkulas.info/steve,0.1345339292,66.134.190.138,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n16383,6,371,2017-02-05 07:10:51,http://runtehegmann.biz/juliana,0.3006660486,104.95.220.70,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n16384,6,371,2017-01-08 19:13:41,http://murray.info/monte.donnelly,0.5379703473,231.34.223.191,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n16385,6,371,2017-04-15 19:42:51,http://kohler.org/valentine,0.4083965299,137.107.180.153,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n16386,6,371,2017-01-16 02:46:30,http://grantmoen.com/lexie,0.2463123588,191.81.206.246,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n16387,6,371,2017-06-04 03:11:43,http://rempel.net/verna,0.5828606327,170.201.248.109,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n16388,6,371,2017-04-27 06:54:00,http://grantboyer.org/vicky,0.4477090816,119.7.226.11,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n16389,6,371,2017-03-16 07:10:03,http://schuppemonahan.io/gaetano.schiller,0.8474319353,167.223.38.128,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n16390,6,371,2017-03-26 10:10:01,http://pfannerstillcollier.com/archibald.dibbert,0.6715038430,71.70.81.49,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n16391,6,371,2016-12-19 14:35:20,http://willms.io/coby,0.7673755570,129.55.198.209,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n16392,6,371,2017-04-16 01:48:00,http://heathcote.info/sydney_wyman,0.0845630446,42.203.246.244,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n16393,6,371,2017-03-13 21:21:14,http://rowerogahn.name/pansy_west,0.4532845679,196.108.9.99,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n16394,6,371,2017-02-03 14:42:18,http://boyer.name/brian_lemke,0.8030681905,119.94.155.222,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n16395,6,371,2017-02-23 00:45:05,http://frami.co/alena,0.9786662685,26.16.22.152,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n16396,6,372,2016-12-26 13:00:47,http://stokehields.org/howell,0.2942215462,249.237.198.99,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n16397,6,372,2017-04-08 13:07:49,http://feestbeahan.net/coleman.schmidt,0.3976201655,236.211.33.136,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n3525,2,77,2017-06-09 17:05:01,http://reichertjaskolski.net/nathan.littel,0.8646689543,227.188.18.36,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n3526,2,77,2017-04-28 19:41:52,http://nicolas.name/natasha,0.2104158513,107.252.68.168,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n3527,2,77,2016-12-17 01:22:03,http://harberrohan.biz/carlos.oconner,0.3867501898,159.157.60.109,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n3528,2,77,2017-04-21 06:08:33,http://schiller.info/kiara,0.6127111111,31.196.245.111,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n3529,2,77,2017-01-31 12:17:11,http://gulgowskitillman.org/chandler,0.6236046562,226.180.14.210,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n3530,2,77,2017-06-03 21:46:28,http://kemmer.biz/mozell_collins,0.8042414686,219.136.95.133,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n3531,2,77,2017-04-07 07:46:07,http://effertz.io/guie,0.9617592151,67.162.129.97,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n3532,2,77,2017-05-31 22:30:33,http://okeefe.biz/guiseppe_marks,0.0438050593,61.220.21.42,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n3533,2,77,2017-02-24 21:23:43,http://gutmann.co/arnaldo,0.7312482272,191.7.207.69,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n3534,2,77,2017-03-08 20:54:11,http://stiedemannankunding.biz/blanca.tromp,0.8118066682,236.128.180.12,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n3535,2,77,2017-01-24 22:58:21,http://beerdooley.net/jerald,0.7538317543,216.40.218.124,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n3536,2,77,2017-02-14 21:06:43,http://waterskiehn.net/emmanuelle.mcglynn,0.0766171818,75.85.207.248,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n3537,2,77,2017-04-21 14:12:59,http://rowe.info/oliver,0.8325669403,228.195.95.20,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n3538,2,77,2017-02-27 16:09:27,http://moen.io/rodger.nitzsche,0.4487657110,23.240.244.244,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n3539,2,77,2017-02-24 21:06:25,http://cain.info/rocky,0.1144254233,198.67.77.138,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n3540,2,77,2016-12-28 08:59:08,http://wintheiser.net/harmon.ryan,0.6079171977,183.45.232.252,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n3541,2,77,2017-05-12 11:25:12,http://kerlukecarter.net/willow_luettgen,0.3401192250,58.72.155.236,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n3542,2,77,2017-01-30 07:25:26,http://witting.org/gayle.welch,0.5360338563,2.99.77.32,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n3543,2,77,2017-01-29 00:12:01,http://doyle.com/eliane,0.2077790720,106.23.20.108,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n3544,2,78,2017-04-08 22:46:20,http://huel.co/beaulah,0.4554331367,3.231.173.86,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n3545,2,78,2017-03-05 17:59:00,http://ratkebreitenberg.name/tillman.kerluke,0.2146511401,192.194.29.53,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n3546,2,78,2017-05-17 03:51:54,http://kuvalis.net/ebony,0.2857327956,55.33.83.230,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n3547,2,78,2016-12-26 01:15:53,http://littel.net/jayden,0.9504714400,226.142.113.46,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n3548,2,78,2017-03-22 19:02:06,http://reilly.org/skyla.haley,0.0503013805,159.174.244.117,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n3549,2,78,2017-05-06 04:25:34,http://pagac.net/idella,0.2488798866,238.88.221.121,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n3550,2,78,2017-02-11 20:16:57,http://green.com/stella.kohler,0.0972691888,205.116.48.135,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n3551,2,78,2017-03-13 22:57:24,http://westhilpert.info/vincenzo.wisoky,0.8488775564,249.215.195.153,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n3552,2,78,2017-02-19 15:51:00,http://leschschamberger.io/maureen_klein,0.9440256971,145.216.103.95,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n3553,2,78,2017-03-15 14:41:00,http://block.co/nyasia.hahn,0.7671362305,210.141.90.219,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n3554,2,78,2017-02-07 03:33:57,http://hartmann.org/mafalda.nader,0.7822364623,177.127.247.132,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n3555,2,78,2017-01-06 09:57:44,http://bergstrom.com/maxime_hayes,0.0869469526,154.135.77.149,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n3556,2,78,2017-05-04 15:49:16,http://lubowitz.com/ron.grimes,0.7574265973,136.11.157.244,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n3557,2,78,2017-03-24 08:24:06,http://cruickshank.org/freda,0.5609586225,138.241.135.217,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n3558,2,78,2017-04-01 08:58:17,http://carter.co/karli_schmidt,0.4374409776,154.30.107.65,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n3559,2,78,2016-12-30 23:13:22,http://oconnell.biz/era_borer,0.0011911789,125.108.187.57,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n3560,2,78,2017-04-26 11:00:33,http://littel.net/marcelina_mayer,0.1881197207,117.125.174.170,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n3561,2,78,2017-03-03 23:27:56,http://eichmann.io/guy,0.9895087895,234.123.37.216,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n3562,2,78,2017-04-04 07:01:47,http://oreilly.net/emery.keeling,0.1047191591,141.3.209.210,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n3563,2,78,2017-02-24 19:41:21,http://grant.org/allison,0.6140782687,100.142.144.65,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n3564,2,78,2017-03-15 12:09:40,http://wilkinson.info/edmund,0.5211968015,254.43.217.211,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n3565,2,78,2017-04-10 19:30:22,http://haagrogahn.org/bennett_vandervort,0.2949713385,218.83.38.201,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n3566,2,78,2017-01-03 17:25:32,http://haley.io/jacklyn.kuphal,0.4572064984,60.219.185.33,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n5092,2,112,2017-01-09 21:04:58,http://mann.info/kade,,253.44.209.73,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n3567,2,78,2017-04-11 08:18:54,http://eichmannmcdermott.info/lois,0.0172292793,9.26.190.54,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n3568,2,78,2017-05-02 18:52:56,http://gutmann.org/nina_hodkiewicz,0.1043503759,139.93.148.152,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n3569,2,78,2017-03-30 08:59:31,http://gottliebbrekke.biz/claudie_jakubowski,0.3856217561,131.224.51.242,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n3570,2,78,2017-02-08 05:31:26,http://leffler.io/dorothy,0.0364641697,90.8.170.178,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n3571,2,78,2017-04-27 23:34:21,http://mitchell.biz/robert,0.1353822738,62.222.245.40,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n3572,2,78,2017-03-08 01:13:51,http://pouros.name/willow.franecki,0.0208733173,203.103.10.142,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n3573,2,78,2017-05-30 22:36:11,http://hackettherzog.co/lenora_weinat,0.4635001035,233.234.9.191,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n3574,2,78,2017-04-04 12:51:24,http://beatty.net/lisette_swaniawski,0.5947414712,194.53.191.240,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n3575,2,78,2017-04-19 02:46:57,http://ratkerunolfsdottir.biz/adolphus,0.8256513761,35.97.30.106,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n10455,4,235,2016-12-21 01:05:05,http://harriswiegand.org/meggie.abernathy,0.9693606419,231.3.201.166,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n10456,4,235,2016-12-28 23:57:43,http://mertz.com/wilburn,0.8264564967,146.67.150.190,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n10457,4,235,2016-12-23 03:19:38,http://lang.io/odell.kuphal,0.1719213082,107.144.39.193,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n10458,4,235,2017-04-25 03:22:43,http://beermcclure.com/kaela.gislason,0.0443778766,27.165.86.6,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n10459,4,235,2017-03-22 08:07:04,http://heidenreich.co/christian_collins,0.3230310248,212.137.189.215,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n10460,4,235,2017-03-08 19:03:25,http://heaney.biz/adelia,0.9470726970,45.48.187.21,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n10461,4,235,2016-12-19 09:23:10,http://kuhickoch.co/dallin,0.1162802783,34.207.19.185,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n10462,4,235,2017-04-23 20:22:19,http://sengershanahan.com/daphnee.ortiz,0.8032905184,251.207.235.65,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n10463,4,235,2017-05-08 08:48:51,http://gerhold.io/lemuel,0.4918276388,105.188.192.78,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n10464,4,235,2017-03-04 14:55:13,http://weinat.io/karlee.keeling,0.6070637305,188.87.221.40,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n10465,4,235,2017-02-24 04:16:37,http://mcdermott.net/marcelo.wintheiser,0.6697459142,20.241.156.163,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n10466,4,235,2017-03-20 08:49:48,http://harrispollich.biz/cleo.klein,0.7022408021,100.235.217.55,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n10467,4,235,2017-02-07 13:08:01,http://hamill.name/alysa,0.4104049618,89.180.231.131,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n10468,4,235,2017-01-18 03:43:36,http://mueller.net/idella,0.8401533589,197.213.157.14,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n10469,4,235,2017-01-12 03:31:42,http://beer.name/reese,0.1363918534,160.74.23.167,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n10470,4,235,2017-02-10 06:14:13,http://vonabbott.co/lenora,0.5569875980,156.60.24.130,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n10471,4,235,2017-01-01 11:44:16,http://dickens.net/agustin,0.7226600862,40.7.158.85,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n10472,4,235,2017-03-07 18:50:59,http://cristveum.com/maci.bernier,0.3112403492,162.247.214.22,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n10473,4,235,2017-06-07 18:59:35,http://huels.org/colby,0.3971920870,201.243.236.196,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n10474,4,235,2017-06-03 13:48:34,http://mcglynn.net/gertrude_sanford,0.1286912343,110.126.192.17,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n10475,4,235,2017-02-11 12:37:27,http://connellykihn.info/prince,0.5799262610,174.157.220.17,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n10476,4,235,2017-04-15 16:34:05,http://doyle.biz/myriam,0.6533878646,99.72.233.59,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n10477,4,235,2017-05-05 06:55:16,http://blickbalistreri.net/elfrieda.marquardt,0.5345555233,47.42.197.135,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n10478,4,235,2017-03-06 22:18:41,http://bergnaumdooley.com/dashawn,0.0956727523,50.147.199.22,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n10479,4,235,2017-02-11 15:20:55,http://anderson.name/stanford_collier,0.4157616880,114.58.165.7,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n10480,4,235,2017-02-25 06:40:36,http://bednar.io/maritza,0.7957961496,89.34.116.152,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n10481,4,235,2017-02-05 23:56:37,http://kunze.info/felix_ziemann,0.7467728283,179.98.214.229,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n10482,4,235,2017-05-19 02:29:56,http://kshleringutmann.net/boris,0.4164079174,211.141.32.212,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n10483,4,235,2017-04-26 11:03:21,http://corkery.net/vivian,0.8206122476,21.99.105.42,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n10484,4,235,2017-01-24 08:10:20,http://walkergreenfelder.info/gage_wisozk,0.8904756572,112.197.162.4,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n10485,4,235,2017-01-04 00:45:18,http://predovicaufderhar.info/janiya_bruen,0.9720644648,182.184.207.165,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n10486,4,235,2017-02-14 06:52:49,http://emmerichheaney.biz/rolando,0.4915439858,155.21.178.24,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n10487,4,235,2017-01-12 03:58:19,http://lehnerroob.biz/eileen_huels,0.3230373007,82.72.218.253,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n10488,4,236,2017-01-12 10:41:58,http://bosco.info/juanita,0.8215787053,133.204.12.119,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n10489,4,236,2017-03-07 10:35:05,http://bednarfeil.org/adelbert,0.6908538357,147.89.109.81,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n10490,4,236,2017-05-02 20:15:55,http://ebert.io/theron_veum,0.4907814716,118.180.197.210,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n10491,4,236,2017-03-03 10:32:50,http://roob.net/santa,0.5856656290,126.15.188.234,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n10492,4,236,2017-02-05 07:45:04,http://schummharvey.org/martine,0.8861414911,203.226.163.254,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n10493,4,236,2017-03-06 17:16:12,http://gorczany.net/kendall_boehm,0.3041111055,198.11.64.28,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n10494,4,236,2017-04-28 23:47:52,http://koeppnader.info/ariane.runolfon,0.4240416080,108.30.188.89,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n10495,4,236,2017-05-01 19:26:02,http://shields.name/baylee,0.4990114562,200.203.135.164,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n10496,4,236,2017-05-13 23:01:19,http://hoppekuhic.io/rachel.ruel,0.7579896293,193.241.231.242,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n10497,4,236,2017-03-20 03:54:39,http://rau.io/myra_smitham,0.8070058865,78.245.69.36,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n10498,4,236,2017-02-16 00:18:23,http://bernier.net/lauryn,0.0479388910,175.51.241.162,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n10499,4,236,2017-02-27 03:17:03,http://rennerheidenreich.org/eleanora,0.4933401575,68.17.37.221,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n10500,4,236,2017-01-15 12:46:58,http://rauharris.org/sandrine,0.9667560721,179.50.246.63,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n10501,4,236,2017-04-03 08:04:32,http://mayer.name/dolores,0.7951249153,216.61.151.27,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n10502,4,236,2017-04-16 10:32:44,http://larsonturner.biz/laurianne,0.8963280140,144.6.189.169,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n10503,4,236,2017-01-14 01:21:39,http://willwolf.biz/noelia.kerluke,0.8580412727,80.220.233.18,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n10504,4,236,2017-01-02 18:12:18,http://balistrerisenger.biz/gustave,0.8909889183,73.35.75.178,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n10505,4,236,2017-04-29 02:53:44,http://barton.info/jeanne,0.3107550533,228.58.118.108,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n10506,4,236,2017-02-15 13:25:18,http://pfannerstill.org/delores,0.7102466642,12.75.118.162,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n7484,3,166,2017-03-14 06:01:38,http://flatleyhilpert.name/demarcus,,253.183.175.158,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n7485,3,166,2017-03-10 03:27:58,http://nitzschesatterfield.info/gardner.hansen,,151.77.161.77,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n7486,3,166,2017-05-02 11:01:46,http://lockman.biz/omer.morar,,141.15.182.72,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n7487,3,166,2017-01-14 22:50:37,http://cronawisozk.biz/ian_cremin,,128.93.19.151,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n7488,3,166,2017-03-15 18:06:30,http://flatley.name/may_wintheiser,,15.86.58.222,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n7489,3,166,2017-06-09 15:53:11,http://smitham.info/jarrod,,225.123.59.120,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n7490,3,166,2017-01-17 12:36:18,http://cain.io/leanna,,37.141.118.248,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n7491,3,166,2017-03-18 02:35:38,http://dickiwalsh.name/dashawn_dickinson,,240.108.81.123,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n7492,3,166,2017-03-18 21:09:21,http://goldnerschowalter.org/winifred,,145.107.177.235,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n7493,3,166,2017-02-09 20:48:53,http://schmidtebert.io/delfina,,25.33.97.99,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n7494,3,166,2017-05-27 19:35:01,http://kautzer.com/rodolfo_botsford,,80.251.190.183,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n7495,3,166,2017-01-17 18:30:21,http://heidenreichwelch.name/mariela,,51.212.156.235,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n7496,3,166,2017-04-23 07:51:12,http://glover.info/amos_farrell,,219.148.94.141,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n7497,3,166,2017-05-19 06:05:34,http://kemmer.info/malinda_lemke,,209.252.8.153,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n7498,3,166,2017-06-07 13:24:19,http://volkmankemmer.com/zakary_denesik,,169.170.121.22,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n7499,3,166,2017-02-08 18:12:03,http://welch.io/delbert,,30.229.213.70,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n7500,3,166,2017-01-10 05:37:50,http://abbottgrant.io/vesta_mohr,,44.169.79.194,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n7501,3,166,2017-06-10 08:29:05,http://block.name/glenda.jenkins,,200.136.139.72,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n7502,3,166,2017-06-04 20:08:54,http://robel.net/dahlia_boyle,,8.100.143.28,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n7503,3,166,2017-01-02 22:45:35,http://daniel.net/jillian.kuhn,,182.39.254.94,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n7504,3,166,2017-04-29 18:56:14,http://larkin.net/twila_sanford,,114.101.173.37,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n7505,3,166,2017-05-12 11:54:16,http://robertsbatz.name/rashawn_monahan,,20.2.185.221,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n7506,3,166,2017-03-31 10:45:03,http://keeblerhane.name/americo,,46.249.99.10,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n7507,3,166,2017-04-26 11:09:14,http://oconner.net/boyd.lebsack,,119.65.127.193,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n7508,3,166,2017-01-19 07:28:30,http://champlin.com/aurelie,,188.71.46.203,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n7509,3,166,2016-12-19 02:15:17,http://wilkinsongulgowski.name/flavio,,34.135.26.77,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n7510,3,166,2017-01-26 21:11:24,http://fritsch.name/zoila,,252.91.225.120,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n7511,3,166,2017-01-13 14:02:00,http://miller.org/kieran,,177.65.233.201,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n7512,3,166,2017-04-05 14:39:49,http://fadelwilkinson.io/penelope.price,,213.139.170.201,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n7513,3,166,2016-12-16 08:43:40,http://adams.org/olaf.reichert,,215.45.128.193,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n7514,3,166,2017-04-17 05:52:40,http://sawayn.co/gerard_swaniawski,,68.216.126.88,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n7515,3,166,2017-02-04 08:12:22,http://vandervort.biz/valerie,,71.25.167.182,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n7516,3,166,2017-01-11 12:08:38,http://damore.org/maida,,134.29.49.223,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n7517,3,166,2017-02-25 18:23:17,http://wilkinsonswaniawski.co/judge.bashirian,,175.13.235.72,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n7518,3,166,2017-01-26 04:57:28,http://cronamarks.org/margot,,185.13.133.198,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n7519,3,166,2017-05-09 18:29:36,http://littel.name/edward,,62.221.238.152,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n7520,3,166,2017-05-27 00:03:53,http://mertzthiel.net/ken.bailey,,248.188.232.25,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n7521,3,166,2017-04-08 21:26:47,http://kundelowe.info/roxane.schiller,,204.217.176.130,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n7522,3,166,2017-01-30 10:56:11,http://hahnwalker.net/betty.schmitt,,237.207.229.119,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n7523,3,167,2017-04-21 03:44:16,http://moen.org/ahmed,,241.206.93.229,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n7524,3,167,2017-03-29 19:06:18,http://vonstracke.name/retta,,136.93.29.97,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n7525,3,167,2017-03-22 03:53:41,http://lynch.info/stephanie,,226.30.73.11,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n7526,3,167,2017-01-25 13:12:58,http://gleichnerhomenick.com/raheem.boyer,,240.68.185.58,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n7527,3,167,2017-05-24 11:23:55,http://dachbarrows.org/kelli.schmidt,,88.189.217.38,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n7528,3,167,2016-12-23 10:16:08,http://ondricka.io/bernice.gerhold,,26.162.170.152,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n7529,3,167,2017-03-14 07:01:40,http://haley.co/zola_sanford,,34.254.190.181,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n7530,3,167,2017-03-26 10:38:18,http://wuckert.info/kacey_fritsch,,9.206.129.18,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n7531,3,167,2017-03-22 12:09:20,http://ratke.co/lucas,,93.174.219.193,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n7532,3,167,2016-12-22 15:04:26,http://bechtelarmosciski.co/hosea,,117.230.202.130,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n7533,3,167,2017-06-12 09:35:24,http://parisian.com/rico,,239.228.207.245,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n7534,3,167,2017-04-13 03:10:56,http://stromanwintheiser.name/vladimir_nienow,,55.46.151.20,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n7535,3,167,2017-01-03 04:36:26,http://douglashomenick.biz/brittany_abernathy,,124.102.179.138,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n7536,3,167,2017-03-22 10:26:05,http://koepp.name/brycen,,203.158.48.26,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n7537,3,167,2017-05-02 01:48:38,http://prosacco.biz/claudie_kutch,,105.254.251.179,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n7538,3,167,2017-01-24 21:10:29,http://collier.net/josephine,,20.138.13.140,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n13395,5,301,2017-02-07 13:57:28,http://zulaufmckenzie.org/lisa_dietrich,,39.64.120.218,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n13396,5,301,2017-03-31 12:20:21,http://senger.org/jon.pacocha,,189.163.141.182,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n13397,5,301,2017-05-01 10:09:14,http://blick.co/manley,,197.143.77.204,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n13398,5,301,2017-01-12 06:26:11,http://lockman.org/edythe,,119.116.82.21,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n13399,5,301,2017-04-14 01:41:39,http://dickialtenwerth.io/braxton,,52.195.149.232,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n13400,5,301,2017-03-04 02:05:26,http://schinner.io/colt,,232.218.107.211,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n13401,5,301,2016-12-15 23:30:47,http://kuphalcummerata.com/coralie.flatley,,241.62.60.91,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n13402,5,301,2017-03-17 21:33:57,http://emmerich.io/tommie,,249.235.4.68,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n13403,5,301,2016-12-31 01:23:17,http://beatty.io/roxane,,44.212.154.101,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n13404,5,301,2017-03-11 18:30:35,http://buckridgewuckert.biz/marcel,,207.68.154.46,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n13405,5,301,2017-05-17 03:08:53,http://berge.org/kim_bernier,,175.2.50.220,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n13406,5,302,2017-04-26 13:13:01,http://kunde.io/camille,,205.232.87.113,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n13407,5,302,2017-05-21 20:35:14,http://goldnerheidenreich.biz/asha,,30.209.52.244,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n13408,5,302,2017-03-21 16:22:09,http://kautzer.info/lavern.predovic,,163.5.212.90,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n13409,5,302,2017-05-17 12:41:11,http://wittingshanahan.name/harmony_collier,,22.171.133.174,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n13410,5,302,2016-12-22 13:15:32,http://kuhic.com/toy,,40.11.167.202,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n13411,5,302,2017-01-15 14:10:28,http://huel.co/dante_green,,68.156.189.107,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n13412,5,302,2017-02-19 11:22:21,http://bauchbashirian.name/marcellus.adams,,31.7.25.153,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n13413,5,302,2017-06-03 22:15:44,http://corkery.net/marjorie,,119.187.42.231,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n13414,5,302,2017-05-29 18:09:24,http://kocarroll.biz/sister.beier,,222.243.58.254,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n13415,5,302,2017-05-21 13:17:04,http://runolfon.biz/obie.west,,247.252.77.254,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n13416,5,302,2017-02-07 23:24:28,http://ko.name/oswald,,166.235.24.217,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n13417,5,302,2017-04-01 16:05:21,http://hackett.org/johanna,,33.244.16.234,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n13418,5,302,2017-05-24 08:06:43,http://kutch.org/courtney,,161.120.20.37,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n13419,5,302,2017-04-11 20:35:29,http://davis.co/bonnie_botsford,,225.32.204.191,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n13420,5,302,2017-03-17 15:35:10,http://hand.name/ricardo.kertzmann,,2.7.138.139,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n13421,5,302,2016-12-28 03:36:23,http://raunicolas.net/ronaldo,,160.178.55.148,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n13422,5,302,2017-03-25 08:49:27,http://mcclure.io/napoleon_ruel,,200.24.220.4,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n13423,5,302,2017-06-04 05:50:19,http://christiansenbrown.com/kyle,,159.122.245.60,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n13424,5,302,2017-04-25 00:32:49,http://hilll.biz/shaun.johnston,,98.154.86.139,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n13425,5,302,2017-02-28 15:48:26,http://gerlach.org/guido,,61.243.252.45,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n13426,5,302,2017-01-11 16:58:40,http://barton.com/clemens.osinski,,126.42.98.250,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n13427,5,302,2017-03-31 10:30:59,http://wilkinson.com/leola.hoppe,,50.90.241.91,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n13428,5,302,2017-05-01 05:03:21,http://gutmannnader.com/rodrick.roob,,61.48.105.112,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n13429,5,302,2017-01-01 03:02:36,http://pagac.co/berniece.windler,,167.192.118.31,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n13430,5,302,2017-06-02 05:16:35,http://parisianullrich.name/serena,,20.174.57.44,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n13431,5,302,2017-05-12 00:57:29,http://casper.com/lisette.smith,,117.62.135.104,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n13432,5,302,2017-06-09 15:11:44,http://bayer.co/francisca_kub,,146.53.179.223,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n13433,5,302,2017-02-03 04:34:49,http://collins.name/annetta.wisozk,,235.24.196.225,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n13434,5,302,2017-05-13 03:42:19,http://faheybednar.co/lilian,,145.175.121.143,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n13435,5,302,2017-05-30 17:26:00,http://schowalterschumm.net/mariela,,105.74.133.177,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n13436,5,302,2017-02-05 16:34:50,http://fritsch.name/mia_dooley,,74.27.207.67,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n13437,5,302,2016-12-26 03:00:21,http://weinat.com/dee.ruecker,,252.122.26.147,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n13438,5,302,2017-03-02 16:50:34,http://mckenzie.com/maddison.waelchi,,169.116.81.44,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n13439,5,302,2017-05-16 21:42:12,http://smithstroman.biz/edyth.gibson,,103.11.7.180,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n13440,5,302,2017-03-31 05:55:31,http://treutel.name/madison.padberg,,72.101.12.164,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n13441,5,302,2017-03-15 13:05:54,http://stoltenbergmayert.net/elmo,,146.57.149.238,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n13442,5,302,2017-03-29 02:04:19,http://konopelski.net/kali,,206.100.227.58,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n13443,5,302,2017-03-17 08:55:54,http://windler.io/alexandra.jacobson,,183.55.7.56,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n13444,5,302,2017-06-13 19:03:37,http://bednarbednar.net/letha.corwin,,3.174.22.17,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n13445,5,302,2017-03-05 04:20:14,http://pfannerstill.org/cordie.heel,,169.100.18.247,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n13446,5,302,2017-03-20 04:11:41,http://parisian.io/jerrod_hartmann,,3.135.200.210,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n13447,5,302,2017-05-17 12:32:10,http://daughertyferry.biz/joseph.torp,,189.55.249.144,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n13448,5,302,2017-05-16 12:13:05,http://feesteichmann.io/bradly_schumm,,119.208.222.217,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n13449,5,302,2016-12-22 20:51:08,http://johnstonmacejkovic.co/jerrod_lynch,,171.219.45.99,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n16398,6,372,2017-03-29 07:47:11,http://hermiston.biz/agnes_block,0.1251294066,26.111.147.46,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n16399,6,372,2017-01-09 11:13:52,http://prosaccofranecki.name/etha,0.8027375769,119.25.96.133,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n16400,6,372,2017-01-20 04:35:35,http://kirlin.biz/cameron_abernathy,0.1366641467,88.60.28.181,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n16401,6,372,2017-04-16 05:27:19,http://moriette.name/camille,0.6796051945,147.35.158.196,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n16402,6,372,2017-05-12 19:30:24,http://kuhlman.biz/vernice,0.9534140100,223.222.115.48,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n16403,6,372,2017-03-15 23:02:35,http://aufderharluettgen.org/laria_rodriguez,0.1768131380,162.51.6.78,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n16404,6,372,2017-03-11 07:35:46,http://adams.co/ena.oconnell,0.7475443611,233.65.173.122,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n16405,6,372,2017-03-30 17:44:08,http://gaylordbogisich.name/fredrick,0.2895152905,212.108.187.247,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n16406,6,372,2017-03-22 01:10:18,http://ziemekaulke.com/maybelle.kutch,0.9330083294,97.61.8.190,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n16407,6,372,2017-04-22 13:07:49,http://rodriguez.info/nyasia.lockman,0.3340102074,185.230.224.199,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n16408,6,372,2017-05-16 16:23:54,http://jaskolskiokon.biz/douglas,0.3438057692,72.68.14.59,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n16409,6,372,2016-12-23 11:54:20,http://parkerprosacco.org/darius,0.1974158276,117.163.252.126,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n16410,6,372,2017-02-27 18:58:58,http://heaneyorn.net/marlene,0.4106341421,163.139.81.63,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n16411,6,372,2017-05-19 04:18:55,http://jacobiko.net/tiana.gorczany,0.7702360372,222.159.174.142,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n16412,6,372,2016-12-29 22:55:03,http://kub.io/bertram_jaskolski,0.0653712018,249.56.138.245,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n16413,6,372,2017-03-03 08:47:43,http://reichelkuhn.io/arvel_schneider,0.7262177865,115.4.119.239,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n16414,6,372,2017-04-20 03:07:29,http://zboncak.com/jarod.funk,0.7239719221,38.224.209.54,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n16415,6,372,2017-02-24 18:07:54,http://monahan.io/mallory.conroy,0.0426637946,98.37.205.89,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n16416,6,372,2017-05-13 13:12:36,http://rippinondricka.biz/eliezer,0.8816189796,194.55.40.40,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n16417,6,372,2017-05-11 03:19:06,http://morar.co/wilfrid_emmerich,0.1699453918,129.195.32.24,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n16418,6,372,2017-03-13 19:03:25,http://shanahan.net/duane_swift,0.0619036335,192.24.150.72,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n16419,6,372,2016-12-25 05:50:37,http://vonrueden.info/ida_veum,0.6345671305,94.35.183.201,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n16420,6,372,2016-12-15 18:58:48,http://macgyver.co/jose,0.7812324820,219.5.168.125,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n16421,6,372,2017-03-24 17:11:28,http://leffler.org/maggie_quitzon,0.7008069996,229.65.13.131,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n16422,6,372,2017-06-05 01:42:58,http://robelryan.biz/jacynthe.auer,0.8802381139,27.99.240.38,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n16423,6,372,2017-03-28 14:28:35,http://hammes.co/travon,0.7156870522,48.77.211.133,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n16424,6,372,2016-12-17 04:58:56,http://von.info/caitlyn,0.4957561939,56.188.104.107,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n16425,6,372,2017-02-20 06:21:54,http://quigley.name/dock,0.3595924243,34.223.92.68,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n16426,6,372,2017-02-13 13:19:38,http://muller.org/alize.daugherty,0.1746717026,200.167.253.165,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n16427,6,372,2017-06-06 03:05:21,http://kub.org/annie.kunde,0.4328178215,111.117.65.39,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n16428,6,372,2017-03-15 11:18:03,http://hillshilll.com/claudia,0.3160476002,208.212.47.183,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n16429,6,372,2017-02-18 12:27:31,http://carroll.info/viviane,0.8305244732,43.106.182.249,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n16430,6,372,2017-03-27 12:53:53,http://skilesheidenreich.org/kiarra,0.8520098704,103.28.116.22,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n16431,6,372,2017-02-18 06:54:21,http://conroy.net/fabiola,0.3322204443,21.197.178.204,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n16432,6,372,2017-01-13 02:23:52,http://eichmann.net/hilton.lowe,0.5071588830,57.85.4.220,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n16433,6,372,2017-02-11 09:44:00,http://reilly.name/fred,0.2999466254,56.218.107.116,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n16434,6,372,2016-12-31 20:12:21,http://prosacco.info/betsy_davis,0.8719771885,221.38.73.244,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n16435,6,372,2017-03-21 14:01:14,http://effertz.info/lexus.konopelski,0.9529029325,140.112.133.106,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n16436,6,372,2017-02-02 18:10:41,http://harveymccullough.co/rollin.gulgowski,0.0003648808,215.177.197.129,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n16437,6,372,2017-01-14 16:03:59,http://herzogkertzmann.name/rico,0.2950396543,28.141.136.35,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n16438,6,372,2017-02-21 17:51:29,http://wehner.com/zoila,0.0115972739,168.77.220.253,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n16439,6,372,2016-12-21 23:46:24,http://gorczany.info/emilia,0.9197509855,5.209.209.216,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n16440,6,372,2017-01-03 10:05:37,http://metz.io/verda.rohan,0.4085709892,22.133.81.234,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n16441,6,372,2017-04-02 21:05:29,http://schimmel.co/rowland_hilll,0.9718457163,202.16.16.140,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n16442,6,372,2017-04-17 20:09:49,http://mclaughlincronin.org/nicola_fadel,0.2946698773,228.85.166.21,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n16443,6,372,2017-03-05 19:45:52,http://balistreri.biz/vidal_feest,0.2746982211,183.161.143.11,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n16444,6,372,2017-01-31 22:53:52,http://sanford.org/juwan,0.7636329492,43.135.177.211,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n16445,6,372,2016-12-27 07:34:12,http://runolfonaufderhar.info/corene,0.6519548338,13.70.110.211,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n16446,6,372,2017-04-04 13:59:39,http://sawayn.co/chyna_schaefer,0.8836918461,124.230.94.101,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n16447,6,372,2016-12-25 22:42:22,http://zboncakmcclure.io/evert,0.0844935369,122.237.77.146,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n16448,6,372,2017-04-01 17:12:46,http://gutkowski.info/stephanie.aufderhar,0.9587922765,189.234.225.230,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n3576,2,78,2017-04-03 15:00:30,http://kuvalis.co/imelda_dooley,0.9513122524,160.145.236.93,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n3577,2,78,2017-05-12 00:15:23,http://dachhills.io/andreane.jacobson,0.4646776912,19.139.212.124,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n3578,2,78,2017-03-19 21:55:32,http://breitenberg.co/domenico,0.8568929204,41.116.63.175,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n3579,2,78,2017-01-31 05:17:16,http://stiedemann.info/ali,0.9001517863,123.220.214.32,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n3580,2,78,2017-01-18 07:59:38,http://kingernser.biz/rita,0.9801864135,189.102.139.184,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n3581,2,78,2017-02-14 05:42:18,http://johnston.net/justyn,0.0908603178,12.175.19.52,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n3582,2,78,2017-01-13 19:32:24,http://kautzer.com/jarod_pollich,0.4904784161,9.206.220.41,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n3583,2,78,2017-03-14 09:12:09,http://marquardt.biz/liam.mills,0.0764839083,7.39.185.147,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n3584,2,78,2017-02-07 01:15:41,http://armstrong.com/augustine_thompson,0.6219164065,82.252.36.10,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n3585,2,78,2016-12-20 00:34:03,http://bogisich.name/isabell,0.7597031035,39.207.194.120,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n3586,2,78,2016-12-25 00:54:39,http://sporer.name/cathryn_beer,0.3494657758,31.43.150.207,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n3587,2,78,2017-05-18 22:45:18,http://rice.net/jaden.beier,0.8690038546,238.126.151.224,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n3588,2,78,2017-05-15 11:07:12,http://moore.io/edgar_paucek,0.7622942359,152.147.186.208,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n3589,2,78,2017-05-05 18:03:36,http://marvinmurphy.org/valentine.buckridge,0.7924143528,137.136.228.28,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n3590,2,78,2016-12-15 22:25:23,http://yundt.biz/dixie.weinat,0.2856726376,30.183.44.48,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n3591,2,78,2017-01-25 13:44:13,http://rohancole.io/cydney,0.6165229552,103.221.90.201,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n3592,2,78,2017-05-18 06:32:21,http://wolffmcdermott.co/brenda,0.0943098887,194.225.252.247,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n3593,2,78,2017-04-12 13:07:54,http://stoltenberg.org/jena_brakus,0.0973377316,40.161.157.42,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n3594,2,78,2017-01-25 14:01:57,http://batz.io/freddie_streich,0.0089402871,139.234.47.42,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n3595,2,78,2017-05-24 02:58:13,http://feil.com/vallie.mayer,0.2172398835,117.10.186.121,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n3596,2,78,2017-02-18 23:43:33,http://okonlueilwitz.com/cory.stanton,0.4150208213,222.165.33.19,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n3597,2,78,2017-05-13 09:48:09,http://ledner.info/katelynn.wyman,0.1625432812,48.9.243.84,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n3598,2,78,2017-03-05 08:03:21,http://fay.com/simeon,0.2633522831,146.252.155.201,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n3599,2,79,2017-04-17 13:53:10,http://herman.biz/blanca,0.1521463905,48.206.254.168,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n3600,2,79,2017-06-08 22:19:34,http://kemmer.name/sammie.king,0.1008228036,166.101.124.54,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n3601,2,79,2017-02-21 13:43:45,http://heller.org/webster,0.5002912926,248.22.19.229,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n3602,2,79,2017-03-15 06:03:38,http://lakin.net/luella,0.1469201658,93.154.167.78,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n3603,2,79,2017-01-24 01:35:32,http://schuster.co/eric.sawayn,0.4941361290,225.143.52.14,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n3604,2,79,2017-02-16 23:08:53,http://bechtelar.name/zoe,0.9550003363,212.132.67.78,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n3605,2,79,2017-01-26 22:34:36,http://yostgrimes.org/justina.rolfson,0.9142675474,107.233.177.55,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n3606,2,79,2017-01-29 12:52:04,http://schillerziemann.com/haven.monahan,0.8873877151,76.14.215.158,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n3607,2,79,2017-03-15 14:17:09,http://yost.io/dane_miller,0.1102816271,182.103.66.111,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n3608,2,79,2016-12-28 17:25:32,http://lehner.net/darrel,0.8827334065,191.99.253.155,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n3609,2,79,2017-03-08 14:57:01,http://raynor.info/buddy.stehr,0.5616336583,128.234.75.248,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n3610,2,79,2017-02-25 01:20:05,http://dickinson.com/annalise,0.6406560648,22.95.101.215,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n3611,2,79,2017-01-30 18:24:55,http://ernser.net/demetrius,0.0511146229,48.130.92.134,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n3612,2,79,2017-01-28 07:27:53,http://kris.com/ransom,0.7012343285,146.133.18.212,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n3613,2,79,2017-06-08 19:17:40,http://cristblanda.com/angeline_gaylord,0.7385760602,8.137.172.246,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n3614,2,79,2017-05-06 07:29:24,http://considinehilpert.name/duane_ruel,0.0873713664,176.76.241.79,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n3615,2,79,2017-05-01 22:02:50,http://boyeroconner.com/mike,0.2064206664,167.77.167.229,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n3616,2,79,2017-03-14 00:22:17,http://prohaska.biz/jeff,0.1428393160,141.191.236.178,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n3617,2,79,2017-04-27 21:48:09,http://schultz.info/zoe_gulgowski,0.6046281189,174.113.39.170,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n3618,2,79,2017-01-05 09:12:17,http://okuneva.co/bethany_jacobson,0.8969673267,253.66.70.35,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n3619,2,79,2017-04-09 11:28:44,http://hicklestiedemann.org/abbigail,0.7665452472,124.138.218.4,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n3620,2,79,2017-03-26 06:26:22,http://boyermoriette.name/jeanette,0.3316051296,140.245.156.121,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n3621,2,79,2017-05-25 03:22:17,http://ziemanntromp.io/lucy,0.8176562502,82.80.89.38,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n3622,2,79,2017-05-15 10:07:43,http://wittingbode.biz/ferne.carroll,0.0739457834,139.243.83.26,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n3623,2,79,2017-05-12 20:26:53,http://gulgowskizulauf.com/jettie_kreiger,0.9327555255,63.238.24.176,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n3624,2,79,2017-02-14 09:48:02,http://weinat.name/fatima,0.9276834610,21.105.201.100,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n3625,2,79,2017-06-13 17:54:38,http://ritchiegleichner.org/betty,0.6594940822,69.223.111.127,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n3626,2,79,2017-04-20 09:58:37,http://howell.com/lamont,0.9814672343,249.78.51.25,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n3627,2,79,2017-04-28 22:21:45,http://beer.name/sigrid,0.1406056101,55.151.151.132,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n10507,4,236,2017-03-08 00:27:33,http://denesik.org/earnest,0.4804708479,151.155.96.84,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n10508,4,236,2017-02-04 22:45:01,http://pollich.biz/nestor,0.8781342456,213.21.224.57,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n10509,4,236,2017-01-05 17:22:40,http://hackett.info/theresia,0.3041773127,138.214.58.176,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n10510,4,236,2017-03-11 06:29:58,http://ward.io/stone_kunde,0.7171746772,169.42.171.177,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n10511,4,236,2017-04-21 22:16:45,http://pouros.biz/baylee_kris,0.3358408031,136.64.113.242,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n10512,4,236,2017-05-12 17:22:31,http://weinat.org/audrey,0.0395049489,64.120.197.228,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n10513,4,236,2017-03-21 03:39:15,http://dooley.info/arne.dicki,0.8566855584,77.48.56.33,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n10514,4,236,2017-01-15 13:45:14,http://heaney.co/salma.towne,0.5998052326,37.114.76.236,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n10515,4,236,2017-01-11 00:52:23,http://vandervort.org/serenity,0.4032090139,145.99.68.230,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n10516,4,236,2017-04-24 05:50:34,http://nicolas.info/madalyn.simonis,0.5747144927,71.129.31.189,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n10517,4,236,2017-01-25 02:41:05,http://koelpin.info/jameson.veum,0.1142909061,59.170.6.150,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n10518,4,236,2017-02-11 01:50:42,http://westkozey.org/sage_braun,0.5506491675,11.238.124.121,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n10519,4,236,2017-01-28 20:19:29,http://howe.info/bert_boyer,0.2586218915,240.12.8.151,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n10520,4,236,2017-01-12 04:13:49,http://anderson.info/wilburn,0.2555767503,27.51.186.72,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n10521,4,236,2017-06-07 12:07:35,http://jaskolski.biz/lacey.balistreri,0.1809298892,209.47.237.171,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n10522,4,236,2017-01-21 23:24:32,http://ernserfritsch.info/brigitte_berge,0.6323257516,212.242.45.211,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n10523,4,236,2017-03-18 06:14:30,http://kohlermohr.info/gloria_hoeger,0.5609849873,185.153.147.209,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n10524,4,236,2017-03-26 22:34:25,http://hackettharris.org/sienna_lehner,0.8158671470,119.25.119.4,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n10525,4,236,2017-01-04 14:57:52,http://huelcorwin.name/price_huel,0.2985459190,92.77.115.111,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n10526,4,236,2016-12-20 21:01:48,http://lehner.name/ryder.auer,0.3975976496,136.151.222.224,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n10527,4,236,2017-04-01 18:48:12,http://koeppmiller.com/makenna,0.3918116529,69.139.138.79,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n10528,4,236,2017-02-11 09:59:16,http://rippin.co/johanna,0.6075352802,148.195.106.87,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n10529,4,236,2017-04-15 17:59:47,http://farrell.org/gladyce,0.7422126487,147.162.155.188,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n10530,4,236,2017-01-09 07:19:03,http://donnelly.net/piper_effertz,0.5163013935,157.186.65.105,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n10531,4,236,2017-03-29 12:21:18,http://kuvalichiller.co/montana,0.2173230074,222.171.193.205,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n10532,4,236,2017-01-09 16:31:39,http://bayer.biz/ronaldo,0.6960075351,245.242.225.5,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n10533,4,236,2017-04-20 20:04:54,http://klockorowe.io/dino,0.5428317097,34.109.65.150,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n10534,4,236,2017-01-04 01:59:46,http://lind.com/zoie.hane,0.9167601343,93.34.170.157,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n10535,4,236,2017-04-16 07:10:29,http://adams.info/pierre_langosh,0.2239015300,109.109.27.169,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n10536,4,236,2017-04-06 05:59:07,http://rice.net/hillard.ledner,0.4194894456,249.245.252.140,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n10537,4,236,2017-02-04 11:38:09,http://kaulke.io/maegan,0.8894278319,250.219.34.205,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n10538,4,236,2017-03-25 00:41:30,http://bashiriankulas.net/domingo.aufderhar,0.3949766793,43.30.240.245,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n10539,4,236,2017-04-10 22:42:14,http://schultzdare.io/gilda,0.9564966603,60.39.235.29,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n10540,4,236,2016-12-26 01:28:21,http://mann.info/demetris,0.2244504561,115.130.111.243,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n10541,4,236,2017-02-16 02:44:35,http://herman.org/rosendo,0.3690045413,107.249.200.220,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n10542,4,236,2017-02-06 01:44:52,http://fritsch.biz/eldon.harber,0.9712747596,132.48.4.249,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n10543,4,236,2017-04-09 04:24:17,http://muller.org/dora,0.3793870202,44.229.64.145,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n10544,4,236,2017-01-30 10:32:04,http://wolff.info/deonte.gutkowski,0.7486952481,59.228.176.39,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n10545,4,236,2016-12-18 18:34:49,http://kubwilderman.io/ole.homenick,0.2491658069,224.250.243.190,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n10546,4,236,2017-03-14 15:10:31,http://pfannerstill.name/velma.kuphal,0.5391647786,6.231.235.216,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n10547,4,236,2017-02-03 20:32:00,http://waelchi.name/thaddeus_christiansen,0.0753712235,151.227.97.111,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n10548,4,236,2017-05-23 02:33:14,http://windlerwintheiser.net/grayce,0.6054031355,163.22.233.39,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n10549,4,236,2017-01-04 08:38:37,http://brownhintz.io/erna,0.9204305397,171.219.161.233,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n10550,4,236,2017-04-03 07:28:26,http://tromp.co/christine_quitzon,0.6780338284,234.20.224.247,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n10551,4,237,2017-01-09 02:06:28,http://prosacco.biz/tyson_prohaska,0.6117945758,198.85.138.85,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n10552,4,237,2017-02-19 05:26:03,http://osinskirice.biz/adalberto_feeney,0.9402731208,135.122.91.40,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n10553,4,237,2017-06-10 07:10:39,http://mccullough.io/shaniya.okon,0.4881826648,15.86.169.226,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n10554,4,237,2017-06-13 05:16:41,http://hintz.biz/tina,0.4732681860,137.214.194.122,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n10555,4,237,2017-04-09 18:36:40,http://bernier.name/brook_barrows,0.4339356897,14.38.132.158,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n10556,4,237,2017-03-01 18:01:16,http://ricegerlach.co/rosa,0.3613574377,129.241.254.190,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n10557,4,237,2016-12-22 21:28:12,http://daughertysteuber.net/tatyana,0.1559138895,91.219.228.156,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n7539,3,167,2017-03-21 10:47:56,http://legrosnitzsche.net/frederique_renner,,241.192.123.113,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n7540,3,167,2017-03-03 21:33:26,http://danielruel.com/edgar,,154.104.114.243,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n7541,3,167,2017-05-05 21:15:21,http://vonrueden.biz/kimberly,,244.37.6.95,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n7542,3,167,2017-02-19 21:13:58,http://moen.name/norwood,,131.46.109.88,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n7543,3,167,2017-01-10 07:31:32,http://kunzerunolfsdottir.net/cale.schoen,,86.147.185.199,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n7544,3,167,2017-03-20 19:35:33,http://kuhlmanmertz.info/rhianna,,18.16.22.26,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n7545,3,167,2017-04-02 12:36:56,http://gleichnergreenfelder.net/ruth_frami,,175.5.69.70,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n7546,3,167,2017-02-04 22:22:23,http://hirthe.io/chet,,186.250.243.202,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n7547,3,167,2017-01-22 01:02:30,http://paucekschulist.net/dan,,90.238.102.183,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n7548,3,167,2017-06-13 18:32:46,http://murphyvolkman.com/chasity,,185.142.116.47,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n7549,3,167,2017-05-06 04:19:11,http://douglas.info/thea,,79.55.231.21,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n7550,3,167,2017-02-02 01:14:13,http://schoen.biz/elody_hilpert,,93.160.89.195,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n7551,3,167,2017-03-15 06:26:54,http://sporerfisher.info/benny,,18.131.50.106,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n7552,3,167,2017-06-10 13:27:56,http://pfannerstill.co/agnes.brakus,,157.118.245.133,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n7553,3,167,2017-01-14 21:57:56,http://lednerhane.com/annamarie,,146.172.232.54,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n7554,3,167,2017-05-04 17:09:31,http://mcclure.io/trystan,,157.141.240.185,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n7555,3,167,2016-12-15 06:40:08,http://kshlerin.net/hallie,,183.20.61.70,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n7556,3,167,2017-01-09 16:54:29,http://murphy.io/gladyce,,96.151.34.15,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n7557,3,167,2017-03-15 11:30:04,http://dietrichkreiger.com/pansy,,254.235.69.78,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n7558,3,167,2017-05-12 08:27:44,http://kshlerinstehr.biz/brock,,108.35.59.223,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n7559,3,167,2016-12-23 19:06:31,http://littelryan.io/ron,,120.127.145.182,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n7560,3,167,2017-04-10 05:30:18,http://kris.co/viva,,85.184.244.18,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n7561,3,167,2017-02-02 10:30:11,http://vonbartell.info/kirstin,,47.107.105.164,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n7562,3,167,2016-12-16 01:41:49,http://moen.io/santa,,85.165.172.9,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n7563,3,167,2017-04-26 02:53:49,http://rolfson.co/henry.dicki,,43.72.19.9,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n7564,3,167,2017-01-19 02:16:51,http://roberts.net/olga.schulist,,191.185.150.88,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n7565,3,167,2017-03-17 17:22:42,http://monahanfisher.biz/joelle,,43.67.150.140,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n7566,3,167,2017-01-25 04:17:26,http://schneider.biz/vivien,,116.118.21.149,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n7567,3,167,2017-06-11 03:02:42,http://lehner.biz/claire,,26.154.160.107,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n7568,3,167,2017-03-31 00:39:38,http://reinger.biz/darrel,,23.7.7.78,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n7569,3,167,2017-04-01 21:55:46,http://hettinger.net/deontae,,159.8.116.11,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n7570,3,167,2017-04-03 11:06:15,http://smitham.net/koby_cain,,40.179.192.232,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n7571,3,167,2017-06-01 00:26:02,http://leuschkeleannon.info/johnathan_smitham,,138.97.60.253,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n7572,3,167,2017-02-09 01:51:50,http://emard.com/gwendolyn.gleason,,149.116.226.218,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n7573,3,167,2017-03-02 10:21:46,http://fisherkoepp.org/wilson_schmeler,,12.45.224.103,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n7574,3,167,2017-02-06 07:52:56,http://block.io/nova.shanahan,,118.2.124.206,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n7575,3,167,2017-04-07 07:31:41,http://beckersawayn.net/clint,,216.141.214.21,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n7576,3,167,2016-12-26 17:39:24,http://beahan.biz/otho,,129.81.49.54,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n7577,3,167,2017-03-31 20:43:49,http://stehrspencer.info/emmalee.mraz,,113.86.72.120,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n7578,3,167,2017-05-17 12:46:07,http://swaniawski.name/luna_stanton,,156.27.85.27,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n7579,3,167,2017-05-15 02:25:33,http://lebsack.io/ally,,102.90.75.104,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n7580,3,167,2017-05-12 17:07:46,http://rutherford.net/haylie,,100.238.132.169,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n7581,3,167,2017-06-02 07:50:53,http://erdman.org/freddy.gulgowski,,59.91.247.217,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n7582,3,167,2017-01-27 02:05:52,http://cummerataernser.net/leslie_towne,,101.43.137.200,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n7583,3,167,2017-02-28 01:51:59,http://parker.org/melvin.kuhlman,,40.19.53.149,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n7584,3,167,2017-04-30 03:02:36,http://beerboyle.biz/jackie,,15.127.67.229,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n7585,3,167,2017-03-12 11:01:20,http://lefflerhermiston.com/alexanne.crooks,,42.6.226.168,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n7586,3,167,2017-04-05 20:23:00,http://gloverhoppe.info/araceli,,252.42.29.165,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n7587,3,167,2017-04-29 06:45:15,http://waelchi.io/ena.metz,,135.85.32.29,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n7588,3,167,2017-01-09 02:38:44,http://framifarrell.biz/tara_ebert,,13.191.206.226,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n7589,3,167,2017-03-10 22:12:28,http://bernhard.co/michaela,,179.3.184.72,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n7590,3,167,2017-03-17 19:13:04,http://schulist.org/marlene.hayes,,238.158.183.12,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n7591,3,168,2017-01-21 16:35:20,http://corkeryschmidt.com/isom,,227.163.227.236,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n7592,3,168,2017-04-07 05:39:20,http://hammes.info/danika,,23.122.221.36,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n7593,3,168,2017-03-06 03:46:24,http://flatley.co/sabrina.stehr,,5.119.158.233,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n7594,3,168,2016-12-15 16:52:49,http://grady.biz/fredrick,,51.4.89.107,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n13450,5,302,2016-12-31 19:05:17,http://ruel.com/thurman.kautzer,,102.47.222.92,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n13451,5,302,2017-01-11 14:48:49,http://muellerweimann.name/webster.hudson,,18.31.19.122,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n13452,5,302,2017-01-08 01:02:35,http://marks.com/lilyan_grant,,144.251.128.15,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n13453,5,302,2017-03-04 06:24:36,http://luettgen.org/vickie_bahringer,,150.17.50.16,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n13454,5,302,2017-01-13 04:14:19,http://baileyjohnston.co/duane.trantow,,22.186.208.123,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n13455,5,302,2017-01-23 23:55:13,http://funkhartmann.com/london,,109.65.129.209,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n13456,5,302,2017-04-17 01:38:54,http://rolfsonterry.info/arden,,68.98.57.227,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n13457,5,302,2017-04-12 13:38:45,http://lang.info/lucienne,,174.173.94.154,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n13458,5,302,2016-12-28 09:25:43,http://skilesritchie.io/brannon_keeling,,172.251.86.153,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n13459,5,302,2017-03-01 02:37:08,http://hirthe.co/diamond,,136.99.43.123,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n13460,5,302,2016-12-29 17:11:02,http://hahn.org/luz,,106.189.146.206,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n13461,5,302,2017-05-04 00:53:45,http://grimes.name/reva,,212.159.124.108,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n13462,5,302,2017-01-22 15:36:53,http://johnsonankunding.co/uriah,,162.66.164.70,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n13463,5,302,2016-12-14 14:10:19,http://shields.org/estrella,,188.133.214.109,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n13464,5,302,2016-12-20 21:40:51,http://pfefferhansen.co/garfield,,153.150.158.131,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n13465,5,302,2017-04-06 00:52:48,http://smith.info/dakota,,172.18.19.141,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n13466,5,302,2017-04-15 06:37:32,http://gutmannherzog.net/antwan,,61.205.74.92,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n13467,5,302,2016-12-19 08:16:12,http://pfannerstillgibson.info/jamison,,4.56.111.66,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n13468,5,302,2017-04-25 07:22:04,http://daniel.info/robbie,,167.14.99.139,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n13469,5,302,2016-12-14 19:34:54,http://schneider.io/kasandra.homenick,,28.233.200.67,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n13470,5,302,2016-12-17 16:24:31,http://gusikowskirice.info/vidal,,127.247.209.207,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n13471,5,302,2017-04-16 04:10:53,http://koch.biz/oswald,,165.32.192.176,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n13472,5,302,2017-04-29 07:22:55,http://metz.biz/trycia,,245.56.34.204,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n13473,5,302,2017-01-04 18:37:15,http://romaguera.co/mackenzie_kreiger,,3.119.182.223,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n13474,5,303,2017-05-21 18:38:16,http://mcclure.com/al_kuhn,,149.50.71.231,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n13475,5,303,2016-12-25 15:59:04,http://schroeder.biz/hermann,,3.61.80.38,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n13476,5,303,2017-02-22 17:56:44,http://kuphal.io/paige,,106.113.121.215,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n13477,5,303,2017-03-11 08:02:02,http://watsicaluettgen.com/ceasar,,108.78.81.221,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n13478,5,303,2017-04-14 01:29:22,http://balistrerihauck.net/jonathan,,31.112.84.164,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n13479,5,303,2017-02-26 23:36:04,http://skiles.org/rick,,224.90.239.171,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n13480,5,303,2016-12-30 15:04:12,http://stracke.co/maia_stroman,,166.26.105.80,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n13481,5,303,2017-05-21 04:36:58,http://medhurst.co/mina.johnston,,142.39.246.161,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n13482,5,303,2017-06-06 06:50:52,http://steuber.info/ena_heel,,197.230.37.101,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n13483,5,303,2017-01-04 12:24:07,http://conn.com/ed,,180.72.78.65,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n13484,5,303,2017-03-16 19:00:51,http://hirthe.biz/carlo.conroy,,94.254.194.208,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n13485,5,303,2017-01-31 12:22:27,http://klein.net/fredrick.spinka,,64.175.131.167,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n13486,5,303,2017-04-25 18:17:28,http://roberts.net/evan.auer,,106.25.55.179,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n13487,5,303,2017-05-06 03:15:05,http://naderhalvorson.co/hilbert,,2.34.91.187,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n13488,5,303,2017-01-11 15:15:31,http://gislason.org/yeenia.paucek,,169.145.8.241,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n13489,5,303,2017-03-03 00:37:17,http://sawaynruel.name/erick.shields,,31.146.142.235,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n13490,5,303,2017-01-12 14:49:38,http://olson.co/kamren,,3.224.139.96,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n13491,5,303,2017-01-21 18:41:47,http://langworth.io/lonzo,,61.219.112.200,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n13492,5,303,2017-01-29 09:01:01,http://sengerbergstrom.biz/maynard,,65.236.46.38,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n13493,5,303,2017-01-28 00:20:32,http://turcottedooley.biz/opal,,195.218.102.28,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n13494,5,303,2017-05-23 01:22:52,http://langosh.com/verna.sawayn,,109.227.89.243,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n13495,5,303,2017-05-03 09:42:27,http://hills.net/camryn,,154.232.119.192,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n13496,5,303,2017-02-11 02:13:27,http://stoltenberg.name/susana.ruel,,226.22.52.77,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n13497,5,303,2017-02-19 09:17:14,http://thiel.org/bradly,,83.153.176.64,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n13498,5,303,2017-02-18 20:49:48,http://mayerbednar.info/sabina,,75.10.176.51,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n13499,5,303,2017-05-15 06:15:26,http://frami.com/norbert,,150.108.112.226,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n13500,5,303,2017-02-15 15:39:56,http://kozeycummerata.org/emil_rodriguez,,133.118.156.237,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n13501,5,303,2016-12-27 21:00:41,http://kuhn.name/tad_schowalter,,147.41.134.210,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n13502,5,303,2017-01-26 16:26:54,http://king.net/lucinda_jenkins,,239.33.209.182,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n13503,5,303,2017-01-23 12:06:15,http://jerde.org/christopher_ward,,145.2.63.170,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n13504,5,303,2017-04-30 16:18:22,http://harris.org/tremaine_raynor,,101.182.143.244,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n16449,6,372,2017-06-02 06:25:17,http://muller.com/quinton,0.1973752570,116.37.229.156,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n16450,6,372,2017-03-06 04:37:14,http://wintheiserbrown.co/vincenzo,0.2030002086,111.16.62.83,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n16451,6,372,2017-04-10 08:03:47,http://kutch.org/sabina,0.2000365490,68.158.176.63,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n16452,6,372,2017-03-16 10:50:53,http://quitzoncollier.org/pete,0.9621480611,75.149.41.33,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n16453,6,372,2017-04-22 09:34:23,http://cartwrightwalter.info/wilber,0.0266869107,86.125.125.209,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n16454,6,372,2017-05-18 12:19:10,http://emard.biz/lemuel,0.8823735958,254.48.59.33,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n17072,6,386,2017-02-04 18:36:36,http://strosin.io/wayne,,216.248.126.173,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n16455,6,372,2016-12-15 17:58:23,http://torp.org/morgan.kozey,0.7551455952,60.63.129.211,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n16456,6,372,2017-03-18 08:58:39,http://macgyverchristiansen.name/alvah,0.2544162446,101.164.193.116,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n16457,6,372,2017-01-18 01:12:40,http://jastwalker.net/kaitlyn,0.5104458740,81.252.22.64,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n16458,6,372,2017-03-19 15:06:56,http://zulauf.co/jamir.rath,0.9617416213,116.168.139.36,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n16459,6,372,2017-02-13 08:16:59,http://waterscremin.io/jackson,0.6614676721,184.40.190.106,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n16460,6,372,2017-01-17 18:02:48,http://spencer.net/dedric_smitham,0.4800381135,103.7.149.107,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n16461,6,372,2017-03-19 04:08:34,http://wolfleffler.co/christelle,0.4860522215,246.43.63.148,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n16462,6,373,2017-03-12 06:09:02,http://ricekiehn.com/travon,0.1851442789,86.254.205.75,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n16463,6,373,2017-06-10 10:59:15,http://kshlerin.net/aisha,0.1888351377,74.160.71.71,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n16464,6,373,2017-02-14 12:58:00,http://blockmurray.co/carmine.volkman,0.1098374658,193.124.100.184,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n16465,6,373,2017-04-02 03:22:30,http://dietrich.info/ruben.schumm,0.0120726040,134.175.156.205,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n16466,6,373,2017-03-02 04:21:00,http://boehmschmidt.org/carole.sporer,0.3128258021,98.179.220.253,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n16467,6,373,2017-03-09 11:50:21,http://fritschharris.co/felipa.durgan,0.2001753947,98.192.84.134,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n16468,6,373,2017-04-30 16:35:03,http://beatty.io/griffin,0.9737877151,104.196.245.74,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n16469,6,373,2017-03-19 09:32:40,http://wolff.net/sidney.runolfsdottir,0.8808831126,78.45.164.164,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n16470,6,373,2017-05-03 19:29:26,http://torphyohara.net/demetris.veum,0.4468978187,202.225.174.118,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n16471,6,373,2017-03-30 09:20:48,http://trompstanton.net/cruz_shields,0.7088947760,210.117.156.13,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n16472,6,373,2016-12-30 14:43:46,http://willdaugherty.com/keshawn_bogan,0.5734123140,61.219.209.200,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n16473,6,373,2017-03-16 01:29:47,http://faheyschinner.io/diamond,0.9750494630,193.26.213.185,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n16474,6,373,2016-12-14 01:49:50,http://hauck.com/jeanie.greenfelder,0.8317289243,133.196.210.186,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n16475,6,373,2017-01-10 20:47:45,http://mcglynn.io/wyatt.haley,0.7854649868,167.86.132.219,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n16476,6,373,2017-06-06 21:05:38,http://schroeder.com/kory.schulist,0.3510878952,158.9.61.87,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n16477,6,373,2017-05-31 18:55:09,http://kuhlman.com/jordane_mante,0.8611408105,160.243.154.144,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n16478,6,373,2017-04-25 06:31:58,http://kuphal.com/tyler,0.8873420106,77.240.25.129,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n16479,6,373,2017-03-15 20:51:34,http://vandervortwisozk.com/katelyn.torphy,0.0599523671,55.63.82.188,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n16480,6,373,2017-01-04 07:54:29,http://cronin.co/grayce,0.7908632368,201.205.184.104,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n16481,6,373,2017-06-12 22:37:22,http://senger.info/newell_metz,0.3048645868,250.63.49.39,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n16482,6,373,2017-04-21 23:21:41,http://grimes.net/amelia,0.1316567862,177.53.93.40,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n16483,6,373,2017-01-14 18:33:05,http://pagacoconnell.com/arnaldo.gislason,0.7483362846,16.122.75.4,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n16484,6,373,2017-02-07 08:13:49,http://cummerata.io/manuela.kunze,0.0023618376,159.75.33.117,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n16485,6,373,2017-03-18 01:49:43,http://gaylord.net/shaina,0.0789131261,210.123.127.103,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n16486,6,373,2017-01-02 04:15:53,http://tromp.net/tod.fay,0.8889491983,134.109.102.168,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n16487,6,373,2017-01-05 06:50:52,http://kohler.io/jerry,0.6844462692,253.134.126.231,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n16488,6,373,2017-02-21 17:37:48,http://paucekhalvorson.biz/rollin,0.3516019262,225.145.155.239,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n16489,6,373,2017-01-18 10:59:46,http://kovacek.co/caitlyn_lemke,0.5204691914,161.10.210.85,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n16490,6,373,2017-04-09 04:07:36,http://lynchparisian.info/bart_purdy,0.1013379108,25.227.106.158,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n16491,6,373,2017-01-20 05:38:50,http://murray.org/neoma.klein,0.1695164730,123.194.12.145,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n16492,6,373,2017-01-20 07:29:48,http://mcclurerogahn.biz/perry.skiles,0.9867254774,222.216.220.76,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n16493,6,373,2017-05-28 11:52:52,http://luettgen.co/alphonso,0.4440297784,231.105.185.213,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n16494,6,373,2017-03-08 06:51:38,http://gislasonquitzon.name/sally,0.3633105596,117.188.38.90,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n16495,6,374,2017-01-09 17:20:44,http://danielhauck.net/cicero,0.4490735170,38.245.146.125,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n16496,6,374,2017-01-22 14:18:17,http://heathcote.io/yeenia,0.4122300320,97.197.28.238,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n16497,6,374,2017-03-03 03:31:48,http://cummings.net/adrianna_klein,0.5954648125,251.90.115.227,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n16498,6,374,2017-01-10 20:05:24,http://little.com/cloyd.walter,0.5211980380,65.177.90.215,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n3628,2,79,2016-12-21 05:59:25,http://crist.info/connie_bins,0.9138911387,129.203.143.170,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n3629,2,80,2017-04-16 15:54:08,http://stokes.co/roman,0.8297012614,31.185.243.211,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n3630,2,80,2017-02-18 14:32:47,http://binchneider.net/gerda,0.7488640118,211.249.152.246,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n3631,2,80,2017-04-20 22:18:03,http://powlowski.com/lukas_kemmer,0.3134077379,34.228.237.7,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n3632,2,80,2016-12-21 23:34:08,http://skilespredovic.name/matilde,0.5065128173,222.251.187.139,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n3633,2,80,2016-12-28 10:28:58,http://feest.name/etha,0.0786364232,230.157.20.194,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n3634,2,80,2017-05-20 00:26:37,http://mckenzie.org/chaya.douglas,0.3290803862,14.48.181.30,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n3635,2,80,2017-03-11 13:01:47,http://kautzer.org/kody.miller,0.4858843772,185.162.200.48,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n3636,2,80,2017-01-29 22:41:27,http://douglas.net/hans,0.8825739187,6.136.229.84,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n3637,2,80,2016-12-29 20:54:59,http://wolffrempel.net/kirsten,0.0850205734,115.32.178.142,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n3638,2,80,2016-12-21 15:40:16,http://hoppe.io/greg,0.9871615284,97.36.171.192,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n3639,2,80,2017-02-23 03:52:46,http://oreilly.net/henderson.ondricka,0.9905054879,165.117.60.32,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n3640,2,80,2017-02-20 00:58:15,http://hartmann.io/mariam,0.0749720015,234.228.81.84,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n3641,2,80,2017-02-20 17:05:15,http://lind.name/jerry.hoeger,0.0431920351,113.203.212.209,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n3642,2,80,2017-04-26 09:48:00,http://littelhilpert.com/emmy,0.5781848495,136.204.143.202,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n3643,2,80,2017-06-13 04:38:08,http://schuppe.name/chesley_hyatt,0.1105078752,201.175.6.125,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n3644,2,80,2017-04-13 12:02:10,http://yost.co/kellen_cole,0.4360119497,214.137.244.231,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n3645,2,80,2016-12-16 04:40:18,http://trantowrobel.info/amina_schumm,0.6903764688,171.248.53.24,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n3646,2,80,2017-05-28 09:53:28,http://wehner.net/drake.mraz,0.1656301198,125.116.106.27,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n3647,2,80,2017-03-04 14:33:26,http://mckenzie.com/lauren.johnson,0.5171414844,108.125.176.153,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n3648,2,80,2017-01-19 12:16:12,http://botsford.org/anastasia,0.8976783584,103.126.177.58,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n3649,2,80,2017-02-18 13:27:46,http://simonis.io/greg,0.1548457723,190.186.225.126,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n3650,2,80,2017-06-12 00:27:29,http://olson.name/eino_cummings,0.5379417945,251.94.19.192,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n3651,2,80,2017-03-29 20:14:02,http://reichelbahringer.biz/helmer,0.9349966692,67.245.90.212,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n3652,2,80,2017-03-31 16:20:09,http://raynor.com/matilda,0.9159226752,150.194.123.30,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n3653,2,81,2016-12-31 04:03:11,http://greenfelder.io/lorenz,0.3109537616,214.190.116.131,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n3654,2,81,2017-05-27 06:34:44,http://welch.io/hermann_spencer,0.6976900727,193.76.117.149,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n3655,2,81,2017-04-02 20:45:56,http://rempel.org/lucile.kuhn,0.8990942647,45.129.117.129,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n3656,2,81,2017-02-16 06:48:33,http://ziemann.io/jorge_bogisich,0.2123913256,83.95.129.176,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n3657,2,81,2017-06-10 04:41:37,http://cummerata.net/allison_luettgen,0.5312368882,205.141.11.174,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n3658,2,81,2017-02-24 08:02:08,http://turcotte.org/alford,0.5048272775,169.56.233.245,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n3659,2,81,2017-03-25 09:53:59,http://muller.com/abbigail,0.1188537265,23.248.133.58,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n3660,2,81,2016-12-21 05:32:37,http://glover.io/lamont,0.6565661835,82.133.88.242,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n3661,2,81,2017-03-04 20:03:26,http://ankunding.co/estel_johns,0.8308224698,196.234.50.12,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n3662,2,81,2017-02-11 09:23:15,http://heel.info/abdiel,0.3500677187,253.33.85.151,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n3663,2,81,2017-01-19 00:34:48,http://oharahuels.org/carli,0.9001636608,68.144.111.18,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n3664,2,81,2017-06-10 18:07:29,http://yost.co/constantin.stokes,0.5806744132,84.225.212.246,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n3665,2,81,2016-12-21 17:42:52,http://hodkiewiczwatsica.org/stewart_pacocha,0.2733941284,110.212.241.216,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n3666,2,81,2017-05-12 01:19:35,http://caspergorczany.info/christa,0.6989731268,106.36.35.70,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n3667,2,81,2017-01-09 23:14:02,http://bayer.info/kelsie_oconnell,0.5609915011,197.98.30.207,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n3668,2,81,2017-01-18 03:01:51,http://jakubowskicarroll.io/fabian,0.8868192423,103.247.229.185,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n3669,2,81,2017-03-23 07:02:10,http://bartontremblay.com/linnea,0.7641608996,11.175.219.221,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n3670,2,81,2017-01-09 07:40:22,http://padberg.io/jovanny,0.8688972377,15.104.75.241,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n3671,2,81,2016-12-25 11:27:47,http://kihn.biz/tamia,0.2865774740,59.176.168.208,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n3672,2,81,2017-02-06 20:49:16,http://marvin.org/elfrieda_schowalter,0.9149511090,111.181.49.52,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n3673,2,81,2017-04-01 02:03:51,http://reichert.co/bryon_mueller,0.7846001947,6.20.223.235,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n3674,2,81,2017-03-02 03:00:01,http://lakin.name/tristin_bosco,0.1090421002,105.72.251.206,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n3675,2,81,2017-06-09 16:49:54,http://swaniawski.co/laverne_haag,0.1925328005,77.71.116.74,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n3676,2,81,2017-04-26 13:52:12,http://volkmanrempel.org/dedrick_pagac,0.1823318305,34.124.53.111,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n3677,2,81,2017-05-12 08:15:08,http://heelbechtelar.info/alvina,0.8544303043,63.155.139.177,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n3678,2,81,2017-05-15 01:01:58,http://grimes.net/herman.wiza,0.2631234503,223.227.109.37,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n10558,4,237,2017-04-01 11:31:54,http://olsonzboncak.io/lew,0.8222770938,251.123.172.114,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n10559,4,237,2017-02-26 06:30:17,http://effertz.co/willow.trantow,0.6116384231,218.196.156.54,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n10560,4,237,2017-05-10 01:58:00,http://gorczany.name/golden_leuschke,0.2125930432,246.194.177.106,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n10561,4,237,2017-03-07 12:16:09,http://dietrich.net/ellsworth.kub,0.7639739247,244.147.214.160,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n10562,4,237,2017-01-21 09:55:28,http://grady.io/dee,0.5625695579,98.247.192.205,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n10563,4,237,2017-06-05 22:28:51,http://doyle.com/madyson,0.2010966497,194.83.95.133,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n10564,4,237,2017-03-04 11:49:07,http://aufderhar.co/verlie.krajcik,0.9460950671,220.107.146.216,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n10565,4,237,2017-03-02 03:08:30,http://mann.info/isaac_bogisich,0.0078230196,243.72.243.229,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n10566,4,237,2017-03-04 15:57:55,http://balistreri.co/cletus_sauer,0.0858922776,39.223.36.80,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n10567,4,237,2017-02-03 00:02:56,http://pouros.io/tanner,0.8202174981,31.57.9.70,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n10568,4,237,2017-02-21 05:37:39,http://shanahanmoore.com/lacey,0.2211712623,36.14.72.76,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n10569,4,237,2017-02-02 22:13:13,http://dubuque.info/andreanne,0.0587440729,207.223.215.91,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n10570,4,237,2017-06-12 16:09:44,http://runolfon.biz/domenica.mueller,0.0696271687,33.139.127.18,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n10571,4,237,2017-01-27 16:03:16,http://tillman.name/dimitri.ritchie,0.0771560471,129.230.176.146,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n10572,4,237,2017-06-07 15:57:20,http://wiegand.name/bridget_heidenreich,0.9598645716,218.156.78.114,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n10573,4,237,2017-05-10 12:21:26,http://nicolas.co/adeline,0.7137310064,243.249.65.147,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n10574,4,237,2017-03-28 19:13:14,http://hintzmueller.net/rose,0.0165286823,89.84.66.60,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n10575,4,237,2016-12-22 09:07:25,http://roob.co/jerrold.von,0.7307312732,9.218.204.106,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n10576,4,237,2017-03-10 10:00:59,http://toyskiles.biz/delmer,0.2528395374,139.75.26.253,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n10577,4,237,2017-04-03 02:08:52,http://kuhlman.name/roberto,0.1232882079,112.118.248.53,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n10578,4,237,2017-05-11 01:07:39,http://lowefisher.io/jacquelyn,0.9416824502,33.147.133.71,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n10579,4,237,2017-01-31 11:47:25,http://von.info/bailey_pouros,0.5309082330,36.29.198.61,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n10580,4,237,2017-03-22 01:50:37,http://feeneylindgren.name/wilburn,0.9433520767,160.93.216.150,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n10581,4,237,2017-04-12 05:15:29,http://oconnell.io/august_murray,0.5542342988,215.249.198.174,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n10582,4,237,2017-06-09 11:42:00,http://huel.info/jannie,0.3171781344,76.203.228.34,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n10583,4,237,2017-05-20 09:29:18,http://hilllschumm.io/ervin,0.1037897024,190.137.111.196,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n10584,4,237,2017-01-28 10:04:08,http://waelchischaden.biz/braden,0.4151032787,113.173.8.6,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n10585,4,237,2017-02-07 01:38:56,http://cummerata.net/easton,0.2455397445,161.42.227.226,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n10586,4,237,2017-01-15 03:04:19,http://gutkowski.info/kaia,0.1476172723,251.77.228.52,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n10587,4,237,2017-05-31 22:57:20,http://goodwinschultz.name/savion,0.4381016055,55.170.53.178,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n10588,4,237,2017-02-03 06:23:32,http://kuvalis.co/ronaldo,0.8467151677,24.87.25.103,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n10589,4,237,2017-03-02 16:13:54,http://schimmelhyatt.name/lauriane,0.1312543562,228.157.247.103,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n10590,4,237,2017-01-31 06:54:49,http://pfannerstill.name/giuseppe_abshire,0.7492461879,237.188.7.164,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n10591,4,237,2017-01-31 21:51:07,http://watsica.com/elisha.dach,0.0195667740,42.213.228.25,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n10592,4,237,2016-12-18 02:08:14,http://pricewaelchi.co/zola,0.1238586174,42.242.219.150,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n10593,4,237,2017-02-12 18:30:58,http://vandervortschinner.net/ray,0.3887518998,208.100.238.20,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n10594,4,237,2017-02-06 04:49:53,http://johnstonwilderman.biz/claud,0.6679085619,21.65.124.58,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n10595,4,238,2017-02-15 23:46:58,http://litteleichmann.co/garfield,,202.183.105.168,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n10596,4,238,2017-02-06 21:08:05,http://cronincain.info/adela.ankunding,,230.133.250.64,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n10597,4,238,2017-05-19 00:50:42,http://cronin.net/cristal.murray,,143.41.137.241,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n10598,4,238,2017-04-15 12:14:48,http://crist.co/leonora,,178.162.113.54,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n10599,4,238,2017-05-12 13:47:30,http://crona.co/yesenia,,158.88.103.227,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n10600,4,238,2017-04-04 10:48:23,http://waelchiprohaska.info/lafayette_greenfelder,,144.236.7.79,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n10601,4,238,2017-05-28 13:38:49,http://padbergabbott.com/fletcher.kohler,,69.153.6.118,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n10602,4,238,2017-01-30 16:21:53,http://smithamcummings.io/isaias_rodriguez,,14.202.162.135,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n10603,4,238,2017-05-14 03:05:20,http://boehm.net/ella.ferry,,189.35.4.10,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n10604,4,238,2016-12-30 11:25:07,http://runolfon.biz/ernestina,,206.161.86.238,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n10605,4,238,2017-03-09 23:07:42,http://bradtkemedhurst.info/reyna.hilll,,125.236.184.52,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n10606,4,238,2017-05-13 06:07:24,http://ko.com/olaf_batz,,131.221.63.43,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n10607,4,238,2017-01-23 07:46:39,http://jacobson.biz/gunner,,254.241.234.6,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n10608,4,238,2016-12-14 22:07:28,http://monahan.name/jaquelin_murazik,,253.249.54.130,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n10609,4,238,2017-05-05 12:35:54,http://framiswaniawski.co/leila.schaefer,,67.94.120.51,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n7595,3,168,2017-05-01 21:08:53,http://lowe.name/eduardo,,205.33.231.167,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n7596,3,168,2017-05-25 23:16:11,http://batz.org/ludwig.wyman,,7.10.241.248,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n7597,3,168,2017-03-06 22:18:29,http://boyer.org/sabina.lind,,84.171.241.138,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n7598,3,168,2017-03-29 02:56:11,http://ratkelueilwitz.info/makayla,,207.61.100.178,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n7599,3,168,2017-01-13 08:19:28,http://weber.net/colin.hansen,,15.164.162.151,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n7600,3,168,2017-06-12 04:22:15,http://kuhlmangoldner.io/wilson,,215.151.203.178,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n7601,3,168,2017-05-10 11:32:27,http://leffler.biz/dayton_crooks,,78.52.113.58,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n7602,3,168,2017-02-20 00:47:08,http://runteschmitt.com/melba,,96.57.32.124,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n7603,3,168,2017-04-08 21:53:00,http://mcclure.io/jedidiah,,148.98.111.144,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n7604,3,168,2017-03-27 19:39:10,http://hagenes.net/dedric.moen,,178.113.109.208,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n7605,3,168,2017-06-04 10:01:44,http://gleasonrutherford.io/zoie_erdman,,131.211.124.244,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n7606,3,168,2017-05-07 09:24:33,http://metz.com/nels,,82.209.180.47,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n7607,3,168,2017-04-21 21:58:43,http://sipes.io/keegan,,249.13.182.34,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n7608,3,168,2017-04-19 14:19:37,http://wintheiser.com/linda_lueilwitz,,169.135.85.242,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n7609,3,168,2017-03-11 17:36:54,http://champlin.biz/graciela.gulgowski,,29.153.168.144,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n7610,3,168,2017-01-05 13:08:23,http://kling.com/colton,,65.64.193.222,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n7611,3,168,2017-04-19 16:35:46,http://morietteromaguera.biz/kenneth,,240.244.33.88,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n7612,3,168,2017-03-12 08:39:45,http://reichelmorar.name/myrna,,53.123.124.236,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n7613,3,168,2016-12-26 16:16:33,http://mills.biz/lauriane,,103.116.139.95,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n7614,3,168,2017-02-26 02:55:14,http://dooley.name/thea.hettinger,,2.49.22.175,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n7615,3,168,2017-03-02 16:51:28,http://schaefer.biz/tracy,,52.237.89.142,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n7616,3,168,2017-05-09 11:43:45,http://bogansipes.name/lera,,135.171.3.235,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n7617,3,168,2017-05-18 22:37:58,http://abbottschinner.co/johnpaul.oconner,,17.163.115.204,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n7618,3,168,2017-02-19 11:09:30,http://littel.co/laverna_bernier,,49.99.126.109,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n7619,3,168,2017-05-30 13:31:20,http://muller.org/earlene,,177.251.216.217,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n7620,3,169,2017-05-29 13:06:25,http://mcdermottmetz.org/casimir_crist,,7.164.243.4,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n7621,3,169,2017-05-01 17:03:46,http://senger.biz/lee_goyette,,41.33.6.146,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n7622,3,169,2016-12-29 18:55:09,http://daniel.net/ruby_pouros,,135.232.193.209,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n7623,3,169,2017-03-15 14:50:14,http://feil.biz/stephany,,201.158.49.46,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n7624,3,169,2017-04-06 17:49:20,http://wilderman.name/darrel,,93.243.184.91,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n7625,3,169,2017-04-06 16:15:32,http://padberg.name/modesta,,153.59.82.121,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n7626,3,169,2017-05-04 02:35:41,http://vonruedenjohnston.biz/maida_reichel,,234.167.187.70,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n7627,3,169,2017-01-07 14:54:58,http://davis.com/duane,,218.35.153.195,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n7628,3,169,2017-04-19 13:39:48,http://carterklocko.name/rolando_crooks,,232.216.70.191,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n7629,3,169,2017-05-11 22:29:55,http://schaden.info/ben,,219.18.149.153,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n7630,3,169,2017-01-11 07:17:11,http://pollichkovacek.co/keagan,,183.108.90.45,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n7631,3,169,2017-02-01 14:46:21,http://bergnaum.com/shaina_hudson,,127.121.136.112,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n7632,3,169,2017-02-18 05:35:11,http://langworth.co/lonzo,,118.2.205.33,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n7633,3,169,2017-04-12 10:35:27,http://mannjohnson.biz/harold.heaney,,127.73.186.4,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n7634,3,169,2017-02-08 22:23:02,http://corkeryhahn.net/hollie,,150.221.157.190,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n7635,3,169,2017-06-05 21:46:22,http://fisher.name/kailyn_crooks,,41.32.97.219,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n7636,3,169,2017-02-06 15:23:55,http://robertsconn.io/dejah,,91.110.233.103,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n7637,3,169,2017-06-08 04:30:21,http://hudson.info/winifred.wilkinson,,237.19.168.232,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n7638,3,169,2017-02-24 23:15:21,http://strackehackett.name/pearlie_daugherty,,111.239.122.183,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n7639,3,169,2017-01-09 17:37:57,http://hillsbarrows.net/erik,,18.43.43.153,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n7640,3,169,2016-12-29 13:24:48,http://dickinsonrunolfon.info/luther_lynch,,49.145.250.109,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n7641,3,169,2017-01-03 17:28:01,http://luettgen.org/breana.monahan,,205.31.25.123,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n7642,3,169,2016-12-13 10:07:37,http://yundt.biz/ladarius,,149.74.23.152,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n7643,3,169,2017-06-07 16:29:59,http://price.io/granville.schimmel,,151.87.162.219,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n7644,3,169,2017-03-24 20:06:30,http://mooreshields.info/earl_ratke,,65.51.126.157,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n7645,3,169,2017-05-01 14:44:13,http://homenick.com/stephon_kuhn,,201.252.10.179,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n7646,3,169,2017-03-13 07:07:32,http://ondricka.com/deion,,73.5.114.234,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n7647,3,169,2017-03-30 12:50:06,http://schultz.io/luella,,75.137.25.166,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n7648,3,169,2017-04-20 15:52:10,http://bayerkeebler.org/tracy.vonrueden,,31.142.214.200,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n7649,3,169,2017-03-23 01:16:06,http://fritschbatz.co/branson.kreiger,,143.226.90.64,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n13505,5,303,2017-05-12 23:14:55,http://weimann.net/lemuel,,2.115.154.218,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n13506,5,303,2017-02-11 18:06:40,http://braun.name/tiana,,56.37.189.234,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n13507,5,303,2017-01-24 20:34:50,http://senger.org/araceli_osinski,,174.235.249.189,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n13508,5,303,2017-05-27 08:08:51,http://rippin.net/alisha,,6.85.63.36,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n13509,5,303,2017-04-27 21:57:31,http://lakin.io/ines,,129.14.112.98,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n13510,5,303,2017-04-26 04:45:31,http://bayerlehner.co/dedrick.legros,,2.125.52.92,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n13511,5,303,2017-04-03 14:28:10,http://padberg.com/kira,,91.120.125.88,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n13512,5,303,2017-05-24 13:26:40,http://quigley.name/scotty_ullrich,,242.247.202.131,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n13513,5,303,2017-02-27 15:44:59,http://wisozk.net/roie,,2.72.95.52,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n13514,5,303,2016-12-20 03:00:31,http://zemlakcormier.org/carlee_hyatt,,225.98.45.73,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n13515,5,303,2017-04-06 22:01:22,http://ruecker.org/nils,,17.72.132.110,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n13516,5,303,2017-05-23 10:03:40,http://dare.biz/aileen.schultz,,111.87.166.177,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n13517,5,303,2017-03-26 15:58:33,http://schusterbarton.com/brielle.bashirian,,214.34.143.230,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n13518,5,303,2017-01-18 00:33:01,http://collier.co/makenna_zulauf,,172.3.128.56,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n13519,5,303,2017-02-27 06:27:46,http://pagaclehner.biz/roxanne,,13.24.66.217,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n13520,5,303,2017-04-30 13:22:24,http://kutchbechtelar.io/calista.jakubowski,,156.39.70.5,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n13521,5,303,2017-02-03 20:42:11,http://huelscasper.name/freeda,,101.166.35.38,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n13522,5,303,2017-05-20 14:47:11,http://johnstonpowlowski.co/gail_pacocha,,253.13.132.11,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n13523,5,303,2017-04-08 17:19:40,http://hermiston.biz/camden,,168.35.137.233,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n13524,5,303,2017-01-09 10:04:50,http://hoppe.name/beulah_price,,75.147.233.125,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n13525,5,303,2017-05-07 07:30:11,http://nader.com/rachael_heaney,,114.192.21.175,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n13526,5,303,2017-05-23 13:37:57,http://casperjerde.name/johnny_jacobs,,150.219.5.105,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n13527,5,303,2017-01-07 01:09:18,http://auer.io/golda,,229.168.64.36,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n13528,5,303,2017-04-16 16:47:11,http://johns.net/curt,,26.147.45.196,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n13529,5,304,2017-05-31 09:35:52,http://gaylord.com/titus,,64.61.241.81,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n13530,5,304,2017-03-21 20:22:15,http://turnergleason.org/edmond,,161.66.146.85,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n13531,5,304,2017-02-18 18:36:02,http://whitemoen.com/anne_aufderhar,,130.144.160.58,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n13532,5,304,2017-04-17 19:03:12,http://vandervort.org/jordy.thiel,,237.137.80.136,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n13533,5,304,2017-03-30 11:21:02,http://heidenreichrempel.info/ian,,32.62.3.126,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n13534,5,304,2017-03-13 19:57:13,http://bergstrom.io/zane,,252.161.25.204,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n13535,5,304,2017-03-17 14:26:09,http://howegoldner.io/serenity_jacobson,,184.202.245.135,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n13536,5,304,2017-05-04 06:30:52,http://orn.net/celia.rosenbaum,,213.80.60.137,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n13537,5,304,2017-01-26 06:43:55,http://gutmanntremblay.co/jeromy,,132.129.247.61,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n13538,5,304,2016-12-29 10:44:43,http://fisher.biz/beulah,,101.162.148.197,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n13539,5,304,2017-04-04 22:56:06,http://mertzconn.net/emilia,,242.213.13.113,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n13540,5,304,2017-01-26 13:23:39,http://schmeler.net/taryn.stokes,,101.153.214.157,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n13541,5,304,2017-03-21 17:22:35,http://heaney.io/henderson.pagac,,11.9.116.213,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n13542,5,304,2017-04-17 14:03:34,http://watsicafay.net/troy,,3.22.202.114,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n13543,5,304,2017-05-09 13:34:50,http://swaniawski.com/ervin,,161.2.91.161,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n13544,5,304,2017-05-15 19:17:57,http://okon.net/dorothy_mosciski,,142.156.185.218,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n13545,5,304,2017-01-02 00:03:00,http://haag.biz/quinn.bergnaum,,218.32.46.141,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n13546,5,304,2017-01-24 04:50:57,http://oconner.io/brock_nolan,,155.67.248.87,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n13547,5,304,2017-01-19 16:59:18,http://hermanrodriguez.name/camilla,,49.10.85.132,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n13548,5,304,2016-12-18 02:05:56,http://nicolas.info/velda,,29.91.143.56,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n13549,5,304,2017-02-10 14:12:44,http://schiller.co/astrid,,34.230.78.35,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n13550,5,304,2017-03-03 21:53:13,http://oreilly.com/emmanuelle,,46.151.157.136,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n13551,5,304,2017-05-31 05:21:55,http://botsford.com/lisandro.ward,,9.3.55.10,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n13552,5,304,2017-01-20 15:58:02,http://goyette.biz/justina,,188.65.162.58,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n13553,5,304,2017-01-04 21:07:36,http://ziemannjohns.info/golden.nikolaus,,246.191.60.228,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n13554,5,304,2017-04-03 12:40:09,http://heel.biz/malvina_lynch,,84.74.65.128,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n13555,5,304,2017-01-04 04:33:36,http://hamill.info/kasey.lueilwitz,,194.223.180.4,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n13556,5,304,2017-05-12 04:06:22,http://collierruecker.org/caidy,,48.190.33.162,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n13557,5,304,2017-05-14 16:46:10,http://littlehowe.co/reva,,243.189.172.101,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n13558,5,305,2017-01-05 09:49:20,http://yundt.info/johathan.vandervort,,73.35.225.122,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n13559,5,305,2017-02-13 07:31:42,http://cronindickinson.com/alicia,,47.233.43.59,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n13560,5,305,2017-05-22 16:59:53,http://lemke.co/fernando,,174.198.36.134,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n16499,6,374,2017-04-27 23:55:30,http://hermiston.io/jaron,0.0876729279,213.101.222.174,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n16500,6,374,2017-02-09 23:54:15,http://ullrichklocko.io/dovie_toy,0.2344138990,125.213.150.214,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n16501,6,374,2017-02-27 08:24:29,http://mccullough.net/elias,0.3315874214,247.155.60.113,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n16502,6,374,2017-03-14 00:17:26,http://tromp.co/pearlie,0.2625691552,189.181.231.111,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n16503,6,374,2017-05-16 14:02:28,http://schultzbednar.co/alphonso.feil,0.7289912067,195.94.192.168,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n16504,6,374,2016-12-21 23:47:15,http://bosco.com/ida.considine,0.9071153985,99.126.178.101,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n16505,6,374,2017-01-29 18:19:03,http://mantelueilwitz.info/emmanuel.lang,0.7134897352,231.30.155.178,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n16506,6,374,2017-02-05 22:53:35,http://reichertmiller.net/max,0.7043530245,147.89.104.121,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n16507,6,374,2017-05-13 20:26:39,http://schiller.org/vincent_stehr,0.9298747852,121.89.65.160,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n16508,6,374,2017-03-19 03:20:17,http://zboncak.info/roosevelt,0.9145210902,6.38.134.150,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n16509,6,374,2016-12-18 11:00:14,http://rauswift.io/lesley_rohan,0.7559308640,76.88.233.211,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n16510,6,374,2016-12-23 16:24:10,http://marvinmedhurst.co/tina,0.9541546718,139.9.18.125,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n16511,6,374,2017-02-17 01:41:13,http://hills.org/ephraim,0.3125044949,178.104.32.117,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n16512,6,374,2017-04-12 12:52:13,http://okuneva.com/cristal,0.0660640862,9.51.128.156,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n16513,6,374,2017-02-28 10:53:04,http://mckenziemoriette.io/juliet,0.7980906704,187.253.193.190,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n16514,6,374,2017-05-20 10:03:58,http://bartoletti.com/maiya.lebsack,0.2118090336,242.58.193.148,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n16515,6,374,2017-06-04 23:43:50,http://davis.biz/monty,0.8455773382,91.71.243.168,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n16516,6,374,2017-01-05 04:50:51,http://sawaynmcglynn.name/jefferey.rosenbaum,0.1260010574,176.228.97.200,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n16517,6,374,2017-03-09 04:55:31,http://boyle.biz/elizabeth,0.1496360318,17.69.47.18,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n16518,6,374,2017-01-20 13:14:47,http://fay.biz/jacques.rice,0.4549677955,124.11.164.193,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n16519,6,374,2016-12-31 17:07:32,http://murazik.net/wilmer_dooley,0.2604744036,134.82.214.48,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n16520,6,374,2016-12-18 18:39:50,http://reichel.biz/athena_johnson,0.0497031707,29.199.203.155,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n16521,6,374,2017-03-08 03:17:15,http://rennerdonnelly.name/lulu.mertz,0.0678041039,84.69.141.161,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n16522,6,374,2017-02-24 12:45:19,http://torpfahey.co/evelyn,0.7294135695,235.148.239.162,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n16523,6,374,2017-04-06 03:05:58,http://tillman.name/dixie_mueller,0.0860107908,251.144.75.232,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n16524,6,374,2017-03-05 07:07:49,http://hoegerhegmann.name/ettie_sporer,0.1880112615,119.107.173.207,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n16525,6,374,2017-05-22 04:49:53,http://mullergreenholt.co/lottie_johnston,0.6013065789,112.122.48.61,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n16526,6,374,2017-01-14 11:27:09,http://tremblaynienow.net/catalina,0.6638085820,41.51.177.173,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n16527,6,374,2017-05-15 04:45:24,http://feeneycarroll.org/lupe,0.4426522710,162.151.62.126,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n16528,6,374,2016-12-22 13:44:49,http://funk.name/sarai,0.2758144877,211.108.212.185,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n16529,6,374,2017-01-18 05:35:22,http://farrellyost.info/eloise.schulist,0.0030401876,90.117.246.214,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n16530,6,374,2017-01-30 05:38:42,http://denesik.net/winifred,0.7855551479,190.249.246.60,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n16531,6,374,2017-01-29 06:19:59,http://jacobson.biz/lillie,0.7882509878,161.215.15.145,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n16532,6,374,2017-01-01 16:53:53,http://creminlehner.info/piper,0.2485135678,13.143.254.38,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n16533,6,374,2017-04-06 13:31:23,http://hills.com/archibald.moore,0.8336688566,177.62.14.78,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n16534,6,374,2017-05-02 16:32:08,http://wuckert.net/marie_olson,0.1472104422,50.146.250.117,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n16535,6,374,2017-04-22 22:09:19,http://wisozk.co/clementina,0.3517094800,72.150.232.182,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n16536,6,374,2017-03-24 10:29:22,http://grimes.co/gaylord.jones,0.6857956060,74.75.235.156,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n16537,6,374,2016-12-26 10:13:55,http://prohaska.name/matteo_sporer,0.1080580447,122.170.158.72,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n16538,6,374,2017-04-05 05:19:53,http://altenwerthroberts.co/haley,0.5313464386,9.188.67.139,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n16539,6,374,2017-01-31 02:01:14,http://beatty.org/solon.lowe,0.0554982072,167.45.25.137,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n16540,6,374,2017-01-06 14:13:17,http://white.co/mazie_turcotte,0.0009740604,240.65.49.44,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n16541,6,374,2017-01-30 00:39:29,http://kiehnharber.biz/rene_rowe,0.5933451805,182.243.39.207,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n16542,6,374,2017-01-10 15:30:55,http://ryan.name/kenyatta,0.2548738447,21.128.34.229,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n16543,6,375,2017-01-09 21:10:14,http://will.org/jane.kutch,0.6700710379,208.175.62.97,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n16544,6,375,2017-05-27 20:45:00,http://crist.io/jaylen_padberg,0.2601721021,102.124.109.56,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n16545,6,375,2017-06-01 04:17:54,http://hegmann.info/cortez_kuhlman,0.1056598516,67.92.181.179,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n16546,6,375,2017-04-02 02:23:36,http://kuhic.io/graham_jerde,0.3594908148,101.125.27.56,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n16547,6,375,2017-04-11 14:43:11,http://trompdavis.org/maximilian,0.7078409207,159.215.200.168,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n16548,6,375,2016-12-15 17:04:56,http://nitzsche.com/krystal,0.4217207367,107.235.56.88,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n16549,6,375,2017-03-14 03:28:34,http://von.net/leann,0.4828579090,22.230.157.37,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n3679,2,81,2017-06-08 12:10:40,http://jaskolski.com/adrian,0.6636959060,253.119.68.171,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n3680,2,81,2017-04-23 22:01:33,http://gerhold.biz/christina,0.8248568875,47.195.125.176,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n3681,2,81,2017-03-27 10:46:43,http://aufderhar.co/willy.langosh,0.9806572447,40.147.133.86,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n3682,2,81,2017-04-01 07:29:18,http://bergstrom.info/brielle,0.7166249107,52.109.23.118,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n3683,2,81,2016-12-26 00:49:13,http://stanton.net/christiana.kautzer,0.2887667627,169.156.231.169,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n3684,2,81,2017-04-18 18:42:25,http://crist.name/hubert.dibbert,0.6098747128,6.14.46.107,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n3685,2,81,2017-06-09 02:17:53,http://larsonkeeling.net/alanna,0.3617874399,94.204.114.194,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n3686,2,81,2017-02-22 08:18:34,http://keebler.org/natalia,0.4698671039,62.202.203.135,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n3687,2,81,2017-05-30 01:57:46,http://lindgrenrau.name/cleveland,0.0099738994,249.46.48.41,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n3688,2,81,2017-03-03 10:58:50,http://pfeffer.co/jayde_stracke,0.6456791993,185.233.7.47,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n3689,2,81,2017-04-12 10:51:35,http://parisianko.com/patience,0.9995092826,81.147.112.87,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n3690,2,81,2017-04-30 09:14:30,http://heidenreich.io/jerald.hudson,0.5765593944,87.25.220.177,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n3691,2,81,2017-03-24 03:28:09,http://boscopfannerstill.name/larue,0.5158344647,193.90.163.180,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n3692,2,81,2017-05-25 06:08:23,http://stehr.biz/abel,0.8240833087,165.24.241.170,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n3693,2,81,2017-04-27 07:57:28,http://jaskolski.com/lonie,0.0411070100,92.214.73.156,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n3694,2,81,2017-04-28 02:06:48,http://moen.io/jerad.bartell,0.2430045730,108.72.240.11,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n3695,2,81,2017-04-03 03:21:47,http://carter.info/claire_botsford,0.1110989982,199.96.136.230,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n3696,2,81,2016-12-23 00:15:14,http://morar.name/reinhold.reynolds,0.3323657707,83.106.236.129,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n3697,2,81,2016-12-23 01:01:26,http://reingerpfeffer.info/ibrahim_graham,0.0348730015,99.100.90.179,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n3698,2,81,2017-02-09 12:48:04,http://sanfordbahringer.net/mitchel,0.3860624331,244.13.11.157,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n3699,2,82,2016-12-16 13:45:03,http://beattylynch.name/ruthe.tremblay,0.1719507383,81.137.65.14,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n3700,2,82,2017-01-03 19:56:06,http://schulist.io/kari,0.4425700728,220.109.167.26,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n3701,2,82,2017-02-04 02:53:08,http://powlowskilynch.io/georgette.windler,0.8548436078,203.17.100.84,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n3702,2,82,2017-04-19 04:31:39,http://ratkeconn.name/rey,0.0940351632,19.150.14.235,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n3703,2,82,2017-01-07 13:02:59,http://greenfelder.net/krista.osinski,0.1918241783,83.133.110.13,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n3704,2,82,2017-05-21 18:52:51,http://reingernolan.biz/santiago_blick,0.2396111637,55.38.230.171,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n3705,2,82,2017-05-21 05:11:35,http://bernier.biz/cheyanne,0.9317791857,211.230.123.239,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n3706,2,82,2016-12-15 13:07:57,http://waters.info/patience_crooks,0.5890127490,120.183.84.176,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n3707,2,82,2017-05-30 15:18:27,http://aufderharrenner.biz/isabel_gutmann,0.7017492607,81.46.107.133,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n3708,2,82,2017-05-11 17:48:19,http://lemke.net/leanna,0.3671928975,57.13.107.77,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n3709,2,82,2017-06-10 07:36:14,http://spinkalesch.com/jacynthe_medhurst,0.1079739084,122.181.65.241,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n3710,2,82,2017-04-14 00:29:55,http://kuphaltorphy.biz/dorian.connelly,0.0918454085,220.253.201.233,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n3711,2,82,2017-06-03 12:10:41,http://abbott.io/holden_mertz,0.1237994727,159.177.245.70,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n3712,2,82,2017-02-07 10:10:34,http://medhurst.io/fabian,0.4427489996,174.120.229.157,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n3713,2,82,2017-01-24 17:03:02,http://steuber.net/madeline,0.0094943570,241.234.221.128,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n3714,2,82,2017-05-01 11:49:47,http://bernierbeer.org/dolores_becker,0.7420093295,126.174.135.119,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n3715,2,82,2017-01-03 01:44:10,http://feeney.org/sierra_murazik,0.1390614430,252.181.89.24,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n3716,2,82,2017-05-07 00:06:24,http://zboncak.net/julie_rolfson,0.8305547022,254.131.6.150,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n3717,2,82,2017-04-29 13:18:02,http://glover.biz/izaiah.dooley,0.8526831156,67.59.82.212,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n3718,2,82,2017-05-23 14:17:44,http://kuhlman.name/mohammed,0.9961869913,219.232.200.106,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n3719,2,82,2017-04-22 19:48:11,http://wisoky.name/roselyn,0.7049974084,96.231.141.80,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n3720,2,82,2017-04-28 12:27:47,http://boyer.net/amira,0.8448352625,129.226.19.84,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n3721,2,82,2016-12-28 17:03:48,http://will.co/saige.wilkinson,0.5892998827,245.164.150.76,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n3722,2,82,2017-01-06 10:59:24,http://ohara.co/regan.langworth,0.9677411832,245.168.149.68,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n3723,2,82,2017-02-08 13:42:44,http://ankunding.com/ila,0.2499449915,131.66.14.157,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n3724,2,82,2017-06-12 11:04:14,http://koelpin.biz/freda_waelchi,0.4451940815,176.156.215.200,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n3725,2,82,2017-01-04 12:35:08,http://fadel.name/nina,0.4290173728,150.254.39.12,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n3726,2,82,2017-04-16 01:58:54,http://reichert.org/jana.murphy,0.5330636222,83.113.167.38,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n3727,2,82,2017-04-24 21:10:15,http://ondrickadamore.com/noemi,0.0051330045,201.228.157.173,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n5265,2,116,2017-03-03 09:06:03,http://blanda.com/amaya,,223.141.162.48,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n3728,2,82,2017-02-20 19:39:37,http://howellbruen.biz/verla,0.4051799151,158.34.47.131,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n3729,2,82,2017-04-24 02:09:05,http://streich.io/jules,0.7551628554,231.38.118.251,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n10610,4,238,2017-04-14 22:29:21,http://kuhlmanweinat.biz/alberto,,100.58.122.219,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n10611,4,238,2017-06-03 00:34:24,http://ruel.io/laury_crooks,,35.117.72.56,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n10612,4,238,2017-06-03 17:03:35,http://gleasonemard.co/dion,,53.85.52.117,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n10613,4,238,2016-12-14 23:47:05,http://jacobson.biz/juana.reynolds,,78.70.63.178,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n10614,4,238,2017-01-02 20:11:50,http://mills.name/margarita,,5.131.150.220,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n10615,4,238,2017-03-26 03:05:52,http://krajcikboehm.co/eldred_pagac,,134.205.139.54,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n10616,4,238,2017-01-19 18:20:05,http://powlowski.io/travon,,234.139.222.204,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n10617,4,238,2017-04-27 14:21:40,http://feeney.biz/lia_price,,228.254.49.98,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n10618,4,238,2017-03-03 12:54:43,http://runolfsdottir.biz/diana.wisozk,,195.189.92.24,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n10619,4,238,2017-01-11 03:35:02,http://kuvalis.io/marcellus,,147.217.116.92,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n10620,4,238,2017-05-21 22:21:28,http://gaylord.net/katheryn,,140.184.39.28,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n10621,4,238,2017-02-04 07:15:12,http://ernserlebsack.io/ward_hilpert,,116.126.152.61,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n10622,4,238,2017-05-07 19:02:43,http://bartolettimraz.info/sidney,,16.153.101.76,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n10623,4,238,2017-03-27 14:45:00,http://olson.io/adeline_blick,,163.62.134.65,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n10624,4,238,2017-04-02 04:39:14,http://howeluettgen.biz/theresia,,151.163.108.84,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n10625,4,238,2017-02-03 22:02:38,http://spinka.info/stephania.fahey,,117.92.194.14,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n10626,4,238,2017-06-08 13:37:19,http://herzoggraham.org/alexandria.heaney,,239.39.112.60,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n10627,4,238,2017-04-09 03:33:34,http://brekke.co/piper_davis,,50.79.101.237,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n10628,4,238,2017-03-24 22:13:15,http://pfannerstill.biz/annetta,,253.156.6.145,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n10629,4,238,2017-04-04 16:37:31,http://leannon.com/abbie,,168.99.230.237,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n10630,4,238,2017-02-18 04:25:20,http://langosh.info/aisha.brekke,,228.62.99.73,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n10631,4,238,2017-01-23 13:41:40,http://hodkiewiczschulist.io/cade_welch,,66.123.152.8,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n10632,4,238,2017-04-23 13:17:33,http://dibbert.io/armando,,148.157.173.24,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n10633,4,238,2017-03-05 03:48:28,http://upton.biz/angie.dietrich,,96.178.48.191,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n10634,4,238,2017-03-05 11:28:17,http://cruickshank.com/kelly,,144.178.240.83,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n10635,4,238,2017-02-13 21:15:07,http://mante.net/dan,,180.241.27.97,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n10636,4,238,2017-06-06 00:05:55,http://padberg.biz/ryder,,73.155.220.147,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n10637,4,238,2017-02-11 12:04:03,http://lynch.net/bernie,,188.45.194.140,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n10638,4,238,2017-04-01 06:21:33,http://lemke.com/hudson,,8.246.106.76,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n10639,4,238,2016-12-30 18:19:08,http://becker.com/crystel.herman,,41.102.81.148,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n10640,4,238,2017-01-02 22:35:28,http://zulauf.co/georgette,,171.70.63.217,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n10641,4,238,2017-02-11 02:12:00,http://howecorkery.com/lurline.jacobs,,213.173.204.154,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n10642,4,238,2017-02-15 22:06:24,http://goldnerkulas.info/abby,,16.111.64.63,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n10643,4,238,2017-04-16 07:01:01,http://block.name/kevon.rutherford,,93.27.224.254,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n10644,4,238,2017-03-03 20:51:47,http://doyle.com/hermann.block,,151.27.140.202,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n10645,4,238,2017-05-19 05:11:07,http://kerluke.io/juanita_orn,,121.56.112.245,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n10646,4,238,2017-05-14 09:32:05,http://howe.co/felix_sauer,,85.225.157.142,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n10647,4,238,2017-03-25 03:50:38,http://romaguera.net/solon,,135.175.106.238,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n10648,4,238,2017-05-04 21:22:33,http://strosin.biz/ruben.gutkowski,,59.89.103.219,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n10649,4,238,2016-12-31 21:33:05,http://ferry.net/beth_prohaska,,164.237.65.65,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n10650,4,238,2017-03-14 22:17:40,http://heel.net/celine_gaylord,,238.115.74.184,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n10651,4,238,2017-04-12 06:37:01,http://lowelockman.name/dahlia.emard,,139.27.224.68,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n10652,4,238,2017-02-01 04:51:24,http://larsonshanahan.io/americo.osinski,,118.12.14.55,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n10653,4,238,2016-12-19 21:45:53,http://rodriguez.com/ignatius,,153.2.223.79,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n10654,4,238,2017-01-27 07:06:38,http://senger.biz/sibyl.hoppe,,192.210.208.147,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n10655,4,238,2017-01-26 20:05:49,http://weberrogahn.name/gus_lebsack,,183.128.67.113,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n10656,4,238,2017-04-26 23:15:34,http://ratke.org/eli_fay,,9.240.131.8,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n10657,4,238,2017-01-31 20:35:57,http://padberg.com/marjorie_kemmer,,161.153.81.151,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n10658,4,239,2017-01-13 19:57:50,http://daviwaniawski.net/samara,,160.151.59.129,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n10659,4,239,2017-06-01 10:44:45,http://bergejones.com/rashad.heller,,128.242.48.89,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n10660,4,239,2017-04-03 15:55:58,http://osinski.org/carroll,,22.125.124.247,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n10661,4,239,2017-03-06 20:26:05,http://goldnerkuhic.org/saul_wisozk,,214.103.183.130,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n10662,4,239,2017-05-20 11:24:18,http://ruel.org/merritt.oreilly,,225.218.9.173,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n10663,4,239,2017-01-17 15:47:00,http://cole.biz/aubrey,,203.243.114.60,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n10664,4,239,2016-12-25 22:49:52,http://rosenbaum.name/jason.kohler,,83.190.3.127,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n7650,3,169,2017-04-27 01:00:45,http://maggio.io/astrid_schimmel,,211.160.242.182,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n7651,3,169,2017-01-14 07:38:56,http://spinka.net/neha_bailey,,187.73.129.211,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n7652,3,169,2017-01-28 03:55:36,http://berniertreutel.info/randi_ondricka,,141.248.114.82,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n7653,3,169,2017-04-21 11:20:20,http://kuhlman.co/may.ratke,,141.152.184.15,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n7654,3,170,2017-02-28 11:03:44,http://kihn.org/madeline.breitenberg,,115.154.5.123,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n7655,3,170,2017-01-12 05:54:14,http://emardstehr.biz/daisy,,161.117.232.18,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n7656,3,170,2017-03-31 07:54:01,http://kozey.name/anya,,101.189.148.63,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n7657,3,170,2017-01-21 19:38:00,http://schuppewatsica.info/kyla.donnelly,,189.217.81.24,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n7658,3,170,2017-02-10 02:16:39,http://nienow.net/grover,,214.140.210.139,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n7659,3,170,2017-06-06 12:31:14,http://flatley.com/dana.denesik,,15.60.113.156,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n7660,3,170,2017-01-29 21:03:08,http://connhermann.net/dorothy_ankunding,,125.33.7.93,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n7661,3,170,2017-04-05 10:05:08,http://simonisharvey.com/liliana.miller,,36.122.85.128,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n7662,3,170,2017-03-15 17:28:34,http://wuckertcasper.co/grant_turcotte,,84.9.231.6,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n7663,3,170,2017-04-07 03:41:25,http://mante.net/haley,,200.125.142.17,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n7664,3,170,2017-05-16 17:38:37,http://hodkiewicz.com/lamont,,27.211.206.153,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n7665,3,170,2017-02-07 03:35:18,http://volkman.com/albin,,161.14.206.54,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n7666,3,170,2017-02-26 11:40:35,http://waters.info/walton,,17.117.107.78,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n7667,3,170,2017-05-14 11:23:28,http://nitzsche.biz/laron,,249.15.212.149,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n7668,3,170,2017-01-01 23:06:35,http://grahamrobel.biz/chris_willms,,35.3.218.86,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n7669,3,170,2017-06-06 00:46:54,http://shields.io/yesenia,,115.247.96.101,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n7670,3,170,2017-05-18 06:36:29,http://marvin.io/elton.willms,,21.12.45.68,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n7671,3,170,2017-04-05 07:38:21,http://bailey.biz/anjali,,58.175.252.175,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n7672,3,170,2017-02-28 04:06:31,http://farrell.name/tamia,,126.183.2.180,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n7673,3,170,2017-05-24 05:12:09,http://littel.org/lenna,,8.106.214.184,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n7674,3,170,2017-01-17 02:54:47,http://rueckersmitham.co/thea_cole,,237.232.68.126,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n7675,3,170,2017-02-17 13:29:19,http://kleinwehner.name/johanna.mayert,,203.21.123.70,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n7676,3,170,2017-04-17 11:12:26,http://gloverhaag.net/amir.corkery,,90.52.44.135,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n7677,3,170,2017-05-09 17:52:08,http://rice.biz/eula,,32.191.224.142,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n7678,3,170,2017-05-10 10:56:26,http://schultz.biz/ania,,133.166.29.226,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n7679,3,170,2017-03-30 14:09:16,http://mohrdoyle.com/rhoda,,189.143.166.202,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n7680,3,170,2017-05-25 22:44:01,http://oberbrunnerwilkinson.biz/keira,,81.58.57.4,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n7681,3,170,2017-04-01 20:11:21,http://metz.info/virginie,,11.214.149.8,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n7682,3,170,2017-01-16 11:03:53,http://aufderhar.name/aaron_terry,,213.242.172.31,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n7683,3,170,2017-06-06 18:28:08,http://vonrueden.io/deion,,146.54.49.3,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n7684,3,170,2017-05-27 23:44:36,http://nienow.co/camryn,,193.179.94.227,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n7685,3,170,2017-05-04 05:27:12,http://wisozk.org/ima,,42.77.122.204,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n7686,3,170,2017-04-11 14:25:48,http://price.co/glennie,,94.119.46.5,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n7687,3,170,2017-05-03 00:07:34,http://metzbatz.biz/frieda,,75.98.47.196,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n7688,3,170,2016-12-31 18:55:58,http://reynolds.biz/mack,,75.78.239.129,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n7689,3,170,2017-05-30 22:49:09,http://langworth.org/jermain.stamm,,135.114.138.137,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n7690,3,170,2017-03-05 07:39:27,http://bartell.biz/kailee,,154.79.208.136,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n7691,3,170,2016-12-19 22:42:26,http://kuhn.com/carter.cummings,,50.118.53.244,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n7692,3,170,2017-03-18 09:38:06,http://cole.co/cleo.towne,,80.67.156.124,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n7693,3,171,2017-03-25 03:50:30,http://olson.biz/yadira.vonrueden,,235.81.97.112,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n7694,3,171,2017-02-04 12:37:57,http://friesenjones.io/alexandrea_koepp,,120.86.147.38,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n7695,3,171,2017-06-05 23:33:56,http://senger.org/violette_trantow,,128.81.248.7,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n7696,3,171,2017-03-26 06:15:32,http://trantowstrosin.name/ashleigh,,175.101.249.28,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n7697,3,171,2017-05-06 22:24:32,http://rempel.com/clara,,181.235.41.245,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n7698,3,171,2017-03-27 03:51:01,http://mantebins.net/richard_lakin,,40.110.159.35,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n7699,3,171,2016-12-26 02:10:56,http://reinger.com/verla.schuster,,60.177.67.67,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n7700,3,171,2017-05-19 20:51:59,http://skiles.io/travon,,155.72.125.67,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n7701,3,171,2017-05-20 04:51:09,http://williamson.io/margot.collins,,181.97.49.244,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n7702,3,171,2016-12-24 23:38:53,http://blockbreitenberg.org/geovanni_corkery,,51.202.203.202,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n7703,3,171,2017-01-10 08:51:31,http://balistreri.info/bertha_kilback,,95.49.216.169,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n7704,3,171,2017-04-26 14:38:49,http://brekke.net/favian_mayer,,244.136.227.99,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n7705,3,171,2017-01-09 17:29:27,http://murphy.name/kelsie.king,,241.64.73.8,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n13561,5,305,2017-03-01 12:34:56,http://bruen.biz/ashlynn,,168.162.91.28,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n13562,5,305,2017-06-02 10:49:37,http://wardwhite.io/kadin.volkman,,233.122.97.16,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n13563,5,305,2017-04-04 20:31:53,http://swaniawskivandervort.co/lilly,,145.242.113.133,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n13564,5,305,2017-02-28 10:43:23,http://lueilwitzcorkery.io/einar,,49.67.235.50,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n13565,5,305,2017-05-24 08:07:34,http://moen.biz/lea.schneider,,5.201.84.92,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n13566,5,305,2017-03-11 21:43:52,http://wintheiser.biz/art.casper,,225.249.21.41,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n13567,5,305,2017-01-10 04:29:21,http://murray.net/dagmar,,93.204.193.142,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n13568,5,305,2017-03-23 23:09:19,http://upton.net/myrtis.romaguera,,253.136.40.228,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n13569,5,305,2017-05-28 09:34:32,http://gislason.biz/nella_strosin,,160.136.222.206,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n13570,5,305,2017-02-18 14:02:10,http://hamillgislason.biz/sarah.leannon,,155.213.205.74,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n13571,5,305,2017-04-22 04:25:26,http://darebechtelar.name/vada.hoppe,,247.215.201.114,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n13572,5,305,2017-01-05 13:44:58,http://shanahan.info/ewald_hagenes,,158.15.137.153,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n13573,5,305,2017-01-24 10:41:52,http://mosciski.info/lilliana.mayert,,149.62.50.236,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n13574,5,305,2017-05-10 20:34:52,http://jenkinchuster.name/giles_larson,,30.155.10.163,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n13575,5,305,2017-02-21 15:22:49,http://kub.net/genesis_armstrong,,19.34.137.223,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n13576,5,305,2017-05-20 18:31:21,http://cain.com/izaiah,,43.97.73.171,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n13577,5,305,2017-01-05 04:34:47,http://zieme.biz/enid,,106.250.195.79,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n13578,5,305,2017-05-03 05:09:26,http://goldner.net/timothy.heaney,,219.12.25.143,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n13579,5,305,2017-05-09 03:14:29,http://lebsackwhite.io/amaya.eichmann,,229.152.180.220,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n13580,5,305,2017-03-05 08:30:39,http://mosciski.com/kennith_borer,,226.148.206.200,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n13581,5,305,2017-03-03 14:08:05,http://marquardt.net/peggie,,208.40.239.144,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n13582,5,305,2017-01-15 10:12:43,http://west.co/tyrel.howell,,211.220.134.254,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n13583,5,305,2017-01-30 04:45:15,http://murphy.net/gertrude,,104.191.162.248,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n13584,5,306,2017-04-16 16:42:33,http://gerhold.com/vesta,,104.242.137.169,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n13585,5,306,2017-03-25 07:49:31,http://pfeffer.io/verda,,174.164.91.183,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n13586,5,306,2017-03-08 21:01:29,http://champlincronin.io/margarete_sporer,,252.51.52.242,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n13587,5,306,2017-04-30 02:47:38,http://shields.net/lazaro_green,,130.213.115.198,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n13588,5,306,2017-04-16 14:40:13,http://johns.net/delilah_kris,,186.173.128.95,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n13589,5,306,2017-05-14 21:31:56,http://considine.net/stanford,,205.114.155.193,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n13590,5,306,2017-02-17 13:43:59,http://thompson.biz/tommie,,228.240.120.86,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n13591,5,306,2017-04-20 08:08:15,http://bogan.com/ryley_kilback,,40.24.36.133,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n13592,5,306,2017-05-13 15:34:22,http://bins.co/adam,,169.11.76.113,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n13593,5,306,2017-01-08 18:55:44,http://bahringerwintheiser.name/reta_spinka,,109.187.30.240,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n13594,5,306,2016-12-16 22:03:33,http://bartoletti.com/parker,,2.211.150.59,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n13595,5,306,2017-03-28 23:08:18,http://collier.info/imelda,,118.161.24.254,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n13596,5,306,2017-01-03 18:55:32,http://lesch.biz/moses,,25.146.250.101,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n13597,5,306,2017-04-02 22:52:20,http://smitham.net/parker.damore,,139.34.26.128,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n13598,5,306,2017-01-13 12:57:23,http://little.name/heidi,,181.46.209.17,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n13599,5,306,2017-05-04 22:28:34,http://armstrong.biz/serenity.nader,,147.3.254.156,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n13600,5,306,2017-01-03 06:16:09,http://kubdickinson.net/carlo_skiles,,2.97.8.185,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n13601,5,306,2016-12-17 07:36:27,http://kreiger.org/jeanne_gerlach,,77.42.79.33,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n13602,5,306,2017-01-31 02:25:37,http://hackettbechtelar.info/melody_wintheiser,,164.139.252.151,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n13603,5,306,2017-04-19 07:38:29,http://kihn.net/ernie,,155.217.149.42,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n13604,5,306,2016-12-31 22:35:54,http://leuschkefranecki.biz/elinor,,127.95.203.24,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n13605,5,307,2017-04-04 06:13:04,http://schmidtkihn.name/zetta_schoen,,181.189.167.23,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n13606,5,307,2016-12-29 22:46:51,http://little.net/patsy,,76.240.150.115,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n13607,5,307,2017-05-20 12:21:18,http://champlin.biz/roosevelt.heaney,,24.134.120.18,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n13608,5,307,2017-01-15 09:16:29,http://larson.name/jacinto.oreilly,,245.200.201.38,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n13609,5,307,2017-05-06 05:32:47,http://bernhardtrantow.info/edgar.luettgen,,59.107.32.107,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n13610,5,307,2017-05-04 18:33:40,http://huel.org/wilmer,,101.113.33.145,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n13611,5,307,2017-03-28 22:05:07,http://baileyupton.name/duncan_kuphal,,99.96.185.159,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n13612,5,307,2017-01-11 09:47:32,http://streich.name/warren_schultz,,109.95.113.89,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n13613,5,307,2017-01-23 13:06:46,http://williamsonkoelpin.org/joe,,24.199.124.147,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n13614,5,307,2017-01-30 15:46:34,http://conroygorczany.com/maggie,,247.19.75.205,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n13615,5,307,2017-01-26 00:40:52,http://gislason.io/daron,,23.30.205.48,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n16550,6,375,2017-02-05 09:21:46,http://robel.com/eloisa.volkman,0.6949929000,193.85.197.233,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n16551,6,375,2017-05-21 19:40:07,http://boehmking.co/emmie,0.2638383181,116.22.112.60,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n16552,6,375,2017-01-05 01:41:10,http://renner.co/arno,0.5445909148,234.117.122.4,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n16553,6,375,2017-03-29 12:48:56,http://larsonlueilwitz.co/dana,0.0607361288,8.203.136.101,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n16554,6,375,2017-04-24 01:44:43,http://oreilly.co/lennie_tremblay,0.4115837413,76.126.140.208,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n16555,6,375,2017-01-07 20:04:25,http://champlinrath.org/noemie_morar,0.7261834535,47.11.11.244,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n16556,6,375,2017-03-20 19:15:18,http://blockemmerich.io/janice,0.7297880856,244.188.36.115,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n16557,6,375,2017-04-12 01:03:59,http://mayert.co/claire,0.5893664110,252.54.210.190,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n16558,6,375,2017-03-23 20:10:15,http://ward.io/desiree,0.1995249701,121.48.239.26,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n16559,6,375,2017-02-24 14:57:16,http://robel.org/eleonore,0.0981679080,121.226.95.251,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n16560,6,375,2017-01-22 05:30:17,http://zemlakmohr.info/jeyca.weinat,0.6726298085,9.162.233.210,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n16561,6,375,2017-02-26 10:48:11,http://hudson.net/jovan,0.9269268113,93.66.169.211,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n16562,6,375,2016-12-25 04:59:12,http://gerlach.info/dudley,0.5294814726,206.164.246.143,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n16563,6,375,2017-02-13 17:52:48,http://jacobsvandervort.name/tyrese.leuschke,0.5935772800,226.127.5.143,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n16564,6,375,2017-02-03 05:50:28,http://tromphermiston.biz/linda,0.4299662640,153.151.84.207,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n16565,6,375,2017-04-16 23:35:55,http://ohara.name/sydney,0.1784514638,45.195.177.173,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n16566,6,375,2017-03-11 15:31:38,http://rosenbaum.com/shanie,0.5172540134,78.48.161.217,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n16567,6,375,2017-02-24 07:25:37,http://dibbertpollich.net/marietta,0.6741563282,246.42.69.240,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n16568,6,375,2017-05-13 18:46:20,http://legros.org/adaline.grady,0.3895963991,25.64.179.226,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n16569,6,376,2017-01-20 09:49:12,http://rohan.org/kay,0.1809315447,87.58.12.110,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n16570,6,376,2017-01-13 22:20:22,http://lemkerodriguez.co/kellen.casper,0.0504526179,232.199.147.174,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n16571,6,376,2016-12-16 17:31:44,http://trantow.biz/sven_maggio,0.4401615469,37.122.119.204,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n16572,6,376,2017-02-05 08:08:35,http://schustermosciski.info/zack,0.6202902262,184.22.111.106,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n16573,6,376,2017-05-25 17:21:37,http://cremin.io/westley,0.4146574479,58.146.26.23,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n16574,6,376,2017-03-20 13:11:46,http://wunschwilkinson.net/rene,0.6886023629,110.251.141.94,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n16575,6,376,2017-02-12 18:36:36,http://reinger.io/giovanna,0.8972720373,59.188.168.12,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n16576,6,376,2017-04-29 19:31:48,http://spencer.biz/kamron,0.3230641352,191.174.110.5,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n16577,6,376,2016-12-16 16:59:44,http://westspencer.name/nasir,0.9636904835,87.57.214.97,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n16578,6,376,2017-04-11 02:21:45,http://boganmcdermott.name/helga_moore,0.2502849076,207.239.125.186,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n16579,6,376,2017-05-22 13:36:33,http://gulgowskiwiza.info/dee_kulas,0.8315659239,128.35.147.238,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n16580,6,376,2016-12-23 07:46:38,http://herman.com/richard.towne,0.6425510365,195.111.207.238,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n16581,6,376,2017-04-19 04:25:37,http://jacobson.com/sabrina,0.5782485286,52.171.169.181,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n16582,6,376,2017-02-12 15:19:59,http://bradtkeroob.com/karianne_rutherford,0.6000834283,249.110.63.63,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n16583,6,376,2017-05-04 13:50:29,http://luettgen.net/demarcus.connelly,0.9955280651,161.251.32.13,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n16584,6,376,2017-04-08 13:17:46,http://erdman.name/elenora,0.8318231835,49.116.58.146,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n16585,6,376,2017-06-12 17:37:15,http://batz.net/idella,0.9257205453,104.28.10.74,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n16586,6,376,2017-05-05 19:15:47,http://greenholt.co/gunnar.schuppe,0.9934659527,67.205.205.202,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n16587,6,376,2017-02-18 00:06:51,http://langworthklein.io/stone,0.5751141785,203.134.136.130,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n16588,6,376,2017-03-11 09:43:31,http://wolfkuhlman.biz/rachael,0.0841935518,215.131.208.181,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n16589,6,376,2017-03-22 13:57:07,http://homenickmonahan.co/eloy.okon,0.7589594296,46.90.124.123,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n16590,6,376,2017-01-18 06:56:30,http://blick.info/wava,0.3006569116,113.153.212.35,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n16591,6,376,2017-02-08 09:58:52,http://schuppe.info/leonard,0.8832744749,57.140.217.4,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n16592,6,376,2017-03-03 20:05:02,http://kuhlmanfritsch.info/carey,0.2515468007,36.171.124.160,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n16593,6,376,2017-04-20 12:59:02,http://hudson.biz/gertrude,0.9893260249,43.61.107.76,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n16594,6,376,2017-02-04 13:35:27,http://ortizhoppe.com/shirley_altenwerth,0.2920871690,205.37.58.23,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n16595,6,376,2017-01-02 16:34:38,http://hartmann.name/enola.powlowski,0.5935290543,203.132.178.215,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n16596,6,376,2016-12-20 09:11:12,http://ankunding.biz/miles_ortiz,0.7730332284,88.93.147.180,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n16597,6,376,2017-01-20 15:00:49,http://ratkefriesen.com/hermina,0.0099469422,146.54.158.216,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n16598,6,376,2017-02-26 09:28:01,http://weimann.co/elisa.orn,0.0039764457,113.172.135.187,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n16599,6,376,2016-12-19 01:37:42,http://bergnaum.io/darlene,0.7238795796,135.170.28.251,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n16600,6,376,2017-03-13 14:44:19,http://cormier.net/sebastian_pollich,0.9447776791,35.18.214.32,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n3730,2,82,2017-01-23 15:59:38,http://krajcik.io/desiree_nienow,0.4445342905,250.235.178.87,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n3731,2,82,2017-05-25 21:40:34,http://batz.net/monique.smitham,0.0245300165,53.171.123.125,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n3732,2,82,2016-12-25 21:13:48,http://mcculloughtillman.io/greyson_nader,0.8107643054,224.20.242.107,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n3733,2,82,2017-05-29 17:11:42,http://trantow.info/corrine,0.3126949361,11.172.68.4,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n3734,2,82,2017-01-28 19:51:01,http://stiedemann.biz/gabrielle,0.1411261311,248.15.89.222,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n3735,2,82,2017-05-18 02:24:44,http://leannon.com/ottis,0.0324509181,106.121.152.218,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n3736,2,82,2017-05-28 16:06:17,http://kautzer.biz/haylee,0.6244162394,123.249.141.128,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n3737,2,82,2017-04-09 19:23:49,http://oberbrunner.net/reagan_durgan,0.9772428084,224.209.238.37,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n3738,2,82,2017-04-24 02:36:58,http://gerhold.biz/joe_ortiz,0.7578866000,114.50.241.242,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n3739,2,82,2017-04-14 12:12:09,http://hirthespinka.co/luigi,0.2498373962,46.162.244.21,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n3740,2,82,2017-02-08 23:53:23,http://deckow.net/jadyn_orn,0.7306647390,242.187.108.183,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n3741,2,82,2017-02-25 01:17:29,http://klein.net/bertrand,0.2235401421,67.218.205.86,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n3742,2,82,2017-04-29 15:27:08,http://bernhardcorkery.com/kaelyn_boyle,0.8041746759,67.183.92.72,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n3743,2,82,2017-01-04 19:36:16,http://baileyschneider.net/jacklyn_blanda,0.3777350757,10.95.202.127,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n3744,2,82,2016-12-13 14:24:52,http://beahan.net/bret,0.3751634681,8.149.220.175,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n3745,2,82,2017-04-30 14:08:39,http://conn.name/guy_mckenzie,0.5590909361,3.29.83.248,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n3746,2,82,2017-06-06 21:31:45,http://robel.info/kennedy_abernathy,0.2610399275,240.58.98.163,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n3747,2,82,2017-04-30 06:06:50,http://grimesarmstrong.io/alysa,0.1555998387,234.184.176.72,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n3748,2,82,2016-12-28 11:24:51,http://bosco.info/sven_gleason,0.6114911391,81.105.114.93,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n3749,2,82,2017-01-19 17:05:14,http://treutel.net/lulu.herzog,0.1282911193,65.7.89.122,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n3750,2,82,2017-05-10 21:14:19,http://hicklegrady.info/randy,0.1158412531,206.10.123.35,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n3751,2,82,2017-03-22 18:02:00,http://heidenreich.net/samanta,0.7563317070,22.218.154.143,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n3752,2,82,2016-12-26 03:06:24,http://armstrong.com/cory,0.1902196335,195.152.177.137,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n3753,2,82,2017-04-21 16:23:45,http://lueilwitzkuhlman.net/helena,0.7396126422,253.177.46.22,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n3754,2,82,2017-04-06 15:50:02,http://jacobswalter.co/araceli,0.1059263279,145.73.92.242,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n3755,2,82,2017-01-10 08:41:49,http://bartoncarter.info/paxton_greenholt,0.6855307198,232.203.165.228,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n3756,2,82,2017-05-12 09:44:23,http://carroll.biz/serenity.nolan,0.0720488790,202.138.61.166,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n3757,2,82,2016-12-13 20:44:12,http://walter.net/landen,0.5364589920,241.158.162.38,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n3758,2,82,2017-05-23 14:14:23,http://leffler.co/stefanie_littel,0.0844675819,19.13.127.148,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n3759,2,82,2017-03-11 17:17:54,http://ohara.com/keaton.sanford,0.4500484821,94.210.132.57,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n3760,2,82,2017-01-22 03:13:30,http://runtehills.org/ashlynn,0.3145458912,138.119.218.26,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n3761,2,82,2017-04-04 07:05:03,http://macgyverblick.info/tyshawn,0.4754459346,41.152.243.37,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n3762,2,82,2017-04-12 19:27:51,http://harber.org/cathrine,0.3886921842,86.175.174.200,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n3763,2,82,2017-05-13 08:43:00,http://simonis.com/alta_jenkins,0.2373924440,114.185.149.191,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n3764,2,82,2017-02-20 08:06:28,http://millsdoyle.biz/jerald,0.4185823068,142.67.115.10,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n3765,2,82,2017-02-11 12:59:00,http://runte.io/lucinda,0.3251611265,168.27.177.91,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n3766,2,82,2016-12-17 00:33:42,http://wolff.co/jefferey,0.6383166665,53.135.40.230,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n3767,2,82,2016-12-21 05:15:02,http://kerluke.name/sister,0.2665727269,222.212.69.4,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n3768,2,82,2017-03-22 12:13:44,http://prohaska.io/katheryn.cartwright,0.0858944588,238.207.94.186,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n3769,2,83,2017-02-07 20:29:16,http://collins.net/demetris_kuhlman,0.6913464708,4.224.208.148,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n3770,2,83,2017-02-07 11:42:18,http://mraz.com/christiana,0.4491614033,241.141.27.241,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n3771,2,83,2017-04-27 07:03:09,http://farrell.name/misty.kub,0.2532366731,16.154.50.81,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n3772,2,83,2017-05-30 11:33:32,http://crookshilll.name/shea.kihn,0.0923430092,216.148.228.159,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n3773,2,83,2017-03-02 22:42:50,http://stanton.com/jude.gaylord,0.1618154374,102.118.250.23,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n3774,2,83,2017-01-15 23:47:30,http://okeefeward.com/judd_bogan,0.7488847529,237.114.138.14,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n3775,2,83,2017-05-06 22:28:56,http://miller.com/allison,0.7724482953,86.129.120.19,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n3776,2,83,2016-12-21 00:01:35,http://jast.name/aileen,0.2876425937,56.158.197.195,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n3777,2,83,2016-12-25 05:23:24,http://schinner.com/cole,0.5613038915,170.27.248.159,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n3778,2,83,2017-03-11 08:15:55,http://white.com/jerrod_kirlin,0.8321620892,60.23.47.195,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n3779,2,83,2017-06-06 21:57:23,http://conn.biz/else.kiehn,0.4787418351,44.13.86.138,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n3780,2,83,2017-06-02 01:38:31,http://schulist.com/donavon_lang,0.8682351409,243.234.227.66,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n3781,2,83,2017-05-11 04:37:03,http://harris.name/aileen,0.4740015994,142.34.3.49,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n10665,4,239,2017-04-09 19:26:32,http://mosciski.org/deontae_reynolds,,208.136.131.78,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n10666,4,239,2017-01-31 21:57:11,http://bosco.net/zane_gutkowski,,241.8.239.156,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n10667,4,239,2017-05-06 06:30:21,http://gerhold.info/arturo_kaulke,,226.119.127.170,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n10668,4,239,2016-12-14 01:59:34,http://gerlachbatz.co/wilfred_leuschke,,221.131.28.128,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n10669,4,239,2017-03-10 14:28:04,http://olsonwaters.org/paige_bartell,,45.202.49.62,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n10670,4,239,2017-02-14 23:08:03,http://purdy.net/caria,,37.149.87.164,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n10671,4,239,2017-01-15 21:40:38,http://vonmoen.biz/columbus.wunsch,,76.248.120.37,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n10672,4,239,2017-02-26 04:08:24,http://labadie.name/murl.swaniawski,,107.110.55.195,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n10673,4,239,2017-05-31 22:39:32,http://bogisich.net/haie,,66.66.177.242,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n10674,4,239,2016-12-26 16:43:45,http://jones.io/mona.bogan,,137.196.16.251,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n10675,4,239,2017-05-31 17:44:57,http://murazikko.biz/josefina.reynolds,,160.39.239.196,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n10676,4,239,2017-05-11 02:20:15,http://hartmann.org/jee,,184.54.120.190,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n10677,4,239,2017-02-10 00:41:56,http://haleygrant.co/pansy,,184.200.118.20,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n10678,4,239,2017-04-13 18:18:59,http://towneerdman.net/fidel.tremblay,,22.212.2.38,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n10679,4,239,2017-05-20 16:01:33,http://fadel.net/nayeli,,75.205.40.131,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n10680,4,239,2017-05-04 01:11:15,http://grant.com/greyson,,122.197.189.40,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n10681,4,239,2017-04-16 01:49:05,http://stiedemann.name/retha_grimes,,57.88.3.177,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n10682,4,239,2017-01-29 02:07:09,http://rohan.com/juwan.hand,,152.203.9.136,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n10683,4,239,2017-03-18 09:01:07,http://goyette.co/pasquale,,17.173.152.38,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n10684,4,239,2017-02-20 22:38:38,http://conn.name/augustus,,22.18.56.141,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n10685,4,239,2017-01-14 13:36:58,http://greenholt.biz/flavio.collier,,247.110.15.105,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n10686,4,239,2017-01-03 14:17:36,http://botsford.co/garnet.medhurst,,236.154.184.195,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n10687,4,239,2017-01-03 20:20:54,http://naderwilliamson.co/mylene,,142.211.2.213,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n10688,4,239,2016-12-29 02:23:05,http://lindgrenschinner.name/isabelle.buckridge,,79.135.235.126,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n10689,4,239,2017-05-18 22:06:28,http://whitebogan.biz/german.huels,,66.121.191.68,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n10690,4,239,2017-03-03 00:03:56,http://aufderhar.net/darrell_harvey,,185.83.56.228,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n10691,4,240,2017-05-11 06:32:51,http://labadie.io/jason,,150.67.201.69,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n10692,4,240,2017-04-27 05:42:43,http://kopouros.biz/arvilla.morar,,132.148.246.208,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n10693,4,240,2017-02-16 17:30:28,http://bernhardlakin.co/felix,,89.225.173.167,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n10694,4,240,2017-06-12 20:11:37,http://williamson.name/ike_ryan,,217.88.170.211,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n10695,4,240,2017-03-01 00:55:34,http://tromp.biz/vicenta_doyle,,16.41.2.248,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n10696,4,240,2017-01-05 15:27:27,http://towne.com/chanel,,245.69.107.232,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n10697,4,240,2017-05-13 16:10:35,http://daniel.name/nelle.huels,,108.37.75.203,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n10698,4,240,2017-06-12 23:01:33,http://thompson.name/demond.brown,,3.166.16.136,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n10699,4,240,2017-02-09 10:43:54,http://rohan.co/margarett.wiza,,19.201.89.107,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n10700,4,240,2017-05-24 19:05:47,http://runolfsdottir.co/morris_trantow,,240.229.185.206,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n10701,4,240,2016-12-14 14:43:35,http://hahndickens.co/giuseppe.parisian,,73.121.43.132,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n10702,4,240,2017-05-20 13:34:58,http://cremin.org/denis.trantow,,79.252.199.27,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n10703,4,240,2017-04-05 06:12:29,http://dickiankunding.info/kylee_eichmann,,50.73.119.200,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n10704,4,240,2017-04-01 10:02:55,http://collier.io/johnson.beatty,,93.166.110.162,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n10705,4,240,2016-12-23 19:44:33,http://cronin.io/bertha_mclaughlin,,124.57.113.209,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n10706,4,240,2017-02-22 14:36:25,http://armstrong.org/rachael,,237.6.217.215,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n10707,4,240,2017-02-08 09:03:34,http://lind.net/armand.kreiger,,170.143.9.85,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n10708,4,240,2017-04-21 22:39:02,http://hoeger.net/dorothy.beahan,,145.8.251.62,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n10709,4,240,2017-04-12 04:14:09,http://spencer.co/donnie,,65.78.101.66,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n10710,4,240,2017-05-01 08:23:42,http://cruickshank.com/felix_gottlieb,,192.180.15.230,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n10711,4,240,2017-06-08 02:37:21,http://reynolds.io/trenton.dietrich,,160.239.187.241,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n10712,4,240,2016-12-25 01:16:43,http://zulauf.org/tyrell.bode,,134.113.222.17,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n10713,4,240,2017-01-11 10:35:34,http://schuppe.co/maddison,,160.57.196.163,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n10714,4,240,2016-12-19 06:40:37,http://mclaughlin.io/alvina_shanahan,,107.208.88.35,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n10715,4,240,2017-05-03 22:49:30,http://rauokon.info/tierra,,251.24.43.105,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n10716,4,240,2017-05-29 03:36:36,http://dickieffertz.com/jules,,58.135.181.26,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n10717,4,240,2017-02-16 04:34:53,http://kulas.co/everette_berge,,100.219.149.56,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n10718,4,240,2017-03-05 05:35:14,http://feil.co/orlando,,146.200.96.145,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n10719,4,240,2017-02-27 16:24:37,http://hackettcruickshank.com/ramona_greenholt,,124.234.185.114,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n7706,3,171,2016-12-20 21:09:07,http://zemlakvandervort.net/marlin.langosh,,56.218.155.211,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n7707,3,171,2017-05-11 21:11:49,http://kautzer.org/dayana_oconner,,196.93.197.135,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n7708,3,171,2017-06-12 11:24:13,http://dooley.biz/king,,215.163.214.208,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n7709,3,171,2017-05-18 07:25:25,http://walter.org/marcella_shanahan,,123.37.244.138,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n7710,3,171,2017-01-22 09:34:21,http://wisozk.name/deangelo_schmitt,,77.95.210.10,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n7711,3,171,2017-01-07 01:11:45,http://jacobs.io/crystal_hettinger,,163.44.76.179,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n7712,3,171,2017-06-02 23:18:27,http://hoppe.org/eleanora,,209.193.50.72,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n7713,3,171,2017-04-17 17:37:02,http://rutherfordtreutel.biz/nicole,,49.36.238.185,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n7714,3,171,2016-12-20 11:31:44,http://lakin.biz/marilie,,251.149.155.4,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n7715,3,171,2017-05-30 09:12:22,http://raynor.biz/terrill,,158.37.25.160,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n7716,3,171,2017-04-16 07:12:02,http://friesensmith.net/citlalli,,125.76.91.205,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n7717,3,171,2017-04-08 10:32:59,http://fisher.name/hobart_boehm,,142.143.14.114,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n7718,3,172,2017-03-20 01:09:07,http://damore.org/dolly.will,0.9870148939,16.90.140.233,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n7719,3,172,2017-04-19 02:34:08,http://boyle.net/audrey,0.0981862012,161.103.20.231,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n7720,3,172,2017-04-14 08:44:05,http://welchemard.co/sylvester.gerhold,0.2991805303,220.164.224.199,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n7721,3,172,2017-06-03 00:36:45,http://hansen.io/dixie,0.4252261409,8.232.244.37,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n7722,3,172,2016-12-14 04:31:39,http://bartolettirohan.org/shayna_hauck,0.2662086494,233.84.63.35,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n7723,3,172,2016-12-15 02:06:01,http://johnson.org/abagail.moriette,0.2696360854,48.226.142.138,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n7724,3,172,2017-05-21 23:24:46,http://mcclure.info/olen_morar,0.7801248300,62.247.67.150,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n7725,3,172,2017-04-12 16:58:33,http://kihngoldner.info/aunta.daniel,0.3234418532,225.137.96.98,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n7726,3,172,2017-03-25 07:27:17,http://mcculloughbednar.org/horacio,0.7912951602,46.125.166.70,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n7727,3,172,2017-01-15 12:45:21,http://kuphal.net/vella,0.0203223484,174.99.39.192,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n7728,3,172,2017-05-29 00:44:29,http://treutelherzog.co/bria,0.2754350707,172.30.173.94,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n7729,3,172,2016-12-15 13:34:21,http://gibson.net/trever.ernser,0.2247154880,114.210.54.95,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n7730,3,172,2017-03-12 04:01:05,http://reingergerlach.org/ward.haag,0.9585523972,203.210.198.119,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n7731,3,172,2017-03-03 16:58:41,http://littelmetz.info/waino.wehner,0.0395603348,244.127.63.226,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n7732,3,172,2017-03-21 17:48:40,http://deckow.info/vickie,0.8080118317,2.108.226.2,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n7733,3,172,2017-04-18 11:45:12,http://bogan.com/adelle,0.7814021989,84.117.250.176,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n7734,3,172,2016-12-22 04:12:14,http://heathcote.io/yasmine_zemlak,0.9524736155,70.222.144.67,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n7735,3,172,2017-02-18 04:51:54,http://tromp.io/hillard.graham,0.8427736778,2.125.164.73,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n7736,3,172,2017-02-03 20:01:14,http://heidenreichyundt.net/ulises_weimann,0.6632473895,213.81.15.139,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n7737,3,172,2017-02-18 00:47:50,http://cristfriesen.io/brian_pollich,0.7213159779,158.118.245.201,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n7738,3,172,2017-02-06 05:22:59,http://olson.org/dayton,0.4909528422,196.251.184.169,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n7739,3,172,2017-02-11 18:19:13,http://kirlinmacgyver.co/ola_leannon,0.1246779852,28.61.19.35,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n7740,3,172,2017-02-08 09:44:19,http://mcglynn.info/ezra,0.4471496276,226.38.207.137,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n7741,3,172,2016-12-30 10:10:51,http://mclaughlin.info/kaelyn.oconner,0.0706284163,99.169.104.62,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n7742,3,172,2017-05-02 05:02:33,http://bashirian.com/carlos,0.7031612968,149.116.36.21,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n7743,3,172,2017-03-22 15:41:00,http://berge.io/stephania,0.8192396451,45.156.201.75,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n7744,3,172,2017-05-10 15:34:51,http://casper.org/oran_ohara,0.5670537300,118.43.83.138,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n7745,3,172,2017-06-02 11:31:56,http://sanford.info/name,0.2036572521,184.151.141.125,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n7746,3,172,2016-12-13 23:49:06,http://kirlin.org/mozell.becker,0.0014899413,181.13.105.85,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n7747,3,172,2017-01-20 06:42:24,http://connelly.name/madge.monahan,0.7065613316,164.97.28.240,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n7748,3,172,2017-05-07 15:56:43,http://ankundingmaggio.com/bo,0.0912106695,205.12.171.173,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n7749,3,172,2016-12-15 05:58:50,http://douglasklocko.biz/rowena_harber,0.6029303275,150.77.23.190,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n7750,3,172,2017-01-04 07:10:57,http://greenfelder.co/charlotte,0.0206210600,28.100.46.197,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n7751,3,172,2017-06-03 17:44:45,http://carroll.name/elvis,0.3298061607,94.7.142.7,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n7752,3,172,2017-02-09 00:37:18,http://witting.io/alfonso_hodkiewicz,0.5386554706,160.145.67.18,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n7753,3,172,2017-06-08 03:47:18,http://nitzsche.net/nolan.buckridge,0.7242867838,29.206.242.36,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n7754,3,172,2017-02-28 05:20:51,http://vonrueden.biz/aglae.schroeder,0.5086554700,15.254.15.89,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n7755,3,172,2017-03-04 00:13:42,http://fadel.info/effie.ko,0.0855208025,196.179.250.110,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n7756,3,172,2016-12-23 08:56:51,http://block.info/tristin,0.5773946599,69.246.16.90,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n7757,3,172,2017-01-19 17:47:13,http://jacobidietrich.biz/jackeline,0.8952473707,113.114.83.247,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n7758,3,172,2017-02-24 19:01:41,http://eichmann.co/audra.auer,0.3185523449,180.186.112.47,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n13616,5,307,2017-04-03 00:48:24,http://hodkiewicz.info/ellsworth,,145.28.110.156,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n13617,5,307,2017-01-16 08:11:04,http://ondricka.name/jodie_hickle,,147.146.74.210,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n13618,5,307,2017-04-24 07:57:46,http://bergstrompollich.org/sterling_rempel,,117.11.3.146,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n13619,5,307,2017-02-10 13:12:46,http://mcglynn.com/khalil.kulas,,246.149.147.238,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n13620,5,307,2017-01-20 15:12:52,http://corkery.name/anastasia,,72.208.40.12,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n13621,5,307,2017-04-29 17:18:18,http://ruel.name/dimitri,,72.46.29.169,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n13622,5,307,2017-01-12 05:59:59,http://block.org/kraig_schinner,,179.200.154.141,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n13623,5,307,2017-01-18 13:43:28,http://kuhlman.com/claudine.prohaska,,155.66.94.57,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n13624,5,307,2016-12-14 08:31:37,http://schoenferry.io/percival,,212.196.169.61,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n13625,5,307,2017-01-23 10:36:56,http://breitenberg.co/kip,,22.102.127.253,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n13626,5,307,2017-01-18 04:40:47,http://deckow.org/meda,,229.85.243.98,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n13627,5,307,2017-04-05 15:27:15,http://okeefeheidenreich.net/germaine,,134.49.93.203,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n13628,5,307,2017-01-08 11:23:57,http://rowemills.info/haleigh,,129.108.9.125,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n13629,5,307,2017-03-06 23:28:49,http://feilfritsch.info/bernie,,86.103.84.164,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n13630,5,307,2017-02-23 10:46:15,http://brakus.org/greg,,26.48.174.45,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n13631,5,307,2017-03-11 07:02:45,http://abbottullrich.net/orion.stanton,,30.122.199.195,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n13632,5,307,2017-01-07 14:28:48,http://lemkefay.biz/odell.morar,,109.153.192.34,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n13633,5,307,2017-03-12 20:51:39,http://muller.com/sister.satterfield,,206.104.253.105,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n13634,5,307,2017-04-01 18:52:28,http://rohanjacobson.biz/kristoffer,,113.17.225.153,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n13635,5,307,2017-05-31 09:36:55,http://watersborer.net/abel,,78.45.198.52,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n13636,5,307,2017-02-06 01:03:10,http://macejkovic.net/darlene_hirthe,,36.56.188.201,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n13637,5,307,2017-02-24 00:19:46,http://crona.co/merle_nikolaus,,242.70.22.199,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n13638,5,307,2017-01-20 22:35:05,http://rosenbaumstracke.io/esmeralda,,182.114.71.228,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n13639,5,307,2017-06-07 15:32:05,http://mills.info/mariane_labadie,,107.93.173.70,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n13640,5,307,2017-05-04 21:33:29,http://quitzon.com/newell.wilderman,,159.240.113.78,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n13641,5,307,2017-01-19 20:09:39,http://leschparisian.net/shea.connelly,,216.139.119.47,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n13642,5,307,2017-06-03 15:09:38,http://yundtmclaughlin.biz/leo,,93.89.193.243,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n13643,5,307,2017-02-05 00:11:27,http://kaulke.org/ethelyn,,45.25.183.83,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n13644,5,307,2017-05-04 21:30:49,http://binsgutmann.co/orlo,,39.57.139.233,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n13645,5,307,2017-04-13 19:54:02,http://lebsack.name/fabiola_erdman,,120.195.102.7,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n13646,5,307,2017-01-18 06:41:09,http://kiehn.biz/keyon,,14.115.5.49,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n13647,5,307,2017-03-28 08:01:40,http://ziemannkuphal.io/ellen_parisian,,195.78.26.80,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n13648,5,307,2017-04-22 02:06:57,http://moriettemills.co/unique.waters,,133.77.147.34,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n13649,5,307,2017-03-30 00:07:00,http://cruickshank.io/cristopher,,110.5.192.241,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n13650,5,307,2017-04-18 05:56:10,http://smithhamill.info/morton,,56.93.218.217,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n13651,5,307,2017-03-23 12:41:57,http://gerhold.com/alyson,,153.29.171.84,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n13652,5,307,2017-02-07 13:52:25,http://rohan.biz/adela.cummings,,82.133.65.80,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n13653,5,307,2017-05-10 23:09:11,http://barton.org/libby.grady,,114.141.228.158,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n13654,5,307,2017-03-30 23:19:01,http://medhurst.org/patrick.kiehn,,188.141.126.48,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n13655,5,307,2017-03-08 01:04:17,http://mohr.org/savanna.herman,,120.187.148.18,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n13656,5,307,2017-03-23 21:08:51,http://marks.com/brandt,,72.173.35.34,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n13657,5,307,2017-01-24 22:25:48,http://wehner.name/karelle,,59.179.131.110,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n13658,5,307,2017-02-28 15:38:26,http://heathcote.biz/major.keeling,,11.88.155.229,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n13659,5,307,2017-01-11 12:08:19,http://breitenberg.io/brenna.schoen,,141.63.30.203,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n13660,5,307,2017-01-01 01:31:54,http://emard.org/taurean.dibbert,,217.181.72.36,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n13661,5,307,2017-06-11 03:59:10,http://jaskolski.info/elia.mitchell,,232.185.47.39,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n13662,5,307,2017-01-27 13:19:15,http://fritschpredovic.info/colten,,14.46.197.240,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n13663,5,307,2017-04-24 15:57:47,http://rempel.com/jade,,80.208.39.28,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n13664,5,307,2017-04-08 00:00:36,http://koepphoeger.biz/ashley,,77.34.215.126,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n13665,5,307,2017-01-07 20:07:27,http://bode.io/asha,,210.20.93.23,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n13666,5,308,2017-04-06 19:49:04,http://gutkowski.net/juwan,,228.204.53.130,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n13667,5,308,2017-05-02 05:12:16,http://oberbrunner.net/orrin_fritsch,,181.4.155.30,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n13668,5,308,2017-01-24 19:46:59,http://goyette.net/aric_beahan,,82.127.32.178,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n13669,5,308,2017-06-12 11:51:03,http://ferrygreen.info/marlon,,213.115.57.36,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n13670,5,308,2017-05-21 05:23:43,http://gaylord.com/micheal.haag,,39.243.185.240,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n16601,6,376,2017-02-12 23:30:11,http://rennerbosco.co/candace,0.6305074180,48.249.32.16,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n16602,6,376,2017-05-06 17:22:27,http://ondricka.co/brent.wehner,0.7518886484,117.99.110.203,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n16603,6,376,2017-05-16 05:08:37,http://rowe.com/blair,0.0326024815,164.52.141.199,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n16604,6,376,2017-05-25 03:50:18,http://abshire.biz/macie,0.3861400726,27.201.64.196,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n16605,6,376,2017-06-14 00:23:16,http://hodkiewiczcain.net/simone.strosin,0.5079105061,89.208.171.143,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n16606,6,376,2017-03-15 13:59:27,http://eichmannrodriguez.io/mauricio,0.4547583888,213.202.248.41,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n16607,6,376,2017-01-31 21:14:43,http://stamm.name/gustave,0.9940792418,53.56.26.82,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n16608,6,376,2017-01-04 19:39:12,http://donnellyroob.io/halie,0.1842309505,159.153.7.79,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n16609,6,376,2017-04-12 10:30:54,http://bode.net/sienna,0.3116145987,202.144.215.132,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n16610,6,376,2017-05-11 17:20:08,http://lynch.co/robert.klein,0.2554555624,155.66.95.216,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n16611,6,376,2017-02-02 23:39:38,http://oreilly.biz/naomi,0.9345552899,229.212.200.231,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n16612,6,377,2016-12-30 00:59:07,http://price.biz/rico,,68.237.245.100,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n16613,6,377,2017-01-06 12:22:07,http://roberts.io/emory,,12.4.64.162,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n16614,6,377,2017-01-08 22:23:27,http://cain.com/osvaldo.abbott,,206.117.134.220,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n16615,6,377,2016-12-21 06:11:14,http://zulaufstracke.co/noemy,,42.13.89.123,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n16616,6,377,2017-03-27 11:11:04,http://hayesryan.net/giles_langworth,,191.210.227.47,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n16617,6,377,2017-04-28 15:59:35,http://pollich.com/elijah.leuschke,,251.62.129.41,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n16618,6,377,2017-04-12 03:11:11,http://kerluke.net/kurt,,72.192.24.163,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n16619,6,377,2017-05-20 02:24:36,http://deckowhartmann.com/bradley,,214.239.177.101,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n16620,6,377,2017-03-05 01:58:58,http://kuhiccain.com/leola.effertz,,24.175.126.217,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n16621,6,377,2017-04-11 02:31:50,http://mcdermott.org/hillard,,85.79.214.58,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n16622,6,377,2017-06-09 14:13:49,http://flatleymcdermott.co/kiara.stanton,,240.149.116.106,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n16623,6,377,2017-05-06 08:14:24,http://farrell.net/anika_bernhard,,26.43.161.176,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n16624,6,377,2017-03-06 13:29:04,http://lind.info/stefanie,,164.48.129.249,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n16625,6,377,2017-05-20 16:55:17,http://mooreblanda.org/dulce.bernier,,205.74.83.146,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n16626,6,377,2017-02-04 00:07:49,http://koch.biz/alvera_purdy,,43.224.57.117,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n16627,6,377,2017-05-17 16:36:31,http://schuster.info/kim.pouros,,68.250.220.36,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n16628,6,377,2017-04-28 12:33:52,http://emmerichruel.name/irwin.christiansen,,76.178.6.134,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n16629,6,377,2017-05-15 17:31:40,http://mcdermott.org/lyda,,72.59.187.223,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n16630,6,377,2017-04-18 00:08:18,http://ryan.biz/karine,,97.14.190.90,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n16631,6,377,2017-04-02 08:53:14,http://bergstrom.info/cristian,,181.38.31.28,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n16632,6,377,2017-03-18 22:26:11,http://oconnell.org/julia_quigley,,104.167.152.113,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n16633,6,377,2017-04-28 04:54:56,http://muller.co/jerald,,164.219.46.211,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n16634,6,377,2017-06-07 03:12:48,http://bradtke.org/jerrod,,157.79.228.146,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n16635,6,377,2017-05-05 00:29:28,http://brown.name/kali,,144.175.220.124,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n16636,6,377,2017-02-11 13:32:37,http://funk.net/neva,,176.97.213.159,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n16637,6,377,2017-03-10 08:40:03,http://robelrosenbaum.io/markus_hyatt,,203.209.205.141,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n16638,6,377,2017-01-14 14:00:39,http://kochcronin.io/nellie_thompson,,164.95.37.4,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n16639,6,377,2017-04-29 22:03:26,http://johnseichmann.name/larue_mcclure,,78.75.126.34,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n16640,6,377,2017-02-23 21:07:25,http://ryanhartmann.name/arnoldo,,218.153.195.97,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n16641,6,377,2016-12-27 13:18:18,http://kostanton.biz/wendy,,123.75.60.130,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n16642,6,377,2017-04-17 13:55:12,http://collins.io/lorine,,132.194.171.157,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n16643,6,377,2016-12-18 06:40:59,http://bergnaum.co/jaleel,,97.53.6.147,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n16644,6,377,2017-03-30 08:56:16,http://millsmann.biz/conner.bosco,,164.135.32.130,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n16645,6,377,2017-01-05 19:21:16,http://gutmann.net/bart.cronin,,231.102.234.53,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n16646,6,377,2017-03-13 19:15:39,http://fishercarroll.com/presley.kirlin,,127.242.77.58,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n16647,6,377,2017-06-10 19:25:13,http://lynchwatsica.info/maude,,242.192.107.186,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n16648,6,377,2016-12-17 00:35:11,http://mosciski.net/marlene,,19.247.189.113,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n16649,6,377,2017-03-16 21:07:48,http://strosin.org/anderson,,99.107.122.135,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n16650,6,377,2017-05-11 23:55:59,http://jakubowski.io/yoshiko.spinka,,223.90.84.243,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n16651,6,377,2017-04-07 00:41:47,http://reichelkuphal.org/jensen_welch,,80.106.249.67,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n16652,6,377,2017-01-03 22:00:52,http://mayertschmeler.co/misael,,241.198.30.132,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n16653,6,377,2017-03-05 01:47:56,http://lakin.com/jerrod.grant,,59.83.172.185,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n16654,6,377,2017-03-24 04:58:04,http://cartwright.co/chad.brown,,111.104.81.220,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n3782,2,83,2017-05-16 03:28:38,http://eichmann.net/deondre,0.2811298498,124.118.167.78,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n3783,2,83,2017-03-11 23:19:04,http://farrell.net/briana,0.9871184350,235.130.80.92,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n3784,2,83,2017-06-13 11:49:02,http://hansen.com/vernon_jones,0.3613562241,168.108.42.155,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n3785,2,83,2017-02-14 11:34:09,http://schroeder.biz/josianne,0.0451508652,77.95.72.13,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n3786,2,83,2017-03-29 17:06:34,http://goldner.com/guido,0.7425097458,185.151.237.90,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n3787,2,83,2017-03-27 07:22:08,http://jacobson.info/margarete.blanda,0.1330591735,164.188.33.97,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n3788,2,83,2017-04-14 15:46:10,http://purdywillms.org/francisco,0.4405247874,212.162.129.108,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n3789,2,83,2017-03-13 03:18:23,http://block.org/bulah_kulas,0.2605070241,108.72.122.196,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n3790,2,83,2017-05-29 13:52:05,http://dietrichschroeder.info/stephon_rolfson,0.1153439895,28.154.201.115,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n3791,2,83,2017-01-17 10:45:34,http://zieme.info/gielle,0.3008681070,224.62.179.116,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n3792,2,83,2017-03-04 12:50:59,http://herzog.co/luz.hilll,0.9207124836,224.135.107.113,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n3793,2,83,2017-04-01 03:32:26,http://zulaufquigley.org/chasity,0.4004565104,251.182.195.226,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n3794,2,83,2017-04-23 06:51:08,http://ferry.name/johan,0.9533464021,87.122.213.114,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n3795,2,83,2017-05-12 01:04:00,http://koelpinharvey.com/efren_nolan,0.4994297520,215.178.172.234,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n3796,2,83,2017-02-24 19:10:55,http://tillman.io/octavia,0.7370758426,214.249.65.114,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n3797,2,83,2016-12-29 12:16:43,http://medhurstcronin.co/loyce_harber,0.9640302644,101.45.121.173,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n3798,2,83,2016-12-22 13:07:48,http://gislasonklocko.co/henderson,0.2415487959,139.188.55.252,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n3799,2,83,2017-03-04 05:33:11,http://renner.io/alvis,0.4650356651,108.45.40.135,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n3800,2,83,2016-12-17 14:36:09,http://fahey.com/ida,0.2364755247,96.161.35.27,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n3801,2,83,2017-04-23 07:46:15,http://vandervort.com/marcelina,0.7997106266,174.175.120.200,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n3802,2,83,2017-04-15 21:47:39,http://bailey.name/veda.blick,0.8077206358,62.202.210.229,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n3803,2,83,2017-01-25 08:36:28,http://gutmann.name/courtney,0.7634836408,119.183.71.178,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n3804,2,83,2017-05-07 19:03:04,http://huel.co/alexanne.zulauf,0.2686630039,32.115.60.244,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n3805,2,83,2017-05-08 13:16:41,http://stroman.info/fletcher,0.5522063347,139.192.3.90,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n3806,2,83,2017-03-18 05:37:08,http://frami.name/holden_gleichner,0.1982739151,112.69.170.146,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n3807,2,83,2017-03-23 00:28:34,http://stark.org/angelica,0.9128288526,247.234.117.183,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n3808,2,83,2017-05-28 15:19:17,http://koelpin.io/luna,0.8984262259,224.252.121.77,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n3809,2,83,2017-02-17 22:33:36,http://romagueracain.com/austin,0.6408306886,69.228.99.193,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n3810,2,83,2017-04-04 23:38:53,http://gutmann.info/magdalena.gaylord,0.9504748502,126.190.166.45,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n3811,2,83,2017-02-18 10:26:19,http://gutmannmarks.org/taylor.white,0.2659960982,177.21.101.38,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n3812,2,83,2017-01-16 21:15:21,http://bernhard.net/karine.hudson,0.2666801845,29.242.220.247,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n3813,2,83,2017-01-25 18:38:55,http://glover.info/shannon.bechtelar,0.5664703481,152.125.108.240,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n3814,2,83,2017-05-12 08:06:41,http://hartmannluettgen.name/paris_wiza,0.9345912830,184.243.93.133,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n3815,2,83,2016-12-17 02:42:20,http://waelchi.com/stefanie,0.8615195812,100.127.164.230,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n3816,2,83,2017-06-13 00:57:01,http://bradtke.name/jayson.schmeler,0.6962484339,143.168.233.147,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n3817,2,83,2017-03-03 08:27:44,http://upton.io/sarina.torphy,0.5202475538,97.87.86.52,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n3818,2,83,2017-03-13 21:55:30,http://blockklocko.org/orie,0.4178897008,176.22.63.153,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n3819,2,83,2017-03-10 10:55:42,http://ward.com/patricia,0.3888814560,95.183.30.244,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n3820,2,84,2016-12-27 18:09:03,http://lebsack.co/vivian,0.2130481939,231.215.221.2,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n3821,2,84,2017-05-19 20:35:32,http://reinger.name/gabriella,0.4563738950,180.149.236.215,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n3822,2,84,2017-04-10 12:45:02,http://kreiger.co/elwin_sipes,0.8368338506,164.78.106.242,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n3823,2,84,2017-03-15 02:18:36,http://wittingheidenreich.com/lamont,0.6477546244,239.254.169.39,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n3824,2,84,2016-12-15 05:27:03,http://christiansen.com/demond,0.7365750633,30.242.253.84,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n3825,2,84,2017-02-26 00:49:15,http://littel.biz/elisha.mcdermott,0.3408252847,226.36.17.151,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n3826,2,84,2017-01-14 08:26:43,http://brown.co/sanford,0.2673639591,162.178.157.137,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n3827,2,84,2016-12-31 23:18:29,http://gorczany.io/barrett,0.0604014761,151.128.231.115,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n3828,2,84,2017-03-07 00:38:19,http://hansen.name/rollin.murazik,0.6278163967,67.186.237.75,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n3829,2,84,2017-03-04 18:07:30,http://rowemoen.info/brayan,0.6065882612,168.138.93.245,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n3830,2,84,2017-02-20 13:16:23,http://mertz.biz/demetrius,0.9482747680,224.145.61.97,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n3831,2,84,2017-03-08 00:04:04,http://leannonsenger.co/lea,0.6436282555,31.26.122.90,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n3832,2,84,2017-02-06 15:50:17,http://mayer.co/shaina_lynch,0.0639014367,170.141.206.23,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n3833,2,84,2017-05-15 16:29:56,http://parisian.com/raegan,0.4475628069,119.32.92.96,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n10720,4,240,2016-12-25 09:46:44,http://dibbert.org/samanta.stehr,,224.112.88.44,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n10721,4,240,2017-06-07 06:55:49,http://huel.name/pearline.fay,,235.95.22.227,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n10722,4,240,2017-01-09 23:11:23,http://lueilwitz.io/bernice,,52.237.151.15,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n10723,4,240,2017-04-10 22:23:21,http://cummeratakemmer.info/arvilla.stanton,,194.97.164.119,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n10724,4,240,2017-01-29 21:03:59,http://hyatt.com/benedict_collier,,168.133.69.19,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n10725,4,240,2017-02-07 14:45:21,http://raynor.org/merl_rogahn,,64.83.27.43,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n10726,4,240,2017-04-10 08:24:56,http://mueller.info/beryl.hayes,,199.184.148.30,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n10727,4,240,2017-02-18 08:40:22,http://harrisdietrich.name/delilah,,152.96.209.2,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n10728,4,240,2017-06-14 01:03:44,http://von.name/muhammad.smith,,233.166.96.209,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n10729,4,240,2016-12-16 16:47:18,http://bashirian.name/fabian,,37.7.172.189,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n10730,4,240,2017-02-07 17:45:42,http://klocko.com/niko.rodriguez,,97.204.55.226,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n10731,4,240,2017-03-14 01:30:28,http://welchwunsch.com/delmer,,32.218.31.244,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n10732,4,240,2017-03-01 15:28:27,http://halvorson.co/reynold,,148.227.178.231,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n10733,4,240,2017-01-06 02:01:22,http://emmerich.io/darius,,114.158.65.130,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n10734,4,240,2017-02-22 19:53:27,http://hegmann.info/shakira_pacocha,,39.96.78.207,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n10735,4,240,2016-12-24 01:43:25,http://kihn.co/dallas.blick,,142.248.33.237,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n10736,4,240,2017-04-13 07:54:49,http://dare.biz/april,,124.119.77.230,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n10737,4,240,2016-12-16 01:07:12,http://willdonnelly.net/scarlett.wunsch,,254.32.25.227,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n10738,4,240,2016-12-29 00:32:02,http://halvorson.com/misael,,98.100.237.76,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n10739,4,240,2017-01-29 06:07:23,http://wilderman.co/clair_hills,,220.62.96.225,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n10740,4,240,2016-12-30 09:00:33,http://reichel.name/arturo.conroy,,196.131.14.148,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n10741,4,240,2017-01-07 17:34:31,http://schimmel.com/bennett,,168.159.144.224,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n10742,4,240,2017-03-03 22:40:47,http://feil.org/catharine,,10.29.69.78,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n10743,4,240,2017-01-19 03:16:40,http://fritschstark.info/dawn,,148.69.190.28,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n10744,4,241,2017-05-26 03:26:53,http://mrazkeebler.io/bridget,,100.218.90.231,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n10745,4,241,2017-05-26 23:47:38,http://osinskireinger.net/obie,,137.121.148.124,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n10746,4,241,2017-01-11 14:12:45,http://wisozk.com/kieran,,99.100.199.46,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n10747,4,241,2017-03-18 08:37:53,http://altenwerth.com/camden_johnson,,19.114.83.173,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n10748,4,241,2017-03-29 20:02:35,http://pollich.io/thurman_reynolds,,213.57.52.180,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n10749,4,241,2016-12-23 23:21:54,http://west.com/felix.kling,,208.217.126.246,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n10750,4,241,2016-12-21 21:15:45,http://rempel.org/evan,,2.210.249.99,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n10751,4,241,2017-03-24 19:39:32,http://grimes.net/makenna_tromp,,189.100.172.30,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n10752,4,241,2017-05-05 03:49:24,http://runolfonglover.co/emil_hills,,222.42.235.145,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n10753,4,241,2017-01-31 19:28:34,http://kunze.info/hilma,,127.127.139.92,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n10754,4,241,2017-05-01 09:17:45,http://schneider.co/davin,,212.169.12.25,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n10755,4,241,2017-05-08 06:31:04,http://bahringer.info/josianne,,145.164.54.26,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n10756,4,241,2017-04-12 15:36:04,http://dicki.info/lenora,,57.145.161.196,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n10757,4,241,2017-02-04 12:20:22,http://rempel.org/mae_parker,,88.202.208.153,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n10758,4,241,2017-05-23 21:18:18,http://schaden.co/katrine_zboncak,,176.3.192.62,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n10760,4,241,2017-01-30 09:11:11,http://vandervortlubowitz.name/don.larson,,119.82.45.120,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n10761,4,241,2017-02-08 02:23:13,http://stehrraynor.org/velma,,169.80.254.157,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n10762,4,241,2017-06-09 00:23:32,http://baumbach.io/garnett,,239.243.176.194,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n10763,4,241,2017-03-12 11:59:00,http://gorczany.io/madeline.olson,,124.245.233.192,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n10764,4,241,2017-04-02 09:42:38,http://spencer.info/dusty,,93.163.13.89,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n10765,4,241,2016-12-25 11:11:30,http://pfefferhowell.net/amina_willms,,116.67.33.224,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n10766,4,241,2017-01-30 09:21:06,http://hettinger.net/dixie_nader,,183.191.114.202,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n10767,4,241,2016-12-15 23:57:21,http://marks.co/alf_gulgowski,,89.13.50.41,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n10768,4,241,2017-03-12 08:53:35,http://ziemecorkery.io/crawford,,188.87.76.124,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n10769,4,241,2017-01-18 01:41:31,http://hilll.info/whitney_kris,,45.147.210.135,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n10770,4,241,2017-01-03 20:44:00,http://eichmannmann.org/kamren,,18.171.7.177,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n10771,4,241,2016-12-24 09:36:56,http://marvin.io/burley_wilkinson,,110.46.12.135,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n10772,4,241,2017-06-11 10:48:31,http://cartwright.net/michale_dach,,120.173.136.182,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n10773,4,241,2017-03-07 09:30:12,http://grady.co/narciso.stiedemann,,253.98.183.20,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n10774,4,241,2017-03-07 10:53:29,http://kub.org/nikko.nienow,,30.70.124.24,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n10775,4,241,2016-12-15 07:16:02,http://nikolaus.org/ardith,,105.230.179.76,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n7759,3,172,2016-12-24 05:59:48,http://millerziemann.com/janelle.schmidt,0.5758763139,42.205.125.91,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n7760,3,172,2017-05-16 16:57:34,http://welchreynolds.biz/tre.schneider,0.7518322846,95.9.247.29,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n7761,3,172,2017-01-21 09:11:52,http://carter.biz/shad.kemmer,0.4955654949,231.69.251.32,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n7762,3,172,2016-12-29 21:06:08,http://anderson.io/haylee.ferry,0.3888264939,52.54.176.52,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n7763,3,172,2017-01-13 13:02:49,http://hahnortiz.biz/shania_kris,0.8901944864,41.147.38.230,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n7764,3,172,2016-12-28 18:34:38,http://dickinson.info/willis_rodriguez,0.7382470133,119.174.170.123,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n7765,3,172,2017-04-18 05:35:25,http://mcclure.com/yvette,0.5087866791,238.133.170.132,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n7766,3,172,2017-05-09 13:09:32,http://hirtheprice.net/sofia.hackett,0.1417928001,135.59.145.90,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n7767,3,172,2017-03-07 18:26:11,http://huels.net/demarco.wiza,0.9916159953,221.190.13.38,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n7768,3,172,2016-12-20 23:19:04,http://larson.io/fern_runte,0.4479922119,190.46.90.86,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n7769,3,172,2017-05-13 01:44:07,http://mcculloughkoch.io/charley.thiel,0.6984428169,98.239.253.232,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n7770,3,172,2017-06-01 05:24:27,http://fadel.io/raleigh.jenkins,0.4123349670,111.169.188.245,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n7771,3,172,2017-05-15 07:48:37,http://rueckermacgyver.org/icie.kunze,0.4819117509,152.238.95.22,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n7772,3,172,2017-05-15 05:08:12,http://boehm.com/alanis,0.7340079976,185.6.240.58,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n7773,3,172,2017-05-22 20:32:21,http://brown.co/camila,0.0565760925,136.103.69.65,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n7774,3,172,2017-02-02 05:34:09,http://ruel.name/edgardo,0.0956523220,121.79.225.52,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n7775,3,172,2017-02-19 21:26:06,http://ankunding.biz/imani_kreiger,0.8953782780,115.44.57.101,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n7776,3,172,2017-01-05 00:50:31,http://morietterodriguez.name/dovie.steuber,0.9101376646,143.23.113.166,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n7777,3,172,2017-02-25 20:14:43,http://ritchiedeckow.org/laisha,0.6118238942,159.11.29.42,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n7778,3,172,2017-06-09 23:21:50,http://weberwolf.com/corine,0.9205468842,202.18.244.244,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n7779,3,172,2017-01-10 05:04:08,http://heathcote.com/maxine,0.1717773907,199.13.242.55,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n7780,3,172,2017-01-19 20:54:00,http://bogisich.net/tara_crona,0.6029252307,216.27.180.138,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n7781,3,172,2017-04-22 18:28:48,http://kilback.org/thurman,0.0642268519,12.28.194.120,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n7782,3,172,2017-03-11 01:45:43,http://langoshkunze.name/lenora,0.7746693127,136.3.64.145,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n7783,3,172,2017-03-31 07:48:10,http://lang.co/baylee_lehner,0.4785013558,97.206.141.105,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n7784,3,173,2017-04-12 20:26:03,http://schiller.name/macie_gibson,0.6091162429,37.215.249.234,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n7785,3,173,2017-03-12 12:49:46,http://erdman.co/caandra,0.3598751538,221.92.90.54,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n7786,3,173,2017-06-01 08:17:02,http://keebler.info/trea.white,0.5116434742,79.90.237.25,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n7787,3,173,2017-04-15 04:20:25,http://cormiergreen.biz/riley.keebler,0.4937220829,99.119.45.241,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n7788,3,173,2017-01-05 21:26:30,http://goodwin.biz/ferne.okuneva,0.2380092060,245.124.174.127,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n7789,3,173,2017-02-17 16:38:24,http://okeefe.org/dortha,0.5662159104,182.82.129.204,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n7790,3,173,2017-02-17 01:44:08,http://daniel.info/bud_ernser,0.8756343066,29.252.155.45,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n7791,3,173,2017-04-09 02:20:20,http://kuphal.biz/estel_ledner,0.3028546513,107.191.225.79,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n7792,3,173,2017-06-04 17:41:04,http://lemke.name/buford.will,0.9401803852,25.26.88.41,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n7793,3,173,2017-06-06 00:28:01,http://runolfon.net/giuseppe,0.6439888565,127.238.79.205,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n7794,3,173,2017-04-05 03:43:59,http://connelly.com/alda,0.2138651856,11.96.90.195,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n7795,3,173,2017-05-07 23:02:49,http://handhalvorson.name/dovie,0.3671982941,97.72.110.191,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n7796,3,173,2016-12-17 05:13:57,http://mohrfunk.biz/don,0.1072522227,195.76.66.65,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n7797,3,173,2017-04-07 04:52:36,http://schaden.com/leanne,0.4248062550,203.20.186.214,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n7798,3,173,2017-03-04 07:15:00,http://harrispollich.com/omer,0.2782420652,178.108.87.113,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n7799,3,173,2017-02-04 05:49:22,http://dibbert.name/adrien,0.8636266594,114.80.90.81,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n7800,3,173,2017-05-18 13:42:54,http://schowalter.io/eda,0.2671007991,240.48.138.131,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n7801,3,173,2017-03-12 08:03:58,http://mcdermottboyer.io/delphine,0.6620544501,225.14.93.40,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n7802,3,173,2017-06-10 02:31:41,http://krishansen.info/eugenia_goyette,0.3839357143,45.82.54.113,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n7803,3,173,2017-06-13 17:24:22,http://mann.org/rita,0.8376311053,89.91.183.159,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n7804,3,173,2017-04-02 15:50:25,http://keebler.net/eddie,0.3019285908,187.58.136.106,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n7805,3,173,2017-06-10 18:05:22,http://kertzmannhowell.io/kevin,0.0432030824,191.239.27.161,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n7806,3,173,2017-04-01 07:11:29,http://mcdermott.info/derrick.king,0.8398729680,109.183.131.254,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n7807,3,173,2017-04-11 00:08:53,http://jacobs.name/maude.buckridge,0.5571221681,126.136.40.134,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n7808,3,173,2017-06-04 01:23:06,http://ledner.io/margie_beatty,0.7499801952,129.121.218.177,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n7809,3,173,2017-04-24 10:40:53,http://bashirianschroeder.org/jo,0.2691613624,54.77.114.157,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n13671,5,308,2017-02-15 12:21:35,http://tillman.org/christian,,223.79.155.232,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n13672,5,308,2017-04-08 22:37:24,http://parisiangutmann.com/adah_buckridge,,248.19.20.36,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n13673,5,308,2017-06-02 02:39:52,http://johnspredovic.co/clinton,,223.165.46.77,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n13674,5,308,2017-02-17 11:13:53,http://ko.biz/lucius,,158.176.35.222,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n13675,5,308,2016-12-13 14:59:23,http://gislasonmedhurst.io/chris,,146.239.38.69,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n13676,5,308,2017-01-27 12:26:21,http://williamson.info/jo,,23.57.34.165,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n13677,5,308,2017-01-06 07:14:47,http://rempelbernhard.org/pinkie,,209.62.32.30,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n13678,5,308,2017-05-10 17:40:34,http://hyatt.com/dolores,,148.24.51.124,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n13679,5,308,2017-05-04 08:02:24,http://hamill.info/sam.reynolds,,204.29.74.7,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n13680,5,308,2016-12-29 19:55:30,http://kelermarvin.net/esteban,,204.204.35.248,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n13681,5,308,2017-03-14 14:06:33,http://osinskiwaelchi.org/marlen.beier,,180.83.128.230,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n13682,5,308,2017-05-03 12:51:43,http://bogan.co/hayden,,172.21.113.226,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n13683,5,308,2017-02-17 00:19:39,http://olsonoreilly.com/kailey,,65.130.190.58,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n13684,5,308,2017-02-05 04:15:21,http://lockman.org/rogelio.bailey,,16.234.19.115,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n13685,5,308,2017-02-11 09:15:42,http://hagenesgrant.org/teagan_mante,,244.212.102.140,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n13686,5,308,2016-12-16 11:42:24,http://shieldsfranecki.name/arden,,119.243.212.37,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n13687,5,308,2017-01-20 22:51:53,http://strackeherman.org/keshawn,,165.250.127.212,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n13688,5,308,2017-04-14 20:17:21,http://bayerbalistreri.org/ahmad,,225.100.252.128,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n13689,5,308,2017-03-16 02:26:53,http://cole.net/izabella.ernser,,55.104.141.157,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n13690,5,308,2017-04-11 16:21:10,http://dicki.net/kianna_gutmann,,79.158.6.201,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n13691,5,308,2017-05-06 03:07:46,http://baumbach.biz/alex_hoeger,,225.78.33.141,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n13692,5,308,2017-05-06 15:30:08,http://larkin.biz/kailyn_harber,,26.223.170.144,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n13693,5,308,2017-03-03 20:43:00,http://breitenberg.co/deshawn,,46.10.66.229,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n13694,5,308,2016-12-18 22:00:05,http://berge.io/samson,,14.178.171.106,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n13695,5,308,2017-03-31 04:44:58,http://ryan.io/dylan,,206.141.43.196,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n13696,5,308,2017-06-11 11:01:14,http://rosenbaum.name/brooklyn_hoppe,,227.248.110.53,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n13697,5,308,2016-12-25 16:53:19,http://shanahan.net/maximo.nienow,,171.200.229.132,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n13698,5,308,2016-12-14 03:17:45,http://rosenbaum.info/florian,,148.140.92.213,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n13699,5,308,2017-03-13 01:49:53,http://king.info/vanea_dickinson,,152.202.49.34,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n13700,5,308,2017-05-09 19:13:04,http://kuhn.org/jerrell.thompson,,68.185.2.101,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n13701,5,308,2017-02-28 07:35:59,http://abshireshanahan.io/conrad.kling,,220.114.164.237,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n13702,5,308,2016-12-22 17:40:19,http://handrobel.net/cordelia,,203.127.68.91,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n13703,5,308,2017-02-20 06:43:24,http://rogahnhaag.com/nicholas.beatty,,219.90.113.55,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n13704,5,308,2017-03-21 03:56:22,http://gulgowski.co/gerry,,233.157.79.53,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n13705,5,308,2017-04-01 19:01:13,http://nikolaus.co/mauricio,,154.176.160.176,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n13706,5,308,2017-05-03 09:45:13,http://hermann.org/rhoda,,65.231.223.68,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n13707,5,308,2017-01-19 02:37:24,http://dare.io/mikayla.kshlerin,,142.50.156.215,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n13708,5,308,2017-06-10 04:53:12,http://daviswatsica.name/saul.oconner,,71.240.100.89,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n13709,5,308,2017-01-30 01:59:48,http://gleichner.co/melvin,,92.25.233.164,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n13710,5,308,2017-05-23 19:52:21,http://ornkaulke.name/dee.white,,254.105.44.38,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n13711,5,308,2017-02-08 18:32:04,http://fayshields.com/lawson_ziemann,,43.164.190.254,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n13712,5,308,2017-01-10 07:10:17,http://littlegrimes.info/krystina.rutherford,,111.139.236.231,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n13713,5,308,2017-06-10 14:14:34,http://crookstremblay.info/laury_bauch,,226.145.123.169,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n13714,5,308,2017-05-25 16:27:55,http://marksdickinson.org/woodrow_zieme,,60.54.90.19,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n13715,5,308,2016-12-24 10:14:25,http://swaniawski.biz/paige_gislason,,29.227.13.109,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n13716,5,308,2016-12-23 20:32:40,http://ziemannboyer.co/alford,,184.234.75.195,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n13717,5,308,2017-03-23 20:40:17,http://nolan.com/heather,,161.3.123.178,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n13718,5,308,2017-05-19 12:56:10,http://haley.org/neoma.klocko,,110.19.164.243,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n13719,5,308,2017-01-12 06:46:11,http://kihn.net/madilyn_wiegand,,246.234.45.139,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n13720,5,308,2017-02-21 04:17:21,http://fahey.com/korey,,195.94.176.2,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n13721,5,309,2016-12-24 17:30:12,http://turcotte.name/tate_powlowski,,15.23.122.216,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n13722,5,309,2017-05-04 15:22:58,http://vandervortwolf.co/laurel,,61.215.132.118,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n13723,5,309,2017-05-01 03:13:07,http://raynorlowe.org/arlo.witting,,51.212.46.11,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n13724,5,309,2016-12-16 10:47:10,http://kaulke.net/vicenta.sauer,,170.216.74.133,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n13725,5,309,2017-01-23 16:04:40,http://okunevako.com/lon.yost,,66.120.220.210,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n16655,6,378,2017-06-07 06:16:21,http://stark.biz/jennyfer,,185.119.59.117,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n16656,6,378,2017-02-21 19:56:33,http://lockman.name/esmeralda_kuphal,,77.53.78.10,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n16657,6,378,2017-02-19 04:29:06,http://hackett.net/paxton.nitzsche,,79.231.15.168,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n16658,6,378,2017-02-23 16:38:54,http://wisozk.co/lelah,,3.92.168.138,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n16659,6,378,2017-03-27 18:21:01,http://darewuckert.io/llewellyn.mcclure,,39.38.89.147,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n16660,6,378,2017-05-01 04:16:24,http://christiansenreynolds.name/jody,,116.31.124.184,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n16661,6,378,2017-01-13 02:07:33,http://marks.info/ethan,,135.142.50.101,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n16662,6,378,2017-04-11 15:00:58,http://stammryan.io/henri,,227.228.230.46,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n16663,6,378,2017-02-09 07:55:49,http://schneider.org/erwin,,55.252.95.214,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n16664,6,378,2017-01-31 23:53:07,http://weimannborer.info/hardy_spencer,,238.154.195.248,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n16665,6,378,2017-01-30 11:43:57,http://thompson.org/beth.ernser,,205.162.28.167,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n16666,6,378,2016-12-19 17:10:38,http://jakubowski.io/janea.hills,,145.226.14.249,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n16667,6,378,2017-05-03 03:19:44,http://ward.org/gustave,,222.152.153.249,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n16668,6,378,2017-06-12 14:05:18,http://beier.biz/mariane,,196.180.19.159,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n16669,6,378,2017-03-18 09:43:43,http://hills.info/trisha,,88.150.157.61,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n16670,6,378,2017-05-14 10:14:35,http://cain.com/consuelo,,72.7.55.156,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n16671,6,378,2017-01-11 18:58:17,http://hartmannrempel.net/pinkie_prosacco,,225.148.181.2,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n16672,6,378,2017-05-01 02:20:19,http://tillman.net/jeff,,235.250.16.96,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n16673,6,378,2017-06-07 21:29:32,http://swaniawski.net/elena_gleichner,,46.128.67.203,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n16674,6,378,2017-05-07 15:01:10,http://upton.com/keyon,,128.218.66.233,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n16675,6,378,2017-04-17 14:50:44,http://ratke.net/alfonzo.kiehn,,97.85.138.229,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n16676,6,378,2017-04-23 01:30:57,http://armstrongmetz.name/hazle,,244.128.74.108,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n16677,6,378,2017-02-26 17:52:28,http://donnelly.biz/daniela,,250.100.210.111,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n16678,6,378,2017-03-29 19:15:17,http://lednerprosacco.com/libby,,226.147.134.134,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n16679,6,378,2017-01-20 15:14:44,http://leannon.biz/savanna.cole,,66.100.228.129,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n16680,6,378,2017-01-25 21:26:24,http://stokes.net/pink,,39.159.52.106,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n16681,6,378,2017-02-03 06:20:01,http://gibson.org/rubie,,131.129.66.170,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n16682,6,378,2017-03-06 19:48:54,http://dibbertnikolaus.com/jeremie.dubuque,,165.122.226.230,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n16683,6,378,2017-05-25 11:13:59,http://howe.org/julianne,,38.227.183.223,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n16684,6,378,2017-02-06 07:27:20,http://oberbrunner.io/kayla,,191.220.49.139,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n16685,6,378,2017-05-26 10:33:14,http://rutherfordchamplin.net/sylvan.nolan,,139.76.202.131,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n16686,6,378,2017-03-15 20:26:44,http://kaulke.biz/melia,,203.217.115.252,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n16687,6,378,2017-04-10 04:49:32,http://volkman.biz/myrtice.ferry,,253.120.147.129,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n16688,6,378,2017-05-10 02:35:43,http://pagac.io/burnice,,217.176.27.76,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n16689,6,378,2017-02-13 04:17:51,http://wolfflegros.com/natalie.ondricka,,91.239.5.247,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n16690,6,378,2017-04-28 00:03:14,http://sawaynwilkinson.com/trace,,164.242.212.159,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n16691,6,378,2017-05-13 12:19:48,http://osinski.biz/kris,,26.81.191.252,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n16692,6,378,2016-12-30 03:31:26,http://reichel.org/davion,,34.85.189.228,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n16693,6,378,2017-05-13 22:09:40,http://armstrongsmith.io/leatha_walsh,,119.144.200.146,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n16694,6,378,2016-12-17 03:08:12,http://schumm.net/miguel,,171.210.235.182,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n16695,6,378,2017-01-22 06:45:37,http://ritchieruecker.co/nat_mcglynn,,61.38.230.204,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n16696,6,378,2017-01-24 23:32:44,http://littlereynolds.info/briana,,165.152.107.186,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n16697,6,378,2017-06-02 08:43:16,http://oreilly.biz/lulu.skiles,,239.194.230.91,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n16698,6,378,2017-05-14 21:32:54,http://waelchi.com/jeika_rowe,,211.250.153.194,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n16699,6,378,2017-05-07 01:26:35,http://bernhardcrooks.com/linnea_cormier,,21.58.183.30,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n16700,6,378,2017-01-06 16:25:35,http://heaney.info/jordan,,83.176.119.99,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n16701,6,378,2017-02-20 06:25:32,http://balistreri.net/kenya_abbott,,98.59.45.14,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n16702,6,378,2017-04-05 01:41:05,http://jonesernser.biz/matteo.hauck,,26.183.163.117,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n16703,6,378,2017-03-18 20:02:07,http://sengerherman.biz/geovanni,,98.37.198.30,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n16704,6,378,2017-04-19 16:45:36,http://dach.net/laisha_bernier,,160.136.164.155,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n16705,6,378,2017-05-10 08:51:08,http://collins.io/piper.schultz,,6.96.160.164,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n16706,6,378,2017-02-06 03:31:50,http://walshvandervort.io/patience.gerhold,,174.127.213.100,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n16707,6,379,2017-06-09 08:17:48,http://ohara.co/hulda_heathcote,,51.20.83.73,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n16708,6,379,2016-12-16 13:22:15,http://goyette.io/allene,,93.6.178.119,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n16709,6,379,2017-06-13 22:36:01,http://fay.com/marguerite_barrows,,118.241.170.25,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n16710,6,379,2016-12-13 23:57:32,http://dickijones.net/reid,,252.161.103.86,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n3834,2,84,2017-02-22 03:04:18,http://coleziemann.info/efren_leuschke,0.5952692137,166.214.34.203,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n3835,2,84,2017-04-17 12:27:57,http://brakuslang.org/hector,0.0609658362,222.148.231.158,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n3836,2,84,2017-01-04 00:40:43,http://bednargulgowski.co/hunter,0.0918979353,76.198.117.136,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n3837,2,84,2017-06-07 20:41:08,http://feest.org/gertrude_kris,0.7863564891,7.15.107.191,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n3838,2,84,2017-01-02 08:47:20,http://kihn.com/vanea.wilderman,0.9080873176,58.190.103.240,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n3839,2,84,2017-05-25 21:07:39,http://oconner.io/elinor,0.1389007248,160.230.106.243,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n3840,2,84,2017-03-24 13:40:32,http://hauck.org/queenie_langosh,0.8452131340,50.166.209.233,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n3841,2,84,2017-01-29 14:24:33,http://ferry.name/ardith_schimmel,0.0496780388,96.104.241.149,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n3842,2,84,2017-01-18 14:47:11,http://wehnerko.name/lexi_hickle,0.9428979289,77.225.113.46,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n3843,2,84,2017-06-06 05:48:15,http://bradtke.org/emile,0.0338051448,191.45.74.237,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n3844,2,84,2017-04-13 20:39:09,http://littlehyatt.biz/rosemarie,0.3127367836,122.21.239.74,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n3845,2,84,2017-06-05 07:39:26,http://bartell.net/franz,0.8907152072,222.94.42.116,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n3846,2,84,2017-03-18 04:38:22,http://mcglynn.org/estrella.cummerata,0.0546735276,195.177.176.90,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n3847,2,84,2017-06-12 03:35:15,http://blanda.net/skye,0.8363039517,178.167.248.96,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n3848,2,84,2017-01-03 17:34:46,http://halvorsonhermiston.co/marlene,0.8330775186,147.89.37.40,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n3849,2,84,2017-03-01 09:23:14,http://harris.co/alfreda,0.9523583343,83.208.214.181,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n3850,2,84,2017-06-12 16:08:40,http://kovacek.info/jermey.larkin,0.6288034264,204.224.156.105,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n3851,2,84,2017-03-11 10:36:21,http://hahn.name/emilio,0.9029883644,62.221.145.130,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n3852,2,84,2017-03-18 14:07:36,http://huelbartell.net/audie,0.0098023043,29.37.216.138,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n3853,2,84,2016-12-25 08:46:37,http://schamberger.io/yesenia,0.8532166072,3.215.19.95,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n3854,2,84,2017-03-04 13:22:26,http://brekke.info/mohammed_gulgowski,0.1368232795,100.172.47.167,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n3855,2,84,2017-01-16 17:10:14,http://bosconitzsche.com/phyllis,0.0109074617,150.96.187.2,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n3856,2,84,2017-01-09 17:41:28,http://quigleykoch.name/wallace,0.9658007683,60.181.204.137,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n3857,2,84,2017-03-03 08:31:35,http://frami.name/etha,0.2908532885,146.68.15.242,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n3858,2,84,2017-03-16 12:17:32,http://lubowitzkris.org/wilton.reichert,0.3897538653,205.88.62.97,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n3859,2,84,2017-04-05 12:54:35,http://upton.name/kayleigh_herman,0.2035934388,149.80.149.221,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n3860,2,84,2017-04-01 23:30:13,http://leannon.io/nathen,0.1347799211,236.132.253.98,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n3861,2,84,2017-04-30 20:04:00,http://sengerconsidine.biz/giovanny.smith,0.8535512916,168.72.54.45,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n3862,2,84,2017-02-22 12:46:08,http://nolan.biz/adalberto,0.2871367699,213.81.95.172,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n3863,2,84,2017-03-03 15:45:12,http://walter.com/hettie,0.8775993932,38.65.86.212,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n3864,2,84,2017-06-13 10:02:13,http://littel.name/shayne,0.2570876400,90.76.205.173,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n3865,2,84,2017-06-10 18:34:48,http://hoppebarrows.io/agnes,0.0260311825,243.87.236.62,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n3866,2,84,2017-03-22 00:42:27,http://kutch.co/sanford,0.0518801126,32.88.11.246,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n3867,2,85,2017-01-12 19:53:45,http://bayer.com/antonio_champlin,0.4509257915,3.206.239.196,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n3868,2,85,2016-12-29 01:30:16,http://zboncak.net/precious,0.8903190243,200.250.191.104,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n3869,2,85,2017-04-05 21:49:36,http://bergnaum.com/omari.ritchie,0.0897596170,165.183.116.98,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n3870,2,85,2017-02-05 03:37:42,http://baileystamm.name/jayden.mann,0.2535172972,84.167.123.216,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n3871,2,85,2017-01-09 11:59:11,http://fadel.org/ibrahim.klein,0.0604008711,11.150.147.197,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n3872,2,85,2017-05-02 11:16:43,http://mohr.biz/ed,0.9041609089,206.97.48.217,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n3873,2,85,2017-04-16 01:45:13,http://spencer.io/aileen.ratke,0.1811435615,83.149.27.220,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n3874,2,85,2017-03-17 15:55:25,http://murray.net/harrison_hamill,0.0789080930,221.209.15.115,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n3875,2,85,2017-02-03 23:51:24,http://larsonrunolfon.info/germaine.auer,0.6277004745,120.23.102.74,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n3876,2,85,2017-05-16 15:08:51,http://abbottsawayn.co/chloe_bednar,0.0972372793,243.186.123.180,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n3877,2,85,2017-05-31 21:10:57,http://zemlakkshlerin.name/jazmyne,0.0259444961,107.41.113.247,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n3878,2,85,2017-01-09 08:19:07,http://leffler.name/willa.wilderman,0.3769691608,61.103.181.167,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n3879,2,85,2016-12-13 17:56:17,http://dickenshyatt.net/consuelo,0.6665946563,246.24.162.97,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n3880,2,85,2017-02-24 01:05:03,http://toy.io/charlene,0.9159693093,49.214.228.50,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n3881,2,85,2017-02-21 23:45:38,http://friesenstroman.name/kiarra_fisher,0.4004157319,48.220.178.219,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n3882,2,85,2017-01-11 23:20:59,http://jacobson.io/waylon_mayer,0.1073032887,154.245.83.100,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n3883,2,85,2017-04-09 03:14:02,http://gleichner.info/geovanni_dietrich,0.3509593339,19.87.198.44,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n3884,2,85,2017-05-02 02:17:19,http://jakubowski.org/marty.gibson,0.2413180487,233.203.106.135,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n10776,4,241,2017-03-11 16:17:07,http://sanford.info/petra.schaden,,168.22.243.125,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n10777,4,241,2017-03-09 19:30:25,http://gulgowski.io/ericka,,238.64.10.225,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n10778,4,241,2016-12-24 14:49:18,http://brekke.co/verner,,15.249.96.57,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n10779,4,241,2017-06-10 14:11:10,http://donnelly.com/tom_mertz,,21.95.180.211,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n10780,4,241,2017-02-14 20:38:57,http://balistreri.co/lafayette.collins,,55.115.20.60,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n10781,4,241,2017-04-18 16:20:53,http://brakushills.com/dax_smitham,,228.162.55.179,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n10782,4,241,2017-03-30 06:15:22,http://steuber.net/dorthy_kihn,,24.30.172.16,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n10783,4,241,2016-12-17 00:47:55,http://gutkowski.net/kurtis_hoppe,,111.3.136.185,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n10784,4,241,2017-02-05 10:42:57,http://ondrickadavis.co/ludwig_donnelly,,194.243.73.245,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n10785,4,241,2017-03-27 23:26:19,http://ruecker.biz/kianna.heel,,187.43.221.151,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n10786,4,241,2017-01-06 19:49:30,http://moriette.net/kitty,,199.23.183.201,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n10787,4,241,2017-01-11 00:26:13,http://rennerturner.name/earnest,,126.245.140.197,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n10788,4,241,2017-05-19 04:06:21,http://mclaughlinzieme.net/rosalinda,,177.240.87.224,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n10789,4,241,2017-03-15 12:20:19,http://beatty.io/gerson,,242.246.127.135,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n10790,4,241,2017-04-20 12:07:29,http://pricegoldner.com/fanny_jast,,101.92.123.52,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n10791,4,241,2017-03-11 08:06:12,http://abernathy.net/frederik_bogan,,89.110.220.154,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n10792,4,241,2017-01-16 13:08:16,http://williamson.co/evangeline,,223.34.84.244,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n10793,4,241,2017-05-03 02:25:30,http://rosenbaum.net/ulises,,52.90.40.196,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n10794,4,241,2017-02-28 21:14:39,http://mayertcorwin.name/jacklyn_windler,,7.132.42.172,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n10795,4,241,2017-01-04 05:20:26,http://strosin.biz/susan,,202.136.121.59,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n10796,4,241,2017-06-08 22:43:31,http://greenholt.com/rodrick_gerhold,,109.196.13.109,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n10797,4,241,2017-01-11 14:06:27,http://feest.com/ofelia,,66.10.94.107,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n10798,4,241,2017-03-13 11:31:08,http://pfannerstill.net/manuel,,60.104.50.163,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n10799,4,241,2017-01-16 05:28:37,http://hilpert.name/lesly_wunsch,,75.12.203.131,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n10800,4,241,2017-06-09 19:06:58,http://hackett.co/trace,,162.178.191.48,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n10801,4,241,2017-04-28 13:17:54,http://spencerbradtke.name/sandrine,,89.168.43.6,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n10802,4,241,2017-04-24 17:01:08,http://howellklocko.org/bryana,,132.165.74.139,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n10803,4,241,2017-06-01 05:59:14,http://zieme.io/kaylee.rempel,,151.219.191.13,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n10804,4,242,2017-03-23 08:39:58,http://schinner.co/florian,,13.148.154.114,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n10805,4,242,2017-02-25 17:55:29,http://heidenreich.biz/pamela,,144.243.165.110,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n10806,4,242,2017-04-15 18:04:12,http://smitham.biz/mallory_kulas,,221.179.80.172,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n10807,4,242,2017-03-14 01:42:55,http://sauer.net/laura_kling,,112.253.72.86,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n10808,4,242,2016-12-22 02:39:29,http://legros.biz/carolanne_boyle,,247.178.21.2,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n10809,4,242,2017-01-08 20:19:33,http://lindgrenkirlin.name/amina_orn,,187.32.84.33,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n10810,4,242,2016-12-24 11:03:22,http://kuhlmanmarks.info/virginia,,213.208.7.233,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n10811,4,242,2017-05-26 13:11:15,http://lynch.net/alvera,,144.188.14.171,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n10812,4,242,2017-03-08 15:18:42,http://ohara.net/nona,,240.34.215.186,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n10813,4,242,2017-03-02 02:50:41,http://friesen.net/zane,,217.96.206.155,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n10814,4,242,2017-02-22 20:40:16,http://hintz.io/dagmar,,92.12.223.39,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n10815,4,242,2017-03-17 21:04:19,http://turner.net/gregorio,,122.100.187.77,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n10816,4,242,2017-01-28 09:04:35,http://kubmann.biz/ruel,,91.133.22.118,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n10817,4,242,2016-12-21 17:12:41,http://cummingsmitchell.co/malachi_schultz,,192.213.246.234,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n10818,4,242,2017-06-07 18:55:23,http://ankunding.net/antonetta.jast,,10.48.144.243,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n10819,4,242,2017-03-03 17:00:03,http://moen.name/kory,,225.122.233.182,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n10820,4,242,2017-06-03 19:52:09,http://gottlieb.name/vita_labadie,,217.148.35.121,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n10821,4,242,2017-03-18 18:36:51,http://deckow.io/donnell_beatty,,192.124.232.229,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n10822,4,242,2016-12-22 07:05:27,http://quitzon.com/sarai,,242.2.216.29,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n10823,4,242,2017-03-12 01:41:45,http://zemlakhills.net/joel.kuhlman,,124.194.110.91,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n10824,4,242,2017-05-09 10:17:32,http://gusikowskischuppe.io/duane.will,,222.9.148.114,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n10825,4,242,2016-12-31 16:51:19,http://crist.biz/granville.bergstrom,,200.11.126.215,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n10826,4,242,2016-12-18 19:30:48,http://reingerboyle.org/heidi,,204.226.156.172,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n10827,4,242,2017-03-15 20:28:22,http://streich.biz/freddie_heaney,,115.173.224.88,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n10828,4,242,2017-01-02 09:37:18,http://reilly.org/william_witting,,220.245.139.52,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n10829,4,242,2017-02-26 20:06:47,http://robel.biz/alysha,,68.187.181.13,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n10830,4,242,2017-04-23 17:50:28,http://stantonkautzer.info/luciano,,110.75.206.221,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n10831,4,242,2017-03-18 16:23:20,http://kaulkebatz.com/taya,,71.193.235.239,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n7810,3,173,2017-04-30 08:50:49,http://kemmer.biz/brielle.wuckert,0.6554664378,203.11.233.16,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n7811,3,173,2017-04-19 00:24:44,http://davisgerlach.name/frederik.kemmer,0.4002814152,47.160.114.146,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n7812,3,173,2017-03-23 03:07:34,http://rosenbaumwest.io/pamela,0.5444390891,197.154.169.202,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n7813,3,173,2017-05-27 09:35:13,http://lindmccullough.name/dedric.welch,0.6264647403,152.232.213.197,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n7814,3,173,2017-04-02 15:17:28,http://rempelsporer.org/davion_johns,0.9711488311,51.251.80.251,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n7815,3,173,2017-02-22 23:03:19,http://ullrichschultz.name/lulu,0.6083168207,60.181.231.18,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n7816,3,173,2016-12-15 14:20:35,http://wuckert.com/bella_frami,0.1759115185,90.89.222.62,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n7817,3,173,2017-04-09 09:32:22,http://schroeder.net/elva,0.3615197798,175.80.158.228,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n7818,3,173,2016-12-15 00:18:52,http://schneider.name/sydni,0.7357132063,110.194.23.221,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n7819,3,173,2017-04-13 05:00:31,http://ohara.biz/destany,0.5305625345,25.203.160.53,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n7820,3,173,2017-05-21 11:54:33,http://stracke.biz/lauretta.braun,0.5866356307,250.47.188.9,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n7821,3,173,2017-02-20 22:05:02,http://waelchi.net/ladarius,0.0319090361,32.244.101.69,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n7822,3,173,2017-03-24 20:31:47,http://vonrueden.net/wanda_herman,0.1650674686,128.135.161.15,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n7823,3,173,2017-04-26 16:44:53,http://gorczany.com/dario_waelchi,0.8345326160,235.233.248.70,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n7824,3,173,2017-05-30 02:39:14,http://predovic.io/colleen_rodriguez,0.4009573320,9.164.226.90,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n7825,3,173,2016-12-17 00:15:12,http://armstrong.co/erich,0.2320792064,155.53.254.126,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n7826,3,173,2017-05-03 20:31:03,http://brown.net/jaunita,0.2399424689,151.148.237.37,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n7827,3,173,2017-01-10 03:41:19,http://baumbach.org/guy,0.4346414049,223.33.22.35,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n7828,3,173,2017-03-17 00:25:42,http://vandervortgorczany.com/wilma,0.7570314042,48.227.90.218,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n7829,3,173,2017-01-29 17:42:39,http://ullrich.io/raphael,0.3605711494,191.83.226.228,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n7830,3,173,2017-06-03 05:50:00,http://treutel.info/zachery,0.5195696527,217.198.96.247,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n7831,3,173,2017-05-25 09:46:43,http://fayhickle.com/jamil.harvey,0.7769349310,105.145.145.179,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n7832,3,173,2017-03-15 08:17:43,http://labadiewalter.name/dorothea_heaney,0.7675033131,69.220.175.116,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n7833,3,173,2017-06-06 23:09:24,http://langworth.com/joaquin_mohr,0.4412650839,250.155.251.46,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n7834,3,173,2017-06-10 08:56:52,http://hand.co/keely_marvin,0.9706078542,28.245.31.159,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n7835,3,173,2017-01-22 23:05:10,http://ryan.com/jadon,0.5721037849,134.35.23.56,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n7836,3,173,2017-02-14 19:45:35,http://morar.io/nayeli,0.4236049669,203.13.245.221,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n7837,3,173,2016-12-23 19:25:44,http://gibson.org/lenore,0.5715791549,144.175.99.125,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n7838,3,173,2017-03-21 23:18:54,http://prosacco.io/jerod.reynolds,0.1434894918,252.169.88.99,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n7839,3,173,2017-03-06 06:23:33,http://erdmanschmidt.name/elsie.kilback,0.2500313649,67.154.187.100,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n7840,3,173,2017-04-30 01:18:01,http://breitenbergreynolds.com/eldora,0.2742778727,40.230.209.226,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n7841,3,173,2017-05-09 23:43:54,http://johnsonsauer.io/jaylen,0.8250444788,245.29.141.60,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n7842,3,173,2017-02-14 07:27:00,http://marvin.com/holly,0.0951856308,69.23.233.126,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n7843,3,173,2017-01-07 21:57:46,http://millskunze.com/cristian,0.5453320169,103.68.43.173,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n7844,3,173,2017-04-10 05:58:38,http://herzogshanahan.net/dudley,0.3071566885,107.70.154.162,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n7845,3,173,2017-03-25 00:36:03,http://barrows.org/amya.berge,0.5022741081,44.44.246.201,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n7846,3,173,2017-01-09 23:34:54,http://hilll.net/ezra,0.3445473513,97.54.30.217,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n7847,3,173,2016-12-25 03:22:26,http://gusikowski.co/lila,0.5884212918,59.24.69.74,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n7848,3,173,2016-12-27 10:37:39,http://kuhlmanerdman.net/betsy_littel,0.9792943798,93.21.246.209,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n7849,3,173,2017-03-31 13:11:24,http://berniergerhold.name/lauryn.langosh,0.6094809820,37.232.225.5,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n7850,3,173,2017-05-10 00:23:00,http://oharacrooks.com/magdalena,0.9342530156,179.136.250.175,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n7851,3,173,2017-05-21 16:45:02,http://murphy.com/morgan.jenkins,0.4212109045,154.182.54.151,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n7852,3,173,2017-01-28 17:03:05,http://bailey.com/brionna_king,0.5380773224,10.183.3.29,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n7853,3,174,2017-01-25 00:29:15,http://kertzmann.com/jaida,0.5045877755,174.227.96.58,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n7854,3,174,2017-02-21 21:44:51,http://botsford.com/peyton,0.5989844639,212.3.24.80,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n7855,3,174,2017-05-06 18:45:53,http://stamm.biz/mya_dietrich,0.6828866631,217.150.243.20,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n7856,3,174,2017-04-04 01:20:17,http://okeefecasper.info/nels_schoen,0.6233104010,204.249.122.201,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n7857,3,174,2017-02-26 08:55:31,http://konopelski.net/eldred_krajcik,0.6051368373,241.36.174.109,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n7858,3,174,2017-04-19 13:24:04,http://gottlieb.name/neva,0.9741511839,192.207.208.175,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n7859,3,174,2017-05-22 10:46:03,http://murazik.org/davonte,0.5356288784,89.159.134.56,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n7860,3,174,2017-01-18 09:46:55,http://medhurst.io/vladimir,0.2812396216,217.94.171.156,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n7861,3,174,2017-04-13 05:51:35,http://watsica.name/kiera_prohaska,0.5287688641,100.137.113.49,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n13726,5,309,2017-05-26 22:09:22,http://hermiston.com/colten,,199.88.58.78,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n13727,5,309,2017-06-09 09:57:53,http://parisianoreilly.name/leanne.gottlieb,,209.137.196.114,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n13728,5,309,2017-03-02 14:26:52,http://gaylordgoyette.biz/nicklaus,,46.84.147.73,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n13729,5,309,2017-06-10 02:05:52,http://johns.co/waylon,,173.237.32.71,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n13730,5,309,2017-03-31 12:58:40,http://beckerhodkiewicz.net/myrtis_quitzon,,169.242.191.144,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n13731,5,309,2017-03-15 19:01:07,http://christiansen.name/kaelyn.konopelski,,116.30.254.25,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n13732,5,309,2016-12-18 23:28:02,http://block.io/edwin,,115.196.244.77,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n13733,5,309,2017-04-12 12:26:27,http://bergnaum.biz/nikita_schmeler,,218.157.134.210,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n13734,5,309,2017-04-09 16:47:51,http://mckenzie.com/jordyn,,53.180.198.38,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n13735,5,309,2017-03-05 04:43:06,http://treutelspencer.io/beaulah.harris,,232.16.175.77,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n13736,5,309,2017-03-06 07:00:24,http://ward.co/macie_renner,,235.104.251.148,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n13737,5,309,2017-05-20 03:39:14,http://stokes.co/eula_waelchi,,134.135.39.123,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n13738,5,309,2017-05-14 14:14:59,http://wuckert.com/leonor,,149.78.179.5,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n13739,5,309,2017-04-16 09:44:23,http://gutkowskikris.name/isaias,,134.95.181.138,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n13740,5,309,2017-02-06 10:34:22,http://grant.co/darlene,,48.186.114.168,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n13741,5,309,2017-01-03 06:09:24,http://jacobson.biz/chester.weimann,,247.127.167.181,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n13742,5,309,2017-02-28 06:32:07,http://hansen.net/sid_stiedemann,,189.112.117.97,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n13743,5,309,2017-02-06 14:07:52,http://balistreri.info/august.bruen,,130.97.93.115,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n13744,5,309,2017-05-13 19:54:53,http://kaulkeboehm.name/shannon,,22.239.160.88,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n13745,5,309,2017-02-28 02:46:15,http://fahey.com/davion,,44.250.137.156,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n13746,5,309,2017-05-30 06:26:02,http://denesik.name/caandre,,16.29.182.205,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n13747,5,309,2017-01-08 01:57:37,http://pfannerstill.com/aurelio_schulist,,205.161.81.175,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n13748,5,309,2017-01-18 23:58:22,http://marquardt.name/tyrel_bogan,,226.235.5.199,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n13749,5,309,2017-02-14 21:19:51,http://hackettblanda.info/hilario_rempel,,245.199.43.23,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n13750,5,309,2017-02-10 19:59:34,http://donnellylebsack.io/alberto,,7.2.97.100,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n13751,5,309,2017-06-10 14:04:22,http://parker.net/sarai,,122.71.148.235,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n13752,5,309,2017-03-12 00:27:50,http://schinner.org/jonathan.baumbach,,155.47.71.240,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n13753,5,309,2017-01-18 08:29:15,http://hansenkaulke.biz/shayne,,72.84.217.93,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n13754,5,309,2017-02-05 02:42:07,http://casperbins.net/garth,,36.33.86.217,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n13755,5,309,2016-12-25 21:54:58,http://huelshills.com/mattie_kunde,,134.45.138.21,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n13756,5,309,2017-01-07 04:42:10,http://schoen.net/bartholome.leffler,,108.39.237.111,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n13757,5,309,2017-03-14 05:25:06,http://kohler.biz/elna,,251.22.69.96,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n13758,5,309,2016-12-22 06:07:04,http://walshlindgren.org/ambrose.erdman,,120.229.173.149,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n13759,5,309,2017-05-12 11:26:47,http://hand.co/wayne,,221.235.140.114,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n13760,5,309,2017-01-25 05:15:08,http://rodriguez.io/macy.white,,150.53.77.137,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n13761,5,309,2017-01-22 01:19:59,http://goyette.com/arvel.tremblay,,13.62.126.175,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n13762,5,309,2016-12-26 03:00:31,http://kub.name/dennis,,126.97.74.68,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n13763,5,309,2017-05-14 15:49:13,http://larson.com/kirk_lehner,,163.165.182.23,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n13764,5,309,2017-01-19 02:28:53,http://hettinger.name/vincenzo,,127.77.245.8,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n13765,5,309,2017-01-06 07:01:33,http://blickkutch.name/odell,,2.252.216.46,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n13766,5,309,2017-02-16 07:26:09,http://howell.net/virgil.kutch,,120.151.59.215,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n13767,5,309,2017-02-15 19:46:30,http://kerluke.co/toy,,254.103.120.151,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n13768,5,309,2017-05-19 11:49:04,http://kuvalis.io/lauriane.gottlieb,,220.219.252.196,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n13769,5,309,2017-03-01 05:32:06,http://reynoldsebert.io/zachariah,,210.249.183.58,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n13770,5,309,2017-02-22 12:10:15,http://muller.biz/josiane,,180.150.46.228,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n13771,5,309,2016-12-26 17:40:25,http://nikolauslynch.name/eryn,,185.26.74.220,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n13772,5,309,2017-03-28 01:55:56,http://schuster.name/caie,,32.223.176.82,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n13773,5,309,2017-04-04 06:24:01,http://schmidtkoepp.io/ania,,111.157.230.206,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n13774,5,309,2017-04-21 10:17:46,http://hirtheconnelly.info/cordell,,21.230.65.134,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n13775,5,309,2017-05-11 13:35:09,http://ondricka.net/kristofer_nolan,,150.144.35.195,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n13776,5,310,2017-04-03 15:13:39,http://kirlin.io/frankie.bode,,48.41.127.223,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n13777,5,310,2017-03-07 01:28:07,http://runte.name/kylie_kunde,,46.152.183.34,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n13778,5,310,2017-05-28 11:06:48,http://von.io/alta.hammes,,152.185.65.72,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n13779,5,310,2017-06-07 10:29:05,http://hintz.net/golden_mohr,,4.22.215.107,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n13780,5,310,2017-05-26 16:52:23,http://halvorson.info/karlee.kris,,76.167.156.65,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n16711,6,379,2017-01-04 04:05:32,http://mrazbernhard.info/addie.dicki,,193.74.59.60,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n16712,6,379,2017-01-18 02:06:14,http://mohrmiller.net/wilhelmine.durgan,,88.46.43.163,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n16713,6,379,2017-05-02 11:05:22,http://lowemaggio.io/edna,,82.73.174.87,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n16714,6,379,2017-04-13 07:48:16,http://strosintowne.org/kirk.gerhold,,76.163.216.3,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n16715,6,379,2017-02-23 16:33:20,http://yost.org/arne_lynch,,63.77.93.46,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n16716,6,379,2017-04-09 23:45:56,http://streichgoyette.com/dusty.runolfsdottir,,135.41.170.2,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n16717,6,379,2017-03-09 14:07:29,http://damore.name/eve,,170.26.74.163,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n16718,6,379,2017-02-16 03:33:47,http://homenick.com/jacquelyn,,20.161.191.193,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n16719,6,379,2017-02-03 01:12:07,http://mclaughlinratke.name/cameron_effertz,,193.169.41.213,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n16720,6,379,2017-03-15 07:56:57,http://runte.com/hertha,,81.159.9.49,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n16721,6,379,2017-05-07 18:45:47,http://kunzebradtke.net/natalie.predovic,,94.242.199.27,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n16722,6,379,2017-04-10 10:36:01,http://gaylordjones.biz/maxine,,121.198.196.112,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n16723,6,379,2017-06-03 20:34:43,http://kaulkekuhic.net/myah,,163.67.118.207,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n16724,6,379,2017-05-15 07:00:04,http://parker.net/erica.haag,,56.212.56.23,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n16725,6,379,2017-01-11 08:32:55,http://orn.name/lacey,,18.5.228.92,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n16726,6,379,2017-01-30 12:45:17,http://hintz.info/van,,163.205.63.239,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n16727,6,379,2016-12-22 08:31:09,http://koelpinkirlin.io/ryley_altenwerth,,104.151.128.82,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n16728,6,379,2017-01-02 01:34:06,http://grant.name/florida.kertzmann,,73.240.182.46,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n16729,6,379,2017-06-08 08:41:41,http://frami.io/lexie,,19.196.24.156,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n16730,6,379,2017-04-23 17:43:26,http://larsonswaniawski.org/clair_mcclure,,85.188.33.178,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n16731,6,379,2017-02-20 08:04:29,http://deckowkertzmann.info/okey_huels,,101.129.143.169,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n16732,6,379,2017-01-24 04:42:57,http://roobgrimes.biz/dolores,,180.196.118.15,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n16733,6,379,2016-12-27 03:12:46,http://corkery.biz/kenneth.schmeler,,145.226.130.133,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n16734,6,379,2017-03-12 10:11:34,http://donnellytreutel.info/prince,,236.254.110.209,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n16735,6,379,2017-05-18 21:10:21,http://schinner.org/ronny.hackett,,89.192.216.14,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n16736,6,379,2017-01-22 00:49:39,http://fay.name/sebastian,,125.156.192.214,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n16737,6,379,2017-05-26 11:15:34,http://lehner.biz/leopold,,106.47.203.2,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n16738,6,379,2017-01-10 10:42:38,http://wintheiser.com/hilda_stroman,,193.197.141.165,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n16739,6,379,2017-05-02 06:22:28,http://feil.net/brendan_halvorson,,21.245.191.164,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n16740,6,379,2017-02-16 23:23:09,http://schowalter.name/jacinto_dare,,129.102.188.231,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n16741,6,379,2017-01-22 15:16:59,http://luettgenzemlak.co/perry.mccullough,,108.147.134.145,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n16742,6,379,2017-06-03 13:47:21,http://hamill.name/vella,,102.214.68.89,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n16743,6,379,2016-12-27 15:07:56,http://cain.info/bruce_terry,,108.208.35.34,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n16744,6,379,2017-03-16 02:26:06,http://stoltenberg.com/madonna,,79.191.16.63,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n16745,6,379,2016-12-30 02:03:38,http://huel.biz/vladimir_dietrich,,150.27.112.175,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n16746,6,379,2017-01-02 09:07:41,http://reinger.org/camryn,,113.86.136.248,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n16747,6,379,2016-12-21 14:52:13,http://hackett.net/consuelo_haley,,202.141.228.78,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n16748,6,379,2017-02-04 18:56:09,http://shields.co/eric.runte,,171.47.14.181,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n16749,6,379,2017-01-23 17:48:44,http://balistrericain.net/chelsie,,104.190.247.163,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n16750,6,379,2017-05-21 11:21:41,http://koeppwiza.com/kendall.torphy,,199.164.237.173,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n16751,6,379,2017-05-01 16:57:37,http://hartmannvandervort.io/carolyne_paucek,,17.173.113.218,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n16752,6,379,2017-04-07 02:59:38,http://mertz.info/grayce.ledner,,28.70.104.167,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n16753,6,379,2017-02-10 02:36:58,http://volkman.info/virginie.mclaughlin,,72.24.13.215,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n16754,6,379,2017-05-12 22:34:32,http://block.biz/justine.west,,171.126.139.159,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n16755,6,379,2017-03-06 20:39:28,http://weimannmacejkovic.name/sarai_cain,,136.104.82.93,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n16756,6,379,2017-05-28 08:15:11,http://kerlukesanford.info/alanna,,7.151.24.67,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n16757,6,379,2017-03-15 17:56:29,http://glover.name/king,,139.193.137.23,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n16758,6,379,2016-12-28 13:03:39,http://pfannerstill.net/maye,,143.98.150.69,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n16759,6,379,2017-01-31 16:31:06,http://streichryan.net/brent_brakus,,217.47.241.62,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n16760,6,380,2017-06-02 16:46:43,http://hilpert.biz/walter_schamberger,,40.179.48.138,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n16761,6,380,2017-01-23 01:00:06,http://terrykunde.biz/annalise,,107.235.57.144,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n16762,6,380,2017-02-06 15:41:19,http://luettgen.info/chelsey.ledner,,59.198.139.140,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n16763,6,380,2017-03-17 03:01:23,http://dareziemann.org/kenya.thompson,,62.2.185.202,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n16764,6,380,2017-03-11 19:00:16,http://batz.io/shanie.hoeger,,15.249.75.71,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n3885,2,85,2017-01-15 13:44:33,http://swaniawskischuster.info/jee.adams,0.5837225407,12.140.162.216,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n3886,2,85,2017-03-20 11:29:04,http://murphy.org/benedict.stroman,0.1641855878,74.82.132.170,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n3887,2,85,2017-03-19 22:25:51,http://handsanford.biz/mariela.bins,0.9454433415,93.231.202.131,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n3888,2,85,2017-02-04 02:15:15,http://hermankovacek.info/heaven,0.4433155936,56.66.21.247,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n3889,2,85,2017-02-04 10:55:04,http://greenfelder.biz/marilou,0.7655142543,214.6.214.59,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n3890,2,85,2017-02-17 17:39:37,http://macejkovic.net/burnice.rice,0.4680082249,52.74.185.124,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n3891,2,85,2017-03-08 04:11:43,http://johnson.name/julio,0.1214440570,220.77.183.36,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n3892,2,86,2017-05-30 04:07:01,http://kuhn.net/zula_smitham,,149.127.137.197,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n3893,2,86,2017-02-21 12:29:39,http://predovicmurray.org/rylan.rolfson,,86.63.237.103,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n3894,2,86,2017-02-27 04:13:17,http://millskautzer.biz/jolie_parker,,130.18.212.56,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n3895,2,86,2017-02-03 17:48:40,http://sanford.com/coralie,,254.144.81.128,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n3896,2,86,2017-06-08 11:12:19,http://mayer.name/dianna,,10.159.57.209,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n3897,2,86,2017-05-29 23:45:57,http://millerjakubowski.com/lesly.walsh,,206.155.181.227,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n3898,2,86,2017-01-02 15:13:33,http://baumbach.com/jennifer,,180.214.204.12,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n3899,2,86,2017-01-31 20:39:20,http://jenkins.io/deie_wuckert,,181.136.101.166,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n3900,2,86,2017-04-26 11:51:30,http://connelly.co/chaya.hauck,,40.65.62.242,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n3901,2,86,2017-02-12 21:50:54,http://hills.info/krystal.hamill,,84.19.249.202,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n3902,2,86,2017-04-26 19:25:34,http://stokes.name/tod,,189.193.170.194,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n3903,2,86,2017-06-10 08:30:30,http://bednarpacocha.biz/rahsaan.rodriguez,,50.117.196.84,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n3904,2,86,2017-03-28 12:06:53,http://howellkonopelski.net/maurine,,90.22.175.21,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n3905,2,86,2017-04-17 15:48:41,http://emmerich.biz/jeika_flatley,,236.165.74.220,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n3906,2,86,2016-12-23 21:08:40,http://dooley.io/tiara_jaskolski,,116.62.56.243,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n3907,2,86,2017-04-22 00:27:27,http://balistreri.io/gina,,196.16.28.18,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n3908,2,86,2017-01-01 10:45:55,http://gleichnerhowe.io/mallie,,122.41.193.69,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n3909,2,86,2017-03-17 17:40:21,http://botsfordgorczany.com/arlo,,34.122.77.249,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n3910,2,86,2017-02-09 01:20:33,http://wiegand.name/kristoffer,,227.147.37.40,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n3911,2,86,2017-03-23 11:32:37,http://keeling.net/domingo.white,,196.8.150.24,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n3912,2,86,2017-01-02 19:27:56,http://emmerichlehner.com/isabell,,133.184.201.182,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n3913,2,86,2017-06-03 17:02:24,http://grant.name/marlen,,123.66.122.150,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n3914,2,86,2017-03-08 08:55:10,http://boyle.info/sydnie_schinner,,142.142.194.200,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n3915,2,86,2017-02-26 02:28:28,http://donnellykulas.name/theo,,248.69.127.31,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n3916,2,86,2017-03-17 11:24:18,http://johns.co/mozelle_emmerich,,55.217.114.183,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n3917,2,86,2017-03-13 06:09:01,http://gerlach.net/moses_green,,82.245.199.148,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n3918,2,86,2017-01-01 03:54:35,http://hodkiewiczgorczany.io/tristin.emard,,141.169.193.160,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n3919,2,86,2016-12-22 20:22:48,http://braungutmann.com/mariano,,195.209.223.133,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n3920,2,86,2017-02-27 02:02:27,http://howegreen.name/beth,,199.18.213.11,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n3921,2,86,2016-12-14 17:08:22,http://stoltenberggleichner.com/reed,,12.71.225.33,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n3922,2,86,2017-05-30 23:19:20,http://schuster.name/alf,,198.121.124.151,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n3923,2,86,2017-06-02 17:18:02,http://ebert.net/eunice_sporer,,228.24.106.219,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n3924,2,86,2017-02-10 10:39:40,http://hills.name/bettie.hagenes,,28.71.88.77,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n3925,2,86,2017-04-28 19:51:09,http://mayer.info/kane,,80.198.145.118,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n3926,2,86,2016-12-20 09:26:04,http://boehm.co/lyla.dickens,,139.123.63.109,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n3927,2,86,2017-05-19 15:46:40,http://pollich.io/mariano_lemke,,63.178.153.23,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n3928,2,86,2017-06-14 02:40:47,http://roberts.info/morgan_emard,,184.120.220.219,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n3929,2,86,2017-06-13 04:00:51,http://connelly.com/retha,,177.91.253.152,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n3930,2,86,2017-05-16 18:47:46,http://lefflerschaefer.info/kyleigh.sanford,,184.208.101.97,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n3931,2,86,2017-05-02 22:48:02,http://stroman.info/sunny_torp,,94.162.40.103,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n3932,2,86,2017-04-01 02:36:16,http://sawayn.name/samson_bernier,,8.108.212.142,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n3933,2,86,2017-03-15 22:02:52,http://hilll.info/demetris,,169.73.106.180,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n3934,2,86,2017-01-03 19:34:41,http://stroman.io/edwin_bogan,,37.75.10.8,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n3935,2,86,2016-12-14 00:29:28,http://wunsch.org/manuela_franecki,,32.64.187.252,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n3936,2,86,2017-01-22 08:24:06,http://larkinblock.com/fay,,253.77.104.48,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n3937,2,86,2017-05-11 02:56:41,http://fisher.info/amelia,,235.168.113.227,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n3938,2,86,2017-02-15 21:45:31,http://leffler.name/damaris.weimann,,174.210.226.121,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n3939,2,86,2017-02-07 18:03:36,http://wintheiser.info/roselyn.emmerich,,67.109.69.133,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n3940,2,86,2017-03-27 23:03:27,http://doyle.io/jensen_schimmel,,173.212.122.73,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n10832,4,242,2017-05-02 07:46:28,http://sipesryan.co/mortimer.klocko,,165.154.119.74,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n10833,4,242,2017-03-16 21:37:35,http://cristreinger.com/norris_gulgowski,,200.86.75.29,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n10834,4,242,2017-01-31 12:50:56,http://schiller.co/buddy,,138.4.85.237,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n10835,4,242,2016-12-17 16:01:56,http://greenfelderbarton.biz/camren.wintheiser,,37.211.161.67,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n10836,4,242,2017-06-08 01:44:43,http://wisoky.name/deshaun.jones,,124.231.249.197,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n10837,4,242,2017-01-23 14:06:46,http://collins.name/chester,,177.240.248.243,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n10838,4,242,2017-01-29 19:22:56,http://kemmer.name/dandre,,204.69.46.138,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n10839,4,242,2017-01-07 07:32:57,http://turnermcdermott.com/rafaela.thiel,,144.141.213.141,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n10840,4,242,2017-02-20 19:38:49,http://marquardtfadel.net/lance.hodkiewicz,,138.175.254.207,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n10841,4,242,2017-02-16 06:09:47,http://gusikowskifeil.org/lilian_zieme,,106.186.5.106,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n10842,4,242,2017-02-28 11:22:40,http://lockmanmoen.biz/maurice,,148.201.68.103,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n10843,4,242,2017-05-20 20:57:02,http://rowenicolas.io/jena_herzog,,74.102.174.118,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n10844,4,242,2017-06-08 22:04:25,http://lakin.info/sam,,78.63.238.183,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n10845,4,242,2017-01-06 10:40:29,http://kaulkekris.info/axel_wolff,,108.92.185.91,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n10846,4,242,2017-01-18 05:31:51,http://okeefemaggio.com/madaline.hintz,,72.134.178.132,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n10847,4,242,2017-05-01 22:57:51,http://aufderhar.com/gilbert_goldner,,189.81.147.78,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n10848,4,242,2017-03-04 02:11:48,http://mayer.co/carmel_littel,,230.188.100.36,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n10849,4,242,2017-02-09 19:30:55,http://dibbert.biz/adriel_skiles,,114.91.91.118,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n10850,4,242,2017-06-09 18:44:40,http://effertzlegros.info/ashlee_pouros,,26.9.69.8,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n10851,4,242,2017-05-01 19:29:17,http://jenkinsaltenwerth.com/murl.olson,,206.199.17.178,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n10852,4,242,2017-03-16 10:13:50,http://manterunte.io/leonie,,165.170.225.4,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n10853,4,242,2017-05-12 14:35:06,http://heaney.biz/ena,,125.149.171.213,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n10854,4,242,2017-02-27 05:13:02,http://bruenruecker.net/jabari,,165.197.186.196,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n10855,4,242,2017-04-17 20:29:37,http://shanahanlarson.org/roosevelt.langworth,,82.145.177.227,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n10856,4,242,2017-02-06 22:42:11,http://swaniawski.info/ed,,220.31.18.130,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n10857,4,242,2016-12-15 11:41:59,http://heidenreichbartoletti.info/viva,,43.69.7.73,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n10858,4,242,2017-03-28 22:51:23,http://dicki.com/samson.abernathy,,223.22.182.16,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n10859,4,243,2017-04-10 22:40:03,http://macgyverkovacek.net/brown,,133.135.175.185,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n10860,4,243,2017-03-23 05:43:21,http://schadenstamm.io/juanita,,4.129.251.240,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n10861,4,243,2017-01-26 15:23:40,http://mcdermottbotsford.biz/rickie.oreilly,,240.153.202.130,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n10862,4,243,2017-02-12 19:12:19,http://romaguera.net/lenora_krajcik,,91.58.92.252,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n10863,4,243,2017-01-05 11:49:04,http://stehrhaley.io/reggie,,29.168.68.167,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n10864,4,243,2017-03-29 08:21:43,http://veum.org/vicenta,,64.206.94.126,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n10865,4,243,2017-03-02 02:09:00,http://mohrbecker.com/taryn_leuschke,,188.252.19.78,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n10866,4,243,2017-06-01 02:12:09,http://ebert.net/johnnie,,115.239.254.59,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n10867,4,243,2017-01-31 08:14:11,http://farrell.biz/marquis.mckenzie,,127.224.129.237,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n10868,4,243,2017-05-11 23:42:45,http://ruel.io/molly.block,,198.177.190.44,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n10869,4,243,2017-03-19 03:22:36,http://weimann.co/eileen_feil,,243.199.124.241,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n10870,4,243,2016-12-23 07:08:58,http://robertshuels.com/stuart,,218.253.92.200,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n10871,4,243,2017-01-28 00:19:23,http://dietrich.io/alejandra_reichel,,66.166.34.156,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n10872,4,243,2017-02-24 18:47:29,http://schimmel.info/austyn,,210.225.221.75,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n10873,4,243,2017-02-26 05:03:27,http://millerrogahn.name/orland,,2.77.250.43,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n10874,4,243,2017-02-18 01:58:53,http://ratkedurgan.com/rubye,,39.76.194.52,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n10875,4,243,2017-04-21 01:01:51,http://moen.net/karen.dibbert,,15.59.233.16,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n10876,4,243,2017-04-02 18:23:42,http://jaskolski.info/oda.keler,,117.236.60.114,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n10877,4,243,2016-12-23 04:21:50,http://jastgrimes.name/steve,,229.94.227.171,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n10878,4,243,2017-06-11 16:58:44,http://kub.co/jace,,10.249.220.31,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n10879,4,243,2017-05-09 22:34:57,http://denesikwalsh.net/orie,,241.140.57.90,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n10880,4,243,2017-01-24 12:14:05,http://ledner.io/marley.eichmann,,239.80.82.52,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n10881,4,243,2017-06-01 14:28:55,http://howell.info/kaitlin.mclaughlin,,218.185.106.178,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n10882,4,243,2017-01-30 15:39:48,http://block.biz/ella.kertzmann,,14.90.66.224,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n10883,4,243,2017-03-11 14:53:37,http://spinka.info/daphnee.bauch,,50.178.140.233,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n10884,4,243,2017-04-22 10:20:58,http://wolf.name/emmalee,,150.216.10.213,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n10885,4,243,2016-12-27 09:55:28,http://breitenbergflatley.io/aric_olson,,112.232.27.191,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n7862,3,174,2017-04-22 18:35:35,http://littel.com/reinhold,0.9166469728,57.22.38.55,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n7863,3,174,2017-06-05 01:53:52,http://hamill.io/destin,0.5139975903,206.98.217.31,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n7864,3,174,2017-05-13 00:33:09,http://bechtelar.biz/armand,0.4308718317,52.192.215.239,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n7865,3,174,2017-01-16 22:16:39,http://botsfordboehm.net/sylvan,0.7321272876,20.30.121.71,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n7866,3,174,2017-02-09 23:59:39,http://trantow.biz/kylie,0.8402582197,114.31.219.127,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n7867,3,174,2017-04-24 10:07:29,http://pagac.co/blanca,0.1732603353,25.119.69.229,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n7868,3,174,2017-06-04 05:13:47,http://dare.io/wade_conn,0.7658330798,191.117.74.198,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n7869,3,174,2017-04-07 12:04:54,http://moriette.com/moses,0.9340995287,167.173.60.84,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n7870,3,174,2017-03-24 15:08:33,http://lakin.io/brent,0.1908519741,224.247.115.57,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n7871,3,174,2017-05-10 04:39:13,http://mcculloughgerhold.info/iliana.becker,0.9521869380,33.44.42.254,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n7872,3,174,2016-12-31 00:40:09,http://dietrich.info/moie,0.0605965968,206.27.161.125,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n7873,3,174,2017-03-13 02:09:50,http://crookskunze.org/rebeka_rippin,0.9697851546,129.46.156.83,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n7874,3,174,2017-03-18 07:10:50,http://oconnellgusikowski.io/brenden.weinat,0.6075556957,72.6.100.45,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n7875,3,174,2017-01-04 20:14:06,http://simonis.net/andreanne_waelchi,0.1809189700,125.65.185.210,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n7876,3,174,2017-03-17 19:57:39,http://fritsch.biz/hudson_mosciski,0.7601568441,76.223.23.60,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n7877,3,174,2017-06-05 17:42:32,http://barrowchneider.io/granville_auer,0.4149225709,36.92.82.160,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n7878,3,174,2017-04-17 20:44:43,http://stehrmckenzie.co/liliane.vandervort,0.7229513675,72.237.185.132,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n7879,3,174,2017-02-15 02:56:22,http://weinat.co/donny,0.6759521361,39.240.20.41,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n7880,3,174,2017-01-04 13:10:59,http://larsonhills.name/zoey,0.5781600811,231.219.100.128,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n7881,3,174,2017-04-12 18:01:31,http://cain.co/arjun,0.8445436672,11.199.147.102,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n7882,3,174,2017-01-17 10:00:28,http://olson.org/casandra_mueller,0.5241645541,185.6.81.18,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n7883,3,174,2017-03-02 08:11:13,http://reinger.org/abigale,0.2680718869,51.84.12.35,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n7884,3,174,2017-05-16 19:56:15,http://lynch.co/eryn,0.3442996792,153.190.139.140,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n7885,3,174,2017-06-01 03:21:30,http://johns.name/jayden,0.6608596201,151.3.164.170,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n7886,3,174,2017-05-22 06:13:17,http://gulgowski.biz/kaci,0.9235670678,149.102.34.127,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n7887,3,174,2017-02-10 07:59:28,http://keler.org/jazmyn.reynolds,0.5639187562,238.102.115.248,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n7888,3,174,2017-03-09 12:57:38,http://barton.name/mattie_maggio,0.9321865052,92.69.212.216,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n7889,3,174,2017-01-06 23:51:00,http://sporer.info/easter_olson,0.9161850057,228.240.3.142,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n7890,3,174,2017-04-22 21:31:07,http://yost.org/ericka.mayert,0.8149270274,93.97.116.144,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n7891,3,174,2017-03-17 16:02:12,http://yundtvolkman.com/amiya,0.3288158562,129.123.94.198,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n7892,3,174,2017-02-08 00:21:57,http://padbergdavis.biz/durward.schroeder,0.9779064640,132.17.122.242,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n7893,3,174,2017-06-10 22:25:53,http://bechtelar.co/johnson_haley,0.8828865510,125.87.4.2,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n7894,3,174,2017-05-21 11:21:18,http://jacobszboncak.com/luisa_cummerata,0.8589780302,210.20.59.96,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n7895,3,174,2017-02-26 20:15:42,http://borerkulas.name/durward.greenholt,0.6696621739,58.222.99.153,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n7896,3,174,2017-04-02 04:02:16,http://greenholtkeebler.net/william,0.2188867782,49.48.81.211,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n7897,3,174,2017-06-07 20:42:10,http://bernhard.net/beatrice_hahn,0.7196042458,36.98.105.9,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n7898,3,174,2017-05-23 18:35:31,http://gutmann.com/hazel,0.7550118031,200.241.99.129,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n7899,3,174,2017-05-24 21:49:11,http://braun.info/ruth.windler,0.1194146827,63.97.152.157,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n7900,3,174,2017-02-20 00:48:51,http://schmidthudson.biz/wyman.heaney,0.7563418325,197.141.244.39,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n7901,3,174,2017-01-21 09:31:10,http://leschleuschke.net/jeika.torp,0.1410776356,152.127.24.184,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n7902,3,174,2017-01-29 03:51:16,http://bauchanderson.io/alexane.jast,0.0836223956,31.74.177.59,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n7903,3,174,2017-05-17 05:26:17,http://prohaska.net/aylin,0.2025571370,202.253.165.209,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n7904,3,174,2017-02-21 06:28:03,http://vandervort.org/alanis,0.8775173629,166.17.171.249,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n7905,3,174,2017-02-13 00:36:11,http://beattygreen.org/christina,0.9563345548,102.165.93.56,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n7906,3,174,2017-06-06 18:03:56,http://swift.name/marcel.reichert,0.2314760320,145.216.242.12,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n7907,3,174,2017-04-30 22:06:40,http://leuschkewitting.com/michael.mayert,0.9766764608,33.6.160.254,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n7908,3,174,2016-12-15 00:15:24,http://cruickshank.biz/flo_hane,0.7444315740,22.198.131.82,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n7909,3,174,2017-01-10 11:03:11,http://williamson.io/velva,0.5210392107,150.106.166.66,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n7910,3,174,2017-02-14 22:51:23,http://powlowskikihn.net/nola,0.2023901062,194.242.117.85,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n7911,3,174,2017-05-23 09:00:34,http://rohan.name/mayra_dubuque,0.2161409555,80.52.237.78,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n7912,3,175,2017-04-20 02:00:36,http://medhurst.com/iliana,0.9698854914,50.139.184.84,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n13781,5,310,2017-03-17 20:55:10,http://stracke.name/elenor_corwin,,99.106.25.162,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n13782,5,310,2016-12-18 16:28:40,http://hettinger.biz/vaughn,,208.73.247.220,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n13783,5,310,2016-12-16 22:29:35,http://harveytrantow.org/ricardo.batz,,227.213.134.43,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n13784,5,310,2017-02-19 17:53:08,http://bernhard.net/daisha.leannon,,166.20.145.10,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n13785,5,310,2017-02-04 23:09:18,http://harristhiel.io/pansy,,252.101.56.170,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n13786,5,310,2017-03-26 03:01:48,http://smith.io/jeika,,135.31.161.32,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n13787,5,310,2017-02-13 11:22:55,http://kaulkegreen.net/rosendo,,217.93.35.231,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n13788,5,310,2017-01-20 10:49:06,http://thiel.biz/armani,,156.217.245.187,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n13789,5,310,2017-03-31 03:08:57,http://yundt.io/daphney,,153.79.56.48,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n13790,5,310,2016-12-29 19:16:40,http://daremurray.io/hector,,79.35.93.135,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n13791,5,310,2017-03-01 22:10:06,http://senger.co/myles,,11.161.42.201,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n13792,5,310,2017-01-13 12:54:18,http://handjaskolski.net/ignacio_cremin,,42.155.137.230,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n13793,5,310,2017-05-22 05:56:13,http://bosco.net/tomas_kuvalis,,62.225.131.172,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n13794,5,310,2017-02-01 23:06:55,http://simonishartmann.name/marques_pfeffer,,107.165.97.64,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n13795,5,310,2017-04-11 15:35:58,http://wuckert.name/grover,,30.52.32.175,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n13796,5,310,2016-12-23 06:34:17,http://feil.co/vivianne_murray,,27.203.185.207,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n13797,5,310,2017-03-27 18:13:07,http://macejkovic.biz/ashton,,227.101.43.131,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n13798,5,310,2017-03-02 16:07:46,http://murazik.com/arielle.welch,,27.74.84.75,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n13799,5,310,2017-05-28 00:14:59,http://larson.info/judd,,229.71.177.61,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n13800,5,310,2017-03-08 14:08:15,http://stanton.io/dayna,,113.151.61.191,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n13801,5,310,2017-01-24 01:59:38,http://stokechuppe.io/anthony,,31.113.176.92,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n13802,5,310,2016-12-13 06:45:23,http://reichel.co/jane,,190.143.92.236,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n13803,5,310,2017-02-19 03:32:30,http://ortiz.org/jamal,,30.51.233.210,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n13804,5,310,2017-04-01 11:17:16,http://oconnell.io/meagan,,168.135.195.8,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n13805,5,310,2017-06-13 17:32:12,http://thompson.net/kathryn_leannon,,45.31.8.15,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n13806,5,310,2017-02-10 16:12:02,http://jacobs.net/nora.weimann,,133.234.249.184,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n13807,5,310,2017-01-12 20:35:54,http://gislasonjohnson.info/stephen.beer,,45.56.150.52,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n13808,5,310,2016-12-18 08:12:58,http://okeefe.org/malinda.torphy,,91.5.242.158,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n13809,5,310,2016-12-22 18:42:44,http://oberbrunnerbayer.com/maxine,,183.109.147.219,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n13810,5,310,2017-06-13 15:06:36,http://welch.com/izabella_ferry,,220.78.217.47,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n13811,5,310,2017-06-13 17:56:54,http://leannon.info/giovani,,69.201.100.81,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n13812,5,310,2016-12-31 08:30:12,http://barton.name/christelle_gutkowski,,33.223.11.250,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n13813,5,310,2017-02-27 17:36:07,http://lindtorp.co/deja,,42.247.206.5,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n13814,5,310,2017-03-06 18:16:52,http://ohara.biz/tatyana,,13.230.151.60,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n13815,5,310,2017-01-05 19:35:13,http://heaneytremblay.name/koby_macejkovic,,52.169.55.212,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n13816,5,310,2017-01-02 20:14:36,http://pouros.net/ladarius,,117.233.240.135,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n13817,5,310,2017-04-10 07:10:35,http://johnsonschuster.name/layne.nikolaus,,136.22.110.68,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n13818,5,310,2017-06-08 01:02:28,http://hammes.info/willy.gislason,,141.142.252.93,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n13819,5,310,2016-12-21 14:02:05,http://heel.net/regan,,243.51.90.144,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n13820,5,310,2017-03-26 22:34:15,http://hermistonrobel.net/claudine.weinat,,185.228.193.157,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n13821,5,310,2017-01-10 08:15:13,http://marvin.biz/virgie,,207.14.146.142,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n13822,5,310,2017-01-09 20:38:28,http://gaylordschuster.biz/wilmer,,104.191.182.169,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n13823,5,310,2017-06-08 17:51:45,http://reichert.io/eleanora,,135.187.203.38,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n13824,5,310,2017-01-23 07:47:58,http://feeneycrist.info/monserrate.kemmer,,57.106.184.8,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n13825,5,310,2017-05-31 04:36:25,http://senger.io/birdie_schmeler,,181.170.134.26,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n13826,5,310,2017-01-26 04:16:19,http://rosenbaum.name/concepcion_gislason,,168.148.74.253,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n13827,5,310,2017-04-24 03:53:58,http://hicklegoodwin.io/anderson_cummerata,,9.116.5.205,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n13828,5,310,2017-06-11 20:04:57,http://hegmann.info/newton_reynolds,,209.220.26.251,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n13829,5,310,2016-12-25 14:16:15,http://powlowski.biz/emelia.koelpin,,80.44.152.62,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n13830,5,310,2017-01-17 16:58:46,http://moen.io/dorris,,197.113.253.165,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n13831,5,310,2016-12-28 20:08:31,http://mclaughlin.net/bettie,,148.6.188.228,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n13832,5,310,2017-04-18 13:14:32,http://daugherty.biz/marshall,,9.145.53.59,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n13833,5,310,2017-05-23 15:10:18,http://harveysmitham.org/maeve.heel,,147.10.64.238,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n13834,5,310,2017-03-11 12:31:59,http://hilll.net/peyton,,102.219.180.75,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n13835,5,310,2017-05-26 19:29:25,http://klocko.com/ashley.padberg,,191.234.224.32,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n16765,6,380,2017-03-01 20:48:00,http://mitchell.name/lelia.thiel,,142.43.245.150,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n16766,6,380,2017-06-11 05:13:15,http://herzog.net/nayeli,,207.207.157.229,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n16767,6,380,2017-04-16 10:46:24,http://dicki.org/jayda.fisher,,36.69.59.76,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n16768,6,380,2017-03-26 07:25:49,http://fisherkuhlman.co/eleazar.rohan,,71.63.233.177,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n16769,6,380,2017-02-20 19:38:53,http://rice.net/golda_klein,,214.49.204.120,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n16770,6,380,2016-12-17 07:23:15,http://gottlieb.co/alfred,,100.69.12.69,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n16771,6,380,2017-04-20 01:08:03,http://hackettmuller.org/francisca.mraz,,87.234.227.103,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n16772,6,380,2017-02-02 20:02:35,http://kulas.org/garnet_toy,,73.137.177.250,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n16773,6,380,2017-04-02 20:06:04,http://hilll.net/dante,,145.4.254.99,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n16774,6,380,2017-04-01 11:48:55,http://ohara.com/nicolette.beer,,133.74.230.134,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n16775,6,380,2017-02-18 07:49:10,http://hegmann.name/nels.ritchie,,238.198.118.16,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n16776,6,380,2017-04-16 02:59:06,http://goldner.com/janea.botsford,,48.132.39.166,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n16777,6,380,2017-05-01 17:16:13,http://denesik.io/luella_koepp,,165.173.131.204,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n16778,6,380,2017-02-01 01:32:59,http://balistreri.org/taurean,,129.132.40.152,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n16779,6,380,2017-03-10 02:51:41,http://graham.name/theresa,,162.125.42.177,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n16780,6,380,2017-05-16 12:51:58,http://waters.net/jammie.shields,,12.22.82.105,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n16781,6,380,2017-03-17 03:53:07,http://kunde.io/freida,,142.100.100.125,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n16782,6,380,2017-01-21 14:35:12,http://denesik.io/mellie_abshire,,115.192.30.111,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n16783,6,380,2016-12-26 14:41:58,http://wisoky.name/may.prosacco,,18.91.129.204,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n16784,6,380,2016-12-25 04:32:06,http://reingerbalistreri.net/madge,,123.150.112.102,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n16785,6,380,2017-04-20 09:50:12,http://jerdefay.io/fredrick_prosacco,,168.23.2.26,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n17073,6,386,2017-03-03 00:56:19,http://roberts.org/te,,9.248.14.79,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n16786,6,380,2017-02-15 15:54:10,http://kuhlman.co/hanna_romaguera,,203.215.193.171,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n16787,6,380,2017-03-08 04:45:59,http://harrisgrady.info/montana_pagac,,78.176.22.238,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n16788,6,380,2017-04-12 17:16:11,http://glover.io/pattie,,91.48.9.167,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n16789,6,380,2017-02-27 07:09:44,http://raynorweimann.co/rasheed,,87.119.42.111,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n16790,6,380,2017-03-13 03:46:19,http://adamsmoriette.net/ignacio_abshire,,51.85.223.41,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n16791,6,380,2017-06-09 13:31:09,http://schimmel.name/waino.grady,,4.169.135.232,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n16792,6,380,2017-03-05 09:20:43,http://stromanwalter.info/rolando,,131.177.37.136,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n16793,6,380,2017-03-05 20:49:20,http://monahanaufderhar.net/ana.feeney,,183.135.184.72,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n16794,6,380,2017-04-29 03:00:06,http://zboncak.net/vinnie_stoltenberg,,170.164.228.186,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n16795,6,380,2017-04-06 16:59:33,http://sawayn.info/madelyn.metz,,101.162.98.37,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n16796,6,380,2017-03-22 16:37:23,http://stiedemann.name/magali.effertz,,88.240.243.222,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n16797,6,380,2017-01-29 04:14:28,http://danielstiedemann.info/parker.schimmel,,53.78.87.171,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n16798,6,380,2017-01-15 03:33:49,http://darechamplin.com/verner,,100.52.56.252,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n16799,6,380,2017-06-06 19:10:03,http://donnellyoberbrunner.biz/rudy.wiza,,91.220.75.99,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n16800,6,380,2016-12-27 13:16:54,http://sawaynbahringer.org/donald_ortiz,,27.197.133.3,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n16801,6,380,2017-05-10 12:52:39,http://kiehnwelch.name/samir,,170.179.9.21,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n16802,6,380,2017-03-11 13:15:07,http://towneschimmel.io/maryjane_littel,,189.31.231.213,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n16803,6,380,2017-05-06 23:19:17,http://white.io/jaquan.anderson,,161.36.66.187,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n16804,6,380,2017-06-01 22:01:10,http://hahn.name/sidney_zieme,,151.99.126.86,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n16805,6,380,2017-06-10 15:57:10,http://bogisich.io/laurel_kerluke,,116.193.243.128,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n16806,6,380,2017-05-23 22:31:45,http://hegmann.name/xavier,,25.212.195.241,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n16807,6,381,2017-01-07 01:53:55,http://kulasparker.org/linda.okon,,228.209.168.208,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n16808,6,381,2016-12-15 06:43:58,http://stanton.co/dedrick.feeney,,33.222.79.128,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n16809,6,381,2016-12-14 01:16:55,http://rennerankunding.info/duncan,,56.141.152.134,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n16810,6,381,2017-03-31 06:53:31,http://kaulke.net/dillan,,139.122.93.161,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n16811,6,381,2017-04-22 02:48:06,http://hamilldouglas.name/rhianna,,177.132.180.80,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n16812,6,381,2017-04-29 11:08:03,http://keler.io/tomas.schuppe,,4.42.93.217,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n16813,6,381,2017-02-04 21:10:46,http://lueilwitz.name/virginie,,170.183.60.26,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n16814,6,381,2017-02-13 18:40:41,http://gutmann.io/julianne,,93.182.149.77,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n16815,6,381,2016-12-26 10:33:29,http://schultzreilly.biz/allie,,192.236.49.80,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n16816,6,381,2017-05-01 01:49:01,http://stroman.info/annalise,,239.201.224.26,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n16817,6,381,2017-04-15 04:48:09,http://tremblaygaylord.io/randal_stoltenberg,,3.92.37.111,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n16818,6,381,2017-01-04 10:00:21,http://hartmann.biz/katharina,,7.170.141.152,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n3941,2,87,2017-01-12 09:02:41,http://littel.name/fabiola.mcclure,,113.149.212.41,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n3942,2,87,2017-04-09 15:55:30,http://hoeger.biz/ruth.torp,,166.190.229.63,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n3943,2,87,2017-02-13 22:26:20,http://marvin.com/ken,,153.117.184.67,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n3944,2,87,2017-04-01 09:03:12,http://hilll.name/montana_cole,,116.111.186.184,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n3945,2,87,2017-05-12 06:21:37,http://hermiston.net/mozell,,118.30.111.171,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n3947,2,87,2017-02-24 17:51:54,http://herzog.com/roger.nader,,254.168.211.56,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n3948,2,87,2017-03-13 08:20:04,http://gloverjaskolski.biz/fidel.kulas,,30.200.85.179,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n3949,2,87,2017-04-11 18:30:43,http://gerlach.name/rebeka,,24.211.147.247,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n3950,2,87,2016-12-20 07:32:54,http://breitenberg.name/felicia.littel,,41.238.159.226,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n3951,2,87,2016-12-20 06:20:37,http://moorelowe.biz/julianne,,10.139.233.123,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n3952,2,87,2017-05-31 11:20:33,http://dach.name/cornelius.price,,148.167.42.17,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n3953,2,87,2017-03-08 17:57:24,http://goyette.com/eduardo,,74.157.5.121,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n3954,2,87,2017-01-18 07:10:37,http://farrell.org/rosalind,,98.212.105.119,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n3955,2,87,2017-04-28 09:04:38,http://cristroberts.net/dannie,,143.17.83.27,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n3956,2,87,2017-03-27 04:35:04,http://crooks.io/nicolas.hackett,,151.253.107.219,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n3957,2,87,2017-04-14 12:15:05,http://schaefer.name/lora,,199.100.125.208,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n3958,2,87,2017-06-07 00:57:31,http://bernieremmerich.biz/ben.barrows,,48.27.147.177,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n3959,2,87,2017-02-13 03:04:23,http://mclaughlingislason.co/wiley_nitzsche,,106.50.238.72,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n3960,2,87,2017-01-15 03:50:37,http://gibsonschaefer.biz/montana.gleason,,47.54.102.214,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n3961,2,87,2017-03-07 21:29:08,http://brekkethiel.co/paula.hayes,,54.20.54.36,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n3962,2,87,2017-01-06 21:33:18,http://botsford.info/rolando_lakin,,104.47.214.103,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n3963,2,87,2017-05-16 19:11:30,http://okeefe.co/emilie,,203.239.102.226,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n3964,2,87,2017-05-27 11:55:59,http://emarddurgan.co/edgardo.haley,,224.227.110.58,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n3965,2,87,2017-01-17 13:03:41,http://konopelskikiehn.org/dangelo_gutmann,,175.159.231.51,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n3966,2,87,2017-01-22 20:05:38,http://beckerlang.net/dashawn,,151.114.201.55,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n3967,2,87,2017-04-21 01:58:56,http://gislason.info/deon_kshlerin,,211.241.95.191,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n3968,2,87,2017-02-17 07:27:45,http://hegmannhegmann.info/dane,,211.252.119.217,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n3969,2,87,2016-12-19 23:56:36,http://schillerernser.com/nikki,,202.217.95.193,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n3970,2,87,2017-01-06 08:40:40,http://medhurst.biz/ryley.keeling,,123.218.150.224,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n3971,2,87,2016-12-23 01:58:13,http://pollichharvey.info/monique.lebsack,,233.143.177.76,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n3972,2,87,2016-12-29 12:33:07,http://crooks.org/maud.rogahn,,211.157.203.31,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n3973,2,87,2017-03-21 18:05:10,http://riceraynor.net/lenny.smitham,,130.66.186.222,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n3974,2,87,2016-12-19 11:29:18,http://keelingreynolds.co/mireille,,136.114.15.201,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n3975,2,87,2017-06-10 03:02:34,http://donnellymedhurst.biz/francesco.beer,,116.232.169.162,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n3976,2,87,2017-02-02 05:50:54,http://terry.com/finn,,179.85.80.137,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n3977,2,87,2017-05-16 09:31:58,http://ryan.org/clementina,,251.12.91.85,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n3978,2,87,2017-01-30 05:56:46,http://reinger.org/carlo,,92.136.199.109,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n3979,2,87,2017-06-08 21:14:01,http://gleason.org/judy,,181.193.143.200,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n3980,2,87,2017-03-29 11:37:32,http://schultz.org/roy.borer,,39.196.224.172,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n3981,2,87,2017-02-25 07:16:19,http://sawayn.net/kenny.hamill,,98.61.112.116,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n3982,2,87,2017-02-07 20:17:41,http://swift.net/lucile,,3.70.186.178,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n3983,2,87,2017-02-03 04:54:09,http://pfannerstillreichel.io/camryn,,186.14.109.21,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n3984,2,87,2016-12-16 13:52:21,http://bechtelarwest.io/addison.armstrong,,21.13.55.80,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n3985,2,87,2017-01-10 05:07:06,http://naderrosenbaum.net/gina_schmitt,,223.177.161.49,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n3986,2,87,2017-02-10 14:26:27,http://cronapacocha.io/eldora,,251.170.246.50,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n3987,2,87,2017-01-26 03:39:29,http://oconnelltrantow.name/vinnie,,168.37.120.85,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n3988,2,87,2017-02-02 18:17:06,http://hermistondonnelly.biz/stephen.konopelski,,22.238.207.105,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n3989,2,87,2017-05-05 22:24:34,http://kling.co/presley,,159.197.245.204,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n3990,2,87,2017-05-18 20:45:00,http://hansen.org/cierra,,109.194.115.30,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n3991,2,87,2017-06-12 08:23:34,http://durgan.name/jaycee,,33.60.96.138,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n3992,2,87,2017-03-22 19:45:28,http://padbergankunding.com/beryl_mcglynn,,102.252.253.174,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n3993,2,87,2017-06-03 01:10:06,http://ullrich.io/trevor,,2.71.124.234,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n3994,2,87,2017-05-07 03:07:13,http://gottliebsteuber.io/eric_bode,,71.155.69.201,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n3995,2,87,2017-04-09 10:04:49,http://vonschoen.io/alvis,,139.25.38.160,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n3996,2,87,2017-03-13 16:11:11,http://doyleruel.info/kacie,,238.183.14.155,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n10886,4,243,2017-03-06 05:44:35,http://dachstanton.net/joy,,177.243.25.147,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n10887,4,243,2017-02-14 09:09:51,http://ornmoen.com/maximilian,,223.142.233.14,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n10888,4,243,2017-03-03 01:19:11,http://jacobson.co/maryjane,,53.154.63.124,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n10889,4,243,2017-03-23 14:39:16,http://hopperunolfon.com/zander,,230.105.148.155,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n10890,4,243,2017-01-03 03:24:21,http://williamson.org/valentin,,193.231.143.170,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n10891,4,243,2017-03-20 11:27:36,http://kaulkeflatley.co/tristian,,196.61.70.213,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n10892,4,243,2017-04-13 06:06:25,http://zulaufsanford.io/cali.osinski,,41.164.16.231,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n10893,4,243,2017-02-14 12:34:36,http://ondrickadibbert.biz/birdie,,247.48.231.215,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n10894,4,243,2017-04-03 08:34:17,http://kuhlman.co/giovani.berge,,47.232.140.124,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n10895,4,243,2017-03-30 17:27:41,http://kautzer.biz/jacynthe_barrows,,33.135.206.204,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n10896,4,243,2017-03-24 01:28:12,http://senger.net/casimir,,179.249.170.158,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n10897,4,243,2017-04-28 04:31:07,http://witting.io/nora,,145.191.40.22,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n10898,4,243,2017-04-08 12:21:15,http://schmelerbauch.org/abe,,119.37.208.223,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n10899,4,243,2017-03-17 09:04:22,http://mohr.name/jamison,,124.199.26.247,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n10900,4,243,2017-05-23 08:43:19,http://oreilly.co/delores_kunze,,28.56.119.28,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n10901,4,243,2017-01-01 16:06:21,http://herzog.com/demarcus,,6.58.10.144,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n10902,4,243,2017-01-31 10:58:48,http://hammes.co/yasmine.rowe,,106.151.70.136,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n10903,4,243,2017-04-01 22:57:24,http://weinatstreich.biz/tremayne,,121.217.127.79,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n10904,4,243,2016-12-28 05:48:32,http://brakus.info/howard,,45.232.111.81,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n10905,4,243,2017-04-29 13:35:56,http://lockman.info/jennings,,104.178.196.99,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n10906,4,243,2017-03-14 14:17:32,http://hamill.org/elya_emmerich,,28.116.35.66,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n10907,4,244,2017-01-05 22:32:19,http://beer.co/oda,,174.173.37.48,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n10908,4,244,2016-12-16 04:56:46,http://mann.co/earnest,,51.223.198.254,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n10909,4,244,2017-03-31 10:49:58,http://hoppe.biz/paris,,219.181.81.173,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n10910,4,244,2017-01-07 18:48:25,http://oreillycole.io/glennie,,133.60.160.163,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n10911,4,244,2017-05-29 06:01:54,http://lindgren.biz/will,,11.251.82.73,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n10912,4,244,2017-01-24 10:03:41,http://spencerullrich.net/leif_schuppe,,147.95.188.205,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n10913,4,244,2017-04-06 07:54:42,http://heathcoteward.io/geraldine.prohaska,,149.128.45.97,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n10914,4,244,2017-04-08 16:03:30,http://vandervort.info/alva,,51.122.14.128,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n10915,4,244,2017-02-19 05:37:26,http://boehm.io/korbin_barton,,158.229.76.223,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n10916,4,244,2017-01-12 21:58:23,http://pfannerstillhammes.biz/melisa_terry,,105.205.60.166,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n10917,4,244,2017-05-18 14:33:41,http://heathcote.biz/keven.flatley,,247.12.221.27,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n10918,4,244,2017-01-05 02:14:45,http://dachrodriguez.info/sierra,,249.67.31.233,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n10919,4,244,2017-06-13 10:56:58,http://sawayn.name/gerda_langworth,,47.41.168.16,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n10920,4,244,2017-04-06 14:57:24,http://wymantorp.info/aniyah.wyman,,197.71.117.83,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n10921,4,244,2017-01-26 18:53:36,http://considinebailey.net/edgar,,150.192.203.42,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n10922,4,244,2017-04-25 02:56:28,http://berge.co/alan,,154.73.60.143,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n10923,4,244,2017-04-16 23:29:33,http://mante.name/denis.beer,,8.15.8.95,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n10924,4,244,2017-03-13 00:27:11,http://ankunding.net/kirk,,223.66.18.111,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n10925,4,244,2017-06-03 10:23:49,http://swaniawski.co/quinton,,93.72.41.104,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n10926,4,244,2016-12-18 14:33:31,http://dachzieme.org/madaline,,44.54.6.83,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n10927,4,244,2017-05-21 20:43:30,http://rutherford.co/claudia_renner,,98.147.61.116,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n10928,4,244,2017-03-30 20:28:14,http://kshlerinhammes.biz/richmond_auer,,190.19.237.229,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n10929,4,244,2017-06-03 00:44:20,http://kovacekprohaska.net/janie_kub,,39.183.146.107,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n10930,4,244,2017-02-25 01:49:28,http://mertzhermann.biz/kody,,33.102.225.62,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n10931,4,244,2017-05-07 21:38:35,http://cartwrightveum.info/lilly,,162.118.194.66,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n10932,4,244,2016-12-27 08:15:42,http://mcdermott.co/sincere_barrows,,146.38.188.55,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n10933,4,244,2017-02-25 06:56:40,http://leffler.org/cedrick,,167.65.152.20,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n10934,4,244,2017-01-29 08:48:12,http://lueilwitzwest.net/mariela_zemlak,,138.249.106.216,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n10935,4,244,2017-02-25 23:29:04,http://schmidt.org/durward.mcclure,,182.34.249.82,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n10936,4,244,2017-06-08 00:06:23,http://hirthebins.co/maud,,61.254.30.159,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n10937,4,244,2017-02-21 15:29:43,http://daremetz.co/eino.kunze,,7.111.143.241,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n10938,4,244,2017-05-21 13:26:11,http://vonkling.info/johnny,,206.123.245.180,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n10939,4,244,2017-05-24 21:31:24,http://olson.co/clementina,,79.222.214.242,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n10940,4,244,2017-02-24 00:43:59,http://gerlach.biz/genevieve,,117.66.211.5,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n10941,4,245,2016-12-16 22:30:59,http://howe.com/delphine,,193.108.58.69,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n7913,3,175,2017-04-30 17:15:51,http://torp.io/mozelle,0.3218307734,226.234.218.148,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n7914,3,175,2017-05-10 00:09:42,http://walshbrakus.net/margret.ward,0.3656563740,2.46.70.28,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n7915,3,175,2017-04-17 09:38:03,http://muellerhayes.co/eli.renner,0.3715112770,19.126.74.142,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n7916,3,175,2017-06-11 20:14:29,http://bauch.org/effie_ferry,0.5110993096,10.252.187.189,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n7917,3,175,2017-01-23 05:44:20,http://littleschuppe.biz/kaley,0.2745430581,21.21.205.205,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n7918,3,175,2017-02-26 21:29:48,http://mccullough.biz/giovani,0.8515915258,102.91.124.219,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n7919,3,175,2017-02-28 06:44:56,http://hagenes.io/sabina,0.6833825453,127.17.54.218,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n7920,3,175,2017-04-30 08:43:21,http://homenick.net/sallie.gerhold,0.2235630439,47.179.75.44,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n7921,3,175,2017-03-30 14:45:36,http://blandadare.info/john,0.3101675885,76.129.201.145,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n7922,3,175,2017-01-28 05:16:27,http://kirlin.info/saige_ryan,0.0761294075,138.223.173.84,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n7923,3,175,2017-04-10 19:26:05,http://zieme.com/bonita.abernathy,0.7457542776,121.140.20.206,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n7924,3,175,2017-04-19 00:35:28,http://turner.io/madyson,0.4710870377,202.101.61.244,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n7925,3,175,2016-12-18 02:31:51,http://zulaufhermann.co/luna,0.2842030498,10.76.35.217,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n7926,3,175,2017-01-19 09:58:35,http://parisian.biz/jaclyn,0.3976442552,237.19.237.31,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n7927,3,175,2017-02-28 20:58:44,http://baumbachboyle.io/camille,0.3875905380,72.91.131.77,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n7928,3,175,2017-03-12 21:12:02,http://gerholdaltenwerth.name/jordy_bayer,0.2769767452,155.180.124.46,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n7929,3,175,2017-05-28 05:34:44,http://rippin.org/muhammad,0.3732024103,205.207.76.98,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n7930,3,175,2017-01-13 05:07:10,http://veum.name/martine.leannon,0.0469703859,55.97.136.201,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n7931,3,175,2017-02-15 04:17:14,http://adams.info/garnett.mayer,0.8142884857,158.123.112.107,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n7932,3,175,2017-01-27 09:53:27,http://mayert.co/dereck,0.7222127642,150.146.39.77,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n7933,3,175,2017-02-12 09:31:45,http://skiles.co/ferne,0.2403680375,79.243.41.141,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n7934,3,175,2017-02-27 19:53:40,http://lang.com/eve,0.1007440226,237.34.253.185,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n7935,3,175,2017-03-24 12:49:15,http://bruenbernier.info/margarette_daniel,0.9745281211,201.158.222.177,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n7936,3,175,2017-01-12 00:00:55,http://ondrickakertzmann.net/theresa.paucek,0.0949857445,146.133.138.228,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n7937,3,175,2017-04-20 23:22:37,http://christiansen.org/emerson_welch,0.6864013637,39.189.232.106,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n7938,3,175,2017-04-10 02:52:25,http://gerhold.info/elvera,0.4767182222,225.199.53.36,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n7939,3,175,2017-04-23 23:30:01,http://mckenzieoberbrunner.org/elsa,0.7492948181,166.124.201.69,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n7940,3,175,2017-03-27 01:34:04,http://jaskolski.name/rickie,0.7093146406,71.157.11.215,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n7941,3,175,2016-12-29 07:08:47,http://bednar.org/lacy,0.5873346596,189.166.235.146,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n7942,3,175,2016-12-19 12:25:50,http://beierthompson.io/richard.rolfson,0.3115867502,140.56.65.219,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n7943,3,175,2017-06-12 00:08:58,http://hermiston.com/aniya_beahan,0.3270498454,143.16.239.71,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n7944,3,175,2017-02-14 11:30:02,http://streichbartoletti.io/antone_yost,0.7069025425,188.219.175.219,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n7945,3,175,2017-01-19 14:19:14,http://schuppe.net/pearline.dickens,0.5217813262,49.197.236.25,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n7946,3,175,2017-05-12 15:59:27,http://pacochaernser.org/amari,0.8538477292,12.145.58.13,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n7947,3,175,2017-05-31 08:02:03,http://halvorson.biz/macie,0.8437397515,21.63.37.182,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n7948,3,175,2017-06-07 00:53:18,http://heaneyokuneva.info/bettye,0.8177091000,218.229.209.3,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n7949,3,175,2017-04-14 22:05:12,http://cronablanda.co/tiana.bednar,0.7816068691,9.40.8.139,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n7950,3,175,2017-02-14 16:20:34,http://brekke.io/stacey,0.9718075345,37.254.209.43,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n7951,3,175,2016-12-22 06:11:23,http://langworth.org/stevie.vonrueden,0.7441396908,130.133.228.113,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n7952,3,175,2017-02-13 01:43:20,http://dicki.name/ettie_nienow,0.7136953485,112.111.14.138,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n7953,3,175,2017-01-21 09:40:10,http://weberberge.org/tobin,0.8083029008,62.29.23.214,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n7954,3,175,2017-02-28 03:18:01,http://oreilly.biz/ellie,0.1888093971,231.251.115.109,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n7955,3,175,2017-04-05 21:13:59,http://heeljohnson.co/carolanne,0.3254085960,232.43.66.150,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n7956,3,175,2017-05-18 16:04:12,http://mcculloughmacejkovic.io/leland_crooks,0.9597548536,65.91.245.238,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n7957,3,175,2017-02-26 19:12:51,http://purdyyundt.net/corine,0.9433513776,32.184.6.89,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n7958,3,175,2017-04-12 02:43:42,http://romaguera.info/alexandrea_jacobs,0.8394344721,182.79.229.58,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n7959,3,175,2017-05-11 19:55:29,http://willms.info/monique_friesen,0.4298751191,239.10.38.20,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n7960,3,175,2017-02-23 11:00:01,http://hegmann.co/alanis,0.8771821686,155.111.220.65,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n7961,3,175,2017-01-24 00:58:58,http://christiansen.info/zion.kuhlman,0.4425200993,75.27.98.211,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n7962,3,175,2016-12-28 21:50:10,http://tillmanprohaska.io/kathleen,0.0401880158,204.122.131.140,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n7963,3,175,2017-02-26 22:41:45,http://grady.co/daisy,0.4622889768,98.175.204.63,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n13836,5,310,2017-05-17 00:59:22,http://brekke.com/alyon,,168.99.41.63,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n13837,5,310,2017-04-05 01:36:21,http://grahamkeebler.co/antonetta_luettgen,,114.157.59.205,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n13838,5,310,2017-01-05 21:38:10,http://damore.co/andy.conroy,,243.236.167.116,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n13839,5,310,2017-05-02 02:22:26,http://haleyspinka.org/aleandro.beahan,,111.89.190.34,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n13840,5,310,2017-04-08 03:29:53,http://lakin.io/otto_heller,,199.72.216.147,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n13841,5,310,2016-12-26 18:39:23,http://turcotte.io/keenan,,61.191.231.179,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n13842,5,310,2017-04-02 09:10:45,http://hermann.name/christop.kertzmann,,212.92.82.71,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n13843,5,311,2016-12-27 22:16:06,http://grimes.co/kacie,,209.54.113.206,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n13844,5,311,2017-01-09 23:26:05,http://haag.org/oral.hartmann,,134.74.197.167,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n13845,5,311,2017-04-07 11:30:45,http://hills.io/pearlie_smith,,119.95.254.215,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n13846,5,311,2017-02-28 12:48:53,http://kerluke.com/maggie.ankunding,,195.214.60.242,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n13847,5,311,2017-04-15 10:24:47,http://cole.org/roderick,,237.80.231.186,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n13848,5,311,2017-02-27 00:37:08,http://robel.name/maci_carter,,147.49.182.217,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n13849,5,311,2017-05-29 01:37:08,http://gaylordgerlach.biz/lexi.tremblay,,90.34.133.225,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n13850,5,311,2017-01-24 16:59:27,http://bins.org/elian,,207.60.10.72,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n13851,5,311,2017-04-13 21:11:58,http://koelpin.name/sierra.simonis,,92.71.35.205,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n13852,5,311,2017-03-29 19:55:29,http://greenfelder.org/buck.schneider,,25.215.240.220,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n13853,5,311,2017-02-20 07:57:51,http://hansen.io/trisha_walker,,108.95.117.241,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n13854,5,311,2017-04-19 06:28:48,http://bradtke.co/manley_littel,,98.128.133.45,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n13855,5,311,2017-03-23 23:23:18,http://binsmarks.com/frederic,,221.203.38.179,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n13856,5,311,2016-12-13 08:47:22,http://littelhilpert.io/tobin,,252.169.196.207,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n13857,5,311,2017-04-30 08:06:25,http://willmskoelpin.net/reagan,,99.212.50.151,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n13858,5,311,2017-05-07 11:47:28,http://rolfsonkoepp.com/noemy.heaney,,126.184.245.209,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n13859,5,311,2017-01-27 18:09:49,http://gislason.io/jennings_corwin,,177.85.39.67,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n13860,5,311,2017-01-23 13:32:03,http://kirlincormier.biz/bart.klein,,244.207.131.111,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n13861,5,311,2017-05-03 08:11:08,http://parisian.net/tracy,,211.62.180.19,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n13862,5,311,2016-12-13 06:17:44,http://jakubowskibuckridge.co/camryn,,183.31.115.61,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n13863,5,311,2017-03-24 00:23:04,http://jerde.name/claud.grimes,,197.229.64.172,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n13864,5,311,2017-06-07 00:38:07,http://adamsbahringer.info/bryon.simonis,,39.42.252.90,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n13865,5,311,2017-02-27 16:27:12,http://marvinbode.name/keeley,,245.139.196.162,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n13866,5,311,2017-04-04 04:12:51,http://purdy.co/francisco,,169.197.54.245,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n13867,5,311,2017-04-14 07:58:01,http://swaniawski.info/dewayne,,225.113.185.23,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n13868,5,311,2017-05-02 05:37:26,http://kiehn.org/salvatore,,238.125.24.158,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n13869,5,311,2017-01-17 23:14:03,http://robertsaufderhar.info/rolando.lubowitz,,158.205.17.114,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n13870,5,311,2017-01-03 01:22:43,http://pollich.io/sonny,,58.46.64.6,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n13871,5,311,2017-02-11 23:03:19,http://konopelski.biz/marc_runolfsdottir,,121.63.102.237,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n13872,5,311,2017-04-28 19:18:32,http://metz.org/velda.cole,,131.240.170.6,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n13873,5,311,2016-12-25 10:51:15,http://dibbert.co/suzanne.rohan,,210.171.215.66,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n13874,5,311,2017-05-17 01:40:25,http://lehner.com/neal,,247.128.53.5,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n13875,5,311,2017-04-13 01:18:29,http://larson.net/damon.dibbert,,137.85.63.14,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n13876,5,311,2017-04-02 19:00:09,http://lueilwitz.net/nettie_schuppe,,158.165.229.155,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n13877,5,311,2017-04-06 19:28:28,http://wardeichmann.biz/yasmeen,,107.48.108.152,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n13878,5,311,2017-04-24 09:51:14,http://gutkowskischimmel.biz/andre.pouros,,118.16.91.4,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n13879,5,311,2017-05-31 06:29:24,http://monahangrimes.co/maya_goldner,,46.43.2.227,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n13880,5,311,2017-06-06 08:46:44,http://runolfon.org/ole,,91.59.238.178,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n13881,5,311,2017-01-19 16:31:01,http://kundeconroy.co/doug_gleason,,150.65.136.97,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n13882,5,311,2017-06-13 17:23:17,http://stehr.io/madilyn,,76.160.234.104,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n13883,5,311,2017-03-05 21:05:50,http://kutchhirthe.co/yeenia.hauck,,81.97.122.196,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n13884,5,311,2017-01-20 11:32:11,http://emard.name/roxane,,23.244.107.138,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n13885,5,311,2017-01-07 11:33:04,http://konopelskiwisozk.name/alexie_schinner,,58.161.5.253,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n13886,5,311,2017-04-26 02:22:38,http://bergstromreilly.com/mavis,,100.185.104.239,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n13887,5,311,2017-05-30 16:01:35,http://botsford.net/jean,,29.135.220.50,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n13888,5,311,2017-01-03 06:29:37,http://wilkinsonharris.io/vidal,,127.134.143.249,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n13889,5,311,2017-03-04 11:57:56,http://baumbach.org/filiberto_sipes,,178.253.126.252,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n13890,5,311,2017-04-22 04:06:28,http://williamson.biz/luigi,,133.105.195.237,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n16819,6,381,2017-01-01 23:39:22,http://johnston.io/elwin,,140.180.254.223,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n16820,6,381,2017-06-08 17:19:46,http://mueller.name/ewald,,100.140.72.46,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n16821,6,381,2017-06-02 05:39:43,http://damore.com/ahmed_heathcote,,126.250.51.133,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n16822,6,381,2017-02-10 01:29:39,http://trompfarrell.info/roslyn,,252.108.6.15,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n16823,6,381,2017-05-10 10:55:17,http://stiedemann.org/helena,,163.173.230.167,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n16824,6,381,2017-03-11 21:52:53,http://turcotte.com/oda_bradtke,,146.121.3.195,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n16825,6,381,2017-05-26 03:17:16,http://bartoletti.io/kaia_lindgren,,10.121.203.16,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n16826,6,381,2017-06-01 04:45:49,http://macejkovic.io/boyd,,208.168.176.70,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n16827,6,381,2016-12-19 01:49:38,http://collier.io/shanon,,84.147.138.110,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n16828,6,381,2017-06-08 16:26:08,http://nikolauwift.net/ernie_purdy,,240.208.231.59,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n16829,6,381,2016-12-24 19:32:40,http://vandervort.io/rose,,143.217.249.127,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n16830,6,381,2017-04-15 12:17:42,http://koch.biz/haven,,140.117.5.22,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n16831,6,381,2017-01-25 13:24:51,http://barton.net/beatrice_koelpin,,33.70.250.151,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n16832,6,381,2017-02-08 09:08:24,http://wunsch.net/nathaniel,,142.235.238.96,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n16833,6,381,2017-03-26 21:30:33,http://bergnaumadams.info/joanie_murazik,,254.11.114.27,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n16834,6,381,2016-12-21 15:54:07,http://dooley.net/gennaro_ritchie,,131.58.212.137,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n16835,6,381,2017-04-28 11:03:42,http://bahringerbrown.name/michael.daniel,,145.235.182.232,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n16836,6,381,2016-12-25 06:50:04,http://powlowski.com/raoul.romaguera,,208.226.169.116,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n16837,6,381,2017-01-02 06:29:29,http://klingkreiger.biz/blaise,,84.178.27.230,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n16838,6,381,2017-05-19 03:45:46,http://leschgrady.name/connie,,202.249.72.25,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n16839,6,381,2017-03-31 07:49:57,http://harber.info/jan,,175.159.232.26,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n16840,6,381,2017-04-05 00:49:04,http://oconnelldeckow.co/rashawn_johnson,,241.16.249.170,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n16841,6,381,2017-04-14 09:32:59,http://goyette.name/uriel_witting,,236.124.66.189,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n17302,6,390,2017-03-05 05:03:55,http://terry.org/stephan,,160.61.146.23,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n16842,6,381,2017-04-19 23:40:05,http://trompmaggio.biz/lizeth_heidenreich,,163.145.65.202,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n16843,6,381,2017-04-08 04:05:47,http://koeppsteuber.info/grace,,93.138.65.168,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n16844,6,381,2017-02-09 05:11:49,http://luettgencummings.info/anthony.bashirian,,250.196.193.136,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n16845,6,381,2017-05-31 19:23:32,http://howe.name/herminio_west,,27.148.146.69,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n16846,6,381,2017-06-11 21:08:44,http://hayeszboncak.com/al.kohler,,94.23.139.139,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n16847,6,381,2017-04-05 00:09:30,http://okongrady.org/morgan.zemlak,,118.44.139.15,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n16848,6,381,2017-03-19 23:02:02,http://weimannullrich.net/ramona,,172.242.30.79,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n16849,6,381,2017-02-20 23:25:01,http://wolffritchie.org/leilani.kutch,,168.252.100.55,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n16850,6,381,2017-02-02 08:06:24,http://beckerstracke.co/titus,,119.113.98.116,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n16851,6,381,2017-03-08 15:27:31,http://prohaska.org/derick,,65.62.171.179,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n16852,6,381,2016-12-15 09:44:48,http://cruickshank.net/annabelle,,93.86.154.118,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n16853,6,381,2016-12-13 19:35:24,http://torphy.name/kristopher.legros,,123.90.253.128,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n16854,6,381,2017-02-03 03:24:32,http://schinner.co/idella.hirthe,,49.194.32.8,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n16855,6,381,2017-04-12 09:32:38,http://altenwerth.co/martin,,158.92.146.164,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n16856,6,381,2017-05-05 13:51:20,http://stoltenbergquitzon.com/opal_wilkinson,,28.185.194.211,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n16857,6,381,2017-03-06 12:55:55,http://hammes.org/marcella,,17.189.113.94,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n16858,6,381,2017-02-06 05:14:15,http://pacocha.info/heber.oberbrunner,,35.240.218.239,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n16859,6,381,2017-03-05 04:14:03,http://runolfsdottir.info/josh,,157.58.8.24,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n16860,6,381,2017-05-19 00:45:35,http://oreilly.net/lora.greenfelder,,160.214.206.15,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n16861,6,382,2017-01-03 03:01:17,http://marks.com/clare_bernhard,,169.220.219.73,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n16862,6,382,2017-01-22 23:50:00,http://denesik.info/bo,,59.63.78.9,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n16863,6,382,2016-12-30 16:26:42,http://welch.co/santos_beer,,116.34.46.213,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n16864,6,382,2017-05-11 09:09:33,http://mueller.org/genevieve_powlowski,,248.113.46.35,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n16865,6,382,2017-01-12 14:11:55,http://kuhn.org/osbaldo,,139.201.11.225,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n16866,6,382,2017-05-05 14:27:06,http://hudsonbauch.biz/etha,,121.4.95.161,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n16867,6,382,2017-04-03 04:57:18,http://schaden.net/mackenzie,,13.151.193.202,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n16868,6,382,2017-06-08 03:35:43,http://homenick.co/rebecca.herzog,,41.11.45.162,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n16869,6,382,2017-03-01 16:50:36,http://leuschke.biz/connie.lind,,157.247.38.20,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n16870,6,382,2017-05-19 18:44:45,http://walker.info/leta_bauch,,193.220.126.8,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n16871,6,382,2017-05-27 09:44:51,http://predovic.io/fatima_schowalter,,118.28.208.136,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n16872,6,382,2017-05-09 14:10:18,http://wehner.info/brandon,,98.207.157.23,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n3997,2,87,2017-04-12 03:42:37,http://medhurst.co/dana.ryan,,238.123.161.103,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n3998,2,87,2017-01-12 08:34:00,http://keler.biz/helena_ruecker,,174.144.164.52,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n3999,2,87,2016-12-27 06:03:54,http://wiza.biz/enid_feeney,,159.156.152.78,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n4000,2,87,2017-02-01 18:01:16,http://moriette.io/emilio.marvin,,94.190.61.141,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n4001,2,87,2017-02-24 13:53:27,http://rogahntremblay.biz/jasper.kreiger,,22.245.80.143,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n4002,2,87,2017-03-28 03:42:54,http://rempelmcclure.com/edmund,,111.85.152.4,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n4003,2,87,2017-04-08 20:29:53,http://barton.co/estell.lakin,,132.244.18.176,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n4004,2,87,2017-02-07 10:19:57,http://maggio.biz/letha.dickinson,,70.164.94.149,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4005,2,87,2017-03-28 11:01:39,http://bernhardluettgen.com/braden.stokes,,2.18.240.18,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n4006,2,88,2017-03-28 09:53:25,http://flatley.info/celine,,26.167.49.106,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n4007,2,88,2017-05-11 16:58:35,http://cummings.name/adalberto,,227.16.79.227,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n4008,2,88,2017-02-21 08:04:48,http://willms.org/delilah,,241.38.37.111,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n4009,2,88,2017-03-20 13:46:49,http://luettgenprice.com/miouri,,155.244.57.179,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n4010,2,88,2017-05-29 08:16:26,http://schmelerschmeler.co/virgie.beatty,,89.159.225.34,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n4011,2,88,2016-12-28 19:49:23,http://stoltenberg.biz/tyson.abbott,,236.23.214.136,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n4012,2,88,2017-03-25 18:42:50,http://schaefer.name/jevon.littel,,189.198.252.221,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n4013,2,88,2017-06-10 21:36:39,http://towne.io/arlene.morar,,15.73.100.75,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n4014,2,88,2017-01-31 14:59:20,http://wunschwhite.org/jamal,,241.117.228.211,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n4015,2,88,2017-05-27 09:55:23,http://bahringer.net/maryjane.keeling,,63.115.114.65,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n4016,2,88,2017-03-03 18:40:24,http://mosciski.io/ethel,,155.118.28.172,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n4017,2,88,2017-01-19 10:00:11,http://tillman.com/blanca_fadel,,140.131.220.48,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n4018,2,88,2017-05-28 09:38:27,http://hudson.io/lula_pagac,,200.74.236.8,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n4019,2,88,2017-05-17 00:00:52,http://wehnerkoch.io/ima_johnston,,189.232.98.188,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n4020,2,88,2017-03-02 14:15:44,http://wyman.org/finn,,186.223.134.157,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n4021,2,88,2017-06-05 22:42:51,http://corwin.name/liam,,37.157.121.235,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n4022,2,88,2017-05-14 13:56:04,http://mann.io/daphnee_jacobs,,187.197.188.246,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n4023,2,88,2017-02-12 22:06:24,http://waelchi.name/natasha,,239.56.63.78,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n4024,2,88,2017-05-18 16:02:46,http://kuhlman.biz/tabitha.hickle,,23.49.115.169,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n4025,2,88,2017-04-21 15:11:02,http://kleingoyette.biz/felipe,,144.8.239.4,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n4026,2,88,2017-04-04 08:18:45,http://schusterbalistreri.info/dannie.turner,,203.92.36.77,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n4027,2,88,2016-12-14 17:19:15,http://williamsonfeest.net/maci.ledner,,44.11.181.154,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n4028,2,88,2017-03-20 13:35:16,http://rodriguez.co/braeden.anderson,,19.204.208.228,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n4029,2,88,2017-04-04 02:04:29,http://brakus.biz/laila.gleason,,254.194.189.12,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n4030,2,88,2017-06-07 14:44:32,http://rathkeeling.com/alexandro.pacocha,,224.55.66.95,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n4031,2,88,2017-05-17 15:26:36,http://walsh.net/edna.flatley,,214.250.168.89,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n4032,2,88,2017-02-08 14:51:51,http://koepp.name/niko.lemke,,166.54.154.216,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n4033,2,88,2016-12-31 06:12:49,http://conn.org/evalyn,,86.187.55.244,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n4034,2,88,2017-03-20 15:28:49,http://connreilly.io/flo,,138.2.175.22,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n4035,2,88,2017-05-14 05:15:59,http://kunde.org/dolly,,245.175.173.217,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n4036,2,88,2017-02-17 17:26:20,http://mclaughlinhahn.org/bernadette_bartoletti,,108.78.231.12,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n4037,2,88,2017-03-25 07:35:43,http://friesen.org/neal,,201.65.68.167,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n4038,2,88,2017-04-15 16:38:11,http://lemke.info/justice_schmidt,,158.62.43.42,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n4039,2,88,2017-05-30 02:46:20,http://mayer.com/grayce.bernier,,43.177.56.247,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n4040,2,89,2017-06-09 18:13:24,http://eichmann.co/cierra,,120.68.44.201,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n4041,2,89,2017-04-27 02:33:06,http://mccullough.name/aliza_gerhold,,124.165.245.174,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n4042,2,89,2016-12-16 22:14:28,http://langosh.biz/esther,,186.217.227.50,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n4043,2,89,2017-05-03 18:32:34,http://welchcruickshank.info/lauriane,,148.94.83.48,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n4044,2,89,2017-05-12 11:37:34,http://ko.io/will_grady,,118.18.39.87,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n4045,2,89,2017-03-20 23:54:27,http://quitzon.org/meredith,,26.246.150.114,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n4046,2,89,2017-02-03 06:50:41,http://larson.co/kiara.lemke,,70.50.11.193,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n4047,2,89,2017-02-07 05:40:42,http://mayer.io/joana.walsh,,186.161.124.161,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n4048,2,89,2017-04-08 16:34:40,http://kemmerterry.co/merl,,222.45.75.242,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n4049,2,89,2017-05-20 21:01:40,http://kihndicki.biz/isom.wilkinson,,221.226.34.68,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n4050,2,89,2016-12-30 00:07:25,http://cartwright.com/braden_johnson,,181.241.59.98,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n4051,2,89,2017-01-27 00:27:01,http://wehner.io/cody,,223.149.19.139,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n4052,2,89,2016-12-27 07:06:47,http://ruel.net/arvel,,235.162.183.209,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n10942,4,245,2017-03-30 16:28:31,http://prosacco.net/rylee,,146.72.32.222,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n10943,4,245,2017-02-22 12:32:49,http://rempel.org/lucio,,38.44.61.56,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n10944,4,245,2016-12-29 08:26:02,http://labadie.com/justina.erdman,,44.115.143.233,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n10945,4,245,2017-01-11 20:21:36,http://thompson.biz/jacklyn_rohan,,150.12.119.162,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n10946,4,245,2017-03-28 16:57:55,http://kutch.com/kara.quitzon,,212.74.247.82,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n10947,4,245,2017-04-18 13:46:11,http://kohlerwalker.co/presley,,175.59.52.63,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n10948,4,245,2017-02-26 23:45:00,http://rodriguezemard.co/samantha.barton,,33.111.92.164,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n10949,4,245,2017-05-15 05:24:20,http://mohr.com/odell_wolff,,150.116.181.126,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n10950,4,245,2017-06-11 02:49:48,http://romagueradouglas.io/ebba_terry,,6.46.229.183,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n10951,4,245,2017-04-29 10:02:44,http://vandervortspencer.name/odell,,21.18.109.190,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n10952,4,245,2017-06-02 22:35:29,http://nader.org/hellen,,171.138.152.67,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n10953,4,245,2017-04-08 12:46:43,http://gleichner.info/katharina.nolan,,235.12.102.192,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n10954,4,245,2017-02-03 09:30:14,http://strosin.co/aaliyah.vonrueden,,162.80.206.71,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n10955,4,245,2016-12-26 13:23:38,http://corkery.net/mariano,,224.163.203.58,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n10956,4,245,2017-05-12 01:40:12,http://lynch.name/rebekah_howell,,235.216.69.222,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n10957,4,245,2016-12-18 13:08:18,http://labadie.name/freddie_schulist,,80.140.107.53,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n10958,4,245,2016-12-15 04:30:43,http://mayertmueller.org/allen.torp,,172.180.71.49,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n10959,4,245,2017-05-10 07:10:02,http://brekkespencer.co/kari,,180.210.179.161,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n10960,4,245,2017-03-02 22:14:49,http://funk.org/natalie,,96.232.55.208,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n10961,4,245,2017-03-10 00:55:25,http://grimesgleason.io/alyce.casper,,104.56.40.224,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n10962,4,245,2017-04-17 02:01:30,http://corwin.io/brooks_gleason,,224.163.224.71,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n10963,4,245,2017-01-16 11:05:04,http://kris.com/marcella,,8.152.186.247,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n10964,4,245,2017-03-20 14:20:25,http://schumm.name/patsy,,142.113.2.82,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n10965,4,245,2017-01-24 17:01:54,http://little.net/eldon,,74.12.72.196,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n10966,4,245,2016-12-29 06:31:13,http://krajcik.co/jayson.oberbrunner,,211.224.238.104,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n10967,4,245,2017-06-12 19:06:43,http://johnston.info/jordy,,38.94.198.114,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n10968,4,245,2017-03-01 10:37:11,http://morar.io/lily,,31.113.206.244,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n10969,4,245,2017-04-21 14:46:40,http://klocko.net/taylor,,18.173.144.212,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n10970,4,245,2017-02-28 03:16:20,http://ortiz.name/jesus_heel,,220.175.217.88,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n10971,4,245,2017-06-11 06:42:52,http://stanton.co/marlee_hills,,137.52.46.177,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n10972,4,245,2017-04-15 11:18:08,http://cummerata.com/lucious,,108.216.253.160,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n10973,4,245,2017-01-08 05:47:27,http://murphystreich.net/devan.paucek,,155.28.215.176,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n10974,4,245,2017-02-21 12:41:14,http://vandervort.com/jonathan_walsh,,69.40.237.38,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n10975,4,245,2017-02-18 20:41:13,http://denesik.name/kraig,,204.83.223.83,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n10976,4,245,2017-03-17 00:24:00,http://lehner.biz/julian_kulas,,78.41.221.4,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n10977,4,245,2017-04-09 17:33:58,http://altenwerth.org/andreanne_schultz,,100.151.222.153,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n10978,4,245,2017-05-13 23:15:33,http://orn.net/jacquelyn_mcdermott,,173.31.9.140,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n10979,4,245,2017-02-07 05:59:44,http://medhurstaltenwerth.org/icie_ziemann,,152.142.182.138,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n10980,4,245,2017-05-16 14:11:16,http://crookslang.co/roscoe.stoltenberg,,106.96.204.158,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n10981,4,245,2017-01-18 16:22:30,http://nolanlarkin.com/june,,56.53.163.114,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n10982,4,245,2017-04-26 07:31:53,http://hoeger.io/cora.hyatt,,116.208.10.177,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n10983,4,245,2016-12-26 23:03:31,http://sipes.co/green,,5.95.163.74,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n10984,4,245,2017-04-28 03:15:45,http://kiehn.name/lera.murazik,,114.144.85.39,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n10985,4,245,2017-02-06 17:29:33,http://whitepaucek.co/marlen,,216.241.60.159,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n10986,4,245,2016-12-14 00:25:45,http://emmerich.name/angie.fahey,,173.134.231.74,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n10987,4,245,2017-05-12 22:49:26,http://lockman.info/rae.stracke,,247.254.250.64,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n10988,4,246,2017-05-04 18:39:22,http://bechtelaraltenwerth.info/haven.schowalter,,252.60.12.251,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n10989,4,246,2017-03-01 02:34:26,http://simonis.net/merl_denesik,,200.92.155.40,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n10990,4,246,2017-02-19 02:56:19,http://lesch.info/leora_tremblay,,187.10.50.39,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n10991,4,246,2017-01-02 22:27:07,http://mcclure.io/alfred_kilback,,174.38.20.197,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n10992,4,246,2017-04-28 17:15:56,http://dibbert.name/orrin,,67.100.216.167,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n10993,4,246,2017-06-10 19:51:58,http://schoen.name/amiya,,131.225.90.141,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n10994,4,246,2017-05-09 18:19:00,http://ondricka.co/max_oconner,,230.242.94.79,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n10995,4,246,2017-03-03 01:12:39,http://doylegibson.io/anderson.vandervort,,136.189.75.12,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n10996,4,246,2017-02-16 05:23:09,http://gerhold.name/allen_mraz,,101.220.226.220,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n7964,3,175,2017-03-26 06:37:28,http://halvorsonbartell.info/reynold.ruecker,0.1770922293,242.56.123.124,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n7965,3,175,2017-02-21 20:56:09,http://armstrong.io/claria,0.3949878181,62.55.198.161,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n7966,3,175,2017-02-18 07:45:41,http://heel.net/laney,0.0015151864,211.142.76.235,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n7967,3,175,2017-02-03 22:48:28,http://ryan.io/arjun_marvin,0.5891185421,189.251.172.209,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n7968,3,175,2017-01-18 11:04:06,http://price.name/meggie_goyette,0.8782751706,202.202.135.239,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n7969,3,175,2017-02-17 14:46:48,http://langworth.org/fausto,0.2026356405,217.125.64.197,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n7970,3,175,2017-05-22 22:28:45,http://whitewehner.info/jailyn.herzog,0.7340996480,42.162.156.198,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n7971,3,175,2017-02-24 10:12:32,http://donnelly.biz/roger_kihn,0.8375209830,82.53.121.148,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n7972,3,176,2017-05-12 04:44:16,http://keler.net/keyon.oconnell,0.8242732446,81.123.33.134,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n7973,3,176,2017-02-14 21:02:00,http://hyattarmstrong.net/camden,0.7097888392,153.101.35.206,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n7974,3,176,2017-02-27 12:02:59,http://crooksbraun.net/eldridge,0.1403471327,90.243.14.23,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n7975,3,176,2017-02-06 16:07:29,http://kilback.co/armando,0.8880322062,163.132.63.225,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n7976,3,176,2017-02-12 11:17:06,http://kirlin.com/elva.grimes,0.2882909818,243.216.143.95,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n7977,3,176,2017-02-22 20:09:20,http://wintheisercronin.info/adrienne_runolfsdottir,0.3190290468,187.132.16.224,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n7978,3,176,2017-02-12 05:41:24,http://romaguera.co/mathilde,0.9978610297,134.69.223.37,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n7979,3,176,2017-04-18 11:08:15,http://sawayn.co/luisa,0.9029364119,52.81.229.164,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n7980,3,176,2016-12-21 00:41:59,http://maggiokoelpin.biz/broderick,0.0314982963,58.232.42.8,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n7981,3,176,2016-12-17 04:34:40,http://bartell.info/rylee_kihn,0.5857787742,195.181.193.147,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n7982,3,176,2017-02-19 05:59:47,http://hilll.biz/benton.stiedemann,0.1728682252,146.4.164.29,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n7983,3,176,2017-06-09 10:01:56,http://borer.net/aaron_kunze,0.9053722242,29.116.32.144,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n7984,3,176,2017-03-31 23:42:12,http://robel.net/vincenza,0.7119945219,145.47.126.102,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n7985,3,176,2016-12-24 00:53:12,http://terryjenkins.co/archibald_turcotte,0.5183296490,224.241.24.9,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n7986,3,176,2017-06-06 05:00:40,http://halvorsonmorar.info/albert,0.9113281081,70.188.75.108,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n7987,3,176,2017-05-22 22:54:20,http://willthiel.com/joanny,0.5353973714,6.42.83.159,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n7988,3,176,2016-12-26 15:10:16,http://okeefe.co/louie,0.8175263848,250.223.15.215,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n7989,3,176,2017-04-10 20:53:02,http://hammesherman.biz/raymundo_romaguera,0.3561497743,144.253.24.111,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n7990,3,176,2017-01-12 09:28:09,http://veum.org/erling_greenholt,0.1661255751,159.152.10.56,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n7991,3,176,2017-05-18 03:31:38,http://krajciklubowitz.org/tyra,0.1536691130,234.197.75.33,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n7992,3,176,2017-01-17 18:52:52,http://walker.org/luigi,0.2574998039,198.25.58.172,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n7993,3,176,2017-02-23 11:14:00,http://swaniawskicain.biz/alexys_deckow,0.4698242346,249.136.15.48,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n7994,3,176,2017-05-27 20:04:57,http://hahn.name/darron.hegmann,0.6833504349,179.68.51.187,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n7995,3,176,2017-04-30 21:25:34,http://altenwerth.co/berry_pouros,0.3840477417,41.148.12.75,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n7996,3,176,2017-05-24 06:37:21,http://larsonschaden.io/clyde,0.8700829022,88.128.80.5,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n7997,3,176,2017-01-08 16:56:43,http://mccullough.info/roie,0.5410355983,200.235.125.157,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n7998,3,176,2017-02-16 15:36:19,http://streichgleason.org/erik.lindgren,0.6796937281,103.17.82.70,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n7999,3,176,2017-06-08 16:38:50,http://torphaag.info/jared,0.8477595758,106.195.199.145,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n8000,3,176,2017-01-29 07:20:37,http://keler.biz/libby.herman,0.2792333661,46.86.149.136,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n8001,3,176,2017-03-31 17:07:45,http://braun.io/terrell_johns,0.8383616576,152.217.34.46,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n8002,3,176,2017-04-02 22:21:30,http://kris.com/jairo_casper,0.4148156934,209.168.41.244,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n8003,3,176,2017-05-17 18:07:34,http://kreiger.net/alysa,0.1652377375,36.85.157.182,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n8004,3,177,2017-05-03 08:35:11,http://powlowskiconsidine.net/hilbert.rowe,0.2100720049,154.161.18.90,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n8005,3,177,2017-02-24 15:03:00,http://aufderhar.com/nyah,0.0413644111,211.84.43.167,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n8006,3,177,2017-06-03 20:39:46,http://dooley.com/kailey,0.5118417278,156.152.208.245,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n8007,3,177,2017-04-27 13:34:22,http://casper.biz/jordane_nitzsche,0.4774063520,134.230.5.152,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n8008,3,177,2017-04-05 13:00:23,http://gislasonullrich.org/titus_gibson,0.3042262956,170.247.149.161,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n8009,3,177,2017-03-25 02:42:19,http://gutkowskifritsch.org/tony.metz,0.5591993881,91.166.86.104,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n8010,3,177,2017-01-28 21:36:22,http://hackettgerlach.org/danika,0.7915168587,193.135.91.134,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n8011,3,177,2017-06-05 04:59:56,http://abernathyhamill.org/emile_hackett,0.0641388188,14.149.191.249,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n8012,3,177,2017-03-05 02:16:34,http://moenhickle.biz/katrine_wisozk,0.2494156840,36.5.126.97,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n8013,3,177,2017-03-14 20:49:34,http://mcdermottwolf.info/alvena,0.5946420682,217.161.168.235,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n8014,3,177,2017-06-08 09:56:36,http://walker.biz/bradford,0.5582160877,227.231.98.150,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n8015,3,177,2016-12-22 12:24:07,http://bruen.co/dan,0.8877522429,33.19.107.34,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n13891,5,311,2017-01-29 13:11:25,http://hammespurdy.io/preston,,234.132.209.83,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n13892,5,311,2017-02-27 16:51:41,http://schulistheathcote.io/ebony.johns,,59.246.191.125,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n13893,5,311,2017-03-15 13:04:15,http://rippin.info/brendon,,226.125.228.8,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n13894,5,311,2017-02-04 13:55:35,http://gottliebpagac.co/trudie,,27.218.40.46,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n13895,5,311,2017-04-18 07:38:50,http://abernathy.com/elenor,,176.123.127.234,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n13896,5,311,2017-03-12 09:42:10,http://cummingsbergstrom.com/arvid.torp,,210.198.231.42,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n13897,5,311,2016-12-19 23:23:42,http://runolfsdottir.io/jamir,,237.41.124.34,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n13898,5,311,2017-01-28 21:51:58,http://jenkins.info/ryann,,138.121.64.200,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n13899,5,311,2017-05-20 11:00:55,http://conroy.com/wilburn_gerlach,,107.27.195.112,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n13900,5,311,2017-04-05 09:01:46,http://murray.io/lafayette_wyman,,147.150.101.16,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n13901,5,311,2017-05-14 04:47:58,http://gislason.org/marian_dubuque,,74.214.64.66,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n13902,5,312,2017-06-01 03:34:05,http://schneider.org/roel_ledner,,111.162.233.145,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n13903,5,312,2017-06-01 23:30:14,http://skiles.name/dora_rosenbaum,,5.84.89.45,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n13904,5,312,2017-02-25 02:17:58,http://dickihudson.io/tyra,,134.251.6.249,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n13905,5,312,2017-04-26 09:17:21,http://lindgren.com/carlos_hamill,,180.141.207.73,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n13906,5,312,2017-04-06 20:30:33,http://keeling.info/cecil_huel,,144.95.54.214,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n13907,5,312,2017-04-28 08:05:02,http://goldner.org/aron,,7.10.76.29,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n13908,5,312,2017-05-16 18:14:01,http://boylerosenbaum.org/roy.kuvalis,,229.57.45.120,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n13909,5,312,2017-03-23 00:05:32,http://jakubowskipredovic.info/dylan,,201.91.154.139,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n13910,5,312,2017-01-17 03:47:40,http://stark.co/beulah,,211.123.251.78,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n13911,5,312,2017-01-27 00:34:31,http://howe.net/camila.satterfield,,84.248.62.228,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n13912,5,312,2017-01-23 05:45:46,http://yundt.co/logan,,201.116.228.245,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n13913,5,312,2017-05-18 10:09:38,http://lakinstamm.io/ephraim,,95.104.218.23,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n13914,5,312,2017-03-30 07:44:53,http://roob.net/summer,,52.223.223.85,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n13915,5,312,2017-05-11 02:07:31,http://kling.info/dylan_reinger,,148.117.17.109,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n13916,5,312,2017-01-01 10:50:06,http://beer.org/aniyah,,170.212.130.152,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n13917,5,312,2017-04-19 23:45:25,http://wunsch.com/zula_schroeder,,249.15.234.74,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n13918,5,312,2017-01-05 08:57:11,http://yundt.biz/leif,,121.117.189.123,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n13919,5,312,2017-06-14 00:03:04,http://hilperthagenes.io/eileen_bahringer,,146.199.168.246,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n13920,5,312,2017-03-17 07:09:27,http://bergstrom.com/bennie.osinski,,31.222.154.154,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n13921,5,312,2017-03-11 20:08:00,http://koelpin.org/yasmine_west,,198.187.196.251,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n13922,5,312,2017-05-23 20:42:44,http://hintz.info/ellie_witting,,215.53.222.204,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n13923,5,312,2017-05-26 21:20:37,http://mckenzie.info/vincent.rolfson,,50.58.82.236,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n13924,5,312,2017-04-24 23:44:45,http://dubuquekerluke.co/foster,,225.194.228.235,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n13925,5,312,2017-04-10 11:48:05,http://eichmann.co/reese,,68.234.73.187,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n13926,5,312,2017-03-10 07:26:13,http://padberg.name/lizzie_brown,,185.134.203.171,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n13927,5,312,2017-01-05 21:45:06,http://zemlak.info/timmothy.hegmann,,30.34.225.59,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n13928,5,312,2017-01-17 14:59:46,http://windler.co/addie,,10.44.111.85,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n13929,5,312,2017-02-25 21:28:05,http://homenick.net/hillard.flatley,,200.15.72.193,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n13930,5,312,2017-05-18 14:15:34,http://tremblaylesch.com/adah.kuhic,,48.91.107.202,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n13931,5,312,2017-04-11 05:43:16,http://wilderman.com/cyril,,245.220.7.163,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n13932,5,312,2017-04-09 15:56:38,http://monahan.io/noelia,,60.227.120.72,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n13933,5,312,2017-01-08 19:27:47,http://wiegandkuhic.name/laverna,,248.86.85.146,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n13934,5,312,2017-01-16 18:15:29,http://pfeffer.io/leopold.flatley,,169.114.238.6,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n13935,5,312,2017-06-07 11:57:40,http://graham.com/will_toy,,232.87.160.149,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n13936,5,312,2016-12-26 02:04:27,http://prosacco.biz/lela.ratke,,105.7.144.49,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n13937,5,312,2017-04-05 21:13:04,http://connellysawayn.co/rodrigo_rowe,,41.173.175.228,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n13938,5,312,2017-06-12 23:12:07,http://bogan.biz/bonita,,167.164.54.165,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n13939,5,312,2017-02-04 11:14:10,http://schuster.net/sidney.wyman,,99.88.69.219,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n13940,5,312,2016-12-31 22:04:10,http://murrayfunk.com/monty.gutmann,,24.228.214.17,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n13941,5,312,2017-02-14 15:17:08,http://marks.com/aliza.grady,,234.200.61.127,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n13942,5,312,2017-03-12 16:03:46,http://rolfson.biz/tre_hane,,215.253.184.227,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n13943,5,312,2017-02-21 09:27:49,http://parisian.biz/jennyfer,,154.102.13.34,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n13944,5,312,2017-02-24 23:55:42,http://pollich.co/pedro,,125.77.73.133,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n13945,5,312,2017-05-09 03:52:47,http://boehm.info/joanie,,2.52.96.59,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n13946,5,312,2017-02-14 09:17:16,http://lockman.info/carolyn,,159.189.66.122,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n16873,6,382,2017-02-08 20:39:22,http://toy.co/felix,,33.149.77.90,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n16874,6,382,2017-05-04 05:32:48,http://creminbrakus.io/wilber_bayer,,64.206.121.83,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n16875,6,382,2017-04-15 13:20:48,http://binsmaggio.net/melvina.hamill,,57.225.199.201,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n16876,6,382,2017-05-30 15:58:55,http://gradyrippin.com/kevon,,137.52.171.215,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n16877,6,382,2017-02-19 02:50:25,http://graham.info/mark_kunde,,49.230.127.71,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n16878,6,382,2017-03-06 00:00:54,http://haley.io/daphney.deckow,,228.249.10.254,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n16879,6,382,2017-06-03 12:29:12,http://macejkovic.biz/nellie,,245.110.112.195,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n16880,6,382,2017-04-15 10:25:09,http://crist.name/vita,,71.185.164.53,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n16881,6,382,2016-12-23 04:49:50,http://koelpin.com/tania.kilback,,111.178.236.200,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n16882,6,382,2016-12-13 13:38:41,http://kozeyromaguera.io/henri.macejkovic,,168.53.160.125,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n16883,6,382,2017-04-25 05:38:40,http://altenwerthcrona.name/lucious,,54.171.3.121,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n16884,6,382,2017-01-20 05:59:05,http://leuschke.info/alphonso,,61.207.151.108,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n16885,6,382,2017-05-23 14:57:00,http://denesik.org/bertha,,25.162.63.36,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n16886,6,382,2017-04-09 11:07:22,http://kunze.net/america,,154.100.162.186,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n16887,6,382,2017-01-12 18:56:04,http://wizaflatley.co/beulah_langworth,,250.197.75.121,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n16888,6,382,2016-12-14 06:36:05,http://haneoreilly.biz/dasia,,23.145.16.174,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n16889,6,382,2017-02-13 08:00:08,http://armstrong.net/floyd_hudson,,218.131.217.98,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n16890,6,382,2017-02-26 11:25:06,http://doyle.net/marjolaine.dach,,129.161.192.114,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n16891,6,382,2017-01-22 02:22:44,http://armstrongtorp.co/tod_brakus,,159.250.110.220,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n16892,6,382,2017-01-09 22:03:44,http://uptonfahey.io/amie,,104.167.146.220,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n16893,6,382,2017-03-12 16:05:19,http://heathcote.biz/fletcher,,91.107.198.222,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n16894,6,382,2017-03-04 21:02:42,http://stantonjast.net/beie.schmitt,,151.102.123.185,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n16895,6,382,2017-04-22 01:02:16,http://haaggaylord.co/kale,,137.53.162.46,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n16896,6,382,2017-06-02 09:27:53,http://wiza.org/connie.bode,,23.15.18.131,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n16897,6,382,2017-05-05 06:48:49,http://wiza.org/noemi_hintz,,65.78.46.66,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n16898,6,382,2017-03-14 10:51:58,http://mertz.io/gennaro.ernser,,102.199.88.40,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n16899,6,382,2017-05-31 12:59:41,http://herzogweinat.co/jeika,,231.34.206.111,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n16900,6,382,2017-05-14 13:35:20,http://bartonhodkiewicz.io/casimer.schuppe,,190.21.60.193,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n16901,6,382,2017-04-10 16:08:22,http://hyattwillms.com/david,,178.87.96.110,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n16902,6,382,2017-01-08 03:59:49,http://moriette.biz/mario,,148.13.15.157,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n16903,6,382,2016-12-30 04:13:02,http://hand.com/loy,,122.245.222.228,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n16904,6,382,2017-03-29 14:54:57,http://kuphal.io/tad,,137.22.25.230,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n16905,6,382,2016-12-15 02:14:28,http://wisoky.com/hilbert,,14.202.56.184,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n16906,6,382,2017-05-07 20:18:38,http://champlin.name/giuseppe.mcglynn,,249.54.233.87,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n16907,6,383,2017-02-02 02:00:25,http://ondrickaschuppe.com/christiana_conn,,52.117.195.242,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n16908,6,383,2017-03-03 04:47:31,http://ruelmcdermott.net/efrain,,74.59.146.157,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n16909,6,383,2017-05-27 00:01:56,http://leuschke.co/kaycee,,179.100.117.101,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n16910,6,383,2017-04-04 17:23:05,http://keebler.org/dayana.crooks,,57.103.242.253,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n16911,6,383,2017-03-26 02:14:50,http://douglaimonis.co/jackeline,,180.24.246.186,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n16912,6,383,2017-01-20 01:43:42,http://turnerbednar.net/ru_lind,,7.58.42.107,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n16913,6,383,2017-04-11 16:47:03,http://gleasonrippin.name/leilani,,66.183.29.224,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n16914,6,383,2017-03-29 07:37:14,http://nader.info/wilbert,,187.38.83.97,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n16915,6,383,2017-01-04 07:20:36,http://heaney.com/linwood,,50.226.123.5,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n16916,6,383,2016-12-29 21:12:06,http://grant.info/ismael,,135.246.76.113,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n16917,6,383,2017-03-14 01:59:25,http://hegmann.name/keven.bosco,,227.250.17.14,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n16918,6,383,2017-01-13 19:48:22,http://pfannerstillbeer.org/marques.watsica,,194.236.200.243,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n16919,6,383,2017-05-26 02:08:52,http://donnelly.io/kaden.roob,,178.118.219.145,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n16920,6,383,2016-12-25 17:34:33,http://kub.io/armani,,17.151.209.212,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n16921,6,383,2017-05-14 11:55:50,http://jacobi.org/cleo.paucek,,239.2.66.232,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n16922,6,383,2017-03-13 18:23:09,http://corwin.io/jada.hegmann,,171.167.189.146,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n16923,6,383,2017-05-27 21:34:48,http://kuhic.name/reie_mayer,,183.218.202.31,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n16924,6,383,2017-05-27 04:04:32,http://swift.name/zena.walsh,,47.89.107.189,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n16925,6,383,2017-03-09 19:46:01,http://carrollschulist.biz/lyla,,101.102.211.158,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n16926,6,383,2017-02-18 19:58:49,http://hellerschaefer.name/raymundo.ondricka,,93.10.67.204,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n16927,6,383,2017-03-16 07:33:38,http://gislason.info/gene,,188.98.52.166,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n16928,6,383,2017-02-27 14:32:55,http://marksterry.info/raul,,123.119.221.223,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n4053,2,89,2017-04-20 06:21:03,http://westwilderman.co/weston,,231.49.65.15,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n4054,2,89,2017-01-25 11:45:54,http://effertz.io/jennifer,,176.54.111.32,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n4055,2,89,2017-03-04 01:07:34,http://dietrich.net/golda,,254.186.94.37,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n4056,2,89,2017-01-06 08:40:50,http://lemkehudson.net/emmitt,,115.242.87.74,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n4057,2,89,2017-06-03 15:16:15,http://kuhnritchie.co/audrey.damore,,159.232.91.182,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n4058,2,89,2017-04-13 09:56:49,http://bins.name/melody.koch,,19.29.89.112,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n4059,2,89,2017-04-23 20:50:12,http://feeney.biz/mae,,139.47.157.59,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n4060,2,89,2017-06-11 22:57:33,http://dare.info/dejuan.marquardt,,35.151.210.31,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n4061,2,89,2017-06-10 16:10:39,http://satterfieldbergstrom.net/arely,,216.79.49.153,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n4062,2,89,2017-04-06 02:26:49,http://ebert.net/shaina,,242.19.179.130,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n4063,2,89,2017-05-18 15:10:56,http://connelly.co/jadyn_leannon,,155.239.159.111,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n4064,2,89,2017-06-06 10:21:49,http://gusikowski.com/everett_kris,,24.206.76.172,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n4065,2,89,2017-03-27 22:20:54,http://bernierdicki.net/anjali,,149.221.201.179,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n4066,2,89,2017-04-26 09:46:57,http://quitzon.info/demarco,,12.205.145.114,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n4067,2,89,2017-01-05 15:30:56,http://kling.org/elna,,184.221.126.98,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n4068,2,89,2017-02-15 19:44:49,http://hayes.net/jeremie.huels,,139.157.66.68,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n4069,2,89,2017-02-02 14:07:12,http://langworth.org/pablo,,80.218.213.90,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n4070,2,89,2017-03-18 23:09:13,http://renner.info/kendall,,5.105.250.223,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n4071,2,89,2017-04-10 04:02:59,http://cole.name/floy_smitham,,154.134.174.160,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n4072,2,89,2017-03-04 17:43:38,http://kautzer.com/alaina_zemlak,,17.124.90.231,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n4073,2,89,2017-03-22 08:41:40,http://nader.net/callie,,182.130.134.114,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n4074,2,89,2016-12-30 20:14:17,http://bednarullrich.com/alfred,,8.111.204.89,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n4075,2,89,2017-04-29 07:08:50,http://goldner.biz/victoria,,186.196.104.128,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n4076,2,89,2017-04-04 23:17:49,http://collierwaters.name/susanna_romaguera,,145.149.212.63,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n4077,2,89,2017-01-22 01:41:27,http://armstrong.co/madeline.haley,,138.158.135.167,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n4078,2,89,2017-04-20 11:06:28,http://millerkutch.name/lila,,192.248.5.162,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n4079,2,89,2017-06-02 20:05:35,http://lehnerbreitenberg.info/faye,,254.37.214.124,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n4080,2,89,2017-02-23 00:39:39,http://oconnerjenkins.name/sophie_fisher,,248.234.122.65,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n4081,2,89,2017-01-16 11:06:59,http://romaguerawest.net/bridie,,19.10.233.189,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n4082,2,89,2017-01-18 03:30:01,http://cummings.info/paige_moriette,,165.80.9.216,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n4083,2,89,2017-05-04 20:58:01,http://cronin.com/barrett,,175.139.148.178,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n4084,2,89,2017-02-18 20:42:19,http://kshlerin.io/harmon,,111.216.168.4,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n4085,2,89,2017-05-05 17:19:09,http://hintzschultz.co/lois.stroman,,176.245.159.58,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n4086,2,89,2016-12-14 23:15:32,http://jones.net/gertrude_stroman,,170.93.42.95,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n4087,2,89,2017-03-21 18:09:54,http://hammes.org/lelia,,27.177.51.226,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n4088,2,89,2017-05-07 20:46:00,http://schulist.com/hudson.muller,,53.241.25.155,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n4089,2,89,2016-12-29 16:27:35,http://hoppe.io/robb_wyman,,132.253.140.28,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n4090,2,89,2016-12-30 14:57:40,http://weimann.net/cydney_spencer,,47.208.33.91,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n4091,2,89,2017-01-01 20:33:10,http://orn.org/sadie.shanahan,,115.71.51.194,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n4092,2,89,2017-01-26 13:48:33,http://lockman.net/khalil,,105.228.101.180,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n4093,2,89,2017-04-08 15:39:46,http://ruel.com/maya,,128.243.100.101,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n4094,2,89,2016-12-24 18:18:28,http://kuhn.net/sharon.treutel,,174.98.94.89,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n4095,2,89,2017-05-20 12:37:25,http://quitzon.com/chelsey,,117.41.81.118,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n4096,2,89,2017-02-19 04:14:27,http://reichelgusikowski.co/forest_jaskolski,,201.224.182.241,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n4097,2,89,2016-12-13 16:29:49,http://cremin.biz/jada,,109.105.98.164,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n4098,2,89,2017-03-03 07:55:46,http://stanton.co/kacey.torp,,74.105.36.138,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n4099,2,90,2017-04-02 17:17:09,http://hackettgoyette.name/lourdes_sauer,,129.66.68.4,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n4100,2,90,2017-02-05 13:22:07,http://pfeffer.com/rosetta.casper,,125.114.51.228,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n4101,2,90,2017-02-15 08:34:26,http://padberg.info/zachery_mohr,,165.45.129.254,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n4102,2,90,2017-01-05 01:07:57,http://jaskolskilueilwitz.name/amanda.davis,,141.89.8.48,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n4103,2,90,2017-03-24 18:30:05,http://steuber.com/mariam,,81.142.111.166,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n4104,2,90,2017-04-11 11:13:15,http://harber.org/keyon.gutmann,,65.156.109.18,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n4105,2,90,2017-03-09 11:45:26,http://kovacek.co/idella.runolfsdottir,,60.133.71.106,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n4106,2,90,2017-04-11 18:35:29,http://ruecker.biz/danika,,201.157.227.95,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n4107,2,90,2017-02-04 07:41:29,http://barrows.co/ariel,,117.151.9.61,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n4108,2,90,2017-01-26 01:56:41,http://gloverkozey.org/joey,,53.45.193.180,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n10997,4,246,2016-12-22 05:55:58,http://weber.info/lily,,60.244.111.169,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n10998,4,246,2016-12-20 07:17:13,http://bauchdooley.biz/vilma,,140.181.79.20,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n10999,4,246,2017-03-18 08:32:53,http://kovacek.name/iliana,,46.246.183.58,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n11000,4,246,2017-03-14 00:42:39,http://wilkinson.io/elda_wisozk,,14.5.125.162,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n11001,4,246,2017-04-26 01:56:34,http://labadie.io/devin,,12.95.38.212,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n11002,4,246,2016-12-22 04:05:14,http://kristorphy.co/tracey.wisozk,,93.36.29.82,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n11003,4,246,2017-03-25 12:40:45,http://skiles.io/moses,,189.62.218.71,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n11004,4,246,2017-01-06 21:19:53,http://wunsch.biz/shaun,,119.125.232.67,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n11005,4,246,2017-04-18 20:13:49,http://smithtillman.biz/dexter,,234.141.31.128,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n11006,4,246,2017-03-06 01:15:10,http://jenkinsrempel.net/antone.bechtelar,,14.119.186.243,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n11007,4,246,2017-03-18 08:43:16,http://heel.com/jairo.fritsch,,218.176.59.198,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n11008,4,246,2017-03-19 22:21:59,http://breitenberg.info/ethan,,124.190.173.202,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n11009,4,246,2017-01-20 12:29:36,http://boyer.com/sim,,50.124.219.63,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n11010,4,246,2016-12-17 22:54:24,http://barton.io/isadore_ankunding,,10.252.208.143,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n11011,4,246,2017-02-18 15:45:22,http://breitenberg.name/rolando,,160.213.114.59,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n11012,4,246,2016-12-30 02:04:43,http://faheyzieme.org/keaton,,144.21.146.132,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n11013,4,246,2017-06-01 01:42:08,http://kuvalis.com/otilia,,132.196.210.161,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n11014,4,246,2017-03-02 09:00:06,http://barton.com/marie.thompson,,151.113.139.107,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n11015,4,246,2017-02-07 09:27:58,http://pagac.io/conner_moriette,,207.240.11.103,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n11016,4,246,2016-12-14 21:58:55,http://bahringer.com/rhett.ortiz,,145.55.140.71,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n11017,4,246,2017-05-30 13:58:50,http://schowalterdavis.co/dulce,,251.44.169.51,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n11018,4,246,2017-05-14 12:11:44,http://larson.info/william,,170.180.239.30,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n11019,4,246,2016-12-21 22:12:04,http://jacobi.io/golden,,18.229.253.153,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n11020,4,246,2017-04-15 04:00:24,http://kreigermarquardt.co/alysa,,61.226.131.19,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n11021,4,246,2017-05-19 14:29:27,http://lehner.info/dolores.howell,,203.106.36.222,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n11022,4,246,2017-04-02 00:32:22,http://mcdermott.io/lori,,98.206.159.78,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n11023,4,246,2016-12-16 11:57:21,http://rodriguezleuschke.org/asha,,153.206.85.55,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n11024,4,246,2017-01-24 16:00:15,http://murphydietrich.name/lulu.kohler,,170.193.62.230,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n11025,4,246,2017-04-24 18:23:47,http://adams.name/anastasia,,183.17.13.139,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n11026,4,246,2017-02-01 12:04:46,http://stark.io/arno.upton,,37.238.26.37,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n11027,4,246,2017-01-19 23:45:05,http://maggio.biz/bradley,,195.68.22.108,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n11028,4,246,2016-12-30 23:29:12,http://heaney.name/xzavier,,57.235.205.189,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n11029,4,246,2017-04-19 06:32:06,http://schulist.org/jammie,,224.47.110.169,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n11030,4,247,2017-04-05 09:02:49,http://gulgowski.co/danielle.muller,,169.21.181.181,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n11031,4,247,2017-02-21 21:57:32,http://schusterwindler.info/guiseppe,,151.210.87.183,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n11032,4,247,2017-03-05 18:43:15,http://leannonstreich.io/nedra_lowe,,219.173.254.208,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n11033,4,247,2017-03-13 11:28:46,http://jacobi.net/vallie,,47.160.103.201,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n11034,4,247,2017-03-27 15:52:46,http://gutmann.info/astrid.rolfson,,34.197.145.118,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n11035,4,247,2017-01-22 16:22:31,http://larson.biz/efren_kris,,97.18.34.197,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n11036,4,247,2017-02-17 10:08:54,http://muller.io/bridie,,180.59.17.98,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n11037,4,247,2017-03-11 07:13:54,http://glover.name/agustin,,58.134.69.89,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n11038,4,247,2017-06-06 13:41:10,http://mckenzie.com/leonie,,104.106.164.86,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n11039,4,247,2017-02-16 01:57:13,http://gutmannrosenbaum.org/neal.herzog,,250.105.90.228,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n11040,4,247,2017-02-02 05:16:06,http://batz.info/elena,,231.142.3.145,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n11041,4,247,2017-05-17 11:49:57,http://paucekfritsch.biz/lindsey,,138.10.16.232,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n11042,4,247,2017-06-07 08:25:30,http://torphylindgren.biz/douglas.hermiston,,210.58.36.154,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n11043,4,247,2016-12-13 21:58:25,http://lind.io/christelle_pagac,,200.128.211.223,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n20873,7,469,2017-02-11 15:32:11,http://mann.info/savanah,,92.197.113.79,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n11044,4,247,2017-06-03 02:34:01,http://satterfieldvon.com/dedrick_eichmann,,171.138.88.195,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n11045,4,247,2017-04-20 04:01:00,http://okuneva.biz/erika.nikolaus,,14.85.43.242,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n11046,4,247,2017-05-18 02:01:20,http://kshlerin.co/ruby_barton,,164.224.189.55,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n11047,4,247,2017-02-23 20:54:06,http://heaney.com/lazaro,,190.128.238.8,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n11048,4,247,2017-02-26 07:12:09,http://schmelernicolas.name/amaya,,164.19.5.169,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n11049,4,247,2017-06-12 02:37:54,http://stehr.name/dakota,,41.165.53.173,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n11050,4,247,2017-05-03 05:00:37,http://ondrickakuhic.info/antonetta.wunsch,,113.86.213.121,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n11051,4,247,2017-03-27 22:49:16,http://kautzer.net/clint,,113.216.32.162,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n8016,3,177,2017-02-13 16:10:12,http://tremblay.io/kevin.mitchell,0.7810997006,20.173.121.193,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n8017,3,177,2017-02-06 18:59:04,http://schamberger.co/arnulfo,0.7099294106,46.253.149.132,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n8018,3,177,2017-03-19 00:31:30,http://rowe.org/vincenzo,0.9436079594,234.128.11.31,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n8019,3,177,2017-05-13 21:29:12,http://west.com/jasen,0.7213813267,107.147.38.173,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n8020,3,177,2016-12-28 17:47:45,http://kulashartmann.info/wyman,0.4970999526,166.241.117.205,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n8021,3,177,2017-02-16 16:44:00,http://marquardt.info/unique,0.9964751140,250.112.137.143,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n8022,3,177,2017-01-09 12:48:57,http://sipesfriesen.biz/miouri,0.3630616840,136.59.208.126,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n8023,3,177,2017-03-26 20:15:26,http://zboncak.biz/constance,0.1826403295,253.135.95.89,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n8024,3,177,2017-05-28 23:03:41,http://heathcote.com/zetta,0.7635537038,79.121.168.215,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n8025,3,177,2016-12-19 20:11:28,http://ritchie.info/alvah_mcglynn,0.8832455742,231.5.75.235,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n8026,3,177,2017-06-12 17:47:36,http://framialtenwerth.org/price,0.3869795801,104.168.198.37,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n8027,3,177,2016-12-26 10:24:12,http://langworthkuhn.info/bryon,0.5038616223,73.95.91.132,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n8028,3,177,2016-12-27 00:05:21,http://durgan.net/benton.mann,0.5662410665,187.195.123.188,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n8029,3,177,2016-12-29 01:39:36,http://hahnklein.biz/deshaun.cole,0.3156903187,148.75.124.8,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n8030,3,177,2017-05-01 13:58:40,http://cainpredovic.name/burley,0.0157745820,126.253.202.177,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n8031,3,177,2016-12-14 11:02:11,http://cummerata.info/priscilla_heathcote,0.7312232683,176.47.168.219,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n8032,3,177,2017-06-05 08:02:34,http://friesen.co/anahi,0.6738535086,190.176.70.107,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n8033,3,177,2017-02-01 20:11:50,http://kautzer.info/hope,0.0454966590,99.14.253.57,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n8034,3,177,2017-06-02 16:24:59,http://gleason.com/patience_mosciski,0.5459525637,137.188.154.193,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n8035,3,177,2017-05-10 13:15:19,http://jaskolski.net/gregoria,0.2487646364,111.79.97.241,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n8036,3,177,2017-03-23 17:12:31,http://quitzon.com/xander_schultz,0.6674486887,132.20.128.217,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n8037,3,177,2017-02-27 06:07:41,http://klein.io/henri_dickinson,0.8924257247,188.124.153.130,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n8038,3,177,2016-12-15 21:56:09,http://stromanorn.co/jadyn.fisher,0.9498596757,69.175.152.201,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n8039,3,177,2017-02-27 07:55:23,http://towne.io/francisco.schinner,0.7018704199,157.176.17.234,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n8040,3,177,2017-02-21 02:44:19,http://reinger.io/korey,0.9411329892,88.207.202.166,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n8041,3,177,2017-03-19 16:29:04,http://nitzsche.info/ubaldo_herman,0.5273322996,168.169.110.137,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n8042,3,177,2017-02-28 15:39:27,http://miller.biz/lulu_ebert,0.3420143253,196.6.118.32,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n8043,3,177,2017-05-04 23:01:42,http://collier.net/brandi,0.5612238369,138.4.85.44,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n8044,3,177,2017-02-28 10:50:35,http://heidenreichgoodwin.biz/bridie_shields,0.1416802464,138.22.36.5,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n8045,3,177,2017-06-03 22:28:03,http://walker.biz/idell,0.6302482235,211.209.232.100,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n8046,3,177,2017-01-21 14:30:29,http://mosciskikutch.co/thaddeus_wolf,0.1719313361,198.120.240.169,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n8047,3,177,2017-02-10 19:25:20,http://tremblaydooley.name/adrien_predovic,0.4279252972,21.121.4.192,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n8048,3,177,2017-02-03 09:16:46,http://murphybotsford.co/jalen,0.9963803364,79.73.208.138,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n8049,3,177,2017-06-11 11:56:29,http://ankunding.com/sylvan_fay,0.1971399844,184.253.4.89,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n8050,3,177,2017-04-13 23:13:36,http://wiza.info/maddison.nader,0.9019514855,49.238.41.68,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n8051,3,177,2017-04-22 15:35:43,http://rauhermann.info/tyrique,0.3842554954,21.10.96.115,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n8052,3,177,2016-12-17 06:15:38,http://wilderman.io/camren,0.0673269346,41.95.143.148,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n8053,3,177,2017-03-17 19:42:47,http://klockomaggio.info/lempi,0.2429432111,3.180.10.25,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n8054,3,177,2017-03-12 23:07:40,http://johnsonheaney.name/leon,0.3679755471,137.55.229.238,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n8055,3,177,2017-03-29 03:34:43,http://durgan.biz/guadalupe,0.3795920586,225.235.98.37,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n8056,3,177,2017-04-17 03:38:46,http://shanahan.info/shaniya_becker,0.5844643821,93.248.140.240,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n8057,3,178,2017-04-15 05:08:44,http://ward.name/kirk_hayes,0.5855662791,203.75.98.30,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n8058,3,178,2017-01-14 21:32:32,http://crooks.co/otilia,0.4926851585,68.104.67.230,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n8059,3,178,2017-03-06 09:19:29,http://rodriguezstark.co/ted.marquardt,0.9685450889,203.12.71.156,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n8060,3,178,2017-05-02 05:07:01,http://creminkaulke.info/lauren.ferry,0.6621673616,57.100.147.177,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n8061,3,178,2017-04-28 08:23:15,http://zemlak.com/chester,0.6646025622,105.52.247.56,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n8062,3,178,2017-06-09 23:40:54,http://damore.io/fern,0.1608152039,59.137.96.51,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n8063,3,178,2017-01-18 19:03:30,http://mrazsteuber.biz/cleo,0.2142916299,252.65.48.141,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n8064,3,178,2017-04-28 11:11:46,http://johns.io/reagan,0.9246977884,208.230.251.128,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n8065,3,178,2017-01-21 22:09:36,http://hyattdenesik.com/rhoda,0.2511539917,238.70.206.247,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n8066,3,178,2017-06-03 00:26:44,http://bins.co/caesar.olson,0.8350315986,252.238.108.160,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n13947,5,312,2017-05-02 10:48:36,http://runolfon.net/josefa,,125.23.207.227,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n13948,5,312,2017-04-10 13:10:52,http://spinka.net/christopher_fay,,173.220.218.78,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n13949,5,312,2016-12-28 06:24:34,http://willmskertzmann.biz/fannie.casper,,104.174.18.203,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n13950,5,312,2017-03-31 03:33:26,http://keeblerondricka.biz/joshuah,,140.30.188.202,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n13951,5,312,2017-02-05 14:00:54,http://johns.co/fredy.schmitt,,31.36.222.239,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n13952,5,313,2017-04-23 03:26:12,http://metzrempel.co/thelma_gulgowski,,251.148.250.17,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n13953,5,313,2017-05-31 18:11:10,http://nienow.com/frederik.gottlieb,,168.248.197.4,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n13954,5,313,2017-05-06 16:43:32,http://effertz.biz/june,,91.101.65.54,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n13955,5,313,2017-01-21 19:41:30,http://langoshmitchell.io/frederik_bailey,,123.111.160.37,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n13956,5,313,2017-06-08 06:09:48,http://borer.co/zander.wolff,,189.219.80.216,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n13957,5,313,2017-03-23 23:18:31,http://langworth.org/jaeden_brekke,,165.112.226.194,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n13958,5,313,2016-12-30 18:58:17,http://heelwisozk.net/sarah,,9.188.200.241,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n13959,5,313,2016-12-22 17:24:21,http://stoltenberg.info/santina.muller,,153.207.2.207,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n13960,5,313,2017-03-22 22:50:24,http://nikolaus.net/mafalda_smith,,83.74.88.254,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n13961,5,313,2017-04-06 02:02:34,http://rau.name/zola.schoen,,11.215.86.248,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n13962,5,313,2017-01-28 09:29:59,http://gulgowski.org/rolando_metz,,42.254.181.152,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n13963,5,313,2017-04-22 10:11:41,http://kshlerin.name/abelardo_schaefer,,62.216.181.147,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n13964,5,313,2017-06-12 00:29:10,http://gottlieb.net/aliya_kohler,,220.159.164.235,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n13965,5,313,2017-04-25 13:38:54,http://hackett.io/kayley,,144.110.86.213,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n13966,5,313,2017-01-15 18:23:49,http://corwinraynor.name/vivian_schmitt,,114.126.99.3,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n13967,5,313,2017-04-28 14:05:53,http://ziemannwolff.org/ubaldo,,120.99.143.40,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n13968,5,313,2017-02-15 14:27:12,http://ortiz.co/celestine,,15.247.232.93,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n13969,5,313,2017-05-25 05:36:44,http://powlowski.name/veronica.roob,,13.214.94.82,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n13970,5,313,2017-03-23 07:39:01,http://hamillhuels.name/evalyn,,198.151.165.211,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n13971,5,313,2017-02-17 13:40:08,http://howebruen.biz/wilhelmine,,39.74.85.124,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n13972,5,313,2017-02-03 01:06:55,http://smith.io/mark_mills,,89.21.221.233,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n13973,5,313,2016-12-23 08:48:07,http://hettingerluettgen.info/wyman,,224.145.67.129,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n13974,5,313,2017-04-05 09:55:27,http://cronindamore.name/irwin,,251.252.147.117,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n13975,5,313,2017-05-11 23:25:54,http://nolan.co/barry,,151.236.105.104,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n13976,5,313,2017-01-29 00:53:16,http://klingparker.com/coy,,124.211.252.187,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n13977,5,313,2017-03-10 10:55:27,http://wehner.org/blair_nolan,,145.182.181.66,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n13978,5,313,2017-06-07 20:39:14,http://uptonhalvorson.info/melyna,,174.62.79.230,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n13979,5,313,2017-01-08 06:40:35,http://vandervort.biz/okey,,150.181.205.67,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n13980,5,313,2017-04-08 06:26:02,http://purdy.com/romaine,,7.46.251.158,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n13981,5,313,2017-04-12 02:48:34,http://buckridge.com/efrain,,214.108.223.144,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n13982,5,313,2016-12-17 11:38:17,http://mayer.io/genesis.waters,,99.173.26.17,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n13983,5,314,2017-05-24 22:44:38,http://tillman.net/tyshawn_schuster,,92.59.35.61,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n13984,5,314,2017-03-31 13:10:26,http://homenickgerlach.com/garnet,,243.15.11.14,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n13985,5,314,2017-04-12 19:09:31,http://boylegrady.co/anibal.rolfson,,101.217.133.204,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n13986,5,314,2017-06-11 21:02:03,http://howellabernathy.com/kieran.weinat,,86.108.218.156,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n13987,5,314,2017-03-28 23:44:16,http://stokes.net/aurelio.altenwerth,,62.152.177.178,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n13988,5,314,2017-01-26 12:14:26,http://murazik.info/alex,,188.195.17.149,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n13989,5,314,2017-05-22 00:41:49,http://spencerhirthe.com/jevon_cain,,126.84.42.222,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n13990,5,314,2017-05-18 19:39:46,http://wilderman.com/lilla,,44.166.157.78,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n13991,5,314,2017-05-18 12:36:10,http://langworth.org/logan.larson,,34.177.174.221,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n13992,5,314,2017-04-29 02:44:35,http://beahan.name/kay,,166.14.40.136,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n13993,5,314,2017-04-27 17:55:02,http://kovacek.net/patsy,,141.130.145.39,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n13994,5,314,2017-05-06 12:04:02,http://millerking.io/thalia.volkman,,49.172.254.207,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n13995,5,314,2016-12-23 11:49:05,http://gutmannrobel.info/bernita,,41.176.249.59,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n13996,5,314,2017-03-06 16:34:33,http://pacochaheaney.net/adam.wyman,,212.238.182.147,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n13997,5,314,2017-05-20 10:51:06,http://upton.name/milan.boyer,,8.174.95.48,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n13998,5,314,2017-04-30 08:21:32,http://shanahan.org/darron_trantow,,165.90.198.24,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n13999,5,314,2017-04-18 22:18:59,http://kozey.net/devin,,2.88.185.182,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n14000,5,314,2017-04-15 14:46:01,http://thompson.org/tobin,,115.47.88.17,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n14001,5,314,2016-12-22 16:04:54,http://stokes.co/jedediah_satterfield,,37.249.189.203,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n16929,6,383,2017-02-20 15:30:39,http://powlowskiraynor.info/liza,,82.64.51.171,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n16930,6,383,2016-12-15 21:11:09,http://mcclure.org/twila_gleichner,,124.186.106.186,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n16931,6,383,2017-05-03 16:20:39,http://altenwerth.org/bret_borer,,39.135.150.245,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n16932,6,383,2017-02-20 01:42:11,http://schuster.biz/april,,5.239.130.40,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n16933,6,383,2017-05-22 13:03:07,http://fay.io/reid,,130.61.83.187,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n16934,6,383,2017-05-27 20:06:31,http://larkin.io/dimitri,,15.61.30.232,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n16935,6,383,2017-05-11 03:00:33,http://hermann.biz/lizeth,,134.197.65.103,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n16936,6,383,2016-12-25 09:09:16,http://connelly.name/allison_berge,,112.136.30.182,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n16937,6,383,2017-02-11 01:20:52,http://keeblerdooley.net/ashly,,131.250.139.33,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n16938,6,383,2017-02-08 11:28:56,http://mcglynn.co/marian,,60.237.195.179,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n16939,6,383,2016-12-26 02:21:12,http://kertzmann.co/edgardo,,112.151.116.170,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n16940,6,383,2017-04-19 18:38:26,http://schmidt.info/randal.greenholt,,169.246.109.11,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n16941,6,383,2017-04-07 02:25:53,http://rath.io/jeanette,,52.9.176.239,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n16942,6,383,2017-05-13 08:05:32,http://pfeffer.biz/grady.harvey,,19.37.129.118,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n16943,6,383,2017-03-29 22:26:42,http://sanford.biz/amely.leffler,,250.163.214.64,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n16944,6,383,2017-01-24 02:47:54,http://cummings.io/elna.mann,,198.137.60.125,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n16945,6,383,2017-03-22 00:57:35,http://dare.co/deondre_batz,,104.41.223.63,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n16946,6,383,2017-05-11 05:53:10,http://turnerkshlerin.biz/evan_larkin,,89.145.248.53,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n16947,6,383,2017-03-31 20:10:35,http://mcglynn.biz/cody,,183.246.221.76,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n16948,6,383,2017-03-03 17:57:40,http://veum.org/janea,,121.202.167.152,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n16949,6,383,2017-06-01 18:07:58,http://wittingcronin.net/ole.grant,,132.176.138.139,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n16950,6,383,2017-02-28 16:33:57,http://klockoaltenwerth.info/camilla,,206.180.130.97,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n16951,6,383,2017-02-12 21:16:21,http://bruen.io/heloise.murphy,,242.158.208.71,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n16952,6,383,2017-02-24 11:46:19,http://heidenreich.org/eloise_ebert,,165.140.154.109,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n16953,6,383,2017-05-31 22:07:37,http://keler.org/edna,,236.20.7.210,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n16954,6,383,2017-03-18 16:17:29,http://abbottjenkins.io/vella_crooks,,192.222.38.195,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n16955,6,383,2017-04-14 11:03:59,http://king.com/gabriel_hane,,63.142.82.166,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n17417,6,392,2017-04-28 17:02:00,http://bauch.io/devonte,,241.252.118.14,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n16956,6,383,2017-05-12 18:27:12,http://corkery.info/juanita.schowalter,,211.253.58.232,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n16957,6,383,2017-01-17 17:00:53,http://kuvalis.co/breanna,,143.138.29.216,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n16958,6,383,2017-03-12 05:16:30,http://bednardeckow.org/dawson_ullrich,,214.103.72.78,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n16959,6,383,2017-05-07 09:25:42,http://kerlukedurgan.name/adolf_stokes,,72.151.24.161,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n16960,6,383,2016-12-14 01:00:29,http://mckenzie.info/audra_oconner,,66.247.38.125,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n16961,6,383,2017-05-16 18:29:37,http://bashirian.org/iac,,62.76.235.245,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n16962,6,383,2017-02-04 02:22:30,http://dibberthegmann.net/jamel,,24.109.196.57,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n16963,6,383,2017-02-16 02:20:39,http://hilpert.co/cary,,250.239.129.96,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n16964,6,383,2017-02-26 13:01:22,http://fisher.name/effie.daniel,,185.192.212.152,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n16965,6,383,2017-05-15 00:51:58,http://wildermanlindgren.info/dameon.quitzon,,69.87.87.55,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n16966,6,384,2017-04-11 07:08:19,http://champlin.co/abraham,,99.128.151.235,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n16967,6,384,2017-01-09 09:33:25,http://spencer.com/zetta,,226.51.41.7,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n16968,6,384,2017-06-12 10:23:48,http://barrows.org/heather_davis,,197.213.155.164,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n16969,6,384,2017-05-25 03:00:12,http://fahey.name/silas,,95.111.65.21,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n16970,6,384,2017-05-21 18:52:10,http://parker.name/lonnie_krajcik,,227.125.167.123,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n16971,6,384,2017-02-20 08:37:48,http://osinski.name/lisette,,79.100.84.127,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n16972,6,384,2017-03-20 00:32:18,http://botsfordprohaska.net/leo,,89.175.30.76,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n16973,6,384,2017-01-24 21:47:25,http://nicolas.biz/lea,,85.166.123.10,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n16974,6,384,2016-12-31 17:03:14,http://oberbrunner.com/christelle,,154.89.92.55,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n16975,6,384,2017-03-26 03:16:12,http://dibbert.com/linda,,109.78.66.42,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n16976,6,384,2017-03-29 21:39:30,http://robel.io/lola,,173.51.48.27,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n16977,6,384,2016-12-16 02:40:34,http://prosaccotillman.name/alicia_bauch,,56.18.244.156,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n16978,6,384,2017-05-28 16:11:27,http://cormiergusikowski.co/lottie,,225.176.247.61,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n16979,6,384,2017-03-13 16:12:20,http://rau.com/alfred_kutch,,38.110.12.98,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n16980,6,384,2017-02-20 22:10:05,http://dooley.io/verdie,,88.198.21.90,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n16981,6,384,2017-02-26 10:29:51,http://hilll.io/judah,,39.92.92.86,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n16982,6,384,2017-02-28 10:12:52,http://townehagenes.info/ilene,,61.100.67.163,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n4109,2,90,2017-03-27 11:32:59,http://lehner.io/mohammed.feeney,,192.43.231.3,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n4110,2,90,2017-03-25 10:11:05,http://okeefe.biz/jonathan.gleason,,94.89.176.180,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n4111,2,90,2017-02-12 21:36:39,http://emmerichmayert.info/ignacio.beatty,,129.234.55.96,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n4112,2,90,2017-04-21 13:21:23,http://ondricka.co/evelyn,,80.211.244.253,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n4113,2,90,2016-12-28 14:17:57,http://lindwilderman.info/roderick.daugherty,,207.95.162.76,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n4114,2,90,2017-04-30 22:08:56,http://hyatt.io/berneice,,61.251.148.37,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n4115,2,90,2017-04-15 15:26:07,http://lemke.biz/otha.mante,,4.70.159.55,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n4116,2,90,2016-12-29 09:51:22,http://sporerschimmel.net/monserrate_deckow,,219.220.68.170,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n4117,2,90,2017-03-15 00:39:40,http://toy.io/aimee,,227.196.65.194,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n4118,2,90,2017-05-03 23:58:54,http://feil.info/edwardo.upton,,206.239.236.147,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n4119,2,90,2017-05-30 07:24:03,http://larsonlesch.org/dedrick_kshlerin,,114.108.101.60,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n4120,2,90,2017-04-30 17:59:18,http://schuppe.org/kacie,,29.215.211.121,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n4121,2,90,2017-01-19 06:19:15,http://gleason.net/megane_stoltenberg,,214.84.142.113,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n4122,2,90,2017-02-12 06:51:20,http://dubuque.biz/torrey,,247.52.174.184,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n4123,2,90,2017-04-08 10:33:33,http://rennerharris.org/tyree.hilll,,233.156.181.124,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n4124,2,90,2017-04-02 13:15:28,http://fahey.com/mariam_pacocha,,167.254.172.198,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n4125,2,90,2017-03-29 20:23:32,http://hyattupton.io/fredrick.labadie,,101.127.239.213,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n4126,2,90,2017-03-02 14:25:06,http://pacocha.name/walton.jenkins,,222.237.19.26,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n4127,2,90,2017-04-26 16:32:31,http://mclaughlin.net/juwan.gusikowski,,197.209.84.221,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n4128,2,90,2017-03-01 11:10:50,http://corkery.io/rosella.kutch,,251.221.119.168,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n4129,2,90,2017-05-28 12:31:51,http://schinner.io/david_raynor,,207.157.213.208,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n4130,2,90,2017-03-06 03:56:18,http://kertzmann.io/adolph,,3.179.179.118,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n4131,2,90,2016-12-21 09:02:09,http://wunschkris.biz/scotty,,181.104.65.80,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n4132,2,90,2017-01-10 09:33:37,http://nadergrant.co/isadore.stamm,,204.122.13.99,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n4133,2,90,2017-02-14 13:08:12,http://schusterharris.org/regan,,83.237.153.195,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n4134,2,90,2017-02-28 22:18:13,http://emard.net/lambert.yundt,,249.17.63.191,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n4135,2,90,2016-12-14 14:05:31,http://fahey.co/elise_harris,,131.253.170.169,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n4136,2,90,2017-05-28 18:52:29,http://stokes.name/madisen_hodkiewicz,,219.249.121.217,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n4137,2,90,2017-02-14 22:02:52,http://cormier.io/vallie,,115.168.136.143,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n4138,2,90,2017-02-25 12:36:00,http://champlin.co/tamara_stark,,165.65.217.113,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n4139,2,90,2017-01-08 22:12:45,http://herzog.info/geraldine,,79.119.98.55,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n4140,2,90,2016-12-25 23:07:28,http://hoppe.info/juliet,,164.152.196.138,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n4141,2,90,2016-12-23 16:02:29,http://considine.co/georgette_wiegand,,46.69.133.154,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n4142,2,90,2017-02-22 15:33:07,http://reynolds.info/jayde,,28.241.94.151,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n4143,2,90,2016-12-13 17:12:59,http://dooley.net/herminio,,254.58.156.236,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n4144,2,90,2017-04-12 02:22:38,http://schumm.com/daron,,119.181.94.218,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n4145,2,90,2017-03-14 17:38:41,http://bogan.co/lorine,,140.218.192.37,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n4146,2,90,2017-04-14 01:20:48,http://welch.name/melyna_beatty,,134.209.179.253,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n4147,2,90,2017-04-20 10:06:38,http://rogahn.io/tyson,,109.44.141.196,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n4148,2,90,2017-05-05 18:12:10,http://heaneycrist.info/cristobal,,152.33.71.251,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n4149,2,90,2017-03-25 19:12:55,http://schulist.com/aaliyah,,70.164.151.174,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n4150,2,90,2017-03-15 16:00:54,http://hayes.net/marjorie,,100.92.251.78,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n4151,2,90,2017-05-04 17:00:24,http://koch.com/william,,203.243.82.219,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n4152,2,90,2017-02-07 08:44:00,http://rutherford.com/emely,,29.39.200.122,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n4153,2,90,2017-04-09 12:48:51,http://goldner.com/arlo_bradtke,,39.80.72.83,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n4154,2,90,2017-06-06 16:23:20,http://larsonmcclure.co/estrella.kiehn,,249.9.82.36,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n4155,2,90,2017-02-11 15:07:15,http://green.co/andreane.goldner,,160.253.5.38,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n4156,2,91,2017-03-22 15:42:33,http://sawayn.name/eldora,,65.106.63.51,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n4157,2,91,2017-05-16 02:22:52,http://beierwindler.biz/jamarcus,,37.106.50.164,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n4158,2,91,2017-03-07 14:04:35,http://jast.com/ron_mraz,,2.83.218.113,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n4159,2,91,2016-12-27 08:33:08,http://sauer.co/sierra_kunze,,94.73.226.176,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n4160,2,91,2017-01-06 22:28:52,http://eichmann.co/riley,,208.83.216.183,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n4161,2,91,2017-04-13 18:28:01,http://bashirian.com/barry_little,,147.39.205.217,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n4162,2,91,2017-01-27 21:31:14,http://dibbertdickens.org/nels,,159.62.233.59,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n4163,2,91,2017-06-02 18:05:56,http://bartoletti.net/dorris.marvin,,167.53.90.27,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n4164,2,91,2017-01-07 19:14:11,http://connelly.info/lavon.ryan,,213.132.168.128,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n11052,4,247,2017-03-19 15:12:53,http://reichel.info/melba,,236.15.49.212,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n11053,4,247,2017-02-05 23:46:38,http://fritschrohan.biz/frederic,,16.238.44.192,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n11054,4,247,2017-02-02 11:59:25,http://stammcole.net/brianne_keeling,,52.153.63.167,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n11055,4,247,2017-01-10 18:03:34,http://mayer.biz/marguerite,,164.134.143.217,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n11056,4,247,2016-12-29 08:10:56,http://hilpertdach.co/olen_buckridge,,14.180.173.30,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n11057,4,247,2017-05-16 15:32:40,http://thompson.org/carolina,,40.245.57.31,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n11058,4,247,2017-03-27 21:16:39,http://millsrath.net/elisabeth.kuphal,,57.191.228.228,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n11059,4,247,2017-04-11 05:37:29,http://nienowjenkins.name/gaetano.welch,,18.140.125.194,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n11060,4,247,2017-01-03 13:32:48,http://cartwright.co/colt.bashirian,,92.134.110.179,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n11061,4,247,2017-04-20 18:09:29,http://ankundinglarson.co/lorenza,,9.73.191.42,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n11062,4,247,2017-01-23 05:44:39,http://cormier.net/norris_gaylord,,117.63.47.60,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n11063,4,247,2017-06-02 23:13:56,http://weber.io/lee,,32.84.172.220,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n11064,4,247,2017-05-12 08:57:37,http://ortiz.com/breanne,,159.4.244.67,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n11065,4,247,2017-04-18 11:20:29,http://bernharddavis.name/tyler,,184.217.219.216,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n11066,4,247,2017-01-12 19:22:41,http://parisian.net/sierra_senger,,202.27.100.8,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n11067,4,247,2017-02-18 14:20:11,http://oreilly.co/myron,,51.193.16.17,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n11068,4,247,2017-02-06 15:10:44,http://lueilwitz.org/ozella_homenick,,164.191.177.214,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n11069,4,247,2017-04-30 08:07:32,http://ankundingraynor.co/pearl.ruecker,,37.65.101.186,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n11070,4,247,2016-12-30 18:46:58,http://wilderman.com/zackary.grant,,204.111.60.3,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n11071,4,247,2017-05-13 21:10:15,http://morar.org/xzavier,,186.229.160.47,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n11072,4,247,2017-04-01 17:10:14,http://altenwerth.com/raina_brown,,7.141.91.78,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n11073,4,247,2017-01-01 23:41:16,http://welchgoyette.org/elisha,,36.78.32.240,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n11074,4,247,2017-01-19 15:22:37,http://schneiderbayer.com/joanne,,139.87.151.84,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n11075,4,247,2017-06-06 03:57:17,http://donnelly.net/alta,,205.71.118.145,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n11076,4,247,2017-04-25 05:30:59,http://herzog.info/ferne,,86.69.195.197,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n11077,4,247,2017-01-22 04:07:32,http://walsh.org/nestor,,238.205.113.196,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n11078,4,247,2017-04-28 20:30:02,http://cruickshank.io/valerie,,22.238.43.58,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n11079,4,247,2017-06-07 01:46:29,http://fadelkulas.info/jamaal.rogahn,,3.12.64.182,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n11080,4,247,2017-03-29 12:48:15,http://tromp.info/eudora,,213.115.219.208,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n11081,4,247,2017-02-18 05:50:51,http://cummings.net/jerald,,178.101.124.88,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n11082,4,248,2016-12-30 15:50:25,http://kreiger.io/earline_schmeler,,198.72.222.34,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n11083,4,248,2017-04-04 12:45:23,http://lehner.co/lavinia_treutel,,165.39.213.218,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n11084,4,248,2017-01-02 01:46:22,http://ortiz.net/trystan.marvin,,23.243.38.223,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n11085,4,248,2017-02-12 09:38:42,http://barrowscrona.biz/rachelle_reinger,,136.144.189.201,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n11086,4,248,2017-04-28 01:20:22,http://bode.com/tia,,198.138.176.165,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n11087,4,248,2017-06-07 06:33:54,http://schowalter.co/antonetta.watsica,,114.203.226.44,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n11088,4,248,2017-04-06 02:06:24,http://rippinparisian.biz/kenya_wiza,,67.237.97.248,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n11089,4,248,2017-04-19 15:40:49,http://pollichnitzsche.info/rose.ebert,,156.129.141.227,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n11090,4,248,2017-02-26 15:01:22,http://wolfwaelchi.co/brielle,,73.36.202.234,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n11091,4,248,2017-04-22 00:39:03,http://wehnerharris.com/kelsi.batz,,103.178.76.24,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n11092,4,248,2017-04-09 04:27:57,http://langosh.org/ettie,,22.149.177.7,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n11093,4,248,2017-05-20 00:55:24,http://kris.io/ryder.huel,,97.216.131.116,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n11094,4,248,2017-04-23 13:28:11,http://carroll.io/bettie,,127.162.218.173,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n11095,4,248,2017-06-08 10:40:22,http://sipes.biz/bernadine.balistreri,,152.127.133.21,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n11096,4,248,2017-05-06 10:20:44,http://damore.biz/joanie,,180.62.242.100,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n11097,4,248,2017-04-08 05:25:45,http://monahan.com/dina.davis,,242.64.149.77,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n11098,4,248,2017-03-15 18:33:18,http://hoppe.co/sheila.renner,,65.148.34.19,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n11099,4,248,2017-02-01 12:50:30,http://considine.name/britney_johnson,,40.224.254.212,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n11100,4,248,2017-05-22 03:14:57,http://pouros.org/keanu.swift,,66.203.111.86,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n11101,4,248,2017-02-02 14:58:21,http://schoen.co/keshawn.hahn,,233.46.160.139,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n11102,4,248,2017-03-20 23:23:10,http://batz.org/deion,,133.20.168.193,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n11103,4,248,2017-06-05 03:33:41,http://cormierhilll.info/cody_maggio,,115.63.207.143,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n11104,4,248,2017-03-05 16:21:20,http://kelergoyette.biz/kelsie_berge,,24.178.86.97,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n11105,4,248,2017-05-09 21:12:14,http://cain.info/braulio,,206.169.244.62,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n11106,4,248,2017-02-18 03:21:45,http://boyle.biz/meggie,,131.141.238.137,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n8067,3,178,2017-04-26 00:27:02,http://gutmannconn.net/lon_waelchi,0.0769093221,57.19.69.50,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n8068,3,178,2017-01-24 20:06:33,http://cain.net/vern,0.6347114855,195.90.35.63,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n8069,3,178,2017-02-01 21:54:32,http://gerlach.io/emie,0.9543811588,75.195.26.87,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n8070,3,178,2017-02-14 23:00:33,http://stehrkovacek.io/wyatt,0.3270426849,94.3.182.235,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n8071,3,178,2017-03-30 09:53:25,http://altenwerth.org/alfred_fritsch,0.4380045352,44.78.148.132,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n8072,3,178,2016-12-25 23:17:50,http://crooks.biz/yesenia_fay,0.6964699730,206.10.120.6,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n8073,3,178,2017-05-14 13:21:11,http://weimann.name/ofelia,0.7261745148,83.117.73.154,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n8074,3,178,2017-03-28 03:04:43,http://borer.com/eleanore,0.0639538618,85.109.200.144,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n8075,3,178,2017-04-27 14:20:03,http://ratke.io/jimmy,0.1333861617,15.180.29.162,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n8076,3,178,2017-04-10 14:59:01,http://reichel.co/tyra_koelpin,0.2490538686,113.63.119.110,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n8077,3,178,2017-02-14 17:30:41,http://carroll.org/gretchen,0.8751089163,43.247.7.202,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n8078,3,178,2017-03-03 14:20:07,http://dicki.io/melya,0.5179380654,129.205.154.240,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n8079,3,178,2017-04-12 07:49:35,http://goodwinsmitham.org/cameron.goldner,0.7173328295,244.62.96.220,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n8080,3,178,2017-03-13 06:20:12,http://wuckert.org/javonte_leuschke,0.8576367199,94.199.191.199,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n8081,3,178,2017-02-13 12:40:54,http://swift.co/alphonso_graham,0.7844783763,216.208.125.128,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n8082,3,178,2016-12-19 06:24:01,http://walkerzulauf.com/destini,0.8173151043,223.3.80.14,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n8083,3,178,2016-12-29 19:59:49,http://blanda.com/raymond,0.9603764927,208.35.237.81,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n8084,3,178,2017-02-01 14:07:36,http://kaulke.org/alexander,0.0795164130,195.83.189.219,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n8085,3,178,2017-02-03 18:44:19,http://hudson.info/elta,0.0963196862,51.132.130.232,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n8086,3,178,2017-03-20 15:06:56,http://krajcik.co/michelle,0.6721164948,41.193.189.62,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n8087,3,178,2017-01-26 01:04:03,http://stehrwalker.name/zetta.bins,0.9919396622,80.248.162.16,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n8088,3,178,2016-12-18 12:11:15,http://schowalter.name/ladarius_wolff,0.5800206024,58.226.123.189,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n8089,3,178,2017-06-10 12:48:39,http://wuckert.net/chanel.weimann,0.4769377616,106.106.10.17,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n8090,3,178,2017-01-25 05:57:00,http://walker.com/logan.terry,0.6884506442,201.68.159.203,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n8091,3,178,2017-05-09 01:13:06,http://wehneroberbrunner.org/misael,0.7741462301,136.39.153.127,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n8092,3,178,2017-02-06 09:36:38,http://quitzonfarrell.org/ozzie_stark,0.2989137874,201.223.143.148,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n8093,3,178,2017-01-12 03:13:16,http://streich.co/joelle,0.0064804259,216.237.27.134,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n8094,3,178,2017-02-18 22:03:58,http://stokes.net/beau,0.2332959552,227.37.114.91,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n8095,3,178,2017-05-17 19:11:55,http://boehmschulist.com/jackson,0.2631739185,32.161.140.123,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n8096,3,178,2017-02-10 15:34:52,http://kautzer.net/hannah,0.6927469619,231.30.202.237,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n8097,3,178,2017-03-13 17:06:20,http://weimannwillms.info/destany,0.8697490400,157.81.223.139,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n8098,3,178,2017-03-01 14:04:34,http://braun.name/marcellus.bartoletti,0.8329557915,54.81.12.161,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n8099,3,178,2017-02-27 17:27:41,http://quigley.com/lori_lowe,0.5676554910,75.98.53.148,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n8100,3,178,2017-03-16 06:23:55,http://oconner.net/zola.konopelski,0.1195646064,110.86.122.155,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n8101,3,178,2017-02-27 19:44:19,http://hermiston.org/horacio,0.4128763155,196.102.22.88,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n8102,3,178,2016-12-24 06:00:36,http://mccullough.info/abbigail,0.9504996729,36.64.58.9,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n8103,3,178,2017-03-03 21:43:57,http://grady.info/karlee,0.5790219514,22.80.26.237,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n8104,3,178,2017-02-10 05:22:14,http://koch.io/micaela,0.6643691624,179.152.237.9,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n8105,3,178,2017-05-14 12:28:00,http://hettinger.com/jaquelin,0.8580395068,90.250.219.187,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n8106,3,178,2017-04-08 17:25:28,http://langlind.info/lillian,0.1030408438,19.206.151.112,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n8107,3,178,2017-04-29 20:18:21,http://kemmer.name/lane_labadie,0.2583597282,143.101.54.250,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n8108,3,178,2016-12-23 04:47:36,http://conroyflatley.com/rubie.gorczany,0.9906786065,210.126.213.76,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n8109,3,178,2017-05-02 06:14:34,http://huel.io/sincere,0.7386507419,50.186.193.99,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n8110,3,178,2017-01-11 07:15:48,http://gulgowskiglover.co/charity,0.7098372113,106.172.225.187,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n8111,3,178,2017-06-09 14:06:59,http://turcotteschiller.co/omari.hermiston,0.6701468437,104.252.49.160,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n8112,3,178,2017-05-30 03:35:28,http://runtetorphy.co/stacey,0.5286765634,57.141.214.132,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n8113,3,178,2017-02-19 20:48:53,http://hettinger.com/deanna.crooks,0.6388499393,253.89.115.251,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n8114,3,178,2017-05-26 09:56:20,http://mills.org/dianna,0.1453451287,24.248.47.39,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n8115,3,178,2017-05-14 08:08:53,http://ritchie.org/ola,0.2790327548,235.23.21.97,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n8116,3,178,2017-05-24 19:42:04,http://legros.info/oren.schroeder,0.4277866950,66.118.188.247,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n8117,3,179,2017-01-02 19:08:53,http://mills.com/lewis,0.1480287664,62.122.106.196,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n8118,3,179,2016-12-24 08:18:16,http://zemlak.co/cornelius_schultz,0.3196769632,92.197.181.102,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n14002,5,314,2017-05-28 23:01:57,http://rodriguez.info/mara.kihn,,190.47.176.97,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n14003,5,314,2016-12-14 23:48:40,http://keeblerbrown.net/carter.will,,189.118.125.4,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n14004,5,314,2017-05-28 06:54:45,http://ebert.name/monica.nader,,142.81.17.143,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n14005,5,314,2017-03-23 00:12:32,http://mann.info/mathilde_wisoky,,9.63.218.2,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n14006,5,314,2017-01-17 10:37:06,http://quitzon.net/maximilian.kshlerin,,37.146.69.89,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n14007,5,314,2017-05-10 22:11:38,http://nicolas.net/travis_purdy,,54.64.27.174,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n14008,5,314,2017-04-07 01:16:27,http://bergstromhoppe.co/wiley.oberbrunner,,241.40.154.90,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n14009,5,314,2017-03-17 20:55:21,http://boylegrady.org/alexanne,,94.143.29.11,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n14010,5,314,2016-12-26 01:53:58,http://bergstrom.biz/maxie,,245.225.240.96,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n14011,5,314,2017-05-05 13:30:24,http://boyle.org/joshuah.hartmann,,13.108.251.64,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n14012,5,314,2017-01-30 12:18:17,http://runolfonbarrows.com/turner.waelchi,,65.57.72.231,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n14013,5,314,2017-04-06 22:32:52,http://trantow.info/angelita_mitchell,,136.213.27.58,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n14014,5,314,2017-01-23 21:09:52,http://naderbeer.co/grayson,,165.157.153.78,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n14015,5,314,2017-02-13 16:22:49,http://oconnerwalsh.biz/tyler_tromp,,178.180.103.108,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n14016,5,314,2017-01-30 04:07:50,http://rogahnrunte.info/scotty,,249.167.83.42,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n14017,5,314,2017-02-17 09:36:34,http://grady.com/tate,,205.228.248.31,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n14018,5,314,2017-05-24 22:06:31,http://goldner.com/horacio,,35.223.86.72,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n14019,5,314,2016-12-18 00:56:06,http://schiller.biz/wilfrid,,7.35.9.84,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n14020,5,314,2017-01-03 04:27:54,http://fay.com/spencer.grimes,,59.107.153.121,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n14021,5,314,2017-03-08 10:04:00,http://pfeffer.info/eldridge_kovacek,,147.218.59.65,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n14022,5,314,2017-04-03 02:00:42,http://witting.com/stephany_batz,,124.52.237.76,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n14023,5,314,2017-05-22 22:18:02,http://grimes.name/percival,,207.159.237.78,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n14024,5,314,2017-05-08 11:15:23,http://feeneyarmstrong.name/shania.hermann,,233.150.152.208,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n14025,5,314,2017-03-08 20:09:09,http://effertzbotsford.co/jamey.erdman,,61.193.217.84,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n14026,5,314,2016-12-25 12:24:21,http://shanahan.biz/adolphus,,21.218.149.137,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n14027,5,314,2016-12-28 12:49:40,http://christiansen.co/toby.ruecker,,189.159.139.190,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n14028,5,314,2017-03-03 20:02:20,http://anderson.co/shad,,139.141.244.242,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n14029,5,314,2017-01-03 21:19:32,http://trantow.co/frederic,,163.16.207.122,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n14030,5,314,2017-04-21 18:19:34,http://mann.com/elise.bednar,,139.60.201.217,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n14031,5,314,2017-04-09 06:33:48,http://lebsack.net/eugenia,,201.62.101.102,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n14032,5,314,2017-05-14 14:26:34,http://cronin.com/keanu_barrows,,8.20.6.148,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n14033,5,314,2017-01-12 02:43:49,http://jacobiadams.net/johnson_weinat,,121.115.171.224,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n14034,5,314,2017-03-28 09:38:26,http://runte.io/edgar_rempel,,22.185.248.94,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n14035,5,314,2017-03-01 06:03:46,http://borer.co/cecelia,,219.11.86.129,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n14036,5,314,2016-12-25 07:57:27,http://halvorson.org/thea,,208.82.97.251,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n14037,5,314,2017-05-22 05:47:24,http://larsonsporer.com/anibal_nader,,239.19.132.245,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n14038,5,314,2017-05-16 05:33:50,http://nader.net/jarred,,115.155.181.254,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n14039,5,314,2017-03-24 16:59:46,http://watsica.name/dylan.johnston,,50.167.103.185,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n14040,5,314,2017-02-02 23:16:39,http://greenfelder.org/kaylah,,72.150.130.150,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n14041,5,314,2017-02-20 09:28:39,http://rice.net/haskell,,95.215.240.36,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n14042,5,314,2017-01-03 05:07:03,http://jones.info/emilia,,110.171.216.242,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n14043,5,315,2017-06-12 23:48:24,http://adams.info/morris_tremblay,,87.97.216.68,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n14044,5,315,2017-04-25 21:52:26,http://connellyhills.org/lindsey.yundt,,54.142.12.188,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n14045,5,315,2016-12-29 12:51:06,http://feeney.com/jamarcus.bauch,,188.251.168.73,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n14046,5,315,2016-12-27 05:51:30,http://murray.com/ron,,247.165.130.70,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n14047,5,315,2017-04-30 20:58:17,http://keler.co/elmira,,185.135.147.33,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n14048,5,315,2017-01-06 11:35:29,http://schoen.com/jaron,,238.55.237.122,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n14049,5,315,2017-03-12 04:57:14,http://sauer.net/albertha,,73.167.124.164,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n14050,5,315,2017-01-31 04:10:54,http://turnerkoch.name/rene,,172.119.40.158,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n14051,5,315,2017-05-19 22:08:54,http://green.io/anne,,63.9.169.102,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n14052,5,315,2017-02-24 23:13:22,http://gottlieb.co/francesco.gleason,,197.45.140.235,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n14053,5,315,2017-01-06 08:18:06,http://ebertschiller.com/alene.wiza,,242.20.250.229,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n14054,5,315,2017-04-11 08:13:52,http://west.org/kristin,,230.254.10.203,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n14055,5,315,2017-03-08 13:10:55,http://handtorp.name/buford.prohaska,,110.68.18.230,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n14056,5,315,2017-01-11 10:25:33,http://hodkiewiczflatley.net/brooklyn_sawayn,,84.23.61.254,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n16983,6,384,2017-01-01 17:59:31,http://turner.org/kevin,,33.245.212.28,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n16984,6,384,2017-03-13 22:13:45,http://prohaska.net/rosie_kohler,,249.184.212.220,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n16985,6,384,2017-02-13 20:48:19,http://rooborn.com/yasmin.mckenzie,,104.214.38.68,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n16986,6,384,2017-04-06 07:44:42,http://hoppemitchell.org/reie_grant,,175.86.102.135,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n16987,6,384,2017-06-06 14:11:02,http://collins.com/bailee,,209.94.18.120,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n16988,6,384,2017-04-24 04:32:07,http://harriscarroll.biz/alana,,200.126.218.178,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n16989,6,384,2017-01-31 09:34:06,http://muller.com/leilani,,82.28.108.175,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n16990,6,384,2017-05-06 11:39:33,http://bednarbogisich.net/serena.breitenberg,,240.57.217.217,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n16991,6,384,2017-01-23 18:57:07,http://simonis.biz/margarette,,163.21.123.25,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n16992,6,384,2017-06-01 08:47:17,http://green.io/arvid.littel,,185.9.132.180,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n16993,6,384,2017-03-22 12:51:23,http://mertz.name/adah,,113.6.29.235,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n16994,6,384,2017-05-26 04:27:51,http://hartmannpowlowski.com/rodger,,56.162.139.196,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n16995,6,384,2017-01-01 14:20:18,http://borergrimes.net/cullen.ko,,198.151.108.141,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n16996,6,385,2017-05-23 11:01:42,http://quitzon.com/karina,,157.160.245.108,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n16997,6,385,2016-12-21 09:08:41,http://cruickshank.info/phoebe_grady,,56.26.203.118,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n16998,6,385,2017-04-28 02:01:18,http://sawayn.biz/ruthe.harris,,209.128.27.92,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n16999,6,385,2017-05-18 17:00:58,http://ortiz.com/romaine,,128.68.209.254,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n17000,6,385,2016-12-17 22:57:38,http://west.biz/eleazar.bayer,,157.211.71.20,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n17001,6,385,2017-01-26 09:09:42,http://stoltenberg.com/daniella.frami,,123.41.129.83,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n17002,6,385,2017-03-21 10:12:02,http://marquardt.biz/rollin,,251.30.154.237,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n17003,6,385,2016-12-17 04:21:23,http://orn.io/raven.lubowitz,,177.245.239.215,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n17004,6,385,2017-04-18 03:29:14,http://kunzehackett.biz/zoe,,3.183.224.136,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n17005,6,385,2017-06-07 20:16:36,http://thielmclaughlin.org/elva.runte,,169.147.131.98,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n17006,6,385,2017-01-19 06:13:32,http://stanton.io/joannie_macejkovic,,153.45.143.73,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n17007,6,385,2017-03-14 01:14:09,http://durgan.org/ruell.sauer,,217.130.168.48,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n17008,6,385,2016-12-19 00:51:28,http://jerde.io/billie,,162.128.200.158,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n17009,6,385,2017-04-19 14:23:38,http://bernier.net/annie,,101.101.223.33,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n17010,6,385,2017-06-08 17:54:35,http://gradyswift.info/rudy,,87.212.183.221,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n17011,6,385,2017-04-22 12:33:25,http://graham.co/eugenia,,234.143.230.221,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n17012,6,385,2017-01-31 07:03:58,http://grady.co/gail,,181.248.220.18,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n17013,6,385,2017-03-07 08:03:10,http://osinski.org/vivien,,105.24.175.117,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n17014,6,385,2017-01-27 09:29:14,http://mayer.info/arianna.hand,,25.237.36.65,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n17015,6,385,2017-02-10 16:21:27,http://hartmannluettgen.io/clemmie.adams,,69.42.192.172,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n17016,6,385,2017-04-10 06:44:19,http://townemccullough.io/cloyd_huels,,174.252.25.58,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n17017,6,385,2017-05-30 16:34:31,http://beahan.org/aylin,,140.237.50.161,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n17018,6,385,2017-04-29 05:53:28,http://satterfield.biz/mara.littel,,99.204.128.93,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n17019,6,385,2017-03-25 02:38:10,http://thompson.biz/peter,,113.202.232.201,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n17020,6,385,2017-05-10 21:17:07,http://reichert.net/sylvan,,178.38.226.168,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n17021,6,385,2017-03-02 01:45:50,http://littel.com/dino_littel,,206.116.68.151,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n17022,6,385,2017-01-09 15:44:47,http://mccullough.com/manuela,,228.176.30.26,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n17023,6,385,2017-06-10 22:00:02,http://fadel.name/bradford_quitzon,,167.160.208.173,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n17024,6,385,2017-02-19 22:11:28,http://murphy.com/willa_heidenreich,,181.99.23.74,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n17025,6,385,2017-04-19 08:00:25,http://johnston.name/jerrell,,250.190.145.98,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n17026,6,385,2017-03-16 19:22:24,http://moore.com/keagan_carter,,244.185.130.58,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n17027,6,385,2017-04-11 02:22:40,http://jaskolski.io/agnes.damore,,45.136.101.22,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n17028,6,385,2017-04-23 13:40:40,http://marquardt.com/dawson,,130.218.82.155,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n17029,6,385,2017-04-08 04:17:52,http://hayeskutch.com/raven.mcclure,,201.164.231.248,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n17030,6,385,2017-02-07 11:35:16,http://terrycarroll.io/judge_welch,,34.207.14.121,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n17031,6,385,2017-02-11 13:31:27,http://gerlachhaley.name/hilda,,77.70.38.252,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n17032,6,385,2017-05-17 13:04:45,http://hintzhauck.co/candida,,221.239.124.104,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n17033,6,385,2016-12-23 08:41:16,http://bashirian.name/kaycee.stanton,,163.108.25.75,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n17034,6,385,2017-04-07 08:15:51,http://kling.name/alexandria,,217.24.133.62,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n17035,6,385,2017-04-29 01:40:18,http://weber.name/orval.torp,,225.87.208.110,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n17036,6,385,2017-05-01 17:15:19,http://mann.biz/barbara,,156.199.72.7,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n17037,6,385,2017-03-24 21:11:38,http://larkin.co/pink.rath,,110.183.102.134,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n17038,6,385,2017-02-05 05:35:36,http://cain.co/melisa,,12.120.77.14,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n4165,2,91,2017-06-04 17:49:03,http://nienowweimann.org/brendon_price,,172.231.231.128,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n4166,2,91,2017-06-09 10:19:34,http://hermistonhamill.com/kiara,,44.233.246.114,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n4167,2,91,2017-04-03 05:04:11,http://strackecain.name/blair.stracke,,19.163.88.229,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n4168,2,91,2017-05-31 23:32:27,http://langosh.info/gerda.kuhn,,165.135.115.15,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n4169,2,91,2017-01-16 10:12:03,http://connellycartwright.co/oran.roob,,142.4.98.91,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n4170,2,91,2016-12-18 20:49:38,http://fisher.info/clinton,,70.44.115.250,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n4171,2,91,2017-05-18 21:57:55,http://hegmannfahey.biz/dasia_bartell,,145.164.161.156,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n4172,2,91,2017-05-16 15:51:52,http://abshire.biz/keeley_cole,,120.170.210.34,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n4173,2,91,2017-02-01 19:56:22,http://naderbergnaum.org/priscilla.towne,,193.19.158.18,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n4174,2,91,2017-01-30 15:15:58,http://smithmills.org/hollis.bednar,,20.122.136.215,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n4176,2,91,2017-03-13 15:30:28,http://lefflermcdermott.name/seth,,201.245.56.169,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n4177,2,91,2017-04-28 11:19:16,http://wardgraham.com/kelley_buckridge,,200.171.41.169,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n4178,2,91,2016-12-25 15:08:39,http://champlinpowlowski.com/laney,,250.8.59.95,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4179,2,91,2017-05-13 18:33:43,http://wolf.name/alena_kuhlman,,107.12.44.61,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n4180,2,91,2017-03-08 01:24:33,http://beer.info/elizabeth,,9.63.149.113,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n4181,2,91,2017-05-15 11:36:57,http://collier.com/dorothea.kuhlman,,204.225.88.80,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n4182,2,91,2017-04-10 22:17:06,http://quigleywest.io/trenton,,206.188.51.116,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n4183,2,92,2017-04-27 11:22:06,http://damorekerluke.co/marianna,,97.22.10.241,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n4184,2,92,2017-06-08 15:53:15,http://hoegermoriette.info/cesar.kuphal,,38.223.116.174,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n4185,2,92,2017-02-12 19:14:16,http://renner.info/fanny.fay,,197.229.122.229,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n4186,2,92,2016-12-15 12:07:25,http://weber.co/ruben,,131.181.235.170,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n4187,2,92,2017-03-23 11:12:47,http://considinehudson.com/luther_stehr,,27.188.158.121,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n4188,2,92,2017-03-05 21:10:32,http://eichmann.info/kendall,,185.186.207.161,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n4189,2,92,2017-05-22 22:46:49,http://kubschowalter.name/alf.baumbach,,205.62.178.126,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n4190,2,92,2017-01-09 12:35:17,http://miller.info/wanda,,80.165.84.247,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n4191,2,92,2017-05-23 19:56:45,http://loweberge.net/tania,,159.223.6.44,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n4192,2,92,2017-02-22 21:40:16,http://ratke.co/paxton.larson,,82.216.199.5,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n4193,2,92,2017-02-01 21:15:48,http://gottlieb.co/maxine,,85.119.156.149,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n4194,2,92,2017-04-23 21:11:56,http://nader.com/kathlyn.walsh,,145.164.140.187,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n4195,2,92,2017-06-08 02:08:36,http://macgyver.co/ole.king,,93.62.5.71,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n4196,2,92,2017-03-06 22:36:17,http://hoppeabbott.co/cooper,,174.149.116.53,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n4197,2,92,2017-01-05 02:50:04,http://hettingerstark.biz/claria,,217.83.62.162,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n4198,2,92,2016-12-31 17:32:10,http://mante.info/jennifer.murphy,,222.89.10.148,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n4199,2,92,2017-04-01 14:50:03,http://strosin.info/shana.feest,,246.17.41.5,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n4200,2,92,2017-05-21 23:40:19,http://johnston.com/bruce,,72.203.24.209,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n4201,2,92,2017-04-19 20:11:06,http://buckridgedaniel.co/camren,,40.55.114.94,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n4202,2,92,2016-12-14 14:04:17,http://koch.org/hoyt,,67.58.89.163,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n4203,2,92,2017-02-04 20:47:30,http://kerluke.co/easton,,79.137.214.68,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n4204,2,92,2017-01-28 10:27:48,http://zboncak.com/tatum_fisher,,156.128.70.21,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n4205,2,92,2017-05-12 11:13:59,http://willms.co/jamal,,4.192.43.235,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n4206,2,92,2017-05-13 14:18:59,http://mills.com/dallas,,145.154.91.130,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n4207,2,92,2017-05-31 14:13:52,http://deckow.info/mylene_kshlerin,,2.166.227.205,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4208,2,92,2017-03-06 20:00:16,http://brekke.name/calista,,87.87.15.251,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n4209,2,92,2017-03-24 23:03:08,http://gaylord.name/pascale,,248.58.23.41,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n4210,2,92,2017-02-12 13:32:55,http://smitham.name/savanah,,176.171.67.123,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n4211,2,92,2017-02-09 18:07:56,http://ankundingkoelpin.name/jillian_marquardt,,51.85.67.232,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n4212,2,92,2017-01-19 11:20:56,http://runolfon.com/mozelle_boyle,,10.70.18.244,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n4213,2,92,2017-05-06 01:43:23,http://bode.net/evan,,10.148.166.114,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n4214,2,92,2017-02-24 18:21:14,http://effertz.biz/aiyana.stanton,,116.126.248.31,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n4215,2,92,2017-05-23 13:48:32,http://vandervortspinka.name/caidy_wuckert,,252.128.192.133,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n4216,2,92,2017-01-30 09:09:00,http://ortizbreitenberg.name/haskell.murray,,62.121.193.191,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n4217,2,92,2017-02-14 22:07:28,http://sanfordschroeder.co/ward,,46.159.139.120,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n4218,2,92,2017-03-25 20:19:23,http://satterfield.net/alexane,,53.71.109.168,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n4219,2,92,2017-05-19 07:23:57,http://aufderhar.biz/ozella.schowalter,,12.29.12.37,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n4220,2,92,2017-04-05 23:13:21,http://ondrickakertzmann.net/nadia,,154.227.188.62,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n4221,2,92,2017-02-25 20:17:24,http://hintzferry.com/andreane_mosciski,,223.8.194.230,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n11107,4,248,2017-03-18 01:58:26,http://gaylord.info/santina,,243.154.126.57,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n11108,4,248,2017-03-22 19:52:40,http://kuvalisterry.biz/maureen,,56.96.101.172,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n11109,4,248,2017-01-11 23:11:04,http://rohan.name/donavon_stanton,,40.27.101.215,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n11110,4,248,2017-01-20 08:10:00,http://bayer.biz/sonya,,152.68.74.99,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n11111,4,248,2017-03-11 09:39:48,http://hammesrogahn.org/kyle_dicki,,96.227.172.253,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n11112,4,248,2017-01-15 08:50:04,http://smithamemmerich.co/mariana.boyle,,160.143.99.230,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n11113,4,248,2016-12-23 05:21:52,http://armstrong.io/shirley,,200.247.77.191,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n11114,4,248,2017-06-13 15:16:26,http://greenfelderbergnaum.biz/bernita_block,,128.189.13.80,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n11115,4,248,2017-06-02 02:32:25,http://bartoletti.net/willie.hayes,,152.202.200.40,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n11116,4,248,2017-01-18 12:21:32,http://lehner.com/rosendo,,218.32.115.123,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n11117,4,249,2017-01-04 08:51:25,http://smitham.biz/jarred,,229.167.248.29,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n11118,4,249,2017-01-09 13:12:50,http://bergstrom.info/eloisa.mueller,,68.111.151.29,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n11119,4,249,2017-03-17 08:06:30,http://williamson.com/chaya,,133.105.234.213,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n11120,4,249,2016-12-18 06:21:48,http://bradtkeframi.name/kitty.cormier,,187.30.160.165,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n11121,4,249,2017-05-05 22:22:48,http://greenholt.biz/savion_nikolaus,,60.91.43.15,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n11122,4,249,2017-01-12 23:26:41,http://ratkeorn.biz/jailyn,,91.130.170.90,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n11123,4,249,2017-01-19 16:02:47,http://bogan.io/joan.pfeffer,,164.45.201.36,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n11124,4,249,2017-03-05 14:17:52,http://harvey.biz/darryl,,248.182.87.119,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n11125,4,249,2017-01-08 07:05:07,http://grant.org/angeline.kozey,,208.244.64.179,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n11126,4,249,2017-04-23 01:03:19,http://kovacek.co/declan.hoeger,,218.161.154.225,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n11127,4,249,2017-05-31 06:18:08,http://vonruedenklein.com/sigmund_abbott,,109.238.74.14,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n11128,4,249,2017-01-25 01:05:59,http://wilkinson.name/lisette,,157.78.84.31,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n11129,4,249,2017-03-17 16:17:32,http://jerdeortiz.biz/imani,,114.150.58.202,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n11130,4,249,2017-01-18 08:24:55,http://kaulke.io/ariane,,204.23.81.33,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n11131,4,249,2017-01-16 21:49:37,http://bins.org/kamille,,163.62.225.135,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n11132,4,249,2017-04-06 03:36:24,http://dooley.name/vergie,,218.45.168.111,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n11133,4,249,2017-03-19 09:31:13,http://schimmelborer.com/aida.barton,,92.67.98.30,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n11134,4,249,2017-01-01 09:54:14,http://wehneraufderhar.name/michael.funk,,140.224.8.34,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n11135,4,249,2017-06-08 20:55:01,http://gutkowskihowe.com/annette,,193.68.69.153,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n11136,4,249,2017-02-24 01:22:50,http://carter.net/glennie_dubuque,,147.23.134.111,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n11137,4,249,2017-04-19 04:04:04,http://pacocha.org/okey,,150.230.214.62,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n11138,4,249,2016-12-16 14:02:41,http://turner.biz/aubree,,76.178.160.163,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n11139,4,249,2017-01-09 15:57:17,http://zulaufkeebler.org/fatima,,193.58.48.218,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n11140,4,249,2017-05-18 09:16:22,http://starkcrona.info/august_pfannerstill,,148.116.173.244,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n11141,4,249,2017-02-08 19:08:53,http://rutherford.biz/cletus,,14.9.42.17,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n11142,4,249,2017-01-25 21:44:21,http://rempel.net/fredy.bode,,127.189.223.171,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n11143,4,249,2017-01-07 08:57:32,http://runte.io/gerda,,174.90.204.35,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n11144,4,249,2017-04-08 00:59:09,http://hodkiewiczmarks.com/reggie.gusikowski,,39.3.17.107,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n11145,4,249,2016-12-21 20:49:11,http://krajcikkeeling.info/lewis_stokes,,215.120.116.70,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n11146,4,249,2017-02-03 05:04:03,http://blick.org/ricardo,,212.113.66.184,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n11147,4,249,2017-04-18 08:50:25,http://feeneygutmann.io/sharon.stanton,,33.130.213.120,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n11148,4,249,2017-05-10 11:06:18,http://torpschaefer.biz/jaeden,,239.125.74.5,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n11149,4,249,2017-01-03 13:09:23,http://trantow.net/darrick_hilpert,,236.224.51.195,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n11150,4,249,2017-03-27 05:00:09,http://schoenschmidt.com/benton.dickens,,67.105.103.47,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n11151,4,249,2017-01-24 12:49:13,http://price.net/kris.hagenes,,188.192.110.51,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n11152,4,249,2017-03-22 06:26:17,http://kreigerberge.org/dane_bernier,,202.137.105.7,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n11153,4,249,2017-02-03 23:52:28,http://wisoky.info/kelli,,234.39.166.78,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n11154,4,249,2017-01-07 18:38:46,http://webergibson.net/thelma,,60.188.193.49,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n11155,4,249,2017-02-17 18:19:07,http://carroll.info/hyman,,178.104.42.84,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n11156,4,249,2017-04-09 23:14:49,http://macejkovicmuller.io/antonina.muller,,164.165.169.129,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n11157,4,249,2017-03-03 16:50:47,http://crist.biz/giles.oberbrunner,,159.194.231.193,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n11158,4,249,2017-06-14 01:17:33,http://bednarsimonis.com/jon,,212.136.102.242,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n11159,4,249,2017-05-16 02:19:39,http://becker.biz/reinhold,,121.54.128.251,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n11160,4,249,2017-01-28 19:22:08,http://howell.biz/cordia.carter,,141.144.40.249,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n11161,4,249,2017-01-15 15:52:45,http://sauer.name/jerome.jones,,203.197.84.84,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n8119,3,179,2017-02-28 14:40:53,http://gaylorddietrich.name/ubaldo,0.9833766270,83.239.150.97,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n8120,3,179,2016-12-16 23:14:58,http://swaniawski.net/felipa_ortiz,0.9752975927,245.167.249.145,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n8121,3,179,2017-03-31 21:33:28,http://lubowitz.biz/maurine_fisher,0.1800505568,57.124.226.173,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n8122,3,179,2017-05-14 20:57:14,http://swiftebert.name/aurore,0.6416575664,70.252.178.31,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n8123,3,179,2017-01-07 05:50:51,http://cormierko.co/roderick.labadie,0.6089417187,158.13.36.85,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n8124,3,179,2016-12-24 12:03:36,http://schuster.com/bennett_funk,0.1694993514,66.99.81.56,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n8125,3,179,2017-01-19 05:46:26,http://ziemann.info/mohammed,0.1703540219,48.80.3.149,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n8126,3,179,2017-01-28 19:40:39,http://labadie.com/eusebio,0.1311062207,236.31.156.109,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n8127,3,179,2017-03-10 04:59:10,http://mertz.io/esperanza.mclaughlin,0.7816323585,11.192.2.35,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n8128,3,179,2017-05-27 10:21:31,http://frami.net/bartholome_wilderman,0.0808691505,105.198.88.32,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n8129,3,179,2017-05-23 07:50:29,http://weimann.com/davonte_welch,0.6874861730,19.158.177.13,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n8130,3,179,2017-04-16 15:17:48,http://bergstromcummerata.biz/camden,0.4292808910,102.69.153.25,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n8131,3,179,2016-12-22 14:53:43,http://bradtkeborer.net/lester,0.3202388588,70.184.204.196,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n8132,3,179,2017-03-06 00:37:57,http://stiedemannvolkman.info/dock,0.0787335352,234.8.232.216,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n8133,3,179,2017-02-26 11:03:13,http://windlerhuel.biz/rafael.oberbrunner,0.9360750066,254.51.96.68,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n8134,3,179,2017-05-22 03:23:41,http://macejkovickuvalis.io/stanton,0.1736102132,126.86.137.87,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n8135,3,179,2017-06-03 13:09:27,http://fritschbernier.io/kailyn.cartwright,0.3242093915,119.178.238.28,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n8136,3,179,2017-02-02 23:40:33,http://medhurst.co/elmo,0.8692437666,67.134.114.51,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n8137,3,179,2017-02-02 03:54:58,http://rolfson.biz/alfred,0.4938066717,226.228.236.182,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n8138,3,179,2017-06-02 23:28:26,http://gislason.co/derrick_ritchie,0.0582383972,99.19.145.141,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n8139,3,179,2017-02-09 16:50:58,http://zboncak.info/thurman,0.9918884059,110.28.212.120,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n8140,3,179,2017-06-01 22:55:57,http://breitenberghuel.io/bradley,0.0951423070,71.29.82.113,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n8141,3,179,2017-04-02 09:17:39,http://wintheiser.name/amina_franecki,0.1945663505,175.157.106.249,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n8142,3,179,2017-02-23 21:54:10,http://toy.co/karina,0.0582550466,155.229.9.228,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n8143,3,179,2017-01-08 22:56:07,http://kshlerinzemlak.biz/kody.kshlerin,0.4314491278,2.185.214.40,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n8144,3,179,2017-02-15 08:43:18,http://schustergibson.org/manley.klein,0.1122279861,134.201.11.14,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n8145,3,179,2017-05-10 16:13:14,http://labadiedoyle.net/grayce,0.4683074123,225.131.62.83,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n8146,3,179,2017-04-20 08:14:14,http://dooley.info/adam.gleason,0.2314141091,94.199.77.191,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n8147,3,179,2017-06-08 12:45:26,http://thiel.name/jensen.shanahan,0.7854670443,49.161.132.233,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n8148,3,179,2017-02-24 19:37:48,http://mueller.net/friedrich_nolan,0.9584563608,113.3.86.142,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n8149,3,180,2017-05-09 05:32:27,http://deckow.co/tillman,0.9855826547,76.159.60.49,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n8150,3,180,2017-02-06 20:42:27,http://murraypouros.name/verna.rosenbaum,0.3361983200,145.63.147.146,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n8151,3,180,2017-03-08 08:15:11,http://huels.name/bertha,0.6639968164,50.243.24.191,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n8152,3,180,2017-01-27 23:50:07,http://mueller.net/verdie.yundt,0.3773574593,96.92.33.43,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n8153,3,180,2017-01-14 15:50:05,http://reichel.org/moie_shields,0.4219320101,56.194.32.134,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n8154,3,180,2016-12-29 21:32:13,http://jerde.biz/markus,0.0550689777,242.126.154.88,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n8155,3,180,2017-03-13 06:18:55,http://watsica.biz/frederick,0.0468130118,193.136.184.11,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n8156,3,180,2017-03-20 04:26:47,http://caspernader.io/shanon.leannon,0.6977839157,35.134.55.233,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n8157,3,180,2017-02-15 07:11:22,http://bergnaumjohns.org/juliana,0.7221265357,129.40.24.125,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n8158,3,180,2017-05-04 21:33:09,http://adams.co/simeon,0.6546617703,195.211.76.251,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n8159,3,180,2016-12-21 21:53:09,http://lind.com/imani,0.0179045825,74.92.102.197,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n8160,3,180,2017-04-06 22:30:56,http://lebsacklowe.biz/esteban,0.5952879980,148.21.182.180,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n8161,3,180,2017-04-29 10:15:30,http://schadenyundt.com/giovanna,0.6107913709,183.2.154.153,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n8162,3,180,2017-04-08 23:29:55,http://wiegand.co/hulda.weimann,0.6796279636,121.51.2.98,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n8163,3,180,2017-05-31 21:23:28,http://tromp.info/sarah_ward,0.2936674032,249.230.186.249,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n8164,3,180,2017-03-07 12:22:40,http://schowalterpaucek.org/jee,0.7866166519,140.227.116.105,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n8165,3,180,2017-02-07 13:47:38,http://von.biz/wade,0.3692678191,137.46.86.105,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n8166,3,180,2017-04-14 06:28:37,http://krajcik.co/adolph.eichmann,0.3531283773,158.250.29.41,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n8167,3,180,2017-01-20 03:20:25,http://lesch.net/travon_hilll,0.3158172017,222.219.90.41,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n8168,3,180,2017-01-07 14:09:46,http://schneiderabbott.io/bria.emmerich,0.4104441183,40.31.40.159,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n8169,3,180,2016-12-17 11:16:28,http://breitenberg.biz/alec_wiza,0.4124026464,194.91.54.140,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n14057,5,315,2017-05-13 22:39:57,http://gaylord.io/adrian.will,,10.225.182.35,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n14058,5,315,2017-03-20 01:57:33,http://kiehnbahringer.co/rosanna,,135.179.230.66,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n14059,5,315,2017-04-11 02:06:51,http://spinka.biz/brando,,74.222.148.229,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n14060,5,315,2017-02-28 09:37:36,http://kling.io/noah,,235.28.86.7,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n14061,5,315,2017-01-16 05:01:29,http://ohara.com/eliezer,,248.42.134.190,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n14062,5,315,2017-04-09 03:34:45,http://heaney.org/tina_bergstrom,,96.43.98.162,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n14063,5,315,2017-03-01 21:36:02,http://dooley.biz/rasheed.walter,,226.163.156.168,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n14064,5,315,2017-02-23 19:51:51,http://christiansen.biz/tyreek.oconnell,,234.42.152.124,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n14065,5,316,2017-01-16 00:31:39,http://towne.org/maegan,,164.87.5.24,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n14066,5,316,2017-05-13 15:50:26,http://dickinsonbashirian.com/brycen.keebler,,121.132.94.209,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n14067,5,316,2017-05-18 12:12:33,http://emard.biz/rodrigo_mante,,99.100.124.21,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n14068,5,316,2016-12-19 13:21:45,http://hayeslabadie.name/adrien_pacocha,,186.202.64.122,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n14069,5,316,2017-05-28 19:01:49,http://wehner.io/eli.doyle,,119.118.160.201,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n14070,5,316,2016-12-20 10:02:11,http://stokes.info/lourdes,,32.102.174.241,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n14071,5,316,2017-03-17 06:34:42,http://goldnergutmann.net/richmond,,148.180.120.21,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n14072,5,316,2016-12-30 14:24:04,http://armstrongweimann.net/lou_nitzsche,,36.122.153.102,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n14073,5,316,2017-03-19 02:56:16,http://luettgen.io/adolphus,,197.108.119.2,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n14074,5,316,2017-05-06 02:12:24,http://hirthe.org/darlene.swift,,133.52.161.184,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n14075,5,316,2017-01-11 17:49:28,http://champlinkohler.com/jazmyn,,148.200.32.233,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n14076,5,316,2017-01-01 22:49:10,http://altenwerth.com/tracey_cummings,,49.203.144.82,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n14077,5,316,2017-04-26 01:33:25,http://kiehn.co/armani.deckow,,183.200.252.122,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n14078,5,316,2017-06-12 08:21:12,http://eichmann.co/gudrun,,149.167.23.164,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n14079,5,316,2017-05-03 10:05:15,http://glover.info/dayne_schmeler,,94.76.67.140,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n14080,5,316,2017-04-16 14:08:58,http://mante.com/clay,,91.157.26.79,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n14081,5,316,2017-02-22 05:53:53,http://jacobs.biz/delphine,,213.91.174.54,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n14082,5,316,2017-04-29 02:00:05,http://senger.com/veronica.dach,,174.248.171.79,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n14083,5,316,2017-03-27 22:17:31,http://maggio.net/hector_crooks,,165.143.56.139,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n14084,5,316,2017-04-24 23:25:49,http://ortizwehner.io/laria_legros,,238.13.57.49,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n14085,5,316,2017-01-13 16:05:42,http://metz.co/pamela,,197.45.159.235,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n14086,5,316,2017-06-02 12:24:49,http://hickle.biz/mireya.thompson,,19.122.71.192,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n14087,5,316,2017-02-11 22:44:28,http://sanfordmayer.info/adriana,,36.234.192.19,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n14088,5,316,2017-04-27 17:54:56,http://rippin.org/roxanne,,70.156.24.65,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n14089,5,316,2017-03-22 13:37:17,http://rogahn.name/adonis,,215.37.187.6,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n14090,5,316,2017-02-15 09:04:28,http://marquardtkuvalis.name/hettie_robel,,81.229.23.161,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n14091,5,316,2017-05-30 22:34:58,http://greenholt.org/keshawn,,200.166.26.38,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n14092,5,316,2017-01-20 19:58:24,http://hermiston.co/brionna_mante,,177.10.151.236,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n14093,5,316,2017-01-22 05:42:41,http://murraymcglynn.org/sydney_lowe,,54.216.144.92,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n14094,5,316,2017-03-31 05:53:06,http://cormier.io/shanel.reichel,,225.76.245.47,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n14095,5,316,2017-03-07 17:12:44,http://leffler.net/krystal,,191.50.218.58,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n14096,5,316,2017-01-01 13:05:58,http://ullrich.biz/keegan,,5.11.113.100,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n14097,5,316,2017-01-24 14:29:28,http://raynorzulauf.net/kacie,,103.121.229.155,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n14098,5,316,2017-02-24 12:04:57,http://oreilly.io/queen.lueilwitz,,39.160.85.114,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n14099,5,316,2017-02-10 17:43:34,http://denesikprohaska.biz/osborne,,52.227.91.7,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n14100,5,316,2017-02-14 12:49:12,http://hodkiewicz.org/bella.kuhlman,,112.110.244.209,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n14101,5,316,2016-12-31 19:43:35,http://starkkovacek.info/kory.rath,,241.83.211.162,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n14102,5,316,2017-02-17 12:37:18,http://pollichkuhic.net/tamara_denesik,,37.27.91.141,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n14103,5,316,2016-12-20 22:28:29,http://kemmer.io/arlie,,122.14.75.43,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n14104,5,316,2017-06-05 03:54:53,http://stoltenberg.com/roel,,102.130.226.191,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n14105,5,316,2016-12-19 06:22:23,http://thielpadberg.net/rosalee_hansen,,236.101.184.63,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n14106,5,316,2017-04-02 07:41:11,http://ullrich.biz/aditya,,233.59.107.95,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n14107,5,316,2017-02-24 08:25:04,http://crist.biz/lucio,,62.94.180.175,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n14108,5,316,2016-12-30 20:20:15,http://wilderman.name/andre.witting,,173.54.69.55,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n14109,5,316,2016-12-18 21:29:48,http://durgan.io/oral,,250.135.179.156,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n14110,5,316,2017-04-16 04:10:37,http://leuschke.com/haylie,,218.145.100.126,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n14111,5,316,2017-03-07 05:54:26,http://orn.biz/bette,,237.232.31.229,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n14112,5,316,2017-01-15 09:34:50,http://renner.co/tatyana,,142.30.243.30,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n17039,6,385,2017-03-20 11:51:41,http://croninjohnson.co/bennett,,120.127.64.40,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n17040,6,385,2017-03-17 17:59:04,http://larsonrutherford.io/mathias.gottlieb,,190.12.63.171,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n17041,6,385,2017-05-30 05:11:18,http://kuvalichaefer.name/retta,,224.185.219.216,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n17042,6,385,2017-01-08 23:44:10,http://sauerkris.org/kennith,,56.67.226.115,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n17043,6,385,2017-04-08 07:16:57,http://cain.name/aurore,,59.67.238.89,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n17044,6,385,2017-02-22 19:52:02,http://kulas.com/collin.towne,,74.5.134.115,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n17045,6,385,2017-05-05 06:52:52,http://swaniawski.com/justyn_kertzmann,,196.190.50.135,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n17046,6,385,2017-02-26 02:53:04,http://lesch.org/rylan.wiza,,182.106.131.46,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n17047,6,385,2017-05-18 04:39:52,http://zboncak.info/georgianna,,153.226.117.22,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n17048,6,385,2017-06-09 07:53:43,http://grady.name/john,,114.187.113.223,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n17049,6,385,2017-01-27 13:38:05,http://rath.com/amaya,,188.122.6.181,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n17050,6,385,2017-01-13 22:23:41,http://batz.co/juanita_reinger,,92.204.134.235,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n17051,6,385,2017-03-24 15:43:33,http://fishermarvin.com/edison,,88.127.53.188,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n17052,6,385,2017-04-13 00:10:40,http://abernathyschroeder.com/myra,,45.210.135.144,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n17053,6,385,2017-05-22 16:05:49,http://little.com/sasha,,30.33.82.195,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n17054,6,385,2017-05-04 08:33:19,http://marks.io/maddison,,54.231.62.137,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n17055,6,385,2017-05-03 18:31:18,http://prosaccohegmann.org/alejandrin,,244.139.245.7,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n17056,6,385,2017-05-30 17:39:34,http://pollich.net/quentin.cummerata,,149.212.104.235,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n17057,6,385,2017-06-05 21:31:11,http://cremin.biz/juvenal,,48.123.125.55,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n17058,6,385,2017-06-10 22:18:26,http://kihnstiedemann.biz/leopold,,254.25.181.204,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n17059,6,385,2017-03-17 23:25:04,http://kirlinjones.io/mariano,,121.53.247.10,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n17060,6,386,2017-05-20 01:16:34,http://faheymarquardt.co/adan,,50.148.16.132,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n17061,6,386,2017-02-19 17:39:41,http://konopelski.org/kattie,,73.209.180.220,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n17062,6,386,2017-02-26 11:24:19,http://farrell.com/edmond.bailey,,208.7.175.140,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n17063,6,386,2016-12-29 12:05:46,http://wisoky.info/bret,,186.41.141.41,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n17064,6,386,2017-05-15 12:22:17,http://sengerkaulke.net/augustine,,41.124.204.49,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n17065,6,386,2016-12-26 00:28:14,http://hackettlueilwitz.io/brody_baumbach,,114.80.209.249,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n17066,6,386,2017-05-13 21:45:48,http://gottlieb.io/ellsworth_schulist,,158.161.36.165,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n17067,6,386,2017-05-06 08:00:36,http://rippinmiller.co/marianne.harber,,132.149.70.105,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n17068,6,386,2017-01-10 01:26:41,http://kub.info/lorine.hyatt,,171.126.211.81,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n17069,6,386,2017-04-05 14:32:23,http://blickklocko.org/roma.hirthe,,248.212.236.179,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n17070,6,386,2017-02-03 23:30:27,http://boyle.name/rogelio_bode,,108.70.180.228,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n17074,6,386,2016-12-17 01:00:45,http://schroedersanford.name/kasey.nitzsche,,6.7.30.233,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n17075,6,386,2017-04-05 08:41:26,http://tromp.info/deon_nienow,,139.173.3.230,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n17076,6,386,2017-02-27 11:37:57,http://kemmer.net/izaiah,,94.141.96.228,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n17077,6,386,2017-01-13 16:44:33,http://shanahannicolas.name/devon.grant,,218.12.196.128,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n17078,6,386,2017-06-01 02:53:23,http://denesiknolan.org/gunner.swift,,148.141.209.84,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n17079,6,386,2017-01-21 09:27:22,http://oreilly.org/ruthe,,236.248.15.206,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n17080,6,386,2017-03-25 21:47:25,http://effertzherman.com/everardo,,122.112.201.98,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n17081,6,386,2017-04-03 03:19:43,http://kerlukegislason.net/reilly,,102.140.215.52,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n17082,6,386,2016-12-19 01:58:42,http://turcotte.com/troy_hintz,,230.75.42.18,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n17083,6,386,2017-04-14 12:48:32,http://larson.info/jared,,177.214.16.189,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n17084,6,386,2017-04-17 05:27:36,http://medhurst.name/gage.keebler,,151.12.81.207,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n17085,6,386,2017-05-06 00:56:45,http://fahey.name/rodrick,,36.94.140.169,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n17086,6,386,2017-04-26 05:35:33,http://connelly.name/wilhelmine,,80.138.102.19,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n17087,6,386,2017-03-08 02:27:31,http://lesch.co/isabelle_nader,,72.212.227.151,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n17088,6,386,2017-01-29 19:17:38,http://schmidt.name/david,,16.195.41.250,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n17089,6,386,2017-05-01 00:30:21,http://rolfson.com/jerel.cruickshank,,165.93.8.12,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n17090,6,386,2017-03-15 04:18:55,http://hodkiewiczgleason.com/cathy.eichmann,,200.106.190.35,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n17091,6,386,2017-01-05 09:00:54,http://hane.org/bobby.rath,,218.26.147.62,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n17092,6,386,2017-03-27 17:45:09,http://gottliebheathcote.net/ansley.ortiz,,93.218.30.183,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n17093,6,386,2017-05-10 17:47:36,http://jacobs.biz/blanca,,145.159.54.246,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n17094,6,386,2017-03-26 10:29:00,http://considine.biz/cory,,38.203.186.191,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n17095,6,386,2017-01-26 18:47:39,http://funk.co/shyann,,109.35.170.241,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n17096,6,386,2017-04-08 14:00:18,http://vonruedengulgowski.info/kelly.conn,,144.17.247.113,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n4222,2,92,2017-01-13 01:55:28,http://casperblick.org/hertha.altenwerth,,114.26.160.32,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n4223,2,92,2017-04-23 09:35:42,http://mcdermott.biz/newton.osinski,,217.9.34.197,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n4224,2,92,2017-01-23 17:47:15,http://rosenbaumlangosh.co/rachel.smitham,,16.120.212.136,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n4225,2,93,2017-05-01 21:26:51,http://bosco.name/mikel,,206.108.52.126,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n4226,2,93,2017-02-03 08:23:56,http://koepp.io/marcelle,,98.128.220.245,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n4227,2,93,2017-06-01 05:29:02,http://ankundingkerluke.com/danika.jerde,,211.94.131.215,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n4228,2,93,2017-04-13 11:07:26,http://friesen.co/filomena,,231.147.40.129,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n4229,2,93,2017-02-05 20:43:52,http://stehrhamill.org/favian.kulas,,70.79.35.203,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n4230,2,93,2017-04-10 13:36:48,http://mrazhansen.io/percy,,170.84.107.163,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n4231,2,93,2017-01-09 15:29:19,http://wiegand.com/kenna.barton,,229.82.149.72,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n4232,2,93,2017-02-10 04:53:30,http://effertz.name/layne,,200.163.123.109,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n4233,2,93,2017-03-09 20:41:09,http://wiegand.io/evans.ohara,,177.142.16.21,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n4234,2,93,2017-03-23 23:34:58,http://blanda.com/elliott_rohan,,26.126.97.199,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n4235,2,93,2017-02-15 11:31:03,http://monahan.biz/cayla.stiedemann,,173.156.201.243,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n4236,2,93,2017-02-21 02:07:19,http://gutkowskikoelpin.io/raoul.oconner,,54.156.189.213,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n4237,2,93,2017-03-17 10:04:38,http://gibsonstokes.net/salma.halvorson,,168.50.33.115,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n4238,2,93,2016-12-26 20:31:09,http://connellykshlerin.co/ludie,,44.79.36.39,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n4239,2,93,2017-03-03 09:26:05,http://heel.io/chauncey.kilback,,182.22.213.225,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n4240,2,93,2017-05-15 01:58:18,http://herzogbotsford.name/camilla_goodwin,,149.68.166.60,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n4241,2,93,2017-02-26 19:09:17,http://legros.com/kailee,,164.186.120.175,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n4242,2,93,2017-03-17 10:38:10,http://krajcikkuvalis.info/cierra_cummerata,,140.191.237.220,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n4243,2,93,2017-01-12 03:05:10,http://yundt.org/lonzo,,54.144.137.4,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n4244,2,93,2017-04-04 17:20:39,http://mrazryan.biz/roger_schroeder,,235.65.183.253,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n4245,2,93,2017-04-30 13:40:41,http://schneiderziemann.io/amelia_walsh,,137.109.170.81,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n4246,2,93,2017-02-02 09:22:42,http://altenwerth.net/tina.frami,,121.50.127.164,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n4247,2,93,2017-06-02 22:21:29,http://bechtelar.net/quinn,,229.18.209.156,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n4248,2,93,2017-03-13 08:57:39,http://pouros.org/jaron,,11.180.196.246,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n4249,2,93,2016-12-18 21:30:48,http://mosciski.info/lane.towne,,14.113.90.40,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n4250,2,93,2017-03-13 10:39:09,http://padberg.net/boyd,,190.54.209.210,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n4251,2,93,2017-06-12 18:41:52,http://kunde.com/stacy,,20.68.196.75,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n4252,2,93,2017-04-17 12:59:44,http://lubowitzkutch.org/fay_bahringer,,83.251.124.232,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n4253,2,93,2016-12-20 03:59:22,http://zulauf.biz/terrence.hartmann,,25.158.5.108,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n4254,2,93,2017-01-05 16:37:37,http://johns.co/dakota_jaskolski,,62.150.91.156,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n4255,2,93,2017-05-23 20:04:40,http://murphyharber.com/tyra,,151.226.82.169,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n4256,2,93,2016-12-29 03:37:30,http://mckenzieprice.biz/quentin.ferry,,16.91.251.59,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n4257,2,93,2017-06-04 16:21:48,http://skiles.com/mckayla_gutkowski,,192.34.16.118,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n4258,2,93,2017-01-03 14:55:17,http://cummings.com/bud,,213.186.189.228,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n4259,2,93,2017-03-06 07:20:36,http://kunde.name/vivianne,,125.186.220.233,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n4260,2,93,2017-05-01 17:14:28,http://leuschke.name/hal.kuvalis,,130.211.140.14,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n4261,2,93,2017-06-01 02:53:17,http://hammes.io/mohammad_mueller,,49.14.158.101,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n4262,2,93,2017-03-03 00:12:15,http://pollich.biz/cruz.okon,,149.172.136.64,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n4263,2,93,2017-01-26 14:29:03,http://cummings.com/ralph.lesch,,59.252.194.201,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n4264,2,93,2017-04-07 10:39:52,http://steuber.biz/columbus,,62.221.197.101,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n4265,2,93,2017-01-26 01:04:54,http://zemlak.co/fritz,,47.129.152.32,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n4266,2,93,2017-03-21 02:16:07,http://nienowkub.info/lamont.bins,,129.139.57.128,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n4267,2,93,2017-04-09 15:25:38,http://hand.info/vincent.monahan,,70.181.167.176,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n4268,2,93,2017-01-08 23:52:58,http://heaney.co/tevin_wolff,,72.72.191.118,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n4269,2,93,2016-12-26 15:40:48,http://fisher.org/clyde.lowe,,179.105.210.164,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n4270,2,93,2017-01-25 22:40:00,http://langosh.info/cory,,252.82.50.176,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n4271,2,93,2016-12-30 11:17:55,http://koch.biz/wendell,,170.172.239.109,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n4272,2,93,2017-01-20 14:24:15,http://hartmann.co/felix,,175.45.231.121,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n4273,2,93,2017-04-20 22:43:05,http://turcotte.biz/sean.abernathy,,109.116.69.191,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n4274,2,93,2017-04-29 11:38:05,http://hand.name/marley,,142.183.176.156,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n4275,2,94,2017-03-30 21:46:41,http://witting.co/tevin,,23.102.124.170,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n4276,2,94,2017-01-27 01:42:28,http://ernser.info/winifred,,108.162.124.98,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n4277,2,94,2017-06-12 10:20:53,http://champlin.com/gabrielle,,57.134.198.183,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n11162,4,249,2017-03-13 04:13:20,http://boehmziemann.co/ephraim.stark,,49.23.163.128,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n11163,4,249,2017-01-14 05:38:40,http://considine.com/camren,,203.197.189.76,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n11164,4,249,2016-12-26 09:07:48,http://schimmel.org/lonnie.ruecker,,225.136.209.13,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n11165,4,249,2017-03-31 23:32:44,http://hackett.co/louvenia,,205.126.31.35,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n11166,4,249,2017-02-14 16:18:20,http://cristshanahan.net/alberto.marquardt,,9.198.105.224,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n11167,4,249,2017-02-15 05:15:39,http://roob.co/ernestina,,3.74.31.102,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n11168,4,250,2017-02-28 15:47:29,http://reichert.com/kiarra.balistreri,,10.80.209.212,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n11169,4,250,2017-05-26 08:46:24,http://lind.co/thea_stroman,,67.185.163.155,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n11170,4,250,2017-04-13 21:43:12,http://lehner.biz/kyle.osinski,,109.11.177.128,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n11171,4,250,2017-02-22 15:42:30,http://white.name/elwyn,,110.99.131.206,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n11172,4,250,2017-05-20 22:39:37,http://sipes.org/jerad_metz,,189.49.116.194,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n11173,4,250,2017-01-26 14:16:05,http://greenwuckert.net/lambert_sawayn,,197.218.21.145,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n11174,4,250,2016-12-14 19:12:52,http://rolfsonhermann.biz/ima_wolf,,28.94.79.188,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n11175,4,250,2017-01-03 14:35:37,http://hahn.name/garth,,129.249.214.122,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n11176,4,250,2016-12-22 06:44:41,http://larkin.name/kelly,,238.74.160.49,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n11177,4,250,2017-06-12 12:04:41,http://streich.name/hettie,,192.7.210.171,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n11178,4,250,2017-04-15 11:48:58,http://kuvalis.com/emilia,,86.167.85.42,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n11179,4,250,2017-05-09 00:21:48,http://littelstrosin.info/dallin,,14.90.12.160,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n11180,4,250,2016-12-24 16:34:49,http://dibbert.biz/shayna,,13.157.127.132,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n11181,4,250,2017-04-29 05:03:11,http://weber.com/karen_eichmann,,108.183.70.5,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n11182,4,250,2017-02-23 05:55:50,http://pacocha.net/brad.abernathy,,38.79.96.245,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n11183,4,250,2017-04-27 15:10:39,http://padberg.name/willard.wiegand,,195.86.57.246,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n11184,4,250,2017-03-11 16:56:09,http://thompson.biz/rahul,,22.189.143.26,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n11185,4,250,2017-05-05 06:16:02,http://auercronin.net/jonathan_rowe,,127.25.168.148,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n11186,4,250,2017-04-10 06:48:40,http://barton.name/helga.cronin,,81.45.185.144,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n11187,4,250,2017-05-09 09:49:11,http://rogahn.org/dominic,,17.201.201.18,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n11188,4,250,2017-04-05 11:36:11,http://ledner.io/mertie,,139.71.73.226,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n11189,4,250,2017-02-01 13:29:09,http://walter.com/kristian,,50.193.218.238,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n11190,4,250,2017-05-21 07:54:29,http://kiehn.io/kevon_kuvalis,,206.172.228.139,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n11191,4,250,2017-04-06 06:26:12,http://buckridge.net/blanca.parisian,,146.175.180.8,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n11192,4,250,2017-05-14 01:48:14,http://cronin.io/donavon,,79.169.46.43,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n11193,4,250,2017-06-06 23:46:49,http://satterfield.co/ashlynn_crona,,50.170.121.182,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n11194,4,251,2017-04-30 10:46:08,http://conndavis.com/lane.gulgowski,,85.19.53.151,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n11195,4,251,2017-03-02 02:31:53,http://oberbrunner.org/rogelio,,130.175.179.156,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n11196,4,251,2017-03-02 19:14:16,http://heidenreich.biz/jaclyn.rogahn,,60.213.78.68,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n11197,4,251,2017-02-21 18:59:29,http://carroll.net/kade,,16.115.121.242,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n11198,4,251,2017-04-01 20:46:51,http://wiegandschaden.net/cole,,42.154.177.239,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n11199,4,251,2017-01-08 05:49:16,http://daviscormier.io/mike.hackett,,150.158.192.20,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n11200,4,251,2017-04-13 19:46:52,http://gorczanykrajcik.co/dolores,,223.32.252.11,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n11201,4,251,2017-03-21 19:02:45,http://jerde.co/montana,,237.127.201.63,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n11202,4,251,2017-04-12 07:02:02,http://heidenreich.net/madonna,,72.85.48.14,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n11203,4,251,2017-02-22 01:02:47,http://heathcotebode.com/mckenzie_olson,,92.72.100.136,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n11204,4,251,2016-12-22 14:05:22,http://emard.com/joesph,,76.148.44.190,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n11205,4,251,2017-05-23 04:51:37,http://pfeffer.io/shannon_price,,95.39.72.21,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n11206,4,251,2017-01-26 00:25:42,http://hand.info/cara,,232.199.62.15,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n11207,4,251,2017-04-04 11:23:03,http://ritchieabernathy.com/terrell,,192.254.159.117,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n11208,4,251,2017-01-11 06:11:34,http://gerlach.biz/dorothea_white,,91.212.163.18,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n11209,4,251,2017-03-12 14:04:02,http://mayert.io/sandra,,31.107.120.98,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n11210,4,251,2017-01-19 09:54:11,http://reichel.net/lenora_macejkovic,,206.173.231.215,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n11211,4,251,2017-01-10 12:46:38,http://champlin.name/jameson,,146.32.51.21,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n11212,4,251,2017-05-02 04:19:33,http://murphy.net/kyle,,167.145.229.233,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n11213,4,251,2017-03-07 02:06:42,http://connellystamm.io/litzy.rau,,63.156.167.124,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n11214,4,251,2017-04-22 12:54:02,http://kreiger.net/isac,,68.238.79.20,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n11215,4,251,2017-02-08 19:23:28,http://andersonwilderman.io/fatima_rutherford,,245.221.163.109,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n11216,4,251,2017-02-19 04:03:27,http://townestreich.io/brandi_veum,,197.160.168.47,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n8170,3,180,2017-04-18 18:23:13,http://mcclure.biz/palma,0.6689673944,152.145.58.84,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n8171,3,181,2017-06-01 19:20:18,http://corkeryzboncak.co/jarvis,0.6209441698,248.244.18.49,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n8172,3,181,2017-02-10 04:09:00,http://adams.info/osborne_collins,0.0612289705,22.168.101.31,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n8173,3,181,2017-02-26 06:09:42,http://kelereichmann.info/alexane,0.0366695163,67.127.128.240,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n8174,3,181,2017-01-17 13:09:36,http://schaefer.name/keenan.wuckert,0.2610657100,111.142.223.84,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n8175,3,181,2016-12-21 11:01:41,http://runolfsdottirmorar.com/oral.rice,0.5323794326,237.17.53.77,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n8176,3,181,2017-03-21 00:08:55,http://turner.com/marie,0.0279746137,244.116.9.235,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n8177,3,181,2017-01-05 15:08:34,http://jenkinmith.com/bridgette_mccullough,0.1113406109,120.153.176.227,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n8178,3,181,2017-05-16 20:51:23,http://skiles.info/trea,0.1585320071,147.25.79.243,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n8179,3,181,2017-02-04 03:49:29,http://wymankreiger.io/leif,0.5343390720,5.37.5.83,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n8180,3,181,2017-01-19 20:58:26,http://nolanemard.co/jefferey.emmerich,0.0123930880,96.155.150.154,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n8181,3,181,2017-02-28 20:21:08,http://kunze.io/rodger_vonrueden,0.2659361171,109.35.148.192,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n8182,3,181,2017-05-07 01:56:31,http://mayer.org/hunter,0.5244500982,230.245.7.38,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n8183,3,181,2017-05-21 02:28:43,http://sipes.co/bruce,0.9347674939,129.244.52.211,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n8184,3,181,2017-04-19 04:34:05,http://bins.io/kara_rippin,0.4649037542,97.90.32.78,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n8185,3,181,2017-01-17 08:58:56,http://will.io/kurt_haag,0.5340803804,137.156.34.35,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n8186,3,181,2017-04-23 13:18:45,http://maggio.info/marta,0.6596025501,133.192.118.112,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n8187,3,181,2017-06-12 11:21:18,http://robel.biz/lazaro,0.2216656035,118.48.81.179,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n8188,3,181,2017-04-24 19:48:31,http://strosin.name/winfield,0.5630480784,101.151.30.144,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n8189,3,181,2016-12-16 03:30:10,http://lebsack.net/camden_heathcote,0.9626469115,135.132.125.60,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n8190,3,181,2017-02-24 10:22:46,http://harvey.biz/julius_kshlerin,0.6077187699,203.35.98.77,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n8191,3,181,2017-01-08 13:19:08,http://lebsack.name/yesenia.weinat,0.1630130272,77.142.189.84,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n8192,3,181,2017-05-21 14:51:51,http://gulgowskiweinat.io/quinten_leannon,0.7999305501,84.165.247.165,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n8193,3,182,2017-01-22 05:39:43,http://goyette.name/rosalyn_hahn,0.8700501962,211.72.21.4,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n8194,3,182,2017-01-16 07:46:57,http://hintz.name/oleta,0.0477622579,202.202.9.117,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n8195,3,182,2017-05-12 12:13:30,http://rippinmcglynn.org/tyrell,0.7145243781,99.50.64.163,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n8196,3,182,2017-02-22 16:32:22,http://hartmann.net/adolphus,0.6328294647,175.96.237.24,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n8197,3,182,2017-03-18 07:36:15,http://keler.name/deron,0.3714287255,32.74.127.21,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n8198,3,182,2017-01-04 04:24:29,http://cole.biz/freddie,0.4313237685,19.227.206.151,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n8199,3,182,2017-06-10 15:20:35,http://murphy.biz/thalia,0.1159187925,53.21.199.216,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n8200,3,182,2017-02-20 21:02:40,http://koeppturner.co/bobbie,0.3350078081,177.20.71.13,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n8201,3,182,2017-01-04 22:20:59,http://adamsokuneva.co/mara,0.9923592883,232.136.43.50,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n8202,3,182,2017-02-04 06:32:17,http://gaylord.info/jordon.robel,0.7060708840,62.178.196.160,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n8203,3,182,2017-02-10 04:35:22,http://muller.io/rosetta,0.7862678147,41.167.185.5,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n8204,3,182,2017-03-14 09:05:04,http://rogahn.com/peggie,0.4203163104,114.46.231.125,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n8205,3,182,2017-03-08 18:31:44,http://goodwin.biz/warren_bosco,0.8694166178,163.206.171.216,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n8206,3,182,2017-02-21 09:52:51,http://murazik.name/mervin.damore,0.4686481876,168.246.63.18,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n8207,3,182,2017-04-19 01:36:37,http://hegmann.net/romaine,0.8870173745,64.22.192.142,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n8208,3,182,2017-05-17 21:40:24,http://marvin.name/domenic.okuneva,0.5710023227,178.3.44.132,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n8209,3,182,2017-05-17 17:55:44,http://millsmorar.biz/archibald,0.2615607867,24.36.226.8,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n8210,3,182,2017-05-02 09:36:34,http://creminhuel.io/douglas_volkman,0.3379744666,21.141.141.76,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n8211,3,182,2017-06-02 08:09:21,http://kingtromp.co/lincoln,0.8997504122,238.189.27.82,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n8212,3,182,2017-04-05 20:22:25,http://cremin.co/daren,0.9310972835,109.230.210.34,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n8213,3,182,2016-12-15 12:54:20,http://schillerhintz.net/carlotta,0.8576071014,115.124.221.55,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n8214,3,182,2016-12-23 22:55:15,http://schneiderrowe.com/derick.davis,0.9336066610,91.59.129.8,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n8215,3,182,2016-12-27 06:32:59,http://wolffschuster.biz/augustine,0.6031123774,232.9.210.116,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n8216,3,182,2017-06-07 20:36:52,http://jones.com/reanna,0.8723301799,53.58.137.48,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n8217,3,182,2017-02-23 02:27:37,http://corwinwisozk.org/marlee_goldner,0.3093142382,6.137.225.6,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n8218,3,182,2017-04-09 10:27:15,http://prosacco.org/leonel_zemlak,0.2723396481,48.140.241.172,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n8219,3,182,2017-03-09 10:03:45,http://gibsonheaney.name/keanu,0.6187223410,53.55.186.123,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n8220,3,182,2017-05-03 04:00:10,http://willziemann.biz/alyson.okeefe,0.9344558864,161.138.57.117,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n8221,3,182,2017-01-12 18:49:18,http://predovic.co/neva,0.5816248388,208.49.8.5,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n14113,5,316,2017-03-16 22:40:53,http://simonis.net/immanuel_spinka,,208.236.68.210,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n14114,5,316,2017-02-09 09:59:30,http://bailey.info/alene,,134.207.17.104,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n14115,5,316,2017-01-26 20:45:40,http://hodkiewicz.io/dolores,,9.214.74.71,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n14116,5,316,2017-05-24 12:22:10,http://crooks.io/dylan,,250.51.119.63,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n14117,5,316,2017-01-15 05:13:21,http://grimes.io/laron_leffler,,32.250.48.95,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n14118,5,316,2017-02-06 07:21:39,http://farrellschuppe.io/kobe_konopelski,,133.58.189.92,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n14119,5,316,2017-01-08 13:55:43,http://ko.info/walter,,212.166.19.135,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n14120,5,316,2017-03-29 14:57:51,http://hodkiewiczpfannerstill.io/rigoberto,,75.54.196.188,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n14121,5,316,2017-01-24 09:47:29,http://hayeslind.org/felicia.pacocha,,117.105.28.138,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n14122,5,316,2017-01-04 16:38:03,http://handgraham.info/lincoln,,81.215.76.125,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n14123,5,316,2017-04-26 08:48:02,http://boyle.info/sophie.miller,,39.180.74.62,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n14124,5,316,2017-05-03 16:17:14,http://daughertybeier.com/roxane,,10.16.46.122,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n14125,5,317,2017-02-03 09:47:40,http://pfefferwilliamson.com/lloyd.howe,,44.12.123.191,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n14126,5,317,2016-12-23 09:51:35,http://douglas.biz/meaghan.pagac,,73.40.107.124,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n14127,5,317,2017-05-17 21:03:44,http://lehner.net/camden.lang,,167.135.111.144,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n14128,5,317,2017-02-11 02:36:44,http://hintz.co/donavon,,5.29.64.238,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n14129,5,317,2016-12-13 13:13:08,http://haagfunk.org/aurelio.lang,,207.9.161.152,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n14130,5,317,2017-04-22 22:40:24,http://kuvalis.io/gunnar.renner,,145.60.4.249,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n14131,5,317,2017-02-24 10:10:53,http://kulasmante.io/carter_ruecker,,182.39.69.14,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n14132,5,317,2017-01-14 11:34:22,http://schmidt.info/myrna,,83.97.127.45,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n14133,5,317,2017-02-03 11:41:21,http://wintheiserhintz.com/cheyanne.gleason,,204.199.164.38,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n14134,5,317,2017-02-04 19:04:19,http://beckeryost.org/louisa,,20.44.20.205,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n14135,5,317,2017-02-23 17:25:02,http://greenholthansen.biz/greg_carter,,135.241.23.242,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n14136,5,317,2017-03-05 06:00:31,http://raynorturcotte.co/damien,,164.16.176.73,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n14137,5,317,2017-05-19 02:51:36,http://abshire.io/alivia,,235.119.240.171,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n14138,5,317,2017-03-03 03:49:46,http://parisian.biz/floy,,116.173.103.71,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n14139,5,317,2017-01-19 04:12:23,http://stoltenberg.com/ethelyn,,4.148.43.201,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n14140,5,317,2016-12-27 10:27:53,http://kundeoberbrunner.org/katlyn,,158.211.84.60,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n14141,5,317,2017-02-07 19:40:28,http://carrollcremin.org/aliyah,,237.106.72.167,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n14142,5,317,2017-03-27 00:49:43,http://hermann.info/malika,,97.226.188.198,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n14143,5,317,2017-03-31 02:13:01,http://hicklekeebler.org/madelynn.tromp,,163.152.26.220,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n14144,5,317,2017-05-29 15:52:36,http://handjacobson.name/arianna.walsh,,194.243.123.33,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n14145,5,317,2017-06-05 12:19:00,http://jakubowski.info/johnnie,,79.97.54.67,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n14146,5,317,2017-01-18 23:11:25,http://mayert.info/morgan_johns,,185.221.21.31,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n14147,5,317,2017-05-08 06:48:24,http://gerlach.com/dahlia,,157.197.251.207,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n14148,5,317,2017-02-14 02:37:07,http://pacochasatterfield.com/raymond.mills,,26.20.96.162,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n14149,5,318,2017-01-15 19:23:13,http://bailey.biz/corine.streich,,52.38.161.120,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n14150,5,318,2017-03-18 03:31:03,http://wehner.co/rosalia,,192.245.211.198,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n14151,5,318,2017-01-11 21:27:17,http://hayespowlowski.com/thora,,51.237.144.179,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n14152,5,318,2017-05-14 23:03:03,http://breitenberg.com/carley.berge,,204.4.158.143,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n14153,5,318,2017-03-24 22:03:31,http://kutch.biz/odie,,81.50.250.30,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n14154,5,318,2017-05-13 19:22:33,http://manteokuneva.info/elroy.lemke,,246.208.125.125,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n14155,5,318,2017-01-02 14:08:06,http://damoredavis.co/mike.hammes,,200.69.143.86,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n14156,5,318,2017-01-25 20:43:19,http://weber.com/jaeden,,136.151.76.102,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n14157,5,318,2016-12-13 16:49:47,http://rice.net/max.blick,,219.65.20.76,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n14158,5,318,2017-03-16 04:28:55,http://leuschkemiller.net/jasper,,177.118.91.248,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n14159,5,318,2017-04-26 15:38:26,http://mantelueilwitz.name/nella.powlowski,,71.96.82.115,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n14160,5,318,2017-03-08 23:28:09,http://wilderman.info/mara,,230.17.191.75,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n14161,5,318,2017-04-29 20:28:31,http://feeney.biz/diana,,156.40.227.64,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n14162,5,318,2017-02-03 09:10:56,http://dooley.info/claudine,,103.79.6.118,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n14163,5,318,2017-05-15 00:53:15,http://stehr.com/maya,,44.107.242.125,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n14164,5,318,2017-03-05 10:18:40,http://denesik.io/travis.runte,,30.133.76.110,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n14165,5,318,2017-01-30 00:42:25,http://goldnerbotsford.info/trycia_runolfsdottir,,48.157.106.86,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n14166,5,318,2017-02-14 14:27:18,http://zboncak.info/johnpaul,,94.114.80.23,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n14167,5,318,2017-03-02 06:28:51,http://wiza.co/deron.bahringer,,10.66.124.168,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n17097,6,386,2017-03-02 14:17:29,http://satterfield.biz/jacinthe,,131.117.116.70,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n17098,6,386,2017-01-28 20:45:27,http://kohler.info/keegan,,133.179.155.233,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n17099,6,386,2016-12-16 08:56:04,http://aufderharklein.net/judy_keler,,196.115.28.154,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n17100,6,386,2017-01-16 09:39:16,http://oconnell.info/patricia,,213.188.17.58,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n17101,6,386,2017-02-01 05:34:02,http://kozeygreenfelder.co/sidney,,65.48.88.14,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n17102,6,386,2017-01-14 05:01:32,http://labadiewindler.name/lillie,,73.23.89.215,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n17103,6,386,2016-12-24 12:30:54,http://gaylordrutherford.info/ashleigh_swift,,100.172.174.175,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n17104,6,386,2017-03-21 01:05:47,http://lueilwitz.org/kailyn_damore,,81.151.184.9,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n17105,6,386,2017-02-11 15:04:59,http://emmerich.io/otis.lesch,,93.14.40.175,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n17106,6,386,2017-05-21 15:56:49,http://heel.net/roscoe.block,,110.186.93.220,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n17107,6,386,2017-02-04 16:19:05,http://satterfieldvolkman.info/friedrich_ferry,,202.29.230.164,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n17108,6,386,2017-03-19 17:17:49,http://reilly.io/lincoln.conroy,,247.253.169.225,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n17109,6,386,2017-03-23 11:41:12,http://mohrhuel.com/jake,,14.170.107.29,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n17110,6,386,2017-03-28 10:22:34,http://manteherman.org/miguel.will,,24.188.182.90,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n17111,6,386,2017-03-19 03:39:18,http://hackett.co/claudine.koepp,,16.166.179.119,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n17112,6,386,2017-01-18 19:40:22,http://baumbachjacobs.biz/houston.senger,,191.144.220.221,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n17113,6,386,2017-06-07 00:43:42,http://oconnerhettinger.io/graham_durgan,,119.102.125.25,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n17114,6,386,2017-01-07 12:48:39,http://herzog.co/marianna,,87.32.19.91,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n17115,6,386,2017-01-09 15:24:46,http://gleason.net/leora_shanahan,,91.229.193.117,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n17116,6,386,2017-05-12 02:45:45,http://millslittel.net/arjun.mante,,38.209.42.157,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n17117,6,386,2017-05-07 23:10:12,http://strosin.io/hazel,,100.14.220.43,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n17118,6,386,2017-01-17 03:31:36,http://eichmann.org/martine,,217.114.68.134,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n17119,6,386,2017-02-21 14:10:50,http://dibbert.net/albertha.thompson,,232.141.168.85,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n17120,6,386,2016-12-14 18:55:54,http://terryokeefe.org/mozell,,164.145.207.150,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n17121,6,386,2017-02-02 04:41:15,http://sauer.net/leone,,180.186.2.188,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n17122,6,387,2017-04-21 07:03:29,http://gaylord.info/franz,,70.253.112.203,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n17123,6,387,2017-04-27 20:12:16,http://heel.name/darlene,,52.194.88.18,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n17124,6,387,2017-05-02 17:05:13,http://cummeratagraham.com/modesta,,144.162.225.44,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n17125,6,387,2017-01-27 06:58:41,http://rowekeebler.com/daisy.gutmann,,11.5.208.54,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n17126,6,387,2017-01-31 05:59:50,http://parisian.co/rosina.corkery,,203.170.201.89,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n17127,6,387,2017-05-01 01:55:42,http://leffler.biz/erica_wyman,,29.209.66.109,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n17128,6,387,2017-03-06 06:49:13,http://klocko.biz/maximillian,,89.119.130.109,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n17129,6,387,2017-06-08 07:40:23,http://beatty.net/darius,,147.89.87.12,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n17130,6,387,2017-01-16 05:10:09,http://beermurazik.org/zachariah,,162.163.26.246,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n17131,6,387,2017-02-15 03:39:51,http://yostlindgren.biz/evelyn.christiansen,,251.238.52.24,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n17132,6,387,2016-12-17 05:02:29,http://kleinwilliamson.co/santino.grimes,,193.89.181.250,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n17133,6,387,2017-02-10 02:19:49,http://okunevafisher.biz/sherwood,,234.27.180.242,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n17134,6,387,2017-05-23 17:00:09,http://yostoconner.info/makenzie,,203.54.105.100,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n17135,6,387,2017-02-18 18:03:39,http://rice.co/beau_reilly,,133.112.99.67,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n17136,6,387,2016-12-24 08:22:55,http://stamm.biz/frankie_lesch,,66.164.203.146,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n17137,6,387,2017-05-21 12:15:04,http://hirthe.net/camylle.gulgowski,,164.186.58.211,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n17138,6,387,2017-04-26 14:24:46,http://jenkins.name/maurine.brown,,234.64.90.81,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n17139,6,387,2017-04-01 15:45:55,http://schoenrodriguez.biz/karson,,44.134.74.14,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n17140,6,387,2017-04-01 23:54:00,http://kihn.net/dangelo,,193.68.39.18,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n17141,6,387,2017-03-13 15:10:54,http://trompschroeder.info/vince.reichert,,62.69.11.21,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n17142,6,387,2017-06-04 22:46:21,http://bodebeer.info/rupert,,40.105.102.98,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n17143,6,387,2016-12-29 09:33:19,http://bogan.com/velva.ernser,,45.232.184.236,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n17144,6,387,2016-12-27 18:56:00,http://murazik.name/ottis,,49.139.153.193,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n17145,6,387,2017-01-29 12:29:34,http://price.org/elouise,,254.31.194.70,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n17146,6,387,2016-12-14 02:09:27,http://hoeger.name/tierra,,200.113.80.239,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n17147,6,387,2017-02-07 21:20:26,http://parker.info/sallie_schowalter,,147.172.244.207,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n17148,6,387,2017-06-11 17:03:13,http://doyle.name/sid,,77.101.110.199,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n17149,6,387,2017-01-01 01:09:37,http://leannon.biz/emma.nikolaus,,48.124.170.246,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n17150,6,387,2017-03-18 21:03:01,http://hills.org/emilia_steuber,,200.100.202.35,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n17151,6,387,2017-01-17 19:31:33,http://douglas.org/saul,,8.87.171.8,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n4278,2,94,2017-05-21 15:42:58,http://bergstrom.org/monroe,,51.4.148.50,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n4279,2,94,2016-12-30 06:26:37,http://grant.co/johnson.langworth,,52.155.164.169,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n4280,2,94,2017-04-28 22:23:57,http://smitham.com/chad,,173.19.189.231,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n4281,2,94,2017-02-12 11:41:49,http://walter.com/john,,145.77.134.115,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n4282,2,94,2016-12-19 04:58:38,http://hermistonwelch.net/madyson.marvin,,40.8.58.181,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n4283,2,94,2017-01-31 08:22:35,http://harveykoepp.com/kade,,252.177.227.69,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n4284,2,94,2017-03-21 17:13:35,http://crona.biz/marquise.konopelski,,68.134.168.157,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n4285,2,94,2017-04-07 11:24:09,http://lang.biz/kathryn,,33.223.12.109,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n4286,2,94,2017-02-18 10:27:57,http://adamsdietrich.net/rebeca_kemmer,,166.251.207.11,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n4287,2,94,2016-12-16 23:14:01,http://mckenziehintz.com/gilberto,,202.247.88.24,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n4288,2,94,2017-03-13 06:28:29,http://mcdermott.net/jaquelin,,189.205.30.110,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n4289,2,94,2017-05-06 15:23:21,http://cummerata.info/katlynn,,153.244.89.194,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n4290,2,94,2017-05-16 16:05:36,http://mclaughlin.co/winnifred,,92.159.18.120,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n4291,2,94,2017-03-31 08:42:13,http://pfannerstill.io/susanna_schiller,,197.53.183.167,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n4292,2,94,2016-12-18 03:34:04,http://bartonjerde.net/estella,,168.253.134.41,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n4293,2,94,2017-04-10 13:41:49,http://dachkuphal.net/alyon_rodriguez,,11.77.76.152,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n4294,2,94,2017-06-04 03:17:31,http://koch.info/jeffry.abbott,,211.154.216.172,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n4295,2,94,2016-12-14 07:00:17,http://boehmhalvorson.net/opal,,103.38.67.242,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n4296,2,94,2017-04-21 02:26:55,http://mills.com/jakob.jakubowski,,154.124.105.206,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n4297,2,94,2017-01-07 22:20:22,http://auer.biz/candice,,151.43.220.118,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n4298,2,94,2017-06-04 23:11:56,http://feest.co/carlos.pfannerstill,,164.190.61.46,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n4299,2,94,2017-03-20 19:28:03,http://mitchell.co/rickie,,63.113.23.176,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n4300,2,94,2017-04-12 11:10:32,http://schaden.org/damion.skiles,,101.41.109.86,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n4301,2,94,2017-01-17 12:02:13,http://harrisbeatty.io/erich_romaguera,,254.190.88.174,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n4302,2,94,2016-12-16 06:29:02,http://lesch.co/harvey,,76.239.247.88,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n4303,2,94,2017-01-24 13:12:24,http://stoltenberg.biz/desmond,,145.139.231.57,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n4304,2,94,2017-05-15 19:18:07,http://kaulke.org/reyes,,171.206.33.114,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n4305,2,94,2017-01-26 19:24:11,http://reinger.co/scottie_klocko,,107.78.226.182,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n4306,2,94,2016-12-22 07:28:15,http://white.io/nikko,,42.164.184.131,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n4307,2,94,2017-03-30 19:43:33,http://stroman.com/antonietta.kris,,17.88.96.139,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n4308,2,95,2017-04-12 22:56:26,http://bernierbaumbach.com/freddie,,174.103.11.223,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n4309,2,95,2017-02-07 08:45:39,http://moen.biz/gunnar,,104.180.182.186,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n4310,2,95,2017-01-03 19:50:53,http://kuphal.biz/diamond_goyette,,23.45.98.142,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n4311,2,95,2017-02-05 12:15:16,http://beier.info/reta.roob,,225.82.221.165,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n4312,2,95,2017-03-11 13:24:17,http://oconnell.info/keshaun_rohan,,118.160.26.143,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n4313,2,95,2017-02-28 03:21:38,http://reichel.biz/chester.frami,,227.210.201.138,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n4314,2,95,2017-05-07 12:44:16,http://jaskolski.io/okey_kutch,,212.97.149.95,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n4315,2,95,2017-02-07 00:48:08,http://skiles.name/alvis,,228.186.92.118,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n4316,2,95,2017-05-13 14:01:38,http://bechtelar.org/kira_pfeffer,,108.93.224.131,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n4317,2,95,2017-01-14 15:27:59,http://windlerkris.info/clint.wintheiser,,86.33.91.198,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n4318,2,95,2017-04-28 15:20:35,http://brekkedubuque.co/max,,76.31.176.208,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n4319,2,95,2016-12-27 19:23:20,http://feil.co/shanny_mante,,20.36.6.110,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n4320,2,95,2017-05-07 19:49:48,http://blick.net/megane,,202.230.43.242,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n4321,2,95,2017-03-07 06:23:57,http://collierstehr.co/julianne_lesch,,45.194.245.186,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n4322,2,95,2017-05-03 15:20:11,http://thiel.co/elian.osinski,,190.181.149.17,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n4323,2,95,2017-03-12 12:18:45,http://nitzsche.biz/tyrese,,97.11.185.45,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n4324,2,95,2017-05-28 23:47:27,http://balistreri.co/jasen,,200.203.154.148,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n4325,2,95,2017-03-12 18:38:20,http://stokes.io/norbert.gulgowski,,75.217.177.43,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n4326,2,95,2017-05-15 22:23:41,http://predoviczieme.name/garret_christiansen,,186.163.133.224,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n4327,2,95,2017-04-22 19:34:04,http://hamill.biz/trea_heathcote,,245.231.63.222,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n4328,2,95,2017-06-04 03:04:33,http://jaskolski.biz/napoleon_franecki,,193.92.151.251,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n4329,2,95,2017-01-07 08:45:51,http://collins.co/mellie.collier,,194.75.27.166,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n4330,2,95,2017-04-19 16:05:18,http://stiedemannkihn.biz/beverly,,220.202.126.184,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n4331,2,95,2017-02-23 10:42:54,http://grimesrunolfon.co/shyanne,,171.202.244.252,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n4332,2,95,2017-01-30 13:31:18,http://ohara.biz/dorris_schroeder,,195.26.43.35,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n4333,2,95,2017-01-02 07:25:12,http://mayert.name/camilla.lemke,,206.199.113.77,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n11217,4,251,2017-04-26 12:13:17,http://bednar.name/lottie.bogisich,,32.30.192.6,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n11218,4,251,2017-05-30 16:54:30,http://cain.com/alexandrine,,30.151.93.247,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n11219,4,251,2017-04-04 02:55:11,http://hyatt.info/eliza,,5.16.64.68,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n11220,4,251,2017-01-12 04:25:32,http://kuhic.com/charley_schowalter,,61.130.239.89,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n11221,4,251,2017-04-09 18:38:03,http://doyle.net/seth_vonrueden,,32.76.179.161,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n11222,4,251,2017-03-27 00:48:22,http://gislason.info/clement_bogan,,89.81.102.189,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n11223,4,251,2017-04-11 17:35:21,http://bergstrom.name/willow_davis,,59.133.174.61,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n11224,4,251,2017-01-02 04:01:46,http://hammeteuber.name/ada_lakin,,253.150.194.120,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n11225,4,251,2017-06-02 03:53:07,http://haag.info/kristoffer.kertzmann,,127.12.44.41,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n11226,4,251,2016-12-28 15:48:08,http://altenwertherdman.net/judson,,124.44.121.53,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n11227,4,251,2017-02-07 16:15:37,http://bednar.info/santiago,,138.125.200.148,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n11228,4,251,2017-05-09 17:29:25,http://cormierhammes.org/jeromy_hilll,,129.25.115.5,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n11229,4,251,2017-01-27 11:38:57,http://olson.co/sven,,235.99.120.30,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n11230,4,251,2017-01-29 12:25:07,http://satterfield.info/bonnie,,221.46.201.181,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n11231,4,251,2017-01-30 01:54:39,http://jacobsonkiehn.biz/jerrod,,150.138.219.241,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n11232,4,251,2017-02-12 03:07:15,http://hackett.com/javonte,,49.211.128.33,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n11233,4,251,2016-12-24 00:27:03,http://kelerhahn.co/casper_lebsack,,194.154.231.88,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n11234,4,251,2017-02-10 19:48:38,http://stark.name/elouise_reynolds,,214.205.109.173,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n11235,4,251,2017-03-23 11:57:41,http://kiehn.co/herman,,64.226.128.247,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n11236,4,251,2016-12-26 13:40:54,http://lehnerwindler.biz/cecilia,,213.33.106.9,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n11237,4,251,2017-02-09 18:04:38,http://witting.io/grayson.douglas,,67.44.81.95,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n11238,4,251,2017-06-03 13:13:53,http://dietrich.io/travon,,168.133.63.148,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n11239,4,251,2017-05-24 16:35:56,http://ziememueller.biz/irma,,203.252.127.25,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n11240,4,251,2017-01-03 19:56:08,http://hoppegutmann.io/kim_volkman,,130.174.3.51,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n11241,4,251,2016-12-18 17:42:53,http://swift.biz/alisa,,150.155.39.233,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n11242,4,251,2017-05-02 08:27:44,http://hirtheskiles.io/aglae.vonrueden,,95.205.191.126,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n11243,4,251,2017-02-18 20:25:21,http://ernserdavis.biz/kenyon,,186.170.97.111,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n11244,4,251,2016-12-23 09:10:44,http://bauchconn.info/oren,,42.111.22.116,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n11245,4,251,2017-05-18 01:54:50,http://okuneva.name/judd.rath,,116.9.37.217,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n11246,4,251,2017-01-17 10:31:06,http://treutel.com/jayden.boyle,,96.99.112.122,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n11247,4,251,2017-05-06 16:30:22,http://hansen.io/jalen,,225.224.11.52,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n11248,4,251,2017-03-01 17:48:38,http://hirthe.net/dorris_vonrueden,,170.131.137.6,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n11249,4,251,2017-03-24 02:32:35,http://moriettecummerata.io/rosalee,,106.131.157.179,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n11250,4,251,2017-05-14 09:07:47,http://reichert.co/quinton,,96.124.98.230,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n11251,4,251,2017-02-05 06:23:06,http://balistreri.biz/felix,,34.211.68.144,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n11252,4,251,2017-01-17 04:20:39,http://trantow.info/bernadine,,22.84.26.176,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n11253,4,251,2017-04-28 04:42:54,http://shieldscarroll.com/rubye.thompson,,159.111.226.169,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n11254,4,251,2017-01-04 18:30:18,http://kiehn.org/novella,,68.53.249.211,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n11255,4,251,2017-02-08 21:22:48,http://hamill.io/allison,,201.69.4.33,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n11256,4,251,2017-04-06 05:59:45,http://mcculloughleuschke.co/cary,,11.90.9.233,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n11257,4,252,2017-05-01 20:38:38,http://jones.name/anabel,,239.46.187.81,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n11258,4,252,2017-05-17 05:37:20,http://hartmanncole.io/dane_bechtelar,,70.199.102.13,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n11259,4,252,2017-01-06 14:08:01,http://simonis.info/tyra,,187.40.14.215,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n11260,4,252,2017-04-22 00:17:53,http://franeckiwiza.co/jaquan,,35.130.82.4,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n11261,4,252,2017-05-06 06:39:36,http://herzog.biz/bradley,,204.241.236.181,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n11262,4,252,2017-03-20 16:42:06,http://bayer.net/avis,,178.72.196.121,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n11263,4,252,2017-02-22 23:11:39,http://cummeratahoeger.name/abigail.homenick,,43.111.48.26,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n11264,4,252,2017-01-06 13:48:45,http://corwin.org/bethany_feil,,117.188.109.100,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n11265,4,252,2017-02-09 17:45:51,http://franeckiabshire.co/celestine.schuster,,11.124.188.78,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n11266,4,252,2017-04-29 12:23:56,http://kovacekdaniel.co/britney,,28.174.140.215,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n11267,4,252,2017-03-10 23:13:28,http://vonrueden.biz/angelo_bashirian,,157.203.194.77,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n11268,4,252,2016-12-24 17:09:51,http://turnerweimann.name/khalil,,21.52.136.211,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n11269,4,252,2017-05-28 20:38:23,http://zulauf.net/danyka.yundt,,192.158.173.202,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n11270,4,252,2017-04-27 07:05:43,http://sauerharber.name/alexis_weber,,200.156.187.68,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n11271,4,252,2017-01-22 21:35:36,http://wintheisermurray.biz/paul_gutkowski,,237.207.94.82,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n11272,4,252,2017-06-07 21:27:15,http://kelerolson.io/ashleigh_mclaughlin,,174.85.143.155,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n8222,3,182,2017-03-23 23:39:16,http://considine.com/robb.durgan,0.2962482277,205.181.84.97,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n8223,3,183,2017-03-23 21:12:08,http://flatley.info/rick,0.0986641912,139.50.211.252,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n8224,3,183,2017-02-11 00:05:14,http://spinka.co/eugenia,0.0079814200,228.213.82.221,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n8225,3,183,2017-05-23 17:52:47,http://casper.info/marianne,0.4677839533,58.222.11.130,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n8226,3,183,2017-02-08 10:37:33,http://moen.io/natasha,0.7039902549,83.125.244.166,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n8227,3,183,2017-06-02 22:24:35,http://bosco.name/katheryn,0.6906414107,57.111.46.95,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n8228,3,183,2017-02-24 09:55:38,http://parisian.info/lorenzo_lueilwitz,0.8408279811,7.48.65.107,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n8229,3,183,2017-05-18 03:08:23,http://reillyhayes.biz/haleigh,0.7657845760,95.93.47.221,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n8230,3,183,2017-02-09 15:43:07,http://labadiefay.info/mose,0.4507568282,162.81.235.124,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n8231,3,183,2017-05-21 00:31:19,http://kling.co/randall_upton,0.7945364698,252.148.86.232,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n8232,3,183,2017-05-29 15:30:02,http://crist.name/yasmeen,0.5527805877,29.155.94.87,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n8233,3,183,2017-03-22 01:53:32,http://smithamrenner.io/maria.quigley,0.0637617724,101.195.193.169,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n8234,3,183,2016-12-17 10:10:11,http://farrellwisozk.co/berry,0.4617551486,84.222.149.111,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n8235,3,183,2017-05-22 13:15:58,http://schneider.name/stefan.heidenreich,0.3708174513,85.173.227.221,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n8236,3,183,2017-01-19 09:27:13,http://anderson.io/libby_streich,0.3609759830,96.38.219.210,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n8237,3,183,2017-03-02 19:40:00,http://gulgowskikihn.net/herminio,0.5674923290,175.177.12.174,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n8238,3,183,2017-05-12 07:27:37,http://olson.info/orval.volkman,0.4694310735,74.51.207.247,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n8239,3,183,2017-03-13 05:08:46,http://stroman.com/marcia_schumm,0.5864230716,2.21.48.235,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n8240,3,183,2017-03-03 02:12:20,http://bode.biz/norris_dooley,0.4041705089,142.253.16.83,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n8241,3,183,2017-01-11 10:48:59,http://schusterolson.net/heber.kozey,0.7007590000,21.129.194.194,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n8242,3,183,2017-04-13 05:22:06,http://hermannjacobs.biz/stuart.bergstrom,0.2622483691,185.76.37.128,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n8243,3,183,2017-02-07 11:02:09,http://leannon.name/ernest.hyatt,0.2423305636,10.180.207.138,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n8244,3,183,2017-05-10 18:02:32,http://waters.io/leanna,0.7585655608,215.143.77.74,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n8245,3,183,2017-01-21 11:51:11,http://trantow.co/terry.kuhn,0.8712726794,251.150.89.113,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n8246,3,183,2017-02-26 00:13:54,http://gislason.org/alicia.weinat,0.6843434964,191.208.73.184,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n8247,3,183,2017-02-21 16:23:07,http://okunevablock.io/orlando_harber,0.1662792097,66.219.80.148,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n8248,3,183,2017-02-28 11:45:54,http://eichmann.biz/aunta,0.2468993075,104.120.103.248,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n8249,3,183,2017-02-04 16:29:44,http://pollich.info/maynard,0.1346319056,156.159.9.54,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n8250,3,183,2017-03-02 08:27:44,http://halvorson.biz/emilio.keler,0.2954837738,61.167.51.18,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n8251,3,183,2017-02-14 12:28:11,http://will.info/haie,0.8628490654,35.243.208.222,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n8252,3,183,2017-03-25 15:25:58,http://oberbrunnerjast.org/eugenia_hane,0.5897202699,243.232.183.61,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n8253,3,183,2016-12-21 03:28:40,http://klocko.co/ozella_klocko,0.3465612983,185.157.96.10,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n8254,3,183,2016-12-27 20:28:31,http://conn.com/margarett,0.0159075601,150.129.142.93,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n8255,3,183,2017-01-21 02:12:03,http://bechtelar.net/joe_hammes,0.3736383371,125.99.252.254,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n8256,3,183,2017-06-07 01:58:29,http://raynor.biz/rigoberto.ferry,0.5635764953,254.188.137.215,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n8257,3,183,2017-03-06 08:00:19,http://millerkeler.co/claudine.price,0.3383798651,86.127.19.217,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n8258,3,183,2017-03-31 02:41:31,http://marquardt.io/tiffany_roberts,0.6536425368,108.176.174.73,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n8259,3,183,2017-01-10 21:09:08,http://kaulke.name/cordie,0.5869265810,4.102.146.57,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n8260,3,183,2017-01-08 17:20:35,http://trantow.io/emmanuelle,0.9267603161,191.66.113.26,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n8261,3,183,2017-03-05 20:11:57,http://lehnerlesch.co/remington,0.1520769775,226.39.125.124,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n8262,3,183,2017-03-27 21:37:14,http://altenwerth.org/sheridan,0.4162691699,204.247.135.39,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n8263,3,183,2017-02-13 11:48:50,http://mosciski.co/jeanne,0.2533960227,149.212.50.31,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n8264,3,183,2017-03-12 00:59:12,http://andersonmarquardt.io/buddy_oberbrunner,0.1052939247,196.53.18.66,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n8265,3,183,2017-04-27 13:27:58,http://mills.name/camden,0.4281029472,20.211.169.134,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n8266,3,183,2017-05-04 16:56:49,http://mcdermott.name/whitney.keler,0.0728621901,126.214.141.239,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n8267,3,183,2017-03-10 23:50:38,http://langschinner.biz/marcos_dibbert,0.2428124048,132.58.27.89,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n8268,3,183,2017-01-07 14:28:08,http://reynolds.info/allison,0.5827405821,248.41.214.65,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n8269,3,184,2017-02-02 11:06:34,http://heathcote.com/casandra.kuhn,0.5287252876,38.239.217.192,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n8270,3,184,2017-01-31 01:22:01,http://rippin.co/lisandro.goodwin,0.0966712014,79.224.6.114,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n8271,3,184,2017-02-23 06:33:19,http://funk.biz/katelynn,0.7497825634,225.244.163.45,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n8272,3,184,2017-03-19 12:32:20,http://stracke.io/agustin,0.9279269219,102.131.143.45,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n8273,3,184,2017-05-05 11:24:55,http://wisozk.name/ulices,0.5639337120,119.12.150.23,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n14168,5,318,2017-04-11 05:08:09,http://dooley.io/reed_price,,32.78.26.117,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n14169,5,318,2016-12-20 17:55:56,http://rosenbaum.net/florine_robel,,110.141.213.49,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n14170,5,318,2017-02-19 05:28:24,http://kris.net/laney,,154.81.253.105,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n14171,5,318,2016-12-23 04:22:45,http://dibbert.info/leda.schroeder,,196.222.242.5,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n14172,5,318,2017-03-28 19:12:13,http://greenholt.co/jovany_hermann,,66.41.46.97,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n14173,5,318,2017-04-07 15:17:11,http://labadie.com/winston.bayer,,72.90.192.14,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n14174,5,318,2017-05-11 15:02:42,http://kuphal.name/autumn,,140.85.174.178,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n14175,5,319,2017-01-26 08:10:15,http://toystanton.io/jeanette_lindgren,,133.17.194.212,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n14176,5,319,2017-01-29 07:55:23,http://johnsonziemann.com/travon_anderson,,88.11.87.212,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n14177,5,319,2017-02-14 08:47:57,http://cole.info/sophia,,95.148.123.98,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n14178,5,319,2017-01-16 01:34:38,http://trantowebert.net/hayley,,192.159.12.61,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n14179,5,319,2017-02-07 20:23:45,http://baumbachspencer.info/leann.kunde,,139.187.253.28,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n14180,5,319,2017-05-18 07:39:20,http://hodkiewicz.biz/anabel.kohler,,222.46.137.219,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n14181,5,319,2017-04-12 07:46:27,http://brekke.info/marietta.ortiz,,5.223.222.129,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n14182,5,319,2017-05-11 12:41:22,http://luettgenwolf.co/shanna.nikolaus,,97.247.97.40,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n14183,5,319,2017-03-15 00:17:22,http://kuphal.org/bulah,,44.184.242.119,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n14184,5,319,2017-04-16 11:53:08,http://harberjacobs.org/elvera.dickens,,131.206.228.87,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n14185,5,319,2017-02-21 14:45:05,http://rempelhuels.com/rasheed.kunze,,241.91.215.176,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n14186,5,319,2017-06-09 13:17:45,http://bartoletti.co/mikayla,,171.108.16.138,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n14187,5,319,2017-02-26 12:32:53,http://adams.biz/meggie.lubowitz,,61.6.218.183,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n14188,5,319,2017-04-16 12:07:06,http://kunze.com/rick_littel,,195.218.209.62,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n14189,5,319,2017-04-13 09:28:23,http://reynolds.io/jacky.koch,,19.19.133.184,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n14190,5,319,2017-03-17 16:47:44,http://gaylordrogahn.net/vladimir.goldner,,57.195.17.20,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n14191,5,319,2017-06-09 03:19:31,http://balistreri.co/hector,,205.231.253.17,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n14192,5,319,2017-03-17 15:35:31,http://haag.name/era,,16.26.36.95,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n14193,5,319,2017-05-09 23:37:17,http://vonrueden.io/liliane_hartmann,,3.30.32.204,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n14194,5,319,2017-05-31 10:32:07,http://runte.co/lillie,,21.73.130.180,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n14195,5,320,2017-02-01 03:54:15,http://pouros.net/kristopher,,52.7.51.230,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n14196,5,320,2017-05-06 20:33:01,http://deckow.info/jeramy,,151.159.73.50,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n14197,5,320,2017-03-19 14:16:24,http://hintz.com/bailee,,45.126.231.7,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n14198,5,320,2017-04-07 06:39:20,http://mohrschultz.com/janea_quitzon,,198.221.198.3,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n14199,5,320,2017-02-01 18:02:55,http://barrows.io/mona.weinat,,237.214.252.43,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n14200,5,320,2017-02-04 15:04:31,http://wunsch.co/corbin_trantow,,193.137.186.178,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n14201,5,320,2017-01-01 04:30:31,http://schadenbarton.net/kaleigh,,44.204.132.223,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n14202,5,320,2017-03-04 20:00:54,http://gusikowskibailey.com/esther.johnson,,47.31.18.138,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n14203,5,320,2017-06-08 11:36:26,http://howell.io/rosemary_lockman,,81.135.218.119,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n14204,5,320,2017-03-07 04:18:46,http://klockobernhard.name/dell_stark,,246.243.181.204,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n14205,5,320,2017-01-06 01:53:21,http://effertz.biz/ofelia.dietrich,,79.82.116.190,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n14206,5,320,2017-04-17 02:09:42,http://gleichner.co/lolita_graham,,199.90.138.49,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n14207,5,320,2017-01-23 23:53:17,http://gutkowski.com/caleb_pagac,,30.43.66.95,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n14208,5,320,2017-03-18 16:59:21,http://wilkinson.name/austyn,,149.20.64.8,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n14209,5,320,2017-05-15 08:04:36,http://effertz.com/martin,,17.144.98.113,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n14210,5,320,2017-05-03 15:19:28,http://turner.co/noelia.klocko,,116.59.242.226,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n14211,5,320,2017-06-01 12:04:19,http://schultz.info/jairo.trantow,,128.220.3.37,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n14212,5,320,2017-01-19 19:54:45,http://uptonabbott.com/stan,,244.104.2.169,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n14213,5,320,2017-04-15 20:34:55,http://ritchieernser.name/jey,,28.16.17.127,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n14214,5,320,2017-02-18 07:11:58,http://naderwyman.io/maximilian,,89.11.248.232,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n14215,5,320,2017-03-07 20:42:01,http://reilly.biz/tierra.mcdermott,,144.196.129.47,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n14216,5,320,2017-01-19 21:27:49,http://hagenesharris.com/nat,,89.111.2.18,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n14217,5,320,2017-02-11 20:52:49,http://rohan.org/moriah.douglas,,75.194.196.139,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n14218,5,320,2017-01-25 13:34:38,http://ortiz.name/renee.sauer,,121.153.172.240,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n14219,5,320,2017-03-08 03:31:00,http://schadenhansen.name/edwina,,62.156.225.135,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n14220,5,320,2017-04-13 00:29:00,http://swaniawski.biz/aiyana.denesik,,8.91.110.94,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n14221,5,320,2017-05-05 01:50:16,http://windler.org/raegan_anderson,,9.238.137.50,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n14222,5,320,2017-05-26 23:59:42,http://botsford.org/abel_schamberger,,25.52.234.72,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n17152,6,387,2017-02-23 03:04:34,http://wehner.org/sandra,,127.204.151.115,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n17153,6,387,2017-01-28 08:39:02,http://wolf.biz/ericka,,224.222.175.201,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n17154,6,387,2017-04-19 17:33:43,http://maggio.com/jude,,162.31.35.76,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n17155,6,387,2017-01-27 11:08:39,http://pfefferleuschke.net/nora.weinat,,247.239.155.77,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n17156,6,387,2017-03-17 06:56:41,http://stokesvolkman.info/moises,,193.128.153.71,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n17157,6,387,2017-06-06 16:48:23,http://beatty.org/hiram,,236.21.79.17,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n17158,6,387,2017-04-21 22:59:45,http://cremin.org/dejon.hettinger,,77.160.80.35,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n17159,6,387,2016-12-18 09:08:52,http://frami.org/victoria.crooks,,192.79.236.179,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n17160,6,387,2017-05-12 19:14:14,http://fadel.com/tony,,19.119.60.62,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n17161,6,387,2016-12-26 19:52:49,http://deckow.net/kirsten_lemke,,25.157.39.193,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n17162,6,387,2017-06-03 23:49:14,http://lednerdietrich.co/ceasar,,232.139.222.150,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n17163,6,387,2017-02-28 19:26:56,http://haag.biz/talon,,148.33.80.119,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n17164,6,387,2017-05-02 13:26:50,http://goldner.io/lindsay_zboncak,,214.207.135.196,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n17165,6,387,2016-12-15 03:45:42,http://stanton.co/ethel,,128.91.172.190,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n17166,6,387,2017-05-20 09:01:54,http://kemmeroconner.name/oceane,,124.34.53.128,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n17167,6,387,2017-05-25 07:19:47,http://rolfson.name/leif,,108.5.121.131,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n17168,6,387,2017-03-10 13:49:55,http://greenfelder.name/kathryn_bechtelar,,239.29.239.2,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n17169,6,387,2017-06-04 20:30:37,http://turcotte.info/eve_schimmel,,47.161.238.14,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n17170,6,387,2017-05-25 18:09:19,http://huelpowlowski.co/cameron_block,,201.74.191.41,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n17171,6,387,2017-02-19 20:43:51,http://lynch.com/blair_herzog,,38.246.158.17,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n17172,6,387,2017-04-14 11:46:02,http://gislason.com/chloe,,12.229.135.200,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n17173,6,387,2017-02-17 20:39:23,http://steuber.name/iliana_daugherty,,166.137.239.228,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n17174,6,388,2017-01-09 10:37:30,http://gibson.org/spencer,,122.232.247.124,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n17175,6,388,2016-12-29 09:47:13,http://hauck.name/derek.heller,,152.75.33.173,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n17176,6,388,2017-01-12 20:59:18,http://hilll.net/fae.conroy,,122.65.151.253,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n17177,6,388,2017-01-14 05:36:30,http://barton.info/ismael,,5.230.208.176,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n17178,6,388,2017-01-12 00:26:18,http://hammes.name/ima,,188.64.54.126,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n17179,6,388,2017-05-04 10:06:38,http://lindgren.com/oma_schmeler,,163.64.5.4,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n17180,6,388,2017-01-21 08:11:40,http://veumfarrell.co/keanu_stoltenberg,,7.157.215.229,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n17181,6,388,2017-06-01 15:17:05,http://wilkinson.com/gwendolyn.kub,,116.97.23.180,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n17182,6,388,2017-05-28 21:54:59,http://kohlergrant.co/eldora,,70.246.158.56,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n17183,6,388,2017-01-11 20:37:34,http://bartonbayer.io/norwood,,230.157.31.242,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n17184,6,388,2016-12-25 21:20:38,http://grant.info/jillian,,4.100.122.112,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n17185,6,388,2017-03-31 04:31:57,http://kozey.info/brendan,,195.143.4.171,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n17186,6,388,2017-06-13 15:53:55,http://windlerwisozk.org/fannie,,20.167.148.238,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n17187,6,388,2017-05-22 11:15:16,http://schneider.org/fleta.fay,,215.66.81.64,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n17188,6,388,2017-01-19 03:09:33,http://bruen.com/giles_bogisich,,133.155.104.209,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n17189,6,388,2017-01-08 06:06:09,http://adams.org/leila,,64.212.157.32,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n17190,6,388,2017-04-02 08:28:44,http://mann.name/damion,,48.41.93.112,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n17191,6,388,2016-12-22 19:24:12,http://ziemepfannerstill.name/ernie,,40.213.147.154,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n17192,6,388,2017-04-17 06:51:25,http://johnsonhuels.biz/natalie,,206.25.216.127,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n17193,6,388,2017-04-14 21:10:31,http://ebertkuhic.net/olen.kub,,85.118.148.64,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n17194,6,388,2017-02-12 23:42:26,http://koepp.net/dedric,,141.106.123.91,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n17195,6,388,2017-01-12 12:20:53,http://ferry.name/stanton,,203.164.117.205,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n17196,6,388,2016-12-23 16:36:51,http://bogan.name/jaren.rutherford,,7.57.169.183,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n17197,6,388,2017-05-07 10:47:07,http://doyle.co/dusty,,101.208.88.137,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n17198,6,388,2017-01-17 20:08:18,http://frami.org/chad,,159.59.11.159,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n17199,6,388,2017-05-31 10:15:13,http://wuckert.name/verda,,48.247.225.234,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n17200,6,388,2017-02-06 19:44:54,http://ko.co/linnea_ondricka,,112.68.137.185,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n17201,6,388,2017-02-13 05:18:49,http://gusikowski.co/newell_gutkowski,,96.249.197.125,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n17202,6,388,2017-05-08 12:55:32,http://monahanveum.org/devante,,200.210.195.87,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n17203,6,388,2017-05-07 09:17:14,http://beahan.biz/loyal.rath,,83.59.121.89,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n17204,6,388,2017-05-08 03:55:55,http://ondricka.info/erick_schinner,,153.180.46.170,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n17205,6,388,2016-12-25 23:35:04,http://boylebarrows.co/laila_breitenberg,,158.253.91.52,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n17206,6,388,2017-05-17 08:41:31,http://schambergerrunte.biz/terrance,,248.60.119.157,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n17207,6,388,2017-02-08 19:48:39,http://rice.name/carolina.volkman,,242.83.234.137,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n4334,2,95,2017-03-13 12:24:57,http://mcclure.com/lavinia,,188.15.151.158,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n4335,2,95,2017-05-22 06:54:00,http://skiles.net/caleigh.yundt,,10.231.45.227,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n4336,2,95,2017-01-27 21:37:31,http://jakubowskinikolaus.io/elizabeth,,30.143.153.198,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n4337,2,95,2016-12-20 14:02:25,http://langosh.name/kayla.barton,,227.119.215.232,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n4338,2,95,2017-02-12 17:47:44,http://langworth.net/jacky,,171.58.14.242,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n4339,2,95,2017-02-28 18:00:19,http://leffler.net/myrtie_thiel,,75.20.194.155,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n4340,2,95,2017-05-09 14:40:05,http://flatley.net/marjorie,,202.122.137.54,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n4341,2,95,2017-01-27 15:00:30,http://jacobi.org/adah.bode,,202.85.116.27,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n4342,2,95,2017-06-05 12:04:28,http://beahanrippin.io/erna.little,,135.67.9.219,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n4343,2,95,2017-05-03 14:07:16,http://emardbayer.io/luciano_ryan,,28.245.62.48,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n4344,2,95,2017-05-16 13:14:37,http://kulas.info/lyric,,72.157.211.66,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n4345,2,95,2017-01-07 03:50:10,http://gaylord.info/hettie,,57.223.47.76,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n4346,2,95,2016-12-30 08:08:01,http://farrell.info/hertha.dach,,156.208.219.153,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n4347,2,95,2017-01-16 22:57:24,http://gusikowski.io/braeden_veum,,182.231.217.62,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n4348,2,95,2017-05-30 15:59:19,http://schuster.com/georgette,,248.127.77.252,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n4349,2,95,2017-05-29 03:09:52,http://osinski.name/bettye,,199.140.96.155,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n4350,2,95,2017-03-31 11:01:47,http://bernhard.com/brando,,249.244.221.59,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n4351,2,95,2017-01-07 02:11:31,http://thompson.org/kaitlin.nitzsche,,23.68.147.217,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n4352,2,95,2016-12-21 05:38:27,http://hegmann.biz/edythe.wolff,,61.113.222.150,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n4353,2,95,2017-04-28 03:53:11,http://dare.com/clemens.schinner,,102.33.154.227,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n4354,2,95,2016-12-30 04:28:06,http://roobnader.org/roselyn,,112.190.254.210,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n4355,2,95,2016-12-27 07:43:31,http://mayert.org/melia.koepp,,80.154.22.158,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n4356,2,95,2017-02-24 02:28:49,http://purdy.net/claria,,41.43.133.48,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n4357,2,95,2017-02-13 11:13:37,http://hoppewolff.io/merlin_green,,239.39.29.102,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n4358,2,96,2016-12-22 10:34:51,http://kertzmann.co/erin_brown,,67.80.41.172,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n4359,2,96,2017-01-18 17:29:30,http://schambergerzulauf.io/joanne.olson,,62.234.207.134,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n4360,2,96,2017-03-26 09:59:00,http://becker.net/augustine_schuster,,197.115.29.10,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n4361,2,96,2016-12-14 10:29:09,http://hills.name/reagan.boyer,,176.40.248.248,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n4362,2,96,2017-05-12 19:53:43,http://hudson.net/glenna.feeney,,62.127.190.181,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n4363,2,96,2017-04-18 10:50:46,http://welch.co/kamryn.hagenes,,69.135.24.150,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n4364,2,96,2017-03-03 07:33:20,http://bergstrom.info/colin,,90.191.183.94,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n4365,2,96,2017-02-02 19:44:10,http://prohaska.biz/frank,,204.82.130.40,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n4366,2,96,2017-03-04 12:49:26,http://schimmel.biz/gregorio_schuster,,44.85.106.95,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n4367,2,96,2017-06-10 10:08:45,http://quitzon.co/luella,,254.156.115.206,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n4368,2,96,2016-12-25 08:24:04,http://hilll.info/hipolito.hirthe,,224.68.32.139,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n4369,2,96,2017-05-28 18:14:10,http://lowe.org/della,,129.216.7.214,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n4370,2,96,2017-04-23 21:29:41,http://hand.co/buster,,134.249.7.66,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n4371,2,96,2017-03-19 21:17:23,http://kaulkelang.biz/rhianna,,68.17.250.224,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n4372,2,96,2017-05-27 09:52:49,http://ohara.com/henriette,,168.10.160.37,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n4373,2,96,2017-03-07 04:08:48,http://jacobi.name/vida_hegmann,,106.70.64.125,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n4374,2,96,2017-05-03 22:07:23,http://hegmann.info/charlene.dicki,,40.228.115.244,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n4375,2,96,2017-01-15 03:02:21,http://konopelski.biz/domenica.jacobi,,206.184.148.87,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4376,2,96,2017-03-12 08:23:49,http://shanahandenesik.info/isaac.torp,,153.117.199.153,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n4377,2,96,2017-04-02 03:13:44,http://lockmanprosacco.info/hipolito,,5.33.127.247,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n4378,2,96,2016-12-25 11:20:34,http://littel.biz/garrick,,97.73.246.245,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n4379,2,96,2017-02-03 09:26:17,http://bernhardmarks.info/jayde_hayes,,49.138.113.173,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n4380,2,96,2017-01-04 16:59:32,http://klein.org/lura,,185.136.245.177,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n4381,2,96,2016-12-25 08:55:29,http://mcculloughward.name/ally,,160.230.47.140,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n4382,2,96,2017-02-17 18:43:00,http://adams.biz/osbaldo,,242.72.205.242,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n4383,2,96,2017-01-21 02:05:30,http://schillercrooks.co/jana,,153.87.186.129,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n4384,2,96,2017-06-09 22:23:03,http://quigleyullrich.co/savannah,,236.200.59.163,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n4385,2,96,2017-05-13 09:07:10,http://price.org/helena,,79.196.131.229,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n4386,2,96,2017-05-01 08:36:17,http://cummings.co/uriel,,233.90.127.232,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n4387,2,96,2017-05-29 23:46:46,http://senger.io/augustine_hermiston,,4.137.83.96,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n4388,2,96,2017-01-01 04:45:00,http://ward.co/velva_mertz,,40.223.243.94,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n4389,2,96,2017-05-26 04:27:52,http://jacobi.biz/lenny,,94.182.43.254,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n11273,4,252,2017-05-02 16:24:54,http://wiza.co/rubie.trantow,,30.26.166.32,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n11274,4,252,2017-01-13 00:56:14,http://bernhard.co/neal,,65.247.10.210,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n11275,4,252,2017-04-07 04:51:30,http://bechtelarlang.info/spencer_armstrong,,4.53.227.121,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n11276,4,252,2017-02-01 05:17:02,http://schamberger.net/bette_klein,,58.132.193.203,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n11277,4,252,2017-02-10 08:30:55,http://lockman.co/vinnie,,100.223.201.231,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n11278,4,252,2017-02-28 02:49:46,http://joneshuels.org/lydia_nitzsche,,4.2.174.154,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n11279,4,252,2017-05-20 23:05:41,http://oconnelllockman.name/mallory,,182.41.51.214,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n11280,4,252,2016-12-16 05:42:26,http://kunzewelch.io/priscilla.eichmann,,24.215.230.97,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n11281,4,252,2017-04-26 07:48:02,http://kutch.name/davin,,72.35.93.128,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n11282,4,252,2017-06-09 05:21:19,http://little.name/eino,,30.34.68.137,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n11283,4,252,2017-01-09 09:09:39,http://haley.com/alf,,91.38.14.250,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n11284,4,252,2017-01-22 03:15:47,http://koepp.com/guie,,51.119.125.130,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n11285,4,252,2017-01-11 05:44:12,http://hickle.net/amie,,181.114.209.145,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n11286,4,252,2016-12-27 10:38:18,http://stracke.net/joe.mayert,,50.100.13.131,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n11287,4,252,2017-01-14 17:11:18,http://hand.co/jaida_lowe,,134.144.126.115,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n11288,4,252,2017-03-16 15:08:33,http://turner.org/nicholaus.rice,,194.83.251.27,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n11289,4,252,2017-05-03 05:48:52,http://schinnerwyman.info/adelia.hilpert,,232.172.88.183,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n11290,4,252,2017-02-07 23:28:14,http://reichert.net/dion.green,,104.75.41.44,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n11291,4,252,2017-03-01 21:04:36,http://casper.co/zack,,133.207.81.168,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n11292,4,252,2017-02-11 10:23:06,http://rogahncummings.co/jayme,,131.131.246.149,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n11293,4,252,2017-04-03 14:52:00,http://whitewalter.com/idella_wiegand,,160.85.228.180,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n11294,4,253,2017-02-20 04:18:16,http://hansen.io/malachi.kohler,,99.221.63.125,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n11295,4,253,2017-05-10 22:07:34,http://herman.biz/norma_nader,,236.64.28.182,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n11296,4,253,2017-03-19 10:06:52,http://buckridge.name/spencer.stehr,,107.196.159.238,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n11297,4,253,2017-02-02 12:03:29,http://reinger.net/camren_jacobi,,159.189.189.224,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n11298,4,253,2017-03-09 15:08:59,http://labadie.com/hortense,,168.214.108.227,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n11299,4,253,2017-02-18 10:07:49,http://okon.co/pablo,,214.180.219.174,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n11300,4,253,2017-01-09 06:46:20,http://harris.org/sibyl,,229.94.138.135,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n11301,4,253,2017-01-10 18:02:41,http://bogangislason.biz/daniela.hoppe,,116.33.180.124,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n11302,4,253,2017-04-19 13:01:18,http://wisokygrady.io/ashtyn.hills,,183.107.238.57,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n11303,4,253,2017-04-02 08:26:41,http://dibbert.org/jewel.mcglynn,,103.2.253.102,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n11304,4,253,2017-05-15 19:43:13,http://moriette.org/reilly,,168.198.22.227,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n11305,4,253,2017-03-11 08:50:07,http://crooks.biz/carley.larson,,233.159.15.180,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n11306,4,253,2017-01-12 04:12:44,http://hudson.co/clay.medhurst,,178.50.242.217,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n11307,4,253,2016-12-23 17:40:11,http://gleichnerthiel.net/kenyon_white,,47.191.112.217,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n11308,4,253,2017-01-15 22:24:55,http://jerde.net/grover.larkin,,26.220.132.238,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n11309,4,253,2017-01-31 02:57:23,http://kutch.name/kim,,168.105.97.211,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n11310,4,253,2017-06-12 21:12:23,http://shanahan.biz/willow,,102.170.123.161,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n11311,4,253,2017-05-11 04:09:08,http://dickinsonauer.biz/idella.schultz,,187.106.181.158,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n11312,4,253,2016-12-17 07:12:41,http://schimmel.name/lauretta_schaefer,,71.220.64.197,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n11313,4,253,2017-05-24 12:30:17,http://adamswaters.org/rogelio.dibbert,,234.26.209.20,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n11314,4,253,2016-12-24 17:36:35,http://baumbach.io/cesar,,168.63.194.245,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n11315,4,253,2017-05-30 15:04:15,http://connellyklein.info/gia,,126.237.42.10,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n11316,4,253,2017-04-21 08:28:59,http://wyman.io/rhianna,,104.50.195.195,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n11317,4,253,2017-05-04 20:00:34,http://glover.com/tom_thompson,,182.164.137.103,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n11318,4,253,2017-06-02 12:34:16,http://mrazmraz.co/mohammed,,80.252.84.29,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n11319,4,253,2017-01-16 06:10:24,http://torphy.com/bethel,,120.158.91.146,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n11320,4,253,2017-06-09 06:59:26,http://leannon.info/reuben.paucek,,52.28.170.44,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n11321,4,253,2017-03-16 13:27:09,http://raynor.net/clifton,,137.146.143.247,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n11322,4,253,2017-01-03 02:50:06,http://grant.info/dewayne.hettinger,,115.169.216.212,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n11323,4,253,2017-04-10 19:15:44,http://ferryfeil.co/charity.bahringer,,110.230.67.8,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n11324,4,253,2017-04-14 15:04:33,http://moenschimmel.name/jaylen,,52.97.135.152,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n11325,4,253,2017-04-26 15:15:06,http://medhurst.com/veronica,,153.111.63.73,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n11326,4,253,2016-12-13 10:00:25,http://gleichner.biz/diamond,,210.129.210.37,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n11327,4,253,2017-01-27 15:42:40,http://okuneva.com/jewell,,199.171.170.236,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n8274,3,184,2017-03-28 04:16:16,http://strosinbosco.co/sigurd.schmidt,0.9783223706,173.74.172.140,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n8275,3,184,2017-05-11 18:06:22,http://wisozk.org/christian,0.8513824783,129.78.116.56,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n8276,3,184,2017-03-06 14:20:07,http://mcclure.net/clinton,0.7003146206,18.66.14.131,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n8277,3,184,2017-01-24 05:00:26,http://grantbins.name/calista,0.1162441637,143.253.122.24,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n8278,3,184,2016-12-15 10:09:04,http://wunsch.net/carmela,0.8223773205,37.92.143.50,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n8279,3,184,2017-01-27 15:30:44,http://olson.com/mckenzie,0.4572296631,98.11.135.113,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n8280,3,184,2016-12-29 02:08:01,http://boehm.io/alana,0.5554985820,160.39.120.163,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n8281,3,184,2017-01-03 20:35:56,http://howell.info/helen,0.8016626880,162.129.219.232,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n8282,3,184,2017-02-12 13:02:34,http://marks.net/marietta.boyer,0.8864731253,129.162.167.192,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n8283,3,184,2017-05-17 20:43:42,http://hueltreutel.name/jordane,0.9022734266,186.26.104.176,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n8284,3,184,2017-01-17 00:45:33,http://turcottewindler.name/kraig,0.8405767351,47.39.10.66,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n8285,3,184,2017-02-01 20:00:41,http://schuster.com/tiffany,0.2172149412,37.117.147.51,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n8286,3,184,2017-03-25 09:29:05,http://ondricka.biz/cleo,0.7610401102,210.89.86.100,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n8287,3,184,2017-03-12 03:22:46,http://franecki.biz/alicia,0.6309844983,181.52.93.82,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n8288,3,184,2017-05-24 08:23:24,http://ullrich.biz/frederick_leannon,0.5364717343,110.65.54.36,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n8289,3,184,2017-05-23 17:13:36,http://yostmuller.io/terrell,0.8929810937,149.227.167.125,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n8290,3,184,2017-02-07 03:32:43,http://witting.name/wava,0.7995073475,233.211.234.171,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n8291,3,184,2017-01-28 07:14:55,http://feil.co/letitia.glover,0.1504495792,78.251.191.132,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n8292,3,184,2017-01-27 04:01:52,http://schultzyundt.net/roger,0.3945907817,34.107.96.121,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n8293,3,184,2017-01-10 11:31:28,http://yostreichel.net/larry,0.5579391575,74.116.111.42,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n8294,3,184,2017-01-13 10:44:55,http://boyerschowalter.info/fernando,0.2683045385,131.197.162.51,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n8295,3,184,2016-12-14 03:11:10,http://reynolds.name/dylan,0.3459579897,184.220.57.161,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n8296,3,184,2017-05-27 11:18:15,http://pollich.co/brandi_torphy,0.8818688379,115.173.127.133,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n8297,3,185,2016-12-18 04:20:11,http://kundereynolds.info/nels,,22.175.25.12,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n8298,3,185,2017-05-10 07:35:17,http://parker.io/marc,,97.77.181.83,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n8299,3,185,2017-06-12 20:27:33,http://rau.biz/jamal_hamill,,210.206.60.164,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n8300,3,185,2017-03-05 20:47:45,http://lang.org/robb,,250.211.62.46,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n8301,3,185,2017-05-18 03:59:02,http://hackett.net/jada,,91.64.164.95,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n8302,3,185,2017-04-30 21:37:45,http://roberts.com/kirk.murphy,,26.66.118.123,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n8303,3,185,2017-03-02 20:56:10,http://leannonkoepp.net/telly,,190.17.100.184,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n8304,3,185,2016-12-15 16:24:38,http://hilll.biz/verna,,18.176.153.136,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n8305,3,185,2017-05-31 06:18:14,http://lynch.name/janet,,250.58.157.181,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n8306,3,185,2017-06-03 23:05:28,http://fadel.biz/eulalia_donnelly,,131.130.13.125,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n8307,3,185,2017-04-08 12:08:11,http://luettgen.info/agnes,,10.11.157.199,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n8308,3,185,2017-04-16 01:26:08,http://mosciskiterry.info/enos.leuschke,,145.164.118.90,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n8309,3,185,2017-01-07 07:41:09,http://fritsch.org/isabelle,,9.87.149.152,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n8310,3,185,2017-02-05 12:54:25,http://weberdenesik.io/conner.glover,,222.103.45.70,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n8311,3,185,2017-04-19 01:09:59,http://legros.com/ismael.heaney,,204.161.83.125,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n8312,3,185,2017-04-13 23:56:31,http://vandervort.co/emmet_terry,,183.98.60.131,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n8313,3,185,2017-06-09 20:50:15,http://schambergerweber.org/milo_heathcote,,111.218.142.174,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n8314,3,185,2017-05-25 04:03:52,http://parisiangulgowski.com/rollin,,74.152.115.192,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n8315,3,185,2016-12-17 12:59:33,http://vongottlieb.co/vilma.stoltenberg,,191.165.179.231,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n8316,3,185,2016-12-30 22:11:16,http://buckridge.io/marlon.renner,,157.102.20.222,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n8317,3,185,2017-04-13 23:52:57,http://cronin.info/kaelyn,,244.253.239.111,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n8318,3,185,2017-06-12 22:28:24,http://hamillgleichner.io/marlene,,194.199.204.225,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n8319,3,185,2017-03-27 16:19:09,http://okeefe.co/emmie.ratke,,29.206.201.162,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n8320,3,185,2017-02-06 04:01:10,http://monahanblanda.info/nedra,,219.22.205.115,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n8321,3,185,2017-02-13 17:04:18,http://kiehn.name/chance,,155.43.123.238,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n8322,3,185,2017-03-29 20:15:03,http://abshire.info/beulah.oreilly,,52.18.167.24,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n8323,3,185,2017-05-30 05:16:01,http://erdman.com/dolores,,215.89.210.165,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n8324,3,185,2017-01-07 19:57:22,http://brown.org/manuela,,190.36.116.76,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n8325,3,185,2017-01-10 22:02:39,http://schmidtschumm.co/veronica_purdy,,74.35.36.100,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n8326,3,185,2017-05-09 19:18:51,http://romaguera.io/maria.barton,,166.41.212.144,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n8327,3,185,2017-01-03 15:51:29,http://murphy.info/maximo,,163.86.189.17,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n14223,5,320,2016-12-14 20:51:39,http://ziemann.info/erika.smitham,,125.37.235.183,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n14224,5,320,2017-03-20 02:16:18,http://lueilwitz.name/trycia,,74.186.165.92,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n14225,5,320,2017-03-05 13:00:49,http://fahey.com/danny_bernier,,76.109.178.202,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n14226,5,320,2017-06-10 01:37:29,http://erdmandibbert.com/darrel,,222.212.251.56,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n14227,5,320,2017-05-22 09:41:43,http://brownfritsch.net/chanelle,,200.156.216.27,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n14228,5,320,2017-06-08 13:15:57,http://ullrich.name/katherine,,254.47.241.33,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n14229,5,320,2017-06-06 19:16:26,http://adams.name/onie_zieme,,241.175.93.146,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n14230,5,320,2017-05-11 16:00:25,http://cole.org/emma_mann,,188.193.204.194,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n14231,5,320,2017-03-28 20:32:21,http://ritchieklein.co/yasmeen,,240.6.157.11,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n14232,5,320,2017-01-26 07:40:53,http://goodwinwalsh.info/conor,,47.241.221.45,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n14233,5,320,2017-03-12 11:16:35,http://bogisich.biz/yvonne.bruen,,45.156.218.212,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n14234,5,320,2017-05-08 19:35:03,http://shields.net/miller,,139.2.233.120,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n14235,5,320,2017-02-19 21:37:08,http://wunsch.info/ruby,,35.56.243.154,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n14236,5,321,2017-01-14 05:12:53,http://lakin.biz/verner,,232.52.185.51,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n14237,5,321,2017-03-16 18:51:19,http://konopelski.com/alejandrin,,114.129.214.182,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n14238,5,321,2017-02-25 22:37:22,http://hackett.biz/dangelo,,198.153.77.70,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n14239,5,321,2017-05-21 17:15:50,http://bernier.co/guie_schamberger,,62.28.19.166,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n14240,5,321,2017-02-14 13:57:32,http://macgyver.name/kristina.zulauf,,122.54.23.159,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n14241,5,321,2017-02-25 10:20:14,http://hyatt.org/adan,,46.235.208.116,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n14242,5,321,2017-02-18 22:55:13,http://koepp.co/ike.strosin,,8.239.191.231,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n14243,5,321,2017-03-15 17:08:48,http://mannwelch.info/remington,,19.24.224.219,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n14244,5,321,2017-01-05 00:53:22,http://stehr.info/florence,,49.117.11.130,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n14245,5,321,2017-05-13 05:27:41,http://nader.name/diana_greenfelder,,105.156.91.206,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n14246,5,321,2017-03-20 15:46:25,http://zemlak.com/euna,,252.252.194.26,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n14247,5,321,2017-06-07 21:36:56,http://hartmann.info/aleen.okon,,35.26.140.33,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n14248,5,321,2016-12-31 01:18:46,http://torphycartwright.org/roscoe.mueller,,165.173.145.241,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n14249,5,321,2017-04-25 16:40:54,http://pollich.io/ford,,168.36.64.58,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n14250,5,321,2017-04-06 04:24:14,http://auer.biz/lukas,,64.92.15.147,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n14251,5,321,2016-12-22 18:44:09,http://mrazoconnell.io/hermina.harber,,199.242.57.177,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n14252,5,321,2017-02-19 05:16:27,http://legrosmoore.name/shea.prohaska,,146.144.207.183,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n14253,5,321,2017-04-24 02:19:57,http://bechtelar.info/angelo,,67.201.32.144,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n14254,5,321,2017-04-23 06:49:20,http://jakubowski.net/michaela,,7.18.184.55,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n14255,5,321,2017-03-28 11:07:07,http://zboncak.info/reyes.gleason,,91.4.195.129,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n14256,5,321,2017-05-31 09:08:40,http://robel.org/grant,,233.227.82.105,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n14257,5,321,2017-02-14 00:57:43,http://funkconnelly.biz/yvonne_wintheiser,,232.208.101.17,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n14258,5,321,2016-12-22 17:04:54,http://schamberger.name/micaela,,40.48.209.46,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n14259,5,321,2017-01-02 16:29:07,http://dach.net/braeden,,39.241.152.126,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n14260,5,321,2017-01-09 18:22:19,http://schamberger.name/alisa.hoeger,,250.198.2.11,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n14261,5,321,2017-05-09 08:37:52,http://baileygaylord.org/emely_kuhn,,186.138.191.42,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n14262,5,321,2017-02-12 09:45:55,http://koelpincummings.co/ozzie,,222.97.86.124,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n14263,5,321,2017-04-15 03:23:09,http://hodkiewiczryan.org/berneice,,9.232.181.129,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n14264,5,321,2017-03-23 10:32:14,http://hudson.biz/talon.haag,,251.164.204.26,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n14265,5,321,2017-01-31 13:14:28,http://schmitt.name/madge.lehner,,231.114.143.237,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n14266,5,321,2017-03-12 02:52:18,http://ohara.co/giovanny.leannon,,127.132.60.16,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n14267,5,321,2017-05-06 05:07:00,http://bashirian.info/cielo,,169.150.235.18,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n14268,5,321,2017-02-09 22:28:38,http://oreilly.name/fredy,,189.84.227.38,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n14269,5,321,2017-01-25 12:01:47,http://goodwinconsidine.info/terry,,64.179.247.199,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n14270,5,321,2017-03-20 08:21:48,http://orn.com/rhianna_lueilwitz,,207.136.251.196,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n14271,5,321,2017-02-12 06:34:14,http://bechtelar.com/khalil,,191.24.184.80,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n14272,5,321,2017-03-14 08:23:20,http://jenkinszieme.biz/araceli.stehr,,248.85.99.30,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n14273,5,321,2017-01-20 14:45:05,http://windler.co/hillary,,145.161.229.146,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n14274,5,321,2017-01-08 06:48:58,http://ko.com/bryce.zboncak,,186.248.116.139,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n14275,5,321,2017-05-07 12:27:52,http://ko.name/kristin,,213.150.126.164,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n14276,5,321,2017-06-08 12:31:59,http://cartwrightledner.com/karson,,86.198.83.117,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n14277,5,321,2017-02-06 06:08:08,http://barrows.com/joanny.kohler,,184.7.93.138,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n17208,6,388,2017-01-13 07:13:56,http://franeckischneider.com/deion_schmitt,,123.41.96.47,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n17209,6,388,2017-02-07 20:28:34,http://mante.info/myron,,134.138.99.19,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n17210,6,388,2016-12-16 22:12:45,http://vandervortdaugherty.com/gavin,,90.95.82.158,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n17211,6,388,2017-02-19 06:24:45,http://rodriguezbraun.com/royce.torphy,,253.138.27.113,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n17212,6,388,2017-06-08 10:29:38,http://windlerwelch.name/gianni,,80.52.129.26,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n17213,6,388,2017-03-09 22:46:02,http://abshirelittle.info/winnifred,,84.13.211.172,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n17214,6,388,2017-04-12 20:55:13,http://hintzauer.io/clifford.beier,,201.54.170.67,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n17215,6,388,2017-04-09 07:01:44,http://mohr.io/rylee,,3.61.13.200,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n17216,6,388,2017-01-23 23:29:11,http://deckow.com/heaven,,61.80.72.20,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n17217,6,389,2017-02-18 16:17:07,http://hirtheheel.io/lourdes.davis,,90.112.63.64,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n17218,6,389,2017-02-27 09:50:40,http://yost.net/jonas,,103.5.57.146,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n17219,6,389,2016-12-20 12:20:40,http://hauckklocko.biz/roger,,44.253.31.169,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n17220,6,389,2017-04-29 23:46:51,http://altenwerth.name/tre_greenfelder,,100.77.84.115,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n17221,6,389,2017-01-31 04:05:54,http://lowegutkowski.com/drew_kling,,85.61.23.167,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n17222,6,389,2017-06-10 06:40:00,http://huel.co/malachi_greenholt,,237.176.40.15,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n17223,6,389,2017-05-24 09:33:49,http://volkmanlangworth.info/franz,,81.66.66.55,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n17224,6,389,2017-06-07 23:06:28,http://ernservon.info/jaiden_wehner,,72.47.34.139,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n17225,6,389,2016-12-16 15:51:36,http://runolfsdottirtrantow.name/milo_bayer,,193.66.244.182,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n17226,6,389,2017-04-05 21:09:58,http://moriette.io/hans,,135.167.165.9,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n17227,6,389,2017-05-11 04:50:54,http://cainmarvin.io/randi,,209.115.222.136,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n17228,6,389,2017-04-21 10:19:59,http://dietrich.org/kyler,,180.3.137.244,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n17229,6,389,2017-06-08 22:57:25,http://torphycormier.com/beryl,,78.76.239.136,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n17230,6,389,2017-03-28 17:20:50,http://erdmanmcglynn.com/adolf,,61.229.121.156,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n17231,6,389,2017-01-14 21:39:35,http://gleichnercummings.co/theresia_heel,,126.156.192.156,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n17232,6,389,2017-02-13 23:01:49,http://hoppetorp.name/eloy_johnson,,124.144.206.168,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n17233,6,389,2017-01-07 11:32:58,http://connellyoconner.net/mozelle,,78.44.136.52,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n17234,6,389,2017-01-22 07:19:04,http://stamm.name/claude,,239.33.67.57,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n17235,6,389,2017-04-23 09:56:05,http://champlinkuhic.io/burdette,,68.64.174.147,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n17236,6,389,2017-02-28 20:54:09,http://blandaschiller.co/deanna,,146.26.6.246,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n17237,6,389,2017-05-05 23:17:23,http://zboncakzboncak.biz/britney.beatty,,38.178.148.21,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n17238,6,389,2017-02-04 22:46:45,http://dare.io/marisol.thiel,,32.19.201.5,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n17239,6,389,2017-04-07 19:09:54,http://uptonmoen.biz/eli,,41.216.41.180,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n17240,6,389,2017-04-26 16:15:17,http://corwin.com/rhianna,,204.80.172.175,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n17241,6,389,2017-05-13 01:24:23,http://schmelerquitzon.org/kenton_hansen,,86.19.189.127,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n17242,6,389,2017-01-17 06:36:58,http://champlin.info/cedrick_nicolas,,36.54.53.21,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n17243,6,389,2017-01-10 19:54:48,http://smitham.io/kim.grant,,136.64.234.72,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n17244,6,389,2017-06-01 12:52:29,http://jacobi.net/heaven_brown,,115.86.65.6,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n17245,6,389,2016-12-18 19:31:59,http://hudson.biz/jose.erdman,,63.156.140.204,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n17246,6,389,2017-06-02 19:58:23,http://johns.co/hermina,,212.136.140.66,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n17247,6,389,2017-04-25 08:29:06,http://marvinbahringer.info/mikel,,241.124.152.100,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n17248,6,389,2017-05-26 13:20:29,http://rolfson.co/zola.moore,,216.195.175.40,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n17249,6,389,2017-03-12 15:59:04,http://hintzkuhn.name/kyra.mosciski,,60.220.130.18,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n17250,6,389,2017-03-29 15:28:30,http://oberbrunner.io/lafayette,,150.114.203.101,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n17251,6,389,2017-02-18 19:56:23,http://hermann.name/josiane,,155.109.40.248,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n17252,6,389,2016-12-18 21:06:40,http://okonhahn.io/aubrey,,17.14.132.246,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n17253,6,389,2017-05-09 11:17:21,http://green.info/seamus_legros,,221.65.251.55,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n17254,6,389,2017-04-11 05:24:41,http://maggiocole.io/bryon,,13.119.89.87,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n17255,6,389,2017-01-23 20:53:51,http://rathtorphy.io/stephanie,,243.206.73.24,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n17256,6,389,2016-12-27 08:30:15,http://blanda.org/stuart_oberbrunner,,204.230.190.124,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n17257,6,389,2017-01-15 16:06:03,http://ohara.info/zelda_larson,,68.15.74.182,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n17258,6,389,2017-04-24 08:46:07,http://bernhard.biz/bethany,,224.58.21.210,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n17259,6,389,2017-05-19 13:11:56,http://pfannerstillkuphal.org/trycia.shanahan,,91.152.196.142,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n17260,6,389,2017-05-22 07:33:45,http://reichert.io/laria,,149.43.151.172,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n17261,6,389,2017-02-25 03:55:48,http://langokuneva.org/fernando_bruen,,163.128.243.103,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n17262,6,389,2017-03-29 16:24:02,http://aufderhar.org/dylan,,248.148.153.124,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n4390,2,96,2017-01-03 23:54:55,http://runolfsdottir.co/lamont.crooks,,154.73.250.103,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n4391,2,96,2017-02-13 08:03:09,http://lebsack.org/magnus.wilderman,,168.62.221.49,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n4392,2,96,2017-02-07 22:31:59,http://damore.net/camden.nikolaus,,96.247.237.123,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n4393,2,97,2017-04-05 02:31:20,http://jones.org/garfield,,132.37.71.23,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n4394,2,97,2017-05-14 20:55:19,http://senger.co/ava.doyle,,110.47.113.11,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n4395,2,97,2017-04-03 01:44:03,http://thompson.org/destany_damore,,65.154.37.233,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n4396,2,97,2017-02-22 17:07:49,http://littel.name/thea.schmeler,,27.75.199.186,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n4397,2,97,2016-12-18 04:30:57,http://schmidt.co/madison.lueilwitz,,191.239.105.110,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n4398,2,97,2017-04-17 18:47:56,http://gerhold.biz/muriel,,236.14.16.221,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n4399,2,97,2016-12-25 13:07:24,http://daviskautzer.io/eric,,54.172.240.47,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n4400,2,97,2017-01-16 06:35:53,http://christiansen.io/torey,,20.92.83.154,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n4401,2,97,2017-01-06 04:43:15,http://cremin.net/fidel,,245.135.169.130,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n4402,2,97,2016-12-30 06:44:33,http://reinger.co/casimir_roberts,,115.41.254.130,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n4403,2,97,2017-02-06 23:11:04,http://cruickshank.biz/aurelia,,143.251.189.254,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n4404,2,97,2017-05-14 20:57:24,http://dibbert.net/reba_wolf,,240.37.130.74,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n4405,2,97,2017-01-14 17:29:48,http://hilll.co/amari.stroman,,185.90.196.249,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n4406,2,97,2017-04-29 07:53:27,http://rolfson.net/amy_mosciski,,241.166.100.191,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n4407,2,97,2017-05-17 01:02:50,http://wizamurazik.com/elta,,95.86.164.50,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n4408,2,97,2017-04-27 16:30:55,http://beahan.net/fanny,,142.196.182.27,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n4409,2,97,2017-03-16 18:19:04,http://konopelskibernier.io/caroline,,95.157.203.178,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n4410,2,97,2017-02-03 20:30:48,http://predovic.co/magdalen,,241.224.45.177,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n4411,2,97,2017-02-19 14:09:23,http://sipes.co/delbert_parker,,185.81.106.202,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n4412,2,97,2017-05-26 03:47:37,http://krislesch.biz/keshawn.weber,,187.108.169.156,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n4413,2,97,2017-03-15 19:22:55,http://eichmann.name/vito,,53.209.238.56,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n4414,2,97,2016-12-23 22:21:44,http://pollichboyer.org/eudora,,65.188.48.174,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n4415,2,97,2017-02-01 00:41:15,http://mann.org/priscilla.jacobson,,201.11.217.75,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n4416,2,97,2016-12-23 11:34:34,http://batz.com/erica,,188.53.17.73,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n4417,2,97,2017-01-03 08:46:02,http://wymanschamberger.io/kaci,,184.196.117.146,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n4418,2,97,2017-01-17 06:33:24,http://dicki.co/mabel.prosacco,,203.108.26.25,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n4419,2,97,2016-12-19 01:35:09,http://walsh.info/thelma,,177.217.153.65,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n4420,2,97,2017-02-19 22:43:49,http://block.biz/bailee,,125.243.47.12,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n4421,2,97,2017-05-16 13:07:10,http://weimann.biz/bailey.christiansen,,54.249.140.233,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n4422,2,97,2017-02-10 21:29:31,http://ankunding.co/mandy.wolf,,146.245.167.175,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n4423,2,97,2017-01-16 00:51:41,http://heidenreich.com/pete,,171.22.49.206,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n4424,2,97,2017-06-11 09:52:54,http://gerholdrunte.io/jordon,,75.110.246.58,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n4425,2,97,2016-12-29 21:32:05,http://emard.co/ora_bednar,,221.240.159.254,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n4426,2,97,2017-04-08 02:23:00,http://stammmann.com/elta_heathcote,,61.84.65.96,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n4427,2,97,2017-01-07 12:53:42,http://lakinward.io/albina.halvorson,,17.173.24.225,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n4428,2,97,2017-05-29 20:05:06,http://feil.org/hanna,,69.241.116.50,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n4429,2,97,2017-06-13 22:35:30,http://kutch.co/libby,,53.74.47.162,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n4430,2,97,2017-05-24 13:12:29,http://runolfsdottirpaucek.info/augustine.hodkiewicz,,114.110.242.4,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n4431,2,97,2016-12-15 18:09:28,http://boylehowell.net/perry.klein,,70.78.110.215,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n4432,2,97,2017-03-20 12:10:14,http://stantonfisher.co/antoinette,,219.137.239.25,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n4433,2,97,2017-04-27 21:49:16,http://mcglynn.name/ian.armstrong,,191.133.67.66,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n4434,2,97,2017-03-13 22:59:54,http://botsfordpacocha.info/isabelle,,202.106.126.45,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n4435,2,97,2017-03-30 00:02:34,http://west.org/jakob.feeney,,221.226.174.43,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n4436,2,97,2017-04-29 19:19:22,http://smithnitzsche.co/watson_hayes,,38.143.142.168,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n4437,2,97,2016-12-24 15:24:12,http://roobbreitenberg.name/aletha_monahan,,40.133.86.250,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n4438,2,97,2017-02-19 08:57:30,http://welch.org/terrill,,6.155.122.152,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n4439,2,97,2017-05-22 23:45:59,http://schinnermayer.info/florian_bechtelar,,192.96.220.157,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n4440,2,97,2017-01-18 05:11:05,http://baumbachwatsica.io/boyd,,158.216.129.10,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n4441,2,97,2017-05-15 06:42:21,http://boylelehner.io/roma_bahringer,,56.71.106.202,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n4442,2,97,2016-12-28 13:33:51,http://barton.io/daniella,,84.5.239.162,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n4443,2,97,2017-02-10 02:10:06,http://jenkins.biz/jon,,207.137.12.212,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n4444,2,97,2017-02-23 07:52:40,http://stiedemann.info/alek,,11.155.36.17,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n4445,2,97,2017-04-02 19:40:58,http://murphy.net/reyes_daniel,,43.155.163.72,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n11328,4,253,2017-04-08 13:43:01,http://stanton.net/monserrate,,42.10.16.57,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n11329,4,253,2017-02-20 15:12:01,http://batz.net/kristina,,228.94.152.223,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n11330,4,253,2017-01-16 18:27:51,http://lehner.info/peggie_ankunding,,251.211.2.231,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n11331,4,253,2017-04-09 15:43:58,http://baumbachshanahan.co/stefan,,199.139.168.146,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n11332,4,253,2017-01-28 10:40:50,http://flatleylockman.io/kiana_sauer,,162.194.149.160,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n11333,4,253,2017-03-21 07:43:23,http://gottliebstiedemann.name/salvatore_bradtke,,56.89.16.105,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n11334,4,253,2017-06-09 21:52:53,http://langoshheidenreich.name/santina.bartoletti,,116.4.90.22,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n11335,4,253,2017-02-11 04:19:36,http://douglas.com/steve_oconnell,,237.171.190.32,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n11336,4,253,2017-01-06 09:39:16,http://bosco.com/jeie_gerhold,,82.70.123.38,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n11337,4,253,2017-01-16 07:57:31,http://streich.info/jaylan.mcdermott,,122.61.6.222,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n11338,4,254,2017-03-04 01:54:39,http://crist.net/leilani.abshire,,140.243.78.44,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n11339,4,254,2017-01-21 03:37:06,http://wehner.biz/fredrick_kovacek,,211.156.197.91,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n11340,4,254,2017-03-18 14:57:29,http://stroman.biz/antonio,,81.103.100.12,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n11341,4,254,2017-05-14 14:43:47,http://auerkeebler.net/thalia_rodriguez,,61.151.22.168,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n11342,4,254,2017-01-21 18:50:39,http://weinat.com/emie,,214.20.45.185,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n11343,4,254,2017-06-10 09:02:08,http://klein.com/alvina_bahringer,,196.133.169.117,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n11344,4,254,2017-04-22 14:58:36,http://lefflerhackett.org/shanon,,62.185.137.230,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n11345,4,254,2017-03-19 14:31:16,http://gaylordpfeffer.info/jeffery_walker,,253.23.213.126,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n11346,4,254,2017-02-28 01:53:12,http://keler.io/torrey,,34.70.46.133,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n11347,4,254,2016-12-26 05:16:57,http://stoltenberg.co/philip_kaulke,,67.178.60.163,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n11348,4,254,2017-01-20 21:07:08,http://walsh.io/darren,,101.161.146.14,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n11349,4,254,2017-02-28 04:38:44,http://kohler.info/frances_schumm,,53.164.174.19,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n11350,4,254,2016-12-31 06:25:56,http://waters.info/gabrielle,,166.148.239.163,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n11351,4,254,2017-02-27 17:21:56,http://gradyrice.io/eleanora_buckridge,,228.166.158.72,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n11352,4,254,2017-04-19 09:22:49,http://jakubowski.org/brigitte.pfannerstill,,222.240.221.114,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n11353,4,254,2016-12-15 13:07:20,http://blick.info/dion,,61.120.165.221,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n11354,4,254,2017-04-11 20:05:26,http://stoltenbergschulist.com/loyal_hintz,,88.53.213.133,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n11355,4,254,2017-02-03 15:16:53,http://herman.org/mackenzie_gusikowski,,24.189.208.7,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n11356,4,254,2017-05-30 18:08:36,http://rowe.name/jackeline.bins,,179.99.69.183,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n11357,4,254,2017-02-22 05:04:33,http://bayer.com/mireille,,198.75.177.79,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n11358,4,254,2017-05-23 13:09:21,http://morar.co/alyon,,10.202.121.60,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n11359,4,254,2017-01-17 22:47:49,http://langworthyost.org/hilton_beahan,,228.111.52.225,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n11360,4,254,2017-06-08 00:34:37,http://smithamskiles.com/janice,,104.181.127.13,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n11361,4,254,2016-12-31 21:20:38,http://nienowhalvorson.biz/meredith.jones,,148.163.218.52,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n11362,4,254,2017-05-14 21:59:13,http://schaefer.biz/marilie_bashirian,,189.236.20.116,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n11363,4,254,2017-05-25 22:54:17,http://schuppe.name/arnulfo,,196.194.7.249,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n11364,4,254,2017-03-14 06:45:55,http://swaniawski.info/rose,,42.78.104.226,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n11365,4,254,2017-05-06 08:10:55,http://collins.net/josh.feeney,,143.7.103.65,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n11366,4,254,2017-05-07 04:08:14,http://leffler.com/hillard.kuhic,,79.59.234.149,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n11367,4,254,2017-02-13 01:38:27,http://legros.io/jeanie,,27.249.167.206,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n11368,4,254,2017-01-08 23:07:45,http://torp.name/karson,,22.169.33.161,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n11369,4,254,2017-05-17 19:44:49,http://waters.com/oma_dicki,,130.226.55.130,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n11370,4,254,2017-01-23 17:59:31,http://beier.net/marques,,144.90.93.120,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n11371,4,254,2017-05-21 06:28:39,http://erdman.io/bernard,,150.228.242.167,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n11372,4,254,2017-02-17 00:36:39,http://dietrich.org/leatha,,65.218.221.65,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n11373,4,254,2017-06-04 17:05:40,http://bruen.org/alberto,,11.128.248.198,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n11374,4,254,2017-02-28 04:05:48,http://harvey.name/danial_pouros,,202.241.160.249,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n11375,4,254,2017-02-01 14:12:27,http://franecki.biz/marisol_west,,160.46.65.33,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n11376,4,254,2017-01-29 10:17:31,http://spinka.co/yasmine,,50.214.117.41,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n11377,4,254,2017-05-24 09:01:36,http://wintheiser.org/randal,,57.94.145.253,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n11378,4,254,2017-05-20 07:17:53,http://windler.co/jaleel,,173.210.94.87,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n11379,4,254,2017-03-20 09:23:13,http://batz.co/monserrate,,112.169.137.247,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n11380,4,254,2017-02-02 19:26:39,http://champlinbeer.name/burnice.kuphal,,86.72.94.239,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n11381,4,254,2017-02-12 12:31:43,http://greenfelderdonnelly.org/jeika.simonis,,73.45.52.36,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n11382,4,254,2017-05-09 19:44:45,http://kochankunding.info/chelsea,,34.132.92.229,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n8328,3,185,2017-03-11 10:28:49,http://stoltenberg.org/carole_nader,,13.84.179.104,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n8329,3,185,2017-05-27 06:46:30,http://runolfon.com/jamil,,80.18.142.10,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n8330,3,185,2016-12-27 07:03:25,http://walker.info/lacey.balistreri,,246.126.70.102,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n8331,3,185,2017-05-30 08:34:06,http://halvorson.org/ahmed_reilly,,159.233.36.218,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n8332,3,185,2017-01-07 21:59:36,http://vandervort.info/shane,,232.179.232.81,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n8333,3,185,2017-02-24 08:51:17,http://hoppe.info/mitchel.harber,,223.112.89.6,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n8334,3,185,2017-04-02 03:58:10,http://ratke.io/annabell,,232.241.32.73,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n8335,3,185,2017-02-27 19:21:13,http://hirthe.net/erna.schmitt,,210.251.44.245,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n8336,3,185,2017-05-08 03:07:16,http://altenwerthdooley.io/glen,,142.244.145.5,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n8337,3,185,2017-05-02 05:41:37,http://mcglynn.info/zena_powlowski,,204.66.140.187,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n8338,3,185,2017-06-08 07:34:34,http://gerlach.name/imogene,,176.218.219.222,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n8339,3,186,2017-04-06 02:22:00,http://kundeyost.co/samson.roob,,194.230.130.110,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n8340,3,186,2017-05-25 03:44:57,http://mclaughlin.com/kenyon_witting,,59.13.216.153,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n8341,3,186,2017-03-23 11:13:15,http://robelfeest.io/mauricio,,23.28.74.68,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n8342,3,186,2017-04-16 21:21:06,http://bosco.info/maia_wisoky,,202.87.236.216,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n8343,3,186,2017-03-05 23:34:01,http://trantow.com/erling.will,,65.127.230.4,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n8344,3,186,2017-02-13 07:29:28,http://blanda.com/rhea.strosin,,27.145.207.77,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n8345,3,186,2017-05-05 12:46:00,http://mueller.biz/gerald,,114.35.37.106,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n8346,3,186,2017-02-05 23:46:59,http://berge.biz/nina.connelly,,166.7.47.139,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n8347,3,186,2017-06-12 03:16:36,http://dibbert.org/collin_corwin,,32.83.12.46,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n8348,3,186,2017-01-21 14:29:48,http://veum.io/candida,,212.37.64.146,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n8349,3,186,2017-04-09 07:34:46,http://mueller.co/lucienne.koepp,,25.40.112.109,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n8350,3,186,2016-12-23 17:31:59,http://gislasonhilpert.net/allan.wisoky,,205.245.6.172,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n8351,3,186,2017-04-14 20:10:01,http://schmidtlindgren.co/ian,,22.194.150.159,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n8352,3,186,2017-05-09 04:09:14,http://boyle.co/delaney_damore,,36.71.184.54,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n8353,3,186,2017-02-12 16:44:40,http://herzoghaley.io/rickie.strosin,,76.205.53.182,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n8354,3,186,2017-04-30 21:31:23,http://lueilwitz.biz/bettie.grimes,,254.99.118.243,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n8355,3,186,2017-01-20 20:28:04,http://auer.name/gianni,,150.113.48.52,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n8356,3,186,2017-04-04 03:15:05,http://ko.io/christopher,,41.138.177.6,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n8357,3,186,2017-04-29 21:28:09,http://lubowitz.co/ellen,,67.128.157.145,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n8358,3,186,2017-02-27 23:31:36,http://lakin.co/reese,,166.130.180.198,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n8359,3,186,2017-05-12 16:55:23,http://collins.co/thad.trantow,,141.171.126.232,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n8360,3,186,2017-04-10 01:36:43,http://koch.io/ulises,,183.104.81.108,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n8361,3,186,2017-04-18 14:37:56,http://doyle.info/arnaldo.ledner,,46.7.117.248,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n8362,3,186,2017-02-18 03:31:34,http://brownkovacek.io/joanie_koelpin,,55.79.11.203,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n8363,3,187,2017-04-24 06:21:25,http://mccullough.co/marco.herman,,51.17.147.119,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n8364,3,187,2017-03-30 14:38:14,http://mayer.net/dudley_vonrueden,,162.183.229.39,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n8365,3,187,2017-05-25 15:00:40,http://johnston.name/georgianna,,60.235.147.157,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n8366,3,187,2017-04-20 00:35:12,http://kutchohara.co/hanna_hagenes,,160.24.3.21,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n8367,3,187,2017-03-02 08:42:28,http://stoltenbergkoepp.biz/callie_hauck,,51.185.20.156,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n8368,3,187,2017-05-07 13:25:30,http://leschdoyle.name/elza,,105.5.113.175,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n8369,3,187,2017-05-22 15:28:37,http://lueilwitzschowalter.info/dejah.swaniawski,,45.62.136.200,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n8370,3,187,2017-06-01 17:18:19,http://wintheiser.info/leonie_ruel,,54.187.136.187,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n8371,3,187,2017-04-02 14:08:20,http://schmidt.biz/angelita,,185.60.55.133,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n8372,3,187,2017-02-07 06:11:35,http://macgyver.org/casey,,41.86.35.201,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n8373,3,187,2017-02-08 08:45:21,http://collins.co/lizeth.beatty,,190.26.163.225,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n8374,3,187,2017-01-21 10:01:14,http://gleichner.io/rahul,,54.252.109.9,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n8375,3,187,2017-05-31 05:47:39,http://dibberthowe.net/janet,,191.23.213.108,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n8376,3,187,2017-05-02 13:12:37,http://schneider.name/blair,,106.106.245.99,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n8377,3,187,2017-04-25 04:45:05,http://fisher.com/betsy,,88.42.133.61,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n8378,3,187,2016-12-25 20:52:41,http://toy.io/fanny.wunsch,,61.157.4.81,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n8379,3,187,2016-12-14 08:40:30,http://beereffertz.co/meggie_koelpin,,155.129.154.106,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n8380,3,187,2017-05-03 23:37:35,http://nikolaus.co/brianne_cormier,,104.229.205.223,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n8381,3,187,2017-02-23 15:53:38,http://leschfunk.net/concepcion.ortiz,,41.238.67.41,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n8382,3,187,2017-03-27 16:30:26,http://fritsch.org/milan,,21.132.243.146,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n8383,3,187,2017-01-21 21:30:53,http://legros.biz/darlene.frami,,71.185.67.72,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n14278,5,321,2017-01-28 10:45:08,http://terryarmstrong.com/vesta,,224.117.140.36,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n14279,5,321,2017-06-05 18:57:20,http://okuneva.info/leanne,,210.252.235.239,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n14280,5,321,2017-03-21 09:47:19,http://ruel.io/eloisa,,13.149.117.156,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n14281,5,321,2017-02-27 17:01:16,http://raynor.net/kathryn,,34.26.159.210,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n14282,5,321,2016-12-22 20:53:40,http://krajcik.org/paolo,,163.165.123.133,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n14283,5,321,2017-02-13 13:51:52,http://wisozk.net/etha,,17.229.176.163,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n14284,5,321,2017-04-16 12:39:28,http://ondricka.co/gregg.gerlach,,3.39.22.165,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n14285,5,321,2017-01-30 06:23:34,http://dibbertnikolaus.info/nick_murphy,,138.251.125.164,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n14286,5,321,2017-01-24 20:23:49,http://bernhard.io/shirley,,172.111.201.94,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n14287,5,321,2017-05-04 11:57:35,http://mills.com/patience,,15.180.208.75,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n14288,5,321,2017-03-22 17:20:00,http://walshmraz.org/alicia,,251.51.84.46,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n14289,5,321,2017-02-23 22:52:01,http://connborer.com/haskell_gerhold,,216.253.7.201,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n14290,5,321,2017-06-03 01:35:25,http://reichert.biz/alyce.dickens,,77.196.21.240,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n14291,5,321,2017-06-10 10:41:55,http://hauckcruickshank.com/johnathon.bode,,123.240.22.204,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n14292,5,321,2017-02-20 22:08:22,http://gutmann.org/santos.wintheiser,,241.2.169.7,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n14293,5,321,2017-04-12 22:06:12,http://legros.net/chanel_pacocha,,156.221.114.226,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n14294,5,321,2017-02-08 06:48:31,http://adams.biz/christian,,243.245.230.251,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n14295,5,321,2017-03-27 19:56:04,http://morietteschaefer.org/felix.walker,,131.223.80.205,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n14296,5,321,2017-05-19 00:08:50,http://kuhn.co/monique_labadie,,200.101.87.228,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n14297,5,321,2016-12-19 17:07:25,http://macejkovichegmann.info/emily_mills,,148.90.189.30,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n14298,5,321,2017-02-21 18:46:45,http://schimmel.co/orion_bernier,,150.249.158.178,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n14299,5,321,2017-02-11 03:12:44,http://auer.co/taylor.dietrich,,63.232.19.185,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n14300,5,321,2017-01-26 09:35:40,http://krajcikchristiansen.co/dovie,,183.32.71.174,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n14301,5,321,2016-12-30 19:08:18,http://wunsch.co/kathryn_hettinger,,89.210.13.6,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n14302,5,322,2017-04-05 10:24:48,http://hammes.org/delia.yundt,,67.162.63.150,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n14303,5,322,2017-01-12 01:55:32,http://bechtelargulgowski.info/ayana.smith,,13.158.241.193,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n14304,5,322,2017-02-08 21:38:01,http://dickens.net/ryleigh.osinski,,44.253.171.79,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n14305,5,322,2017-06-11 04:00:07,http://gerhold.info/matilde.nolan,,15.164.73.144,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n14306,5,322,2017-04-19 18:25:52,http://sipesabbott.name/ottilie,,197.171.26.224,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n14307,5,322,2017-01-27 10:20:24,http://leuschke.com/melvina.hudson,,182.179.163.10,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n14308,5,322,2017-06-05 09:17:17,http://heidenreich.org/furman_schowalter,,99.17.19.78,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n14309,5,322,2017-05-08 07:24:13,http://lehner.org/rosalyn.farrell,,222.16.64.88,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n14310,5,322,2017-05-06 11:19:55,http://brekkebuckridge.net/elisa_keeling,,156.169.141.251,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n14311,5,322,2017-03-21 09:11:23,http://mosciski.biz/americo_durgan,,140.106.125.156,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n14312,5,322,2017-02-19 23:34:19,http://johnsonpowlowski.org/cristobal.halvorson,,175.237.62.234,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n14313,5,322,2017-06-10 02:08:31,http://gerlach.net/sophia.reilly,,93.244.100.103,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n14314,5,322,2017-02-09 07:34:30,http://ledner.name/carlie.schowalter,,198.45.13.75,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n14315,5,322,2017-05-08 18:20:13,http://vonhegmann.biz/dorris,,147.84.230.154,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n14316,5,322,2017-05-08 18:22:29,http://lynch.com/selina.shields,,12.46.50.231,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n14317,5,322,2016-12-14 06:09:45,http://durgan.io/osbaldo.mills,,18.172.206.9,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n14318,5,322,2017-02-23 19:10:50,http://metz.io/oma_bayer,,13.169.62.178,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n14319,5,322,2017-05-26 21:35:56,http://stantonolson.io/ignacio.nitzsche,,106.235.186.124,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n14320,5,322,2017-02-27 15:32:58,http://cruickshank.net/lucius,,7.163.49.152,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n14321,5,322,2017-02-14 08:35:51,http://rodriguez.name/neha.ebert,,231.134.111.72,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n14322,5,322,2016-12-29 18:44:33,http://waelchi.com/tillman,,72.221.33.203,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n14323,5,322,2017-03-05 18:15:00,http://jacobs.net/bo,,70.67.70.124,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n14324,5,322,2017-04-23 04:52:20,http://gerholdritchie.name/mittie,,231.125.251.198,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n14325,5,322,2017-05-12 03:14:53,http://ziemann.org/maverick,,14.176.16.101,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n14326,5,322,2016-12-22 08:31:04,http://barton.com/aunta.parisian,,24.81.244.42,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n14327,5,322,2016-12-16 20:34:15,http://lynchmarquardt.biz/erik,,192.187.28.137,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n14328,5,322,2017-01-15 19:07:23,http://okeefe.org/isabell,,85.209.83.183,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n14329,5,322,2017-02-22 02:53:27,http://wolffschmeler.net/jeremy,,73.180.99.222,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n14330,5,322,2017-06-01 23:30:07,http://keeling.biz/izabella,,168.17.142.56,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n14331,5,322,2017-01-05 05:40:55,http://roberts.info/trea.hane,,167.4.111.184,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n14332,5,322,2017-02-02 10:34:04,http://dare.io/rosalinda.rogahn,,185.37.72.203,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n17263,6,389,2017-04-05 23:00:29,http://kilbackhane.net/verla,,162.26.198.57,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n17264,6,389,2017-04-26 03:41:25,http://greenfelder.co/mercedes,,221.56.188.9,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n17265,6,389,2017-05-22 03:28:37,http://berge.name/kareem,,125.97.254.66,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n17266,6,389,2017-03-17 13:16:04,http://abshire.io/gretchen_dare,,65.158.87.9,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n17267,6,389,2017-05-11 21:44:19,http://wizazulauf.info/willis,,43.211.197.241,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n17268,6,389,2017-05-07 20:43:58,http://altenwerth.net/darby_von,,100.159.147.109,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n17269,6,389,2017-03-13 10:24:33,http://wolfruel.net/fermin,,46.70.81.40,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n17270,6,389,2017-05-22 19:37:45,http://willms.co/berenice,,193.229.240.88,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n17271,6,389,2017-04-03 12:06:16,http://hermiston.info/celestine.walker,,241.72.41.215,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n17272,6,389,2016-12-30 12:11:19,http://quigleywaters.net/arely,,107.154.164.220,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n17273,6,390,2017-04-30 01:39:41,http://kemmer.name/soledad.konopelski,,105.129.241.166,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n17274,6,390,2017-01-20 13:37:16,http://conroy.io/garrick.bartoletti,,215.99.221.149,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n17275,6,390,2017-04-01 02:25:50,http://franeckibahringer.io/fletcher,,122.138.156.198,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n17276,6,390,2017-01-17 14:14:00,http://jacobi.biz/nicola,,181.72.227.151,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n17277,6,390,2017-05-08 19:07:16,http://emard.co/elouise,,104.77.146.219,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n17278,6,390,2017-01-10 08:25:20,http://wisokyveum.io/cade,,86.75.82.71,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n17279,6,390,2017-03-30 11:07:57,http://abernathykoepp.co/meagan,,57.12.109.63,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n17280,6,390,2017-03-31 13:16:26,http://donnelly.net/glenna,,145.112.86.244,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n17281,6,390,2017-04-26 17:15:56,http://white.name/ibrahim,,198.200.77.245,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n17282,6,390,2017-02-04 16:20:38,http://goldner.com/willard_brown,,238.217.51.230,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n17283,6,390,2017-01-05 18:12:05,http://von.net/margaretta,,157.172.134.72,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n17284,6,390,2016-12-13 18:11:19,http://wittingarmstrong.org/hudson_kerluke,,184.52.153.135,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n17285,6,390,2017-04-05 16:57:11,http://schuppe.io/donny,,100.30.153.71,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n17286,6,390,2017-04-08 18:46:19,http://satterfieldko.net/callie.white,,218.191.74.248,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n17287,6,390,2017-03-17 08:22:54,http://schmeleroconner.biz/evangeline_conroy,,127.204.8.139,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n17288,6,390,2017-05-03 07:39:21,http://mohrhoeger.name/alia.mcdermott,,51.143.219.104,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n17289,6,390,2017-05-02 12:41:49,http://quitzonhowell.co/idell,,182.76.108.97,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n17290,6,390,2017-01-16 10:42:58,http://kohler.org/haleigh,,111.53.254.214,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n17291,6,390,2016-12-31 10:13:37,http://durgan.biz/wilhelm,,5.35.236.33,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n17292,6,390,2017-04-12 12:10:21,http://casper.net/boris,,226.199.190.9,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n17293,6,390,2017-03-22 08:08:08,http://watersromaguera.info/madelynn.lynch,,41.175.43.23,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n17294,6,390,2016-12-22 00:55:45,http://dickinsonkuhic.net/titus.weimann,,206.215.21.55,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n17295,6,390,2017-04-21 22:15:33,http://ziemann.net/nasir.jones,,110.215.119.105,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n17296,6,390,2017-01-27 19:43:50,http://waelchi.com/elia.flatley,,106.14.130.200,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n17297,6,390,2017-06-08 07:27:45,http://stehr.org/golden.stokes,,68.173.158.16,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n17298,6,390,2017-01-28 03:03:12,http://osinski.org/orlando.ryan,,57.14.4.88,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n17299,6,390,2017-05-07 15:15:16,http://macejkovichauck.info/manuel.white,,106.74.105.63,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n17300,6,390,2017-01-11 10:31:45,http://rueckerkiehn.co/mona_kling,,133.176.180.107,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n17301,6,390,2017-02-03 12:42:57,http://ratke.co/carey,,114.213.244.29,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n17303,6,390,2017-05-24 10:26:12,http://schaefer.io/jacques,,32.163.182.10,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n17304,6,390,2016-12-16 03:06:54,http://hermanhoppe.io/lavada,,112.139.212.193,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n17305,6,390,2017-06-05 22:08:38,http://brakus.io/pansy,,153.149.98.27,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n17306,6,390,2017-03-30 11:17:45,http://lang.info/reyna.upton,,21.147.74.226,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n17307,6,390,2017-01-02 10:12:48,http://spencer.co/devan_franecki,,244.169.75.32,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n17308,6,390,2017-04-19 08:20:44,http://cummingsaufderhar.name/marlin,,209.122.197.240,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n17309,6,390,2017-01-05 06:08:22,http://vonruedenwhite.name/helga,,16.117.213.3,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n17310,6,390,2017-01-01 10:42:34,http://hoppe.com/johnathan.fadel,,96.148.120.150,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n17311,6,390,2017-03-06 18:49:40,http://fadelcorkery.com/trevion.bahringer,,146.253.13.76,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n17312,6,390,2017-03-18 09:00:20,http://oconnercrist.net/patience,,15.194.237.229,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n17313,6,390,2017-03-07 18:53:25,http://connhayes.info/kacie.streich,,245.147.126.247,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n17314,6,390,2017-04-05 09:16:10,http://schamberger.com/crystal_paucek,,147.145.146.59,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n17315,6,391,2017-05-27 09:43:53,http://hickledavis.info/lilly_weber,,52.236.111.109,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n17316,6,391,2016-12-25 05:22:35,http://breitenberg.name/matilde,,216.65.211.210,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n17317,6,391,2017-02-02 10:12:53,http://baumbach.co/marcel_kshlerin,,119.210.172.121,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n17318,6,391,2017-03-26 15:27:12,http://littel.io/loma,,27.100.221.83,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n4446,2,97,2017-05-15 18:17:43,http://jaskolski.io/major,,236.127.180.143,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n4447,2,97,2017-01-15 14:18:47,http://deckowbarrows.org/althea,,99.19.55.183,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n4448,2,98,2017-02-20 19:03:54,http://beier.org/betty,,51.132.211.150,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n4449,2,98,2017-03-31 00:53:37,http://gulgowski.co/bo,,27.251.82.198,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n4450,2,98,2017-05-25 18:32:18,http://collier.co/rory.williamson,,63.155.185.207,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n4451,2,98,2017-05-07 01:24:09,http://lueilwitz.com/aleandra,,70.49.24.53,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n4452,2,98,2017-02-16 04:02:09,http://hintz.co/arden_altenwerth,,6.221.13.191,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n4453,2,98,2017-04-01 11:01:34,http://mcdermottkihn.io/olin_hodkiewicz,,129.85.129.59,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n4454,2,98,2017-02-07 03:52:57,http://bode.com/dolores,,14.119.205.252,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n4455,2,98,2017-04-21 14:08:15,http://bednar.co/pinkie,,56.71.107.209,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n4456,2,98,2017-02-27 03:20:29,http://miller.biz/sean,,181.108.111.52,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n4457,2,98,2017-01-31 14:01:34,http://gorczany.com/justyn,,96.78.20.182,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n4458,2,98,2017-02-07 14:35:47,http://treutel.io/sim_gusikowski,,63.16.107.252,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n4459,2,98,2017-01-01 04:11:30,http://fadel.io/corene.yost,,108.37.196.21,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n4460,2,98,2017-01-02 09:23:53,http://brekkehackett.net/celine.koch,,19.91.246.126,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n4461,2,98,2017-04-02 15:27:33,http://oreilly.co/carson_franecki,,184.191.109.118,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n4462,2,98,2017-05-19 13:23:30,http://lebsack.net/destany,,27.217.139.43,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n4463,2,98,2017-03-29 07:43:13,http://dach.net/mabel,,131.190.99.97,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n4464,2,98,2016-12-20 21:34:09,http://yost.net/rozella,,107.120.166.67,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n4465,2,98,2017-05-08 07:28:10,http://bartell.info/alex.raynor,,214.157.149.253,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4466,2,98,2017-05-27 19:35:28,http://harveyeffertz.name/dorcas,,92.181.222.173,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n4467,2,98,2016-12-26 11:31:14,http://kunde.name/andrew,,15.198.121.65,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n4468,2,98,2017-04-10 00:39:34,http://marquardt.biz/else.ward,,28.123.59.27,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n4469,2,98,2017-05-17 19:41:36,http://greenfelder.io/helga,,165.103.196.6,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n4470,2,98,2017-02-13 06:29:47,http://hermann.org/cayla.tromp,,240.154.51.63,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n4471,2,98,2017-01-13 16:09:47,http://yost.biz/janae.cummerata,,17.154.183.12,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n4472,2,98,2017-01-16 18:21:31,http://colewill.co/bobby_satterfield,,30.140.43.59,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n4473,2,98,2017-03-25 05:59:04,http://murphy.org/isabelle_marks,,16.62.88.155,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n4474,2,98,2017-04-23 09:57:18,http://schadenankunding.info/christina,,30.210.65.56,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4475,2,98,2017-03-11 07:40:13,http://gerhold.com/nia_daugherty,,27.24.149.140,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n4476,2,98,2017-01-07 00:02:16,http://conroyhodkiewicz.co/brad.sauer,,112.17.108.115,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n4477,2,98,2017-04-18 04:58:28,http://krisnikolaus.org/marianne.mohr,,100.109.142.40,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n4478,2,98,2017-03-08 01:30:53,http://hand.org/joanie.dooley,,66.21.46.127,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n4479,2,98,2017-03-29 15:21:22,http://reichelconsidine.co/destin,,79.26.5.16,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n4480,2,98,2017-02-27 03:57:29,http://quigley.biz/stefanie_wilkinson,,127.155.54.177,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n4481,2,99,2017-03-21 20:20:58,http://rennerrath.info/mattie.gusikowski,,240.145.6.250,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n4482,2,99,2017-06-13 13:31:45,http://denesik.info/sibyl,,127.248.237.241,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n4483,2,99,2017-02-26 21:34:09,http://mertz.io/milton,,42.203.254.176,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n4484,2,99,2016-12-15 16:22:32,http://anderson.io/abelardo_mraz,,190.15.26.219,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n4485,2,99,2016-12-14 23:10:10,http://heller.name/cyril_kertzmann,,157.135.158.190,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n4486,2,99,2017-01-09 22:55:09,http://effertz.net/wyatt_wiegand,,140.138.140.194,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n4487,2,99,2017-03-02 13:45:10,http://torp.biz/rosalind,,129.212.251.111,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n4488,2,99,2017-02-02 02:32:04,http://bode.info/mariano.corkery,,225.98.152.231,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n4489,2,99,2017-04-18 11:45:38,http://swift.org/don_lemke,,219.245.130.191,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n4490,2,99,2017-03-04 02:46:29,http://christiansen.net/sandy_muller,,88.194.65.245,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n4491,2,99,2017-04-17 07:41:56,http://rice.biz/estevan_jerde,,99.34.34.235,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n4492,2,99,2017-05-14 06:21:19,http://kuhlman.info/toney,,40.150.142.226,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n4493,2,99,2017-06-03 21:02:40,http://lemke.name/karen.bode,,54.106.48.114,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n4494,2,99,2017-06-11 11:45:40,http://kovacek.biz/layla,,56.93.157.16,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n4495,2,99,2017-03-24 14:50:42,http://weimann.com/mortimer.koelpin,,74.43.60.150,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n4496,2,99,2016-12-13 20:34:21,http://herman.io/ibrahim_friesen,,224.145.120.135,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n4497,2,99,2017-06-08 16:42:59,http://macejkovicullrich.info/gloria_schultz,,213.125.69.59,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n4498,2,99,2017-02-13 12:22:57,http://harvey.info/ernestina,,109.165.173.138,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n4499,2,99,2017-05-23 08:31:52,http://conroyweber.org/toy.bauch,,103.160.232.163,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n4500,2,99,2017-05-05 13:42:15,http://vonrueden.co/ali,,45.148.213.11,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n4501,2,99,2017-02-24 21:03:35,http://ziemeprice.net/wallace_hermiston,,249.170.118.79,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n11383,4,254,2017-02-16 19:17:49,http://schuster.info/dominic,,220.202.97.55,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n11384,4,254,2017-06-11 23:43:38,http://morargaylord.org/toni.hilll,,250.114.6.117,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n11385,4,254,2017-04-02 10:02:47,http://borerbreitenberg.net/eda,,139.224.228.240,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n11386,4,254,2017-04-03 12:09:08,http://thiel.net/nestor_lubowitz,,204.59.34.181,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n11387,4,254,2017-05-25 01:38:39,http://hoppeoconner.info/michaela,,150.254.145.5,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n11388,4,254,2017-04-08 20:08:50,http://keeling.name/laria,,130.62.195.142,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n11389,4,254,2017-02-07 21:41:15,http://rau.com/eliza,,234.46.78.96,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n11390,4,254,2017-02-06 23:10:44,http://greenoconnell.org/freeda.turner,,163.156.30.117,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n11391,4,254,2017-01-23 00:41:13,http://funk.com/dolores_powlowski,,202.110.17.134,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n11392,4,254,2017-02-01 19:53:45,http://littleauer.org/sydnie,,252.177.208.101,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n11393,4,254,2017-02-01 06:21:45,http://torphy.info/corene,,239.100.57.26,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n11394,4,254,2017-01-14 04:17:52,http://brown.biz/kyla,,49.178.108.20,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n11395,4,254,2016-12-28 18:42:42,http://homenick.name/armando,,134.174.188.63,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n11396,4,254,2017-03-30 06:09:14,http://goldnercrona.net/olaf_hilpert,,204.246.112.159,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n11397,4,254,2017-02-07 10:34:24,http://tillman.net/charlene,,230.161.34.219,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n11398,4,254,2017-04-19 19:33:57,http://terry.co/serena.dibbert,,123.203.71.96,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n11399,4,254,2017-05-19 18:20:33,http://hammes.name/fannie,,19.91.220.237,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n11400,4,254,2017-03-15 15:13:05,http://cronin.co/sven_schneider,,217.119.158.241,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n11401,4,254,2017-02-14 08:17:47,http://oconner.info/maximilian_casper,,113.182.207.133,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n11402,4,254,2017-02-11 13:57:03,http://graham.io/keven.stiedemann,,124.58.242.34,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n11403,4,254,2017-02-18 02:53:44,http://crooks.info/wilson,,80.87.82.47,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n11404,4,254,2017-02-19 06:50:28,http://schusterjaskolski.info/amelie_carroll,,59.56.146.178,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n11405,4,254,2017-03-15 23:31:34,http://toybeier.io/lee.white,,101.152.202.37,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n11406,4,254,2017-04-27 00:15:13,http://gleasonlangworth.com/kylie.mosciski,,181.229.113.200,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n11407,4,255,2016-12-14 06:22:25,http://keler.org/simeon,,100.221.70.117,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n11408,4,255,2016-12-14 12:53:59,http://nolan.name/celestine,,160.193.96.122,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n11409,4,255,2017-03-10 18:43:21,http://murphycummings.name/ozella.keler,,127.30.143.45,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n11410,4,255,2017-04-08 13:05:44,http://douglas.name/berry,,34.180.4.211,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n11411,4,255,2017-04-02 19:35:51,http://sauer.biz/christine_miller,,185.122.164.66,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n11412,4,255,2017-06-04 18:02:17,http://ward.name/abelardo,,66.192.114.64,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n11413,4,255,2017-03-28 01:42:19,http://stamm.info/oma_johns,,57.210.93.207,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n11414,4,255,2017-01-16 11:33:51,http://runolfonmiller.net/gina,,19.82.204.159,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n11415,4,255,2017-03-19 06:39:41,http://lowe.name/bartholome.kaulke,,142.158.38.53,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n11416,4,255,2017-05-26 17:20:59,http://douglaswunsch.io/krystal_deckow,,59.19.136.241,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n11417,4,255,2017-01-28 03:20:51,http://fritsch.io/diamond,,163.71.194.224,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n11418,4,255,2017-01-25 08:51:38,http://goodwinheller.info/jazmyn,,252.139.8.205,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n11419,4,255,2017-01-04 23:13:55,http://langworth.biz/neal,,95.67.119.51,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n11420,4,255,2017-04-14 04:16:41,http://mayert.io/keegan_considine,,108.15.92.133,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n11421,4,255,2017-01-13 09:54:02,http://wiza.info/domenico_blick,,172.245.228.177,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n11422,4,255,2017-01-30 21:46:01,http://considineleannon.com/dwight,,163.8.55.19,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n11423,4,255,2017-02-04 23:48:31,http://windler.net/schuyler_turner,,111.53.53.215,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n11424,4,255,2017-06-12 10:06:07,http://daugherty.io/annabell,,10.248.225.143,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n11425,4,255,2017-02-13 08:24:53,http://lueilwitz.io/sabrina.kuvalis,,176.244.110.168,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n11426,4,255,2017-05-31 15:51:07,http://jacobson.info/arno,,194.152.220.131,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n11427,4,255,2017-05-20 08:32:48,http://morar.net/llewellyn.heaney,,81.109.128.115,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n11428,4,255,2017-02-10 04:20:06,http://gislasonvon.biz/bailey,,94.214.157.243,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n11429,4,255,2017-03-06 04:12:35,http://kris.org/judy_parisian,,94.176.92.73,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n11430,4,255,2017-03-27 14:08:35,http://denesikgusikowski.org/dustin_torp,,21.217.148.64,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n11431,4,255,2017-01-26 11:24:35,http://barton.net/luther,,146.84.120.231,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n11432,4,255,2017-01-08 23:49:02,http://grimespadberg.name/jaydon.hagenes,,225.118.148.96,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n11433,4,255,2017-01-25 14:15:56,http://altenwerthsporer.com/aunta,,70.156.69.243,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n11434,4,255,2017-02-28 04:15:00,http://schmidt.co/kaley_conn,,158.79.82.80,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n11435,4,255,2017-06-03 18:08:17,http://schinnervolkman.info/wyman,,61.176.226.222,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n11436,4,255,2017-06-07 19:02:12,http://colepadberg.biz/wendy.shanahan,,219.149.213.187,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n11437,4,255,2016-12-25 22:38:32,http://deckow.io/nya.padberg,,92.140.247.192,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n8384,3,187,2017-05-13 13:51:26,http://hirthe.co/isabel,,132.50.174.47,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n8385,3,187,2017-02-24 21:50:21,http://keelingmiller.name/lempi,,182.175.196.37,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n8386,3,187,2017-05-18 10:29:44,http://schultz.io/alva,,107.121.165.74,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n8387,3,187,2017-05-14 17:45:21,http://smith.org/erwin,,187.149.30.226,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n8388,3,187,2016-12-23 04:06:59,http://dickens.name/wilma.emard,,144.70.77.9,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n8389,3,187,2017-04-24 12:55:58,http://cronin.biz/alene,,213.38.158.91,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n8390,3,187,2017-01-27 07:28:51,http://kris.co/kasey,,246.124.29.152,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n8391,3,187,2016-12-24 05:46:09,http://osinski.org/anais_rodriguez,,179.242.39.249,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n8392,3,187,2017-02-02 02:40:23,http://beahan.io/ellen.powlowski,,25.68.35.240,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n8393,3,187,2017-02-18 15:17:52,http://rodriguez.name/orval,,238.46.37.111,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n8394,3,187,2017-05-24 18:54:05,http://beckeryost.co/charlie.marvin,,105.203.241.147,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n8395,3,187,2017-05-15 05:12:14,http://bode.org/seth,,87.48.234.69,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n8396,3,187,2017-02-26 11:25:42,http://balistreri.info/geovanny,,198.92.230.78,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n8397,3,187,2016-12-18 14:29:33,http://collins.io/daphnee_halvorson,,107.86.254.79,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n8398,3,187,2017-02-20 04:35:24,http://gleason.info/dashawn_collier,,4.131.201.25,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n8399,3,187,2017-03-05 16:03:35,http://howelllangosh.info/collin.fritsch,,127.199.212.123,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n8400,3,187,2017-02-14 08:22:43,http://pagac.biz/andre,,155.16.87.48,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n8401,3,187,2017-05-12 10:52:58,http://schulist.biz/linda.conroy,,242.5.30.36,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n8402,3,187,2017-03-11 15:18:26,http://schneiderpollich.biz/jada,,206.206.37.166,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n8403,3,187,2017-06-13 22:32:36,http://hickle.com/mya.kihn,,239.154.43.143,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n8404,3,187,2017-05-25 02:51:02,http://heidenreich.biz/jacey_kutch,,99.125.58.144,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n8405,3,188,2017-04-21 22:24:23,http://uptonwilkinson.io/taurean.farrell,,59.189.242.207,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n8406,3,188,2017-01-08 15:59:48,http://schummbartell.co/katheryn,,104.59.222.154,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n8407,3,188,2017-05-27 15:52:47,http://hills.biz/wilfred,,183.41.50.200,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n8408,3,188,2017-01-21 15:32:17,http://lemke.info/caterina.runolfsdottir,,153.173.209.211,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n8409,3,188,2016-12-23 17:10:32,http://nienow.org/kamren,,148.106.75.22,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n8410,3,188,2017-02-09 08:16:44,http://doyle.io/amanda,,65.27.5.82,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n8411,3,188,2017-02-13 11:51:16,http://kozeykihn.name/alexander,,15.138.177.41,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n8412,3,188,2016-12-19 21:04:50,http://wisozkheaney.org/timmothy.dickens,,11.124.139.76,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n8413,3,188,2017-06-12 19:22:18,http://lindgrensauer.net/gus.moen,,189.11.169.160,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n8414,3,188,2017-05-24 19:15:23,http://marksveum.com/christa.okuneva,,252.99.16.92,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n8415,3,188,2017-01-01 05:43:30,http://trantow.com/erich,,212.206.53.27,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n8416,3,188,2017-06-05 22:35:35,http://oreilly.name/keaton_padberg,,36.101.11.224,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n8417,3,188,2017-03-15 17:44:35,http://donnellyondricka.com/magnus,,219.71.136.230,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n8418,3,188,2016-12-14 22:03:59,http://jakubowskiwolff.co/stephany.bode,,237.81.64.34,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n8419,3,188,2017-03-02 16:48:53,http://gibson.co/aisha_murazik,,231.36.157.3,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n8420,3,188,2017-04-19 14:07:45,http://schmeler.biz/alena,,177.65.9.196,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n8421,3,188,2017-01-27 09:00:27,http://okunevajohnston.co/zane_labadie,,158.178.163.81,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n8422,3,188,2017-02-26 22:59:59,http://nicolas.org/irwin,,16.200.119.229,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n8423,3,188,2017-01-29 04:29:27,http://lemkekeeling.io/lamar.kling,,94.199.14.31,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n8424,3,188,2017-02-09 04:28:04,http://rolfson.com/tillman_ledner,,67.62.190.176,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n8425,3,188,2016-12-25 00:10:49,http://mosciski.co/nayeli,,29.84.72.206,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n8426,3,188,2016-12-22 11:03:38,http://kunze.info/daniela,,185.32.247.100,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n8427,3,188,2017-05-16 23:25:22,http://jacobsbauch.name/tatum,,218.97.56.75,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n8428,3,189,2017-05-23 17:08:35,http://stracke.com/jakob_keebler,,162.139.250.77,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n8429,3,189,2017-03-17 07:08:54,http://hodkiewicz.org/johanna.toy,,7.153.154.186,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n8430,3,189,2017-05-25 07:03:30,http://leuschkeking.com/wanda,,174.128.189.115,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n8431,3,189,2017-05-11 16:02:46,http://waelchileannon.org/albina,,211.77.59.251,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n8432,3,189,2017-01-17 01:10:46,http://goodwin.org/gina.cremin,,2.234.70.194,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n8433,3,189,2017-02-09 13:01:39,http://kovacek.co/candido,,106.41.254.214,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n8434,3,189,2017-05-28 18:57:37,http://shanahan.io/clara,,125.116.176.125,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n8435,3,189,2017-03-08 10:56:05,http://lubowitz.biz/caandra,,21.108.116.37,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n8436,3,189,2017-06-05 00:29:43,http://fayreynolds.com/name.stracke,,240.222.179.6,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n8437,3,189,2017-05-09 12:04:36,http://haneskiles.io/jadon,,179.94.12.56,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n8438,3,189,2017-05-06 17:27:26,http://haag.com/ronny.schimmel,,188.191.203.189,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n14333,5,322,2017-03-10 04:54:40,http://millerjenkins.biz/walton,,160.84.211.55,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n14334,5,322,2017-01-20 18:54:56,http://hansen.org/lourdes.thompson,,251.187.43.190,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n14335,5,322,2017-04-12 13:13:13,http://simonis.com/modesto_murazik,,105.139.122.171,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n14336,5,322,2017-03-03 18:07:44,http://considine.org/jamaal.spinka,,98.171.201.188,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n14337,5,322,2016-12-15 10:24:20,http://romaguera.net/ruthie.schamberger,,167.72.26.69,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n14338,5,322,2017-05-25 03:37:07,http://hirthemacgyver.com/ethel,,47.76.107.96,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n14339,5,322,2017-02-21 04:05:07,http://heaneyfeest.com/jordane_klocko,,115.26.114.19,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n14340,5,322,2017-05-14 09:25:43,http://lueilwitz.com/rogers.torphy,,65.193.214.243,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n14341,5,322,2017-01-24 22:47:59,http://west.co/dana,,94.45.232.127,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n14342,5,322,2017-04-16 22:29:43,http://jaskolskidaniel.biz/lina_rolfson,,195.177.147.66,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n14343,5,322,2017-04-22 19:24:45,http://bergnaum.net/fabian.heller,,16.243.94.62,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n14344,5,322,2017-03-22 00:28:03,http://mcclure.net/clovis,,93.120.118.177,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n14345,5,323,2017-02-12 06:36:14,http://maggio.co/leo.connelly,,240.121.15.110,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n14346,5,323,2016-12-29 12:52:20,http://pouros.net/ottilie.weimann,,7.196.153.22,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n14347,5,323,2017-02-01 09:34:13,http://metz.info/jonathon,,201.132.39.18,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n14348,5,323,2017-06-13 04:54:57,http://lueilwitz.io/dariana,,174.24.67.73,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n14349,5,323,2017-01-29 09:01:53,http://nolanbednar.net/trevion_kovacek,,140.98.128.185,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n14350,5,323,2017-05-26 02:52:01,http://kemmer.info/clare,,40.74.43.215,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n14351,5,323,2017-04-29 14:23:08,http://marvin.net/hildegard,,216.174.122.210,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n14352,5,323,2017-06-10 20:50:28,http://crooks.com/julie,,66.123.53.233,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n14353,5,323,2017-03-31 21:35:03,http://stantonlabadie.biz/aleia_orn,,234.25.218.15,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n14354,5,323,2016-12-24 11:09:36,http://lesch.com/kelvin.corwin,,173.177.63.224,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n14355,5,323,2017-02-06 11:25:03,http://grant.biz/robyn.legros,,143.185.13.169,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n14356,5,323,2017-03-26 20:54:24,http://vandervort.com/allie,,107.161.34.165,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n14357,5,323,2017-03-18 09:48:39,http://ondricka.name/augustine,,224.186.81.187,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n14358,5,323,2017-01-15 08:23:19,http://larson.co/odie,,247.63.237.254,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n14359,5,323,2017-06-01 04:27:30,http://schmitt.name/ariane,,67.227.241.212,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n14360,5,323,2017-05-23 00:51:10,http://pfeffer.biz/leonel,,198.159.55.166,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n14361,5,323,2017-03-25 13:14:44,http://howellhammes.com/elroy.jones,,252.113.158.118,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n14362,5,323,2017-03-24 18:49:18,http://hickle.net/ali,,218.23.60.69,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n14363,5,323,2017-06-11 07:52:21,http://bernier.co/georgiana,,14.236.48.227,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n14364,5,323,2017-05-21 18:50:50,http://harveyreichert.biz/ahmed,,153.55.167.96,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n14365,5,323,2016-12-31 10:38:49,http://kilbackwilkinson.co/linnea,,188.172.226.79,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n14366,5,323,2017-04-30 08:55:16,http://mayertklocko.biz/domenick,,23.181.232.153,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n14367,5,323,2017-04-11 04:27:34,http://zemlak.name/jayson_witting,,17.213.14.109,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n14368,5,323,2017-05-01 23:27:31,http://ullrich.io/kaia.smitham,,20.81.161.110,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n14369,5,323,2016-12-21 05:52:52,http://marks.io/trea_feil,,18.13.70.124,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n14370,5,323,2017-03-28 17:47:35,http://lang.net/buford_rosenbaum,,94.92.154.15,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n14371,5,323,2017-05-06 10:24:02,http://weber.net/elvera,,243.81.87.252,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n14372,5,323,2017-01-03 07:41:50,http://lueilwitzernser.name/kacie_nicolas,,112.193.126.99,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n14373,5,323,2017-02-14 10:08:40,http://braun.name/jeramy_gleason,,244.134.177.94,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n14374,5,323,2017-01-13 09:35:54,http://brakus.org/dominique,,170.73.116.111,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n14375,5,323,2017-03-24 02:55:14,http://runte.biz/monte,,152.217.51.80,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n14376,5,323,2017-03-19 14:34:34,http://littlehickle.com/bernhard_hahn,,116.113.54.155,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n14377,5,323,2016-12-27 06:46:19,http://legros.io/angelita,,164.118.72.156,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n14378,5,323,2017-04-14 08:31:38,http://mohr.biz/rashawn,,192.8.66.253,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n14379,5,323,2017-05-09 06:19:43,http://hauck.name/alize,,205.58.248.102,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n14380,5,323,2017-03-25 22:46:34,http://jenkinsbartell.co/april,,168.170.215.3,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n14381,5,323,2017-04-30 01:18:48,http://bechtelar.co/obie,,131.5.214.121,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n14382,5,323,2017-03-09 05:13:42,http://mraz.net/moshe,,144.161.220.11,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n14383,5,323,2017-05-01 05:54:07,http://ryanstroman.io/lura,,96.48.141.26,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n14384,5,323,2017-02-28 00:35:02,http://king.co/malcolm_lowe,,58.51.8.69,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n14385,5,323,2017-04-28 05:41:45,http://tromp.biz/chelsey.davis,,241.244.5.20,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n14386,5,323,2017-01-03 22:32:22,http://weinat.com/billie_mann,,203.202.149.90,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n14387,5,323,2017-04-24 14:22:59,http://reichel.com/dalton.reynolds,,134.104.240.86,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n14388,5,323,2017-03-19 14:05:10,http://ornjenkins.com/isaac,,16.16.122.223,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n17319,6,391,2017-03-15 08:27:09,http://lindgren.name/akeem_buckridge,,128.154.18.229,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n17320,6,391,2017-03-27 01:31:15,http://kaulke.com/karli,,87.83.247.248,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n17321,6,391,2016-12-26 01:02:56,http://reillybayer.info/baby,,180.253.159.207,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n17322,6,391,2017-05-12 12:41:28,http://leannonlindgren.co/abe,,183.86.222.89,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n17323,6,391,2017-04-12 04:59:46,http://pfannerstillspencer.org/minerva,,159.36.129.49,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n17324,6,391,2017-04-09 09:55:59,http://tillmanweinat.info/boris,,24.79.130.9,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n17325,6,391,2017-02-09 08:52:07,http://robel.net/conrad.lynch,,61.135.237.56,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n17326,6,391,2017-05-04 17:46:59,http://schadenpowlowski.biz/elsie_gerhold,,193.228.33.242,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n17327,6,391,2017-03-02 22:08:31,http://kunde.com/macie.carter,,100.162.99.88,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n17328,6,391,2017-04-11 04:56:05,http://lindsmith.com/vivien,,238.6.150.58,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n17329,6,391,2017-02-27 10:26:30,http://bartoletti.net/alayna_abbott,,112.42.153.24,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n17330,6,391,2017-01-03 08:58:09,http://klingleffler.io/monroe.macejkovic,,191.225.219.110,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n17331,6,391,2017-04-07 00:21:32,http://schulist.org/everardo.wolf,,207.163.49.122,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n17332,6,391,2017-02-16 23:50:33,http://rueckerking.name/kamille_prosacco,,20.39.54.72,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n17333,6,391,2017-04-12 05:09:11,http://wyman.biz/bradford,,45.40.7.70,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n17334,6,391,2017-03-04 08:59:47,http://adams.co/kenna.parisian,,16.221.132.104,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n17335,6,391,2017-06-08 11:24:11,http://tromp.com/wendell.west,,171.69.89.211,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n17336,6,391,2017-04-23 18:12:04,http://sengercremin.io/bobby_schaden,,132.86.52.92,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n17337,6,391,2017-04-13 13:55:15,http://heidenreich.com/damian,,136.59.209.219,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n17338,6,391,2017-04-15 08:51:51,http://spinka.info/elwyn,,145.73.222.135,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n17339,6,391,2017-03-16 00:44:10,http://beier.info/nedra,,55.44.228.64,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n17340,6,391,2016-12-24 04:41:09,http://jacobsonfeest.com/emie.kuvalis,,239.177.253.149,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n17341,6,391,2016-12-31 01:14:57,http://vandervorttillman.name/americo,,244.187.124.125,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n17342,6,391,2017-02-11 07:41:42,http://jacobijakubowski.name/jordan.rosenbaum,,82.82.149.242,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n17343,6,391,2017-01-27 13:31:50,http://gutmann.net/georgianna_wuckert,,178.198.104.141,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n17344,6,391,2017-06-03 06:02:32,http://andersonweimann.net/gabrielle,,252.240.171.198,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n17345,6,391,2017-03-03 08:37:09,http://raynor.net/francesca,,10.225.225.143,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n17346,6,391,2017-01-10 07:31:50,http://bashirian.org/darron_kuhn,,247.89.215.151,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n17347,6,391,2016-12-16 00:38:27,http://wunschbaumbach.com/ethel,,197.187.133.106,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n17348,6,391,2017-04-16 12:53:37,http://rowe.io/arch,,196.108.184.164,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n17349,6,391,2017-06-05 07:06:30,http://cummerata.io/torrey.ruel,,103.250.56.171,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n17350,6,391,2017-04-15 01:04:53,http://pfannerstillbreitenberg.net/sasha.ullrich,,186.130.175.236,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n17351,6,391,2017-05-27 22:29:40,http://kunde.org/sibyl_wintheiser,,169.95.112.104,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n17352,6,391,2017-01-25 02:35:02,http://wiegandheel.io/brandyn_gutkowski,,66.58.125.197,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n17353,6,391,2016-12-19 10:37:09,http://veum.co/kayleigh,,84.122.214.100,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n17354,6,391,2017-05-12 23:36:31,http://spencer.io/nathen,,174.19.68.173,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n17355,6,391,2017-05-19 03:05:23,http://smitham.biz/eldon,,48.71.181.65,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n17356,6,391,2017-01-19 17:21:50,http://spencer.info/bradford,,14.40.203.82,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n17357,6,391,2017-01-29 17:09:26,http://hayes.com/trea,,205.119.68.11,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n17358,6,391,2017-05-27 16:39:09,http://kuvalis.org/giuseppe,,178.180.9.135,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n17359,6,391,2017-01-15 10:01:46,http://parisianebert.io/freeman_daugherty,,54.29.204.41,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n17360,6,392,2017-05-20 09:44:52,http://gerhold.co/rachelle.marquardt,,108.87.94.247,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n17361,6,392,2017-04-25 07:44:25,http://kingdaugherty.org/sebastian,,220.12.47.111,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n17362,6,392,2017-06-03 13:13:22,http://walter.org/taylor.pacocha,,59.106.228.33,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n17363,6,392,2017-03-24 14:05:42,http://wilkinsonzemlak.name/emmie.heller,,130.15.2.193,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n17364,6,392,2017-01-16 05:09:33,http://ziemannolson.co/frieda,,110.90.8.240,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n17365,6,392,2017-02-07 10:15:23,http://jenkins.net/matilda,,41.12.22.61,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n17366,6,392,2017-05-13 17:02:56,http://rueckeraufderhar.org/beulah_botsford,,21.103.44.171,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n17367,6,392,2017-03-10 06:05:01,http://doyleoconnell.name/destiny_lesch,,110.169.9.74,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n17368,6,392,2017-05-26 02:59:50,http://koch.co/gunner.luettgen,,189.153.130.133,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n17369,6,392,2017-04-06 03:24:44,http://blanda.org/dwight,,63.191.176.4,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n17370,6,392,2017-03-10 05:14:12,http://waelchi.com/keanu,,210.231.91.67,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n17371,6,392,2017-01-19 22:09:45,http://daniel.co/carroll.rath,,88.252.175.59,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n17372,6,392,2017-03-25 13:08:07,http://homenickvandervort.io/elvera_batz,,216.236.24.88,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n17373,6,392,2017-05-24 04:36:42,http://lakincruickshank.name/brant_kemmer,,76.29.130.25,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n4502,2,99,2016-12-29 08:56:58,http://macejkovic.net/autumn,,175.88.125.50,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n4503,2,99,2017-03-23 10:21:05,http://wizakoch.biz/eda_leannon,,40.250.193.67,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n4504,2,99,2016-12-29 03:37:25,http://price.io/daren,,38.188.3.201,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n4505,2,100,2017-01-13 13:07:36,http://casperstokes.io/lucius,,17.145.29.58,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n4506,2,100,2017-02-23 19:40:49,http://stark.biz/elbert,,93.70.150.79,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n4507,2,100,2017-03-21 10:08:49,http://herman.org/jamie,,49.35.135.225,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n4508,2,100,2017-05-01 19:53:44,http://okeefe.io/vida_wintheiser,,85.155.209.80,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n4509,2,100,2017-04-08 21:33:37,http://kihn.org/cynthia_metz,,213.171.169.171,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n4510,2,100,2016-12-23 22:13:00,http://waelchi.io/christ.kshlerin,,83.202.15.17,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n4511,2,100,2016-12-14 17:36:32,http://konopelskilind.biz/glennie.prosacco,,91.153.155.234,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n4512,2,100,2017-05-10 08:16:59,http://colliersmith.biz/ayla_smitham,,44.204.55.197,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n4513,2,100,2016-12-23 22:54:48,http://farrell.com/madie,,169.131.53.199,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n4514,2,100,2017-02-15 13:36:07,http://goodwin.co/ila,,248.64.10.129,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n4515,2,100,2017-01-31 14:05:18,http://heel.info/jany_brakus,,81.125.184.120,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n4516,2,100,2017-02-11 04:14:18,http://fadel.org/kamron_herzog,,241.187.45.18,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n4517,2,100,2017-02-22 02:14:06,http://keler.com/dell.berge,,119.80.218.10,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n4518,2,100,2017-02-25 17:58:13,http://bogan.info/arvilla,,123.91.211.146,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n4519,2,100,2017-06-13 12:09:28,http://connellyullrich.net/madyson,,214.85.174.194,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n4520,2,100,2017-03-27 13:33:51,http://zemlakkohler.biz/terrell,,70.236.185.228,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n4521,2,100,2016-12-25 00:43:32,http://welchhaag.io/reyes.thompson,,125.64.185.157,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n4522,2,100,2016-12-27 07:50:20,http://von.org/tyree,,236.222.115.118,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n4523,2,100,2016-12-29 23:45:17,http://oconner.co/ewald.crist,,158.58.211.52,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n4524,2,100,2017-04-27 18:11:18,http://dooleypadberg.net/bobbie.strosin,,24.129.232.93,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n4525,2,100,2017-05-17 04:15:46,http://kovacekkeeling.biz/kathlyn_sawayn,,183.30.223.50,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n4526,2,100,2016-12-19 17:48:27,http://ondrickabreitenberg.net/preston,,133.15.22.28,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n4527,2,100,2017-05-10 02:46:48,http://gislason.biz/bonnie.oconnell,,105.183.135.201,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n4528,2,100,2017-05-18 14:18:01,http://beatty.com/charles,,172.248.142.213,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n4529,2,100,2017-05-09 16:39:54,http://kozeyking.name/rollin,,139.169.209.93,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n4530,2,100,2017-04-22 12:59:50,http://frami.org/trudie,,220.213.65.64,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n4531,2,100,2016-12-15 02:19:31,http://barrows.org/gabriella.cronin,,72.139.147.193,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n4532,2,100,2017-03-19 05:01:03,http://schmitt.io/aiyana_stiedemann,,170.215.30.87,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n4533,2,100,2017-01-22 07:46:21,http://littelfunk.info/raoul.abbott,,222.187.21.159,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n4534,2,100,2016-12-14 06:53:42,http://walsh.org/eryn,,139.74.225.45,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n4535,2,100,2016-12-13 20:25:27,http://sporer.biz/suzanne,,74.131.229.207,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n4536,2,100,2017-05-14 09:16:54,http://connellylegros.biz/keely.frami,,84.156.245.33,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n4537,2,100,2017-05-09 12:46:55,http://hoegerroberts.info/mabelle.runte,,130.36.241.222,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n4538,2,101,2016-12-27 11:38:34,http://bins.info/ernestine,,120.156.225.18,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n4539,2,101,2017-01-26 04:02:19,http://klocko.org/giovanni,,18.126.197.22,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n4540,2,101,2017-05-11 04:55:23,http://hand.biz/adah,,220.138.111.102,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n4541,2,101,2017-05-31 09:23:59,http://davitehr.com/noemy.torp,,225.214.138.66,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n4542,2,101,2017-05-13 22:40:28,http://keler.name/elza,,227.84.162.182,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n4543,2,101,2017-01-18 05:27:07,http://langworth.com/branson.harris,,205.150.12.159,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n4544,2,101,2017-04-04 13:20:40,http://hermistonboyle.io/una,,48.92.122.126,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n4545,2,101,2017-02-04 03:08:33,http://waters.co/burnice_feil,,138.112.97.118,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n4546,2,101,2017-02-28 22:31:17,http://trantowemmerich.name/kaela,,160.110.84.144,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n4547,2,101,2017-06-08 10:13:16,http://bernhardsporer.co/kameron_marks,,60.238.71.48,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n4548,2,101,2017-04-21 02:50:25,http://toy.co/samantha_lindgren,,56.238.123.146,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n4549,2,101,2017-02-28 18:04:32,http://runolfon.info/loren.kuhic,,208.34.223.231,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n4550,2,101,2017-04-27 17:52:22,http://mitchell.name/berneice,,144.96.224.114,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n4551,2,101,2017-03-02 21:33:07,http://kutch.org/wallace,,71.222.196.61,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n4552,2,101,2017-03-22 17:11:22,http://nolan.org/domenico,,151.169.89.242,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n4553,2,101,2017-05-26 22:46:22,http://toy.info/colton_erdman,,186.33.184.195,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n4554,2,101,2017-02-19 16:08:12,http://ullrich.biz/regan.bogan,,78.76.126.64,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n4555,2,101,2017-03-18 02:32:59,http://nikolauswiza.net/malinda,,37.163.208.78,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n4556,2,101,2017-02-17 09:31:39,http://nicolas.com/eulalia,,100.243.5.194,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n4557,2,101,2017-03-20 00:12:14,http://bayer.io/ariane_jast,,156.207.8.123,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n11438,4,255,2017-05-12 10:07:05,http://muller.name/kariane.balistreri,,242.224.18.3,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n11439,4,255,2017-04-19 12:09:22,http://schamberger.com/andreanne.becker,,86.14.154.18,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n11440,4,255,2017-05-18 21:53:54,http://reingerboyle.info/okey_mcclure,,226.21.54.110,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n11441,4,255,2017-02-17 21:22:39,http://schambergerwilderman.info/mona_gleichner,,76.54.147.167,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n11442,4,255,2017-01-06 16:57:26,http://oconnell.net/richie,,69.177.94.254,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n11443,4,255,2017-05-03 21:23:27,http://schambergerarmstrong.name/zora,,109.143.186.98,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n11444,4,255,2017-06-03 17:41:15,http://jones.com/ophelia,,42.251.178.114,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n11445,4,255,2017-02-28 23:48:45,http://okuneva.net/uriel,,214.162.22.42,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n11446,4,255,2016-12-18 20:30:44,http://wilkinson.info/elvera,,56.236.109.9,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n11447,4,255,2017-03-14 07:36:02,http://langoshharvey.io/griffin,,202.116.24.188,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n11448,4,255,2016-12-31 10:32:29,http://kemmer.co/ellsworth_cartwright,,221.194.77.45,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n11449,4,255,2017-05-08 15:43:47,http://olson.com/lois,,215.73.117.101,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n11450,4,255,2017-02-03 17:40:49,http://emmerich.io/ivy,,113.43.6.92,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n11451,4,255,2017-03-21 15:33:03,http://marvinjacobson.net/jairo_dietrich,,45.27.131.57,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n11452,4,255,2017-05-30 12:52:10,http://schaden.io/hobart,,11.218.8.222,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n11453,4,255,2017-02-25 04:03:31,http://rutherfordschiller.com/guido.borer,,238.3.138.73,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n11454,4,255,2017-04-11 03:04:49,http://corwinrowe.co/cindy,,52.161.176.241,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n11455,4,255,2017-06-07 08:50:17,http://howell.biz/sage,,10.113.162.18,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n11456,4,255,2017-02-21 18:40:15,http://jerdegislason.net/stefanie_auer,,130.54.181.178,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n11457,4,255,2017-03-23 22:58:51,http://satterfieldweimann.org/carlos,,29.59.132.86,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n11458,4,255,2017-02-25 00:43:28,http://gusikowskikoch.com/melody.bode,,62.81.209.163,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n11459,4,255,2017-01-20 15:17:06,http://wittingkuhic.name/virgie.jast,,20.114.128.114,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n11460,4,255,2017-02-11 07:30:47,http://nikolaus.name/kathleen,,179.148.102.246,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n11461,4,255,2017-01-26 17:30:49,http://homenick.co/virgie_romaguera,,9.221.92.154,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n11462,4,255,2017-05-19 09:31:08,http://wiza.name/hilbert,,89.99.174.100,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n11463,4,255,2017-01-09 11:18:44,http://bins.name/devon,,67.71.204.127,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n11464,4,255,2017-01-22 03:24:27,http://wiegandmosciski.net/leonardo_barton,,175.169.192.14,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n11465,4,255,2017-04-09 13:08:15,http://hilpert.info/hershel,,209.191.154.209,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n11466,4,255,2017-01-06 05:51:47,http://franecki.org/danial,,220.242.210.192,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n11467,4,255,2017-01-30 04:32:42,http://halvorson.org/eleonore,,42.244.168.89,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n11468,4,255,2017-01-27 09:30:08,http://deckowgottlieb.io/stevie.hintz,,247.185.34.132,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n11469,4,255,2016-12-15 18:47:01,http://blickbartell.net/armani_moriette,,177.224.133.55,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n11470,4,255,2017-02-04 14:43:26,http://rutherford.io/alfreda,,236.51.196.152,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n11471,4,255,2017-03-06 10:17:58,http://hicklebernhard.org/corrine.west,,148.244.97.118,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n11472,4,255,2017-04-12 12:15:21,http://boyle.biz/reta,,111.202.254.66,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n11473,4,255,2017-01-10 11:10:53,http://koeppmertz.info/danial_sporer,,152.202.246.128,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n11474,4,255,2017-03-02 14:41:56,http://shields.net/jana,,249.229.16.199,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n11475,4,255,2017-02-20 00:29:03,http://huel.co/nia,,123.228.115.83,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n11476,4,256,2017-01-21 12:23:50,http://lefflerrippin.io/jackson,,172.168.189.222,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n11477,4,256,2017-05-21 21:46:45,http://beer.name/cortney,,78.217.2.117,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n11478,4,256,2017-02-10 08:32:02,http://jerde.name/ruel,,85.25.164.77,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n11479,4,256,2016-12-26 04:35:11,http://nikolaupencer.name/garett,,150.160.172.117,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n11480,4,256,2017-05-15 19:58:05,http://schinner.info/randall,,109.8.149.246,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n11481,4,256,2017-03-20 04:57:46,http://welch.biz/evangeline.bashirian,,54.64.128.8,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n11482,4,256,2017-02-09 16:36:21,http://haneturcotte.biz/cortney,,143.229.95.192,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n11483,4,256,2017-01-03 22:58:14,http://wardaufderhar.info/aylin.rosenbaum,,151.130.161.150,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n11484,4,256,2017-02-09 11:03:09,http://croninkoepp.biz/emily.hahn,,218.120.173.33,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n11485,4,256,2017-04-17 23:01:28,http://bergstrom.biz/damian.bode,,24.21.213.38,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n11486,4,256,2017-03-15 06:28:13,http://ankundingleffler.co/theresa,,99.150.241.122,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n11487,4,256,2017-01-01 17:40:34,http://gleason.net/ryann_lind,,92.67.196.235,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n11488,4,256,2017-03-17 03:36:05,http://baumbach.info/robbie.halvorson,,177.200.201.39,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n11489,4,256,2017-04-26 12:56:24,http://torphy.io/kacie.runolfsdottir,,56.8.81.97,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n11490,4,256,2017-01-24 03:47:01,http://abshire.org/ephraim,,161.42.166.35,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n11491,4,256,2017-05-25 06:07:14,http://walsh.org/macie,,238.230.52.80,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n11492,4,256,2017-05-27 07:17:52,http://jacobson.info/bobby_rolfson,,69.188.176.69,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n8439,3,189,2017-03-10 03:39:41,http://joneswatsica.info/tara.lesch,,19.41.229.37,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n8440,3,189,2017-05-06 18:20:53,http://ortizroob.info/jamey,,52.119.173.18,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n8441,3,189,2017-03-29 03:08:51,http://bradtke.com/chelsey.hilll,,77.132.90.217,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n8442,3,189,2017-03-10 16:01:37,http://stantondubuque.net/kian.macgyver,,40.47.172.180,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n8443,3,189,2017-01-05 12:03:54,http://koelpin.net/brisa,,167.79.244.178,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n8444,3,189,2017-01-26 12:26:30,http://runolfon.net/vena_bernhard,,250.77.42.66,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n8445,3,189,2017-01-02 02:44:15,http://botsford.info/eula.abshire,,249.86.66.79,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n8446,3,189,2017-05-16 11:39:23,http://fishernikolaus.co/tom.zulauf,,14.178.18.52,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n8447,3,189,2017-03-19 20:54:48,http://lesch.info/kayla,,70.216.227.184,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n8448,3,189,2017-02-04 22:17:27,http://luettgen.com/roselyn,,247.149.139.180,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n8449,3,189,2017-03-21 22:05:58,http://reinger.org/carmella_hamill,,106.193.27.120,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n8450,3,189,2017-04-08 19:05:21,http://rolfsondare.info/brandon,,3.13.100.34,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n8451,3,189,2017-04-12 00:12:51,http://ward.net/humberto,,28.108.35.220,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n8452,3,189,2017-03-02 15:22:26,http://oconnell.info/hortense.herzog,,92.218.227.49,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n8453,3,189,2016-12-16 10:41:49,http://bogan.biz/isobel_mueller,,120.207.227.86,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n8454,3,189,2017-05-02 21:23:43,http://cruickshank.org/frieda,,216.26.109.63,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n8455,3,189,2016-12-31 22:06:17,http://lakin.net/london.prohaska,,185.221.8.106,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n8456,3,189,2017-01-18 20:00:35,http://weberkling.io/anna,,59.248.48.189,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n8457,3,190,2017-01-21 08:06:07,http://wisokyspencer.com/graham.lindgren,,136.107.159.116,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n8458,3,190,2017-04-06 15:16:46,http://wisoky.info/kirstin,,6.175.81.5,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n8459,3,190,2017-01-27 16:46:12,http://breitenberg.io/jeffery.doyle,,129.222.235.173,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n8460,3,190,2017-05-25 22:54:08,http://jonesroob.io/darian,,147.54.169.218,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n8461,3,190,2017-04-26 20:36:55,http://larson.io/noe,,232.248.163.153,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n8462,3,190,2017-02-20 16:06:01,http://hyatthaag.co/julia,,93.238.6.177,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n8463,3,190,2017-01-22 04:36:40,http://padberg.name/garfield.ebert,,64.189.155.78,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n8464,3,190,2017-01-09 01:24:56,http://runolfsdottir.org/tiara_kuphal,,42.56.10.5,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n8465,3,190,2017-02-23 20:47:33,http://rempelfunk.biz/elsie_lind,,106.100.128.234,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n8466,3,190,2017-04-20 20:57:57,http://oreillyboyer.com/elvie,,235.49.218.139,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n8467,3,190,2017-02-11 00:36:07,http://pfannerstillbeier.io/elmore,,141.241.25.245,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n8468,3,190,2017-04-14 15:43:26,http://okeefe.net/shanel,,245.12.14.105,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n8469,3,190,2016-12-21 04:14:59,http://nikolausgislason.net/fae,,44.84.171.73,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n8470,3,190,2017-06-06 02:36:50,http://krajcik.org/jameson,,140.234.146.218,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n8471,3,190,2017-01-22 12:26:00,http://nicolas.io/antwon.kunze,,146.2.244.137,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n8472,3,190,2017-02-06 06:38:50,http://wolf.co/melya_macejkovic,,37.101.21.89,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n8473,3,190,2017-03-13 14:13:00,http://wiegand.biz/keyon,,18.153.92.138,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n8474,3,190,2016-12-13 15:22:29,http://marks.co/liza.johnston,,205.101.209.203,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n8475,3,190,2017-03-25 19:19:46,http://harberankunding.io/jeramy_corwin,,135.236.210.23,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n8476,3,190,2017-04-14 11:14:05,http://cronamann.co/laria,,226.89.140.188,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n8477,3,191,2017-02-14 17:48:46,http://homenick.co/eladio.schuster,,94.224.160.91,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n8478,3,191,2017-03-25 23:50:22,http://heller.com/giovanna_little,,169.139.198.68,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n8479,3,191,2017-04-14 08:20:04,http://lehner.info/hailey_kunze,,147.66.132.147,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n8480,3,191,2017-02-27 14:32:05,http://thieldonnelly.org/josie,,74.136.111.178,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n8481,3,191,2017-01-21 08:54:19,http://ziemann.co/grayson,,125.119.10.181,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n8482,3,191,2017-05-11 13:11:45,http://wuckert.biz/nelda.schmitt,,202.95.47.193,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n8483,3,191,2017-03-30 07:54:23,http://franeckiherzog.biz/bret,,64.245.18.206,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n8484,3,191,2017-03-25 14:53:35,http://larson.net/tania,,234.27.162.95,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n8485,3,191,2016-12-17 01:38:08,http://nitzsche.io/gabriel,,163.136.196.230,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n8486,3,191,2017-03-16 07:06:31,http://uptonankunding.info/peyton,,164.26.233.135,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n8487,3,191,2017-04-10 01:37:59,http://handweinat.info/gavin.blanda,,139.238.118.67,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n8488,3,191,2017-01-09 07:38:45,http://sporerschoen.name/jesus,,159.22.137.152,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n8489,3,191,2017-05-22 19:59:23,http://johnson.info/kaleigh_schmidt,,149.171.105.77,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n8490,3,191,2017-01-19 18:36:24,http://mclaughlinhayes.net/lisette.bednar,,171.20.129.133,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n8491,3,191,2017-05-29 18:35:55,http://mclaughlin.biz/christ_beatty,,3.40.108.226,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n8492,3,191,2017-01-15 22:43:13,http://cummerata.name/rudy.conn,,86.76.164.81,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n8493,3,191,2016-12-22 12:21:19,http://mohr.net/clark_schimmel,,114.17.149.204,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n8494,3,191,2017-05-12 12:49:32,http://labadie.io/garth.kuhlman,,211.5.116.88,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n14389,5,323,2017-04-29 11:43:03,http://ziemannleuschke.net/lavern_kuphal,,76.118.154.95,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n14390,5,323,2017-04-09 18:27:15,http://schroeder.net/deontae,,90.239.80.224,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n14391,5,323,2017-02-21 18:00:55,http://bayerbruen.org/albert_williamson,,17.115.163.90,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n14392,5,323,2017-05-16 19:20:41,http://maggio.io/julianne_volkman,,82.17.217.232,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n14393,5,324,2017-02-09 12:46:49,http://olson.org/godfrey,,7.98.170.254,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n14394,5,324,2017-03-25 07:01:48,http://kshlerin.info/hailey,,52.216.218.251,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n14395,5,324,2017-02-05 14:20:06,http://prohaskamueller.biz/emmie,,251.93.157.251,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n14396,5,324,2017-03-08 04:42:43,http://koepprobel.com/dante_pagac,,54.185.188.131,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n14397,5,324,2017-02-24 04:24:17,http://roberts.org/keyon.borer,,133.248.99.254,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n14398,5,324,2017-02-08 23:20:11,http://hane.io/samantha,,46.177.14.15,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n14399,5,324,2017-03-30 23:27:14,http://mcglynn.info/stephany_grady,,153.205.84.46,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n14400,5,324,2017-03-25 17:56:15,http://wittinghamill.info/ezekiel.schiller,,94.23.114.132,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n14401,5,324,2017-04-24 18:57:27,http://fisherjacobi.name/ena,,104.36.108.185,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n14402,5,324,2016-12-20 04:30:39,http://denesik.org/bud_cain,,154.226.221.37,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n14403,5,324,2017-03-23 11:55:55,http://gleason.name/paula.huel,,231.99.41.237,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n14404,5,324,2017-02-04 17:01:43,http://gottlieb.co/riley,,187.118.205.144,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n14405,5,324,2017-04-25 03:32:37,http://nader.com/jonatan.kulas,,129.16.252.65,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n14406,5,324,2017-05-22 18:24:11,http://keeblerdicki.org/ernest,,163.209.95.151,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n14407,5,324,2017-03-18 20:31:07,http://oconnell.biz/torey_schuppe,,66.121.142.111,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n14408,5,324,2016-12-19 06:25:08,http://okeefecummerata.co/hertha,,147.44.37.159,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n14409,5,324,2017-05-11 06:57:49,http://trantow.net/jermaine_feil,,253.50.220.167,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n14410,5,324,2016-12-23 11:38:11,http://greenholtkuphal.io/sharon,,93.29.176.204,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n14411,5,324,2017-04-23 22:38:40,http://nader.net/marion_zieme,,96.193.194.173,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n14412,5,324,2017-03-13 06:02:00,http://bahringer.io/mitchell.weimann,,224.194.238.139,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n14413,5,324,2017-06-09 04:30:23,http://hahnpagac.org/clay,,99.204.23.203,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n14414,5,324,2017-05-31 04:52:52,http://farrell.co/koby_kreiger,,129.177.253.138,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n14415,5,324,2017-03-18 00:03:15,http://flatleycummerata.biz/zetta,,163.247.206.252,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n14416,5,324,2017-03-29 10:20:11,http://schoen.org/adella_renner,,25.220.119.115,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n14417,5,324,2017-04-18 08:13:37,http://braun.biz/leonora.waters,,216.126.118.30,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n14418,5,324,2017-04-22 22:06:14,http://bayer.biz/amina.pfeffer,,208.95.155.235,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n14419,5,324,2017-04-15 20:28:32,http://hermistonmertz.co/martine,,225.156.59.20,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n14420,5,324,2017-06-03 21:32:43,http://schmitt.org/jackie.bashirian,,241.188.216.173,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n14421,5,324,2017-01-15 20:57:24,http://jast.info/jennyfer,,243.6.72.201,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n14422,5,324,2017-02-26 17:59:14,http://collinshowell.io/melia,,84.168.120.83,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n14423,5,324,2017-01-14 18:26:46,http://ruecker.com/earl,,29.139.85.81,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n14424,5,324,2017-05-04 22:53:00,http://lesch.net/noemi,,110.51.42.217,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n14425,5,324,2017-02-24 03:02:12,http://dooleygrimes.net/seth,,242.171.119.196,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n14426,5,324,2017-02-26 21:30:02,http://armstrong.com/eveline_dickens,,51.210.220.113,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n14427,5,324,2017-05-27 08:27:24,http://braun.org/nicole.kerluke,,45.123.17.82,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n14428,5,324,2017-04-21 07:14:29,http://koelpin.co/elya,,73.212.88.20,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n14429,5,324,2017-06-03 06:31:29,http://torphydaniel.net/jamel_wintheiser,,120.160.64.147,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n14430,5,324,2017-03-03 06:11:57,http://sporeradams.info/anais_boyer,,36.189.240.208,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n14431,5,324,2017-03-16 20:33:29,http://keeblerdamore.biz/sarah.frami,,40.197.166.140,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n14432,5,324,2016-12-28 15:47:21,http://brakusmuller.name/jada_schimmel,,210.68.182.183,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n14433,5,324,2017-02-17 09:42:41,http://corkerynikolaus.org/garry,,77.145.35.85,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n14434,5,324,2017-02-11 08:45:26,http://gusikowski.com/moie_mckenzie,,144.46.142.128,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n14435,5,325,2017-04-03 13:06:36,http://beahan.co/arturo,,49.76.141.147,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n14436,5,325,2017-05-20 05:23:27,http://wuckert.net/nicklaus_bergstrom,,62.109.50.11,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n14437,5,325,2017-04-20 06:07:48,http://hegmannnitzsche.com/birdie,,45.181.41.22,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n14438,5,325,2017-03-24 21:27:36,http://conroy.com/michaela_mitchell,,102.127.58.156,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n14439,5,325,2017-02-23 11:45:30,http://walker.io/hazle,,45.147.168.211,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n14440,5,325,2017-03-18 02:04:40,http://wisozk.info/theo,,133.139.196.169,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n14441,5,325,2017-03-26 10:36:30,http://klockokuhn.biz/skylar,,94.82.18.96,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n14442,5,325,2017-04-01 23:18:50,http://lind.info/eldred,,21.145.244.79,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n14443,5,325,2017-02-21 10:03:13,http://klocko.biz/cathrine,,187.169.33.62,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n17374,6,392,2017-06-02 20:36:12,http://hackett.org/madison_mohr,,250.96.203.95,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n17375,6,392,2017-02-27 22:55:02,http://goodwin.name/jody_medhurst,,39.198.233.12,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n17376,6,392,2017-01-21 04:10:15,http://danielhuels.com/pattie_shanahan,,117.179.68.213,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n17377,6,392,2017-01-26 23:53:00,http://gerlach.com/lenny.green,,176.210.44.40,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n17378,6,392,2017-02-05 18:10:20,http://jerdeledner.org/garrett_schamberger,,33.228.50.45,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n17379,6,392,2017-05-17 11:41:59,http://douglastromp.io/darrick,,149.168.213.240,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n17380,6,392,2017-02-12 09:48:57,http://swiftoberbrunner.co/hans,,107.186.119.250,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n17381,6,392,2017-02-22 18:53:14,http://runolfonmckenzie.io/murl.ankunding,,207.206.185.173,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n17382,6,392,2017-05-01 22:14:09,http://dickinsonraynor.org/crystal_moen,,218.243.12.33,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n17383,6,392,2017-05-10 05:05:00,http://goldnersmith.info/trystan,,245.25.18.252,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n17384,6,392,2017-05-10 17:33:08,http://hilpert.io/tianna_veum,,49.204.99.67,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n17385,6,392,2017-04-20 12:44:21,http://reillychamplin.io/wilber_champlin,,120.181.151.136,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n17386,6,392,2017-02-28 00:17:52,http://hudson.net/julien,,72.193.143.140,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n17387,6,392,2017-03-30 10:43:53,http://howegutmann.co/vivianne_morar,,204.214.221.138,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n17388,6,392,2017-06-12 20:31:16,http://oberbrunner.info/pearl,,141.232.198.66,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n17389,6,392,2017-04-05 18:21:02,http://jenkinsziemann.name/al.gorczany,,119.216.216.194,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n17390,6,392,2017-03-10 20:40:05,http://hicklestamm.com/lenny,,195.189.59.201,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n17391,6,392,2017-02-12 13:58:31,http://bauchvon.co/bill_okeefe,,120.30.66.29,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n17392,6,392,2017-01-08 18:46:50,http://huel.info/maud_hackett,,197.196.244.86,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n17393,6,392,2017-01-20 11:36:47,http://adams.org/vicenta,,162.136.67.244,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n17394,6,392,2017-04-03 10:21:59,http://schultz.com/baron_waelchi,,175.221.148.164,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n17395,6,392,2017-06-04 14:02:49,http://gerlachwolf.info/oleta,,182.117.131.49,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n17396,6,392,2017-03-25 23:14:32,http://jacobi.biz/elsie,,15.140.185.122,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n17397,6,392,2017-06-12 19:41:07,http://kovacek.com/telly_dare,,83.246.144.65,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n17398,6,392,2017-06-10 07:46:49,http://purdyosinski.io/brain_flatley,,249.61.180.188,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n17399,6,392,2017-02-28 02:22:51,http://price.info/madison_jast,,33.174.115.77,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n17400,6,392,2017-01-16 14:34:32,http://bernhardkeeling.co/rudolph_towne,,140.57.239.95,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n17401,6,392,2017-03-16 09:08:58,http://hamill.co/caterina.crooks,,112.235.176.79,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n17402,6,392,2017-05-13 16:39:56,http://auerbayer.name/carolyn_mitchell,,30.54.145.141,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n17403,6,392,2017-01-17 19:09:37,http://vonrueden.name/belle_schmeler,,192.91.4.15,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n17404,6,392,2017-06-13 13:12:32,http://bergstrom.org/ana,,82.211.213.81,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n17405,6,392,2017-04-26 11:07:08,http://hane.info/charles_pagac,,63.56.217.240,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n17406,6,392,2016-12-28 03:34:31,http://connelly.com/jude_lind,,204.112.175.107,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n17407,6,392,2017-01-23 21:38:17,http://schmidt.info/frederique,,83.209.73.231,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n17408,6,392,2017-01-11 12:03:50,http://hagenes.name/reese,,158.67.8.146,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n17409,6,392,2017-02-18 02:30:50,http://konopelski.io/sheldon.zemlak,,5.73.104.42,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n17410,6,392,2017-01-02 20:30:25,http://wisozkbraun.info/emma_huel,,248.68.217.104,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n17411,6,392,2017-04-13 22:29:53,http://nolan.co/timothy.schmitt,,83.117.57.154,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n17412,6,392,2017-01-01 08:53:09,http://grant.name/zelda.ward,,5.199.237.135,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n17413,6,392,2017-01-05 07:35:20,http://turcotte.name/rocio.hodkiewicz,,98.96.14.115,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n17414,6,392,2017-04-08 22:50:16,http://lesch.com/carolina_langosh,,137.14.168.110,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n17415,6,392,2017-02-26 05:26:11,http://emmerichrolfson.info/colin,,243.173.90.35,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n17416,6,392,2017-01-02 16:37:12,http://ledner.io/myrtie_schamberger,,68.146.75.61,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n17418,6,392,2017-03-19 15:12:06,http://leuschke.net/dean.dickinson,,90.241.55.222,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n17419,6,392,2017-04-10 04:00:31,http://keelingcole.com/ardith,,167.84.217.81,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n17420,6,393,2017-03-02 15:38:05,http://mclaughlingaylord.com/wilhelm.brakus,,40.239.176.171,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n17421,6,393,2017-03-11 18:22:22,http://auerwelch.biz/josue,,111.21.194.78,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n17422,6,393,2017-04-27 05:30:23,http://gutkowski.biz/emerson.huel,,23.118.90.66,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n17423,6,393,2017-05-14 19:04:06,http://mayert.io/evangeline,,130.121.120.213,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n17424,6,393,2017-02-06 07:24:16,http://marquardtcrooks.org/kari_nicolas,,14.127.254.131,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n17425,6,393,2017-05-18 23:54:30,http://hodkiewicz.biz/izaiah,,54.58.23.193,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n17426,6,393,2017-04-16 00:47:39,http://schultz.co/itzel.gottlieb,,228.210.201.159,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n17427,6,393,2017-01-21 01:51:15,http://witting.info/zander,,149.244.81.119,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n17428,6,393,2017-03-25 06:33:13,http://jast.biz/rosario_stanton,,65.156.109.16,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n4558,2,101,2017-06-06 07:50:12,http://feilprosacco.io/abby,,96.233.23.219,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n4559,2,101,2017-04-03 01:52:59,http://fahey.com/paolo,,114.88.130.82,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n4560,2,101,2017-05-20 10:39:31,http://caingutkowski.co/stephania_shanahan,,25.88.109.30,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n4561,2,101,2017-03-03 11:58:01,http://mclaughlinhilpert.info/olin.nitzsche,,111.223.155.188,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n4562,2,101,2017-05-19 22:51:27,http://beier.co/porter,,37.23.89.112,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n4563,2,101,2017-04-20 14:04:09,http://schowalterhilpert.info/layne,,206.66.105.66,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n4564,2,101,2017-06-10 18:48:29,http://hicklewisoky.co/okey,,152.231.114.234,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n4565,2,101,2017-01-16 20:47:22,http://hodkiewicz.biz/jarret,,231.96.230.41,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n4566,2,101,2017-03-05 03:51:16,http://botsfordmayert.name/bonita,,53.2.162.241,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n4567,2,101,2017-01-01 02:11:07,http://bednarboehm.net/margret,,100.4.49.192,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n4568,2,101,2017-05-03 18:21:59,http://stoltenberg.io/aida.connelly,,235.115.179.92,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n4569,2,101,2017-04-23 01:28:44,http://parker.net/emiliano,,103.34.227.221,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n4570,2,101,2017-04-22 09:55:17,http://romaguera.name/tremaine_hyatt,,225.216.55.100,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n4571,2,101,2017-03-15 12:12:45,http://wolff.co/jimmy.prohaska,,219.20.93.50,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n4572,2,101,2017-01-17 16:13:53,http://okeefe.net/laverne,,63.158.79.67,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n4573,2,101,2017-04-24 23:36:09,http://bartell.org/cary_doyle,,138.159.38.233,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n4574,2,102,2017-04-22 04:37:08,http://mertzsenger.co/levi_schmidt,,180.83.232.161,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n4575,2,102,2017-02-23 21:19:59,http://danielmccullough.info/roy_hackett,,146.136.88.65,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n4576,2,102,2017-05-22 02:49:50,http://denesikjakubowski.biz/lexie,,177.200.236.59,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n4577,2,102,2017-05-24 01:59:09,http://beahanoberbrunner.com/taylor.walsh,,3.90.231.189,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n4578,2,102,2017-03-26 16:28:25,http://kling.org/vilma,,185.50.123.190,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n4579,2,102,2017-04-24 00:48:12,http://baumbach.org/estrella.schamberger,,159.150.214.25,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n4580,2,102,2016-12-18 13:27:02,http://mclaughlin.io/caesar.considine,,89.160.113.106,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n4581,2,102,2017-06-10 15:44:31,http://tillman.io/nyah_bauch,,210.212.239.40,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n4582,2,102,2017-06-07 06:41:39,http://waters.io/hilton,,60.96.179.176,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n4583,2,102,2016-12-29 20:42:13,http://friesen.co/otilia,,2.38.88.107,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n4584,2,102,2017-02-02 00:06:34,http://hackett.biz/marcelino,,26.215.73.2,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n4585,2,102,2017-04-20 15:19:03,http://heaneyromaguera.info/trystan.brakus,,248.62.120.57,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n4586,2,102,2017-03-31 14:42:16,http://denesik.name/demario,,82.220.158.239,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n4587,2,102,2017-04-18 07:45:20,http://rathbauch.io/lynn,,218.227.141.176,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n4588,2,102,2017-05-18 04:19:28,http://morar.net/milan.windler,,198.165.116.161,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n4589,2,102,2017-03-30 16:35:01,http://weimannturner.co/floy_senger,,24.55.203.124,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n4590,2,102,2017-02-25 08:37:16,http://robelrolfson.name/maeve.watsica,,3.245.128.99,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n4591,2,102,2017-01-17 19:19:33,http://yost.co/gia,,104.201.28.103,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n4592,2,102,2017-06-10 01:00:34,http://turnerkihn.io/bria_harris,,54.85.133.254,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n4593,2,102,2017-03-13 03:14:17,http://stokes.org/adella_erdman,,164.62.95.70,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n4594,2,102,2017-04-02 04:55:32,http://abernathy.com/jey_schoen,,145.22.227.68,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n4595,2,102,2017-05-15 05:57:06,http://lubowitzgulgowski.name/consuelo,,81.214.204.175,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n4596,2,102,2017-05-05 23:23:55,http://wisoky.org/dell.lueilwitz,,252.102.239.245,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n4597,2,102,2017-04-25 09:28:09,http://mcglynn.com/bethany,,198.101.151.20,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n4598,2,102,2017-05-18 13:00:19,http://langhickle.com/llewellyn.keler,,187.232.220.88,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n4599,2,102,2017-04-09 02:41:21,http://dubuque.net/evans.zboncak,,50.81.125.194,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n4600,2,102,2017-03-19 20:57:01,http://gerlach.name/neha.schuppe,,39.99.229.248,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n4601,2,102,2017-04-14 07:45:32,http://kautzer.biz/annalise.dicki,,28.230.107.18,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n4602,2,102,2017-04-03 08:32:10,http://wintheiser.info/fabian.conroy,,4.202.252.17,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n4603,2,102,2017-01-16 07:58:07,http://jacobi.org/chad.barrows,,154.218.85.147,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n4604,2,102,2017-05-11 09:40:51,http://boyle.co/judah,,64.118.19.171,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n4605,2,102,2017-04-09 21:01:53,http://moen.name/devon,,135.40.207.120,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n4606,2,102,2017-01-20 16:22:25,http://schoen.name/loyal,,100.227.45.71,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n4607,2,102,2017-02-28 01:22:39,http://mccullough.name/alayna,,134.103.51.208,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n4608,2,102,2017-01-23 02:43:43,http://greenfelderkoelpin.io/noah.wintheiser,,246.20.157.127,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n4609,2,102,2017-03-25 13:52:36,http://keler.com/lisandro.jacobi,,195.220.105.135,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n4610,2,102,2017-05-04 03:17:22,http://emmerich.io/verner,,226.96.22.13,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n4611,2,102,2017-01-08 12:08:12,http://stroman.io/amos,,234.38.32.5,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n4612,2,102,2017-01-04 18:02:44,http://keebler.info/fabiola,,133.232.40.55,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n4613,2,102,2017-02-27 04:25:05,http://lefflerschowalter.info/aiyana,,43.177.73.69,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n11493,4,256,2017-05-02 08:54:43,http://krajcikkoepp.co/cheyanne,,227.96.170.229,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n11494,4,256,2017-04-30 11:41:06,http://adamsjast.io/elisa,,243.224.96.85,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n11495,4,256,2017-05-28 20:57:40,http://kuvalis.co/mayra,,88.16.121.41,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n11496,4,256,2016-12-17 12:55:56,http://wizalesch.co/ethel_white,,125.13.42.136,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n11497,4,256,2017-06-03 04:12:38,http://kirlin.io/florida_daugherty,,106.122.143.63,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n11498,4,256,2017-04-15 22:02:28,http://ohara.com/parker.blanda,,213.65.205.132,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n11499,4,256,2016-12-27 10:31:32,http://anderson.co/laisha.koepp,,63.191.71.245,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n11501,4,256,2017-01-17 15:49:25,http://nadermohr.name/monique.durgan,,156.91.18.85,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n11502,4,256,2017-02-20 23:46:00,http://hane.co/jaclyn,,117.169.151.70,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n11503,4,257,2017-05-30 19:09:17,http://hansenhansen.info/cecilia.moore,0.4633392625,215.174.31.103,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n11504,4,257,2017-03-21 13:10:08,http://lowepfeffer.biz/elda_jakubowski,0.7960573644,51.136.240.25,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n11505,4,257,2017-05-07 07:44:34,http://lowe.io/imani,0.2517515881,143.56.213.54,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n11506,4,257,2017-01-31 07:45:19,http://hintz.com/devin_medhurst,0.5024504847,190.79.61.27,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n11507,4,257,2017-02-07 03:14:56,http://runolfsdottir.biz/ken,0.3150932455,88.234.16.24,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n11508,4,257,2016-12-22 01:30:09,http://grady.net/leonor.rempel,0.3383840981,145.25.30.181,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n11509,4,257,2017-03-11 22:02:38,http://moriettejacobi.io/ralph.reinger,0.9142528013,192.175.102.173,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n11510,4,257,2017-03-04 02:43:39,http://rowe.org/carole_schmidt,0.9903054692,189.139.189.3,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n11511,4,257,2017-02-20 22:48:06,http://blockreichel.org/bonita.barrows,0.9496117959,118.237.246.8,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n11512,4,257,2017-06-02 06:22:15,http://fishersipes.co/caterina_stracke,0.3474244839,6.177.88.212,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n11513,4,257,2016-12-20 07:35:29,http://sporereffertz.io/dagmar,0.3539464803,238.125.215.152,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n11514,4,257,2017-02-24 06:57:40,http://wuckert.org/arnulfo,0.0016712258,233.145.81.211,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n11515,4,257,2016-12-25 17:52:46,http://goyettekozey.name/chris,0.6702420562,252.198.110.23,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n11516,4,257,2017-02-08 18:34:40,http://kling.co/declan.prohaska,0.4047119067,230.244.69.54,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n11517,4,257,2017-01-27 02:49:41,http://jacobs.biz/kirk,0.8176344629,202.67.48.75,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n11518,4,257,2017-06-12 21:25:18,http://parkerziemann.name/raoul.greenfelder,0.0627852717,129.39.10.90,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n11519,4,257,2017-05-20 21:29:04,http://schoen.info/antonina_mitchell,0.1868133793,184.231.93.192,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n11520,4,257,2017-04-18 15:25:59,http://bradtke.co/olin.hackett,0.1037683511,95.37.187.88,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n11521,4,257,2017-03-16 14:16:45,http://rice.co/raphaelle_hilll,0.4357422821,63.207.167.224,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n11522,4,257,2017-05-01 07:28:59,http://stanton.io/prince.beahan,0.1732560633,127.110.205.187,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n11523,4,257,2016-12-23 07:17:30,http://hyatt.co/myrtice,0.7044671494,181.84.221.122,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n11524,4,257,2017-02-21 21:43:45,http://marvingerhold.biz/tavares,0.8910622839,218.50.145.122,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n11525,4,257,2017-03-15 00:14:19,http://heller.co/caitlyn_hahn,0.3675519977,128.124.201.244,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n11526,4,257,2017-05-24 06:38:23,http://monahanjakubowski.net/olen.huels,0.4163701753,122.207.210.14,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n11527,4,257,2017-02-15 10:12:41,http://hudson.net/jacynthe.anderson,0.9326168886,74.144.32.213,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n11528,4,257,2016-12-16 02:41:13,http://bergebeahan.biz/shawna_bosco,0.3993878245,212.88.211.29,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n11529,4,257,2017-05-29 02:11:40,http://robelluettgen.name/rosetta,0.8556819505,29.123.82.219,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n11530,4,257,2017-01-17 05:14:59,http://willmsmclaughlin.net/jordane.stiedemann,0.1134318770,98.249.219.220,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n11531,4,257,2016-12-21 23:27:28,http://sauer.name/gail_robel,0.9493475871,25.93.106.38,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n11532,4,257,2017-05-27 21:54:18,http://shieldsklocko.com/luis,0.8152181967,171.74.244.201,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n11533,4,257,2017-05-10 18:18:35,http://cruickshankruel.io/mercedes_padberg,0.9522419536,148.5.9.146,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n11534,4,257,2017-03-17 12:41:56,http://haagmckenzie.net/cathy.ondricka,0.2857463829,238.62.112.133,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n11535,4,257,2017-01-10 14:13:56,http://heidenreichrodriguez.org/dayne.ferry,0.6086572738,140.62.229.111,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n11536,4,257,2017-04-24 12:20:31,http://kutchlueilwitz.com/soledad,0.7859845903,153.108.20.211,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n11537,4,257,2017-02-06 10:36:05,http://langoshwalter.com/whitney,0.4502194254,42.114.22.140,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n11538,4,257,2017-03-23 07:52:20,http://stiedemann.org/cierra_wisozk,0.0496087608,154.184.51.178,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n11539,4,257,2016-12-23 09:08:32,http://kuphal.com/rodolfo,0.0639655674,224.119.248.45,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n11540,4,257,2017-04-26 19:22:19,http://stammhermann.biz/emmitt.koch,0.2827400614,46.182.177.197,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n11541,4,257,2017-02-14 23:10:24,http://nader.name/colin,0.3350360506,166.216.54.242,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n11542,4,257,2017-06-12 21:05:09,http://padberg.io/dejuan,0.6840970109,187.221.17.85,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n11543,4,257,2017-04-20 11:43:29,http://roob.org/yazmin.bode,0.3815136612,157.254.31.165,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n11544,4,257,2017-01-05 18:43:40,http://oconnerhoeger.biz/treie,0.6944559609,116.223.17.54,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n8495,3,191,2017-02-22 19:12:54,http://dach.biz/mauricio,,191.116.146.123,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n8496,3,191,2016-12-24 10:49:15,http://reynolds.io/emmett,,173.110.197.238,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n8497,3,191,2017-02-13 11:16:30,http://sipesmayer.io/roosevelt.heidenreich,,217.80.168.68,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n8498,3,191,2017-01-23 13:13:22,http://muellerwisozk.biz/alek.shields,,181.129.94.127,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n8499,3,191,2017-05-03 22:16:21,http://zieme.io/davon,,42.39.94.105,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n8500,3,191,2017-04-12 22:25:32,http://volkman.co/fiona,,154.242.211.48,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n8501,3,191,2017-03-05 07:38:32,http://dicki.com/idella,,41.78.151.232,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n8502,3,191,2016-12-29 18:12:08,http://moore.info/harvey.wisozk,,194.160.51.129,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n8503,3,191,2017-03-22 16:42:20,http://grimes.com/orpha,,124.116.183.121,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n8504,3,191,2017-05-09 05:11:54,http://toy.com/terence,,42.51.148.75,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n8505,3,191,2017-02-13 15:37:10,http://herzog.biz/lottie_buckridge,,133.77.243.59,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n8506,3,191,2017-03-30 12:03:35,http://schmittwalter.io/annetta,,228.9.79.167,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n8507,3,191,2017-01-25 22:25:09,http://hartmann.biz/sandra,,56.223.193.118,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n8508,3,191,2016-12-25 18:42:35,http://rodriguez.org/christopher,,19.168.167.200,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n8509,3,191,2017-05-09 02:53:05,http://schuppe.com/joanie,,156.212.179.134,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n8510,3,191,2017-02-24 11:00:21,http://bartell.co/sallie.spinka,,4.4.182.34,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n8511,3,191,2017-03-01 00:38:03,http://farrelllittle.com/alisha,,224.48.235.4,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n8512,3,191,2017-01-31 17:11:36,http://deckow.org/shawna.lynch,,172.162.118.240,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n8513,3,191,2017-02-15 06:14:29,http://tromp.org/elwyn,,157.34.137.51,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n8514,3,191,2017-06-01 05:55:40,http://jones.io/major_cummings,,24.184.124.29,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n8515,3,191,2017-02-03 09:34:43,http://fay.net/deie,,101.19.25.199,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n8516,3,191,2017-05-09 13:54:58,http://rathgrady.name/newell.hyatt,,144.107.12.137,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n8517,3,191,2017-03-12 20:52:04,http://west.org/odell,,161.193.130.109,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n8518,3,191,2017-01-11 19:30:18,http://tremblay.org/madelyn,,158.109.166.17,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n8519,3,191,2017-04-06 13:07:32,http://heel.name/carlie,,175.45.185.34,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n8520,3,191,2017-03-11 00:44:14,http://boyle.info/beverly,,118.221.173.105,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n8521,3,191,2017-03-10 12:34:49,http://robel.biz/estell,,74.183.193.64,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n8522,3,191,2017-04-06 21:25:42,http://herman.net/drake,,182.207.175.157,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n8523,3,191,2017-04-01 23:59:39,http://yundt.biz/carmela,,32.137.9.40,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n8524,3,191,2017-02-21 00:44:11,http://rutherfordkilback.name/blanche,,61.140.5.92,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n8525,3,191,2017-01-19 01:33:07,http://murray.io/nia.stehr,,109.212.14.166,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n8526,3,191,2017-01-05 23:16:45,http://casper.name/ania,,56.167.37.6,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n8527,3,191,2017-03-18 16:52:10,http://markspaucek.org/anya,,160.226.102.96,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n8528,3,191,2017-02-14 22:10:32,http://heel.biz/mario_schuppe,,115.152.118.138,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n8529,3,192,2017-04-27 07:14:05,http://schumm.io/justice,,13.89.189.45,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n8530,3,192,2017-03-11 14:45:50,http://hansencasper.com/roslyn,,127.40.105.6,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n8531,3,192,2017-04-09 14:36:36,http://schuster.info/cecilia.greenholt,,112.189.118.203,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n8532,3,192,2017-01-27 06:54:36,http://paucek.biz/alicia.grimes,,95.10.107.54,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n8533,3,192,2017-04-05 00:38:28,http://bartell.name/amelia,,195.124.209.144,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n8534,3,192,2017-04-26 11:07:52,http://hermann.info/mara,,180.17.88.218,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n8535,3,192,2017-04-27 15:59:06,http://wiegandvolkman.org/vergie,,128.24.164.3,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n8536,3,192,2016-12-17 03:32:31,http://green.biz/israel,,223.55.248.134,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n8537,3,192,2017-01-06 16:50:41,http://treutelpadberg.biz/makayla,,207.171.246.12,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n8538,3,192,2017-03-27 01:08:22,http://sawayn.net/halie_mayert,,164.155.231.95,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n8539,3,192,2017-04-20 01:39:02,http://mraz.biz/raina,,87.57.33.164,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n8540,3,192,2016-12-27 02:26:16,http://wuckert.io/libby,,219.235.166.17,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n8541,3,192,2017-04-09 10:09:29,http://greenholtdickens.co/garfield,,25.250.206.99,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n8542,3,192,2017-01-12 18:52:52,http://becker.biz/lance.konopelski,,19.89.40.198,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n8543,3,192,2017-01-03 06:54:25,http://kuvaliskuhic.co/alberto,,246.231.204.127,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n8544,3,192,2017-02-15 11:49:11,http://marks.io/noe,,252.13.175.209,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n8545,3,192,2017-01-27 11:47:19,http://schneider.net/darrel.ernser,,77.157.104.92,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n8546,3,192,2017-03-23 04:11:52,http://auerheidenreich.info/elda.king,,12.82.98.152,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n8547,3,192,2017-02-21 00:50:23,http://dubuque.org/emelia_langosh,,109.31.16.148,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n8548,3,192,2016-12-14 11:25:23,http://erdmandoyle.info/maverick,,95.199.29.165,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n8549,3,192,2017-02-02 07:23:43,http://murphyheaney.co/dangelo.mclaughlin,,237.204.99.2,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n8550,3,192,2016-12-14 15:02:04,http://collins.com/rachael.jacobi,,69.78.85.34,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n8551,3,192,2017-01-23 05:02:49,http://donnelly.org/eulalia,,60.227.88.190,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n14444,5,325,2017-02-12 23:58:15,http://mcdermott.biz/arthur,,116.252.185.165,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n14445,5,325,2017-04-21 14:05:30,http://bruen.net/isabelle.bergstrom,,13.73.17.132,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n14446,5,325,2017-06-07 08:29:36,http://marvin.com/anastasia,,97.165.85.120,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n14447,5,325,2016-12-17 04:18:00,http://gleichner.com/onie_jakubowski,,230.120.91.128,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n14448,5,325,2016-12-29 04:47:11,http://manteolson.io/gracie.spinka,,99.38.218.98,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n14449,5,325,2017-04-11 19:24:20,http://rogahnspencer.io/aglae_prohaska,,238.80.190.211,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n14450,5,325,2017-02-19 16:03:04,http://blockmann.name/maverick,,235.79.176.34,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n14451,5,325,2017-04-22 17:19:58,http://pfefferprohaska.net/jacynthe,,64.18.88.44,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n14452,5,325,2017-03-16 22:17:45,http://ohara.com/eryn.stroman,,252.83.32.80,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n14453,5,325,2017-04-20 18:55:37,http://boyerleuschke.com/cleve,,112.57.217.112,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n14454,5,325,2017-05-26 09:49:08,http://sawaynjast.co/stanley,,217.226.45.16,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n14455,5,325,2017-02-01 16:23:36,http://gulgowskikub.org/demetris,,162.57.77.155,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n14456,5,325,2017-02-18 11:01:38,http://greenholtjacobi.org/gianni,,159.153.58.132,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n14457,5,325,2017-01-22 05:35:14,http://cruickshank.com/elnora_gerlach,,31.11.57.59,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n14458,5,325,2017-05-17 19:05:14,http://rolfson.name/melvin_kozey,,81.116.60.218,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n14459,5,325,2017-02-16 23:10:44,http://collins.info/brown,,168.82.113.210,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n14460,5,325,2016-12-17 03:43:34,http://bradtke.io/braeden.brown,,244.60.24.252,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n14461,5,325,2017-01-27 11:38:13,http://ferryokon.biz/nathen,,118.174.215.77,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n14462,5,325,2017-01-03 02:22:17,http://macgyver.net/maeve.eichmann,,232.159.40.15,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n14463,5,325,2017-01-24 20:27:21,http://hirthespinka.net/vaughn,,138.236.205.66,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n14464,5,325,2017-05-31 09:28:43,http://hammesnienow.co/tomas_herman,,216.203.212.7,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n14465,5,325,2016-12-23 05:04:13,http://kunde.name/jeanie_nienow,,43.120.76.21,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n14466,5,325,2017-01-27 12:25:23,http://muller.name/bonita,,59.174.224.113,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n14467,5,325,2017-02-09 11:12:05,http://leffler.com/lew,,56.159.114.15,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n14468,5,325,2017-01-30 19:36:42,http://fisherswaniawski.info/dena,,89.32.205.143,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n14469,5,325,2017-01-07 18:30:18,http://klocko.info/colton.runolfon,,194.137.88.108,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n14470,5,325,2017-01-09 08:18:42,http://schimmelheathcote.name/maye.kuvalis,,186.37.69.187,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n14471,5,325,2016-12-21 20:49:15,http://oconnereichmann.io/pauline_jacobs,,15.25.171.76,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n14472,5,325,2017-03-22 00:22:21,http://kemmer.io/else,,149.156.5.206,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n14473,5,325,2017-06-10 21:28:11,http://schimmel.info/alberta,,87.30.57.103,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n14474,5,325,2017-04-27 00:14:58,http://breitenbergankunding.info/myah_hoeger,,33.16.191.125,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n14475,5,325,2016-12-28 06:23:29,http://schulist.io/paul,,92.215.160.92,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n14476,5,325,2017-01-06 16:13:45,http://hettinger.com/conner,,46.59.24.131,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n14477,5,325,2017-05-24 22:28:29,http://monahanthompson.com/brett,,232.34.211.45,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n14478,5,325,2017-05-12 22:37:52,http://sawaynschowalter.co/ottilie_hayes,,234.16.130.215,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n14479,5,325,2017-04-03 18:31:59,http://mcdermotthickle.name/stevie,,155.56.247.113,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n14480,5,325,2017-01-07 07:26:14,http://mosciski.org/barton.lynch,,27.162.241.146,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n14481,5,326,2017-04-11 06:10:52,http://kozeytremblay.biz/maymie,,179.205.230.121,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n14482,5,326,2017-04-11 19:13:01,http://sanfordsmitham.org/kolby,,83.219.93.249,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n14483,5,326,2017-01-26 06:09:19,http://turcotte.org/lauren.daniel,,181.181.104.72,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n14484,5,326,2017-03-26 04:28:49,http://paucekmarks.biz/florida,,179.132.188.228,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n14485,5,326,2017-02-06 19:59:49,http://hettinger.biz/danyka.lowe,,27.77.134.103,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n14486,5,326,2017-05-19 03:19:12,http://gulgowskigusikowski.name/domenico_dickinson,,231.15.37.134,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n14487,5,326,2017-03-20 22:50:24,http://kaulkecorwin.co/estell.wilkinson,,61.241.116.10,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n14488,5,326,2017-05-14 18:25:40,http://greenfelder.io/veronica_dooley,,3.188.23.147,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n14489,5,326,2017-04-19 19:17:51,http://rodriguez.com/shanny.morar,,34.121.216.254,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n14490,5,326,2017-02-11 12:25:54,http://langworthberge.org/lorna.quitzon,,19.112.7.211,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n14491,5,326,2017-04-05 00:06:08,http://satterfieldherman.org/scot_johns,,224.202.63.161,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n14492,5,326,2017-04-11 21:10:51,http://roberts.net/candice,,58.159.119.232,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n14493,5,326,2017-04-10 10:18:48,http://emmerichmosciski.io/giuseppe,,210.212.84.20,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n14494,5,326,2017-06-02 01:10:48,http://stiedemann.co/erica_hudson,,30.121.94.125,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n14495,5,326,2017-01-23 14:33:21,http://dibbert.info/mavis.ryan,,223.202.32.249,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n14496,5,326,2017-05-16 02:17:58,http://mcdermott.co/angelina.gusikowski,,66.16.17.119,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n14497,5,326,2017-05-13 13:56:33,http://goyette.biz/malvina,,228.147.115.203,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n14498,5,326,2017-01-11 07:03:25,http://johnsonjohnson.org/kirk.bernier,,70.125.109.153,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n17429,6,393,2017-01-19 05:25:42,http://murphy.org/selmer.bayer,,158.139.169.153,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n17430,6,393,2017-05-29 20:22:08,http://prohaska.biz/mittie,,70.115.56.14,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n17431,6,393,2017-03-24 14:54:40,http://pagac.co/jedediah,,160.204.33.240,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n17432,6,393,2017-02-10 03:19:43,http://oharadaniel.co/maye,,91.218.14.56,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n17433,6,393,2017-06-09 17:28:14,http://walker.net/elvis.osinski,,50.217.190.61,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n17434,6,393,2017-04-12 15:40:17,http://kleinrice.io/candice_kiehn,,72.154.178.155,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n17435,6,393,2016-12-31 13:59:38,http://blick.org/lia.schroeder,,99.126.128.133,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n17436,6,393,2016-12-19 23:28:08,http://barton.name/lynn_rowe,,98.10.178.53,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n17437,6,393,2016-12-24 06:03:08,http://emmerichsawayn.com/rhianna.schaefer,,196.174.133.227,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n17438,6,393,2016-12-25 10:48:07,http://ernser.info/kitty_haley,,45.211.128.165,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n17439,6,393,2017-01-17 15:19:05,http://marksvon.net/denis,,81.34.106.52,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n17440,6,393,2016-12-29 07:15:56,http://cummerata.net/clint,,24.184.91.54,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n17441,6,393,2016-12-23 04:20:46,http://lebsack.name/romaine_williamson,,201.31.165.29,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n17442,6,393,2017-01-07 01:16:16,http://kohlergreenfelder.net/jaunita_prosacco,,70.190.163.164,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n17443,6,393,2017-05-29 20:39:13,http://kuvaliskonopelski.net/winifred_grady,,238.24.133.102,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n17444,6,393,2017-04-16 07:32:27,http://lindgren.org/kyler.larkin,,157.92.26.205,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n17445,6,393,2017-04-02 04:37:20,http://oharawalker.org/sandra.murray,,58.181.98.93,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n17446,6,393,2017-02-09 22:13:14,http://pacocha.com/jevon,,63.204.102.114,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n17447,6,393,2017-05-07 12:18:18,http://jaskolskidubuque.info/willy_heathcote,,18.250.91.210,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n17448,6,393,2017-01-03 22:32:43,http://toy.biz/isabella.rowe,,85.171.86.217,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n17449,6,393,2017-04-20 04:18:38,http://kovacek.org/laurianne,,127.128.204.118,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n17450,6,393,2017-03-10 00:08:55,http://fadel.info/zion,,180.39.24.245,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n17451,6,393,2017-03-11 12:03:21,http://gusikowski.net/rosemarie,,57.240.50.167,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n17452,6,393,2017-01-28 10:43:47,http://witting.info/jacinto,,3.215.173.223,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n17453,6,393,2017-03-17 00:46:15,http://turnerkilback.io/garrett.marquardt,,254.234.41.62,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n17454,6,393,2017-03-19 14:50:56,http://wilderman.co/penelope,,152.49.224.231,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n17455,6,393,2017-05-25 21:51:28,http://klein.org/ollie_kirlin,,168.189.30.203,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n17456,6,393,2016-12-19 12:45:43,http://turner.biz/alexandrea_marks,,147.7.193.84,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n17457,6,393,2016-12-22 09:26:50,http://bruen.biz/jermey,,163.219.125.26,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n17458,6,393,2016-12-19 04:57:37,http://heaney.net/eladio.prosacco,,131.189.116.196,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n17459,6,393,2017-02-25 07:43:02,http://heller.io/maribel_hagenes,,17.172.6.132,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n17460,6,393,2017-02-22 10:06:46,http://jacobson.net/cordie,,135.19.59.195,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n17461,6,393,2017-01-24 23:27:24,http://spencerlabadie.io/gracie,,116.221.157.46,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n17462,6,393,2017-06-12 05:26:05,http://turnerruel.biz/nakia,,120.41.13.234,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n17463,6,393,2017-03-02 15:15:39,http://pagacgleichner.biz/rudy,,42.23.33.55,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n17464,6,393,2017-05-11 05:53:41,http://ondrickawhite.io/gene.collins,,239.176.252.40,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n17465,6,393,2017-05-16 22:44:48,http://hirthe.com/bradly.reichel,,147.169.88.2,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n17466,6,393,2017-02-07 20:58:18,http://bogisichgottlieb.name/ardella,,244.186.38.220,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n17467,6,393,2017-04-28 08:26:38,http://priceparisian.name/lonny,,36.131.249.118,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n17468,6,393,2017-03-17 03:45:05,http://effertz.io/janie,,174.250.231.215,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n17469,6,394,2017-03-21 07:57:26,http://graham.io/mariela,,118.77.144.197,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n17470,6,394,2017-05-11 00:47:23,http://townemarquardt.biz/peggie_mills,,158.222.98.45,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n17471,6,394,2017-05-03 21:38:50,http://stoltenberg.info/joyce,,27.237.142.65,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n17472,6,394,2017-03-11 23:57:00,http://blandalangosh.org/matilde.wisozk,,215.90.135.30,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n17473,6,394,2017-01-17 07:24:03,http://bahringerrunolfsdottir.net/abbie,,246.53.162.87,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n17474,6,394,2016-12-31 22:28:12,http://mantehaley.com/alysa,,8.95.16.48,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n17475,6,394,2017-04-20 20:15:58,http://okon.com/rupert_rempel,,132.168.232.115,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n17476,6,394,2017-04-26 06:49:17,http://okonritchie.org/maurine.lehner,,3.191.119.49,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n17477,6,394,2017-03-31 22:21:20,http://nitzsche.co/libby,,138.28.184.230,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n17478,6,394,2017-04-05 14:49:28,http://priceabernathy.com/dallas,,138.208.198.208,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n17479,6,394,2017-03-09 00:08:05,http://towne.info/jevon,,71.226.65.110,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n17480,6,394,2017-01-23 22:43:02,http://jenkinsvon.io/robyn_gottlieb,,108.125.18.84,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n17481,6,394,2017-05-10 00:24:57,http://zemlak.name/tara.rohan,,167.129.93.222,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n17482,6,394,2017-04-11 21:16:57,http://yundt.co/joany,,185.158.157.244,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n17483,6,394,2016-12-21 08:23:50,http://beatty.co/augusta_wilkinson,,132.147.102.173,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n4614,2,102,2017-01-10 08:49:41,http://smitham.biz/tyrel,,208.138.71.208,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n4615,2,102,2017-06-01 20:38:20,http://tremblay.org/stefan_little,,209.24.213.133,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n4616,2,102,2017-04-11 16:51:09,http://koepp.info/jada_ruecker,,192.94.187.57,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n4617,2,102,2017-02-24 02:48:04,http://connellylowe.org/jordon,,157.150.251.159,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n4618,2,102,2017-04-01 03:33:19,http://dooleypowlowski.org/velva,,246.246.42.192,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n4619,2,102,2017-04-29 06:05:50,http://heathcote.net/austen,,77.49.49.155,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n4620,2,102,2017-01-06 13:21:38,http://denesik.biz/estelle.lakin,,118.241.128.247,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n4621,2,102,2017-05-21 09:46:02,http://weimanncrooks.name/suzanne,,123.6.226.165,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n4622,2,102,2016-12-20 14:53:29,http://mcglynn.com/cyrus.boyer,,7.248.109.244,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n4623,2,102,2017-04-11 10:16:05,http://block.org/rick.crist,,55.229.122.65,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n4624,2,102,2017-01-23 14:03:45,http://dickinsongoodwin.biz/santiago,,100.249.120.169,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n4625,2,102,2017-02-24 21:33:14,http://koelpin.net/eric,,76.243.136.97,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n4626,2,102,2017-03-04 20:14:44,http://kovacek.info/jamie,,13.41.118.239,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n4627,2,102,2017-04-07 05:47:46,http://rice.biz/brice.bahringer,,163.19.227.163,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n4628,2,102,2017-04-22 17:21:13,http://williamson.info/frances,,233.109.156.193,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n4629,2,102,2017-03-06 04:17:03,http://heidenreichkutch.net/amie.schneider,,125.220.98.35,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n4630,2,102,2017-06-12 22:21:50,http://effertzkeler.name/skyla.mckenzie,,233.161.35.210,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n4631,2,102,2017-06-01 03:01:56,http://cartwright.org/ora,,143.212.133.154,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n4632,2,102,2017-04-26 22:14:16,http://litteltillman.info/payton.rodriguez,,79.42.153.123,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n4633,2,102,2017-04-17 23:10:52,http://oconnellcormier.biz/eloy.runolfsdottir,,238.109.192.25,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n4634,2,102,2017-06-05 15:14:25,http://homenick.net/luciano,,12.155.20.156,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n4635,2,102,2017-05-10 02:42:04,http://waelchi.co/rosina_schmeler,,87.185.193.42,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n4636,2,102,2017-04-05 07:14:40,http://kiehn.co/arden,,252.186.69.58,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n4637,2,102,2016-12-29 20:36:23,http://wuckert.com/keara,,100.220.115.169,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n4638,2,102,2017-02-16 05:25:23,http://maggio.io/sharon_kaulke,,27.195.239.204,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n4639,2,102,2016-12-24 08:55:33,http://gerhold.org/micaela.haley,,92.105.107.52,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n4640,2,103,2016-12-24 04:01:56,http://murphy.io/alfonzo,,246.148.55.15,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n4641,2,103,2017-01-24 15:05:50,http://nolan.name/ernie_herzog,,66.151.162.227,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n4642,2,103,2016-12-16 08:45:21,http://mcglynnwiegand.io/ambrose,,185.135.158.214,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n4643,2,103,2017-04-26 10:15:53,http://dietrichheller.info/lawson.frami,,214.45.157.235,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n4644,2,103,2017-01-24 09:34:16,http://bednar.biz/joyce,,36.13.50.152,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n4645,2,103,2017-05-27 03:29:21,http://adams.com/quinn.purdy,,211.161.21.56,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n4646,2,103,2017-02-18 10:12:52,http://mccullough.org/johnnie_nader,,149.25.145.7,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n4647,2,103,2017-02-02 15:15:23,http://larkinlarkin.co/harvey.jerde,,246.53.192.144,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n4648,2,103,2017-04-11 12:09:32,http://daughertycarter.org/johnson,,81.171.198.25,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n4649,2,103,2016-12-13 09:57:32,http://lang.info/joe.green,,148.225.157.152,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n4650,2,103,2017-02-24 13:05:07,http://hammes.co/marisol.gottlieb,,101.6.123.127,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n4651,2,103,2017-06-14 02:58:11,http://rodriguez.name/lukas,,24.144.191.140,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n4652,2,103,2017-04-16 14:53:38,http://becker.io/lukas,,202.223.246.246,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n4653,2,103,2017-03-04 21:21:53,http://hills.com/columbus.lockman,,175.74.236.203,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n4654,2,103,2017-02-06 02:58:16,http://sanford.info/edd,,96.228.118.116,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n4655,2,103,2017-03-08 22:13:34,http://collier.com/karl,,90.47.83.224,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n4656,2,103,2017-04-05 05:55:02,http://ebertfahey.biz/khalid_stiedemann,,141.224.212.142,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n4657,2,103,2017-06-06 03:17:56,http://daugherty.io/vidal_doyle,,231.199.31.88,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n4658,2,103,2017-02-24 16:01:18,http://weber.biz/ocie,,23.209.77.150,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n4659,2,103,2017-06-13 14:28:12,http://cummerata.co/jenifer,,109.184.67.235,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n4660,2,103,2017-05-23 01:42:34,http://maggio.co/vinnie,,31.25.126.158,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n4661,2,103,2017-03-03 17:17:42,http://monahanebert.biz/giovanny.considine,,35.81.86.95,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n4662,2,103,2017-05-18 09:35:52,http://gusikowskigislason.co/derrick,,222.206.217.252,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n4663,2,103,2017-03-19 20:07:18,http://wisozk.io/reece.oberbrunner,,5.119.110.42,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n4664,2,103,2017-02-03 20:10:13,http://stiedemann.org/brant,,140.174.218.182,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n4665,2,103,2017-02-18 00:40:44,http://gutmann.net/rory.farrell,,226.174.39.229,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n4666,2,103,2017-01-21 05:29:31,http://schummhansen.biz/jarod,,241.89.199.242,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n4667,2,104,2017-06-12 11:22:44,http://aufderhar.org/tre,,48.210.58.72,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n4668,2,104,2017-01-31 18:57:51,http://hackett.com/tillman,,248.156.251.90,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n11545,4,257,2017-03-15 00:36:55,http://ferry.com/jillian_lind,0.9280659973,68.79.137.162,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n11546,4,257,2017-03-21 17:36:33,http://hegmann.org/michaela,0.3542053402,182.118.60.70,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n11547,4,257,2016-12-18 05:13:28,http://williamsonconsidine.org/addison_langosh,0.1065460354,116.245.7.183,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n11548,4,257,2017-01-14 19:39:26,http://stoltenberg.io/dameon,0.4757582087,3.151.93.162,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n11549,4,257,2017-03-03 09:10:24,http://schroederdamore.io/mireille,0.1696252014,40.180.8.238,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n11550,4,257,2017-04-10 14:58:53,http://brekkeschimmel.com/leila,0.8446581441,140.162.171.52,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n11551,4,257,2017-03-13 15:29:13,http://green.org/percival,0.9796250342,185.116.45.189,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n11552,4,257,2016-12-25 16:04:36,http://ernser.net/ellen_welch,0.3579824059,203.169.36.228,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n11553,4,257,2017-05-25 18:07:07,http://gloverkovacek.biz/keanu.gulgowski,0.8673735476,70.63.121.148,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n11554,4,257,2017-03-23 11:08:36,http://luettgen.org/kariane.turcotte,0.5764153080,222.47.121.8,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n11555,4,257,2017-05-13 19:55:15,http://murphy.io/ernest,0.2000862972,80.95.53.184,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n11556,4,257,2017-05-30 00:17:48,http://kautzer.co/mariah,0.6410352675,169.183.169.3,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n11557,4,257,2016-12-18 17:59:15,http://torp.info/taylor,0.0813071641,18.124.18.195,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n11558,4,257,2017-06-13 11:28:17,http://durgan.info/thelma.walter,0.9707125083,43.222.80.183,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n11559,4,257,2016-12-15 17:55:21,http://lockman.com/marlen_bradtke,0.8089259904,187.119.115.130,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n11560,4,257,2017-06-12 17:16:37,http://blanda.name/everette_reynolds,0.5497470657,216.43.233.205,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n11561,4,257,2017-05-05 18:29:08,http://gutmann.info/kaylie.kirlin,0.1887399049,232.65.80.232,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n11562,4,257,2017-04-28 07:38:59,http://dietrich.io/leonardo_nikolaus,0.7456756390,32.226.182.166,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n11563,4,258,2017-01-29 03:27:24,http://goodwin.co/madison_casper,0.3420433811,81.74.90.243,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n11564,4,258,2016-12-16 16:26:02,http://hodkiewicz.io/wilma.steuber,0.3203987676,196.198.44.46,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n11565,4,258,2017-01-16 23:19:22,http://maggio.biz/betty.heidenreich,0.7598164387,84.28.62.136,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n11566,4,258,2017-01-30 23:35:09,http://dicki.io/nelson,0.1483920646,210.172.183.216,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n11567,4,258,2016-12-23 17:08:27,http://west.org/jacey,0.2732236549,114.170.27.229,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n11568,4,258,2016-12-18 09:01:20,http://franeckicollier.biz/parker.wolff,0.5352635125,18.8.155.147,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n11569,4,258,2017-04-11 01:43:19,http://fay.net/amari,0.1869228067,184.71.8.4,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n11570,4,258,2017-01-07 02:31:17,http://little.name/demond,0.1716153007,162.234.23.253,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n11571,4,258,2017-02-01 00:56:38,http://orn.info/trace.conn,0.4809649094,201.206.222.213,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n11572,4,258,2017-03-05 21:19:59,http://harvey.info/mia,0.3567055792,82.157.187.25,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n11573,4,258,2017-03-31 21:01:10,http://denesik.name/pascale,0.7504794898,240.63.216.57,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n11574,4,258,2017-02-22 23:22:28,http://powlowski.com/maya_rolfson,0.1032670211,235.24.144.145,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n11575,4,258,2017-02-26 08:01:39,http://zulauf.co/dennis,0.5043870284,178.172.122.32,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n11576,4,258,2017-04-20 14:53:10,http://pfefferdenesik.biz/eleonore.borer,0.2204665496,222.104.2.197,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n11577,4,258,2017-06-10 07:43:23,http://ziemann.net/holly,0.9506536424,229.160.20.161,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n11578,4,258,2016-12-14 18:10:02,http://reichert.org/cynthia,0.3147175302,9.223.159.173,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n11579,4,258,2017-03-20 08:28:32,http://lednerjohns.name/constance,0.4778086708,81.33.233.209,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n11580,4,258,2017-05-07 01:56:35,http://hermann.name/verner_kunze,0.6558076442,47.201.210.106,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n11581,4,258,2017-01-30 17:22:56,http://treutel.name/mavis_carroll,0.1775367463,219.110.133.126,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n11582,4,258,2017-02-03 06:45:49,http://daughertyparisian.biz/fae_lind,0.7272382041,118.93.156.189,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n11583,4,258,2017-05-31 16:21:14,http://pacochasmith.org/jaylan,0.8925104143,67.174.210.158,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n11584,4,258,2017-01-14 17:10:31,http://wuckert.co/kamron,0.3332484618,250.138.249.196,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n11585,4,258,2016-12-21 14:21:30,http://waters.net/polly,0.9591109098,96.179.170.65,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n11586,4,258,2017-04-14 09:17:26,http://parker.info/reba,0.2514086660,92.200.184.218,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n11587,4,258,2017-05-11 13:18:56,http://wisoky.io/abelardo,0.6340643031,45.39.26.64,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n11588,4,258,2017-03-30 14:49:38,http://padbergjast.io/clay_huels,0.7319772874,253.242.110.22,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n11589,4,258,2017-04-12 20:50:27,http://ankunding.io/aida_kilback,0.2108026609,254.25.31.210,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n11590,4,258,2017-04-04 06:01:00,http://donnelly.com/loy.pacocha,0.5160398141,253.8.7.155,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n11591,4,258,2017-04-01 01:10:32,http://bauch.name/lyric_jacobson,0.3176902796,33.8.179.51,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n11592,4,258,2017-04-17 18:42:44,http://effertz.io/damion.medhurst,0.7779726516,187.50.165.100,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n11593,4,258,2017-01-01 12:43:22,http://homenick.info/soledad_reinger,0.4763954647,83.15.156.218,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n11594,4,258,2017-05-12 06:34:39,http://bode.io/reggie.howell,0.9146283791,216.249.78.91,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n11595,4,258,2017-01-19 13:34:33,http://schinner.biz/nicholas,0.1958545459,102.220.121.247,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n11596,4,258,2017-05-04 17:04:48,http://nikolaus.io/brooklyn_dickens,0.4162357608,202.83.2.210,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n8552,3,192,2016-12-22 20:41:54,http://schulist.info/dahlia,,164.240.220.166,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n8553,3,192,2017-02-26 12:33:10,http://hackett.io/martine,,229.67.193.151,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n8554,3,192,2017-01-12 03:45:46,http://abernathy.biz/ike,,251.85.5.178,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n8555,3,192,2017-02-22 13:51:54,http://koepp.io/celestino_spencer,,207.60.105.82,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n8556,3,192,2017-02-18 02:47:17,http://runte.org/ethel_goldner,,218.74.21.164,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n8557,3,192,2017-06-04 20:26:22,http://will.io/glennie,,201.20.90.184,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n8558,3,192,2016-12-25 23:52:42,http://stokes.info/marcelino,,173.193.194.188,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n8559,3,192,2017-03-08 22:00:06,http://schuster.com/orpha_zemlak,,123.119.221.113,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n8560,3,192,2017-06-12 03:50:32,http://friesen.name/alfonso,,96.77.89.166,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n8561,3,192,2017-01-21 04:09:12,http://cartwrightullrich.co/adonis,,49.128.189.81,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n8562,3,192,2017-06-07 20:57:31,http://tremblay.net/esmeralda.lehner,,119.191.11.4,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n8563,3,192,2017-03-26 09:03:36,http://barrows.biz/verna,,55.76.79.170,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n8564,3,192,2017-03-20 06:04:32,http://auer.net/dalton.grimes,,159.171.33.75,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n8565,3,192,2017-05-23 05:22:35,http://hills.com/maxine_pagac,,90.66.45.172,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n8566,3,192,2017-01-27 16:32:41,http://shieldsmiller.org/roderick,,192.96.151.221,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n8567,3,192,2017-03-10 22:11:43,http://becker.name/greta_sipes,,76.18.214.248,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n8568,3,192,2017-04-06 15:45:03,http://jast.name/loyal,,29.209.62.85,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n8569,3,192,2017-05-05 23:11:13,http://moore.biz/orlando_feest,,177.141.32.73,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n8570,3,192,2017-02-09 20:19:16,http://fisherdurgan.net/raheem.wilderman,,90.103.170.54,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n8571,3,192,2017-02-14 05:56:18,http://heathcote.biz/aric.hackett,,9.155.174.13,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n8572,3,192,2017-05-03 03:08:20,http://fay.org/ryley.bartell,,234.250.211.236,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n8573,3,192,2017-03-08 13:55:39,http://barton.org/cory,,90.234.85.150,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n8574,3,192,2017-03-29 06:57:18,http://ratke.info/myrna,,123.139.236.59,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n8575,3,192,2017-06-09 17:51:47,http://schuppe.org/elmo.zboncak,,252.118.45.209,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n8576,3,192,2017-04-22 10:17:21,http://powlowski.info/stephanie_ankunding,,53.191.180.186,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n8577,3,192,2017-01-24 06:30:42,http://littel.info/aubree.orn,,61.134.186.44,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n8578,3,192,2017-06-01 17:21:39,http://ziemannhowe.io/kathleen,,133.149.178.13,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n8579,3,192,2017-06-06 03:37:44,http://grahamondricka.net/elvie,,137.228.218.216,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n8580,3,192,2016-12-14 12:43:24,http://swift.biz/luisa.turner,,165.200.78.123,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n8581,3,193,2017-01-07 07:54:17,http://nitzsche.net/berry_parker,,7.16.86.86,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n8582,3,193,2017-01-14 19:32:10,http://little.name/duane,,212.108.127.164,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n8583,3,193,2017-02-22 20:44:13,http://purdy.org/nola.miller,,74.51.234.175,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n8584,3,193,2017-02-25 06:57:27,http://mertz.co/anahi_hilll,,83.247.70.94,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n8585,3,193,2017-02-25 15:37:36,http://schamberger.co/louie_brown,,250.9.146.132,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n8586,3,193,2017-05-25 20:45:47,http://walter.name/kim,,38.129.235.92,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n8587,3,193,2017-02-01 18:42:15,http://treutel.org/zaria.hills,,79.35.166.201,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n8588,3,193,2017-04-28 12:38:45,http://windlereffertz.co/walton_white,,119.109.64.104,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n8589,3,193,2017-04-29 21:28:52,http://nitzschekoepp.biz/concepcion_aufderhar,,190.233.249.14,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n8590,3,193,2017-04-21 00:30:10,http://rice.io/ru,,251.216.17.45,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n8591,3,193,2017-04-21 19:55:41,http://marvinhills.name/brennan.leuschke,,189.96.159.13,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n8592,3,193,2017-03-04 03:02:55,http://herman.net/hazle.christiansen,,248.239.183.22,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n8593,3,193,2017-03-13 02:07:13,http://rutherford.biz/alan,,105.106.132.40,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n8594,3,193,2017-03-22 19:41:18,http://wolf.co/jason,,239.162.7.103,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n8595,3,193,2017-03-02 00:39:11,http://stoltenberg.io/edgardo,,134.207.238.48,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n8596,3,193,2016-12-31 21:04:18,http://batz.org/jeanne.terry,,142.105.176.230,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n8597,3,193,2017-03-13 01:55:11,http://pacocha.io/lynn_rohan,,165.176.73.62,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n8598,3,193,2017-05-11 06:04:07,http://mckenzie.net/johnpaul,,29.131.226.152,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n8599,3,193,2017-01-18 11:42:17,http://kohler.io/ulices.reynolds,,94.132.242.54,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n8600,3,193,2017-04-22 23:47:23,http://feestmoriette.name/kenyatta_pacocha,,44.166.240.204,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n8601,3,193,2017-06-06 00:26:58,http://batz.name/tate.maggio,,108.222.200.73,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n8602,3,193,2017-03-09 10:53:34,http://osinski.org/aditya,,235.189.172.215,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n8603,3,193,2017-01-21 17:14:48,http://gaylordglover.co/thea.quigley,,146.173.209.191,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n8604,3,193,2017-01-30 07:53:54,http://johnson.org/terrance.king,,183.67.81.20,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n8605,3,193,2017-06-09 13:09:48,http://spinka.io/derick,,238.79.70.28,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n8606,3,193,2017-03-25 23:32:01,http://schamberger.org/zion,,49.241.75.154,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n8607,3,193,2017-05-20 09:20:15,http://hegmann.info/johann,,35.170.85.206,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n14499,5,326,2017-05-22 16:53:57,http://eichmannwalter.io/daren,,239.178.218.171,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n14500,5,326,2017-06-12 21:47:56,http://bauch.net/triston,,63.206.18.140,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n14501,5,326,2017-05-28 11:21:59,http://tremblay.name/lora.bashirian,,95.203.126.55,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n14502,5,326,2017-01-04 05:53:46,http://brakus.co/murl.ullrich,,217.237.169.29,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n14503,5,327,2017-04-27 00:07:31,http://dickensmoore.org/floyd,0.1384120216,116.62.149.11,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n14504,5,327,2017-03-13 01:44:02,http://bechtelar.com/yasmine.strosin,0.7039573489,175.83.220.183,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n14505,5,327,2017-01-22 17:36:10,http://anderson.net/shemar_jacobs,0.9622484281,56.252.127.143,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n14506,5,327,2016-12-26 19:27:28,http://auer.com/anne,0.4387544220,136.219.117.106,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n14507,5,327,2017-03-29 17:48:22,http://sauerboehm.info/mckayla_koepp,0.0918569334,160.87.146.221,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n14508,5,327,2017-03-19 05:06:17,http://bergnaum.name/faye,0.2519265351,92.243.245.142,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n14509,5,327,2017-04-15 20:19:35,http://kautzer.co/terrill.gottlieb,0.8497015316,27.74.53.13,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n14510,5,327,2017-01-19 20:24:38,http://schmittbednar.org/claudine,0.3198189676,203.46.73.163,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n14511,5,327,2017-06-03 02:28:39,http://thiel.io/edgardo_padberg,0.3771096653,55.16.108.223,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n14512,5,327,2017-03-21 23:55:21,http://shieldslockman.info/daija_herman,0.0949968615,133.180.9.101,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n14513,5,327,2017-03-30 02:30:11,http://ruecker.name/luigi.mertz,0.0376904458,48.95.46.223,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n14514,5,327,2017-03-14 20:19:26,http://wiza.info/judge_strosin,0.2565915226,38.137.186.245,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n14515,5,327,2017-04-09 09:29:16,http://dickensrosenbaum.biz/macie,0.6964404538,112.102.138.123,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n14516,5,327,2017-04-22 03:29:24,http://stracke.name/nikolas_huels,0.2010071826,107.227.56.240,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n14517,5,327,2017-03-20 18:12:20,http://boylekuhlman.co/evelyn.green,0.9566766332,105.39.143.185,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n14518,5,327,2017-01-13 21:47:19,http://rogahn.com/kristoffer_pfannerstill,0.2422783351,6.37.223.76,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n14519,5,327,2017-05-28 01:16:25,http://jacobs.net/anita_mccullough,0.8751413972,220.61.151.217,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n14520,5,327,2017-04-29 08:00:05,http://casper.biz/quinn.rath,0.6942506101,19.199.36.244,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n14521,5,327,2017-01-13 10:47:33,http://dooley.io/amy.rippin,0.5574630694,170.12.166.88,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n14522,5,327,2016-12-19 02:24:49,http://schulistboehm.info/bell.huels,0.1642967026,197.105.217.160,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n14523,5,327,2017-02-05 22:42:07,http://anderson.biz/sam_mann,0.4945594178,7.189.26.127,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n14524,5,327,2017-03-23 01:10:56,http://ullrich.net/jarred_hickle,0.4450312038,243.17.70.251,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n14525,5,327,2017-04-11 00:17:46,http://zieme.io/gerardo,0.1358359501,188.95.184.241,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n14526,5,327,2017-03-06 19:51:53,http://johnstonabbott.info/ludwig,0.7068070115,78.235.67.89,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n14527,5,327,2017-04-03 23:59:55,http://deckow.biz/ronny,0.5763898697,240.245.189.176,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n14528,5,327,2017-05-08 00:40:31,http://okon.io/morton,0.6900680867,170.160.141.195,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n14529,5,327,2017-01-20 01:00:45,http://lehner.name/coleman,0.3159058553,190.208.54.213,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n14530,5,327,2016-12-23 08:03:36,http://eichmannberge.net/leda,0.6245302262,55.247.207.181,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n14531,5,327,2017-05-20 19:37:01,http://lindgrenhodkiewicz.com/demarco,0.1529621480,51.195.154.32,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n14532,5,327,2017-04-14 21:26:06,http://wisoky.org/schuyler_hettinger,0.7536770636,94.30.180.74,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n14533,5,327,2017-03-22 04:37:13,http://schroeder.biz/eddie.satterfield,0.7754900537,162.211.39.143,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n14534,5,327,2017-05-05 21:57:08,http://koelpin.name/anibal.bartell,0.8426241034,101.80.137.75,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n14535,5,327,2016-12-30 00:26:41,http://beer.name/eden,0.6555693016,107.171.247.15,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n14536,5,327,2017-01-31 19:46:07,http://hettinger.name/roxanne,0.2598105405,47.44.29.231,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n14537,5,327,2017-04-30 00:00:40,http://roweruel.net/lurline.carroll,0.1126398162,87.158.98.61,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n14538,5,327,2017-03-10 19:06:39,http://reilly.co/winifred.kuhn,0.2396389600,45.90.238.140,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n14539,5,327,2017-03-25 19:06:36,http://walker.info/isom,0.9718005506,199.203.86.18,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n14540,5,327,2017-02-12 13:56:06,http://lueilwitzgraham.com/clemmie_weber,0.9304386349,124.42.163.205,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n14541,5,327,2017-01-13 00:48:59,http://schroedertorphy.io/vicenta_emard,0.7540365704,251.103.137.194,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n14542,5,327,2016-12-24 22:41:44,http://herzog.net/santino_stracke,0.6078702063,69.123.134.76,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n14543,5,327,2016-12-22 16:46:49,http://schoen.net/yasmeen,0.8856699433,94.151.17.157,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n14544,5,327,2017-04-11 08:01:44,http://goldnerdaniel.io/marcia,0.0431837654,45.130.72.109,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n14545,5,327,2017-01-23 09:28:45,http://beier.org/antonietta,0.5603793324,158.92.40.5,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n14546,5,328,2017-05-09 14:29:31,http://ebert.co/ole.prohaska,0.3729042165,46.182.220.141,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n14547,5,328,2017-05-17 01:08:57,http://pagac.name/ahmed,0.4744185308,70.16.197.55,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n14548,5,328,2017-06-05 05:35:37,http://grady.biz/lennie.mitchell,0.1943797502,88.190.156.39,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n14549,5,328,2017-03-12 08:40:55,http://lockman.name/ariane.rice,0.9180027710,12.121.128.32,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n17484,6,394,2017-05-03 11:46:49,http://lowe.name/quentin.schuster,,218.85.26.24,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n17485,6,394,2017-03-03 01:01:08,http://cronahagenes.net/mireille,,67.89.124.107,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n17486,6,394,2017-01-31 12:53:54,http://boscohahn.biz/martina_swift,,234.55.192.72,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n17487,6,394,2017-03-08 17:14:18,http://kuvalis.biz/katlynn.oconnell,,45.120.38.207,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n17488,6,394,2017-02-05 07:37:40,http://hills.net/gus,,136.19.42.197,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n17489,6,394,2017-04-05 19:56:57,http://vandervort.co/henderson.bailey,,66.179.176.166,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n17490,6,394,2017-04-22 21:35:04,http://treutelmarquardt.co/nikki,,250.242.226.243,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n17491,6,394,2017-04-25 09:30:29,http://wolf.co/moses_vandervort,,249.249.17.212,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n17492,6,394,2017-03-20 12:47:22,http://nikolaus.io/margot,,87.235.84.133,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n17493,6,394,2017-01-24 06:20:56,http://haley.name/wiley,,18.114.128.147,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n17494,6,394,2017-03-22 18:04:34,http://bashirian.name/monica_sanford,,162.100.14.210,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n17495,6,394,2017-02-21 11:51:13,http://block.name/vernice,,79.77.169.171,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n17496,6,394,2017-04-05 21:49:59,http://schmidt.io/trycia,,18.200.250.91,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n17497,6,394,2017-01-21 07:32:45,http://bins.com/jasen.swaniawski,,67.179.137.234,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n17498,6,394,2017-01-03 03:42:20,http://hintz.io/ezekiel_fahey,,212.79.246.51,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n17499,6,394,2016-12-17 15:39:09,http://jast.biz/maud_jerde,,18.32.160.96,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n17500,6,394,2017-03-12 12:42:52,http://botsfordkuhic.co/alfonso.pouros,,226.68.136.32,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n17501,6,394,2017-05-01 10:50:13,http://predovicwehner.net/bettie_wuckert,,28.72.63.84,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n17502,6,394,2017-03-04 18:27:37,http://romaguera.co/avery,,211.172.139.32,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n17503,6,394,2016-12-21 08:17:31,http://ryanmorar.net/dixie_padberg,,100.133.128.183,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n17504,6,394,2017-05-30 17:15:21,http://corkeryreichel.co/ava.padberg,,41.165.243.185,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n17505,6,394,2016-12-15 03:38:22,http://kohlerzulauf.net/martin_tremblay,,56.122.239.199,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n17506,6,394,2017-04-10 10:44:52,http://zieme.io/jonathan,,47.74.166.96,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n17507,6,394,2017-06-09 20:18:15,http://rosenbaum.com/gordon_gorczany,,22.132.216.58,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n17508,6,395,2017-01-16 06:00:56,http://block.com/weldon,,195.23.119.81,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n17509,6,395,2017-05-29 03:32:23,http://barton.co/brook.bruen,,16.165.28.71,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n17510,6,395,2017-06-11 10:54:48,http://doyle.biz/etha_wehner,,67.115.89.33,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n17511,6,395,2017-05-18 03:29:11,http://kiehnbailey.name/guadalupe.jerde,,112.146.203.125,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n17512,6,395,2017-02-20 03:46:16,http://bashirian.com/van,,165.210.16.78,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n17513,6,395,2017-03-02 03:47:04,http://bartoletti.biz/eudora.roberts,,28.2.146.208,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n17514,6,395,2016-12-14 23:03:35,http://schmitt.co/marc.prosacco,,148.64.216.180,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n17515,6,395,2016-12-29 02:18:18,http://kuhn.io/dan,,224.39.40.247,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n17516,6,395,2017-03-30 02:28:59,http://shanahankirlin.co/mazie,,92.49.31.136,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n17517,6,395,2017-02-03 03:51:42,http://christiansen.org/lenora,,94.229.78.47,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n17518,6,395,2017-02-05 11:27:53,http://nicolasterry.co/nettie,,24.221.68.104,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n17519,6,395,2016-12-31 11:01:30,http://douglasemard.biz/carolyne_nader,,211.6.34.34,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n17520,6,395,2017-04-13 23:08:38,http://corkery.biz/mortimer,,17.98.210.35,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n17521,6,395,2017-03-06 14:04:56,http://rosenbaum.io/reba.gusikowski,,181.184.36.101,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n17522,6,395,2016-12-18 05:56:03,http://langworthohara.info/bertrand_harber,,232.129.76.167,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n17523,6,395,2017-05-20 09:50:18,http://gusikowski.io/alexandria,,33.23.248.224,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n17524,6,395,2017-01-25 16:51:14,http://kub.co/dimitri,,250.74.145.82,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n17525,6,395,2016-12-21 22:10:38,http://baumbach.com/modesto,,253.145.223.170,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n17526,6,395,2017-01-01 11:15:23,http://rowehaag.info/jonatan.braun,,38.52.84.15,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n17527,6,395,2017-04-16 12:09:01,http://will.com/carmine,,6.204.185.219,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n17528,6,395,2016-12-18 03:37:50,http://stanton.com/karelle,,40.11.50.238,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n17529,6,395,2017-04-14 18:27:01,http://baumbachmraz.org/imelda,,13.169.212.31,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n17530,6,395,2017-03-14 04:56:54,http://beiersporer.org/lorna,,79.253.79.88,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n17531,6,395,2017-04-23 07:48:25,http://kleinwest.org/bailee,,153.74.48.31,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n17532,6,395,2017-04-12 06:20:35,http://cummerata.name/maximillian.dooley,,168.163.245.93,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n17533,6,395,2017-04-08 11:24:07,http://howe.info/evans,,183.105.154.13,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n17534,6,395,2017-03-26 05:58:21,http://ko.net/jeff,,82.31.178.240,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n17535,6,395,2017-01-25 15:12:40,http://lindgren.co/trea,,165.187.134.224,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n17536,6,395,2017-06-11 09:17:21,http://hoppevandervort.info/viola,,60.3.11.247,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n17537,6,395,2017-04-08 20:11:38,http://strosinparisian.name/jennings_feeney,,96.178.215.81,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n17538,6,395,2017-06-12 15:31:42,http://robel.co/emie,,195.148.203.246,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n17539,6,395,2017-05-14 22:34:35,http://lindgrenyost.net/marlin,,55.97.45.245,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n4669,2,104,2017-01-20 06:16:20,http://medhurst.biz/elena,,31.165.75.88,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n4670,2,104,2017-05-09 11:57:10,http://lindgrenflatley.biz/hailey,,240.193.20.62,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n4671,2,104,2017-04-27 20:05:03,http://wiza.biz/damien.mcdermott,,121.121.17.101,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n4672,2,104,2017-02-19 12:27:52,http://rippin.co/alycia,,74.183.240.66,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n4673,2,104,2017-01-04 10:09:51,http://hirthe.name/noble_rosenbaum,,110.19.52.139,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n4674,2,104,2017-01-13 20:54:08,http://haagblock.name/addison,,221.132.138.45,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n4675,2,104,2016-12-20 08:58:04,http://nicolasgreenholt.io/corine.schinner,,128.48.161.112,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n4676,2,104,2016-12-18 21:07:13,http://kiehn.co/camila,,132.100.96.26,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n4677,2,104,2017-06-12 23:36:52,http://douglascarter.info/else_schaefer,,113.95.178.66,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n4678,2,104,2017-02-03 18:50:28,http://quigley.org/harrison,,238.137.87.195,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n4679,2,104,2017-03-28 05:15:36,http://walterdach.io/flo.quigley,,117.127.197.240,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n4680,2,104,2017-04-01 05:50:57,http://cristkohler.net/bartholome,,246.109.61.25,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n4681,2,104,2017-02-03 09:27:07,http://hilllwaters.info/dexter,,122.134.75.62,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n4682,2,104,2016-12-17 22:52:19,http://abbottschultz.io/mercedes_legros,,130.64.24.24,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n4683,2,104,2017-02-25 21:18:14,http://boscogerlach.io/lura_swaniawski,,132.73.4.193,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n4684,2,104,2016-12-28 20:37:34,http://cruickshank.co/ru,,46.70.221.39,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n4685,2,104,2017-02-22 18:25:41,http://bode.org/celestine,,114.24.19.64,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n4686,2,104,2017-02-02 14:49:03,http://pagacvolkman.co/madonna_mills,,140.248.31.17,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n4687,2,104,2017-06-02 19:22:30,http://bechtelar.net/cole,,76.206.140.23,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n4688,2,104,2017-05-19 10:56:41,http://murphy.net/yesenia.kuhlman,,162.83.78.236,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n4689,2,104,2016-12-18 05:07:31,http://cartwright.co/orie,,52.174.22.166,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n4690,2,104,2017-04-18 03:42:58,http://orn.name/astrid,,158.210.135.3,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n4691,2,104,2017-01-17 20:08:49,http://keeling.name/grayce,,105.203.131.247,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n4692,2,104,2016-12-17 06:54:57,http://lemkegrady.info/kameron,,22.15.63.248,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n4693,2,104,2017-02-19 23:54:11,http://hermann.io/sincere_towne,,237.88.53.162,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n4694,2,104,2017-04-26 01:21:11,http://hermistonkuhic.name/pink_corkery,,183.198.36.53,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n4695,2,104,2017-02-05 19:25:10,http://kaulke.name/opal,,117.250.38.136,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n4696,2,104,2016-12-18 14:48:58,http://little.info/frederique,,109.60.62.28,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n4697,2,104,2017-04-05 05:28:17,http://bartell.name/ruthie,,67.117.148.213,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n4698,2,104,2017-03-01 02:27:32,http://legrosbednar.net/randi_kerluke,,131.186.245.86,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n4699,2,104,2017-03-01 08:19:05,http://gerlachokeefe.org/barbara.reichert,,194.153.98.179,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n4700,2,104,2017-04-23 19:02:24,http://simonisheel.org/juliet,,128.50.91.51,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n4701,2,104,2017-05-19 00:09:33,http://reynolds.co/kaelyn_corwin,,185.52.173.137,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n4702,2,104,2017-01-30 06:08:21,http://waelchi.info/micheal_kling,,60.47.7.221,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n4703,2,104,2017-01-16 04:29:02,http://jerderice.co/kaandra_corkery,,128.196.54.78,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n4704,2,104,2017-06-03 17:36:26,http://kovacek.info/amanda_bergstrom,,226.30.157.20,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n4705,2,104,2017-06-04 21:01:33,http://emard.name/astrid_mcglynn,,88.143.88.93,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n4706,2,104,2017-06-06 12:53:32,http://wunsch.co/holden,,155.168.141.205,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n4707,2,104,2017-06-09 00:41:43,http://damore.name/karley_schiller,,113.79.225.253,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n4708,2,104,2017-05-14 19:49:14,http://braun.io/ella.armstrong,,225.149.9.115,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n4709,2,104,2017-06-06 13:39:50,http://schillervolkman.co/summer,,12.188.69.12,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n4710,2,104,2017-05-29 23:52:04,http://dare.co/madonna_wunsch,,105.128.100.72,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n4711,2,104,2017-02-20 13:00:57,http://monahanluettgen.io/conor,,242.61.123.84,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n4712,2,104,2017-02-22 07:18:23,http://hickle.net/gage,,210.85.204.141,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n4713,2,104,2017-03-09 01:20:06,http://dickens.com/morgan_moen,,178.210.92.74,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n4714,2,104,2017-04-20 02:22:03,http://hirthe.io/felton.effertz,,167.95.147.135,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n4715,2,104,2017-02-21 16:20:46,http://paucek.net/howell,,158.214.71.195,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n4716,2,104,2016-12-25 13:21:56,http://powlowski.biz/aric,,232.124.9.57,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n4717,2,104,2017-04-04 13:44:48,http://sanford.co/solon,,244.70.22.195,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n4718,2,104,2017-01-07 20:29:25,http://fadel.net/emerson,,178.40.109.232,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n4719,2,104,2017-02-09 23:54:08,http://daugherty.io/demarco.rosenbaum,,12.188.118.106,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n4720,2,104,2017-04-10 08:31:09,http://witting.com/kaylin,,122.72.201.49,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n4721,2,104,2016-12-22 18:40:09,http://vonrueden.name/yvonne_gutmann,,218.219.97.31,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n4722,2,104,2017-02-02 05:34:29,http://casper.net/petra.hills,,214.76.113.211,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n4723,2,104,2017-01-12 10:05:41,http://rogahnwill.net/jermaine_rau,,120.110.204.204,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n4724,2,104,2017-06-01 06:01:59,http://kshlerin.net/elisha.harris,,180.62.248.147,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n11597,4,258,2017-01-20 11:08:49,http://browntoy.com/seth.nitzsche,0.3900888372,72.60.178.143,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n11598,4,258,2017-01-24 11:29:38,http://cristbrekke.biz/germaine_mertz,0.9043735709,138.3.149.68,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n11599,4,258,2017-05-07 13:04:12,http://erdman.info/dominique,0.5075391706,44.248.38.165,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n11600,4,258,2017-02-10 22:42:42,http://schinnergusikowski.net/cordie.breitenberg,0.0143759593,244.20.220.226,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n11601,4,258,2017-05-10 02:34:01,http://gaylord.co/mariano,0.8956517886,17.41.102.118,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n11602,4,258,2017-01-30 08:08:14,http://swaniawski.biz/noah,0.1733337392,222.50.73.37,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n11603,4,258,2017-02-21 17:37:20,http://cronin.net/angelica,0.0798292794,227.191.65.52,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n11604,4,258,2017-02-28 04:36:18,http://wiegand.biz/kara,0.3541409111,127.13.192.123,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n11605,4,258,2017-05-24 13:07:58,http://kreiger.name/alva,0.8080927846,105.17.239.200,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n11606,4,258,2017-05-15 21:13:09,http://wunsch.biz/lionel,0.9413530043,140.113.246.116,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n11607,4,258,2017-05-20 18:41:44,http://raynor.com/dan,0.4784039353,187.189.22.41,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n11608,4,258,2017-03-03 17:41:33,http://walker.biz/delia,0.6215827555,160.190.212.42,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n11609,4,258,2017-01-02 02:03:59,http://klocko.org/wyatt,0.1492230972,67.240.55.68,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n11610,4,258,2016-12-31 09:20:59,http://flatley.org/gay.macgyver,0.9433086064,48.175.209.239,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n11611,4,258,2017-04-01 16:53:45,http://osinski.io/jewell,0.6565067381,210.193.243.147,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n11612,4,258,2017-06-09 16:20:40,http://botsford.com/jaylin_dietrich,0.4596350877,237.173.236.99,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n11613,4,258,2017-02-10 09:25:43,http://nienow.com/aaron,0.5896261969,38.225.12.154,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n11614,4,258,2017-01-22 01:47:21,http://rodriguez.net/demario,0.4315269652,211.242.142.25,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n11615,4,258,2017-01-20 01:18:17,http://keebler.org/elna.bahringer,0.3768888458,179.39.206.171,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n11616,4,258,2017-04-23 13:25:35,http://mante.com/adonis.koepp,0.8444580186,230.169.124.168,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n11617,4,258,2017-06-11 19:23:56,http://satterfield.io/julianne.hettinger,0.0588100454,155.157.6.5,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n11618,4,258,2017-02-20 05:27:41,http://funk.name/brandon_kovacek,0.0345742021,101.129.153.100,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n11619,4,258,2016-12-20 03:17:07,http://senger.io/otis,0.6624472002,16.121.189.29,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n11620,4,258,2017-03-25 08:21:22,http://stanton.name/eryn.herzog,0.0355706625,107.140.51.117,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n11621,4,258,2017-03-07 12:06:01,http://bartonbatz.org/bonita_wilderman,0.9233750255,145.53.238.166,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n11622,4,258,2017-03-21 15:06:10,http://kovacekhowell.co/alfred.schowalter,0.2415424299,86.134.106.196,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n11623,4,258,2017-06-05 06:34:36,http://frami.org/fannie,0.6530475413,52.145.157.112,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n11624,4,259,2017-01-04 14:39:02,http://medhurst.biz/dahlia.reichel,0.0177962369,21.80.41.186,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n11625,4,259,2017-04-29 09:43:45,http://harrisjohnston.co/izaiah_bosco,0.9534862279,98.179.241.82,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n11626,4,259,2017-01-24 01:52:38,http://walterkirlin.io/trystan_paucek,0.6879869612,146.6.114.150,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n11627,4,259,2017-06-06 14:24:47,http://kozeymaggio.io/sheila.batz,0.6485500657,83.118.52.254,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n11628,4,259,2017-05-15 06:53:08,http://daviscollier.info/katherine,0.2495910457,108.90.57.215,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n11629,4,259,2017-05-21 23:56:53,http://bradtke.info/amiya_barton,0.7595586095,44.35.198.217,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n11630,4,259,2017-05-28 01:40:03,http://haleyfritsch.biz/dannie.mcclure,0.3597333150,5.221.111.165,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n11631,4,259,2017-05-09 13:07:36,http://windler.name/phoebe.orn,0.1897607168,179.27.120.102,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n11632,4,259,2016-12-24 15:17:02,http://tremblayprice.co/gaylord,0.7199524437,98.175.232.164,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n11633,4,259,2017-06-10 18:18:10,http://zieme.net/alexander.haag,0.9213563285,185.219.144.53,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n11634,4,259,2017-03-29 13:14:31,http://herman.co/alverta,0.6432323453,95.87.137.196,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n11635,4,259,2017-01-15 08:44:32,http://hilpertschmeler.net/beulah,0.1990038265,56.50.11.43,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n11636,4,259,2017-02-23 17:35:05,http://lueilwitz.net/ellen,0.4356598925,223.5.23.118,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n11637,4,259,2017-02-11 06:35:41,http://hyattwiegand.name/nellie,0.1053231467,55.213.190.218,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n11638,4,259,2017-02-17 14:33:53,http://boyer.info/adan.rice,0.4535596475,51.174.159.137,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n11639,4,259,2017-02-20 11:35:39,http://schuppestark.io/armand.baumbach,0.5464048724,215.216.233.169,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n11640,4,259,2017-03-10 18:24:53,http://littel.co/barton,0.0000263975,195.95.43.124,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n11641,4,259,2017-03-23 18:49:31,http://hermistonhaley.com/saul.wilkinson,0.3037041812,218.95.204.105,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n11642,4,259,2017-04-25 07:38:32,http://thiel.io/donnell,0.6907806031,236.24.240.227,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n11643,4,259,2017-04-29 23:18:34,http://okunevawaters.io/maximo,0.1599861134,196.144.96.234,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n11644,4,259,2017-01-26 13:22:55,http://dicki.biz/rebeka,0.2692828993,251.76.220.192,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n11645,4,260,2017-04-28 01:07:12,http://schneider.co/elena,0.7493779905,81.181.214.186,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n11646,4,260,2017-04-30 07:58:36,http://gusikowski.name/lloyd,0.6218600175,218.159.90.144,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n11647,4,260,2017-01-20 21:42:03,http://heelfadel.net/favian.denesik,0.0388832374,47.2.237.18,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n8608,3,193,2017-04-06 11:49:43,http://hamillcorwin.info/daryl_ortiz,,244.114.96.230,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n8609,3,193,2017-03-01 05:38:18,http://wisoky.co/torrey_yost,,172.105.228.61,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n8610,3,193,2017-05-02 03:10:31,http://kreigerwalker.co/annabel_jacobi,,244.6.223.140,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n8611,3,193,2016-12-24 04:48:15,http://keelinglesch.info/rozella_thompson,,80.130.125.87,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n8612,3,193,2017-02-01 07:12:27,http://moriette.net/shawna_mante,,129.24.92.166,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n8613,3,193,2017-03-15 08:22:24,http://becker.co/elinore.bahringer,,12.66.139.194,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n8614,3,193,2017-05-26 15:31:43,http://mertz.info/wilfredo_mosciski,,96.140.220.237,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n8615,3,193,2017-02-19 20:14:27,http://marquardt.biz/rosalind,,200.232.107.86,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n8616,3,193,2017-02-08 04:31:10,http://strackejaskolski.name/michael,,113.30.199.122,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n8617,3,193,2017-05-06 19:09:29,http://crist.org/jazmyne.harris,,11.57.52.185,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n8618,3,193,2017-04-07 16:15:31,http://maggio.io/london,,197.195.42.163,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n8619,3,193,2017-02-10 00:36:35,http://abbott.com/rosetta,,47.217.247.75,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n8620,3,193,2017-06-10 08:57:16,http://rolfson.io/vaughn,,189.244.196.14,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n8621,3,193,2017-05-05 23:32:31,http://gorczany.io/orlo,,173.234.224.203,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n8622,3,193,2016-12-25 20:46:39,http://pacochahintz.biz/talia,,119.119.219.176,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n8623,3,193,2017-04-26 14:43:09,http://oconnerschamberger.org/lina,,186.9.234.129,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n8624,3,193,2017-05-21 21:08:19,http://gulgowskimetz.co/clifford,,12.128.231.203,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n8625,3,193,2017-02-14 09:26:34,http://morar.info/adolph,,58.111.129.120,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n8626,3,193,2017-06-07 14:57:32,http://toy.info/aryanna,,198.162.129.95,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n8627,3,193,2017-02-06 23:34:14,http://thiel.co/selmer_predovic,,18.175.244.198,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n8628,3,194,2017-04-09 16:22:22,http://hackett.org/lori,,153.9.24.101,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n8629,3,194,2016-12-22 04:01:28,http://buckridgekilback.info/maritza,,185.27.118.51,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n8630,3,194,2017-02-09 16:49:30,http://ward.info/bernice_oconnell,,150.13.135.9,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n8631,3,194,2017-05-25 10:48:25,http://ankundingbarton.com/webster,,8.75.170.32,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n8632,3,194,2017-04-10 18:10:35,http://gibson.info/abbigail,,165.188.230.231,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n8633,3,194,2017-05-25 10:15:31,http://dicki.co/aidan.bechtelar,,216.163.154.135,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n8634,3,194,2017-05-04 12:17:57,http://rempel.biz/annamarie_watsica,,217.5.112.248,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n8635,3,194,2017-02-06 00:23:59,http://rempel.net/quinten_larson,,76.38.21.116,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n8636,3,194,2017-03-16 06:25:39,http://spencerhammes.org/philip,,30.7.38.75,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n8637,3,194,2017-05-31 20:33:16,http://medhurst.com/gregg.grant,,203.38.71.77,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n8638,3,194,2017-03-20 14:58:31,http://dickinsonnienow.io/lauryn,,226.4.118.170,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n8639,3,194,2016-12-23 04:33:54,http://trompmante.net/marcel.roob,,195.31.173.217,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n8640,3,194,2017-06-11 17:11:44,http://mayert.co/kendall_parisian,,76.4.63.13,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n8641,3,194,2017-02-23 02:41:50,http://doylelebsack.name/jacquelyn,,159.177.61.111,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n8642,3,194,2017-02-23 00:43:57,http://jacobs.org/jesus,,49.205.8.174,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n8643,3,194,2017-04-11 22:07:32,http://haagheathcote.info/joshua,,76.238.119.12,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n8644,3,194,2017-05-25 13:51:52,http://konopelski.info/brayan.breitenberg,,243.129.211.168,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n8645,3,194,2017-04-04 10:45:36,http://roberts.net/crystal_aufderhar,,33.160.227.109,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n8646,3,194,2017-04-23 22:22:31,http://franeckimaggio.org/dorris.dooley,,199.212.60.46,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n8647,3,194,2017-02-26 06:23:59,http://grant.net/oral,,31.46.151.19,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n8648,3,194,2017-04-26 04:52:00,http://hudson.com/jose,,117.253.95.202,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n8649,3,194,2017-03-24 04:01:20,http://hahn.name/laurine,,111.28.142.92,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n8650,3,194,2017-04-20 09:12:34,http://bergnaumpfannerstill.name/leann,,44.178.162.197,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n8651,3,194,2017-06-04 07:17:42,http://ankunding.name/marc.reynolds,,58.153.106.61,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n8652,3,194,2016-12-21 06:50:27,http://waelchi.biz/arnoldo.boehm,,229.123.156.235,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n8653,3,194,2017-05-17 01:33:41,http://fisher.net/orval,,84.103.92.38,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n8654,3,194,2017-05-15 19:32:10,http://altenwerthhowell.info/benton,,35.135.213.154,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n8655,3,194,2017-05-19 08:12:47,http://heathcotelindgren.org/lisa,,250.119.53.18,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n8656,3,194,2016-12-20 09:17:01,http://hills.io/eloise,,247.230.173.247,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n8657,3,194,2017-05-26 11:20:30,http://krisreinger.biz/korbin_hackett,,41.13.228.96,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n8658,3,194,2017-03-10 16:57:01,http://wuckert.name/maxime,,113.205.171.204,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n8659,3,194,2017-01-26 22:48:54,http://doyle.name/elton,,152.29.221.13,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n8660,3,194,2017-04-25 18:53:49,http://mueller.info/jeie.mueller,,172.250.191.101,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n8661,3,194,2017-06-08 07:22:34,http://jones.net/olen_kaulke,,156.26.214.55,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n8662,3,194,2017-05-24 13:53:22,http://zemlakmacejkovic.info/roosevelt_hirthe,,27.185.149.50,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n14550,5,328,2017-02-10 02:51:42,http://champlinschowalter.biz/telly,0.4564437382,238.99.75.245,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n14551,5,328,2016-12-16 01:16:19,http://faybahringer.name/karli.waters,0.8144665863,93.187.14.231,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n14552,5,328,2017-03-29 17:31:55,http://harvey.info/alvena,0.9050428570,84.29.27.74,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n14553,5,328,2017-04-25 18:38:53,http://durganratke.com/sincere,0.4635440620,244.41.31.51,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n14554,5,328,2017-05-21 05:10:24,http://reingerabbott.co/ethyl,0.1638787548,188.248.163.143,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n14555,5,328,2017-06-02 21:25:58,http://baileyschoen.org/name.braun,0.9053264796,243.4.176.34,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n14556,5,328,2017-01-04 13:38:00,http://bartell.org/carlotta.boyle,0.7439635686,162.229.27.86,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n14557,5,328,2017-05-18 00:06:34,http://wittingdickens.net/eloise,0.7697618414,157.63.209.18,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n14558,5,328,2017-03-07 04:12:53,http://hansen.io/rolando,0.6233004508,220.32.50.133,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n14559,5,328,2017-01-18 14:24:38,http://boyer.biz/carmela,0.9226546899,136.205.254.253,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n14560,5,328,2017-02-01 06:37:22,http://stiedemannhamill.net/sabina,0.5646388168,59.180.154.246,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n14561,5,328,2016-12-22 23:10:12,http://schmeler.co/myrtie,0.1284955313,166.125.82.133,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n14562,5,328,2017-01-21 21:39:28,http://fahey.co/janelle_heidenreich,0.5910432435,105.15.5.228,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n14563,5,328,2017-02-04 10:53:43,http://hudsonbartell.io/bennie_kuhic,0.7343763449,165.143.28.172,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n14564,5,328,2017-02-28 18:15:05,http://kunzechamplin.info/urban,0.5479244681,213.139.140.231,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n14565,5,328,2017-01-04 12:34:12,http://marvinlittle.biz/roberta,0.8203340665,154.245.207.46,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n14566,5,328,2017-02-11 00:19:36,http://flatley.net/montana,0.3156031208,240.163.25.105,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n14567,5,328,2017-04-17 03:45:33,http://schinner.org/alana.pfannerstill,0.1735591024,169.71.105.103,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n14568,5,328,2017-01-18 05:45:50,http://mayervon.org/axel,0.4487964975,101.99.13.123,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n14569,5,328,2017-04-05 22:50:13,http://mann.com/bart.ruecker,0.4113343047,88.135.48.85,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n14570,5,328,2017-03-29 13:42:35,http://brakus.name/justus.armstrong,0.9561056189,87.153.163.195,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n14571,5,328,2017-03-30 18:28:41,http://moore.org/pierce_nolan,0.2376822097,191.156.122.224,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n14572,5,328,2017-06-01 12:37:29,http://purdykeeling.name/antonio,0.9663228153,18.111.43.200,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n14573,5,328,2017-05-31 10:54:51,http://langworthdickinson.co/erick_west,0.2667171005,112.87.153.95,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n14574,5,328,2017-05-17 19:31:18,http://koelpin.info/marcos_schroeder,0.6920374386,76.173.23.135,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n14575,5,328,2017-01-23 21:27:10,http://hammes.biz/rey_ernser,0.7693724317,97.209.190.228,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n14576,5,328,2017-03-22 17:39:03,http://botsford.org/corene_anderson,0.2808732964,47.4.47.161,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n14577,5,328,2016-12-19 10:06:26,http://bednarmills.com/brice_west,0.1004768496,139.228.61.168,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n14578,5,328,2016-12-20 12:07:27,http://wolff.org/rosalee.kuphal,0.3223591949,5.244.61.52,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n14579,5,328,2017-01-24 07:35:01,http://thompson.io/boris,0.3108516435,2.172.63.126,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n14580,5,328,2017-06-02 08:51:34,http://hackett.org/kallie_rolfson,0.1337023238,240.100.104.125,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n14581,5,328,2017-04-08 00:59:18,http://pollicheffertz.info/cecilia,0.0460379950,197.170.217.114,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n14582,5,328,2017-01-05 19:58:41,http://davis.net/taylor.moen,0.0830697353,175.232.132.229,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n14583,5,328,2016-12-24 06:39:32,http://haleyrodriguez.co/hellen,0.4383022794,115.127.25.240,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n14584,5,328,2017-01-26 23:30:30,http://abshire.co/kira_denesik,0.6178580243,36.91.58.174,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n14585,5,328,2017-04-14 04:30:05,http://jacobsonfay.org/justina.bailey,0.4496018982,145.195.143.146,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n14586,5,328,2017-01-07 01:20:24,http://wintheiser.net/susanna,0.6628999980,190.148.50.243,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n14587,5,328,2017-02-12 09:43:56,http://faheyhahn.org/payton_crona,0.2576172604,151.104.240.45,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n14588,5,328,2017-01-18 06:22:50,http://gleichnerhermann.org/wyman,0.3478715874,143.232.222.142,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n14589,5,328,2017-03-24 18:27:33,http://gutkowski.org/janelle,0.2064236482,93.176.157.123,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n14590,5,328,2016-12-17 20:59:25,http://fritsch.biz/maymie_howe,0.5018278703,159.148.33.8,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n14591,5,328,2017-03-30 20:58:14,http://heathcote.info/antonetta_becker,0.0986687737,17.27.86.250,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n14592,5,328,2017-06-06 15:12:29,http://hodkiewicz.com/roie,0.5597413525,120.207.46.215,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n14593,5,328,2017-05-05 15:06:16,http://weinatreichert.biz/jules.orn,0.7505744551,24.147.187.218,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n14594,5,328,2017-05-06 08:16:39,http://bogisich.co/aleen,0.5259781773,160.90.76.239,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n14595,5,328,2017-06-05 20:18:38,http://medhurst.io/jayden,0.0105361749,103.80.196.225,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n14596,5,328,2017-04-05 05:04:05,http://zulauf.info/weston.sauer,0.4113027299,96.124.104.116,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n14597,5,328,2017-02-18 00:32:43,http://naderlittle.info/leonie,0.4342690384,162.108.89.24,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n14598,5,328,2016-12-31 05:27:19,http://oconnell.biz/rashawn,0.7563748103,108.189.169.22,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n14599,5,328,2016-12-29 06:36:27,http://bahringerprosacco.net/daphney.breitenberg,0.6584530050,71.104.136.102,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n14600,5,328,2017-04-25 12:01:30,http://grady.com/conner.gutmann,0.3775506885,14.245.4.48,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n17540,6,395,2017-01-11 12:40:46,http://sipesheidenreich.net/jermain,,233.59.47.144,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n17541,6,396,2017-03-12 00:39:29,http://boehm.co/simone,0.8269868062,98.34.148.174,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n17542,6,396,2017-06-02 11:46:14,http://hauckrowe.biz/dora,0.1287436370,39.72.93.43,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n17543,6,396,2017-02-05 13:50:21,http://koeppmraz.com/triston.witting,0.9989360957,221.143.2.8,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n17544,6,396,2017-04-02 02:59:06,http://nienowfadel.net/jaylan,0.7274698604,103.109.207.52,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n17545,6,396,2017-03-18 08:30:17,http://legrotark.com/gayle_kreiger,0.8832576629,21.177.201.237,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n17546,6,396,2017-02-16 01:46:00,http://pagac.net/sherwood,0.6266033082,38.100.4.144,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n17547,6,396,2017-02-12 12:11:38,http://hegmann.net/erling_keeling,0.6054624820,39.193.230.183,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n17548,6,396,2017-04-03 08:28:17,http://danielgleason.io/jimmie_adams,0.2960118179,4.221.112.116,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n17549,6,396,2017-04-27 10:17:08,http://oconner.name/friedrich,0.2195330101,40.211.177.154,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n17550,6,396,2017-03-01 10:35:18,http://effertz.biz/alize,0.5476580971,244.37.66.215,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n17551,6,396,2017-04-20 01:47:53,http://murphy.biz/myron,0.0998926812,231.196.211.121,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n17552,6,396,2017-03-15 04:48:52,http://glover.net/jannie,0.8076755873,123.118.85.48,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n17553,6,396,2017-04-02 08:26:32,http://nienow.info/audrey_lemke,0.0901234288,34.193.76.232,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n17554,6,396,2017-05-13 13:53:22,http://orn.org/raleigh,0.7275300223,218.17.201.124,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n17555,6,396,2016-12-18 15:31:16,http://stamm.org/marcelina,0.3826953955,242.137.42.245,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n17556,6,396,2017-02-10 00:18:19,http://weinat.com/margarete_predovic,0.3508230748,9.209.224.199,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n17557,6,396,2017-01-14 15:18:56,http://baumbachwaelchi.io/deborah.zieme,0.9444982064,44.15.202.195,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n17558,6,396,2017-04-27 06:02:24,http://nienow.org/meghan.prohaska,0.5736051446,122.25.214.236,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n17559,6,396,2017-04-24 04:14:16,http://bauch.co/jovany,0.4397971580,190.111.165.135,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n17560,6,396,2017-01-25 01:19:31,http://paucekbergstrom.info/camron,0.2104795038,124.211.114.214,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n17561,6,396,2017-03-27 08:27:12,http://emard.net/candido.bradtke,0.3269063860,218.125.44.116,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n17562,6,396,2017-02-21 12:31:27,http://flatley.name/brooklyn_aufderhar,0.1889129187,120.248.170.112,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n17563,6,396,2017-04-29 14:36:23,http://dickinsonmcclure.name/mayra.hettinger,0.1130581088,20.95.83.137,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n17564,6,396,2017-01-17 07:03:45,http://romaguera.info/amos,0.1885866871,115.173.153.251,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n17565,6,396,2017-05-17 09:47:48,http://tillmangoyette.io/garett,0.2159858433,133.33.140.34,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n17566,6,396,2017-06-07 14:30:19,http://haag.io/myrtis_gaylord,0.5163346656,86.253.127.97,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n17567,6,396,2017-02-24 17:55:30,http://lednerjohnson.io/ada_treutel,0.4617176277,189.74.144.213,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n17568,6,396,2017-05-13 22:23:55,http://lesch.org/violette_cruickshank,0.7161465196,214.241.238.168,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n17569,6,396,2017-05-07 03:01:14,http://hagenesbecker.name/rafael,0.5768192243,234.142.118.248,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n17570,6,396,2017-03-18 06:16:30,http://bednar.co/leo.mayer,0.5898919153,179.117.95.192,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n17571,6,396,2017-02-19 18:08:35,http://andersongraham.com/bella.corkery,0.2893625288,227.61.142.252,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n17572,6,396,2017-05-10 10:08:33,http://kerlukeleuschke.info/kyler_morar,0.1993902074,57.186.74.80,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n17573,6,396,2017-03-02 06:39:19,http://grady.com/noel_macejkovic,0.7700011373,97.250.97.45,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n17574,6,396,2016-12-28 10:27:17,http://dietrichdickinson.co/jaeden,0.7302575102,103.126.58.216,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n17575,6,396,2017-01-02 08:53:40,http://thiel.io/pansy,0.8108761572,229.97.54.190,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n17576,6,396,2017-02-13 02:09:51,http://medhurstroob.biz/daryl,0.8360931837,10.214.253.104,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n17577,6,396,2017-02-26 19:44:42,http://mccullough.biz/malcolm,0.4005772536,79.83.40.156,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n17578,6,396,2017-01-04 21:43:00,http://goyette.co/jaden_robel,0.0306393692,151.202.118.241,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n17579,6,396,2017-06-14 00:29:14,http://sauermaggio.biz/anibal.cole,0.0219520194,223.253.123.98,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n17580,6,396,2017-06-03 02:52:54,http://glover.io/miouri_rice,0.4586698112,169.68.71.57,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n17581,6,396,2017-02-17 14:58:07,http://runolfsdottirwelch.info/holly.gibson,0.0422500020,204.166.123.67,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n17582,6,396,2017-02-26 23:45:24,http://wilkinson.name/rogelio,0.3673508294,176.131.191.108,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n17583,6,396,2017-03-25 05:53:41,http://klocko.io/aileen.hoeger,0.4753547134,25.252.62.168,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n17584,6,396,2017-05-12 11:34:05,http://zboncak.info/ryann,0.4612686158,30.167.97.100,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n17585,6,396,2016-12-31 18:13:22,http://block.co/samson_keler,0.3283788142,20.230.66.252,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n17586,6,396,2017-06-04 10:30:27,http://grady.org/natalie,0.6372258680,15.5.176.186,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n17587,6,396,2016-12-20 01:23:42,http://koelpinwunsch.co/dillan,0.9163598667,197.154.4.249,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n17588,6,396,2017-05-27 14:07:43,http://ferry.info/jazmyne,0.0018496331,27.79.38.73,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n17589,6,396,2017-03-01 14:29:32,http://rennerdach.io/kiarra,0.9708681463,138.92.99.187,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n17590,6,396,2017-01-22 16:02:39,http://gaylordfriesen.name/henriette,0.4278752143,151.37.200.35,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n4725,2,104,2017-02-11 18:49:59,http://kuhic.co/bernita.kautzer,,18.226.164.119,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n4726,2,105,2017-02-13 09:45:13,http://mannwelch.net/lydia,,87.180.190.9,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n4727,2,105,2017-06-04 09:25:40,http://wolff.name/ansley,,224.202.127.154,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n4728,2,105,2017-05-24 20:33:35,http://zboncak.biz/declan.auer,,246.206.86.102,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n4729,2,105,2016-12-18 12:25:10,http://gleichner.biz/ari.rempel,,47.170.3.174,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n4730,2,105,2017-06-07 10:41:44,http://bahringer.name/lonnie,,27.183.209.123,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n4731,2,105,2017-04-15 08:42:59,http://dickiwuckert.com/andreane,,165.111.80.205,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n4732,2,105,2017-01-09 01:11:57,http://hagenes.com/edwina.kuvalis,,16.55.132.222,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n4733,2,105,2016-12-24 19:33:43,http://treutel.info/berneice.denesik,,201.172.113.31,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n4734,2,105,2017-03-16 20:15:42,http://dibbert.co/eula,,178.65.108.16,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n4735,2,105,2017-01-27 20:59:11,http://sanfordbeer.com/beth_will,,73.82.203.73,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n4736,2,105,2016-12-31 23:52:30,http://lemkegerlach.biz/joanny,,38.22.118.117,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n4737,2,105,2017-03-01 04:51:08,http://leuschke.biz/dewitt,,93.73.187.153,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n4738,2,105,2017-01-05 19:46:34,http://reichertlubowitz.biz/daisy_bashirian,,179.17.30.157,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n4739,2,105,2017-03-15 03:26:41,http://franecki.co/gardner.zboncak,,7.167.110.58,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n4740,2,105,2017-03-22 05:36:29,http://hagenes.co/tiffany,,16.44.10.195,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n4741,2,105,2017-03-25 09:54:44,http://kuhnreichert.net/monroe.barton,,22.212.166.136,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n4742,2,105,2017-01-08 22:41:18,http://dietrichstark.co/terry,,30.171.61.107,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n4743,2,105,2016-12-28 16:39:36,http://wisoky.com/jaylon.kuvalis,,170.50.24.250,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n4744,2,105,2017-06-05 05:29:53,http://hartmann.co/andrew.kilback,,9.242.164.166,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n4745,2,105,2017-05-16 03:06:24,http://goodwinfay.co/bonita_leffler,,240.104.131.73,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n4746,2,105,2017-01-26 12:26:27,http://cummingsgutkowski.io/sadie.dibbert,,220.175.250.231,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n4747,2,105,2017-02-20 08:00:03,http://ziemedeckow.net/teie.runolfsdottir,,63.100.11.92,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n4748,2,105,2017-03-21 13:26:57,http://mullerwaelchi.net/georgette,,20.9.83.31,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4749,2,105,2017-02-13 03:56:21,http://ernsereffertz.biz/viva,,109.134.234.146,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n4750,2,105,2017-02-16 21:00:30,http://moenmoriette.org/jamal.satterfield,,241.254.204.33,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n4751,2,105,2017-06-03 21:00:07,http://greenholt.com/kaylee,,123.234.253.13,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n4752,2,105,2017-03-27 21:27:22,http://lowe.co/jaunita.sauer,,67.94.88.217,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n4753,2,105,2017-03-20 05:14:13,http://gerlach.io/vincenzo,,204.155.217.229,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n4754,2,105,2017-02-28 04:45:46,http://johnson.name/timmy_zboncak,,62.215.247.198,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n4755,2,105,2016-12-21 21:02:49,http://stamm.com/emmalee,,152.174.252.18,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n4756,2,105,2017-03-03 17:23:04,http://runte.org/catharine.corwin,,168.17.138.161,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n4757,2,105,2017-01-17 13:49:42,http://brakusheathcote.org/libby,,171.107.184.67,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n4758,2,105,2017-04-28 03:19:46,http://langworthjacobi.net/vivian,,36.135.24.68,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n4759,2,105,2017-02-08 19:06:44,http://schoen.name/marquise,,196.50.20.60,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n4760,2,105,2017-02-24 08:48:33,http://cummerata.io/demetrius,,235.195.69.129,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n4761,2,105,2016-12-24 13:30:08,http://okon.info/jee_heathcote,,226.104.9.77,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n4762,2,105,2017-04-06 13:36:09,http://moen.info/leann.keebler,,98.89.173.231,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n4763,2,105,2016-12-25 04:51:10,http://keeling.info/rickey.denesik,,82.73.14.250,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n4764,2,105,2017-05-19 16:47:59,http://brakus.io/vicente_bogan,,169.98.59.197,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4765,2,105,2017-01-11 13:00:23,http://carrollokeefe.net/rowena.hammes,,71.11.53.138,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n4766,2,105,2017-05-12 19:24:05,http://denesik.net/jeanne,,117.221.86.171,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n4767,2,105,2017-01-24 04:46:10,http://quigley.com/jerrod,,98.207.186.178,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n4768,2,105,2017-04-21 19:00:35,http://dubuquekoch.biz/ottilie.bashirian,,243.6.237.215,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n4769,2,105,2017-02-25 15:02:30,http://carroll.info/myrna_okon,,156.16.32.32,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n4770,2,105,2017-03-07 01:17:33,http://ritchieheel.biz/jesus_bins,,78.237.132.95,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n4771,2,105,2017-01-14 19:30:19,http://cruickshank.io/edison,,100.56.36.195,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n4772,2,105,2017-04-27 05:38:51,http://stracke.name/christ_lindgren,,235.134.139.62,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4773,2,105,2017-05-21 20:52:50,http://quitzon.net/faustino,,74.203.79.40,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n4774,2,105,2017-06-14 00:50:19,http://jakubowski.org/baron_romaguera,,48.52.163.85,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n4775,2,105,2017-01-24 01:08:47,http://schimmel.info/richmond.heaney,,115.252.203.87,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n4776,2,105,2017-06-05 05:02:59,http://mrazbogisich.net/amiya_ankunding,,230.221.40.64,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n4777,2,105,2017-03-03 00:54:33,http://doyle.co/hallie,,196.99.126.84,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n4778,2,105,2017-06-02 22:51:29,http://jasthudson.co/kirstin,,179.23.5.162,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n4779,2,105,2017-04-13 06:40:48,http://larkin.io/dayana.altenwerth,,241.94.140.245,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n11648,4,260,2017-03-14 16:17:25,http://champlinblock.info/eulalia_rolfson,0.3272726382,40.38.189.208,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n11649,4,260,2017-02-14 06:01:44,http://nienowjohnson.io/taryn.weimann,0.3817416203,216.93.33.134,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n11650,4,260,2017-05-30 13:54:15,http://eichmann.name/davin.nolan,0.4123957080,73.243.131.254,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n11651,4,260,2017-05-04 05:36:39,http://trantow.org/ivah.steuber,0.9488949032,240.188.88.249,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n11652,4,260,2017-04-16 17:32:15,http://nolan.name/sibyl,0.5216739081,172.215.15.107,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n11653,4,260,2017-02-15 06:08:02,http://marquardt.com/mina,0.4641678635,137.190.251.195,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n11654,4,260,2017-02-28 14:23:33,http://stokes.biz/trever_rempel,0.4971366393,156.215.229.211,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n11655,4,260,2017-04-13 15:44:08,http://hamill.name/eduardo,0.4359254520,110.179.30.64,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n11656,4,260,2017-01-02 18:55:04,http://dach.org/lelia.waters,0.9435299803,45.198.96.175,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n11657,4,260,2017-05-30 19:52:14,http://krajcikdurgan.co/percival.hane,0.7594818592,234.227.180.203,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n11658,4,260,2017-04-03 16:46:04,http://donnellymertz.org/willis.wintheiser,0.5693078824,12.147.228.167,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n11659,4,260,2017-05-09 11:12:38,http://bashirian.biz/raven_kulas,0.8178909588,56.85.227.80,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n11660,4,260,2017-02-28 22:23:11,http://zboncak.biz/jamal,0.6524296656,183.230.171.25,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n11661,4,260,2017-02-07 18:05:54,http://haley.io/mack_paucek,0.2935040997,233.49.232.58,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n11662,4,260,2016-12-23 10:36:24,http://bechtelar.org/edgardo,0.6496016616,215.211.175.137,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n11663,4,260,2017-06-10 09:39:43,http://marvinbosco.co/mozell.will,0.7255222658,229.19.78.34,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n11664,4,260,2017-04-21 09:24:03,http://williamsonbashirian.name/okey,0.3221773580,217.138.83.36,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n11665,4,260,2017-04-19 04:20:25,http://reilly.net/jackson.von,0.9454124798,248.184.69.34,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n11666,4,260,2017-05-30 10:10:55,http://tromprunolfon.name/dereck.beatty,0.4761137122,97.243.128.135,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n11667,4,260,2017-02-12 21:55:13,http://nader.net/marques.bradtke,0.2157249709,66.90.203.59,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n11668,4,260,2017-01-23 11:47:07,http://turcottefritsch.info/gracie.casper,0.4265939720,253.4.106.114,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n11669,4,260,2017-01-31 03:01:16,http://schmeler.biz/carrie_mayert,0.0068299564,116.90.127.62,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n11670,4,260,2017-04-09 00:18:14,http://ward.org/joany,0.6263898908,15.81.61.163,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n11671,4,260,2017-04-28 21:20:26,http://glover.org/axel_senger,0.3976075114,238.165.226.135,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n11672,4,260,2017-02-15 02:13:49,http://moenkshlerin.io/kathleen,0.8947703055,138.41.89.29,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n11673,4,260,2017-02-23 16:55:40,http://morar.biz/janie_oberbrunner,0.4361175739,18.121.163.235,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n11674,4,260,2017-02-12 13:49:27,http://kubschuster.com/julianne,0.8028296976,173.208.153.90,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n11675,4,260,2016-12-26 01:42:59,http://wyman.com/cameron,0.6421118716,41.97.158.172,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n11676,4,260,2016-12-14 06:45:39,http://hyattlang.com/robbie.tromp,0.6877885955,59.64.135.77,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n11677,4,260,2017-05-26 13:41:01,http://parker.io/otho_ortiz,0.7328938826,175.177.248.62,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n11678,4,260,2016-12-15 03:31:53,http://terrylittel.org/denis_kovacek,0.1163061350,87.47.27.77,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n11679,4,260,2017-01-07 15:11:59,http://little.info/adrian,0.9907061139,34.108.152.13,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n11680,4,260,2017-03-25 20:12:57,http://kemmer.org/ed.lubowitz,0.9732950687,179.88.20.25,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n11681,4,260,2017-04-01 15:13:27,http://flatley.biz/marcelino_hermiston,0.3283120356,243.184.44.103,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n11682,4,260,2017-04-10 09:22:32,http://willmsprohaska.com/emmalee.baumbach,0.8493050746,35.87.245.143,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n11683,4,260,2017-03-10 20:11:32,http://windler.io/delaney,0.1472668234,239.231.85.213,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n11684,4,260,2017-03-04 17:53:50,http://hickleprice.org/raul,0.9237036914,206.21.162.52,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n11685,4,260,2016-12-23 04:12:41,http://yundt.info/justice.quitzon,0.0531579867,146.62.75.48,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n11686,4,260,2017-03-13 16:17:57,http://boyer.com/anna.hamill,0.0168632713,222.159.71.49,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n11687,4,260,2017-03-26 01:59:08,http://baumbach.co/clint.veum,0.9262249759,64.132.242.69,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n11688,4,260,2017-04-01 12:09:07,http://bogan.co/alfonso.king,0.9421169881,64.50.241.216,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n11689,4,260,2017-03-15 05:38:23,http://frami.biz/emery_grant,0.5555718748,119.245.138.195,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n11690,4,260,2016-12-26 06:07:06,http://sawayn.biz/krystina_schmidt,0.1754370518,137.19.132.206,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n11691,4,261,2017-04-13 06:40:07,http://beerspinka.biz/allene_kling,0.1478201037,181.108.88.204,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n11692,4,261,2016-12-24 17:57:18,http://considine.com/giovanna,0.1218645865,185.97.32.129,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n11693,4,261,2017-01-20 23:51:52,http://glover.io/yvonne_bayer,0.4055639652,127.51.180.25,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n11694,4,261,2017-01-04 16:40:59,http://pacocha.io/adelia,0.6598896915,162.83.151.157,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n11695,4,261,2017-04-17 04:45:30,http://kozeyroob.io/oceane,0.9225986431,166.234.238.193,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n11696,4,261,2017-04-22 06:49:42,http://mcclure.com/walter,0.4599166333,95.52.140.244,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n11697,4,261,2017-03-24 17:38:47,http://jacobson.org/eleanora_connelly,0.2231418106,97.243.27.252,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n11698,4,261,2017-01-06 09:25:25,http://kaulke.co/titus,0.2656916940,26.74.53.135,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n8663,3,194,2017-03-25 13:36:11,http://stantondonnelly.co/mertie,,51.105.122.249,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n8664,3,194,2017-01-21 08:43:57,http://hamilltremblay.net/golda,,88.26.98.128,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n8665,3,194,2017-04-19 17:01:27,http://treutel.net/verona.barton,,153.138.206.177,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n8666,3,194,2016-12-14 19:17:44,http://goldner.org/dariana_klein,,191.104.119.67,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n8667,3,194,2016-12-24 16:04:54,http://wisokyabbott.io/noble_dickinson,,135.48.103.147,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n8668,3,194,2017-05-14 05:03:41,http://strosin.name/rickey_walter,,27.232.100.161,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n8669,3,194,2017-03-09 04:44:32,http://kovacek.org/olin,,216.99.176.253,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n8670,3,194,2017-04-20 02:04:53,http://hermann.info/terrill,,31.241.84.113,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n8671,3,194,2017-03-16 05:18:13,http://wymanwiegand.info/maximus,,98.204.181.94,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n8672,3,194,2017-06-05 17:34:08,http://rice.biz/chloe.lemke,,197.115.235.120,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n8673,3,194,2016-12-20 19:22:16,http://padberg.org/laria,,49.107.73.14,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n8674,3,194,2016-12-30 19:56:37,http://dickenskiehn.biz/anabelle,,79.84.189.149,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n8675,3,194,2017-04-24 07:41:02,http://gutkowski.co/dallas_nikolaus,,87.226.133.161,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n8676,3,194,2017-03-21 12:07:33,http://bednar.org/anderson.grant,,173.219.136.211,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n8677,3,194,2016-12-15 23:54:34,http://renner.com/rashawn_mueller,,4.51.96.198,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n8678,3,194,2017-04-03 09:42:28,http://jacobson.name/eduardo.shields,,215.214.37.159,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n8679,3,194,2017-03-21 20:21:05,http://dickinsonfadel.info/daron,,127.195.208.44,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n8680,3,194,2017-06-01 14:29:46,http://johns.co/cristobal,,124.108.77.57,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n8681,3,194,2017-01-31 12:14:50,http://hoppelittel.biz/deja,,150.7.14.118,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n8682,3,194,2017-03-26 23:06:37,http://gusikowskipurdy.biz/melba,,169.143.21.8,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n14601,5,328,2016-12-14 13:57:17,http://bosco.com/aurore,0.4938302456,27.197.2.81,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n14602,5,328,2017-04-24 11:58:56,http://shanahan.info/tod.keeling,0.2304215410,215.103.163.53,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n14603,5,328,2017-03-09 09:37:32,http://stroman.net/dayna.okuneva,0.0467347543,254.217.112.3,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n14604,5,329,2017-02-06 05:54:34,http://osinski.co/elmer,0.2979140030,148.230.111.224,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n14605,5,329,2016-12-20 11:27:36,http://mraz.co/marge,0.3054259430,95.75.115.77,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n14606,5,329,2017-03-02 11:10:03,http://lehner.biz/darius_donnelly,0.3647384217,189.98.231.233,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n14607,5,329,2017-03-26 14:17:59,http://erdman.biz/roosevelt.gaylord,0.1889105790,201.233.156.66,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n14608,5,329,2016-12-21 09:01:03,http://mitchellhuel.net/deion,0.4946846992,67.92.143.3,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n14609,5,329,2017-06-07 07:30:21,http://fay.info/aurelie_block,0.4799849593,89.219.184.217,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n14610,5,329,2017-05-31 06:53:50,http://kuvalisolson.name/bud_bailey,0.8538804683,30.214.99.5,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n14611,5,329,2017-01-12 23:08:50,http://dach.biz/kaylin.bogisich,0.8437003064,219.61.150.183,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n14612,5,329,2017-06-12 15:48:47,http://rippin.co/hillary,0.4205839004,44.168.247.165,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n14613,5,329,2017-01-06 23:39:08,http://roberts.net/martine,0.0462446851,148.169.154.186,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n14614,5,329,2017-03-11 22:32:54,http://green.org/shanelle.emmerich,0.5892924791,150.58.168.74,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n14615,5,329,2017-05-30 08:28:04,http://feest.biz/else.schamberger,0.9458818277,184.233.71.191,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n14616,5,329,2017-02-06 11:51:20,http://hermiston.io/rosalyn.strosin,0.1289847067,160.3.119.168,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n14617,5,329,2017-01-22 01:45:07,http://mclaughlin.com/rasheed,0.7935942403,237.225.97.199,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n14618,5,329,2017-05-13 20:37:11,http://spinkacollier.net/marc,0.9908252653,67.144.131.133,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n14619,5,329,2017-03-20 21:03:09,http://fisher.com/cristina_schumm,0.2818792953,65.204.119.105,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n14620,5,329,2017-04-05 00:30:30,http://ernser.net/maida.schaefer,0.8133036569,121.33.164.155,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n14621,5,329,2017-05-28 16:40:14,http://brownhoppe.com/joanne_smith,0.0530127776,210.214.206.202,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n14622,5,329,2016-12-16 00:47:25,http://ziemann.io/adrienne.jacobson,0.6533413975,59.70.12.144,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n14623,5,329,2017-05-25 03:41:36,http://mullerlittel.name/helmer,0.2907618692,21.253.111.169,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n14624,5,329,2017-06-05 17:17:49,http://doyle.biz/tania_roob,0.6982486765,191.22.74.30,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n14625,5,329,2017-04-06 16:36:26,http://eichmann.name/ramona,0.2042484255,36.226.23.80,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n14626,5,329,2017-04-18 03:42:56,http://gislasongerhold.co/francisca,0.6238910704,121.199.233.125,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n14627,5,329,2017-03-02 10:52:02,http://ohara.name/meaghan,0.1325450670,167.115.248.20,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n14628,5,329,2017-04-17 03:00:38,http://rosenbaum.info/gennaro,0.3921923502,66.194.77.121,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n14629,5,329,2017-01-01 20:54:00,http://klingkeler.name/ivory,0.5957394266,230.57.19.208,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n14630,5,329,2016-12-26 15:27:18,http://yundt.co/gregoria_kuvalis,0.8670897939,202.189.70.149,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n14631,5,329,2017-04-11 03:02:25,http://rath.net/domenick,0.5890477930,87.29.236.37,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n14632,5,329,2017-01-03 01:02:32,http://schmeler.biz/sarina,0.0049859855,85.61.137.147,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n14633,5,329,2017-02-06 08:07:26,http://herzoggleason.io/pearline,0.1050722482,175.83.182.5,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n14634,5,329,2017-03-27 00:26:35,http://davisolson.info/milo,0.6648797344,172.216.249.131,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n14635,5,329,2017-02-25 17:27:56,http://bartoletti.biz/elisha,0.9289754118,159.117.78.66,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n14636,5,329,2017-05-27 04:06:29,http://smitham.org/alison,0.8280837570,119.239.192.113,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n14637,5,329,2017-02-25 21:28:01,http://johns.info/gerard.rempel,0.0160202248,199.118.30.123,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n14638,5,329,2017-06-13 14:52:16,http://wolff.name/bradford.abshire,0.9736689961,89.91.186.114,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n14639,5,329,2017-02-20 03:58:45,http://howedibbert.net/holden_pfannerstill,0.6514958504,117.22.187.66,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n14640,5,329,2017-03-16 21:35:19,http://parisian.info/ubaldo.morar,0.5754576702,211.125.54.134,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n14641,5,329,2017-04-03 11:06:03,http://heathcoteward.org/shayna_morar,0.6333359885,147.43.4.196,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n14642,5,329,2016-12-29 13:21:17,http://wyman.io/tyrel_hand,0.6766201484,238.33.213.117,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n14643,5,329,2017-03-18 07:38:00,http://roweluettgen.com/bettie.lowe,0.5771449745,223.235.52.253,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n14644,5,329,2017-04-11 13:46:42,http://boyledeckow.biz/dedrick_botsford,0.6200701712,242.25.252.244,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n14645,5,329,2017-04-06 16:45:13,http://whitethompson.org/ima,0.1139048706,113.120.216.11,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n14646,5,330,2017-05-28 06:34:21,http://vonruedencronin.biz/edgardo,0.0272455763,91.6.196.94,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n14647,5,330,2017-02-18 03:19:20,http://koch.com/josh.lesch,0.7627253588,148.133.28.180,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n14648,5,330,2017-02-15 03:32:32,http://harvey.name/aurore,0.3293171884,107.55.38.234,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n14649,5,330,2016-12-14 02:56:35,http://flatley.info/savanna.blick,0.3873135315,252.59.157.84,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n14650,5,330,2016-12-14 14:51:04,http://boscodouglas.org/joy_hills,0.3507887284,18.149.156.121,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n14651,5,330,2017-03-30 06:25:26,http://satterfieldheel.net/darrell_nader,0.4826571415,214.192.242.91,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n17591,6,396,2017-01-31 19:40:07,http://schustergreenfelder.info/louvenia.carter,0.1721131881,224.218.148.107,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n17592,6,396,2017-04-29 10:17:19,http://kertzmann.name/idell,0.8515397491,54.75.151.11,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n17593,6,396,2017-02-02 19:22:29,http://senger.biz/yasmin,0.2986219903,230.100.237.89,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n17594,6,396,2017-03-28 22:50:07,http://larson.org/delphia,0.3436174776,227.6.92.88,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n17595,6,396,2017-01-07 16:35:49,http://jonesmurphy.co/reed_dietrich,0.8450530722,96.115.195.74,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n17596,6,396,2017-05-26 05:27:47,http://cummingsmoen.co/dallin,0.5656749579,40.251.200.158,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n17597,6,396,2017-02-03 11:02:04,http://gusikowski.co/kameron.mante,0.6265438864,6.76.118.13,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n17598,6,396,2017-05-10 12:04:20,http://fadel.co/malachi.gerhold,0.6307204184,45.127.97.218,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n17599,6,396,2017-04-16 20:42:03,http://armstrong.net/dorothy,0.7735923543,73.164.183.247,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n17600,6,396,2017-05-04 18:14:26,http://borer.info/salvatore_morar,0.8089752299,74.178.75.171,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n17601,6,396,2016-12-30 21:57:42,http://heel.org/neha_flatley,0.1390659300,147.114.234.122,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n17602,6,396,2017-05-20 16:13:08,http://jakubowski.net/bobbie,0.8225662682,59.133.252.234,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n17603,6,396,2017-03-22 02:29:41,http://cummingsbechtelar.io/jarrett_leuschke,0.4957458554,50.126.126.143,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n17604,6,396,2017-01-02 07:35:03,http://pagac.co/vergie.grant,0.4954213593,224.36.29.212,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n17605,6,396,2017-04-05 22:24:58,http://hoegerhansen.org/shaun_kuhic,0.4340931131,187.124.39.227,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n17606,6,396,2017-02-06 07:58:06,http://rohan.info/grady,0.8681269933,126.69.8.81,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n17607,6,396,2017-03-18 19:38:33,http://runolfsdottirpredovic.info/jermain_lindgren,0.9319994204,163.10.10.133,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n17608,6,396,2017-04-10 00:54:49,http://wuckertbednar.io/norris,0.0945757170,185.18.187.128,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n17609,6,396,2017-02-03 11:37:41,http://rau.name/mina,0.8454116393,233.34.219.73,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n17610,6,396,2017-02-04 20:54:52,http://cruickshank.co/ashly,0.1057485600,253.152.156.67,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n17611,6,397,2017-04-13 10:38:13,http://marquardtruel.co/wilhelm,0.5247291643,241.21.218.190,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n17612,6,397,2017-01-06 14:29:41,http://schroeder.co/muriel,0.0206358151,101.204.165.73,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n17613,6,397,2017-05-26 03:55:11,http://treutel.org/madonna_murphy,0.9078711030,214.114.100.225,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n17614,6,397,2016-12-22 16:10:25,http://connwolff.net/lemuel.hauck,0.7509836236,39.36.78.122,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n17615,6,397,2017-01-10 06:29:05,http://emardmoen.org/ewell,0.1948054540,235.202.31.69,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n17616,6,397,2017-02-15 13:05:14,http://durganstehr.co/kirsten,0.2189394491,133.210.231.89,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n17617,6,397,2017-02-01 16:07:12,http://cronahilpert.name/dejuan,0.6117390624,15.53.254.222,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n17618,6,397,2017-03-29 10:28:43,http://hammeswest.info/forrest_vonrueden,0.0485405745,14.45.191.67,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n17619,6,397,2017-03-14 23:58:45,http://predovic.net/christa,0.6890921445,172.106.71.189,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n17620,6,397,2017-01-10 10:51:26,http://rempel.net/abigayle.bergstrom,0.6591556512,13.147.210.102,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n17621,6,397,2017-05-06 23:15:21,http://abernathy.info/dayana_olson,0.8903187567,134.79.62.149,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n17622,6,397,2017-05-17 07:39:26,http://strackeondricka.co/henri.stamm,0.1542310204,89.228.49.219,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n17623,6,397,2017-05-16 12:27:04,http://millsfeil.com/durward.armstrong,0.8979026282,16.247.189.175,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n17624,6,397,2017-05-24 06:43:23,http://beahan.biz/albina_bailey,0.0947845829,122.147.213.119,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n17625,6,397,2017-04-02 17:04:19,http://altenwerth.com/andreane.schmitt,0.3091304031,177.160.97.16,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n17626,6,397,2016-12-27 01:51:55,http://hegmann.io/reymundo.streich,0.1736976176,108.57.65.225,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n17627,6,397,2017-01-07 17:29:47,http://wisozkstoltenberg.name/destiney.abshire,0.3058227262,223.106.151.90,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n17628,6,397,2017-03-31 04:28:34,http://gibson.net/jalyn,0.1318832456,151.103.58.139,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n17629,6,397,2017-04-26 10:31:18,http://jones.net/joel.goldner,0.6448163686,229.210.238.225,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n17630,6,397,2017-04-18 22:13:06,http://runolfsdottirwillms.co/shakira_jacobson,0.6826193408,160.221.172.10,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n17631,6,397,2017-03-16 17:19:03,http://zboncakherman.info/samson,0.2337726849,51.140.232.31,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n17632,6,397,2017-03-27 09:05:42,http://pfannerstill.net/caria,0.4235359338,78.181.190.92,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n17633,6,397,2017-02-21 17:23:42,http://eichmann.name/zena,0.2557629251,229.171.117.113,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n17634,6,397,2017-04-02 20:05:23,http://binshayes.org/mckayla,0.6239015595,40.103.56.64,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n17635,6,397,2017-01-07 10:01:44,http://parkerdenesik.info/dylan,0.9769936111,201.129.211.148,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n17636,6,397,2017-05-11 10:03:14,http://hermann.net/marietta_mayer,0.3726025496,167.111.215.42,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n17637,6,397,2017-04-24 19:02:53,http://simonis.com/dewayne_pouros,0.6610616968,66.120.153.194,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n17638,6,397,2017-03-02 01:55:56,http://okuneva.com/kacey_ruel,0.6469739603,35.226.153.233,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n17639,6,397,2017-01-01 05:22:17,http://homenicktromp.info/travis.leffler,0.4099863729,157.87.250.177,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n17640,6,397,2017-05-29 23:02:17,http://collins.com/linnie,0.3334607812,184.128.188.217,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n17641,6,397,2017-02-21 23:12:09,http://pfeffer.info/maya_hyatt,0.2482504869,201.239.214.173,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n4780,2,105,2017-06-02 22:33:06,http://kiehn.net/jordan,,9.105.174.39,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n4781,2,106,2017-01-02 20:39:11,http://fritsch.io/sandrine,,161.204.114.86,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n4782,2,106,2017-01-09 13:49:34,http://ullrich.co/aron,,238.70.211.249,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n4783,2,106,2017-05-11 21:11:26,http://schinner.com/keshaun,,117.10.219.34,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n4784,2,106,2017-06-08 06:57:48,http://heidenreich.com/daisy_ziemann,,194.132.37.14,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n4785,2,106,2017-03-01 11:15:01,http://hahnglover.net/adrian,,31.172.238.216,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n4786,2,106,2017-03-12 01:57:45,http://glover.name/dorthy,,71.40.176.128,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n4787,2,106,2017-02-05 14:23:21,http://wolff.name/matilda.nicolas,,35.5.189.43,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n4788,2,106,2017-02-19 03:37:38,http://shanahansauer.co/alvena.wolff,,78.200.13.86,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n4789,2,106,2017-04-28 15:24:43,http://kingkutch.info/else.kutch,,195.160.219.166,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n4790,2,106,2017-01-22 18:51:13,http://moriette.com/ahmad,,111.122.138.42,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n4791,2,106,2017-05-06 11:21:45,http://simoniskuhn.name/emmanuelle_deckow,,71.177.113.45,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n4792,2,106,2017-01-06 21:18:11,http://ebert.io/dixie.oconner,,65.226.242.124,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n4793,2,106,2017-02-07 16:07:09,http://rice.biz/cayla_schiller,,200.239.22.203,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n4794,2,106,2017-04-15 19:48:12,http://reilly.info/melya_douglas,,64.7.93.222,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n4795,2,106,2017-03-14 17:05:01,http://hammes.biz/kim,,96.173.38.158,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n4796,2,106,2016-12-28 22:11:04,http://sauerbauch.co/jacinto.boyle,,2.73.26.182,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n4797,2,106,2017-03-22 03:22:04,http://hand.com/alek,,92.223.135.2,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n4798,2,106,2017-06-01 00:47:47,http://shieldsharber.com/cecelia,,206.63.199.140,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n4799,2,106,2017-03-01 15:03:32,http://grimes.co/akeem,,152.228.26.189,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n4800,2,106,2017-01-04 03:04:30,http://torphy.info/gordon,,189.195.141.70,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n4801,2,106,2017-05-15 07:59:59,http://lakinmitchell.org/dayana,,136.14.158.144,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n4802,2,106,2017-04-24 01:20:48,http://connelly.biz/wilmer,,220.215.131.19,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n4803,2,106,2017-01-02 23:36:22,http://kuhn.org/naomie.schoen,,103.172.34.225,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n4804,2,106,2017-01-04 08:05:49,http://emmerich.net/marlin_keler,,100.228.10.75,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n4805,2,106,2017-05-27 21:22:16,http://purdy.com/leonel.kemmer,,17.236.155.85,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n4806,2,106,2017-01-07 19:03:28,http://wiegandfriesen.co/orrin.zulauf,,171.69.138.46,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n4807,2,106,2017-04-29 00:26:38,http://wiegand.biz/joannie,,235.237.204.252,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n4808,2,106,2017-03-16 12:58:40,http://krajcik.com/victoria,,39.179.244.44,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n4809,2,106,2017-04-12 17:08:28,http://reingerfranecki.biz/jayme,,250.117.134.133,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n4810,2,106,2017-05-11 13:25:50,http://kemmer.io/madaline_ziemann,,133.168.243.3,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n4811,2,106,2017-01-13 12:40:39,http://macgyver.com/bertha,,189.6.60.151,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n4812,2,106,2017-04-28 05:45:21,http://linddaugherty.net/katherine.mccullough,,242.247.168.90,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n4813,2,106,2017-01-17 16:49:12,http://mosciski.biz/tiana_mckenzie,,51.201.109.73,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n4814,2,106,2017-05-13 16:59:50,http://dietrichhauck.io/samara_weimann,,167.229.193.251,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n4815,2,106,2017-01-26 20:31:38,http://kreiger.biz/meggie,,135.216.244.204,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n4816,2,106,2017-02-21 16:47:16,http://boyle.io/emely,,230.212.71.15,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n4817,2,106,2017-05-29 18:00:16,http://creminryan.io/emmanuelle_ortiz,,12.46.167.65,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n4818,2,106,2017-03-05 01:07:20,http://hyatt.com/doug.gaylord,,167.61.233.54,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n4819,2,106,2016-12-20 21:34:11,http://goyette.info/mauricio_hammes,,184.160.60.204,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n4820,2,106,2017-05-11 08:46:18,http://dare.net/karley,,163.35.242.32,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n4821,2,106,2017-01-01 01:40:18,http://altenwerthruel.name/buster,,241.36.2.132,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n4822,2,106,2017-03-23 12:53:01,http://kuhn.io/henri_lubowitz,,29.190.125.73,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n4823,2,107,2017-05-13 18:46:13,http://eichmann.org/emil.crist,,134.224.109.244,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n4824,2,107,2017-04-01 23:22:53,http://senger.org/flo,,127.179.182.149,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n4825,2,107,2017-02-13 04:05:36,http://runte.name/coby_will,,226.117.2.227,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n4826,2,107,2017-02-19 03:05:44,http://quigley.info/leif,,92.13.167.95,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n4827,2,107,2017-05-02 06:37:23,http://douglas.biz/marcel_lakin,,92.139.22.15,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n4828,2,107,2017-05-18 03:18:46,http://orn.com/lila.dibbert,,77.126.108.188,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n4829,2,107,2017-02-07 18:47:34,http://stiedemann.com/jedediah,,171.56.16.100,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n4830,2,107,2017-03-03 11:28:59,http://effertzwunsch.org/delores.feil,,166.183.30.129,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n4831,2,107,2017-04-25 21:06:05,http://thielhintz.name/hailee.wisozk,,25.145.160.41,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n4832,2,107,2017-06-07 12:39:03,http://ruecker.org/tobin,,151.205.87.87,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n4833,2,107,2017-03-20 09:57:14,http://bernhard.biz/kaleigh,,94.254.117.110,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n4834,2,107,2017-05-11 02:59:42,http://leannon.com/fae,,127.217.19.232,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n4835,2,107,2017-02-18 22:46:47,http://strosin.biz/joshua.mccullough,,117.160.134.82,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n11699,4,261,2017-05-19 01:43:26,http://walsh.name/kacey,0.8743415722,240.128.67.213,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n11700,4,261,2017-04-22 21:06:12,http://kunzebreitenberg.net/madie_kilback,0.3560150484,93.171.2.239,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n11701,4,261,2017-02-20 00:05:30,http://mcclure.io/tyrel_lockman,0.0855593485,179.97.251.37,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n11702,4,261,2017-01-30 17:27:08,http://christiansenblanda.name/miles,0.5185737210,110.234.146.39,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n11703,4,261,2017-04-30 06:39:58,http://okeefe.info/merritt_jakubowski,0.0664650429,140.74.97.247,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n11704,4,261,2017-06-06 12:31:23,http://turner.biz/alexys.bayer,0.3068166761,102.206.73.142,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n11705,4,261,2017-02-05 06:34:19,http://hyatt.net/krystina_bernier,0.2450087298,117.19.133.129,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n11706,4,261,2017-06-03 07:16:33,http://bogan.org/ryleigh,0.6496984442,237.129.40.169,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n11707,4,261,2016-12-22 19:28:38,http://walkerquigley.info/gennaro,0.3669014061,140.7.233.63,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n11708,4,261,2017-05-02 10:02:33,http://boyle.io/kyra_steuber,0.8734812351,145.190.16.10,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n11709,4,261,2017-05-13 15:35:41,http://simonis.net/kory_schmeler,0.2695039323,144.217.94.230,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n11710,4,261,2017-04-14 23:14:50,http://nicolasglover.info/martina_schinner,0.3933549179,248.253.16.35,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n11711,4,261,2017-05-03 19:01:27,http://hilllrogahn.info/steve,0.7664157669,80.77.92.160,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n11712,4,261,2017-01-05 20:13:36,http://gulgowskiwiza.info/colton,0.2490651167,236.35.251.176,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n11713,4,261,2017-01-20 00:52:28,http://block.info/candace_hayes,0.6545086958,75.43.106.92,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n11714,4,261,2017-03-20 16:25:04,http://shanahandurgan.info/cade_hills,0.1970440804,252.67.190.232,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n11715,4,262,2017-01-29 22:24:22,http://barton.co/daryl.haley,0.3656582479,211.93.239.123,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n11716,4,262,2017-04-23 06:06:45,http://doyle.net/estelle,0.6512117451,56.185.84.85,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n11717,4,262,2017-01-22 02:02:12,http://johnston.co/lexie.stroman,0.2594070468,216.189.128.222,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n11718,4,262,2017-03-25 16:14:37,http://ratharmstrong.com/nicholas,0.7474760921,43.247.55.19,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n11719,4,262,2017-02-04 17:02:29,http://hermann.net/verona_maggio,0.0575981399,37.20.75.113,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n11720,4,262,2017-02-08 16:09:02,http://stanton.net/moie,0.2123649615,205.173.209.200,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n11721,4,262,2016-12-18 17:33:05,http://altenwerthmonahan.biz/heber.hermiston,0.2925691142,71.113.75.230,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n11722,4,262,2017-04-22 06:18:07,http://streichstokes.io/brianne,0.7710329015,116.103.174.99,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n11723,4,262,2017-01-29 07:06:43,http://goodwinvon.net/liana.bernier,0.3494733815,219.8.80.153,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n11724,4,262,2017-01-02 08:21:08,http://hickle.co/estel,0.5950528313,128.96.194.124,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n11725,4,262,2017-05-27 21:29:15,http://harber.io/noe,0.4514563340,71.76.13.159,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n11726,4,262,2016-12-29 15:25:08,http://cummingtehr.biz/orion.carroll,0.5935874076,203.64.183.60,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n11727,4,262,2017-03-27 07:18:58,http://dibbert.biz/samanta,0.5419003321,238.15.209.199,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n11728,4,262,2017-03-10 04:25:15,http://herzog.name/luella,0.6435586662,189.253.38.250,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n11729,4,262,2017-01-03 15:37:16,http://ohara.com/andreanne,0.9572494647,120.15.162.38,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n11730,4,262,2017-04-26 16:46:23,http://gleichnermarks.biz/liliana.mccullough,0.2402264278,4.231.48.90,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n11731,4,262,2017-01-25 14:06:06,http://nitzsche.com/ardith,0.0853387935,121.73.125.213,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n11732,4,262,2017-02-19 17:47:56,http://kihnrunolfsdottir.biz/wanda,0.6784124525,54.146.144.152,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n11733,4,262,2017-06-05 11:28:15,http://terry.name/javonte,0.9762075166,205.79.253.237,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n11734,4,262,2017-05-02 06:22:27,http://krajcikkutch.net/anita,0.0232549296,80.6.29.135,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n11735,4,262,2017-01-01 03:39:53,http://spencer.info/harvey,0.6660079839,7.59.148.80,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n11736,4,262,2017-04-09 12:11:40,http://ruecker.io/monserrat,0.9566614944,240.82.203.202,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n11737,4,262,2017-03-24 19:18:54,http://ornferry.info/clemens,0.6245346574,99.67.35.187,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n11738,4,262,2017-04-21 21:06:54,http://turcotte.name/verla.cartwright,0.2821926132,225.46.139.145,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n11739,4,262,2017-02-20 01:18:18,http://mrazboyer.info/sydnee_hermann,0.0526084622,108.164.89.254,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n11740,4,262,2016-12-21 17:26:52,http://douglas.biz/emiliano,0.7235854972,254.56.250.117,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n11741,4,262,2017-05-27 16:05:12,http://kingerdman.info/elyse,0.8940323175,41.108.62.230,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n11742,4,262,2017-05-24 21:01:19,http://reichertbashirian.org/stephanie,0.8196002564,189.182.150.178,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n11743,4,262,2017-01-23 12:19:36,http://nikolaus.io/neoma,0.1005516697,209.156.199.230,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n11744,4,262,2017-04-03 04:32:38,http://aufderhar.co/zola_larkin,0.7303087541,149.211.69.118,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n11745,4,262,2017-05-06 14:38:59,http://turcotte.net/jaunita_kihn,0.6029717536,163.12.134.32,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n11746,4,262,2017-04-15 16:04:21,http://smitham.biz/cleveland_stanton,0.6588527583,250.156.173.22,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n11747,4,262,2017-04-28 01:19:32,http://muller.info/je.robel,0.7659668083,219.53.108.185,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n11748,4,262,2016-12-17 21:21:26,http://johnstonnienow.co/nikki.kautzer,0.9762927496,177.229.37.72,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n11749,4,262,2017-02-23 03:05:47,http://keebler.org/louisa_aufderhar,0.7780791992,16.253.82.176,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n14652,5,330,2017-05-04 19:59:47,http://jacobi.info/hazel,0.2325358978,15.43.158.10,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n14653,5,330,2017-06-05 12:26:03,http://hermiston.info/consuelo,0.2646967671,110.218.118.139,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n14654,5,330,2017-05-03 07:27:12,http://shields.io/amaya,0.8262764608,174.74.35.206,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n14655,5,330,2017-02-13 12:56:37,http://daniel.co/lydia,0.6537342550,157.66.138.191,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n14656,5,330,2017-05-25 04:38:22,http://fritsch.biz/jaren_pfannerstill,0.5201998057,165.253.55.228,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n14657,5,330,2017-02-10 13:59:21,http://marquardtkris.info/casey,0.8893991117,145.124.83.200,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n14658,5,330,2017-06-04 15:45:20,http://johnscruickshank.biz/stephania,0.1020652600,213.199.174.78,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n14659,5,330,2017-01-21 21:46:18,http://borerhills.com/angela_rau,0.6659969416,97.190.105.241,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n14660,5,330,2017-01-05 19:26:04,http://buckridge.org/alvis,0.8220261318,183.135.152.79,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n14661,5,330,2016-12-20 01:33:14,http://dietrichthiel.com/glenda,0.7352875328,230.215.104.2,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n14662,5,330,2017-05-06 11:21:38,http://lebsack.biz/nathan_yost,0.3064470622,21.163.185.251,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n14663,5,330,2017-06-01 15:10:18,http://kihnleuschke.com/caandra.bechtelar,0.7113466540,131.144.222.32,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n14664,5,330,2017-03-06 15:15:12,http://schoen.info/donny_oberbrunner,0.2401347817,158.220.62.174,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n14665,5,330,2017-03-07 17:40:09,http://franeckispencer.info/cooper,0.9372319480,121.227.138.95,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n14666,5,331,2016-12-29 11:27:50,http://schusterkris.net/patrick.dare,0.2443856292,146.254.154.111,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n14667,5,331,2017-06-02 08:50:34,http://swift.net/chelsie_daniel,0.2898655278,237.72.30.62,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n14668,5,331,2017-04-27 06:59:25,http://block.com/zetta,0.7804417437,47.3.74.83,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n14669,5,331,2016-12-19 19:27:57,http://okonwillms.name/rhea.lakin,0.7554555868,121.208.77.94,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n14670,5,331,2017-01-19 02:09:18,http://daniel.co/liam,0.7642826636,94.183.187.38,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n14671,5,331,2017-04-30 22:18:17,http://zemlak.co/autumn_hintz,0.4700178319,31.119.238.32,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n14672,5,331,2016-12-18 09:36:15,http://erdman.name/veda_dietrich,0.8273486174,222.207.36.234,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n14673,5,331,2017-05-15 11:08:08,http://colerowe.io/lysanne,0.4302946374,61.40.25.197,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n14674,5,331,2017-03-05 12:57:30,http://framilemke.io/ebony,0.3203229262,219.99.161.207,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n14675,5,331,2017-06-06 02:55:59,http://kirlin.net/maegan,0.3370020743,93.143.226.107,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n14676,5,331,2017-05-12 00:50:51,http://von.net/verlie,0.6233119038,218.16.180.217,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n14677,5,331,2017-05-14 17:15:10,http://nitzschekemmer.org/gus_cain,0.2365747328,170.189.103.78,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n14678,5,331,2017-05-10 01:46:08,http://morar.com/elaina.botsford,0.6967860857,98.192.61.117,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n14679,5,331,2017-05-27 10:35:13,http://kerlukeboehm.com/giovanna_pagac,0.7050660887,207.73.171.55,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n14680,5,331,2017-01-17 00:41:29,http://schulist.biz/ottilie,0.0658972127,14.86.218.165,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n14681,5,331,2017-05-08 02:15:17,http://lehnerbashirian.org/maeve,0.4563063351,172.140.120.206,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n14682,5,331,2017-02-09 16:08:47,http://tillman.org/nedra,0.6521436622,245.92.132.2,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n14683,5,331,2017-06-10 16:44:03,http://koepp.name/jake.farrell,0.2338609575,205.49.160.216,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n14684,5,331,2017-04-18 06:58:20,http://turcotte.net/leonie,0.2083530683,115.177.5.19,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n14685,5,331,2017-04-01 12:28:42,http://thompsoncorwin.info/lorenza,0.8420526365,56.13.46.20,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n14686,5,331,2016-12-17 10:00:15,http://kulaswolff.co/evert.wintheiser,0.3742161103,251.149.52.107,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n14687,5,331,2017-05-23 18:02:44,http://heidenreich.info/lester,0.6043919789,42.238.164.93,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n14688,5,331,2017-05-31 15:30:48,http://bradtke.com/rodrigo,0.5907889040,184.8.211.169,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n14689,5,331,2017-02-11 05:17:44,http://hansen.com/chaim.auer,0.4008318024,26.75.233.185,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n14690,5,331,2017-03-04 05:27:08,http://keebler.co/filiberto,0.8868175417,121.107.164.138,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n14691,5,331,2017-03-12 07:32:41,http://ebertgleason.org/raven_stark,0.7425114570,144.116.165.142,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n14692,5,331,2017-02-14 21:25:18,http://ruecker.biz/marianne,0.8225852173,182.23.155.232,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n14693,5,331,2017-02-16 00:06:52,http://walsh.com/brendan,0.0983186296,199.108.152.242,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n14694,5,331,2016-12-24 13:58:38,http://kuvalis.co/lloyd.schaden,0.0386907775,197.189.94.45,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n14695,5,331,2017-03-07 11:53:01,http://littel.biz/leilani_thompson,0.9826430796,122.187.203.138,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n14696,5,331,2017-03-26 03:48:01,http://schneider.io/emie.bayer,0.7819464022,157.253.189.113,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n14697,5,331,2017-01-26 15:07:00,http://runolfon.io/heather.ratke,0.0860383083,111.64.49.191,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n14698,5,331,2017-03-31 16:27:27,http://runolfsdottirschaden.org/stan.tremblay,0.2471061150,24.151.176.72,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n14699,5,331,2017-03-18 04:01:10,http://bins.co/deron.price,0.6596700641,17.165.195.226,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n14700,5,331,2017-02-13 01:55:48,http://reichertmacgyver.net/vilma.dooley,0.9310456846,205.246.34.58,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n14701,5,331,2017-05-22 04:47:40,http://wintheiser.co/rollin,0.6119566482,42.148.216.162,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n14702,5,331,2017-06-03 17:48:52,http://beerreinger.co/mireille,0.7890395724,38.237.208.75,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n17642,6,397,2017-02-09 09:18:35,http://lockmanwisozk.org/julian_schmitt,0.1207674398,176.123.189.55,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n17643,6,397,2017-04-04 10:59:04,http://pfannerstill.org/arnaldo.kihn,0.5884988578,239.132.70.79,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n17644,6,397,2017-05-14 16:52:57,http://gleason.info/aditya_muller,0.2921562683,94.2.115.28,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n17645,6,397,2017-03-25 07:30:25,http://bergnaumturner.name/blair.bogisich,0.4445086035,251.127.94.202,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n17646,6,397,2017-05-21 09:51:47,http://streichboehm.io/donna.herman,0.4131711419,153.117.248.82,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n17647,6,397,2017-03-01 18:11:00,http://wilkinson.info/enid_heidenreich,0.4166243511,230.135.47.177,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n17648,6,397,2017-05-03 21:39:02,http://emard.io/herminia.jacobs,0.7270855991,75.164.53.45,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n17649,6,397,2017-05-17 08:50:32,http://spinka.net/brant.kerluke,0.3880866900,64.225.23.210,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n17650,6,397,2017-04-10 08:08:41,http://faheypredovic.com/letha_sauer,0.0090437921,109.99.83.254,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n17651,6,397,2017-01-27 08:41:20,http://pfeffer.com/dashawn,0.4570704919,234.245.154.178,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n17652,6,397,2017-01-17 04:54:47,http://heathcotebednar.name/bo_deckow,0.7755067289,195.202.91.155,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n17653,6,397,2017-05-15 01:31:38,http://haag.name/theodora_crona,0.5429641814,174.211.174.5,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n17654,6,397,2017-03-29 13:32:31,http://cummings.com/melya,0.1459191072,236.73.251.159,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n17655,6,397,2017-01-29 17:39:27,http://parisian.io/ozella_romaguera,0.4894780083,82.48.88.40,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n17656,6,397,2017-02-03 22:38:41,http://kshlerin.co/eleanora,0.7802459643,15.242.81.209,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n17657,6,397,2017-04-19 14:18:30,http://hansen.co/rene,0.9961141511,251.24.47.206,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n17658,6,397,2017-02-17 14:56:31,http://ankunding.io/isac,0.6758877399,86.58.244.67,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n17659,6,397,2017-04-15 21:13:47,http://lubowitzoreilly.org/frederick,0.9477762195,86.188.130.252,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n17660,6,397,2017-03-09 10:35:03,http://macgyverrunolfsdottir.org/jefferey.volkman,0.5877242621,79.88.91.135,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n17661,6,397,2017-01-24 07:03:32,http://jasttremblay.net/louvenia,0.0253228657,77.146.119.175,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n17662,6,397,2016-12-15 10:44:53,http://rohan.info/oma_huel,0.7812498539,139.76.12.31,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n17663,6,397,2017-04-14 02:48:07,http://muller.org/ignatius_pouros,0.4748161225,63.250.11.197,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n17664,6,397,2017-01-03 16:41:40,http://koepp.net/nils,0.9921148356,36.27.236.179,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n17665,6,397,2017-02-14 16:54:30,http://schowalterfritsch.net/angus,0.8715384341,245.42.147.98,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n17666,6,397,2017-05-29 23:39:47,http://powlowski.net/ward_botsford,0.2908112356,252.122.62.19,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n17667,6,397,2016-12-31 00:11:17,http://hettinger.biz/camryn_hermiston,0.1722919918,237.59.209.181,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n17668,6,397,2017-05-16 11:37:39,http://blanda.net/caterina,0.9108275777,33.225.192.160,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n17669,6,397,2016-12-23 12:56:32,http://ferry.io/owen_harber,0.1676347311,124.56.4.105,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n17670,6,397,2017-03-18 11:12:52,http://hellerjerde.co/dora_weber,0.2921955598,100.190.211.167,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n17671,6,397,2017-04-09 18:55:15,http://kuphal.org/ezra.weinat,0.8296761631,25.12.159.41,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n17672,6,397,2016-12-18 16:20:46,http://baumbachkaulke.io/callie,0.9578062218,60.58.254.196,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n17673,6,397,2017-05-30 10:24:21,http://harvey.biz/dillon.stanton,0.6945464826,111.235.173.150,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n17674,6,397,2017-05-13 06:50:14,http://colelangworth.io/conrad_prosacco,0.2771849137,241.61.243.13,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n17675,6,397,2017-02-10 22:39:12,http://streich.name/una,0.9069206935,141.50.78.190,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n17676,6,397,2017-05-12 13:10:08,http://heaneygaylord.biz/claudie.wiza,0.8462124866,176.105.100.179,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n17677,6,398,2017-04-21 04:33:16,http://mante.info/alanis,0.8943627033,150.149.142.169,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n17678,6,398,2017-03-16 10:07:33,http://bartoletti.name/eddie_fay,0.2164434329,121.133.166.147,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n17679,6,398,2017-05-13 18:09:21,http://mcglynn.info/coby.lindgren,0.1749993262,152.37.35.78,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n17680,6,398,2017-03-17 12:55:26,http://bernier.co/romaine.sporer,0.1282572600,44.41.223.150,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n17681,6,398,2016-12-30 14:47:49,http://reingerterry.com/brian,0.9111078919,145.168.3.191,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n17682,6,398,2016-12-21 14:07:26,http://greenfeldercarroll.biz/raquel_huel,0.8084772702,120.50.9.139,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n17683,6,398,2017-05-28 22:00:10,http://blick.org/joana.wuckert,0.0682506693,31.90.150.9,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n17684,6,398,2017-06-02 12:22:44,http://dibbert.biz/vicky.romaguera,0.3405378293,83.166.55.203,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n17685,6,398,2017-03-27 03:28:34,http://feeneyboyer.co/kenton,0.0060133811,61.172.225.104,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n17686,6,398,2017-02-07 06:30:25,http://murazik.net/adaline,0.6380084818,123.8.250.153,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n17687,6,398,2017-02-19 14:46:30,http://baumbachhintz.name/yesenia_tillman,0.8570087596,142.65.11.42,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n17688,6,398,2017-03-27 03:07:38,http://pacocha.net/kristopher.walker,0.6753941382,73.122.129.25,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n17689,6,398,2017-02-14 09:14:19,http://dickenstrantow.info/vena,0.0477307774,171.73.157.96,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n17690,6,398,2017-02-22 17:12:27,http://grahamgulgowski.info/elody,0.8327230784,11.231.231.34,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n17691,6,398,2017-05-10 05:17:07,http://boyle.info/colten,0.6818436362,115.133.247.160,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n4836,2,107,2017-01-12 22:17:20,http://conroy.org/alyon,,48.160.12.64,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n4837,2,107,2017-02-06 10:12:10,http://bergstrom.org/kendrick_balistreri,,25.126.21.66,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n4838,2,107,2017-03-13 12:05:12,http://mcclure.org/aniya_ziemann,,182.101.98.77,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n4839,2,107,2016-12-28 10:14:16,http://wiza.com/kiley,,63.247.248.209,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n4840,2,107,2017-05-17 06:15:52,http://gorczany.biz/willard,,181.116.232.140,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n4841,2,107,2017-03-06 13:39:26,http://schaden.co/shaniya_carter,,18.236.3.101,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n4842,2,107,2017-01-16 08:38:08,http://veum.com/myah.barton,,61.160.103.54,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n4843,2,107,2017-05-26 21:34:08,http://prosaccosmith.name/maryse,,92.132.38.53,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n4844,2,107,2017-02-25 17:04:55,http://schulist.io/deangelo,,130.198.163.23,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n4845,2,107,2017-04-25 11:53:04,http://cronin.info/cierra,,151.195.199.162,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n4846,2,107,2016-12-20 01:49:46,http://stark.org/casandra,,46.134.167.241,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n4847,2,107,2017-04-04 11:35:30,http://lindgren.biz/bert_olson,,53.100.101.200,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n4848,2,107,2016-12-31 05:04:38,http://schowaltermosciski.net/bradly,,65.239.153.7,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n4849,2,107,2017-05-21 21:02:06,http://roberts.info/kris,,189.205.251.150,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n4850,2,107,2017-05-23 23:21:09,http://oreilly.name/sydni,,109.13.72.71,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n4851,2,107,2016-12-29 05:24:50,http://wiegand.org/virgie,,220.236.81.103,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n4852,2,107,2017-02-19 14:25:08,http://kaulkerunolfon.com/adalberto,,186.38.17.6,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n4853,2,107,2017-05-02 00:57:53,http://kuhic.org/caie_botsford,,184.220.73.3,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n4854,2,107,2017-05-07 08:41:01,http://hermanngusikowski.org/broderick,,204.104.182.198,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n4855,2,107,2017-01-08 01:27:51,http://harris.name/clay,,31.13.11.49,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n4856,2,107,2017-05-04 20:01:58,http://okon.net/mozell.harvey,,100.220.177.129,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n4857,2,107,2016-12-27 14:53:19,http://koelpinhartmann.biz/melia,,86.250.31.238,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n4858,2,107,2017-04-05 15:42:21,http://sporer.info/luther,,171.96.181.177,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n4859,2,107,2017-03-29 14:05:14,http://stammhuels.co/drake_bednar,,104.92.183.208,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n4860,2,107,2017-04-14 17:39:13,http://welch.info/jasper,,71.45.115.132,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n4861,2,107,2017-01-12 18:01:24,http://roberts.org/elsie,,218.9.22.70,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n4862,2,107,2017-06-05 09:28:02,http://bauchhayes.org/sid,,170.96.241.96,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n4863,2,107,2017-05-07 23:06:43,http://robertsbeahan.name/annabell,,38.117.221.62,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n4864,2,107,2017-05-22 02:40:36,http://schroedernolan.co/pearl,,151.85.162.32,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n4865,2,107,2017-01-15 03:13:18,http://jast.net/royal.hansen,,62.153.191.47,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n4866,2,107,2017-04-27 08:14:37,http://senger.biz/briana,,47.137.113.62,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n4867,2,107,2017-02-04 21:09:30,http://mclaughlinstrosin.io/august,,220.250.185.170,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n4868,2,107,2017-03-16 02:27:49,http://moen.biz/gregg,,193.19.228.249,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n4869,2,107,2017-04-04 17:36:49,http://gorczany.info/guadalupe,,66.173.145.197,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n4870,2,107,2016-12-16 07:16:44,http://howepouros.com/dena,,237.33.232.41,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n4871,2,107,2017-05-06 18:40:20,http://hoeger.biz/wilhelmine,,190.124.40.154,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n4872,2,107,2017-02-01 20:59:20,http://dach.com/kariane,,185.54.178.11,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n4873,2,107,2017-03-27 02:32:45,http://corkerysporer.net/anahi_erdman,,203.111.9.155,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n4874,2,107,2017-05-10 05:36:48,http://white.name/judson_waelchi,,96.120.136.47,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n4875,2,107,2017-03-07 12:20:51,http://hagenes.info/saige.walter,,39.254.179.227,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n4876,2,107,2017-01-13 23:29:35,http://tillmanlesch.name/jade,,108.186.53.113,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n4877,2,107,2016-12-31 07:02:17,http://bashiriankautzer.com/freddy,,221.198.247.242,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n4878,2,107,2017-02-14 04:06:19,http://ondricka.name/lorenza,,38.189.133.64,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n4879,2,107,2017-05-06 22:26:35,http://predovic.co/winnifred,,153.162.55.234,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n4880,2,107,2017-03-10 06:52:37,http://vonruedenwalsh.co/weldon,,38.34.52.38,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n4881,2,107,2017-03-16 19:42:56,http://abshirestoltenberg.name/keely,,9.238.220.73,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n4882,2,108,2017-02-17 04:38:32,http://torp.org/boyd.shanahan,,73.50.149.216,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n4883,2,108,2017-01-27 11:13:13,http://mayert.com/yesenia_sanford,,83.146.145.79,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n4884,2,108,2017-01-24 01:35:30,http://becker.net/roslyn.kunde,,186.173.248.103,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n4885,2,108,2017-04-15 08:37:50,http://ziemebalistreri.net/maribel_shields,,155.64.210.249,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n4886,2,108,2017-04-21 09:36:18,http://spinka.biz/winifred,,254.125.125.126,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n4887,2,108,2017-05-24 20:52:44,http://ankunding.co/yazmin_heel,,21.78.121.190,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n4888,2,108,2017-02-11 09:04:27,http://mcglynn.co/estevan.mayer,,224.99.5.60,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n4889,2,108,2017-03-29 10:59:29,http://ricelubowitz.net/anibal,,208.242.6.25,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n4890,2,108,2017-05-18 11:56:02,http://homenicksmitham.io/nellie.green,,78.229.26.123,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n4891,2,108,2017-02-22 12:48:15,http://tremblay.name/casimer.leannon,,221.116.157.60,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n11750,4,262,2017-02-22 08:28:36,http://littlejerde.org/bridget_hirthe,0.3378686770,38.20.132.148,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n11751,4,262,2017-01-26 11:28:55,http://smith.net/felipa.batz,0.2174058446,107.97.228.95,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n11752,4,262,2017-05-18 18:03:37,http://larkin.com/wellington_ward,0.8829933347,140.187.238.241,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n17825,7,403,2017-03-12 08:46:21,http://tillman.org/clement_wiegand,,47.129.116.142,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n17826,7,403,2017-01-08 19:21:51,http://gerholdhermiston.name/rory_crona,,183.3.121.210,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n17827,7,403,2016-12-20 11:09:23,http://streichokuneva.io/mortimer,,63.196.194.98,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n17828,7,403,2017-05-22 05:02:00,http://framibogisich.io/kellie,,190.17.14.165,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n17829,7,403,2017-06-03 11:06:54,http://hirthereichel.info/sarai.dubuque,,241.212.11.45,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n17830,7,403,2017-02-23 00:10:25,http://medhurst.org/annie,,186.5.89.84,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n17831,7,403,2017-05-18 09:05:42,http://deckow.io/ezekiel.beier,,78.45.133.126,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n17832,7,403,2017-04-17 22:52:04,http://zieme.io/jeramie,,159.132.175.52,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n17833,7,403,2017-01-03 03:29:26,http://hansen.org/magali.botsford,,101.224.153.130,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n17834,7,403,2017-02-11 19:16:18,http://bode.com/kenneth,,23.74.38.30,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n17835,7,403,2017-05-14 12:28:37,http://funk.name/rhett_okon,,76.40.132.13,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n17836,7,403,2017-05-28 15:10:15,http://schiller.biz/gladyce.moen,,168.116.52.24,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n17837,7,403,2017-04-08 22:53:56,http://beervolkman.net/jakayla,,176.219.208.171,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n17838,7,403,2016-12-20 19:29:17,http://croninstroman.biz/marguerite.halvorson,,54.128.34.174,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n17839,7,403,2017-06-07 09:07:21,http://feest.net/augusta,,238.79.126.118,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n17840,7,403,2017-05-22 00:44:16,http://damore.net/kole_cartwright,,152.43.150.142,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n17841,7,403,2016-12-22 05:12:13,http://lehner.org/earl,,195.219.189.70,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n17842,7,403,2017-04-24 01:39:28,http://graham.io/shanny.wilderman,,70.216.196.161,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n17843,7,403,2017-05-11 10:54:03,http://sporerschaden.org/mattie,,216.113.36.61,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n17844,7,403,2017-01-17 04:28:27,http://upton.info/joany_hermann,,37.169.163.151,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n17845,7,403,2017-03-05 16:39:42,http://macejkovic.biz/queen.johnson,,192.253.184.168,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n17846,7,403,2017-04-18 16:35:44,http://brakusturcotte.com/elouise,,116.16.57.135,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n17847,7,403,2016-12-14 19:31:24,http://schoen.info/conner,,202.225.235.126,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n17848,7,403,2017-02-24 16:43:28,http://hackett.io/arielle.ankunding,,75.112.42.43,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n17849,7,403,2017-03-07 00:30:46,http://baumbach.net/arianna_homenick,,216.136.93.193,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n17850,7,403,2017-02-25 07:26:53,http://lockmandare.org/abigayle,,238.159.34.149,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n17851,7,403,2016-12-27 00:29:22,http://pollich.io/joshuah,,115.25.242.19,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n17852,7,403,2017-01-05 04:45:10,http://hills.name/gabriel,,18.106.132.227,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n17853,7,403,2017-04-24 05:41:59,http://miller.name/dock_fritsch,,125.252.88.67,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n17854,7,403,2017-05-03 13:03:19,http://wilkinson.net/alan_turner,,131.41.214.75,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n17855,7,403,2017-03-23 10:46:43,http://doyle.net/omer_hammes,,198.167.72.12,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n17856,7,403,2016-12-28 05:28:13,http://stammlehner.com/oma,,141.153.77.33,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n17857,7,403,2017-06-01 11:49:47,http://schaeferbarton.biz/jayde,,40.148.209.206,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n17858,7,403,2017-01-11 02:36:02,http://schuppe.biz/prince,,155.230.246.40,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n17859,7,403,2017-04-26 15:52:16,http://considinehamill.com/geo.conn,,189.150.235.189,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n17860,7,403,2017-01-01 05:19:08,http://schowalterjakubowski.biz/rick,,46.112.137.84,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n17861,7,403,2017-05-01 12:43:58,http://bednarzemlak.io/breanne.stiedemann,,181.24.220.109,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n17862,7,403,2017-02-04 19:50:34,http://ritchiehintz.co/emil,,109.221.116.96,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n17863,7,403,2017-05-18 09:48:08,http://champlin.net/johann,,46.176.32.50,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n17864,7,403,2017-03-15 19:07:57,http://mcclure.org/efrain,,42.95.230.126,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n17865,7,404,2017-01-03 01:03:40,http://mckenzie.name/lou.funk,,52.186.141.52,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n17866,7,404,2017-05-25 02:41:59,http://kingrau.info/merlin,,13.79.40.84,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n17867,7,404,2017-01-09 15:59:48,http://ohara.io/candice_herzog,,80.148.19.78,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n17868,7,404,2017-03-11 17:49:10,http://hudsonkeebler.co/jaycee,,213.180.125.143,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n17869,7,404,2017-01-31 20:54:15,http://hartmann.com/rebecca,,161.215.216.247,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n17870,7,404,2017-03-18 18:45:01,http://schadendaniel.io/eugenia.lubowitz,,183.82.178.173,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n17871,7,404,2016-12-31 05:30:51,http://kuvalitreich.net/darren,,65.25.171.229,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n17872,7,404,2017-04-01 11:36:35,http://mcdermott.info/shaina,,198.171.81.225,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n17873,7,404,2017-01-12 07:11:31,http://oconner.info/robyn_goyette,,242.102.78.254,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n17874,7,404,2017-01-12 04:31:53,http://wintheiser.biz/don,,85.35.208.176,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n17875,7,404,2017-03-26 16:19:29,http://mosciski.info/kirstin_jast,,131.7.56.137,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n17876,7,404,2016-12-17 00:12:42,http://donnelly.io/garfield_shanahan,,174.107.113.110,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n14703,5,331,2017-05-14 17:18:17,http://lakincole.info/deshawn.kovacek,0.1367501830,9.2.253.172,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n14704,5,331,2017-06-08 04:06:43,http://reinger.net/sean,0.9219266992,179.224.12.194,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n14705,5,331,2017-01-13 17:31:10,http://durgan.com/yvonne_ernser,0.0072071357,246.59.227.108,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n14706,5,331,2017-02-25 04:28:04,http://spencer.name/lincoln.lueilwitz,0.2018808724,56.69.238.83,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n14707,5,331,2017-02-23 20:41:31,http://beer.name/rick_corwin,0.7461956022,92.6.119.248,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n14708,5,331,2017-05-19 21:47:30,http://wymankeebler.name/kailyn_walter,0.9082239700,217.229.57.148,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n14709,5,331,2017-05-16 02:56:02,http://bogisichwill.com/christopher,0.0689343116,2.251.5.14,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n14710,5,331,2017-03-03 22:02:16,http://weimannskiles.net/erin_yost,0.8285622430,9.201.50.244,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n14711,5,331,2017-05-14 11:47:52,http://metz.org/tracy,0.3752662051,103.159.80.5,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n14712,5,331,2017-01-26 13:36:25,http://batz.biz/daniela,0.8770948383,27.237.147.239,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n14713,5,331,2016-12-25 13:46:10,http://townekoepp.name/geo,0.6246946044,62.120.90.73,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n14714,5,331,2017-04-08 13:37:56,http://marquardt.biz/willa_reilly,0.9455184382,252.230.108.142,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n14715,5,331,2017-05-22 04:28:59,http://wolff.com/sally.fahey,0.0999147003,189.166.182.178,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n14716,5,331,2016-12-18 06:15:39,http://andersonrau.biz/evie,0.2203005410,86.55.21.89,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n14717,5,331,2017-01-02 09:04:12,http://schneider.io/olaf,0.9907748150,49.156.34.143,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n14718,5,331,2016-12-20 13:09:04,http://wilderman.net/jany,0.2161471299,194.105.15.96,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n14719,5,331,2017-03-06 06:09:10,http://little.info/barbara.abshire,0.5462280100,166.200.70.32,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n14720,5,331,2017-05-26 01:45:40,http://hamill.net/stephon_gulgowski,0.3833828098,233.4.169.15,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n14721,5,331,2017-06-06 13:24:08,http://kulasfahey.info/estelle_botsford,0.2606890554,159.38.59.27,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n14722,5,332,2017-03-12 22:29:16,http://corwin.info/jana,0.2386205386,234.88.166.242,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n14723,5,332,2017-01-27 08:22:56,http://kovacek.info/casandra,0.4377326311,166.205.52.148,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n14724,5,332,2017-05-02 15:57:44,http://macejkovic.info/sven,0.3436730370,18.194.249.28,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n14725,5,332,2017-01-22 11:14:15,http://mraz.net/emelie,0.5858670199,239.96.115.189,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n14726,5,332,2016-12-24 15:50:52,http://hartmann.info/herbert,0.8422704458,233.184.101.15,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n14727,5,332,2017-06-12 13:05:57,http://will.com/landen_schumm,0.3007364159,180.125.242.6,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n14728,5,332,2017-03-08 08:26:55,http://pouros.co/deion.hauck,0.1501976817,62.170.168.97,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n14729,5,332,2017-03-06 09:26:09,http://johnston.co/mariela.metz,0.8120029292,54.246.191.78,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n14730,5,332,2017-02-24 21:09:14,http://cremin.co/reie_kautzer,0.0716985997,2.127.65.248,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n14731,5,332,2017-02-14 21:20:18,http://crona.com/ricardo_block,0.2341368880,69.145.88.168,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n14732,5,332,2017-04-26 16:10:28,http://dubuquekrajcik.info/rosalia,0.3775255251,162.116.206.181,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n14733,5,332,2017-05-29 05:22:43,http://gibsonhartmann.co/paolo_hudson,0.2361965101,201.102.230.103,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n14734,5,332,2017-02-15 10:05:14,http://jacobsbergstrom.io/johnathon_kemmer,0.5604634544,212.125.77.251,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n14735,5,332,2017-03-19 05:07:27,http://nicolas.info/leone,0.6728938691,155.182.214.38,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n14736,5,332,2016-12-17 10:51:15,http://lindgren.biz/enos.fritsch,0.3924670223,45.200.66.94,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n14737,5,332,2017-05-09 11:44:23,http://koch.net/jonathon_medhurst,0.6550492306,51.23.169.170,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n14738,5,332,2016-12-21 15:39:15,http://larkin.co/myrtice,0.6460994836,97.87.188.155,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n14739,5,332,2017-01-19 09:57:27,http://swaniawski.net/jean.carroll,0.9910162251,116.145.60.144,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n14740,5,332,2017-02-18 14:36:56,http://armstrong.com/robbie,0.5205134163,35.144.79.195,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n14741,5,332,2016-12-18 14:54:16,http://armstrongwaters.io/glenna,0.4850643600,39.37.32.98,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n14742,5,332,2017-05-09 08:48:13,http://hackett.info/demond_bogan,0.9079141321,56.104.37.242,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n14743,5,332,2016-12-16 07:59:15,http://herzog.net/jayden,0.9658499534,159.43.123.182,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n14744,5,332,2017-04-13 08:12:00,http://cremin.info/hildegard_stokes,0.4144352455,142.235.96.31,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n14745,5,332,2017-03-27 13:46:13,http://kreigerframi.biz/chandler.kuphal,0.4092450409,188.157.197.104,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n14746,5,333,2016-12-23 23:48:20,http://krajcikfritsch.io/drew_schroeder,0.9822358828,209.133.109.169,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n14747,5,333,2017-04-10 23:06:59,http://gutmannkuhic.net/danielle,0.3189304459,195.230.122.198,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n14748,5,333,2017-02-14 07:53:23,http://daremetz.co/jerald,0.9546816447,137.213.26.202,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n14749,5,333,2017-04-09 13:37:21,http://macgyver.com/khalil,0.0405680813,65.63.142.145,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n14750,5,333,2016-12-29 16:24:36,http://jacobs.biz/jerome,0.9077540361,195.157.18.62,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n14751,5,333,2017-03-11 11:53:00,http://willmswintheiser.io/nannie,0.9251853611,214.94.108.81,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n14752,5,333,2017-05-11 09:23:55,http://morar.net/eldridge,0.2394336846,35.179.234.82,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n14753,5,333,2017-04-12 14:02:45,http://kulasblanda.co/maribel_kunde,0.4046506687,137.16.222.5,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n17692,6,398,2017-02-04 08:30:44,http://sawaynzulauf.biz/vernie,0.6287410715,26.198.29.182,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n17693,6,398,2017-05-12 21:15:37,http://collinsnicolas.io/ferne_mertz,0.3987874898,189.152.140.26,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n17694,6,398,2017-01-13 10:39:50,http://hahnoconner.biz/era.daugherty,0.9330704961,14.124.199.27,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n17695,6,398,2017-02-23 04:26:56,http://nicolas.biz/jermey_nolan,0.0620368169,35.208.58.239,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n17696,6,398,2017-02-23 21:07:27,http://batz.info/viola.ritchie,0.8362231651,79.118.183.112,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n17697,6,398,2016-12-18 00:41:21,http://keebler.com/caitlyn.pfeffer,0.4008740068,102.73.112.33,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n17698,6,398,2017-05-15 01:50:57,http://watsica.info/nash_smith,0.6721545576,6.64.202.8,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n17699,6,398,2017-04-01 11:51:20,http://stammbartell.biz/merlin,0.3025212671,251.76.17.27,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n17700,6,398,2017-02-12 08:39:18,http://yost.biz/raymond,0.2061240069,59.181.232.227,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n17701,6,398,2017-04-07 02:18:06,http://reichert.info/reid,0.5102469824,37.181.122.228,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n17702,6,399,2017-03-23 10:39:20,http://batztowne.info/alexander_deckow,0.5487109987,173.170.65.105,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n17703,6,399,2017-06-04 01:14:29,http://maggio.io/reagan,0.5449074943,57.142.76.99,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n17704,6,399,2017-02-20 08:13:56,http://stehr.info/lonzo,0.4933567305,129.206.116.39,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n17705,6,399,2017-05-28 02:22:54,http://kirlincorkery.io/leon,0.4134862295,227.62.106.20,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n17706,6,399,2017-04-23 23:50:48,http://muellerleannon.info/ara,0.9451264427,238.189.184.40,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n17707,6,399,2017-01-12 15:23:31,http://schoencarter.name/floie,0.8115406439,94.83.140.146,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n17708,6,399,2017-06-13 05:35:13,http://mckenzie.io/vernon,0.5545412368,129.46.176.200,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n17709,6,399,2017-01-08 09:14:18,http://jerdebraun.name/trevor,0.2180867498,188.177.196.76,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n17710,6,399,2016-12-26 03:55:05,http://jakubowski.name/katelynn,0.1665825133,225.91.79.75,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n17711,6,399,2017-05-24 03:04:32,http://bernhard.name/freda.emard,0.6636047331,130.45.139.97,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n17712,6,399,2017-02-28 06:57:02,http://hammesbernhard.biz/arianna.gutkowski,0.4420130910,127.86.101.186,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n17713,6,399,2017-04-04 22:33:29,http://kirlin.io/zoie.ruel,0.7248452224,172.227.154.21,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n17714,6,399,2017-03-31 23:11:04,http://kutch.biz/humberto,0.7680616119,119.50.122.248,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n17715,6,399,2017-05-12 14:53:01,http://mclaughlin.io/gustave_jacobi,0.7907308132,185.243.6.43,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n17716,6,399,2017-01-02 18:38:22,http://koelpinstoltenberg.io/hanna,0.7855979094,19.185.226.96,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n17717,6,399,2017-04-17 01:40:00,http://hodkiewicz.org/zachery,0.5799441458,219.112.39.43,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n17718,6,399,2017-02-05 11:45:48,http://lehner.co/karina.nitzsche,0.1477900563,196.81.83.147,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n17719,6,399,2017-03-11 09:48:20,http://dubuquepadberg.biz/jeramie.beatty,0.7922005828,218.102.219.74,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n17720,6,399,2017-03-23 03:27:44,http://padberg.com/jaeden.treutel,0.7721796568,85.18.83.58,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n17721,6,399,2017-03-31 22:27:25,http://barton.io/daphnee.crona,0.7749302953,206.61.203.101,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n17722,6,399,2017-04-06 18:49:13,http://barrowsauer.name/taya,0.1781988620,145.175.242.151,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n17723,6,399,2017-05-01 09:31:42,http://huelborer.biz/darion.toy,0.3495630395,47.48.103.106,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n17724,6,399,2017-05-29 00:36:22,http://jerde.info/dedric_shields,0.4598322415,124.87.118.81,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n17725,6,399,2017-01-20 23:12:46,http://sauermclaughlin.name/winnifred_jaskolski,0.6299211104,136.209.169.75,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n17726,6,399,2016-12-29 13:16:29,http://reichertmertz.org/ava,0.0690251814,117.164.100.63,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n17727,6,399,2017-05-07 09:06:30,http://ullrichkunde.org/marge,0.7051249415,194.173.93.155,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n17728,6,399,2017-04-27 05:15:38,http://pollichnicolas.co/raphaelle,0.7314432492,193.162.237.156,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n17729,6,399,2017-05-31 18:05:55,http://heller.co/skye_doyle,0.3643732336,216.102.95.56,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n17730,6,399,2017-03-02 23:18:58,http://cruickshankhauck.co/tyreek,0.7826487759,91.146.127.104,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n17731,6,399,2017-01-18 02:26:54,http://ritchie.biz/tyshawn,0.1766218510,185.76.50.73,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n17732,6,399,2016-12-22 22:38:40,http://roberts.info/kelly_little,0.9709513749,73.234.86.210,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n17733,6,400,2017-03-28 05:43:32,http://kris.info/imelda.gottlieb,0.8618895659,111.163.230.85,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n17734,6,400,2017-06-12 12:21:55,http://medhurst.net/ricardo,0.4964682571,233.112.8.139,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n17735,6,400,2017-02-23 23:34:21,http://kozey.name/betsy_hansen,0.9599432974,117.101.117.115,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n17736,6,400,2017-06-11 12:43:14,http://stokes.biz/jared,0.0391132105,4.76.243.70,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n17737,6,400,2017-04-07 22:38:21,http://dubuque.io/elian.mclaughlin,0.5294712981,121.177.39.254,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n17738,6,400,2017-04-24 00:04:41,http://litteljacobi.info/leland,0.5434395398,217.215.210.20,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n17739,6,400,2017-03-30 08:05:08,http://kuhn.net/megane,0.7045714148,42.242.205.123,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n17740,6,400,2017-02-25 02:45:24,http://harvey.name/mavis.schaden,0.1850798196,155.243.76.40,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n17741,6,400,2017-03-13 22:46:32,http://maggiowunsch.io/eldon,0.3320982580,183.160.168.239,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n17742,6,400,2016-12-26 01:09:47,http://damorepaucek.co/rodrigo,0.8168516603,75.183.97.190,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n4892,2,108,2017-05-23 14:25:01,http://lebsackleffler.co/cleveland_kuhn,,230.236.47.31,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n4893,2,108,2017-01-19 12:25:39,http://ruelmitchell.com/john_gottlieb,,248.36.94.33,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n4894,2,108,2017-01-28 00:14:14,http://ruecker.name/stella,,70.159.107.118,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n4895,2,108,2017-03-02 04:07:23,http://abshireborer.io/sophia,,250.211.109.213,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n4896,2,108,2017-02-11 06:09:34,http://oberbrunner.info/mac.kub,,97.5.9.62,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n4897,2,108,2017-02-12 01:46:08,http://lehnerborer.biz/donavon.maggio,,54.41.117.250,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n4898,2,108,2017-03-14 20:17:42,http://gaylordnitzsche.info/sanford.reilly,,22.245.228.36,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n4899,2,108,2017-02-03 18:24:44,http://volkman.com/aurelio,,168.230.39.120,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n4900,2,108,2016-12-29 01:07:17,http://johnston.info/jocelyn,,109.97.250.156,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n4901,2,108,2017-03-10 10:38:54,http://bergstromblick.net/reggie,,220.203.125.49,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n4902,2,108,2016-12-24 16:03:32,http://hudsonkemmer.co/anne_reichert,,203.48.147.74,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n4903,2,108,2017-02-17 19:24:14,http://anderson.co/dusty.schimmel,,236.215.5.136,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n4904,2,108,2016-12-19 09:43:24,http://haley.biz/ambrose,,233.13.239.67,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n4905,2,108,2017-01-05 19:51:01,http://brakus.co/richie.beahan,,20.48.41.204,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n4906,2,108,2017-05-27 17:31:38,http://schustergerhold.com/dane,,218.180.142.94,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n4907,2,108,2017-05-27 09:41:53,http://labadie.biz/kayla,,51.218.56.101,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n4908,2,108,2016-12-26 05:59:19,http://cremin.name/colton_dare,,51.155.23.18,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n4909,2,108,2017-02-08 02:09:56,http://bergnaum.co/magnus,,187.140.236.231,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n4910,2,108,2017-06-01 15:11:33,http://turcotterobel.co/amya,,225.228.220.171,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n4911,2,108,2017-01-25 13:32:21,http://konopelskiokuneva.net/amanda,,226.76.92.39,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n4912,2,108,2017-06-02 15:33:12,http://moriette.org/hilario.friesen,,238.129.149.173,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n4913,2,108,2017-04-10 17:40:12,http://pollich.name/aleia,,28.199.74.156,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n4914,2,108,2017-04-14 02:01:21,http://borerschneider.com/kamille_gibson,,144.244.34.6,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n4915,2,108,2017-05-24 15:56:53,http://wuckert.io/kevin,,163.206.26.4,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n4916,2,108,2017-05-28 19:12:21,http://kuhic.name/stephen_cronin,,173.44.126.2,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n4917,2,108,2017-04-04 01:09:28,http://mosciskimurphy.com/titus,,85.83.232.71,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n4918,2,108,2017-05-01 02:02:24,http://daniel.co/andy,,243.208.181.103,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n4919,2,108,2017-04-23 21:41:21,http://kundeswift.info/rickey_thiel,,26.249.25.180,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n4920,2,108,2017-03-05 10:41:24,http://nader.io/harry_schulist,,37.161.91.92,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n4921,2,108,2017-01-11 21:40:03,http://jastboehm.io/river,,202.118.129.242,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n4922,2,108,2017-04-29 10:13:01,http://kulashalvorson.net/emelia_glover,,110.14.109.8,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n4923,2,108,2017-05-24 19:05:48,http://gorczanybergstrom.name/della.botsford,,128.228.72.6,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n4924,2,108,2017-05-23 14:14:01,http://kozey.net/jackson,,118.212.64.182,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n4925,2,108,2016-12-30 02:05:41,http://emard.com/alivia,,13.38.119.67,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n4926,2,108,2017-02-03 13:08:33,http://feeney.net/olin,,193.151.121.99,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n4927,2,108,2017-04-18 01:12:27,http://blickgraham.biz/max,,126.222.127.34,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n4928,2,108,2017-03-13 09:23:51,http://thiel.info/daniella_rodriguez,,166.169.229.73,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n4929,2,108,2017-03-31 09:03:56,http://rodriguez.org/sydni,,81.251.115.173,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n4930,2,108,2017-04-05 18:25:43,http://koelpin.org/izabella,,102.213.2.219,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n4931,2,109,2017-06-03 16:06:22,http://bayer.biz/judy,,207.128.15.13,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n4932,2,109,2016-12-26 15:58:08,http://klein.io/donny_roob,,28.231.93.74,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n4933,2,109,2017-01-28 06:41:09,http://grimes.name/kylie,,65.172.251.141,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n4934,2,109,2016-12-30 13:23:59,http://reilly.io/heath,,26.59.233.207,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n4935,2,109,2017-03-23 23:42:35,http://rohan.net/kari_rosenbaum,,126.19.214.74,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n4936,2,109,2017-05-25 19:23:19,http://keeling.info/jillian,,15.179.85.126,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n4937,2,109,2016-12-16 04:56:11,http://ricecronin.name/davin.schultz,,217.21.56.46,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n4938,2,109,2017-02-23 21:44:54,http://bosco.io/camden,,137.35.252.19,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n4939,2,109,2017-02-10 07:12:25,http://willmsmarvin.com/laura,,30.6.99.142,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n4940,2,109,2017-05-02 00:16:59,http://mann.net/korey,,15.44.134.76,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n4941,2,109,2017-04-18 12:05:30,http://cruickshankrogahn.info/grace,,31.182.122.25,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n4942,2,109,2017-01-17 00:08:08,http://moen.net/dahlia,,221.168.40.20,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n4943,2,109,2017-05-30 23:51:04,http://stiedemann.org/dylan.douglas,,39.44.248.150,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n4944,2,109,2017-06-10 18:57:00,http://hanereynolds.info/garth,,223.173.164.173,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n4945,2,109,2017-01-10 08:46:52,http://wehnerbayer.name/serena.hayes,,108.3.77.60,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n4946,2,109,2017-03-25 01:14:53,http://kuhnluettgen.io/dusty,,152.168.173.186,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n4947,2,109,2017-02-21 20:37:30,http://dooleyoberbrunner.org/antonetta,,157.230.26.59,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n17877,7,404,2017-03-02 07:53:08,http://hintzkaulke.com/patsy,,4.95.207.166,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n17878,7,404,2017-05-11 04:49:40,http://schambergerkertzmann.io/foster_cronin,,224.151.54.224,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n17879,7,404,2017-04-30 16:04:38,http://borer.org/nathen,,182.82.177.224,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n17880,7,404,2017-04-05 16:27:53,http://kutch.com/hildegard_mayert,,199.225.82.114,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n17881,7,404,2017-03-01 02:01:58,http://skiles.biz/ferne,,214.189.90.53,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n17882,7,404,2016-12-20 18:05:39,http://wiegand.info/frederick_barton,,10.93.242.239,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n17883,7,404,2017-04-27 22:33:00,http://stiedemann.biz/cleo.beahan,,139.34.179.61,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n17884,7,404,2017-03-09 12:43:12,http://walker.name/francisca,,204.28.174.247,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n17885,7,405,2017-04-06 03:56:19,http://crona.org/maude.sauer,,186.185.46.13,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n17886,7,405,2017-06-05 19:15:21,http://grantspencer.info/javon,,176.5.49.187,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n17887,7,405,2017-05-14 09:18:55,http://rutherford.biz/kevon.ferry,,180.40.15.86,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n17888,7,405,2017-02-07 19:29:11,http://lemkeparisian.org/rita,,219.214.40.76,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n17889,7,405,2016-12-17 00:46:30,http://wizagrady.io/wyatt,,187.47.117.16,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n17890,7,405,2016-12-30 08:54:25,http://rowe.net/audie,,130.214.5.240,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n17891,7,405,2017-05-05 19:50:20,http://carrollbergnaum.name/winifred,,253.242.19.164,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n17892,7,405,2016-12-13 16:02:32,http://wilderman.com/omari,,235.142.192.82,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n17893,7,405,2017-05-19 17:08:48,http://connellywhite.io/weldon.hane,,98.231.38.78,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n17894,7,405,2017-04-05 23:56:24,http://hand.org/jeanette,,191.91.125.128,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n17895,7,405,2017-02-28 19:46:31,http://klingconroy.info/melba,,82.139.163.136,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n17896,7,405,2016-12-17 20:46:31,http://hickle.com/vergie.hammes,,86.177.92.12,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n17897,7,405,2017-02-06 07:29:23,http://schmelerokuneva.net/bryce,,20.21.99.225,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n17898,7,405,2017-01-13 22:03:27,http://labadie.biz/dane,,75.169.201.178,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n17899,7,405,2017-02-02 16:38:58,http://creminabshire.info/katelynn,,211.54.3.129,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n17900,7,405,2017-02-17 09:18:54,http://quitzonokon.name/dandre,,37.181.39.116,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n17901,7,405,2017-02-07 20:05:59,http://gulgowski.biz/trace_stehr,,5.161.175.177,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n17902,7,405,2017-04-20 21:54:58,http://rueckerrice.net/celine,,93.185.115.39,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n17903,7,405,2017-06-08 13:54:45,http://collins.biz/aubree_romaguera,,130.33.65.231,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n17904,7,405,2017-01-21 21:38:26,http://welchfisher.net/ivah,,16.80.18.86,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n17905,7,405,2016-12-22 09:32:26,http://hicklebayer.io/pietro,,232.91.124.23,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n17906,7,405,2017-03-27 23:58:49,http://orn.org/rubye.zboncak,,178.175.129.5,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n17907,7,405,2017-05-03 12:48:34,http://rempel.info/winnifred,,8.80.40.25,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n17908,7,405,2017-03-18 14:02:13,http://stamm.info/juliet,,145.4.154.143,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n17909,7,405,2017-06-07 04:55:49,http://nicolatehr.info/kennith_stehr,,92.98.93.198,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n17910,7,405,2017-05-12 13:17:41,http://christiansen.info/angeline,,250.21.142.60,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n17911,7,405,2017-03-07 04:28:41,http://ullrich.io/colby.glover,,117.251.168.190,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n17912,7,405,2017-05-19 18:46:38,http://jast.org/creola,,232.90.188.69,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n17913,7,405,2017-03-16 18:47:38,http://johnston.com/cordie.konopelski,,70.204.153.156,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n17914,7,405,2017-01-30 18:31:10,http://eichmannheathcote.co/lavonne_hilpert,,207.179.178.251,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n17915,7,405,2017-01-16 03:37:55,http://bechtelar.net/kaleigh,,65.150.131.253,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n17916,7,405,2017-02-25 04:26:10,http://konopelski.com/liam.bernhard,,204.137.68.63,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n17917,7,405,2017-03-22 20:04:49,http://brakus.biz/lora,,175.37.228.231,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n17918,7,405,2017-02-08 00:28:55,http://hermann.io/kim.larkin,,86.195.166.77,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n17919,7,405,2017-04-26 19:09:51,http://kulas.name/lawson_davis,,137.137.118.13,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n17920,7,405,2017-04-11 13:49:04,http://howell.info/daniella,,9.73.244.104,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n17921,7,405,2017-03-10 09:52:45,http://stiedemann.net/tillman,,71.235.63.148,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n17922,7,405,2017-05-16 02:41:58,http://williamsonhalvorson.biz/rachael,,32.202.148.141,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n17923,7,405,2017-03-29 20:06:16,http://bechtelar.org/meggie_steuber,,196.159.31.227,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n17924,7,405,2017-06-04 15:46:52,http://dooley.org/lacey,,78.49.214.77,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n17925,7,405,2017-03-09 14:04:59,http://skiles.name/jazlyn.bogisich,,122.208.144.46,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n17926,7,405,2017-05-03 16:07:36,http://miller.co/verla.leffler,,133.223.40.136,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n17927,7,405,2017-05-16 13:51:24,http://kunze.com/oleta.dietrich,,253.5.140.214,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n17928,7,405,2017-05-21 10:30:36,http://fisher.io/elva_kozey,,69.43.16.74,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n17929,7,405,2017-04-25 23:54:49,http://stanton.name/rylan_sanford,,118.168.101.240,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n17930,7,405,2017-04-20 08:44:34,http://swaniawskijacobson.name/vernon.crist,,53.53.16.84,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n17931,7,405,2017-01-21 17:23:33,http://hammes.com/nelson,,43.107.213.230,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n14754,5,333,2017-01-14 05:52:16,http://klockowelch.name/hunter,0.0154178608,139.44.2.22,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n14755,5,333,2017-03-30 21:39:50,http://streich.info/gaston,0.8708701607,164.176.157.156,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n14756,5,333,2017-01-11 20:24:39,http://kling.io/ted,0.8075684005,149.63.211.236,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n14757,5,333,2016-12-13 14:18:21,http://emard.net/cristian,0.0583404723,85.109.248.121,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n14758,5,333,2017-03-09 06:58:12,http://smith.biz/houston,0.4978359865,143.5.31.19,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n14759,5,333,2017-06-12 07:50:30,http://homenickoconnell.net/monte.wilderman,0.5342460037,53.83.144.11,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n14760,5,333,2016-12-28 17:20:05,http://klein.biz/janiya.ratke,0.0449911030,30.99.34.88,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n14761,5,333,2017-03-10 10:26:29,http://weber.com/montana.conn,0.2030414064,9.205.247.63,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n14762,5,333,2017-06-02 04:10:35,http://crooks.co/imelda,0.4099687776,68.208.151.69,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n14763,5,333,2017-01-20 14:26:17,http://blanda.org/flavie.bartell,0.2029236640,101.219.29.123,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n14764,5,333,2017-01-25 04:35:48,http://hickle.co/kane_windler,0.9427166483,191.35.104.19,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n14765,5,333,2017-02-27 20:03:35,http://olson.com/cole,0.5378219988,239.222.97.17,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n14766,5,333,2017-02-28 01:48:05,http://feest.biz/palma,0.1382801516,76.158.153.165,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n14767,5,333,2017-03-11 15:47:04,http://reynoldsgottlieb.biz/nikko_von,0.6161831292,156.39.130.201,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n14768,5,333,2017-05-30 05:04:15,http://nader.net/corbin,0.3836002490,44.120.13.69,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n14769,5,333,2017-04-11 06:19:10,http://lind.info/leanna,0.2151647470,37.212.25.100,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n14770,5,333,2017-06-05 09:30:36,http://lemkekutch.info/lindsay.hane,0.4226778667,18.85.35.138,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n14771,5,333,2017-05-29 18:04:46,http://bogisich.com/noah_hagenes,0.0037660155,105.85.244.34,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n14772,5,333,2017-05-25 03:41:34,http://windler.org/napoleon,0.5209924730,133.147.52.11,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n14773,5,333,2017-03-25 14:08:07,http://kochmoore.net/santiago.harber,0.9788932245,16.171.192.72,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n14774,5,333,2017-03-11 18:27:08,http://danieltromp.co/koby.jakubowski,0.7570235082,207.120.129.221,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n14775,5,333,2016-12-31 02:58:51,http://mckenzielang.net/kaylin,0.0518388555,113.93.189.103,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n14776,5,333,2017-01-23 05:24:14,http://kovacek.net/abdul.hudson,0.9852601542,135.139.12.4,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n14777,5,333,2017-03-30 22:02:44,http://conroymuller.io/jevon.moore,0.3566116725,250.18.198.254,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n14778,5,333,2017-06-04 23:50:21,http://smith.co/rozella,0.0460518910,96.173.131.44,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n14779,5,333,2016-12-28 05:20:12,http://waelchi.io/aurelia.ratke,0.0207261167,68.43.186.221,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n14780,5,333,2017-01-27 08:51:21,http://upton.com/danny,0.0967959936,29.189.35.213,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n14781,5,333,2017-06-13 13:21:50,http://sawayn.biz/wilma,0.2386106715,50.229.229.20,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n14782,5,333,2016-12-20 13:02:45,http://zulauf.org/noemi_vandervort,0.7153790696,184.214.121.59,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n14783,5,333,2017-04-27 01:07:46,http://kuvalisbahringer.biz/leanne,0.5575310801,14.119.162.42,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n14784,5,333,2017-02-27 18:28:09,http://feestcummerata.biz/meredith,0.2924126158,147.100.77.151,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n14785,5,333,2017-02-25 12:59:23,http://bauchstiedemann.name/mustafa,0.3821085634,245.141.137.32,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n14786,5,333,2017-02-27 17:59:58,http://reichel.co/annetta.kuphal,0.7414083995,91.189.193.223,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n14787,5,333,2017-05-19 12:18:12,http://price.com/katherine,0.8605903582,50.144.221.180,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n14788,5,333,2017-04-02 04:28:25,http://aufderhar.com/isabella_kuhic,0.6096960457,240.196.199.100,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n14789,5,333,2017-05-04 10:06:08,http://funk.io/jeromy,0.7788973814,24.184.140.205,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n14790,5,333,2017-02-03 13:20:15,http://corwin.com/maddison_bayer,0.4826985890,101.168.132.200,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n14791,5,333,2017-01-30 11:33:31,http://emard.info/devonte.weinat,0.7773630482,104.214.82.115,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n14792,5,333,2017-05-28 12:26:44,http://orn.info/jacques,0.4559780094,137.157.18.105,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n14793,5,333,2016-12-21 05:19:00,http://metzwill.biz/gwendolyn,0.4013447100,212.76.247.86,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n14794,5,333,2017-04-25 22:55:20,http://boscocain.org/giovani,0.8530469523,31.114.203.209,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n14795,5,333,2017-04-17 12:12:23,http://crooks.biz/tillman,0.0509182051,130.127.173.61,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n14796,5,333,2017-01-25 21:39:58,http://bechtelar.biz/mina,0.9136161018,248.171.118.135,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n14797,5,333,2017-04-29 12:44:06,http://jakubowski.co/jose.lueilwitz,0.9581178118,85.42.120.238,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n14798,5,333,2017-02-17 20:19:43,http://schowaltercarter.org/katlynn_schaden,0.9526559837,184.36.216.134,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n14799,5,333,2017-04-09 03:49:22,http://balistreribrakus.name/ashleigh.willms,0.5117037104,244.197.181.75,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n14800,5,333,2017-06-08 14:01:18,http://emmerich.io/luna_heaney,0.7630292558,32.189.43.141,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n14801,5,333,2017-03-26 08:09:37,http://osinski.biz/gustave_heathcote,0.6506484451,157.108.201.105,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n14802,5,333,2017-03-05 23:49:01,http://rennerjohns.info/kole_oconner,0.7114171727,87.205.253.245,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n14803,5,333,2017-02-18 04:19:28,http://prohaska.name/ladarius_goldner,0.9320423016,45.193.206.106,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n14804,5,333,2017-05-12 14:48:16,http://upton.com/arden_altenwerth,0.8576073391,207.216.91.177,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n17743,6,400,2017-04-27 07:58:23,http://nader.name/bruce,0.3003676385,194.109.224.58,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n17744,6,400,2017-04-08 23:20:07,http://lubowitz.info/jarrett,0.8507836526,200.168.56.221,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n17745,6,400,2017-06-07 02:07:40,http://leannon.name/doug_kirlin,0.9505358963,26.121.199.238,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n17746,6,400,2017-03-21 04:16:16,http://oharakeler.info/ford,0.8279375320,244.200.17.135,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n17747,6,400,2017-01-12 04:56:17,http://walker.com/bailey.sanford,0.4780100794,129.126.44.67,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n17748,6,400,2016-12-29 18:13:26,http://treutel.co/zaria.terry,0.5467226349,89.56.95.111,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n17749,6,400,2017-01-29 15:19:39,http://muellerdonnelly.net/treva_greenholt,0.8703019439,130.199.143.120,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n17750,6,400,2017-02-18 01:13:35,http://altenwerthko.com/chester.cartwright,0.2813976903,39.212.242.12,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n17751,6,400,2017-01-14 13:11:20,http://rolfsonchamplin.info/daron_bauch,0.9605289256,248.71.161.172,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n17752,6,400,2017-03-25 10:21:48,http://cummingsrippin.net/mina_little,0.4951174532,86.96.198.53,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n17753,6,400,2017-06-13 13:18:56,http://sengerrath.info/alena_pfannerstill,0.5866918858,244.59.12.15,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n17754,6,400,2017-04-22 12:21:22,http://kuboreilly.name/weldon,0.0925881710,184.169.139.96,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n17755,6,400,2017-02-11 01:29:57,http://ornrosenbaum.info/margarett,0.1819895889,51.181.53.184,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n17756,6,400,2017-04-04 12:06:08,http://goodwin.info/francisca_jerde,0.2685873593,96.77.157.53,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n17757,6,400,2017-05-30 06:31:25,http://raynorgreenholt.org/ahmad_damore,0.5249470828,98.177.96.242,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n17758,6,400,2017-01-01 02:44:05,http://welch.net/leonora.runolfon,0.7585016104,52.41.149.15,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n17759,6,400,2017-06-08 18:50:41,http://fisher.net/mayra,0.3527328745,47.205.40.106,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n17760,6,400,2017-02-08 20:43:54,http://stracke.com/sigrid_johnson,0.4545747213,162.46.173.6,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n17761,6,400,2017-05-29 19:04:19,http://hilpert.io/carter,0.4308062028,128.176.35.221,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n17762,6,400,2017-02-07 21:31:01,http://frami.com/nikolas_brekke,0.2367161771,122.157.106.84,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n17763,6,401,2017-04-24 18:57:04,http://wyman.info/emery_pollich,0.5546329172,108.219.66.203,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n17764,6,401,2017-01-24 20:18:18,http://halvorson.net/emma_dubuque,0.1738263348,107.73.84.183,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n17765,6,401,2017-01-01 10:47:31,http://von.com/ulises_thiel,0.1254377386,5.185.83.189,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n17766,6,401,2017-03-28 20:02:28,http://herzogsporer.org/jeffery,0.0601743306,8.154.252.67,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n17767,6,401,2017-01-17 17:40:56,http://wittingsmitham.com/mark.pfeffer,0.1831998726,78.170.75.215,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n17768,6,401,2017-05-17 14:01:16,http://willkuhn.info/angelo,0.6452071309,55.127.226.44,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n17769,6,401,2017-03-18 17:42:02,http://cruickshank.co/sophia,0.4894272524,212.22.121.168,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n17770,6,401,2017-02-22 13:03:14,http://lockman.info/rickie.bergnaum,0.4483870541,154.75.57.21,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n17771,6,401,2017-06-09 07:47:10,http://zulauf.co/isabella.fisher,0.9687829019,237.90.15.6,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n17772,6,401,2017-04-18 01:49:30,http://emard.name/pierce_konopelski,0.8060151489,134.52.44.135,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n17773,6,401,2017-06-07 11:48:32,http://hayesdickinson.co/antonia,0.6378363851,2.131.189.190,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n17774,6,401,2017-03-31 05:03:24,http://millsbrown.net/stephen,0.9547896807,51.214.117.239,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n17775,6,401,2017-03-05 12:14:44,http://adamsgutkowski.info/brandon,0.2213598236,115.22.128.216,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n17776,6,401,2017-06-07 20:50:57,http://conn.co/breana,0.1111170384,56.8.135.36,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n17777,6,401,2016-12-30 02:42:13,http://schmitt.com/carmen_gleichner,0.1571452230,235.136.145.89,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n17778,6,401,2017-02-08 06:18:43,http://kihn.biz/gordon,0.0813262319,121.221.29.48,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n17779,6,401,2017-05-31 17:03:20,http://stroman.org/rowland,0.1802823323,212.29.20.228,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n17780,6,401,2017-01-14 03:53:11,http://schimmel.io/joey_crist,0.6316644878,55.5.73.240,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n17781,6,401,2017-02-08 08:54:52,http://nicolas.name/giuseppe_pfannerstill,0.4368183312,164.168.83.123,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n17782,6,401,2017-02-22 17:37:00,http://rosenbaum.info/asia_konopelski,0.6610643288,139.233.234.201,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n17783,6,401,2017-04-21 02:31:59,http://brownfritsch.net/celine,0.2342171793,59.28.36.74,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n17784,6,401,2017-03-29 13:11:16,http://towneschaden.biz/nash,0.5031444857,11.208.161.244,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n17785,6,401,2017-01-04 08:58:06,http://halvorson.name/janice_king,0.3999343641,57.89.175.68,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n17786,6,401,2017-02-20 03:22:27,http://dach.co/fredrick_little,0.5738276300,135.20.249.64,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n17787,6,401,2017-03-15 16:43:21,http://walter.info/estella_nikolaus,0.9257807631,242.40.96.161,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n17788,6,401,2017-01-12 03:40:58,http://cruickshank.biz/nia,0.5841908217,123.110.149.230,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n17789,6,401,2017-02-03 20:00:14,http://mitchellohara.biz/berniece.predovic,0.2684177130,113.133.96.131,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n17790,6,401,2017-04-26 22:35:39,http://thompsoncollins.com/juston,0.7855468969,12.25.19.239,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n17791,6,401,2017-06-09 19:29:29,http://ruel.biz/xavier_fritsch,0.5996875793,43.10.172.9,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n17792,6,401,2016-12-28 19:43:54,http://tromp.co/christina,0.3220469112,64.200.233.96,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n17793,6,401,2017-04-13 02:26:34,http://rogahn.name/kurt.zboncak,0.6836646779,66.4.236.10,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n4948,2,109,2017-06-09 22:12:24,http://ziemann.org/carlotta,,143.19.233.5,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n4949,2,109,2017-05-17 18:42:21,http://dickens.org/vivienne,,109.219.155.217,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n4950,2,109,2017-01-12 06:34:28,http://mclaughlinhintz.name/rosie,,167.124.247.178,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n4951,2,109,2017-03-23 18:34:40,http://quigleyschmidt.net/jordane_hermann,,102.171.12.159,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n4952,2,109,2017-01-10 19:55:33,http://ruecker.biz/tanya,,140.239.36.98,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n4953,2,109,2016-12-19 18:57:57,http://swift.co/roel,,221.33.219.79,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n4954,2,109,2017-01-27 06:35:26,http://turner.net/mose.treutel,,80.30.191.128,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n4955,2,109,2017-03-22 19:55:33,http://larson.info/kelsi,,155.158.55.212,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n4956,2,109,2016-12-20 17:56:24,http://wintheisermckenzie.com/henri,,61.18.136.96,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n4957,2,109,2017-05-24 15:55:15,http://turcotte.biz/emelie,,172.202.28.41,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n4958,2,109,2017-03-09 20:16:05,http://casper.net/meggie,,212.112.4.96,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n4959,2,109,2017-05-09 16:13:22,http://haag.co/enid,,205.188.110.25,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n4960,2,109,2017-02-01 05:43:56,http://turner.co/ralph.wuckert,,56.79.109.238,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n4961,2,109,2016-12-29 03:43:11,http://schoenemmerich.org/joshuah,,109.236.133.147,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n4962,2,109,2017-03-20 04:45:06,http://lemke.com/guadalupe,,123.254.92.198,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n4963,2,109,2017-01-31 13:49:13,http://goodwin.io/antonina,,233.147.78.99,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n4964,2,109,2017-02-22 22:50:51,http://zboncakhuels.co/marshall,,207.92.80.91,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n4965,2,109,2017-04-03 16:28:55,http://bartoletti.biz/sanford.zulauf,,230.188.210.212,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n4966,2,109,2017-06-09 04:29:50,http://mclaughlinmoen.com/anabel.johnston,,21.42.164.20,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n4967,2,109,2017-05-11 02:10:35,http://connelly.co/jasmin.predovic,,228.117.232.137,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n4968,2,110,2017-05-20 05:14:39,http://hoppe.io/jordy,,168.154.20.136,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n4969,2,110,2017-01-24 03:19:03,http://brekkeabernathy.io/victor,,166.108.231.88,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n4970,2,110,2017-04-09 19:46:35,http://hagenesturner.net/adrianna_keler,,202.131.110.33,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n4971,2,110,2017-01-31 03:42:22,http://wiegandcole.io/jake,,150.147.135.200,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n4972,2,110,2017-02-28 13:25:13,http://coleeichmann.info/sally,,105.37.107.23,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n4973,2,110,2017-01-19 17:04:37,http://kozeyryan.info/dusty_beatty,,195.156.122.250,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n4974,2,110,2017-05-23 04:44:47,http://gusikowskimedhurst.biz/ariel,,51.98.222.45,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n4975,2,110,2017-06-01 00:41:29,http://batz.net/cooper,,172.227.89.211,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n4976,2,110,2017-05-31 00:22:10,http://treutel.io/toni,,147.13.127.134,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n4977,2,110,2017-03-06 23:44:54,http://gottliebmosciski.net/marcelle.bayer,,53.229.12.252,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n4978,2,110,2017-04-05 03:23:57,http://heathcote.biz/neil,,176.3.131.10,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n4979,2,110,2017-03-07 22:18:18,http://conroy.biz/ellen.goldner,,112.72.87.98,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n4980,2,110,2017-06-07 09:46:42,http://reilly.net/alexys_tromp,,15.16.23.54,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n4981,2,110,2017-01-15 09:16:56,http://rathprosacco.net/javon,,145.91.117.182,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n4982,2,110,2017-04-26 09:29:03,http://harber.com/coty_rogahn,,2.101.213.2,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n4983,2,110,2017-01-24 14:19:37,http://lehner.io/travis.balistreri,,76.143.172.140,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n4984,2,110,2017-03-20 05:18:12,http://roobokeefe.org/casimir,,18.107.200.153,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n4985,2,110,2017-06-07 03:26:47,http://ebert.io/greyson,,22.110.67.173,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n4986,2,110,2017-06-04 20:50:03,http://walsh.info/gilbert,,174.83.95.241,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n4987,2,110,2016-12-20 17:16:07,http://waters.co/bethel_leffler,,248.72.117.4,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n4988,2,110,2017-05-25 11:23:02,http://wardhahn.co/marilie,,227.73.236.166,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n4989,2,110,2016-12-25 16:28:48,http://durgan.org/clair,,226.80.81.120,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n4990,2,110,2017-03-13 08:04:13,http://kerluke.info/valentin,,115.190.18.162,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n4991,2,110,2016-12-20 22:41:46,http://bechtelar.io/araceli,,25.74.221.10,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n4992,2,110,2017-03-24 12:25:21,http://vonruedenborer.biz/samson,,196.113.113.59,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n4993,2,110,2017-04-27 20:05:49,http://simonis.biz/gerard_cormier,,56.74.181.71,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n4994,2,110,2016-12-24 12:32:57,http://ondricka.org/alvena,,97.113.184.149,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n4995,2,110,2017-05-10 17:12:05,http://pfannerstill.name/demond,,142.100.208.189,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n4996,2,110,2017-02-08 03:57:48,http://stehrjerde.name/cheyanne.purdy,,193.130.25.235,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n4997,2,110,2017-05-31 06:41:07,http://oreilly.name/lionel,,119.91.95.155,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n4998,2,110,2017-01-12 11:30:59,http://legros.co/trevor.jaskolski,,51.13.222.216,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n4999,2,111,2017-06-06 16:04:26,http://harrisleannon.co/name_gleichner,,184.222.170.68,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n5000,2,111,2017-05-29 23:11:52,http://stiedemannquitzon.biz/margie,,128.49.186.77,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n5001,2,111,2017-05-05 20:39:35,http://ruel.biz/maye,,94.194.153.170,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n5002,2,111,2016-12-29 03:21:59,http://beahanoberbrunner.co/evelyn.jenkins,,167.32.188.67,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n5003,2,111,2017-05-02 09:34:13,http://hoppe.info/verdie_johnston,,204.142.56.177,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n17932,7,405,2017-02-13 09:30:12,http://boyer.io/loraine_hyatt,,245.142.18.150,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n17933,7,405,2017-01-05 15:23:22,http://dibbertterry.org/elinore.hudson,,129.10.115.43,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n17934,7,405,2017-01-11 01:54:14,http://bartoletti.com/dorcas,,27.123.158.220,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n17935,7,405,2017-05-01 14:29:00,http://keeblerratke.com/titus,,32.83.224.145,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n17936,7,405,2016-12-16 16:34:26,http://conn.co/josue.ledner,,158.6.46.191,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n17937,7,405,2017-01-08 22:54:58,http://johns.info/chasity,,49.249.131.208,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n17938,7,405,2017-04-22 23:08:10,http://rohan.biz/ora,,199.254.85.55,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n17939,7,405,2017-04-29 21:59:34,http://breitenberg.co/else,,115.50.28.174,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n17940,7,405,2017-04-27 20:16:51,http://lemkelowe.info/charley_walter,,37.62.108.202,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n17941,7,406,2017-06-04 17:33:29,http://carroll.co/jee,,244.103.155.237,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n17942,7,406,2017-05-30 07:59:26,http://ornjenkins.info/mauricio,,82.229.199.119,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n17943,7,406,2017-06-12 09:22:22,http://predovic.com/micaela,,120.77.177.12,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n17944,7,406,2017-02-22 13:24:54,http://keeblerborer.info/enrique,,178.40.32.158,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n17945,7,406,2017-04-23 11:29:16,http://strosinokuneva.biz/jefferey,,103.135.20.35,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n17946,7,406,2017-04-16 17:43:41,http://bogankeeling.com/mabel.sawayn,,37.236.39.142,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n17947,7,406,2017-06-01 08:01:07,http://mante.name/wilmer.spinka,,125.180.129.124,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n17948,7,406,2017-02-07 18:57:49,http://williamson.co/petra.keler,,136.186.251.79,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n17949,7,406,2017-06-12 17:53:33,http://bartoletti.co/roberta_kozey,,54.210.65.24,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n17950,7,406,2017-05-22 16:54:21,http://ullrich.com/emilio.weber,,190.162.214.157,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n17951,7,406,2017-02-12 23:36:24,http://bruen.com/chandler_feeney,,37.111.151.183,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n17952,7,406,2017-02-03 10:04:41,http://leuschke.org/celine,,17.178.172.133,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n17953,7,406,2017-01-21 09:28:12,http://lynchkoch.co/delaney.renner,,229.40.182.27,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n17954,7,406,2017-01-22 14:50:52,http://hagenes.biz/reid,,243.99.30.230,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n17955,7,406,2017-02-11 09:56:25,http://ward.io/caandra_deckow,,48.174.233.34,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n17956,7,406,2017-03-01 01:09:51,http://ferrylind.name/ella,,57.88.60.122,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n17957,7,406,2017-05-23 07:00:27,http://walshwilkinson.net/karine,,11.24.94.48,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n17958,7,406,2017-02-18 20:17:06,http://osinskireichel.org/albin_schultz,,179.240.160.161,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n17959,7,406,2016-12-21 15:24:03,http://prosaccowintheiser.io/javonte,,77.10.223.24,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n17960,7,406,2017-03-05 10:51:57,http://grantheathcote.com/lisandro,,128.184.108.234,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n17961,7,406,2017-05-27 05:06:45,http://glover.name/dereck.pfannerstill,,126.149.107.70,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n17962,7,406,2016-12-20 15:27:58,http://kreiger.io/mack,,103.187.87.252,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n17963,7,406,2017-02-09 12:26:47,http://hicklebailey.name/lesly,,97.43.143.189,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n17964,7,406,2017-05-17 12:15:00,http://donnelly.org/louie,,222.46.18.191,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n17965,7,406,2017-03-22 16:25:02,http://senger.co/johnson_brakus,,152.11.101.17,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n17966,7,406,2017-06-03 14:11:44,http://bayerbogisich.name/wade.mayer,,116.151.109.86,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n17967,7,406,2017-05-15 14:19:18,http://barrows.info/clemens,,46.122.43.36,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n17968,7,406,2017-06-13 23:52:59,http://haley.org/arch,,75.94.174.49,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n17969,7,406,2017-06-12 03:00:09,http://schmitt.com/garett,,216.34.141.27,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n17970,7,406,2017-02-07 23:29:03,http://romagueraabernathy.org/sadie.osinski,,103.120.221.150,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n17971,7,406,2017-01-20 06:44:59,http://kuhlman.io/sigrid.haley,,71.196.198.3,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n17972,7,406,2016-12-23 20:36:43,http://langoshkemmer.io/armando,,181.5.222.30,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n17973,7,406,2017-04-26 17:04:43,http://rice.net/brittany_heller,,15.222.57.57,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n17974,7,406,2017-03-24 06:52:54,http://weberlang.io/lou_keebler,,79.100.60.20,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n17975,7,406,2017-04-05 00:45:28,http://bernier.io/nathen.bailey,,228.214.149.206,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n17976,7,407,2017-01-19 10:39:13,http://okeefecollins.info/evelyn,,212.185.63.153,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n17977,7,407,2016-12-27 09:02:03,http://nitzschekeeling.info/cruz_kiehn,,245.106.124.240,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n17978,7,407,2017-02-16 10:29:17,http://kautzer.name/guillermo,,185.106.84.72,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n17979,7,407,2017-04-06 09:11:57,http://zulauf.biz/darrel_bernhard,,70.153.42.119,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n17980,7,407,2017-04-30 18:27:42,http://harvey.biz/micaela_hyatt,,242.3.58.204,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n17981,7,407,2017-05-26 02:52:16,http://aufderhar.name/dax,,50.82.184.112,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n17982,7,407,2017-01-04 02:10:38,http://jakubowski.info/giovanna_kovacek,,250.137.218.215,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n17983,7,407,2017-05-23 21:30:31,http://schuppekonopelski.info/josefina,,132.111.203.177,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n17984,7,407,2017-06-10 23:42:15,http://cain.name/garett,,248.67.23.174,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n17985,7,407,2017-05-05 08:39:03,http://deckowhirthe.biz/johann,,123.179.228.183,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n17986,7,407,2017-03-18 09:08:29,http://kuhic.net/kenyatta,,92.77.147.24,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n14805,5,333,2017-04-06 16:27:40,http://goyettelangosh.org/brenda,0.8949632230,163.84.95.34,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n14806,5,333,2017-01-14 19:01:08,http://ortizschaefer.io/jordy,0.0110467469,50.244.139.197,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n14807,5,333,2017-06-07 18:52:42,http://swift.biz/wendy,0.0789015432,69.140.179.241,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n14808,5,333,2017-06-13 19:20:10,http://murazik.info/nelda,0.2228829319,213.189.157.111,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n14809,5,333,2017-03-20 00:36:51,http://fay.info/yazmin.jacobi,0.2950804805,209.22.209.155,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n14810,5,333,2017-01-04 21:29:03,http://mcdermott.name/parker_douglas,0.3266341490,217.5.91.244,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n14811,5,333,2017-01-13 11:10:39,http://thiel.info/javier,0.5767643664,145.155.56.143,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n14812,5,333,2017-05-03 07:08:59,http://toy.co/muhammad,0.7387387242,77.226.89.93,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n14813,5,333,2017-05-23 04:36:56,http://ruecker.info/katlyn,0.0520191437,150.179.108.18,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n14814,5,333,2017-03-06 09:20:01,http://wildermanleannon.info/sonya,0.3097641996,94.44.211.212,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n14815,5,334,2017-04-29 07:18:10,http://haagmckenzie.io/mollie.nicolas,0.9263457139,43.189.95.77,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n14816,5,334,2017-03-04 07:16:41,http://haagko.io/myrna_roob,0.1164561007,181.16.193.236,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n14817,5,334,2017-05-06 20:06:08,http://orn.info/marina.stracke,0.6463184422,231.230.75.97,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n14818,5,334,2017-01-02 20:32:58,http://ernserharber.io/juston,0.0195307896,231.17.151.109,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n14819,5,334,2016-12-29 00:44:12,http://abernathywolff.info/charity_koch,0.5188536161,253.145.89.21,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n14820,5,334,2017-04-19 08:09:22,http://parisianmann.io/ayla.spencer,0.7902000115,11.85.204.213,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n14821,5,334,2017-06-06 21:15:34,http://howe.info/myrtie,0.6716637325,163.77.34.206,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n14822,5,334,2017-03-12 16:05:44,http://deckow.net/richmond.moore,0.4266917538,248.216.44.124,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n14823,5,334,2017-01-25 06:05:40,http://zieme.net/ardith_crist,0.6683800024,179.6.43.201,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n14824,5,334,2016-12-25 07:00:11,http://hermiston.info/ike,0.1696066291,190.137.74.239,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n14825,5,334,2017-03-31 22:21:15,http://bahringerparisian.co/jensen,0.2092787585,8.67.4.222,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n14826,5,334,2017-03-07 01:06:19,http://hirtheoconner.org/eric_vonrueden,0.8809697345,168.203.249.241,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n14827,5,334,2017-05-03 19:02:06,http://padberg.co/hoyt_klocko,0.7817733038,250.115.190.203,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n14828,5,334,2017-03-05 14:36:16,http://dicki.name/cole_homenick,0.5319859108,14.127.182.163,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n14829,5,334,2017-02-20 15:33:05,http://hoppe.com/braulio.mraz,0.0023731605,164.203.251.179,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n14830,5,334,2017-04-22 17:57:06,http://padberg.info/delfina.keler,0.4303281742,45.173.2.85,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n14831,5,334,2016-12-14 15:51:30,http://jacobi.io/joan,0.4517379684,46.222.21.224,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n14832,5,334,2017-01-27 21:41:39,http://oberbrunner.name/ernesto,0.1946967689,180.75.132.57,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n14833,5,334,2017-03-15 01:02:59,http://hettinger.name/bertrand,0.7095158243,31.216.236.211,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n14834,5,334,2017-05-27 20:24:15,http://dooley.co/francisca,0.0348833838,99.207.66.134,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n14835,5,334,2017-01-20 17:13:40,http://miller.net/caesar,0.9152311706,227.214.31.50,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n14836,5,334,2017-06-06 07:08:22,http://altenwerth.com/toney.rodriguez,0.4847720637,232.105.120.157,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n14837,5,334,2017-01-13 21:26:55,http://wiza.name/levi.lehner,0.8922165978,184.59.140.100,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n14838,5,334,2017-04-06 18:33:05,http://gottlieb.co/wellington,0.3041502207,167.137.220.218,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n14839,5,334,2017-01-15 19:52:41,http://schumm.org/meda,0.7439553285,42.106.31.25,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n14840,5,334,2017-03-07 12:27:22,http://thompson.name/rhiannon_davis,0.3539875599,195.52.211.99,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n14841,5,334,2017-04-05 04:20:39,http://lemke.com/kieran,0.7833709896,19.232.132.228,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n14842,5,334,2016-12-31 18:38:13,http://murphy.net/christa,0.0422766943,10.205.129.213,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n14843,5,334,2017-04-02 00:57:10,http://turner.biz/adolf_nikolaus,0.3596844914,195.30.231.237,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n14844,5,334,2016-12-20 13:04:00,http://cronarogahn.info/willis.littel,0.8173389743,170.178.116.2,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n14845,5,334,2016-12-24 14:46:09,http://ryan.net/nolan.osinski,0.0959474252,52.222.17.72,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n14846,5,334,2017-02-25 15:55:41,http://homenickhintz.com/elwyn_frami,0.9084830013,4.69.91.189,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n14847,5,334,2017-05-05 03:01:11,http://hirthemaggio.info/mariana_langworth,0.8874152858,183.178.143.101,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n14848,5,334,2017-06-07 09:22:44,http://erdman.info/kailee_keler,0.0066674758,181.12.199.21,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n14849,5,334,2017-03-13 00:45:23,http://cronastroman.org/alfonzo.jast,0.3195654580,173.190.22.97,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n14850,5,334,2016-12-22 03:35:26,http://joneshagenes.org/ernestina,0.5646248772,216.195.127.96,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n14851,5,334,2017-03-30 14:48:50,http://heathcote.biz/justen,0.7844926310,230.208.99.207,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n14852,5,334,2017-03-16 04:32:14,http://harris.com/horacio,0.5438370984,61.123.204.241,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n14853,5,334,2017-05-29 07:30:45,http://macgyver.biz/elliott.metz,0.0481887497,149.102.182.30,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n14854,5,334,2017-01-11 05:43:32,http://schillerbuckridge.net/khalid_stokes,0.5003269028,187.152.184.104,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n14855,5,334,2017-04-15 17:53:14,http://lesch.io/zaria_mckenzie,0.9220832370,46.215.77.190,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n14856,5,334,2017-05-24 23:44:05,http://paucek.info/stevie.ko,0.9285035236,163.184.161.23,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n17794,6,401,2017-04-09 19:08:13,http://raynor.biz/yesenia,0.0885874910,170.121.205.221,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n17795,6,401,2017-06-03 12:19:13,http://price.name/jaylon,0.6101637815,156.220.131.223,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n17796,6,401,2017-06-03 04:00:03,http://schroeder.net/frank,0.2423566584,184.231.34.196,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n17797,6,401,2017-01-01 08:48:36,http://barrows.info/aron_krajcik,0.5719899222,230.82.61.104,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n17798,6,401,2017-05-20 12:08:43,http://wilderman.biz/jamey_witting,0.1792129596,6.141.169.32,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n17799,6,402,2017-05-29 14:51:02,http://ricehodkiewicz.io/karley_casper,0.0437166773,24.3.221.201,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n17800,6,402,2017-02-11 15:22:38,http://kohler.org/margarete,0.2115146148,88.13.161.59,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n17801,6,402,2017-04-07 00:42:17,http://goodwin.co/fatima,0.3695017664,95.61.151.30,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n17802,6,402,2017-05-12 08:26:30,http://fisher.com/king,0.1160896013,81.61.192.212,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n17803,6,402,2017-05-02 14:50:55,http://beatty.net/isabel,0.7745603134,172.55.36.191,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n17804,6,402,2017-01-15 03:24:41,http://christiansen.info/nikko,0.6943300333,9.227.62.196,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n17805,6,402,2016-12-30 17:01:51,http://altenwerth.org/landen,0.0211069043,185.89.175.138,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n17806,6,402,2016-12-14 13:02:50,http://gorczanymarvin.co/eunice,0.4965794532,123.244.223.164,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n17807,6,402,2017-05-11 23:36:44,http://keelingcronin.com/sophia_kreiger,0.4347450261,190.27.254.133,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n17808,6,402,2017-06-07 05:45:56,http://jacobi.info/vivian.kohler,0.4386901309,13.5.15.158,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n17809,6,402,2017-01-11 20:05:36,http://haagkeeling.name/haleigh,0.5849949724,242.215.194.153,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n17810,6,402,2017-04-20 19:26:25,http://bartelldamore.biz/gerda,0.2807117956,180.11.122.157,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n17811,6,402,2016-12-18 12:38:37,http://stiedemann.biz/hadley_fisher,0.2254377169,173.78.205.201,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n17812,6,402,2017-06-09 22:16:51,http://konopelski.org/giles.vandervort,0.6483984874,41.103.36.60,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n17813,6,402,2017-04-12 17:55:07,http://handskiles.co/clinton.towne,0.6213692123,69.161.169.144,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n17814,6,402,2017-03-13 20:56:29,http://rodriguezthiel.info/dylan,0.3326371606,47.71.22.157,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n17815,6,402,2017-03-06 02:50:56,http://hyatt.io/robin_schmeler,0.0338952333,27.11.66.24,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n17816,6,402,2016-12-24 14:12:49,http://hilllnitzsche.biz/louie,0.5988971452,217.64.181.136,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n17817,6,402,2017-01-14 16:10:08,http://mitchell.com/adriel_hoeger,0.0295333358,117.47.16.6,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n17818,6,402,2017-01-09 03:44:50,http://wiegandhammes.info/terry,0.9210997671,27.52.87.199,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n17819,6,402,2017-01-20 17:17:59,http://feil.name/lee,0.4260689976,136.77.194.227,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n17820,6,402,2017-01-27 18:33:34,http://towneemmerich.org/tiana.hane,0.1149769482,213.221.12.151,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n17821,6,402,2017-03-16 01:27:10,http://goodwinschmitt.biz/beie,0.0963811127,228.58.47.98,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n17822,6,402,2017-05-11 11:02:53,http://luettgenhamill.org/saige_halvorson,0.7967879494,67.225.169.81,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n17823,6,402,2017-02-07 17:58:57,http://lockman.name/mariam,0.5623703583,87.144.72.136,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n17824,6,402,2017-04-05 03:29:32,http://macejkovic.net/silas,0.6803988489,96.94.155.20,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n5004,2,111,2017-06-06 07:02:37,http://batzratke.com/linnie,,187.78.192.163,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n5005,2,111,2017-04-09 15:49:31,http://lueilwitz.co/giles.tromp,,209.84.64.60,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n5006,2,111,2017-05-22 12:39:27,http://effertz.co/jay,,196.110.234.40,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n5007,2,111,2016-12-22 20:40:52,http://grimesmuller.co/lucinda,,41.16.20.195,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n5008,2,111,2017-02-19 10:57:03,http://casper.name/sigurd.mayer,,192.183.63.101,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n5009,2,111,2017-02-03 17:38:51,http://bailey.biz/sherwood_schulist,,134.245.3.157,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n5010,2,111,2016-12-14 13:47:20,http://swiftrunolfsdottir.com/anais_dach,,8.89.104.243,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n5011,2,111,2017-03-24 07:50:41,http://ruel.biz/gregg_toy,,55.200.235.252,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n5012,2,111,2017-02-28 18:21:21,http://weberkshlerin.org/florence_macgyver,,200.221.213.206,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n5013,2,111,2017-03-09 00:16:26,http://barton.biz/monserrat_powlowski,,182.60.183.104,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n5014,2,111,2016-12-19 03:40:47,http://dareveum.co/bo,,8.105.129.12,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n5015,2,111,2017-05-28 22:34:50,http://herzog.com/favian_kiehn,,53.32.104.78,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n5016,2,111,2017-03-30 09:46:24,http://hansenhilpert.io/davon.waelchi,,79.88.163.31,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n5017,2,111,2017-02-04 10:23:23,http://nikolausroob.co/judah,,135.170.125.240,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n5018,2,111,2017-01-19 05:22:39,http://johnson.biz/wellington_witting,,213.190.236.16,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n5019,2,111,2016-12-15 09:55:31,http://prosacco.com/osvaldo,,43.198.100.231,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n5020,2,111,2017-01-29 21:18:59,http://homenick.io/reba,,64.114.162.110,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n5021,2,111,2017-04-16 05:15:11,http://stamm.name/tyshawn_sawayn,,193.172.245.243,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n5022,2,111,2017-02-13 14:47:38,http://gottliebbosco.co/gayle_stanton,,89.64.197.152,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n5023,2,111,2017-03-29 08:11:47,http://mraz.info/osvaldo_larkin,,163.98.241.226,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n5024,2,111,2017-01-13 21:32:53,http://mayert.co/kian,,68.170.44.11,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n5025,2,111,2017-03-21 06:38:03,http://sawayn.net/harold,,126.150.16.252,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n5026,2,111,2017-05-26 10:54:05,http://olson.org/lempi,,48.214.173.19,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n5027,2,111,2017-05-12 18:19:30,http://murraymacejkovic.net/demond_donnelly,,91.147.217.207,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n5028,2,111,2017-02-08 23:03:35,http://jerde.com/parker_rogahn,,204.92.135.180,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n5029,2,111,2017-03-12 08:31:27,http://treutel.name/itzel.goldner,,147.25.194.253,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n5030,2,111,2017-02-03 09:42:40,http://cremin.net/coleman,,61.12.55.151,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n5031,2,111,2017-01-02 05:24:57,http://reillymoriette.io/davion,,198.225.229.101,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n5032,2,111,2017-02-12 03:44:58,http://mohr.org/gerson.jacobson,,120.83.216.245,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n5033,2,111,2017-01-19 04:20:52,http://mcdermottschaefer.name/shane_reilly,,226.43.235.254,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n5034,2,111,2016-12-18 01:56:11,http://mohr.biz/adeline.mclaughlin,,43.171.234.58,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n5035,2,111,2017-06-07 23:59:28,http://kunze.name/delpha.rowe,,99.163.108.124,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n5036,2,111,2016-12-15 10:56:32,http://grantbode.com/santiago.fadel,,31.221.161.194,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n5037,2,111,2017-02-06 10:09:44,http://shanahangraham.com/evans_gleichner,,31.182.35.46,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n5038,2,111,2017-01-05 11:20:59,http://mcglynn.name/annette,,147.157.117.137,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n5039,2,111,2017-02-19 03:27:41,http://lefflerkutch.com/cleta,,109.163.162.57,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n5040,2,111,2017-04-28 21:03:55,http://anderson.co/christop_hand,,72.64.248.63,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n5041,2,111,2017-04-22 17:46:40,http://eichmannrodriguez.org/franz,,191.62.3.132,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n5042,2,111,2017-02-08 12:39:11,http://kochhills.io/conrad.upton,,219.135.151.147,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n5043,2,111,2017-05-09 07:33:25,http://witting.net/pink_bins,,68.201.91.94,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n5044,2,111,2017-04-04 08:31:37,http://mraz.io/maida.ferry,,151.145.164.231,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n5045,2,111,2017-01-21 17:10:17,http://schummcrist.net/retha_lakin,,59.246.244.127,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n5046,2,111,2017-02-06 01:39:33,http://schmitt.info/harmony.bartoletti,,229.78.90.159,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n5047,2,111,2017-03-09 13:49:18,http://grimetracke.co/valentin.paucek,,135.94.89.179,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n5048,2,111,2017-01-05 03:43:37,http://rosenbaum.co/beryl_collier,,186.217.134.130,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n5049,2,111,2017-04-13 22:58:48,http://hanestoltenberg.com/griffin_bayer,,133.24.12.38,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n5050,2,111,2017-03-31 09:00:10,http://olson.name/claudia_senger,,118.78.144.196,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n5051,2,111,2017-06-10 00:26:02,http://rempelharber.org/stan_dare,,73.128.168.134,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n5052,2,111,2017-06-03 12:10:04,http://mckenzie.co/timmothy,,168.228.52.7,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n5053,2,111,2017-04-23 17:39:46,http://kohlerwitting.org/norma_torp,,87.199.76.194,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n5054,2,111,2017-05-19 22:34:50,http://olson.info/margarett.ratke,,203.24.97.165,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n5055,2,111,2017-06-10 12:02:10,http://eberthills.io/kacey,,27.220.84.74,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n5056,2,111,2017-04-12 23:59:10,http://deckow.info/dashawn,,29.119.43.252,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n5057,2,111,2016-12-24 20:52:37,http://cronin.io/gay,,17.114.223.44,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n5058,2,111,2017-01-07 12:14:55,http://franeckismith.info/verner_mohr,,158.148.229.223,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n5059,2,111,2017-05-31 21:24:29,http://beatty.biz/sid_veum,,152.148.147.191,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n17987,7,407,2017-03-19 23:10:33,http://haley.org/madelyn.moen,,187.138.48.43,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n17988,7,407,2017-04-10 15:41:32,http://nitzsche.info/yasmine_wyman,,183.243.67.234,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n17989,7,407,2017-02-26 03:19:59,http://daugherty.net/amelia,,139.101.229.179,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n17990,7,407,2017-01-22 01:51:13,http://lowefahey.com/marge.anderson,,9.58.152.212,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n17991,7,407,2017-02-04 20:30:59,http://faheyberge.net/vincenza_cummerata,,39.18.62.133,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n17992,7,407,2017-01-25 14:33:20,http://sanford.biz/dina,,20.20.181.95,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n17993,7,407,2016-12-19 03:37:43,http://beier.co/paxton.becker,,97.252.235.195,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n17994,7,407,2017-02-20 07:38:22,http://padberg.info/arjun.farrell,,199.94.165.130,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n17995,7,407,2017-05-05 14:59:02,http://kunzeswaniawski.co/eloy.schuppe,,104.177.214.57,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n17996,7,407,2016-12-18 15:01:11,http://beahan.org/judah.harber,,84.221.66.146,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n17997,7,407,2017-06-06 10:09:21,http://macgyver.com/isom,,114.250.169.212,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n17998,7,407,2017-03-24 17:25:05,http://bergstromsenger.co/erik,,81.94.88.234,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n17999,7,407,2017-06-08 17:25:45,http://swift.org/hipolito,,90.97.230.120,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n18000,7,407,2017-01-07 01:04:38,http://mosciskinienow.org/myrna.barrows,,217.188.125.248,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n18001,7,407,2017-01-16 17:36:11,http://batzlesch.net/juvenal,,2.136.191.137,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n18002,7,407,2017-05-26 06:40:31,http://veum.co/kailey,,81.148.248.246,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n18003,7,407,2017-01-07 21:41:51,http://connkrajcik.org/alexandrine,,186.116.15.83,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n18004,7,407,2017-01-21 22:29:07,http://sipes.org/soledad,,82.10.134.192,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n18005,7,407,2017-03-03 12:50:27,http://harriskautzer.com/delilah,,61.179.79.176,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n18006,7,407,2016-12-13 17:02:09,http://gerlach.com/tierra,,185.106.46.46,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n18007,7,407,2017-02-04 09:04:30,http://gleichnerrempel.org/presley,,77.144.237.221,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n18008,7,407,2017-06-07 11:06:40,http://blandastrosin.biz/neil.kohler,,197.227.71.94,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n18009,7,407,2017-06-06 03:17:30,http://kunze.com/abe,,138.49.145.235,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n18010,7,407,2017-05-08 08:07:29,http://aufderhar.net/cleora,,148.176.220.119,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n18011,7,408,2017-05-23 08:52:31,http://nader.org/lindsey.wilkinson,,172.243.51.172,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n18012,7,408,2017-03-17 04:34:33,http://turcotte.name/prudence_haley,,104.131.43.95,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n18013,7,408,2017-03-08 17:20:04,http://kilback.info/carmelo_mclaughlin,,101.248.5.170,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n18014,7,408,2017-04-20 15:29:37,http://larson.name/lora,,70.249.236.73,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n18015,7,408,2016-12-14 01:29:06,http://bahringer.biz/marietta,,228.234.152.126,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n18016,7,408,2017-01-15 03:50:03,http://dicki.co/tania,,252.49.103.206,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n18017,7,408,2017-01-01 23:05:06,http://mohrbogisich.com/marie,,35.19.109.179,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n18018,7,408,2017-02-20 01:45:07,http://smith.info/eda.hills,,245.128.243.140,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n18019,7,408,2017-01-06 02:42:29,http://corkery.biz/marilou.anderson,,89.201.242.176,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n18020,7,408,2017-04-11 22:41:59,http://millsreichel.io/tre,,184.249.98.14,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n18021,7,408,2016-12-14 06:15:56,http://balistreri.biz/addie_bogisich,,198.10.93.42,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n18022,7,408,2017-06-10 02:50:15,http://botsfordmcdermott.name/aubrey,,137.102.151.30,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n18023,7,408,2017-02-14 13:48:40,http://wizabotsford.net/marjory.hagenes,,34.39.158.100,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n18024,7,408,2017-05-11 10:30:59,http://lebsack.net/danial,,88.11.41.6,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n18025,7,408,2017-03-08 00:51:09,http://reynoldsrenner.com/sterling,,252.67.163.62,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n18026,7,408,2017-03-22 16:33:57,http://tillmanrice.biz/lyda,,199.12.17.119,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n18027,7,408,2017-01-16 03:54:14,http://schimmelboyle.com/amari_rutherford,,169.249.176.86,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n18028,7,408,2017-05-19 02:12:48,http://keeling.com/waylon,,87.100.69.116,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n18029,7,408,2017-04-12 16:05:41,http://weinat.name/israel_roberts,,41.241.20.106,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n18030,7,408,2017-06-01 10:34:45,http://schroederullrich.org/elenor.hilll,,140.26.190.227,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n18031,7,408,2017-05-04 21:46:04,http://riceschmeler.name/cecelia,,26.130.243.32,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n18032,7,408,2016-12-15 17:53:36,http://nikolaus.name/ruben.schiller,,73.230.4.135,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n18033,7,409,2017-02-08 19:34:00,http://fahey.net/robin,,254.88.218.94,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n18034,7,409,2017-02-27 05:25:27,http://kunde.org/connor_mueller,,25.250.78.176,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n18035,7,409,2017-01-10 02:18:56,http://ondricka.co/kaelyn.ondricka,,67.108.171.20,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n18036,7,409,2017-01-13 02:34:51,http://wisozkgrimes.com/alvah,,151.97.199.67,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n18037,7,409,2017-05-08 16:06:55,http://abshire.co/ernie,,94.43.243.113,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n18038,7,409,2017-01-26 08:12:52,http://hirtheking.biz/shirley_gorczany,,115.39.116.171,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n18039,7,409,2017-03-12 17:48:24,http://hudson.co/gracie.gibson,,160.99.103.168,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n18040,7,409,2017-04-02 20:11:56,http://boscogulgowski.co/hattie,,205.99.187.251,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n18041,7,409,2017-02-15 02:36:20,http://conroy.com/trace.johnson,,188.41.169.89,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n18042,7,409,2017-06-08 15:36:59,http://ritchie.net/nikko,,137.88.184.228,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n14857,5,334,2017-04-29 05:05:12,http://braun.org/kelsie,0.7955821915,183.33.229.51,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n14858,5,334,2017-03-09 21:29:19,http://wintheiserstanton.info/uriah,0.1090970533,51.109.71.192,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n14859,5,334,2017-03-19 01:55:46,http://jaskolski.org/filiberto,0.3364525802,169.144.178.116,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n14860,5,334,2017-05-08 17:46:45,http://gottlieb.name/catalina_mcglynn,0.6390382937,224.184.6.17,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n14861,5,334,2017-05-28 21:47:47,http://bahringer.org/nathen_bayer,0.9059479677,210.51.247.172,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n14862,5,334,2017-01-04 15:52:13,http://pouroshudson.co/alexane,0.9282460861,160.60.102.218,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n14863,5,334,2017-03-17 06:17:17,http://funk.org/vada.mcclure,0.8159219933,215.191.90.86,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n14864,5,334,2017-05-13 14:07:56,http://ratke.info/maegan_baumbach,0.1222042069,19.184.230.27,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": true}\"\n14865,5,335,2017-02-14 20:08:08,http://kirlin.io/jared,0.2238270035,225.15.52.13,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n14866,5,335,2017-05-16 10:40:20,http://frami.com/hattie_murazik,0.4160646000,57.79.130.152,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n14867,5,335,2017-04-22 16:09:55,http://boylemurphy.co/buck,0.6167972528,118.189.56.45,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n14868,5,335,2016-12-15 23:28:16,http://kohlergibson.co/vladimir_corwin,0.8987802990,195.15.132.198,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n14869,5,335,2017-01-24 22:06:35,http://metz.co/creola,0.8390200241,38.234.180.189,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n14870,5,335,2017-05-22 06:32:08,http://turnerbailey.io/letha,0.6462490408,28.237.40.186,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n14871,5,335,2017-05-29 05:54:14,http://yundt.com/rosalia,0.4591286197,134.147.49.114,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n14872,5,335,2016-12-18 12:33:38,http://flatley.io/syble_rempel,0.3434840658,140.99.244.85,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n14873,5,335,2017-06-07 01:05:17,http://braun.info/maximus_braun,0.8881730804,73.161.192.153,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n14874,5,335,2017-02-11 18:46:02,http://batz.info/kenton_thiel,0.5845240570,70.71.2.114,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n14875,5,335,2017-01-09 14:17:05,http://greenfelder.io/presley.hammes,0.4916420728,181.155.3.170,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n14876,5,335,2017-05-01 23:50:13,http://kihn.org/mattie_bogisich,0.8379338199,204.115.246.174,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n14877,5,335,2016-12-26 06:06:06,http://jones.co/rahsaan,0.8739959545,29.45.123.5,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n14878,5,335,2017-01-06 18:04:32,http://willmsgibson.com/alda,0.5167544007,218.37.157.171,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n14879,5,335,2017-02-24 01:04:52,http://schmidtgerhold.biz/nils_hahn,0.5503675261,230.176.239.17,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n14880,5,335,2017-01-15 14:17:51,http://mcglynnsanford.com/asha_christiansen,0.9650068273,226.39.16.98,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n14881,5,335,2016-12-22 19:54:06,http://kilbackwiza.co/marc,0.1974326324,49.171.90.166,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n14882,5,335,2017-01-22 16:27:30,http://hilll.io/cedrick.trantow,0.1044500483,109.247.32.66,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n14883,5,335,2017-04-05 14:55:37,http://dibbert.net/arlo_daniel,0.9915621866,107.63.34.78,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n14884,5,335,2017-05-26 21:27:24,http://kiehndouglas.biz/elsie_franecki,0.3460806878,251.143.127.243,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n14885,5,335,2017-03-28 14:23:15,http://gulgowskiullrich.co/cordia.gislason,0.5124294077,61.99.27.97,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n14886,5,335,2017-05-08 22:18:14,http://padbergsanford.io/je_mcdermott,0.6733214470,66.245.25.239,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n14887,5,335,2017-06-03 18:31:38,http://ward.net/unique_ebert,0.1727547751,80.41.215.128,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n14888,5,335,2017-04-22 04:21:22,http://hartmann.co/kennedi_witting,0.6445928313,213.124.78.111,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n5060,2,111,2017-01-29 16:28:50,http://schmidt.co/sydni,,187.166.239.167,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n5061,2,111,2017-04-20 23:59:23,http://kuhlman.info/dale,,90.237.45.243,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n5062,2,112,2017-02-12 06:53:05,http://larson.io/boyd.ferry,,254.230.166.221,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n5063,2,112,2017-04-28 11:44:05,http://boyer.io/brandi,,213.129.235.39,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n5064,2,112,2017-01-09 13:13:43,http://lindhintz.biz/percival,,106.93.188.96,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n5065,2,112,2017-04-07 14:44:46,http://gradybaumbach.com/erika.doyle,,122.34.165.187,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n5066,2,112,2017-05-15 00:10:37,http://friesen.co/vito,,139.96.112.45,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n5067,2,112,2017-05-08 15:12:09,http://thompsonnienow.io/darion.ward,,224.6.175.20,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n5068,2,112,2017-03-07 23:54:42,http://kochvon.io/sammy,,36.175.167.169,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n5069,2,112,2017-02-16 21:03:06,http://zieme.io/lillie,,169.179.129.25,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n5070,2,112,2017-03-29 22:21:53,http://dubuque.net/sterling.swift,,199.240.204.173,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n5071,2,112,2017-04-26 08:08:27,http://hudson.org/eduardo_gleichner,,245.83.217.39,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n5072,2,112,2017-01-01 04:42:23,http://weimann.co/declan,,195.93.27.118,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n5073,2,112,2017-05-11 03:01:03,http://jacobimcglynn.io/meggie.hackett,,225.34.144.67,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n5074,2,112,2017-06-10 15:25:55,http://lakin.net/jarred.daugherty,,210.8.134.104,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n5075,2,112,2017-02-08 00:00:13,http://koeppblanda.com/reece.block,,41.251.202.168,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n5076,2,112,2017-01-01 15:38:50,http://reilly.com/isidro,,191.118.39.136,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n5077,2,112,2017-05-01 13:24:44,http://waelchiwaelchi.info/green_heathcote,,222.218.217.92,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n5078,2,112,2017-03-12 04:59:26,http://walsh.co/kolby,,81.214.228.105,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n5079,2,112,2017-04-17 14:12:06,http://king.io/sedrick,,170.135.44.123,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n5080,2,112,2016-12-20 01:58:31,http://king.com/jerrold.schuster,,10.152.101.246,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n5081,2,112,2017-01-12 13:18:34,http://kutch.org/domenick.goldner,,51.206.31.253,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n5082,2,112,2017-02-05 11:12:08,http://donnellyhayes.co/samir.kulas,,89.116.63.58,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n5083,2,112,2017-05-03 20:33:45,http://jacobson.net/enrique,,35.38.128.119,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n5084,2,112,2017-01-10 08:44:25,http://champlin.co/garret_hills,,54.127.203.183,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n5085,2,112,2017-02-11 01:11:38,http://ruel.name/cody_haag,,180.144.94.38,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n5086,2,112,2017-05-12 07:14:51,http://durganmcdermott.name/aisha_heaney,,140.170.194.183,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n5087,2,112,2017-03-09 10:21:43,http://greenholtschinner.biz/dewayne,,57.183.161.224,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n5088,2,112,2017-02-04 04:46:19,http://collinshettinger.co/te.buckridge,,26.230.249.248,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n5089,2,112,2017-05-04 14:17:28,http://bednarbrakus.io/jermaine,,8.191.107.65,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n5090,2,112,2017-03-20 15:51:11,http://waelchi.com/damien,,48.140.112.49,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n5091,2,112,2017-04-07 20:49:36,http://nader.io/skyla_torp,,252.34.136.100,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n5093,2,112,2017-01-29 00:19:17,http://uptonquigley.co/lori,,208.85.22.211,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n5094,2,112,2017-01-19 06:43:54,http://lynch.info/filomena,,57.157.5.132,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n5095,2,112,2017-03-04 23:27:33,http://bogan.io/gregoria,,198.150.76.135,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n5096,2,112,2017-01-18 20:36:19,http://herzog.net/leola,,159.228.30.141,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n5097,2,112,2017-01-08 05:04:48,http://langosh.net/reginald,,239.89.90.139,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n5098,2,112,2017-02-20 15:03:51,http://durgan.net/yasmeen,,230.240.68.145,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n5099,2,112,2017-03-24 15:10:02,http://sauerrunte.name/dayne,,123.132.222.60,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n5100,2,112,2017-06-05 12:23:11,http://gleason.biz/toy.hansen,,135.44.224.92,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n5101,2,112,2017-04-17 22:13:49,http://reilly.co/lon.daniel,,246.252.173.115,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n5102,2,112,2017-02-21 06:20:13,http://upton.net/loraine,,137.90.241.214,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n5103,2,112,2017-03-01 07:02:53,http://willms.info/autumn,,43.5.188.14,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n5104,2,112,2017-02-22 07:08:09,http://swiftcrona.biz/katelynn.hoeger,,25.170.246.238,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n5105,2,112,2017-01-20 17:13:27,http://gusikowski.info/harold,,249.144.117.193,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n5106,2,112,2017-05-01 05:58:37,http://weinat.net/timmothy,,205.30.155.16,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n5107,2,112,2017-04-25 14:39:23,http://rodriguez.name/maddison,,59.36.133.199,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n5108,2,112,2017-04-30 06:28:23,http://rogahnkuphal.name/johnpaul,,165.111.202.147,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n5109,2,112,2017-01-05 08:59:48,http://streichbahringer.co/brayan,,105.246.104.218,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n5110,2,112,2017-04-22 10:36:36,http://abbott.info/urban,,106.125.190.162,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n5111,2,112,2017-05-02 16:45:42,http://schoen.info/thea_becker,,43.114.103.76,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n5112,2,112,2017-05-18 01:25:38,http://walker.net/louisa,,5.76.3.209,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n5113,2,113,2017-02-16 05:02:44,http://schuppe.io/jacynthe,,96.180.29.84,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n5114,2,113,2017-02-27 20:32:22,http://farrellterry.name/arnaldo_cremin,,218.201.138.8,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n5115,2,113,2017-03-11 11:22:08,http://yost.com/winona,,247.97.135.183,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n5116,2,113,2017-01-31 15:56:40,http://feil.org/violet_stanton,,30.63.183.102,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n18043,7,409,2017-02-03 22:39:11,http://lebsack.co/edgar.mckenzie,,206.67.67.190,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n18044,7,409,2017-05-02 15:41:30,http://cremin.io/milton,,90.4.13.225,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n18045,7,409,2016-12-28 03:19:09,http://handkeler.info/maiya_stark,,5.25.22.150,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n18046,7,409,2017-03-05 09:05:30,http://bergeyost.net/jarod_treutel,,247.162.57.135,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n18047,7,409,2017-06-12 14:59:17,http://windlerdaugherty.info/haven.armstrong,,239.230.187.45,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n18048,7,409,2017-03-29 06:31:43,http://graham.biz/berenice,,178.203.44.110,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n18049,7,409,2017-03-07 23:57:27,http://renner.biz/grayson_gerlach,,168.237.163.15,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n18050,7,409,2017-03-29 14:19:04,http://wyman.name/emmitt_pfannerstill,,113.128.28.57,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n18051,7,409,2017-03-14 19:25:01,http://skiletamm.info/vidal,,176.68.91.130,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n18052,7,409,2017-01-11 06:20:13,http://emard.co/harley_prosacco,,161.150.73.86,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n18053,7,409,2017-02-20 22:00:07,http://robelcronin.com/edwardo,,217.129.230.215,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n18054,7,410,2017-03-29 18:10:39,http://zulauf.io/tyler_heathcote,,226.135.239.179,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n18055,7,410,2017-05-22 08:19:04,http://rodriguez.info/marianne,,37.179.238.45,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n18056,7,410,2017-05-10 09:43:30,http://schmidt.name/caden_price,,193.49.244.212,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n18057,7,410,2017-01-09 06:06:44,http://zieme.co/cale_toy,,15.24.83.58,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n18058,7,410,2017-05-29 21:05:40,http://waelchitreutel.com/donavon,,89.103.51.137,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n18059,7,410,2017-03-28 09:55:01,http://kihn.biz/shanon,,63.125.133.192,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n18060,7,410,2017-05-26 04:17:04,http://padbergfeil.info/frederik_ruel,,254.54.112.24,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n18061,7,410,2017-02-04 14:49:46,http://adams.net/casimir_weber,,124.127.35.70,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n18062,7,410,2016-12-27 09:18:07,http://mooreschneider.org/ezekiel.zulauf,,70.37.81.31,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n18063,7,410,2017-05-06 19:56:59,http://ernser.net/sarah_borer,,225.40.40.199,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n18064,7,410,2017-04-29 07:04:17,http://kuphalhyatt.org/nelson,,47.252.189.81,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n18065,7,410,2017-01-27 09:58:21,http://schumm.info/hope,,76.134.145.246,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n18066,7,410,2017-03-24 12:06:00,http://hilpertlebsack.info/gerhard_grant,,202.235.226.96,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n18067,7,410,2017-03-10 07:46:34,http://heathcote.name/arlo,,201.161.193.105,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n18068,7,410,2017-05-20 20:43:52,http://bartell.org/brenna,,211.112.82.98,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n18069,7,410,2017-01-04 19:53:56,http://breitenbergfarrell.net/herminio,,202.157.150.35,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n18070,7,410,2017-01-23 13:03:39,http://nikolaus.name/cyril,,59.226.245.57,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n18071,7,410,2017-01-28 14:34:23,http://stehrritchie.info/elisa,,224.91.89.61,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n18072,7,410,2017-03-15 02:32:31,http://mcglynn.net/rosemarie_greenholt,,41.60.148.27,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n18073,7,410,2017-04-07 18:42:18,http://hagenes.io/estel,,239.43.204.24,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n18074,7,410,2017-02-16 13:33:02,http://jacobi.com/sigrid,,31.158.173.163,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n18075,7,410,2017-05-06 00:50:47,http://bednarheel.name/kelsie,,232.74.126.108,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n18076,7,410,2017-05-12 12:29:05,http://thielkreiger.co/wyatt.cremin,,221.199.147.216,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n18077,7,410,2017-02-04 00:20:34,http://bergnaumquitzon.io/dereck_bergstrom,,52.199.174.120,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n18078,7,410,2017-05-22 19:07:03,http://okunevareichert.info/elbert,,173.238.182.110,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n18079,7,410,2016-12-21 16:08:23,http://kshlerinmacejkovic.org/favian,,130.205.52.97,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n18080,7,410,2017-03-07 16:28:38,http://feesthane.io/jacklyn,,165.92.88.198,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n18081,7,410,2017-05-01 00:24:45,http://herzog.org/alfreda_reynolds,,158.221.74.53,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n18082,7,410,2017-02-03 04:18:21,http://dooley.org/patsy,,130.27.223.198,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n18083,7,410,2017-01-04 08:25:11,http://will.net/bernita,,16.172.245.102,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n18084,7,410,2017-01-06 19:22:18,http://larkin.io/tyrique,,28.184.7.199,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n18085,7,410,2017-05-12 08:47:29,http://ankunding.io/rex.marks,,166.72.186.232,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n18086,7,410,2017-05-28 18:51:54,http://schaeferwisozk.io/elvera,,90.250.225.145,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n18087,7,410,2016-12-28 07:07:24,http://harber.biz/antone,,206.180.233.112,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n18088,7,410,2017-02-17 12:53:47,http://vandervort.io/glennie,,122.92.144.34,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n18089,7,410,2017-01-02 09:30:23,http://cole.biz/richard.murazik,,206.27.219.30,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n18090,7,410,2017-05-21 20:28:41,http://marks.com/ivory,,191.226.210.209,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n18091,7,410,2017-02-24 15:21:22,http://mrazoreilly.biz/laverne,,79.206.209.173,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n18092,7,410,2017-01-18 07:41:49,http://schowalter.co/deon.goodwin,,195.74.14.225,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n18093,7,410,2017-04-14 11:26:04,http://hamill.co/hardy.johnston,,175.23.112.67,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n18094,7,410,2017-06-12 08:11:02,http://jenkins.biz/vincenzo_hettinger,,194.152.243.199,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n18095,7,410,2017-06-08 18:55:41,http://shanahan.net/janick,,147.177.161.209,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n18096,7,410,2017-06-07 05:44:14,http://hilll.org/mac.gibson,,88.35.223.54,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n18097,7,410,2017-05-10 18:06:45,http://frami.io/candido_lowe,,191.142.34.78,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n5117,2,113,2017-04-14 22:25:51,http://prosacco.info/erik.kerluke,,123.81.218.94,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n5118,2,113,2017-04-07 18:33:21,http://kulas.io/deie,,233.72.231.170,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n5119,2,113,2017-04-30 18:09:21,http://langworth.co/jade_kirlin,,135.140.128.73,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n5120,2,113,2017-06-02 11:12:10,http://bartellohara.io/alyson.mante,,217.85.231.196,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n5121,2,113,2017-03-08 03:44:22,http://weber.io/breana_mohr,,150.8.186.137,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n5122,2,113,2017-04-13 11:23:27,http://roberts.co/rodolfo,,225.207.241.129,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n5123,2,113,2017-06-08 18:44:10,http://daniel.org/diana_runte,,149.30.119.168,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n5124,2,113,2017-04-22 19:01:19,http://volkman.net/olaf_ankunding,,230.42.112.201,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n5125,2,113,2017-02-08 01:23:30,http://jacobson.io/isobel,,228.50.218.60,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n5126,2,113,2017-01-09 19:02:08,http://koch.io/ethan.cronin,,233.59.40.122,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n5127,2,113,2017-03-12 15:07:41,http://thiel.com/titus,,190.217.179.217,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n5128,2,113,2017-04-29 18:37:05,http://corwin.biz/corbin_waelchi,,157.19.248.11,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n5129,2,113,2017-03-10 15:03:43,http://romaguera.info/derek_beatty,,187.150.27.138,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n5130,2,113,2016-12-27 19:35:06,http://strosin.info/elfrieda_feil,,143.103.112.252,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n5131,2,113,2016-12-19 02:29:17,http://barton.org/delores.wiegand,,112.193.106.144,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n5132,2,113,2017-06-05 06:16:30,http://stoltenbergmurphy.io/mckayla.zulauf,,212.143.137.212,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n5133,2,113,2017-01-02 21:10:16,http://wuckert.name/katlyn,,189.222.40.230,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n5134,2,113,2017-06-13 22:45:10,http://okondaugherty.com/easton_kulas,,216.65.188.184,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n5135,2,113,2017-03-27 14:22:01,http://corkery.org/reinhold,,125.5.172.232,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n5136,2,113,2016-12-24 18:58:41,http://becker.com/leopold.bauch,,105.180.122.117,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n5137,2,113,2017-01-03 07:59:19,http://ward.io/sydni.terry,,224.205.184.229,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n5138,2,113,2017-03-04 08:32:38,http://murazik.info/grant,,96.128.136.176,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n5139,2,113,2017-06-13 20:34:29,http://murray.net/emmett_robel,,139.216.127.142,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n5140,2,113,2017-06-02 01:21:34,http://kub.info/derek_maggio,,91.93.115.91,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n5141,2,113,2017-05-01 01:57:30,http://yundtbrown.com/guillermo_graham,,96.43.148.188,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n5142,2,114,2017-05-13 01:48:49,http://swiftvandervort.net/clara_reinger,,47.103.112.106,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n5143,2,114,2017-05-04 16:06:55,http://windler.name/haskell,,63.44.248.203,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n5144,2,114,2017-06-11 01:22:33,http://hamill.biz/zakary,,128.43.233.144,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n5145,2,114,2017-01-01 01:00:47,http://stoltenberg.co/steve,,168.2.169.238,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n5146,2,114,2017-02-19 01:44:24,http://jerde.info/haven,,36.68.69.175,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n5147,2,114,2017-05-02 13:18:51,http://rempel.org/janie,,122.22.250.105,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n5148,2,114,2017-03-15 11:21:27,http://kiehnweimann.org/will,,148.121.4.251,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n5149,2,114,2017-04-12 19:26:07,http://hauck.net/dortha_watsica,,209.54.211.93,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n5150,2,114,2017-05-01 01:45:19,http://lebsack.info/delfina,,236.140.205.38,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n5151,2,114,2017-04-13 01:59:40,http://sanford.net/priscilla.bernhard,,164.192.168.102,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n5152,2,114,2017-04-06 18:10:41,http://gaylord.io/austen,,178.41.228.77,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n5153,2,114,2017-04-06 10:20:35,http://wehner.io/ania,,127.23.177.81,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n5154,2,114,2017-03-01 23:25:38,http://schuppe.info/garth,,42.120.4.95,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n5155,2,114,2017-01-21 13:00:59,http://langoshwyman.com/wyman.aufderhar,,229.83.6.154,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n5156,2,114,2017-04-01 08:47:30,http://christiansenruel.biz/ellie_moen,,246.64.144.97,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n5157,2,114,2016-12-31 06:38:13,http://stracke.org/claudie_braun,,152.25.97.131,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n5158,2,114,2017-01-23 04:25:24,http://botsford.com/ed.gottlieb,,186.50.246.47,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n5159,2,114,2016-12-21 08:43:03,http://reichert.co/kenton_batz,,148.84.96.56,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n5160,2,114,2017-04-11 15:19:08,http://kertzmannweber.name/chad.muller,,99.172.88.130,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n5161,2,114,2017-01-21 03:54:24,http://hickle.info/matilda.hartmann,,190.192.223.4,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n5162,2,114,2017-03-11 12:41:57,http://denesik.co/mitchell_boyer,,113.104.43.162,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n5163,2,114,2017-05-05 04:05:44,http://cole.org/wiley,,95.118.223.95,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n5164,2,114,2017-05-12 08:50:57,http://carter.net/lukas.stehr,,18.88.187.77,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n5165,2,114,2017-03-14 08:20:59,http://feeneyauer.co/keely,,143.7.205.140,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n5166,2,114,2017-05-24 22:59:52,http://hodkiewicz.biz/gustave.lubowitz,,185.211.252.72,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n5167,2,114,2017-05-01 20:49:12,http://sauerswaniawski.info/aliza.hane,,120.235.243.116,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n5168,2,114,2017-01-01 12:27:14,http://stehrreichert.net/domenico,,32.229.6.78,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n5169,2,114,2017-03-12 20:59:34,http://flatleymetz.io/florencio,,48.122.10.33,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n5170,2,114,2017-05-19 18:49:34,http://kutch.net/effie,,141.8.179.194,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n5171,2,114,2017-04-22 18:44:18,http://jast.io/eriberto,,199.60.156.171,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n18098,7,410,2016-12-15 15:52:46,http://shanahan.biz/adolf,,208.130.237.185,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n18099,7,410,2017-01-05 04:57:15,http://moore.com/kamryn.hintz,,45.162.248.248,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n18100,7,410,2017-02-26 11:29:36,http://kilbackhilpert.co/jodie_shanahan,,134.89.107.214,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n18101,7,410,2017-01-05 02:55:20,http://reichel.com/clinton_nicolas,,231.118.31.208,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n18102,7,410,2017-01-07 23:07:17,http://fahey.io/sydney_kemmer,,128.22.149.238,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n18103,7,410,2017-04-15 04:19:40,http://veum.org/anabelle,,46.14.145.35,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n18104,7,410,2017-05-17 21:14:43,http://bernhard.org/madalyn_rath,,97.245.22.52,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n18105,7,410,2017-03-09 03:58:38,http://heathcote.info/josh.mueller,,3.23.186.123,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n18106,7,410,2017-05-29 22:58:14,http://mosciskiullrich.name/columbus_rolfson,,211.160.210.187,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n18107,7,410,2017-03-04 23:32:52,http://strackebergstrom.org/letitia,,68.153.104.170,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n18108,7,410,2017-02-16 18:05:07,http://raynor.net/juana,,218.193.122.245,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n18109,7,410,2017-04-21 06:16:23,http://hamill.net/ulices,,152.176.245.193,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n18110,7,410,2017-02-08 01:13:51,http://tromp.co/delmer,,251.250.158.77,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n18111,7,410,2017-01-14 19:50:48,http://kuphalhilll.biz/jaclyn,,146.229.166.174,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n18112,7,410,2017-01-01 10:12:06,http://dubuquesawayn.name/juston,,172.23.177.218,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n18113,7,410,2017-02-27 03:25:15,http://wiegand.biz/reese,,184.81.174.203,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n18114,7,410,2017-02-04 13:13:14,http://oconnell.org/edyth.nicolas,,161.15.20.8,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n18115,7,411,2017-02-11 07:58:33,http://kshlerin.io/ian,,235.130.63.53,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n18116,7,411,2017-01-08 05:04:30,http://runolfsdottir.info/casey.herzog,,237.117.128.155,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n18117,7,411,2017-03-28 13:53:31,http://larson.info/antwan_schultz,,10.30.138.71,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n18118,7,411,2017-02-06 19:31:57,http://terry.info/selmer_white,,34.135.61.182,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n18119,7,411,2017-06-13 00:12:42,http://schowalterweinat.com/haan.cain,,138.164.15.212,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n18120,7,411,2016-12-19 20:46:28,http://christiansenhaley.org/nikita,,224.100.213.172,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n18121,7,411,2017-05-14 03:31:50,http://aufderhar.info/kaya.brakus,,243.62.120.24,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n18122,7,411,2017-02-24 23:12:59,http://ledner.io/nellie_parker,,120.22.133.104,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n18123,7,411,2017-05-01 23:02:18,http://mohr.name/estell,,252.49.11.120,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n18124,7,411,2017-03-31 10:17:06,http://flatley.name/lenore_hoeger,,168.83.153.211,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n18125,7,411,2017-04-15 07:32:28,http://wisozkpaucek.co/florian.weimann,,251.182.95.122,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n18126,7,411,2017-03-01 06:24:24,http://dickinson.info/imogene_hermiston,,140.152.217.97,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n18127,7,411,2017-01-11 08:44:11,http://treutelbahringer.co/ernestina,,129.119.140.146,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n18128,7,411,2017-01-07 08:18:44,http://boyer.name/sherman,,139.200.101.114,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n18129,7,411,2016-12-25 09:44:26,http://bartoletti.com/emmet.vonrueden,,145.232.146.227,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n18130,7,411,2016-12-29 06:39:11,http://hoppe.org/colt.ferry,,231.48.13.121,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n18131,7,411,2017-02-15 14:37:14,http://nitzsche.info/lester,,21.30.3.92,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n18132,7,411,2017-05-18 08:57:32,http://macgyvereffertz.biz/grayce.senger,,54.73.124.141,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n18133,7,411,2017-04-03 04:14:34,http://damorejohns.com/ursula,,184.44.40.236,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n18134,7,411,2017-04-13 03:56:38,http://runte.io/alford,,242.86.118.34,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n18135,7,411,2017-01-10 23:14:40,http://leschnitzsche.biz/milo,,36.63.109.37,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n18136,7,411,2017-02-11 14:30:46,http://bogisich.biz/bud.olson,,49.153.170.235,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n18137,7,411,2017-03-10 17:30:32,http://weimanncasper.name/marlen.ward,,38.43.81.52,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n18138,7,411,2017-05-11 22:36:07,http://pollich.info/gage,,162.53.77.92,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n18139,7,411,2017-04-27 23:35:00,http://crooks.com/sherman.sipes,,142.72.216.119,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n18140,7,411,2017-02-02 02:48:27,http://wuckert.info/fay_torp,,31.250.28.112,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n18141,7,411,2016-12-18 08:06:55,http://hartmannschaden.biz/annabell_luettgen,,92.141.59.27,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n18142,7,411,2016-12-21 18:18:49,http://watsicamiller.org/loma_spencer,,82.123.67.18,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n18143,7,411,2017-02-06 14:22:03,http://medhurstdoyle.org/jonas,,234.145.102.155,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n18144,7,411,2017-05-06 03:51:10,http://bartell.info/annamae_lowe,,13.82.65.162,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n18145,7,411,2017-05-02 11:42:06,http://herzog.org/vivien,,15.22.36.142,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n18146,7,411,2017-03-25 08:36:33,http://hodkiewicz.net/daren.kuhic,,21.54.218.218,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n18147,7,411,2017-05-08 08:46:00,http://howell.info/willard,,208.146.103.169,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n18148,7,411,2017-05-25 04:31:51,http://lemkehermann.com/jordy,,67.15.220.21,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n18149,7,411,2016-12-28 11:16:44,http://ondricka.io/theodora_stokes,,137.228.188.223,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n18150,7,411,2017-02-04 02:27:28,http://breitenberg.org/floie_thiel,,93.156.127.212,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n18151,7,411,2017-04-02 19:29:50,http://steuber.com/dangelo_leannon,,57.20.245.123,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": true}\"\n18152,7,411,2017-02-07 00:17:53,http://brown.org/esteban.stanton,,216.221.6.5,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n5172,2,114,2017-04-14 03:30:25,http://kilback.io/zaria_harvey,,59.15.15.24,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n5173,2,114,2017-04-20 09:25:37,http://bauch.name/barry.bosco,,4.71.149.103,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n5174,2,114,2017-05-06 08:11:04,http://rohan.biz/giovanny,,146.58.106.84,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n5175,2,114,2017-02-01 05:02:02,http://hahn.org/anibal,,39.38.157.185,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n5176,2,114,2017-05-10 21:35:22,http://sanfordlehner.io/easter,,30.85.111.15,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n5177,2,114,2017-04-15 21:23:33,http://crooks.info/wilford,,209.85.194.131,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n5178,2,114,2017-02-28 13:33:25,http://farrell.com/eduardo,,98.178.162.159,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n5179,2,114,2016-12-29 05:15:55,http://upton.io/murl,,32.230.78.121,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n5180,2,114,2017-03-16 12:58:43,http://lueilwitz.com/gunnar_mcdermott,,214.12.43.170,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n5181,2,114,2017-04-08 14:13:57,http://braunhowell.name/aleandra,,15.213.114.65,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n5182,2,114,2017-04-14 00:48:08,http://haley.io/maye_renner,,176.179.180.115,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n5183,2,114,2017-02-21 22:31:37,http://volkmanbogisich.com/bridget,,145.195.175.116,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n5184,2,114,2016-12-21 05:20:03,http://schroeder.org/audra,,40.122.238.149,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n5185,2,114,2017-05-15 02:09:25,http://runolfsdottir.name/rhoda_hettinger,,136.189.84.210,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n5186,2,114,2017-02-13 01:37:24,http://morarkaulke.name/marcos,,22.42.198.241,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n5187,2,114,2017-02-01 05:33:06,http://kling.org/mazie,,78.148.231.130,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n5188,2,115,2017-01-08 06:07:17,http://bauch.biz/johnnie,,20.102.2.228,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n5189,2,115,2017-02-15 22:34:08,http://wisozkmitchell.name/dell_keebler,,139.52.92.109,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n5190,2,115,2017-03-04 13:52:30,http://kunze.org/delphia,,73.230.152.56,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n5191,2,115,2017-05-23 15:34:08,http://ziemann.org/wendell.mcdermott,,178.185.227.35,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n5192,2,115,2016-12-25 15:32:18,http://daughertydavis.co/ted_mcclure,,185.37.188.89,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n5193,2,115,2017-05-27 15:21:53,http://hills.org/jamir_crooks,,24.46.30.181,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n5194,2,115,2017-01-03 22:04:27,http://feil.net/arturo.koelpin,,106.65.84.158,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n5195,2,115,2017-02-08 02:30:46,http://wintheiser.co/rosie,,19.175.226.103,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n5196,2,115,2016-12-27 07:33:44,http://bruen.biz/jazmyne,,40.204.74.114,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n5197,2,115,2017-02-01 20:41:36,http://harvey.co/scottie_schultz,,88.207.7.75,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n5198,2,115,2017-04-08 11:40:05,http://hintz.com/name_brakus,,222.248.25.165,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n5199,2,115,2017-05-18 07:09:58,http://wittingjast.io/rebekah,,104.226.244.141,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n5200,2,115,2017-01-04 23:11:13,http://roobrowe.com/angela.mann,,231.245.227.177,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n5201,2,115,2016-12-22 14:43:12,http://boyle.biz/maya_schmeler,,75.84.24.126,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n5202,2,115,2017-01-16 17:02:11,http://gottlieb.info/malika.simonis,,28.239.53.30,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n5203,2,115,2017-03-19 07:47:16,http://greenfelderullrich.co/cortez,,244.123.157.155,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n5204,2,115,2017-03-27 13:48:36,http://ebert.biz/osbaldo_tillman,,53.95.80.197,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n5205,2,115,2017-01-25 13:05:43,http://koeppgulgowski.info/destin.herman,,6.253.223.120,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n5206,2,115,2017-02-11 20:50:44,http://lefflerkautzer.info/sister_gislason,,37.98.109.21,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n5207,2,115,2017-06-10 18:03:13,http://thompson.com/cristobal.schmitt,,22.31.82.31,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n5208,2,115,2017-01-15 10:33:49,http://ondricka.co/grace_krajcik,,134.208.69.114,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n5209,2,115,2016-12-16 17:41:26,http://schmidt.net/dortha_baumbach,,168.232.28.14,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n5210,2,115,2017-05-29 17:19:50,http://bahringerrunolfsdottir.co/madie,,183.123.249.140,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n5211,2,115,2017-03-24 16:04:25,http://webermcdermott.name/mariela,,192.65.43.56,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n5212,2,115,2017-01-28 11:33:10,http://schulist.io/sydnie,,169.60.138.15,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n5213,2,115,2017-02-15 22:28:38,http://okuneva.com/johnpaul_heaney,,57.61.100.116,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n5214,2,115,2017-05-09 15:27:04,http://little.io/ramon,,117.138.23.187,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n5215,2,115,2017-01-12 15:07:28,http://oreillyrodriguez.com/arlie_king,,38.18.47.5,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n5216,2,115,2017-01-29 13:25:30,http://crist.info/desiree_wilkinson,,237.150.99.156,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n5217,2,115,2017-06-03 16:49:49,http://handcorkery.name/joanne,,235.142.203.141,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n5218,2,115,2017-05-25 23:04:52,http://stehr.name/addie_bayer,,94.179.253.250,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n5219,2,115,2017-01-02 12:46:56,http://cartwright.org/bridie_hills,,50.158.9.145,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n5220,2,115,2017-04-20 19:50:20,http://funk.net/angelica,,20.162.208.29,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n5221,2,115,2017-03-30 16:31:19,http://goldner.co/christy,,18.137.246.152,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n5222,2,115,2016-12-25 23:07:31,http://kemmer.biz/lorena.emmerich,,83.80.48.83,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n5223,2,115,2016-12-19 14:40:06,http://shanahan.org/asa.thiel,,153.11.48.219,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n5224,2,115,2016-12-28 05:47:55,http://reingergerlach.com/hettie_kihn,,148.43.226.90,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n5225,2,116,2017-01-25 05:40:34,http://raynor.biz/nicolette.ward,,127.26.212.39,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n5226,2,116,2017-04-21 18:22:59,http://mckenzie.biz/noemie.crona,,86.94.88.134,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n5227,2,116,2017-04-29 20:36:21,http://adams.com/rhoda,,169.25.145.238,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n18153,7,411,2017-06-13 21:26:26,http://auerprohaska.biz/ryley,,123.45.24.61,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n18154,7,411,2017-03-11 15:51:44,http://adams.name/constance,,152.152.171.60,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n18155,7,412,2017-05-17 14:29:33,http://danielfadel.net/seth.krajcik,0.2475055007,81.226.82.247,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n18156,7,412,2016-12-19 08:06:19,http://borer.net/arno,0.4782058202,197.217.188.146,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n18157,7,412,2017-01-24 16:14:08,http://toy.name/bruce,0.6663642073,157.51.40.203,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n18158,7,412,2017-05-16 00:46:16,http://ritchie.com/earline_ebert,0.6653865978,216.33.210.246,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n18159,7,412,2016-12-19 14:24:09,http://schimmel.co/warren,0.4970929015,52.14.182.192,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n18160,7,412,2016-12-14 14:47:27,http://cole.biz/emmitt.kreiger,0.8944623803,86.235.152.48,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n18161,7,412,2017-05-31 14:53:43,http://harris.biz/jadyn_haag,0.7564550772,227.72.96.33,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n18162,7,412,2017-05-18 11:54:00,http://volkmanlangosh.name/oceane,0.8479121241,133.217.131.102,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n18163,7,412,2017-06-04 14:21:11,http://abernathy.io/zachery.thompson,0.8076185935,191.223.63.241,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n18164,7,412,2017-01-17 09:47:30,http://bechtelar.org/alycia.raynor,0.1166746351,146.169.230.173,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n18165,7,412,2017-02-18 08:16:56,http://graham.io/lily.upton,0.5038774821,175.109.254.160,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n18166,7,412,2017-01-15 22:20:11,http://strosin.org/elouise,0.4584685611,21.101.213.99,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n18167,7,412,2017-05-11 12:17:31,http://vandervort.co/van_ratke,0.1032804727,30.71.237.191,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n18168,7,412,2017-05-15 22:24:13,http://hammes.name/marilie.mckenzie,0.2116805410,104.236.198.245,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n18169,7,412,2017-02-09 13:58:18,http://kulasraynor.io/nicholas.corkery,0.2819116770,230.100.75.158,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n18170,7,412,2017-04-16 06:21:49,http://brakuhields.name/ella,0.4330045137,49.90.115.128,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n18171,7,412,2017-01-05 08:09:23,http://moore.info/monte_hammes,0.3060940720,163.55.26.143,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n18172,7,412,2017-03-15 08:19:27,http://prohaskalakin.biz/cole.osinski,0.0841574449,35.134.110.5,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n18173,7,412,2017-04-19 10:29:35,http://blick.info/jack,0.4917110323,59.82.71.221,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n18174,7,412,2017-05-25 20:29:15,http://lueilwitz.com/maritza.goodwin,0.2017217135,120.42.23.141,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n18175,7,412,2016-12-26 00:33:13,http://schoen.name/alvina,0.3748777697,115.100.172.183,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n18176,7,412,2016-12-20 19:45:27,http://jacobilubowitz.io/danny,0.2823873943,49.53.82.211,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n18177,7,412,2017-03-19 14:47:10,http://hodkiewicz.net/dominic,0.8112028144,87.74.144.90,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n18178,7,412,2017-02-19 04:08:03,http://fadel.io/benton,0.2670336509,231.208.152.79,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n18179,7,412,2017-03-23 03:04:14,http://marquardtweber.net/aubrey_gaylord,0.8277131989,37.108.200.64,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n18180,7,412,2017-04-09 07:24:39,http://metznicolas.info/lincoln,0.0585045907,13.58.229.173,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n18181,7,412,2017-05-26 07:20:01,http://friesen.org/jimmie_pfannerstill,0.4331628860,150.193.56.2,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n18182,7,412,2017-05-24 00:03:25,http://sanford.net/itzel,0.2567155166,244.118.53.164,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n18183,7,412,2017-03-11 16:44:27,http://hanerenner.net/samara.kulas,0.2081050704,248.80.209.45,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n18184,7,412,2017-04-07 19:16:17,http://wilderman.info/laron,0.8259666303,31.125.191.20,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n18185,7,412,2017-02-17 05:18:43,http://jones.net/callie,0.8025016162,160.178.47.250,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n18186,7,412,2017-01-15 08:41:25,http://jerde.biz/nash.hackett,0.3491432179,109.5.94.229,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n18187,7,412,2017-02-27 16:53:35,http://kuhn.biz/keshawn,0.0881032026,213.5.41.169,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n18188,7,412,2017-05-02 07:08:12,http://jones.net/kayli_carroll,0.2271281960,137.157.13.107,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n18189,7,412,2017-05-26 10:33:51,http://stantontillman.org/travis,0.6566856448,163.127.245.112,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n18190,7,412,2016-12-25 18:07:44,http://predovickunze.net/ivy.sporer,0.0499665640,74.10.145.24,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n18191,7,412,2017-05-04 15:32:37,http://fadel.net/amari_bechtelar,0.0491835816,243.69.101.81,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n18192,7,412,2017-01-29 23:12:50,http://weber.info/tre,0.4884859335,35.87.179.51,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n18193,7,412,2017-02-27 09:20:09,http://frami.info/demarco,0.0709808532,105.157.85.250,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n18194,7,412,2017-03-16 05:17:44,http://torphy.info/brendan,0.8288865903,190.115.132.191,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n18195,7,412,2016-12-31 14:32:08,http://schamberger.net/raul,0.9377594776,144.104.54.96,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n18196,7,412,2017-02-23 13:03:06,http://beer.name/tony_murray,0.6310415842,238.208.106.75,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n18197,7,412,2017-04-09 11:08:06,http://simonisokeefe.info/wilford,0.4366598755,182.72.171.64,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n18198,7,412,2017-05-29 02:43:05,http://colliermante.name/luz.denesik,0.5572316311,190.34.65.112,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n18199,7,412,2017-05-07 22:54:39,http://beierdeckow.org/wanda,0.5919323309,58.103.191.181,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n18200,7,412,2017-02-06 09:25:35,http://mclaughlin.net/cathy.rau,0.8721308937,24.166.110.102,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n18201,7,412,2017-03-22 11:06:17,http://hettinger.net/kaylin,0.4590516421,97.134.21.70,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n18202,7,412,2017-03-21 21:43:52,http://grant.net/delphine_weimann,0.0676858863,184.220.93.59,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n18203,7,412,2017-04-05 05:53:48,http://kertzmann.io/cicero_schroeder,0.0070825967,55.126.218.64,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n5228,2,116,2017-04-18 10:40:49,http://raynor.info/giovanna,,67.42.118.8,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n5229,2,116,2017-02-07 04:41:20,http://rippin.com/micaela_okon,,128.65.190.45,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n5230,2,116,2017-04-30 04:29:02,http://hoppe.com/tyrese,,219.239.178.152,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n5231,2,116,2017-01-26 21:49:45,http://lueilwitz.io/rachelle,,79.99.132.129,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n5232,2,116,2017-04-11 16:50:26,http://lowe.co/asha,,64.103.2.236,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n5233,2,116,2017-04-25 17:08:11,http://howellkuhlman.info/leie,,196.20.164.51,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n5234,2,116,2017-02-22 15:40:56,http://rempelshanahan.net/ian,,135.42.56.198,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n5235,2,116,2017-04-17 03:43:42,http://nicolaslakin.biz/dalton_connelly,,120.181.172.169,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n5236,2,116,2017-04-26 20:22:16,http://gleasoncormier.net/aubrey_zieme,,162.152.198.171,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n5237,2,116,2017-05-28 12:50:51,http://pfeffer.co/abelardo_dooley,,110.40.60.12,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n5238,2,116,2017-05-14 08:33:40,http://durgan.org/carolina,,50.168.223.172,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n5239,2,116,2016-12-28 03:56:26,http://dietrich.net/aleandra.white,,195.135.65.212,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n5240,2,116,2017-05-21 12:09:06,http://schroeder.info/oral.oberbrunner,,25.86.140.55,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n5241,2,116,2017-02-11 12:58:01,http://trantow.net/kellie_wolf,,31.2.102.38,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n5242,2,116,2017-04-26 05:19:05,http://kelerharber.net/myrtice,,232.92.158.46,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n5243,2,116,2017-05-14 10:15:48,http://quitzonmayert.net/luigi,,137.12.130.198,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n5244,2,116,2017-03-30 01:48:35,http://jastschneider.info/alfonzo_daugherty,,130.26.79.142,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n5245,2,116,2017-06-01 01:27:49,http://wolf.io/keaton_roberts,,117.171.10.122,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n5246,2,116,2017-02-24 06:38:08,http://roweschuster.co/brayan_feeney,,207.203.105.127,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n5247,2,116,2017-04-30 01:06:19,http://koelpin.com/selmer_bailey,,56.57.54.156,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n5248,2,116,2016-12-13 08:08:13,http://keler.co/dandre_moriette,,43.135.52.150,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n5249,2,116,2017-04-14 22:09:49,http://klocko.org/amparo,,91.88.38.70,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n5250,2,116,2017-03-09 23:13:57,http://gerlach.org/leila.ziemann,,156.32.28.254,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n5251,2,116,2016-12-19 01:25:40,http://hermiston.io/dominic,,21.66.240.210,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n5252,2,116,2017-01-14 16:57:24,http://hauckgleason.org/amya,,148.121.25.193,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n5253,2,116,2017-03-03 23:01:14,http://dubuque.info/ivy_pfannerstill,,170.73.206.207,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n5254,2,116,2017-01-14 03:32:50,http://koeppeichmann.co/janet,,117.180.98.28,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n5255,2,116,2017-02-26 09:39:24,http://heller.net/brycen.davis,,224.36.15.92,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n5256,2,116,2017-05-09 14:22:45,http://gleichner.io/braden,,109.193.91.240,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n5257,2,116,2017-05-26 10:39:46,http://barton.net/blake.romaguera,,161.111.208.219,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n5258,2,116,2017-02-26 03:05:36,http://reilly.com/clement_pfeffer,,114.59.30.70,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n5259,2,116,2017-05-21 18:49:11,http://nicolaskub.org/salvador_kuvalis,,145.76.244.229,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n5260,2,116,2017-02-24 03:47:34,http://dickikiehn.org/jayce_spinka,,148.187.56.172,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n5261,2,116,2016-12-29 23:30:11,http://botsford.co/mara,,50.170.34.213,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n5262,2,116,2017-01-23 20:51:50,http://rippinharvey.name/adonis,,55.123.79.16,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n5263,2,116,2017-01-28 13:12:59,http://strackepredovic.net/adelia_veum,,100.150.63.218,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n5264,2,116,2017-01-31 21:05:08,http://gutkowski.io/gonzalo,,91.33.167.140,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n5266,2,116,2017-05-26 18:32:11,http://jacobson.info/elliott,,196.237.243.245,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n5267,2,116,2016-12-26 19:20:05,http://green.biz/myriam_vandervort,,100.94.139.174,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n5268,2,116,2017-04-14 12:12:28,http://orn.io/ransom.stehr,,175.21.140.241,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n5269,2,116,2016-12-17 01:23:31,http://powlowski.name/mose,,105.75.157.161,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n5270,2,116,2017-03-13 13:07:00,http://keebler.io/luigi.schinner,,42.47.211.226,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n5271,2,116,2017-04-07 18:07:18,http://breitenbergfeil.com/orlando,,43.125.204.162,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n5272,2,116,2017-03-08 11:59:42,http://waelchi.net/mason,,134.186.119.188,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n5273,2,116,2017-06-04 21:23:39,http://mann.io/haan_feeney,,143.81.50.43,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n5274,2,116,2017-02-22 06:32:12,http://damoregorczany.name/raoul,,26.52.77.11,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n5275,2,116,2017-05-30 22:34:29,http://boyerbartoletti.io/ned.gerhold,,121.127.126.210,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n5276,2,116,2017-02-22 06:04:08,http://gislasonokon.net/noelia,,184.46.178.84,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n5277,2,116,2017-02-02 00:59:47,http://macejkovichaley.info/colby,,43.200.114.108,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n5278,2,116,2017-02-10 12:31:33,http://koeppstamm.com/jeie,,8.194.117.100,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n5279,2,116,2017-04-22 09:06:16,http://hanehammes.org/alisa,,58.161.187.217,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n5280,2,116,2017-03-09 08:27:11,http://lefflermurray.net/mack_leffler,,159.159.78.176,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n5281,2,116,2016-12-30 08:17:16,http://goodwinkautzer.name/kyra,,184.169.158.191,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n5282,2,116,2017-03-03 06:02:39,http://funk.biz/agustina_hodkiewicz,,225.215.53.84,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n5283,2,116,2017-02-26 13:47:21,http://rosenbaum.name/mary,,144.238.68.237,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n5284,2,116,2017-01-27 20:39:55,http://vonprosacco.name/bertram,,155.198.105.54,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n18204,7,412,2017-01-04 18:43:34,http://doylewiegand.co/lila,0.3290597401,79.99.155.166,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n18205,7,412,2017-05-19 12:12:34,http://nicolashodkiewicz.biz/paula.koepp,0.2387989665,37.63.253.87,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n18206,7,412,2017-05-04 05:16:55,http://emmerich.info/lesly.doyle,0.1129051210,88.70.145.249,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n18207,7,412,2017-02-01 22:59:15,http://casperkeler.biz/romaine_kuhic,0.8367403998,64.96.207.14,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n18208,7,412,2017-04-15 03:23:42,http://gusikowski.info/cathy.pagac,0.8062288962,251.207.194.66,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n18209,7,412,2017-03-16 20:51:06,http://osinski.name/ignatius_lueilwitz,0.0202311535,66.212.203.212,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n18210,7,412,2017-02-25 16:16:46,http://lefflersmitham.io/roselyn_zulauf,0.4649378848,26.170.60.196,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n18211,7,412,2017-05-01 14:23:27,http://creminpadberg.info/darien.becker,0.3505750577,144.135.76.165,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n18212,7,412,2017-06-01 05:23:03,http://collieroconnell.biz/davion_grant,0.2656894126,238.243.90.61,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n18213,7,412,2016-12-16 15:19:04,http://kulas.io/urban_prohaska,0.4349147808,94.160.230.189,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n18214,7,412,2017-06-12 11:09:42,http://auer.net/amari_jast,0.3545578736,231.133.201.55,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n18215,7,412,2017-02-20 16:07:14,http://gusikowski.net/darien,0.7843047747,56.184.211.14,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n18216,7,412,2017-04-23 11:31:16,http://jacobi.biz/estelle_stoltenberg,0.3283227170,225.5.163.58,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n18217,7,412,2017-03-03 03:39:15,http://lemke.name/keely_nolan,0.0504542907,247.139.204.38,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n18218,7,412,2017-04-24 16:54:03,http://olson.biz/josh,0.9849161055,46.216.116.158,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n18219,7,412,2017-05-19 01:20:18,http://schuppe.info/giovanny,0.3512212332,192.109.217.155,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n18220,7,412,2016-12-19 08:02:25,http://framischultz.info/oran,0.8722886873,130.82.79.189,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n18221,7,412,2017-01-18 08:36:47,http://kunde.info/margarete_cremin,0.0636933698,82.41.236.154,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n18222,7,412,2017-02-05 22:08:03,http://christiansen.org/chelsie,0.2410600369,137.233.206.102,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n18223,7,412,2017-04-10 00:01:12,http://kutch.com/shaylee_gusikowski,0.0465149048,110.65.213.195,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n18224,7,413,2017-03-12 07:22:31,http://kshlerinbeer.co/sasha.brekke,0.2984459835,54.209.246.130,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n18225,7,413,2017-01-09 19:14:48,http://reichelklein.biz/rebecca,0.9182663226,20.82.137.219,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n18226,7,413,2017-01-28 17:23:53,http://bogisichkovacek.biz/solon_kertzmann,0.1394795326,149.130.221.90,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n18227,7,413,2017-01-10 02:20:22,http://carterbernhard.net/thad,0.6196317298,145.94.102.64,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n18228,7,413,2017-03-31 21:54:52,http://gerhold.info/francesco.koelpin,0.0873193642,161.87.8.151,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n18229,7,413,2017-01-02 06:33:05,http://bergekeeling.org/lauren,0.0129682477,250.102.124.161,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n18230,7,413,2017-05-28 01:22:56,http://schumm.info/gwendolyn,0.4368173895,127.244.94.147,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n18231,7,413,2017-02-12 07:06:14,http://lehnerfeest.biz/cletus_murazik,0.4053395304,209.241.221.67,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": false}\"\n18232,7,413,2017-04-05 00:26:38,http://shanahan.name/arielle,0.7209472737,186.162.254.158,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n18233,7,413,2017-05-09 06:17:27,http://fritsch.org/brenda.dickinson,0.5336984521,122.48.30.114,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n18234,7,413,2017-04-10 06:15:46,http://zboncak.biz/amie_roberts,0.2549330612,97.224.48.23,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n18235,7,413,2017-01-26 07:45:16,http://ondricka.biz/terrence.ziemann,0.8904361777,115.53.140.36,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n18236,7,413,2017-01-07 05:02:02,http://pollichmacgyver.name/jonathan,0.4121121016,47.93.253.245,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n18237,7,413,2017-04-29 13:17:25,http://champlinbradtke.io/owen_stokes,0.4893869482,203.45.211.26,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n18238,7,413,2016-12-29 01:31:04,http://crooks.org/briana.simonis,0.2003453251,53.65.69.231,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n18239,7,413,2017-02-11 02:30:46,http://weber.io/gaston,0.9705766445,101.164.123.98,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n18240,7,413,2017-04-12 07:36:21,http://konopelski.co/emmie,0.7764639309,167.143.108.110,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n18241,7,413,2017-03-14 14:15:40,http://nader.io/tristin,0.4295140544,52.79.228.206,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n18242,7,413,2017-01-22 21:55:57,http://weinat.name/marvin,0.2219808952,202.109.152.10,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n18243,7,413,2017-05-21 17:08:10,http://ledner.com/darrick,0.1617750355,61.67.142.237,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n18244,7,413,2017-05-14 15:36:11,http://tillmanbeer.com/sigurd,0.6574607424,63.95.190.127,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n18245,7,413,2017-02-20 17:16:23,http://wisokycole.name/alyson,0.8661699089,243.81.23.140,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n18246,7,413,2017-02-13 13:28:22,http://cainwiegand.org/loyce_hilll,0.8388750611,243.89.129.182,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n18247,7,413,2017-04-04 19:32:22,http://metz.com/dereck.will,0.8557535488,163.4.79.181,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n18248,7,413,2017-05-06 14:45:39,http://crooksheel.io/adrienne_braun,0.2859725312,197.140.177.246,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n18249,7,413,2017-03-01 07:54:44,http://blickauer.name/luciano.kovacek,0.6083793394,198.76.140.156,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n18250,7,413,2017-01-04 13:48:25,http://franecki.name/mohamed.marks,0.1037669128,186.240.189.51,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n18251,7,413,2017-01-27 10:19:39,http://abshire.biz/logan.johnston,0.2402437716,136.229.229.209,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n18252,7,413,2017-04-11 15:18:43,http://ratkedare.net/adolfo_waelchi,0.6926983212,57.112.114.98,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n18253,7,413,2017-04-22 11:56:50,http://sipes.info/annetta.sawayn,0.7383155412,241.53.63.247,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n18254,7,413,2017-02-17 01:26:30,http://hillsthiel.com/moises,0.2941057518,71.73.110.98,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n5285,2,116,2017-06-11 06:57:25,http://stiedemannhackett.info/diana,,216.229.231.31,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n5286,2,116,2017-04-13 05:28:54,http://harrisbechtelar.co/ardella,,32.166.3.201,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n5287,2,116,2017-04-28 21:29:50,http://wiegand.biz/gerald,,119.12.127.45,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n5288,2,116,2017-05-12 23:15:34,http://keeblerreichel.name/sedrick,,100.118.188.225,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n5289,2,116,2017-04-15 02:01:27,http://lindgren.info/clemens,,49.232.141.110,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n5290,2,117,2017-02-10 13:13:54,http://hickle.net/griffin_gleason,,24.143.253.39,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n5291,2,117,2017-03-14 01:31:15,http://weinatbaumbach.com/tillman_will,,44.145.34.104,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n5292,2,117,2017-03-10 23:17:04,http://shields.name/pat_nienow,,84.80.59.219,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n5293,2,117,2017-05-21 12:18:38,http://kriskoelpin.net/jarod,,7.100.195.193,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n5294,2,117,2017-01-20 02:27:34,http://moen.name/unique,,39.248.113.228,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n5295,2,117,2017-03-21 07:03:52,http://pollich.com/westley,,10.66.75.157,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n5296,2,117,2017-03-12 00:51:38,http://howelllehner.co/camille,,239.81.98.251,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n5297,2,117,2017-05-01 09:45:24,http://lesch.info/xzavier_torphy,,89.13.120.151,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n5298,2,117,2017-05-16 22:59:30,http://monahangorczany.org/julianne,,127.4.65.251,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n5299,2,117,2017-01-27 12:12:10,http://creminbosco.org/kamren_wolff,,177.242.93.50,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n5300,2,117,2017-01-08 00:08:14,http://bayer.info/vivianne,,152.120.207.61,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n5301,2,117,2016-12-13 06:41:34,http://boyle.co/rosa,,103.91.54.147,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n5302,2,117,2017-01-11 19:08:56,http://oconnell.net/stephon_schuster,,228.157.155.60,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n5303,2,117,2017-02-02 02:42:06,http://hamill.biz/noelia_cummerata,,126.234.44.202,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n5304,2,117,2017-02-23 07:02:11,http://schulist.org/weston,,218.134.198.95,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n5305,2,117,2017-03-18 15:21:52,http://emmerich.name/antwon,,120.93.77.217,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n5306,2,117,2017-02-15 23:40:57,http://halvorson.net/dan,,96.244.145.35,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n5307,2,117,2017-05-06 11:34:47,http://grantgoldner.co/rafael.krajcik,,29.57.82.6,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n5308,2,117,2017-03-11 22:07:22,http://macejkovic.net/buster,,109.41.97.234,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n5309,2,117,2017-05-03 14:34:09,http://swaniawskitorp.net/caleigh,,172.229.23.208,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n5310,2,117,2017-02-08 04:47:03,http://kreiger.biz/nicklaus.turner,,77.106.97.21,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n5311,2,117,2017-02-25 22:02:39,http://barton.com/deanna,,180.225.191.231,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n5312,2,117,2017-02-12 09:36:44,http://kihnortiz.biz/bertha,,98.151.23.41,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n5313,2,117,2017-04-06 16:20:06,http://grahamwintheiser.org/jason_muller,,121.249.243.150,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n5314,2,117,2017-01-16 16:26:25,http://kiehnbuckridge.net/shayne_douglas,,184.220.105.228,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n5315,2,117,2017-02-08 03:25:20,http://friesen.name/rose,,188.57.92.141,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n5316,2,117,2017-04-08 09:43:07,http://fay.co/arlene.reinger,,166.156.164.15,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n5317,2,117,2017-02-15 21:20:20,http://jakubowski.biz/fernando.wisoky,,106.52.130.244,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n5318,2,117,2017-06-07 12:22:14,http://friesenbeer.info/newton,,144.150.217.211,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n5319,2,117,2017-01-15 13:57:21,http://waelchi.com/mortimer_weimann,,206.167.215.170,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n5320,2,117,2017-02-25 21:41:52,http://jenkinwaniawski.info/rebecca,,83.119.202.201,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n5321,2,117,2017-06-04 14:26:31,http://spinka.net/toby.cole,,146.243.38.198,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n5322,2,117,2017-06-11 21:49:12,http://bergstrom.info/eve_quigley,,41.176.120.16,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n5323,2,117,2017-03-02 13:09:27,http://johnson.info/madalyn,,22.90.164.89,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n5324,2,117,2017-02-25 23:37:30,http://cormier.biz/ora,,20.112.210.124,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n5325,2,117,2017-06-13 10:14:22,http://schaefer.org/mikel,,30.201.27.242,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n5326,2,117,2017-01-26 02:17:44,http://rempelbogan.info/melany,,122.122.164.227,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n5327,2,117,2017-06-06 01:04:15,http://krajcik.biz/rodolfo,,114.231.51.73,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n5328,2,117,2017-02-08 06:09:27,http://hansenmante.name/allison,,112.145.57.217,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n5329,2,117,2017-01-05 04:42:32,http://schneider.com/raleigh_stark,,69.171.53.112,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n5330,2,117,2017-01-13 21:23:47,http://emard.com/monte.purdy,,97.225.129.2,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n5331,2,117,2017-06-05 13:01:13,http://frami.co/jaleel_friesen,,93.26.241.64,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n5332,2,117,2017-05-09 02:31:52,http://hansenquitzon.org/sonny.kovacek,,136.160.209.19,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n5333,2,117,2017-04-12 08:15:26,http://weinat.org/mac,,99.142.77.215,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n5334,2,117,2017-02-18 23:58:22,http://considinebergnaum.name/blair.tillman,,64.83.62.63,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n5335,2,117,2017-05-11 09:18:49,http://sauer.net/mckenna.cruickshank,,194.226.178.214,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n5336,2,117,2017-04-20 15:06:30,http://ondrickarutherford.net/mario.volkman,,74.31.14.223,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n5337,2,117,2017-03-13 11:56:18,http://fahey.biz/giovanni,,3.195.50.229,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n5338,2,117,2017-04-23 07:13:41,http://goldner.co/beie_daugherty,,114.201.237.76,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n5339,2,117,2017-06-03 18:03:37,http://schmidtglover.io/lavina,,206.48.19.124,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n18255,7,413,2017-05-31 14:53:41,http://waters.name/dalton,0.9013478278,230.154.78.65,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n18256,7,413,2016-12-20 16:17:20,http://schultz.com/ivory_gusikowski,0.5175505230,44.210.148.194,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n18257,7,413,2016-12-14 01:34:12,http://treutelcasper.com/olaf,0.6439367974,247.237.12.35,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n18258,7,413,2017-02-08 16:09:44,http://connelly.info/monserrate.satterfield,0.8959012724,143.240.239.135,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n18259,7,413,2017-03-01 18:43:49,http://mraz.io/norbert_graham,0.0312851970,103.37.87.219,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n18260,7,413,2017-05-16 03:26:38,http://rolfsonwaters.info/quentin_hagenes,0.7973845640,201.224.136.123,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n18261,7,413,2017-01-11 13:55:38,http://schumm.info/chanelle,0.6937589904,50.72.186.216,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n18262,7,413,2017-05-23 16:59:51,http://oconnerharvey.io/floie.kemmer,0.4887354456,33.233.50.200,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n18263,7,413,2017-01-06 13:51:21,http://greenfelder.co/jermaine.schmeler,0.1359947353,119.27.75.47,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n18264,7,413,2017-04-26 00:35:31,http://keebler.com/easter,0.9430812407,18.74.118.5,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n18265,7,413,2017-02-09 20:49:49,http://dooley.biz/mariah,0.1612580775,210.121.89.51,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n18266,7,413,2017-05-11 16:35:03,http://pacocha.name/daphney.harvey,0.9105187406,165.131.162.39,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n18267,7,413,2017-04-27 19:16:55,http://thompsoncrona.io/deshawn.cain,0.3240834556,167.180.56.196,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n18268,7,413,2017-06-03 14:11:14,http://bosco.net/jazmyne.hammes,0.0247810521,156.115.21.142,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n18269,7,413,2017-03-06 09:15:26,http://schowalterkoepp.com/hillard.stroman,0.3895253312,94.82.10.110,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n18270,7,413,2017-04-07 17:28:36,http://wunsch.com/jolie,0.4561257136,251.11.163.71,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n18271,7,413,2017-05-06 23:00:19,http://schiller.name/caroline.murray,0.7837829566,122.197.144.4,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n18272,7,413,2017-02-24 12:50:06,http://kaulkekulas.com/kayli,0.7953028497,152.33.199.168,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n18273,7,413,2017-04-23 18:50:32,http://boyer.org/tillman,0.2293116863,126.97.169.95,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n18274,7,413,2017-03-25 07:22:36,http://fritsch.co/emilie_kulas,0.1777002933,14.47.189.3,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n18275,7,413,2017-01-26 07:06:05,http://mcclure.info/ivah,0.1713454340,188.122.136.35,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n18276,7,413,2017-03-15 13:37:10,http://paucek.org/rahul,0.0103650447,193.79.225.203,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n18277,7,413,2017-04-13 07:00:11,http://heathcote.com/justyn_gottlieb,0.2144179597,66.25.204.248,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n18278,7,413,2016-12-14 01:14:15,http://kilbacknicolas.info/arnaldo.funk,0.6845809790,197.226.115.148,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n18279,7,413,2017-02-06 10:25:20,http://greenkonopelski.net/frankie,0.1590915518,91.8.18.169,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n18280,7,413,2017-04-01 04:20:33,http://oconnell.com/georgianna.abshire,0.6365974172,49.220.24.253,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n18281,7,413,2017-02-09 09:39:08,http://jacobson.name/talon,0.8755106178,38.2.56.22,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n18282,7,413,2017-01-06 18:10:02,http://wilkinson.com/vallie_hartmann,0.7163069719,25.128.200.68,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n18283,7,413,2017-05-14 09:31:56,http://ruecker.com/eulalia.blanda,0.1918535759,191.82.161.162,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n18284,7,413,2017-03-18 06:26:49,http://mohrwintheiser.info/callie_armstrong,0.9563819129,192.82.190.72,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n18285,7,413,2017-01-22 02:39:01,http://hayesprohaska.info/herbert,0.6205671890,208.49.79.193,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n18286,7,414,2017-01-29 17:00:43,http://yostsmith.io/drake_lemke,0.3471171621,110.250.228.129,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n18287,7,414,2017-04-20 09:01:03,http://aufderhar.co/belle_botsford,0.4261460224,53.50.107.85,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n18288,7,414,2017-05-02 17:38:54,http://emard.name/antonia.sporer,0.3257829693,200.44.79.182,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n18289,7,414,2017-04-12 19:11:40,http://marvindooley.net/marge,0.4265294089,156.16.98.9,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n18290,7,414,2017-04-13 11:01:18,http://romaguera.biz/isobel,0.7755255087,205.223.55.80,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n18291,7,414,2017-02-03 01:30:24,http://jakubowski.name/ivy_runte,0.6707866046,127.30.57.158,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n18292,7,414,2017-05-10 06:57:02,http://wisoky.info/kurt_king,0.5296781054,71.140.16.246,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n18293,7,414,2017-02-13 18:45:14,http://spinka.co/wilhelmine,0.9517529948,151.165.29.99,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n18294,7,414,2017-06-02 00:33:40,http://kulas.io/aidan,0.5903294340,38.251.220.20,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n18295,7,414,2017-05-13 13:43:10,http://lehner.io/karley_kling,0.6276331548,8.22.139.133,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n18296,7,414,2016-12-29 03:40:28,http://wolff.name/baby_turcotte,0.6193567280,188.217.42.53,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n18297,7,414,2017-01-14 17:30:56,http://gulgowski.io/isabelle.bode,0.8469381621,132.135.10.39,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n18298,7,414,2017-04-25 00:03:43,http://howe.net/michel.hansen,0.4334255996,236.2.79.41,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n18299,7,414,2017-05-06 15:17:08,http://toy.io/beulah,0.9754207723,70.175.48.223,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n18300,7,414,2016-12-30 21:44:46,http://ullrichhermiston.com/jett,0.3454432664,223.17.211.54,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n18301,7,414,2017-05-18 00:18:44,http://goodwinbrakus.info/alvera,0.3355344511,145.6.174.237,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n18302,7,414,2017-05-03 14:45:34,http://greenholt.net/derrick_herzog,0.2678260256,54.200.21.151,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n18303,7,414,2017-02-08 16:01:39,http://hoeger.biz/manley.oconner,0.1364505300,241.210.188.35,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n18304,7,414,2017-05-06 06:23:25,http://leuschke.io/jonas,0.4403285856,166.50.114.172,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n18305,7,414,2017-03-01 00:47:46,http://waters.info/tamara_brakus,0.2485162583,135.174.3.128,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n5340,2,117,2017-01-23 16:50:29,http://boyer.com/leanna,,8.218.35.157,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n5341,2,117,2017-06-10 09:21:08,http://kuhn.org/eden_glover,,30.231.98.210,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n5342,2,117,2017-02-13 23:55:33,http://terryaufderhar.name/tremaine,,2.180.204.225,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n5343,2,117,2017-01-11 10:35:15,http://zboncak.net/aracely,,241.70.105.193,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n5344,2,117,2017-03-22 04:21:22,http://legrosdaniel.com/mikayla,,6.193.120.129,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n5345,2,117,2017-01-02 15:58:31,http://rippinquigley.net/rocky,,204.96.176.202,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n5346,2,117,2016-12-22 03:39:33,http://stamm.name/jacques,,47.132.145.213,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n5347,2,117,2017-01-13 09:25:37,http://lednerrau.io/tracey_harber,,182.137.240.156,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n5348,2,117,2017-03-28 00:30:33,http://huel.biz/christop,,241.238.180.64,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n5349,2,117,2017-01-08 16:30:18,http://renner.co/imani,,168.134.161.196,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n5350,2,117,2017-06-10 10:47:11,http://lindhuel.co/kareem,,97.204.248.101,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n5351,2,117,2017-01-11 17:57:44,http://kulas.info/guadalupe.waters,,15.220.94.205,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n5352,2,117,2017-03-29 01:08:20,http://welchkling.com/parker,,36.216.73.2,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n5353,2,117,2017-04-25 04:28:44,http://robel.biz/elaina,,102.164.109.218,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n5354,2,117,2017-01-30 07:21:46,http://bodekuphal.info/vito_konopelski,,45.149.5.134,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n5355,2,117,2017-01-03 16:25:56,http://bartell.name/demetrius_ankunding,,49.16.93.107,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n5356,2,117,2017-01-28 11:51:13,http://schroeder.net/ursula,,132.131.247.208,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n5357,2,118,2017-04-23 04:17:24,http://osinski.co/beaulah_stroman,,25.48.154.83,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n5358,2,118,2016-12-21 07:31:11,http://marks.io/jeyca.ebert,,84.139.175.174,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n5359,2,118,2017-03-13 10:55:23,http://durgan.net/donald.hagenes,,122.167.33.116,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n5360,2,118,2017-03-04 13:35:18,http://mayeroconnell.biz/vernice,,77.206.143.97,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n5361,2,118,2017-05-26 02:47:19,http://quigley.net/jason,,55.239.243.78,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n5362,2,118,2017-02-27 23:38:18,http://beckerzulauf.biz/velva,,139.39.46.249,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n5363,2,118,2016-12-24 18:03:23,http://gaylord.org/alba_lesch,,78.35.191.111,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n5364,2,118,2017-05-25 22:27:52,http://ankunding.co/mitchell,,136.206.166.74,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n5365,2,118,2017-05-14 02:56:58,http://rogahnbeer.co/leanne_wuckert,,181.92.207.143,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n5366,2,118,2016-12-31 17:15:50,http://gislason.net/otto_walsh,,152.254.228.73,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n5367,2,118,2017-02-17 15:21:06,http://feest.biz/elda,,198.30.225.179,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n5368,2,118,2017-05-13 20:01:36,http://baumbach.info/katheryn,,214.148.8.15,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n5369,2,118,2016-12-29 03:09:07,http://robelboyle.co/freddie_armstrong,,104.49.107.228,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n5370,2,118,2017-04-25 08:15:54,http://labadie.com/nickolas,,75.216.205.204,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n5371,2,118,2017-05-11 15:26:41,http://klein.com/elmo.smith,,26.190.54.22,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n5372,2,118,2017-02-04 21:57:14,http://nitzsche.io/carolina_nader,,145.128.134.20,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n5373,2,118,2017-04-24 19:39:19,http://cainkiehn.biz/elwyn,,140.114.34.92,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n5374,2,118,2016-12-31 12:31:27,http://keler.name/noel,,179.32.231.69,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n5375,2,118,2017-05-09 04:47:54,http://corwinjast.co/jackeline,,223.46.34.60,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n5376,2,118,2017-02-12 01:24:36,http://bode.name/melvina_schneider,,125.12.218.11,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n5377,2,118,2017-05-13 22:38:01,http://goldner.name/graham,,76.214.87.189,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n5378,2,118,2017-03-20 15:17:44,http://waelchi.biz/rolando,,58.82.121.145,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n5379,2,118,2017-04-13 23:48:04,http://gleichner.net/laurie_wolf,,113.9.119.224,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n5380,2,118,2017-04-23 18:27:41,http://jenkins.io/myles_walter,,70.194.155.134,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n5381,2,118,2017-04-14 21:20:41,http://feest.name/jennyfer_cain,,14.46.183.126,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n5382,2,118,2017-01-26 23:23:27,http://nikolaus.co/adelbert,,136.207.184.21,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n5383,2,118,2017-01-08 22:55:10,http://terry.co/pat.pouros,,44.10.138.63,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n5384,2,118,2017-03-24 19:42:26,http://leuschkeherman.co/telly,,161.40.225.153,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n5385,2,118,2017-01-01 04:10:26,http://jerdewillms.com/allison_stracke,,113.242.207.237,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n5386,2,118,2017-01-14 23:28:36,http://hilll.biz/vidal.pfeffer,,77.153.31.184,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n5387,2,118,2017-01-18 11:24:49,http://lockman.com/marlee.leffler,,218.36.203.229,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n5388,2,118,2017-03-24 03:09:24,http://lockman.biz/celine.armstrong,,143.205.232.65,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n5389,2,118,2016-12-17 18:35:19,http://schowaltermoriette.io/violette,,84.164.154.21,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": true}\"\n5390,2,118,2017-01-04 05:14:44,http://lowe.org/hailie.gaylord,,136.51.55.250,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n5391,2,118,2017-06-04 02:14:19,http://ullrich.org/glennie.grady,,117.234.64.126,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n5392,2,118,2017-04-07 21:03:16,http://mayert.biz/destin_wehner,,136.94.88.207,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n5393,2,118,2017-02-25 05:46:41,http://smitham.io/winston.oberbrunner,,129.148.243.228,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n5394,2,118,2017-04-23 17:54:47,http://hand.name/jordi.weinat,,36.57.232.125,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n5395,2,118,2017-04-06 10:51:49,http://schummbins.biz/sedrick,,24.88.62.153,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n18306,7,414,2017-04-02 20:41:31,http://mcdermott.net/elmo_hartmann,0.3384166287,216.208.222.229,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n18307,7,414,2017-03-16 04:17:32,http://kelerdoyle.com/domenica_romaguera,0.8955626990,10.168.10.45,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n18308,7,414,2017-04-02 11:20:13,http://sipes.net/louvenia_schinner,0.9150820092,241.138.42.70,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n18309,7,414,2017-03-27 23:26:11,http://cruickshank.net/micah_sanford,0.7745826326,88.8.20.116,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n18310,7,414,2017-06-12 10:08:51,http://schuppe.info/hipolito,0.4234439620,227.87.236.158,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n18311,7,414,2016-12-19 07:00:36,http://pouros.com/oliver.torp,0.4424891707,87.86.212.103,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n18312,7,414,2017-01-06 18:14:50,http://will.com/althea_murphy,0.9553044139,48.167.72.52,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n18313,7,414,2017-05-29 04:28:30,http://torphyeffertz.co/wiley.schumm,0.2838863125,145.54.129.28,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n18314,7,414,2017-03-13 22:31:20,http://grady.co/queenie.hartmann,0.5958307651,241.249.222.134,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n18315,7,414,2017-04-02 00:44:12,http://bahringerhaag.net/lysanne_murray,0.6001990054,12.35.248.179,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n18316,7,414,2017-02-01 06:30:56,http://nader.org/maryam,0.8590879427,161.61.66.172,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n18317,7,414,2017-02-11 14:49:28,http://schoen.info/eden_veum,0.5982209259,199.217.239.234,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n18318,7,414,2017-01-09 09:16:21,http://goodwinhintz.info/nola,0.6733536512,8.60.41.249,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n18319,7,414,2017-06-10 12:53:49,http://terry.biz/winnifred.cronin,0.4039770722,16.192.222.104,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n18320,7,414,2017-03-04 12:42:44,http://ruelkuhic.name/archibald,0.4342060105,246.3.118.206,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n18321,7,414,2017-04-02 20:03:20,http://dibbert.org/ocie,0.1498775225,167.91.46.135,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n18322,7,414,2017-01-18 13:51:38,http://armstrong.biz/lazaro_considine,0.5973222551,224.193.140.91,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n18323,7,414,2017-03-19 05:31:53,http://feest.io/maia.grimes,0.4433366735,3.177.236.214,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n18324,7,414,2017-06-01 15:26:32,http://funk.co/malcolm,0.3202596868,56.169.66.224,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n18325,7,414,2017-01-07 14:26:13,http://corwinmoore.co/jammie,0.3557559858,83.175.210.203,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n18326,7,414,2017-02-02 02:19:28,http://haley.net/jonathon,0.5828188318,105.56.159.194,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n18327,7,414,2017-05-31 19:22:24,http://howe.com/caden.mante,0.4879809430,225.94.136.242,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n18328,7,414,2017-02-06 13:41:13,http://ruelmosciski.name/morgan.kerluke,0.3001206586,190.241.121.183,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n18329,7,414,2017-05-12 20:01:09,http://larkin.biz/hulda.steuber,0.1903106680,17.169.156.199,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n18330,7,414,2017-03-14 15:31:12,http://hammes.org/kenna,0.4353369766,198.231.223.99,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n18331,7,414,2017-02-04 13:32:33,http://hageneshintz.co/rodolfo,0.4966014856,44.214.86.249,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n18332,7,414,2017-03-28 05:59:04,http://friesenorn.net/jose_kunze,0.3520171442,235.248.252.213,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n18333,7,414,2017-04-17 17:18:32,http://harber.org/devante.langworth,0.8598844506,61.241.159.82,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n18334,7,414,2017-04-26 15:42:50,http://smitham.info/maryjane.beer,0.8075898531,6.116.159.95,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n18335,7,414,2017-02-25 14:22:52,http://rempelkihn.co/greta_robel,0.2548119771,170.66.130.204,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n18336,7,414,2017-03-31 15:52:18,http://ratke.biz/buford_batz,0.9262118086,95.161.142.178,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n18337,7,414,2017-03-15 22:24:38,http://gutmann.io/litzy,0.6617881228,21.139.197.106,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n18338,7,414,2017-04-08 07:17:31,http://veumdach.org/kenyatta.hodkiewicz,0.0342793004,75.189.73.180,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n18339,7,414,2017-04-03 14:38:56,http://rice.info/armani_jaskolski,0.7877287657,172.247.106.213,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n18340,7,414,2017-03-25 12:30:18,http://hammes.net/bernie,0.3839381026,188.25.233.169,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n18341,7,415,2017-05-23 15:43:49,http://flatleyrosenbaum.info/darryl,0.0986228818,6.239.193.210,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n18342,7,415,2017-05-08 21:38:19,http://douglas.io/quinton,0.3063229357,47.249.138.151,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n18343,7,415,2017-06-12 22:48:02,http://ohara.io/frederick.kutch,0.1187569177,23.132.6.223,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n18344,7,415,2017-02-12 23:57:58,http://cartwright.net/ambrose,0.0516480678,53.233.121.77,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n18345,7,415,2016-12-17 12:19:27,http://littel.net/jenifer,0.4113069972,69.127.57.170,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n18346,7,415,2017-05-30 01:38:17,http://kris.co/jacklyn.nikolaus,0.0010740846,127.106.22.144,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n18347,7,415,2017-02-18 03:39:51,http://smithamtillman.org/ferne,0.1447721405,67.34.170.237,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n18348,7,415,2017-04-27 15:57:18,http://collierkilback.biz/rodrigo.lowe,0.8249812764,3.139.157.96,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n18349,7,415,2017-03-11 09:32:01,http://ritchiestrosin.net/alfonso.kuhic,0.4657332354,207.40.185.48,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n18350,7,415,2017-02-18 05:08:59,http://witting.biz/ashly.macgyver,0.9303896750,52.114.22.44,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n18351,7,415,2017-02-26 18:57:35,http://blockschmeler.co/troy.fritsch,0.2272574142,156.176.214.88,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n18352,7,415,2017-06-09 04:35:11,http://pouros.org/leonel_welch,0.1508420910,221.85.108.54,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n18353,7,415,2017-03-26 01:03:02,http://hayes.io/sienna_beier,0.6754990549,63.104.220.33,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n18354,7,415,2017-06-08 03:19:25,http://lebsack.com/cielo.reilly,0.7171427471,127.225.109.12,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n18355,7,415,2017-03-04 00:19:32,http://cole.org/drake,0.3359478071,102.108.159.168,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n18356,7,415,2017-02-25 04:56:00,http://purdy.com/brycen.ohara,0.4385667854,162.105.206.148,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n5396,2,118,2017-04-19 17:22:44,http://sanfordbogisich.co/donato,,182.209.243.205,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n5397,2,118,2017-01-19 18:53:44,http://heathcote.io/wava_walker,,228.81.124.158,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n5398,2,118,2017-05-26 08:22:12,http://bogisichmaggio.biz/dahlia_bogisich,,68.147.19.8,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n5399,2,118,2017-04-01 12:28:31,http://reynolds.io/emmalee.bogisich,,146.48.17.242,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n5400,2,118,2016-12-30 15:29:32,http://nitzsche.co/tavares_hegmann,,177.94.116.101,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n5401,2,118,2017-02-10 14:33:26,http://kiehnohara.org/rahul_braun,,246.5.207.242,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n5402,2,118,2017-04-13 15:37:17,http://windleroconner.biz/violette,,117.130.235.27,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n5403,2,118,2017-02-02 08:35:28,http://mertz.net/zena.aufderhar,,167.28.237.89,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n5404,2,118,2017-04-15 04:44:47,http://wardfahey.name/marley,,143.192.69.72,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n5405,2,118,2017-05-29 12:04:18,http://framiheathcote.com/niko.buckridge,,121.170.84.91,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n5406,2,118,2017-04-29 15:26:54,http://kochpagac.com/luna_carter,,210.159.197.109,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n5407,2,118,2017-02-04 17:21:04,http://volkman.name/delfina,,114.240.223.133,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n5408,2,118,2017-05-20 21:28:40,http://farrell.net/cole.mills,,156.203.23.120,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n5409,2,118,2017-03-27 07:23:22,http://sanfordraynor.org/frankie,,154.187.209.115,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n5410,2,118,2017-06-03 06:40:23,http://spencer.co/cleta,,169.115.176.219,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n5411,2,118,2017-05-07 16:15:42,http://hermann.co/lulu.mcclure,,65.136.117.186,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n5412,2,118,2017-02-03 09:41:06,http://hirthe.io/henriette.collins,,168.69.39.85,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n5413,2,118,2017-05-22 10:39:14,http://eichmann.com/kiara,,141.228.91.81,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n5414,2,118,2016-12-21 00:36:19,http://hirthe.co/nicklaus,,145.241.44.156,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n5415,2,118,2017-05-18 09:02:27,http://hamillturner.biz/janelle_cronin,,241.181.47.182,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n5416,2,118,2017-02-09 21:23:51,http://conroylynch.io/lucio_legros,,227.103.217.243,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n5417,2,118,2017-01-07 18:56:08,http://franecki.biz/marcel_hettinger,,182.67.220.6,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n5418,2,118,2017-04-21 18:01:28,http://padberg.biz/melyna.okuneva,,225.103.254.40,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n5419,2,118,2017-01-13 15:01:35,http://jastcain.name/nils.boyle,,252.8.76.10,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n5420,2,119,2017-05-27 08:34:53,http://browndooley.org/antone_witting,0.2492050845,185.131.102.72,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n5421,2,119,2017-01-22 17:32:18,http://steuber.info/edmond,0.6889339223,22.196.50.224,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n5422,2,119,2017-01-13 04:30:44,http://hettinger.net/alta,0.3449814628,56.67.73.140,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n5423,2,119,2017-04-19 12:00:02,http://emard.io/reyes,0.6671358567,47.193.22.20,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n5424,2,119,2017-01-04 13:24:03,http://roberts.info/pablo,0.5847843632,66.34.79.54,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n5425,2,119,2017-04-12 19:46:11,http://baumbach.com/solon,0.3586138928,90.68.251.94,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n5426,2,119,2017-05-13 02:53:16,http://mclaughlin.net/kali,0.9559220816,99.248.234.49,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n5427,2,119,2017-02-22 14:07:56,http://rippin.name/manuela,0.8009746544,138.162.87.206,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n5428,2,119,2017-03-09 04:04:27,http://schulist.net/jeromy,0.4053409821,235.64.128.128,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n5429,2,119,2017-03-05 16:48:13,http://cronatrantow.com/angelo,0.5994320575,75.80.254.114,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n5430,2,119,2017-02-23 23:21:47,http://larson.net/joan_price,0.0779456961,20.3.131.129,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n5431,2,119,2017-02-14 07:05:26,http://stehr.co/neha,0.9459188907,237.60.180.144,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n5432,2,119,2017-05-28 07:15:15,http://ohara.io/ethelyn.grady,0.2506039447,160.202.72.48,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n5433,2,119,2017-04-03 17:14:54,http://franecki.io/carter_king,0.2689586009,206.31.217.71,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n5434,2,119,2017-05-20 23:26:34,http://mante.net/triston,0.8348596354,228.148.54.180,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n5435,2,119,2017-06-09 05:09:06,http://lefflerwalsh.com/oren.macgyver,0.5333056756,169.42.217.183,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n5436,2,119,2017-02-03 20:44:12,http://schamberger.co/heath_metz,0.9337192633,160.74.18.61,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n5437,2,119,2017-04-10 11:38:49,http://hilpert.net/domingo,0.7108595683,16.69.200.29,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n5438,2,119,2017-01-07 04:53:43,http://beier.io/theresa.maggio,0.5161988492,194.126.49.26,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n5439,2,119,2017-06-12 01:35:16,http://moen.biz/harley,0.0236461932,130.157.209.85,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n5440,2,119,2017-01-21 00:06:07,http://boylemurray.biz/kamren,0.9578046921,2.200.4.30,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n5441,2,119,2017-06-07 05:00:23,http://lehner.name/emilio,0.8817157658,90.115.90.172,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n5442,2,119,2017-03-05 06:40:47,http://ziemannfahey.name/roberta.kunze,0.2809998475,248.79.104.173,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n5443,2,119,2017-02-28 01:02:49,http://faylindgren.co/christian,0.0817535122,144.12.165.21,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n5444,2,119,2017-06-12 09:04:37,http://parisian.biz/rosamond_mills,0.9875076698,174.37.241.150,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n5445,2,119,2017-01-23 11:42:09,http://deckow.io/freddy.schuster,0.8964179086,214.66.172.85,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n5446,2,119,2017-01-26 11:50:40,http://ondrickamiller.name/isaac_walter,0.3578197129,117.49.70.203,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n5447,2,119,2017-05-18 07:55:38,http://skileshammes.org/shana,0.6273497769,101.237.75.248,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n5448,2,119,2017-01-24 01:49:37,http://bergnaum.io/brandi.von,0.3510352277,135.189.26.245,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n18357,7,415,2017-05-20 13:34:28,http://lebsackvonrueden.net/felicia.farrell,0.9536816444,145.121.92.59,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n18358,7,415,2017-01-16 10:13:43,http://howehahn.org/jeie.conn,0.2142225308,74.68.162.221,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n18359,7,415,2017-02-13 07:52:07,http://greenholt.info/timmothy,0.5677431901,10.164.225.88,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n18360,7,415,2017-05-17 08:38:51,http://welchbergstrom.co/mariano,0.4017832524,184.63.226.142,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n18361,7,415,2017-01-22 12:18:37,http://millerhyatt.org/samir.kiehn,0.4597164152,30.238.226.234,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n18362,7,415,2017-04-16 00:24:26,http://windler.info/naomi,0.7825656634,246.152.18.148,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n18363,7,415,2017-04-25 13:35:18,http://parisian.net/ken.emmerich,0.3402118103,254.63.142.191,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n18364,7,415,2017-05-26 06:19:18,http://grimes.co/adelle_wisozk,0.5277950595,15.49.38.106,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n18365,7,415,2017-05-19 00:08:00,http://greenfelder.com/melvina_schinner,0.7159736506,74.116.104.239,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n18366,7,415,2017-03-11 07:26:46,http://pacochatorphy.co/roberto.howell,0.0965114058,77.245.40.57,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n18367,7,415,2017-04-13 06:32:08,http://von.io/cruz.gottlieb,0.4895133213,89.61.22.70,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n18368,7,415,2017-01-05 04:11:58,http://harberheller.com/joan.donnelly,0.1359900122,144.77.214.52,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n18369,7,415,2017-03-29 10:47:59,http://riceko.biz/hillary_schmeler,0.7429887969,31.238.209.237,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n18370,7,415,2017-01-29 17:07:20,http://kuhicprice.biz/roy.leuschke,0.5342587255,119.24.37.138,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n18371,7,415,2017-05-20 23:21:15,http://runolfsdottir.info/brianne.schuppe,0.8725656993,39.122.33.21,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n18372,7,415,2017-01-09 09:27:29,http://weber.com/eddie.herzog,0.3876463258,163.181.90.31,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n18373,7,415,2017-06-03 03:28:04,http://mcglynn.info/lafayette,0.4242364573,51.225.191.109,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n18374,7,415,2017-05-31 10:57:29,http://cummings.info/carole.schinner,0.3258437098,67.169.228.76,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n18375,7,415,2017-03-30 21:06:02,http://murphy.info/delbert,0.9964740587,88.105.143.234,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n18376,7,415,2017-01-04 16:35:12,http://weimannbaumbach.name/devon,0.8040280540,168.36.17.18,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n18377,7,415,2017-04-03 09:42:19,http://gaylord.net/nicklaus,0.6647349338,233.11.95.221,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n18378,7,415,2017-03-29 10:49:47,http://rutherford.org/daron,0.8493735973,140.10.152.240,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n18379,7,415,2017-04-19 19:00:20,http://mante.com/arvid,0.8997131259,53.222.133.215,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n18380,7,415,2017-01-18 15:33:06,http://monahanryan.name/rodger_nienow,0.8026228335,166.8.65.135,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n18381,7,415,2017-04-11 06:24:17,http://smitham.name/kaley,0.3342658064,30.174.40.47,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n18382,7,415,2016-12-21 01:34:31,http://rolfson.info/angela,0.3189572262,75.19.35.153,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n18383,7,415,2017-03-12 23:39:41,http://trantowhilpert.info/tracy.bashirian,0.6971272872,149.121.27.77,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n18384,7,415,2017-04-21 16:50:26,http://pfeffer.io/ralph,0.6162218182,158.238.24.129,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n18385,7,415,2017-04-05 17:50:35,http://grant.co/marianna,0.8227554408,247.78.151.119,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n18386,7,415,2016-12-27 21:30:19,http://gorczanydeckow.net/gabriella_jast,0.1941117073,202.251.153.217,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n18387,7,415,2017-04-28 00:11:43,http://bernierwhite.biz/isobel_hilll,0.1677040099,184.39.59.185,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n18388,7,415,2017-01-17 08:27:23,http://stiedemann.net/reie,0.7417731626,9.62.138.115,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n18389,7,415,2017-01-30 04:18:28,http://murrayconn.biz/rafael,0.6332637990,201.95.16.244,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n18390,7,415,2017-01-11 10:06:30,http://mueller.co/tobin,0.0375753172,121.206.184.39,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n18391,7,415,2017-02-27 07:30:15,http://block.net/lupe_friesen,0.6157693426,179.251.227.11,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n18392,7,415,2017-03-21 03:28:38,http://fahey.com/myles_kihn,0.8542414493,192.4.121.98,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n18393,7,415,2017-02-25 19:39:23,http://altenwerth.co/dejah.medhurst,0.4959573022,73.152.117.126,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n18394,7,415,2017-03-15 13:34:46,http://oreilly.info/elta.collier,0.9227965553,200.212.154.242,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n18395,7,416,2017-04-25 10:17:48,http://rowe.info/harry,0.1000436170,235.205.51.232,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n18396,7,416,2017-05-06 00:34:34,http://hermiston.net/linwood_ankunding,0.6730097068,66.105.84.204,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n18397,7,416,2017-03-20 02:27:19,http://kerluke.co/olen_hauck,0.8659692913,88.170.65.154,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n18398,7,416,2017-04-09 23:57:58,http://prohaskahodkiewicz.co/myah,0.6624466028,21.3.193.129,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n18399,7,416,2017-02-15 09:38:19,http://leuschkehettinger.name/kasey_koepp,0.7602539492,63.214.72.212,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n18400,7,416,2017-01-19 09:51:57,http://oconnellhaley.name/jammie,0.4470441458,219.232.26.194,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": false}\"\n18401,7,416,2017-06-14 03:45:16,http://shields.name/kameron_corkery,0.1520191230,244.68.206.186,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n18402,7,416,2017-05-10 07:46:50,http://hahn.info/aisha.jacobson,0.6517950225,85.247.117.190,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n18403,7,416,2017-01-10 23:09:53,http://heaneywilkinson.biz/ethelyn.lockman,0.9555444550,146.92.206.64,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n18404,7,416,2017-05-30 13:34:23,http://mccullough.org/bella_blick,0.0434454133,243.135.52.203,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n18405,7,416,2017-03-28 08:14:03,http://davis.net/kennith,0.5509550306,83.226.129.208,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n18406,7,416,2017-05-21 02:21:09,http://kerluke.com/lionel_hackett,0.7642745867,47.8.60.183,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n18407,7,416,2017-02-06 04:37:22,http://treutel.net/clair,0.4122643459,6.141.207.185,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n5449,2,119,2017-04-30 01:37:07,http://kiehn.com/sarah_schuster,0.6088684164,251.13.125.89,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n5450,2,119,2017-01-06 08:17:24,http://glovercummings.io/reie.muller,0.5765107641,97.13.82.37,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n5451,2,119,2017-04-02 09:34:39,http://herzog.name/audreanne,0.4674742074,195.135.97.208,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n5452,2,119,2017-01-28 07:05:15,http://jacobson.net/zora,0.5937096285,205.12.150.187,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n5453,2,119,2017-06-12 11:41:26,http://deckow.biz/jeremy_donnelly,0.7162913971,159.122.98.58,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n5454,2,119,2017-02-27 15:33:01,http://shanahanswaniawski.info/devin.treutel,0.9994977374,159.112.33.99,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n5455,2,119,2017-01-25 20:19:28,http://jacobs.net/ashleigh,0.2400971339,39.71.93.121,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n5456,2,119,2017-05-19 17:04:03,http://hilll.com/barry.gusikowski,0.3023623080,148.19.168.134,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n5457,2,119,2017-03-17 04:50:45,http://reingerbradtke.net/nicola,0.6237094183,217.22.34.237,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n5458,2,119,2017-06-02 11:41:44,http://yundtbogisich.name/rusty,0.2850381029,162.76.153.144,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n5459,2,119,2017-01-31 20:45:34,http://tillman.com/kathryne_pouros,0.7941343620,133.210.186.244,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n5460,2,119,2017-01-29 17:34:44,http://gulgowski.name/marcelina.lemke,0.6034034464,55.220.197.159,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n5461,2,119,2017-04-22 19:02:41,http://quigley.net/hertha_skiles,0.3113281002,111.33.189.214,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n5462,2,119,2017-01-05 19:38:30,http://hirthemonahan.org/ryan.stiedemann,0.7415625376,3.117.235.227,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n5463,2,119,2017-04-19 18:40:48,http://fisherschaden.org/oswaldo.schroeder,0.9107412211,165.222.121.123,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n5464,2,119,2017-01-26 00:32:43,http://bosco.io/tavares_kshlerin,0.9868495826,5.219.187.130,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n5465,2,119,2017-05-02 11:05:15,http://cartwright.org/meggie,0.3750326552,139.77.238.15,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n5466,2,119,2017-05-24 18:28:04,http://huel.biz/ferne_trantow,0.4929106406,169.24.99.196,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n5467,2,119,2017-05-17 12:15:41,http://mannolson.io/richard,0.2008329064,30.17.52.159,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n5468,2,119,2017-05-16 19:25:41,http://dietrich.biz/marquis.rath,0.0525561023,44.63.124.241,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n5469,2,119,2016-12-31 18:44:06,http://tromp.net/jimmy_adams,0.2472869188,84.16.106.64,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n5470,2,119,2017-01-03 22:31:20,http://willmawayn.co/conrad,0.0410864125,124.93.155.150,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n5471,2,119,2017-05-31 02:28:25,http://johnston.name/otto.abshire,0.7553744116,70.2.82.2,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n5472,2,119,2017-03-04 02:23:46,http://ratke.biz/summer,0.4940357971,155.16.90.67,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n5473,2,119,2017-04-27 03:18:55,http://gradygoyette.com/name,0.5868864724,28.235.81.9,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n5474,2,119,2017-01-16 23:30:47,http://pouroswatsica.biz/janis_murphy,0.8236873187,151.239.52.134,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n5475,2,119,2017-06-09 15:50:50,http://pagac.biz/ola,0.3533620915,219.188.110.21,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n5476,2,119,2017-02-25 07:34:06,http://marks.net/myrl,0.3708815102,103.226.243.25,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n5477,2,119,2017-05-17 04:10:16,http://larson.com/gilberto,0.7878404327,40.12.49.13,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n5478,2,119,2017-02-25 12:47:56,http://leannon.info/hellen,0.9326705066,216.123.149.148,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n5479,2,119,2017-03-13 23:19:03,http://leuschke.biz/alvis.bruen,0.2949698907,71.72.90.222,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n5480,2,119,2017-06-06 04:10:06,http://jones.info/josue_emard,0.8785397550,100.70.173.61,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n5481,2,119,2017-03-07 21:23:26,http://lowe.info/ramiro,0.7410858999,44.79.116.75,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n5482,2,119,2017-04-07 11:54:36,http://grimes.co/collin.kemmer,0.4221687637,98.60.113.75,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n5483,2,119,2017-04-02 07:32:42,http://lemke.net/ashley,0.3493839012,72.212.126.181,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n5484,2,119,2017-05-14 04:39:13,http://donnellystamm.info/gaston.doyle,0.1623175422,209.251.55.57,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n5485,2,119,2017-02-25 14:37:27,http://erdman.biz/elenora,0.3852012353,234.39.208.101,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n5486,2,119,2017-03-17 07:39:36,http://gorczanyyost.info/magnus,0.1895678989,118.86.81.90,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n5487,2,119,2017-05-09 03:32:11,http://grimes.net/willy.johnson,0.5324589253,156.6.202.29,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n5488,2,119,2016-12-20 21:28:00,http://botsford.name/nels,0.4280526859,94.182.14.2,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n5489,2,119,2017-01-15 09:32:44,http://olson.biz/erna,0.8510849072,57.57.94.72,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n5490,2,120,2017-01-22 00:18:47,http://wiza.biz/coty_marquardt,0.9543919375,130.17.113.22,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n5491,2,120,2017-03-17 13:53:30,http://king.name/jerrod_cummerata,0.0099144057,153.167.5.212,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n5492,2,120,2017-05-22 23:28:48,http://pfannerstillcorwin.name/roel,0.9149966980,227.62.61.247,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n5493,2,120,2017-06-01 17:49:23,http://mante.co/nia,0.2417519213,165.135.22.35,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n5494,2,120,2017-03-23 16:43:40,http://douglas.biz/kaylin,0.7204583233,224.65.207.166,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n5495,2,120,2017-01-09 21:20:46,http://raynor.org/deie.white,0.5578054638,253.61.151.218,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n5496,2,120,2017-03-24 00:44:13,http://haley.biz/dominic.mraz,0.3266337348,94.8.62.176,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": false}\"\n5497,2,120,2017-03-12 17:14:25,http://dubuquecremin.name/junius.gerhold,0.3456903413,41.24.106.141,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n5498,2,120,2017-01-22 15:39:56,http://prohaskaemard.org/laria,0.5743522560,166.240.136.24,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n5499,2,120,2017-06-08 22:33:46,http://anderson.biz/keara_sipes,0.3569993285,239.85.56.16,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n5500,2,120,2016-12-17 07:33:56,http://quitzon.com/alan,0.5368718563,50.212.196.226,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n18408,7,416,2017-05-28 02:17:59,http://spinka.io/charles_bartoletti,0.2675935009,97.177.237.69,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n18409,7,416,2017-02-17 06:12:41,http://goldnertreutel.org/alison,0.9935325114,8.131.71.87,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n18410,7,416,2017-01-14 08:19:22,http://rempel.name/oliver.swaniawski,0.1963997616,123.73.175.42,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n18411,7,416,2016-12-24 11:36:38,http://heaney.name/tracey,0.6645956207,55.63.78.232,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n18412,7,416,2017-02-02 23:49:21,http://balistreriosinski.biz/casper,0.4781632376,201.143.176.94,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n18413,7,416,2017-04-09 16:22:18,http://baumbachkovacek.org/nicklaus_dibbert,0.4771254355,22.186.158.99,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n18414,7,416,2017-04-09 12:01:33,http://gutkowski.info/verona.jacobson,0.1794544146,194.98.227.98,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n18415,7,416,2017-03-22 05:45:51,http://stiedemann.com/rosella,0.8259341356,74.33.74.240,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n18416,7,416,2017-06-04 18:34:50,http://blanda.co/mekhi_ondricka,0.4273942055,46.9.66.124,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n18417,7,416,2017-03-12 22:22:35,http://nitzscherutherford.info/webster,0.2440273726,192.94.63.194,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n18418,7,416,2017-05-28 04:43:46,http://ankundinghudson.co/evalyn,0.6610370025,225.145.247.109,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n18419,7,416,2017-01-12 09:22:27,http://fahey.com/kory_wolff,0.3300961927,95.216.150.22,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n18420,7,416,2017-05-07 23:59:05,http://mosciski.com/jonas.abshire,0.8750618151,39.17.57.235,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n18421,7,416,2017-04-22 15:30:47,http://marquardt.co/blanche,0.7325480628,161.119.54.146,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n18422,7,416,2017-06-05 08:47:09,http://hillldouglas.name/candelario_batz,0.6504104899,200.207.175.151,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n18423,7,416,2017-06-06 20:50:29,http://christiansengoodwin.io/sigurd,0.1091466296,93.216.210.110,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n18424,7,416,2017-05-07 21:50:13,http://wisoky.info/austin.price,0.8093448163,9.80.254.180,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n18425,7,416,2017-02-23 08:04:46,http://kirlin.org/shane,0.7029212359,65.31.23.21,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n18426,7,416,2017-05-13 11:43:11,http://reichel.com/jayde,0.4266690125,22.66.166.120,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n18427,7,416,2016-12-19 02:26:13,http://raynor.co/cristopher_wolff,0.9422052493,10.26.178.224,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n18428,7,416,2017-03-07 03:22:37,http://rutherfordbecker.co/frieda,0.4307102854,123.144.150.191,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n18429,7,416,2017-01-26 03:35:58,http://blickkaulke.info/caria_trantow,0.3313317357,140.69.68.180,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n18430,7,416,2017-02-03 19:21:35,http://oconner.biz/lois,0.4111703541,79.139.203.30,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n18431,7,416,2017-03-25 08:46:28,http://hintz.org/vincenza,0.2862307847,152.73.236.145,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n18432,7,416,2017-02-14 14:24:19,http://davis.biz/elmo_von,0.8156368163,206.198.248.190,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n18433,7,416,2017-01-26 23:03:25,http://gutkowski.net/franco,0.7818963322,39.93.126.242,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n18434,7,416,2017-02-16 15:02:49,http://bahringer.net/jeramie,0.5548689796,199.94.228.112,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n18435,7,416,2016-12-27 10:50:32,http://mosciski.name/itzel.bartell,0.5050450257,13.163.183.164,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n18436,7,416,2017-01-30 11:46:45,http://connmayer.com/aurelio.gutkowski,0.0671781701,212.194.182.135,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n18437,7,416,2017-04-16 23:13:36,http://fay.name/vella,0.1384333417,107.120.200.219,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n18438,7,416,2017-05-26 11:04:40,http://sengerdavis.info/brent_boyer,0.9842614826,22.142.58.62,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n18439,7,416,2017-02-10 02:29:18,http://kertzmann.biz/tremayne,0.0081475036,208.217.93.70,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n18440,7,416,2017-04-30 01:41:20,http://padberg.biz/skyla.bailey,0.1730928909,90.153.123.181,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n18441,7,416,2017-03-17 18:38:24,http://okeefe.co/gayle_harvey,0.1738790582,201.123.101.43,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n18442,7,416,2017-03-09 16:37:40,http://roob.com/tyreek_pacocha,0.4926428450,178.206.200.29,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n18443,7,416,2017-03-14 23:53:59,http://gleason.io/bart.wolff,0.9711146429,236.162.223.173,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": true}\"\n18444,7,416,2017-03-03 09:42:22,http://greenfelderlegros.io/major_dietrich,0.5455859016,191.184.92.250,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n18445,7,416,2017-04-22 12:52:26,http://kaulke.io/mafalda,0.9391166676,100.220.71.66,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n18446,7,416,2017-06-12 01:23:43,http://emmerich.name/aileen_wolff,0.6660278943,248.27.139.191,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n18447,7,416,2017-02-24 06:47:42,http://schaden.com/candice_gaylord,0.5078758378,230.241.248.254,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n18448,7,416,2017-04-08 00:14:54,http://rogahnquitzon.name/jonathan_marks,0.7688430350,130.165.152.211,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n18449,7,416,2017-01-17 14:15:46,http://macgyver.co/nicholaus.schroeder,0.1805354450,13.236.36.192,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n18450,7,416,2017-04-27 13:25:08,http://hand.org/domenic_ullrich,0.7644790687,153.118.109.157,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n18451,7,416,2016-12-15 17:22:19,http://will.name/jeremie,0.0782472640,89.40.193.147,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n18452,7,416,2017-05-23 20:47:38,http://dickens.com/whitney,0.2793112364,147.90.246.77,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n18453,7,416,2017-01-31 19:55:00,http://mccullough.biz/marion,0.1604298044,25.197.139.110,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n18454,7,416,2017-02-12 22:27:06,http://steubercrooks.io/edwardo,0.7004791023,179.134.211.171,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n18455,7,416,2017-03-10 09:03:33,http://gutkowski.co/jeromy,0.0404074546,133.49.192.181,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n18456,7,416,2017-03-11 16:43:58,http://wiegand.com/joel_lesch,0.2865265216,231.8.181.133,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n18457,7,416,2016-12-31 13:34:32,http://torphykuvalis.com/lucio,0.2121208940,159.220.183.149,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n18458,7,416,2017-01-03 11:15:27,http://bayer.org/malvina,0.4429551832,215.234.20.130,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n5501,2,120,2016-12-18 09:04:20,http://rathmueller.co/mylene.dubuque,0.8259377309,85.216.113.24,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n5502,2,120,2017-03-24 19:41:10,http://bartoletti.com/angeline.lynch,0.1328882972,252.28.210.227,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n5503,2,120,2017-06-05 01:22:14,http://schmidtwest.info/adolph_jacobs,0.1044139011,137.27.45.63,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n5504,2,120,2017-02-10 07:29:09,http://pfannerstillemard.org/martin.bayer,0.9687006020,184.237.178.80,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n5505,2,120,2017-02-25 07:51:45,http://schulisthudson.name/cielo_schneider,0.5173888883,176.242.68.251,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n5506,2,120,2017-06-01 07:49:39,http://hackett.org/marjolaine,0.6093732472,150.248.34.30,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n5507,2,120,2017-02-21 14:30:41,http://torp.co/carmel,0.8291072256,197.253.200.209,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n5508,2,120,2017-03-05 03:20:52,http://bauch.org/zachariah.sipes,0.7318775478,212.49.185.54,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n5509,2,120,2017-02-26 21:33:24,http://christiansen.info/veda.hackett,0.3498928026,22.244.100.167,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n5510,2,120,2017-03-07 20:27:53,http://berge.com/eddie.botsford,0.7322815564,148.54.148.46,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n5511,2,120,2017-03-30 15:50:13,http://willms.name/gilberto,0.9886240855,35.229.52.34,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n5512,2,120,2017-03-21 19:11:22,http://fahey.com/nat,0.6434010238,60.170.187.167,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n5513,2,120,2017-06-11 14:47:01,http://mills.info/josephine,0.6670096829,208.31.46.160,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n5514,2,120,2017-03-30 03:44:55,http://gottlieboconnell.name/mathias.fritsch,0.5181405504,81.37.166.70,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n5515,2,120,2017-01-12 22:13:53,http://westraynor.com/dawson.renner,0.2703050966,206.141.126.157,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n5516,2,120,2016-12-24 16:53:20,http://morarjacobi.io/jenifer_lindgren,0.7823292917,232.127.229.242,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n5517,2,120,2016-12-31 06:12:00,http://jaskolski.info/agustina.welch,0.5758787800,42.119.245.8,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n5518,2,120,2017-03-01 08:06:30,http://torphy.co/keely_emmerich,0.9318438674,168.245.53.4,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n5519,2,120,2017-04-22 06:30:51,http://anderson.name/selena,0.7408079713,21.163.6.34,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n5520,2,120,2016-12-16 08:23:02,http://sauerhane.com/walter,0.5390974594,225.42.59.203,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n5521,2,120,2017-02-10 17:58:00,http://howe.io/johnathan,0.8119888924,194.11.31.20,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n5522,2,120,2017-04-23 01:39:10,http://bauch.biz/travon,0.2684072771,124.77.78.66,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n5523,2,120,2017-03-30 19:01:59,http://leffler.net/ocie.stiedemann,0.8027683249,51.4.90.53,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n5524,2,120,2017-04-26 19:56:21,http://lemkeherman.net/annette_kiehn,0.9662531647,11.81.145.43,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n5525,2,120,2017-01-25 18:14:32,http://kuhlman.com/leonard,0.1965516624,173.155.207.174,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n5526,2,120,2017-05-20 12:31:08,http://grimes.info/rhiannon.wilderman,0.1423853210,14.147.71.111,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n5527,2,120,2016-12-26 06:43:05,http://mertz.com/joey_auer,0.5780280170,219.129.195.37,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n5528,2,120,2017-03-28 23:58:11,http://rueckerharber.info/tracy_wisozk,0.0548812848,229.37.68.232,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n5529,2,120,2017-02-06 09:41:43,http://armstrongquigley.biz/marjolaine,0.0691797563,84.95.170.69,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n5530,2,120,2017-04-17 19:51:21,http://wiza.io/alexys.gleason,0.0990591068,117.4.120.8,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n5531,2,120,2016-12-23 10:31:47,http://howell.info/natasha_moriette,0.9945563357,9.66.20.248,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n5532,2,120,2016-12-29 08:05:32,http://heller.co/sabryna,0.4112693477,127.86.91.254,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n5533,2,120,2017-04-20 22:07:31,http://damore.net/irma_hills,0.3377528199,73.250.194.246,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n5534,2,120,2017-06-12 22:13:57,http://ortizheller.org/krystel,0.6680755529,128.92.114.191,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n5535,2,120,2017-03-01 22:17:05,http://funk.org/ashley,0.3658564470,62.98.8.79,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n5536,2,120,2017-02-06 21:41:33,http://corkery.net/cameron_adams,0.4346272081,202.15.36.223,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n5537,2,120,2017-03-23 19:27:20,http://parker.biz/maximo_skiles,0.0585257071,148.52.90.148,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n5538,2,120,2017-01-11 15:45:57,http://larsonlemke.info/lacy.howell,0.4716159134,230.225.94.46,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n5539,2,120,2017-04-01 13:09:40,http://oreilly.co/adrian.crooks,0.8024919043,28.138.191.89,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n5540,2,120,2017-02-04 16:25:27,http://mcclure.co/yasmine_gislason,0.8510236303,195.205.181.225,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n5541,2,120,2016-12-15 07:06:45,http://jacobsondicki.info/favian_koelpin,0.9802510945,251.128.129.242,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n5542,2,120,2016-12-31 15:10:31,http://simonis.io/hulda,0.0965267763,179.233.158.59,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n5543,2,120,2017-05-13 07:18:01,http://bergnaum.info/sienna.skiles,0.6125258822,90.140.85.175,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n5544,2,120,2017-05-06 22:36:30,http://weinat.io/manley.bins,0.6336454640,227.5.235.248,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n5545,2,120,2017-04-11 16:59:22,http://bradtke.com/ariane.labadie,0.7586789409,247.234.68.105,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n5546,2,120,2017-06-01 23:19:10,http://nitzschepurdy.com/calista.kemmer,0.6687160733,84.47.57.135,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n5547,2,120,2017-02-25 12:28:10,http://schimmel.io/maymie,0.4996392183,53.186.208.164,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n5548,2,120,2017-06-07 11:02:42,http://ricekoelpin.co/gregg.ankunding,0.9425330303,141.91.142.204,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n5549,2,120,2017-01-31 11:34:17,http://kubhagenes.com/quinn.hintz,0.4755195735,41.184.169.213,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n5550,2,120,2017-03-29 01:03:23,http://satterfield.co/delbert_waelchi,0.4296005915,15.230.188.125,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n5551,2,120,2017-02-19 20:00:28,http://legrosturner.net/chris.becker,0.7659125345,106.167.182.11,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n18459,7,417,2017-02-15 16:53:43,http://block.biz/reagan,0.5184060773,98.24.26.207,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n18460,7,417,2017-03-31 03:46:38,http://prosacco.co/olaf,0.6202498380,110.168.146.32,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n18461,7,417,2017-01-16 02:26:49,http://grady.net/lauren,0.1622232166,232.219.181.95,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n18462,7,417,2016-12-31 12:33:49,http://witting.org/emmitt,0.1961430411,125.133.76.39,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n18463,7,417,2016-12-15 14:13:19,http://flatley.org/alphonso,0.0315891669,248.72.218.169,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n18464,7,417,2016-12-29 17:36:50,http://gerlach.io/alana.simonis,0.5022246738,241.83.208.248,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n18465,7,417,2017-05-04 15:25:34,http://gusikowskicasper.co/corrine_thiel,0.2995710804,78.220.191.253,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n18466,7,417,2017-04-08 01:24:49,http://schowalter.biz/easter_willms,0.1641752738,248.223.246.220,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n18467,7,417,2017-03-20 10:38:32,http://moore.co/elsie,0.7621142493,129.163.19.110,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n18468,7,417,2017-05-12 00:22:02,http://baileybrekke.co/evan,0.5937354992,126.68.217.31,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n18469,7,417,2017-05-13 10:21:33,http://bradtke.io/benton,0.7121752938,55.251.87.81,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n18470,7,417,2017-02-27 23:48:02,http://heidenreich.org/alia.kertzmann,0.4032712823,165.104.73.251,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n18471,7,417,2017-05-28 17:01:25,http://hauck.org/savion,0.4877386511,74.95.112.22,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n18472,7,417,2017-05-13 22:55:10,http://von.name/bettye,0.7981097576,44.12.197.197,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n18473,7,417,2016-12-22 05:38:09,http://ernser.org/gabriel.bruen,0.6743047493,154.14.144.144,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n18474,7,417,2017-01-02 01:17:43,http://nolan.biz/verda_bruen,0.5583197644,7.8.202.104,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n18475,7,417,2017-05-03 03:51:54,http://greenholtbechtelar.biz/ethan,0.2803176181,159.60.125.63,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n18476,7,417,2017-04-28 17:04:03,http://bogan.com/reed.kiehn,0.9087704416,177.25.10.229,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n18477,7,417,2017-03-04 00:40:39,http://cummings.com/keyshawn,0.3379930839,91.205.78.232,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n18478,7,417,2017-01-31 00:03:33,http://kunze.io/heber,0.3029487179,189.227.14.156,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n18479,7,417,2017-06-05 03:07:23,http://gottlieb.io/alison,0.5000185614,69.15.7.52,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n18480,7,417,2017-05-29 05:18:31,http://kohlerdavis.net/giles_roberts,0.1724714221,54.138.200.238,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n18481,7,417,2017-03-25 20:30:19,http://emmerich.biz/camylle,0.0601073222,142.91.82.146,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n18482,7,417,2017-01-19 01:37:57,http://langworth.info/earline,0.3569130300,77.242.33.236,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n18483,7,417,2017-05-11 00:40:53,http://botsford.org/patience_medhurst,0.6383825136,127.101.115.75,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n18484,7,417,2017-04-29 02:20:45,http://smith.info/thurman,0.9798645240,178.39.123.108,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n18485,7,417,2017-05-08 10:50:41,http://cormier.name/kurtis,0.4052787813,154.17.121.232,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n18486,7,417,2017-02-25 21:29:29,http://volkman.com/delilah_connelly,0.2877295627,6.34.18.2,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n18487,7,417,2017-04-03 04:33:01,http://jerde.net/bradford,0.5417145290,21.209.171.32,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n18488,7,417,2017-03-08 14:03:58,http://ernser.name/kenya,0.2937915294,226.120.217.167,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n18489,7,417,2016-12-20 22:31:12,http://wolff.io/casimir,0.7844972379,218.108.85.77,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n18490,7,417,2016-12-24 17:58:07,http://macejkovic.io/barbara,0.2897103268,2.32.148.250,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n18491,7,417,2017-05-12 03:05:07,http://rau.org/jakayla,0.0147850738,161.40.219.230,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n18492,7,417,2017-05-14 04:50:22,http://daughertyfadel.org/brook,0.7311137664,113.135.197.171,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n18493,7,417,2017-03-12 02:29:32,http://jacobsonjacobs.io/orland.becker,0.3723712307,84.25.130.229,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n18494,7,417,2017-06-05 15:55:39,http://west.co/jermain.vandervort,0.8167792060,117.153.222.17,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n18495,7,417,2017-02-18 06:00:13,http://hermiston.co/lula.hahn,0.8191423484,246.252.184.107,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n18496,7,417,2017-02-26 06:29:56,http://ohara.info/elia,0.6192668522,210.241.232.73,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n18497,7,417,2017-02-16 10:12:55,http://kuhlman.net/mortimer,0.8930207122,60.64.167.17,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n18498,7,417,2017-03-05 09:57:12,http://dickens.biz/reymundo,0.5814674391,210.109.116.113,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n18499,7,417,2017-02-12 14:24:48,http://nitzsche.co/colby.walter,0.8997184732,81.122.171.186,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n18500,7,417,2017-06-05 17:09:43,http://hirtheshanahan.info/wellington_grady,0.8214712118,242.53.149.193,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n18501,7,417,2017-03-12 07:22:39,http://lockman.com/gennaro,0.6654188502,7.184.212.237,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n18502,7,417,2017-02-18 15:06:09,http://croninoberbrunner.name/brooklyn,0.2674198411,49.243.15.219,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n18503,7,417,2017-01-08 21:39:06,http://goldner.biz/demarcus.hilpert,0.9454828466,68.102.75.155,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n18504,7,417,2017-02-01 23:08:13,http://jaskolskihirthe.io/bennett.aufderhar,0.8105860600,145.20.59.51,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n18505,7,417,2017-05-13 15:43:13,http://boyle.net/betty,0.5084118334,147.188.96.50,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n18506,7,417,2017-02-05 04:22:12,http://simonis.net/donna_thiel,0.3626108852,210.174.129.9,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n18507,7,417,2017-05-27 19:34:48,http://schuster.com/darron.watsica,0.4369563826,196.52.208.123,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n18508,7,417,2017-02-07 15:09:39,http://jastzulauf.com/eudora,0.1179031297,73.100.107.86,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n18509,7,417,2017-02-15 15:25:59,http://zboncak.info/augustus_bashirian,0.8364224235,126.81.231.98,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n5552,2,120,2017-02-06 18:28:07,http://baumbachdamore.biz/camila_paucek,0.2769631784,25.77.128.93,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n5553,2,120,2017-05-05 08:25:11,http://hackett.org/loy_schinner,0.2624665798,121.84.95.101,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n5554,2,120,2017-02-23 09:30:15,http://cremin.com/ulises,0.3104429988,3.87.26.249,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n5555,2,120,2017-02-25 07:55:16,http://lindgren.org/alfred_funk,0.5243660597,209.44.249.9,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n5556,2,120,2017-04-02 19:40:39,http://jerdeanderson.biz/reanna_graham,0.5872046756,96.160.41.45,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n5557,2,120,2017-04-14 22:03:11,http://krajcikbailey.org/letitia_mckenzie,0.8991767370,254.71.197.89,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n5558,2,121,2017-05-06 23:48:53,http://raynor.io/napoleon_mann,0.7174410153,170.178.98.161,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n5559,2,121,2016-12-29 18:31:00,http://koelpin.co/brenda,0.3291658923,200.62.131.209,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n5560,2,121,2017-01-26 21:28:38,http://oconnellondricka.org/arlo,0.3260029647,30.49.92.178,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n5561,2,121,2017-06-05 18:52:22,http://larson.info/taurean,0.8736773487,246.235.65.122,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n5562,2,121,2017-03-25 09:19:18,http://macejkovicrice.biz/nayeli.lemke,0.2503879138,55.177.125.168,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n5563,2,121,2017-04-01 23:12:27,http://nicolaswehner.net/mitchell,0.9501968867,84.177.177.106,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n5564,2,121,2017-06-10 13:41:24,http://effertz.co/joanne,0.2393192311,152.248.17.94,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n5565,2,121,2017-03-14 04:13:27,http://hermannmurray.io/fritz.shanahan,0.6420546242,236.22.122.238,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n5566,2,121,2017-02-22 20:48:11,http://schambergermurray.biz/hailey_jones,0.5501665785,78.217.238.80,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n5567,2,121,2016-12-31 21:48:41,http://bogisich.co/kaylin,0.3629404283,250.132.193.31,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n5568,2,121,2017-04-12 22:59:14,http://zieme.com/vicky.koepp,0.2712191309,11.88.191.69,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n5569,2,121,2017-03-03 14:44:54,http://reilly.org/erica.lesch,0.7141491912,223.97.115.252,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n5570,2,121,2017-02-08 01:31:52,http://brakus.name/naomi.waters,0.6222800761,194.212.193.197,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n5571,2,121,2016-12-15 04:27:25,http://rogahn.com/brennan,0.0157249185,162.87.86.215,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n5572,2,121,2017-03-10 17:35:58,http://little.co/myrl.johnston,0.7704758479,68.175.39.81,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n5573,2,121,2016-12-16 06:42:40,http://kingklocko.org/carley_rodriguez,0.7177251067,231.194.55.144,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n5574,2,121,2017-02-28 00:06:38,http://hauck.co/madyson,0.5809788234,141.162.66.99,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n5575,2,121,2017-03-02 07:42:59,http://gleason.com/eliseo,0.4808586162,91.30.228.249,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n5576,2,121,2017-02-17 03:13:41,http://gottlieb.org/don,0.5846346886,253.2.221.10,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n5577,2,121,2017-03-02 02:45:48,http://stehr.name/sophie.schaefer,0.3063645881,89.166.235.38,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n5578,2,121,2017-03-27 06:15:44,http://sauer.info/janice,0.5732882677,177.34.159.250,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n5579,2,121,2017-04-23 20:15:29,http://reilly.biz/zoila,0.8406832400,77.39.232.35,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n5580,2,121,2017-05-05 22:22:18,http://shields.net/jaden,0.2359884292,194.126.3.58,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n5581,2,121,2017-03-06 22:22:34,http://jakubowskistiedemann.co/orrin,0.4463581055,187.172.33.186,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n5582,2,121,2017-04-29 01:32:20,http://hagenes.co/gerry,0.5756031944,221.83.220.177,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n5583,2,121,2017-04-22 16:16:14,http://kshlerin.org/lizzie_sauer,0.8930634425,214.112.233.202,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n5584,2,121,2017-03-31 21:10:03,http://maggiodouglas.co/aleandra.johnston,0.3282436168,179.28.138.74,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n5585,2,121,2017-01-28 18:55:02,http://bradtke.co/kian_heel,0.8189887782,193.208.165.153,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n5586,2,121,2016-12-31 01:08:44,http://heller.co/tristian_schowalter,0.1290170533,78.16.243.21,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n5587,2,121,2017-03-14 22:15:52,http://ward.biz/marie,0.2171582042,244.194.125.12,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n5588,2,121,2016-12-26 07:55:17,http://schneiderbotsford.net/clement_labadie,0.5816226055,149.117.124.177,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n5589,2,121,2017-03-15 01:38:22,http://bergstrom.name/madie_mcclure,0.8410342241,76.23.148.129,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n5590,2,121,2017-03-05 09:18:00,http://zemlak.name/brycen_gusikowski,0.0750288517,20.226.94.68,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n5591,2,121,2017-01-11 21:31:54,http://rathstroman.com/dallas.grant,0.4044119428,42.149.206.196,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n5592,2,121,2017-02-19 23:12:47,http://weinat.io/marjorie,0.4101038468,61.102.161.123,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n5593,2,121,2017-05-28 22:07:11,http://wisozkbraun.io/laura.corkery,0.8177847166,192.247.134.249,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n5594,2,121,2017-02-21 03:14:17,http://huels.org/marjorie.rau,0.8572015392,120.43.144.164,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n5595,2,121,2017-03-27 15:46:41,http://spencer.biz/elton,0.5073776146,148.152.134.218,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n5596,2,122,2017-06-11 05:23:21,http://upton.biz/elbert,0.0525197416,97.27.152.3,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n5597,2,122,2017-01-25 03:52:14,http://schneider.name/timmothy_zemlak,0.6151952879,78.253.211.228,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n5598,2,122,2016-12-21 16:37:56,http://kovacekfadel.co/mac,0.8012405835,110.208.38.185,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n5599,2,122,2017-05-20 09:20:11,http://mayerspencer.org/ardith_pagac,0.0732647060,180.92.34.211,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n5600,2,122,2017-02-03 00:54:47,http://schaeferlabadie.com/enos,0.5889282112,9.153.147.254,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n5601,2,122,2017-02-16 04:13:36,http://brekke.net/alexandra_keeling,0.1781271260,114.117.86.72,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n5602,2,122,2017-04-03 06:26:09,http://weinat.net/wilson.huels,0.2621352054,207.219.102.3,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n18510,7,417,2017-04-14 18:14:33,http://parisianyundt.co/randall,0.8717181298,140.138.170.9,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n18511,7,417,2017-03-20 01:28:08,http://durgan.org/willard.bruen,0.4289423634,233.78.183.227,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n18512,7,418,2017-03-29 23:31:39,http://west.name/lukas.turner,0.0301787969,33.47.119.76,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n18513,7,418,2017-04-17 23:12:16,http://towne.io/sincere.beahan,0.6611728461,128.207.68.239,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n18514,7,418,2017-03-03 00:44:17,http://ziemezieme.com/otilia,0.4612432409,140.208.175.203,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n18515,7,418,2017-05-23 02:12:47,http://keelinggoyette.io/tyrel_dooley,0.2437858065,178.129.78.193,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n18516,7,418,2017-02-24 11:52:49,http://schmelerlind.name/christine,0.9400889493,153.43.217.115,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n18517,7,418,2017-03-27 23:05:35,http://abbottheaney.com/kiera,0.8430033825,147.206.93.5,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n18518,7,418,2017-05-31 08:28:21,http://mertz.com/jennyfer,0.7463442173,43.178.125.43,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n18519,7,418,2017-06-03 19:06:31,http://schimmel.com/augustus,0.7599732722,178.38.148.230,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n18520,7,418,2017-04-09 03:59:06,http://lynchkoch.com/armani,0.6067520875,84.140.135.72,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n18521,7,418,2017-02-14 10:50:39,http://langworthwilderman.info/london_ferry,0.3115539105,184.152.163.234,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n18522,7,418,2017-01-02 12:58:51,http://langosh.co/isidro,0.5014771035,118.123.44.62,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n18523,7,418,2017-02-19 18:04:47,http://uptonmckenzie.io/dayton_jacobson,0.5799118817,243.148.4.25,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n18524,7,418,2017-03-17 20:21:41,http://becker.biz/lauren_ward,0.0210379870,182.7.125.56,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n18525,7,418,2017-03-08 23:21:05,http://schaefer.biz/domenic_bins,0.4673874946,31.99.78.181,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n18526,7,418,2017-05-07 06:57:27,http://schustergutkowski.info/sam.bins,0.5499749960,56.120.68.183,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n18527,7,418,2017-03-26 16:44:51,http://bednarhauck.co/catharine_mann,0.5069481611,8.131.124.130,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": true}\"\n18528,7,418,2017-01-09 20:05:23,http://breitenbergsatterfield.info/kaley,0.7534040917,130.32.228.200,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n18529,7,418,2017-06-14 04:37:02,http://hamillfranecki.com/zula,0.2037844598,128.137.128.104,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n18530,7,418,2017-05-07 02:12:06,http://schusterhudson.co/lura_lang,0.1159288876,140.84.130.106,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n18531,7,418,2017-05-31 23:21:39,http://jerde.io/clovis,0.6758328080,127.72.78.170,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n18532,7,418,2016-12-15 07:07:30,http://graham.org/rod_tromp,0.0890454975,151.250.155.198,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n18533,7,418,2017-01-13 18:38:41,http://gislasondonnelly.com/mitchell,0.8325298254,159.208.49.113,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n18534,7,418,2016-12-17 03:03:00,http://kuvalis.org/tre,0.3284567331,100.157.37.249,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n18535,7,418,2017-01-17 06:10:01,http://reingerconn.co/jeffrey_hand,0.1293170239,166.181.58.139,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n18536,7,418,2016-12-16 09:28:56,http://predovicmarks.biz/maxwell,0.7122404881,105.157.245.238,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n18537,7,418,2017-02-28 07:22:20,http://leschturcotte.biz/eugene.flatley,0.0357598565,31.200.36.4,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n18538,7,418,2017-06-10 14:22:08,http://fadelwalter.biz/jeremy.kunze,0.6757649290,159.189.172.109,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n18539,7,418,2017-05-23 09:01:33,http://brakus.info/kevin,0.8788702178,127.232.254.55,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n18540,7,418,2017-05-01 17:51:05,http://larson.com/kaleigh,0.9617764927,133.12.174.71,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n18541,7,418,2017-01-01 07:46:47,http://mertztowne.net/hermina_mohr,0.6041216142,134.128.213.144,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n18542,7,418,2017-03-18 02:21:03,http://fay.net/martine_erdman,0.5020300660,162.143.9.173,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n18543,7,418,2017-01-31 15:41:26,http://weimann.com/evans.schmitt,0.2189270209,172.108.94.58,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n18544,7,418,2017-06-03 02:11:43,http://cronin.biz/ciara.hills,0.3581861300,172.65.59.218,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n18545,7,418,2017-04-11 17:56:56,http://mcdermottauer.com/beau.brekke,0.1489634535,197.221.9.214,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n18546,7,418,2017-06-13 13:39:28,http://stantonmarks.org/jeremy_bruen,0.0924847992,109.161.2.17,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n18547,7,418,2017-01-07 12:54:07,http://larson.co/waldo.wuckert,0.5097729165,106.62.178.221,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n18548,7,418,2017-02-21 12:04:38,http://brownlangosh.name/zane,0.7662681548,231.63.73.12,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n18549,7,418,2017-01-19 03:10:27,http://durgan.biz/jackie_white,0.4981614923,35.108.47.201,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n18550,7,418,2017-01-08 09:13:36,http://kris.org/kirk.macgyver,0.1461896194,236.59.109.164,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n18551,7,418,2017-04-01 14:13:06,http://denesikkoepp.net/brannon_beier,0.9989906343,72.20.52.59,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n18552,7,418,2017-04-05 23:46:30,http://lang.org/dock.funk,0.7172034649,155.229.116.157,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n18553,7,418,2017-04-23 19:41:13,http://hudsonraynor.biz/moshe,0.1221233251,202.111.100.32,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n18554,7,418,2017-05-24 15:49:42,http://nitzsche.io/trisha_gulgowski,0.2183577295,184.250.227.112,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n18555,7,418,2017-03-11 22:00:18,http://bergnaum.com/stephan,0.9008008435,235.169.61.194,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n18556,7,418,2017-01-14 20:49:19,http://oconnerkunde.name/alvah,0.5852602244,183.19.250.136,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n18557,7,418,2017-05-08 04:36:01,http://mertz.info/benny,0.6766673304,147.228.56.201,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n18558,7,418,2017-03-06 12:51:32,http://ohara.name/alexandrine_becker,0.1037649795,62.110.203.210,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n18559,7,418,2017-05-23 02:31:01,http://langworthwolf.io/caden,0.7307231736,228.61.37.179,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n18560,7,418,2017-03-12 20:41:54,http://dickens.co/makenzie.macgyver,0.3741991620,26.191.6.171,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n5603,2,122,2017-02-28 03:08:49,http://lehner.io/thalia_hamill,0.6552790678,169.106.242.209,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n5604,2,122,2017-03-03 05:33:57,http://stehrgerlach.co/mackenzie,0.8465299343,70.243.150.115,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n5605,2,122,2017-03-16 11:46:06,http://kautzer.io/casimir,0.3418915507,150.28.54.183,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n5606,2,122,2016-12-18 14:08:44,http://doylewiza.org/tobin,0.0689830195,4.196.238.40,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n5607,2,122,2017-01-10 06:53:08,http://eichmannprosacco.net/adonis,0.8222809510,41.237.87.138,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n5608,2,122,2017-05-14 07:23:30,http://boehm.info/roderick_vonrueden,0.1232292598,91.70.16.41,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n5609,2,122,2017-03-23 20:14:15,http://hane.com/adalberto_stokes,0.0511921362,54.102.28.51,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n5610,2,122,2017-06-12 04:28:04,http://will.name/lavern,0.2048638625,93.223.101.131,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n5611,2,122,2017-02-14 06:04:59,http://langondricka.name/lauren.ledner,0.3938950614,14.11.236.184,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n5612,2,122,2017-01-08 12:28:09,http://funk.org/jordy,0.5584174342,19.145.94.170,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n5613,2,122,2017-02-09 18:38:06,http://doyle.biz/kayden,0.3975905337,156.213.243.45,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n5614,2,122,2017-01-21 19:08:20,http://breitenberg.name/jaleel,0.2252362847,186.147.216.220,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n5615,2,122,2017-04-16 05:01:44,http://beattylangosh.co/arlo,0.2373610426,146.31.222.51,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n5616,2,122,2017-03-04 02:32:54,http://lockmanorn.net/monte,0.5203617269,113.11.182.230,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n5617,2,122,2017-04-15 13:44:57,http://langarmstrong.com/jee,0.6489333067,136.40.142.102,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n5618,2,123,2017-02-28 14:52:41,http://casper.net/isabell_koch,0.1977091970,178.23.82.102,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n5619,2,123,2017-04-30 18:58:54,http://hills.org/anya_champlin,0.6130047890,153.136.87.218,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n5620,2,123,2017-03-06 08:43:57,http://pouroscummings.name/harrison.streich,0.4953669465,105.130.216.217,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n5621,2,123,2017-04-20 17:10:50,http://klockoreilly.co/reanna_feest,0.6596823521,225.182.184.149,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n5622,2,123,2016-12-14 06:06:46,http://jaskolskilangworth.co/benjamin,0.7586395181,140.140.123.206,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n5623,2,123,2017-02-27 01:33:21,http://pollich.org/shaina_torphy,0.1806073164,71.171.150.205,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n5624,2,123,2017-01-29 16:41:03,http://daugherty.name/miguel,0.9640283984,105.75.128.238,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n5625,2,123,2017-05-25 06:59:46,http://bernhard.info/terrence,0.1376543723,208.236.101.188,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n5626,2,123,2017-01-15 00:41:00,http://stiedemann.io/zoila,0.5488238597,116.153.115.35,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n5627,2,123,2017-03-29 06:22:47,http://larkin.co/dale.kling,0.3315218729,96.17.82.203,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n5628,2,123,2017-01-31 23:42:54,http://kovacekmoen.info/jackie,0.7619061454,32.225.68.30,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n5629,2,123,2017-04-06 20:25:50,http://hettinger.io/lexus,0.2956006013,200.91.235.142,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n5630,2,123,2017-04-19 06:50:06,http://ratkedamore.biz/marlen,0.3363867294,109.114.227.56,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n5631,2,123,2017-05-19 11:16:43,http://willms.org/dusty_homenick,0.6639271765,100.45.205.56,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n5632,2,123,2016-12-25 00:29:30,http://collins.name/jana,0.1172922812,201.8.4.233,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n5633,2,123,2017-06-07 22:33:52,http://cremin.co/enoch.jones,0.4941839749,4.46.228.211,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n5634,2,123,2017-06-10 00:08:37,http://gaylord.com/sierra.will,0.2271125552,70.58.146.198,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n5635,2,123,2017-02-28 20:39:24,http://rodriguez.org/sheldon_lemke,0.4082438863,3.70.254.216,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n5636,2,123,2017-01-10 02:18:26,http://rath.biz/nikita_predovic,0.8353409176,63.38.22.15,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n5637,2,123,2017-03-13 18:19:02,http://lebsack.net/neil,0.0391403937,202.46.125.226,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n5638,2,123,2017-02-25 13:57:48,http://greenholt.co/lesley,0.8703039451,38.93.174.201,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n5639,2,123,2017-03-01 13:54:10,http://quigleyhackett.name/kenna,0.1733707779,34.12.11.33,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n5640,2,123,2017-03-27 11:59:02,http://brekke.biz/al,0.6509157802,112.209.233.178,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n5641,2,123,2017-01-14 15:29:28,http://bruen.co/dorcas.donnelly,0.0098901288,201.103.159.81,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n5642,2,123,2016-12-19 17:00:53,http://nolanveum.co/stuart_baumbach,0.1958748831,56.252.213.118,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n5643,2,123,2017-01-19 18:13:02,http://rosenbaumbraun.org/newell_nolan,0.6876655548,151.71.167.149,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n5644,2,123,2017-06-11 12:41:43,http://botsfordweber.name/micheal.rogahn,0.0833434086,227.21.155.118,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n5645,2,123,2017-03-15 04:45:27,http://weber.co/gladyce.lubowitz,0.6758070682,11.31.30.4,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n5646,2,123,2017-03-23 00:37:43,http://watsica.org/breanna_boyer,0.9707997681,119.148.207.70,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n5647,2,123,2017-05-12 20:27:07,http://paucek.info/alexis_johns,0.6935045532,158.81.50.197,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n5648,2,123,2017-03-06 00:46:20,http://mayer.biz/alverta,0.9500753404,194.232.136.118,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n5649,2,123,2017-03-03 19:59:20,http://barrows.com/amely,0.9095211672,80.99.171.141,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n5650,2,123,2017-03-07 00:31:19,http://pourosjerde.net/amina,0.5417570059,209.165.149.48,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n5651,2,123,2017-04-03 05:51:23,http://labadie.net/janis,0.8281787863,219.24.70.80,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n5652,2,123,2017-06-07 03:27:46,http://cartwrightebert.biz/rosemary_moriette,0.0407220224,184.177.238.72,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n5653,2,123,2017-05-04 15:43:01,http://beatty.co/dayton_grant,0.2032023397,4.103.29.244,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n5654,2,123,2017-03-25 20:50:52,http://brakus.io/jerod_huel,0.7082145468,8.190.11.40,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n18561,7,418,2017-02-20 06:03:48,http://skiles.info/yesenia_schamberger,0.6931588131,17.138.109.46,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n18562,7,418,2017-04-14 17:45:21,http://dibbertreynolds.co/marcelo.hermann,0.9263711703,137.224.245.254,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n18563,7,418,2016-12-23 09:36:40,http://robertsparisian.name/sofia,0.1651552198,167.208.52.34,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n18564,7,418,2017-01-17 14:53:00,http://townebode.org/olaf,0.7328211955,14.100.90.168,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n18565,7,418,2017-01-12 23:44:35,http://nienow.name/lincoln.konopelski,0.1526281651,203.211.122.222,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n18566,7,418,2017-01-12 10:52:05,http://ritchieklein.name/johnpaul,0.8370281320,168.114.176.154,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n18567,7,418,2017-03-28 05:09:38,http://jacobs.biz/brionna,0.2652550534,250.171.55.159,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n18568,7,418,2017-01-03 03:44:01,http://carrollpadberg.io/zion,0.0821872285,69.17.245.40,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n18569,7,418,2017-01-20 12:11:23,http://dietrich.name/eleanora_kutch,0.9704491274,251.75.228.72,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n18570,7,418,2017-01-04 00:11:36,http://walsh.org/dee,0.6817774294,135.128.79.32,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n18571,7,418,2017-05-24 02:57:18,http://abshire.name/elia,0.8786104802,184.39.204.100,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n18572,7,418,2017-03-16 23:19:55,http://waelchi.io/olin,0.8168462119,111.88.156.131,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n18573,7,418,2017-04-21 10:20:43,http://lesch.net/jada,0.8789025921,201.165.10.153,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n18574,7,418,2017-05-01 20:20:02,http://zulauf.io/toy_green,0.2230369480,115.144.107.157,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n18575,7,418,2017-01-14 09:32:27,http://moenfritsch.biz/rigoberto,0.0353390856,62.251.77.36,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n18576,7,418,2017-04-11 23:16:11,http://mayert.co/camille_casper,0.6474404396,126.227.44.26,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n18577,7,418,2017-02-19 20:00:21,http://raynor.org/gretchen.metz,0.6057090155,73.240.94.217,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n18578,7,418,2016-12-28 10:27:49,http://ullrich.biz/tyson,0.3270929404,60.63.146.196,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n18579,7,418,2017-03-19 00:41:17,http://marks.org/donna,0.2146488746,132.125.180.174,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n18580,7,418,2017-06-02 12:50:26,http://goyettejacobs.info/colin,0.6860281696,25.171.236.195,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n18581,7,419,2017-01-08 06:34:34,http://emard.info/brisa,0.6204600086,66.137.171.116,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n18582,7,419,2017-05-18 15:12:03,http://barrows.io/helmer,0.3408333938,4.214.196.253,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n18583,7,419,2017-05-12 01:20:35,http://braundamore.net/mustafa_streich,0.8533678981,206.207.170.177,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n18584,7,419,2017-04-14 20:14:40,http://ebert.name/michel.daniel,0.4590760856,231.33.202.103,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n18585,7,419,2017-01-05 07:37:11,http://coleschuppe.co/karli,0.1008267552,61.109.161.160,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n18586,7,419,2017-04-03 22:10:12,http://dubuque.net/irving,0.5712131003,66.145.105.206,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n18587,7,419,2017-05-30 13:32:09,http://wisoky.co/shanon,0.2317456837,142.170.94.73,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n18588,7,419,2017-04-17 03:02:39,http://beatty.org/penelope.dickinson,0.5194353192,22.121.79.82,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n18589,7,419,2017-03-22 06:03:51,http://osinski.io/alene,0.0843663061,200.141.173.171,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n18590,7,419,2017-03-20 06:37:42,http://carter.info/shaniya,0.7558983671,123.215.36.191,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n18591,7,419,2017-05-01 19:57:45,http://friesenbuckridge.biz/josephine_trantow,0.8530990932,47.135.148.8,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n18592,7,419,2017-05-19 06:58:35,http://brekke.name/ettie,0.2249988232,115.94.164.240,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n18593,7,419,2017-03-06 11:50:44,http://feestabbott.com/khalid,0.3810928544,164.16.55.55,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n18594,7,419,2017-05-02 10:03:24,http://reinger.com/minnie,0.8782992899,174.244.205.177,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n18595,7,419,2017-01-19 17:46:19,http://keler.org/eloisa.denesik,0.7055818251,139.118.236.124,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n18596,7,419,2017-01-22 11:11:18,http://cummeratamarquardt.name/everardo,0.2110117857,142.125.3.48,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n18597,7,419,2017-05-07 16:17:03,http://schinnerkertzmann.info/mara,0.1080676228,184.28.240.198,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n18598,7,419,2017-03-17 20:40:53,http://dare.co/dillan_beier,0.1686890602,147.244.68.214,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n18599,7,419,2017-05-19 11:38:02,http://wyman.com/trenton.kris,0.0757121430,76.220.184.231,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": true}\"\n18600,7,419,2017-06-08 11:37:32,http://lind.net/jamarcus,0.1369721214,4.174.179.220,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n18601,7,419,2017-02-27 14:11:05,http://wildermanhand.co/guillermo,0.0218152477,182.178.13.88,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n18602,7,419,2017-04-08 05:42:45,http://quigley.name/prudence,0.3216867933,170.77.245.240,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n18603,7,419,2017-05-03 14:21:18,http://parisianmclaughlin.io/terrell_lynch,0.4612640062,13.29.27.109,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n18604,7,419,2017-01-31 13:55:53,http://nikolaus.org/kenyatta,0.3960438848,234.245.190.191,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n18605,7,419,2017-01-13 08:29:12,http://spencerbahringer.net/nikita,0.7358828051,55.135.221.203,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n18606,7,419,2017-04-29 12:53:00,http://schowalter.net/jennie,0.7373861824,194.249.107.89,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n18607,7,419,2016-12-31 08:37:34,http://reichel.com/otha_cruickshank,0.8614764957,142.148.245.131,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n18608,7,419,2017-04-10 03:32:58,http://connweber.biz/leanne.douglas,0.7029770384,236.221.203.75,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n18609,7,419,2017-03-02 14:38:31,http://dickiromaguera.com/carmine,0.4631965600,116.22.118.15,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n18610,7,419,2017-04-14 19:11:33,http://walker.net/robyn,0.9720840630,243.84.141.151,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n18611,7,419,2017-06-07 01:46:14,http://trantow.biz/maxine,0.3445042321,32.106.67.204,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n5655,2,123,2017-04-06 13:18:03,http://koelpinkoepp.biz/florine,0.9335310549,241.55.65.251,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n5656,2,123,2017-04-11 11:01:19,http://wisozk.info/dimitri,0.3134116083,147.112.227.170,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n5657,2,123,2017-01-03 15:03:14,http://stracke.com/hettie.homenick,0.0857529851,115.200.63.80,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n5658,2,123,2017-04-17 23:12:02,http://nienow.co/garett_kuhic,0.8255140213,142.61.224.210,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n5659,2,123,2017-04-02 15:01:57,http://stanton.org/maria_kutch,0.7386932645,195.245.131.144,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n5660,2,123,2017-04-13 02:28:30,http://wardwisozk.info/jordy,0.0477991954,46.166.125.240,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n5661,2,123,2017-04-24 05:04:27,http://ankundingdach.biz/dayne_price,0.3981853780,193.132.233.14,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n5662,2,123,2017-02-13 07:47:47,http://mitchell.io/susana.turner,0.0533075858,52.226.14.230,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n5663,2,123,2017-02-11 03:12:50,http://becker.org/mittie,0.2915007985,206.186.210.231,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n5664,2,124,2017-05-24 11:37:31,http://thiel.info/keely.mohr,0.8405438866,166.165.59.167,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n5665,2,124,2017-02-20 07:36:08,http://hodkiewicz.org/josiah,0.2806561176,70.164.141.201,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n5666,2,124,2016-12-31 08:32:48,http://batzharris.net/emma,0.7316693190,239.33.243.139,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n5667,2,124,2017-01-06 13:10:02,http://watsicabaumbach.biz/yasmin,0.9629460603,36.70.50.154,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n5668,2,124,2017-06-04 22:43:36,http://mcdermottschmeler.biz/briana,0.4395656165,227.74.211.72,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n5669,2,124,2017-04-03 15:15:33,http://beahan.info/ryder,0.7247823506,188.16.231.232,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n5670,2,124,2017-05-17 03:17:02,http://cristkuphal.co/cloyd,0.1586950389,25.98.49.80,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n5671,2,124,2017-04-11 21:38:16,http://walterlindgren.co/jorge_lowe,0.9868320089,53.118.162.128,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n5672,2,124,2017-03-03 02:06:02,http://kihn.name/tobin,0.5741109510,36.171.118.111,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n5673,2,124,2017-05-20 17:23:45,http://will.net/stacey_christiansen,0.9258507477,72.39.87.99,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n5674,2,124,2017-05-19 09:17:32,http://kaulke.co/aleandra.schoen,0.8014101535,192.185.69.234,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n5675,2,124,2017-02-13 09:41:05,http://wiegand.org/libbie.borer,0.4388111177,197.40.226.228,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n5676,2,124,2017-06-08 19:05:22,http://veum.biz/heber,0.2923166925,124.135.92.96,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n5677,2,124,2017-02-17 00:39:27,http://bartell.io/hortense.powlowski,0.1484269955,167.179.221.208,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n5678,2,124,2017-01-02 04:03:21,http://kihn.co/alberto_schamberger,0.8967549967,102.69.128.127,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n5679,2,124,2017-04-20 18:12:45,http://heller.biz/aditya.kaulke,0.2824785719,198.172.101.224,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n5680,2,124,2017-02-27 22:03:06,http://ebert.org/charlie,0.8589684329,100.210.219.233,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n5681,2,124,2017-05-30 09:02:47,http://kuhlman.net/sandrine,0.3366069807,184.3.202.217,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n5682,2,124,2017-03-11 21:13:50,http://beahan.name/providenci.beatty,0.2907061814,88.239.142.245,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n5683,2,124,2016-12-25 20:50:08,http://collins.name/quinn,0.0107070467,203.98.228.115,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n5684,2,124,2016-12-19 00:28:54,http://haag.org/gust,0.2249859458,21.163.116.132,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n5685,2,124,2017-01-24 11:44:47,http://heller.org/niko,0.4411218813,194.247.150.3,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n5686,2,124,2017-05-20 23:23:54,http://jerde.info/boyd.lowe,0.7871691249,242.50.45.72,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n5687,2,124,2017-01-11 15:12:00,http://robelmarks.io/jermain,0.7969071278,30.144.62.248,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n5688,2,124,2017-05-19 11:35:11,http://smitham.io/lee,0.7661389335,2.177.6.132,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n5689,2,124,2017-05-18 13:40:35,http://dietrich.info/lela,0.3077776691,168.47.47.6,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n5690,2,124,2017-01-04 00:41:48,http://spinka.name/jettie,0.5417384372,234.125.39.174,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n5691,2,124,2017-05-10 10:28:46,http://hahn.net/dominic_hickle,0.4454823206,162.163.247.122,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n5692,2,124,2017-02-11 02:26:09,http://runolfsdottirgrant.net/rosina_jerde,0.7561602164,166.9.119.207,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n5693,2,124,2017-05-27 13:08:18,http://labadie.net/carley.balistreri,0.0160168194,252.98.229.152,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n5694,2,124,2017-06-07 21:31:31,http://bernier.org/lavern,0.1340360039,66.49.38.129,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n5695,2,124,2017-03-03 09:25:40,http://kelerleuschke.info/jaylan,0.6176727127,30.161.118.251,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n5696,2,125,2017-02-08 16:10:11,http://walshpadberg.com/abbie,0.3464973249,203.14.161.239,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n5697,2,125,2016-12-20 08:42:53,http://williamson.biz/abe,0.0347840189,40.169.250.129,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n5698,2,125,2017-03-02 09:58:12,http://wisozkfeeney.io/elena,0.4882208024,34.101.113.246,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n5699,2,125,2017-05-08 12:34:55,http://kovacek.info/susie_veum,0.4791404052,142.187.227.9,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n5700,2,125,2017-05-27 22:18:26,http://schowalterhuels.biz/tremaine.green,0.8983678558,183.125.36.71,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n5701,2,125,2017-02-19 09:10:40,http://jacobs.biz/lurline_stark,0.9132341113,10.11.165.225,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n5702,2,125,2017-04-09 08:50:09,http://hagenesjaskolski.name/rose_armstrong,0.1122665714,91.245.190.194,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n5703,2,125,2017-03-13 21:01:55,http://barrowskrajcik.com/giovanny,0.3671157882,164.133.194.226,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n5704,2,125,2017-03-05 22:48:31,http://schiller.io/abelardo_gaylord,0.4720550590,238.147.105.175,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n5705,2,125,2017-02-02 02:20:29,http://howelloconner.net/kristoffer,0.4810909228,12.115.239.150,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n5706,2,125,2017-01-27 18:21:49,http://fisher.info/sigrid,0.4759769567,12.203.245.145,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n18612,7,419,2017-04-24 07:59:54,http://marks.com/reynold_wisozk,0.1937712935,194.115.219.73,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n18613,7,419,2017-06-05 01:52:34,http://robel.net/rhoda,0.6999549475,140.88.114.93,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n18614,7,419,2016-12-28 12:47:48,http://kochbergnaum.com/nikita,0.5266354258,131.197.118.227,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n18615,7,419,2017-01-29 07:25:43,http://labadiewelch.com/onie,0.5033666487,194.40.74.91,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n18616,7,419,2017-01-15 09:37:37,http://mckenzie.io/marcelino,0.1925604771,139.165.242.169,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n18617,7,419,2017-04-24 12:14:22,http://erdmantrantow.io/kenya,0.7701233203,40.133.250.239,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n18618,7,419,2017-05-26 19:21:54,http://kundecarroll.co/michel_hand,0.2709972438,139.114.44.157,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n18619,7,419,2017-01-08 07:41:23,http://bahringer.org/cale_koch,0.9519592116,19.36.232.96,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n18620,7,419,2017-01-04 18:00:00,http://olson.co/mervin.spinka,0.2472054413,22.62.201.64,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n18621,7,419,2016-12-16 07:18:45,http://feeney.biz/lorenzo,0.5655499789,143.110.22.213,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n18622,7,419,2017-03-04 17:09:40,http://schmittondricka.info/harmon.pfeffer,0.9022058989,146.39.167.37,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n18623,7,419,2017-01-22 11:29:04,http://lynch.co/dylan,0.4912149774,178.107.247.90,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n18624,7,419,2017-05-21 17:50:22,http://zieme.com/ashleigh.shanahan,0.2076619599,29.5.197.115,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n18625,7,419,2017-05-19 16:52:00,http://hansen.info/chance,0.0194977910,183.108.66.224,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n18626,7,419,2016-12-29 05:35:10,http://reichelhamill.net/lauren.bauch,0.8698815196,234.100.10.177,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n18627,7,419,2017-01-29 13:16:50,http://barrows.io/alex,0.8551501617,109.57.135.62,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n18628,7,419,2017-01-02 01:19:37,http://simonis.biz/octavia_oreilly,0.7091323866,45.47.91.79,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n18629,7,419,2016-12-31 10:17:38,http://gleichner.org/faustino_schuster,0.5540109884,108.140.79.6,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n18630,7,419,2017-05-10 03:53:43,http://homenick.net/roie.raynor,0.5402966620,218.33.181.135,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n18631,7,419,2017-03-03 05:18:03,http://deckow.info/solon,0.4230222890,33.48.86.19,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n18632,7,419,2016-12-27 08:55:59,http://harberbeahan.org/garrett,0.1775928646,131.61.194.241,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n18633,7,419,2017-05-09 01:43:29,http://rice.org/wilmer.gislason,0.7779141485,62.217.226.81,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n18634,7,419,2016-12-17 02:50:58,http://herzog.name/benedict_williamson,0.8140199007,121.24.117.119,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n18635,7,419,2017-03-14 15:18:29,http://lebsack.org/candido,0.8799269078,11.2.198.252,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n18636,7,419,2017-05-31 10:27:43,http://hodkiewiczgottlieb.org/sabina,0.0679025015,147.8.122.87,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n18637,7,419,2017-05-10 16:15:19,http://brakus.name/shania.hills,0.6189204345,122.225.77.116,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n18638,7,419,2017-05-05 23:09:48,http://larsonhaag.name/hermina.turcotte,0.8758381364,27.32.254.131,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n18639,7,419,2017-01-11 05:03:37,http://beierglover.name/mohammed,0.3327202858,228.20.180.98,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n18640,7,419,2016-12-30 18:24:51,http://mohr.co/kasey,0.5436893746,16.24.208.2,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n18641,7,420,2017-06-08 20:08:43,http://orn.co/eddie,0.1839117382,146.162.93.66,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n18642,7,420,2017-04-04 17:40:39,http://jacobson.name/neha.wyman,0.3665096985,173.77.116.140,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n18643,7,420,2017-03-09 22:44:47,http://cruickshank.co/louie_streich,0.0182442208,59.23.88.31,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n18644,7,420,2017-03-08 02:52:12,http://tillman.org/chris,0.0748292146,181.156.34.107,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n18645,7,420,2017-05-09 05:57:03,http://ratke.net/luna.hoppe,0.8119468535,62.133.168.180,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n18646,7,420,2017-05-13 21:41:53,http://littlecrona.net/magali.flatley,0.2580786977,98.250.244.101,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n18647,7,420,2017-04-17 15:48:00,http://goodwin.net/abbie_green,0.9732770747,243.92.171.104,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n18648,7,420,2017-01-23 05:12:04,http://osinskihaag.name/arne.reichert,0.0850321967,47.42.172.243,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n18649,7,420,2017-01-19 19:42:02,http://dickinson.io/jerel,0.0258945725,19.77.54.156,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n18650,7,420,2017-04-27 14:21:01,http://beer.io/arlo,0.6497238361,245.20.76.169,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n18651,7,420,2017-01-05 06:33:36,http://zieme.co/cooper_hirthe,0.9145242025,145.106.154.177,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n18652,7,420,2017-03-29 02:25:10,http://leffler.name/giovanna.walsh,0.9348739745,75.127.167.100,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n18653,7,420,2017-04-29 21:54:18,http://kubhauck.biz/jerrold.konopelski,0.5682223392,21.163.34.61,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n18654,7,420,2017-01-14 07:03:25,http://mclaughlin.io/aleen_hammes,0.0955544667,72.76.224.80,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n18655,7,420,2017-06-06 07:53:36,http://price.io/gwendolyn,0.5485752464,209.225.228.21,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n18656,7,420,2017-01-05 20:11:13,http://swaniawski.biz/abbigail.franecki,0.6502061795,197.5.227.251,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n18657,7,420,2016-12-15 15:31:40,http://beer.io/enrique.kohler,0.6166477783,26.55.148.60,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n18658,7,420,2017-02-06 17:00:25,http://schumm.net/karlee.moore,0.0209405660,93.108.155.63,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n18659,7,420,2017-01-06 10:23:24,http://oconnell.info/dustin,0.3276830708,202.52.228.105,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n18660,7,420,2017-02-04 12:16:23,http://king.com/cade_harris,0.7454817077,110.142.7.85,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n18661,7,420,2017-01-22 21:40:23,http://mcclure.net/darron.brekke,0.9804861014,156.213.11.10,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n18662,7,420,2017-03-18 18:40:24,http://gutkowski.biz/braulio.gerlach,0.6462051146,15.7.236.55,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n18663,7,420,2016-12-22 13:03:28,http://gerhold.co/darlene.davis,0.1018772520,48.86.113.7,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n5707,2,125,2017-04-25 06:39:56,http://graham.info/mattie.grady,0.6879303366,198.153.18.58,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n5708,2,125,2017-03-03 13:50:12,http://koepp.biz/trenton,0.4858386925,43.185.44.75,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n5709,2,125,2017-06-03 19:53:05,http://harveyvonrueden.org/spencer,0.9245473294,204.254.207.2,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n5710,2,125,2016-12-26 09:25:46,http://altenwerth.co/millie,0.9872360031,107.253.227.230,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n5711,2,125,2017-03-15 16:55:19,http://price.info/abbigail_bogan,0.6538980573,66.167.90.50,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n5712,2,125,2016-12-21 20:06:34,http://roob.info/arnaldo,0.5865488798,31.14.152.202,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n5713,2,125,2017-04-02 00:41:36,http://spinka.com/jacinto,0.8309431320,193.203.249.115,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n5714,2,125,2017-04-24 06:19:57,http://hahn.com/hellen_barrows,0.7483152285,49.95.132.75,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n5715,2,125,2017-02-17 22:14:50,http://reichertkulas.co/vincenza,0.5028912570,167.174.23.231,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n5716,2,125,2017-04-08 08:28:12,http://abshire.biz/reyes.eichmann,0.5763745445,29.228.229.96,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n5717,2,125,2017-04-11 08:45:05,http://ohara.io/annabel.hoeger,0.8079369172,12.52.177.103,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n5718,2,125,2017-05-13 02:29:05,http://bashirian.biz/sylvia_carroll,0.0889986954,18.115.126.112,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n5719,2,125,2017-04-27 11:39:49,http://stehr.name/lura.bailey,0.2049648940,34.95.173.197,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n5720,2,125,2017-03-12 20:06:57,http://toy.io/robert_west,0.1019208932,237.8.83.39,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n5721,2,125,2017-05-21 05:50:28,http://pacocha.net/ryann,0.9893897625,176.184.145.142,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n5722,2,125,2016-12-15 21:56:42,http://schulistledner.io/adolph,0.6652935204,153.129.63.219,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n5723,2,125,2017-05-04 13:00:57,http://davisgutmann.net/bryce,0.0467733859,63.176.55.237,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n5724,2,125,2017-01-06 22:22:47,http://emmerich.com/alec.gleichner,0.5097088406,139.20.184.101,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n5725,2,125,2017-03-21 14:49:36,http://skiles.info/kiarra_hyatt,0.2623198137,52.5.7.243,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n5726,2,125,2017-01-03 11:07:36,http://yundt.net/yazmin,0.4110089486,95.36.164.28,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n5727,2,125,2017-01-20 11:08:33,http://watsica.net/cindy.kuhlman,0.1563485564,160.253.61.116,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n5728,2,125,2017-03-08 04:10:59,http://langosh.com/dominique.pfeffer,0.3951057650,78.230.84.198,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n5729,2,125,2017-05-30 18:54:51,http://rodriguez.biz/nasir_rolfson,0.3308764539,167.20.99.55,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n5730,2,125,2016-12-25 01:58:47,http://miller.io/jacinthe,0.2993533731,188.90.157.241,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n5731,2,125,2017-01-14 16:03:08,http://pagac.org/edyth.block,0.5347196123,9.50.188.35,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n5732,2,125,2017-05-04 22:33:14,http://kerluke.info/nelda,0.2076213924,137.89.167.71,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n5733,2,125,2017-02-17 13:55:55,http://kuhn.co/kevon_johnston,0.3148997644,20.3.206.234,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n5734,2,125,2017-04-27 13:36:33,http://grimes.info/lenna_moore,0.3917430880,151.206.16.57,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n5735,2,125,2017-04-06 08:29:58,http://denesik.co/general,0.5451744971,159.67.71.59,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n5736,2,125,2016-12-16 08:35:25,http://christiansen.name/aidan.mckenzie,0.0080310611,30.40.182.13,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n5737,2,125,2017-06-07 13:47:08,http://nolan.net/laney,0.3537142880,64.187.190.68,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n5738,2,125,2017-03-01 02:31:29,http://ankunding.co/noemi,0.3315876561,58.162.43.14,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n5739,2,125,2016-12-31 21:43:00,http://wolfgislason.net/alvis.hickle,0.4075203074,195.57.25.65,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n5740,2,125,2017-05-15 06:25:15,http://rohan.com/leonard_dickens,0.8373116051,202.6.145.102,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n5741,2,125,2017-01-28 11:11:20,http://mckenzie.org/marcella.sporer,0.9079151795,156.83.53.242,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n5742,2,125,2017-05-26 13:28:54,http://jacobi.net/haylie,0.7748926883,52.24.70.42,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n5743,2,125,2017-04-06 02:34:54,http://cain.com/vicente_bosco,0.1402625990,15.33.113.49,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n5744,2,125,2017-04-23 14:07:34,http://hackett.co/zachery,0.6858901046,212.9.109.59,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n5745,2,125,2017-03-29 10:00:10,http://frami.biz/luther_feeney,0.7946323803,189.197.201.142,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n5746,2,125,2016-12-26 04:27:12,http://whiterau.net/willie,0.9639645596,205.204.128.22,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n5747,2,125,2017-03-22 06:23:45,http://pollich.io/gielle.larkin,0.8030997661,15.134.19.59,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n5748,2,125,2017-03-04 18:02:30,http://cormier.co/daphnee,0.0128740019,223.202.10.75,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n5749,2,125,2017-05-10 08:02:46,http://spencergrady.co/adelle,0.4460829373,75.33.41.253,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n5750,2,125,2017-04-18 17:48:20,http://hettingerprice.com/desiree_kilback,0.1001380591,243.36.191.72,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n5751,2,125,2017-03-03 17:19:07,http://breitenberg.net/neil.ko,0.0408304577,48.28.69.142,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n5752,2,125,2017-05-22 10:03:27,http://brekke.info/edwardo,0.1523261269,251.31.125.33,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n5753,2,125,2017-02-11 03:39:27,http://jerde.info/fabiola.olson,0.3222941682,13.204.94.211,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n5754,2,125,2017-02-23 10:08:38,http://bruen.biz/stone.breitenberg,0.8404515410,213.128.94.28,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n5755,2,125,2016-12-15 16:14:34,http://borerrogahn.name/titus,0.7284782756,102.9.170.186,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n5756,2,125,2017-01-11 20:46:08,http://schuster.info/jasmin.heel,0.3039356598,119.183.116.228,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n5757,2,125,2017-01-19 02:51:07,http://ward.biz/alana,0.1391968998,141.162.10.126,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n5758,2,125,2017-05-01 04:57:41,http://blocknolan.net/sincere_grant,0.2472235401,243.67.151.103,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n18664,7,420,2016-12-26 09:29:17,http://johnsondickinson.name/alexander,0.8331171230,11.180.7.52,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n18665,7,420,2017-05-15 11:59:53,http://okeefe.biz/laverne.schumm,0.9207832080,2.197.146.168,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n18666,7,420,2017-04-02 23:29:25,http://brekke.com/hal_schroeder,0.4419138618,193.42.193.71,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n18667,7,420,2017-02-04 05:54:43,http://leuschke.name/hollie,0.5264180643,151.56.100.231,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n18668,7,420,2017-01-22 00:52:38,http://barton.name/brook.anderson,0.5024227634,140.100.151.25,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n18669,7,420,2017-05-02 09:11:52,http://lebsack.biz/zoila,0.0760032177,195.214.142.35,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n18670,7,420,2017-03-09 04:55:02,http://gusikowski.co/mikayla.hoeger,0.3965342115,45.136.38.157,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n18671,7,420,2016-12-14 09:25:59,http://dubuquezulauf.name/candida_kuhlman,0.2377473345,56.238.192.70,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n18672,7,420,2017-01-22 08:46:42,http://kihn.biz/bethel_batz,0.2914282227,9.87.12.137,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n18673,7,421,2017-01-29 09:24:27,http://champlin.co/brandy_lowe,0.6408389231,68.103.244.231,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n18674,7,421,2017-04-07 16:22:52,http://berge.biz/angie,0.2550252876,88.178.227.128,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n18675,7,421,2017-01-13 07:22:20,http://oberbrunner.info/johnpaul,0.3654884191,236.6.102.62,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n18676,7,421,2017-02-10 06:22:51,http://okeefe.com/noemie_ritchie,0.9894539430,232.2.228.102,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n18677,7,421,2017-05-25 20:15:56,http://greenholtwest.io/daryl,0.7433163908,144.51.135.213,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n18678,7,421,2017-01-11 06:47:49,http://jacobi.name/gordon,0.2747754596,228.122.111.103,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n18679,7,421,2017-03-07 20:43:49,http://gutmann.name/jakob.shanahan,0.1250963092,35.170.132.203,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n18680,7,421,2017-04-07 12:29:54,http://schmidtmarquardt.info/gerson_cronin,0.2110727370,172.96.140.110,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n18681,7,421,2017-04-29 05:22:50,http://kovacek.io/janea_gorczany,0.7240982690,215.168.43.156,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n18682,7,421,2016-12-21 10:48:30,http://schiller.io/kayley,0.9419713241,21.229.136.219,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n18683,7,421,2017-03-21 17:29:29,http://powlowski.com/teagan_dicki,0.3827397944,174.34.77.46,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n18684,7,421,2017-06-09 14:18:21,http://beahangrant.info/carlotta,0.9531253724,227.47.61.232,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n18685,7,421,2017-06-02 17:39:14,http://sauerhoeger.com/patsy_marvin,0.3277269441,58.81.168.216,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n18686,7,421,2017-01-16 15:39:32,http://green.net/catharine,0.3152517760,47.59.253.22,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n18687,7,421,2017-04-07 06:51:30,http://mclaughlinkuhlman.com/milford_smith,0.9359290859,195.170.230.174,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n18688,7,421,2017-05-13 06:45:44,http://okon.biz/christelle,0.2306708552,23.251.145.60,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n18689,7,421,2017-01-26 17:25:42,http://schimmelmckenzie.org/noemie.grimes,0.7676126698,164.40.106.98,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n18690,7,421,2016-12-15 01:43:50,http://osinski.biz/berry,0.4528190301,176.79.172.169,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n18691,7,421,2017-04-25 19:43:56,http://pfannerstill.name/newton.jast,0.3896514015,55.219.224.232,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n18692,7,421,2017-01-28 09:08:31,http://balistreri.co/tierra.kris,0.9875079054,6.156.212.210,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n18693,7,421,2017-03-26 18:17:24,http://croninbahringer.com/ladarius,0.2036684588,65.29.151.154,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n18694,7,421,2017-04-07 09:23:28,http://dach.net/oral.nikolaus,0.8034129725,240.71.193.226,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n18695,7,421,2017-02-09 18:37:11,http://reichert.co/carroll.ferry,0.9772909142,233.113.146.61,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n18696,7,421,2017-04-07 09:11:52,http://nicolas.name/charity,0.3280880635,162.144.180.36,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n18697,7,421,2017-01-23 17:42:51,http://ernser.org/efren.cain,0.5422840943,11.4.5.142,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n18698,7,421,2017-05-07 00:53:25,http://runolfon.name/vito,0.6668328485,53.71.139.212,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n18699,7,421,2017-05-25 11:20:01,http://brekke.biz/kim.bartoletti,0.9669548086,80.35.129.84,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n18700,7,421,2017-04-16 06:49:48,http://emmerich.biz/greyson,0.9390479754,123.125.218.229,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n18701,7,421,2017-02-12 15:10:36,http://friesenprohaska.com/sydnee_gerhold,0.8091859141,190.66.209.16,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n18702,7,421,2017-01-03 15:15:16,http://mayer.biz/mariano.boyer,0.0313623118,183.153.195.153,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n18703,7,421,2017-03-02 23:18:17,http://bode.com/armando,0.7107091005,39.83.151.245,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n18704,7,421,2017-03-12 08:27:35,http://pagaclindgren.name/rylan,0.2219702868,151.58.15.177,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n18705,7,421,2017-02-20 18:33:09,http://larkinklein.org/simone,0.9827321966,117.170.165.193,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n18706,7,421,2017-03-07 12:34:51,http://kuhlman.io/foster_lebsack,0.4975826006,50.138.15.215,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n18707,7,421,2017-02-09 19:48:18,http://fritschhomenick.co/jackson,0.5224766200,90.164.210.112,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n18708,7,421,2016-12-31 14:43:49,http://roobcruickshank.co/willis,0.0144242891,111.215.62.248,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n18709,7,421,2017-05-13 13:00:42,http://crist.io/emerson,0.7403719257,222.219.217.86,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n18710,7,421,2017-01-01 04:07:16,http://hammes.org/raven,0.3636948243,42.47.195.124,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n18711,7,421,2017-01-20 20:07:31,http://zemlakkreiger.co/rodrigo_kirlin,0.5886571700,129.100.206.72,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n18712,7,421,2017-01-31 22:54:17,http://greenfelderskiles.co/dannie,0.5840463277,248.158.85.77,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n18713,7,421,2017-03-24 07:35:32,http://casper.org/marquis_pfeffer,0.2572122852,47.127.59.219,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n18714,7,421,2016-12-30 10:31:37,http://dickens.co/veronica,0.4382266900,18.128.148.68,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n5759,2,125,2017-06-07 16:14:45,http://osinskirutherford.org/mertie,0.7268262465,114.171.122.212,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n5760,2,125,2017-04-06 04:24:46,http://funkkeeling.biz/travis.lockman,0.5203302906,34.104.222.240,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n18715,7,421,2017-04-03 17:22:05,http://erdman.net/eileen,0.6104548598,88.74.171.226,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n18716,7,421,2017-02-23 10:30:42,http://johnson.co/austyn.hartmann,0.1691366230,176.74.8.235,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n18717,7,421,2016-12-17 01:46:57,http://mcglynn.co/santina_gutmann,0.2092302928,92.224.136.125,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n18718,7,421,2017-01-14 12:38:44,http://nikolaus.io/ewell_ruecker,0.5333841450,177.144.39.193,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n18719,7,421,2017-03-02 04:48:27,http://danielbeier.net/kellie,0.1207128197,80.115.161.184,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n18720,7,421,2017-02-04 20:53:31,http://gaylord.name/santos_altenwerth,0.4112577677,93.210.121.30,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n18721,7,421,2016-12-31 15:14:05,http://gorczany.net/esmeralda,0.4427082397,230.56.197.43,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n18722,7,421,2017-03-19 16:29:14,http://koepphyatt.info/torrance,0.1707628121,77.81.163.17,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n18723,7,421,2016-12-26 02:59:35,http://mccullough.name/francesco,0.2646414391,233.23.234.17,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n18724,7,421,2016-12-16 19:33:15,http://hauck.name/justice_connelly,0.9098955065,196.197.93.167,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n18725,7,421,2017-01-31 17:54:32,http://abbottoconner.io/frankie_schiller,0.7195487788,224.230.49.206,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n18726,7,421,2016-12-24 07:07:20,http://westschimmel.co/quinton,0.0428506336,210.132.72.4,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n18727,7,421,2017-05-02 01:50:16,http://toyyundt.org/treie.herman,0.3942108595,45.210.173.229,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n18728,7,421,2017-06-12 10:42:47,http://brown.io/ru.heidenreich,0.2424074613,253.214.66.135,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n18729,7,421,2017-02-19 19:37:40,http://fay.info/sebastian_hickle,0.1373344996,175.39.163.127,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n18730,7,421,2017-05-12 08:51:32,http://kohler.name/liam,0.4251598376,214.7.68.7,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n18731,7,422,2017-01-12 14:49:12,http://jacobson.biz/torrance,0.1397712337,89.157.29.62,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n18732,7,422,2017-03-18 17:52:54,http://kris.com/king,0.8178504621,41.108.4.174,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n18733,7,422,2017-03-29 04:07:54,http://borer.name/kianna_hegmann,0.6865084450,231.145.230.19,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n18734,7,422,2017-02-14 18:02:16,http://stanton.net/filomena.gislason,0.0349397868,167.217.161.41,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n18735,7,422,2017-03-15 08:01:17,http://mcculloughhamill.co/janae.hodkiewicz,0.9646312950,138.72.112.33,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n18736,7,422,2017-05-21 17:20:29,http://kiehn.org/krystina,0.1685891472,124.228.244.139,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n18737,7,422,2017-02-16 21:12:44,http://bauchkaulke.org/bartholome,0.5844397715,135.230.83.156,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n18738,7,422,2017-06-08 08:19:48,http://homenick.co/alexandro,0.6654047646,243.192.24.232,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n18739,7,422,2017-04-20 09:47:20,http://mckenzie.net/ebony,0.2690423219,22.13.29.149,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n18740,7,422,2017-02-22 05:45:11,http://towne.info/kristian_nolan,0.9261665420,48.12.25.71,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n18741,7,422,2016-12-21 12:09:47,http://kuvalis.org/jimmy.braun,0.8604778607,96.63.222.155,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n18742,7,422,2017-01-16 17:17:44,http://runolfonbeier.org/charles.crist,0.7634561829,224.220.122.194,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n18743,7,422,2017-03-16 07:24:56,http://cormier.net/jey.west,0.7159501845,205.240.167.158,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n18744,7,422,2017-04-12 09:32:58,http://deckowcartwright.info/lea.murphy,0.6872713548,245.189.137.6,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n18745,7,422,2016-12-26 21:01:48,http://jast.biz/carolyn.heller,0.5557902267,107.227.225.196,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n18746,7,422,2016-12-21 13:01:39,http://halvorsonbrakus.com/loraine_klocko,0.0850726953,61.110.200.130,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n18747,7,422,2016-12-31 00:23:16,http://pourochulist.org/chadd_hodkiewicz,0.9410907621,156.31.154.227,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n18748,7,422,2017-02-19 02:25:41,http://ohara.co/sid_ledner,0.6151563942,249.92.31.210,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n18749,7,422,2017-04-14 05:52:00,http://hellerlubowitz.io/elliot,0.6807388582,111.241.193.238,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n18750,7,422,2017-05-13 01:47:05,http://mcglynn.net/esmeralda_dubuque,0.1672716102,175.131.173.136,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n18751,7,422,2017-05-06 03:46:15,http://wilkinson.io/keshawn,0.1396268983,168.85.166.204,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n18752,7,422,2017-06-11 23:35:41,http://kutchhickle.io/brady_bergnaum,0.6924088078,29.108.163.134,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n18753,7,422,2017-01-11 14:36:50,http://bayerlueilwitz.info/mikel.mayert,0.5613044650,229.232.249.149,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n18754,7,422,2017-02-12 04:45:14,http://douglas.org/barney,0.9599832764,184.55.65.73,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n18755,7,422,2017-05-10 14:34:45,http://rempelwunsch.info/etha_witting,0.4466870980,138.213.101.31,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n18756,7,422,2017-01-27 16:24:20,http://lednerherman.com/haven,0.3723039754,81.8.144.54,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n18757,7,423,2016-12-20 06:57:33,http://hyattbuckridge.com/dwight_oberbrunner,0.1084255265,214.119.36.130,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n18758,7,423,2017-02-20 23:58:49,http://doyledenesik.io/alfredo,0.4880590557,23.65.181.76,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n18759,7,423,2017-06-01 20:20:23,http://haag.net/zakary,0.6442926918,27.40.120.159,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n18760,7,423,2017-04-18 03:50:03,http://littel.biz/isabelle,0.6041362333,29.99.49.79,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n18761,7,423,2017-01-20 01:23:37,http://franecki.org/monte_towne,0.2228415107,127.158.145.226,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n18762,7,423,2017-03-12 14:51:55,http://tillman.net/delphia,0.6243527426,185.249.42.155,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n18763,7,423,2017-02-28 16:50:22,http://lindhackett.org/leonardo,0.9064792447,128.153.4.67,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n18764,7,423,2017-05-03 22:11:00,http://okeefekaulke.org/haie_rodriguez,0.3444117020,238.94.20.200,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n18765,7,423,2017-03-26 09:25:40,http://waters.com/amya_runte,0.6959851710,118.152.249.73,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n18766,7,423,2017-01-09 19:57:53,http://millerkihn.name/casimir,0.4847022732,245.34.184.104,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n18767,7,423,2017-03-21 11:45:32,http://luettgenpfannerstill.name/johnny_hansen,0.7055212822,24.244.220.104,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n18768,7,423,2017-04-21 04:31:08,http://marksrempel.info/kaci.abbott,0.7746874300,203.60.39.251,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n18769,7,423,2017-04-22 14:45:18,http://ernser.net/austen_larkin,0.6089508439,26.155.133.236,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n18770,7,423,2017-01-21 03:23:58,http://pacochagutkowski.net/cathrine,0.9071000577,34.229.144.194,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n18771,7,423,2017-04-19 08:28:57,http://lebsackcole.info/kari_beier,0.6850290182,123.71.7.22,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n18772,7,423,2017-01-13 17:42:03,http://koepp.io/elta_bode,0.4023853512,111.41.205.81,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n18773,7,423,2017-05-13 05:22:39,http://beatty.io/claire_gleason,0.4778280485,253.18.184.100,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n18774,7,423,2017-06-02 23:53:31,http://framigrimes.name/jimmy.romaguera,0.5941875477,69.70.244.149,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n18775,7,423,2017-03-10 21:06:47,http://millsernser.info/adolphus.crooks,0.1528826185,221.5.14.198,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n18776,7,423,2017-01-25 07:27:22,http://jacobioconnell.info/cale,0.0557757833,122.130.112.180,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n18777,7,423,2017-02-06 03:17:55,http://mills.org/ebony,0.2657109497,162.253.185.188,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n18778,7,423,2017-01-08 17:33:07,http://wolffmaggio.co/gretchen,0.7480388670,13.249.179.5,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n18779,7,423,2017-03-09 07:25:19,http://funkboyle.net/rita.abernathy,0.6246213783,208.243.24.122,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n18780,7,423,2017-04-21 04:57:39,http://mullerdietrich.name/pauline,0.1801731008,94.66.125.48,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n18781,7,423,2017-03-31 20:08:06,http://tremblay.biz/carlos_miller,0.9758454383,152.108.147.111,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n18782,7,423,2017-05-11 10:19:02,http://wisozk.biz/preston_dare,0.8654015927,149.89.214.146,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n18783,7,423,2017-01-10 16:16:49,http://nikolaus.co/ines_fay,0.5689677720,254.248.91.13,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n18784,7,423,2017-03-24 17:33:28,http://murazik.name/alayna,0.5775581111,159.157.15.182,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n18785,7,423,2017-02-11 20:46:01,http://bartell.name/minerva_balistreri,0.8486969180,150.147.94.131,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n18786,7,423,2017-06-05 15:22:23,http://brekkeherzog.name/laria,0.1171609518,38.185.154.103,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n18787,7,423,2017-03-28 22:55:04,http://torpkoepp.net/terence_reilly,0.5542935401,103.237.240.226,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n18788,7,423,2017-05-06 08:56:53,http://mclaughlin.name/camryn,0.2621027162,111.196.228.103,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n18789,7,423,2017-06-11 03:29:13,http://kuvalis.co/ambrose.reinger,0.7118096172,104.250.74.84,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n18790,7,423,2017-02-20 19:38:59,http://blanda.name/anastasia.littel,0.9271415326,149.208.185.31,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n18791,7,423,2017-04-26 21:42:49,http://weinathintz.org/jared,0.1654651059,167.143.207.175,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n18792,7,423,2017-06-05 05:15:51,http://millswuckert.biz/carmelo,0.7048228243,210.16.184.201,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n18793,7,423,2017-04-22 20:54:30,http://hahn.com/amanda.ward,0.2379880511,11.3.78.81,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n18794,7,423,2017-02-28 03:26:00,http://wisoky.io/cameron,0.6798202822,69.202.165.27,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n18795,7,423,2017-05-30 18:09:24,http://wuckert.co/name.dickens,0.9509489666,164.201.58.23,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n18796,7,423,2017-04-23 22:53:48,http://powlowski.co/susana_mckenzie,0.1540995475,94.164.60.44,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n18797,7,423,2017-03-07 01:27:31,http://cristpowlowski.org/bernadine,0.8407123444,120.26.31.59,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n18798,7,423,2017-04-23 23:05:57,http://runte.info/amy,0.6710479579,32.235.181.43,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n18799,7,424,2017-02-15 18:58:09,http://legrosblick.co/giovani,0.7057047089,18.240.8.247,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n18800,7,424,2017-02-28 17:23:10,http://hermiston.biz/aurelia,0.2752460620,197.25.138.98,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n18801,7,424,2017-03-27 02:26:21,http://haag.org/sarah_turner,0.3039557715,70.223.142.149,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n18802,7,424,2016-12-13 18:23:41,http://hoppe.org/josephine,0.2840345531,72.155.217.25,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n18803,7,424,2017-05-26 06:40:51,http://kerluke.info/jerome,0.5459567946,34.204.183.131,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n18804,7,424,2017-01-11 20:38:59,http://thiel.co/kaylee,0.4396290312,90.103.248.120,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n18805,7,424,2017-04-26 11:01:04,http://collins.net/claudia,0.6827068769,101.103.166.9,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n18806,7,424,2017-06-04 10:29:28,http://stoltenbergfarrell.io/ryann.carter,0.6345564765,131.234.29.227,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n18807,7,424,2017-05-27 12:25:30,http://bartonvolkman.com/loren,0.9603213807,228.146.127.25,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n18808,7,424,2017-03-26 11:39:09,http://terry.org/alfredo,0.5841196994,151.211.97.149,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n18809,7,424,2016-12-28 14:10:58,http://hayeshahn.co/clare,0.0020311440,195.189.187.86,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n18810,7,424,2017-03-13 08:38:16,http://wiegand.co/nikko,0.8371923710,234.51.108.185,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n18811,7,424,2017-03-22 08:33:09,http://lang.biz/bridgette_oconnell,0.9564314602,228.55.148.31,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n18812,7,424,2017-02-23 20:39:03,http://mclaughlindickens.biz/kade.kirlin,0.0760666776,160.137.192.108,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n18813,7,424,2017-05-19 15:04:46,http://heel.net/alison,0.4863040777,128.44.220.204,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n18814,7,424,2017-03-01 08:32:18,http://erdmansipes.info/eloisa.rohan,0.1313289922,121.94.163.214,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n18815,7,424,2017-03-12 03:51:22,http://koepp.io/emily.conroy,0.7710888795,39.179.137.11,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n18816,7,424,2017-03-15 09:42:20,http://rodriguez.com/dale,0.3152990493,25.16.182.126,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n18817,7,424,2017-02-22 09:13:35,http://konopelskizboncak.com/gage.borer,0.7386919344,95.28.34.188,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n18818,7,424,2017-05-11 22:45:52,http://hagenes.biz/kirk,0.9451548207,161.76.198.176,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n18819,7,424,2017-06-13 23:08:09,http://bednar.co/kacey.yost,0.7239267627,187.205.8.90,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n18820,7,424,2017-02-12 05:03:55,http://mcglynn.name/carmelo,0.4430942827,126.26.89.109,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n18821,7,424,2017-02-23 09:43:59,http://robelpurdy.io/zoe_volkman,0.9543472506,124.60.32.235,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n18822,7,424,2017-01-27 15:55:23,http://kuhnmcclure.info/amiya_hand,0.5957609892,53.149.6.149,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n18823,7,424,2017-05-21 07:16:26,http://okeefebuckridge.com/annabelle.marks,0.3898827988,37.143.16.192,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n18824,7,424,2017-01-23 17:29:36,http://joneshudson.info/steve_kirlin,0.9967681595,201.86.234.202,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n18825,7,424,2017-03-07 08:39:37,http://thielmaggio.com/wendell_hilll,0.0898476878,109.110.206.104,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n18826,7,424,2017-03-18 02:56:31,http://kubglover.info/juwan,0.7279446237,64.104.70.131,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n18827,7,424,2016-12-24 11:27:02,http://pfannerstill.io/german,0.6421894274,75.104.100.170,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n18828,7,424,2017-01-25 05:47:05,http://schmitt.co/savanah_ohara,0.2668193754,170.62.13.118,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n18829,7,424,2017-03-05 03:31:38,http://mosciskinolan.com/libbie.bechtelar,0.6902012320,19.19.215.235,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n18830,7,424,2017-06-09 06:02:34,http://altenwerthgoldner.biz/kameron_hammes,0.3188945287,231.184.210.226,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n18831,7,424,2017-01-06 00:58:32,http://huel.net/forest_johns,0.9979699951,121.217.38.246,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n18832,7,424,2017-02-18 15:11:44,http://beierschaden.co/vladimir_blick,0.1736462305,130.127.38.18,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n18833,7,424,2017-05-20 15:49:20,http://west.org/sanford,0.2980051739,38.212.163.10,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n18834,7,424,2017-04-04 12:11:15,http://kuvalisconsidine.org/chad_rolfson,0.9250327917,13.88.171.200,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n18835,7,424,2017-05-20 03:26:17,http://reichelwelch.io/ivory.sawayn,0.6415398637,51.93.47.247,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n18836,7,424,2016-12-25 01:09:36,http://huel.name/kraig_vonrueden,0.1466624533,11.36.205.6,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n18837,7,424,2017-03-19 11:59:55,http://keeblergleason.com/zelda,0.4967049605,172.206.208.81,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n18838,7,424,2017-02-14 16:17:25,http://kihnrowe.biz/caroline.schoen,0.3520093230,43.191.197.80,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n18839,7,424,2017-03-01 08:35:51,http://uptonhermann.com/buster_leuschke,0.8355263812,210.37.191.178,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n18840,7,424,2017-01-05 09:33:19,http://kuhicdeckow.io/aditya_hills,0.6436877200,84.47.234.201,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n18841,7,424,2017-05-16 05:19:19,http://ritchie.name/margarett,0.9279654882,108.219.19.183,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n18842,7,424,2017-05-04 00:44:37,http://hansenokuneva.net/retha_morar,0.8517722666,81.204.101.46,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n18843,7,424,2017-06-08 04:36:32,http://kelerkrajcik.net/jo_runolfon,0.8950329092,85.5.69.129,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n18844,7,424,2017-04-20 07:04:51,http://hackettschinner.co/caleigh.mueller,0.0734160858,113.74.115.98,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n18845,7,424,2017-04-09 09:06:41,http://walkerwill.info/ryleigh.senger,0.0762922065,65.229.176.18,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n18846,7,424,2017-03-26 12:40:13,http://bins.io/izabella,0.7279309891,223.216.227.5,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n18847,7,424,2017-06-10 18:54:05,http://yost.info/jayce_harvey,0.8596169908,71.8.6.207,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n18848,7,424,2017-05-08 05:42:52,http://mcculloughsporer.co/thalia,0.7922783448,173.63.211.162,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n18849,7,424,2017-06-14 01:52:23,http://schoennikolaus.org/dorthy,0.4022323617,74.210.94.9,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n18850,7,424,2017-04-18 07:36:32,http://ondrickamcclure.io/makayla,0.2872190698,48.244.116.212,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n18851,7,424,2017-02-22 13:32:34,http://naderschinner.info/dudley,0.4299741657,18.180.96.78,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n18852,7,424,2017-04-27 11:24:53,http://bayerschiller.org/anais.abshire,0.9668415481,88.136.223.180,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n18853,7,424,2017-02-12 21:56:25,http://shieldshayes.io/mikel,0.6813928048,12.103.147.110,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n18854,7,424,2017-02-10 00:44:20,http://heaney.name/tony,0.7343459390,231.122.8.28,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n18855,7,424,2017-04-05 19:42:45,http://heathcote.co/jesus,0.8548761029,193.14.210.124,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n18856,7,424,2017-01-16 19:32:54,http://heller.com/janet.feeney,0.4505842499,172.142.174.33,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n18857,7,424,2017-05-03 11:15:14,http://hirthelarson.info/vena.strosin,0.1610218110,23.242.88.8,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n18858,7,424,2017-01-06 12:48:45,http://larkin.io/geo,0.3053404252,23.200.147.186,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n18859,7,424,2017-04-15 15:35:49,http://veum.biz/freda,0.1001481383,148.94.250.182,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n18860,7,424,2017-04-25 07:51:47,http://satterfieldbrown.name/raina.doyle,0.9285968439,134.35.106.45,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n18861,7,425,2017-02-05 16:15:23,http://nitzsche.biz/roxanne,0.5821259369,63.110.157.17,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n18862,7,425,2017-05-16 05:14:02,http://gislason.net/angie.bins,0.2758675486,208.136.92.155,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n18863,7,425,2017-05-22 12:00:28,http://schmidttillman.io/jeyca_wilkinson,0.5411108295,161.190.65.146,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n18864,7,425,2017-01-15 14:24:42,http://koeppwalsh.info/gaetano,0.1207049644,47.186.220.7,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n18865,7,425,2016-12-27 20:50:00,http://hills.info/cameron,0.5479459627,97.163.56.35,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n18866,7,425,2017-03-30 19:31:31,http://rohan.co/madaline,0.8509683040,97.63.238.67,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n18867,7,425,2017-04-30 17:35:19,http://farrell.name/barney_zboncak,0.7929119054,202.41.42.154,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n18868,7,425,2016-12-15 22:12:55,http://johnston.net/hettie,0.6108610126,163.94.48.80,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n18869,7,425,2017-04-10 23:15:45,http://bodeskiles.org/odea_mohr,0.8512465565,210.183.202.31,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n18870,7,425,2017-02-19 08:25:05,http://king.biz/lorenz,0.9161939051,139.139.127.196,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n18871,7,425,2017-06-13 04:13:52,http://hermannwaelchi.info/gerson.feil,0.6703363834,172.87.213.102,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n18872,7,425,2017-01-21 04:45:10,http://harris.net/jeyca,0.4943332863,197.147.208.149,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n18873,7,425,2017-03-21 14:19:45,http://murphy.net/elsa_mueller,0.0386805183,68.229.147.205,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n18874,7,425,2017-01-07 18:24:27,http://cole.info/kristoffer.schuster,0.0375527458,40.151.58.71,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n18875,7,425,2017-06-09 01:26:50,http://ko.io/kareem,0.0733072613,41.70.47.37,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n18876,7,425,2017-01-20 03:29:55,http://schneider.info/marc,0.4388430720,98.13.26.147,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n18877,7,425,2017-02-22 11:49:48,http://mcglynn.name/yesenia.hickle,0.3395245308,54.100.21.247,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n18878,7,425,2017-05-28 07:25:22,http://murazik.co/everett_kaulke,0.0328551975,142.166.143.159,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n18879,7,425,2017-02-27 11:32:19,http://weimannschuster.com/lora,0.3083424019,185.203.49.132,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n18880,7,425,2017-06-04 23:32:34,http://hoegermcclure.org/corene,0.6579782693,119.233.100.59,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n18881,7,425,2017-05-01 13:21:38,http://bechtelar.org/bertrand,0.7128746576,226.227.212.57,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n18882,7,425,2017-04-12 10:02:49,http://mitchell.name/laurie,0.7291928600,167.9.147.101,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n18883,7,425,2017-02-03 15:25:01,http://kuphal.org/noel,0.1172615827,135.150.24.147,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n18884,7,425,2017-06-08 09:10:49,http://veum.co/jodie,0.7374242241,64.118.207.78,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n18885,7,425,2017-02-20 01:55:07,http://mertz.org/marco,0.1362357160,120.85.29.159,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n18886,7,425,2017-02-25 09:06:33,http://ritchie.biz/austin,0.2250461512,81.17.101.125,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n18887,7,425,2017-04-05 19:08:15,http://denesikjacobson.name/dolly.hilll,0.8656749959,166.19.229.38,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n18888,7,425,2017-03-28 06:23:06,http://rosenbaum.io/jocelyn_kaulke,0.7007745679,151.104.193.216,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n18889,7,425,2017-05-29 00:24:04,http://marksankunding.com/davin_keler,0.6042596553,245.85.163.118,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n18890,7,425,2017-01-21 01:56:23,http://collinsheaney.io/johan_spencer,0.3688629410,159.52.60.140,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n18891,7,425,2017-06-11 16:30:12,http://effertz.net/jacinthe,0.7570197560,64.71.24.51,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n18892,7,425,2017-05-03 06:56:47,http://cartwright.org/nova.rutherford,0.0606327131,103.243.121.129,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n18893,7,425,2017-06-10 13:29:10,http://jerde.co/johnny.kaulke,0.1943964822,159.90.202.60,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n18894,7,425,2017-02-02 17:22:04,http://schoenhaag.co/josephine,0.5210939574,89.106.9.149,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n18895,7,425,2017-03-19 15:26:51,http://paucek.co/mariano,0.7969896052,24.12.74.226,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n18896,7,425,2017-05-31 15:19:44,http://bogisich.io/wava,0.4893807962,122.67.19.122,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n18897,7,425,2017-02-06 12:36:05,http://pfeffer.org/peggie,0.4037772951,222.26.244.87,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n18898,7,425,2017-03-13 17:47:57,http://moen.info/aliyah,0.3849622502,38.109.185.45,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n18899,7,425,2017-01-21 07:21:53,http://hillwaniawski.biz/brycen.bergnaum,0.0439720279,60.204.163.71,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n18900,7,425,2017-05-10 08:56:44,http://roob.org/darron,0.0386805868,121.11.228.228,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n18901,7,425,2017-02-10 14:58:40,http://kuhn.io/randy,0.9831097267,249.90.10.240,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n18902,7,425,2017-01-22 14:19:46,http://kling.info/samson,0.9046765181,158.147.120.171,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n18903,7,425,2016-12-19 01:22:28,http://hoppe.com/alejandra_kemmer,0.0522724592,87.225.189.73,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n18904,7,425,2017-05-18 15:27:09,http://haag.net/alda,0.9733864068,195.26.6.45,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n18905,7,425,2017-05-01 05:25:31,http://ledner.net/helen_abshire,0.4162259035,98.48.195.109,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n18906,7,426,2017-03-11 06:45:56,http://hegmann.net/eino.towne,0.4294517699,205.131.160.38,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n18907,7,426,2017-02-10 03:24:22,http://lang.io/arnold,0.0086292703,91.38.62.119,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n18908,7,426,2017-02-18 22:09:03,http://rueckerspinka.io/christelle,0.2504043590,77.40.93.231,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n18909,7,426,2017-04-23 11:26:13,http://hills.io/zackery,0.6226209609,245.76.98.177,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n18910,7,426,2017-02-19 19:53:30,http://abshire.biz/judah,0.4061295610,100.75.209.72,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n18911,7,426,2017-04-22 23:13:37,http://kirlin.com/antwan,0.6763836925,205.232.204.181,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n18912,7,426,2017-04-19 09:50:41,http://walterstark.name/zane.reilly,0.9051106655,146.111.94.87,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n18913,7,426,2017-06-12 20:55:15,http://fritschortiz.info/carolyne_monahan,0.4622283874,155.184.113.46,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n18914,7,426,2016-12-20 01:32:28,http://hoegerdickinson.io/annabelle,0.7865352208,140.102.77.75,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n18915,7,426,2017-06-01 05:42:54,http://sauer.com/hattie_ullrich,0.9701937192,60.236.159.215,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n18916,7,426,2017-04-17 06:27:12,http://collins.info/durward,0.7647278915,187.65.195.177,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n18917,7,426,2017-02-04 07:23:39,http://hirthejenkins.name/ansley_heel,0.3740679580,2.38.234.25,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n18918,7,426,2017-04-24 14:00:07,http://dooley.org/taya_effertz,0.3520311896,152.21.131.251,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n18919,7,426,2017-02-23 23:32:42,http://brakus.com/eloisa.mcclure,0.0073553153,185.128.32.70,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n18920,7,426,2017-04-21 03:19:36,http://klingdooley.io/abdiel,0.9190063822,70.34.218.156,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n18921,7,426,2017-04-09 06:52:04,http://little.info/domenico.hartmann,0.8593080909,169.57.203.95,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n18922,7,426,2016-12-23 07:39:34,http://kshlerin.com/morgan.schroeder,0.1533359026,153.138.170.139,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n18923,7,426,2017-03-07 03:30:02,http://murazik.info/helmer,0.6358662741,229.75.92.154,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n18924,7,426,2017-05-04 05:00:51,http://mosciskiankunding.info/felix,0.4802037942,116.243.191.73,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n18925,7,426,2017-05-16 20:59:07,http://halvorson.net/alyon,0.0744733044,251.185.130.181,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n18926,7,426,2017-05-13 09:11:25,http://legros.org/lesley,0.0927159294,174.123.149.56,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n18927,7,426,2017-02-17 14:47:41,http://mannkirlin.net/jacques,0.5749792152,105.161.217.253,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n18928,7,426,2017-01-12 10:02:54,http://orn.info/bernice_gusikowski,0.4063275015,188.168.11.252,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n18929,7,426,2017-03-14 14:43:02,http://gaylordpaucek.name/theodore,0.6645056537,45.58.52.46,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n18930,7,426,2017-05-12 15:36:03,http://langworth.name/norene,0.3852759727,103.16.230.49,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n18931,7,426,2017-01-28 15:42:07,http://farrell.name/adelia.altenwerth,0.0337799691,55.186.8.165,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n18932,7,426,2017-06-09 09:59:35,http://balistreri.co/wellington.reynolds,0.8622185270,167.102.213.64,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n18933,7,426,2017-05-13 22:50:16,http://hickle.name/demarcus,0.2581259735,225.84.139.104,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n18934,7,426,2017-01-21 02:38:46,http://haley.org/wanda.marquardt,0.2418921009,7.224.98.209,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n18935,7,426,2017-05-05 06:52:46,http://goyette.net/collin_greenfelder,0.4476510048,223.184.226.85,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n18936,7,426,2017-02-13 03:01:28,http://kling.com/catherine_skiles,0.6747213880,173.218.6.11,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n18937,7,426,2017-01-06 05:28:59,http://streichheller.io/graciela.homenick,0.2949914716,11.59.247.20,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n18938,7,426,2017-04-23 22:17:30,http://rowe.net/madisen.hansen,0.3089262603,158.50.16.246,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n18939,7,426,2017-06-01 11:58:22,http://tremblay.net/dock_leannon,0.3058609278,148.142.215.219,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n18940,7,426,2017-06-12 02:58:13,http://kub.name/charley,0.7156406523,144.54.142.57,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n18941,7,426,2017-06-03 19:55:25,http://rempel.net/amos,0.0140923330,2.33.153.88,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n18942,7,426,2017-06-12 12:24:51,http://johnston.info/mazie.bashirian,0.9158931845,226.53.201.49,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n18943,7,426,2017-02-09 02:58:03,http://grahamschamberger.info/jaclyn.stracke,0.4130724746,69.210.113.114,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n18944,7,426,2016-12-31 23:40:18,http://farrellboyle.info/merl_pagac,0.5267899264,119.133.57.144,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n18945,7,426,2017-05-04 16:54:48,http://reilly.biz/fritz,0.3081975003,205.225.169.131,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n18946,7,426,2017-03-25 14:49:44,http://gibson.org/alda,0.8697548115,157.54.177.168,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n18947,7,426,2017-01-27 07:20:00,http://hills.info/vergie,0.9896333763,243.78.22.228,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n18948,7,426,2017-02-15 14:45:13,http://mayer.io/toby.wunsch,0.0435741173,171.39.40.93,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n18949,7,426,2017-05-02 03:18:40,http://wehnerrolfson.net/camila,0.5392535323,150.52.14.21,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n18950,7,427,2017-04-05 06:24:55,http://klein.info/casey.mccullough,0.7576334508,242.32.72.103,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n18951,7,427,2017-01-30 07:25:07,http://schuster.net/andrew_predovic,0.8529680713,152.56.147.161,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n18952,7,427,2017-02-12 16:32:49,http://abernathy.name/zander_howe,0.3551304883,6.160.28.217,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": true}\"\n18953,7,427,2017-04-04 17:34:49,http://weber.info/cecilia_fritsch,0.8331105991,24.105.173.186,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n18954,7,427,2017-05-19 21:41:46,http://langworth.info/carolina,0.7335877499,187.67.68.182,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n18955,7,427,2017-05-14 07:02:07,http://lubowitz.info/ida,0.9687174809,46.142.122.149,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n18956,7,427,2016-12-16 12:06:08,http://brekke.net/taryn,0.4829981026,210.200.113.146,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n18957,7,427,2017-02-16 15:01:39,http://hegmann.net/vernice.wintheiser,0.4169756099,134.244.131.121,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n18958,7,427,2017-04-26 18:46:16,http://morar.biz/nikita.rice,0.2400067598,150.241.53.183,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n18959,7,427,2017-03-26 06:21:05,http://mrazullrich.name/berenice,0.3020694857,4.32.118.25,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n18960,7,427,2016-12-25 03:05:01,http://kuhn.org/morris,0.6333359796,240.186.28.139,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n18961,7,427,2017-01-15 06:49:43,http://greenfelder.biz/mabel_schmidt,0.2139445384,213.160.199.21,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n18962,7,427,2017-06-13 21:58:57,http://powlowski.name/kelli_kovacek,0.1551217532,21.147.83.73,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n18963,7,427,2017-04-15 12:29:18,http://mitchell.net/liam,0.2165492341,65.115.106.206,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n18964,7,427,2017-04-29 05:40:16,http://rau.org/raymond_williamson,0.8577014316,30.5.30.212,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n18965,7,427,2017-05-05 06:55:26,http://mayer.com/kevon,0.8756326505,250.115.244.50,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n18966,7,427,2016-12-31 18:09:47,http://yundttorphy.org/mack.willms,0.0994225229,47.118.165.143,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n18967,7,427,2017-04-18 05:36:22,http://friesenwillms.org/esteban.gerlach,0.5906951441,118.174.117.230,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n18968,7,427,2017-02-12 08:24:41,http://stroman.io/nigel,0.6276292914,181.216.177.180,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n18969,7,427,2017-06-12 20:44:37,http://shanahanhahn.name/jovan.runolfon,0.9684960820,104.139.206.144,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n18970,7,427,2017-01-20 11:34:08,http://heel.info/alize.bins,0.4057892702,28.198.203.212,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n18971,7,427,2017-06-06 08:04:15,http://walker.io/colleen,0.4284207031,211.135.73.243,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n18972,7,427,2017-04-21 11:02:38,http://stehr.co/felicity,0.3734471476,174.245.84.208,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n18973,7,427,2016-12-23 15:32:04,http://bechtelar.name/aric_paucek,0.0333618060,181.25.87.71,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n18974,7,427,2017-01-13 15:40:36,http://labadie.org/judy,0.2385336360,99.195.34.31,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n18975,7,427,2017-04-05 14:13:48,http://treutel.name/helmer_keeling,0.6030522055,98.231.179.96,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n18976,7,427,2017-03-01 20:26:21,http://greenfelderkilback.org/gudrun,0.0157478924,153.66.136.50,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n18977,7,427,2017-05-14 18:07:47,http://glover.biz/orie.hermann,0.5090140007,66.170.181.27,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n18978,7,427,2016-12-14 12:00:30,http://kochdickinson.biz/anya_hagenes,0.4040356011,112.134.44.34,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n18979,7,427,2017-02-14 00:17:22,http://vonrueden.name/anastacio,0.1068795941,242.176.119.226,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n18980,7,427,2017-04-20 00:45:46,http://quitzonjohnson.biz/marcellus_weinat,0.3766739390,136.162.221.239,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n18981,7,427,2017-03-26 07:11:00,http://hyatt.com/ashly_tillman,0.4671923210,5.9.16.127,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n18982,7,427,2017-06-07 09:03:16,http://strosinblanda.info/otha_goodwin,0.0151850135,193.216.196.51,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n18983,7,427,2016-12-25 14:46:57,http://hodkiewicz.com/wyatt,0.5331922586,134.118.194.67,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n18984,7,427,2017-01-05 22:54:56,http://trantow.org/trace_buckridge,0.7630491209,208.108.116.118,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n18985,7,427,2017-01-18 01:29:49,http://sipes.biz/mallory.gutmann,0.0601174091,5.97.205.3,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n18986,7,427,2017-03-08 11:14:47,http://zemlak.info/nichole.cummings,0.0450422821,89.68.33.29,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n18987,7,427,2017-04-25 15:10:00,http://grant.name/sherman,0.5366644655,116.125.224.95,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": false}\"\n18988,7,427,2017-05-09 01:09:39,http://johnson.com/opal.gutmann,0.9434287992,116.203.31.95,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n18989,7,427,2017-01-07 11:42:35,http://jaskolski.com/sister,0.2239283060,132.152.62.120,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n18990,7,427,2016-12-19 16:28:10,http://wisoky.co/brooklyn.erdman,0.2682414450,43.173.254.69,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n18991,7,427,2017-04-26 16:17:41,http://schroederernser.name/brian_schroeder,0.4822956081,9.11.6.40,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n18992,7,427,2017-04-22 22:14:33,http://halvorson.com/adolph.kihn,0.5324550275,181.215.171.32,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n18993,7,427,2017-04-08 20:26:23,http://mraz.name/dixie,0.8691700342,159.240.190.15,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n18994,7,427,2017-06-02 04:24:44,http://hettinger.biz/manuel.mcglynn,0.7642585601,198.161.114.121,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n18995,7,427,2017-01-18 02:21:48,http://ratke.io/ansel,0.8398247009,10.140.222.194,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n18996,7,427,2017-03-15 17:52:05,http://mantehoeger.io/warren_spinka,0.1123914691,235.226.122.69,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n18997,7,427,2017-06-01 15:40:00,http://fahey.com/malcolm.jacobs,0.1527839764,171.185.31.136,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n18998,7,427,2016-12-19 17:38:44,http://sauer.io/marques_barton,0.7466241166,6.204.183.136,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n18999,7,427,2017-03-21 06:59:50,http://champlin.com/stephon.oberbrunner,0.9542421775,17.196.32.236,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n19000,7,427,2017-04-14 06:35:43,http://grant.net/myra,0.7291043324,136.41.211.254,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n19001,7,427,2017-01-27 17:31:28,http://wolff.info/augusta_runolfsdottir,0.7512145947,97.41.204.77,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n19002,7,427,2017-01-10 21:13:28,http://huel.info/porter_smitham,0.3920148133,107.42.194.138,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n19003,7,427,2017-02-12 21:41:38,http://lesch.info/florian,0.0208996369,12.126.69.37,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n19004,7,427,2017-06-07 02:37:41,http://faheyhammes.org/terrance.paucek,0.2594419403,33.107.42.242,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n19005,7,427,2017-05-21 04:36:21,http://ruel.io/lyda,0.5286473770,87.109.151.71,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n19006,7,427,2017-01-09 23:40:44,http://runolfsdottir.name/mortimer.herzog,0.9150390317,124.52.144.48,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n19007,7,427,2017-04-11 23:24:40,http://kemmer.name/nathen,0.4734909360,160.223.101.248,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n19008,7,427,2017-03-23 10:55:16,http://luettgen.co/dallas_mcglynn,0.2074056003,233.248.32.202,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n19009,7,427,2017-04-03 05:32:03,http://collins.net/lenna,0.7066170221,10.165.218.95,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n19010,7,427,2017-04-05 12:18:41,http://conroy.org/luna_nader,0.9544507585,2.87.134.57,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n19011,7,427,2017-06-04 11:56:39,http://dubuque.name/chaz,0.6605090923,196.24.102.78,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n19012,7,427,2017-02-11 17:19:58,http://reillyruel.org/favian_bashirian,0.4678278904,233.214.154.146,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n19013,7,427,2017-03-31 23:42:43,http://shields.net/kathryn,0.2661984637,185.86.87.115,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n19014,7,427,2017-03-25 14:53:49,http://okon.com/wanda,0.1714935409,11.207.20.122,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n19015,7,428,2017-03-15 10:07:22,http://mcglynnmclaughlin.biz/israel,0.2745220596,90.52.186.168,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n19016,7,428,2017-01-29 08:47:49,http://vonrueden.info/collin.mayer,0.2652139441,144.78.254.6,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n19017,7,428,2017-02-23 00:56:08,http://schuppe.net/rick,0.0862767219,181.180.167.192,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n19018,7,428,2017-03-10 10:41:05,http://wolffschulist.co/mckenna_hickle,0.3455486523,69.97.68.251,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n19019,7,428,2017-03-28 07:26:33,http://conn.io/nelle_jacobs,0.6284229977,100.156.106.2,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n19020,7,428,2017-01-05 19:29:19,http://pfefferpacocha.com/uriah_lemke,0.0113270956,147.176.214.249,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n19021,7,428,2017-04-12 10:38:46,http://dickens.net/cooper,0.4334752925,127.157.157.75,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n19022,7,428,2017-01-04 07:49:48,http://cummingchuppe.com/alanna,0.8506323934,206.84.71.209,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n19023,7,428,2017-04-08 17:11:51,http://lubowitz.com/elsa,0.2819020560,56.194.231.230,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n19024,7,428,2017-03-13 04:53:43,http://homenick.com/bernadette_upton,0.0150343498,198.101.161.197,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n19025,7,428,2017-05-31 10:39:49,http://wilderman.io/daron,0.4978751305,218.200.22.113,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n19026,7,428,2016-12-26 23:02:13,http://brown.biz/vilma,0.2382526621,171.113.133.109,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n19027,7,428,2017-05-09 10:13:03,http://pfeffer.name/jonathon_connelly,0.5919482289,69.108.86.223,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n19028,7,428,2017-04-05 19:54:53,http://boyle.info/devante,0.7832313697,108.170.140.11,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n19029,7,428,2017-04-17 23:04:41,http://hegmann.net/daren_shanahan,0.1935665060,47.227.141.231,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n19030,7,428,2017-05-09 17:59:54,http://hicklecremin.net/naomie,0.0770459220,198.95.208.254,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n19031,7,428,2017-02-03 06:36:42,http://rolfson.biz/marcellus,0.5801586438,206.250.107.94,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n19032,7,428,2017-01-28 15:50:10,http://feeneytorphy.info/alyson_beatty,0.2933218082,70.168.226.114,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n19033,7,428,2017-01-13 19:47:13,http://moore.info/sam_hilpert,0.4536278542,74.14.14.157,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n19034,7,428,2017-02-18 08:56:20,http://tillmanrohan.info/libby,0.4979785301,30.21.158.137,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n19035,7,428,2017-01-15 16:48:12,http://collins.com/werner,0.9635852337,113.117.110.237,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n19036,7,428,2017-03-26 06:54:32,http://denesik.net/evert.reinger,0.1438154631,233.32.190.202,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n19037,7,429,2017-04-15 23:04:53,http://lowe.org/margret,0.5182091536,27.75.14.233,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n19038,7,429,2017-03-16 22:15:41,http://haley.co/pearline.leffler,0.1237532065,86.78.203.234,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n19039,7,429,2017-05-21 06:40:40,http://hodkiewicz.com/cornelius_jakubowski,0.9179219213,170.212.190.166,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n19040,7,429,2017-03-13 02:03:23,http://ritchiefranecki.biz/donna_watsica,0.1952112932,178.53.181.65,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n19041,7,429,2017-03-28 05:42:03,http://hayesbrown.net/janae_fadel,0.7442527821,46.173.28.234,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n19042,7,429,2017-05-16 20:24:05,http://ankundinghuels.name/khalil_brakus,0.2107031712,209.116.48.79,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n19043,7,429,2017-02-04 03:14:50,http://berge.io/dayton.harvey,0.1221169705,19.126.21.16,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n19044,7,429,2017-01-04 12:55:43,http://cummerata.org/magdalena,0.9197538012,177.87.125.23,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n19045,7,429,2017-05-05 03:13:05,http://satterfield.name/rowland.terry,0.1232725479,57.55.57.242,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n19046,7,429,2017-01-06 20:21:11,http://kuhic.biz/tillman,0.5986801042,163.77.81.241,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n19047,7,429,2016-12-24 08:43:48,http://medhurst.co/ryleigh,0.0376113270,60.251.141.172,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n19048,7,429,2017-04-09 11:49:44,http://carrolllittel.com/deie.brown,0.4272487617,9.244.161.36,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n19049,7,429,2017-03-17 07:24:11,http://grant.org/ted_breitenberg,0.9449467938,161.4.234.48,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n19050,7,429,2017-01-30 01:16:32,http://emardflatley.io/yasmine_treutel,0.3092434651,135.45.94.211,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n19051,7,429,2017-02-14 18:32:35,http://watsicaheidenreich.info/geovanny_erdman,0.4314858016,71.126.85.104,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n19052,7,429,2017-05-08 05:24:45,http://wintheiser.net/kaley_stark,0.0825669889,143.73.208.113,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n19053,7,429,2017-02-13 02:35:52,http://moriette.net/roberta,0.9576020241,12.244.117.154,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n19054,7,429,2017-02-04 12:09:02,http://von.io/susanna_volkman,0.7315659049,67.164.221.109,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n19055,7,429,2017-03-05 17:33:03,http://wolf.name/lenore,0.2785521589,120.219.181.106,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": false}\"\n19056,7,429,2017-02-22 06:31:05,http://purdy.net/augusta,0.9597705035,172.85.78.132,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n19057,7,429,2017-02-28 02:40:59,http://trantowbeahan.biz/angelina_daugherty,0.9755004689,242.87.34.226,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n19058,7,429,2017-05-10 22:54:15,http://medhurst.name/marisol_haag,0.8330861295,174.79.192.70,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": true}\"\n19059,7,429,2017-04-14 22:00:17,http://weber.info/chet,0.8715131232,65.74.163.104,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n19060,7,429,2017-01-20 22:39:44,http://runolfon.co/denis,0.2282702882,42.189.78.226,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n19061,7,429,2016-12-18 13:25:19,http://emardzieme.io/chasity,0.5840311422,142.33.201.238,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n19062,7,429,2017-02-13 08:08:58,http://tromp.co/katrine_okon,0.2053526318,61.77.65.188,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n19063,7,429,2016-12-24 14:11:29,http://goodwinruecker.name/ollie,0.5298457402,163.36.226.81,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n19064,7,429,2017-03-16 23:37:14,http://sauer.io/bud,0.2470211136,65.62.185.173,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n19065,7,429,2017-04-01 02:30:20,http://dickens.com/braeden.satterfield,0.3759014284,124.13.208.241,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n19066,7,429,2017-04-06 23:18:50,http://blick.org/wanda.howe,0.5104041106,30.127.233.17,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n19067,7,429,2017-05-18 02:25:05,http://marvin.io/royce,0.9796879005,142.142.98.150,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n19068,7,429,2017-05-11 05:01:10,http://johnston.com/christiana,0.9488145589,89.159.147.227,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n19069,7,429,2017-01-24 01:15:13,http://hilll.biz/glen.emmerich,0.0605781343,113.44.209.168,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n19070,7,429,2017-02-02 23:59:53,http://ledner.com/hector,0.6049578857,81.151.130.201,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n19071,7,429,2017-06-11 12:40:49,http://howe.info/clotilde_koepp,0.8174673048,245.159.185.148,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n19072,7,429,2017-03-31 17:16:34,http://hane.io/darryl.lueilwitz,0.2105300744,7.4.88.171,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n19073,7,429,2016-12-21 03:42:39,http://mertz.net/ford,0.4663740358,225.252.145.101,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n19074,7,429,2017-04-05 18:03:51,http://balistreri.net/bennie,0.7155146463,174.16.179.209,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": true}\"\n19075,7,429,2017-02-06 10:20:22,http://frami.biz/kory_hilpert,0.9163563514,216.192.121.49,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": true}\"\n19076,7,429,2017-02-08 02:50:24,http://hauckhoppe.net/fern.davis,0.4115034549,121.83.79.147,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n19077,7,429,2017-05-03 23:46:08,http://willms.info/hailey,0.9896121995,110.37.171.82,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n19078,7,429,2017-05-11 17:04:03,http://sporermills.biz/jalen,0.6589864726,193.76.213.122,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n19079,7,429,2017-04-16 15:56:26,http://davis.biz/caandra_smith,0.2756109521,66.197.2.196,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n19080,7,429,2017-04-09 02:55:21,http://kulas.info/marielle_cain,0.3696429386,192.93.186.54,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n19081,7,429,2017-03-04 21:06:41,http://treutel.info/ayla,0.4639756348,78.166.114.95,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n19082,7,429,2017-04-15 10:36:01,http://kihn.co/dahlia.sauer,0.5898669890,81.97.143.248,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": false}\"\n19083,7,429,2017-05-22 23:32:45,http://ferry.info/troy,0.8404307810,90.70.130.192,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n19084,7,430,2017-01-23 10:38:38,http://rath.io/jeanette,0.9442018844,244.72.216.129,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n19085,7,430,2017-03-21 01:17:44,http://nitzsche.biz/eduardo_jacobson,0.7041721895,21.187.3.137,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n19086,7,430,2017-01-15 15:13:07,http://koepp.org/chelsie,0.7017735169,157.191.216.164,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n19087,7,430,2017-01-31 19:53:55,http://dickens.info/sydnee,0.4386028390,145.38.250.31,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n19088,7,430,2017-05-24 02:43:59,http://larkin.org/addison,0.8163125569,219.2.29.2,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n19089,7,430,2017-05-23 07:04:00,http://rohan.biz/pierce,0.8063943861,135.47.32.71,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n19090,7,430,2017-04-01 21:05:51,http://hermiston.org/bailee,0.9015150320,240.239.61.175,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n19091,7,430,2017-02-10 11:01:01,http://padbergveum.org/zelda,0.2434684029,71.137.149.184,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n19092,7,430,2017-02-11 10:06:48,http://grant.io/aliyah_mitchell,0.1932067052,233.14.70.139,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n19093,7,430,2017-04-15 22:33:43,http://turcottegrimes.info/frederic,0.9371822376,201.142.201.96,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n19094,7,430,2017-03-12 23:47:07,http://gottlieb.name/miles_damore,0.5645486698,93.136.75.60,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n19095,7,430,2017-02-02 16:41:50,http://gutkowskiorn.io/lulu,0.5427576897,72.135.245.183,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n19096,7,430,2017-04-20 12:10:44,http://jakubowskibayer.info/timmothy,0.3072360987,230.168.142.153,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n19097,7,430,2017-02-07 20:56:44,http://larsonkuhic.com/roberto_herzog,0.2621290885,62.98.61.252,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": false}\"\n19098,7,430,2017-06-10 14:30:54,http://romaguera.name/lorenzo.walker,0.3054911970,34.248.221.219,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n19099,7,430,2016-12-24 09:49:32,http://torphydubuque.name/brittany.jacobi,0.0233194095,137.221.9.60,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n19100,7,430,2017-06-04 19:13:29,http://kiehn.net/eleanore,0.8489264120,159.84.185.87,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n19101,7,430,2017-02-09 00:11:21,http://erdmanroob.co/name_reichel,0.0798206062,156.121.29.145,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n19102,7,430,2017-03-24 22:48:13,http://moorekuphal.com/salvador,0.8148968281,80.22.76.105,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n19103,7,430,2017-05-20 05:38:43,http://bruen.biz/shyanne,0.3942129750,246.127.18.205,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n19104,7,430,2017-03-20 16:19:27,http://mertzlangosh.name/curtis,0.4760729725,195.119.136.117,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n19105,7,430,2017-04-22 10:21:47,http://bogan.info/donny,0.5416580512,43.245.221.102,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n19106,7,430,2017-02-26 23:20:05,http://erdman.net/lisette_williamson,0.8234078454,166.53.218.222,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n19107,7,430,2017-06-02 07:00:27,http://kirlin.org/estrella,0.1727153718,90.26.234.72,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n19108,7,430,2017-01-07 04:42:38,http://schaefer.net/effie,0.5371502123,35.10.232.7,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n19109,7,430,2017-03-22 15:38:18,http://rosenbaum.info/lynn,0.3909754397,16.130.176.189,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n19110,7,430,2017-04-23 18:56:41,http://kling.name/martine,0.7903912734,7.152.82.183,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n19111,7,431,2017-02-14 19:39:29,http://schroeder.co/leilani.kerluke,0.8551824863,21.170.196.238,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n19112,7,431,2017-02-16 16:08:25,http://wisozk.info/erwin.cain,0.7891024901,179.161.141.200,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n19113,7,431,2017-02-07 02:00:06,http://eichmann.biz/larry_koch,0.0454653556,244.85.8.181,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n19114,7,431,2017-01-08 10:12:16,http://stantondonnelly.info/francesco,0.4072434660,187.202.62.225,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n19115,7,431,2017-04-19 12:43:21,http://marvin.org/rodolfo_auer,0.3742028492,173.117.243.132,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n19116,7,431,2017-01-06 02:45:02,http://kohler.io/delbert,0.1397286866,78.33.207.215,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n19117,7,431,2017-01-26 21:21:10,http://cole.org/justine.cronin,0.6660359042,195.47.44.38,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n19118,7,431,2017-04-12 05:23:51,http://faheypadberg.info/makenna_bosco,0.2206883711,216.147.109.103,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n19119,7,431,2017-05-12 18:40:29,http://bayer.info/carmel.corwin,0.1586184195,238.37.127.172,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n19120,7,431,2017-04-18 01:25:25,http://casperreinger.name/marjory.rice,0.1803574113,236.18.182.166,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n19121,7,431,2017-05-14 08:01:54,http://carrollpouros.name/arnold_heaney,0.8323397234,150.254.220.44,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n19122,7,431,2017-03-17 22:40:22,http://kochmertz.org/eryn.roberts,0.1509183988,13.143.170.217,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n19123,7,431,2017-01-25 05:12:25,http://wehnercorwin.com/nathan.reichert,0.6921706314,170.252.195.140,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n19124,7,431,2017-03-04 13:35:17,http://carter.info/gaylord,0.5553901716,249.15.162.216,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n19125,7,431,2017-02-21 08:26:08,http://zulaufgerhold.co/arturo,0.6884513535,104.208.102.131,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n19126,7,431,2017-05-02 08:54:25,http://fritsch.co/talon.weinat,0.8485629707,28.88.38.188,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n19127,7,431,2017-03-23 14:21:56,http://hirthe.org/geovanni,0.5921038372,77.111.54.185,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n19128,7,431,2017-03-17 07:50:36,http://stoltenberg.com/leta,0.1019472945,183.211.12.150,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n19129,7,431,2017-04-01 06:27:58,http://wittingmacgyver.co/van,0.1563322790,21.86.212.245,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n19130,7,431,2017-03-12 10:16:26,http://auergutkowski.com/aliza.connelly,0.6832730403,181.172.13.246,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n19131,7,431,2017-03-04 18:41:21,http://schuster.org/neal.doyle,0.3587616591,234.30.239.20,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n19132,7,431,2017-01-18 14:15:58,http://huels.org/camden.mann,0.3409766861,236.110.202.22,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n19133,7,431,2017-04-28 16:42:38,http://spinka.biz/yasmin,0.4193828584,183.61.79.211,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n19134,7,431,2017-05-12 00:56:45,http://konopelskigorczany.info/jan.oconner,0.2039640904,122.100.171.41,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n19135,7,431,2017-02-06 05:56:26,http://simonis.org/devonte,0.4494304194,148.105.206.150,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n19136,7,431,2017-01-09 04:41:39,http://barrows.biz/glenda,0.8780483943,203.49.148.39,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": false}\"\n19137,7,431,2017-05-14 01:09:26,http://stehrbarrows.co/pinkie.greenholt,0.8539221794,138.12.234.163,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n19138,7,431,2017-03-27 23:39:05,http://fisherheller.name/randy,0.3733252827,111.152.218.206,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n19139,7,431,2017-05-16 06:27:16,http://denesik.biz/josefa_hayes,0.0607076716,120.195.12.141,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n19140,7,431,2017-05-15 18:38:32,http://daugherty.name/tia,0.8719554768,239.82.225.47,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n19141,7,431,2017-04-15 18:17:34,http://heel.com/niko_mitchell,0.1210792052,48.225.165.58,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n19142,7,431,2017-04-16 21:53:44,http://botsford.io/suzanne,0.2172621439,95.132.143.219,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n19143,7,431,2017-05-27 07:00:55,http://kleinwiza.info/dagmar,0.8043929651,44.99.57.15,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n19144,7,431,2017-03-27 03:13:50,http://abernathyabshire.info/rupert.bins,0.2154781606,149.117.86.3,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n19145,7,431,2017-04-05 10:03:14,http://dubuque.com/chanelle.wuckert,0.2815475409,90.252.233.46,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n19146,7,431,2017-01-27 12:45:35,http://cristlangworth.co/jordon.lemke,0.4841303465,254.217.68.238,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n19147,7,431,2017-06-02 20:45:51,http://weimann.io/tyson,0.6895748453,62.111.49.36,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n19148,7,431,2016-12-14 18:08:14,http://wunschziemann.org/ethan,0.5464468088,18.136.59.74,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n19149,7,431,2017-03-05 07:52:18,http://batz.co/luna_runte,0.7139212198,243.174.85.147,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n19150,7,431,2017-05-04 18:11:35,http://weimann.net/alba,0.4198401519,216.110.113.224,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": false}\"\n19151,7,431,2017-02-09 01:40:18,http://mosciskimante.name/jaeden,0.8418701244,134.201.148.244,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n19152,7,431,2017-02-24 06:02:34,http://weinatfranecki.org/reggie_feil,0.4882981539,243.178.113.38,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n19153,7,431,2017-03-24 05:07:15,http://schmittbeatty.info/rodrigo.schamberger,0.1819384088,14.95.77.225,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n19154,7,431,2016-12-26 02:31:49,http://brakusmorar.net/leonard.yundt,0.8622472947,14.192.103.252,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n19155,7,431,2017-01-03 18:32:32,http://kirlin.com/zola_greenfelder,0.6444492467,105.14.181.56,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n19156,7,431,2017-03-02 19:49:55,http://swift.name/johnathan,0.4055303989,234.177.175.30,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n19157,7,431,2017-02-06 00:13:53,http://moencartwright.co/roger,0.5322759850,191.75.33.200,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n19158,7,431,2017-02-16 01:43:49,http://zieme.org/jamal,0.8898468021,18.54.230.187,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n19159,7,431,2017-05-04 07:42:42,http://cremin.com/octavia,0.2456547642,56.205.61.131,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n19160,7,431,2017-05-23 03:45:36,http://reichertwolf.com/alan,0.7158791819,209.95.221.246,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": false}\"\n19161,7,431,2016-12-13 06:02:37,http://strackesipes.io/enoch,0.2268984988,79.3.41.169,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n19162,7,431,2017-02-24 03:15:43,http://fishermacejkovic.org/laila.rice,0.1532992624,126.174.121.242,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n19163,7,431,2016-12-20 23:44:52,http://wardeffertz.org/antonietta.ko,0.9754175816,24.184.195.38,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n19164,7,432,2017-01-30 20:47:55,http://casper.io/jeffrey_ritchie,0.4045322801,151.175.98.81,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n19165,7,432,2017-01-23 07:29:25,http://moriette.net/andy_bayer,0.8234106268,54.243.28.217,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n19166,7,432,2017-05-22 17:38:12,http://west.com/vaughn,0.9238606911,186.252.178.55,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n19167,7,432,2017-01-12 05:13:20,http://schusterkshlerin.org/jermaine.stamm,0.9727139968,12.249.14.170,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n19168,7,432,2017-03-17 09:56:54,http://friesenlittel.co/eveline,0.1955769125,40.185.203.158,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n19169,7,432,2016-12-20 07:09:07,http://jastwaelchi.io/natalia.smitham,0.9059808610,144.177.58.70,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n19170,7,432,2017-01-25 05:53:43,http://bartoletti.org/amy,0.2873068587,227.35.43.206,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n19171,7,432,2017-01-24 10:28:04,http://rolfson.co/lynn_leannon,0.9190223272,87.56.76.116,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n19172,7,432,2016-12-17 12:16:30,http://schaden.com/bart_goldner,0.1024777250,165.11.14.35,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n19173,7,432,2017-04-26 10:42:36,http://grant.co/chad,0.0725835013,232.11.61.122,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n19174,7,432,2017-02-10 18:52:50,http://swaniawskimitchell.com/lea,0.8392268363,161.5.210.53,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n19175,7,432,2017-01-12 01:11:18,http://vonrueden.biz/ruel,0.1289284546,202.150.49.65,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n19176,7,432,2016-12-13 14:14:41,http://cartermayer.info/leila_schulist,0.1383196191,36.33.107.148,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n19177,7,432,2017-01-05 09:05:06,http://adams.net/emmanuel,0.5296292770,86.33.15.225,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n19178,7,432,2017-04-06 17:04:41,http://hagenehanahan.org/frederique,0.3764823938,46.154.183.78,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n19179,7,432,2017-01-10 22:32:17,http://hyattrau.io/tamara_turcotte,0.4187300099,30.20.2.25,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n19180,7,432,2017-01-24 20:31:40,http://larkin.co/oliver.stamm,0.3061400619,200.238.157.227,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n19181,7,432,2017-02-20 16:49:01,http://nienow.net/lysanne_hills,0.0892539447,240.113.157.159,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n19182,7,432,2017-02-05 03:49:01,http://whitestrosin.net/kirsten,0.0027793514,25.229.24.82,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n19183,7,432,2017-03-31 15:39:24,http://hilllwest.name/saul,0.2284265638,22.104.105.87,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n19184,7,432,2017-06-06 09:04:03,http://ruelabbott.org/zakary,0.4771687864,117.29.195.213,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n19185,7,432,2017-03-25 23:32:50,http://schillerjaskolski.biz/reese_quitzon,0.7745158636,215.221.122.250,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n19186,7,432,2017-02-17 05:42:24,http://wunsch.co/maximillia,0.0332066575,56.53.221.163,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n19187,7,432,2017-03-24 00:01:14,http://dickiosinski.biz/mike.mccullough,0.0822002826,52.215.129.4,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n19188,7,432,2017-03-22 01:12:39,http://padberg.com/ophelia,0.9833664308,142.66.58.68,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n19189,7,433,2017-05-16 06:06:50,http://hansenkuphal.co/kaylie.gibson,0.9079437401,139.186.243.5,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n19190,7,433,2017-05-30 07:58:53,http://leannon.net/alanis.zemlak,0.3020117204,10.188.10.36,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n19191,7,433,2017-01-09 17:14:52,http://muller.org/diego.emard,0.3939639216,211.246.35.84,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n19192,7,433,2017-01-25 11:50:59,http://hoppe.biz/gretchen,0.6494100546,88.90.39.181,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n19193,7,433,2017-05-22 09:55:54,http://pagac.name/carlos,0.8090123365,142.78.210.46,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n19194,7,433,2017-05-09 23:19:43,http://weimann.net/clifton,0.5599798674,3.29.213.120,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n19195,7,433,2017-02-10 17:56:39,http://franecki.name/karolann.littel,0.8653383353,156.171.48.218,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n19196,7,433,2017-02-22 11:26:05,http://markshegmann.net/guiseppe,0.6138858656,102.196.130.26,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n19197,7,433,2016-12-29 23:45:00,http://eichmannhahn.io/nina_langosh,0.1479398096,115.221.197.95,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n19198,7,433,2017-04-09 19:57:30,http://trantowcrist.biz/elwyn_senger,0.2539247377,108.251.70.102,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n19199,7,433,2017-03-07 09:56:17,http://johnsongaylord.org/natasha.keebler,0.0107671904,202.209.52.131,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n19200,7,433,2017-02-26 17:59:48,http://ebertbrown.co/raegan_boyle,0.0942366675,251.92.161.64,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n19201,7,433,2016-12-13 22:03:51,http://simonis.net/kattie,0.4044438643,67.149.20.47,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n19202,7,433,2017-06-08 11:13:43,http://vandervorthilpert.name/granville,0.0606465062,126.13.191.168,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n19203,7,433,2017-01-12 15:31:29,http://crona.name/heidi_oconnell,0.9268730679,49.27.9.247,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n19204,7,433,2017-05-15 20:52:29,http://shields.com/katherine_gaylord,0.9944317649,67.37.16.120,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n19205,7,433,2017-02-26 02:56:50,http://volkman.net/dasia.kautzer,0.4696588505,123.135.4.43,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n19206,7,433,2017-05-20 07:27:41,http://darekeebler.info/shawna,0.7481839055,241.236.108.213,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n19207,7,433,2017-05-02 16:05:52,http://wintheiserbarton.org/zackery,0.6142528252,182.230.141.79,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n19208,7,433,2017-02-21 07:56:11,http://krajcikgerlach.com/shemar.jerde,0.1497658268,252.206.195.127,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n19209,7,433,2017-02-19 20:01:18,http://berge.net/robb.terry,0.8908552694,154.237.210.111,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n19210,7,433,2017-03-13 05:49:14,http://heidenreich.info/angelo.bahringer,0.4476078453,249.104.225.210,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n19211,7,433,2017-05-28 06:41:59,http://stokes.com/madisen,0.5929885231,137.100.239.10,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n19212,7,433,2017-04-22 20:35:22,http://becker.io/alaina,0.7865318921,175.155.239.177,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n19213,7,433,2016-12-24 23:25:57,http://bashirian.info/savanna,0.2357477522,244.168.87.81,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n19214,7,433,2017-05-19 04:55:36,http://schneider.org/vena_purdy,0.7972181944,205.36.198.39,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n19215,7,433,2017-04-13 00:32:47,http://king.info/josefina,0.9505073281,76.167.246.28,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n19216,7,433,2016-12-14 07:39:30,http://schaefer.net/alisha.casper,0.3392938867,197.132.77.129,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n19217,7,433,2017-05-14 21:26:56,http://rau.io/blaise.balistreri,0.6936777518,57.194.111.78,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n19218,7,433,2017-04-19 02:42:16,http://bogisichdare.net/amira_crooks,0.8582723616,206.15.163.222,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n19219,7,433,2017-05-31 15:16:14,http://kuvalis.net/lucas,0.1994338655,46.250.105.94,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n19220,7,433,2017-04-07 23:08:10,http://shanahan.biz/eve,0.6824431245,227.49.55.115,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n19221,7,433,2017-05-12 18:07:56,http://considine.io/ashly,0.0243650170,117.69.16.251,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n19222,7,433,2017-01-26 01:39:49,http://torphy.info/pierce_muller,0.4709275418,119.141.207.54,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n19223,7,433,2016-12-13 20:07:20,http://halvorson.org/wilfredo,0.9572022714,185.153.2.51,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n19224,7,434,2017-01-06 15:19:39,http://okonkutch.com/josiane,0.8565884682,104.218.145.59,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n19225,7,434,2017-01-28 08:55:34,http://heathcotelangosh.net/vance_bogisich,0.5571656300,160.8.35.114,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n19226,7,434,2017-05-01 10:56:36,http://walker.biz/autumn,0.3868769005,108.123.110.175,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n19227,7,434,2017-06-11 19:03:37,http://watsicahermiston.name/faustino,0.5379977392,249.14.146.91,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n19228,7,434,2017-03-28 14:35:11,http://carrollwatsica.org/newell_wilkinson,0.4410697945,154.221.11.142,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n19229,7,434,2017-01-20 00:00:05,http://koch.io/vincenzo,0.8276739262,174.124.177.98,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n19230,7,434,2017-05-14 13:55:11,http://schowalterjacobson.com/orlo.mitchell,0.6641366790,57.44.73.50,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n19231,7,434,2017-04-05 20:47:27,http://ruel.biz/sanford_mcclure,0.9545897655,195.51.198.33,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n19232,7,434,2017-02-10 07:15:32,http://reingergoldner.info/rodger,0.3765338583,29.160.222.107,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n19233,7,434,2016-12-27 07:21:06,http://sipes.io/shanie,0.4827338070,102.3.171.29,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": false}\"\n19234,7,434,2017-04-12 10:22:47,http://kuhlman.co/lavon_balistreri,0.8556530955,214.26.230.77,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n19235,7,434,2017-03-11 07:28:45,http://schuppe.net/elody,0.9663019254,108.195.195.142,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n19236,7,434,2017-06-05 14:24:32,http://gutkowski.co/terrance,0.6239644716,245.236.163.22,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n19237,7,434,2017-06-07 13:53:44,http://collins.io/arjun_reichel,0.1510900045,68.220.209.166,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n19238,7,434,2017-04-30 07:24:03,http://grant.io/morgan.harber,0.1592700078,103.181.5.42,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n19239,7,434,2017-04-28 05:50:05,http://homenick.biz/edmond_wiegand,0.8797599023,121.187.218.88,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n19240,7,434,2017-05-24 21:40:06,http://farrell.io/dorian,0.3008895014,187.89.30.174,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": false}\"\n19241,7,434,2017-04-18 09:01:58,http://carroll.io/lina.hansen,0.9279847140,4.128.43.14,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n19242,7,434,2017-06-06 01:08:03,http://green.com/emmalee,0.6681584703,58.106.163.58,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n19243,7,434,2017-03-11 02:28:06,http://stroman.biz/consuelo.douglas,0.1660971930,57.195.151.187,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n19244,7,434,2017-05-20 05:38:23,http://sauermaggio.info/florian_hirthe,0.2260282809,176.250.163.25,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n19245,7,434,2017-03-11 01:49:13,http://corkery.name/lloyd_grimes,0.5174716550,4.210.194.174,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n19246,7,434,2017-04-23 06:11:07,http://langosh.net/cathrine.streich,0.8375219316,149.16.198.190,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n19247,7,434,2017-03-12 18:42:10,http://dubuqueshields.net/sadie_bosco,0.6820788837,39.236.5.194,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n19248,7,434,2017-06-09 03:09:23,http://cruickshank.org/joannie.nitzsche,0.6903846519,155.131.239.220,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n19249,7,434,2017-04-08 06:53:33,http://prosacco.com/pedro,0.5645434301,122.32.123.143,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n19250,7,434,2017-02-10 05:40:34,http://okonhodkiewicz.org/quinton,0.1830904530,122.123.22.224,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n19251,7,434,2017-05-06 11:58:17,http://heaney.io/ceasar,0.0704778176,71.93.41.213,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n19252,7,434,2017-04-23 07:59:14,http://hodkiewicz.com/abner.deckow,0.7905017055,172.66.222.39,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n19253,7,434,2017-01-22 12:27:21,http://sipes.info/joey.carroll,0.5478566736,168.28.127.154,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n19254,7,434,2017-03-23 08:18:18,http://lockmankovacek.name/kara_rosenbaum,0.7226226383,155.224.15.45,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n19255,7,434,2017-05-10 22:46:27,http://schaefer.name/adela,0.1380021494,196.75.245.58,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n19256,7,434,2017-03-07 03:02:48,http://kilbackreichert.biz/bonnie.roberts,0.8039962552,175.35.155.227,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n19257,7,434,2017-04-27 13:00:25,http://walter.net/nick,0.6122206709,21.163.189.20,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n19258,7,434,2017-02-21 14:26:44,http://dare.com/abner.stokes,0.1158551086,55.102.4.64,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n19259,7,434,2017-05-03 10:39:41,http://hamillspinka.net/obie,0.5449599243,53.146.134.200,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n19260,7,434,2016-12-30 17:14:46,http://krajcik.io/adella_kuhn,0.9495532738,221.109.94.192,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n19261,7,434,2017-03-30 04:37:53,http://hagenes.net/keanu_kirlin,0.7554506255,249.47.242.191,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n19262,7,434,2017-04-07 09:28:57,http://mccullough.net/antonia.macgyver,0.1738503457,253.150.207.39,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n19263,7,434,2017-01-31 01:17:47,http://sauerklocko.com/queen_schaden,0.5719006891,120.171.34.47,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n19264,7,435,2017-04-09 14:29:21,http://walter.info/reese_thompson,0.4965963551,49.151.33.126,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n19265,7,435,2017-01-25 08:06:25,http://macejkovicwilliamson.co/dallas,0.4803641462,112.165.219.131,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n19266,7,435,2017-03-27 19:37:43,http://moen.io/mustafa.cummerata,0.9354750196,165.77.205.33,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n19267,7,435,2017-05-04 14:16:05,http://murazik.io/hellen_abernathy,0.5692468821,191.185.170.242,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n19268,7,435,2017-05-17 15:07:05,http://cremin.co/jade,0.7448666428,180.36.64.165,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n19269,7,435,2017-01-09 16:26:14,http://cartwright.io/helga_kunde,0.6508672991,186.112.89.94,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n19270,7,435,2016-12-27 04:04:23,http://heidenreich.info/kris,0.6726196979,227.38.186.16,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n19271,7,435,2017-04-14 01:57:13,http://gusikowski.name/lizzie,0.5830112063,214.197.175.198,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n19272,7,435,2017-03-01 16:14:55,http://crist.net/angeline.wintheiser,0.1759480298,3.254.189.172,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n19273,7,435,2016-12-30 17:54:25,http://harveytremblay.info/brandyn,0.9082858454,165.252.23.222,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n19274,7,435,2017-06-04 18:35:43,http://legrosaltenwerth.net/cedrick,0.0909172437,22.67.209.65,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n19275,7,435,2017-04-11 17:59:58,http://heller.biz/maribel_mayer,0.1961691658,95.29.206.232,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n19276,7,435,2017-03-04 06:42:53,http://franecki.com/columbus,0.4550987455,50.102.213.67,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n19277,7,435,2017-05-07 18:20:25,http://ratke.com/jared,0.3480881229,86.94.241.184,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n19278,7,435,2017-05-13 18:18:09,http://beahanglover.net/natalia.baumbach,0.3675836265,202.29.190.21,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n19279,7,435,2017-04-21 08:51:48,http://barrows.info/tad,0.4877750758,253.46.166.250,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n19280,7,435,2017-03-02 06:10:50,http://conroy.io/cedrick_turcotte,0.3670381089,159.221.163.231,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n19281,7,435,2017-02-14 11:22:57,http://bergstrom.name/jordyn,0.2270984078,116.109.192.29,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n19282,7,435,2017-05-17 03:39:27,http://ratkelesch.co/leatha,0.2907802434,222.15.9.50,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n19283,7,435,2017-02-19 18:50:36,http://kulas.biz/colleen_graham,0.6693754259,240.76.96.85,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n19284,7,435,2016-12-29 08:23:29,http://denesik.net/filiberto.glover,0.4163556521,197.245.144.138,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n19285,7,435,2017-05-30 12:11:07,http://rau.co/laila,0.1930928099,54.153.171.110,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n19286,7,435,2016-12-25 11:25:02,http://mosciski.io/jensen.schuster,0.5098755882,157.141.85.23,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n19287,7,435,2017-01-17 02:11:52,http://lynchschulist.info/enoch,0.4748075190,219.35.148.101,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n19288,7,435,2017-02-22 09:27:23,http://hammes.com/adrien_white,0.6891645214,2.229.177.214,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n19289,7,435,2017-01-12 21:09:50,http://swaniawskimarquardt.net/stanley_beier,0.5144745311,7.42.60.208,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": false}\"\n19290,7,435,2017-04-12 09:50:19,http://jerde.net/sabrina.schroeder,0.9677031988,166.131.210.98,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n19291,7,435,2017-02-20 01:56:27,http://fritschzemlak.net/stone,0.9140191148,210.45.92.156,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n19292,7,435,2017-04-21 06:59:22,http://marquardt.org/nora.pfannerstill,0.6703550596,163.161.44.146,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n19293,7,435,2017-01-23 18:47:17,http://ko.org/quinten,0.0727213803,37.116.231.221,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n19294,7,435,2017-03-01 19:16:25,http://predovic.com/prince_marquardt,0.0773468436,227.230.42.124,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n19295,7,435,2017-02-02 06:16:05,http://mcculloughmurray.name/paige_collins,0.0874399151,156.26.183.241,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n19296,7,435,2017-01-20 16:47:48,http://ryan.com/rosalyn_bartoletti,0.9231210116,198.230.213.115,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n19297,7,435,2017-04-14 13:19:03,http://aufderhar.io/madalyn,0.9360343407,241.212.190.57,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n19298,7,435,2017-04-18 01:33:08,http://block.co/marilyne.wisoky,0.3282297005,239.36.105.170,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n19299,7,435,2017-05-06 04:52:24,http://little.info/pearline.bradtke,0.1072166889,112.96.95.115,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n19300,7,435,2017-03-29 06:31:37,http://aufderhar.net/emmanuelle,0.9191894886,58.58.220.24,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n19301,7,435,2017-05-27 08:48:10,http://cartwrightratke.name/sadie_haag,0.9960533779,7.145.18.113,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n19302,7,435,2016-12-14 03:05:56,http://mcdermott.name/clinton,0.5608125955,235.195.250.23,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n19303,7,436,2017-01-07 13:37:00,http://ruecker.name/orpha,0.6990218684,106.246.64.20,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n19304,7,436,2017-02-02 02:40:36,http://purdy.com/jimmie.herzog,0.6765526842,45.186.90.149,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n19305,7,436,2017-05-23 18:03:55,http://erdman.net/willie,0.0955192204,113.207.28.27,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n19306,7,436,2017-06-04 12:58:56,http://bogan.co/leonora.stehr,0.6013070568,105.177.143.110,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n19307,7,436,2017-01-22 09:23:19,http://brown.info/elmira.reilly,0.2431304430,174.3.42.2,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n19308,7,436,2016-12-14 15:53:43,http://walter.net/bernard,0.2286594614,24.224.171.177,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n19309,7,436,2016-12-19 17:28:42,http://wolfdibbert.name/maribel_daniel,0.7459146876,91.179.101.155,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n19310,7,436,2017-03-08 23:44:31,http://rennerfarrell.net/brain,0.0350114650,110.223.82.214,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n19311,7,436,2017-01-07 23:38:59,http://koelpin.co/marshall,0.3575798078,80.39.165.155,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n19312,7,436,2017-05-07 11:56:00,http://stanton.info/dashawn,0.0502439885,237.229.86.79,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n19313,7,436,2017-02-27 14:25:59,http://hills.org/graciela_schulist,0.4561248728,218.229.64.61,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n19314,7,436,2017-06-11 06:20:57,http://wunsch.org/hertha_keeling,0.5347957004,130.177.21.137,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n19315,7,436,2017-01-02 14:03:02,http://homenick.biz/crystel.schiller,0.8087231529,128.224.117.20,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n19316,7,436,2017-02-28 20:22:33,http://yost.name/golden_marvin,0.5151054377,123.100.87.22,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n19317,7,436,2017-04-02 15:23:31,http://bradtke.name/valentina,0.8871129816,203.146.109.99,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n19318,7,436,2017-01-25 14:58:24,http://keebler.info/floie_daugherty,0.7808908171,10.220.76.22,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n19319,7,436,2017-06-05 19:47:42,http://hansen.biz/libby.reinger,0.6786396099,59.19.248.161,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n19320,7,436,2017-04-29 16:43:23,http://mann.com/bernita.bahringer,0.1913744451,209.51.250.156,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n19321,7,436,2017-02-26 16:23:38,http://carroll.com/alanna.glover,0.1337063317,127.111.194.47,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n19322,7,436,2017-03-15 10:26:52,http://goyette.info/collin,0.5250296423,246.82.34.151,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n19323,7,436,2017-04-09 13:54:20,http://jenkinsmraz.com/jovany_collier,0.6674125454,53.179.110.42,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n19324,7,436,2017-05-01 12:19:44,http://wisokyhackett.org/emilio,0.3259684569,58.251.224.145,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n19325,7,436,2017-05-15 04:03:03,http://keeblerkuphal.io/lane_bogan,0.8175580611,211.33.161.109,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n19326,7,436,2017-04-06 06:00:32,http://grimes.info/loyal_larson,0.7758494330,105.230.241.55,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n19327,7,436,2017-05-10 17:12:00,http://lueilwitz.info/brenda_jakubowski,0.6435306764,165.207.32.203,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n19328,7,436,2016-12-14 20:20:09,http://metz.org/celestino,0.6914839289,235.12.17.58,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n19329,7,436,2017-05-25 23:00:46,http://mann.biz/david,0.6570238409,174.19.11.24,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n19330,7,436,2017-03-13 17:02:53,http://hodkiewicz.info/ruthe_beer,0.8292433085,145.216.108.206,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n19331,7,436,2017-05-24 02:21:59,http://carroll.com/kenny.beer,0.4352508641,210.246.8.120,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n19332,7,436,2017-01-05 20:39:10,http://jones.biz/johann.champlin,0.5995534289,195.229.246.38,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n19333,7,436,2017-02-13 19:55:21,http://darehermann.io/columbus,0.2966273569,245.232.128.179,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n19334,7,436,2017-02-22 10:42:05,http://shields.info/isadore,0.3385859833,230.88.120.176,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n19335,7,436,2017-01-04 08:11:36,http://gerlach.com/meagan.lowe,0.2321268989,168.184.142.149,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n19336,7,436,2017-01-03 09:07:17,http://kiehngleichner.co/dane,0.8480267611,220.130.14.227,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n19337,7,436,2017-03-09 18:42:18,http://yostmacgyver.co/elfrieda_kertzmann,0.5499228091,166.5.53.87,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n19338,7,436,2016-12-13 23:32:15,http://bartoletti.org/ashlee_moriette,0.3675226286,113.132.46.237,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n19339,7,436,2017-06-02 01:57:51,http://becker.co/clifford,0.8985787049,105.236.159.2,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n19340,7,436,2016-12-17 09:06:24,http://vonrueden.name/urban,0.3226290555,21.183.127.105,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n19341,7,436,2017-06-03 16:46:47,http://wymanmccullough.org/gilbert,0.0879206027,95.175.124.218,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n19342,7,436,2017-02-23 22:40:18,http://uptonhalvorson.co/edwin.beer,0.7628928850,35.231.24.226,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n19343,7,436,2017-01-09 06:54:51,http://schmidtwisozk.co/toney,0.5852921166,187.165.210.46,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n19344,7,436,2017-02-11 22:30:18,http://rath.io/nora.koch,0.0858814639,105.183.154.154,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n19345,7,436,2017-04-06 18:10:19,http://trantow.co/darren.steuber,0.8329950757,19.160.116.42,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n19346,7,436,2017-05-29 22:09:27,http://murrayjerde.co/jasmin_stracke,0.3303443960,105.84.187.101,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n19347,7,436,2016-12-21 16:45:41,http://pricestokes.net/gerald_rice,0.5678984913,213.134.254.166,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n19348,7,436,2017-01-13 01:03:52,http://rempelthompson.org/wiley,0.6860213068,60.106.41.233,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n19349,7,436,2017-05-10 12:58:30,http://gaylord.net/margarette,0.0951653473,69.85.43.183,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n19350,7,436,2016-12-30 07:11:27,http://howellrolfson.com/willis_rippin,0.5737650881,27.225.196.109,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": true}\"\n19351,7,436,2017-06-13 11:04:19,http://veum.co/karine_mohr,0.0831646532,192.189.199.144,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n19352,7,436,2017-05-05 11:22:30,http://rathwilkinson.co/miracle,0.5073178608,217.75.20.235,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n19353,7,436,2017-04-01 00:26:36,http://schroederpouros.info/novella,0.6466877717,42.49.31.165,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n19354,7,436,2017-01-01 14:54:46,http://nitzsche.net/madelyn,0.2177615274,141.16.106.243,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n19355,7,436,2017-01-04 01:29:07,http://emmerich.biz/melvin,0.5584486911,203.164.26.233,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n19356,7,436,2017-05-01 13:27:03,http://connellytreutel.co/royce.orn,0.7556217996,59.10.183.83,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n19357,7,436,2017-05-02 07:24:15,http://quigley.io/olin.gislason,0.5077153490,48.59.88.60,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n19358,7,436,2017-02-04 14:53:16,http://larsonortiz.com/camren,0.1847943662,236.74.52.230,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n19359,7,436,2017-03-20 21:42:52,http://dibbertmitchell.io/roderick,0.2587050212,194.231.3.133,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n19360,7,436,2017-03-22 23:43:18,http://pacochaharris.net/moriah,0.9420208811,33.77.212.197,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n19361,7,437,2017-04-28 03:38:49,http://marquardt.info/otto,0.2936159126,3.137.121.179,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n19362,7,437,2017-05-25 16:27:17,http://kuhlman.com/jose_goyette,0.0048084671,88.173.33.78,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n19363,7,437,2017-05-25 23:02:26,http://rau.biz/arlene,0.5158672359,115.22.191.238,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n19364,7,437,2017-03-12 09:52:22,http://strosinwatsica.co/modesto,0.8330214512,103.119.38.227,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": false}\"\n19365,7,437,2017-04-27 20:57:21,http://osinski.name/kyra_morar,0.2583135208,235.33.238.110,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n19366,7,437,2017-02-25 15:34:28,http://botsfordborer.com/mattie,0.1965058013,211.221.196.131,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n19367,7,437,2016-12-20 19:21:12,http://morar.com/kevon,0.0740035610,104.80.156.203,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": true}\"\n19368,7,437,2017-02-17 00:13:55,http://sanfordmonahan.biz/nyah,0.8815004721,94.116.14.178,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n19369,7,437,2017-04-15 20:32:59,http://denesikmoen.biz/kathleen.doyle,0.9519569775,19.161.10.119,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n19370,7,437,2017-01-19 10:33:41,http://fisherspencer.biz/meggie,0.9657555277,221.116.235.82,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n19371,7,437,2017-02-19 23:44:56,http://simonisroob.net/adolfo_connelly,0.1359376379,231.90.33.239,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n19372,7,437,2017-06-11 15:17:29,http://satterfieldmueller.info/coty,0.9154371281,240.158.206.127,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n19373,7,437,2017-05-04 23:54:46,http://schowalter.biz/monroe,0.5181530278,217.248.57.88,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n19374,7,437,2017-04-05 14:33:17,http://mohr.info/eula,0.2477125017,151.237.239.182,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n19375,7,437,2017-06-02 22:26:40,http://cummerata.info/reva.simonis,0.9880516776,10.120.80.101,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n19376,7,437,2017-03-30 15:47:14,http://keeling.name/daryl,0.8694148970,174.223.128.77,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n19377,7,437,2017-03-12 05:16:34,http://oharawyman.io/oscar,0.5159772360,145.206.124.176,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n19378,7,437,2017-05-10 08:48:45,http://beahan.info/doyle_jones,0.3607761390,5.153.111.189,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n19379,7,437,2017-02-01 08:30:26,http://hane.co/ruby_spinka,0.3293592382,236.27.22.16,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": true}\"\n19380,7,437,2017-05-27 06:53:02,http://lubowitznader.com/gielle_hirthe,0.0313268506,229.149.138.194,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": false}\"\n19381,7,437,2017-04-17 07:42:22,http://robertsfisher.name/ottilie,0.9291701201,199.162.28.63,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n19382,7,437,2017-01-27 06:43:02,http://koelpinbosco.biz/nova_grimes,0.5238402803,70.56.135.157,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n19383,7,437,2017-04-22 09:14:30,http://boscoklocko.com/roxane_mayert,0.4152496551,9.26.149.222,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n19384,7,437,2017-03-26 17:49:45,http://langworthrunolfsdottir.net/ben.leffler,0.3558803294,16.170.218.210,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n19385,7,437,2017-05-03 02:12:53,http://jacobi.info/rhea_predovic,0.8209920123,236.94.2.220,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n19386,7,437,2017-01-14 20:24:50,http://lesch.biz/jaylen,0.2552621670,179.215.126.81,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n19387,7,437,2017-06-09 10:22:34,http://okon.co/rudolph,0.2993179172,74.16.207.43,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n19388,7,437,2017-01-07 19:16:24,http://berge.info/reginald_hagenes,0.3487480234,18.19.143.129,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n19389,7,437,2017-02-07 02:08:07,http://beckerkreiger.net/karley,0.3098333871,29.6.112.247,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n19390,7,437,2017-01-19 17:32:40,http://blanda.io/daren_baumbach,0.0820974895,20.149.196.234,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n19391,7,437,2017-05-20 03:11:45,http://hamill.biz/alda.kreiger,0.7492107205,239.116.124.176,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n19392,7,437,2016-12-23 07:02:24,http://batzhettinger.net/grady.wiza,0.8943490896,59.244.27.48,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n19393,7,437,2017-04-19 22:34:57,http://heller.org/ali_schumm,0.2030994990,194.160.23.87,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n19394,7,437,2017-03-23 20:13:54,http://moen.info/consuelo.mcclure,0.4590498233,170.151.7.136,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n19395,7,437,2017-04-15 18:17:17,http://grady.com/randy,0.4593683755,250.136.22.216,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n19396,7,437,2017-03-07 21:40:01,http://bergstrom.org/zaria,0.1565026894,196.232.118.81,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n19397,7,437,2017-01-20 15:42:52,http://lynch.org/sheldon_king,0.3833346603,102.75.217.148,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": false}\"\n19398,7,437,2017-03-11 21:59:19,http://dietrichdare.info/georgette,0.1162860017,246.210.228.108,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n19399,7,437,2017-01-19 23:14:18,http://muellerraynor.net/stone_rowe,0.2167310549,236.68.239.139,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n19400,7,437,2017-05-19 02:39:18,http://aufderharschultz.biz/scot_hilpert,0.5149431669,248.79.204.144,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n19401,7,437,2017-02-07 10:00:17,http://kiehn.org/kamren.kutch,0.9408925295,229.197.193.10,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n19402,7,437,2017-02-27 10:21:34,http://bernhardorn.org/freeman.lesch,0.3392729540,37.175.171.88,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n19403,7,437,2017-03-19 10:59:04,http://huels.io/buford_macejkovic,0.9544016454,68.213.234.215,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": false}\"\n19404,7,437,2017-04-17 11:34:06,http://lindgrenstrosin.io/lavina.osinski,0.8976437013,236.25.188.14,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n19405,7,437,2017-05-04 00:44:27,http://tremblay.name/blaze_lehner,0.2987974260,58.99.218.15,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n19406,7,437,2017-05-27 19:05:13,http://schmeler.biz/maryse,0.6587963510,123.123.196.223,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n19407,7,437,2017-04-15 07:45:12,http://gerhold.com/antwan,0.1041400434,152.149.128.241,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n19408,7,437,2017-04-23 18:57:22,http://gutkowski.info/estella,0.8929698010,79.181.213.216,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n19409,7,437,2017-03-13 20:37:07,http://hand.org/lizeth,0.6058382266,178.163.71.95,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n19410,7,437,2017-04-25 18:32:01,http://lindyost.co/lelah,0.2661820340,153.8.39.88,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n19411,7,437,2017-03-18 23:42:26,http://trantow.net/ruthe_jast,0.0563644436,186.53.199.19,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n19412,7,437,2017-03-09 06:46:50,http://gleasonzulauf.info/devan.daniel,0.7163560516,32.227.27.66,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n19413,7,437,2017-01-25 01:12:05,http://crooksherman.com/chyna_oreilly,0.0627577167,72.204.192.79,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n19414,7,437,2017-01-31 06:30:57,http://ortizhyatt.com/kiarra.blanda,0.3858808407,208.145.13.87,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n19415,7,437,2017-04-28 00:35:52,http://prosaccocartwright.org/lewis,0.3880068274,2.170.99.179,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n19416,7,437,2017-05-06 18:29:25,http://quitzon.net/linda.collins,0.9945611200,198.121.127.176,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n19417,7,437,2017-01-21 05:11:39,http://bashirianschuppe.info/august,0.5354954041,90.7.94.184,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n19418,7,437,2017-05-17 06:26:05,http://blick.info/salma_windler,0.1236777061,154.145.53.191,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n19419,7,437,2017-03-16 05:48:46,http://denesik.com/samantha.ohara,0.3660451833,111.196.216.154,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n19420,7,437,2017-02-26 21:13:03,http://bernhard.co/chauncey_jenkins,0.0891780007,116.36.95.19,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n19421,7,437,2017-03-11 10:34:49,http://dickinsonrobel.biz/maximillia.harvey,0.4873245084,43.117.12.97,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n19422,7,437,2017-03-18 15:48:30,http://ondrickastracke.biz/sven.marvin,0.7314159649,245.77.116.195,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": false}\"\n19423,7,437,2017-05-13 13:22:15,http://connmoen.biz/edna.konopelski,0.0478780947,93.143.158.41,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n19424,7,437,2017-05-28 01:07:00,http://johns.org/marquise.paucek,0.8259093264,208.52.50.79,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n19425,7,437,2017-05-02 17:55:17,http://heelblanda.io/hanna_schimmel,0.9426320087,183.54.203.219,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n19426,7,437,2017-03-27 18:16:01,http://trompmacejkovic.com/verla,0.3163437541,13.92.48.158,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n19427,7,437,2016-12-23 02:58:12,http://wiegand.name/justyn,0.0878461472,153.40.11.14,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n19428,7,438,2017-04-21 02:13:15,http://beer.org/elvis_dach,0.3264541887,195.169.57.38,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n19429,7,438,2017-04-23 21:54:42,http://quitzon.info/mariana_kuhlman,0.3543945987,67.144.230.247,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n19430,7,438,2017-02-18 01:01:16,http://wehner.name/otha.witting,0.8178275395,210.187.92.59,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n19431,7,438,2017-03-01 23:14:35,http://jones.biz/guie.kulas,0.7979304972,158.230.239.108,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n19432,7,438,2017-04-11 12:30:54,http://treutel.io/nikolas,0.1631620813,201.121.119.140,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n19433,7,438,2017-04-16 10:34:15,http://johns.org/mohammed_nienow,0.3031753247,122.16.230.190,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n19434,7,438,2017-03-19 10:09:42,http://yost.biz/allie.williamson,0.1287658352,85.99.120.205,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n19435,7,438,2017-06-05 23:41:13,http://mertz.co/karina,0.9093141757,144.167.76.177,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n19436,7,438,2017-04-10 11:22:42,http://renner.net/tamia,0.8508010910,87.45.34.144,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n19437,7,438,2017-02-10 05:47:20,http://rempelhackett.biz/calista,0.7166554685,115.103.169.180,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n19438,7,438,2017-06-08 22:11:30,http://turner.name/kristina.ledner,0.8299925150,143.42.238.212,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n19439,7,438,2016-12-24 19:34:16,http://heidenreichhermann.com/kendrick,0.3334206266,164.110.19.197,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n19440,7,438,2017-01-19 04:58:20,http://jenkintehr.io/ulices,0.0355354851,61.167.7.92,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n19441,7,438,2016-12-19 01:06:37,http://durgan.org/verona,0.0684624037,213.137.84.21,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n19442,7,438,2017-05-10 03:44:31,http://bailey.biz/susie,0.6365195473,210.182.184.248,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n19443,7,438,2017-05-22 19:34:05,http://labadie.net/louie_ferry,0.5849884927,33.172.185.153,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n19444,7,438,2017-03-26 12:32:36,http://reichert.com/royal.terry,0.6990075739,34.137.24.160,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n19445,7,438,2016-12-23 08:35:52,http://moriettehalvorson.co/vincent_hammes,0.2804372099,3.88.14.95,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n19446,7,438,2017-04-05 11:34:23,http://towne.biz/kayden_becker,0.6520673948,56.193.141.120,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n19447,7,438,2017-01-02 07:18:09,http://halvorsonlarson.net/alexandrine_jenkins,0.2086088486,113.210.153.54,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n19448,7,438,2017-04-14 17:00:35,http://mills.com/newell,0.2671485761,129.233.173.157,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n19449,7,438,2017-01-29 13:12:58,http://beierjakubowski.io/wilhelm,0.1917829265,190.134.125.222,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n19450,7,438,2017-04-13 04:52:12,http://gislasonward.info/maya_dietrich,0.5289212538,248.133.251.170,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n19451,7,438,2017-03-20 21:27:56,http://tremblay.io/kenton_little,0.7951791715,79.183.24.179,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n19452,7,438,2017-01-05 12:26:39,http://hilpert.com/john.abernathy,0.3387173144,152.188.231.238,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n19453,7,438,2017-04-10 19:32:52,http://boscoritchie.io/heaven_schumm,0.5495527092,133.123.63.30,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n19454,7,438,2017-03-28 02:16:18,http://vonrueden.co/hulda_paucek,0.9848759368,147.60.6.190,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n19455,7,438,2016-12-26 15:31:45,http://zboncak.biz/bryce,0.7726250731,232.36.196.185,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n19456,7,438,2017-02-07 03:38:09,http://little.com/adrien,0.9466538827,227.30.237.16,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n19457,7,438,2017-06-05 21:42:45,http://hermistonmitchell.biz/marcia_rutherford,0.0477819322,111.6.158.81,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n19458,7,438,2017-04-10 12:49:53,http://kihn.info/arvilla,0.6508691408,228.167.21.64,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n19459,7,438,2017-05-22 00:14:21,http://kling.org/gloria,0.2595620876,23.208.86.35,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n19460,7,438,2017-04-16 00:40:29,http://hagenes.info/stacey,0.8504740166,238.114.227.232,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n19461,7,438,2016-12-17 22:24:34,http://jacobson.org/harvey,0.7013045956,140.141.117.141,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n19462,7,438,2017-05-29 22:13:51,http://reichel.co/marley_schaefer,0.2234932896,20.109.169.176,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n19463,7,438,2017-02-08 22:42:50,http://schaden.biz/lewis_toy,0.1529790218,67.170.158.246,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n19464,7,438,2017-04-28 10:41:54,http://borerkovacek.com/kaley,0.7028910688,205.2.60.27,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n19465,7,438,2017-05-12 22:38:14,http://lowe.info/agustina,0.6411421695,94.106.206.82,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n19466,7,438,2017-04-10 09:51:02,http://gottlieb.com/margarette.koelpin,0.1392908571,111.58.68.156,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n19467,7,438,2017-01-27 08:44:34,http://mitchell.name/mariano,0.2423815571,159.22.44.248,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n19468,7,438,2017-04-10 00:28:13,http://hilll.io/myrna,0.1543903365,44.105.205.181,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n19469,7,438,2016-12-26 11:17:29,http://franecki.name/jude_boyle,0.3081476820,9.59.130.221,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n19470,7,438,2017-03-25 20:44:12,http://grady.co/freddie,0.3476775189,177.246.218.15,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n19471,7,438,2017-01-17 20:59:58,http://baumbach.io/daphne,0.0605619923,184.82.42.9,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": false}\"\n19472,7,438,2017-03-28 21:05:20,http://altenwerth.biz/collin,0.8071972993,146.176.107.202,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n19473,7,438,2017-03-27 07:05:47,http://corwin.name/oran,0.6636772607,129.88.65.68,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n19474,7,439,2017-05-24 13:45:42,http://kuphalmarvin.name/alexa_okuneva,0.5679580252,23.81.111.180,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n19475,7,439,2017-02-26 11:45:14,http://willms.biz/kay_marquardt,0.3998110771,146.196.103.236,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n19476,7,439,2017-06-05 18:31:20,http://pfeffer.co/darion.dickinson,0.8961477257,25.109.199.231,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n19477,7,439,2017-01-08 13:41:02,http://ullrichreynolds.co/dawn,0.4272436273,130.41.90.101,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n19478,7,439,2017-03-09 14:34:24,http://jerde.biz/lolita.labadie,0.8701679631,53.27.102.91,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n19479,7,439,2017-02-24 05:49:23,http://beatty.info/cade,0.0997826696,217.166.61.125,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n19480,7,439,2017-06-04 07:16:02,http://nitzsche.net/gianni_kuhn,0.7755155486,15.93.149.226,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n19481,7,439,2016-12-22 07:34:41,http://heel.com/amiya_waters,0.7583893304,12.148.20.129,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n19482,7,439,2017-05-12 07:51:44,http://hirthe.org/donnie.rohan,0.5691699678,22.116.17.178,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n19483,7,439,2017-03-05 08:24:11,http://jenkins.co/cole,0.1509034502,36.82.59.226,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n19484,7,439,2017-02-28 03:23:15,http://schowalter.io/queenie,0.2115958270,141.253.205.98,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n19485,7,439,2017-03-02 20:57:31,http://ebertoberbrunner.info/jarrod,0.5551900192,90.119.137.188,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n19486,7,439,2017-04-22 01:13:41,http://hoeger.name/asha_shanahan,0.5242020484,8.7.8.68,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n19487,7,439,2017-04-05 12:17:17,http://rowe.info/brandi.oberbrunner,0.5976215681,162.133.162.110,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n19488,7,439,2017-04-15 00:41:48,http://jakubowskiturcotte.org/ethan.dubuque,0.2123093733,150.182.220.126,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n19489,7,439,2017-01-06 05:57:21,http://thompsonfeest.org/jaylon,0.1917592019,144.61.128.186,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n19490,7,439,2017-03-04 11:52:06,http://smitham.com/elvie,0.8640143270,170.225.215.54,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n19491,7,439,2017-04-02 21:35:22,http://herzoglockman.name/cordell,0.5418249420,34.183.102.241,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n19492,7,439,2016-12-26 22:11:36,http://cummerata.name/madelynn.lind,0.5553095274,238.141.246.78,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n19493,7,439,2017-03-03 14:46:57,http://cummeratalemke.name/marquis_kerluke,0.9961390799,40.111.208.104,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n19494,7,439,2016-12-20 16:04:08,http://durgan.co/alfredo.sauer,0.2017059918,205.150.235.68,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n19495,7,439,2017-05-03 16:58:41,http://prohaska.net/jerrod,0.0982831331,147.157.179.226,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n19496,7,439,2017-04-21 07:25:26,http://oberbrunnernikolaus.info/reba_romaguera,0.9132871737,119.52.63.38,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": true}\"\n19497,7,439,2017-06-05 19:19:45,http://cainweinat.org/khalid.koepp,0.0617269845,10.60.19.232,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n19498,7,439,2017-04-08 14:24:06,http://von.biz/johann_mohr,0.8836142084,119.224.82.80,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n19499,7,439,2017-01-12 12:24:14,http://macgyver.biz/elaina_fadel,0.6792642160,249.196.15.64,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n19500,7,439,2017-05-24 01:21:31,http://gibson.org/janae,0.4939656473,94.14.118.80,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n19501,7,439,2017-04-07 05:50:27,http://stracke.name/reymundo.doyle,0.4581285890,15.62.132.6,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n19502,7,439,2017-02-17 15:33:42,http://cruickshank.org/jeica,0.8268612634,24.232.113.112,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n19503,7,439,2017-02-15 17:34:55,http://effertz.io/marilie,0.5792882335,165.41.21.215,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n19504,7,439,2017-06-04 03:15:50,http://stokes.org/tyrel_leuschke,0.0385576580,113.143.52.29,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n19505,7,439,2017-02-24 18:57:00,http://gutmann.com/edd,0.2775151457,205.172.218.115,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n19506,7,439,2017-05-28 12:10:17,http://ritchie.org/murphy,0.6528699920,100.101.113.238,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": true}\"\n19507,7,439,2017-04-17 11:10:28,http://runolfsdottir.co/wayne_ullrich,0.4767541624,75.227.206.170,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n19508,7,439,2017-01-05 15:34:25,http://watsica.info/raven.hegmann,0.9329569955,44.186.48.115,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n19509,7,439,2017-01-16 02:16:53,http://harrisabernathy.name/justice.hoeger,0.5102907514,161.127.247.160,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n19510,7,439,2017-05-06 12:01:11,http://hillljacobi.name/janie,0.0866433721,71.85.212.98,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n19511,7,439,2017-02-04 04:33:13,http://kris.org/albert,0.0651579417,2.226.169.138,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": false}\"\n19512,7,439,2017-04-14 16:09:34,http://graham.info/jadyn_larson,0.6675473608,210.38.93.252,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n19513,7,439,2017-03-24 02:04:48,http://effertzmills.net/orville_sawayn,0.6856207105,136.222.97.107,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n19514,7,439,2017-03-22 16:49:49,http://bayer.net/kirk,0.6804911453,215.94.44.91,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n19515,7,439,2016-12-18 14:32:46,http://ohara.biz/geraldine,0.7790209694,71.166.252.251,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n19516,7,439,2017-01-19 13:18:37,http://goyette.biz/audra_beahan,0.3156226280,10.195.23.55,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n19517,7,439,2017-04-15 17:06:35,http://thiel.net/rodolfo,0.4323517662,200.38.169.156,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n19518,7,439,2017-01-09 03:42:54,http://kunde.name/gerda,0.0003925644,249.197.184.81,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n19519,7,439,2017-02-14 00:25:27,http://stanton.biz/ethel_stark,0.7637305116,66.103.211.200,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n19520,7,439,2017-06-09 12:55:23,http://jacobi.biz/ezekiel,0.4475354180,79.38.133.76,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n19521,7,439,2017-01-03 20:58:32,http://padbergkozey.co/elsa_rau,0.8912980484,17.239.154.238,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n19522,7,439,2017-04-06 06:16:39,http://gusikowski.net/kane_howe,0.3331294009,178.113.44.27,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n19523,7,439,2017-01-18 00:03:30,http://bernhardkris.co/gladyce,0.5095950434,94.160.201.19,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n19524,7,439,2017-05-06 03:06:32,http://hellerkreiger.com/meghan,0.6433001896,154.239.70.86,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n19525,7,439,2017-04-09 20:16:56,http://sporerhuel.org/bridget.hane,0.8060124951,127.92.18.108,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n19526,7,439,2017-01-11 02:11:31,http://champlin.info/barney,0.0840226491,203.5.25.97,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n19527,7,439,2017-01-19 20:30:43,http://maggio.co/daniella.hammes,0.9978383325,156.197.241.154,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n19528,7,439,2017-05-16 16:43:51,http://kreiger.com/easter_hickle,0.8466497521,238.130.205.122,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n19529,7,439,2017-01-30 03:50:06,http://kilback.org/trycia.medhurst,0.3373854282,13.127.120.195,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n19530,7,440,2017-02-27 15:44:27,http://predovicmedhurst.biz/charity.ankunding,0.0942658899,133.12.241.69,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n19531,7,440,2017-05-01 08:56:31,http://howeschulist.net/stefan,0.2313030603,235.18.96.218,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n19532,7,440,2017-03-17 04:14:13,http://durgankeeling.io/sophie,0.0921386268,183.15.78.12,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n19533,7,440,2016-12-29 02:16:52,http://andersonlebsack.biz/erik,0.5578169324,16.172.83.8,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n19534,7,440,2017-01-09 16:29:29,http://fisher.com/christine_fisher,0.6393479405,174.124.144.108,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n19535,7,440,2017-03-02 08:50:48,http://maggio.name/everett.breitenberg,0.7559755573,173.56.26.35,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n19536,7,440,2017-04-18 10:49:25,http://tromp.net/cathryn.kozey,0.1204576187,176.186.176.127,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n19537,7,440,2017-01-14 23:09:03,http://crona.com/julia,0.5580320682,141.21.180.228,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n19538,7,440,2017-01-22 22:00:56,http://cainbruen.io/desiree.little,0.2345894994,192.43.231.233,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n19539,7,440,2016-12-23 05:03:20,http://oconnell.biz/mozelle,0.7225699710,105.26.162.71,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n19540,7,440,2017-06-06 05:22:33,http://connelly.info/alec_wolff,0.7531374383,110.154.146.193,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n19541,7,440,2017-01-16 13:33:04,http://lubowitz.info/jayne.konopelski,0.5418522510,241.207.213.215,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n19542,7,440,2017-01-31 04:00:49,http://feil.net/daniela_prohaska,0.0423697244,4.28.51.91,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n19543,7,440,2016-12-24 12:19:24,http://kuhn.net/brent_price,0.0814590303,122.51.13.2,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n19544,7,440,2017-05-03 20:41:53,http://walker.io/jeyca_heathcote,0.4011520598,135.165.130.108,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n19545,7,440,2017-01-08 12:14:40,http://lynchspencer.net/otilia_casper,0.7802577215,81.167.55.140,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n19546,7,440,2017-02-11 02:35:37,http://adams.co/maia,0.7582663615,60.86.239.148,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n19547,7,440,2017-04-15 08:36:56,http://maggio.name/kristoffer,0.7292674118,63.84.139.87,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n19548,7,440,2017-05-31 09:19:26,http://langworth.co/earline,0.2316779311,246.210.46.100,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n19549,7,440,2017-02-24 22:45:42,http://dickinsonmohr.co/marc_bergstrom,0.6956366257,254.70.60.222,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n19550,7,440,2016-12-13 13:54:44,http://christiansen.net/wilhelmine_wintheiser,0.6101495672,19.210.52.177,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": false}\"\n19551,7,440,2017-01-23 07:10:53,http://berge.co/sydni,0.5269091893,186.10.242.234,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n19552,7,440,2017-03-12 13:49:39,http://macgyver.info/myrna,0.5107295899,90.142.142.54,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n19553,7,440,2017-04-25 19:15:57,http://olson.io/rhianna,0.8220042374,46.16.67.82,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n19554,7,440,2017-03-22 09:15:04,http://walshsporer.co/marlen,0.0971093316,126.115.176.56,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n19555,7,440,2016-12-18 19:52:18,http://schimmelupton.com/lillian,0.6308171506,44.26.2.158,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n19556,7,440,2017-01-10 06:42:54,http://altenwerthprice.co/william,0.9692550788,184.95.43.97,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n19557,7,440,2017-06-02 12:39:29,http://larson.net/declan,0.9843033986,21.237.251.17,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n19558,7,440,2017-01-25 08:57:07,http://gaylord.info/jermaine,0.7987270672,206.51.133.225,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n19559,7,440,2017-03-12 19:08:09,http://mcclure.org/cordia,0.4148879006,192.66.126.228,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n19560,7,440,2017-05-25 02:22:46,http://kuhn.co/rosemarie,0.7664360307,224.182.38.19,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n19561,7,440,2017-04-04 18:00:57,http://rodriguezeichmann.net/wendell,0.7823928044,28.159.193.222,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n19562,7,440,2017-01-04 13:35:42,http://hettinger.co/chaz_mraz,0.2759403213,231.242.163.144,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n19563,7,441,2017-04-13 14:09:11,http://wisozk.io/jey.glover,0.8670222872,188.55.99.5,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n19564,7,441,2017-05-21 10:43:33,http://bradtkecormier.io/joanny_kirlin,0.7591794068,184.105.195.122,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n19565,7,441,2017-01-29 00:26:33,http://hayes.name/rafael_orn,0.3113514515,87.139.155.193,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n19566,7,441,2017-02-17 17:55:05,http://herzoglind.co/americo,0.8476196896,245.16.220.185,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n19567,7,441,2017-04-20 16:24:12,http://jast.net/golden,0.8869959113,164.202.223.181,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n19568,7,441,2017-01-13 22:34:58,http://grimes.co/lindsay,0.0340345326,207.30.11.139,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n19569,7,441,2017-03-22 02:50:30,http://towne.name/camille,0.3547136298,113.209.172.182,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n19570,7,441,2017-02-10 17:27:35,http://wiza.name/leonor,0.5573843559,132.215.234.54,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n19571,7,441,2017-01-09 01:55:23,http://durgan.net/kaycee,0.4829766337,253.148.115.227,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n19572,7,441,2017-01-13 15:21:19,http://gottliebauer.org/edmund,0.1020699768,199.83.76.161,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n19573,7,441,2017-02-10 13:27:37,http://konopelski.io/fae,0.8692917036,96.249.246.6,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n19574,7,441,2017-03-30 10:53:35,http://pouros.info/jermaine,0.7649468557,213.154.233.165,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n19575,7,441,2017-03-13 17:32:34,http://emmerich.biz/ima.kling,0.0491787595,130.248.150.205,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n19576,7,441,2017-01-04 08:56:12,http://schaefercummings.info/monica_zboncak,0.8338738951,122.157.253.72,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n19577,7,441,2017-02-17 12:42:58,http://von.co/sheldon,0.3554692635,21.196.175.192,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n19578,7,441,2017-05-11 23:11:16,http://mcclure.io/aisha.jerde,0.6847278867,243.30.137.154,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n19579,7,441,2017-06-11 19:15:23,http://reichert.io/laurel_boyle,0.1743031502,234.27.107.193,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n19580,7,441,2017-06-07 22:59:11,http://fadel.co/scotty,0.2056355056,229.239.253.159,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n19581,7,441,2017-01-24 05:46:54,http://ferry.co/ofelia.barton,0.5665589639,244.170.184.29,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n19582,7,441,2017-05-01 12:39:38,http://cruickshank.biz/helmer,0.3581386158,69.7.224.251,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n19583,7,441,2017-03-29 20:01:06,http://dickibahringer.biz/branson,0.2323289140,124.7.179.159,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n19584,7,441,2016-12-14 01:13:06,http://frami.co/eliza_hermiston,0.1361940706,190.160.204.251,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n19585,7,441,2017-05-09 03:37:15,http://balistrerijones.name/kattie_leuschke,0.6296184210,225.153.47.21,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n19586,7,441,2017-05-19 15:19:11,http://walsh.io/cody,0.5349650564,88.232.50.173,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n19587,7,441,2017-04-11 08:38:47,http://ankunding.org/samantha_glover,0.4761389649,188.57.190.77,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n19588,7,441,2017-02-10 12:53:49,http://pfannerstillrunolfsdottir.biz/gayle.effertz,0.2506628200,69.225.109.47,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n19589,7,441,2017-05-25 03:10:40,http://frami.info/webster_kub,0.5072315957,213.176.247.79,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": true}\"\n19590,7,441,2016-12-18 06:25:36,http://grady.co/yazmin,0.7256938526,71.90.25.78,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n19591,7,441,2017-01-22 02:48:21,http://schmittmccullough.biz/emile_damore,0.6716308475,65.48.149.140,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n19592,7,441,2017-05-30 16:37:38,http://hagenes.info/aleandra,0.3841781933,251.111.51.237,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n19593,7,441,2016-12-14 06:16:48,http://gorczany.com/samantha_stiedemann,0.9236519733,167.30.26.112,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n19594,7,441,2017-03-28 14:18:37,http://beatty.biz/annie,0.9870678170,131.61.197.238,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n19595,7,441,2017-06-08 23:30:23,http://greenholt.biz/janae,0.2739576855,20.209.38.65,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": true}\"\n19596,7,441,2017-04-27 04:31:32,http://grady.info/houston,0.7799695472,84.179.33.36,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n19597,7,441,2017-05-25 01:38:41,http://boehmschimmel.biz/ashlynn_dare,0.8547236757,66.40.229.119,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n19598,7,441,2017-04-30 01:46:33,http://yundt.biz/norris.grady,0.1158134121,129.128.252.185,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n19599,7,441,2017-02-10 15:26:35,http://lemke.io/orlando_rutherford,0.3672857886,168.40.206.107,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n19600,7,441,2017-01-05 02:14:38,http://crooks.info/preston,0.2544682763,135.129.122.175,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n19601,7,441,2017-05-22 15:52:51,http://abernathyrobel.info/margaretta,0.0096675325,94.200.252.112,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n19602,7,441,2017-05-30 23:15:16,http://willnitzsche.info/rod,0.0690548226,150.149.89.26,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n19603,7,441,2017-03-06 11:37:47,http://collier.com/donavon.kling,0.8400101289,85.150.156.252,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n19604,7,441,2016-12-24 23:12:22,http://jaskolskiruel.net/hailee,0.4488187901,111.47.187.97,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n19605,7,441,2017-01-12 04:57:48,http://lueilwitzmacejkovic.com/louie_sauer,0.8401310587,99.227.82.136,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n19606,7,442,2017-04-11 11:43:51,http://tromp.io/juliana_wisoky,0.6209368795,109.111.25.214,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n19607,7,442,2017-06-10 04:59:13,http://zboncakabbott.biz/phoebe,0.5219821737,186.246.191.169,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": false}\"\n19608,7,442,2017-04-21 05:28:08,http://prosacco.com/jaquan_reinger,0.7193336751,95.235.90.20,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n19609,7,442,2017-01-05 13:46:52,http://osinskihammes.co/edmond.white,0.5658363012,19.203.12.3,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n19610,7,442,2017-06-05 07:37:35,http://ankundingjohnston.org/nikolas_larson,0.8973665665,21.125.84.3,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n19611,7,442,2017-05-04 20:15:04,http://lowe.com/virgie,0.4072345117,234.182.178.71,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n19612,7,442,2017-03-31 21:55:36,http://parisian.io/fay,0.9112006059,219.171.162.46,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n19613,7,442,2017-05-09 03:44:49,http://oconnell.org/gideon_kuhn,0.0717987719,18.120.188.247,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n19614,7,442,2017-05-04 03:11:48,http://framibrekke.co/bobbie_dickinson,0.6610394706,203.61.155.82,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n19615,7,442,2017-01-02 03:58:53,http://rice.info/hunter_hermiston,0.0946420074,248.56.5.2,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n19616,7,442,2017-01-24 22:34:40,http://kulas.info/mozell.nikolaus,0.2989197529,78.6.201.20,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n19617,7,442,2017-01-09 23:23:33,http://moriette.com/hillard,0.5591397724,75.236.127.86,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n19618,7,442,2017-01-15 18:19:09,http://bosco.net/janea.kiehn,0.9532549832,158.235.169.214,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n19619,7,442,2017-02-12 21:22:44,http://hagenes.biz/maximillian.okon,0.6404196590,47.40.193.134,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": false}\"\n19620,7,442,2017-02-24 05:09:53,http://blandahowell.io/gordon.lynch,0.0207757113,5.144.236.129,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n19621,7,442,2016-12-19 23:37:08,http://boylekertzmann.org/justyn_kautzer,0.5123411535,91.158.144.207,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n19622,7,442,2017-03-30 01:37:09,http://gleason.info/johnny,0.6722638684,215.34.115.73,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n19623,7,442,2017-04-24 07:28:49,http://mcglynn.co/else,0.2157759203,171.213.34.181,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n19624,7,442,2017-04-16 16:05:22,http://hyatt.info/aiden,0.6083632024,207.85.133.18,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n19625,7,442,2017-05-14 01:47:01,http://beatty.net/rashad,0.3434029141,55.217.232.138,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n19626,7,442,2017-03-26 03:34:44,http://gottlieb.info/mazie,0.6618835598,111.100.57.218,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n19627,7,442,2017-01-19 09:15:00,http://padbergtowne.io/mia.powlowski,0.3729598671,98.70.220.98,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n19628,7,442,2017-01-25 10:43:52,http://harrisabernathy.biz/steve_larkin,0.2701172856,178.174.47.96,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n19629,7,442,2017-03-26 19:39:10,http://zulaufbartell.com/herman_stiedemann,0.5487510924,30.156.200.127,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n19630,7,442,2017-03-16 09:03:38,http://zulaufcormier.biz/aniya.berge,0.1011773246,111.94.15.223,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n19631,7,442,2017-05-06 14:23:36,http://torp.net/aryanna_hoppe,0.3668525786,191.75.82.37,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n19632,7,442,2017-06-03 03:55:46,http://moorerempel.net/jonathan,0.1187774024,159.108.17.208,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n19633,7,442,2017-03-07 21:22:06,http://adamsreynolds.com/elena_prohaska,0.0629636804,227.27.66.153,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n19634,7,442,2017-04-15 14:07:39,http://hartmann.biz/alverta_roob,0.8915126652,61.247.116.45,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n19635,7,442,2016-12-31 21:15:48,http://leannonstoltenberg.biz/lawson,0.3982673825,125.234.35.161,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n19636,7,442,2017-04-14 02:12:20,http://wisozk.org/federico.ruel,0.3094504299,154.33.25.198,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n19637,7,442,2016-12-24 04:18:34,http://streichkuphal.com/jakayla,0.7692826393,153.92.213.106,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n19638,7,442,2017-05-21 06:57:14,http://block.net/malvina,0.1582159588,45.183.92.218,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n19639,7,442,2017-02-15 00:32:22,http://adams.name/nelda.gorczany,0.4285022720,56.54.157.221,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n19640,7,442,2017-02-27 20:40:17,http://ritchie.name/katlynn_rippin,0.0552916742,196.37.254.24,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n19641,7,442,2017-01-08 05:00:21,http://stehr.info/virginia,0.5073778200,75.17.18.205,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n19642,7,442,2017-03-15 09:54:43,http://mcclure.net/amiya,0.8137124440,171.78.237.237,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n19643,7,442,2017-02-03 03:18:59,http://hudsonlittel.name/scarlett.boyle,0.5544233846,139.237.104.153,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n19644,7,442,2017-01-22 10:57:31,http://moriette.info/abbie.kuhlman,0.2720240777,27.167.39.79,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n19645,7,442,2017-03-05 04:22:17,http://nikolaushilpert.co/tod,0.7473047306,215.195.118.92,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": false}\"\n19646,7,442,2017-03-07 20:13:59,http://walker.net/augusta.pfeffer,0.8170471663,116.197.240.3,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n19647,7,442,2017-01-02 21:11:58,http://baumbach.io/alexander,0.2866458636,32.222.6.127,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n19648,7,443,2017-05-04 00:52:21,http://price.co/melia.daugherty,0.1225193900,26.121.127.101,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n19649,7,443,2017-05-06 11:55:48,http://barrows.co/dwight,0.2383812366,59.110.138.184,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n19650,7,443,2017-02-12 00:13:23,http://abshirekonopelski.net/erling,0.7713834734,190.46.193.20,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n19651,7,443,2017-03-06 06:53:48,http://torp.name/jayda,0.4381348958,235.5.87.94,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n19652,7,443,2017-04-18 23:38:57,http://lubowitz.io/eleanore,0.6605250044,244.107.141.107,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n19653,7,443,2016-12-21 05:50:40,http://bogan.net/tianna.macejkovic,0.6574824163,118.31.186.111,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n19654,7,443,2017-03-15 22:49:28,http://watsica.co/lorenza_senger,0.8623125695,18.195.61.214,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n19655,7,443,2016-12-30 10:27:04,http://murphy.co/kellie,0.6436932304,96.9.165.190,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n19656,7,443,2017-06-10 21:02:38,http://kovacekcormier.name/chandler_stiedemann,0.5129516031,101.140.155.117,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n19657,7,443,2017-03-29 14:08:06,http://ondricka.biz/gaston.deckow,0.3949355260,214.177.181.66,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n19658,7,443,2017-05-18 21:02:34,http://nicolas.info/aleen,0.3677090038,188.251.5.44,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n19659,7,443,2017-06-06 10:20:16,http://thompsonruel.org/rogers,0.2246546217,226.13.42.213,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n19660,7,443,2017-03-24 20:01:22,http://hicklebuckridge.com/julian.hansen,0.1934123219,233.232.180.152,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n19661,7,443,2017-01-27 08:00:06,http://keler.info/nathanial.pfannerstill,0.4443891634,186.187.56.192,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n19662,7,443,2017-03-11 05:36:51,http://bechtelar.io/greyson_lebsack,0.5225393026,43.101.33.148,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n19663,7,443,2017-06-07 23:51:03,http://schmitt.net/jackson.welch,0.8581394119,122.135.226.187,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n19664,7,443,2017-04-18 05:47:38,http://hamill.info/hilbert,0.8715917939,224.52.230.42,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n19665,7,443,2017-01-01 17:28:05,http://rath.info/spencer,0.1987452746,126.134.220.238,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n19666,7,443,2017-05-23 04:51:24,http://kovacek.name/hershel,0.1468033461,95.65.254.209,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n19667,7,443,2017-01-04 01:22:38,http://wolff.org/steve,0.3811262540,235.194.75.183,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n19668,7,443,2016-12-24 22:46:52,http://harris.co/kelton,0.0577696705,27.65.232.163,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": true}\"\n19669,7,443,2016-12-21 06:00:29,http://gerholdmaggio.io/flo.barton,0.6736343070,164.218.67.23,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n19670,7,443,2017-02-04 17:48:35,http://stehrstoltenberg.biz/tom.schamberger,0.7896468151,253.85.68.79,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n19671,7,443,2017-05-21 18:23:21,http://schowalter.biz/imani,0.4565596166,190.130.84.250,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n19672,7,443,2017-02-19 22:25:47,http://price.info/tremaine,0.5218716878,220.11.67.8,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n19673,7,443,2017-04-20 08:06:59,http://halvorson.info/johnpaul_swift,0.9153586881,172.236.167.121,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n19674,7,443,2017-01-09 00:02:23,http://williamson.info/shanna,0.0734843063,161.171.191.246,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n19675,7,443,2017-04-07 02:58:05,http://stoltenberg.net/beverly_simonis,0.3089618503,176.73.133.186,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n19676,7,443,2017-01-10 06:23:48,http://kundemoore.name/hanna.turcotte,0.1921771904,195.64.187.5,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n19677,7,443,2017-04-06 20:34:04,http://mohrhilll.com/arvel_stehr,0.7563170970,35.146.4.25,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n19678,7,443,2017-05-09 01:42:59,http://hauck.com/andre,0.2366914113,182.14.9.76,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n19679,7,443,2016-12-13 20:38:54,http://conn.io/kayli,0.9736347154,189.184.25.146,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n19680,7,443,2017-01-28 03:18:16,http://ryan.name/johnny,0.3918529799,117.224.111.20,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n19681,7,443,2017-03-08 18:23:24,http://ferry.net/fredrick.weimann,0.7667330416,239.230.101.193,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n19682,7,443,2017-04-14 19:17:23,http://gulgowski.co/kathleen,0.9162763341,234.224.69.32,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n19683,7,443,2017-01-26 06:26:43,http://pfefferschroeder.org/ansley_douglas,0.5336769960,42.94.101.223,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n19684,7,443,2017-04-22 00:40:30,http://langworth.net/sebastian,0.7098052344,175.5.226.239,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n19685,7,443,2017-04-14 07:25:55,http://cummings.com/margarete,0.9645013953,195.111.155.142,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n19686,7,443,2017-04-15 01:41:05,http://ruecker.co/braxton,0.7801000351,252.99.126.159,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n19687,7,443,2017-06-12 22:03:10,http://haagschroeder.com/georgette_ruel,0.9668981768,8.26.245.94,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n19688,7,443,2017-01-18 01:04:10,http://rogahn.io/dasia,0.8276276148,191.252.20.189,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n19689,7,443,2017-04-11 05:55:04,http://cruickshank.io/melvin.powlowski,0.2380127806,20.161.139.84,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n19690,7,443,2017-03-21 14:43:07,http://hills.co/emmet.kuvalis,0.5006976499,82.38.120.204,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n19691,7,443,2017-04-30 15:07:08,http://wisozk.io/hannah_robel,0.2999779655,225.57.151.194,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n19692,7,443,2017-01-09 18:20:46,http://zieme.biz/jonathan_durgan,0.3110469202,82.90.164.104,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n19693,7,443,2017-05-14 03:26:42,http://wehneraufderhar.org/deion,0.9759897800,36.161.246.49,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n19694,7,443,2017-05-15 05:43:11,http://zieme.name/philip.gusikowski,0.8931834298,30.109.128.95,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n19695,7,443,2017-03-22 15:19:26,http://zemlak.name/jarrod,0.6781219411,20.137.231.140,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n19696,7,443,2017-05-20 09:04:36,http://schumm.io/cleo,0.0408481942,61.29.231.37,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n19697,7,443,2017-01-05 23:47:08,http://orn.info/annetta,0.0449994133,100.203.217.154,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": false}\"\n19698,7,443,2017-05-26 09:42:59,http://monahan.net/jewel,0.4609891247,168.168.24.164,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n19699,7,443,2017-05-15 17:47:47,http://gleichnerbogisich.io/bert_watsica,0.2493793057,109.66.220.172,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n19700,7,443,2017-01-07 08:58:29,http://lebsack.io/kamille,0.6432757372,21.205.194.100,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n19701,7,443,2017-03-30 17:34:21,http://lehner.info/brannon,0.4893731155,98.59.3.84,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": false}\"\n19702,7,443,2016-12-30 16:09:23,http://crooks.info/daniella,0.9491194207,106.47.27.16,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n19703,7,443,2017-05-02 10:31:39,http://fahey.com/kitty,0.5449586144,171.211.100.112,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n19704,7,443,2017-01-09 19:52:26,http://reichert.co/justyn.kirlin,0.3932263762,162.242.233.211,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n19705,7,443,2017-05-19 02:42:50,http://bernhard.name/cheyenne.kuhic,0.8592078219,169.187.109.217,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n19706,7,443,2017-05-17 18:11:28,http://kunzehilpert.net/jarrett_pfannerstill,0.6793745747,58.76.232.214,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n19707,7,443,2017-04-09 19:12:08,http://smith.org/herta,0.6976586336,30.85.226.156,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n19708,7,443,2017-05-14 23:53:38,http://dicki.net/green.howell,0.4446681502,104.115.146.138,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n19709,7,443,2017-03-01 03:23:23,http://wehner.com/julien,0.3780134519,116.240.250.92,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": true}\"\n19710,7,443,2017-05-09 15:53:45,http://aufderhar.biz/raina,0.1902623164,194.197.84.84,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n19711,7,443,2017-01-18 16:17:36,http://hermannmacgyver.biz/carmel,0.2814613551,239.229.215.15,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n19712,7,443,2017-03-18 11:37:08,http://flatley.io/elia,0.7001805562,253.115.138.43,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n19713,7,444,2017-04-20 03:14:44,http://dooleylehner.info/lora,0.0619632067,167.32.218.158,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n19714,7,444,2017-01-05 10:27:06,http://oreillygutkowski.org/aric.macejkovic,0.8545478158,163.161.163.201,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n19715,7,444,2016-12-28 09:06:41,http://marquardtko.io/shaylee_price,0.5876390920,80.153.222.25,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n19716,7,444,2017-02-17 05:24:34,http://schuppe.co/jamel_daniel,0.9532822756,192.243.202.243,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n19717,7,444,2016-12-26 22:24:02,http://koch.co/isabella_erdman,0.8238588049,13.187.164.52,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n19718,7,444,2017-01-13 04:24:10,http://bartoletti.co/chet_brakus,0.1841494407,33.230.249.121,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n19719,7,444,2017-06-03 19:29:24,http://lindgren.biz/lenore,0.6988375907,131.139.37.67,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n19720,7,444,2016-12-16 12:16:20,http://bode.info/sonny,0.5020229342,47.152.201.229,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n19721,7,444,2017-05-18 12:44:09,http://corwin.info/tina.dooley,0.6983668267,203.165.169.33,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n19722,7,444,2017-02-03 10:38:22,http://huels.biz/german,0.5456514479,173.116.237.157,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n19723,7,444,2017-01-10 06:23:46,http://oreilly.name/lyda,0.7578362455,101.239.196.3,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n19724,7,444,2017-02-18 01:30:47,http://wintheiser.name/jena_bechtelar,0.0624061007,167.83.28.102,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n19725,7,444,2017-01-22 02:38:28,http://rath.org/fanny.mclaughlin,0.3212635498,250.147.245.41,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n19726,7,444,2017-06-09 17:58:51,http://yundtruel.com/donny_ankunding,0.2428915674,204.75.151.227,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n19727,7,444,2017-06-03 16:54:18,http://lubowitz.name/adela,0.6118696148,169.113.52.99,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n19728,7,444,2017-01-03 04:03:50,http://keler.org/braxton,0.6262531462,98.116.135.124,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n19729,7,444,2017-03-25 05:38:59,http://konopelski.net/dariana,0.7380521719,126.57.9.135,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n19730,7,444,2017-03-31 07:19:38,http://corkerybartoletti.io/candida,0.8230901735,98.59.190.145,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n19731,7,444,2017-02-23 02:26:44,http://lynch.info/tre.schmidt,0.3491701771,224.109.191.240,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n19732,7,444,2017-03-09 15:11:25,http://reichertfeeney.biz/peter.windler,0.9176353022,70.84.243.204,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n19733,7,444,2017-01-28 15:23:23,http://schuppe.org/antonia,0.6949826966,233.154.231.76,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n19734,7,444,2017-05-19 01:22:48,http://labadie.net/myriam,0.7788296295,47.242.186.230,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n19735,7,444,2017-02-25 09:25:52,http://heaney.co/loma,0.0388489415,244.75.35.254,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n19736,7,444,2017-03-01 19:24:22,http://kozey.org/naomi,0.4645092967,5.41.138.215,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n19737,7,444,2017-03-02 22:02:41,http://langosh.net/raleigh.sauer,0.4908219665,118.162.201.5,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n19738,7,444,2017-03-22 18:45:04,http://brekke.com/hailee,0.8217332022,181.83.121.208,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n19739,7,444,2017-03-31 22:23:51,http://kuhn.net/marquise,0.4126286919,207.33.242.58,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n19740,7,444,2017-02-12 15:30:56,http://haneauer.com/javonte,0.4903425143,240.59.53.189,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n19741,7,444,2016-12-27 03:43:43,http://hettinger.biz/josefina_dibbert,0.1725732533,247.98.125.142,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n19742,7,444,2017-02-09 03:26:42,http://marvingerhold.io/erick,0.5989431066,154.209.200.100,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n19743,7,444,2017-01-04 19:57:09,http://yundt.info/cyrus_carroll,0.7249368651,246.112.130.187,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n19744,7,444,2017-03-12 01:05:13,http://hackett.org/abbie_ullrich,0.5402926016,186.140.28.166,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n19745,7,444,2017-04-13 22:28:04,http://gerholdschamberger.co/cullen_boyle,0.1686637401,88.27.19.84,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n19746,7,444,2017-06-06 21:02:33,http://welch.co/adolf,0.4627308344,250.229.106.89,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n19747,7,444,2017-03-01 01:43:14,http://quigley.net/sunny_gutmann,0.0310120354,142.119.250.51,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n19748,7,444,2017-06-13 18:25:25,http://nolanherman.biz/adriel,0.9296986130,106.28.52.207,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n19749,7,444,2017-04-10 22:14:29,http://quitzon.com/maynard,0.4531194766,188.61.178.42,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n19750,7,444,2017-02-10 17:11:43,http://braun.info/estel,0.0104729257,97.42.240.107,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n19751,7,444,2017-04-15 22:48:22,http://jones.org/pete.crooks,0.3815869957,103.162.181.45,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n19752,7,444,2017-04-13 19:24:08,http://gutmann.co/keeley_armstrong,0.0027265837,204.19.242.138,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n19753,7,444,2017-02-11 02:15:20,http://block.name/della,0.9065344500,89.24.63.246,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n19754,7,444,2017-04-04 09:47:51,http://osinskiwalter.com/penelope.casper,0.4616732658,107.207.217.27,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n19755,7,444,2017-06-04 17:36:03,http://kulachneider.info/lacy.watsica,0.9063828740,126.112.253.18,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n19756,7,445,2017-04-03 09:31:03,http://nikolausdamore.name/yvonne_hickle,0.3012898436,48.90.251.169,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n19757,7,445,2017-01-04 08:48:02,http://abshire.co/abbie,0.4042448225,236.253.223.4,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n19758,7,445,2017-01-13 11:12:46,http://skiles.com/breanne_orn,0.9138047927,231.149.122.128,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n19759,7,445,2017-04-27 04:45:24,http://wehnerkuhn.org/dominic,0.1648245670,28.76.26.253,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n19760,7,445,2017-05-13 10:29:31,http://ratke.net/tyler,0.0907613806,211.201.232.40,\"{\"\"location\"\": \"\"LY\"\", \"\"is_mobile\"\": true}\"\n19761,7,445,2017-03-01 08:43:55,http://hackettjast.org/thalia,0.9833397691,10.221.111.74,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n19762,7,445,2017-05-20 23:45:27,http://turcottegleason.co/blaze,0.0408388082,177.58.30.195,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n19763,7,445,2017-04-12 00:08:59,http://wisokylindgren.net/jayson.goldner,0.1029923915,15.139.114.18,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n19764,7,445,2017-03-09 10:01:47,http://gorczany.name/buster,0.2966321910,68.139.224.49,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": true}\"\n19765,7,445,2017-04-25 02:10:53,http://greenkulas.co/nathanial,0.4193785597,196.74.183.203,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n19766,7,445,2017-02-02 19:57:39,http://reilly.biz/georgiana,0.4857991853,53.241.122.90,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n19767,7,445,2017-03-20 04:57:16,http://pagac.biz/kelli,0.8460524923,100.2.186.95,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n19768,7,445,2017-02-15 17:00:07,http://robelzemlak.name/buster.turcotte,0.3857207812,247.107.142.162,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n19769,7,445,2017-06-06 16:49:04,http://hartmannreilly.co/mozelle_jones,0.0114440873,251.167.5.220,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n19770,7,445,2016-12-22 10:35:27,http://metzleannon.biz/tatum_koelpin,0.6290479793,80.218.51.41,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n19771,7,445,2017-02-26 21:16:04,http://herzog.co/beverly_ruel,0.4360360659,121.11.29.167,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n19772,7,445,2017-05-06 13:41:48,http://bogisich.name/tony,0.4785370274,83.228.146.89,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": true}\"\n19773,7,445,2017-03-24 15:57:04,http://bernhard.org/nathanial,0.9948555592,200.155.231.139,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n19774,7,445,2017-02-18 02:27:50,http://lind.info/josiah,0.5315466039,155.18.212.209,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n19775,7,445,2017-05-18 03:13:30,http://tromp.co/adelia,0.2745135216,4.79.6.217,\"{\"\"location\"\": \"\"MN\"\", \"\"is_mobile\"\": false}\"\n19776,7,445,2017-05-22 07:46:04,http://hauck.com/jaden,0.6399019982,46.21.109.11,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n19777,7,445,2017-06-11 15:06:20,http://wisozk.biz/alanis,0.1577075965,48.33.109.39,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n19778,7,445,2017-05-31 19:55:16,http://rogahnfay.net/emely,0.6452665864,19.37.183.148,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": true}\"\n19779,7,445,2017-03-15 00:05:23,http://krajcik.com/adaline,0.9295453876,23.227.253.144,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n19780,7,445,2017-02-16 13:31:43,http://mann.name/marcos.witting,0.7604996812,72.177.31.252,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n19781,7,445,2017-04-22 17:02:20,http://langworthboyer.com/jewell_stracke,0.1668950079,108.253.218.104,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n19782,7,445,2017-05-11 11:35:13,http://kohler.biz/abelardo,0.1214966297,132.96.142.92,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n19783,7,445,2017-01-17 12:34:15,http://schaeferkunde.com/junior.windler,0.0684231034,228.108.219.175,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n19784,7,445,2017-05-31 11:47:50,http://shields.org/efrain_heathcote,0.4714970128,23.217.152.92,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n19785,7,445,2017-01-06 07:21:02,http://reinger.io/theo_lowe,0.4325708336,26.177.73.49,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n19786,7,445,2017-04-27 06:03:16,http://wunsch.org/damian,0.9679457345,220.219.126.198,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n19787,7,445,2017-01-02 02:03:04,http://vonrueden.biz/mozelle,0.7560734882,251.72.80.54,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n19788,7,445,2016-12-29 19:13:49,http://hauck.info/magnolia,0.5320740480,229.164.176.155,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n19789,7,445,2016-12-14 14:44:14,http://sawayn.biz/ollie_hermiston,0.2104631325,9.249.46.68,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n19790,7,445,2017-05-24 01:55:03,http://goldner.org/adell,0.0723474088,100.67.196.126,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n19791,7,445,2017-04-27 04:58:29,http://wiza.org/ariel.gutkowski,0.0368458952,135.177.93.130,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n19792,7,445,2017-04-03 14:11:53,http://halvorson.biz/katheryn.bruen,0.3138953040,225.95.70.199,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n19793,7,445,2017-01-26 18:14:23,http://hilpert.org/wilhelmine.breitenberg,0.3350452787,211.238.206.179,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n19794,7,445,2017-03-19 10:57:13,http://legros.co/heidi,0.4382614246,199.232.52.126,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n19795,7,445,2017-05-08 01:59:19,http://reynolds.net/desmond.nitzsche,0.7264977776,38.102.66.118,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n19796,7,445,2017-01-17 13:09:05,http://mcdermottmayert.biz/darryl_denesik,0.6466706685,114.91.144.100,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n19797,7,445,2017-04-23 05:12:47,http://hyattmacejkovic.info/reese.hand,0.6050099217,244.93.227.79,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n19798,7,445,2017-02-13 08:39:07,http://christiansen.info/kristopher,0.4809983489,61.135.71.118,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n19799,7,445,2017-05-26 09:06:14,http://powlowskihaag.biz/vladimir_kuphal,0.3404575072,98.95.63.178,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n19800,7,445,2017-01-23 16:27:25,http://dachreichel.info/jakayla,0.8293980364,98.18.79.213,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n19801,7,445,2017-01-30 17:50:28,http://dubuquemccullough.biz/dulce,0.7809267441,101.99.68.247,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": true}\"\n19802,7,445,2017-01-22 10:44:42,http://hauck.org/ariel,0.9565000817,202.174.11.30,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n19803,7,445,2017-05-31 00:20:48,http://eichmann.io/rachael_reinger,0.0511797215,121.71.97.214,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n19804,7,445,2017-03-24 11:43:39,http://damorenitzsche.io/roscoe.vonrueden,0.9479168968,223.129.51.243,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n19805,7,445,2017-02-28 22:27:56,http://schroeder.co/demetrius,0.7499512721,120.190.119.203,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n19806,7,445,2017-01-26 21:37:51,http://spinka.name/imani_emmerich,0.6996261298,202.22.210.156,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n19807,7,445,2017-06-01 17:01:33,http://boscoryan.org/soledad,0.7182514237,177.77.100.32,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n19808,7,445,2017-02-17 20:04:24,http://feeney.name/kaley.koch,0.5108435989,231.41.150.162,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n19809,7,445,2017-01-02 23:39:38,http://buckridge.biz/tyreek_reichel,0.4864316964,144.91.91.56,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n19810,7,445,2017-06-06 16:44:59,http://kihn.biz/trystan,0.0950261270,71.229.22.234,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n19811,7,445,2017-01-13 22:13:02,http://collins.io/herminio,0.3765793834,174.59.249.210,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n19812,7,445,2017-04-06 04:23:18,http://schmittpfannerstill.io/sylvan,0.4660054804,135.237.143.106,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n19813,7,445,2017-05-01 20:50:50,http://wiza.org/dorcas,0.6969862193,254.87.121.87,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n19814,7,445,2017-04-28 23:25:44,http://luettgen.com/mike,0.0581838053,152.47.158.48,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n19815,7,445,2017-02-07 11:27:40,http://gerholdhammes.net/luz_weinat,0.0389785187,191.79.133.40,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n19816,7,445,2017-05-09 20:24:54,http://sanfordokon.io/bell,0.5917260737,44.137.199.100,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n19817,7,445,2017-05-28 10:00:01,http://wilderman.co/norene.tremblay,0.2974840065,248.194.38.227,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n19818,7,445,2017-01-19 04:23:07,http://wisoky.info/gardner,0.9560449658,198.133.88.32,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n19819,7,445,2016-12-24 09:35:57,http://kautzer.co/kris_hodkiewicz,0.3172784783,147.210.117.132,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n19820,7,445,2017-05-01 04:41:55,http://hudsonmedhurst.net/jeramie,0.0523580566,137.59.91.243,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n19821,7,445,2016-12-22 12:05:42,http://legroshuel.co/jonathon,0.1204294698,145.80.132.118,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n19822,7,445,2017-06-05 11:19:42,http://stammbechtelar.com/darion.rolfson,0.6069907292,166.35.231.216,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n19823,7,446,2017-04-05 05:28:22,http://cormierstehr.io/retha.hartmann,0.3667065477,71.19.106.211,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": false}\"\n19824,7,446,2017-03-13 23:35:21,http://haneparker.co/linnie_kris,0.9547454232,203.145.82.186,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": true}\"\n19825,7,446,2017-01-27 00:20:04,http://will.io/rollin,0.5814945055,156.129.62.207,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n19826,7,446,2017-02-08 10:36:41,http://schimmelhaag.org/greta.schultz,0.1718125307,228.197.201.81,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": true}\"\n19827,7,446,2017-04-22 01:53:13,http://daugherty.org/josephine_trantow,0.5788097915,53.49.38.232,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": true}\"\n19828,7,446,2017-01-18 12:57:55,http://okeefemcclure.com/romaine,0.8886257430,155.44.77.91,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n19829,7,446,2017-05-08 22:31:45,http://greenokon.biz/abagail.thiel,0.1704710643,140.182.237.9,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n19830,7,446,2017-03-23 13:29:38,http://lang.com/hobart,0.6497802762,89.72.169.40,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": true}\"\n19831,7,446,2017-04-13 20:15:28,http://schroederklein.biz/herminio_swaniawski,0.3538336421,228.19.222.60,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n19832,7,446,2017-02-21 03:16:43,http://green.org/wilfred.pfannerstill,0.1265019424,81.233.148.73,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n19833,7,446,2017-02-14 20:46:29,http://christiansen.io/akeem_aufderhar,0.6896098761,72.10.19.34,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n19834,7,446,2017-02-21 11:06:50,http://kelerstreich.info/malachi.murray,0.6059924951,113.228.93.73,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n19835,7,446,2016-12-19 01:42:39,http://bergstrom.info/lucius,0.0021521802,165.59.18.79,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n19836,7,446,2017-05-08 01:33:47,http://cummerataspencer.info/elian,0.5141668623,41.145.226.193,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n19837,7,446,2017-01-23 04:58:11,http://cronin.io/georgiana,0.3329181166,103.124.57.96,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n19838,7,446,2017-05-18 23:25:44,http://douglas.co/deven.keebler,0.5703182478,164.97.15.102,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n19839,7,446,2017-06-07 02:02:39,http://kuvaliskeler.biz/eino.nader,0.6702293997,182.66.123.13,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n19840,7,446,2017-05-09 12:37:34,http://ratke.io/jenifer.paucek,0.8629230367,9.235.185.95,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n19841,7,446,2016-12-24 03:15:15,http://anderson.org/nat.yundt,0.1465649514,210.37.90.20,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n19842,7,446,2017-04-15 20:29:17,http://schumm.io/hilario.frami,0.9571323686,203.179.70.201,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n19843,7,446,2016-12-29 20:05:49,http://bartell.biz/ora,0.2613275531,12.138.58.135,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n19844,7,446,2017-04-22 11:14:00,http://collins.name/domingo.lowe,0.5981523593,180.92.32.56,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n19845,7,446,2017-02-23 12:40:13,http://prosacco.co/corene.runte,0.2817227877,240.40.42.182,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n19846,7,446,2017-04-19 19:02:58,http://gottliebstokes.biz/murphy.bogisich,0.8451707631,43.209.231.132,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n19847,7,446,2017-02-11 10:51:20,http://borer.co/reuben,0.6474235584,223.61.102.158,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n19848,7,446,2017-06-07 04:23:49,http://barrowskrajcik.com/marion_langworth,0.9598765492,35.244.112.11,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n19849,7,446,2017-04-19 15:28:47,http://morarmueller.name/lavinia.block,0.2334105942,101.191.250.189,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n19850,7,446,2017-01-12 05:52:18,http://rathdavis.net/eva.white,0.4719264193,132.112.20.68,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n19851,7,446,2017-03-27 22:10:48,http://herzoggrant.info/ethelyn.breitenberg,0.6298324373,90.102.241.179,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n19852,7,446,2017-01-21 21:06:15,http://uptonbernier.co/tito.lynch,0.7123488604,118.113.55.145,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n19853,7,446,2017-05-12 00:44:10,http://rohanpadberg.biz/hobart,0.7575000473,147.228.41.201,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n19854,7,446,2017-01-26 06:34:26,http://dickisawayn.info/jimmy,0.8031834849,254.3.234.220,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n19855,7,446,2017-02-25 11:56:49,http://mueller.org/aylin.watsica,0.9631571700,129.91.244.245,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n19856,7,446,2017-06-04 04:28:43,http://kuhn.org/geo_schuppe,0.9151701771,23.15.84.167,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n19857,7,446,2017-01-18 05:08:27,http://pouros.io/wilbert,0.9358739806,174.235.233.156,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n19858,7,446,2017-05-11 13:26:12,http://zulaufshanahan.io/jameson.blanda,0.1341643463,120.32.140.144,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n19859,7,446,2017-04-11 13:51:56,http://nikolaus.co/lexi,0.3480416213,133.218.163.19,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n19860,7,446,2017-02-17 06:50:16,http://lubowitz.com/claire,0.2768121295,116.244.104.28,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n19861,7,446,2017-04-10 20:07:06,http://pfeffer.io/reina,0.1360233540,63.231.69.2,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n19862,7,446,2017-02-16 04:37:32,http://jakubowskiyundt.info/keegan.padberg,0.1973569759,132.89.67.127,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n19863,7,446,2017-04-11 19:55:02,http://nadertillman.org/jolie.johnson,0.5252482968,170.165.211.96,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n19864,7,446,2017-05-12 12:17:36,http://moen.org/salvatore,0.2847419581,128.241.231.28,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n19865,7,446,2017-03-16 22:49:46,http://trompheller.info/michale.gusikowski,0.0313025729,81.217.105.146,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n19866,7,446,2017-02-06 21:53:05,http://erdmanspinka.net/aron_cartwright,0.9304760863,103.172.139.245,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n19867,7,446,2017-01-15 14:26:40,http://gutkowski.name/rusty,0.0070562558,222.85.190.117,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n19868,7,446,2016-12-24 06:43:30,http://davis.biz/eleanore_borer,0.1668731722,10.55.30.112,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n19869,7,446,2017-04-05 23:36:43,http://ziemannmccullough.net/bryana,0.8867023634,121.163.121.208,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n19870,7,446,2017-01-12 04:20:50,http://bartell.org/harrison.nicolas,0.1254613271,8.137.67.123,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n19871,7,446,2017-06-08 06:01:02,http://balistrerigreenfelder.com/name.medhurst,0.2946881249,208.3.130.253,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n19872,7,446,2016-12-14 18:11:50,http://hodkiewiczankunding.com/magnolia.bogan,0.4638797704,94.175.85.159,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n19873,7,446,2017-04-30 16:28:17,http://waelchi.net/peggie_towne,0.8599837656,62.102.197.234,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n19874,7,447,2017-05-29 16:23:53,http://torphy.info/dallin,0.4853187048,243.101.197.226,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n19875,7,447,2017-01-11 05:30:32,http://dickens.com/benedict.mertz,0.6066742178,182.4.119.59,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n19876,7,447,2017-02-28 08:51:46,http://nolan.net/linwood,0.9610919964,68.26.142.225,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n19877,7,447,2017-02-10 19:01:43,http://mante.com/filiberto,0.5485873090,193.169.73.200,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n19878,7,447,2016-12-31 08:36:22,http://hoppe.com/devante.quigley,0.2536187848,224.116.115.72,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n19879,7,447,2017-02-12 21:30:04,http://okon.name/olin.huels,0.7775783715,132.89.14.81,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n19880,7,447,2017-03-31 07:37:10,http://stamm.biz/laisha,0.3784012303,214.76.141.164,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n19881,7,447,2016-12-26 22:15:55,http://bergnaumbarrows.com/edward,0.5780201590,144.92.206.251,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n19882,7,447,2017-02-03 00:26:15,http://gleasonwalker.org/waino.maggio,0.4221100525,83.81.14.168,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n19883,7,447,2017-01-29 17:10:38,http://ondrickaaufderhar.info/jadon_morar,0.2601126746,204.66.243.246,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n19884,7,447,2017-01-03 18:18:11,http://corkery.net/cleora_moen,0.6422126380,29.20.211.58,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n19885,7,447,2017-05-10 04:12:02,http://halvorson.name/rigoberto,0.7058206102,77.43.57.161,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n19886,7,447,2017-05-26 11:27:08,http://kihn.co/casimir,0.0104750345,28.205.61.149,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n19887,7,447,2017-01-09 14:14:48,http://gorczany.com/erin,0.5684985402,85.29.35.23,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n19888,7,447,2017-03-19 22:04:49,http://gorczany.org/melya.marquardt,0.2141204178,177.125.97.25,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n19889,7,447,2017-05-28 14:22:23,http://labadie.co/ozella_willms,0.6073482084,32.239.189.120,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n19890,7,447,2017-04-30 16:52:57,http://reichel.name/lincoln_damore,0.4872071079,110.214.183.142,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": true}\"\n19891,7,447,2017-01-21 03:57:58,http://trantowohara.net/natasha.smitham,0.1395539437,160.237.11.141,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n19892,7,447,2016-12-19 13:45:23,http://millerhagenes.info/kobe.langworth,0.7279191394,216.185.40.241,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n19893,7,447,2017-05-03 04:29:51,http://beatty.net/eulalia.lockman,0.9614330260,14.222.252.109,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n19894,7,447,2017-01-23 01:14:47,http://hodkiewicz.biz/christina_towne,0.2252828875,196.103.47.92,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n19895,7,447,2017-05-01 16:22:47,http://kreigerlesch.net/rodger.barrows,0.4883790397,202.80.91.107,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n19896,7,447,2017-03-05 18:35:39,http://huelmurazik.net/vida,0.5043971977,82.51.72.145,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n19897,7,447,2017-05-26 14:51:56,http://durganprohaska.net/jerod_tremblay,0.5449111217,22.33.161.241,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n19898,7,448,2017-02-11 23:28:14,http://handturcotte.info/zachery.swift,0.7221189231,204.154.35.48,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n19899,7,448,2017-03-26 09:50:56,http://mcculloughfritsch.com/maurice,0.3870437842,173.230.149.71,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n19900,7,448,2017-02-20 19:36:02,http://hettingerpfeffer.biz/darryl,0.1737565404,217.69.124.81,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n19901,7,448,2016-12-22 05:54:22,http://halvorson.co/beie,0.4867643153,57.11.171.90,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n19902,7,448,2017-03-03 17:49:24,http://uptonzemlak.net/jody,0.9110570063,193.38.14.127,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n19903,7,448,2017-04-24 08:46:09,http://lebsack.co/lexi.fahey,0.4971459958,124.105.237.43,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": false}\"\n19904,7,448,2017-03-25 22:01:26,http://okon.org/laurence,0.9195223876,4.71.68.76,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n19905,7,448,2017-02-21 23:01:17,http://schmelerhyatt.net/aileen.kutch,0.8250507837,200.11.135.57,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n19906,7,448,2017-03-09 04:43:02,http://wymanchristiansen.net/santina,0.6306117401,10.200.193.16,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n19907,7,448,2017-01-21 20:45:42,http://rowewisoky.org/mertie.simonis,0.7770441570,202.143.91.175,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n19908,7,448,2017-01-17 13:17:28,http://bechtelarsteuber.info/joaquin,0.7185593055,43.202.192.104,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n19909,7,448,2017-02-18 07:57:22,http://bernhardnicolas.biz/etha,0.2642945121,214.81.58.14,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n19910,7,448,2017-01-21 17:46:37,http://stoltenberg.net/camden.nitzsche,0.8832544668,243.21.48.15,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n19911,7,448,2017-06-09 01:43:55,http://simonis.com/gennaro.christiansen,0.5144353132,17.32.209.216,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n19912,7,448,2017-04-15 03:11:26,http://mayert.biz/elena.leffler,0.6475385900,110.103.93.109,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n19913,7,448,2017-01-19 14:09:50,http://mccluremraz.io/mable.lockman,0.0778479612,140.29.249.132,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n19914,7,448,2017-01-18 21:46:42,http://greenfelderparker.co/craig_boyle,0.4222004224,54.135.216.156,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n19915,7,448,2017-03-19 13:16:53,http://dicki.net/daphney.herzog,0.0518001983,85.108.236.181,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n19916,7,448,2017-01-14 00:05:02,http://gleason.org/leopold,0.1833823797,230.132.95.192,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n19917,7,448,2017-05-05 22:25:24,http://bayer.info/eric_renner,0.9466158122,200.113.76.50,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n19918,7,448,2017-06-12 11:03:19,http://mcdermott.info/jettie.blick,0.2235157309,232.74.162.13,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n19919,7,448,2017-05-28 01:27:38,http://armstrongdamore.name/sunny_keeling,0.7818557595,40.253.34.188,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n19920,7,448,2017-01-25 02:11:10,http://tillman.info/marian_ortiz,0.0203748274,130.231.197.171,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": false}\"\n19921,7,448,2017-03-18 02:26:15,http://brakus.com/tyrese,0.5434196556,232.45.93.109,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n19922,7,448,2017-05-06 06:39:11,http://kuphal.io/jett_leffler,0.3592694092,32.194.102.189,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n19923,7,448,2017-04-30 23:36:02,http://bartell.net/bryana,0.0341597810,111.107.176.230,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n19924,7,448,2017-05-11 01:08:24,http://goldnerwest.io/greg,0.6683598520,196.213.66.158,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n19925,7,448,2017-06-07 17:22:16,http://goodwin.biz/kyle.olson,0.4873611921,120.126.206.123,\"{\"\"location\"\": \"\"MW\"\", \"\"is_mobile\"\": true}\"\n19926,7,448,2017-02-13 22:02:50,http://hammes.io/susie.wunsch,0.7796090299,105.155.132.177,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n19927,7,448,2017-03-29 01:13:00,http://dach.name/matt.thompson,0.9522301570,44.223.226.149,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n19928,7,449,2017-03-16 12:59:39,http://oberbrunner.biz/marina.okeefe,0.2129032165,20.238.97.96,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n19929,7,449,2017-03-04 07:10:57,http://roobstamm.biz/sherwood,0.7309105783,252.174.137.198,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": false}\"\n19930,7,449,2017-04-21 14:07:57,http://harvey.org/lavonne,0.7884092824,190.36.54.4,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n19931,7,449,2017-04-07 12:41:18,http://volkman.net/chris,0.9489798984,31.176.81.100,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n19932,7,449,2016-12-19 16:42:13,http://hudson.info/elise_kautzer,0.2537434235,241.212.204.73,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n19933,7,449,2016-12-17 11:01:37,http://greenholt.co/millie,0.9520291122,60.235.216.33,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n19934,7,449,2017-04-18 20:33:44,http://leannon.biz/marty.braun,0.7310939616,51.100.173.64,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": false}\"\n19935,7,449,2017-05-01 00:45:31,http://balistreridaugherty.io/kirsten,0.6265857798,233.187.216.125,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n19936,7,449,2017-03-27 21:23:27,http://kulasjacobs.name/electa,0.4705129910,55.58.192.179,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n19937,7,449,2017-01-18 22:04:24,http://watsicabogisich.com/kaleb.ebert,0.4965878322,176.221.168.183,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n19938,7,449,2017-05-09 15:20:36,http://gerlach.org/cheyanne,0.2632492768,247.14.78.42,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n19939,7,449,2017-01-11 13:08:28,http://jaskolskijones.biz/florine,0.1617192583,27.14.235.66,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n19940,7,449,2017-02-28 01:24:56,http://abernathy.net/sherman_marvin,0.3822796959,80.169.20.96,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n19941,7,449,2017-03-22 22:59:14,http://torpmills.org/karson,0.3629335657,86.165.124.3,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n19942,7,449,2017-01-02 21:50:43,http://mcculloughwolff.co/domingo,0.4059612271,90.189.94.165,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n19943,7,449,2017-05-08 22:05:35,http://mertz.io/verda,0.5092581650,225.219.87.80,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": true}\"\n19944,7,449,2017-01-09 03:40:48,http://veum.org/lucio,0.0745150563,186.119.251.82,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n19945,7,449,2017-03-13 02:20:24,http://dickibahringer.io/madalyn_hyatt,0.8778706778,168.240.180.106,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n19946,7,449,2017-04-25 00:49:26,http://kuhn.org/ella.buckridge,0.5962503400,253.227.77.176,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n19947,7,449,2017-05-22 01:41:58,http://nader.io/dimitri_vonrueden,0.5228558376,3.115.126.165,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": true}\"\n19948,7,449,2017-03-22 02:50:29,http://dickenimonis.info/isabell,0.2877072266,151.169.214.9,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n19949,7,449,2017-04-15 04:30:22,http://greenholt.co/wava_paucek,0.6775401341,81.74.204.197,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n19950,7,449,2017-06-07 12:03:35,http://walkerjacobi.co/lenna.moore,0.8265959959,107.114.116.70,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n19951,7,449,2016-12-21 21:14:33,http://feil.io/felix_dubuque,0.3083414280,134.110.122.222,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n19952,7,449,2017-01-31 07:39:17,http://gorczanykiehn.org/esteban_farrell,0.9328444768,13.3.224.113,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": false}\"\n19953,7,449,2017-02-02 05:07:31,http://barton.biz/delbert,0.5715674792,99.203.214.251,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n19954,7,450,2017-01-07 16:22:54,http://schultzkeebler.net/justus,0.2628296107,173.93.14.109,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n19955,7,450,2017-01-09 08:59:28,http://harber.name/cordie_larson,0.7584927367,12.137.50.81,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n19956,7,450,2017-06-03 09:53:42,http://nicolasko.info/moises,0.1882253383,118.250.160.91,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n19957,7,450,2017-01-03 07:43:38,http://cristdurgan.info/cali.bergnaum,0.8794934766,201.27.253.235,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n19958,7,450,2017-02-12 09:24:31,http://skilescrist.biz/stephania.ryan,0.9068629577,88.79.248.228,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": true}\"\n19959,7,450,2017-03-01 10:58:36,http://ernserheaney.io/lavinia.reinger,0.4672795362,145.161.105.174,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n19960,7,450,2017-05-01 06:59:44,http://ryanbradtke.com/evelyn,0.6674609555,145.233.41.254,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n19961,7,450,2017-01-04 05:48:37,http://schiller.name/jed.quitzon,0.0650430190,188.108.51.139,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n19962,7,450,2017-01-22 10:55:30,http://williamson.info/candelario_mccullough,0.0221284619,235.5.136.133,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n19963,7,450,2017-01-18 05:44:28,http://sporer.info/katrina,0.1343091990,147.189.162.43,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n19964,7,450,2017-05-20 06:39:37,http://vandervortledner.co/mallie,0.2871714297,60.99.248.53,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n19965,7,450,2017-02-14 13:37:43,http://bernier.net/mia.conn,0.6312411967,7.133.154.137,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n19966,7,450,2017-03-13 09:57:37,http://denesik.co/rowan.rau,0.8957951575,48.17.94.59,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n19967,7,450,2017-02-06 17:13:48,http://purdy.com/maryam,0.3169947304,160.90.21.78,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n19968,7,450,2017-04-15 16:48:56,http://steuber.name/ramiro,0.7774363054,193.110.105.32,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n19969,7,450,2017-01-17 06:45:03,http://waelchihauck.io/rhianna.kertzmann,0.7321297659,107.10.125.9,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n19970,7,450,2017-03-26 15:07:31,http://gutmannmarvin.com/cary,0.1171158047,40.32.249.191,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n19971,7,450,2017-03-08 14:18:33,http://predovicfeeney.com/genesis_harber,0.1669495641,26.139.100.47,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n19972,7,450,2017-01-29 19:35:24,http://heel.org/rebeca.brakus,0.7818870765,158.98.240.92,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n19973,7,450,2017-04-26 05:13:48,http://simonis.biz/geo_fisher,0.6449607116,103.169.94.22,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": false}\"\n19974,7,450,2017-03-20 18:35:12,http://king.net/elinore_flatley,0.0874728442,144.130.208.204,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n19975,7,450,2017-03-01 10:50:31,http://kovacek.biz/gunner.treutel,0.5981250087,162.76.20.177,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n19976,7,450,2017-05-06 21:31:54,http://klockomckenzie.com/marian_langosh,0.8219454019,7.64.220.214,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n19977,7,450,2017-03-01 00:05:12,http://johnston.name/tamara.sanford,0.5540924125,238.110.174.75,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n19978,7,450,2016-12-27 20:29:04,http://jones.biz/domenic.steuber,0.1349992136,143.241.125.149,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n19979,7,450,2017-06-08 23:55:24,http://kertzmannkunze.io/wallace_rohan,0.1519256474,65.183.55.3,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": true}\"\n19980,7,450,2017-01-13 18:40:11,http://turnerfriesen.co/hillard_wilderman,0.9933214674,11.145.70.23,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n19981,7,450,2017-02-10 11:55:07,http://beatty.biz/roxane.quitzon,0.9463700267,40.169.43.67,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n19982,7,450,2017-02-06 10:41:31,http://corwin.name/santiago,0.4146215154,26.23.136.44,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n19983,7,450,2017-04-10 05:54:43,http://willmscrona.name/emelia,0.9025451064,218.250.32.17,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n19984,7,450,2017-05-26 04:42:23,http://kautzerkovacek.org/marianne,0.7684219259,214.243.51.175,\"{\"\"location\"\": \"\"LT\"\", \"\"is_mobile\"\": false}\"\n19985,7,450,2017-04-20 04:22:57,http://wiza.io/baby,0.8067559627,13.30.40.80,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n19986,7,450,2017-01-25 07:25:10,http://leffler.com/isidro_kutch,0.0566794168,206.193.41.44,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n19987,7,450,2017-05-15 08:16:17,http://baumbachcrist.net/jacklyn_rolfson,0.4459026923,230.114.110.73,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n19988,7,450,2017-03-26 15:26:08,http://lesch.name/zakary.mann,0.3461426800,143.163.140.179,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n19989,7,450,2016-12-14 21:52:15,http://bernier.co/rey,0.3787848710,102.31.23.72,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n19990,7,450,2016-12-17 13:28:20,http://barton.biz/lysanne.fay,0.1161963131,141.169.41.67,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n19991,7,450,2017-02-04 05:23:05,http://carterhuel.org/ernesto_connelly,0.1065653865,230.207.4.35,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": true}\"\n19992,7,450,2017-01-13 20:19:31,http://padberg.io/raphael,0.2628363620,246.248.243.123,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n19993,7,450,2017-05-07 23:45:09,http://boyer.org/blaise_tremblay,0.5134728018,223.195.76.191,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n19994,7,451,2017-04-12 12:39:30,http://pagac.biz/harold_gibson,0.5440416345,212.2.92.164,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n19995,7,451,2017-01-20 13:22:01,http://connellyschoen.biz/kenneth,0.8806951850,251.200.138.3,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n19996,7,451,2016-12-13 16:54:38,http://gerlachmitchell.io/shakira_weinat,0.8639770608,228.88.194.6,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n19997,7,451,2017-04-19 16:23:13,http://harber.biz/roie.waelchi,0.6698739063,173.165.74.235,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n19998,7,451,2017-05-28 13:04:53,http://sawaynhintz.io/vita.gibson,0.6503405346,34.89.154.240,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n19999,7,451,2017-02-16 13:57:37,http://bednar.info/bert_kaulke,0.7984595099,171.96.124.203,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n20000,7,451,2017-04-02 01:40:15,http://littel.co/emmanuelle_feeney,0.6234458740,152.136.251.193,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n20001,7,451,2017-03-25 21:58:07,http://schultzhirthe.io/rollin,0.6281701462,177.169.138.211,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n20002,7,451,2017-06-01 12:15:55,http://steuber.name/reta.waelchi,0.2598265238,100.7.135.194,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n20003,7,451,2017-03-31 19:28:09,http://pouros.net/laury_ryan,0.9459334348,183.43.66.197,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n20004,7,451,2017-04-18 13:40:15,http://kautzer.biz/bert_upton,0.1428861470,78.216.209.201,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n20005,7,451,2017-03-11 00:01:56,http://hamill.biz/valentine.feil,0.0279833966,23.226.40.221,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n20006,7,451,2017-05-11 10:15:37,http://wolfbernhard.name/freddy_krajcik,0.7809194400,89.238.228.207,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n20007,7,451,2017-03-02 16:05:40,http://simonis.biz/layne.mcclure,0.5716131155,152.81.134.198,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n20008,7,451,2017-06-01 23:56:06,http://parker.name/hillard_bayer,0.4223482891,110.40.212.149,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n20009,7,451,2017-05-09 07:07:53,http://ullrichernser.org/kailee,0.3798040057,108.138.168.33,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n20010,7,451,2017-03-25 02:42:47,http://bins.io/guy.schoen,0.4846568059,160.188.202.14,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n20011,7,451,2017-05-14 08:25:19,http://stehr.co/demarco,0.0175834674,141.111.245.171,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n20012,7,451,2017-03-26 18:53:44,http://conn.name/reagan_grant,0.3773185506,186.128.101.171,\"{\"\"location\"\": \"\"GH\"\", \"\"is_mobile\"\": true}\"\n20013,7,451,2017-06-14 00:25:27,http://barrowkiles.org/francesco.huel,0.7857109341,37.223.53.117,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n20014,7,451,2017-06-09 11:17:33,http://stiedemann.name/abagail,0.8073841135,181.57.103.212,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n20015,7,451,2017-02-20 11:48:27,http://bogan.info/arvid.schuster,0.5508920074,207.210.219.54,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n20016,7,451,2017-05-06 11:52:25,http://rice.com/ryann_koepp,0.4455253465,34.66.152.14,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n20017,7,451,2017-03-18 10:09:02,http://koepp.biz/eileen,0.8225260899,10.150.71.225,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n20018,7,451,2017-03-24 02:09:53,http://bosco.com/jevon_metz,0.9802093226,144.154.110.216,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n20019,7,451,2017-02-20 01:52:39,http://murazikdooley.name/werner,0.0532413387,160.236.223.141,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n20020,7,451,2017-02-01 00:42:31,http://koelpin.co/omer,0.4277268489,221.234.82.113,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n20021,7,451,2017-03-11 12:20:47,http://bashirian.org/georgette.denesik,0.8328098194,232.41.37.85,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": true}\"\n20022,7,451,2017-05-19 07:51:07,http://streich.co/rusty,0.1133982093,154.19.195.89,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": true}\"\n20023,7,451,2017-03-08 02:55:52,http://reichel.com/hayden_sauer,0.8795992360,130.54.83.202,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n20024,7,451,2017-01-11 20:53:26,http://zieme.io/carmel_abshire,0.5132044268,231.142.49.207,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n20025,7,452,2017-03-28 08:14:27,http://homenick.io/chris,0.1424929230,104.56.149.69,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n20026,7,452,2017-02-26 06:03:21,http://toy.org/fredy.wiegand,0.5638119411,209.84.251.163,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n20027,7,452,2017-01-22 19:18:58,http://daugherty.org/clay,0.1887266200,17.107.66.137,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n20028,7,452,2017-02-25 01:13:09,http://towneschinner.info/brenna.hegmann,0.2827146766,25.70.8.162,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n20029,7,452,2017-03-26 22:42:53,http://heller.com/scot,0.1901443920,246.130.252.188,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n20030,7,452,2016-12-30 22:50:52,http://considine.biz/hilda.kunze,0.3743318281,212.155.250.138,\"{\"\"location\"\": \"\"TZ\"\", \"\"is_mobile\"\": true}\"\n20031,7,452,2017-04-10 09:58:38,http://maggio.name/amina_casper,0.9970411664,144.8.152.117,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n20032,7,452,2016-12-22 14:40:38,http://ryan.biz/judah.oreilly,0.1769774798,147.56.20.90,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n20033,7,452,2017-04-26 09:30:32,http://morar.com/judge,0.7956837041,3.98.35.194,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n20034,7,452,2017-04-25 02:01:17,http://mitchellgreen.io/eduardo,0.6209708346,70.101.181.9,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n20035,7,452,2017-03-20 02:43:43,http://kilback.com/emmanuelle,0.8876753127,244.109.240.142,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n20036,7,452,2017-01-10 10:18:39,http://ward.org/loyal,0.6300397214,122.105.51.182,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n20037,7,452,2017-04-21 21:41:28,http://cain.com/eloisa,0.9798189819,251.144.218.67,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n20038,7,452,2017-06-14 03:45:08,http://mcdermott.io/dewayne,0.3147852888,59.218.36.164,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n20039,7,452,2017-03-07 05:44:11,http://bruen.co/hipolito.schmitt,0.9447708429,2.222.213.242,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n20040,7,452,2017-01-29 15:05:47,http://kovacekdibbert.net/geovanny.mayer,0.9911476526,44.105.208.242,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n20041,7,452,2017-06-12 16:56:51,http://goodwin.name/madeline_shanahan,0.1999867589,186.83.175.198,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n20042,7,452,2017-03-29 17:36:25,http://mohr.net/sofia.mills,0.2129477794,79.215.107.167,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n20043,7,452,2017-03-25 21:08:10,http://durgan.io/lucas.bernhard,0.3192161724,52.136.243.56,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n20044,7,452,2017-05-26 23:13:11,http://wuckertstehr.io/yvette_gerlach,0.1454363882,231.134.58.105,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n20045,7,452,2017-02-20 01:14:35,http://binsgrady.co/jackeline,0.4683177115,212.18.175.155,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n20046,7,452,2017-02-26 13:30:12,http://rowe.com/otha.rodriguez,0.8783672071,168.218.130.2,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n20047,7,452,2017-01-20 14:02:30,http://krajcik.org/fay,0.8833221651,199.82.112.94,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n20048,7,452,2017-01-06 12:43:05,http://kreiger.org/joana.senger,0.1355797065,184.231.155.41,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n20049,7,452,2017-01-31 13:48:26,http://lynch.org/barton,0.8994114064,90.122.20.184,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n20050,7,452,2016-12-26 12:34:00,http://konopelski.name/holden_lesch,0.9383141546,229.19.65.146,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n20051,7,452,2017-04-09 20:17:58,http://muraziklubowitz.com/cary.jaskolski,0.6933992315,234.137.62.200,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n20052,7,452,2017-03-11 01:08:29,http://walker.co/maggie,0.9656165120,179.222.108.36,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n20053,7,452,2017-02-27 09:11:56,http://franecki.info/micah,0.3667443720,101.204.47.141,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n20054,7,452,2017-04-30 12:28:25,http://anderson.name/dudley.jacobson,0.4348877830,59.217.171.212,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n20055,7,452,2017-04-02 03:52:09,http://treutelfritsch.name/lonzo.crist,0.8379667496,242.38.184.102,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n20056,7,452,2017-03-31 11:43:02,http://abbott.name/mikayla,0.5995996595,206.99.46.68,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n20057,7,452,2016-12-19 17:22:41,http://zboncak.net/bart,0.9064631899,21.65.206.111,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n20058,7,452,2017-02-25 19:37:58,http://greencronin.net/omari,0.8301432453,228.241.195.148,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n20059,7,452,2017-03-07 10:07:32,http://blickhintz.net/lavern,0.7210194921,38.199.163.250,\"{\"\"location\"\": \"\"FJ\"\", \"\"is_mobile\"\": true}\"\n20060,7,452,2017-02-07 07:16:52,http://stromansatterfield.info/alexys_ritchie,0.3101273641,63.215.204.217,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n20061,7,452,2017-05-09 00:23:23,http://waelchileannon.info/kendrick.hammes,0.3605867161,65.254.32.110,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": false}\"\n20062,7,452,2017-06-06 02:05:51,http://schultzbatz.co/moshe.stroman,0.2869869633,227.210.116.17,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n20063,7,452,2017-01-11 04:38:55,http://mraz.org/alva_lebsack,0.1529891853,10.7.245.171,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": true}\"\n20064,7,452,2017-01-21 12:57:28,http://olson.name/charlene.cartwright,0.1340270222,90.145.6.105,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n20065,7,453,2017-02-11 23:33:24,http://cruickshank.org/griffin_nikolaus,0.4607441393,222.211.56.151,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n20066,7,453,2017-01-23 06:23:06,http://dibbert.com/brendon,0.8738310337,42.186.190.189,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": true}\"\n20067,7,453,2017-04-10 23:56:32,http://hermistonlynch.net/cayla,0.8746677256,119.4.153.47,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n20068,7,453,2017-02-18 23:00:36,http://hillsziemann.io/scotty,0.9494868625,214.206.238.65,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n20069,7,453,2017-06-08 22:24:32,http://damore.io/ezekiel,0.5863183098,235.196.136.201,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n20070,7,453,2016-12-28 09:54:55,http://treutelgleason.co/izaiah_senger,0.3387897445,19.184.47.173,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n20071,7,453,2017-06-04 04:49:37,http://feil.net/ernesto.gleichner,0.2555174358,53.232.159.38,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": false}\"\n20072,7,453,2017-03-29 18:46:23,http://nienow.org/demarco,0.4842839511,199.87.56.55,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n20073,7,453,2017-04-07 14:19:14,http://medhurst.net/gilda.kovacek,0.0250914802,205.129.42.176,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n20074,7,453,2017-03-20 20:26:32,http://lueilwitz.com/cierra_pfannerstill,0.0363293071,172.234.22.161,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n20075,7,453,2017-05-23 23:06:00,http://gloverstroman.co/alvena,0.4313477803,3.4.144.111,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n20076,7,453,2017-06-06 04:49:14,http://lynch.biz/roy,0.7594560095,37.16.219.77,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": false}\"\n20077,7,453,2016-12-28 20:04:11,http://haleyrodriguez.info/shemar,0.8220964991,12.108.127.173,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n20078,7,453,2017-05-23 23:59:08,http://mosciskiprohaska.co/freddie,0.2646724330,219.159.51.216,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n20079,7,453,2017-03-09 09:57:59,http://weinat.net/gino,0.4402651395,81.5.154.45,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": true}\"\n20080,7,453,2016-12-26 16:46:47,http://beatty.co/jacey,0.8939643885,175.172.25.172,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n20081,7,453,2017-01-13 03:09:49,http://weber.info/keith.mitchell,0.6172424077,124.212.45.22,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n20082,7,453,2017-03-10 10:52:57,http://goldner.biz/daniela,0.2947625392,31.229.62.9,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": true}\"\n20083,7,453,2017-01-26 19:40:15,http://wiza.info/ray.zemlak,0.5645605074,209.123.102.34,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n20084,7,453,2017-02-11 21:43:58,http://stokes.biz/margie,0.0361391675,83.235.222.176,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n20085,7,453,2017-01-18 02:55:20,http://welchlangosh.co/terrence_lemke,0.0937365061,179.124.118.212,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n20086,7,453,2017-03-13 09:24:24,http://boyer.com/cydney,0.0133174499,87.60.182.218,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n20087,7,453,2017-02-17 16:27:21,http://simonis.info/teresa,0.7107941983,121.51.57.192,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": false}\"\n20088,7,453,2017-03-29 07:57:45,http://berge.name/roman.dooley,0.6078462708,206.133.225.179,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n20089,7,453,2017-04-27 13:15:54,http://hodkiewicz.name/kristoffer,0.8740169193,226.217.161.16,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n20090,7,453,2017-05-16 01:03:23,http://lueilwitz.name/desiree,0.5253260393,139.205.203.122,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n20091,7,453,2017-04-05 11:09:19,http://bernhardvolkman.io/elian,0.7442859638,16.251.182.238,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n20092,7,453,2017-01-28 02:54:23,http://vonruedenrowe.biz/agustin_durgan,0.0309683423,180.92.144.6,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n20093,7,453,2017-04-20 14:07:51,http://rippin.net/macey,0.7281940178,190.108.37.89,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n20094,7,453,2017-04-17 15:03:23,http://connelly.io/deontae,0.2874486315,154.224.43.165,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n20095,7,453,2017-04-18 03:16:55,http://andersonhickle.org/meghan_howe,0.3756739375,119.24.135.176,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n20096,7,453,2017-04-16 12:42:04,http://hammes.net/joanne_mertz,0.7094501702,92.109.153.225,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n20097,7,453,2016-12-14 14:20:02,http://walshsawayn.com/arvid,0.7286981762,6.52.174.161,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n20098,7,453,2017-05-02 12:27:18,http://block.org/yazmin,0.5549433935,192.176.87.127,\"{\"\"location\"\": \"\"VU\"\", \"\"is_mobile\"\": true}\"\n20099,7,453,2017-02-16 18:32:24,http://yost.io/kariane,0.0174151520,42.184.198.30,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n20100,7,453,2017-06-01 11:42:32,http://weinatsawayn.com/justice,0.9500469916,142.152.194.26,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n20101,7,453,2017-02-23 02:12:33,http://zemlakschowalter.io/verona,0.9096817670,110.46.78.178,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n20102,7,453,2017-05-17 21:46:51,http://schummmacejkovic.info/lily,0.0416177013,61.166.104.84,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n20103,7,453,2017-03-01 13:07:40,http://abbott.co/lauryn.schaefer,0.9427335595,217.98.78.23,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n20104,7,453,2016-12-22 22:12:40,http://kunze.com/elmore_hauck,0.4648129703,149.185.194.230,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n20105,7,453,2017-04-26 08:41:08,http://wardschmitt.io/orlo,0.3691511606,112.62.34.132,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n20106,7,453,2017-04-16 13:18:24,http://gleichner.info/carmen_oberbrunner,0.8216546483,193.40.172.107,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n20107,7,453,2017-01-16 23:20:21,http://hagenesoreilly.net/fiona,0.5072559274,161.113.123.39,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n20108,7,453,2017-01-01 10:53:10,http://padberg.net/evie_moriette,0.0733334962,9.139.211.196,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n20109,7,453,2017-02-14 08:18:41,http://kuhlman.io/herminio_marquardt,0.9697277641,74.89.75.158,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n20110,7,453,2017-01-25 19:44:07,http://robel.net/dagmar_schmidt,0.4481791212,152.136.208.111,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": true}\"\n20111,7,453,2017-03-17 05:40:33,http://bernier.biz/dorthy,0.0141854131,110.149.108.123,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n20112,7,453,2016-12-14 14:34:35,http://glover.name/isac,0.9657659499,169.90.109.201,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n20113,7,453,2016-12-20 09:09:38,http://blanda.biz/hobart_legros,0.9666211204,222.245.175.181,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n20114,7,453,2017-06-01 10:42:40,http://daugherty.name/hulda,0.9142201514,119.237.23.215,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n20115,7,453,2017-06-10 08:19:20,http://boehm.net/clement.nicolas,0.3721970375,113.68.164.233,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n20116,7,453,2017-01-22 13:32:37,http://greenfelder.info/will,0.8233371800,67.150.165.144,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n20117,7,453,2017-01-15 16:23:31,http://stamm.com/carson.schuster,0.6302664365,103.237.246.95,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": true}\"\n20118,7,453,2017-05-07 04:39:53,http://jacobskunze.co/allene,0.5180995441,237.188.52.214,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": false}\"\n20119,7,453,2017-04-08 13:19:04,http://wuckert.com/fidel,0.1314310999,33.154.85.220,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n20120,7,453,2017-02-15 22:13:34,http://konopelski.com/davon.weber,0.4867926662,233.61.33.12,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n20121,7,453,2017-04-12 17:51:05,http://hermiston.info/alvina,0.7969459883,202.96.111.168,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n20122,7,453,2016-12-25 10:04:32,http://spinka.net/valentina.schaden,0.3701627648,157.107.232.208,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n20123,7,453,2017-05-03 20:02:18,http://macejkovic.com/federico,0.9012986161,205.33.129.22,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": false}\"\n20124,7,453,2017-04-30 15:14:45,http://miller.io/devyn,0.4057740475,190.97.40.116,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n20125,7,453,2017-03-27 10:41:29,http://beer.co/charlene.rogahn,0.5842719900,52.141.53.60,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n20126,7,453,2017-02-17 18:34:35,http://starkernser.net/mollie,0.8425656158,224.225.41.60,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n20127,7,454,2017-04-13 07:05:21,http://dibberthansen.name/marjory,0.1565310800,175.23.149.200,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n20128,7,454,2017-05-16 06:57:51,http://bergstrom.co/darron,0.1641211412,249.146.58.46,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n20129,7,454,2017-04-18 10:52:30,http://schusterbode.net/henriette.hodkiewicz,0.4074724308,74.204.226.195,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n20130,7,454,2016-12-15 23:46:04,http://bogan.org/abdul,0.7283562527,136.121.132.56,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n20131,7,454,2017-04-22 08:22:52,http://heathcote.org/alvena,0.1507634904,60.3.10.140,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n20132,7,454,2017-04-14 11:17:02,http://leannon.info/donato,0.9600211824,171.17.164.11,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n20133,7,454,2017-01-15 19:54:52,http://lakin.name/isidro.hoppe,0.4312138574,148.70.128.2,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n20134,7,454,2017-04-04 08:34:10,http://olsonbogisich.org/makayla,0.9244004021,34.80.49.22,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n20135,7,454,2017-01-11 15:35:16,http://nienowgottlieb.io/eileen,0.4714690129,196.119.31.94,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n20136,7,454,2017-03-10 21:20:29,http://abernathy.info/lia.lockman,0.8269034430,175.183.36.33,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n20137,7,454,2017-01-22 11:20:21,http://keelingbradtke.co/lysanne,0.6742722468,139.25.70.53,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n20138,7,454,2017-04-10 17:58:17,http://hodkiewicz.net/brisa,0.8697056549,126.160.151.222,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n20139,7,454,2017-06-12 02:53:39,http://stanton.io/chris_homenick,0.8561129111,18.167.225.173,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n20140,7,454,2017-02-11 02:17:28,http://kreigerlindgren.org/verla_hyatt,0.2556469804,15.43.108.172,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n20141,7,454,2016-12-28 02:10:35,http://prohaskatowne.org/rosalinda,0.9100843647,200.164.17.3,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n20142,7,454,2017-05-07 11:12:04,http://lindgrenmcglynn.info/alysa_harris,0.4608370729,2.248.130.83,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n20143,7,454,2017-03-23 06:44:14,http://leffler.name/teie.koepp,0.6670016296,60.173.70.59,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n20144,7,454,2017-02-21 00:10:01,http://weberwiza.name/clinton,0.8077196244,227.202.111.219,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": true}\"\n20145,7,454,2017-05-15 14:54:00,http://price.com/jannie_kuhic,0.9154760413,68.39.163.252,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n20146,7,454,2017-03-01 16:36:49,http://dicki.name/coralie_hilpert,0.9028130353,77.229.5.127,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n20147,7,454,2017-01-04 19:31:14,http://reichel.biz/yadira.lockman,0.1412285795,16.182.12.5,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n20148,7,454,2017-02-17 02:07:33,http://connelly.name/sofia.johnson,0.1906207780,227.105.18.170,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n20149,7,454,2017-01-06 23:47:12,http://leffler.co/tiana,0.4133583026,154.222.232.66,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n20150,7,454,2017-02-06 01:01:47,http://parisian.io/hilda.nicolas,0.8138395467,174.244.203.152,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n20151,7,454,2017-04-12 09:09:13,http://okunevadietrich.org/rahul.bosco,0.8798152699,205.237.88.214,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": true}\"\n20152,7,454,2017-04-17 01:07:13,http://mertzbode.co/carlee,0.7923017025,228.100.172.5,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n20153,7,454,2017-01-21 00:26:29,http://heathcote.org/autumn_wunsch,0.2164194298,171.182.153.223,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n20154,7,454,2017-05-25 21:21:26,http://howell.biz/domenica.oberbrunner,0.6043591818,239.175.111.235,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n20155,7,454,2016-12-22 21:39:39,http://upton.biz/tamara,0.9098246152,246.227.44.17,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n20156,7,454,2017-02-17 14:05:13,http://jast.org/ottilie,0.3054036756,246.168.184.224,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n20157,7,454,2017-05-22 07:28:36,http://stanton.com/laisha.bayer,0.5141584358,62.214.126.58,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n20158,7,454,2017-04-22 08:57:23,http://boehm.name/shawna,0.9513482271,25.205.31.135,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n20159,7,454,2017-02-06 11:43:11,http://wyman.info/eunice,0.7745252937,105.96.87.233,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n20160,7,454,2017-02-28 05:59:06,http://kaulkedouglas.info/emmy,0.9069653640,32.253.237.101,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": false}\"\n20161,7,454,2016-12-31 06:44:24,http://nicolas.biz/mellie.beier,0.8867022296,4.98.229.212,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n20162,7,454,2016-12-31 17:33:00,http://goyette.io/benton.tillman,0.6652965874,62.192.181.112,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n20163,7,454,2017-02-27 21:39:50,http://yundt.com/marlin,0.6366144501,76.186.52.242,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n20164,7,454,2017-02-19 17:39:56,http://moore.biz/claud,0.2178176715,167.201.47.209,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n20165,7,454,2017-01-19 00:29:47,http://nader.info/brown.maggio,0.1294600693,203.107.35.59,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n20166,7,454,2017-02-01 16:35:19,http://conn.biz/leanne.eichmann,0.4520506909,20.236.24.239,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n20167,7,454,2016-12-22 21:13:53,http://hayesbayer.com/wilmer.deckow,0.7940482083,222.108.45.126,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n20168,7,454,2017-01-15 16:14:48,http://ritchie.co/alva.kshlerin,0.5832637203,87.161.181.70,\"{\"\"location\"\": \"\"BB\"\", \"\"is_mobile\"\": false}\"\n20169,7,454,2017-02-03 16:35:40,http://shields.co/arely,0.7162053792,224.6.159.152,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n20170,7,454,2017-03-13 05:45:24,http://kozey.net/frederik,0.4262684987,184.77.234.63,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n20171,7,454,2017-05-12 02:57:23,http://pfeffer.info/fritz_reichert,0.0864068490,222.97.116.85,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": false}\"\n20172,7,455,2017-02-11 14:43:34,http://rippin.io/meaghan_morar,0.5586492005,186.117.211.248,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n20173,7,455,2017-04-02 19:54:15,http://schneider.org/liana.lockman,0.0545489894,10.35.61.214,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n20174,7,455,2017-05-19 10:58:14,http://goyette.org/fabian,0.1162703469,250.218.44.192,\"{\"\"location\"\": \"\"GE\"\", \"\"is_mobile\"\": false}\"\n20175,7,455,2017-03-31 02:27:59,http://walter.name/fritz_brown,0.1336207366,70.163.75.129,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n20176,7,455,2017-02-25 14:35:56,http://hintz.co/nickolas,0.0219743970,126.141.127.72,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n20177,7,455,2017-01-11 22:20:25,http://breitenberghowell.org/israel,0.5248609106,189.198.97.19,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": true}\"\n20178,7,455,2017-01-06 05:25:27,http://schuster.name/salma,0.5663467380,196.37.73.113,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n20179,7,455,2017-01-21 21:14:40,http://jacobi.com/fannie.harber,0.0674846197,17.145.118.221,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": false}\"\n20180,7,455,2017-06-05 16:39:32,http://crona.org/rex,0.3245762393,111.254.114.248,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n20181,7,455,2017-04-28 18:11:09,http://streich.io/geraldine_parisian,0.9188970977,178.161.41.37,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n20182,7,455,2017-01-30 08:32:42,http://watsica.info/jey,0.2582170519,53.199.45.244,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n20183,7,455,2016-12-24 23:07:52,http://bartonwisoky.info/emmy,0.5895308892,253.135.243.212,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n20184,7,455,2017-05-22 11:38:46,http://treutel.biz/lela,0.9098635947,175.2.199.25,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n20185,7,455,2017-06-05 06:27:19,http://blanda.name/guillermo_lind,0.4952850720,216.236.51.133,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n20186,7,455,2017-02-13 04:09:43,http://murazikwalker.org/dale_murazik,0.0531864378,121.6.191.84,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": false}\"\n20187,7,455,2017-04-05 02:40:21,http://bergstrom.name/keara_ruecker,0.4068115930,72.193.58.151,\"{\"\"location\"\": \"\"KR\"\", \"\"is_mobile\"\": true}\"\n20188,7,455,2017-02-20 08:20:23,http://hintz.biz/chandler,0.2119566411,22.2.75.220,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n20189,7,455,2017-03-30 00:14:56,http://bosco.io/amy.gerhold,0.6234410175,235.18.119.119,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n20190,7,455,2017-03-21 20:30:27,http://crist.org/lorna.lindgren,0.6854845192,54.32.112.199,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n20191,7,455,2017-03-19 10:04:39,http://dubuquefritsch.net/chesley,0.3424936111,222.76.46.163,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n20192,7,455,2017-02-08 21:27:24,http://littel.com/melisa.sipes,0.0500693963,221.75.173.160,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n20193,7,455,2016-12-26 12:45:41,http://kohler.org/markus,0.9061969684,92.37.137.230,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n20194,7,455,2017-03-20 10:00:42,http://swift.io/katharina,0.0735210372,29.252.55.236,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n20195,7,455,2017-03-27 04:15:38,http://rau.info/hiram.nader,0.0560321725,237.207.56.23,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n20196,7,455,2017-05-27 10:23:33,http://swift.name/jazmin.feest,0.2244240667,173.133.196.83,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n20197,7,455,2017-02-24 07:40:27,http://dare.net/guy.walter,0.8386266935,122.186.83.161,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n20198,7,455,2017-05-17 10:44:07,http://bailey.biz/ebba.corkery,0.3326412491,71.153.117.200,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": true}\"\n20199,7,455,2017-01-01 22:33:49,http://rath.com/earl,0.5517931384,167.215.246.9,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n20200,7,455,2017-02-13 07:49:39,http://lockman.name/kyle.marquardt,0.4932212990,17.104.175.12,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n20201,7,455,2017-05-22 20:53:36,http://kelergutmann.net/nya.weinat,0.3892757578,228.14.192.215,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n20202,7,456,2017-05-22 15:05:02,http://ferry.name/henry_muller,0.1743677088,194.238.34.247,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n20203,7,456,2017-02-14 12:57:06,http://ortizrenner.org/carley_armstrong,0.0921361259,248.75.109.241,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n20204,7,456,2017-05-17 22:43:31,http://schinner.com/liza,0.4699390676,100.238.35.87,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n20205,7,456,2017-03-02 22:35:01,http://schiller.org/helmer,0.2887658823,241.179.75.48,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n20206,7,456,2017-04-19 20:18:21,http://stiedemann.io/genesis,0.6559949842,76.206.74.82,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n20207,7,456,2017-05-11 16:45:17,http://maggio.co/gwen,0.1717243132,92.156.4.117,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n20208,7,456,2017-03-29 18:43:43,http://kingrutherford.org/nikolas,0.4812353150,245.207.169.176,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n20209,7,456,2017-03-13 12:05:55,http://feil.co/adah,0.2548993199,50.72.184.115,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n20210,7,456,2017-02-12 14:09:30,http://quigley.name/jordy,0.3070709074,11.27.240.148,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n20211,7,456,2017-04-07 09:15:26,http://monahan.net/jazlyn_heaney,0.9937216016,86.21.175.7,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n20212,7,456,2017-03-06 12:34:37,http://rosenbaumcrooks.biz/callie,0.7145944057,110.202.31.243,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": false}\"\n20213,7,456,2017-03-14 07:19:38,http://stracke.com/lilly_deckow,0.4693122541,47.36.70.241,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n20214,7,456,2017-03-10 10:03:08,http://reynolds.name/keira,0.0073902591,141.186.254.129,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n20215,7,456,2017-04-07 08:57:44,http://streichbayer.org/sibyl_fritsch,0.3464954103,81.26.111.198,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n20216,7,456,2017-04-15 20:39:39,http://reingermarks.biz/marshall.herman,0.4342721112,212.249.103.178,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n20217,7,456,2017-05-02 23:42:43,http://white.org/charlie.roob,0.1703346430,81.106.83.95,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n20218,7,456,2017-01-09 10:55:26,http://dietrich.name/toy,0.7323955486,118.130.183.2,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n20219,7,456,2017-04-06 20:30:14,http://marvinbeatty.io/telly.yundt,0.2966351966,198.51.92.150,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n20220,7,456,2017-01-21 18:47:11,http://wilkinson.name/kelsie,0.7618833124,173.159.190.125,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": true}\"\n20221,7,456,2017-05-09 19:48:00,http://rohan.com/breana,0.2000101679,68.129.65.249,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": false}\"\n20222,7,456,2017-02-23 13:41:55,http://gaylord.co/buford.bayer,0.7077819160,172.139.87.181,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n20223,7,456,2017-03-28 16:35:49,http://wisokysipes.name/jeffry,0.2865335906,39.18.118.88,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n20224,7,456,2017-05-05 17:04:40,http://davis.biz/peter,0.2799199698,185.171.98.224,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n20225,7,456,2017-02-20 17:15:16,http://blick.name/damaris.pouros,0.6257435742,137.144.94.187,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n20226,7,456,2017-03-11 07:50:39,http://fadelkshlerin.org/lysanne.white,0.0526322371,76.207.175.144,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n20227,7,456,2017-02-01 22:15:17,http://gerlach.net/lesly.reinger,0.8827255840,141.156.97.23,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n20228,7,456,2016-12-29 11:23:40,http://haley.name/judy.halvorson,0.4398981705,202.180.48.99,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n20229,7,456,2017-01-27 10:34:39,http://heaney.biz/aida.dicki,0.7029109805,219.97.98.27,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n20230,7,456,2017-05-26 11:54:13,http://leannonlarson.com/nya.lynch,0.3203653383,161.220.47.224,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n20231,7,456,2017-01-18 04:17:58,http://bins.org/owen.labadie,0.8962039380,68.138.32.142,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n20232,7,456,2017-03-22 13:55:55,http://davichmidt.com/alia.king,0.6431296056,110.247.125.159,\"{\"\"location\"\": \"\"UM\"\", \"\"is_mobile\"\": false}\"\n20233,7,456,2017-04-26 16:12:15,http://hickle.name/shanna,0.3981366605,224.98.204.244,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": false}\"\n20234,7,456,2017-04-20 23:28:22,http://dickinsoncollins.info/andreane.torp,0.8995006630,180.120.41.20,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": true}\"\n20235,7,456,2017-03-25 15:48:36,http://daniel.info/sammie,0.6685174152,81.170.227.31,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n20236,7,456,2017-05-22 13:47:15,http://ondricka.org/mae,0.5953337714,127.96.50.206,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n20237,7,456,2017-05-29 20:46:28,http://koelpin.org/caitlyn,0.6383575951,66.159.63.4,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n20238,7,456,2017-02-10 19:53:05,http://conroyking.com/taurean,0.3126727791,183.77.167.42,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n20239,7,456,2017-05-09 14:44:36,http://gerhold.io/aron.bruen,0.8005662347,177.90.247.182,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n20240,7,456,2017-05-18 06:20:36,http://strackeherman.io/catharine,0.8874757705,168.82.92.216,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": false}\"\n20241,7,456,2017-05-13 23:45:11,http://gerhold.net/kavon_bernier,0.4526218795,205.149.94.115,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n20242,7,456,2017-01-22 20:52:12,http://gibsoncremin.co/rosie,0.2062231182,183.24.93.153,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n20243,7,456,2017-04-23 00:01:02,http://welchschinner.net/monserrat_quitzon,0.3895392350,102.171.252.12,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n20244,7,456,2016-12-14 07:31:55,http://oconner.com/rod_pacocha,0.6147645490,12.34.42.229,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n20245,7,456,2017-03-01 07:45:04,http://brekkegrimes.com/sincere,0.9589009777,40.47.142.33,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n20246,7,456,2017-01-30 12:13:11,http://marks.com/margaret,0.0277538902,211.203.106.251,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n20247,7,456,2017-04-30 12:46:26,http://kulas.name/lizzie,0.7284399782,77.114.195.212,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n20248,7,456,2017-03-15 00:04:20,http://conroy.com/aracely,0.3977206178,160.203.51.39,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": true}\"\n20249,7,456,2017-02-15 12:51:08,http://schultz.biz/kasandra,0.6958528259,103.228.27.179,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n20250,7,456,2017-03-09 07:39:12,http://wymandeckow.net/lisandro_smith,0.5488899606,101.171.193.128,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n20251,7,456,2017-01-18 21:25:17,http://haag.name/dusty.heaney,0.6282172027,239.251.83.34,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n20252,7,456,2017-03-04 22:14:14,http://schultz.com/destinee.farrell,0.5688629798,66.59.146.239,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n20253,7,456,2016-12-20 03:30:54,http://watsicadaugherty.org/herminio,0.6364837842,76.231.241.216,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n20254,7,456,2017-04-10 07:47:13,http://nader.io/elian,0.3942892598,175.240.39.68,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n20255,7,456,2017-05-18 23:09:11,http://schillerdaniel.com/marcella,0.7220398627,19.189.143.192,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n20256,7,456,2017-02-21 12:52:48,http://sauer.org/lisa.boehm,0.8721457538,116.175.231.204,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n20257,7,457,2017-03-07 00:43:54,http://schaden.biz/luciano.hermann,0.6167741499,106.152.135.147,\"{\"\"location\"\": \"\"IQ\"\", \"\"is_mobile\"\": false}\"\n20258,7,457,2017-03-31 09:25:06,http://damoresanford.biz/bailey,0.8873429687,44.95.126.10,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n20259,7,457,2016-12-16 05:29:35,http://ruel.io/scot,0.8724430277,36.158.239.110,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n20260,7,457,2017-02-05 20:18:07,http://metzgleason.biz/branson,0.9420530928,143.154.95.239,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n20261,7,457,2016-12-24 09:57:37,http://jerde.io/maxime_veum,0.6225101476,216.33.154.253,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": true}\"\n20262,7,457,2017-05-05 02:31:41,http://kiehn.name/giovanni.champlin,0.8721173080,111.182.192.50,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n20263,7,457,2017-04-14 10:54:47,http://welch.org/mekhi_gislason,0.7819502495,17.123.100.173,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n20264,7,457,2017-05-05 11:26:02,http://olson.net/hattie,0.4551393156,225.243.57.232,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n20265,7,457,2017-06-13 02:03:58,http://welch.name/maggie,0.6292586638,183.120.224.220,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n20266,7,457,2017-03-27 16:09:31,http://armstrong.biz/devan,0.3990196149,208.82.75.160,\"{\"\"location\"\": \"\"AT\"\", \"\"is_mobile\"\": false}\"\n20267,7,457,2017-02-02 03:38:11,http://goldner.biz/kolby_weinat,0.9789102236,239.152.236.228,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n20268,7,457,2017-05-02 23:47:28,http://wolftremblay.info/mafalda_ohara,0.3745720540,140.193.83.47,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n20269,7,457,2017-05-31 22:38:46,http://gerhold.org/clyde.schmidt,0.1667452667,32.9.55.254,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n20270,7,457,2017-05-04 07:17:09,http://streich.io/shakira,0.0227991730,211.58.77.243,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n20271,7,457,2017-02-18 05:23:42,http://gaylord.info/isai.satterfield,0.8544580987,91.98.247.48,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n20272,7,457,2017-04-26 17:21:07,http://ko.co/timmy,0.7454016816,87.218.111.101,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n20273,7,457,2017-04-14 18:55:35,http://wintheiser.co/araceli.jacobson,0.4213046456,119.75.219.19,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": false}\"\n20274,7,457,2017-01-24 06:44:21,http://mccullough.org/mathias,0.9141970249,41.95.183.185,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n20275,7,457,2017-05-30 03:43:50,http://jenkins.biz/pearl,0.9057703429,216.248.216.61,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n20276,7,457,2017-05-11 20:14:55,http://nitzschepfannerstill.info/yasmin,0.4880628804,40.119.248.137,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n20277,7,457,2017-01-04 18:59:46,http://west.name/patricia.beier,0.8904859017,135.218.230.44,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": false}\"\n20278,7,457,2017-01-07 09:11:12,http://crookscrooks.com/kaylie,0.0620780146,163.146.208.25,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n20279,7,457,2017-04-24 06:09:23,http://cain.biz/lucious,0.9276977672,249.72.132.230,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": true}\"\n20280,7,457,2017-01-04 06:42:55,http://schneider.io/malinda_kozey,0.4593138255,214.217.62.17,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n20281,7,457,2017-04-14 17:33:30,http://ruecker.org/leopoldo,0.8059134669,162.65.240.229,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n20282,7,457,2017-01-19 18:06:36,http://cremin.biz/dewitt.mckenzie,0.8035038445,253.153.102.143,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n20283,7,457,2017-03-20 12:29:35,http://dickens.org/evert,0.0600358872,125.205.164.6,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n20284,7,457,2017-05-12 05:40:02,http://stehr.co/carrie,0.2968682201,229.54.234.30,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n20285,7,457,2017-02-07 10:55:20,http://leffler.co/ethan,0.8722179280,16.112.41.70,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n20286,7,457,2017-04-19 18:19:55,http://reilly.io/ewald.dooley,0.3619664180,224.7.103.189,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n20287,7,457,2017-05-02 09:19:37,http://kuphalconn.biz/dedrick.ziemann,0.4901530372,111.177.166.254,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n20288,7,457,2017-05-10 08:27:10,http://boyle.com/meaghan_lueilwitz,0.8785970000,245.198.25.203,\"{\"\"location\"\": \"\"BS\"\", \"\"is_mobile\"\": false}\"\n20289,7,457,2017-03-21 13:45:42,http://nicolas.co/enoch,0.2733629075,30.102.6.168,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n20290,7,457,2017-06-07 07:05:42,http://grady.name/jayden.stokes,0.9570592343,22.199.68.223,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n20291,7,457,2017-01-19 22:06:57,http://schimmelwiegand.com/frances_lowe,0.3532811451,42.23.117.175,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n20292,7,457,2017-04-19 03:29:14,http://olsonschinner.name/nettie,0.1331167964,57.82.127.253,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n20293,7,457,2017-02-17 21:03:40,http://price.name/trycia.crist,0.1876498991,225.107.213.133,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": false}\"\n20294,7,457,2017-03-12 10:19:30,http://schaefer.name/annamarie.jast,0.0112510155,240.161.4.238,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n20295,7,457,2017-05-20 02:24:20,http://padbergromaguera.name/lambert.morar,0.6107560702,217.96.134.225,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n20296,7,457,2017-02-09 18:34:50,http://homenickgreen.com/glenna,0.3836935342,88.47.178.143,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n20297,7,457,2017-01-06 18:37:02,http://jaskolski.io/amaya,0.2778524067,56.108.176.44,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": true}\"\n20298,7,457,2016-12-24 07:44:53,http://haag.co/mabel,0.5197233285,3.27.187.104,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n20299,7,457,2017-02-28 01:12:36,http://beier.co/delphia,0.0173243047,241.132.9.241,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n20300,7,457,2017-03-19 08:50:59,http://okeefe.com/helmer.bechtelar,0.5460240228,168.62.10.175,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n20301,7,457,2017-01-28 18:00:07,http://considinepurdy.co/myrtis.mcdermott,0.0128894437,117.218.17.2,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n20302,7,457,2017-04-29 20:03:42,http://kozey.name/urban,0.6191559116,42.2.222.69,\"{\"\"location\"\": \"\"PY\"\", \"\"is_mobile\"\": false}\"\n20303,7,457,2017-06-10 01:43:34,http://fahey.co/shaylee_welch,0.4660586600,106.241.135.21,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n20304,7,457,2016-12-26 14:02:37,http://johnsonjohns.com/alfonzo_goldner,0.3881081057,171.114.83.75,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n20305,7,457,2017-04-23 06:04:05,http://denesik.com/kaylah_monahan,0.6611958271,139.148.228.84,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n20306,7,457,2017-05-07 01:00:13,http://macejkovicgoldner.org/malcolm,0.2656384495,136.119.15.31,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": false}\"\n20307,7,457,2017-03-29 18:08:26,http://crist.com/francesca_mertz,0.5860774145,20.212.156.55,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": true}\"\n20308,7,457,2017-02-25 19:02:55,http://gleichner.biz/camryn,0.1203447290,225.28.112.53,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n20309,7,457,2017-01-23 11:41:24,http://orn.net/modesto_mayert,0.5999962802,65.72.5.141,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n20310,7,458,2017-01-28 13:22:17,http://prosacco.biz/rogers,0.0657405008,24.241.139.171,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n20311,7,458,2016-12-19 13:45:30,http://lowe.name/francis_moore,0.4254576537,130.137.225.68,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n20312,7,458,2017-02-07 15:01:35,http://kunzemcclure.net/cody,0.3207187130,253.58.81.202,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n20313,7,458,2017-06-10 01:36:45,http://bauchsimonis.io/wilbert,0.8111343529,76.227.69.56,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": true}\"\n20314,7,458,2017-03-13 01:12:11,http://grimes.name/rebeka,0.8409745914,97.33.158.221,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": false}\"\n20315,7,458,2017-03-20 02:03:46,http://kreigermiller.info/marvin,0.3896437299,253.35.136.14,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n20316,7,458,2017-02-25 06:40:49,http://senger.com/candace.tillman,0.2352081350,97.25.26.72,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n20317,7,458,2017-02-02 17:49:01,http://schaden.info/mathew_crooks,0.8440698574,54.186.189.60,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n20318,7,458,2017-04-07 08:55:27,http://stantonwillms.biz/spencer_bernier,0.7410133146,61.214.217.204,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n20319,7,458,2017-05-27 06:31:30,http://gleason.name/karine,0.9333776410,72.231.242.13,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n20320,7,458,2017-06-06 06:18:27,http://oconner.info/general.sawayn,0.5574738281,156.254.208.187,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n20321,7,458,2017-02-18 23:54:52,http://welch.biz/franz.bayer,0.7232154333,229.166.229.39,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n20322,7,458,2017-02-24 06:25:46,http://williamson.io/cristopher,0.5693670831,179.156.11.32,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n20323,7,458,2017-06-09 01:30:16,http://hirthe.biz/jazmyn,0.9590525178,213.11.42.132,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n20324,7,458,2016-12-19 13:03:04,http://hermanbosco.com/pierre,0.0402623595,60.137.89.108,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n20325,7,458,2017-01-19 19:41:45,http://mayer.biz/uriah_stamm,0.8645220265,248.96.15.204,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n20326,7,458,2017-01-17 09:06:03,http://kuvalis.info/rosemary,0.3730481781,116.40.117.151,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n20327,7,458,2017-06-01 06:08:13,http://schulist.co/gilberto_botsford,0.9925948147,131.5.32.174,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n20328,7,458,2017-02-23 02:18:38,http://walker.name/winifred_wunsch,0.2924607865,128.162.220.116,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n20329,7,458,2017-03-30 05:41:15,http://eichmann.co/ali.witting,0.0678543480,248.128.86.219,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n20330,7,458,2017-05-18 18:33:12,http://raucorwin.name/lea,0.6077144856,115.123.125.32,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n20331,7,458,2017-04-11 14:24:03,http://ebertferry.co/alphonso,0.3853444098,12.26.105.154,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n20332,7,458,2017-02-17 15:07:50,http://halvorsonweinat.org/jairo.lebsack,0.1208609510,145.95.225.252,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n20333,7,458,2017-03-31 14:27:00,http://gibson.biz/junior_west,0.9877590382,36.155.253.241,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n20334,7,458,2017-06-11 19:53:39,http://bins.net/theo.gorczany,0.0930208852,162.13.253.77,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": true}\"\n20335,7,458,2016-12-27 04:18:01,http://langworth.io/clare.mohr,0.0895288133,190.244.72.69,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n20336,7,458,2017-02-15 16:59:26,http://grant.org/dewayne.mohr,0.0014907330,9.46.132.23,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n20337,7,458,2017-01-20 18:55:55,http://sawayn.com/moises.kuhn,0.6669614148,169.188.114.79,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n20338,7,458,2017-05-22 04:26:07,http://grimes.biz/catherine.mitchell,0.4073406215,155.180.40.211,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": true}\"\n20339,7,458,2017-04-07 14:41:00,http://thompson.net/selena_oconnell,0.9969953348,77.224.138.212,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n20340,7,458,2017-06-01 10:44:33,http://schultzgoldner.name/sim,0.2997667927,4.178.38.186,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n20341,7,458,2017-05-29 05:47:06,http://heller.info/clemmie,0.5421952694,147.231.249.112,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n20342,7,458,2017-01-01 21:36:31,http://deckow.info/esta,0.1213497887,198.172.173.17,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n20343,7,458,2017-06-09 14:46:18,http://bernier.com/cleo_torphy,0.0563993442,154.85.115.66,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n20344,7,458,2016-12-29 00:31:09,http://spinka.info/reece_ratke,0.6853576131,254.171.141.192,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n20345,7,458,2017-04-23 15:34:11,http://lindgren.org/milan,0.0127271542,233.61.165.197,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n20346,7,458,2016-12-25 04:58:05,http://wuckertgreen.net/perry.hudson,0.3514873832,145.8.18.96,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n20347,7,458,2017-02-21 15:32:43,http://wunsch.org/jolie,0.5658921886,28.98.88.66,\"{\"\"location\"\": \"\"PS\"\", \"\"is_mobile\"\": false}\"\n20348,7,458,2016-12-22 15:25:43,http://friesen.co/elenor,0.3841652827,64.57.56.21,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n20349,7,458,2017-03-22 01:51:51,http://nitzsche.name/kristoffer.kshlerin,0.1338506263,212.148.26.43,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": false}\"\n20350,7,458,2017-04-19 12:05:26,http://medhurstebert.co/veronica.littel,0.5460280565,117.87.19.239,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": false}\"\n20351,7,458,2016-12-14 19:07:06,http://keeling.info/ruthie,0.6273254582,166.139.204.110,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n20352,7,458,2017-05-14 02:09:49,http://beerthiel.info/ignacio_hodkiewicz,0.0864295128,185.103.248.77,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n20353,7,458,2017-02-16 15:21:48,http://windlermante.info/jerrod.schneider,0.1226761942,78.8.212.105,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n20354,7,458,2017-06-11 16:42:27,http://grant.io/norwood,0.5174488139,40.34.200.226,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": false}\"\n20355,7,458,2017-01-29 00:27:50,http://reillyblanda.co/percival,0.6916314315,33.37.70.92,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n20356,7,458,2017-05-02 01:47:01,http://collins.net/je,0.5151054317,242.185.33.67,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n20357,7,458,2017-02-13 04:00:50,http://howe.net/timmy.collins,0.9621941719,103.52.99.97,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n20358,7,458,2017-06-06 06:19:13,http://waters.name/jovanny,0.7453629153,173.84.244.162,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n20359,7,458,2017-02-18 16:38:17,http://heaneymorar.io/sophia.emmerich,0.9347496640,23.47.244.99,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n20360,7,458,2017-05-19 04:24:11,http://boehm.biz/deja.murphy,0.7469309241,162.2.166.234,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n20361,7,458,2017-06-07 10:00:11,http://zulauf.info/lorine,0.9367457762,143.53.18.66,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n20362,7,458,2017-02-01 19:58:10,http://hermanlegros.com/alek,0.9666201468,216.200.15.97,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n20363,7,458,2017-04-20 17:11:48,http://hickle.info/alexandre.parker,0.7718268294,171.80.97.78,\"{\"\"location\"\": \"\"ZM\"\", \"\"is_mobile\"\": false}\"\n20364,7,458,2017-01-22 23:13:20,http://turnerterry.info/maddison,0.7918600683,109.86.104.75,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n20365,7,458,2016-12-24 13:08:23,http://bartellbecker.info/brenna,0.9863855541,65.210.82.194,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n20366,7,458,2017-05-12 16:02:54,http://schroederjacobs.com/kellen,0.9644703944,205.42.8.213,\"{\"\"location\"\": \"\"CG\"\", \"\"is_mobile\"\": false}\"\n20367,7,458,2017-01-22 10:59:33,http://considine.info/jermey.bergstrom,0.1151994320,92.114.227.163,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n20368,7,458,2017-01-10 02:45:46,http://smith.net/myrtle.watsica,0.6692737881,72.58.117.12,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n20369,7,458,2017-05-02 15:38:24,http://kertzmann.co/isadore,0.1219855216,9.51.253.218,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n20370,7,458,2016-12-31 08:30:58,http://shanahanankunding.io/bertram_beahan,0.4694222791,49.69.192.133,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n20371,7,458,2017-06-08 19:14:39,http://weberhoppe.net/nola_kunze,0.9295167619,16.250.65.85,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n20372,7,458,2017-01-07 06:47:08,http://jones.net/raoul,0.3112580176,173.180.101.157,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n20373,7,458,2017-01-26 04:53:37,http://toy.org/pearlie,0.9240641680,85.110.30.94,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n20374,7,458,2017-03-09 02:30:05,http://vandervort.name/matteo,0.4318091509,195.43.181.39,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": true}\"\n20375,7,458,2017-02-26 22:44:55,http://stokes.biz/hope_bartoletti,0.6784227513,83.3.193.134,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": false}\"\n20376,7,458,2017-04-28 20:12:32,http://white.io/mariah,0.4668340881,102.88.248.81,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": false}\"\n20377,7,458,2017-02-18 08:18:28,http://cremin.name/haven,0.2192987253,62.246.214.148,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": true}\"\n20378,7,459,2016-12-27 11:09:44,http://macejkovic.org/alejandrin,0.1774726879,184.244.184.227,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n20379,7,459,2017-06-13 08:28:10,http://monahan.org/felicia.crist,0.6820175006,176.136.69.69,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n20380,7,459,2017-01-01 09:44:05,http://leschprohaska.net/stewart,0.0449240903,142.79.67.18,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": true}\"\n20381,7,459,2017-06-02 10:38:40,http://runte.co/dave_haley,0.5900471383,12.90.139.19,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n20382,7,459,2017-05-31 18:00:29,http://boyer.name/lance,0.6727171741,224.153.205.231,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": true}\"\n20383,7,459,2017-04-09 18:52:47,http://watsicaemard.org/sallie.raynor,0.9779469704,76.11.164.233,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n20384,7,459,2017-04-07 07:10:05,http://homenick.co/clair.volkman,0.6765359479,165.63.160.33,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n20385,7,459,2017-05-03 08:24:55,http://skiles.io/gordon,0.7105146827,236.30.44.216,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n20386,7,459,2017-01-23 07:43:41,http://hartmannreichel.co/avis_von,0.0903264332,153.52.105.65,\"{\"\"location\"\": \"\"SL\"\", \"\"is_mobile\"\": true}\"\n20387,7,459,2017-04-04 09:23:50,http://davis.io/trea,0.0921529047,94.159.157.113,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n20388,7,459,2017-02-22 13:26:41,http://romagueramoore.info/oscar.beahan,0.2345166377,92.100.97.126,\"{\"\"location\"\": \"\"TL\"\", \"\"is_mobile\"\": false}\"\n20389,7,459,2017-02-13 00:09:11,http://west.com/malachi,0.4894206282,107.125.48.86,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": false}\"\n20390,7,459,2017-05-20 19:16:46,http://gutkowski.org/wellington,0.8908165760,65.209.63.246,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n20391,7,459,2017-02-07 07:18:46,http://price.info/ralph.bartell,0.4980153918,73.176.67.156,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": true}\"\n20392,7,459,2017-06-04 09:56:03,http://nicolatreich.org/elenor,0.6610886802,240.54.170.163,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n20393,7,459,2017-01-24 19:49:31,http://thiel.name/graham,0.8864319514,104.215.240.8,\"{\"\"location\"\": \"\"SR\"\", \"\"is_mobile\"\": false}\"\n20394,7,459,2017-05-25 22:14:17,http://ryanturcotte.info/leslie,0.4107727447,229.73.214.227,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n20395,7,459,2017-06-03 12:21:01,http://gleichner.net/ro,0.0162763569,188.113.176.224,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n20396,7,459,2017-03-13 22:12:03,http://shanahanwelch.info/dexter,0.4370542766,134.99.28.246,\"{\"\"location\"\": \"\"BJ\"\", \"\"is_mobile\"\": true}\"\n20397,7,459,2017-02-28 10:22:15,http://klingconsidine.io/arjun,0.4670077191,142.88.250.50,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": true}\"\n20398,7,459,2017-02-19 21:37:50,http://konopelski.info/saige,0.1085647875,160.154.13.144,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n20399,7,459,2016-12-28 14:09:47,http://rempel.co/tevin_flatley,0.6153830220,212.101.9.103,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n20400,7,459,2017-04-14 20:28:54,http://hyatt.info/gilda,0.3952125807,251.187.201.160,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n20401,7,459,2017-02-19 04:21:26,http://lemke.io/ervin,0.3972757163,190.94.140.176,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n20402,7,459,2017-02-13 13:58:27,http://connelly.info/terrell,0.1489538814,224.21.91.200,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n20403,7,459,2017-02-03 05:08:46,http://andersonbergnaum.io/beth.bednar,0.8990156920,231.190.104.37,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n20404,7,459,2016-12-22 02:56:42,http://zulaufmoore.net/hertha,0.6900647882,27.251.8.202,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n20405,7,459,2017-06-02 06:02:56,http://adamsbarton.org/cole,0.3779920427,196.208.170.184,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n20406,7,459,2017-05-17 05:21:02,http://donnelly.org/zoe,0.1524159436,155.102.216.50,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n20407,7,459,2017-05-21 18:16:28,http://metz.io/jimmy,0.0086102309,46.39.143.144,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n20408,7,459,2017-02-13 21:14:06,http://kaulke.com/laisha,0.6655099225,97.250.22.164,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n20409,7,459,2017-04-18 12:13:08,http://hamill.net/darlene,0.0911324188,32.3.190.19,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n20410,7,459,2016-12-18 22:25:22,http://torphy.name/helga.fisher,0.1500730628,124.129.107.231,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": false}\"\n20411,7,459,2017-01-31 04:07:38,http://gleason.com/sedrick,0.8972606161,212.226.166.175,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n20412,7,459,2017-06-05 15:36:12,http://abbotthansen.name/beth,0.5555663342,78.212.245.56,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n20413,7,459,2017-05-21 13:07:10,http://boyerhermann.co/lennie.littel,0.7737225199,40.20.30.144,\"{\"\"location\"\": \"\"BG\"\", \"\"is_mobile\"\": false}\"\n20414,7,459,2016-12-29 09:59:06,http://rau.net/glen.mitchell,0.6071966051,128.216.142.213,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": false}\"\n20415,7,459,2017-04-24 09:27:57,http://williamsondibbert.org/magnus,0.9889825327,69.75.178.44,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n20416,7,459,2017-02-05 19:57:40,http://wisoky.com/sonny.moriette,0.2477858461,4.115.253.121,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n20417,7,459,2017-05-07 00:16:26,http://feeney.io/adrain,0.0856218038,194.167.144.25,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n20418,7,459,2017-01-25 10:46:14,http://pouros.co/lazaro.abbott,0.5513458384,49.20.178.123,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n20419,7,459,2017-04-12 01:58:29,http://west.org/rey.waters,0.9756855583,131.14.45.103,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n20420,7,459,2016-12-17 04:10:53,http://hicklehane.io/vernie,0.1004779556,225.157.238.250,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n20421,7,459,2017-02-01 13:04:03,http://orn.biz/gene,0.6231290446,168.216.64.153,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n20422,7,459,2017-05-16 00:13:45,http://stark.biz/josh,0.5422409734,174.208.227.35,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n20423,7,459,2017-02-26 04:55:38,http://eichmannkeler.info/aleandro,0.8350294634,47.250.211.82,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n20424,7,459,2017-02-16 03:51:42,http://murphy.com/okey,0.0640895028,2.195.92.42,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n20425,7,459,2017-06-11 16:19:33,http://senger.net/bell,0.8303497725,247.119.174.49,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n20426,7,459,2016-12-27 17:00:50,http://howell.io/graciela,0.1713042747,13.93.129.224,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n20427,7,459,2017-06-13 02:48:17,http://dubuque.info/muriel_bruen,0.6962711451,246.206.116.96,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n20428,7,459,2017-03-23 16:35:47,http://hirthewolff.info/laury,0.6494221374,147.29.147.188,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n20429,7,459,2016-12-26 02:47:51,http://aufderhar.net/dario,0.9293687293,71.7.241.70,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n20430,7,459,2017-05-27 11:06:24,http://franeckiheel.info/jaida_bogan,0.5134819747,226.32.206.214,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n20431,7,459,2017-04-06 16:12:13,http://parkerweber.io/ivory,0.7374426191,135.51.149.33,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": true}\"\n20432,7,459,2017-06-09 11:44:45,http://treutel.com/alexandro,0.7058545964,94.159.144.183,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n20433,7,459,2017-04-16 16:18:39,http://yostmohr.com/chet.mante,0.1301738510,35.102.91.59,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n20434,7,459,2017-04-26 19:18:17,http://gottliebwilliamson.info/junius,0.3781206306,210.250.104.248,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n20435,7,459,2017-01-01 22:14:07,http://murrayrutherford.net/emilia.parisian,0.7449360640,83.111.164.34,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n20436,7,459,2017-03-24 06:34:21,http://mosciski.io/isaias,0.2311649785,6.104.73.53,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": false}\"\n20437,7,459,2017-04-02 13:36:39,http://ferry.com/leta.hammes,0.4579389880,69.20.76.124,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": true}\"\n20438,7,459,2017-05-01 18:09:46,http://langosh.info/dovie,0.1712189808,69.155.160.71,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n20439,7,459,2016-12-29 22:42:05,http://fritschprohaska.info/bettie,0.2945224601,144.82.168.31,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n20440,7,459,2017-02-22 05:41:00,http://gutmannhuels.name/jonathan.emard,0.1711115879,173.204.97.43,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": false}\"\n20441,7,459,2017-03-04 04:20:29,http://buckridge.biz/wilford,0.9182525289,118.172.153.181,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n20442,7,459,2017-05-17 10:50:15,http://daugherty.org/axel,0.1158320210,224.39.114.31,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": true}\"\n20443,7,459,2017-05-19 08:20:09,http://gulgowski.co/fernando,0.4488181446,116.115.251.13,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n20444,7,459,2017-02-26 09:08:06,http://conroy.io/catalina,0.2548739728,117.43.66.25,\"{\"\"location\"\": \"\"BH\"\", \"\"is_mobile\"\": true}\"\n20445,7,460,2017-01-19 08:18:41,http://turcottewisozk.co/larue.bogisich,0.2698391726,147.112.218.54,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n20446,7,460,2017-01-29 00:04:06,http://powlowskikautzer.co/julien,0.4623388210,153.88.155.182,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n20447,7,460,2017-03-10 06:32:12,http://wuckertgulgowski.io/jammie,0.0545398921,236.52.166.112,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n20448,7,460,2017-03-21 16:13:47,http://jacobsbradtke.info/earl,0.7910042580,186.27.167.157,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n20449,7,460,2017-04-04 19:41:15,http://witting.net/laurine,0.8964527907,85.239.118.81,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": true}\"\n20450,7,460,2016-12-17 08:55:03,http://wuckerthowell.net/reed.walsh,0.6354823793,85.201.32.167,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n20451,7,460,2016-12-21 15:44:33,http://keebler.com/gielle,0.2660865892,105.186.170.213,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n20452,7,460,2016-12-16 07:14:05,http://parker.io/noah.johnston,0.7535968160,218.214.136.144,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n20453,7,460,2017-05-29 14:58:17,http://konopelski.info/valentin,0.8896195644,70.32.170.12,\"{\"\"location\"\": \"\"TO\"\", \"\"is_mobile\"\": true}\"\n20454,7,460,2016-12-13 06:43:42,http://mayert.name/jaren,0.8892370604,236.152.65.249,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n20455,7,460,2017-02-01 04:42:59,http://purdywisozk.info/terry,0.7025448649,43.2.176.98,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": false}\"\n20456,7,460,2017-02-20 09:46:02,http://durgan.co/geovanny,0.7074232174,217.9.164.216,\"{\"\"location\"\": \"\"WF\"\", \"\"is_mobile\"\": true}\"\n20457,7,460,2017-05-04 13:38:54,http://hodkiewicz.io/althea,0.4064072966,59.143.120.46,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n20458,7,460,2017-04-11 09:32:28,http://considinebeer.biz/electa.emmerich,0.5294936549,150.41.31.87,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n20459,7,460,2017-06-06 14:01:58,http://jacobson.biz/arnaldo,0.6940564876,207.66.16.10,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n20460,7,460,2017-02-21 22:52:36,http://hintz.info/cheyanne.kihn,0.6150241341,34.198.185.247,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n20461,7,460,2017-03-09 12:40:44,http://shields.com/kellen_heathcote,0.2668711472,105.41.92.229,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n20462,7,460,2016-12-23 09:05:59,http://kirlinsipes.net/mylene,0.8629622747,211.72.69.164,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": false}\"\n20463,7,460,2017-03-27 15:41:02,http://kertzmann.name/avery_gerhold,0.3141044919,246.232.103.147,\"{\"\"location\"\": \"\"LK\"\", \"\"is_mobile\"\": true}\"\n20464,7,460,2017-06-08 17:09:41,http://simonis.co/ignatius,0.0272649455,208.91.197.169,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n20465,7,460,2017-02-17 20:39:35,http://vonrueden.name/dustin_herzog,0.8886427298,172.57.78.139,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n20466,7,460,2017-01-15 01:45:30,http://wilderman.info/schuyler.shields,0.1869628904,114.18.166.142,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n20467,7,460,2017-04-23 12:56:01,http://kertzmannschneider.info/newton,0.1300107203,163.66.236.94,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n20468,7,460,2017-05-16 03:38:03,http://ruel.co/osborne_fritsch,0.5606247742,223.40.6.109,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n20469,7,460,2017-01-30 04:49:14,http://braun.name/enrico_gibson,0.8447333931,113.172.142.55,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n20470,7,460,2017-01-27 14:42:09,http://mitchell.co/dallas,0.5514177976,170.22.199.211,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n20471,7,460,2017-04-13 09:13:54,http://dietrich.io/howard_rolfson,0.4391790270,178.66.49.182,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n20472,7,460,2017-01-30 02:23:28,http://ritchie.info/buddy,0.4591446939,65.144.98.254,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n20473,7,460,2017-03-06 11:11:22,http://king.name/eulah_yundt,0.0958843669,52.86.134.19,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n20474,7,460,2017-01-24 11:39:39,http://cartwright.net/daija.zemlak,0.5299589132,165.217.219.143,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n20475,7,460,2017-05-23 14:33:25,http://romaguera.org/shemar_klein,0.5070393872,192.140.79.221,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n20476,7,461,2017-02-06 21:40:37,http://ward.io/asia,0.6765391541,221.196.166.58,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n20477,7,461,2017-02-19 15:29:18,http://stark.info/janelle,0.5408847258,61.98.158.165,\"{\"\"location\"\": \"\"RU\"\", \"\"is_mobile\"\": true}\"\n20478,7,461,2017-01-15 20:36:43,http://blockkulas.biz/eryn,0.6261763557,244.219.231.195,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n20479,7,461,2016-12-29 02:45:40,http://kochschinner.io/destany,0.3574212152,93.204.172.226,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n20480,7,461,2017-05-27 20:54:11,http://huel.info/hosea.rau,0.8361857777,5.46.69.52,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n20481,7,461,2017-04-01 22:47:47,http://wuckert.co/camryn.ruecker,0.0531465418,5.190.24.194,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n20482,7,461,2017-02-02 06:06:27,http://bayertremblay.com/brennon,0.7085416386,247.157.7.247,\"{\"\"location\"\": \"\"PA\"\", \"\"is_mobile\"\": true}\"\n20483,7,461,2017-05-13 23:50:01,http://weber.com/oran.langworth,0.0003302118,57.90.35.140,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n20484,7,461,2017-01-15 03:58:31,http://johnston.net/jennie,0.7260026923,186.27.168.5,\"{\"\"location\"\": \"\"SK\"\", \"\"is_mobile\"\": false}\"\n20485,7,461,2017-04-19 02:35:50,http://mcclure.info/benny_marquardt,0.3567910721,102.28.243.14,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n20486,7,461,2017-04-13 22:52:26,http://huels.com/myriam.langosh,0.5015630554,99.28.190.112,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n20487,7,461,2017-02-07 17:46:33,http://mayert.io/domenick_heller,0.2499664897,56.186.232.81,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": true}\"\n20488,7,461,2017-02-20 08:19:56,http://mraz.co/willie,0.7327423723,17.217.23.17,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n20489,7,461,2017-02-05 06:57:07,http://muellerhaag.io/leonard_bradtke,0.0534500874,76.228.148.246,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": false}\"\n20490,7,461,2017-03-21 19:36:14,http://schoen.biz/florida_bosco,0.1613674622,229.240.176.11,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n20491,7,461,2017-02-21 09:09:47,http://wuckerthauck.co/markus.murphy,0.8583326545,59.79.76.237,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": true}\"\n20492,7,461,2017-02-05 00:29:15,http://lefflerjacobs.org/dane,0.4065496298,117.248.254.128,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": true}\"\n20493,7,461,2017-05-01 09:28:15,http://rowekihn.name/aleandro,0.2963735846,68.111.87.233,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": false}\"\n20494,7,461,2017-02-22 07:23:07,http://keeblerquitzon.info/clark,0.9818972480,77.56.4.161,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n20495,7,461,2017-05-01 04:51:37,http://yundthettinger.co/blanche.jaskolski,0.3305986004,48.78.155.241,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": true}\"\n20496,7,461,2017-03-25 16:40:58,http://bartellswift.co/ashlynn.stanton,0.5924407664,74.169.13.192,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n20497,7,461,2017-04-20 14:22:21,http://breitenbergkuhn.net/riley.langosh,0.9478298058,163.24.110.11,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n20498,7,461,2017-03-27 15:12:18,http://zieme.info/garfield,0.1295235871,5.98.143.76,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n20499,7,461,2017-05-18 17:35:20,http://dietrich.net/fernando,0.6635082509,82.162.147.38,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n20500,7,461,2017-02-10 08:56:29,http://mcclure.co/lorine_cruickshank,0.9011319465,70.87.96.77,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": false}\"\n20501,7,461,2017-04-20 15:50:51,http://torpbins.io/lonnie_steuber,0.5839723854,101.145.112.146,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": false}\"\n20502,7,461,2017-03-17 19:15:15,http://marvin.co/landen.douglas,0.4869572251,153.126.193.51,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n20503,7,461,2017-02-06 05:17:08,http://blandareichel.co/elda_stroman,0.9242760249,130.133.84.107,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n20504,7,461,2017-04-25 15:25:58,http://smitham.io/layla.schmitt,0.4120661861,207.48.129.38,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": false}\"\n20505,7,461,2017-04-26 12:59:17,http://wyman.co/ian.metz,0.0861156669,223.66.84.155,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n20506,7,461,2017-01-22 06:18:19,http://turcotte.co/ivah.dickinson,0.8393131624,70.58.199.16,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": false}\"\n20507,7,461,2017-03-09 19:28:14,http://barrows.biz/kaley.medhurst,0.5347517250,219.117.166.241,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n20508,7,461,2017-06-13 07:02:37,http://abshirewillms.name/maida,0.0182511951,114.214.45.202,\"{\"\"location\"\": \"\"AW\"\", \"\"is_mobile\"\": false}\"\n20509,7,461,2017-04-29 17:19:02,http://barton.io/carey,0.3497663871,148.191.58.228,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": true}\"\n20510,7,461,2017-05-02 10:57:01,http://walter.info/taryn,0.6839685481,233.243.223.25,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n20511,7,461,2017-01-07 07:37:18,http://luettgenlabadie.com/sydnie,0.4022297931,148.227.29.202,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n20512,7,461,2017-04-05 14:30:26,http://adamswuckert.name/hope,0.3551192524,223.67.157.243,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n20513,7,461,2017-06-11 21:06:01,http://kirlin.info/lucas,0.4079809696,113.231.99.143,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n20514,7,461,2017-03-16 02:00:17,http://denesik.org/ramona_beier,0.3337569335,197.125.48.161,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n20515,7,461,2017-03-15 17:27:09,http://hartmann.com/ansel.kub,0.0357296385,78.77.13.13,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n20516,7,461,2017-03-22 00:31:53,http://baileywyman.io/devan_blanda,0.9705709396,76.190.123.91,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n20517,7,461,2017-06-13 17:54:40,http://mills.biz/isaiah_kaulke,0.5371540714,84.195.222.146,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": false}\"\n20518,7,461,2017-05-27 23:07:46,http://buckridgecain.net/stephen,0.2852246021,231.204.2.21,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n20519,7,461,2017-01-06 15:14:40,http://rippin.name/jaida.kulas,0.5903413466,132.248.155.110,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": true}\"\n20520,7,461,2017-01-04 02:07:21,http://treutel.name/maiya_nader,0.7262063338,122.165.61.193,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n20521,7,461,2017-03-03 04:41:51,http://mitchelldach.name/vesta.dibbert,0.2456912671,205.73.69.146,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": false}\"\n20522,7,461,2017-03-27 17:26:57,http://balistreriruecker.io/vita,0.5021253177,86.219.63.118,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n20523,7,461,2017-05-01 12:36:38,http://connelly.info/anibal,0.1770442761,31.168.204.4,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n20524,7,461,2016-12-16 04:39:10,http://dickioconnell.org/alexis_homenick,0.3526142148,223.2.37.52,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n20525,7,461,2017-01-19 21:11:31,http://botsford.net/marian.hirthe,0.0091201011,131.155.122.121,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": true}\"\n20526,7,461,2017-04-26 23:10:49,http://ko.co/parker_rodriguez,0.9452017858,246.228.50.2,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": true}\"\n20527,7,461,2017-04-13 07:21:32,http://abernathy.net/carolyne.johnson,0.1120538305,206.57.162.9,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n20528,7,461,2017-03-04 07:04:15,http://anderson.net/annie_schneider,0.0086994231,214.196.181.238,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n20529,7,461,2017-02-24 23:37:27,http://waterskutch.net/john_hyatt,0.5690397003,214.143.167.215,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n20530,7,461,2017-04-27 06:43:58,http://wintheiserhahn.co/dominique_bins,0.0094055585,144.63.78.30,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n20531,7,461,2017-03-02 06:25:22,http://kutchbernhard.org/berry_brakus,0.6843621181,105.210.148.25,\"{\"\"location\"\": \"\"ER\"\", \"\"is_mobile\"\": true}\"\n20532,7,461,2017-05-09 02:58:00,http://murphyupton.biz/laron_paucek,0.8117936878,150.253.8.21,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n20533,7,461,2017-04-23 01:43:53,http://wizaeffertz.biz/burnice,0.4603382976,168.242.85.214,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n20534,7,461,2017-04-23 07:00:59,http://murazik.org/mitchel,0.1117089435,136.220.178.41,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n20535,7,461,2017-05-19 03:25:09,http://swiftkulas.io/quinton.rau,0.7432072300,16.247.247.11,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n20536,7,461,2017-01-19 00:13:47,http://lehner.co/nicholas_thompson,0.7662858503,137.212.220.109,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": false}\"\n20537,7,462,2017-03-14 19:18:30,http://hauckkuhn.name/clara,0.0945343614,31.20.39.154,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n20538,7,462,2017-01-20 15:04:29,http://turner.co/jayda_boyle,0.0189378381,149.249.222.134,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n20539,7,462,2017-04-25 23:27:43,http://huel.org/carlo_dare,0.2271429562,252.137.155.177,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n20540,7,462,2017-02-26 17:07:55,http://wiegand.net/carleton,0.1513109462,120.143.127.161,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n20541,7,462,2017-05-19 16:26:12,http://vonruedencartwright.info/carolanne.daniel,0.4472891010,207.204.218.142,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n20542,7,462,2017-01-01 09:31:58,http://schadenwhite.com/ayana,0.7965623605,59.7.102.162,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n20543,7,462,2016-12-29 08:47:39,http://beatty.org/pat.stroman,0.3275208803,138.5.111.246,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": false}\"\n20544,7,462,2017-02-16 00:26:38,http://bashirian.biz/winona_damore,0.2162774051,69.27.214.245,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n20545,7,462,2017-05-30 18:16:46,http://dibbertsipes.co/selina,0.6960933800,20.145.72.183,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n20546,7,462,2017-05-21 20:09:33,http://hand.info/oliver_jaskolski,0.8898932803,8.68.171.18,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n20547,7,462,2017-04-16 04:16:23,http://harberschulist.info/minerva,0.5074218120,4.18.20.243,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n20548,7,462,2017-04-07 01:50:10,http://feestkunze.net/lucile.schneider,0.2704984208,183.117.241.147,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n20549,7,462,2017-05-26 05:34:00,http://hills.info/jermain,0.4381525080,233.111.169.138,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": true}\"\n20550,7,462,2017-04-07 03:21:34,http://jastgaylord.net/bernice.becker,0.6873713769,189.253.241.120,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n20551,7,462,2017-01-05 14:47:45,http://hettinger.org/christian,0.0030581784,193.248.141.89,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n20552,7,462,2017-05-05 03:33:33,http://hudson.biz/amos_adams,0.4273246033,240.20.123.253,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n20553,7,462,2017-05-09 07:38:57,http://kihn.co/general,0.7233097561,86.8.18.242,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n20554,7,462,2016-12-24 12:24:12,http://west.co/kris,0.1274979865,164.182.166.247,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n20555,7,462,2016-12-26 01:13:49,http://feestkonopelski.info/alexandro,0.3459427642,132.219.58.211,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": true}\"\n20556,7,462,2016-12-28 23:01:22,http://gaylord.net/laurine_treutel,0.7382684776,43.67.92.123,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": true}\"\n20557,7,462,2017-05-20 23:19:15,http://hilll.name/enola.moen,0.5800277757,162.47.242.174,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n20558,7,462,2017-01-14 00:49:55,http://swaniawski.name/evan.kunde,0.5380685195,55.118.229.119,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n20559,7,462,2017-02-17 11:10:08,http://ankunding.io/vincenza,0.4520818613,158.228.89.204,\"{\"\"location\"\": \"\"HU\"\", \"\"is_mobile\"\": false}\"\n20560,7,462,2017-01-26 09:53:19,http://franeckiwalsh.io/norris,0.4905584257,184.7.219.240,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n20561,7,462,2017-03-15 16:16:07,http://torphy.org/maiya_bauch,0.7583886916,5.236.214.31,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n20562,7,462,2017-06-12 08:05:39,http://price.name/ora_morar,0.3966139565,123.63.148.123,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n20563,7,462,2017-01-29 07:49:08,http://schamberger.biz/travon.mosciski,0.2538696618,164.18.4.4,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n20564,7,462,2017-02-05 18:49:40,http://auer.net/general,0.1411818528,248.47.197.131,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n20565,7,462,2017-06-10 13:14:07,http://wiegand.io/angelita.simonis,0.1805672116,171.57.30.142,\"{\"\"location\"\": \"\"AU\"\", \"\"is_mobile\"\": true}\"\n20566,7,462,2016-12-16 09:01:10,http://hackett.biz/casper_littel,0.8442826486,13.132.107.193,\"{\"\"location\"\": \"\"CI\"\", \"\"is_mobile\"\": true}\"\n20567,7,462,2017-03-11 10:52:27,http://bahringer.biz/mortimer,0.9796338545,54.94.109.247,\"{\"\"location\"\": \"\"GT\"\", \"\"is_mobile\"\": true}\"\n20568,7,462,2017-03-09 06:06:49,http://wiegand.biz/lula_bergstrom,0.6601253639,30.95.68.13,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n20569,7,462,2016-12-13 10:41:51,http://kovacek.info/ken_christiansen,0.4809446945,32.128.47.78,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n20570,7,462,2016-12-31 15:15:01,http://sauerohara.org/jordan.feeney,0.5583940497,169.187.155.7,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": true}\"\n20571,7,462,2017-04-05 13:25:51,http://mrazpfannerstill.org/nikki,0.1492373932,188.79.248.104,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n20572,7,462,2017-04-08 20:43:56,http://daniel.io/krista_blick,0.9297490862,20.192.158.179,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n20573,7,462,2017-02-20 15:24:17,http://hickle.biz/frank.bartoletti,0.9637539200,90.22.9.136,\"{\"\"location\"\": \"\"AE\"\", \"\"is_mobile\"\": true}\"\n20574,7,462,2017-02-18 20:26:48,http://williamsonohara.name/orion_glover,0.1713837552,19.191.38.163,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n20575,7,462,2017-01-18 17:15:47,http://okeefemraz.com/georgiana.buckridge,0.2002533224,203.99.187.8,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n20576,7,462,2017-03-10 23:38:41,http://farrell.co/hardy,0.6157458264,73.219.165.3,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n20577,7,462,2017-03-18 21:26:47,http://kunze.name/deshawn.kerluke,0.9332043449,139.218.167.67,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": true}\"\n20578,7,462,2017-02-19 14:47:36,http://bins.name/jacquelyn,0.8136325883,119.250.37.225,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n20579,7,462,2016-12-30 17:17:48,http://mccullough.info/jeffery_upton,0.8774578287,252.238.59.138,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": false}\"\n20580,7,462,2016-12-15 18:26:37,http://quitzon.com/clark.becker,0.5978482989,172.144.120.212,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n20581,7,462,2017-04-18 21:39:36,http://lubowitz.net/colin,0.4433748658,111.244.53.123,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n20582,7,462,2017-04-11 21:25:38,http://ankunding.net/coralie,0.7686556876,206.221.42.116,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n20583,7,462,2017-06-02 03:43:18,http://sauer.co/sylvester,0.6975777221,237.167.84.14,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n20584,7,462,2017-01-16 12:04:00,http://bauchledner.io/noel_feest,0.2816123062,214.177.193.166,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n20585,7,462,2016-12-28 01:42:23,http://runolfsdottir.io/gregorio,0.8456592720,20.71.245.24,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n20586,7,462,2017-06-07 15:26:38,http://weinat.biz/lamont,0.4464005877,67.219.221.24,\"{\"\"location\"\": \"\"NZ\"\", \"\"is_mobile\"\": false}\"\n20587,7,462,2017-02-03 22:52:18,http://klein.io/maya,0.1058497737,35.103.186.133,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n20588,7,462,2017-02-09 16:54:03,http://mertztowne.biz/brandon,0.4183055618,238.106.209.72,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": true}\"\n20589,7,462,2017-03-02 09:13:27,http://runtewolff.com/marian,0.2426991058,93.191.87.111,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n20590,7,462,2017-03-01 09:34:38,http://gerhold.net/ansley,0.7570914303,223.52.213.172,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": true}\"\n20591,7,462,2016-12-19 13:23:58,http://hyatt.co/annette,0.1724399421,33.154.234.164,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n20592,7,462,2017-02-12 02:35:19,http://kshlerin.net/jenifer,0.2654034205,109.2.97.179,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": false}\"\n20593,7,462,2017-02-16 08:33:04,http://brekke.org/allene,0.0022226707,111.139.196.233,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": false}\"\n20594,7,462,2017-06-04 22:50:49,http://lowe.io/louie,0.7293006218,243.172.118.220,\"{\"\"location\"\": \"\"LS\"\", \"\"is_mobile\"\": false}\"\n20595,7,462,2017-04-02 20:25:24,http://toy.biz/lew,0.3639540202,53.248.180.172,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n20596,7,462,2017-01-11 03:06:30,http://volkman.info/meaghan_witting,0.1048041616,230.177.241.83,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n20597,7,462,2017-02-18 04:43:38,http://klein.info/kurtis,0.8442176372,137.154.244.126,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": false}\"\n20598,7,462,2017-03-07 19:15:07,http://halvorsonschaefer.net/antwan,0.9091517907,27.220.193.58,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n20599,7,462,2017-06-05 00:40:30,http://hansen.org/clemmie,0.9348182343,118.74.106.106,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n20600,7,462,2017-01-11 06:37:14,http://cain.io/lance,0.9056704278,107.207.243.80,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n20601,7,462,2016-12-23 05:55:01,http://altenwerth.name/dell,0.0125544969,24.73.78.98,\"{\"\"location\"\": \"\"MF\"\", \"\"is_mobile\"\": true}\"\n20602,7,462,2016-12-16 02:36:07,http://dooleywilliamson.co/jayme_sauer,0.2843900681,217.26.177.118,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": true}\"\n20603,7,462,2016-12-29 08:06:38,http://wuckert.co/jey_braun,0.5935775258,5.107.237.128,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n20604,7,462,2017-01-07 22:16:56,http://ebertokuneva.biz/jamil.schroeder,0.3650511850,112.219.237.186,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": true}\"\n20605,7,462,2017-03-17 19:30:15,http://collins.org/rosanna,0.2961223934,150.86.210.149,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n20606,7,462,2017-02-20 17:55:47,http://tremblay.org/rhiannon.schmitt,0.4414758421,209.174.129.90,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n20607,7,463,2017-02-14 10:23:09,http://quigley.biz/stephon_white,0.1735824547,186.4.132.6,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n20608,7,463,2017-02-15 20:37:11,http://klingjacobi.name/michaela.bahringer,0.7096724095,171.244.203.91,\"{\"\"location\"\": \"\"KE\"\", \"\"is_mobile\"\": true}\"\n20609,7,463,2017-06-04 03:32:04,http://simonismarks.org/adelia.steuber,0.7940213578,75.185.73.111,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": true}\"\n20610,7,463,2017-05-20 03:46:33,http://hintz.co/austen,0.9490886976,179.19.116.47,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": true}\"\n20611,7,463,2017-01-24 04:17:43,http://rutherford.org/jordane,0.3172445363,164.185.161.38,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n20612,7,463,2017-01-28 10:02:42,http://lesch.net/molly.baumbach,0.2875007738,220.237.220.219,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": false}\"\n20613,7,463,2017-03-13 23:21:38,http://dare.net/cielo.goodwin,0.1771021143,4.87.27.183,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n20614,7,463,2017-04-03 02:10:46,http://braungreenfelder.info/taryn,0.5377825218,104.8.119.230,\"{\"\"location\"\": \"\"HT\"\", \"\"is_mobile\"\": true}\"\n20615,7,463,2017-05-18 13:04:24,http://bogantromp.co/noe.kub,0.7755756202,16.208.76.230,\"{\"\"location\"\": \"\"IR\"\", \"\"is_mobile\"\": true}\"\n20616,7,463,2017-03-07 18:31:28,http://boyle.net/eve,0.7181861309,254.185.237.29,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n20617,7,463,2017-04-15 09:09:14,http://oreillymccullough.net/alden_luettgen,0.6257164736,184.148.142.63,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n20618,7,463,2016-12-31 00:54:30,http://stokes.co/johann.okon,0.2142418431,140.153.15.11,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": false}\"\n20619,7,463,2017-02-06 16:22:16,http://rogahncasper.com/richmond_oberbrunner,0.7722902539,18.160.118.126,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n20620,7,463,2017-04-07 09:54:15,http://lind.com/aleandra,0.7527854663,55.46.46.200,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n20621,7,463,2017-03-30 18:20:44,http://oconnellstanton.org/vernie_heel,0.0180662454,151.206.166.117,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": true}\"\n20622,7,463,2017-04-03 14:27:43,http://kautzer.org/maxime,0.6472409280,30.89.82.33,\"{\"\"location\"\": \"\"QA\"\", \"\"is_mobile\"\": false}\"\n20623,7,463,2017-01-09 01:04:28,http://littel.com/nestor.leuschke,0.1875516591,119.80.188.112,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n20624,7,463,2017-01-12 13:20:59,http://schmeler.biz/antonietta.thompson,0.9366288977,87.229.135.221,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n20625,7,463,2017-05-21 17:16:53,http://weimann.org/monte,0.7200350545,61.189.149.19,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n20626,7,463,2017-03-17 23:20:25,http://kleinohara.name/korbin_ko,0.4157655493,98.189.175.63,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n20627,7,463,2017-06-03 13:20:08,http://yost.net/greta,0.0308760670,76.43.198.91,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": false}\"\n20628,7,463,2017-06-11 08:09:01,http://rogahnmcclure.io/ova,0.5492472117,134.93.93.175,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": false}\"\n20629,7,463,2017-03-23 19:04:16,http://bashirian.name/adonis,0.5797671717,208.66.83.212,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": false}\"\n20630,7,463,2017-03-24 08:28:53,http://pacocharomaguera.name/kurtis,0.5737046537,81.249.103.172,\"{\"\"location\"\": \"\"LU\"\", \"\"is_mobile\"\": true}\"\n20631,7,463,2017-06-05 05:12:31,http://mohr.info/neal.jaskolski,0.8590862698,242.115.187.207,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": false}\"\n20632,7,463,2017-02-14 08:23:02,http://howe.net/caroline,0.4039133147,220.20.167.87,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n20633,7,463,2017-05-22 13:33:21,http://yost.net/elenor_kihn,0.5459873248,245.54.159.66,\"{\"\"location\"\": \"\"GA\"\", \"\"is_mobile\"\": true}\"\n20634,7,463,2017-06-03 14:03:17,http://rodriguez.com/bertha.olson,0.7555875356,253.39.172.167,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n20635,7,463,2017-03-30 21:15:56,http://kerluke.name/travon,0.4969776711,75.241.106.191,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n20636,7,463,2017-02-24 13:28:24,http://beer.com/kayley.witting,0.3799572545,191.58.47.25,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": false}\"\n20637,7,463,2017-02-05 22:47:41,http://dach.co/alan.ortiz,0.0718670616,124.93.30.42,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n20638,7,463,2017-01-17 07:20:33,http://lueilwitzkeler.net/birdie.bahringer,0.6914998676,199.71.170.182,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n20639,7,463,2017-05-03 03:10:44,http://purdy.co/camilla_raynor,0.0177320347,93.128.96.41,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n20640,7,463,2017-06-02 23:04:58,http://swiftroob.name/gerhard,0.9323192100,18.30.204.130,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n20641,7,463,2017-01-10 00:00:32,http://sawaynschaden.org/royal.gutkowski,0.0679063670,124.135.181.218,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n20642,7,463,2017-05-22 13:04:15,http://weimann.com/kiley.kemmer,0.9475287651,126.188.215.106,\"{\"\"location\"\": \"\"ML\"\", \"\"is_mobile\"\": false}\"\n20643,7,463,2017-01-27 11:09:58,http://marvin.biz/della,0.8993254470,228.194.69.150,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": true}\"\n20644,7,463,2017-02-27 05:37:05,http://olsonheidenreich.info/mya,0.0348009912,45.228.105.223,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n20645,7,463,2017-04-01 05:23:18,http://herzog.biz/montana.rodriguez,0.6499917124,30.70.146.224,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n20646,7,463,2017-05-04 23:34:46,http://schmelerbuckridge.biz/sammie_abshire,0.0900730187,37.138.37.157,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n20647,7,463,2017-04-04 13:12:40,http://gutmann.co/ron,0.1088638050,193.225.49.15,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n20648,7,463,2017-01-30 16:24:26,http://larsonschinner.net/derrick,0.1253004166,223.235.238.197,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n20649,7,463,2017-01-01 10:49:47,http://kozey.co/eleazar,0.2521910634,222.125.6.3,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n20650,7,463,2017-01-08 01:47:22,http://vonmetz.info/albina,0.8138741353,222.197.175.197,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n20651,7,463,2017-01-17 22:44:31,http://nienow.io/orion,0.8481916212,125.31.73.9,\"{\"\"location\"\": \"\"GI\"\", \"\"is_mobile\"\": false}\"\n20652,7,463,2017-04-05 17:43:53,http://wehner.co/garth.jacobson,0.5647061458,188.46.162.14,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n20653,7,463,2016-12-14 03:45:27,http://romaguera.co/eliane.jakubowski,0.4067478331,48.76.3.89,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": true}\"\n20654,7,463,2017-01-08 13:05:34,http://lebsackheaney.info/patience,0.2398581857,64.254.23.115,\"{\"\"location\"\": \"\"EE\"\", \"\"is_mobile\"\": false}\"\n20655,7,463,2017-05-02 13:50:17,http://mante.org/dolores,0.3321204214,41.132.15.179,\"{\"\"location\"\": \"\"YE\"\", \"\"is_mobile\"\": false}\"\n20656,7,463,2017-03-07 14:46:39,http://heathcoteschmeler.net/gonzalo_fay,0.4141631719,144.173.204.83,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n20657,7,463,2016-12-20 11:49:08,http://kojohnston.name/green.oreilly,0.5889941318,86.142.225.39,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n20658,7,463,2017-03-26 19:13:42,http://shanahan.net/aimee,0.0273242832,210.198.235.95,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": false}\"\n20659,7,463,2017-02-07 22:06:57,http://kuvalis.io/rhiannon.conn,0.3894184426,244.42.68.146,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n20660,7,463,2017-03-30 00:22:32,http://gleichner.net/rodolfo,0.2631553983,12.41.243.217,\"{\"\"location\"\": \"\"CR\"\", \"\"is_mobile\"\": false}\"\n20661,7,463,2017-05-03 09:47:40,http://harber.name/brody_baumbach,0.6544719070,140.163.173.211,\"{\"\"location\"\": \"\"SD\"\", \"\"is_mobile\"\": false}\"\n20662,7,463,2017-04-01 07:59:54,http://pacocha.co/constance,0.7011394629,135.164.191.148,\"{\"\"location\"\": \"\"DM\"\", \"\"is_mobile\"\": true}\"\n20663,7,463,2016-12-20 04:49:14,http://price.net/magnus.barrows,0.2574064906,190.111.112.148,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n20664,7,463,2017-04-13 15:02:11,http://murphy.net/jeffrey,0.4464747018,249.172.10.95,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n20665,7,463,2017-06-06 02:11:50,http://kautzer.co/benny,0.4597787984,104.230.50.7,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n20666,7,463,2017-05-09 17:43:03,http://dietrichrolfson.net/ibrahim_olson,0.6298198025,211.208.4.111,\"{\"\"location\"\": \"\"AG\"\", \"\"is_mobile\"\": true}\"\n20667,7,463,2017-05-30 15:58:11,http://upton.com/crystel_thiel,0.0942367782,194.126.222.134,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n20668,7,464,2017-02-15 08:42:33,http://welch.net/micheal,0.7771281469,176.200.162.190,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n20669,7,464,2017-01-03 03:21:05,http://quigley.biz/savion.ward,0.9591071755,245.151.171.207,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": true}\"\n20670,7,464,2017-02-12 03:25:57,http://berge.org/arely_pollich,0.1123515732,154.198.47.226,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n20671,7,464,2016-12-28 17:53:03,http://vonrueden.info/scottie,0.9035246850,108.230.64.159,\"{\"\"location\"\": \"\"PT\"\", \"\"is_mobile\"\": true}\"\n20672,7,464,2016-12-28 02:26:49,http://kuhnschultz.name/felicia.marks,0.7880815778,251.182.146.194,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": true}\"\n20673,7,464,2017-06-05 08:38:31,http://veum.co/ariane,0.1594036039,136.227.201.46,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n20674,7,464,2016-12-22 01:48:16,http://rueckerharris.name/elda,0.2958373810,234.54.188.249,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n20675,7,464,2017-05-20 21:13:23,http://mohr.biz/mandy,0.0772311855,182.141.153.61,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n20676,7,464,2017-05-09 04:22:13,http://kuhic.io/johnny.mitchell,0.5028327726,218.170.31.201,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": false}\"\n20677,7,464,2017-04-06 07:08:50,http://greenholtwilkinson.com/oran.ohara,0.0526857484,144.142.125.249,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n20678,7,464,2016-12-26 16:17:17,http://stiedemannward.net/doyle,0.2296227008,205.25.97.213,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n20679,7,464,2017-01-13 15:11:05,http://prosacco.org/claudia_feil,0.5399573719,98.88.12.203,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": false}\"\n20680,7,464,2017-01-09 15:38:58,http://little.name/eulah.schultz,0.3012797400,142.61.146.4,\"{\"\"location\"\": \"\"NL\"\", \"\"is_mobile\"\": true}\"\n20681,7,464,2016-12-18 04:23:42,http://mrazkunze.io/jerad,0.9235316698,15.237.173.212,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n20682,7,464,2017-01-28 22:15:24,http://reynolds.com/joany_upton,0.6174983838,99.14.172.56,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n20683,7,464,2017-03-15 14:01:50,http://doyleokon.net/wilfred,0.8533102235,126.122.116.99,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": false}\"\n20684,7,464,2017-04-22 23:51:08,http://lakin.co/lizeth,0.4828241363,49.199.18.139,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n20685,7,464,2017-04-27 11:52:23,http://gerholdcruickshank.name/eleanore,0.4533873498,197.180.183.41,\"{\"\"location\"\": \"\"GD\"\", \"\"is_mobile\"\": false}\"\n20686,7,464,2017-04-23 06:49:03,http://gerholdquigley.org/afton,0.4959406843,106.213.161.18,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n20687,7,464,2017-03-30 16:27:41,http://donnelly.com/marina.gleason,0.1547354422,7.13.76.144,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n20688,7,464,2017-04-17 20:22:37,http://hirthedickinson.name/allie.wilkinson,0.7152977059,177.38.12.91,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n20689,7,464,2016-12-20 05:46:39,http://cremin.info/delmer,0.4385304038,80.34.49.160,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n20690,7,464,2016-12-21 18:37:52,http://tremblayerdman.info/sienna,0.0649381109,25.233.231.209,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": false}\"\n20691,7,464,2017-04-13 08:06:06,http://yundtwaelchi.org/wilfred_simonis,0.0447212653,156.205.96.79,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n20692,7,464,2017-05-16 04:55:26,http://mohr.io/reyes,0.5253013240,128.245.184.29,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n20693,7,464,2017-01-22 04:55:29,http://conroymacejkovic.org/misael_bartoletti,0.5741443610,67.207.159.106,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": true}\"\n20694,7,464,2017-02-26 20:30:13,http://wuckert.name/alexane,0.0317358356,32.193.90.10,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n20695,7,464,2017-02-14 19:25:54,http://runte.biz/wilmer_rodriguez,0.9082299032,150.246.141.19,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": false}\"\n20696,7,464,2017-05-26 05:02:25,http://roob.io/hollie.mueller,0.0618998819,40.183.249.9,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n20697,7,464,2017-02-13 11:53:35,http://schamberger.com/soledad.thiel,0.7355551218,122.50.111.171,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n20698,7,464,2017-01-23 09:27:15,http://wolff.io/alexzander,0.5201348082,126.136.233.27,\"{\"\"location\"\": \"\"BV\"\", \"\"is_mobile\"\": true}\"\n20699,7,464,2017-01-29 14:56:09,http://rau.biz/taya,0.2621586861,112.163.158.236,\"{\"\"location\"\": \"\"GP\"\", \"\"is_mobile\"\": false}\"\n20700,7,464,2016-12-19 05:58:54,http://botsford.org/mohammed.mclaughlin,0.9708658472,40.168.35.243,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": false}\"\n20701,7,464,2016-12-26 01:32:21,http://hermann.info/salvador_gutkowski,0.7178655437,102.84.178.65,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n20702,7,464,2017-03-31 04:13:41,http://brekkedickens.com/karley,0.3246634590,213.45.25.142,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": true}\"\n20703,7,464,2017-05-30 05:55:54,http://ruel.org/jody.kaulke,0.6204214454,150.193.70.213,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": false}\"\n20704,7,464,2017-04-06 15:55:59,http://ondricka.com/violette.jakubowski,0.0856653617,226.161.17.210,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n20705,7,464,2017-03-12 15:29:34,http://stanton.co/drake,0.7658475616,187.195.142.70,\"{\"\"location\"\": \"\"TH\"\", \"\"is_mobile\"\": true}\"\n20706,7,464,2017-05-18 09:43:39,http://lockman.biz/ward,0.6234949636,200.83.180.249,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n20707,7,464,2017-06-11 01:17:46,http://marvinbraun.info/keshawn,0.4227261939,144.222.99.243,\"{\"\"location\"\": \"\"CA\"\", \"\"is_mobile\"\": true}\"\n20708,7,464,2017-05-27 00:45:33,http://upton.biz/theodore.beer,0.6159790710,73.104.69.23,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n20709,7,464,2017-05-10 16:36:11,http://purdywintheiser.biz/thomas,0.4599664841,14.253.131.89,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n20710,7,464,2017-05-22 00:35:59,http://goodwin.biz/elwyn,0.2934692156,180.189.118.154,\"{\"\"location\"\": \"\"SC\"\", \"\"is_mobile\"\": true}\"\n20711,7,464,2017-01-07 01:53:33,http://davis.net/stacy_skiles,0.3973370525,154.112.228.206,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": false}\"\n20712,7,464,2017-01-04 00:25:19,http://moore.org/helmer,0.9526056056,118.87.21.235,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": true}\"\n20713,7,464,2017-05-24 04:13:09,http://kuphaljones.net/kendra,0.1183698339,249.12.217.37,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": true}\"\n20714,7,464,2017-01-31 09:28:05,http://lynch.com/libby.feeney,0.5188299417,237.112.174.64,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n20715,7,464,2016-12-21 15:20:14,http://pollich.io/elroy,0.0680576374,183.134.211.128,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n20716,7,464,2017-01-28 16:34:16,http://schamberger.info/kiel,0.1322103981,244.181.184.249,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": false}\"\n20717,7,464,2017-05-30 16:29:35,http://legros.io/hugh_block,0.3853684895,210.128.28.196,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n20718,7,464,2017-04-16 22:22:11,http://dachauer.info/carmine,0.4245075758,248.37.176.108,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n20719,7,464,2017-06-08 20:20:07,http://baumbachmosciski.biz/orland_homenick,0.7124332814,225.227.206.238,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n20720,7,464,2017-05-21 16:09:14,http://fisherjakubowski.org/reggie.satterfield,0.4259934313,133.193.173.210,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n20721,7,464,2017-01-01 23:59:39,http://stark.com/eugene_mertz,0.0336354613,169.93.126.25,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n20722,7,464,2017-06-06 05:45:10,http://conn.co/godfrey.davis,0.2674962451,157.54.193.70,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n20723,7,464,2017-04-11 11:43:56,http://boyer.info/stanton,0.5098841318,128.221.222.121,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n20724,7,464,2017-06-13 20:20:52,http://wehnermann.org/werner_powlowski,0.5678745076,7.60.250.53,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n20725,7,464,2017-01-21 12:59:30,http://dicki.biz/hattie,0.4277350558,198.127.197.182,\"{\"\"location\"\": \"\"YT\"\", \"\"is_mobile\"\": true}\"\n20726,7,464,2017-02-05 00:40:24,http://stehrrenner.org/janae_trantow,0.1961715293,130.72.19.225,\"{\"\"location\"\": \"\"HN\"\", \"\"is_mobile\"\": false}\"\n20727,7,464,2017-02-28 22:00:42,http://volkman.com/yazmin_reinger,0.9128622028,194.254.11.188,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n20728,7,465,2017-03-20 12:57:56,http://erdmankertzmann.io/alexandre.pouros,0.8668532803,25.108.196.116,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n20729,7,465,2017-02-26 16:52:39,http://mayertstiedemann.name/fred_bahringer,0.2201874422,102.126.52.197,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n20730,7,465,2017-03-15 09:57:10,http://harvey.co/august_dooley,0.1024390323,102.65.199.132,\"{\"\"location\"\": \"\"MU\"\", \"\"is_mobile\"\": true}\"\n20731,7,465,2017-04-23 14:09:33,http://schmeler.org/anna_roob,0.2405552519,195.8.38.242,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n20732,7,465,2017-05-29 04:21:53,http://moorebotsford.io/khalil_schamberger,0.7413494407,157.37.46.11,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": true}\"\n20733,7,465,2017-01-26 12:59:19,http://effertzmuller.net/rowan_reichert,0.7478708921,194.51.59.247,\"{\"\"location\"\": \"\"PL\"\", \"\"is_mobile\"\": true}\"\n20734,7,465,2017-01-07 03:15:54,http://keeling.info/lenny.carroll,0.6506244590,157.89.200.132,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": false}\"\n20735,7,465,2017-04-23 12:20:46,http://kohler.co/juliana,0.7163178441,87.52.56.53,\"{\"\"location\"\": \"\"ST\"\", \"\"is_mobile\"\": true}\"\n20736,7,465,2017-05-21 05:43:32,http://lehner.io/ettie_kohler,0.2861530705,49.50.184.57,\"{\"\"location\"\": \"\"DJ\"\", \"\"is_mobile\"\": false}\"\n20737,7,465,2017-04-01 06:55:57,http://schambergermoen.net/eleanora,0.6832681426,156.214.230.197,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n20738,7,465,2017-04-23 03:16:01,http://boyleschaden.org/kira.frami,0.7757851649,199.225.126.210,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n20739,7,465,2017-04-15 01:31:53,http://satterfieldtrantow.biz/zackary,0.1451707386,117.251.108.222,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n20740,7,465,2017-03-10 10:00:41,http://price.org/lamar,0.3663464751,31.118.18.12,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": false}\"\n20741,7,465,2017-05-29 01:03:51,http://steuber.co/maryam,0.3634874806,119.147.174.69,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": false}\"\n20742,7,465,2017-02-17 22:02:58,http://conroy.org/eulalia,0.7318829594,55.117.225.44,\"{\"\"location\"\": \"\"EH\"\", \"\"is_mobile\"\": true}\"\n20743,7,465,2017-01-11 13:30:23,http://boyer.name/charlie,0.2709582728,196.207.97.204,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n20744,7,465,2017-04-21 15:45:09,http://pouroskohler.net/earnestine,0.7666231048,53.157.53.9,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": false}\"\n20745,7,465,2017-06-13 00:33:27,http://hansen.info/liza,0.2322296365,237.246.21.245,\"{\"\"location\"\": \"\"CD\"\", \"\"is_mobile\"\": true}\"\n20746,7,465,2017-02-05 18:24:16,http://rutherfordfadel.name/heloise.schoen,0.1777425800,227.110.173.208,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n20747,7,465,2017-04-15 23:59:59,http://lynch.io/johnny_luettgen,0.5583506265,208.173.21.118,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n20748,7,465,2016-12-24 19:27:31,http://dach.co/benton,0.8467192068,250.51.235.236,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n20749,7,465,2017-03-22 09:34:31,http://mrazhayes.org/jazlyn,0.4312089758,155.113.53.186,\"{\"\"location\"\": \"\"CV\"\", \"\"is_mobile\"\": false}\"\n20750,7,465,2017-01-29 12:00:40,http://andersonveum.com/daren.kunde,0.0700273867,124.181.166.154,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n20751,7,465,2017-01-09 14:44:55,http://herzog.name/vince,0.8235911405,151.201.122.247,\"{\"\"location\"\": \"\"AR\"\", \"\"is_mobile\"\": false}\"\n20752,7,465,2016-12-27 09:35:09,http://legros.com/vern,0.3227479276,48.171.94.124,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n20753,7,465,2017-04-28 17:29:20,http://medhurst.name/nash.mckenzie,0.4346939075,168.109.44.71,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": true}\"\n20754,7,465,2017-01-02 13:18:45,http://grady.info/ian_bergnaum,0.6576590651,190.228.251.237,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n20755,7,465,2017-04-18 00:45:28,http://kiehn.biz/roxanne,0.6556239678,218.55.94.78,\"{\"\"location\"\": \"\"KW\"\", \"\"is_mobile\"\": false}\"\n20756,7,465,2017-03-23 09:50:50,http://wehner.com/mayra_nikolaus,0.1900667676,250.58.129.134,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": false}\"\n20757,7,465,2017-02-14 01:03:44,http://champlin.info/deontae,0.2413020067,104.181.5.89,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": true}\"\n20758,7,465,2016-12-31 03:17:38,http://mosciskibernier.biz/madisyn.kertzmann,0.2057230900,104.48.155.111,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n20759,7,465,2016-12-21 14:55:20,http://kling.com/dock_dietrich,0.9768993600,238.4.224.166,\"{\"\"location\"\": \"\"FM\"\", \"\"is_mobile\"\": true}\"\n20760,7,465,2016-12-24 15:41:30,http://stark.co/francisca,0.0064629913,12.164.136.123,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": true}\"\n20761,7,465,2017-06-06 05:18:02,http://howell.com/audreanne,0.9165147073,117.243.156.52,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n20762,7,466,2017-04-06 22:39:49,http://zulauf.net/ismael,,139.175.103.102,\"{\"\"location\"\": \"\"NR\"\", \"\"is_mobile\"\": false}\"\n20763,7,466,2017-01-24 03:47:27,http://leannonjaskolski.com/gerardo.hyatt,,197.127.187.165,\"{\"\"location\"\": \"\"KG\"\", \"\"is_mobile\"\": false}\"\n20764,7,466,2017-05-10 07:07:24,http://schmitt.biz/greyson_ferry,,114.51.129.87,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": false}\"\n20765,7,466,2017-02-16 01:45:32,http://abshire.co/jarrett_hoppe,,30.56.78.111,\"{\"\"location\"\": \"\"FI\"\", \"\"is_mobile\"\": true}\"\n20766,7,466,2017-04-20 15:15:02,http://boscomclaughlin.name/reyna.stokes,,254.130.242.146,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": false}\"\n20767,7,466,2017-03-15 15:10:34,http://lesch.name/ania.stark,,125.147.232.126,\"{\"\"location\"\": \"\"UA\"\", \"\"is_mobile\"\": true}\"\n20768,7,466,2017-03-01 19:30:55,http://west.co/carolyne.stamm,,154.118.65.110,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n20769,7,466,2017-02-20 02:38:33,http://corwin.co/kianna.skiles,,71.98.159.193,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n20770,7,466,2017-05-23 00:08:34,http://mann.com/bernardo,,195.177.142.222,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n20771,7,466,2017-02-18 00:05:02,http://hintzschmitt.info/reva,,182.163.33.68,\"{\"\"location\"\": \"\"NG\"\", \"\"is_mobile\"\": true}\"\n20772,7,466,2017-05-30 15:47:13,http://heathcotezieme.net/jillian,,195.146.9.17,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n20773,7,466,2017-05-09 22:46:06,http://kuhic.org/marcelle_wehner,,213.71.183.230,\"{\"\"location\"\": \"\"PK\"\", \"\"is_mobile\"\": true}\"\n20774,7,466,2017-05-06 08:34:45,http://fahey.io/lexi.heller,,251.63.51.151,\"{\"\"location\"\": \"\"SB\"\", \"\"is_mobile\"\": true}\"\n20775,7,466,2017-02-12 00:42:05,http://carroll.org/esperanza,,164.15.37.50,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": false}\"\n20776,7,466,2017-01-16 06:05:31,http://leuschke.co/maude_rosenbaum,,143.193.156.174,\"{\"\"location\"\": \"\"IT\"\", \"\"is_mobile\"\": true}\"\n20777,7,466,2017-03-07 11:30:15,http://millerbreitenberg.com/immanuel,,121.98.102.210,\"{\"\"location\"\": \"\"JP\"\", \"\"is_mobile\"\": true}\"\n20778,7,466,2016-12-31 13:41:04,http://rempelhalvorson.name/berenice.erdman,,15.77.33.81,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n20779,7,466,2017-02-05 20:58:48,http://hackett.info/emilio_gutmann,,222.159.250.154,\"{\"\"location\"\": \"\"SS\"\", \"\"is_mobile\"\": false}\"\n20780,7,466,2017-02-18 21:40:18,http://mosciskiheathcote.name/selina_schimmel,,237.236.244.219,\"{\"\"location\"\": \"\"LB\"\", \"\"is_mobile\"\": false}\"\n20781,7,466,2017-05-06 23:49:00,http://bosco.com/ryann,,162.78.34.33,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n20782,7,466,2017-04-21 16:00:03,http://hettinger.net/myron_runolfsdottir,,51.174.142.118,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n20783,7,466,2017-02-25 15:36:56,http://feest.info/aletha.larkin,,166.190.83.80,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n20784,7,466,2017-04-25 10:00:48,http://creminmayert.net/wilmer,,42.253.174.10,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n20785,7,466,2017-05-26 18:25:34,http://hagenes.info/damaris,,234.254.144.34,\"{\"\"location\"\": \"\"BR\"\", \"\"is_mobile\"\": true}\"\n20786,7,466,2017-06-06 22:16:42,http://bins.name/andre,,211.39.235.248,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": true}\"\n20787,7,466,2017-05-09 15:36:36,http://gibsonruecker.com/keegan,,249.251.157.154,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n20788,7,466,2017-04-16 15:50:37,http://ankunding.org/ervin,,55.137.166.181,\"{\"\"location\"\": \"\"DO\"\", \"\"is_mobile\"\": true}\"\n20789,7,466,2017-01-05 07:45:39,http://fadel.io/henri,,218.171.144.7,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n20790,7,466,2017-05-21 09:00:24,http://steuber.com/lenny_kreiger,,3.201.213.83,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": true}\"\n20791,7,466,2016-12-21 10:08:14,http://berniergleichner.co/adolphus,,187.64.90.181,\"{\"\"location\"\": \"\"MV\"\", \"\"is_mobile\"\": false}\"\n20792,7,466,2017-04-24 19:36:05,http://abshire.name/cathy,,125.137.179.190,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": false}\"\n20793,7,466,2017-04-29 13:57:14,http://senger.name/gielle,,204.243.125.230,\"{\"\"location\"\": \"\"TC\"\", \"\"is_mobile\"\": true}\"\n20794,7,467,2017-01-17 15:35:03,http://gorczany.net/garrett,,134.101.38.36,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n20795,7,467,2017-03-01 14:16:07,http://hermannschneider.info/angelo.jaskolski,,223.139.134.231,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": false}\"\n20796,7,467,2017-03-20 19:53:09,http://ziemannhyatt.info/nikita.jerde,,117.61.134.112,\"{\"\"location\"\": \"\"JO\"\", \"\"is_mobile\"\": false}\"\n20797,7,467,2017-06-06 11:38:40,http://spencer.name/alexander,,96.5.238.214,\"{\"\"location\"\": \"\"SO\"\", \"\"is_mobile\"\": false}\"\n20798,7,467,2017-05-17 17:56:01,http://kirlin.org/brittany_mills,,199.150.181.21,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n20799,7,467,2016-12-24 00:29:15,http://boscospinka.io/bethany.bruen,,120.49.196.3,\"{\"\"location\"\": \"\"LV\"\", \"\"is_mobile\"\": true}\"\n20800,7,467,2017-03-04 23:06:28,http://legros.io/leta_roberts,,156.43.186.15,\"{\"\"location\"\": \"\"KY\"\", \"\"is_mobile\"\": true}\"\n20801,7,467,2017-05-06 00:17:20,http://haagkertzmann.info/filomena_kertzmann,,197.76.238.249,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n20802,7,467,2017-04-07 17:40:06,http://feest.net/landen,,105.225.97.91,\"{\"\"location\"\": \"\"GY\"\", \"\"is_mobile\"\": true}\"\n20803,7,467,2017-05-09 20:08:33,http://bayer.name/beverly_hills,,99.63.188.137,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n20804,7,467,2016-12-23 03:52:37,http://lynchzulauf.net/naomie,,20.131.162.212,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": true}\"\n20805,7,467,2017-05-28 05:00:25,http://ondrickaterry.com/al,,3.104.197.32,\"{\"\"location\"\": \"\"RO\"\", \"\"is_mobile\"\": true}\"\n20806,7,467,2017-04-17 14:07:03,http://romaguera.biz/ezekiel,,146.109.235.5,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": false}\"\n20807,7,467,2017-04-05 05:17:36,http://lebsack.io/dina,,141.182.228.158,\"{\"\"location\"\": \"\"CK\"\", \"\"is_mobile\"\": false}\"\n20808,7,467,2017-04-24 23:59:24,http://kertzmann.co/dee,,127.105.7.151,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n20809,7,467,2017-01-07 09:08:22,http://vonbode.org/flavio.vonrueden,,117.253.51.74,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": false}\"\n20810,7,467,2017-01-13 22:12:53,http://haagkulas.name/johnson,,124.148.69.25,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": false}\"\n20811,7,467,2017-05-16 15:33:52,http://gusikowskimueller.org/king,,126.240.39.96,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n20812,7,467,2017-06-12 07:09:25,http://okeefefeest.com/mike.thompson,,65.107.105.210,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n20813,7,467,2017-02-05 00:18:09,http://kirlin.io/ayana.hintz,,26.165.44.241,\"{\"\"location\"\": \"\"MP\"\", \"\"is_mobile\"\": false}\"\n20814,7,467,2017-04-19 21:29:19,http://walsh.net/casandra_bergstrom,,43.90.242.172,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n20815,7,468,2017-01-13 04:06:21,http://wymanhansen.net/ward.kuhn,,86.2.123.56,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n20816,7,468,2017-06-02 06:07:44,http://wuckertbrown.biz/audrey,,37.207.254.43,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n20817,7,468,2017-05-13 07:50:58,http://yost.co/sienna,,164.22.29.56,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n20818,7,468,2017-01-25 04:19:52,http://schinnerdubuque.biz/tyson_hermiston,,235.242.139.228,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n20819,7,468,2017-01-19 06:42:53,http://hintzjacobi.co/marlen,,140.74.189.47,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": false}\"\n20820,7,468,2017-03-12 10:29:50,http://sawaynmarquardt.biz/constantin,,230.205.232.79,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n20821,7,468,2017-03-01 21:20:50,http://schoen.io/tyrique,,24.83.212.234,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n20822,7,468,2017-04-06 04:59:15,http://paucek.co/mathias_oconner,,26.218.115.48,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": false}\"\n20823,7,468,2017-03-12 00:00:14,http://reynolds.biz/jailyn,,221.84.32.32,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": false}\"\n20824,7,468,2017-05-31 18:30:45,http://friesen.org/leta,,196.27.152.62,\"{\"\"location\"\": \"\"MA\"\", \"\"is_mobile\"\": false}\"\n20825,7,468,2017-01-01 11:35:58,http://kunze.info/jackson,,113.191.15.208,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n20826,7,468,2017-04-29 16:09:22,http://kovacekrutherford.net/chanel,,153.72.117.71,\"{\"\"location\"\": \"\"VI\"\", \"\"is_mobile\"\": false}\"\n20827,7,468,2017-02-08 04:00:33,http://luettgen.biz/graham,,218.228.91.9,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": true}\"\n20828,7,468,2017-02-06 14:14:44,http://bayermuller.info/olen,,235.180.61.216,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n20829,7,468,2017-06-06 19:16:39,http://kautzer.name/alvina,,185.78.20.224,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n20830,7,468,2017-05-07 05:09:39,http://johns.name/joany,,165.133.179.24,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": true}\"\n20831,7,468,2016-12-18 09:56:44,http://heathcote.com/mathew,,26.2.114.68,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n20832,7,468,2017-01-26 10:56:09,http://baumbach.org/nicholaus,,61.84.164.11,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": false}\"\n20833,7,468,2017-03-30 15:41:25,http://fay.name/reagan.shanahan,,159.113.205.244,\"{\"\"location\"\": \"\"CL\"\", \"\"is_mobile\"\": true}\"\n20834,7,468,2017-02-21 12:21:06,http://dickens.io/candelario,,35.201.14.134,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": false}\"\n20835,7,468,2017-05-14 14:55:20,http://hoppe.net/ernesto.will,,234.130.228.83,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n20836,7,468,2017-05-09 16:08:10,http://bogisich.com/curtis,,175.55.148.101,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n20837,7,468,2017-01-31 09:26:19,http://nicolas.com/alexzander_mcdermott,,27.116.170.43,\"{\"\"location\"\": \"\"MS\"\", \"\"is_mobile\"\": false}\"\n20838,7,468,2017-06-13 01:20:16,http://feeney.org/itzel_west,,210.55.219.187,\"{\"\"location\"\": \"\"CN\"\", \"\"is_mobile\"\": true}\"\n20839,7,468,2017-01-19 14:31:35,http://anderson.net/dejuan.padberg,,108.210.57.182,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": true}\"\n20840,7,468,2017-02-12 17:57:13,http://brown.org/orlo.spencer,,194.74.241.53,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n20841,7,468,2016-12-19 10:57:32,http://willledner.co/ruth.denesik,,239.252.181.158,\"{\"\"location\"\": \"\"BQ\"\", \"\"is_mobile\"\": true}\"\n20842,7,468,2017-01-18 11:40:43,http://ullrichfriesen.io/americo.hilpert,,181.189.220.16,\"{\"\"location\"\": \"\"BW\"\", \"\"is_mobile\"\": true}\"\n20843,7,468,2017-05-17 10:24:58,http://olson.info/gregory.kub,,84.245.45.34,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n20844,7,468,2017-04-16 14:17:47,http://kunze.co/santino.pfannerstill,,118.204.155.41,\"{\"\"location\"\": \"\"BO\"\", \"\"is_mobile\"\": false}\"\n20845,7,468,2017-03-07 18:31:38,http://bailey.io/jerad.runolfon,,7.210.64.184,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": false}\"\n20846,7,468,2017-01-04 23:26:19,http://cole.org/keven,,254.100.193.99,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": false}\"\n20847,7,468,2017-01-05 18:15:27,http://wilkinson.name/annie,,186.53.187.223,\"{\"\"location\"\": \"\"MC\"\", \"\"is_mobile\"\": false}\"\n20848,7,468,2017-06-12 18:43:38,http://reichel.org/milo,,188.168.242.221,\"{\"\"location\"\": \"\"BZ\"\", \"\"is_mobile\"\": false}\"\n20849,7,468,2017-01-31 16:18:12,http://colliersanford.com/rodrigo_senger,,75.217.122.129,\"{\"\"location\"\": \"\"PN\"\", \"\"is_mobile\"\": true}\"\n20850,7,468,2017-05-14 10:07:18,http://leschko.biz/reece.frami,,83.92.73.26,\"{\"\"location\"\": \"\"WS\"\", \"\"is_mobile\"\": false}\"\n20851,7,468,2016-12-29 14:20:24,http://boyer.co/burnice,,74.127.111.197,\"{\"\"location\"\": \"\"IE\"\", \"\"is_mobile\"\": true}\"\n20852,7,468,2017-01-07 09:39:02,http://lynchhuel.io/adelbert_moore,,131.83.161.107,\"{\"\"location\"\": \"\"PR\"\", \"\"is_mobile\"\": false}\"\n20853,7,468,2017-01-03 05:09:44,http://walker.io/kennith.goldner,,222.152.6.148,\"{\"\"location\"\": \"\"UY\"\", \"\"is_mobile\"\": true}\"\n20854,7,469,2017-04-08 06:23:38,http://frami.io/bradly,,120.206.47.115,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": false}\"\n20855,7,469,2016-12-18 12:52:38,http://beer.com/valentina_langworth,,208.183.68.105,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n20856,7,469,2017-02-19 10:09:37,http://mohr.com/trea.langosh,,195.33.69.90,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n20857,7,469,2017-02-17 11:37:56,http://mosciski.info/jameson,,78.101.14.146,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": true}\"\n20858,7,469,2017-05-06 08:56:00,http://ortiz.io/omari,,118.230.153.140,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n20859,7,469,2017-02-27 07:36:02,http://johns.info/destini,,178.191.225.52,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n20860,7,469,2017-04-11 11:50:47,http://gorczany.net/corrine,,193.143.183.124,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n20861,7,469,2016-12-17 02:46:51,http://hintzritchie.biz/laura,,39.135.41.76,\"{\"\"location\"\": \"\"US\"\", \"\"is_mobile\"\": false}\"\n20862,7,469,2017-02-04 02:58:05,http://gerholdhayes.co/beverly.kuhic,,161.67.78.175,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": false}\"\n20863,7,469,2017-02-12 20:17:58,http://keeling.com/rigoberto,,57.211.117.152,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": false}\"\n20864,7,469,2017-03-06 11:39:54,http://zboncakweimann.net/maye.connelly,,231.191.65.85,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n20865,7,469,2017-05-01 14:33:45,http://hoppe.biz/destiney_toy,,146.179.178.124,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n20866,7,469,2017-06-11 03:10:37,http://heel.net/juliet,,18.94.183.125,\"{\"\"location\"\": \"\"AI\"\", \"\"is_mobile\"\": true}\"\n20867,7,469,2017-01-13 20:30:20,http://pfefferupton.info/virginia,,76.83.178.176,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": true}\"\n20868,7,469,2017-04-05 11:23:09,http://schaefersporer.name/haley.reichert,,107.19.64.175,\"{\"\"location\"\": \"\"ID\"\", \"\"is_mobile\"\": true}\"\n20869,7,469,2017-02-15 23:34:47,http://barrows.net/billie,,16.19.20.39,\"{\"\"location\"\": \"\"SM\"\", \"\"is_mobile\"\": false}\"\n20870,7,469,2017-03-06 16:51:11,http://okon.co/evie,,253.136.66.183,\"{\"\"location\"\": \"\"IN\"\", \"\"is_mobile\"\": false}\"\n20871,7,469,2017-05-28 16:13:49,http://lakin.org/hoyt,,18.214.11.74,\"{\"\"location\"\": \"\"TG\"\", \"\"is_mobile\"\": true}\"\n20874,7,469,2017-01-18 08:55:26,http://jakubowskihamill.com/heath,,159.169.224.10,\"{\"\"location\"\": \"\"RS\"\", \"\"is_mobile\"\": false}\"\n20875,7,469,2017-04-19 00:17:31,http://doylekoch.com/orrin.bins,,4.220.132.38,\"{\"\"location\"\": \"\"TR\"\", \"\"is_mobile\"\": true}\"\n20876,7,469,2017-03-03 01:01:51,http://kihn.co/arnold_jacobi,,121.51.61.210,\"{\"\"location\"\": \"\"SE\"\", \"\"is_mobile\"\": true}\"\n20877,7,469,2017-01-16 16:23:52,http://wildermanharber.biz/claudie.schaefer,,22.82.185.203,\"{\"\"location\"\": \"\"IL\"\", \"\"is_mobile\"\": true}\"\n20878,7,470,2017-04-07 00:26:21,http://konopelskistehr.com/jerad.schoen,,211.30.51.48,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": true}\"\n20879,7,470,2017-03-23 15:17:37,http://kub.biz/davion.grant,,239.90.63.19,\"{\"\"location\"\": \"\"GN\"\", \"\"is_mobile\"\": false}\"\n20880,7,470,2017-01-20 06:01:21,http://shanahan.com/pietro_farrell,,3.78.72.78,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": true}\"\n20881,7,470,2017-04-19 04:25:21,http://breitenberg.name/lila.reinger,,110.29.229.95,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": false}\"\n20882,7,470,2017-05-06 21:09:53,http://hagenes.io/cory,,190.109.89.48,\"{\"\"location\"\": \"\"OM\"\", \"\"is_mobile\"\": true}\"\n20883,7,470,2017-04-27 18:20:20,http://dibbert.org/federico,,182.129.205.70,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n20884,7,470,2017-03-27 21:47:56,http://pacochakirlin.net/dante,,30.196.7.228,\"{\"\"location\"\": \"\"MZ\"\", \"\"is_mobile\"\": true}\"\n20885,7,470,2017-01-27 23:04:22,http://hyattcrist.info/cali_mraz,,183.40.86.203,\"{\"\"location\"\": \"\"HK\"\", \"\"is_mobile\"\": false}\"\n20886,7,470,2017-05-12 21:49:47,http://fritschlubowitz.info/marcus,,97.72.10.41,\"{\"\"location\"\": \"\"LI\"\", \"\"is_mobile\"\": true}\"\n20887,7,470,2016-12-17 05:36:00,http://wilkinsonmuller.info/brendan_tromp,,16.86.147.181,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n20888,7,470,2017-01-08 06:29:26,http://mertzreichert.info/morton.effertz,,48.135.103.114,\"{\"\"location\"\": \"\"SY\"\", \"\"is_mobile\"\": true}\"\n20889,7,470,2017-02-26 12:29:36,http://nitzsche.com/vernie.shields,,94.115.223.78,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": false}\"\n20890,7,470,2017-02-25 06:01:13,http://lindernser.org/bernadette,,234.102.35.87,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": false}\"\n20891,7,470,2017-02-26 12:34:07,http://leuschkeschroeder.io/nathaniel_kris,,252.194.14.54,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n20892,7,470,2017-02-11 08:20:12,http://gerhold.org/monica.greenholt,,150.56.155.125,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n20893,7,470,2017-01-15 02:07:48,http://gerhold.co/ramon_renner,,21.130.22.115,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": false}\"\n20894,7,470,2017-03-19 20:00:13,http://lockman.org/aliza.roob,,126.208.48.220,\"{\"\"location\"\": \"\"ET\"\", \"\"is_mobile\"\": false}\"\n20895,7,470,2017-02-25 14:25:12,http://brekke.com/theo_anderson,,213.73.226.207,\"{\"\"location\"\": \"\"SH\"\", \"\"is_mobile\"\": true}\"\n20896,7,470,2017-05-04 11:52:13,http://leschmohr.name/gina,,86.74.112.248,\"{\"\"location\"\": \"\"BE\"\", \"\"is_mobile\"\": false}\"\n20897,7,470,2017-03-08 17:55:11,http://marvin.io/aiyana_harber,,45.238.248.179,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n20898,7,470,2017-06-10 08:46:55,http://rogahnglover.org/rosalee.nader,,203.9.207.79,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": true}\"\n20899,7,470,2017-06-10 01:31:34,http://vonrueden.org/louie,,63.123.174.144,\"{\"\"location\"\": \"\"BY\"\", \"\"is_mobile\"\": false}\"\n20900,7,470,2017-03-14 01:05:18,http://kertzmann.net/ena.pacocha,,211.17.130.70,\"{\"\"location\"\": \"\"RE\"\", \"\"is_mobile\"\": true}\"\n20901,7,470,2017-04-29 12:01:58,http://howe.io/orion_nicolas,,98.6.76.140,\"{\"\"location\"\": \"\"AZ\"\", \"\"is_mobile\"\": true}\"\n20902,7,470,2017-04-02 18:23:19,http://ratke.org/sheila,,126.241.199.82,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": false}\"\n20903,7,470,2017-04-21 13:29:41,http://kshlerin.info/ashton,,226.51.149.100,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n20904,7,470,2017-05-24 20:15:25,http://mayer.com/laurel,,138.15.46.204,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n20905,7,470,2017-03-27 21:01:00,http://romaguera.net/naomi,,174.190.54.254,\"{\"\"location\"\": \"\"NO\"\", \"\"is_mobile\"\": true}\"\n20906,7,470,2017-01-04 22:44:02,http://marquardt.net/esther,,153.206.208.233,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": false}\"\n20907,7,470,2017-04-14 13:15:28,http://greenholt.co/april,,42.147.46.167,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": true}\"\n20908,7,470,2017-06-12 18:37:54,http://padbergharris.com/molly.bergnaum,,106.32.236.177,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": false}\"\n20909,7,470,2017-01-13 18:57:55,http://orngrant.biz/ashley.sporer,,225.141.8.61,\"{\"\"location\"\": \"\"GU\"\", \"\"is_mobile\"\": true}\"\n20910,7,470,2017-03-24 18:07:25,http://olson.co/hubert,,230.81.165.195,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": false}\"\n20911,7,470,2017-05-23 18:53:09,http://wittingcormier.net/pamela,,53.230.34.105,\"{\"\"location\"\": \"\"DK\"\", \"\"is_mobile\"\": true}\"\n20912,7,470,2017-03-27 14:37:29,http://mccullough.info/jeremy,,204.145.25.205,\"{\"\"location\"\": \"\"MQ\"\", \"\"is_mobile\"\": false}\"\n20913,7,470,2017-01-12 05:49:55,http://braun.biz/nico,,225.130.193.102,\"{\"\"location\"\": \"\"TW\"\", \"\"is_mobile\"\": true}\"\n20914,7,470,2016-12-27 14:57:49,http://lindgren.io/derrick,,241.161.167.245,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n20915,7,470,2017-06-13 00:13:55,http://goyette.com/bertrand_moore,,111.70.133.137,\"{\"\"location\"\": \"\"BI\"\", \"\"is_mobile\"\": false}\"\n20916,7,470,2017-04-18 00:15:50,http://nicolas.com/myrtis,,202.72.50.170,\"{\"\"location\"\": \"\"FK\"\", \"\"is_mobile\"\": false}\"\n20917,7,470,2017-06-06 18:32:06,http://oberbrunnerleffler.info/flavio,,199.106.89.147,\"{\"\"location\"\": \"\"KZ\"\", \"\"is_mobile\"\": true}\"\n20918,7,470,2017-05-19 22:33:39,http://dickinson.org/lela,,104.168.50.13,\"{\"\"location\"\": \"\"MO\"\", \"\"is_mobile\"\": false}\"\n20919,7,471,2017-03-28 19:01:54,http://oberbrunnerframi.name/willis.heathcote,,172.179.233.18,\"{\"\"location\"\": \"\"CU\"\", \"\"is_mobile\"\": true}\"\n20920,7,471,2017-04-14 03:34:40,http://simonis.com/mabelle_considine,,103.235.57.16,\"{\"\"location\"\": \"\"MT\"\", \"\"is_mobile\"\": false}\"\n20921,7,471,2016-12-19 03:35:11,http://treutelzieme.co/ettie.conn,,24.69.233.104,\"{\"\"location\"\": \"\"KI\"\", \"\"is_mobile\"\": true}\"\n20922,7,471,2017-05-03 17:13:42,http://herzog.biz/maximillian,,135.176.50.96,\"{\"\"location\"\": \"\"VC\"\", \"\"is_mobile\"\": true}\"\n20923,7,471,2017-02-10 18:03:11,http://mcglynn.biz/eugene,,236.116.160.221,\"{\"\"location\"\": \"\"CO\"\", \"\"is_mobile\"\": false}\"\n20924,7,471,2017-05-16 08:59:42,http://gerlachbeahan.com/alf,,162.213.235.212,\"{\"\"location\"\": \"\"ZW\"\", \"\"is_mobile\"\": true}\"\n20925,7,471,2017-02-04 17:27:52,http://douglas.biz/newell,,53.49.185.125,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n20926,7,471,2017-02-20 04:48:43,http://dickinson.org/brook,,41.77.28.220,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": false}\"\n20927,7,471,2017-01-12 23:05:15,http://collins.name/uriah,,150.181.135.232,\"{\"\"location\"\": \"\"MY\"\", \"\"is_mobile\"\": false}\"\n20928,7,471,2017-03-19 07:31:06,http://rippin.org/nya_marvin,,128.14.20.206,\"{\"\"location\"\": \"\"MX\"\", \"\"is_mobile\"\": true}\"\n20929,7,471,2016-12-21 20:28:02,http://reinger.org/margarett,,211.62.65.254,\"{\"\"location\"\": \"\"TT\"\", \"\"is_mobile\"\": true}\"\n20930,7,471,2017-03-25 14:06:40,http://hansenmurphy.name/daryl.kshlerin,,48.188.181.63,\"{\"\"location\"\": \"\"NI\"\", \"\"is_mobile\"\": true}\"\n20931,7,471,2017-02-10 21:22:12,http://considinekuvalis.name/murphy.witting,,77.95.58.220,\"{\"\"location\"\": \"\"NC\"\", \"\"is_mobile\"\": true}\"\n20932,7,471,2017-05-20 04:51:30,http://mosciski.com/dusty,,140.94.250.87,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": true}\"\n20933,7,471,2017-03-27 01:55:37,http://erdmanjones.name/kyle.bins,,99.133.66.172,\"{\"\"location\"\": \"\"BF\"\", \"\"is_mobile\"\": false}\"\n20934,7,471,2017-01-23 19:53:33,http://bruenfeeney.com/salvatore_lynch,,71.69.169.54,\"{\"\"location\"\": \"\"AF\"\", \"\"is_mobile\"\": true}\"\n20935,7,471,2017-04-22 11:49:12,http://lynch.biz/michale,,158.11.249.172,\"{\"\"location\"\": \"\"GS\"\", \"\"is_mobile\"\": false}\"\n20936,7,471,2017-05-26 09:06:35,http://stehrconn.info/eulah,,254.33.194.192,\"{\"\"location\"\": \"\"KP\"\", \"\"is_mobile\"\": true}\"\n20937,7,471,2017-06-09 09:19:10,http://glovermueller.co/demarco_vandervort,,147.119.70.50,\"{\"\"location\"\": \"\"FO\"\", \"\"is_mobile\"\": true}\"\n20938,7,471,2017-02-08 11:20:09,http://mayer.co/dorthy.daugherty,,205.197.206.145,\"{\"\"location\"\": \"\"DE\"\", \"\"is_mobile\"\": false}\"\n20939,7,471,2017-05-05 03:53:23,http://wiza.info/darryl_gusikowski,,169.36.18.122,\"{\"\"location\"\": \"\"MH\"\", \"\"is_mobile\"\": false}\"\n20940,7,471,2017-02-20 17:40:58,http://williamson.org/jamel,,170.49.130.156,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n20941,7,471,2017-02-03 10:52:48,http://kuhn.io/laury,,94.221.83.143,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n20942,7,471,2017-03-20 01:22:07,http://ko.info/carolyne_deckow,,33.137.171.114,\"{\"\"location\"\": \"\"TK\"\", \"\"is_mobile\"\": true}\"\n20943,7,471,2017-04-07 18:02:16,http://mueller.com/clare.collins,,211.125.5.131,\"{\"\"location\"\": \"\"SJ\"\", \"\"is_mobile\"\": true}\"\n20944,7,471,2017-02-14 17:37:53,http://runolfsdottirhettinger.com/rubie,,187.81.106.146,\"{\"\"location\"\": \"\"UZ\"\", \"\"is_mobile\"\": false}\"\n20945,7,471,2017-04-24 12:19:16,http://schaefer.biz/enrique,,224.24.234.227,\"{\"\"location\"\": \"\"JE\"\", \"\"is_mobile\"\": false}\"\n20946,7,471,2017-01-05 03:04:40,http://batzgibson.com/caitlyn.mitchell,,92.40.204.170,\"{\"\"location\"\": \"\"PE\"\", \"\"is_mobile\"\": true}\"\n20947,7,471,2017-04-28 02:09:38,http://gorczany.co/phyllis_volkman,,133.159.46.24,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": true}\"\n20948,7,471,2017-05-01 22:47:31,http://simonismayer.net/stan,,24.223.19.152,\"{\"\"location\"\": \"\"ES\"\", \"\"is_mobile\"\": false}\"\n20949,7,471,2017-01-25 22:39:41,http://rempel.com/audra.jaskolski,,4.34.144.221,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": true}\"\n20950,7,471,2017-04-25 03:41:33,http://blanda.biz/ashley.moore,,223.50.27.162,\"{\"\"location\"\": \"\"SV\"\", \"\"is_mobile\"\": false}\"\n20951,7,471,2017-05-12 17:08:20,http://paucek.io/wilson,,62.127.231.45,\"{\"\"location\"\": \"\"TF\"\", \"\"is_mobile\"\": false}\"\n20952,7,472,2017-02-14 17:30:03,http://daniel.net/domenica,,152.60.42.238,\"{\"\"location\"\": \"\"VE\"\", \"\"is_mobile\"\": false}\"\n20953,7,472,2016-12-29 18:50:12,http://ebertdavis.org/lyla,,244.230.14.174,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n20954,7,472,2017-04-13 14:37:54,http://hickle.info/leone,,28.118.221.96,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": true}\"\n20955,7,472,2017-04-18 19:49:38,http://jerde.io/tanner,,225.5.43.22,\"{\"\"location\"\": \"\"KH\"\", \"\"is_mobile\"\": true}\"\n20956,7,472,2017-04-02 19:38:03,http://hammes.org/kiara,,171.9.25.253,\"{\"\"location\"\": \"\"NP\"\", \"\"is_mobile\"\": false}\"\n20957,7,472,2016-12-15 18:58:46,http://gerholdkris.org/taylor_wintheiser,,151.144.13.217,\"{\"\"location\"\": \"\"AM\"\", \"\"is_mobile\"\": true}\"\n20958,7,472,2017-01-17 23:52:25,http://larkinbailey.net/anabelle,,39.177.207.7,\"{\"\"location\"\": \"\"BN\"\", \"\"is_mobile\"\": true}\"\n20959,7,472,2017-06-02 01:41:54,http://schmidt.biz/joan.upton,,139.198.163.90,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n20960,7,472,2016-12-26 15:07:19,http://batzbergstrom.org/maye_simonis,,15.135.213.165,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": false}\"\n20961,7,472,2017-03-16 07:05:57,http://schuster.io/darrin.murray,,245.190.8.81,\"{\"\"location\"\": \"\"SI\"\", \"\"is_mobile\"\": true}\"\n20962,7,472,2017-03-04 16:21:16,http://gislason.org/kelley,,10.215.211.203,\"{\"\"location\"\": \"\"GG\"\", \"\"is_mobile\"\": false}\"\n20963,7,472,2017-01-28 15:10:17,http://corwin.name/josie.stracke,,210.96.214.30,\"{\"\"location\"\": \"\"SA\"\", \"\"is_mobile\"\": false}\"\n20964,7,472,2017-02-11 15:01:24,http://sawayn.name/ransom_medhurst,,179.208.122.55,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n20965,7,472,2017-02-26 15:37:14,http://johns.org/genoveva.ward,,214.156.192.223,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": true}\"\n20966,7,472,2017-04-15 07:16:21,http://lockmanhermann.net/blanca_ko,,143.231.137.201,\"{\"\"location\"\": \"\"DZ\"\", \"\"is_mobile\"\": true}\"\n20967,7,472,2017-01-03 13:38:12,http://jerde.org/gail_trantow,,181.59.190.173,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n20968,7,472,2017-02-17 20:19:43,http://gorczany.info/anastacio.bins,,13.114.172.103,\"{\"\"location\"\": \"\"PW\"\", \"\"is_mobile\"\": false}\"\n20969,7,472,2017-03-26 12:56:24,http://bartoletti.io/marcel,,54.24.61.120,\"{\"\"location\"\": \"\"HR\"\", \"\"is_mobile\"\": true}\"\n20970,7,472,2017-05-04 19:24:58,http://howell.co/viola.macejkovic,,212.6.171.161,\"{\"\"location\"\": \"\"GL\"\", \"\"is_mobile\"\": true}\"\n20971,7,472,2017-01-07 09:12:52,http://miller.name/mervin.cartwright,,81.37.251.142,\"{\"\"location\"\": \"\"GF\"\", \"\"is_mobile\"\": false}\"\n20972,7,472,2017-04-03 07:36:10,http://dietrich.name/lance,,207.222.9.114,\"{\"\"location\"\": \"\"GB\"\", \"\"is_mobile\"\": false}\"\n20973,7,472,2017-03-09 22:01:03,http://howewalter.com/fae.hintz,,159.99.103.146,\"{\"\"location\"\": \"\"BL\"\", \"\"is_mobile\"\": true}\"\n20974,7,472,2017-01-02 03:54:42,http://wolff.co/jacquelyn.jast,,70.8.75.124,\"{\"\"location\"\": \"\"PH\"\", \"\"is_mobile\"\": true}\"\n20975,7,472,2017-02-11 21:34:30,http://gibson.io/judson,,63.118.43.105,\"{\"\"location\"\": \"\"TJ\"\", \"\"is_mobile\"\": false}\"\n20976,7,472,2017-04-17 12:20:07,http://will.org/walton.tillman,,202.202.186.155,\"{\"\"location\"\": \"\"IM\"\", \"\"is_mobile\"\": true}\"\n20977,7,472,2017-03-25 00:13:14,http://murphylindgren.net/antwon.gottlieb,,85.153.148.228,\"{\"\"location\"\": \"\"BD\"\", \"\"is_mobile\"\": false}\"\n20978,7,472,2017-04-12 08:53:44,http://hahn.com/jo.grady,,138.168.147.59,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n20979,7,472,2017-03-29 01:17:36,http://balistreri.co/vivienne,,110.213.146.44,\"{\"\"location\"\": \"\"LA\"\", \"\"is_mobile\"\": true}\"\n20980,7,472,2017-04-01 12:06:05,http://padbergblanda.co/laverna,,108.171.198.184,\"{\"\"location\"\": \"\"VN\"\", \"\"is_mobile\"\": false}\"\n20981,7,472,2017-05-29 02:50:50,http://doylebechtelar.com/melany.kihn,,46.186.187.169,\"{\"\"location\"\": \"\"MR\"\", \"\"is_mobile\"\": false}\"\n20982,7,472,2017-03-26 05:06:10,http://pollich.name/eulah_wilderman,,6.197.56.140,\"{\"\"location\"\": \"\"BT\"\", \"\"is_mobile\"\": true}\"\n20983,7,472,2016-12-25 08:15:31,http://gleichner.org/saige,,184.77.172.50,\"{\"\"location\"\": \"\"GR\"\", \"\"is_mobile\"\": true}\"\n20984,7,472,2017-05-10 13:40:20,http://blickfarrell.name/stewart,,235.196.94.164,\"{\"\"location\"\": \"\"CW\"\", \"\"is_mobile\"\": true}\"\n20985,7,472,2017-03-24 06:21:43,http://gleason.net/adam,,118.105.212.28,\"{\"\"location\"\": \"\"PM\"\", \"\"is_mobile\"\": false}\"\n20986,7,472,2017-04-11 02:46:53,http://thompson.name/shaylee,,229.73.190.119,\"{\"\"location\"\": \"\"EC\"\", \"\"is_mobile\"\": false}\"\n20987,7,472,2017-03-14 07:56:03,http://nikolaus.io/isabelle_senger,,122.73.122.156,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": false}\"\n20988,7,472,2017-04-12 05:57:15,http://schulistbode.name/nora_gaylord,,207.83.215.249,\"{\"\"location\"\": \"\"ZA\"\", \"\"is_mobile\"\": false}\"\n20989,7,472,2017-01-03 07:34:53,http://schaefernicolas.net/loyce,,185.116.21.100,\"{\"\"location\"\": \"\"NU\"\", \"\"is_mobile\"\": false}\"\n20990,7,472,2017-05-21 14:28:58,http://leannon.org/elvie.schoen,,89.34.204.152,\"{\"\"location\"\": \"\"KN\"\", \"\"is_mobile\"\": true}\"\n20991,7,472,2017-05-30 16:02:12,http://dibbert.com/trey,,46.199.90.123,\"{\"\"location\"\": \"\"MK\"\", \"\"is_mobile\"\": false}\"\n20992,7,472,2017-01-28 02:44:04,http://block.co/neha,,59.184.247.164,\"{\"\"location\"\": \"\"CY\"\", \"\"is_mobile\"\": true}\"\n20993,7,472,2016-12-18 20:21:15,http://schmeler.net/abner.carroll,,227.117.98.166,\"{\"\"location\"\": \"\"AL\"\", \"\"is_mobile\"\": true}\"\n20994,7,472,2017-05-22 19:52:52,http://macgyver.com/darby_oconnell,,149.55.233.193,\"{\"\"location\"\": \"\"TD\"\", \"\"is_mobile\"\": false}\"\n20995,7,472,2017-03-12 19:53:01,http://veumprohaska.biz/sabryna.abernathy,,210.229.110.26,\"{\"\"location\"\": \"\"GQ\"\", \"\"is_mobile\"\": false}\"\n20996,7,473,2017-06-07 02:49:34,http://gusikowski.info/maud.auer,,43.40.201.248,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": true}\"\n20997,7,473,2016-12-24 00:12:11,http://macejkovicnienow.org/stephen.fadel,,154.153.219.22,\"{\"\"location\"\": \"\"RW\"\", \"\"is_mobile\"\": false}\"\n20998,7,473,2017-01-12 20:13:18,http://hermann.biz/rachel,,156.105.194.187,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": true}\"\n20999,7,473,2017-04-10 20:54:20,http://lueilwitzharvey.co/jamel.sanford,,251.211.14.151,\"{\"\"location\"\": \"\"MG\"\", \"\"is_mobile\"\": true}\"\n21000,7,473,2016-12-14 17:21:39,http://crooks.org/lisa,,187.76.82.93,\"{\"\"location\"\": \"\"NA\"\", \"\"is_mobile\"\": true}\"\n21001,7,473,2017-01-13 06:09:22,http://nicolas.biz/imelda.rice,,219.241.227.57,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": true}\"\n21002,7,473,2017-04-11 12:05:09,http://oconnell.net/bailee_howe,,89.25.126.98,\"{\"\"location\"\": \"\"TN\"\", \"\"is_mobile\"\": false}\"\n21003,7,473,2017-05-27 04:58:46,http://lockman.biz/allen,,92.187.134.47,\"{\"\"location\"\": \"\"LC\"\", \"\"is_mobile\"\": false}\"\n21004,7,473,2016-12-18 06:48:52,http://grimes.com/kade,,116.156.104.42,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": true}\"\n21005,7,473,2017-03-01 10:05:46,http://ledner.info/camilla,,226.142.13.248,\"{\"\"location\"\": \"\"HM\"\", \"\"is_mobile\"\": true}\"\n21006,7,473,2017-01-02 14:36:04,http://criststroman.net/enrique.funk,,148.164.200.48,\"{\"\"location\"\": \"\"AQ\"\", \"\"is_mobile\"\": true}\"\n21007,7,473,2017-01-26 21:42:47,http://reichel.io/myra,,252.91.140.216,\"{\"\"location\"\": \"\"VG\"\", \"\"is_mobile\"\": true}\"\n21008,7,473,2016-12-30 08:26:04,http://jacobsgislason.com/graciela_rempel,,254.148.9.42,\"{\"\"location\"\": \"\"JM\"\", \"\"is_mobile\"\": true}\"\n21009,7,473,2017-01-08 06:38:28,http://stehr.name/favian,,137.151.68.82,\"{\"\"location\"\": \"\"SN\"\", \"\"is_mobile\"\": false}\"\n21010,7,473,2017-04-20 19:33:48,http://denesikrunolfon.net/leilani_balistreri,,213.246.144.134,\"{\"\"location\"\": \"\"LR\"\", \"\"is_mobile\"\": false}\"\n21011,7,473,2016-12-18 23:45:16,http://dibbert.com/gilbert,,38.136.185.67,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n21012,7,473,2017-02-27 02:14:24,http://dietrichberge.net/fiona,,55.34.238.71,\"{\"\"location\"\": \"\"AD\"\", \"\"is_mobile\"\": false}\"\n21013,7,473,2017-05-17 11:39:23,http://adams.co/bernard_feest,,34.92.172.92,\"{\"\"location\"\": \"\"FR\"\", \"\"is_mobile\"\": false}\"\n21014,7,473,2017-02-19 10:10:22,http://schambergerdurgan.net/zakary_franecki,,99.131.147.82,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": false}\"\n21015,7,473,2017-04-28 01:18:28,http://ortiz.info/thora,,15.100.55.227,\"{\"\"location\"\": \"\"GW\"\", \"\"is_mobile\"\": false}\"\n21016,7,473,2017-03-26 02:16:36,http://robel.info/kadin.terry,,33.224.186.220,\"{\"\"location\"\": \"\"BM\"\", \"\"is_mobile\"\": false}\"\n21017,7,473,2017-06-07 18:16:09,http://cole.name/merl,,57.244.147.4,\"{\"\"location\"\": \"\"KM\"\", \"\"is_mobile\"\": false}\"\n21018,7,473,2017-02-22 21:24:16,http://gislason.io/quinton,,241.170.116.158,\"{\"\"location\"\": \"\"MD\"\", \"\"is_mobile\"\": true}\"\n21019,7,473,2017-04-25 17:34:14,http://ratke.info/misael,,11.173.234.86,\"{\"\"location\"\": \"\"CZ\"\", \"\"is_mobile\"\": false}\"\n21020,7,473,2016-12-23 23:38:15,http://ko.net/lexie_brekke,,30.25.227.180,\"{\"\"location\"\": \"\"BA\"\", \"\"is_mobile\"\": false}\"\n21021,7,473,2017-03-06 21:59:16,http://howe.name/selena.towne,,214.162.219.210,\"{\"\"location\"\": \"\"MM\"\", \"\"is_mobile\"\": false}\"\n21022,7,473,2017-01-28 03:18:20,http://gulgowski.biz/mikel,,248.219.195.218,\"{\"\"location\"\": \"\"IO\"\", \"\"is_mobile\"\": true}\"\n21023,7,473,2017-04-15 08:10:18,http://lynch.biz/leif,,29.126.254.21,\"{\"\"location\"\": \"\"SG\"\", \"\"is_mobile\"\": true}\"\n21024,7,473,2017-01-08 06:40:11,http://wuckert.biz/karley.veum,,65.68.104.161,\"{\"\"location\"\": \"\"CF\"\", \"\"is_mobile\"\": true}\"\n21025,7,473,2017-06-04 13:16:27,http://wolftoy.net/lydia,,88.240.104.13,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": false}\"\n21026,7,473,2017-04-21 21:36:54,http://wolf.org/joseph_keeling,,43.201.109.124,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n21027,7,473,2016-12-30 21:33:29,http://bayer.com/santa,,216.5.108.239,\"{\"\"location\"\": \"\"VA\"\", \"\"is_mobile\"\": true}\"\n21028,7,473,2017-04-24 11:56:11,http://toy.co/marisa_terry,,187.46.24.83,\"{\"\"location\"\": \"\"EG\"\", \"\"is_mobile\"\": false}\"\n21029,7,473,2017-01-28 17:15:51,http://greenholt.net/arne,,24.134.234.121,\"{\"\"location\"\": \"\"AS\"\", \"\"is_mobile\"\": false}\"\n21030,7,473,2017-02-16 04:41:51,http://brakus.org/loy,,184.73.50.157,\"{\"\"location\"\": \"\"TM\"\", \"\"is_mobile\"\": true}\"\n21031,7,473,2017-01-17 05:09:44,http://schneider.net/tamara,,163.43.135.208,\"{\"\"location\"\": \"\"AX\"\", \"\"is_mobile\"\": true}\"\n21032,7,473,2017-03-14 20:05:29,http://smithwelch.net/jennifer.bins,,164.115.40.56,\"{\"\"location\"\": \"\"UG\"\", \"\"is_mobile\"\": false}\"\n21033,7,473,2017-01-16 09:18:01,http://emardsmitham.info/ladarius,,173.46.214.118,\"{\"\"location\"\": \"\"PG\"\", \"\"is_mobile\"\": true}\"\n21034,7,473,2017-06-07 11:21:23,http://frami.biz/solon,,213.150.5.254,\"{\"\"location\"\": \"\"TV\"\", \"\"is_mobile\"\": false}\"\n21035,7,473,2017-03-01 13:39:16,http://goldner.co/susie.donnelly,,23.249.237.224,\"{\"\"location\"\": \"\"IS\"\", \"\"is_mobile\"\": false}\"\n21036,7,473,2017-02-24 20:57:07,http://cronin.org/aric,,129.252.140.176,\"{\"\"location\"\": \"\"ME\"\", \"\"is_mobile\"\": true}\"\n21037,7,473,2016-12-31 00:30:57,http://lehner.info/reba_padberg,,246.18.54.175,\"{\"\"location\"\": \"\"CH\"\", \"\"is_mobile\"\": false}\"\n21038,7,473,2017-04-06 20:21:38,http://lang.org/mylene,,29.214.147.93,\"{\"\"location\"\": \"\"SZ\"\", \"\"is_mobile\"\": false}\"\n21039,7,473,2017-02-09 03:44:06,http://predovicgerhold.com/gerry,,207.15.30.212,\"{\"\"location\"\": \"\"NF\"\", \"\"is_mobile\"\": true}\"\n21040,7,473,2017-01-03 19:58:51,http://crona.biz/dorothea_schimmel,,46.164.229.23,\"{\"\"location\"\": \"\"AO\"\", \"\"is_mobile\"\": false}\"\n21041,7,473,2017-01-25 21:04:30,http://pfeffer.name/evie,,108.17.78.119,\"{\"\"location\"\": \"\"CM\"\", \"\"is_mobile\"\": false}\"\n21042,7,473,2017-05-20 21:32:03,http://klockoabbott.io/lilliana,,164.22.252.99,\"{\"\"location\"\": \"\"GM\"\", \"\"is_mobile\"\": true}\"\n21043,7,473,2017-02-17 23:19:43,http://denesik.biz/devan,,71.160.251.120,\"{\"\"location\"\": \"\"CX\"\", \"\"is_mobile\"\": true}\"\n21044,7,473,2017-01-30 23:28:55,http://abbott.biz/ian,,179.134.245.188,\"{\"\"location\"\": \"\"NE\"\", \"\"is_mobile\"\": true}\"\n21045,7,473,2017-02-28 00:22:43,http://grady.co/jayden.mayer,,153.105.253.144,\"{\"\"location\"\": \"\"SX\"\", \"\"is_mobile\"\": true}\"\n21046,7,473,2017-02-19 00:25:08,http://vonrueden.com/dorris,,100.132.154.151,\"{\"\"location\"\": \"\"PF\"\", \"\"is_mobile\"\": false}\"\n21047,7,473,2017-05-12 10:19:58,http://bins.co/ardella.strosin,,77.226.215.67,\"{\"\"location\"\": \"\"CC\"\", \"\"is_mobile\"\": false}\"\n"
  },
  {
    "path": "src/test/regress/data/large_records.data",
    "content": "1|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n2|a\n"
  },
  {
    "path": "src/test/regress/data/lineitem.1.data",
    "content": "1|155190|7706|1|17|21168.23|0.04|0.02|N|O|1996-03-13|1996-02-12|1996-03-22|DELIVER IN PERSON|TRUCK|egular courts above the\n1|67310|7311|2|36|45983.16|0.09|0.06|N|O|1996-04-12|1996-02-28|1996-04-20|TAKE BACK RETURN|MAIL|ly final  \t dependencies: slyly bold \n1|63700|3701|3|8|13309.60|0.10|0.02|N|O|1996-01-29|1996-03-05|1996-01-31|TAKE BACK RETURN|REG AIR|riously. regular, express dep\n1|2132|4633|4|28|28955.64|0.09|0.06|N|O|1996-04-21|1996-03-30|1996-05-16|NONE|AIR|lites. fluffily even de\n1|24027|1534|5|24|22824.48|0.10|0.04|N|O|1996-03-30|1996-03-14|1996-04-01|NONE|FOB| pending foxes. slyly re\n1|15635|638|6|32|49620.16|0.07|0.02|N|O|1996-01-30|1996-02-07|1996-02-03|DELIVER IN PERSON|MAIL|arefully slyly ex\n2|106170|1191|1|38|44694.46|0.00|0.05|N|O|1997-01-28|1997-01-14|1997-02-02|TAKE BACK RETURN|RAIL|ven requests. deposits breach a\n3|4297|1798|1|45|54058.05|0.06|0.00|R|F|1994-02-02|1994-01-04|1994-02-23|NONE|AIR|ongside of the furiously brave acco\n3|19036|6540|2|49|46796.47|0.10|0.00|R|F|1993-11-09|1993-12-20|1993-11-24|TAKE BACK RETURN|RAIL| unusual accounts. eve\n3|128449|3474|3|27|39890.88|0.06|0.07|A|F|1994-01-16|1993-11-22|1994-01-23|DELIVER IN PERSON|SHIP|nal foxes wake. \n3|29380|1883|4|2|2618.76|0.01|0.06|A|F|1993-12-04|1994-01-07|1994-01-01|NONE|TRUCK|y. fluffily pending d\n3|183095|650|5|28|32986.52|0.04|0.00|R|F|1993-12-14|1994-01-10|1994-01-01|TAKE BACK RETURN|FOB|ages nag slyly pending\n3|62143|9662|6|26|28733.64|0.10|0.02|A|F|1993-10-29|1993-12-18|1993-11-04|TAKE BACK RETURN|RAIL|ges sleep after the caref\n4|88035|5560|1|30|30690.90|0.03|0.08|N|O|1996-01-10|1995-12-14|1996-01-18|DELIVER IN PERSON|REG AIR|- quickly regular packages sleep. idly\n5|108570|8571|1|15|23678.55|0.02|0.04|R|F|1994-10-31|1994-08-31|1994-11-20|NONE|AIR|ts wake furiously \n5|123927|3928|2|26|50723.92|0.07|0.08|R|F|1994-10-16|1994-09-25|1994-10-19|NONE|FOB|sts use slyly quickly special instruc\n5|37531|35|3|50|73426.50|0.08|0.03|A|F|1994-08-08|1994-10-13|1994-08-26|DELIVER IN PERSON|AIR|eodolites. fluffily unusual\n6|139636|2150|1|37|61998.31|0.08|0.03|A|F|1992-04-27|1992-05-15|1992-05-02|TAKE BACK RETURN|TRUCK|p furiously special foxes\n7|182052|9607|1|12|13608.60|0.07|0.03|N|O|1996-05-07|1996-03-13|1996-06-03|TAKE BACK RETURN|FOB|ss pinto beans wake against th\n7|145243|7758|2|9|11594.16|0.08|0.08|N|O|1996-02-01|1996-03-02|1996-02-19|TAKE BACK RETURN|SHIP|es. instructions\n7|94780|9799|3|46|81639.88|0.10|0.07|N|O|1996-01-15|1996-03-27|1996-02-03|COLLECT COD|MAIL| unusual reques\n7|163073|3074|4|28|31809.96|0.03|0.04|N|O|1996-03-21|1996-04-08|1996-04-20|NONE|FOB|. slyly special requests haggl\n7|151894|9440|5|38|73943.82|0.08|0.01|N|O|1996-02-11|1996-02-24|1996-02-18|DELIVER IN PERSON|TRUCK|ns haggle carefully ironic deposits. bl\n7|79251|1759|6|35|43058.75|0.06|0.03|N|O|1996-01-16|1996-02-23|1996-01-22|TAKE BACK RETURN|FOB|jole. excuses wake carefully alongside of \n7|157238|2269|7|5|6476.15|0.04|0.02|N|O|1996-02-10|1996-03-26|1996-02-13|NONE|FOB|ithely regula\n32|82704|7721|1|28|47227.60|0.05|0.08|N|O|1995-10-23|1995-08-27|1995-10-26|TAKE BACK RETURN|TRUCK|sleep quickly. req\n32|197921|441|2|32|64605.44|0.02|0.00|N|O|1995-08-14|1995-10-07|1995-08-27|COLLECT COD|AIR|lithely regular deposits. fluffily \n32|44161|6666|3|2|2210.32|0.09|0.02|N|O|1995-08-07|1995-10-07|1995-08-23|DELIVER IN PERSON|AIR| express accounts wake according to the\n32|2743|7744|4|4|6582.96|0.09|0.03|N|O|1995-08-04|1995-10-01|1995-09-03|NONE|REG AIR|e slyly final pac\n32|85811|8320|5|44|79059.64|0.05|0.06|N|O|1995-08-28|1995-08-20|1995-09-14|DELIVER IN PERSON|AIR|symptotes nag according to the ironic depo\n32|11615|4117|6|6|9159.66|0.04|0.03|N|O|1995-07-21|1995-09-23|1995-07-25|COLLECT COD|RAIL| gifts cajole carefully.\n33|61336|8855|1|31|40217.23|0.09|0.04|A|F|1993-10-29|1993-12-19|1993-11-08|COLLECT COD|TRUCK|ng to the furiously ironic package\n33|60519|5532|2|32|47344.32|0.02|0.05|A|F|1993-12-09|1994-01-04|1993-12-28|COLLECT COD|MAIL|gular theodolites\n33|137469|9983|3|5|7532.30|0.05|0.03|A|F|1993-12-09|1993-12-25|1993-12-23|TAKE BACK RETURN|AIR|. stealthily bold exc\n33|33918|3919|4|41|75928.31|0.09|0.00|R|F|1993-11-09|1994-01-24|1993-11-11|TAKE BACK RETURN|MAIL|unusual packages doubt caref\n34|88362|871|1|13|17554.68|0.00|0.07|N|O|1998-10-23|1998-09-14|1998-11-06|NONE|REG AIR|nic accounts. deposits are alon\n34|89414|1923|2|22|30875.02|0.08|0.06|N|O|1998-10-09|1998-10-16|1998-10-12|NONE|FOB|thely slyly p\n34|169544|4577|3|6|9681.24|0.02|0.06|N|O|1998-10-30|1998-09-20|1998-11-05|NONE|FOB|ar foxes sleep \n35|450|2951|1|24|32410.80|0.02|0.00|N|O|1996-02-21|1996-01-03|1996-03-18|TAKE BACK RETURN|FOB|, regular tithe\n35|161940|4457|2|34|68065.96|0.06|0.08|N|O|1996-01-22|1996-01-06|1996-01-27|DELIVER IN PERSON|RAIL|s are carefully against the f\n35|120896|8433|3|7|13418.23|0.06|0.04|N|O|1996-01-19|1995-12-22|1996-01-29|NONE|MAIL| the carefully regular \n35|85175|7684|4|25|29004.25|0.06|0.05|N|O|1995-11-26|1995-12-25|1995-12-21|DELIVER IN PERSON|SHIP| quickly unti\n35|119917|4940|5|34|65854.94|0.08|0.06|N|O|1995-11-08|1996-01-15|1995-11-26|COLLECT COD|MAIL|. silent, unusual deposits boost\n35|30762|3266|6|28|47397.28|0.03|0.02|N|O|1996-02-01|1995-12-24|1996-02-28|COLLECT COD|RAIL|ly alongside of \n36|119767|9768|1|42|75043.92|0.09|0.00|N|O|1996-02-03|1996-01-21|1996-02-23|COLLECT COD|SHIP| careful courts. special \n37|22630|5133|1|40|62105.20|0.09|0.03|A|F|1992-07-21|1992-08-01|1992-08-15|NONE|REG AIR|luffily regular requests. slyly final acco\n37|126782|1807|2|39|70542.42|0.05|0.02|A|F|1992-07-02|1992-08-18|1992-07-28|TAKE BACK RETURN|RAIL|the final requests. ca\n37|12903|5405|3|43|78083.70|0.05|0.08|A|F|1992-07-10|1992-07-06|1992-08-02|DELIVER IN PERSON|TRUCK|iously ste\n38|175839|874|1|44|84252.52|0.04|0.02|N|O|1996-09-29|1996-11-17|1996-09-30|COLLECT COD|MAIL|s. blithely unusual theodolites am\n39|2320|9821|1|44|53782.08|0.09|0.06|N|O|1996-11-14|1996-12-15|1996-12-12|COLLECT COD|RAIL|eodolites. careful\n39|186582|4137|2|26|43383.08|0.08|0.04|N|O|1996-11-04|1996-10-20|1996-11-20|NONE|FOB|ckages across the slyly silent\n39|67831|5350|3|46|82746.18|0.06|0.08|N|O|1996-09-26|1996-12-19|1996-10-26|DELIVER IN PERSON|AIR|he carefully e\n39|20590|3093|4|32|48338.88|0.07|0.05|N|O|1996-10-02|1996-12-19|1996-10-14|COLLECT COD|MAIL|heodolites sleep silently pending foxes. ac\n39|54519|9530|5|43|63360.93|0.01|0.01|N|O|1996-10-17|1996-11-14|1996-10-26|COLLECT COD|MAIL|yly regular i\n39|94368|6878|6|40|54494.40|0.06|0.05|N|O|1996-12-08|1996-10-22|1997-01-01|COLLECT COD|AIR|quickly ironic fox\n64|85951|5952|1|21|40675.95|0.05|0.02|R|F|1994-09-30|1994-09-18|1994-10-26|DELIVER IN PERSON|REG AIR|ch slyly final, thin platelets.\n65|59694|4705|1|26|42995.94|0.03|0.03|A|F|1995-04-20|1995-04-25|1995-05-13|NONE|TRUCK|pending deposits nag even packages. ca\n65|73815|8830|2|22|39353.82|0.00|0.05|N|O|1995-07-17|1995-06-04|1995-07-19|COLLECT COD|FOB| ideas. special, r\n65|1388|3889|3|21|27076.98|0.09|0.07|N|O|1995-07-06|1995-05-14|1995-07-31|DELIVER IN PERSON|RAIL|bove the even packages. accounts nag carefu\n66|115118|7630|1|31|35126.41|0.00|0.08|R|F|1994-02-19|1994-03-11|1994-02-20|TAKE BACK RETURN|RAIL|ut the unusual accounts sleep at the bo\n66|173489|3490|2|41|64061.68|0.04|0.07|A|F|1994-02-21|1994-03-01|1994-03-18|COLLECT COD|AIR| regular de\n67|21636|9143|1|4|6230.52|0.09|0.04|N|O|1997-04-17|1997-01-31|1997-04-20|NONE|SHIP| cajole thinly expres\n67|20193|5198|2|12|13358.28|0.09|0.05|N|O|1997-01-27|1997-02-21|1997-02-22|NONE|REG AIR| even packages cajole\n67|173600|6118|3|5|8368.00|0.03|0.07|N|O|1997-02-20|1997-02-12|1997-02-21|DELIVER IN PERSON|TRUCK|y unusual packages thrash pinto \n67|87514|7515|4|44|66066.44|0.08|0.06|N|O|1997-03-18|1997-01-29|1997-04-13|DELIVER IN PERSON|RAIL|se quickly above the even, express reques\n67|40613|8126|5|23|35733.03|0.05|0.07|N|O|1997-04-19|1997-02-14|1997-05-06|DELIVER IN PERSON|REG AIR|ly regular deposit\n67|178306|824|6|29|40144.70|0.02|0.05|N|O|1997-01-25|1997-01-27|1997-01-27|DELIVER IN PERSON|FOB|ultipliers \n68|7068|9569|1|3|2925.18|0.05|0.02|N|O|1998-07-04|1998-06-05|1998-07-21|NONE|RAIL|fully special instructions cajole. furious\n68|175180|2732|2|46|57738.28|0.02|0.05|N|O|1998-06-26|1998-06-07|1998-07-05|NONE|MAIL| requests are unusual, regular pinto \n68|34980|7484|3|46|88089.08|0.04|0.05|N|O|1998-08-13|1998-07-08|1998-08-29|NONE|RAIL|egular dependencies affix ironically along \n68|94728|2256|4|20|34454.40|0.07|0.01|N|O|1998-06-27|1998-05-23|1998-07-02|NONE|REG AIR| excuses integrate fluffily \n68|82758|5267|5|27|47000.25|0.03|0.06|N|O|1998-06-19|1998-06-25|1998-06-29|DELIVER IN PERSON|SHIP|ccounts. deposits use. furiously\n68|102561|5072|6|30|46906.80|0.05|0.06|N|O|1998-08-11|1998-07-11|1998-08-14|NONE|RAIL|oxes are slyly blithely fin\n68|139247|1761|7|41|52735.84|0.09|0.08|N|O|1998-06-24|1998-06-27|1998-07-06|NONE|SHIP|eposits nag special ideas. furiousl\n69|115209|7721|1|48|58761.60|0.01|0.07|A|F|1994-08-17|1994-08-11|1994-09-08|NONE|TRUCK|regular epitaphs. carefully even ideas hag\n69|104180|9201|2|32|37893.76|0.08|0.06|A|F|1994-08-24|1994-08-17|1994-08-31|NONE|REG AIR|s sleep carefully bold, \n69|137267|4807|3|17|22172.42|0.09|0.00|A|F|1994-07-02|1994-07-07|1994-07-03|TAKE BACK RETURN|AIR|final, pending instr\n69|37502|2509|4|3|4318.50|0.09|0.04|R|F|1994-06-06|1994-07-27|1994-06-15|NONE|MAIL| blithely final d\n69|92070|7089|5|42|44606.94|0.07|0.04|R|F|1994-07-31|1994-07-26|1994-08-28|DELIVER IN PERSON|REG AIR|tect regular, speci\n69|18504|1006|6|23|32717.50|0.05|0.00|A|F|1994-10-03|1994-08-06|1994-10-24|NONE|SHIP|nding accounts ca\n70|64128|9141|1|8|8736.96|0.03|0.08|R|F|1994-01-12|1994-02-27|1994-01-14|TAKE BACK RETURN|FOB|ggle. carefully pending dependenc\n70|196156|1195|2|13|16277.95|0.06|0.06|A|F|1994-03-03|1994-02-13|1994-03-26|COLLECT COD|AIR|lyly special packag\n70|179809|7361|3|1|1888.80|0.03|0.05|R|F|1994-01-26|1994-03-05|1994-01-28|TAKE BACK RETURN|RAIL|quickly. fluffily unusual theodolites c\n70|45734|743|4|11|18477.03|0.01|0.05|A|F|1994-03-17|1994-03-17|1994-03-27|NONE|MAIL|alongside of the deposits. fur\n70|37131|2138|5|37|39520.81|0.09|0.04|R|F|1994-02-13|1994-03-16|1994-02-21|COLLECT COD|MAIL|n accounts are. q\n70|55655|3171|6|19|30602.35|0.06|0.03|A|F|1994-01-26|1994-02-17|1994-02-06|TAKE BACK RETURN|SHIP| packages wake pending accounts.\n71|61931|1932|1|25|47323.25|0.09|0.07|N|O|1998-04-10|1998-04-22|1998-04-11|COLLECT COD|FOB|ckly. slyly\n71|65916|3435|2|3|5645.73|0.09|0.07|N|O|1998-05-23|1998-04-03|1998-06-02|COLLECT COD|SHIP|y. pinto beans haggle after the\n71|34432|1942|3|45|61489.35|0.00|0.07|N|O|1998-02-23|1998-03-20|1998-03-24|DELIVER IN PERSON|SHIP| ironic packages believe blithely a\n71|96645|9155|4|33|54174.12|0.00|0.01|N|O|1998-04-12|1998-03-20|1998-04-15|NONE|FOB| serve quickly fluffily bold deposi\n71|103255|5766|5|39|49071.75|0.08|0.06|N|O|1998-01-29|1998-04-07|1998-02-18|DELIVER IN PERSON|RAIL|l accounts sleep across the pack\n71|195635|674|6|34|58841.42|0.04|0.01|N|O|1998-03-05|1998-04-22|1998-03-30|DELIVER IN PERSON|TRUCK|s cajole. \n96|123076|613|1|23|25278.61|0.10|0.06|A|F|1994-07-19|1994-06-29|1994-07-25|DELIVER IN PERSON|TRUCK|ep-- carefully reg\n96|135390|5391|2|30|42761.70|0.01|0.06|R|F|1994-06-03|1994-05-29|1994-06-22|DELIVER IN PERSON|TRUCK|e quickly even ideas. furiou\n97|119477|1989|1|13|19454.11|0.00|0.02|R|F|1993-04-01|1993-04-04|1993-04-08|NONE|TRUCK|ayers cajole against the furiously\n97|49568|2073|2|37|56149.72|0.02|0.06|A|F|1993-04-13|1993-03-30|1993-04-14|DELIVER IN PERSON|SHIP|ic requests boost carefully quic\n97|77699|5221|3|19|31857.11|0.06|0.08|R|F|1993-05-14|1993-03-05|1993-05-25|TAKE BACK RETURN|RAIL|gifts. furiously ironic packages cajole. \n98|40216|217|1|28|32373.88|0.06|0.07|A|F|1994-12-24|1994-10-25|1995-01-16|COLLECT COD|REG AIR| pending, regular accounts s\n98|109743|7274|2|1|1752.74|0.00|0.00|A|F|1994-12-01|1994-12-12|1994-12-15|DELIVER IN PERSON|TRUCK|. unusual instructions against\n98|44706|4707|3|14|23109.80|0.05|0.02|A|F|1994-12-30|1994-11-22|1995-01-27|COLLECT COD|AIR| cajole furiously. blithely ironic ideas \n98|167180|7181|4|10|12471.80|0.03|0.03|A|F|1994-10-23|1994-11-08|1994-11-09|COLLECT COD|RAIL| carefully. quickly ironic ideas\n99|87114|4639|1|10|11011.10|0.02|0.01|A|F|1994-05-18|1994-06-03|1994-05-23|COLLECT COD|RAIL|kages. requ\n99|123766|3767|2|5|8948.80|0.02|0.07|R|F|1994-05-06|1994-05-28|1994-05-20|TAKE BACK RETURN|RAIL|ests cajole fluffily waters. blithe\n99|134082|1622|3|42|46875.36|0.02|0.02|A|F|1994-04-19|1994-05-18|1994-04-20|NONE|RAIL|kages are fluffily furiously ir\n99|108338|849|4|36|48467.88|0.09|0.02|A|F|1994-07-04|1994-04-17|1994-07-30|DELIVER IN PERSON|AIR|slyly. slyly e\n100|62029|2030|1|28|27748.56|0.04|0.05|N|O|1998-05-08|1998-05-13|1998-06-07|COLLECT COD|TRUCK|sts haggle. slowl\n100|115979|8491|2|22|43889.34|0.00|0.07|N|O|1998-06-24|1998-04-12|1998-06-29|DELIVER IN PERSON|SHIP|nto beans alongside of the fi\n100|46150|8655|3|46|50422.90|0.03|0.04|N|O|1998-05-02|1998-04-10|1998-05-22|TAKE BACK RETURN|SHIP|ular accounts. even\n100|38024|3031|4|14|13468.28|0.06|0.03|N|O|1998-05-22|1998-05-01|1998-06-03|COLLECT COD|MAIL|y. furiously ironic ideas gr\n100|53439|955|5|37|51519.91|0.05|0.00|N|O|1998-03-06|1998-04-16|1998-03-31|TAKE BACK RETURN|TRUCK|nd the quickly s\n101|118282|5816|1|49|63713.72|0.10|0.00|N|O|1996-06-21|1996-05-27|1996-06-29|DELIVER IN PERSON|REG AIR|ts-- final packages sleep furiousl\n101|163334|883|2|36|50303.88|0.00|0.01|N|O|1996-05-19|1996-05-01|1996-06-04|DELIVER IN PERSON|AIR|tes. blithely pending dolphins x-ray f\n101|138418|5958|3|12|17476.92|0.06|0.02|N|O|1996-03-29|1996-04-20|1996-04-12|COLLECT COD|MAIL|. quickly regular\n102|88914|3931|1|37|70407.67|0.06|0.00|N|O|1997-07-24|1997-08-02|1997-08-07|TAKE BACK RETURN|SHIP|ully across the ideas. final deposit\n102|169238|6787|2|34|44445.82|0.03|0.08|N|O|1997-08-09|1997-07-28|1997-08-26|TAKE BACK RETURN|SHIP|eposits cajole across\n102|182321|4840|3|25|35083.00|0.01|0.01|N|O|1997-07-31|1997-07-24|1997-08-17|NONE|RAIL|bits. ironic accoun\n102|61158|8677|4|15|16787.25|0.07|0.07|N|O|1997-06-02|1997-07-13|1997-06-04|DELIVER IN PERSON|SHIP|final packages. carefully even excu\n103|194658|2216|1|6|10515.90|0.03|0.05|N|O|1996-10-11|1996-07-25|1996-10-28|NONE|FOB|cajole. carefully ex\n103|10426|2928|2|37|49447.54|0.02|0.07|N|O|1996-09-17|1996-07-27|1996-09-20|TAKE BACK RETURN|MAIL|ies. quickly ironic requests use blithely\n103|28431|8432|3|23|31266.89|0.01|0.04|N|O|1996-09-11|1996-09-18|1996-09-26|NONE|FOB|ironic accou\n103|29022|4027|4|32|30432.64|0.01|0.07|N|O|1996-07-30|1996-08-06|1996-08-04|NONE|RAIL|kages doze. special, regular deposit\n128|106828|9339|1|38|69723.16|0.06|0.01|A|F|1992-09-01|1992-08-27|1992-10-01|TAKE BACK RETURN|FOB| cajole careful\n129|2867|5368|1|46|81413.56|0.08|0.02|R|F|1993-02-15|1993-01-24|1993-03-05|COLLECT COD|TRUCK|uietly bold theodolites. fluffil\n129|185164|5165|2|36|44969.76|0.01|0.02|A|F|1992-11-25|1992-12-25|1992-12-09|TAKE BACK RETURN|REG AIR|packages are care\n129|39444|1948|3|33|45653.52|0.04|0.06|A|F|1993-01-08|1993-02-14|1993-01-29|COLLECT COD|SHIP|sts nag bravely. fluffily\n129|135137|164|4|34|39852.42|0.00|0.01|R|F|1993-01-29|1993-02-14|1993-02-10|COLLECT COD|MAIL|quests. express ideas\n129|31373|8883|5|24|31304.88|0.06|0.00|A|F|1992-12-07|1993-01-02|1992-12-11|TAKE BACK RETURN|FOB|uests. foxes cajole slyly after the ca\n129|77050|4572|6|22|22595.10|0.06|0.01|R|F|1993-02-15|1993-01-31|1993-02-24|COLLECT COD|SHIP|e. fluffily regular \n129|168569|3602|7|1|1637.56|0.05|0.04|R|F|1993-01-26|1993-01-08|1993-02-24|DELIVER IN PERSON|FOB|e carefully blithely bold dolp\n130|128816|8817|1|14|25827.34|0.08|0.05|A|F|1992-08-15|1992-07-25|1992-09-13|COLLECT COD|RAIL| requests. final instruction\n130|1739|4240|2|48|78755.04|0.03|0.02|R|F|1992-07-01|1992-07-12|1992-07-24|NONE|AIR|lithely alongside of the regu\n130|11860|1861|3|18|31893.48|0.04|0.08|A|F|1992-07-04|1992-06-14|1992-07-29|DELIVER IN PERSON|MAIL| slyly ironic decoys abou\n130|115635|3169|4|13|21458.19|0.09|0.02|R|F|1992-06-26|1992-07-29|1992-07-05|NONE|FOB| pending dolphins sleep furious\n130|69130|4143|5|31|34073.03|0.06|0.05|R|F|1992-09-01|1992-07-18|1992-09-02|TAKE BACK RETURN|RAIL|thily about the ruth\n131|167505|22|1|45|70762.50|0.10|0.02|R|F|1994-09-14|1994-09-02|1994-10-04|NONE|FOB|ironic, bold accounts. careful\n131|44255|9264|2|50|59962.50|0.02|0.04|A|F|1994-09-17|1994-08-10|1994-09-21|NONE|SHIP|ending requests. final, ironic pearls slee\n131|189021|1540|3|4|4440.08|0.04|0.03|A|F|1994-09-20|1994-08-30|1994-09-23|COLLECT COD|REG AIR| are carefully slyly i\n132|140449|2964|1|18|26809.92|0.00|0.08|R|F|1993-07-10|1993-08-05|1993-07-13|NONE|TRUCK|ges. platelets wake furio\n132|119053|9054|2|43|46098.15|0.01|0.08|R|F|1993-09-01|1993-08-16|1993-09-22|NONE|TRUCK|y pending theodolites\n132|114419|4420|3|32|45869.12|0.04|0.04|A|F|1993-07-12|1993-08-05|1993-08-05|COLLECT COD|TRUCK|d instructions hagg\n132|28082|5589|4|23|23231.84|0.10|0.00|A|F|1993-06-16|1993-08-27|1993-06-23|DELIVER IN PERSON|AIR|refully blithely bold acco\n133|103432|5943|1|27|38756.61|0.00|0.02|N|O|1997-12-21|1998-02-23|1997-12-27|TAKE BACK RETURN|MAIL|yly even gifts after the sl\n133|176279|3831|2|12|16263.24|0.02|0.06|N|O|1997-12-02|1998-01-15|1997-12-29|DELIVER IN PERSON|REG AIR|ts cajole fluffily quickly i\n133|117350|4884|3|29|39653.15|0.09|0.08|N|O|1998-02-28|1998-01-30|1998-03-09|DELIVER IN PERSON|RAIL| the carefully regular theodoli\n133|89855|7380|4|11|20293.35|0.06|0.01|N|O|1998-03-21|1998-01-15|1998-04-04|DELIVER IN PERSON|REG AIR|e quickly across the dolphins\n134|641|642|1|21|32374.44|0.00|0.03|A|F|1992-07-17|1992-07-08|1992-07-26|COLLECT COD|SHIP|s. quickly regular\n134|164645|9678|2|35|59837.40|0.06|0.07|A|F|1992-08-23|1992-06-01|1992-08-24|NONE|MAIL|ajole furiously. instructio\n134|188252|3289|3|26|34846.50|0.09|0.06|A|F|1992-06-20|1992-07-12|1992-07-16|NONE|RAIL| among the pending depos\n134|144002|4003|4|47|49162.00|0.05|0.00|A|F|1992-08-16|1992-07-06|1992-08-28|NONE|REG AIR|s! carefully unusual requests boost careful\n134|35172|5173|5|12|13286.04|0.05|0.02|A|F|1992-07-03|1992-06-01|1992-07-11|COLLECT COD|TRUCK|nts are quic\n134|133103|5617|6|12|13633.20|0.00|0.00|A|F|1992-08-08|1992-07-07|1992-08-20|TAKE BACK RETURN|FOB|lyly regular pac\n135|108205|8206|1|47|57020.40|0.06|0.08|N|O|1996-02-18|1996-01-01|1996-02-25|COLLECT COD|RAIL|ctions wake slyly abo\n135|198344|5902|2|21|30289.14|0.00|0.07|N|O|1996-02-11|1996-01-12|1996-02-13|DELIVER IN PERSON|SHIP| deposits believe. furiously regular p\n135|157510|5056|3|33|51727.83|0.02|0.00|N|O|1996-01-03|1995-11-21|1996-02-01|TAKE BACK RETURN|MAIL|ptotes boost slowly care\n135|67005|9512|4|34|33048.00|0.02|0.03|N|O|1996-01-12|1996-01-19|1996-02-05|NONE|TRUCK|counts doze against the blithely ironi\n135|136248|1275|5|20|25684.80|0.01|0.04|N|O|1996-01-25|1995-11-20|1996-02-09|NONE|MAIL|theodolites. quickly p\n135|115000|2534|6|13|13195.00|0.04|0.02|N|O|1995-11-12|1995-12-22|1995-11-17|NONE|FOB|nal ideas. final instr\n160|14785|9788|1|36|61192.08|0.07|0.01|N|O|1997-03-11|1997-03-11|1997-03-20|COLLECT COD|MAIL|old, ironic deposits are quickly abov\n160|86382|8891|2|22|30104.36|0.00|0.04|N|O|1997-02-18|1997-03-05|1997-03-05|COLLECT COD|RAIL|ncies about the request\n160|20080|5085|3|34|34002.72|0.01|0.05|N|O|1997-01-31|1997-03-13|1997-02-14|NONE|FOB|st sleep even gifts. dependencies along\n161|102810|341|1|19|34443.39|0.01|0.01|A|F|1994-12-13|1994-11-19|1994-12-26|DELIVER IN PERSON|TRUCK|, regular sheaves sleep along\n162|189288|4325|1|2|2754.56|0.02|0.01|N|O|1995-09-02|1995-06-17|1995-09-08|COLLECT COD|FOB|es! final somas integrate\n163|167545|5094|1|43|69339.22|0.01|0.00|N|O|1997-09-19|1997-11-19|1997-10-03|COLLECT COD|REG AIR|al, bold dependencies wake. iron\n163|120702|703|2|13|22395.10|0.01|0.04|N|O|1997-11-11|1997-10-18|1997-12-07|DELIVER IN PERSON|TRUCK|inal requests. even pinto beans hag\n163|36818|9322|3|27|47379.87|0.04|0.08|N|O|1997-12-26|1997-11-28|1998-01-05|COLLECT COD|REG AIR|ously express dependen\n163|192642|5162|4|5|8673.20|0.02|0.00|N|O|1997-11-17|1997-10-09|1997-12-05|DELIVER IN PERSON|TRUCK| must belie\n163|126090|8603|5|12|13393.08|0.10|0.00|N|O|1997-12-18|1997-10-26|1997-12-22|COLLECT COD|TRUCK|ly blithe accounts cajole \n163|190825|5864|6|20|38316.40|0.00|0.07|N|O|1997-09-27|1997-11-15|1997-10-07|TAKE BACK RETURN|FOB|tructions integrate b\n164|91309|3819|1|26|33807.80|0.09|0.04|A|F|1993-01-04|1992-11-21|1993-01-07|NONE|RAIL|s. blithely special courts are blithel\n164|18488|3491|2|24|33755.52|0.05|0.05|R|F|1992-12-22|1992-11-27|1993-01-06|NONE|AIR|side of the slyly unusual theodolites. f\n164|125509|3046|3|38|58311.00|0.03|0.06|R|F|1992-12-04|1992-11-23|1993-01-02|TAKE BACK RETURN|AIR|counts cajole fluffily regular packages. b\n164|17526|28|4|32|46192.64|0.05|0.01|R|F|1992-12-21|1992-12-23|1992-12-28|COLLECT COD|RAIL|ts wake again\n164|147505|2534|5|43|66757.50|0.06|0.01|R|F|1992-11-26|1993-01-03|1992-12-08|COLLECT COD|RAIL|y carefully regular dep\n164|108896|8897|6|27|51432.03|0.10|0.04|R|F|1992-12-23|1993-01-16|1993-01-10|DELIVER IN PERSON|AIR|ayers wake carefully a\n164|3037|5538|7|23|21620.69|0.09|0.04|A|F|1992-11-03|1992-12-02|1992-11-12|NONE|REG AIR|ress packages haggle ideas. blithely spec\n165|33175|8182|1|3|3324.51|0.01|0.08|R|F|1993-03-29|1993-03-06|1993-04-12|DELIVER IN PERSON|REG AIR|riously requests. depos\n165|161627|9176|2|43|72610.66|0.08|0.05|R|F|1993-02-27|1993-04-19|1993-03-03|DELIVER IN PERSON|TRUCK|jole slyly according \n165|58520|6036|3|15|22177.80|0.00|0.05|R|F|1993-04-10|1993-03-29|1993-05-01|COLLECT COD|SHIP| bold packages mainta\n165|139190|4217|4|49|60230.31|0.07|0.06|A|F|1993-02-20|1993-04-02|1993-03-10|COLLECT COD|REG AIR|uses sleep slyly ruthlessly regular a\n165|155084|7600|5|27|30755.16|0.01|0.04|R|F|1993-04-27|1993-03-04|1993-05-13|NONE|MAIL|around the ironic, even orb\n166|64888|9901|1|37|68556.56|0.09|0.03|N|O|1995-11-16|1995-10-17|1995-12-13|NONE|MAIL|lar frays wake blithely a\n166|166366|6367|2|13|18620.68|0.09|0.05|N|O|1995-11-09|1995-11-18|1995-11-14|COLLECT COD|SHIP|fully above the blithely fina\n166|99652|2162|3|41|67717.65|0.07|0.03|N|O|1995-11-13|1995-11-07|1995-12-08|COLLECT COD|FOB|hily along the blithely pending fo\n166|45027|7532|4|8|7776.16|0.05|0.02|N|O|1995-12-30|1995-11-29|1996-01-29|DELIVER IN PERSON|RAIL|e carefully bold \n167|101171|1172|1|28|32820.76|0.06|0.01|R|F|1993-02-19|1993-02-16|1993-03-03|DELIVER IN PERSON|TRUCK|sly during the u\n167|171555|4073|2|27|43916.85|0.09|0.00|R|F|1993-05-01|1993-03-31|1993-05-31|TAKE BACK RETURN|FOB|eans affix furiously-- packages\n192|97017|2036|1|23|23322.23|0.00|0.00|N|O|1998-02-05|1998-02-06|1998-03-07|TAKE BACK RETURN|AIR|ly pending theodolites haggle quickly fluf\n192|161368|8917|2|20|28587.20|0.07|0.01|N|O|1998-03-13|1998-02-02|1998-03-31|TAKE BACK RETURN|REG AIR|tes. carefu\n192|110252|5275|3|15|18933.75|0.09|0.01|N|O|1998-01-30|1998-02-10|1998-02-23|TAKE BACK RETURN|TRUCK|he ironic requests haggle about\n192|196400|3958|4|2|2992.80|0.06|0.02|N|O|1998-03-06|1998-02-03|1998-03-24|COLLECT COD|SHIP|s. dependencies nag furiously alongside\n192|82915|7932|5|25|47447.75|0.02|0.03|N|O|1998-02-15|1998-01-11|1998-03-17|COLLECT COD|TRUCK|. carefully regular\n192|141003|3518|6|45|46980.00|0.00|0.05|N|O|1998-03-11|1998-01-09|1998-04-03|NONE|MAIL|equests. ideas sleep idea\n193|92638|5148|1|9|14675.67|0.06|0.06|A|F|1993-09-17|1993-10-08|1993-09-30|COLLECT COD|TRUCK|against the fluffily regular d\n193|153954|1500|2|15|30119.25|0.02|0.07|R|F|1993-11-22|1993-10-09|1993-12-05|TAKE BACK RETURN|SHIP|ffily. regular packages d\n193|93878|6388|3|23|43053.01|0.06|0.05|A|F|1993-08-21|1993-10-11|1993-09-02|DELIVER IN PERSON|TRUCK|ly even accounts wake blithely bold\n194|2594|5095|1|17|25442.03|0.05|0.04|R|F|1992-05-24|1992-05-22|1992-05-30|COLLECT COD|AIR| regular deposi\n194|183523|6042|2|1|1606.52|0.04|0.06|R|F|1992-04-30|1992-05-18|1992-05-23|NONE|REG AIR| regular theodolites. regular, iron\n194|65994|3513|3|13|25479.87|0.08|0.08|A|F|1992-05-07|1992-06-18|1992-05-10|NONE|AIR|about the blit\n194|145146|5147|4|36|42881.04|0.00|0.05|R|F|1992-05-21|1992-05-18|1992-05-27|TAKE BACK RETURN|RAIL|pecial packages wake after the slyly r\n194|56176|1187|5|8|9057.36|0.04|0.00|R|F|1992-07-06|1992-06-25|1992-07-11|COLLECT COD|FOB|uriously unusual excuses\n194|148984|1499|6|16|32527.68|0.06|0.03|A|F|1992-05-14|1992-06-14|1992-05-21|TAKE BACK RETURN|TRUCK|y regular requests. furious\n194|167828|345|7|21|39812.22|0.02|0.01|R|F|1992-05-06|1992-05-20|1992-05-07|COLLECT COD|REG AIR|accounts detect quickly dogged \n195|84590|9607|1|6|9447.54|0.04|0.02|A|F|1994-01-09|1994-03-27|1994-01-28|COLLECT COD|REG AIR|y, even deposits haggle carefully. bli\n195|93847|1375|2|41|75474.44|0.05|0.07|A|F|1994-02-24|1994-02-11|1994-03-20|NONE|TRUCK|rts detect in place of t\n195|85446|7955|3|34|48668.96|0.08|0.08|R|F|1994-01-31|1994-02-11|1994-02-12|NONE|TRUCK| cajole furiously bold i\n195|85442|7951|4|41|58525.04|0.06|0.04|R|F|1994-03-14|1994-03-13|1994-04-09|COLLECT COD|RAIL|ggle fluffily foxes. fluffily ironic ex\n196|135052|79|1|19|20653.95|0.03|0.02|R|F|1993-04-17|1993-05-27|1993-04-30|NONE|SHIP|sts maintain foxes. furiously regular p\n196|9852|2353|2|15|26427.75|0.03|0.04|A|F|1993-07-05|1993-05-08|1993-07-06|TAKE BACK RETURN|SHIP|s accounts. furio\n197|98494|1004|1|39|58207.11|0.02|0.04|N|O|1995-07-21|1995-07-01|1995-08-14|TAKE BACK RETURN|AIR|press accounts. daringly sp\n197|177103|9621|2|8|9440.80|0.09|0.02|A|F|1995-04-17|1995-07-01|1995-04-27|DELIVER IN PERSON|SHIP|y blithely even deposits. blithely fina\n197|155829|8345|3|17|32041.94|0.06|0.02|N|O|1995-08-02|1995-06-23|1995-08-03|COLLECT COD|REG AIR|ts. careful\n197|17936|2939|4|25|46348.25|0.04|0.01|N|F|1995-06-13|1995-05-23|1995-06-24|TAKE BACK RETURN|FOB|s-- quickly final accounts\n197|41466|3971|5|14|19704.44|0.09|0.01|R|F|1995-05-08|1995-05-24|1995-05-12|TAKE BACK RETURN|RAIL|use slyly slyly silent depo\n197|105880|901|6|1|1885.88|0.07|0.05|N|O|1995-07-15|1995-06-21|1995-08-11|COLLECT COD|RAIL| even, thin dependencies sno\n198|56061|6062|1|33|33562.98|0.07|0.02|N|O|1998-01-05|1998-03-20|1998-01-10|TAKE BACK RETURN|TRUCK|carefully caref\n198|15229|7731|2|20|22884.40|0.03|0.00|N|O|1998-01-15|1998-03-31|1998-01-25|DELIVER IN PERSON|FOB|carefully final escapades a\n198|148058|3087|3|15|16590.75|0.04|0.02|N|O|1998-04-12|1998-02-26|1998-04-15|COLLECT COD|MAIL|es. quickly pending deposits s\n198|10371|2873|4|35|44847.95|0.08|0.02|N|O|1998-02-27|1998-03-23|1998-03-14|TAKE BACK RETURN|RAIL|ests nod quickly furiously sly pinto be\n198|101952|1953|5|33|64480.35|0.02|0.01|N|O|1998-03-22|1998-03-12|1998-04-14|DELIVER IN PERSON|SHIP|ending foxes acr\n199|132072|9612|1|50|55203.50|0.02|0.00|N|O|1996-06-12|1996-06-03|1996-07-04|DELIVER IN PERSON|MAIL|essly regular ideas boost sly\n199|133998|3999|2|30|60959.70|0.08|0.05|N|O|1996-03-27|1996-05-29|1996-04-14|NONE|TRUCK|ilent packages doze quickly. thinly \n224|150111|112|1|16|18577.76|0.04|0.00|A|F|1994-08-01|1994-07-30|1994-08-27|DELIVER IN PERSON|MAIL|y unusual foxes \n224|108609|1120|2|34|54998.40|0.04|0.08|R|F|1994-07-13|1994-08-25|1994-07-31|COLLECT COD|TRUCK| carefully. final platelets \n224|189967|7522|3|41|84335.36|0.07|0.04|A|F|1994-09-01|1994-09-15|1994-09-02|TAKE BACK RETURN|SHIP|after the furiou\n224|166377|1410|4|12|17320.44|0.08|0.06|R|F|1994-10-12|1994-08-29|1994-10-20|DELIVER IN PERSON|MAIL|uriously regular packages. slyly fina\n224|93857|8876|5|45|83288.25|0.07|0.07|R|F|1994-08-14|1994-09-02|1994-08-27|COLLECT COD|AIR|leep furiously regular requests. furiousl\n224|50010|7526|6|4|3840.04|0.02|0.00|R|F|1994-09-08|1994-08-24|1994-10-04|DELIVER IN PERSON|FOB|tructions \n225|171925|1926|1|4|7987.68|0.09|0.07|N|O|1995-08-05|1995-08-19|1995-09-03|TAKE BACK RETURN|SHIP|ng the ironic packages. asymptotes among \n225|130565|8105|2|3|4786.68|0.00|0.08|N|O|1995-07-25|1995-07-08|1995-08-17|DELIVER IN PERSON|REG AIR| fluffily about the carefully bold a\n225|198212|3251|3|45|58959.45|0.06|0.01|N|O|1995-08-17|1995-08-20|1995-08-30|TAKE BACK RETURN|FOB|the slyly even platelets use aro\n225|146071|8586|4|24|26809.68|0.00|0.06|N|O|1995-09-23|1995-08-05|1995-10-16|COLLECT COD|MAIL|ironic accounts are final account\n225|7589|5090|5|31|46393.98|0.04|0.06|N|O|1995-06-21|1995-07-24|1995-07-04|TAKE BACK RETURN|FOB|special platelets. quickly r\n225|131835|9375|6|12|22401.96|0.00|0.00|A|F|1995-06-04|1995-07-15|1995-06-08|COLLECT COD|MAIL| unusual requests. bus\n225|141233|8776|7|44|56066.12|0.10|0.06|N|O|1995-09-22|1995-08-16|1995-10-22|NONE|REG AIR|leep slyly \n226|96909|9419|1|4|7623.60|0.00|0.00|R|F|1993-03-31|1993-04-30|1993-04-10|NONE|TRUCK|c foxes integrate carefully against th\n226|137802|5342|2|46|84630.80|0.06|0.01|A|F|1993-07-06|1993-04-24|1993-07-13|COLLECT COD|FOB|s. carefully bold accounts cajol\n226|37309|4819|3|35|43620.50|0.09|0.03|A|F|1993-03-31|1993-05-18|1993-04-01|NONE|RAIL|osits cajole. final, even foxes a\n226|40633|8146|4|45|70813.35|0.10|0.02|R|F|1993-04-17|1993-05-27|1993-05-11|DELIVER IN PERSON|AIR| carefully pending pi\n226|117956|5490|5|2|3947.90|0.07|0.02|R|F|1993-03-26|1993-04-13|1993-04-20|TAKE BACK RETURN|SHIP|al platelets. express somas \n226|82937|7954|6|48|92156.64|0.02|0.00|A|F|1993-06-11|1993-05-15|1993-06-19|NONE|REG AIR|efully silent packages. final deposit\n226|117961|5495|7|14|27705.44|0.09|0.00|R|F|1993-05-20|1993-06-05|1993-05-27|COLLECT COD|MAIL|ep carefully regular accounts. ironic\n227|165335|2884|1|19|26606.27|0.05|0.06|N|O|1995-12-10|1996-01-30|1995-12-26|NONE|RAIL|s cajole furiously a\n227|174102|1654|2|24|28226.40|0.07|0.07|N|O|1996-02-03|1995-12-24|1996-02-12|COLLECT COD|SHIP|uses across the blithe dependencies cajol\n228|4039|6540|1|3|2829.09|0.10|0.08|A|F|1993-05-20|1993-04-08|1993-05-26|DELIVER IN PERSON|SHIP|ckages. sly\n229|83580|8597|1|20|31271.60|0.02|0.03|R|F|1994-01-11|1994-01-31|1994-01-26|DELIVER IN PERSON|REG AIR|le. instructions use across the quickly fin\n229|128904|8905|2|29|56054.10|0.07|0.00|A|F|1994-03-15|1994-03-02|1994-03-26|COLLECT COD|SHIP|s, final request\n229|78526|8527|3|28|42126.56|0.02|0.02|R|F|1994-02-10|1994-02-02|1994-03-10|DELIVER IN PERSON|FOB| final, regular requests. platel\n229|176948|1983|4|3|6074.82|0.02|0.08|R|F|1994-03-22|1994-03-24|1994-04-04|DELIVER IN PERSON|REG AIR|posits. furiously regular theodol\n229|155180|211|5|33|40760.94|0.03|0.06|R|F|1994-03-25|1994-02-11|1994-04-13|NONE|FOB| deposits; bold, ruthless theodolites\n229|105393|7904|6|29|40553.31|0.04|0.00|R|F|1994-01-14|1994-02-16|1994-01-22|NONE|FOB|uriously pending \n230|185863|900|1|46|89647.56|0.09|0.00|R|F|1994-02-03|1994-01-15|1994-02-23|TAKE BACK RETURN|SHIP|old packages ha\n230|194908|7428|2|6|12017.40|0.03|0.08|A|F|1994-01-26|1994-01-25|1994-02-13|NONE|REG AIR| sleep furiously about the p\n230|7367|4868|3|1|1274.36|0.07|0.06|R|F|1994-01-22|1994-01-03|1994-02-05|TAKE BACK RETURN|RAIL|blithely unusual dolphins. bold, ex\n230|9164|1665|4|44|47219.04|0.08|0.06|R|F|1994-02-09|1994-01-18|1994-03-11|NONE|MAIL|deposits integrate slyly sile\n230|18923|6427|5|8|14735.36|0.09|0.06|R|F|1993-11-03|1994-01-20|1993-11-11|TAKE BACK RETURN|TRUCK|g the instructions. fluffil\n230|33927|1437|6|8|14887.36|0.00|0.05|R|F|1993-11-21|1994-01-05|1993-12-19|TAKE BACK RETURN|FOB|nal ideas. silent, reg\n231|158356|8357|1|16|22629.60|0.04|0.08|R|F|1994-11-20|1994-10-29|1994-12-17|TAKE BACK RETURN|AIR|e furiously ironic pinto beans.\n231|83359|884|2|46|61748.10|0.04|0.05|R|F|1994-12-13|1994-12-02|1994-12-14|DELIVER IN PERSON|SHIP|affix blithely. bold requests among the f\n231|198124|644|3|50|61106.00|0.09|0.01|A|F|1994-12-11|1994-12-14|1994-12-13|NONE|RAIL|onic packages haggle fluffily a\n231|56760|6761|4|31|53219.56|0.08|0.02|A|F|1994-11-05|1994-12-27|1994-11-30|TAKE BACK RETURN|SHIP|iously special decoys wake q\n256|88233|742|1|22|26867.06|0.09|0.02|R|F|1994-01-12|1993-12-28|1994-01-26|COLLECT COD|FOB|ke quickly ironic, ironic deposits. reg\n256|118399|3422|2|40|56695.60|0.10|0.01|A|F|1993-11-30|1993-12-13|1993-12-02|NONE|FOB|nal theodolites. deposits cajole s\n256|129111|4136|3|45|51304.95|0.02|0.08|R|F|1994-01-14|1994-01-17|1994-02-10|COLLECT COD|SHIP| grouches. ideas wake quickly ar\n257|146229|6230|1|7|8926.54|0.05|0.02|N|O|1998-06-18|1998-05-15|1998-06-27|COLLECT COD|FOB|ackages sleep bold realms. f\n258|106194|3725|1|8|9601.52|0.00|0.07|R|F|1994-01-20|1994-03-21|1994-02-09|NONE|REG AIR|ully about the fluffily silent dependencies\n258|196119|3677|2|40|48604.40|0.10|0.01|A|F|1994-03-13|1994-02-23|1994-04-05|DELIVER IN PERSON|FOB|silent frets nod daringly busy, bold\n258|161762|1763|3|45|82069.20|0.07|0.07|R|F|1994-03-04|1994-02-13|1994-03-30|DELIVER IN PERSON|TRUCK|regular excuses-- fluffily ruthl\n258|132912|5426|4|31|60292.21|0.02|0.05|A|F|1994-04-20|1994-03-20|1994-04-28|COLLECT COD|REG AIR| slyly blithely special mul\n258|35959|8463|5|25|47373.75|0.08|0.02|A|F|1994-04-13|1994-02-26|1994-04-29|TAKE BACK RETURN|TRUCK|leep pending packages.\n258|146467|8982|6|36|54484.56|0.09|0.04|A|F|1994-01-11|1994-03-04|1994-01-18|DELIVER IN PERSON|AIR|nic asymptotes. slyly silent r\n259|98779|8780|1|14|24888.78|0.00|0.08|A|F|1993-12-17|1993-12-09|1993-12-31|COLLECT COD|SHIP|ons against the express acco\n259|161982|4499|2|14|28615.72|0.03|0.05|R|F|1993-11-10|1993-11-20|1993-11-17|DELIVER IN PERSON|FOB|ully even, regul\n259|23514|3515|3|42|60375.42|0.09|0.00|R|F|1993-10-20|1993-11-18|1993-11-12|NONE|TRUCK|the slyly ironic pinto beans. fi\n259|195335|2893|4|3|4290.99|0.08|0.06|R|F|1993-10-04|1993-11-07|1993-10-14|TAKE BACK RETURN|SHIP|ng slyly at the accounts.\n259|192201|7240|5|6|7759.20|0.00|0.05|R|F|1993-12-05|1993-12-22|1993-12-21|COLLECT COD|TRUCK| requests sleep\n260|155887|5888|1|50|97144.00|0.07|0.08|N|O|1997-03-24|1997-02-09|1997-04-20|TAKE BACK RETURN|REG AIR|c deposits \n260|182736|2737|2|26|47286.98|0.02|0.07|N|O|1996-12-12|1997-02-06|1996-12-15|NONE|TRUCK|ld theodolites boost fl\n260|41222|8735|3|27|31406.94|0.05|0.08|N|O|1997-03-23|1997-02-15|1997-04-22|TAKE BACK RETURN|RAIL|ions according to the\n260|5337|338|4|29|36027.57|0.10|0.06|N|O|1997-03-15|1997-01-14|1997-04-13|NONE|MAIL|fluffily even asymptotes. express wa\n260|95286|305|5|44|56376.32|0.01|0.05|N|O|1997-03-26|1997-02-03|1997-04-19|DELIVER IN PERSON|MAIL|above the blithely ironic instr\n261|1349|6350|1|34|42511.56|0.05|0.08|R|F|1993-08-18|1993-09-24|1993-08-20|COLLECT COD|REG AIR|c packages. asymptotes da\n261|65662|5663|2|20|32553.20|0.00|0.06|R|F|1993-10-21|1993-08-02|1993-11-04|DELIVER IN PERSON|RAIL|ites hinder \n261|173959|8994|3|28|56922.60|0.08|0.03|R|F|1993-07-24|1993-08-20|1993-08-05|COLLECT COD|AIR|ironic packages nag slyly. carefully fin\n261|118455|967|4|49|72199.05|0.04|0.05|R|F|1993-09-12|1993-08-31|1993-10-07|COLLECT COD|SHIP|ions. bold accounts \n261|60469|7988|5|49|70043.54|0.01|0.08|A|F|1993-09-29|1993-09-08|1993-10-01|COLLECT COD|SHIP| pinto beans haggle slyly furiously pending\n261|96989|9499|6|20|39719.60|0.06|0.06|A|F|1993-10-15|1993-09-05|1993-11-07|NONE|AIR|ing to the special, ironic deposi\n262|191186|1187|1|39|49810.02|0.01|0.05|N|O|1996-01-15|1996-02-18|1996-01-28|COLLECT COD|RAIL|usual, regular requests\n262|60074|7593|2|33|34124.31|0.09|0.03|N|O|1996-03-10|1996-01-31|1996-03-27|TAKE BACK RETURN|AIR|atelets sleep furiously. requests cajole. b\n262|58695|6211|3|35|57879.15|0.05|0.08|N|O|1996-03-12|1996-02-14|1996-04-11|COLLECT COD|MAIL|lites cajole along the pending packag\n263|23960|6463|1|22|41447.12|0.06|0.08|R|F|1994-08-24|1994-06-20|1994-09-09|NONE|FOB|efully express fo\n263|84557|9574|2|9|13873.95|0.08|0.00|A|F|1994-07-21|1994-07-16|1994-08-08|TAKE BACK RETURN|TRUCK|lms wake bl\n263|142891|434|3|50|96694.50|0.06|0.04|R|F|1994-08-18|1994-07-31|1994-08-22|NONE|TRUCK|re the packages. special\n288|50641|8157|1|31|49340.84|0.00|0.03|N|O|1997-03-17|1997-04-28|1997-04-06|TAKE BACK RETURN|AIR|instructions wa\n288|116386|8898|2|49|68716.62|0.08|0.05|N|O|1997-04-19|1997-05-19|1997-05-18|TAKE BACK RETURN|TRUCK|ic excuses sleep always spe\n288|98833|8834|3|36|65945.88|0.02|0.02|N|O|1997-02-22|1997-05-07|1997-03-07|TAKE BACK RETURN|TRUCK|yly pending excu\n288|78406|8407|4|19|26303.60|0.07|0.07|N|O|1997-03-14|1997-04-04|1997-03-26|NONE|MAIL|deposits. blithely quick courts ar\n288|161894|6927|5|31|60632.59|0.10|0.04|N|O|1997-05-29|1997-04-24|1997-06-20|TAKE BACK RETURN|RAIL|ns. fluffily\n289|173280|832|1|25|33832.00|0.07|0.05|N|O|1997-03-18|1997-05-05|1997-04-15|DELIVER IN PERSON|FOB|out the quickly bold theodol\n289|111800|9334|2|6|10870.80|0.06|0.05|N|O|1997-02-18|1997-05-08|1997-03-19|DELIVER IN PERSON|SHIP|d packages use fluffily furiously\n289|16996|1999|3|44|84171.56|0.10|0.08|N|O|1997-06-05|1997-04-20|1997-07-02|COLLECT COD|MAIL|ly ironic foxes. asymptotes \n289|39439|1943|4|48|66164.64|0.01|0.08|N|O|1997-03-14|1997-03-30|1997-03-24|DELIVER IN PERSON|RAIL|sits cajole. bold pinto beans x-ray fl\n289|46285|8790|5|13|16006.64|0.10|0.03|N|O|1997-06-08|1997-04-06|1997-06-18|TAKE BACK RETURN|REG AIR|ts. quickly bold deposits alongside\n290|5351|352|1|35|43972.25|0.01|0.02|R|F|1994-04-01|1994-02-05|1994-04-27|NONE|MAIL|ove the final foxes detect slyly fluffily\n290|128923|1436|2|2|3903.84|0.05|0.04|A|F|1994-01-30|1994-02-13|1994-02-21|TAKE BACK RETURN|TRUCK|. permanently furious reques\n290|1888|4389|3|5|8949.40|0.03|0.05|A|F|1994-01-19|1994-02-24|1994-01-27|NONE|MAIL|ans integrate. requests sleep. fur\n290|123741|6254|4|23|40589.02|0.05|0.08|R|F|1994-03-14|1994-02-21|1994-04-09|NONE|AIR|refully unusual packages. \n291|122565|102|1|21|33338.76|0.05|0.07|A|F|1994-05-26|1994-05-10|1994-06-23|COLLECT COD|TRUCK|y quickly regular theodolites. final t\n291|137316|7317|2|19|25712.89|0.08|0.02|R|F|1994-06-14|1994-04-25|1994-06-19|NONE|REG AIR|e. ruthlessly final accounts after the\n291|60874|5887|3|30|55046.10|0.10|0.02|R|F|1994-03-22|1994-04-30|1994-03-24|DELIVER IN PERSON|FOB| fluffily regular deposits. quickl\n292|153561|3562|1|8|12916.48|0.10|0.03|R|F|1992-02-18|1992-03-30|1992-03-18|DELIVER IN PERSON|RAIL|sily bold deposits alongside of the ex\n292|99249|9250|2|24|29957.76|0.08|0.04|R|F|1992-03-24|1992-03-06|1992-04-20|COLLECT COD|TRUCK| bold, pending theodolites u\n293|8960|6461|1|14|26165.44|0.02|0.05|R|F|1992-10-19|1992-12-23|1992-11-10|DELIVER IN PERSON|SHIP|es. packages above the\n293|186406|6407|2|11|16416.40|0.10|0.04|R|F|1992-12-24|1992-12-01|1993-01-12|COLLECT COD|MAIL| affix carefully quickly special idea\n293|117267|4801|3|13|16695.38|0.04|0.02|A|F|1992-12-17|1992-12-26|1992-12-22|COLLECT COD|RAIL| wake after the quickly even deposits. bli\n294|59620|7136|1|31|48968.22|0.00|0.01|R|F|1993-08-06|1993-08-19|1993-08-13|TAKE BACK RETURN|AIR|le fluffily along the quick\n295|197507|27|1|29|46530.50|0.02|0.07|A|F|1994-11-09|1994-12-08|1994-12-07|COLLECT COD|MAIL|inst the carefully ironic pinto beans. blit\n295|91344|8872|2|26|34718.84|0.04|0.03|R|F|1994-12-13|1994-11-30|1995-01-06|DELIVER IN PERSON|AIR|ts above the slyly regular requests x-ray q\n295|15283|7785|3|8|9586.24|0.10|0.07|R|F|1995-01-13|1994-11-17|1995-01-25|NONE|TRUCK| final instructions h\n295|60621|3128|4|26|41122.12|0.10|0.04|A|F|1995-01-12|1994-11-22|1995-01-22|DELIVER IN PERSON|MAIL| carefully iron\n320|4415|1916|1|30|39582.30|0.05|0.01|N|O|1997-12-04|1998-01-21|1997-12-13|NONE|RAIL| ironic, final accounts wake quick de\n320|192158|4678|2|13|16251.95|0.03|0.00|N|O|1997-12-16|1997-12-26|1997-12-17|TAKE BACK RETURN|AIR|he furiously regular pinto beans. car\n321|318|7819|1|21|25584.51|0.01|0.08|A|F|1993-07-18|1993-04-24|1993-08-13|TAKE BACK RETURN|REG AIR|hockey players sleep slyly sl\n321|140433|5462|2|41|60410.63|0.08|0.07|R|F|1993-06-21|1993-06-07|1993-07-09|NONE|REG AIR|special packages shall have to doze blit\n322|152499|7530|1|12|18617.88|0.08|0.07|A|F|1992-06-29|1992-05-30|1992-07-11|NONE|AIR|ular theodolites promise qu\n322|43662|3663|2|48|77071.68|0.02|0.07|A|F|1992-06-11|1992-06-16|1992-06-26|COLLECT COD|RAIL|dolites detect qu\n322|12673|177|3|20|31713.40|0.04|0.01|R|F|1992-04-26|1992-05-04|1992-05-22|DELIVER IN PERSON|MAIL|ckly toward \n322|183246|5765|4|10|13292.40|0.06|0.03|R|F|1992-04-12|1992-05-13|1992-04-14|DELIVER IN PERSON|AIR| deposits grow slyly according to th\n322|11605|9109|5|35|53081.00|0.07|0.06|A|F|1992-07-17|1992-05-03|1992-08-14|TAKE BACK RETURN|RAIL|egular accounts cajole carefully. even d\n322|33310|8317|6|3|3729.93|0.08|0.05|A|F|1992-07-03|1992-05-10|1992-07-28|NONE|AIR|ending, ironic deposits along the blith\n322|37435|4945|7|5|6862.15|0.01|0.02|A|F|1992-04-15|1992-05-12|1992-04-26|COLLECT COD|REG AIR| special grouches sleep quickly instructio\n323|163628|1177|1|50|84581.00|0.05|0.04|A|F|1994-04-20|1994-04-25|1994-05-12|DELIVER IN PERSON|REG AIR|cial requests \n323|95136|7646|2|18|20360.34|0.06|0.07|R|F|1994-04-13|1994-06-02|1994-05-10|DELIVER IN PERSON|TRUCK|posits cajole furiously pinto beans. \n323|142725|2726|3|9|15909.48|0.07|0.04|A|F|1994-06-26|1994-06-10|1994-07-13|COLLECT COD|TRUCK|nic accounts. regular, regular pack\n324|199475|4514|1|26|40936.22|0.07|0.01|R|F|1992-04-19|1992-05-28|1992-05-12|DELIVER IN PERSON|RAIL|ross the slyly regular s\n325|158791|6337|1|34|62892.86|0.09|0.04|A|F|1993-10-28|1993-12-13|1993-11-17|TAKE BACK RETURN|MAIL|ly bold deposits. always iron\n325|185139|7658|2|5|6120.65|0.07|0.08|A|F|1994-01-02|1994-01-05|1994-01-04|TAKE BACK RETURN|MAIL| theodolites. \n325|18788|1290|3|35|59737.30|0.07|0.07|A|F|1993-12-06|1994-01-03|1993-12-26|DELIVER IN PERSON|REG AIR|packages wa\n326|179094|4129|1|41|48096.69|0.06|0.03|N|O|1995-08-30|1995-07-09|1995-09-12|DELIVER IN PERSON|TRUCK|ily quickly bold ideas.\n326|19480|1982|2|38|53180.24|0.02|0.08|N|O|1995-09-12|1995-08-23|1995-09-14|COLLECT COD|RAIL|es sleep slyly. carefully regular inst\n326|183739|8776|3|25|45568.25|0.03|0.04|N|O|1995-08-03|1995-07-27|1995-08-16|NONE|AIR|ily furiously unusual accounts. \n326|84836|9853|4|5|9104.15|0.03|0.08|N|O|1995-07-29|1995-07-13|1995-08-12|NONE|REG AIR|deas sleep according to the sometimes spe\n326|34543|9550|5|31|45803.74|0.04|0.08|N|O|1995-09-27|1995-07-06|1995-10-22|NONE|TRUCK|cies sleep quick\n326|156712|4258|6|41|72517.11|0.02|0.00|N|O|1995-07-05|1995-07-23|1995-07-20|TAKE BACK RETURN|REG AIR|to beans wake before the furiously re\n326|42134|4639|7|47|50578.11|0.04|0.04|N|O|1995-09-16|1995-07-04|1995-10-04|NONE|REG AIR| special accounts sleep \n327|143503|1046|1|16|24744.00|0.03|0.01|N|O|1995-07-05|1995-06-07|1995-07-09|TAKE BACK RETURN|TRUCK|cial ideas sleep af\n327|41715|4220|2|9|14910.39|0.09|0.05|A|F|1995-05-24|1995-07-11|1995-06-05|NONE|AIR| asymptotes are fu\n352|63762|3763|1|17|29337.92|0.07|0.05|R|F|1994-06-02|1994-05-31|1994-06-29|NONE|FOB|pending deposits sleep furiously \n353|119305|4328|1|41|54296.30|0.00|0.06|A|F|1994-03-25|1994-03-31|1994-03-30|DELIVER IN PERSON|AIR|refully final theodoli\n353|147542|7543|2|29|46096.66|0.09|0.00|A|F|1994-01-11|1994-03-19|1994-02-09|COLLECT COD|FOB|ctions impr\n353|134318|1858|3|12|16227.72|0.06|0.01|R|F|1994-01-02|1994-03-26|1994-01-19|DELIVER IN PERSON|RAIL|g deposits cajole \n353|77071|2086|4|46|48211.22|0.00|0.04|A|F|1994-04-14|1994-01-31|1994-05-05|DELIVER IN PERSON|FOB| ironic dolphins \n353|116803|1826|5|9|16378.20|0.02|0.02|A|F|1994-03-15|1994-03-20|1994-03-18|TAKE BACK RETURN|RAIL|ual accounts! carefu\n353|102699|2700|6|39|66365.91|0.02|0.05|A|F|1994-01-15|1994-03-30|1994-02-01|NONE|MAIL|losely quickly even accounts. c\n354|49480|1985|1|14|20012.72|0.08|0.04|N|O|1996-04-12|1996-06-03|1996-05-08|NONE|SHIP|quickly regular grouches will eat. careful\n354|193864|1422|2|24|46988.64|0.01|0.01|N|O|1996-05-08|1996-05-17|1996-06-07|DELIVER IN PERSON|AIR|y silent requests. regular, even accounts\n354|58125|8126|3|50|54156.00|0.08|0.05|N|O|1996-03-21|1996-05-20|1996-04-04|COLLECT COD|TRUCK|to beans s\n354|106672|4203|4|7|11750.69|0.06|0.01|N|O|1996-05-07|1996-04-18|1996-05-24|NONE|MAIL|ously idly ironic accounts-- quickl\n354|30527|528|5|18|26235.36|0.04|0.08|N|O|1996-03-31|1996-05-13|1996-04-27|DELIVER IN PERSON|RAIL| about the carefully unusual \n354|61082|3589|6|36|37550.88|0.03|0.02|N|O|1996-03-19|1996-05-29|1996-03-30|NONE|AIR|onic requests thrash bold g\n354|4660|9661|7|14|21905.24|0.01|0.07|N|O|1996-07-06|1996-06-08|1996-07-10|TAKE BACK RETURN|MAIL|t thinly above the ironic, \n355|113959|8982|1|31|61161.45|0.09|0.07|A|F|1994-07-13|1994-08-18|1994-07-18|DELIVER IN PERSON|FOB|y unusual, ironic\n355|96030|3558|2|41|42067.23|0.05|0.00|A|F|1994-08-15|1994-07-19|1994-09-06|DELIVER IN PERSON|TRUCK| deposits. carefully r\n356|45214|5215|1|4|4636.84|0.10|0.01|A|F|1994-07-28|1994-08-01|1994-08-04|DELIVER IN PERSON|REG AIR| the dependencies nod unusual, final ac\n356|107463|2484|2|48|70582.08|0.02|0.03|R|F|1994-08-12|1994-07-31|1994-08-26|NONE|FOB|unusual packages. furiously \n356|118002|514|3|35|35700.00|0.08|0.07|R|F|1994-10-14|1994-07-31|1994-10-23|COLLECT COD|TRUCK|s. unusual, final\n356|55342|353|4|41|53190.94|0.07|0.05|A|F|1994-09-28|1994-09-20|1994-10-07|COLLECT COD|SHIP| according to the express foxes will\n356|124271|1808|5|37|47924.99|0.05|0.03|A|F|1994-07-15|1994-08-24|1994-08-09|DELIVER IN PERSON|FOB|ndencies are since the packag\n357|113143|3144|1|26|30059.64|0.06|0.03|N|O|1996-12-28|1996-11-26|1997-01-13|NONE|FOB| carefully pending accounts use a\n357|185814|8333|2|36|68393.16|0.07|0.06|N|O|1996-12-28|1996-11-13|1997-01-24|DELIVER IN PERSON|AIR|d the carefully even requests. \n357|164807|9840|3|32|59897.60|0.05|0.07|N|O|1997-01-28|1996-12-29|1997-02-14|NONE|MAIL|y above the carefully final accounts\n358|190028|2548|1|41|45838.82|0.06|0.01|A|F|1993-11-18|1993-11-14|1993-11-28|NONE|TRUCK|ely frets. furious deposits sleep \n358|189955|7510|2|32|65438.40|0.05|0.08|A|F|1993-10-18|1993-12-12|1993-10-31|NONE|TRUCK|y final foxes sleep blithely sl\n358|168710|3743|3|40|71148.40|0.09|0.01|A|F|1993-12-05|1993-11-04|1994-01-01|COLLECT COD|MAIL|ng the ironic theo\n358|96557|1576|4|15|23303.25|0.08|0.08|A|F|1993-10-04|1993-12-17|1993-10-23|TAKE BACK RETURN|MAIL|out the blithely ironic deposits slee\n358|28629|6136|5|18|28037.16|0.01|0.02|R|F|1993-10-07|1993-11-01|1993-10-26|COLLECT COD|SHIP|olphins haggle ironic accounts. f\n358|161283|1284|6|32|43016.96|0.03|0.05|R|F|1993-12-21|1993-11-06|1994-01-17|DELIVER IN PERSON|RAIL|lyly express deposits \n358|82916|7933|7|45|85450.95|0.05|0.02|A|F|1993-12-08|1993-10-29|1993-12-30|NONE|REG AIR|to beans. regular, unusual deposits sl\n359|165980|5981|1|30|61379.40|0.00|0.08|A|F|1995-01-06|1995-02-20|1995-01-20|TAKE BACK RETURN|AIR|uses detect spec\n359|11158|6161|2|18|19244.70|0.00|0.03|A|F|1995-01-27|1995-03-18|1995-01-31|DELIVER IN PERSON|RAIL|unusual warthogs. ironically sp\n359|131463|3977|3|17|25405.82|0.07|0.06|A|F|1995-01-31|1995-03-18|1995-02-10|COLLECT COD|SHIP|sts according to the blithely\n359|89985|2494|4|38|75049.24|0.10|0.08|R|F|1995-03-30|1995-01-20|1995-04-25|DELIVER IN PERSON|RAIL|g furiously. regular, sile\n359|167239|2272|5|11|14368.53|0.01|0.03|A|F|1995-02-15|1995-01-27|1995-02-18|NONE|FOB|rets wake blithely. slyly final dep\n359|182663|218|6|23|40150.18|0.04|0.07|R|F|1995-01-31|1995-03-11|1995-02-16|DELIVER IN PERSON|REG AIR|ic courts snooze quickly furiously final fo\n384|178442|3477|1|38|57776.72|0.07|0.01|R|F|1992-06-02|1992-04-18|1992-06-10|DELIVER IN PERSON|TRUCK|totes cajole blithely against the even\n384|63342|5849|2|49|63961.66|0.09|0.07|A|F|1992-04-01|1992-04-25|1992-04-18|COLLECT COD|AIR|refully carefully ironic instructions. bl\n384|181502|6539|3|11|17418.50|0.02|0.08|A|F|1992-04-02|1992-04-21|1992-04-15|COLLECT COD|MAIL|ash carefully\n384|92053|7072|4|11|11495.55|0.00|0.06|R|F|1992-06-24|1992-05-29|1992-07-22|COLLECT COD|TRUCK|nic excuses are furiously above the blith\n384|131403|8943|5|14|20081.60|0.08|0.06|R|F|1992-06-14|1992-05-29|1992-07-05|DELIVER IN PERSON|TRUCK|ckages are slyly after the slyly specia\n385|166446|8963|1|7|10587.08|0.05|0.06|N|O|1996-05-23|1996-05-09|1996-06-06|DELIVER IN PERSON|REG AIR| special asymptote\n385|53025|8036|2|46|44988.92|0.08|0.07|N|O|1996-03-29|1996-05-17|1996-04-18|NONE|REG AIR|lthily ironic f\n386|152405|9951|1|39|56838.60|0.10|0.07|A|F|1995-05-10|1995-02-28|1995-05-25|NONE|SHIP|hely. carefully regular accounts hag\n386|68123|5642|2|16|17457.92|0.06|0.01|A|F|1995-04-12|1995-04-18|1995-05-11|DELIVER IN PERSON|MAIL|lithely fluffi\n386|130081|82|3|37|41109.96|0.09|0.04|A|F|1995-05-23|1995-03-01|1995-05-25|TAKE BACK RETURN|MAIL|ending pearls breach fluffily. slyly pen\n387|136667|1694|1|1|1703.66|0.08|0.03|N|O|1997-05-06|1997-04-23|1997-05-10|NONE|SHIP| pinto beans wake furiously carefu\n387|152800|2801|2|42|77817.60|0.07|0.05|N|O|1997-05-25|1997-02-25|1997-05-29|DELIVER IN PERSON|RAIL|lithely final theodolites.\n387|96392|1411|3|40|55535.60|0.09|0.02|N|O|1997-03-08|1997-04-18|1997-03-31|COLLECT COD|TRUCK| quickly ironic platelets are slyly. fluff\n387|55927|5928|4|19|35775.48|0.08|0.00|N|O|1997-03-14|1997-04-21|1997-04-04|NONE|REG AIR|gular dependencies\n387|148313|828|5|32|43561.92|0.08|0.06|N|O|1997-05-02|1997-04-11|1997-05-11|DELIVER IN PERSON|TRUCK|gle. silent, fur\n388|32590|100|1|42|63948.78|0.05|0.06|R|F|1993-02-21|1993-02-26|1993-03-15|COLLECT COD|FOB|accounts sleep furiously\n388|127808|7809|2|46|84446.80|0.07|0.01|A|F|1993-03-22|1993-01-26|1993-03-24|COLLECT COD|FOB|to beans nag about the careful reque\n388|64486|9499|3|40|58019.20|0.06|0.01|A|F|1992-12-24|1993-01-28|1993-01-19|TAKE BACK RETURN|REG AIR|quests against the carefully unusual epi\n389|189295|1814|1|2|2768.58|0.09|0.00|R|F|1994-04-13|1994-04-10|1994-04-25|TAKE BACK RETURN|RAIL|fts. courts eat blithely even dependenc\n390|106523|9034|1|10|15295.20|0.02|0.05|N|O|1998-05-26|1998-07-06|1998-06-23|TAKE BACK RETURN|SHIP| requests. final accounts x-ray beside the\n390|123353|890|2|17|23397.95|0.09|0.06|N|O|1998-06-07|1998-06-14|1998-07-07|COLLECT COD|SHIP|ending, pending pinto beans wake slyl\n390|183266|8303|3|46|62065.96|0.07|0.04|N|O|1998-06-06|1998-05-20|1998-06-14|DELIVER IN PERSON|SHIP|cial excuses. bold, pending packages\n390|141937|1938|4|42|83115.06|0.01|0.05|N|O|1998-06-06|1998-06-22|1998-07-05|COLLECT COD|SHIP|counts nag across the sly, sil\n390|127657|170|5|13|21900.45|0.02|0.06|N|O|1998-07-08|1998-05-10|1998-07-18|DELIVER IN PERSON|SHIP|sleep carefully idle packages. blithely \n390|124632|9657|6|11|18222.93|0.09|0.06|N|O|1998-05-05|1998-05-15|1998-06-01|DELIVER IN PERSON|SHIP|according to the foxes are furiously \n390|84937|2462|7|24|46126.32|0.05|0.02|N|O|1998-04-18|1998-05-19|1998-04-28|TAKE BACK RETURN|AIR|y. enticingly final depos\n391|121586|6611|1|14|22506.12|0.09|0.02|R|F|1995-02-11|1995-02-03|1995-02-13|TAKE BACK RETURN|TRUCK| escapades sleep furiously about \n416|93563|6073|1|25|38914.00|0.00|0.05|A|F|1993-10-11|1993-11-26|1993-10-21|DELIVER IN PERSON|TRUCK|y final theodolites about\n416|110869|8403|2|22|41356.92|0.10|0.00|R|F|1993-12-27|1993-12-17|1994-01-09|COLLECT COD|RAIL|rint blithely above the pending sentim\n416|174101|6619|3|25|29377.50|0.07|0.01|R|F|1993-10-16|1993-12-03|1993-10-29|NONE|AIR|ses boost after the bold requests.\n417|39560|9561|1|39|58482.84|0.01|0.02|A|F|1994-05-31|1994-05-02|1994-06-06|NONE|SHIP|y regular requests wake along \n417|69212|4225|2|18|21261.78|0.00|0.01|R|F|1994-03-29|1994-04-10|1994-04-26|TAKE BACK RETURN|FOB|- final requests sle\n417|44192|6697|3|41|46583.79|0.10|0.01|R|F|1994-04-11|1994-03-08|1994-05-06|COLLECT COD|RAIL|tes. regular requests across the \n417|131087|1088|4|2|2236.16|0.01|0.03|R|F|1994-02-13|1994-04-19|1994-03-15|DELIVER IN PERSON|SHIP|uriously bol\n418|18552|1054|1|31|45587.05|0.00|0.03|N|F|1995-06-05|1995-06-18|1995-06-26|COLLECT COD|FOB|final theodolites. fluffil\n418|1062|3563|2|1|963.06|0.04|0.07|N|O|1995-06-23|1995-06-16|1995-07-23|DELIVER IN PERSON|AIR|regular, silent pinto\n418|34829|7333|3|3|5291.46|0.04|0.06|N|O|1995-06-29|1995-07-12|1995-07-01|COLLECT COD|AIR|ly furiously regular w\n419|152691|7722|1|33|57541.77|0.05|0.02|N|O|1996-11-06|1996-12-25|1996-11-20|TAKE BACK RETURN|TRUCK|y above the bli\n419|64192|9205|2|32|36998.08|0.01|0.06|N|O|1996-12-04|1996-12-04|1996-12-24|COLLECT COD|SHIP|blithely regular requests. special pinto\n419|70495|3003|3|15|21982.35|0.07|0.04|N|O|1996-12-17|1996-11-28|1996-12-19|TAKE BACK RETURN|REG AIR| sleep final, regular theodolites. fluffi\n419|8756|6257|4|15|24971.25|0.01|0.02|N|O|1997-01-09|1996-12-22|1997-01-25|COLLECT COD|FOB|of the careful, thin theodolites. quickly s\n419|148401|3430|5|17|24639.80|0.01|0.00|N|O|1997-01-13|1996-12-20|1997-02-01|COLLECT COD|REG AIR|lar dependencies: carefully regu\n420|100885|5906|1|5|9429.40|0.04|0.03|N|O|1995-11-04|1996-01-02|1995-11-30|NONE|REG AIR|cajole blit\n420|161079|8628|2|22|25081.54|0.05|0.04|N|O|1996-01-25|1995-12-16|1996-02-03|TAKE BACK RETURN|AIR|ly against the blithely re\n420|47557|2566|3|45|67704.75|0.09|0.08|N|O|1996-01-14|1996-01-01|1996-01-26|COLLECT COD|FOB| final accounts. furiously express forges\n420|74795|4796|4|12|21237.48|0.08|0.08|N|O|1996-02-05|1996-01-03|1996-02-12|TAKE BACK RETURN|REG AIR|c instructions are \n420|72918|7933|5|37|69963.67|0.02|0.00|N|O|1995-11-16|1995-12-13|1995-11-19|DELIVER IN PERSON|SHIP|rbits. bold requests along the quickl\n420|123736|1273|6|40|70389.20|0.01|0.05|N|O|1995-11-26|1995-12-26|1995-12-20|TAKE BACK RETURN|FOB| after the special\n420|15978|5979|7|39|73864.83|0.00|0.08|N|O|1995-12-09|1995-12-16|1995-12-31|DELIVER IN PERSON|REG AIR|s. ironic waters about the car\n421|133070|3071|1|1|1103.07|0.02|0.07|R|F|1992-05-29|1992-04-27|1992-06-09|NONE|TRUCK|oldly busy deposit\n422|151816|4332|1|25|46695.25|0.10|0.07|N|O|1997-07-01|1997-08-17|1997-07-09|DELIVER IN PERSON|SHIP|carefully bold theodolit\n422|170666|3184|2|10|17366.60|0.02|0.03|N|O|1997-06-15|1997-08-04|1997-07-08|TAKE BACK RETURN|AIR|he furiously ironic theodolite\n422|175984|3536|3|46|94759.08|0.09|0.00|N|O|1997-06-21|1997-07-14|1997-06-27|DELIVER IN PERSON|RAIL| ideas. qu\n422|161622|9171|4|25|42090.50|0.10|0.04|N|O|1997-08-24|1997-07-09|1997-09-22|NONE|FOB|ep along the furiousl\n423|131890|6917|1|27|51891.03|0.06|0.03|N|O|1996-08-20|1996-08-01|1996-08-23|TAKE BACK RETURN|SHIP|ccounts. blithely regular pack\n448|125197|5198|1|4|4888.76|0.00|0.04|N|O|1995-11-25|1995-10-20|1995-11-26|TAKE BACK RETURN|MAIL|nts thrash quickly among the b\n448|172359|9911|2|46|65842.10|0.05|0.00|N|O|1995-08-31|1995-09-30|1995-09-09|COLLECT COD|SHIP| to the fluffily ironic packages.\n448|26809|1814|3|35|60753.00|0.10|0.08|N|O|1995-09-27|1995-11-19|1995-10-20|COLLECT COD|REG AIR|ses nag quickly quickly ir\n448|169045|9046|4|8|8912.32|0.10|0.00|N|O|1995-11-02|1995-10-16|1995-11-15|COLLECT COD|TRUCK|ounts wake blithely. furiously pending\n448|137283|7284|5|23|30366.44|0.02|0.05|N|O|1995-09-26|1995-11-02|1995-10-17|NONE|SHIP|ious, final gifts\n449|151908|6939|1|12|23518.80|0.02|0.08|N|O|1995-11-06|1995-08-25|1995-11-18|TAKE BACK RETURN|SHIP|ly. blithely ironic \n449|108408|5939|2|4|5665.60|0.10|0.06|N|O|1995-10-27|1995-09-14|1995-11-21|DELIVER IN PERSON|FOB|are fluffily. requests are furiously\n449|9982|9983|3|3|5675.94|0.07|0.08|N|O|1995-07-28|1995-09-11|1995-08-01|NONE|RAIL| bold deposits. express theodolites haggle\n449|157659|2690|4|22|37766.30|0.07|0.00|N|O|1995-08-17|1995-09-04|1995-09-10|COLLECT COD|FOB|furiously final theodolites eat careful\n450|161582|9131|1|42|69030.36|0.03|0.00|N|F|1995-06-07|1995-05-29|1995-06-23|TAKE BACK RETURN|SHIP|y asymptotes. regular depen\n450|106298|6299|2|5|6521.45|0.03|0.02|A|F|1995-04-02|1995-05-06|1995-04-13|TAKE BACK RETURN|TRUCK|the pinto bea\n450|142528|7557|3|32|50256.64|0.06|0.03|N|O|1995-07-02|1995-04-25|1995-07-30|TAKE BACK RETURN|SHIP| accounts nod fluffily even, pending\n450|56267|3783|4|40|48930.40|0.05|0.03|R|F|1995-03-20|1995-05-25|1995-04-14|NONE|RAIL|ve. asymptote\n450|78048|8049|5|2|2052.08|0.09|0.00|A|F|1995-03-11|1995-05-21|1995-03-16|COLLECT COD|AIR|y even pinto beans; qui\n450|152726|5242|6|33|58697.76|0.08|0.05|R|F|1995-05-18|1995-05-22|1995-05-23|TAKE BACK RETURN|REG AIR|ily carefully final depo\n451|129532|4557|1|36|56215.08|0.02|0.06|N|O|1998-06-18|1998-08-14|1998-06-20|TAKE BACK RETURN|AIR|rges can haggle carefully ironic, dogged \n451|32028|7035|2|42|40320.84|0.05|0.01|N|O|1998-08-01|1998-08-05|1998-08-30|DELIVER IN PERSON|TRUCK|express excuses. blithely ironic pin\n451|86136|6137|3|1|1122.13|0.07|0.05|N|O|1998-07-13|1998-07-03|1998-08-04|DELIVER IN PERSON|AIR| carefully ironic packages solve furiously \n451|76558|4080|4|28|42967.40|0.04|0.05|N|O|1998-06-16|1998-07-09|1998-06-17|DELIVER IN PERSON|SHIP| theodolites. even cou\n452|114639|4640|1|2|3307.26|0.04|0.03|N|O|1997-12-26|1998-01-03|1998-01-12|COLLECT COD|FOB|y express instru\n453|197917|2956|1|45|90670.95|0.01|0.00|N|O|1997-06-30|1997-08-20|1997-07-19|COLLECT COD|REG AIR|ifts wake carefully.\n453|175131|2683|2|38|45832.94|0.08|0.04|N|O|1997-06-30|1997-07-08|1997-07-16|DELIVER IN PERSON|REG AIR| furiously f\n453|13144|8147|3|38|40171.32|0.10|0.01|N|O|1997-08-10|1997-07-24|1997-09-07|NONE|SHIP|sts cajole. furiously un\n453|95748|5749|4|45|78468.30|0.10|0.01|N|O|1997-09-18|1997-06-29|1997-10-14|TAKE BACK RETURN|AIR|ironic foxes. slyly pending depos\n453|25722|8225|5|32|52727.04|0.04|0.01|N|O|1997-07-15|1997-06-27|1997-07-18|NONE|REG AIR|s. fluffily bold packages cajole. unu\n453|94318|6828|6|28|36744.68|0.07|0.07|N|O|1997-08-16|1997-08-12|1997-08-27|NONE|MAIL|final dependencies. slyly special pl\n454|117595|5129|1|24|38702.16|0.06|0.01|N|O|1996-04-26|1996-03-23|1996-05-20|NONE|TRUCK|le. deposits after the ideas nag unusual pa\n455|156485|4031|1|42|64742.16|0.10|0.02|N|O|1997-01-26|1997-01-10|1997-02-22|DELIVER IN PERSON|REG AIR|around the quickly blit\n455|27230|7231|2|44|50918.12|0.05|0.08|N|O|1997-01-17|1997-02-22|1997-02-12|TAKE BACK RETURN|TRUCK| accounts sleep slyly ironic asymptote\n455|48360|3369|3|45|58876.20|0.04|0.06|N|O|1996-12-20|1997-01-31|1997-01-07|TAKE BACK RETURN|SHIP|thrash ironically regular packages. qui\n455|170012|7564|4|11|11902.11|0.01|0.02|N|O|1997-03-15|1997-02-14|1997-03-26|DELIVER IN PERSON|MAIL|g deposits against the slyly idle foxes u\n480|52148|2149|1|22|24203.08|0.04|0.02|A|F|1993-06-16|1993-07-28|1993-07-09|NONE|MAIL|into beans cajole furiously. accounts s\n481|18649|6153|1|17|26649.88|0.07|0.05|A|F|1992-10-21|1992-12-09|1992-11-19|DELIVER IN PERSON|MAIL|. quickly final accounts among the \n481|20646|647|2|19|29766.16|0.08|0.01|R|F|1993-01-09|1992-11-27|1993-01-14|TAKE BACK RETURN|AIR|p blithely after t\n481|185785|5786|3|42|78572.76|0.08|0.08|A|F|1992-11-27|1992-11-11|1992-12-08|COLLECT COD|RAIL|mptotes are furiously among the iron\n481|81009|6026|4|11|10890.00|0.05|0.06|A|F|1993-01-12|1992-11-17|1993-02-05|NONE|FOB|eful attai\n481|111956|6979|5|31|61006.45|0.05|0.01|A|F|1993-01-15|1992-12-31|1993-01-21|DELIVER IN PERSON|AIR|usly final packages believe. quick\n482|137343|7344|1|32|44170.88|0.00|0.02|N|O|1996-05-22|1996-05-14|1996-05-29|NONE|SHIP|usual deposits affix against \n482|121382|8919|2|1|1403.38|0.05|0.08|N|O|1996-05-29|1996-05-20|1996-05-31|COLLECT COD|AIR|es. quickly ironic escapades sleep furious\n482|61141|6154|3|31|34166.34|0.04|0.03|N|O|1996-06-01|1996-05-06|1996-06-17|NONE|MAIL| blithe pin\n482|195826|5827|4|8|15374.56|0.02|0.05|N|O|1996-04-19|1996-05-05|1996-04-21|NONE|TRUCK|tructions near the final, regular ideas de\n482|38215|3222|5|46|53047.66|0.01|0.06|N|O|1996-07-19|1996-06-05|1996-08-10|NONE|MAIL|furiously thin realms. final, fina\n482|78696|8697|6|19|31819.11|0.04|0.00|N|O|1996-03-27|1996-04-25|1996-04-15|NONE|FOB|ts hinder carefully silent requests\n483|32694|5198|1|8|13013.52|0.00|0.08|N|O|1995-08-22|1995-08-23|1995-09-18|COLLECT COD|RAIL|osits. carefully fin\n483|79758|9759|2|23|39968.25|0.04|0.06|N|O|1995-07-20|1995-08-11|1995-08-04|DELIVER IN PERSON|MAIL|requests was quickly against th\n483|87745|254|3|9|15594.66|0.04|0.03|N|O|1995-09-10|1995-09-02|1995-09-13|NONE|AIR| carefully express ins\n484|30133|5140|1|49|52093.37|0.10|0.02|N|O|1997-03-06|1997-02-28|1997-03-23|COLLECT COD|TRUCK|ven accounts\n484|31950|9460|2|45|84687.75|0.06|0.07|N|O|1997-04-09|1997-03-20|1997-04-19|DELIVER IN PERSON|TRUCK|usly final excuses boost slyly blithe\n484|183351|5870|3|50|71717.50|0.06|0.05|N|O|1997-01-24|1997-03-27|1997-02-22|DELIVER IN PERSON|MAIL|uctions wake. final, silent requests haggle\n484|164805|4806|4|22|41135.60|0.07|0.03|N|O|1997-04-29|1997-03-26|1997-05-17|TAKE BACK RETURN|SHIP|es are pending instructions. furiously unu\n484|76308|1323|5|48|61646.40|0.00|0.05|N|O|1997-03-05|1997-02-08|1997-03-22|TAKE BACK RETURN|MAIL|l, bold packages? even mult\n484|96871|9381|6|10|18678.70|0.01|0.08|N|O|1997-04-06|1997-02-14|1997-04-16|COLLECT COD|FOB|x fluffily carefully regular\n485|149523|9524|1|50|78626.00|0.01|0.00|N|O|1997-03-28|1997-05-26|1997-04-18|TAKE BACK RETURN|MAIL|iously quick excuses. carefully final f\n485|27973|2978|2|40|76038.80|0.08|0.01|N|O|1997-04-29|1997-05-08|1997-04-30|TAKE BACK RETURN|TRUCK|al escapades\n485|136884|4424|3|22|42259.36|0.00|0.05|N|O|1997-04-06|1997-04-27|1997-05-01|DELIVER IN PERSON|TRUCK|refully final notornis haggle according \n486|75437|5438|1|36|50847.48|0.00|0.01|N|O|1996-06-25|1996-05-06|1996-07-07|COLLECT COD|AIR|deposits around the quickly regular packa\n486|67040|7041|2|40|40281.60|0.03|0.08|N|O|1996-05-21|1996-06-06|1996-06-07|COLLECT COD|SHIP|ts nag quickly among the slyl\n486|135912|8426|3|26|50645.66|0.04|0.03|N|O|1996-03-16|1996-05-25|1996-03-31|NONE|RAIL|forges along the \n486|71865|6880|4|38|69800.68|0.08|0.05|N|O|1996-05-07|1996-04-26|1996-05-26|TAKE BACK RETURN|TRUCK| blithely final pinto \n486|28099|5606|5|3|3081.27|0.07|0.05|N|O|1996-07-07|1996-04-20|1996-07-23|DELIVER IN PERSON|RAIL|ccounts ha\n486|46543|9048|6|46|68518.84|0.00|0.03|N|O|1996-04-18|1996-05-02|1996-04-20|COLLECT COD|AIR|theodolites eat carefully furious\n487|91896|1897|1|47|88730.83|0.06|0.06|R|F|1992-09-30|1992-10-08|1992-10-24|NONE|TRUCK|tions. blithely reg\n487|82099|2100|2|2|2162.18|0.02|0.06|R|F|1992-10-19|1992-11-04|1992-11-11|COLLECT COD|TRUCK|oss the unusual pinto beans. reg\n512|188804|1323|1|19|35963.20|0.08|0.05|N|O|1995-07-12|1995-07-11|1995-08-04|COLLECT COD|MAIL| sleep. requests alongside of the fluff\n512|22847|7852|2|37|65484.08|0.01|0.04|N|O|1995-06-20|1995-07-05|1995-07-16|NONE|RAIL|nic depths cajole? blithely b\n512|179419|9420|3|40|59936.40|0.05|0.02|N|O|1995-07-06|1995-07-08|1995-07-08|COLLECT COD|TRUCK|quests are da\n512|82470|7487|4|10|14524.70|0.09|0.02|N|O|1995-09-16|1995-07-29|1995-10-07|NONE|AIR|xes. pinto beans cajole carefully; \n512|64154|4155|5|6|6708.90|0.03|0.05|R|F|1995-06-10|1995-06-21|1995-06-16|DELIVER IN PERSON|FOB|en ideas haggle \n512|32014|4518|6|12|11352.12|0.04|0.00|R|F|1995-05-21|1995-08-03|1995-06-09|COLLECT COD|FOB|old furiously express deposits. specia\n512|50769|3275|7|2|3439.52|0.09|0.08|N|O|1995-06-19|1995-08-13|1995-06-24|NONE|TRUCK|e slyly silent accounts serve with\n513|61732|9251|1|20|33874.60|0.09|0.07|N|O|1995-07-12|1995-05-31|1995-07-31|NONE|AIR|efully ironic ideas doze slyl\n513|121628|9165|2|44|72583.28|0.01|0.01|N|O|1995-07-14|1995-07-14|1995-08-12|NONE|MAIL|kages sleep boldly ironic theodolites. acco\n514|78713|1221|1|21|35525.91|0.06|0.02|N|O|1996-06-09|1996-05-15|1996-07-07|DELIVER IN PERSON|RAIL|s sleep quickly blithely\n514|117452|9964|2|34|49961.30|0.08|0.02|N|O|1996-04-14|1996-06-03|1996-04-23|COLLECT COD|REG AIR|ily even patterns. bold, silent instruc\n514|12812|5314|3|6|10348.86|0.06|0.01|N|O|1996-05-30|1996-06-04|1996-06-28|COLLECT COD|SHIP|as haggle blithely; quickly s\n514|115362|5363|4|43|59226.48|0.00|0.08|N|O|1996-06-07|1996-05-14|1996-07-01|TAKE BACK RETURN|FOB|thely regular \n515|104014|6525|1|10|10180.10|0.03|0.02|A|F|1993-10-04|1993-11-03|1993-10-08|NONE|FOB|ar deposits th\n515|147605|2634|2|38|62798.80|0.10|0.07|A|F|1993-09-19|1993-11-12|1993-10-03|DELIVER IN PERSON|SHIP|ays. furiously express requests haggle furi\n515|182145|9700|3|11|13498.54|0.00|0.02|R|F|1993-09-04|1993-10-02|1993-09-05|DELIVER IN PERSON|FOB|ly pending accounts haggle blithel\n515|108606|8607|4|34|54896.40|0.09|0.03|R|F|1993-10-03|1993-10-26|1993-10-15|DELIVER IN PERSON|REG AIR|ic dependencie\n515|130881|3395|5|32|61180.16|0.01|0.07|R|F|1993-10-10|1993-10-08|1993-11-02|TAKE BACK RETURN|FOB|r sauternes boost. final theodolites wake a\n515|108692|3713|6|25|42517.25|0.04|0.08|R|F|1993-11-14|1993-11-07|1993-12-03|DELIVER IN PERSON|MAIL|e packages engag\n516|24974|9979|1|11|20888.67|0.01|0.06|N|O|1998-05-02|1998-05-23|1998-05-12|DELIVER IN PERSON|FOB|ongside of the blithely final reque\n517|44551|4552|1|28|41875.40|0.03|0.02|N|O|1997-04-30|1997-05-18|1997-05-17|COLLECT COD|MAIL| requests. special, fi\n517|155391|7907|2|15|21695.85|0.02|0.00|N|O|1997-04-09|1997-06-26|1997-05-01|NONE|TRUCK| slyly. express requests ar\n517|40932|3437|3|9|16856.37|0.04|0.00|N|O|1997-05-03|1997-06-16|1997-05-24|COLLECT COD|SHIP| slyly stealthily express instructions. \n517|132197|2198|4|11|13521.09|0.06|0.02|N|O|1997-06-20|1997-06-01|1997-06-27|NONE|REG AIR|ly throughout the fu\n517|23349|8354|5|23|29263.82|0.00|0.01|N|O|1997-04-19|1997-05-07|1997-05-12|COLLECT COD|RAIL| kindle. furiously bold requests mus\n518|164711|4712|1|30|53271.30|0.07|0.05|N|O|1998-02-18|1998-03-27|1998-03-16|COLLECT COD|TRUCK|slyly by the packages. carefull\n518|83164|689|2|23|26384.68|0.05|0.07|N|O|1998-02-20|1998-05-05|1998-03-11|COLLECT COD|TRUCK| special requests. fluffily ironic re\n518|133178|8205|3|12|14534.04|0.01|0.06|N|O|1998-03-08|1998-03-31|1998-04-06|NONE|AIR| packages thrash slyly\n518|121990|1991|4|46|92551.54|0.07|0.02|N|O|1998-04-07|1998-04-17|1998-04-29|NONE|MAIL|. blithely even ideas cajole furiously. b\n518|70019|20|5|16|15824.16|0.01|0.01|N|O|1998-03-15|1998-03-24|1998-04-08|NONE|MAIL|use quickly expre\n518|196358|1397|6|39|56719.65|0.09|0.08|N|O|1998-02-26|1998-03-17|1998-03-21|DELIVER IN PERSON|FOB| the bold, special deposits are carefully \n518|185956|8475|7|48|98013.60|0.03|0.07|N|O|1998-03-06|1998-04-22|1998-03-14|NONE|FOB| slyly final platelets; quickly even deposi\n519|158970|4001|1|1|2028.97|0.07|0.07|N|O|1997-12-01|1998-01-26|1997-12-23|COLLECT COD|REG AIR|bold requests believe furiou\n519|2946|2947|2|38|70259.72|0.05|0.08|N|O|1998-02-19|1997-12-15|1998-03-19|DELIVER IN PERSON|FOB|gular excuses detect quickly furiously \n519|105900|921|3|19|36212.10|0.00|0.02|N|O|1998-01-09|1998-01-03|1998-02-06|COLLECT COD|AIR|asymptotes. p\n519|46267|3780|4|27|32758.02|0.08|0.06|N|O|1997-11-20|1997-12-06|1997-12-16|DELIVER IN PERSON|REG AIR|le. even, final dependencies\n519|9041|4042|5|13|12350.52|0.06|0.08|N|O|1998-02-06|1997-12-02|1998-03-03|TAKE BACK RETURN|TRUCK|c accounts wake along the ironic so\n519|150926|5957|6|3|5930.76|0.04|0.00|N|O|1998-02-01|1998-01-25|1998-02-27|TAKE BACK RETURN|FOB|erve blithely blithely ironic asymp\n544|138474|8475|1|47|71086.09|0.08|0.06|R|F|1993-03-14|1993-03-27|1993-03-27|COLLECT COD|SHIP|ecial pains. deposits grow foxes. \n545|169547|9548|1|4|6466.16|0.02|0.00|N|O|1996-02-23|1995-12-16|1996-03-21|DELIVER IN PERSON|FOB|, ironic grouches cajole over\n545|170188|5223|2|18|22647.24|0.00|0.00|N|O|1996-02-21|1996-01-17|1996-02-26|NONE|RAIL|al, final packages affix. even a\n546|84585|2110|1|16|25113.28|0.08|0.02|N|O|1997-02-04|1996-12-30|1997-02-25|DELIVER IN PERSON|TRUCK|de of the orbits. sometimes regula\n547|70789|5804|1|44|77430.32|0.08|0.08|N|O|1996-10-18|1996-08-17|1996-10-27|TAKE BACK RETURN|FOB|thely express dependencies. qu\n547|136347|1374|2|48|66400.32|0.01|0.04|N|O|1996-10-21|1996-08-04|1996-11-20|COLLECT COD|SHIP|thely specia\n547|181345|6382|3|3|4279.02|0.05|0.02|N|O|1996-09-04|1996-08-01|1996-09-21|COLLECT COD|SHIP|pinto beans. ironi\n548|196550|6551|1|2|3293.10|0.06|0.05|A|F|1994-11-26|1994-11-06|1994-12-06|COLLECT COD|MAIL|ests haggle quickly eve\n548|4641|4642|2|6|9273.84|0.00|0.08|A|F|1995-01-18|1994-12-08|1995-02-10|NONE|TRUCK|sits wake furiously regular\n548|182|7683|3|21|22725.78|0.03|0.08|A|F|1995-01-13|1994-12-18|1995-01-25|NONE|AIR|ideas. special accounts above the furiou\n548|56720|4236|4|21|35211.12|0.08|0.03|A|F|1994-10-27|1994-12-04|1994-11-21|DELIVER IN PERSON|AIR| engage quickly. regular theo\n548|92995|523|5|19|37771.81|0.00|0.02|A|F|1994-09-24|1994-11-24|1994-10-01|DELIVER IN PERSON|MAIL|courts boost care\n548|152753|7784|6|32|57784.00|0.06|0.04|A|F|1994-12-16|1994-11-20|1994-12-29|NONE|REG AIR|c instruction\n549|195061|100|1|18|20809.08|0.07|0.04|R|F|1992-10-19|1992-08-12|1992-11-13|COLLECT COD|REG AIR|furiously according to the ironic, regular \n549|188735|8736|2|38|69301.74|0.07|0.05|A|F|1992-08-17|1992-08-28|1992-09-05|COLLECT COD|RAIL|the regular, furious excuses. carefu\n549|65213|5214|3|36|42415.56|0.08|0.04|R|F|1992-09-11|1992-10-11|1992-09-12|DELIVER IN PERSON|AIR|ts against the ironic, even theodolites eng\n549|20101|7608|4|18|18379.80|0.09|0.01|A|F|1992-07-31|1992-09-11|1992-08-08|NONE|RAIL|ely regular accounts above the \n549|23480|987|5|38|53332.24|0.06|0.02|R|F|1992-08-23|1992-08-12|1992-08-25|COLLECT COD|REG AIR|eposits. carefully regular depos\n550|190307|2827|1|31|43316.30|0.04|0.02|N|O|1995-10-24|1995-09-27|1995-11-04|COLLECT COD|AIR|thely silent packages. unusual\n551|23786|6289|1|8|13678.24|0.08|0.02|N|O|1995-07-29|1995-07-18|1995-08-02|NONE|REG AIR| wake quickly slyly pending platel\n551|158813|3844|2|20|37436.20|0.00|0.07|N|O|1995-09-18|1995-08-25|1995-10-11|COLLECT COD|TRUCK|r ideas. final, even ideas hinder alongside\n551|161089|6122|3|16|18401.28|0.07|0.06|N|O|1995-07-29|1995-08-19|1995-08-10|COLLECT COD|MAIL|y along the carefully ex\n576|86490|1507|1|2|2952.98|0.07|0.01|N|O|1997-05-15|1997-06-30|1997-05-28|NONE|RAIL|ccounts along the ac\n576|33096|8103|2|6|6174.54|0.06|0.05|N|O|1997-05-15|1997-07-26|1997-06-03|DELIVER IN PERSON|TRUCK|al deposits. slyly even sauternes a\n576|36565|9069|3|6|9009.36|0.08|0.07|N|O|1997-08-28|1997-06-16|1997-09-25|DELIVER IN PERSON|FOB|ts. ironic multipliers \n576|137608|2635|4|5|8228.00|0.03|0.07|N|O|1997-06-11|1997-06-17|1997-07-05|NONE|REG AIR|l foxes boost slyly. accounts af\n577|25886|891|1|25|45297.00|0.06|0.01|A|F|1995-04-09|1995-02-20|1995-05-09|TAKE BACK RETURN|AIR|ve slyly of the frets. careful\n577|63233|8246|2|14|16747.22|0.08|0.03|R|F|1995-03-19|1995-02-25|1995-04-09|DELIVER IN PERSON|RAIL|l accounts wake deposits. ironic packa\n578|155542|5543|1|40|63901.60|0.02|0.08|N|O|1997-02-10|1997-03-18|1997-02-11|NONE|SHIP|usly even platel\n578|187025|2062|2|23|25576.46|0.05|0.08|N|O|1997-03-06|1997-03-03|1997-03-20|TAKE BACK RETURN|FOB|nstructions. ironic deposits\n579|150618|5649|1|9|15017.49|0.00|0.05|N|O|1998-06-20|1998-04-28|1998-07-19|DELIVER IN PERSON|RAIL|e ironic, express deposits are furiously\n579|32145|7152|2|39|42008.46|0.02|0.01|N|O|1998-06-21|1998-06-03|1998-06-26|COLLECT COD|REG AIR|ncies. furiously final r\n579|59048|4059|3|6|6042.24|0.03|0.00|N|O|1998-04-24|1998-05-03|1998-05-08|TAKE BACK RETURN|TRUCK|ickly final requests-- bold accou\n579|6189|8690|4|41|44902.38|0.04|0.05|N|O|1998-05-28|1998-05-01|1998-06-04|COLLECT COD|REG AIR|bold, express requests sublate slyly. blith\n579|12612|5114|5|28|42689.08|0.00|0.03|N|O|1998-07-10|1998-05-24|1998-07-19|NONE|RAIL|ic ideas until th\n579|166717|9234|6|5|8918.55|0.05|0.08|N|O|1998-05-02|1998-04-25|1998-05-05|COLLECT COD|REG AIR|refully silent ideas cajole furious\n580|84916|2441|1|33|62730.03|0.03|0.05|N|O|1997-10-11|1997-09-19|1997-10-16|TAKE BACK RETURN|FOB|y express theodolites cajole carefully \n580|173320|3321|2|31|43192.92|0.04|0.08|N|O|1997-10-04|1997-09-08|1997-10-15|COLLECT COD|FOB|ose alongside of the sl\n580|184444|6963|3|19|29040.36|0.04|0.04|N|O|1997-07-23|1997-09-21|1997-08-15|NONE|FOB|mong the special packag\n581|63384|8397|1|41|55242.58|0.09|0.07|N|O|1997-05-26|1997-04-06|1997-06-10|TAKE BACK RETURN|MAIL|nts. quickly\n581|92527|5037|2|14|21273.28|0.06|0.08|N|O|1997-05-17|1997-04-14|1997-06-08|NONE|MAIL|. deposits s\n581|100106|5127|3|49|54198.90|0.10|0.02|N|O|1997-02-27|1997-04-24|1997-03-10|TAKE BACK RETURN|MAIL|. slyly regular pinto beans acr\n581|74925|9940|4|30|56997.60|0.10|0.08|N|O|1997-06-19|1997-05-21|1997-06-22|TAKE BACK RETURN|TRUCK| regular ideas grow furio\n582|56409|3925|1|7|9557.80|0.07|0.00|N|O|1997-11-16|1997-11-29|1997-12-10|TAKE BACK RETURN|FOB|ithely unusual t\n582|50262|263|2|49|59400.74|0.05|0.03|N|O|1997-12-17|1998-01-12|1997-12-31|COLLECT COD|REG AIR|nts according to the furiously regular pin\n582|140309|5338|3|42|56670.60|0.07|0.00|N|O|1997-11-15|1997-12-21|1997-12-03|COLLECT COD|SHIP|iously beside the silent de\n582|167750|7751|4|36|65439.00|0.06|0.01|N|O|1997-12-09|1997-11-27|1997-12-26|TAKE BACK RETURN|SHIP|lar requests. quickly \n583|144364|4365|1|1|1408.36|0.07|0.07|N|O|1997-06-17|1997-04-29|1997-06-28|NONE|TRUCK| regular, regular ideas. even, bra\n583|119625|2137|2|47|77297.14|0.10|0.06|N|O|1997-07-14|1997-05-12|1997-08-11|DELIVER IN PERSON|AIR|nts are fluffily. furiously even re\n583|129431|1944|3|34|49654.62|0.01|0.02|N|O|1997-05-11|1997-04-24|1997-06-03|DELIVER IN PERSON|MAIL|express req\n583|141250|8793|4|33|42611.25|0.10|0.01|N|O|1997-05-28|1997-04-25|1997-06-24|NONE|AIR|kages cajole slyly across the\n583|188537|6092|5|13|21131.89|0.04|0.06|N|O|1997-06-23|1997-05-29|1997-07-08|COLLECT COD|TRUCK|y sly theodolites. ironi\n608|153579|1125|1|19|31018.83|0.08|0.06|N|O|1996-04-19|1996-05-02|1996-05-03|DELIVER IN PERSON|RAIL|ideas. the\n608|197310|2349|2|40|56292.40|0.03|0.01|N|O|1996-05-21|1996-04-11|1996-06-02|NONE|AIR| alongside of the regular tithes. sly\n609|65533|8040|1|21|31469.13|0.01|0.05|R|F|1994-08-24|1994-08-23|1994-08-27|DELIVER IN PERSON|FOB|de of the special warthogs. excu\n610|110792|5815|1|49|88336.71|0.10|0.07|N|O|1995-08-29|1995-10-26|1995-09-12|TAKE BACK RETURN|SHIP|ular instruc\n610|67896|5415|2|11|20502.79|0.07|0.08|N|O|1995-10-31|1995-10-25|1995-11-18|NONE|MAIL|blithely final \n610|117617|7618|3|26|42499.86|0.09|0.04|N|O|1995-11-22|1995-09-09|1995-12-04|TAKE BACK RETURN|AIR|cross the furiously even theodolites sl\n610|185206|7725|4|17|21950.40|0.03|0.03|N|O|1995-11-01|1995-10-30|1995-11-04|COLLECT COD|FOB|p quickly instead of the slyly pending foxe\n610|145743|5744|5|39|69760.86|0.08|0.05|N|O|1995-10-30|1995-10-21|1995-11-11|TAKE BACK RETURN|REG AIR|counts. ironic warhorses are \n610|94365|6875|6|5|6796.80|0.00|0.07|N|O|1995-08-11|1995-10-22|1995-08-26|TAKE BACK RETURN|FOB|n pinto beans. iro\n610|189280|4317|7|27|36970.56|0.06|0.03|N|O|1995-09-02|1995-09-19|1995-09-15|NONE|REG AIR| ironic pinto beans haggle. blithe\n611|16855|4359|1|39|69102.15|0.05|0.06|R|F|1993-05-06|1993-04-09|1993-05-22|TAKE BACK RETURN|SHIP|nto beans \n611|80676|677|2|1|1656.67|0.08|0.07|R|F|1993-05-17|1993-02-26|1993-06-15|DELIVER IN PERSON|MAIL|ts. pending platelets aff\n611|119545|2057|3|39|61017.06|0.09|0.02|A|F|1993-03-10|1993-03-10|1993-03-17|TAKE BACK RETURN|TRUCK|the evenly bold requests. furious\n612|184959|9996|1|5|10219.75|0.07|0.00|R|F|1992-11-08|1992-11-20|1992-12-03|TAKE BACK RETURN|RAIL|structions. q\n612|194864|7384|2|28|54848.08|0.07|0.06|R|F|1993-01-02|1992-12-11|1993-01-30|DELIVER IN PERSON|TRUCK|regular instructions affix bl\n612|66130|1143|3|49|53710.37|0.00|0.08|A|F|1993-01-08|1992-11-25|1993-01-17|TAKE BACK RETURN|REG AIR|theodolite\n612|38942|1446|4|28|52666.32|0.05|0.00|A|F|1992-11-12|1992-12-05|1992-12-02|TAKE BACK RETURN|REG AIR|lyly regular asym\n612|87737|246|5|1|1724.73|0.08|0.04|R|F|1992-12-18|1992-12-13|1992-12-20|TAKE BACK RETURN|FOB| requests.\n612|188203|5758|6|33|42609.60|0.10|0.03|R|F|1992-11-30|1992-12-01|1992-12-12|COLLECT COD|MAIL|bove the blithely even ideas. careful\n613|90027|7555|1|17|17289.34|0.06|0.06|N|O|1995-09-23|1995-08-04|1995-10-15|NONE|SHIP|ar dependencie\n613|78348|5870|2|6|7958.04|0.05|0.05|N|O|1995-08-05|1995-08-09|1995-08-08|TAKE BACK RETURN|MAIL|y ironic deposits eat \n613|185016|7535|3|3|3303.03|0.03|0.01|N|O|1995-09-27|1995-09-11|1995-10-05|NONE|TRUCK|ccounts cajole. \n613|158304|8305|4|7|9536.10|0.02|0.04|N|O|1995-09-07|1995-08-02|1995-09-16|DELIVER IN PERSON|MAIL|ously blithely final pinto beans. regula\n614|194109|9148|1|21|25265.10|0.00|0.03|R|F|1993-03-29|1993-01-06|1993-04-16|TAKE BACK RETURN|TRUCK|arefully. slyly express packag\n614|186897|9416|2|48|95226.72|0.07|0.07|A|F|1993-03-09|1993-01-19|1993-03-19|DELIVER IN PERSON|SHIP|riously special excuses haggle along the\n614|166963|4512|3|43|87288.28|0.05|0.00|A|F|1993-03-07|1993-02-22|1993-03-18|DELIVER IN PERSON|SHIP| express accounts wake. slyly ironic ins\n614|146951|4494|4|14|27971.30|0.04|0.06|A|F|1992-12-03|1993-02-14|1992-12-27|DELIVER IN PERSON|SHIP|ular packages haggle about the pack\n614|195308|7828|5|30|42099.00|0.08|0.07|R|F|1993-01-16|1993-02-08|1993-02-12|TAKE BACK RETURN|FOB|tructions are f\n614|136241|1268|6|48|61307.52|0.04|0.08|A|F|1992-12-14|1993-01-22|1993-01-11|NONE|TRUCK| regular platelets cajole quickly eve\n615|104545|4546|1|36|55783.44|0.10|0.01|A|F|1992-06-01|1992-07-14|1992-06-27|NONE|FOB| packages. carefully final pinto bea\n640|92997|525|1|49|97509.51|0.09|0.02|R|F|1993-03-27|1993-04-17|1993-04-15|NONE|RAIL|s haggle slyly\n640|416|2917|2|40|52656.40|0.09|0.05|A|F|1993-05-11|1993-04-11|1993-05-15|COLLECT COD|TRUCK|oach according to the bol\n640|179475|7027|3|22|34198.34|0.05|0.07|A|F|1993-05-07|1993-04-14|1993-05-21|TAKE BACK RETURN|TRUCK|osits across the slyly regular theodo\n640|31474|1475|4|45|63246.15|0.07|0.07|R|F|1993-04-15|1993-04-23|1993-04-21|DELIVER IN PERSON|REG AIR|ong the qui\n641|125192|2729|1|18|21909.42|0.01|0.08|R|F|1993-10-17|1993-10-11|1993-10-29|DELIVER IN PERSON|AIR|p blithely bold packages. quick\n641|99477|1987|2|1|1476.47|0.09|0.02|R|F|1993-12-03|1993-10-28|1993-12-26|TAKE BACK RETURN|RAIL| nag across the regular foxes.\n641|94311|6821|3|40|52212.40|0.05|0.06|R|F|1993-11-22|1993-10-20|1993-12-11|DELIVER IN PERSON|REG AIR|lets. furiously regular requests cajo\n641|70043|5058|4|25|25326.00|0.03|0.02|A|F|1993-12-04|1993-11-18|1993-12-18|TAKE BACK RETURN|FOB|d, regular d\n641|3794|8795|5|41|69609.39|0.07|0.04|R|F|1993-11-29|1993-10-27|1993-12-04|TAKE BACK RETURN|FOB| asymptotes are quickly. bol\n642|53624|3625|1|26|41018.12|0.10|0.03|A|F|1994-04-16|1994-02-01|1994-04-27|COLLECT COD|REG AIR|quests according to the unu\n643|12260|9764|1|28|32823.28|0.00|0.08|A|F|1995-04-13|1995-05-12|1995-04-14|TAKE BACK RETURN|TRUCK|ly regular requests nag sly\n643|50168|169|2|48|53671.68|0.01|0.02|N|O|1995-07-10|1995-06-07|1995-08-01|NONE|FOB|ly ironic accounts\n643|162447|4964|3|23|34717.12|0.05|0.03|N|O|1995-07-09|1995-05-18|1995-07-31|COLLECT COD|RAIL|sits are carefully according to the e\n643|44743|2256|4|39|65821.86|0.08|0.04|A|F|1995-06-08|1995-06-16|1995-06-13|COLLECT COD|RAIL| the pains. carefully s\n643|189459|4496|5|47|72777.15|0.10|0.03|R|F|1995-04-05|1995-06-14|1995-04-26|DELIVER IN PERSON|RAIL|y against \n644|133143|5657|1|46|54102.44|0.02|0.01|A|F|1992-05-20|1992-06-14|1992-06-14|DELIVER IN PERSON|RAIL| special requests was sometimes expre\n644|129821|7358|2|11|20359.02|0.05|0.02|A|F|1992-08-20|1992-07-21|1992-09-11|TAKE BACK RETURN|TRUCK|ealthy pinto beans use carefu\n644|100047|5068|3|44|46069.76|0.04|0.04|R|F|1992-08-17|1992-07-26|1992-08-20|COLLECT COD|REG AIR|iously ironic pinto beans. bold packa\n644|79744|7266|4|7|12066.18|0.01|0.02|A|F|1992-05-18|1992-07-01|1992-06-07|COLLECT COD|RAIL| regular requests are blithely. slyly\n644|49295|9296|5|23|28618.67|0.02|0.04|R|F|1992-07-31|1992-07-28|1992-08-13|DELIVER IN PERSON|TRUCK|uctions nag quickly alongside of t\n644|84932|2457|6|33|63258.69|0.00|0.07|R|F|1992-08-26|1992-07-27|1992-08-28|NONE|AIR|ages sleep. bold, bo\n644|50239|2745|7|38|45190.74|0.08|0.06|R|F|1992-05-17|1992-07-10|1992-06-06|TAKE BACK RETURN|MAIL| packages. blithely slow accounts nag quic\n645|159694|2210|1|33|57871.77|0.01|0.02|A|F|1994-12-09|1995-02-21|1995-01-03|NONE|TRUCK|heodolites b\n645|169422|9423|2|47|70096.74|0.07|0.05|R|F|1995-02-16|1995-02-15|1995-02-25|COLLECT COD|TRUCK|hely regular instructions alon\n645|69227|4240|3|46|55026.12|0.10|0.01|A|F|1995-01-04|1995-02-21|1995-01-21|COLLECT COD|REG AIR| regular dependencies across the speci\n645|95402|421|4|49|68472.60|0.05|0.03|R|F|1995-01-24|1995-01-06|1995-02-17|NONE|TRUCK|y. slyly iron\n645|4703|7204|5|43|69131.10|0.06|0.02|A|F|1995-02-12|1995-02-27|1995-03-06|TAKE BACK RETURN|REG AIR| furiously accounts. slyly\n645|33631|8638|6|18|28163.34|0.10|0.08|A|F|1995-03-02|1995-02-08|1995-03-03|COLLECT COD|RAIL|ep. slyly even \n645|27031|7032|7|9|8622.27|0.03|0.03|A|F|1994-12-25|1995-01-04|1995-01-15|COLLECT COD|REG AIR|special deposits. regular, final th\n646|108975|6506|1|31|61503.07|0.00|0.05|R|F|1994-12-17|1995-02-16|1995-01-04|COLLECT COD|MAIL|ag furiousl\n646|126723|6724|2|1|1749.72|0.07|0.01|A|F|1994-12-05|1995-01-07|1994-12-31|TAKE BACK RETURN|MAIL|t blithely regular deposits. quic\n646|29744|4749|3|24|40169.76|0.06|0.02|A|F|1995-02-20|1994-12-30|1995-03-16|TAKE BACK RETURN|TRUCK|regular accounts haggle dog\n646|98738|3757|4|34|59048.82|0.01|0.00|R|F|1994-12-28|1994-12-27|1994-12-31|COLLECT COD|SHIP|slow accounts. fluffily idle instructions\n646|89173|4190|5|17|19756.89|0.04|0.01|A|F|1994-12-31|1994-12-26|1995-01-01|DELIVER IN PERSON|REG AIR|inal packages haggle carefully \n646|114481|9504|6|40|59819.20|0.10|0.01|R|F|1995-01-01|1995-01-13|1995-01-11|COLLECT COD|TRUCK|ronic packages sleep across th\n647|16310|8812|1|41|50278.71|0.08|0.08|N|O|1997-11-19|1997-09-24|1997-12-15|COLLECT COD|REG AIR|r instructions. quickly unusu\n647|112177|7200|2|5|5945.85|0.10|0.00|N|O|1997-09-25|1997-09-22|1997-10-25|TAKE BACK RETURN|AIR|ly express packages haggle caref\n647|152882|7913|3|15|29023.20|0.08|0.00|N|O|1997-09-23|1997-10-09|1997-10-21|NONE|MAIL|ve the even, bold foxes sleep \n672|172190|2191|1|41|51749.79|0.06|0.06|R|F|1994-06-20|1994-07-03|1994-06-22|COLLECT COD|REG AIR| dependencies in\n672|189656|9657|2|9|15710.85|0.03|0.04|R|F|1994-06-25|1994-06-06|1994-07-19|TAKE BACK RETURN|TRUCK|haggle carefully carefully reg\n672|142390|9933|3|35|50133.65|0.02|0.01|R|F|1994-07-13|1994-06-04|1994-07-14|COLLECT COD|RAIL| dependencies haggle quickly. theo\n673|70495|5510|1|22|32240.78|0.03|0.02|R|F|1994-03-15|1994-04-27|1994-03-29|TAKE BACK RETURN|TRUCK| the regular, even requests. carefully fin\n674|101366|3877|1|23|31449.28|0.06|0.07|A|F|1992-10-25|1992-10-15|1992-11-03|COLLECT COD|SHIP|ve the quickly even deposits. blithe\n674|58285|3296|2|4|4973.12|0.02|0.07|R|F|1992-10-05|1992-11-22|1992-10-22|NONE|RAIL|ly express pinto beans sleep car\n675|156455|4001|1|1|1511.45|0.04|0.08|N|O|1997-11-27|1997-09-30|1997-12-12|DELIVER IN PERSON|REG AIR|ide of the slyly regular packages. unus\n675|136633|4173|2|35|58437.05|0.08|0.07|N|O|1997-08-19|1997-10-16|1997-09-17|DELIVER IN PERSON|REG AIR|s. furiously expre\n675|175802|8320|3|34|63845.20|0.10|0.04|N|O|1997-11-17|1997-10-07|1997-11-27|NONE|FOB|y final accounts unwind around the \n675|99269|6797|4|15|19023.90|0.09|0.05|N|O|1997-10-18|1997-09-28|1997-11-13|COLLECT COD|TRUCK|posits after the furio\n675|4669|7170|5|46|72388.36|0.09|0.05|N|O|1997-09-18|1997-10-14|1997-10-01|DELIVER IN PERSON|AIR| deposits along the express foxes \n676|50972|8488|1|9|17306.73|0.09|0.02|N|O|1997-04-03|1997-02-02|1997-04-08|COLLECT COD|REG AIR|aintain sl\n676|77668|5190|2|20|32913.20|0.07|0.07|N|O|1997-02-02|1997-02-01|1997-02-11|NONE|REG AIR|riously around the blithely \n676|162330|2331|3|35|48731.55|0.05|0.01|N|O|1996-12-30|1997-01-13|1997-01-19|DELIVER IN PERSON|RAIL|into beans. blithe\n676|72825|347|4|24|43147.68|0.01|0.06|N|O|1997-02-05|1997-01-16|1997-03-07|TAKE BACK RETURN|TRUCK|ress, regular dep\n676|165127|2676|5|31|36955.72|0.01|0.06|N|O|1997-02-06|1997-02-28|1997-03-08|COLLECT COD|TRUCK|ial deposits cajo\n676|75930|5931|6|33|62895.69|0.09|0.05|N|O|1997-03-02|1997-02-22|1997-03-19|TAKE BACK RETURN|TRUCK|as wake slyly furiously close pinto b\n676|142123|7152|7|11|12816.32|0.07|0.02|N|O|1997-03-09|1997-03-06|1997-03-31|TAKE BACK RETURN|MAIL|he final acco\n677|58986|1492|1|32|62239.36|0.04|0.08|R|F|1994-01-06|1994-01-31|1994-02-02|NONE|RAIL|slyly final\n677|167361|7362|2|39|55706.04|0.00|0.07|R|F|1993-12-19|1994-02-11|1994-01-05|TAKE BACK RETURN|SHIP|ges. furiously regular packages use \n677|23226|3227|3|46|52864.12|0.01|0.02|R|F|1993-12-02|1994-02-12|1993-12-06|COLLECT COD|RAIL|ng theodolites. furiously unusual theodo\n677|147638|5181|4|1|1685.63|0.06|0.05|R|F|1993-12-01|1994-01-14|1993-12-26|DELIVER IN PERSON|MAIL|ly. regular \n677|149613|7156|5|25|41565.25|0.00|0.05|A|F|1994-03-12|1994-02-02|1994-03-28|DELIVER IN PERSON|AIR| packages integrate blithely\n678|145537|5538|1|20|31650.60|0.05|0.08|R|F|1993-06-21|1993-04-07|1993-07-10|TAKE BACK RETURN|MAIL|furiously express excuses. foxes eat fu\n678|36553|4063|2|22|32770.10|0.01|0.02|A|F|1993-05-10|1993-04-29|1993-06-08|NONE|REG AIR|de of the carefully even requests. bl\n678|142489|5004|3|16|24503.68|0.06|0.02|R|F|1993-03-20|1993-04-13|1993-04-16|DELIVER IN PERSON|REG AIR|equests cajole around the carefully regular\n678|198067|8068|4|48|55922.88|0.08|0.08|R|F|1993-02-28|1993-04-04|1993-03-24|NONE|REG AIR|ithely. slyly express foxes\n678|97451|7452|5|16|23175.20|0.06|0.04|R|F|1993-03-09|1993-04-18|1993-04-07|NONE|AIR| about the \n678|42888|2889|6|11|20139.68|0.09|0.00|R|F|1993-04-28|1993-05-16|1993-05-11|COLLECT COD|TRUCK|ess deposits dazzle f\n679|191759|1760|1|9|16656.75|0.09|0.00|N|O|1995-12-20|1996-01-27|1996-01-07|COLLECT COD|REG AIR|leep slyly. entici\n704|189981|9982|1|40|82839.20|0.05|0.05|N|O|1997-01-30|1997-01-10|1997-02-20|COLLECT COD|AIR|ggle quickly. r\n704|3839|3840|2|14|24399.62|0.07|0.08|N|O|1997-02-02|1996-12-26|1997-02-19|DELIVER IN PERSON|REG AIR|ve the quickly final forges. furiously p\n705|188322|841|1|46|64874.72|0.05|0.06|N|O|1997-04-18|1997-05-06|1997-05-05|DELIVER IN PERSON|SHIP|ss deposits. ironic packa\n705|116218|3752|2|35|43197.35|0.10|0.04|N|O|1997-03-25|1997-03-20|1997-04-23|TAKE BACK RETURN|FOB|carefully ironic accounts\n706|196629|9149|1|23|39689.26|0.05|0.00|N|O|1995-12-06|1995-12-02|1995-12-16|COLLECT COD|SHIP|ckey players. requests above the\n707|154736|4737|1|34|60884.82|0.01|0.02|R|F|1994-12-08|1995-01-15|1995-01-02|NONE|RAIL| dependencies\n707|42642|5147|2|22|34862.08|0.00|0.06|A|F|1995-01-12|1994-12-28|1995-01-16|DELIVER IN PERSON|REG AIR| kindle ironically\n708|123805|1342|1|3|5486.40|0.05|0.02|N|O|1998-10-09|1998-09-22|1998-11-07|COLLECT COD|FOB|e slyly pending foxes. \n708|179124|9125|2|19|22859.28|0.06|0.00|N|O|1998-10-28|1998-09-23|1998-11-25|COLLECT COD|SHIP| requests. even, thin ideas\n708|121298|8835|3|33|43536.57|0.09|0.06|N|O|1998-09-10|1998-09-20|1998-09-22|COLLECT COD|RAIL|s boost carefully ruthless theodolites. f\n708|55176|5177|4|5|5655.85|0.07|0.07|N|O|1998-07-22|1998-08-15|1998-07-28|TAKE BACK RETURN|REG AIR|c pinto beans nag after the account\n708|142490|33|5|36|55169.64|0.08|0.01|N|O|1998-07-16|1998-09-04|1998-08-11|NONE|SHIP|ests. even, regular hockey p\n708|22352|9859|6|7|8920.45|0.10|0.03|N|O|1998-08-16|1998-08-15|1998-09-10|COLLECT COD|REG AIR|lly express ac\n709|86203|1220|1|7|8324.40|0.00|0.00|N|O|1998-06-14|1998-06-08|1998-06-18|TAKE BACK RETURN|RAIL| special orbits cajole \n709|197250|9770|2|15|20208.75|0.08|0.00|N|O|1998-07-10|1998-06-26|1998-08-09|NONE|RAIL|ily regular deposits. sauternes was accor\n709|168496|1013|3|10|15644.90|0.01|0.02|N|O|1998-06-04|1998-06-30|1998-06-11|NONE|REG AIR|ts cajole boldly \n709|107229|7230|4|40|49448.80|0.10|0.08|N|O|1998-08-12|1998-06-20|1998-08-20|DELIVER IN PERSON|RAIL|ggle fluffily carefully ironic\n710|162111|9660|1|47|55136.17|0.06|0.08|A|F|1993-01-18|1993-03-24|1993-01-24|TAKE BACK RETURN|MAIL|usual ideas into th\n710|192916|2917|2|38|76338.58|0.07|0.02|R|F|1993-04-18|1993-03-12|1993-05-15|COLLECT COD|FOB|sts boost fluffily aft\n710|138984|6524|3|7|14160.86|0.04|0.06|R|F|1993-01-20|1993-03-28|1993-02-15|TAKE BACK RETURN|REG AIR|xpress, special ideas. bl\n710|89308|9309|4|25|32432.50|0.00|0.05|R|F|1993-03-31|1993-02-05|1993-04-22|COLLECT COD|FOB|eas detect do\n710|185454|491|5|12|18473.40|0.01|0.02|A|F|1993-02-18|1993-02-27|1993-03-07|DELIVER IN PERSON|MAIL|ions. slyly express theodolites al\n710|113665|1199|6|21|35251.86|0.04|0.06|R|F|1993-03-22|1993-03-05|1993-03-27|DELIVER IN PERSON|SHIP|es. furiously p\n710|159288|6834|7|46|61974.88|0.03|0.07|R|F|1993-04-16|1993-03-27|1993-05-05|COLLECT COD|MAIL|ges use; blithely pending excuses inte\n711|145767|8282|1|2|3625.52|0.10|0.04|R|F|1993-12-01|1993-12-09|1993-12-16|DELIVER IN PERSON|REG AIR|ely across t\n711|102501|7522|2|27|40594.50|0.00|0.08|A|F|1993-10-02|1993-10-26|1993-10-08|DELIVER IN PERSON|MAIL|slyly. ironic asy\n711|127086|2111|3|46|51201.68|0.10|0.00|R|F|1993-12-26|1993-11-19|1994-01-21|TAKE BACK RETURN|MAIL|deposits. permanen\n711|127682|7683|4|20|34193.60|0.09|0.00|A|F|1994-01-17|1993-11-10|1994-01-31|DELIVER IN PERSON|TRUCK|kly regular acco\n736|157380|7381|1|46|66119.48|0.05|0.01|N|O|1998-07-16|1998-09-01|1998-08-09|NONE|AIR|uctions cajole\n736|79423|9424|2|23|32255.66|0.02|0.05|N|O|1998-10-08|1998-08-27|1998-10-19|TAKE BACK RETURN|AIR|k accounts are carefully\n736|56626|4142|3|13|20574.06|0.00|0.03|N|O|1998-08-16|1998-07-26|1998-08-19|DELIVER IN PERSON|FOB|st furiously among the \n736|97851|5379|4|14|25883.90|0.06|0.04|N|O|1998-10-04|1998-08-14|1998-10-16|COLLECT COD|REG AIR|nstructions.\n736|168002|3035|5|32|34240.00|0.04|0.03|N|O|1998-07-30|1998-08-22|1998-08-12|DELIVER IN PERSON|RAIL|iously final accoun\n737|181815|9370|1|12|22761.72|0.01|0.01|R|F|1992-04-28|1992-06-30|1992-05-08|COLLECT COD|RAIL|posits after the slyly bold du\n738|197203|2242|1|34|44206.80|0.00|0.06|R|F|1993-06-09|1993-04-15|1993-07-09|TAKE BACK RETURN|TRUCK|s against the ironic exc\n738|187016|7017|2|4|4412.04|0.00|0.03|A|F|1993-06-20|1993-04-08|1993-07-09|NONE|AIR|ar packages. fluffily bo\n738|169700|9701|3|23|40703.10|0.04|0.08|A|F|1993-03-17|1993-04-02|1993-04-05|TAKE BACK RETURN|SHIP|nic, final excuses promise quickly regula\n738|140786|8329|4|12|21921.36|0.04|0.08|A|F|1993-06-16|1993-05-05|1993-06-22|NONE|SHIP|ove the slyly regular p\n738|174837|9872|5|30|57354.90|0.02|0.00|A|F|1993-06-12|1993-05-29|1993-06-25|NONE|AIR|ecial instructions haggle blithely regula\n739|84489|6998|1|28|41257.44|0.00|0.03|N|O|1998-06-03|1998-08-04|1998-06-29|TAKE BACK RETURN|RAIL|elets about the pe\n739|3502|6003|2|50|70275.00|0.07|0.06|N|O|1998-08-26|1998-07-16|1998-09-02|COLLECT COD|MAIL|ndencies. blith\n739|48733|3742|3|12|20180.76|0.05|0.00|N|O|1998-08-20|1998-07-24|1998-08-22|NONE|MAIL|le slyly along the close i\n739|43470|983|4|47|66433.09|0.09|0.07|N|O|1998-08-12|1998-07-09|1998-08-28|NONE|REG AIR|deas according to the theodolites sn\n739|187523|5078|5|30|48315.60|0.07|0.06|N|O|1998-06-19|1998-08-26|1998-07-02|DELIVER IN PERSON|REG AIR|above the even deposits. ironic requests\n740|1206|8707|1|22|24358.40|0.10|0.02|N|O|1995-07-24|1995-09-11|1995-08-11|TAKE BACK RETURN|FOB|odolites cajole ironic, pending instruc\n740|65211|2730|2|35|41167.35|0.00|0.00|N|O|1995-09-06|1995-08-22|1995-10-02|NONE|TRUCK|p quickly. fu\n740|198037|8038|3|29|32915.87|0.06|0.05|N|O|1995-10-26|1995-09-17|1995-10-29|DELIVER IN PERSON|FOB|ntly bold pinto beans sleep quickl\n741|186404|1441|1|25|37260.00|0.03|0.06|N|O|1998-07-15|1998-08-27|1998-08-12|DELIVER IN PERSON|MAIL|accounts. blithely bold pa\n741|90396|5415|2|22|30500.58|0.09|0.01|N|O|1998-09-07|1998-09-28|1998-09-12|COLLECT COD|AIR|ven deposits about the regular, ironi\n742|101309|1310|1|46|60273.80|0.04|0.08|A|F|1995-03-12|1995-03-20|1995-03-16|TAKE BACK RETURN|SHIP|e slyly bold deposits cajole according to\n742|95395|7905|2|15|20855.85|0.08|0.05|A|F|1995-02-26|1995-03-20|1995-03-03|NONE|SHIP|blithely unusual pinto\n742|101553|4064|3|24|37309.20|0.08|0.08|A|F|1995-02-12|1995-03-12|1995-02-14|DELIVER IN PERSON|SHIP|affix slyly. furiously i\n742|191891|4411|4|16|31726.24|0.01|0.05|A|F|1995-01-15|1995-02-25|1995-01-24|COLLECT COD|AIR|eodolites haggle carefully regul\n742|100006|2517|5|48|48288.00|0.09|0.08|R|F|1995-03-24|1995-01-23|1995-04-08|TAKE BACK RETURN|TRUCK| platelets \n742|191966|9524|6|49|100840.04|0.02|0.07|A|F|1995-01-13|1995-02-13|1995-01-26|TAKE BACK RETURN|RAIL| carefully bold foxes sle\n743|191460|6499|1|21|32580.66|0.01|0.04|N|O|1996-10-26|1996-11-05|1996-11-11|COLLECT COD|MAIL|d requests. packages afte\n768|195813|5814|1|39|74443.59|0.06|0.08|N|O|1996-09-25|1996-10-27|1996-10-20|NONE|SHIP|out the ironic\n768|17862|7863|2|2|3559.72|0.00|0.04|N|O|1996-11-13|1996-10-03|1996-11-25|DELIVER IN PERSON|SHIP|ular courts. slyly dogged accou\n768|5964|965|3|30|56098.80|0.06|0.05|N|O|1996-09-22|1996-11-03|1996-10-13|NONE|MAIL| furiously fluffy pinto beans haggle along\n768|24493|2000|4|37|52447.13|0.10|0.00|N|O|1996-10-02|1996-09-23|1996-10-14|TAKE BACK RETURN|REG AIR|ending requests across the quickly\n768|46500|1509|5|47|67985.50|0.06|0.05|N|O|1996-11-28|1996-10-30|1996-12-12|NONE|TRUCK|foxes. slyly ironic deposits a\n768|111635|6658|6|43|70805.09|0.10|0.06|N|O|1996-09-22|1996-11-03|1996-10-22|TAKE BACK RETURN|AIR|sual ideas wake quickly\n768|48062|8063|7|33|33331.98|0.01|0.04|N|O|1996-09-06|1996-09-29|1996-10-01|COLLECT COD|RAIL|sly ironic instructions. excuses can hagg\n769|175068|7586|1|36|41150.16|0.02|0.02|A|F|1993-10-01|1993-08-07|1993-10-15|NONE|AIR|es. furiously iro\n769|159077|1593|2|4|4544.28|0.01|0.04|R|F|1993-06-25|1993-08-12|1993-07-15|DELIVER IN PERSON|FOB| ideas. even\n770|180119|2638|1|39|46765.29|0.09|0.06|N|O|1998-07-19|1998-08-09|1998-08-04|NONE|REG AIR|osits. foxes cajole \n770|53281|5787|2|25|30857.00|0.03|0.02|N|O|1998-05-26|1998-07-23|1998-06-04|TAKE BACK RETURN|AIR| deposits dazzle fluffily alongside of \n771|6983|4484|1|12|22679.76|0.10|0.08|N|O|1995-07-18|1995-08-02|1995-08-07|COLLECT COD|TRUCK|carefully. pending in\n771|160505|3022|2|38|59489.00|0.03|0.08|N|O|1995-07-22|1995-09-10|1995-07-29|TAKE BACK RETURN|REG AIR| quickly final requests are final packages.\n771|6393|6394|3|14|18191.46|0.02|0.05|N|O|1995-07-31|1995-08-13|1995-08-07|DELIVER IN PERSON|AIR|r, final packages are slyly iro\n771|41262|1263|4|7|8422.82|0.06|0.02|N|O|1995-06-18|1995-08-31|1995-06-20|NONE|REG AIR|theodolites after the fluffily express \n771|77402|4924|5|13|17932.20|0.09|0.01|N|O|1995-08-10|1995-08-21|1995-08-30|NONE|FOB|packages affix slyly about the quickly \n771|81921|9446|6|23|43767.16|0.08|0.03|N|O|1995-06-19|1995-09-07|1995-07-09|COLLECT COD|FOB|cajole besides the quickly ironic pin\n772|52698|214|1|35|57774.15|0.10|0.06|R|F|1993-07-05|1993-06-05|1993-08-02|NONE|SHIP|kly thin packages wake slowly\n772|83007|8024|2|10|9900.00|0.05|0.01|R|F|1993-05-20|1993-05-19|1993-06-15|DELIVER IN PERSON|MAIL| deposits cajole carefully instructions. t\n772|85119|5120|3|35|38643.85|0.03|0.04|R|F|1993-04-18|1993-06-13|1993-05-01|COLLECT COD|MAIL|ng ideas. special packages haggle alon\n772|179834|7386|4|10|19138.30|0.08|0.02|A|F|1993-05-17|1993-06-09|1993-05-29|COLLECT COD|AIR|o the furiously final deposits. furi\n772|53649|3650|5|42|67310.88|0.02|0.07|A|F|1993-06-09|1993-07-16|1993-06-12|DELIVER IN PERSON|MAIL| express foxes abo\n773|99317|9318|1|5|6581.55|0.06|0.04|A|F|1993-11-21|1993-12-19|1993-12-21|COLLECT COD|MAIL|ar requests. regular, thin packages u\n773|10005|2507|2|31|28365.00|0.02|0.06|A|F|1993-12-30|1993-11-02|1994-01-01|TAKE BACK RETURN|MAIL|e slyly unusual deposit\n773|150082|7628|3|39|44151.12|0.06|0.05|A|F|1994-01-04|1993-12-23|1994-01-26|DELIVER IN PERSON|FOB|quickly eve\n773|28056|3061|4|28|27553.40|0.10|0.06|R|F|1994-01-19|1993-11-05|1994-01-23|NONE|TRUCK|he furiously slow deposits.\n773|133007|3008|5|9|9360.00|0.09|0.02|R|F|1993-10-09|1993-12-25|1993-11-04|TAKE BACK RETURN|FOB|ent orbits haggle fluffily after the \n773|39505|9506|6|43|62113.50|0.07|0.03|A|F|1993-11-06|1993-11-20|1993-11-08|TAKE BACK RETURN|SHIP|furiously bold dependencies. blithel\n774|182652|2653|1|49|84997.85|0.08|0.03|N|O|1995-12-06|1996-01-07|1995-12-14|DELIVER IN PERSON|SHIP|ess accounts are carefully \n774|16038|1041|2|3|2862.09|0.10|0.06|N|O|1996-02-13|1996-01-14|1996-03-04|COLLECT COD|FOB| slyly even courts nag blith\n774|147809|5352|3|34|63131.20|0.02|0.07|N|O|1996-03-16|1996-01-03|1996-03-22|COLLECT COD|FOB|lar excuses are furiously final instr\n774|14993|4994|4|8|15263.92|0.00|0.02|N|O|1996-01-24|1996-01-15|1996-02-13|COLLECT COD|RAIL|ully ironic requests c\n774|176160|3712|5|44|54391.04|0.09|0.07|N|O|1996-02-29|1996-01-16|1996-03-06|NONE|REG AIR|s according to the deposits unwind ca\n774|119813|9814|6|2|3665.62|0.07|0.03|N|O|1995-12-11|1996-02-10|1995-12-14|TAKE BACK RETURN|SHIP|accounts; slyly regular\n775|31640|6647|1|16|25146.24|0.10|0.06|N|F|1995-05-23|1995-05-07|1995-06-19|NONE|TRUCK|un quickly slyly\n775|173244|796|2|21|27662.04|0.01|0.06|R|F|1995-05-01|1995-06-02|1995-05-13|DELIVER IN PERSON|FOB| quickly sile\n775|107093|4624|3|20|22001.80|0.01|0.08|N|F|1995-06-17|1995-05-22|1995-07-13|COLLECT COD|AIR|en dependencies nag slowly \n800|71833|6848|1|38|68583.54|0.00|0.05|N|O|1998-07-21|1998-09-25|1998-08-07|TAKE BACK RETURN|TRUCK|according to the bold, final dependencies \n800|84262|6771|2|21|26171.46|0.04|0.05|N|O|1998-07-23|1998-10-01|1998-08-20|TAKE BACK RETURN|RAIL|ckly even requests after the carefully r\n800|175028|63|3|26|28678.52|0.01|0.02|N|O|1998-07-23|1998-10-08|1998-07-25|DELIVER IN PERSON|FOB|bove the pending requests.\n801|5111|2612|1|13|13209.43|0.10|0.02|R|F|1992-04-25|1992-04-24|1992-05-16|COLLECT COD|RAIL|s are fluffily stealthily expres\n801|94616|9635|2|21|33822.81|0.05|0.02|A|F|1992-03-14|1992-04-01|1992-04-05|COLLECT COD|AIR|wake silently furiously idle deposits. \n801|2700|2701|3|21|33656.70|0.05|0.03|A|F|1992-04-25|1992-03-20|1992-05-04|COLLECT COD|REG AIR|cial, special packages.\n801|163556|1105|4|12|19434.60|0.08|0.04|A|F|1992-06-06|1992-04-14|1992-06-12|TAKE BACK RETURN|RAIL|s. ironic pinto b\n801|73144|666|5|45|50271.30|0.01|0.06|R|F|1992-03-22|1992-03-22|1992-03-25|COLLECT COD|REG AIR| even asymptotes\n801|121868|4381|6|10|18898.60|0.08|0.01|A|F|1992-06-05|1992-05-15|1992-06-21|DELIVER IN PERSON|MAIL|al accounts. carefully regular foxes wake\n801|25641|646|7|11|17233.04|0.01|0.03|A|F|1992-05-09|1992-04-19|1992-05-15|DELIVER IN PERSON|REG AIR|y special pinto beans cajole \n802|142568|7597|1|40|64422.40|0.08|0.08|A|F|1995-01-07|1995-04-03|1995-01-23|DELIVER IN PERSON|RAIL|y bold accou\n802|132751|2752|2|34|60647.50|0.08|0.06|A|F|1995-03-01|1995-03-15|1995-03-12|COLLECT COD|AIR|instructions cajole carefully. quietl\n802|130920|921|3|44|85840.48|0.07|0.04|R|F|1995-01-09|1995-02-04|1995-01-18|TAKE BACK RETURN|SHIP|rmanently idly special requ\n802|156598|1629|4|18|29782.62|0.09|0.02|R|F|1995-03-06|1995-02-07|1995-03-19|TAKE BACK RETURN|RAIL|y regular requests engage furiously final d\n802|131708|1709|5|19|33054.30|0.08|0.06|A|F|1995-04-01|1995-02-20|1995-04-23|DELIVER IN PERSON|REG AIR|old, furious\n803|53401|8412|1|8|10835.20|0.07|0.01|N|O|1997-08-04|1997-06-19|1997-08-12|NONE|SHIP|ronic theodo\n803|98771|8772|2|21|37165.17|0.08|0.06|N|O|1997-08-25|1997-06-30|1997-09-10|TAKE BACK RETURN|AIR|ironic packages cajole slyly. un\n804|125293|5294|1|30|39548.70|0.08|0.04|A|F|1993-03-29|1993-05-07|1993-04-14|COLLECT COD|REG AIR|ehind the quietly regular pac\n804|198375|5933|2|2|2946.74|0.02|0.00|A|F|1993-06-23|1993-04-30|1993-06-25|NONE|TRUCK|slyly silent \n804|75519|534|3|44|65758.44|0.04|0.05|R|F|1993-07-06|1993-04-13|1993-07-28|DELIVER IN PERSON|TRUCK|ly final deposits? special \n804|37511|2518|4|21|30418.71|0.01|0.00|A|F|1993-04-12|1993-06-06|1993-04-20|DELIVER IN PERSON|RAIL|ular, ironic foxes. quickly even accounts\n805|197826|346|1|25|48095.50|0.07|0.06|N|O|1995-08-05|1995-09-30|1995-08-06|NONE|AIR|ide of the pending, sly requests. quickly f\n805|56623|9129|2|29|45808.98|0.07|0.01|N|O|1995-08-24|1995-08-15|1995-09-16|TAKE BACK RETURN|AIR|dolites according to the slyly f\n805|46414|6415|3|12|16324.92|0.01|0.06|N|O|1995-07-13|1995-09-27|1995-08-02|TAKE BACK RETURN|REG AIR| regular foxes. furio\n805|75351|7859|4|26|34485.10|0.08|0.07|N|O|1995-08-28|1995-09-24|1995-09-11|TAKE BACK RETURN|RAIL|. ironic deposits sleep across \n806|104752|2283|1|1|1756.75|0.04|0.07|N|O|1996-07-14|1996-09-12|1996-07-25|COLLECT COD|RAIL|ar accounts? pending, pending foxes a\n806|159749|4780|2|22|39792.28|0.08|0.02|N|O|1996-10-03|1996-08-11|1996-10-20|DELIVER IN PERSON|REG AIR|fily pending \n806|90335|2845|3|4|5301.32|0.04|0.03|N|O|1996-08-09|1996-09-18|1996-08-13|COLLECT COD|TRUCK|eans. quickly ironic ideas \n807|116568|4102|1|49|77643.44|0.00|0.00|R|F|1993-12-05|1994-01-13|1993-12-25|COLLECT COD|REG AIR| furiously according to the un\n807|154875|9906|2|49|94563.63|0.01|0.06|A|F|1994-01-17|1994-01-24|1994-01-22|COLLECT COD|TRUCK|y regular requests haggle.\n807|180228|7783|3|48|62794.56|0.07|0.07|A|F|1994-01-08|1994-02-02|1994-01-15|DELIVER IN PERSON|SHIP|kly across the f\n807|79226|9227|4|10|12052.20|0.09|0.00|R|F|1994-01-19|1994-02-12|1994-01-28|NONE|TRUCK|furiously final depths sleep a\n807|142418|7447|5|30|43812.30|0.02|0.01|R|F|1994-01-19|1994-01-09|1994-01-27|NONE|RAIL|cial accoun\n807|11627|9131|6|11|16924.82|0.02|0.04|R|F|1994-03-25|1994-01-26|1994-04-14|NONE|FOB|unts above the slyly final ex\n807|149|5150|7|19|19933.66|0.08|0.05|A|F|1994-02-10|1994-02-20|1994-03-06|NONE|SHIP|ns haggle quickly across the furi\n832|102949|5460|1|45|87837.30|0.01|0.02|A|F|1992-05-08|1992-06-06|1992-06-04|COLLECT COD|MAIL|foxes engage slyly alon\n832|47411|2420|2|24|32601.84|0.05|0.06|A|F|1992-06-15|1992-07-14|1992-06-17|NONE|TRUCK|ully. carefully speci\n833|53556|3557|1|1|1509.55|0.04|0.04|R|F|1994-04-26|1994-04-05|1994-04-29|COLLECT COD|MAIL|ffily ironic theodolites\n833|111833|4345|2|38|70103.54|0.05|0.05|A|F|1994-04-05|1994-04-21|1994-05-01|COLLECT COD|TRUCK| platelets promise furiously. \n833|161174|8723|3|9|11116.53|0.05|0.07|A|F|1994-02-28|1994-04-26|1994-03-20|TAKE BACK RETURN|FOB|ecial, even requests. even, bold instructi\n834|144119|6634|1|36|41871.96|0.06|0.04|R|F|1994-06-28|1994-07-25|1994-07-07|TAKE BACK RETURN|SHIP|ccounts haggle after the furiously \n834|6951|1952|2|11|20437.45|0.03|0.00|A|F|1994-09-18|1994-08-03|1994-10-02|DELIVER IN PERSON|TRUCK|inst the regular packa\n835|106431|1452|1|33|47435.19|0.09|0.06|N|O|1995-11-01|1995-12-02|1995-11-24|DELIVER IN PERSON|RAIL|onic instructions among the carefully iro\n835|184562|9599|2|28|46103.68|0.02|0.02|N|O|1995-12-27|1995-12-11|1996-01-21|NONE|SHIP| fluffily furious pinto beans\n836|187915|5470|1|6|12017.46|0.09|0.03|N|O|1996-12-09|1997-01-31|1996-12-29|COLLECT COD|TRUCK|fully bold theodolites are daringly across\n836|83534|1059|2|18|27315.54|0.03|0.05|N|O|1997-02-27|1997-02-11|1997-03-22|NONE|REG AIR|y pending packages use alon\n836|140671|3186|3|46|78736.82|0.05|0.07|N|O|1997-03-21|1997-02-06|1997-04-05|NONE|REG AIR|boldly final pinto beans haggle furiously\n837|56565|9071|1|39|59340.84|0.03|0.08|A|F|1994-07-22|1994-08-10|1994-08-11|NONE|RAIL|ecial pinto bea\n837|87341|4866|2|24|31880.16|0.08|0.00|R|F|1994-06-27|1994-09-02|1994-07-27|DELIVER IN PERSON|FOB|p carefully. theodolites use. bold courts a\n838|133708|6222|1|20|34834.00|0.10|0.07|N|O|1998-04-11|1998-03-25|1998-04-19|COLLECT COD|TRUCK| furiously final ideas. slow, bold \n838|28244|8245|2|27|31650.48|0.05|0.07|N|O|1998-02-15|1998-04-03|1998-02-20|DELIVER IN PERSON|SHIP| pending pinto beans haggle about t\n838|94785|7295|3|23|40934.94|0.10|0.07|N|O|1998-03-26|1998-04-17|1998-04-02|COLLECT COD|AIR|ets haggle furiously furiously regular r\n838|43152|3153|4|18|19712.70|0.09|0.00|N|O|1998-03-28|1998-04-06|1998-03-31|TAKE BACK RETURN|AIR|hely unusual foxes. furio\n839|157077|4623|1|23|26083.61|0.07|0.02|N|O|1995-10-17|1995-11-03|1995-11-04|COLLECT COD|AIR|ng ideas haggle accord\n839|188315|8316|2|47|65955.57|0.08|0.00|N|O|1995-10-17|1995-11-06|1995-11-10|NONE|AIR|refully final excuses about \n864|129655|2168|1|34|57278.10|0.03|0.04|N|O|1997-12-16|1997-10-23|1998-01-12|TAKE BACK RETURN|SHIP|gside of the furiously special\n864|97941|2960|2|7|13572.58|0.01|0.02|N|O|1997-11-13|1997-10-07|1997-12-13|TAKE BACK RETURN|MAIL|ven requests should sleep along \n864|79304|1812|3|34|43632.20|0.03|0.00|N|O|1997-09-14|1997-11-04|1997-09-21|TAKE BACK RETURN|REG AIR|to the furiously ironic platelets! \n865|197300|9820|1|16|22356.80|0.07|0.03|R|F|1993-08-24|1993-06-26|1993-08-28|TAKE BACK RETURN|TRUCK|y even accounts. quickly bold decoys\n865|19188|4191|2|3|3321.54|0.02|0.05|A|F|1993-07-17|1993-07-14|1993-08-01|NONE|MAIL|fully regular the\n865|86034|1051|3|15|15300.45|0.00|0.06|R|F|1993-07-05|1993-06-25|1993-07-26|NONE|SHIP| deposits sleep quickl\n865|168398|5947|4|34|49857.26|0.09|0.06|A|F|1993-05-09|1993-07-28|1993-05-18|DELIVER IN PERSON|REG AIR|furiously fluffily unusual account\n866|135703|5704|1|5|8693.50|0.08|0.00|R|F|1993-01-22|1993-01-14|1993-02-07|TAKE BACK RETURN|AIR|tegrate fluffily. carefully f\n867|138798|8799|1|7|12857.53|0.04|0.07|A|F|1994-02-19|1993-12-25|1994-02-25|DELIVER IN PERSON|TRUCK|pendencies-- slyly unusual packages hagg\n868|167908|7909|1|8|15807.20|0.06|0.03|R|F|1992-10-07|1992-08-01|1992-10-16|NONE|MAIL|l deposits. blithely regular pint\n868|28659|3664|2|13|20639.45|0.05|0.07|R|F|1992-07-25|1992-08-26|1992-08-04|NONE|AIR|gged instructi\n868|67432|2445|3|19|26589.17|0.09|0.06|R|F|1992-06-22|1992-08-27|1992-07-04|COLLECT COD|SHIP|lyly ironic platelets wake. rut\n868|121059|6084|4|43|46442.15|0.02|0.04|A|F|1992-07-02|1992-07-22|1992-07-21|COLLECT COD|SHIP|kly silent deposits wake dar\n868|24498|2005|5|27|38407.23|0.04|0.01|R|F|1992-08-01|1992-08-25|1992-08-12|TAKE BACK RETURN|RAIL|oss the fluffily unusual pinto \n868|124220|4221|6|19|23640.18|0.02|0.05|R|F|1992-09-20|1992-07-18|1992-10-04|NONE|FOB|ely even deposits lose blithe\n869|62275|4782|1|27|33406.29|0.07|0.07|N|O|1997-01-30|1997-02-17|1997-02-26|TAKE BACK RETURN|TRUCK|uffily even excuses? slyly even deposits \n869|46068|8573|2|36|36506.16|0.04|0.01|N|O|1997-05-03|1997-03-17|1997-05-24|NONE|RAIL|ong the furiously bold instructi\n870|49110|6623|1|36|38127.96|0.04|0.07|A|F|1993-10-18|1993-09-16|1993-11-15|COLLECT COD|MAIL|fily. furiously final accounts are \n870|185850|5851|2|5|9679.25|0.06|0.05|A|F|1993-08-13|1993-09-11|1993-08-24|COLLECT COD|FOB|e slyly excuses. ironi\n871|96392|6393|1|48|66642.72|0.10|0.03|N|O|1996-02-25|1996-02-09|1996-03-18|NONE|AIR|coys dazzle slyly slow notornis. f\n871|54591|9602|2|47|72642.73|0.07|0.03|N|O|1995-12-25|1996-02-01|1996-01-24|TAKE BACK RETURN|RAIL|ss, final dep\n871|107967|5498|3|13|25674.48|0.09|0.01|N|O|1996-01-25|1996-01-24|1996-02-03|NONE|REG AIR| haggle furiou\n871|189917|9918|4|29|58200.39|0.06|0.07|N|O|1995-11-16|1996-01-27|1995-12-16|DELIVER IN PERSON|RAIL|ests are carefu\n871|127480|2505|5|8|12059.84|0.00|0.01|N|O|1995-11-25|1996-01-12|1995-12-12|DELIVER IN PERSON|AIR|lar ideas-- slyly even accou\n871|142499|42|6|26|40078.74|0.00|0.06|N|O|1996-02-07|1996-01-05|1996-02-25|COLLECT COD|AIR|symptotes use quickly near the \n871|173371|8406|7|4|5777.48|0.00|0.07|N|O|1996-03-09|1996-01-20|1996-03-26|COLLECT COD|FOB|l, regular dependencies w\n896|38675|8676|1|47|75842.49|0.07|0.08|R|F|1993-05-28|1993-05-15|1993-06-15|DELIVER IN PERSON|TRUCK|ly even pinto beans integrate. b\n896|197621|5179|2|10|17186.20|0.03|0.07|A|F|1993-07-07|1993-06-03|1993-07-24|COLLECT COD|SHIP| quickly even theodolites. carefully regu\n896|1925|9426|3|7|12788.44|0.09|0.02|A|F|1993-05-02|1993-05-24|1993-05-31|DELIVER IN PERSON|MAIL| requests \n896|151663|1664|4|11|18861.26|0.08|0.04|A|F|1993-05-19|1993-05-22|1993-06-08|COLLECT COD|MAIL|the multipliers sleep\n896|187481|5036|5|34|53328.32|0.00|0.05|R|F|1993-05-21|1993-06-01|1993-05-23|NONE|TRUCK|ular, close requests cajo\n896|176862|1897|6|44|85309.84|0.09|0.08|R|F|1993-05-19|1993-04-14|1993-06-02|DELIVER IN PERSON|FOB|lar, pending packages. deposits are q\n896|108889|1400|7|11|20876.68|0.01|0.07|A|F|1993-05-01|1993-04-09|1993-05-06|TAKE BACK RETURN|FOB|rding to the pinto beans wa\n897|90450|5469|1|15|21606.75|0.07|0.04|R|F|1995-05-25|1995-05-09|1995-06-07|COLLECT COD|REG AIR|r ideas. slyly spec\n897|183170|725|2|26|32582.42|0.05|0.08|N|O|1995-07-01|1995-06-10|1995-07-14|COLLECT COD|MAIL|tions sleep according to the special\n897|125704|8217|3|13|22486.10|0.07|0.00|A|F|1995-03-30|1995-05-17|1995-04-21|TAKE BACK RETURN|MAIL|bold accounts mold carefully! braids\n897|101182|6203|4|2|2366.36|0.08|0.08|R|F|1995-05-22|1995-05-07|1995-06-16|COLLECT COD|RAIL|into beans. slyly special fox\n898|160159|160|1|9|10972.35|0.07|0.08|A|F|1993-07-04|1993-07-09|1993-07-25|NONE|AIR|e slyly across the blithe\n898|178512|6064|2|37|58848.87|0.03|0.05|A|F|1993-08-17|1993-08-04|1993-09-01|DELIVER IN PERSON|REG AIR|packages sleep furiously\n898|48242|5755|3|11|13092.64|0.01|0.00|A|F|1993-09-13|1993-08-31|1993-09-25|TAKE BACK RETURN|MAIL|etly bold accounts \n898|192166|7205|4|36|45293.76|0.04|0.07|R|F|1993-08-04|1993-07-25|1993-08-23|DELIVER IN PERSON|REG AIR| after the carefully \n899|60085|2592|1|18|18811.44|0.04|0.05|N|O|1998-08-06|1998-05-09|1998-09-05|DELIVER IN PERSON|AIR|re daring, pending deposits. blit\n899|46070|8575|2|25|25401.75|0.00|0.07|N|O|1998-07-21|1998-05-12|1998-08-16|NONE|REG AIR|rly final sentiments. bold pinto beans \n899|84976|2501|3|4|7843.88|0.09|0.05|N|O|1998-06-02|1998-06-28|1998-06-14|TAKE BACK RETURN|REG AIR|ter the carefully regular deposits are agai\n899|179704|4739|4|14|24971.80|0.05|0.03|N|O|1998-05-21|1998-05-28|1998-06-03|TAKE BACK RETURN|FOB|ades impress carefully\n899|70776|5791|5|4|6987.08|0.06|0.02|N|O|1998-04-11|1998-05-14|1998-04-27|NONE|TRUCK|ges. blithe, ironic waters cajole care\n899|119083|1595|6|47|51797.76|0.00|0.04|N|O|1998-04-14|1998-05-30|1998-05-13|DELIVER IN PERSON|TRUCK|furiously final foxes after the s\n899|13423|8426|7|11|14700.62|0.02|0.08|N|O|1998-06-03|1998-06-15|1998-06-20|COLLECT COD|REG AIR|t the ironic\n900|198532|1052|1|44|71743.32|0.01|0.06|R|F|1994-12-15|1994-12-03|1994-12-27|COLLECT COD|MAIL| detect quick\n900|114624|4625|2|48|78653.76|0.08|0.04|A|F|1994-12-22|1994-11-08|1995-01-19|COLLECT COD|TRUCK|cial pinto beans nag \n900|74130|4131|3|24|26499.12|0.03|0.00|R|F|1994-10-21|1994-12-25|1994-10-22|TAKE BACK RETURN|SHIP|-ray furiously un\n901|21113|3616|1|36|37227.96|0.01|0.01|N|O|1998-08-11|1998-10-09|1998-08-27|DELIVER IN PERSON|REG AIR|. accounts are care\n901|45222|5223|2|2|2334.44|0.09|0.02|N|O|1998-10-25|1998-09-27|1998-11-01|DELIVER IN PERSON|AIR|d foxes use slyly\n901|42615|5120|3|37|57631.57|0.04|0.08|N|O|1998-11-01|1998-09-13|1998-11-05|NONE|AIR|ickly final deposits \n901|17758|7759|4|11|18433.25|0.00|0.06|N|O|1998-11-13|1998-10-19|1998-11-14|TAKE BACK RETURN|TRUCK|ourts among the quickly expre\n902|110509|510|1|3|4558.50|0.06|0.00|R|F|1994-10-01|1994-10-25|1994-10-28|COLLECT COD|MAIL|into beans thrash blithely about the flu\n902|117211|9723|2|8|9825.68|0.06|0.07|R|F|1994-10-25|1994-09-20|1994-11-07|COLLECT COD|RAIL| orbits al\n902|164836|9869|3|24|45619.92|0.02|0.05|R|F|1994-11-08|1994-10-12|1994-11-26|NONE|FOB|. blithely even accounts poach furiously i\n903|64367|1886|1|27|35946.72|0.04|0.03|N|O|1995-09-18|1995-09-20|1995-10-02|TAKE BACK RETURN|SHIP|lly pending foxes. furiously\n903|8890|1391|2|35|62961.15|0.06|0.05|N|O|1995-09-18|1995-08-21|1995-10-12|TAKE BACK RETURN|TRUCK|rets wake fin\n903|8238|739|3|33|37825.59|0.02|0.03|N|O|1995-09-24|1995-09-01|1995-10-12|COLLECT COD|MAIL|ely ironic packages wake blithely\n903|55408|419|4|9|12270.60|0.09|0.00|N|O|1995-10-06|1995-09-14|1995-10-24|NONE|TRUCK|he slyly ev\n903|41041|1042|5|1|982.04|0.04|0.00|N|O|1995-10-22|1995-09-13|1995-11-03|NONE|AIR|y final platelets sublate among the \n903|167067|7068|6|13|14742.78|0.07|0.02|N|O|1995-09-11|1995-10-04|1995-10-03|COLLECT COD|SHIP|sleep along the final\n928|168148|8149|1|29|35268.06|0.07|0.02|R|F|1995-05-17|1995-05-12|1995-05-21|NONE|REG AIR|ly alongside of the s\n928|47775|5288|2|24|41346.48|0.05|0.08|A|F|1995-04-06|1995-05-08|1995-04-24|DELIVER IN PERSON|AIR|s the furiously regular warthogs im\n928|151774|4290|3|46|83985.42|0.08|0.00|A|F|1995-05-09|1995-04-09|1995-06-01|DELIVER IN PERSON|REG AIR| beans sleep against the carefully ir\n928|51313|8829|4|43|54365.33|0.10|0.05|A|F|1995-04-14|1995-04-21|1995-05-09|NONE|REG AIR|blithely. express, silent requests doze at\n928|11725|1726|5|38|62195.36|0.02|0.08|N|F|1995-06-08|1995-04-15|1995-06-30|TAKE BACK RETURN|SHIP|xpress grouc\n928|54038|4039|6|50|49601.50|0.05|0.00|N|F|1995-06-07|1995-04-15|1995-07-01|DELIVER IN PERSON|TRUCK| slyly slyly special request\n928|10337|2839|7|11|13720.63|0.00|0.01|A|F|1995-04-29|1995-04-16|1995-04-30|NONE|AIR|longside of\n929|128672|3697|1|45|76530.15|0.09|0.01|R|F|1993-01-24|1992-12-06|1993-02-16|DELIVER IN PERSON|REG AIR|ges haggle careful\n929|174218|6736|2|44|56857.24|0.02|0.00|A|F|1992-10-09|1992-11-20|1992-10-22|DELIVER IN PERSON|SHIP|s. excuses cajole. carefully regu\n929|73036|3037|3|14|14126.42|0.06|0.07|A|F|1992-10-21|1992-11-17|1992-11-15|NONE|FOB|gainst the\n929|101639|4150|4|7|11484.41|0.06|0.01|A|F|1992-12-24|1992-12-19|1993-01-08|TAKE BACK RETURN|TRUCK|ithely. slyly c\n930|44804|2317|1|36|62956.80|0.10|0.04|R|F|1994-12-21|1995-02-20|1994-12-24|COLLECT COD|RAIL|quickly regular pinto beans sle\n930|17295|4799|2|47|56977.63|0.08|0.00|A|F|1995-03-20|1995-02-04|1995-04-04|DELIVER IN PERSON|AIR|ackages. fluffily e\n930|64230|1749|3|10|11942.30|0.07|0.08|A|F|1994-12-18|1995-01-27|1995-01-16|COLLECT COD|AIR|ckly regular requests: regular instructions\n930|99635|2145|4|21|34327.23|0.06|0.02|A|F|1995-02-16|1995-03-03|1995-03-13|DELIVER IN PERSON|SHIP|foxes. regular deposits integrate carefu\n930|163239|788|5|50|65111.50|0.03|0.06|A|F|1995-04-03|1995-01-29|1995-04-22|COLLECT COD|MAIL| excuses among the furiously express ideas \n930|144557|2100|6|10|16015.50|0.00|0.04|A|F|1995-02-09|1995-02-17|1995-02-16|NONE|SHIP|blithely bold i\n930|166196|1229|7|30|37865.70|0.07|0.08|R|F|1995-01-20|1995-02-28|1995-02-04|TAKE BACK RETURN|RAIL|g accounts sleep along the platelets.\n931|39783|9784|1|18|31010.04|0.00|0.05|A|F|1993-04-04|1993-01-11|1993-04-13|NONE|RAIL|slyly ironic re\n931|16788|4292|2|10|17047.80|0.05|0.07|A|F|1993-03-01|1993-01-09|1993-03-07|TAKE BACK RETURN|SHIP|ajole quickly. slyly sil\n931|146052|3595|3|48|52706.40|0.01|0.08|A|F|1993-02-03|1993-03-02|1993-02-09|TAKE BACK RETURN|FOB|ep alongside of the fluffy \n931|81324|6341|4|38|49602.16|0.08|0.08|A|F|1993-03-06|1993-02-24|1993-03-27|DELIVER IN PERSON|RAIL|usly final packages integrate carefully\n932|43763|6268|1|41|69977.16|0.01|0.05|N|O|1997-06-05|1997-07-22|1997-06-26|COLLECT COD|RAIL|foxes. ironic pl\n933|48267|5780|1|23|27950.98|0.02|0.04|R|F|1992-08-13|1992-09-18|1992-08-25|DELIVER IN PERSON|MAIL| the furiously bold dinos. sly\n933|12132|2133|2|27|28191.51|0.02|0.01|R|F|1992-10-03|1992-10-02|1992-10-26|DELIVER IN PERSON|RAIL|ests. express\n933|99192|1702|3|26|30970.94|0.05|0.00|A|F|1992-11-09|1992-11-03|1992-11-16|DELIVER IN PERSON|AIR| the deposits affix slyly after t\n934|117900|2923|1|18|34522.20|0.07|0.01|N|O|1996-09-10|1996-09-20|1996-09-25|COLLECT COD|RAIL|y unusual requests dazzle above t\n935|27410|9913|1|23|30760.43|0.05|0.00|N|O|1997-11-11|1997-11-22|1997-11-29|COLLECT COD|REG AIR|ular accounts about\n935|64553|2072|2|23|34903.65|0.02|0.01|N|O|1998-01-11|1997-11-25|1998-02-05|COLLECT COD|TRUCK|hes haggle furiously dolphins. qu\n935|134031|6545|3|36|38341.08|0.06|0.00|N|O|1997-11-05|1997-12-05|1997-11-25|TAKE BACK RETURN|AIR|leep about the exp\n935|57192|2203|4|13|14939.47|0.08|0.04|N|O|1998-01-13|1997-11-30|1998-02-08|DELIVER IN PERSON|TRUCK|ld platelet\n935|12130|4632|5|8|8337.04|0.02|0.05|N|O|1998-01-12|1997-11-02|1998-02-05|NONE|TRUCK|cept the quickly regular p\n935|58299|5815|6|1|1257.29|0.01|0.08|N|O|1997-12-14|1997-11-22|1998-01-08|DELIVER IN PERSON|TRUCK| instructions. ironic acc\n960|106999|9510|1|1|2005.99|0.07|0.00|A|F|1994-12-24|1994-10-26|1995-01-20|DELIVER IN PERSON|AIR|y ironic packages. quickly even \n960|116212|3746|2|25|30705.25|0.06|0.08|R|F|1994-12-01|1994-10-29|1994-12-27|DELIVER IN PERSON|RAIL|ts. fluffily regular requests \n960|174776|2328|3|32|59224.64|0.01|0.08|R|F|1995-01-19|1994-12-17|1995-02-04|DELIVER IN PERSON|FOB|around the blithe, even pl\n961|117921|2944|1|7|13572.44|0.10|0.00|N|O|1995-07-23|1995-07-20|1995-08-11|TAKE BACK RETURN|RAIL|usual dolphins. ironic pearls sleep blit\n961|90469|470|2|18|26270.28|0.09|0.05|N|O|1995-07-01|1995-08-14|1995-07-04|DELIVER IN PERSON|AIR|rmanent foxes haggle speci\n961|96938|6939|3|42|81267.06|0.06|0.01|N|O|1995-08-24|1995-08-21|1995-09-10|TAKE BACK RETURN|SHIP|ests do cajole blithely. furiously bo\n961|33476|5980|4|29|40874.63|0.00|0.07|N|F|1995-06-10|1995-08-20|1995-06-26|TAKE BACK RETURN|TRUCK|l accounts use blithely against the\n961|25276|5277|5|38|45648.26|0.03|0.05|N|O|1995-08-21|1995-07-19|1995-08-27|NONE|RAIL|he blithely special requests. furiousl\n961|196988|6989|6|30|62549.40|0.09|0.03|N|O|1995-07-06|1995-07-20|1995-07-26|DELIVER IN PERSON|MAIL|warhorses slee\n962|56598|6599|1|36|55965.24|0.01|0.03|R|F|1994-08-09|1994-07-10|1994-09-02|COLLECT COD|TRUCK|al foxes. iron\n962|35276|7780|2|27|32704.29|0.09|0.02|A|F|1994-05-11|1994-07-10|1994-06-03|TAKE BACK RETURN|SHIP|y slyly express deposits. final i\n962|79649|9650|3|3|4885.92|0.07|0.08|A|F|1994-05-08|1994-07-06|1994-06-02|DELIVER IN PERSON|FOB|ag furiously. even pa\n962|56308|8814|4|20|25286.00|0.04|0.02|R|F|1994-08-26|1994-06-27|1994-09-11|DELIVER IN PERSON|SHIP| deposits use fluffily according to \n962|151996|7027|5|12|24575.88|0.02|0.00|A|F|1994-06-09|1994-06-07|1994-06-11|COLLECT COD|TRUCK|across the furiously regular escapades daz\n962|187354|2391|6|5|7206.75|0.02|0.05|A|F|1994-08-29|1994-07-15|1994-09-19|COLLECT COD|TRUCK|efully bold packages run slyly caref\n963|193152|710|1|7|8716.05|0.01|0.00|R|F|1994-09-12|1994-07-18|1994-09-17|DELIVER IN PERSON|REG AIR|s. slyly regular depe\n963|97483|9993|2|48|71063.04|0.10|0.06|R|F|1994-08-25|1994-08-12|1994-09-21|DELIVER IN PERSON|RAIL|ages. quickly express deposits cajole pe\n964|198936|8937|1|39|79362.27|0.04|0.01|N|O|1995-06-21|1995-07-24|1995-06-24|NONE|AIR|se furiously regular instructions. blith\n964|112231|2232|2|1|1243.23|0.02|0.05|N|O|1995-08-20|1995-07-29|1995-09-10|DELIVER IN PERSON|REG AIR|unts. quickly even platelets s\n964|56790|9296|3|49|85592.71|0.01|0.03|N|O|1995-09-06|1995-08-10|1995-10-05|NONE|MAIL|ounts. blithely regular packag\n964|54514|7020|4|44|64614.44|0.05|0.02|N|O|1995-09-18|1995-08-02|1995-10-17|TAKE BACK RETURN|TRUCK|ronic deposit\n965|107207|9718|1|20|24284.00|0.04|0.05|N|F|1995-06-16|1995-07-20|1995-07-06|COLLECT COD|MAIL|kly. carefully pending requ\n965|17015|2018|2|23|21436.23|0.06|0.08|N|O|1995-07-12|1995-07-08|1995-08-11|COLLECT COD|MAIL|ld kindle carefully across th\n966|179337|6889|1|19|26910.27|0.07|0.01|N|O|1998-05-26|1998-07-15|1998-05-29|COLLECT COD|FOB|efully final pinto beans. quickly \n966|116560|1583|2|42|66215.52|0.02|0.06|N|O|1998-06-28|1998-06-20|1998-07-05|NONE|TRUCK|tions boost furiously car\n966|21218|6223|3|42|47846.82|0.06|0.08|N|O|1998-06-15|1998-06-08|1998-07-05|NONE|RAIL|sly ironic asymptotes hagg\n966|4189|1690|4|20|21863.60|0.04|0.01|N|O|1998-07-19|1998-07-15|1998-07-27|NONE|TRUCK|pecial ins\n967|58957|3968|1|41|78553.95|0.05|0.05|R|F|1992-09-21|1992-08-15|1992-10-21|NONE|MAIL|ld foxes wake closely special\n967|84402|1927|2|4|5545.60|0.01|0.02|A|F|1992-07-15|1992-07-27|1992-07-18|DELIVER IN PERSON|REG AIR|platelets hang carefully along \n967|131550|4064|3|10|15815.50|0.00|0.02|A|F|1992-09-18|1992-08-06|1992-09-19|DELIVER IN PERSON|MAIL|old pinto beans alongside of the exp\n967|147690|5233|4|49|85146.81|0.01|0.04|A|F|1992-09-28|1992-09-15|1992-10-14|NONE|SHIP|the slyly even ideas. carefully even\n967|16893|9395|5|41|74205.49|0.08|0.04|A|F|1992-07-23|1992-08-07|1992-08-13|TAKE BACK RETURN|FOB|efully special ide\n967|105311|7822|6|17|22377.27|0.05|0.06|A|F|1992-10-02|1992-08-19|1992-10-25|NONE|MAIL|y ironic foxes caj\n967|160878|5911|7|18|34899.66|0.00|0.02|A|F|1992-10-06|1992-08-05|1992-10-15|DELIVER IN PERSON|RAIL|ngage blith\n992|59906|7422|1|14|26122.60|0.10|0.03|N|O|1998-01-29|1997-12-29|1998-02-18|TAKE BACK RETURN|MAIL|the unusual, even dependencies affix fluff\n992|37967|7968|2|34|64768.64|0.02|0.00|N|O|1997-11-29|1998-01-21|1997-11-30|NONE|RAIL|s use silently. blithely regular ideas b\n992|104722|4723|3|30|51801.60|0.10|0.00|N|O|1997-12-15|1998-02-02|1998-01-12|NONE|SHIP|nic instructions n\n992|47212|9717|4|21|24343.41|0.06|0.06|N|O|1997-11-13|1997-12-28|1997-12-10|NONE|TRUCK|fily. quickly special deposit\n992|91050|3560|5|7|7287.35|0.09|0.05|N|O|1997-11-30|1997-12-24|1997-12-16|DELIVER IN PERSON|TRUCK|ideas haggle. special theodolit\n992|74101|1623|6|41|44079.10|0.10|0.01|N|O|1997-11-14|1998-02-04|1997-11-23|TAKE BACK RETURN|AIR|eodolites cajole across the accounts.\n993|174476|6994|1|33|51165.51|0.01|0.05|N|O|1996-01-03|1995-11-28|1996-01-23|DELIVER IN PERSON|AIR| the deposits affix agains\n993|2145|4646|2|28|29319.92|0.06|0.08|N|O|1995-10-24|1995-11-20|1995-11-06|DELIVER IN PERSON|RAIL|lites. even theodolite\n993|39924|4931|3|10|18639.20|0.03|0.08|N|O|1995-12-17|1995-11-13|1995-12-20|NONE|RAIL|encies wake fur\n993|190751|5790|4|40|73670.00|0.01|0.01|N|O|1995-11-16|1995-11-01|1995-12-05|TAKE BACK RETURN|RAIL|gle above the furiously \n993|145484|5485|5|33|50472.84|0.09|0.08|N|O|1995-09-28|1995-10-24|1995-10-03|COLLECT COD|RAIL|fluffily. quiet excuses sleep furiously sly\n993|136979|4519|6|35|70558.95|0.04|0.02|N|O|1995-10-26|1995-10-20|1995-11-05|DELIVER IN PERSON|FOB|es. ironic, ironic requests\n993|4967|2468|7|15|28079.40|0.09|0.03|N|O|1995-09-27|1995-10-21|1995-10-17|DELIVER IN PERSON|MAIL|sits. pending pinto beans haggle? ca\n994|64486|4487|1|4|5801.92|0.07|0.03|R|F|1994-07-05|1994-05-21|1994-07-20|COLLECT COD|SHIP|aggle carefully acc\n994|9177|1678|2|11|11947.87|0.01|0.00|R|F|1994-05-03|1994-06-10|1994-05-22|NONE|AIR|ular accounts sleep \n994|30745|8255|3|5|8378.70|0.08|0.08|A|F|1994-06-24|1994-06-14|1994-06-26|NONE|MAIL|ainst the pending requests. packages sl\n994|130471|8011|4|25|37536.75|0.10|0.00|A|F|1994-06-03|1994-06-02|1994-06-06|COLLECT COD|RAIL|usual pinto beans.\n995|172294|9846|1|15|20494.35|0.08|0.05|N|O|1995-06-30|1995-08-04|1995-07-27|NONE|REG AIR|uses. fluffily fina\n995|128707|1220|2|28|48599.60|0.08|0.03|N|F|1995-06-12|1995-07-20|1995-06-19|DELIVER IN PERSON|SHIP|pades. quick, final frays use flu\n995|165067|100|3|45|50942.70|0.00|0.05|N|O|1995-08-02|1995-07-21|1995-08-03|DELIVER IN PERSON|SHIP|lar packages detect blithely above t\n995|65736|749|4|25|42543.25|0.01|0.08|N|O|1995-09-08|1995-08-05|1995-09-28|NONE|TRUCK|lyly even \n995|23196|3197|5|18|20145.42|0.06|0.03|N|O|1995-07-03|1995-07-29|1995-07-22|TAKE BACK RETURN|AIR| even accounts unwind c\n996|172396|7431|1|43|63140.77|0.03|0.06|N|O|1998-03-27|1998-03-25|1998-04-06|COLLECT COD|SHIP| the blithely ironic foxes. slyly silent d\n997|162614|2615|1|11|18442.71|0.00|0.02|N|O|1997-06-16|1997-07-21|1997-07-14|DELIVER IN PERSON|TRUCK|p furiously according to t\n997|47438|7439|2|17|23552.31|0.03|0.00|N|O|1997-07-28|1997-07-26|1997-08-20|DELIVER IN PERSON|SHIP|aggle quickly furiously\n998|9924|7425|1|22|40346.24|0.04|0.05|A|F|1994-12-03|1995-02-17|1994-12-19|TAKE BACK RETURN|RAIL|lites. qui\n998|180410|5447|2|7|10432.87|0.10|0.05|R|F|1995-03-24|1995-01-18|1995-04-03|NONE|MAIL|nic deposits. even asym\n998|141752|4267|3|30|53812.50|0.05|0.07|A|F|1994-12-02|1995-01-23|1994-12-23|NONE|SHIP|lyly idle Tir\n998|10143|5146|4|6|6318.84|0.09|0.05|R|F|1995-03-20|1994-12-27|1995-04-13|DELIVER IN PERSON|MAIL|refully accounts. carefully express ac\n998|72407|7422|5|1|1379.40|0.04|0.00|R|F|1995-01-05|1995-01-06|1995-01-13|NONE|SHIP|es sleep. regular dependencies use bl\n999|60071|7590|1|34|35056.38|0.00|0.08|R|F|1993-10-30|1993-10-17|1993-10-31|TAKE BACK RETURN|SHIP|its. daringly final instruc\n999|198758|1278|2|41|76126.75|0.08|0.01|A|F|1993-10-16|1993-12-04|1993-11-03|DELIVER IN PERSON|REG AIR|us depths. carefully ironic instruc\n999|117178|2201|3|15|17927.55|0.07|0.06|A|F|1993-12-12|1993-10-18|1994-01-08|COLLECT COD|REG AIR|y ironic requests. carefully regu\n999|2643|2644|4|10|15456.40|0.05|0.02|A|F|1993-11-23|1993-12-02|1993-11-29|NONE|MAIL|efully pending\n999|18337|8338|5|3|3765.99|0.03|0.00|R|F|1993-09-17|1993-10-22|1993-10-13|NONE|FOB|nic, pending ideas. bl\n999|180934|8489|6|37|74552.41|0.00|0.04|R|F|1994-01-03|1993-10-28|1994-01-12|DELIVER IN PERSON|TRUCK|ckly slyly unusual packages: packages hagg\n1024|198068|3107|1|49|57136.94|0.03|0.05|N|O|1998-03-06|1998-01-26|1998-03-29|TAKE BACK RETURN|FOB|ts. asymptotes nag fur\n1024|125344|369|2|34|46557.56|0.00|0.01|N|O|1998-01-06|1998-02-05|1998-01-26|COLLECT COD|SHIP|des the slyly even\n1024|43565|1078|3|28|42239.68|0.04|0.01|N|O|1998-03-04|1998-03-12|1998-03-15|TAKE BACK RETURN|TRUCK|e blithely regular pi\n1024|183259|5778|4|13|17449.25|0.02|0.04|N|O|1998-04-11|1998-02-26|1998-04-18|NONE|FOB|e slyly around the slyly special instructi\n1024|20829|8336|5|49|85741.18|0.02|0.04|N|O|1998-02-27|1998-03-10|1998-03-27|COLLECT COD|FOB| carefully bold \n1025|149335|9336|1|36|49835.88|0.03|0.04|A|F|1995-05-15|1995-07-05|1995-06-10|COLLECT COD|FOB|e unusual, regular instr\n1025|68247|8248|2|23|27950.52|0.08|0.03|N|F|1995-06-02|1995-07-29|1995-06-23|COLLECT COD|RAIL| regular platelets nag carefu\n1025|22445|7450|3|25|34186.00|0.06|0.05|R|F|1995-05-29|1995-06-21|1995-06-13|DELIVER IN PERSON|REG AIR|xpress foxes. furiousl\n1026|37213|4723|1|36|41407.56|0.10|0.02|N|O|1997-06-14|1997-07-20|1997-06-23|NONE|SHIP|st the ide\n1026|36855|1862|2|6|10751.10|0.10|0.08|N|O|1997-07-07|1997-08-16|1997-07-14|TAKE BACK RETURN|TRUCK|to beans. special, regular packages hagg\n1027|155942|973|1|43|85911.42|0.07|0.08|R|F|1992-06-17|1992-08-28|1992-07-10|DELIVER IN PERSON|MAIL|oxes. carefully regular deposits\n1027|112414|7437|2|20|28528.20|0.01|0.02|A|F|1992-06-08|1992-08-29|1992-06-14|NONE|TRUCK|ar excuses eat f\n1027|125692|3229|3|2|3435.38|0.01|0.02|R|F|1992-08-28|1992-07-09|1992-09-10|NONE|FOB|s. quickly unusual waters inside \n1027|99229|6757|4|13|15966.86|0.08|0.01|R|F|1992-08-22|1992-07-10|1992-09-12|DELIVER IN PERSON|RAIL|ily ironic ideas use\n1027|135427|7941|5|22|32173.24|0.02|0.00|A|F|1992-09-03|1992-08-14|1992-10-01|DELIVER IN PERSON|FOB|the furiously express ex\n1027|104958|7469|6|10|19629.50|0.06|0.08|R|F|1992-08-28|1992-08-06|1992-09-03|COLLECT COD|REG AIR|ilent, express foxes near the blithely sp\n1028|127811|324|1|2|3677.62|0.09|0.03|A|F|1994-01-10|1994-03-22|1994-01-26|COLLECT COD|FOB|s alongside of the regular asymptotes sleep\n1028|111876|6899|2|39|73626.93|0.06|0.05|R|F|1994-02-18|1994-03-22|1994-03-06|TAKE BACK RETURN|MAIL| final dependencies affix a\n1028|99556|4575|3|8|12444.40|0.03|0.07|A|F|1994-02-14|1994-03-28|1994-02-22|NONE|AIR|e carefully final packages. furiously fi\n1028|31239|3743|4|26|30425.98|0.07|0.02|A|F|1994-03-18|1994-02-08|1994-03-19|TAKE BACK RETURN|RAIL|ronic platelets. carefully f\n1028|28550|6057|5|27|39920.85|0.00|0.04|A|F|1994-04-03|1994-02-07|1994-04-26|NONE|REG AIR|ial accounts nag. slyly\n1028|25111|7614|6|39|40408.29|0.03|0.02|A|F|1994-02-27|1994-02-16|1994-03-02|DELIVER IN PERSON|AIR|c theodoli\n1028|30960|5967|7|22|41601.12|0.03|0.00|R|F|1994-04-24|1994-02-27|1994-05-08|NONE|REG AIR| Tiresias alongside of the carefully spec\n1029|136137|8651|1|45|52790.85|0.05|0.07|R|F|1994-07-21|1994-08-30|1994-07-29|TAKE BACK RETURN|FOB|sits boost blithely\n1030|64397|1916|1|17|23143.63|0.06|0.06|R|F|1994-10-13|1994-08-01|1994-11-10|DELIVER IN PERSON|RAIL|ly. carefully even packages dazz\n1031|45551|5552|1|15|22448.25|0.10|0.08|A|F|1994-11-07|1994-10-29|1994-11-09|TAKE BACK RETURN|FOB|about the carefully bold a\n1031|164788|7305|2|28|51877.84|0.05|0.01|A|F|1994-12-10|1994-10-29|1994-12-18|COLLECT COD|FOB|ly ironic accounts across the q\n1031|186928|4483|3|27|54402.84|0.07|0.02|R|F|1994-09-20|1994-10-18|1994-10-10|DELIVER IN PERSON|SHIP|gular deposits cajole. blithely unus\n1031|87154|2171|4|7|7988.05|0.03|0.03|R|F|1994-12-07|1994-11-11|1994-12-30|COLLECT COD|FOB|r instructions. car\n1031|190193|7751|5|44|56460.36|0.01|0.07|R|F|1994-11-20|1994-11-24|1994-12-11|NONE|AIR|re slyly above the furio\n1056|120970|3483|1|37|73665.89|0.04|0.06|R|F|1995-02-18|1995-04-01|1995-03-20|NONE|TRUCK| special packages. qui\n1057|192240|4760|1|29|38634.96|0.10|0.01|A|F|1992-05-05|1992-05-05|1992-06-03|TAKE BACK RETURN|SHIP|es wake according to the q\n1057|168078|595|2|11|12606.77|0.00|0.02|R|F|1992-03-31|1992-04-18|1992-04-18|COLLECT COD|AIR|yly final theodolites. furi\n1057|84143|9160|3|21|23669.94|0.03|0.04|A|F|1992-02-28|1992-05-01|1992-03-10|NONE|REG AIR|ar orbits boost bli\n1057|181895|6932|4|20|39537.80|0.06|0.03|R|F|1992-03-02|1992-05-19|1992-03-13|DELIVER IN PERSON|TRUCK|s wake bol\n1057|96404|3932|5|7|9802.80|0.06|0.05|R|F|1992-06-05|1992-04-30|1992-06-20|NONE|TRUCK|y slyly express theodolites. slyly bo\n1057|51461|6472|6|19|26836.74|0.04|0.07|A|F|1992-05-31|1992-05-09|1992-06-02|DELIVER IN PERSON|FOB|r-- packages haggle alon\n1058|139555|7095|1|24|38269.20|0.08|0.04|A|F|1993-07-09|1993-05-28|1993-07-22|DELIVER IN PERSON|TRUCK|fully ironic accounts. express accou\n1058|88088|5613|2|5|5380.40|0.04|0.07|R|F|1993-05-11|1993-05-29|1993-05-27|COLLECT COD|TRUCK|refully even requests boost along\n1058|89249|9250|3|44|54482.56|0.10|0.01|R|F|1993-06-26|1993-06-21|1993-07-20|COLLECT COD|TRUCK|uriously f\n1058|4119|1620|4|25|25577.75|0.09|0.01|A|F|1993-05-27|1993-06-10|1993-06-20|TAKE BACK RETURN|MAIL| the final requests believe carefully \n1059|177583|7584|1|16|26569.28|0.07|0.02|A|F|1994-04-24|1994-03-31|1994-04-28|DELIVER IN PERSON|SHIP|y ironic pinto \n1059|28839|6346|2|7|12374.81|0.07|0.06|R|F|1994-03-30|1994-04-01|1994-04-24|DELIVER IN PERSON|MAIL|the furiously silent excuses are e\n1059|87935|444|3|45|86531.85|0.00|0.02|R|F|1994-06-10|1994-05-08|1994-06-21|COLLECT COD|RAIL|riously even theodolites. slyly regula\n1059|109784|7315|4|26|46638.28|0.09|0.01|A|F|1994-03-17|1994-04-18|1994-03-26|DELIVER IN PERSON|TRUCK|ar pinto beans at the furiously \n1059|138061|575|5|37|40665.22|0.09|0.04|R|F|1994-03-31|1994-05-08|1994-04-06|COLLECT COD|RAIL| packages lose in place of the slyly unusu\n1059|189215|1734|6|50|65210.50|0.00|0.03|A|F|1994-06-15|1994-05-11|1994-06-29|NONE|MAIL|s impress furiously about\n1059|122187|2188|7|13|15719.34|0.01|0.03|R|F|1994-06-12|1994-05-11|1994-07-02|COLLECT COD|TRUCK|usly regular theodo\n1060|195479|3037|1|8|12595.76|0.07|0.04|R|F|1993-05-21|1993-05-06|1993-06-10|DELIVER IN PERSON|FOB|iously. furiously regular in\n1060|7614|5115|2|26|39561.86|0.06|0.08|R|F|1993-04-12|1993-04-01|1993-04-20|DELIVER IN PERSON|TRUCK|counts; even deposits are carefull\n1060|163141|5658|3|11|13245.54|0.01|0.07|A|F|1993-05-13|1993-05-08|1993-05-17|TAKE BACK RETURN|MAIL|e regular deposits: re\n1060|109815|7346|4|16|29196.96|0.03|0.06|A|F|1993-06-15|1993-04-18|1993-07-05|COLLECT COD|SHIP|ccounts. foxes maintain care\n1060|52852|7863|5|1|1804.85|0.04|0.06|A|F|1993-06-19|1993-05-10|1993-06-21|COLLECT COD|RAIL|posits detect carefully abo\n1060|71383|3891|6|26|35213.88|0.01|0.03|A|F|1993-02-28|1993-04-01|1993-03-09|TAKE BACK RETURN|FOB|quickly abo\n1060|120102|5127|7|36|40395.60|0.09|0.01|R|F|1993-03-14|1993-03-24|1993-04-02|TAKE BACK RETURN|FOB|r the quickly\n1061|150016|5047|1|7|7462.07|0.04|0.04|N|O|1998-08-09|1998-08-12|1998-08-16|COLLECT COD|FOB|es are slyly expr\n1061|118529|1041|2|2|3095.04|0.06|0.02|N|O|1998-08-15|1998-08-05|1998-08-24|COLLECT COD|MAIL|. regular accounts impre\n1061|110165|5188|3|26|30554.16|0.08|0.02|N|O|1998-06-18|1998-07-25|1998-06-24|TAKE BACK RETURN|AIR|ave to slee\n1061|135309|5310|4|41|55116.30|0.00|0.05|N|O|1998-06-29|1998-07-02|1998-07-27|NONE|MAIL|s are. ironic theodolites cajole. dep\n1061|130014|15|5|50|52200.50|0.04|0.08|N|O|1998-05-25|1998-07-22|1998-06-22|COLLECT COD|AIR|nding excuses are around the e\n1061|143492|6007|6|35|53742.15|0.09|0.05|N|O|1998-07-05|1998-07-07|1998-07-30|TAKE BACK RETURN|MAIL|ending requests nag careful\n1062|136066|6067|1|38|41878.28|0.00|0.01|N|O|1997-01-27|1997-03-07|1997-02-16|DELIVER IN PERSON|TRUCK|deas. pending acc\n1063|95042|61|1|42|43555.68|0.03|0.02|A|F|1994-07-10|1994-05-25|1994-07-26|NONE|RAIL|tructions about the blithely ex\n1088|106899|6900|1|30|57176.70|0.07|0.03|A|F|1992-05-22|1992-06-25|1992-06-11|TAKE BACK RETURN|SHIP|long the packages snooze careful\n1088|36841|4351|2|11|19556.24|0.06|0.00|A|F|1992-08-30|1992-07-25|1992-09-10|TAKE BACK RETURN|AIR|inal requests. fluffily express theod\n1088|180349|2868|3|5|7146.70|0.03|0.07|A|F|1992-07-01|1992-07-25|1992-07-02|NONE|AIR|refully ironic packages. r\n1088|123962|3963|4|3|5957.88|0.09|0.03|A|F|1992-06-15|1992-08-02|1992-06-18|DELIVER IN PERSON|MAIL|pecial theodolites \n1089|150036|7582|1|47|51043.41|0.05|0.06|N|O|1996-06-26|1996-06-25|1996-07-11|NONE|TRUCK|aggle furiously among the bravely eve\n1089|49934|2439|2|35|65937.55|0.03|0.00|N|O|1996-08-14|1996-07-10|1996-08-26|NONE|TRUCK|ly express deposits haggle\n1089|25189|5190|3|23|25626.14|0.10|0.05|N|O|1996-06-24|1996-07-25|1996-07-20|DELIVER IN PERSON|AIR|g dolphins. deposits integrate. s\n1089|140435|7978|4|1|1475.43|0.01|0.03|N|O|1996-07-08|1996-07-07|1996-07-17|COLLECT COD|RAIL|n courts among the caref\n1090|21522|1523|1|5|7217.60|0.02|0.05|N|O|1998-02-19|1997-12-25|1998-02-24|DELIVER IN PERSON|AIR|s above the \n1090|112475|7498|2|28|41649.16|0.08|0.08|N|O|1998-02-20|1998-01-03|1998-03-19|NONE|FOB|s cajole above the regular\n1091|37233|7234|1|40|46809.20|0.10|0.06|N|O|1996-12-17|1996-10-14|1996-12-24|TAKE BACK RETURN|REG AIR|platelets. regular packag\n1092|183493|3494|1|48|75671.52|0.04|0.04|N|O|1995-06-25|1995-04-06|1995-07-18|DELIVER IN PERSON|AIR|unusual accounts. fluffi\n1092|152637|183|2|1|1689.63|0.01|0.06|A|F|1995-03-10|1995-04-21|1995-04-06|COLLECT COD|RAIL|lent, pending requests-- requests nag accor\n1092|160701|5734|3|28|49327.60|0.05|0.08|R|F|1995-04-08|1995-05-01|1995-05-02|DELIVER IN PERSON|FOB|affix carefully. u\n1092|85157|5158|4|2|2284.30|0.05|0.07|R|F|1995-04-09|1995-05-12|1995-05-03|TAKE BACK RETURN|TRUCK|ans. slyly eve\n1093|86927|1944|1|7|13397.44|0.04|0.02|N|O|1997-11-24|1997-09-23|1997-11-25|TAKE BACK RETURN|SHIP|bold deposits. blithely ironic depos\n1093|176300|3852|2|37|50923.10|0.08|0.04|N|O|1997-11-06|1997-10-08|1997-11-22|COLLECT COD|FOB|le furiously across the carefully sp\n1093|60434|435|3|34|47410.62|0.01|0.06|N|O|1997-11-07|1997-09-06|1997-11-28|TAKE BACK RETURN|REG AIR|sits. express accounts play carefully. bol\n1094|114492|4493|1|9|13558.41|0.07|0.06|N|O|1997-12-28|1998-03-16|1998-01-18|DELIVER IN PERSON|AIR|as. slyly pe\n1095|136083|8597|1|33|36929.64|0.01|0.02|N|O|1995-10-03|1995-09-22|1995-10-13|NONE|MAIL|slyly around the iron\n1095|135953|3493|2|24|47734.80|0.04|0.06|N|O|1995-08-24|1995-10-20|1995-09-09|COLLECT COD|TRUCK|packages nod furiously above the carefully \n1095|155206|7722|3|13|16395.60|0.06|0.01|N|O|1995-08-24|1995-10-19|1995-09-02|TAKE BACK RETURN|REG AIR|ously even accounts. slyly bold a\n1095|134380|1920|4|28|39602.64|0.08|0.03|N|O|1995-09-20|1995-11-18|1995-10-02|DELIVER IN PERSON|SHIP| regular pac\n1095|111798|9332|5|40|72391.60|0.09|0.03|N|O|1995-10-18|1995-11-14|1995-11-09|NONE|MAIL| bold accounts haggle slyly furiously even\n1095|180389|2908|6|37|54367.06|0.07|0.08|N|O|1995-10-04|1995-11-13|1995-10-12|NONE|SHIP|. quickly even dolphins sle\n1120|177951|469|1|10|20289.50|0.08|0.05|N|O|1997-12-17|1998-01-21|1997-12-23|DELIVER IN PERSON|MAIL|dependencies. blithel\n1120|19045|9046|2|49|47237.96|0.01|0.07|N|O|1998-01-03|1998-02-02|1998-01-09|TAKE BACK RETURN|RAIL|heodolites. quick re\n1120|75472|7980|3|21|30396.87|0.06|0.01|N|O|1998-01-11|1998-02-04|1998-01-19|COLLECT COD|REG AIR|s: fluffily even packages c\n1120|45653|662|4|22|35170.30|0.09|0.08|N|O|1997-11-15|1998-01-25|1997-12-07|TAKE BACK RETURN|REG AIR|ons. slyly silent requests sleep silent\n1120|82435|2436|5|10|14174.30|0.07|0.08|N|O|1997-11-10|1998-02-01|1997-11-28|TAKE BACK RETURN|AIR|ages haggle furiously \n1121|167726|5275|1|42|75336.24|0.04|0.05|N|O|1997-03-05|1997-03-18|1997-03-14|DELIVER IN PERSON|SHIP|nts are slyly special packages. f\n1121|160276|2793|2|27|36079.29|0.08|0.00|N|O|1997-05-08|1997-03-28|1997-05-14|NONE|MAIL|ly ironic accounts cajole slyly abou\n1121|156826|9342|3|10|18828.20|0.00|0.04|N|O|1997-04-17|1997-03-18|1997-05-02|TAKE BACK RETURN|RAIL|dencies. quickly regular theodolites n\n1121|165255|2804|4|29|38287.25|0.02|0.01|N|O|1997-03-07|1997-04-02|1997-04-01|DELIVER IN PERSON|REG AIR| use furiously. quickly silent package\n1121|29726|4731|5|47|77818.84|0.09|0.03|N|O|1997-04-27|1997-03-28|1997-05-14|COLLECT COD|FOB|ly idle, i\n1121|199422|9423|6|50|76071.00|0.06|0.03|N|O|1997-04-21|1997-02-16|1997-04-25|NONE|TRUCK|odolites. slyly even accounts\n1121|79080|6602|7|37|39185.96|0.06|0.01|N|O|1997-02-27|1997-03-04|1997-03-02|COLLECT COD|RAIL|special packages. fluffily final requests s\n1122|91176|8704|1|8|9337.36|0.10|0.06|N|O|1997-02-02|1997-04-03|1997-02-22|TAKE BACK RETURN|RAIL|c foxes are along the slyly r\n1122|181767|6804|2|29|53614.04|0.05|0.04|N|O|1997-05-07|1997-04-07|1997-05-15|COLLECT COD|SHIP|ptotes. quickl\n1122|146422|3965|3|25|36710.50|0.09|0.01|N|O|1997-03-21|1997-03-03|1997-04-07|TAKE BACK RETURN|RAIL|d furiously. pinto \n1122|105680|8191|4|40|67427.20|0.08|0.08|N|O|1997-02-07|1997-03-25|1997-02-25|NONE|REG AIR|packages sleep after the asym\n1122|150352|353|5|15|21035.25|0.05|0.03|N|O|1997-04-15|1997-03-15|1997-05-07|COLLECT COD|SHIP|olve blithely regular, \n1122|161814|9363|6|24|45019.44|0.04|0.01|N|O|1997-03-08|1997-02-20|1997-04-05|NONE|RAIL|blithely requests. slyly pending r\n1122|299|5300|7|38|45573.02|0.00|0.08|N|O|1997-01-23|1997-04-02|1997-02-16|NONE|TRUCK|t theodolites sleep. even, ironic\n1123|11173|8677|1|10|10841.70|0.05|0.08|N|O|1996-11-12|1996-10-04|1996-11-30|NONE|MAIL|ckages are above the depths. slyly ir\n1123|177599|117|2|39|65387.01|0.03|0.08|N|O|1996-08-25|1996-10-21|1996-09-04|DELIVER IN PERSON|REG AIR|rding to the furiously ironic requests: r\n1123|100807|3318|3|38|68696.40|0.03|0.08|N|O|1996-09-23|1996-10-04|1996-09-27|DELIVER IN PERSON|FOB| blithely carefully unusual reques\n1124|197704|5262|1|1|1801.70|0.09|0.08|N|O|1998-10-06|1998-10-02|1998-10-30|NONE|REG AIR| instructions cajole qu\n1124|5301|302|2|13|15681.90|0.05|0.04|N|O|1998-09-05|1998-10-03|1998-09-30|DELIVER IN PERSON|SHIP|t the slyly \n1124|92298|4808|3|35|45160.15|0.10|0.05|N|O|1998-11-25|1998-10-08|1998-12-25|TAKE BACK RETURN|AIR|ut the slyly bold pinto beans; fi\n1124|49104|9105|4|25|26327.50|0.08|0.05|N|O|1998-08-05|1998-10-14|1998-08-11|NONE|MAIL|ggle slyly according\n1124|74888|7396|5|33|61475.04|0.05|0.04|N|O|1998-10-19|1998-09-17|1998-10-26|TAKE BACK RETURN|SHIP|eposits sleep slyly. stealthily f\n1124|26745|1750|6|43|71884.82|0.01|0.03|N|O|1998-09-19|1998-10-28|1998-10-10|COLLECT COD|MAIL|across the \n1124|94195|4196|7|1|1189.19|0.09|0.01|N|O|1998-10-07|1998-08-31|1998-10-12|NONE|TRUCK|ly bold accou\n1125|132073|7100|1|4|4420.28|0.08|0.02|A|F|1994-12-10|1994-12-28|1994-12-30|NONE|MAIL| quickly express packages a\n1125|137772|7773|2|24|43434.48|0.10|0.03|R|F|1995-01-31|1994-12-02|1995-02-20|COLLECT COD|AIR|es about the slyly s\n1125|121039|3552|3|26|27560.78|0.05|0.04|A|F|1995-02-24|1995-01-18|1995-03-05|COLLECT COD|TRUCK|l instruction\n1125|97823|2842|4|29|52803.78|0.06|0.00|A|F|1994-11-29|1994-12-20|1994-12-10|DELIVER IN PERSON|RAIL| platelets wake against the carefully i\n1126|35025|2535|1|44|42240.88|0.08|0.03|N|O|1998-05-07|1998-04-02|1998-05-29|NONE|TRUCK|es. carefully special\n1126|57176|2187|2|7|7932.19|0.06|0.01|N|O|1998-05-02|1998-03-22|1998-05-21|COLLECT COD|MAIL|ons. final, unusual\n1126|146694|1723|3|14|24369.66|0.07|0.07|N|O|1998-04-17|1998-04-15|1998-05-12|DELIVER IN PERSON|TRUCK|nstructions. blithe\n1127|42596|5101|1|35|53850.65|0.02|0.03|N|O|1995-11-25|1995-11-03|1995-12-17|NONE|TRUCK|l instructions boost blithely according \n1127|109765|4786|2|38|67440.88|0.09|0.05|N|O|1995-11-07|1995-11-11|1995-11-26|DELIVER IN PERSON|RAIL|. never final packages boost acro\n1127|19646|9647|3|29|45403.56|0.09|0.07|N|O|1995-09-20|1995-11-21|1995-10-11|DELIVER IN PERSON|REG AIR|y. blithely r\n1127|174304|4305|4|7|9648.10|0.07|0.05|N|O|1995-11-05|1995-11-02|1995-11-11|DELIVER IN PERSON|FOB| idly pending pains \n1152|8646|8647|1|23|35756.72|0.06|0.04|A|F|1994-10-14|1994-10-22|1994-10-21|DELIVER IN PERSON|MAIL|equests alongside of the unusual \n1152|99604|2114|2|25|40090.00|0.04|0.08|R|F|1994-10-20|1994-09-18|1994-10-28|DELIVER IN PERSON|REG AIR|efully ironic accounts. sly instructions wa\n1152|41846|4351|3|6|10727.04|0.07|0.03|A|F|1994-12-07|1994-11-05|1994-12-25|DELIVER IN PERSON|FOB|p furiously; packages above th\n1153|85058|2583|1|15|15645.75|0.00|0.08|N|O|1996-04-24|1996-07-17|1996-04-29|TAKE BACK RETURN|SHIP|uctions boost fluffily according to\n1153|168992|1509|2|50|103049.50|0.00|0.07|N|O|1996-06-27|1996-07-13|1996-07-05|COLLECT COD|REG AIR|ronic asymptotes nag slyly. \n1153|43393|3394|3|25|33409.75|0.00|0.05|N|O|1996-06-18|1996-06-28|1996-07-09|NONE|TRUCK| theodolites\n1153|91126|1127|4|43|48036.16|0.01|0.00|N|O|1996-06-09|1996-06-01|1996-07-04|DELIVER IN PERSON|MAIL|special instructions are. unusual, final du\n1153|141212|6241|5|45|56394.45|0.00|0.02|N|O|1996-06-18|1996-06-20|1996-07-03|TAKE BACK RETURN|AIR|oss the ex\n1153|135360|5361|6|26|36279.36|0.02|0.03|N|O|1996-08-16|1996-07-12|1996-09-08|NONE|MAIL|kages haggle carefully. f\n1153|191834|4354|7|5|9629.15|0.02|0.03|N|O|1996-05-03|1996-06-12|1996-05-28|TAKE BACK RETURN|FOB|special excuses promi\n1154|142724|5239|1|31|54768.32|0.06|0.06|A|F|1992-04-17|1992-04-26|1992-05-17|COLLECT COD|AIR|ithely. final, blithe \n1154|147409|4952|2|50|72820.00|0.07|0.06|A|F|1992-04-22|1992-04-21|1992-05-01|NONE|TRUCK|ove the furiously bold Tires\n1154|96103|3631|3|5|5495.50|0.09|0.04|A|F|1992-06-07|1992-05-07|1992-07-05|DELIVER IN PERSON|MAIL|the furiously \n1154|574|575|4|35|51609.95|0.00|0.07|A|F|1992-03-30|1992-04-02|1992-04-21|DELIVER IN PERSON|TRUCK|the carefully regular pinto beans boost\n1154|35462|7966|5|18|25154.28|0.02|0.03|A|F|1992-02-26|1992-03-24|1992-03-20|TAKE BACK RETURN|REG AIR|y regular excuses cajole blithely. fi\n1154|195101|7621|6|50|59805.00|0.06|0.03|A|F|1992-03-04|1992-04-01|1992-04-01|TAKE BACK RETURN|TRUCK| even, special \n1155|69329|9330|1|4|5193.28|0.09|0.05|N|O|1997-10-19|1997-12-09|1997-11-02|DELIVER IN PERSON|SHIP|ic foxes according to the carefully final \n1155|195749|788|2|39|71944.86|0.08|0.05|N|O|1998-01-29|1998-01-03|1998-02-01|COLLECT COD|TRUCK|ckly final pinto beans was.\n1155|146710|9225|3|23|40404.33|0.08|0.03|N|O|1997-11-24|1997-11-28|1997-12-06|DELIVER IN PERSON|FOB|ly unusual packages. iro\n1155|139205|9206|4|12|14930.40|0.01|0.06|N|O|1997-11-01|1998-01-03|1997-11-19|DELIVER IN PERSON|RAIL|packages do\n1155|4313|1814|5|49|59648.19|0.04|0.08|N|O|1997-12-07|1997-12-30|1997-12-08|NONE|AIR|ccounts are alongside of t\n1156|86609|1626|1|15|23934.00|0.07|0.06|N|O|1996-12-21|1997-01-03|1997-01-10|TAKE BACK RETURN|AIR|the furiously pen\n1156|32488|2489|2|21|29830.08|0.02|0.08|N|O|1996-11-07|1997-01-14|1996-12-03|NONE|AIR|dolphins. fluffily ironic packages sleep re\n1156|11149|8653|3|29|30744.06|0.09|0.06|N|O|1997-01-24|1996-12-26|1997-02-04|DELIVER IN PERSON|TRUCK|ts sleep sly\n1156|171422|1423|4|42|62723.64|0.02|0.00|N|O|1997-01-18|1997-01-12|1997-02-13|NONE|REG AIR|s. quickly bold pains are\n1156|73755|6263|5|49|84708.75|0.04|0.01|N|O|1996-11-16|1996-12-02|1996-12-05|COLLECT COD|AIR|ithely unusual in\n1156|194006|1564|6|42|46200.00|0.02|0.06|N|O|1997-01-27|1997-01-09|1997-01-28|DELIVER IN PERSON|MAIL|even requests boost ironic deposits. pe\n1156|46115|3628|7|20|21222.20|0.08|0.07|N|O|1997-01-01|1997-01-06|1997-01-16|COLLECT COD|MAIL|deposits sleep bravel\n1157|48128|3137|1|16|17217.92|0.06|0.00|N|O|1998-04-12|1998-03-09|1998-04-23|DELIVER IN PERSON|MAIL|tions hang\n1157|82041|7058|2|4|4092.16|0.10|0.05|N|O|1998-02-24|1998-03-30|1998-03-24|DELIVER IN PERSON|SHIP|ounts. ironic deposits\n1157|47985|5498|3|8|15463.84|0.02|0.00|N|O|1998-03-25|1998-03-16|1998-03-29|NONE|REG AIR|blithely even pa\n1157|76907|6908|4|46|86659.40|0.07|0.08|N|O|1998-04-19|1998-03-13|1998-04-23|NONE|FOB|slyly regular excuses. accounts\n1157|159850|4881|5|14|26737.90|0.03|0.03|N|O|1998-04-17|1998-03-03|1998-05-01|NONE|FOB|theodolites. fluffily re\n1158|44218|6723|1|5|5811.05|0.02|0.04|N|O|1996-10-20|1996-07-30|1996-11-14|COLLECT COD|AIR|symptotes along the care\n1158|156084|3630|2|23|26221.84|0.00|0.08|N|O|1996-10-21|1996-08-19|1996-10-31|COLLECT COD|MAIL|ularly ironic requests use care\n1159|108033|8034|1|39|40600.17|0.01|0.00|A|F|1992-11-20|1992-10-28|1992-12-18|TAKE BACK RETURN|FOB| blithely express reques\n1159|95490|509|2|7|10398.43|0.08|0.00|A|F|1992-11-25|1992-10-27|1992-12-20|NONE|AIR|olve somet\n1159|97431|9941|3|11|15712.73|0.10|0.03|R|F|1992-12-09|1992-12-07|1992-12-18|DELIVER IN PERSON|MAIL|h furiousl\n1184|46728|9233|1|27|45217.44|0.01|0.00|N|O|1998-01-10|1997-12-02|1998-02-06|TAKE BACK RETURN|REG AIR|s wake fluffily. fl\n1184|146616|1645|2|4|6650.44|0.04|0.03|N|O|1997-12-25|1998-01-24|1998-01-18|DELIVER IN PERSON|RAIL| express packages. slyly expres\n1184|163940|3941|3|7|14027.58|0.05|0.00|N|O|1998-02-14|1998-01-06|1998-03-11|COLLECT COD|TRUCK|ckly warthogs. blithely bold foxes hag\n1184|125544|3081|4|3|4708.62|0.02|0.05|N|O|1998-01-15|1997-12-19|1998-02-02|NONE|REG AIR|ar packages. final packages cajol\n1185|71925|6940|1|8|15175.36|0.01|0.06|A|F|1992-12-05|1992-10-05|1992-12-28|DELIVER IN PERSON|MAIL|ely according to the furiously regular r\n1185|30749|750|2|28|47032.72|0.07|0.06|A|F|1992-09-24|1992-10-07|1992-10-10|DELIVER IN PERSON|REG AIR|ke. slyly regular t\n1185|189350|1869|3|12|17272.20|0.05|0.06|R|F|1992-10-12|1992-09-26|1992-11-11|NONE|REG AIR|instructions. daringly pend\n1186|2457|2458|1|28|38064.60|0.08|0.07|N|O|1996-12-08|1996-10-17|1996-12-15|TAKE BACK RETURN|TRUCK|ffily spec\n1186|91123|6142|2|11|12255.32|0.07|0.05|N|O|1996-10-03|1996-10-21|1996-10-17|DELIVER IN PERSON|AIR|s haggle furiously; slyl\n1186|100774|775|3|20|35495.40|0.07|0.07|N|O|1996-08-20|1996-10-23|1996-09-05|COLLECT COD|FOB|ely alongside of the blithel\n1186|105917|5918|4|27|51918.57|0.06|0.04|N|O|1996-10-08|1996-11-06|1996-10-09|TAKE BACK RETURN|SHIP|accounts. express, e\n1187|177779|5331|1|29|53846.33|0.01|0.04|R|F|1992-12-10|1993-02-09|1992-12-29|TAKE BACK RETURN|RAIL|riously express ac\n1187|130451|2965|2|15|22221.75|0.03|0.04|A|F|1992-12-22|1993-01-13|1993-01-01|NONE|TRUCK|ests. foxes wake. carefu\n1187|77243|9751|3|40|48809.60|0.08|0.06|R|F|1993-03-05|1992-12-31|1993-03-12|NONE|TRUCK|ar, brave deposits nag blithe\n1188|114959|7471|1|2|3947.90|0.00|0.04|N|O|1996-05-22|1996-05-23|1996-06-06|COLLECT COD|RAIL|its breach blit\n1188|112024|2025|2|9|9324.18|0.01|0.08|N|O|1996-08-04|1996-06-04|1996-08-19|NONE|REG AIR|ow carefully ironic d\n1188|178737|8738|3|41|74444.93|0.07|0.04|N|O|1996-06-29|1996-05-21|1996-07-21|TAKE BACK RETURN|TRUCK|althy packages. fluffily unusual ideas h\n1189|50676|677|1|23|37413.41|0.06|0.00|R|F|1994-07-25|1994-06-07|1994-08-02|COLLECT COD|FOB|s. fluffy Tiresias run quickly. bra\n1189|104889|2420|2|32|60604.16|0.09|0.02|R|F|1994-05-06|1994-07-03|1994-05-15|TAKE BACK RETURN|FOB|e regular deposits. quickly quiet deposi\n1189|56505|9011|3|22|32153.00|0.05|0.03|R|F|1994-06-09|1994-06-29|1994-06-23|DELIVER IN PERSON|TRUCK|quickly unusual platelets lose forges. ca\n1190|83788|1313|1|32|56696.96|0.07|0.06|N|O|1997-05-08|1997-04-17|1997-06-01|COLLECT COD|FOB|y final packages? slyly even\n1191|48288|793|1|29|35852.12|0.00|0.04|N|O|1996-01-24|1996-01-28|1996-02-17|COLLECT COD|AIR| regular pin\n1216|96917|4445|1|8|15311.28|0.03|0.04|R|F|1993-02-01|1993-03-06|1993-02-08|TAKE BACK RETURN|TRUCK| of the carefully express\n1216|74067|1589|2|48|49970.88|0.10|0.01|R|F|1993-01-17|1993-02-01|1993-02-13|COLLECT COD|SHIP|symptotes use against th\n1216|41738|1739|3|18|30235.14|0.00|0.03|A|F|1993-01-20|1993-01-28|1993-02-02|COLLECT COD|MAIL|y final packages nod \n1217|59148|4159|1|45|49821.30|0.07|0.02|A|F|1992-07-01|1992-06-23|1992-07-06|COLLECT COD|AIR|riously close ideas\n1218|139055|1569|1|16|17504.80|0.04|0.07|A|F|1994-06-26|1994-08-07|1994-06-30|TAKE BACK RETURN|FOB|ven realms be\n1218|93393|5903|2|41|56841.99|0.06|0.06|R|F|1994-08-04|1994-08-05|1994-08-11|TAKE BACK RETURN|SHIP|dolphins. theodolites beyond th\n1218|47852|5365|3|44|79193.40|0.07|0.06|A|F|1994-10-05|1994-09-03|1994-10-30|COLLECT COD|TRUCK|thely ironic accounts wake slyly\n1218|41364|3869|4|1|1305.36|0.01|0.08|R|F|1994-09-15|1994-09-07|1994-10-03|COLLECT COD|TRUCK|press furio\n1219|131882|6909|1|6|11483.28|0.08|0.04|N|O|1995-11-13|1995-12-24|1995-11-18|NONE|MAIL|pecial, ironic requ\n1219|128599|1112|2|4|6510.36|0.01|0.04|N|O|1995-11-24|1995-11-22|1995-12-07|TAKE BACK RETURN|SHIP|lly quick requests. blithely even h\n1220|168916|6465|1|25|49622.75|0.10|0.03|N|O|1996-10-15|1996-11-07|1996-11-06|COLLECT COD|REG AIR| regular orbi\n1220|159710|4741|2|36|63709.56|0.01|0.02|N|O|1996-12-10|1996-11-14|1997-01-07|COLLECT COD|SHIP|ar packages. blithely final acc\n1220|36807|6808|3|3|5231.40|0.08|0.06|N|O|1996-09-06|1996-11-03|1996-09-10|COLLECT COD|REG AIR| final theodolites. blithely silent \n1220|5144|145|4|36|37769.04|0.07|0.03|N|O|1996-12-12|1996-10-03|1996-12-15|TAKE BACK RETURN|TRUCK|unusual, silent pinto beans aga\n1220|48992|4001|5|25|48524.75|0.03|0.08|N|O|1996-09-11|1996-10-09|1996-09-25|DELIVER IN PERSON|RAIL|packages affi\n1221|80049|2558|1|43|44248.72|0.05|0.05|R|F|1992-06-22|1992-07-15|1992-07-20|DELIVER IN PERSON|FOB|y slyly above the slyly unusual ideas\n1221|169470|9471|2|12|18473.64|0.00|0.08|R|F|1992-08-07|1992-06-24|1992-08-13|COLLECT COD|AIR|yly ironic \n1221|68229|3242|3|3|3591.66|0.10|0.08|R|F|1992-07-01|1992-06-04|1992-07-27|COLLECT COD|TRUCK|ing to the fluffily\n1221|119731|7265|4|41|71779.93|0.06|0.02|A|F|1992-04-28|1992-07-02|1992-05-19|NONE|RAIL|ns. bold deposit\n1221|107651|162|5|13|21562.45|0.10|0.00|R|F|1992-08-01|1992-06-29|1992-08-27|TAKE BACK RETURN|AIR|ajole furiously. blithely expres\n1221|84249|4250|6|7|8632.68|0.08|0.06|A|F|1992-06-27|1992-06-16|1992-07-23|TAKE BACK RETURN|RAIL|xpress accounts \n1222|71266|8788|1|12|14847.12|0.09|0.02|A|F|1993-02-12|1993-03-14|1993-03-12|TAKE BACK RETURN|RAIL|s print permanently unusual packages. \n1222|158637|1153|2|12|20347.56|0.08|0.01|A|F|1993-05-05|1993-03-27|1993-05-18|TAKE BACK RETURN|REG AIR| furiously bold instructions\n1222|7554|55|3|26|38000.30|0.02|0.08|R|F|1993-02-13|1993-03-20|1993-02-22|TAKE BACK RETURN|MAIL|, even accounts are ironic\n1223|99460|9461|1|28|40864.88|0.10|0.06|N|O|1996-08-07|1996-07-24|1996-08-13|TAKE BACK RETURN|MAIL| quickly ironic requests. furious\n1248|163061|3062|1|45|50582.70|0.00|0.08|A|F|1992-04-17|1992-03-31|1992-05-13|NONE|RAIL|ter the pending pl\n1248|150275|2791|2|37|49034.99|0.06|0.06|R|F|1992-01-26|1992-02-05|1992-02-13|COLLECT COD|TRUCK|. final requests integrate quickly. blit\n1248|55368|2884|3|26|34407.36|0.09|0.06|A|F|1992-01-16|1992-03-01|1992-02-06|TAKE BACK RETURN|AIR| ironic dependen\n1248|155169|5170|4|49|59983.84|0.02|0.01|A|F|1992-04-24|1992-02-18|1992-05-03|TAKE BACK RETURN|AIR|beans run quickly according to the carefu\n1248|121465|3978|5|20|29729.20|0.08|0.00|A|F|1992-03-12|1992-03-23|1992-04-07|TAKE BACK RETURN|AIR|nal foxes cajole carefully slyl\n1248|61457|6470|6|30|42553.50|0.10|0.01|R|F|1992-02-01|1992-03-24|1992-02-08|TAKE BACK RETURN|MAIL|fily special foxes kindle am\n1249|58157|3168|1|49|54642.35|0.07|0.05|A|F|1994-03-03|1994-02-28|1994-03-08|NONE|RAIL|ffily express theodo\n1250|1035|1036|1|15|14040.45|0.10|0.06|A|F|1992-11-05|1992-12-17|1992-12-03|TAKE BACK RETURN|SHIP| regular, i\n1251|3325|3326|1|37|45447.84|0.08|0.08|N|O|1997-12-21|1998-01-12|1997-12-26|COLLECT COD|AIR|. furiously\n1251|77902|7903|2|36|67676.40|0.07|0.04|N|O|1997-11-29|1998-01-07|1997-12-03|TAKE BACK RETURN|RAIL|y ironic Tiresias are slyly furio\n1251|98778|6306|3|37|65740.49|0.09|0.02|N|O|1998-01-11|1997-12-01|1998-01-23|DELIVER IN PERSON|RAIL|finally bold requests\n1251|149818|7361|4|7|13074.67|0.07|0.00|N|O|1998-01-08|1997-12-27|1998-01-18|COLLECT COD|MAIL|riously pe\n1251|187484|2521|5|1|1571.48|0.02|0.03|N|O|1997-12-08|1998-01-06|1998-01-01|DELIVER IN PERSON|REG AIR| use quickly final packages. iron\n1252|86435|8944|1|13|18478.59|0.10|0.01|N|O|1997-09-07|1997-09-12|1997-10-01|COLLECT COD|REG AIR|sts dazzle\n1252|110885|5908|2|27|51188.76|0.00|0.08|N|O|1997-10-22|1997-10-10|1997-11-10|TAKE BACK RETURN|REG AIR|packages hag\n1252|39144|9145|3|19|20579.66|0.07|0.02|N|O|1997-10-13|1997-10-23|1997-10-18|NONE|AIR|ts wake carefully-- packages sleep. quick \n1252|91193|3703|4|11|13026.09|0.10|0.01|N|O|1997-10-16|1997-09-22|1997-10-28|COLLECT COD|AIR|s are. slyly final requests among the\n1252|78748|8749|5|26|44895.24|0.05|0.05|N|O|1997-08-05|1997-10-24|1997-08-07|DELIVER IN PERSON|SHIP|onic pinto beans haggle furiously \n1253|179988|7540|1|14|28951.72|0.00|0.06|R|F|1993-04-03|1993-04-16|1993-04-27|TAKE BACK RETURN|MAIL|lar foxes sleep furiously final, final pack\n1253|53958|8969|2|13|24855.35|0.01|0.06|A|F|1993-03-05|1993-04-26|1993-03-08|DELIVER IN PERSON|FOB|al packages\n1253|69375|9376|3|22|29576.14|0.05|0.06|A|F|1993-02-23|1993-04-06|1993-03-07|TAKE BACK RETURN|SHIP|telets cajole alongside of the final reques\n1253|175589|624|4|23|38285.34|0.09|0.02|R|F|1993-04-18|1993-04-18|1993-05-07|COLLECT COD|FOB| the slyly silent re\n1253|113918|6430|5|19|36706.29|0.05|0.05|A|F|1993-04-01|1993-04-22|1993-04-14|TAKE BACK RETURN|AIR|al pinto bea\n1254|192385|4905|1|6|8864.28|0.08|0.01|N|O|1996-02-02|1996-03-21|1996-02-29|NONE|REG AIR|lithely even deposits eat!\n1254|199414|4453|2|47|71130.27|0.05|0.06|N|O|1996-03-07|1996-02-20|1996-04-05|COLLECT COD|MAIL| platelets cajol\n1254|134485|4486|3|35|53181.80|0.05|0.06|N|O|1996-04-08|1996-02-29|1996-04-18|DELIVER IN PERSON|FOB|ckages boost. furious warhorses cajole\n1255|191801|4321|1|12|22713.60|0.00|0.02|A|F|1994-08-17|1994-06-29|1994-09-04|TAKE BACK RETURN|REG AIR| regular, express accounts are \n1255|193712|1270|2|46|83062.66|0.07|0.05|R|F|1994-07-06|1994-07-14|1994-08-05|NONE|MAIL|ons nag qui\n1280|128696|3721|1|17|29319.73|0.01|0.01|A|F|1993-02-04|1993-04-10|1993-02-07|NONE|FOB|ructions integrate across the th\n1280|188305|5860|2|6|8359.80|0.05|0.06|R|F|1993-03-30|1993-02-16|1993-04-18|DELIVER IN PERSON|AIR|gular deposits \n1280|32176|7183|3|13|14406.21|0.03|0.02|R|F|1993-03-06|1993-03-11|1993-03-18|DELIVER IN PERSON|TRUCK|blithely final accounts use evenly \n1280|174752|2304|4|5|9133.75|0.06|0.03|R|F|1993-02-03|1993-02-11|1993-02-23|DELIVER IN PERSON|AIR|beans haggle. quickly bold instructions h\n1280|51971|4477|5|24|46151.28|0.07|0.02|R|F|1993-03-20|1993-03-01|1993-04-09|COLLECT COD|RAIL|y pending orbits boost after the slyly\n1280|65229|242|6|9|10747.98|0.00|0.05|R|F|1993-04-18|1993-03-28|1993-05-04|DELIVER IN PERSON|FOB|usual accou\n1280|91590|9118|7|19|30050.21|0.02|0.06|A|F|1993-02-07|1993-02-28|1993-02-12|NONE|TRUCK|lyly along the furiously regular \n1281|137813|327|1|33|61076.73|0.07|0.08|R|F|1995-02-01|1995-01-18|1995-03-03|NONE|REG AIR|dencies. thinly final pinto beans wake\n1281|6372|1373|2|37|47299.69|0.08|0.03|A|F|1995-03-19|1995-02-02|1995-03-27|NONE|AIR|ounts detect\n1281|93625|8644|3|2|3237.24|0.05|0.06|A|F|1994-12-27|1995-01-26|1995-01-21|TAKE BACK RETURN|FOB|ly unusual requests. final reques\n1281|153005|5521|4|38|40204.00|0.04|0.06|R|F|1995-03-28|1995-01-11|1995-04-14|TAKE BACK RETURN|MAIL| ideas-- blithely regular\n1281|151357|3873|5|13|18308.55|0.03|0.07|A|F|1995-02-06|1995-02-13|1995-02-18|DELIVER IN PERSON|TRUCK|fully final platelets wa\n1281|49270|6783|6|4|4877.08|0.07|0.04|R|F|1995-03-15|1995-02-21|1995-03-20|NONE|SHIP|ggle against the even requests. requests \n1281|77886|5408|7|43|80146.84|0.10|0.02|R|F|1995-01-28|1995-02-08|1995-02-10|DELIVER IN PERSON|AIR|final accounts. final packages slee\n1282|22493|2494|1|14|19816.86|0.04|0.02|R|F|1992-06-29|1992-04-05|1992-07-21|TAKE BACK RETURN|REG AIR|ecial deposit\n1282|29129|4134|2|10|10581.20|0.09|0.06|R|F|1992-04-10|1992-04-16|1992-05-01|DELIVER IN PERSON|SHIP|r theodolite\n1282|159521|9522|3|19|30029.88|0.01|0.03|R|F|1992-05-07|1992-04-07|1992-05-13|NONE|RAIL|ts x-ray across the furi\n1282|58316|8317|4|19|24211.89|0.00|0.05|A|F|1992-06-20|1992-04-17|1992-07-05|DELIVER IN PERSON|REG AIR|nto beans. carefully close theodo\n1283|92682|210|1|47|78709.96|0.05|0.03|N|O|1996-10-21|1996-10-29|1996-11-12|DELIVER IN PERSON|TRUCK|even instructions boost slyly blithely \n1283|105334|355|2|1|1339.33|0.00|0.08|N|O|1996-10-07|1996-10-12|1996-10-08|NONE|RAIL|d the sauternes. slyly ev\n1283|137074|4614|3|18|19999.26|0.02|0.01|N|O|1996-10-14|1996-11-07|1996-10-22|DELIVER IN PERSON|AIR|equests use along the fluff\n1283|191999|4519|4|40|83639.60|0.07|0.03|N|O|1996-11-09|1996-11-23|1996-11-28|NONE|MAIL|riously. even, ironic instructions after\n1283|123473|5986|5|43|64348.21|0.01|0.04|N|O|1996-09-29|1996-11-19|1996-10-26|TAKE BACK RETURN|RAIL|requests sleep slyly about the \n1283|7533|5034|6|30|43215.90|0.06|0.07|N|O|1996-11-22|1996-11-22|1996-12-15|COLLECT COD|TRUCK|t the fluffily\n1283|196227|6228|7|21|27787.62|0.04|0.03|N|O|1996-09-12|1996-10-02|1996-10-12|NONE|REG AIR|fully regular \n1284|177427|2462|1|49|73716.58|0.00|0.06|N|O|1996-04-11|1996-03-04|1996-04-16|NONE|MAIL|lar packages. special packages ac\n1284|5304|5305|2|4|4837.20|0.07|0.06|N|O|1996-02-29|1996-02-11|1996-03-01|TAKE BACK RETURN|TRUCK| regular asymptotes. \n1284|132592|2593|3|39|63359.01|0.08|0.00|N|O|1996-01-11|1996-02-07|1996-02-05|COLLECT COD|MAIL|even accoun\n1284|58252|8253|4|1|1210.25|0.01|0.07|N|O|1996-04-28|1996-04-02|1996-05-08|DELIVER IN PERSON|SHIP|al packages use carefully express de\n1284|33328|3329|5|9|11351.88|0.05|0.06|N|O|1996-03-03|1996-03-19|1996-04-01|DELIVER IN PERSON|REG AIR|after the pending\n1285|21548|1549|1|12|17634.48|0.00|0.06|A|F|1992-06-21|1992-08-16|1992-07-12|COLLECT COD|MAIL|ss foxes. blithe theodolites cajole slyly\n1285|142274|4789|2|45|59232.15|0.01|0.02|R|F|1992-09-05|1992-08-08|1992-10-02|COLLECT COD|REG AIR| special requests haggle blithely.\n1285|188148|5703|3|4|4944.56|0.09|0.06|A|F|1992-07-20|1992-08-17|1992-07-26|DELIVER IN PERSON|FOB|l packages sleep slyly quiet i\n1285|187439|9958|4|39|59530.77|0.05|0.01|A|F|1992-09-15|1992-08-05|1992-10-05|DELIVER IN PERSON|TRUCK|uctions. car\n1285|83541|8558|5|33|50309.82|0.00|0.08|R|F|1992-09-08|1992-08-25|1992-09-16|NONE|SHIP|ites affix\n1286|177013|7014|1|49|53410.49|0.08|0.01|R|F|1993-06-24|1993-08-12|1993-06-26|DELIVER IN PERSON|SHIP|gged accoun\n1286|48230|735|2|48|56555.04|0.01|0.04|A|F|1993-07-11|1993-07-11|1993-08-01|COLLECT COD|TRUCK|unts alongs\n1286|188576|3613|3|11|18310.27|0.03|0.04|R|F|1993-08-08|1993-07-30|1993-09-05|DELIVER IN PERSON|FOB| slyly even packages. requ\n1286|183767|3768|4|37|68478.12|0.00|0.02|R|F|1993-05-27|1993-07-11|1993-06-01|COLLECT COD|SHIP|lyly ironic pinto beans cajole furiously s\n1286|164048|1597|5|14|15568.56|0.00|0.01|R|F|1993-05-23|1993-08-09|1993-06-01|NONE|REG AIR|blithely bo\n1286|145926|3469|6|41|80848.72|0.04|0.05|R|F|1993-08-02|1993-08-06|1993-08-07|TAKE BACK RETURN|FOB| the furiously expre\n1287|173172|8207|1|35|43580.95|0.09|0.06|A|F|1994-09-07|1994-09-12|1994-09-30|TAKE BACK RETURN|FOB|s wake unusual grou\n1287|94556|9575|2|10|15505.50|0.08|0.03|R|F|1994-07-08|1994-08-28|1994-07-10|TAKE BACK RETURN|RAIL|thely alongside of the unusual, ironic pa\n1287|278|279|3|30|35348.10|0.00|0.07|R|F|1994-07-12|1994-09-23|1994-08-07|NONE|RAIL|ar packages. even, even\n1287|61652|9171|4|10|16136.50|0.01|0.05|A|F|1994-09-03|1994-08-12|1994-09-16|TAKE BACK RETURN|REG AIR|ding, regular accounts\n1287|178903|3938|5|21|41619.90|0.06|0.02|A|F|1994-10-06|1994-09-25|1994-10-16|TAKE BACK RETURN|TRUCK|y quickly bold theodoli\n1287|20481|5486|6|26|36438.48|0.03|0.08|R|F|1994-10-03|1994-09-27|1994-10-30|DELIVER IN PERSON|RAIL|egular foxes. theodolites nag along t\n1312|80689|5706|1|9|15027.12|0.04|0.08|R|F|1994-07-19|1994-06-29|1994-07-24|TAKE BACK RETURN|MAIL|. furiously \n1312|135990|5991|2|28|56727.72|0.06|0.06|A|F|1994-09-09|1994-08-01|1994-10-02|TAKE BACK RETURN|FOB|uriously final frays should use quick\n1312|172935|487|3|18|36142.74|0.03|0.07|A|F|1994-09-13|1994-07-08|1994-09-22|TAKE BACK RETURN|MAIL|. slyly ironic\n1313|51361|8877|1|48|62993.28|0.01|0.03|A|F|1994-12-20|1994-10-29|1995-01-07|COLLECT COD|MAIL|s are quick\n1314|197722|242|1|5|9098.60|0.03|0.01|A|F|1994-05-26|1994-08-06|1994-05-31|TAKE BACK RETURN|AIR|equests nag across the furious\n1314|109303|4324|2|39|51179.70|0.01|0.03|R|F|1994-08-09|1994-06-14|1994-08-31|TAKE BACK RETURN|TRUCK| unusual accounts slee\n1314|40911|912|3|11|20371.01|0.01|0.04|A|F|1994-05-16|1994-07-30|1994-05-31|COLLECT COD|REG AIR|tegrate furious\n1315|95509|8019|1|27|40621.50|0.01|0.03|N|O|1998-07-04|1998-06-13|1998-07-28|NONE|SHIP|latelets. fluffily ironic account\n1315|15742|3246|2|15|24866.10|0.05|0.01|N|O|1998-07-12|1998-06-10|1998-08-07|COLLECT COD|AIR|. foxes integrate carefully special\n1315|167190|4739|3|25|31429.75|0.01|0.08|N|O|1998-06-26|1998-06-10|1998-07-06|TAKE BACK RETURN|FOB|lites. unusual foxes affi\n1315|160919|8468|4|19|37618.29|0.02|0.05|N|O|1998-07-05|1998-05-23|1998-08-04|TAKE BACK RETURN|SHIP|nal, regular warhorses about the fu\n1315|158532|1048|5|32|50896.96|0.10|0.05|N|O|1998-03-30|1998-06-12|1998-04-25|NONE|SHIP|neath the final p\n1316|126429|1454|1|46|66949.32|0.05|0.04|A|F|1994-01-13|1994-01-24|1994-02-03|COLLECT COD|TRUCK|ges haggle of the\n1316|78158|666|2|15|17042.25|0.02|0.01|R|F|1994-03-12|1994-03-02|1994-03-14|COLLECT COD|FOB|se. furiously final depo\n1316|197244|7245|3|33|44260.92|0.10|0.06|R|F|1994-03-31|1994-01-23|1994-04-20|TAKE BACK RETURN|AIR|manently; blithely special deposits\n1316|65047|60|4|15|15180.60|0.00|0.06|R|F|1993-12-17|1994-02-04|1993-12-20|NONE|RAIL|fully express dugouts. furiously silent ide\n1316|40746|747|5|40|67469.60|0.01|0.03|R|F|1994-02-04|1994-02-09|1994-02-27|NONE|REG AIR|l dugouts. co\n1316|3390|5891|6|7|9053.73|0.05|0.04|A|F|1993-12-09|1994-01-12|1993-12-30|TAKE BACK RETURN|MAIL|. furiously even accounts a\n1316|162096|9645|7|8|9264.72|0.10|0.04|A|F|1994-03-26|1994-02-08|1994-04-19|NONE|SHIP|packages against the express requests wa\n1317|133799|3800|1|34|62314.86|0.08|0.04|N|O|1995-08-13|1995-08-08|1995-09-10|COLLECT COD|RAIL|deposits boost thinly blithely final id\n1317|159586|7132|2|7|11519.06|0.05|0.01|A|F|1995-06-08|1995-08-03|1995-06-16|TAKE BACK RETURN|SHIP| pinto beans according to the final, pend\n1317|157179|7180|3|26|32140.42|0.01|0.02|N|O|1995-07-13|1995-06-26|1995-08-06|COLLECT COD|RAIL|leep along th\n1317|105156|2687|4|35|40640.25|0.05|0.02|N|O|1995-07-16|1995-07-07|1995-07-22|TAKE BACK RETURN|FOB|r packages impress blithely car\n1317|149415|6958|5|36|52718.76|0.02|0.00|N|O|1995-09-03|1995-07-06|1995-09-04|DELIVER IN PERSON|AIR| deposits. quic\n1318|113682|1216|1|24|40696.32|0.08|0.06|N|O|1998-09-27|1998-09-15|1998-10-12|TAKE BACK RETURN|AIR|ual, unusual packages. fluffy, iro\n1318|45822|8327|2|26|45963.32|0.01|0.03|N|O|1998-09-26|1998-08-09|1998-10-07|DELIVER IN PERSON|FOB|ly. regular, u\n1318|128344|857|3|31|42542.54|0.01|0.04|N|O|1998-08-25|1998-07-31|1998-08-31|COLLECT COD|AIR|ve the carefully expr\n1319|60918|5931|1|21|39457.11|0.03|0.04|N|O|1996-10-05|1996-12-02|1996-10-28|COLLECT COD|FOB|s: carefully express \n1319|36685|1692|2|12|19460.16|0.09|0.05|N|O|1996-11-05|1996-12-12|1996-11-29|DELIVER IN PERSON|TRUCK|packages integrate furiously. expres\n1344|140001|5030|1|15|15615.00|0.10|0.07|A|F|1992-06-22|1992-06-24|1992-06-23|TAKE BACK RETURN|MAIL|rding to the blithely ironic theodolite\n1344|189888|4925|2|29|57358.52|0.09|0.00|A|F|1992-07-17|1992-06-07|1992-07-21|NONE|REG AIR|ffily quiet foxes wake blithely. slyly \n1345|197110|7111|1|49|59148.39|0.08|0.00|A|F|1992-12-27|1993-01-23|1993-01-06|NONE|FOB|sly. furiously final accounts are blithely \n1345|11261|6264|2|37|43373.62|0.10|0.07|A|F|1992-11-27|1992-12-11|1992-12-07|COLLECT COD|FOB|e slyly express requests. ironic accounts c\n1345|56666|6667|3|31|50302.46|0.08|0.07|R|F|1992-12-02|1992-12-29|1992-12-14|COLLECT COD|REG AIR|. slyly silent accounts sublat\n1346|159096|1612|1|29|33497.61|0.07|0.05|A|F|1992-08-18|1992-09-15|1992-09-17|TAKE BACK RETURN|REG AIR|the pinto \n1346|124499|4500|2|48|73127.52|0.06|0.03|A|F|1992-09-28|1992-07-22|1992-10-13|TAKE BACK RETURN|REG AIR| along the carefully spec\n1346|53819|3820|3|13|23046.53|0.10|0.04|A|F|1992-07-22|1992-08-10|1992-08-06|NONE|SHIP|arefully brave deposits into the slyly iro\n1346|123555|3556|4|6|9471.30|0.02|0.02|R|F|1992-09-13|1992-07-21|1992-09-27|TAKE BACK RETURN|AIR|inst the furiously final theodolites. caref\n1346|186043|6044|5|30|33871.20|0.01|0.07|R|F|1992-10-01|1992-07-22|1992-10-24|NONE|SHIP| nag blithely. unusual, ru\n1346|15002|2506|6|45|41265.00|0.02|0.04|A|F|1992-09-11|1992-08-06|1992-09-12|COLLECT COD|FOB|press deposits.\n1347|80130|7655|1|45|49955.85|0.02|0.05|N|O|1997-08-24|1997-09-03|1997-09-08|COLLECT COD|AIR|ages wake around t\n1347|142381|7410|2|34|48394.92|0.07|0.04|N|O|1997-06-25|1997-09-08|1997-07-24|COLLECT COD|FOB|r packages. f\n1347|184871|4872|3|23|44985.01|0.03|0.04|N|O|1997-07-31|1997-08-25|1997-08-21|COLLECT COD|SHIP|ronic pinto beans. express reques\n1347|112077|4589|4|28|30493.96|0.01|0.00|N|O|1997-07-30|1997-07-22|1997-08-18|TAKE BACK RETURN|FOB|foxes after the blithely special i\n1347|64173|4174|5|9|10234.53|0.01|0.03|N|O|1997-08-28|1997-09-16|1997-09-26|DELIVER IN PERSON|AIR| detect blithely above the fina\n1347|152357|7388|6|21|29596.35|0.06|0.04|N|O|1997-10-10|1997-08-16|1997-11-02|NONE|FOB|g pinto beans affix car\n1347|50283|7799|7|10|12332.80|0.02|0.07|N|O|1997-07-04|1997-07-23|1997-07-05|DELIVER IN PERSON|SHIP|y ironic pin\n1348|94296|6806|1|13|16773.77|0.01|0.01|N|O|1998-04-28|1998-06-05|1998-05-12|TAKE BACK RETURN|SHIP| blithely r\n1348|21908|9415|2|41|75025.90|0.07|0.03|N|O|1998-05-02|1998-05-26|1998-05-09|COLLECT COD|RAIL|kages. platelets about the ca\n1348|198221|8222|3|40|52768.80|0.07|0.05|N|O|1998-08-14|1998-07-10|1998-08-27|COLLECT COD|AIR|fter the regu\n1348|97544|2563|4|2|3083.08|0.01|0.04|N|O|1998-05-30|1998-06-20|1998-06-05|COLLECT COD|MAIL|lly final packages use fluffily express ac\n1349|180575|3094|1|1|1655.57|0.06|0.03|N|O|1998-01-07|1998-01-14|1998-02-03|COLLECT COD|REG AIR| express inst\n1349|117686|198|2|45|76665.60|0.03|0.02|N|O|1997-12-24|1998-01-17|1997-12-28|NONE|AIR| ironic, unusual deposits wake carefu\n1350|53683|8694|1|21|34370.28|0.04|0.04|A|F|1993-12-17|1993-10-17|1993-12-25|COLLECT COD|REG AIR|lyly above the evenly \n1350|43604|3605|2|32|49523.20|0.03|0.00|R|F|1993-11-18|1993-09-30|1993-12-16|COLLECT COD|MAIL|ic, final \n1351|107227|7228|1|25|30855.50|0.06|0.04|N|O|1998-06-02|1998-05-25|1998-06-22|COLLECT COD|SHIP|iously regul\n1376|168528|1045|1|22|35123.44|0.01|0.03|N|O|1997-08-05|1997-07-08|1997-09-03|NONE|REG AIR|inst the final, pending \n1377|153593|1139|1|5|8232.95|0.06|0.05|N|O|1998-05-06|1998-07-08|1998-06-01|TAKE BACK RETURN|FOB| final, final grouches. accoun\n1377|32599|5103|2|3|4594.77|0.10|0.04|N|O|1998-04-30|1998-07-02|1998-05-14|DELIVER IN PERSON|REG AIR|yly enticing requ\n1377|83378|8395|3|26|35395.62|0.07|0.07|N|O|1998-05-28|1998-06-11|1998-06-25|COLLECT COD|SHIP|egular deposits. quickly regular acco\n1377|120024|7561|4|39|40716.78|0.00|0.03|N|O|1998-07-27|1998-07-18|1998-08-13|DELIVER IN PERSON|SHIP|e ironic, regular requests. carefully \n1377|32294|4798|5|19|23299.51|0.10|0.00|N|O|1998-06-20|1998-06-27|1998-07-20|NONE|AIR|ught to are bold foxes\n1377|153755|1301|6|17|30748.75|0.03|0.04|N|O|1998-06-19|1998-07-20|1998-07-14|NONE|REG AIR|s must have to mold b\n1378|196057|1096|1|34|39203.70|0.09|0.07|N|O|1996-07-08|1996-04-23|1996-07-09|COLLECT COD|RAIL|le furiously slyly final accounts. careful\n1378|123358|5871|2|18|24864.30|0.05|0.02|N|O|1996-06-19|1996-05-16|1996-06-21|DELIVER IN PERSON|RAIL| theodolites. i\n1378|72873|2874|3|11|20304.57|0.10|0.03|N|O|1996-06-07|1996-05-09|1996-07-05|COLLECT COD|TRUCK| blithely express hoc\n1378|170689|690|4|12|21116.16|0.02|0.06|N|O|1996-06-16|1996-05-23|1996-07-09|COLLECT COD|SHIP|notornis. b\n1378|155233|5234|5|9|11594.07|0.06|0.05|N|O|1996-04-20|1996-04-13|1996-05-09|COLLECT COD|REG AIR|e carefully. carefully iron\n1378|193453|5973|6|29|44847.05|0.05|0.05|N|O|1996-04-15|1996-04-23|1996-05-14|NONE|REG AIR|ual packages are furiously blith\n1379|72894|5402|1|13|24269.57|0.04|0.01|N|O|1998-06-08|1998-07-13|1998-06-16|NONE|AIR|ully across the furiously iron\n1379|117107|9619|2|50|56205.00|0.07|0.08|N|O|1998-08-31|1998-07-13|1998-09-02|TAKE BACK RETURN|FOB|olphins. ca\n1379|12350|4852|3|24|30296.40|0.05|0.02|N|O|1998-07-06|1998-07-09|1998-07-29|DELIVER IN PERSON|MAIL|ages cajole carefully idly express re\n1380|148197|3226|1|6|7471.14|0.00|0.04|N|O|1996-08-06|1996-10-01|1996-08-14|NONE|RAIL|e foxes. slyly specia\n1380|140792|5821|2|40|73311.60|0.02|0.02|N|O|1996-10-01|1996-08-14|1996-10-20|COLLECT COD|RAIL|ly final frets. ironic,\n1380|77746|7747|3|15|25856.10|0.05|0.02|N|O|1996-07-14|1996-08-12|1996-08-03|NONE|FOB|riously ironic foxes aff\n1380|60776|3283|4|33|57313.41|0.04|0.07|N|O|1996-08-23|1996-10-01|1996-09-18|TAKE BACK RETURN|SHIP|e ironic, even excuses haggle \n1381|143074|5589|1|47|52502.29|0.08|0.04|N|O|1998-09-22|1998-08-12|1998-10-12|DELIVER IN PERSON|AIR|ly ironic deposits\n1381|33128|5632|2|12|12733.44|0.07|0.08|N|O|1998-08-13|1998-08-12|1998-08-28|TAKE BACK RETURN|AIR| furiously regular package\n1382|161373|1374|1|18|25818.66|0.08|0.03|R|F|1993-08-30|1993-10-19|1993-09-03|DELIVER IN PERSON|AIR|hely regular deposits. fluffy s\n1382|180829|830|2|29|55384.78|0.08|0.04|A|F|1993-10-08|1993-11-11|1993-10-10|COLLECT COD|FOB| haggle: closely even asymptot\n1382|177866|2901|3|43|83585.98|0.10|0.04|A|F|1993-09-02|1993-10-06|1993-09-15|DELIVER IN PERSON|AIR|ress deposits. slyly ironic foxes are blit\n1382|180589|8144|4|11|18365.38|0.04|0.04|R|F|1993-09-17|1993-09-29|1993-09-21|NONE|SHIP|furiously unusual packages play quickly \n1382|156162|6163|5|31|37762.96|0.07|0.03|R|F|1993-10-26|1993-10-15|1993-11-09|TAKE BACK RETURN|FOB|hely regular dependencies. f\n1382|9855|4856|6|38|67064.30|0.07|0.07|R|F|1993-11-17|1993-09-28|1993-11-20|COLLECT COD|SHIP|ake pending pinto beans. s\n1382|22788|2789|7|5|8553.90|0.07|0.01|R|F|1993-10-02|1993-09-29|1993-10-12|DELIVER IN PERSON|REG AIR|ter the carefully final excuses. blit\n1383|192894|452|1|14|27816.46|0.07|0.06|A|F|1993-08-25|1993-07-09|1993-09-12|DELIVER IN PERSON|RAIL|ole carefully silent requests. car\n1383|160122|2639|2|19|22460.28|0.06|0.04|R|F|1993-05-24|1993-07-07|1993-06-14|NONE|AIR|lyly unusual accounts sle\n1408|147902|5445|1|29|56547.10|0.03|0.04|N|O|1998-03-12|1998-02-14|1998-03-17|COLLECT COD|MAIL|en accounts grow furiousl\n1408|172326|7361|2|7|9788.24|0.05|0.06|N|O|1998-01-14|1998-03-21|1998-01-29|COLLECT COD|AIR|fully final instructions. theodolites ca\n1408|75549|8057|3|11|16769.94|0.00|0.03|N|O|1998-04-04|1998-01-29|1998-04-18|NONE|REG AIR|y even accounts thrash care\n1408|147092|9607|4|20|22781.80|0.06|0.00|N|O|1998-04-21|1998-01-25|1998-05-12|DELIVER IN PERSON|TRUCK| blithely fluffi\n1408|169688|9689|5|41|72064.88|0.02|0.06|N|O|1998-02-25|1998-02-03|1998-03-13|COLLECT COD|REG AIR|ep along the fina\n1408|133484|1024|6|42|63734.16|0.05|0.08|N|O|1998-01-30|1998-02-07|1998-02-18|TAKE BACK RETURN|REG AIR|even packages. even accounts cajole\n1408|54519|4520|7|26|38311.26|0.00|0.00|N|O|1998-03-19|1998-03-14|1998-04-01|COLLECT COD|RAIL|ic foxes ca\n1409|98868|1378|1|23|42937.78|0.01|0.03|A|F|1993-04-18|1993-02-25|1993-05-06|DELIVER IN PERSON|FOB|ions. slyly ironic packages wake quick\n1409|64412|9425|2|36|49550.76|0.09|0.02|A|F|1993-01-27|1993-01-31|1993-02-07|COLLECT COD|FOB|ncies sleep carefully r\n1409|159012|9013|3|17|18207.17|0.07|0.00|R|F|1993-04-15|1993-03-01|1993-04-29|NONE|REG AIR|pending accounts poach. care\n1410|120271|5296|1|15|19369.05|0.06|0.05|N|O|1997-05-25|1997-07-08|1997-06-15|NONE|SHIP| bold packages are fluf\n1410|178206|724|2|18|23115.60|0.03|0.00|N|O|1997-06-03|1997-05-17|1997-06-07|TAKE BACK RETURN|RAIL|gle furiously fluffily regular requests\n1410|108109|3130|3|37|41332.70|0.02|0.01|N|O|1997-04-17|1997-06-18|1997-04-19|COLLECT COD|TRUCK|to beans b\n1410|187510|5065|4|22|35145.22|0.10|0.00|N|O|1997-07-31|1997-05-17|1997-08-19|TAKE BACK RETURN|RAIL|gular account\n1410|65802|3321|5|25|44195.00|0.09|0.02|N|O|1997-05-07|1997-07-10|1997-05-16|NONE|REG AIR|unts haggle against the furiously fina\n1411|16321|3825|1|9|11135.88|0.06|0.04|A|F|1995-03-08|1995-03-04|1995-03-11|DELIVER IN PERSON|AIR|accounts. furiou\n1411|106434|6435|2|26|37451.18|0.02|0.02|A|F|1995-04-12|1995-01-24|1995-05-03|TAKE BACK RETURN|TRUCK|c packages. \n1411|26013|1018|3|37|34743.37|0.00|0.06|A|F|1995-02-27|1995-03-02|1995-03-24|NONE|MAIL|d excuses. furiously final pear\n1411|199946|4985|4|20|40918.80|0.01|0.03|R|F|1995-04-06|1995-03-16|1995-04-17|COLLECT COD|FOB|s against the\n1411|82816|7833|5|46|82745.26|0.08|0.05|A|F|1995-04-03|1995-01-20|1995-04-05|DELIVER IN PERSON|REG AIR|ly daring instructions\n1411|76148|1163|6|30|33724.20|0.09|0.04|A|F|1995-01-12|1995-02-01|1995-01-23|DELIVER IN PERSON|MAIL|ious foxes wake courts. caref\n1412|57200|2211|1|37|42816.40|0.06|0.01|A|F|1993-04-10|1993-04-19|1993-04-12|DELIVER IN PERSON|RAIL|hely express excuses are \n1412|155021|52|2|20|21520.40|0.10|0.05|A|F|1993-07-04|1993-05-18|1993-07-22|DELIVER IN PERSON|REG AIR|odolites sleep ironically\n1412|22620|7625|3|2|3085.24|0.10|0.07|R|F|1993-04-01|1993-05-03|1993-04-12|DELIVER IN PERSON|REG AIR|s among the requests are a\n1412|166155|6156|4|11|13432.65|0.05|0.07|R|F|1993-05-27|1993-05-30|1993-06-07|DELIVER IN PERSON|MAIL|en packages. regular packages dete\n1412|157647|163|5|11|18751.04|0.08|0.06|A|F|1993-03-30|1993-05-25|1993-04-21|NONE|FOB|se slyly. special, unusual accounts nag bl\n1413|177936|7937|1|18|36250.74|0.08|0.05|N|O|1997-10-11|1997-08-17|1997-10-25|NONE|FOB|yly bold packages haggle quickly acr\n1413|164292|1841|2|49|66458.21|0.07|0.06|N|O|1997-08-28|1997-08-23|1997-09-12|DELIVER IN PERSON|MAIL|nstructions br\n1413|41545|4050|3|6|8919.24|0.04|0.02|N|O|1997-09-07|1997-07-30|1997-09-21|TAKE BACK RETURN|MAIL|lithely excuses. f\n1414|37241|4751|1|39|45951.36|0.10|0.03|N|O|1995-09-22|1995-09-30|1995-10-07|NONE|MAIL|quickly aro\n1414|106423|6424|2|4|5717.68|0.02|0.05|N|O|1995-09-16|1995-11-01|1995-10-02|COLLECT COD|AIR| haggle quickly\n1415|148235|8236|1|25|32080.75|0.06|0.00|A|F|1994-09-03|1994-07-12|1994-09-13|DELIVER IN PERSON|RAIL|ect never fluff\n1440|192213|7252|1|3|3915.63|0.06|0.01|N|O|1995-10-30|1995-10-17|1995-11-08|COLLECT COD|SHIP|instructions boost. fluffily regul\n1440|113748|1282|2|46|81040.04|0.02|0.03|N|O|1995-09-21|1995-10-19|1995-10-19|NONE|RAIL|blithely even instructions. \n1441|143130|8159|1|5|5865.65|0.04|0.01|N|O|1997-05-17|1997-05-11|1997-05-30|COLLECT COD|MAIL|egular courts. fluffily even grouches \n1441|176636|9154|2|5|8563.15|0.02|0.05|N|O|1997-04-25|1997-04-16|1997-05-23|COLLECT COD|FOB|he quickly enticing pac\n1441|117735|2758|3|14|24538.22|0.01|0.03|N|O|1997-06-30|1997-04-29|1997-07-24|DELIVER IN PERSON|REG AIR|special requests ha\n1441|159879|2395|4|37|71738.19|0.01|0.00|N|O|1997-04-26|1997-04-27|1997-04-29|NONE|REG AIR|accounts. slyly special dolphins b\n1441|71377|8899|5|34|45844.58|0.09|0.00|N|O|1997-06-12|1997-05-11|1997-06-29|TAKE BACK RETURN|RAIL|e carefully. blithely ironic dep\n1441|24329|9334|6|15|18799.80|0.09|0.08|N|O|1997-05-21|1997-05-06|1997-06-04|NONE|REG AIR| dependencies-- cour\n1441|95795|3323|7|50|89539.50|0.03|0.01|N|O|1997-06-07|1997-05-12|1997-06-08|NONE|SHIP| requests. blithely e\n1442|25080|85|1|8|8040.64|0.05|0.01|A|F|1994-10-31|1994-09-04|1994-11-25|COLLECT COD|AIR|c deposits haggle after the even\n1443|33679|6183|1|47|75795.49|0.04|0.06|N|O|1997-02-05|1997-02-02|1997-03-03|NONE|RAIL|carefully ironic requests sl\n1444|169552|7101|1|42|68105.10|0.01|0.02|R|F|1994-12-22|1995-03-03|1994-12-31|NONE|SHIP|ly bold packages boost regular ideas. spe\n1444|56665|1676|2|34|55136.44|0.04|0.08|A|F|1995-02-22|1995-02-15|1995-03-19|TAKE BACK RETURN|AIR|y. doggedly pend\n1444|154729|7245|3|34|60646.48|0.02|0.07|R|F|1994-12-17|1995-01-12|1995-01-03|COLLECT COD|AIR|ular accounts \n1444|118714|3737|4|6|10396.26|0.06|0.03|A|F|1995-01-07|1995-03-05|1995-01-17|COLLECT COD|RAIL|al accounts. br\n1444|19173|9174|5|35|38225.95|0.02|0.05|A|F|1995-02-25|1995-03-05|1995-03-24|DELIVER IN PERSON|SHIP|aggle furiou\n1444|32266|7273|6|42|50326.92|0.00|0.02|A|F|1994-12-16|1995-02-18|1994-12-22|DELIVER IN PERSON|RAIL|ss requests. ironic ideas wake above\n1444|81925|1926|7|12|22883.04|0.00|0.03|R|F|1994-12-23|1995-01-15|1995-01-13|COLLECT COD|TRUCK|ly among the bol\n1445|99368|9369|1|24|32816.64|0.01|0.00|A|F|1995-02-21|1995-02-22|1995-03-18|DELIVER IN PERSON|SHIP|al accounts use furiously a\n1445|66877|6878|2|48|88505.76|0.10|0.02|A|F|1995-02-28|1995-03-16|1995-03-12|COLLECT COD|MAIL|. final ideas are carefully dar\n1445|191125|3645|3|7|8512.84|0.10|0.04|A|F|1995-04-25|1995-02-25|1995-05-10|NONE|SHIP|structions: slyly regular re\n1445|27878|5385|4|17|30699.79|0.04|0.07|A|F|1995-04-02|1995-04-04|1995-05-01|COLLECT COD|FOB|ges. furiously regular pint\n1445|134431|6945|5|24|35170.32|0.10|0.06|R|F|1995-04-23|1995-02-16|1995-05-18|NONE|REG AIR|rate after the carefully reg\n1445|167046|7047|6|39|43408.56|0.03|0.02|A|F|1995-02-05|1995-02-20|1995-02-06|NONE|MAIL|ully unusual reques\n1446|71867|1868|1|31|57004.66|0.10|0.02|N|O|1998-05-01|1998-05-17|1998-05-30|NONE|REG AIR|. slyly reg\n1447|166076|1109|1|19|21699.33|0.06|0.04|A|F|1993-01-31|1992-12-07|1993-02-04|COLLECT COD|MAIL|. quickly ironic \n1447|31049|6056|2|6|5880.24|0.01|0.05|A|F|1992-10-24|1992-12-10|1992-11-05|DELIVER IN PERSON|AIR|as! regular packages poach above the\n1447|38700|6210|3|9|14748.30|0.04|0.00|R|F|1992-11-15|1993-01-07|1992-11-29|DELIVER IN PERSON|MAIL|counts wake s\n1447|21725|9232|4|8|13173.76|0.09|0.08|R|F|1992-11-20|1993-01-12|1992-12-14|COLLECT COD|FOB|ost carefully \n1447|129435|9436|5|23|33681.89|0.02|0.07|A|F|1992-12-07|1992-12-25|1993-01-06|TAKE BACK RETURN|AIR| dazzle quickly deposits. f\n1447|199588|4627|6|41|69190.78|0.08|0.02|R|F|1993-01-06|1993-01-05|1993-01-13|TAKE BACK RETURN|MAIL|rts boost s\n1472|7243|4744|1|36|41408.64|0.04|0.05|N|O|1996-11-06|1996-11-13|1996-11-12|COLLECT COD|SHIP|riously silent deposits to the pending d\n1472|132029|7056|2|26|27586.52|0.03|0.05|N|O|1996-11-08|1996-11-13|1996-12-02|DELIVER IN PERSON|FOB|ic packages w\n1472|567|8068|3|6|8805.36|0.08|0.01|N|O|1996-10-24|1996-11-19|1996-11-23|COLLECT COD|FOB|onic theodolites hinder slyly slyly r\n1473|53482|8493|1|50|71774.00|0.04|0.03|N|O|1997-05-05|1997-05-20|1997-05-09|NONE|TRUCK|requests wake express deposits. special, ir\n1473|67401|4920|2|32|43788.80|0.00|0.08|N|O|1997-04-18|1997-05-12|1997-05-10|DELIVER IN PERSON|REG AIR|out the packages lose furiously ab\n1474|14918|2422|1|5|9164.55|0.05|0.04|A|F|1995-04-22|1995-02-20|1995-05-06|COLLECT COD|SHIP|ully final a\n1474|122645|5158|2|30|50029.20|0.04|0.02|A|F|1995-03-23|1995-02-11|1995-04-17|DELIVER IN PERSON|TRUCK|usly. evenly express \n1474|91052|6071|3|18|18774.90|0.06|0.02|A|F|1995-01-23|1995-03-28|1995-02-03|NONE|RAIL|after the special\n1475|167997|5546|1|15|30974.85|0.08|0.06|N|O|1998-02-12|1997-12-17|1998-03-02|TAKE BACK RETURN|SHIP|xpress requests haggle after the final, fi\n1475|117282|7283|2|18|23387.04|0.07|0.00|N|O|1998-03-08|1998-01-18|1998-03-10|TAKE BACK RETURN|AIR|al deposits use. ironic packages along the \n1475|143964|6479|3|30|60238.80|0.03|0.02|N|O|1998-03-11|1997-12-30|1998-03-15|COLLECT COD|REG AIR| regular theodolites mold across th\n1475|186972|6973|4|50|102948.50|0.03|0.05|N|O|1997-12-14|1997-12-13|1997-12-21|COLLECT COD|AIR|. slyly bold re\n1475|31490|6497|5|33|46909.17|0.01|0.06|N|O|1998-01-02|1998-01-27|1998-01-11|NONE|FOB|quickly fluffy\n1475|49379|1884|6|12|15940.44|0.04|0.04|N|O|1998-01-09|1997-12-30|1998-01-23|NONE|TRUCK|arefully-- excuses sublate\n1475|111789|1790|7|23|41417.94|0.02|0.00|N|O|1998-02-13|1998-02-05|1998-03-08|NONE|TRUCK|hely regular hocke\n1476|30547|3051|1|20|29550.80|0.02|0.03|N|O|1996-08-11|1996-09-18|1996-08-26|TAKE BACK RETURN|AIR|. bold deposits are carefully amo\n1477|71965|6980|1|31|60045.76|0.00|0.06|N|O|1997-12-16|1997-09-30|1997-12-17|COLLECT COD|RAIL| requests. fluffily final \n1477|109605|7136|2|8|12916.80|0.09|0.05|N|O|1997-10-25|1997-10-18|1997-11-16|COLLECT COD|MAIL|ironic realms wake unusual, even ac\n1477|124479|4480|3|42|63145.74|0.06|0.00|N|O|1997-11-02|1997-11-02|1997-11-20|DELIVER IN PERSON|SHIP|lithely after the ir\n1477|106201|6202|4|32|38630.40|0.05|0.08|N|O|1997-09-12|1997-10-26|1997-10-12|TAKE BACK RETURN|AIR|; quickly regula\n1477|114850|4851|5|41|76458.85|0.04|0.06|N|O|1997-12-16|1997-10-31|1998-01-12|DELIVER IN PERSON|REG AIR|y. final pearls kindle. accounts \n1477|68931|3944|6|49|93096.57|0.06|0.00|N|O|1997-11-18|1997-11-06|1997-11-27|COLLECT COD|FOB|ise according to the sly, bold p\n1477|119012|1524|7|33|34023.33|0.06|0.00|N|O|1997-11-12|1997-11-06|1997-11-24|DELIVER IN PERSON|TRUCK|yly regular p\n1478|33650|3651|1|21|33256.65|0.00|0.06|N|O|1997-09-20|1997-10-25|1997-10-06|TAKE BACK RETURN|MAIL| fluffily pending acc\n1479|148818|1333|1|33|61604.73|0.10|0.01|N|O|1996-03-12|1996-02-28|1996-03-31|DELIVER IN PERSON|FOB| carefully special courts affix. fluff\n1504|81389|3898|1|42|57555.96|0.02|0.03|R|F|1992-10-18|1992-10-14|1992-11-10|TAKE BACK RETURN|FOB|ep. carefully ironic excuses haggle quickl\n1504|102385|9916|2|22|30522.36|0.04|0.03|A|F|1992-09-09|1992-10-29|1992-09-10|NONE|REG AIR| accounts sleep. furiou\n1504|177148|9666|3|9|11026.26|0.07|0.02|R|F|1992-11-02|1992-10-12|1992-11-15|TAKE BACK RETURN|RAIL|y slyly regular courts.\n1504|114073|9096|4|10|10870.70|0.04|0.07|A|F|1992-09-22|1992-10-22|1992-10-13|TAKE BACK RETURN|TRUCK|final theodolites. furiously e\n1504|19772|7276|5|7|11842.39|0.02|0.00|R|F|1992-11-20|1992-11-23|1992-12-13|COLLECT COD|MAIL|y final packa\n1505|119898|4921|1|4|7671.56|0.09|0.00|A|F|1992-12-14|1992-11-11|1993-01-02|COLLECT COD|SHIP|side of the s\n1505|122702|5215|2|50|86235.00|0.00|0.02|R|F|1992-11-22|1992-09-24|1992-11-26|TAKE BACK RETURN|FOB|lyly special platelets. requests ar\n1506|132024|2025|1|46|48576.92|0.04|0.05|R|F|1993-01-18|1992-11-11|1993-02-09|COLLECT COD|REG AIR|sits whithout the blithely ironic packages\n1506|113893|1427|2|30|57206.70|0.07|0.02|A|F|1992-11-22|1992-10-25|1992-12-04|DELIVER IN PERSON|FOB|deposits cajole \n1506|190697|3217|3|28|50055.32|0.10|0.06|A|F|1992-09-22|1992-11-19|1992-10-09|TAKE BACK RETURN|AIR| unwind carefully: theodolit\n1506|27798|2803|4|37|63854.23|0.00|0.03|R|F|1992-11-04|1992-12-01|1992-11-23|TAKE BACK RETURN|TRUCK|carefully bold dolphins. accounts su\n1506|194708|9747|5|15|27040.50|0.05|0.00|R|F|1992-09-24|1992-11-11|1992-10-05|NONE|REG AIR| carefully fluffy packages-- caref\n1506|49549|4558|6|38|56944.52|0.05|0.02|R|F|1992-12-02|1992-12-19|1992-12-29|NONE|REG AIR|xpress, regular excuse\n1506|168077|3110|7|4|4580.28|0.07|0.00|R|F|1993-01-03|1992-12-06|1993-01-05|COLLECT COD|REG AIR|posits. furiou\n1507|67094|2107|1|25|26527.25|0.01|0.08|R|F|1994-01-07|1994-01-06|1994-01-11|NONE|RAIL|xes. slyly busy de\n1507|39977|2481|2|33|63260.01|0.04|0.02|A|F|1993-10-29|1993-12-23|1993-11-14|DELIVER IN PERSON|REG AIR| asymptotes nag furiously above t\n1507|85770|8279|3|39|68475.03|0.03|0.07|R|F|1993-11-04|1993-12-16|1993-12-03|TAKE BACK RETURN|REG AIR|ly even instructions.\n1508|50350|7866|1|16|20805.60|0.02|0.06|N|O|1998-06-21|1998-05-30|1998-07-11|COLLECT COD|MAIL|riously across the ironic, unusua\n1508|24829|9834|2|20|35076.40|0.06|0.01|N|O|1998-04-17|1998-06-11|1998-05-17|DELIVER IN PERSON|MAIL|nic platelets. carefully final fra\n1508|92173|9701|3|43|50102.31|0.01|0.02|N|O|1998-06-01|1998-06-24|1998-06-03|TAKE BACK RETURN|TRUCK|ndencies h\n1508|147102|4645|4|1|1149.10|0.02|0.02|N|O|1998-07-13|1998-06-03|1998-07-17|TAKE BACK RETURN|AIR|s the blithely bold instruction\n1508|134671|9698|5|29|49464.43|0.02|0.00|N|O|1998-08-03|1998-07-08|1998-08-22|COLLECT COD|RAIL|r instructions. carefully\n1508|2673|174|6|5|7878.35|0.06|0.08|N|O|1998-05-22|1998-07-06|1998-06-04|COLLECT COD|REG AIR|cording to the furiously ironic depe\n1508|116953|6954|7|38|74858.10|0.03|0.06|N|O|1998-04-30|1998-06-23|1998-05-18|DELIVER IN PERSON|RAIL|tes wake furiously regular w\n1509|27109|2114|1|14|14505.40|0.04|0.01|A|F|1993-10-04|1993-09-25|1993-10-21|NONE|TRUCK|nal realms\n1509|10883|884|2|46|82518.48|0.08|0.02|A|F|1993-10-15|1993-10-04|1993-11-01|TAKE BACK RETURN|FOB|uriously regula\n1509|106243|6244|3|17|21237.08|0.06|0.05|A|F|1993-07-25|1993-08-28|1993-08-19|DELIVER IN PERSON|AIR| furiously. blithely regular ideas haggle c\n1509|19486|1988|4|11|15460.28|0.03|0.08|R|F|1993-11-04|1993-10-03|1993-11-14|TAKE BACK RETURN|FOB|ily ironic packages nod carefully.\n1509|89534|7059|5|37|56370.61|0.01|0.08|A|F|1993-08-31|1993-09-10|1993-09-24|NONE|FOB|he slyly even deposits wake a\n1509|186349|3904|6|31|44495.54|0.04|0.03|A|F|1993-07-14|1993-08-21|1993-08-06|COLLECT COD|SHIP|ic deposits cajole carefully. quickly bold \n1509|156167|1198|7|27|33025.32|0.01|0.01|A|F|1993-09-29|1993-09-08|1993-10-04|TAKE BACK RETURN|FOB|lithely after the \n1510|97465|4993|1|11|16087.06|0.09|0.04|N|O|1996-09-23|1996-12-03|1996-10-01|DELIVER IN PERSON|RAIL|e of the unusual accounts. stealthy deposit\n1510|83096|621|2|24|25898.16|0.05|0.04|N|O|1996-10-07|1996-10-22|1996-11-03|DELIVER IN PERSON|REG AIR|yly brave theod\n1510|189992|2511|3|36|74951.64|0.07|0.02|N|O|1996-10-02|1996-11-23|1996-10-05|NONE|SHIP|old deposits along the carefully\n1510|181428|8983|4|8|12075.36|0.01|0.08|N|O|1996-10-26|1996-11-07|1996-10-30|TAKE BACK RETURN|RAIL|blithely express\n1510|58453|8454|5|27|38109.15|0.08|0.06|N|O|1996-10-20|1996-12-05|1996-11-02|NONE|MAIL|he blithely regular req\n1510|13698|3699|6|3|4835.07|0.05|0.02|N|O|1996-10-31|1996-12-03|1996-11-13|COLLECT COD|RAIL|along the slyly regular pin\n1510|21462|6467|7|50|69173.00|0.04|0.05|N|O|1996-11-01|1996-10-17|1996-11-28|NONE|MAIL|even packages. carefully regular fo\n1511|97166|4694|1|29|33731.64|0.01|0.04|N|O|1997-03-17|1997-02-11|1997-03-27|DELIVER IN PERSON|AIR|s cajole furiously against \n1511|61718|6731|2|32|53750.72|0.04|0.01|N|O|1997-01-06|1997-03-21|1997-01-26|TAKE BACK RETURN|REG AIR| deposits. carefully ironi\n1536|193719|3720|1|5|9063.55|0.08|0.03|N|O|1997-02-08|1997-03-11|1997-03-02|COLLECT COD|MAIL|requests sleep pe\n1537|17492|9994|1|17|23961.33|0.01|0.03|A|F|1992-04-12|1992-04-19|1992-04-13|NONE|TRUCK|he regular pack\n1537|178379|3414|2|50|72868.50|0.08|0.00|R|F|1992-05-30|1992-05-14|1992-06-23|TAKE BACK RETURN|MAIL|special packages haggle slyly at the silent\n1537|12480|2481|3|44|61269.12|0.05|0.04|R|F|1992-04-01|1992-03-31|1992-04-21|NONE|TRUCK|lar courts.\n1537|139612|2126|4|3|4954.83|0.08|0.07|R|F|1992-03-20|1992-04-14|1992-03-21|TAKE BACK RETURN|SHIP|s, final ideas detect sl\n1538|101743|4254|1|32|55831.68|0.05|0.05|N|O|1995-07-08|1995-07-29|1995-08-01|TAKE BACK RETURN|RAIL|uses maintain blithely. fluffily\n1538|191738|1739|2|27|49402.71|0.05|0.01|N|O|1995-09-19|1995-08-03|1995-09-24|DELIVER IN PERSON|TRUCK|ngly even packag\n1538|129762|7299|3|36|64503.36|0.08|0.04|N|O|1995-07-11|1995-09-10|1995-07-26|DELIVER IN PERSON|MAIL|al deposits mo\n1538|103730|1261|4|28|48544.44|0.10|0.04|N|O|1995-09-19|1995-08-27|1995-10-10|COLLECT COD|RAIL|bout the fluffily unusual\n1538|177315|2350|5|13|18100.03|0.01|0.05|N|O|1995-06-26|1995-07-30|1995-07-25|NONE|SHIP|ly. packages sleep f\n1538|127205|9718|6|42|51752.40|0.08|0.08|N|O|1995-10-10|1995-09-12|1995-11-08|DELIVER IN PERSON|TRUCK|equests cajole blithely \n1539|195722|761|1|21|38172.12|0.08|0.02|R|F|1995-04-19|1995-05-10|1995-04-27|COLLECT COD|TRUCK|ounts haggle. busy\n1539|85981|998|2|11|21636.78|0.01|0.08|A|F|1995-05-27|1995-04-13|1995-06-10|TAKE BACK RETURN|TRUCK|ly express requests. furiously \n1539|67462|2475|3|7|10006.22|0.09|0.04|R|F|1995-05-14|1995-04-16|1995-05-30|DELIVER IN PERSON|AIR|. fluffily reg\n1540|172385|9937|1|38|55380.44|0.03|0.01|R|F|1992-09-30|1992-10-27|1992-10-12|TAKE BACK RETURN|SHIP| final grouches bo\n1540|59905|7421|2|35|65271.50|0.02|0.07|R|F|1992-10-31|1992-09-04|1992-11-05|TAKE BACK RETURN|SHIP|e blithely a\n1540|7166|2167|3|25|26829.00|0.08|0.04|R|F|1992-11-15|1992-10-24|1992-12-14|DELIVER IN PERSON|SHIP|ironic deposits amo\n1540|24603|2110|4|6|9165.60|0.09|0.03|R|F|1992-08-28|1992-09-17|1992-09-14|COLLECT COD|MAIL|ing to the slyly express asymptote\n1540|86906|9415|5|27|51108.30|0.10|0.08|R|F|1992-12-02|1992-10-18|1992-12-31|NONE|SHIP|carefully final packages; b\n1541|63380|5887|1|44|59108.72|0.10|0.05|N|O|1995-08-24|1995-07-13|1995-08-26|TAKE BACK RETURN|MAIL|o beans boost fluffily abou\n1541|25684|5685|2|8|12877.44|0.10|0.08|N|F|1995-06-05|1995-08-07|1995-06-21|TAKE BACK RETURN|TRUCK|y pending packages. blithely fi\n1542|57750|7751|1|37|63186.75|0.07|0.06|A|F|1993-12-15|1993-10-17|1994-01-07|TAKE BACK RETURN|REG AIR|e blithely unusual accounts. quic\n1542|2488|4989|2|12|16685.76|0.09|0.06|R|F|1993-10-29|1993-11-02|1993-11-09|TAKE BACK RETURN|RAIL|carefully \n1542|5566|5567|3|18|26488.08|0.05|0.05|R|F|1993-10-17|1993-11-15|1993-10-26|TAKE BACK RETURN|FOB|pending instr\n1542|142665|5180|4|21|35860.86|0.01|0.05|R|F|1993-10-13|1993-12-13|1993-11-12|NONE|RAIL|y pending foxes nag blithely \n1542|154955|2501|5|46|92457.70|0.00|0.00|R|F|1993-09-28|1993-11-03|1993-10-15|COLLECT COD|FOB|ial instructions. ironically\n1543|70883|5898|1|34|63031.92|0.02|0.08|N|O|1997-05-25|1997-03-30|1997-06-04|NONE|AIR|ic requests are ac\n1543|114492|7004|2|6|9038.94|0.09|0.01|N|O|1997-04-16|1997-05-20|1997-05-16|DELIVER IN PERSON|MAIL| among the carefully bold or\n1543|66020|6021|3|42|41412.84|0.06|0.01|N|O|1997-05-26|1997-03-30|1997-06-12|DELIVER IN PERSON|FOB|its sleep until the fur\n1543|188591|3628|4|42|70542.78|0.05|0.06|N|O|1997-04-11|1997-04-11|1997-04-23|TAKE BACK RETURN|MAIL|xpress instructions. regular acc\n1543|39868|9869|5|9|16270.74|0.08|0.06|N|O|1997-03-14|1997-05-19|1997-03-26|DELIVER IN PERSON|FOB|ravely special requests \n1543|48782|6295|6|3|5192.34|0.10|0.04|N|O|1997-03-29|1997-05-10|1997-04-22|COLLECT COD|MAIL|sleep along the furiou\n1543|67763|270|7|3|5192.28|0.00|0.02|N|O|1997-03-22|1997-04-06|1997-03-30|NONE|AIR|quickly. final accounts haggle slyl\n1568|89369|1878|1|36|48900.96|0.02|0.03|N|O|1997-05-31|1997-04-22|1997-06-21|TAKE BACK RETURN|RAIL|platelets-- furiously sly excu\n1568|8928|1429|2|46|84498.32|0.04|0.00|N|O|1997-04-06|1997-04-08|1997-04-23|TAKE BACK RETURN|MAIL|g the blithely even acco\n1569|74460|1982|1|5|7172.30|0.07|0.00|N|O|1998-04-16|1998-06-21|1998-04-18|COLLECT COD|REG AIR| packages. ironic, even excuses a\n1569|38749|8750|2|16|27003.84|0.01|0.08|N|O|1998-04-26|1998-06-16|1998-05-26|COLLECT COD|MAIL|deposits. blithely final asymptotes ac\n1569|48818|8819|3|43|75972.83|0.10|0.03|N|O|1998-06-05|1998-05-31|1998-06-28|DELIVER IN PERSON|FOB| instructions.\n1569|69193|9194|4|30|34865.70|0.02|0.03|N|O|1998-07-19|1998-06-04|1998-08-10|NONE|SHIP|packages. excuses lose evenly carefully reg\n1570|182632|5151|1|25|42865.75|0.00|0.06|N|O|1998-05-03|1998-06-02|1998-06-02|DELIVER IN PERSON|REG AIR|its. slyly regular sentiments\n1570|85017|34|2|7|7014.07|0.05|0.05|N|O|1998-07-10|1998-06-01|1998-07-23|TAKE BACK RETURN|MAIL|requests boost quickly re\n1571|51371|1372|1|47|62151.39|0.00|0.05|R|F|1992-12-07|1993-02-24|1993-01-01|TAKE BACK RETURN|REG AIR|ng to the fluffily unusual \n1571|182725|280|2|6|10846.32|0.03|0.00|A|F|1993-01-08|1993-02-13|1993-02-07|COLLECT COD|SHIP| special, ironic depo\n1571|58030|536|3|18|17784.54|0.05|0.08|A|F|1993-01-09|1993-01-12|1993-01-31|COLLECT COD|AIR| pending grouches \n1571|100330|2841|4|48|63855.84|0.05|0.05|A|F|1992-12-28|1993-01-04|1993-01-04|DELIVER IN PERSON|RAIL|slyly pending p\n1571|41509|6518|5|10|14505.00|0.03|0.06|R|F|1992-12-12|1993-02-13|1992-12-29|DELIVER IN PERSON|AIR|lets. carefully regular ideas wake\n1571|33276|786|6|24|29022.48|0.05|0.07|A|F|1993-03-22|1993-01-31|1993-04-09|NONE|TRUCK|warthogs wake carefully acro\n1572|23675|3676|1|41|65545.47|0.02|0.00|N|O|1996-05-16|1996-04-09|1996-05-28|TAKE BACK RETURN|REG AIR|. pinto beans alongside\n1572|92330|9858|2|10|13223.30|0.04|0.06|N|O|1996-05-17|1996-03-26|1996-05-19|NONE|AIR| accounts affix slyly. \n1573|185330|5331|1|5|7076.65|0.05|0.01|A|F|1993-04-24|1993-03-13|1993-05-17|TAKE BACK RETURN|MAIL|ymptotes could u\n1573|30683|684|2|17|27432.56|0.00|0.06|R|F|1993-02-24|1993-02-16|1993-03-08|TAKE BACK RETURN|TRUCK|carefully regular deposits. \n1573|82204|2205|3|16|18979.20|0.04|0.03|A|F|1993-03-15|1993-03-16|1993-03-31|COLLECT COD|AIR|ely. furiously final requests wake slyl\n1573|193749|8788|4|11|20270.14|0.09|0.01|R|F|1993-03-23|1993-03-24|1993-04-12|TAKE BACK RETURN|RAIL|nently pending\n1573|136810|1837|5|7|12927.67|0.00|0.01|R|F|1993-01-30|1993-03-14|1993-02-27|DELIVER IN PERSON|SHIP|eodolites sleep slyly. slyly f\n1573|153063|609|6|30|33481.80|0.03|0.01|A|F|1992-12-29|1993-03-06|1993-01-02|DELIVER IN PERSON|TRUCK|. blithely even theodolites boos\n1574|47706|5219|1|41|67801.70|0.06|0.02|N|O|1997-03-08|1997-02-09|1997-04-01|COLLECT COD|AIR|s. slyly regular depen\n1574|190353|7911|2|50|72167.50|0.00|0.05|N|O|1996-12-14|1997-02-14|1996-12-16|TAKE BACK RETURN|FOB|le regular, regular foxes. blithely e\n1574|54013|6519|3|25|24175.25|0.06|0.02|N|O|1997-01-16|1997-02-14|1997-02-12|DELIVER IN PERSON|TRUCK|ly silent accounts.\n1574|190880|5919|4|6|11825.28|0.03|0.05|N|O|1997-02-24|1997-02-03|1997-03-01|NONE|AIR|e silent, final packages. speci\n1574|108301|3322|5|6|7855.80|0.05|0.05|N|O|1997-02-09|1997-03-02|1997-02-14|COLLECT COD|MAIL|nic, final ideas snooze. \n1574|4733|2234|6|42|68784.66|0.07|0.01|N|O|1996-12-19|1997-01-13|1996-12-28|NONE|FOB|o beans according t\n1574|135755|782|7|14|25070.50|0.04|0.01|N|O|1996-12-30|1997-01-19|1997-01-20|NONE|AIR|ily bold a\n1575|28443|8444|1|42|57600.48|0.05|0.08|N|O|1995-10-21|1995-11-25|1995-10-24|DELIVER IN PERSON|RAIL|ly pending pinto beans.\n1575|35236|243|2|39|45677.97|0.00|0.06|N|O|1995-10-30|1995-10-15|1995-11-10|COLLECT COD|TRUCK| ironic requests snooze ironic, regular acc\n1575|1887|4388|3|12|21466.56|0.01|0.05|N|O|1995-12-27|1995-11-11|1996-01-23|TAKE BACK RETURN|AIR| bold accounts. furi\n1575|110162|7696|4|39|45714.24|0.07|0.00|N|O|1995-09-23|1995-11-05|1995-09-25|TAKE BACK RETURN|TRUCK| after the unusual asym\n1575|82215|4724|5|10|11972.10|0.09|0.00|N|O|1996-01-10|1995-11-20|1996-01-13|DELIVER IN PERSON|RAIL|k excuses. pinto beans wake a\n1575|177660|5212|6|14|24327.24|0.08|0.06|N|O|1995-10-31|1995-12-06|1995-11-30|NONE|AIR|beans breach among the furiously specia\n1575|116986|9498|7|48|96143.04|0.08|0.04|N|O|1995-11-19|1995-10-25|1995-12-07|DELIVER IN PERSON|TRUCK|cies. regu\n1600|171408|8960|1|20|29588.00|0.02|0.01|R|F|1993-06-16|1993-04-23|1993-07-02|COLLECT COD|FOB|pths sleep blithely about the\n1600|43128|641|2|48|51413.76|0.07|0.02|R|F|1993-04-17|1993-04-14|1993-05-03|DELIVER IN PERSON|FOB|furiously silent foxes could wake. car\n1600|38465|3472|3|8|11227.68|0.04|0.07|R|F|1993-03-07|1993-04-22|1993-03-26|TAKE BACK RETURN|FOB|cajole furiously fluf\n1600|68481|988|4|25|36237.00|0.00|0.06|A|F|1993-05-25|1993-04-07|1993-06-05|TAKE BACK RETURN|REG AIR|press packages. ironic excuses bo\n1600|146041|6042|5|30|32611.20|0.03|0.08|R|F|1993-06-03|1993-05-03|1993-06-07|DELIVER IN PERSON|RAIL|al escapades alongside of the depo\n1601|166156|6157|1|6|7332.90|0.00|0.00|A|F|1994-10-19|1994-09-28|1994-10-23|COLLECT COD|SHIP| bold sheaves. furiously per\n1601|174374|1926|2|50|72418.50|0.03|0.02|R|F|1994-12-24|1994-10-23|1995-01-11|COLLECT COD|FOB|ideas doubt\n1601|89887|2396|3|14|26276.32|0.04|0.08|R|F|1994-09-17|1994-11-22|1994-10-03|DELIVER IN PERSON|RAIL|he special, fin\n1602|182806|2807|1|4|7555.20|0.08|0.06|R|F|1993-10-31|1993-09-05|1993-11-21|NONE|RAIL|y. even excuses\n1603|38191|5701|1|1|1129.19|0.08|0.00|R|F|1993-08-17|1993-09-04|1993-08-22|TAKE BACK RETURN|REG AIR|d accounts. special warthogs use fur\n1603|65209|7716|2|29|34051.80|0.06|0.08|A|F|1993-09-28|1993-09-20|1993-10-28|NONE|SHIP|ses wake furiously. theodolite\n1604|41563|1564|1|15|22568.40|0.09|0.08|R|F|1993-09-22|1993-09-03|1993-09-29|TAKE BACK RETURN|MAIL| instructions haggle\n1604|140413|5442|2|37|53776.17|0.06|0.06|A|F|1993-08-22|1993-09-21|1993-09-10|COLLECT COD|SHIP|requests. blithely ironic somas s\n1604|113214|5726|3|19|23316.99|0.09|0.07|A|F|1993-10-15|1993-10-04|1993-11-09|COLLECT COD|RAIL| ideas. bol\n1604|174239|9274|4|15|19698.45|0.03|0.00|R|F|1993-09-10|1993-08-31|1993-09-30|TAKE BACK RETURN|RAIL|ending realms along the special, p\n1604|20091|7598|5|23|23255.07|0.08|0.05|A|F|1993-10-11|1993-08-30|1993-10-18|DELIVER IN PERSON|RAIL|en requests. blithely fin\n1605|141821|9364|1|47|87552.54|0.00|0.01|N|O|1998-04-29|1998-06-12|1998-05-20|DELIVER IN PERSON|AIR|. carefully r\n1605|179691|7243|2|18|31872.42|0.10|0.00|N|O|1998-05-13|1998-06-17|1998-06-03|COLLECT COD|REG AIR|ly regular foxes wake carefully. bol\n1605|58546|8547|3|39|58677.06|0.02|0.03|N|O|1998-07-12|1998-06-05|1998-08-09|DELIVER IN PERSON|MAIL|nal dependencies-- quickly final frets acc\n1605|182574|129|4|25|41414.25|0.06|0.02|N|O|1998-05-26|1998-06-14|1998-06-05|COLLECT COD|AIR|ole carefully car\n1606|114082|4083|1|21|23017.68|0.04|0.00|N|O|1997-06-02|1997-07-02|1997-06-27|DELIVER IN PERSON|RAIL| pending theodolites prom\n1606|173565|8600|2|35|57349.60|0.00|0.02|N|O|1997-06-20|1997-06-19|1997-06-22|COLLECT COD|TRUCK|carefully sil\n1606|99477|7005|3|23|33958.81|0.00|0.06|N|O|1997-04-19|1997-06-26|1997-04-30|NONE|MAIL|ously final requests. slowly ironic ex\n1606|96286|8796|4|20|25645.60|0.02|0.04|N|O|1997-05-01|1997-05-26|1997-05-28|TAKE BACK RETURN|TRUCK|fily carefu\n1606|70181|5196|5|14|16116.52|0.10|0.01|N|O|1997-05-19|1997-07-05|1997-06-10|COLLECT COD|FOB|structions haggle f\n1607|189131|6686|1|2|2440.26|0.02|0.00|N|O|1996-01-11|1996-02-15|1996-01-19|DELIVER IN PERSON|MAIL|packages haggle. regular requests boost s\n1607|118923|1435|2|37|71851.04|0.05|0.02|N|O|1996-02-27|1996-02-18|1996-03-16|NONE|AIR|alongside \n1607|122680|2681|3|39|66404.52|0.00|0.00|N|O|1996-02-01|1996-02-12|1996-02-16|NONE|FOB|uches cajole. accounts ar\n1607|75587|8095|4|34|53127.72|0.05|0.06|N|O|1996-01-06|1996-02-24|1996-01-10|DELIVER IN PERSON|SHIP| quickly above the \n1607|177948|466|5|48|97245.12|0.00|0.05|N|O|1996-02-22|1996-02-13|1996-03-09|TAKE BACK RETURN|MAIL|ular forges. deposits a\n1632|190513|8071|1|47|75364.97|0.08|0.00|N|O|1997-01-25|1997-02-09|1997-02-19|TAKE BACK RETURN|RAIL|g to the closely special no\n1632|147914|5457|2|14|27466.74|0.08|0.05|N|O|1997-01-15|1997-02-25|1997-01-28|NONE|RAIL|oxes. deposits nag slyly along the slyly \n1632|176169|1204|3|47|58522.52|0.03|0.04|N|O|1997-01-29|1997-03-03|1997-02-21|NONE|MAIL|sts. blithely regular \n1632|56164|3680|4|33|36965.28|0.09|0.02|N|O|1997-04-01|1997-02-24|1997-04-29|TAKE BACK RETURN|REG AIR|ructions! slyly\n1632|141072|8615|5|43|47862.01|0.10|0.03|N|O|1997-02-24|1997-02-19|1997-03-25|DELIVER IN PERSON|FOB|ts. blithe, bold ideas cajo\n1633|177903|2938|1|35|69331.50|0.01|0.02|N|O|1996-01-09|1995-12-02|1996-01-21|COLLECT COD|REG AIR|ly against the dolph\n1633|4124|4125|2|15|15421.80|0.00|0.05|N|O|1995-12-13|1995-11-13|1996-01-04|TAKE BACK RETURN|FOB|ges wake fluffil\n1634|47063|7064|1|21|21211.26|0.00|0.00|N|O|1996-10-04|1996-10-22|1996-11-01|NONE|MAIL|counts alo\n1634|171632|1633|2|44|74959.72|0.05|0.01|N|O|1996-09-17|1996-11-09|1996-10-03|COLLECT COD|SHIP|requests affix slyly. quickly even pack\n1634|18568|8569|3|21|31217.76|0.06|0.07|N|O|1996-11-16|1996-10-21|1996-11-27|NONE|TRUCK|y along the excuses.\n1634|67465|4984|4|17|24351.82|0.08|0.07|N|O|1996-10-29|1996-10-15|1996-11-02|TAKE BACK RETURN|SHIP|cial, bold platelets alongside of the f\n1634|75908|5909|5|2|3767.80|0.07|0.04|N|O|1996-11-22|1996-10-28|1996-12-17|NONE|SHIP|ly. carefully regular asymptotes wake\n1634|169681|2198|6|11|19257.48|0.01|0.08|N|O|1996-10-04|1996-12-06|1996-10-14|DELIVER IN PERSON|SHIP|final requests \n1634|12416|4918|7|35|46494.35|0.06|0.02|N|O|1996-11-25|1996-11-25|1996-12-12|TAKE BACK RETURN|RAIL|cies. regular, special de\n1635|70167|2675|1|3|3411.48|0.06|0.08|N|O|1997-03-13|1997-03-25|1997-03-27|COLLECT COD|FOB| quickly ironic r\n1635|89018|9019|2|8|8056.08|0.04|0.05|N|O|1997-04-30|1997-04-21|1997-05-09|DELIVER IN PERSON|AIR|ravely carefully express \n1635|113791|3792|3|20|36095.80|0.07|0.01|N|O|1997-05-19|1997-04-01|1997-06-17|TAKE BACK RETURN|FOB|oost according to the carefully even accou\n1635|76063|3585|4|40|41562.40|0.01|0.04|N|O|1997-02-25|1997-03-20|1997-03-12|TAKE BACK RETURN|RAIL|uriously up the ironic deposits. slyly i\n1636|84083|1608|1|2|2134.16|0.09|0.03|N|O|1997-09-26|1997-08-22|1997-10-05|NONE|TRUCK|nal foxes cajole above the blithely reg\n1636|168925|8926|2|45|89726.40|0.03|0.01|N|O|1997-07-14|1997-08-08|1997-07-27|COLLECT COD|RAIL|ely express reque\n1636|107869|380|3|24|45044.64|0.07|0.08|N|O|1997-10-07|1997-08-12|1997-11-04|TAKE BACK RETURN|MAIL|e carefully unusual ideas are f\n1636|152367|4883|4|43|61032.48|0.06|0.00|N|O|1997-08-23|1997-08-10|1997-09-17|NONE|REG AIR|blithely special r\n1636|18378|3381|5|22|28520.14|0.05|0.02|N|O|1997-07-22|1997-08-18|1997-08-03|COLLECT COD|AIR|ular, regu\n1636|62910|5417|6|34|63678.94|0.10|0.01|N|O|1997-08-11|1997-09-09|1997-08-23|NONE|TRUCK|ular depos\n1636|113018|8041|7|7|7217.07|0.04|0.00|N|O|1997-07-28|1997-09-10|1997-07-31|NONE|MAIL|ronic instructions. final\n1637|85755|5756|1|49|85296.75|0.02|0.03|N|F|1995-06-08|1995-04-19|1995-07-01|COLLECT COD|REG AIR|. blithely i\n1637|72269|7284|2|1|1241.26|0.10|0.02|A|F|1995-02-14|1995-03-26|1995-03-09|TAKE BACK RETURN|AIR|ly final pinto beans. furiously\n1637|21652|6657|3|10|15736.50|0.02|0.05|R|F|1995-02-21|1995-03-17|1995-03-11|NONE|AIR|uriously? blithely even sauternes wake. \n1637|92629|5139|4|42|68108.04|0.06|0.01|A|F|1995-03-18|1995-04-24|1995-03-31|COLLECT COD|SHIP|blithely a\n1637|4005|6506|5|25|22725.00|0.05|0.00|R|F|1995-06-07|1995-03-26|1995-06-08|COLLECT COD|RAIL| haggle carefully silent accou\n1637|108780|3801|6|38|67973.64|0.02|0.08|R|F|1995-03-20|1995-05-05|1995-04-14|DELIVER IN PERSON|SHIP|even, pending foxes nod regular\n1637|51705|4211|7|21|34790.70|0.07|0.08|A|F|1995-04-30|1995-04-30|1995-05-05|COLLECT COD|SHIP|ly ironic theodolites use b\n1638|5840|5841|1|46|80308.64|0.03|0.02|N|O|1997-10-16|1997-10-28|1997-11-09|COLLECT COD|MAIL|otes haggle before the slyly bold instructi\n1638|148976|8977|2|30|60749.10|0.00|0.04|N|O|1997-12-05|1997-09-17|1997-12-06|NONE|REG AIR|s cajole boldly bold requests. closely \n1638|30158|2662|3|5|5440.75|0.08|0.07|N|O|1997-10-15|1997-11-01|1997-11-08|DELIVER IN PERSON|FOB|xcuses sleep furiou\n1638|55974|3490|4|19|36669.43|0.00|0.08|N|O|1997-10-15|1997-10-27|1997-11-03|DELIVER IN PERSON|MAIL| quickly expres\n1638|142867|7896|5|25|47746.50|0.05|0.03|N|O|1997-10-06|1997-09-30|1997-11-02|DELIVER IN PERSON|REG AIR|gle final, ironic pinto beans. \n1638|154436|9467|6|46|68559.78|0.07|0.08|N|O|1997-08-20|1997-10-10|1997-09-09|COLLECT COD|AIR|ckages are carefully even instru\n1639|186473|6474|1|24|37427.28|0.07|0.00|N|O|1995-08-24|1995-10-06|1995-08-31|COLLECT COD|REG AIR| the regular packages. courts dou\n1639|42945|7954|2|38|71741.72|0.01|0.04|N|O|1995-08-23|1995-11-09|1995-08-29|TAKE BACK RETURN|FOB|y regular packages. b\n1639|170376|5411|3|41|59301.17|0.04|0.02|N|O|1995-12-19|1995-11-11|1996-01-12|DELIVER IN PERSON|FOB|structions w\n1664|117307|2330|1|48|63566.40|0.04|0.02|N|O|1996-06-21|1996-05-01|1996-07-19|TAKE BACK RETURN|RAIL| use. ironic deposits integrate. slyly unu\n1664|172152|7187|2|30|36724.50|0.06|0.05|N|O|1996-04-04|1996-05-04|1996-05-03|COLLECT COD|FOB|ess multip\n1664|150899|900|3|10|19498.90|0.00|0.06|N|O|1996-04-10|1996-05-13|1996-05-07|TAKE BACK RETURN|RAIL|instructions up the acc\n1664|154273|6789|4|35|46454.45|0.00|0.04|N|O|1996-03-06|1996-05-16|1996-03-09|DELIVER IN PERSON|REG AIR|y regular ide\n1664|56747|6748|5|9|15333.66|0.07|0.04|N|O|1996-04-15|1996-05-14|1996-05-11|DELIVER IN PERSON|TRUCK|ges. fluffil\n1664|140115|2630|6|40|46204.40|0.09|0.07|N|O|1996-04-02|1996-04-22|1996-04-17|COLLECT COD|REG AIR|se blithely unusual pains. carefully\n1665|46402|3915|1|4|5393.60|0.02|0.03|A|F|1994-09-01|1994-06-07|1994-09-12|DELIVER IN PERSON|TRUCK|ely final requests. requests\n1665|77098|4620|2|1|1075.09|0.03|0.05|R|F|1994-05-22|1994-07-06|1994-05-24|TAKE BACK RETURN|TRUCK|sly final p\n1666|184344|1899|1|30|42850.20|0.04|0.03|N|O|1995-10-28|1995-11-30|1995-11-18|TAKE BACK RETURN|AIR| breach evenly final accounts. r\n1666|63994|9007|2|20|39159.80|0.01|0.00|N|O|1996-01-27|1995-12-12|1996-01-31|NONE|REG AIR|uietly regular foxes wake quick\n1666|133479|1019|3|31|46886.57|0.05|0.07|N|O|1996-02-11|1996-01-11|1996-02-28|COLLECT COD|RAIL|ding to the express, bold accounts. fu\n1666|168802|1319|4|41|76702.80|0.06|0.08|N|O|1995-11-29|1996-01-04|1995-12-24|NONE|TRUCK|ly regular excuses; regular ac\n1667|20267|7774|1|6|7123.56|0.04|0.02|N|O|1997-12-07|1997-11-16|1998-01-02|COLLECT COD|FOB|riously busy requests. blithely final a\n1667|21164|6169|2|29|31469.64|0.06|0.07|N|O|1997-10-15|1997-11-09|1997-11-11|TAKE BACK RETURN|MAIL|l accounts. furiously final courts h\n1667|94941|9960|3|48|92925.12|0.05|0.01|N|O|1998-01-27|1998-01-06|1998-02-09|TAKE BACK RETURN|SHIP|tes sleep furiously. carefully eve\n1667|58119|5635|4|24|25850.64|0.04|0.01|N|O|1997-10-14|1997-12-01|1997-11-09|TAKE BACK RETURN|MAIL|hrash final requests. care\n1667|194315|1873|5|2|2818.62|0.07|0.00|N|O|1997-12-17|1997-11-22|1998-01-16|NONE|SHIP|pecial requests hag\n1667|47733|5246|6|6|10084.38|0.01|0.03|N|O|1998-01-21|1997-12-19|1998-01-28|NONE|TRUCK| nag quickly above th\n1667|39956|2460|7|19|36023.05|0.09|0.03|N|O|1998-01-23|1997-11-24|1998-01-26|DELIVER IN PERSON|SHIP|around the pinto beans. express, special\n1668|131636|4150|1|8|13341.04|0.06|0.01|N|O|1997-07-23|1997-10-09|1997-08-06|DELIVER IN PERSON|FOB|arefully regular tithes! slyl\n1668|945|8446|2|25|46148.50|0.01|0.06|N|O|1997-08-08|1997-09-28|1997-09-01|NONE|TRUCK|y ironic requests. bold, final ideas a\n1668|74160|6668|3|42|47634.72|0.08|0.01|N|O|1997-08-09|1997-09-08|1997-08-31|NONE|FOB|ole carefully excuses. final\n1668|190774|8332|4|9|16782.93|0.05|0.03|N|O|1997-10-17|1997-09-05|1997-11-01|COLLECT COD|RAIL|wake furiously even instructions. sil\n1668|127924|7925|5|25|48798.00|0.01|0.02|N|O|1997-10-08|1997-09-20|1997-10-11|DELIVER IN PERSON|REG AIR|even platelets across the silent \n1668|9920|2421|6|38|69536.96|0.07|0.01|N|O|1997-08-26|1997-09-17|1997-09-05|DELIVER IN PERSON|TRUCK|ep slyly across the furi\n1669|78373|8374|1|24|32432.88|0.04|0.08|N|O|1997-09-04|1997-07-30|1997-09-20|DELIVER IN PERSON|RAIL| regular, final deposits use quick\n1670|31411|6418|1|41|55038.81|0.07|0.01|N|O|1997-07-19|1997-08-20|1997-07-23|DELIVER IN PERSON|TRUCK|thely according to the sly\n1670|121378|1379|2|10|13993.70|0.07|0.03|N|O|1997-09-14|1997-08-16|1997-09-23|NONE|SHIP|fily special ideas \n1670|185284|321|3|41|56140.48|0.07|0.07|N|O|1997-07-19|1997-08-05|1997-07-26|COLLECT COD|SHIP|al gifts. speci\n1671|148885|3914|1|21|40611.48|0.02|0.07|N|O|1996-07-28|1996-09-28|1996-08-08|TAKE BACK RETURN|AIR|s accounts slee\n1671|95111|2639|2|4|4424.44|0.05|0.00|N|O|1996-08-30|1996-09-19|1996-09-23|DELIVER IN PERSON|TRUCK|lyly regular ac\n1671|123591|8616|3|11|17760.49|0.06|0.08|N|O|1996-09-16|1996-10-21|1996-09-18|NONE|SHIP|tes sleep blithely\n1671|177303|2338|4|5|6901.50|0.00|0.00|N|O|1996-11-14|1996-10-20|1996-11-25|TAKE BACK RETURN|FOB|luffily regular deposits\n1671|126590|6591|5|12|19399.08|0.07|0.04|N|O|1996-11-17|1996-09-02|1996-12-17|COLLECT COD|RAIL|special, ironic\n1671|196217|8737|6|46|60407.66|0.08|0.05|N|O|1996-09-13|1996-10-14|1996-09-28|TAKE BACK RETURN|REG AIR|. slyly bold instructions boost. furiousl\n1696|15419|422|1|8|10675.28|0.04|0.02|N|O|1998-04-28|1998-02-07|1998-05-10|NONE|TRUCK|the blithely\n1696|138312|826|2|13|17554.03|0.08|0.06|N|O|1998-03-01|1998-03-25|1998-03-24|TAKE BACK RETURN|TRUCK|tructions play slyly q\n1696|1107|3608|3|19|19153.90|0.08|0.05|N|O|1998-05-03|1998-03-13|1998-05-28|TAKE BACK RETURN|REG AIR|its maintain alongside of the f\n1696|192712|2713|4|21|37898.91|0.05|0.00|N|O|1998-05-04|1998-02-18|1998-05-07|NONE|MAIL|y players sleep along the final, pending \n1696|93788|8807|5|43|76616.54|0.03|0.06|N|O|1998-02-14|1998-03-29|1998-02-20|COLLECT COD|FOB|arefully regular dep\n1697|74452|6960|1|6|8558.70|0.05|0.00|N|O|1997-01-28|1996-11-27|1997-01-31|NONE|FOB|accounts breach slyly even de\n1697|103417|5928|2|24|34089.84|0.00|0.08|N|O|1996-12-29|1996-12-19|1997-01-10|NONE|SHIP|ts cajole carefully above the carefully\n1697|123693|6206|3|27|46350.63|0.06|0.00|N|O|1997-01-20|1996-12-02|1997-02-05|COLLECT COD|MAIL|ly regular packages across the silent, b\n1697|93083|3084|4|49|52727.92|0.08|0.04|N|O|1996-12-07|1997-01-02|1996-12-31|COLLECT COD|TRUCK|lar foxes. fluffily furious ideas doubt qu\n1697|34808|7312|5|19|33113.20|0.03|0.07|N|O|1997-01-08|1996-11-12|1997-01-11|DELIVER IN PERSON|FOB|ons? special, special accounts after\n1698|96537|6538|1|44|67475.32|0.05|0.05|N|O|1997-05-16|1997-07-05|1997-05-27|NONE|RAIL|ts wake slyly after t\n1698|92106|4616|2|6|6588.60|0.08|0.00|N|O|1997-08-21|1997-06-08|1997-09-03|DELIVER IN PERSON|RAIL| pending packages affix ne\n1698|20546|3049|3|22|32263.88|0.03|0.04|N|O|1997-08-07|1997-05-28|1997-08-24|DELIVER IN PERSON|TRUCK|oward the furiously iro\n1698|111909|4421|4|19|36497.10|0.00|0.07|N|O|1997-07-04|1997-06-21|1997-08-01|NONE|RAIL| fluffily e\n1698|52417|2418|5|37|50668.17|0.00|0.03|N|O|1997-05-16|1997-05-29|1997-05-27|NONE|AIR|ly regular ideas. deposit\n1698|165824|5825|6|15|28347.30|0.10|0.01|N|O|1997-07-20|1997-06-07|1997-07-21|TAKE BACK RETURN|RAIL|final ideas. even, ironic \n1699|37350|2357|1|50|64367.50|0.00|0.06|A|F|1994-03-26|1994-03-23|1994-04-20|NONE|FOB|to the final requests are carefully silent \n1699|134825|4826|2|17|31616.94|0.07|0.02|R|F|1994-01-12|1994-03-12|1994-02-08|NONE|AIR|haggle blithely slyly\n1700|139702|4729|1|38|66184.60|0.04|0.04|N|O|1996-10-03|1996-07-27|1996-10-22|NONE|RAIL|ular dependencies engage slyly \n1700|155943|5944|2|49|97948.06|0.04|0.00|N|O|1996-09-26|1996-07-28|1996-10-16|NONE|TRUCK|kly even dependencies haggle fluffi\n1701|149418|6961|1|47|68968.27|0.08|0.05|R|F|1992-05-25|1992-06-29|1992-06-15|NONE|RAIL|slyly final requests cajole requests. f\n1701|53004|3005|2|2|1914.00|0.01|0.04|R|F|1992-06-24|1992-07-12|1992-06-29|COLLECT COD|SHIP|ween the pending, final accounts. \n1701|34683|7187|3|26|42059.68|0.10|0.06|R|F|1992-06-04|1992-07-11|1992-07-04|DELIVER IN PERSON|FOB| accounts. blithely pending pinto be\n1702|66974|4493|1|19|36878.43|0.02|0.01|N|F|1995-06-02|1995-06-30|1995-06-29|NONE|REG AIR|ies haggle blith\n1702|29762|2265|2|38|64286.88|0.00|0.00|N|O|1995-09-01|1995-06-10|1995-09-10|DELIVER IN PERSON|REG AIR|as believe blithely. bo\n1702|194425|4426|3|46|69893.32|0.00|0.08|N|O|1995-07-14|1995-06-30|1995-07-20|NONE|FOB|y even foxes. carefully final dependencies \n1702|92039|2040|4|28|28868.84|0.07|0.05|R|F|1995-06-10|1995-07-26|1995-06-16|TAKE BACK RETURN|AIR|nts haggle along the packa\n1702|88038|8039|5|34|34885.02|0.01|0.06|N|O|1995-07-04|1995-06-08|1995-07-28|DELIVER IN PERSON|AIR|y careful packages; dogged acco\n1702|41506|4011|6|28|40530.00|0.10|0.00|N|O|1995-08-14|1995-07-31|1995-09-08|COLLECT COD|RAIL|ackages sleep. furiously even excuses snooz\n1703|165404|7921|1|36|52898.40|0.09|0.01|R|F|1993-04-22|1993-03-05|1993-04-24|DELIVER IN PERSON|SHIP|riously express \n1703|136118|1145|2|35|40393.85|0.01|0.08|R|F|1993-04-14|1993-03-31|1993-04-27|NONE|RAIL|he carefully\n1703|123460|3461|3|48|71206.08|0.06|0.02|R|F|1993-02-07|1993-04-20|1993-02-24|TAKE BACK RETURN|AIR|ggle slyly furiously regular theodol\n1728|125205|230|1|1|1230.20|0.07|0.04|N|O|1996-09-16|1996-08-19|1996-09-18|COLLECT COD|FOB|lly. carefully ex\n1728|104383|6894|2|23|31909.74|0.05|0.02|N|O|1996-09-08|1996-07-24|1996-09-20|NONE|FOB|ns. pending, final ac\n1728|164065|1614|3|44|49678.64|0.08|0.07|N|O|1996-07-31|1996-06-22|1996-08-06|COLLECT COD|FOB|ide of the slyly blithe\n1728|26347|6348|4|34|43293.56|0.08|0.05|N|O|1996-08-28|1996-07-20|1996-09-12|DELIVER IN PERSON|MAIL|special req\n1728|198088|3127|5|31|36768.48|0.09|0.02|N|O|1996-07-26|1996-06-28|1996-08-14|NONE|REG AIR|kly sly theodolites.\n1729|156948|6949|1|12|24059.28|0.08|0.04|A|F|1992-08-11|1992-07-24|1992-08-16|COLLECT COD|RAIL|y pending packages detect. carefully re\n1730|165429|7946|1|41|61271.22|0.01|0.03|N|O|1998-08-11|1998-08-29|1998-09-02|TAKE BACK RETURN|TRUCK| instructions. unusual, even Tiresi\n1730|161312|1313|2|15|20599.65|0.07|0.04|N|O|1998-09-07|1998-09-12|1998-09-30|TAKE BACK RETURN|AIR|pinto beans cajole. bravely bold\n1730|161029|3546|3|9|9810.18|0.10|0.00|N|O|1998-09-18|1998-09-15|1998-09-21|DELIVER IN PERSON|FOB|gular dependencies wake. blithely final e\n1730|9457|6958|4|40|54658.00|0.02|0.03|N|O|1998-10-02|1998-10-06|1998-10-03|NONE|SHIP|ven dinos slee\n1730|140291|5320|5|43|57245.47|0.04|0.06|N|O|1998-10-26|1998-10-22|1998-11-02|DELIVER IN PERSON|TRUCK|ng deposits cajo\n1731|183932|3933|1|36|72573.48|0.10|0.00|N|O|1996-04-18|1996-04-03|1996-04-29|TAKE BACK RETURN|MAIL|ngside of the even instruct\n1731|138267|3294|2|7|9136.82|0.04|0.07|N|O|1996-04-11|1996-02-13|1996-04-30|DELIVER IN PERSON|REG AIR|fily quick asymptotes\n1731|50920|3426|3|50|93546.00|0.05|0.04|N|O|1996-01-14|1996-03-13|1996-01-29|COLLECT COD|RAIL|ly slyly speci\n1731|195874|3432|4|23|45307.01|0.10|0.04|N|O|1996-04-22|1996-02-25|1996-05-16|TAKE BACK RETURN|RAIL|rays? bold, express pac\n1731|52355|2356|5|37|48371.95|0.10|0.05|N|O|1996-04-30|1996-03-17|1996-05-27|TAKE BACK RETURN|RAIL| beans use furiously slyly b\n1731|123416|953|6|41|59015.81|0.03|0.08|N|O|1996-04-05|1996-02-28|1996-05-01|TAKE BACK RETURN|RAIL|haggle across the blithely ironi\n1732|4397|4398|1|50|65069.50|0.02|0.01|R|F|1993-12-05|1994-01-23|1993-12-20|TAKE BACK RETURN|FOB|fily final asymptotes according \n1732|98183|8184|2|36|42522.48|0.01|0.03|A|F|1994-03-15|1994-02-09|1994-04-02|DELIVER IN PERSON|TRUCK|ve the accounts. slowly ironic multip\n1732|160895|5928|3|41|80191.49|0.00|0.04|R|F|1994-02-20|1994-01-07|1994-02-27|TAKE BACK RETURN|AIR|quests sublate against the silent \n1732|151257|1258|4|9|11774.25|0.04|0.04|A|F|1994-02-25|1994-01-29|1994-03-16|TAKE BACK RETURN|FOB|ular platelets. deposits wak\n1732|168899|1416|5|25|49197.25|0.02|0.05|A|F|1994-02-15|1994-01-07|1994-02-21|COLLECT COD|REG AIR|nag slyly. even, special de\n1732|72930|452|6|16|30446.88|0.01|0.05|R|F|1994-01-07|1994-01-02|1994-01-25|COLLECT COD|SHIP|ix carefully at the furiously regular pac\n1733|110444|2956|1|41|59632.04|0.08|0.01|N|O|1996-06-13|1996-07-08|1996-07-07|TAKE BACK RETURN|AIR|ess notornis. fur\n1733|23938|1445|2|16|29790.88|0.00|0.04|N|O|1996-08-28|1996-07-25|1996-09-27|COLLECT COD|MAIL|slyly express deposits sleep abo\n1733|119673|7207|3|29|49087.43|0.10|0.06|N|O|1996-07-16|1996-08-08|1996-07-28|NONE|TRUCK|ns detect among the special accounts. qu\n1733|135517|544|4|38|58995.38|0.01|0.03|N|O|1996-08-26|1996-07-23|1996-08-28|NONE|FOB| deposits \n1733|33586|8593|5|22|33430.76|0.06|0.07|N|O|1996-07-16|1996-07-24|1996-07-30|COLLECT COD|AIR|gainst the final deposits. carefully final \n1733|65422|5423|6|9|12486.78|0.06|0.08|N|O|1996-05-25|1996-07-23|1996-06-10|COLLECT COD|TRUCK|ven foxes was according to t\n1733|145889|918|7|13|25153.44|0.02|0.03|N|O|1996-08-03|1996-08-02|1996-08-18|NONE|MAIL|olites sleep furious\n1734|154883|7399|1|38|73639.44|0.03|0.03|R|F|1994-08-09|1994-09-07|1994-08-12|COLLECT COD|FOB|ts doubt b\n1734|117726|238|2|4|6974.88|0.06|0.03|A|F|1994-08-20|1994-07-17|1994-08-25|DELIVER IN PERSON|AIR|final warhorses.\n1735|155456|5457|1|43|64992.35|0.02|0.06|A|F|1993-01-14|1993-03-25|1993-02-02|DELIVER IN PERSON|FOB|iously after the \n1735|138514|1028|2|49|76072.99|0.03|0.04|A|F|1992-12-31|1993-02-03|1993-01-25|TAKE BACK RETURN|TRUCK|y express accounts above the exp\n1760|95414|433|1|38|53557.58|0.09|0.03|N|O|1996-06-15|1996-06-29|1996-07-11|NONE|MAIL|tions. blithely regular orbits against the \n1760|7256|7257|2|3|3489.75|0.00|0.06|N|O|1996-07-18|1996-07-01|1996-08-01|NONE|RAIL|lyly bold dolphins haggle carefully. sl\n1760|136818|1845|3|44|81611.64|0.05|0.01|N|O|1996-06-11|1996-06-16|1996-07-02|COLLECT COD|REG AIR|instructions poach slyly ironic theodolites\n1761|51042|8558|1|33|32770.32|0.09|0.03|R|F|1994-01-03|1994-01-23|1994-01-31|NONE|FOB|s. excuses a\n1761|51137|1138|2|37|40260.81|0.02|0.07|R|F|1994-02-17|1994-03-08|1994-03-16|NONE|RAIL| integrate. quickly unusual\n1761|48901|1406|3|37|68446.30|0.06|0.04|R|F|1994-01-02|1994-03-12|1994-01-25|DELIVER IN PERSON|TRUCK|regular packages wake after\n1761|72503|25|4|49|72299.50|0.06|0.07|R|F|1994-01-08|1994-03-03|1994-02-05|TAKE BACK RETURN|FOB|y even packages promise\n1761|156877|9393|5|37|71553.19|0.03|0.04|R|F|1994-04-24|1994-03-14|1994-04-29|TAKE BACK RETURN|MAIL|express requests print blithely around the\n1761|23419|926|6|12|16108.92|0.01|0.05|A|F|1994-04-16|1994-03-08|1994-04-21|DELIVER IN PERSON|AIR| sleep furiously. deposits are acco\n1761|811|5812|7|13|22253.53|0.03|0.08|R|F|1994-03-06|1994-03-18|1994-03-22|DELIVER IN PERSON|TRUCK|ons boost fu\n1762|25243|248|1|15|17523.60|0.04|0.08|A|F|1994-12-18|1994-10-29|1995-01-17|TAKE BACK RETURN|REG AIR|old packages thrash. care\n1762|49506|4515|2|39|56764.50|0.10|0.02|A|F|1994-09-12|1994-11-09|1994-10-08|DELIVER IN PERSON|MAIL| ironic platelets sleep along t\n1762|31865|4369|3|7|12578.02|0.05|0.01|R|F|1994-09-03|1994-10-02|1994-09-10|NONE|REG AIR|uickly express packages wake slyly-- regul\n1762|144131|6646|4|24|28203.12|0.03|0.03|A|F|1994-11-30|1994-11-02|1994-12-20|NONE|REG AIR|accounts solve alongside of the fluffily \n1762|7408|7409|5|49|64454.60|0.08|0.05|A|F|1994-10-20|1994-11-02|1994-11-10|TAKE BACK RETURN|SHIP| packages sleep fluffily pen\n1762|93430|8449|6|35|49820.05|0.05|0.05|A|F|1994-11-25|1994-10-21|1994-11-28|COLLECT COD|AIR|ind quickly. accounts ca\n1762|72971|5479|7|47|91366.59|0.03|0.01|A|F|1994-11-02|1994-10-07|1994-11-08|NONE|SHIP| blithely brave\n1763|11731|6734|1|22|36140.06|0.09|0.06|N|O|1997-01-17|1997-01-15|1997-02-03|TAKE BACK RETURN|SHIP|ld. fluffily final ideas boos\n1763|156496|9012|2|43|66757.07|0.04|0.04|N|O|1996-11-04|1996-12-09|1996-11-28|DELIVER IN PERSON|FOB|r deposits integrate blithely pending, quic\n1763|24137|6640|3|16|16978.08|0.06|0.02|N|O|1996-12-12|1996-12-04|1996-12-25|DELIVER IN PERSON|RAIL|ously pending asymptotes a\n1763|60187|7706|4|44|50475.92|0.04|0.05|N|O|1996-12-04|1997-01-06|1996-12-25|DELIVER IN PERSON|REG AIR| instructions need to integrate deposits. \n1763|146555|9070|5|13|20820.15|0.03|0.05|N|O|1996-11-23|1997-01-24|1996-12-05|TAKE BACK RETURN|SHIP|s sleep carefully. fluffily unusua\n1763|142048|2049|6|3|3270.12|0.05|0.03|N|O|1996-12-10|1996-12-06|1997-01-04|TAKE BACK RETURN|FOB|ut the slyly pending deposi\n1763|183931|1486|7|2|4029.86|0.05|0.07|N|O|1997-02-27|1996-12-04|1997-03-27|COLLECT COD|FOB|even pinto beans snooze fluffi\n1764|120300|301|1|20|26406.00|0.09|0.02|A|F|1992-06-09|1992-05-22|1992-07-06|COLLECT COD|MAIL|y quickly regular packages. car\n1764|66465|1478|2|3|4294.38|0.07|0.07|R|F|1992-05-13|1992-06-07|1992-05-26|COLLECT COD|RAIL|es wake slowly. \n1764|77176|4698|3|27|31135.59|0.07|0.04|A|F|1992-05-06|1992-05-11|1992-05-23|COLLECT COD|TRUCK|ly final foxes wake blithely even requests\n1765|160566|567|1|36|58556.16|0.08|0.04|N|O|1996-03-02|1996-02-17|1996-03-14|DELIVER IN PERSON|SHIP|he blithely pending accou\n1766|86716|6717|1|32|54486.72|0.08|0.01|N|O|1997-01-08|1996-11-11|1997-01-31|TAKE BACK RETURN|AIR|ess accounts. stealthily ironic accou\n1766|33969|6473|2|12|22835.52|0.05|0.01|N|O|1996-10-28|1996-12-18|1996-11-15|DELIVER IN PERSON|AIR|heodolites above the final, regular acc\n1766|110761|8295|3|1|1771.76|0.10|0.02|N|O|1997-01-21|1997-01-07|1997-02-19|NONE|TRUCK|ly blithely pending accounts. reg\n1767|24933|9938|1|32|59453.76|0.08|0.04|A|F|1995-05-22|1995-05-14|1995-05-23|COLLECT COD|SHIP|to the bravely ironic requests i\n1767|41434|8947|2|1|1375.43|0.09|0.05|N|O|1995-06-23|1995-05-25|1995-07-03|TAKE BACK RETURN|RAIL|ing to the slyly fin\n1767|173977|3978|3|24|49223.28|0.06|0.03|R|F|1995-03-16|1995-04-29|1995-04-11|DELIVER IN PERSON|RAIL|luffy theodolites need to detect furi\n1767|22387|4890|4|50|65469.00|0.01|0.02|R|F|1995-05-29|1995-04-14|1995-06-15|NONE|REG AIR|y unusual foxe\n1767|51549|4055|5|40|60021.60|0.06|0.00|R|F|1995-04-16|1995-05-06|1995-04-21|TAKE BACK RETURN|AIR|ep. accounts nag blithely fu\n1792|87191|2208|1|9|10603.71|0.09|0.04|R|F|1994-02-28|1993-12-11|1994-03-12|TAKE BACK RETURN|AIR|final packages s\n1792|8700|6201|2|5|8043.50|0.04|0.02|R|F|1994-02-13|1994-01-03|1994-02-28|DELIVER IN PERSON|TRUCK|ely regular accounts are slyly. pending, bo\n1792|8139|640|3|8|8377.04|0.01|0.04|A|F|1994-02-21|1994-01-26|1994-02-27|DELIVER IN PERSON|RAIL|nts. fluffily special instructions integr\n1792|190135|2655|4|45|55130.85|0.00|0.01|A|F|1994-02-27|1993-12-24|1994-03-07|DELIVER IN PERSON|MAIL|ests are. ironic, regular asy\n1792|198786|3825|5|35|65967.30|0.06|0.05|R|F|1994-01-31|1994-01-20|1994-02-17|NONE|FOB|e against the quic\n1793|47119|9624|1|29|30917.19|0.01|0.06|R|F|1992-10-24|1992-09-20|1992-11-23|NONE|MAIL|ar excuses. \n1793|125194|2731|2|4|4876.76|0.07|0.05|A|F|1992-07-28|1992-08-26|1992-08-21|COLLECT COD|RAIL|nic foxes along the even\n1793|130060|2574|3|6|6540.36|0.01|0.05|R|F|1992-09-21|1992-09-05|1992-10-01|DELIVER IN PERSON|REG AIR|uctions; depo\n1793|117679|5213|4|4|6786.68|0.00|0.08|R|F|1992-09-27|1992-09-21|1992-10-07|DELIVER IN PERSON|AIR|equests nod ac\n1793|24521|4522|5|42|60711.84|0.03|0.03|A|F|1992-10-13|1992-10-02|1992-11-06|NONE|RAIL|uctions sleep carefully special, fl\n1794|167389|7390|1|36|52429.68|0.09|0.08|N|O|1997-11-07|1997-11-01|1997-11-18|TAKE BACK RETURN|FOB|ely fluffily ironi\n1794|94575|9594|2|3|4708.71|0.02|0.03|N|O|1997-11-15|1997-12-16|1997-11-20|DELIVER IN PERSON|FOB| sentiments according to the q\n1794|116160|6161|3|23|27051.68|0.08|0.04|N|O|1997-10-13|1997-11-30|1997-10-28|TAKE BACK RETURN|AIR|usly unusual theodolites doze about \n1794|84627|9644|4|34|54795.08|0.06|0.08|N|O|1997-09-29|1997-11-13|1997-10-07|TAKE BACK RETURN|SHIP|rs above the accoun\n1794|116434|1457|5|47|68170.21|0.10|0.06|N|O|1998-01-15|1997-11-30|1998-02-14|DELIVER IN PERSON|TRUCK| haggle slyly. furiously express orbit\n1794|90185|2695|6|37|43481.66|0.01|0.01|N|O|1998-01-12|1997-12-21|1998-01-17|DELIVER IN PERSON|MAIL|ackages. pinto\n1795|136200|6201|1|44|54392.80|0.08|0.08|A|F|1994-04-28|1994-05-24|1994-05-27|NONE|AIR|ites sleep carefully slyly p\n1795|113154|3155|2|34|39683.10|0.08|0.00|A|F|1994-04-24|1994-06-01|1994-05-08|DELIVER IN PERSON|SHIP|closely regular instructions wake. \n1795|167545|5094|3|25|40313.50|0.07|0.01|A|F|1994-05-18|1994-05-22|1994-05-20|TAKE BACK RETURN|RAIL|he always express accounts ca\n1795|124863|2400|4|32|60411.52|0.03|0.06|R|F|1994-05-10|1994-04-21|1994-05-17|DELIVER IN PERSON|SHIP| asymptotes across the bold,\n1795|162306|9855|5|11|15051.30|0.08|0.02|R|F|1994-06-19|1994-04-24|1994-07-02|TAKE BACK RETURN|TRUCK|slyly. special pa\n1796|9888|9889|1|28|50340.64|0.08|0.04|A|F|1992-12-01|1993-01-01|1992-12-24|DELIVER IN PERSON|FOB|y quickly ironic accounts.\n1796|184834|9871|2|8|15350.64|0.00|0.08|R|F|1993-01-07|1993-01-04|1993-01-10|NONE|SHIP|slyly bold accounts are furiously agains\n1797|30639|3143|1|17|26683.71|0.01|0.02|N|O|1996-08-06|1996-07-11|1996-08-29|NONE|TRUCK| cajole carefully. unusual Tiresias e\n1797|144215|6730|2|16|20147.36|0.01|0.00|N|O|1996-06-03|1996-07-21|1996-06-07|NONE|FOB|o beans wake regular accounts. blit\n1797|11709|6712|3|21|34034.70|0.02|0.01|N|O|1996-08-05|1996-08-05|1996-08-06|DELIVER IN PERSON|AIR|ns. regular, regular deposit\n1798|108858|8859|1|43|80274.55|0.01|0.08|N|O|1997-08-27|1997-10-23|1997-09-09|DELIVER IN PERSON|MAIL|ld packages sleep furiously. depend\n1799|51159|3665|1|8|8881.20|0.04|0.08|R|F|1994-06-14|1994-05-27|1994-06-27|TAKE BACK RETURN|MAIL|ealms upon the special, ironic waters\n1799|26318|3825|2|42|52261.02|0.02|0.02|R|F|1994-04-05|1994-04-28|1994-04-09|DELIVER IN PERSON|FOB|es pending \n1824|119669|7203|1|45|75989.70|0.03|0.02|R|F|1994-08-21|1994-06-21|1994-09-19|NONE|RAIL|ent Tiresias. quickly express \n1824|68198|5717|2|40|46647.60|0.10|0.03|A|F|1994-05-08|1994-07-24|1994-06-06|NONE|FOB|es mold furiously final instructions. s\n1825|155040|71|1|43|47086.72|0.05|0.05|A|F|1994-02-18|1994-02-19|1994-03-02|TAKE BACK RETURN|RAIL| accounts breach fluffily spe\n1825|147333|9848|2|39|53832.87|0.00|0.00|R|F|1994-04-01|1994-01-12|1994-04-21|DELIVER IN PERSON|REG AIR|ual, bold ideas haggle above the quickly ir\n1825|16370|1373|3|7|9004.59|0.04|0.03|A|F|1994-01-02|1994-01-30|1994-01-30|TAKE BACK RETURN|REG AIR|fully ironic requests. requests cajole ex\n1825|120223|5248|4|23|28594.06|0.05|0.01|R|F|1994-01-08|1994-02-08|1994-01-19|NONE|MAIL| wake express, even r\n1825|177840|7841|5|33|63288.72|0.04|0.04|A|F|1993-12-07|1994-03-01|1993-12-16|TAKE BACK RETURN|RAIL|about the ne\n1826|26601|4108|1|4|6110.40|0.06|0.00|R|F|1992-07-05|1992-06-12|1992-08-04|DELIVER IN PERSON|MAIL|alongside of the quickly unusual re\n1826|67102|4621|2|9|9621.90|0.07|0.07|R|F|1992-07-12|1992-07-11|1992-07-15|DELIVER IN PERSON|TRUCK| blithely special\n1826|175212|2764|3|14|18020.94|0.05|0.01|A|F|1992-04-28|1992-05-31|1992-05-25|COLLECT COD|TRUCK|uriously bold pinto beans are carefully ag\n1826|179826|4861|4|6|11434.92|0.05|0.04|R|F|1992-06-30|1992-05-17|1992-07-30|DELIVER IN PERSON|RAIL|kages. blithely silent\n1826|134617|2157|5|46|75974.06|0.05|0.06|R|F|1992-05-02|1992-06-25|1992-05-26|TAKE BACK RETURN|FOB|ously? quickly pe\n1826|107352|2373|6|43|58452.05|0.02|0.03|A|F|1992-07-28|1992-06-14|1992-08-03|NONE|MAIL|ss tithes use even ideas. fluffily final t\n1827|89460|6985|1|47|68124.62|0.00|0.01|N|O|1996-08-01|1996-08-07|1996-08-23|TAKE BACK RETURN|RAIL|. pending courts about the even e\n1827|153091|8122|2|48|54916.32|0.03|0.05|N|O|1996-08-28|1996-09-15|1996-09-01|COLLECT COD|RAIL|oxes. special, final asymptote\n1827|199818|9819|3|37|70958.97|0.01|0.07|N|O|1996-07-20|1996-08-18|1996-08-08|DELIVER IN PERSON|REG AIR|ously ironic theodolites serve quickly af\n1827|126488|4025|4|4|6057.92|0.04|0.04|N|O|1996-07-22|1996-09-10|1996-08-11|DELIVER IN PERSON|RAIL|special requests. blithely\n1827|79528|2036|5|24|36180.48|0.00|0.08|N|O|1996-08-07|1996-09-01|1996-09-04|DELIVER IN PERSON|SHIP|al gifts! re\n1827|20469|470|6|7|9726.22|0.10|0.02|N|O|1996-08-28|1996-08-07|1996-08-31|DELIVER IN PERSON|AIR|egular foxes\n1827|5933|5934|7|38|69879.34|0.05|0.01|N|O|1996-10-17|1996-08-29|1996-11-07|TAKE BACK RETURN|SHIP| blithely. express, bo\n1828|99555|7083|1|33|51300.15|0.05|0.04|R|F|1994-06-27|1994-06-10|1994-07-24|COLLECT COD|FOB|s boost carefully. pending d\n1828|12436|9940|2|40|53937.20|0.08|0.07|R|F|1994-05-05|1994-07-02|1994-05-19|COLLECT COD|REG AIR|s use above the quietly fin\n1828|195860|5861|3|11|21514.46|0.07|0.08|R|F|1994-07-21|1994-05-28|1994-08-13|DELIVER IN PERSON|FOB| wake blithely \n1828|7764|2765|4|45|75229.20|0.02|0.05|R|F|1994-05-15|1994-05-29|1994-05-28|COLLECT COD|RAIL| accounts run slyly \n1828|78153|5675|5|14|15836.10|0.01|0.08|A|F|1994-05-20|1994-06-02|1994-05-25|TAKE BACK RETURN|SHIP|. final packages along the carefully bold\n1829|149926|2441|1|12|23711.04|0.05|0.06|A|F|1994-08-23|1994-07-13|1994-09-04|DELIVER IN PERSON|FOB|ges wake furiously express pinto\n1829|4612|4613|2|11|16682.71|0.04|0.05|A|F|1994-05-18|1994-06-13|1994-06-07|COLLECT COD|MAIL|ding orbits\n1829|103502|8523|3|49|73769.50|0.09|0.08|A|F|1994-08-26|1994-08-01|1994-09-16|NONE|TRUCK|ound the quickly \n1829|152994|2995|4|14|28657.86|0.03|0.06|A|F|1994-08-15|1994-06-08|1994-08-30|TAKE BACK RETURN|AIR|regular deposits alongside of the flu\n1829|165795|8312|5|6|11164.74|0.02|0.07|A|F|1994-08-09|1994-08-05|1994-09-05|DELIVER IN PERSON|MAIL|s haggle! slyl\n1829|114621|2155|6|36|58882.32|0.09|0.04|R|F|1994-06-10|1994-06-23|1994-06-22|NONE|FOB|ackages-- express requests sleep; pen\n1830|119595|2107|1|38|61354.42|0.00|0.07|R|F|1995-04-20|1995-05-22|1995-04-24|TAKE BACK RETURN|TRUCK|ely even a\n1830|24483|6986|2|9|12667.32|0.05|0.07|R|F|1995-03-09|1995-05-24|1995-03-14|NONE|SHIP|st furiously among \n1830|81005|3514|3|36|35496.00|0.07|0.07|R|F|1995-04-21|1995-04-14|1995-05-10|DELIVER IN PERSON|SHIP| slowly unusual orbits. carefull\n1831|135126|7640|1|9|10450.08|0.02|0.03|A|F|1993-12-17|1994-01-27|1993-12-26|NONE|TRUCK|mptotes. furiously regular dolphins al\n1831|47035|7036|2|9|8838.27|0.07|0.06|R|F|1994-03-22|1994-01-07|1994-04-06|COLLECT COD|MAIL|ent deposits. regular saute\n1831|114911|2445|3|17|32740.47|0.02|0.08|R|F|1994-01-18|1994-02-12|1994-01-30|TAKE BACK RETURN|MAIL|s boost ironic foxe\n1831|94173|9192|4|23|26844.91|0.06|0.02|R|F|1993-12-21|1994-02-08|1994-01-04|NONE|SHIP|ests. express pinto beans abou\n1856|54971|9982|1|10|19259.70|0.05|0.07|R|F|1992-05-11|1992-05-20|1992-06-02|TAKE BACK RETURN|FOB|he furiously even theodolites. account\n1856|96908|1927|2|47|89530.30|0.07|0.07|R|F|1992-03-22|1992-06-09|1992-04-17|DELIVER IN PERSON|FOB|ingly blithe theodolites. slyly pending \n1856|116508|4042|3|20|30490.00|0.04|0.06|R|F|1992-05-04|1992-05-06|1992-05-11|DELIVER IN PERSON|MAIL|ost carefully. slyly bold accounts\n1856|149295|9296|4|22|29574.38|0.08|0.02|A|F|1992-05-02|1992-05-26|1992-05-20|TAKE BACK RETURN|REG AIR|platelets detect slyly regular packages. ca\n1856|189673|9674|5|14|24677.38|0.01|0.01|A|F|1992-04-14|1992-05-02|1992-05-11|COLLECT COD|SHIP|ans are even requests. deposits caj\n1856|22677|184|6|36|57588.12|0.03|0.05|A|F|1992-06-19|1992-05-12|1992-06-28|TAKE BACK RETURN|TRUCK|ly even foxes kindle blithely even realm\n1856|129838|7375|7|42|78448.86|0.04|0.00|R|F|1992-05-23|1992-06-06|1992-06-19|COLLECT COD|RAIL|usly final deposits\n1857|173807|3808|1|15|28212.00|0.10|0.03|R|F|1993-04-05|1993-02-28|1993-04-13|COLLECT COD|RAIL|egular, regular inst\n1857|166343|8860|2|40|56373.60|0.10|0.00|R|F|1993-02-15|1993-03-08|1993-02-21|NONE|AIR|slyly close d\n1857|118981|1493|3|8|15999.84|0.01|0.07|R|F|1993-01-27|1993-04-04|1993-02-20|TAKE BACK RETURN|AIR|slyly about the fluffily silent req\n1857|99627|4646|4|41|66691.42|0.07|0.07|A|F|1993-04-16|1993-02-16|1993-04-18|NONE|REG AIR| the slyly\n1858|13932|6434|1|33|60915.69|0.01|0.02|N|O|1997-12-28|1998-02-03|1998-01-13|NONE|RAIL|tect along the slyly final\n1859|74969|4970|1|18|34991.28|0.10|0.00|N|O|1997-08-08|1997-06-30|1997-08-26|TAKE BACK RETURN|SHIP|e carefully a\n1859|187741|5296|2|36|65834.64|0.02|0.01|N|O|1997-05-05|1997-07-08|1997-05-25|TAKE BACK RETURN|REG AIR|regular requests. carefully unusual theo\n1859|157708|5254|3|5|8828.50|0.06|0.03|N|O|1997-06-20|1997-05-20|1997-07-19|TAKE BACK RETURN|AIR|across the p\n1859|190876|877|4|21|41304.27|0.00|0.03|N|O|1997-08-06|1997-05-29|1997-08-26|TAKE BACK RETURN|REG AIR|lar packages wake quickly exp\n1859|45333|7838|5|11|14061.63|0.06|0.06|N|O|1997-07-15|1997-06-05|1997-07-29|TAKE BACK RETURN|SHIP|ffily ironic pac\n1859|104305|6816|6|12|15711.60|0.08|0.03|N|O|1997-05-22|1997-06-08|1997-06-07|COLLECT COD|TRUCK|es. unusual, silent request\n1860|112942|2943|1|9|17594.46|0.04|0.04|N|O|1996-08-03|1996-05-31|1996-08-04|DELIVER IN PERSON|TRUCK|c realms print carefully car\n1861|67093|2106|1|7|7420.63|0.08|0.05|A|F|1994-01-14|1994-04-03|1994-01-16|COLLECT COD|RAIL|s foxes. slyly\n1861|26765|6766|2|31|52444.56|0.10|0.05|R|F|1994-01-29|1994-03-07|1994-02-15|TAKE BACK RETURN|RAIL|arefully unusual\n1861|23676|6179|3|23|36792.41|0.00|0.08|A|F|1994-04-09|1994-03-04|1994-04-11|DELIVER IN PERSON|MAIL|in packages sleep silent dolphins; sly\n1861|115108|2642|4|38|42677.80|0.10|0.05|R|F|1994-02-26|1994-02-05|1994-03-01|NONE|RAIL|pending deposits cajole quic\n1861|15110|113|5|2|2050.22|0.03|0.08|R|F|1994-04-26|1994-03-15|1994-05-15|TAKE BACK RETURN|MAIL|e final, regular requests. carefully \n1862|29629|2132|1|41|63903.42|0.10|0.00|N|O|1998-06-05|1998-05-17|1998-07-04|COLLECT COD|FOB| carefully along\n1862|165414|5415|2|37|54738.17|0.06|0.02|N|O|1998-04-15|1998-05-15|1998-05-14|TAKE BACK RETURN|MAIL|l deposits. carefully even dep\n1862|103981|1512|3|26|51609.48|0.02|0.01|N|O|1998-03-25|1998-05-17|1998-04-17|TAKE BACK RETURN|TRUCK|g carefully: thinly ironic deposits af\n1863|62472|4979|1|48|68854.56|0.09|0.04|A|F|1993-10-10|1993-12-09|1993-10-19|NONE|FOB|ans hinder furiou\n1863|156951|1982|2|48|96381.60|0.04|0.08|A|F|1993-11-08|1993-11-05|1993-12-08|COLLECT COD|AIR|onic theodolites alongside of the pending a\n1888|97643|153|1|27|44297.28|0.03|0.06|R|F|1994-02-13|1994-01-16|1994-02-25|NONE|REG AIR|. carefully special dolphins sle\n1888|73403|3404|2|38|52303.20|0.03|0.03|R|F|1993-11-29|1994-01-16|1993-12-08|TAKE BACK RETURN|TRUCK|dazzle carefull\n1888|79623|9624|3|49|78528.38|0.07|0.05|A|F|1994-02-27|1994-01-14|1994-03-28|DELIVER IN PERSON|FOB|lar accounts haggle carefu\n1888|18538|8539|4|9|13108.77|0.01|0.04|A|F|1994-02-09|1994-01-22|1994-02-19|NONE|AIR| packages are blithely. carefu\n1888|159509|9510|5|4|6274.00|0.03|0.06|R|F|1993-12-28|1993-12-19|1994-01-11|COLLECT COD|FOB|lphins. ironically special theodolit\n1888|52536|7547|6|48|71449.44|0.08|0.08|R|F|1994-02-28|1993-12-16|1994-03-15|COLLECT COD|TRUCK|ar ideas cajole. regular p\n1888|166333|8850|7|50|69966.50|0.04|0.07|R|F|1993-12-22|1994-01-10|1994-01-06|DELIVER IN PERSON|FOB|ependencies affix blithely regular warhors\n1889|151174|8720|1|41|50231.97|0.10|0.02|N|O|1997-06-15|1997-05-10|1997-07-08|NONE|AIR|s! furiously pending r\n1889|171604|1605|2|13|21782.80|0.05|0.00|N|O|1997-06-12|1997-04-28|1997-06-23|NONE|REG AIR|to the regular accounts. carefully express\n1889|137156|2183|3|36|42953.40|0.05|0.07|N|O|1997-05-19|1997-06-14|1997-05-23|NONE|SHIP|l pinto beans kindle \n1889|167656|2689|4|5|8618.25|0.02|0.07|N|O|1997-06-26|1997-06-09|1997-07-21|COLLECT COD|AIR|ording to the blithely silent r\n1890|140281|2796|1|26|34353.28|0.03|0.07|N|O|1997-04-02|1997-03-13|1997-04-22|DELIVER IN PERSON|FOB|ngage. slyly ironic \n1890|99403|9404|2|43|60303.20|0.07|0.03|N|O|1996-12-30|1997-01-31|1997-01-19|DELIVER IN PERSON|FOB|p ironic, express accounts. fu\n1890|58873|6389|3|24|43964.88|0.06|0.04|N|O|1997-02-09|1997-02-10|1997-02-12|COLLECT COD|MAIL|is wake carefully above the even id\n1890|67265|7266|4|43|52987.18|0.09|0.04|N|O|1997-04-08|1997-02-19|1997-04-30|TAKE BACK RETURN|FOB|lyly. instructions across the furiously\n1890|121175|1176|5|45|53827.65|0.08|0.05|N|O|1997-04-15|1997-03-16|1997-04-19|COLLECT COD|FOB|he carefully regular sauternes. ironic fret\n1890|180789|3308|6|16|29916.48|0.08|0.02|N|O|1997-02-13|1997-02-18|1997-03-12|TAKE BACK RETURN|TRUCK|ged pinto beans. regular, regular id\n1890|120205|7742|7|10|12252.00|0.01|0.04|N|O|1996-12-24|1997-02-19|1997-01-01|DELIVER IN PERSON|AIR|. even, unusual inst\n1891|76099|6100|1|45|48379.05|0.07|0.04|A|F|1994-12-20|1995-01-16|1995-01-05|NONE|RAIL|ests along\n1891|183091|5610|2|18|21133.62|0.06|0.00|A|F|1995-01-24|1995-01-29|1995-02-14|NONE|RAIL| foxes above the carefu\n1891|197159|7160|3|15|18842.25|0.03|0.00|R|F|1995-03-11|1995-03-05|1995-03-18|TAKE BACK RETURN|MAIL| accounts are furiou\n1892|112777|5289|1|48|85908.96|0.02|0.01|A|F|1994-06-16|1994-06-16|1994-06-28|NONE|RAIL|tornis detect regul\n1892|42003|9516|2|35|33075.00|0.04|0.08|R|F|1994-04-05|1994-05-09|1994-05-03|NONE|MAIL|hes nod furiously around the instruc\n1892|133278|8305|3|37|48516.99|0.10|0.03|R|F|1994-04-11|1994-06-04|1994-04-24|TAKE BACK RETURN|SHIP|nts. slyly regular asymptot\n1892|196964|9484|4|14|28853.44|0.06|0.07|R|F|1994-04-08|1994-06-12|1994-04-27|DELIVER IN PERSON|FOB|furiously about the furiously\n1893|98266|776|1|43|54363.18|0.10|0.00|N|O|1998-01-25|1998-01-06|1998-02-14|COLLECT COD|SHIP|he carefully regular \n1893|147335|7336|2|49|67734.17|0.03|0.05|N|O|1998-01-19|1998-01-28|1998-02-02|TAKE BACK RETURN|FOB|y final foxes bo\n1893|44119|4120|3|3|3189.33|0.03|0.02|N|O|1998-02-10|1998-01-18|1998-02-25|DELIVER IN PERSON|MAIL|gular, even ideas. fluffily bol\n1893|100605|5626|4|18|28900.80|0.07|0.06|N|O|1998-01-24|1998-01-12|1998-02-13|TAKE BACK RETURN|RAIL|g packages. fluffily final reques\n1893|52070|2071|5|6|6132.42|0.10|0.02|N|O|1998-01-23|1997-12-22|1998-02-09|DELIVER IN PERSON|TRUCK|ar accounts use. daringly ironic packag\n1894|168292|8293|1|40|54411.60|0.03|0.07|R|F|1992-06-07|1992-05-11|1992-07-01|DELIVER IN PERSON|FOB|ily furiously bold packages. flu\n1895|160192|7741|1|43|53844.17|0.09|0.07|R|F|1994-07-26|1994-07-19|1994-08-11|NONE|AIR| carefully eve\n1920|95392|5393|1|24|33297.36|0.04|0.05|N|O|1998-09-27|1998-08-23|1998-10-15|DELIVER IN PERSON|AIR|thely. bold, pend\n1920|50707|5718|2|31|51388.70|0.05|0.06|N|O|1998-08-01|1998-08-30|1998-08-17|COLLECT COD|SHIP|lly. ideas wa\n1920|17189|9691|3|6|6637.08|0.01|0.05|N|O|1998-10-01|1998-08-20|1998-10-24|COLLECT COD|SHIP|l ideas boost slyly pl\n1920|83349|3350|4|50|66617.00|0.09|0.06|N|O|1998-10-03|1998-08-04|1998-10-29|DELIVER IN PERSON|MAIL|e blithely unusual foxes. brave packages\n1920|33789|1299|5|14|24118.92|0.08|0.05|N|O|1998-10-22|1998-08-10|1998-10-27|DELIVER IN PERSON|AIR|ickly ironic d\n1921|20643|5648|1|9|14072.76|0.08|0.00|R|F|1994-02-01|1994-03-20|1994-03-01|DELIVER IN PERSON|FOB|to beans. even excuses integrate specia\n1921|139685|2199|2|21|36218.28|0.02|0.06|R|F|1994-02-08|1994-03-28|1994-02-15|COLLECT COD|FOB|ckly regula\n1921|70822|823|3|27|48406.14|0.00|0.04|A|F|1994-04-26|1994-04-07|1994-04-30|TAKE BACK RETURN|FOB|ing pinto beans above the pend\n1922|9775|4776|1|13|21902.01|0.05|0.03|N|O|1996-10-24|1996-09-21|1996-11-15|NONE|SHIP|quests. furiously\n1923|36198|6199|1|9|10207.71|0.01|0.08|N|O|1997-08-29|1997-09-13|1997-09-07|NONE|FOB|lites. ironic instructions integrate bravel\n1923|177330|9848|2|23|32368.59|0.07|0.05|N|O|1997-09-08|1997-08-11|1997-09-14|TAKE BACK RETURN|MAIL|aggle carefully. furiously permanent\n1923|179628|9629|3|11|18783.82|0.03|0.03|N|O|1997-07-12|1997-09-04|1997-08-01|TAKE BACK RETURN|REG AIR|ages wake slyly about the furiously regular\n1923|192622|5142|4|49|84016.38|0.06|0.05|N|O|1997-07-21|1997-08-08|1997-07-26|NONE|AIR|de of the carefully expre\n1923|183057|8094|5|25|28501.25|0.10|0.08|N|O|1997-08-18|1997-08-20|1997-09-12|DELIVER IN PERSON|TRUCK|the ideas: slyly pendin\n1923|36955|9459|6|50|94597.50|0.03|0.03|N|O|1997-11-04|1997-08-08|1997-11-25|NONE|TRUCK|uickly along the bold courts. bold the\n1924|72567|89|1|7|10776.92|0.06|0.07|N|O|1997-01-01|1996-12-02|1997-01-08|COLLECT COD|SHIP|osits. even accounts nag furious\n1924|17366|4870|2|47|60317.92|0.02|0.06|N|O|1996-11-24|1996-10-18|1996-12-13|COLLECT COD|REG AIR|silent requests cajole blithely final pack\n1924|56788|6789|3|40|69791.20|0.04|0.08|N|O|1996-10-31|1996-11-30|1996-11-21|NONE|REG AIR|ains sleep carefully\n1924|33179|8186|4|31|34477.27|0.03|0.03|N|O|1996-09-20|1996-10-19|1996-10-19|DELIVER IN PERSON|SHIP| the slyly regular foxes. ruthle\n1924|35419|426|5|17|23024.97|0.04|0.05|N|O|1996-12-31|1996-11-12|1997-01-25|COLLECT COD|TRUCK|e carefully theodolites. ironically ironic \n1924|75709|3231|6|15|25270.50|0.02|0.04|N|O|1997-01-04|1996-11-13|1997-01-27|NONE|SHIP|he package\n1924|39531|9532|7|21|30881.13|0.09|0.03|N|O|1996-09-21|1996-11-12|1996-10-02|TAKE BACK RETURN|AIR| blithely reg\n1925|183718|8755|1|50|90085.50|0.01|0.02|R|F|1992-04-12|1992-04-23|1992-05-08|TAKE BACK RETURN|TRUCK|usual pinto\n1925|134273|1813|2|35|45754.45|0.06|0.06|R|F|1992-05-11|1992-04-10|1992-05-14|TAKE BACK RETURN|AIR|counts. carefully ironic packages boost ab\n1925|115014|7526|3|40|41160.40|0.08|0.08|A|F|1992-05-17|1992-05-20|1992-06-08|TAKE BACK RETURN|AIR|e carefully regul\n1925|29757|2260|4|17|28674.75|0.06|0.02|R|F|1992-05-18|1992-04-06|1992-06-16|TAKE BACK RETURN|MAIL|instructions sleep. pinto bea\n1926|50307|2813|1|24|30175.20|0.06|0.05|N|O|1996-05-04|1996-03-14|1996-06-01|DELIVER IN PERSON|RAIL|e theodolites.\n1926|105027|7538|2|29|29928.58|0.09|0.08|N|O|1996-02-26|1996-03-14|1996-03-14|TAKE BACK RETURN|TRUCK|es. dependencies according to the fl\n1926|177645|5197|3|10|17226.40|0.02|0.03|N|O|1996-05-23|1996-03-02|1996-06-04|NONE|AIR|usly bold accounts. express accounts\n1926|67772|7773|4|13|22617.01|0.04|0.02|N|O|1996-04-26|1996-04-13|1996-05-08|DELIVER IN PERSON|MAIL|eans wake bli\n1926|39928|4935|5|29|54169.68|0.06|0.00|N|O|1996-02-29|1996-03-13|1996-03-24|DELIVER IN PERSON|MAIL|hily unusual packages are fluffily am\n1927|67893|2906|1|3|5582.67|0.00|0.05|N|O|1995-10-06|1995-12-08|1995-11-05|COLLECT COD|FOB|ccounts affi\n1927|72462|7477|2|15|21516.90|0.08|0.08|N|O|1995-12-25|1995-12-26|1995-12-31|COLLECT COD|RAIL| carefully regular requests sleep car\n1927|64783|2302|3|6|10486.68|0.05|0.05|N|O|1995-11-29|1995-11-20|1995-12-08|TAKE BACK RETURN|TRUCK|furiously even wat\n1952|52059|7070|1|7|7077.35|0.04|0.05|A|F|1994-05-06|1994-06-11|1994-05-12|NONE|RAIL|about the express, even requ\n1952|141675|6704|2|6|10300.02|0.06|0.05|A|F|1994-05-09|1994-05-21|1994-05-26|DELIVER IN PERSON|AIR|packages haggle. \n1953|127615|5152|1|25|41065.25|0.07|0.06|A|F|1994-01-07|1994-01-28|1994-01-29|TAKE BACK RETURN|RAIL|ular, regular i\n1953|13880|3881|2|35|62785.80|0.06|0.06|R|F|1994-02-03|1994-02-25|1994-02-14|DELIVER IN PERSON|FOB|among the fur\n1954|151270|6301|1|31|40959.37|0.06|0.06|N|O|1997-08-18|1997-07-07|1997-09-03|DELIVER IN PERSON|RAIL|against the packages. bold, ironic e\n1954|181175|6212|2|1|1256.17|0.03|0.01|N|O|1997-09-16|1997-07-08|1997-10-07|COLLECT COD|MAIL|te. furiously final deposits hag\n1954|198665|3704|3|11|19400.26|0.07|0.07|N|O|1997-08-07|1997-07-23|1997-08-25|DELIVER IN PERSON|TRUCK|y carefully ironi\n1954|158218|3249|4|12|15314.52|0.02|0.08|N|O|1997-07-19|1997-07-04|1997-08-06|COLLECT COD|AIR|ongside of the slyly unusual requests. reg\n1954|169702|4735|5|29|51379.30|0.08|0.08|N|O|1997-08-25|1997-07-15|1997-09-02|DELIVER IN PERSON|RAIL|use thinly furiously regular asy\n1954|176272|6273|6|13|17527.51|0.00|0.07|N|O|1997-06-15|1997-08-22|1997-06-20|TAKE BACK RETURN|MAIL|y ironic instructions cajole\n1954|193476|3477|7|49|76904.03|0.05|0.06|N|O|1997-06-04|1997-08-29|1997-06-14|COLLECT COD|TRUCK|eans. final pinto beans sleep furiousl\n1955|136052|8566|1|32|34817.60|0.02|0.02|A|F|1992-07-05|1992-06-29|1992-08-03|TAKE BACK RETURN|TRUCK|g to the carefully sile\n1955|17074|4578|2|2|1982.14|0.03|0.01|R|F|1992-07-06|1992-07-06|1992-08-01|COLLECT COD|TRUCK|ickly aroun\n1955|157697|213|3|41|71942.29|0.08|0.06|A|F|1992-08-01|1992-06-04|1992-08-07|COLLECT COD|AIR| carefully against the furiously reg\n1955|8245|3246|4|16|18451.84|0.03|0.07|A|F|1992-04-30|1992-06-23|1992-05-23|TAKE BACK RETURN|FOB|odolites eat s\n1955|158432|8433|5|11|16394.73|0.09|0.01|A|F|1992-06-03|1992-07-04|1992-06-07|NONE|REG AIR|ously quickly pendi\n1956|176350|6351|1|8|11410.80|0.02|0.04|A|F|1992-12-25|1992-11-24|1993-01-12|TAKE BACK RETURN|AIR|efully about the ironic, ironic de\n1956|102954|5465|2|16|31311.20|0.00|0.05|R|F|1992-11-11|1992-11-11|1992-11-30|NONE|FOB|es cajole blithely. pen\n1956|138920|1434|3|39|76397.88|0.08|0.02|A|F|1992-09-24|1992-11-26|1992-10-15|DELIVER IN PERSON|REG AIR|r theodolites sleep above the b\n1956|28284|8285|4|11|13335.08|0.10|0.00|A|F|1992-12-19|1992-10-29|1993-01-07|TAKE BACK RETURN|AIR| the braids slee\n1956|154900|9931|5|16|31278.40|0.08|0.02|R|F|1992-09-28|1992-10-21|1992-09-30|TAKE BACK RETURN|FOB| wake after the \n1957|78284|792|1|50|63114.00|0.09|0.05|N|O|1998-08-08|1998-09-28|1998-08-27|COLLECT COD|FOB|gainst the re\n1957|118027|539|2|31|32395.62|0.10|0.08|N|O|1998-08-13|1998-08-31|1998-08-16|NONE|REG AIR|express packages maintain fluffi\n1958|72052|7067|1|9|9216.45|0.01|0.05|N|O|1995-12-08|1995-12-17|1995-12-18|DELIVER IN PERSON|REG AIR|ickly. slyly bold \n1958|175065|5066|2|29|33061.74|0.05|0.06|N|O|1996-01-19|1995-12-05|1996-02-14|COLLECT COD|SHIP|d pinto beans\n1958|101773|1774|3|4|7099.08|0.04|0.02|N|O|1995-10-24|1995-12-09|1995-10-28|DELIVER IN PERSON|AIR|he slyly even dependencies \n1958|82603|7620|4|38|60252.80|0.09|0.07|N|O|1995-10-09|1995-11-26|1995-11-05|COLLECT COD|TRUCK|yly. slyly regular courts use silentl\n1958|100912|8443|5|31|59300.21|0.08|0.01|N|O|1995-10-31|1995-11-12|1995-11-07|TAKE BACK RETURN|TRUCK|r deposits c\n1958|16175|1178|6|44|48011.48|0.08|0.04|N|O|1995-12-17|1995-11-30|1996-01-15|TAKE BACK RETURN|RAIL|c theodolites after the unusual deposit\n1958|38399|5909|7|29|38784.31|0.02|0.05|N|O|1995-10-14|1995-11-06|1995-11-01|NONE|REG AIR|final requests nag according to the \n1959|168675|8676|1|46|80208.82|0.04|0.00|N|O|1997-05-05|1997-03-03|1997-05-24|TAKE BACK RETURN|AIR| furiously ex\n1959|119439|4462|2|15|21876.45|0.08|0.07|N|O|1997-01-20|1997-02-18|1997-02-08|DELIVER IN PERSON|MAIL| quickly sp\n1984|52054|9570|1|45|45272.25|0.03|0.04|N|O|1998-04-09|1998-06-11|1998-05-01|COLLECT COD|AIR|p. quickly final ideas sle\n1984|69990|5003|2|35|68599.65|0.01|0.07|N|O|1998-05-18|1998-05-04|1998-06-01|COLLECT COD|RAIL|tes. quickly pending packages haggle boldl\n1985|27157|4664|1|33|35776.95|0.10|0.03|R|F|1994-12-04|1994-11-01|1994-12-05|DELIVER IN PERSON|FOB|s are express packages. pendin\n1985|20393|2896|2|50|65669.50|0.04|0.02|R|F|1994-09-30|1994-10-18|1994-10-12|COLLECT COD|AIR|ate carefully. carefully\n1985|133207|5721|3|20|24804.00|0.07|0.03|R|F|1994-10-29|1994-11-12|1994-11-27|NONE|TRUCK|regular requests. furiously express\n1985|198140|8141|4|30|37144.20|0.05|0.07|R|F|1994-09-06|1994-10-10|1994-09-26|NONE|RAIL|uickly. instr\n1985|123819|6332|5|42|77398.02|0.05|0.05|R|F|1994-10-25|1994-11-03|1994-11-19|DELIVER IN PERSON|SHIP| patterns? final requests after the sp\n1985|19676|4679|6|2|3191.34|0.02|0.00|A|F|1994-11-25|1994-10-09|1994-12-25|TAKE BACK RETURN|FOB| silent inst\n1986|91481|1482|1|12|17669.76|0.06|0.05|A|F|1994-08-17|1994-06-28|1994-09-02|COLLECT COD|RAIL|sleep furiously fluffily final\n1986|104675|7186|2|10|16796.70|0.10|0.03|R|F|1994-05-14|1994-06-21|1994-06-02|COLLECT COD|REG AIR|yly into the carefully even \n1986|62714|5221|3|14|23473.94|0.04|0.02|R|F|1994-07-14|1994-06-19|1994-08-08|NONE|SHIP|the packages. pending, unusual\n1987|15018|2522|1|7|6531.07|0.03|0.03|A|F|1994-07-30|1994-07-06|1994-08-29|NONE|REG AIR| regular a\n1988|71007|6022|1|36|35208.00|0.09|0.04|N|O|1996-01-21|1995-11-24|1996-01-27|NONE|RAIL|gular theodolites. \n1988|198445|6003|2|19|29325.36|0.08|0.08|N|O|1996-02-03|1995-12-10|1996-02-14|COLLECT COD|FOB|lly about the slyly thin instructions. f\n1988|53790|1306|3|8|13950.32|0.06|0.01|N|O|1995-10-20|1995-11-11|1995-11-18|DELIVER IN PERSON|AIR|le quickly ac\n1988|35669|8173|4|27|43325.82|0.08|0.00|N|O|1996-01-27|1995-12-24|1996-02-24|TAKE BACK RETURN|TRUCK|uests. regular requests are according to t\n1988|78175|3190|5|26|29982.42|0.08|0.04|N|O|1996-01-25|1995-12-15|1996-01-26|COLLECT COD|SHIP| ironic dolphins haggl\n1988|85943|3468|6|9|17360.46|0.08|0.03|N|O|1995-12-26|1996-01-02|1996-01-25|DELIVER IN PERSON|MAIL|lar platelets. slyly ironic packa\n1989|9871|7372|1|47|83700.89|0.10|0.02|R|F|1994-06-21|1994-05-27|1994-06-22|TAKE BACK RETURN|REG AIR|final deposits s\n1990|100961|962|1|46|90250.16|0.01|0.07|R|F|1994-12-29|1995-03-14|1995-01-13|NONE|TRUCK|ar sentiments.\n1991|109006|1517|1|39|39585.00|0.06|0.02|A|F|1993-01-01|1992-11-29|1993-01-10|TAKE BACK RETURN|TRUCK|ckages? carefully bold depos\n1991|52810|5316|2|49|86377.69|0.08|0.06|R|F|1992-10-19|1992-11-29|1992-10-25|NONE|SHIP|nd the ideas affi\n1991|173555|3556|3|6|9771.30|0.02|0.01|A|F|1992-11-02|1992-10-08|1992-11-14|TAKE BACK RETURN|REG AIR|hes nag slyly\n1991|137375|2402|4|6|8474.22|0.10|0.06|A|F|1992-11-21|1992-11-03|1992-11-27|NONE|RAIL|uickly blithely final de\n1991|59310|1816|5|49|62196.19|0.06|0.00|R|F|1992-09-10|1992-11-30|1992-10-07|NONE|AIR|quests cajole blithely\n2016|146003|8518|1|2|2098.00|0.02|0.07|N|O|1996-10-12|1996-11-09|1996-10-31|DELIVER IN PERSON|TRUCK|carefully according to the \n2016|62672|191|2|15|24520.05|0.04|0.05|N|O|1996-09-24|1996-10-05|1996-10-21|TAKE BACK RETURN|MAIL|uests haggle carefully furiously regul\n2016|121423|3936|3|8|11555.36|0.09|0.02|N|O|1996-09-19|1996-10-21|1996-10-13|TAKE BACK RETURN|SHIP|mptotes haggle ideas. packages wake flu\n2017|102545|2546|1|49|75829.46|0.10|0.06|N|O|1998-05-26|1998-07-01|1998-06-06|COLLECT COD|TRUCK| after the unusual instructions. sly\n2017|70072|73|2|14|14588.98|0.07|0.04|N|O|1998-06-28|1998-06-15|1998-07-11|NONE|TRUCK|ily final w\n2017|83446|8463|3|11|15723.84|0.05|0.02|N|O|1998-05-22|1998-07-13|1998-05-26|TAKE BACK RETURN|TRUCK|gside of the slyly dogged dolp\n2018|194201|4202|1|2|2590.40|0.02|0.07|N|O|1995-06-25|1995-06-20|1995-07-04|NONE|TRUCK|ly ironic accounts against the slyly sly\n2018|128111|8112|2|23|26199.53|0.05|0.01|R|F|1995-05-05|1995-05-12|1995-05-22|TAKE BACK RETURN|RAIL|ingly even theodolites s\n2019|3228|8229|1|31|35067.82|0.07|0.03|R|F|1992-11-18|1992-12-26|1992-11-24|DELIVER IN PERSON|FOB|l ideas across the slowl\n2019|51852|6863|2|18|32469.30|0.04|0.03|R|F|1993-01-24|1992-12-22|1993-02-02|NONE|MAIL|are carefully furiously regular requ\n2020|33521|1031|1|50|72726.00|0.06|0.01|R|F|1993-07-12|1993-08-28|1993-08-02|COLLECT COD|TRUCK|ts against the pending ideas serve along\n2020|175666|3218|2|40|69666.40|0.09|0.00|A|F|1993-10-17|1993-09-14|1993-10-29|TAKE BACK RETURN|RAIL|ently across the\n2020|13808|1312|3|30|51654.00|0.07|0.04|A|F|1993-09-08|1993-08-11|1993-09-29|TAKE BACK RETURN|AIR|ly about the blithely ironic foxes. bold\n2020|60852|5865|4|27|48946.95|0.05|0.06|A|F|1993-07-14|1993-09-02|1993-08-03|NONE|FOB|e of the bold foxes haggle \n2021|84701|2226|1|7|11799.90|0.08|0.04|N|O|1995-10-17|1995-09-29|1995-10-20|NONE|MAIL| accounts boost blithely. blithely reg\n2021|165276|309|2|19|25484.13|0.04|0.05|N|O|1995-08-14|1995-09-05|1995-08-23|NONE|RAIL| above the slyly fl\n2022|168410|927|1|38|56179.58|0.00|0.08|R|F|1992-07-05|1992-04-20|1992-07-13|TAKE BACK RETURN|REG AIR| against the express accounts wake ca\n2022|54766|7272|2|38|65388.88|0.05|0.04|R|F|1992-06-17|1992-05-15|1992-06-28|COLLECT COD|SHIP|instructions dazzle carefull\n2022|48896|8897|3|48|88554.72|0.10|0.02|A|F|1992-06-14|1992-06-04|1992-07-12|DELIVER IN PERSON|SHIP|counts. slyly enticing accounts are during \n2022|181336|1337|4|16|22677.28|0.05|0.03|R|F|1992-06-23|1992-05-22|1992-07-07|NONE|TRUCK|ages wake slyly care\n2022|99490|9491|5|36|53621.64|0.05|0.02|R|F|1992-03-24|1992-05-07|1992-04-13|NONE|MAIL|ly after the foxes. regular, final inst\n2022|128142|5679|6|20|23402.80|0.08|0.08|A|F|1992-03-31|1992-04-17|1992-04-02|NONE|SHIP|r deposits kindle \n2022|77154|7155|7|13|14704.95|0.06|0.08|R|F|1992-04-04|1992-05-30|1992-04-21|NONE|FOB| orbits haggle fluffily fl\n2023|126248|3785|1|9|11468.16|0.05|0.04|R|F|1992-06-04|1992-06-30|1992-06-10|NONE|AIR|ly regular pinto beans poa\n2023|37736|5246|2|2|3347.46|0.01|0.00|R|F|1992-08-27|1992-07-16|1992-08-29|DELIVER IN PERSON|RAIL|ing packages. fluffily silen\n2023|18539|3542|3|25|36438.25|0.10|0.03|A|F|1992-07-19|1992-07-07|1992-08-15|NONE|REG AIR| wake furiously among the slyly final\n2023|184818|9855|4|9|17125.29|0.02|0.00|A|F|1992-07-23|1992-07-04|1992-08-20|TAKE BACK RETURN|AIR|nts maintain blithely alongside of the\n2023|19435|6939|5|22|29797.46|0.04|0.06|A|F|1992-06-15|1992-07-13|1992-06-21|TAKE BACK RETURN|SHIP|ronic attainments. \n2023|42824|337|6|29|51237.78|0.02|0.06|A|F|1992-08-29|1992-07-28|1992-09-18|COLLECT COD|RAIL|usual instructions. bli\n2023|133971|6485|7|50|100248.50|0.00|0.03|R|F|1992-06-20|1992-07-04|1992-06-23|DELIVER IN PERSON|FOB|its! carefully ex\n2048|34296|6800|1|7|8612.03|0.06|0.01|R|F|1993-12-07|1994-01-31|1994-01-05|TAKE BACK RETURN|REG AIR|lent platelets boost deposits. carefully sp\n2048|7372|4873|2|5|6396.85|0.04|0.04|A|F|1994-01-18|1994-02-01|1994-01-29|TAKE BACK RETURN|TRUCK|affix carefully against \n2048|100536|537|3|12|18438.36|0.01|0.05|R|F|1994-01-28|1994-01-19|1994-02-08|NONE|AIR| even theodoli\n2048|96292|3820|4|11|14171.19|0.10|0.03|R|F|1993-12-20|1994-01-19|1994-01-04|TAKE BACK RETURN|MAIL|totes. idly ironic packages nag\n2049|188067|8068|1|25|28876.50|0.08|0.00|N|O|1996-03-31|1996-02-29|1996-04-15|DELIVER IN PERSON|MAIL| excuses above the \n2049|34609|7113|2|31|47851.60|0.10|0.05|N|O|1995-12-25|1996-02-25|1995-12-29|TAKE BACK RETURN|MAIL| packages are slyly alongside\n2049|66556|9063|3|18|27405.90|0.05|0.05|N|O|1996-01-09|1996-01-22|1996-01-25|TAKE BACK RETURN|AIR| sleep fluffily. dependencies use never\n2049|5410|5411|4|39|51300.99|0.02|0.05|N|O|1996-01-17|1996-01-21|1996-02-03|TAKE BACK RETURN|MAIL|the even pinto beans \n2049|125275|7788|5|30|39008.10|0.04|0.06|N|O|1995-12-16|1996-02-04|1995-12-22|NONE|TRUCK|ial accounts are among the furiously perma\n2049|83083|8100|6|17|18123.36|0.07|0.00|N|O|1996-02-04|1996-03-01|1996-02-24|NONE|FOB|al, regular foxes. pending, \n2050|72622|7637|1|47|74947.14|0.05|0.03|A|F|1994-08-25|1994-07-18|1994-09-15|DELIVER IN PERSON|TRUCK|tside the blithely pending packages eat f\n2050|151587|1588|2|48|78651.84|0.05|0.01|A|F|1994-09-30|1994-08-23|1994-10-29|COLLECT COD|AIR| final packages. pinto\n2050|112099|2100|3|41|45554.69|0.10|0.04|A|F|1994-06-08|1994-08-27|1994-06-23|NONE|AIR| final theodolites. depende\n2050|31521|9031|4|11|15977.72|0.02|0.01|A|F|1994-07-27|1994-08-18|1994-08-02|DELIVER IN PERSON|REG AIR|ns. bold, final ideas cajole among the fi\n2050|167826|7827|5|16|30301.12|0.07|0.01|R|F|1994-08-17|1994-07-28|1994-09-05|DELIVER IN PERSON|REG AIR|al accounts. closely even \n2050|48412|3421|6|29|39451.89|0.00|0.05|A|F|1994-09-23|1994-08-01|1994-10-23|TAKE BACK RETURN|MAIL|oxes alongsid\n2050|47268|9773|7|25|30381.50|0.10|0.00|R|F|1994-08-18|1994-07-04|1994-09-04|TAKE BACK RETURN|RAIL|y according to \n2051|24284|4285|1|43|51956.04|0.08|0.04|N|O|1996-04-22|1996-06-16|1996-04-28|COLLECT COD|RAIL|ounts sleep fluffily even requ\n2051|129342|9343|2|48|65824.32|0.01|0.02|N|O|1996-05-04|1996-06-14|1996-05-19|NONE|TRUCK|unts. pending platelets believe about\n2052|67826|333|1|50|89691.00|0.09|0.08|R|F|1992-06-22|1992-06-03|1992-07-19|DELIVER IN PERSON|AIR|wake after the decoy\n2052|134863|7377|2|35|66425.10|0.09|0.05|A|F|1992-05-29|1992-05-24|1992-06-11|NONE|TRUCK|ts according t\n2052|42203|9716|3|16|18323.20|0.01|0.08|A|F|1992-06-30|1992-07-09|1992-07-12|NONE|SHIP|y final deposits cajole according \n2052|95911|5912|4|47|89624.77|0.08|0.01|A|F|1992-06-18|1992-05-16|1992-07-02|TAKE BACK RETURN|REG AIR|final requests. stealt\n2053|100687|3198|1|20|33753.60|0.09|0.00|A|F|1995-04-25|1995-04-12|1995-05-13|NONE|TRUCK|ly ironic foxes haggle slyly speci\n2053|32300|2301|2|34|41898.20|0.07|0.00|A|F|1995-03-15|1995-03-20|1995-04-09|TAKE BACK RETURN|TRUCK|ions. unusual dependencies\n2053|64279|9292|3|46|57190.42|0.01|0.03|R|F|1995-04-01|1995-04-02|1995-04-18|NONE|RAIL|tions. furiously even requests hagg\n2053|120953|3466|4|31|61192.45|0.06|0.08|R|F|1995-03-23|1995-03-13|1995-04-16|DELIVER IN PERSON|SHIP|ts. fluffily final mul\n2054|112318|2319|1|11|14633.41|0.03|0.05|R|F|1992-08-13|1992-08-26|1992-08-22|NONE|AIR|ular accou\n2054|119303|4326|2|31|40991.30|0.05|0.08|A|F|1992-08-18|1992-09-04|1992-08-24|NONE|FOB|se bold, regular accounts. unusual depos\n2054|120984|985|3|32|64159.36|0.06|0.00|A|F|1992-06-23|1992-07-08|1992-07-22|NONE|FOB| packages thrash. carefully final\n2054|173988|9023|4|14|28867.72|0.10|0.05|R|F|1992-06-25|1992-09-05|1992-07-14|DELIVER IN PERSON|SHIP|uickly final\n2054|5970|971|5|40|75038.80|0.08|0.06|R|F|1992-06-23|1992-08-09|1992-07-04|TAKE BACK RETURN|RAIL|n pinto beans. ironic courts are iro\n2054|133446|5960|6|17|25150.48|0.08|0.01|A|F|1992-06-09|1992-08-28|1992-06-16|NONE|AIR|ges nag acc\n2054|10356|7860|7|4|5065.40|0.00|0.08|R|F|1992-08-12|1992-08-31|1992-08-15|DELIVER IN PERSON|AIR|lyly careful requests wake fl\n2055|44447|4448|1|15|20871.60|0.04|0.06|A|F|1993-09-15|1993-10-06|1993-10-07|NONE|REG AIR|furiously bold \n2055|8279|8280|2|15|17809.05|0.06|0.05|R|F|1993-10-30|1993-11-21|1993-11-22|COLLECT COD|RAIL|gular foxes. b\n2055|134333|6847|3|12|16407.96|0.00|0.02|A|F|1993-10-26|1993-11-23|1993-11-22|COLLECT COD|TRUCK|al pains. acco\n2055|133702|6216|4|16|27771.20|0.02|0.02|A|F|1993-11-16|1993-11-12|1993-11-28|NONE|TRUCK|arefully daringly regular accounts.\n2080|6855|4356|1|5|8809.25|0.08|0.05|R|F|1993-08-26|1993-08-07|1993-09-02|DELIVER IN PERSON|TRUCK|refully unusual theo\n2080|196959|9479|2|39|80182.05|0.07|0.04|A|F|1993-08-22|1993-09-09|1993-08-23|COLLECT COD|FOB|ic deposits haggle slyly carefully eve\n2081|88449|5974|1|26|37373.44|0.03|0.08|N|O|1997-10-21|1997-10-03|1997-11-10|NONE|FOB|among the slyly express accounts. silen\n2081|148309|3338|2|13|17644.90|0.07|0.05|N|O|1997-08-23|1997-08-22|1997-09-09|TAKE BACK RETURN|MAIL|fter the even deposi\n2081|12941|7944|3|32|59326.08|0.09|0.07|N|O|1997-09-05|1997-09-26|1997-10-03|TAKE BACK RETURN|SHIP|e. final, regular dependencies sleep slyly!\n2081|84662|4663|4|23|37873.18|0.03|0.08|N|O|1997-07-06|1997-09-11|1997-07-21|TAKE BACK RETURN|MAIL|ual requests wake blithely above the\n2081|112141|4653|5|19|21909.66|0.02|0.06|N|O|1997-10-01|1997-08-12|1997-10-18|COLLECT COD|SHIP|s affix sometimes express requests. quickly\n2081|141545|4060|6|31|49182.74|0.03|0.06|N|O|1997-09-19|1997-09-13|1997-09-27|NONE|AIR| silent, spe\n2082|74452|1974|1|36|51352.20|0.00|0.00|R|F|1995-01-20|1995-03-18|1995-01-31|COLLECT COD|MAIL|haggle furiously silent pinto beans\n2082|104536|9557|2|12|18486.36|0.08|0.05|A|F|1995-01-27|1995-02-11|1995-02-07|NONE|FOB| ironic instructions. carefull\n2083|23917|8922|1|37|68113.67|0.07|0.00|R|F|1993-09-07|1993-09-30|1993-09-18|TAKE BACK RETURN|MAIL|ng the special foxes wake packages. f\n2084|181162|8717|1|42|52212.72|0.03|0.05|A|F|1993-03-29|1993-05-05|1993-04-22|COLLECT COD|REG AIR|y fluffily even foxes. \n2084|179838|2356|2|23|44110.09|0.09|0.08|A|F|1993-06-05|1993-05-26|1993-06-06|DELIVER IN PERSON|AIR|es against \n2084|135354|2894|3|37|51405.95|0.07|0.05|A|F|1993-07-16|1993-04-20|1993-08-06|NONE|AIR|y careful courts.\n2084|93773|1301|4|9|15900.93|0.02|0.02|A|F|1993-03-18|1993-06-08|1993-03-30|NONE|TRUCK|heaves boost slyly after the pla\n2084|26853|4360|5|28|49835.80|0.07|0.02|R|F|1993-05-04|1993-05-14|1993-05-31|COLLECT COD|TRUCK|cajole quickly carefu\n2084|114964|7476|6|15|29684.40|0.09|0.04|A|F|1993-06-23|1993-04-25|1993-07-23|COLLECT COD|SHIP|tithes. bravely pendi\n2084|193410|5930|7|34|51115.94|0.09|0.02|R|F|1993-06-20|1993-05-28|1993-06-25|DELIVER IN PERSON|RAIL| carefully ironic requests. fluffil\n2085|40196|2701|1|45|51128.55|0.00|0.07|R|F|1994-02-27|1994-01-11|1994-03-29|TAKE BACK RETURN|MAIL|. carefully e\n2086|59829|9830|1|22|39354.04|0.03|0.07|R|F|1994-12-04|1994-12-16|1994-12-20|DELIVER IN PERSON|RAIL|idly busy acc\n2086|140912|8455|2|32|62493.12|0.04|0.06|A|F|1994-11-15|1995-01-05|1994-12-09|TAKE BACK RETURN|TRUCK|e carefully along th\n2086|104938|4939|3|44|85488.92|0.02|0.01|A|F|1994-12-04|1994-11-30|1994-12-21|DELIVER IN PERSON|FOB|latelets s\n2086|83891|8908|4|27|50622.03|0.02|0.00|A|F|1994-11-04|1995-01-14|1994-11-25|COLLECT COD|REG AIR|theodolites haggle blithely blithe p\n2086|155067|98|5|33|37027.98|0.04|0.00|A|F|1995-02-06|1994-11-25|1995-02-15|NONE|SHIP| slyly regular foxes. un\n2086|199165|4204|6|20|25283.20|0.01|0.03|R|F|1994-11-30|1994-12-28|1994-12-07|COLLECT COD|FOB|lithely ironic acc\n2086|155955|3501|7|7|14076.65|0.04|0.05|R|F|1994-12-27|1994-12-10|1995-01-05|COLLECT COD|RAIL| beans haggle car\n2087|126684|6685|1|1|1710.68|0.05|0.04|N|O|1998-03-27|1998-03-24|1998-04-18|DELIVER IN PERSON|REG AIR|the quickly idle acco\n2087|167256|4805|2|46|60869.50|0.10|0.03|N|O|1998-02-24|1998-04-02|1998-03-04|DELIVER IN PERSON|AIR|ter the dolphins.\n2087|61740|1741|3|1|1701.74|0.02|0.05|N|O|1998-05-27|1998-04-11|1998-06-12|COLLECT COD|REG AIR|hely final acc\n2087|58738|6254|4|6|10180.38|0.03|0.08|N|O|1998-04-23|1998-03-27|1998-05-18|DELIVER IN PERSON|REG AIR|dazzle after the slyly si\n2112|70095|96|1|18|19171.62|0.02|0.05|N|O|1997-05-02|1997-03-16|1997-05-25|TAKE BACK RETURN|TRUCK|lphins solve ideas. even, special reque\n2113|122730|5243|1|40|70109.20|0.04|0.06|N|O|1998-01-16|1997-12-11|1998-02-06|TAKE BACK RETURN|TRUCK|bout the quickly ironic t\n2113|111214|8748|2|24|29405.04|0.03|0.02|N|O|1998-02-19|1998-01-08|1998-03-16|COLLECT COD|MAIL|kly regular accounts hinder about the\n2114|167477|7478|1|50|77223.50|0.05|0.05|A|F|1995-02-05|1995-03-18|1995-02-13|COLLECT COD|RAIL|pecial pinto bean\n2114|185076|113|2|26|30187.82|0.02|0.02|A|F|1995-04-30|1995-04-16|1995-05-28|NONE|SHIP|ar asymptotes sleep \n2114|161760|4277|3|25|45544.00|0.07|0.01|A|F|1995-02-15|1995-03-13|1995-02-22|COLLECT COD|AIR|unts. regular, express accounts wake. b\n2115|195180|7700|1|27|34429.86|0.06|0.03|N|O|1998-09-01|1998-07-29|1998-09-04|NONE|AIR|de of the carefully bold accounts \n2115|183023|578|2|43|47558.86|0.06|0.02|N|O|1998-07-14|1998-07-25|1998-07-24|COLLECT COD|FOB| carefully pending requests alongs\n2115|50851|8367|3|3|5405.55|0.03|0.04|N|O|1998-07-23|1998-07-30|1998-08-14|DELIVER IN PERSON|FOB|quickly ironic dolphin\n2115|48123|8124|4|47|50342.64|0.06|0.07|N|O|1998-08-29|1998-07-30|1998-09-05|TAKE BACK RETURN|REG AIR|regular accounts integrate brav\n2115|198956|6514|5|13|26714.35|0.04|0.00|N|O|1998-08-07|1998-08-06|1998-08-13|DELIVER IN PERSON|REG AIR|into beans. even accounts abou\n2116|130050|51|1|2|2160.10|0.00|0.02|R|F|1994-10-16|1994-11-24|1994-11-09|DELIVER IN PERSON|TRUCK|r theodolites use blithely about the ir\n2116|139382|9383|2|47|66804.86|0.10|0.06|R|F|1994-09-01|1994-11-18|1994-09-25|COLLECT COD|MAIL|iously ironic dependencies around the iro\n2116|183247|5766|3|11|14632.64|0.03|0.05|R|F|1994-09-15|1994-10-21|1994-09-21|NONE|FOB| pinto beans. final, final sauternes play \n2117|164531|9564|1|36|57439.08|0.10|0.01|N|O|1997-08-06|1997-07-15|1997-08-07|DELIVER IN PERSON|SHIP|ronic accounts wake\n2117|60193|7712|2|19|21910.61|0.04|0.00|N|O|1997-07-30|1997-06-18|1997-08-13|DELIVER IN PERSON|REG AIR|s between the slyly regula\n2117|57434|2445|3|43|59831.49|0.04|0.03|N|O|1997-06-27|1997-06-12|1997-07-22|DELIVER IN PERSON|SHIP| foxes sleep furiously \n2117|90507|5526|4|24|35940.00|0.00|0.07|N|O|1997-06-15|1997-05-27|1997-06-18|COLLECT COD|SHIP|thely slyly pending platelets. ironic, \n2117|146773|6774|5|3|5459.31|0.02|0.05|N|O|1997-05-05|1997-07-20|1997-05-26|TAKE BACK RETURN|TRUCK|tes cajole\n2117|179|2680|6|27|29137.59|0.09|0.08|N|O|1997-06-30|1997-06-27|1997-07-11|TAKE BACK RETURN|REG AIR| the carefully ironic ideas\n2118|159552|9553|1|24|38677.20|0.10|0.03|N|O|1997-01-06|1996-12-14|1997-01-14|TAKE BACK RETURN|RAIL|about the slyly bold depende\n2118|183278|3279|2|4|5445.08|0.08|0.01|N|O|1996-10-25|1996-11-10|1996-11-22|COLLECT COD|AIR|theodolites affix according \n2118|144382|1925|3|11|15690.18|0.05|0.04|N|O|1996-12-23|1996-12-20|1997-01-01|COLLECT COD|RAIL|y ironic accounts sleep upon the packages. \n2119|101751|6772|1|36|63099.00|0.04|0.00|N|O|1996-11-10|1996-10-25|1996-12-03|TAKE BACK RETURN|RAIL|ly bold foxes. ironic accoun\n2144|91477|9005|1|33|48459.51|0.00|0.07|R|F|1994-04-04|1994-06-20|1994-04-23|NONE|AIR| ironic excuses haggle final dependencies. \n2144|50884|3390|2|46|84404.48|0.03|0.08|R|F|1994-04-08|1994-04-29|1994-05-07|COLLECT COD|SHIP| foxes haggle blithel\n2144|3496|8497|3|29|40585.21|0.00|0.07|R|F|1994-05-03|1994-05-16|1994-06-01|DELIVER IN PERSON|FOB|ns wake carefully carefully ironic\n2144|157171|7172|4|10|12281.70|0.00|0.04|R|F|1994-06-16|1994-05-03|1994-07-05|COLLECT COD|AIR| furiously unusual ideas. carefull\n2145|77370|9878|1|13|17515.81|0.04|0.05|A|F|1992-11-12|1992-12-13|1992-12-07|TAKE BACK RETURN|MAIL|alongside of the slyly final\n2145|153894|1440|2|6|11687.34|0.05|0.01|A|F|1992-10-10|1992-11-29|1992-10-14|NONE|AIR|s. fluffily express accounts sleep. slyl\n2146|56891|9397|1|42|77611.38|0.10|0.01|A|F|1992-09-21|1992-11-02|1992-09-23|NONE|AIR|ns according to the doggedly \n2146|156627|9143|2|6|10101.72|0.07|0.05|A|F|1993-01-03|1992-10-24|1993-01-24|DELIVER IN PERSON|RAIL|ing to the requests. dependencies boost \n2146|24361|1868|3|14|17995.04|0.03|0.01|R|F|1992-09-16|1992-10-16|1992-09-20|COLLECT COD|SHIP|ecial, express a\n2146|25952|3459|4|31|58216.45|0.02|0.00|A|F|1993-01-04|1992-10-24|1993-01-15|DELIVER IN PERSON|TRUCK|lly even deposit\n2146|168542|6091|5|28|45095.12|0.02|0.05|R|F|1993-01-03|1992-10-17|1993-01-08|COLLECT COD|MAIL|r accounts sleep furio\n2146|70175|7697|6|32|36645.44|0.07|0.03|R|F|1993-01-10|1992-10-19|1993-02-05|COLLECT COD|TRUCK|y regular foxes wake among the final\n2146|24278|4279|7|39|46888.53|0.07|0.06|R|F|1993-01-05|1992-11-06|1993-01-14|DELIVER IN PERSON|TRUCK|uickly regular excuses detect. regular c\n2147|28768|3773|1|50|84838.00|0.04|0.06|R|F|1992-11-18|1992-11-30|1992-11-30|NONE|RAIL|al accounts. even, even foxes wake\n2147|100604|605|2|4|6418.40|0.01|0.04|A|F|1992-09-27|1992-11-15|1992-10-22|NONE|AIR|mong the blithely special\n2147|43483|8492|3|34|48500.32|0.10|0.04|R|F|1992-11-29|1992-11-08|1992-12-22|TAKE BACK RETURN|REG AIR|egular deposits hang car\n2147|10236|5239|4|11|12608.53|0.06|0.07|A|F|1992-09-27|1992-11-16|1992-10-16|NONE|AIR| the fluffily\n2148|115933|3467|1|21|40927.53|0.09|0.01|R|F|1995-05-28|1995-05-26|1995-06-15|NONE|FOB|deposits ag\n2149|18213|5717|1|12|13574.52|0.05|0.07|R|F|1993-06-01|1993-05-06|1993-06-11|TAKE BACK RETURN|TRUCK|riously bl\n2149|98974|8975|2|10|19729.70|0.06|0.01|R|F|1993-06-09|1993-04-17|1993-06-16|DELIVER IN PERSON|TRUCK|eposits sleep above\n2149|48678|3687|3|47|76453.49|0.00|0.04|R|F|1993-06-27|1993-05-12|1993-07-11|COLLECT COD|AIR|hely final depo\n2149|128105|3130|4|18|20395.80|0.06|0.00|A|F|1993-04-05|1993-05-11|1993-04-23|DELIVER IN PERSON|REG AIR|uriously final pac\n2149|59360|4371|5|22|29025.92|0.06|0.04|R|F|1993-05-24|1993-04-23|1993-06-20|TAKE BACK RETURN|SHIP|ptotes sleep along the blithely ir\n2150|77886|2901|1|26|48460.88|0.00|0.03|A|F|1994-06-21|1994-08-05|1994-06-23|NONE|TRUCK|. always unusual packages\n2150|17738|5242|2|29|48016.17|0.04|0.03|A|F|1994-09-02|1994-08-04|1994-10-02|TAKE BACK RETURN|RAIL|y ironic theodolites. foxes ca\n2150|106769|1790|3|29|51497.04|0.04|0.08|R|F|1994-06-10|1994-07-31|1994-06-26|COLLECT COD|RAIL|arefully final att\n2150|53727|1243|4|39|65548.08|0.05|0.02|R|F|1994-07-31|1994-08-17|1994-08-11|TAKE BACK RETURN|TRUCK|ess accounts nag. unusual asymptotes haggl\n2150|182496|2497|5|35|55247.15|0.01|0.01|A|F|1994-09-27|1994-08-17|1994-10-13|COLLECT COD|RAIL|refully pending dependen\n2150|6331|8832|6|12|14847.96|0.09|0.03|A|F|1994-08-27|1994-08-22|1994-09-18|COLLECT COD|AIR|press platelets haggle until the slyly fi\n2151|166944|4493|1|23|46251.62|0.06|0.02|N|O|1996-11-20|1996-12-17|1996-11-30|DELIVER IN PERSON|AIR| silent dependencies about the slyl\n2151|14480|6982|2|29|40439.92|0.00|0.02|N|O|1997-03-04|1996-12-27|1997-03-21|TAKE BACK RETURN|SHIP| bold packages acro\n2151|164838|9871|3|49|93238.67|0.07|0.01|N|O|1997-01-20|1997-02-09|1997-02-18|NONE|FOB| packages. f\n2151|17670|2673|4|28|44454.76|0.10|0.08|N|O|1996-12-11|1996-12-26|1996-12-12|DELIVER IN PERSON|AIR|y special packages. carefully ironic instru\n2176|190213|5252|1|38|49521.98|0.02|0.08|R|F|1992-11-29|1993-01-14|1992-12-22|DELIVER IN PERSON|REG AIR|lithely ironic pinto beans. furious\n2176|94137|9156|2|14|15835.82|0.00|0.06|A|F|1992-11-17|1993-01-07|1992-12-03|DELIVER IN PERSON|SHIP|ely ironic platelets \n2176|159466|9467|3|25|38136.50|0.02|0.02|R|F|1993-02-23|1993-01-05|1993-03-07|COLLECT COD|RAIL| ruthless deposits according to the ent\n2176|142226|7255|4|2|2536.44|0.05|0.06|A|F|1993-02-26|1993-01-08|1993-03-23|DELIVER IN PERSON|AIR|s pinto beans\n2177|128562|8563|1|45|71575.20|0.02|0.01|N|O|1997-02-11|1997-02-27|1997-02-17|NONE|SHIP|. theodolites haggle carefu\n2177|138448|962|2|27|40133.88|0.04|0.08|N|O|1997-01-29|1997-03-20|1997-02-04|DELIVER IN PERSON|SHIP|even, regula\n2177|80066|2575|3|23|24059.38|0.07|0.05|N|O|1997-01-28|1997-03-02|1997-02-13|DELIVER IN PERSON|AIR|he silent foxes. iro\n2177|54933|7439|4|34|64189.62|0.05|0.07|N|O|1997-02-03|1997-04-10|1997-02-21|COLLECT COD|REG AIR|tes are doggedly quickly\n2177|56437|3953|5|46|64097.78|0.09|0.05|N|O|1997-05-10|1997-02-23|1997-05-28|COLLECT COD|RAIL|ending asymptotes.\n2177|121616|4129|6|11|18013.71|0.02|0.04|N|O|1997-03-20|1997-03-07|1997-04-09|DELIVER IN PERSON|MAIL|gainst the ca\n2178|156127|1158|1|15|17746.80|0.10|0.01|N|O|1997-03-27|1997-03-10|1997-04-18|NONE|REG AIR|l accounts. quickly expr\n2178|15804|8306|2|27|46434.60|0.01|0.02|N|O|1997-02-26|1997-02-19|1997-03-25|NONE|MAIL| across the ironic reques\n2178|4825|2326|3|40|69192.80|0.00|0.03|N|O|1997-03-17|1997-02-09|1997-04-15|COLLECT COD|RAIL|foxes are slowly regularly specia\n2178|77639|5161|4|3|4849.89|0.07|0.07|N|O|1997-04-07|1997-01-23|1997-04-18|COLLECT COD|MAIL| permanentl\n2179|129479|4504|1|22|33186.34|0.05|0.08|N|O|1996-11-16|1996-11-03|1996-11-25|DELIVER IN PERSON|FOB|lphins cajole acr\n2179|138056|570|2|20|21881.00|0.03|0.01|N|O|1996-09-30|1996-11-10|1996-10-30|NONE|REG AIR|ncies. fin\n2179|103363|8384|3|5|6831.80|0.03|0.02|N|O|1996-11-09|1996-10-08|1996-11-11|DELIVER IN PERSON|REG AIR|ts haggle blithely. ironic, careful theodol\n2179|5267|2768|4|24|28134.24|0.04|0.04|N|O|1996-10-26|1996-11-05|1996-11-16|COLLECT COD|RAIL| cajole carefully. \n2179|107337|4868|5|7|9410.31|0.00|0.02|N|O|1996-10-24|1996-11-14|1996-11-21|TAKE BACK RETURN|RAIL|gular dependencies. ironic packages haggle\n2180|15125|128|1|31|32243.72|0.06|0.04|N|O|1996-10-20|1996-11-21|1996-11-06|COLLECT COD|REG AIR|n requests are furiously at the quickly\n2180|192230|9788|2|39|51566.97|0.01|0.00|N|O|1997-01-03|1996-10-29|1997-01-25|NONE|RAIL|ep furiously furiously final request\n2180|196985|9505|3|24|49967.52|0.03|0.00|N|O|1997-01-03|1996-10-24|1997-01-19|NONE|SHIP|uriously f\n2180|110249|2761|4|47|59184.28|0.07|0.02|N|O|1996-09-23|1996-12-08|1996-10-12|NONE|FOB|pending, regular ideas. iron\n2180|142825|368|5|23|42959.86|0.02|0.06|N|O|1996-11-08|1996-10-25|1996-11-28|NONE|TRUCK|ggle alongside of the fluffily speci\n2180|54140|4141|6|48|52518.72|0.09|0.03|N|O|1996-12-30|1996-11-22|1997-01-16|DELIVER IN PERSON|RAIL|nic instructions haggle careful\n2181|177848|7849|1|4|7703.36|0.05|0.04|N|O|1995-09-25|1995-11-12|1995-09-28|COLLECT COD|FOB|tes. slyly silent packages use along th\n2181|87791|2808|2|46|81824.34|0.00|0.02|N|O|1995-11-28|1995-10-17|1995-12-26|COLLECT COD|AIR|osits. final packages sleep\n2181|90740|741|3|15|25961.10|0.08|0.05|N|O|1995-10-05|1995-10-27|1995-11-03|DELIVER IN PERSON|FOB|e above the fluffily regul\n2181|54662|9673|4|28|45266.48|0.04|0.05|N|O|1995-12-21|1995-10-23|1996-01-04|TAKE BACK RETURN|AIR|s excuses sleep car\n2181|95783|5784|5|9|16009.02|0.06|0.07|N|O|1996-01-05|1995-12-05|1996-01-08|COLLECT COD|TRUCK|ward the quietly even requests. ir\n2182|131671|9211|1|27|45972.09|0.02|0.07|R|F|1994-05-10|1994-07-04|1994-06-04|DELIVER IN PERSON|SHIP|en platele\n2182|189760|7315|2|3|5549.28|0.05|0.03|R|F|1994-04-20|1994-07-04|1994-04-24|TAKE BACK RETURN|SHIP|y bold theodolites wi\n2182|93343|5853|3|34|45435.56|0.02|0.03|R|F|1994-05-28|1994-06-02|1994-06-10|COLLECT COD|MAIL| slow tithes. ironi\n2182|6068|3569|4|12|11688.72|0.04|0.07|A|F|1994-05-08|1994-06-02|1994-05-09|COLLECT COD|REG AIR|ments are fu\n2182|178869|1387|5|37|72070.82|0.06|0.02|A|F|1994-04-08|1994-06-29|1994-04-18|TAKE BACK RETURN|TRUCK|ges. blithely ironic\n2183|70665|3173|1|29|47434.14|0.05|0.01|N|O|1996-07-21|1996-08-24|1996-08-15|TAKE BACK RETURN|RAIL|ly unusual deposits sleep carefully\n2183|51094|1095|2|25|26127.25|0.06|0.02|N|O|1996-07-06|1996-08-21|1996-08-05|NONE|RAIL|he quickly f\n2208|57232|2243|1|48|57083.04|0.08|0.07|A|F|1995-05-13|1995-06-30|1995-05-20|COLLECT COD|MAIL|sits. idly permanent request\n2208|96798|4326|2|11|19742.69|0.08|0.01|A|F|1995-05-06|1995-07-19|1995-05-22|COLLECT COD|TRUCK|ding waters lose. furiously regu\n2208|73389|5897|3|41|55857.58|0.08|0.02|N|O|1995-08-18|1995-06-19|1995-09-05|COLLECT COD|RAIL|nd the furious, express dependencies.\n2208|42342|9855|4|50|64217.00|0.07|0.07|N|F|1995-06-11|1995-05-31|1995-06-29|TAKE BACK RETURN|FOB|al foxes will hav\n2208|29570|2073|5|43|64481.51|0.03|0.06|A|F|1995-05-10|1995-06-02|1995-06-09|TAKE BACK RETURN|MAIL|es. accounts cajole. fi\n2208|166229|3778|6|18|23313.96|0.02|0.08|R|F|1995-06-06|1995-06-10|1995-06-11|TAKE BACK RETURN|TRUCK|packages are quickly bold de\n2208|6106|1107|7|45|45544.50|0.00|0.08|A|F|1995-05-05|1995-06-10|1995-05-11|NONE|SHIP|e fluffily regular theodolites caj\n2209|22676|7681|1|40|63946.80|0.05|0.01|R|F|1992-11-01|1992-09-25|1992-11-08|DELIVER IN PERSON|SHIP|ully special sheaves serve\n2209|102771|2772|2|10|17737.70|0.00|0.02|R|F|1992-09-02|1992-09-24|1992-09-21|DELIVER IN PERSON|AIR|players. carefully reg\n2209|63521|8534|3|11|16329.72|0.01|0.01|A|F|1992-07-12|1992-08-24|1992-08-10|DELIVER IN PERSON|REG AIR|express, regular pinto be\n2209|180897|3416|4|39|77137.71|0.08|0.07|R|F|1992-11-04|1992-09-02|1992-11-11|TAKE BACK RETURN|MAIL|ly around the final packages. deposits ca\n2209|123364|901|5|24|33296.64|0.08|0.06|R|F|1992-08-09|1992-08-18|1992-08-25|COLLECT COD|AIR| along the bol\n2209|177413|2448|6|7|10432.87|0.09|0.07|A|F|1992-08-18|1992-09-09|1992-09-12|DELIVER IN PERSON|AIR| quickly regular pack\n2210|77764|2779|1|36|62703.36|0.10|0.00|A|F|1992-03-04|1992-03-24|1992-03-21|DELIVER IN PERSON|AIR| requests wake enticingly final\n2211|47822|2831|1|25|44245.50|0.04|0.01|A|F|1994-10-09|1994-08-04|1994-11-03|TAKE BACK RETURN|RAIL|deas. carefully special theodolites along\n2211|139773|2287|2|40|72510.80|0.09|0.06|A|F|1994-09-30|1994-09-10|1994-10-26|NONE|MAIL|posits among the express dolphins\n2211|159820|7366|3|25|46995.50|0.00|0.07|A|F|1994-08-13|1994-08-17|1994-08-16|NONE|AIR|ly regular, express\n2211|84711|9728|4|23|39001.33|0.03|0.02|R|F|1994-10-05|1994-09-13|1994-10-17|DELIVER IN PERSON|AIR|ependencies \n2211|134301|6815|5|3|4005.90|0.02|0.04|A|F|1994-08-28|1994-09-10|1994-09-06|TAKE BACK RETURN|SHIP|pendencies after the regular f\n2211|186189|6190|6|18|22953.24|0.05|0.08|A|F|1994-08-31|1994-09-07|1994-09-22|NONE|TRUCK|c grouches. slyly express pinto \n2211|78139|647|7|3|3351.39|0.06|0.05|R|F|1994-09-21|1994-08-10|1994-10-19|TAKE BACK RETURN|RAIL|y slyly final\n2212|70810|5825|1|18|32054.58|0.07|0.06|R|F|1994-06-22|1994-06-18|1994-06-25|TAKE BACK RETURN|FOB| cajole. final, pending ideas should are bl\n2213|117158|4692|1|20|23503.00|0.01|0.00|A|F|1993-01-21|1993-04-14|1993-01-29|COLLECT COD|REG AIR|iously express accounts; \n2213|59646|9647|2|4|6422.56|0.09|0.05|R|F|1993-04-15|1993-04-15|1993-05-05|COLLECT COD|SHIP| affix carefully furiously \n2213|69541|7060|3|1|1510.54|0.05|0.05|A|F|1993-04-25|1993-04-06|1993-04-28|TAKE BACK RETURN|AIR|s along the ironic reques\n2213|173204|8239|4|39|49810.80|0.09|0.05|A|F|1993-05-12|1993-04-07|1993-05-23|TAKE BACK RETURN|SHIP|the blithely \n2213|37976|2983|5|43|82300.71|0.04|0.03|A|F|1993-04-18|1993-03-11|1993-05-11|TAKE BACK RETURN|RAIL|r packages are along the carefully bol\n2213|47675|180|6|41|66529.47|0.01|0.00|R|F|1993-01-31|1993-03-31|1993-02-28|COLLECT COD|FOB| carefully pend\n2213|63836|1355|7|3|5399.49|0.02|0.04|A|F|1993-03-09|1993-03-17|1993-04-07|TAKE BACK RETURN|AIR|o wake. ironic platel\n2214|75623|638|1|27|43162.74|0.04|0.04|N|O|1998-05-31|1998-06-07|1998-06-19|DELIVER IN PERSON|REG AIR|x fluffily along the even packages-- \n2214|193950|3951|2|50|102197.50|0.00|0.02|N|O|1998-07-06|1998-06-16|1998-07-16|TAKE BACK RETURN|MAIL|accounts. blith\n2214|112406|4918|3|42|59572.80|0.04|0.08|N|O|1998-05-26|1998-07-13|1998-06-22|COLLECT COD|FOB|ons. deposi\n2214|195389|428|4|22|32656.36|0.01|0.01|N|O|1998-05-30|1998-07-02|1998-06-09|DELIVER IN PERSON|RAIL|t the blithely\n2215|72044|9566|1|33|33529.32|0.00|0.00|N|O|1996-07-19|1996-08-10|1996-07-30|COLLECT COD|RAIL|dolites cajole b\n2215|32526|5030|2|30|43755.60|0.01|0.00|N|O|1996-08-15|1996-09-10|1996-08-25|NONE|FOB|ckages caj\n2215|56652|9158|3|30|48259.50|0.07|0.03|N|O|1996-09-09|1996-07-20|1996-09-28|COLLECT COD|TRUCK|against the carefu\n2215|145112|7627|4|20|23142.20|0.02|0.02|N|O|1996-09-09|1996-08-10|1996-09-19|NONE|MAIL| unusual deposits haggle carefully. ide\n2240|163898|6415|1|6|11771.34|0.01|0.00|A|F|1992-06-23|1992-05-17|1992-07-20|COLLECT COD|AIR|ymptotes boost. furiously bold p\n2240|27897|5404|2|37|67520.93|0.03|0.07|R|F|1992-03-16|1992-05-31|1992-04-05|COLLECT COD|FOB| quickly after the packages? blithely si\n2240|52103|9619|3|39|41148.90|0.08|0.06|A|F|1992-05-22|1992-05-10|1992-06-08|NONE|FOB|y orbits. final depos\n2240|85867|884|4|10|18528.60|0.09|0.00|A|F|1992-05-25|1992-04-14|1992-06-23|DELIVER IN PERSON|REG AIR|are across the ironic packages.\n2240|160522|3039|5|29|45893.08|0.02|0.06|A|F|1992-03-29|1992-05-08|1992-04-09|COLLECT COD|MAIL|lyly even ideas w\n2240|80721|722|6|32|54455.04|0.06|0.06|R|F|1992-04-11|1992-04-18|1992-04-22|NONE|MAIL|ss thinly deposits. blithely bold package\n2240|77504|2519|7|24|35556.00|0.04|0.05|R|F|1992-05-13|1992-04-09|1992-05-14|DELIVER IN PERSON|FOB|ng the silent accounts. slyly ironic t\n2241|4034|4035|1|25|23450.75|0.00|0.08|R|F|1993-08-11|1993-07-23|1993-09-01|DELIVER IN PERSON|MAIL| final deposits use fluffily. even f\n2241|194616|9655|2|38|65003.18|0.04|0.06|A|F|1993-08-04|1993-07-31|1993-08-06|TAKE BACK RETURN|TRUCK| silent, unusual d\n2241|96673|1692|3|48|80144.16|0.08|0.04|A|F|1993-05-14|1993-07-30|1993-05-26|TAKE BACK RETURN|RAIL|ss accounts engage furiously. slyly even re\n2241|166305|1338|4|19|26054.70|0.10|0.00|A|F|1993-06-01|1993-08-05|1993-06-07|TAKE BACK RETURN|TRUCK| are furiously quickl\n2241|81485|1486|5|2|2932.96|0.04|0.03|A|F|1993-08-16|1993-08-02|1993-08-24|NONE|REG AIR|, express deposits. pear\n2241|115401|424|6|22|31160.80|0.02|0.08|R|F|1993-08-13|1993-06-15|1993-08-16|DELIVER IN PERSON|TRUCK|, ironic depen\n2241|141592|1593|7|9|14702.31|0.09|0.03|A|F|1993-05-14|1993-07-12|1993-05-29|NONE|AIR|lyly final \n2242|122099|2100|1|15|16816.35|0.09|0.08|N|O|1997-08-04|1997-09-21|1997-08-11|COLLECT COD|FOB|its. carefully express packages cajole. bli\n2243|126988|6989|1|10|20149.80|0.04|0.06|N|O|1995-07-26|1995-07-18|1995-08-03|NONE|RAIL|express, daring foxes affix fur\n2244|50086|5097|1|3|3108.24|0.02|0.02|A|F|1993-04-30|1993-03-15|1993-05-19|TAKE BACK RETURN|FOB| beans for the regular platel\n2244|192426|7465|2|16|24294.72|0.01|0.06|R|F|1993-02-12|1993-03-09|1993-02-28|COLLECT COD|FOB|rate around the reques\n2245|75664|5665|1|44|72145.04|0.03|0.03|A|F|1993-06-12|1993-06-10|1993-06-16|NONE|TRUCK|refully even sheaves\n2245|73630|8645|2|28|44901.64|0.05|0.03|R|F|1993-08-19|1993-07-27|1993-09-04|COLLECT COD|TRUCK|e requests sleep furiou\n2245|85310|7819|3|33|42745.23|0.03|0.01|R|F|1993-06-26|1993-06-11|1993-07-17|TAKE BACK RETURN|AIR|ing to the carefully ruthless accounts\n2245|188474|8475|4|14|21874.58|0.02|0.04|R|F|1993-05-06|1993-07-21|1993-05-19|DELIVER IN PERSON|RAIL|nts. always unusual dep\n2245|79264|6786|5|33|41027.58|0.03|0.07|R|F|1993-06-16|1993-06-05|1993-07-07|NONE|MAIL| across the express reques\n2246|52330|2331|1|22|28211.26|0.02|0.01|N|O|1996-07-25|1996-08-03|1996-08-24|DELIVER IN PERSON|SHIP|ructions wake carefully fina\n2246|103090|3091|2|43|47002.87|0.07|0.06|N|O|1996-08-25|1996-08-23|1996-09-19|DELIVER IN PERSON|AIR|ainst the ironic theodolites haggle fi\n2246|17158|4662|3|11|11826.65|0.10|0.00|N|O|1996-06-21|1996-07-24|1996-07-18|TAKE BACK RETURN|TRUCK|quests alongside o\n2246|162721|270|4|13|23188.36|0.08|0.05|N|O|1996-09-15|1996-07-21|1996-10-08|DELIVER IN PERSON|AIR|equests. fluffily special epitaphs use\n2247|171922|4440|1|12|23927.04|0.02|0.07|A|F|1992-09-06|1992-09-18|1992-09-26|NONE|MAIL|final accounts. requests across the furiou\n2272|89989|9990|1|18|35621.64|0.04|0.00|R|F|1993-08-01|1993-07-06|1993-08-25|NONE|MAIL|ons along the blithely e\n2272|33466|976|2|40|55978.40|0.07|0.00|A|F|1993-04-25|1993-07-12|1993-05-15|DELIVER IN PERSON|FOB|lithely ir\n2272|55426|7932|3|36|49731.12|0.03|0.02|A|F|1993-05-25|1993-05-23|1993-06-09|TAKE BACK RETURN|RAIL|about the ironic packages; quickly iron\n2272|137602|116|4|30|49188.00|0.09|0.07|A|F|1993-07-27|1993-05-15|1993-08-13|NONE|RAIL|quests at the foxes haggle evenly pack\n2272|75858|3380|5|12|22006.20|0.03|0.03|A|F|1993-04-19|1993-05-14|1993-04-23|NONE|RAIL| accounts cajole. quickly b\n2273|183596|8633|1|34|57106.06|0.02|0.03|N|O|1997-01-08|1997-02-02|1997-01-23|COLLECT COD|MAIL| furiously carefully bold de\n2273|84638|9655|2|35|56792.05|0.00|0.05|N|O|1997-01-02|1997-01-19|1997-01-14|NONE|REG AIR|arefully f\n2273|94138|9157|3|8|9057.04|0.00|0.04|N|O|1996-12-15|1997-02-27|1997-01-10|NONE|FOB|dependencies. slyly ir\n2273|160895|8444|4|20|39117.80|0.06|0.04|N|O|1997-03-05|1997-02-25|1997-04-01|NONE|RAIL|cuses. quickly enticing requests wake \n2273|161026|8575|5|18|19566.36|0.07|0.00|N|O|1996-12-16|1997-01-21|1997-01-03|COLLECT COD|TRUCK| beans. doggedly final packages wake\n2273|154366|1912|6|16|22725.76|0.10|0.03|N|O|1997-01-10|1997-02-03|1997-02-01|TAKE BACK RETURN|RAIL|furiously above the ironic requests. \n2273|19493|9494|7|7|9887.43|0.05|0.05|N|O|1997-02-19|1997-01-22|1997-02-21|TAKE BACK RETURN|TRUCK|ts. furiou\n2274|11038|3540|1|18|17082.54|0.04|0.03|R|F|1993-09-06|1993-12-03|1993-09-22|COLLECT COD|SHIP|usly final re\n2274|110510|5533|2|23|34971.73|0.04|0.03|R|F|1993-10-28|1993-11-03|1993-11-05|NONE|MAIL|kly special warhorse\n2274|128795|8796|3|18|32828.22|0.03|0.06|R|F|1993-09-28|1993-11-22|1993-10-12|DELIVER IN PERSON|SHIP| express packages. even accounts hagg\n2275|33183|8190|1|30|33485.40|0.08|0.05|R|F|1993-01-10|1992-11-21|1993-01-22|NONE|REG AIR|re slyly slyly special idea\n2275|90399|5418|2|11|15283.29|0.08|0.03|A|F|1993-01-16|1992-12-10|1993-01-25|COLLECT COD|REG AIR|ost across the never express instruction\n2276|118185|5719|1|5|6015.90|0.07|0.08|N|O|1996-05-09|1996-06-18|1996-05-13|DELIVER IN PERSON|FOB|ias instea\n2276|134871|2411|2|13|24776.31|0.08|0.04|N|O|1996-07-24|1996-06-18|1996-08-16|COLLECT COD|RAIL|arefully ironic foxes cajole q\n2276|170995|996|3|27|55781.73|0.07|0.08|N|O|1996-07-30|1996-06-10|1996-07-31|DELIVER IN PERSON|RAIL|the carefully unusual accoun\n2276|108060|5591|4|38|40586.28|0.06|0.03|N|O|1996-07-07|1996-06-28|1996-07-17|COLLECT COD|RAIL|ans. pinto beans boost c\n2276|152402|9948|5|50|72720.00|0.03|0.05|N|O|1996-07-13|1996-06-25|1996-07-22|DELIVER IN PERSON|REG AIR| accounts dete\n2276|5041|7542|6|4|3784.16|0.10|0.03|N|O|1996-07-05|1996-06-30|1996-08-04|COLLECT COD|FOB|s. deposits \n2277|136849|6850|1|38|71661.92|0.03|0.07|R|F|1995-04-23|1995-03-25|1995-05-20|TAKE BACK RETURN|TRUCK|fully bold\n2277|7914|415|2|2|3643.82|0.10|0.08|A|F|1995-02-01|1995-02-04|1995-03-02|TAKE BACK RETURN|AIR|endencies sleep idly pending p\n2277|197832|352|3|4|7719.32|0.05|0.06|R|F|1995-04-27|1995-03-16|1995-04-29|TAKE BACK RETURN|SHIP|. quickly unusual deposi\n2277|158467|3498|4|31|47289.26|0.02|0.00|R|F|1995-03-07|1995-03-19|1995-03-26|TAKE BACK RETURN|MAIL|ic instructions detect ru\n2278|44247|6752|1|36|42884.64|0.04|0.05|N|O|1998-06-04|1998-06-06|1998-06-30|NONE|TRUCK|y ironic pinto beans br\n2278|44868|7373|2|50|90643.00|0.02|0.00|N|O|1998-08-09|1998-07-08|1998-09-05|DELIVER IN PERSON|RAIL|into beans. blit\n2278|96343|8853|3|22|29465.48|0.03|0.00|N|O|1998-05-15|1998-07-14|1998-06-04|TAKE BACK RETURN|REG AIR|ep regular accounts. blithely even\n2279|13676|3677|1|12|19076.04|0.07|0.08|A|F|1993-05-10|1993-03-25|1993-06-02|COLLECT COD|REG AIR|lets across the excuses nag quickl\n2279|40972|973|2|38|72692.86|0.08|0.07|R|F|1993-06-09|1993-04-06|1993-06-26|COLLECT COD|TRUCK|s above the furiously express dep\n2279|3443|5944|3|3|4039.32|0.09|0.04|A|F|1993-05-31|1993-05-07|1993-06-05|COLLECT COD|REG AIR|ing foxes above the even accounts use slyly\n2279|51132|8648|4|42|45491.46|0.02|0.00|R|F|1993-02-28|1993-04-25|1993-03-02|TAKE BACK RETURN|REG AIR| above the furiously ironic deposits. \n2279|168844|1361|5|9|17215.56|0.05|0.04|R|F|1993-05-21|1993-03-29|1993-06-17|DELIVER IN PERSON|MAIL|ns cajole after the final platelets. s\n2279|146161|1190|6|12|14485.92|0.02|0.00|R|F|1993-05-04|1993-04-26|1993-05-28|DELIVER IN PERSON|FOB|ccounts. slyl\n2279|118915|6449|7|32|61885.12|0.05|0.05|A|F|1993-04-20|1993-05-22|1993-05-18|DELIVER IN PERSON|RAIL|re quickly. furiously ironic ide\n2304|199490|2010|1|42|66758.58|0.00|0.01|A|F|1994-01-20|1994-03-04|1994-02-05|COLLECT COD|RAIL|quests are blithely alongside of\n2304|18186|5690|2|48|53000.64|0.00|0.00|R|F|1994-02-12|1994-02-16|1994-03-10|COLLECT COD|REG AIR| deposits cajole blithely e\n2304|47934|7935|3|3|5645.79|0.00|0.05|R|F|1994-03-19|1994-03-04|1994-03-20|DELIVER IN PERSON|AIR|l excuses after the ev\n2305|173979|6497|1|3|6158.91|0.00|0.01|A|F|1993-03-24|1993-04-05|1993-03-29|NONE|AIR|kages haggle quickly across the blithely \n2305|59018|1524|2|39|38103.39|0.07|0.00|R|F|1993-04-16|1993-04-17|1993-04-22|COLLECT COD|MAIL|ms after the foxes \n2305|101013|1014|3|32|32448.32|0.03|0.06|A|F|1993-04-02|1993-03-18|1993-04-03|NONE|AIR| haggle caref\n2305|111463|1464|4|17|25065.82|0.00|0.05|A|F|1993-02-21|1993-03-30|1993-03-19|TAKE BACK RETURN|MAIL| carefully alongside of \n2305|154380|1926|5|26|37293.88|0.06|0.07|A|F|1993-05-14|1993-02-28|1993-06-04|NONE|SHIP|arefully final theodo\n2305|50969|8485|6|7|13439.72|0.06|0.00|R|F|1993-05-15|1993-04-25|1993-06-09|DELIVER IN PERSON|RAIL|gular deposits boost about the foxe\n2306|195761|800|1|50|92838.00|0.09|0.01|N|O|1995-07-27|1995-09-26|1995-08-06|DELIVER IN PERSON|FOB|y quickly \n2306|148556|3585|2|39|62577.45|0.04|0.00|N|O|1995-09-07|1995-09-13|1995-10-03|COLLECT COD|SHIP|f the slyly unusual accounts. furiousl\n2306|177684|5236|3|35|61658.80|0.01|0.07|N|O|1995-08-18|1995-08-30|1995-08-20|TAKE BACK RETURN|RAIL|raids along the furiously unusual asympto\n2306|118936|1448|4|21|41053.53|0.06|0.01|N|O|1995-10-07|1995-09-18|1995-10-17|COLLECT COD|MAIL| ironic pinto \n2306|141746|4261|5|42|75085.08|0.04|0.07|N|O|1995-09-05|1995-08-25|1995-09-28|COLLECT COD|MAIL|furiously final acco\n2306|123507|3508|6|29|44384.50|0.00|0.03|N|O|1995-11-01|1995-09-01|1995-11-22|DELIVER IN PERSON|REG AIR|uld have to mold. s\n2306|175400|2952|7|19|28032.60|0.07|0.01|N|O|1995-11-17|1995-09-06|1995-11-30|DELIVER IN PERSON|AIR|tainments nag furiously carefull\n2307|141367|3882|1|24|33800.64|0.10|0.05|R|F|1993-10-07|1993-08-05|1993-10-20|COLLECT COD|AIR|stealthily special packages nag a\n2307|139516|2030|2|2|3111.02|0.01|0.00|A|F|1993-09-21|1993-08-22|1993-10-03|COLLECT COD|SHIP|ously. furiously furious requ\n2307|33306|5810|3|7|8675.10|0.07|0.04|R|F|1993-08-03|1993-09-04|1993-08-28|DELIVER IN PERSON|AIR|ven instructions wake fluffily \n2307|164357|4358|4|19|27005.65|0.08|0.06|R|F|1993-10-23|1993-09-09|1993-11-09|TAKE BACK RETURN|TRUCK|olites haggle furiously around the \n2307|142685|2686|5|7|12093.76|0.01|0.06|R|F|1993-09-01|1993-08-08|1993-09-29|NONE|AIR| packages cajo\n2308|117514|7515|1|24|36756.24|0.06|0.04|R|F|1993-02-23|1992-12-24|1993-03-10|NONE|MAIL|ts sleep. busy excuses along the s\n2308|55630|641|2|36|57082.68|0.05|0.06|A|F|1992-11-11|1992-11-27|1992-11-23|NONE|MAIL|ong the pending hockey players. blithe\n2309|169856|4889|1|14|26961.90|0.10|0.03|N|O|1996-01-01|1995-10-22|1996-01-23|NONE|AIR|asymptotes. furiously pending acco\n2309|168253|770|2|1|1321.25|0.01|0.05|N|O|1995-12-08|1995-11-03|1995-12-31|COLLECT COD|RAIL|eposits alongside of the final re\n2309|14764|9767|3|5|8393.80|0.01|0.00|N|O|1995-12-10|1995-10-29|1996-01-06|TAKE BACK RETURN|SHIP|s. requests wake blithely specia\n2309|138046|3073|4|46|49865.84|0.08|0.04|N|O|1995-10-02|1995-10-30|1995-10-30|NONE|REG AIR|sly according to the carefully \n2309|136205|3745|5|9|11170.80|0.00|0.07|N|O|1995-12-21|1995-10-10|1996-01-20|COLLECT COD|AIR|ding, unusual instructions. dep\n2309|194880|9919|6|21|41472.48|0.09|0.00|N|O|1995-11-05|1995-11-07|1995-11-22|NONE|AIR|unts around the dolphins ar\n2309|137744|258|7|48|85523.52|0.03|0.05|N|O|1995-10-21|1995-11-21|1995-11-09|NONE|MAIL|ccounts. id\n2310|57879|385|1|36|66127.32|0.03|0.03|N|O|1996-10-09|1996-10-28|1996-10-29|TAKE BACK RETURN|RAIL|iously against the slyly special accounts\n2310|170444|445|2|6|9086.64|0.07|0.01|N|O|1996-11-08|1996-12-09|1996-12-07|COLLECT COD|REG AIR|e slyly about the quickly ironic theodo\n2310|41031|8544|3|48|46657.44|0.08|0.02|N|O|1996-10-04|1996-11-20|1996-10-25|TAKE BACK RETURN|FOB|ep slyly alongside of the \n2311|140068|2583|1|18|19945.08|0.01|0.01|N|F|1995-06-11|1995-06-18|1995-07-02|NONE|FOB| fluffily even patterns haggle blithely. re\n2311|121572|6597|2|49|78084.93|0.09|0.02|R|F|1995-05-14|1995-07-11|1995-05-20|COLLECT COD|FOB|ideas sleep\n2311|53793|3794|3|15|26201.85|0.08|0.04|N|O|1995-06-23|1995-06-06|1995-07-09|COLLECT COD|AIR|ve the blithely pending accounts. furio\n2311|89740|4757|4|42|72649.08|0.01|0.06|R|F|1995-06-03|1995-06-27|1995-06-11|DELIVER IN PERSON|MAIL|gle furiously. bold \n2311|46446|1455|5|1|1392.44|0.05|0.02|A|F|1995-06-07|1995-06-20|1995-06-10|NONE|AIR|ptotes. furiously regular theodolite\n2311|11993|6996|6|32|60959.68|0.01|0.03|N|O|1995-07-19|1995-06-26|1995-07-26|NONE|RAIL|sts along the slyly\n2336|192161|4681|1|20|25063.20|0.01|0.03|N|O|1996-03-12|1996-02-25|1996-03-18|NONE|REG AIR|across the fi\n2337|44792|7297|1|49|85102.71|0.06|0.05|N|O|1997-08-08|1997-08-15|1997-08-31|TAKE BACK RETURN|FOB| along the packages. furiously p\n2338|51054|6065|1|30|30151.50|0.07|0.06|N|O|1997-12-10|1997-10-15|1997-12-11|TAKE BACK RETURN|REG AIR|ould have to nag quickly\n2339|191542|1543|1|22|35937.88|0.03|0.03|A|F|1994-01-06|1994-03-06|1994-01-10|NONE|FOB| furiously above \n2339|29709|2212|2|28|45883.60|0.00|0.00|R|F|1994-01-25|1994-01-22|1994-01-28|DELIVER IN PERSON|RAIL|e bold, even packag\n2339|116749|1772|3|13|22954.62|0.06|0.08|R|F|1994-03-10|1994-02-18|1994-03-20|TAKE BACK RETURN|REG AIR|ges. blithely special depend\n2340|137788|5328|1|9|16432.02|0.08|0.02|N|O|1996-05-01|1996-02-24|1996-05-16|COLLECT COD|RAIL|. carefully ironic\n2340|192904|5424|2|21|41934.90|0.06|0.02|N|O|1996-01-17|1996-03-04|1996-01-29|DELIVER IN PERSON|SHIP| asymptotes. unusual theo\n2341|46009|1018|1|12|11460.00|0.08|0.03|R|F|1993-06-06|1993-07-08|1993-06-17|DELIVER IN PERSON|FOB|. quickly final deposits sl\n2341|70364|5379|2|37|49371.32|0.07|0.08|A|F|1993-09-23|1993-07-25|1993-10-14|DELIVER IN PERSON|RAIL|was blithel\n2341|194541|9580|3|8|13084.32|0.03|0.07|R|F|1993-06-08|1993-07-09|1993-06-10|COLLECT COD|FOB|ns affix above the iron\n2342|41169|8682|1|12|13321.92|0.00|0.08|N|O|1996-07-31|1996-07-26|1996-08-14|NONE|TRUCK|print blithely even deposits. carefull\n2342|116462|8974|2|24|35483.04|0.10|0.06|N|O|1996-09-30|1996-07-22|1996-10-28|TAKE BACK RETURN|AIR|nstructions c\n2342|169920|9921|3|50|99496.00|0.10|0.01|N|O|1996-08-28|1996-07-18|1996-09-22|COLLECT COD|RAIL|cial asymptotes pr\n2342|35809|5810|4|1|1744.80|0.04|0.06|N|O|1996-08-31|1996-08-09|1996-09-07|DELIVER IN PERSON|REG AIR|ffily. unusual pinto beans wake c\n2342|26756|9259|5|22|37020.50|0.08|0.01|N|O|1996-08-10|1996-08-02|1996-08-31|DELIVER IN PERSON|AIR|s. ironic \n2343|109899|9900|1|27|51540.03|0.00|0.00|N|O|1995-11-10|1995-11-17|1995-12-10|TAKE BACK RETURN|SHIP|old theodolites.\n2343|65555|3074|2|35|53219.25|0.03|0.06|N|O|1995-10-24|1995-11-09|1995-10-26|COLLECT COD|TRUCK|ges haggle furiously carefully regular req\n2343|178754|6306|3|21|38487.75|0.00|0.03|N|O|1995-09-07|1995-10-26|1995-10-07|TAKE BACK RETURN|RAIL|osits. unusual theodolites boost furio\n2368|151986|1987|1|16|32607.68|0.04|0.03|R|F|1993-10-31|1993-10-22|1993-11-06|NONE|REG AIR|telets wake carefully iro\n2368|13052|3053|2|32|30881.60|0.03|0.00|R|F|1993-09-23|1993-10-07|1993-09-27|COLLECT COD|TRUCK|gular courts use blithely around the\n2368|148805|1320|3|39|72298.20|0.08|0.03|R|F|1993-09-03|1993-09-20|1993-09-28|COLLECT COD|RAIL|ng the doggedly ironic requests are blithe\n2368|155900|3446|4|17|33250.30|0.10|0.08|A|F|1993-10-03|1993-09-27|1993-10-05|NONE|FOB|fily. slyly final ideas alongside o\n2369|23834|1341|1|30|52734.90|0.05|0.04|N|O|1997-04-23|1997-02-12|1997-05-21|COLLECT COD|REG AIR|pecial deposits sleep. blithely unusual w\n2369|168416|8417|2|47|69767.27|0.10|0.02|N|O|1997-01-02|1997-02-18|1997-01-13|COLLECT COD|RAIL| to the regular dep\n2370|45041|7546|1|3|2958.12|0.03|0.07|R|F|1994-03-24|1994-03-26|1994-04-15|COLLECT COD|SHIP|ly regular Tiresia\n2370|1051|3552|2|24|22849.20|0.00|0.05|A|F|1994-05-15|1994-04-09|1994-06-12|NONE|REG AIR|final depen\n2370|60844|845|3|32|57754.88|0.05|0.02|A|F|1994-04-24|1994-03-03|1994-05-15|DELIVER IN PERSON|MAIL|ies since the final deposits\n2370|5044|2545|4|21|19929.84|0.04|0.01|R|F|1994-02-01|1994-02-19|1994-02-09|TAKE BACK RETURN|MAIL|ecial dependencies must have to \n2371|158603|3634|1|37|61479.20|0.05|0.05|N|O|1998-02-11|1998-03-24|1998-02-27|DELIVER IN PERSON|TRUCK|s boost fluffil\n2371|34147|6651|2|21|22703.94|0.00|0.05|N|O|1998-04-14|1998-02-14|1998-04-18|COLLECT COD|AIR|gle furiously regu\n2371|100521|3032|3|11|16736.72|0.05|0.02|N|O|1998-02-25|1998-04-06|1998-03-23|TAKE BACK RETURN|TRUCK|requests. regular pinto beans wake. car\n2371|42076|7085|4|33|33596.31|0.05|0.08|N|O|1998-03-30|1998-02-06|1998-04-05|DELIVER IN PERSON|AIR|deas are. express r\n2371|164508|9541|5|22|34595.00|0.02|0.05|N|O|1998-03-26|1998-03-19|1998-04-16|DELIVER IN PERSON|REG AIR|y daring accounts. regular ins\n2371|85027|5028|6|39|39468.78|0.05|0.03|N|O|1998-04-01|1998-03-13|1998-04-27|NONE|REG AIR|tructions. regular, stealthy packages wak\n2371|35714|8218|7|32|52790.72|0.07|0.07|N|O|1998-02-15|1998-04-03|1998-02-23|NONE|REG AIR|the ruthless accounts. \n2372|42661|2662|1|42|67353.72|0.08|0.02|N|O|1998-01-04|1998-01-02|1998-02-02|COLLECT COD|REG AIR|lar packages. regular\n2372|2319|9820|2|17|20762.27|0.07|0.01|N|O|1997-12-17|1998-01-17|1997-12-25|NONE|RAIL|xcuses. slyly ironic theod\n2372|163333|8366|3|12|16755.96|0.04|0.04|N|O|1998-03-21|1997-12-21|1998-04-12|DELIVER IN PERSON|SHIP|lyly according to\n2372|121677|6702|4|4|6794.68|0.00|0.07|N|O|1997-12-14|1997-12-28|1997-12-16|TAKE BACK RETURN|REG AIR|e carefully blithely even epitaphs. r\n2372|19420|4423|5|5|6697.10|0.02|0.04|N|O|1998-02-08|1998-01-18|1998-03-02|TAKE BACK RETURN|RAIL|ets against the \n2372|188437|5992|6|11|16779.73|0.02|0.06|N|O|1998-02-14|1998-01-18|1998-03-10|TAKE BACK RETURN|FOB| silent, pending de\n2372|56012|6013|7|19|18392.19|0.01|0.06|N|O|1997-12-26|1998-02-19|1998-01-02|COLLECT COD|SHIP| beans haggle sometimes\n2373|190025|7583|1|17|18955.34|0.02|0.01|R|F|1994-03-29|1994-05-19|1994-04-20|COLLECT COD|AIR|auternes. blithely even pinto bea\n2373|135214|2754|2|3|3747.63|0.08|0.08|R|F|1994-05-15|1994-06-10|1994-06-04|COLLECT COD|TRUCK|dependencies wake ironical\n2373|140804|3319|3|29|53499.20|0.05|0.02|A|F|1994-06-01|1994-05-14|1994-06-17|NONE|TRUCK|yly silent ideas affix furiousl\n2373|90692|8220|4|5|8413.45|0.10|0.01|R|F|1994-06-02|1994-05-03|1994-06-21|NONE|REG AIR|uffily blithely ironic requests\n2374|117116|9628|1|41|46457.51|0.07|0.00|A|F|1994-01-27|1993-12-11|1994-02-12|TAKE BACK RETURN|RAIL|heodolites. requests\n2374|159783|7329|2|24|44226.72|0.07|0.08|A|F|1994-02-02|1994-01-12|1994-02-04|DELIVER IN PERSON|TRUCK|. requests are above t\n2374|60682|5695|3|2|3285.36|0.06|0.02|R|F|1993-12-30|1994-01-24|1994-01-02|COLLECT COD|FOB|, unusual ideas. deposits cajole quietl\n2374|73993|3994|4|28|55075.72|0.04|0.08|R|F|1994-02-19|1993-12-16|1994-03-15|COLLECT COD|MAIL|ets cajole fu\n2374|309|310|5|25|30232.50|0.08|0.00|A|F|1993-11-26|1993-12-15|1993-12-10|COLLECT COD|RAIL|refully pending d\n2375|167272|7273|1|3|4017.81|0.02|0.08|N|O|1997-02-14|1996-12-25|1997-02-22|COLLECT COD|RAIL|slyly across the furiously e\n2375|131282|3796|2|9|11819.52|0.09|0.02|N|O|1997-02-17|1996-12-27|1997-02-27|DELIVER IN PERSON|MAIL|ly against the packages. bold pinto bean\n2375|46470|8975|3|26|36828.22|0.02|0.06|N|O|1997-03-18|1997-02-02|1997-03-29|TAKE BACK RETURN|TRUCK|rate across the\n2375|4801|7302|4|5|8529.00|0.01|0.00|N|O|1997-01-31|1997-01-25|1997-02-22|COLLECT COD|REG AIR|final packages cajole according to the furi\n2375|87210|4735|5|42|50282.82|0.01|0.08|N|O|1997-01-24|1997-02-15|1997-02-07|DELIVER IN PERSON|FOB|apades. idea\n2375|125524|5525|6|20|30990.40|0.09|0.08|N|O|1996-12-01|1996-12-26|1996-12-19|TAKE BACK RETURN|SHIP|ckages! blithely enticing deposi\n2400|102546|5057|1|48|74329.92|0.01|0.02|N|O|1998-10-07|1998-08-30|1998-11-03|DELIVER IN PERSON|MAIL|fore the car\n2400|89966|2475|2|1|1955.96|0.04|0.07|N|O|1998-08-18|1998-09-12|1998-09-11|NONE|MAIL|silent deposits serve furious\n2400|52503|19|3|23|33476.50|0.02|0.08|N|O|1998-08-05|1998-08-28|1998-08-30|NONE|SHIP|tions. fluffily ironic platelets cajole c\n2400|16820|4324|4|23|39946.86|0.09|0.04|N|O|1998-10-04|1998-10-04|1998-10-31|NONE|RAIL|ages lose carefully around the regula\n2401|181615|4134|1|39|66167.79|0.00|0.03|N|O|1997-09-29|1997-10-21|1997-10-17|DELIVER IN PERSON|FOB|ould affix \n2401|2221|7222|2|49|55037.78|0.05|0.07|N|O|1997-09-02|1997-09-11|1997-09-13|TAKE BACK RETURN|AIR|lites cajole carefully \n2402|85533|8042|1|43|65296.79|0.03|0.08|N|O|1996-09-17|1996-11-20|1996-09-22|DELIVER IN PERSON|RAIL|slyly slyly blithe sheaves\n2402|151328|8874|2|24|33103.68|0.02|0.05|N|O|1996-11-21|1996-10-19|1996-11-29|DELIVER IN PERSON|SHIP|as; blithely ironic requ\n2403|82868|393|1|34|62929.24|0.04|0.07|N|O|1998-05-30|1998-06-19|1998-06-05|NONE|REG AIR| slyly bold re\n2403|151901|9447|2|19|37105.10|0.08|0.07|N|O|1998-04-20|1998-07-02|1998-05-13|DELIVER IN PERSON|FOB|sits. ironic in\n2403|192434|2435|3|27|41213.61|0.05|0.03|N|O|1998-07-27|1998-07-08|1998-08-03|NONE|SHIP|deposits sleep slyly special theodolit\n2403|30422|423|4|30|40572.60|0.05|0.06|N|O|1998-08-08|1998-06-17|1998-08-20|NONE|TRUCK|ackages sleep furiously pendin\n2404|146703|1732|1|36|62989.20|0.07|0.00|N|O|1997-03-27|1997-05-16|1997-04-06|COLLECT COD|REG AIR|s nag furi\n2404|35641|8145|2|1|1576.64|0.02|0.04|N|O|1997-05-22|1997-06-06|1997-05-28|DELIVER IN PERSON|MAIL|from the final orbits? even pinto beans hag\n2404|17378|2381|3|41|53110.17|0.02|0.06|N|O|1997-06-12|1997-05-03|1997-07-12|NONE|AIR| dolphins are\n2404|56668|6669|4|19|30868.54|0.09|0.03|N|O|1997-05-07|1997-05-24|1997-05-24|TAKE BACK RETURN|SHIP|cuses. quickly even in\n2404|3058|8059|5|18|17298.90|0.00|0.04|N|O|1997-06-25|1997-05-06|1997-07-02|NONE|RAIL|packages. even requests according to \n2405|88560|3577|1|18|27874.08|0.09|0.07|N|O|1997-01-23|1997-03-10|1997-02-03|COLLECT COD|REG AIR|carefully ironic accounts. slyly \n2405|26169|3676|2|30|32854.80|0.10|0.08|N|O|1997-03-24|1997-03-10|1997-04-14|TAKE BACK RETURN|AIR|y final deposits are slyly caref\n2405|16135|6136|3|49|51505.37|0.00|0.06|N|O|1996-12-24|1997-03-23|1997-01-01|TAKE BACK RETURN|FOB|cial requests. ironic, regu\n2405|176791|9309|4|23|42959.17|0.08|0.05|N|O|1996-12-28|1997-01-29|1997-01-07|NONE|AIR|t wake blithely blithely regular idea\n2406|169075|6624|1|18|20593.26|0.07|0.05|N|O|1997-02-17|1996-12-25|1997-02-19|COLLECT COD|MAIL|azzle furiously careful\n2406|40475|2980|2|40|56618.80|0.02|0.07|N|O|1997-01-09|1996-12-02|1997-01-16|NONE|SHIP|gular accounts caj\n2406|49072|9073|3|16|16337.12|0.07|0.03|N|O|1996-10-31|1996-11-28|1996-11-08|TAKE BACK RETURN|SHIP| special accou\n2406|145035|64|4|34|36721.02|0.07|0.06|N|O|1996-12-01|1996-12-07|1996-12-16|NONE|AIR|hinly even accounts are slyly q\n2406|186950|9469|5|25|50923.75|0.08|0.02|N|O|1996-12-03|1996-12-14|1996-12-26|COLLECT COD|MAIL|al, regular in\n2406|58277|3288|6|22|27175.94|0.05|0.02|N|O|1996-11-22|1997-01-17|1996-12-15|NONE|TRUCK|hely even foxes unwind furiously aga\n2406|59491|7007|7|30|43514.70|0.07|0.07|N|O|1997-01-17|1997-01-12|1997-01-22|TAKE BACK RETURN|TRUCK| final pinto beans han\n2407|63032|5539|1|14|13930.42|0.04|0.02|N|O|1998-10-10|1998-08-25|1998-10-27|NONE|FOB|l dependencies s\n2407|165166|5167|2|9|11080.44|0.07|0.05|N|O|1998-08-06|1998-08-11|1998-08-20|TAKE BACK RETURN|TRUCK|ts. special deposits are closely.\n2407|130261|262|3|39|50359.14|0.02|0.02|N|O|1998-08-20|1998-09-12|1998-08-22|DELIVER IN PERSON|MAIL|iously final deposits solv\n2407|90439|5458|4|10|14294.30|0.01|0.07|N|O|1998-08-14|1998-09-10|1998-08-29|COLLECT COD|FOB| pending instructions. theodolites x-\n2407|197553|2592|5|14|23107.70|0.04|0.05|N|O|1998-09-24|1998-08-18|1998-10-06|DELIVER IN PERSON|FOB|tructions wake stealt\n2407|70290|7812|6|18|22685.22|0.04|0.01|N|O|1998-10-03|1998-08-30|1998-10-19|TAKE BACK RETURN|MAIL| wake carefully. fluffily \n2407|160877|5910|7|7|13565.09|0.07|0.03|N|O|1998-09-11|1998-08-15|1998-09-30|TAKE BACK RETURN|MAIL|totes are carefully accordin\n2432|49371|4380|1|30|39611.10|0.03|0.02|N|O|1996-09-05|1996-10-10|1996-10-05|TAKE BACK RETURN|TRUCK| requests wake alongside of\n2432|161797|1798|2|8|14870.32|0.07|0.01|N|O|1996-10-16|1996-10-01|1996-11-13|COLLECT COD|RAIL|s about the bold, close deposit\n2432|108837|1348|3|13|23995.79|0.07|0.06|N|O|1996-09-03|1996-10-10|1996-10-03|NONE|RAIL|arefully about the caref\n2432|12405|2406|4|14|18443.60|0.00|0.06|N|O|1996-08-18|1996-09-04|1996-08-27|TAKE BACK RETURN|RAIL|riously regular packages. p\n2433|86794|6795|1|39|69450.81|0.01|0.04|R|F|1994-11-20|1994-09-23|1994-12-10|DELIVER IN PERSON|SHIP|ly final asy\n2433|133323|8350|2|20|27126.40|0.05|0.06|A|F|1994-12-09|1994-10-20|1994-12-15|COLLECT COD|REG AIR|lithely blithely final ide\n2433|156347|1378|3|38|53326.92|0.08|0.03|A|F|1994-10-15|1994-10-23|1994-11-06|DELIVER IN PERSON|SHIP|. slyly regular requests sle\n2433|120254|2767|4|43|54792.75|0.01|0.05|A|F|1994-10-16|1994-10-23|1994-11-08|DELIVER IN PERSON|RAIL|ular requests. slyly even pa\n2433|107164|9675|5|3|3513.48|0.06|0.02|A|F|1994-11-08|1994-09-24|1994-11-17|COLLECT COD|AIR|usly pending depos\n2434|94433|4434|1|1|1427.43|0.01|0.06|N|O|1997-08-02|1997-05-28|1997-08-19|TAKE BACK RETURN|MAIL| furiously express packages. ironic, pend\n2434|126777|4314|2|39|70347.03|0.09|0.05|N|O|1997-06-10|1997-06-08|1997-07-03|COLLECT COD|RAIL|r deposits sleep furiou\n2434|129445|6982|3|28|41284.32|0.02|0.05|N|O|1997-06-28|1997-06-26|1997-07-15|COLLECT COD|RAIL|ven theodolites around the slyly\n2434|167173|7174|4|49|60768.33|0.00|0.05|N|O|1997-08-08|1997-07-23|1997-08-27|DELIVER IN PERSON|FOB| after the requests haggle bold, fina\n2435|38868|8869|1|8|14454.88|0.08|0.03|A|F|1993-06-08|1993-04-04|1993-06-29|COLLECT COD|SHIP|e fluffily quickly final accounts. care\n2435|48350|3359|2|43|55829.05|0.03|0.08|A|F|1993-03-27|1993-05-20|1993-04-18|DELIVER IN PERSON|TRUCK|alongside of the s\n2435|11457|6460|3|24|32842.80|0.07|0.08|R|F|1993-03-14|1993-05-20|1993-03-26|DELIVER IN PERSON|SHIP|s. carefully regular d\n2435|155868|8384|4|22|42324.92|0.02|0.05|R|F|1993-05-23|1993-04-14|1993-06-04|NONE|SHIP|e final, final deposits. carefully regular\n2435|71676|4184|5|3|4943.01|0.07|0.07|R|F|1993-06-01|1993-03-25|1993-06-27|DELIVER IN PERSON|FOB| final accounts ar\n2435|45524|533|6|17|24981.84|0.02|0.02|A|F|1993-06-05|1993-05-05|1993-06-14|NONE|TRUCK|cajole aft\n2435|120686|5711|7|8|13653.44|0.07|0.02|R|F|1993-05-03|1993-04-02|1993-05-17|COLLECT COD|SHIP|ng the fluffily special foxes nag \n2436|154062|4063|1|48|53570.88|0.04|0.02|N|O|1995-10-22|1995-10-22|1995-11-16|DELIVER IN PERSON|FOB|he furiously \n2436|116509|4043|2|18|27459.00|0.05|0.03|N|O|1995-10-14|1995-11-21|1995-11-12|TAKE BACK RETURN|TRUCK|y ironic accounts. furiously even packa\n2436|163647|6164|3|6|10263.84|0.06|0.08|N|O|1995-10-25|1995-11-30|1995-11-24|DELIVER IN PERSON|RAIL|odolites. ep\n2437|93699|6209|1|46|77863.74|0.07|0.04|A|F|1993-08-12|1993-06-16|1993-08-29|NONE|RAIL|e of the bold, dogged requests\n2437|189398|1917|2|26|38672.14|0.00|0.04|A|F|1993-06-25|1993-05-22|1993-07-07|DELIVER IN PERSON|REG AIR|lyly regular accounts.\n2437|1745|6746|3|23|37875.02|0.01|0.00|A|F|1993-08-15|1993-06-28|1993-08-23|TAKE BACK RETURN|SHIP|s deposits. pendi\n2437|115739|8251|4|12|21056.76|0.03|0.08|A|F|1993-04-27|1993-07-01|1993-05-18|TAKE BACK RETURN|FOB|thely regular deposits. ironic fray\n2437|16697|4201|5|29|46797.01|0.02|0.06|A|F|1993-05-12|1993-06-10|1993-05-25|NONE|FOB|ress dolphins. furiously fin\n2437|18571|1073|6|10|14895.70|0.10|0.06|A|F|1993-05-20|1993-06-23|1993-05-22|TAKE BACK RETURN|MAIL|unts. even, ironic pl\n2438|164492|9525|1|45|70042.05|0.01|0.00|A|F|1993-10-27|1993-09-24|1993-11-02|COLLECT COD|REG AIR|en theodolites w\n2438|12785|2786|2|31|52631.18|0.08|0.01|R|F|1993-10-16|1993-08-31|1993-11-10|COLLECT COD|REG AIR|t. slyly ironic sh\n2438|67446|9953|3|10|14134.40|0.10|0.00|R|F|1993-08-18|1993-08-28|1993-09-08|NONE|SHIP|engage car\n2438|160408|5441|4|27|39646.80|0.01|0.02|R|F|1993-07-27|1993-10-01|1993-08-06|TAKE BACK RETURN|FOB|inal accounts. slyly final reques\n2438|165750|783|5|28|50841.00|0.07|0.06|R|F|1993-11-05|1993-08-22|1993-11-22|TAKE BACK RETURN|TRUCK|ctions. bli\n2438|148426|941|6|23|33911.66|0.09|0.02|R|F|1993-10-06|1993-08-17|1993-10-16|DELIVER IN PERSON|MAIL|ely; blithely special pinto beans breach\n2438|182589|7626|7|46|76892.68|0.02|0.05|R|F|1993-10-27|1993-08-30|1993-11-14|COLLECT COD|SHIP| ironic requests cajole f\n2439|163036|8069|1|2|2198.06|0.09|0.03|N|O|1997-04-14|1997-06-11|1997-05-09|COLLECT COD|MAIL|courts boos\n2439|143488|3489|2|5|7657.40|0.07|0.01|N|O|1997-04-23|1997-04-26|1997-04-28|DELIVER IN PERSON|FOB|ites. furiously\n2439|194676|7196|3|33|58432.11|0.08|0.05|N|O|1997-06-01|1997-05-15|1997-06-07|TAKE BACK RETURN|FOB|asymptotes wake packages-- furiously\n2464|48367|5880|1|10|13153.60|0.05|0.03|N|O|1998-02-04|1997-12-29|1998-02-16|TAKE BACK RETURN|RAIL|slyly final pinto bean\n2464|100451|5472|2|20|29029.00|0.01|0.07|N|O|1997-12-26|1998-01-02|1998-01-24|DELIVER IN PERSON|FOB|sts. slyly close ideas shall h\n2465|67190|2203|1|27|31244.13|0.05|0.02|N|O|1995-09-05|1995-09-07|1995-09-17|DELIVER IN PERSON|FOB|posits boost carefully unusual instructio\n2465|50636|8152|2|34|53945.42|0.02|0.05|N|O|1995-10-02|1995-08-04|1995-10-09|COLLECT COD|RAIL|posits wake. regular package\n2465|31032|6039|3|8|7704.24|0.10|0.00|N|O|1995-10-16|1995-08-26|1995-11-07|TAKE BACK RETURN|FOB|s across the express deposits wak\n2465|147716|5259|4|45|79366.95|0.03|0.01|N|O|1995-09-27|1995-08-25|1995-10-06|NONE|TRUCK|y silent foxes. final pinto beans above \n2465|46762|9267|5|50|85438.00|0.01|0.04|N|O|1995-09-01|1995-09-06|1995-09-18|TAKE BACK RETURN|TRUCK|the pending th\n2465|123555|3556|6|20|31571.00|0.03|0.03|N|O|1995-08-16|1995-08-13|1995-09-02|COLLECT COD|FOB|uriously? furiously ironic excu\n2466|185322|2877|1|16|22517.12|0.00|0.02|R|F|1994-04-20|1994-04-20|1994-05-09|COLLECT COD|FOB|to beans sl\n2466|104696|7207|2|10|17006.90|0.00|0.00|A|F|1994-05-08|1994-04-06|1994-06-05|DELIVER IN PERSON|AIR|sly regular deposits. regular, regula\n2466|13198|8201|3|29|32224.51|0.10|0.07|A|F|1994-06-11|1994-04-27|1994-07-10|DELIVER IN PERSON|FOB|ckages. bold requests nag carefully.\n2466|10394|5397|4|29|37827.31|0.04|0.04|A|F|1994-04-01|1994-04-20|1994-04-23|DELIVER IN PERSON|MAIL|es boost fluffily ab\n2466|78716|8717|5|30|50841.30|0.02|0.01|A|F|1994-04-11|1994-05-02|1994-05-02|DELIVER IN PERSON|REG AIR|. fluffily even pinto beans are idly. f\n2466|172628|7663|6|19|32311.78|0.10|0.07|R|F|1994-06-12|1994-04-18|1994-07-12|NONE|MAIL|ccounts cajole a\n2466|154218|1764|7|35|44527.35|0.10|0.00|A|F|1994-06-01|1994-05-27|1994-06-21|COLLECT COD|AIR| packages detect carefully: ironically sl\n2467|132883|423|1|7|13411.16|0.00|0.00|N|O|1995-07-28|1995-10-04|1995-08-27|NONE|REG AIR|gular packages cajole \n2468|93522|8541|1|46|69713.92|0.00|0.04|N|O|1997-07-16|1997-08-09|1997-08-07|COLLECT COD|SHIP|unusual theodolites su\n2468|20527|5532|2|43|62243.36|0.00|0.04|N|O|1997-08-17|1997-08-21|1997-08-30|DELIVER IN PERSON|FOB|uriously eve\n2468|194115|4116|3|44|53200.84|0.00|0.03|N|O|1997-10-01|1997-08-02|1997-10-09|TAKE BACK RETURN|RAIL|egular, silent sheave\n2468|81607|1608|4|5|7943.00|0.08|0.00|N|O|1997-06-28|1997-08-02|1997-07-22|NONE|MAIL| sleep fluffily acc\n2468|158353|869|5|18|25404.30|0.07|0.00|N|O|1997-07-25|1997-08-26|1997-08-14|DELIVER IN PERSON|REG AIR|cies. fluffily r\n2469|165688|3237|1|11|19290.48|0.00|0.04|N|O|1997-02-09|1997-01-26|1997-02-16|NONE|TRUCK|ies wake carefully b\n2469|113359|8382|2|16|21957.60|0.07|0.06|N|O|1997-02-19|1997-02-04|1997-03-18|NONE|MAIL|ing asymptotes \n2469|10263|2765|3|48|56316.48|0.05|0.06|N|O|1997-01-11|1997-01-03|1997-01-15|TAKE BACK RETURN|AIR|riously even theodolites u\n2469|87876|5401|4|35|65235.45|0.06|0.06|N|O|1997-02-04|1997-02-02|1997-02-17|DELIVER IN PERSON|RAIL|ld packages haggle regular frets. fluffily \n2469|120671|8208|5|30|50750.10|0.09|0.01|N|O|1996-12-21|1997-01-29|1997-01-02|COLLECT COD|SHIP| accounts. regular theodolites affix fu\n2469|103013|3014|6|49|49784.49|0.02|0.02|N|O|1997-03-03|1996-12-26|1997-03-13|NONE|AIR| requests are car\n2469|126264|3801|7|8|10322.08|0.02|0.00|N|O|1997-03-15|1997-01-20|1997-04-13|NONE|TRUCK|s. regular\n2470|109485|4506|1|12|17933.76|0.06|0.06|N|O|1997-07-12|1997-05-24|1997-07-17|TAKE BACK RETURN|FOB|l accounts. deposits nag daringly. express,\n2470|99927|7455|2|50|96346.00|0.03|0.03|N|O|1997-06-02|1997-06-01|1997-06-09|COLLECT COD|AIR| packages \n2470|63936|6443|3|10|18999.30|0.05|0.08|N|O|1997-06-20|1997-06-19|1997-06-24|TAKE BACK RETURN|FOB| ironic requests a\n2470|161046|1047|4|30|33211.20|0.04|0.08|N|O|1997-08-04|1997-07-13|1997-08-14|DELIVER IN PERSON|AIR|s across the furiously fina\n2471|83845|3846|1|37|67667.08|0.05|0.01|N|O|1998-05-28|1998-04-17|1998-06-08|COLLECT COD|TRUCK|ounts mold blithely carefully express depo\n2496|140193|2708|1|38|46861.22|0.02|0.07|R|F|1994-03-26|1994-04-06|1994-04-23|COLLECT COD|RAIL| bold accounts. furi\n2496|22609|2610|2|39|59732.40|0.03|0.00|R|F|1994-03-23|1994-02-18|1994-04-10|TAKE BACK RETURN|FOB|arefully special dependencies abo\n2496|188554|3591|3|36|59131.80|0.09|0.04|R|F|1994-03-27|1994-03-15|1994-04-17|TAKE BACK RETURN|SHIP|ully ironic f\n2496|23236|5739|4|30|34776.90|0.04|0.01|A|F|1994-01-27|1994-03-11|1994-01-31|DELIVER IN PERSON|RAIL|ake. ironic foxes cajole quickly. fu\n2497|11060|8564|1|34|33016.04|0.02|0.03|R|F|1992-09-02|1992-10-19|1992-09-12|COLLECT COD|AIR|ronic accounts. p\n2497|76887|9395|2|15|27958.20|0.09|0.02|A|F|1992-12-23|1992-11-20|1993-01-18|DELIVER IN PERSON|SHIP|sly against the\n2497|33578|8585|3|28|42323.96|0.02|0.08|A|F|1992-12-02|1992-11-21|1992-12-04|DELIVER IN PERSON|REG AIR|ouches. special, regular requests\n2497|143832|3833|4|48|90039.84|0.06|0.05|A|F|1992-09-29|1992-11-13|1992-10-19|TAKE BACK RETURN|AIR| even, regular requests across \n2497|174813|7331|5|28|52858.68|0.04|0.05|A|F|1992-11-10|1992-09-30|1992-11-18|DELIVER IN PERSON|MAIL|hely bold ideas. unusual instructions ac\n2497|70598|599|6|19|29803.21|0.05|0.08|A|F|1992-11-10|1992-11-20|1992-12-05|TAKE BACK RETURN|TRUCK| instructions? carefully daring accounts\n2498|142210|9753|1|48|60106.08|0.10|0.01|R|F|1993-11-25|1994-01-09|1993-12-24|DELIVER IN PERSON|RAIL|onic requests wake\n2499|149310|4339|1|15|20389.65|0.04|0.06|N|O|1995-12-21|1995-12-06|1996-01-19|DELIVER IN PERSON|FOB| slyly across the slyly\n2499|45085|7590|2|48|49443.84|0.09|0.03|N|O|1995-10-14|1995-12-12|1995-11-11|DELIVER IN PERSON|AIR|ronic ideas cajole quickly requests. caref\n2499|132086|4600|3|31|34660.48|0.09|0.05|N|O|1995-12-09|1995-10-28|1996-01-05|COLLECT COD|AIR|to beans across the carefully ironic theodo\n2499|158406|922|4|39|57111.60|0.06|0.02|N|O|1995-10-26|1995-10-27|1995-11-07|TAKE BACK RETURN|SHIP|otes sublat\n2499|129132|4157|5|6|6966.78|0.02|0.01|N|O|1995-11-19|1995-12-14|1995-12-08|NONE|SHIP|cording to the\n2499|118583|1095|6|12|19218.96|0.04|0.05|N|O|1995-11-18|1995-12-13|1995-11-23|COLLECT COD|REG AIR|le furiously along the r\n2500|191268|1269|1|40|54370.40|0.00|0.02|A|F|1992-09-02|1992-09-30|1992-09-06|DELIVER IN PERSON|SHIP|efully unusual dolphins s\n2500|36252|6253|2|34|40400.50|0.06|0.02|R|F|1992-10-03|1992-11-11|1992-10-29|DELIVER IN PERSON|TRUCK| stealthy a\n2500|79210|1718|3|41|48757.61|0.02|0.00|R|F|1992-09-02|1992-11-11|1992-09-06|DELIVER IN PERSON|RAIL|s could have to integrate after the \n2500|68182|689|4|17|19553.06|0.01|0.02|A|F|1992-09-30|1992-10-16|1992-10-05|DELIVER IN PERSON|REG AIR|encies-- ironic, even packages\n2501|83413|938|1|4|5585.64|0.10|0.06|N|O|1997-07-17|1997-07-27|1997-07-22|COLLECT COD|RAIL|quests. furiously final\n2501|105961|982|2|33|64909.68|0.01|0.04|N|O|1997-07-14|1997-08-09|1997-07-26|NONE|MAIL|leep furiously packages. even sauternes \n2501|71879|4387|3|20|37017.40|0.10|0.06|N|O|1997-09-23|1997-07-01|1997-10-03|DELIVER IN PERSON|RAIL|equests. furiou\n2501|57411|4927|4|26|35578.66|0.09|0.01|N|O|1997-07-15|1997-08-15|1997-07-28|DELIVER IN PERSON|SHIP|c accounts. express, iron\n2502|162155|2156|1|33|40165.95|0.10|0.06|R|F|1993-08-12|1993-07-22|1993-09-04|COLLECT COD|REG AIR|have to print\n2503|122548|7573|1|33|51827.82|0.06|0.01|R|F|1993-07-06|1993-08-14|1993-08-02|NONE|SHIP|nal courts integrate according to the\n2503|64060|1579|2|28|28673.68|0.06|0.01|R|F|1993-08-08|1993-08-31|1993-08-10|NONE|SHIP|s wake quickly slyly \n2503|45308|5309|3|50|62665.00|0.09|0.01|A|F|1993-09-22|1993-08-17|1993-09-29|DELIVER IN PERSON|TRUCK|s around the slyly \n2503|90061|7589|4|27|28378.62|0.09|0.00|A|F|1993-07-12|1993-07-24|1993-07-22|DELIVER IN PERSON|TRUCK|lly even p\n2503|47679|184|5|3|4880.01|0.04|0.02|A|F|1993-07-10|1993-09-17|1993-07-19|TAKE BACK RETURN|TRUCK|s cajole. slyly close courts nod f\n2503|127722|2747|6|39|68239.08|0.05|0.05|R|F|1993-10-11|1993-09-09|1993-10-16|NONE|MAIL|d carefully fluffily\n2503|18588|3591|7|17|25611.86|0.09|0.08|R|F|1993-09-04|1993-07-31|1993-09-23|DELIVER IN PERSON|SHIP|c accounts haggle blithel\n2528|195|196|1|10|10951.90|0.02|0.03|R|F|1994-12-12|1994-12-29|1994-12-28|COLLECT COD|REG AIR|ely. fluffily even re\n2528|73433|8448|2|13|18283.59|0.00|0.03|A|F|1994-11-27|1995-01-20|1994-12-03|TAKE BACK RETURN|REG AIR|ggle furiously. slyly final asympt\n2528|174348|4349|3|35|49781.90|0.10|0.00|R|F|1994-12-19|1995-02-04|1995-01-15|NONE|MAIL|, even excuses. even,\n2528|64125|6632|4|37|40297.44|0.00|0.01|A|F|1994-12-25|1995-02-02|1994-12-31|COLLECT COD|AIR|ng the pending excuses haggle after the bl\n2529|130636|8176|1|4|6666.52|0.07|0.07|N|O|1996-10-19|1996-11-18|1996-10-24|DELIVER IN PERSON|SHIP|al dependencies haggle slyly alongsi\n2530|20319|320|1|9|11153.79|0.09|0.03|R|F|1994-05-10|1994-04-30|1994-05-24|TAKE BACK RETURN|REG AIR|lyly ironic\n2530|92397|9925|2|42|58354.38|0.04|0.08|R|F|1994-03-27|1994-05-20|1994-03-29|NONE|RAIL|ng platelets wake s\n2530|107824|335|3|8|14654.56|0.10|0.08|A|F|1994-05-02|1994-05-08|1994-05-24|DELIVER IN PERSON|MAIL|ial asymptotes snooze slyly regular \n2531|147722|5265|1|9|15927.48|0.03|0.07|N|O|1996-07-27|1996-07-03|1996-08-01|DELIVER IN PERSON|AIR|t the dogged, un\n2531|157000|2031|2|3|3171.00|0.07|0.06|N|O|1996-07-20|1996-06-20|1996-08-10|NONE|MAIL|he quickly ev\n2531|85917|934|3|20|38058.20|0.06|0.04|N|O|1996-07-18|1996-06-25|1996-07-29|TAKE BACK RETURN|TRUCK|into beans. furious\n2531|190889|8447|4|36|71275.68|0.08|0.01|N|O|1996-06-11|1996-07-26|1996-06-27|NONE|MAIL|y ironic, bold packages. blithely e\n2531|55891|8397|5|28|51712.92|0.03|0.07|N|O|1996-07-06|1996-07-31|1996-07-19|TAKE BACK RETURN|REG AIR|its. busily\n2531|144342|1885|6|46|63771.64|0.10|0.08|N|O|1996-07-03|1996-06-27|1996-07-12|TAKE BACK RETURN|REG AIR|e final, bold pains. ir\n2532|52631|2632|1|3|4750.89|0.06|0.07|N|O|1995-12-14|1995-11-28|1995-12-15|COLLECT COD|FOB|unusual sentiments. even pinto\n2532|159799|7345|2|33|61340.07|0.06|0.05|N|O|1995-11-23|1996-01-04|1995-12-16|DELIVER IN PERSON|TRUCK|rve carefully slyly ironic accounts! fluf\n2532|134223|1763|3|1|1257.22|0.00|0.06|N|O|1996-01-27|1995-11-23|1996-01-29|DELIVER IN PERSON|REG AIR|ely final ideas cajole despite the ca\n2532|77534|42|4|50|75576.50|0.02|0.02|N|O|1995-11-13|1996-01-01|1995-11-26|NONE|TRUCK|yly after the fluffily regul\n2532|113046|8069|5|9|9531.36|0.09|0.04|N|O|1995-11-30|1995-11-23|1995-12-12|DELIVER IN PERSON|TRUCK|cial ideas haggle slyly pending request\n2532|149281|9282|6|20|26605.60|0.09|0.05|N|O|1995-12-02|1995-11-26|1995-12-08|TAKE BACK RETURN|AIR|er the slyly pending\n2533|53585|8596|1|36|55388.88|0.06|0.04|N|O|1997-06-10|1997-04-28|1997-07-01|NONE|REG AIR|ss requests sleep neve\n2533|197072|9592|2|5|5845.35|0.10|0.04|N|O|1997-05-26|1997-06-02|1997-06-24|NONE|FOB|ccounts. ironic, special accounts boo\n2533|182962|517|3|37|75663.52|0.00|0.08|N|O|1997-05-10|1997-04-26|1997-05-28|COLLECT COD|SHIP| haggle carefully \n2533|29341|1844|4|17|21595.78|0.06|0.02|N|O|1997-05-23|1997-05-10|1997-06-18|NONE|FOB|ackages. blith\n2533|125554|8067|5|38|60022.90|0.09|0.00|N|O|1997-05-10|1997-06-02|1997-05-28|TAKE BACK RETURN|REG AIR|of the regular accounts. even packages caj\n2533|183334|3335|6|20|28346.60|0.05|0.08|N|O|1997-07-04|1997-04-30|1997-07-05|COLLECT COD|FOB|thless excuses are b\n2533|93767|8786|7|14|24650.64|0.06|0.04|N|O|1997-07-06|1997-05-08|1997-08-03|COLLECT COD|FOB|ut the pending, special depos\n2534|138552|6092|1|29|46125.95|0.07|0.07|N|O|1996-08-09|1996-09-29|1996-08-11|COLLECT COD|TRUCK|ugouts haggle slyly. final\n2534|26716|1721|2|49|80492.79|0.08|0.08|N|O|1996-09-01|1996-08-20|1996-09-06|NONE|SHIP|sometimes regular requests. blithely unus\n2534|802|3303|3|50|85140.00|0.10|0.06|N|O|1996-09-25|1996-10-07|1996-10-09|TAKE BACK RETURN|AIR|ideas. deposits use. slyly regular pa\n2534|74217|1739|4|43|51222.03|0.09|0.02|N|O|1996-10-25|1996-09-30|1996-11-05|TAKE BACK RETURN|REG AIR|ngly final depos\n2534|164808|9841|5|14|26219.20|0.05|0.02|N|O|1996-08-12|1996-09-26|1996-08-28|COLLECT COD|MAIL|eposits doze quickly final\n2534|115962|8474|6|12|23735.52|0.02|0.02|N|O|1996-07-29|1996-10-12|1996-08-14|TAKE BACK RETURN|AIR|sual depos\n2534|172536|5054|7|17|27345.01|0.02|0.07|N|O|1996-07-22|1996-09-15|1996-08-03|NONE|SHIP|riously regular \n2535|198027|3066|1|5|5625.10|0.06|0.01|A|F|1993-09-07|1993-07-25|1993-09-29|DELIVER IN PERSON|REG AIR|, unusual reque\n2535|38012|516|2|12|11400.12|0.08|0.05|A|F|1993-07-17|1993-08-17|1993-07-31|TAKE BACK RETURN|FOB|uses sleep among the packages. excuses \n2535|53012|3013|3|5|4825.05|0.09|0.06|R|F|1993-07-28|1993-08-14|1993-08-11|DELIVER IN PERSON|SHIP| across the express requests. silent, eve\n2535|159290|4321|4|19|25636.51|0.01|0.02|A|F|1993-06-01|1993-08-01|1993-06-19|DELIVER IN PERSON|FOB|ructions. final requests\n2535|173753|8788|5|25|45668.75|0.07|0.04|A|F|1993-07-19|1993-08-07|1993-07-27|NONE|REG AIR|ions believe ab\n2560|168823|8824|1|41|77564.62|0.07|0.01|R|F|1992-10-23|1992-11-11|1992-11-22|NONE|SHIP| after the accounts. regular foxes are be\n2560|3764|8765|2|27|45029.52|0.00|0.01|R|F|1992-12-03|1992-11-16|1992-12-30|NONE|MAIL| against the carefully\n2560|45914|3427|3|31|57657.21|0.01|0.05|A|F|1992-11-14|1992-10-14|1992-12-11|DELIVER IN PERSON|AIR|to beans. blithely regular Tiresias int\n2560|71128|6143|4|36|39568.32|0.01|0.02|A|F|1992-10-18|1992-10-30|1992-11-05|TAKE BACK RETURN|MAIL|accounts alongside of the excuses are \n2560|41835|9348|5|9|15991.47|0.04|0.02|A|F|1992-10-23|1992-10-29|1992-11-02|COLLECT COD|REG AIR| deposits affix quickly. unusual, eve\n2560|107511|7512|6|13|19740.63|0.03|0.06|A|F|1992-09-07|1992-10-21|1992-09-24|COLLECT COD|FOB|slyly final accoun\n2561|24579|9584|1|32|48114.24|0.02|0.01|N|O|1998-01-05|1997-12-28|1998-01-26|DELIVER IN PERSON|REG AIR|bold packages wake slyly. slyly\n2561|97066|2085|2|5|5315.30|0.07|0.04|N|O|1997-12-27|1998-01-23|1998-01-13|TAKE BACK RETURN|AIR|p ironic, regular pinto beans.\n2561|172518|2519|3|47|74753.97|0.04|0.02|N|O|1997-11-19|1998-01-21|1997-12-03|DELIVER IN PERSON|REG AIR|larly pending t\n2561|107536|7537|4|39|60197.67|0.08|0.06|N|O|1998-01-20|1997-12-16|1998-02-05|TAKE BACK RETURN|MAIL|equests are furiously against the\n2561|149929|4958|5|2|3957.84|0.04|0.08|N|O|1998-03-14|1998-01-21|1998-03-27|DELIVER IN PERSON|TRUCK|s are. silently silent foxes sleep about\n2561|50268|5279|6|14|17055.64|0.02|0.03|N|O|1998-03-07|1998-02-04|1998-03-21|COLLECT COD|RAIL|ep unusual, ironic accounts\n2562|52202|9718|1|28|32317.60|0.04|0.03|R|F|1992-10-04|1992-09-24|1992-10-09|COLLECT COD|MAIL|ans haggle special, special packages. \n2562|147205|7206|2|1|1252.20|0.01|0.06|R|F|1992-10-16|1992-09-18|1992-10-17|NONE|TRUCK| slyly final ideas haggle car\n2562|65577|5578|3|25|38564.25|0.05|0.03|A|F|1992-11-23|1992-10-08|1992-12-19|DELIVER IN PERSON|REG AIR| accounts-- silent, unusual ideas a\n2562|147127|2156|4|37|43442.44|0.08|0.03|R|F|1992-10-29|1992-10-06|1992-11-09|COLLECT COD|FOB|. slyly regular ideas according to the fl\n2562|159068|1584|5|29|32684.74|0.05|0.08|A|F|1992-11-01|1992-09-29|1992-11-13|TAKE BACK RETURN|MAIL|eep against the furiously r\n2562|49696|2201|6|17|27976.73|0.01|0.06|A|F|1992-10-15|1992-10-08|1992-10-26|DELIVER IN PERSON|TRUCK|lar pinto beans. blithely ev\n2563|64353|6860|1|10|13173.50|0.07|0.04|A|F|1994-01-26|1993-12-19|1994-01-28|DELIVER IN PERSON|AIR|tealthily abo\n2563|166759|1792|2|28|51121.00|0.04|0.03|R|F|1994-03-17|1994-02-04|1994-04-13|TAKE BACK RETURN|RAIL|hely regular depe\n2563|118456|5990|3|39|57503.55|0.07|0.00|R|F|1994-02-10|1993-12-31|1994-02-19|COLLECT COD|FOB|lent requests should integrate; carefully e\n2563|89071|6596|4|50|53003.50|0.01|0.01|A|F|1994-01-26|1994-01-03|1994-02-09|DELIVER IN PERSON|SHIP|ly regular, regular excuses. bold plate\n2563|14990|4991|5|42|80009.58|0.06|0.08|R|F|1994-02-21|1994-02-14|1994-03-04|DELIVER IN PERSON|AIR|ymptotes nag furiously slyly even inst\n2563|120830|831|6|5|9254.15|0.10|0.00|R|F|1993-12-27|1993-12-19|1994-01-02|DELIVER IN PERSON|REG AIR| the quickly final theodolite\n2564|111220|1221|1|4|4924.88|0.02|0.00|R|F|1994-11-12|1994-10-29|1994-12-04|NONE|MAIL|y express requests sleep furi\n2565|143630|3631|1|42|70292.46|0.04|0.08|N|O|1998-04-07|1998-04-02|1998-05-04|NONE|AIR|ngly silent \n2565|188845|8846|2|26|50279.84|0.05|0.08|N|O|1998-05-07|1998-04-09|1998-05-15|DELIVER IN PERSON|TRUCK| pinto beans about the slyly regula\n2565|114394|1928|3|34|47885.26|0.06|0.06|N|O|1998-03-19|1998-04-12|1998-04-17|DELIVER IN PERSON|SHIP|nstructions was carefu\n2565|16224|3728|4|25|28505.50|0.10|0.08|N|O|1998-06-27|1998-05-20|1998-07-13|DELIVER IN PERSON|RAIL|, express accounts. final id\n2565|75465|5466|5|26|37451.96|0.08|0.03|N|O|1998-03-05|1998-04-11|1998-03-11|TAKE BACK RETURN|AIR|ites wake. ironic acco\n2565|140191|5220|6|48|59097.12|0.08|0.07|N|O|1998-06-18|1998-05-06|1998-07-13|DELIVER IN PERSON|TRUCK|r instructions sleep qui\n2566|147224|9739|1|19|24153.18|0.06|0.07|R|F|1992-12-21|1992-11-24|1992-12-22|DELIVER IN PERSON|MAIL|ests. silent\n2566|180474|8029|2|42|65287.74|0.08|0.02|R|F|1992-12-20|1992-12-22|1992-12-29|COLLECT COD|MAIL|ously ironic accounts\n2566|22648|5151|3|18|28271.52|0.09|0.02|A|F|1992-11-16|1992-12-24|1992-12-16|COLLECT COD|FOB| braids according t\n2566|41449|3954|4|3|4171.32|0.05|0.02|A|F|1992-11-04|1992-12-30|1992-12-04|TAKE BACK RETURN|FOB|ckages are ironic Tiresias. furious\n2566|21214|1215|5|9|10216.89|0.04|0.03|R|F|1992-12-14|1992-12-28|1992-12-16|NONE|FOB|blithely bold accounts? quickl\n2566|127422|9935|6|1|1449.42|0.07|0.03|A|F|1992-10-28|1992-11-20|1992-11-22|TAKE BACK RETURN|AIR|theodolites wake pending\n2567|25711|3218|1|39|63831.69|0.03|0.04|N|O|1998-05-10|1998-05-10|1998-05-21|NONE|SHIP|ns. furiously final dependencies cajo\n2567|111886|1887|2|50|94894.00|0.06|0.05|N|O|1998-05-05|1998-04-18|1998-05-09|DELIVER IN PERSON|TRUCK|. carefully pending foxes are furi\n2567|51679|4185|3|6|9784.02|0.03|0.06|N|O|1998-04-21|1998-04-14|1998-05-11|NONE|RAIL|s cajole regular, final acco\n2567|157535|51|4|50|79626.50|0.05|0.03|N|O|1998-03-27|1998-05-25|1998-04-23|DELIVER IN PERSON|FOB|pinto beans? r\n2567|80318|7843|5|46|59722.26|0.07|0.02|N|O|1998-06-02|1998-04-30|1998-06-13|COLLECT COD|AIR|efully pending epitaphs. carefully reg\n2567|99455|4474|6|32|46542.40|0.01|0.07|N|O|1998-05-24|1998-04-30|1998-06-14|NONE|RAIL| the even, iro\n2567|134307|9334|7|43|57675.90|0.06|0.02|N|O|1998-05-11|1998-04-15|1998-05-29|NONE|RAIL|requests. final courts cajole \n2592|89634|9635|1|7|11365.41|0.10|0.04|R|F|1993-03-13|1993-04-25|1993-04-01|NONE|REG AIR| carefully special theodolites integrate \n2592|65700|3219|2|2|3331.40|0.10|0.00|A|F|1993-03-24|1993-04-05|1993-04-16|DELIVER IN PERSON|RAIL|side of the b\n2593|104022|1553|1|37|37962.74|0.08|0.06|R|F|1993-12-14|1993-10-08|1994-01-04|NONE|SHIP|s wake bravel\n2593|89185|6710|2|28|32877.04|0.08|0.03|A|F|1993-10-30|1993-10-18|1993-11-06|DELIVER IN PERSON|SHIP|y even escapades shall\n2593|127040|9553|3|6|6402.24|0.04|0.05|A|F|1993-11-28|1993-10-04|1993-12-28|TAKE BACK RETURN|REG AIR|ular packages. re\n2593|160005|2522|4|44|46860.00|0.02|0.08|A|F|1993-09-05|1993-10-23|1993-09-29|NONE|RAIL|ents impress furiously; unusual theodoli\n2593|3498|3499|5|3|4204.47|0.03|0.00|A|F|1993-12-16|1993-11-01|1993-12-29|COLLECT COD|SHIP|the furiously \n2593|174570|4571|6|1|1644.57|0.08|0.08|A|F|1993-11-23|1993-10-25|1993-12-04|DELIVER IN PERSON|RAIL| accounts wake slyly \n2593|191389|6428|7|11|16284.18|0.00|0.07|R|F|1993-11-01|1993-11-19|1993-11-28|TAKE BACK RETURN|RAIL|express packages sleep bold re\n2594|71490|1491|1|7|10230.43|0.06|0.02|R|F|1993-03-26|1993-03-05|1993-04-24|DELIVER IN PERSON|FOB|arls cajole \n2594|123171|5684|2|13|15524.21|0.10|0.05|R|F|1993-02-06|1993-03-01|1993-02-23|TAKE BACK RETURN|TRUCK|fully special accounts use courts\n2594|125492|8005|3|24|36419.76|0.03|0.00|A|F|1993-01-31|1993-03-10|1993-02-04|COLLECT COD|REG AIR|lar accounts sleep fur\n2594|143957|8986|4|46|92043.70|0.00|0.08|R|F|1993-04-17|1993-03-06|1993-04-21|TAKE BACK RETURN|SHIP|beans. instructions across t\n2595|60945|946|1|42|80049.48|0.08|0.02|N|O|1996-03-24|1996-01-28|1996-04-10|DELIVER IN PERSON|MAIL|ggle furiou\n2595|87061|9570|2|30|31441.80|0.05|0.01|N|O|1996-03-05|1996-02-23|1996-03-19|NONE|AIR|ctions. regula\n2595|23369|8374|3|19|24554.84|0.01|0.05|N|O|1995-12-23|1996-03-02|1996-01-17|COLLECT COD|MAIL|ns are neve\n2595|158785|6331|4|29|53469.62|0.07|0.05|N|O|1996-01-01|1996-02-13|1996-01-18|TAKE BACK RETURN|RAIL|ronic accounts haggle carefully fin\n2595|85077|94|5|30|31862.10|0.09|0.07|N|O|1996-03-16|1996-01-31|1996-04-05|TAKE BACK RETURN|FOB|. final orbits cajole \n2595|81859|9384|6|31|57066.35|0.06|0.04|N|O|1996-02-07|1996-02-10|1996-03-05|DELIVER IN PERSON|AIR|tipliers w\n2596|169486|7035|1|6|9332.88|0.05|0.01|N|O|1996-12-15|1996-11-02|1996-12-29|TAKE BACK RETURN|TRUCK|ily special re\n2596|138885|3912|2|43|82726.84|0.07|0.03|N|O|1996-09-03|1996-10-26|1996-09-15|NONE|FOB|ial packages haggl\n2596|38443|947|3|19|26247.36|0.10|0.00|N|O|1996-09-02|1996-11-03|1996-09-06|COLLECT COD|AIR|ias mold! sp\n2596|104266|4267|4|10|12702.60|0.06|0.05|N|O|1996-08-25|1996-11-05|1996-09-13|DELIVER IN PERSON|REG AIR| instructions shall have\n2597|83782|8799|1|24|42378.72|0.07|0.00|A|F|1993-05-15|1993-03-06|1993-05-25|TAKE BACK RETURN|FOB|pending packages. enticingly fi\n2598|6983|4484|1|12|22679.76|0.00|0.01|N|O|1996-06-17|1996-04-12|1996-06-24|COLLECT COD|TRUCK|express packages nag sly\n2598|147005|4548|2|40|42080.00|0.07|0.02|N|O|1996-05-11|1996-05-19|1996-06-08|TAKE BACK RETURN|AIR|the enticing\n2598|103709|8730|3|4|6850.80|0.03|0.03|N|O|1996-05-23|1996-05-13|1996-05-25|COLLECT COD|AIR| across the furiously fi\n2598|22081|7086|4|19|19058.52|0.02|0.00|N|O|1996-04-09|1996-05-30|1996-04-17|TAKE BACK RETURN|RAIL|nic packages. even accounts\n2598|105924|3455|5|12|23159.04|0.01|0.08|N|O|1996-04-14|1996-04-24|1996-04-21|TAKE BACK RETURN|REG AIR|eposits cajol\n2599|100735|3246|1|11|19093.03|0.08|0.08|N|O|1997-02-01|1996-12-14|1997-02-27|TAKE BACK RETURN|FOB| express accoun\n2599|41370|6379|2|26|34095.62|0.03|0.04|N|O|1996-11-08|1996-12-21|1996-11-24|TAKE BACK RETURN|AIR|nag carefully \n2599|98819|8820|3|29|52716.49|0.09|0.03|N|O|1997-01-10|1996-12-10|1997-02-02|COLLECT COD|RAIL|ly express dolphins. special, \n2624|62426|7439|1|15|20826.30|0.03|0.07|N|O|1997-02-28|1997-02-19|1997-03-21|DELIVER IN PERSON|AIR|le. quickly pending requests\n2624|188685|8686|2|12|21284.16|0.07|0.00|N|O|1997-02-24|1997-02-22|1997-02-27|DELIVER IN PERSON|SHIP|er the quickly unu\n2625|19346|9347|1|42|53144.28|0.02|0.04|R|F|1992-10-18|1992-11-17|1992-10-23|DELIVER IN PERSON|AIR| even accounts haggle furiously\n2626|21431|8938|1|45|60859.35|0.09|0.04|N|O|1995-11-22|1995-11-01|1995-11-23|NONE|AIR|deposits wake blithely according to \n2626|174354|1906|2|2|2856.70|0.05|0.07|N|O|1995-10-19|1995-11-09|1995-10-24|TAKE BACK RETURN|FOB|uffy accounts haggle furiously above\n2626|153317|5833|3|40|54812.40|0.05|0.07|N|O|1995-09-28|1995-12-03|1995-10-10|NONE|REG AIR|eans. ironic deposits haggle. depo\n2627|130582|8122|1|28|45152.24|0.09|0.02|R|F|1992-05-14|1992-05-09|1992-05-31|COLLECT COD|SHIP|ggedly final excuses nag packages. f\n2628|105867|8378|1|44|82405.84|0.07|0.03|R|F|1994-01-11|1994-01-14|1994-01-13|DELIVER IN PERSON|SHIP|lyly final, pending ide\n2628|105425|7936|2|14|20025.88|0.01|0.03|A|F|1994-01-28|1993-11-30|1994-02-20|TAKE BACK RETURN|SHIP|g the furiously unusual pi\n2628|63884|1403|3|42|77610.96|0.00|0.00|A|F|1993-11-20|1994-01-04|1993-12-19|DELIVER IN PERSON|TRUCK|ld notornis alongside \n2628|94014|6524|4|23|23184.23|0.08|0.04|A|F|1993-10-27|1994-01-08|1993-11-12|DELIVER IN PERSON|TRUCK|usual packages sleep about the fina\n2628|89996|2505|5|50|99299.50|0.07|0.01|A|F|1994-01-13|1993-12-11|1994-01-14|NONE|AIR|posits serve carefully toward \n2629|117875|7876|1|6|11357.22|0.06|0.05|N|O|1998-06-10|1998-05-29|1998-06-13|DELIVER IN PERSON|SHIP|dolites hinder bli\n2629|123076|613|2|31|34071.17|0.08|0.03|N|O|1998-05-24|1998-05-26|1998-06-10|COLLECT COD|AIR|ate blithely bold, regular deposits. bold\n2629|127428|7429|3|29|42207.18|0.08|0.07|N|O|1998-07-09|1998-06-17|1998-07-12|TAKE BACK RETURN|AIR|eposits serve unusual, express i\n2629|69303|6822|4|33|41985.90|0.06|0.03|N|O|1998-05-29|1998-05-14|1998-05-30|NONE|TRUCK|es. slowly express accounts are along the\n2630|28480|3485|1|46|64790.08|0.05|0.03|R|F|1992-11-05|1992-12-17|1992-12-05|TAKE BACK RETURN|MAIL|uests cajole. e\n2630|56285|1296|2|8|9930.24|0.09|0.07|A|F|1992-11-16|1993-01-01|1992-12-07|DELIVER IN PERSON|TRUCK|indle fluffily silent, ironic pi\n2630|172605|7640|3|45|75492.00|0.08|0.07|A|F|1993-01-04|1993-01-11|1993-01-09|NONE|FOB|edly express ideas. carefully final \n2630|161536|6569|4|29|46328.37|0.08|0.07|A|F|1992-12-03|1993-01-04|1992-12-12|DELIVER IN PERSON|SHIP|efully unusual dependencies. even i\n2631|121656|4169|1|42|70461.30|0.00|0.03|A|F|1994-01-04|1993-12-01|1994-01-16|TAKE BACK RETURN|SHIP|ect carefully at the furiously final the\n2631|66551|1564|2|4|6070.20|0.07|0.06|R|F|1993-11-03|1993-12-17|1993-11-05|COLLECT COD|AIR|special theodolites. a\n2631|117876|5410|3|15|28408.05|0.06|0.05|A|F|1993-09-30|1993-11-06|1993-10-13|DELIVER IN PERSON|SHIP|y. furiously even pinto be\n2656|180671|3190|1|10|17516.70|0.02|0.06|R|F|1993-06-28|1993-07-04|1993-07-12|TAKE BACK RETURN|TRUCK|s nag regularly about the deposits. slyly\n2656|136884|6885|2|38|72993.44|0.07|0.02|A|F|1993-06-25|1993-06-04|1993-07-24|NONE|RAIL|structions wake along the furio\n2656|1209|3710|3|19|21093.80|0.03|0.02|R|F|1993-08-03|1993-07-25|1993-08-20|TAKE BACK RETURN|MAIL|ts serve deposi\n2656|109246|1757|4|40|50209.60|0.05|0.04|R|F|1993-06-09|1993-07-24|1993-06-21|DELIVER IN PERSON|RAIL|refully final pearls. final ideas wake. qu\n2657|114332|6844|1|22|29619.26|0.02|0.03|N|O|1995-12-08|1995-12-28|1995-12-21|TAKE BACK RETURN|MAIL|r ideas. furiously special dolphins\n2657|164877|9910|2|15|29128.05|0.08|0.05|N|O|1995-12-09|1995-12-16|1995-12-18|NONE|RAIL|ole carefully above the ironic ideas. b\n2657|78233|741|3|25|30280.75|0.02|0.04|N|O|1995-10-21|1995-12-12|1995-11-09|COLLECT COD|FOB|lly pinto beans. final \n2657|54839|2355|4|11|19732.13|0.04|0.08|N|O|1995-11-19|1995-12-11|1995-11-24|COLLECT COD|TRUCK|ckly enticing requests. fur\n2657|77611|7612|5|42|66721.62|0.06|0.03|N|O|1996-01-23|1995-11-22|1996-01-25|COLLECT COD|RAIL|ckly slyly even accounts. platelets x-ray\n2657|193153|8192|6|31|38630.65|0.01|0.03|N|O|1995-11-10|1995-11-27|1995-12-06|COLLECT COD|RAIL|re blithely \n2658|131174|1175|1|41|49411.97|0.05|0.04|N|O|1995-11-07|1995-11-04|1995-12-04|NONE|MAIL|eposits. furiously final theodolite\n2658|28573|1076|2|22|33034.54|0.08|0.05|N|O|1995-11-12|1995-11-18|1995-11-14|DELIVER IN PERSON|TRUCK|ts cajole. pending packages affix\n2658|17832|2835|3|13|22747.79|0.07|0.06|N|O|1995-10-24|1995-12-12|1995-11-14|COLLECT COD|FOB|s kindle blithely regular accounts.\n2658|91939|6958|4|22|42480.46|0.04|0.04|N|O|1995-12-02|1995-11-03|1995-12-26|DELIVER IN PERSON|SHIP| dependencies. blithely pending foxes abou\n2658|6721|6722|5|45|73247.40|0.03|0.01|N|O|1995-11-02|1995-11-08|1995-11-29|DELIVER IN PERSON|MAIL|e special requests. quickly ex\n2658|146785|9300|6|27|49458.06|0.05|0.07|N|O|1995-09-26|1995-12-08|1995-09-30|NONE|AIR|ecial packages use abov\n2659|41783|9296|1|28|48293.84|0.08|0.05|A|F|1994-03-17|1994-01-24|1994-03-19|NONE|FOB|idle tithes\n2659|42253|9766|2|21|25100.25|0.00|0.00|A|F|1993-12-23|1994-02-10|1994-01-17|DELIVER IN PERSON|RAIL|y beyond the furiously even co\n2659|134547|2087|3|24|37956.96|0.04|0.03|R|F|1994-03-28|1994-02-20|1994-04-05|DELIVER IN PERSON|REG AIR| haggle carefully \n2659|118095|3118|4|2|2226.18|0.00|0.08|R|F|1994-02-19|1994-03-12|1994-02-21|NONE|MAIL|sts above the fluffily express fo\n2659|6161|3662|5|9|9604.44|0.08|0.03|A|F|1994-02-07|1994-03-17|1994-03-04|DELIVER IN PERSON|AIR|ly final packages sleep ac\n2660|47703|5216|1|17|28061.90|0.00|0.05|N|O|1995-08-18|1995-09-13|1995-09-17|NONE|SHIP|al pinto beans wake after the furious\n2661|177159|7160|1|31|38320.65|0.03|0.02|N|O|1997-04-07|1997-03-10|1997-04-23|TAKE BACK RETURN|AIR|e ironicall\n2661|102402|7423|2|22|30896.80|0.08|0.02|N|O|1997-03-14|1997-03-17|1997-04-08|COLLECT COD|REG AIR| foxes affix quickly ironic request\n2661|66183|8690|3|11|12640.98|0.00|0.08|N|O|1997-04-14|1997-02-11|1997-05-05|TAKE BACK RETURN|FOB|equests are a\n2661|136127|1154|4|41|47687.92|0.06|0.02|N|O|1997-03-06|1997-03-27|1997-03-15|DELIVER IN PERSON|AIR|iously ironically ironic requests. \n2662|101609|4120|1|43|69255.80|0.09|0.07|N|O|1996-11-24|1996-11-04|1996-12-08|NONE|RAIL|. slyly specia\n2662|127424|7425|2|8|11611.36|0.02|0.07|N|O|1996-09-10|1996-10-09|1996-09-21|TAKE BACK RETURN|REG AIR|ajole carefully. sp\n2662|1049|3550|3|6|5700.24|0.02|0.00|N|O|1996-11-30|1996-09-20|1996-12-03|DELIVER IN PERSON|REG AIR|olites cajole quickly along the b\n2662|29760|9761|4|34|57451.84|0.06|0.07|N|O|1996-10-04|1996-11-05|1996-10-19|NONE|SHIP|ding theodolites use carefully. p\n2663|113221|755|1|35|43197.70|0.02|0.01|N|O|1995-12-11|1995-10-16|1996-01-07|TAKE BACK RETURN|REG AIR|tect. slyly fina\n2688|17768|2771|1|45|75859.20|0.08|0.08|R|F|1992-05-21|1992-04-14|1992-05-28|NONE|FOB|sits run carefully\n2688|14500|4501|2|46|65067.00|0.01|0.01|R|F|1992-05-24|1992-04-01|1992-05-26|COLLECT COD|TRUCK|elets. regular reque\n2688|88810|1319|3|30|53964.30|0.05|0.04|A|F|1992-04-18|1992-03-18|1992-05-18|TAKE BACK RETURN|RAIL|ithely final \n2688|24410|6913|4|3|4003.23|0.00|0.03|R|F|1992-02-04|1992-03-18|1992-02-24|DELIVER IN PERSON|RAIL|e fluffily \n2688|58449|8450|5|22|30963.68|0.02|0.05|R|F|1992-02-09|1992-04-09|1992-02-11|DELIVER IN PERSON|RAIL|press, ironic excuses wake carefully id\n2688|148297|8298|6|42|56502.18|0.01|0.01|R|F|1992-04-29|1992-04-04|1992-05-17|TAKE BACK RETURN|FOB|lly even account\n2689|5731|732|1|45|73652.85|0.02|0.04|R|F|1992-04-29|1992-06-22|1992-04-30|COLLECT COD|SHIP|e quickly. carefully silent\n2690|139065|4092|1|44|48578.64|0.05|0.06|N|O|1996-05-30|1996-05-19|1996-06-26|NONE|REG AIR|ly alongside of th\n2690|50963|964|2|50|95698.00|0.03|0.03|N|O|1996-06-13|1996-05-22|1996-06-14|DELIVER IN PERSON|MAIL| doubt careful\n2690|124448|4449|3|45|66259.80|0.02|0.07|N|O|1996-05-23|1996-06-02|1996-05-29|DELIVER IN PERSON|MAIL|ounts. slyly regular dependencies wa\n2690|194436|4437|4|12|18365.16|0.04|0.07|N|O|1996-07-18|1996-06-03|1996-07-25|NONE|AIR|nal, regular atta\n2690|85190|2715|5|30|35255.70|0.01|0.08|N|O|1996-05-20|1996-06-01|1996-06-04|TAKE BACK RETURN|SHIP|d accounts above the express req\n2690|188034|5589|6|3|3366.09|0.07|0.01|N|O|1996-07-04|1996-05-28|1996-07-06|TAKE BACK RETURN|RAIL|. final reques\n2690|78616|6138|7|35|55811.35|0.05|0.06|N|O|1996-07-25|1996-05-14|1996-08-03|COLLECT COD|FOB|y silent pinto be\n2691|90549|3059|1|11|16934.94|0.04|0.07|R|F|1992-06-21|1992-06-08|1992-07-09|COLLECT COD|FOB|leep alongside of the accounts. slyly ironi\n2691|47716|5229|2|2|3327.42|0.00|0.07|R|F|1992-05-10|1992-06-04|1992-05-11|TAKE BACK RETURN|TRUCK|s cajole at the blithely ironic warthog\n2691|161253|1254|3|16|21028.00|0.09|0.03|R|F|1992-06-11|1992-07-29|1992-06-29|NONE|RAIL|bove the even foxes. unusual theodoli\n2691|165770|803|4|1|1835.77|0.08|0.00|A|F|1992-08-11|1992-06-07|1992-08-16|NONE|SHIP|egular instructions b\n2692|16131|8633|1|3|3141.39|0.10|0.04|N|O|1998-02-25|1998-01-29|1998-03-27|TAKE BACK RETURN|MAIL|equests. bold, even foxes haggle slyl\n2692|113501|8524|2|21|31804.50|0.03|0.05|N|O|1998-03-11|1998-02-11|1998-03-19|NONE|SHIP|posits. final, express requests nag furi\n2693|8670|8671|1|26|41045.42|0.04|0.00|N|O|1996-09-14|1996-10-07|1996-10-03|COLLECT COD|MAIL|cajole alo\n2693|101842|1843|2|43|79285.12|0.03|0.04|N|O|1996-10-24|1996-10-24|1996-11-03|TAKE BACK RETURN|TRUCK|as are according to th\n2694|152518|5034|1|30|47115.30|0.02|0.06|N|O|1996-06-20|1996-06-01|1996-07-15|NONE|TRUCK|oxes. never iro\n2694|156889|1920|2|35|68105.80|0.07|0.03|N|O|1996-05-24|1996-06-01|1996-05-25|NONE|RAIL|atelets past the furiously final deposits \n2694|18030|532|3|15|14220.45|0.08|0.02|N|O|1996-06-30|1996-05-01|1996-07-25|TAKE BACK RETURN|REG AIR|e blithely even platelets. special wa\n2694|19451|6955|4|12|16445.40|0.00|0.05|N|O|1996-04-24|1996-04-22|1996-05-14|DELIVER IN PERSON|RAIL|foxes atop the hockey pla\n2694|107185|7186|5|10|11921.80|0.08|0.08|N|O|1996-06-23|1996-05-28|1996-06-27|COLLECT COD|REG AIR|fluffily fluffy accounts. even packages hi\n2695|183259|8296|1|21|28187.25|0.07|0.00|N|O|1996-10-04|1996-11-02|1996-10-21|NONE|MAIL|y regular pinto beans. evenly regular packa\n2695|18835|6339|2|44|77168.52|0.09|0.07|N|O|1996-10-05|1996-10-10|1996-11-01|NONE|MAIL|ts. busy platelets boost\n2695|143480|8509|3|21|31993.08|0.02|0.07|N|O|1996-09-13|1996-09-25|1996-10-13|NONE|TRUCK|s. furiously ironic platelets ar\n2695|57398|9904|4|16|21686.24|0.08|0.08|N|O|1996-11-16|1996-10-05|1996-11-22|NONE|TRUCK|its. theodolites sleep slyly\n2695|85282|2807|5|40|50691.20|0.02|0.03|N|O|1996-11-02|1996-10-26|1996-11-14|NONE|FOB|ructions. pending\n2720|44483|4484|1|5|7137.40|0.10|0.06|A|F|1993-06-24|1993-08-08|1993-07-08|NONE|FOB|ously ironic foxes thrash\n2720|16655|6656|2|42|66009.30|0.09|0.03|R|F|1993-07-25|1993-07-23|1993-08-23|COLLECT COD|REG AIR|fter the inst\n2720|119065|9066|3|50|54203.00|0.10|0.02|A|F|1993-08-10|1993-07-29|1993-09-06|NONE|SHIP|l requests. deposits nag furiously\n2720|108938|1449|4|49|95399.57|0.06|0.02|A|F|1993-07-09|1993-07-14|1993-07-13|NONE|REG AIR| accounts. fluffily bold pack\n2720|120884|3397|5|27|51431.76|0.04|0.00|R|F|1993-06-29|1993-08-06|1993-07-28|NONE|TRUCK|eas. carefully regular \n2721|182460|4979|1|49|75580.54|0.00|0.08|N|O|1996-02-14|1996-04-26|1996-03-02|DELIVER IN PERSON|AIR|ounts poach carefu\n2721|2633|2634|2|2|3071.26|0.02|0.05|N|O|1996-02-13|1996-03-14|1996-02-28|TAKE BACK RETURN|TRUCK| slyly final requests against \n2722|123803|1340|1|21|38362.80|0.09|0.01|A|F|1994-07-29|1994-06-26|1994-08-09|NONE|RAIL|e carefully around the furiously ironic pac\n2722|145739|5740|2|15|26770.95|0.05|0.03|R|F|1994-07-02|1994-06-01|1994-07-13|COLLECT COD|AIR|refully final asympt\n2722|33709|6213|3|16|26283.20|0.04|0.06|R|F|1994-05-25|1994-06-09|1994-05-26|NONE|MAIL|ts besides the fluffy,\n2723|12688|5190|1|47|75231.96|0.09|0.07|N|O|1995-12-05|1995-11-19|1995-12-11|TAKE BACK RETURN|AIR|furiously r\n2723|31913|6920|2|10|18449.10|0.06|0.08|N|O|1995-11-27|1995-11-29|1995-12-12|DELIVER IN PERSON|MAIL|al, special r\n2723|161747|4264|3|2|3617.48|0.10|0.01|N|O|1995-11-09|1995-11-10|1995-11-14|TAKE BACK RETURN|FOB| courts boost quickly about th\n2723|81212|6229|4|12|14318.52|0.01|0.05|N|O|1995-12-24|1995-11-15|1996-01-17|DELIVER IN PERSON|RAIL|bold foxes are bold packages. regular, fin\n2723|128077|8078|5|40|44202.80|0.09|0.05|N|O|1995-11-17|1995-11-22|1995-11-18|TAKE BACK RETURN|MAIL|unwind fluffily carefully regular realms.\n2724|91316|3826|1|47|61443.57|0.09|0.01|A|F|1994-11-23|1994-11-13|1994-12-03|COLLECT COD|TRUCK|unusual patterns nag. special p\n2724|146966|6967|2|21|42272.16|0.09|0.02|A|F|1994-11-25|1994-10-15|1994-12-07|COLLECT COD|RAIL|as. carefully regular dependencies wak\n2724|49839|4848|3|22|39354.26|0.04|0.06|A|F|1994-09-19|1994-11-18|1994-10-17|TAKE BACK RETURN|TRUCK|express fo\n2724|34495|9502|4|1|1429.49|0.07|0.03|A|F|1994-12-26|1994-11-27|1995-01-07|NONE|MAIL|lyly carefully blithe theodolites-- pl\n2724|148071|3100|5|29|32453.03|0.05|0.06|A|F|1995-01-10|1994-11-17|1995-02-04|COLLECT COD|MAIL|l requests hagg\n2725|117595|107|1|23|37089.57|0.10|0.08|R|F|1994-08-25|1994-06-22|1994-08-28|TAKE BACK RETURN|REG AIR|y regular deposits. brave foxes \n2725|4819|7320|2|41|70676.21|0.01|0.00|R|F|1994-07-05|1994-06-29|1994-08-02|DELIVER IN PERSON|TRUCK|ns sleep furiously c\n2725|188318|5873|3|15|21094.65|0.07|0.03|R|F|1994-08-06|1994-08-09|1994-08-15|TAKE BACK RETURN|AIR|? furiously regular a\n2726|655|5656|1|50|77782.50|0.00|0.06|R|F|1993-03-04|1993-01-29|1993-03-28|COLLECT COD|TRUCK| furiously bold theodolites\n2727|150928|5959|1|3|5936.76|0.03|0.01|N|O|1998-06-18|1998-06-06|1998-06-23|NONE|RAIL| the carefully regular foxes u\n2752|30702|5709|1|41|66940.70|0.02|0.05|A|F|1994-03-02|1994-01-31|1994-03-06|DELIVER IN PERSON|AIR|tructions hag\n2752|6810|1811|2|29|49787.49|0.02|0.04|R|F|1994-01-22|1994-01-08|1994-01-28|COLLECT COD|TRUCK|gly blithely re\n2752|55050|5051|3|4|4020.20|0.08|0.00|A|F|1993-12-14|1994-02-13|1994-01-05|DELIVER IN PERSON|TRUCK|telets haggle. regular, final \n2752|23153|660|4|40|43046.00|0.09|0.06|A|F|1994-01-24|1994-01-18|1994-02-22|DELIVER IN PERSON|MAIL|into beans are after the sly\n2752|125934|959|5|22|43118.46|0.03|0.04|A|F|1994-03-20|1994-02-08|1994-04-01|TAKE BACK RETURN|TRUCK|equests nag. regular dependencies are furio\n2752|169347|6896|6|21|29743.14|0.09|0.05|R|F|1994-01-01|1994-01-24|1994-01-24|COLLECT COD|SHIP| along the quickly \n2752|198918|8919|7|38|76642.58|0.08|0.00|R|F|1994-02-23|1993-12-23|1994-03-24|DELIVER IN PERSON|SHIP|es boost. slyly silent ideas\n2753|12855|359|1|6|10607.10|0.10|0.04|A|F|1993-12-30|1994-01-28|1994-01-29|COLLECT COD|TRUCK|s accounts\n2753|47010|4523|2|40|38280.40|0.03|0.05|A|F|1994-01-06|1994-02-13|1994-02-03|DELIVER IN PERSON|SHIP|latelets kindle slyly final depos\n2753|88608|3625|3|30|47898.00|0.00|0.07|A|F|1994-01-26|1994-01-29|1994-02-02|NONE|RAIL|ans wake fluffily blithely iro\n2753|30853|3357|4|7|12486.95|0.07|0.03|R|F|1994-02-11|1994-01-22|1994-03-10|DELIVER IN PERSON|AIR|xpress ideas detect b\n2753|136899|6900|5|36|69692.04|0.04|0.08|R|F|1994-03-15|1994-01-03|1994-04-03|DELIVER IN PERSON|SHIP|gle slyly final c\n2753|49203|9204|6|17|19587.40|0.01|0.08|A|F|1994-03-08|1994-01-17|1994-03-11|TAKE BACK RETURN|REG AIR| carefully bold deposits sublate s\n2753|147138|7139|7|20|23702.60|0.01|0.06|R|F|1994-02-24|1994-02-04|1994-03-23|DELIVER IN PERSON|FOB| express pack\n2754|148092|607|1|4|4560.36|0.05|0.08|A|F|1994-07-13|1994-05-15|1994-08-02|NONE|REG AIR|blithely silent requests. regular depo\n2754|176662|4214|2|19|33034.54|0.01|0.07|A|F|1994-06-27|1994-05-06|1994-06-28|NONE|FOB|latelets hag\n2755|91404|3914|1|19|26512.60|0.10|0.00|R|F|1992-02-11|1992-03-15|1992-02-14|TAKE BACK RETURN|MAIL|furiously special deposits\n2755|23483|8488|2|11|15471.28|0.03|0.08|A|F|1992-04-12|1992-05-07|1992-04-21|COLLECT COD|RAIL|egular excuses sleep carefully.\n2755|63720|6227|3|21|35358.12|0.08|0.04|R|F|1992-02-13|1992-04-20|1992-03-02|NONE|AIR|furious re\n2755|130096|7636|4|5|5630.45|0.01|0.00|A|F|1992-02-27|1992-04-07|1992-03-09|TAKE BACK RETURN|AIR|e the furi\n2755|115039|5040|5|48|50593.44|0.05|0.06|R|F|1992-03-22|1992-03-10|1992-04-14|DELIVER IN PERSON|MAIL|yly even epitaphs for the \n2756|117254|7255|1|35|44493.75|0.03|0.02|R|F|1994-06-08|1994-06-01|1994-06-21|TAKE BACK RETURN|AIR| deposits grow bold sheaves; iro\n2756|79212|4227|2|47|55986.87|0.06|0.01|R|F|1994-05-10|1994-05-25|1994-05-13|NONE|AIR|e final, f\n2756|104806|7317|3|31|56134.80|0.01|0.07|A|F|1994-07-27|1994-07-06|1994-08-22|TAKE BACK RETURN|TRUCK|en instructions use quickly.\n2756|71763|4271|4|30|52042.80|0.00|0.04|A|F|1994-06-05|1994-06-30|1994-06-14|DELIVER IN PERSON|TRUCK|ular packages. regular deposi\n2757|147369|9884|1|26|36825.36|0.07|0.00|N|O|1995-08-19|1995-10-02|1995-09-06|DELIVER IN PERSON|MAIL|around the blithely\n2757|21865|4368|2|12|21442.32|0.07|0.08|N|O|1995-08-01|1995-09-04|1995-08-08|TAKE BACK RETURN|SHIP| regular, eve\n2757|72740|5248|3|17|29116.58|0.10|0.04|N|O|1995-09-06|1995-09-27|1995-09-22|DELIVER IN PERSON|AIR|er the furiously silent \n2757|139197|4224|4|25|30904.75|0.08|0.01|N|O|1995-11-09|1995-09-12|1995-11-23|NONE|AIR|uickly regular \n2757|69696|4709|5|14|23319.66|0.04|0.05|N|O|1995-09-01|1995-08-24|1995-09-03|TAKE BACK RETURN|SHIP|special deposits u\n2758|120880|5905|1|20|38017.60|0.02|0.04|N|O|1998-07-27|1998-09-10|1998-08-21|TAKE BACK RETURN|AIR|ptotes sleep furiously\n2758|22897|5400|2|17|30938.13|0.10|0.06|N|O|1998-09-25|1998-10-03|1998-10-25|NONE|MAIL| accounts! qui\n2758|25560|565|3|1|1485.56|0.06|0.02|N|O|1998-10-09|1998-09-15|1998-10-16|NONE|TRUCK|ake furious\n2759|58995|6511|1|10|19539.90|0.10|0.03|R|F|1993-12-14|1994-01-08|1994-01-01|COLLECT COD|FOB|s. busily ironic theodo\n2759|112674|7697|2|37|62406.79|0.00|0.06|R|F|1994-03-05|1994-02-22|1994-03-18|DELIVER IN PERSON|REG AIR|lar Tiresias affix ironically carefully sp\n2759|111646|6669|3|11|18234.04|0.03|0.08|A|F|1994-01-24|1994-01-16|1994-02-21|DELIVER IN PERSON|TRUCK|hely regular \n2759|22777|7782|4|31|52692.87|0.02|0.05|A|F|1994-01-11|1994-01-15|1994-01-23|NONE|SHIP|ithely aft\n2784|32335|7342|1|45|57029.85|0.03|0.01|N|O|1998-02-15|1998-04-07|1998-02-26|COLLECT COD|AIR|yly along the asymptotes. reque\n2784|53524|3525|2|23|33982.96|0.03|0.05|N|O|1998-03-28|1998-02-07|1998-04-17|DELIVER IN PERSON|AIR|uests lose after \n2784|174715|9750|3|40|71588.40|0.07|0.01|N|O|1998-04-28|1998-03-19|1998-05-03|DELIVER IN PERSON|TRUCK|deas nag furiously never unusual \n2784|28004|8005|4|3|2796.00|0.04|0.03|N|O|1998-01-19|1998-04-05|1998-02-05|TAKE BACK RETURN|AIR|n packages. foxes haggle quickly sile\n2785|99049|4068|1|34|35633.36|0.08|0.06|N|O|1995-08-07|1995-09-09|1995-09-05|NONE|RAIL|ly final packages haggl\n2785|109744|7275|2|37|64888.38|0.08|0.04|N|O|1995-07-25|1995-09-12|1995-08-06|DELIVER IN PERSON|TRUCK|tructions. furiously \n2785|64524|2043|3|33|49121.16|0.08|0.06|N|O|1995-10-16|1995-08-24|1995-11-02|DELIVER IN PERSON|MAIL|fter the furiously final p\n2785|47167|2176|4|34|37881.44|0.00|0.02|N|O|1995-09-16|1995-09-09|1995-10-11|COLLECT COD|SHIP|kages wake carefully silent \n2786|135566|3106|1|15|24023.40|0.03|0.04|A|F|1992-05-19|1992-05-08|1992-05-28|COLLECT COD|TRUCK|low deposits are ironic\n2786|50144|7660|2|42|45953.88|0.10|0.04|R|F|1992-05-15|1992-04-22|1992-05-30|DELIVER IN PERSON|AIR|unts are against the furious\n2786|155318|349|3|41|56305.71|0.04|0.05|R|F|1992-07-01|1992-06-04|1992-07-13|COLLECT COD|RAIL|ix requests. bold requests a\n2786|22696|2697|4|24|38848.56|0.05|0.02|A|F|1992-04-04|1992-06-09|1992-05-02|DELIVER IN PERSON|MAIL|ans. slyly unusual platelets detect. unus\n2786|49478|4487|5|43|61381.21|0.06|0.03|R|F|1992-04-22|1992-05-13|1992-04-29|NONE|RAIL|ons. theodolites after\n2786|161228|3745|6|21|27073.62|0.08|0.00|A|F|1992-05-03|1992-05-01|1992-05-14|COLLECT COD|AIR|slow instructi\n2787|32479|9989|1|4|5645.88|0.04|0.04|N|O|1996-01-26|1995-11-26|1996-02-20|TAKE BACK RETURN|SHIP|ts. instructions nag furiously according \n2788|176196|6197|1|16|20355.04|0.06|0.06|A|F|1994-10-04|1994-11-25|1994-10-18|DELIVER IN PERSON|AIR| requests wake carefully. carefully si\n2789|162410|9959|1|16|23558.56|0.03|0.02|N|O|1998-04-18|1998-05-25|1998-05-12|DELIVER IN PERSON|REG AIR|o beans use carefully\n2789|22835|2836|2|41|72071.03|0.02|0.05|N|O|1998-03-20|1998-05-15|1998-03-21|COLLECT COD|MAIL|d packages-- fluffily specia\n2789|175892|927|3|33|64940.37|0.06|0.02|N|O|1998-04-21|1998-05-02|1998-04-30|COLLECT COD|TRUCK|deposits. ironic \n2789|15982|985|4|47|89205.06|0.02|0.04|N|O|1998-03-29|1998-05-05|1998-04-07|NONE|RAIL|usly busy packages wake against the unusual\n2789|196629|4187|5|23|39689.26|0.02|0.07|N|O|1998-03-25|1998-05-10|1998-04-24|COLLECT COD|RAIL|cording to the careful de\n2789|143603|3604|6|16|26345.60|0.07|0.03|N|O|1998-05-11|1998-05-08|1998-05-24|TAKE BACK RETURN|RAIL|d the carefully iron\n2789|132206|7233|7|42|52004.40|0.01|0.00|N|O|1998-04-28|1998-05-17|1998-05-24|TAKE BACK RETURN|AIR|ending packages shoul\n2790|184300|4301|1|27|37376.10|0.06|0.08|R|F|1994-09-04|1994-09-27|1994-09-16|TAKE BACK RETURN|MAIL|ilent packages cajole. quickly ironic requ\n2790|116975|9487|2|50|99598.50|0.00|0.06|A|F|1994-12-08|1994-11-17|1994-12-19|NONE|RAIL|fter the regular ideas. f\n2790|183138|8175|3|19|23201.47|0.06|0.00|R|F|1994-10-23|1994-10-03|1994-10-26|TAKE BACK RETURN|RAIL|uffily even excuses. furiously thin\n2790|196404|6405|4|24|36009.60|0.07|0.01|A|F|1994-12-04|1994-10-10|1994-12-25|NONE|MAIL|ments. slyly f\n2790|147955|7956|5|11|22032.45|0.08|0.03|A|F|1994-09-28|1994-11-14|1994-10-04|TAKE BACK RETURN|AIR|lar requests poach slyly foxes\n2790|72637|5145|6|13|20925.19|0.08|0.00|R|F|1994-09-20|1994-10-10|1994-10-20|COLLECT COD|SHIP|n deposits according to the regul\n2790|3931|1432|7|32|58717.76|0.08|0.02|A|F|1994-09-25|1994-10-26|1994-10-01|NONE|SHIP|ully pending\n2791|58694|8695|1|49|80981.81|0.10|0.04|A|F|1995-01-11|1994-11-10|1995-02-08|COLLECT COD|MAIL| accounts sleep at the bold, regular pinto \n2791|62804|2805|2|4|7067.20|0.10|0.08|A|F|1995-01-02|1994-12-28|1995-01-29|NONE|SHIP|slyly bold packages boost. slyly\n2791|132300|4814|3|44|58621.20|0.08|0.06|R|F|1994-11-17|1994-11-12|1994-12-14|NONE|FOB|heodolites use furio\n2791|155894|3440|4|24|46797.36|0.04|0.02|R|F|1995-01-30|1994-11-20|1995-02-08|DELIVER IN PERSON|TRUCK|ilent forges. quickly special pinto beans \n2791|104521|2052|5|8|12204.16|0.02|0.04|R|F|1995-01-30|1994-11-24|1995-02-13|NONE|FOB|se. close ideas alongs\n2791|74435|1957|6|9|12684.87|0.08|0.02|R|F|1994-11-19|1994-12-14|1994-12-10|TAKE BACK RETURN|AIR|pendencies. blithely bold patterns acr\n2791|28635|6142|7|26|40654.38|0.06|0.03|R|F|1995-02-06|1994-12-07|1995-02-23|DELIVER IN PERSON|AIR|uriously special instructio\n2816|58258|8259|1|33|40136.25|0.00|0.07|R|F|1994-10-19|1994-11-10|1994-11-09|NONE|REG AIR|s; slyly even theodo\n2816|141342|1343|2|4|5533.36|0.05|0.04|R|F|1994-12-11|1994-12-07|1995-01-03|NONE|FOB|. blithely pending id\n2816|120406|2919|3|4|5705.60|0.02|0.06|R|F|1994-12-12|1994-12-05|1994-12-30|NONE|RAIL| requests print above the final deposits\n2817|59597|2103|1|25|38914.75|0.07|0.01|R|F|1994-04-21|1994-06-20|1994-05-07|DELIVER IN PERSON|FOB|doze blithely.\n2817|31271|3775|2|5|6011.35|0.03|0.04|A|F|1994-05-07|1994-05-31|1994-05-12|TAKE BACK RETURN|AIR|furiously unusual theodolites use furiou\n2817|171177|8729|3|35|43685.95|0.01|0.07|A|F|1994-05-20|1994-06-03|1994-05-22|COLLECT COD|FOB|gular foxes\n2817|160884|885|4|4|7779.52|0.00|0.05|R|F|1994-06-04|1994-06-11|1994-06-10|NONE|TRUCK|n accounts wake across the fluf\n2818|120379|7916|1|12|16792.44|0.10|0.03|A|F|1995-02-01|1995-03-10|1995-02-16|NONE|AIR|lms. quickly bold asymp\n2818|198766|3805|2|22|41024.72|0.06|0.07|R|F|1995-02-28|1995-03-10|1995-03-06|TAKE BACK RETURN|RAIL|egrate toward the carefully iron\n2818|44234|4235|3|11|12960.53|0.01|0.06|R|F|1995-02-18|1995-02-11|1995-03-19|TAKE BACK RETURN|TRUCK|ggle across the carefully blithe\n2818|39711|7221|4|32|52822.72|0.08|0.08|R|F|1995-02-04|1995-03-05|1995-02-18|COLLECT COD|REG AIR|arefully! ac\n2818|17380|4884|5|42|54489.96|0.08|0.04|A|F|1995-02-12|1995-02-19|1995-03-13|COLLECT COD|MAIL|ar accounts wake carefully a\n2818|90636|8164|6|7|11386.41|0.06|0.03|R|F|1995-03-24|1995-03-09|1995-04-06|TAKE BACK RETURN|TRUCK|ly according to the r\n2819|69083|9084|1|17|17885.36|0.08|0.08|A|F|1994-07-16|1994-07-15|1994-07-17|TAKE BACK RETURN|RAIL|en deposits above the f\n2819|66461|3980|2|12|17129.52|0.03|0.08|R|F|1994-07-18|1994-06-24|1994-07-28|NONE|MAIL| regular, regular a\n2819|4924|2425|3|28|51209.76|0.03|0.08|R|F|1994-05-09|1994-07-02|1994-05-15|NONE|RAIL|ckages sublate carefully closely regular \n2819|152830|2831|4|5|9414.15|0.00|0.02|R|F|1994-05-29|1994-06-12|1994-06-28|NONE|TRUCK| fluffily unusual foxes sleep caref\n2819|199179|4218|5|6|7669.02|0.03|0.01|A|F|1994-07-22|1994-08-02|1994-07-29|NONE|REG AIR|eas after the carefully express pack\n2820|173150|702|1|23|28132.45|0.04|0.08|R|F|1994-07-10|1994-08-08|1994-07-21|NONE|MAIL| was furiously. deposits among the ironic\n2820|125718|3255|2|33|57542.43|0.08|0.06|A|F|1994-07-07|1994-08-17|1994-08-02|DELIVER IN PERSON|AIR|carefully even pinto beans. \n2820|140013|7556|3|38|40014.38|0.03|0.08|A|F|1994-09-10|1994-08-07|1994-10-07|TAKE BACK RETURN|MAIL|ests despite the carefully unusual a\n2820|196270|8790|4|40|54650.80|0.06|0.06|A|F|1994-08-08|1994-07-30|1994-08-21|TAKE BACK RETURN|REG AIR|g multipliers. final c\n2821|180834|8389|1|4|7659.32|0.00|0.00|A|F|1993-09-15|1993-10-02|1993-09-17|TAKE BACK RETURN|TRUCK|nding foxes.\n2821|71159|6174|2|4|4520.60|0.09|0.00|A|F|1993-11-19|1993-09-20|1993-11-27|TAKE BACK RETURN|TRUCK|ual multipliers. final deposits cajol\n2821|163639|8672|3|27|45971.01|0.01|0.01|A|F|1993-11-27|1993-10-11|1993-12-08|COLLECT COD|TRUCK|requests. blit\n2822|150765|3281|1|39|70814.64|0.04|0.02|R|F|1993-09-11|1993-08-29|1993-09-18|NONE|MAIL|kly about the sly\n2823|85712|8221|1|45|76396.95|0.03|0.04|N|O|1995-12-28|1995-11-27|1996-01-02|DELIVER IN PERSON|SHIP|furiously special idea\n2823|159624|4655|2|18|30305.16|0.00|0.03|N|O|1995-11-11|1995-10-30|1995-12-08|TAKE BACK RETURN|TRUCK| final deposits. furiously regular foxes u\n2823|185277|7796|3|11|14984.97|0.07|0.02|N|O|1995-12-10|1995-11-24|1995-12-21|DELIVER IN PERSON|SHIP|bold requests nag blithely s\n2823|138806|3833|4|48|88550.40|0.09|0.03|N|O|1995-11-21|1995-10-30|1995-11-27|NONE|SHIP|ously busily slow excus\n2823|98943|3962|5|18|34954.92|0.04|0.06|N|O|1995-11-09|1995-10-30|1995-11-19|NONE|AIR|eas. decoys cajole deposi\n2823|122443|7468|6|20|29308.80|0.07|0.00|N|O|1995-11-13|1995-12-06|1995-12-07|NONE|MAIL|its sleep between the unusual, ironic pac\n2823|85076|2601|7|12|12732.84|0.02|0.04|N|O|1995-12-22|1995-11-20|1996-01-13|NONE|REG AIR|the slyly ironic dolphins; fin\n2848|64222|6729|1|44|52193.68|0.01|0.05|R|F|1992-04-14|1992-05-09|1992-04-19|DELIVER IN PERSON|MAIL|ions. slyly express instructions n\n2848|164452|4453|2|8|12131.60|0.07|0.01|A|F|1992-03-21|1992-05-18|1992-04-07|DELIVER IN PERSON|TRUCK|. silent, final ideas sublate packages. ir\n2848|137332|9846|3|8|10954.64|0.07|0.08|A|F|1992-06-20|1992-04-12|1992-07-09|NONE|SHIP|sly regular foxes. \n2848|124948|4949|4|34|67079.96|0.02|0.08|A|F|1992-03-15|1992-04-24|1992-04-12|TAKE BACK RETURN|RAIL|ts along the blithely regu\n2848|194197|6717|5|18|23241.42|0.07|0.03|R|F|1992-04-10|1992-06-01|1992-05-05|DELIVER IN PERSON|TRUCK|osits haggle. stealthily ironic packa\n2849|153805|6321|1|16|29740.80|0.09|0.08|N|O|1996-05-20|1996-07-23|1996-06-18|NONE|TRUCK|. furiously regular requ\n2849|186843|1880|2|39|75263.76|0.10|0.03|N|O|1996-05-22|1996-07-18|1996-06-05|TAKE BACK RETURN|SHIP|s sleep furiously silently regul\n2849|59872|9873|3|24|43964.88|0.01|0.05|N|O|1996-06-12|1996-07-10|1996-06-27|TAKE BACK RETURN|AIR|e slyly even asymptotes. slo\n2849|54230|1746|4|48|56843.04|0.05|0.02|N|O|1996-05-03|1996-06-05|1996-05-28|NONE|AIR|mong the carefully regular theodol\n2849|27255|2260|5|30|35467.50|0.10|0.06|N|O|1996-08-24|1996-07-08|1996-09-03|TAKE BACK RETURN|SHIP|ly. carefully silent\n2849|68651|6170|6|30|48589.50|0.06|0.07|N|O|1996-06-20|1996-07-23|1996-07-06|NONE|FOB|yly furiously even id\n2850|96931|4459|1|43|82900.99|0.02|0.05|N|O|1997-01-11|1996-11-03|1997-02-01|COLLECT COD|REG AIR|unusual accounts\n2850|109283|6814|2|30|38768.40|0.09|0.01|N|O|1996-12-14|1996-11-29|1997-01-03|COLLECT COD|AIR|even ideas. busy pinto beans sleep above t\n2850|104731|4732|3|49|85050.77|0.09|0.04|N|O|1996-10-07|1996-12-12|1996-10-12|TAKE BACK RETURN|MAIL| slyly unusual req\n2850|198372|5930|4|4|5881.48|0.04|0.04|N|O|1996-10-28|1996-12-26|1996-11-07|COLLECT COD|RAIL|al deposits cajole carefully quickly \n2851|147452|9967|1|8|11995.60|0.09|0.03|N|O|1997-11-12|1997-11-22|1997-12-11|NONE|REG AIR|y special theodolites. carefully\n2852|176179|1214|1|6|7531.02|0.01|0.01|R|F|1993-03-02|1993-04-11|1993-03-11|TAKE BACK RETURN|RAIL| accounts above the furiously un\n2852|40065|7578|2|24|24121.44|0.05|0.07|R|F|1993-01-18|1993-03-13|1993-02-14|DELIVER IN PERSON|MAIL| the blithe\n2852|163403|952|3|29|42525.60|0.09|0.05|R|F|1993-04-21|1993-03-22|1993-05-02|COLLECT COD|SHIP|lyly ironi\n2852|99183|4202|4|12|14186.16|0.08|0.02|A|F|1993-02-25|1993-03-24|1993-03-07|TAKE BACK RETURN|TRUCK|le. request\n2852|153182|5698|5|28|34585.04|0.05|0.03|R|F|1993-02-08|1993-03-30|1993-02-11|NONE|MAIL|e accounts. caref\n2853|138855|1369|1|14|26513.90|0.07|0.05|R|F|1994-05-16|1994-07-01|1994-05-27|NONE|TRUCK|oach slyly along t\n2853|133051|591|2|26|28185.30|0.06|0.01|R|F|1994-06-26|1994-06-05|1994-07-02|TAKE BACK RETURN|MAIL|dolphins wake slyly. blith\n2853|172674|5192|3|40|69866.80|0.06|0.04|A|F|1994-08-06|1994-06-24|1994-08-29|NONE|RAIL|lyly. pearls cajole. final accounts ca\n2853|131257|8797|4|20|25765.00|0.02|0.04|A|F|1994-08-30|1994-06-16|1994-09-06|TAKE BACK RETURN|TRUCK|e slyly silent foxes. express deposits sno\n2853|35872|879|5|1|1807.87|0.08|0.05|R|F|1994-09-01|1994-06-27|1994-09-12|TAKE BACK RETURN|FOB|refully slyly quick packages. final c\n2854|180193|7748|1|46|58566.74|0.00|0.04|A|F|1994-09-22|1994-08-02|1994-09-30|COLLECT COD|AIR|. furiously regular deposits across th\n2854|87581|7582|2|29|45488.82|0.09|0.07|R|F|1994-07-06|1994-08-26|1994-07-09|COLLECT COD|SHIP|y slyly ironic accounts. foxes haggle slyl\n2854|159170|1686|3|20|24583.40|0.08|0.01|R|F|1994-09-18|1994-08-03|1994-10-12|COLLECT COD|AIR|rs impress after the deposits. \n2854|169448|9449|4|34|51592.96|0.06|0.03|A|F|1994-09-06|1994-08-07|1994-09-22|NONE|REG AIR|age carefully\n2854|101316|1317|5|7|9221.17|0.03|0.06|A|F|1994-09-23|1994-08-14|1994-10-10|DELIVER IN PERSON|REG AIR| the pending\n2854|17762|264|6|13|21836.88|0.04|0.03|R|F|1994-09-15|1994-08-18|1994-09-19|DELIVER IN PERSON|SHIP| excuses wak\n2855|32224|7231|1|50|57811.00|0.03|0.07|A|F|1993-05-20|1993-06-28|1993-06-16|TAKE BACK RETURN|TRUCK|beans. deposits \n2880|34078|9085|1|40|40482.80|0.09|0.00|A|F|1992-05-26|1992-06-01|1992-05-31|COLLECT COD|TRUCK|even requests. quick\n2880|138854|6394|2|26|49214.10|0.07|0.07|R|F|1992-04-12|1992-04-15|1992-04-28|NONE|RAIL|ully among the regular warthogs\n2880|114490|7002|3|42|63188.58|0.01|0.01|R|F|1992-06-17|1992-05-29|1992-07-11|NONE|REG AIR|ions. carefully final accounts are unusual,\n2880|17656|158|4|46|72387.90|0.02|0.02|A|F|1992-04-21|1992-06-05|1992-05-16|COLLECT COD|RAIL|eep quickly according to t\n2881|179343|1861|1|16|22757.44|0.02|0.06|A|F|1992-06-21|1992-06-27|1992-07-03|TAKE BACK RETURN|TRUCK|usly bold \n2881|9181|9182|2|1|1090.18|0.09|0.03|A|F|1992-05-13|1992-07-21|1992-05-18|COLLECT COD|MAIL|final theodolites. quickly\n2881|92018|7037|3|21|21210.21|0.07|0.03|A|F|1992-05-28|1992-07-03|1992-06-02|TAKE BACK RETURN|SHIP|hely express Tiresias. final dependencies \n2881|139814|7354|4|7|12976.67|0.06|0.01|R|F|1992-08-03|1992-07-10|1992-08-27|NONE|REG AIR|ironic packages are carefully final ac\n2882|3763|6264|1|14|23334.64|0.09|0.02|N|O|1995-09-28|1995-11-11|1995-10-18|TAKE BACK RETURN|MAIL|kly. even requests w\n2882|41007|8520|2|30|28440.00|0.00|0.00|N|O|1995-10-15|1995-10-13|1995-10-25|NONE|REG AIR|among the furiously even theodolites. regu\n2882|196935|9455|3|29|58925.97|0.10|0.08|N|O|1995-09-10|1995-11-01|1995-10-02|NONE|TRUCK|kages. furiously ironic\n2882|77298|4820|4|27|34432.83|0.06|0.02|N|O|1995-09-04|1995-11-11|1995-09-12|DELIVER IN PERSON|MAIL|rding to the regu\n2882|133878|3879|5|32|61179.84|0.07|0.03|N|O|1995-10-21|1995-11-10|1995-11-01|COLLECT COD|RAIL|sts. quickly regular e\n2882|86425|6426|6|47|66336.74|0.06|0.03|N|O|1995-09-13|1995-09-21|1995-09-14|NONE|REG AIR|l, special\n2883|91|2592|1|33|32705.97|0.08|0.07|R|F|1995-02-26|1995-03-04|1995-03-01|NONE|RAIL|s. final i\n2883|124341|4342|2|27|36864.18|0.00|0.02|A|F|1995-03-12|1995-03-10|1995-04-04|TAKE BACK RETURN|REG AIR|s. brave pinto beans nag furiously\n2883|188690|6245|3|47|83598.43|0.05|0.04|R|F|1995-01-29|1995-04-19|1995-02-05|DELIVER IN PERSON|SHIP|ep carefully ironic\n2883|97483|5011|4|23|34051.04|0.00|0.02|R|F|1995-02-03|1995-03-17|1995-02-19|TAKE BACK RETURN|AIR| even requests cajole. special, regular \n2883|194465|9504|5|36|56140.56|0.07|0.06|A|F|1995-05-02|1995-03-14|1995-05-30|COLLECT COD|MAIL|ests detect slyly special packages\n2884|70492|493|1|41|59962.09|0.03|0.00|N|O|1998-01-02|1997-12-17|1998-01-20|DELIVER IN PERSON|TRUCK|ep. slyly even accounts a\n2884|145132|2675|2|25|29428.25|0.09|0.08|N|O|1998-01-18|1997-12-06|1998-02-16|TAKE BACK RETURN|MAIL|onic theodolites with the instructi\n2884|25106|5107|3|8|8248.80|0.08|0.08|N|O|1997-11-30|1997-11-28|1997-12-14|COLLECT COD|TRUCK|pending accounts about \n2885|3054|8055|1|6|5742.30|0.10|0.01|A|F|1993-01-05|1992-12-12|1993-01-19|COLLECT COD|FOB|ctions solve. slyly regular requests n\n2885|111966|1967|2|4|7911.84|0.07|0.00|A|F|1992-10-09|1992-12-17|1992-11-04|TAKE BACK RETURN|SHIP| pending packages wake. \n2885|716|5717|3|45|72751.95|0.10|0.04|A|F|1992-12-24|1992-10-30|1993-01-04|NONE|SHIP|ess ideas. regular, silen\n2885|31307|6314|4|15|18574.50|0.03|0.04|R|F|1992-10-31|1992-11-24|1992-11-21|DELIVER IN PERSON|MAIL|odolites. boldly pending packages han\n2885|174189|6707|5|43|54316.74|0.06|0.00|R|F|1992-11-17|1992-10-30|1992-12-04|DELIVER IN PERSON|SHIP|cial deposits use bold\n2885|189615|7170|6|5|8523.05|0.01|0.02|R|F|1993-01-06|1992-11-13|1993-02-05|TAKE BACK RETURN|TRUCK|s. slyly express th\n2885|49234|6747|7|40|47329.20|0.05|0.03|A|F|1992-09-23|1992-11-15|1992-10-07|TAKE BACK RETURN|AIR| express depos\n2886|59748|9749|1|1|1707.74|0.09|0.05|A|F|1995-02-01|1994-12-18|1995-02-28|COLLECT COD|REG AIR|eposits fr\n2886|183832|3833|2|38|72801.54|0.02|0.04|A|F|1995-01-21|1995-01-08|1995-01-30|NONE|SHIP|old requests along the fur\n2886|62934|453|3|2|3793.86|0.04|0.07|A|F|1994-11-18|1995-01-31|1994-12-05|COLLECT COD|REG AIR|ar theodolites. e\n2886|129991|7528|4|46|92965.54|0.03|0.08|A|F|1995-02-02|1995-01-26|1995-02-15|TAKE BACK RETURN|SHIP|ously final packages sleep blithely regular\n2887|65504|517|1|11|16164.50|0.06|0.00|N|O|1997-07-08|1997-07-17|1997-07-15|COLLECT COD|SHIP|ackages. unusual, speci\n2887|111534|4046|2|17|26274.01|0.00|0.08|N|O|1997-08-31|1997-07-04|1997-09-17|DELIVER IN PERSON|SHIP|fily final packages. regula\n2912|121445|6470|1|8|11731.52|0.06|0.04|A|F|1992-04-09|1992-04-19|1992-04-26|NONE|RAIL|hs cajole over the slyl\n2912|114577|7089|2|18|28648.26|0.00|0.08|R|F|1992-03-13|1992-04-19|1992-03-30|TAKE BACK RETURN|RAIL|unts cajole reg\n2913|122736|273|1|39|68590.47|0.06|0.04|N|O|1997-08-28|1997-09-27|1997-09-02|TAKE BACK RETURN|AIR|. final packages a\n2913|21727|9234|2|22|36271.84|0.10|0.07|N|O|1997-09-18|1997-08-11|1997-10-02|COLLECT COD|MAIL|riously pending realms. blithely even pac\n2913|165309|2858|3|17|23363.10|0.07|0.04|N|O|1997-10-21|1997-09-25|1997-11-20|NONE|FOB|requests doze quickly. furious\n2913|142502|2503|4|5|7722.50|0.10|0.07|N|O|1997-10-07|1997-08-25|1997-10-09|TAKE BACK RETURN|RAIL|haggle. even, bold instructi\n2913|14885|7387|5|13|23398.44|0.03|0.01|N|O|1997-10-02|1997-08-20|1997-10-26|COLLECT COD|MAIL|inos are carefully alongside of the bol\n2913|167718|2751|6|35|62499.85|0.06|0.08|N|O|1997-08-30|1997-08-21|1997-09-03|COLLECT COD|MAIL|es. quickly even braids against\n2914|65685|5686|1|22|36314.96|0.05|0.06|R|F|1993-05-11|1993-04-09|1993-05-22|DELIVER IN PERSON|FOB| carefully about the fluffily ironic gifts\n2914|162197|7230|2|25|31479.75|0.03|0.04|A|F|1993-05-14|1993-04-04|1993-05-22|NONE|SHIP|cross the carefully even accounts.\n2914|34588|7092|3|4|6090.32|0.00|0.05|R|F|1993-06-11|1993-04-09|1993-06-14|TAKE BACK RETURN|SHIP|s integrate. bold deposits sleep req\n2914|120324|325|4|9|12098.88|0.06|0.01|R|F|1993-06-17|1993-05-26|1993-06-19|NONE|REG AIR|s. carefully final foxes ar\n2915|174124|6642|1|28|33547.36|0.10|0.02|R|F|1994-04-17|1994-06-09|1994-05-10|NONE|MAIL|yly special \n2915|93386|8405|2|12|16552.56|0.00|0.03|A|F|1994-07-18|1994-06-11|1994-07-27|TAKE BACK RETURN|RAIL|accounts. slyly final\n2915|135401|7915|3|15|21546.00|0.07|0.00|A|F|1994-05-01|1994-06-12|1994-05-15|DELIVER IN PERSON|TRUCK|al requests haggle furiousl\n2915|80116|2625|4|43|47132.73|0.06|0.05|R|F|1994-06-02|1994-05-24|1994-06-06|DELIVER IN PERSON|SHIP|into beans dazzle alongside of\n2916|82501|5010|1|21|31153.50|0.06|0.04|N|O|1996-03-11|1996-02-21|1996-03-30|NONE|REG AIR|uickly express ideas over the slyly even \n2917|92630|2631|1|36|58414.68|0.10|0.01|N|O|1998-04-07|1998-02-23|1998-05-01|DELIVER IN PERSON|RAIL|usly ironic d\n2917|20711|712|2|20|32634.20|0.06|0.03|N|O|1997-12-31|1998-01-22|1998-01-12|NONE|MAIL|slyly even ideas wa\n2917|89342|6867|3|4|5325.36|0.02|0.07|N|O|1998-01-10|1998-01-18|1998-02-08|TAKE BACK RETURN|REG AIR|s. unusual instruct\n2917|166826|4375|4|5|9464.10|0.05|0.01|N|O|1997-12-16|1998-01-26|1998-01-07|NONE|RAIL|bove the furiously silent packages. pend\n2917|40412|7925|5|37|50039.17|0.04|0.01|N|O|1997-12-12|1998-02-03|1997-12-23|COLLECT COD|RAIL|dependencies. express \n2917|193921|1479|6|7|14104.44|0.05|0.01|N|O|1998-03-21|1998-03-03|1998-03-25|NONE|REG AIR|ly about the regular accounts. carefully pe\n2918|77412|2427|1|24|33345.84|0.10|0.03|N|O|1996-12-20|1996-10-28|1996-12-26|DELIVER IN PERSON|FOB| quickly. express requests haggle careful\n2919|101323|3834|1|2|2648.64|0.03|0.05|R|F|1993-12-28|1994-02-23|1994-01-18|COLLECT COD|TRUCK|re slyly. regular ideas detect furiousl\n2919|120203|7740|2|49|59936.80|0.07|0.02|R|F|1993-12-16|1994-02-28|1993-12-19|COLLECT COD|FOB|hely final inst\n2919|45595|3108|3|44|67785.96|0.07|0.07|A|F|1994-04-01|1994-01-12|1994-04-07|TAKE BACK RETURN|TRUCK|final ideas haggle carefully fluff\n2919|101408|3919|4|44|62013.60|0.00|0.05|R|F|1994-02-04|1994-02-03|1994-03-02|TAKE BACK RETURN|AIR|es doze around the furiously \n2944|119834|9835|1|44|81568.52|0.08|0.05|N|O|1997-12-25|1997-10-28|1998-01-21|COLLECT COD|AIR|ickly special theodolit\n2944|41779|4284|2|44|75713.88|0.06|0.02|N|O|1997-10-28|1997-11-22|1997-11-10|NONE|SHIP|ickly. regular requests haggle. idea\n2944|169657|7206|3|2|3453.30|0.06|0.07|N|O|1997-12-13|1997-12-01|1998-01-08|DELIVER IN PERSON|REG AIR|luffily expr\n2944|16331|3835|4|23|28688.59|0.02|0.03|N|O|1998-01-12|1997-12-03|1998-01-17|TAKE BACK RETURN|MAIL| excuses? regular platelets e\n2944|74247|9262|5|18|21982.32|0.10|0.01|N|O|1998-01-07|1997-10-26|1998-01-27|TAKE BACK RETURN|FOB| furiously slyl\n2944|59267|6783|6|17|20846.42|0.00|0.03|N|O|1997-10-18|1997-11-27|1997-10-29|TAKE BACK RETURN|SHIP|slyly final dolphins sleep silent the\n2944|89264|1773|7|7|8772.82|0.01|0.06|N|O|1997-10-30|1997-11-03|1997-11-03|DELIVER IN PERSON|FOB|fluffily blithely express pea\n2945|58944|8945|1|37|70408.78|0.00|0.02|N|O|1996-02-10|1996-03-20|1996-02-12|COLLECT COD|SHIP|l instructions. regular, regular \n2945|71252|3760|2|30|36697.50|0.05|0.01|N|O|1996-01-19|1996-02-11|1996-01-26|NONE|TRUCK|ular instructions\n2945|126197|6198|3|28|34249.32|0.06|0.02|N|O|1996-03-17|1996-03-13|1996-04-15|COLLECT COD|FOB|le slyly along the eve\n2945|187861|7862|4|34|66261.24|0.08|0.06|N|O|1996-02-03|1996-03-17|1996-02-29|COLLECT COD|REG AIR|at the unusual theodolite\n2945|172280|9832|5|10|13522.80|0.09|0.05|N|O|1996-03-13|1996-03-10|1996-04-06|COLLECT COD|FOB|thely. final courts could hang qu\n2945|96154|8664|6|45|51756.75|0.07|0.00|N|O|1996-03-01|1996-03-25|1996-03-08|TAKE BACK RETURN|MAIL|ainst the final packages\n2945|51230|3736|7|47|55517.81|0.07|0.05|N|O|1996-01-05|1996-02-11|1996-01-12|DELIVER IN PERSON|MAIL|quests use\n2946|9193|4194|1|25|27554.75|0.05|0.02|N|O|1996-05-06|1996-04-23|1996-05-16|DELIVER IN PERSON|SHIP|ic deposits. furiously\n2946|93176|3177|2|48|56120.16|0.03|0.07|N|O|1996-06-02|1996-03-31|1996-06-16|COLLECT COD|TRUCK|oss the platelets. furi\n2946|2968|5469|3|35|65483.60|0.03|0.00|N|O|1996-03-15|1996-04-02|1996-03-26|NONE|REG AIR| sublate along the fluffily iron\n2947|9960|9961|1|37|69188.52|0.09|0.07|N|O|1995-08-09|1995-07-05|1995-08-20|DELIVER IN PERSON|RAIL|e accounts: expres\n2947|185207|7726|2|10|12922.00|0.09|0.07|A|F|1995-06-07|1995-06-26|1995-06-08|NONE|MAIL|lly special \n2948|117469|7470|1|48|71350.08|0.00|0.04|R|F|1994-08-29|1994-10-23|1994-09-23|NONE|TRUCK|unusual excuses use about the \n2948|91710|1711|2|49|83383.79|0.04|0.07|R|F|1994-12-16|1994-11-08|1995-01-07|DELIVER IN PERSON|MAIL|ress requests. furiously blithe foxes \n2949|20139|2642|1|4|4236.52|0.06|0.06|A|F|1994-06-07|1994-06-17|1994-07-04|TAKE BACK RETURN|REG AIR|gular pinto beans wake alongside of the reg\n2949|69423|6942|2|50|69621.00|0.05|0.04|A|F|1994-08-04|1994-06-23|1994-08-17|TAKE BACK RETURN|FOB|gular courts cajole across t\n2949|179465|4500|3|38|58689.48|0.02|0.06|R|F|1994-05-22|1994-05-25|1994-05-27|COLLECT COD|REG AIR|se slyly requests. carefull\n2950|129486|9487|1|32|48495.36|0.01|0.05|N|O|1997-09-21|1997-08-25|1997-10-08|DELIVER IN PERSON|REG AIR|its wake carefully slyly final ideas.\n2950|65786|5787|2|18|31532.04|0.10|0.01|N|O|1997-07-19|1997-08-29|1997-08-17|COLLECT COD|TRUCK|uests cajole furio\n2950|52680|2681|3|14|22857.52|0.01|0.02|N|O|1997-07-29|1997-08-05|1997-07-31|TAKE BACK RETURN|MAIL|ccounts haggle carefully according \n2950|186367|6368|4|45|65401.20|0.08|0.00|N|O|1997-09-05|1997-09-23|1997-09-11|NONE|FOB|ides the b\n2950|60950|951|5|46|87903.70|0.02|0.05|N|O|1997-07-15|1997-09-30|1997-07-25|COLLECT COD|RAIL|to the regular accounts are slyly carefu\n2950|173548|3549|6|27|43781.58|0.01|0.03|N|O|1997-10-01|1997-09-13|1997-10-08|NONE|TRUCK|are alongside of the carefully silent \n2951|2079|7080|1|5|4905.35|0.03|0.03|N|O|1996-03-27|1996-04-16|1996-03-30|NONE|REG AIR|to beans wake ac\n2951|135955|8469|2|24|47782.80|0.07|0.03|N|O|1996-03-24|1996-04-16|1996-04-08|NONE|SHIP| ironic multipliers. express, regular\n2951|186046|3601|3|40|45281.60|0.02|0.07|N|O|1996-05-03|1996-04-20|1996-05-22|COLLECT COD|REG AIR|ial deposits wake fluffily about th\n2951|72188|4696|4|21|24363.78|0.06|0.08|N|O|1996-04-12|1996-04-27|1996-04-14|DELIVER IN PERSON|REG AIR|nt instructions toward the f\n2951|50131|5142|5|15|16216.95|0.07|0.00|N|O|1996-03-25|1996-04-23|1996-03-27|COLLECT COD|REG AIR|inal account\n2951|137099|4639|6|18|20449.62|0.06|0.00|N|O|1996-04-04|1996-04-27|1996-04-06|COLLECT COD|FOB|ep about the final, even package\n2976|8521|3522|1|32|45744.64|0.06|0.00|A|F|1994-01-26|1994-02-13|1994-02-10|NONE|MAIL|nding, ironic deposits sleep f\n2976|3059|3060|2|24|23089.20|0.00|0.03|A|F|1994-03-19|1994-01-26|1994-04-18|COLLECT COD|TRUCK|ronic pinto beans. slyly bol\n2976|9775|4776|3|35|58966.95|0.10|0.07|R|F|1993-12-19|1994-02-14|1994-01-11|NONE|RAIL|boost slyly about the regular, regular re\n2976|81070|6087|4|22|23123.54|0.00|0.04|A|F|1994-02-08|1994-03-03|1994-02-12|TAKE BACK RETURN|FOB|ncies kindle furiously. carefull\n2976|133261|8288|5|13|16825.38|0.00|0.06|A|F|1994-02-06|1994-02-02|1994-02-19|NONE|FOB| furiously final courts boost \n2976|108348|859|6|30|40690.20|0.08|0.03|R|F|1994-03-27|1994-02-01|1994-04-26|TAKE BACK RETURN|RAIL|c ideas! unusual\n2977|69769|7288|1|25|43469.00|0.03|0.07|N|O|1996-09-21|1996-10-06|1996-10-13|TAKE BACK RETURN|RAIL|furiously pe\n2978|89697|9698|1|29|48914.01|0.00|0.08|A|F|1995-06-03|1995-07-25|1995-06-06|NONE|SHIP|ecial ideas promise slyly\n2978|126998|9511|2|42|85049.58|0.01|0.06|N|O|1995-08-19|1995-07-18|1995-09-07|DELIVER IN PERSON|MAIL|ial requests nag blithely alongside of th\n2978|42972|485|3|26|49789.22|0.07|0.05|N|O|1995-07-29|1995-07-22|1995-08-20|COLLECT COD|REG AIR|as haggle against the carefully express dep\n2978|27039|4546|4|7|6762.21|0.00|0.00|N|O|1995-07-18|1995-07-03|1995-07-23|NONE|FOB|. final ideas are blithe\n2978|28403|5910|5|33|43936.20|0.09|0.03|R|F|1995-05-06|1995-07-23|1995-05-16|COLLECT COD|FOB|s. blithely unusual pack\n2978|167063|9580|6|4|4520.24|0.08|0.04|N|O|1995-07-06|1995-07-31|1995-07-19|COLLECT COD|AIR|ffily unusual \n2979|8022|5523|1|8|7440.16|0.00|0.08|N|O|1996-06-18|1996-05-21|1996-07-06|COLLECT COD|REG AIR|st blithely; blithely regular gifts dazz\n2979|10662|663|2|47|73915.02|0.05|0.00|N|O|1996-03-25|1996-05-13|1996-04-04|TAKE BACK RETURN|SHIP|iously unusual dependencies wake across\n2979|187872|5427|3|35|68595.45|0.04|0.03|N|O|1996-05-25|1996-06-11|1996-06-24|DELIVER IN PERSON|MAIL|old ideas beneath the blit\n2979|164037|6554|4|28|30828.84|0.05|0.08|N|O|1996-06-04|1996-04-23|1996-06-24|DELIVER IN PERSON|FOB|ing, regular pinto beans. blithel\n2980|36367|8871|1|2|2606.72|0.09|0.03|N|O|1996-11-18|1996-10-22|1996-11-27|TAKE BACK RETURN|SHIP|enly across the special, pending packag\n2980|9576|7077|2|48|71307.36|0.04|0.05|N|O|1996-09-25|1996-12-09|1996-10-12|NONE|REG AIR|totes. regular pinto \n2980|132090|4604|3|27|30296.43|0.08|0.08|N|O|1996-12-08|1996-12-03|1996-12-14|NONE|REG AIR| theodolites cajole blithely sl\n2980|24644|7147|4|49|76863.36|0.03|0.02|N|O|1996-10-04|1996-12-04|1996-10-06|NONE|RAIL|hy packages sleep quic\n2980|186048|6049|5|24|27216.96|0.05|0.04|N|O|1997-01-12|1996-10-27|1997-01-14|NONE|MAIL|elets. fluffily regular in\n2980|108666|3687|6|43|72010.38|0.01|0.01|N|O|1996-12-07|1996-11-10|1997-01-02|COLLECT COD|AIR|sts. slyly regu\n2981|13505|1009|1|17|24114.50|0.03|0.05|N|O|1998-10-17|1998-10-02|1998-10-21|DELIVER IN PERSON|RAIL|, unusual packages x-ray. furious\n2981|175430|2982|2|8|12043.44|0.06|0.03|N|O|1998-08-21|1998-09-28|1998-09-05|DELIVER IN PERSON|MAIL|ng to the f\n2981|36615|4125|3|14|21722.54|0.03|0.07|N|O|1998-08-30|1998-10-04|1998-09-04|DELIVER IN PERSON|MAIL|kages detect furiously express requests.\n2982|111936|4448|1|21|40906.53|0.00|0.01|A|F|1995-04-03|1995-06-08|1995-04-18|DELIVER IN PERSON|AIR|ironic deposits. furiously ex\n2982|98071|3090|2|13|13897.91|0.02|0.08|R|F|1995-03-31|1995-05-07|1995-04-18|TAKE BACK RETURN|RAIL|regular deposits unwind alongside \n2982|69479|6998|3|21|30417.87|0.01|0.01|R|F|1995-04-19|1995-06-03|1995-04-28|COLLECT COD|SHIP|egular ideas use furiously? bl\n2983|162174|2175|1|44|54391.48|0.03|0.06|R|F|1992-02-09|1992-03-07|1992-03-09|TAKE BACK RETURN|AIR|ly regular instruct\n2983|48039|5552|2|11|10857.33|0.09|0.06|A|F|1992-04-29|1992-02-27|1992-05-26|NONE|MAIL|aids integrate s\n3008|131565|6592|1|8|12772.48|0.10|0.04|N|O|1995-12-06|1996-01-12|1995-12-22|TAKE BACK RETURN|FOB|yly ironic foxes. regular requests h\n3008|199716|4755|2|31|56287.01|0.05|0.06|N|O|1995-12-14|1995-12-11|1995-12-31|TAKE BACK RETURN|AIR| bold packages. quic\n3008|23054|3055|3|40|39082.00|0.01|0.03|N|O|1995-12-18|1996-01-06|1996-01-11|COLLECT COD|AIR|esias. theodolites detect blithely \n3008|59018|9019|4|48|46896.48|0.07|0.06|N|O|1996-01-23|1996-01-07|1996-02-09|COLLECT COD|SHIP|ld theodolites. fluffily bold theodolit\n3008|104156|9177|5|31|35964.65|0.03|0.02|N|O|1995-12-01|1996-01-20|1995-12-28|COLLECT COD|RAIL|nts use thinly around the carefully iro\n3009|44862|9871|1|48|86729.28|0.10|0.02|N|O|1997-03-19|1997-05-13|1997-04-11|TAKE BACK RETURN|TRUCK| dependencies sleep quickly a\n3009|184598|7117|2|38|63938.42|0.00|0.01|N|O|1997-05-01|1997-04-10|1997-05-17|TAKE BACK RETURN|AIR|nal packages should haggle slyly. quickl\n3009|129770|7307|3|26|46794.02|0.08|0.02|N|O|1997-05-15|1997-05-10|1997-06-13|TAKE BACK RETURN|SHIP|uriously specia\n3010|137941|455|1|23|45515.62|0.04|0.00|N|O|1996-03-08|1996-02-29|1996-03-27|NONE|TRUCK|ounts. pendin\n3010|173015|5533|2|22|23936.22|0.09|0.06|N|O|1996-03-06|1996-04-06|1996-03-18|COLLECT COD|REG AIR| final deposit\n3010|57050|9556|3|24|24169.20|0.04|0.07|N|O|1996-05-09|1996-03-14|1996-05-15|DELIVER IN PERSON|RAIL|ar, even reques\n3010|23675|1182|4|28|44762.76|0.09|0.06|N|O|1996-03-05|1996-03-28|1996-04-03|DELIVER IN PERSON|FOB|ake carefully carefully even request\n3010|103499|3500|5|9|13522.41|0.02|0.02|N|O|1996-04-28|1996-03-17|1996-05-18|NONE|SHIP|inal packages. quickly even pinto\n3010|91955|1956|6|38|73984.10|0.05|0.07|N|O|1996-04-15|1996-03-16|1996-04-21|DELIVER IN PERSON|RAIL|accounts ar\n3011|197592|112|1|5|8447.95|0.02|0.04|R|F|1992-04-21|1992-02-23|1992-05-15|NONE|TRUCK|nusual sentiments. carefully bold idea\n3011|122709|2710|2|42|72731.40|0.05|0.00|A|F|1992-02-01|1992-03-18|1992-02-29|NONE|TRUCK|osits haggle quickly pending, \n3012|194274|6794|1|49|67045.23|0.00|0.00|A|F|1993-08-07|1993-07-01|1993-08-08|NONE|MAIL| quickly furious packages. silently unusua\n3012|160226|227|2|37|47590.14|0.06|0.03|A|F|1993-08-16|1993-06-07|1993-08-24|TAKE BACK RETURN|REG AIR|uickly permanent packages sleep caref\n3013|93613|6123|1|31|49804.91|0.08|0.08|N|O|1997-05-03|1997-04-05|1997-05-25|NONE|AIR|y furious depen\n3013|138866|6406|2|30|57145.80|0.05|0.06|N|O|1997-05-02|1997-03-09|1997-05-12|TAKE BACK RETURN|MAIL|ronic packages. slyly even\n3013|119628|7162|3|35|57666.70|0.00|0.03|N|O|1997-04-02|1997-05-04|1997-04-16|COLLECT COD|MAIL|ely accord\n3013|180929|3448|4|17|34168.64|0.01|0.07|N|O|1997-02-26|1997-05-02|1997-03-27|DELIVER IN PERSON|SHIP|fully unusual account\n3013|59896|4907|5|20|37117.80|0.00|0.04|N|O|1997-05-06|1997-03-18|1997-05-12|COLLECT COD|RAIL|unts boost regular ideas. slyly pe\n3013|71557|4065|6|19|29042.45|0.08|0.07|N|O|1997-05-11|1997-04-18|1997-05-15|COLLECT COD|REG AIR|fluffily pending packages nag furiously al\n3014|162368|2369|1|36|51492.96|0.05|0.03|A|F|1992-11-16|1993-01-20|1992-11-28|TAKE BACK RETURN|FOB|ding accounts boost fu\n3014|105025|46|2|36|37080.72|0.00|0.08|R|F|1992-12-28|1992-12-29|1993-01-24|COLLECT COD|MAIL|iously ironic r\n3014|150885|3401|3|48|92922.24|0.06|0.02|A|F|1992-12-19|1993-01-08|1992-12-25|DELIVER IN PERSON|REG AIR|y pending theodolites wake. reg\n3014|113767|8790|4|14|24930.64|0.10|0.02|R|F|1992-11-19|1993-01-01|1992-12-17|DELIVER IN PERSON|SHIP|. slyly brave platelets nag. careful,\n3014|74210|6718|5|28|33157.88|0.02|0.08|R|F|1993-01-09|1992-12-18|1993-01-10|TAKE BACK RETURN|FOB|es are. final braids nag slyly. fluff\n3014|37443|9947|6|30|41413.20|0.04|0.01|R|F|1993-02-28|1993-01-02|1993-03-20|TAKE BACK RETURN|AIR| final foxes.\n3015|2643|7644|1|5|7728.20|0.09|0.00|A|F|1993-01-10|1992-12-02|1993-01-19|TAKE BACK RETURN|RAIL| the furiously pendi\n3015|17510|12|2|17|24267.67|0.03|0.01|R|F|1992-10-16|1992-11-20|1992-10-28|COLLECT COD|AIR|s above the fluffily final t\n3015|90543|5562|3|23|35271.42|0.03|0.05|A|F|1992-12-03|1992-11-19|1992-12-23|DELIVER IN PERSON|FOB|s are slyly carefully special pinto bea\n3015|155005|5006|4|7|7420.00|0.10|0.03|A|F|1992-12-07|1992-12-17|1992-12-30|DELIVER IN PERSON|REG AIR| after the evenly special packages ca\n3015|164614|7131|5|42|70501.62|0.04|0.02|R|F|1993-01-21|1992-11-07|1993-02-11|DELIVER IN PERSON|AIR|encies haggle furious\n3015|65123|5124|6|18|19586.16|0.02|0.03|R|F|1992-10-10|1992-11-19|1992-10-18|TAKE BACK RETURN|MAIL|equests wake fluffil\n3040|15347|2851|1|18|22722.12|0.08|0.04|R|F|1993-06-25|1993-07-06|1993-07-19|TAKE BACK RETURN|SHIP|ly thin accou\n3040|132715|255|2|9|15729.39|0.00|0.01|A|F|1993-06-12|1993-05-16|1993-06-14|NONE|RAIL|ges. pending packages wake. requests\n3040|125608|633|3|30|49008.00|0.01|0.01|A|F|1993-08-06|1993-05-18|1993-08-19|NONE|MAIL|x furiously bold packages. expres\n3040|82065|7082|4|14|14658.84|0.05|0.04|A|F|1993-05-13|1993-05-18|1993-05-19|TAKE BACK RETURN|REG AIR| haggle carefully. express hocke\n3040|51516|1517|5|43|63102.93|0.04|0.04|R|F|1993-05-21|1993-05-25|1993-05-26|NONE|MAIL|sts nag slyly alongside of the depos\n3040|17878|2881|6|10|17958.70|0.08|0.04|R|F|1993-05-16|1993-06-24|1993-06-11|DELIVER IN PERSON|MAIL|ely regular foxes haggle dari\n3041|180636|8191|1|5|8583.15|0.07|0.04|N|O|1997-07-20|1997-07-15|1997-08-17|COLLECT COD|FOB|posits dazzle special p\n3041|145837|866|2|9|16945.47|0.03|0.03|N|O|1997-06-29|1997-08-14|1997-07-19|COLLECT COD|AIR|iously across the silent pinto beans. furi\n3041|67448|2461|3|9|12738.96|0.09|0.06|N|O|1997-08-28|1997-07-23|1997-09-16|TAKE BACK RETURN|FOB|scapades after the special\n3042|104915|2446|1|30|57597.30|0.08|0.06|A|F|1995-01-12|1995-02-15|1995-01-24|DELIVER IN PERSON|SHIP|the requests detect fu\n3042|101459|1460|2|28|40892.60|0.05|0.03|A|F|1994-11-24|1995-01-02|1994-12-06|TAKE BACK RETURN|MAIL|ng the furiously r\n3042|13664|6166|3|34|53640.44|0.04|0.00|R|F|1994-12-11|1995-02-03|1994-12-21|TAKE BACK RETURN|TRUCK|can wake after the enticingly stealthy i\n3042|47338|2347|4|19|24421.27|0.02|0.01|A|F|1995-03-05|1995-01-24|1995-03-17|COLLECT COD|TRUCK|e carefully. regul\n3043|45387|396|1|23|30644.74|0.07|0.04|R|F|1992-05-08|1992-07-22|1992-05-18|COLLECT COD|TRUCK|uickly above the pending,\n3043|5894|3395|2|15|26998.35|0.03|0.05|A|F|1992-05-27|1992-06-03|1992-06-09|COLLECT COD|FOB|usly furiously\n3043|59231|9232|3|42|49989.66|0.10|0.07|R|F|1992-07-15|1992-06-19|1992-07-23|NONE|MAIL|ide of the un\n3043|90453|454|4|5|7217.25|0.10|0.01|A|F|1992-05-22|1992-07-02|1992-06-20|TAKE BACK RETURN|TRUCK|ake blithely re\n3044|100992|993|1|10|19929.90|0.07|0.08|N|O|1996-07-13|1996-05-06|1996-07-21|TAKE BACK RETURN|REG AIR| slyly ironic requests. s\n3044|167015|9532|2|3|3246.03|0.06|0.02|N|O|1996-07-27|1996-05-26|1996-08-15|TAKE BACK RETURN|AIR|ecoys haggle furiously pending requests.\n3044|18478|980|3|47|65634.09|0.09|0.00|N|O|1996-05-24|1996-06-22|1996-05-30|NONE|REG AIR|ly around the car\n3045|87670|7671|1|41|67964.47|0.05|0.01|N|O|1995-09-30|1995-11-24|1995-10-03|TAKE BACK RETURN|MAIL|ely final foxes. carefully ironic pinto b\n3045|68109|3122|2|48|51700.80|0.02|0.03|N|O|1995-10-01|1995-12-16|1995-10-10|TAKE BACK RETURN|MAIL|ole quickly outside th\n3046|73235|3236|1|44|53162.12|0.03|0.03|N|O|1996-03-03|1996-02-25|1996-04-01|NONE|AIR| are quickly. blithe\n3046|53645|3646|2|46|73537.44|0.03|0.08|N|O|1996-03-22|1996-02-28|1996-04-07|TAKE BACK RETURN|AIR|sits sleep furious\n3046|1537|9038|3|31|44594.43|0.03|0.07|N|O|1996-03-24|1996-01-30|1996-03-26|NONE|RAIL|y pending somas alongside of the slyly iro\n3047|103994|3995|1|17|33965.83|0.08|0.02|N|O|1997-06-14|1997-04-20|1997-06-23|COLLECT COD|FOB|onic instruction\n3047|13957|8960|2|23|43031.85|0.00|0.04|N|O|1997-05-20|1997-06-14|1997-05-28|TAKE BACK RETURN|REG AIR| slyly ironi\n3072|56531|4047|1|6|8925.18|0.09|0.05|R|F|1994-02-09|1994-03-24|1994-02-28|DELIVER IN PERSON|REG AIR|gular requests abov\n3072|107152|2173|2|36|41729.40|0.07|0.02|R|F|1994-04-14|1994-04-22|1994-05-06|COLLECT COD|AIR| theodolites. blithely e\n3072|96715|6716|3|7|11981.97|0.04|0.07|R|F|1994-05-09|1994-03-31|1994-05-19|COLLECT COD|TRUCK|uests. ironic, ironic depos\n3072|82745|5254|4|39|67381.86|0.05|0.08|A|F|1994-05-27|1994-04-20|1994-06-14|COLLECT COD|MAIL|es; slyly spe\n3072|87380|7381|5|1|1367.38|0.01|0.08|R|F|1994-02-26|1994-03-14|1994-03-19|NONE|AIR| slyly ironic attainments. car\n3073|193992|9031|1|16|33375.84|0.07|0.01|R|F|1994-03-02|1994-03-23|1994-03-31|DELIVER IN PERSON|AIR|n requests. ironi\n3073|21735|9242|2|47|77866.31|0.09|0.00|R|F|1994-03-26|1994-02-12|1994-04-21|NONE|REG AIR|eposits. fluffily\n3073|86050|8559|3|10|10360.50|0.03|0.00|R|F|1994-02-11|1994-03-24|1994-02-26|COLLECT COD|FOB| furiously caref\n3073|28574|1077|4|14|21035.98|0.09|0.07|R|F|1994-03-24|1994-04-01|1994-04-07|NONE|RAIL|ilently quiet epitaphs.\n3073|40433|7946|5|25|34335.75|0.00|0.07|R|F|1994-04-14|1994-03-07|1994-04-22|NONE|TRUCK|nag asymptotes. pinto beans sleep \n3073|146763|6764|6|39|70580.64|0.09|0.02|R|F|1994-05-01|1994-02-16|1994-05-12|DELIVER IN PERSON|AIR|lar excuses across the furiously even \n3073|43770|3771|7|11|18851.47|0.08|0.07|A|F|1994-05-01|1994-03-06|1994-05-08|COLLECT COD|SHIP|instructions sleep according to the \n3074|36636|1643|1|50|78631.50|0.08|0.08|A|F|1993-01-31|1992-12-15|1993-02-20|NONE|AIR|furiously pending requests haggle s\n3074|138309|5849|2|39|52544.70|0.03|0.00|R|F|1992-12-08|1993-01-28|1992-12-09|DELIVER IN PERSON|TRUCK|iously throu\n3075|8905|6406|1|39|70742.10|0.02|0.03|A|F|1994-06-10|1994-06-21|1994-06-20|NONE|FOB|ing deposits nag \n3075|51164|3670|2|2|2230.32|0.07|0.08|R|F|1994-06-14|1994-06-10|1994-06-25|TAKE BACK RETURN|AIR|. unusual, unusual accounts haggle furious\n3076|84436|4437|1|44|62498.92|0.00|0.05|A|F|1993-09-14|1993-10-04|1993-09-17|TAKE BACK RETURN|FOB| instructions h\n3076|105266|287|2|22|27967.72|0.08|0.00|A|F|1993-09-05|1993-09-10|1993-09-27|NONE|REG AIR|packages wake furiou\n3076|4172|6673|3|31|33361.27|0.06|0.06|A|F|1993-08-10|1993-09-17|1993-08-17|TAKE BACK RETURN|SHIP|regular depos\n3077|71785|4293|1|25|43919.50|0.06|0.01|N|O|1997-09-14|1997-10-16|1997-10-06|NONE|TRUCK|lent account\n3077|90110|2620|2|40|44004.40|0.05|0.06|N|O|1997-10-22|1997-09-19|1997-11-19|DELIVER IN PERSON|AIR|to the enticing packag\n3077|77820|2835|3|13|23371.66|0.03|0.07|N|O|1997-09-09|1997-10-15|1997-09-19|NONE|TRUCK|luffily close depende\n3077|114603|2137|4|23|37204.80|0.03|0.02|N|O|1997-11-05|1997-09-16|1997-11-20|NONE|MAIL|lly. fluffily pending dinos across\n3078|131450|1451|1|25|37036.25|0.01|0.03|A|F|1993-04-22|1993-05-01|1993-04-28|TAKE BACK RETURN|AIR|express dinos. carefully ironic\n3078|77158|9666|2|21|23838.15|0.09|0.07|A|F|1993-03-20|1993-03-21|1993-04-01|COLLECT COD|AIR|e fluffily. \n3079|69026|6545|1|20|19900.40|0.05|0.00|N|O|1997-10-18|1997-10-26|1997-11-14|NONE|RAIL|ets are according to the quickly dari\n3079|116489|9001|2|38|57208.24|0.08|0.07|N|O|1997-11-07|1997-11-25|1997-12-06|NONE|RAIL|e carefully regular realms\n3079|16662|6663|3|40|63146.40|0.02|0.08|N|O|1997-09-26|1997-12-11|1997-10-09|NONE|RAIL|ide of the pending, special deposi\n3079|23216|3217|4|2|2278.42|0.00|0.08|N|O|1998-01-05|1997-11-17|1998-01-28|NONE|FOB|ly busy requests believ\n3079|187516|35|5|2|3207.02|0.10|0.00|N|O|1997-12-27|1997-10-25|1998-01-08|COLLECT COD|SHIP|y regular asymptotes doz\n3079|165391|2940|6|46|66993.94|0.00|0.00|N|O|1997-11-19|1997-11-04|1997-11-25|DELIVER IN PERSON|REG AIR|es. final, regula\n3104|50589|5600|1|20|30791.60|0.01|0.08|A|F|1993-12-31|1993-11-24|1994-01-12|DELIVER IN PERSON|REG AIR|s are. furiously s\n3104|47520|2529|2|47|68973.44|0.02|0.05|A|F|1993-12-25|1993-11-02|1994-01-12|COLLECT COD|RAIL|ily daring acc\n3104|62977|2978|3|11|21339.67|0.02|0.03|A|F|1993-10-05|1993-11-30|1993-10-27|NONE|TRUCK| special deposits u\n3104|37868|7869|4|26|46952.36|0.02|0.08|R|F|1994-01-02|1993-12-05|1994-01-31|TAKE BACK RETURN|TRUCK|es boost carefully. slyly \n3105|183486|8523|1|11|17264.28|0.01|0.06|N|O|1997-02-07|1997-02-09|1997-03-01|NONE|FOB|kly bold depths caj\n3105|44436|4437|2|9|12423.87|0.08|0.08|N|O|1996-12-25|1997-02-04|1997-01-09|COLLECT COD|SHIP|es wake among t\n3105|24472|9477|3|48|67030.56|0.02|0.05|N|O|1997-02-28|1997-01-31|1997-03-18|DELIVER IN PERSON|REG AIR|ending platelets wake carefully ironic inst\n3105|90456|7984|4|23|33268.35|0.04|0.07|N|O|1997-03-08|1996-12-14|1997-03-18|COLLECT COD|REG AIR| detect slyly. blithely unusual requests ar\n3105|89347|4364|5|8|10690.72|0.07|0.07|N|O|1996-12-28|1996-12-28|1997-01-25|NONE|FOB|s. blithely unusual ideas was after\n3105|46261|3774|6|30|36217.80|0.08|0.05|N|O|1997-03-03|1997-02-03|1997-03-05|NONE|FOB|ess accounts boost among t\n3106|85699|8208|1|22|37063.18|0.03|0.02|N|O|1997-02-28|1997-02-12|1997-03-03|DELIVER IN PERSON|FOB|structions atop the blithely\n3106|135816|3356|2|49|90738.69|0.06|0.06|N|O|1997-02-27|1997-03-11|1997-03-12|NONE|TRUCK|lets. quietly regular courts \n3106|51764|6775|3|42|72061.92|0.09|0.07|N|O|1997-04-05|1997-03-17|1997-04-22|COLLECT COD|REG AIR|nstructions wake. furiously \n3106|195649|3207|4|6|10467.84|0.10|0.07|N|O|1997-02-02|1997-04-11|1997-02-27|COLLECT COD|REG AIR|symptotes. slyly bold platelets cajol\n3106|64580|9593|5|16|24713.28|0.09|0.08|N|O|1997-02-25|1997-04-10|1997-03-16|NONE|AIR|sits wake slyl\n3107|148150|665|1|16|19170.40|0.05|0.04|N|O|1997-08-30|1997-10-20|1997-09-20|TAKE BACK RETURN|REG AIR|regular pinto beans. ironic ideas haggle\n3107|141690|1691|2|35|60609.15|0.05|0.06|N|O|1997-08-27|1997-11-19|1997-09-14|COLLECT COD|TRUCK|ets doubt furiously final ideas. final\n3107|169733|2250|3|23|41462.79|0.03|0.06|N|O|1997-12-10|1997-11-11|1997-12-14|TAKE BACK RETURN|SHIP|atelets must ha\n3107|86080|8589|4|27|28784.16|0.00|0.08|N|O|1997-11-15|1997-10-31|1997-11-28|DELIVER IN PERSON|FOB|furiously final \n3108|108989|1500|1|37|73925.26|0.06|0.04|A|F|1993-10-16|1993-10-01|1993-11-09|DELIVER IN PERSON|RAIL| final requests. \n3108|165850|3399|2|26|49812.10|0.08|0.05|A|F|1993-11-12|1993-10-05|1993-12-09|COLLECT COD|TRUCK| slyly slow foxes wake furious\n3109|17514|16|1|32|45808.32|0.08|0.03|A|F|1993-09-05|1993-10-06|1993-09-18|DELIVER IN PERSON|FOB|ecial orbits are furiou\n3109|144736|2279|2|49|87255.77|0.08|0.06|R|F|1993-10-24|1993-09-30|1993-11-21|TAKE BACK RETURN|AIR| even pearls. furiously pending \n3109|175265|2817|3|43|57631.18|0.04|0.07|R|F|1993-09-29|1993-09-06|1993-10-13|COLLECT COD|MAIL|ding to the foxes. \n3109|78107|8108|4|26|28212.60|0.01|0.05|R|F|1993-11-16|1993-10-18|1993-12-06|TAKE BACK RETURN|TRUCK| sleep slyly according to t\n3109|142097|9640|5|50|56954.50|0.01|0.08|A|F|1993-09-17|1993-10-16|1993-10-11|NONE|FOB| regular packages boost blithely even, re\n3109|14933|7435|6|10|18479.30|0.10|0.04|A|F|1993-10-26|1993-10-03|1993-11-09|NONE|TRUCK|sits haggle carefully. regular, unusual ac\n3110|88455|3472|1|1|1443.45|0.02|0.07|A|F|1995-01-15|1995-01-20|1995-01-30|DELIVER IN PERSON|REG AIR|c theodolites a\n3110|56453|1464|2|31|43692.95|0.01|0.06|R|F|1995-03-31|1995-03-07|1995-04-21|TAKE BACK RETURN|REG AIR|en deposits. ironic\n3110|2097|9598|3|34|33969.06|0.02|0.02|A|F|1995-02-23|1995-01-27|1995-03-09|TAKE BACK RETURN|FOB|ly pending requests ha\n3110|39004|9005|4|16|15088.00|0.04|0.04|A|F|1995-01-10|1995-02-06|1995-01-26|NONE|MAIL|across the regular acco\n3110|139664|7204|5|39|66442.74|0.09|0.01|A|F|1995-02-09|1995-01-21|1995-02-21|NONE|MAIL|side of the blithely unusual courts. slyly \n3111|136959|1986|1|22|43910.90|0.06|0.05|N|O|1995-09-21|1995-11-09|1995-10-17|COLLECT COD|REG AIR|quests. regular dolphins against the \n3111|57194|4710|2|30|34535.70|0.06|0.05|N|O|1995-10-05|1995-11-15|1995-11-01|TAKE BACK RETURN|TRUCK|eas are furiously slyly special deposits.\n3111|51332|1333|3|10|12833.30|0.02|0.02|N|O|1995-11-10|1995-11-02|1995-12-04|NONE|FOB|ng the slyly ironic inst\n3111|131080|6107|4|31|34443.48|0.00|0.08|N|O|1995-10-26|1995-09-26|1995-11-02|TAKE BACK RETURN|MAIL|kages detect express attainments\n3111|53483|999|5|14|20110.72|0.05|0.04|N|O|1995-10-17|1995-10-19|1995-10-19|TAKE BACK RETURN|SHIP|re. pinto \n3111|85509|8018|6|5|7472.50|0.03|0.08|N|O|1995-08-30|1995-10-16|1995-09-04|DELIVER IN PERSON|TRUCK|. carefully even ideas\n3111|147305|7306|7|41|55444.30|0.09|0.05|N|O|1995-11-22|1995-11-01|1995-12-01|TAKE BACK RETURN|FOB|fily slow ideas. \n3136|141341|6370|1|30|41470.20|0.02|0.08|R|F|1994-08-13|1994-10-02|1994-09-02|TAKE BACK RETURN|RAIL|leep blithel\n3136|102634|2635|2|7|11456.41|0.05|0.07|A|F|1994-10-08|1994-09-14|1994-10-11|TAKE BACK RETURN|SHIP|ic pinto beans are slyly. f\n3136|157204|2235|3|43|54231.60|0.00|0.07|A|F|1994-09-05|1994-09-25|1994-09-11|NONE|RAIL|. special theodolites ha\n3136|115183|2717|4|26|31152.68|0.04|0.05|A|F|1994-10-13|1994-11-07|1994-11-05|TAKE BACK RETURN|AIR|eep fluffily. daringly silent attainments d\n3136|66751|6752|5|2|3435.50|0.08|0.07|R|F|1994-11-21|1994-11-03|1994-11-26|DELIVER IN PERSON|TRUCK|? special, silent \n3136|79827|9828|6|29|52397.78|0.08|0.07|A|F|1994-11-16|1994-10-03|1994-12-14|NONE|FOB|latelets. final \n3137|2357|2358|1|6|7556.10|0.02|0.02|N|O|1995-09-19|1995-10-23|1995-10-16|NONE|SHIP|ly express as\n3137|5178|2679|2|4|4332.68|0.06|0.04|N|O|1995-10-01|1995-09-11|1995-10-30|COLLECT COD|RAIL|posits wake. silent excuses boost about\n3138|92417|4927|1|7|9865.87|0.05|0.05|R|F|1994-03-04|1994-03-14|1994-03-20|NONE|AIR|lithely quickly even packages. packages\n3138|43554|3555|2|27|40433.85|0.09|0.01|R|F|1994-03-24|1994-03-23|1994-04-18|DELIVER IN PERSON|FOB|counts cajole fluffily carefully special i\n3138|196512|6513|3|32|51472.32|0.00|0.01|R|F|1994-02-24|1994-05-07|1994-02-28|TAKE BACK RETURN|MAIL|inal foxes affix slyly. fluffily regul\n3138|171358|1359|4|38|54315.30|0.07|0.04|R|F|1994-02-21|1994-03-21|1994-03-13|COLLECT COD|FOB|lithely fluffily un\n3138|9594|9595|5|12|18043.08|0.09|0.02|A|F|1994-03-04|1994-04-11|1994-03-21|COLLECT COD|FOB|. bold pinto beans haggl\n3138|43107|8116|6|25|26252.50|0.05|0.08|A|F|1994-05-19|1994-04-07|1994-06-17|TAKE BACK RETURN|AIR|dolites around the carefully busy the\n3139|39310|1814|1|46|57468.26|0.08|0.03|R|F|1992-04-28|1992-03-04|1992-05-19|TAKE BACK RETURN|FOB|of the unusual, unusual re\n3140|6539|4040|1|21|30356.13|0.08|0.02|R|F|1992-04-12|1992-05-31|1992-04-21|NONE|REG AIR| furiously sly excuses according to the\n3140|88674|1183|2|10|16626.70|0.07|0.01|A|F|1992-05-30|1992-05-09|1992-06-09|COLLECT COD|RAIL|accounts. expres\n3140|132668|2669|3|28|47618.48|0.06|0.00|R|F|1992-06-08|1992-07-07|1992-07-08|TAKE BACK RETURN|SHIP|lar ideas. slyly ironic d\n3141|176416|1451|1|32|47757.12|0.06|0.00|N|O|1995-11-21|1995-12-18|1995-11-26|DELIVER IN PERSON|FOB|oxes are quickly about t\n3141|9358|6859|2|37|46891.95|0.10|0.05|N|O|1996-01-24|1995-12-16|1996-01-27|DELIVER IN PERSON|AIR|press pinto beans. bold accounts boost b\n3141|78185|5707|3|9|10468.62|0.09|0.02|N|O|1995-11-11|1995-12-10|1995-12-02|DELIVER IN PERSON|MAIL|uickly ironic, pendi\n3141|45956|965|4|47|89391.65|0.03|0.01|N|O|1995-11-29|1996-01-13|1995-12-10|TAKE BACK RETURN|TRUCK| are slyly pi\n3142|119225|4248|1|15|18663.30|0.03|0.08|R|F|1992-08-15|1992-08-18|1992-08-22|DELIVER IN PERSON|AIR|instructions are. ironic packages doz\n3143|89831|7356|1|22|40058.26|0.02|0.00|A|F|1993-05-11|1993-03-26|1993-05-20|TAKE BACK RETURN|MAIL|l, special instructions nag \n3143|182257|9812|2|40|53570.00|0.03|0.08|A|F|1993-05-07|1993-03-29|1993-05-17|COLLECT COD|FOB|sly unusual theodolites. slyly ev\n3143|182464|4983|3|22|34022.12|0.05|0.03|A|F|1993-03-18|1993-05-09|1993-04-14|DELIVER IN PERSON|MAIL|beans. fluf\n3143|65651|5652|4|46|74365.90|0.05|0.08|R|F|1993-04-19|1993-03-21|1993-05-05|COLLECT COD|REG AIR|low forges haggle. even packages use bli\n3168|59901|2407|1|46|85601.40|0.08|0.08|R|F|1992-02-14|1992-03-02|1992-03-02|TAKE BACK RETURN|SHIP|y across the express accounts. fluff\n3168|153658|3659|2|1|1711.65|0.06|0.08|A|F|1992-05-27|1992-03-12|1992-06-09|TAKE BACK RETURN|SHIP|pinto beans. slyly regular courts haggle \n3168|127053|9566|3|13|14040.65|0.09|0.02|A|F|1992-03-05|1992-04-29|1992-03-15|NONE|SHIP|ironic somas haggle quick\n3168|164440|1989|4|11|16548.84|0.02|0.05|R|F|1992-04-12|1992-03-17|1992-05-12|COLLECT COD|SHIP|ously furious dependenc\n3169|191052|3572|1|12|13716.60|0.01|0.04|R|F|1994-01-05|1994-03-18|1994-01-21|COLLECT COD|REG AIR| regular d\n3169|199869|4908|2|17|33470.62|0.05|0.04|R|F|1994-03-02|1994-01-21|1994-03-03|DELIVER IN PERSON|TRUCK|usly regular packages. ironi\n3169|187322|7323|3|12|16911.84|0.08|0.07|A|F|1994-04-18|1994-03-12|1994-05-08|TAKE BACK RETURN|FOB|atelets. pac\n3169|104999|5000|4|26|52103.74|0.10|0.04|R|F|1994-04-08|1994-03-21|1994-04-29|NONE|TRUCK|ter the regular ideas. slyly iro\n3169|107704|7705|5|6|10270.20|0.09|0.01|A|F|1994-03-24|1994-02-22|1994-04-04|TAKE BACK RETURN|AIR|ular instructions. ca\n3169|176053|6054|6|46|51936.30|0.02|0.07|A|F|1994-02-01|1994-01-22|1994-02-24|DELIVER IN PERSON|RAIL|thely bold theodolites are fl\n3170|39883|7393|1|12|21874.56|0.03|0.03|N|O|1998-02-12|1998-01-17|1998-02-24|NONE|TRUCK|ing accounts along the speci\n3170|99060|1570|2|21|22240.26|0.01|0.00|N|O|1997-12-09|1998-01-31|1997-12-21|DELIVER IN PERSON|MAIL|o beans. carefully final requests dou\n3170|88383|3400|3|27|37027.26|0.00|0.05|N|O|1998-02-25|1998-01-29|1998-02-27|COLLECT COD|AIR|efully bold foxes. regular, ev\n3170|40846|847|4|34|60752.56|0.05|0.04|N|O|1998-02-01|1998-01-11|1998-02-20|TAKE BACK RETURN|TRUCK|s about the fluffily final de\n3170|89767|9768|5|32|56216.32|0.02|0.04|N|O|1997-11-24|1997-12-12|1997-12-15|COLLECT COD|SHIP|ggle about the furiously r\n3170|109150|4171|6|43|49843.45|0.08|0.05|N|O|1998-01-05|1998-01-04|1998-01-14|NONE|REG AIR|. express dolphins use sly\n3170|83873|3874|7|26|48278.62|0.10|0.05|N|O|1998-02-12|1997-12-22|1998-02-28|COLLECT COD|TRUCK|s engage furiously. \n3171|46382|8887|1|34|45164.92|0.04|0.00|A|F|1993-05-30|1993-05-27|1993-06-06|DELIVER IN PERSON|REG AIR|r the final, even packages. quickly\n3171|138340|8341|2|50|68917.00|0.01|0.04|A|F|1993-07-19|1993-05-15|1993-07-31|TAKE BACK RETURN|REG AIR|riously final foxes about the ca\n3172|95767|786|1|4|7051.04|0.06|0.07|A|F|1992-09-26|1992-08-15|1992-10-20|DELIVER IN PERSON|TRUCK|s are slyly thin package\n3172|147816|5359|2|43|80143.83|0.05|0.07|R|F|1992-08-22|1992-07-07|1992-08-26|COLLECT COD|MAIL| final packages. \n3172|131989|7016|3|13|26272.74|0.03|0.01|R|F|1992-07-06|1992-08-06|1992-08-05|DELIVER IN PERSON|MAIL|inal deposits haggle along the\n3172|134555|9582|4|28|44507.40|0.08|0.04|R|F|1992-07-09|1992-07-14|1992-07-16|NONE|MAIL|regular ideas. packages are furi\n3172|63775|3776|5|31|53901.87|0.05|0.08|A|F|1992-09-01|1992-08-27|1992-09-23|NONE|SHIP|. slyly regular dependencies haggle quiet\n3173|194121|4122|1|35|42529.20|0.01|0.08|N|O|1996-09-09|1996-10-15|1996-10-04|TAKE BACK RETURN|RAIL| across the slyly even requests.\n3173|177060|2095|2|5|5685.30|0.09|0.07|N|O|1996-12-06|1996-09-17|1996-12-07|DELIVER IN PERSON|REG AIR|express depo\n3173|45832|841|3|16|28445.28|0.06|0.01|N|O|1996-08-12|1996-09-21|1996-08-22|NONE|SHIP|e special,\n3173|93949|3950|4|2|3885.88|0.00|0.00|N|O|1996-10-15|1996-11-06|1996-10-18|COLLECT COD|MAIL|ular pearls\n3173|184484|4485|5|2|3136.96|0.00|0.06|N|O|1996-08-18|1996-09-21|1996-09-07|DELIVER IN PERSON|MAIL|fluffily above t\n3174|185221|7740|1|6|7837.32|0.04|0.08|N|O|1996-03-13|1996-02-09|1996-03-22|DELIVER IN PERSON|AIR| furiously ironic\n3174|193509|8548|2|4|6410.00|0.01|0.05|N|O|1995-11-17|1996-01-08|1995-11-27|DELIVER IN PERSON|RAIL|deas sleep thi\n3174|91355|3865|3|21|28273.35|0.08|0.05|N|O|1996-02-20|1995-12-28|1996-03-17|NONE|MAIL|iously. idly bold theodolites a\n3174|191449|9007|4|13|20025.72|0.08|0.06|N|O|1996-01-11|1996-01-26|1996-02-01|DELIVER IN PERSON|SHIP|leep quickly? slyly special platelets\n3174|71002|3510|5|39|37947.00|0.02|0.06|N|O|1995-12-02|1996-02-08|1995-12-12|TAKE BACK RETURN|TRUCK| wake slyly foxes. bold requests p\n3174|119325|4348|6|8|10754.56|0.07|0.08|N|O|1995-12-07|1996-01-08|1995-12-29|DELIVER IN PERSON|TRUCK|nic deposits among t\n3175|119225|6759|1|28|34838.16|0.10|0.01|R|F|1994-09-27|1994-10-05|1994-10-04|NONE|FOB|ore the even, silent foxes. b\n3175|532|3033|2|38|54436.14|0.01|0.07|R|F|1994-10-10|1994-08-25|1994-10-28|NONE|MAIL|the quickly even dolph\n3175|128833|1346|3|12|22341.96|0.09|0.07|R|F|1994-10-16|1994-09-15|1994-10-18|NONE|AIR|ter the pending deposits. slyly e\n3175|84978|2503|4|14|27481.58|0.02|0.05|R|F|1994-10-21|1994-09-05|1994-11-15|NONE|MAIL|nt dependencies are quietly even \n3175|17028|4532|5|47|44415.94|0.08|0.03|R|F|1994-08-08|1994-09-10|1994-08-21|COLLECT COD|REG AIR| final requests x-r\n3175|174487|4488|6|44|68705.12|0.01|0.00|R|F|1994-09-26|1994-08-30|1994-10-24|TAKE BACK RETURN|MAIL|are carefully furiously ironic accounts. e\n3175|864|3365|7|32|56475.52|0.01|0.02|R|F|1994-09-29|1994-09-20|1994-10-10|TAKE BACK RETURN|SHIP|lites sleep\n3200|115903|3437|1|17|32621.30|0.10|0.00|N|O|1996-06-06|1996-04-21|1996-06-14|DELIVER IN PERSON|AIR|side of the furiously pendin\n3200|165487|3036|2|27|41916.96|0.03|0.00|N|O|1996-05-07|1996-05-01|1996-05-09|TAKE BACK RETURN|REG AIR|as haggle furiously against the fluff\n3200|130983|6010|3|36|72503.28|0.01|0.01|N|O|1996-03-22|1996-03-19|1996-03-30|DELIVER IN PERSON|FOB|f the carefu\n3200|29905|4910|4|11|20183.90|0.10|0.02|N|O|1996-03-18|1996-03-21|1996-04-14|COLLECT COD|RAIL|osits sleep fur\n3200|197667|7668|5|16|28234.56|0.05|0.00|N|O|1996-02-28|1996-03-13|1996-03-11|NONE|RAIL|ly against the quiet packages. blith\n3200|174336|1888|6|25|35258.25|0.10|0.01|N|O|1996-02-08|1996-04-11|1996-03-06|COLLECT COD|FOB| slyly regular hockey players! pinto beans \n3201|45786|5787|1|11|19049.58|0.10|0.06|A|F|1993-09-27|1993-08-29|1993-10-18|NONE|TRUCK|ing to the furiously expr\n3201|117792|2815|2|27|48864.33|0.08|0.02|R|F|1993-08-31|1993-08-24|1993-09-08|TAKE BACK RETURN|FOB|deposits are slyly along\n3201|118958|3981|3|50|98847.50|0.00|0.08|R|F|1993-10-27|1993-09-30|1993-11-16|COLLECT COD|TRUCK| deposits. express, ir\n3202|182676|7713|1|30|52760.10|0.09|0.02|A|F|1993-03-18|1993-03-10|1993-03-23|COLLECT COD|SHIP|ven platelets. furiously final\n3202|19748|2250|2|22|36690.28|0.01|0.02|R|F|1993-02-16|1993-02-16|1993-03-16|TAKE BACK RETURN|MAIL|the express packages. fu\n3203|143488|3489|1|23|35224.04|0.01|0.07|N|O|1998-01-04|1998-01-12|1998-01-24|COLLECT COD|SHIP|uses. fluffily ironic pinto bea\n3203|187707|2744|2|22|39483.40|0.03|0.03|N|O|1998-02-12|1998-01-01|1998-02-18|TAKE BACK RETURN|REG AIR|e the blithely regular accounts boost f\n3204|11924|9428|1|10|18359.20|0.10|0.07|R|F|1993-01-27|1993-03-08|1993-01-29|COLLECT COD|SHIP|counts. bold \n3204|6474|8975|2|39|53838.33|0.10|0.03|R|F|1993-02-11|1993-03-19|1993-02-28|TAKE BACK RETURN|MAIL|sits sleep theodolites. slyly bo\n3205|67629|2642|1|7|11176.34|0.09|0.00|R|F|1992-07-05|1992-06-17|1992-07-07|NONE|SHIP|ly alongsi\n3205|28792|8793|2|32|55065.28|0.08|0.03|A|F|1992-06-01|1992-07-10|1992-06-06|TAKE BACK RETURN|RAIL|lar accoun\n3205|102663|5174|3|38|63295.08|0.10|0.08|A|F|1992-07-31|1992-06-03|1992-08-20|DELIVER IN PERSON|AIR|usly quiet accounts. slyly pending pinto \n3205|55351|5352|4|10|13063.50|0.01|0.07|A|F|1992-06-18|1992-07-04|1992-07-16|COLLECT COD|RAIL| deposits cajole careful\n3205|69090|1597|5|18|19063.62|0.03|0.03|A|F|1992-07-04|1992-06-14|1992-08-03|TAKE BACK RETURN|RAIL|symptotes. slyly even deposits ar\n3205|194388|9427|6|19|28165.22|0.07|0.08|R|F|1992-05-28|1992-05-30|1992-06-05|COLLECT COD|AIR|yly pending packages snooz\n3205|68994|1501|7|36|70667.64|0.06|0.03|A|F|1992-05-31|1992-06-19|1992-06-03|TAKE BACK RETURN|SHIP|s. ironic platelets above the s\n3206|175184|2736|1|1|1259.18|0.07|0.05|N|O|1996-11-22|1996-10-16|1996-12-07|TAKE BACK RETURN|FOB|y unusual foxes cajole ab\n3206|110775|3287|2|37|66073.49|0.07|0.01|N|O|1996-09-06|1996-10-31|1996-09-25|COLLECT COD|SHIP| quick theodolites hagg\n3206|185591|5592|3|24|40238.16|0.00|0.08|N|O|1996-08-25|1996-10-01|1996-09-04|COLLECT COD|TRUCK|encies sleep deposits--\n3207|112470|4|1|2|2964.94|0.10|0.03|N|O|1998-06-15|1998-04-20|1998-06-21|COLLECT COD|MAIL|among the ironic, even packages \n3207|70504|8026|2|42|61929.00|0.00|0.00|N|O|1998-05-02|1998-05-10|1998-06-01|NONE|SHIP|to the quickly special accounts? ironically\n3207|151474|6505|3|17|25932.99|0.03|0.04|N|O|1998-03-27|1998-04-06|1998-03-28|COLLECT COD|RAIL|eep against the instructions. gifts hag\n3207|18890|3893|4|32|57884.48|0.00|0.03|N|O|1998-06-17|1998-04-26|1998-07-07|TAKE BACK RETURN|SHIP|y across the slyly express foxes. bl\n3207|82724|5233|5|8|13653.76|0.00|0.06|N|O|1998-06-13|1998-04-26|1998-07-11|COLLECT COD|SHIP|y. final pint\n3207|133661|8688|6|32|54229.12|0.03|0.05|N|O|1998-04-19|1998-05-01|1998-05-08|COLLECT COD|FOB|l deposits wake beyond the carefully\n3232|13217|3218|1|22|24864.62|0.10|0.01|A|F|1992-11-30|1992-12-09|1992-12-04|NONE|RAIL|thely. furio\n3232|134360|6874|2|34|47408.24|0.07|0.04|R|F|1993-01-09|1992-11-14|1993-02-03|NONE|SHIP|old packages integrate quickly \n3232|180226|7781|3|3|3918.66|0.04|0.06|R|F|1992-12-14|1992-12-11|1992-12-29|DELIVER IN PERSON|FOB|ily blithely ironic acco\n3233|50584|585|1|23|35295.34|0.04|0.05|A|F|1994-12-07|1995-01-11|1994-12-26|NONE|AIR|pending instructions use after the carefu\n3233|153443|989|2|6|8978.64|0.02|0.08|A|F|1994-12-06|1994-12-05|1994-12-07|TAKE BACK RETURN|REG AIR|requests are quickly above the slyly p\n3233|99840|7368|3|2|3679.68|0.04|0.06|R|F|1995-01-03|1995-01-02|1995-01-21|TAKE BACK RETURN|AIR| across the bold packages\n3233|8532|1033|4|25|36013.25|0.04|0.07|A|F|1994-11-24|1995-01-07|1994-12-11|NONE|RAIL|oss the pl\n3234|78973|8974|1|45|87838.65|0.01|0.04|N|O|1996-05-15|1996-05-09|1996-06-02|DELIVER IN PERSON|TRUCK| express packages are carefully. f\n3234|83220|745|2|23|27674.06|0.03|0.00|N|O|1996-05-29|1996-05-15|1996-06-17|DELIVER IN PERSON|AIR|d-- fluffily special packag\n3234|74096|9111|3|16|17121.44|0.06|0.05|N|O|1996-06-10|1996-05-30|1996-06-18|COLLECT COD|RAIL|ithely ironic accounts wake along t\n3234|121031|6056|4|50|52601.50|0.09|0.05|N|O|1996-06-11|1996-05-19|1996-06-18|NONE|MAIL|ly regular ideas according to the regula\n3234|164274|9307|5|14|18735.78|0.01|0.07|N|O|1996-04-06|1996-05-30|1996-04-13|NONE|REG AIR|lithely regular f\n3235|108187|698|1|9|10756.62|0.07|0.00|N|O|1995-11-17|1995-12-24|1995-11-30|COLLECT COD|AIR|l courts sleep quickly slyly \n3235|94886|4887|2|43|80877.84|0.10|0.07|N|O|1995-12-25|1996-01-23|1996-01-09|COLLECT COD|MAIL|ckly final instru\n3235|137620|7621|3|29|48070.98|0.06|0.06|N|O|1996-01-28|1995-12-26|1996-02-12|DELIVER IN PERSON|RAIL|e fluffy pinto bea\n3235|177363|7364|4|23|33128.28|0.00|0.01|N|O|1996-02-16|1996-01-05|1996-03-07|DELIVER IN PERSON|SHIP|ldly ironic pinto beans\n3236|116245|1268|1|10|12612.40|0.06|0.05|N|O|1996-11-15|1996-12-14|1996-11-29|TAKE BACK RETURN|AIR|arefully. fluffily reg\n3236|121623|4136|2|21|34537.02|0.01|0.07|N|O|1996-12-23|1996-12-12|1997-01-21|NONE|AIR| final pinto \n3236|117099|9611|3|7|7812.63|0.07|0.01|N|O|1996-12-27|1996-12-18|1997-01-24|DELIVER IN PERSON|SHIP|dolites. slyly unus\n3237|10711|3213|1|11|17838.81|0.02|0.07|A|F|1992-08-03|1992-07-31|1992-08-13|TAKE BACK RETURN|AIR|es. permanently express platelets besid\n3238|71612|1613|1|12|19003.32|0.06|0.01|R|F|1993-03-06|1993-05-08|1993-04-01|DELIVER IN PERSON|AIR|ackages affix furiously. furiously bol\n3238|172398|7433|2|26|38230.14|0.01|0.06|A|F|1993-02-25|1993-04-04|1993-03-20|TAKE BACK RETURN|REG AIR|g accounts sleep furiously ironic attai\n3238|80096|2605|3|1|1076.09|0.00|0.04|R|F|1993-05-17|1993-04-18|1993-05-27|NONE|SHIP|wake alongs\n3239|44485|9494|1|50|71474.00|0.05|0.01|N|O|1998-02-09|1998-04-02|1998-02-22|NONE|FOB|d blithely stea\n3239|44840|9849|2|43|76748.12|0.01|0.06|N|O|1998-01-15|1998-03-12|1998-01-29|COLLECT COD|REG AIR|y. bold pinto beans use \n3239|12947|5449|3|13|24179.22|0.01|0.05|N|O|1998-02-10|1998-02-19|1998-02-25|DELIVER IN PERSON|MAIL|r deposits solve fluf\n3239|194825|4826|4|26|49915.32|0.03|0.05|N|O|1998-01-21|1998-03-21|1998-02-08|DELIVER IN PERSON|SHIP|ngly pending platelets are fluff\n3239|11739|6742|5|31|51172.63|0.10|0.08|N|O|1998-04-14|1998-03-24|1998-04-17|DELIVER IN PERSON|FOB|foxes. pendin\n3264|199532|9533|1|39|63629.67|0.06|0.06|N|O|1996-11-07|1996-12-12|1996-11-20|TAKE BACK RETURN|REG AIR|sleep carefully after the slyly final\n3264|130251|252|2|34|43562.50|0.00|0.01|N|O|1997-01-03|1997-01-06|1997-01-29|TAKE BACK RETURN|REG AIR|rns haggle carefully. blit\n3264|124205|1742|3|11|13521.20|0.09|0.03|N|O|1996-12-11|1996-12-19|1996-12-15|DELIVER IN PERSON|SHIP|regular packages\n3264|108626|8627|4|24|39230.88|0.09|0.07|N|O|1997-01-07|1996-12-13|1997-01-11|TAKE BACK RETURN|RAIL|ctions. quick\n3264|62126|2127|5|6|6528.72|0.04|0.03|N|O|1996-11-10|1996-12-05|1996-11-22|TAKE BACK RETURN|SHIP|press packages. ironical\n3264|140345|346|6|43|59569.62|0.06|0.06|N|O|1997-01-17|1997-01-24|1997-02-01|TAKE BACK RETURN|TRUCK|leep at the blithely bold\n3265|24887|9892|1|8|14495.04|0.06|0.02|A|F|1992-09-01|1992-09-12|1992-09-27|DELIVER IN PERSON|TRUCK|thely ironic requests sleep slyly-- i\n3265|71780|4288|2|7|12262.46|0.09|0.00|R|F|1992-09-16|1992-09-04|1992-10-14|DELIVER IN PERSON|MAIL|he forges. fluffily regular asym\n3265|190046|5085|3|28|31809.12|0.09|0.08|A|F|1992-10-22|1992-08-23|1992-10-25|NONE|RAIL|n requests. quickly final dinos\n3266|63179|8192|1|31|35407.27|0.09|0.02|N|O|1995-06-19|1995-05-04|1995-07-06|COLLECT COD|MAIL|grate among the quickly express deposits\n3266|37805|309|2|43|74940.40|0.06|0.07|R|F|1995-05-04|1995-05-30|1995-05-11|COLLECT COD|AIR|ular asymptotes use careful\n3267|184283|1838|1|33|45120.24|0.06|0.01|N|O|1997-03-30|1997-03-25|1997-04-23|TAKE BACK RETURN|AIR|es boost. \n3268|95677|5678|1|1|1672.67|0.06|0.08|A|F|1994-09-12|1994-08-31|1994-09-16|NONE|TRUCK|. ironic, bold requests use carefull\n3268|41914|4419|2|40|74236.40|0.08|0.01|R|F|1994-06-30|1994-08-22|1994-07-25|COLLECT COD|FOB|ly. bold, eve\n3269|160745|3262|1|40|72229.60|0.02|0.07|N|O|1996-06-11|1996-05-06|1996-06-15|DELIVER IN PERSON|TRUCK|es. pending d\n3269|37202|4712|2|46|52403.20|0.00|0.02|N|O|1996-04-21|1996-04-12|1996-05-10|DELIVER IN PERSON|MAIL|final asymptotes nag\n3269|43059|572|3|39|39079.95|0.02|0.03|N|O|1996-03-13|1996-05-26|1996-03-19|COLLECT COD|MAIL|he express packages?\n3269|82008|7025|4|37|36630.00|0.07|0.05|N|O|1996-06-14|1996-04-27|1996-07-07|NONE|MAIL|egular requests. carefully un\n3269|92446|9974|5|42|60414.48|0.09|0.05|N|O|1996-03-19|1996-04-24|1996-04-18|COLLECT COD|TRUCK| the special packages. \n3269|130091|7631|6|16|17937.44|0.01|0.08|N|O|1996-03-03|1996-04-06|1996-03-06|NONE|RAIL|s cajole. silent deposits are f\n3270|34060|6564|1|11|10934.66|0.07|0.06|N|O|1997-07-29|1997-08-11|1997-08-05|TAKE BACK RETURN|AIR| solve at the regular deposits. \n3270|37156|4666|2|44|48098.60|0.10|0.05|N|O|1997-07-20|1997-08-15|1997-08-04|DELIVER IN PERSON|SHIP| accounts. carefully even \n3270|64294|6801|3|20|25165.80|0.01|0.02|N|O|1997-08-26|1997-07-31|1997-08-30|DELIVER IN PERSON|FOB|en accounts among the c\n3270|188091|5646|4|29|34193.61|0.06|0.05|N|O|1997-07-01|1997-07-23|1997-07-10|TAKE BACK RETURN|MAIL|sly regular asymptotes. slyly dog\n3270|33740|1250|5|32|53559.68|0.03|0.00|N|O|1997-09-23|1997-08-17|1997-09-27|NONE|REG AIR|promise carefully.\n3270|56984|9490|6|29|56288.42|0.01|0.04|N|O|1997-08-22|1997-08-17|1997-09-06|COLLECT COD|RAIL|ptotes nag above the quickly bold deposits\n3270|116753|9265|7|9|15927.75|0.06|0.08|N|O|1997-08-14|1997-08-11|1997-09-09|DELIVER IN PERSON|SHIP|ual packages\n3271|56433|3949|1|30|41682.90|0.01|0.04|A|F|1992-01-16|1992-03-20|1992-01-17|DELIVER IN PERSON|AIR|r the unusual Tiresia\n3271|53210|3211|2|18|20937.78|0.09|0.06|R|F|1992-05-01|1992-03-28|1992-05-29|DELIVER IN PERSON|FOB| packages eat around the furiously regul\n3271|94870|4871|3|14|26108.18|0.05|0.01|A|F|1992-02-24|1992-02-14|1992-03-23|NONE|AIR|ending, even packa\n3271|63369|8382|4|29|38638.44|0.07|0.04|A|F|1992-03-10|1992-02-05|1992-03-14|COLLECT COD|MAIL|lar instructions. carefully regular\n3296|83475|1000|1|12|17501.64|0.06|0.07|R|F|1994-12-08|1994-12-14|1994-12-24|COLLECT COD|AIR|y about the slyly bold pinto bea\n3296|148438|5981|2|31|46079.33|0.08|0.00|R|F|1995-01-26|1994-12-25|1995-02-16|NONE|REG AIR|ainst the furi\n3296|184182|4183|3|29|36719.22|0.02|0.04|A|F|1995-01-12|1994-11-26|1995-02-06|DELIVER IN PERSON|SHIP|ss ideas are reg\n3296|139691|4718|4|47|81342.43|0.06|0.00|A|F|1994-11-08|1994-12-20|1994-11-30|NONE|FOB|egular deposits. quic\n3296|176498|1533|5|16|25191.84|0.06|0.02|R|F|1995-01-11|1994-12-27|1995-01-12|DELIVER IN PERSON|SHIP|kages cajole carefully \n3296|196504|4062|6|40|64020.00|0.00|0.04|A|F|1994-12-28|1994-12-08|1995-01-13|COLLECT COD|REG AIR|ronic ideas across the\n3296|35755|3265|7|6|10144.50|0.02|0.01|R|F|1995-01-03|1994-12-23|1995-01-27|TAKE BACK RETURN|AIR|carefully fur\n3297|133262|5776|1|10|12952.60|0.10|0.04|A|F|1992-12-14|1993-01-21|1992-12-26|NONE|SHIP|ironic idea\n3298|148414|929|1|9|13161.69|0.01|0.06|N|O|1996-08-15|1996-05-24|1996-09-12|COLLECT COD|REG AIR|ly final accou\n3298|185925|5926|2|27|54294.84|0.06|0.06|N|O|1996-07-10|1996-05-21|1996-07-15|DELIVER IN PERSON|FOB|lar packages. regular deposit\n3298|28881|6388|3|25|45247.00|0.10|0.08|N|O|1996-06-30|1996-05-31|1996-07-23|COLLECT COD|SHIP|ly express f\n3298|190710|8268|4|1|1800.71|0.10|0.03|N|O|1996-07-31|1996-05-23|1996-08-24|TAKE BACK RETURN|FOB|refully regular requ\n3299|182485|40|1|40|62699.20|0.03|0.02|A|F|1994-03-21|1994-03-23|1994-04-12|COLLECT COD|AIR|lyly even request\n3300|128765|1278|1|3|5381.28|0.07|0.02|N|O|1995-11-01|1995-10-02|1995-11-20|NONE|REG AIR|g according to the dugouts. caref\n3300|148043|8044|2|23|25093.92|0.02|0.02|N|O|1995-08-17|1995-09-03|1995-09-04|COLLECT COD|TRUCK|he fluffily final a\n3301|168995|1512|1|45|92879.55|0.04|0.05|A|F|1994-11-19|1994-10-27|1994-11-24|TAKE BACK RETURN|FOB|nusual, final excuses after the entici\n3302|35376|7880|1|45|59011.65|0.09|0.00|N|O|1996-01-24|1995-12-16|1996-02-13|COLLECT COD|FOB|counts use quickl\n3303|183830|3831|1|25|47845.75|0.06|0.01|N|O|1998-03-25|1998-01-31|1998-04-12|NONE|SHIP|lly regular pi\n3303|20879|880|2|15|26998.05|0.04|0.06|N|O|1998-01-29|1998-01-22|1998-02-21|COLLECT COD|SHIP| detect sly\n3303|98533|8534|3|37|56666.61|0.05|0.02|N|O|1998-02-16|1998-03-07|1998-02-18|TAKE BACK RETURN|TRUCK| carefully ironic asympt\n3303|35272|2782|4|26|31389.02|0.09|0.00|N|O|1998-01-18|1998-03-11|1998-02-11|DELIVER IN PERSON|REG AIR|ickly permanent requests w\n3328|112585|5097|1|6|9585.48|0.03|0.08|A|F|1993-03-07|1993-01-25|1993-03-29|COLLECT COD|TRUCK|ffily even instructions detect b\n3328|4400|1901|2|23|30001.20|0.01|0.06|R|F|1993-01-12|1993-02-07|1993-01-30|TAKE BACK RETURN|MAIL|y. careful\n3328|138924|8925|3|44|86368.48|0.05|0.00|R|F|1992-12-03|1992-12-19|1992-12-09|TAKE BACK RETURN|FOB|dly quickly final foxes? re\n3328|94005|1533|4|42|41958.00|0.01|0.05|R|F|1992-11-24|1992-12-20|1992-12-06|DELIVER IN PERSON|AIR|ronic requests\n3328|130322|7862|5|25|33808.00|0.05|0.00|R|F|1993-01-28|1993-01-04|1993-01-31|NONE|RAIL|e unusual, r\n3329|137884|398|1|36|69187.68|0.09|0.08|N|O|1995-08-06|1995-08-03|1995-08-14|DELIVER IN PERSON|TRUCK|ts at the re\n3329|5898|3399|2|9|16235.01|0.00|0.02|N|O|1995-07-24|1995-08-02|1995-08-01|COLLECT COD|MAIL|lly final depo\n3329|122108|2109|3|1|1130.10|0.04|0.08|N|O|1995-08-22|1995-09-28|1995-09-09|COLLECT COD|REG AIR|regular packages are carefull\n3330|19095|4098|1|49|49690.41|0.05|0.01|R|F|1995-03-02|1995-03-03|1995-03-16|DELIVER IN PERSON|TRUCK|haggle carefully alongside of the bold r\n3331|63261|780|1|9|11018.34|0.08|0.07|A|F|1993-07-18|1993-07-03|1993-08-16|TAKE BACK RETURN|AIR|odolites. bold accounts\n3331|20445|446|2|38|51886.72|0.06|0.04|R|F|1993-07-24|1993-06-22|1993-08-23|NONE|AIR|ymptotes haggle across the ca\n3331|2546|47|3|26|37662.04|0.09|0.05|A|F|1993-08-05|1993-07-17|1993-08-29|DELIVER IN PERSON|MAIL|p asymptotes. carefully unusual in\n3332|83771|3772|1|28|49133.56|0.10|0.02|R|F|1994-12-30|1995-01-16|1995-01-16|COLLECT COD|FOB|s against the carefully special multipl\n3332|135964|3504|2|21|41999.16|0.08|0.04|R|F|1995-02-04|1995-01-08|1995-02-06|COLLECT COD|MAIL| quick packages sle\n3332|133508|8535|3|27|41620.50|0.03|0.02|A|F|1994-12-10|1995-01-14|1994-12-11|TAKE BACK RETURN|FOB|ording to the slyly regula\n3333|149926|7469|1|27|53349.84|0.06|0.08|A|F|1992-12-06|1992-10-26|1992-12-07|COLLECT COD|SHIP|s dazzle fluffil\n3333|198900|6458|2|36|71960.40|0.08|0.07|R|F|1992-11-20|1992-11-06|1992-12-16|TAKE BACK RETURN|FOB|foxes sleep neve\n3333|107224|9735|3|38|46786.36|0.05|0.05|A|F|1992-10-30|1992-11-03|1992-11-04|NONE|MAIL|ccounts promise bl\n3333|112230|2231|4|49|60869.27|0.07|0.07|R|F|1992-10-02|1992-11-30|1992-10-12|DELIVER IN PERSON|MAIL|riously ironic r\n3333|42929|442|5|45|84236.40|0.07|0.08|A|F|1992-10-04|1992-11-08|1992-10-27|COLLECT COD|SHIP|dolites. quickly r\n3334|186434|1471|1|20|30408.60|0.04|0.03|N|O|1996-05-21|1996-04-08|1996-05-26|TAKE BACK RETURN|AIR|uses nag furiously. instructions are ca\n3334|189013|4050|2|7|7714.07|0.09|0.07|N|O|1996-04-28|1996-04-08|1996-05-25|NONE|SHIP|nts sublate slyly express pack\n3335|104399|9420|1|13|18244.07|0.06|0.07|N|O|1996-01-20|1995-12-20|1996-02-09|COLLECT COD|REG AIR|out the special asymptotes\n3335|30734|735|2|44|73248.12|0.07|0.02|N|O|1996-01-05|1995-12-25|1996-01-18|DELIVER IN PERSON|SHIP|r packages cajole ac\n3335|139710|2224|3|16|27995.36|0.01|0.06|N|O|1995-10-18|1995-12-08|1995-11-03|DELIVER IN PERSON|SHIP|g packages. carefully regular reque\n3335|89353|6878|4|47|63090.45|0.10|0.03|N|O|1995-12-02|1995-11-19|1995-12-27|NONE|MAIL| quickly special ideas.\n3360|173707|6225|1|31|55201.70|0.08|0.04|N|O|1998-04-24|1998-04-12|1998-05-23|COLLECT COD|REG AIR|quests. carefully even deposits wake acros\n3360|90182|2692|2|29|33993.22|0.00|0.06|N|O|1998-04-15|1998-02-25|1998-05-13|TAKE BACK RETURN|FOB|press asymptotes. furiously final \n3360|81355|1356|3|39|52117.65|0.08|0.03|N|O|1998-04-09|1998-04-20|1998-05-05|DELIVER IN PERSON|REG AIR|s. blithely express pinto bean\n3360|116741|4275|4|29|50974.46|0.10|0.01|N|O|1998-05-19|1998-03-03|1998-06-09|TAKE BACK RETURN|FOB|hely gifts. spe\n3360|57076|9582|5|4|4132.28|0.08|0.07|N|O|1998-02-27|1998-03-23|1998-03-28|COLLECT COD|SHIP|ly busy inst\n3360|70055|2563|6|42|43052.10|0.04|0.01|N|O|1998-05-07|1998-04-18|1998-06-04|DELIVER IN PERSON|FOB|ages cajole. pending, \n3361|143748|3749|1|6|10750.44|0.02|0.02|R|F|1992-10-02|1992-10-25|1992-10-05|DELIVER IN PERSON|FOB| packages sleep. furiously unus\n3361|170012|5047|2|33|35706.33|0.01|0.02|R|F|1992-11-09|1992-10-15|1992-11-11|TAKE BACK RETURN|MAIL|uriously ironic accounts. ironic, ir\n3361|190085|7643|3|31|36427.48|0.06|0.04|R|F|1992-08-29|1992-10-13|1992-09-08|NONE|FOB|ts. pending, regular accounts sleep fur\n3362|21372|8879|1|14|18107.18|0.06|0.05|N|O|1995-08-01|1995-09-06|1995-08-22|NONE|FOB|even Tires\n3362|194902|4903|2|41|81872.90|0.05|0.03|N|O|1995-10-31|1995-09-04|1995-11-17|COLLECT COD|REG AIR|ake alongside of the \n3362|114822|7334|3|40|73472.80|0.05|0.06|N|O|1995-08-19|1995-10-17|1995-09-05|TAKE BACK RETURN|FOB|packages haggle furi\n3362|1127|6128|4|3|3084.36|0.03|0.01|N|O|1995-08-26|1995-09-02|1995-09-17|NONE|SHIP|its cajole blithely excuses. de\n3362|137109|7110|5|36|41259.60|0.06|0.00|N|O|1995-10-05|1995-08-28|1995-11-03|TAKE BACK RETURN|RAIL|es against the quickly permanent pint\n3362|187847|7848|6|46|89002.64|0.09|0.05|N|O|1995-08-02|1995-10-12|1995-08-28|COLLECT COD|REG AIR|ly bold packages. regular deposits cajol\n3363|9770|2271|1|42|70550.34|0.00|0.08|N|O|1995-11-09|1995-11-25|1995-11-15|TAKE BACK RETURN|RAIL| blithely final ideas nag after\n3363|190181|5220|2|21|26694.78|0.08|0.08|N|O|1995-12-10|1995-10-28|1995-12-28|COLLECT COD|RAIL|he regular, brave deposits. f\n3363|158620|1136|3|2|3357.24|0.01|0.07|N|O|1996-01-22|1995-12-01|1996-02-18|TAKE BACK RETURN|SHIP|uickly bold ide\n3363|112620|154|4|20|32652.40|0.07|0.06|N|O|1995-12-11|1995-11-15|1995-12-21|COLLECT COD|MAIL|carefully quiet excuses wake. sl\n3363|199176|6734|5|4|5100.68|0.00|0.08|N|O|1995-10-30|1995-11-17|1995-11-22|COLLECT COD|FOB| ironic dependencie\n3364|89208|6733|1|49|58662.80|0.03|0.05|N|O|1997-09-17|1997-08-23|1997-10-06|NONE|SHIP|d accounts? caref\n3364|110107|108|2|38|42449.80|0.02|0.02|N|O|1997-08-30|1997-09-12|1997-09-27|COLLECT COD|REG AIR| slyly express\n3364|155969|8485|3|10|20249.60|0.00|0.01|N|O|1997-08-10|1997-08-24|1997-08-15|TAKE BACK RETURN|SHIP|g the accounts. final, busy accounts wi\n3364|159356|4387|4|7|9907.45|0.10|0.05|N|O|1997-07-09|1997-08-01|1997-07-16|NONE|TRUCK|furiously regular ideas haggle furiously b\n3364|80467|5484|5|3|4342.38|0.01|0.00|N|O|1997-10-19|1997-08-15|1997-10-28|TAKE BACK RETURN|TRUCK|c theodolites. blithely ir\n3365|150138|5169|1|37|43960.81|0.02|0.08|R|F|1994-12-22|1995-02-07|1995-01-20|TAKE BACK RETURN|SHIP|requests. quickly pending instructions a\n3365|166378|3927|2|37|53441.69|0.07|0.08|A|F|1994-11-24|1995-01-09|1994-11-27|NONE|REG AIR|oze blithely. furiously ironic theodolit\n3365|114118|4119|3|13|14717.43|0.09|0.02|R|F|1995-02-25|1995-01-31|1995-03-16|NONE|RAIL|pths wake r\n3365|175036|2588|4|49|54440.47|0.02|0.07|R|F|1995-01-03|1995-01-01|1995-01-18|COLLECT COD|MAIL|lyly unusual asymptotes. final\n3365|15264|267|5|2|2358.52|0.00|0.03|R|F|1995-02-04|1994-12-30|1995-03-06|TAKE BACK RETURN|FOB|es cajole fluffily pe\n3365|125386|411|6|24|33873.12|0.01|0.00|R|F|1995-02-27|1995-01-09|1995-03-27|DELIVER IN PERSON|REG AIR|into beans? carefully regula\n3366|39754|9755|1|4|6775.00|0.07|0.01|N|O|1997-05-20|1997-06-25|1997-06-03|DELIVER IN PERSON|AIR| carefully about \n3366|135173|7687|2|9|10873.53|0.00|0.08|N|O|1997-06-02|1997-07-05|1997-06-26|COLLECT COD|REG AIR|ackages sleep carefully across the bli\n3367|40397|7910|1|27|36109.53|0.01|0.03|A|F|1993-04-13|1993-03-16|1993-04-26|NONE|RAIL|kly even instructions caj\n3367|140913|8456|2|34|66432.94|0.04|0.08|A|F|1993-03-30|1993-02-23|1993-04-11|COLLECT COD|MAIL| accounts wake slyly \n3367|119437|4460|3|38|55344.34|0.03|0.03|R|F|1993-03-13|1993-02-12|1993-03-31|NONE|RAIL|even packages sleep blithely slyly expr\n3392|170472|5507|1|40|61698.80|0.01|0.01|N|O|1996-02-18|1995-12-16|1996-02-26|COLLECT COD|MAIL|ress instructions affix carefully. fur\n3392|122112|7137|2|13|14743.43|0.09|0.02|N|O|1995-11-26|1996-01-17|1995-12-01|NONE|MAIL|across the fluffily bold deposits.\n3392|126515|4052|3|34|52411.34|0.10|0.08|N|O|1996-01-20|1996-01-21|1996-01-24|DELIVER IN PERSON|MAIL|e carefully even braids. \n3392|123260|8285|4|7|8982.82|0.08|0.05|N|O|1995-12-07|1996-01-09|1995-12-29|TAKE BACK RETURN|RAIL|as. express, final accounts dou\n3393|116495|4029|1|16|24183.84|0.01|0.00|N|O|1995-07-17|1995-08-19|1995-08-04|COLLECT COD|TRUCK|uses. instructions after the blithely \n3393|124847|9872|2|44|82360.96|0.08|0.04|N|O|1995-10-16|1995-08-05|1995-11-01|NONE|AIR|ld requests hag\n3393|96369|3897|3|25|34134.00|0.07|0.02|N|O|1995-10-17|1995-08-12|1995-11-11|DELIVER IN PERSON|MAIL|ng excuses\n3393|71005|3513|4|48|46848.00|0.06|0.06|N|O|1995-07-12|1995-09-15|1995-08-02|NONE|FOB| blithely final reques\n3393|177894|2929|5|37|72959.93|0.07|0.02|N|O|1995-10-16|1995-08-19|1995-10-19|COLLECT COD|AIR|ss the slyly ironic pinto beans. ironic,\n3393|61866|9385|6|17|31073.62|0.04|0.01|N|O|1995-08-15|1995-09-07|1995-09-10|COLLECT COD|MAIL|kly ironic deposits could\n3394|154659|4660|1|33|56550.45|0.07|0.08|N|O|1996-08-07|1996-07-17|1996-09-02|TAKE BACK RETURN|SHIP|ideas alongside of th\n3394|145731|8246|2|43|76399.39|0.08|0.03|N|O|1996-08-23|1996-07-20|1996-08-25|COLLECT COD|RAIL|hockey players. slyly regular requests afte\n3394|87945|454|3|26|50256.44|0.01|0.00|N|O|1996-08-08|1996-06-12|1996-09-05|TAKE BACK RETURN|RAIL|its use furiously. even, even account\n3394|80118|119|4|14|15373.54|0.08|0.00|N|O|1996-06-02|1996-07-02|1996-06-19|COLLECT COD|MAIL|e furiously final theodolites. furio\n3394|126483|6484|5|30|45284.40|0.04|0.06|N|O|1996-05-12|1996-07-24|1996-05-19|COLLECT COD|REG AIR|t ideas according to the fluffily iro\n3394|183447|8484|6|14|21426.16|0.05|0.05|N|O|1996-06-18|1996-06-24|1996-07-17|NONE|REG AIR|arefully regular do\n3395|141634|1635|1|21|35188.23|0.03|0.06|R|F|1994-12-19|1995-01-13|1994-12-25|TAKE BACK RETURN|SHIP| careful dep\n3395|35345|2855|2|38|48652.92|0.01|0.07|R|F|1995-01-13|1995-01-13|1995-01-25|COLLECT COD|SHIP| silent accounts are blithely\n3395|42169|2170|3|43|47779.88|0.06|0.07|A|F|1994-12-13|1995-01-07|1994-12-14|COLLECT COD|AIR|ckages above the furiously regu\n3395|121513|6538|4|39|59845.89|0.05|0.07|R|F|1994-12-03|1995-01-17|1994-12-10|NONE|AIR|riously unusual theodolites. fur\n3396|127938|2963|1|34|66841.62|0.00|0.06|A|F|1994-05-30|1994-08-16|1994-06-11|NONE|AIR|. slyly unusual packages wak\n3396|48322|827|2|43|54623.76|0.03|0.08|A|F|1994-07-03|1994-08-09|1994-07-14|TAKE BACK RETURN|MAIL|cial packages cajole blithely around the \n3396|137579|93|3|9|14549.13|0.01|0.06|R|F|1994-07-01|1994-08-18|1994-07-21|DELIVER IN PERSON|AIR|usly special foxes. accounts wake careful\n3396|74334|1856|4|32|41866.56|0.06|0.02|R|F|1994-08-07|1994-08-10|1994-09-05|COLLECT COD|TRUCK|osits are slyly. final, bold foxes s\n3396|125022|47|5|27|28269.54|0.02|0.01|A|F|1994-09-14|1994-07-26|1994-09-28|DELIVER IN PERSON|FOB| theodolites \n3396|38687|8688|6|18|29262.24|0.10|0.00|A|F|1994-07-27|1994-06-26|1994-08-25|TAKE BACK RETURN|REG AIR|l requests haggle furiously along the fur\n3396|197228|4786|7|31|41081.82|0.05|0.06|A|F|1994-06-07|1994-06-23|1994-06-19|TAKE BACK RETURN|REG AIR|l, express pinto beans. quic\n3397|194289|9328|1|8|11066.24|0.07|0.01|A|F|1994-08-05|1994-08-11|1994-08-08|DELIVER IN PERSON|RAIL|y final foxes\n3397|12042|9546|2|11|10494.44|0.00|0.07|A|F|1994-07-29|1994-09-18|1994-08-12|DELIVER IN PERSON|REG AIR|iously careful packages. s\n3397|183905|6424|3|1|1988.90|0.07|0.05|R|F|1994-08-03|1994-07-30|1994-08-28|NONE|RAIL| regular packag\n3397|85592|5593|4|33|52060.47|0.05|0.01|R|F|1994-09-04|1994-08-06|1994-09-22|COLLECT COD|RAIL|gular accounts. blithely re\n3397|131561|1562|5|28|44591.68|0.05|0.05|R|F|1994-07-13|1994-08-26|1994-07-17|NONE|TRUCK|counts around the final reques\n3398|172957|2958|1|1|2029.95|0.01|0.08|N|O|1996-11-22|1996-11-16|1996-12-09|COLLECT COD|MAIL| blithely final deposits.\n3399|133853|8880|1|28|52831.80|0.09|0.05|N|O|1995-06-29|1995-05-19|1995-07-12|COLLECT COD|AIR|oggedly final theodolites grow. fi\n3399|54517|4518|2|8|11772.08|0.01|0.05|A|F|1995-05-15|1995-04-19|1995-06-05|COLLECT COD|TRUCK|s use carefully carefully ir\n3399|66486|1499|3|3|4357.44|0.03|0.00|N|F|1995-06-16|1995-04-04|1995-06-23|NONE|SHIP|hely pending dugouts \n3399|13659|3660|4|21|33025.65|0.09|0.06|A|F|1995-03-12|1995-05-18|1995-03-28|TAKE BACK RETURN|MAIL|se final courts. exc\n3424|180866|8421|1|39|75927.54|0.06|0.07|N|O|1996-11-03|1996-11-08|1996-11-23|DELIVER IN PERSON|MAIL|bits boost closely slyly p\n3425|119527|9528|1|11|17011.72|0.03|0.08|N|O|1996-04-24|1996-05-29|1996-05-23|DELIVER IN PERSON|FOB|ckly final deposits use quickly?\n3425|78421|5943|2|37|51778.54|0.06|0.03|N|O|1996-06-04|1996-05-09|1996-06-12|NONE|SHIP|as sleep carefully into the caref\n3425|13460|964|3|8|10987.68|0.06|0.08|N|O|1996-07-22|1996-06-07|1996-07-26|TAKE BACK RETURN|AIR|iously regular theodolites wake. s\n3425|18914|8915|4|37|67817.67|0.04|0.01|N|O|1996-07-10|1996-05-10|1996-08-02|NONE|SHIP|ngside of the furiously thin dol\n3425|78322|830|5|48|62415.36|0.08|0.04|N|O|1996-04-14|1996-05-25|1996-04-23|TAKE BACK RETURN|AIR|uctions wake fluffily. care\n3425|147931|7932|6|24|47494.32|0.05|0.04|N|O|1996-04-22|1996-06-24|1996-04-25|TAKE BACK RETURN|AIR|ajole blithely sl\n3426|109706|4727|1|20|34314.00|0.05|0.04|N|O|1996-11-10|1996-12-24|1996-12-01|COLLECT COD|FOB|sits cajole blit\n3426|13356|860|2|19|24117.65|0.10|0.08|N|O|1996-11-02|1997-01-13|1996-11-15|DELIVER IN PERSON|RAIL|slyly special packages oug\n3426|66426|8933|3|19|26455.98|0.08|0.05|N|O|1996-12-07|1996-12-15|1996-12-14|DELIVER IN PERSON|FOB|c accounts cajole carefu\n3426|5263|5264|4|9|10514.34|0.09|0.05|N|O|1996-12-24|1997-01-14|1997-01-13|NONE|FOB|pecial theodolites haggle fluf\n3426|48579|1084|5|31|47354.67|0.07|0.08|N|O|1996-11-11|1996-12-10|1996-12-10|DELIVER IN PERSON|SHIP| even sentiment\n3427|53394|3395|1|41|55242.99|0.10|0.01|N|O|1997-09-11|1997-07-03|1997-10-04|COLLECT COD|RAIL|s the carefully\n3427|188918|3955|2|24|48165.84|0.02|0.04|N|O|1997-07-01|1997-07-28|1997-07-30|NONE|SHIP|y bold, sly deposits. pendi\n3427|138997|1511|3|40|81439.60|0.06|0.05|N|O|1997-06-12|1997-08-19|1997-06-23|COLLECT COD|MAIL|patterns cajole ca\n3427|118088|3111|4|31|34288.48|0.08|0.04|N|O|1997-08-12|1997-07-26|1997-08-25|COLLECT COD|RAIL|s are carefull\n3428|197651|7652|1|4|6994.60|0.00|0.03|N|O|1996-05-09|1996-06-13|1996-06-02|NONE|REG AIR|sly pending requests int\n3428|117436|7437|2|35|50870.05|0.02|0.03|N|O|1996-05-01|1996-06-07|1996-05-20|COLLECT COD|TRUCK|ly regular pinto beans sleep\n3428|135307|5308|3|47|63088.10|0.07|0.05|N|O|1996-04-16|1996-06-08|1996-05-05|NONE|REG AIR|y final pinto \n3429|136768|1795|1|48|86628.48|0.06|0.02|N|O|1997-04-08|1997-03-09|1997-04-25|TAKE BACK RETURN|SHIP| haggle furiously ir\n3429|58421|927|2|15|20691.30|0.03|0.04|N|O|1997-02-04|1997-03-09|1997-03-01|TAKE BACK RETURN|TRUCK|beans are fu\n3429|68561|6080|3|10|15295.60|0.05|0.07|N|O|1997-01-19|1997-02-22|1997-01-25|TAKE BACK RETURN|REG AIR|ackages. quickly e\n3429|88366|875|4|28|37922.08|0.10|0.07|N|O|1997-01-30|1997-03-18|1997-02-17|TAKE BACK RETURN|AIR|nstructions boost. thin\n3429|164054|4055|5|45|50312.25|0.10|0.00|N|O|1997-04-21|1997-03-08|1997-05-05|COLLECT COD|REG AIR|ites poach a\n3430|188117|3154|1|2|2410.22|0.07|0.06|R|F|1995-03-07|1995-01-28|1995-03-30|TAKE BACK RETURN|MAIL|sh furiously according to the evenly e\n3430|80604|5621|2|32|50707.20|0.08|0.00|R|F|1995-01-17|1995-01-28|1995-02-06|NONE|TRUCK|egular instruction\n3430|96381|6382|3|41|56472.58|0.06|0.04|R|F|1995-02-18|1995-02-21|1995-03-11|TAKE BACK RETURN|AIR|cuses. silent excuses h\n3430|64955|9968|4|50|95997.50|0.01|0.00|R|F|1994-12-15|1995-03-03|1994-12-24|COLLECT COD|REG AIR|ironic theodolites. carefully regular pac\n3430|94745|2273|5|5|8698.70|0.05|0.05|A|F|1995-04-02|1995-02-12|1995-04-08|DELIVER IN PERSON|FOB|even accounts haggle slyly bol\n3430|170718|5753|6|15|26830.65|0.08|0.07|A|F|1995-02-01|1995-03-12|1995-02-04|COLLECT COD|SHIP|cajole around the accounts. qui\n3430|51323|6334|7|23|29309.36|0.09|0.08|A|F|1995-03-06|1995-03-01|1995-03-10|COLLECT COD|MAIL|eas according to the\n3431|179230|6782|1|41|53678.43|0.03|0.06|A|F|1993-09-26|1993-10-13|1993-10-22|NONE|AIR| sleep carefully ironically special\n3456|110400|5423|1|34|47953.60|0.10|0.06|A|F|1993-08-29|1993-08-26|1993-09-07|TAKE BACK RETURN|SHIP|usy pinto beans b\n3457|181229|6266|1|29|37996.38|0.03|0.02|R|F|1995-05-12|1995-07-13|1995-06-05|NONE|TRUCK|refully final excuses wake\n3457|105915|5916|2|22|42260.02|0.06|0.01|N|O|1995-06-23|1995-06-16|1995-06-29|NONE|SHIP|packages nag furiously against\n3457|108003|514|3|7|7077.00|0.07|0.08|N|O|1995-08-14|1995-07-06|1995-08-18|COLLECT COD|SHIP| pending accounts along the\n3457|983|984|4|24|45215.52|0.07|0.07|N|O|1995-08-03|1995-05-30|1995-08-14|TAKE BACK RETURN|REG AIR|tructions haggle alongsid\n3457|108595|3616|5|42|67350.78|0.05|0.01|A|F|1995-06-12|1995-06-14|1995-06-14|COLLECT COD|MAIL|riously final instruc\n3457|143724|6239|6|45|79547.40|0.08|0.01|N|O|1995-08-12|1995-07-18|1995-08-23|TAKE BACK RETURN|SHIP| packages. care\n3457|166641|1674|7|9|15368.76|0.04|0.00|R|F|1995-05-29|1995-06-30|1995-06-12|DELIVER IN PERSON|FOB|quests. foxes sleep quickly\n3458|132484|7511|1|48|72791.04|0.06|0.04|R|F|1995-03-17|1995-01-25|1995-03-28|TAKE BACK RETURN|AIR|iously pending dep\n3458|49179|4188|2|46|51895.82|0.06|0.06|R|F|1995-03-08|1995-01-21|1995-03-10|TAKE BACK RETURN|SHIP|nod across the boldly even instruct\n3458|142061|2062|3|36|39710.16|0.01|0.06|R|F|1995-04-20|1995-02-14|1995-05-09|TAKE BACK RETURN|REG AIR|s lose. blithely ironic requests boost\n3458|15253|7755|4|16|18692.00|0.09|0.03|R|F|1995-03-01|1995-02-25|1995-03-06|TAKE BACK RETURN|AIR|s grow carefully. express, final grouc\n3458|156396|8912|5|2|2904.78|0.09|0.03|A|F|1995-02-05|1995-02-01|1995-03-07|COLLECT COD|FOB|ironic packages haggle past the furiously \n3458|141853|9396|6|6|11369.10|0.09|0.04|A|F|1995-03-10|1995-02-02|1995-03-23|TAKE BACK RETURN|AIR|dolites; regular theodolites cajole \n3459|178065|5617|1|31|35434.86|0.06|0.01|A|F|1994-09-05|1994-10-20|1994-10-03|NONE|REG AIR|y regular pain\n3459|129270|4295|2|30|38978.10|0.04|0.08|R|F|1994-11-22|1994-09-12|1994-12-11|NONE|REG AIR|nic theodolites; evenly i\n3459|40035|2540|3|45|43876.35|0.04|0.05|A|F|1994-07-31|1994-09-09|1994-08-02|TAKE BACK RETURN|REG AIR|ntly speci\n3459|68031|8032|4|10|9990.30|0.05|0.06|A|F|1994-10-06|1994-09-16|1994-11-03|TAKE BACK RETURN|REG AIR| furiously silent dolphi\n3459|188574|6129|5|10|16625.70|0.02|0.02|R|F|1994-08-01|1994-10-17|1994-08-11|TAKE BACK RETURN|FOB|. blithely ironic pinto beans above\n3460|10655|8159|1|40|62626.00|0.10|0.06|N|O|1995-12-28|1995-12-14|1996-01-02|NONE|REG AIR|odolites are slyly bold deposits\n3460|73365|5873|2|3|4015.08|0.06|0.00|N|O|1996-01-19|1995-12-28|1996-01-31|COLLECT COD|AIR|er quickly \n3460|34658|7162|3|40|63706.00|0.08|0.07|N|O|1995-10-29|1995-11-10|1995-11-24|TAKE BACK RETURN|REG AIR|o the even deposits\n3460|94647|9666|4|50|82082.00|0.02|0.07|N|O|1996-01-30|1995-12-10|1996-02-06|DELIVER IN PERSON|SHIP|e slyly about the sly\n3460|129391|9392|5|47|66758.33|0.08|0.05|N|O|1995-12-09|1995-11-12|1995-12-22|TAKE BACK RETURN|SHIP|es haggle slyly regular accounts. fi\n3460|62084|7097|6|46|48119.68|0.03|0.07|N|O|1996-01-27|1996-01-01|1996-02-01|NONE|TRUCK|uses run among the carefully even deposits\n3460|44512|7017|7|28|40782.28|0.00|0.01|N|O|1995-10-28|1995-11-13|1995-11-17|COLLECT COD|SHIP|inal, ironic instructions. carefully\n3461|99796|7324|1|49|87993.71|0.06|0.06|A|F|1993-03-09|1993-04-16|1993-03-13|DELIVER IN PERSON|RAIL|ual request\n3461|62417|2418|2|27|37244.07|0.06|0.06|A|F|1993-02-10|1993-03-02|1993-03-04|COLLECT COD|SHIP|ely unusual deposits. quickly ir\n3461|38930|1434|3|44|82232.92|0.09|0.06|A|F|1993-05-20|1993-04-03|1993-05-27|COLLECT COD|RAIL| haggle quickly even ideas. fin\n3461|94288|6798|4|41|52573.48|0.09|0.02|R|F|1993-02-19|1993-04-20|1993-02-21|NONE|TRUCK|heodolites. blithely ironi\n3461|89771|4788|5|16|28172.32|0.08|0.06|A|F|1993-05-09|1993-04-29|1993-05-26|TAKE BACK RETURN|TRUCK| pending deposi\n3461|166821|4370|6|24|45307.68|0.10|0.00|A|F|1993-06-01|1993-03-12|1993-06-20|TAKE BACK RETURN|MAIL|thely. carefully re\n3462|150880|8426|1|4|7723.52|0.09|0.04|N|O|1997-06-12|1997-07-31|1997-06-16|COLLECT COD|RAIL|ackages. fu\n3462|39432|4439|2|43|58971.49|0.08|0.03|N|O|1997-08-01|1997-07-18|1997-08-29|NONE|RAIL| carefully. final, final ideas sleep slyly\n3462|128298|811|3|6|7957.74|0.05|0.04|N|O|1997-06-02|1997-08-09|1997-06-30|NONE|RAIL|iously regular fo\n3462|98093|5621|4|2|2182.18|0.09|0.07|N|O|1997-09-10|1997-08-08|1997-09-19|NONE|AIR|nic packages. even accounts alongside \n3462|37353|4863|5|14|18064.90|0.01|0.02|N|O|1997-05-31|1997-07-05|1997-06-24|COLLECT COD|MAIL|yly. blithely bold theodolites wa\n3463|60779|3286|1|45|78289.65|0.02|0.02|A|F|1993-10-30|1993-11-04|1993-11-08|DELIVER IN PERSON|FOB|nts are slyly \n3463|97924|2943|2|43|82642.56|0.04|0.02|A|F|1993-10-28|1993-09-24|1993-11-03|DELIVER IN PERSON|FOB| across the \n3488|159702|4733|1|1|1761.70|0.04|0.01|A|F|1995-03-06|1995-02-16|1995-03-23|DELIVER IN PERSON|FOB| final excuses. carefully even waters hagg\n3488|103455|8476|2|48|70005.60|0.00|0.03|A|F|1995-03-29|1995-03-26|1995-04-28|COLLECT COD|SHIP|sly? final requests \n3488|159080|9081|3|11|12529.88|0.03|0.08|R|F|1995-03-25|1995-02-08|1995-04-16|COLLECT COD|TRUCK|unusual re\n3488|41028|3533|4|12|11628.24|0.05|0.07|R|F|1995-04-27|1995-02-16|1995-05-09|DELIVER IN PERSON|RAIL|e slyly; furiously final packages wak\n3488|155132|163|5|18|21368.34|0.09|0.06|A|F|1995-03-18|1995-03-19|1995-03-29|DELIVER IN PERSON|FOB|s the carefully r\n3489|185663|8182|1|19|33224.54|0.09|0.05|A|F|1993-07-31|1993-10-26|1993-08-15|NONE|SHIP|c deposits alongside of the pending, fu\n3489|28091|594|2|46|46878.14|0.00|0.00|A|F|1993-08-02|1993-10-09|1993-08-10|TAKE BACK RETURN|TRUCK|xcuses? quickly stealthy dependenci\n3490|91360|8888|1|43|58108.48|0.05|0.05|N|O|1997-08-04|1997-08-06|1997-08-14|TAKE BACK RETURN|SHIP|. even requests cajol\n3490|85447|2972|2|50|71622.00|0.05|0.07|N|O|1997-06-27|1997-08-15|1997-06-28|NONE|RAIL| haggle carefu\n3490|92453|9981|3|8|11563.60|0.10|0.04|N|O|1997-08-11|1997-07-25|1997-08-28|COLLECT COD|MAIL|inal deposits use furiousl\n3491|153491|6007|1|28|43245.72|0.04|0.03|N|O|1998-09-29|1998-09-08|1998-10-23|COLLECT COD|FOB|ccounts. sly\n3491|121773|1774|2|22|39484.94|0.08|0.02|N|O|1998-08-19|1998-08-22|1998-09-03|TAKE BACK RETURN|REG AIR| grow against the boldly pending pinto bea\n3492|155860|5861|1|3|5747.58|0.02|0.08|R|F|1994-11-26|1994-12-28|1994-12-19|COLLECT COD|REG AIR|the deposits. carefully \n3492|125526|3063|2|7|10860.64|0.04|0.00|R|F|1995-03-10|1995-01-03|1995-03-16|COLLECT COD|FOB|thely regular dolphi\n3492|108869|8870|3|34|63847.24|0.05|0.06|A|F|1994-12-07|1994-12-29|1994-12-24|COLLECT COD|AIR| unusual requests. ir\n3492|146899|4442|4|30|58376.70|0.02|0.06|A|F|1995-01-29|1995-01-02|1995-02-13|DELIVER IN PERSON|MAIL| detect furiously permanent, unusual accou\n3492|121499|6524|5|47|71463.03|0.09|0.07|R|F|1995-03-24|1994-12-28|1995-03-29|NONE|REG AIR|deposits. quickly express \n3492|21118|3621|6|47|48838.17|0.04|0.07|R|F|1994-12-12|1995-01-18|1994-12-26|COLLECT COD|RAIL|ronic instructions u\n3493|92506|7525|1|31|46453.50|0.06|0.07|R|F|1993-10-22|1993-10-12|1993-11-07|DELIVER IN PERSON|REG AIR|ructions. slyly regular accounts across the\n3493|131994|1995|2|10|20259.90|0.02|0.06|R|F|1993-08-27|1993-10-07|1993-09-23|COLLECT COD|TRUCK|hall have to integ\n3494|116147|8659|1|40|46525.60|0.05|0.04|R|F|1993-07-10|1993-06-01|1993-07-25|TAKE BACK RETURN|TRUCK|lites haggle furiously about the fin\n3494|74124|4125|2|23|25256.76|0.10|0.01|A|F|1993-06-19|1993-06-04|1993-07-14|NONE|FOB|osits nag \n3494|197161|4719|3|40|50326.40|0.02|0.08|A|F|1993-05-30|1993-07-02|1993-06-20|TAKE BACK RETURN|MAIL|uests cajole blithely\n3494|76076|6077|4|30|31562.10|0.04|0.03|R|F|1993-07-01|1993-06-08|1993-07-15|TAKE BACK RETURN|TRUCK|ns are quickly regular, \n3495|27321|9824|1|20|24966.40|0.10|0.03|N|O|1996-04-24|1996-05-18|1996-05-01|TAKE BACK RETURN|RAIL|posits are carefully; forges cajole qui\n3495|172119|9671|2|24|28586.64|0.05|0.02|N|O|1996-03-22|1996-04-10|1996-04-07|DELIVER IN PERSON|RAIL|ic, final pains along the even request\n3495|198597|8598|3|16|27129.44|0.08|0.02|N|O|1996-03-30|1996-04-02|1996-04-12|TAKE BACK RETURN|AIR|y bold dependencies; blithely idle sautern\n3520|27999|5506|1|30|57809.70|0.04|0.02|N|O|1997-11-11|1997-10-02|1997-12-06|COLLECT COD|SHIP|deas should solve blithely among the ironi\n3520|166759|1792|2|38|69378.50|0.00|0.04|N|O|1997-08-14|1997-10-26|1997-09-09|NONE|RAIL|yly final packages according to the quickl\n3520|105258|7769|3|5|6316.25|0.01|0.02|N|O|1997-11-13|1997-09-22|1997-12-09|NONE|MAIL|ly even ideas haggle \n3520|63745|3746|4|41|70058.34|0.01|0.01|N|O|1997-08-06|1997-09-20|1997-08-20|TAKE BACK RETURN|AIR| carefully pendi\n3520|162174|7207|5|35|43265.95|0.02|0.02|N|O|1997-09-16|1997-09-03|1997-09-24|DELIVER IN PERSON|FOB|s nag carefully. sometimes unusual account\n3521|58448|3459|1|48|67509.12|0.09|0.03|A|F|1993-01-03|1992-12-31|1993-01-22|NONE|AIR|ses use. furiously express ideas wake f\n3521|130897|898|2|2|3855.78|0.05|0.06|R|F|1993-01-29|1992-12-20|1993-02-23|NONE|MAIL|refully duri\n3521|177971|489|3|38|77860.86|0.00|0.08|A|F|1993-02-15|1992-12-10|1993-03-10|COLLECT COD|FOB|ges hang q\n3521|143978|9007|4|26|52571.22|0.02|0.08|R|F|1993-01-04|1993-01-20|1993-01-17|DELIVER IN PERSON|AIR|onic dependencies haggle. fur\n3521|35052|59|5|28|27637.40|0.10|0.01|A|F|1993-01-06|1993-01-22|1993-02-02|TAKE BACK RETURN|FOB|e slyly above the slyly final\n3522|3671|8672|1|6|9448.02|0.08|0.03|A|F|1995-01-21|1994-12-09|1995-01-23|NONE|SHIP|tes snooze \n3522|86438|8947|2|48|68372.64|0.00|0.03|R|F|1994-12-05|1994-10-30|1994-12-26|TAKE BACK RETURN|SHIP|ve the quickly special packages\n3522|156693|1724|3|46|80485.74|0.09|0.02|A|F|1994-11-12|1994-11-30|1994-11-20|NONE|AIR|d the express, silent foxes. blit\n3522|129818|4843|4|7|12934.67|0.10|0.02|A|F|1994-10-31|1994-11-19|1994-11-28|NONE|TRUCK|e stealthil\n3522|49144|6657|5|27|29514.78|0.02|0.05|R|F|1994-11-29|1994-12-15|1994-12-08|COLLECT COD|REG AIR|ic tithes. car\n3522|157970|5516|6|18|36503.46|0.01|0.03|A|F|1994-11-16|1994-10-29|1994-11-29|COLLECT COD|RAIL|sits wake carefully pen\n3523|24474|4475|1|15|20977.05|0.06|0.02|N|O|1998-06-26|1998-05-22|1998-07-24|COLLECT COD|REG AIR|se slyly pending, sp\n3523|132127|9667|2|4|4636.48|0.03|0.06|N|O|1998-05-08|1998-05-18|1998-05-25|TAKE BACK RETURN|MAIL|ts. final accounts detect furiously along \n3523|49146|1651|3|24|26283.36|0.07|0.04|N|O|1998-08-02|1998-06-22|1998-08-27|COLLECT COD|FOB|ke according to the doggedly re\n3523|191238|3758|4|36|47852.28|0.06|0.08|N|O|1998-05-26|1998-06-04|1998-06-25|DELIVER IN PERSON|SHIP|accounts. fluffily regu\n3523|133109|3110|5|48|54820.80|0.00|0.01|N|O|1998-07-22|1998-06-25|1998-08-19|DELIVER IN PERSON|AIR| regular requests\n3524|136119|6120|1|5|5775.55|0.01|0.04|R|F|1992-05-23|1992-07-25|1992-06-19|DELIVER IN PERSON|RAIL|ts whithout the bold depende\n3524|142948|7977|2|17|33845.98|0.09|0.08|A|F|1992-09-01|1992-07-17|1992-09-05|DELIVER IN PERSON|FOB|g, final epitaphs about the pinto \n3525|45478|5479|1|12|17081.64|0.01|0.03|N|O|1996-03-08|1996-03-18|1996-03-16|NONE|TRUCK|lar excuses wake carefull\n3525|137210|2237|2|27|33674.67|0.03|0.03|N|O|1995-12-30|1996-01-23|1996-01-02|DELIVER IN PERSON|SHIP|y slyly special asymptotes\n3525|74397|6905|3|31|42513.09|0.00|0.03|N|O|1996-03-08|1996-02-27|1996-03-13|COLLECT COD|TRUCK|he careful\n3525|183399|954|4|28|41506.92|0.03|0.02|N|O|1996-01-22|1996-02-08|1996-01-27|COLLECT COD|FOB| nag according \n3526|97645|7646|1|11|18069.04|0.02|0.03|R|F|1995-05-23|1995-05-28|1995-05-24|NONE|TRUCK|ges. furiously regular d\n3526|116383|3917|2|23|32185.74|0.03|0.04|A|F|1995-05-01|1995-05-31|1995-05-25|DELIVER IN PERSON|FOB|special, regular packages cajole. \n3526|32541|51|3|20|29470.80|0.05|0.08|N|F|1995-06-16|1995-04-26|1995-06-22|DELIVER IN PERSON|REG AIR|kages. bold, special requests detect sl\n3527|101388|6409|1|47|65300.86|0.07|0.02|N|O|1997-07-14|1997-07-29|1997-07-21|DELIVER IN PERSON|RAIL|unts. express re\n3527|25083|2590|2|33|33266.64|0.01|0.02|N|O|1997-09-25|1997-09-17|1997-10-12|NONE|FOB|kly alongside of \n3527|161298|8847|3|50|67964.50|0.09|0.07|N|O|1997-07-17|1997-08-03|1997-07-29|DELIVER IN PERSON|SHIP|e even accounts was about th\n3527|127163|9676|4|17|20232.72|0.02|0.05|N|O|1997-07-30|1997-09-01|1997-08-17|COLLECT COD|MAIL|ular instruction\n3552|196483|6484|1|18|28430.64|0.01|0.07|N|O|1997-08-11|1997-07-14|1997-08-15|DELIVER IN PERSON|TRUCK|s deposits against the blithely unusual pin\n3552|89318|1827|2|44|57521.64|0.01|0.00|N|O|1997-08-08|1997-06-15|1997-08-29|COLLECT COD|FOB|ns after the blithely reg\n3552|160455|8004|3|36|54556.20|0.04|0.08|N|O|1997-06-29|1997-06-24|1997-07-21|COLLECT COD|TRUCK|ly regular theodolites. fin\n3553|142076|4591|1|4|4472.28|0.05|0.01|R|F|1994-06-13|1994-07-10|1994-07-03|COLLECT COD|RAIL|olites boost bli\n3553|64118|6625|2|26|28134.86|0.05|0.08|A|F|1994-08-06|1994-07-30|1994-08-23|DELIVER IN PERSON|MAIL|fily special p\n3553|21291|8798|3|18|21821.22|0.04|0.03|A|F|1994-07-03|1994-06-30|1994-07-07|COLLECT COD|RAIL|. quickly ironic\n3553|31009|3513|4|40|37600.00|0.06|0.00|A|F|1994-09-14|1994-06-26|1994-09-25|NONE|RAIL| slyly pending asymptotes against the furi\n3553|156020|1051|5|36|38736.72|0.06|0.08|R|F|1994-08-12|1994-06-25|1994-09-06|DELIVER IN PERSON|TRUCK| realms. pending, bold theodolites \n3554|174151|6669|1|32|39204.80|0.01|0.05|N|O|1995-09-28|1995-09-01|1995-10-07|NONE|RAIL|. blithely ironic t\n3554|144071|4072|2|18|20071.26|0.03|0.00|N|O|1995-09-11|1995-08-12|1995-10-04|DELIVER IN PERSON|REG AIR| haggle. furiously fluffy requests ac\n3554|191547|1548|3|41|67180.14|0.02|0.01|N|O|1995-07-13|1995-08-28|1995-07-27|DELIVER IN PERSON|MAIL|ent dependencies. sly\n3555|165543|576|1|11|17693.94|0.05|0.02|N|O|1996-09-25|1996-10-01|1996-10-03|NONE|FOB|oost caref\n3555|78411|8412|2|15|20841.15|0.03|0.08|N|O|1996-07-13|1996-09-01|1996-08-02|TAKE BACK RETURN|RAIL|y across the pending a\n3555|42078|9591|3|25|25501.75|0.09|0.07|N|O|1996-10-01|1996-08-23|1996-10-24|TAKE BACK RETURN|MAIL|sual packages. quickly \n3555|4429|4430|4|19|25334.98|0.00|0.05|N|O|1996-09-08|1996-09-14|1996-10-01|COLLECT COD|REG AIR|leep special theodolit\n3555|32976|7983|5|29|55360.13|0.07|0.04|N|O|1996-08-02|1996-09-04|1996-08-08|DELIVER IN PERSON|TRUCK|deas. carefully s\n3555|27342|9845|6|33|41888.22|0.04|0.08|N|O|1996-09-20|1996-09-23|1996-10-05|TAKE BACK RETURN|AIR|fluffily regular a\n3555|125254|279|7|9|11513.25|0.07|0.02|N|O|1996-10-13|1996-10-02|1996-10-22|NONE|SHIP|are. slyly final foxes acro\n3556|141443|3958|1|45|66799.80|0.05|0.06|A|F|1992-10-14|1992-12-21|1992-10-16|NONE|TRUCK|ckages boost quickl\n3556|30350|5357|2|43|55055.05|0.02|0.06|R|F|1993-01-18|1992-11-09|1993-02-04|NONE|FOB|wake carefull\n3556|86856|6857|3|28|51599.80|0.10|0.04|A|F|1993-01-06|1992-11-27|1993-01-16|NONE|MAIL|refully final instructions? ironic packa\n3557|174245|1797|1|41|54088.84|0.01|0.07|R|F|1993-01-30|1992-12-31|1993-02-18|COLLECT COD|FOB|ideas breach c\n3557|128842|8843|2|37|69221.08|0.03|0.05|R|F|1993-02-16|1993-01-05|1993-03-15|DELIVER IN PERSON|RAIL|gside of the ca\n3558|86334|6335|1|8|10562.64|0.01|0.03|N|O|1996-05-31|1996-05-26|1996-06-25|COLLECT COD|AIR|? even requests sle\n3558|9567|7068|2|28|41343.68|0.02|0.08|N|O|1996-06-02|1996-04-18|1996-06-24|COLLECT COD|TRUCK|l deposits \n3558|186452|1489|3|3|4615.35|0.03|0.06|N|O|1996-05-19|1996-04-28|1996-05-26|DELIVER IN PERSON|RAIL|l, final deposits haggle. fina\n3558|90560|8088|4|22|34112.32|0.06|0.03|N|O|1996-04-27|1996-04-19|1996-04-30|DELIVER IN PERSON|SHIP|refully ironic theodolites are fu\n3558|28850|3855|5|38|67596.30|0.03|0.08|N|O|1996-05-29|1996-05-02|1996-06-09|COLLECT COD|RAIL|refully permanently iron\n3558|71821|6836|6|17|30477.94|0.07|0.07|N|O|1996-03-14|1996-05-04|1996-04-05|NONE|RAIL|ithely unusual packa\n3559|89591|9592|1|29|45837.11|0.00|0.07|R|F|1992-12-10|1992-12-03|1992-12-20|COLLECT COD|REG AIR|l, regular accounts wake flu\n3584|10196|5199|1|4|4424.76|0.04|0.08|N|O|1997-08-16|1997-10-31|1997-08-28|DELIVER IN PERSON|TRUCK|nal packag\n3584|159495|2011|2|23|35753.27|0.00|0.03|N|O|1997-09-10|1997-10-15|1997-09-30|COLLECT COD|TRUCK|l platelets until the asymptotes \n3584|23297|3298|3|6|7321.74|0.03|0.06|N|O|1997-10-28|1997-11-09|1997-11-24|TAKE BACK RETURN|MAIL|deposits across the\n3584|145596|3139|4|11|18057.49|0.06|0.02|N|O|1997-11-27|1997-10-15|1997-12-08|NONE|REG AIR|lithely slyly \n3584|17924|2927|5|39|71834.88|0.09|0.07|N|O|1997-09-20|1997-10-31|1997-10-06|COLLECT COD|AIR|eposits. carefu\n3585|121713|6738|1|21|36428.91|0.05|0.04|A|F|1994-12-04|1994-12-25|1995-01-01|TAKE BACK RETURN|TRUCK|ounts use. express, final platelets us\n3585|18535|8536|2|40|58141.20|0.03|0.00|R|F|1995-01-22|1995-01-17|1995-02-07|TAKE BACK RETURN|RAIL|elets affix. even asymptotes play care\n3585|111350|8884|3|11|14974.85|0.01|0.04|R|F|1995-01-04|1995-02-14|1995-01-15|NONE|MAIL|even packages\n3585|47911|2920|4|33|61344.03|0.08|0.08|A|F|1994-12-14|1995-01-19|1994-12-22|NONE|RAIL|ironic dependencies serve furi\n3585|24543|2050|5|13|19078.02|0.06|0.07|R|F|1995-03-15|1995-01-22|1995-03-17|DELIVER IN PERSON|AIR|ccording to the foxes. slyly iro\n3585|93452|8471|6|7|10118.15|0.10|0.02|A|F|1994-12-13|1995-01-20|1995-01-05|TAKE BACK RETURN|TRUCK|dependencies sleep un\n3585|41685|9198|7|45|73200.60|0.03|0.00|A|F|1995-01-20|1995-02-19|1995-02-11|DELIVER IN PERSON|MAIL|are blithely c\n3586|193699|8738|1|2|3585.38|0.03|0.08|R|F|1994-02-10|1994-01-07|1994-03-03|DELIVER IN PERSON|RAIL|he even, unusual decoy\n3586|83314|3315|2|29|37621.99|0.04|0.07|R|F|1994-03-06|1994-03-02|1994-03-13|DELIVER IN PERSON|RAIL| slyly unusual i\n3586|57422|2433|3|2|2758.84|0.03|0.06|R|F|1994-03-22|1994-02-20|1994-04-08|NONE|REG AIR|unts. slyly final ideas agai\n3586|83722|3723|4|33|56288.76|0.06|0.01|R|F|1994-01-24|1994-02-09|1994-02-07|NONE|TRUCK|refully across the fur\n3586|107949|460|5|8|15655.52|0.06|0.02|A|F|1994-03-29|1994-02-26|1994-04-02|NONE|FOB|theodolites hagg\n3586|98598|1108|6|8|12772.72|0.09|0.01|A|F|1994-03-18|1994-01-17|1994-04-06|DELIVER IN PERSON|RAIL| ironic pinto beans cajole carefully theo\n3586|122699|2700|7|33|56815.77|0.05|0.04|A|F|1994-02-11|1994-01-15|1994-03-03|NONE|REG AIR|iously regular pinto beans integrate\n3587|196712|1751|1|5|9043.55|0.09|0.07|N|O|1996-09-03|1996-07-05|1996-09-11|DELIVER IN PERSON|SHIP|ithely regular decoys above the \n3587|131240|8780|2|48|61019.52|0.00|0.03|N|O|1996-08-02|1996-07-02|1996-08-05|TAKE BACK RETURN|MAIL|beans. blithely final depe\n3587|150350|7896|3|36|50412.60|0.05|0.05|N|O|1996-07-26|1996-06-16|1996-08-23|TAKE BACK RETURN|MAIL|ully regular excuse\n3587|123620|6133|4|31|50952.22|0.03|0.01|N|O|1996-07-21|1996-07-01|1996-07-23|COLLECT COD|SHIP|press fluffily regul\n3587|69585|4598|5|12|18654.96|0.06|0.03|N|O|1996-08-30|1996-07-04|1996-09-22|DELIVER IN PERSON|RAIL|g the even pinto beans. special,\n3587|106080|1101|6|16|17377.28|0.01|0.03|N|O|1996-05-11|1996-06-19|1996-06-04|COLLECT COD|FOB|y ruthless dolphins to \n3587|73225|747|7|23|27559.06|0.07|0.05|N|O|1996-08-30|1996-07-01|1996-09-10|COLLECT COD|FOB|l multipliers sleep theodolites-- slyly \n3588|90135|7663|1|28|31503.64|0.04|0.08|R|F|1995-05-03|1995-05-03|1995-05-14|DELIVER IN PERSON|TRUCK|special pinto beans cajole slyly. slyly \n3588|87759|268|2|6|10480.50|0.06|0.08|A|F|1995-04-09|1995-05-30|1995-04-10|TAKE BACK RETURN|MAIL|s. fluffily fluf\n3588|158716|8717|3|45|79861.95|0.04|0.02|R|F|1995-05-07|1995-05-04|1995-05-28|TAKE BACK RETURN|TRUCK|ecial pains integrate blithely. reques\n3588|126284|3821|4|22|28826.16|0.05|0.00|A|F|1995-04-08|1995-05-06|1995-04-27|NONE|RAIL|inal accounts. pending, bo\n3588|54948|7454|5|28|53282.32|0.03|0.03|A|F|1995-04-23|1995-05-25|1995-04-28|DELIVER IN PERSON|TRUCK| express sheaves. unusual theodo\n3588|109433|1944|6|37|53369.91|0.08|0.04|N|F|1995-06-17|1995-05-25|1995-06-24|TAKE BACK RETURN|RAIL|xcuses sleep quickly along th\n3588|38463|967|7|46|64467.16|0.08|0.07|A|F|1995-06-06|1995-05-08|1995-06-08|NONE|AIR| slyly ironic deposits sublate ab\n3589|36519|4029|1|42|61131.42|0.08|0.08|R|F|1994-08-11|1994-07-17|1994-08-23|DELIVER IN PERSON|AIR|he blithely unusual pac\n3590|175980|8498|1|10|20559.80|0.08|0.00|N|O|1995-07-17|1995-06-26|1995-08-12|TAKE BACK RETURN|SHIP|t the quickly ironic\n3590|94336|4337|2|19|25276.27|0.03|0.03|N|O|1995-08-02|1995-06-20|1995-08-08|NONE|SHIP|special pinto beans. blithely reg\n3590|95583|602|3|43|67878.94|0.07|0.06|N|O|1995-07-12|1995-07-25|1995-07-16|DELIVER IN PERSON|SHIP|s could have to use\n3590|55334|2850|4|26|33522.58|0.01|0.03|N|O|1995-07-08|1995-06-17|1995-08-02|DELIVER IN PERSON|SHIP|arefully along th\n3590|190226|227|5|37|48700.14|0.00|0.08|N|O|1995-09-01|1995-06-29|1995-09-10|NONE|SHIP|ccounts above the silent waters thrash f\n3590|118843|8844|6|31|57717.04|0.03|0.01|N|O|1995-06-24|1995-07-12|1995-06-25|DELIVER IN PERSON|REG AIR|ve furiously final instructions. slyly regu\n3590|193517|8556|7|44|70862.44|0.05|0.04|N|F|1995-06-07|1995-06-15|1995-06-27|NONE|MAIL|s sleep after the regular platelets. blit\n3591|28873|3878|1|21|37839.27|0.03|0.03|A|F|1994-02-25|1994-02-02|1994-03-05|DELIVER IN PERSON|TRUCK|structions against \n3591|68232|3245|2|24|28805.52|0.04|0.04|R|F|1993-12-26|1994-01-07|1994-01-25|COLLECT COD|FOB|ages. slyly regular dependencies cajo\n3591|163239|788|3|4|5208.92|0.01|0.03|A|F|1994-04-04|1994-02-19|1994-05-02|DELIVER IN PERSON|RAIL|he final packages. deposits serve quick\n3591|152523|2524|4|49|77200.48|0.01|0.00|A|F|1994-03-21|1994-01-26|1994-03-28|COLLECT COD|AIR| mold slyly. bl\n3616|196546|9066|1|30|49276.20|0.01|0.00|A|F|1994-05-05|1994-04-24|1994-05-12|TAKE BACK RETURN|FOB|ly ironic accounts unwind b\n3616|137984|7985|2|28|56615.44|0.08|0.06|R|F|1994-02-20|1994-04-18|1994-03-05|DELIVER IN PERSON|REG AIR|ironic packages. furiously ev\n3617|116065|6066|1|46|49728.76|0.03|0.02|N|O|1996-05-19|1996-05-14|1996-06-11|NONE|RAIL|ar theodolites. regu\n3617|97942|7943|2|16|31039.04|0.05|0.02|N|O|1996-05-08|1996-06-03|1996-05-19|COLLECT COD|RAIL| slyly on th\n3617|97724|5252|3|32|55095.04|0.00|0.06|N|O|1996-04-20|1996-06-07|1996-05-19|DELIVER IN PERSON|MAIL|uriously against the express accounts. ex\n3617|40706|8219|4|22|36227.40|0.10|0.05|N|O|1996-07-11|1996-05-02|1996-07-25|NONE|REG AIR|uffily even accounts. packages sleep blithe\n3617|136286|1313|5|11|14545.08|0.08|0.05|N|O|1996-07-16|1996-04-23|1996-07-28|COLLECT COD|MAIL|ly quickly even requests. final\n3618|139641|4668|1|38|63864.32|0.08|0.00|N|O|1997-12-22|1998-02-23|1998-01-03|TAKE BACK RETURN|TRUCK|nts haggle fluffily above the regular \n3618|143397|3398|2|48|69138.72|0.04|0.00|N|O|1998-03-12|1998-02-13|1998-03-29|DELIVER IN PERSON|TRUCK|tructions atop the ironi\n3618|62032|4539|3|24|23856.72|0.01|0.04|N|O|1998-01-26|1998-01-15|1998-02-17|TAKE BACK RETURN|AIR|xpress acc\n3618|160418|419|4|26|38438.66|0.01|0.05|N|O|1998-03-23|1998-01-24|1998-04-15|DELIVER IN PERSON|AIR|iously regular deposits cajole ruthless\n3619|95741|5742|1|49|85100.26|0.01|0.08|N|O|1997-01-22|1996-12-21|1997-02-17|TAKE BACK RETURN|MAIL| waters. furiously even deposits \n3619|115622|8134|2|27|44215.74|0.08|0.04|N|O|1996-12-12|1997-01-18|1996-12-18|TAKE BACK RETURN|SHIP|pecial accounts haggle care\n3619|47022|4535|3|46|44574.92|0.08|0.03|N|O|1997-01-31|1997-01-27|1997-02-11|NONE|SHIP|press, expres\n3619|92969|7988|4|18|35315.28|0.04|0.02|N|O|1997-03-18|1996-12-24|1997-03-21|COLLECT COD|AIR|eodolites \n3619|119269|6803|5|38|48953.88|0.05|0.08|N|O|1996-12-08|1997-02-03|1997-01-07|NONE|RAIL|theodolites detect abo\n3619|151951|1952|6|43|86126.85|0.01|0.01|N|O|1997-01-25|1997-01-06|1997-02-07|COLLECT COD|RAIL| bold, even\n3620|58688|1194|1|41|67513.88|0.03|0.08|N|O|1997-03-21|1997-04-20|1997-03-30|COLLECT COD|FOB|t attainments cajole qui\n3620|166574|1607|2|16|26249.12|0.00|0.06|N|O|1997-05-17|1997-05-08|1997-06-03|COLLECT COD|SHIP|s. even, pending in\n3621|16475|6476|1|29|40352.63|0.02|0.06|A|F|1993-08-03|1993-07-08|1993-08-10|DELIVER IN PERSON|FOB|al requests. fl\n3621|92329|4839|2|13|17177.16|0.09|0.04|R|F|1993-08-30|1993-06-30|1993-09-01|NONE|REG AIR|r the unusual packages. brave theodoli\n3621|163518|1067|3|45|71167.95|0.07|0.07|R|F|1993-08-09|1993-06-18|1993-09-05|DELIVER IN PERSON|AIR| doubt about the bold deposits. carefully\n3621|43242|755|4|20|23704.80|0.05|0.04|R|F|1993-05-27|1993-07-04|1993-06-22|TAKE BACK RETURN|SHIP|gular accounts use carefully with\n3622|174754|4755|1|47|85951.25|0.09|0.00|N|O|1996-02-24|1996-02-22|1996-03-12|TAKE BACK RETURN|TRUCK|are careful\n3622|88392|901|2|4|5521.56|0.04|0.04|N|O|1996-02-03|1996-02-19|1996-02-16|TAKE BACK RETURN|TRUCK|lithely brave foxes. furi\n3622|189480|9481|3|46|72196.08|0.07|0.07|N|O|1995-12-18|1996-01-23|1996-01-12|TAKE BACK RETURN|AIR|sits wake. blithe\n3622|176517|6518|4|9|14341.59|0.08|0.05|N|O|1995-12-12|1996-02-09|1995-12-13|TAKE BACK RETURN|SHIP|arefully. furiously regular ideas n\n3623|79831|2339|1|32|57946.56|0.05|0.00|N|O|1997-04-18|1997-03-15|1997-05-09|COLLECT COD|SHIP| courts. furiously regular ideas b\n3623|116203|1226|2|33|40233.60|0.08|0.01|N|O|1997-03-17|1997-02-13|1997-04-02|TAKE BACK RETURN|TRUCK|odolites. blithely spe\n3623|23025|532|3|21|19908.42|0.02|0.02|N|O|1997-01-19|1997-03-18|1997-01-24|NONE|FOB|ress ideas are furio\n3623|164747|9780|4|42|76093.08|0.05|0.06|N|O|1997-01-11|1997-03-24|1997-01-21|COLLECT COD|RAIL|g to the slyly regular packa\n3623|87761|5286|5|30|52462.80|0.10|0.04|N|O|1997-04-04|1997-03-03|1997-05-01|NONE|RAIL| ironic somas sleep fluffily\n3623|185158|195|6|7|8702.05|0.01|0.02|N|O|1997-01-05|1997-03-26|1997-01-26|NONE|TRUCK|aves. slyly special packages cajole. fu\n3623|139856|2370|7|13|24646.05|0.03|0.08|N|O|1997-01-02|1997-02-26|1997-01-26|DELIVER IN PERSON|SHIP|deas. furiously expres\n3648|143090|3091|1|16|18129.44|0.02|0.06|A|F|1993-08-14|1993-08-14|1993-08-15|COLLECT COD|FOB|s nag packages.\n3648|104769|2300|2|30|53212.80|0.00|0.01|R|F|1993-08-31|1993-09-06|1993-09-06|DELIVER IN PERSON|FOB| above the somas boost furious\n3648|45851|5852|3|34|61092.90|0.10|0.00|A|F|1993-08-21|1993-07-25|1993-09-15|DELIVER IN PERSON|FOB| deposits are furiously. careful, \n3648|12026|7029|4|16|15008.32|0.06|0.03|R|F|1993-07-27|1993-08-26|1993-08-24|DELIVER IN PERSON|FOB|uriously stealthy deposits haggle furi\n3648|116962|4496|5|25|49474.00|0.06|0.03|R|F|1993-08-15|1993-08-25|1993-09-09|TAKE BACK RETURN|TRUCK|s requests. silent asymp\n3648|168406|8407|6|14|20641.60|0.08|0.06|R|F|1993-10-02|1993-08-26|1993-10-09|COLLECT COD|AIR|sly pending excuses. carefully i\n3648|194110|4111|7|49|59001.39|0.09|0.03|R|F|1993-06-27|1993-07-27|1993-07-24|TAKE BACK RETURN|FOB|egular instructions. slyly regular pinto\n3649|4730|4731|1|25|40868.25|0.10|0.04|A|F|1994-10-27|1994-08-23|1994-11-05|TAKE BACK RETURN|TRUCK|special re\n3649|88968|3985|2|23|45010.08|0.08|0.00|R|F|1994-09-26|1994-10-01|1994-09-28|NONE|REG AIR|rs promise blithe\n3649|69238|4251|3|14|16901.22|0.02|0.04|A|F|1994-09-19|1994-08-17|1994-10-12|DELIVER IN PERSON|TRUCK|ithely bold accounts wake \n3649|75499|3021|4|40|58979.60|0.00|0.08|R|F|1994-07-20|1994-08-30|1994-08-14|TAKE BACK RETURN|RAIL|luffy somas sleep quickly-- ironic de\n3649|99669|9670|5|24|40047.84|0.05|0.03|A|F|1994-07-07|1994-08-20|1994-07-27|TAKE BACK RETURN|FOB|c accounts. quickly final theodo\n3649|121063|1064|6|3|3252.18|0.10|0.04|A|F|1994-07-17|1994-08-10|1994-08-03|NONE|FOB|lly bold requests nag; \n3650|135535|8049|1|30|47115.90|0.10|0.00|A|F|1992-08-26|1992-07-05|1992-09-01|DELIVER IN PERSON|SHIP|ckly special platelets. furiously sil\n3650|127935|7936|2|43|84405.99|0.05|0.05|A|F|1992-09-07|1992-08-12|1992-09-10|COLLECT COD|TRUCK|gside of the quick\n3650|1634|9135|3|1|1535.63|0.04|0.06|A|F|1992-06-23|1992-07-18|1992-07-08|NONE|REG AIR|re about the pinto \n3650|62057|4564|4|31|31590.55|0.10|0.08|R|F|1992-06-15|1992-07-01|1992-07-15|DELIVER IN PERSON|RAIL| against the ironic accounts cajol\n3650|186345|6346|5|19|27195.46|0.05|0.04|R|F|1992-08-29|1992-08-09|1992-09-21|DELIVER IN PERSON|AIR|y even forges. fluffily furious accounts\n3650|93525|1053|6|27|41000.04|0.07|0.08|A|F|1992-07-03|1992-07-23|1992-07-13|COLLECT COD|MAIL|ular requests snooze fluffily regular pi\n3650|69009|4022|7|43|42054.00|0.10|0.07|A|F|1992-06-25|1992-07-09|1992-07-22|DELIVER IN PERSON|RAIL|structions use caref\n3651|18097|5601|1|20|20301.80|0.01|0.04|N|O|1998-06-10|1998-06-06|1998-06-23|NONE|SHIP|tect quickly among the r\n3651|154336|1882|2|24|33367.92|0.09|0.04|N|O|1998-06-22|1998-07-17|1998-07-10|DELIVER IN PERSON|RAIL|excuses haggle according to th\n3651|112460|7483|3|41|60370.86|0.00|0.05|N|O|1998-05-10|1998-07-09|1998-05-13|NONE|RAIL|blithely. furiously \n3651|109126|4147|4|27|30648.24|0.05|0.03|N|O|1998-05-03|1998-06-30|1998-05-05|DELIVER IN PERSON|RAIL| sleep blithely furiously do\n3652|179779|7331|1|24|44610.48|0.05|0.03|N|O|1997-06-07|1997-04-07|1997-06-12|COLLECT COD|MAIL|the final p\n3652|136489|6490|2|37|56442.76|0.02|0.05|N|O|1997-05-11|1997-04-06|1997-06-05|COLLECT COD|MAIL|osits haggle carefu\n3652|162640|189|3|39|66402.96|0.01|0.02|N|O|1997-03-10|1997-04-03|1997-03-21|NONE|REG AIR|y express instructions. un\n3652|79671|4686|4|1|1650.67|0.01|0.04|N|O|1997-04-20|1997-05-03|1997-05-18|DELIVER IN PERSON|SHIP| bold dependencies sublate. r\n3653|144753|2296|1|38|68314.50|0.08|0.05|A|F|1994-06-26|1994-05-13|1994-07-13|NONE|REG AIR|ainst the \n3653|63021|8034|2|29|28536.58|0.07|0.01|A|F|1994-04-11|1994-06-11|1994-04-29|COLLECT COD|RAIL|ording to the special, final\n3653|180579|3098|3|17|28212.69|0.09|0.03|R|F|1994-06-24|1994-06-02|1994-07-17|DELIVER IN PERSON|RAIL|gle slyly regular\n3653|185220|257|4|9|11746.98|0.10|0.07|R|F|1994-04-03|1994-05-19|1994-04-10|COLLECT COD|FOB|slyly silent account\n3653|187794|313|5|41|77153.39|0.08|0.01|A|F|1994-06-18|1994-05-18|1994-06-20|COLLECT COD|RAIL|onic packages affix sly\n3653|42462|2463|6|9|12640.14|0.05|0.03|A|F|1994-07-21|1994-05-31|1994-08-17|NONE|MAIL|tes: blithely bo\n3653|48442|947|7|2|2780.88|0.06|0.03|R|F|1994-06-02|1994-05-31|1994-06-29|NONE|FOB|n accounts. fina\n3654|164675|9708|1|46|80024.82|0.08|0.05|A|F|1992-06-05|1992-08-19|1992-06-06|DELIVER IN PERSON|FOB|usly regular foxes. furio\n3654|92688|2689|2|29|48739.72|0.07|0.06|A|F|1992-09-11|1992-07-20|1992-10-04|DELIVER IN PERSON|FOB|odolites detect. quickly r\n3654|1676|6677|3|37|58373.79|0.07|0.05|A|F|1992-09-22|1992-07-20|1992-10-19|TAKE BACK RETURN|RAIL|unts doze bravely ab\n3654|167246|7247|4|11|14445.64|0.08|0.00|A|F|1992-07-20|1992-07-30|1992-07-23|TAKE BACK RETURN|SHIP|quickly along the express, ironic req\n3654|93515|3516|5|34|51289.34|0.04|0.00|R|F|1992-07-26|1992-08-26|1992-08-12|TAKE BACK RETURN|REG AIR| the quick\n3654|106597|4128|6|20|32071.80|0.03|0.02|A|F|1992-07-30|1992-07-05|1992-08-05|COLLECT COD|SHIP|s sleep about the slyly \n3654|172225|9777|7|45|58374.90|0.01|0.07|A|F|1992-09-15|1992-07-04|1992-09-20|DELIVER IN PERSON|FOB|sly ironic notornis nag slyly\n3655|183811|1366|1|5|9474.05|0.03|0.04|R|F|1993-01-17|1992-12-31|1993-01-23|DELIVER IN PERSON|TRUCK|riously bold pinto be\n3655|96652|1671|2|1|1648.65|0.10|0.06|R|F|1992-10-24|1992-12-18|1992-11-07|DELIVER IN PERSON|AIR|arefully slow pinto beans are\n3655|29968|2471|3|35|66428.60|0.01|0.04|R|F|1992-12-20|1992-11-16|1993-01-15|TAKE BACK RETURN|MAIL|blithely even accounts! furiously regular\n3655|71347|1348|4|35|46141.90|0.04|0.07|R|F|1992-10-17|1992-12-23|1992-10-28|COLLECT COD|MAIL|ng foxes cajole fluffily slyly final fo\n3680|176451|1486|1|48|73317.60|0.00|0.06|R|F|1993-01-16|1993-01-23|1993-01-19|COLLECT COD|FOB|packages. quickly fluff\n3680|4228|6729|2|41|46421.02|0.00|0.04|A|F|1993-01-06|1993-03-02|1993-01-08|NONE|FOB|iously ironic platelets in\n3680|55001|7507|3|33|31548.00|0.09|0.08|R|F|1993-03-16|1993-02-19|1993-04-05|NONE|FOB|ts. ironic, fina\n3681|105232|7743|1|35|43303.05|0.03|0.08|R|F|1992-07-31|1992-05-18|1992-08-07|COLLECT COD|FOB|lyly special pinto \n3682|60637|3144|1|6|9585.78|0.07|0.02|N|O|1997-05-06|1997-04-04|1997-05-11|NONE|AIR|ronic deposits wake slyly. ca\n3682|115598|5599|2|18|29044.62|0.06|0.06|N|O|1997-04-30|1997-03-21|1997-05-10|NONE|FOB|regular dependencies\n3682|46639|1648|3|17|26955.71|0.03|0.05|N|O|1997-02-12|1997-04-04|1997-02-22|COLLECT COD|FOB|, ironic packages wake a\n3682|56495|9001|4|30|43544.70|0.09|0.05|N|O|1997-04-16|1997-04-16|1997-04-29|NONE|MAIL|he requests cajole quickly pending package\n3683|100842|3353|1|35|64499.40|0.05|0.03|A|F|1993-05-31|1993-04-17|1993-06-14|NONE|SHIP| the furiously expr\n3683|48689|6202|2|41|67144.88|0.01|0.06|A|F|1993-03-26|1993-05-06|1993-04-09|NONE|TRUCK|ress instructions. slyly express a\n3683|99314|4333|3|23|30206.13|0.00|0.08|R|F|1993-07-02|1993-05-16|1993-07-30|NONE|TRUCK|xpress accounts sleep slyly re\n3684|125106|5107|1|48|54292.80|0.04|0.06|A|F|1993-08-20|1993-09-02|1993-09-10|DELIVER IN PERSON|REG AIR|its boost alongside\n3684|45384|5385|2|6|7976.28|0.06|0.08|R|F|1993-08-09|1993-10-05|1993-09-06|DELIVER IN PERSON|FOB|he silent requests. packages sleep fu\n3684|162500|49|3|19|29687.50|0.04|0.02|A|F|1993-10-19|1993-08-25|1993-11-02|COLLECT COD|FOB|e slyly carefully pending foxes. d\n3684|134811|2351|4|13|23995.53|0.02|0.05|A|F|1993-07-23|1993-09-16|1993-08-06|NONE|TRUCK|ing, unusual pinto beans! thinly p\n3685|46199|8704|1|37|42372.03|0.02|0.03|R|F|1992-03-11|1992-04-09|1992-04-05|DELIVER IN PERSON|TRUCK|ress attai\n3685|57413|9919|2|7|9592.87|0.05|0.00|R|F|1992-05-16|1992-02-23|1992-05-17|DELIVER IN PERSON|FOB|sits. special asymptotes about the r\n3685|133706|3707|3|38|66108.60|0.08|0.03|A|F|1992-05-17|1992-03-16|1992-06-06|TAKE BACK RETURN|TRUCK|thely unusual pack\n3685|191626|6665|4|39|66987.18|0.10|0.05|R|F|1992-02-19|1992-04-06|1992-03-02|COLLECT COD|FOB|ic courts nag carefully after the \n3685|55782|5783|5|37|64297.86|0.00|0.01|A|F|1992-03-02|1992-04-10|1992-03-04|NONE|FOB|. carefully sly requests are regular, regu\n3686|121557|9094|1|7|11049.85|0.02|0.04|N|O|1998-07-15|1998-08-22|1998-07-30|DELIVER IN PERSON|TRUCK| furiously unusual accou\n3686|199146|1666|2|38|47315.32|0.06|0.03|N|O|1998-09-04|1998-08-11|1998-09-19|DELIVER IN PERSON|AIR|y silent foxes! carefully ruthless cour\n3686|44727|4728|3|31|51823.32|0.10|0.06|N|O|1998-09-09|1998-08-28|1998-10-09|COLLECT COD|MAIL|gle across the courts. furiously regu\n3686|116078|8590|4|7|7658.49|0.10|0.01|N|O|1998-07-16|1998-09-02|1998-07-22|NONE|FOB|ake carefully carefully q\n3687|144005|1548|1|32|33568.00|0.03|0.06|R|F|1993-05-07|1993-04-05|1993-05-25|DELIVER IN PERSON|AIR|deas cajole fo\n3687|80501|3010|2|2|2963.00|0.00|0.08|R|F|1993-02-23|1993-03-25|1993-03-11|NONE|TRUCK| express requests. slyly regular depend\n3687|173664|6182|3|10|17376.60|0.01|0.02|A|F|1993-02-11|1993-03-22|1993-03-09|NONE|FOB|ing pinto beans\n3687|161643|6676|4|19|32388.16|0.02|0.05|A|F|1993-05-14|1993-04-24|1993-06-01|DELIVER IN PERSON|MAIL|ly final asymptotes according to t\n3687|118039|5573|5|31|32767.93|0.07|0.08|A|F|1993-05-28|1993-03-20|1993-06-05|DELIVER IN PERSON|FOB|foxes cajole quickly about the furiously f\n3712|140013|5042|1|27|28431.27|0.01|0.05|R|F|1992-02-01|1992-02-26|1992-03-02|TAKE BACK RETURN|SHIP|ctions. even accounts haggle alongside \n3712|184915|2470|2|13|25998.83|0.03|0.03|R|F|1992-04-30|1992-02-11|1992-05-30|DELIVER IN PERSON|FOB|s around the furiously ironic account\n3712|63923|8936|3|44|83024.48|0.01|0.01|A|F|1992-03-26|1992-02-19|1992-04-18|TAKE BACK RETURN|FOB|ously permanently regular req\n3712|147022|4565|4|38|40622.76|0.01|0.06|A|F|1992-01-15|1992-03-24|1992-01-27|COLLECT COD|RAIL|s nag carefully-- even, reg\n3713|111310|3822|1|41|54173.71|0.07|0.08|N|O|1998-05-11|1998-07-17|1998-05-22|COLLECT COD|RAIL|eposits wake blithely fina\n3713|176914|9432|2|19|37827.29|0.04|0.04|N|O|1998-06-25|1998-07-24|1998-07-08|DELIVER IN PERSON|AIR|tructions serve blithely around the furi\n3713|179662|9663|3|19|33091.54|0.03|0.02|N|O|1998-05-19|1998-07-06|1998-06-09|DELIVER IN PERSON|REG AIR|quests cajole careful\n3713|168912|8913|4|45|89140.95|0.06|0.04|N|O|1998-06-15|1998-07-30|1998-07-14|DELIVER IN PERSON|MAIL|al pinto beans affix after the slyly \n3713|89165|4182|5|46|53091.36|0.10|0.04|N|O|1998-08-22|1998-06-27|1998-08-31|NONE|MAIL|totes. carefully special theodolites s\n3713|181492|4011|6|29|45631.21|0.09|0.03|N|O|1998-08-04|1998-06-13|1998-08-21|NONE|RAIL|the regular dugouts wake furiously sil\n3713|129891|9892|7|14|26892.46|0.04|0.00|N|O|1998-07-19|1998-07-02|1998-07-28|DELIVER IN PERSON|SHIP|eposits impress according\n3714|68433|3446|1|13|18218.59|0.07|0.03|N|O|1998-06-26|1998-06-17|1998-07-07|TAKE BACK RETURN|REG AIR| the furiously final\n3714|145675|8190|2|14|24089.38|0.02|0.05|N|O|1998-05-30|1998-06-30|1998-05-31|DELIVER IN PERSON|RAIL|ending ideas. thinly unusual theodo\n3714|158877|8878|3|16|30973.92|0.00|0.02|N|O|1998-05-25|1998-07-07|1998-06-17|TAKE BACK RETURN|AIR|ccounts cajole fu\n3714|29287|4292|4|44|53516.32|0.04|0.02|N|O|1998-07-18|1998-07-10|1998-07-22|DELIVER IN PERSON|AIR|s. quickly ironic dugouts sublat\n3715|96625|4153|1|13|21081.06|0.00|0.03|N|O|1996-05-11|1996-04-25|1996-06-09|TAKE BACK RETURN|SHIP|e quickly ironic\n3715|168955|3988|2|16|32383.20|0.01|0.06|N|O|1996-06-28|1996-04-22|1996-06-30|TAKE BACK RETURN|AIR|usly regular pearls haggle final packages\n3715|11629|1630|3|37|57002.94|0.05|0.02|N|O|1996-05-03|1996-04-30|1996-05-17|NONE|SHIP|ut the carefully expr\n3716|31701|4205|1|10|16327.00|0.09|0.04|N|O|1997-12-02|1997-11-09|1997-12-14|TAKE BACK RETURN|SHIP|ts. quickly sly ideas slee\n3716|193081|3082|2|39|45789.12|0.02|0.08|N|O|1997-11-27|1997-10-23|1997-12-24|COLLECT COD|REG AIR|even deposits.\n3716|106882|6883|3|42|79332.96|0.02|0.08|N|O|1997-12-03|1997-10-12|1997-12-15|NONE|TRUCK| of the pend\n3716|164383|1932|4|19|27500.22|0.05|0.08|N|O|1997-09-25|1997-10-18|1997-10-12|NONE|TRUCK|arefully unusual accounts. flu\n3716|181638|4157|5|25|42990.75|0.06|0.05|N|O|1997-11-23|1997-10-24|1997-11-24|COLLECT COD|REG AIR|fully unusual accounts. carefu\n3717|152273|7304|1|45|59637.15|0.07|0.04|N|O|1998-08-09|1998-08-18|1998-08-14|TAKE BACK RETURN|TRUCK|ests wake whithout the blithely final pl\n3717|52014|9530|2|3|2898.03|0.01|0.07|N|O|1998-06-09|1998-07-31|1998-06-14|NONE|REG AIR|nside the regular packages sleep\n3717|195221|5222|3|45|59229.90|0.05|0.08|N|O|1998-09-19|1998-07-22|1998-09-28|DELIVER IN PERSON|MAIL|s the blithely unu\n3717|68318|3331|4|5|6431.55|0.06|0.03|N|O|1998-09-02|1998-08-20|1998-09-26|TAKE BACK RETURN|AIR|quickly among \n3717|15272|5273|5|7|8310.89|0.09|0.02|N|O|1998-09-08|1998-07-18|1998-09-10|DELIVER IN PERSON|RAIL| after the packa\n3717|63587|8600|6|38|58922.04|0.01|0.07|N|O|1998-07-10|1998-07-08|1998-07-29|COLLECT COD|RAIL|ly about the car\n3717|105267|5268|7|28|35623.28|0.03|0.01|N|O|1998-07-25|1998-08-12|1998-08-16|COLLECT COD|RAIL|ts sleep q\n3718|20775|5780|1|40|67830.80|0.01|0.04|N|O|1996-11-20|1996-12-17|1996-12-03|DELIVER IN PERSON|MAIL|out the express deposits\n3718|162663|212|2|16|27610.56|0.02|0.06|N|O|1996-11-11|1996-12-25|1996-11-12|COLLECT COD|TRUCK|slyly even accounts. blithely special acco\n3718|69553|7072|3|8|12180.40|0.05|0.03|N|O|1996-12-06|1996-12-06|1996-12-15|TAKE BACK RETURN|AIR| the even deposits sleep carefully b\n3719|21430|8937|1|35|47300.05|0.06|0.08|N|O|1997-06-11|1997-04-03|1997-06-15|TAKE BACK RETURN|TRUCK|ly foxes. pending braids haggle furio\n3719|173998|6516|2|2|4143.98|0.02|0.08|N|O|1997-02-17|1997-04-25|1997-03-03|NONE|REG AIR|ccounts boost carefu\n3719|181502|6539|3|12|19002.00|0.05|0.06|N|O|1997-06-10|1997-05-04|1997-07-09|TAKE BACK RETURN|REG AIR|grate according to the \n3719|89266|6791|4|13|16318.38|0.02|0.00|N|O|1997-05-03|1997-04-16|1997-05-27|TAKE BACK RETURN|SHIP|iously. regular dep\n3719|77643|151|5|19|30792.16|0.06|0.08|N|O|1997-05-22|1997-03-20|1997-06-12|COLLECT COD|TRUCK|he regular ideas integrate acros\n3719|141336|6365|6|43|59225.19|0.03|0.08|N|O|1997-05-08|1997-04-15|1997-06-06|COLLECT COD|RAIL|the furiously special pinto bean\n3719|18433|8434|7|16|21622.88|0.10|0.01|N|O|1997-03-02|1997-03-18|1997-03-28|TAKE BACK RETURN|RAIL| express asymptotes. ir\n3744|194756|9795|1|30|55522.50|0.05|0.06|A|F|1992-05-07|1992-02-12|1992-05-17|TAKE BACK RETURN|FOB|nts among \n3745|136294|1321|1|18|23945.22|0.01|0.05|A|F|1993-10-17|1993-11-16|1993-11-13|DELIVER IN PERSON|SHIP| slyly bold pinto beans according to \n3746|164012|4013|1|37|39812.37|0.07|0.00|A|F|1994-12-29|1994-10-25|1995-01-03|COLLECT COD|FOB|e of the careful\n3746|143595|8624|2|28|45880.52|0.06|0.08|R|F|1994-09-20|1994-10-21|1994-09-27|DELIVER IN PERSON|FOB|s after the even, special requests\n3746|187888|7889|3|3|5927.64|0.10|0.01|R|F|1994-11-03|1994-12-10|1994-11-12|NONE|MAIL| the silent ideas cajole carefully \n3746|27486|2491|4|11|15548.28|0.00|0.05|R|F|1994-10-02|1994-11-19|1994-10-10|COLLECT COD|SHIP| ironic theodolites are among th\n3747|140559|8102|1|42|67181.10|0.05|0.05|N|O|1996-11-10|1996-10-19|1996-11-19|TAKE BACK RETURN|REG AIR|y. blithely fina\n3747|169914|9915|2|33|65469.03|0.01|0.03|N|O|1996-10-14|1996-11-12|1996-11-11|NONE|REG AIR| regular p\n3747|138552|8553|3|30|47716.50|0.00|0.07|N|O|1996-12-16|1996-11-15|1996-12-17|NONE|RAIL|! furiously f\n3747|32970|480|4|21|39962.37|0.00|0.06|N|O|1996-11-18|1996-09-23|1996-11-26|TAKE BACK RETURN|AIR|ithely bold orbits mold furiously blit\n3747|125802|827|5|32|58489.60|0.08|0.05|N|O|1996-09-10|1996-11-04|1996-10-10|DELIVER IN PERSON|MAIL|quests shall h\n3747|153991|3992|6|14|28629.86|0.08|0.07|N|O|1996-11-03|1996-10-29|1996-11-06|TAKE BACK RETURN|AIR|packages cajole carefu\n3747|117993|505|7|23|46252.77|0.00|0.04|N|O|1996-11-08|1996-11-10|1996-12-03|NONE|REG AIR|kages are ironic\n3748|103539|6050|1|12|18510.36|0.06|0.01|N|O|1998-04-17|1998-04-15|1998-05-12|NONE|AIR|old reques\n3748|164717|7234|2|24|42761.04|0.08|0.04|N|O|1998-06-07|1998-05-02|1998-06-21|DELIVER IN PERSON|TRUCK|al deposits. blithely\n3748|196085|3643|3|19|22440.52|0.05|0.01|N|O|1998-04-23|1998-05-17|1998-05-23|COLLECT COD|RAIL|pinto beans run carefully quic\n3748|186693|1730|4|5|8898.45|0.00|0.07|N|O|1998-06-29|1998-05-06|1998-07-12|DELIVER IN PERSON|MAIL| regular accounts sleep quickly-- furious\n3748|146699|9214|5|21|36659.49|0.07|0.08|N|O|1998-03-30|1998-04-07|1998-04-05|TAKE BACK RETURN|MAIL|fix carefully furiously express ideas. furi\n3749|172888|5406|1|11|21569.68|0.07|0.05|N|O|1995-06-25|1995-05-23|1995-07-10|TAKE BACK RETURN|RAIL|egular requests along the \n3749|128102|3127|2|9|10170.90|0.08|0.05|A|F|1995-04-23|1995-04-18|1995-04-26|NONE|REG AIR|uses cajole blithely pla\n3749|198854|3893|3|31|60538.35|0.00|0.05|N|F|1995-06-11|1995-05-20|1995-06-27|COLLECT COD|REG AIR|s. foxes sleep slyly unusual grouc\n3749|130302|5329|4|7|9326.10|0.07|0.06|A|F|1995-03-31|1995-04-05|1995-04-11|NONE|TRUCK|he slyly ironic packages\n3749|182309|4828|5|14|19478.20|0.02|0.00|N|F|1995-06-11|1995-05-19|1995-07-11|DELIVER IN PERSON|SHIP|press instruc\n3749|53716|1232|6|10|16697.10|0.10|0.03|N|O|1995-06-24|1995-05-24|1995-07-18|COLLECT COD|SHIP|essly. regular pi\n3750|133299|5813|1|37|49294.73|0.04|0.03|N|O|1995-07-08|1995-07-28|1995-07-28|DELIVER IN PERSON|REG AIR|usly busy account\n3750|151799|1800|2|33|61076.07|0.05|0.03|N|O|1995-06-27|1995-06-20|1995-07-03|TAKE BACK RETURN|REG AIR|theodolites haggle. slyly pendin\n3750|79370|1878|3|20|26987.40|0.09|0.05|N|F|1995-06-17|1995-06-06|1995-06-28|TAKE BACK RETURN|REG AIR|ss, ironic requests! fur\n3750|165817|3366|4|33|62132.73|0.04|0.03|N|F|1995-06-15|1995-06-04|1995-06-29|COLLECT COD|RAIL|ep blithely according to the flu\n3750|82651|5160|5|1|1633.65|0.05|0.01|N|O|1995-07-24|1995-06-25|1995-08-21|DELIVER IN PERSON|REG AIR|l dolphins against the slyly\n3750|112383|4895|6|47|65582.86|0.01|0.08|R|F|1995-05-11|1995-06-13|1995-06-02|TAKE BACK RETURN|FOB|slowly regular accounts. blithely ev\n3751|171747|4265|1|37|67293.38|0.00|0.04|R|F|1994-04-30|1994-05-30|1994-05-30|NONE|REG AIR|ly express courts \n3751|140684|3199|2|32|55189.76|0.03|0.05|R|F|1994-05-05|1994-07-02|1994-06-02|COLLECT COD|MAIL|rthogs could have to slee\n3751|64236|9249|3|45|54010.35|0.08|0.06|R|F|1994-05-27|1994-06-19|1994-06-14|NONE|RAIL|according to \n3751|13891|1395|4|39|70390.71|0.07|0.01|A|F|1994-08-16|1994-07-11|1994-09-12|COLLECT COD|TRUCK|refully according to the iro\n3751|57100|2111|5|12|12685.20|0.02|0.03|A|F|1994-08-09|1994-06-30|1994-08-12|TAKE BACK RETURN|TRUCK|accounts wake furious\n3751|75535|550|6|39|58910.67|0.02|0.08|R|F|1994-08-01|1994-06-01|1994-08-26|COLLECT COD|SHIP|to beans. pending, express packages c\n3776|2128|9629|1|39|40174.68|0.05|0.01|R|F|1993-01-03|1993-02-05|1993-01-08|COLLECT COD|FOB|yly blithely pending packages\n3776|158157|3188|2|14|17012.10|0.06|0.08|R|F|1992-12-30|1993-02-12|1993-01-27|DELIVER IN PERSON|RAIL|y special ideas. express packages pr\n3776|140995|3510|3|49|99763.51|0.01|0.08|R|F|1992-12-03|1993-02-16|1992-12-28|TAKE BACK RETURN|RAIL|equests. final, thin grouches \n3776|91553|9081|4|49|75682.95|0.08|0.05|A|F|1993-02-11|1993-01-06|1993-02-27|COLLECT COD|MAIL|es: careful warthogs haggle fluffi\n3777|99495|7023|1|11|16439.39|0.02|0.03|A|F|1994-04-09|1994-06-05|1994-04-14|NONE|FOB|ld ideas. even theodolites\n3777|7436|4937|2|10|13434.30|0.03|0.01|R|F|1994-05-22|1994-05-29|1994-06-13|COLLECT COD|RAIL|le. ironic depths a\n3777|165409|5410|3|18|26539.20|0.10|0.06|R|F|1994-05-04|1994-05-23|1994-05-22|COLLECT COD|REG AIR|eful packages use slyly: even deposits \n3777|17130|7131|4|35|36649.55|0.10|0.04|A|F|1994-05-25|1994-05-26|1994-06-13|COLLECT COD|AIR|s. carefully express asymptotes accordi\n3777|97412|9922|5|14|19731.74|0.04|0.05|R|F|1994-05-06|1994-06-24|1994-05-31|NONE|TRUCK|ording to the iro\n3778|56669|1680|1|21|34138.86|0.01|0.06|R|F|1993-05-27|1993-07-10|1993-06-03|COLLECT COD|REG AIR|ts. blithely special theodoli\n3778|28589|8590|2|32|48562.56|0.09|0.00|A|F|1993-06-22|1993-08-18|1993-07-03|TAKE BACK RETURN|MAIL|tes affix carefully above the \n3778|93436|5946|3|41|58606.63|0.05|0.00|R|F|1993-06-21|1993-07-27|1993-07-15|COLLECT COD|FOB|e the furiously ironi\n3778|168841|6390|4|28|53475.52|0.03|0.05|R|F|1993-08-18|1993-07-10|1993-09-06|TAKE BACK RETURN|REG AIR|y silent orbits print carefully against \n3778|97021|4549|5|28|28504.56|0.01|0.06|R|F|1993-09-02|1993-08-08|1993-10-02|DELIVER IN PERSON|FOB|r deposits. theodol\n3778|19451|4454|6|26|35631.70|0.00|0.01|A|F|1993-09-24|1993-07-06|1993-10-22|NONE|TRUCK| against the fluffily\n3778|104793|4794|7|49|88091.71|0.02|0.04|A|F|1993-06-13|1993-08-08|1993-07-04|DELIVER IN PERSON|MAIL|ans. furiously \n3779|45738|3251|1|28|47144.44|0.04|0.05|N|O|1997-05-06|1997-04-01|1997-05-18|TAKE BACK RETURN|AIR|s. close requests sleep\n3779|109004|1515|2|5|5065.00|0.07|0.03|N|O|1997-01-07|1997-03-26|1997-02-05|DELIVER IN PERSON|AIR|heodolites. slyly regular a\n3780|126173|6174|1|25|29979.25|0.08|0.04|N|O|1996-06-27|1996-07-02|1996-07-22|NONE|AIR|l, unusual \n3780|189496|7051|2|40|63419.60|0.10|0.04|N|O|1996-06-06|1996-05-29|1996-07-01|COLLECT COD|SHIP|gular deposits-- furiously regular \n3781|13185|3186|1|48|52712.64|0.02|0.06|N|O|1996-08-22|1996-08-13|1996-09-15|NONE|REG AIR|equests may cajole careful\n3781|187496|7497|2|39|61756.11|0.10|0.00|N|O|1996-08-20|1996-08-16|1996-09-01|DELIVER IN PERSON|REG AIR|unts are carefully. ir\n3781|29895|9896|3|17|31023.13|0.01|0.03|N|O|1996-06-23|1996-09-04|1996-07-19|TAKE BACK RETURN|REG AIR|. theodolite\n3781|30810|5817|4|15|26112.15|0.05|0.00|N|O|1996-08-23|1996-08-08|1996-09-06|TAKE BACK RETURN|AIR| carefully blithe\n3781|15001|2505|5|23|21068.00|0.09|0.08|N|O|1996-09-05|1996-08-18|1996-09-27|DELIVER IN PERSON|SHIP|pendencies are b\n3782|26844|4351|1|29|51354.36|0.01|0.07|N|O|1996-09-17|1996-10-03|1996-10-07|DELIVER IN PERSON|REG AIR|quickly unusual pinto beans. carefully fina\n3782|152897|5413|2|10|19498.90|0.03|0.05|N|O|1996-09-07|1996-11-19|1996-10-04|COLLECT COD|FOB|ven pinto b\n3782|135201|5202|3|30|37086.00|0.06|0.06|N|O|1996-12-19|1996-10-31|1997-01-14|TAKE BACK RETURN|MAIL|slyly even pinto beans hag\n3782|116305|3839|4|34|44924.20|0.02|0.06|N|O|1996-11-07|1996-10-22|1996-11-19|DELIVER IN PERSON|MAIL|gage after the even\n3782|129239|6776|5|40|50729.20|0.09|0.04|N|O|1996-12-16|1996-11-22|1997-01-01|COLLECT COD|AIR|s instructions. regular accou\n3783|166578|1611|1|36|59204.52|0.04|0.08|R|F|1993-12-17|1994-02-26|1994-01-03|DELIVER IN PERSON|SHIP|ites haggle among the carefully unusu\n3783|72856|5364|2|36|65838.60|0.02|0.02|R|F|1994-03-02|1994-02-09|1994-03-15|COLLECT COD|TRUCK|egular accounts\n3783|84967|7476|3|50|97598.00|0.04|0.01|R|F|1994-03-14|1994-01-09|1994-04-10|DELIVER IN PERSON|FOB|he furiously regular deposits. \n3783|26279|1284|4|37|44594.99|0.10|0.05|R|F|1993-12-09|1994-02-17|1993-12-30|COLLECT COD|REG AIR|ing to the ideas. regular accounts de\n3808|42716|5221|1|28|46443.88|0.02|0.01|R|F|1994-05-27|1994-06-18|1994-06-22|TAKE BACK RETURN|FOB|lly final accounts alo\n3808|126319|1344|2|47|63229.57|0.04|0.08|R|F|1994-06-12|1994-06-03|1994-07-02|COLLECT COD|TRUCK|fully for the quickly final deposits: flu\n3808|30291|292|3|45|54958.05|0.00|0.03|R|F|1994-07-03|1994-05-29|1994-07-14|TAKE BACK RETURN|REG AIR| carefully special\n3808|99468|9469|4|34|49893.64|0.07|0.04|R|F|1994-08-13|1994-07-22|1994-08-31|DELIVER IN PERSON|FOB| pearls will have to \n3808|154755|2301|5|29|52482.75|0.08|0.03|A|F|1994-06-22|1994-05-26|1994-07-06|TAKE BACK RETURN|TRUCK| deposits across the pac\n3808|167127|2160|6|44|52541.28|0.06|0.06|A|F|1994-06-07|1994-06-04|1994-06-25|NONE|REG AIR|the blithely regular foxes. even, final \n3809|190322|2842|1|17|24009.44|0.10|0.04|N|O|1996-08-14|1996-07-05|1996-09-04|DELIVER IN PERSON|FOB|es detect furiously sil\n3809|132963|2964|2|32|63870.72|0.01|0.02|N|O|1996-07-03|1996-06-01|1996-07-25|COLLECT COD|SHIP|xcuses would boost against the fluffily eve\n3809|104789|4790|3|46|82513.88|0.10|0.06|N|O|1996-08-20|1996-06-01|1996-08-24|TAKE BACK RETURN|TRUCK|l asymptotes. special \n3809|177249|7250|4|43|57028.32|0.00|0.04|N|O|1996-05-06|1996-06-22|1996-06-05|TAKE BACK RETURN|TRUCK|yly ironic decoys; regular, iron\n3810|183458|1013|1|49|75531.05|0.05|0.01|R|F|1992-11-27|1992-10-30|1992-12-16|COLLECT COD|AIR|cajole. fur\n3810|168724|1241|2|18|32268.96|0.01|0.04|A|F|1992-11-28|1992-11-15|1992-12-27|DELIVER IN PERSON|SHIP|s. furiously careful deposi\n3810|136788|9302|3|41|74815.98|0.08|0.08|A|F|1992-10-26|1992-10-27|1992-11-05|COLLECT COD|SHIP|l requests boost slyly along the slyl\n3810|181036|3555|4|11|12287.33|0.06|0.04|A|F|1992-12-18|1992-12-11|1993-01-15|DELIVER IN PERSON|MAIL| the pending pinto beans. expr\n3811|163383|5900|1|24|34713.12|0.04|0.02|N|O|1998-07-13|1998-05-16|1998-08-12|TAKE BACK RETURN|TRUCK|deposits. slyly regular accounts cajo\n3811|165958|8475|2|2|4047.90|0.01|0.08|N|O|1998-06-16|1998-06-16|1998-06-23|NONE|MAIL|slyly fluff\n3811|42469|7478|3|19|26817.74|0.02|0.06|N|O|1998-07-20|1998-06-14|1998-07-29|NONE|MAIL|s boost blithely furiou\n3811|170416|2934|4|50|74320.50|0.08|0.03|N|O|1998-07-28|1998-07-06|1998-08-16|COLLECT COD|FOB|ts are slyly fluffy ideas. furiou\n3811|181104|3623|5|23|27257.30|0.00|0.04|N|O|1998-08-13|1998-07-09|1998-08-29|COLLECT COD|AIR|nstructions sleep quickly. slyly final \n3811|1635|6636|6|35|53782.05|0.04|0.07|N|O|1998-04-17|1998-06-30|1998-04-25|NONE|REG AIR|yly final dolphins? quickly ironic frets\n3812|144641|2184|1|33|55626.12|0.00|0.05|N|O|1996-10-10|1996-10-05|1996-10-15|TAKE BACK RETURN|MAIL|posits engage. ironic, regular p\n3812|172434|7469|2|33|49712.19|0.06|0.03|N|O|1996-10-05|1996-10-13|1996-10-22|TAKE BACK RETURN|MAIL|inal excuses d\n3813|175506|5507|1|37|58515.50|0.05|0.04|N|O|1998-10-13|1998-09-19|1998-10-28|NONE|REG AIR|ravely special packages haggle p\n3813|122720|7745|2|39|67966.08|0.05|0.00|N|O|1998-08-30|1998-08-12|1998-09-29|COLLECT COD|FOB|y ideas. final ideas about the sp\n3814|130445|7985|1|7|10328.08|0.02|0.02|R|F|1995-05-01|1995-05-09|1995-05-28|DELIVER IN PERSON|REG AIR|es sleep furiou\n3814|172629|5147|2|14|23822.68|0.01|0.00|R|F|1995-03-17|1995-05-10|1995-04-16|DELIVER IN PERSON|AIR|sits along the final, ironic deposit\n3814|167125|9642|3|36|42916.32|0.06|0.02|N|O|1995-06-19|1995-04-18|1995-06-28|COLLECT COD|SHIP|beans cajole quickly sl\n3814|65004|5005|4|20|19380.00|0.04|0.07|R|F|1995-02-23|1995-03-26|1995-03-04|DELIVER IN PERSON|SHIP|. doggedly ironic deposits will have to wa\n3814|106211|1232|5|15|18258.15|0.03|0.04|N|O|1995-06-23|1995-03-25|1995-07-09|COLLECT COD|SHIP| carefully final deposits haggle slyly\n3814|82129|7146|6|47|52222.64|0.09|0.05|A|F|1995-04-16|1995-04-03|1995-05-14|DELIVER IN PERSON|AIR|nusual requests. bli\n3814|131307|3821|7|12|16059.60|0.10|0.01|R|F|1995-03-18|1995-04-16|1995-03-20|TAKE BACK RETURN|REG AIR|ages cajole. packages haggle. final\n3815|76048|8556|1|3|3072.12|0.07|0.00|N|O|1997-11-16|1997-11-15|1997-11-30|NONE|FOB|egular, express ideas. ironic, final dep\n3815|129840|2353|2|11|20568.24|0.02|0.04|N|O|1997-11-01|1997-11-05|1997-11-27|COLLECT COD|TRUCK|sleep blithe\n3840|186402|8921|1|45|66978.00|0.02|0.08|N|O|1998-10-31|1998-09-19|1998-11-30|DELIVER IN PERSON|TRUCK|o beans are. carefully final courts x\n3840|45522|531|2|12|17610.24|0.04|0.07|N|O|1998-10-02|1998-08-19|1998-10-20|TAKE BACK RETURN|RAIL|xpress pinto beans. accounts a\n3840|72943|2944|3|45|86217.30|0.02|0.05|N|O|1998-10-12|1998-10-12|1998-10-28|TAKE BACK RETURN|FOB|onic, even packages are. pe\n3840|147247|7248|4|41|53063.84|0.07|0.02|N|O|1998-07-21|1998-10-08|1998-08-01|TAKE BACK RETURN|MAIL| nag slyly? slyly pending accounts \n3840|172923|5441|5|7|13971.44|0.09|0.08|N|O|1998-09-17|1998-09-20|1998-10-14|DELIVER IN PERSON|MAIL|. furiously final gifts sleep carefully pin\n3840|106112|6113|6|33|36897.63|0.10|0.02|N|O|1998-07-29|1998-10-06|1998-08-04|DELIVER IN PERSON|SHIP|hely silent deposits w\n3841|156889|9405|1|1|1945.88|0.06|0.03|A|F|1994-10-10|1994-11-12|1994-10-21|DELIVER IN PERSON|AIR| boost even re\n3841|20882|5887|2|31|55889.28|0.09|0.03|A|F|1995-01-24|1994-11-25|1995-02-20|TAKE BACK RETURN|SHIP|n theodolites shall promise carefully. qui\n3841|151821|4337|3|40|74912.80|0.06|0.02|A|F|1995-02-02|1994-11-30|1995-02-14|TAKE BACK RETURN|MAIL|its. quickly regular ideas nag carefully\n3841|49764|9765|4|9|15423.84|0.10|0.07|A|F|1994-11-21|1994-12-26|1994-11-26|NONE|FOB|s according to the courts shall nag s\n3841|175794|5795|5|3|5609.37|0.04|0.02|R|F|1994-10-24|1994-12-07|1994-11-09|COLLECT COD|FOB|foxes integrate \n3841|162372|9921|6|48|68849.76|0.03|0.00|R|F|1994-11-23|1994-11-22|1994-12-01|DELIVER IN PERSON|FOB| according to the regular, \n3842|161893|9442|1|28|54736.92|0.05|0.07|A|F|1992-06-17|1992-06-03|1992-06-24|DELIVER IN PERSON|TRUCK|s excuses thrash carefully.\n3842|121783|6808|2|21|37900.38|0.07|0.05|R|F|1992-07-15|1992-06-02|1992-07-21|NONE|RAIL|r pinto be\n3842|193009|8048|3|28|30856.00|0.00|0.00|A|F|1992-06-20|1992-05-22|1992-07-13|DELIVER IN PERSON|MAIL|lly alongside of the\n3842|87373|4898|4|15|20405.55|0.07|0.01|A|F|1992-06-26|1992-06-23|1992-07-09|COLLECT COD|MAIL|ave packages are slyl\n3842|67312|4831|5|13|16631.03|0.09|0.02|R|F|1992-04-13|1992-06-22|1992-05-11|COLLECT COD|RAIL|t blithely. busily regular accounts alon\n3842|106971|4502|6|24|47471.28|0.08|0.08|R|F|1992-08-05|1992-06-29|1992-08-16|TAKE BACK RETURN|MAIL|phins are quickly\n3843|14732|4733|1|7|11527.11|0.10|0.03|N|O|1997-02-13|1997-02-21|1997-02-20|TAKE BACK RETURN|SHIP|slyly even instructions. furiously eve\n3843|484|2985|2|30|41534.40|0.01|0.05|N|O|1997-02-14|1997-03-25|1997-03-13|DELIVER IN PERSON|AIR| wake. slyly even packages boost \n3844|134522|7036|1|2|3113.04|0.03|0.07|R|F|1995-02-24|1995-02-03|1995-03-18|TAKE BACK RETURN|AIR|es haggle final acco\n3844|101965|6986|2|5|9834.80|0.10|0.03|R|F|1995-04-29|1995-02-24|1995-05-05|TAKE BACK RETURN|RAIL| unwind quickly about the pending, i\n3845|33809|3810|1|44|76683.20|0.01|0.08|A|F|1992-07-20|1992-07-15|1992-07-24|DELIVER IN PERSON|REG AIR|s haggle among the fluffily regula\n3845|23875|1382|2|16|28781.92|0.09|0.05|A|F|1992-08-08|1992-06-08|1992-08-26|DELIVER IN PERSON|SHIP|ely bold ideas use. ex\n3845|58967|6483|3|17|32741.32|0.08|0.01|A|F|1992-06-12|1992-07-05|1992-06-26|TAKE BACK RETURN|RAIL|counts haggle. reg\n3845|45398|407|4|1|1343.39|0.04|0.05|R|F|1992-05-21|1992-06-07|1992-06-17|COLLECT COD|REG AIR| blithely ironic t\n3845|195390|5391|5|27|40105.53|0.00|0.05|R|F|1992-08-20|1992-07-17|1992-09-02|COLLECT COD|REG AIR|kages. care\n3845|104773|7284|6|30|53333.10|0.09|0.06|R|F|1992-08-21|1992-07-07|1992-08-25|COLLECT COD|FOB|counts do wake blithely. ironic requests \n3846|60152|2659|1|15|16682.25|0.06|0.03|N|O|1998-02-17|1998-04-27|1998-02-21|NONE|REG AIR|uternes. carefully even\n3846|170299|300|2|30|41078.70|0.08|0.07|N|O|1998-05-01|1998-03-12|1998-05-20|TAKE BACK RETURN|FOB|deposits according to the fur\n3846|14707|2211|3|49|79463.30|0.08|0.07|N|O|1998-02-14|1998-03-22|1998-02-17|DELIVER IN PERSON|RAIL|efully even packages against the blithe\n3846|164058|1607|4|33|37027.65|0.05|0.00|N|O|1998-05-12|1998-03-14|1998-05-14|DELIVER IN PERSON|TRUCK|s instructions are. fu\n3847|188290|8291|1|7|9648.03|0.08|0.00|A|F|1993-05-06|1993-06-06|1993-05-22|COLLECT COD|MAIL| about the blithely daring Tiresias. fl\n3872|180331|5368|1|28|39517.24|0.10|0.04|N|O|1996-11-05|1996-11-10|1996-11-24|DELIVER IN PERSON|REG AIR|t after the carefully ironic excuses. f\n3872|16635|1638|2|38|58961.94|0.04|0.05|N|O|1996-10-18|1996-12-03|1996-11-15|TAKE BACK RETURN|AIR|iously against the ironic, unusual a\n3872|168467|6016|3|18|27638.28|0.07|0.07|N|O|1996-12-25|1996-10-24|1997-01-08|TAKE BACK RETURN|SHIP|s. regular, brave accounts sleep blith\n3872|10643|644|4|41|63699.24|0.07|0.03|N|O|1996-11-23|1996-11-12|1996-12-03|COLLECT COD|REG AIR|ly regular epitaphs boost\n3872|69266|4279|5|42|51880.92|0.03|0.00|N|O|1997-01-03|1996-10-12|1997-01-16|COLLECT COD|MAIL|s the furio\n3872|139604|7144|6|40|65744.00|0.07|0.05|N|O|1997-01-02|1996-10-29|1997-01-14|NONE|REG AIR|nts? regularly ironic ex\n3873|67790|5309|1|19|33398.01|0.04|0.04|N|O|1998-05-15|1998-05-10|1998-05-17|NONE|FOB|y final ac\n3873|144960|9989|2|44|88218.24|0.05|0.05|N|O|1998-07-23|1998-05-22|1998-08-14|COLLECT COD|AIR|yly even platelets wake. \n3873|139267|1781|3|29|37881.54|0.01|0.04|N|O|1998-06-22|1998-05-20|1998-07-05|COLLECT COD|REG AIR|olphins af\n3874|169193|4226|1|21|26505.99|0.09|0.08|R|F|1993-06-19|1993-07-20|1993-07-08|DELIVER IN PERSON|SHIP| requests cajole fluff\n3874|18904|3907|2|48|87499.20|0.06|0.07|R|F|1993-06-13|1993-07-20|1993-06-20|NONE|RAIL| ideas throughout \n3875|80973|8498|1|24|46895.28|0.02|0.08|N|O|1997-10-15|1997-11-27|1997-11-09|COLLECT COD|AIR|ecial packages. \n3875|112716|5228|2|49|84706.79|0.04|0.04|N|O|1997-10-18|1997-10-13|1997-10-19|NONE|MAIL|sleep furiously about the deposits. quickl\n3876|140105|2620|1|12|13741.20|0.06|0.07|N|O|1996-09-16|1996-10-23|1996-10-05|TAKE BACK RETURN|REG AIR|y above the pending tithes. blithely ironi\n3876|139212|1726|2|37|46294.77|0.00|0.03|N|O|1996-11-30|1996-10-18|1996-12-18|DELIVER IN PERSON|AIR|t dependencies. blithely final packages u\n3876|126650|6651|3|41|68742.65|0.02|0.04|N|O|1996-10-15|1996-10-17|1996-10-19|NONE|AIR| quickly blit\n3877|49774|2279|1|12|20685.24|0.06|0.01|R|F|1993-05-30|1993-08-09|1993-06-24|TAKE BACK RETURN|FOB|nal requests. even requests are. pac\n3877|144635|2178|2|47|78942.61|0.05|0.00|A|F|1993-08-01|1993-08-16|1993-08-04|NONE|FOB|furiously quick requests nag along the theo\n3877|79690|7212|3|44|73466.36|0.09|0.00|A|F|1993-06-07|1993-07-15|1993-07-06|DELIVER IN PERSON|REG AIR|elets. quickly regular accounts caj\n3877|147844|7845|4|36|68106.24|0.06|0.01|A|F|1993-07-27|1993-07-13|1993-08-11|DELIVER IN PERSON|AIR|lithely about the dogged ideas. ac\n3877|4649|4650|5|41|63699.24|0.03|0.07|A|F|1993-06-30|1993-07-20|1993-07-01|DELIVER IN PERSON|FOB|integrate against the expres\n3877|122502|2503|6|7|10671.50|0.04|0.08|R|F|1993-06-14|1993-07-09|1993-06-28|NONE|TRUCK|lar dolphins cajole silently \n3878|199696|9697|1|6|10774.14|0.07|0.04|N|O|1997-06-21|1997-05-22|1997-07-01|COLLECT COD|FOB|s. regular instru\n3878|87395|7396|2|13|17971.07|0.01|0.06|N|O|1997-06-08|1997-06-03|1997-06-25|TAKE BACK RETURN|TRUCK|leep ruthlessly about the carefu\n3878|40052|2557|3|20|19841.00|0.08|0.03|N|O|1997-06-20|1997-05-24|1997-07-20|TAKE BACK RETURN|MAIL|the furiously careful ideas cajole slyly sl\n3878|151976|1977|4|20|40559.40|0.01|0.07|N|O|1997-07-13|1997-05-22|1997-07-20|NONE|FOB|about the carefully ironic pa\n3879|125689|714|1|45|77160.60|0.10|0.08|N|O|1996-03-18|1996-01-03|1996-04-03|COLLECT COD|RAIL|ly according to the expr\n3879|44020|1533|2|35|33740.70|0.00|0.07|N|O|1995-12-08|1996-01-23|1995-12-28|TAKE BACK RETURN|MAIL|o beans. accounts cajole furiously. re\n3904|37424|4934|1|22|29951.24|0.04|0.03|N|O|1998-02-02|1998-02-09|1998-02-10|TAKE BACK RETURN|REG AIR|structions cajole carefully. carefully f\n3904|183753|1308|2|19|34898.25|0.09|0.01|N|O|1998-02-10|1998-02-13|1998-02-20|TAKE BACK RETURN|AIR| excuses sleep slyly according to th\n3905|100739|8270|1|43|74808.39|0.07|0.08|A|F|1994-03-30|1994-02-18|1994-04-09|DELIVER IN PERSON|REG AIR|uses are care\n3905|115915|8427|2|7|13516.37|0.03|0.00|R|F|1994-03-01|1994-02-19|1994-03-11|DELIVER IN PERSON|AIR|ully furiously furious packag\n3905|169943|4976|3|6|12077.64|0.07|0.02|R|F|1994-04-07|1994-03-07|1994-04-21|DELIVER IN PERSON|RAIL|ow furiously. deposits wake ironic \n3906|152987|5503|1|42|85679.16|0.00|0.04|R|F|1992-09-03|1992-07-22|1992-09-04|COLLECT COD|RAIL|jole blithely after the furiously regular \n3906|39340|4347|2|50|63967.00|0.01|0.07|R|F|1992-09-24|1992-08-24|1992-09-29|NONE|MAIL|ke slyly. stealt\n3906|179655|4690|3|15|26019.75|0.06|0.02|R|F|1992-07-30|1992-08-26|1992-08-02|TAKE BACK RETURN|FOB|dependencies at the \n3906|58264|8265|4|36|44001.36|0.08|0.08|A|F|1992-08-07|1992-08-08|1992-08-24|NONE|SHIP|y. ironic deposits haggle sl\n3907|111784|4296|1|41|73626.98|0.06|0.02|A|F|1992-09-13|1992-10-23|1992-09-29|COLLECT COD|MAIL|ackages wake along the carefully regul\n3907|144491|2034|2|41|62955.09|0.03|0.00|A|F|1992-10-25|1992-10-17|1992-11-01|TAKE BACK RETURN|RAIL|s above the unusual ideas sleep furiousl\n3907|51501|9017|3|45|65362.50|0.02|0.07|R|F|1992-09-21|1992-09-19|1992-10-18|COLLECT COD|REG AIR| about the regular pac\n3907|175693|728|4|48|84897.12|0.05|0.07|A|F|1992-09-24|1992-10-16|1992-10-06|DELIVER IN PERSON|TRUCK|nt asymptotes lose across th\n3907|61958|1959|5|22|42238.90|0.09|0.01|R|F|1992-09-20|1992-10-30|1992-09-29|TAKE BACK RETURN|TRUCK|ly. furiously unusual deposits use afte\n3907|125062|2599|6|34|36960.04|0.02|0.02|R|F|1992-09-06|1992-10-08|1992-09-12|COLLECT COD|FOB| requests according to the slyly pending \n3907|109234|4255|7|8|9945.84|0.10|0.01|A|F|1992-09-18|1992-10-29|1992-09-27|NONE|REG AIR|furiously final packages.\n3908|91840|4350|1|50|91592.00|0.05|0.04|R|F|1993-06-19|1993-04-27|1993-07-05|DELIVER IN PERSON|MAIL| even accounts wake \n3908|147084|7085|2|8|9048.64|0.06|0.03|A|F|1993-03-12|1993-04-13|1993-03-22|DELIVER IN PERSON|SHIP|r instructions was requests. ironically \n3909|177914|5466|1|30|59757.30|0.03|0.07|N|O|1998-10-17|1998-10-14|1998-10-28|COLLECT COD|TRUCK|ly even deposits across the ironic notorni\n3909|190896|5935|2|46|91396.94|0.03|0.01|N|O|1998-10-08|1998-10-15|1998-10-24|NONE|FOB|the blithely unusual ideas\n3910|138602|3629|1|10|16406.00|0.00|0.08|N|O|1996-10-18|1996-10-31|1996-11-14|DELIVER IN PERSON|FOB|tions boost furiously unusual e\n3910|70782|5797|2|31|54336.18|0.05|0.03|N|O|1996-12-22|1996-11-14|1997-01-01|TAKE BACK RETURN|SHIP|ess instructions. \n3910|19716|4719|3|6|9814.26|0.04|0.04|N|O|1996-12-08|1996-10-30|1996-12-31|DELIVER IN PERSON|MAIL|ly sly platelets are fluffily slyly si\n3910|152611|5127|4|1|1663.61|0.03|0.06|N|O|1996-09-12|1996-10-21|1996-09-19|DELIVER IN PERSON|FOB|s sleep neve\n3911|112842|5354|1|10|18548.40|0.07|0.06|N|O|1995-06-22|1995-05-30|1995-06-28|COLLECT COD|FOB|ss theodolites are blithely along t\n3911|118861|6395|2|14|26318.04|0.08|0.05|R|F|1995-04-28|1995-05-03|1995-05-22|NONE|RAIL|e blithely brave depo\n3911|91085|6104|3|12|12912.96|0.10|0.05|R|F|1995-04-04|1995-04-16|1995-04-10|COLLECT COD|FOB|uctions. blithely regula\n3936|136372|6373|1|25|35209.25|0.06|0.03|N|O|1996-12-03|1996-12-27|1997-01-01|DELIVER IN PERSON|RAIL|gular requests nag quic\n3936|187641|160|2|24|41487.36|0.10|0.07|N|O|1996-11-22|1997-01-01|1996-12-08|NONE|AIR|ns. accounts mold fl\n3936|82065|7082|3|42|43976.52|0.00|0.07|N|O|1997-01-03|1997-01-29|1997-01-14|COLLECT COD|AIR|elets wake amo\n3936|61051|8570|4|12|12144.60|0.06|0.05|N|O|1996-11-25|1997-01-09|1996-12-06|DELIVER IN PERSON|SHIP|ithely across the carefully brave req\n3936|83519|3520|5|35|52587.85|0.02|0.08|N|O|1996-12-04|1997-01-06|1996-12-22|NONE|SHIP|lly ironic requ\n3936|102029|4540|6|26|26806.52|0.01|0.02|N|O|1997-02-27|1997-01-16|1997-03-22|NONE|RAIL|quickly pen\n3937|69439|4452|1|48|67604.64|0.10|0.02|N|O|1998-03-15|1998-02-22|1998-03-30|DELIVER IN PERSON|FOB|gainst the thinl\n3937|47321|2330|2|30|38049.60|0.01|0.07|N|O|1998-01-17|1998-01-03|1998-02-08|COLLECT COD|TRUCK|al packages slee\n3937|114474|2008|3|27|40188.69|0.03|0.00|N|O|1998-02-06|1998-01-12|1998-02-20|NONE|MAIL|ven ideas. slyly expr\n3937|153056|5572|4|50|55452.50|0.01|0.02|N|O|1998-01-15|1998-01-09|1998-02-04|DELIVER IN PERSON|AIR|ong the carefully exp\n3937|2904|405|5|29|52400.10|0.03|0.07|N|O|1998-03-06|1998-02-22|1998-03-14|NONE|TRUCK|nt pinto beans above the pending instr\n3937|192002|7041|6|6|6564.00|0.00|0.00|N|O|1998-01-24|1998-02-13|1998-01-27|DELIVER IN PERSON|FOB|into beans. slyly silent orbits alongside o\n3937|163846|1395|7|1|1909.84|0.02|0.05|N|O|1998-03-29|1998-01-08|1998-04-27|TAKE BACK RETURN|TRUCK|refully agains\n3938|158367|3398|1|46|65566.56|0.10|0.07|R|F|1993-05-20|1993-05-04|1993-06-12|DELIVER IN PERSON|FOB|ly even foxes are slyly fu\n3939|159523|2039|1|8|12660.16|0.03|0.06|N|O|1996-01-29|1996-04-05|1996-02-26|COLLECT COD|REG AIR|e packages. express, pen\n3940|177038|2073|1|33|36795.99|0.10|0.07|N|O|1996-05-19|1996-04-19|1996-05-23|TAKE BACK RETURN|RAIL|ly ironic packages about the pending accou\n3940|68501|6020|2|40|58780.00|0.08|0.02|N|O|1996-02-29|1996-03-22|1996-03-04|NONE|MAIL|ts. regular fox\n3940|88876|8877|3|8|14918.96|0.07|0.08|N|O|1996-04-04|1996-04-12|1996-04-18|DELIVER IN PERSON|RAIL|ions cajole furiously regular pinto beans. \n3940|136206|8720|4|11|13664.20|0.09|0.05|N|O|1996-03-09|1996-05-13|1996-03-17|COLLECT COD|REG AIR|e of the special packages. furiously\n3940|942|5943|5|41|75560.54|0.00|0.07|N|O|1996-05-08|1996-05-03|1996-06-03|COLLECT COD|MAIL|thily. deposits cajole.\n3941|40836|837|1|47|83511.01|0.05|0.07|N|O|1996-11-24|1996-10-09|1996-12-22|DELIVER IN PERSON|RAIL| carefully pending\n3941|122533|70|2|19|29555.07|0.05|0.00|N|O|1996-11-10|1996-10-26|1996-12-05|COLLECT COD|RAIL|eposits haggle furiously even\n3941|9693|2194|3|2|3205.38|0.01|0.03|N|O|1996-12-04|1996-10-01|1996-12-25|NONE|REG AIR|es wake after the\n3941|109348|6879|4|29|39362.86|0.00|0.03|N|O|1996-09-14|1996-10-04|1996-09-19|NONE|MAIL|g the blithely\n3942|182281|7318|1|6|8179.68|0.05|0.05|A|F|1993-07-01|1993-09-14|1993-07-23|DELIVER IN PERSON|SHIP|ep ruthlessly carefully final accounts: s\n3942|193715|8754|2|5|9043.55|0.06|0.02|R|F|1993-09-27|1993-09-24|1993-10-07|DELIVER IN PERSON|MAIL|. fluffily pending deposits above the flu\n3942|155497|8013|3|25|38812.25|0.04|0.06|R|F|1993-09-13|1993-08-01|1993-09-29|COLLECT COD|RAIL|d the quick packages\n3943|197698|5256|1|15|26935.35|0.03|0.01|N|O|1997-01-13|1996-12-17|1997-02-02|COLLECT COD|REG AIR| grow fluffily according to the \n3943|95557|5558|2|9|13972.95|0.00|0.06|N|O|1996-11-27|1997-01-03|1996-12-17|COLLECT COD|RAIL|refully ironic \n3943|16818|1821|3|32|55513.92|0.00|0.02|N|O|1996-10-22|1996-12-17|1996-11-04|TAKE BACK RETURN|TRUCK| unusual ideas into the furiously even pack\n3943|49034|9035|4|5|4915.15|0.04|0.04|N|O|1997-01-09|1996-11-10|1997-02-06|COLLECT COD|RAIL|arefully regular deposits accord\n3968|53646|6152|1|27|43190.28|0.04|0.05|N|O|1997-04-25|1997-04-17|1997-05-11|TAKE BACK RETURN|MAIL|t silently.\n3968|25166|2673|2|45|49102.20|0.00|0.07|N|O|1997-06-18|1997-04-24|1997-06-25|DELIVER IN PERSON|FOB|ully slyly fi\n3968|155118|5119|3|43|50443.73|0.07|0.06|N|O|1997-04-30|1997-05-14|1997-05-18|TAKE BACK RETURN|SHIP|ly regular accounts\n3968|60159|5172|4|7|7834.05|0.07|0.02|N|O|1997-03-30|1997-05-01|1997-04-12|DELIVER IN PERSON|SHIP|efully bold instructions. express\n3969|51062|8578|1|39|39509.34|0.04|0.04|N|O|1997-06-12|1997-06-13|1997-07-05|NONE|MAIL|ly bold ideas s\n3969|196745|4303|2|26|47885.24|0.05|0.03|N|O|1997-07-08|1997-07-30|1997-07-10|TAKE BACK RETURN|AIR|fluffily; braids detect.\n3969|78703|3718|3|46|77358.20|0.04|0.02|N|O|1997-05-29|1997-06-15|1997-06-10|TAKE BACK RETURN|SHIP|fully final requests sleep stealthily. care\n3969|150168|2684|4|21|25581.36|0.07|0.04|N|O|1997-08-31|1997-07-16|1997-09-02|TAKE BACK RETURN|MAIL|unts doze quickly final reque\n3969|71513|1514|5|40|59380.40|0.09|0.00|N|O|1997-05-19|1997-08-02|1997-06-05|COLLECT COD|TRUCK|lar requests cajole furiously blithely regu\n3969|104963|7474|6|4|7871.84|0.02|0.01|N|O|1997-06-04|1997-07-31|1997-06-13|COLLECT COD|REG AIR|dencies wake blithely? quickly even theodo\n3970|87268|7269|1|2|2510.52|0.01|0.07|R|F|1992-04-24|1992-06-03|1992-05-16|TAKE BACK RETURN|RAIL|carefully pending foxes wake blithely \n3970|108084|5615|2|18|19657.44|0.03|0.08|A|F|1992-06-06|1992-06-18|1992-07-05|DELIVER IN PERSON|TRUCK| maintain slyly. ir\n3970|153569|1115|3|10|16225.60|0.10|0.04|A|F|1992-07-01|1992-05-31|1992-07-02|NONE|AIR| special packages wake after the final br\n3970|21600|9107|4|34|51734.40|0.05|0.00|A|F|1992-06-25|1992-05-23|1992-07-12|COLLECT COD|SHIP|y final gifts are. carefully pe\n3970|29048|6555|5|23|22471.92|0.05|0.04|A|F|1992-06-04|1992-06-14|1992-06-13|COLLECT COD|TRUCK| above the final braids. regular\n3970|8771|6272|6|46|77269.42|0.07|0.04|R|F|1992-04-29|1992-05-14|1992-05-24|NONE|FOB|yly ironic\n3970|4076|6577|7|46|45083.22|0.08|0.08|R|F|1992-05-02|1992-05-12|1992-05-10|COLLECT COD|MAIL|ix slyly. quickly silen\n3971|95389|7899|1|47|65065.86|0.06|0.04|N|O|1996-07-07|1996-08-08|1996-08-01|TAKE BACK RETURN|RAIL|e slyly final dependencies x-ray \n3971|190223|7781|2|2|2626.44|0.04|0.03|N|O|1996-07-15|1996-08-12|1996-07-26|NONE|SHIP|haggle abou\n3972|50440|7956|1|2|2780.88|0.05|0.03|A|F|1994-07-24|1994-06-30|1994-08-13|TAKE BACK RETURN|SHIP|y final theodolite\n3973|29590|4595|1|21|31911.39|0.02|0.06|R|F|1992-06-18|1992-06-03|1992-07-02|COLLECT COD|REG AIR|equests. furiously\n3973|114205|9228|2|37|45110.40|0.07|0.00|A|F|1992-05-29|1992-05-04|1992-06-23|TAKE BACK RETURN|SHIP|inos wake fluffily. pending requests nag \n3973|39243|1747|3|40|47289.60|0.08|0.05|R|F|1992-05-03|1992-06-09|1992-05-21|COLLECT COD|RAIL|g the carefully blithe f\n3974|21091|6096|1|47|47568.23|0.10|0.03|N|O|1996-06-03|1996-05-08|1996-06-28|NONE|TRUCK|dencies above the re\n3974|60454|5467|2|17|24045.65|0.05|0.07|N|O|1996-04-05|1996-05-21|1996-04-28|COLLECT COD|TRUCK|ions eat slyly after the blithely \n3975|56042|3558|1|38|37925.52|0.01|0.05|N|O|1995-08-02|1995-06-18|1995-08-19|COLLECT COD|TRUCK|es are furiously: furi\n4000|195697|5698|1|41|73500.29|0.06|0.01|A|F|1992-03-02|1992-03-14|1992-03-27|COLLECT COD|FOB|ve the even, fi\n4000|74526|7034|2|44|66022.88|0.09|0.06|A|F|1992-03-27|1992-02-18|1992-03-31|COLLECT COD|AIR|equests use blithely blithely bold d\n4001|105185|206|1|26|30944.68|0.00|0.01|N|O|1997-07-26|1997-06-18|1997-08-08|DELIVER IN PERSON|RAIL|tegrate blithely\n4001|40918|8431|2|19|35319.29|0.03|0.02|N|O|1997-08-23|1997-06-15|1997-09-18|COLLECT COD|SHIP|ackages. carefully ironi\n4001|93081|3082|3|18|19333.44|0.07|0.00|N|O|1997-06-04|1997-06-22|1997-06-13|DELIVER IN PERSON|MAIL|lithely ironic d\n4001|1923|9424|4|39|71171.88|0.00|0.00|N|O|1997-06-13|1997-06-17|1997-06-25|NONE|SHIP| dogged excuses. blithe\n4002|110728|3240|1|35|60855.20|0.01|0.08|N|O|1997-05-16|1997-06-15|1997-06-02|DELIVER IN PERSON|TRUCK|eep. quickly\n4002|197837|7838|2|20|38696.60|0.00|0.03|N|O|1997-06-15|1997-05-20|1997-07-11|NONE|MAIL|lly even ins\n4002|39841|4848|3|6|10685.04|0.08|0.07|N|O|1997-05-02|1997-07-07|1997-05-16|TAKE BACK RETURN|RAIL| furiously furiously special theodoli\n4002|198650|6208|4|6|10491.90|0.06|0.06|N|O|1997-07-01|1997-05-15|1997-07-31|NONE|MAIL|he slyly iro\n4002|98186|696|5|4|4736.72|0.08|0.07|N|O|1997-05-06|1997-06-15|1997-05-24|NONE|REG AIR|ccording to the careful\n4003|51112|8628|1|18|19135.98|0.04|0.07|R|F|1993-02-02|1993-04-15|1993-02-28|TAKE BACK RETURN|AIR|ar grouches s\n4004|120061|62|1|23|24864.38|0.07|0.02|A|F|1993-08-12|1993-07-13|1993-08-16|TAKE BACK RETURN|TRUCK| bold theodolites? special packages accordi\n4004|63505|3506|2|47|69019.50|0.07|0.04|R|F|1993-06-25|1993-08-03|1993-07-12|NONE|SHIP|thely instead of the even, unu\n4004|113136|3137|3|39|44816.07|0.10|0.05|R|F|1993-07-12|1993-07-27|1993-07-18|NONE|MAIL|ccounts sleep furious\n4004|73634|6142|4|46|73950.98|0.10|0.04|R|F|1993-09-04|1993-07-13|1993-09-28|COLLECT COD|FOB|ncies. slyly pending dolphins sleep furio\n4004|154767|7283|5|9|16395.84|0.04|0.06|A|F|1993-08-25|1993-06-10|1993-09-24|COLLECT COD|MAIL|ly ironic requests. quickly pending ide\n4004|160037|2554|6|44|48269.32|0.07|0.05|R|F|1993-07-25|1993-07-23|1993-08-16|TAKE BACK RETURN|REG AIR|ut the sauternes. bold, ironi\n4004|125377|2914|7|20|28047.40|0.07|0.05|A|F|1993-06-19|1993-06-14|1993-07-04|COLLECT COD|REG AIR|. ironic deposits cajole blithely?\n4005|3667|1168|1|26|40837.16|0.09|0.05|N|O|1996-12-01|1997-02-03|1996-12-15|NONE|REG AIR| to the quic\n4005|16605|6606|2|28|42604.80|0.02|0.06|N|O|1996-12-11|1997-01-24|1996-12-17|DELIVER IN PERSON|REG AIR|ly carefully ironic deposits. slyly\n4005|71317|8839|3|28|36072.68|0.03|0.01|N|O|1996-12-08|1997-01-14|1996-12-30|TAKE BACK RETURN|MAIL|y pending dependenc\n4005|14650|7152|4|49|76667.85|0.09|0.00|N|O|1997-01-31|1996-12-24|1997-03-02|NONE|RAIL|tions sleep across the silent d\n4005|5747|5748|5|14|23138.36|0.09|0.08|N|O|1996-11-27|1997-01-09|1996-12-25|NONE|TRUCK|ld requests. slyly final instructi\n4006|54773|2289|1|11|19005.47|0.05|0.08|A|F|1995-04-29|1995-02-21|1995-05-20|TAKE BACK RETURN|RAIL|ress foxes cajole quick\n4006|158031|3062|2|18|19602.54|0.05|0.03|A|F|1995-01-29|1995-03-08|1995-02-02|TAKE BACK RETURN|MAIL|gouts! slyly iron\n4006|23176|3177|3|15|16487.55|0.01|0.02|R|F|1995-02-23|1995-04-02|1995-02-25|TAKE BACK RETURN|RAIL|n deposits cajole slyl\n4006|113466|3467|4|25|36986.50|0.00|0.07|A|F|1995-02-23|1995-02-09|1995-02-24|DELIVER IN PERSON|SHIP| requests use depos\n4007|56091|1102|1|32|33506.88|0.00|0.03|R|F|1993-09-30|1993-08-16|1993-10-03|DELIVER IN PERSON|RAIL|nal accounts across t\n4007|115470|7982|2|41|60904.27|0.04|0.06|A|F|1993-10-11|1993-08-30|1993-11-04|DELIVER IN PERSON|TRUCK|eposits. regular epitaphs boost blithely.\n4007|101478|9009|3|5|7397.35|0.09|0.06|A|F|1993-09-17|1993-08-29|1993-10-12|TAKE BACK RETURN|FOB|y unusual packa\n4007|137992|5532|4|15|30449.85|0.05|0.02|A|F|1993-09-01|1993-07-19|1993-09-03|DELIVER IN PERSON|FOB|le furiously quickly \n4007|25894|5895|5|23|41857.47|0.02|0.07|A|F|1993-10-08|1993-09-09|1993-10-23|COLLECT COD|MAIL|ter the accounts. expr\n4032|101625|1626|1|8|13012.96|0.06|0.00|N|O|1998-06-04|1998-05-17|1998-07-03|TAKE BACK RETURN|RAIL|ometimes even cou\n4032|1652|9153|2|27|41948.55|0.09|0.00|N|O|1998-05-31|1998-04-19|1998-06-24|COLLECT COD|REG AIR|le furiously according to\n4032|153608|6124|3|23|38216.80|0.09|0.06|N|O|1998-06-12|1998-05-11|1998-06-24|COLLECT COD|MAIL|ording to the \n4032|84644|7153|4|10|16286.40|0.09|0.05|N|O|1998-03-31|1998-04-22|1998-04-07|NONE|REG AIR| carefully bol\n4033|109529|9530|1|27|41540.04|0.01|0.04|R|F|1993-08-08|1993-08-14|1993-08-09|NONE|AIR|pinto beans\n4033|37321|9825|2|34|42782.88|0.07|0.00|R|F|1993-07-19|1993-08-05|1993-07-26|NONE|RAIL|t the blithely dogg\n4034|189949|4986|1|48|97869.12|0.03|0.03|A|F|1994-03-01|1994-01-16|1994-03-16|NONE|RAIL| blithely regular requests play carefull\n4034|56730|9236|2|47|79276.31|0.07|0.05|A|F|1994-01-27|1993-12-26|1994-02-04|NONE|TRUCK|eodolites was slyly ironic ideas. de\n4034|53884|3885|3|43|79028.84|0.10|0.03|A|F|1993-11-29|1994-01-08|1993-12-10|DELIVER IN PERSON|FOB|posits wake carefully af\n4034|27679|7680|4|46|73906.82|0.06|0.00|A|F|1994-02-22|1994-01-09|1994-03-04|DELIVER IN PERSON|AIR|uests. furiously unusual instructions wake\n4034|195196|2754|5|7|9038.33|0.07|0.06|R|F|1994-03-04|1994-01-22|1994-04-01|NONE|AIR|y even theodolites. slyly regular instru\n4034|49283|6796|6|5|6161.40|0.01|0.06|A|F|1994-02-12|1994-01-24|1994-02-13|COLLECT COD|AIR|fully around the furiously ironic re\n4035|96790|6791|1|4|7147.16|0.08|0.03|R|F|1992-04-21|1992-04-23|1992-04-25|COLLECT COD|AIR|ilent, even pear\n4035|135094|5095|2|4|4516.36|0.07|0.00|A|F|1992-05-21|1992-04-24|1992-05-24|DELIVER IN PERSON|FOB|en instructions sleep blith\n4035|117010|4544|3|1|1027.01|0.03|0.01|R|F|1992-06-18|1992-05-19|1992-07-02|COLLECT COD|FOB| requests. quickly \n4035|181284|8839|4|13|17748.64|0.00|0.01|R|F|1992-06-10|1992-05-16|1992-07-10|NONE|SHIP|s. furiously even courts wake slyly\n4036|5585|586|1|46|68566.68|0.09|0.00|N|O|1997-06-21|1997-05-29|1997-07-18|NONE|REG AIR|usly across the even th\n4036|52428|4934|2|21|28988.82|0.09|0.07|N|O|1997-08-08|1997-06-28|1997-08-09|COLLECT COD|MAIL|e carefully. qui\n4036|141687|1688|3|6|10372.08|0.07|0.01|N|O|1997-06-19|1997-06-16|1997-07-01|DELIVER IN PERSON|SHIP|equests wake about the bold id\n4036|126016|3553|4|20|20840.20|0.08|0.02|N|O|1997-08-11|1997-07-11|1997-09-03|NONE|TRUCK|slyly bold deposits cajole pending, blithe\n4037|63703|1222|1|32|53334.40|0.00|0.06|A|F|1993-05-06|1993-06-08|1993-05-31|DELIVER IN PERSON|AIR|e of the pending, iron\n4037|46766|6767|2|4|6851.04|0.09|0.07|A|F|1993-07-05|1993-06-12|1993-08-03|DELIVER IN PERSON|RAIL|s around the blithely ironic ac\n4038|195610|3168|1|40|68224.40|0.05|0.01|N|O|1996-01-15|1996-03-13|1996-01-25|COLLECT COD|TRUCK|t. slyly silent pinto beans amo\n4038|11283|6286|2|37|44188.36|0.04|0.03|N|O|1996-03-17|1996-03-19|1996-04-07|DELIVER IN PERSON|REG AIR| packages \n4038|31841|1842|3|24|42548.16|0.10|0.04|N|O|1996-04-06|1996-02-15|1996-04-18|TAKE BACK RETURN|RAIL|the furiously regu\n4038|149264|9265|4|29|38084.54|0.07|0.06|N|O|1996-01-07|1996-03-08|1996-01-13|NONE|FOB|ffix. quietly ironic packages a\n4038|78767|6289|5|24|41898.24|0.07|0.06|N|O|1996-04-01|1996-04-05|1996-04-28|DELIVER IN PERSON|TRUCK|ake quickly after the final, ironic ac\n4038|35867|8371|6|6|10817.16|0.07|0.05|N|O|1996-02-09|1996-03-05|1996-03-10|COLLECT COD|SHIP| special instructions. packa\n4039|93195|3196|1|38|45151.22|0.03|0.06|N|O|1998-03-09|1997-12-31|1998-03-21|DELIVER IN PERSON|REG AIR|sual asymptotes. ironic deposits nag aft\n4039|121085|8622|2|17|18803.36|0.10|0.04|N|O|1998-01-15|1998-01-20|1998-01-28|TAKE BACK RETURN|MAIL| regular foxes haggle carefully bo\n4039|63297|8310|3|9|11342.61|0.10|0.01|N|O|1998-03-08|1998-02-05|1998-04-05|TAKE BACK RETURN|FOB|t? pinto beans cajole across the thinly r\n4039|27080|9583|4|43|43304.44|0.01|0.02|N|O|1998-01-02|1997-12-22|1998-01-15|NONE|FOB|beans believe bene\n4039|133273|3274|5|43|56169.61|0.09|0.00|N|O|1998-01-20|1998-01-11|1998-02-05|COLLECT COD|SHIP|sts along the regular in\n4064|198313|833|1|3|4233.93|0.10|0.04|N|O|1997-01-04|1997-01-01|1997-01-23|NONE|SHIP|its! quickly sp\n4064|39472|1976|2|15|21172.05|0.02|0.02|N|O|1996-11-09|1996-12-04|1996-11-18|DELIVER IN PERSON|MAIL|braids affix across the regular sheave\n4064|196654|1693|3|32|56020.80|0.04|0.07|N|O|1997-01-14|1997-01-01|1997-01-21|COLLECT COD|REG AIR|es boost. careful\n4064|162125|9674|4|24|28490.88|0.02|0.02|N|O|1997-01-01|1996-12-31|1997-01-23|DELIVER IN PERSON|SHIP|ly regular ideas.\n4064|20658|659|5|12|18943.80|0.08|0.08|N|O|1997-02-08|1996-12-18|1997-03-06|TAKE BACK RETURN|RAIL|ding to the requests\n4064|183388|943|6|46|67683.48|0.03|0.00|N|O|1996-10-13|1997-01-05|1996-11-06|DELIVER IN PERSON|REG AIR|alongside of the f\n4064|199699|2219|7|9|16188.21|0.01|0.06|N|O|1996-12-17|1996-12-13|1997-01-12|NONE|AIR|furiously f\n4065|137373|7374|1|14|19745.18|0.04|0.02|A|F|1994-08-22|1994-07-29|1994-09-19|DELIVER IN PERSON|TRUCK|e furiously outside \n4065|14060|4061|2|46|44806.76|0.03|0.05|A|F|1994-06-29|1994-08-01|1994-07-03|TAKE BACK RETURN|SHIP|, regular requests may mold above the \n4065|96443|1462|3|33|47501.52|0.00|0.03|A|F|1994-09-03|1994-08-16|1994-09-13|DELIVER IN PERSON|AIR|ain blithely \n4065|106463|1484|4|8|11755.68|0.00|0.01|R|F|1994-10-04|1994-08-05|1994-10-25|TAKE BACK RETURN|SHIP|ages haggle carefully\n4065|122670|2671|5|29|49087.43|0.02|0.07|A|F|1994-06-29|1994-08-19|1994-07-17|NONE|RAIL|equests. packages sleep slyl\n4065|109059|4080|6|16|17088.80|0.05|0.00|R|F|1994-08-25|1994-08-06|1994-09-09|COLLECT COD|TRUCK|ncies use furiously. quickly un\n4065|143199|8228|7|11|13664.09|0.10|0.04|A|F|1994-07-25|1994-08-02|1994-07-30|NONE|RAIL|hang silently about \n4066|138329|5869|1|9|12305.88|0.01|0.05|N|O|1997-05-06|1997-03-25|1997-05-27|COLLECT COD|FOB|nal, ironic accounts. blithel\n4066|92550|5060|2|19|29308.45|0.05|0.00|N|O|1997-05-13|1997-04-17|1997-06-08|NONE|TRUCK|quests. slyly regu\n4066|75789|804|3|8|14118.24|0.03|0.03|N|O|1997-04-24|1997-03-11|1997-05-20|NONE|REG AIR|accounts. special pinto beans\n4066|178139|657|4|49|59639.37|0.01|0.01|N|O|1997-02-17|1997-03-24|1997-02-19|NONE|TRUCK|ial braids. furiously final deposits sl\n4066|170234|235|5|43|56081.89|0.05|0.02|N|O|1997-02-16|1997-04-14|1997-02-18|DELIVER IN PERSON|MAIL|r instructions. slyly special \n4066|108491|1002|6|44|65977.56|0.01|0.00|N|O|1997-03-01|1997-04-27|1997-03-29|DELIVER IN PERSON|MAIL|express accounts nag bli\n4067|179063|9064|1|18|20557.08|0.03|0.08|A|F|1993-01-24|1992-12-23|1993-02-20|TAKE BACK RETURN|FOB|e the slyly final packages d\n4067|95338|2866|2|14|18666.62|0.00|0.00|R|F|1993-02-03|1992-12-02|1993-02-07|TAKE BACK RETURN|TRUCK|ructions. quickly ironic accounts detect \n4067|140221|7764|3|17|21440.74|0.03|0.05|A|F|1993-01-26|1992-11-23|1993-01-27|NONE|REG AIR|ts haggle slyly unusual, final\n4067|89682|2191|4|40|66867.20|0.07|0.08|R|F|1993-01-09|1992-11-21|1993-01-16|DELIVER IN PERSON|TRUCK|lar theodolites nag blithely above the\n4067|84828|9845|5|17|30817.94|0.08|0.03|A|F|1993-01-20|1992-12-29|1993-02-03|DELIVER IN PERSON|REG AIR|r accounts. slyly special pa\n4067|95501|8011|6|12|17958.00|0.04|0.03|A|F|1992-12-12|1992-11-28|1992-12-15|DELIVER IN PERSON|AIR|lly slyly even theodol\n4067|82675|5184|7|17|28180.39|0.01|0.01|R|F|1992-12-12|1992-12-23|1992-12-30|NONE|AIR|ts affix. regular, regular requests s\n4068|109101|9102|1|43|47734.30|0.05|0.06|N|O|1996-11-28|1996-11-16|1996-12-22|NONE|AIR|ructions. regular, special packag\n4068|56989|9495|2|31|60325.38|0.08|0.03|N|O|1996-12-11|1996-12-07|1996-12-30|NONE|SHIP|ds wake carefully amon\n4069|128033|5570|1|39|41380.17|0.09|0.02|R|F|1992-09-06|1992-07-22|1992-09-25|COLLECT COD|SHIP|ven theodolites nag quickly. fluffi\n4069|42553|2554|2|32|47857.60|0.10|0.08|A|F|1992-06-18|1992-07-20|1992-07-07|TAKE BACK RETURN|TRUCK|unts. deposit\n4069|185023|7542|3|3|3324.06|0.06|0.01|R|F|1992-07-26|1992-07-07|1992-08-04|COLLECT COD|FOB|l packages. even, \n4069|78465|3480|4|22|31756.12|0.10|0.05|A|F|1992-08-05|1992-08-04|1992-08-25|COLLECT COD|SHIP|ts. slyly special instruction\n4069|156895|9411|5|50|97594.50|0.09|0.06|A|F|1992-07-26|1992-06-30|1992-08-01|TAKE BACK RETURN|REG AIR|even foxes among the express wate\n4069|124947|2484|6|3|5915.82|0.02|0.01|A|F|1992-05-24|1992-06-18|1992-06-12|COLLECT COD|MAIL|y final deposits wake furiously! slyl\n4069|183483|6002|7|50|78324.00|0.00|0.01|R|F|1992-09-03|1992-06-14|1992-10-01|NONE|REG AIR|ages. carefully regular \n4070|182612|7649|1|2|3389.22|0.09|0.08|N|O|1995-08-03|1995-09-10|1995-08-17|TAKE BACK RETURN|REG AIR|ptotes affix\n4070|154868|7384|2|40|76914.40|0.07|0.07|N|O|1995-07-13|1995-07-23|1995-08-06|COLLECT COD|MAIL|about the sentiments. quick\n4070|61774|1775|3|11|19093.47|0.00|0.08|N|O|1995-08-23|1995-08-15|1995-08-31|TAKE BACK RETURN|MAIL| carefully final pack\n4070|28495|998|4|46|65480.54|0.02|0.02|N|O|1995-06-22|1995-07-14|1995-07-11|DELIVER IN PERSON|REG AIR|nticing ideas. boldly\n4071|111872|9406|1|22|41445.14|0.02|0.07|N|O|1996-10-31|1996-12-14|1996-11-05|NONE|REG AIR|sits cajole carefully final instructio\n4071|17736|5240|2|47|77725.31|0.00|0.03|N|O|1996-11-04|1996-12-09|1996-11-16|NONE|TRUCK|ts cajole furiously along the\n4096|26850|4357|1|31|55082.35|0.10|0.02|A|F|1992-07-14|1992-09-03|1992-07-31|COLLECT COD|TRUCK|y final, even platelets. boldly\n4096|56025|3541|2|17|16677.34|0.07|0.03|R|F|1992-09-30|1992-08-11|1992-10-11|TAKE BACK RETURN|REG AIR|platelets alongside of the \n4096|8635|8636|3|21|32416.23|0.08|0.00|A|F|1992-08-24|1992-09-04|1992-09-11|DELIVER IN PERSON|MAIL|tes mold flu\n4096|127636|149|4|20|33272.60|0.02|0.07|R|F|1992-08-24|1992-09-13|1992-08-28|DELIVER IN PERSON|TRUCK|sual requests. furiously bold packages wake\n4097|73486|3487|1|50|72974.00|0.04|0.04|N|O|1996-08-31|1996-08-14|1996-09-27|DELIVER IN PERSON|MAIL|egular deposits. blithely pending\n4097|73230|5738|2|46|55348.58|0.10|0.01|N|O|1996-07-29|1996-08-19|1996-08-25|COLLECT COD|AIR| even depend\n4097|173706|1258|3|42|74747.40|0.06|0.06|N|O|1996-08-11|1996-07-30|1996-08-15|NONE|FOB|carefully silent foxes are against the \n4098|199417|9418|1|46|69754.86|0.07|0.03|N|O|1997-01-26|1997-01-27|1997-02-13|TAKE BACK RETURN|SHIP|e slyly blithely silent deposits. fluff\n4099|3007|5508|1|29|26390.00|0.09|0.07|R|F|1992-11-21|1992-11-04|1992-11-30|NONE|FOB| slowly final warthogs sleep blithely. q\n4099|136994|4534|2|3|6092.97|0.04|0.06|A|F|1992-09-12|1992-10-18|1992-10-01|NONE|RAIL|. special packages sleep\n4099|50012|7528|3|36|34632.36|0.06|0.06|R|F|1992-11-06|1992-09-28|1992-12-02|NONE|FOB|beans cajole slyly quickly ironic \n4099|138940|6480|4|7|13852.58|0.05|0.02|A|F|1992-09-12|1992-11-13|1992-09-14|TAKE BACK RETURN|AIR|onic foxes. quickly final fox\n4099|162936|7969|5|48|95948.64|0.00|0.02|R|F|1992-10-18|1992-10-14|1992-11-01|NONE|REG AIR|ts haggle according to the slyly f\n4099|58106|8107|6|39|41499.90|0.07|0.02|R|F|1992-12-13|1992-11-13|1992-12-26|DELIVER IN PERSON|REG AIR|fluffy accounts impress pending, iro\n4099|179228|6780|7|46|60132.12|0.06|0.07|R|F|1992-10-29|1992-11-03|1992-11-10|DELIVER IN PERSON|REG AIR|ages nag requests.\n4100|73624|3625|1|4|6390.48|0.03|0.03|N|O|1996-06-20|1996-04-29|1996-06-21|TAKE BACK RETURN|FOB|lyly regular, bold requ\n4101|114376|9399|1|22|30588.14|0.05|0.02|R|F|1994-02-02|1994-02-19|1994-02-12|COLLECT COD|AIR|ly express instructions. careful\n4102|9762|2263|1|17|28419.92|0.02|0.02|N|O|1996-06-03|1996-05-06|1996-07-02|COLLECT COD|AIR|ly silent theodolites sleep unusual exc\n4102|68535|1042|2|5|7517.65|0.08|0.02|N|O|1996-05-11|1996-05-11|1996-05-16|COLLECT COD|AIR| the furiously even\n4102|66779|1792|3|39|68085.03|0.08|0.01|N|O|1996-04-14|1996-05-18|1996-04-20|DELIVER IN PERSON|AIR|ffix blithely slyly special \n4102|139097|1611|4|39|44307.51|0.02|0.00|N|O|1996-06-15|1996-06-06|1996-06-30|DELIVER IN PERSON|SHIP|y among the furiously special\n4102|175|5176|5|32|34405.44|0.08|0.01|N|O|1996-05-14|1996-04-29|1996-05-29|NONE|RAIL| the even requests; regular pinto\n4102|136116|1143|6|7|8064.77|0.02|0.01|N|O|1996-06-19|1996-05-21|1996-07-15|NONE|REG AIR|bove the carefully pending the\n4103|74738|9753|1|40|68509.20|0.05|0.03|R|F|1992-09-19|1992-08-14|1992-09-21|COLLECT COD|RAIL|usly across the slyly busy accounts! fin\n4128|195824|8344|1|5|9599.10|0.04|0.04|N|O|1995-10-18|1995-11-28|1995-10-28|TAKE BACK RETURN|FOB|ake permanently \n4129|55253|2769|1|32|38664.00|0.03|0.04|A|F|1993-09-16|1993-08-25|1993-09-25|TAKE BACK RETURN|MAIL|ckages haggl\n4129|26905|1910|2|39|71444.10|0.06|0.07|R|F|1993-10-21|1993-08-04|1993-10-29|COLLECT COD|MAIL|y regular foxes. slyly ironic deposits \n4130|177966|5518|1|44|89934.24|0.07|0.04|N|O|1996-05-14|1996-04-15|1996-05-15|COLLECT COD|TRUCK|eaves haggle qui\n4130|62910|7923|2|2|3745.82|0.05|0.06|N|O|1996-05-19|1996-04-24|1996-06-17|TAKE BACK RETURN|RAIL|uriously regular instructions around th\n4131|49740|2245|1|6|10138.44|0.05|0.01|N|O|1998-04-27|1998-04-18|1998-04-29|TAKE BACK RETURN|MAIL|ns cajole slyly. even, iro\n4131|177535|53|2|32|51600.96|0.08|0.01|N|O|1998-03-02|1998-03-21|1998-03-07|TAKE BACK RETURN|TRUCK| furiously regular asymptotes nod sly\n4131|25254|2761|3|25|29481.25|0.02|0.07|N|O|1998-02-24|1998-03-01|1998-02-27|TAKE BACK RETURN|FOB|uickly exp\n4131|35077|84|4|8|8096.56|0.04|0.01|N|O|1998-03-03|1998-03-15|1998-03-26|COLLECT COD|FOB| after the furiously ironic d\n4131|124360|4361|5|30|41530.80|0.01|0.01|N|O|1998-04-01|1998-04-13|1998-04-08|TAKE BACK RETURN|FOB|he fluffily express depen\n4131|101926|6947|6|47|90612.24|0.02|0.00|N|O|1998-03-09|1998-04-05|1998-03-13|TAKE BACK RETURN|RAIL|ges. ironic pinto be\n4132|137396|9910|1|28|40134.92|0.07|0.03|N|O|1995-08-16|1995-08-01|1995-08-29|TAKE BACK RETURN|SHIP|pths wake against the stealthily special pi\n4132|14238|1742|2|23|26501.29|0.07|0.07|N|O|1995-06-27|1995-07-27|1995-07-13|TAKE BACK RETURN|FOB|d deposits. fluffily even requests haggle b\n4132|86626|1643|3|18|29027.16|0.09|0.04|A|F|1995-06-01|1995-08-01|1995-06-02|TAKE BACK RETURN|RAIL|y final de\n4133|23646|3647|1|35|54937.40|0.02|0.00|A|F|1992-11-25|1992-09-15|1992-12-25|NONE|AIR|g above the quickly bold packages. ev\n4134|120218|7755|1|34|42099.14|0.02|0.05|R|F|1995-04-29|1995-03-13|1995-05-11|DELIVER IN PERSON|FOB|e furiously regular sheaves sleep\n4134|95661|3189|2|34|56326.44|0.01|0.03|A|F|1995-05-06|1995-03-28|1995-05-13|DELIVER IN PERSON|SHIP|ual asymptotes wake carefully alo\n4134|170507|8059|3|12|18930.00|0.05|0.04|A|F|1995-03-19|1995-03-27|1995-04-14|COLLECT COD|TRUCK|kly above the quickly regular \n4134|99989|7517|4|45|89504.10|0.08|0.02|A|F|1995-04-11|1995-03-27|1995-04-17|TAKE BACK RETURN|MAIL|ironic pin\n4135|1947|1948|1|23|42525.62|0.06|0.01|N|O|1997-04-09|1997-05-12|1997-04-16|TAKE BACK RETURN|FOB|posits cajole furiously carefully\n4135|119317|9318|2|32|42761.92|0.07|0.00|N|O|1997-03-14|1997-04-23|1997-04-12|TAKE BACK RETURN|TRUCK| ideas. requests use. furiously\n4135|159231|4262|3|33|42577.59|0.05|0.05|N|O|1997-05-01|1997-05-23|1997-05-23|DELIVER IN PERSON|AIR|he fluffil\n4135|194719|4720|4|13|23578.23|0.04|0.07|N|O|1997-03-16|1997-05-19|1997-04-03|COLLECT COD|RAIL|efully special account\n4160|112413|7436|1|25|35635.25|0.10|0.04|N|O|1996-09-22|1996-10-17|1996-09-24|NONE|SHIP|ar accounts sleep blithe\n4160|121668|4181|2|12|20275.92|0.00|0.03|N|O|1996-11-22|1996-09-25|1996-12-10|DELIVER IN PERSON|REG AIR|y bold package\n4160|62498|2499|3|48|70103.52|0.04|0.04|N|O|1996-09-19|1996-11-02|1996-09-24|COLLECT COD|FOB| unusual dolphins \n4161|121535|4048|1|12|18678.36|0.08|0.02|R|F|1993-08-25|1993-10-04|1993-09-22|COLLECT COD|RAIL|onic dolphins. in\n4161|27319|9822|2|47|58576.57|0.05|0.00|A|F|1993-12-20|1993-10-29|1994-01-19|TAKE BACK RETURN|RAIL|r requests about the final, even foxes hag\n4161|137027|9541|3|42|44688.84|0.03|0.04|R|F|1993-11-12|1993-10-04|1993-11-27|COLLECT COD|MAIL|thely across the even attainments. express\n4161|9463|4464|4|45|61760.70|0.02|0.06|A|F|1993-10-22|1993-10-17|1993-10-30|COLLECT COD|REG AIR|about the ironic packages cajole blithe\n4161|28343|8344|5|46|58481.64|0.05|0.01|A|F|1993-11-09|1993-11-17|1993-11-17|TAKE BACK RETURN|TRUCK|he stealthily ironic foxes. ideas haggl\n4161|147061|7062|6|19|21053.14|0.07|0.00|R|F|1993-08-22|1993-11-11|1993-09-01|TAKE BACK RETURN|REG AIR|beans breach s\n4162|73106|8121|1|45|48559.50|0.10|0.07|A|F|1992-03-21|1992-05-02|1992-03-29|DELIVER IN PERSON|AIR|elets. slyly regular i\n4162|89349|9350|2|29|38811.86|0.00|0.05|R|F|1992-02-25|1992-04-25|1992-03-17|NONE|REG AIR|nding pinto beans haggle blithe\n4163|32870|2871|1|13|23437.31|0.08|0.03|A|F|1993-02-17|1993-03-13|1993-03-15|DELIVER IN PERSON|REG AIR|phins wake. pending requests inte\n4164|119658|4681|1|9|15098.85|0.07|0.02|N|O|1998-08-25|1998-08-13|1998-09-19|DELIVER IN PERSON|SHIP|re fluffily slyly bold requests. \n4165|40102|103|1|12|12505.20|0.00|0.01|N|O|1997-09-20|1997-10-20|1997-10-12|TAKE BACK RETURN|REG AIR|nwind slow theodolites. carefully pending \n4166|140721|8264|1|8|14093.76|0.00|0.08|A|F|1993-06-05|1993-04-10|1993-07-05|COLLECT COD|MAIL|uickly. blithely pending de\n4166|92165|4675|2|8|9257.28|0.06|0.04|A|F|1993-06-07|1993-04-17|1993-06-16|DELIVER IN PERSON|REG AIR|es along the furiously regular acc\n4166|6324|8825|3|17|20915.44|0.02|0.06|R|F|1993-06-29|1993-05-15|1993-07-24|DELIVER IN PERSON|SHIP|ackages. re\n4166|85553|3078|4|36|55387.80|0.06|0.05|R|F|1993-03-01|1993-05-25|1993-03-05|COLLECT COD|MAIL|unts. furiously express accounts w\n4166|76379|1394|5|5|6776.85|0.08|0.01|A|F|1993-06-19|1993-04-24|1993-06-27|NONE|REG AIR|hely unusual packages are above the f\n4166|101840|4351|6|6|11051.04|0.04|0.08|R|F|1993-04-30|1993-04-17|1993-05-08|DELIVER IN PERSON|MAIL|ily ironic deposits print furiously. iron\n4166|23149|3150|7|26|27875.64|0.09|0.01|R|F|1993-03-17|1993-05-09|1993-03-25|NONE|MAIL|lar dependencies. s\n4167|60623|5636|1|47|74430.14|0.04|0.02|N|O|1998-08-02|1998-08-24|1998-08-28|DELIVER IN PERSON|REG AIR| carefully final asymptotes. slyly bo\n4167|86504|6505|2|17|25338.50|0.06|0.07|N|O|1998-09-18|1998-09-06|1998-10-07|COLLECT COD|REG AIR|ly around the even instr\n4167|72505|5013|3|1|1477.50|0.03|0.06|N|O|1998-10-11|1998-08-14|1998-10-13|COLLECT COD|TRUCK|xpress platelets. blithely \n4192|10837|8341|1|36|62921.88|0.06|0.08|N|O|1998-04-25|1998-05-26|1998-05-03|COLLECT COD|TRUCK|eodolites sleep\n4192|120696|3209|2|15|25750.35|0.04|0.08|N|O|1998-06-26|1998-05-26|1998-07-16|COLLECT COD|AIR|e slyly special grouches. express pinto b\n4192|134922|9949|3|7|13698.44|0.06|0.03|N|O|1998-05-19|1998-07-08|1998-05-31|COLLECT COD|FOB|y; excuses use. ironic, close instru\n4192|23256|8261|4|32|37736.00|0.09|0.04|N|O|1998-06-23|1998-06-25|1998-07-17|NONE|FOB|ounts are fluffily slyly bold req\n4192|47037|4550|5|48|47233.44|0.08|0.01|N|O|1998-08-17|1998-07-11|1998-09-03|NONE|AIR|ests. quickly bol\n4192|149567|2082|6|44|71128.64|0.10|0.02|N|O|1998-08-06|1998-07-09|1998-08-20|NONE|FOB|structions mai\n4192|169807|7356|7|27|50673.60|0.02|0.00|N|O|1998-07-03|1998-06-26|1998-07-13|TAKE BACK RETURN|AIR| carefully even escapades. care\n4193|130286|7826|1|37|48702.36|0.09|0.06|A|F|1994-04-25|1994-02-24|1994-05-08|NONE|AIR|er the quickly regular dependencies wake\n4193|116403|3937|2|3|4258.20|0.09|0.05|R|F|1994-04-29|1994-03-20|1994-05-29|TAKE BACK RETURN|REG AIR|osits above the depo\n4193|178441|8442|3|10|15194.40|0.06|0.03|A|F|1994-02-10|1994-03-22|1994-03-09|COLLECT COD|RAIL|uffily spe\n4193|50040|2546|4|29|28711.16|0.09|0.05|A|F|1994-02-11|1994-03-11|1994-03-13|TAKE BACK RETURN|RAIL|ly. final packages use blit\n4193|19877|4880|5|50|89843.50|0.01|0.01|R|F|1994-04-28|1994-03-23|1994-05-09|NONE|FOB| beans. regular accounts cajole. de\n4193|65248|2767|6|21|25478.04|0.02|0.04|R|F|1994-04-26|1994-03-22|1994-05-23|DELIVER IN PERSON|TRUCK|accounts cajole b\n4194|196755|4313|1|43|79625.25|0.08|0.06|A|F|1994-11-06|1994-12-09|1994-11-16|NONE|TRUCK|olites are after the exp\n4194|46577|1586|2|18|27424.26|0.07|0.07|A|F|1995-02-14|1994-12-04|1995-03-11|TAKE BACK RETURN|TRUCK|ld packages. quickly eve\n4195|5693|8194|1|14|22381.66|0.09|0.04|R|F|1993-09-06|1993-07-21|1993-09-18|DELIVER IN PERSON|REG AIR|ironic packages. carefully express\n4195|65973|3492|2|22|42657.34|0.10|0.08|R|F|1993-07-01|1993-07-23|1993-07-28|COLLECT COD|RAIL|lly express pinto bea\n4195|193646|1204|3|19|33053.16|0.01|0.06|R|F|1993-09-06|1993-08-13|1993-09-15|TAKE BACK RETURN|REG AIR|telets sleep even requests. final, even i\n4196|155079|7595|1|30|34022.10|0.02|0.06|N|O|1998-08-09|1998-06-30|1998-09-05|COLLECT COD|SHIP|egular foxes us\n4196|8234|5735|2|31|35409.13|0.09|0.08|N|O|1998-06-12|1998-07-28|1998-07-11|NONE|MAIL|ut the blithely ironic inst\n4196|177505|7506|3|46|72795.00|0.05|0.00|N|O|1998-09-05|1998-06-28|1998-09-10|TAKE BACK RETURN|MAIL|according to t\n4196|113898|6410|4|42|80299.38|0.04|0.06|N|O|1998-08-13|1998-07-18|1998-09-07|TAKE BACK RETURN|AIR| instructions. courts cajole slyly ev\n4196|71690|4198|5|3|4985.07|0.01|0.03|N|O|1998-05-17|1998-07-21|1998-05-18|DELIVER IN PERSON|TRUCK| accounts. fu\n4196|86060|8569|6|43|44980.58|0.01|0.06|N|O|1998-08-12|1998-07-12|1998-08-22|DELIVER IN PERSON|FOB|es. slyly even \n4196|3066|567|7|3|2907.18|0.00|0.06|N|O|1998-08-05|1998-07-28|1998-08-15|DELIVER IN PERSON|REG AIR|y regular packages haggle furiously alongs\n4197|128035|3060|1|50|53151.50|0.06|0.03|N|O|1996-11-15|1996-11-01|1996-11-20|NONE|FOB|. carefully bold asymptotes nag blithe\n4197|69664|2171|2|39|63712.74|0.02|0.08|N|O|1996-10-07|1996-10-11|1996-10-18|DELIVER IN PERSON|RAIL|ronic requests. quickly bold packages in\n4197|31921|4425|3|28|51881.76|0.06|0.02|N|O|1996-10-05|1996-10-24|1996-10-22|TAKE BACK RETURN|AIR|regular pin\n4197|95301|5302|4|23|29814.90|0.00|0.03|N|O|1996-09-10|1996-10-10|1996-09-25|NONE|AIR|l instructions print slyly past the reg\n4197|120952|3465|5|37|72999.15|0.03|0.04|N|O|1996-10-20|1996-10-10|1996-11-10|COLLECT COD|TRUCK|carefully enticing decoys boo\n4197|30658|3162|6|48|76255.20|0.08|0.00|N|O|1996-10-07|1996-10-25|1996-10-23|COLLECT COD|REG AIR| final instructions. blithe, spe\n4198|145163|192|1|48|57991.68|0.09|0.05|N|O|1997-09-03|1997-07-18|1997-09-11|NONE|REG AIR|cajole carefully final, ironic ide\n4198|142247|7276|2|46|59305.04|0.09|0.01|N|O|1997-08-17|1997-09-08|1997-09-11|COLLECT COD|TRUCK|posits among th\n4198|144524|2067|3|13|20390.76|0.03|0.04|N|O|1997-07-18|1997-07-24|1997-08-10|NONE|REG AIR| furious excuses. bli\n4199|69662|7181|1|16|26106.56|0.10|0.00|A|F|1992-06-11|1992-04-10|1992-07-10|COLLECT COD|TRUCK|ncies. furiously special accounts\n4199|8840|8841|2|18|31479.12|0.00|0.01|A|F|1992-06-01|1992-03-30|1992-06-28|DELIVER IN PERSON|RAIL|pending, regular accounts. carefully\n4224|198265|8266|1|27|36808.02|0.05|0.03|N|O|1997-09-05|1997-08-19|1997-09-30|NONE|SHIP|ly special deposits sleep qui\n4224|36701|4211|2|20|32754.00|0.07|0.05|N|O|1997-11-09|1997-08-23|1997-11-14|NONE|FOB|unts promise across the requests. blith\n4224|23726|1233|3|4|6598.88|0.08|0.05|N|O|1997-09-07|1997-09-05|1997-09-25|TAKE BACK RETURN|FOB| even dinos. carefull\n4224|159653|7199|4|50|85632.50|0.10|0.06|N|O|1997-07-30|1997-09-10|1997-08-19|COLLECT COD|RAIL|side of the carefully silent dep\n4224|84136|6645|5|48|53766.24|0.00|0.04|N|O|1997-10-03|1997-08-31|1997-10-10|NONE|RAIL| final, regular asymptotes use alway\n4225|48772|6285|1|25|43019.25|0.08|0.04|N|O|1997-07-10|1997-08-08|1997-07-31|TAKE BACK RETURN|TRUCK|se fluffily. busily ironic requests are;\n4225|95002|7512|2|23|22931.00|0.02|0.04|N|O|1997-09-18|1997-08-31|1997-10-11|TAKE BACK RETURN|RAIL|. quickly b\n4225|97926|436|3|28|53869.76|0.08|0.03|N|O|1997-07-11|1997-09-01|1997-08-03|DELIVER IN PERSON|FOB|ts are requests. even, bold depos\n4226|187065|9584|1|27|31105.62|0.06|0.08|A|F|1993-05-03|1993-04-12|1993-05-16|COLLECT COD|AIR|sly alongside of the slyly ironic pac\n4227|157199|9715|1|19|23867.61|0.01|0.08|A|F|1995-05-05|1995-05-03|1995-05-22|COLLECT COD|REG AIR|ns sleep along the blithely even theodolit\n4227|32622|2623|2|8|12436.96|0.09|0.00|N|F|1995-06-11|1995-04-30|1995-06-28|COLLECT COD|REG AIR| packages since the bold, u\n4227|74624|4625|3|11|17584.82|0.10|0.04|A|F|1995-03-30|1995-05-02|1995-04-26|DELIVER IN PERSON|SHIP|l requests-- bold requests cajole dogg\n4227|199283|6841|4|2|2764.56|0.02|0.05|R|F|1995-04-24|1995-05-09|1995-05-21|DELIVER IN PERSON|AIR|ep. specia\n4227|146835|4378|5|49|92209.67|0.05|0.06|R|F|1995-05-19|1995-04-12|1995-06-12|TAKE BACK RETURN|REG AIR|ts sleep blithely carefully unusual ideas.\n4228|140949|8492|1|20|39798.80|0.00|0.06|N|O|1997-04-24|1997-05-29|1997-05-17|NONE|RAIL|f the slyly fluffy pinto beans are\n4229|95318|337|1|44|57785.64|0.02|0.05|N|O|1998-05-29|1998-05-12|1998-06-16|DELIVER IN PERSON|AIR|s. carefully e\n4229|4570|7071|2|34|50135.38|0.07|0.05|N|O|1998-05-26|1998-04-13|1998-06-08|DELIVER IN PERSON|MAIL|thely final accounts use even packa\n4230|45124|2637|1|38|40626.56|0.10|0.03|A|F|1992-04-28|1992-04-21|1992-05-28|TAKE BACK RETURN|FOB|ly regular packages. regular ideas boost\n4230|198644|6202|2|43|74933.52|0.02|0.08|R|F|1992-03-14|1992-05-13|1992-03-28|NONE|FOB|ses lose blithely slyly final e\n4230|195055|94|3|10|11500.50|0.06|0.02|A|F|1992-06-11|1992-04-11|1992-07-02|TAKE BACK RETURN|MAIL|ar packages are \n4230|74334|4335|4|28|36633.24|0.01|0.03|R|F|1992-05-12|1992-05-10|1992-06-01|TAKE BACK RETURN|MAIL|nt instruct\n4230|124748|7261|5|50|88637.00|0.00|0.01|A|F|1992-03-29|1992-05-19|1992-04-20|TAKE BACK RETURN|SHIP|ts. final instructions in\n4230|34864|9871|6|30|53965.80|0.05|0.07|A|F|1992-03-11|1992-04-29|1992-03-30|NONE|AIR|s. final excuses across the\n4230|151041|1042|7|18|19656.72|0.10|0.04|R|F|1992-06-23|1992-05-10|1992-07-04|COLLECT COD|SHIP| the final acco\n4231|141535|1536|1|47|74096.91|0.09|0.03|N|O|1997-11-27|1998-01-26|1997-12-17|NONE|REG AIR|hely along the silent at\n4231|165189|222|2|4|5016.72|0.06|0.02|N|O|1997-11-28|1998-01-26|1997-12-12|TAKE BACK RETURN|MAIL|lithely even packages. \n4231|120334|335|3|31|41984.23|0.07|0.08|N|O|1998-02-14|1997-12-27|1998-03-01|DELIVER IN PERSON|FOB|ublate. theodoli\n4231|39030|4037|4|35|33916.05|0.10|0.00|N|O|1998-02-21|1998-01-24|1998-03-18|DELIVER IN PERSON|FOB|le quickly regular, unus\n4256|150384|2900|1|22|31556.36|0.05|0.05|R|F|1992-07-30|1992-05-14|1992-08-14|NONE|TRUCK|, final platelets are slyly final pint\n4257|64416|1935|1|3|4141.23|0.10|0.03|N|O|1995-06-18|1995-05-01|1995-07-12|DELIVER IN PERSON|MAIL|thin the theodolites use after the bl\n4257|34970|9977|2|5|9524.85|0.01|0.04|R|F|1995-04-29|1995-06-05|1995-05-13|TAKE BACK RETURN|TRUCK|n deposits. furiously e\n4257|127007|7008|3|33|34122.00|0.03|0.04|A|F|1995-05-23|1995-05-03|1995-05-31|COLLECT COD|AIR|uffily regular accounts ar\n4258|165025|5026|1|36|39240.72|0.02|0.06|N|O|1997-02-23|1997-01-25|1997-02-27|TAKE BACK RETURN|SHIP|ns use alongs\n4258|161733|4250|2|19|34099.87|0.03|0.02|N|O|1997-01-14|1996-12-12|1997-01-20|TAKE BACK RETURN|AIR|ly busily ironic foxes. f\n4258|30847|8357|3|46|81780.64|0.04|0.07|N|O|1997-01-02|1996-12-26|1997-01-12|DELIVER IN PERSON|AIR| furiously pend\n4258|34234|4235|4|22|25701.06|0.04|0.04|N|O|1996-12-12|1996-12-06|1996-12-20|TAKE BACK RETURN|AIR|e regular, even asym\n4258|162806|7839|5|9|16819.20|0.04|0.03|N|O|1996-12-04|1996-12-08|1996-12-20|DELIVER IN PERSON|TRUCK|counts wake permanently after the bravely\n4259|42595|7604|1|14|21526.26|0.05|0.03|N|O|1998-01-09|1997-11-21|1998-01-29|TAKE BACK RETURN|RAIL| furiously pending excuses. ideas hagg\n4260|23397|904|1|21|27728.19|0.08|0.04|R|F|1992-08-06|1992-06-18|1992-08-22|NONE|AIR|al, pending accounts must\n4261|109388|9389|1|12|16768.56|0.05|0.01|A|F|1992-11-01|1993-01-01|1992-11-12|NONE|FOB|into beans \n4261|81703|9228|2|4|6738.80|0.02|0.07|R|F|1992-12-11|1992-12-18|1992-12-24|DELIVER IN PERSON|FOB|ackages unwind furiously fluff\n4261|174248|6766|3|3|3966.72|0.07|0.02|R|F|1992-11-10|1992-12-14|1992-11-17|COLLECT COD|RAIL|ly even deposits eat blithely alo\n4261|173239|8274|4|36|47240.28|0.04|0.06|R|F|1992-12-02|1992-12-18|1992-12-25|NONE|REG AIR| slyly pendi\n4261|23678|1185|5|28|44846.76|0.07|0.06|A|F|1992-10-08|1992-12-23|1992-10-11|TAKE BACK RETURN|MAIL|packages. fluffily i\n4262|75546|5547|1|30|45646.20|0.01|0.03|N|O|1996-08-11|1996-10-11|1996-09-09|TAKE BACK RETURN|RAIL|tes after the carefully\n4262|95696|5697|2|5|8458.45|0.02|0.05|N|O|1996-09-27|1996-09-05|1996-10-25|COLLECT COD|SHIP|blithely final asymptotes integrate\n4262|161033|3550|3|5|5470.15|0.08|0.00|N|O|1996-10-02|1996-10-16|1996-10-05|NONE|REG AIR|ironic accounts are unusu\n4262|73333|855|4|45|58784.85|0.02|0.01|N|O|1996-11-09|1996-09-09|1996-11-12|DELIVER IN PERSON|SHIP|ackages boost. pending, even instruction\n4262|99103|4122|5|28|30858.80|0.06|0.02|N|O|1996-10-22|1996-09-06|1996-11-13|DELIVER IN PERSON|FOB|ironic, regular depend\n4262|16420|3924|6|26|34746.92|0.03|0.02|N|O|1996-08-29|1996-09-25|1996-08-31|NONE|RAIL|s boost slyly along the bold, iro\n4262|159435|4466|7|41|61271.63|0.03|0.01|N|O|1996-08-28|1996-09-14|1996-09-20|COLLECT COD|RAIL|cuses unwind ac\n4263|17017|7018|1|9|8406.09|0.08|0.07|N|O|1998-04-04|1998-04-29|1998-05-04|COLLECT COD|AIR|structions cajole quic\n4263|195562|3120|2|28|46411.68|0.05|0.03|N|O|1998-06-24|1998-06-08|1998-07-14|NONE|MAIL|ideas for the carefully re\n4263|10259|7763|3|38|44431.50|0.01|0.01|N|O|1998-07-10|1998-05-08|1998-07-17|NONE|TRUCK|rding to the dep\n4263|18148|650|4|20|21322.80|0.02|0.07|N|O|1998-04-09|1998-04-30|1998-05-04|NONE|RAIL|uietly regular deposits. sly deposits w\n4263|197901|5459|5|14|27984.60|0.09|0.06|N|O|1998-05-06|1998-04-17|1998-05-11|DELIVER IN PERSON|TRUCK|d accounts. daringly regular accounts hagg\n4263|112955|7978|6|47|92493.65|0.08|0.06|N|O|1998-06-28|1998-05-09|1998-07-02|DELIVER IN PERSON|TRUCK|y. theodolites wake idly ironic do\n4263|28779|1282|7|6|10246.62|0.04|0.04|N|O|1998-05-01|1998-06-02|1998-05-14|TAKE BACK RETURN|REG AIR|g the final, regular instructions: \n4288|73060|3061|1|32|33057.92|0.10|0.07|R|F|1993-03-19|1993-01-26|1993-04-18|TAKE BACK RETURN|AIR|e blithely even instructions. speci\n4288|104952|4953|2|39|76321.05|0.05|0.02|R|F|1993-03-25|1993-02-06|1993-03-28|DELIVER IN PERSON|AIR|uffy theodolites run\n4288|124632|2169|3|7|11596.41|0.03|0.01|A|F|1993-01-15|1993-02-05|1993-01-26|NONE|TRUCK|ngside of the special platelet\n4289|195619|5620|1|19|32577.59|0.06|0.06|R|F|1993-12-31|1993-11-06|1994-01-23|DELIVER IN PERSON|TRUCK|e carefully regular ideas. sl\n4290|136795|9309|1|23|42131.17|0.06|0.04|R|F|1995-04-04|1995-02-16|1995-04-07|TAKE BACK RETURN|REG AIR|uests cajole carefully.\n4290|98378|3397|2|3|4129.11|0.09|0.03|A|F|1995-03-25|1995-03-07|1995-04-11|NONE|RAIL|lar platelets cajole\n4291|191809|9367|1|3|5702.40|0.08|0.08|A|F|1994-03-17|1994-02-21|1994-03-27|COLLECT COD|SHIP|tes sleep slyly above the quickly sl\n4291|124889|2426|2|43|82296.84|0.01|0.06|A|F|1994-02-01|1994-02-27|1994-02-06|DELIVER IN PERSON|REG AIR|s. quietly regular \n4291|7976|477|3|25|47099.25|0.09|0.08|R|F|1994-02-14|1994-02-08|1994-03-15|COLLECT COD|AIR|uctions. furiously regular ins\n4292|43509|1022|1|22|31955.00|0.08|0.03|R|F|1992-02-14|1992-02-16|1992-03-01|NONE|FOB|refully expres\n4292|39824|7334|2|1|1763.82|0.03|0.01|A|F|1992-02-07|1992-03-16|1992-02-10|DELIVER IN PERSON|FOB| the furiously ev\n4292|119234|6768|3|35|43863.05|0.03|0.06|A|F|1992-03-23|1992-04-04|1992-04-02|COLLECT COD|TRUCK|dugouts use. furiously bold packag\n4292|162381|7414|4|40|57735.20|0.05|0.04|A|F|1992-04-27|1992-03-07|1992-05-04|COLLECT COD|REG AIR|ounts according to the furiously \n4292|130292|7832|5|6|7933.74|0.07|0.08|R|F|1992-03-03|1992-02-24|1992-03-25|COLLECT COD|FOB|bove the silently regula\n4292|3041|542|6|47|44369.88|0.05|0.00|R|F|1992-05-02|1992-03-21|1992-05-27|TAKE BACK RETURN|FOB|y packages; even ideas boost\n4293|957|5958|1|34|63170.30|0.03|0.08|N|O|1996-11-05|1996-10-12|1996-12-04|NONE|FOB|ions sleep blithely on\n4293|76837|4359|2|50|90691.50|0.01|0.05|N|O|1996-11-27|1996-10-30|1996-12-22|COLLECT COD|MAIL| special deposits. furiousl\n4293|198566|1086|3|47|78234.32|0.08|0.02|N|O|1996-09-07|1996-10-24|1996-09-15|NONE|RAIL|ithely pending deposits af\n4293|87832|5357|4|25|45495.75|0.04|0.04|N|O|1996-09-11|1996-11-14|1996-09-22|DELIVER IN PERSON|FOB|inal asympt\n4293|180656|3175|5|1|1736.65|0.06|0.05|N|O|1996-11-15|1996-10-09|1996-11-26|COLLECT COD|AIR|eposits should boost along the \n4293|78993|6515|6|45|88739.55|0.10|0.04|N|O|1996-11-04|1996-11-06|1996-11-23|NONE|MAIL|lar ideas use carefully\n4294|104911|7422|1|19|36402.29|0.03|0.04|A|F|1992-10-16|1992-11-13|1992-10-26|DELIVER IN PERSON|AIR|nt dependencies. furiously regular ideas d\n4294|26561|9064|2|16|23800.96|0.01|0.02|R|F|1992-08-17|1992-09-24|1992-09-04|TAKE BACK RETURN|REG AIR|lithely pint\n4294|197032|2071|3|30|33870.90|0.01|0.00|A|F|1992-09-12|1992-11-06|1992-09-25|NONE|MAIL|olites. bold foxes affix ironic theodolite\n4294|104405|1936|4|34|47919.60|0.02|0.01|R|F|1992-09-09|1992-11-06|1992-10-04|TAKE BACK RETURN|REG AIR|pendencies!\n4294|118956|1468|5|37|73073.15|0.05|0.01|R|F|1992-09-07|1992-10-13|1992-09-08|NONE|REG AIR|cial packages nag f\n4294|86273|8782|6|42|52889.34|0.02|0.03|A|F|1992-09-30|1992-11-13|1992-10-15|DELIVER IN PERSON|FOB| carefully; furiously ex\n4294|174787|2339|7|47|87503.66|0.02|0.08|R|F|1992-11-09|1992-11-03|1992-12-05|TAKE BACK RETURN|SHIP|es. blithely r\n4295|28410|5917|1|49|65582.09|0.09|0.01|N|O|1996-05-25|1996-03-17|1996-06-19|TAKE BACK RETURN|REG AIR|refully silent requests. f\n4295|70554|8076|2|4|6098.20|0.09|0.07|N|O|1996-06-05|1996-04-26|1996-06-13|DELIVER IN PERSON|TRUCK|arefully according to the pending ac\n4295|192627|2628|3|3|5158.86|0.04|0.00|N|O|1996-06-04|1996-04-24|1996-06-24|DELIVER IN PERSON|AIR|telets cajole bravely\n4295|79439|4454|4|30|42552.90|0.07|0.06|N|O|1996-03-22|1996-04-23|1996-04-20|NONE|SHIP|yly ironic frets. pending foxes after \n4320|45894|3407|1|28|51516.92|0.02|0.06|N|O|1997-01-28|1997-02-07|1997-02-07|COLLECT COD|FOB|nts. even, ironic excuses hagg\n4320|139168|1682|2|6|7242.96|0.08|0.08|N|O|1997-01-11|1997-01-26|1997-01-22|DELIVER IN PERSON|SHIP|against the carefully careful asym\n4320|187628|2665|3|33|56615.46|0.09|0.02|N|O|1996-12-11|1997-02-27|1997-01-08|TAKE BACK RETURN|SHIP|ess asymptotes so\n4321|146895|4438|1|33|64082.37|0.09|0.02|A|F|1994-09-01|1994-08-17|1994-09-05|DELIVER IN PERSON|TRUCK|yly special excuses. fluffily \n4321|53251|5757|2|45|54191.25|0.00|0.08|R|F|1994-11-13|1994-09-15|1994-11-18|DELIVER IN PERSON|SHIP| haggle ironically bold theodolites. quick\n4321|185298|5299|3|23|31815.67|0.01|0.05|A|F|1994-11-03|1994-10-08|1994-11-06|DELIVER IN PERSON|SHIP|ly even orbits slee\n4321|90271|272|4|4|5045.08|0.02|0.00|R|F|1994-09-10|1994-10-06|1994-09-11|NONE|FOB|ironic deposi\n4321|171625|4143|5|10|16966.20|0.04|0.03|A|F|1994-09-07|1994-08-23|1994-09-17|TAKE BACK RETURN|SHIP|wake carefully alongside of \n4322|68766|6285|1|39|67655.64|0.04|0.02|N|O|1998-04-27|1998-06-03|1998-05-04|TAKE BACK RETURN|MAIL|its integrate fluffily \n4322|139800|9801|2|9|16558.20|0.05|0.08|N|O|1998-05-18|1998-04-27|1998-05-28|COLLECT COD|AIR|ual instructio\n4322|7548|7549|3|12|17466.48|0.09|0.05|N|O|1998-03-29|1998-06-05|1998-04-16|DELIVER IN PERSON|TRUCK|e blithely against the slyly unusu\n4322|45880|5881|4|17|31039.96|0.09|0.08|N|O|1998-05-31|1998-05-31|1998-06-10|TAKE BACK RETURN|FOB|ructions boost \n4322|101331|6352|5|10|13323.30|0.00|0.05|N|O|1998-05-31|1998-04-27|1998-06-25|TAKE BACK RETURN|REG AIR| regular ideas engage carefully quick\n4322|59477|1983|6|39|56022.33|0.09|0.08|N|O|1998-03-16|1998-05-21|1998-04-11|COLLECT COD|AIR|ccounts. dogged pin\n4322|13150|654|7|34|36147.10|0.05|0.00|N|O|1998-05-27|1998-04-12|1998-06-16|NONE|REG AIR|ounts haggle fluffily ideas. pend\n4323|358|359|1|33|41525.55|0.09|0.02|A|F|1994-05-04|1994-03-06|1994-05-23|COLLECT COD|TRUCK|the slyly bold deposits slee\n4324|50292|293|1|44|54660.76|0.05|0.04|N|O|1995-10-15|1995-09-07|1995-11-07|DELIVER IN PERSON|AIR|ainst the u\n4324|47117|4630|2|12|12769.32|0.04|0.02|N|O|1995-10-05|1995-09-07|1995-10-18|NONE|REG AIR|c packages. furiously express sauternes\n4324|81374|8899|3|14|18975.18|0.07|0.06|N|O|1995-11-12|1995-08-26|1995-11-21|COLLECT COD|AIR| packages nag express excuses. qui\n4324|49985|2490|4|14|27089.72|0.02|0.04|N|O|1995-09-20|1995-10-08|1995-10-06|COLLECT COD|RAIL| express ideas. blithely blit\n4324|83480|5989|5|22|32196.56|0.07|0.03|N|O|1995-09-13|1995-10-04|1995-09-23|DELIVER IN PERSON|SHIP|ke express, special ideas.\n4324|42432|9945|6|31|42607.33|0.08|0.04|N|O|1995-10-23|1995-09-14|1995-11-09|COLLECT COD|RAIL|efully flu\n4324|153018|564|7|46|49266.46|0.00|0.03|N|O|1995-11-03|1995-09-28|1995-11-22|NONE|SHIP|ular, final theodo\n4325|159610|7156|1|18|30052.98|0.01|0.07|N|O|1996-10-07|1996-09-28|1996-10-31|DELIVER IN PERSON|RAIL|. blithely\n4326|162728|2729|1|11|19697.92|0.01|0.01|N|O|1997-02-02|1996-12-10|1997-02-20|DELIVER IN PERSON|TRUCK|press reque\n4326|166954|9471|2|27|54565.65|0.06|0.01|N|O|1996-11-29|1997-01-20|1996-12-23|COLLECT COD|AIR|inal packages. final asymptotes about t\n4327|94813|9832|1|18|32540.58|0.08|0.00|N|F|1995-06-16|1995-04-20|1995-07-12|COLLECT COD|RAIL|y final excuses. ironic, special requests a\n4327|105917|8428|2|40|76916.40|0.07|0.01|N|F|1995-05-26|1995-04-17|1995-06-18|NONE|AIR|quests. packages are after th\n4327|144418|6933|3|11|16086.51|0.10|0.07|R|F|1995-04-24|1995-05-27|1995-05-24|TAKE BACK RETURN|FOB| ironic dolphins\n4327|20003|5008|4|8|7384.00|0.04|0.08|N|F|1995-05-26|1995-05-28|1995-06-19|DELIVER IN PERSON|AIR|eodolites cajole; unusual Tiresias\n4327|189038|9039|5|39|43954.17|0.01|0.00|N|O|1995-06-23|1995-04-18|1995-07-13|TAKE BACK RETURN|FOB|kages against the blit\n4327|151058|8604|6|10|11090.50|0.00|0.06|A|F|1995-04-28|1995-06-11|1995-05-07|TAKE BACK RETURN|TRUCK|arefully sile\n4352|105786|8297|1|18|32252.04|0.00|0.03|N|O|1998-02-27|1998-02-02|1998-03-01|DELIVER IN PERSON|RAIL|ding to th\n4353|93627|1155|1|22|35653.64|0.05|0.05|N|O|1998-01-19|1998-01-23|1998-02-10|COLLECT COD|FOB|ent packages. accounts are slyly. \n4354|14218|6720|1|30|33966.30|0.08|0.07|R|F|1995-01-27|1994-11-24|1995-02-25|TAKE BACK RETURN|REG AIR|around the ir\n4354|152107|7138|2|23|26659.30|0.01|0.08|R|F|1994-11-20|1994-12-23|1994-11-27|TAKE BACK RETURN|AIR|kly along the ironic, ent\n4354|50734|5745|3|2|3369.46|0.10|0.04|A|F|1995-01-09|1994-12-15|1995-01-24|TAKE BACK RETURN|REG AIR|s nag quickly \n4354|85171|2696|4|36|41622.12|0.05|0.05|A|F|1994-11-20|1994-12-06|1994-12-06|DELIVER IN PERSON|AIR| wake slyly eve\n4354|64313|1832|5|37|47260.47|0.06|0.02|R|F|1995-01-13|1994-12-29|1995-01-31|DELIVER IN PERSON|FOB|deas use blithely! special foxes print af\n4354|107393|2414|6|36|50414.04|0.03|0.04|R|F|1994-12-03|1994-12-05|1995-01-02|TAKE BACK RETURN|TRUCK|efully special packages use fluffily\n4354|138728|1242|7|18|31800.96|0.03|0.04|A|F|1994-12-07|1994-12-11|1994-12-11|TAKE BACK RETURN|SHIP|ross the furiously \n4355|194040|6560|1|32|36289.28|0.10|0.02|N|O|1996-12-29|1997-02-08|1997-01-24|DELIVER IN PERSON|REG AIR|y silent deposits. b\n4355|16565|9067|2|4|5926.24|0.05|0.02|N|O|1997-02-25|1997-01-29|1997-03-17|TAKE BACK RETURN|TRUCK|slyly blithely regular packag\n4355|738|739|3|13|21303.49|0.07|0.05|N|O|1997-01-21|1996-12-22|1997-02-14|COLLECT COD|TRUCK| ought to mold. blithely pending ideas \n4355|193886|6406|4|14|27718.32|0.04|0.02|N|O|1997-03-08|1997-01-22|1997-03-26|NONE|RAIL|he furiously ironic accounts. quickly iro\n4355|30531|8041|5|50|73076.50|0.10|0.00|N|O|1996-11-25|1997-01-01|1996-12-06|DELIVER IN PERSON|REG AIR| regular accounts boost along the \n4355|121860|4373|6|35|65865.10|0.00|0.08|N|O|1997-01-28|1997-01-28|1997-02-20|NONE|FOB|ess accounts affix ironic\n4355|100815|3326|7|47|85343.07|0.09|0.02|N|O|1996-12-28|1996-12-29|1997-01-09|NONE|RAIL|e. realms integrate \n4356|193770|3771|1|35|65231.95|0.00|0.04|R|F|1994-05-30|1994-06-14|1994-06-08|COLLECT COD|MAIL|arefully ironic \n4357|83306|831|1|50|64465.00|0.04|0.07|N|O|1997-11-25|1997-12-03|1997-12-17|DELIVER IN PERSON|RAIL|s. final, e\n4357|107896|7897|2|17|32366.13|0.10|0.07|N|O|1998-02-01|1997-12-08|1998-02-09|DELIVER IN PERSON|MAIL|e carefully furiou\n4358|125030|55|1|47|49586.41|0.04|0.00|N|O|1997-10-15|1997-10-14|1997-11-04|DELIVER IN PERSON|SHIP|refully busy dep\n4359|173759|8794|1|41|75142.75|0.03|0.07|A|F|1993-04-06|1993-05-06|1993-04-14|COLLECT COD|RAIL|s affix sly\n4359|152794|7825|2|8|14774.32|0.03|0.08|R|F|1993-06-27|1993-05-16|1993-07-04|DELIVER IN PERSON|MAIL|packages affix. fluffily regular f\n4359|192211|7250|3|32|41702.72|0.10|0.03|R|F|1993-06-18|1993-04-04|1993-07-18|COLLECT COD|MAIL|olites nag quietly caref\n4359|77506|14|4|1|1483.50|0.05|0.03|R|F|1993-04-27|1993-05-09|1993-05-08|NONE|MAIL| fluffily ironic, bold pac\n4359|32507|7514|5|22|31669.00|0.04|0.01|A|F|1993-03-28|1993-06-01|1993-04-13|NONE|REG AIR|accounts wake ironic deposits. ironic\n4384|135485|5486|1|5|7602.40|0.09|0.01|A|F|1992-08-22|1992-08-24|1992-09-20|DELIVER IN PERSON|MAIL|instructions sleep. blithely express pa\n4384|88695|6220|2|38|63980.22|0.07|0.06|A|F|1992-10-18|1992-09-24|1992-11-04|NONE|FOB|ly final requests. regu\n4384|88921|6446|3|11|21009.12|0.05|0.04|R|F|1992-08-31|1992-10-04|1992-09-28|TAKE BACK RETURN|FOB|deposits promise carefully even, regular e\n4385|110994|6017|1|38|76189.62|0.00|0.02|N|O|1996-11-22|1996-10-30|1996-12-21|DELIVER IN PERSON|TRUCK|inal frays. final, bold exc\n4386|129394|6931|1|10|14233.90|0.05|0.07|N|O|1998-06-03|1998-04-16|1998-06-28|TAKE BACK RETURN|MAIL|gainst the quickly expre\n4386|117055|9567|2|28|30017.40|0.03|0.06|N|O|1998-03-19|1998-05-01|1998-03-27|NONE|FOB|. quick packages play slyly \n4386|139628|4655|3|4|6670.48|0.07|0.05|N|O|1998-04-07|1998-03-25|1998-04-19|COLLECT COD|FOB|ns wake carefully carefully iron\n4386|120458|459|4|21|31047.45|0.09|0.00|N|O|1998-05-05|1998-03-19|1998-05-13|NONE|RAIL|e pending, sp\n4386|129214|6751|5|39|48485.19|0.09|0.06|N|O|1998-03-05|1998-03-15|1998-03-16|NONE|RAIL|structions cajole quickly express\n4386|89453|9454|6|18|25964.10|0.02|0.05|N|O|1998-04-12|1998-04-09|1998-05-12|TAKE BACK RETURN|SHIP| deposits use according to the pending, \n4386|19821|2323|7|16|27853.12|0.07|0.02|N|O|1998-05-05|1998-03-17|1998-06-03|COLLECT COD|AIR|e furiously final pint\n4387|121746|9283|1|3|5303.22|0.02|0.01|N|O|1996-01-17|1996-01-14|1996-01-28|COLLECT COD|AIR| boost slyly ironic instructions. furiou\n4387|176820|4372|2|48|91047.36|0.06|0.05|N|O|1995-10-29|1995-12-11|1995-11-01|NONE|REG AIR|sleep slyly. blithely sl\n4387|1622|4123|3|15|22854.30|0.00|0.03|N|O|1996-01-11|1996-01-14|1996-01-30|TAKE BACK RETURN|REG AIR|s hinder quietly across the pla\n4387|46843|6844|4|9|16108.56|0.00|0.03|N|O|1996-01-04|1995-12-26|1996-01-12|DELIVER IN PERSON|REG AIR|c ideas. slyly regular packages sol\n4387|81324|6341|5|3|3915.96|0.05|0.08|N|O|1995-11-17|1995-12-28|1995-11-25|COLLECT COD|SHIP| pinto beans \n4387|5012|2513|6|40|36680.40|0.02|0.04|N|O|1995-11-29|1995-12-10|1995-12-20|NONE|REG AIR|deas according to the blithely regular fox\n4388|64943|2462|1|30|57238.20|0.02|0.07|N|O|1996-06-07|1996-05-07|1996-06-22|DELIVER IN PERSON|FOB|s cajole fluffil\n4388|83255|8272|2|28|34671.00|0.05|0.04|N|O|1996-05-08|1996-06-20|1996-05-12|TAKE BACK RETURN|RAIL|ove the ide\n4388|51377|8893|3|13|17268.81|0.07|0.05|N|O|1996-06-28|1996-05-23|1996-07-04|DELIVER IN PERSON|REG AIR|ly even, expre\n4389|156730|1761|1|20|35734.60|0.08|0.00|A|F|1994-06-06|1994-06-17|1994-06-17|DELIVER IN PERSON|SHIP|ng the carefully express d\n4389|152822|368|2|13|24372.66|0.00|0.00|A|F|1994-08-18|1994-06-06|1994-08-20|NONE|RAIL|nal, regula\n4389|78001|509|3|39|38181.00|0.04|0.07|A|F|1994-06-08|1994-06-04|1994-06-10|TAKE BACK RETURN|TRUCK| unusual, final excuses cajole carefully \n4389|159554|7100|4|5|8067.75|0.09|0.00|A|F|1994-09-03|1994-06-23|1994-09-16|NONE|FOB| ironic request\n4389|10461|2963|5|22|30172.12|0.08|0.00|R|F|1994-07-05|1994-06-12|1994-07-12|NONE|TRUCK|lly silent de\n4389|1428|1429|6|22|29247.24|0.01|0.04|R|F|1994-06-07|1994-06-29|1994-06-19|COLLECT COD|TRUCK|at the final excuses hinder carefully a\n4389|184028|4029|7|4|4448.08|0.09|0.08|R|F|1994-06-14|1994-06-30|1994-07-06|NONE|REG AIR| blithely even d\n4390|151140|3656|1|35|41689.90|0.07|0.04|R|F|1995-05-30|1995-07-02|1995-06-15|DELIVER IN PERSON|TRUCK|ongside of the slyly regular ideas\n4390|195068|7588|2|28|32565.68|0.03|0.00|N|O|1995-09-07|1995-06-22|1995-10-05|COLLECT COD|SHIP|ld braids haggle atop the for\n4390|100762|8293|3|42|74035.92|0.05|0.08|A|F|1995-06-12|1995-07-16|1995-06-17|NONE|AIR|arefully even accoun\n4390|97112|4640|4|32|35491.52|0.07|0.08|N|O|1995-09-15|1995-08-12|1995-10-05|TAKE BACK RETURN|TRUCK|ctions across\n4391|160992|3509|1|1|2052.99|0.09|0.00|R|F|1992-06-18|1992-04-27|1992-06-20|COLLECT COD|TRUCK|ong the silent deposits\n4391|186943|4498|2|45|91347.30|0.07|0.04|R|F|1992-04-01|1992-05-01|1992-04-13|TAKE BACK RETURN|TRUCK|ep quickly after \n4416|93919|8938|1|37|70777.67|0.08|0.03|A|F|1992-10-23|1992-08-23|1992-11-16|COLLECT COD|RAIL|fluffily ironic \n4416|88976|8977|2|3|5894.91|0.06|0.03|R|F|1992-10-22|1992-08-06|1992-11-13|DELIVER IN PERSON|SHIP| requests sleep along the \n4416|8657|6158|3|45|70454.25|0.09|0.03|A|F|1992-10-16|1992-09-09|1992-10-28|COLLECT COD|AIR|the final pinto beans. special frets \n4417|74480|6988|1|28|40725.44|0.08|0.02|N|O|1998-09-04|1998-10-04|1998-09-19|TAKE BACK RETURN|REG AIR|ies across the furious\n4417|180729|5766|2|1|1809.72|0.06|0.08|N|O|1998-10-23|1998-08-22|1998-10-24|NONE|REG AIR|press deposits promise stealthily amo\n4417|97701|5229|3|35|59454.50|0.06|0.04|N|O|1998-08-08|1998-09-23|1998-09-02|DELIVER IN PERSON|FOB|slyly regular, silent courts. even packag\n4418|34773|7277|1|32|54648.64|0.02|0.06|A|F|1993-05-28|1993-06-02|1993-05-30|TAKE BACK RETURN|RAIL|ly. bold pinto b\n4418|21913|9420|2|14|25688.74|0.03|0.04|A|F|1993-05-20|1993-06-18|1993-06-05|TAKE BACK RETURN|SHIP| blithely regular requests. blith\n4418|78027|5549|3|3|3015.06|0.00|0.02|R|F|1993-04-08|1993-06-04|1993-05-02|NONE|SHIP|luffily across the unusual ideas. reque\n4419|107398|7399|1|45|63242.55|0.01|0.05|N|O|1996-07-20|1996-09-07|1996-08-18|DELIVER IN PERSON|TRUCK|s doze sometimes fluffily regular a\n4419|31638|4142|2|42|65924.46|0.00|0.03|N|O|1996-09-18|1996-07-25|1996-09-21|COLLECT COD|RAIL|sts. furious\n4419|131738|6765|3|6|10618.38|0.02|0.08|N|O|1996-06-25|1996-09-04|1996-07-20|DELIVER IN PERSON|AIR|ts wake slyly final dugou\n4420|7317|4818|1|7|8570.17|0.07|0.03|R|F|1994-08-30|1994-09-03|1994-09-25|NONE|FOB| regular instructions sleep around\n4421|97523|5051|1|37|56259.24|0.09|0.08|N|O|1997-07-22|1997-06-27|1997-07-25|DELIVER IN PERSON|SHIP|l accounts. ironic request\n4421|55380|391|2|46|61427.48|0.04|0.04|N|O|1997-04-21|1997-05-13|1997-05-15|DELIVER IN PERSON|FOB|reful packages. bold, \n4421|166181|8698|3|46|57370.28|0.00|0.06|N|O|1997-05-25|1997-05-21|1997-06-23|COLLECT COD|TRUCK|g dependenci\n4421|190694|5733|4|32|57110.08|0.06|0.04|N|O|1997-07-09|1997-06-03|1997-07-25|NONE|SHIP|ar ideas eat among the furiousl\n4421|189912|9913|5|32|64061.12|0.06|0.04|N|O|1997-07-28|1997-06-14|1997-08-13|NONE|REG AIR|uickly final pinto beans impress. bold \n4421|46583|4096|6|44|67301.52|0.09|0.06|N|O|1997-06-17|1997-06-20|1997-06-29|NONE|TRUCK|le carefully. bl\n4421|115306|329|7|18|23783.40|0.01|0.00|N|O|1997-06-07|1997-05-13|1997-06-10|DELIVER IN PERSON|FOB|. regular, s\n4422|134519|2059|1|5|7767.55|0.09|0.07|N|O|1995-07-17|1995-08-13|1995-07-25|NONE|SHIP|e furiously about t\n4422|47451|9956|2|41|57336.45|0.08|0.05|N|F|1995-06-12|1995-07-09|1995-06-20|COLLECT COD|TRUCK| theodolites shal\n4422|102348|9879|3|39|52663.26|0.00|0.05|N|O|1995-09-02|1995-06-24|1995-09-14|NONE|TRUCK|en hockey players engage\n4422|152192|2193|4|4|4976.76|0.02|0.05|N|O|1995-09-18|1995-08-12|1995-10-18|COLLECT COD|FOB|cies along the bo\n4422|79692|4707|5|20|33433.80|0.07|0.05|N|O|1995-08-17|1995-07-16|1995-09-13|DELIVER IN PERSON|RAIL|ructions wake slyly al\n4423|149239|6782|1|3|3864.69|0.03|0.00|A|F|1995-03-22|1995-04-06|1995-04-19|NONE|TRUCK| final theodolites nag after the bli\n4423|59576|4587|2|2|3071.14|0.07|0.04|A|F|1995-03-04|1995-04-04|1995-03-08|TAKE BACK RETURN|REG AIR|old sheaves sleep\n4448|51574|6585|1|24|36613.68|0.10|0.07|N|O|1998-09-09|1998-07-06|1998-09-27|DELIVER IN PERSON|SHIP|nal packages along the ironic instructi\n4448|188627|1146|2|13|22303.06|0.00|0.01|N|O|1998-07-26|1998-07-03|1998-08-14|COLLECT COD|MAIL|fluffily express accounts integrate furiou\n4448|40980|5989|3|35|67234.30|0.10|0.06|N|O|1998-09-18|1998-07-27|1998-10-08|NONE|REG AIR|aggle carefully alongside of the q\n4448|140138|139|4|3|3534.39|0.01|0.01|N|O|1998-07-20|1998-07-10|1998-08-07|DELIVER IN PERSON|TRUCK|ronic theod\n4448|90140|141|5|41|46335.74|0.00|0.08|N|O|1998-07-30|1998-08-09|1998-08-03|NONE|AIR|pon the permanently even excuses nag \n4448|171401|1402|6|12|17668.80|0.06|0.03|N|O|1998-08-21|1998-06-30|1998-09-09|COLLECT COD|RAIL|sits about the ironic, bu\n4449|31484|6491|1|42|59450.16|0.10|0.07|N|O|1998-03-22|1998-05-09|1998-04-03|NONE|FOB| packages. blithely final \n4449|140638|3153|2|10|16786.30|0.02|0.03|N|O|1998-05-09|1998-05-04|1998-05-15|NONE|SHIP|ccounts alongside of the platelets integr\n4450|173191|3192|1|44|55624.36|0.10|0.00|N|O|1997-10-12|1997-10-13|1997-10-29|DELIVER IN PERSON|RAIL| the slyly eve\n4450|14714|4715|2|9|14658.39|0.03|0.03|N|O|1997-08-13|1997-08-16|1997-08-15|NONE|FOB|gular requests cajole carefully. regular c\n4450|95500|8010|3|45|67297.50|0.08|0.01|N|O|1997-09-01|1997-10-06|1997-09-19|NONE|TRUCK|express ideas are furiously regular\n4450|61930|6943|4|13|24595.09|0.00|0.00|N|O|1997-08-26|1997-09-18|1997-09-20|COLLECT COD|MAIL| brave foxes. slyly unusual\n4450|55040|5041|5|6|5970.24|0.09|0.01|N|O|1997-09-02|1997-09-30|1997-09-09|NONE|FOB|eposits. foxes cajole unusual fox\n4451|163032|3033|1|40|43801.20|0.03|0.03|A|F|1994-11-18|1994-12-25|1994-11-26|DELIVER IN PERSON|RAIL|y. slyly special deposits are sly\n4451|62282|2283|2|34|42305.52|0.10|0.02|A|F|1994-11-30|1994-12-04|1994-12-13|COLLECT COD|SHIP| regular ideas.\n4451|158586|8587|3|19|31247.02|0.05|0.06|R|F|1994-10-09|1994-11-26|1994-10-23|COLLECT COD|FOB|ly after the fluffi\n4452|113429|5941|1|21|30290.82|0.07|0.03|R|F|1994-10-06|1994-08-23|1994-10-15|COLLECT COD|TRUCK|multipliers x-ray carefully in place of \n4452|149|7650|2|47|49309.58|0.01|0.06|A|F|1994-10-08|1994-08-09|1994-10-09|TAKE BACK RETURN|TRUCK|ts. slyly regular cour\n4453|146971|2000|1|41|82736.77|0.00|0.08|N|O|1997-07-17|1997-05-15|1997-07-31|NONE|REG AIR|anent theodolites are slyly except t\n4453|132711|2712|2|16|27899.36|0.03|0.00|N|O|1997-07-22|1997-05-05|1997-08-03|COLLECT COD|FOB|ar excuses nag quickly even accounts. b\n4453|61826|9345|3|48|85815.36|0.02|0.07|N|O|1997-05-29|1997-06-24|1997-06-03|NONE|SHIP|eep. fluffily express accounts at the furi\n4453|101706|4217|4|26|44400.20|0.06|0.07|N|O|1997-05-07|1997-06-07|1997-05-22|NONE|TRUCK|express packages are\n4454|150851|3367|1|20|38037.00|0.10|0.08|R|F|1994-05-06|1994-03-17|1994-05-20|COLLECT COD|SHIP|lar theodolites. even instructio\n4454|151032|3548|2|22|23826.66|0.06|0.02|A|F|1994-02-06|1994-04-11|1994-03-06|DELIVER IN PERSON|RAIL|ully. carefully final accounts accordi\n4454|191552|1553|3|45|73959.75|0.07|0.04|A|F|1994-03-29|1994-03-26|1994-04-04|TAKE BACK RETURN|RAIL|ests promise. packages print fur\n4454|1324|1325|4|1|1225.32|0.09|0.05|A|F|1994-02-05|1994-04-19|1994-02-12|COLLECT COD|RAIL|equests run.\n4454|51060|8576|5|48|48530.88|0.00|0.07|R|F|1994-04-23|1994-04-03|1994-04-26|COLLECT COD|FOB|to beans wake across th\n4454|159632|2148|6|20|33832.60|0.10|0.03|A|F|1994-04-08|1994-03-06|1994-04-26|DELIVER IN PERSON|TRUCK|quickly regular requests. furiously\n4455|69731|7250|1|20|34014.60|0.01|0.05|A|F|1994-01-31|1993-11-21|1994-03-02|DELIVER IN PERSON|MAIL| express packages. packages boost quickly\n4455|152587|2588|2|47|77060.26|0.09|0.01|R|F|1994-01-01|1993-12-25|1994-01-05|COLLECT COD|FOB| requests. even, even accou\n4455|122723|7748|3|34|59354.48|0.00|0.06|A|F|1993-10-24|1993-11-27|1993-11-04|TAKE BACK RETURN|AIR| slyly ironic requests. quickly even d\n4480|107888|5419|1|30|56876.40|0.08|0.03|R|F|1994-07-29|1994-06-22|1994-08-01|NONE|FOB|ven braids us\n4481|23918|6421|1|50|92095.50|0.02|0.06|N|O|1996-07-22|1996-05-13|1996-08-14|DELIVER IN PERSON|RAIL|ar packages. regula\n4481|189040|1559|2|27|30484.08|0.02|0.03|N|O|1996-04-06|1996-05-17|1996-04-12|TAKE BACK RETURN|AIR|ackages haggle even, \n4482|70869|870|1|32|58875.52|0.06|0.03|A|F|1995-05-16|1995-07-22|1995-06-07|NONE|RAIL| quickly pendin\n4482|95182|201|2|32|37669.76|0.01|0.06|N|O|1995-08-16|1995-06-26|1995-09-10|DELIVER IN PERSON|AIR|eans wake according \n4483|5133|5134|1|32|33220.16|0.07|0.07|R|F|1992-04-05|1992-05-25|1992-04-08|DELIVER IN PERSON|MAIL|ests haggle. slyl\n4483|61832|4339|2|50|89691.50|0.01|0.06|A|F|1992-06-19|1992-05-12|1992-07-08|DELIVER IN PERSON|TRUCK|ag blithely even\n4483|8334|3335|3|50|62116.50|0.00|0.04|R|F|1992-06-10|1992-04-18|1992-06-17|DELIVER IN PERSON|MAIL|ackages. furiously ironi\n4484|94779|2307|1|4|7095.08|0.06|0.03|N|O|1997-04-09|1997-02-11|1997-04-12|TAKE BACK RETURN|TRUCK|packages de\n4484|136812|6813|2|39|72103.59|0.05|0.02|N|O|1997-04-01|1997-01-26|1997-04-21|NONE|RAIL|onic accounts wake blithel\n4484|190000|1|3|38|41420.00|0.06|0.07|N|O|1997-03-07|1997-01-31|1997-04-01|COLLECT COD|REG AIR|. even requests un\n4484|121153|8690|4|41|48140.15|0.06|0.03|N|O|1997-01-25|1997-02-15|1997-01-29|TAKE BACK RETURN|REG AIR|ress accounts. ironic deposits unwind fur\n4484|2585|2586|5|42|62478.36|0.03|0.07|N|O|1997-03-25|1997-02-21|1997-04-05|DELIVER IN PERSON|REG AIR|ding, pending requests wake. fluffily \n4484|35516|5517|6|29|42093.79|0.09|0.06|N|O|1996-12-27|1997-03-10|1997-01-13|NONE|FOB| wake blithely ironic\n4484|102267|7288|7|50|63463.00|0.07|0.01|N|O|1997-03-17|1997-03-16|1997-03-21|COLLECT COD|FOB|the ironic, final theodo\n4485|190656|8214|1|1|1746.65|0.03|0.05|R|F|1994-12-04|1995-02-07|1994-12-09|NONE|AIR|play according to the ironic, ironic\n4485|140072|7615|2|46|51155.22|0.04|0.06|R|F|1995-03-09|1994-12-14|1995-03-23|DELIVER IN PERSON|AIR|. ironic foxes haggle. regular war\n4485|174651|4652|3|43|74202.95|0.01|0.05|R|F|1995-01-17|1995-02-11|1995-02-07|DELIVER IN PERSON|TRUCK|al accounts according to the slyly r\n4485|143072|3073|4|43|47948.01|0.08|0.06|R|F|1995-01-28|1995-01-26|1995-02-07|DELIVER IN PERSON|AIR|. blithely\n4485|5035|5036|5|47|44181.41|0.08|0.04|R|F|1995-03-11|1995-01-11|1995-03-21|TAKE BACK RETURN|RAIL|luffily pending acc\n4486|134209|6723|1|46|57187.20|0.08|0.00|N|O|1998-05-02|1998-04-05|1998-05-08|COLLECT COD|MAIL|ackages. specia\n4486|48748|3757|2|19|32238.06|0.10|0.01|N|O|1998-06-07|1998-05-28|1998-07-02|NONE|MAIL|pending foxes after\n4486|95192|5193|3|47|55797.93|0.02|0.07|N|O|1998-04-09|1998-05-24|1998-05-07|DELIVER IN PERSON|MAIL|ts around the quiet packages ar\n4486|90018|5037|4|28|28224.28|0.07|0.02|N|O|1998-04-21|1998-04-19|1998-04-26|TAKE BACK RETURN|AIR|to the furious, regular foxes play abov\n4487|137105|9619|1|37|42257.70|0.03|0.07|R|F|1993-02-28|1993-04-18|1993-03-17|TAKE BACK RETURN|MAIL|bove the fu\n4487|112846|7869|2|49|91083.16|0.10|0.00|R|F|1993-06-13|1993-05-08|1993-07-10|COLLECT COD|FOB|sual packages should ha\n4487|189582|2101|3|1|1671.58|0.02|0.07|A|F|1993-05-11|1993-05-23|1993-05-17|TAKE BACK RETURN|FOB|ithely final asym\n4487|92846|2847|4|25|45971.00|0.07|0.03|A|F|1993-03-09|1993-04-27|1993-03-30|COLLECT COD|RAIL|g the final instructions. slyly c\n4512|161210|3727|1|30|38136.30|0.07|0.07|N|O|1996-01-28|1995-12-22|1996-02-22|TAKE BACK RETURN|TRUCK|ly unusual package\n4512|40933|5942|2|24|44974.32|0.04|0.06|N|O|1995-12-16|1996-01-16|1995-12-25|NONE|SHIP|ly regular pinto beans. carefully bold depo\n4512|144996|25|3|21|42860.79|0.00|0.00|N|O|1995-10-31|1995-12-30|1995-11-15|NONE|REG AIR|lly unusual pinto b\n4512|140248|249|4|32|41223.68|0.10|0.01|N|O|1995-11-25|1995-12-28|1995-12-06|NONE|FOB|counts are against the quickly regular \n4512|132998|8025|5|43|87332.57|0.06|0.00|N|O|1995-12-20|1995-11-28|1996-01-14|NONE|AIR|are carefully. theodolites wake\n4513|169018|9019|1|29|31523.29|0.03|0.01|N|O|1996-05-18|1996-05-23|1996-06-08|NONE|REG AIR|cajole. regular packages boost. s\n4513|69944|2451|2|39|74643.66|0.01|0.04|N|O|1996-06-25|1996-05-14|1996-07-24|NONE|MAIL|slyly furiously unusual deposits. blit\n4513|137348|4888|3|34|47101.56|0.00|0.03|N|O|1996-03-27|1996-06-12|1996-04-06|DELIVER IN PERSON|SHIP|sits. quickly even instructions \n4513|191361|8919|4|13|18880.68|0.08|0.08|N|O|1996-04-12|1996-05-19|1996-04-25|DELIVER IN PERSON|AIR|l, final excuses detect furi\n4514|163673|1222|1|27|46890.09|0.06|0.06|R|F|1994-07-01|1994-07-13|1994-07-26|COLLECT COD|AIR| even, silent foxes be\n4514|45129|7634|2|15|16111.80|0.10|0.04|R|F|1994-08-24|1994-07-11|1994-09-14|DELIVER IN PERSON|RAIL|! unusual, special deposits afte\n4514|77425|9933|3|10|14024.20|0.09|0.05|A|F|1994-06-19|1994-06-25|1994-07-01|COLLECT COD|SHIP|ake furiously. carefully regular requests\n4514|80738|3247|4|9|15468.57|0.10|0.03|A|F|1994-08-04|1994-07-01|1994-09-01|DELIVER IN PERSON|REG AIR|wly. quick\n4514|148628|6171|5|12|20119.44|0.02|0.03|R|F|1994-08-20|1994-06-09|1994-09-15|TAKE BACK RETURN|FOB| carefully ironic foxes nag caref\n4514|188706|8707|6|38|68198.60|0.03|0.05|A|F|1994-07-28|1994-07-06|1994-08-25|NONE|AIR|ending excuses. sl\n4514|176429|6430|7|27|40646.34|0.04|0.06|A|F|1994-06-24|1994-07-14|1994-06-30|TAKE BACK RETURN|TRUCK|. slyly sile\n4515|38003|8004|1|15|14115.00|0.06|0.01|R|F|1992-05-26|1992-05-25|1992-06-03|NONE|SHIP|posits wake\n4515|102050|9581|2|50|52602.50|0.06|0.03|A|F|1992-03-28|1992-05-16|1992-04-20|NONE|AIR|ding instructions again\n4515|153202|748|3|27|33890.40|0.09|0.01|A|F|1992-06-06|1992-06-08|1992-06-07|DELIVER IN PERSON|REG AIR| against the even re\n4515|53814|3815|4|32|56569.92|0.06|0.03|R|F|1992-04-07|1992-05-11|1992-04-09|COLLECT COD|MAIL|carefully express depo\n4515|44659|9668|5|22|35280.30|0.09|0.07|A|F|1992-07-16|1992-05-07|1992-07-23|NONE|SHIP|le quickly above the even, bold ideas.\n4515|179908|7460|6|23|45721.70|0.04|0.00|R|F|1992-05-23|1992-06-15|1992-06-20|TAKE BACK RETURN|FOB|ns. bold r\n4516|169570|2087|1|34|55745.38|0.05|0.04|A|F|1994-05-16|1994-06-23|1994-06-12|NONE|SHIP|even pinto beans wake qui\n4517|42540|2541|1|50|74127.00|0.01|0.02|N|O|1998-06-08|1998-04-18|1998-06-20|DELIVER IN PERSON|MAIL|refully pending acco\n4518|143741|8770|1|9|16062.66|0.09|0.04|N|O|1997-06-26|1997-07-07|1997-07-10|NONE|RAIL| pending deposits. slyly re\n4518|44083|4084|2|19|19514.52|0.10|0.05|N|O|1997-08-09|1997-06-06|1997-08-27|COLLECT COD|RAIL|ter the slyly bo\n4519|54680|7186|1|30|49040.40|0.09|0.07|R|F|1993-04-11|1993-06-05|1993-04-22|DELIVER IN PERSON|REG AIR|totes. slyly bold somas after the \n4519|190062|2582|2|37|42626.22|0.06|0.08|R|F|1993-07-22|1993-06-16|1993-08-19|COLLECT COD|AIR|ly slyly furious depth\n4544|130682|3196|1|40|68507.20|0.07|0.01|N|O|1997-08-15|1997-10-16|1997-08-20|DELIVER IN PERSON|RAIL| detect slyly. evenly pending instru\n4544|171088|3606|2|19|22022.52|0.08|0.01|N|O|1997-08-14|1997-09-08|1997-08-25|NONE|SHIP|regular ideas are furiously about\n4544|70629|8151|3|20|31992.40|0.02|0.07|N|O|1997-10-12|1997-10-11|1997-10-13|COLLECT COD|REG AIR| waters about the\n4544|50571|5582|4|39|59341.23|0.07|0.05|N|O|1997-08-20|1997-09-07|1997-08-27|COLLECT COD|REG AIR|ular packages. s\n4544|132280|7307|5|31|40680.68|0.09|0.03|N|O|1997-08-09|1997-09-29|1997-08-17|COLLECT COD|TRUCK|dolites detect quickly reg\n4544|26100|6101|6|8|8208.80|0.10|0.03|N|O|1997-10-13|1997-10-06|1997-10-25|COLLECT COD|AIR|olites. fi\n4545|172885|437|1|38|74399.44|0.06|0.06|R|F|1993-01-27|1993-03-01|1993-02-04|NONE|TRUCK|nts serve according to th\n4545|62456|2457|2|27|38298.15|0.01|0.06|R|F|1993-02-07|1993-02-18|1993-02-18|NONE|FOB|ously bold asymptotes! blithely pen\n4545|86195|3720|3|9|10630.71|0.10|0.06|R|F|1993-03-20|1993-02-23|1993-04-11|TAKE BACK RETURN|AIR|xpress accounts\n4545|63981|1500|4|2|3889.96|0.10|0.00|R|F|1993-04-16|1993-04-17|1993-05-03|NONE|REG AIR|ages use. slyly even i\n4545|116732|9244|5|27|47215.71|0.08|0.05|A|F|1993-03-18|1993-02-22|1993-03-23|NONE|RAIL|ccounts haggle carefully. deposits \n4545|108682|1193|6|8|13525.44|0.03|0.02|A|F|1993-05-01|1993-03-12|1993-05-15|NONE|FOB| boost slyly. slyly\n4545|8362|863|7|36|45732.96|0.10|0.04|R|F|1993-01-28|1993-03-30|1993-02-04|DELIVER IN PERSON|SHIP|sublate slyly. furiously ironic accounts b\n4546|132467|7494|1|10|14994.60|0.09|0.02|N|O|1995-09-23|1995-10-10|1995-10-23|COLLECT COD|TRUCK|osits alongside of the\n4546|170327|5362|2|15|20959.80|0.04|0.07|N|O|1995-07-31|1995-10-17|1995-08-06|NONE|REG AIR|ught to cajole furiously. qu\n4546|76977|6978|3|4|7815.88|0.06|0.08|N|O|1995-08-14|1995-10-07|1995-08-16|COLLECT COD|MAIL|kly pending dependencies along the furio\n4546|148745|1260|4|10|17937.40|0.08|0.02|N|O|1995-09-02|1995-09-16|1995-09-10|DELIVER IN PERSON|FOB|above the enticingly ironic dependencies\n4547|187798|317|1|15|28286.85|0.10|0.04|A|F|1993-12-08|1993-11-15|1993-12-22|NONE|REG AIR|ets haggle. regular dinos affix fu\n4547|115181|7693|2|7|8373.26|0.10|0.02|A|F|1993-09-04|1993-09-29|1993-09-20|COLLECT COD|RAIL|slyly express a\n4547|44955|7460|3|15|28499.25|0.00|0.00|R|F|1993-11-18|1993-10-06|1993-12-13|NONE|TRUCK|e carefully across the unus\n4547|147087|4630|4|15|17011.20|0.05|0.08|R|F|1993-11-29|1993-10-12|1993-12-29|COLLECT COD|REG AIR|ironic gifts integrate \n4548|13272|5774|1|21|24890.67|0.10|0.05|N|O|1996-07-11|1996-09-04|1996-07-30|COLLECT COD|REG AIR|pecial theodoli\n4548|46001|1010|2|17|16099.00|0.00|0.08|N|O|1996-07-23|1996-09-21|1996-07-26|DELIVER IN PERSON|REG AIR|y ironic requests above the fluffily d\n4548|122710|7735|3|47|81437.37|0.05|0.04|N|O|1996-07-24|1996-09-12|1996-08-08|NONE|MAIL|ts. excuses use slyly spec\n4548|176323|1358|4|22|30785.04|0.07|0.01|N|O|1996-07-06|1996-08-23|1996-07-15|DELIVER IN PERSON|RAIL|s. furiously ironic theodolites c\n4548|44556|2069|5|36|54019.80|0.04|0.06|N|O|1996-08-19|1996-09-12|1996-09-08|COLLECT COD|FOB|tions integrat\n4549|158023|5569|1|44|47564.88|0.08|0.00|N|O|1998-03-13|1998-04-15|1998-03-27|TAKE BACK RETURN|TRUCK|ding to the regular, silent requests\n4549|88030|539|2|1|1018.03|0.05|0.08|N|O|1998-05-04|1998-04-11|1998-05-14|TAKE BACK RETURN|AIR| requests wake. furiously even \n4550|149038|1553|1|9|9783.27|0.05|0.06|R|F|1995-04-19|1995-02-07|1995-04-24|COLLECT COD|SHIP|l dependencies boost slyly after th\n4550|65547|8054|2|19|28738.26|0.06|0.04|A|F|1995-01-01|1995-02-13|1995-01-20|NONE|AIR|quests. express \n4551|10447|7951|1|6|8144.64|0.08|0.08|N|O|1996-05-18|1996-04-23|1996-06-13|DELIVER IN PERSON|TRUCK|fily silent fo\n4551|178094|3129|2|26|30474.34|0.02|0.04|N|O|1996-04-14|1996-04-26|1996-04-17|TAKE BACK RETURN|RAIL|le. carefully dogged accounts use furiousl\n4551|21706|6711|3|22|35809.40|0.08|0.01|N|O|1996-05-12|1996-03-17|1996-05-29|TAKE BACK RETURN|REG AIR|ly ironic reques\n4551|197665|185|4|27|47591.82|0.00|0.01|N|O|1996-04-28|1996-03-22|1996-05-22|TAKE BACK RETURN|RAIL|y along the slyly even \n4576|89514|9515|1|5|7517.55|0.09|0.03|N|O|1996-08-23|1996-11-08|1996-09-20|TAKE BACK RETURN|AIR|ly express, special asymptote\n4576|57907|7908|2|43|80190.70|0.08|0.06|N|O|1996-10-24|1996-09-23|1996-11-10|NONE|SHIP|ly final deposits. never\n4576|41350|8863|3|14|18078.90|0.09|0.01|N|O|1996-09-12|1996-09-30|1996-09-24|COLLECT COD|MAIL|detect slyly.\n4577|184089|6608|1|43|50442.44|0.01|0.03|N|O|1998-06-16|1998-07-09|1998-06-17|TAKE BACK RETURN|AIR|packages. \n4577|176990|2025|2|43|88880.57|0.05|0.03|N|O|1998-08-24|1998-06-02|1998-09-14|TAKE BACK RETURN|RAIL|ly accounts. carefully \n4577|68494|3507|3|12|17549.88|0.07|0.05|N|O|1998-07-29|1998-06-17|1998-08-04|DELIVER IN PERSON|TRUCK|equests alongsi\n4578|73111|633|1|10|10841.10|0.09|0.06|R|F|1993-01-01|1992-11-19|1993-01-28|TAKE BACK RETURN|REG AIR|uests. blithely unus\n4578|168393|8394|2|42|61378.38|0.06|0.00|R|F|1993-01-05|1992-11-06|1993-01-13|DELIVER IN PERSON|FOB|s are caref\n4578|178494|3529|3|15|23587.35|0.01|0.01|R|F|1992-10-23|1992-11-22|1992-11-09|DELIVER IN PERSON|REG AIR|gular theodo\n4578|138667|3694|4|7|11939.62|0.09|0.08|A|F|1992-12-07|1992-11-27|1993-01-05|TAKE BACK RETURN|SHIP|odolites. carefully unusual ideas accor\n4578|162341|4858|5|20|28066.80|0.04|0.02|A|F|1993-01-11|1992-11-09|1993-01-23|TAKE BACK RETURN|RAIL|iously pending theodolites--\n4579|174085|9120|1|14|16227.12|0.02|0.02|N|O|1996-02-01|1996-01-08|1996-02-08|TAKE BACK RETURN|MAIL|nding theodolites. fluffil\n4579|41886|1887|2|28|51180.64|0.02|0.05|N|O|1996-01-22|1996-02-13|1996-02-03|DELIVER IN PERSON|RAIL|slyly across the \n4579|177457|7458|3|34|52171.30|0.05|0.02|N|O|1996-02-26|1996-02-22|1996-03-16|COLLECT COD|MAIL|hely. carefully blithe dependen\n4579|119554|9555|4|8|12588.40|0.05|0.06|N|O|1995-12-16|1996-01-15|1995-12-18|TAKE BACK RETURN|AIR|posits. carefully perman\n4580|91060|6079|1|22|23123.32|0.01|0.05|A|F|1994-01-16|1994-01-26|1994-02-05|COLLECT COD|AIR|nticingly final packag\n4580|31880|6887|2|10|18118.80|0.05|0.04|R|F|1993-12-20|1993-12-30|1994-01-17|COLLECT COD|RAIL|gular, pending deposits. fina\n4580|963|8464|3|41|76422.36|0.00|0.07|R|F|1993-12-13|1994-01-31|1994-01-06|NONE|SHIP|requests. quickly silent asymptotes sle\n4580|177771|289|4|5|9243.85|0.07|0.00|A|F|1994-01-28|1993-12-17|1994-02-22|NONE|TRUCK|o beans. f\n4580|188804|1323|5|39|73819.20|0.03|0.02|R|F|1993-12-28|1993-12-26|1994-01-23|NONE|RAIL|. fluffily final dolphins use furiously al\n4581|164947|7464|1|37|74441.78|0.01|0.04|A|F|1992-10-17|1992-11-05|1992-11-04|DELIVER IN PERSON|MAIL|e the blithely bold pearls ha\n4581|49672|4681|2|7|11351.69|0.01|0.02|A|F|1992-10-09|1992-10-20|1992-10-21|TAKE BACK RETURN|MAIL|express accounts d\n4581|20508|5513|3|46|65711.00|0.04|0.04|A|F|1992-09-09|1992-11-27|1992-09-26|NONE|REG AIR|nag toward the carefully final accounts. \n4582|191457|6496|1|17|26323.65|0.09|0.08|N|O|1996-08-17|1996-08-26|1996-08-20|COLLECT COD|REG AIR|ng packages. depo\n4583|140441|442|1|17|25184.48|0.01|0.05|A|F|1994-11-08|1994-11-03|1994-11-29|COLLECT COD|MAIL|romise. reques\n4583|186542|6543|2|43|70027.22|0.04|0.04|A|F|1994-10-30|1994-12-17|1994-11-16|COLLECT COD|RAIL|fully after the speci\n4583|195513|3071|3|28|45038.28|0.00|0.07|A|F|1994-10-29|1994-11-21|1994-11-28|NONE|SHIP|to beans haggle sly\n4583|172130|2131|4|27|32457.51|0.08|0.03|R|F|1995-01-11|1994-12-24|1995-02-10|DELIVER IN PERSON|TRUCK| detect silent requests. furiously speci\n4583|183800|8837|5|36|67816.80|0.09|0.06|A|F|1995-01-06|1994-11-25|1995-01-29|DELIVER IN PERSON|RAIL|ar requests haggle after the furiously \n4583|121705|4218|6|14|24173.80|0.09|0.01|R|F|1994-11-17|1994-11-08|1994-11-21|DELIVER IN PERSON|AIR|detect. doggedly regular pi\n4583|86867|1884|7|32|59323.52|0.04|0.00|A|F|1995-01-13|1994-10-29|1995-02-08|TAKE BACK RETURN|RAIL|across the pinto beans-- quickly\n4608|172217|9769|1|30|38676.30|0.08|0.05|R|F|1994-10-08|1994-07-18|1994-10-25|DELIVER IN PERSON|SHIP|s cajole. slyly \n4608|46212|6213|2|50|57910.50|0.06|0.01|A|F|1994-07-25|1994-09-01|1994-08-10|NONE|FOB| theodolites\n4608|78184|692|3|50|58109.00|0.03|0.01|A|F|1994-08-04|1994-09-10|1994-08-13|COLLECT COD|TRUCK| wake closely. even decoys haggle above\n4608|30557|558|4|36|53551.80|0.05|0.06|R|F|1994-10-04|1994-08-02|1994-10-21|COLLECT COD|FOB|ages wake quickly slyly iron\n4609|46409|3922|1|28|37951.20|0.10|0.05|N|O|1997-02-02|1997-02-17|1997-03-02|DELIVER IN PERSON|REG AIR|ously. quickly final requests cajole fl\n4609|184318|6837|2|3|4206.93|0.09|0.03|N|O|1996-12-28|1997-02-06|1997-01-20|NONE|FOB|nstructions. furious instructions \n4609|22399|2400|3|46|60783.94|0.05|0.05|N|O|1997-02-11|1997-01-16|1997-03-07|NONE|FOB|r foxes. fluffily ironic ideas ha\n4610|86177|1194|1|21|24426.57|0.07|0.07|R|F|1993-08-10|1993-08-05|1993-08-27|NONE|REG AIR|ly special theodolites. even,\n4610|174842|7360|2|14|26835.76|0.00|0.07|R|F|1993-07-28|1993-07-25|1993-07-31|TAKE BACK RETURN|SHIP| ironic frays. dependencies detect blithel\n4610|158379|5925|3|44|63244.28|0.05|0.03|A|F|1993-08-05|1993-07-20|1993-08-19|COLLECT COD|TRUCK| final theodolites \n4610|74538|2060|4|26|39325.78|0.06|0.03|R|F|1993-07-01|1993-07-19|1993-07-19|NONE|MAIL| to the fluffily ironic requests h\n4610|146158|6159|5|29|34920.35|0.08|0.04|R|F|1993-08-09|1993-07-27|1993-08-16|DELIVER IN PERSON|AIR| foxes. special, express package\n4611|51267|6278|1|47|57258.22|0.09|0.06|A|F|1993-03-05|1993-03-01|1993-03-17|COLLECT COD|TRUCK|iously. furiously regular\n4611|34872|4873|2|31|56012.97|0.04|0.02|A|F|1993-01-28|1993-02-14|1993-01-29|TAKE BACK RETURN|AIR| final pinto beans. permanent, sp\n4611|81649|1650|3|50|81532.00|0.08|0.01|R|F|1993-01-22|1993-03-30|1993-02-16|TAKE BACK RETURN|AIR|l platelets. \n4611|70451|7973|4|48|68229.60|0.02|0.08|R|F|1993-02-28|1993-02-12|1993-03-01|COLLECT COD|AIR|ular accounts \n4612|5989|8490|1|20|37899.60|0.02|0.03|R|F|1993-09-24|1993-12-18|1993-10-22|NONE|AIR|beans sleep blithely iro\n4612|49750|2255|2|17|28895.75|0.10|0.06|A|F|1994-01-09|1993-11-08|1994-02-06|TAKE BACK RETURN|REG AIR|equests haggle carefully silent excus\n4612|136371|1398|3|40|56294.80|0.08|0.01|R|F|1993-10-08|1993-11-23|1993-10-24|DELIVER IN PERSON|RAIL|special platelets.\n4612|184664|7183|4|10|17486.60|0.10|0.06|A|F|1993-11-11|1993-11-19|1993-11-13|TAKE BACK RETURN|SHIP|unusual theodol\n4613|37372|7373|1|17|22259.29|0.09|0.07|N|O|1998-06-07|1998-05-11|1998-06-29|DELIVER IN PERSON|SHIP|liers cajole a\n4613|107883|394|2|25|47272.00|0.05|0.04|N|O|1998-05-22|1998-04-11|1998-05-27|TAKE BACK RETURN|SHIP|y pending platelets x-ray ironically! pend\n4613|173667|8702|3|15|26109.90|0.10|0.02|N|O|1998-05-31|1998-04-16|1998-06-25|DELIVER IN PERSON|MAIL|against the quickly r\n4613|7183|9684|4|36|39246.48|0.04|0.01|N|O|1998-04-22|1998-05-05|1998-05-04|DELIVER IN PERSON|AIR|gainst the furiously ironic\n4613|110121|5144|5|35|39589.20|0.04|0.06|N|O|1998-06-04|1998-04-17|1998-06-20|COLLECT COD|MAIL|e blithely against the even, bold pi\n4613|195878|8398|6|47|92771.89|0.04|0.04|N|O|1998-07-03|1998-05-26|1998-07-09|NONE|FOB|uriously special requests wak\n4613|118713|1225|7|39|67536.69|0.09|0.05|N|O|1998-06-12|1998-06-01|1998-07-06|DELIVER IN PERSON|REG AIR|ously express\n4614|6014|1015|1|19|17480.19|0.09|0.08|N|O|1996-05-17|1996-06-21|1996-06-08|TAKE BACK RETURN|AIR|ix. carefully regular \n4614|64629|4630|2|3|4780.86|0.08|0.01|N|O|1996-07-22|1996-07-21|1996-08-07|NONE|MAIL|ions engage final, ironic \n4614|7136|9637|3|36|37552.68|0.10|0.04|N|O|1996-07-05|1996-06-26|1996-07-07|NONE|REG AIR|onic foxes affix furi\n4614|125036|2573|4|6|6366.18|0.09|0.01|N|O|1996-06-11|1996-05-30|1996-07-03|COLLECT COD|REG AIR|ake quickly quickly regular epitap\n4614|72044|4552|5|24|24384.96|0.07|0.06|N|O|1996-07-01|1996-06-24|1996-07-08|COLLECT COD|REG AIR|regular, even\n4614|33149|3150|6|32|34628.48|0.10|0.05|N|O|1996-08-21|1996-05-28|1996-09-16|NONE|REG AIR|ickly furio\n4614|127060|4597|7|41|44569.46|0.01|0.07|N|O|1996-07-31|1996-07-12|1996-08-16|COLLECT COD|REG AIR|ackages haggle carefully about the even, b\n4615|91296|3806|1|10|12872.90|0.02|0.08|A|F|1993-11-20|1993-10-05|1993-12-08|DELIVER IN PERSON|AIR|sits. slyly express deposits are\n4640|87422|4947|1|5|7047.10|0.03|0.08|N|O|1996-02-05|1996-02-14|1996-02-15|TAKE BACK RETURN|RAIL| warthogs against the regular\n4640|87287|4812|2|9|11468.52|0.03|0.05|N|O|1996-02-12|1996-02-14|1996-02-29|DELIVER IN PERSON|AIR| accounts. unu\n4640|26265|3772|3|18|21442.68|0.02|0.07|N|O|1996-02-28|1996-03-06|1996-03-28|DELIVER IN PERSON|RAIL|boost furiously accord\n4640|22346|7351|4|36|45660.24|0.06|0.08|N|O|1996-01-03|1996-03-09|1996-01-11|DELIVER IN PERSON|RAIL|iously furious accounts boost. carefully\n4640|155974|1005|5|15|30449.55|0.03|0.02|N|O|1996-03-19|1996-02-09|1996-04-11|TAKE BACK RETURN|FOB|y regular instructions doze furiously. reg\n4641|189796|7351|1|45|84860.55|0.07|0.03|R|F|1993-05-11|1993-04-19|1993-05-21|DELIVER IN PERSON|MAIL| about the close \n4641|94261|6771|2|39|48955.14|0.06|0.00|R|F|1993-02-10|1993-03-06|1993-02-15|TAKE BACK RETURN|REG AIR| the bold reque\n4641|35287|5288|3|15|18334.20|0.01|0.08|R|F|1993-01-25|1993-04-09|1993-02-05|TAKE BACK RETURN|AIR|s. carefully even exc\n4642|193181|8220|1|11|14015.98|0.04|0.07|A|F|1995-05-23|1995-04-26|1995-06-04|COLLECT COD|TRUCK|lithely express asympt\n4642|179594|2112|2|34|56902.06|0.04|0.07|R|F|1995-04-01|1995-05-11|1995-04-23|COLLECT COD|SHIP|theodolites detect among the ironically sp\n4642|20152|153|3|10|10721.50|0.04|0.02|R|F|1995-04-16|1995-04-28|1995-04-24|COLLECT COD|RAIL|urts. even deposits nag beneath \n4642|93698|8717|4|18|30450.42|0.00|0.04|N|F|1995-06-16|1995-04-16|1995-06-21|NONE|TRUCK|ily pending accounts hag\n4642|178937|8938|5|41|82653.13|0.10|0.00|R|F|1995-04-08|1995-04-13|1995-05-01|DELIVER IN PERSON|MAIL|s are blithely. requests wake above the fur\n4643|184931|2486|1|50|100796.50|0.08|0.05|N|O|1995-09-11|1995-08-13|1995-09-30|DELIVER IN PERSON|SHIP|. ironic deposits cajo\n4644|176873|9391|1|4|7799.48|0.06|0.03|N|O|1998-05-06|1998-03-19|1998-05-28|NONE|MAIL|gular requests? pendi\n4644|96503|6504|2|16|23992.00|0.03|0.04|N|O|1998-03-13|1998-02-21|1998-04-03|COLLECT COD|SHIP|lar excuses across the \n4644|114307|6819|3|10|13213.00|0.02|0.02|N|O|1998-02-21|1998-02-28|1998-03-19|COLLECT COD|REG AIR|osits according to the\n4644|153953|6469|4|45|90312.75|0.10|0.07|N|O|1998-02-02|1998-04-08|1998-02-15|COLLECT COD|SHIP| carefully a\n4644|86667|4192|5|10|16536.60|0.08|0.08|N|O|1998-03-12|1998-03-11|1998-03-19|TAKE BACK RETURN|REG AIR| the slow, final fo\n4645|49353|1858|1|45|58605.75|0.09|0.05|A|F|1994-12-27|1994-11-02|1994-12-31|DELIVER IN PERSON|AIR|ular ideas. slyly\n4645|65060|5061|2|32|32801.92|0.10|0.08|A|F|1994-11-17|1994-10-30|1994-11-18|COLLECT COD|REG AIR| final accounts alongside\n4645|53599|3600|3|25|38814.75|0.03|0.00|R|F|1994-10-25|1994-12-11|1994-11-14|NONE|REG AIR|braids. ironic dependencies main\n4645|36614|1621|4|42|65125.62|0.10|0.02|R|F|1994-12-02|1994-12-18|1994-12-16|COLLECT COD|TRUCK|regular pinto beans amon\n4645|160043|2560|5|35|38606.40|0.03|0.07|A|F|1994-12-08|1994-11-25|1994-12-09|TAKE BACK RETURN|FOB|sias believe bl\n4645|41437|3942|6|27|37217.61|0.09|0.08|R|F|1994-11-26|1994-10-25|1994-12-04|NONE|SHIP|ously express pinto beans. ironic depos\n4645|30142|143|7|42|45029.88|0.10|0.06|A|F|1994-12-31|1994-10-22|1995-01-28|DELIVER IN PERSON|AIR|e slyly regular pinto beans. thin\n4646|190170|2690|1|24|30244.08|0.02|0.05|N|O|1996-09-18|1996-08-09|1996-09-21|TAKE BACK RETURN|RAIL|ic platelets lose carefully. blithely unu\n4646|177583|5135|2|26|43175.08|0.07|0.00|N|O|1996-10-02|1996-08-25|1996-10-27|DELIVER IN PERSON|MAIL|ix according to the slyly spe\n4646|33120|630|3|18|18956.16|0.01|0.00|N|O|1996-06-30|1996-08-10|1996-07-12|TAKE BACK RETURN|TRUCK|beans sleep car\n4646|39105|9106|4|38|39675.80|0.08|0.01|N|O|1996-09-01|1996-08-23|1996-09-27|COLLECT COD|SHIP|al platelets cajole. slyly final dol\n4646|25896|8399|5|22|40081.58|0.01|0.08|N|O|1996-07-14|1996-08-06|1996-07-29|DELIVER IN PERSON|MAIL|cies are blithely after the slyly reg\n4647|92862|7881|1|16|29677.76|0.09|0.07|R|F|1994-09-07|1994-07-15|1994-10-06|COLLECT COD|RAIL|o beans about the fluffily special the\n4647|128884|6421|2|34|65037.92|0.01|0.02|R|F|1994-05-20|1994-06-20|1994-05-29|COLLECT COD|TRUCK|ly sly accounts\n4647|146614|6615|3|27|44836.47|0.03|0.08|R|F|1994-05-20|1994-06-26|1994-05-30|NONE|FOB|ully even ti\n4647|138275|8276|4|2|2626.54|0.04|0.07|R|F|1994-07-03|1994-07-22|1994-07-22|TAKE BACK RETURN|RAIL|dolites wake furiously special pinto be\n4647|186829|6830|5|2|3831.64|0.07|0.06|A|F|1994-05-27|1994-08-05|1994-06-10|TAKE BACK RETURN|FOB| pinto beans believe furiously slyly silent\n4647|28488|991|6|28|39661.44|0.02|0.03|A|F|1994-08-25|1994-08-06|1994-09-18|DELIVER IN PERSON|FOB| are above the fluffily fin\n4672|58147|653|1|22|24313.08|0.01|0.07|N|O|1995-12-03|1995-12-08|1995-12-17|COLLECT COD|AIR|l instructions. blithely ironic packages \n4672|60961|3468|2|41|78800.36|0.00|0.00|N|O|1995-12-01|1995-12-15|1995-12-12|COLLECT COD|RAIL| slyly quie\n4672|162043|7076|3|24|26520.96|0.04|0.03|N|O|1995-11-11|1995-12-28|1995-12-04|NONE|REG AIR|y fluffily stealt\n4672|56171|1182|4|13|14653.21|0.10|0.03|N|O|1996-02-02|1995-12-13|1996-03-02|DELIVER IN PERSON|RAIL|ar requests? pending accounts against\n4672|54022|9033|5|45|43920.90|0.08|0.07|N|O|1996-02-07|1996-01-16|1996-02-14|DELIVER IN PERSON|MAIL| platelets use amon\n4672|140922|3437|6|20|39258.40|0.02|0.07|N|O|1995-12-08|1996-01-25|1995-12-19|COLLECT COD|REG AIR|s boost at the ca\n4672|71853|9375|7|38|69344.30|0.01|0.01|N|O|1995-11-28|1995-12-08|1995-12-13|COLLECT COD|SHIP|ests. idle, regular ex\n4673|16607|6608|1|8|12188.80|0.08|0.01|N|O|1996-10-12|1996-10-05|1996-11-04|TAKE BACK RETURN|FOB|lithely final re\n4673|100894|895|2|44|83375.16|0.06|0.01|N|O|1996-12-11|1996-10-31|1997-01-08|DELIVER IN PERSON|RAIL| gifts cajole dari\n4673|122258|7283|3|9|11522.25|0.04|0.07|N|O|1996-10-15|1996-09-30|1996-10-30|DELIVER IN PERSON|MAIL|ages nag across \n4674|149045|1560|1|50|54702.00|0.07|0.08|A|F|1994-05-13|1994-06-15|1994-06-05|COLLECT COD|RAIL|haggle about the blithel\n4674|188714|1233|2|35|63094.85|0.02|0.05|A|F|1994-08-02|1994-06-04|1994-08-21|COLLECT COD|FOB|le quickly after the express sent\n4674|110443|2955|3|3|4360.32|0.01|0.05|A|F|1994-07-19|1994-05-28|1994-07-23|TAKE BACK RETURN|RAIL| regular requests na\n4674|12164|4666|4|21|22599.36|0.02|0.08|R|F|1994-05-08|1994-07-02|1994-06-04|COLLECT COD|RAIL|ent accounts sublate deposits. instruc\n4675|170390|391|1|6|8762.34|0.00|0.05|R|F|1994-01-22|1994-01-06|1994-02-12|TAKE BACK RETURN|TRUCK| unusual ideas thrash bl\n4675|143689|8718|2|12|20792.16|0.00|0.04|A|F|1993-12-22|1994-01-12|1993-12-23|TAKE BACK RETURN|AIR|posits affix carefully\n4675|180963|964|3|5|10219.80|0.05|0.05|A|F|1994-01-16|1994-01-05|1994-01-18|DELIVER IN PERSON|RAIL|lent pinto beans\n4675|33646|1156|4|26|41070.64|0.03|0.01|A|F|1993-12-16|1993-12-29|1993-12-23|DELIVER IN PERSON|SHIP|nts. express requests are quickly \n4675|80498|499|5|18|26612.82|0.01|0.08|R|F|1994-02-23|1994-01-18|1994-03-05|TAKE BACK RETURN|FOB|cajole unusual dep\n4675|118089|8090|6|1|1107.08|0.10|0.06|R|F|1994-03-18|1994-02-14|1994-04-17|NONE|SHIP|unts. caref\n4676|164646|9679|1|47|80400.08|0.03|0.06|N|O|1995-12-20|1995-10-04|1996-01-09|NONE|AIR|lithely about the carefully special requ\n4676|5043|44|2|33|31285.32|0.08|0.05|N|O|1995-12-29|1995-10-01|1996-01-18|TAKE BACK RETURN|FOB|yly express \n4676|145173|7688|3|4|4872.68|0.10|0.06|N|O|1995-12-12|1995-10-22|1995-12-13|TAKE BACK RETURN|TRUCK|detect above the ironic platelets. fluffily\n4676|110831|832|4|50|92091.50|0.07|0.01|N|O|1995-09-20|1995-11-20|1995-10-18|TAKE BACK RETURN|AIR|r deposits boost boldly quickly quick asymp\n4676|121096|3609|5|29|32395.61|0.01|0.02|N|O|1995-12-29|1995-11-12|1996-01-06|TAKE BACK RETURN|RAIL|ly regular theodolites sleep.\n4676|45569|5570|6|8|12116.48|0.08|0.08|N|O|1995-12-05|1995-10-18|1996-01-02|COLLECT COD|AIR|cuses boost above\n4676|63244|8257|7|13|15694.12|0.05|0.07|N|O|1995-11-18|1995-11-07|1995-12-10|TAKE BACK RETURN|TRUCK| at the slyly bold attainments. silently e\n4677|127864|377|1|25|47296.50|0.04|0.04|N|O|1998-04-11|1998-05-11|1998-04-18|TAKE BACK RETURN|REG AIR|unts doubt furiousl\n4678|57388|9894|1|35|47088.30|0.04|0.08|N|O|1998-11-27|1998-10-02|1998-12-17|TAKE BACK RETURN|AIR|he accounts. fluffily bold sheaves b\n4678|116672|9184|2|18|30396.06|0.03|0.06|N|O|1998-10-30|1998-09-22|1998-11-25|TAKE BACK RETURN|SHIP|usly ironic \n4678|95355|374|3|13|17554.55|0.10|0.07|N|O|1998-11-03|1998-10-17|1998-11-06|TAKE BACK RETURN|SHIP|its. carefully final fr\n4678|21704|6709|4|23|37391.10|0.06|0.05|N|O|1998-09-03|1998-09-20|1998-09-04|DELIVER IN PERSON|SHIP|ily sly deposi\n4678|177771|7772|5|40|73950.80|0.03|0.07|N|O|1998-11-11|1998-10-27|1998-11-24|TAKE BACK RETURN|AIR|. final, unusual requests sleep thinl\n4679|189136|4173|1|7|8575.91|0.10|0.05|R|F|1993-05-11|1993-04-11|1993-05-16|NONE|TRUCK|kages. bold, regular packa\n4704|77934|5456|1|14|26767.02|0.04|0.04|N|O|1996-10-27|1996-11-02|1996-11-07|DELIVER IN PERSON|TRUCK| above the slyly final requests. quickly \n4704|27671|174|2|7|11190.69|0.03|0.04|N|O|1996-12-04|1996-10-30|1996-12-23|DELIVER IN PERSON|SHIP|ers wake car\n4704|63081|3082|3|44|45939.52|0.02|0.05|N|O|1996-09-02|1996-10-07|1996-09-17|DELIVER IN PERSON|REG AIR|out the care\n4705|110778|5801|1|22|39352.94|0.04|0.04|R|F|1992-07-05|1992-05-11|1992-07-29|DELIVER IN PERSON|SHIP| fluffily pending accounts ca\n4705|30527|3031|2|14|20405.28|0.00|0.08|R|F|1992-07-14|1992-05-23|1992-07-25|DELIVER IN PERSON|TRUCK|ain carefully amon\n4705|55426|437|3|16|22102.72|0.07|0.08|R|F|1992-07-02|1992-06-06|1992-07-06|DELIVER IN PERSON|RAIL|special ideas nag sl\n4705|129147|6684|4|31|36460.34|0.03|0.03|R|F|1992-04-03|1992-05-30|1992-04-05|COLLECT COD|TRUCK|furiously final accou\n4705|162432|7465|5|28|41844.04|0.10|0.01|A|F|1992-06-03|1992-06-07|1992-06-22|DELIVER IN PERSON|MAIL|tes wake according to the unusual plate\n4705|183715|6234|6|23|41370.33|0.06|0.03|R|F|1992-06-22|1992-06-11|1992-07-18|DELIVER IN PERSON|MAIL| above the furiously ev\n4705|88534|3551|7|40|60901.20|0.08|0.06|A|F|1992-04-19|1992-04-28|1992-05-07|COLLECT COD|TRUCK|blithely. sly\n4706|181872|4391|1|37|72293.19|0.02|0.06|A|F|1993-02-20|1993-03-05|1993-03-03|DELIVER IN PERSON|TRUCK|kly final deposits c\n4706|121133|1134|2|23|26544.99|0.03|0.01|A|F|1993-04-01|1993-03-13|1993-05-01|COLLECT COD|FOB|deas across t\n4706|67134|2147|3|6|6606.78|0.01|0.04|R|F|1993-01-20|1993-03-18|1993-01-26|NONE|MAIL|efully eve\n4706|115455|7967|4|5|7352.25|0.06|0.06|R|F|1993-02-14|1993-01-31|1993-02-26|NONE|REG AIR|ptotes haggle ca\n4706|49189|1694|5|27|30730.86|0.06|0.08|A|F|1993-04-04|1993-03-11|1993-04-09|COLLECT COD|REG AIR|into beans. finally special instruct\n4707|33590|8597|1|7|10665.13|0.02|0.05|R|F|1995-05-14|1995-04-06|1995-06-06|COLLECT COD|SHIP|ecial sheaves boost blithely accor\n4707|135550|577|2|49|77691.95|0.00|0.07|N|F|1995-06-17|1995-05-16|1995-06-25|COLLECT COD|FOB| alongside of the slyly ironic instructio\n4708|190913|5952|1|18|36070.38|0.02|0.04|A|F|1994-11-11|1994-11-15|1994-11-26|NONE|REG AIR|special, eve\n4708|74046|1568|2|5|5100.20|0.05|0.05|A|F|1994-10-15|1994-12-02|1994-11-12|COLLECT COD|MAIL|ely. carefully sp\n4708|76824|9332|3|32|57626.24|0.04|0.07|A|F|1994-11-12|1994-11-14|1994-11-23|TAKE BACK RETURN|MAIL|the accounts. e\n4709|24334|4335|1|25|31458.25|0.03|0.05|N|O|1996-02-21|1996-02-11|1996-03-17|DELIVER IN PERSON|AIR|deposits grow. fluffily unusual accounts \n4709|176003|3555|2|25|26975.00|0.05|0.03|N|O|1996-01-22|1996-03-03|1996-02-21|DELIVER IN PERSON|REG AIR|inst the ironic, regul\n4710|182634|189|1|40|68665.20|0.10|0.08|A|F|1995-03-09|1995-02-25|1995-03-29|TAKE BACK RETURN|AIR|cross the blithely bold packages. silen\n4710|127253|9766|2|47|60171.75|0.04|0.01|R|F|1995-02-22|1995-01-12|1995-02-28|NONE|RAIL|blithely express packages. even, ironic re\n4711|132347|2348|1|7|9655.38|0.03|0.01|N|O|1998-05-12|1998-06-24|1998-05-24|COLLECT COD|MAIL|ly. bold accounts use fluff\n4711|144056|4057|2|15|16500.75|0.08|0.07|N|O|1998-06-09|1998-07-30|1998-06-18|COLLECT COD|SHIP| beans wake. deposits could bo\n4711|149014|9015|3|22|23386.22|0.02|0.03|N|O|1998-06-21|1998-06-18|1998-07-19|TAKE BACK RETURN|REG AIR|along the quickly careful packages. bli\n4711|64012|1531|4|8|7808.08|0.07|0.00|N|O|1998-06-17|1998-06-13|1998-06-27|TAKE BACK RETURN|SHIP|g to the carefully ironic deposits. specia\n4711|48820|3829|5|15|26532.30|0.05|0.01|N|O|1998-09-03|1998-07-15|1998-09-13|TAKE BACK RETURN|SHIP|ld requests: furiously final inst\n4711|115669|5670|6|45|75809.70|0.05|0.06|N|O|1998-05-19|1998-07-14|1998-05-21|COLLECT COD|SHIP| ironic theodolites \n4711|45160|2673|7|18|19892.88|0.05|0.04|N|O|1998-07-03|1998-07-31|1998-07-23|DELIVER IN PERSON|RAIL| blithely. bold asymptote\n4736|195526|3084|1|26|42159.52|0.03|0.03|N|O|1996-02-02|1996-01-18|1996-02-09|DELIVER IN PERSON|AIR|efully speci\n4736|3390|891|2|43|55615.77|0.06|0.07|N|O|1996-02-05|1995-12-21|1996-02-06|COLLECT COD|MAIL|quests. carefully \n4737|190140|7698|1|37|45515.18|0.03|0.04|R|F|1993-05-17|1993-04-10|1993-05-30|DELIVER IN PERSON|TRUCK|s. fluffily regular \n4737|68303|810|2|22|27968.60|0.04|0.04|A|F|1993-03-29|1993-05-22|1993-04-16|TAKE BACK RETURN|RAIL| hang fluffily around t\n4738|186778|4333|1|9|16782.93|0.04|0.04|A|F|1992-06-01|1992-06-26|1992-06-02|COLLECT COD|TRUCK|posits serve slyly. unusual pint\n4738|172482|5000|2|16|24871.68|0.07|0.08|A|F|1992-06-17|1992-06-20|1992-06-21|NONE|MAIL|nic deposits are slyly! carefu\n4738|99175|1685|3|50|58708.50|0.04|0.02|A|F|1992-06-18|1992-07-04|1992-07-07|TAKE BACK RETURN|TRUCK|the blithely ironic braids sleep slyly\n4738|28274|777|4|22|26449.94|0.02|0.08|A|F|1992-05-25|1992-05-19|1992-06-12|COLLECT COD|SHIP|ld, even packages. furio\n4738|186242|6243|5|13|17267.12|0.04|0.05|R|F|1992-05-30|1992-06-11|1992-06-26|COLLECT COD|AIR| wake. unusual platelets for the\n4738|158558|6104|6|10|16165.50|0.10|0.01|R|F|1992-07-10|1992-06-16|1992-07-25|TAKE BACK RETURN|SHIP|hins above the\n4738|82750|275|7|28|48517.00|0.05|0.07|A|F|1992-06-09|1992-07-05|1992-06-25|NONE|AIR|e furiously ironic excuses. care\n4739|167941|7942|1|8|16071.52|0.07|0.07|R|F|1993-06-22|1993-05-10|1993-07-11|TAKE BACK RETURN|SHIP|cording to the \n4739|184061|6580|2|31|35496.86|0.09|0.06|R|F|1993-06-20|1993-05-18|1993-06-26|COLLECT COD|SHIP|blithely special pin\n4739|99183|6711|3|30|35465.40|0.09|0.00|A|F|1993-05-29|1993-04-12|1993-06-18|NONE|TRUCK|ly even packages use across th\n4740|2282|2283|1|22|26054.16|0.06|0.01|N|O|1996-10-04|1996-08-17|1996-10-05|TAKE BACK RETURN|RAIL|final dependencies nag \n4740|152247|9793|2|24|31181.76|0.08|0.02|N|O|1996-09-10|1996-09-27|1996-10-07|TAKE BACK RETURN|TRUCK|hely regular deposits\n4741|72763|7778|1|24|41658.24|0.00|0.01|A|F|1992-09-16|1992-09-19|1992-09-20|DELIVER IN PERSON|RAIL|deas boost furiously slyly regular id\n4741|112165|2166|2|16|18834.56|0.01|0.07|R|F|1992-08-25|1992-08-10|1992-08-29|TAKE BACK RETURN|FOB|final foxes haggle r\n4741|155605|3151|3|24|39854.40|0.05|0.08|A|F|1992-11-04|1992-08-14|1992-11-06|TAKE BACK RETURN|MAIL|even requests.\n4741|50954|8470|4|39|74293.05|0.09|0.06|R|F|1992-10-28|1992-10-03|1992-11-11|COLLECT COD|SHIP|t, regular requests\n4741|178507|8508|5|40|63420.00|0.09|0.03|R|F|1992-09-20|1992-09-23|1992-10-09|TAKE BACK RETURN|REG AIR| fluffily slow deposits. fluffily regu\n4741|156686|9202|6|34|59251.12|0.02|0.07|R|F|1992-08-25|1992-08-18|1992-09-20|DELIVER IN PERSON|RAIL|sly special packages after the furiously\n4742|155564|8080|1|32|51825.92|0.10|0.08|R|F|1995-04-04|1995-06-12|1995-04-19|COLLECT COD|RAIL|eposits boost blithely. carefully regular a\n4742|154833|2379|2|29|54747.07|0.02|0.03|N|F|1995-06-15|1995-05-05|1995-06-24|COLLECT COD|REG AIR|integrate closely among t\n4742|71018|8540|3|15|14835.15|0.06|0.04|N|O|1995-07-20|1995-05-26|1995-08-11|NONE|SHIP|terns are sl\n4742|187940|5495|4|31|62866.14|0.05|0.08|N|F|1995-06-13|1995-05-08|1995-06-24|COLLECT COD|REG AIR|ke slyly among the furiousl\n4742|99331|9332|5|45|59864.85|0.05|0.00|R|F|1995-05-12|1995-05-14|1995-06-07|TAKE BACK RETURN|RAIL|ke carefully. do\n4743|59402|4413|1|19|25866.60|0.04|0.07|A|F|1993-06-23|1993-05-03|1993-07-20|COLLECT COD|AIR|hely even accounts\n4743|158379|3410|2|3|4312.11|0.01|0.03|R|F|1993-04-14|1993-06-08|1993-05-09|NONE|TRUCK|al requests. express idea\n4743|72936|7951|3|21|40087.53|0.08|0.03|A|F|1993-07-02|1993-06-15|1993-07-26|DELIVER IN PERSON|RAIL|ake blithely against the packages. reg\n4743|33527|3528|4|27|39434.04|0.08|0.05|R|F|1993-07-26|1993-05-27|1993-08-24|DELIVER IN PERSON|AIR|aids use. express deposits\n4768|35822|829|1|5|8789.10|0.00|0.03|R|F|1993-12-27|1994-02-09|1994-01-11|NONE|MAIL|egular accounts. bravely final fra\n4769|34240|1750|1|16|18787.84|0.08|0.05|N|O|1995-07-16|1995-07-05|1995-07-22|TAKE BACK RETURN|FOB| deposits. slyly even asymptote\n4769|62918|437|2|34|63950.94|0.06|0.07|N|O|1995-07-26|1995-05-18|1995-08-03|COLLECT COD|REG AIR|ven instructions. ca\n4769|46414|1423|3|36|48974.76|0.10|0.03|N|O|1995-07-22|1995-06-16|1995-08-11|NONE|RAIL|. slyly even deposit\n4769|68431|8432|4|45|62974.35|0.08|0.06|R|F|1995-06-01|1995-07-13|1995-06-04|TAKE BACK RETURN|RAIL|accounts are. even accounts sleep\n4769|111623|4135|5|15|24519.30|0.07|0.08|N|F|1995-06-12|1995-07-07|1995-07-04|NONE|SHIP|egular platelets can cajole across the \n4770|31961|9471|1|41|77611.36|0.00|0.08|N|O|1995-09-04|1995-08-08|1995-09-10|COLLECT COD|FOB|ithely even packages sleep caref\n4770|156036|8552|2|30|32760.90|0.09|0.07|N|O|1995-08-25|1995-08-27|1995-09-07|COLLECT COD|SHIP|ffily carefully ironic ideas. ironic d\n4771|48612|8613|1|9|14045.49|0.01|0.00|R|F|1993-02-28|1993-02-19|1993-03-25|NONE|FOB|riously after the packages. fina\n4771|15316|5317|2|21|25857.51|0.09|0.01|R|F|1993-01-19|1993-02-10|1993-02-01|NONE|FOB|fluffily pendi\n4771|11568|1569|3|5|7397.80|0.06|0.08|R|F|1993-01-07|1993-01-19|1993-01-26|NONE|RAIL|ar, quiet accounts nag furiously express id\n4771|8448|3449|4|21|28485.24|0.05|0.04|A|F|1992-12-20|1993-01-22|1992-12-26|TAKE BACK RETURN|SHIP| carefully re\n4772|86830|6831|1|1|1816.83|0.10|0.00|R|F|1994-11-13|1994-10-25|1994-11-15|DELIVER IN PERSON|AIR|ans. slyly even acc\n4772|145505|534|2|16|24808.00|0.07|0.06|R|F|1994-10-27|1994-12-07|1994-10-29|TAKE BACK RETURN|MAIL|egular accounts wake s\n4772|94751|4752|3|31|54118.25|0.02|0.04|A|F|1994-10-02|1994-10-21|1994-10-13|TAKE BACK RETURN|FOB|ests are thinly. furiously unusua\n4772|70799|5814|4|15|26546.85|0.02|0.07|R|F|1994-09-19|1994-10-22|1994-09-26|COLLECT COD|TRUCK| requests. express, regular th\n4773|143131|3132|1|23|27004.99|0.00|0.08|N|O|1996-01-01|1996-03-19|1996-01-04|NONE|FOB|ly express grouches wak\n4773|196476|8996|2|36|56608.92|0.09|0.04|N|O|1996-04-08|1996-03-03|1996-05-01|COLLECT COD|REG AIR| dependencies. quickly\n4773|166028|6029|3|49|53606.98|0.05|0.02|N|O|1996-01-26|1996-02-29|1996-01-27|TAKE BACK RETURN|FOB|y final reque\n4773|19370|6874|4|49|63179.13|0.09|0.04|N|O|1996-01-12|1996-02-17|1996-02-05|TAKE BACK RETURN|TRUCK|ly pending theodolites cajole caref\n4773|149461|4490|5|20|30209.20|0.02|0.07|N|O|1995-12-28|1996-02-17|1996-01-15|COLLECT COD|TRUCK| blithely final deposits nag after t\n4773|189276|1795|6|11|15017.97|0.10|0.06|N|O|1996-01-02|1996-01-29|1996-01-24|DELIVER IN PERSON|REG AIR|en accounts. slyly b\n4773|157333|2364|7|6|8341.98|0.07|0.01|N|O|1996-03-09|1996-03-18|1996-03-27|NONE|AIR|latelets haggle s\n4774|83182|5691|1|45|52433.10|0.10|0.00|R|F|1993-07-07|1993-06-08|1993-07-31|COLLECT COD|TRUCK| haggle busily afte\n4774|38828|1332|2|4|7067.28|0.02|0.03|A|F|1993-08-03|1993-05-30|1993-08-19|COLLECT COD|FOB|xes according to the foxes wake above the f\n4774|172460|2461|3|47|72025.62|0.10|0.08|R|F|1993-06-13|1993-07-04|1993-07-09|TAKE BACK RETURN|FOB|regular dolphins above the furi\n4774|129151|6688|4|30|35404.50|0.05|0.08|A|F|1993-08-18|1993-06-08|1993-08-21|DELIVER IN PERSON|REG AIR|tions against the blithely final theodolit\n4775|73979|6487|1|1|1952.97|0.10|0.02|N|O|1995-09-06|1995-09-28|1995-09-29|DELIVER IN PERSON|MAIL|furiously ironic theodolite\n4775|152779|5295|2|37|67775.49|0.02|0.01|N|O|1995-09-06|1995-09-28|1995-09-28|COLLECT COD|TRUCK|ts. pinto beans use according to th\n4775|152242|9788|3|34|44004.16|0.09|0.06|N|O|1995-09-14|1995-10-15|1995-09-21|DELIVER IN PERSON|MAIL|onic epitaphs. f\n4775|118461|5995|4|39|57698.94|0.07|0.04|N|O|1995-08-30|1995-10-12|1995-09-20|NONE|AIR|eep never with the slyly regular acc\n4800|96498|1517|1|11|16439.39|0.03|0.03|R|F|1992-01-27|1992-03-16|1992-02-19|TAKE BACK RETURN|RAIL|ic dependenc\n4800|25491|496|2|1|1416.49|0.06|0.06|A|F|1992-02-23|1992-03-16|1992-03-20|TAKE BACK RETURN|MAIL|nal accounts are blithely deposits. bol\n4800|10518|5521|3|21|29998.71|0.09|0.05|A|F|1992-02-14|1992-03-15|1992-02-26|NONE|SHIP|ithely according to \n4800|175228|5229|4|38|49522.36|0.10|0.08|R|F|1992-02-01|1992-02-28|1992-02-21|NONE|TRUCK|s sleep fluffily. furiou\n4800|52240|2241|5|24|28613.76|0.08|0.04|R|F|1992-01-14|1992-02-23|1992-01-25|NONE|TRUCK|ully carefully r\n4801|183937|1492|1|37|74774.41|0.10|0.02|N|O|1996-03-09|1996-02-29|1996-03-25|TAKE BACK RETURN|FOB|uests hinder blithely against the instr\n4801|25246|7749|2|34|39822.16|0.03|0.02|N|O|1996-02-05|1996-04-16|1996-02-23|NONE|SHIP|y final requests \n4801|109282|9283|3|4|5165.12|0.04|0.04|N|O|1996-03-23|1996-04-04|1996-03-25|COLLECT COD|RAIL|pitaphs. regular, reg\n4801|91183|1184|4|39|45793.02|0.07|0.01|N|O|1996-03-19|1996-03-21|1996-04-17|TAKE BACK RETURN|REG AIR|warhorses wake never for the care\n4802|39444|9445|1|6|8300.64|0.00|0.06|N|O|1997-04-16|1997-03-25|1997-04-21|TAKE BACK RETURN|SHIP|unusual accounts wake blithely. b\n4803|131052|6079|1|2|2166.10|0.08|0.03|N|O|1996-04-16|1996-03-20|1996-05-15|NONE|REG AIR|gular reque\n4803|175165|2717|2|47|58287.52|0.10|0.00|N|O|1996-03-14|1996-03-30|1996-03-15|DELIVER IN PERSON|FOB|ly final excuses. slyly express requ\n4803|195742|8262|3|42|77185.08|0.04|0.08|N|O|1996-04-27|1996-05-05|1996-05-17|NONE|TRUCK| accounts affix quickly ar\n4803|21312|6317|4|24|29599.44|0.10|0.04|N|O|1996-02-24|1996-04-02|1996-02-28|NONE|MAIL|t blithely slyly special decoys. \n4803|188148|5703|5|21|25958.94|0.03|0.06|N|O|1996-05-25|1996-03-15|1996-06-09|COLLECT COD|FOB| silent packages use. b\n4803|193599|3600|6|19|32159.21|0.07|0.00|N|O|1996-04-20|1996-03-25|1996-04-27|TAKE BACK RETURN|RAIL|sts. enticing, even\n4804|127009|4546|1|44|45584.00|0.06|0.08|A|F|1992-05-02|1992-03-24|1992-05-28|TAKE BACK RETURN|AIR|aggle quickly among the slyly fi\n4804|34942|9949|2|41|76954.54|0.10|0.02|R|F|1992-04-06|1992-04-12|1992-05-03|COLLECT COD|MAIL|. deposits haggle express tithes?\n4804|64829|9842|3|33|59196.06|0.09|0.05|A|F|1992-03-02|1992-04-14|1992-03-13|DELIVER IN PERSON|AIR|, thin excuses. \n4805|149235|9236|1|7|8989.61|0.09|0.03|A|F|1992-05-01|1992-07-09|1992-05-09|NONE|FOB| requests. regular deposit\n4805|188967|1486|2|45|92518.20|0.02|0.03|R|F|1992-06-16|1992-06-08|1992-07-03|NONE|TRUCK|the furiously sly t\n4805|153857|1403|3|44|84077.40|0.01|0.02|R|F|1992-05-14|1992-06-23|1992-05-25|DELIVER IN PERSON|SHIP|eposits sleep furiously qui\n4805|64284|9297|4|13|16227.64|0.04|0.04|R|F|1992-07-16|1992-06-07|1992-08-10|COLLECT COD|AIR|its serve about the accounts. slyly regu\n4805|8529|8530|5|42|60375.84|0.03|0.03|R|F|1992-08-17|1992-07-03|1992-09-14|NONE|REG AIR|the regular, fina\n4805|135959|986|6|18|35909.10|0.06|0.04|A|F|1992-06-07|1992-07-10|1992-06-12|COLLECT COD|TRUCK|o use pending, unusu\n4806|15046|5047|1|26|24987.04|0.10|0.05|R|F|1993-05-28|1993-06-07|1993-05-29|DELIVER IN PERSON|SHIP| bold pearls sublate blithely. quickly pe\n4806|71151|8673|2|6|6732.90|0.01|0.06|A|F|1993-05-17|1993-07-19|1993-05-29|TAKE BACK RETURN|SHIP|even theodolites. packages sl\n4806|28806|1309|3|8|13878.40|0.09|0.00|A|F|1993-05-08|1993-07-16|1993-05-28|NONE|TRUCK|requests boost blithely. qui\n4807|121903|6928|1|9|17324.10|0.04|0.08|N|O|1997-04-23|1997-03-01|1997-05-15|TAKE BACK RETURN|TRUCK|may are blithely. carefully even pinto b\n4807|9767|9768|2|41|68747.16|0.07|0.08|N|O|1997-05-02|1997-03-31|1997-05-15|TAKE BACK RETURN|AIR| fluffily re\n4807|144309|4310|3|34|46012.20|0.06|0.02|N|O|1997-01-31|1997-03-13|1997-02-01|NONE|SHIP|ecial ideas. deposits according to the fin\n4807|189336|4373|4|32|45610.56|0.05|0.00|N|O|1997-04-04|1997-03-21|1997-04-16|NONE|RAIL|efully even dolphins slee\n4807|158706|6252|5|2|3529.40|0.02|0.05|N|O|1997-05-09|1997-04-03|1997-06-05|TAKE BACK RETURN|RAIL|deas wake bli\n4807|159022|9023|6|22|23782.44|0.09|0.06|N|O|1997-03-13|1997-02-23|1997-04-01|NONE|FOB|es use final excuses. furiously final\n4832|14730|4731|1|23|37828.79|0.03|0.01|N|O|1997-12-05|1998-01-05|1997-12-10|NONE|RAIL|y express depo\n4832|151303|8849|2|10|13543.00|0.00|0.06|N|O|1998-01-08|1998-02-01|1998-01-11|DELIVER IN PERSON|MAIL|ly. blithely bold pinto beans should have\n4832|148228|743|3|4|5104.88|0.04|0.01|N|O|1998-01-16|1998-02-12|1998-02-08|TAKE BACK RETURN|AIR|ages. slyly express deposits cajole car\n4832|63875|3876|4|6|11033.22|0.02|0.01|N|O|1997-12-08|1998-02-03|1997-12-10|COLLECT COD|TRUCK|ages cajole after the bold requests. furi\n4832|137354|4894|5|43|59828.05|0.10|0.08|N|O|1997-12-31|1998-02-20|1998-01-26|COLLECT COD|RAIL|oze according to the accou\n4833|106154|8665|1|31|35964.65|0.08|0.04|N|O|1996-06-24|1996-07-15|1996-07-02|NONE|SHIP|ven instructions cajole against the caref\n4833|116084|3618|2|11|12100.88|0.03|0.01|N|O|1996-08-24|1996-07-26|1996-09-19|NONE|REG AIR|s nag above the busily sile\n4833|17043|7044|3|26|24961.04|0.08|0.04|N|O|1996-05-13|1996-07-12|1996-05-31|NONE|SHIP|s packages. even gif\n4833|35345|352|4|19|24326.46|0.07|0.07|N|O|1996-08-21|1996-07-09|1996-09-10|TAKE BACK RETURN|AIR|y quick theodolit\n4833|34266|1776|5|4|4801.04|0.10|0.02|N|O|1996-08-16|1996-06-29|1996-08-22|NONE|AIR|y pending packages sleep blithely regular r\n4834|182667|222|1|27|47240.82|0.06|0.02|N|O|1997-01-09|1996-10-27|1997-01-27|DELIVER IN PERSON|RAIL|es nag blithe\n4834|70960|3468|2|26|50204.96|0.01|0.00|N|O|1996-10-04|1996-10-21|1996-10-10|DELIVER IN PERSON|TRUCK|ages dazzle carefully. slyly daring foxes\n4834|22952|7957|3|34|63748.30|0.03|0.01|N|O|1996-12-09|1996-11-26|1996-12-10|NONE|MAIL|ounts haggle bo\n4834|142668|5183|4|38|65005.08|0.03|0.06|N|O|1997-01-10|1996-12-06|1997-01-22|COLLECT COD|FOB|alongside of the carefully even plate\n4835|178289|8290|1|18|24611.04|0.00|0.03|R|F|1995-02-17|1994-12-14|1995-03-17|DELIVER IN PERSON|MAIL|eat furiously against the slyly \n4835|90575|3085|2|3|4696.71|0.09|0.06|R|F|1995-01-24|1995-01-12|1995-02-16|COLLECT COD|AIR|etimes final pac\n4835|85483|5484|3|27|39648.96|0.05|0.00|A|F|1994-12-10|1994-12-13|1995-01-02|DELIVER IN PERSON|REG AIR| accounts after the car\n4835|101125|6146|4|23|25900.76|0.08|0.07|A|F|1995-02-05|1995-01-04|1995-02-28|NONE|SHIP|e carefully regular foxes. deposits are sly\n4836|161531|4048|1|22|35035.66|0.01|0.03|N|O|1997-03-03|1997-02-23|1997-03-04|NONE|SHIP|al pinto beans. care\n4836|47331|9836|2|16|20453.28|0.07|0.08|N|O|1997-01-14|1997-03-05|1997-01-30|COLLECT COD|MAIL|gular packages against the express reque\n4836|75666|3188|3|14|22983.24|0.03|0.08|N|O|1997-02-21|1997-02-06|1997-03-08|COLLECT COD|MAIL|lites. unusual, bold dolphins ar\n4836|105341|362|4|15|20195.10|0.10|0.00|N|O|1997-03-08|1997-03-14|1997-03-30|TAKE BACK RETURN|TRUCK|eep slyly. even requests cajole\n4836|50120|5131|5|12|12841.44|0.01|0.04|N|O|1997-02-02|1997-02-10|1997-02-03|COLLECT COD|TRUCK|sly ironic accoun\n4837|41768|9281|1|16|27356.16|0.09|0.04|N|O|1998-08-12|1998-06-06|1998-08-26|COLLECT COD|FOB|ing requests are blithely regular instructi\n4837|192758|5278|2|16|29612.00|0.01|0.02|N|O|1998-08-19|1998-06-18|1998-08-26|NONE|RAIL|counts cajole slyly furiou\n4837|67706|2719|3|42|70295.40|0.10|0.00|N|O|1998-06-19|1998-07-06|1998-06-23|COLLECT COD|MAIL|o the furiously final theodolites boost\n4838|121792|1793|1|35|63482.65|0.01|0.00|R|F|1992-10-30|1992-10-23|1992-11-21|TAKE BACK RETURN|RAIL|ly blithely unusual foxes. even package\n4838|147002|9517|2|2|2098.00|0.03|0.08|R|F|1992-08-11|1992-09-16|1992-08-26|COLLECT COD|MAIL|hely final notornis are furiously blithe\n4838|51200|1201|3|26|29931.20|0.06|0.04|R|F|1992-09-03|1992-10-25|1992-09-11|TAKE BACK RETURN|FOB|ular requests boost about the packages. r\n4839|59762|7278|1|5|8608.80|0.10|0.07|A|F|1994-09-07|1994-07-15|1994-10-05|DELIVER IN PERSON|FOB|ses integrate. regular deposits are about \n4839|9631|9632|2|25|38515.75|0.02|0.02|R|F|1994-05-20|1994-07-08|1994-05-30|NONE|REG AIR|regular packages ab\n4839|59660|9661|3|18|29153.88|0.06|0.01|R|F|1994-05-18|1994-06-13|1994-06-09|TAKE BACK RETURN|FOB|blithely ironic theodolites use along\n4839|99142|9143|4|19|21681.66|0.07|0.08|R|F|1994-05-20|1994-07-14|1994-05-30|NONE|REG AIR| deposits sublate furiously ir\n4839|70988|6003|5|9|17630.82|0.05|0.01|R|F|1994-06-17|1994-06-18|1994-07-10|NONE|SHIP|ounts haggle carefully above\n4864|149726|7269|1|28|49720.16|0.06|0.08|A|F|1993-02-06|1992-12-15|1993-02-10|COLLECT COD|REG AIR|thely around the bli\n4864|37481|9985|2|38|53902.24|0.10|0.02|R|F|1992-12-20|1993-01-07|1993-01-06|TAKE BACK RETURN|SHIP|ording to the ironic, ir\n4864|132447|7474|3|45|66574.80|0.02|0.01|A|F|1992-11-17|1993-01-02|1992-11-26|COLLECT COD|SHIP|round the furiously careful pa\n4864|30832|5839|4|46|81090.18|0.07|0.03|A|F|1993-02-24|1993-01-02|1993-03-17|TAKE BACK RETURN|RAIL|sts use carefully across the carefull\n4865|161406|8955|1|16|23478.40|0.07|0.05|N|O|1997-10-02|1997-08-20|1997-10-04|COLLECT COD|TRUCK|osits haggle. fur\n4865|136210|6211|2|4|4984.84|0.07|0.01|N|O|1997-07-24|1997-07-25|1997-08-07|TAKE BACK RETURN|FOB|sts. blithely special instruction\n4865|67618|5137|3|44|69766.84|0.10|0.08|N|O|1997-07-25|1997-08-20|1997-08-22|COLLECT COD|FOB|even deposits sleep against the quickly r\n4865|49947|4956|4|21|39835.74|0.04|0.02|N|O|1997-07-17|1997-08-10|1997-07-21|NONE|RAIL|eposits detect sly\n4865|53744|8755|5|33|56025.42|0.00|0.05|N|O|1997-07-17|1997-08-16|1997-07-30|TAKE BACK RETURN|FOB|y pending notornis ab\n4865|64770|9783|6|47|81534.19|0.00|0.05|N|O|1997-08-26|1997-08-07|1997-08-31|NONE|RAIL|y unusual packages. packages\n4866|10585|5588|1|9|13460.22|0.01|0.05|N|O|1997-08-30|1997-09-18|1997-09-24|TAKE BACK RETURN|MAIL|ven dependencies x-ray. quic\n4866|101252|1253|2|1|1253.25|0.06|0.00|N|O|1997-10-15|1997-10-01|1997-11-14|TAKE BACK RETURN|AIR|latelets nag. q\n4866|130432|2946|3|17|24861.31|0.07|0.00|N|O|1997-11-26|1997-10-11|1997-12-12|COLLECT COD|TRUCK|ess packages doubt. even somas wake f\n4867|81666|1667|1|7|11533.62|0.09|0.03|A|F|1992-07-17|1992-08-17|1992-07-22|COLLECT COD|FOB|e carefully even packages. slyly ironic i\n4867|159641|2157|2|3|5101.92|0.04|0.08|R|F|1992-07-04|1992-07-15|1992-07-21|NONE|AIR|yly silent deposits\n4868|72576|5084|1|47|72782.79|0.03|0.03|N|O|1997-04-29|1997-04-27|1997-05-11|DELIVER IN PERSON|SHIP|gle unusual, fluffy packages. foxes cajol\n4868|179044|9045|2|8|8984.32|0.10|0.08|N|O|1997-03-26|1997-05-09|1997-04-16|NONE|RAIL|ly special th\n4868|190723|724|3|49|88872.28|0.09|0.03|N|O|1997-04-23|1997-05-07|1997-04-26|NONE|SHIP|ys engage. th\n4868|79046|9047|4|34|34851.36|0.04|0.02|N|O|1997-05-19|1997-04-27|1997-06-15|NONE|RAIL|en instructions about th\n4868|121574|1575|5|22|35102.54|0.07|0.06|N|O|1997-04-26|1997-05-16|1997-05-01|DELIVER IN PERSON|FOB|osits. final foxes boost regular,\n4869|40808|3313|1|31|54212.80|0.10|0.01|A|F|1995-01-17|1994-11-30|1995-02-02|NONE|SHIP|ins. always unusual ideas across the ir\n4869|57367|2378|2|24|31784.64|0.09|0.06|A|F|1994-11-17|1994-11-07|1994-11-27|COLLECT COD|MAIL|olites cajole after the ideas. special t\n4869|156025|6026|3|25|27025.50|0.00|0.05|R|F|1994-11-25|1994-11-14|1994-12-19|DELIVER IN PERSON|AIR|e according t\n4869|102539|7560|4|24|36996.72|0.10|0.07|R|F|1994-11-23|1994-11-18|1994-12-11|DELIVER IN PERSON|MAIL|se deposits above the sly, q\n4869|172852|7887|5|42|80843.70|0.07|0.04|R|F|1994-10-16|1994-12-10|1994-11-07|TAKE BACK RETURN|REG AIR| slyly even instructions. \n4869|121042|8579|6|30|31891.20|0.00|0.05|A|F|1995-01-09|1994-11-20|1995-02-02|COLLECT COD|RAIL|gedly even requests. s\n4870|47881|386|1|49|89615.12|0.05|0.05|R|F|1994-11-14|1994-10-24|1994-12-12|TAKE BACK RETURN|SHIP| regular packages \n4870|126176|3713|2|6|7213.02|0.06|0.08|A|F|1994-09-09|1994-10-16|1994-09-21|DELIVER IN PERSON|TRUCK|ress requests. bold, silent pinto bea\n4870|30179|5186|3|5|5545.85|0.05|0.00|R|F|1994-10-11|1994-10-07|1994-10-24|NONE|AIR|s haggle furiously. slyly ironic dinos\n4870|5978|8479|4|4|7535.88|0.03|0.08|A|F|1994-10-23|1994-09-16|1994-11-04|COLLECT COD|RAIL|its wake quickly. slyly quick\n4870|70598|3106|5|36|56469.24|0.09|0.06|A|F|1994-09-06|1994-09-17|1994-10-01|COLLECT COD|REG AIR| instructions. carefully pending pac\n4871|176651|4203|1|14|24187.10|0.07|0.03|N|O|1995-09-30|1995-07-29|1995-10-18|TAKE BACK RETURN|REG AIR|inst the never ironic \n4871|160509|8058|2|17|26681.50|0.07|0.03|N|O|1995-09-09|1995-09-01|1995-10-02|DELIVER IN PERSON|AIR|es. carefully ev\n4871|62067|2068|3|3|3087.18|0.03|0.06|N|O|1995-10-03|1995-08-10|1995-10-06|DELIVER IN PERSON|TRUCK|y special packages wak\n4871|148471|6014|4|35|53181.45|0.08|0.07|N|O|1995-08-11|1995-07-18|1995-08-29|DELIVER IN PERSON|TRUCK|ackages sle\n4871|151422|1423|5|10|14734.20|0.09|0.02|N|O|1995-09-12|1995-09-02|1995-10-05|TAKE BACK RETURN|AIR|s integrate after the a\n4871|135500|3040|6|36|55278.00|0.02|0.08|N|O|1995-09-18|1995-08-29|1995-10-05|TAKE BACK RETURN|AIR|ely according\n4871|139040|6580|7|10|10790.40|0.10|0.02|N|O|1995-07-13|1995-08-19|1995-07-29|NONE|REG AIR|p ironic theodolites. slyly even platel\n4896|40766|767|1|19|32428.44|0.09|0.05|A|F|1992-12-13|1992-11-13|1993-01-09|NONE|AIR|nusual requ\n4896|139589|4616|2|44|71657.52|0.04|0.03|A|F|1992-11-24|1992-11-15|1992-12-18|COLLECT COD|MAIL|e after the slowly f\n4896|57169|4685|3|6|6756.96|0.04|0.04|A|F|1992-10-30|1992-11-12|1992-11-28|DELIVER IN PERSON|TRUCK|usly regular deposits\n4896|22746|2747|4|5|8343.70|0.08|0.02|R|F|1992-12-02|1992-11-11|1992-12-19|COLLECT COD|SHIP|eposits hang carefully. sly\n4896|85033|2558|5|21|21378.63|0.07|0.08|R|F|1992-11-18|1992-11-18|1992-11-29|DELIVER IN PERSON|TRUCK|ly express deposits. carefully pending depo\n4897|54403|4404|1|26|35292.40|0.01|0.01|R|F|1992-12-22|1992-10-25|1992-12-27|DELIVER IN PERSON|TRUCK|. carefully ironic dep\n4897|142171|7200|2|34|41247.78|0.02|0.00|R|F|1992-12-31|1992-11-11|1993-01-30|COLLECT COD|AIR|ts. special dependencies use fluffily \n4897|54770|2286|3|42|72440.34|0.09|0.03|A|F|1992-09-23|1992-10-28|1992-10-02|DELIVER IN PERSON|FOB|sts. blithely regular deposits will have\n4897|103352|3353|4|19|25751.65|0.03|0.00|A|F|1992-11-08|1992-12-14|1992-12-03|DELIVER IN PERSON|FOB|! ironic, pending dependencies doze furiou\n4898|71592|6607|1|44|68797.96|0.07|0.02|A|F|1994-09-13|1994-08-18|1994-09-16|NONE|FOB|y regular grouches about\n4899|33087|597|1|14|14281.12|0.06|0.00|R|F|1993-11-10|1994-01-10|1993-11-20|NONE|REG AIR| foxes eat\n4900|115397|420|1|40|56495.60|0.10|0.03|A|F|1992-09-02|1992-09-25|1992-09-21|COLLECT COD|TRUCK|heodolites. request\n4900|76356|6357|2|33|43967.55|0.06|0.06|R|F|1992-08-18|1992-09-20|1992-08-19|COLLECT COD|MAIL|nto beans nag slyly reg\n4900|102019|7040|3|48|49008.48|0.02|0.00|R|F|1992-09-18|1992-08-14|1992-09-28|TAKE BACK RETURN|MAIL|uickly ironic ideas kindle s\n4900|31089|1090|4|20|20401.60|0.05|0.00|R|F|1992-09-22|1992-09-23|1992-09-27|TAKE BACK RETURN|MAIL|yers. accounts affix somet\n4900|104891|7402|5|40|75835.60|0.03|0.02|R|F|1992-07-14|1992-09-05|1992-07-20|NONE|REG AIR|luffily final dol\n4900|102405|4916|6|46|64740.40|0.06|0.08|R|F|1992-07-11|1992-09-19|1992-07-16|TAKE BACK RETURN|SHIP|ly final acco\n4901|140928|8471|1|37|72850.04|0.00|0.04|N|O|1998-01-26|1998-02-20|1998-01-31|DELIVER IN PERSON|TRUCK| furiously ev\n4901|164332|6849|2|12|16755.96|0.00|0.04|N|O|1998-01-12|1998-02-06|1998-02-03|COLLECT COD|REG AIR|y unusual deposits prom\n4901|119727|2239|3|16|27947.52|0.05|0.08|N|O|1998-04-19|1998-03-18|1998-04-21|NONE|AIR|deposits. blithely fin\n4901|35623|5624|4|41|63903.42|0.03|0.00|N|O|1998-03-18|1998-02-18|1998-04-14|TAKE BACK RETURN|AIR|efully bold packages affix carefully eve\n4901|115677|5678|5|40|67706.80|0.06|0.02|N|O|1998-01-08|1998-01-30|1998-01-15|DELIVER IN PERSON|MAIL|ect across the furiou\n4902|195198|2756|1|22|28450.18|0.00|0.04|N|O|1998-10-17|1998-08-10|1998-10-21|COLLECT COD|RAIL|r the furiously final fox\n4902|82370|7387|2|1|1352.37|0.09|0.04|N|O|1998-10-12|1998-08-20|1998-11-08|NONE|RAIL|daring foxes? even, bold requests wake f\n4903|120758|759|1|1|1778.75|0.06|0.03|R|F|1992-04-23|1992-06-13|1992-05-03|NONE|SHIP|nusual requests\n4903|164392|4393|2|6|8738.34|0.09|0.07|R|F|1992-04-01|1992-05-16|1992-04-11|DELIVER IN PERSON|SHIP|azzle quickly along the blithely final pla\n4903|119755|7289|3|27|47918.25|0.07|0.06|A|F|1992-06-29|1992-06-09|1992-07-08|COLLECT COD|RAIL|pinto beans are; \n4928|99357|9358|1|4|5425.40|0.04|0.02|R|F|1993-10-25|1993-12-24|1993-11-16|TAKE BACK RETURN|REG AIR|bout the slyly final accounts. carefull\n4928|92887|2888|2|20|37597.60|0.03|0.08|A|F|1994-01-19|1993-11-29|1994-02-13|DELIVER IN PERSON|SHIP|quiet theodolites ca\n4928|148249|5792|3|34|44106.16|0.06|0.05|A|F|1993-10-12|1993-12-31|1993-10-14|DELIVER IN PERSON|AIR|, regular depos\n4929|13595|8598|1|20|30171.80|0.00|0.04|N|O|1996-03-12|1996-05-23|1996-03-20|COLLECT COD|REG AIR| final pinto beans detect. final,\n4929|78044|5566|2|40|40881.60|0.08|0.03|N|O|1996-05-30|1996-04-13|1996-06-22|TAKE BACK RETURN|AIR|unts against \n4929|76404|8912|3|32|44172.80|0.08|0.02|N|O|1996-04-28|1996-05-23|1996-04-30|COLLECT COD|TRUCK|usly at the blithely pending pl\n4929|108935|3956|4|26|50542.18|0.00|0.05|N|O|1996-06-10|1996-05-29|1996-06-26|DELIVER IN PERSON|RAIL| slyly. fl\n4929|66430|6431|5|24|33514.32|0.09|0.05|N|O|1996-04-15|1996-04-30|1996-05-09|NONE|MAIL| accounts boost\n4930|186572|6573|1|35|58049.95|0.03|0.01|A|F|1994-07-09|1994-07-30|1994-07-15|NONE|RAIL|lose slyly regular dependencies. fur\n4930|114748|2282|2|20|35254.80|0.02|0.04|A|F|1994-08-21|1994-06-17|1994-08-24|COLLECT COD|FOB|he carefully\n4930|167762|279|3|28|51233.28|0.00|0.08|R|F|1994-08-27|1994-06-27|1994-09-18|COLLECT COD|TRUCK|e ironic, unusual courts. regula\n4930|165538|5539|4|42|67348.26|0.00|0.00|A|F|1994-06-18|1994-06-22|1994-07-10|COLLECT COD|AIR|ions haggle. furiously regular ideas use \n4930|189517|7072|5|38|61047.38|0.02|0.03|A|F|1994-06-06|1994-06-18|1994-07-03|TAKE BACK RETURN|AIR|bold requests sleep never\n4931|193048|8087|1|1|1141.04|0.08|0.06|A|F|1995-01-24|1994-12-19|1995-02-07|DELIVER IN PERSON|SHIP| furiously \n4931|150506|8052|2|8|12452.00|0.06|0.02|R|F|1994-12-15|1995-01-14|1995-01-06|NONE|SHIP|ts boost. packages wake sly\n4931|143024|3025|3|20|21340.40|0.09|0.00|A|F|1995-01-25|1994-12-21|1995-02-06|DELIVER IN PERSON|MAIL|the furious\n4931|199283|6841|4|50|69114.00|0.04|0.01|A|F|1994-12-15|1994-12-18|1994-12-23|COLLECT COD|REG AIR|s haggle al\n4931|149123|1638|5|25|29303.00|0.05|0.05|R|F|1994-12-19|1995-01-05|1994-12-21|COLLECT COD|FOB|aggle bravely according to the quic\n4931|102419|4930|6|8|11371.28|0.02|0.03|A|F|1995-02-16|1994-12-30|1995-03-15|DELIVER IN PERSON|SHIP|dependencies are slyly\n4932|50248|7764|1|13|15577.12|0.04|0.03|A|F|1993-09-13|1993-10-16|1993-09-20|DELIVER IN PERSON|SHIP|slyly according to the furiously fin\n4932|102499|30|2|15|22522.35|0.01|0.02|R|F|1993-11-15|1993-10-25|1993-11-29|NONE|REG AIR|yly. unusu\n4932|86827|9336|3|5|9069.10|0.06|0.06|A|F|1993-10-01|1993-09-13|1993-10-04|NONE|MAIL| haggle furiously. slyly ironic packages sl\n4932|97299|2318|4|11|14259.19|0.09|0.06|A|F|1993-09-21|1993-09-30|1993-09-23|COLLECT COD|SHIP|as. special depende\n4933|31326|8836|1|48|60351.36|0.08|0.00|N|O|1995-10-10|1995-10-03|1995-11-04|COLLECT COD|SHIP|ideas. sly\n4933|81917|6934|2|2|3797.82|0.09|0.00|N|O|1995-10-01|1995-09-29|1995-10-19|DELIVER IN PERSON|MAIL|ctions nag final instructions. accou\n4934|96074|1093|1|48|51363.36|0.00|0.01|N|O|1997-05-20|1997-04-22|1997-06-02|TAKE BACK RETURN|SHIP| ideas cajol\n4934|109380|9381|2|41|56964.58|0.06|0.06|N|O|1997-06-04|1997-04-11|1997-06-25|TAKE BACK RETURN|FOB|wake final, ironic f\n4934|139725|9726|3|8|14117.76|0.03|0.06|N|O|1997-05-20|1997-04-30|1997-05-27|TAKE BACK RETURN|MAIL|arefully express pains cajo\n4934|147233|9748|4|9|11522.07|0.06|0.08|N|O|1997-06-10|1997-04-09|1997-06-12|TAKE BACK RETURN|REG AIR| haggle alongside of the\n4934|137089|7090|5|29|32656.32|0.09|0.03|N|O|1997-04-10|1997-05-05|1997-05-04|DELIVER IN PERSON|AIR|aggle furiously among the busily final re\n4934|51634|1635|6|42|66596.46|0.00|0.07|N|O|1997-03-19|1997-05-05|1997-03-25|NONE|MAIL|ven, ironic ideas\n4934|10672|3174|7|2|3165.34|0.10|0.06|N|O|1997-06-05|1997-03-26|1997-06-09|COLLECT COD|MAIL|ongside of the brave, regula\n4935|160733|734|1|13|23318.49|0.09|0.01|A|F|1993-06-20|1993-08-13|1993-06-27|COLLECT COD|REG AIR|ly requests. final deposits might \n4935|39163|6673|2|37|40779.92|0.01|0.05|R|F|1993-08-30|1993-07-23|1993-09-07|TAKE BACK RETURN|RAIL|y even dependencies nag a\n4935|10868|5871|3|24|42692.64|0.06|0.04|A|F|1993-05-29|1993-08-17|1993-06-22|NONE|RAIL|ly quickly s\n4935|44935|4936|4|49|92116.57|0.06|0.01|A|F|1993-09-16|1993-08-21|1993-10-12|COLLECT COD|TRUCK|ffily after the furiou\n4935|9508|9509|5|14|19845.00|0.08|0.08|A|F|1993-05-30|1993-07-25|1993-05-31|COLLECT COD|FOB|slowly. blith\n4935|187915|2952|6|36|72104.76|0.10|0.00|R|F|1993-07-11|1993-07-04|1993-08-01|DELIVER IN PERSON|RAIL|requests across the quick\n4960|17091|2094|1|36|36291.24|0.01|0.05|R|F|1995-03-06|1995-05-04|1995-04-05|TAKE BACK RETURN|RAIL|c, unusual accou\n4960|44360|9369|2|6|7826.16|0.03|0.08|R|F|1995-03-21|1995-05-13|1995-04-14|TAKE BACK RETURN|SHIP|ual package\n4960|148041|5584|3|9|9801.36|0.01|0.03|A|F|1995-03-20|1995-05-05|1995-04-17|COLLECT COD|RAIL|e blithely carefully fina\n4960|119052|4075|4|14|14994.70|0.00|0.06|A|F|1995-04-03|1995-04-17|1995-04-07|NONE|RAIL|accounts. warhorses are. grouches \n4960|97171|2190|5|8|9345.36|0.07|0.04|R|F|1995-03-14|1995-04-18|1995-04-09|NONE|FOB|as. busily regular packages nag. \n4960|145105|5106|6|37|42553.70|0.10|0.04|R|F|1995-05-23|1995-04-12|1995-06-01|DELIVER IN PERSON|MAIL|ending theodolites w\n4960|169635|9636|7|42|71594.46|0.08|0.07|A|F|1995-04-19|1995-04-11|1995-05-08|NONE|SHIP|s requests cajole. \n4961|43748|8757|1|38|64286.12|0.10|0.07|N|O|1998-07-09|1998-06-03|1998-07-11|TAKE BACK RETURN|FOB|e on the blithely bold accounts. unu\n4961|59997|5008|2|1|1956.99|0.08|0.08|N|O|1998-07-08|1998-05-25|1998-07-12|DELIVER IN PERSON|MAIL|s affix carefully silent dependen\n4961|161177|1178|3|41|50764.97|0.02|0.02|N|O|1998-07-15|1998-06-15|1998-08-05|TAKE BACK RETURN|REG AIR|ily against the n\n4961|99354|4373|4|10|13533.50|0.02|0.04|N|O|1998-04-15|1998-07-03|1998-04-18|DELIVER IN PERSON|MAIL|quests. regular, ironic ideas at the ironi\n4962|18377|3380|1|46|59587.02|0.01|0.07|R|F|1993-08-23|1993-09-04|1993-08-27|COLLECT COD|REG AIR| pinto beans grow about the sl\n4963|167840|2873|1|38|72497.92|0.08|0.02|N|O|1996-12-25|1996-12-12|1997-01-02|COLLECT COD|AIR|tegrate daringly accou\n4963|75326|2848|2|16|20821.12|0.00|0.03|N|O|1996-11-20|1997-01-13|1996-12-06|COLLECT COD|MAIL| carefully slyly u\n4964|132406|4920|1|29|41713.60|0.04|0.01|N|O|1997-10-18|1997-08-30|1997-11-01|NONE|AIR|k accounts nag carefully-- ironic, fin\n4964|147067|9582|2|46|51246.76|0.06|0.06|N|O|1997-10-05|1997-09-12|1997-10-11|NONE|TRUCK|althy deposits\n4964|142559|2560|3|18|28827.90|0.00|0.06|N|O|1997-10-13|1997-09-01|1997-11-10|DELIVER IN PERSON|AIR| platelets. furio\n4964|179773|2291|4|12|22233.24|0.08|0.01|N|O|1997-09-03|1997-10-25|1997-09-15|NONE|TRUCK|ully silent instructions ca\n4964|40697|8210|5|42|68782.98|0.06|0.04|N|O|1997-09-04|1997-08-28|1997-10-02|TAKE BACK RETURN|AIR| hinder. idly even\n4964|192728|286|6|22|40055.84|0.04|0.08|N|O|1997-09-11|1997-10-06|1997-09-29|NONE|AIR|equests doubt quickly. caref\n4964|172770|2771|7|28|51597.56|0.00|0.05|N|O|1997-08-30|1997-09-15|1997-09-18|COLLECT COD|RAIL|among the carefully regula\n4965|130042|5069|1|28|30017.12|0.05|0.03|A|F|1994-01-02|1993-11-20|1994-01-04|TAKE BACK RETURN|REG AIR| deposits. requests sublate quickly \n4965|12001|7004|2|25|22825.00|0.10|0.02|R|F|1994-02-05|1993-12-15|1994-02-24|TAKE BACK RETURN|MAIL|wake at the carefully speci\n4965|100514|8045|3|27|40891.77|0.05|0.06|R|F|1993-11-06|1993-12-24|1993-11-30|TAKE BACK RETURN|SHIP|efully final foxes\n4965|137469|2496|4|33|49713.18|0.04|0.04|A|F|1993-12-31|1993-11-29|1994-01-27|DELIVER IN PERSON|REG AIR|iously slyly\n4966|75766|8274|1|10|17417.60|0.06|0.03|N|O|1996-09-23|1996-11-02|1996-10-07|TAKE BACK RETURN|SHIP| requests. carefully pending requests\n4966|193834|6354|2|6|11566.98|0.02|0.01|N|O|1996-12-09|1996-11-29|1996-12-30|NONE|AIR|d deposits are sly excuses. slyly iro\n4966|164992|4993|3|7|14398.93|0.00|0.01|N|O|1996-12-08|1996-10-09|1997-01-06|COLLECT COD|MAIL|ckly ironic tithe\n4966|15871|3375|4|26|46458.62|0.08|0.03|N|O|1996-11-14|1996-11-29|1996-12-05|COLLECT COD|REG AIR|nt pearls haggle carefully slyly even \n4966|143660|6175|5|12|20443.92|0.02|0.07|N|O|1996-12-07|1996-11-23|1996-12-20|DELIVER IN PERSON|RAIL|eodolites. ironic requests across the exp\n4967|70796|3304|1|50|88339.50|0.07|0.01|N|O|1997-05-27|1997-05-13|1997-06-12|NONE|REG AIR|kages. final, unusual accounts c\n4967|52398|9914|2|43|58066.77|0.00|0.07|N|O|1997-05-28|1997-04-10|1997-06-09|NONE|TRUCK|ons. slyly ironic requests\n4967|49975|9976|3|15|28874.55|0.08|0.02|N|O|1997-04-16|1997-04-12|1997-05-08|TAKE BACK RETURN|MAIL|y. blithel\n4967|122478|7503|4|1|1500.47|0.10|0.07|N|O|1997-06-04|1997-03-29|1997-06-23|NONE|FOB|osits. unusual frets thrash furiously\n4992|183528|3529|1|42|67683.84|0.07|0.01|R|F|1992-07-19|1992-06-16|1992-08-17|TAKE BACK RETURN|RAIL|foxes about the quickly final platele\n4992|146436|8951|2|47|69674.21|0.10|0.08|A|F|1992-09-04|1992-08-05|1992-09-21|COLLECT COD|MAIL|atterns use fluffily.\n4992|143147|8176|3|17|20232.38|0.03|0.03|A|F|1992-07-05|1992-07-19|1992-07-30|TAKE BACK RETURN|FOB|s along the perma\n4992|69920|4933|4|25|47248.00|0.04|0.06|R|F|1992-08-06|1992-07-11|1992-08-20|NONE|SHIP|ly about the never ironic requests. pe\n4992|138500|6040|5|23|35385.50|0.01|0.08|R|F|1992-06-28|1992-07-15|1992-07-12|DELIVER IN PERSON|MAIL|uickly regul\n4992|162601|150|6|44|73198.40|0.05|0.02|A|F|1992-06-01|1992-07-22|1992-06-03|NONE|RAIL|rmanent, sly packages print slyly. regula\n4993|37231|4741|1|34|39719.82|0.05|0.00|R|F|1994-09-21|1994-10-31|1994-09-24|TAKE BACK RETURN|REG AIR|ular, pending packages at the even packa\n4993|128552|1065|2|39|61641.45|0.03|0.08|R|F|1994-09-10|1994-09-04|1994-09-26|COLLECT COD|SHIP|pending, regular requests solve caref\n4993|165619|3168|3|42|70753.62|0.06|0.00|A|F|1994-08-27|1994-09-24|1994-09-05|NONE|MAIL| final packages at the q\n4993|157398|9914|4|31|45117.09|0.10|0.06|A|F|1994-10-02|1994-10-29|1994-10-15|NONE|AIR|nwind thinly platelets. a\n4994|155833|3379|1|36|67997.88|0.00|0.06|N|O|1996-09-29|1996-07-30|1996-10-03|TAKE BACK RETURN|TRUCK|ess ideas. blithely silent brai\n4994|79276|4291|2|47|58997.69|0.04|0.05|N|O|1996-09-20|1996-08-04|1996-10-15|COLLECT COD|TRUCK|sts. blithely close ideas sleep quic\n4994|182034|4553|3|29|32364.87|0.08|0.01|N|O|1996-08-26|1996-09-27|1996-09-25|DELIVER IN PERSON|RAIL|ptotes boost carefully\n4994|38154|8155|4|40|43686.00|0.01|0.06|N|O|1996-08-25|1996-08-16|1996-09-07|TAKE BACK RETURN|REG AIR|eposits. regula\n4994|41357|3862|5|24|31160.40|0.01|0.07|N|O|1996-08-19|1996-09-24|1996-08-25|TAKE BACK RETURN|FOB|s. slyly ironic deposits cajole f\n4994|72561|2562|6|6|9201.36|0.01|0.02|N|O|1996-09-05|1996-08-04|1996-09-30|TAKE BACK RETURN|FOB|grate carefully around th\n4994|129748|9749|7|31|55109.94|0.07|0.04|N|O|1996-10-14|1996-09-23|1996-11-08|TAKE BACK RETURN|RAIL|lar decoys cajole fluffil\n4995|64943|7450|1|16|30527.04|0.02|0.05|N|O|1996-02-27|1996-04-03|1996-02-29|DELIVER IN PERSON|MAIL|egular, bold packages. accou\n4995|80476|8001|2|43|62628.21|0.00|0.06|N|O|1996-02-24|1996-02-20|1996-03-07|NONE|AIR|ts. blithely silent ideas after t\n4995|155462|5463|3|22|33384.12|0.03|0.06|N|O|1996-03-17|1996-03-12|1996-04-01|DELIVER IN PERSON|MAIL|s wake furious, express dependencies.\n4995|39933|4940|4|9|16856.37|0.07|0.07|N|O|1996-03-07|1996-03-17|1996-03-11|DELIVER IN PERSON|FOB| ironic packages cajole across t\n4995|147648|5191|5|48|81390.72|0.08|0.07|N|O|1996-03-22|1996-04-01|1996-04-07|NONE|SHIP|t blithely. requests affix blithely. \n4995|109830|4851|6|48|88311.84|0.09|0.07|N|O|1996-04-14|1996-04-04|1996-05-07|DELIVER IN PERSON|RAIL|nstructions. carefully final depos\n4996|55891|902|1|35|64641.15|0.07|0.01|A|F|1992-10-30|1992-10-27|1992-11-05|TAKE BACK RETURN|SHIP|s. unusual, regular dolphins integrate care\n4996|155848|5849|2|39|74249.76|0.02|0.07|A|F|1992-09-19|1992-10-19|1992-10-06|COLLECT COD|FOB|equests are carefully final\n4996|127777|2802|3|12|21657.24|0.04|0.06|R|F|1993-01-09|1992-11-22|1993-02-04|DELIVER IN PERSON|SHIP|usly bold requests sleep dogge\n4996|143576|1119|4|13|21054.41|0.00|0.00|A|F|1992-09-17|1992-12-02|1992-10-07|DELIVER IN PERSON|TRUCK|o beans use about the furious\n4997|78040|5562|1|44|44793.76|0.02|0.05|N|O|1998-06-09|1998-06-12|1998-07-07|NONE|RAIL|r escapades ca\n4997|16188|3692|2|5|5520.90|0.02|0.04|N|O|1998-05-16|1998-06-05|1998-06-07|COLLECT COD|REG AIR|cuses are furiously unusual asymptotes\n4997|57193|7194|3|24|27604.56|0.04|0.06|N|O|1998-04-20|1998-04-23|1998-05-16|NONE|AIR|xpress, bo\n4997|39081|1585|4|5|5100.40|0.10|0.03|N|O|1998-06-12|1998-04-24|1998-06-13|DELIVER IN PERSON|TRUCK|aggle slyly alongside of the slyly i\n4997|21826|4329|5|46|80399.72|0.00|0.04|N|O|1998-04-28|1998-06-04|1998-05-08|TAKE BACK RETURN|SHIP|ecial courts are carefully\n4997|28912|6419|6|2|3681.82|0.07|0.01|N|O|1998-07-09|1998-06-10|1998-07-21|TAKE BACK RETURN|REG AIR|counts. slyl\n4998|153471|5987|1|12|18293.64|0.04|0.03|A|F|1992-02-20|1992-03-06|1992-03-01|TAKE BACK RETURN|RAIL| sleep slyly furiously final accounts. ins\n4998|182630|7667|2|15|25689.45|0.06|0.00|R|F|1992-04-24|1992-03-21|1992-05-02|NONE|REG AIR|heodolites sleep quickly.\n4998|58992|8993|3|27|52676.73|0.06|0.02|R|F|1992-03-17|1992-02-26|1992-04-05|DELIVER IN PERSON|MAIL|the blithely ironic \n4998|62376|7389|4|47|62903.39|0.10|0.04|A|F|1992-02-07|1992-03-07|1992-02-19|DELIVER IN PERSON|TRUCK|mong the careful\n4998|144589|2132|5|24|39205.92|0.01|0.04|R|F|1992-01-25|1992-03-16|1992-01-27|COLLECT COD|REG AIR| unwind about\n4998|98492|1002|6|8|11923.92|0.03|0.07|A|F|1992-05-01|1992-03-03|1992-05-24|TAKE BACK RETURN|AIR|ions nag quickly according to the theodolit\n4999|152687|7718|1|30|52190.40|0.00|0.02|A|F|1993-08-20|1993-08-15|1993-08-30|NONE|AIR|ades cajole carefully unusual ide\n4999|9204|9205|2|44|48980.80|0.03|0.01|A|F|1993-08-01|1993-08-04|1993-08-17|COLLECT COD|REG AIR|ependencies. slowly regu\n4999|85996|5997|3|30|59459.70|0.09|0.01|R|F|1993-07-21|1993-08-11|1993-08-20|DELIVER IN PERSON|RAIL|s cajole among the blithel\n5024|165411|444|1|17|25098.97|0.10|0.02|N|O|1996-11-24|1997-01-10|1996-12-04|NONE|AIR| to the expre\n5024|57578|84|2|41|62958.37|0.06|0.01|N|O|1996-11-09|1996-12-03|1996-12-01|COLLECT COD|REG AIR|osits hinder carefully \n5024|111009|3521|3|18|18360.00|0.04|0.03|N|O|1996-12-02|1997-01-16|1996-12-05|NONE|MAIL|zle carefully sauternes. quickly\n5024|122508|5021|4|42|64281.00|0.03|0.06|N|O|1996-12-02|1996-12-08|1996-12-04|DELIVER IN PERSON|RAIL|tegrate. busily spec\n5025|29421|4426|1|11|14854.62|0.00|0.04|N|O|1997-02-21|1997-04-16|1997-03-14|COLLECT COD|SHIP|the carefully final esc\n5025|77470|2485|2|10|14474.70|0.07|0.04|N|O|1997-06-04|1997-04-29|1997-06-28|COLLECT COD|RAIL|lly silent deposits boost busily again\n5026|95360|7870|1|13|17619.68|0.02|0.04|N|O|1997-12-23|1997-11-02|1998-01-03|TAKE BACK RETURN|SHIP|endencies sleep carefully alongs\n5027|97261|4789|1|6|7549.56|0.04|0.05|N|O|1997-09-28|1997-11-24|1997-10-25|NONE|FOB|ar, ironic deposi\n5027|61744|1745|2|39|66523.86|0.06|0.01|N|O|1997-09-09|1997-11-13|1997-09-21|TAKE BACK RETURN|FOB|ess requests! quickly regular pac\n5027|125140|165|3|32|37284.48|0.00|0.01|N|O|1997-11-13|1997-10-29|1997-11-18|TAKE BACK RETURN|RAIL|cording to\n5027|25594|5595|4|37|56224.83|0.02|0.00|N|O|1997-10-05|1997-10-30|1997-10-26|NONE|REG AIR|ost slyly fluffily\n5027|142732|2733|5|3|5324.19|0.03|0.06|N|O|1997-09-30|1997-11-26|1997-10-05|DELIVER IN PERSON|AIR|t the even mu\n5027|86493|9002|6|25|36987.25|0.06|0.00|N|O|1997-09-16|1997-11-25|1997-10-08|TAKE BACK RETURN|RAIL|ic ideas. requests sleep fluffily am\n5027|80444|2953|7|50|71222.00|0.07|0.02|N|O|1997-09-18|1997-11-07|1997-10-05|DELIVER IN PERSON|MAIL| beans dazzle according to the fluffi\n5028|13876|8879|1|15|26848.05|0.07|0.07|R|F|1992-07-17|1992-07-16|1992-08-05|COLLECT COD|REG AIR|es are quickly final pains. furiously pend\n5028|198686|8687|2|15|26770.20|0.03|0.07|R|F|1992-08-02|1992-07-09|1992-08-30|NONE|REG AIR|gular, bold pinto bea\n5029|153597|3598|1|17|28060.03|0.02|0.01|A|F|1993-03-12|1992-12-18|1993-04-02|DELIVER IN PERSON|FOB|! packages boost blithely. furious\n5029|96432|8942|2|2|2856.86|0.00|0.04|A|F|1992-11-25|1993-01-04|1992-12-20|DELIVER IN PERSON|MAIL|packages. furiously ironi\n5030|101356|1357|1|22|29861.70|0.04|0.06|N|O|1998-09-01|1998-08-15|1998-09-30|TAKE BACK RETURN|TRUCK|. quickly regular foxes believe\n5030|79762|4777|2|50|87088.00|0.05|0.06|N|O|1998-08-22|1998-07-25|1998-09-18|TAKE BACK RETURN|FOB|ss excuses serve bli\n5031|49942|9943|1|15|28379.10|0.02|0.05|R|F|1995-04-01|1995-02-24|1995-04-12|DELIVER IN PERSON|AIR|yly pending theodolites.\n5031|160895|8444|2|40|78235.60|0.10|0.04|A|F|1994-12-04|1995-01-27|1995-01-01|NONE|TRUCK|ns hang blithely across th\n5031|153067|613|3|4|4480.24|0.01|0.07|R|F|1994-12-26|1995-02-24|1995-01-11|NONE|RAIL|after the even frays: ironic, unusual th\n5031|180435|2954|4|31|46978.33|0.10|0.08|R|F|1995-01-15|1995-01-08|1995-02-09|COLLECT COD|MAIL|ts across the even requests doze furiously\n5056|47302|4815|1|7|8745.10|0.09|0.01|N|O|1997-04-28|1997-04-07|1997-05-15|DELIVER IN PERSON|TRUCK|rouches after the pending instruc\n5056|196388|3946|2|19|28203.22|0.04|0.00|N|O|1997-03-24|1997-05-05|1997-04-23|DELIVER IN PERSON|AIR|c theodolites. ironic a\n5056|89040|4057|3|23|23667.92|0.02|0.05|N|O|1997-05-12|1997-04-28|1997-05-25|NONE|SHIP|ickly regular requests cajole. depos\n5056|86915|1932|4|14|26626.74|0.08|0.00|N|O|1997-06-09|1997-04-13|1997-07-06|COLLECT COD|SHIP|sts haggle carefully along the slyl\n5057|36914|4424|1|38|70334.58|0.02|0.03|N|O|1997-10-24|1997-09-07|1997-10-30|TAKE BACK RETURN|MAIL|packages. stealthily bold wa\n5057|7083|9584|2|45|44553.60|0.08|0.07|N|O|1997-09-20|1997-10-02|1997-10-20|NONE|FOB| asymptotes wake slyl\n5058|192440|4960|1|16|24519.04|0.09|0.07|N|O|1998-07-12|1998-06-09|1998-07-15|DELIVER IN PERSON|SHIP| the special foxes \n5059|69538|7057|1|5|7537.65|0.03|0.08|R|F|1993-12-23|1994-01-12|1993-12-24|TAKE BACK RETURN|FOB|ts affix slyly accordi\n5059|122882|7907|2|19|36192.72|0.06|0.04|R|F|1994-03-02|1993-12-26|1994-03-14|TAKE BACK RETURN|MAIL| special ideas poach blithely qu\n5059|76305|8813|3|45|57658.50|0.02|0.00|A|F|1994-01-28|1994-01-08|1994-02-18|DELIVER IN PERSON|MAIL|enly. requests doze. express, close pa\n5060|24932|2439|1|27|50137.11|0.10|0.07|R|F|1992-07-23|1992-09-05|1992-08-07|COLLECT COD|SHIP|s. ironic \n5060|31033|3537|2|28|26992.84|0.04|0.04|R|F|1992-09-25|1992-08-11|1992-10-09|NONE|REG AIR|c requests\n5060|160257|258|3|15|19758.75|0.06|0.01|A|F|1992-08-28|1992-08-20|1992-09-01|DELIVER IN PERSON|AIR|ular deposits sl\n5061|164896|9929|1|18|35296.02|0.03|0.00|A|F|1993-10-20|1993-10-05|1993-10-28|TAKE BACK RETURN|SHIP|atelets among the ca\n5061|197491|2530|2|8|12707.92|0.01|0.02|R|F|1993-09-07|1993-10-31|1993-10-04|DELIVER IN PERSON|REG AIR|regular foxes. ir\n5061|23216|3217|3|26|29619.46|0.02|0.05|A|F|1993-11-07|1993-09-13|1993-11-13|NONE|REG AIR| cajole slyly. carefully spe\n5062|100784|3295|1|9|16063.02|0.08|0.00|R|F|1993-01-02|1992-12-01|1993-01-20|TAKE BACK RETURN|MAIL| silent theodolites wake. c\n5062|74681|4682|2|4|6622.72|0.02|0.02|R|F|1993-02-06|1992-12-14|1993-03-03|DELIVER IN PERSON|AIR|ke furiously express theodolites. \n5062|158504|8505|3|50|78125.00|0.09|0.07|A|F|1992-12-25|1992-12-13|1992-12-29|TAKE BACK RETURN|MAIL| the regular, unusual pains. specia\n5062|160148|2665|4|18|21746.52|0.03|0.07|R|F|1992-11-04|1992-12-25|1992-11-05|NONE|SHIP|furiously pending requests are ruthles\n5062|193632|1190|5|25|43140.75|0.08|0.02|R|F|1992-12-15|1992-11-17|1993-01-01|NONE|TRUCK|uthless excuses ag\n5063|128529|8530|1|31|48283.12|0.08|0.01|N|O|1997-06-02|1997-06-20|1997-06-27|NONE|RAIL|kages. ironic, ironic courts wake. carefu\n5063|173640|1192|2|43|73686.52|0.04|0.08|N|O|1997-09-14|1997-07-05|1997-10-05|TAKE BACK RETURN|TRUCK|latelets might nod blithely regular requ\n5063|166201|1234|3|2|2534.40|0.02|0.03|N|O|1997-06-17|1997-07-27|1997-06-24|COLLECT COD|SHIP|kly regular i\n5063|134525|9552|4|18|28071.36|0.08|0.05|N|O|1997-06-02|1997-06-18|1997-06-06|TAKE BACK RETURN|RAIL|refully quiet reques\n5063|160636|5669|5|1|1696.63|0.06|0.07|N|O|1997-09-03|1997-06-26|1997-10-03|NONE|FOB|ously special \n5088|77900|5422|1|23|43191.70|0.06|0.06|R|F|1993-03-03|1993-03-07|1993-03-08|NONE|FOB|cording to the fluffily expr\n5088|50009|7525|2|41|39319.00|0.09|0.00|R|F|1993-01-22|1993-03-07|1993-02-09|TAKE BACK RETURN|TRUCK|ing requests. \n5088|85245|2770|3|36|44288.64|0.10|0.05|A|F|1993-04-16|1993-04-03|1993-05-14|NONE|TRUCK|the furiously final deposits. furiously re\n5088|108479|6010|4|10|14874.70|0.04|0.05|R|F|1993-04-07|1993-02-06|1993-04-26|NONE|FOB|beans. special requests af\n5089|157064|9580|1|4|4484.24|0.05|0.06|R|F|1992-09-18|1992-09-28|1992-10-13|DELIVER IN PERSON|TRUCK|nts sleep blithely \n5089|161548|1549|2|20|32190.80|0.00|0.07|R|F|1992-10-10|1992-10-07|1992-11-06|COLLECT COD|RAIL| ironic accounts\n5089|123570|1107|3|46|73304.22|0.03|0.04|A|F|1992-11-09|1992-10-13|1992-11-10|TAKE BACK RETURN|RAIL|above the express accounts. exc\n5089|33436|5940|4|38|52038.34|0.05|0.03|R|F|1992-11-23|1992-09-11|1992-12-22|TAKE BACK RETURN|SHIP|regular instructions are\n5090|21309|1310|1|22|27066.60|0.07|0.00|N|O|1997-05-10|1997-05-25|1997-05-24|TAKE BACK RETURN|TRUCK|ets integrate ironic, regul\n5090|128542|8543|2|46|72244.84|0.05|0.00|N|O|1997-04-05|1997-04-14|1997-05-01|COLLECT COD|REG AIR|lose theodolites sleep blit\n5090|1804|9305|3|22|37527.60|0.09|0.05|N|O|1997-07-03|1997-04-12|1997-07-26|NONE|REG AIR|ular requests su\n5090|113646|6158|4|2|3319.28|0.03|0.06|N|O|1997-04-07|1997-04-23|1997-05-01|TAKE BACK RETURN|AIR|tes. slowly iro\n5090|47604|7605|5|21|32583.60|0.10|0.02|N|O|1997-03-29|1997-04-24|1997-04-25|TAKE BACK RETURN|FOB|ly express accounts. slyly even r\n5090|79662|4677|6|30|49249.80|0.02|0.03|N|O|1997-05-04|1997-04-14|1997-05-30|COLLECT COD|MAIL|osits nag slyly. fluffily ex\n5091|77312|4834|1|50|64465.50|0.05|0.03|N|O|1998-07-21|1998-06-22|1998-07-26|COLLECT COD|REG AIR|al dependencies. r\n5092|163090|8123|1|30|34592.70|0.06|0.00|N|O|1995-12-27|1995-12-08|1996-01-09|DELIVER IN PERSON|MAIL|ss, ironic deposits. furiously stea\n5092|44716|2229|2|34|56464.14|0.04|0.02|N|O|1995-12-09|1995-12-26|1995-12-21|TAKE BACK RETURN|AIR|ckages nag \n5092|139286|6826|3|13|17228.64|0.06|0.01|N|O|1995-11-21|1996-01-05|1995-12-19|TAKE BACK RETURN|SHIP|es detect sly\n5092|179246|9247|4|14|18553.36|0.04|0.00|N|O|1996-02-20|1995-11-30|1996-03-20|DELIVER IN PERSON|REG AIR| deposits cajole furiously against the sly\n5092|185660|8179|5|42|73317.72|0.01|0.02|N|O|1995-11-06|1996-01-01|1995-12-06|DELIVER IN PERSON|AIR|s use along t\n5092|177555|5107|6|11|17958.05|0.03|0.03|N|O|1995-12-02|1995-12-27|1995-12-11|COLLECT COD|MAIL|ly against the slyly silen\n5092|158325|8326|7|50|69166.00|0.10|0.03|N|O|1995-11-30|1996-01-14|1995-12-19|NONE|REG AIR|r platelets maintain car\n5093|167649|7650|1|40|68665.60|0.05|0.01|R|F|1993-09-16|1993-11-04|1993-10-05|TAKE BACK RETURN|REG AIR|ing pinto beans. quickly bold dependenci\n5093|73419|941|2|15|20886.15|0.01|0.04|A|F|1993-12-02|1993-11-18|1994-01-01|DELIVER IN PERSON|FOB|ly among the unusual foxe\n5093|150780|3296|3|31|56754.18|0.00|0.02|R|F|1993-09-22|1993-11-14|1993-09-26|TAKE BACK RETURN|REG AIR| against the\n5093|155669|700|4|37|63812.42|0.04|0.00|A|F|1993-10-26|1993-12-02|1993-10-27|NONE|TRUCK|courts. qui\n5093|114865|9888|5|30|56395.80|0.06|0.05|A|F|1993-11-22|1993-11-27|1993-12-14|DELIVER IN PERSON|TRUCK|ithely ironic sheaves use fluff\n5093|120197|2710|6|31|37732.89|0.01|0.08|A|F|1993-12-17|1993-11-14|1994-01-02|NONE|SHIP|he final foxes. fluffily ironic \n5094|142519|5034|1|19|29668.69|0.03|0.03|R|F|1993-03-31|1993-06-12|1993-04-04|NONE|AIR|ronic foxes. furi\n5094|107604|5135|2|23|37066.80|0.05|0.07|R|F|1993-06-13|1993-05-19|1993-07-06|NONE|MAIL|st furiously above the fluffily care\n5094|91455|8983|3|11|15910.95|0.04|0.08|A|F|1993-06-25|1993-06-24|1993-07-18|TAKE BACK RETURN|MAIL|s cajole quickly against the furiously ex\n5094|78510|8511|4|21|31258.71|0.09|0.08|R|F|1993-07-26|1993-05-03|1993-08-16|NONE|MAIL| blithely furiously final re\n5095|64242|1761|1|46|55487.04|0.07|0.01|A|F|1992-06-26|1992-06-25|1992-07-05|COLLECT COD|RAIL|egular instruction\n5095|105488|3019|2|2|2986.96|0.07|0.08|A|F|1992-07-09|1992-05-25|1992-07-21|DELIVER IN PERSON|REG AIR|detect car\n5095|122758|5271|3|28|49861.00|0.01|0.04|A|F|1992-06-20|1992-06-27|1992-06-22|DELIVER IN PERSON|AIR| into the final courts. ca\n5095|177984|3019|4|42|86603.16|0.08|0.08|R|F|1992-05-23|1992-06-01|1992-06-18|COLLECT COD|TRUCK|ccounts. packages could have t\n5095|165425|5426|5|9|13413.78|0.10|0.07|R|F|1992-08-14|1992-06-23|1992-08-16|TAKE BACK RETURN|REG AIR|bold theodolites wake about the expr\n5095|96768|6769|6|15|26471.40|0.01|0.06|A|F|1992-07-11|1992-07-12|1992-08-09|COLLECT COD|AIR| to the packages wake sly\n5095|168890|8891|7|40|78355.60|0.05|0.02|A|F|1992-07-11|1992-06-07|1992-07-26|DELIVER IN PERSON|MAIL|carefully unusual plat\n5120|132365|7392|1|28|39126.08|0.06|0.03|N|O|1996-07-20|1996-08-31|1996-08-06|NONE|RAIL| across the silent requests. caref\n5121|183562|1117|1|23|37847.88|0.06|0.01|A|F|1992-05-18|1992-06-20|1992-06-02|TAKE BACK RETURN|REG AIR|even courts are blithely ironically \n5121|110722|8256|2|45|77972.40|0.08|0.04|A|F|1992-08-13|1992-07-27|1992-09-12|NONE|TRUCK|pecial accounts cajole ca\n5121|96595|1614|3|27|42972.93|0.08|0.07|R|F|1992-06-17|1992-06-11|1992-06-19|NONE|MAIL|ly silent theodolit\n5121|67014|9521|4|10|9810.10|0.04|0.05|R|F|1992-06-08|1992-07-10|1992-07-02|TAKE BACK RETURN|FOB|e quickly according \n5121|88202|8203|5|46|54749.20|0.03|0.02|R|F|1992-05-27|1992-07-19|1992-05-28|TAKE BACK RETURN|FOB|use express foxes. slyly \n5121|79|7580|6|2|1958.14|0.04|0.07|R|F|1992-08-10|1992-06-28|1992-08-11|NONE|FOB| final, regular account\n5122|182287|9842|1|28|38339.84|0.03|0.00|N|O|1996-04-20|1996-03-29|1996-04-29|DELIVER IN PERSON|FOB|g the busily ironic accounts boos\n5122|81957|9482|2|43|83374.85|0.09|0.03|N|O|1996-05-31|1996-04-12|1996-06-13|NONE|MAIL|ut the carefully special foxes. idle,\n5122|44058|4059|3|12|12024.60|0.07|0.03|N|O|1996-04-02|1996-04-27|1996-04-10|DELIVER IN PERSON|AIR|lar instructions \n5123|25745|5746|1|13|21719.62|0.08|0.07|N|O|1998-05-17|1998-03-23|1998-06-02|COLLECT COD|MAIL|regular pearls\n5124|54193|1709|1|43|49329.17|0.00|0.02|N|O|1997-07-10|1997-05-13|1997-07-31|COLLECT COD|AIR|onic package\n5124|5101|2602|2|41|41250.10|0.02|0.06|N|O|1997-07-05|1997-06-29|1997-07-23|DELIVER IN PERSON|RAIL|wake across the\n5124|124798|4799|3|44|80202.76|0.03|0.03|N|O|1997-07-13|1997-06-26|1997-08-01|DELIVER IN PERSON|RAIL|equests. carefully unusual d\n5124|69465|1972|4|36|51640.56|0.10|0.07|N|O|1997-04-20|1997-07-03|1997-05-04|TAKE BACK RETURN|AIR|r deposits ab\n5125|5424|7925|1|38|50517.96|0.09|0.05|N|O|1998-03-20|1998-04-14|1998-03-22|COLLECT COD|MAIL|ily even deposits w\n5125|159077|9078|2|5|5680.35|0.08|0.06|N|O|1998-04-07|1998-04-14|1998-04-29|COLLECT COD|RAIL| thinly even pack\n5126|23568|8573|1|33|49221.48|0.02|0.02|R|F|1993-02-04|1992-12-23|1993-02-14|NONE|RAIL|ipliers promise furiously whithout the \n5126|100832|5853|2|43|78811.69|0.09|0.04|R|F|1993-01-07|1992-12-19|1993-01-16|COLLECT COD|MAIL|e silently. ironic, unusual accounts\n5126|77521|29|3|23|34465.96|0.08|0.01|R|F|1993-01-02|1993-01-02|1993-01-05|COLLECT COD|TRUCK|egular, blithe packages.\n5127|18637|1139|1|33|51335.79|0.08|0.04|N|O|1997-03-25|1997-03-02|1997-04-04|NONE|SHIP| bold deposits use carefully a\n5127|31122|3626|2|20|21062.40|0.01|0.03|N|O|1997-05-11|1997-02-26|1997-05-12|TAKE BACK RETURN|SHIP|dolites about the final platelets w\n5152|104917|2448|1|9|17297.19|0.04|0.03|N|O|1997-04-11|1997-02-11|1997-04-18|COLLECT COD|AIR| cajole furiously alongside of the bo\n5152|133398|5912|2|50|71569.50|0.04|0.04|N|O|1997-03-10|1997-02-04|1997-03-15|COLLECT COD|FOB| the final deposits. slyly ironic warth\n5153|34465|6969|1|42|58777.32|0.03|0.01|N|O|1995-10-03|1995-11-09|1995-10-11|COLLECT COD|RAIL|re thinly. ironic\n5153|52804|320|2|14|24595.20|0.05|0.05|N|O|1995-11-29|1995-10-21|1995-12-08|TAKE BACK RETURN|TRUCK| slyly daring pinto beans lose blithely fi\n5153|67863|370|3|30|54925.80|0.09|0.01|N|O|1995-11-10|1995-11-14|1995-11-16|DELIVER IN PERSON|AIR|beans sleep bl\n5153|172162|7197|4|32|39493.12|0.10|0.08|N|O|1995-12-05|1995-09-25|1996-01-03|DELIVER IN PERSON|MAIL|egular deposits. ironi\n5153|111676|9210|5|36|60756.12|0.01|0.03|N|O|1995-12-15|1995-11-08|1995-12-30|COLLECT COD|TRUCK| ironic instru\n5153|135055|2595|6|42|45782.10|0.00|0.03|N|O|1995-10-19|1995-11-23|1995-11-06|TAKE BACK RETURN|RAIL|ickly even deposi\n5154|189775|2294|1|11|20512.47|0.02|0.05|N|O|1997-08-06|1997-06-30|1997-09-04|NONE|RAIL|luffily bold foxes. final\n5154|143948|3949|2|15|29879.10|0.07|0.08|N|O|1997-06-23|1997-07-11|1997-07-11|NONE|AIR|even packages. packages use\n5155|47932|7933|1|1|1879.93|0.00|0.00|A|F|1994-07-03|1994-08-11|1994-07-29|COLLECT COD|FOB|oze slyly after the silent, regular idea\n5155|187548|2585|2|5|8177.70|0.08|0.02|A|F|1994-06-30|1994-08-13|1994-07-15|TAKE BACK RETURN|AIR|ole blithely slyly ironic \n5155|105449|2980|3|28|40724.32|0.05|0.02|R|F|1994-07-01|1994-07-19|1994-07-18|COLLECT COD|REG AIR|s cajole. accounts wake. thinly quiet pla\n5155|78860|6382|4|39|71715.54|0.09|0.06|A|F|1994-08-25|1994-09-01|1994-09-18|COLLECT COD|TRUCK|l dolphins nag caref\n5156|116520|1543|1|21|32266.92|0.06|0.03|N|O|1997-01-01|1997-01-30|1997-01-11|TAKE BACK RETURN|TRUCK|ts detect against the furiously reg\n5156|147020|2049|2|36|38412.72|0.04|0.07|N|O|1997-02-12|1996-12-10|1997-03-13|TAKE BACK RETURN|REG AIR| slyly even orbi\n5157|54165|1681|1|35|39170.60|0.06|0.08|N|O|1997-07-28|1997-09-30|1997-08-15|TAKE BACK RETURN|REG AIR|to the furiously sil\n5157|137834|7835|2|18|33692.94|0.10|0.04|N|O|1997-09-06|1997-10-03|1997-09-19|COLLECT COD|MAIL|y bold deposits nag blithely. final reque\n5157|166387|6388|3|15|21800.70|0.09|0.00|N|O|1997-07-27|1997-08-30|1997-08-08|DELIVER IN PERSON|RAIL|cajole. spec\n5157|58188|694|4|25|28654.50|0.00|0.03|N|O|1997-08-24|1997-09-23|1997-08-28|COLLECT COD|REG AIR| packages detect. even requests against th\n5157|148947|6490|5|40|79837.60|0.09|0.06|N|O|1997-08-11|1997-08-28|1997-09-01|TAKE BACK RETURN|FOB|ial packages according to \n5157|149156|6699|6|26|31333.90|0.10|0.01|N|O|1997-07-28|1997-08-22|1997-08-22|NONE|FOB|nto beans cajole car\n5157|48745|6258|7|12|20324.88|0.10|0.08|N|O|1997-10-19|1997-08-07|1997-10-26|NONE|FOB|es. busily \n5158|44249|1762|1|43|51309.32|0.10|0.04|N|O|1997-04-10|1997-03-06|1997-04-15|DELIVER IN PERSON|AIR|nusual platelets. slyly even foxes cajole \n5158|84248|4249|2|18|22180.32|0.04|0.04|N|O|1997-04-30|1997-03-28|1997-05-12|COLLECT COD|REG AIR|hely regular pa\n5158|141878|4393|3|41|78714.67|0.05|0.05|N|O|1997-02-25|1997-03-19|1997-03-03|COLLECT COD|AIR|deposits. quickly special \n5158|130049|7589|4|49|52872.96|0.05|0.01|N|O|1997-04-10|1997-03-21|1997-04-30|NONE|REG AIR|r requests sleep q\n5158|118468|6002|5|20|29729.20|0.01|0.04|N|O|1997-02-03|1997-02-20|1997-02-08|TAKE BACK RETURN|AIR|latelets use accordin\n5158|87969|478|6|39|76321.44|0.08|0.04|N|O|1997-05-15|1997-04-04|1997-06-02|DELIVER IN PERSON|FOB|lithely fina\n5158|90030|7558|7|38|38761.14|0.10|0.05|N|O|1997-05-09|1997-03-03|1997-06-04|NONE|SHIP|uffily regular ac\n5159|123367|904|1|39|54224.04|0.06|0.07|N|O|1996-12-17|1996-12-08|1997-01-10|COLLECT COD|MAIL|re furiously after the pending dolphin\n5159|16681|9183|2|46|73493.28|0.01|0.01|N|O|1996-12-15|1996-12-07|1996-12-30|DELIVER IN PERSON|SHIP|s kindle slyly carefully regular\n5159|151238|8784|3|22|28363.06|0.01|0.02|N|O|1996-11-06|1996-11-04|1996-11-15|TAKE BACK RETURN|SHIP|he furiously sile\n5159|51907|1908|4|5|9294.50|0.10|0.00|N|O|1996-11-25|1996-12-19|1996-12-25|TAKE BACK RETURN|FOB|nal deposits. pending, ironic ideas grow\n5159|197897|417|5|36|71816.04|0.06|0.01|N|O|1997-01-24|1996-11-07|1997-02-08|NONE|REG AIR|packages wake.\n5184|152521|7552|1|33|51926.16|0.07|0.04|N|O|1998-08-17|1998-10-16|1998-08-24|TAKE BACK RETURN|AIR|posits. carefully express asympto\n5184|15749|3253|2|47|78242.78|0.05|0.01|N|O|1998-11-02|1998-08-19|1998-11-07|COLLECT COD|TRUCK|se. carefully express pinto beans x\n5184|87427|9936|3|39|55162.38|0.03|0.06|N|O|1998-10-27|1998-10-17|1998-11-19|DELIVER IN PERSON|FOB|es above the care\n5184|175958|5959|4|26|52882.70|0.05|0.08|N|O|1998-11-11|1998-08-26|1998-12-01|TAKE BACK RETURN|TRUCK| packages are\n5184|123097|5610|5|19|21281.71|0.06|0.03|N|O|1998-11-15|1998-10-12|1998-11-21|COLLECT COD|REG AIR|refully express platelets sleep carefull\n5184|79525|4540|6|49|73721.48|0.02|0.00|N|O|1998-09-18|1998-08-28|1998-10-14|COLLECT COD|FOB|thlessly closely even reque\n5185|196408|3966|1|37|55662.80|0.00|0.04|N|O|1997-08-08|1997-09-08|1997-08-14|COLLECT COD|SHIP|gainst the courts dazzle care\n5185|24597|2104|2|32|48690.88|0.06|0.00|N|O|1997-08-17|1997-09-30|1997-08-24|TAKE BACK RETURN|REG AIR|ackages. slyly even requests\n5185|195889|928|3|41|81380.08|0.00|0.05|N|O|1997-10-15|1997-10-11|1997-11-02|COLLECT COD|REG AIR|ly blithe deposits. furi\n5185|95730|5731|4|30|51771.90|0.09|0.04|N|O|1997-10-17|1997-09-16|1997-10-23|TAKE BACK RETURN|SHIP|ress packages are furiously\n5185|127706|7707|5|8|13869.60|0.04|0.00|N|O|1997-08-30|1997-09-02|1997-09-22|COLLECT COD|REG AIR|sts around the slyly perma\n5185|145806|835|6|50|92590.00|0.03|0.04|N|O|1997-10-15|1997-10-19|1997-11-06|TAKE BACK RETURN|FOB|final platelets. ideas sleep careful\n5186|54412|9423|1|38|51923.58|0.06|0.02|N|O|1996-11-23|1996-09-21|1996-12-11|DELIVER IN PERSON|MAIL|y ruthless foxes. fluffily \n5186|90920|921|2|31|59238.52|0.09|0.03|N|O|1996-10-19|1996-09-26|1996-10-25|TAKE BACK RETURN|REG AIR| accounts use furiously slyly spe\n5186|88279|3296|3|26|32949.02|0.03|0.02|N|O|1996-08-08|1996-10-05|1996-08-21|DELIVER IN PERSON|FOB|capades. accounts sublate. pinto\n5186|89226|9227|4|8|9721.76|0.10|0.05|N|O|1996-09-23|1996-09-29|1996-09-30|COLLECT COD|RAIL|y regular notornis k\n5186|17403|9905|5|28|36971.20|0.09|0.03|N|O|1996-10-05|1996-10-27|1996-10-19|TAKE BACK RETURN|RAIL|al decoys. blit\n5186|81990|4499|6|35|69019.65|0.00|0.05|N|O|1996-10-20|1996-10-12|1996-11-12|TAKE BACK RETURN|RAIL|sly silent pack\n5186|197847|367|7|44|85572.96|0.00|0.08|N|O|1996-09-23|1996-10-14|1996-10-01|NONE|TRUCK|old, final accounts cajole sl\n5187|10977|8481|1|49|92510.53|0.04|0.06|N|O|1997-10-20|1997-10-12|1997-10-26|DELIVER IN PERSON|AIR|l, regular platelets instead of the foxes w\n5187|82848|2849|2|1|1830.84|0.10|0.08|N|O|1997-08-08|1997-08-24|1997-08-22|DELIVER IN PERSON|REG AIR|aggle never bold \n5188|117534|46|1|18|27927.54|0.04|0.03|N|O|1995-06-19|1995-05-19|1995-06-25|DELIVER IN PERSON|AIR|p according to the sometimes regu\n5188|193702|1260|2|36|64645.20|0.04|0.02|A|F|1995-03-09|1995-05-16|1995-03-19|NONE|TRUCK|packages? blithely s\n5188|147932|2961|3|9|17819.37|0.06|0.08|A|F|1995-05-09|1995-05-22|1995-05-19|TAKE BACK RETURN|REG AIR|r attainments are across the \n5189|137325|2352|1|44|59942.08|0.02|0.06|A|F|1994-01-13|1994-02-07|1994-01-21|DELIVER IN PERSON|MAIL|y finally pendin\n5189|15881|884|2|38|68281.44|0.06|0.00|A|F|1994-03-26|1994-01-28|1994-04-20|DELIVER IN PERSON|REG AIR|ideas. idle, final deposits de\n5189|109889|4910|3|4|7595.52|0.09|0.02|A|F|1993-12-21|1994-02-23|1994-01-09|DELIVER IN PERSON|REG AIR|. blithely exp\n5189|93195|8214|4|49|58221.31|0.05|0.01|R|F|1994-01-22|1994-01-19|1994-02-04|TAKE BACK RETURN|SHIP| requests \n5189|122046|7071|5|14|14952.56|0.02|0.03|A|F|1994-01-23|1994-01-05|1994-02-12|DELIVER IN PERSON|REG AIR|unusual packag\n5189|16138|6139|6|41|43219.33|0.02|0.06|R|F|1993-12-12|1994-02-05|1994-01-09|DELIVER IN PERSON|RAIL|ial theodolites cajole slyly. slyly unus\n5190|55509|520|1|43|62973.50|0.09|0.06|A|F|1992-08-19|1992-06-10|1992-09-01|DELIVER IN PERSON|FOB|encies use fluffily unusual requests? hoc\n5190|131308|6335|2|6|8035.80|0.10|0.08|A|F|1992-08-08|1992-07-14|1992-08-22|COLLECT COD|RAIL|furiously regular pinto beans. furiously i\n5190|88403|3420|3|45|62613.00|0.04|0.03|A|F|1992-07-23|1992-06-16|1992-08-04|NONE|FOB|y carefully final ideas. f\n5191|114713|4714|1|41|70836.11|0.00|0.08|A|F|1995-02-05|1995-02-27|1995-02-15|DELIVER IN PERSON|AIR|uests! ironic theodolites cajole care\n5191|167719|236|2|40|71468.40|0.02|0.01|A|F|1995-03-31|1995-02-21|1995-04-02|NONE|AIR|nes haggle sometimes. requests eng\n5191|42244|2245|3|27|32028.48|0.07|0.05|A|F|1994-12-26|1995-01-24|1995-01-14|DELIVER IN PERSON|RAIL|tructions nag bravely within the re\n5191|182828|383|4|7|13375.74|0.01|0.04|A|F|1995-03-24|1995-01-30|1995-03-30|NONE|RAIL|eposits. express\n5216|68007|8008|1|17|16575.00|0.04|0.06|N|O|1997-08-20|1997-11-07|1997-09-14|COLLECT COD|FOB|s according to the accounts bo\n5217|79528|9529|1|50|75376.00|0.05|0.02|N|O|1995-12-26|1995-11-21|1996-01-24|DELIVER IN PERSON|MAIL|s. express, express accounts c\n5217|15345|5346|2|23|28987.82|0.06|0.07|N|O|1996-01-18|1995-12-24|1996-02-10|COLLECT COD|RAIL|ven ideas. requests amo\n5217|101296|6317|3|23|29837.67|0.03|0.02|N|O|1995-11-15|1995-12-17|1995-11-27|DELIVER IN PERSON|FOB|pending packages cajole ne\n5217|80311|2820|4|47|60691.57|0.04|0.00|N|O|1995-11-24|1995-12-25|1995-11-25|COLLECT COD|AIR|ronic packages i\n5218|82747|272|1|43|74378.82|0.05|0.04|A|F|1992-08-04|1992-09-12|1992-08-17|DELIVER IN PERSON|SHIP|k theodolites. express, even id\n5218|124729|9754|2|33|57872.76|0.06|0.01|R|F|1992-09-16|1992-09-30|1992-09-27|NONE|TRUCK|ronic instructi\n5219|134892|4893|1|2|3853.78|0.08|0.00|N|O|1997-06-26|1997-04-29|1997-07-08|TAKE BACK RETURN|FOB| blithely according to the stea\n5219|118635|6169|2|20|33072.60|0.05|0.00|N|O|1997-04-20|1997-05-26|1997-05-13|COLLECT COD|FOB|e along the ironic,\n5220|82721|7738|1|27|46000.44|0.10|0.04|R|F|1992-09-21|1992-08-29|1992-10-16|DELIVER IN PERSON|RAIL|s cajole blithely furiously iron\n5221|103446|8467|1|24|34786.56|0.07|0.03|N|O|1995-10-04|1995-08-11|1995-10-30|COLLECT COD|REG AIR|s pinto beans sleep. sly\n5221|8710|8711|2|34|55036.14|0.01|0.05|N|O|1995-09-11|1995-07-17|1995-10-10|COLLECT COD|SHIP|eans. furio\n5221|179197|1715|3|16|20419.04|0.04|0.01|N|O|1995-08-29|1995-09-06|1995-09-12|TAKE BACK RETURN|TRUCK|ending request\n5222|150471|8017|1|1|1521.47|0.00|0.00|A|F|1994-08-19|1994-07-16|1994-09-08|TAKE BACK RETURN|FOB|idle requests. carefully pending pinto bean\n5223|44573|2086|1|24|36421.68|0.00|0.00|A|F|1994-10-03|1994-09-20|1994-10-11|TAKE BACK RETURN|TRUCK|refully bold courts besides the regular,\n5223|123366|5879|2|25|34734.00|0.09|0.02|R|F|1994-07-12|1994-08-13|1994-08-01|NONE|FOB|y express ideas impress\n5223|5732|3233|3|19|31116.87|0.04|0.01|R|F|1994-10-28|1994-08-26|1994-10-31|COLLECT COD|REG AIR|ntly. furiously even excuses a\n5223|129873|4898|4|40|76114.80|0.01|0.04|R|F|1994-10-01|1994-09-18|1994-10-28|COLLECT COD|SHIP|kly pending \n5248|80249|7774|1|39|47940.36|0.05|0.03|N|O|1995-08-10|1995-07-04|1995-09-09|TAKE BACK RETURN|MAIL|yly even accounts. spe\n5248|137103|2130|2|45|51304.50|0.00|0.06|A|F|1995-05-09|1995-07-12|1995-05-27|DELIVER IN PERSON|FOB|. bold, pending foxes h\n5249|49720|7233|1|31|51761.32|0.07|0.03|A|F|1994-11-21|1994-11-19|1994-12-08|NONE|REG AIR|f the excuses. furiously fin\n5249|30026|2530|2|44|42064.88|0.05|0.00|A|F|1994-12-28|1994-11-29|1994-12-29|TAKE BACK RETURN|MAIL|ole furiousl\n5249|31770|4274|3|13|22123.01|0.09|0.00|R|F|1994-09-27|1994-10-20|1994-10-05|DELIVER IN PERSON|SHIP|ites. finally exp\n5249|145318|7833|4|29|39535.99|0.00|0.05|A|F|1994-09-16|1994-11-03|1994-10-06|NONE|TRUCK| players. f\n5249|157742|258|5|12|21596.88|0.01|0.08|R|F|1994-12-28|1994-11-07|1995-01-15|COLLECT COD|MAIL|press depths could have to sleep carefu\n5250|43529|1042|1|2|2945.04|0.08|0.04|N|O|1995-08-09|1995-10-10|1995-08-13|COLLECT COD|AIR|its. final pinto\n5250|191534|9092|2|27|43889.31|0.10|0.05|N|O|1995-10-24|1995-09-03|1995-11-18|COLLECT COD|TRUCK|l forges are. furiously unusual pin\n5251|138965|3992|1|36|72142.56|0.10|0.01|N|O|1995-07-16|1995-07-05|1995-07-28|DELIVER IN PERSON|FOB|slowly! bli\n5252|140986|8529|1|13|26350.74|0.02|0.01|N|O|1996-03-02|1996-05-10|1996-03-11|NONE|FOB|boost fluffily across \n5252|138978|1492|2|39|78661.83|0.06|0.05|N|O|1996-05-17|1996-04-23|1996-05-23|COLLECT COD|AIR|gular requests.\n5252|194020|1578|3|9|10026.18|0.09|0.03|N|O|1996-05-30|1996-05-03|1996-06-26|TAKE BACK RETURN|RAIL|x. slyly special depos\n5252|86576|1593|4|48|75003.36|0.01|0.06|N|O|1996-04-17|1996-03-19|1996-05-03|COLLECT COD|AIR|bold requests. furious\n5252|67746|2759|5|24|41129.76|0.04|0.05|N|O|1996-05-11|1996-04-17|1996-05-12|COLLECT COD|REG AIR|posits after the fluffi\n5252|2202|9703|6|41|45272.20|0.02|0.03|N|O|1996-03-16|1996-04-18|1996-03-17|NONE|TRUCK|ording to the blithely express somas sho\n5253|30616|5623|1|35|54131.35|0.02|0.00|N|O|1995-07-23|1995-06-12|1995-08-03|DELIVER IN PERSON|AIR|ven deposits. careful\n5253|149475|1990|2|38|57929.86|0.02|0.06|N|O|1995-08-03|1995-06-14|1995-08-27|DELIVER IN PERSON|REG AIR|onic dependencies are furiou\n5253|13981|3982|3|9|17054.82|0.03|0.08|N|F|1995-06-08|1995-05-12|1995-06-23|DELIVER IN PERSON|REG AIR|lyly express deposits use furiou\n5253|165076|2625|4|25|28526.75|0.04|0.03|A|F|1995-05-21|1995-06-13|1995-06-09|COLLECT COD|TRUCK|urts. even theodoli\n5254|110141|142|1|35|40289.90|0.01|0.07|A|F|1992-07-28|1992-09-05|1992-08-07|COLLECT COD|REG AIR|ntegrate carefully among the pending\n5254|134265|9292|2|10|12992.60|0.05|0.04|A|F|1992-11-19|1992-10-20|1992-12-15|COLLECT COD|SHIP| accounts. silent deposit\n5254|191752|6791|3|32|59000.00|0.00|0.08|A|F|1992-08-10|1992-09-21|1992-08-16|NONE|RAIL|ts impress closely furi\n5254|162208|4725|4|45|57159.00|0.05|0.06|A|F|1992-11-11|1992-09-01|1992-12-07|COLLECT COD|REG AIR| wake. blithely silent excuse\n5254|28653|3658|5|23|36377.95|0.02|0.06|A|F|1992-08-16|1992-09-05|1992-09-15|COLLECT COD|RAIL|lyly regular accounts. furiously pendin\n5254|157626|2657|6|34|57243.08|0.09|0.02|R|F|1992-08-29|1992-10-16|1992-09-20|TAKE BACK RETURN|RAIL| furiously above the furiously \n5254|19583|4586|7|9|13523.22|0.09|0.03|R|F|1992-07-29|1992-10-15|1992-08-20|TAKE BACK RETURN|REG AIR| wake blithely fluff\n5255|130706|8246|1|2|3473.40|0.04|0.08|N|O|1996-09-27|1996-10-04|1996-10-04|DELIVER IN PERSON|RAIL|ajole blithely fluf\n5255|171139|8691|2|30|36303.90|0.04|0.08|N|O|1996-09-20|1996-08-18|1996-10-09|TAKE BACK RETURN|AIR| to the silent requests cajole b\n5255|129900|7437|3|41|79125.90|0.09|0.03|N|O|1996-08-21|1996-09-24|1996-09-05|COLLECT COD|FOB|tect blithely against t\n5280|96078|8588|1|16|17185.12|0.02|0.03|N|O|1998-03-29|1998-01-28|1998-04-03|TAKE BACK RETURN|SHIP| foxes are furiously. theodoli\n5280|175550|585|2|46|74775.30|0.01|0.06|N|O|1998-01-04|1998-01-21|1998-02-03|TAKE BACK RETURN|FOB|efully carefully pen\n5281|113555|8578|1|37|58036.35|0.05|0.02|N|O|1995-11-10|1996-01-31|1995-11-22|DELIVER IN PERSON|MAIL|ronic dependencies. fluffily final p\n5281|104572|2103|2|38|59909.66|0.00|0.05|N|O|1996-02-17|1995-12-19|1996-02-29|NONE|RAIL|n asymptotes could wake about th\n5281|126120|8633|3|23|26360.76|0.08|0.00|N|O|1995-12-30|1996-01-26|1996-01-23|COLLECT COD|REG AIR|. final theodolites cajole. ironic p\n5281|86431|1448|4|48|68036.64|0.03|0.05|N|O|1996-01-31|1995-12-23|1996-02-08|TAKE BACK RETURN|REG AIR|ss the furiously \n5281|42872|5377|5|33|59890.71|0.01|0.07|N|O|1996-03-01|1995-12-28|1996-03-05|COLLECT COD|RAIL|ly brave foxes. bold deposits above the \n5282|117371|9883|1|36|49981.32|0.05|0.02|N|O|1998-05-20|1998-04-10|1998-06-14|DELIVER IN PERSON|TRUCK|re slyly accor\n5282|51792|4298|2|32|55801.28|0.02|0.05|N|O|1998-03-01|1998-03-31|1998-03-03|NONE|FOB|onic deposits; furiou\n5282|57236|4752|3|28|33410.44|0.03|0.06|N|O|1998-05-06|1998-04-24|1998-05-30|COLLECT COD|SHIP|fily final instruc\n5283|4165|1666|1|20|21383.20|0.05|0.02|A|F|1994-09-16|1994-08-03|1994-10-15|TAKE BACK RETURN|TRUCK|al deposits? blithely even pinto beans\n5283|185925|8444|2|1|2010.92|0.10|0.08|R|F|1994-06-20|1994-08-03|1994-07-01|COLLECT COD|FOB|deposits within the furio\n5284|172419|9971|1|16|23862.56|0.04|0.02|N|O|1995-08-17|1995-08-23|1995-08-26|DELIVER IN PERSON|TRUCK|unts detect furiously even d\n5284|43086|8095|2|24|24697.92|0.03|0.08|N|O|1995-10-21|1995-08-23|1995-10-27|COLLECT COD|AIR| haggle according \n5285|192958|5478|1|31|63579.45|0.08|0.00|A|F|1994-04-17|1994-04-05|1994-05-09|NONE|RAIL|ubt. quickly blithe \n5285|30061|5068|2|37|36669.22|0.09|0.02|R|F|1994-02-26|1994-02-18|1994-03-27|NONE|SHIP|uffily regu\n5285|33846|1356|3|24|42716.16|0.02|0.04|A|F|1994-04-19|1994-04-03|1994-04-25|DELIVER IN PERSON|FOB|ess packages. quick, even deposits snooze b\n5285|42130|9643|4|12|12865.56|0.05|0.06|A|F|1994-04-22|1994-04-07|1994-05-19|NONE|AIR| deposits-- quickly bold requests hag\n5285|70694|695|5|1|1664.69|0.03|0.05|R|F|1994-03-14|1994-02-20|1994-04-10|COLLECT COD|TRUCK|e fluffily about the slyly special pa\n5285|145672|5673|6|1|1717.67|0.06|0.01|R|F|1994-02-08|1994-04-02|1994-02-17|COLLECT COD|SHIP|ing deposits integra\n5286|198566|1086|1|1|1664.56|0.01|0.07|N|O|1997-11-25|1997-11-07|1997-12-17|COLLECT COD|REG AIR|ly! furiously final pack\n5286|96223|3751|2|7|8534.54|0.06|0.05|N|O|1997-10-23|1997-12-10|1997-11-20|TAKE BACK RETURN|RAIL|y express instructions sleep carefull\n5286|15473|7975|3|3|4165.41|0.06|0.08|N|O|1997-12-04|1997-11-06|1997-12-09|COLLECT COD|MAIL|re fluffily\n5286|39019|1523|4|6|5748.06|0.04|0.03|N|O|1997-10-15|1997-12-05|1997-11-12|COLLECT COD|RAIL|y special a\n5286|185301|5302|5|38|52679.40|0.07|0.05|N|O|1997-11-29|1997-11-26|1997-12-15|TAKE BACK RETURN|SHIP|fluffily. special, ironic deposit\n5286|137943|7944|6|24|47542.56|0.08|0.00|N|O|1997-09-27|1997-12-21|1997-09-30|COLLECT COD|TRUCK|s. express foxes of the\n5287|38269|3276|1|32|38632.32|0.01|0.01|A|F|1994-01-29|1994-01-27|1994-02-08|NONE|RAIL|heodolites haggle caref\n5312|60844|8363|1|27|48730.68|0.04|0.08|A|F|1995-04-20|1995-04-09|1995-04-25|COLLECT COD|SHIP|tructions cajol\n5312|1004|3505|2|43|38915.00|0.05|0.08|A|F|1995-03-24|1995-05-07|1995-03-28|NONE|TRUCK|ly unusual\n5313|16090|8592|1|34|34207.06|0.10|0.02|N|O|1997-08-07|1997-08-12|1997-08-24|DELIVER IN PERSON|FOB|ccording to the blithely final account\n5313|12109|7112|2|17|17358.70|0.00|0.02|N|O|1997-09-02|1997-08-20|1997-09-07|NONE|SHIP|uests wake\n5313|111704|6727|3|47|80637.90|0.06|0.08|N|O|1997-08-12|1997-08-18|1997-08-13|TAKE BACK RETURN|RAIL|pinto beans across the \n5313|196554|4112|4|16|26408.80|0.08|0.00|N|O|1997-10-04|1997-08-02|1997-10-25|COLLECT COD|REG AIR|ckages wake carefully aga\n5313|71942|6957|5|30|57418.20|0.06|0.08|N|O|1997-06-27|1997-07-18|1997-06-30|NONE|SHIP|nding packages use\n5313|119930|4953|6|21|40948.53|0.05|0.05|N|O|1997-09-26|1997-09-02|1997-10-18|COLLECT COD|FOB|he blithely regular packages. quickly\n5314|117209|7210|1|10|12262.00|0.07|0.05|N|O|1995-09-26|1995-07-24|1995-10-19|DELIVER IN PERSON|RAIL|latelets haggle final\n5314|124781|4782|2|16|28892.48|0.00|0.04|N|O|1995-09-25|1995-07-08|1995-10-17|COLLECT COD|SHIP|hely unusual packages acc\n5315|34544|2054|1|12|17742.48|0.08|0.06|R|F|1992-12-18|1993-01-16|1993-01-10|NONE|AIR|ccounts. furiously ironi\n5315|178618|8619|2|39|66167.79|0.00|0.06|R|F|1992-11-09|1992-12-29|1992-12-07|NONE|SHIP|ly alongside of the ca\n5316|107714|225|1|29|49929.59|0.10|0.05|R|F|1994-03-28|1994-04-29|1994-04-09|DELIVER IN PERSON|REG AIR|ckly unusual foxes bo\n5316|135125|5126|2|31|35963.72|0.00|0.08|A|F|1994-04-01|1994-04-21|1994-04-12|DELIVER IN PERSON|MAIL|s. deposits cajole around t\n5317|81001|8526|1|29|28478.00|0.02|0.06|A|F|1994-11-28|1994-11-27|1994-12-16|COLLECT COD|FOB|oss the carefull\n5317|170294|295|2|18|24557.22|0.06|0.06|A|F|1995-01-02|1994-10-29|1995-01-16|NONE|RAIL|g to the blithely p\n5317|119181|1693|3|37|44406.66|0.09|0.00|R|F|1994-09-15|1994-10-24|1994-09-23|NONE|TRUCK|totes nag theodolites. pend\n5317|66431|8938|4|50|69871.50|0.09|0.01|A|F|1994-10-17|1994-10-25|1994-11-03|NONE|REG AIR|cajole furiously. accounts use quick\n5317|94765|9784|5|19|33435.44|0.07|0.07|R|F|1994-12-15|1994-10-18|1994-12-27|NONE|MAIL|onic requests boost bli\n5317|114725|7237|6|48|83506.56|0.01|0.03|A|F|1994-09-19|1994-11-25|1994-10-03|COLLECT COD|MAIL|ts about the packages cajole furio\n5317|168493|6042|7|30|46844.70|0.07|0.07|A|F|1994-10-13|1994-10-31|1994-10-28|NONE|AIR|cross the attainments. slyly \n5318|60046|7565|1|13|13078.52|0.10|0.04|R|F|1993-07-15|1993-06-25|1993-08-13|COLLECT COD|REG AIR|ly silent ideas. ideas haggle among the \n5318|179521|9522|2|26|41613.52|0.00|0.04|R|F|1993-07-07|1993-05-23|1993-07-28|COLLECT COD|SHIP|al, express foxes. bold requests sleep alwa\n5318|6173|8674|3|37|39929.29|0.07|0.05|A|F|1993-07-09|1993-06-22|1993-07-21|COLLECT COD|SHIP|ickly final deposi\n5318|141228|6257|4|31|39345.82|0.01|0.04|R|F|1993-07-28|1993-05-06|1993-08-06|DELIVER IN PERSON|REG AIR|requests must sleep slyly quickly\n5319|149703|7246|1|31|54333.70|0.04|0.07|N|O|1996-03-26|1996-03-07|1996-04-24|COLLECT COD|TRUCK|d carefully about the courts. fluffily spe\n5319|43282|795|2|39|47785.92|0.09|0.05|N|O|1996-05-17|1996-03-14|1996-06-11|NONE|TRUCK|unts. furiously silent\n5344|18176|678|1|6|6565.02|0.07|0.01|N|O|1998-08-04|1998-09-03|1998-08-11|TAKE BACK RETURN|REG AIR|ithely about the pending plate\n5344|78150|658|2|37|41741.55|0.03|0.07|N|O|1998-10-09|1998-07-26|1998-11-08|NONE|TRUCK|thely express packages\n5344|66249|6250|3|26|31596.24|0.02|0.06|N|O|1998-08-27|1998-08-22|1998-09-24|NONE|AIR|furiously pending, silent multipliers.\n5344|38164|3171|4|21|23145.36|0.03|0.01|N|O|1998-08-31|1998-09-06|1998-09-02|NONE|MAIL|xes. furiously even pinto beans sleep f\n5345|82522|5031|1|3|4513.56|0.05|0.01|N|O|1997-12-10|1997-10-03|1998-01-05|COLLECT COD|SHIP|ites wake carefully unusual \n5345|145618|3161|2|2|3327.22|0.10|0.02|N|O|1997-11-18|1997-10-12|1997-12-08|NONE|MAIL|ut the slyly specia\n5345|191094|6133|3|46|54514.14|0.06|0.04|N|O|1997-10-06|1997-09-27|1997-10-18|COLLECT COD|REG AIR|slyly special deposits. fin\n5345|113634|1168|4|37|60962.31|0.01|0.01|N|O|1997-11-01|1997-10-09|1997-11-26|DELIVER IN PERSON|AIR| along the ironically fina\n5345|33600|6104|5|22|33739.20|0.02|0.02|N|O|1997-08-27|1997-11-22|1997-09-10|TAKE BACK RETURN|MAIL|leep slyly regular fox\n5346|148105|5648|1|21|24215.10|0.07|0.08|R|F|1994-03-11|1994-03-07|1994-04-04|DELIVER IN PERSON|RAIL|integrate blithely a\n5346|191665|6704|2|13|22836.58|0.04|0.04|A|F|1994-02-03|1994-02-05|1994-02-09|COLLECT COD|TRUCK|y. fluffily bold accounts grow. furio\n5346|108881|1392|3|7|13229.16|0.08|0.05|A|F|1994-01-30|1994-03-26|1994-01-31|DELIVER IN PERSON|SHIP|equests use carefully care\n5346|161238|1239|4|35|45473.05|0.06|0.02|A|F|1994-02-09|1994-03-01|1994-02-14|TAKE BACK RETURN|FOB|nic excuses cajole entic\n5346|120595|596|5|25|40389.75|0.05|0.06|R|F|1993-12-28|1994-03-19|1994-01-09|TAKE BACK RETURN|REG AIR|he ironic ideas are boldly slyly ironi\n5346|32956|466|6|6|11333.70|0.08|0.04|R|F|1994-03-01|1994-02-04|1994-03-09|NONE|REG AIR|escapades sleep furiously beside the \n5346|79234|4249|7|41|49742.43|0.05|0.04|R|F|1994-01-10|1994-02-15|1994-01-26|TAKE BACK RETURN|REG AIR|fully close instructi\n5347|82283|7300|1|48|60733.44|0.04|0.08|A|F|1995-02-25|1995-04-26|1995-03-26|NONE|SHIP|equests are slyly. blithely regu\n5347|123439|8464|2|47|68734.21|0.02|0.01|N|F|1995-06-05|1995-03-29|1995-06-28|COLLECT COD|AIR|across the slyly bol\n5347|22862|7867|3|34|60685.24|0.06|0.00|A|F|1995-05-18|1995-04-04|1995-06-02|DELIVER IN PERSON|SHIP| pending deposits. fluffily regular senti\n5347|39456|9457|4|4|5581.80|0.06|0.03|A|F|1995-03-24|1995-04-03|1995-04-01|NONE|SHIP|ldly pending asymptotes ki\n5347|130711|5738|5|21|36575.91|0.08|0.04|R|F|1995-04-01|1995-04-16|1995-04-23|NONE|SHIP|sly slyly final requests. careful\n5347|55468|479|6|6|8540.76|0.06|0.02|A|F|1995-04-11|1995-04-14|1995-05-02|NONE|TRUCK|lly unusual ideas. sl\n5347|49567|2072|7|18|27298.08|0.01|0.01|N|F|1995-05-24|1995-05-07|1995-06-19|NONE|FOB|he ideas among the requests \n5348|68446|5965|1|21|29703.24|0.10|0.04|N|O|1997-12-11|1997-12-24|1997-12-28|NONE|REG AIR| regular theodolites haggle car\n5348|155865|896|2|31|59546.66|0.07|0.02|N|O|1998-01-04|1997-12-09|1998-01-17|COLLECT COD|RAIL|are finally\n5348|16638|6639|3|16|24874.08|0.06|0.08|N|O|1998-02-28|1997-12-25|1998-03-12|DELIVER IN PERSON|AIR|uriously thin pinto beans \n5348|19183|1685|4|7|7715.26|0.04|0.00|N|O|1998-01-29|1997-12-20|1998-02-10|DELIVER IN PERSON|RAIL|even foxes. epitap\n5348|1818|4319|5|37|63632.97|0.06|0.07|N|O|1997-12-01|1998-02-02|1997-12-07|NONE|FOB|y according to the carefully pending acco\n5348|142875|5390|6|14|26850.18|0.06|0.05|N|O|1997-12-16|1998-01-12|1997-12-24|COLLECT COD|FOB|en pinto beans. somas cajo\n5349|155065|5066|1|19|21281.14|0.06|0.01|N|O|1996-09-11|1996-11-18|1996-09-22|TAKE BACK RETURN|FOB|endencies use whithout the special \n5349|167110|4659|2|14|16479.54|0.06|0.00|N|O|1996-11-07|1996-11-17|1996-11-20|TAKE BACK RETURN|TRUCK|fully regular \n5349|3482|3483|3|6|8312.88|0.10|0.01|N|O|1996-12-30|1996-10-08|1997-01-01|DELIVER IN PERSON|MAIL|inal deposits affix carefully\n5350|121045|1046|1|19|20254.76|0.02|0.06|R|F|1993-10-20|1993-11-15|1993-11-17|DELIVER IN PERSON|RAIL|romise slyly alongsi\n5350|190829|5868|2|44|84472.08|0.04|0.06|R|F|1993-10-30|1993-11-23|1993-11-25|DELIVER IN PERSON|AIR|p above the ironic, pending dep\n5350|53095|8106|3|12|12577.08|0.10|0.04|A|F|1994-01-30|1993-11-21|1994-02-15|COLLECT COD|REG AIR| cajole. even instructions haggle. blithe\n5350|154932|9963|4|7|13908.51|0.08|0.00|R|F|1993-10-19|1993-12-28|1993-11-04|NONE|SHIP|alongside of th\n5350|128746|8747|5|27|47917.98|0.07|0.04|A|F|1993-11-25|1993-12-27|1993-12-08|COLLECT COD|TRUCK|es. blithe theodolites haggl\n5351|6095|1096|1|36|36039.24|0.06|0.05|N|O|1998-07-27|1998-07-06|1998-08-25|NONE|MAIL|ss the ironic, regular asymptotes cajole \n5351|32041|4545|2|47|45732.88|0.04|0.01|N|O|1998-05-30|1998-08-08|1998-06-23|DELIVER IN PERSON|REG AIR|s. grouches cajole. sile\n5351|105327|2858|3|2|2664.64|0.00|0.02|N|O|1998-05-12|1998-07-15|1998-05-24|NONE|TRUCK|g accounts wake furiously slyly even dolph\n5376|60778|8297|1|42|73028.34|0.10|0.04|A|F|1994-09-20|1994-08-30|1994-09-29|TAKE BACK RETURN|REG AIR|y even asymptotes. courts are unusual pa\n5376|90990|6009|2|44|87163.56|0.05|0.02|R|F|1994-08-30|1994-08-05|1994-09-07|COLLECT COD|AIR|ithe packages detect final theodolites. f\n5376|64871|4872|3|18|33045.66|0.02|0.08|A|F|1994-10-29|1994-09-13|1994-11-01|COLLECT COD|MAIL| accounts boo\n5377|78172|3187|1|40|46006.80|0.00|0.04|N|O|1997-05-21|1997-06-15|1997-05-26|DELIVER IN PERSON|AIR|lithely ironic theodolites are care\n5377|29316|6823|2|17|21170.27|0.09|0.00|N|O|1997-07-05|1997-05-25|1997-07-22|COLLECT COD|RAIL|dencies. carefully regular re\n5377|102464|7485|3|23|33728.58|0.07|0.08|N|O|1997-06-26|1997-07-13|1997-07-08|COLLECT COD|RAIL| silent wa\n5377|103123|5634|4|12|13513.44|0.05|0.07|N|O|1997-05-08|1997-06-15|1997-05-15|DELIVER IN PERSON|MAIL| ironic, final\n5377|172292|4810|5|27|36835.83|0.08|0.02|N|O|1997-07-11|1997-06-12|1997-08-08|TAKE BACK RETURN|MAIL|press theodolites. e\n5378|154773|7289|1|39|71283.03|0.07|0.04|R|F|1992-11-25|1992-12-22|1992-12-02|COLLECT COD|AIR|ts are quickly around the\n5378|61083|6096|2|46|48027.68|0.01|0.04|A|F|1993-02-17|1993-01-20|1993-02-26|COLLECT COD|REG AIR|into beans sleep. fu\n5378|9760|7261|3|18|30055.68|0.02|0.03|R|F|1992-11-25|1992-12-21|1992-12-10|COLLECT COD|FOB|onic accounts was bold, \n5379|198787|1307|1|40|75431.20|0.01|0.08|N|O|1995-10-01|1995-10-19|1995-10-30|COLLECT COD|MAIL|carefully final accounts haggle blithely. \n5380|181291|1292|1|14|19212.06|0.10|0.01|N|O|1997-12-18|1997-12-03|1998-01-06|NONE|RAIL|final platelets.\n5380|146343|3886|2|10|13893.40|0.09|0.05|N|O|1997-11-24|1998-01-10|1997-12-21|COLLECT COD|AIR|refully pending deposits. special, even t\n5380|183714|6233|3|40|71908.40|0.02|0.08|N|O|1997-12-30|1997-11-27|1998-01-09|DELIVER IN PERSON|SHIP|ar asymptotes. blithely r\n5380|65789|802|4|6|10528.68|0.09|0.05|N|O|1997-11-15|1998-01-08|1997-12-11|COLLECT COD|MAIL|es. fluffily brave accounts across t\n5380|106753|6754|5|48|84468.00|0.04|0.03|N|O|1997-12-01|1997-12-28|1997-12-05|DELIVER IN PERSON|FOB|encies haggle car\n5381|187310|9829|1|37|51700.47|0.04|0.01|A|F|1993-04-08|1993-04-07|1993-04-12|DELIVER IN PERSON|SHIP|ly final deposits print carefully. unusua\n5381|110002|5025|2|48|48576.00|0.04|0.03|R|F|1993-04-22|1993-04-17|1993-05-14|TAKE BACK RETURN|FOB|luffily spec\n5381|191097|1098|3|13|15445.17|0.08|0.03|R|F|1993-05-09|1993-04-26|1993-05-25|NONE|FOB|s after the f\n5381|167208|4757|4|17|21678.40|0.05|0.05|R|F|1993-05-25|1993-04-14|1993-06-17|NONE|MAIL|ckly final requests haggle qui\n5381|62375|9894|5|49|65531.13|0.06|0.02|R|F|1993-05-08|1993-04-07|1993-06-03|NONE|FOB| accounts. regular, regula\n5381|131056|6083|6|33|35872.65|0.10|0.00|A|F|1993-04-09|1993-04-03|1993-04-22|DELIVER IN PERSON|SHIP|ly special deposits \n5381|43349|862|7|31|40062.54|0.04|0.05|A|F|1993-04-10|1993-03-22|1993-04-13|TAKE BACK RETURN|MAIL|the carefully expre\n5382|152952|7983|1|34|68168.30|0.03|0.03|R|F|1992-02-22|1992-02-18|1992-03-02|DELIVER IN PERSON|FOB|gular accounts. even accounts integrate\n5382|54879|7385|2|13|23840.31|0.09|0.06|A|F|1992-01-16|1992-03-12|1992-02-06|NONE|MAIL|eodolites. final foxes \n5382|148097|8098|3|3|3435.27|0.10|0.06|A|F|1992-03-22|1992-03-06|1992-04-19|TAKE BACK RETURN|AIR|efully unusua\n5382|61771|6784|4|20|34655.40|0.08|0.02|A|F|1992-03-26|1992-02-17|1992-04-15|DELIVER IN PERSON|FOB|carefully regular accounts. slyly ev\n5382|176212|6213|5|14|18034.94|0.02|0.02|A|F|1992-04-05|1992-04-05|1992-05-04|TAKE BACK RETURN|FOB| brave platelets. ev\n5382|179706|4741|6|6|10714.20|0.02|0.01|A|F|1992-03-07|1992-04-02|1992-03-18|TAKE BACK RETURN|FOB|y final foxes by the sl\n5382|104356|1887|7|48|65296.80|0.05|0.05|A|F|1992-02-14|1992-03-19|1992-02-25|DELIVER IN PERSON|REG AIR|nts integrate quickly ca\n5383|95116|5117|1|12|13333.32|0.04|0.00|N|O|1995-07-02|1995-08-16|1995-08-01|TAKE BACK RETURN|AIR|y regular instructi\n5408|101941|6962|1|2|3885.88|0.07|0.04|R|F|1992-08-21|1992-10-03|1992-08-28|DELIVER IN PERSON|MAIL|cross the dolphins h\n5408|117718|230|2|35|60749.85|0.04|0.05|R|F|1992-10-02|1992-10-17|1992-10-13|TAKE BACK RETURN|AIR|thely ironic requests alongside of the sl\n5408|75127|7635|3|34|37472.08|0.10|0.02|A|F|1992-10-22|1992-08-25|1992-11-16|DELIVER IN PERSON|TRUCK|requests detect blithely a\n5408|53108|5614|4|48|50932.80|0.04|0.05|R|F|1992-09-30|1992-08-27|1992-10-27|NONE|TRUCK|. furiously regular \n5408|182529|7566|5|8|12892.16|0.03|0.07|A|F|1992-10-24|1992-09-06|1992-11-03|NONE|AIR|thely regular hocke\n5409|193319|877|1|27|38132.37|0.01|0.02|A|F|1992-02-14|1992-03-18|1992-02-23|DELIVER IN PERSON|AIR|eodolites \n5409|103573|3574|2|38|59909.66|0.01|0.02|A|F|1992-03-17|1992-03-29|1992-04-13|NONE|REG AIR|onic, regular accounts! blithely even\n5409|140347|7890|3|17|23584.78|0.07|0.00|A|F|1992-01-13|1992-04-05|1992-01-20|DELIVER IN PERSON|AIR|cross the sil\n5409|403|7904|4|9|11730.60|0.07|0.03|A|F|1992-02-15|1992-04-02|1992-02-28|DELIVER IN PERSON|AIR| unusual, unusual reques\n5409|158141|8142|5|37|44368.18|0.06|0.04|R|F|1992-05-07|1992-02-10|1992-05-20|DELIVER IN PERSON|FOB|ously regular packages. packages\n5409|63902|6409|6|14|26122.60|0.03|0.08|R|F|1992-02-14|1992-03-26|1992-02-29|DELIVER IN PERSON|AIR|osits cajole furiously\n5410|116768|6769|1|48|85668.48|0.04|0.08|N|O|1998-09-27|1998-09-11|1998-10-01|TAKE BACK RETURN|AIR| about the slyly even courts. quickly regul\n5410|104411|6922|2|41|58031.81|0.01|0.07|N|O|1998-08-25|1998-10-20|1998-09-01|DELIVER IN PERSON|REG AIR|sly. slyly ironic theodolites\n5410|28027|530|3|40|38200.80|0.07|0.08|N|O|1998-11-17|1998-10-02|1998-11-27|COLLECT COD|TRUCK|iously special accounts are along th\n5410|49503|2008|4|8|11620.00|0.05|0.04|N|O|1998-09-12|1998-10-22|1998-09-22|DELIVER IN PERSON|TRUCK|ly. fluffily ironic platelets alon\n5411|95432|451|1|17|24266.31|0.05|0.01|N|O|1997-07-22|1997-07-14|1997-07-30|TAKE BACK RETURN|REG AIR| slyly slyly even deposits. carefully b\n5411|112029|4541|2|10|10410.20|0.08|0.01|N|O|1997-07-19|1997-08-04|1997-07-26|TAKE BACK RETURN|MAIL|nding, special foxes unw\n5411|55057|5058|3|5|5060.25|0.10|0.01|N|O|1997-09-12|1997-08-03|1997-09-23|DELIVER IN PERSON|FOB| bold, ironic theodo\n5411|128590|3615|4|15|24278.85|0.08|0.05|N|O|1997-07-01|1997-07-15|1997-07-07|COLLECT COD|RAIL|attainments sleep slyly ironic\n5411|3016|3017|5|19|17461.19|0.05|0.08|N|O|1997-05-25|1997-07-30|1997-06-19|COLLECT COD|RAIL|ial accounts according to the f\n5412|53078|8089|1|2|2062.14|0.03|0.07|N|O|1998-04-14|1998-04-02|1998-04-19|TAKE BACK RETURN|REG AIR| sleep above the furiou\n5412|65168|2687|2|48|54391.68|0.01|0.08|N|O|1998-02-22|1998-03-28|1998-03-18|TAKE BACK RETURN|TRUCK|s. slyly final packages cajole blithe\n5412|73678|1200|3|31|51201.77|0.05|0.08|N|O|1998-03-23|1998-04-17|1998-04-10|NONE|SHIP|t the accounts detect slyly about the c\n5412|96319|1338|4|26|34198.06|0.02|0.08|N|O|1998-01-22|1998-04-19|1998-02-17|NONE|AIR| the blithel\n5413|125457|5458|1|48|71157.60|0.02|0.08|N|O|1998-01-25|1997-11-20|1998-02-22|COLLECT COD|SHIP| theodolites. furiously ironic instr\n5413|141805|4320|2|37|68331.60|0.02|0.07|N|O|1997-12-08|1998-01-01|1997-12-13|COLLECT COD|TRUCK|usly bold instructions affix idly unusual, \n5413|110854|5877|3|36|67134.60|0.02|0.07|N|O|1997-12-12|1997-11-28|1997-12-25|NONE|TRUCK|ular, regular ideas mold! final requests\n5413|109216|1727|4|22|26954.62|0.02|0.08|N|O|1997-11-10|1997-11-24|1997-11-22|DELIVER IN PERSON|FOB|posits. quick\n5413|188636|6191|5|5|8623.15|0.10|0.01|N|O|1997-11-28|1997-11-24|1997-12-05|NONE|RAIL|tes are al\n5413|189019|9020|6|32|35456.32|0.02|0.03|N|O|1997-10-28|1998-01-03|1997-11-10|NONE|TRUCK|refully special package\n5413|30016|2520|7|32|30272.32|0.06|0.07|N|O|1997-10-23|1997-12-09|1997-11-17|NONE|TRUCK|he quickly ironic ideas. slyly ironic ide\n5414|67051|7052|1|40|40722.00|0.07|0.06|R|F|1993-04-07|1993-05-18|1993-04-23|COLLECT COD|AIR|ts are evenly across\n5414|122106|4619|2|48|54148.80|0.06|0.07|R|F|1993-06-08|1993-05-14|1993-07-06|DELIVER IN PERSON|FOB| silent dolphins; fluffily regular tithe\n5414|34768|2278|3|23|39163.48|0.10|0.00|A|F|1993-07-22|1993-05-26|1993-08-08|COLLECT COD|MAIL|e bold, express dolphins. spec\n5414|132077|2078|4|15|16636.05|0.06|0.08|R|F|1993-05-18|1993-06-09|1993-05-27|DELIVER IN PERSON|REG AIR|e slyly about the carefully regula\n5414|8372|873|5|19|24327.03|0.01|0.05|R|F|1993-04-06|1993-05-12|1993-05-02|DELIVER IN PERSON|RAIL|ffily silent theodolites na\n5414|97119|2138|6|28|31251.08|0.10|0.05|A|F|1993-03-27|1993-06-04|1993-04-07|TAKE BACK RETURN|SHIP|ts sleep sl\n5415|101746|4257|1|44|76900.56|0.00|0.06|A|F|1992-08-19|1992-10-26|1992-09-17|TAKE BACK RETURN|TRUCK| requests. unusual theodolites sleep agains\n5415|30220|7730|2|16|18403.52|0.08|0.00|A|F|1992-09-29|1992-09-12|1992-10-10|NONE|AIR|pinto beans haggle furiously\n5415|101066|6087|3|6|6402.36|0.10|0.03|A|F|1992-10-28|1992-09-09|1992-11-20|COLLECT COD|RAIL|ges around the fur\n5415|15099|5100|4|43|43605.87|0.01|0.02|R|F|1992-11-17|1992-09-14|1992-12-14|DELIVER IN PERSON|SHIP|yly blithely stealthy deposits. carefu\n5415|160014|7563|5|11|11814.11|0.00|0.01|R|F|1992-11-22|1992-10-19|1992-12-10|DELIVER IN PERSON|SHIP|gle among t\n5415|143712|6227|6|46|80762.66|0.03|0.03|R|F|1992-08-25|1992-09-10|1992-09-22|DELIVER IN PERSON|REG AIR|ve the fluffily \n5415|152967|2968|7|11|22219.56|0.08|0.06|A|F|1992-08-21|1992-09-04|1992-08-23|NONE|TRUCK|unts maintain carefully unusual\n5440|114100|9123|1|3|3342.30|0.02|0.08|N|O|1997-02-18|1997-02-28|1997-03-15|NONE|SHIP|y. accounts haggle along the blit\n5441|163452|8485|1|3|4546.35|0.00|0.02|R|F|1994-08-12|1994-10-14|1994-09-01|TAKE BACK RETURN|REG AIR|are. unusual, \n5441|130764|765|2|49|87943.24|0.02|0.03|A|F|1994-09-23|1994-09-22|1994-10-22|NONE|FOB|ording to the furio\n5441|143016|559|3|33|34947.33|0.09|0.02|R|F|1994-10-09|1994-10-06|1994-10-30|DELIVER IN PERSON|TRUCK|ges. final instruction\n5441|66124|1137|4|47|51235.64|0.07|0.08|R|F|1994-11-19|1994-10-16|1994-12-16|TAKE BACK RETURN|FOB|ounts wake slyly about the express instr\n5442|41917|6926|1|16|29742.56|0.00|0.00|N|O|1998-04-12|1998-03-03|1998-05-04|TAKE BACK RETURN|RAIL|r packages. accounts haggle dependencies. f\n5442|87111|4636|2|45|49414.95|0.08|0.01|N|O|1998-03-30|1998-02-24|1998-04-18|TAKE BACK RETURN|AIR|old slyly after \n5442|60318|5331|3|12|15339.72|0.01|0.08|N|O|1998-04-15|1998-03-18|1998-05-05|DELIVER IN PERSON|TRUCK|fully final\n5442|157234|7235|4|21|27115.83|0.07|0.06|N|O|1998-03-13|1998-02-19|1998-04-06|COLLECT COD|MAIL|ffily furiously ironic theodolites. furio\n5442|15990|5991|5|25|47649.75|0.04|0.00|N|O|1998-03-29|1998-02-13|1998-04-13|TAKE BACK RETURN|REG AIR|ake furiously. slyly express th\n5442|143507|1050|6|26|40313.00|0.08|0.07|N|O|1998-03-21|1998-03-21|1998-03-25|TAKE BACK RETURN|AIR|have to sleep furiously bold ideas. blith\n5443|177288|7289|1|14|19113.92|0.02|0.00|N|O|1996-10-27|1996-11-11|1996-11-21|DELIVER IN PERSON|RAIL|s after the regular, regular deposits hag\n5443|71326|1327|2|39|50595.48|0.03|0.07|N|O|1996-11-01|1996-11-30|1996-11-19|NONE|RAIL|gage carefully across the furiously\n5443|159719|4750|3|25|44467.75|0.05|0.00|N|O|1996-12-07|1997-01-08|1997-01-05|NONE|FOB|use carefully above the pinto bea\n5443|190188|5227|4|6|7669.08|0.05|0.02|N|O|1996-11-17|1996-12-03|1996-11-30|TAKE BACK RETURN|AIR|p fluffily foxe\n5443|82716|5225|5|40|67948.40|0.03|0.03|N|O|1997-01-28|1996-12-10|1997-02-13|NONE|FOB|n courts. special re\n5444|185509|3064|1|21|33484.50|0.01|0.07|A|F|1995-04-11|1995-04-25|1995-04-21|DELIVER IN PERSON|RAIL|ar packages haggle above th\n5444|42182|7191|2|40|44967.20|0.05|0.08|N|O|1995-07-09|1995-04-25|1995-07-19|COLLECT COD|TRUCK|ously bold ideas. instructions wake slyl\n5444|149858|7401|3|40|76314.00|0.08|0.01|A|F|1995-04-06|1995-05-08|1995-05-06|DELIVER IN PERSON|AIR| even packages.\n5444|58950|3961|4|33|62995.35|0.05|0.04|N|O|1995-06-24|1995-04-24|1995-07-13|DELIVER IN PERSON|SHIP|ut the courts cajole blithely excuses\n5444|170692|8244|5|21|37016.49|0.04|0.00|R|F|1995-05-05|1995-05-25|1995-05-29|TAKE BACK RETURN|REG AIR|aves serve sly\n5444|19026|4029|6|21|19845.42|0.07|0.01|A|F|1995-03-30|1995-05-01|1995-03-31|COLLECT COD|AIR|furiously even theodolites.\n5445|89126|4143|1|33|36798.96|0.08|0.07|A|F|1993-10-21|1993-10-14|1993-10-28|DELIVER IN PERSON|REG AIR|ests. final instructions\n5445|130119|120|2|12|13789.32|0.09|0.08|R|F|1993-11-02|1993-09-05|1993-11-26|COLLECT COD|FOB| slyly pending pinto beans was slyly al\n5445|102859|7880|3|46|85645.10|0.04|0.07|A|F|1993-10-06|1993-09-15|1993-10-28|DELIVER IN PERSON|RAIL|old depend\n5445|148677|8678|4|10|17256.70|0.08|0.06|A|F|1993-09-16|1993-10-05|1993-10-01|NONE|TRUCK|ncies abou\n5445|12496|7499|5|14|19718.86|0.00|0.02|R|F|1993-11-19|1993-10-18|1993-12-07|NONE|RAIL| requests. bravely i\n5446|189366|4403|1|27|39294.72|0.05|0.07|R|F|1994-07-21|1994-08-25|1994-08-17|TAKE BACK RETURN|RAIL|ously across the quic\n5447|98536|1046|1|31|47570.43|0.09|0.03|N|O|1996-07-14|1996-05-07|1996-07-17|COLLECT COD|SHIP| foxes sleep. blithely unusual accounts det\n5472|58956|8957|1|27|51703.65|0.09|0.06|A|F|1993-08-04|1993-07-07|1993-09-03|COLLECT COD|TRUCK|fily pending attainments. unus\n5472|67697|5216|2|28|46611.32|0.00|0.03|A|F|1993-07-28|1993-05-28|1993-08-11|TAKE BACK RETURN|FOB|ffily pendin\n5472|177295|2330|3|45|61753.05|0.06|0.02|R|F|1993-06-05|1993-05-14|1993-06-10|NONE|TRUCK| idle packages. furi\n5472|183690|8727|4|37|65626.53|0.07|0.05|R|F|1993-06-15|1993-07-03|1993-07-09|DELIVER IN PERSON|RAIL|egrate carefully dependencies. \n5472|74051|4052|5|40|41002.00|0.02|0.05|A|F|1993-04-13|1993-07-04|1993-05-04|NONE|REG AIR|e requests detect furiously. ruthlessly un\n5472|166920|4469|6|39|77489.88|0.02|0.03|R|F|1993-04-18|1993-07-10|1993-05-12|TAKE BACK RETURN|MAIL|uriously carefully \n5472|14390|1894|7|1|1304.39|0.03|0.02|A|F|1993-04-14|1993-06-28|1993-04-16|NONE|RAIL|s use furiou\n5473|47878|383|1|9|16432.83|0.03|0.07|R|F|1992-06-03|1992-05-30|1992-06-09|TAKE BACK RETURN|AIR| excuses sleep blithely! regular dep\n5473|69115|9116|2|27|29270.97|0.01|0.03|A|F|1992-04-06|1992-04-26|1992-04-29|TAKE BACK RETURN|MAIL|the deposits. warthogs wake fur\n5473|14473|1977|3|33|45786.51|0.09|0.00|R|F|1992-05-18|1992-06-10|1992-06-13|TAKE BACK RETURN|MAIL|efully above the even, \n5474|183763|3764|1|38|70176.88|0.01|0.08|A|F|1992-07-15|1992-07-16|1992-07-20|NONE|REG AIR| slyly beneath \n5474|93631|1159|2|10|16246.30|0.06|0.00|R|F|1992-08-08|1992-08-10|1992-08-24|TAKE BACK RETURN|TRUCK|pinto bean\n5474|47831|2840|3|31|55143.73|0.00|0.08|R|F|1992-08-02|1992-07-12|1992-08-04|NONE|TRUCK|the furiously express ideas. speci\n5474|89506|4523|4|46|68793.00|0.03|0.04|A|F|1992-06-07|1992-07-11|1992-06-22|NONE|TRUCK|nstructions. furio\n5475|182422|9977|1|10|15044.20|0.09|0.08|N|O|1996-07-19|1996-08-22|1996-07-23|COLLECT COD|AIR|ding to the deposits wake fina\n5476|47660|2669|1|13|20899.58|0.01|0.04|N|O|1997-12-27|1997-12-08|1997-12-29|COLLECT COD|TRUCK|iously special ac\n5476|19679|2181|2|17|27177.39|0.10|0.01|N|O|1998-02-02|1998-01-28|1998-02-14|COLLECT COD|FOB|ng dependencies until the f\n5477|79222|6744|1|20|24024.40|0.03|0.01|N|O|1998-03-21|1998-02-09|1998-04-07|TAKE BACK RETURN|SHIP|platelets about the ironic\n5477|76155|8663|2|21|23754.15|0.03|0.00|N|O|1998-01-28|1998-02-15|1998-02-24|TAKE BACK RETURN|SHIP|blate slyly. silent\n5477|133790|3791|3|31|56537.49|0.04|0.01|N|O|1998-01-11|1998-01-30|1998-02-04|DELIVER IN PERSON|MAIL| special Tiresias cajole furiously. pending\n5477|192129|7168|4|16|19537.92|0.00|0.01|N|O|1998-03-07|1998-03-12|1998-04-06|COLLECT COD|RAIL|regular, s\n5477|95742|761|5|23|39968.02|0.00|0.06|N|O|1998-01-04|1998-02-23|1998-01-24|NONE|REG AIR|telets wake blithely ab\n5477|120319|2832|6|19|25446.89|0.10|0.03|N|O|1998-02-03|1998-01-30|1998-03-04|TAKE BACK RETURN|MAIL|ost carefully packages.\n5478|7283|7284|1|39|46420.92|0.09|0.06|N|O|1996-08-19|1996-06-25|1996-09-08|DELIVER IN PERSON|SHIP|s. furiously \n5478|1079|3580|2|47|46063.29|0.10|0.01|N|O|1996-08-15|1996-07-12|1996-08-31|NONE|RAIL| instructions; slyly even accounts hagg\n5478|118847|1359|3|25|46646.00|0.09|0.07|N|O|1996-06-08|1996-07-12|1996-07-07|NONE|TRUCK|unusual, pending requests haggle accoun\n5479|137771|285|1|50|90438.50|0.02|0.02|A|F|1993-12-24|1994-02-14|1994-01-18|DELIVER IN PERSON|MAIL|ironic gifts. even dependencies sno\n5479|103906|3907|2|19|36288.10|0.05|0.03|A|F|1994-01-22|1994-03-07|1994-02-11|DELIVER IN PERSON|SHIP|arefully bo\n5504|67102|2115|1|4|4276.40|0.10|0.07|A|F|1993-04-30|1993-03-01|1993-05-22|DELIVER IN PERSON|AIR|into beans boost. \n5504|176056|6057|2|7|7924.35|0.03|0.05|R|F|1993-04-25|1993-03-15|1993-05-06|NONE|TRUCK|packages detect furiously express reques\n5504|159600|7146|3|29|48128.40|0.05|0.03|A|F|1993-01-28|1993-02-13|1993-02-27|NONE|SHIP|ajole carefully. care\n5505|24501|2008|1|43|61296.50|0.07|0.01|N|O|1997-12-30|1997-11-28|1998-01-09|TAKE BACK RETURN|TRUCK|y alongside of the special requests.\n5505|181434|8989|2|33|50009.19|0.05|0.08|N|O|1998-01-11|1997-11-11|1998-01-30|TAKE BACK RETURN|AIR|ithely unusual excuses integrat\n5505|154641|9672|3|10|16956.40|0.06|0.01|N|O|1997-10-28|1997-11-27|1997-10-29|DELIVER IN PERSON|AIR| furiously special asym\n5505|39422|4429|4|18|24505.56|0.04|0.04|N|O|1997-10-25|1997-12-12|1997-10-30|TAKE BACK RETURN|RAIL| to the quickly express pac\n5505|161992|7025|5|46|94483.54|0.05|0.00|N|O|1998-01-06|1997-11-04|1998-02-04|TAKE BACK RETURN|SHIP|usly ironic dependencies haggle across \n5506|139309|9310|1|2|2696.60|0.00|0.03|R|F|1994-02-04|1994-01-13|1994-02-17|COLLECT COD|MAIL|onic theodolites are fluffil\n5506|159024|9025|2|6|6498.12|0.07|0.06|R|F|1994-02-21|1994-01-30|1994-02-27|DELIVER IN PERSON|MAIL|hely according to the furiously unusua\n5507|9600|4601|1|23|34720.80|0.05|0.04|N|O|1998-09-04|1998-07-04|1998-09-18|TAKE BACK RETURN|AIR|ously slow packages poach whithout the\n5507|137601|7602|2|48|78652.80|0.03|0.01|N|O|1998-08-03|1998-08-10|1998-08-24|DELIVER IN PERSON|AIR|yly idle deposits. final, final fox\n5507|44676|7181|3|4|6482.68|0.04|0.06|N|O|1998-06-06|1998-07-02|1998-06-27|TAKE BACK RETURN|RAIL|into beans are\n5507|66850|9357|4|22|39970.70|0.07|0.01|N|O|1998-07-08|1998-08-10|1998-07-22|DELIVER IN PERSON|TRUCK|gular ideas. carefully unu\n5507|131412|1413|5|48|69283.68|0.06|0.01|N|O|1998-07-21|1998-07-15|1998-07-31|DELIVER IN PERSON|SHIP|uriously regular acc\n5508|116947|4481|1|4|7855.76|0.10|0.04|N|O|1996-09-01|1996-08-02|1996-09-17|COLLECT COD|AIR|fluffily about the even \n5509|196663|1702|1|3|5278.98|0.03|0.02|A|F|1994-06-14|1994-05-11|1994-06-17|NONE|SHIP| quickly fin\n5509|98560|6088|2|17|26495.52|0.03|0.07|R|F|1994-07-01|1994-06-30|1994-07-31|COLLECT COD|AIR|ccounts wake ar\n5509|92611|139|3|30|48108.30|0.04|0.04|A|F|1994-07-23|1994-06-01|1994-08-08|NONE|AIR|counts haggle pinto beans. furiously \n5509|99849|4868|4|45|83197.80|0.00|0.07|A|F|1994-07-24|1994-05-28|1994-08-20|COLLECT COD|AIR|counts sleep. f\n5509|155719|3265|5|35|62114.85|0.04|0.03|A|F|1994-04-17|1994-06-29|1994-04-24|COLLECT COD|RAIL|c accounts. ca\n5510|15828|3332|1|8|13950.56|0.01|0.01|A|F|1993-03-16|1993-03-29|1993-03-24|DELIVER IN PERSON|FOB|n packages boost sly\n5510|19300|6804|2|46|56087.80|0.02|0.07|A|F|1993-03-12|1993-02-09|1993-03-19|NONE|TRUCK|silent packages cajole doggedly regular \n5510|161054|1055|3|47|52407.35|0.03|0.01|A|F|1993-01-20|1993-03-25|1993-02-15|DELIVER IN PERSON|SHIP|riously even requests. slyly bold accou\n5510|23375|882|4|29|37652.73|0.09|0.08|A|F|1993-02-28|1993-03-28|1993-03-12|COLLECT COD|AIR|lithely fluffily ironic req\n5511|164477|6994|1|16|24663.52|0.10|0.05|A|F|1995-02-02|1995-01-06|1995-02-19|TAKE BACK RETURN|RAIL|thely bold theodolites \n5511|164130|1679|2|31|37018.03|0.09|0.01|A|F|1995-02-23|1995-01-21|1995-03-02|COLLECT COD|REG AIR|gular excuses. fluffily even pinto beans c\n5511|127764|277|3|49|87796.24|0.05|0.05|R|F|1994-12-21|1995-01-27|1994-12-26|NONE|REG AIR|bout the requests. theodolites \n5511|121474|3987|4|4|5981.88|0.08|0.02|R|F|1994-12-28|1995-01-16|1995-01-24|TAKE BACK RETURN|RAIL|lphins. carefully blithe de\n5511|8948|1449|5|23|42709.62|0.10|0.07|A|F|1995-03-11|1995-01-21|1995-03-27|TAKE BACK RETURN|TRUCK|ing dugouts \n5511|187419|2456|6|5|7532.05|0.08|0.05|R|F|1994-12-29|1995-01-16|1995-01-24|DELIVER IN PERSON|MAIL|al theodolites. blithely final de\n5511|142696|239|7|23|39989.87|0.02|0.07|R|F|1995-02-03|1995-01-05|1995-02-18|COLLECT COD|REG AIR|ully deposits. warthogs hagg\n5536|89717|7242|1|14|23893.94|0.08|0.06|N|O|1998-05-18|1998-05-08|1998-06-05|COLLECT COD|MAIL|instructions sleep \n5536|61023|3530|2|20|19680.40|0.08|0.04|N|O|1998-05-08|1998-05-10|1998-05-31|DELIVER IN PERSON|REG AIR|equests mo\n5536|196848|9368|3|35|68069.40|0.07|0.02|N|O|1998-05-19|1998-06-08|1998-06-05|NONE|MAIL|c, final theo\n5536|8827|8828|4|30|52074.60|0.05|0.07|N|O|1998-04-15|1998-05-23|1998-05-03|NONE|FOB|arefully regular theodolites according\n5536|140844|845|5|11|20733.24|0.02|0.08|N|O|1998-03-18|1998-05-12|1998-03-28|TAKE BACK RETURN|FOB| snooze furio\n5537|44015|9024|1|10|9590.10|0.05|0.08|N|O|1997-01-13|1996-12-25|1997-01-28|TAKE BACK RETURN|AIR| sleep carefully slyly bold depos\n5537|149459|7002|2|15|22626.75|0.07|0.04|N|O|1997-01-13|1996-12-25|1997-01-27|COLLECT COD|AIR|eposits. permanently pending packag\n5537|150896|5927|3|39|75928.71|0.03|0.00|N|O|1996-12-17|1996-11-08|1997-01-15|COLLECT COD|REG AIR| slyly bold packages are. qu\n5537|96020|3548|4|38|38608.76|0.01|0.00|N|O|1996-11-06|1996-11-23|1996-11-12|TAKE BACK RETURN|MAIL|s above the carefully ironic deposits \n5538|153301|8332|1|42|56880.60|0.05|0.00|A|F|1994-04-08|1994-03-17|1994-05-05|DELIVER IN PERSON|REG AIR|vely ironic accounts. furiously unusual acc\n5538|120083|84|2|4|4412.32|0.02|0.03|R|F|1994-03-21|1994-02-17|1994-04-11|TAKE BACK RETURN|REG AIR|ithely along the c\n5538|18521|1023|3|38|54701.76|0.03|0.06|R|F|1994-03-17|1994-02-11|1994-04-10|TAKE BACK RETURN|FOB|ular pinto beans. silent ideas above \n5538|77667|5189|4|9|14801.94|0.00|0.01|R|F|1993-12-26|1994-01-31|1994-01-03|TAKE BACK RETURN|REG AIR|encies across the blithely fina\n5539|64404|1923|1|42|57472.80|0.10|0.08|A|F|1994-09-29|1994-09-17|1994-10-20|DELIVER IN PERSON|RAIL|ons across the carefully si\n5540|180895|896|1|42|82987.38|0.02|0.08|N|O|1996-11-12|1996-12-18|1996-12-05|TAKE BACK RETURN|RAIL|ss dolphins haggle \n5540|101022|1023|2|2|2046.04|0.06|0.02|N|O|1996-12-12|1997-01-09|1996-12-25|DELIVER IN PERSON|MAIL|nic asymptotes could hav\n5540|63336|5843|3|19|24687.27|0.01|0.03|N|O|1997-02-06|1996-11-18|1997-02-20|DELIVER IN PERSON|SHIP| slyly slyl\n5540|71466|8988|4|24|34499.04|0.10|0.05|N|O|1997-01-09|1996-12-02|1997-01-23|COLLECT COD|FOB|deposits! ironic depths may engage-- b\n5541|95359|7869|1|39|52819.65|0.08|0.05|N|O|1997-11-17|1997-12-27|1997-12-11|TAKE BACK RETURN|RAIL|ding theodolites haggle against the slyly \n5542|188250|8251|1|6|8029.50|0.03|0.01|N|O|1996-06-14|1996-05-28|1996-07-11|DELIVER IN PERSON|TRUCK| foxes doubt. theodolites ca\n5543|142617|5132|1|14|23234.54|0.02|0.03|R|F|1993-10-09|1993-12-09|1993-10-21|NONE|SHIP|ecial reque\n5543|161544|9093|2|22|35321.88|0.04|0.00|A|F|1993-11-06|1993-11-02|1993-12-02|DELIVER IN PERSON|SHIP|instructions. deposits use quickly. ir\n5543|66993|9500|3|3|5879.97|0.08|0.05|R|F|1993-11-18|1993-11-05|1993-12-17|NONE|FOB|ress, even \n5543|146766|1795|4|8|14502.08|0.05|0.01|R|F|1993-10-28|1993-11-18|1993-11-07|NONE|SHIP|totes? iron\n5543|79919|9920|5|32|60765.12|0.03|0.03|R|F|1993-10-04|1993-11-14|1993-11-03|DELIVER IN PERSON|AIR|ully around the \n5543|183372|927|6|1|1455.37|0.03|0.07|A|F|1993-10-29|1993-11-11|1993-11-23|TAKE BACK RETURN|FOB|uriously. slyly\n5543|128171|3196|7|39|46767.63|0.06|0.00|R|F|1993-10-07|1993-11-15|1993-10-28|TAKE BACK RETURN|MAIL|l excuses are furiously. slyly unusual requ\n5568|165351|7868|1|50|70817.50|0.05|0.05|N|O|1995-07-14|1995-09-04|1995-08-03|COLLECT COD|TRUCK|furious ide\n5568|43012|3013|2|18|17190.18|0.01|0.08|N|O|1995-08-19|1995-08-18|1995-08-24|DELIVER IN PERSON|SHIP|structions haggle. carefully regular \n5568|88766|8767|3|35|61416.60|0.08|0.07|N|O|1995-09-17|1995-09-04|1995-10-14|NONE|SHIP|lyly. blit\n5569|28825|1328|1|25|43845.50|0.10|0.03|R|F|1993-06-29|1993-07-18|1993-07-05|TAKE BACK RETURN|TRUCK| deposits cajole above\n5569|57218|4734|2|26|30555.46|0.09|0.06|A|F|1993-08-21|1993-07-22|1993-09-09|DELIVER IN PERSON|MAIL|pitaphs. ironic req\n5569|54713|7219|3|48|80050.08|0.02|0.03|R|F|1993-06-16|1993-06-15|1993-07-09|COLLECT COD|SHIP|the fluffily\n5569|146526|1555|4|19|29877.88|0.10|0.08|R|F|1993-07-30|1993-06-21|1993-08-13|TAKE BACK RETURN|FOB| detect ca\n5569|58516|6032|5|15|22117.65|0.02|0.06|A|F|1993-06-29|1993-07-06|1993-07-05|DELIVER IN PERSON|MAIL|lithely bold requests boost fur\n5570|160555|8104|1|37|59775.35|0.08|0.02|N|O|1996-08-29|1996-10-23|1996-09-11|NONE|RAIL|y ironic pin\n5570|38607|3614|2|15|23184.00|0.09|0.02|N|O|1996-10-04|1996-10-05|1996-10-28|TAKE BACK RETURN|REG AIR|beans nag slyly special, regular pack\n5570|59349|9350|3|29|37941.86|0.02|0.05|N|O|1996-10-12|1996-10-20|1996-11-08|TAKE BACK RETURN|SHIP|he silent, enticing requests.\n5571|153454|5970|1|32|48238.40|0.05|0.01|R|F|1992-12-25|1993-03-01|1993-01-23|NONE|FOB| the blithely even packages nag q\n5571|93241|769|2|31|38261.44|0.09|0.07|R|F|1993-01-05|1993-01-18|1993-02-04|DELIVER IN PERSON|SHIP|uffily even accounts. quickly re\n5571|91430|8958|3|18|25585.74|0.10|0.05|R|F|1993-03-11|1993-02-28|1993-04-03|COLLECT COD|REG AIR|uests haggle furiously pending d\n5572|21549|6554|1|24|35292.96|0.08|0.08|R|F|1994-10-30|1994-10-02|1994-11-27|TAKE BACK RETURN|MAIL|ests cajole. evenly ironic exc\n5572|171660|9212|2|27|46754.82|0.03|0.04|A|F|1994-08-29|1994-09-10|1994-08-30|TAKE BACK RETURN|SHIP| accounts. carefully final accoun\n5572|86234|6235|3|19|23184.37|0.10|0.00|A|F|1994-08-12|1994-10-07|1994-09-01|DELIVER IN PERSON|RAIL|es. final, final requests wake blithely ag\n5572|134303|1843|4|46|61515.80|0.02|0.01|R|F|1994-09-08|1994-10-14|1994-10-01|NONE|REG AIR|ully regular platelet\n5572|23056|8061|5|34|33287.70|0.10|0.08|R|F|1994-10-22|1994-08-16|1994-11-08|NONE|TRUCK|asymptotes integrate. s\n5572|100044|45|6|14|14616.56|0.04|0.05|A|F|1994-11-02|1994-09-20|1994-11-03|COLLECT COD|RAIL|he fluffily express packages. fluffily fina\n5572|25241|7744|7|24|27989.76|0.01|0.05|R|F|1994-09-26|1994-09-04|1994-10-22|DELIVER IN PERSON|FOB| beans. foxes sleep fluffily across th\n5573|20138|2641|1|32|33860.16|0.05|0.07|N|O|1996-09-30|1996-10-25|1996-10-15|DELIVER IN PERSON|RAIL|egular depths haggl\n5573|49469|4478|2|2|2836.92|0.01|0.07|N|O|1996-08-26|1996-09-29|1996-09-04|COLLECT COD|TRUCK| even foxes. specia\n5573|10720|5723|3|46|75013.12|0.06|0.01|N|O|1996-11-04|1996-10-02|1996-11-15|DELIVER IN PERSON|MAIL|s haggle qu\n5573|168653|6202|4|43|74030.95|0.10|0.03|N|O|1996-10-22|1996-11-03|1996-11-02|TAKE BACK RETURN|FOB| furiously pending packages against \n5573|137649|7650|5|43|72525.52|0.05|0.04|N|O|1996-09-09|1996-09-24|1996-09-28|COLLECT COD|AIR| bold package\n5574|184533|7052|1|46|74406.38|0.02|0.07|A|F|1992-06-20|1992-04-19|1992-07-11|NONE|FOB|arefully express requests wake furiousl\n5574|32304|7311|2|21|25962.30|0.05|0.08|A|F|1992-03-22|1992-04-26|1992-04-16|TAKE BACK RETURN|TRUCK|fully final dugouts. express foxes nag \n5574|118564|3587|3|27|42729.12|0.10|0.06|R|F|1992-05-08|1992-05-19|1992-06-05|TAKE BACK RETURN|REG AIR|ecial realms. furiously entici\n5574|93359|5869|4|14|18932.90|0.09|0.01|R|F|1992-05-20|1992-04-09|1992-05-23|COLLECT COD|REG AIR| use slyly carefully special requests? slyl\n5574|84053|9070|5|19|19703.95|0.05|0.03|A|F|1992-05-28|1992-04-24|1992-06-11|TAKE BACK RETURN|REG AIR|old deposits int\n5575|57026|4542|1|7|6881.14|0.01|0.07|N|O|1995-10-01|1995-09-30|1995-10-06|NONE|FOB|s. slyly pending theodolites prin\n5575|30170|7680|2|23|25303.91|0.04|0.02|N|O|1995-10-26|1995-10-09|1995-11-13|TAKE BACK RETURN|AIR|enticingly final requests. ironically\n5575|62433|9952|3|16|22326.88|0.00|0.07|N|O|1995-08-17|1995-10-14|1995-08-30|NONE|RAIL|jole boldly beyond the final as\n5575|109718|9719|4|7|12093.97|0.01|0.04|N|O|1995-10-15|1995-09-14|1995-10-18|DELIVER IN PERSON|RAIL|special requests. final, final \n5600|186280|8799|1|34|46453.52|0.02|0.00|N|O|1997-03-22|1997-04-05|1997-04-09|TAKE BACK RETURN|MAIL|ly above the stealthy ideas. permane\n5600|7352|4853|2|19|23927.65|0.00|0.01|N|O|1997-04-10|1997-03-24|1997-04-16|TAKE BACK RETURN|TRUCK|dencies. carefully p\n5601|37982|5492|1|29|55679.42|0.09|0.04|A|F|1992-04-06|1992-02-24|1992-04-29|DELIVER IN PERSON|TRUCK| ironic ideas. final\n5601|163175|8208|2|45|55717.65|0.10|0.07|A|F|1992-03-25|1992-04-03|1992-04-04|TAKE BACK RETURN|MAIL|ts-- blithely final accounts cajole. carefu\n5601|72205|2206|3|38|44733.60|0.07|0.00|A|F|1992-01-08|1992-03-01|1992-01-09|TAKE BACK RETURN|REG AIR|ter the evenly final deposit\n5601|147847|362|4|12|22738.08|0.03|0.01|A|F|1992-02-27|1992-03-16|1992-03-27|COLLECT COD|TRUCK|ep carefully a\n5602|175324|2876|1|9|12593.88|0.08|0.03|N|O|1997-10-14|1997-09-14|1997-11-11|COLLECT COD|FOB|lar foxes; quickly ironic ac\n5602|61015|8534|2|31|30256.31|0.04|0.08|N|O|1997-09-04|1997-10-24|1997-09-07|NONE|TRUCK|rate fluffily regular platelets. blithel\n5602|67680|2693|3|30|49430.40|0.04|0.00|N|O|1997-09-20|1997-10-25|1997-10-12|DELIVER IN PERSON|FOB|e slyly even packages. careful\n5603|97812|5340|1|50|90490.50|0.03|0.02|A|F|1992-10-06|1992-08-20|1992-10-08|COLLECT COD|SHIP|final theodolites accor\n5603|115704|3238|2|49|84265.30|0.06|0.05|A|F|1992-06-24|1992-07-28|1992-07-01|DELIVER IN PERSON|FOB|fully silent requests. carefully fin\n5603|31836|9346|3|49|86623.67|0.00|0.02|R|F|1992-10-07|1992-07-21|1992-10-10|DELIVER IN PERSON|TRUCK|nic, pending dependencies print\n5604|135036|5037|1|44|47125.32|0.05|0.01|N|O|1998-08-06|1998-07-08|1998-09-04|NONE|RAIL|efully ironi\n5604|135056|7570|2|49|53461.45|0.10|0.00|N|O|1998-05-02|1998-07-07|1998-05-20|NONE|FOB|ove the regula\n5604|77004|9512|3|10|9810.00|0.07|0.05|N|O|1998-08-03|1998-06-23|1998-08-04|COLLECT COD|SHIP|ly final realms wake blit\n5605|86747|1764|1|50|86687.00|0.08|0.05|N|O|1996-08-26|1996-10-15|1996-09-04|TAKE BACK RETURN|RAIL|instructions sleep carefully ironic req\n5605|150710|711|2|7|12324.97|0.06|0.01|N|O|1996-12-13|1996-10-13|1996-12-15|TAKE BACK RETURN|FOB|lowly special courts nag among the furi\n5605|172146|7181|3|3|3654.42|0.01|0.02|N|O|1996-09-01|1996-10-02|1996-09-20|TAKE BACK RETURN|AIR|posits. accounts boost. t\n5605|54851|7357|4|45|81263.25|0.00|0.01|N|O|1996-09-05|1996-10-04|1996-09-13|COLLECT COD|FOB|ly unusual instructions. carefully ironic p\n5605|69922|4935|5|39|73784.88|0.00|0.08|N|O|1996-12-13|1996-11-03|1996-12-24|DELIVER IN PERSON|REG AIR|cial deposits. theodolites w\n5605|165991|5992|6|29|59652.71|0.08|0.08|N|O|1996-09-19|1996-10-22|1996-10-06|DELIVER IN PERSON|SHIP| quickly. quickly pending sen\n5606|173945|3946|1|47|94890.18|0.10|0.04|N|O|1996-12-23|1997-01-31|1997-01-20|DELIVER IN PERSON|REG AIR|carefully final foxes. pending, final\n5606|91684|1685|2|34|56973.12|0.09|0.06|N|O|1997-02-23|1997-02-08|1997-03-09|TAKE BACK RETURN|REG AIR|uses. slyly final \n5606|126750|6751|3|46|81730.50|0.04|0.00|N|O|1997-03-11|1997-01-13|1997-03-23|DELIVER IN PERSON|REG AIR|ter the ironic accounts. even, ironic depos\n5606|81657|1658|4|30|49159.50|0.08|0.04|N|O|1997-02-06|1997-01-26|1997-02-16|DELIVER IN PERSON|REG AIR| nag always. blithely express packages \n5606|6317|1318|5|25|30582.75|0.06|0.00|N|O|1996-12-25|1997-01-12|1997-01-11|TAKE BACK RETURN|AIR|breach about the furiously bold \n5606|153929|3930|6|3|5948.76|0.04|0.06|N|O|1997-01-11|1997-01-04|1997-02-08|COLLECT COD|AIR| sauternes. asympto\n5606|73859|3860|7|46|84311.10|0.07|0.01|N|O|1997-02-01|1997-01-31|1997-02-15|DELIVER IN PERSON|TRUCK|ow requests wake around the regular accoun\n5607|131532|4046|1|23|35961.19|0.02|0.06|R|F|1992-04-17|1992-02-12|1992-04-30|DELIVER IN PERSON|MAIL|the special, final patterns \n5632|9074|1575|1|48|47187.36|0.06|0.06|N|O|1996-05-08|1996-03-24|1996-06-04|TAKE BACK RETURN|FOB|unts. decoys u\n5632|105591|5592|2|21|33528.39|0.02|0.08|N|O|1996-03-22|1996-03-10|1996-04-10|NONE|AIR|refully regular pinto beans. ironic reques\n5632|66291|3810|3|24|30174.96|0.04|0.06|N|O|1996-03-23|1996-04-02|1996-03-30|TAKE BACK RETURN|MAIL|beans detect. quickly final i\n5633|159226|6772|1|28|35986.16|0.02|0.00|N|O|1998-08-14|1998-07-24|1998-08-26|TAKE BACK RETURN|SHIP|as boost quickly. unusual pinto \n5633|101711|1712|2|10|17127.10|0.09|0.04|N|O|1998-07-15|1998-08-03|1998-08-03|COLLECT COD|AIR|its cajole fluffily fluffily special pinto\n5633|45872|5873|3|27|49082.49|0.03|0.02|N|O|1998-09-28|1998-07-28|1998-10-12|DELIVER IN PERSON|AIR|ructions. even ideas haggle carefully r\n5633|163653|3654|4|50|85832.50|0.02|0.05|N|O|1998-07-23|1998-07-09|1998-08-21|DELIVER IN PERSON|TRUCK|ts. slyly regular \n5633|99323|1833|5|48|63471.36|0.01|0.05|N|O|1998-06-24|1998-07-22|1998-07-18|DELIVER IN PERSON|TRUCK|even courts haggle slyly at the requ\n5633|106564|1585|6|1|1570.56|0.02|0.03|N|O|1998-09-29|1998-08-28|1998-10-19|NONE|RAIL|thely notornis: \n5633|10697|3199|7|39|62699.91|0.02|0.08|N|O|1998-07-12|1998-07-03|1998-07-13|COLLECT COD|TRUCK|ding ideas cajole furiously after\n5634|184279|1834|1|26|35445.02|0.10|0.08|N|O|1996-10-29|1996-09-15|1996-11-24|COLLECT COD|REG AIR|ptotes mold qu\n5634|174875|2427|2|22|42897.14|0.02|0.05|N|O|1996-09-01|1996-08-31|1996-09-05|DELIVER IN PERSON|MAIL|silently unusual foxes above the blithely\n5634|108573|6104|3|16|25305.12|0.08|0.02|N|O|1996-11-15|1996-09-14|1996-12-04|NONE|AIR|ess ideas are carefully pending, even re\n5634|181044|6081|4|29|32626.16|0.00|0.01|N|O|1996-08-10|1996-10-29|1996-08-11|TAKE BACK RETURN|MAIL|ely final ideas. deposits sleep. reg\n5634|309|310|5|1|1209.30|0.04|0.02|N|O|1996-10-02|1996-10-21|1996-10-27|COLLECT COD|MAIL|ctions haggle carefully. carefully clo\n5635|82066|7083|1|43|45066.58|0.03|0.00|R|F|1992-10-12|1992-09-29|1992-11-01|TAKE BACK RETURN|TRUCK|cross the d\n5635|71392|1393|2|5|6816.95|0.05|0.08|R|F|1992-10-02|1992-11-05|1992-10-26|TAKE BACK RETURN|REG AIR|yly along the ironic, fi\n5635|71245|6260|3|12|14594.88|0.09|0.02|A|F|1992-10-18|1992-09-24|1992-11-17|NONE|REG AIR|ke slyly against the carefully final req\n5635|7781|5282|4|40|67551.20|0.03|0.01|A|F|1992-09-25|1992-11-05|1992-10-11|NONE|FOB|pending foxes. regular packages\n5635|168511|8512|5|38|60021.38|0.05|0.06|A|F|1992-10-09|1992-09-25|1992-10-18|NONE|MAIL|ckly pendin\n5635|161513|6546|6|23|36213.73|0.05|0.04|A|F|1992-08-24|1992-11-10|1992-09-21|NONE|AIR|ily pending packages. bold,\n5635|136198|8712|7|32|39494.08|0.03|0.08|R|F|1992-11-24|1992-09-20|1992-12-17|TAKE BACK RETURN|TRUCK|slyly even\n5636|69821|2328|1|18|32234.76|0.05|0.03|R|F|1995-05-14|1995-05-17|1995-06-12|DELIVER IN PERSON|REG AIR|slyly express requests. furiously pen\n5636|69120|6639|2|26|28317.12|0.03|0.06|A|F|1995-03-05|1995-05-16|1995-03-23|TAKE BACK RETURN|AIR| furiously final pinto beans o\n5636|89315|9316|3|21|27390.51|0.03|0.03|A|F|1995-03-13|1995-05-11|1995-03-24|COLLECT COD|AIR| are furiously unusual \n5636|108256|5787|4|15|18963.75|0.03|0.04|R|F|1995-04-21|1995-04-30|1995-05-05|DELIVER IN PERSON|REG AIR|efully special\n5636|46791|9296|5|13|22591.27|0.10|0.03|A|F|1995-05-11|1995-04-27|1995-05-26|COLLECT COD|AIR|en, fluffy accounts amon\n5636|11278|1279|6|33|39245.91|0.06|0.04|A|F|1995-03-09|1995-04-05|1995-03-23|DELIVER IN PERSON|MAIL|ding to the \n5636|133257|5771|7|24|30966.00|0.10|0.05|R|F|1995-04-12|1995-03-27|1995-04-16|DELIVER IN PERSON|RAIL|counts sleep furiously b\n5637|46670|9175|1|14|22633.38|0.03|0.05|N|O|1996-07-20|1996-07-26|1996-08-14|COLLECT COD|MAIL|y bold deposits wak\n5637|171887|1888|2|35|68560.80|0.09|0.08|N|O|1996-08-01|1996-08-04|1996-08-20|NONE|AIR|s sleep blithely alongside of the ironic\n5637|95346|2874|3|22|29509.48|0.01|0.07|N|O|1996-08-28|1996-07-30|1996-09-17|COLLECT COD|REG AIR|nding requests are ca\n5637|65175|2694|4|16|18242.72|0.03|0.03|N|O|1996-09-08|1996-08-31|1996-09-29|TAKE BACK RETURN|TRUCK|d packages. express requests\n5637|195700|5701|5|10|17957.00|0.01|0.00|N|O|1996-08-25|1996-08-11|1996-09-23|TAKE BACK RETURN|MAIL|ickly ironic gifts. blithely even cour\n5637|128281|794|6|27|35350.56|0.01|0.05|N|O|1996-06-27|1996-08-09|1996-07-27|DELIVER IN PERSON|REG AIR|oss the carefully express warhorses\n5638|137834|7835|1|45|84232.35|0.09|0.07|A|F|1994-05-17|1994-03-09|1994-06-15|NONE|TRUCK|ar foxes. fluffily pending accounts \n5638|167678|5227|2|12|20948.04|0.02|0.05|A|F|1994-02-05|1994-04-01|1994-02-25|COLLECT COD|TRUCK|n, even requests. furiously ironic not\n5638|161923|6956|3|21|41683.32|0.08|0.00|A|F|1994-03-13|1994-03-27|1994-03-17|DELIVER IN PERSON|TRUCK|press courts use f\n5639|46609|1618|1|11|17111.60|0.09|0.02|R|F|1994-09-18|1994-07-10|1994-10-12|TAKE BACK RETURN|SHIP|g the unusual pinto beans caj\n5664|121079|6104|1|25|27501.75|0.00|0.06|N|O|1998-10-29|1998-09-23|1998-11-25|COLLECT COD|FOB|eposits: furiously ironic grouch\n5664|172408|7443|2|9|13323.60|0.07|0.05|N|O|1998-07-31|1998-08-26|1998-08-12|COLLECT COD|RAIL| ironic deposits haggle furiously. re\n5664|52439|2440|3|31|43134.33|0.01|0.03|N|O|1998-11-10|1998-09-12|1998-12-07|TAKE BACK RETURN|FOB|ainst the never silent request\n5664|137545|2572|4|33|52223.82|0.08|0.03|N|O|1998-08-29|1998-09-17|1998-09-25|DELIVER IN PERSON|RAIL|d the final \n5664|111779|9313|5|44|78793.88|0.01|0.06|N|O|1998-09-24|1998-09-26|1998-10-23|NONE|TRUCK|ang thinly bold pa\n5664|67733|2746|6|34|57824.82|0.09|0.01|N|O|1998-09-10|1998-10-05|1998-09-15|COLLECT COD|RAIL|st. fluffily pending foxes na\n5664|181490|9045|7|9|14143.41|0.01|0.05|N|O|1998-11-04|1998-10-15|1998-11-20|TAKE BACK RETURN|REG AIR|yly. express ideas agai\n5665|100935|936|1|32|61949.76|0.00|0.02|A|F|1993-08-11|1993-08-01|1993-09-07|NONE|AIR|f the slyly even requests! regular request\n5665|4924|7425|2|14|25604.88|0.02|0.00|R|F|1993-06-29|1993-09-16|1993-07-16|DELIVER IN PERSON|AIR|- special pinto beans sleep quickly blithel\n5665|157104|7105|3|41|47605.10|0.09|0.02|A|F|1993-08-23|1993-09-22|1993-09-11|COLLECT COD|REG AIR| idle ideas across \n5665|45118|127|4|47|49966.17|0.01|0.01|A|F|1993-10-06|1993-09-19|1993-11-01|NONE|RAIL|s mold fluffily. final deposits along the\n5666|121226|8763|1|7|8730.54|0.09|0.08|R|F|1994-05-10|1994-04-06|1994-05-21|NONE|FOB| ideas. regular packag\n5666|35783|790|2|14|24062.92|0.08|0.01|A|F|1994-02-27|1994-04-11|1994-03-06|DELIVER IN PERSON|TRUCK|lar deposits nag against the slyly final d\n5666|192178|7217|3|39|49536.63|0.00|0.01|A|F|1994-05-13|1994-04-02|1994-06-12|DELIVER IN PERSON|TRUCK|the even, final foxes. quickly iron\n5666|130643|5670|4|24|40167.36|0.07|0.01|R|F|1994-02-14|1994-03-09|1994-03-06|DELIVER IN PERSON|FOB|on the carefully pending asympto\n5666|108483|8484|5|36|53693.28|0.07|0.07|R|F|1994-03-15|1994-03-16|1994-03-18|COLLECT COD|TRUCK|accounts. furiousl\n5667|144431|1974|1|37|54590.91|0.09|0.06|N|O|1995-09-24|1995-09-17|1995-10-03|NONE|REG AIR|s cajole blit\n5668|3645|8646|1|15|23229.60|0.03|0.04|A|F|1995-04-06|1995-05-12|1995-04-17|COLLECT COD|FOB| the express, pending requests. bo\n5669|190847|848|1|7|13564.88|0.06|0.06|N|O|1996-06-19|1996-07-07|1996-07-11|COLLECT COD|SHIP|yly regular requests lose blithely. careful\n5669|155814|3360|2|2|3739.62|0.06|0.07|N|O|1996-08-04|1996-06-15|1996-08-20|NONE|SHIP| blithely excuses. slyly\n5669|157654|7655|3|40|68466.00|0.00|0.02|N|O|1996-08-30|1996-06-15|1996-09-07|TAKE BACK RETURN|FOB|ar accounts alongside of the final, p\n5669|89245|9246|4|31|38261.44|0.04|0.05|N|O|1996-08-05|1996-06-10|1996-08-29|COLLECT COD|AIR|to beans against the regular depo\n5669|139680|7220|5|30|51590.40|0.07|0.01|N|O|1996-07-14|1996-07-28|1996-08-10|TAKE BACK RETURN|TRUCK|l accounts. care\n5670|89569|9570|1|27|42081.12|0.10|0.06|R|F|1993-05-09|1993-05-30|1993-06-06|TAKE BACK RETURN|REG AIR| ideas promise bli\n5670|185257|7776|2|43|57716.75|0.06|0.00|A|F|1993-07-09|1993-06-03|1993-07-14|DELIVER IN PERSON|FOB|ests in place of the carefully sly depos\n5670|6482|6483|3|24|33323.52|0.09|0.04|A|F|1993-07-17|1993-07-01|1993-08-03|NONE|AIR|press, express requests haggle\n5670|141383|3898|4|11|15668.18|0.06|0.06|R|F|1993-07-11|1993-06-26|1993-07-24|DELIVER IN PERSON|MAIL|etect furiously among the even pin\n5671|119482|4505|1|25|37537.00|0.00|0.08|N|O|1998-04-17|1998-03-28|1998-05-06|DELIVER IN PERSON|AIR|cording to the quickly final requests-- \n5671|128838|3863|2|46|85874.18|0.05|0.08|N|O|1998-03-28|1998-04-22|1998-04-19|TAKE BACK RETURN|MAIL|lar pinto beans detect care\n5671|171340|8892|3|13|18347.42|0.10|0.06|N|O|1998-03-02|1998-04-03|1998-03-08|TAKE BACK RETURN|TRUCK|bold theodolites about\n5671|110212|7746|4|42|51332.82|0.00|0.07|N|O|1998-02-17|1998-04-24|1998-03-17|TAKE BACK RETURN|SHIP|carefully slyly special deposit\n5671|128097|610|5|13|14626.17|0.09|0.00|N|O|1998-04-24|1998-03-26|1998-04-27|NONE|REG AIR|ers according to the ironic, unusual excu\n5671|113340|8363|6|30|40600.20|0.09|0.07|N|O|1998-06-06|1998-04-15|1998-07-01|DELIVER IN PERSON|TRUCK|fily ironi\n5696|136316|3856|1|28|37864.68|0.03|0.06|N|O|1995-07-03|1995-06-14|1995-07-27|COLLECT COD|REG AIR| the fluffily brave pearls \n5696|58914|6430|2|46|86153.86|0.01|0.00|N|O|1995-08-10|1995-07-08|1995-08-25|COLLECT COD|AIR|ter the instruct\n5696|166282|3831|3|42|56627.76|0.04|0.01|N|F|1995-06-06|1995-06-11|1995-06-19|TAKE BACK RETURN|SHIP|te furious\n5696|97646|156|4|20|32872.80|0.08|0.00|N|O|1995-06-25|1995-07-18|1995-07-16|NONE|TRUCK|silent, pending ideas sleep fluffil\n5696|123505|6018|5|19|29041.50|0.07|0.05|N|O|1995-08-31|1995-06-13|1995-09-10|COLLECT COD|SHIP|unusual requests sleep furiously ru\n5696|131928|9468|6|37|72517.04|0.04|0.05|N|O|1995-07-21|1995-06-23|1995-08-19|NONE|RAIL| carefully expres\n5696|101569|9100|7|6|9423.36|0.07|0.05|N|O|1995-08-03|1995-07-15|1995-09-01|DELIVER IN PERSON|REG AIR|n patterns lose slyly fina\n5697|54193|1709|1|24|27532.56|0.10|0.07|R|F|1992-10-27|1992-11-28|1992-11-20|NONE|RAIL|uffily iro\n5697|15283|7785|2|43|51526.04|0.06|0.02|R|F|1992-12-08|1992-12-03|1992-12-17|TAKE BACK RETURN|FOB|blithely reg\n5697|55460|2976|3|42|59449.32|0.03|0.01|A|F|1992-12-19|1992-12-08|1993-01-03|COLLECT COD|TRUCK|inal theodolites cajole after the bli\n5698|10470|5473|1|30|41414.10|0.01|0.05|A|F|1994-05-26|1994-08-16|1994-06-19|COLLECT COD|AIR|its. quickly regular foxes aro\n5698|162073|2074|2|25|28376.75|0.08|0.07|R|F|1994-08-06|1994-06-21|1994-08-25|NONE|SHIP| asymptotes sleep slyly above the\n5698|154975|7491|3|45|91348.65|0.03|0.01|A|F|1994-06-23|1994-08-13|1994-07-02|NONE|FOB|ng excuses. slyly express asymptotes\n5698|57104|9610|4|15|15916.50|0.07|0.08|R|F|1994-06-29|1994-07-03|1994-07-02|COLLECT COD|REG AIR|ly ironic frets haggle carefully \n5698|139406|9407|5|37|53479.80|0.06|0.06|A|F|1994-06-30|1994-06-23|1994-07-22|TAKE BACK RETURN|SHIP|ts. even, ironic \n5698|187630|149|6|1|1717.63|0.06|0.04|R|F|1994-05-31|1994-07-10|1994-06-03|DELIVER IN PERSON|MAIL|nts. slyly quiet pinto beans nag carefu\n5699|1884|6885|1|24|42861.12|0.01|0.07|A|F|1992-10-21|1992-09-04|1992-11-04|COLLECT COD|AIR|kages. fin\n5699|54419|9430|2|26|35708.66|0.06|0.06|R|F|1992-08-11|1992-09-21|1992-08-14|COLLECT COD|MAIL|y final deposits wake fluffily u\n5699|17473|9975|3|48|66742.56|0.10|0.05|R|F|1992-11-23|1992-10-20|1992-11-29|DELIVER IN PERSON|TRUCK|s. carefully regul\n5699|54313|6819|4|46|58296.26|0.08|0.02|A|F|1992-11-28|1992-09-23|1992-12-27|TAKE BACK RETURN|FOB|o the slyly\n5699|27286|2291|5|21|25478.88|0.02|0.02|A|F|1992-10-13|1992-09-30|1992-10-19|NONE|MAIL|lyly final pla\n5699|190036|7594|6|30|33780.90|0.08|0.05|R|F|1992-11-13|1992-10-01|1992-12-11|DELIVER IN PERSON|AIR| the carefully final \n5699|128219|3244|7|45|56124.45|0.09|0.06|A|F|1992-09-23|1992-10-22|1992-10-04|DELIVER IN PERSON|SHIP|rmanent packages sleep across the f\n5700|167890|2923|1|24|46989.36|0.09|0.00|N|O|1997-12-26|1998-01-28|1998-01-18|DELIVER IN PERSON|REG AIR|ix carefully \n5700|122421|4934|2|30|43302.60|0.00|0.06|N|O|1998-04-19|1998-03-13|1998-04-27|COLLECT COD|MAIL|ly blithely final instructions. fl\n5700|125320|345|3|23|30942.36|0.03|0.05|N|O|1998-01-30|1998-01-31|1998-01-31|NONE|REG AIR| wake quickly carefully fluffy hockey\n5701|53487|5993|1|17|24488.16|0.02|0.05|N|O|1997-03-27|1997-04-08|1997-04-21|DELIVER IN PERSON|RAIL|tes. quickly final a\n5702|76997|9505|1|44|86855.56|0.06|0.02|R|F|1994-01-04|1993-11-25|1994-01-22|NONE|RAIL|lites. carefully final requests doze b\n5702|85532|5533|2|37|56148.61|0.10|0.05|R|F|1993-12-14|1993-10-21|1994-01-08|NONE|FOB|ix slyly. regular instructions slee\n5702|130640|8180|3|44|73508.16|0.00|0.02|R|F|1993-11-28|1993-12-02|1993-12-22|NONE|TRUCK|ake according to th\n5702|62204|9723|4|31|36152.20|0.00|0.04|A|F|1994-01-04|1993-10-22|1994-01-26|DELIVER IN PERSON|TRUCK|pinto beans. blithely \n5703|87698|207|1|2|3371.38|0.09|0.01|R|F|1993-05-29|1993-07-26|1993-06-05|TAKE BACK RETURN|REG AIR|nts against the blithely sile\n5728|43599|6104|1|47|72501.73|0.10|0.05|A|F|1994-12-13|1995-01-25|1994-12-25|TAKE BACK RETURN|MAIL|nd the bravely final deposits. final ideas\n5728|158786|6332|2|40|73791.20|0.05|0.08|A|F|1995-03-28|1995-01-17|1995-04-14|TAKE BACK RETURN|SHIP|final deposits. theodolite\n5729|142388|2389|1|5|7151.90|0.07|0.00|R|F|1994-11-27|1994-11-11|1994-12-23|TAKE BACK RETURN|MAIL|s. even sheaves nag courts. \n5729|106136|8647|2|39|44543.07|0.10|0.00|A|F|1995-01-22|1994-11-21|1995-02-13|TAKE BACK RETURN|MAIL|. special pl\n5729|11181|1182|3|50|54609.00|0.00|0.05|R|F|1994-12-09|1994-12-31|1994-12-24|TAKE BACK RETURN|AIR|ly special sentiments. car\n5730|150009|10|1|2|2118.00|0.08|0.00|N|O|1998-02-24|1998-03-15|1998-03-11|COLLECT COD|SHIP|ely ironic foxes. carefu\n5730|199690|9691|2|9|16107.21|0.10|0.01|N|O|1998-03-05|1998-02-02|1998-03-28|DELIVER IN PERSON|MAIL|s lose blithely. specia\n5731|191796|9354|1|13|24541.27|0.02|0.04|N|O|1997-07-30|1997-06-23|1997-08-13|COLLECT COD|RAIL|ngside of the quickly regular depos\n5731|104178|4179|2|11|13003.87|0.00|0.08|N|O|1997-06-06|1997-07-08|1997-06-25|NONE|MAIL| furiously final accounts wake. d\n5731|110812|813|3|6|10936.86|0.01|0.04|N|O|1997-07-02|1997-07-01|1997-07-08|COLLECT COD|SHIP|sits integrate slyly close platelets. quick\n5731|13471|8474|4|6|8306.82|0.03|0.06|N|O|1997-09-07|1997-06-20|1997-09-20|TAKE BACK RETURN|RAIL|rs. quickly regular theo\n5731|194960|4961|5|19|39044.24|0.08|0.02|N|O|1997-06-29|1997-06-27|1997-07-15|NONE|REG AIR|ly unusual ideas above the \n5732|138287|5827|1|26|34457.28|0.02|0.07|N|O|1997-08-18|1997-10-25|1997-09-12|TAKE BACK RETURN|TRUCK|totes cajole according to the theodolites.\n5733|32868|2869|1|39|70233.54|0.01|0.07|A|F|1993-03-22|1993-05-24|1993-04-04|DELIVER IN PERSON|FOB|side of the\n5734|182945|5464|1|29|58810.26|0.05|0.01|N|O|1997-12-01|1997-12-08|1997-12-23|NONE|RAIL|structions cajole final, express \n5734|149299|4328|2|6|8089.74|0.07|0.00|N|O|1997-10-27|1997-12-19|1997-11-02|COLLECT COD|RAIL|s. regular platelets cajole furiously. regu\n5734|66727|6728|3|10|16937.20|0.01|0.03|N|O|1997-12-28|1997-12-24|1998-01-24|DELIVER IN PERSON|TRUCK|equests; accounts above\n5735|59754|9755|1|41|70263.75|0.01|0.01|R|F|1994-12-23|1995-02-10|1995-01-22|COLLECT COD|MAIL|lthily ruthless i\n5760|667|8168|1|6|9405.96|0.09|0.03|R|F|1994-07-30|1994-07-31|1994-08-16|COLLECT COD|REG AIR|ng the acco\n5760|5757|758|2|24|39906.00|0.04|0.05|A|F|1994-07-15|1994-07-04|1994-08-08|NONE|MAIL|s. bravely ironic accounts among\n5760|147375|9890|3|8|11378.96|0.07|0.04|A|F|1994-09-06|1994-08-03|1994-10-06|NONE|AIR|l accounts among the carefully even de\n5760|122293|2294|4|19|24990.51|0.10|0.01|R|F|1994-08-02|1994-08-02|1994-08-15|COLLECT COD|SHIP|sits nag. even, regular ideas cajole b\n5760|165638|3187|5|6|10221.78|0.03|0.07|R|F|1994-06-09|1994-07-06|1994-06-16|DELIVER IN PERSON|MAIL| shall have to cajole along the \n5761|46273|3786|1|41|49990.07|0.08|0.00|N|O|1998-07-31|1998-08-09|1998-08-08|TAKE BACK RETURN|TRUCK|pecial deposits. qu\n5761|107289|7290|2|36|46666.08|0.00|0.07|N|O|1998-09-07|1998-09-21|1998-09-11|TAKE BACK RETURN|TRUCK| pinto beans thrash alongside of the pendi\n5761|197395|4953|3|49|73127.11|0.04|0.08|N|O|1998-07-14|1998-08-20|1998-07-25|NONE|SHIP|ly bold accounts wake above the\n5762|174993|4994|1|6|12407.94|0.05|0.02|N|O|1997-04-07|1997-03-25|1997-05-02|NONE|AIR|ironic dependencies doze carefu\n5762|101749|9280|2|27|47269.98|0.02|0.08|N|O|1997-02-21|1997-05-08|1997-03-23|NONE|REG AIR|across the bold ideas. carefully sp\n5762|88317|8318|3|40|52212.40|0.00|0.08|N|O|1997-04-30|1997-05-09|1997-05-08|COLLECT COD|SHIP|al instructions. furiousl\n5762|132375|2376|4|47|66146.39|0.05|0.06|N|O|1997-03-02|1997-03-23|1997-03-19|NONE|RAIL|equests sleep after the furiously ironic pa\n5762|24527|4528|5|28|40642.56|0.02|0.06|N|O|1997-02-22|1997-03-25|1997-02-24|TAKE BACK RETURN|SHIP|ic foxes among the blithely qui\n5762|11673|4175|6|12|19016.04|0.00|0.06|N|O|1997-04-18|1997-04-27|1997-05-11|DELIVER IN PERSON|REG AIR|ages are abo\n5763|130799|5826|1|32|58553.28|0.02|0.06|N|O|1998-07-16|1998-09-13|1998-08-02|DELIVER IN PERSON|FOB|ding instruct\n5763|135695|8209|2|23|39805.87|0.09|0.04|N|O|1998-07-25|1998-09-21|1998-08-15|DELIVER IN PERSON|SHIP|re after the blithel\n5763|12360|9864|3|25|31809.00|0.01|0.02|N|O|1998-10-04|1998-08-16|1998-10-09|DELIVER IN PERSON|REG AIR|inal theodolites. even re\n5763|120969|3482|4|47|93528.12|0.09|0.00|N|O|1998-08-22|1998-09-22|1998-09-04|NONE|REG AIR|gle slyly. slyly final re\n5763|122996|2997|5|8|16151.92|0.06|0.05|N|O|1998-09-23|1998-09-15|1998-09-27|DELIVER IN PERSON|TRUCK|foxes wake slyly. car\n5763|189104|9105|6|9|10737.90|0.08|0.02|N|O|1998-09-24|1998-09-01|1998-10-02|NONE|AIR| deposits. instru\n5764|100625|626|1|28|45517.36|0.04|0.04|A|F|1993-12-07|1993-12-20|1993-12-26|TAKE BACK RETURN|RAIL|sleep furi\n5764|199143|4182|2|20|24842.80|0.10|0.05|A|F|1993-10-17|1993-12-24|1993-10-18|TAKE BACK RETURN|FOB|ng to the fluffily qu\n5764|187173|4728|3|4|5040.68|0.03|0.05|A|F|1993-10-25|1993-12-23|1993-11-06|DELIVER IN PERSON|AIR|ily regular courts haggle\n5765|161940|9489|1|31|62060.14|0.00|0.06|A|F|1995-01-11|1995-02-13|1995-01-23|TAKE BACK RETURN|AIR|r foxes. ev\n5765|123802|6315|2|29|52948.20|0.07|0.08|A|F|1994-12-29|1995-02-01|1995-01-26|NONE|RAIL|nic requests. deposits wake quickly among \n5765|138309|3336|3|31|41766.30|0.05|0.01|R|F|1995-03-01|1995-01-23|1995-03-31|TAKE BACK RETURN|REG AIR|the furiou\n5765|151733|9279|4|46|82097.58|0.07|0.07|R|F|1995-03-13|1995-02-12|1995-03-20|DELIVER IN PERSON|MAIL|ccounts sleep about th\n5765|173421|8456|5|48|71732.16|0.09|0.02|A|F|1995-03-30|1995-01-14|1995-04-09|DELIVER IN PERSON|SHIP|theodolites integrate furiously\n5765|82052|7069|6|41|42396.05|0.04|0.00|A|F|1994-12-31|1995-02-11|1995-01-17|TAKE BACK RETURN|SHIP| furiously. slyly sile\n5765|41209|6218|7|21|24154.20|0.05|0.04|R|F|1995-04-05|1995-02-12|1995-05-05|COLLECT COD|TRUCK|ole furiously. quick, special dependencies \n5766|187050|9569|1|1|1137.05|0.10|0.01|R|F|1994-01-16|1993-11-16|1994-01-23|NONE|MAIL|blithely regular the\n5766|148529|6072|2|39|61523.28|0.02|0.07|A|F|1993-10-24|1993-12-07|1993-11-08|DELIVER IN PERSON|SHIP| furiously unusual courts. slyly final pear\n5766|117751|5285|3|4|7075.00|0.08|0.08|R|F|1993-11-10|1993-10-30|1993-12-01|COLLECT COD|TRUCK|ly even requests. furiou\n5767|166864|6865|1|11|21239.46|0.08|0.01|A|F|1992-06-02|1992-05-30|1992-06-08|NONE|TRUCK|instructions. carefully final accou\n5767|68555|1062|2|15|22853.25|0.07|0.05|R|F|1992-06-05|1992-07-28|1992-06-08|DELIVER IN PERSON|MAIL|warthogs. carefully unusual g\n5767|190490|3010|3|42|66380.58|0.06|0.01|R|F|1992-07-31|1992-06-09|1992-08-09|COLLECT COD|TRUCK| blithe deposi\n5767|152475|2476|4|34|51933.98|0.06|0.01|R|F|1992-06-02|1992-06-23|1992-06-17|NONE|FOB|sits among the\n5767|45270|5271|5|36|43749.72|0.03|0.00|A|F|1992-07-17|1992-06-10|1992-07-19|COLLECT COD|AIR|ake carefully. packages \n5792|177019|9537|1|34|37264.34|0.08|0.07|R|F|1993-05-23|1993-06-25|1993-06-12|NONE|RAIL|requests are against t\n5792|156410|8926|2|47|68921.27|0.10|0.00|A|F|1993-06-08|1993-05-10|1993-06-26|COLLECT COD|AIR|regular, ironic excuses n\n5792|182806|2807|3|32|60441.60|0.05|0.08|R|F|1993-06-26|1993-05-23|1993-07-07|COLLECT COD|RAIL|s are slyly against the ev\n5792|13894|6396|4|14|25310.46|0.09|0.02|A|F|1993-07-28|1993-06-17|1993-08-27|DELIVER IN PERSON|RAIL|olites print carefully\n5792|101362|8893|5|31|42264.16|0.02|0.01|A|F|1993-06-17|1993-05-05|1993-07-01|COLLECT COD|TRUCK|s? furiously even instructions \n5793|52084|9600|1|20|20721.60|0.05|0.03|N|O|1997-10-05|1997-09-04|1997-10-30|COLLECT COD|AIR|e carefully ex\n5793|169992|7541|2|41|84541.59|0.06|0.06|N|O|1997-08-04|1997-10-10|1997-08-12|DELIVER IN PERSON|TRUCK|snooze quick\n5793|42777|2778|3|8|13758.16|0.07|0.03|N|O|1997-08-16|1997-09-08|1997-08-28|COLLECT COD|AIR|al foxes l\n5793|147676|5219|4|48|82736.16|0.02|0.02|N|O|1997-09-27|1997-08-23|1997-10-27|DELIVER IN PERSON|REG AIR|quickly enticing excuses use slyly abov\n5794|157144|7145|1|42|50447.88|0.06|0.05|R|F|1993-06-29|1993-05-30|1993-07-28|COLLECT COD|REG AIR|he careful\n5794|114786|9809|2|14|25210.92|0.09|0.02|R|F|1993-04-19|1993-07-02|1993-05-18|COLLECT COD|SHIP|uriously carefully ironic reque\n5794|6827|6828|3|15|26007.30|0.09|0.06|R|F|1993-06-25|1993-06-27|1993-07-09|NONE|MAIL|blithely regular ideas. final foxes haggle \n5794|136244|8758|4|47|60171.28|0.00|0.08|A|F|1993-07-16|1993-06-21|1993-08-05|TAKE BACK RETURN|REG AIR|quests. blithely final excu\n5795|192991|8030|1|34|70855.66|0.09|0.05|A|F|1992-08-21|1992-07-30|1992-08-27|COLLECT COD|REG AIR|al instructions must affix along the ironic\n5796|57971|2982|1|27|52082.19|0.10|0.00|N|O|1996-04-06|1996-02-29|1996-04-20|DELIVER IN PERSON|RAIL|s wake quickly aro\n5797|60967|8486|1|17|32775.32|0.09|0.03|N|O|1997-12-13|1998-01-12|1997-12-23|NONE|REG AIR|the ironic, even theodoli\n5798|126015|6016|1|2|2082.02|0.09|0.00|N|O|1998-05-25|1998-06-22|1998-06-09|COLLECT COD|FOB|e furiously across \n5798|123814|6327|2|14|25729.34|0.06|0.05|N|O|1998-04-01|1998-06-14|1998-04-27|NONE|RAIL|he special, bold packages. carefully iron\n5798|133608|8635|3|22|36115.20|0.02|0.01|N|O|1998-06-24|1998-06-06|1998-07-20|COLLECT COD|TRUCK|sits poach carefully\n5798|145699|8214|4|40|69787.60|0.08|0.06|N|O|1998-07-09|1998-06-24|1998-07-16|NONE|TRUCK| integrate carefu\n5798|148631|6174|5|7|11757.41|0.06|0.07|N|O|1998-06-06|1998-05-10|1998-06-07|NONE|SHIP|ts against the blithely final p\n5798|37444|4954|6|9|12432.96|0.06|0.02|N|O|1998-05-05|1998-05-25|1998-05-09|DELIVER IN PERSON|REG AIR|e blithely\n5798|114851|7363|7|32|59707.20|0.08|0.01|N|O|1998-04-27|1998-05-03|1998-05-08|TAKE BACK RETURN|REG AIR|ubt blithely above the \n5799|94586|4587|1|41|64803.78|0.04|0.02|N|O|1995-11-13|1995-10-31|1995-11-16|COLLECT COD|TRUCK|al accounts sleep ruthlessl\n5799|99882|4901|2|30|56456.40|0.03|0.08|N|O|1995-09-12|1995-09-13|1995-09-19|NONE|RAIL| furiously s\n5824|76111|8619|1|40|43484.40|0.06|0.06|N|O|1997-01-14|1997-01-17|1997-02-02|NONE|REG AIR|he final packag\n5824|181001|6038|2|42|45444.00|0.09|0.00|N|O|1997-02-01|1997-02-20|1997-02-07|COLLECT COD|SHIP|ts sleep. carefully regular accounts h\n5824|72131|9653|3|16|17650.08|0.03|0.02|N|O|1997-02-13|1997-01-07|1997-02-17|TAKE BACK RETURN|TRUCK|sly express Ti\n5824|91100|6119|4|32|34915.20|0.03|0.02|N|O|1997-02-16|1997-01-24|1997-02-20|DELIVER IN PERSON|RAIL|ven requests. \n5824|107000|7001|5|44|44308.00|0.08|0.03|N|O|1997-01-24|1997-01-31|1997-02-11|COLLECT COD|TRUCK|fily fluffily bold\n5825|158214|730|1|23|29260.83|0.10|0.05|R|F|1995-05-10|1995-04-28|1995-05-13|DELIVER IN PERSON|TRUCK| special pinto beans. dependencies haggl\n5826|143968|6483|1|4|8047.84|0.03|0.06|N|O|1998-07-31|1998-09-10|1998-08-27|NONE|AIR| packages across the fluffily spec\n5826|63777|3778|2|18|31333.86|0.04|0.01|N|O|1998-07-17|1998-09-03|1998-07-22|NONE|SHIP|atelets use above t\n5827|186619|6620|1|30|51168.30|0.03|0.05|N|O|1998-11-11|1998-09-27|1998-11-30|DELIVER IN PERSON|RAIL|ounts may c\n5827|102134|4645|2|23|26130.99|0.09|0.05|N|O|1998-11-16|1998-09-14|1998-11-17|COLLECT COD|RAIL|ans. furiously special instruct\n5827|163706|8739|3|3|5309.10|0.03|0.06|N|O|1998-10-17|1998-09-29|1998-10-28|DELIVER IN PERSON|MAIL|uses eat along the furiously\n5827|199067|9068|4|26|30317.56|0.06|0.00|N|O|1998-07-29|1998-09-24|1998-07-30|DELIVER IN PERSON|SHIP|arefully special packages wake thin\n5827|111610|6633|5|38|61621.18|0.03|0.06|N|O|1998-10-18|1998-08-27|1998-10-23|TAKE BACK RETURN|TRUCK|ly ruthless accounts\n5827|16474|1477|6|14|19466.58|0.05|0.01|N|O|1998-08-31|1998-09-06|1998-09-13|TAKE BACK RETURN|RAIL|rges. fluffily pending \n5828|1343|8844|1|28|34841.52|0.10|0.03|A|F|1994-05-15|1994-05-20|1994-06-08|DELIVER IN PERSON|MAIL| special ideas haggle slyly ac\n5828|157303|2334|2|37|50331.10|0.01|0.00|R|F|1994-06-07|1994-05-30|1994-06-17|NONE|RAIL|e carefully spec\n5829|39657|4664|1|4|6386.60|0.01|0.02|N|O|1997-03-01|1997-02-17|1997-03-22|NONE|TRUCK|ithely; accounts cajole ideas. regular foxe\n5829|106523|9034|2|40|61180.80|0.04|0.01|N|O|1997-04-21|1997-02-12|1997-05-04|COLLECT COD|TRUCK| the carefully ironic accounts. a\n5829|128238|3263|3|6|7597.38|0.05|0.06|N|O|1997-01-22|1997-03-12|1997-02-02|TAKE BACK RETURN|AIR|sts. slyly special fo\n5829|89153|4170|4|42|47970.30|0.02|0.07|N|O|1997-03-26|1997-04-01|1997-03-30|COLLECT COD|REG AIR|pearls. slyly bold deposits solve final\n5829|190779|8337|5|49|91618.73|0.05|0.01|N|O|1997-01-31|1997-03-13|1997-02-18|NONE|MAIL| ironic excuses use fluf\n5829|17069|2072|6|17|16763.02|0.09|0.02|N|O|1997-04-10|1997-03-29|1997-04-22|COLLECT COD|AIR|after the furiously ironic ideas no\n5829|77942|7943|7|27|51838.38|0.08|0.04|N|O|1997-02-25|1997-03-31|1997-03-03|DELIVER IN PERSON|AIR|ns about the excuses are c\n5830|159261|6807|1|29|38287.54|0.10|0.02|R|F|1993-06-19|1993-05-10|1993-07-13|DELIVER IN PERSON|REG AIR|y bold excuses\n5831|190330|331|1|2|2840.66|0.10|0.01|N|O|1997-02-09|1997-01-20|1997-03-07|TAKE BACK RETURN|TRUCK|quickly silent req\n5831|73475|8490|2|33|47799.51|0.04|0.03|N|O|1996-11-20|1997-01-18|1996-12-18|TAKE BACK RETURN|MAIL| instructions wake. slyly sil\n5831|81622|4131|3|6|9621.72|0.05|0.07|N|O|1997-01-29|1997-01-14|1997-02-09|NONE|MAIL|ly ironic accounts nag pendin\n5831|12962|7965|4|46|86248.16|0.06|0.02|N|O|1997-02-24|1997-01-18|1997-03-02|COLLECT COD|MAIL|ly final pa\n5831|42828|2829|5|37|65520.34|0.05|0.01|N|O|1997-01-17|1997-02-08|1997-02-01|NONE|FOB|uriously even requests\n5856|3680|1181|1|1|1583.68|0.03|0.02|A|F|1994-12-29|1995-01-07|1995-01-10|TAKE BACK RETURN|MAIL|tly. special deposits wake blithely even\n5856|34305|9312|2|35|43375.50|0.09|0.02|R|F|1994-11-24|1994-12-23|1994-11-30|COLLECT COD|AIR|excuses. finally ir\n5856|152739|2740|3|39|69877.47|0.05|0.03|A|F|1995-01-18|1995-01-11|1995-01-19|DELIVER IN PERSON|TRUCK|uickly quickly fluffy in\n5857|57226|7227|1|25|29580.50|0.03|0.02|N|O|1997-12-02|1997-12-17|1997-12-08|DELIVER IN PERSON|REG AIR|ding platelets. pending excu\n5857|194769|2327|2|50|93188.00|0.06|0.07|N|O|1997-12-04|1997-12-16|1997-12-20|NONE|TRUCK|y regular d\n5857|67860|5379|3|1|1827.86|0.03|0.01|N|O|1998-02-01|1997-12-09|1998-02-20|TAKE BACK RETURN|SHIP|instructions detect final reques\n5857|117998|510|4|12|24191.88|0.03|0.08|N|O|1998-01-24|1997-12-27|1998-02-10|TAKE BACK RETURN|AIR|counts. express, final\n5857|191260|3780|5|14|18917.64|0.07|0.07|N|O|1997-12-10|1998-01-06|1998-01-04|TAKE BACK RETURN|TRUCK|ffily pendin\n5857|92862|5372|6|49|90888.14|0.00|0.04|N|O|1998-01-23|1997-12-12|1998-01-28|DELIVER IN PERSON|REG AIR|egular pinto beans\n5858|120832|8369|1|20|37056.60|0.02|0.06|A|F|1992-07-23|1992-08-26|1992-07-24|COLLECT COD|SHIP|uffily unusual pinto beans sleep\n5858|15003|5004|2|36|33048.00|0.00|0.05|A|F|1992-09-25|1992-08-16|1992-10-11|NONE|SHIP|osits wake quickly quickly sile\n5858|147996|511|3|7|14307.93|0.08|0.02|A|F|1992-10-07|1992-08-16|1992-10-15|TAKE BACK RETURN|REG AIR|. doggedly regular packages use pendin\n5858|163490|1039|4|46|71460.54|0.07|0.06|R|F|1992-09-07|1992-10-06|1992-10-06|DELIVER IN PERSON|MAIL|posits withi\n5858|160181|5214|5|18|22341.24|0.00|0.07|A|F|1992-11-05|1992-10-08|1992-12-03|NONE|TRUCK|al excuses. bold\n5858|153937|8968|6|7|13936.51|0.04|0.00|A|F|1992-09-14|1992-10-01|1992-10-01|TAKE BACK RETURN|RAIL|dly pending ac\n5858|10709|3211|7|50|80985.00|0.06|0.00|R|F|1992-07-20|1992-10-07|1992-07-25|NONE|TRUCK|r the ironic ex\n5859|174988|23|1|50|103149.00|0.07|0.01|N|O|1997-07-08|1997-06-20|1997-07-27|COLLECT COD|MAIL|ly regular deposits use. ironic\n5859|8773|6274|2|17|28590.09|0.03|0.03|N|O|1997-05-15|1997-06-30|1997-05-26|DELIVER IN PERSON|AIR|ly ironic requests. quickly unusual pin\n5859|45701|8206|3|33|54341.10|0.10|0.04|N|O|1997-07-08|1997-06-22|1997-07-18|TAKE BACK RETURN|TRUCK|eposits unwind furiously final pinto bea\n5859|92269|2270|4|40|50450.40|0.09|0.02|N|O|1997-08-05|1997-06-17|1997-08-20|NONE|REG AIR|l dependenci\n5859|152689|7720|5|35|60958.80|0.00|0.08|N|O|1997-05-28|1997-07-14|1997-06-15|COLLECT COD|TRUCK|egular acco\n5859|43446|3447|6|9|12504.96|0.01|0.02|N|O|1997-06-15|1997-06-06|1997-06-20|NONE|RAIL|ges boost quickly. blithely r\n5859|190127|7685|7|27|32862.24|0.05|0.08|N|O|1997-07-30|1997-07-08|1997-08-08|NONE|MAIL| across th\n5860|50168|7684|1|10|11181.60|0.04|0.04|A|F|1992-03-11|1992-03-30|1992-03-31|NONE|MAIL|ual patterns try to eat carefully above\n5861|190867|8425|1|32|62651.52|0.00|0.03|N|O|1997-05-27|1997-05-29|1997-05-28|TAKE BACK RETURN|MAIL|nt asymptotes. carefully express request\n5861|85611|628|2|6|9579.66|0.10|0.03|N|O|1997-07-28|1997-05-18|1997-08-24|TAKE BACK RETURN|TRUCK|olites. slyly\n5862|112061|4573|1|4|4292.24|0.09|0.06|N|O|1997-06-04|1997-04-26|1997-06-19|NONE|TRUCK|yly silent deposit\n5862|1334|6335|2|29|35824.57|0.03|0.05|N|O|1997-04-02|1997-04-16|1997-04-04|NONE|FOB|e fluffily. furiously\n5863|160562|3079|1|45|73015.20|0.07|0.06|A|F|1993-12-19|1994-01-25|1994-01-05|NONE|REG AIR| deposits are ab\n5863|159369|1885|2|21|29995.56|0.09|0.03|R|F|1994-01-13|1994-01-09|1994-01-28|DELIVER IN PERSON|FOB|atelets nag blithely furi\n5888|61291|8810|1|46|57605.34|0.02|0.00|N|O|1996-11-18|1996-11-05|1996-12-08|TAKE BACK RETURN|FOB|yly final accounts hag\n5888|111595|1596|2|24|38558.16|0.03|0.01|N|O|1996-11-07|1996-11-30|1996-11-20|COLLECT COD|SHIP|ing to the spe\n5889|76129|8637|1|17|18787.04|0.09|0.02|N|O|1995-07-01|1995-08-12|1995-07-25|NONE|AIR|blithely pending packages. flu\n5890|112221|2222|1|38|46862.36|0.01|0.08|A|F|1993-02-14|1992-12-09|1993-02-27|COLLECT COD|FOB| accounts. carefully final asymptotes\n5891|84905|4906|1|22|41577.80|0.00|0.06|R|F|1993-01-01|1993-02-18|1993-01-14|DELIVER IN PERSON|TRUCK|iresias cajole deposits. special, ir\n5891|185580|8099|2|9|14990.22|0.03|0.07|R|F|1993-01-20|1993-02-27|1993-02-10|COLLECT COD|REG AIR|cajole carefully \n5891|29674|4679|3|10|16036.70|0.08|0.01|A|F|1993-04-14|1993-02-07|1993-04-15|DELIVER IN PERSON|RAIL|nding requests. b\n5892|147722|7723|1|7|12388.04|0.02|0.03|N|O|1995-06-26|1995-07-18|1995-07-25|COLLECT COD|AIR|e furiously. quickly even deposits da\n5892|149926|7469|2|37|73109.04|0.09|0.06|N|O|1995-08-12|1995-06-11|1995-09-05|NONE|REG AIR|maintain. bold, expre\n5892|2064|2065|3|28|27049.68|0.03|0.06|N|O|1995-08-16|1995-07-06|1995-08-22|DELIVER IN PERSON|MAIL|ithely unusual accounts will have to integ\n5892|74798|4799|4|23|40774.17|0.08|0.04|R|F|1995-05-18|1995-07-06|1995-05-29|COLLECT COD|MAIL| foxes nag slyly about the qui\n5893|133707|1247|1|43|74850.10|0.05|0.02|R|F|1992-11-02|1992-09-27|1992-11-21|TAKE BACK RETURN|RAIL|s. regular courts above the carefully silen\n5893|1868|9369|2|2|3539.72|0.10|0.04|R|F|1992-07-18|1992-09-10|1992-08-12|NONE|RAIL|ckages wake sly\n5894|7312|4813|1|23|28044.13|0.04|0.08|A|F|1994-09-05|1994-10-27|1994-09-13|NONE|TRUCK| furiously even deposits haggle alw\n5894|78446|3461|2|48|68373.12|0.04|0.08|A|F|1994-09-04|1994-11-03|1994-09-17|NONE|TRUCK| asymptotes among the blithely silent \n5895|14728|7230|1|38|62423.36|0.05|0.08|N|O|1997-04-05|1997-03-06|1997-05-03|DELIVER IN PERSON|RAIL|ts are furiously. regular, final excuses \n5895|121538|1539|2|47|73297.91|0.04|0.06|N|O|1997-04-27|1997-03-17|1997-05-07|DELIVER IN PERSON|AIR|r packages wake carefull\n5895|83712|1237|3|49|83089.79|0.03|0.07|N|O|1997-03-15|1997-02-17|1997-04-04|NONE|TRUCK|permanent foxes. packages\n5895|145714|5715|4|31|54551.01|0.03|0.01|N|O|1997-03-03|1997-03-30|1997-03-08|TAKE BACK RETURN|TRUCK| final deposits nod slyly careful\n5895|199172|9173|5|20|25423.40|0.07|0.00|N|O|1997-04-30|1997-02-07|1997-05-08|DELIVER IN PERSON|AIR|gular deposits wake blithely carefully fin\n5895|77733|2748|6|15|25660.95|0.08|0.08|N|O|1997-04-19|1997-03-09|1997-05-13|TAKE BACK RETURN|RAIL|silent package\n5920|186069|6070|1|50|57753.00|0.06|0.00|A|F|1995-03-13|1995-01-03|1995-03-31|TAKE BACK RETURN|RAIL|across the carefully pending platelets\n5920|57793|7794|2|24|42018.96|0.01|0.05|A|F|1994-12-28|1995-01-21|1994-12-31|DELIVER IN PERSON|FOB|fully regular dolphins. furiousl\n5920|116952|9464|3|2|3937.90|0.08|0.07|A|F|1995-02-18|1995-01-13|1995-03-04|NONE|SHIP| evenly spe\n5920|11323|8827|4|28|34560.96|0.06|0.02|R|F|1994-12-17|1995-02-13|1994-12-31|NONE|SHIP|le slyly slyly even deposits. f\n5920|99165|6693|5|42|48894.72|0.09|0.08|A|F|1994-12-18|1995-01-07|1995-01-14|COLLECT COD|AIR|lar, ironic dependencies sno\n5921|98016|5544|1|44|44616.44|0.07|0.01|R|F|1994-07-14|1994-06-30|1994-07-15|NONE|TRUCK|ain about the special\n5921|145670|699|2|25|42891.75|0.06|0.01|A|F|1994-05-19|1994-06-15|1994-06-17|COLLECT COD|TRUCK|nd the slyly regular deposits. quick\n5921|67177|2190|3|17|19450.89|0.06|0.01|R|F|1994-05-20|1994-05-26|1994-05-23|NONE|FOB|final asymptotes. even packages boost \n5921|27331|2336|4|26|32716.58|0.03|0.04|A|F|1994-05-03|1994-07-06|1994-05-06|NONE|AIR|hy dependenc\n5921|142246|4761|5|41|52817.84|0.04|0.02|R|F|1994-04-13|1994-05-31|1994-04-26|DELIVER IN PERSON|AIR|nusual, regular theodol\n5921|114421|4422|6|5|7177.10|0.02|0.00|R|F|1994-06-01|1994-05-07|1994-06-10|COLLECT COD|TRUCK|eas cajole across the final, fi\n5922|195631|3189|1|9|15539.67|0.07|0.00|N|O|1996-12-04|1997-01-20|1996-12-08|DELIVER IN PERSON|RAIL|haggle slyly even packages. packages\n5922|156595|1626|2|37|61108.83|0.01|0.04|N|O|1996-12-19|1996-12-16|1997-01-15|COLLECT COD|RAIL|s wake slyly. requests cajole furiously asy\n5922|89515|4532|3|35|52657.85|0.08|0.00|N|O|1996-12-12|1997-01-21|1997-01-01|DELIVER IN PERSON|SHIP|accounts. regu\n5922|65048|5049|4|13|13169.52|0.08|0.07|N|O|1997-03-08|1996-12-26|1997-04-03|DELIVER IN PERSON|FOB|sly special accounts wake ironically.\n5922|56428|8934|5|39|53992.38|0.04|0.07|N|O|1997-03-04|1997-01-17|1997-03-25|TAKE BACK RETURN|SHIP|e of the instructions. quick\n5922|178810|1328|6|10|18888.10|0.04|0.01|N|O|1997-02-23|1996-12-26|1997-03-04|NONE|REG AIR|sly regular deposits haggle quickly ins\n5923|176934|6935|1|27|54295.11|0.08|0.03|N|O|1997-08-16|1997-06-27|1997-08-29|DELIVER IN PERSON|RAIL|arefully i\n5923|118577|1089|2|42|67013.94|0.01|0.08|N|O|1997-09-16|1997-07-23|1997-09-27|COLLECT COD|REG AIR|y regular theodolites w\n5923|107597|5128|3|2|3209.18|0.06|0.05|N|O|1997-06-19|1997-07-31|1997-06-28|TAKE BACK RETURN|TRUCK|express patterns. even deposits\n5923|173158|5676|4|46|56632.90|0.05|0.04|N|O|1997-07-29|1997-07-23|1997-08-23|COLLECT COD|SHIP|nto beans cajole blithe\n5923|58535|3546|5|35|52273.55|0.04|0.05|N|O|1997-07-21|1997-07-11|1997-08-01|DELIVER IN PERSON|AIR|sts affix unusual, final requests. request\n5924|175145|180|1|38|46365.32|0.06|0.05|N|O|1995-12-17|1995-12-11|1996-01-06|TAKE BACK RETURN|AIR|ions cajole carefully along the \n5924|52349|4855|2|49|63765.66|0.04|0.00|N|O|1995-10-25|1995-12-11|1995-11-08|NONE|MAIL|inly final excuses. blithely regular requ\n5924|16705|6706|3|24|38920.80|0.09|0.08|N|O|1996-01-12|1995-12-13|1996-01-25|COLLECT COD|REG AIR| use carefully. special, e\n5925|86543|9052|1|42|64240.68|0.05|0.02|N|O|1996-03-05|1996-01-13|1996-03-10|COLLECT COD|SHIP|to the furiously\n5925|124966|9991|2|31|61719.76|0.03|0.03|N|O|1996-01-02|1995-12-14|1996-01-07|TAKE BACK RETURN|FOB|e slyly. furiously regular deposi\n5925|88288|3305|3|50|63814.00|0.03|0.04|N|O|1996-02-14|1996-01-10|1996-02-15|NONE|TRUCK|es. stealthily express pains print bli\n5925|53309|8320|4|30|37869.00|0.02|0.07|N|O|1996-02-21|1996-02-11|1996-03-10|NONE|TRUCK| the packa\n5925|159746|9747|5|41|74035.34|0.00|0.06|N|O|1996-02-03|1995-12-24|1996-02-20|NONE|SHIP| across the pending deposits nag caref\n5925|49812|7325|6|48|84566.88|0.02|0.00|N|O|1996-02-03|1996-01-19|1996-03-04|DELIVER IN PERSON|REG AIR| haggle after the fo\n5926|89216|4233|1|8|9641.68|0.02|0.00|R|F|1994-07-17|1994-07-20|1994-08-11|COLLECT COD|MAIL|gle furiously express foxes. bo\n5926|49773|7286|2|27|46514.79|0.09|0.05|A|F|1994-07-05|1994-08-11|1994-08-02|DELIVER IN PERSON|MAIL|ironic requests\n5926|126721|6722|3|46|80395.12|0.01|0.03|R|F|1994-09-05|1994-08-12|1994-09-11|COLLECT COD|RAIL|ts integrate. courts haggl\n5926|189693|7248|4|23|41001.87|0.01|0.02|A|F|1994-07-23|1994-08-10|1994-07-27|DELIVER IN PERSON|FOB|ickly special packages among \n5927|89541|2050|1|44|67343.76|0.04|0.05|N|O|1997-11-29|1997-11-21|1997-12-13|DELIVER IN PERSON|TRUCK|rding to the special, final decoy\n5927|114570|9593|2|8|12676.56|0.04|0.05|N|O|1997-09-24|1997-11-15|1997-10-22|TAKE BACK RETURN|SHIP|ilent dependencies nod c\n5927|166058|8575|3|32|35969.60|0.10|0.07|N|O|1997-12-26|1997-10-27|1997-12-31|COLLECT COD|AIR|telets. carefully bold accounts was\n5952|199810|2330|1|49|93580.69|0.10|0.02|N|O|1997-06-30|1997-07-10|1997-07-02|COLLECT COD|AIR|e furiously regular\n5952|190128|7686|2|11|13399.32|0.10|0.05|N|O|1997-05-13|1997-06-04|1997-05-27|DELIVER IN PERSON|FOB|y nag blithely aga\n5952|70338|339|3|43|56258.19|0.01|0.01|N|O|1997-06-29|1997-06-06|1997-07-15|COLLECT COD|MAIL|posits sleep furiously quickly final p\n5952|157819|2850|4|23|43166.63|0.00|0.07|N|O|1997-05-13|1997-06-27|1997-05-20|NONE|TRUCK|e blithely packages. eve\n5953|128103|8104|1|36|40719.60|0.03|0.00|R|F|1992-05-28|1992-06-24|1992-05-29|DELIVER IN PERSON|FOB| cajole furio\n5953|12029|4531|2|34|31994.68|0.03|0.04|A|F|1992-05-04|1992-06-12|1992-06-02|NONE|RAIL|hockey players use furiously against th\n5953|161015|6048|3|5|5380.05|0.07|0.06|A|F|1992-04-10|1992-04-27|1992-04-14|NONE|SHIP|s. blithely \n5953|168308|825|4|23|31654.90|0.09|0.02|R|F|1992-06-05|1992-06-03|1992-06-29|TAKE BACK RETURN|FOB|he silent ideas. silent foxes po\n5954|146706|4249|1|8|14021.60|0.03|0.00|A|F|1993-03-27|1993-01-22|1993-04-04|TAKE BACK RETURN|AIR|unusual th\n5954|80235|2744|2|40|48609.20|0.02|0.01|A|F|1992-12-30|1993-01-16|1993-01-09|COLLECT COD|RAIL|iously ironic deposits after\n5954|93387|915|3|20|27607.60|0.09|0.07|A|F|1992-12-25|1993-02-05|1992-12-31|COLLECT COD|REG AIR| accounts wake carefu\n5954|144672|2215|4|20|34333.40|0.00|0.01|R|F|1993-02-27|1993-01-04|1993-03-08|NONE|TRUCK|ke furiously blithely special packa\n5954|99780|7308|5|35|62292.30|0.04|0.06|A|F|1993-03-17|1993-02-06|1993-04-10|NONE|SHIP|tions maintain slyly. furious\n5954|192029|4549|6|39|43719.78|0.04|0.08|A|F|1993-02-27|1993-02-25|1993-03-29|DELIVER IN PERSON|REG AIR| always regular dolphins. furiously p\n5955|139375|4402|1|14|19801.18|0.08|0.08|N|O|1995-06-22|1995-05-23|1995-06-24|DELIVER IN PERSON|TRUCK| unusual, bold theodolit\n5955|61553|9072|2|15|22718.25|0.08|0.07|R|F|1995-04-22|1995-05-28|1995-04-27|NONE|FOB|y final accounts above the regu\n5955|111196|6219|3|40|48287.60|0.03|0.00|R|F|1995-04-01|1995-06-11|1995-04-27|NONE|FOB|oss the fluffily regular\n5956|154047|6563|1|10|11010.40|0.04|0.05|N|O|1998-07-27|1998-07-04|1998-08-21|NONE|MAIL|ic packages am\n5956|54179|1695|2|23|26062.91|0.08|0.03|N|O|1998-06-06|1998-07-10|1998-06-15|DELIVER IN PERSON|RAIL|ly slyly special \n5956|174834|7352|3|47|89715.01|0.04|0.06|N|O|1998-09-06|1998-06-29|1998-09-18|TAKE BACK RETURN|MAIL|lyly express theodol\n5956|19995|7499|4|40|76599.60|0.09|0.05|N|O|1998-06-11|1998-07-19|1998-06-21|NONE|MAIL|final theodolites sleep carefully ironic c\n5957|14617|7119|1|37|56669.57|0.07|0.00|A|F|1994-04-18|1994-02-19|1994-05-11|NONE|AIR| ideas use ruthlessly.\n5957|58726|3737|2|46|77497.12|0.04|0.08|A|F|1994-01-23|1994-01-30|1994-02-07|NONE|SHIP|platelets. furiously unusual requests \n5957|1377|6378|3|17|21732.29|0.01|0.01|A|F|1994-01-24|1994-02-16|1994-02-08|TAKE BACK RETURN|SHIP|. final, pending packages\n5957|131499|6526|4|29|44384.21|0.01|0.03|R|F|1994-02-24|1994-03-04|1994-03-08|COLLECT COD|REG AIR|sits. final, even asymptotes cajole quickly\n5957|87262|4787|5|40|49970.40|0.04|0.04|R|F|1994-01-07|1994-02-05|1994-01-26|DELIVER IN PERSON|SHIP|ironic asymptotes sleep blithely again\n5957|5079|80|6|41|40346.87|0.10|0.07|R|F|1994-03-25|1994-02-20|1994-03-31|DELIVER IN PERSON|MAIL|es across the regular requests maint\n5957|158431|5977|7|32|47661.76|0.10|0.07|A|F|1994-03-05|1994-02-20|1994-03-09|NONE|TRUCK| boost carefully across the \n5958|148834|6377|1|33|62133.39|0.02|0.04|N|O|1995-09-24|1995-12-12|1995-10-05|COLLECT COD|MAIL|lar, regular accounts wake furi\n5958|42932|7941|2|23|43123.39|0.03|0.04|N|O|1995-09-26|1995-10-19|1995-09-27|COLLECT COD|SHIP|regular requests. bold, bold deposits unwin\n5958|152606|7637|3|42|69661.20|0.10|0.00|N|O|1995-12-12|1995-10-19|1996-01-09|NONE|AIR|n accounts. final, ironic packages \n5958|38433|8434|4|18|24685.74|0.04|0.05|N|O|1995-12-02|1995-10-17|1995-12-22|COLLECT COD|FOB|regular requests haggle\n5958|131786|4300|5|32|58168.96|0.06|0.00|N|O|1995-09-20|1995-12-10|1995-10-14|COLLECT COD|REG AIR|e carefully special theodolites. carefully \n5959|134156|6670|1|49|58317.35|0.07|0.03|R|F|1992-07-16|1992-08-09|1992-08-14|DELIVER IN PERSON|SHIP|usual packages haggle slyly pi\n5959|146297|6298|2|17|22835.93|0.09|0.07|R|F|1992-06-10|1992-07-06|1992-06-23|COLLECT COD|MAIL|ackages. blithely ex\n5959|4379|4380|3|4|5133.48|0.04|0.03|R|F|1992-06-14|1992-07-05|1992-07-01|NONE|MAIL|gular requests ar\n5959|195921|5922|4|13|26219.96|0.03|0.00|A|F|1992-07-29|1992-07-13|1992-08-20|COLLECT COD|SHIP|ar forges. deposits det\n5959|39979|7489|5|37|71001.89|0.04|0.01|R|F|1992-06-05|1992-07-18|1992-06-29|NONE|TRUCK|endencies. brai\n5959|118109|621|6|35|39448.50|0.03|0.00|A|F|1992-05-27|1992-06-19|1992-06-23|NONE|TRUCK|ely silent deposits. \n5959|42154|4659|7|47|51519.05|0.02|0.01|R|F|1992-08-28|1992-07-24|1992-09-09|TAKE BACK RETURN|RAIL|deposits. slyly special cou\n5984|69454|6973|1|13|18504.85|0.06|0.07|R|F|1994-10-16|1994-09-06|1994-11-11|NONE|MAIL|lar platelets. f\n5984|101208|1209|2|25|30230.00|0.05|0.08|R|F|1994-10-06|1994-07-21|1994-10-28|COLLECT COD|RAIL|gular accounts. even packages nag slyly\n5984|321|2822|3|8|9770.56|0.10|0.00|R|F|1994-09-17|1994-08-28|1994-09-25|COLLECT COD|RAIL|its. express,\n5984|189708|9709|4|35|62919.50|0.00|0.01|A|F|1994-08-25|1994-08-05|1994-08-31|DELIVER IN PERSON|SHIP|le fluffily regula\n5985|85717|8226|1|4|6810.84|0.02|0.02|A|F|1995-05-04|1995-04-01|1995-05-17|DELIVER IN PERSON|MAIL|ole along the quickly slow d\n5986|78789|6311|1|26|45962.28|0.00|0.00|R|F|1992-08-10|1992-05-23|1992-08-24|TAKE BACK RETURN|SHIP|e fluffily ironic ideas. silent \n5986|195254|7774|2|25|33731.25|0.03|0.06|A|F|1992-06-16|1992-07-17|1992-06-29|TAKE BACK RETURN|MAIL| instructions. slyly regular de\n5986|29636|2139|3|1|1565.63|0.07|0.06|A|F|1992-05-21|1992-06-21|1992-05-24|DELIVER IN PERSON|REG AIR|fix quickly quickly final deposits. fluffil\n5986|89590|7115|4|31|48967.29|0.00|0.03|A|F|1992-08-21|1992-06-29|1992-09-14|NONE|AIR|structions! furiously pending instructi\n5986|135143|5144|5|6|7068.84|0.05|0.02|A|F|1992-07-16|1992-06-10|1992-07-29|DELIVER IN PERSON|RAIL|al foxes within the slyly speci\n"
  },
  {
    "path": "src/test/regress/data/nation.data",
    "content": "0|ALGERIA|0| haggle. carefully final deposits detect slyly agai\n1|ARGENTINA|1|al foxes promise slyly according to the regular accounts. bold requests alon\n2|BRAZIL|1|y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special \n3|CANADA|1|eas hang ironic, silent packages. slyly regular packages are furiously over the tithes. fluffily bold\n4|EGYPT|4|y above the carefully unusual theodolites. final dugouts are quickly across the furiously regular d\n5|ETHIOPIA|0|ven packages wake quickly. regu\n6|FRANCE|3|refully final requests. regular, ironi\n7|GERMANY|3|l platelets. regular accounts x-ray: unusual, regular acco\n8|INDIA|2|ss excuses cajole slyly across the packages. deposits print aroun\n9|INDONESIA|2| slyly express asymptotes. regular deposits haggle slyly. carefully ironic hockey players sleep blithely. carefull\n10|IRAN|4|efully alongside of the slyly final dependencies. \n11|IRAQ|4|nic deposits boost atop the quickly final requests? quickly regula\n12|JAPAN|2|ously. final, express gifts cajole a\n13|JORDAN|4|ic deposits are blithely about the carefully regular pa\n14|KENYA|0| pending excuses haggle furiously deposits. pending, express pinto beans wake fluffily past t\n15|MOROCCO|0|rns. blithely bold courts among the closely regular packages use furiously bold platelets?\n16|MOZAMBIQUE|0|s. ironic, unusual asymptotes wake blithely r\n17|PERU|1|platelets. blithely pending dependencies use fluffily across the even pinto beans. carefully silent accoun\n18|CHINA|2|c dependencies. furiously express notornis sleep slyly regular accounts. ideas sleep. depos\n19|ROMANIA|3|ular asymptotes are about the furious multipliers. express dependencies nag above the ironically ironic account\n20|SAUDI ARABIA|4|ts. silent requests haggle. closely express packages sleep across the blithely\n21|VIETNAM|2|hely enticingly express accounts. even, final \n22|RUSSIA|3| requests against the platelets use never according to the quickly regular pint\n23|UNITED KINGDOM|3|eans boost carefully special requests. accounts are. carefull\n24|UNITED STATES|1|y final packages. slow foxes cajole quickly. quickly silent platelets breach ironic accounts. unusual pinto be\n"
  },
  {
    "path": "src/test/regress/data/null_values.csv",
    "content": ",{NULL},\"(,)\"\n,,\n"
  },
  {
    "path": "src/test/regress/data/orders.2.data",
    "content": "8997|607|F|147614.36|1994-07-02|5-LOW|Clerk#000000404|0|lithely. express, ironic pearls nag permanently. \n8998|80|F|147264.16|1993-01-04|5-LOW|Clerk#000000733|0| fluffily pending sauternes cajo\n8999|1175|F|113671.53|1994-06-13|2-HIGH|Clerk#000000097|0|ly even foxes. slyly express a\n9024|1469|F|298241.36|1992-06-03|3-MEDIUM|Clerk#000000901|0|ar the theodolites. fluffily stealthy requests among the quickly regular asy\n9025|739|F|97454.69|1994-05-20|5-LOW|Clerk#000000379|0|ording to the quickly regular ideas integrate above the sly platelets. sly\n9026|677|O|63256.87|1996-07-24|5-LOW|Clerk#000000320|0|ironic escapades would wake carefully \n9027|514|O|155500.43|1995-09-03|5-LOW|Clerk#000000918|0| sleep carefully with th\n9028|1475|F|63063.84|1993-12-22|5-LOW|Clerk#000000364|0| the regular packages. daringly even f\n9029|1213|F|78703.86|1992-11-20|3-MEDIUM|Clerk#000000965|0| excuses nag quickly carefully unusual excuse\n9030|535|O|225136.87|1998-07-14|3-MEDIUM|Clerk#000000872|0|foxes according to the furiously silent excuses could haggle unusual d\n9031|1379|F|270589.18|1993-12-27|5-LOW|Clerk#000000890|0|r packages. slyly express ideas\n9056|121|O|170074.18|1996-08-08|3-MEDIUM|Clerk#000000117|0|was carefully after the furiously bold dug\n9057|71|F|320227.49|1994-11-05|1-URGENT|Clerk#000000539|0| fluffily quickly ironic packages. furiously ironic accounts a\n9058|403|F|63464.13|1993-06-29|2-HIGH|Clerk#000000376|0|ealthily special deposits. quickly regular requests wake silently. fur\n9059|926|O|187590.77|1996-08-01|2-HIGH|Clerk#000000725|0|ar pinto beans sleep special\n9060|463|O|45295.71|1996-06-09|1-URGENT|Clerk#000000438|0|iously. slyly regular dol\n9061|325|O|66191.69|1996-01-15|2-HIGH|Clerk#000000428|0|silent excuses! slyl\n9062|1102|P|212710.32|1995-03-21|2-HIGH|Clerk#000000201|0|sts cajole slyly according to the carefully slow foxes. furio\n9063|1399|O|51019.90|1997-02-12|2-HIGH|Clerk#000000323|0|ffily regular grouche\n9088|610|F|224148.01|1994-06-23|1-URGENT|Clerk#000000975|0|ter the blithely final deposits. furiously\n9089|370|F|39118.01|1993-05-24|1-URGENT|Clerk#000000538|0|d platelets are deposits. pinto beans cajole boldly.\n9090|775|O|9848.22|1996-11-21|4-NOT SPECIFIED|Clerk#000000699|0|cial theodolites at the evenl\n9091|679|F|242198.88|1993-11-12|2-HIGH|Clerk#000000426|0|final packages wake carefully.\n9092|664|F|202836.36|1994-07-25|4-NOT SPECIFIED|Clerk#000000144|0| slyly final waters. special packages solve\n9093|619|O|19431.49|1995-12-10|3-MEDIUM|Clerk#000000333|0|even accounts. special,\n9094|1430|O|46784.32|1998-05-27|3-MEDIUM|Clerk#000000805|0|es boost slyly after the platelets. shea\n9095|220|O|49946.10|1995-06-11|2-HIGH|Clerk#000000621|0|riously regular accounts wake slyly special theodolites. asymptotes use f\n9120|1010|F|128719.48|1992-06-22|3-MEDIUM|Clerk#000000252|0|ly ironic realms. furio\n9121|434|O|94637.46|1996-07-05|5-LOW|Clerk#000000930|0| final dependencies. carefully even excuses after the \n9122|79|O|234630.52|1996-12-14|4-NOT SPECIFIED|Clerk#000000991|0|across the carefull\n9123|367|F|64575.87|1993-06-27|1-URGENT|Clerk#000000988|0|wake carefully pendin\n9124|559|O|173690.73|1995-10-17|5-LOW|Clerk#000000381|0|nstructions after the c\n9125|151|O|195514.25|1998-05-30|2-HIGH|Clerk#000000591|0|kages around the fluffily bold f\n9126|371|F|106144.91|1994-11-18|3-MEDIUM|Clerk#000000319|0|deposits boost carefully against the fluffily final instructions. i\n9127|611|O|79003.80|1995-10-11|4-NOT SPECIFIED|Clerk#000000628|0|ress packages across the furio\n9152|1210|F|129410.55|1993-08-21|4-NOT SPECIFIED|Clerk#000000612|0|nto beans! final instructions nag slyly. slyly ironic foxes nag blith\n9153|1147|O|210245.80|1997-07-19|4-NOT SPECIFIED|Clerk#000000488|0|uffily express instructions haggle carefully. quickly fluffy th\n9154|1|O|357345.46|1997-06-23|4-NOT SPECIFIED|Clerk#000000328|0|y ironic packages cajole. blithely final depende\n9155|355|F|83263.25|1992-08-31|1-URGENT|Clerk#000000253|0|ickly regular requests sleep alongside of the car\n9156|154|F|135183.22|1994-02-07|1-URGENT|Clerk#000000387|0|deposits. dependencies wake ca\n9157|595|F|52660.33|1992-06-20|5-LOW|Clerk#000000736|0|requests detect furiously special, silent packages. carefu\n9158|271|F|96814.40|1995-04-25|4-NOT SPECIFIED|Clerk#000000195|0|ecial, even packages. even foxes print. regu\n9159|1135|O|99594.61|1995-07-26|1-URGENT|Clerk#000000892|0|xcuses. quickly ironic deposits wake alongside of the quickly pending p\n9184|1156|O|56334.07|1997-07-19|1-URGENT|Clerk#000000086|0|blithely final packages haggle according to the slyly quick pack\n9185|145|F|64122.78|1994-06-16|2-HIGH|Clerk#000000951|0|leep carefully blithely regular orbits. blithely regular foxes cajol\n9186|457|F|167929.61|1992-03-30|5-LOW|Clerk#000000044|0|lar, silent asymptotes haggl\n9187|769|F|212469.45|1994-08-22|2-HIGH|Clerk#000000985|0|lithely ironic theodolites. furiously ironic instructions print along \n9188|619|O|224857.18|1998-01-25|1-URGENT|Clerk#000000765|0|aggle quickly above the accounts. excuses \n9189|1069|O|109530.95|1996-12-02|3-MEDIUM|Clerk#000000501|0|r waters haggle furiously fluffily ironic id\n9190|460|O|272754.44|1998-03-03|2-HIGH|Clerk#000000196|0|side of the permanently ironic courts unwind\n9191|535|O|21182.03|1996-03-24|3-MEDIUM|Clerk#000000095|0|final packages wake carefully. \n9216|643|O|147294.33|1995-04-15|3-MEDIUM|Clerk#000000320|0|its nag regular deposits. slyly even reques\n9217|1489|O|117161.58|1997-05-26|1-URGENT|Clerk#000000051|0| above the blithely regular d\n9218|241|O|31407.74|1997-12-10|4-NOT SPECIFIED|Clerk#000000795|0|hely regular foxes are alongside of the furiously regular deposit\n9219|412|F|205446.61|1994-09-29|5-LOW|Clerk#000000382|0|nst the furiously regular dinos. regular asymptotes against the slyly fina\n9220|482|O|992.46|1998-04-07|1-URGENT|Clerk#000000880|0|y bravely ironic deposits. furiously unusual sentiments about the fluffily ir\n9221|85|F|273797.07|1995-01-04|5-LOW|Clerk#000000240|0|ess accounts sleep carefully. carefully final pinto beans sleep f\n9222|211|F|258460.61|1994-02-11|2-HIGH|Clerk#000000148|0|ding to the instructions. regular requests nag f\n9223|304|O|215658.19|1997-08-05|4-NOT SPECIFIED|Clerk#000000785|0|ully according to the blithely \n9248|1163|F|110964.49|1994-12-24|4-NOT SPECIFIED|Clerk#000000259|0|ids cajole regular, regular dependencies. deposits along the dolphi\n9249|1348|F|161815.08|1993-05-20|2-HIGH|Clerk#000000756|0|slyly pending accounts. packages wake c\n9250|1345|F|85572.38|1992-08-14|1-URGENT|Clerk#000000535|0|y final packages; carefully pendi\n9251|323|F|29910.23|1993-08-30|3-MEDIUM|Clerk#000000374|0|ld requests. deposits use blithely ruthlessly unusual packages. fluffil\n9252|412|O|89891.37|1997-12-11|2-HIGH|Clerk#000000454|0|encies affix slyly perma\n9253|818|F|255856.03|1992-01-31|1-URGENT|Clerk#000000307|0|es. sometimes regular grouches wa\n9254|1276|F|56365.27|1993-05-15|3-MEDIUM|Clerk#000000761|0|r deposits. quickly bold requests use-\n9255|472|O|153124.60|1995-12-08|4-NOT SPECIFIED|Clerk#000000476|0|y bold asymptotes-- carefully\n9280|1223|O|275205.91|1998-06-12|5-LOW|Clerk#000000022|0|beans alongside of the fluffily express asymptotes integrate \n9281|904|F|173278.28|1992-02-24|1-URGENT|Clerk#000000530|0|eep furiously according to the requests; ideas integrate \n9282|79|O|34765.35|1995-11-26|4-NOT SPECIFIED|Clerk#000000824|0|ses? carefully express platelets sleep blithely against the blithely specia\n9283|1255|F|7798.12|1994-06-03|3-MEDIUM|Clerk#000000859|0|ully even platelets. silent packages\n9284|661|F|56207.47|1994-06-14|4-NOT SPECIFIED|Clerk#000000544|0| furiously across the final deposits. quickly ironic requests accordin\n9285|1012|F|118683.77|1994-01-03|1-URGENT|Clerk#000000553|0|sts. express accounts snooze; furiously final\n9286|505|P|155839.92|1995-04-04|1-URGENT|Clerk#000000557|0|ake. carefully bold packages promise with the\n9287|790|O|13525.92|1998-01-02|4-NOT SPECIFIED|Clerk#000000636|0|ously unusual packages. regular foxes detect. blithely slow ideas sl\n9312|920|F|226584.81|1992-05-19|4-NOT SPECIFIED|Clerk#000000897|0|ckly slyly regular packages. unusual packages are. regular\n9313|1348|O|222818.15|1996-04-06|3-MEDIUM|Clerk#000000926|0|s wake slyly against the slyly express packages. ironic packages\n9314|1313|F|14188.37|1995-02-15|4-NOT SPECIFIED|Clerk#000000907|0|eas. blithely regular asymptotes sleep carefully across the slyly even\n9315|587|O|16012.39|1998-03-17|3-MEDIUM|Clerk#000000559|0|ackages are furiously alongside of the slyly regular theodolite\n9316|1393|O|225240.88|1995-09-18|4-NOT SPECIFIED|Clerk#000000363|0|inal pinto beans haggle c\n9317|100|F|156751.98|1994-03-15|4-NOT SPECIFIED|Clerk#000000347|0|aves across the unusual, regular sauternes wake\n9318|1463|F|109251.02|1992-07-28|1-URGENT|Clerk#000000738|0|ss courts across the slyly silent foxes are regular, final pac\n9319|880|F|104039.47|1992-09-11|1-URGENT|Clerk#000000155|0|. fluffily ironic instructions detect\n9344|664|O|348335.72|1995-12-07|5-LOW|Clerk#000000961|0|ial deposits nag blithely alongside of the caref\n9345|1376|O|163627.01|1996-01-22|4-NOT SPECIFIED|Clerk#000000126|0|out the daringly ironic packages affix furiously fluffi\n9346|917|O|173943.50|1996-10-09|4-NOT SPECIFIED|Clerk#000000088|0|furiously final packages integrate stealthily expres\n9347|673|F|162913.90|1992-08-10|5-LOW|Clerk#000000268|0|he gifts. slyly permanent requests \n9348|658|O|120413.73|1996-06-07|5-LOW|Clerk#000000416|0|ackages. carefully ironic pinto beans about the close accoun\n9349|1414|O|217101.05|1997-08-29|4-NOT SPECIFIED|Clerk#000000804|0|. fluffily final accounts haggle; furiously reg\n9350|145|F|255516.01|1992-11-01|1-URGENT|Clerk#000000253|0|ackages doze evenly across the foxes. quickly ironic reques\n9351|898|O|81166.06|1996-05-17|5-LOW|Clerk#000000430|0|ven dependencies. furiously ironic dependencies promise along the slyly\n9376|835|O|145196.26|1997-07-24|2-HIGH|Clerk#000000348|0|ng the carefully pending ac\n9377|340|F|9021.26|1993-04-22|2-HIGH|Clerk#000000466|0|ers above the carefull\n9378|457|O|115914.73|1997-10-16|1-URGENT|Clerk#000000014|0|ultipliers wake furiously never special deposits!\n9379|350|F|152004.78|1992-01-04|1-URGENT|Clerk#000000764|0|nusual deposits. furiously final packages wake evenly even tithes. qu\n9380|887|F|167911.91|1994-08-26|1-URGENT|Clerk#000000962|0| deposits boost along the carefully ironic deposits. express requests use. \n9381|1184|O|94270.85|1995-12-18|4-NOT SPECIFIED|Clerk#000000215|0|y final Tiresias are. requests \n9382|337|O|150840.29|1996-02-19|1-URGENT|Clerk#000000308|0|l accounts. furiously ironic deposits use. \n9383|140|F|180583.64|1994-03-17|4-NOT SPECIFIED|Clerk#000000237|0|uffily final pinto beans cajole ca\n9408|184|F|11891.10|1992-10-21|3-MEDIUM|Clerk#000000664|0|xes cajole carefully furiously final pains. carefully express accou\n9409|970|F|155483.25|1992-05-06|1-URGENT|Clerk#000000300|0|ly express platelets above the sl\n9410|787|O|88246.41|1997-07-18|5-LOW|Clerk#000000985|0|theodolites solve fluffily. blithely iron\n9411|661|F|55576.82|1995-02-07|2-HIGH|Clerk#000000594|0|rses. furiously regular requests haggl\n9412|1216|O|245785.65|1998-07-14|2-HIGH|Clerk#000000016|0|requests. carefully regular packages play carefully pending dependencies. ca\n9413|148|O|150237.99|1995-07-13|5-LOW|Clerk#000000202|0|latelets are blithely requests. ironic packages boost slyly across t\n9414|1333|F|98169.43|1993-09-10|3-MEDIUM|Clerk#000000661|0|above the slyly ironic\n9415|1049|F|241463.63|1992-08-24|1-URGENT|Clerk#000000311|0|ccounts boost blithely. final\n9440|643|F|4331.68|1994-06-08|5-LOW|Clerk#000000711|0| requests. regular, final pinto beans are. furiously regular accounts det\n9441|334|O|3216.31|1997-06-18|4-NOT SPECIFIED|Clerk#000000381|0|iously final deposits. express, regular waters haggle sl\n9442|1267|F|103119.65|1993-03-11|4-NOT SPECIFIED|Clerk#000000948|0|hely special courts. furiously final foxes sleep quickly. quick, \n9443|304|O|166940.25|1997-05-07|4-NOT SPECIFIED|Clerk#000000317|0|y along the carefully reg\n9444|106|O|36255.65|1996-07-18|2-HIGH|Clerk#000000387|0| quickly. always silent platelets must sleep f\n9445|928|O|76754.58|1997-11-14|4-NOT SPECIFIED|Clerk#000000394|0|as. slyly ironic deposits along\n9446|1358|O|216362.20|1997-12-29|1-URGENT|Clerk#000000173|0|ake pinto beans. slyly f\n9447|1162|O|79483.65|1995-06-19|2-HIGH|Clerk#000000115|0|theodolites haggle carefully instructio\n9472|481|O|44176.66|1995-06-17|1-URGENT|Clerk#000000979|0|bove the quickly final deposits are carefully\n9473|23|F|212682.92|1992-10-12|4-NOT SPECIFIED|Clerk#000000541|0|ts sleep above the accounts. slyly final deposits are regularly. r\n9474|133|P|246662.69|1995-05-12|3-MEDIUM|Clerk#000000710|0|ely final requests: dependencies cajole slyly. slyly\n9475|1442|O|206441.11|1996-11-22|2-HIGH|Clerk#000000233|0| notornis. ironically even instructions en\n9476|1283|F|177524.44|1994-05-26|4-NOT SPECIFIED|Clerk#000000088|0|e slyly final requests might integrate carefully against the slyly express p\n9477|170|O|269394.57|1998-03-19|3-MEDIUM|Clerk#000000812|0|lites along the even deposits use bold pinto beans. regular requests \n9478|940|F|173044.15|1994-03-29|2-HIGH|Clerk#000000033|0|ound the blithely express instructions. furiously \n9479|1373|F|76316.92|1994-09-22|3-MEDIUM|Clerk#000000047|0|ter the carefully ironic\n9504|736|F|171165.10|1992-11-26|3-MEDIUM|Clerk#000000902|0|among the quickly bold deposits haggle exp\n9505|316|F|283831.70|1992-06-08|2-HIGH|Clerk#000000873|0|hely ironic asymptot\n9506|1232|F|47720.50|1994-02-11|4-NOT SPECIFIED|Clerk#000000441|0|carefully ironic accou\n9507|517|F|34161.51|1994-11-30|5-LOW|Clerk#000000438|0|press pinto beans are slyly. final packages cajole. quickly regular excus\n9508|448|O|235237.15|1996-04-22|5-LOW|Clerk#000000312|0|ss attainments slee\n9509|37|F|224330.32|1992-10-08|4-NOT SPECIFIED|Clerk#000000350|0|iously final instructions sleep fu\n9510|235|O|93930.42|1996-11-20|5-LOW|Clerk#000000302|0|packages sleep furiously. bold theodolites haggle slyly. ironic,\n9511|1141|O|96086.28|1996-08-01|5-LOW|Clerk#000000555|0|out the packages may\n9536|1138|O|105639.69|1997-08-20|4-NOT SPECIFIED|Clerk#000000224|0|. slyly even ideas doubt slyly. slowly regular orbits cajole. fur\n9537|806|F|249384.96|1995-02-14|4-NOT SPECIFIED|Clerk#000000203|0| packages. final, regular foxes haggle bli\n9538|1399|F|32388.43|1994-03-11|4-NOT SPECIFIED|Clerk#000000040|0|ly. slyly final accounts nag carefully regular ideas. blithely ironic theodo\n9539|262|O|26683.19|1995-11-15|2-HIGH|Clerk#000000917|0|iously ironic deposits affix furiously alongside of the slyly even foxes. even\n9540|784|F|96163.55|1994-06-11|1-URGENT|Clerk#000000048|0|t above the stealthily bold theodolites\n9541|598|F|105885.33|1992-03-26|4-NOT SPECIFIED|Clerk#000000840|0|xcuses dazzle furiously regular the\n9542|1147|O|121584.41|1995-12-16|2-HIGH|Clerk#000000349|0|theodolites. blithely pendin\n9543|850|F|145441.29|1992-05-10|1-URGENT|Clerk#000000187|0| unusual warhorses cajole\n9568|619|F|80658.16|1993-03-08|4-NOT SPECIFIED|Clerk#000000270|0|ng the quickly unusual pinto beans sublate blithely ca\n9569|1264|O|210401.62|1997-11-01|2-HIGH|Clerk#000000856|0|pecial deposits after the final, pending packages wake carefully\n9570|1435|O|79124.61|1996-06-04|2-HIGH|Clerk#000000397|0|ccounts. foxes nag special, special theodolites\n9571|1366|O|58131.32|1996-04-30|4-NOT SPECIFIED|Clerk#000000263|0| furiously express requests. even requests are. carefully final a\n9572|49|O|85669.13|1998-03-28|4-NOT SPECIFIED|Clerk#000000452|0|atelets. slyly regular requests nag quickly ev\n9573|1361|F|140154.64|1992-06-14|3-MEDIUM|Clerk#000000250|0|ix carefully busily unusual deposits. thin excuses haggle quickly. quickly \n9574|790|O|145838.53|1995-07-31|3-MEDIUM|Clerk#000000814|0|efully regular platelets. pending packages unwind carefully among the fur\n9575|916|F|27475.15|1992-03-31|5-LOW|Clerk#000000133|0|nts haggle busily unusual, even packages. regular packages u\n9600|536|F|99615.09|1993-11-08|4-NOT SPECIFIED|Clerk#000000467|0|he unusual, ironic requests nag furiously ironic accounts\n9601|1435|O|143520.49|1996-03-20|1-URGENT|Clerk#000000677|0|ugh the slyly regular requests. furiously even pinto beans are blithely slyl\n9602|1481|F|193843.49|1992-11-11|1-URGENT|Clerk#000000086|0| across the slyly ironic ideas. carefully unusual requests\n9603|1204|O|315928.32|1998-01-09|5-LOW|Clerk#000000191|0|ng the quickly special requests caj\n9604|620|O|48743.62|1996-06-25|2-HIGH|Clerk#000000414|0|usly ironic theodolites! blithely final ideas \n9605|1220|F|113112.29|1992-12-26|4-NOT SPECIFIED|Clerk#000000777|0|yly bold pinto beans are furiously packages. slyly express courts play\n9606|152|F|149838.50|1994-07-28|4-NOT SPECIFIED|Clerk#000000474|0| above the always regular pinto beans. special, unusual accounts grow. pending\n9607|643|O|44978.35|1995-12-18|1-URGENT|Clerk#000000752|0| ironic requests are carefully. silent \n9632|1429|F|146044.97|1992-01-17|5-LOW|Clerk#000000857|0|y ironic theodolites are silent foxes. blithely final plate\n9633|1336|O|191021.09|1995-09-12|4-NOT SPECIFIED|Clerk#000000918|0|ns around the slyly ironic pinto beans cajole carefully ironic depths. careful\n9634|958|O|314926.50|1998-01-13|2-HIGH|Clerk#000000166|0| carefully. slyly ironic courts mold. ironic\n9635|847|F|168349.65|1994-04-14|1-URGENT|Clerk#000000127|0|the quickly ironic ideas sleep at the quickly \n9636|304|O|109568.95|1997-01-30|1-URGENT|Clerk#000000263|0|lar foxes are requests. quickly even pinto be\n9637|1411|F|112613.87|1994-01-04|5-LOW|Clerk#000000953|0|s. furiously final requests believe furiously against the brave \n9638|1126|O|112055.66|1996-06-08|2-HIGH|Clerk#000000953|0|requests after the furiously unusual accounts integrate slyly u\n9639|722|F|244899.69|1993-09-06|4-NOT SPECIFIED|Clerk#000000666|0|press, express pinto \n9664|310|F|105001.15|1993-04-08|2-HIGH|Clerk#000000715|0|ccounts across the slyly ironic pinto be\n9665|1067|F|81734.81|1994-04-24|5-LOW|Clerk#000000277|0|sleep furiously carefully ironic foxes. pinto beans wake. final deposits caj\n9666|221|O|240666.92|1996-05-09|1-URGENT|Clerk#000000511|0|ss the fluffily express tithes.\n9667|1102|O|208860.66|1996-02-24|3-MEDIUM|Clerk#000000142|0|nal instructions. special accounts along the regular\n9668|847|F|77447.46|1994-09-07|5-LOW|Clerk#000000469|0|refully ironic accounts. furiously bold packages a\n9669|619|F|79738.94|1994-04-23|3-MEDIUM|Clerk#000000297|0|deposits promise blithely ironic theodolites. fluffily bold requests\n9670|1387|O|200921.16|1997-11-08|3-MEDIUM|Clerk#000000035|0|unts? furiously express deposits are blithely. regular, special forges sle\n9671|136|O|157241.36|1995-09-13|2-HIGH|Clerk#000000417|0|ages boost slyly against the furiously\n9696|575|F|211382.08|1995-02-20|4-NOT SPECIFIED|Clerk#000000971|0|xpress requests would are pinto beans.\n9697|164|F|174800.10|1995-01-27|3-MEDIUM|Clerk#000000432|0|courts are. even platelets was alongside of \n9698|1487|O|7676.88|1995-08-07|2-HIGH|Clerk#000000040|0| around the quickly bold packages sle\n9699|875|F|10782.39|1995-03-12|3-MEDIUM|Clerk#000000853|0|kly regular platelets. slyly silent accounts alongside of the blithely r\n9700|544|O|266611.98|1995-08-09|1-URGENT|Clerk#000000137|0|althy pinto beans wake blithely. qui\n9701|112|F|277385.28|1993-02-28|5-LOW|Clerk#000000399|0|bold deposits cajole furiously bold requests. even excuses\n9702|1201|O|182347.71|1996-08-22|3-MEDIUM|Clerk#000000852|0|tions haggle slyly among the ironic theodolites. blithel\n9703|1126|F|28954.25|1994-10-07|5-LOW|Clerk#000000101|0|realms wake. express, ironic accou\n9728|826|F|39398.24|1993-07-03|5-LOW|Clerk#000000647|0|ng requests. fluffily enticing re\n9729|970|O|162532.59|1996-07-06|3-MEDIUM|Clerk#000000148|0|ndencies about the care\n9730|143|O|278148.33|1997-03-04|5-LOW|Clerk#000000743|0| final dependencies doze furiously slyly silent ideas? furiousl\n9731|283|F|21613.76|1993-10-21|5-LOW|Clerk#000000586|0| dolphins nag slyly even deposits-- blithely \n9732|820|P|69634.87|1995-03-14|4-NOT SPECIFIED|Clerk#000000599|0|f the special, even instructions. carefully final requests use along the \n9733|1000|F|72374.84|1994-03-18|4-NOT SPECIFIED|Clerk#000000997|0|n dolphins haggle. slyly express accounts wake carefully final asymptotes. qui\n9734|835|O|124297.04|1997-02-03|1-URGENT|Clerk#000000086|0|equests affix quietly bold deposits. dependencies affix among the quietly\n9735|337|O|115276.76|1997-06-28|4-NOT SPECIFIED|Clerk#000000454|0|nic foxes. accounts detect fur\n9760|1181|O|158777.10|1998-04-14|5-LOW|Clerk#000000207|0|anent accounts. accounts sleep carefully slyl\n9761|433|F|70880.34|1992-01-23|4-NOT SPECIFIED|Clerk#000000468|0| permanent requests. furious requests nag blithely about the q\n9762|1492|F|238951.03|1994-01-04|2-HIGH|Clerk#000000861|0|ic requests cajole quickly unusual accounts. regular foxes are blithely. in\n9763|1492|O|132201.56|1995-08-14|3-MEDIUM|Clerk#000000374|0| requests detect carefull\n9764|1256|F|56685.16|1992-12-30|1-URGENT|Clerk#000000289|0|nts. platelets wake blithe\n9765|1153|F|45183.21|1992-10-13|5-LOW|Clerk#000000364|0|ts. even pinto beans haggle alongside of the theodolites.\n9766|235|F|165508.82|1992-08-15|1-URGENT|Clerk#000000265|0|s excuses cajole quickly. regul\n9767|973|F|46852.11|1994-06-28|5-LOW|Clerk#000000546|0|asymptotes hinder blithely after the busily final fo\n9792|1114|O|171667.54|1995-07-24|5-LOW|Clerk#000000929|0| the special accounts. carefully special ideas mainta\n9793|1198|F|80712.82|1992-04-04|3-MEDIUM|Clerk#000000770|0| carefully ironic courts haggle evenly. carefully final requests ca\n9794|505|F|216443.67|1992-03-25|4-NOT SPECIFIED|Clerk#000000852|0|dolites grow furiously fluffily final requests. blithely special \n9795|370|O|215242.29|1998-02-04|3-MEDIUM|Clerk#000000481|0|ng the furiously iron\n9796|1052|F|246896.17|1993-09-11|1-URGENT|Clerk#000000150|0|lyly regular packages. furio\n9797|43|F|209264.66|1993-09-13|1-URGENT|Clerk#000000296|0|packages. blithe requests affix quickly carefully unusual ideas. blithe\n9798|1135|O|23604.81|1996-03-09|1-URGENT|Clerk#000000485|0|ial deposits. accounts detect dependencies. silent accounts cajole flu\n9799|461|O|152265.02|1996-04-07|4-NOT SPECIFIED|Clerk#000000526|0|ully final requests cajole carefully according to t\n9824|472|F|240691.30|1995-01-23|1-URGENT|Clerk#000000653|0|furiously even theodolites impress even, special asymptotes. idle ideas dazzl\n9825|28|O|93645.30|1996-04-09|1-URGENT|Clerk#000000990|0|rding to the pending\n9826|698|F|105223.73|1992-03-23|3-MEDIUM|Clerk#000000777|0|deas haggle slyly final reques\n9827|1289|O|36448.45|1998-07-04|4-NOT SPECIFIED|Clerk#000000085|0|e unusual dependencies. regular accounts alongside of the quickly regular f\n9828|1306|F|269649.39|1992-04-25|3-MEDIUM|Clerk#000000355|0| deposits are carefully bold notornis. blith\n9829|1379|O|6613.63|1995-12-09|1-URGENT|Clerk#000000004|0|ke across the requests. silently final foxes wake never amo\n9830|493|O|202703.99|1997-09-08|2-HIGH|Clerk#000000191|0|final packages are furiously beside the quickly final e\n9831|790|O|86410.08|1996-06-20|4-NOT SPECIFIED|Clerk#000000010|0|mas above the even instructions haggle ironical\n9856|802|F|236403.75|1993-05-30|3-MEDIUM|Clerk#000000278|0|usual tithes against the express requests sleep across t\n9857|172|F|158381.98|1993-05-12|2-HIGH|Clerk#000000627|0|ons haggle quickly above the blithely regular sauternes. slyly express instru\n9858|1399|O|103920.84|1997-12-08|3-MEDIUM|Clerk#000000432|0|e of the fluffily final packa\n9859|475|O|247788.34|1996-07-28|3-MEDIUM|Clerk#000000989|0|xes boost slyly. furiously regul\n9860|623|F|151734.45|1993-10-11|1-URGENT|Clerk#000000345|0|carefully. ironic ideas haggle theodolites. quickly speci\n9861|1477|F|66189.04|1992-10-29|5-LOW|Clerk#000000113|0|le after the unusual deposits. even acc\n9862|10|O|193015.83|1997-10-10|2-HIGH|Clerk#000000168|0|among the instructions was after the slyly special packages. final, final hoc\n9863|247|O|362398.22|1995-09-05|1-URGENT|Clerk#000000473|0|urts nag finally even, stealthy instructions. quickly final ideas \n9888|1319|F|74228.41|1994-09-13|5-LOW|Clerk#000000434|0|g to the carefully final foxes. fur\n9889|82|F|35362.96|1994-09-09|3-MEDIUM|Clerk#000000786|0|lly atop the foxes. doggedly final packages integrate thin\n9890|731|P|87423.78|1995-02-22|2-HIGH|Clerk#000000168|0|uickly silent theodolites. pe\n9891|1093|P|61268.10|1995-06-09|1-URGENT|Clerk#000000544|0|ut the theodolites. bold, even grouches across t\n9892|691|F|16104.32|1992-07-17|3-MEDIUM|Clerk#000000387|0|ts. blithe deposits wake carefully carefully\n9893|412|F|105380.78|1994-08-17|3-MEDIUM|Clerk#000000931|0|y special dependencies run\n9894|851|F|100473.64|1994-03-19|2-HIGH|Clerk#000000424|0| asymptotes along the regular, bold instructions eat quickly again\n9895|142|F|245503.17|1993-04-21|5-LOW|Clerk#000000965|0|lithely. furiously even pinto beans h\n9920|1073|O|75014.21|1996-06-06|1-URGENT|Clerk#000000007|0|ites. special requests believe against the furiously regular requests. furiou\n9921|1379|P|143610.80|1995-05-15|5-LOW|Clerk#000000936|0|r, blithe deposits. furiously bold\n9922|802|P|186264.65|1995-04-04|4-NOT SPECIFIED|Clerk#000000131|0|ajole blithely across the unusual, unusual packages. car\n9923|1217|F|223804.09|1994-10-06|2-HIGH|Clerk#000000818|0|instructions use slyl\n9924|799|O|192545.87|1997-05-09|4-NOT SPECIFIED|Clerk#000000639|0|onic excuses wake slyly\n9925|52|F|296109.93|1992-01-31|1-URGENT|Clerk#000000751|0|ve the always ironic pinto bean\n9926|254|F|225023.94|1994-10-12|2-HIGH|Clerk#000000090|0|sits believe carefully ironic pinto beans. sl\n9927|13|O|238640.42|1995-08-17|2-HIGH|Clerk#000000388|0|sts sleep regular, dogged packages. s\n9952|521|F|87414.24|1992-05-02|2-HIGH|Clerk#000000549|0|ully about the daringly pending idea\n9953|946|F|18034.69|1995-02-28|2-HIGH|Clerk#000000364|0|iresias. furiously bold deposits wake. furiously\n9954|19|F|207935.24|1994-06-20|4-NOT SPECIFIED|Clerk#000000651|0| furiously express packages. slyly ironic packages nag sly\n9955|1010|O|188555.97|1995-09-05|1-URGENT|Clerk#000000588|0|gainst the quickly regular deposits. carefully final accounts are furiously \n9956|530|F|38746.89|1993-03-17|2-HIGH|Clerk#000000001|0|s? ironic foxes wake even, ironic de\n9957|1465|F|64682.58|1993-12-01|4-NOT SPECIFIED|Clerk#000000874|0| unusual sauternes. fluffily e\n9958|764|F|192897.71|1993-11-26|4-NOT SPECIFIED|Clerk#000000199|0| idle depths. quickly even pearls are express theodolites. final pint\n9959|689|O|19023.25|1995-09-25|3-MEDIUM|Clerk#000000755|0|y special deposits. slyly final deposits against \n9984|376|O|232843.69|1998-07-10|1-URGENT|Clerk#000000161|0|ronic packages-- final packages boost! even, express frets boo\n9985|1207|F|82406.34|1993-08-08|5-LOW|Clerk#000000636|0|ing depths boost about the even depo\n9986|289|O|150409.70|1997-09-22|3-MEDIUM|Clerk#000000913|0|the ruthlessly regular courts. ironic pack\n9987|997|O|241701.18|1995-11-28|5-LOW|Clerk#000000308|0|ding to the regular foxes dazzle slyly f\n9988|1372|O|139134.68|1997-12-05|4-NOT SPECIFIED|Clerk#000000015|0|d, final foxes. fluffily even sheaves w\n9989|1210|F|113256.41|1993-06-22|3-MEDIUM|Clerk#000000298|0| bold orbits. pinto beans haggle carefully unusual accounts. furiously final \n9990|391|F|80673.96|1992-03-28|2-HIGH|Clerk#000000466|0|y express foxes. blithely\n9991|823|O|88850.20|1996-06-10|2-HIGH|Clerk#000000542|0|foxes nag fluffily packages. b\n10016|1295|F|29180.26|1993-02-02|1-URGENT|Clerk#000000546|0|ptotes. platelets across the blithely regular accounts us\n10017|811|O|100168.42|1998-04-09|4-NOT SPECIFIED|Clerk#000000617|0|ly accounts. carefully regular pinto beans wake according to th\n10018|307|F|85330.56|1993-06-10|3-MEDIUM|Clerk#000000855|0|ld accounts above the bold, regular requests ha\n10019|919|F|169596.33|1994-07-19|4-NOT SPECIFIED|Clerk#000000887|0|ests are carefully blithely ironic instructi\n10020|469|O|99184.61|1998-04-08|3-MEDIUM|Clerk#000000162|0|yly against the final warth\n10021|1282|F|50708.88|1992-08-01|4-NOT SPECIFIED|Clerk#000000321|0|ide of the slyly even Tiresias. carefully express fox\n10022|929|F|71058.00|1994-03-05|5-LOW|Clerk#000000445|0|nto beans. furiously silent deposits nag. bold requests are ironic \n10023|1204|O|107965.02|1996-12-02|1-URGENT|Clerk#000000596|0|onically even packages haggle among the slyly brave pin\n10048|803|F|63327.47|1994-05-16|4-NOT SPECIFIED|Clerk#000000893|0|nt pinto beans are. ironi\n10049|683|O|169774.94|1997-07-23|1-URGENT|Clerk#000000648|0|ly special forges. blithely bo\n10050|1046|O|151895.89|1996-09-03|2-HIGH|Clerk#000000530|0|bove the permanently regular multipliers. special req\n10051|661|O|168753.53|1996-05-25|2-HIGH|Clerk#000000804|0|ly. quickly unusual de\n10052|763|F|195461.73|1994-09-08|4-NOT SPECIFIED|Clerk#000000488|0|carefully final packages. ironic foxes cajo\n10053|1441|F|152632.75|1992-01-11|2-HIGH|Clerk#000000508|0|ual deposits haggle against the furiously final realms. b\n10054|665|O|111854.62|1995-04-29|2-HIGH|Clerk#000000093|0|furiously regular platele\n10055|1276|O|108949.78|1996-02-29|5-LOW|Clerk#000000508|0|e bravely bold notornis. carefully reg\n10080|109|F|219815.04|1993-02-13|5-LOW|Clerk#000000362|0|carefully blithely express epitaph\n10081|493|F|267898.00|1993-08-08|4-NOT SPECIFIED|Clerk#000000097|0|eans would sleep. carefully iro\n10082|1264|F|216021.80|1994-08-31|4-NOT SPECIFIED|Clerk#000000049|0|ayers use special excuses. special req\n10083|565|O|55168.12|1995-09-26|5-LOW|Clerk#000000949|0|t the brave decoys sleep regularly among the iron\n10084|1424|O|5317.21|1997-07-08|1-URGENT|Clerk#000000088|0|grow blithely packages. slyly final requests cajole; accounts \n10085|518|O|224630.63|1996-03-31|4-NOT SPECIFIED|Clerk#000000074|0|o beans wake against the blithely final accounts. fluffily silent f\n10086|397|O|51011.55|1995-05-16|5-LOW|Clerk#000000728|0|its wake alongside of the f\n10087|404|O|78013.94|1997-03-31|2-HIGH|Clerk#000000001|0|ounts integrate despite the regular ideas. furiously quiet idea\n10112|223|F|35243.42|1993-12-21|5-LOW|Clerk#000000586|0| deposits. furiously even asymptotes alongside of the slyly regular \n10113|56|F|193866.35|1994-06-13|1-URGENT|Clerk#000000094|0|e quickly. regular accounts boost bravely regular instructions. quickly fina\n10114|1064|F|46899.44|1993-02-09|1-URGENT|Clerk#000000757|0|al requests. quickly express de\n10115|799|O|144986.20|1998-07-07|2-HIGH|Clerk#000000530|0|even accounts. express ideas \n10116|118|O|222761.53|1997-03-26|5-LOW|Clerk#000000991|0|mptotes. carefully express depo\n10117|734|F|130230.17|1993-11-17|3-MEDIUM|Clerk#000000343|0|ilent theodolites sleep furiously\n10118|742|F|24563.35|1994-04-11|1-URGENT|Clerk#000000431|0|ithely pending foxes integrate across the fin\n10119|509|O|51762.72|1996-07-11|3-MEDIUM|Clerk#000000868|0|ar depths. slyly sly theodolites sleep slyly. furiously bold foxes integrate \n10144|311|F|235400.84|1995-01-03|1-URGENT|Clerk#000000751|0|ironic accounts cajole blithely around the blithe deposits. ir\n10145|646|P|302026.89|1995-05-25|5-LOW|Clerk#000000550|0|y unusual deposits sleep furiously i\n10146|994|F|237459.34|1994-05-02|5-LOW|Clerk#000000653|0|hin gifts nag. furiously pending deposits after the sly frets sl\n10147|1166|F|203640.84|1993-10-10|2-HIGH|Clerk#000000212|0|ilently ironic pinto beans. \n10148|1462|F|140605.75|1994-03-26|5-LOW|Clerk#000000334|0|the bold ideas are carefully ruthlessly special pac\n10149|26|F|169118.62|1994-09-24|2-HIGH|Clerk#000000681|0|final sentiments. slyly special bra\n10150|1325|F|291469.33|1992-04-30|1-URGENT|Clerk#000000802|0|r the requests sleep s\n10151|589|F|10295.06|1992-01-15|1-URGENT|Clerk#000000645|0|ideas. deposits haggle quickly accounts; f\n10176|985|O|102621.47|1996-08-26|3-MEDIUM|Clerk#000000456|0|unts. bold accounts are carefully according to the quickly p\n10177|568|O|209213.44|1996-06-29|3-MEDIUM|Clerk#000000221|0|nstructions; bold, regular attainments among th\n10178|1148|F|46311.36|1993-12-15|4-NOT SPECIFIED|Clerk#000000756|0|ray across the ironic, final dependencies.\n10179|1157|O|153533.06|1996-04-30|4-NOT SPECIFIED|Clerk#000000803|0|sly bold packages haggle about the the\n10180|712|O|76230.32|1997-03-10|1-URGENT|Clerk#000000743|0|ke blithely carefully even packag\n10181|874|O|46249.63|1996-02-13|3-MEDIUM|Clerk#000000866|0|, pending deposits. express\n10182|268|F|199446.10|1994-05-25|5-LOW|Clerk#000000304|0|s. slyly final pinto beans wake a\n10183|250|F|110520.40|1994-11-25|1-URGENT|Clerk#000000534|0| dependencies. blithely pending requests about the furiously regular pac\n10208|1028|O|286549.53|1996-08-26|3-MEDIUM|Clerk#000000259|0| ideas. slow, ironic instructions doze furiou\n10209|1210|F|400191.77|1993-11-30|3-MEDIUM|Clerk#000000153|0|ts wake. slyly blithe\n10210|937|O|148557.25|1995-06-24|4-NOT SPECIFIED|Clerk#000000573|0|ts. furiously bold ideas about the furiously bold pac\n10211|797|O|192242.04|1997-07-29|4-NOT SPECIFIED|Clerk#000000363|0| careful theodolites. special accounts caj\n10212|1471|F|126475.34|1993-05-24|5-LOW|Clerk#000000011|0|uickly according to the pending pinto beans. instructions brea\n10213|1288|F|42898.55|1995-02-18|2-HIGH|Clerk#000000845|0|ly ironic dependencies. final excuses acros\n10214|913|O|145287.98|1996-01-30|2-HIGH|Clerk#000000991|0|encies after the deposits sleep furiously bold packages. even deposits \n10215|1031|O|156012.73|1996-08-17|1-URGENT|Clerk#000000770|0|pecial instructions. fluffily regular packa\n10240|691|F|41343.99|1994-05-16|4-NOT SPECIFIED|Clerk#000000109|0|al accounts are along the unusual packages. unusual, even platelets use slyl\n10241|271|O|54454.72|1997-09-30|5-LOW|Clerk#000000366|0|y unusual asymptotes. regular dependencies wake fluffily about the\n10242|101|F|193761.97|1993-12-13|3-MEDIUM|Clerk#000000234|0|s was slyly alongside of the deposits.\n10243|385|O|72189.37|1995-12-25|2-HIGH|Clerk#000000844|0|nic requests. boldly regular deposits haggle blithely. orbits integrate \n10244|298|O|129419.40|1996-04-04|3-MEDIUM|Clerk#000000459|0|lent pinto beans. furiously iro\n10245|587|F|268471.58|1995-02-14|3-MEDIUM|Clerk#000000785|0| beans. foxes haggle around the car\n10246|1231|O|188201.88|1997-06-19|1-URGENT|Clerk#000000571|0|s. blithely unusual packages affix according to the hockey players. regular\n10247|866|F|187970.24|1993-01-11|3-MEDIUM|Clerk#000000646|0|fully after the carefully pending requests. even, pending ideas alongside \n10272|730|F|303855.22|1994-01-18|3-MEDIUM|Clerk#000000680|0| blithely express accounts. slyly regular pinto beans use slyly. \n10273|112|O|272748.60|1998-04-24|1-URGENT|Clerk#000000303|0|n, ironic packages. slyly close accounts sleep. blithe\n10274|1369|F|50775.14|1993-12-10|1-URGENT|Clerk#000000939|0|sits. fluffily bold requests cajole accounts\n10275|83|O|78422.44|1997-05-16|5-LOW|Clerk#000000202|0|fts at the furiously express accounts are whithout the slyly slow deposits. f\n10276|1463|O|264683.22|1996-02-24|3-MEDIUM|Clerk#000000706|0|y even deposits. even accounts nag among the ironic asymptotes. req\n10277|538|F|139188.55|1994-04-08|3-MEDIUM|Clerk#000000655|0|counts sleep around the special\n10278|359|O|162008.27|1995-09-13|5-LOW|Clerk#000000697|0|le quickly. pending, bold theo\n10279|1223|O|227770.72|1996-09-22|5-LOW|Clerk#000000051|0|nal deposits. fluffily silent ideas are across the\n10304|814|O|302630.21|1995-11-11|3-MEDIUM|Clerk#000000230|0|l deposits nag pending, regular attainments. ironic ideas detect. unusu\n10305|25|F|153676.38|1994-07-08|5-LOW|Clerk#000000429|0|phins use furiously about the quickly bold id\n10306|152|F|91506.19|1993-07-22|5-LOW|Clerk#000000345|0| slyly never final requests. perm\n10307|298|O|230682.65|1998-01-28|4-NOT SPECIFIED|Clerk#000000488|0|yly ironic foxes. quickly regular\n10308|413|P|278555.38|1995-04-21|2-HIGH|Clerk#000001000|0|are above the furiously final deposits. special packages nag s\n10309|1312|F|215890.35|1994-11-22|5-LOW|Clerk#000000622|0| above the carefully bold packages. carefully even pinto beans mai\n10310|1027|F|213874.91|1994-02-06|5-LOW|Clerk#000000645|0| accounts kindle qu\n10311|52|F|5195.95|1992-08-18|2-HIGH|Clerk#000000147|0|ntegrate carefully above the regular pinto beans. quick\n10336|1358|O|57610.67|1997-06-15|4-NOT SPECIFIED|Clerk#000000519|0|. carefully special pinto beans are ironic accounts. foxes \n10337|1360|O|63068.37|1996-03-19|3-MEDIUM|Clerk#000000499|0|ly silent instructions wake qui\n10338|1213|O|100334.47|1995-07-15|4-NOT SPECIFIED|Clerk#000000964|0|gular packages. slyly even packages haggle furious\n10339|154|O|116081.18|1995-10-12|5-LOW|Clerk#000000585|0|ts. always ironic deposits haggle thinly.\n10340|1486|O|243706.09|1995-06-23|2-HIGH|Clerk#000000064|0|hely final warthogs detect blithely regular pa\n10341|1276|F|250921.23|1992-12-22|2-HIGH|Clerk#000000964|0|efully final packages cajole f\n10342|638|O|273325.12|1995-11-23|3-MEDIUM|Clerk#000000277|0|theodolites. carefully regul\n10343|91|F|44177.42|1992-03-27|4-NOT SPECIFIED|Clerk#000000600|0|ackages. carefully pending idea\n10368|508|O|64465.48|1996-06-25|3-MEDIUM|Clerk#000000722|0|carefully special theodolites doze. fluffily ironic pinto be\n10369|1397|O|120738.63|1995-11-10|5-LOW|Clerk#000000636|0| deposits are quickly; regular requests nag carefully regular i\n10370|839|F|112585.48|1994-04-28|4-NOT SPECIFIED|Clerk#000000084|0|thrash. accounts boost never quickly final ac\n10371|1022|F|204611.98|1994-09-17|2-HIGH|Clerk#000000231|0|nding requests. ideas af\n10372|1241|O|101993.71|1995-06-28|4-NOT SPECIFIED|Clerk#000000276|0|sleep after the carefully ironic platelets. regular, unusual deposits wake qui\n10373|671|F|254861.41|1993-06-26|1-URGENT|Clerk#000000681|0|cajole blithely final foxes. carefully \n10374|337|F|28405.59|1993-07-23|5-LOW|Clerk#000000885|0|ing courts? carefully unusual ideas\n10375|814|O|55990.35|1997-01-08|2-HIGH|Clerk#000000384|0|ending deposits. special, regular tith\n10400|230|O|214820.02|1996-10-24|2-HIGH|Clerk#000000375|0|cing accounts sleep slyly regu\n10401|1405|O|15159.38|1998-07-20|4-NOT SPECIFIED|Clerk#000000351|0|ts x-ray? slyly unusual a\n10402|7|O|69976.43|1997-12-20|1-URGENT|Clerk#000000487|0|ing pinto beans for the carefully regular\n10403|820|O|273217.58|1996-05-21|5-LOW|Clerk#000000523|0|ounts. carefully special accounts grow\n10404|1255|O|148128.43|1998-03-29|1-URGENT|Clerk#000000142|0|uffy foxes about the qui\n10405|929|O|156274.53|1998-01-04|4-NOT SPECIFIED|Clerk#000000343|0|uriously final dolphins. sly\n10406|46|O|143470.97|1996-02-29|4-NOT SPECIFIED|Clerk#000000923|0| the slyly express deposits. deposits\n10407|442|O|72476.48|1996-03-26|4-NOT SPECIFIED|Clerk#000000011|0|onic foxes. asymptotes across the ironi\n10432|1231|F|167753.06|1992-01-06|5-LOW|Clerk#000000429|0|thely bold pinto bea\n10433|1295|F|19194.57|1993-02-20|2-HIGH|Clerk#000000566|0|ajole blithely. furiously re\n10434|781|F|63237.89|1994-12-24|2-HIGH|Clerk#000000939|0| blithely regular ideas are deposits. requests nod slyly final r\n10435|1195|F|185835.34|1993-08-11|3-MEDIUM|Clerk#000000660|0|te. slyly final theodolites are after th\n10436|916|F|32528.92|1995-01-03|5-LOW|Clerk#000001000|0|lthily special asymptotes. blithely pending deposits after the unusual, e\n10437|305|F|64469.14|1994-08-19|1-URGENT|Clerk#000000098|0|inal requests about the regular,\n10438|715|F|189906.48|1992-12-03|5-LOW|Clerk#000000148|0|tect among the blithely \n10439|940|F|300103.82|1992-03-29|4-NOT SPECIFIED|Clerk#000000626|0|nal, even requests h\n10464|233|F|41683.39|1994-08-11|1-URGENT|Clerk#000000752|0|e slyly after the platelets; furiously even ac\n10465|79|O|289512.24|1997-04-10|4-NOT SPECIFIED|Clerk#000000791|0|ic tithes sleep carefully across the furiously ironic ideas. sl\n10466|616|F|147826.40|1992-12-26|2-HIGH|Clerk#000000899|0|thely even ideas cajole quickly across the carefu\n10467|1088|O|200254.31|1997-03-12|4-NOT SPECIFIED|Clerk#000000125|0|ffily unusual deposits. carefully regular asymptotes use fur\n10468|574|F|50946.66|1994-12-12|2-HIGH|Clerk#000000429|0|osits wake-- quickly pending foxes alongside of the slyly even\n10469|622|O|89980.03|1996-09-03|2-HIGH|Clerk#000000913|0|hely unusual, unusual accounts. furiously fi\n10470|184|F|321900.81|1992-05-05|5-LOW|Clerk#000000632|0|even, ironic accounts among the bold theodolites use across the ste\n10471|262|F|309852.68|1992-10-10|2-HIGH|Clerk#000000590|0|lites wake doggedly accounts. blithely final deposits sleep evenly. \n10496|400|O|66825.60|1997-02-20|2-HIGH|Clerk#000000907|0|iously regular requests. furiously slow hockey players are quickly even ac\n10497|119|O|103079.38|1996-06-22|3-MEDIUM|Clerk#000000423|0|ar hockey players sleep furiously until the furious\n10498|1081|F|146950.27|1993-10-24|5-LOW|Clerk#000000424|0|lithely bold packages. re\n10499|872|F|186269.45|1993-11-17|4-NOT SPECIFIED|Clerk#000000065|0|es haggle slyly even instructions. ironica\n10500|998|F|26808.34|1994-01-24|2-HIGH|Clerk#000000179|0| packages wake quickly pearls. slyly even deposits haggle blithe\n10501|911|O|227512.69|1998-07-03|3-MEDIUM|Clerk#000000977|0|equests. packages nag slyly. quickly even accounts toward the ironic\n10502|1462|F|177583.01|1993-05-15|4-NOT SPECIFIED|Clerk#000000530|0|tructions alongside of the blithely bold foxes could have to sleep slyly a\n10503|853|O|150884.39|1996-06-09|5-LOW|Clerk#000000028|0|eodolites play against the pending accounts. special packages are slyly ab\n10528|166|F|127054.41|1994-09-14|3-MEDIUM|Clerk#000000724|0|press pinto beans. express theodolites do cajole furiously. slyly f\n10529|692|O|141709.31|1997-10-18|1-URGENT|Clerk#000000573|0|yly. doggedly final epitaphs haggle among the flu\n10530|1276|F|120484.95|1994-01-28|3-MEDIUM|Clerk#000000879|0|ounts. blithely unus\n10531|55|F|176261.61|1992-03-19|2-HIGH|Clerk#000000021|0|regular somas. fluffily special instructions doze blithely. quick\n10532|1337|O|318050.97|1997-11-28|5-LOW|Clerk#000000805|0|ly slyly bold accounts. carefully express theodolites nag\n10533|961|O|186070.73|1998-05-01|3-MEDIUM|Clerk#000000778|0| nag busily bold dependencies. special, ironic p\n10534|1070|F|4438.69|1993-04-19|4-NOT SPECIFIED|Clerk#000000973|0|ly according to the slyly r\n10535|1225|O|249751.99|1995-05-28|5-LOW|Clerk#000000967|0|lyly unusual frets nag alongsi\n10560|91|O|185333.40|1997-07-29|1-URGENT|Clerk#000000157|0|lar requests among the blithely regular packages doze unusual requests; \n10561|718|O|219244.25|1997-04-27|5-LOW|Clerk#000000435|0|r requests. even, even deposits b\n10562|1246|F|297373.68|1994-11-12|4-NOT SPECIFIED|Clerk#000000034|0|ests across the courts print r\n10563|2|F|143707.70|1993-09-30|1-URGENT|Clerk#000000889|0|etect after the pending requests. packa\n10564|1019|O|77547.24|1996-03-14|4-NOT SPECIFIED|Clerk#000000362|0|ests. final, express foxes are slyly regular ideas. darin\n10565|613|O|138255.60|1997-08-17|1-URGENT|Clerk#000000126|0| slyly unusual deposits after the carefully \n10566|578|O|220319.71|1995-08-20|4-NOT SPECIFIED|Clerk#000000541|0|s sleep blithely permanently even ideas. bl\n10567|328|F|104838.06|1992-03-30|2-HIGH|Clerk#000000046|0|y even pinto beans cajole bold instructions. carefully sly wate\n10592|778|O|84645.88|1995-11-04|2-HIGH|Clerk#000000435|0|as-- ironic packages \n10593|1123|F|160662.68|1992-02-13|2-HIGH|Clerk#000000573|0|excuses after the fina\n10594|214|O|6650.94|1996-03-21|3-MEDIUM|Clerk#000000126|0|ly express asymptotes against th\n10595|818|O|23233.65|1997-11-22|3-MEDIUM|Clerk#000000425|0|gular excuses. slowly ca\n10596|308|O|106243.04|1997-03-31|5-LOW|Clerk#000000662|0|oss the furiously regular theodolit\n10597|619|O|67904.94|1996-09-13|2-HIGH|Clerk#000000040|0|ch. bold ideas about the deposits wake fluffily \n10598|461|F|19331.61|1992-03-08|4-NOT SPECIFIED|Clerk#000000682|0|e among the fluffily regular deposits. slyly final requests haggle furious\n10599|194|O|142776.50|1997-07-21|4-NOT SPECIFIED|Clerk#000000559|0|ly regular requests wake regular instructions. pending, pending dolphins\n10624|1288|F|192178.14|1994-12-21|1-URGENT|Clerk#000000666|0|re furiously along the slyly final ideas. carefu\n10625|259|O|233381.66|1996-01-30|1-URGENT|Clerk#000000688|0|. pending deposits above the car\n10626|997|O|19103.82|1995-12-14|3-MEDIUM|Clerk#000000656|0| regular deposits cajo\n10627|127|F|210497.85|1993-02-25|5-LOW|Clerk#000000236|0|s. unusual instructions are. furiously express requests a\n10628|1429|O|5714.19|1998-06-28|4-NOT SPECIFIED|Clerk#000000437|0|etimes quickly final packa\n10629|650|F|55072.00|1994-09-12|1-URGENT|Clerk#000000229|0|endencies wake. accounts are carefully. a\n10630|313|F|143006.19|1993-02-05|3-MEDIUM|Clerk#000000607|0|iously special instruct\n10631|1243|F|258430.09|1993-05-27|1-URGENT|Clerk#000000844|0|affix blithely blithely regular dinos. furiously blithe instructions ar\n10656|371|O|96681.23|1995-10-25|1-URGENT|Clerk#000000233|0| accounts use never blithely bold deposits. i\n10657|734|F|91096.35|1993-09-26|3-MEDIUM|Clerk#000000502|0|ts wake. bold packages cajole fluffily according to the furi\n10658|1199|F|117872.35|1994-08-28|5-LOW|Clerk#000000204|0|xes cajole thinly ironic requests. theodolites \n10659|397|F|290769.79|1994-05-15|1-URGENT|Clerk#000000587|0| slyly across the bold request\n10660|469|O|135068.75|1998-05-01|2-HIGH|Clerk#000000352|0|onic depths? furiously\n10661|179|F|90250.70|1994-01-24|4-NOT SPECIFIED|Clerk#000000986|0| about the final foxes. eve\n10662|1420|P|157252.72|1995-05-13|5-LOW|Clerk#000000241|0|tain blithely quickly regular pinto beans. furiously bold deposits \n10663|317|F|153682.58|1994-05-24|2-HIGH|Clerk#000000137|0|s above the furiously final foxes nag idly requests. caref\n10688|4|F|43453.24|1992-05-13|3-MEDIUM|Clerk#000000226|0|ly ironic instructions lose quickly alongside of the carefully \n10689|203|O|99242.62|1996-07-23|5-LOW|Clerk#000000436|0|usly. furiously ironic instructions wak\n10690|103|O|54775.41|1997-12-15|5-LOW|Clerk#000000041|0|ltipliers cajole slyly busy accounts. express theodolites af\n10691|664|F|87254.45|1995-03-14|3-MEDIUM|Clerk#000000536|0|al decoys among the carefull\n10692|187|F|158201.74|1994-07-27|5-LOW|Clerk#000000832|0|orbits. asymptotes cajole. furiously eve\n10693|1030|F|43518.47|1995-04-17|4-NOT SPECIFIED|Clerk#000000797|0|s. slyly regular accounts are among the slyly regular sentiments. \n10694|1423|O|183151.32|1997-12-05|2-HIGH|Clerk#000000812|0|olites. quickly bold package\n10695|1456|P|93566.64|1995-05-03|2-HIGH|Clerk#000000039|0| asymptotes. quickly unus\n10720|736|O|89368.05|1998-03-31|2-HIGH|Clerk#000000597|0|ccounts sleep bold, even theodol\n10721|781|O|107381.35|1996-02-27|5-LOW|Clerk#000000599|0|haggle slyly blithely even instructions. quickly even dolphins wa\n10722|79|F|218680.31|1995-01-20|4-NOT SPECIFIED|Clerk#000000743|0|ding instructions print. f\n10723|1040|O|69126.13|1998-05-21|3-MEDIUM|Clerk#000000144|0| bold accounts. carefully busy platelets sleep fluffily. spe\n10724|1492|F|215958.98|1994-09-14|2-HIGH|Clerk#000000165|0|furiously regular dolphins. regular foxes along the daringly express \n10725|1444|O|77193.41|1998-04-09|5-LOW|Clerk#000000505|0| carefully unusual sauternes after the fluffily regular foxes use pending,\n10726|164|F|215356.45|1993-04-25|4-NOT SPECIFIED|Clerk#000000210|0| regular dependencies. quickly spe\n10727|496|F|311532.26|1992-08-05|5-LOW|Clerk#000000396|0|nusual pinto beans. special, pending requests breach slyly fina\n10752|199|O|223930.05|1995-09-11|1-URGENT|Clerk#000000610|0|ic accounts. carefully ironic courts against the caref\n10753|1430|F|20768.66|1994-05-02|1-URGENT|Clerk#000000218|0|ously ironic packag\n10754|1061|F|229997.58|1993-03-18|5-LOW|Clerk#000000827|0|r pinto beans sleep even \n10755|1435|F|132981.98|1993-07-15|1-URGENT|Clerk#000000257|0|se furiously excuses. always unusual packages above the furiously unusual de\n10756|1087|F|40769.85|1992-04-15|2-HIGH|Clerk#000000994|0| even, express platelets nag above\n10757|128|O|92703.05|1998-06-06|4-NOT SPECIFIED|Clerk#000001000|0|patterns haggle quickly across the bravely\n10758|368|O|136484.05|1996-11-15|2-HIGH|Clerk#000000209|0|es. pinto beans sleep carefully final \n10759|34|O|54534.36|1996-09-22|4-NOT SPECIFIED|Clerk#000000258|0| boost evenly final pinto beans.\n10784|1396|F|184632.93|1993-07-06|4-NOT SPECIFIED|Clerk#000000982|0|en packages sleep. final foxes detec\n10785|148|F|1508.63|1992-06-26|3-MEDIUM|Clerk#000000948|0|iously unusual packages use quickly furiously \n10786|154|O|70962.05|1995-08-06|3-MEDIUM|Clerk#000000705|0| ironic, special theodolites caj\n10787|1117|O|372954.40|1997-02-12|2-HIGH|Clerk#000000133|0|ckages could have to wake blithely above the final requests. furiously \n10788|4|O|147767.08|1997-01-15|3-MEDIUM|Clerk#000000905|0|esides the slyly ironic dependencies. ironic foxes cajole fur\n10789|1378|F|93918.24|1993-08-24|4-NOT SPECIFIED|Clerk#000000463|0|s unwind blithely agains\n10790|113|F|77512.26|1994-12-15|5-LOW|Clerk#000000381|0|ly alongside of the express, regular foxes. req\n10791|319|O|173850.27|1995-08-20|4-NOT SPECIFIED|Clerk#000000131|0|efully special pinto beans dazzle fluffily according to the express, silen\n10816|1303|O|45628.88|1996-01-11|2-HIGH|Clerk#000000003|0|after the final, spe\n10817|712|O|213714.56|1996-12-09|1-URGENT|Clerk#000000852|0|final ideas across the\n10818|157|O|245383.91|1998-05-07|2-HIGH|Clerk#000000735|0|ids nag carefully against the final deposit\n10819|571|F|79971.29|1993-08-25|1-URGENT|Clerk#000000144|0|ncies use furiously alongside of the fluffily final instructions. quickly re\n10820|661|O|220224.56|1995-12-28|4-NOT SPECIFIED|Clerk#000000630|0|ecial ideas. silently unusual somas are blith\n10821|590|O|179780.96|1996-05-13|2-HIGH|Clerk#000000856|0| the furiously final excuses. blithely final pack\n10822|1186|F|19208.70|1993-12-01|5-LOW|Clerk#000000046|0|hely regular requests. carefully silent instructions use slyly f\n10823|391|F|109958.79|1992-06-13|5-LOW|Clerk#000000869|0|blithely boldly pending deposits. silent hockey players acros\n10848|841|O|67233.17|1996-10-26|3-MEDIUM|Clerk#000000131|0| according to the boldly final accounts. slyly bold asymptotes detect qui\n10849|56|O|309269.38|1997-01-31|1-URGENT|Clerk#000000279|0|y even requests. blithely silent theodolites cajole carefully ev\n10850|1052|O|78474.67|1996-09-23|2-HIGH|Clerk#000000055|0|its cajole quickly. permanently even pinto beans dazz\n10851|1201|F|256653.25|1994-04-07|2-HIGH|Clerk#000000841|0|ffix blithely blithely even dugouts? quickly re\n10852|559|F|290873.68|1993-10-27|2-HIGH|Clerk#000000623|0|oxes. carefully final pinto be\n10853|275|F|68787.35|1994-01-06|4-NOT SPECIFIED|Clerk#000000276|0|ronic dugouts wake s\n10854|917|O|4885.38|1998-06-20|2-HIGH|Clerk#000000219|0|old, ironic dependencies. fluffily bold excuses shall have to integrate amon\n10855|608|O|227086.67|1997-07-16|1-URGENT|Clerk#000000149|0|fully unusual pinto beans sleep sl\n10880|278|F|40046.10|1992-05-23|4-NOT SPECIFIED|Clerk#000000553|0|e furiously bold deposits. slyly spe\n10881|1001|F|235385.68|1994-02-16|1-URGENT|Clerk#000000872|0|quickly final pinto beans engage quickly pending, final \n10882|1462|O|103982.52|1998-06-02|5-LOW|Clerk#000000949|0|gouts sleep fluffily according to the dep\n10883|226|O|118687.62|1998-02-03|5-LOW|Clerk#000000759|0|sly silent accounts. carefully express pinto bean\n10884|749|F|193592.93|1994-11-26|5-LOW|Clerk#000000802|0| express, regular accounts nag furiously! platele\n10885|991|O|253167.35|1998-04-18|5-LOW|Clerk#000000893|0|thely ironic accounts sleep carefully depo\n10886|1312|F|275313.31|1994-08-24|1-URGENT|Clerk#000000422|0|along the final epitaphs dazzle carefull\n10887|436|O|50293.15|1995-05-26|4-NOT SPECIFIED|Clerk#000000575|0|ag against the dependencies. furiously regular foxes sleep fu\n10912|293|O|243339.25|1996-12-25|1-URGENT|Clerk#000000579|0|odolites wake sometimes on the unusual deposits. epitaphs cajole b\n10913|514|F|125458.51|1993-02-07|5-LOW|Clerk#000000114|0|ches are blithely around t\n10914|689|F|213268.60|1995-01-23|4-NOT SPECIFIED|Clerk#000000582|0| quickly regular packages above the furiously bold pl\n10915|1357|F|178357.32|1993-11-11|5-LOW|Clerk#000000649|0|bold packages nag. furiously regular excu\n10916|328|P|253026.49|1995-03-11|3-MEDIUM|Clerk#000000047|0|pinto beans cajole furiously according to the dependencies. fi\n10917|251|F|127026.23|1992-02-11|1-URGENT|Clerk#000000078|0|beans across the regular theodolites affix silent instructions. even deposits\n10918|1030|O|80094.04|1995-10-16|3-MEDIUM|Clerk#000000874|0|structions. ironic, express dep\n10919|1339|F|56302.36|1993-03-28|2-HIGH|Clerk#000000180|0|press requests haggle blithely. blithely \n10944|319|O|129953.86|1995-11-13|2-HIGH|Clerk#000000077|0|ily unusual requests. special instruct\n10945|248|F|168422.71|1992-01-04|3-MEDIUM|Clerk#000000296|0|ages are. final, silent deposits haggle quickly! ironic \n10946|1147|O|332313.82|1997-03-22|3-MEDIUM|Clerk#000000975|0|nal deposits detect among the quickly\n10947|1070|O|135370.80|1996-01-07|2-HIGH|Clerk#000000347|0|ar excuses x-ray fluffily a\n10948|670|F|223943.13|1992-08-14|3-MEDIUM|Clerk#000000489|0|nic grouches detect stealthily fluffily even de\n10949|331|F|112812.10|1994-03-31|3-MEDIUM|Clerk#000000874|0|luffily even grouches. regular notornis against the sl\n10950|767|O|37856.60|1997-06-11|3-MEDIUM|Clerk#000000015|0|y carefully pending packages. silent accounts nod careful\n10951|1159|F|175198.95|1992-11-07|4-NOT SPECIFIED|Clerk#000000619|0|dependencies. sometimes pending foxes cajole. quickly \n10976|685|F|186488.10|1992-10-02|4-NOT SPECIFIED|Clerk#000000282|0| deposits: brave, ironic packages maintain fluffily about the regular \n10977|757|O|99211.94|1998-06-04|2-HIGH|Clerk#000000089|0|rious accounts about the regular excuses wake ironically special r\n10978|1346|F|106794.18|1994-04-02|1-URGENT|Clerk#000000546|0|cial multipliers wake pending, pending theodolites: blithe\n10979|1267|O|166156.36|1995-10-05|3-MEDIUM|Clerk#000000527|0|ans cajole carefully. regular pinto be\n10980|754|O|75950.23|1996-07-29|4-NOT SPECIFIED|Clerk#000000818|0| ironic, stealthy patterns detect\n10981|919|F|235684.93|1993-05-15|2-HIGH|Clerk#000000341|0|ly bold attainments nag! furiously special acco\n10982|739|F|199304.65|1992-12-11|4-NOT SPECIFIED|Clerk#000000481|0|tithes use slyly silent, unusual a\n10983|599|O|106734.94|1997-02-06|1-URGENT|Clerk#000000299|0|sts sleep of the regular foxes. permanent re\n11008|523|F|222231.62|1994-02-25|1-URGENT|Clerk#000000927|0|ickly regular deposits detect fluffily: caref\n11009|1303|O|65554.60|1997-03-05|3-MEDIUM|Clerk#000000515|0|deposits are blithely furio\n11010|440|O|163532.15|1997-03-22|4-NOT SPECIFIED|Clerk#000000659|0|ithely-- express, final ideas sleep slyly to the blithely express reque\n11011|14|F|248409.17|1992-05-14|2-HIGH|Clerk#000000948|0|luffily even warhors\n11012|1420|O|114085.80|1998-04-07|1-URGENT|Clerk#000000771|0|ze fluffily quickly sly excuses. quickly pending frays haggle accordin\n11013|395|F|2591.65|1994-04-18|2-HIGH|Clerk#000000759|0|ng deposits nag. special deposits sublate furio\n11014|319|F|150733.48|1993-09-08|2-HIGH|Clerk#000000934|0|yly regular depths. re\n11015|514|O|132711.70|1997-09-16|5-LOW|Clerk#000000216|0|its cajole against the unusual ideas? sly deposits boost fluffily. furiously \n11040|82|O|211495.01|1995-10-24|4-NOT SPECIFIED|Clerk#000000003|0|arefully pending dolphins print brave\n11041|1306|O|128785.82|1997-03-30|4-NOT SPECIFIED|Clerk#000000892|0|instructions. blithely pending packages solve of the furiously ironic requests\n11042|857|O|223492.72|1998-05-07|4-NOT SPECIFIED|Clerk#000000677|0|ccounts. quickly unusual pinto beans nag enticingly a\n11043|820|F|102166.94|1995-02-20|1-URGENT|Clerk#000000196|0|dolphins. accounts are f\n11044|164|F|68884.04|1992-06-25|3-MEDIUM|Clerk#000000861|0|he deposits hinder above the furiously express excuses. silent depo\n11045|556|F|226703.02|1992-03-26|3-MEDIUM|Clerk#000000216|0|uriously ironic asy\n11046|1411|O|337558.78|1997-06-17|5-LOW|Clerk#000000454|0|lly special theodolites use furiously. carefully\n11047|175|F|70365.24|1993-12-11|5-LOW|Clerk#000000223|0|. pending accounts alongside o\n11072|265|O|207339.71|1996-07-14|2-HIGH|Clerk#000000804|0|osits: pending requests sleep quietly silently unusual deposits. special fo\n11073|548|F|177610.35|1994-12-02|4-NOT SPECIFIED|Clerk#000000009|0|ing deposits-- slyly express dolphins detect fluffily ironic dep\n11074|709|O|302642.18|1995-06-10|2-HIGH|Clerk#000000449|0|t the special asymptotes. furiously even dolphins sleep quickly ir\n11075|1244|O|215012.92|1996-03-05|5-LOW|Clerk#000000379|0|efully furiously special pinto beans. fluffily unusual acc\n11076|1012|F|54498.05|1994-08-18|1-URGENT|Clerk#000000361|0|lyly special notornis. bold requests haggle careful\n11077|610|F|76484.04|1994-03-21|5-LOW|Clerk#000000024|0|gularly pending deposits. quick\n11078|1301|F|103896.23|1992-05-28|2-HIGH|Clerk#000000227|0|ag. ironic, unusual accounts nag fluffily across the ironic, final ideas. car\n11079|218|O|98046.49|1997-08-11|3-MEDIUM|Clerk#000000507|0|tainments affix doggedly slyly special asymptotes.\n11104|1315|O|51132.08|1998-07-07|4-NOT SPECIFIED|Clerk#000000062|0|s cajole slyly alon\n11105|1124|O|20773.48|1995-11-30|4-NOT SPECIFIED|Clerk#000000769|0|blithely about the closely silent foxes. blithely r\n11106|1175|P|59390.31|1995-04-01|2-HIGH|Clerk#000000828|0|fter the quickly bold theodolites are blithely across\n11107|445|O|64188.60|1995-10-24|5-LOW|Clerk#000000896|0|ymptotes. escapades use slyly? slyly b\n11108|1186|F|238113.15|1992-08-17|4-NOT SPECIFIED|Clerk#000000137|0|ond the carefully unusual courts. regul\n11109|664|F|5341.59|1995-03-16|3-MEDIUM|Clerk#000000578|0|. silent platelets haggle slyly. quickly ironic asymptotes \n11110|757|O|153722.23|1996-06-23|4-NOT SPECIFIED|Clerk#000000243|0|es are quickly alongside of the spe\n11111|631|F|121957.72|1995-01-18|3-MEDIUM|Clerk#000000301|0|nic deposits affix quickly regula\n11136|1313|F|60530.16|1993-11-14|4-NOT SPECIFIED|Clerk#000000987|0|ular foxes haggle against the unusual frays. i\n11137|668|F|204264.90|1992-11-30|2-HIGH|Clerk#000000515|0|bout the final accounts. regular ideas cajole slyly. blithely sly d\n11138|181|F|162678.89|1995-02-27|4-NOT SPECIFIED|Clerk#000000983|0|d platelets. carefully ironic packages alongside of the carefully \n11139|883|O|115264.28|1998-05-17|5-LOW|Clerk#000000891|0|the furiously silent i\n11140|988|O|194048.42|1997-07-26|1-URGENT|Clerk#000000117|0|. fluffily busy sauternes cajole slyly above the quickly express excuses. exp\n11141|1030|O|72800.37|1998-01-17|1-URGENT|Clerk#000000084|0|ckages. carefully regul\n11142|1375|O|395039.05|1997-10-03|4-NOT SPECIFIED|Clerk#000000457|0| even accounts sublate carefully \n11143|97|F|172537.11|1992-11-27|5-LOW|Clerk#000000613|0|unusual instructions. q\n11168|1441|F|74602.03|1992-10-22|1-URGENT|Clerk#000000585|0|es. even patterns use fluff\n11169|1222|F|121781.45|1993-05-22|3-MEDIUM|Clerk#000000536|0|eodolites wake. fluffily express packages\n11170|736|O|152992.55|1997-07-30|1-URGENT|Clerk#000000832|0|eyond the furiously regular pinto beans. car\n11171|730|O|104672.68|1995-07-17|1-URGENT|Clerk#000000792|0|l deposits promise according to the express deposits. fluffily final r\n11172|1076|O|166824.37|1997-12-30|1-URGENT|Clerk#000000187|0|ructions. furiously pending dependencies wake. deposits\n11173|319|F|179045.62|1992-03-31|1-URGENT|Clerk#000000815|0|e ironically silent deposits wake blithely across the regular instructions\n11174|1315|F|149433.95|1994-04-10|3-MEDIUM|Clerk#000000321|0|se about the pending, special decoys-- fluffily express \n11175|1421|O|44114.12|1998-02-26|2-HIGH|Clerk#000000812|0| slyly even deposits are blithely; packages integrate furiously. bravely ironi\n11200|956|O|113819.40|1997-02-07|1-URGENT|Clerk#000000417|0|ajole. silent requests boost carefully blithely thin multiplier\n11201|632|O|3297.46|1996-12-08|3-MEDIUM|Clerk#000000746|0|ns cajole after the pending pinto beans. carefully ironic requests wake r\n11202|1168|F|157836.48|1992-03-18|3-MEDIUM|Clerk#000000614|0|wake daringly. carefully regular account\n11203|1498|O|84795.43|1996-06-15|2-HIGH|Clerk#000000010|0|al instructions haggle slyly. fu\n11204|1249|O|29604.53|1998-07-25|2-HIGH|Clerk#000000752|0| express requests. slyly special packages cajole fluffily; quickly ironi\n11205|1294|O|93334.58|1996-09-15|1-URGENT|Clerk#000000800|0|ckages. furiously final packages ac\n11206|379|F|209373.08|1994-12-29|5-LOW|Clerk#000000139|0|ending packages cajole busily. slyly \n11207|1213|O|84129.92|1996-04-03|2-HIGH|Clerk#000000354|0|ress accounts haggle permanently about the furiously unusua\n11232|661|O|77549.43|1996-02-01|1-URGENT|Clerk#000000676|0|fully regular accounts. final, unusual deposits sleep eve\n11233|680|O|96728.84|1995-08-31|4-NOT SPECIFIED|Clerk#000000820|0|lphins. carefully ironic ideas use after the quickly unusual a\n11234|1153|F|117092.84|1993-03-01|4-NOT SPECIFIED|Clerk#000000291|0|e slyly across the carefully even requests. slyly \n11235|590|O|278256.83|1996-04-03|4-NOT SPECIFIED|Clerk#000000495|0|ackages solve carefully furiously final accounts. fluf\n11236|223|F|210654.72|1993-10-31|2-HIGH|Clerk#000000792|0|ess pinto beans. foxes b\n11237|1423|O|155158.38|1996-06-16|4-NOT SPECIFIED|Clerk#000000627|0|e the carefully special ideas inte\n11238|535|O|126764.00|1996-07-23|1-URGENT|Clerk#000000809|0|gular foxes run after the even instructions. regular instructions wake. unu\n11239|475|F|310241.51|1992-02-10|1-URGENT|Clerk#000000240|0|e regular, even excuses.\n11264|676|O|152483.91|1996-09-02|4-NOT SPECIFIED|Clerk#000000227|0|usly silent deposits. carefully unusual asymptotes ha\n11265|905|O|88998.81|1997-05-20|3-MEDIUM|Clerk#000000427|0|ias. carefully pending platel\n11266|292|O|13609.27|1997-08-10|4-NOT SPECIFIED|Clerk#000000315|0|lets nag about the carefully special packages. ironic sheaves \n11267|548|F|45572.31|1992-02-15|5-LOW|Clerk#000000096|0| wake slyly even deposits. c\n11268|43|O|189596.87|1998-06-30|1-URGENT|Clerk#000000130|0|ptotes are blithely slyly silen\n11269|199|F|256118.53|1992-05-27|5-LOW|Clerk#000000752|0|ath the decoys. final excuses must have to wake. ironic asymptotes wake q\n11270|160|O|197781.66|1995-07-01|2-HIGH|Clerk#000000757|0|ily pending theodolites a\n11271|745|O|255335.88|1995-10-19|2-HIGH|Clerk#000000144|0|sts. furiously ironic foxes wake ca\n11296|832|F|378166.33|1992-01-10|1-URGENT|Clerk#000000966|0|s-- ironic, unusual requests haggle furiously. carefully special depend\n11297|694|F|109722.85|1993-01-09|4-NOT SPECIFIED|Clerk#000000247|0|hely. express escapades kindle blithely. even requests wake\n11298|805|O|228663.94|1998-01-03|2-HIGH|Clerk#000000160|0| blithely express ideas sleep slyly ruthl\n11299|50|F|253114.92|1993-11-13|4-NOT SPECIFIED|Clerk#000000885|0|ns haggle bravely alongside of the fluffily express requests.\n11300|346|O|77247.48|1996-07-10|5-LOW|Clerk#000000697|0|he final platelets. blithely even theodolites along the car\n11301|1358|F|191271.29|1992-03-23|3-MEDIUM|Clerk#000000321|0|hes detect fluffily ironic requests. deposits br\n11302|451|F|243997.09|1994-03-02|5-LOW|Clerk#000000492|0|. never ironic requests sleep furiousl\n11303|388|F|92968.88|1992-03-30|5-LOW|Clerk#000000788|0|he slyly pending requests. final deposits \n11328|466|F|18702.67|1992-03-21|1-URGENT|Clerk#000000101|0|theodolites. blithely pending accounts above the carefully\n11329|1285|O|272307.07|1995-08-05|1-URGENT|Clerk#000000903|0|on the daringly express instructions. fluf\n11330|281|F|3817.37|1992-03-28|5-LOW|Clerk#000000429|0|he slyly ironic ideas are q\n11331|179|O|18118.60|1996-01-21|3-MEDIUM|Clerk#000000616|0|platelets wake carefully ruthless requests. silent deposits believe \n11332|422|F|213792.99|1994-11-17|5-LOW|Clerk#000000572|0|o the unusual foxes-- ironic requests wake slyly enticingly re\n11333|103|F|15730.53|1994-06-10|2-HIGH|Clerk#000000914|0|excuses cajole blithely against the slyly express r\n11334|245|O|343561.63|1997-07-29|3-MEDIUM|Clerk#000000843|0|ronic deposits haggle fu\n11335|871|F|133549.00|1994-10-22|2-HIGH|Clerk#000000669|0|ealms. theodolites maintain. regular, even instructions against t\n11360|886|O|67314.59|1997-09-07|1-URGENT|Clerk#000000561|0|. slyly regular deposits lose\n11361|442|F|251600.98|1994-09-28|1-URGENT|Clerk#000000345|0| final requests boost never pinto beans. special accounts are slyly unusual t\n11362|1042|F|250063.55|1992-09-11|5-LOW|Clerk#000000922|0|ly blithe deposits cajole blithely slyly silent deposits. regular\n11363|1358|O|163511.72|1998-02-09|5-LOW|Clerk#000000844|0| silent pinto beans haggle al\n11364|163|O|107551.91|1997-03-11|1-URGENT|Clerk#000000582|0|sts wake fluffily about the slyly special accounts. qu\n11365|1214|O|211938.69|1997-08-29|3-MEDIUM|Clerk#000000520|0|de of the blithely final requests. fluffily regular dolphin\n11366|863|F|12711.31|1992-06-08|3-MEDIUM|Clerk#000000993|0|lites nag blithely un\n11367|418|F|18148.48|1994-12-17|1-URGENT|Clerk#000000566|0| carefully regular deposits. bold theodolites haggle. enticingly final fo\n11392|1340|F|135223.44|1994-09-06|5-LOW|Clerk#000000607|0|tect blithely across the express dependencies. instructions after the slyly re\n11393|1150|F|138539.25|1992-03-19|5-LOW|Clerk#000000022|0|al packages. carefully speci\n11394|1255|O|181795.18|1998-03-06|2-HIGH|Clerk#000000559|0| deposits! fluffily sp\n11395|940|O|46826.01|1997-08-26|1-URGENT|Clerk#000000306|0|beans nag carefully even sentiments-- express platelets\n11396|1273|F|107605.76|1992-07-22|1-URGENT|Clerk#000000265|0|ously regular accounts haggle\n11397|757|O|55531.65|1996-10-29|5-LOW|Clerk#000000603|0|oss the regular pac\n11398|587|F|258130.52|1992-08-07|2-HIGH|Clerk#000000407|0|f the theodolites are fu\n11399|346|F|39653.25|1994-04-19|5-LOW|Clerk#000000460|0|ffily furiously even pa\n11424|343|F|58887.47|1993-04-03|4-NOT SPECIFIED|Clerk#000000547|0|s. quickly final requests around the slyly unusual dependencies cajol\n11425|1123|O|156058.42|1995-06-23|1-URGENT|Clerk#000000336|0|arefully special requests cajole bravely fluffy pinto beans. ironic\n11426|1357|F|94572.71|1994-05-08|3-MEDIUM|Clerk#000000066|0|ilent notornis boost blithely quickly even pinto beans. carefully speci\n11427|427|F|306906.43|1993-11-25|3-MEDIUM|Clerk#000000401|0|y slyly brave excuses. slow packages sleep quickl\n11428|628|F|7046.94|1992-01-22|3-MEDIUM|Clerk#000000926|0|ep slyly even, unusual packages. spe\n11429|1192|F|145545.35|1993-03-04|5-LOW|Clerk#000000765|0| bold courts sleep blithely. regular, even r\n11430|1342|O|3641.35|1998-01-05|5-LOW|Clerk#000000817|0|tions breach. regular, express pinto beans wake across the carefully\n11431|10|F|230289.60|1992-08-02|2-HIGH|Clerk#000000055|0|press ideas use slyly regular pinto beans. furiously \n11456|271|F|53466.58|1993-04-13|5-LOW|Clerk#000000209|0|ar foxes against the quickly special th\n11457|1430|O|232660.01|1995-12-14|1-URGENT|Clerk#000000294|0|the slyly quiet ideas. idly final deposits nag. carefully regu\n11458|1468|O|201085.02|1998-04-02|5-LOW|Clerk#000000402|0|l pinto beans cajole carefully\n11459|325|O|230925.13|1996-07-15|4-NOT SPECIFIED|Clerk#000000852|0|ular requests use blithely. quickly special packages boost furiously. deposits\n11460|763|F|51498.95|1993-08-25|2-HIGH|Clerk#000000014|0| brave dependencies nag. blithely final foxe\n11461|1049|O|80204.90|1996-05-01|1-URGENT|Clerk#000000630|0|ymptotes. packages haggle whithout the evenly final requests. req\n11462|226|O|164290.80|1996-08-25|2-HIGH|Clerk#000000210|0|grouches. closely dogged deposits sleep. special, pending packages slee\n11463|140|O|258622.38|1997-12-28|3-MEDIUM|Clerk#000000886|0|ular deposits. slyly final attainments detect careful\n11488|751|F|92903.96|1993-08-23|4-NOT SPECIFIED|Clerk#000000119|0|ickly final requests. furiously even attainments sleep fluffily slyly regu\n11489|929|O|100935.67|1996-09-19|4-NOT SPECIFIED|Clerk#000000155|0|ions. ironic foxes wake. regular platelets cajole slyly close \n11490|745|O|66256.76|1996-09-27|1-URGENT|Clerk#000000489|0|quests affix according to the special p\n11491|158|F|242695.19|1993-08-18|5-LOW|Clerk#000000329|0|cial foxes across the final packag\n11492|478|O|141195.82|1997-03-06|2-HIGH|Clerk#000000708|0|ely regular instructions was fluffily among the fluffil\n11493|1486|O|63852.92|1995-10-21|1-URGENT|Clerk#000000543|0|e blithely regular accounts\n11494|1484|O|90512.73|1997-07-20|3-MEDIUM|Clerk#000000486|0|s. blithely unusual pi\n11495|1048|O|43956.73|1995-07-05|2-HIGH|Clerk#000000684|0|y bold accounts cajo\n11520|1217|F|111920.94|1994-09-28|3-MEDIUM|Clerk#000000106|0|courts. carefully final requests along the carefully final \n11521|55|O|19730.00|1996-09-09|1-URGENT|Clerk#000000010|0| express platelets according to the iro\n11522|47|F|21454.81|1993-09-16|5-LOW|Clerk#000000121|0|ual foxes x-ray carefully alo\n11523|355|O|210039.66|1998-07-27|4-NOT SPECIFIED|Clerk#000000008|0|y: fluffily regular instructions sleep quickly enticing orbits. carefully un\n11524|766|O|7445.25|1995-12-14|5-LOW|Clerk#000000943|0|encies. foxes detect around the final, even theodolites. special depos\n11525|1495|O|169021.86|1997-03-20|1-URGENT|Clerk#000000159|0|ong the ironic accounts are slyly furiously pending dependencies. even exc\n11526|466|O|124012.92|1998-02-23|5-LOW|Clerk#000000798|0|ickly regular packages use slyly ironic,\n11527|1108|F|46961.88|1994-07-03|3-MEDIUM|Clerk#000000187|0|ously final packages was slyly above the bli\n11552|1421|F|34742.82|1992-12-15|1-URGENT|Clerk#000000960|0|g, dogged notornis. carefully even instructions across the furiou\n11553|196|F|207629.22|1994-04-19|3-MEDIUM|Clerk#000000467|0|s thrash blithely carefully ironic requests. quickl\n11554|344|O|269777.97|1998-06-07|2-HIGH|Clerk#000000746|0| platelets are fluffily above the ironic theodolites. slyly pending \n11555|1318|O|242411.22|1996-05-26|3-MEDIUM|Clerk#000000308|0|hinly bold deposits detect along the courts. express theodo\n11556|707|O|237841.53|1996-03-03|5-LOW|Clerk#000000790|0|around the furiously ironic courts. blithely unusual deposits cajo\n11557|1412|O|39415.83|1997-04-12|5-LOW|Clerk#000000522|0|ily. fluffily dogged asymptotes wake slyly regular grouches. a\n11558|61|F|47438.62|1994-03-28|2-HIGH|Clerk#000000181|0| the slyly final excuses. \n11559|931|O|54171.71|1995-09-26|1-URGENT|Clerk#000000582|0| pending excuses; carefully final dependencies\n11584|643|F|350276.97|1994-11-23|3-MEDIUM|Clerk#000000150|0|l pinto beans haggle bold packages. fina\n11585|895|F|59671.89|1994-03-16|3-MEDIUM|Clerk#000000105|0|g to the slyly unusual deposits. bold, final requests boost f\n11586|1013|F|24377.52|1993-11-17|4-NOT SPECIFIED|Clerk#000000180|0|ously special packages solve carefully above th\n11587|220|O|149730.78|1998-03-05|4-NOT SPECIFIED|Clerk#000000103|0| the slyly regular platelets. silently final packages may nag. foxes accordi\n11588|1396|O|187668.87|1997-10-30|5-LOW|Clerk#000000492|0|ep. final, even excuses dazzle about the carefully special req\n11589|1315|F|106954.20|1992-05-19|4-NOT SPECIFIED|Clerk#000000420|0|y express foxes shall have to cajole about the quickly unusu\n11590|883|O|251411.55|1996-04-15|2-HIGH|Clerk#000000744|0|dolites sleep across the carefully regular depo\n11591|269|F|108716.86|1993-01-21|2-HIGH|Clerk#000000691|0|pinto beans. bold deposits a\n11616|403|O|261053.80|1996-08-02|2-HIGH|Clerk#000000003|0|cording to the express\n11617|1459|F|186543.96|1994-12-11|4-NOT SPECIFIED|Clerk#000000519|0|etect blithely pending\n11618|356|O|126867.40|1998-02-04|1-URGENT|Clerk#000000594|0|s kindle fluffily after \n11619|55|O|42866.58|1998-07-12|2-HIGH|Clerk#000000245|0|ress instructions. ideas solve carefully special requests. patterns p\n11620|353|O|115080.33|1997-07-22|4-NOT SPECIFIED|Clerk#000000961|0|ies nag slyly along\n11621|964|O|233849.43|1995-11-11|5-LOW|Clerk#000000316|0|pinto beans. ruthless pinto beans haggle. slyly regular \n11622|349|O|169440.18|1996-04-27|4-NOT SPECIFIED|Clerk#000000006|0| never instructions. express Tiresias boost. unusual, even pinto bean\n11623|715|O|335923.49|1995-12-16|2-HIGH|Clerk#000000827|0|atelets cajole quietly final requests. slyly final courts are ironic, final \n11648|949|P|223995.17|1995-04-10|5-LOW|Clerk#000000916|0| cajole carefully alon\n11649|718|O|336620.13|1996-06-30|5-LOW|Clerk#000000666|0|kly even pinto beans cajole fu\n11650|667|F|153010.80|1992-09-25|4-NOT SPECIFIED|Clerk#000000444|0|ccording to the ironic, regular excuses are \n11651|689|F|66834.76|1995-02-23|3-MEDIUM|Clerk#000000982|0|sly furious sheaves. regular requests c\n11652|1442|O|228052.10|1995-11-11|4-NOT SPECIFIED|Clerk#000000098|0|uriously bold foxes promise among the blithely ironic packages. fluffily regu\n11653|314|O|191078.29|1997-07-05|2-HIGH|Clerk#000000533|0|kages doubt according to the final theodolites. furiously regular\n11654|791|F|115033.02|1993-09-28|4-NOT SPECIFIED|Clerk#000000756|0|onic waters. carefully even packages haggle f\n11655|910|O|184768.86|1998-06-27|1-URGENT|Clerk#000000614|0|slyly special asymptotes. final, unusual requests engage carefully pe\n11680|1187|F|288910.60|1994-09-05|3-MEDIUM|Clerk#000000731|0|pecial asymptotes. final platelets cajole among the ironic pinto beans. pla\n11681|857|F|246493.05|1992-10-17|1-URGENT|Clerk#000000485|0|final packages. carefully final accounts affi\n11682|5|F|153588.74|1993-07-05|5-LOW|Clerk#000000852|0|s grow slyly. express, fin\n11683|209|F|183381.18|1992-02-08|4-NOT SPECIFIED|Clerk#000000583|0|to beans cajole. furiously even frets detect. unusual pinto beans are \n11684|323|F|282136.80|1992-07-28|2-HIGH|Clerk#000000986|0|instructions. final, even packages throughout the epi\n11685|445|O|254981.92|1997-03-19|3-MEDIUM|Clerk#000000641|0|leep slyly unusual foxes. blithely even ideas through the t\n11686|1039|F|169100.57|1994-01-28|4-NOT SPECIFIED|Clerk#000000169|0|ts wake furiously. accounts about the regular ideas are furiously\n11687|1025|O|182611.71|1995-12-26|2-HIGH|Clerk#000000390|0|fluffily ideas. blithely pending packages lose blithely. slyly \n11712|173|F|117391.91|1994-04-27|5-LOW|Clerk#000000376|0|special excuses. final accounts use. dependencies detect by the carefu\n11713|1397|F|124407.44|1993-12-25|4-NOT SPECIFIED|Clerk#000000481|0|nic packages. unusual, unusual Ti\n11714|1250|F|170165.48|1994-08-05|1-URGENT|Clerk#000000877|0|sts could have to wake ironic dependencies. furiously regul\n11715|863|F|67076.38|1994-09-07|2-HIGH|Clerk#000000106|0|xpress foxes. pinto beans above the express deposits are final foxes. furio\n11716|481|F|178602.59|1993-10-12|3-MEDIUM|Clerk#000000021|0|sly ironic packages haggle blithely around the unusual accounts. slyly re\n11717|413|O|153316.94|1998-03-19|5-LOW|Clerk#000000185|0|furiously regular waters \n11718|688|F|35335.31|1994-12-19|3-MEDIUM|Clerk#000000131|0|ly regular asymptotes. decoys integrate slyly among the bo\n11719|1147|F|240503.39|1995-02-06|4-NOT SPECIFIED|Clerk#000000307|0|hely slyly special instr\n11744|127|O|183922.62|1996-07-05|1-URGENT|Clerk#000000197|0|pecial packages above the furio\n11745|25|F|147334.29|1992-07-20|2-HIGH|Clerk#000000549|0|lar ideas. slyly expre\n11746|5|O|177360.54|1998-04-29|3-MEDIUM|Clerk#000000038|0|heodolites. final asymptotes was above the furiously final pin\n11747|460|F|46484.18|1993-02-26|3-MEDIUM|Clerk#000000537|0|. requests use across\n11748|733|F|43216.31|1994-06-04|4-NOT SPECIFIED|Clerk#000000147|0|uffily regular accounts cajole blithely alongside of the furiously pending p\n11749|818|O|75180.45|1998-02-17|5-LOW|Clerk#000000819|0| excuses boost against the final pai\n11750|823|O|117385.37|1997-03-05|2-HIGH|Clerk#000000801|0| furiously packages. carefully silent instruc\n11751|1282|O|150450.18|1996-05-23|1-URGENT|Clerk#000000605|0|uickly express pinto beans. blithely bold foxes use above the dep\n11776|386|F|90162.21|1992-02-25|5-LOW|Clerk#000000548|0|ck theodolites integrate furiously along the bold deposits. even, pending \n11777|271|F|56387.90|1994-12-26|5-LOW|Clerk#000000292|0|g dependencies; quickly dogged courts wake quick\n11778|217|O|102865.25|1997-03-28|5-LOW|Clerk#000000253|0|ss the final, unusual packages. quickly quick theodolites haggle. slyl\n11779|1045|F|183456.75|1993-06-30|4-NOT SPECIFIED|Clerk#000000740|0|according to the care\n11780|28|F|129283.92|1993-09-22|1-URGENT|Clerk#000000793|0|ove the regular theodolites are furiously regular notornis.\n11781|1484|O|147766.37|1996-04-16|5-LOW|Clerk#000000619|0|s. final, final deposi\n11782|427|F|163855.06|1992-07-29|5-LOW|Clerk#000000609|0|bold, unusual requests are slyly against the quickly bold dependencies. s\n11783|1498|O|22953.13|1998-04-03|1-URGENT|Clerk#000000028|0|. regular theodolites snooze furiou\n11808|874|F|60831.20|1992-08-01|4-NOT SPECIFIED|Clerk#000000710|0|could detect quickly unusu\n11809|556|O|161697.76|1996-04-25|2-HIGH|Clerk#000000617|0|old realms alongside of the special dugouts use along the fluffily iro\n11810|647|F|77198.94|1992-04-23|1-URGENT|Clerk#000000804|0| final requests boost among the furiously bold accounts. final\n11811|527|F|7385.35|1992-10-28|2-HIGH|Clerk#000000940|0| packages haggle furiously pend\n11812|940|F|17214.00|1993-06-03|3-MEDIUM|Clerk#000000844|0|y regular accounts. regular, pending requests print careful\n11813|376|F|211762.16|1994-02-10|1-URGENT|Clerk#000000813|0|ly regular instructions. quickly ironi\n11814|121|F|198422.64|1993-01-26|2-HIGH|Clerk#000000404|0|final ideas sleep sometimes deposits. final, final foxes boost fu\n11815|1396|O|199250.12|1995-09-13|5-LOW|Clerk#000000342|0|counts use according to the bli\n11840|596|F|283439.67|1994-08-15|1-URGENT|Clerk#000000466|0|ggle slowly across the sly\n11841|727|F|158024.06|1993-04-21|3-MEDIUM|Clerk#000000521|0|s sleep carefully unusual ac\n11842|1414|O|90262.04|1996-04-08|4-NOT SPECIFIED|Clerk#000000566|0|furiously unusual platelets. express requests \n11843|730|F|211984.66|1994-07-18|3-MEDIUM|Clerk#000000747|0|g requests snooze care\n11844|121|O|86777.18|1997-01-18|2-HIGH|Clerk#000000609|0|ular deposits haggle \n11845|887|O|125342.31|1997-03-29|1-URGENT|Clerk#000000902|0|ts. final accounts detect furiously bold foxes. carefully regular packages h\n11846|1046|F|51318.21|1993-04-21|5-LOW|Clerk#000000688|0|uriously ironic packages cajole furiously express, even ideas. ironic ideas\n11847|1007|O|94551.77|1997-10-02|2-HIGH|Clerk#000000860|0|ely pending deposits haggle furiously across the pending \n11872|145|F|75770.24|1994-07-25|5-LOW|Clerk#000000620|0| courts across the furiously \n11873|863|O|21009.93|1996-04-06|4-NOT SPECIFIED|Clerk#000000322|0|press, final deposits da\n11874|481|F|221467.60|1992-09-10|3-MEDIUM|Clerk#000000163|0|r the slyly bold deposits wake about the c\n11875|698|F|98592.08|1992-08-21|3-MEDIUM|Clerk#000000300|0|. quickly special asymptotes after the slowly final pa\n11876|611|O|130732.20|1995-07-04|2-HIGH|Clerk#000000325|0| to the quickly even pains: always \n11877|268|F|122384.64|1993-05-24|4-NOT SPECIFIED|Clerk#000000312|0|ly above the unusua\n11878|1349|F|92047.44|1993-02-25|4-NOT SPECIFIED|Clerk#000000041|0|ole blithely fluffy, unusua\n11879|1057|F|185103.30|1993-06-20|2-HIGH|Clerk#000000371|0|to the special excuses detect carefully carefully express accounts. carefully\n11904|415|O|279468.40|1997-12-02|4-NOT SPECIFIED|Clerk#000000924|0|accounts. platelets k\n11905|343|O|101799.49|1997-07-04|5-LOW|Clerk#000000543|0|thely regular foxes. fluffily regular pinto beans i\n11906|524|O|325004.58|1996-12-15|5-LOW|Clerk#000000929|0|deas cajole quickly? blithely final dolphins boost. blithely final deposits wa\n11907|1327|F|127812.95|1993-08-24|5-LOW|Clerk#000000062|0|osits above the quickly ironic instructions are carefully after t\n11908|1063|F|180189.92|1993-05-26|4-NOT SPECIFIED|Clerk#000000456|0|atelets across the express deposits kindle evenly unu\n11909|868|F|51603.50|1994-11-25|4-NOT SPECIFIED|Clerk#000000012|0|lithely unusual accounts aga\n11910|644|O|102331.93|1995-06-16|2-HIGH|Clerk#000000664|0|uriously final ideas cajole furi\n11911|355|F|219307.51|1993-11-11|1-URGENT|Clerk#000000563|0| requests try to wake according to the carefully regular deposit\n11936|749|O|167898.60|1995-09-16|1-URGENT|Clerk#000000869|0|ns wake blithely even, express theodolites; final\n11937|1424|O|159471.41|1998-06-27|1-URGENT|Clerk#000000941|0|ic deposits sleep carefully special, regular foxes. special theo\n11938|67|O|161199.79|1998-07-25|1-URGENT|Clerk#000000911|0|iously above the reg\n11939|1333|O|84765.05|1996-10-01|2-HIGH|Clerk#000000610|0|iously regular dinos cajole furiously foxes. blithely regular foxes acr\n11940|683|O|149688.34|1998-05-30|2-HIGH|Clerk#000000152|0|ular ideas cajole quickly\n11941|659|F|157391.90|1993-06-25|1-URGENT|Clerk#000000392|0| instructions. carefully final theodolites wake quickly. carefully iro\n11942|1324|F|43794.61|1994-05-09|1-URGENT|Clerk#000000317|0|ely bold accounts are carefully alongside of the unusual packages. slyly eve\n11943|244|F|235683.21|1993-04-08|5-LOW|Clerk#000000724|0|requests haggle quickly about the carefully \n11968|311|F|43428.32|1995-03-17|5-LOW|Clerk#000000526|0|g frets was above the fur\n11969|449|F|270998.74|1992-01-20|4-NOT SPECIFIED|Clerk#000000641|0|efully. blithely pending deposits haggle regular, regular\n11970|937|O|255762.04|1998-05-24|1-URGENT|Clerk#000000885|0|cial pinto beans. blithely expres\n11971|1351|O|119482.38|1997-03-21|2-HIGH|Clerk#000000026|0|slyly regular requests lose quickly: depen\n11972|1396|F|147645.78|1994-01-04|4-NOT SPECIFIED|Clerk#000000751|0|riously silent gifts affix slyl\n11973|631|F|158758.99|1994-03-04|4-NOT SPECIFIED|Clerk#000000545|0| regular theodolites use carefully pen\n11974|1366|F|88216.78|1992-10-09|4-NOT SPECIFIED|Clerk#000000595|0|slyly unusual accounts according to the blithely ironic ideas boost furio\n11975|322|O|230472.15|1995-04-14|1-URGENT|Clerk#000000551|0|on the bold ideas cajole across the f\n12000|1150|F|89148.77|1994-05-13|4-NOT SPECIFIED|Clerk#000000683|0|s against the furiously ironic grouches sleep according to the e\n12001|739|F|138635.75|1994-07-07|2-HIGH|Clerk#000000863|0|old, even theodolites. regular, special theodolites use furio\n12002|826|F|79579.51|1993-11-30|3-MEDIUM|Clerk#000000431|0| regular packages wake qui\n12003|1205|O|16311.25|1998-05-24|1-URGENT|Clerk#000000708|0|s along the quickly regular instructions haggle carefully furiously u\n12004|656|F|74814.13|1994-12-05|3-MEDIUM|Clerk#000000633|0|nis against the slyly specia\n12005|1102|F|250917.29|1992-06-25|4-NOT SPECIFIED|Clerk#000000904|0| after the ironic, unusua\n12006|944|O|129075.40|1997-06-02|4-NOT SPECIFIED|Clerk#000000501|0|n packages. carefully ironic accounts are after the pending platele\n12007|613|F|239431.45|1994-09-28|4-NOT SPECIFIED|Clerk#000000363|0|even requests wake carefully unusual packages. quickly\n12032|902|O|102348.27|1997-08-10|4-NOT SPECIFIED|Clerk#000000349|0| after the even, regular instructions. blithe tithes use furiously. b\n12033|334|F|214360.53|1992-09-12|2-HIGH|Clerk#000000274|0| bold pearls haggle. carefully ironic pinto beans cajole. blit\n12034|121|O|88674.23|1996-12-08|2-HIGH|Clerk#000000363|0|ges. deposits sleep slyly. ideas sleep\n12035|1010|O|245999.17|1996-11-09|5-LOW|Clerk#000000963|0|furiously special instructions. pending deposits nod. blithely unusual pint\n12036|838|O|241236.07|1998-01-16|5-LOW|Clerk#000000041|0|ic requests. unusual pinto beans sleep fluffily about the furiously regular \n12037|1462|O|139739.68|1995-07-01|5-LOW|Clerk#000000375|0|pending foxes shall cajol\n12038|164|O|59423.94|1995-11-10|2-HIGH|Clerk#000000587|0|ironic asymptotes mainta\n12039|1252|F|303373.40|1993-06-17|2-HIGH|Clerk#000000765|0|packages integrate c\n12064|38|F|133067.27|1992-09-16|5-LOW|Clerk#000000994|0| blithely quickly pending t\n12065|557|O|60068.73|1997-01-30|4-NOT SPECIFIED|Clerk#000000064|0|the final deposits boost pending deposits. pending\n12066|1066|F|187325.65|1995-02-07|3-MEDIUM|Clerk#000000084|0|heodolites cajole a\n12067|257|F|227467.91|1993-02-05|1-URGENT|Clerk#000000019|0|requests. quickly regular packages run f\n12068|1231|O|20347.58|1996-05-08|2-HIGH|Clerk#000000402|0|ctions. furiously even accounts \n12069|1381|O|235666.55|1995-11-08|2-HIGH|Clerk#000000460|0|ong the pinto beans. deposits among the excuses cajole\n12070|746|O|256785.35|1998-05-23|5-LOW|Clerk#000000287|0|bold pinto beans hagg\n12071|956|O|100260.23|1998-06-26|1-URGENT|Clerk#000000205|0|ly bold multipliers cajole quickly re\n12096|1004|F|257237.41|1992-09-20|3-MEDIUM|Clerk#000000156|0|hely final requests kindle among the regular foxes. orb\n12097|118|F|156644.02|1993-11-10|1-URGENT|Clerk#000000239|0|out the slyly regular theodolites. regularly reg\n12098|1325|F|34956.25|1993-05-01|2-HIGH|Clerk#000000214|0| carefully ironic, express deposits. ideas are slyly a\n12099|112|F|230209.47|1994-09-12|1-URGENT|Clerk#000000042|0|furiously regular accounts haggle quic\n12100|1298|O|62857.66|1996-03-15|2-HIGH|Clerk#000000518|0|ress frays use blithely pending requests-- quickly regular somas acros\n12101|754|F|103072.24|1995-01-07|1-URGENT|Clerk#000000853|0|fily regular packages w\n12102|1264|O|208734.50|1995-11-18|3-MEDIUM|Clerk#000000981|0|eposits are blithely along\n12103|229|O|169239.68|1996-11-25|3-MEDIUM|Clerk#000000605|0|ironic foxes. quickly brave pinto beans \n12128|1192|O|163301.92|1997-06-27|4-NOT SPECIFIED|Clerk#000000102|0|yly across the furiously ironic accounts. carefully special real\n12129|418|F|117561.31|1992-12-26|3-MEDIUM|Clerk#000000119|0|dencies cajole furiously about the \n12130|1261|O|157170.80|1995-12-10|4-NOT SPECIFIED|Clerk#000000894|0|according to the even, regular packages. furiously unusual pinto beans wake \n12131|1172|O|285746.20|1998-06-29|3-MEDIUM|Clerk#000000074|0|ironic ideas. blithely unus\n12132|1277|O|271963.86|1996-09-06|1-URGENT|Clerk#000000831|0|ptotes boost permanently. carefully unusual instr\n12133|361|F|52225.60|1992-04-15|1-URGENT|Clerk#000000634|0|st after the furiously special \n12134|355|O|56547.11|1996-05-31|3-MEDIUM|Clerk#000000868|0|blithely blithely regular theodolites. slyly even packages nag slyly slyly e\n12135|1420|O|268120.03|1995-10-10|5-LOW|Clerk#000000246|0|ual excuses alongside of th\n12160|1423|F|67966.80|1993-12-13|5-LOW|Clerk#000000707|0|ully about the furiously ironic braids-- carefully en\n12161|1307|O|215072.11|1997-03-15|4-NOT SPECIFIED|Clerk#000000382|0|le blithely across the blithely slow fox\n12162|1222|O|47989.76|1997-03-18|3-MEDIUM|Clerk#000000066|0| players according to the fluffily ironic dolphins cajole slyly spe\n12163|88|O|205826.29|1997-07-20|5-LOW|Clerk#000000951|0|se furiously carefully special pinto beans. blithely special \n12164|1313|F|180895.16|1993-10-17|1-URGENT|Clerk#000000148|0|ests. instructions haggle blithely express dependencies. furiously bold war\n12165|1489|F|153853.42|1994-04-11|5-LOW|Clerk#000000091|0|nic pinto beans boost carefully \n12166|62|F|18006.96|1995-03-15|4-NOT SPECIFIED|Clerk#000000469|0|of the slyly even pinto bean\n12167|91|O|60173.82|1998-05-31|1-URGENT|Clerk#000000618|0|counts. bravely special pac\n12192|277|F|27209.81|1994-10-19|1-URGENT|Clerk#000000387|0| bold ideas boost slyly slyly final frays. carefully unusual pinto b\n12193|802|O|159382.08|1996-07-28|3-MEDIUM|Clerk#000000559|0|y. blithely final pinto beans according to the theodolites haggle care\n12194|271|F|59806.63|1995-03-02|1-URGENT|Clerk#000000922|0|atelets haggle among the regular accounts. furiously even plate\n12195|73|O|108946.27|1997-04-27|4-NOT SPECIFIED|Clerk#000000947|0|iously silent foxes eat carefull\n12196|1261|F|83958.83|1993-06-03|4-NOT SPECIFIED|Clerk#000000284|0|e furiously ironic ideas affix fluffily above the quickly\n12197|577|O|206318.73|1997-06-27|2-HIGH|Clerk#000000976|0|ular packages affix upon the slyly\n12198|1300|F|59977.08|1993-02-28|3-MEDIUM|Clerk#000000917|0| ironic deposits. furiously express escapades detect\n12199|520|O|103350.42|1995-12-15|5-LOW|Clerk#000000478|0|he ironic accounts. ideas detect. slyly final packa\n12224|646|F|212674.36|1994-12-13|2-HIGH|Clerk#000000672|0|. slyly final accounts boost. blithely express deposits haggle sl\n12225|656|O|188067.32|1997-01-29|2-HIGH|Clerk#000000903|0|ons. blithely bold requests are carefu\n12226|1348|O|120809.59|1998-04-01|5-LOW|Clerk#000000415|0| across the final instructi\n12227|1282|O|210133.42|1998-06-27|3-MEDIUM|Clerk#000000980|0| packages. quickly ironic accounts affix quickly. ironic, even packages\n12228|226|F|276130.19|1994-06-26|4-NOT SPECIFIED|Clerk#000000474|0|fully regular excuses? ironic foxes are across the dogge\n12229|1250|O|106159.32|1996-07-24|1-URGENT|Clerk#000000293|0|ptotes boost slyly even accounts. \n12230|712|O|161632.17|1998-04-05|2-HIGH|Clerk#000000383|0|s. regular foxes breach regularly pending theodo\n12231|1021|O|243683.82|1997-07-30|4-NOT SPECIFIED|Clerk#000000012|0|sual sauternes use across the bold theodolites. req\n12256|532|F|146646.67|1992-12-19|3-MEDIUM|Clerk#000000238|0|iously alongside of the \n12257|149|O|128270.26|1996-04-17|2-HIGH|Clerk#000000278|0|y above the unusual foxes. slyly even accounts haggl\n12258|1408|F|300041.05|1994-12-01|5-LOW|Clerk#000000878|0|ing foxes boost. stealthy\n12259|250|F|173853.14|1993-02-15|2-HIGH|Clerk#000000282|0| boost about the carefully ironic ideas. fluffily iron\n12260|496|F|172336.18|1992-11-26|3-MEDIUM|Clerk#000000484|0|s along the slyly ruthless pinto beans haggle about the \n12261|1126|F|362237.85|1993-10-24|5-LOW|Clerk#000000279|0|ccounts use quickly about the furiously bold foxes. furiously final \n12262|1441|O|139469.07|1995-12-11|5-LOW|Clerk#000000886|0|efully. slyly final theodolit\n12263|550|O|161275.31|1995-07-07|2-HIGH|Clerk#000000566|0|ts sleep across the carefully bold instruc\n12288|1153|O|125448.87|1996-11-06|1-URGENT|Clerk#000000183|0|slyly regular deposits above the foxes are at the packages-\n12289|421|O|128835.39|1995-12-25|5-LOW|Clerk#000000269|0| even deposits. ironic, regular deposits haggle blith\n12290|1304|O|103022.30|1995-07-10|3-MEDIUM|Clerk#000000947|0|ully pending instructions boost slyly furiously \n12291|740|O|19328.06|1998-03-23|1-URGENT|Clerk#000000074|0|al deposits. warhorses inte\n12292|739|F|176370.50|1992-06-03|4-NOT SPECIFIED|Clerk#000000375|0|sometimes final foxes after the qui\n12293|329|O|102326.14|1995-06-19|2-HIGH|Clerk#000000103|0| quietly ironic instructions sleep carefully furiously iro\n12294|784|O|182534.50|1995-07-06|5-LOW|Clerk#000000583|0|ns run about the qui\n12295|883|F|84218.70|1993-11-10|5-LOW|Clerk#000000955|0|ost around the slowly iron\n12320|298|O|118435.28|1995-12-21|1-URGENT|Clerk#000000356|0|o beans wake carefully theodolites. final gifts haggle quickly \n12321|79|F|12511.06|1994-04-04|3-MEDIUM|Clerk#000000705|0|uriously ironic deposits cajole furiously doggedly ironic depend\n12322|856|P|42466.03|1995-05-12|1-URGENT|Clerk#000000760|0| the special instructions detect fluffily ac\n12323|926|P|139824.58|1995-04-03|4-NOT SPECIFIED|Clerk#000000019|0|ts detect above the even dep\n12324|1033|O|202248.40|1998-08-02|3-MEDIUM|Clerk#000000746|0|ending theodolites try to thrash. regular deposits about the fluffily pending\n12325|973|F|70150.43|1995-01-05|1-URGENT|Clerk#000000440|0|packages are final p\n12326|506|F|181528.08|1995-02-03|1-URGENT|Clerk#000000765|0|ccounts are fluffily carefu\n12327|166|O|39113.28|1998-04-23|4-NOT SPECIFIED|Clerk#000000969|0|tions haggle carefully slyly\n12352|1466|F|331745.34|1992-03-24|3-MEDIUM|Clerk#000000997|0|eas. regular instructions need to boost slyly re\n12353|595|O|219706.88|1996-04-13|4-NOT SPECIFIED|Clerk#000000312|0|ependencies. special dependencies nag quickly even packages. slyly final de\n12354|1243|O|54975.46|1997-01-21|5-LOW|Clerk#000000859|0|g to the carefully pending excuses cajole\n12355|178|P|286003.34|1995-05-29|4-NOT SPECIFIED|Clerk#000000081|0|arhorses. close, final foxes are slyly\n12356|656|F|121696.19|1992-10-19|1-URGENT|Clerk#000000540|0|ges haggle quickly! blithely careful courts impress alongside of the bold fox\n12357|154|O|90986.72|1995-08-23|2-HIGH|Clerk#000000169|0|instructions poach furiously final requests. quickly express depo\n12358|880|O|231011.45|1996-10-10|2-HIGH|Clerk#000000335|0| blithely pending ideas. quickly ironic pinto b\n12359|1400|O|215672.79|1997-05-29|4-NOT SPECIFIED|Clerk#000000382|0| express packages grow above the blithely even foxes:\n12384|1168|O|213609.26|1998-08-02|3-MEDIUM|Clerk#000000311|0|gle. furiously unus\n12385|821|F|92862.99|1992-11-24|5-LOW|Clerk#000000324|0|sts use furiously. ironi\n12386|424|F|314497.24|1992-03-21|3-MEDIUM|Clerk#000000709|0|e fluffily ironic dolphins. quick\n12387|638|O|52638.42|1997-07-03|2-HIGH|Clerk#000000051|0|o the quickly regular requests use slyly unusual theodolites.\n12388|952|O|331123.13|1997-10-16|4-NOT SPECIFIED|Clerk#000000611|0|boost furiously furiously final accounts. slyly regular deposits are am\n12389|653|F|242568.31|1994-08-05|3-MEDIUM|Clerk#000000139|0|s boost according to the slyly even dolphins. final depend\n12390|1285|O|49792.77|1996-06-24|3-MEDIUM|Clerk#000000421|0|posits doze blithely. fluffily ironic d\n12391|1000|O|94747.78|1998-07-01|5-LOW|Clerk#000000433|0|cies are blithely. furious\n12416|1150|F|9933.08|1993-06-26|1-URGENT|Clerk#000000400|0|ow ideas cajole furiou\n12417|1081|F|230795.43|1993-10-25|1-URGENT|Clerk#000000152|0|requests. furiously express instructions haggle. e\n12418|1300|F|124402.47|1992-02-19|4-NOT SPECIFIED|Clerk#000000547|0|tructions. foxes nag furiously abo\n12419|982|O|268861.58|1995-10-16|1-URGENT|Clerk#000000104|0|y. furiously final sauternes sleep slyly above the pending \n12420|1489|F|179274.43|1993-12-27|3-MEDIUM|Clerk#000000798|0|ackages cajole permanently. blithely quick packages alongs\n12421|673|O|212213.47|1995-11-02|1-URGENT|Clerk#000000203|0|pending theodolites wake: ironic, express platelets cajole furiously.\n12422|575|P|217235.25|1995-05-22|3-MEDIUM|Clerk#000000349|0|ssly pending requests cajole quietly final i\n12423|986|F|90571.46|1992-07-18|1-URGENT|Clerk#000000742|0|nic courts haggle carefully finally regular requests. depo\n12448|511|O|158476.25|1995-11-09|1-URGENT|Clerk#000000711|0|he quickly pending accounts. ironic \n12449|461|F|133597.11|1993-02-01|3-MEDIUM|Clerk#000000312|0|s hinder carefully across the iro\n12450|1321|F|41643.96|1994-03-13|4-NOT SPECIFIED|Clerk#000000550|0|kages use quickly final accounts. carefully regular packages integrate blithe\n12451|1150|F|298853.62|1993-04-20|5-LOW|Clerk#000000319|0|uriously even ideas hagg\n12452|364|F|173339.75|1993-12-08|3-MEDIUM|Clerk#000000867|0|indle furiously near the quickly regular accounts: silent,\n12453|1213|F|180966.45|1994-01-14|5-LOW|Clerk#000000085|0|ic ideas promise. slyly ironic pinto beans above the dinos wake quickly bold\n12454|217|O|42464.91|1997-03-21|1-URGENT|Clerk#000000910|0|ic, regular deposits are against the carefully unusual accounts. e\n12455|292|F|216570.72|1992-09-04|4-NOT SPECIFIED|Clerk#000000594|0|nusual asymptotes. regular ideas haggle blithely across the ironic ideas. \n12480|320|F|122198.33|1994-07-08|3-MEDIUM|Clerk#000000055|0|ructions wake fluffily fluffily final gifts! furiou\n12481|271|O|85849.06|1995-07-11|1-URGENT|Clerk#000000785|0|uests sleep furiously bold deposits. blithely express acc\n12482|998|O|153413.61|1996-01-08|5-LOW|Clerk#000000311|0|y express dependencies along the f\n12483|124|F|153915.13|1993-08-05|1-URGENT|Clerk#000000652|0|cial ideas sleep furiously against the final, regular re\n12484|1027|O|184195.36|1995-08-22|3-MEDIUM|Clerk#000000268|0|olphins. blithely ironic platelets s\n12485|188|F|209369.25|1993-08-08|1-URGENT|Clerk#000000994|0|tions along the ideas \n12486|1286|P|184001.37|1995-02-21|2-HIGH|Clerk#000000206|0| deposits; quickly ironic packages use stealthily about the qui\n12487|401|F|41222.94|1994-11-30|2-HIGH|Clerk#000000575|0|xes. requests sleep carefully\n12512|1402|O|106021.12|1996-04-02|2-HIGH|Clerk#000000813|0|nal foxes are fluffily. foxes snooze about the ironi\n12513|419|O|62011.18|1997-09-03|5-LOW|Clerk#000000422|0| wake final, special requests. express instructions cajole after the furi\n12514|1495|F|38477.49|1994-04-25|2-HIGH|Clerk#000000120|0|c courts doubt express, bo\n12515|1459|F|128077.61|1993-05-14|3-MEDIUM|Clerk#000000624|0|kly special accounts. blithely regular packages boost slyly packages. pl\n12516|1373|F|112837.28|1994-05-25|5-LOW|Clerk#000000087|0|y after the quickly unusual gifts. fluffily exp\n12517|1303|O|84279.32|1997-02-01|5-LOW|Clerk#000000162|0|packages. express packages impress furiously even, bold theodolite\n12518|1082|F|153987.45|1993-07-23|5-LOW|Clerk#000000194|0|ultipliers serve furiously a\n12519|913|F|187489.35|1994-01-01|2-HIGH|Clerk#000000387|0|ependencies. carefully unusual deposits use finally ironic deposits. e\n12544|440|F|62982.76|1994-03-04|5-LOW|Clerk#000000600|0|thely above the even \n12545|175|O|74242.81|1996-07-01|1-URGENT|Clerk#000000472|0|ly. carefully silent excuses use carefully around the reg\n12546|217|O|89344.66|1996-02-08|4-NOT SPECIFIED|Clerk#000000547|0|ounts mold. blithely stealthy depths haggle blithely blithe\n12547|1141|O|205195.87|1998-04-14|2-HIGH|Clerk#000000729|0|odolites? blithely ir\n12548|145|O|209786.08|1997-11-20|4-NOT SPECIFIED|Clerk#000000517|0|s excuses x-ray against\n12549|28|O|100471.72|1997-06-03|1-URGENT|Clerk#000000845|0|he furiously regular pint\n12550|878|F|39446.33|1993-03-09|4-NOT SPECIFIED|Clerk#000000282|0|as. blithely ironic pinto beans wake. slyly bold deposits detect blithely alon\n12551|790|F|176659.70|1992-06-12|5-LOW|Clerk#000000090|0| slyly alongside of the special waters. asymptotes outside the pend\n12576|1057|F|33914.11|1993-01-26|4-NOT SPECIFIED|Clerk#000000762|0|n decoys behind the carefully final requests\n12577|616|O|134783.72|1998-05-18|4-NOT SPECIFIED|Clerk#000000586|0|special instruction\n12578|1207|O|255196.69|1996-11-15|2-HIGH|Clerk#000000150|0|p slyly furious foxes. slyly pending a\n12579|193|O|47191.62|1995-06-17|4-NOT SPECIFIED|Clerk#000000536|0|y silent pinto beans-- ironic deposits affix. furiously fina\n12580|589|F|41858.68|1993-01-14|5-LOW|Clerk#000000373|0|y bold theodolites above the carefully regular \n12581|823|F|106379.64|1994-10-21|2-HIGH|Clerk#000000582|0| final requests after the final patterns wake blithel\n12582|538|F|22603.80|1993-01-22|4-NOT SPECIFIED|Clerk#000000671|0| ironic packages against the requests slee\n12583|943|O|42860.56|1996-01-18|5-LOW|Clerk#000000676|0|yly fluffily even accounts. quickly careful asymptotes boost. ironic, bo\n12608|301|F|61932.81|1993-03-08|1-URGENT|Clerk#000000087|0| have to nag quietly among the carefully bold pinto beans. expres\n12609|805|O|128069.23|1996-01-17|3-MEDIUM|Clerk#000000207|0|l requests haggle furiously\n12610|85|F|10061.76|1993-07-17|5-LOW|Clerk#000000952|0| bold theodolites engage blithely against th\n12611|41|O|185736.53|1995-10-16|3-MEDIUM|Clerk#000000579|0|ckly carefully regular deposits\n12612|1444|F|185433.93|1993-12-22|5-LOW|Clerk#000000381|0| accounts. furiously ironic requests along the final, ruthless accounts cajol\n12613|328|F|116318.24|1993-04-11|3-MEDIUM|Clerk#000000550|0|lithely express ins\n12614|670|O|273057.78|1996-02-28|3-MEDIUM|Clerk#000000387|0|wake. quickly stealthy foxes affix slyly ironic requests. \n12615|118|F|20866.10|1995-03-06|4-NOT SPECIFIED|Clerk#000000993|0|old packages. accounts use slyly after the foxes. \n12640|413|O|76597.62|1998-07-20|2-HIGH|Clerk#000000081|0|ites. slyly express deposits integrate fluffily. ironic courts abo\n12641|1097|F|239626.14|1995-02-23|5-LOW|Clerk#000000617|0|its wake according to the slyly unusual excuses. even patterns are carefully \n12642|1243|F|247694.31|1994-04-28|1-URGENT|Clerk#000000015|0|symptotes thrash blithely above the furiously regular accounts. q\n12643|322|F|124302.25|1993-03-20|5-LOW|Clerk#000000486|0| slyly silent requests. qui\n12644|1159|F|150689.23|1992-02-01|2-HIGH|Clerk#000000394|0| the final instructions. blithely ironic asymptotes boost carefully regular \n12645|250|F|231538.56|1994-07-07|5-LOW|Clerk#000000705|0|de of the blithely regular accounts use slyly final accounts. fluff\n12646|1334|F|116946.28|1993-02-18|1-URGENT|Clerk#000000155|0|regular ideas. slyly ironic re\n12647|1198|O|38285.07|1997-02-27|4-NOT SPECIFIED|Clerk#000000190|0|. regular theodolites sleep after the care\n12672|257|F|37187.79|1994-07-07|4-NOT SPECIFIED|Clerk#000000963|0|es are carefully blithely unusual dugouts. regular, i\n12673|62|F|21911.85|1994-06-12|1-URGENT|Clerk#000000435|0|ffily. express accounts sleep slyly regular deposit\n12674|586|F|96952.15|1992-07-05|5-LOW|Clerk#000000811|0|xcuses. express deposits wake quickl\n12675|679|F|155533.85|1994-01-28|3-MEDIUM|Clerk#000000921|0|r theodolites. furiously pending gifts alongside\n12676|839|F|5884.55|1994-01-16|2-HIGH|Clerk#000000960|0|ake slyly special deposits. slyly un\n12677|190|O|187187.63|1996-05-25|5-LOW|Clerk#000000500|0|ely above the furiously silent accounts; carefully regular depend\n12678|844|O|77831.02|1998-01-26|5-LOW|Clerk#000000395|0|around the blithely slow packages nag quickly express packages. carefully\n12679|1117|O|75859.23|1997-06-22|2-HIGH|Clerk#000000326|0|s. furiously pending deposits sleep furiously a\n12704|439|F|143027.03|1993-02-10|5-LOW|Clerk#000000553|0|ackages sleep. pinto beans haggle furiously. quickly final dolphins use blit\n12705|295|F|74933.03|1994-09-16|5-LOW|Clerk#000000983|0|grow above the fluffily final packages. slyly pending ideas nag furiou\n12706|458|F|157344.88|1994-11-21|1-URGENT|Clerk#000000997|0|ing instructions. deposits cajole. slyly sp\n12707|1219|F|253413.30|1993-05-30|2-HIGH|Clerk#000000820|0|ffix furiously according to the final, ironic dolphin\n12708|172|F|262921.58|1993-01-08|5-LOW|Clerk#000000882|0|ly ironic deposits. quickly final sentiments cajole car\n12709|268|O|74462.32|1996-05-11|5-LOW|Clerk#000000991|0|hely alongside of the a\n12710|1169|F|305191.71|1993-08-18|1-URGENT|Clerk#000000329|0|es wake furiously ironic accounts. fluffily ironic pinto bea\n12711|85|F|80711.39|1992-02-20|1-URGENT|Clerk#000000326|0|usual instructions. pending, final deposits use. \n12736|751|F|346186.75|1993-03-09|5-LOW|Clerk#000000385|0|ithely express deposits. pinto beans n\n12737|1187|F|320360.43|1994-05-04|1-URGENT|Clerk#000000047|0|ly regular dinos affix slyly. ironic, quick packages boost carefully. specia\n12738|1334|O|182506.16|1998-06-12|2-HIGH|Clerk#000000613|0|sts boost: carefully even decoys integrate against the furiously reg\n12739|772|F|56336.51|1992-03-02|4-NOT SPECIFIED|Clerk#000000078|0|side the bold, express depend\n12740|439|O|61728.22|1997-06-05|2-HIGH|Clerk#000000643|0| the special deposits ca\n12741|1348|F|162616.34|1992-07-20|4-NOT SPECIFIED|Clerk#000000800|0| of the instructions. furiou\n12742|748|O|36355.83|1997-06-07|5-LOW|Clerk#000000591|0|always final sauternes. carefully regula\n12743|695|F|101594.83|1993-06-24|2-HIGH|Clerk#000000015|0|inal foxes cajole quickly. bli\n12768|1202|F|121309.88|1994-04-09|1-URGENT|Clerk#000000642|0|sly even accounts haggle slyly. blithely pending epitaphs boost. carefully f\n12769|1057|F|29166.32|1992-01-08|4-NOT SPECIFIED|Clerk#000000590|0|blithely across the furiously express foxes; slyly silent pa\n12770|1415|F|129333.56|1993-04-18|4-NOT SPECIFIED|Clerk#000000243|0|bove the special, regular ideas. final pinto beans grow? iro\n12771|451|F|203114.43|1994-01-18|1-URGENT|Clerk#000000548|0|counts. furiously final dependencies sleep. fluffi\n12772|1451|P|221390.02|1995-05-12|4-NOT SPECIFIED|Clerk#000000248|0|ual requests wake quickly even pinto\n12773|817|F|304639.93|1995-01-12|5-LOW|Clerk#000000670|0|s, pending ideas. fluffily ironic deposits cajole blithely furiously unusual\n12774|1108|F|60657.84|1992-10-10|1-URGENT|Clerk#000000067|0|ounts. quickly final requests \n12775|979|O|127678.72|1996-10-15|5-LOW|Clerk#000000553|0|s the quickly unusual \n12800|11|F|302431.56|1993-05-23|5-LOW|Clerk#000000898|0|. slyly final accounts cajole i\n12801|1412|O|226079.43|1997-08-28|3-MEDIUM|Clerk#000000582|0|bove the fluffily express asymptot\n12802|1481|O|233609.74|1998-03-02|1-URGENT|Clerk#000000774|0|ounts. special, pending ideas cajole pe\n12803|392|F|27391.77|1993-02-26|4-NOT SPECIFIED|Clerk#000000876|0|sly final accounts use slyly final accounts. blithe\n12804|734|F|358058.27|1992-05-26|3-MEDIUM|Clerk#000000544|0|l packages boost quickly ironic, \n12805|478|O|76234.32|1996-12-11|3-MEDIUM|Clerk#000000035|0| to the quickly special platelets? \n12806|526|O|208746.01|1995-12-25|4-NOT SPECIFIED|Clerk#000000181|0|s about the slyly regular pinto beans nag\n12807|391|O|34178.61|1998-01-16|5-LOW|Clerk#000000393|0|uffily final accounts wake fur\n12832|362|F|66392.28|1994-12-11|3-MEDIUM|Clerk#000000566|0|unts are after the regular packages. furiously pending\n12833|1285|O|102301.35|1998-06-14|5-LOW|Clerk#000000773|0|nts. even, pending foxes are\n12834|757|O|263553.51|1996-11-03|4-NOT SPECIFIED|Clerk#000000370|0|ns. unusual ideas are. somas b\n12835|370|O|142811.34|1997-01-20|4-NOT SPECIFIED|Clerk#000000894|0|ges. slyly final id\n12836|1057|O|222420.89|1996-04-18|3-MEDIUM|Clerk#000000728|0|es sleep furiously against the quickly even inst\n12837|590|F|112787.85|1992-12-03|3-MEDIUM|Clerk#000000431|0|ter the slowly unusual foxes. blithely express pinto beans haggle. furiou\n12838|112|O|104930.86|1998-07-01|1-URGENT|Clerk#000000424|0|fully ironic foxes. quickly pending accounts about the fin\n12839|1424|O|178226.93|1998-01-08|3-MEDIUM|Clerk#000001000|0|ng pinto beans. unusual theodolites engag\n12864|647|F|142470.94|1994-05-17|2-HIGH|Clerk#000000620|0| the ironic, silent foxes. special\n12865|1271|O|244542.24|1997-05-23|5-LOW|Clerk#000000362|0|requests boost slyly carefully even instructions. carefully regular packages c\n12866|1433|O|165429.43|1997-01-17|3-MEDIUM|Clerk#000000996|0|y regular requests play carefully against the always final \n12867|59|F|197098.68|1995-03-12|4-NOT SPECIFIED|Clerk#000000638|0|lar platelets. blithely unusual deposits cajole carefully s\n12868|1042|P|229577.12|1995-03-02|1-URGENT|Clerk#000000993|0|ns sleep above the carefully ex\n12869|1396|F|131379.62|1994-01-26|4-NOT SPECIFIED|Clerk#000000871|0|s the pending foxes: fluffily regular requests wake slyly against the slyl\n12870|1366|F|51595.17|1993-07-28|1-URGENT|Clerk#000000157|0| furiously dogged theodolites. regular requests among the blithely \n12871|1174|O|194923.17|1995-10-05|4-NOT SPECIFIED|Clerk#000000646|0|ally ironic packages. blithely regular asymptotes are a\n12896|370|O|321570.67|1996-12-22|1-URGENT|Clerk#000000337|0|nic theodolites. slyly even pinto beans use quickly. care\n12897|1169|O|153229.93|1995-07-19|4-NOT SPECIFIED|Clerk#000000854|0|ost carefully regular packages. blithely \n12898|1045|F|44854.49|1994-09-17|3-MEDIUM|Clerk#000000873|0|packages maintain carefully after \n12899|868|O|40541.89|1996-04-07|4-NOT SPECIFIED|Clerk#000000006|0|efully pending ideas cajole fluf\n12900|220|F|41268.96|1993-05-04|2-HIGH|Clerk#000000285|0|s. even requests sleep carefully blithely unusual ac\n12901|1283|F|302296.36|1994-05-06|2-HIGH|Clerk#000000994|0|ely final requests. fu\n12902|640|F|321334.39|1993-10-30|4-NOT SPECIFIED|Clerk#000000254|0|y regular deposits. regular instructions sleep\n12903|301|F|89081.29|1994-09-26|1-URGENT|Clerk#000000914|0|wake furiously fluffily bold dolphins. blithely regular pinto beans sle\n12928|634|F|258054.14|1994-03-28|2-HIGH|Clerk#000000465|0|ng slyly after the final requests. furiously special \n12929|917|F|138543.10|1993-08-31|1-URGENT|Clerk#000000397|0|g the blithely bold asymptotes. pending dependencies \n12930|1072|O|10854.39|1995-11-25|2-HIGH|Clerk#000000162|0|es cajole alongside of the fluffily pending dependencies. regular\n12931|775|F|72091.62|1992-04-25|3-MEDIUM|Clerk#000000196|0|final foxes boost across the slyly pending dependencies. even,\n12932|238|O|151284.17|1997-08-27|3-MEDIUM|Clerk#000000787|0|ackages. foxes cajole blithely regular\n12933|173|O|253181.34|1998-02-17|3-MEDIUM|Clerk#000000224|0|haggle according to\n12934|623|F|273516.60|1994-03-06|4-NOT SPECIFIED|Clerk#000000441|0|sual, pending dependencies among \n12935|509|F|69433.26|1994-05-10|3-MEDIUM|Clerk#000000549|0|gouts at the even packages cajole slyly final packages. slyly enticin\n12960|1243|F|132107.73|1993-10-10|2-HIGH|Clerk#000000340|0|ng theodolites haggle after the final id\n12961|1144|F|133684.91|1994-12-13|2-HIGH|Clerk#000000913|0|rding to the enticing, final foxes use blithely furiou\n12962|1111|O|130541.81|1995-10-29|1-URGENT|Clerk#000000488|0|. accounts breach carefully blithely ironic deposits.\n12963|1232|O|72621.23|1998-07-08|3-MEDIUM|Clerk#000000546|0|uriously regular deposits wake slowly along t\n12964|556|O|228128.18|1998-01-13|2-HIGH|Clerk#000000980|0|ronic instructions. quickly regular a\n12965|964|F|107134.13|1993-06-26|3-MEDIUM|Clerk#000000650|0|ate slyly against the carefully final packages. furiously final requests alo\n12966|224|O|51608.06|1997-10-11|5-LOW|Clerk#000000252|0|ly furiously final packages. carefully regular packages integrate quic\n12967|1147|F|9777.25|1993-09-22|2-HIGH|Clerk#000000650|0|gly final deposits are slyly carefully ironic cour\n12992|139|O|199084.84|1997-02-25|5-LOW|Clerk#000000099|0|xpress theodolites use across the un\n12993|1348|O|46875.17|1998-05-14|5-LOW|Clerk#000000435|0| across the depths. idle, final accounts lose furiously regular, \n12994|1451|F|54188.56|1994-11-18|5-LOW|Clerk#000000557|0|ily among the slyly unusual deposits. quickly pending acco\n12995|1162|O|200689.05|1996-07-29|3-MEDIUM|Clerk#000000027|0|ts except the quickly express request\n12996|967|O|107497.47|1995-12-05|4-NOT SPECIFIED|Clerk#000000531|0|ccounts. blithely ironic pinto beans cajol\n12997|520|O|23304.56|1996-05-27|1-URGENT|Clerk#000000085|0|accounts boost. final asymptotes affix blithely fluff\n12998|799|O|25952.72|1996-10-03|2-HIGH|Clerk#000000213|0|instructions sleep. express, final patterns detect furiously. furi\n12999|940|F|216567.07|1993-04-04|2-HIGH|Clerk#000000745|0|nusual foxes. express accounts sublate q\n13024|664|F|58015.05|1992-03-14|3-MEDIUM|Clerk#000000700|0| bold requests nag quickly. d\n13025|158|O|138165.03|1998-01-28|2-HIGH|Clerk#000000785|0|lly regular instructions serve. special deposits against the careful\n13026|1126|O|194927.69|1997-04-01|2-HIGH|Clerk#000000050|0| bold ideas! express ideas across the furiously dogged theodolites use bold,\n13027|955|F|160057.71|1992-01-16|1-URGENT|Clerk#000000236|0|ongside of the ideas wak\n13028|685|F|231028.06|1994-04-09|1-URGENT|Clerk#000000687|0|regular platelets a\n13029|340|O|177062.13|1997-11-11|3-MEDIUM|Clerk#000000699|0|riously above the blithely f\n13030|1459|F|146978.50|1994-07-23|3-MEDIUM|Clerk#000000878|0|hely ironic packages are slyly regular ideas. quickly final pack\n13031|7|F|237595.83|1992-12-05|5-LOW|Clerk#000000325|0|s are furiously. busy requests haggle furiously pinto beans. asymptotes are. \n13056|437|F|193618.95|1994-04-23|4-NOT SPECIFIED|Clerk#000000174|0|ounts. quickly regular packages above the furiously unusual sauternes ha\n13057|67|O|102973.85|1997-12-16|1-URGENT|Clerk#000000125|0|y. final requests breach furiously. regular deposits engage. \n13058|1156|O|12461.38|1997-03-26|1-URGENT|Clerk#000000685|0|ly! special instructions sleep furiously throughout the deposits.\n13059|136|O|197435.35|1997-05-31|5-LOW|Clerk#000000463|0| requests. blithely special\n13060|1345|O|272394.13|1997-02-06|4-NOT SPECIFIED|Clerk#000000724|0|ously above the pending, special theodolites. dolphins ar\n13061|821|O|76034.36|1997-09-17|1-URGENT|Clerk#000000937|0|regular deposits. s\n13062|1291|O|63228.29|1996-05-03|2-HIGH|Clerk#000000216|0|s asymptotes cajole carefully regular depen\n13063|1412|O|318204.60|1997-01-15|5-LOW|Clerk#000000506|0|packages are slyly fluffil\n13088|1460|F|261581.56|1993-04-23|4-NOT SPECIFIED|Clerk#000000089|0|s. even, final packages on the carefully regular platelets cajole quick\n13089|1403|O|277440.38|1995-12-15|4-NOT SPECIFIED|Clerk#000000502|0|e slyly ironic pinto beans. blithely express accounts boost. pen\n13090|130|F|146458.92|1993-07-27|4-NOT SPECIFIED|Clerk#000000717|0|packages impress quickly across the fluffily regul\n13091|856|F|200741.15|1994-11-16|3-MEDIUM|Clerk#000000786|0|ironic requests nag furiously stealthy pinto beans. slyly even pains \n13092|1430|O|142733.13|1998-06-24|1-URGENT|Clerk#000000241|0|eans haggle furiously about the slyly daring deposi\n13093|1132|O|144898.05|1996-05-05|2-HIGH|Clerk#000000244|0| slyly even sheaves haggl\n13094|1129|F|64336.85|1993-08-19|4-NOT SPECIFIED|Clerk#000000376|0|theodolites serve around the slyly special accounts. instruc\n13095|1454|O|282335.80|1996-07-22|1-URGENT|Clerk#000000837|0|ve theodolites detect according to the slyly special grouches. un\n13120|1039|F|90929.21|1994-12-15|5-LOW|Clerk#000000149|0| to the quickly express requests nag carefully stealthily regular requ\n13121|878|F|190958.73|1993-03-08|2-HIGH|Clerk#000000148|0|cross the blithely ironic the\n13122|242|F|164977.59|1994-09-24|2-HIGH|Clerk#000000229|0|t blithely furiously special accounts. carefully regular \n13123|841|O|208768.85|1998-07-03|2-HIGH|Clerk#000000231|0|fter the slyly speci\n13124|10|F|57676.34|1993-12-18|3-MEDIUM|Clerk#000000862|0|ggle quickly according to the carefully even pac\n13125|355|F|190034.02|1993-12-14|4-NOT SPECIFIED|Clerk#000000302|0|ly quickly final ideas. carefully bold ideas slee\n13126|956|O|131328.54|1995-10-20|4-NOT SPECIFIED|Clerk#000000919|0|ithely bold foxes wake. accounts according to the quickly ev\n13127|1165|F|38692.83|1993-08-01|3-MEDIUM|Clerk#000000178|0|cording to the ironic asymptotes sleep care\n13152|850|F|283938.70|1994-02-15|2-HIGH|Clerk#000000367|0|ts. blithely final instructions sleep quickly final requ\n13153|1144|O|165659.63|1996-11-06|1-URGENT|Clerk#000000666|0|refully across the carefully careful notornis. qu\n13154|520|O|286298.87|1997-10-22|5-LOW|Clerk#000000557|0|foxes? quickly blithe ideas nag slyly. blithely regular packages cajole. fluff\n13155|1079|O|190720.20|1996-06-18|1-URGENT|Clerk#000000727|0|ns play. express requests cajole quickly theodolites. carefully furious dino\n13156|1283|O|180693.69|1995-10-06|5-LOW|Clerk#000000482|0| about the carefully regular a\n13157|656|F|277209.62|1992-02-12|3-MEDIUM|Clerk#000000655|0|regular requests wake. never special ac\n13158|134|F|198301.99|1992-11-21|1-URGENT|Clerk#000000638|0|ns promise slyly even requests. carefully ironic packages haggle\n13159|784|O|175684.88|1996-12-02|1-URGENT|Clerk#000000262|0|efully among the blithely regular instructions. carefu\n13184|472|O|21843.01|1998-03-28|4-NOT SPECIFIED|Clerk#000000845|0|ilent deposits haggle furiously about the silent deposits. \n13185|805|O|300236.26|1997-07-15|5-LOW|Clerk#000000862|0|ly even tithes sleep daringly. unusual, ironic accou\n13186|556|O|206062.94|1996-09-18|2-HIGH|Clerk#000000427|0|ckly pending accounts. quick\n13187|923|F|78566.07|1993-12-09|2-HIGH|Clerk#000000421|0|ns haggle slyly. slyly pending instructions at the carefully final\n13188|1436|O|95591.25|1998-07-20|3-MEDIUM|Clerk#000000061|0|ckey players. accounts haggle fluffily against the foxes. unusual, expre\n13189|94|F|2361.93|1995-04-05|1-URGENT|Clerk#000000002|0|l deposits above the pending pinto beans are\n13190|136|O|24775.00|1998-05-04|4-NOT SPECIFIED|Clerk#000000173|0|furiously express accounts. carefully i\n13191|214|O|331578.75|1997-12-24|5-LOW|Clerk#000000080|0|regular accounts at the blithely even sh\n13216|1075|F|97503.77|1992-04-14|3-MEDIUM|Clerk#000000901|0|e furiously express pinto beans. unusual deposits are. even instructio\n13217|946|O|67106.36|1997-09-25|5-LOW|Clerk#000000659|0|se dependencies nag blithely after the blithely regular ideas. \n13218|1438|F|210525.16|1994-05-17|2-HIGH|Clerk#000000715|0|ccounts. slyly pending instru\n13219|535|O|190074.19|1997-10-15|2-HIGH|Clerk#000000251|0|s. regular, special platelets wake. unusual, even \n13220|1480|O|134628.97|1998-07-31|1-URGENT|Clerk#000000586|0|refully final platelets: accounts of the busily special \n13221|946|O|106536.33|1995-10-15|4-NOT SPECIFIED|Clerk#000000459|0|lthy accounts around the \n13222|1363|O|93525.50|1997-05-04|4-NOT SPECIFIED|Clerk#000000435|0|quests. furiously silent packages\n13223|352|O|45917.67|1996-08-08|4-NOT SPECIFIED|Clerk#000000713|0| slyly blithely regular requests. furiously regular multipliers wake \n13248|892|O|39800.14|1997-02-02|3-MEDIUM|Clerk#000000920|0|eans use. even packages eat after the ca\n13249|569|O|222028.79|1997-07-18|5-LOW|Clerk#000000660|0|nstructions nag blithely quick\n13250|1325|O|141857.68|1997-08-18|3-MEDIUM|Clerk#000000150|0|sts about the final requests a\n13251|982|O|123168.81|1997-03-28|1-URGENT|Clerk#000000928|0|cording to the blithely silent deposit\n13252|1309|F|163473.91|1994-03-23|4-NOT SPECIFIED|Clerk#000000026|0|n ideas must boost blithely. expre\n13253|790|O|277363.52|1996-10-28|2-HIGH|Clerk#000000767|0|refully even packages print. blithely bold deposits boost fluffily\n13254|1066|O|204264.00|1998-02-23|3-MEDIUM|Clerk#000000890|0|blithely silent packages. fin\n13255|1115|F|113899.92|1993-07-18|3-MEDIUM|Clerk#000000993|0|metimes across the even deposits. pending accounts affix blithely furi\n13280|314|F|236922.45|1994-09-12|2-HIGH|Clerk#000000073|0|pinto beans promise carefully final requests. r\n13281|1480|F|164425.24|1992-04-23|1-URGENT|Clerk#000000327|0|slyly. regularly ironic deposits haggle bli\n13282|817|F|153262.10|1992-05-10|5-LOW|Clerk#000000436|0|luffily express foxes \n13283|946|O|129981.62|1997-12-29|4-NOT SPECIFIED|Clerk#000000842|0|dependencies? quickly quick deposits cajol\n13284|851|F|136289.59|1992-08-13|2-HIGH|Clerk#000000186|0|equests. slyly final de\n13285|406|O|8165.39|1995-05-17|4-NOT SPECIFIED|Clerk#000000312|0|r packages. silent, even \n13286|1114|O|81226.73|1995-10-12|2-HIGH|Clerk#000000186|0|osits. slyly express requests promise quickly. bold, final asymptote\n13287|895|O|168246.03|1997-03-10|3-MEDIUM|Clerk#000000335|0|e theodolites. blithely even deposits dazzle care\n13312|1324|O|80963.75|1996-11-04|1-URGENT|Clerk#000000904|0|final accounts wake slyl\n13313|1438|F|254146.32|1994-07-17|4-NOT SPECIFIED|Clerk#000000036|0|d platelets. slyly express requests print carefully across the quickl\n13314|412|F|226838.60|1994-05-14|2-HIGH|Clerk#000000842|0|ajole stealthily even deposits. furiously express foxes was evenly r\n13315|449|O|192954.51|1996-10-16|2-HIGH|Clerk#000000759|0|thinly ironic accoun\n13316|1316|F|42308.46|1992-10-23|5-LOW|Clerk#000000555|0|tain even, pending instructions. quickly pending pinto beans sleep against the\n13317|865|O|43720.88|1996-04-12|3-MEDIUM|Clerk#000000096|0|ggedly unusual packages along the carefully final \n13318|325|O|247805.76|1997-06-11|4-NOT SPECIFIED|Clerk#000000381|0|ounts. sometimes ironic packages sleep stealthily. slyly regular accounts are\n13319|425|O|138467.85|1996-01-19|1-URGENT|Clerk#000000158|0|ecial excuses boost. carefully regular asymptotes was acco\n13344|367|O|90946.06|1997-03-27|4-NOT SPECIFIED|Clerk#000000924|0|de the quickly regular excuses. blithely express packages nod fur\n13345|1339|F|136543.60|1992-09-03|4-NOT SPECIFIED|Clerk#000000876|0|thely special dolphins. slyly regular foxes sleep carefull\n13346|1012|F|244898.42|1992-11-16|5-LOW|Clerk#000000594|0|thely bold foxes sleep final reque\n13347|560|F|250026.98|1993-04-24|4-NOT SPECIFIED|Clerk#000000933|0|uriously against the carefully final foxes. carefully\n13348|1267|F|94270.40|1993-11-08|3-MEDIUM|Clerk#000000878|0|ns doubt fluffily across the requests. even theodolites around the fluffi\n13349|1454|F|91069.45|1994-09-20|2-HIGH|Clerk#000000916|0|usual courts haggle ruthlessly. final deposi\n13350|1307|O|246713.06|1998-05-06|2-HIGH|Clerk#000000152|0|gular requests cajole quickl\n13351|709|F|203403.50|1993-05-07|3-MEDIUM|Clerk#000000892|0|eposits haggle slowly alongside of \n13376|1456|F|245552.88|1992-10-10|3-MEDIUM|Clerk#000000807|0| deposits. furiously regular packages unwind slyly bl\n13377|394|O|60077.37|1995-07-19|2-HIGH|Clerk#000000094|0|ily along the bold deposits. carefully unusual accounts use quickl\n13378|1151|F|207411.39|1994-09-22|5-LOW|Clerk#000000729|0|nis integrate; deposits wake bravely special frets. furiously expre\n13379|286|F|107484.04|1993-11-08|5-LOW|Clerk#000000398|0|hely special dependencies. carefully unusual deposits are regularly. b\n13380|934|F|129938.34|1994-07-16|1-URGENT|Clerk#000000683|0|ffy deposits haggle blithely. bold deposits amon\n13381|1109|O|102850.64|1995-08-18|3-MEDIUM|Clerk#000000835|0|s platelets nod carefully. final, unusual deposits nag blith\n13382|1207|O|64313.96|1995-11-02|4-NOT SPECIFIED|Clerk#000000427|0|kages. regular packages boost slyly alongside \n13383|1346|F|165841.68|1992-06-16|4-NOT SPECIFIED|Clerk#000000258|0|pecial ideas boost stealthily about the regular, express pinto bean\n13408|1370|F|211990.67|1994-02-15|5-LOW|Clerk#000000772|0| since the pinto beans. furiously pending accounts ru\n13409|1279|F|46643.47|1993-07-14|4-NOT SPECIFIED|Clerk#000000002|0|n dependencies detect furi\n13410|592|F|71187.66|1993-06-07|4-NOT SPECIFIED|Clerk#000000922|0|lly ironic braids are. quickly final gifts haggle si\n13411|1376|F|130230.72|1994-04-13|2-HIGH|Clerk#000000179|0|l foxes. silent theodolit\n13412|1240|O|115408.80|1997-07-12|1-URGENT|Clerk#000000124|0|lithely slow packages. packages past the ru\n13413|473|F|231772.27|1993-01-30|1-URGENT|Clerk#000000565|0|ironic instructions. special instructions boost quick\n13414|1333|O|143244.82|1995-07-07|3-MEDIUM|Clerk#000000693|0|across the blithely regular\n13415|1222|O|271662.08|1997-05-31|2-HIGH|Clerk#000000851|0|ully. packages haggle slyly around the \n13440|865|F|24449.73|1993-10-14|4-NOT SPECIFIED|Clerk#000000189|0|fily ironic accounts. quietly regular deposits among th\n13441|1282|O|60048.20|1995-08-31|1-URGENT|Clerk#000000983|0|tes. blithely regular packages wake enticingly. quickly da\n13442|238|O|138794.82|1997-08-06|4-NOT SPECIFIED|Clerk#000000830|0|p quickly fluffily \n13443|545|F|216308.57|1993-10-07|3-MEDIUM|Clerk#000000379|0|uriously silent packages nag. ideas cajole quickly carefully regula\n13444|176|F|304179.27|1992-01-06|2-HIGH|Clerk#000000199|0|. forges are carefully. furiously final packages are blithely ironic the\n13445|682|O|21693.67|1997-12-24|4-NOT SPECIFIED|Clerk#000000599|0|quickly. excuses cajole. idly express packages cajole blithely pending pinto b\n13446|484|O|55646.82|1996-02-29|1-URGENT|Clerk#000000050|0|lly regular requests. blithely express excuses integra\n13447|244|F|72392.32|1993-01-09|2-HIGH|Clerk#000000925|0|ts. blithely ironic ideas accord\n13472|814|O|69332.61|1995-10-27|1-URGENT|Clerk#000000454|0|uests. regular asymptotes haggle quickly aro\n13473|655|O|192774.21|1996-03-22|5-LOW|Clerk#000000655|0|usual pinto beans haggle blithely at the carefully regular p\n13474|1069|O|101285.14|1997-06-09|3-MEDIUM|Clerk#000000912|0|out the bold instructions. requests a\n13475|1403|F|295575.64|1994-06-06|1-URGENT|Clerk#000000426|0|ake among the quickly express depo\n13476|1459|F|264702.77|1993-10-17|4-NOT SPECIFIED|Clerk#000000203|0|r, permanent accounts. ironic, ironic packages dazzle at the \n13477|1276|O|143935.11|1997-11-23|3-MEDIUM|Clerk#000000242|0|express instructions. regular ideas \n13478|325|O|167211.22|1998-01-04|2-HIGH|Clerk#000000849|0|ajole ironic, stealthy theodolites. d\n13479|1217|F|170168.00|1994-12-05|2-HIGH|Clerk#000000469|0|s the furiously regular accounts. blithely \n13504|1495|O|126515.95|1997-06-18|2-HIGH|Clerk#000000486|0|ngly after the pending accounts. final instr\n13505|286|O|219315.83|1997-06-03|3-MEDIUM|Clerk#000000880|0|sly against the furiously regular packages. care\n13506|1099|O|111048.07|1995-07-22|5-LOW|Clerk#000000974|0|. slyly ironic requests among the\n13507|923|F|117398.65|1992-12-30|3-MEDIUM|Clerk#000000127|0|. bold deposits affix furiously. furiously ironi\n13508|802|O|2962.11|1997-04-17|4-NOT SPECIFIED|Clerk#000000951|0|accounts. blithely eve\n13509|946|O|68465.06|1996-10-07|2-HIGH|Clerk#000000562|0|p. dolphins are. regular reques\n13510|710|O|80383.62|1997-10-15|3-MEDIUM|Clerk#000000723|0|tions. slyly ironic in\n13511|467|O|134751.21|1998-01-03|4-NOT SPECIFIED|Clerk#000000611|0|ly regular foxes are slyly furiously bold deposits. furiously fin\n13536|316|F|192964.26|1994-08-19|3-MEDIUM|Clerk#000000262|0|posits. blithely final i\n13537|430|F|26435.99|1993-05-31|5-LOW|Clerk#000000623|0|telets haggle carefully. quickly regular frays are! slyly regular pla\n13538|782|O|65147.01|1997-02-01|2-HIGH|Clerk#000000598|0|lly alongside of the pending, unusual reque\n13539|1231|O|190664.21|1997-02-13|3-MEDIUM|Clerk#000000022|0|ding dolphins use. final pinto beans \n13540|460|F|277929.48|1993-05-24|2-HIGH|Clerk#000000994|0|. blithely even asymptotes poach blithely alongside of the doggedly un\n13541|634|O|53890.86|1996-07-29|4-NOT SPECIFIED|Clerk#000000094|0| the furiously quick accounts. final packages nag\n13542|1016|F|302527.26|1994-03-09|5-LOW|Clerk#000000170|0|ts are until the furiously \n13543|790|F|24097.46|1992-08-09|3-MEDIUM|Clerk#000000636|0|l dependencies nag against the packages. expr\n13568|31|F|81474.17|1993-05-07|5-LOW|Clerk#000000024|0|sts. carefully bold dolphins cajole across the even requ\n13569|839|P|187147.11|1995-04-11|1-URGENT|Clerk#000000973|0|s? regular platelets cajole above the furiousl\n13570|1468|F|226767.02|1992-12-03|4-NOT SPECIFIED|Clerk#000000602|0|e of the theodolites. slyly pending deposits between the regula\n13571|1231|O|142142.08|1998-06-04|5-LOW|Clerk#000000452|0|ites? carefully regular\n13572|1498|F|115177.91|1992-05-18|1-URGENT|Clerk#000000144|0|inal requests are regularly against th\n13573|898|O|78389.93|1997-04-23|5-LOW|Clerk#000000144|0| slyly final packages. carefully e\n13574|151|O|72886.11|1997-11-14|1-URGENT|Clerk#000000472|0|y above the fluffily final instructions. express\n13575|349|F|92113.36|1992-08-30|1-URGENT|Clerk#000000008|0|! express, express instruct\n13600|1129|F|124567.73|1993-07-11|4-NOT SPECIFIED|Clerk#000000910|0|lites boost furiously after the quickly ironic packages. carefully fina\n13601|8|F|256717.52|1992-08-17|5-LOW|Clerk#000000608|0| ironic, special foxes-\n13602|1348|F|284393.60|1994-01-14|5-LOW|Clerk#000000312|0|nusual packages cajole stealthily regular accounts. regular, \n13603|496|O|198335.91|1997-06-09|4-NOT SPECIFIED|Clerk#000000777|0|nic deposits? blithely ironic packages do nag\n13604|1141|F|280511.75|1994-02-03|4-NOT SPECIFIED|Clerk#000000798|0|p stealthily. requests thrash carefully. accounts wake furiously afte\n13605|1388|O|201717.90|1996-06-14|3-MEDIUM|Clerk#000000099|0| regular depths use furiou\n13606|160|F|209589.77|1994-04-27|4-NOT SPECIFIED|Clerk#000000310|0|eas. unusual courts haggle slyly ruthless realms. even, unusua\n13607|358|F|210043.39|1993-11-19|1-URGENT|Clerk#000000366|0|es mold. blithely express packages integrate after the bold pinto beans. b\n13632|1084|F|264841.12|1994-03-11|3-MEDIUM|Clerk#000000635|0|quickly special ideas use bravely bold, bold packag\n13633|1373|F|173408.51|1992-04-04|4-NOT SPECIFIED|Clerk#000000419|0|ifts cajole quickly \n13634|869|P|43165.51|1995-04-19|2-HIGH|Clerk#000000538|0|ain carefully according to t\n13635|269|F|121071.76|1994-05-02|1-URGENT|Clerk#000000172|0|uctions thrash quickly. furiously regular requests about the n\n13636|107|F|171401.11|1994-01-23|5-LOW|Clerk#000000855|0|gular ideas wake among th\n13637|188|O|66820.97|1995-12-11|1-URGENT|Clerk#000000074|0| quickly ironic requests. care\n13638|1361|O|327915.84|1995-10-01|2-HIGH|Clerk#000000261|0| ironic requests haggle quickly ironic packages. final \n13639|1009|O|126608.97|1997-06-13|2-HIGH|Clerk#000000848|0|s cajole slyly doggedly ironic asymptotes. ca\n13664|814|O|292106.85|1995-12-16|3-MEDIUM|Clerk#000000542|0|ely bold theodolites nag. pinto beans above the p\n13665|1039|O|181151.79|1996-04-08|1-URGENT|Clerk#000000591|0|as wake even, pending requests\n13666|445|F|59180.35|1992-03-03|4-NOT SPECIFIED|Clerk#000000031|0| the packages. ironi\n13667|1216|O|208665.97|1996-12-04|2-HIGH|Clerk#000000893|0| ruthless ideas poach ironically-- unusual\n13668|878|F|218731.00|1992-02-25|1-URGENT|Clerk#000000198|0|press ideas. blithely unusual instructions are blithely. carefully final pac\n13669|454|O|178957.15|1996-08-15|3-MEDIUM|Clerk#000000955|0| instructions could have to cajol\n13670|226|F|153065.25|1992-05-24|1-URGENT|Clerk#000000988|0|s haggle at the even platelets. even\n13671|1291|O|218819.92|1996-07-14|1-URGENT|Clerk#000000321|0| blithely regular requests. furiously special idea\n13696|722|O|118614.92|1997-08-28|5-LOW|Clerk#000000882|0|ag fluffily carefully even theodolite\n13697|1457|O|103807.36|1998-04-06|4-NOT SPECIFIED|Clerk#000000473|0|st the blithely unusual foxes cajole fluf\n13698|1483|F|155040.76|1994-08-05|4-NOT SPECIFIED|Clerk#000000640|0|iously bold deposits are carefully blithely unusual \n13699|577|O|22846.00|1997-04-08|4-NOT SPECIFIED|Clerk#000000746|0|. ironically ironic foxes among the f\n13700|1063|F|20210.10|1992-02-14|1-URGENT|Clerk#000000801|0|heodolites cajole blithely. blithely unusual de\n13701|1270|O|222424.50|1998-05-18|2-HIGH|Clerk#000000270|0|final accounts wake furiously among the regular foxes. slyly bold t\n13702|1132|O|51969.64|1995-09-11|5-LOW|Clerk#000000834|0|round the fluffily silen\n13703|236|O|219200.99|1996-12-29|5-LOW|Clerk#000000778|0| quickly furiously regular accou\n13728|4|O|123722.52|1995-12-11|2-HIGH|Clerk#000000094|0|theodolites. ironic deposits boost among the slyly regular instru\n13729|79|F|88426.57|1994-01-02|4-NOT SPECIFIED|Clerk#000000866|0|y regular platelets\n13730|952|F|36769.57|1995-05-10|2-HIGH|Clerk#000000826|0|- quickly even pinto beans boost fluffily ironic, even requests. ironic, u\n13731|1475|F|209159.47|1993-05-10|4-NOT SPECIFIED|Clerk#000000549|0|beans detect boldly. sheaves after the eve\n13732|605|F|166563.09|1992-11-28|1-URGENT|Clerk#000000599|0|uriously regular courts \n13733|100|O|328307.40|1998-01-03|5-LOW|Clerk#000000431|0|e quickly express foxes. blithely ironic instructio\n13734|124|O|156502.58|1996-08-14|2-HIGH|Clerk#000000344|0|n deposits should have to thrash fluffily quickly regu\n13735|58|F|130804.16|1994-05-22|3-MEDIUM|Clerk#000000589|0|ccounts are slyly blithely final accounts. furiously final accounts w\n13760|964|O|130352.76|1996-06-03|5-LOW|Clerk#000000053|0|s wake. blithely bold dependencies\n13761|169|F|199387.32|1994-10-18|5-LOW|Clerk#000000226|0|ainst the quickly regular accounts. quickly en\n13762|1337|F|210739.37|1993-06-22|3-MEDIUM|Clerk#000000053|0|re blithely ideas. blithely regular ideas haggle above the foxe\n13763|1142|O|164241.80|1997-06-27|3-MEDIUM|Clerk#000000027|0|as affix carefully express ideas. carefully ironic asymptotes unwin\n13764|598|O|122061.97|1995-07-14|3-MEDIUM|Clerk#000000236|0|ronic deposits? furiously re\n13765|715|F|156724.02|1994-04-06|4-NOT SPECIFIED|Clerk#000000969|0|ronic theodolites will have to haggle except the slyl\n13766|1387|F|45293.38|1992-10-19|1-URGENT|Clerk#000000274|0|boost unusual accounts. regular reques\n13767|923|F|211468.27|1993-08-27|2-HIGH|Clerk#000000679|0|. thinly express dolphins sleep; slyly final instr\n13792|1366|O|48602.86|1996-02-04|4-NOT SPECIFIED|Clerk#000000980|0|y slyly final foxes. slyly even ideas affix furiously dependencies. caref\n13793|904|O|18039.82|1995-09-18|2-HIGH|Clerk#000000222|0|e blithely above the slyly pending ideas. furiously furious pl\n13794|587|F|234351.58|1994-01-05|1-URGENT|Clerk#000000119|0|ages cajole furiously alongside of the slyly express ideas-\n13795|1406|P|233445.40|1995-04-11|4-NOT SPECIFIED|Clerk#000000673|0|ld dolphins cajole quickly unusual instructions. \n13796|1004|O|245467.47|1997-06-22|4-NOT SPECIFIED|Clerk#000000344|0|special deposits wake\n13797|272|O|227061.71|1996-08-26|1-URGENT|Clerk#000000379|0|usly silent theodolites wake slyly across\n13798|1298|O|123932.29|1996-02-12|1-URGENT|Clerk#000000778|0|fluffily special, bol\n13799|994|F|142646.96|1993-12-02|1-URGENT|Clerk#000000945|0|g according to the regularly even requests. carefully bold accoun\n13824|1096|F|115028.49|1994-12-28|5-LOW|Clerk#000000604|0|equests. slow, sly ideas cajole fu\n13825|56|F|174217.02|1994-03-22|2-HIGH|Clerk#000000038|0|ests. slyly express pin\n13826|232|O|160230.62|1997-02-22|3-MEDIUM|Clerk#000000053|0|ost packages. slyly pending requests are c\n13827|479|F|44465.43|1993-06-21|5-LOW|Clerk#000000455|0|busily close requests. ironic, even pinto beans use furiously requests. c\n13828|985|F|85589.73|1994-07-13|1-URGENT|Clerk#000000682|0| unusual instructions sleep quickly. packages \n13829|1075|O|188654.94|1996-09-22|3-MEDIUM|Clerk#000000010|0|raids alongside of the quick\n13830|598|F|169613.65|1994-12-22|2-HIGH|Clerk#000000693|0|impress. even, special requests alongside of the instructions cajole fu\n13831|740|O|234924.78|1996-04-17|1-URGENT|Clerk#000000204|0|efully unusual theodolites s\n13856|1156|O|154144.16|1997-11-28|4-NOT SPECIFIED|Clerk#000000601|0| special theodolites cajole fluffi\n13857|275|O|9265.55|1995-08-15|1-URGENT|Clerk#000000012|0|uses boost furiously even\n13858|1105|F|44168.23|1994-07-13|2-HIGH|Clerk#000000003|0| fluffily even, regular accounts. furiously permanent dependencies are. slyl\n13859|221|O|228467.72|1997-01-09|4-NOT SPECIFIED|Clerk#000000630|0|; enticingly ironic deposits detect along the blithely bold \n13860|454|F|61875.64|1994-09-25|3-MEDIUM|Clerk#000000878|0|ccounts nod carefully. express requests whithout the slyly regular in\n13861|1249|F|223838.87|1993-08-12|2-HIGH|Clerk#000000153|0|ng requests. quickly ironic packages around th\n13862|1240|O|233858.28|1997-09-03|3-MEDIUM|Clerk#000000838|0|odolites integrate carefully unusual requests. sly ideas are\n13863|1399|F|132450.97|1992-05-31|2-HIGH|Clerk#000000471|0|y special deposits boost ca\n13888|733|F|6632.13|1993-05-04|3-MEDIUM|Clerk#000000635|0|ly regular pinto beans boost about the sheav\n13889|848|O|73735.52|1995-09-05|3-MEDIUM|Clerk#000000379|0|ng the silent foxes. even, quiet patterns about th\n13890|34|O|50473.48|1996-05-25|5-LOW|Clerk#000000263|0|eposits along the fluffily final accounts wake final, pending foxe\n13891|508|O|62430.54|1996-07-05|4-NOT SPECIFIED|Clerk#000000147|0|es are slyly. special packages use slyly against the theodolites. pa\n13892|1013|F|216407.28|1992-04-28|2-HIGH|Clerk#000000365|0|ithely ironic packages sleep after the ruthless instructions. quickly dogged t\n13893|289|O|132790.59|1998-04-12|2-HIGH|Clerk#000000931|0| blithely regular, unusual dep\n13894|1246|F|125452.99|1992-04-11|3-MEDIUM|Clerk#000000524|0|y pending hockey players cajole fluffily special sentiments. closely ironic \n13895|671|F|225938.31|1993-11-21|2-HIGH|Clerk#000000381|0|across the fluffily even accounts. carefully silent ideas wake. blithe\n13920|37|F|155193.67|1994-10-10|1-URGENT|Clerk#000000060|0| unusual deposits sleep blithely\n13921|49|O|152288.97|1997-08-19|4-NOT SPECIFIED|Clerk#000000358|0|elets doubt against the slyly final\n13922|241|O|252983.77|1995-08-03|2-HIGH|Clerk#000000301|0|requests use ironic de\n13923|1000|F|155909.13|1992-07-09|2-HIGH|Clerk#000000189|0|s. regular packages\n13924|328|F|3658.90|1994-12-20|2-HIGH|Clerk#000000251|0|haggle quickly ironic platelets. even tithes wake blit\n13925|73|O|162158.06|1997-01-06|1-URGENT|Clerk#000000303|0|uriously ironic accounts around the never regular packages\n13926|49|O|52124.55|1997-07-13|4-NOT SPECIFIED|Clerk#000000881|0|ages haggle furiously bold, final accounts. special foxes \n13927|562|F|121815.82|1994-08-27|5-LOW|Clerk#000000534|0|lar, silent packages wake quickly \n13952|571|O|115051.12|1997-04-25|4-NOT SPECIFIED|Clerk#000000896|0|ly ironic pinto beans. furiously even accounts haggle carefully accordi\n13953|388|O|233895.85|1995-06-01|1-URGENT|Clerk#000000337|0|riously deposits. blithely\n13954|775|O|132924.82|1996-05-02|5-LOW|Clerk#000000809|0|t furiously above the final requ\n13955|1409|O|161957.19|1998-05-12|2-HIGH|Clerk#000000359|0|ar foxes. packages nag blithely fina\n13956|716|P|107050.72|1995-03-13|1-URGENT|Clerk#000000062|0|e final deposits are according to the regula\n13957|475|O|176779.20|1995-07-22|4-NOT SPECIFIED|Clerk#000000019|0|mpress carefully slyly even requests. blithely pending t\n13958|616|F|52000.53|1993-01-24|5-LOW|Clerk#000000863|0|ly. carefully special accounts among the slyly express requests sleep afte\n13959|451|F|99030.36|1994-04-05|3-MEDIUM|Clerk#000000952|0|efully bold requests above the busy, express deposits use express p\n13984|401|F|197913.83|1992-09-25|5-LOW|Clerk#000000750|0|final, special foxes. carefully express theodolites boost ne\n13985|397|O|174830.89|1998-06-14|2-HIGH|Clerk#000000876|0|old accounts wake instructions. furiously ironic requests use quickly. quickl\n13986|1333|O|103675.53|1995-10-12|2-HIGH|Clerk#000000793|0|riously final foxes. fur\n13987|643|O|64387.39|1998-04-14|4-NOT SPECIFIED|Clerk#000000268|0|es. furiously even \n13988|53|F|114321.03|1992-08-02|4-NOT SPECIFIED|Clerk#000000556|0|le daring packages. final, final escapades cajole expre\n13989|1480|O|189523.80|1997-07-06|5-LOW|Clerk#000000996|0|ate about the bold, express packages. closely brave accou\n13990|751|O|143454.90|1998-02-07|1-URGENT|Clerk#000000683|0| slyly according to the furiously spe\n13991|1204|F|261061.37|1992-03-07|2-HIGH|Clerk#000000245|0|nic, special accounts. quickly pending accounts wake\n14016|1145|F|201366.56|1992-07-12|5-LOW|Clerk#000000150|0|ymptotes: packages around the\n14017|1033|F|142714.54|1994-03-08|2-HIGH|Clerk#000000238|0|unusual deposits sleep furiously \n14018|367|F|101182.03|1993-06-07|2-HIGH|Clerk#000000376|0|uctions. pending instructions cajole about th\n14019|184|O|216427.67|1997-09-08|1-URGENT|Clerk#000000961|0|tect fluffily final pac\n14020|841|O|75424.30|1998-01-29|2-HIGH|Clerk#000000275|0|aggle quickly along the blithely bold ins\n14021|62|P|206447.05|1995-03-08|2-HIGH|Clerk#000000467|0|sly. carefully regular \n14022|685|F|71271.43|1995-02-14|2-HIGH|Clerk#000000732|0|es. quickly express deposits nag across the iron\n14023|787|F|127450.06|1993-02-21|4-NOT SPECIFIED|Clerk#000000349|0|ironic, regular dugouts\n14048|1016|F|221205.20|1993-05-18|4-NOT SPECIFIED|Clerk#000000548|0|s. blithely even asy\n14049|952|O|70228.24|1996-01-29|5-LOW|Clerk#000000189|0|yly express epitaphs are slyly express depo\n14050|88|O|161689.12|1997-08-22|5-LOW|Clerk#000000283|0|en courts. carefully express packages\n14051|599|F|22238.93|1993-09-17|4-NOT SPECIFIED|Clerk#000000098|0|ss blithely among the closely r\n14052|1190|F|23078.95|1995-02-07|4-NOT SPECIFIED|Clerk#000000647|0|mong the regular foxes boost blithely special pinto beans. furious\n14053|1453|F|25549.20|1993-02-09|2-HIGH|Clerk#000000597|0| carefully ironic courts sleep quickly pending, pending packag\n14054|517|O|285667.97|1996-01-11|4-NOT SPECIFIED|Clerk#000000890|0|unusual waters above the dependencies cajole r\n14055|904|F|90239.70|1994-09-02|2-HIGH|Clerk#000000825|0|ounts. carefully final somas unwind fluffily.\n14080|791|O|62517.87|1998-05-29|4-NOT SPECIFIED|Clerk#000000106|0|nic packages use final, bold\n14081|895|F|114279.17|1993-10-07|2-HIGH|Clerk#000000857|0|uests. fluffily final packages wake slow, ironic dep\n14082|875|O|48224.95|1998-05-08|4-NOT SPECIFIED|Clerk#000000700|0|bove the sometimes even deposits. ruthlessly unusual deposit\n14083|464|F|11732.48|1994-08-16|4-NOT SPECIFIED|Clerk#000000420|0| quickly ruthless a\n14084|647|O|303760.75|1995-08-03|4-NOT SPECIFIED|Clerk#000000160|0|luffily above the final packages. requests\n14085|895|F|136353.74|1994-02-05|2-HIGH|Clerk#000000503|0|eas boost carefully evenly pending requests-- furiously thin accounts \n14086|346|F|54201.92|1993-10-21|5-LOW|Clerk#000000289|0|ial deposits sleep. express requests nag carefull\n14087|275|O|208589.10|1997-02-12|5-LOW|Clerk#000000927|0|dependencies. quickly ironic frets above the fluffily ironic accounts use \n14112|112|F|20742.84|1994-07-25|5-LOW|Clerk#000000463|0|ickly dogged accounts breach doggedly \n14113|835|O|27091.83|1995-08-05|2-HIGH|Clerk#000000885|0|; carefully pending accounts use among the pending packag\n14114|895|F|171306.58|1994-04-07|1-URGENT|Clerk#000000397|0|beans play regular, ironic deposits. instructio\n14115|46|F|68488.60|1994-08-15|4-NOT SPECIFIED|Clerk#000000493|0|ckages sleep across the regular, silent pinto beans. bold, un\n14116|1291|O|192210.86|1995-10-10|2-HIGH|Clerk#000000065|0|. carefully bold ideas sleep. carefully bold idea\n14117|1297|F|281939.12|1993-08-26|4-NOT SPECIFIED|Clerk#000000480|0|uickly ironic ideas boost among the furiously pending deposits.\n14118|385|F|41143.91|1993-05-23|4-NOT SPECIFIED|Clerk#000000730|0| toward the express, unusual instructions sleep accordin\n14119|482|F|203405.63|1993-06-01|5-LOW|Clerk#000000989|0|osits. furiously pending accounts across the slyly\n14144|301|O|308016.67|1997-04-26|1-URGENT|Clerk#000000089|0|nusual instructions sleep. blithely silent requests thr\n14145|7|O|270751.41|1997-04-17|1-URGENT|Clerk#000000920|0|x carefully ideas. evenly silent \n14146|592|O|108225.88|1996-01-03|3-MEDIUM|Clerk#000000968|0|xpress accounts. even packages about the pen\n14147|728|F|262030.95|1993-03-08|4-NOT SPECIFIED|Clerk#000000152|0|, special epitaphs haggle a\n14148|965|O|130798.36|1998-04-30|3-MEDIUM|Clerk#000000188|0|ag carefully special foxes. carefully unus\n14149|73|O|157174.97|1998-01-28|1-URGENT|Clerk#000000303|0|ests. ironic, special pinto beans try to print slyly along the pending\n14150|89|F|196189.74|1994-02-09|2-HIGH|Clerk#000000695|0|y ironic accounts haggle across the furi\n14151|1418|O|286984.16|1997-06-06|4-NOT SPECIFIED|Clerk#000000278|0|special accounts. blithely express deposits cajole slyly slyly ex\n14176|991|F|141050.11|1994-03-13|5-LOW|Clerk#000000407|0|eodolites. slyly final platelets ca\n14177|565|F|112612.65|1992-04-03|5-LOW|Clerk#000000442|0|nooze carefully slyly final packages. platelets about the q\n14178|391|F|11236.45|1995-01-01|4-NOT SPECIFIED|Clerk#000000246|0| quick accounts nag carefully. regular accounts could\n14179|458|O|384265.43|1997-09-07|1-URGENT|Clerk#000000053|0|about the bold, final pinto bean\n14180|292|O|187330.32|1997-05-25|3-MEDIUM|Clerk#000000478|0|out the carefully regular accoun\n14181|94|O|226844.98|1998-02-13|3-MEDIUM|Clerk#000000106|0|efully even depende\n14182|934|F|68283.44|1994-06-23|5-LOW|Clerk#000000791|0|arefully. sentiments integrate \n14183|875|O|85725.94|1997-03-17|5-LOW|Clerk#000000052|0| final requests detect slyly at the\n14208|385|O|121214.10|1995-05-01|4-NOT SPECIFIED|Clerk#000000758|0|sly slyly silent requests. carefully special accounts sleep\n14209|1025|O|137491.26|1996-11-04|2-HIGH|Clerk#000000211|0|leep slyly against the carefully bold dolphins.\n14210|25|F|23549.76|1992-06-27|4-NOT SPECIFIED|Clerk#000000356|0|atelets nag carefully final foxes. ironic, silent \n14211|1202|F|189400.90|1994-02-13|4-NOT SPECIFIED|Clerk#000000937|0|the blithely bold deposits are according to the accounts. f\n14212|806|F|196439.01|1992-02-17|1-URGENT|Clerk#000000384|0| packages affix special deposits? carefully special pinto bean\n14213|478|F|50949.10|1993-12-28|1-URGENT|Clerk#000000959|0|, careful platelets abo\n14214|79|O|128802.82|1997-08-15|1-URGENT|Clerk#000000891|0|thely unusual pinto beans against the blithely special warhorses hagg\n14215|1408|F|50568.60|1992-07-14|3-MEDIUM|Clerk#000000823|0|ounts haggle furiously. special gifts am\n14240|689|F|210064.90|1992-02-18|2-HIGH|Clerk#000000728|0|sly. express, unusual asymptotes across the never fin\n14241|928|F|153112.39|1992-06-15|1-URGENT|Clerk#000000866|0|c packages. instructions a\n14242|913|P|115246.93|1995-05-09|2-HIGH|Clerk#000000927|0|d have to haggle according \n14243|1073|O|63765.25|1997-02-15|3-MEDIUM|Clerk#000000943|0|sly ironic foxes nag carefully along t\n14244|205|O|53072.12|1998-03-05|5-LOW|Clerk#000000139|0|press excuses doubt permanently.\n14245|1309|F|73717.72|1993-02-12|1-URGENT|Clerk#000000975|0|furiously final foxes boost silent, final requests. slyly unusual deposi\n14246|289|O|133280.40|1997-10-10|4-NOT SPECIFIED|Clerk#000000806|0| bold pinto beans. regular accounts \n14247|671|O|219431.33|1995-08-17|1-URGENT|Clerk#000000531|0|ests haggle furiously about the pa\n14272|1348|F|97840.61|1992-02-13|3-MEDIUM|Clerk#000000075|0|ses. blithely final deposits are. stealthy accounts engage. slyly\n14273|1033|O|90555.37|1996-03-13|5-LOW|Clerk#000000938|0| special foxes lose quickly; fina\n14274|565|F|244782.98|1994-01-07|4-NOT SPECIFIED|Clerk#000000564|0|nt requests are furiously furiously daring accounts. even\n14275|1412|F|168905.36|1993-06-14|5-LOW|Clerk#000000789|0|furiously. slyly pending packages sublate furious\n14276|499|F|241009.94|1994-06-19|3-MEDIUM|Clerk#000000149|0|ly bold requests haggle slyly according to the closely\n14277|946|O|116805.58|1998-02-14|4-NOT SPECIFIED|Clerk#000000951|0|eep. thin theodolites are bl\n14278|559|O|266421.62|1998-06-03|2-HIGH|Clerk#000000768|0|jole slyly ironic theodolites. carefully even deposi\n14279|1117|O|157844.27|1998-02-09|2-HIGH|Clerk#000000441|0| after the furiously unusual realms. \n14304|788|O|215723.64|1997-11-13|3-MEDIUM|Clerk#000000488|0|egular requests affix. even requ\n14305|1427|O|80794.79|1996-07-23|1-URGENT|Clerk#000000396|0|mas cajole above the \n14306|587|F|27239.18|1993-10-30|4-NOT SPECIFIED|Clerk#000000981|0|furiously regular e\n14307|484|O|178901.14|1997-06-24|1-URGENT|Clerk#000000255|0| deposits wake. regular, special excuses us\n14308|5|O|132817.36|1997-08-11|3-MEDIUM|Clerk#000000354|0|iously. packages wake according to the furio\n14309|1382|F|103342.31|1994-01-31|1-URGENT|Clerk#000000753|0|s haggle furiously against the carefully final asymptotes. blith\n14310|1393|O|59609.50|1997-09-13|5-LOW|Clerk#000000627|0|p furiously evenly final requests. even, ironic excuses are\n14311|1412|F|46918.86|1994-08-02|1-URGENT|Clerk#000000916|0|nic pinto beans along the enticingly final reque\n14336|760|F|172716.15|1994-11-19|2-HIGH|Clerk#000000699|0|eodolites cajole carefully. quickly ironic foxes about the fluffily ironic\n14337|299|O|280177.62|1997-10-09|4-NOT SPECIFIED|Clerk#000000949|0| special ideas into the silently quick dep\n14338|752|F|97316.04|1993-02-01|5-LOW|Clerk#000000658|0|l accounts. quickly special packages h\n14339|1199|O|209018.11|1997-07-23|4-NOT SPECIFIED|Clerk#000000303|0|ar theodolites after the express, final pinto beans engage car\n14340|391|O|222337.16|1995-10-13|5-LOW|Clerk#000000386|0|theodolites. furiously ironic pinto beans are busily \n14341|667|F|197341.53|1993-09-03|4-NOT SPECIFIED|Clerk#000000669|0|lphins cajole furiously after the c\n14342|160|F|137933.49|1994-04-28|5-LOW|Clerk#000000040|0| haggle quickly. bold excuses against the regular re\n14343|1408|O|58414.89|1996-02-25|3-MEDIUM|Clerk#000000792|0| unusual deposits wake slyly even, even packages. slyly final depe\n14368|676|O|121598.53|1995-09-11|2-HIGH|Clerk#000000228|0|ly express requests affix furiously. ironically final asymptotes according \n14369|1357|O|272333.98|1996-11-26|1-URGENT|Clerk#000000063|0|sublate quickly furiously bold asymptot\n14370|958|O|163080.52|1997-05-03|2-HIGH|Clerk#000000475|0|ly regular accounts. fluffil\n14371|1192|F|116441.72|1993-06-23|2-HIGH|Clerk#000000831|0|. carefully express packages at th\n14372|523|F|166914.23|1992-10-24|1-URGENT|Clerk#000000449|0|y. ironic deposits sleep? finally special pinto bean\n14373|511|F|53131.83|1993-09-18|3-MEDIUM|Clerk#000000099|0|nto beans. fluffy requests affix bravely fluffily iron\n14374|1180|O|121655.04|1995-05-17|1-URGENT|Clerk#000000237|0|ross the blithely final deposits. asymptotes are. slyl\n14375|733|F|140929.00|1993-02-01|3-MEDIUM|Clerk#000000046|0|ans sleep. blithely regular foxes cajole before the ironic packa\n14400|632|F|262307.57|1994-02-05|3-MEDIUM|Clerk#000000479|0|uests. furiously unusual platelets hinder final packages. bold\n14401|1489|O|156911.31|1995-08-05|4-NOT SPECIFIED|Clerk#000000059|0|ly carefully even instructions. epitaphs solve instructions! bli\n14402|838|F|109228.63|1993-10-15|3-MEDIUM|Clerk#000000672|0|azzle slyly. carefully regular instructions affix carefully deposits. careful\n14403|241|O|120881.79|1998-03-02|4-NOT SPECIFIED|Clerk#000000202|0|hely packages. blithely pending dependencies wake furiously\n14404|7|O|354885.81|1996-11-03|5-LOW|Clerk#000000657|0| the furiously unus\n14405|269|O|94417.57|1996-04-24|5-LOW|Clerk#000000460|0| beans until the final, regular theodolites \n14406|409|F|194997.99|1993-03-09|3-MEDIUM|Clerk#000000623|0|s. excuses boost bl\n14407|952|F|21880.64|1993-11-05|2-HIGH|Clerk#000000124|0|cajole ruthless theodolites. carefully ironic req\n14432|1226|O|44225.65|1996-11-12|4-NOT SPECIFIED|Clerk#000000784|0|re carefully against the fluffily final theodolites. furiously unusual d\n14433|1082|O|203663.86|1996-05-13|2-HIGH|Clerk#000000011|0|es. furiously final deposits wake b\n14434|1015|F|171433.55|1995-02-16|4-NOT SPECIFIED|Clerk#000000769|0|he unusual pinto beans. special realms cajole. quietly blith\n14435|269|F|190900.78|1992-12-02|4-NOT SPECIFIED|Clerk#000000308|0|quests nag. final platelets haggle among the st\n14436|658|F|231251.51|1994-03-13|5-LOW|Clerk#000000906|0|regular requests run furiously. unusual, \n14437|721|F|285484.64|1994-05-15|1-URGENT|Clerk#000000348|0|refully even excuses alongside of the packages are busily final\n14438|1117|O|107298.91|1995-08-23|3-MEDIUM|Clerk#000000059|0| the silent ideas wake after the express requests. unusual, final inst\n14439|523|F|4033.74|1992-08-31|2-HIGH|Clerk#000000944|0|riously even courts. ev\n14464|1429|O|43902.48|1998-01-23|5-LOW|Clerk#000000197|0| blithely. special deposits\n14465|617|F|108913.02|1994-06-12|2-HIGH|Clerk#000000816|0|ndencies nag against the furiously special pin\n14466|353|O|19731.95|1996-05-22|3-MEDIUM|Clerk#000000795|0|lithely after the slyly special deposits-- quickly regular gifts acros\n14467|1126|O|160773.46|1996-05-31|4-NOT SPECIFIED|Clerk#000000927|0|regular, brave foxes\n14468|716|O|60718.43|1998-07-13|3-MEDIUM|Clerk#000000164|0|g pinto beans after the special accounts sleep carefully after the regula\n14469|1076|O|82731.79|1997-02-01|2-HIGH|Clerk#000000837|0|ep regular, final accounts? fluffily pending the\n14470|949|F|140145.86|1995-01-25|3-MEDIUM|Clerk#000000018|0| asymptotes cajole carefully final a\n14471|94|P|324194.82|1995-05-27|4-NOT SPECIFIED|Clerk#000001000|0|l pinto beans ought to nag carefully carefully ironic foxes.\n14496|1153|O|206156.59|1996-04-11|3-MEDIUM|Clerk#000000350|0|g theodolites eat c\n14497|1492|F|130424.80|1992-05-21|3-MEDIUM|Clerk#000000053|0|symptotes. slyly fluffy excuses ought to wake according to the slyly even de\n14498|115|O|172949.99|1996-10-05|3-MEDIUM|Clerk#000000709|0| deposits! quickly pend\n14499|361|F|306903.43|1993-11-15|5-LOW|Clerk#000000358|0|packages. carefully final ide\n14500|1222|O|100773.58|1995-06-25|4-NOT SPECIFIED|Clerk#000000451|0| slyly regular ideas haggle slyly unusual packages. quickly unusual pinto\n14501|607|O|83186.27|1997-08-31|2-HIGH|Clerk#000000826|0|e ideas. ironic deposits sleep according to the blithely ironi\n14502|799|O|87836.22|1996-07-21|2-HIGH|Clerk#000000875|0| furiously ironic deposits cajole around the carefully silent accounts. expre\n14503|671|F|198098.70|1995-02-06|4-NOT SPECIFIED|Clerk#000000519|0|ely regular depths haggle carefull\n14528|334|F|155412.29|1994-06-16|1-URGENT|Clerk#000000568|0|gle instead of the carefully pending pinto beans. express, ex\n14529|1372|F|193341.12|1993-09-21|5-LOW|Clerk#000000521|0|arefully unusual packages haggle carefully slyly final pinto beans. exp\n14530|133|F|240313.77|1994-04-07|2-HIGH|Clerk#000000444|0|eodolites may wake final requests. furious\n14531|1331|O|222583.58|1997-06-11|2-HIGH|Clerk#000000003|0|across the blithely even instructions. carefull\n14532|1252|O|15069.98|1996-04-17|5-LOW|Clerk#000000906|0|ep furiously according to the quickly final deposits. reg\n14533|43|O|161212.58|1996-05-06|4-NOT SPECIFIED|Clerk#000000394|0|ans alongside of the carefully ironic re\n14534|1414|F|248752.30|1993-07-19|3-MEDIUM|Clerk#000000324|0|furiously. silent foxes boost finally. dependencies among the furiously fi\n14535|1306|F|95671.80|1993-10-02|4-NOT SPECIFIED|Clerk#000000983|0|thely unusual accounts are fluffi\n14560|1258|O|33048.76|1997-12-15|3-MEDIUM|Clerk#000000587|0|ckages grow furiously-- carefully final foxes according \n14561|697|O|274551.87|1998-06-29|3-MEDIUM|Clerk#000000840|0|onic accounts. even, express excuses cajole carefully. bl\n14562|973|O|150256.16|1996-07-20|5-LOW|Clerk#000000189|0|ve the blithely even dependencies-- quickly busy realms wake quickly. permane\n14563|568|O|236613.87|1995-12-03|1-URGENT|Clerk#000000904|0|quests. quickly even theodolites nag blit\n14564|520|F|1358.25|1993-05-22|4-NOT SPECIFIED|Clerk#000000799|0|carefully even deposits. furiously express acco\n14565|1301|O|50022.91|1997-11-23|1-URGENT|Clerk#000000409|0| special foxes. blithely ironic ideas sleep carefully quietly final instruct\n14566|1480|O|221337.42|1996-10-25|3-MEDIUM|Clerk#000000118|0|ns? slyly regular theodolites haggle quickly. fluffily bol\n14567|1060|O|176372.95|1996-10-18|1-URGENT|Clerk#000000527|0|efully blithe excuses. slyly regular ideas promise. escapad\n14592|926|O|93588.02|1995-07-06|3-MEDIUM|Clerk#000000402|0|ts affix alongside of the blithely ironic pinto beans.\n14593|184|O|61820.82|1998-01-23|1-URGENT|Clerk#000000880|0| dependencies. slyly\n14594|143|F|16529.84|1993-09-07|3-MEDIUM|Clerk#000000022|0| across the carefull\n14595|202|O|32654.01|1997-12-03|3-MEDIUM|Clerk#000000854|0|kages. asymptotes dazzle. final, bold pinto be\n14596|1198|F|167802.21|1994-01-11|1-URGENT|Clerk#000000754|0| even accounts above the furiously brave dep\n14597|1192|F|293673.06|1993-08-31|1-URGENT|Clerk#000000768|0|haggle ironic, quick accounts. quickly special foxes nag slyly above the furi\n14598|898|O|171449.52|1998-02-10|4-NOT SPECIFIED|Clerk#000000229|0|slyly even deposits are carefully. final, pe\n14599|814|F|13810.80|1992-05-09|2-HIGH|Clerk#000000704|0|ke final theodolites\n14624|1177|O|210247.47|1998-04-17|4-NOT SPECIFIED|Clerk#000000014|0|heodolites. quickly regular theodolites haggle ag\n14625|1075|F|2059.98|1992-02-12|1-URGENT|Clerk#000000491|0|instructions play blithely? silent excuses snooze quick\n14626|1453|O|118335.57|1997-08-12|3-MEDIUM|Clerk#000000450|0|egular platelets are. even requ\n14627|679|F|212366.75|1994-02-05|3-MEDIUM|Clerk#000000520|0|quickly according to the b\n14628|668|F|75317.77|1993-06-07|3-MEDIUM|Clerk#000000076|0|accounts hang furiously. furiously regular theodolites \n14629|1469|F|78503.51|1993-01-08|2-HIGH|Clerk#000000344|0|uests sleep quickly. furiously ironic accounts against the quickly ironic id\n14630|277|F|189160.54|1992-10-17|5-LOW|Clerk#000000575|0| theodolites. carefully \n14631|1291|O|34502.66|1997-04-14|2-HIGH|Clerk#000000022|0|ly above the pendin\n14656|1|O|28599.83|1997-11-18|2-HIGH|Clerk#000000270|0|uests. blithely even platelet\n14657|370|F|116160.53|1994-02-28|1-URGENT|Clerk#000000756|0|ly across the ironic, ironic instructions. bold ideas\n14658|1381|F|48274.02|1994-04-07|1-URGENT|Clerk#000000175|0|kly regular requests? regular theod\n14659|25|O|145504.68|1998-02-10|3-MEDIUM|Clerk#000000260|0|l, ironic attainment\n14660|899|O|139267.14|1997-05-24|4-NOT SPECIFIED|Clerk#000000605|0|c pinto beans. fluff\n14661|1468|F|38295.53|1993-01-25|4-NOT SPECIFIED|Clerk#000000429|0| believe. silent packages haggle express instructio\n14662|1330|O|200128.12|1995-11-17|2-HIGH|Clerk#000000988|0|efully pending accounts about the bold \n14663|592|F|42406.05|1992-11-17|1-URGENT|Clerk#000000596|0|ietly above the packages. regular frets haggle slyly blithely regular pinto b\n14688|427|O|127535.78|1997-02-18|5-LOW|Clerk#000000822|0|unusual, bold deposits. furiously bold ideas cajole fluffily ironic theodo\n14689|226|O|68912.05|1998-07-31|2-HIGH|Clerk#000000100|0|quickly regular realms are along the carefully spe\n14690|935|O|41205.72|1997-05-24|1-URGENT|Clerk#000000342|0|ic packages affix sly\n14691|1381|O|81185.28|1998-04-11|5-LOW|Clerk#000000211|0|ffily even instructions use blithely. careful\n14692|478|O|53591.54|1996-02-27|5-LOW|Clerk#000000309|0|xpress deposits wake slyly after the deposits. slyly re\n14693|67|F|246072.95|1995-01-10|1-URGENT|Clerk#000000039|0|to beans. Tiresias above the special, bold packages sleep \n14694|379|O|318967.92|1998-07-28|1-URGENT|Clerk#000000742|0|nic pinto beans sleep blithely pending, unusual somas. blithely ironic id\n14695|1406|F|205288.40|1992-09-06|2-HIGH|Clerk#000000475|0|. ideas boost carefully around the even, final instruc\n14720|401|F|171435.70|1993-09-05|3-MEDIUM|Clerk#000000354|0|ongside of the quickly final excuses sleep quickly dolphins. dinos \n14721|1087|O|166081.83|1997-05-05|4-NOT SPECIFIED|Clerk#000000701|0|instructions haggle slyly. \n14722|742|O|264702.15|1997-05-28|5-LOW|Clerk#000000048|0|blithely bold requests ar\n14723|1282|O|40317.37|1997-01-04|5-LOW|Clerk#000000822|0|ic deposits affix carefully above th\n14724|1396|O|41941.26|1995-11-18|1-URGENT|Clerk#000000886|0|bold dependencies about the busy instructions haggle regular ins\n14725|569|O|261801.45|1995-06-17|2-HIGH|Clerk#000000177|0|ng asymptotes. final, ironic accounts cajole after\n14726|1279|F|93802.35|1992-01-09|5-LOW|Clerk#000000590|0| foxes. deposits cajole blithely even grouches. b\n14727|316|F|102382.66|1992-07-20|3-MEDIUM|Clerk#000000383|0|structions. daringly even packages wake slyly final requests. c\n14752|1051|F|31543.83|1994-01-30|5-LOW|Clerk#000000802|0|. carefully regular pinto beans grow idly abou\n14753|1118|O|45387.36|1997-01-05|1-URGENT|Clerk#000000855|0|n deposits across the \n14754|86|O|112289.25|1996-06-23|3-MEDIUM|Clerk#000000283|0|ns. quickly ironic packages sleep furiously fluffily unusual excuses. de\n14755|592|F|358175.60|1993-01-06|2-HIGH|Clerk#000000867|0|egular requests sleep careful packages. quickly r\n14756|328|F|132718.02|1994-09-01|5-LOW|Clerk#000000491|0|olites. ironic, final instructions pro\n14757|1420|O|55954.21|1997-12-24|4-NOT SPECIFIED|Clerk#000000011|0|ggle furiously. carefully special packages are c\n14758|1225|F|37812.49|1993-10-27|2-HIGH|Clerk#000000687|0|ages nag about the furio\n14759|70|O|40915.46|1997-01-05|3-MEDIUM|Clerk#000000034|0|he dolphins. ruthlessly regular packages play carefully. f\n14784|1036|F|188067.67|1992-03-15|3-MEDIUM|Clerk#000000479|0|lyly final theodoli\n14785|1249|F|107683.78|1994-10-10|2-HIGH|Clerk#000000446|0| slyly about the quickly sp\n14786|1255|O|34058.87|1997-04-08|2-HIGH|Clerk#000000656|0|e carefully special deposits can nag blithely express, express accounts\n14787|578|O|135287.72|1998-07-19|5-LOW|Clerk#000000522|0|deas against the blithely r\n14788|1192|O|51229.59|1997-10-23|1-URGENT|Clerk#000000647|0|e the slyly pending deposits. c\n14789|451|F|214256.97|1993-11-30|4-NOT SPECIFIED|Clerk#000000616|0|st furiously about the ca\n14790|613|O|270163.54|1996-08-21|2-HIGH|Clerk#000000347|0|p. regular deposits wake. final n\n14791|289|F|86492.66|1993-02-15|3-MEDIUM|Clerk#000000770|0|ideas wake blithely regularly regular requests. s\n14816|508|F|89977.38|1993-07-15|4-NOT SPECIFIED|Clerk#000000721|0| requests. slyly even requests haggle? unusual, regula\n14817|235|F|138541.57|1992-07-28|3-MEDIUM|Clerk#000000963|0|arefully unusual dolphins. furiously final accounts above the slyly \n14818|643|O|182026.46|1996-11-18|4-NOT SPECIFIED|Clerk#000000588|0|totes. bold, final requests are according to the deposits. quickly regular \n14819|473|F|141776.24|1993-01-26|4-NOT SPECIFIED|Clerk#000000641|0| permanent deposits. blithely final warthogs x-ray blithely slyly ir\n14820|1135|F|160021.51|1992-01-10|2-HIGH|Clerk#000000090|0|posits. regular pinto beans detect carefully at the final pinto beans. unu\n14821|1435|O|322002.95|1998-06-12|2-HIGH|Clerk#000000630|0|n packages are furiously ironic ideas. d\n14822|473|O|182443.15|1996-03-26|2-HIGH|Clerk#000000675|0|es. even, special request\n14823|832|F|190065.85|1994-02-13|5-LOW|Clerk#000000844|0| slyly final accounts: packages integrate quickly along the packages\n14848|256|O|115009.62|1996-10-17|5-LOW|Clerk#000000567|0|warthogs use furiously across the\n14849|739|O|82597.02|1997-02-07|3-MEDIUM|Clerk#000000123|0| cajole fluffily against the final, reg\n14850|1348|O|227401.85|1997-12-04|1-URGENT|Clerk#000000960|0| quickly regular braids. ironic, regular accounts are carefully\n14851|175|F|229811.50|1992-05-12|4-NOT SPECIFIED|Clerk#000000075|0|. blithely regular requests wake blithely after the furiously final accou\n14852|1060|O|34634.76|1995-12-25|3-MEDIUM|Clerk#000000907|0|ages haggle accounts. careful, ironic excuses sleep quickly dogged asymptotes\n14853|458|O|152920.35|1996-09-10|5-LOW|Clerk#000000036|0|ding instructions about the quickly pending requests sleep quickly iro\n14854|472|F|75626.15|1993-04-29|5-LOW|Clerk#000000872|0|ronic pinto beans? even deposits nag c\n14855|836|F|141609.96|1993-05-01|4-NOT SPECIFIED|Clerk#000000655|0|nent accounts sleep dependencies. furiously regular ideas ca\n14880|1210|O|209395.33|1997-11-23|4-NOT SPECIFIED|Clerk#000000545|0|equests above the foxes wake blithely about the packages: ironic ex\n14881|23|O|126051.68|1996-10-20|2-HIGH|Clerk#000000826|0|ades past the fluffily ironic foxes m\n14882|833|O|89650.26|1997-02-13|1-URGENT|Clerk#000000353|0|y against the slyly ironic accounts. blithely silent acc\n14883|865|P|148947.38|1995-05-07|5-LOW|Clerk#000000543|0|detect blithely: carefully regular asymptotes caj\n14884|1450|O|278225.35|1995-09-28|2-HIGH|Clerk#000000481|0| carefully ironic multipliers. furiously final deposits \n14885|19|O|261263.74|1997-09-21|5-LOW|Clerk#000000462|0|p slyly regular excu\n14886|337|F|84441.18|1994-06-23|3-MEDIUM|Clerk#000000757|0|slyly above the deposi\n14887|245|O|124720.14|1996-03-25|2-HIGH|Clerk#000000176|0|he pinto beans boost slyly regular deposits. fi\n14912|752|F|28125.10|1993-11-07|1-URGENT|Clerk#000000924|0| ideas cajole slyly around the ironic, bold asymptotes. \n14913|1411|F|178192.17|1994-02-15|2-HIGH|Clerk#000000823|0|gainst the carefully final orbits hang furiously above t\n14914|1199|O|191598.59|1998-02-08|1-URGENT|Clerk#000000536|0|e carefully carefully ironic requests: never final packag\n14915|811|O|59911.87|1996-09-10|4-NOT SPECIFIED|Clerk#000000708|0| accounts are above the enticingly express pin\n14916|380|F|72290.87|1994-02-28|5-LOW|Clerk#000000656|0|al requests solve slyly above the never express requests. furious\n14917|1148|O|280961.18|1998-01-13|1-URGENT|Clerk#000000974|0|as would use fluffily after the \n14918|884|O|68255.07|1997-06-01|5-LOW|Clerk#000000674|0|ets. bold, pending deposits sleep. foxes wake\n14919|838|O|36633.03|1996-09-10|2-HIGH|Clerk#000000455|0|as. carefully final ideas cajole finally blithely express foxes. slow\n14944|535|O|119586.69|1997-10-14|2-HIGH|Clerk#000000962|0|lly. even instructions against\n14945|68|O|210519.05|1996-03-30|1-URGENT|Clerk#000000467|0|nts? fluffily bold grouches after\n14946|580|O|100402.47|1996-11-12|1-URGENT|Clerk#000000116|0|ffily bold dependencies wake. furiously regular instructions aro\n14947|580|O|100402.47|1996-11-12|1-URGENT|Clerk#000000116|0|ffily bold dependencies wake. furiously regular instructions aro "
  },
  {
    "path": "src/test/regress/data/other_types.csv",
    "content": "f,\\xdeadbeef,$1.00,192.168.1.2,10101,a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11,\"{\"\"key\"\": \"\"value\"\"}\"\nt,\\xcdb0,$1.50,127.0.0.1,\"\",a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11,[]\n"
  },
  {
    "path": "src/test/regress/data/part.data",
    "content": "1|goldenrod lavender spring chocolate lace|Manufacturer#1|Brand#13|PROMO BURNISHED COPPER|7|JUMBO PKG|901.00|ly. slyly ironi\n2|blush thistle blue yellow saddle|Manufacturer#1|Brand#13|LARGE BRUSHED BRASS|1|LG CASE|902.00|lar accounts amo\n3|spring green yellow purple cornsilk|Manufacturer#4|Brand#42|STANDARD POLISHED BRASS|21|WRAP CASE|903.00|egular deposits hag\n4|cornflower chocolate smoke green pink|Manufacturer#3|Brand#34|SMALL PLATED BRASS|14|MED DRUM|904.00|p furiously r\n5|forest brown coral puff cream|Manufacturer#3|Brand#32|STANDARD POLISHED TIN|15|SM PKG|905.00| wake carefully \n6|bisque cornflower lawn forest magenta|Manufacturer#2|Brand#24|PROMO PLATED STEEL|4|MED BAG|906.00|sual a\n7|moccasin green thistle khaki floral|Manufacturer#1|Brand#11|SMALL PLATED COPPER|45|SM BAG|907.00|lyly. ex\n8|misty lace thistle snow royal|Manufacturer#4|Brand#44|PROMO BURNISHED TIN|41|LG DRUM|908.00|eposi\n9|thistle dim navajo dark gainsboro|Manufacturer#4|Brand#43|SMALL BURNISHED STEEL|12|WRAP CASE|909.00|ironic foxe\n10|linen pink saddle puff powder|Manufacturer#5|Brand#54|LARGE BURNISHED STEEL|44|LG CAN|910.01|ithely final deposit\n11|spring maroon seashell almond orchid|Manufacturer#2|Brand#25|STANDARD BURNISHED NICKEL|43|WRAP BOX|911.01|ng gr\n12|cornflower wheat orange maroon ghost|Manufacturer#3|Brand#33|MEDIUM ANODIZED STEEL|25|JUMBO CASE|912.01| quickly\n13|ghost olive orange rosy thistle|Manufacturer#5|Brand#55|MEDIUM BURNISHED NICKEL|1|JUMBO PACK|913.01|osits.\n14|khaki seashell rose cornsilk navajo|Manufacturer#1|Brand#13|SMALL POLISHED STEEL|28|JUMBO BOX|914.01|kages c\n15|blanched honeydew sky turquoise medium|Manufacturer#1|Brand#15|LARGE ANODIZED BRASS|45|LG CASE|915.01|usual ac\n16|deep sky turquoise drab peach|Manufacturer#3|Brand#32|PROMO PLATED TIN|2|MED PACK|916.01|unts a\n17|indian navy coral pink deep|Manufacturer#4|Brand#43|ECONOMY BRUSHED STEEL|16|LG BOX|917.01| regular accounts\n18|turquoise indian lemon lavender misty|Manufacturer#1|Brand#11|SMALL BURNISHED STEEL|42|JUMBO PACK|918.01|s cajole slyly a\n19|chocolate navy tan deep brown|Manufacturer#2|Brand#23|SMALL ANODIZED NICKEL|33|WRAP BOX|919.01| pending acc\n20|ivory navy honeydew sandy midnight|Manufacturer#1|Brand#12|LARGE POLISHED NICKEL|48|MED BAG|920.02|are across the asympt\n21|lemon floral azure frosted lime|Manufacturer#3|Brand#33|SMALL BURNISHED TIN|31|MED BAG|921.02|ss packages. pendin\n22|medium forest blue ghost black|Manufacturer#4|Brand#43|PROMO POLISHED BRASS|19|LG DRUM|922.02| even p\n23|coral lavender seashell rosy burlywood|Manufacturer#3|Brand#35|MEDIUM BURNISHED TIN|42|JUMBO JAR|923.02|nic, fina\n24|seashell coral metallic midnight floral|Manufacturer#5|Brand#52|MEDIUM PLATED STEEL|20|MED CASE|924.02| final the\n25|aquamarine steel firebrick light turquoise|Manufacturer#5|Brand#55|STANDARD BRUSHED COPPER|3|JUMBO BAG|925.02|requests wake\n26|beige frosted moccasin chocolate snow|Manufacturer#3|Brand#32|SMALL BRUSHED STEEL|32|SM CASE|926.02| instructions i\n27|saddle puff beige linen yellow|Manufacturer#1|Brand#14|LARGE ANODIZED TIN|20|MED PKG|927.02|s wake. ir\n28|navajo yellow drab white misty|Manufacturer#4|Brand#44|SMALL PLATED COPPER|19|JUMBO PKG|928.02|x-ray pending, iron\n29|lemon sky grey salmon orchid|Manufacturer#3|Brand#33|PROMO PLATED COPPER|7|LG DRUM|929.02| carefully fluffi\n30|cream misty steel spring medium|Manufacturer#4|Brand#42|PROMO ANODIZED TIN|17|LG BOX|930.03|carefully bus\n31|slate seashell steel medium moccasin|Manufacturer#5|Brand#53|STANDARD BRUSHED TIN|10|LG BAG|931.03|uriously s\n32|sandy wheat coral spring burnished|Manufacturer#4|Brand#42|ECONOMY PLATED BRASS|31|LG CASE|932.03|urts. carefully fin\n33|spring bisque salmon slate pink|Manufacturer#2|Brand#22|ECONOMY PLATED NICKEL|16|LG PKG|933.03|ly eve\n34|khaki steel rose ghost salmon|Manufacturer#1|Brand#13|LARGE BRUSHED STEEL|8|JUMBO BOX|934.03|riously ironic\n35|green blush tomato burlywood seashell|Manufacturer#4|Brand#43|MEDIUM ANODIZED BRASS|14|JUMBO PACK|935.03|e carefully furi\n36|chiffon tan forest moccasin dark|Manufacturer#2|Brand#25|SMALL BURNISHED COPPER|3|JUMBO CAN|936.03|olites o\n37|royal coral orange burnished navajo|Manufacturer#4|Brand#45|LARGE POLISHED TIN|48|JUMBO BOX|937.03|silent \n38|seashell papaya white mint brown|Manufacturer#4|Brand#43|ECONOMY ANODIZED BRASS|11|SM JAR|938.03|structions inte\n39|rose medium floral salmon powder|Manufacturer#5|Brand#53|SMALL POLISHED TIN|43|JUMBO JAR|939.03|se slowly above the fl\n40|lemon midnight metallic sienna steel|Manufacturer#2|Brand#25|ECONOMY BURNISHED COPPER|27|SM CASE|940.04|! blithely specia\n41|burlywood goldenrod pink peru sienna|Manufacturer#2|Brand#23|ECONOMY ANODIZED TIN|7|WRAP JAR|941.04|uriously. furiously cl\n42|midnight turquoise lawn beige thistle|Manufacturer#5|Brand#52|MEDIUM BURNISHED TIN|45|LG BOX|942.04|the slow\n43|medium lace midnight royal chartreuse|Manufacturer#4|Brand#44|PROMO POLISHED STEEL|5|WRAP CASE|943.04|e slyly along the ir\n44|saddle cream wheat lemon burnished|Manufacturer#4|Brand#45|MEDIUM PLATED TIN|48|SM PACK|944.04|pinto beans. carefully\n45|lawn peru ghost khaki maroon|Manufacturer#4|Brand#43|SMALL BRUSHED NICKEL|9|WRAP BAG|945.04|nts bo\n46|honeydew turquoise aquamarine spring tan|Manufacturer#1|Brand#11|STANDARD POLISHED TIN|45|WRAP CASE|946.04|the blithely unusual \n47|honeydew red azure magenta brown|Manufacturer#4|Brand#45|LARGE BURNISHED BRASS|14|JUMBO PACK|947.04| even plate\n48|slate thistle cornsilk pale forest|Manufacturer#5|Brand#53|STANDARD BRUSHED STEEL|27|JUMBO CASE|948.04|ng to the depo\n49|light firebrick cyan puff blue|Manufacturer#2|Brand#24|SMALL BURNISHED TIN|31|MED DRUM|949.04|ar pack\n50|linen blanched tomato slate medium|Manufacturer#3|Brand#33|LARGE ANODIZED TIN|25|WRAP PKG|950.05|kages m\n51|lime frosted indian dodger linen|Manufacturer#4|Brand#45|ECONOMY BURNISHED NICKEL|34|JUMBO PACK|951.05|n foxes\n52|lemon midnight lace sky deep|Manufacturer#3|Brand#35|STANDARD BURNISHED TIN|25|WRAP CASE|952.05| final deposits. fu\n53|bisque rose cornsilk seashell purple|Manufacturer#2|Brand#23|ECONOMY BURNISHED NICKEL|32|MED BAG|953.05|mptot\n54|blanched mint yellow papaya cyan|Manufacturer#2|Brand#21|LARGE BURNISHED COPPER|19|WRAP CASE|954.05|e blithely\n55|sky cream deep tomato rosy|Manufacturer#2|Brand#23|ECONOMY BRUSHED COPPER|9|MED BAG|955.05|ly final pac\n56|antique beige brown deep dodger|Manufacturer#1|Brand#12|MEDIUM PLATED STEEL|20|WRAP DRUM|956.05|ts. blithel\n57|purple blue light sienna deep|Manufacturer#3|Brand#32|MEDIUM BURNISHED BRASS|49|MED PKG|957.05|lly abov\n58|linen hot cornsilk drab bisque|Manufacturer#5|Brand#53|STANDARD POLISHED TIN|44|LG PACK|958.05| fluffily blithely reg\n59|misty brown medium mint salmon|Manufacturer#5|Brand#53|MEDIUM POLISHED TIN|2|LG BAG|959.05|regular exc\n60|snow spring sandy olive tomato|Manufacturer#1|Brand#11|LARGE POLISHED COPPER|27|JUMBO CASE|960.06| integ\n61|light tan linen tomato peach|Manufacturer#5|Brand#54|SMALL BURNISHED NICKEL|18|WRAP DRUM|961.06|es. blithely en\n62|tan cornsilk spring grey chocolate|Manufacturer#3|Brand#35|STANDARD BRUSHED BRASS|39|JUMBO BOX|962.06|ckly across the carefu\n63|burnished puff coral light papaya|Manufacturer#3|Brand#32|STANDARD BURNISHED NICKEL|10|JUMBO CAN|963.06| quickly \n64|aquamarine coral lemon ivory gainsboro|Manufacturer#2|Brand#21|MEDIUM ANODIZED BRASS|1|JUMBO CAN|964.06|efully regular pi\n65|slate drab medium puff gainsboro|Manufacturer#5|Brand#53|MEDIUM BRUSHED COPPER|3|MED CAN|965.06|posits after the quic\n66|cornflower pale almond lemon linen|Manufacturer#3|Brand#35|PROMO ANODIZED NICKEL|46|SM CASE|966.06|haggle blithely iro\n67|slate salmon rose spring seashell|Manufacturer#2|Brand#21|SMALL BRUSHED TIN|31|WRAP DRUM|967.06| regular, p\n68|bisque ivory mint purple almond|Manufacturer#1|Brand#11|PROMO ANODIZED STEEL|10|WRAP BOX|968.06|eposits shall h\n69|lace burnished rosy antique metallic|Manufacturer#5|Brand#52|MEDIUM POLISHED BRASS|2|SM BOX|969.06|ely final depo\n70|violet seashell firebrick dark navajo|Manufacturer#1|Brand#11|STANDARD BRUSHED STEEL|42|LG PACK|970.07|inal gifts. sl\n71|violet firebrick cream peru white|Manufacturer#3|Brand#33|STANDARD PLATED BRASS|26|WRAP DRUM|971.07| packages alongside\n72|hot spring yellow azure dodger|Manufacturer#2|Brand#23|STANDARD ANODIZED TIN|25|JUMBO PACK|972.07|efully final the\n73|cream moccasin royal dim chiffon|Manufacturer#2|Brand#21|SMALL BRUSHED COPPER|35|WRAP DRUM|973.07|ts haggl\n74|frosted grey aquamarine thistle papaya|Manufacturer#5|Brand#55|ECONOMY ANODIZED BRASS|25|JUMBO CASE|974.07|ent foxes\n75|aquamarine maroon wheat salmon metallic|Manufacturer#3|Brand#35|SMALL BURNISHED NICKEL|39|SM JAR|975.07|s sleep furiou\n76|rosy light lime puff sandy|Manufacturer#3|Brand#34|MEDIUM BRUSHED COPPER|9|SM PKG|976.07|n accounts sleep qu\n77|mint bisque chiffon snow firebrick|Manufacturer#5|Brand#52|STANDARD BRUSHED COPPER|13|MED PKG|977.07|uests.\n78|blush forest slate seashell puff|Manufacturer#1|Brand#14|ECONOMY POLISHED STEEL|24|LG JAR|978.07|icing deposits wake\n79|gainsboro pink grey tan almond|Manufacturer#4|Brand#45|PROMO ANODIZED BRASS|22|JUMBO BAG|979.07| foxes are slyly regu\n80|tomato chartreuse coral turquoise linen|Manufacturer#4|Brand#44|PROMO PLATED BRASS|28|MED CAN|980.08|unusual dependencies i\n81|misty sandy cornsilk dodger blush|Manufacturer#5|Brand#53|ECONOMY BRUSHED TIN|21|MED BAG|981.08|ove the furiou\n82|khaki tomato purple almond tan|Manufacturer#1|Brand#15|ECONOMY POLISHED TIN|12|WRAP BOX|982.08|ial requests haggle \n83|blush green dim lawn peru|Manufacturer#1|Brand#12|PROMO BURNISHED NICKEL|47|SM CAN|983.08|ly regul\n84|salmon floral cream rose dark|Manufacturer#4|Brand#45|SMALL ANODIZED NICKEL|26|JUMBO PACK|984.08|ideas nag\n85|dim deep aquamarine smoke pale|Manufacturer#5|Brand#55|PROMO ANODIZED NICKEL|16|LG BAG|985.08| silent\n86|green blanched firebrick dim cream|Manufacturer#4|Brand#44|STANDARD PLATED TIN|37|LG CASE|986.08| daring sheaves \n87|purple lace seashell antique orange|Manufacturer#4|Brand#41|LARGE PLATED STEEL|41|WRAP PACK|987.08|yly final\n88|lime orange bisque chartreuse lemon|Manufacturer#4|Brand#44|PROMO PLATED COPPER|16|SM CASE|988.08|e regular packages. \n89|ghost lace lemon sienna saddle|Manufacturer#5|Brand#53|STANDARD BURNISHED STEEL|7|MED JAR|989.08|y final pinto \n90|hot rosy violet plum pale|Manufacturer#5|Brand#51|ECONOMY POLISHED STEEL|49|JUMBO CAN|990.09|caref\n91|misty bisque lavender spring turquoise|Manufacturer#2|Brand#21|STANDARD BRUSHED TIN|32|JUMBO PKG|991.09|counts dete\n92|blush magenta ghost tomato rose|Manufacturer#2|Brand#22|STANDARD ANODIZED TIN|35|JUMBO PKG|992.09|he ironic accounts. sp\n93|pale yellow cornsilk dodger moccasin|Manufacturer#2|Brand#24|LARGE ANODIZED TIN|2|WRAP DRUM|993.09| platel\n94|blanched pink frosted mint snow|Manufacturer#3|Brand#35|STANDARD POLISHED BRASS|32|SM BOX|994.09|s accounts cajo\n95|dodger beige wheat orchid navy|Manufacturer#3|Brand#33|LARGE BRUSHED TIN|36|WRAP DRUM|995.09| final pinto beans \n96|chocolate light firebrick rose indian|Manufacturer#5|Brand#53|STANDARD BRUSHED STEEL|32|SM CASE|996.09|ng to the bli\n97|coral dodger beige black chartreuse|Manufacturer#3|Brand#33|MEDIUM POLISHED BRASS|49|WRAP CAN|997.09|ss excuses sleep am\n98|frosted peru chiffon yellow aquamarine|Manufacturer#5|Brand#54|STANDARD ANODIZED BRASS|22|MED JAR|998.09|e the q\n99|mint grey purple sienna metallic|Manufacturer#2|Brand#21|SMALL BURNISHED STEEL|11|JUMBO PKG|999.09|press\n100|cyan orchid indian cornflower saddle|Manufacturer#3|Brand#33|ECONOMY ANODIZED TIN|4|LG BAG|1000.10|of the steal\n101|powder deep lavender violet gainsboro|Manufacturer#3|Brand#32|LARGE ANODIZED STEEL|26|JUMBO JAR|1001.10|ly even,\n102|papaya maroon blush powder sky|Manufacturer#3|Brand#31|MEDIUM BURNISHED BRASS|17|SM DRUM|1002.10|ular packa\n103|navy sky spring orchid forest|Manufacturer#2|Brand#25|MEDIUM PLATED BRASS|45|WRAP DRUM|1003.10|e blithely blith\n104|plum cyan cornflower midnight royal|Manufacturer#1|Brand#13|MEDIUM ANODIZED STEEL|36|JUMBO BAG|1004.10|ites sleep quickly\n105|dodger slate pale mint navajo|Manufacturer#1|Brand#15|SMALL POLISHED COPPER|27|LG DRUM|1005.10|odolites was \n106|cornsilk bisque seashell lemon frosted|Manufacturer#3|Brand#31|MEDIUM PLATED BRASS|28|WRAP DRUM|1006.10|unts maintain \n107|violet honeydew bisque sienna orchid|Manufacturer#5|Brand#53|SMALL BURNISHED TIN|12|MED BOX|1007.10|slyly special depos\n108|bisque peach magenta tomato yellow|Manufacturer#1|Brand#12|PROMO PLATED NICKEL|41|MED PKG|1008.10|after the carefully \n109|lemon black indian cornflower pale|Manufacturer#3|Brand#33|ECONOMY POLISHED TIN|11|LG PACK|1009.10|instruction\n110|firebrick navy rose beige black|Manufacturer#3|Brand#33|STANDARD BURNISHED COPPER|46|LG DRUM|1010.11|t quickly a\n111|orange cornflower mint snow peach|Manufacturer#5|Brand#54|LARGE BRUSHED COPPER|28|JUMBO JAR|1011.11|kly bold epitaphs \n112|hot aquamarine tomato lace indian|Manufacturer#4|Brand#43|PROMO BRUSHED STEEL|42|JUMBO CAN|1012.11|the express, \n113|almond seashell azure blanched light|Manufacturer#3|Brand#31|PROMO POLISHED TIN|23|LG CAN|1013.11|finally even \n114|pink black blanched lace chartreuse|Manufacturer#5|Brand#51|MEDIUM POLISHED NICKEL|41|MED PACK|1014.11|ully final foxes. pint\n115|spring chiffon cream orchid dodger|Manufacturer#4|Brand#45|STANDARD POLISHED STEEL|24|MED CAN|1015.11|counts nag! caref\n116|goldenrod black slate forest red|Manufacturer#5|Brand#53|PROMO POLISHED NICKEL|33|SM PACK|1016.11|usly final courts \n117|tomato honeydew pale red yellow|Manufacturer#1|Brand#14|SMALL BRUSHED TIN|25|LG BAG|1017.11|ages acc\n118|ghost plum brown coral cornsilk|Manufacturer#2|Brand#25|PROMO ANODIZED TIN|31|MED PACK|1018.11|ly ironic pinto\n119|olive metallic slate peach green|Manufacturer#4|Brand#43|LARGE POLISHED STEEL|30|WRAP CASE|1019.11|out the quickly r\n120|pink powder mint moccasin navajo|Manufacturer#1|Brand#14|SMALL ANODIZED NICKEL|45|WRAP JAR|1020.12|lly a\n121|bisque royal goldenrod medium thistle|Manufacturer#1|Brand#14|ECONOMY BRUSHED COPPER|13|SM PKG|1021.12|deposi\n122|gainsboro royal forest dark lace|Manufacturer#2|Brand#21|MEDIUM ANODIZED TIN|8|LG DRUM|1022.12|sts c\n123|deep dim peach light beige|Manufacturer#1|Brand#12|SMALL BURNISHED TIN|31|JUMBO PKG|1023.12|ray regula\n124|wheat blush forest metallic navajo|Manufacturer#3|Brand#32|PROMO ANODIZED STEEL|1|LG BOX|1024.12|g the expr\n125|mint ivory saddle peach midnight|Manufacturer#1|Brand#12|STANDARD BRUSHED BRASS|17|WRAP BAG|1025.12|kages against\n126|burnished black blue metallic orchid|Manufacturer#4|Brand#45|MEDIUM BRUSHED NICKEL|4|LG BAG|1026.12|es sleep al\n127|royal coral orchid spring sky|Manufacturer#5|Brand#52|SMALL BURNISHED NICKEL|14|LG JAR|1027.12|lithely expr\n128|dark burlywood burnished snow sky|Manufacturer#2|Brand#22|PROMO PLATED TIN|5|SM BAG|1028.12|e of the furiously ex\n129|grey spring chiffon thistle lime|Manufacturer#1|Brand#15|LARGE POLISHED TIN|20|SM JAR|1029.12| careful\n130|gainsboro powder cyan pale rosy|Manufacturer#2|Brand#23|SMALL PLATED NICKEL|26|LG BOX|1030.13|ake slyly\n131|tomato moccasin cyan brown goldenrod|Manufacturer#5|Brand#52|STANDARD ANODIZED BRASS|43|MED DRUM|1031.13|nts wake dar\n132|seashell papaya tomato lime hot|Manufacturer#4|Brand#45|STANDARD BURNISHED BRASS|2|WRAP DRUM|1032.13|ckly expre\n133|firebrick black dodger pink salmon|Manufacturer#1|Brand#13|SMALL BRUSHED NICKEL|19|LG PKG|1033.13| final pinto beans\n134|steel beige mint maroon indian|Manufacturer#4|Brand#42|SMALL POLISHED STEEL|35|SM PKG|1034.13|es. bold pa\n135|thistle chocolate ghost gainsboro peru|Manufacturer#2|Brand#21|MEDIUM BURNISHED STEEL|24|JUMBO CASE|1035.13|l frets \n136|cornsilk maroon blanched thistle rosy|Manufacturer#2|Brand#22|SMALL PLATED STEEL|2|WRAP BAG|1036.13|kages print carefully\n137|cornsilk drab ghost sandy royal|Manufacturer#3|Brand#31|ECONOMY PLATED STEEL|25|MED PACK|1037.13|the t\n138|dark aquamarine tomato medium puff|Manufacturer#1|Brand#13|ECONOMY BURNISHED COPPER|42|JUMBO DRUM|1038.13|ts solve acro\n139|floral steel burlywood navy cream|Manufacturer#3|Brand#32|MEDIUM BRUSHED STEEL|7|SM BOX|1039.13|ter t\n140|aquamarine lavender maroon slate hot|Manufacturer#5|Brand#53|STANDARD PLATED STEEL|45|SM BOX|1040.14|oss the carefu\n141|honeydew magenta tomato spring medium|Manufacturer#3|Brand#35|STANDARD ANODIZED STEEL|23|SM PKG|1041.14|ans nag furiously pen\n142|chartreuse linen grey slate saddle|Manufacturer#5|Brand#55|STANDARD ANODIZED BRASS|36|MED JAR|1042.14|he accounts. pac\n143|bisque dodger blanched steel maroon|Manufacturer#3|Brand#34|ECONOMY PLATED TIN|44|MED BAG|1043.14|nts across the\n144|hot midnight orchid dim steel|Manufacturer#1|Brand#14|SMALL ANODIZED TIN|26|SM BOX|1044.14|owly \n145|navajo lavender chocolate deep hot|Manufacturer#5|Brand#53|PROMO BRUSHED COPPER|24|SM BAG|1045.14|es wake furiously blit\n146|azure smoke mint cream burlywood|Manufacturer#3|Brand#34|STANDARD BRUSHED COPPER|11|WRAP PACK|1046.14|unts cajole\n147|honeydew orange dodger linen lace|Manufacturer#1|Brand#11|MEDIUM PLATED COPPER|29|JUMBO PKG|1047.14|wake never bold \n148|yellow white ghost lavender salmon|Manufacturer#3|Brand#31|STANDARD PLATED STEEL|20|SM BOX|1048.14|platelets wake fu\n149|tan thistle frosted indian lawn|Manufacturer#2|Brand#24|MEDIUM BURNISHED NICKEL|6|MED PKG|1049.14|leep requests. dog\n150|pale rose navajo firebrick aquamarine|Manufacturer#3|Brand#35|LARGE BRUSHED TIN|21|SM BAG|1050.15|ironic foxes\n151|chartreuse linen violet ghost thistle|Manufacturer#3|Brand#34|LARGE PLATED BRASS|45|MED CAN|1051.15|ccounts nag i\n152|white sky antique tomato chartreuse|Manufacturer#5|Brand#53|MEDIUM POLISHED STEEL|48|MED CASE|1052.15|thely regular t\n153|linen frosted slate coral peru|Manufacturer#1|Brand#11|STANDARD PLATED TIN|20|MED BAG|1053.15|thlessly. silen\n154|peru moccasin peach pale spring|Manufacturer#1|Brand#11|ECONOMY ANODIZED TIN|1|JUMBO BAG|1054.15|posits \n155|puff yellow cyan tomato purple|Manufacturer#2|Brand#21|SMALL BRUSHED NICKEL|28|WRAP CASE|1055.15|lly ironic, r\n156|almond ghost powder blush forest|Manufacturer#4|Brand#43|SMALL POLISHED NICKEL|2|LG PKG|1056.15| pinto beans. eve\n157|navajo linen coral brown forest|Manufacturer#1|Brand#11|ECONOMY ANODIZED STEEL|26|JUMBO PACK|1057.15|ial courts. ru\n158|magenta light misty navy honeydew|Manufacturer#4|Brand#45|MEDIUM BURNISHED COPPER|47|LG JAR|1058.15| ideas detect slyl\n159|white orange antique beige aquamarine|Manufacturer#4|Brand#43|SMALL ANODIZED BRASS|46|SM BAG|1059.15| ironic requests-- pe\n160|frosted cornflower khaki salmon metallic|Manufacturer#5|Brand#55|STANDARD POLISHED COPPER|47|JUMBO CAN|1060.16|nts are carefully\n161|metallic khaki navy forest cyan|Manufacturer#2|Brand#22|STANDARD PLATED TIN|17|SM PACK|1061.16|r the bl\n162|burlywood cornflower aquamarine misty snow|Manufacturer#3|Brand#33|MEDIUM ANODIZED COPPER|35|JUMBO PACK|1062.16|e slyly around th\n163|blush metallic maroon lawn forest|Manufacturer#2|Brand#21|ECONOMY PLATED TIN|34|WRAP DRUM|1063.16|nly s\n164|orange cyan magenta navajo indian|Manufacturer#2|Brand#23|LARGE PLATED BRASS|35|JUMBO BAG|1064.16|mong th\n165|white dim cornflower sky seashell|Manufacturer#1|Brand#15|STANDARD PLATED STEEL|24|SM CAN|1065.16| carefully fin\n166|linen bisque tomato gainsboro goldenrod|Manufacturer#5|Brand#52|LARGE POLISHED COPPER|4|MED BAG|1066.16|ss the\n167|almond floral grey dim sky|Manufacturer#3|Brand#32|LARGE ANODIZED STEEL|46|WRAP BOX|1067.16|ic ac\n168|lace gainsboro burlywood smoke tomato|Manufacturer#1|Brand#13|SMALL BRUSHED COPPER|20|JUMBO DRUM|1068.16|ss package\n169|bisque misty sky cornflower peach|Manufacturer#5|Brand#55|STANDARD POLISHED BRASS|10|JUMBO CASE|1069.16|lets alongside of\n170|peru grey blanched goldenrod yellow|Manufacturer#3|Brand#33|LARGE POLISHED COPPER|28|LG DRUM|1070.17|yly s\n171|beige violet black magenta chartreuse|Manufacturer#1|Brand#11|STANDARD BURNISHED COPPER|40|LG JAR|1071.17| the r\n172|medium goldenrod linen sky coral|Manufacturer#5|Brand#53|PROMO PLATED NICKEL|28|MED CASE|1072.17|quick as\n173|chartreuse seashell powder navy grey|Manufacturer#1|Brand#12|ECONOMY BURNISHED TIN|17|LG CASE|1073.17|sly bold excuses haggl\n174|hot cornflower slate saddle pale|Manufacturer#1|Brand#15|ECONOMY BRUSHED COPPER|25|LG CASE|1074.17| accounts nag ab\n175|magenta blue chartreuse tan green|Manufacturer#1|Brand#11|PROMO ANODIZED TIN|45|JUMBO JAR|1075.17|ole against the\n176|pink drab ivory papaya grey|Manufacturer#2|Brand#24|SMALL ANODIZED STEEL|40|MED CAN|1076.17|blithely. ironic\n177|indian turquoise purple green spring|Manufacturer#2|Brand#21|MEDIUM BRUSHED STEEL|42|LG BAG|1077.17|ermanently eve\n178|lace blanched magenta yellow almond|Manufacturer#1|Brand#13|STANDARD POLISHED TIN|10|LG JAR|1078.17|regular instructions.\n179|deep puff brown blue burlywood|Manufacturer#4|Brand#43|ECONOMY BRUSHED STEEL|20|LG JAR|1079.17|ely regul\n180|seashell maroon lace burnished lavender|Manufacturer#3|Brand#33|STANDARD BURNISHED NICKEL|7|WRAP BAG|1080.18|oss the \n181|antique plum smoke pink dodger|Manufacturer#2|Brand#24|MEDIUM PLATED STEEL|19|WRAP CAN|1081.18|al deposits \n182|beige cyan burlywood chiffon light|Manufacturer#3|Brand#31|MEDIUM ANODIZED COPPER|11|JUMBO CAN|1082.18|bits are \n183|ivory white burnished papaya cornflower|Manufacturer#5|Brand#52|PROMO POLISHED STEEL|35|LG PKG|1083.18|ly regular excus\n184|ghost honeydew cyan lawn powder|Manufacturer#5|Brand#53|SMALL POLISHED TIN|42|LG BOX|1084.18|ding courts. idly iro\n185|firebrick black ivory spring medium|Manufacturer#4|Brand#44|ECONOMY POLISHED TIN|4|WRAP BAG|1085.18|even foxe\n186|grey purple chocolate turquoise plum|Manufacturer#2|Brand#23|ECONOMY BRUSHED TIN|15|JUMBO PKG|1086.18|ly reg\n187|white red lace deep pale|Manufacturer#4|Brand#45|PROMO ANODIZED BRASS|45|MED CAN|1087.18|leep slyly s\n188|moccasin steel rosy drab white|Manufacturer#5|Brand#54|ECONOMY ANODIZED BRASS|9|MED CAN|1088.18| above the silent p\n189|dodger moccasin lemon purple thistle|Manufacturer#2|Brand#22|MEDIUM BRUSHED BRASS|13|WRAP DRUM|1089.18|en requests. sauternes\n190|chartreuse goldenrod midnight cornflower blush|Manufacturer#5|Brand#53|LARGE BURNISHED NICKEL|23|WRAP BAG|1090.19| furiously even d\n191|mint midnight puff forest peach|Manufacturer#3|Brand#31|MEDIUM POLISHED BRASS|36|WRAP BOX|1091.19| asymptote\n192|thistle puff pink cream orange|Manufacturer#3|Brand#34|STANDARD BRUSHED COPPER|17|MED BAG|1092.19|uickly regular, expr\n193|turquoise lime royal metallic azure|Manufacturer#4|Brand#45|ECONOMY BURNISHED BRASS|31|SM PKG|1093.19|final ideas wake furi\n194|brown black cream navy plum|Manufacturer#5|Brand#51|ECONOMY POLISHED STEEL|7|SM CAN|1094.19|y special accoun\n195|bisque sienna hot goldenrod khaki|Manufacturer#4|Brand#41|STANDARD BRUSHED NICKEL|40|MED CASE|1095.19|oxes sleep care\n196|pale peru linen hot maroon|Manufacturer#3|Brand#33|SMALL BURNISHED NICKEL|3|JUMBO JAR|1096.19|uickly special \n197|lawn lemon khaki rosy blue|Manufacturer#5|Brand#52|SMALL ANODIZED COPPER|18|SM JAR|1097.19|lithely after the eve\n198|orange cornflower indian aquamarine white|Manufacturer#4|Brand#41|PROMO BRUSHED NICKEL|43|SM PACK|1098.19|ackages? carefully re\n199|ivory slate lavender tan royal|Manufacturer#3|Brand#31|ECONOMY PLATED STEEL|23|JUMBO DRUM|1099.19|ickly regul\n200|peach cornsilk navy rosy red|Manufacturer#5|Brand#54|MEDIUM POLISHED BRASS|22|LG PKG|1100.20|furiously even depo\n201|dodger white chiffon moccasin green|Manufacturer#4|Brand#42|SMALL POLISHED STEEL|18|JUMBO BAG|1101.20| courts sl\n202|brown violet turquoise frosted navajo|Manufacturer#4|Brand#45|MEDIUM BRUSHED COPPER|49|MED DRUM|1102.20| use slyly c\n203|beige cornflower gainsboro chiffon wheat|Manufacturer#5|Brand#55|STANDARD ANODIZED STEEL|42|MED BAG|1103.20|usual instructio\n204|floral powder deep linen hot|Manufacturer#5|Brand#54|LARGE BURNISHED COPPER|22|SM JAR|1104.20|egular pl\n205|navajo bisque gainsboro forest coral|Manufacturer#5|Brand#54|ECONOMY PLATED NICKEL|2|JUMBO DRUM|1105.20| blithely regular\n206|chiffon coral wheat peru lavender|Manufacturer#2|Brand#25|LARGE ANODIZED NICKEL|23|JUMBO CAN|1106.20|ng theodolites \n207|blush chocolate light coral midnight|Manufacturer#3|Brand#35|STANDARD PLATED COPPER|48|MED CAN|1107.20|lar, \n208|medium pink metallic honeydew ghost|Manufacturer#3|Brand#35|LARGE PLATED COPPER|5|MED PACK|1108.20|l deposits wak\n209|sky plum pink lavender firebrick|Manufacturer#1|Brand#11|ECONOMY PLATED STEEL|6|MED CAN|1109.20| haggle a\n210|white sky mint chiffon blanched|Manufacturer#3|Brand#32|ECONOMY BURNISHED TIN|5|LG JAR|1110.21|ly until the flu\n211|turquoise forest orchid cream blue|Manufacturer#3|Brand#32|MEDIUM ANODIZED NICKEL|43|LG PKG|1111.21|s cajole \n212|light sandy white bisque burlywood|Manufacturer#5|Brand#53|PROMO BRUSHED BRASS|42|JUMBO BOX|1112.21|uffily fin\n213|pale midnight light ghost saddle|Manufacturer#3|Brand#35|SMALL POLISHED COPPER|14|WRAP CAN|1113.21|der slyly accor\n214|cornsilk tomato firebrick tan puff|Manufacturer#4|Brand#41|PROMO PLATED BRASS|30|LG BAG|1114.21|quests. regular, fi\n215|lace pink black orange cornflower|Manufacturer#5|Brand#52|ECONOMY BURNISHED NICKEL|19|SM PACK|1115.21| the carefully i\n216|tan azure slate white mint|Manufacturer#4|Brand#45|MEDIUM PLATED NICKEL|35|MED JAR|1116.21|gside of the unus\n217|lemon thistle sandy royal peru|Manufacturer#5|Brand#52|PROMO PLATED COPPER|44|MED DRUM|1117.21|heodolites integrat\n218|sandy khaki ghost cornflower metallic|Manufacturer#4|Brand#41|PROMO BURNISHED TIN|24|WRAP PACK|1118.21| furiously\n219|cyan aquamarine red plum frosted|Manufacturer#4|Brand#45|PROMO ANODIZED BRASS|31|MED JAR|1119.21|riously ironic reque\n220|lemon firebrick lime white wheat|Manufacturer#1|Brand#14|PROMO BRUSHED BRASS|25|MED CASE|1120.22|heodolites sleep. c\n221|chartreuse azure lemon saddle dark|Manufacturer#5|Brand#51|PROMO BRUSHED COPPER|7|LG DRUM|1121.22|ld asymptotes sleep ca\n222|aquamarine puff antique drab beige|Manufacturer#3|Brand#33|LARGE ANODIZED BRASS|35|LG CASE|1122.22|ross the ironic, un\n223|rose salmon yellow turquoise grey|Manufacturer#3|Brand#33|LARGE ANODIZED STEEL|29|MED BAG|1123.22|ons. even depos\n224|drab lavender moccasin almond purple|Manufacturer#1|Brand#13|STANDARD POLISHED NICKEL|46|WRAP BOX|1124.22|y special depos\n225|cornsilk powder bisque chartreuse spring|Manufacturer#4|Brand#45|ECONOMY BRUSHED TIN|23|MED CASE|1125.22|ts ca\n226|blush navy indian peru lemon|Manufacturer#3|Brand#33|PROMO ANODIZED STEEL|7|SM PKG|1126.22|ular packages. some\n227|dim yellow aquamarine lavender peach|Manufacturer#4|Brand#44|SMALL BRUSHED TIN|21|LG PACK|1127.22| silent r\n228|saddle lawn blue burlywood white|Manufacturer#4|Brand#44|ECONOMY PLATED STEEL|12|SM DRUM|1128.22|f the fluffily\n229|orchid misty cornsilk chartreuse medium|Manufacturer#1|Brand#15|SMALL POLISHED STEEL|19|MED CAN|1129.22|ng to the quick\n230|thistle navy lawn sky slate|Manufacturer#5|Brand#51|STANDARD PLATED STEEL|20|SM PKG|1130.23|fter the ironic pin\n231|bisque blush beige honeydew slate|Manufacturer#5|Brand#51|MEDIUM BURNISHED COPPER|17|MED PACK|1131.23|ffily. fur\n232|ivory peru lavender orange dark|Manufacturer#5|Brand#53|LARGE BURNISHED NICKEL|50|SM PKG|1132.23|r, unusual requests \n233|seashell tomato red lemon saddle|Manufacturer#3|Brand#34|MEDIUM ANODIZED BRASS|25|SM PACK|1133.23|ully ironic\n234|chocolate slate maroon azure goldenrod|Manufacturer#1|Brand#13|MEDIUM ANODIZED NICKEL|26|WRAP BOX|1134.23| furiously special \n235|sky mint aquamarine dark cornsilk|Manufacturer#3|Brand#32|ECONOMY BRUSHED COPPER|4|MED JAR|1135.23|s. carefully regular d\n236|salmon antique burlywood linen peach|Manufacturer#5|Brand#55|ECONOMY ANODIZED STEEL|31|LG JAR|1136.23|ss packages hag\n237|yellow orchid dark light smoke|Manufacturer#2|Brand#25|LARGE BURNISHED TIN|49|SM DRUM|1137.23|g the furiously\n238|purple dark lawn navajo indian|Manufacturer#3|Brand#34|SMALL POLISHED TIN|35|LG CAN|1138.23|d, expres\n239|medium olive pink dim firebrick|Manufacturer#2|Brand#24|LARGE PLATED NICKEL|36|WRAP DRUM|1139.23|lites are perman\n240|rose beige magenta coral sandy|Manufacturer#3|Brand#32|MEDIUM BURNISHED NICKEL|1|LG CAN|1140.24|ructions.\n241|purple drab puff peach tomato|Manufacturer#5|Brand#51|STANDARD BRUSHED COPPER|3|WRAP CASE|1141.24| quickly regular fo\n242|spring green lemon medium olive|Manufacturer#3|Brand#35|SMALL POLISHED STEEL|42|LG BAG|1142.24|ously final theodo\n243|bisque chiffon orange misty tan|Manufacturer#3|Brand#32|MEDIUM ANODIZED STEEL|49|SM BOX|1143.24|ress sentim\n244|seashell ghost cyan burlywood thistle|Manufacturer#5|Brand#51|ECONOMY BURNISHED BRASS|48|LG BOX|1144.24|ns use above the ir\n245|beige orchid chartreuse powder slate|Manufacturer#2|Brand#23|PROMO BURNISHED BRASS|39|JUMBO PKG|1145.24| instructions. ca\n246|burlywood orchid dark drab dodger|Manufacturer#2|Brand#21|PROMO BRUSHED BRASS|18|SM CAN|1146.24|e caref\n247|medium gainsboro lawn coral peach|Manufacturer#5|Brand#53|LARGE BURNISHED BRASS|4|JUMBO PACK|1147.24|r accounts. carefully\n248|drab aquamarine red papaya pale|Manufacturer#1|Brand#15|SMALL PLATED BRASS|8|MED PACK|1148.24|furiously fluffily\n249|hot sandy lavender saddle rosy|Manufacturer#4|Brand#44|ECONOMY BURNISHED BRASS|15|LG JAR|1149.24| excuses kindle f\n250|antique goldenrod floral forest slate|Manufacturer#4|Brand#44|PROMO POLISHED NICKEL|35|WRAP CASE|1150.25|nstructions affix furi\n251|dodger gainsboro violet steel papaya|Manufacturer#1|Brand#15|STANDARD BRUSHED NICKEL|41|WRAP CAN|1151.25|inal ideas aff\n252|royal sienna magenta lace brown|Manufacturer#1|Brand#11|PROMO PLATED NICKEL|22|WRAP DRUM|1152.25|e slyly after the de\n253|blanched ghost turquoise burlywood spring|Manufacturer#4|Brand#45|PROMO ANODIZED TIN|16|LG PKG|1153.25|he even, re\n254|navy light red royal olive|Manufacturer#4|Brand#44|MEDIUM PLATED TIN|10|MED JAR|1154.25|ly at t\n255|aquamarine seashell grey navy white|Manufacturer#2|Brand#21|LARGE ANODIZED NICKEL|24|JUMBO CAN|1155.25|fter the c\n256|royal cream lawn purple powder|Manufacturer#3|Brand#32|LARGE BURNISHED TIN|2|SM DRUM|1156.25|bove the fur\n257|blue gainsboro maroon green burnished|Manufacturer#4|Brand#41|ECONOMY POLISHED COPPER|11|SM JAR|1157.25|riously final foxes\n258|royal frosted blue pale dim|Manufacturer#4|Brand#43|STANDARD ANODIZED COPPER|18|WRAP DRUM|1158.25|leep blith\n259|ghost puff sky linen burnished|Manufacturer#2|Brand#23|SMALL BURNISHED BRASS|41|WRAP PACK|1159.25|l platelets. evenly \n260|firebrick thistle lime frosted khaki|Manufacturer#5|Brand#55|STANDARD ANODIZED STEEL|10|LG BAG|1160.26| haggle f\n261|floral moccasin puff rosy ghost|Manufacturer#4|Brand#41|STANDARD POLISHED TIN|44|JUMBO PACK|1161.26|ges was i\n262|drab lavender lawn purple puff|Manufacturer#5|Brand#52|MEDIUM PLATED NICKEL|16|JUMBO BAG|1162.26|lar theodolites. f\n263|linen red plum purple steel|Manufacturer#1|Brand#14|ECONOMY BURNISHED COPPER|46|MED CASE|1163.26|ly ironic the\n264|plum floral sienna magenta ivory|Manufacturer#1|Brand#14|SMALL BURNISHED TIN|23|SM CAN|1164.26|ake. noto\n265|aquamarine magenta seashell orange royal|Manufacturer#4|Brand#43|SMALL POLISHED STEEL|19|WRAP CASE|1165.26| slyly ac\n266|medium brown tomato cornflower moccasin|Manufacturer#4|Brand#43|ECONOMY BRUSHED STEEL|25|LG DRUM|1166.26|odoli\n267|pink salmon moccasin orange wheat|Manufacturer#2|Brand#24|LARGE POLISHED BRASS|41|JUMBO PACK|1167.26|ckly speci\n268|cornsilk seashell lime aquamarine violet|Manufacturer#2|Brand#22|MEDIUM PLATED NICKEL|8|MED JAR|1168.26| carefully sil\n269|chartreuse navajo moccasin orchid spring|Manufacturer#1|Brand#13|LARGE PLATED BRASS|5|WRAP BOX|1169.26|ully about the ca\n270|mint deep white navajo floral|Manufacturer#2|Brand#25|PROMO BURNISHED COPPER|45|MED BAG|1170.27|ly even pinto beans. c\n271|lace floral peru dim magenta|Manufacturer#5|Brand#52|PROMO PLATED COPPER|32|MED PKG|1171.27| fluffy acco\n272|red purple tomato medium papaya|Manufacturer#5|Brand#52|LARGE POLISHED BRASS|39|LG PKG|1172.27| furiousl\n273|pink white sky burnished coral|Manufacturer#2|Brand#25|STANDARD BRUSHED BRASS|50|LG BOX|1173.27|ackages along the\n274|cyan lawn snow sky peru|Manufacturer#2|Brand#23|MEDIUM POLISHED STEEL|46|LG JAR|1174.27|hless accounts prin\n275|steel chartreuse red plum sienna|Manufacturer#4|Brand#45|LARGE BURNISHED BRASS|37|SM BOX|1175.27|equests\n276|orange yellow drab grey blanched|Manufacturer#2|Brand#25|SMALL BRUSHED COPPER|5|LG JAR|1176.27|iously.\n277|grey navy rosy red burnished|Manufacturer#4|Brand#43|ECONOMY ANODIZED NICKEL|49|JUMBO JAR|1177.27|arefully special dep\n278|blanched lime almond moccasin floral|Manufacturer#4|Brand#42|STANDARD BURNISHED NICKEL|49|SM JAR|1178.27| bold p\n279|linen olive lawn blanched gainsboro|Manufacturer#3|Brand#35|SMALL ANODIZED BRASS|27|MED CASE|1179.27|eans boos\n280|blue azure slate lace burlywood|Manufacturer#2|Brand#21|STANDARD BURNISHED STEEL|33|LG CAN|1180.28|egular, s\n281|red mint brown powder rose|Manufacturer#1|Brand#12|SMALL PLATED STEEL|7|MED CAN|1181.28|regula\n282|steel burnished cornsilk beige lace|Manufacturer#1|Brand#11|SMALL BRUSHED COPPER|44|WRAP CAN|1182.28|packages. final ex\n283|metallic burlywood lavender midnight cornsilk|Manufacturer#1|Brand#12|SMALL PLATED TIN|8|SM DRUM|1183.28| above the i\n284|burlywood chocolate almond ivory coral|Manufacturer#5|Brand#51|ECONOMY POLISHED COPPER|9|MED DRUM|1184.28|its haggle fu\n285|cream seashell orange frosted brown|Manufacturer#2|Brand#25|LARGE ANODIZED TIN|42|SM PACK|1185.28| the furiously fi\n286|chocolate cornsilk goldenrod violet puff|Manufacturer#5|Brand#53|MEDIUM POLISHED TIN|1|WRAP CASE|1186.28| foxes. regular, fi\n287|misty snow thistle burlywood wheat|Manufacturer#1|Brand#15|SMALL PLATED NICKEL|27|SM DRUM|1187.28|y even foxes. final,\n288|tan purple yellow ivory olive|Manufacturer#2|Brand#23|STANDARD BURNISHED BRASS|31|SM CASE|1188.28|dencies mold among the\n289|peach antique deep peru saddle|Manufacturer#4|Brand#43|PROMO POLISHED TIN|40|LG CASE|1189.28|odolites sleep care\n290|deep wheat forest powder firebrick|Manufacturer#1|Brand#12|LARGE BURNISHED BRASS|19|JUMBO JAR|1190.29|according to t\n291|goldenrod aquamarine olive white pink|Manufacturer#5|Brand#54|ECONOMY PLATED BRASS|26|SM JAR|1191.29|kages alo\n292|navajo tan coral slate thistle|Manufacturer#1|Brand#12|SMALL PLATED NICKEL|2|MED CAN|1192.29|zzle among t\n293|lawn cyan plum bisque cream|Manufacturer#5|Brand#52|STANDARD POLISHED STEEL|24|SM PACK|1193.29|onic requests\n294|midnight misty royal floral brown|Manufacturer#2|Brand#24|ECONOMY POLISHED TIN|35|SM JAR|1194.29|truct\n295|honeydew lace powder almond red|Manufacturer#2|Brand#25|MEDIUM BURNISHED NICKEL|49|JUMBO JAR|1195.29|ully ironic \n296|tan seashell chocolate lemon orange|Manufacturer#1|Brand#13|LARGE BURNISHED BRASS|28|SM PKG|1196.29|, regul\n297|antique powder almond navajo lace|Manufacturer#2|Brand#22|LARGE BURNISHED STEEL|23|LG PACK|1197.29|e of the slyl\n298|azure sky tomato midnight lace|Manufacturer#4|Brand#45|PROMO BRUSHED COPPER|13|JUMBO CAN|1198.29|al deposits wake\n299|deep coral dodger green peach|Manufacturer#4|Brand#41|STANDARD ANODIZED COPPER|26|WRAP PKG|1199.29|regular dol\n300|light goldenrod green lime papaya|Manufacturer#4|Brand#44|PROMO ANODIZED BRASS|1|WRAP PACK|1200.30|tructions int\n301|misty snow lace burnished linen|Manufacturer#4|Brand#42|ECONOMY BRUSHED STEEL|6|WRAP CAN|1201.30|orges\n302|black seashell sky grey snow|Manufacturer#4|Brand#41|PROMO POLISHED BRASS|34|SM BAG|1202.30| ideas. special asy\n303|almond chocolate firebrick black bisque|Manufacturer#4|Brand#45|ECONOMY PLATED BRASS|7|JUMBO CASE|1203.30|yly according\n304|forest mint sienna navy ghost|Manufacturer#4|Brand#43|SMALL POLISHED NICKEL|12|LG DRUM|1204.30|ly regu\n305|moccasin tan chartreuse cornsilk drab|Manufacturer#1|Brand#15|SMALL ANODIZED TIN|6|SM BAG|1205.30|ake above \n306|lemon peach spring rose ghost|Manufacturer#3|Brand#33|SMALL BURNISHED NICKEL|25|LG PKG|1206.30| regular req\n307|peru beige firebrick royal navy|Manufacturer#2|Brand#21|LARGE BURNISHED BRASS|30|WRAP DRUM|1207.30|eas. e\n308|olive ivory turquoise sienna wheat|Manufacturer#2|Brand#21|STANDARD BURNISHED BRASS|40|WRAP JAR|1208.30|ngside of the s\n309|red smoke blue pale lace|Manufacturer#4|Brand#42|LARGE ANODIZED NICKEL|49|JUMBO PKG|1209.30|lly even c\n310|sandy sky dark navy blanched|Manufacturer#1|Brand#14|ECONOMY BRUSHED NICKEL|32|MED JAR|1210.31|arefully. requ\n311|indian orchid navajo dark forest|Manufacturer#4|Brand#44|MEDIUM PLATED TIN|18|LG DRUM|1211.31|final,\n312|powder firebrick white peach goldenrod|Manufacturer#3|Brand#32|PROMO BURNISHED STEEL|29|MED CASE|1212.31|nts doze slyly. d\n313|moccasin rosy lemon linen royal|Manufacturer#3|Brand#34|PROMO BRUSHED TIN|13|WRAP PACK|1213.31|nly regular\n314|grey royal seashell violet misty|Manufacturer#3|Brand#35|STANDARD PLATED STEEL|14|LG DRUM|1214.31|ular deposits. \n315|navy cyan orchid seashell moccasin|Manufacturer#2|Brand#23|LARGE ANODIZED BRASS|20|LG JAR|1215.31|s was slyly pac\n316|almond rosy green hot midnight|Manufacturer#4|Brand#41|MEDIUM BRUSHED TIN|9|MED CASE|1216.31|deposits haggle\n317|linen peru thistle turquoise medium|Manufacturer#1|Brand#15|LARGE POLISHED STEEL|34|JUMBO BOX|1217.31|e furiously regular pa\n318|rosy rose indian puff pale|Manufacturer#1|Brand#12|PROMO BURNISHED NICKEL|20|JUMBO CASE|1218.31|ar re\n319|royal metallic lawn spring firebrick|Manufacturer#3|Brand#31|MEDIUM BURNISHED TIN|44|LG BOX|1219.31|kages boost along \n320|chiffon linen rose white rosy|Manufacturer#5|Brand#53|LARGE ANODIZED STEEL|26|WRAP DRUM|1220.32|structions a\n321|tomato drab cream tan navajo|Manufacturer#5|Brand#51|ECONOMY ANODIZED STEEL|9|SM JAR|1221.32|tructions boost car\n322|dark black rosy yellow ivory|Manufacturer#3|Brand#35|STANDARD PLATED STEEL|32|JUMBO CASE|1222.32|uses. blithely pendin\n323|moccasin goldenrod tan maroon bisque|Manufacturer#4|Brand#41|MEDIUM BRUSHED BRASS|15|MED CASE|1223.32|ular pi\n324|aquamarine ivory magenta linen black|Manufacturer#2|Brand#25|SMALL PLATED TIN|36|SM BAG|1224.32|ages. requests wake.\n325|plum cyan almond dark orchid|Manufacturer#1|Brand#15|LARGE PLATED COPPER|22|MED PACK|1225.32|ackages. slyly pendin\n326|sky papaya green azure chiffon|Manufacturer#3|Brand#34|LARGE ANODIZED STEEL|48|JUMBO CAN|1226.32|aggle slyly special f\n327|metallic lavender green firebrick ghost|Manufacturer#2|Brand#23|PROMO POLISHED BRASS|14|SM PACK|1227.32|regular\n328|turquoise bisque wheat cornflower forest|Manufacturer#3|Brand#34|PROMO POLISHED COPPER|24|JUMBO CASE|1228.32|ronic dependencies\n329|mint dodger chocolate papaya azure|Manufacturer#3|Brand#31|MEDIUM ANODIZED NICKEL|21|LG PKG|1229.32|. quickly pending pa\n330|royal lime misty frosted smoke|Manufacturer#3|Brand#35|LARGE PLATED COPPER|18|LG JAR|1230.33|efully pending depend\n331|metallic ivory cornsilk mint hot|Manufacturer#2|Brand#25|SMALL BURNISHED COPPER|15|WRAP JAR|1231.33| the slyly iron\n332|frosted blush firebrick linen salmon|Manufacturer#2|Brand#24|STANDARD BRUSHED BRASS|14|SM CAN|1232.33|instru\n333|chocolate tomato light chartreuse orange|Manufacturer#2|Brand#21|ECONOMY POLISHED COPPER|1|LG DRUM|1233.33|accordi\n334|lawn wheat dodger forest firebrick|Manufacturer#3|Brand#31|ECONOMY PLATED BRASS|32|LG JAR|1234.33|deas. blithely express\n335|dark cream plum goldenrod smoke|Manufacturer#3|Brand#32|SMALL BRUSHED COPPER|3|SM JAR|1235.33|ly ironic deposits h\n336|maroon medium green tomato rosy|Manufacturer#2|Brand#25|PROMO ANODIZED TIN|3|WRAP JAR|1236.33|nto beans.\n337|magenta lavender blush peru aquamarine|Manufacturer#2|Brand#23|MEDIUM BRUSHED COPPER|29|MED DRUM|1237.33|uests. carefully fin\n338|turquoise honeydew dim saddle grey|Manufacturer#1|Brand#14|LARGE ANODIZED NICKEL|26|JUMBO JAR|1238.33|of th\n339|saddle white red royal midnight|Manufacturer#2|Brand#23|PROMO BURNISHED COPPER|14|LG PACK|1239.33|across the slyly expre\n340|sienna purple lawn coral navy|Manufacturer#2|Brand#23|STANDARD BRUSHED TIN|3|JUMBO BOX|1240.34|l ideas wake. quic\n341|pale smoke drab spring burnished|Manufacturer#4|Brand#45|PROMO POLISHED COPPER|34|MED PKG|1241.34|ully regular a\n342|purple indian burnished khaki puff|Manufacturer#4|Brand#43|LARGE BURNISHED NICKEL|37|JUMBO CAN|1242.34|riously final pac\n343|blush thistle chartreuse tomato navy|Manufacturer#3|Brand#35|PROMO ANODIZED NICKEL|7|SM JAR|1243.34|s-- ironic foxes boost\n344|lime chiffon sky magenta midnight|Manufacturer#4|Brand#45|LARGE ANODIZED TIN|39|SM BOX|1244.34| according to the pend\n345|cyan frosted spring orange puff|Manufacturer#4|Brand#45|ECONOMY BURNISHED NICKEL|22|LG CAN|1245.34|un carefull\n346|medium honeydew blush lace lemon|Manufacturer#5|Brand#51|ECONOMY BRUSHED COPPER|8|LG PKG|1246.34|benea\n347|deep antique royal bisque rosy|Manufacturer#3|Brand#34|SMALL ANODIZED TIN|28|WRAP BAG|1247.34|r, even instruct\n348|blush navajo peru chartreuse dim|Manufacturer#5|Brand#53|MEDIUM POLISHED STEEL|16|WRAP BOX|1248.34| pending\n349|ghost black hot salmon lace|Manufacturer#2|Brand#24|ECONOMY BURNISHED NICKEL|12|MED JAR|1249.34|tes. unusual acco\n350|peach thistle royal papaya cornsilk|Manufacturer#3|Brand#32|STANDARD ANODIZED NICKEL|25|WRAP CAN|1250.35|luffily p\n351|slate pale misty sienna red|Manufacturer#3|Brand#32|STANDARD PLATED TIN|48|WRAP CAN|1251.35|sts integrate. bl\n352|sandy almond peach ivory violet|Manufacturer#4|Brand#42|MEDIUM BURNISHED BRASS|44|JUMBO CAN|1252.35|lites ne\n353|azure linen grey blanched dim|Manufacturer#3|Brand#34|PROMO BURNISHED TIN|43|WRAP JAR|1253.35|final packages use\n354|frosted deep powder beige lemon|Manufacturer#4|Brand#42|PROMO BURNISHED STEEL|38|LG CAN|1254.35|d requests wake \n355|mint goldenrod sandy burnished olive|Manufacturer#5|Brand#52|ECONOMY POLISHED NICKEL|16|JUMBO JAR|1255.35| slyly bold theodoli\n356|hot pink peach lemon chartreuse|Manufacturer#1|Brand#14|STANDARD BRUSHED NICKEL|46|WRAP BAG|1256.35|according to the furio\n357|frosted rosy powder floral mint|Manufacturer#4|Brand#42|STANDARD PLATED COPPER|19|SM DRUM|1257.35|s use at\n358|purple maroon lavender cornsilk salmon|Manufacturer#2|Brand#24|STANDARD POLISHED BRASS|39|JUMBO PACK|1258.35|g grouches use slyly\n359|pink medium olive rosy linen|Manufacturer#3|Brand#35|PROMO PLATED BRASS|19|LG DRUM|1259.35|ithely \n360|midnight papaya violet peach cream|Manufacturer#3|Brand#35|SMALL POLISHED COPPER|14|WRAP BOX|1260.36|. bold\n361|indian dim tan antique puff|Manufacturer#5|Brand#52|SMALL ANODIZED COPPER|42|WRAP DRUM|1261.36| dolphins. carefully r\n362|sienna green ghost rose lawn|Manufacturer#1|Brand#13|ECONOMY POLISHED COPPER|9|JUMBO BAG|1262.36|accounts. furi\n363|rosy grey blush dark lime|Manufacturer#2|Brand#22|MEDIUM ANODIZED BRASS|16|WRAP JAR|1263.36|ideas. regular ac\n364|puff almond blanched drab misty|Manufacturer#4|Brand#43|LARGE BRUSHED NICKEL|21|MED PKG|1264.36|ach caref\n365|olive blue navy frosted violet|Manufacturer#3|Brand#31|SMALL POLISHED STEEL|8|LG CAN|1265.36|efully regula\n366|seashell antique beige thistle cream|Manufacturer#5|Brand#53|ECONOMY BURNISHED STEEL|46|WRAP DRUM|1266.36|unts cajole\n367|black lavender dark cornflower grey|Manufacturer#2|Brand#23|STANDARD PLATED BRASS|2|JUMBO PACK|1267.36|ly about the tithe\n368|cornsilk blush bisque hot cyan|Manufacturer#5|Brand#52|ECONOMY BRUSHED TIN|8|SM PACK|1268.36|t the even, \n369|drab plum olive firebrick beige|Manufacturer#2|Brand#21|SMALL POLISHED BRASS|11|SM BOX|1269.36|ilent request\n370|almond linen pale aquamarine ghost|Manufacturer#5|Brand#52|LARGE BRUSHED COPPER|46|JUMBO BOX|1270.37|ost quickly against\n371|maroon dark beige forest drab|Manufacturer#4|Brand#45|ECONOMY ANODIZED COPPER|27|MED BAG|1271.37|o beans. even inst\n372|black frosted aquamarine maroon orange|Manufacturer#3|Brand#32|LARGE ANODIZED STEEL|35|LG CAN|1272.37| regular accounts \n373|beige blanched coral cornsilk sky|Manufacturer#5|Brand#53|STANDARD PLATED TIN|37|JUMBO PACK|1273.37| after the fl\n374|orange lemon cream dim midnight|Manufacturer#1|Brand#11|LARGE PLATED STEEL|18|MED BOX|1274.37| packages. furiou\n375|floral blush lawn puff brown|Manufacturer#1|Brand#15|SMALL POLISHED BRASS|16|JUMBO JAR|1275.37| ironic courts aga\n376|lace drab wheat red tan|Manufacturer#4|Brand#44|MEDIUM BRUSHED STEEL|44|SM PKG|1276.37|blithe\n377|green forest rose slate cornflower|Manufacturer#4|Brand#44|STANDARD BURNISHED COPPER|16|WRAP BOX|1277.37|ly regula\n378|burnished lime green turquoise red|Manufacturer#3|Brand#32|SMALL PLATED COPPER|45|JUMBO PACK|1278.37| slyly careful, ironic\n379|cream sienna sandy thistle medium|Manufacturer#3|Brand#33|ECONOMY POLISHED STEEL|5|SM CASE|1279.37|l have to sl\n380|khaki powder royal maroon snow|Manufacturer#4|Brand#41|LARGE BRUSHED TIN|6|JUMBO DRUM|1280.38|tions. blithely ev\n381|sienna peach wheat linen tomato|Manufacturer#5|Brand#53|ECONOMY ANODIZED COPPER|37|LG CASE|1281.38|ons. furiousl\n382|honeydew ghost lace plum sky|Manufacturer#4|Brand#41|LARGE POLISHED BRASS|4|SM CAN|1282.38|blithely \n383|seashell tan purple almond coral|Manufacturer#2|Brand#23|SMALL BRUSHED COPPER|45|SM PACK|1283.38|telet\n384|papaya thistle rosy rose puff|Manufacturer#4|Brand#43|STANDARD BURNISHED TIN|2|WRAP CAN|1284.38|blithel\n385|snow chocolate ivory lemon magenta|Manufacturer#1|Brand#12|STANDARD PLATED TIN|41|WRAP JAR|1285.38|nts wake \n386|red blanched thistle bisque wheat|Manufacturer#1|Brand#13|STANDARD PLATED STEEL|4|SM BOX|1286.38|ests. final a\n387|smoke white metallic sky firebrick|Manufacturer#3|Brand#34|LARGE BRUSHED COPPER|23|WRAP CASE|1287.38|gular w\n388|lawn puff grey forest indian|Manufacturer#1|Brand#11|STANDARD POLISHED STEEL|20|LG PKG|1288.38|nt pinto beans after t\n389|magenta ivory lawn antique turquoise|Manufacturer#1|Brand#11|PROMO BRUSHED TIN|16|MED PKG|1289.38|cial pinto beans amo\n390|maroon magenta saddle linen lime|Manufacturer#5|Brand#54|SMALL ANODIZED COPPER|24|MED DRUM|1290.39|unts integrate \n391|seashell cornsilk metallic royal sky|Manufacturer#4|Brand#41|SMALL PLATED STEEL|18|LG PACK|1291.39|ickly ironic f\n392|medium plum powder blush yellow|Manufacturer#4|Brand#42|MEDIUM BRUSHED COPPER|21|SM BOX|1292.39|even request\n393|blue coral gainsboro slate lawn|Manufacturer#3|Brand#34|STANDARD PLATED NICKEL|36|JUMBO CAN|1293.39|gular pi\n394|chocolate royal cornflower papaya yellow|Manufacturer#1|Brand#11|MEDIUM POLISHED STEEL|33|MED JAR|1294.39|ely even excuses.\n395|drab steel royal medium chartreuse|Manufacturer#3|Brand#32|MEDIUM BURNISHED BRASS|9|LG BOX|1295.39|sits haggle fluffily \n396|medium seashell lavender almond tan|Manufacturer#3|Brand#35|SMALL BURNISHED COPPER|37|SM CASE|1296.39|ctions. silent packag\n397|purple spring turquoise goldenrod grey|Manufacturer#1|Brand#15|LARGE ANODIZED COPPER|3|JUMBO JAR|1297.39|ly ironic pains. f\n398|red salmon sandy white hot|Manufacturer#3|Brand#32|MEDIUM BRUSHED NICKEL|32|SM DRUM|1298.39|ickly sp\n399|orange peru yellow blanched seashell|Manufacturer#2|Brand#21|LARGE BRUSHED BRASS|37|WRAP BAG|1299.39|nts sleep dog\n400|rose blue smoke olive sandy|Manufacturer#2|Brand#21|ECONOMY POLISHED BRASS|32|SM BOX|1300.40| nag. quick\n401|almond chiffon indian green dim|Manufacturer#2|Brand#21|ECONOMY POLISHED STEEL|10|SM PKG|1301.40|ideas cajole \n402|black rosy ghost yellow ivory|Manufacturer#4|Brand#45|LARGE ANODIZED BRASS|16|SM PACK|1302.40|s above t\n403|indian lemon linen almond tomato|Manufacturer#4|Brand#45|MEDIUM BURNISHED STEEL|19|JUMBO DRUM|1303.40|ic requ\n404|maroon olive pink rose cornflower|Manufacturer#4|Brand#41|STANDARD ANODIZED NICKEL|9|SM CASE|1304.40|ckages integrate blit\n405|ivory yellow peru almond cornsilk|Manufacturer#3|Brand#34|STANDARD BRUSHED BRASS|17|WRAP JAR|1305.40|e to maintain careful\n406|thistle saddle white puff lemon|Manufacturer#2|Brand#21|STANDARD PLATED STEEL|43|JUMBO JAR|1306.40|ake foxes-\n407|tomato turquoise dim powder ivory|Manufacturer#5|Brand#53|LARGE BRUSHED NICKEL|39|SM JAR|1307.40|quickl\n408|indian metallic peach gainsboro pale|Manufacturer#2|Brand#24|PROMO POLISHED STEEL|4|MED PKG|1308.40|refully b\n409|smoke forest metallic saddle hot|Manufacturer#4|Brand#44|SMALL PLATED NICKEL|40|MED CASE|1309.40|usly above\n410|tomato sienna hot chartreuse dodger|Manufacturer#2|Brand#25|STANDARD PLATED NICKEL|36|JUMBO BOX|1310.41| ironic pa\n411|lemon dark khaki antique slate|Manufacturer#1|Brand#12|ECONOMY BURNISHED STEEL|17|JUMBO DRUM|1311.41|o beans a\n412|slate forest spring pink peru|Manufacturer#1|Brand#14|SMALL ANODIZED STEEL|33|WRAP PKG|1312.41|ccording to the fur\n413|chiffon ivory lawn metallic beige|Manufacturer#3|Brand#33|PROMO PLATED STEEL|49|MED PKG|1313.41|r deposits wake. alw\n414|pink brown purple puff snow|Manufacturer#4|Brand#41|SMALL BURNISHED STEEL|50|WRAP CASE|1314.41|efully. dolph\n415|dark chocolate wheat ivory orchid|Manufacturer#2|Brand#22|MEDIUM ANODIZED COPPER|42|SM DRUM|1315.41|blithely blit\n416|ghost misty chocolate peach green|Manufacturer#1|Brand#13|MEDIUM POLISHED STEEL|11|WRAP JAR|1316.41|yly pending deposit\n417|gainsboro sky turquoise ghost mint|Manufacturer#1|Brand#14|MEDIUM PLATED TIN|31|LG BAG|1317.41| nag blithely: ex\n418|lavender forest puff beige tan|Manufacturer#1|Brand#11|MEDIUM ANODIZED NICKEL|32|JUMBO JAR|1318.41| ironic deposits. requ\n419|moccasin navy seashell turquoise sienna|Manufacturer#4|Brand#43|ECONOMY BRUSHED TIN|42|WRAP BAG|1319.41|boost slyly among t\n420|chocolate cornsilk midnight beige floral|Manufacturer#2|Brand#23|PROMO BRUSHED TIN|15|LG JAR|1320.42|y. fluffi\n421|white black burnished brown medium|Manufacturer#1|Brand#12|LARGE PLATED STEEL|36|WRAP PACK|1321.42|regular\n422|honeydew smoke violet pink bisque|Manufacturer#3|Brand#33|STANDARD POLISHED TIN|16|LG CAN|1322.42| bold\n423|bisque misty dark hot blush|Manufacturer#3|Brand#31|PROMO BRUSHED BRASS|2|SM JAR|1323.42|accou\n424|dim almond turquoise beige royal|Manufacturer#3|Brand#34|SMALL BRUSHED TIN|9|LG CASE|1324.42|usly slyl\n425|ivory wheat chiffon navy burlywood|Manufacturer#3|Brand#32|PROMO BRUSHED STEEL|23|JUMBO BOX|1325.42|ndenci\n426|maroon linen sienna firebrick burnished|Manufacturer#1|Brand#14|STANDARD ANODIZED NICKEL|42|WRAP PACK|1326.42|furiously even pa\n427|honeydew azure brown royal sky|Manufacturer#2|Brand#22|PROMO BURNISHED NICKEL|47|JUMBO CAN|1327.42|ts. express, unusual \n428|beige spring floral wheat drab|Manufacturer#5|Brand#54|LARGE BURNISHED TIN|14|JUMBO CASE|1328.42|furiously ir\n429|red firebrick smoke magenta moccasin|Manufacturer#4|Brand#45|ECONOMY BRUSHED COPPER|21|MED DRUM|1329.42| carefully even \n430|snow mint deep brown goldenrod|Manufacturer#3|Brand#34|PROMO BURNISHED NICKEL|34|MED CAN|1330.43| sleep. slyly pend\n431|grey smoke navy peach white|Manufacturer#4|Brand#45|MEDIUM POLISHED TIN|30|SM PACK|1331.43|iousl\n432|white cornflower honeydew red ivory|Manufacturer#2|Brand#21|MEDIUM PLATED BRASS|6|MED JAR|1332.43|s nag furiously fluff\n433|olive moccasin almond tomato plum|Manufacturer#1|Brand#12|STANDARD BURNISHED TIN|2|SM PACK|1333.43|ts haggle furiously \n434|turquoise coral violet dim midnight|Manufacturer#5|Brand#51|ECONOMY BURNISHED COPPER|20|JUMBO BAG|1334.43|y against the c\n435|coral misty brown grey hot|Manufacturer#3|Brand#35|ECONOMY ANODIZED BRASS|17|WRAP BAG|1335.43|carefully ironic pack\n436|turquoise yellow dim purple antique|Manufacturer#1|Brand#14|LARGE POLISHED BRASS|50|WRAP CASE|1336.43| the regul\n437|purple slate gainsboro powder magenta|Manufacturer#5|Brand#54|ECONOMY PLATED TIN|17|WRAP PACK|1337.43|e furiously\n438|antique orchid cornflower puff almond|Manufacturer#1|Brand#15|LARGE BURNISHED TIN|31|LG DRUM|1338.43|epitaphs wake \n439|bisque cyan rosy ivory blanched|Manufacturer#2|Brand#21|MEDIUM BURNISHED TIN|32|SM PKG|1339.43|uests wake slyly over \n440|khaki turquoise violet maroon misty|Manufacturer#3|Brand#34|ECONOMY BURNISHED BRASS|22|MED CASE|1340.44|furiously above the\n441|goldenrod royal slate grey burlywood|Manufacturer#3|Brand#35|PROMO BURNISHED STEEL|24|MED BOX|1341.44|s wake atop the ru\n442|honeydew cornsilk powder salmon purple|Manufacturer#4|Brand#41|LARGE BURNISHED TIN|22|MED PACK|1342.44|refully al\n443|chocolate burnished orchid mint royal|Manufacturer#4|Brand#44|STANDARD BURNISHED BRASS|10|SM BOX|1343.44|arefully. sl\n444|ghost blanched forest khaki rose|Manufacturer#3|Brand#32|MEDIUM ANODIZED COPPER|42|LG JAR|1344.44|ickly special excuse\n445|metallic ivory floral cornsilk burnished|Manufacturer#1|Brand#12|LARGE BURNISHED NICKEL|47|SM DRUM|1345.44|s about the re\n446|sandy brown midnight tan bisque|Manufacturer#1|Brand#12|STANDARD BURNISHED NICKEL|11|WRAP CASE|1346.44|s sentiments affix \n447|forest smoke aquamarine cyan dim|Manufacturer#5|Brand#53|STANDARD BURNISHED COPPER|22|LG JAR|1347.44|s theodolites above th\n448|wheat tomato cyan lemon maroon|Manufacturer#1|Brand#13|PROMO POLISHED BRASS|31|LG CAN|1348.44| excuses affix silen\n449|almond thistle cornsilk bisque blush|Manufacturer#2|Brand#25|MEDIUM BRUSHED BRASS|47|JUMBO BAG|1349.44|ular, unusual p\n450|sky lace green pale lime|Manufacturer#4|Brand#43|MEDIUM BURNISHED TIN|23|SM CASE|1350.45|slyly\n451|rosy ghost sky mint smoke|Manufacturer#4|Brand#42|PROMO PLATED TIN|28|WRAP DRUM|1351.45| detect blithely bl\n452|saddle sandy cream sienna blue|Manufacturer#1|Brand#11|STANDARD ANODIZED STEEL|48|JUMBO BOX|1352.45|ic requests wake \n453|lemon navy seashell khaki sienna|Manufacturer#3|Brand#34|ECONOMY ANODIZED TIN|20|LG CASE|1353.45|y above the pen\n454|royal lime saddle magenta violet|Manufacturer#5|Brand#53|MEDIUM BURNISHED STEEL|32|LG BOX|1354.45|ual requests.\n455|aquamarine blanched misty moccasin sky|Manufacturer#2|Brand#22|LARGE BRUSHED BRASS|18|MED JAR|1355.45|ongside of the careful\n456|antique maroon olive turquoise lace|Manufacturer#5|Brand#53|PROMO POLISHED BRASS|40|SM PKG|1356.45|usual,\n457|navy linen forest moccasin slate|Manufacturer#5|Brand#52|SMALL POLISHED TIN|46|SM CAN|1357.45|express accounts. sp\n458|beige papaya grey ivory white|Manufacturer#2|Brand#22|PROMO BRUSHED COPPER|38|MED BAG|1358.45|structions sleep \n459|beige slate magenta dim salmon|Manufacturer#2|Brand#23|MEDIUM ANODIZED NICKEL|27|SM PACK|1359.45| blithely. fur\n460|maroon papaya orange spring light|Manufacturer#3|Brand#35|STANDARD ANODIZED BRASS|47|JUMBO DRUM|1360.46|he carefully\n461|goldenrod dim wheat tan violet|Manufacturer#5|Brand#54|SMALL BRUSHED TIN|44|JUMBO CASE|1361.46|: slyly express pa\n462|lime yellow sandy floral khaki|Manufacturer#5|Brand#54|MEDIUM BURNISHED BRASS|11|WRAP CAN|1362.46|s sleep quickly care\n463|steel cream grey mint dark|Manufacturer#2|Brand#21|MEDIUM ANODIZED COPPER|48|MED PACK|1363.46|unts use fluffily\n464|azure magenta lace smoke ghost|Manufacturer#4|Brand#42|LARGE BURNISHED NICKEL|34|LG BOX|1364.46|l decoys. close exc\n465|smoke sky bisque ghost black|Manufacturer#4|Brand#42|SMALL POLISHED BRASS|12|MED JAR|1365.46|nts. slyly special ac\n466|deep ghost cornflower ivory burnished|Manufacturer#3|Brand#33|PROMO POLISHED BRASS|12|LG PKG|1366.46|ly special pains. iron\n467|cornflower lime midnight plum forest|Manufacturer#2|Brand#24|STANDARD BURNISHED STEEL|48|JUMBO PACK|1367.46|ependenc\n468|spring grey azure khaki lace|Manufacturer#4|Brand#41|MEDIUM BURNISHED STEEL|24|SM PACK|1368.46|o the\n469|burlywood dark steel sky blue|Manufacturer#1|Brand#13|MEDIUM POLISHED STEEL|43|WRAP PKG|1369.46|osits. slyly unusua\n470|honeydew lime khaki saddle indian|Manufacturer#2|Brand#24|STANDARD BRUSHED TIN|38|JUMBO CASE|1370.47|ess deposits\n471|goldenrod honeydew frosted almond chiffon|Manufacturer#1|Brand#11|LARGE BRUSHED STEEL|21|JUMBO BAG|1371.47|sits nag caref\n472|dim misty lime purple cornsilk|Manufacturer#3|Brand#31|PROMO BRUSHED COPPER|24|SM JAR|1372.47| according\n473|moccasin antique lace peach chiffon|Manufacturer#1|Brand#12|ECONOMY PLATED STEEL|2|MED DRUM|1373.47|e furious\n474|papaya green royal burlywood saddle|Manufacturer#1|Brand#14|ECONOMY PLATED STEEL|45|LG PACK|1374.47|ously even reques\n475|coral peru forest thistle khaki|Manufacturer#3|Brand#34|STANDARD ANODIZED NICKEL|30|MED PACK|1375.47|thely unusual th\n476|cornflower deep turquoise slate sandy|Manufacturer#3|Brand#33|STANDARD POLISHED COPPER|33|WRAP PACK|1376.47| according to the blit\n477|plum peru spring firebrick lavender|Manufacturer#1|Brand#11|ECONOMY ANODIZED COPPER|35|JUMBO BAG|1377.47|lent s\n478|lemon snow coral lime seashell|Manufacturer#4|Brand#45|STANDARD POLISHED COPPER|11|MED CASE|1378.47|ly bold foxes ca\n479|snow frosted slate magenta sky|Manufacturer#2|Brand#25|MEDIUM PLATED STEEL|35|SM BAG|1379.47| pending ac\n480|saddle light black drab navajo|Manufacturer#5|Brand#54|MEDIUM ANODIZED STEEL|31|WRAP BOX|1380.48|eans. quickly\n481|slate red firebrick beige aquamarine|Manufacturer#1|Brand#14|MEDIUM BURNISHED BRASS|17|JUMBO PACK|1381.48|s cajole som\n482|burnished spring azure green chocolate|Manufacturer#5|Brand#53|MEDIUM POLISHED STEEL|11|SM DRUM|1382.48|deposits. fluffily b\n483|deep white khaki floral cornflower|Manufacturer#1|Brand#15|MEDIUM ANODIZED COPPER|9|SM PKG|1383.48|usly final instru\n484|bisque rosy olive peach chiffon|Manufacturer#1|Brand#15|MEDIUM POLISHED BRASS|34|SM CASE|1384.48|uffily final deposits.\n485|orchid dark antique deep ivory|Manufacturer#3|Brand#31|PROMO PLATED STEEL|44|LG JAR|1385.48| requests. regularly r\n486|tomato lime moccasin turquoise grey|Manufacturer#4|Brand#45|SMALL PLATED COPPER|15|MED PKG|1386.48|ent accounts among t\n487|firebrick saddle chocolate peru ghost|Manufacturer#5|Brand#52|SMALL BRUSHED COPPER|43|WRAP PACK|1387.48|iously ruthles\n488|forest puff drab beige blue|Manufacturer#2|Brand#21|PROMO PLATED BRASS|47|JUMBO JAR|1388.48| the slyly pending r\n489|smoke green blush deep royal|Manufacturer#2|Brand#22|LARGE BURNISHED TIN|36|LG CAN|1389.48|unts. quickly bold id\n490|yellow mint saddle dodger orchid|Manufacturer#4|Brand#41|SMALL PLATED NICKEL|11|MED BOX|1390.49|fluffily express packa\n491|snow mint rose almond frosted|Manufacturer#3|Brand#31|SMALL POLISHED BRASS|42|MED JAR|1391.49| deposit\n492|turquoise rose navajo deep chiffon|Manufacturer#4|Brand#42|MEDIUM BRUSHED COPPER|13|JUMBO DRUM|1392.49|yly slyly express th\n493|blue gainsboro sky burnished puff|Manufacturer#4|Brand#45|MEDIUM BURNISHED BRASS|8|MED PKG|1393.49|uses. bold \n494|navy pale frosted lawn spring|Manufacturer#4|Brand#42|STANDARD BRUSHED NICKEL|40|MED PKG|1394.49|y pending theodo\n495|lemon burlywood chartreuse forest honeydew|Manufacturer#4|Brand#42|SMALL BRUSHED BRASS|28|MED PKG|1395.49|, ironic pac\n496|orchid bisque antique ivory lavender|Manufacturer#1|Brand#12|STANDARD BRUSHED BRASS|11|SM BAG|1396.49|ealthily\n497|hot powder dim cream metallic|Manufacturer#2|Brand#25|PROMO PLATED TIN|1|SM CAN|1397.49|l accounts sl\n498|sandy sky gainsboro peach cornflower|Manufacturer#2|Brand#25|MEDIUM POLISHED STEEL|21|WRAP CASE|1398.49|lthily even co\n499|lemon pale blue burnished white|Manufacturer#3|Brand#35|SMALL PLATED COPPER|38|JUMBO PKG|1399.49|furiously across \n500|hot medium spring orange violet|Manufacturer#4|Brand#41|MEDIUM POLISHED STEEL|13|SM JAR|1400.50|regul\n501|tomato navy midnight cyan chiffon|Manufacturer#4|Brand#45|SMALL BURNISHED COPPER|37|SM BAG|1401.50|press pinto b\n502|maroon dark plum brown red|Manufacturer#2|Brand#21|SMALL PLATED TIN|6|JUMBO DRUM|1402.50|quests integrate \n503|lavender cyan red lace light|Manufacturer#2|Brand#24|PROMO BRUSHED NICKEL|30|SM PKG|1403.50| the dep\n504|pink tan papaya ivory lace|Manufacturer#2|Brand#24|PROMO BRUSHED STEEL|10|SM JAR|1404.50|refully after the fu\n505|pink cornsilk antique mint forest|Manufacturer#4|Brand#42|STANDARD PLATED BRASS|32|WRAP JAR|1405.50|ly even \n506|beige floral goldenrod lace almond|Manufacturer#1|Brand#14|PROMO BRUSHED NICKEL|30|WRAP PACK|1406.50|y ruthless, fi\n507|red misty salmon ivory khaki|Manufacturer#4|Brand#43|SMALL ANODIZED BRASS|20|SM CASE|1407.50|ial foxes. furiously \n508|pale peach maroon burlywood tan|Manufacturer#1|Brand#14|LARGE ANODIZED TIN|40|WRAP PKG|1408.50|ect according\n509|tomato cornsilk chartreuse blue magenta|Manufacturer#1|Brand#13|MEDIUM BRUSHED NICKEL|30|MED CAN|1409.50|eposits. slyly\n510|blush thistle orchid red lace|Manufacturer#4|Brand#43|SMALL PLATED NICKEL|25|JUMBO PACK|1410.51|al platelets. iro\n511|red pale plum orchid moccasin|Manufacturer#1|Brand#15|STANDARD BRUSHED COPPER|2|LG JAR|1411.51|ss package\n512|firebrick azure mint chocolate wheat|Manufacturer#5|Brand#51|PROMO ANODIZED COPPER|18|LG PACK|1412.51|dolphins cajo\n513|mint thistle chiffon magenta salmon|Manufacturer#3|Brand#32|MEDIUM BRUSHED STEEL|10|WRAP CAN|1413.51| after th\n514|rosy sienna purple light rose|Manufacturer#4|Brand#42|LARGE POLISHED BRASS|10|JUMBO BOX|1414.51|omise\n515|cream navajo drab dark peru|Manufacturer#5|Brand#53|MEDIUM PLATED TIN|37|WRAP BOX|1415.51|ular asymptotes im\n516|purple linen lace blanched snow|Manufacturer#1|Brand#12|SMALL BRUSHED NICKEL|28|LG CAN|1416.51|ly express\n517|aquamarine rosy violet moccasin snow|Manufacturer#3|Brand#34|SMALL BURNISHED COPPER|30|WRAP CAN|1417.51|uses. \n518|yellow tomato lawn rosy lemon|Manufacturer#4|Brand#43|PROMO BRUSHED BRASS|33|JUMBO DRUM|1418.51|n requests hag\n519|violet medium dark red smoke|Manufacturer#3|Brand#32|LARGE BRUSHED BRASS|24|WRAP PACK|1419.51|xes are fluffily fluf\n520|spring honeydew sky sandy almond|Manufacturer#1|Brand#12|PROMO BURNISHED BRASS|49|MED CASE|1420.52| foxes.\n521|grey drab honeydew coral pale|Manufacturer#3|Brand#35|PROMO BRUSHED STEEL|21|JUMBO DRUM|1421.52|the blithe\n522|frosted lawn violet turquoise coral|Manufacturer#3|Brand#32|STANDARD BRUSHED NICKEL|15|MED CASE|1422.52|deas. brave ac\n523|light goldenrod honeydew indian lemon|Manufacturer#4|Brand#42|ECONOMY BRUSHED NICKEL|7|JUMBO JAR|1423.52|e blithe\n524|burnished hot drab midnight tan|Manufacturer#1|Brand#14|SMALL ANODIZED STEEL|17|JUMBO PACK|1424.52|ly reg\n525|plum midnight coral snow lemon|Manufacturer#3|Brand#33|STANDARD BURNISHED STEEL|47|JUMBO CAN|1425.52|ly except \n526|dodger moccasin wheat puff lace|Manufacturer#5|Brand#53|PROMO ANODIZED BRASS|47|JUMBO PKG|1426.52|ronic, iro\n527|moccasin light purple sky magenta|Manufacturer#3|Brand#32|MEDIUM POLISHED NICKEL|39|LG JAR|1427.52|counts wake abov\n528|dim deep puff pink floral|Manufacturer#2|Brand#24|STANDARD PLATED STEEL|40|LG CAN|1428.52|ly ironic pl\n529|powder tan brown royal blush|Manufacturer#3|Brand#31|MEDIUM PLATED NICKEL|33|SM PKG|1429.52|y along the \n530|mint grey chiffon magenta saddle|Manufacturer#4|Brand#42|PROMO PLATED STEEL|45|JUMBO CASE|1430.53|kages about t\n531|navajo hot forest puff olive|Manufacturer#2|Brand#22|ECONOMY POLISHED NICKEL|16|WRAP DRUM|1431.53| atta\n532|spring wheat purple chiffon puff|Manufacturer#1|Brand#14|LARGE BRUSHED NICKEL|45|WRAP BAG|1432.53|s. blithely\n533|cream lemon goldenrod rosy aquamarine|Manufacturer#2|Brand#24|PROMO PLATED STEEL|38|JUMBO PACK|1433.53|ng the blithely final \n534|bisque saddle hot steel frosted|Manufacturer#5|Brand#53|STANDARD PLATED NICKEL|27|LG CASE|1434.53|rts poach over th\n535|mint chocolate tomato peach sky|Manufacturer#5|Brand#53|STANDARD PLATED TIN|17|SM CASE|1435.53|he unusual idea\n536|orchid snow thistle peru moccasin|Manufacturer#2|Brand#23|STANDARD PLATED STEEL|36|JUMBO BAG|1436.53|le slyl\n537|hot orange red cornsilk goldenrod|Manufacturer#1|Brand#15|ECONOMY BRUSHED TIN|26|WRAP BOX|1437.53|fully regular platelet\n538|rose black peach orchid cyan|Manufacturer#1|Brand#15|ECONOMY POLISHED STEEL|24|SM PACK|1438.53|ic epitap\n539|dodger midnight salmon drab saddle|Manufacturer#3|Brand#34|MEDIUM BURNISHED TIN|4|SM DRUM|1439.53| deposits. fi\n540|violet white steel chocolate burlywood|Manufacturer#1|Brand#14|MEDIUM PLATED NICKEL|7|LG PACK|1440.54|cial, pending account\n541|magenta tan antique sky pale|Manufacturer#3|Brand#35|STANDARD ANODIZED NICKEL|34|LG JAR|1441.54|s are fluffily above t\n542|light lace gainsboro coral lavender|Manufacturer#1|Brand#11|SMALL BRUSHED STEEL|40|WRAP CASE|1442.54|sits bo\n543|sky powder saddle metallic moccasin|Manufacturer#5|Brand#54|ECONOMY POLISHED BRASS|4|JUMBO BOX|1443.54| cajole \n544|peach red azure indian lavender|Manufacturer#2|Brand#22|STANDARD BURNISHED NICKEL|42|SM CASE|1444.54|leep slyl\n545|purple peru ivory gainsboro peach|Manufacturer#3|Brand#31|LARGE PLATED NICKEL|14|MED JAR|1445.54|arefully regular packa\n546|metallic grey black rosy papaya|Manufacturer#2|Brand#25|ECONOMY ANODIZED STEEL|8|LG CASE|1446.54|ly unusu\n547|white turquoise olive cornflower pale|Manufacturer#4|Brand#43|MEDIUM BURNISHED COPPER|48|WRAP DRUM|1447.54|ial theodolites among \n548|cyan lime lawn misty mint|Manufacturer#3|Brand#33|ECONOMY ANODIZED TIN|28|SM CAN|1448.54|gular\n549|thistle green indian sandy purple|Manufacturer#5|Brand#53|SMALL BRUSHED BRASS|36|SM CAN|1449.54|ly unus\n550|brown blush dark white mint|Manufacturer#4|Brand#44|STANDARD BRUSHED STEEL|27|SM JAR|1450.55|arefully iron\n551|tan misty floral firebrick puff|Manufacturer#5|Brand#54|LARGE BURNISHED TIN|10|MED JAR|1451.55|dolites cajole furious\n552|lime violet puff snow chiffon|Manufacturer#4|Brand#44|LARGE POLISHED BRASS|1|JUMBO CAN|1452.55|longside of the sl\n553|gainsboro tomato lawn peru bisque|Manufacturer#2|Brand#23|SMALL POLISHED NICKEL|1|WRAP DRUM|1453.55|fily regular depen\n554|blue medium royal blush ghost|Manufacturer#5|Brand#53|STANDARD BRUSHED STEEL|31|LG JAR|1454.55|ts? ironically\n555|midnight honeydew brown beige slate|Manufacturer#4|Brand#42|PROMO BURNISHED TIN|49|SM JAR|1455.55|es hag\n556|frosted antique aquamarine seashell powder|Manufacturer#2|Brand#23|SMALL BRUSHED BRASS|49|WRAP JAR|1456.55|ar theodolites ma\n557|firebrick ghost steel green chartreuse|Manufacturer#3|Brand#35|LARGE BURNISHED TIN|8|JUMBO PKG|1457.55|ccounts im\n558|blanched seashell navy black dim|Manufacturer#3|Brand#31|STANDARD PLATED BRASS|25|MED BOX|1458.55|eposits. slyly\n559|wheat coral navajo cornflower dark|Manufacturer#3|Brand#31|LARGE PLATED STEEL|27|SM PKG|1459.55|stealthily express \n560|tan indian lime olive dodger|Manufacturer#2|Brand#25|STANDARD BRUSHED COPPER|37|WRAP PKG|1460.56|e special pac\n561|firebrick slate grey coral goldenrod|Manufacturer#1|Brand#14|SMALL POLISHED STEEL|44|JUMBO CASE|1461.56|-- ironic, iron\n562|tan blue thistle forest cornsilk|Manufacturer#3|Brand#32|SMALL BURNISHED STEEL|16|MED PACK|1462.56| fluffi\n563|blanched light indian goldenrod dim|Manufacturer#3|Brand#31|MEDIUM POLISHED STEEL|39|MED DRUM|1463.56|furiously bold,\n564|drab bisque puff ghost floral|Manufacturer#2|Brand#22|LARGE ANODIZED BRASS|41|SM PKG|1464.56|blithely after the ev\n565|coral mint sky rosy red|Manufacturer#1|Brand#14|PROMO BRUSHED STEEL|9|JUMBO JAR|1465.56| blithely unusual r\n566|seashell dark yellow turquoise white|Manufacturer#2|Brand#25|ECONOMY BURNISHED TIN|19|LG PKG|1466.56|ounts wake across t\n567|cornsilk lace white medium honeydew|Manufacturer#5|Brand#55|STANDARD BRUSHED STEEL|19|JUMBO BAG|1467.56|pending pinto \n568|purple burlywood pale cornsilk chartreuse|Manufacturer#3|Brand#31|PROMO POLISHED STEEL|6|SM BOX|1468.56|ts. ideas along t\n569|cornflower burlywood blue spring purple|Manufacturer#4|Brand#41|SMALL BRUSHED TIN|46|MED BOX|1469.56|y according to\n570|cyan navajo turquoise forest firebrick|Manufacturer#5|Brand#51|ECONOMY BRUSHED COPPER|44|SM PKG|1470.57|le. s\n571|medium orchid purple metallic ghost|Manufacturer#2|Brand#22|LARGE PLATED TIN|1|WRAP CAN|1471.57|ly ironic\n572|navy plum lime goldenrod saddle|Manufacturer#4|Brand#43|LARGE BURNISHED COPPER|3|SM BOX|1472.57|requests use\n573|dark midnight forest lace sandy|Manufacturer#2|Brand#23|SMALL PLATED TIN|42|WRAP PACK|1473.57|expres\n574|bisque snow turquoise lace blush|Manufacturer#2|Brand#23|SMALL PLATED BRASS|36|SM PACK|1474.57|oss th\n575|brown seashell hot lavender frosted|Manufacturer#1|Brand#14|SMALL BRUSHED NICKEL|10|MED CAN|1475.57|ns integrate a\n576|puff lavender black yellow lawn|Manufacturer#3|Brand#32|PROMO BURNISHED TIN|27|SM CAN|1476.57| foxes. accounts bo\n577|lime green dark misty chiffon|Manufacturer#2|Brand#22|LARGE BRUSHED STEEL|34|JUMBO BAG|1477.57|ding, fur\n578|black lawn light plum blush|Manufacturer#4|Brand#41|MEDIUM ANODIZED NICKEL|9|WRAP DRUM|1478.57| furiously iron\n579|spring brown peach bisque sienna|Manufacturer#3|Brand#31|STANDARD POLISHED STEEL|8|WRAP JAR|1479.57|ades. carefully \n580|navajo chocolate lemon hot green|Manufacturer#1|Brand#12|LARGE BURNISHED COPPER|30|JUMBO PACK|1480.58|usly expre\n581|frosted gainsboro chiffon lawn green|Manufacturer#2|Brand#25|ECONOMY ANODIZED BRASS|44|SM BAG|1481.58| furious foxes. silen\n582|tan cyan peach rose salmon|Manufacturer#4|Brand#45|LARGE BRUSHED TIN|23|MED JAR|1482.58|y bold\n583|turquoise burnished orchid firebrick blush|Manufacturer#3|Brand#33|LARGE BURNISHED NICKEL|35|JUMBO CAN|1483.58| sleep \n584|goldenrod magenta papaya medium chiffon|Manufacturer#1|Brand#12|SMALL POLISHED COPPER|42|MED JAR|1484.58|lar, ironic ac\n585|slate hot honeydew midnight burnished|Manufacturer#5|Brand#53|SMALL PLATED COPPER|7|JUMBO PKG|1485.58|sts haggle q\n586|ivory rosy royal powder firebrick|Manufacturer#1|Brand#11|PROMO ANODIZED BRASS|11|LG DRUM|1486.58|ts haggle fluffily \n587|blanched maroon blush burlywood salmon|Manufacturer#4|Brand#44|SMALL POLISHED STEEL|35|MED PKG|1487.58|c accounts. qui\n588|cyan powder brown sandy burlywood|Manufacturer#5|Brand#55|SMALL PLATED COPPER|45|WRAP BAG|1488.58|ss foxes. even, ex\n589|red orange salmon rosy thistle|Manufacturer#1|Brand#13|LARGE POLISHED BRASS|3|SM PKG|1489.58|cies. regul\n590|azure burnished honeydew navy cyan|Manufacturer#4|Brand#42|LARGE POLISHED COPPER|38|WRAP PKG|1490.59| dependencies sho\n591|sienna burlywood salmon navy green|Manufacturer#1|Brand#14|ECONOMY PLATED NICKEL|40|SM PKG|1491.59| the b\n592|dodger burnished yellow wheat antique|Manufacturer#2|Brand#23|LARGE POLISHED STEEL|7|MED DRUM|1492.59|ide of the notorni\n593|metallic sienna sandy spring frosted|Manufacturer#2|Brand#25|PROMO POLISHED STEEL|20|JUMBO CASE|1493.59|ake after\n594|seashell magenta rosy cornsilk smoke|Manufacturer#3|Brand#31|PROMO BRUSHED BRASS|28|SM JAR|1494.59|s detect fu\n595|chiffon lace linen steel bisque|Manufacturer#5|Brand#51|PROMO BRUSHED BRASS|23|JUMBO JAR|1495.59|yly fin\n596|drab snow chartreuse dim ivory|Manufacturer#5|Brand#52|STANDARD POLISHED TIN|35|JUMBO JAR|1496.59|epend\n597|cyan orchid grey tan bisque|Manufacturer#4|Brand#44|STANDARD ANODIZED NICKEL|36|SM DRUM|1497.59|ronic foxes. speci\n598|deep royal drab violet chartreuse|Manufacturer#2|Brand#24|MEDIUM POLISHED BRASS|9|MED JAR|1498.59|he fluffily regular\n599|cornsilk peru steel beige blue|Manufacturer#5|Brand#51|MEDIUM BRUSHED NICKEL|30|JUMBO PACK|1499.59|ckages against t\n600|puff bisque midnight seashell burlywood|Manufacturer#4|Brand#42|STANDARD POLISHED NICKEL|4|SM JAR|1500.60|ow furiously agai\n601|coral thistle blanched linen tan|Manufacturer#1|Brand#13|STANDARD POLISHED BRASS|13|SM PKG|1501.60|lithely thi\n602|metallic smoke purple rosy magenta|Manufacturer#1|Brand#12|PROMO BRUSHED COPPER|8|SM BAG|1502.60|telets a\n603|dim blush puff chiffon salmon|Manufacturer#1|Brand#12|PROMO ANODIZED COPPER|41|WRAP PKG|1503.60|uests. fluffily regul\n604|bisque light frosted white olive|Manufacturer#4|Brand#41|MEDIUM PLATED STEEL|19|MED PACK|1504.60|are after the regula\n605|grey burlywood linen tan tomato|Manufacturer#1|Brand#11|LARGE POLISHED STEEL|30|LG JAR|1505.60| bold, final accou\n606|navajo lace sandy aquamarine light|Manufacturer#3|Brand#32|STANDARD PLATED STEEL|36|LG DRUM|1506.60|hins p\n607|dark sienna thistle sandy maroon|Manufacturer#5|Brand#54|MEDIUM POLISHED TIN|13|MED JAR|1507.60|ctions wake qu\n608|mint medium blush black metallic|Manufacturer#2|Brand#25|PROMO PLATED BRASS|35|JUMBO PKG|1508.60|xes. express r\n609|almond lemon olive smoke moccasin|Manufacturer#2|Brand#21|ECONOMY BRUSHED TIN|49|JUMBO DRUM|1509.60|cial deposits haggle\n610|plum purple seashell white lemon|Manufacturer#1|Brand#11|ECONOMY BURNISHED TIN|38|JUMBO BOX|1510.61|slyly pending re\n611|light burnished linen sienna dim|Manufacturer#4|Brand#42|MEDIUM PLATED TIN|45|SM DRUM|1511.61|iously even packages. \n612|midnight azure moccasin rosy lawn|Manufacturer#4|Brand#42|PROMO PLATED STEEL|19|LG BOX|1512.61|se quickly;\n613|cream coral sandy thistle spring|Manufacturer#3|Brand#34|LARGE BURNISHED COPPER|22|LG DRUM|1513.61| cajo\n614|dodger beige maroon puff medium|Manufacturer#2|Brand#23|STANDARD BURNISHED NICKEL|6|WRAP PKG|1514.61|ending accounts ca\n615|rosy seashell brown blush tan|Manufacturer#4|Brand#41|PROMO ANODIZED COPPER|4|WRAP PKG|1515.61| dogged \n616|tan powder lavender dim goldenrod|Manufacturer#2|Brand#25|STANDARD BRUSHED STEEL|1|WRAP JAR|1516.61|w packages cajole b\n617|peru grey snow papaya firebrick|Manufacturer#2|Brand#22|MEDIUM ANODIZED NICKEL|37|WRAP DRUM|1517.61|furious\n618|ivory wheat smoke deep lemon|Manufacturer#2|Brand#22|PROMO BRUSHED BRASS|9|LG BOX|1518.61|ogs. furiously \n619|powder medium drab lemon tomato|Manufacturer#3|Brand#34|LARGE ANODIZED STEEL|46|MED JAR|1519.61|cuses are \n620|frosted tomato almond salmon powder|Manufacturer#3|Brand#35|MEDIUM BRUSHED COPPER|4|LG PKG|1520.62|efully carefully speci\n621|almond azure drab ghost mint|Manufacturer#5|Brand#53|PROMO PLATED TIN|47|JUMBO DRUM|1521.62|ffily furiously fin\n622|burlywood dark blue thistle navajo|Manufacturer#5|Brand#51|LARGE POLISHED STEEL|19|WRAP BAG|1522.62|sly deposits. f\n623|chiffon bisque steel orange tomato|Manufacturer#3|Brand#32|ECONOMY POLISHED NICKEL|8|MED BOX|1523.62|ly ironic \n624|burlywood ghost orchid misty coral|Manufacturer#1|Brand#12|LARGE BURNISHED NICKEL|29|SM BAG|1524.62|dencies. even d\n625|beige indian lime sienna plum|Manufacturer#3|Brand#35|SMALL PLATED COPPER|38|LG JAR|1525.62|blithely slow multipl\n626|cream ivory brown peach honeydew|Manufacturer#1|Brand#12|SMALL ANODIZED COPPER|23|SM JAR|1526.62|furiously-- \n627|drab yellow spring turquoise rose|Manufacturer#4|Brand#44|PROMO ANODIZED NICKEL|13|SM BAG|1527.62|y ironic \n628|black mint olive drab medium|Manufacturer#3|Brand#33|ECONOMY BRUSHED BRASS|40|LG BOX|1528.62|sits. \n629|magenta lace red hot pale|Manufacturer#5|Brand#51|MEDIUM POLISHED COPPER|25|WRAP BOX|1529.62|fully even co\n630|lawn lace burnished hot frosted|Manufacturer#2|Brand#25|MEDIUM PLATED NICKEL|20|LG DRUM|1530.63|g to th\n631|moccasin lawn cornsilk chartreuse midnight|Manufacturer#2|Brand#22|STANDARD ANODIZED NICKEL|34|WRAP BAG|1531.63|ely silen\n632|blush floral tan thistle chiffon|Manufacturer#3|Brand#34|PROMO PLATED NICKEL|2|MED PACK|1532.63|y ironic instru\n633|navy burnished wheat slate deep|Manufacturer#3|Brand#33|ECONOMY POLISHED NICKEL|24|MED PKG|1533.63|bold brai\n634|grey aquamarine deep violet blush|Manufacturer#3|Brand#35|SMALL ANODIZED NICKEL|34|MED DRUM|1534.63|ions.\n635|ghost aquamarine gainsboro lemon peach|Manufacturer#5|Brand#51|PROMO POLISHED STEEL|49|WRAP JAR|1535.63|y ironic excuses.\n636|burlywood deep antique snow wheat|Manufacturer#5|Brand#55|PROMO BRUSHED STEEL|36|SM PACK|1536.63|ithely\n637|brown smoke sandy honeydew antique|Manufacturer#5|Brand#51|LARGE BURNISHED NICKEL|48|JUMBO CASE|1537.63|oxes after the blithel\n638|lace royal pink cornsilk antique|Manufacturer#5|Brand#54|SMALL PLATED TIN|37|JUMBO PACK|1538.63| carefully. blithely \n639|black plum tomato cornflower medium|Manufacturer#2|Brand#25|STANDARD POLISHED BRASS|33|SM BAG|1539.63|s cajole quickly. care\n640|grey seashell bisque indian deep|Manufacturer#1|Brand#12|MEDIUM BRUSHED TIN|20|WRAP CASE|1540.64|ly regular acco\n641|mint saddle yellow sienna frosted|Manufacturer#5|Brand#55|MEDIUM PLATED COPPER|18|SM JAR|1541.64| bold instr\n642|moccasin ghost sandy goldenrod cornsilk|Manufacturer#4|Brand#43|ECONOMY BRUSHED BRASS|12|MED PACK|1542.64| bold packages bey\n643|frosted chocolate dodger honeydew ghost|Manufacturer#3|Brand#32|MEDIUM POLISHED STEEL|8|MED DRUM|1543.64|refully fina\n644|rosy bisque hot burnished dark|Manufacturer#5|Brand#52|LARGE PLATED STEEL|34|SM PACK|1544.64|lphins. blithely\n645|thistle sky antique khaki chartreuse|Manufacturer#4|Brand#45|MEDIUM BURNISHED NICKEL|45|LG CASE|1545.64| the theodol\n646|tan honeydew lime light white|Manufacturer#5|Brand#52|SMALL POLISHED COPPER|21|LG PACK|1546.64|ajole according to t\n647|rose khaki drab smoke peach|Manufacturer#3|Brand#35|LARGE BURNISHED STEEL|38|MED PKG|1547.64|ters. ironic pinto\n648|bisque blue drab cyan almond|Manufacturer#3|Brand#34|SMALL BRUSHED STEEL|2|WRAP JAR|1548.64|eas. regular\n649|lavender blush rosy beige sky|Manufacturer#1|Brand#12|STANDARD BURNISHED TIN|33|JUMBO DRUM|1549.64|fully!\n650|bisque navajo mint medium seashell|Manufacturer#5|Brand#53|PROMO ANODIZED NICKEL|40|MED JAR|1550.65|ar dependen\n651|bisque floral dim burlywood moccasin|Manufacturer#5|Brand#52|MEDIUM POLISHED BRASS|4|WRAP BAG|1551.65|efully final instruct\n652|navajo white indian yellow grey|Manufacturer#1|Brand#12|LARGE BRUSHED TIN|39|WRAP PKG|1552.65|le furiously unusua\n653|lime cornflower sky beige antique|Manufacturer#1|Brand#15|ECONOMY BURNISHED BRASS|28|JUMBO DRUM|1553.65|ias. i\n654|cyan burnished tomato chiffon navy|Manufacturer#2|Brand#23|PROMO PLATED COPPER|2|WRAP BAG|1554.65|posits haggle \n655|tomato firebrick yellow rosy orchid|Manufacturer#1|Brand#12|STANDARD BURNISHED TIN|23|JUMBO CAN|1555.65|ic instructions.\n656|maroon dodger ghost gainsboro pink|Manufacturer#1|Brand#15|STANDARD BURNISHED COPPER|17|MED CASE|1556.65|tions wake ca\n657|saddle lawn blue cyan cornsilk|Manufacturer#3|Brand#31|ECONOMY BURNISHED COPPER|36|LG PACK|1557.65| packages accord\n658|ghost mint hot bisque salmon|Manufacturer#1|Brand#15|MEDIUM PLATED COPPER|27|SM JAR|1558.65| somas haggle quickly\n659|black bisque plum pale cyan|Manufacturer#3|Brand#34|MEDIUM BRUSHED BRASS|20|LG JAR|1559.65|across the \n660|sky dim thistle saddle pale|Manufacturer#5|Brand#51|STANDARD POLISHED TIN|8|SM CAN|1560.66|ages cajole fluffily \n661|snow beige moccasin red lime|Manufacturer#3|Brand#32|PROMO PLATED STEEL|35|SM PKG|1561.66|ng asympt\n662|ivory chocolate slate midnight ghost|Manufacturer#4|Brand#45|STANDARD PLATED STEEL|35|LG PACK|1562.66|e eve\n663|ivory orchid azure frosted light|Manufacturer#4|Brand#41|PROMO POLISHED COPPER|6|WRAP PACK|1563.66| beans\n664|frosted burnished tomato chiffon seashell|Manufacturer#4|Brand#43|MEDIUM BURNISHED TIN|26|LG BAG|1564.66|about the slyly s\n665|rose antique cyan cornflower drab|Manufacturer#1|Brand#13|MEDIUM POLISHED STEEL|14|MED DRUM|1565.66|usly final excuse\n666|ivory dim orchid antique spring|Manufacturer#1|Brand#13|ECONOMY BURNISHED NICKEL|20|MED PACK|1566.66|ronic theodolites ca\n667|wheat ghost honeydew plum grey|Manufacturer#4|Brand#43|MEDIUM PLATED STEEL|42|MED JAR|1567.66|ly pending packages. \n668|honeydew pink dodger cream dim|Manufacturer#2|Brand#22|SMALL ANODIZED NICKEL|10|LG PACK|1568.66|odolites. furio\n669|khaki metallic plum smoke hot|Manufacturer#1|Brand#11|STANDARD BRUSHED TIN|37|MED BAG|1569.66|bold deposi\n670|slate black smoke pale moccasin|Manufacturer#1|Brand#12|ECONOMY PLATED COPPER|24|LG DRUM|1570.67|es use fluffily unusu\n671|powder metallic salmon slate chocolate|Manufacturer#5|Brand#53|SMALL PLATED STEEL|31|JUMBO BOX|1571.67|es. s\n672|lawn rosy cornsilk floral misty|Manufacturer#2|Brand#23|PROMO BURNISHED STEEL|13|LG PACK|1572.67|, pen\n673|navy azure sandy yellow sky|Manufacturer#2|Brand#23|MEDIUM ANODIZED COPPER|26|SM DRUM|1573.67|y final \n674|tomato chartreuse cornflower green pale|Manufacturer#3|Brand#35|SMALL BRUSHED TIN|5|LG PKG|1574.67|unts use slyly bold\n675|misty hot pale ghost yellow|Manufacturer#1|Brand#11|LARGE BRUSHED COPPER|2|SM CAN|1575.67|ely re\n676|moccasin cornflower burlywood tomato light|Manufacturer#1|Brand#11|MEDIUM ANODIZED BRASS|45|SM CAN|1576.67|ges. enticingly ironi\n677|pale steel blush mint cream|Manufacturer#4|Brand#44|PROMO ANODIZED BRASS|6|WRAP BAG|1577.67|accounts \n678|salmon ivory red dodger spring|Manufacturer#4|Brand#44|PROMO BRUSHED TIN|15|SM BAG|1578.67|ronic pin\n679|purple blanched linen metallic indian|Manufacturer#4|Brand#41|SMALL BURNISHED TIN|50|MED BOX|1579.67|iously ironic in\n680|burnished ghost coral maroon yellow|Manufacturer#1|Brand#14|SMALL PLATED COPPER|44|LG PACK|1580.68|l depo\n681|dark slate almond ghost chartreuse|Manufacturer#3|Brand#32|ECONOMY PLATED COPPER|9|WRAP CAN|1581.68|ic requests wake accor\n682|lawn khaki green cornflower sienna|Manufacturer#5|Brand#53|LARGE POLISHED NICKEL|48|SM BOX|1582.68|ackages. \n683|sienna cornsilk chiffon olive blush|Manufacturer#5|Brand#53|LARGE BURNISHED NICKEL|1|MED PACK|1583.68|its hag\n684|metallic azure hot orange spring|Manufacturer#3|Brand#33|PROMO BURNISHED TIN|6|WRAP BOX|1584.68|unts are fluff\n685|turquoise orchid plum green tomato|Manufacturer#3|Brand#31|SMALL PLATED COPPER|36|LG JAR|1585.68|s. furiously ruthless\n686|goldenrod deep cornflower dodger rose|Manufacturer#5|Brand#54|STANDARD BRUSHED COPPER|15|WRAP BAG|1586.68|lly ironic accounts \n687|frosted cornsilk tomato burnished smoke|Manufacturer#5|Brand#51|LARGE POLISHED NICKEL|47|JUMBO BOX|1587.68|packages. even,\n688|navajo khaki almond royal chartreuse|Manufacturer#4|Brand#45|PROMO BRUSHED COPPER|38|JUMBO CAN|1588.68|rious ideas \n689|bisque red peru almond grey|Manufacturer#4|Brand#43|LARGE PLATED COPPER|22|SM PACK|1589.68|ual package\n690|cornflower dodger saddle ivory tan|Manufacturer#3|Brand#32|SMALL PLATED BRASS|31|WRAP PACK|1590.69|al, bold fra\n691|burnished dim gainsboro thistle blue|Manufacturer#3|Brand#35|PROMO BURNISHED BRASS|3|SM JAR|1591.69|ully slyly unusu\n692|puff beige smoke seashell sienna|Manufacturer#1|Brand#14|MEDIUM BRUSHED BRASS|20|SM DRUM|1592.69|tructions. ev\n693|honeydew ghost azure yellow magenta|Manufacturer#5|Brand#54|SMALL PLATED TIN|14|WRAP CASE|1593.69|lets sle\n694|pink maroon blanched beige cyan|Manufacturer#3|Brand#35|ECONOMY PLATED STEEL|35|JUMBO CASE|1594.69|es. final \n695|deep peru lavender antique royal|Manufacturer#2|Brand#23|PROMO ANODIZED COPPER|19|MED DRUM|1595.69|eep blithely \n696|forest lemon cream black pink|Manufacturer#3|Brand#33|MEDIUM ANODIZED BRASS|3|LG CAN|1596.69|al acco\n697|dark white slate honeydew maroon|Manufacturer#4|Brand#41|LARGE PLATED TIN|8|MED CAN|1597.69| special pa\n698|blush sienna honeydew yellow goldenrod|Manufacturer#4|Brand#42|ECONOMY ANODIZED COPPER|44|LG PACK|1598.69|ronic, ironic \n699|plum orchid red linen misty|Manufacturer#1|Brand#11|MEDIUM PLATED STEEL|5|WRAP CAN|1599.69| regular notornis b\n700|lace hot khaki steel orchid|Manufacturer#3|Brand#34|PROMO BRUSHED STEEL|31|SM JAR|1600.70|ly even foxes. fi\n701|frosted lavender black deep ghost|Manufacturer#4|Brand#43|MEDIUM ANODIZED COPPER|36|WRAP DRUM|1601.70| use blithe\n702|navajo lavender dim puff bisque|Manufacturer#4|Brand#41|STANDARD BURNISHED BRASS|26|SM CAN|1602.70|yly f\n703|puff floral red linen dark|Manufacturer#3|Brand#32|ECONOMY POLISHED TIN|20|WRAP CASE|1603.70|nstructions wake alon\n704|salmon lawn chocolate lace honeydew|Manufacturer#3|Brand#34|LARGE ANODIZED BRASS|23|MED CAN|1604.70|l, ironic\n705|snow drab lawn dark tan|Manufacturer#3|Brand#33|ECONOMY PLATED TIN|47|LG PKG|1605.70|ironic\n706|cream white navajo frosted puff|Manufacturer#3|Brand#33|LARGE PLATED BRASS|6|SM PKG|1606.70|quests. i\n707|salmon khaki misty deep peru|Manufacturer#1|Brand#11|SMALL POLISHED BRASS|17|SM BAG|1607.70|osits about the fluf\n708|brown midnight plum violet tomato|Manufacturer#3|Brand#32|SMALL BRUSHED NICKEL|29|SM DRUM|1608.70|ully final instru\n709|tomato sienna cornflower black misty|Manufacturer#4|Brand#43|SMALL BRUSHED TIN|16|SM BAG|1609.70|ns. blithely i\n710|chartreuse thistle midnight magenta violet|Manufacturer#1|Brand#15|SMALL POLISHED TIN|27|JUMBO PACK|1610.71|fluffily unu\n711|goldenrod sienna linen steel floral|Manufacturer#1|Brand#11|PROMO BRUSHED NICKEL|24|LG DRUM|1611.71|s hang slyly regula\n712|blanched rose royal tomato hot|Manufacturer#3|Brand#33|ECONOMY BRUSHED COPPER|25|SM CASE|1612.71|even, fluffy \n713|gainsboro plum powder seashell lawn|Manufacturer#2|Brand#25|SMALL ANODIZED COPPER|20|SM CAN|1613.71|onic de\n714|chartreuse medium gainsboro honeydew saddle|Manufacturer#1|Brand#11|MEDIUM POLISHED NICKEL|35|SM JAR|1614.71|ions. furiously final\n715|deep yellow coral sienna white|Manufacturer#2|Brand#24|LARGE BRUSHED TIN|28|WRAP BAG|1615.71|ons boost. furiou\n716|grey floral sienna cyan gainsboro|Manufacturer#1|Brand#12|SMALL POLISHED TIN|37|JUMBO CAN|1616.71|osits! packages hag\n717|papaya turquoise spring midnight medium|Manufacturer#2|Brand#22|STANDARD PLATED TIN|28|JUMBO JAR|1617.71| final notornis na\n718|khaki navy saddle ghost orchid|Manufacturer#3|Brand#35|PROMO BURNISHED COPPER|24|SM CAN|1618.71|ests. final, exp\n719|chocolate honeydew khaki aquamarine white|Manufacturer#3|Brand#35|STANDARD BRUSHED NICKEL|49|JUMBO BAG|1619.71|ect. furiously \n720|light khaki dodger dark lawn|Manufacturer#4|Brand#45|SMALL BURNISHED NICKEL|8|JUMBO BAG|1620.72|as. furiously\n721|gainsboro chocolate maroon hot sienna|Manufacturer#5|Brand#52|STANDARD ANODIZED STEEL|33|MED PKG|1621.72|ly special instruct\n722|forest chartreuse plum royal papaya|Manufacturer#4|Brand#43|ECONOMY BURNISHED NICKEL|43|JUMBO BAG|1622.72|ic packages. fin\n723|slate magenta lemon peru pink|Manufacturer#3|Brand#31|LARGE BRUSHED BRASS|34|WRAP CASE|1623.72|unts. excu\n724|rosy coral thistle ghost papaya|Manufacturer#3|Brand#35|PROMO POLISHED COPPER|28|JUMBO BOX|1624.72| carefully pend\n725|hot blanched salmon slate burlywood|Manufacturer#4|Brand#43|LARGE PLATED NICKEL|11|JUMBO BOX|1625.72|r theo\n726|cream aquamarine violet medium midnight|Manufacturer#4|Brand#43|PROMO POLISHED NICKEL|41|JUMBO BAG|1626.72|arefully? carefully i\n727|royal pink smoke bisque lime|Manufacturer#3|Brand#35|PROMO PLATED BRASS|35|SM PKG|1627.72| about the furio\n728|cornsilk grey sienna blue sky|Manufacturer#3|Brand#33|ECONOMY PLATED TIN|27|LG JAR|1628.72|fily regular package\n729|papaya cornsilk gainsboro rose pink|Manufacturer#3|Brand#35|ECONOMY BRUSHED TIN|6|LG JAR|1629.72|egular accou\n730|rose blush saddle cyan hot|Manufacturer#4|Brand#45|MEDIUM PLATED TIN|8|SM PKG|1630.73|ironic \n731|firebrick indian forest rose khaki|Manufacturer#3|Brand#33|PROMO BURNISHED NICKEL|34|MED CASE|1631.73|snooze above the\n732|violet frosted red papaya drab|Manufacturer#2|Brand#21|MEDIUM BRUSHED BRASS|1|SM BAG|1632.73|. sauternes haggl\n733|burnished goldenrod royal plum midnight|Manufacturer#4|Brand#43|PROMO ANODIZED STEEL|1|MED CASE|1633.73| special instruct\n734|lace light bisque dodger aquamarine|Manufacturer#2|Brand#21|MEDIUM BRUSHED STEEL|34|WRAP DRUM|1634.73|quests \n735|wheat white honeydew almond coral|Manufacturer#4|Brand#42|SMALL BRUSHED STEEL|44|SM CAN|1635.73|kages are according \n736|slate honeydew pink magenta lace|Manufacturer#3|Brand#32|PROMO ANODIZED BRASS|44|JUMBO DRUM|1636.73|telets wake carefu\n737|orange navajo blanched cyan snow|Manufacturer#5|Brand#53|PROMO POLISHED NICKEL|25|SM BAG|1637.73|re slyly. bold, regul\n738|peru floral sky steel maroon|Manufacturer#2|Brand#21|ECONOMY BURNISHED COPPER|29|JUMBO CAN|1638.73|ckages wake. slyly fin\n739|powder lavender wheat pale puff|Manufacturer#3|Brand#31|ECONOMY BRUSHED COPPER|24|JUMBO BOX|1639.73|the regular\n740|almond aquamarine mint misty red|Manufacturer#3|Brand#35|STANDARD POLISHED COPPER|7|WRAP PACK|1640.74| even dep\n741|thistle seashell cyan cornsilk indian|Manufacturer#4|Brand#44|STANDARD PLATED TIN|33|WRAP PACK|1641.74|oxes slee\n742|dodger cream snow pink floral|Manufacturer#5|Brand#51|STANDARD BRUSHED NICKEL|30|SM PACK|1642.74|out the furiously\n743|navajo midnight peru orange salmon|Manufacturer#4|Brand#42|LARGE ANODIZED COPPER|4|MED JAR|1643.74|al packages ar\n744|burlywood deep hot turquoise chartreuse|Manufacturer#5|Brand#53|MEDIUM BURNISHED COPPER|47|MED PKG|1644.74|uriously brave ideas\n745|seashell dark lime midnight puff|Manufacturer#3|Brand#31|PROMO POLISHED COPPER|25|JUMBO BOX|1645.74| silent\n746|honeydew brown sienna red chocolate|Manufacturer#2|Brand#23|ECONOMY ANODIZED STEEL|31|MED CAN|1646.74|osits affix furiously \n747|lace red hot mint goldenrod|Manufacturer#1|Brand#11|LARGE BURNISHED NICKEL|20|WRAP PKG|1647.74|deposits b\n748|forest indian snow grey peru|Manufacturer#5|Brand#54|STANDARD BURNISHED BRASS|32|SM CAN|1648.74|arefully bo\n749|snow goldenrod puff violet mint|Manufacturer#2|Brand#24|SMALL PLATED COPPER|16|MED JAR|1649.74| bold re\n750|puff slate magenta powder lawn|Manufacturer#4|Brand#43|LARGE POLISHED NICKEL|47|MED PACK|1650.75|tipliers about the q\n751|steel blush indian rosy pink|Manufacturer#1|Brand#11|SMALL POLISHED TIN|17|SM DRUM|1651.75|uests haggle bl\n752|medium blue midnight misty frosted|Manufacturer#5|Brand#53|MEDIUM ANODIZED COPPER|1|MED PACK|1652.75|lites mold b\n753|blanched plum navajo beige indian|Manufacturer#2|Brand#25|STANDARD ANODIZED TIN|13|LG DRUM|1653.75|nal foxes. quickly f\n754|thistle steel sky light red|Manufacturer#5|Brand#51|SMALL ANODIZED STEEL|42|WRAP JAR|1654.75|es. blithely dogge\n755|spring dodger floral metallic moccasin|Manufacturer#2|Brand#22|STANDARD PLATED TIN|12|WRAP PACK|1655.75| wake. even packages a\n756|ghost antique snow cream cyan|Manufacturer#5|Brand#52|STANDARD PLATED BRASS|44|MED BAG|1656.75|rious\n757|papaya dim slate saddle navajo|Manufacturer#3|Brand#35|ECONOMY POLISHED NICKEL|12|JUMBO CASE|1657.75|the quickly\n758|yellow orchid dim cyan burlywood|Manufacturer#1|Brand#11|ECONOMY ANODIZED TIN|25|MED BOX|1658.75|haggle \n759|yellow powder navajo maroon chartreuse|Manufacturer#3|Brand#34|LARGE BURNISHED TIN|20|JUMBO CAN|1659.75|struction\n760|orange navy hot aquamarine sienna|Manufacturer#4|Brand#44|MEDIUM POLISHED COPPER|6|LG JAR|1660.76|jole according \n761|seashell green dodger beige linen|Manufacturer#3|Brand#34|ECONOMY ANODIZED TIN|7|SM JAR|1661.76|ages according \n762|peach grey firebrick dim smoke|Manufacturer#1|Brand#15|LARGE POLISHED TIN|35|MED PKG|1662.76|xpress ideas. fluff\n763|wheat seashell azure chartreuse dodger|Manufacturer#4|Brand#44|LARGE BRUSHED TIN|50|SM PKG|1663.76|counts. regu\n764|cyan moccasin blanched light purple|Manufacturer#1|Brand#13|SMALL ANODIZED NICKEL|11|LG JAR|1664.76|es. final, bold \n765|thistle red smoke chartreuse orange|Manufacturer#3|Brand#35|STANDARD BRUSHED BRASS|3|WRAP BAG|1665.76|ly regular pint\n766|midnight sienna orange gainsboro black|Manufacturer#2|Brand#24|MEDIUM BURNISHED NICKEL|20|MED PACK|1666.76|inal ideas. asy\n767|blush firebrick misty blanched purple|Manufacturer#2|Brand#24|LARGE POLISHED TIN|50|MED DRUM|1667.76|ts. carefully unu\n768|maroon gainsboro seashell hot sandy|Manufacturer#4|Brand#43|LARGE BRUSHED COPPER|41|SM DRUM|1668.76|olites haggle: car\n769|tomato royal firebrick turquoise cream|Manufacturer#2|Brand#24|MEDIUM BRUSHED TIN|8|LG CASE|1669.76|ake carefull\n770|lemon yellow coral deep lime|Manufacturer#2|Brand#25|LARGE BRUSHED TIN|7|SM PACK|1670.77|ave ideas. \n771|plum maroon lavender tan firebrick|Manufacturer#1|Brand#15|SMALL BURNISHED TIN|17|LG DRUM|1671.77| cajole slyly fina\n772|dodger firebrick peach ivory seashell|Manufacturer#3|Brand#35|SMALL PLATED BRASS|33|MED PKG|1672.77|dolites haggle sp\n773|saddle medium beige purple plum|Manufacturer#3|Brand#32|PROMO BURNISHED STEEL|16|LG CASE|1673.77| final, sp\n774|wheat chiffon cyan misty moccasin|Manufacturer#2|Brand#24|STANDARD ANODIZED NICKEL|26|WRAP CASE|1674.77|ly express dependen\n775|papaya misty orchid snow metallic|Manufacturer#1|Brand#14|LARGE PLATED TIN|2|MED PACK|1675.77|azzle carefully \n776|powder indian dodger hot lemon|Manufacturer#5|Brand#51|STANDARD ANODIZED BRASS|21|WRAP DRUM|1676.77|egular orbits haggl\n777|blanched indian pink frosted grey|Manufacturer#5|Brand#53|SMALL ANODIZED STEEL|50|JUMBO JAR|1677.77| theodolites \n778|saddle mint navy cyan cornflower|Manufacturer#3|Brand#32|PROMO BURNISHED STEEL|20|WRAP BOX|1678.77|nic re\n779|salmon burnished orange rose cornsilk|Manufacturer#2|Brand#22|ECONOMY BRUSHED STEEL|26|WRAP DRUM|1679.77| regular epitaphs are \n780|rosy chocolate tan moccasin salmon|Manufacturer#5|Brand#55|MEDIUM ANODIZED STEEL|31|MED CASE|1680.78|t quickly sly\n781|light orchid purple black navy|Manufacturer#1|Brand#13|ECONOMY PLATED STEEL|22|LG DRUM|1681.78|gular, regu\n782|peru firebrick coral chartreuse rosy|Manufacturer#1|Brand#13|LARGE POLISHED BRASS|29|SM BAG|1682.78|hely u\n783|cyan turquoise azure pink dark|Manufacturer#1|Brand#13|SMALL PLATED NICKEL|4|MED DRUM|1683.78|ss acc\n784|light smoke seashell snow chartreuse|Manufacturer#1|Brand#12|MEDIUM BRUSHED COPPER|47|LG DRUM|1684.78|unts. furiously\n785|coral saddle indian lime frosted|Manufacturer#3|Brand#33|PROMO ANODIZED TIN|41|SM PACK|1685.78|use carefully\n786|royal azure ivory moccasin salmon|Manufacturer#2|Brand#23|STANDARD PLATED NICKEL|20|SM PACK|1686.78|eodoli\n787|pale metallic ivory peach slate|Manufacturer#5|Brand#55|SMALL ANODIZED NICKEL|35|WRAP PKG|1687.78|es alongside of\n788|floral dark dodger chartreuse lavender|Manufacturer#3|Brand#34|LARGE PLATED STEEL|10|LG JAR|1688.78|s solv\n789|dodger spring antique lace papaya|Manufacturer#3|Brand#35|PROMO BURNISHED STEEL|14|LG PACK|1689.78|ts hagg\n790|lavender yellow pink puff olive|Manufacturer#4|Brand#42|PROMO POLISHED COPPER|35|JUMBO BOX|1690.79|s are \n791|indian blush medium thistle lime|Manufacturer#2|Brand#24|STANDARD ANODIZED COPPER|24|MED PACK|1691.79| special pinto bean\n792|plum indian cornflower frosted purple|Manufacturer#1|Brand#13|SMALL BURNISHED BRASS|8|MED PKG|1692.79|efully unusual deposi\n793|white chiffon blue green violet|Manufacturer#5|Brand#51|LARGE POLISHED NICKEL|28|JUMBO CAN|1693.79|tions. furiou\n794|ivory peach light thistle antique|Manufacturer#5|Brand#51|PROMO PLATED COPPER|47|MED PKG|1694.79|le bl\n795|cream royal light yellow hot|Manufacturer#5|Brand#54|MEDIUM POLISHED TIN|46|MED JAR|1695.79|ully regula\n796|beige frosted cyan hot puff|Manufacturer#5|Brand#51|ECONOMY BRUSHED STEEL|50|WRAP CAN|1696.79|yly fina\n797|violet peach puff orange white|Manufacturer#4|Brand#41|STANDARD BURNISHED STEEL|10|WRAP BAG|1697.79|ments\n798|turquoise indian white bisque chartreuse|Manufacturer#1|Brand#12|ECONOMY PLATED NICKEL|18|LG JAR|1698.79|ffily even excuses\n799|green azure rose aquamarine floral|Manufacturer#3|Brand#35|PROMO POLISHED TIN|7|MED JAR|1699.79|he slyly brave excuses\n800|maroon mint medium lace plum|Manufacturer#1|Brand#11|PROMO BRUSHED BRASS|29|WRAP BAG|1700.80|s sleep about the car\n801|linen steel salmon beige lemon|Manufacturer#5|Brand#54|LARGE BRUSHED NICKEL|18|JUMBO DRUM|1701.80|latelets. slyly fi\n802|rosy ghost cyan puff dark|Manufacturer#3|Brand#34|ECONOMY PLATED BRASS|41|WRAP JAR|1702.80|equest\n803|brown navy tan salmon honeydew|Manufacturer#5|Brand#52|SMALL ANODIZED TIN|50|MED PKG|1703.80|ly at the accou\n804|bisque grey honeydew goldenrod ghost|Manufacturer#2|Brand#23|SMALL POLISHED BRASS|9|SM BAG|1704.80|itaphs sle\n805|tan ghost cyan salmon goldenrod|Manufacturer#5|Brand#52|STANDARD PLATED BRASS|8|JUMBO PKG|1705.80|t blithe\n806|blue violet yellow khaki azure|Manufacturer#3|Brand#32|PROMO BURNISHED TIN|45|LG BAG|1706.80| ironic theodolites a\n807|turquoise snow navy brown lime|Manufacturer#2|Brand#24|LARGE ANODIZED NICKEL|25|WRAP JAR|1707.80| quietly express pi\n808|rosy indian sky frosted blush|Manufacturer#5|Brand#51|SMALL BRUSHED TIN|36|MED PKG|1708.80| pending asymptotes a\n809|almond steel maroon chiffon frosted|Manufacturer#3|Brand#32|STANDARD BRUSHED COPPER|17|SM BAG|1709.80|n pla\n810|blanched lemon magenta medium dark|Manufacturer#5|Brand#54|LARGE ANODIZED TIN|13|WRAP BOX|1710.81|instructions. \n811|ivory bisque black chiffon gainsboro|Manufacturer#3|Brand#33|PROMO BRUSHED NICKEL|32|SM BOX|1711.81|pecial, ironic pac\n812|firebrick olive chartreuse frosted ivory|Manufacturer#3|Brand#33|LARGE PLATED TIN|11|WRAP BAG|1712.81|asymptot\n813|sandy ghost khaki frosted goldenrod|Manufacturer#4|Brand#43|MEDIUM POLISHED STEEL|2|WRAP DRUM|1713.81|riously ironic deposi\n814|burnished seashell floral moccasin antique|Manufacturer#5|Brand#54|ECONOMY PLATED NICKEL|43|MED PKG|1714.81|xpress requests. \n815|light peach saddle medium firebrick|Manufacturer#2|Brand#22|ECONOMY BRUSHED NICKEL|5|LG PACK|1715.81|nt requests a\n816|thistle ivory pink olive puff|Manufacturer#1|Brand#13|STANDARD POLISHED NICKEL|35|LG CAN|1716.81| thin \n817|azure chocolate lavender blanched burnished|Manufacturer#4|Brand#41|STANDARD ANODIZED TIN|36|JUMBO PKG|1717.81|carefu\n818|navajo saddle indian cornflower steel|Manufacturer#4|Brand#43|PROMO BRUSHED BRASS|36|MED JAR|1718.81|ckages\n819|light magenta indian khaki lavender|Manufacturer#5|Brand#54|ECONOMY BRUSHED STEEL|27|SM CAN|1719.81|as are slyly against\n820|wheat salmon forest lavender papaya|Manufacturer#1|Brand#11|STANDARD BURNISHED STEEL|20|WRAP JAR|1720.82|ely regular theodo\n821|smoke drab rose hot spring|Manufacturer#4|Brand#42|PROMO POLISHED TIN|15|MED BOX|1721.82|gle across t\n822|purple lawn ivory black slate|Manufacturer#5|Brand#52|PROMO ANODIZED BRASS|21|LG CASE|1722.82|ding to t\n823|maroon salmon puff medium brown|Manufacturer#2|Brand#25|SMALL BURNISHED COPPER|21|WRAP CASE|1723.82|inal the\n824|dodger snow orange almond magenta|Manufacturer#1|Brand#15|LARGE BURNISHED TIN|34|MED BAG|1724.82|s. ironic\n825|puff deep hot tomato powder|Manufacturer#1|Brand#13|PROMO PLATED TIN|47|LG CAN|1725.82|lar ide\n826|ghost rosy beige salmon lemon|Manufacturer#3|Brand#34|ECONOMY PLATED TIN|21|SM CASE|1726.82|ifts could \n827|linen ghost smoke blanched cream|Manufacturer#1|Brand#13|PROMO POLISHED COPPER|15|JUMBO PACK|1727.82|sits; depe\n828|lemon light grey burlywood drab|Manufacturer#4|Brand#44|LARGE BRUSHED NICKEL|11|LG PKG|1728.82|symptotes. furiously\n829|cream indian deep linen antique|Manufacturer#3|Brand#35|MEDIUM BURNISHED BRASS|33|SM JAR|1729.82|tegrate. \n830|cornflower medium salmon coral magenta|Manufacturer#5|Brand#54|SMALL POLISHED TIN|37|JUMBO CAN|1730.83|ep final instructi\n831|violet dark midnight lawn cream|Manufacturer#5|Brand#51|STANDARD POLISHED COPPER|8|LG CAN|1731.83|se blithely. special,\n832|orange peach midnight burnished lavender|Manufacturer#4|Brand#44|STANDARD ANODIZED COPPER|3|WRAP BAG|1732.83|fily bo\n833|thistle puff cream papaya deep|Manufacturer#4|Brand#41|SMALL BRUSHED STEEL|34|JUMBO CAN|1733.83|s thrash furiously fin\n834|aquamarine frosted orange dodger khaki|Manufacturer#5|Brand#52|MEDIUM POLISHED TIN|43|JUMBO CAN|1734.83|mptotes integrate \n835|turquoise peru light aquamarine dark|Manufacturer#5|Brand#53|MEDIUM PLATED STEEL|21|SM BOX|1735.83|uses. furi\n836|almond sky red lime smoke|Manufacturer#3|Brand#31|LARGE BURNISHED TIN|32|SM JAR|1736.83|lyly bold accounts. t\n837|papaya goldenrod burlywood purple brown|Manufacturer#1|Brand#13|MEDIUM BURNISHED BRASS|27|WRAP PKG|1737.83|hely furious packages\n838|lime peach puff papaya olive|Manufacturer#2|Brand#22|MEDIUM ANODIZED TIN|9|WRAP BAG|1738.83| final pac\n839|cornflower white papaya violet navajo|Manufacturer#2|Brand#25|LARGE BRUSHED NICKEL|19|JUMBO CAN|1739.83|ggle a\n840|khaki mint chartreuse dark frosted|Manufacturer#4|Brand#44|ECONOMY ANODIZED BRASS|42|WRAP PKG|1740.84|ending ideas w\n841|floral khaki grey metallic firebrick|Manufacturer#1|Brand#15|MEDIUM BURNISHED BRASS|48|WRAP BOX|1741.84|y regul\n842|gainsboro dim navajo chartreuse olive|Manufacturer#1|Brand#11|SMALL ANODIZED BRASS|4|MED PACK|1742.84|en accounts! carefu\n843|frosted metallic mint lawn blanched|Manufacturer#1|Brand#15|MEDIUM POLISHED STEEL|50|LG PACK|1743.84| the \n844|honeydew smoke lemon ghost navajo|Manufacturer#3|Brand#35|LARGE PLATED BRASS|22|SM CASE|1744.84|ix furiously beyond\n845|papaya burlywood bisque linen navy|Manufacturer#4|Brand#41|LARGE PLATED STEEL|25|LG JAR|1745.84|ully regula\n846|purple papaya saddle cream medium|Manufacturer#2|Brand#24|ECONOMY POLISHED NICKEL|6|SM BAG|1746.84|sometimes bold foxes \n847|chartreuse pink azure tan slate|Manufacturer#2|Brand#25|SMALL PLATED STEEL|21|SM PACK|1747.84|ely blith\n848|orange olive puff midnight almond|Manufacturer#5|Brand#53|LARGE BURNISHED TIN|26|LG JAR|1748.84|gular requests u\n849|lawn cornflower puff rosy saddle|Manufacturer#4|Brand#44|STANDARD POLISHED TIN|41|MED DRUM|1749.84|nts among the pending\n850|peach goldenrod honeydew moccasin sienna|Manufacturer#4|Brand#41|STANDARD BRUSHED NICKEL|31|JUMBO DRUM|1750.85|kages are carefu\n851|maroon beige navy forest honeydew|Manufacturer#5|Brand#54|ECONOMY BURNISHED COPPER|30|LG CAN|1751.85| asympto\n852|lemon slate khaki misty hot|Manufacturer#2|Brand#24|PROMO PLATED NICKEL|47|JUMBO BOX|1752.85|side of the\n853|peach honeydew cyan peru light|Manufacturer#5|Brand#51|SMALL ANODIZED NICKEL|42|JUMBO CASE|1753.85|ts sleep!\n854|pale wheat lace midnight papaya|Manufacturer#4|Brand#41|SMALL POLISHED COPPER|29|JUMBO CASE|1754.85|ests cajole furious\n855|dim blue slate dodger yellow|Manufacturer#3|Brand#31|STANDARD BURNISHED TIN|46|MED BOX|1755.85|ly express fox\n856|blush frosted powder antique blanched|Manufacturer#1|Brand#13|MEDIUM BRUSHED STEEL|12|MED BAG|1756.85|posits integrate sly\n857|goldenrod beige lemon midnight cornflower|Manufacturer#3|Brand#35|PROMO PLATED TIN|8|MED PACK|1757.85|final de\n858|papaya maroon hot blue pink|Manufacturer#3|Brand#33|ECONOMY BURNISHED COPPER|14|SM CAN|1758.85|elets breach f\n859|ghost blue indian cornsilk drab|Manufacturer#3|Brand#35|ECONOMY ANODIZED COPPER|10|WRAP BAG|1759.85| even Tiresias wake fu\n860|burlywood peach papaya violet midnight|Manufacturer#3|Brand#32|SMALL POLISHED STEEL|36|LG PKG|1760.86|oss the f\n861|blue sienna navy coral violet|Manufacturer#4|Brand#43|ECONOMY BRUSHED COPPER|48|JUMBO BOX|1761.86| furiousl\n862|tan dim chiffon steel purple|Manufacturer#5|Brand#54|LARGE POLISHED NICKEL|12|LG PKG|1762.86| pending depo\n863|light khaki lime lemon burnished|Manufacturer#2|Brand#25|STANDARD ANODIZED COPPER|41|LG CAN|1763.86| regular pinto beans\n864|linen drab blush maroon sienna|Manufacturer#1|Brand#11|STANDARD POLISHED TIN|39|JUMBO PACK|1764.86|boost.\n865|brown seashell red bisque sandy|Manufacturer#1|Brand#11|SMALL ANODIZED BRASS|12|JUMBO PACK|1765.86|s along t\n866|dodger antique tan coral honeydew|Manufacturer#1|Brand#11|STANDARD PLATED COPPER|13|WRAP BOX|1766.86|eas sleep quickly\n867|snow spring black white burnished|Manufacturer#5|Brand#54|PROMO POLISHED NICKEL|4|LG BOX|1767.86|nts wake care\n868|navajo azure beige aquamarine blush|Manufacturer#3|Brand#33|PROMO BRUSHED BRASS|48|JUMBO PKG|1768.86|packages. e\n869|snow blush violet lace ghost|Manufacturer#2|Brand#24|STANDARD ANODIZED COPPER|28|LG BAG|1769.86|y fina\n870|antique hot snow burnished ghost|Manufacturer#5|Brand#54|SMALL PLATED NICKEL|11|JUMBO CAN|1770.87|ldly unusual depth\n871|midnight dark coral ivory burlywood|Manufacturer#5|Brand#55|SMALL ANODIZED BRASS|27|SM PKG|1771.87|olites snooze quickl\n872|papaya frosted cornflower green almond|Manufacturer#1|Brand#15|MEDIUM PLATED STEEL|41|LG CASE|1772.87|gular inst\n873|goldenrod maroon snow cream indian|Manufacturer#1|Brand#11|MEDIUM BRUSHED BRASS|45|MED CAN|1773.87|ecial ideas. slyly\n874|drab slate hot black khaki|Manufacturer#1|Brand#15|MEDIUM PLATED TIN|9|MED BOX|1774.87|e regular req\n875|pale peru red orchid almond|Manufacturer#2|Brand#24|ECONOMY BRUSHED COPPER|1|MED DRUM|1775.87|e furiously. b\n876|frosted lace turquoise sky sandy|Manufacturer#3|Brand#31|MEDIUM PLATED NICKEL|25|SM CAN|1776.87|s after the fur\n877|lime violet tomato drab blush|Manufacturer#3|Brand#31|STANDARD ANODIZED NICKEL|20|MED PACK|1777.87|thely rut\n878|steel lace wheat orchid linen|Manufacturer#3|Brand#32|STANDARD POLISHED NICKEL|40|JUMBO CASE|1778.87| slyly ironic g\n879|spring red indian floral sky|Manufacturer#3|Brand#34|STANDARD BURNISHED TIN|23|MED PKG|1779.87|ar, even request\n880|sandy turquoise cream firebrick rose|Manufacturer#2|Brand#25|STANDARD ANODIZED COPPER|10|LG CASE|1780.88|ly stealthy deposits\n881|bisque frosted khaki linen royal|Manufacturer#2|Brand#21|LARGE BRUSHED BRASS|12|MED PACK|1781.88|ully for \n882|chiffon royal lime almond midnight|Manufacturer#4|Brand#44|ECONOMY BRUSHED STEEL|12|MED BAG|1782.88| silent, pendi\n883|smoke grey dark yellow brown|Manufacturer#3|Brand#31|STANDARD PLATED NICKEL|18|LG PACK|1783.88|fluffily unusual pint\n884|peach medium goldenrod cyan ghost|Manufacturer#1|Brand#11|SMALL POLISHED NICKEL|38|WRAP BAG|1784.88|luffy accounts.\n885|dim peach moccasin snow floral|Manufacturer#4|Brand#42|ECONOMY BRUSHED STEEL|31|WRAP DRUM|1785.88|lar, even ideas caj\n886|turquoise dodger lemon antique green|Manufacturer#5|Brand#55|LARGE BRUSHED COPPER|46|MED BAG|1786.88|tornis haggle! \n887|navajo cream salmon orange smoke|Manufacturer#4|Brand#41|SMALL BRUSHED STEEL|48|JUMBO CAN|1787.88|x slyly aroun\n888|pale turquoise rose olive chiffon|Manufacturer#3|Brand#31|STANDARD BRUSHED NICKEL|44|SM DRUM|1788.88|y according t\n889|saddle midnight almond drab blanched|Manufacturer#5|Brand#51|LARGE BURNISHED BRASS|19|JUMBO DRUM|1789.88|efully regular\n890|sandy lawn chartreuse peru blue|Manufacturer#5|Brand#52|SMALL BRUSHED STEEL|23|MED PKG|1790.89|ests d\n891|plum floral dodger lemon almond|Manufacturer#4|Brand#42|ECONOMY POLISHED STEEL|5|MED CASE|1791.89|yly regular, expr\n892|thistle chocolate sandy powder drab|Manufacturer#2|Brand#22|SMALL BRUSHED NICKEL|3|LG DRUM|1792.89|quickly regular r\n893|dark moccasin bisque cyan azure|Manufacturer#1|Brand#14|PROMO BRUSHED STEEL|9|LG JAR|1793.89|ly regular \n894|chocolate blue smoke azure sandy|Manufacturer#3|Brand#35|SMALL BURNISHED TIN|43|MED BAG|1794.89|s. carefully expr\n895|thistle azure magenta mint burnished|Manufacturer#4|Brand#41|MEDIUM BRUSHED STEEL|17|SM BOX|1795.89|eaves might\n896|peach ivory rose honeydew lace|Manufacturer#4|Brand#43|PROMO ANODIZED TIN|7|LG CASE|1796.89|e across\n897|misty olive purple cream red|Manufacturer#4|Brand#41|SMALL BRUSHED COPPER|26|SM JAR|1797.89|cajole bli\n898|olive burlywood royal cream drab|Manufacturer#4|Brand#45|PROMO POLISHED NICKEL|8|WRAP BAG|1798.89|e against the pack\n899|gainsboro dim misty violet coral|Manufacturer#2|Brand#25|SMALL ANODIZED NICKEL|50|WRAP CASE|1799.89|lithely pending excuse\n900|medium antique khaki cream slate|Manufacturer#2|Brand#21|STANDARD PLATED NICKEL|40|LG CAN|1800.90|foxes. carefully s\n901|white honeydew orange saddle black|Manufacturer#2|Brand#23|ECONOMY PLATED TIN|38|LG CAN|1801.90|ffily final theodoli\n902|red chartreuse firebrick burlywood papaya|Manufacturer#2|Brand#23|LARGE ANODIZED COPPER|14|MED CAN|1802.90|inal pint\n903|navajo chiffon mint ghost burlywood|Manufacturer#4|Brand#44|ECONOMY BURNISHED BRASS|4|SM BAG|1803.90|the i\n904|black olive light sandy linen|Manufacturer#1|Brand#12|STANDARD ANODIZED TIN|20|WRAP DRUM|1804.90|ar theodolites sleep b\n905|burlywood honeydew cornsilk indian green|Manufacturer#2|Brand#25|PROMO BRUSHED BRASS|27|MED CASE|1805.90|es. furiously ir\n906|rosy forest navajo light turquoise|Manufacturer#4|Brand#44|MEDIUM BURNISHED COPPER|45|MED DRUM|1806.90|unusua\n907|plum lawn black chartreuse sandy|Manufacturer#5|Brand#52|ECONOMY ANODIZED STEEL|8|LG CASE|1807.90|es. carefu\n908|pink navy lace papaya brown|Manufacturer#2|Brand#25|MEDIUM BURNISHED TIN|13|JUMBO JAR|1808.90|tions ca\n909|cornsilk tomato chiffon bisque navy|Manufacturer#1|Brand#12|STANDARD PLATED TIN|40|WRAP PKG|1809.90|ht boost quic\n910|coral antique peach navy violet|Manufacturer#4|Brand#42|STANDARD ANODIZED TIN|15|JUMBO JAR|1810.91|ly final \n911|blush coral medium ivory snow|Manufacturer#1|Brand#14|ECONOMY BURNISHED TIN|23|MED CAN|1811.91|foxes cajole after the\n912|indian tomato rose powder turquoise|Manufacturer#5|Brand#55|STANDARD POLISHED NICKEL|17|SM BAG|1812.91|ar theodo\n913|seashell medium burnished moccasin blue|Manufacturer#2|Brand#23|LARGE PLATED BRASS|49|SM DRUM|1813.91|yly even dol\n914|snow rose moccasin tomato linen|Manufacturer#3|Brand#35|ECONOMY POLISHED STEEL|8|LG PKG|1814.91|ckly busil\n915|aquamarine purple spring gainsboro salmon|Manufacturer#2|Brand#21|LARGE ANODIZED BRASS|25|WRAP BAG|1815.91|depos\n916|chartreuse tomato rose chocolate magenta|Manufacturer#2|Brand#21|MEDIUM BURNISHED BRASS|28|MED CAN|1816.91|ons. bravely ironic\n917|midnight khaki medium linen peru|Manufacturer#2|Brand#24|SMALL ANODIZED COPPER|3|SM CASE|1817.91|riously past th\n918|ghost steel beige turquoise ivory|Manufacturer#5|Brand#55|ECONOMY PLATED TIN|15|LG PKG|1818.91|losely according to\n919|sandy cream royal cyan orchid|Manufacturer#1|Brand#13|PROMO BRUSHED COPPER|49|MED BOX|1819.91|along \n920|sky lemon misty slate lawn|Manufacturer#4|Brand#44|MEDIUM BRUSHED STEEL|19|WRAP CAN|1820.92|y regu\n921|khaki yellow plum cyan forest|Manufacturer#1|Brand#11|LARGE BURNISHED STEEL|35|JUMBO DRUM|1821.92|pecial in\n922|frosted dodger cyan medium indian|Manufacturer#3|Brand#34|LARGE BURNISHED BRASS|30|SM CASE|1822.92|uctions. sl\n923|moccasin lace rose navajo forest|Manufacturer#2|Brand#24|STANDARD PLATED TIN|26|WRAP CASE|1823.92|he packag\n924|indian smoke orange ghost papaya|Manufacturer#1|Brand#11|ECONOMY POLISHED STEEL|41|JUMBO PKG|1824.92|the ac\n925|tomato antique plum snow burlywood|Manufacturer#4|Brand#44|STANDARD POLISHED NICKEL|44|MED PACK|1825.92|aggle slyly. blithe\n926|tan deep medium smoke slate|Manufacturer#4|Brand#45|STANDARD BRUSHED TIN|39|MED JAR|1826.92|gle quickly ironic \n927|dodger purple green peach navajo|Manufacturer#1|Brand#14|LARGE ANODIZED TIN|13|WRAP BAG|1827.92|otes snooz\n928|violet dodger cyan orange moccasin|Manufacturer#4|Brand#43|SMALL ANODIZED TIN|43|SM BOX|1828.92|ully ev\n929|burnished pale medium turquoise lace|Manufacturer#3|Brand#33|LARGE PLATED COPPER|40|MED DRUM|1829.92|hely iron\n930|violet ivory orchid rose frosted|Manufacturer#2|Brand#23|SMALL BRUSHED BRASS|26|LG DRUM|1830.93|uickly ironic \n931|red puff chiffon sienna dark|Manufacturer#3|Brand#31|STANDARD ANODIZED NICKEL|37|LG CAN|1831.93|are fluffily. \n932|cornflower sky powder cyan linen|Manufacturer#2|Brand#24|SMALL ANODIZED COPPER|12|LG BOX|1832.93|ly regular depos\n933|pink magenta frosted blanched lime|Manufacturer#3|Brand#33|STANDARD BRUSHED STEEL|25|LG CASE|1833.93|he blit\n934|beige saddle magenta cyan dim|Manufacturer#4|Brand#45|STANDARD ANODIZED BRASS|40|MED JAR|1834.93|ual packages\n935|royal cornflower maroon khaki cornsilk|Manufacturer#5|Brand#53|STANDARD BRUSHED STEEL|8|JUMBO DRUM|1835.93|arefully ironic\n936|white beige firebrick dodger olive|Manufacturer#2|Brand#24|MEDIUM PLATED BRASS|6|WRAP DRUM|1836.93|longside of the reg\n937|drab peru royal pink peach|Manufacturer#3|Brand#33|PROMO ANODIZED TIN|2|JUMBO CAN|1837.93|ithely\n938|chartreuse peach spring cornflower tan|Manufacturer#2|Brand#22|LARGE BRUSHED NICKEL|1|JUMBO CASE|1838.93|ajole slyly r\n939|salmon magenta orange coral aquamarine|Manufacturer#1|Brand#14|PROMO BURNISHED BRASS|41|WRAP PACK|1839.93|ages wake doggedly of \n940|violet coral blanched orange light|Manufacturer#1|Brand#15|PROMO POLISHED TIN|49|JUMBO JAR|1840.94|ate blithely bold, si\n941|lace cornsilk pink medium slate|Manufacturer#2|Brand#25|MEDIUM PLATED NICKEL|8|LG PKG|1841.94|fully \n942|coral peach bisque green seashell|Manufacturer#3|Brand#35|PROMO POLISHED TIN|44|MED BAG|1842.94|nic pinto b\n943|smoke sky chiffon green steel|Manufacturer#5|Brand#55|ECONOMY BRUSHED BRASS|46|SM PKG|1843.94|s wake\n944|medium sandy olive wheat turquoise|Manufacturer#3|Brand#33|SMALL PLATED STEEL|1|LG CAN|1844.94| requests nag furiou\n945|chiffon steel peach black moccasin|Manufacturer#5|Brand#52|MEDIUM PLATED NICKEL|22|MED JAR|1845.94|uffily abo\n946|medium drab rosy spring salmon|Manufacturer#2|Brand#25|MEDIUM PLATED TIN|30|LG BOX|1846.94|y along\n947|black tan salmon maroon chartreuse|Manufacturer#1|Brand#13|MEDIUM BURNISHED STEEL|16|JUMBO JAR|1847.94|usual accounts. b\n948|gainsboro chocolate bisque sandy steel|Manufacturer#2|Brand#22|MEDIUM ANODIZED BRASS|31|WRAP PKG|1848.94|furious\n949|thistle burnished pink yellow wheat|Manufacturer#5|Brand#51|LARGE BURNISHED TIN|47|LG PKG|1849.94|l theodolit\n950|steel purple midnight beige coral|Manufacturer#3|Brand#31|SMALL ANODIZED STEEL|26|WRAP CASE|1850.95|kages cajole quickly \n951|gainsboro indian aquamarine lemon blush|Manufacturer#2|Brand#21|STANDARD BURNISHED STEEL|40|JUMBO JAR|1851.95| final deposits nag a\n952|frosted purple sky steel papaya|Manufacturer#3|Brand#31|MEDIUM BRUSHED TIN|4|MED PACK|1852.95|l deposits sleep bli\n953|tomato cornflower honeydew sandy grey|Manufacturer#4|Brand#41|PROMO ANODIZED TIN|2|WRAP JAR|1853.95|ly quickly final acco\n954|turquoise bisque khaki coral antique|Manufacturer#5|Brand#55|ECONOMY ANODIZED STEEL|35|JUMBO CASE|1854.95|furious\n955|blue olive tan rosy navajo|Manufacturer#3|Brand#32|PROMO BURNISHED STEEL|43|SM CASE|1855.95|tions\n956|orchid yellow dark lawn thistle|Manufacturer#1|Brand#13|STANDARD BRUSHED STEEL|7|LG CASE|1856.95|furiously pending d\n957|snow turquoise coral lime goldenrod|Manufacturer#3|Brand#33|SMALL ANODIZED COPPER|23|SM CAN|1857.95|ndencies wake s\n958|turquoise sandy drab dodger cyan|Manufacturer#3|Brand#35|LARGE PLATED NICKEL|13|LG BOX|1858.95|quickly careful i\n959|sky snow peach olive spring|Manufacturer#4|Brand#41|SMALL BURNISHED STEEL|5|WRAP JAR|1859.95|iously bold depos\n960|sienna royal light orchid moccasin|Manufacturer#5|Brand#55|LARGE PLATED COPPER|26|LG CASE|1860.96|lly b\n961|cornflower chocolate floral burnished green|Manufacturer#1|Brand#15|PROMO BRUSHED BRASS|1|SM PACK|1861.96|fully. f\n962|magenta linen ghost almond floral|Manufacturer#1|Brand#13|MEDIUM PLATED BRASS|38|JUMBO BOX|1862.96|lithely. \n963|violet beige lime dark metallic|Manufacturer#4|Brand#44|MEDIUM PLATED BRASS|44|LG CASE|1863.96|y final deposits wake.\n964|sky orange indian pale burlywood|Manufacturer#5|Brand#54|SMALL POLISHED BRASS|48|MED BOX|1864.96|nal asymptotes. regul\n965|dim cyan bisque black smoke|Manufacturer#3|Brand#34|MEDIUM PLATED STEEL|39|LG PKG|1865.96|dolite\n966|turquoise antique brown violet plum|Manufacturer#4|Brand#42|PROMO BURNISHED TIN|24|MED CAN|1866.96| the carefully \n967|cyan snow azure slate papaya|Manufacturer#5|Brand#55|SMALL BURNISHED COPPER|16|JUMBO CAN|1867.96|leep fur\n968|rose chiffon chocolate lace papaya|Manufacturer#1|Brand#11|LARGE PLATED TIN|23|LG PKG|1868.96|press accounts acro\n969|hot azure royal red dark|Manufacturer#5|Brand#55|PROMO POLISHED STEEL|34|JUMBO CASE|1869.96|le aga\n970|sienna azure light medium green|Manufacturer#1|Brand#11|STANDARD BURNISHED STEEL|44|SM CAN|1870.97|ironic \n971|orange misty green aquamarine forest|Manufacturer#5|Brand#54|LARGE POLISHED BRASS|19|SM DRUM|1871.97|ugouts. bl\n972|tomato burlywood pale moccasin plum|Manufacturer#3|Brand#32|MEDIUM PLATED BRASS|21|JUMBO JAR|1872.97|yly ironic deposits\n973|medium lavender blue tomato slate|Manufacturer#2|Brand#22|STANDARD ANODIZED STEEL|42|LG PACK|1873.97|along \n974|almond steel slate sky rose|Manufacturer#4|Brand#44|MEDIUM ANODIZED BRASS|35|WRAP BAG|1874.97|s. caref\n975|moccasin tomato peach yellow drab|Manufacturer#3|Brand#34|MEDIUM ANODIZED COPPER|22|LG JAR|1875.97|torni\n976|white maroon firebrick snow light|Manufacturer#5|Brand#51|STANDARD BRUSHED NICKEL|19|JUMBO PACK|1876.97| special reque\n977|frosted tomato deep burnished indian|Manufacturer#5|Brand#53|STANDARD BRUSHED STEEL|2|LG CASE|1877.97|the quick\n978|cream tomato rose floral steel|Manufacturer#4|Brand#45|MEDIUM POLISHED TIN|46|LG DRUM|1878.97|quests sleep abo\n979|blush cyan azure snow sandy|Manufacturer#4|Brand#45|STANDARD ANODIZED TIN|4|MED CAN|1879.97| accounts. blithe\n980|saddle burlywood drab deep red|Manufacturer#4|Brand#41|SMALL ANODIZED STEEL|45|LG CASE|1880.98|ronic deposi\n981|floral blanched papaya khaki light|Manufacturer#2|Brand#25|MEDIUM POLISHED COPPER|31|WRAP CAN|1881.98| ironic as\n982|drab steel linen puff rosy|Manufacturer#3|Brand#31|SMALL POLISHED BRASS|28|SM BAG|1882.98|boost blithely\n983|violet mint moccasin black olive|Manufacturer#4|Brand#43|ECONOMY POLISHED TIN|39|MED BOX|1883.98| pack\n984|tan frosted antique midnight thistle|Manufacturer#2|Brand#22|LARGE PLATED TIN|40|SM BAG|1884.98|ly. furiously pe\n985|drab rosy orange peach medium|Manufacturer#5|Brand#53|MEDIUM BURNISHED STEEL|10|JUMBO PKG|1885.98|s beside\n986|forest purple lawn yellow azure|Manufacturer#3|Brand#33|PROMO BRUSHED BRASS|50|JUMBO DRUM|1886.98|ronic foxes\n987|seashell pink linen salmon khaki|Manufacturer#5|Brand#53|PROMO POLISHED BRASS|39|LG BOX|1887.98|furiou\n988|rose saddle linen peach cream|Manufacturer#2|Brand#24|STANDARD BURNISHED COPPER|27|WRAP PKG|1888.98|special requests sleep\n989|misty ivory plum tan steel|Manufacturer#5|Brand#55|LARGE PLATED TIN|9|JUMBO JAR|1889.98|lets. e\n990|azure cornsilk indian floral aquamarine|Manufacturer#1|Brand#14|SMALL BURNISHED COPPER|14|SM BAG|1890.99|quick\n991|indian yellow red lime seashell|Manufacturer#3|Brand#32|MEDIUM POLISHED BRASS|9|SM CASE|1891.99|ickly slowly spe\n992|hot peru beige magenta metallic|Manufacturer#5|Brand#51|PROMO BURNISHED COPPER|39|JUMBO BOX|1892.99| haggle\n993|chiffon papaya lavender tomato spring|Manufacturer#5|Brand#54|MEDIUM BRUSHED NICKEL|2|MED JAR|1893.99|bold pear\n994|almond saddle papaya seashell burlywood|Manufacturer#2|Brand#25|PROMO ANODIZED STEEL|38|JUMBO BOX|1894.99|ost slyly. boldly bo\n995|papaya violet navy khaki sky|Manufacturer#3|Brand#34|ECONOMY BRUSHED TIN|47|MED DRUM|1895.99|ke furiously fluffily \n996|slate pink navajo orange firebrick|Manufacturer#3|Brand#31|STANDARD POLISHED COPPER|13|LG CAN|1896.99|ously\n997|khaki lawn rose drab cornsilk|Manufacturer#5|Brand#51|STANDARD ANODIZED BRASS|22|JUMBO BAG|1897.99|riously final \n998|ivory maroon cream red peru|Manufacturer#5|Brand#53|STANDARD BRUSHED NICKEL|2|MED BOX|1898.99|s. bold requests wake \n999|indian honeydew chartreuse navy cyan|Manufacturer#4|Brand#44|STANDARD PLATED STEEL|16|WRAP CAN|1899.99|ans. slyly expre\n1000|wheat frosted chiffon aquamarine saddle|Manufacturer#2|Brand#24|ECONOMY BRUSHED NICKEL|10|SM DRUM|901.00|g fluf\n"
  },
  {
    "path": "src/test/regress/data/part.more.data",
    "content": "6001|black lavender tomato brown violet|Manufacturer#3|Brand#32|STANDARD BURNISHED STEEL|41|LG BAG|907.00|eposits. \n6002|cyan saddle plum navajo tan|Manufacturer#5|Brand#53|MEDIUM POLISHED BRASS|19|MED BOX|908.00|final pinto beans\n6003|chocolate wheat cornflower spring linen|Manufacturer#4|Brand#42|LARGE PLATED BRASS|50|LG JAR|909.00|structions. fluffily\n6004|white indian burlywood smoke medium|Manufacturer#1|Brand#13|PROMO BURNISHED TIN|19|JUMBO CAN|910.00|about the care\n6005|powder green red peru lavender|Manufacturer#2|Brand#25|PROMO BURNISHED TIN|40|SM CAN|911.00|lar notorni\n6006|maroon green blanched indian ghost|Manufacturer#5|Brand#51|SMALL BRUSHED COPPER|46|LG CASE|912.00|es. slyly re\n6007|steel cornsilk puff navajo drab|Manufacturer#5|Brand#54|PROMO BURNISHED STEEL|37|SM BOX|913.00|t excus\n6008|medium midnight orange papaya brown|Manufacturer#5|Brand#53|PROMO POLISHED STEEL|15|LG BAG|914.00|s unwind unusual\n6009|navy floral puff misty salmon|Manufacturer#5|Brand#54|STANDARD ANODIZED STEEL|27|SM BAG|915.00|arefull\n6010|saddle green salmon red olive|Manufacturer#3|Brand#32|STANDARD ANODIZED COPPER|42|LG JAR|916.01|bold, spec\n6011|turquoise royal papaya azure grey|Manufacturer#1|Brand#15|PROMO BRUSHED NICKEL|50|SM BAG|917.01|ts maintai\n6012|burlywood chocolate peru deep rose|Manufacturer#4|Brand#42|STANDARD BRUSHED COPPER|18|SM CASE|918.01|final deposits doubt \n6013|gainsboro lime rose hot sky|Manufacturer#3|Brand#32|PROMO ANODIZED COPPER|42|JUMBO JAR|919.01|ate slyly silent, iro\n6014|red frosted powder medium sienna|Manufacturer#5|Brand#55|STANDARD ANODIZED NICKEL|46|MED DRUM|920.01|egular packages\n6015|chartreuse chocolate peru smoke lace|Manufacturer#1|Brand#11|MEDIUM BRUSHED NICKEL|50|LG DRUM|921.01|nts. evenly unusual\n6016|red linen orange floral tomato|Manufacturer#1|Brand#14|ECONOMY POLISHED BRASS|41|MED BAG|922.01| pinto be\n6017|firebrick light smoke mint midnight|Manufacturer#3|Brand#32|LARGE BRUSHED NICKEL|44|LG BOX|923.01|ly against the \n6018|lavender plum gainsboro mint salmon|Manufacturer#3|Brand#33|ECONOMY POLISHED BRASS|46|WRAP BAG|924.01|ckly r\n6019|plum rose midnight lawn papaya|Manufacturer#5|Brand#51|PROMO BRUSHED TIN|38|MED CASE|925.01|press dolp\n6020|olive forest purple magenta smoke|Manufacturer#5|Brand#55|STANDARD POLISHED TIN|17|WRAP BOX|926.02|ar packages hagg\n6021|lavender slate sienna blush tomato|Manufacturer#5|Brand#52|LARGE PLATED STEEL|17|LG PACK|927.02|ve furiou\n6022|cream royal white lace rose|Manufacturer#1|Brand#14|PROMO POLISHED COPPER|18|SM DRUM|928.02|ickly\n6023|blush chartreuse gainsboro beige maroon|Manufacturer#1|Brand#15|ECONOMY BURNISHED BRASS|43|SM PKG|929.02|ave packa\n6024|almond blush snow salmon midnight|Manufacturer#5|Brand#55|LARGE BURNISHED COPPER|5|LG CAN|930.02|l accoun\n6025|purple medium light aquamarine dark|Manufacturer#5|Brand#55|MEDIUM PLATED STEEL|34|SM BAG|931.02|ake after t\n6026|coral blush honeydew rose dark|Manufacturer#5|Brand#53|STANDARD BRUSHED STEEL|10|JUMBO BAG|932.02|final\n6027|snow cream salmon misty tan|Manufacturer#3|Brand#32|LARGE BURNISHED NICKEL|6|JUMBO JAR|933.02|al dep\n6028|orchid almond honeydew grey lace|Manufacturer#1|Brand#12|SMALL PLATED NICKEL|38|SM BOX|934.02|ss the pe\n6029|seashell moccasin bisque goldenrod violet|Manufacturer#1|Brand#13|MEDIUM ANODIZED TIN|20|MED CASE|935.02| the special pac\n6030|linen yellow orchid drab bisque|Manufacturer#5|Brand#51|SMALL PLATED STEEL|28|JUMBO DRUM|936.03|ly across\n6031|green drab coral metallic orchid|Manufacturer#1|Brand#14|LARGE POLISHED BRASS|36|MED CAN|937.03|e. regularly ironic \n6032|papaya cream cornsilk khaki navajo|Manufacturer#3|Brand#33|ECONOMY BRUSHED TIN|29|SM PACK|938.03|dolites. evenly ir\n6033|drab green smoke salmon navy|Manufacturer#3|Brand#32|STANDARD BRUSHED STEEL|31|JUMBO DRUM|939.03|he fur\n6034|forest lawn steel smoke dodger|Manufacturer#4|Brand#42|ECONOMY ANODIZED BRASS|44|MED DRUM|940.03|usual packages. depe\n6035|royal orange pink burnished salmon|Manufacturer#1|Brand#11|MEDIUM POLISHED BRASS|39|WRAP CAN|941.03|y thin\n6036|chartreuse antique linen puff medium|Manufacturer#3|Brand#34|PROMO ANODIZED NICKEL|1|WRAP BAG|942.03|slyly afte\n6037|chocolate rose black steel cyan|Manufacturer#1|Brand#13|ECONOMY PLATED STEEL|34|WRAP BAG|943.03|s are speci\n6038|dim almond red burnished lace|Manufacturer#2|Brand#24|MEDIUM ANODIZED TIN|1|WRAP PKG|944.03| deposits.\n6039|lavender grey dark drab purple|Manufacturer#4|Brand#45|STANDARD BURNISHED TIN|14|LG CAN|945.03|ress packages cajole q\n6040|honeydew dark midnight khaki bisque|Manufacturer#4|Brand#42|MEDIUM PLATED NICKEL|29|LG CASE|946.04|l, even asympt\n6041|seashell forest linen brown goldenrod|Manufacturer#2|Brand#21|STANDARD ANODIZED NICKEL|19|MED DRUM|947.04|ecial \n6042|peach chocolate firebrick frosted papaya|Manufacturer#5|Brand#54|LARGE BURNISHED STEEL|48|WRAP PKG|948.04|y quiet theodol\n6043|khaki misty deep almond medium|Manufacturer#4|Brand#41|SMALL ANODIZED TIN|38|SM CASE|949.04|ideas affix\n6044|dodger turquoise midnight papaya seashell|Manufacturer#2|Brand#22|SMALL BURNISHED TIN|4|JUMBO CASE|950.04|ccounts. blith\n6045|turquoise blanched chocolate blush almond|Manufacturer#4|Brand#42|SMALL BRUSHED NICKEL|37|WRAP DRUM|951.04| final theodolites\n6046|dim green wheat light honeydew|Manufacturer#5|Brand#51|ECONOMY BURNISHED BRASS|14|JUMBO PKG|952.04|l packages\n6047|orchid dodger magenta beige snow|Manufacturer#2|Brand#22|MEDIUM POLISHED NICKEL|8|JUMBO BOX|953.04|ut the warthog\n6048|ivory saddle drab steel pink|Manufacturer#1|Brand#12|MEDIUM BURNISHED STEEL|34|SM PACK|954.04|oss the carefully exp\n6049|khaki floral yellow lemon metallic|Manufacturer#5|Brand#55|MEDIUM POLISHED COPPER|17|LG PKG|955.04|uests use \n6050|dark thistle orchid slate brown|Manufacturer#4|Brand#42|ECONOMY BRUSHED BRASS|7|JUMBO BAG|956.05|nding packages \n6051|linen steel brown medium floral|Manufacturer#3|Brand#33|ECONOMY POLISHED STEEL|20|LG PKG|957.05|usual requests. regul\n6052|almond purple burnished dim coral|Manufacturer#2|Brand#21|STANDARD PLATED BRASS|20|SM BOX|958.05| the quickly special \n6053|puff chocolate snow sky pale|Manufacturer#2|Brand#24|PROMO BRUSHED TIN|10|MED CAN|959.05|tes. even, \n6054|aquamarine antique firebrick saddle cornsilk|Manufacturer#2|Brand#24|MEDIUM ANODIZED COPPER|45|WRAP PACK|960.05|ecial\n6055|gainsboro firebrick coral lace hot|Manufacturer#3|Brand#33|SMALL PLATED BRASS|25|MED BOX|961.05|ses are dolphins. qu\n6056|green dark tomato blanched cornflower|Manufacturer#4|Brand#43|PROMO PLATED COPPER|42|WRAP CAN|962.05|s. furiously re\n6057|cornflower lawn royal linen cyan|Manufacturer#4|Brand#44|STANDARD BURNISHED NICKEL|11|SM CASE|963.05|ly pend\n6058|light tomato ivory white medium|Manufacturer#5|Brand#51|LARGE BRUSHED COPPER|21|WRAP CASE|964.05|o the slyly regula\n6059|azure peach peru pale ghost|Manufacturer#3|Brand#33|STANDARD POLISHED NICKEL|15|SM BAG|965.05|sits affix around \n6060|bisque firebrick antique sandy white|Manufacturer#3|Brand#35|ECONOMY POLISHED COPPER|6|JUMBO BOX|966.06|lyly re\n6061|snow coral green midnight floral|Manufacturer#1|Brand#12|SMALL BRUSHED NICKEL|23|MED BOX|967.06|fully iron\n6062|lace pink lavender frosted snow|Manufacturer#5|Brand#54|PROMO BURNISHED BRASS|11|JUMBO DRUM|968.06|al ideas use slyly\n6063|pale plum aquamarine violet cornflower|Manufacturer#2|Brand#23|PROMO ANODIZED TIN|28|SM BAG|969.06|y unus\n6064|dark navajo floral navy chocolate|Manufacturer#5|Brand#55|STANDARD PLATED NICKEL|2|SM BAG|970.06|es-- always regu\n6065|royal sienna khaki frosted dim|Manufacturer#1|Brand#11|STANDARD BURNISHED BRASS|3|WRAP BAG|971.06|ronic Tiresias. e\n6066|brown papaya seashell magenta burnished|Manufacturer#3|Brand#34|SMALL ANODIZED COPPER|24|JUMBO BOX|972.06| haggle caref\n6067|spring sandy blue orange cyan|Manufacturer#4|Brand#43|MEDIUM PLATED BRASS|12|WRAP DRUM|973.06|ly carefully silent\n6068|cyan sienna moccasin grey tan|Manufacturer#4|Brand#44|SMALL BURNISHED TIN|2|LG PKG|974.06|gedly regular platel\n6069|gainsboro lime maroon cornflower olive|Manufacturer#2|Brand#22|STANDARD ANODIZED TIN|32|WRAP PKG|975.06|ding to t\n6070|orange cornsilk khaki salmon snow|Manufacturer#3|Brand#33|MEDIUM POLISHED TIN|35|MED CAN|976.07|s boo\n6071|burlywood pink lime turquoise orchid|Manufacturer#2|Brand#24|PROMO BRUSHED NICKEL|35|JUMBO PACK|977.07| maintain ag\n6072|pink orange cornflower floral light|Manufacturer#4|Brand#44|SMALL BRUSHED NICKEL|46|MED BAG|978.07|sauternes \n6073|cornsilk puff metallic cyan peru|Manufacturer#3|Brand#35|LARGE PLATED STEEL|18|WRAP BOX|979.07|nic instructions d\n6074|puff cornflower bisque black khaki|Manufacturer#3|Brand#35|MEDIUM POLISHED STEEL|44|JUMBO CAN|980.07|equests after t\n6075|wheat mint chartreuse papaya spring|Manufacturer#3|Brand#35|MEDIUM BRUSHED COPPER|17|WRAP BAG|981.07|unusual, regular\n6076|thistle magenta green floral olive|Manufacturer#5|Brand#51|PROMO PLATED NICKEL|41|JUMBO BOX|982.07|ously iron\n6077|peru ivory dim pink blush|Manufacturer#1|Brand#15|LARGE BRUSHED NICKEL|32|LG BAG|983.07|arefully. \n6078|pale navy coral lace peach|Manufacturer#2|Brand#21|STANDARD BURNISHED NICKEL|23|LG PACK|984.07|y bold d\n6079|olive hot magenta maroon thistle|Manufacturer#2|Brand#23|PROMO ANODIZED COPPER|13|MED BOX|985.07|as. express accounts \n6080|coral red dim maroon green|Manufacturer#5|Brand#51|PROMO POLISHED NICKEL|44|WRAP CAN|986.08|al instructions wake\n6081|blush puff midnight wheat navajo|Manufacturer#5|Brand#53|SMALL PLATED TIN|27|WRAP DRUM|987.08|sts. fluffily fi\n6082|bisque blue burnished turquoise steel|Manufacturer#1|Brand#15|LARGE BURNISHED BRASS|34|MED PKG|988.08|lly even courts wake \n6083|cyan slate dim purple sky|Manufacturer#2|Brand#25|LARGE ANODIZED COPPER|25|WRAP BAG|989.08|nal requests along \n6084|gainsboro floral goldenrod rose papaya|Manufacturer#4|Brand#42|SMALL BURNISHED STEEL|42|SM PKG|990.08| dolphi\n6085|blush burnished lime purple mint|Manufacturer#2|Brand#24|ECONOMY POLISHED NICKEL|29|WRAP PKG|991.08|foxes\n6086|misty hot tomato gainsboro azure|Manufacturer#1|Brand#11|SMALL BURNISHED COPPER|30|JUMBO CAN|992.08|ly dogged accounts \n6087|indian cornsilk khaki burlywood pale|Manufacturer#1|Brand#12|MEDIUM POLISHED BRASS|7|SM BAG|993.08|ly. ex\n6088|slate wheat orange orchid chartreuse|Manufacturer#4|Brand#45|SMALL BURNISHED BRASS|8|WRAP PACK|994.08|ffix \n6089|light royal green yellow dark|Manufacturer#5|Brand#52|PROMO BURNISHED STEEL|31|JUMBO BAG|995.08|ing excu\n6090|turquoise thistle cornsilk forest linen|Manufacturer#4|Brand#42|MEDIUM ANODIZED NICKEL|26|LG BAG|996.09| accounts\n6091|wheat violet saddle royal metallic|Manufacturer#5|Brand#51|ECONOMY PLATED COPPER|41|LG PACK|997.09|es. carefully pendin\n6092|forest goldenrod deep maroon misty|Manufacturer#3|Brand#34|PROMO ANODIZED STEEL|22|JUMBO JAR|998.09|ns run f\n6093|bisque antique cream gainsboro navajo|Manufacturer#4|Brand#43|LARGE ANODIZED BRASS|40|JUMBO CASE|999.09|g depende\n6094|bisque frosted drab sky snow|Manufacturer#5|Brand#55|ECONOMY BRUSHED TIN|28|JUMBO BAG|1000.09|ess, pending requests\n6095|green orchid dark misty moccasin|Manufacturer#1|Brand#14|ECONOMY BRUSHED STEEL|30|JUMBO JAR|1001.09|. furiously\n6096|khaki gainsboro black firebrick royal|Manufacturer#3|Brand#31|SMALL BURNISHED BRASS|30|MED CAN|1002.09|nd the blithely \n6097|chocolate violet gainsboro smoke puff|Manufacturer#3|Brand#31|PROMO ANODIZED NICKEL|17|SM CASE|1003.09|cajole blithely \n6098|ghost chocolate steel moccasin goldenrod|Manufacturer#1|Brand#13|MEDIUM BRUSHED NICKEL|26|MED PKG|1004.09|. daringly \n6099|royal green plum honeydew medium|Manufacturer#2|Brand#25|LARGE BURNISHED STEEL|11|LG PACK|1005.09|ng packages hag\n6100|papaya yellow chocolate sky indian|Manufacturer#5|Brand#55|MEDIUM PLATED NICKEL|20|LG BOX|1006.10|ns integrate. regu\n6101|goldenrod rosy metallic plum seashell|Manufacturer#2|Brand#25|PROMO ANODIZED COPPER|34|MED BAG|1007.10|. furiously\n6102|rosy yellow violet sienna snow|Manufacturer#5|Brand#51|ECONOMY ANODIZED BRASS|50|SM BOX|1008.10|ts are. c\n6103|olive mint deep dodger bisque|Manufacturer#4|Brand#44|STANDARD ANODIZED BRASS|8|WRAP PKG|1009.10|leep blith\n6104|aquamarine bisque cornflower cyan pale|Manufacturer#5|Brand#52|SMALL BRUSHED STEEL|37|WRAP JAR|1010.10|gedly a\n6105|goldenrod cornflower light sandy rosy|Manufacturer#4|Brand#42|MEDIUM BRUSHED TIN|14|MED BAG|1011.10|ns. regular ideas \n6106|lace pale dark blush frosted|Manufacturer#3|Brand#35|LARGE ANODIZED COPPER|26|SM BOX|1012.10|ccounts doze careful\n6107|dim chartreuse brown salmon antique|Manufacturer#4|Brand#42|ECONOMY ANODIZED TIN|40|MED BOX|1013.10|are carefully among th\n6108|cream medium rosy navy brown|Manufacturer#5|Brand#54|SMALL PLATED COPPER|42|SM CAN|1014.10| ironic requests inte\n6109|saddle lawn goldenrod yellow dark|Manufacturer#1|Brand#13|STANDARD PLATED COPPER|19|SM PKG|1015.10|y among th\n6110|chiffon snow ivory blanched cream|Manufacturer#1|Brand#12|SMALL BURNISHED NICKEL|42|LG JAR|1016.11|ual requests-- fluffi\n6111|deep yellow dark lawn antique|Manufacturer#5|Brand#52|PROMO BRUSHED TIN|18|SM JAR|1017.11|tain above the \n6112|metallic dark indian brown chartreuse|Manufacturer#4|Brand#44|ECONOMY ANODIZED NICKEL|30|MED PACK|1018.11|equests amo\n6113|peach purple puff beige spring|Manufacturer#4|Brand#45|PROMO PLATED COPPER|10|JUMBO PKG|1019.11|he slyly ironic\n6114|pale powder chartreuse lime moccasin|Manufacturer#4|Brand#41|STANDARD BRUSHED BRASS|41|LG CASE|1020.11|thely s\n6115|chiffon rose plum puff peach|Manufacturer#1|Brand#11|ECONOMY BURNISHED STEEL|33|SM JAR|1021.11|n, unus\n6116|burnished khaki wheat sienna violet|Manufacturer#5|Brand#54|PROMO BRUSHED TIN|43|LG BOX|1022.11|ing acc\n6117|brown dim lace honeydew burlywood|Manufacturer#5|Brand#51|STANDARD PLATED COPPER|13|SM JAR|1023.11|sual instructions. \n6118|rose violet linen burnished frosted|Manufacturer#3|Brand#32|ECONOMY ANODIZED STEEL|37|LG BOX|1024.11|ly acr\n6119|royal almond magenta puff navy|Manufacturer#3|Brand#32|MEDIUM BURNISHED BRASS|36|LG BAG|1025.11|atelets. quickly regul\n6120|peach white puff dim yellow|Manufacturer#2|Brand#23|ECONOMY ANODIZED COPPER|28|MED BAG|1026.12|bold instructions.\n6121|sky papaya bisque gainsboro aquamarine|Manufacturer#1|Brand#13|MEDIUM BRUSHED TIN|29|LG DRUM|1027.12|ly pending pinto b\n6122|tomato drab misty grey medium|Manufacturer#5|Brand#55|ECONOMY PLATED NICKEL|41|JUMBO BAG|1028.12|sublate furi\n6123|cornsilk sandy khaki indian lawn|Manufacturer#3|Brand#33|PROMO POLISHED STEEL|23|JUMBO CAN|1029.12|bove \n6124|red papaya lime cornsilk maroon|Manufacturer#1|Brand#13|SMALL ANODIZED TIN|15|MED DRUM|1030.12|efully fi\n6125|ghost cornflower forest indian antique|Manufacturer#4|Brand#44|ECONOMY BURNISHED BRASS|34|MED BAG|1031.12|ve the express pint\n6126|yellow smoke cyan khaki steel|Manufacturer#5|Brand#55|MEDIUM PLATED NICKEL|20|WRAP BOX|1032.12|of the blithel\n6127|dim lavender thistle magenta sienna|Manufacturer#3|Brand#35|STANDARD BURNISHED TIN|15|WRAP PKG|1033.12| bold deposits ca\n6128|sandy tomato light cream midnight|Manufacturer#2|Brand#24|LARGE POLISHED COPPER|8|SM JAR|1034.12|ans integrate quickl\n6129|grey cyan sky seashell light|Manufacturer#4|Brand#43|LARGE POLISHED TIN|44|MED JAR|1035.12| should boos\n6130|coral azure chartreuse tomato turquoise|Manufacturer#3|Brand#31|MEDIUM BURNISHED STEEL|9|WRAP PACK|1036.13|ggle furiousl\n6131|coral brown forest puff lemon|Manufacturer#1|Brand#11|ECONOMY POLISHED NICKEL|1|WRAP BAG|1037.13|es must nag across the\n6132|spring lime beige chiffon ghost|Manufacturer#1|Brand#13|MEDIUM PLATED COPPER|41|SM DRUM|1038.13|eas among the slyl\n6133|turquoise goldenrod coral hot chartreuse|Manufacturer#3|Brand#32|STANDARD ANODIZED NICKEL|32|LG BOX|1039.13|ully special\n6134|brown steel magenta blush sky|Manufacturer#4|Brand#42|LARGE BURNISHED NICKEL|18|MED CAN|1040.13|uests dazzle\n6135|cream yellow puff lavender almond|Manufacturer#4|Brand#44|LARGE BURNISHED BRASS|48|JUMBO JAR|1041.13|riously regular \n6136|slate peach moccasin mint forest|Manufacturer#4|Brand#41|ECONOMY POLISHED COPPER|16|LG BAG|1042.13|ar pinto \n6137|snow misty honeydew thistle ivory|Manufacturer#1|Brand#11|ECONOMY PLATED COPPER|19|JUMBO DRUM|1043.13|riously \n6138|forest black deep mint navy|Manufacturer#1|Brand#11|MEDIUM BRUSHED COPPER|8|SM JAR|1044.13|ges play after t\n6139|rose lavender snow forest cream|Manufacturer#4|Brand#45|ECONOMY BRUSHED STEEL|4|JUMBO CASE|1045.13|es. asympt\n6140|almond medium midnight cornflower beige|Manufacturer#2|Brand#24|SMALL PLATED NICKEL|6|MED CASE|1046.14|equests \n6141|purple honeydew midnight lavender red|Manufacturer#3|Brand#35|STANDARD POLISHED TIN|32|SM BOX|1047.14|ole carefully speci\n6142|drab deep ivory ghost lace|Manufacturer#2|Brand#23|SMALL BURNISHED NICKEL|30|MED JAR|1048.14|endencies sleep furio\n6143|lawn peru medium maroon blue|Manufacturer#1|Brand#12|ECONOMY ANODIZED BRASS|8|LG BAG|1049.14|equests. fluffily\n6144|burlywood grey sky sienna gainsboro|Manufacturer#1|Brand#12|STANDARD BURNISHED STEEL|33|WRAP DRUM|1050.14|erns are. sl\n6145|dodger peru metallic ghost cornsilk|Manufacturer#1|Brand#15|STANDARD PLATED BRASS|30|WRAP CASE|1051.14|etly ironic accounts.\n6146|pink powder white firebrick smoke|Manufacturer#3|Brand#34|SMALL BURNISHED NICKEL|37|JUMBO CASE|1052.14|rhorses wake. caref\n6147|yellow bisque cream light medium|Manufacturer#5|Brand#54|ECONOMY POLISHED BRASS|34|MED BOX|1053.14|ously r\n6148|sienna spring turquoise almond orange|Manufacturer#3|Brand#34|PROMO BRUSHED STEEL|50|LG PACK|1054.14|y silent \n6149|deep papaya blanched honeydew snow|Manufacturer#3|Brand#35|ECONOMY BRUSHED TIN|50|MED CAN|1055.14|coys.\n6150|orange powder tomato lemon brown|Manufacturer#1|Brand#11|STANDARD ANODIZED TIN|26|SM PKG|1056.15| blithe do\n6151|floral ivory cyan sienna pale|Manufacturer#4|Brand#42|LARGE BRUSHED NICKEL|7|JUMBO CASE|1057.15|hins should cajole.\n6152|burnished chiffon cornsilk linen orange|Manufacturer#3|Brand#32|PROMO BURNISHED TIN|42|WRAP PKG|1058.15|ly. unusua\n6153|almond orange indian ghost chocolate|Manufacturer#2|Brand#23|MEDIUM BURNISHED COPPER|25|MED BAG|1059.15|instr\n6154|lavender metallic black chiffon orchid|Manufacturer#5|Brand#54|PROMO PLATED BRASS|1|LG DRUM|1060.15|ss, express accoun\n6155|cyan orange yellow papaya moccasin|Manufacturer#5|Brand#52|PROMO ANODIZED COPPER|2|JUMBO CAN|1061.15|es affi\n6156|red maroon lavender burnished brown|Manufacturer#3|Brand#34|PROMO BRUSHED STEEL|36|SM PKG|1062.15|nal instructions \n6157|blanched ghost deep sky red|Manufacturer#2|Brand#21|MEDIUM ANODIZED NICKEL|38|JUMBO CASE|1063.15|s wake \n6158|navajo peach pink orchid midnight|Manufacturer#3|Brand#34|STANDARD POLISHED STEEL|13|WRAP PACK|1064.15|lly b\n6159|orchid salmon tomato honeydew indian|Manufacturer#3|Brand#34|LARGE POLISHED STEEL|15|JUMBO DRUM|1065.15|cording to the final, \n6160|navy navajo gainsboro chocolate pale|Manufacturer#2|Brand#22|SMALL POLISHED TIN|40|LG BAG|1066.16|ithely ironic \n6161|smoke medium green gainsboro violet|Manufacturer#2|Brand#22|STANDARD PLATED STEEL|12|MED JAR|1067.16| fluffily. slyly \n6162|seashell dim snow saddle grey|Manufacturer#3|Brand#32|STANDARD ANODIZED COPPER|16|JUMBO BAG|1068.16|hely slyly regu\n6163|linen ghost dodger drab burlywood|Manufacturer#1|Brand#11|MEDIUM BURNISHED TIN|1|WRAP BOX|1069.16|t, final foxes cajo\n6164|white drab saddle snow purple|Manufacturer#1|Brand#14|PROMO BRUSHED TIN|8|JUMBO BAG|1070.16|sly slow pearls wake \n6165|frosted moccasin red orchid khaki|Manufacturer#1|Brand#12|SMALL ANODIZED BRASS|44|JUMBO JAR|1071.16|carefully b\n6166|indian orange orchid turquoise blush|Manufacturer#4|Brand#45|MEDIUM PLATED NICKEL|25|WRAP PACK|1072.16|ven pinto b\n6167|lawn seashell cornflower coral indian|Manufacturer#5|Brand#54|SMALL PLATED COPPER|16|LG BOX|1073.16|packages mold slyly r\n6168|midnight plum blue spring floral|Manufacturer#1|Brand#14|STANDARD BURNISHED BRASS|12|JUMBO BOX|1074.16|ideas affix\n6169|dim violet sienna bisque saddle|Manufacturer#4|Brand#43|STANDARD PLATED BRASS|26|LG PKG|1075.16|even accounts boost. f\n6170|honeydew yellow deep blanched smoke|Manufacturer#3|Brand#31|LARGE BRUSHED BRASS|5|LG DRUM|1076.17| fluffily ab\n6171|peru light sandy sky antique|Manufacturer#4|Brand#42|ECONOMY ANODIZED STEEL|48|JUMBO JAR|1077.17| along the c\n6172|khaki rose dodger metallic orange|Manufacturer#5|Brand#54|PROMO BRUSHED COPPER|34|JUMBO CAN|1078.17|ests integrate\n6173|rosy mint cream pale lavender|Manufacturer#4|Brand#43|SMALL PLATED STEEL|10|WRAP BOX|1079.17|ang slyly of\n6174|dark seashell spring lavender sienna|Manufacturer#2|Brand#25|SMALL BRUSHED STEEL|8|JUMBO DRUM|1080.17|onic, ironic requests \n6175|plum olive papaya purple orange|Manufacturer#1|Brand#15|STANDARD POLISHED TIN|43|SM JAR|1081.17|y against the slyly p\n6176|tomato cyan chartreuse sky navy|Manufacturer#5|Brand#55|SMALL BRUSHED STEEL|47|SM CAN|1082.17|ully care\n6177|ghost peach turquoise goldenrod spring|Manufacturer#5|Brand#51|ECONOMY BURNISHED COPPER|45|SM CASE|1083.17|ids integrate sil\n6178|blush aquamarine black moccasin slate|Manufacturer#3|Brand#33|STANDARD POLISHED COPPER|46|JUMBO PKG|1084.17|ial instructions\n6179|beige dark peru moccasin brown|Manufacturer#3|Brand#33|LARGE ANODIZED STEEL|3|LG PKG|1085.17|nts of the unu\n6180|mint gainsboro powder maroon royal|Manufacturer#2|Brand#21|MEDIUM POLISHED BRASS|14|LG BOX|1086.18|e carefully unusual th\n6181|brown mint drab lemon orange|Manufacturer#2|Brand#23|ECONOMY POLISHED COPPER|8|WRAP BAG|1087.18|ans cajole.\n6182|burlywood light dodger honeydew cyan|Manufacturer#3|Brand#34|MEDIUM BRUSHED NICKEL|5|LG BOX|1088.18|ly inside the pla\n6183|sienna sandy aquamarine cornsilk deep|Manufacturer#5|Brand#52|SMALL BRUSHED STEEL|39|WRAP BAG|1089.18|ly ironic accounts int\n6184|dark orchid blush goldenrod khaki|Manufacturer#5|Brand#55|LARGE PLATED BRASS|15|MED JAR|1090.18|fully final \n6185|light snow tan cornsilk misty|Manufacturer#4|Brand#41|STANDARD POLISHED COPPER|39|LG BOX|1091.18|ts bo\n6186|antique burnished moccasin cornflower light|Manufacturer#1|Brand#13|LARGE BRUSHED NICKEL|29|LG JAR|1092.18| careful\n6187|black burnished cornflower light saddle|Manufacturer#1|Brand#11|LARGE PLATED STEEL|5|JUMBO BAG|1093.18|sits.\n6188|indian steel magenta misty olive|Manufacturer#2|Brand#23|SMALL BRUSHED STEEL|13|MED CASE|1094.18| beans boost. r\n6189|chocolate midnight deep papaya salmon|Manufacturer#5|Brand#55|SMALL BURNISHED COPPER|29|JUMBO CASE|1095.18|bold foxes wake\n6190|chiffon slate navy orchid almond|Manufacturer#4|Brand#43|PROMO BURNISHED COPPER|4|WRAP BOX|1096.19|e fluffily a\n6191|papaya cyan purple aquamarine ghost|Manufacturer#4|Brand#43|STANDARD BURNISHED STEEL|4|SM BAG|1097.19|ts. idl\n6192|frosted medium mint light drab|Manufacturer#5|Brand#52|PROMO POLISHED BRASS|1|MED PACK|1098.19|s. express pl\n6193|aquamarine thistle green almond spring|Manufacturer#4|Brand#45|MEDIUM POLISHED COPPER|38|MED BOX|1099.19|gside of the ruthless,\n6194|gainsboro violet pink blanched royal|Manufacturer#1|Brand#11|LARGE BURNISHED COPPER|24|WRAP CAN|1100.19| foxes cajole. s\n6195|violet tan salmon moccasin linen|Manufacturer#4|Brand#45|LARGE ANODIZED NICKEL|10|SM JAR|1101.19|ular theodolites. r\n6196|cream maroon metallic orange peru|Manufacturer#4|Brand#43|ECONOMY POLISHED TIN|48|MED JAR|1102.19|d, unu\n6197|violet saddle cyan orange ivory|Manufacturer#4|Brand#44|SMALL POLISHED STEEL|27|MED JAR|1103.19|gular pinto beans nag \n6198|saddle cyan mint linen turquoise|Manufacturer#3|Brand#32|LARGE POLISHED NICKEL|33|JUMBO CASE|1104.19|accounts across t\n6199|chartreuse snow saddle ghost medium|Manufacturer#1|Brand#12|LARGE BURNISHED STEEL|18|MED JAR|1105.19|r courts. sl\n6200|sky indian lavender coral midnight|Manufacturer#4|Brand#41|ECONOMY BURNISHED COPPER|25|JUMBO BAG|1106.20|ccounts are deposits. \n6201|orchid sky frosted yellow lemon|Manufacturer#2|Brand#25|LARGE ANODIZED STEEL|46|SM CAN|1107.20|ole carefully sile\n6202|cornsilk metallic sky puff papaya|Manufacturer#5|Brand#53|PROMO BRUSHED BRASS|45|WRAP PACK|1108.20|courts \n6203|navy plum linen gainsboro midnight|Manufacturer#5|Brand#54|MEDIUM BRUSHED COPPER|50|WRAP DRUM|1109.20| tithes. furiously ev\n6204|hot gainsboro beige rosy chiffon|Manufacturer#5|Brand#51|SMALL PLATED COPPER|40|LG CASE|1110.20|ecial depos\n6205|antique gainsboro floral puff blush|Manufacturer#1|Brand#13|LARGE ANODIZED BRASS|16|SM PKG|1111.20|ar requests sleep abov\n6206|salmon sandy floral khaki mint|Manufacturer#3|Brand#31|SMALL ANODIZED BRASS|35|LG JAR|1112.20|refully final\n6207|antique goldenrod rosy metallic cornsilk|Manufacturer#4|Brand#42|STANDARD POLISHED NICKEL|45|MED PKG|1113.20|nal packa\n6208|bisque light saddle black sandy|Manufacturer#1|Brand#11|PROMO BRUSHED TIN|17|SM CAN|1114.20|y unusual deposits a\n6209|maroon sienna peach tomato deep|Manufacturer#2|Brand#25|MEDIUM PLATED BRASS|47|JUMBO DRUM|1115.20|ial theodoli\n6210|lime lavender grey chiffon burlywood|Manufacturer#5|Brand#51|MEDIUM ANODIZED TIN|9|MED BAG|1116.21|ons lose \n6211|orange cyan light beige chiffon|Manufacturer#4|Brand#42|STANDARD POLISHED NICKEL|2|SM JAR|1117.21|ss asymptot\n6212|lace medium burnished peach tan|Manufacturer#2|Brand#25|LARGE POLISHED BRASS|9|SM BAG|1118.21|lithely sly\n6213|steel snow slate papaya dark|Manufacturer#3|Brand#35|MEDIUM ANODIZED BRASS|15|LG BAG|1119.21|across the furiously \n6214|floral spring light firebrick blush|Manufacturer#5|Brand#55|MEDIUM PLATED NICKEL|42|MED CASE|1120.21|ideas. accou\n6215|midnight cornflower cornsilk brown antique|Manufacturer#3|Brand#35|STANDARD POLISHED TIN|28|LG JAR|1121.21|e fluffi\n6216|forest beige red blush deep|Manufacturer#5|Brand#51|MEDIUM BRUSHED BRASS|47|MED JAR|1122.21|ly about the furiou\n6217|aquamarine peru chartreuse green pink|Manufacturer#4|Brand#43|PROMO POLISHED STEEL|36|LG JAR|1123.21|egular pinto beans caj\n6218|powder coral olive maroon lace|Manufacturer#3|Brand#33|STANDARD ANODIZED NICKEL|8|WRAP BOX|1124.21|ounts must\n6219|honeydew wheat chartreuse drab sky|Manufacturer#4|Brand#44|ECONOMY ANODIZED NICKEL|37|LG PKG|1125.21|nic de\n6220|purple light misty frosted puff|Manufacturer#5|Brand#54|LARGE PLATED NICKEL|18|SM BAG|1126.22|ly iron\n6221|chartreuse rosy peach tan goldenrod|Manufacturer#4|Brand#41|STANDARD ANODIZED NICKEL|42|MED PKG|1127.22|lly final idea\n6222|yellow lemon blush beige light|Manufacturer#2|Brand#25|SMALL POLISHED TIN|16|LG JAR|1128.22| quickly expre\n6223|bisque sienna drab yellow almond|Manufacturer#3|Brand#32|PROMO POLISHED COPPER|40|SM BAG|1129.22|lly ironic th\n6224|sky dim orchid snow pink|Manufacturer#5|Brand#53|LARGE BRUSHED NICKEL|23|LG PKG|1130.22|realms nag carefu\n6225|red cyan green dodger burnished|Manufacturer#5|Brand#54|LARGE BRUSHED STEEL|9|JUMBO BAG|1131.22|usly unusual \n6226|drab black tomato pink almond|Manufacturer#3|Brand#34|LARGE ANODIZED TIN|18|JUMBO CAN|1132.22|ls. foxes i\n6227|sienna firebrick misty linen sandy|Manufacturer#4|Brand#42|ECONOMY POLISHED TIN|40|LG CASE|1133.22|lly regular theodolit\n6228|frosted burlywood dodger red aquamarine|Manufacturer#1|Brand#13|PROMO PLATED STEEL|16|LG BOX|1134.22|d excuses c\n6229|bisque white khaki honeydew azure|Manufacturer#4|Brand#43|PROMO BRUSHED BRASS|29|WRAP PKG|1135.22|lar fo\n6230|powder navajo linen salmon turquoise|Manufacturer#2|Brand#22|SMALL POLISHED COPPER|35|SM PACK|1136.23|ickly express i\n6231|green dodger almond gainsboro indian|Manufacturer#2|Brand#21|PROMO ANODIZED STEEL|20|MED PACK|1137.23| even accounts m\n6232|blanched linen cream almond orange|Manufacturer#1|Brand#11|MEDIUM ANODIZED BRASS|30|LG JAR|1138.23|unusual, regular acco\n6233|blush floral bisque dodger drab|Manufacturer#3|Brand#33|ECONOMY BURNISHED BRASS|49|JUMBO BOX|1139.23| the final foxes s\n6234|green steel grey olive thistle|Manufacturer#4|Brand#45|STANDARD BURNISHED BRASS|50|LG PACK|1140.23|phins wake caref\n6235|white tomato goldenrod wheat grey|Manufacturer#2|Brand#25|SMALL PLATED TIN|50|WRAP PACK|1141.23|packages boost\n6236|misty lace metallic ivory pink|Manufacturer#4|Brand#45|ECONOMY BRUSHED NICKEL|41|MED PKG|1142.23|onic grouches detect\n6237|firebrick white papaya sandy magenta|Manufacturer#1|Brand#14|ECONOMY PLATED TIN|15|SM PKG|1143.23|y after the\n6238|powder lace deep lime steel|Manufacturer#1|Brand#15|LARGE ANODIZED TIN|30|MED PACK|1144.23|under the final cour\n6239|chocolate spring wheat lawn tan|Manufacturer#5|Brand#51|LARGE BURNISHED STEEL|8|SM JAR|1145.23|y regular multipliers\n6240|chartreuse grey thistle azure moccasin|Manufacturer#2|Brand#22|MEDIUM ANODIZED STEEL|34|JUMBO CAN|1146.24|y eve\n6241|misty cornflower plum slate medium|Manufacturer#1|Brand#14|SMALL ANODIZED TIN|23|WRAP JAR|1147.24|ptotes across the eve\n6242|linen powder brown lace red|Manufacturer#2|Brand#22|PROMO POLISHED STEEL|14|MED BAG|1148.24|ly regular the\n6243|royal blush forest papaya navajo|Manufacturer#5|Brand#53|SMALL POLISHED TIN|1|SM CASE|1149.24|he express, reg\n6244|azure plum magenta rosy sandy|Manufacturer#5|Brand#55|SMALL ANODIZED TIN|35|LG CAN|1150.24|lly express packages\n6245|almond burnished beige peru brown|Manufacturer#1|Brand#12|STANDARD PLATED BRASS|38|WRAP PKG|1151.24|al platele\n6246|aquamarine azure smoke lime rose|Manufacturer#4|Brand#44|PROMO PLATED BRASS|47|MED BOX|1152.24|y iro\n6247|rose yellow chiffon lemon thistle|Manufacturer#3|Brand#33|LARGE PLATED BRASS|18|MED BOX|1153.24|ong the unusual acco\n6248|orange goldenrod chocolate papaya smoke|Manufacturer#3|Brand#35|SMALL POLISHED NICKEL|29|MED PKG|1154.24|efully stealthy req\n6249|metallic chiffon ghost goldenrod salmon|Manufacturer#4|Brand#42|MEDIUM BRUSHED BRASS|25|JUMBO BOX|1155.24|e unusua\n6250|navajo lace slate steel purple|Manufacturer#3|Brand#34|PROMO BURNISHED NICKEL|2|MED DRUM|1156.25| requests. blithel\n6251|lemon gainsboro khaki lavender ivory|Manufacturer#3|Brand#34|PROMO POLISHED BRASS|40|SM BOX|1157.25|egular, unusual re\n6252|salmon peach chocolate sky blush|Manufacturer#2|Brand#23|MEDIUM ANODIZED STEEL|45|SM CAN|1158.25|ong the deposits.\n6253|seashell navajo snow sky grey|Manufacturer#2|Brand#21|STANDARD BRUSHED STEEL|18|LG CASE|1159.25|uffily ironic \n6254|dark tan beige black moccasin|Manufacturer#3|Brand#32|SMALL BRUSHED BRASS|17|WRAP DRUM|1160.25|ular acc\n6255|misty olive saddle peach navajo|Manufacturer#5|Brand#55|LARGE BURNISHED BRASS|38|LG PKG|1161.25|ymptotes about the fr\n6256|steel lace lavender hot tomato|Manufacturer#3|Brand#33|ECONOMY ANODIZED STEEL|21|LG PACK|1162.25|sly pending gifts ha\n6257|tan smoke azure rosy firebrick|Manufacturer#2|Brand#25|ECONOMY ANODIZED NICKEL|22|SM BOX|1163.25|wake. ironic pearls i\n6258|red plum tan snow navajo|Manufacturer#5|Brand#55|ECONOMY PLATED STEEL|48|JUMBO BAG|1164.25|eposits. ironic, \n6259|chartreuse cream saddle hot spring|Manufacturer#3|Brand#35|STANDARD ANODIZED TIN|33|WRAP PKG|1165.25|ly ironic ideas will \n6260|dark cornsilk thistle cornflower saddle|Manufacturer#1|Brand#11|LARGE PLATED STEEL|23|SM PACK|1166.26|ss requests sleep fl\n6261|khaki pale blush turquoise mint|Manufacturer#2|Brand#25|ECONOMY BURNISHED COPPER|11|MED PACK|1167.26|ns wake regu\n6262|goldenrod aquamarine forest beige purple|Manufacturer#1|Brand#11|MEDIUM POLISHED STEEL|28|WRAP PKG|1168.26|le. pinto beans d\n6263|saddle cornsilk aquamarine coral magenta|Manufacturer#5|Brand#52|STANDARD BRUSHED COPPER|14|MED PACK|1169.26|s haggle final pinto \n6264|khaki ghost azure antique red|Manufacturer#2|Brand#21|ECONOMY POLISHED TIN|1|WRAP PKG|1170.26|heodolite\n6265|lawn lemon indian thistle firebrick|Manufacturer#1|Brand#12|PROMO PLATED COPPER|27|SM PACK|1171.26|ons are careful\n6266|powder lace indian rose brown|Manufacturer#1|Brand#13|ECONOMY BURNISHED BRASS|38|SM CAN|1172.26|iously. final instr\n6267|azure sky peach slate frosted|Manufacturer#3|Brand#35|ECONOMY POLISHED STEEL|13|WRAP DRUM|1173.26|. carefully b\n6268|spring puff coral sandy drab|Manufacturer#1|Brand#11|MEDIUM ANODIZED TIN|17|LG CASE|1174.26|es. fi\n6269|navajo saddle thistle papaya black|Manufacturer#3|Brand#31|LARGE ANODIZED NICKEL|41|LG PKG|1175.26|g the\n6270|wheat coral chiffon puff blanched|Manufacturer#3|Brand#34|STANDARD BURNISHED STEEL|28|LG BOX|1176.27|l deposits cajole ca\n6271|azure sandy blue dodger honeydew|Manufacturer#5|Brand#52|LARGE BURNISHED NICKEL|42|JUMBO JAR|1177.27|onic platelets above \n6272|blue floral forest steel salmon|Manufacturer#3|Brand#32|SMALL BRUSHED COPPER|20|JUMBO DRUM|1178.27|among the carefully re\n6273|ghost saddle black seashell moccasin|Manufacturer#5|Brand#52|ECONOMY PLATED TIN|26|WRAP BAG|1179.27|inal \n6274|lace azure blush wheat frosted|Manufacturer#2|Brand#24|MEDIUM BURNISHED STEEL|18|WRAP DRUM|1180.27|o the even grouches \n6275|gainsboro ghost tan rosy ivory|Manufacturer#1|Brand#13|MEDIUM PLATED STEEL|10|SM DRUM|1181.27|foxes. f\n6276|khaki slate red frosted snow|Manufacturer#5|Brand#53|SMALL ANODIZED TIN|47|WRAP JAR|1182.27|sts dazzle blit\n6277|black forest orange pink medium|Manufacturer#1|Brand#11|SMALL POLISHED TIN|18|LG BAG|1183.27|ily fi\n6278|saddle navy orchid forest olive|Manufacturer#5|Brand#54|STANDARD PLATED BRASS|23|WRAP PKG|1184.27|nts. sl\n6279|steel ivory blush chiffon cornsilk|Manufacturer#4|Brand#41|SMALL BURNISHED STEEL|15|JUMBO PACK|1185.27|elets\n6280|azure antique thistle dark pink|Manufacturer#1|Brand#11|LARGE ANODIZED COPPER|1|MED CASE|1186.28|he regular, regu\n6281|tomato honeydew lawn black orange|Manufacturer#3|Brand#33|LARGE BRUSHED BRASS|4|JUMBO PKG|1187.28|deposits haggle \n6282|navajo linen dodger honeydew misty|Manufacturer#5|Brand#51|MEDIUM BRUSHED STEEL|3|LG PKG|1188.28|y. even req\n6283|puff dim sandy chocolate snow|Manufacturer#5|Brand#52|PROMO ANODIZED NICKEL|50|WRAP CASE|1189.28|ecial orbits bo\n6284|maroon spring dodger black blush|Manufacturer#3|Brand#33|MEDIUM PLATED BRASS|38|LG CAN|1190.28|deposits\n6285|steel plum mint purple green|Manufacturer#2|Brand#23|STANDARD PLATED STEEL|1|MED PKG|1191.28|nder \n6286|wheat pale thistle chartreuse brown|Manufacturer#3|Brand#32|PROMO BRUSHED BRASS|21|JUMBO BOX|1192.28|s. sl\n6287|frosted peru bisque powder hot|Manufacturer#5|Brand#55|SMALL PLATED COPPER|49|WRAP DRUM|1193.28|anent deposits haggle \n6288|yellow lawn pink peach mint|Manufacturer#2|Brand#21|PROMO BRUSHED TIN|27|MED DRUM|1194.28|the slyly pending the\n6289|beige aquamarine dodger chiffon black|Manufacturer#4|Brand#45|MEDIUM BURNISHED BRASS|38|SM BAG|1195.28|s. final, even requ\n6290|orange black tomato powder purple|Manufacturer#2|Brand#24|LARGE ANODIZED STEEL|44|WRAP CASE|1196.29|ts boost car\n6291|burnished powder chiffon orange wheat|Manufacturer#3|Brand#34|MEDIUM BURNISHED NICKEL|39|WRAP CAN|1197.29|eaves sle\n6292|bisque brown moccasin snow maroon|Manufacturer#4|Brand#44|PROMO PLATED BRASS|28|MED DRUM|1198.29|ugouts. final accounts\n6293|floral azure brown blue tomato|Manufacturer#5|Brand#54|PROMO BRUSHED COPPER|28|MED CASE|1199.29|dependencies. even\n6294|metallic magenta aquamarine midnight linen|Manufacturer#4|Brand#44|MEDIUM POLISHED TIN|44|SM BOX|1200.29|excuses above the \n6295|blanched cyan turquoise saddle white|Manufacturer#4|Brand#43|LARGE BRUSHED BRASS|40|SM BAG|1201.29|kly fluffily unusual \n6296|coral metallic yellow plum goldenrod|Manufacturer#1|Brand#13|PROMO BRUSHED NICKEL|47|MED BOX|1202.29|cial deposits d\n6297|steel burnished dodger cornflower coral|Manufacturer#4|Brand#45|ECONOMY POLISHED TIN|2|JUMBO DRUM|1203.29|ly unusual theodol\n6298|cornsilk sky violet green burnished|Manufacturer#3|Brand#35|MEDIUM POLISHED NICKEL|47|SM BAG|1204.29|tes. furiously pend\n6299|antique wheat blue seashell hot|Manufacturer#3|Brand#34|PROMO BRUSHED STEEL|43|JUMBO JAR|1205.29|totes. regularly\n6300|navy black cornflower white aquamarine|Manufacturer#4|Brand#41|LARGE POLISHED BRASS|10|JUMBO BAG|1206.30|quests haggle \n6301|blush ghost azure peach lemon|Manufacturer#4|Brand#45|LARGE ANODIZED BRASS|8|LG CAN|1207.30| pinto b\n6302|lace frosted mint misty cream|Manufacturer#4|Brand#42|LARGE PLATED TIN|28|LG JAR|1208.30|blithely even instru\n6303|dark powder almond honeydew burnished|Manufacturer#1|Brand#11|SMALL ANODIZED STEEL|46|JUMBO BOX|1209.30|ckages; \n6304|chocolate dodger coral maroon beige|Manufacturer#2|Brand#21|MEDIUM BURNISHED BRASS|8|JUMBO DRUM|1210.30|ackages cajole at th\n6305|white metallic hot antique olive|Manufacturer#3|Brand#33|ECONOMY BURNISHED COPPER|11|MED PKG|1211.30|ing packages\n6306|navajo papaya metallic antique tan|Manufacturer#5|Brand#54|SMALL BRUSHED COPPER|5|WRAP PKG|1212.30|ound the care\n6307|orchid magenta papaya cornflower wheat|Manufacturer#5|Brand#52|PROMO ANODIZED STEEL|25|JUMBO CAN|1213.30|kages. caref\n6308|seashell red medium violet misty|Manufacturer#2|Brand#22|ECONOMY BRUSHED BRASS|14|LG CAN|1214.30|lose requests. s\n6309|hot gainsboro cornflower chartreuse chiffon|Manufacturer#4|Brand#44|PROMO BRUSHED NICKEL|22|SM BOX|1215.30|realms cajole fu\n6310|brown cream olive black goldenrod|Manufacturer#4|Brand#44|STANDARD ANODIZED TIN|12|WRAP PKG|1216.31|bout the\n6311|rose misty ghost burlywood blush|Manufacturer#2|Brand#25|PROMO PLATED NICKEL|6|LG BOX|1217.31|ully even accounts\n6312|burnished smoke metallic mint purple|Manufacturer#2|Brand#24|LARGE BURNISHED COPPER|48|JUMBO CAN|1218.31|ccounts-- sly\n6313|yellow salmon gainsboro beige lace|Manufacturer#1|Brand#14|SMALL BURNISHED STEEL|27|LG CAN|1219.31|gular inst\n6314|white turquoise plum goldenrod smoke|Manufacturer#5|Brand#51|MEDIUM PLATED TIN|3|LG CAN|1220.31|ake after the sly\n6315|purple olive slate pale rosy|Manufacturer#5|Brand#51|LARGE PLATED COPPER|20|JUMBO PKG|1221.31|ecial a\n6316|light peach wheat medium orange|Manufacturer#2|Brand#22|LARGE ANODIZED TIN|29|MED DRUM|1222.31|phs. iro\n6317|orchid snow maroon ghost smoke|Manufacturer#2|Brand#22|PROMO BRUSHED NICKEL|34|WRAP JAR|1223.31|en accounts.\n6318|dim indian coral seashell tomato|Manufacturer#5|Brand#54|ECONOMY BRUSHED COPPER|43|JUMBO BOX|1224.31|dolites. slyly iro\n6319|tan pink salmon lavender spring|Manufacturer#2|Brand#23|PROMO BURNISHED COPPER|29|JUMBO CAN|1225.31|sits after the caref\n6320|smoke orange purple white dark|Manufacturer#2|Brand#24|ECONOMY BRUSHED COPPER|21|MED BAG|1226.32|nly. furiously unu\n6321|green medium goldenrod yellow honeydew|Manufacturer#2|Brand#23|LARGE PLATED COPPER|3|JUMBO PACK|1227.32|ts sleep along\n6322|yellow violet medium firebrick turquoise|Manufacturer#5|Brand#52|ECONOMY POLISHED TIN|16|WRAP CAN|1228.32|y final foxes. bli\n6323|aquamarine linen moccasin coral medium|Manufacturer#3|Brand#35|MEDIUM POLISHED BRASS|13|JUMBO PKG|1229.32|ng to the\n6324|beige dodger burnished misty pink|Manufacturer#3|Brand#35|SMALL BURNISHED TIN|22|MED PKG|1230.32|gularly bo\n6325|puff honeydew blush coral magenta|Manufacturer#1|Brand#11|LARGE ANODIZED STEEL|20|WRAP DRUM|1231.32|even deposits. \n6326|blue olive lemon goldenrod slate|Manufacturer#3|Brand#35|PROMO POLISHED TIN|45|JUMBO CAN|1232.32|ounts dazzle\n6327|forest navy powder black papaya|Manufacturer#1|Brand#11|MEDIUM BRUSHED BRASS|37|MED CAN|1233.32|ncies. even, even p\n6328|chocolate red papaya lavender honeydew|Manufacturer#2|Brand#25|MEDIUM BURNISHED TIN|9|WRAP BOX|1234.32|kages. a\n6329|lawn hot brown sienna tomato|Manufacturer#4|Brand#41|LARGE PLATED COPPER|47|JUMBO BOX|1235.32| special the\n6330|firebrick lime ghost honeydew coral|Manufacturer#1|Brand#14|SMALL ANODIZED STEEL|15|SM PACK|1236.33|ts haggle furiousl\n6331|maroon floral blanched burlywood red|Manufacturer#5|Brand#53|LARGE ANODIZED NICKEL|8|WRAP CAN|1237.33|ackages. bo\n6332|honeydew sienna rose aquamarine moccasin|Manufacturer#4|Brand#44|LARGE PLATED STEEL|27|MED BOX|1238.33|among the regular\n6333|blanched pink black drab wheat|Manufacturer#2|Brand#23|LARGE BURNISHED TIN|28|JUMBO BAG|1239.33|ages. careful\n6334|indian wheat turquoise blanched linen|Manufacturer#4|Brand#42|ECONOMY PLATED STEEL|46|JUMBO DRUM|1240.33|e furiously unus\n6335|lime burnished purple lemon linen|Manufacturer#3|Brand#31|ECONOMY BURNISHED TIN|12|JUMBO JAR|1241.33|ep. permanent, fina\n6336|lemon dodger peach cyan blue|Manufacturer#2|Brand#24|SMALL BRUSHED STEEL|18|SM BAG|1242.33|. express\n6337|steel orchid ivory antique navajo|Manufacturer#5|Brand#55|PROMO BURNISHED STEEL|5|SM DRUM|1243.33|xpress packa\n6338|plum lace almond cyan seashell|Manufacturer#1|Brand#11|ECONOMY PLATED NICKEL|45|MED PKG|1244.33|unts again\n6339|orange navajo coral mint black|Manufacturer#3|Brand#33|ECONOMY ANODIZED BRASS|10|WRAP DRUM|1245.33|eans cajo\n6340|lavender midnight cornflower misty red|Manufacturer#2|Brand#21|LARGE POLISHED BRASS|27|MED DRUM|1246.34|nusual pac\n6341|sky slate ghost smoke bisque|Manufacturer#5|Brand#51|STANDARD BRUSHED TIN|34|WRAP CASE|1247.34|ely thin accoun\n6342|navy puff olive hot violet|Manufacturer#5|Brand#54|ECONOMY BRUSHED BRASS|30|MED CAN|1248.34|slyly unusual asymp\n6343|saddle yellow moccasin tomato plum|Manufacturer#2|Brand#22|SMALL BURNISHED BRASS|6|SM CAN|1249.34|sts. fu\n6344|blue frosted black linen chocolate|Manufacturer#3|Brand#33|SMALL BURNISHED BRASS|19|WRAP PKG|1250.34|s cajole fluffily. \n6345|thistle sienna almond grey lemon|Manufacturer#5|Brand#53|SMALL BURNISHED COPPER|46|LG BAG|1251.34|kly f\n6346|saddle ghost orchid brown blush|Manufacturer#5|Brand#55|STANDARD BRUSHED TIN|45|MED JAR|1252.34|nts haggle. ev\n6347|honeydew cornsilk rosy linen blue|Manufacturer#4|Brand#44|LARGE BURNISHED BRASS|49|LG BOX|1253.34| alongside of \n6348|purple rosy tomato maroon black|Manufacturer#5|Brand#54|ECONOMY POLISHED NICKEL|18|JUMBO PACK|1254.34| blithel\n6349|forest frosted bisque orchid sky|Manufacturer#2|Brand#21|SMALL BURNISHED TIN|19|WRAP BAG|1255.34| the f\n6350|blush papaya salmon sky gainsboro|Manufacturer#5|Brand#54|ECONOMY BRUSHED BRASS|28|JUMBO BAG|1256.35| carefully\n6351|tomato orange honeydew blanched metallic|Manufacturer#1|Brand#11|SMALL BRUSHED BRASS|32|JUMBO BAG|1257.35|refully regular, unu\n6352|rosy plum light tomato wheat|Manufacturer#5|Brand#53|MEDIUM BURNISHED TIN|14|LG DRUM|1258.35|t ideas affix along\n6353|drab linen blue steel floral|Manufacturer#5|Brand#54|LARGE PLATED COPPER|46|MED CAN|1259.35|ain qui\n6354|lawn midnight olive plum blush|Manufacturer#4|Brand#43|SMALL PLATED NICKEL|22|MED PACK|1260.35| final theodolites caj\n6355|burlywood moccasin beige dodger lace|Manufacturer#5|Brand#55|STANDARD BURNISHED COPPER|41|LG PACK|1261.35|reful\n6356|moccasin magenta dim slate orchid|Manufacturer#3|Brand#35|STANDARD POLISHED STEEL|8|MED CASE|1262.35|according to the \n6357|chartreuse snow sandy saddle blush|Manufacturer#1|Brand#12|LARGE BURNISHED NICKEL|14|LG BAG|1263.35|ng foxe\n6358|slate royal ghost cyan dark|Manufacturer#5|Brand#51|ECONOMY BURNISHED TIN|35|JUMBO PACK|1264.35|slyly fluffily \n6359|cream mint yellow grey sandy|Manufacturer#3|Brand#33|PROMO BRUSHED BRASS|10|WRAP CAN|1265.35|es are blithely.\n6360|floral drab salmon purple navy|Manufacturer#3|Brand#32|PROMO BRUSHED COPPER|26|JUMBO PACK|1266.36|al instructions a\n6361|orchid magenta indian cream ivory|Manufacturer#5|Brand#55|STANDARD PLATED TIN|35|MED PACK|1267.36|ts use. slyly exp\n6362|maroon peru navy drab lawn|Manufacturer#2|Brand#21|SMALL PLATED COPPER|7|JUMBO PACK|1268.36|ecial accounts at \n6363|midnight orange maroon forest tomato|Manufacturer#1|Brand#12|ECONOMY BRUSHED TIN|16|LG JAR|1269.36|press the\n6364|yellow almond tan chartreuse cream|Manufacturer#5|Brand#54|ECONOMY ANODIZED COPPER|11|LG BAG|1270.36|longside of th\n6365|maroon coral magenta salmon mint|Manufacturer#5|Brand#55|SMALL BRUSHED TIN|37|SM CASE|1271.36|le fluffily reque\n6366|lawn blue rose gainsboro navajo|Manufacturer#5|Brand#54|STANDARD BURNISHED BRASS|23|SM BAG|1272.36|he furiously d\n6367|dark papaya misty forest violet|Manufacturer#4|Brand#44|LARGE PLATED STEEL|14|MED DRUM|1273.36|sleep after th\n6368|moccasin linen cornflower light chartreuse|Manufacturer#3|Brand#31|MEDIUM POLISHED BRASS|2|SM CASE|1274.36|r the depo\n6369|chocolate lawn navajo violet antique|Manufacturer#4|Brand#44|MEDIUM POLISHED TIN|4|WRAP PKG|1275.36|riously specia\n6370|dodger sienna pink aquamarine sky|Manufacturer#5|Brand#53|LARGE ANODIZED TIN|28|LG PKG|1276.37|ctions\n6371|peach chocolate dark midnight frosted|Manufacturer#2|Brand#24|STANDARD PLATED BRASS|7|LG JAR|1277.37|ely even reques\n6372|plum wheat tomato blue dim|Manufacturer#2|Brand#22|STANDARD PLATED BRASS|5|SM CAN|1278.37|e fluf\n6373|royal gainsboro pale lemon misty|Manufacturer#5|Brand#52|STANDARD ANODIZED BRASS|40|MED DRUM|1279.37| across the slyly ex\n6374|navajo white plum lemon midnight|Manufacturer#5|Brand#53|ECONOMY BRUSHED STEEL|40|MED CAN|1280.37|xpress \n6375|dim peach cornsilk magenta gainsboro|Manufacturer#3|Brand#33|MEDIUM ANODIZED NICKEL|3|LG CAN|1281.37|eodolites. ironic r\n6376|white coral burlywood ivory ghost|Manufacturer#3|Brand#32|LARGE BURNISHED STEEL|22|JUMBO JAR|1282.37|to the busily fi\n6377|magenta black burnished lawn blush|Manufacturer#1|Brand#12|MEDIUM BURNISHED COPPER|38|LG BAG|1283.37|fily eve\n6378|turquoise dim pale red tomato|Manufacturer#5|Brand#52|STANDARD ANODIZED COPPER|21|JUMBO BAG|1284.37|furiously final ide\n6379|almond magenta light violet chiffon|Manufacturer#3|Brand#31|LARGE ANODIZED COPPER|27|JUMBO CAN|1285.37|hout the \n6380|sky saddle azure yellow cornflower|Manufacturer#3|Brand#33|LARGE BRUSHED NICKEL|29|WRAP JAR|1286.38|the quic\n6381|seashell chocolate midnight cyan misty|Manufacturer#2|Brand#25|LARGE BURNISHED BRASS|49|WRAP BOX|1287.38|nag quickly q\n6382|orange peru chartreuse linen honeydew|Manufacturer#4|Brand#42|MEDIUM BRUSHED BRASS|8|SM JAR|1288.38|s lose blithely \n6383|slate light spring magenta snow|Manufacturer#2|Brand#22|MEDIUM POLISHED STEEL|4|WRAP BAG|1289.38|gular asymptotes. \n6384|dark snow maroon spring cornsilk|Manufacturer#5|Brand#51|SMALL ANODIZED BRASS|18|LG PKG|1290.38|unts. blithely dar\n6385|sienna white gainsboro peach aquamarine|Manufacturer#1|Brand#11|LARGE BURNISHED NICKEL|24|WRAP BOX|1291.38|s slee\n6386|navy azure blue chiffon papaya|Manufacturer#2|Brand#24|STANDARD POLISHED NICKEL|18|MED BAG|1292.38|furiously. \n6387|slate firebrick orchid light azure|Manufacturer#4|Brand#45|STANDARD BURNISHED COPPER|25|SM PKG|1293.38|ar instructions. caref\n6388|seashell pale maroon floral coral|Manufacturer#2|Brand#23|PROMO POLISHED TIN|47|SM DRUM|1294.38|onic package\n6389|antique blush green khaki purple|Manufacturer#5|Brand#51|STANDARD PLATED BRASS|12|JUMBO PACK|1295.38|r foxes. blithely b\n6390|bisque pink gainsboro rose lavender|Manufacturer#3|Brand#32|PROMO BRUSHED BRASS|22|MED BOX|1296.39|ctions cajole about\n6391|rosy powder yellow linen cyan|Manufacturer#2|Brand#25|LARGE BURNISHED COPPER|1|LG DRUM|1297.39|unusua\n6392|linen orchid navajo white ivory|Manufacturer#5|Brand#53|ECONOMY POLISHED COPPER|28|MED BAG|1298.39|furiousl\n6393|cornflower dodger khaki rose blush|Manufacturer#4|Brand#42|PROMO ANODIZED TIN|32|JUMBO PACK|1299.39|oxes. regular accou\n6394|salmon pink dim azure tomato|Manufacturer#1|Brand#12|PROMO BRUSHED TIN|26|MED PACK|1300.39|cording to the pe\n6395|hot turquoise rose ghost burnished|Manufacturer#3|Brand#33|LARGE BRUSHED TIN|22|MED BOX|1301.39|quickly i\n6396|dim cornflower frosted pale purple|Manufacturer#3|Brand#34|MEDIUM PLATED BRASS|8|SM BAG|1302.39|t the enticingly fina\n6397|turquoise ivory hot rosy violet|Manufacturer#3|Brand#34|STANDARD PLATED COPPER|9|SM JAR|1303.39|accounts sle\n6398|papaya sandy black forest lace|Manufacturer#1|Brand#13|ECONOMY BURNISHED BRASS|31|SM BOX|1304.39|ve the iro\n6399|papaya saddle midnight sky aquamarine|Manufacturer#4|Brand#42|LARGE BURNISHED COPPER|30|SM JAR|1305.39|nstruction\n6400|royal papaya navajo rose thistle|Manufacturer#4|Brand#42|SMALL ANODIZED COPPER|14|SM DRUM|1306.40|d the ironic instruc\n6401|floral smoke saddle antique mint|Manufacturer#3|Brand#33|SMALL BRUSHED STEEL|11|SM BAG|1307.40|ial dolphins abov\n6402|lawn light puff dark deep|Manufacturer#1|Brand#12|STANDARD POLISHED NICKEL|35|MED JAR|1308.40|he carefully ironi\n6403|chiffon tan navajo grey plum|Manufacturer#2|Brand#21|LARGE ANODIZED BRASS|28|WRAP BOX|1309.40|e blithely. sly\n6404|rosy turquoise saddle ghost lace|Manufacturer#2|Brand#22|PROMO BURNISHED COPPER|29|SM DRUM|1310.40| requ\n6405|wheat metallic sandy ivory spring|Manufacturer#3|Brand#33|LARGE ANODIZED STEEL|24|LG BAG|1311.40| the ironic frays\n6406|dim metallic burnished plum salmon|Manufacturer#2|Brand#25|PROMO ANODIZED BRASS|41|LG CAN|1312.40|furio\n6407|blue red forest azure violet|Manufacturer#2|Brand#25|STANDARD POLISHED TIN|36|LG CASE|1313.40|y regular requests sl\n6408|turquoise frosted tomato navy moccasin|Manufacturer#5|Brand#54|PROMO PLATED TIN|23|JUMBO PKG|1314.40| accounts \n6409|frosted plum dark purple aquamarine|Manufacturer#1|Brand#12|PROMO POLISHED TIN|7|SM CAN|1315.40|osits. regu\n6410|violet misty rosy antique olive|Manufacturer#4|Brand#44|PROMO BURNISHED TIN|12|MED CAN|1316.41|pecial instruc\n6411|lemon deep almond red yellow|Manufacturer#1|Brand#14|STANDARD PLATED TIN|11|WRAP PKG|1317.41| the furiously sp\n6412|misty plum moccasin slate orchid|Manufacturer#1|Brand#11|MEDIUM PLATED COPPER|37|JUMBO CAN|1318.41|its. ev\n6413|peach azure cornsilk chocolate brown|Manufacturer#5|Brand#51|SMALL PLATED TIN|11|SM CASE|1319.41|inal a\n6414|ghost floral khaki aquamarine hot|Manufacturer#4|Brand#43|PROMO BURNISHED TIN|20|WRAP DRUM|1320.41|tructions haggle \n6415|forest saddle royal maroon slate|Manufacturer#5|Brand#55|STANDARD BRUSHED STEEL|28|WRAP BOX|1321.41|pending packag\n6416|firebrick ivory tomato chiffon floral|Manufacturer#3|Brand#31|PROMO PLATED BRASS|22|JUMBO CASE|1322.41|lyly around t\n6417|lace dark thistle medium navajo|Manufacturer#1|Brand#13|PROMO BURNISHED STEEL|23|SM PKG|1323.41|its nod \n6418|rose puff orange moccasin royal|Manufacturer#2|Brand#22|PROMO PLATED NICKEL|6|JUMBO CASE|1324.41|furio\n6419|chartreuse seashell lemon floral pale|Manufacturer#1|Brand#11|STANDARD POLISHED TIN|47|MED CAN|1325.41|s. perman\n6420|azure sky honeydew puff papaya|Manufacturer#2|Brand#24|ECONOMY POLISHED BRASS|36|WRAP CAN|1326.42|reful\n6421|sienna peru lace dodger mint|Manufacturer#2|Brand#23|LARGE POLISHED STEEL|10|MED PKG|1327.42|tructions. dogged\n6422|olive dark black wheat navy|Manufacturer#2|Brand#24|MEDIUM ANODIZED STEEL|39|MED PACK|1328.42| the s\n6423|magenta ghost pale blue honeydew|Manufacturer#3|Brand#33|SMALL PLATED TIN|36|LG BOX|1329.42| even packages hagg\n6424|rosy khaki black burlywood floral|Manufacturer#1|Brand#13|LARGE POLISHED BRASS|17|JUMBO BOX|1330.42| pinto beans h\n6425|dodger lawn honeydew ghost blush|Manufacturer#1|Brand#12|STANDARD POLISHED BRASS|50|LG PKG|1331.42|ackages. blithel\n6426|green steel frosted burlywood puff|Manufacturer#2|Brand#23|LARGE PLATED TIN|44|LG CASE|1332.42|long the carefull\n6427|thistle powder floral bisque sienna|Manufacturer#1|Brand#14|MEDIUM POLISHED BRASS|28|SM JAR|1333.42| the \n6428|chartreuse lime burlywood magenta lawn|Manufacturer#4|Brand#43|SMALL POLISHED NICKEL|38|JUMBO CAN|1334.42|ironic dependenci\n6429|olive tomato rose blue maroon|Manufacturer#2|Brand#22|SMALL ANODIZED TIN|36|JUMBO DRUM|1335.42|ess blithely around t\n6430|turquoise tan indian olive ghost|Manufacturer#2|Brand#22|SMALL PLATED STEEL|3|LG JAR|1336.43| regular asymptotes \n6431|yellow beige pale gainsboro orchid|Manufacturer#4|Brand#45|ECONOMY BRUSHED COPPER|50|WRAP BOX|1337.43|ironic epitaphs. pint\n6432|floral spring snow papaya black|Manufacturer#2|Brand#23|LARGE PLATED TIN|33|SM BOX|1338.43|s. quick, spec\n6433|khaki peru turquoise blanched lemon|Manufacturer#1|Brand#14|ECONOMY BRUSHED BRASS|19|LG DRUM|1339.43|dencies doubt. unusu\n6434|midnight aquamarine chartreuse powder khaki|Manufacturer#3|Brand#35|MEDIUM ANODIZED NICKEL|46|SM BOX|1340.43|e slyly\n6435|frosted wheat royal grey ghost|Manufacturer#5|Brand#51|ECONOMY ANODIZED TIN|12|MED JAR|1341.43|press pack\n6436|navy saddle misty blanched gainsboro|Manufacturer#3|Brand#34|PROMO BURNISHED BRASS|40|MED CAN|1342.43|across\n6437|brown drab mint sky chocolate|Manufacturer#3|Brand#31|PROMO POLISHED BRASS|16|LG CAN|1343.43| special re\n6438|ivory coral misty steel salmon|Manufacturer#5|Brand#52|ECONOMY POLISHED BRASS|23|LG CASE|1344.43|ly express instr\n6439|thistle steel lace chocolate chiffon|Manufacturer#5|Brand#55|SMALL ANODIZED COPPER|41|LG JAR|1345.43|arefully bold\n6440|floral dim almond ghost thistle|Manufacturer#1|Brand#12|SMALL BURNISHED COPPER|32|LG PKG|1346.44|ously pending r\n6441|peach honeydew red blush puff|Manufacturer#1|Brand#13|MEDIUM POLISHED COPPER|16|SM JAR|1347.44|deposi\n6442|magenta ivory orange coral grey|Manufacturer#4|Brand#41|ECONOMY BRUSHED TIN|22|WRAP PACK|1348.44|ckages. ca\n6443|rose misty brown lavender orange|Manufacturer#5|Brand#55|LARGE PLATED BRASS|26|JUMBO JAR|1349.44|ep quietly across\n6444|blanched frosted blush forest burnished|Manufacturer#4|Brand#41|ECONOMY ANODIZED NICKEL|15|SM JAR|1350.44| ironic accounts si\n6445|navy lemon grey sky rosy|Manufacturer#1|Brand#11|LARGE ANODIZED STEEL|9|SM CAN|1351.44|pinto beans haggle c\n6446|almond floral metallic blush azure|Manufacturer#5|Brand#52|PROMO ANODIZED STEEL|29|SM BOX|1352.44|y regula\n6447|olive purple chiffon linen magenta|Manufacturer#2|Brand#21|SMALL PLATED COPPER|48|WRAP BOX|1353.44| sleep. blithely\n6448|tomato chartreuse chocolate seashell moccasin|Manufacturer#1|Brand#12|SMALL ANODIZED NICKEL|17|LG BOX|1354.44| fluffily. regula\n6449|yellow orchid cream puff dark|Manufacturer#3|Brand#34|SMALL BURNISHED STEEL|12|LG PACK|1355.44|special d\n6450|puff light red goldenrod salmon|Manufacturer#5|Brand#55|PROMO ANODIZED BRASS|14|LG PKG|1356.45|equests w\n6451|chartreuse cream deep moccasin hot|Manufacturer#1|Brand#13|LARGE POLISHED COPPER|27|WRAP CASE|1357.45| haggle slyly qui\n6452|red steel magenta frosted firebrick|Manufacturer#2|Brand#22|PROMO ANODIZED STEEL|36|MED DRUM|1358.45|regular reque\n6453|peach wheat sienna dark black|Manufacturer#5|Brand#51|MEDIUM BURNISHED TIN|37|MED JAR|1359.45|y. blithely silen\n6454|wheat olive papaya pale goldenrod|Manufacturer#4|Brand#41|LARGE BRUSHED BRASS|20|MED DRUM|1360.45|ackages. slyl\n6455|rose snow olive grey navajo|Manufacturer#5|Brand#51|LARGE BURNISHED COPPER|14|WRAP JAR|1361.45|nic dep\n6456|lemon almond spring ghost forest|Manufacturer#4|Brand#43|SMALL POLISHED STEEL|22|JUMBO JAR|1362.45|e furiously bold depo\n6457|antique pink yellow salmon coral|Manufacturer#3|Brand#33|LARGE BURNISHED NICKEL|50|MED BAG|1363.45| unusual, \n6458|turquoise wheat honeydew maroon midnight|Manufacturer#2|Brand#25|MEDIUM POLISHED STEEL|11|SM CAN|1364.45|s sleep blithely abov\n6459|navajo almond ghost chartreuse plum|Manufacturer#1|Brand#14|MEDIUM BRUSHED STEEL|14|JUMBO BOX|1365.45|d the ideas nag\n6460|peach rose spring snow moccasin|Manufacturer#4|Brand#44|PROMO ANODIZED STEEL|36|MED DRUM|1366.46| reque\n6461|black navy khaki sandy ivory|Manufacturer#2|Brand#21|MEDIUM BURNISHED BRASS|33|JUMBO PKG|1367.46|ular accounts hag\n6462|papaya lemon lime misty light|Manufacturer#4|Brand#45|MEDIUM BRUSHED NICKEL|16|JUMBO DRUM|1368.46|ns sleep \n6463|linen rose blue white honeydew|Manufacturer#3|Brand#31|PROMO BRUSHED BRASS|11|LG BOX|1369.46| sometime\n6464|blanched spring chiffon lace indian|Manufacturer#3|Brand#31|PROMO PLATED BRASS|20|JUMBO PKG|1370.46|thely ideas.\n6465|metallic cornsilk purple light firebrick|Manufacturer#3|Brand#35|SMALL ANODIZED NICKEL|41|LG JAR|1371.46|he busily regu\n6466|thistle azure orange honeydew ivory|Manufacturer#5|Brand#54|LARGE ANODIZED NICKEL|33|MED DRUM|1372.46|lithely\n6467|forest sandy azure thistle pale|Manufacturer#4|Brand#41|MEDIUM BURNISHED TIN|3|SM BOX|1373.46|lites. caref\n6468|grey honeydew cornsilk light firebrick|Manufacturer#1|Brand#15|SMALL ANODIZED BRASS|1|JUMBO CASE|1374.46| regular a\n6469|turquoise navajo indian olive forest|Manufacturer#1|Brand#14|MEDIUM ANODIZED STEEL|8|WRAP JAR|1375.46|ged req\n6470|cornflower khaki powder blue black|Manufacturer#4|Brand#43|SMALL PLATED COPPER|45|WRAP CAN|1376.47|ts int\n6471|cyan drab almond red burnished|Manufacturer#4|Brand#41|ECONOMY ANODIZED COPPER|40|WRAP PACK|1377.47|g pinto beans dete\n6472|cornflower metallic cream bisque plum|Manufacturer#2|Brand#23|PROMO POLISHED STEEL|36|SM PACK|1378.47|al courts wake fur\n6473|royal linen antique cornsilk cyan|Manufacturer#3|Brand#31|ECONOMY ANODIZED COPPER|35|LG CASE|1379.47| packages wake s\n6474|navy sandy khaki gainsboro tan|Manufacturer#5|Brand#51|MEDIUM ANODIZED BRASS|25|SM BAG|1380.47|nding attainments.\n6475|peru peach navajo white lemon|Manufacturer#4|Brand#41|LARGE BURNISHED COPPER|39|LG BAG|1381.47|special reque\n6476|peru thistle moccasin olive smoke|Manufacturer#4|Brand#42|MEDIUM POLISHED NICKEL|42|JUMBO JAR|1382.47|uriously \n6477|antique black seashell deep burnished|Manufacturer#3|Brand#34|SMALL BURNISHED NICKEL|6|MED BAG|1383.47| careful, ironic \n6478|bisque royal seashell floral coral|Manufacturer#1|Brand#12|ECONOMY ANODIZED TIN|29|MED DRUM|1384.47| deposits. pending, do\n6479|rose maroon powder orange snow|Manufacturer#3|Brand#33|MEDIUM BURNISHED NICKEL|22|WRAP JAR|1385.47| the final, regul\n6480|floral spring beige black cornsilk|Manufacturer#5|Brand#54|STANDARD ANODIZED TIN|3|WRAP BAG|1386.48|re. quickly ir\n6481|green khaki cornsilk misty lavender|Manufacturer#4|Brand#42|SMALL BRUSHED BRASS|26|LG JAR|1387.48|ages sleep flu\n6482|indian sky orchid orange chocolate|Manufacturer#4|Brand#44|MEDIUM POLISHED NICKEL|24|LG CASE|1388.48| ironic asymptotes wak\n6483|almond blush navy mint ghost|Manufacturer#3|Brand#35|PROMO ANODIZED STEEL|5|JUMBO CAN|1389.48|ccounts nag\n6484|smoke drab peach pale misty|Manufacturer#2|Brand#25|PROMO PLATED COPPER|1|WRAP BAG|1390.48|w slyly pendi\n6485|cream saddle slate floral frosted|Manufacturer#2|Brand#24|MEDIUM POLISHED TIN|26|MED BAG|1391.48|. quickl\n6486|frosted indian drab seashell ghost|Manufacturer#2|Brand#23|PROMO BRUSHED TIN|22|WRAP PACK|1392.48|ithely regular dep\n6487|slate dodger linen drab green|Manufacturer#1|Brand#11|ECONOMY ANODIZED TIN|46|SM BOX|1393.48|e even\n6488|puff royal spring firebrick honeydew|Manufacturer#4|Brand#44|ECONOMY BURNISHED BRASS|4|LG BOX|1394.48|atelet\n6489|ivory dodger floral antique plum|Manufacturer#5|Brand#53|STANDARD ANODIZED BRASS|28|LG CASE|1395.48|hes. ironically unusua\n6490|cream gainsboro slate puff spring|Manufacturer#3|Brand#34|SMALL PLATED COPPER|36|MED CASE|1396.49|packages. furiously fi\n6491|blue goldenrod rose lawn lavender|Manufacturer#2|Brand#21|SMALL ANODIZED STEEL|13|SM DRUM|1397.49|ests boost sly\n6492|navy burnished deep cornflower sandy|Manufacturer#5|Brand#55|SMALL POLISHED STEEL|47|WRAP JAR|1398.49|s play carefully \n6493|antique turquoise snow gainsboro slate|Manufacturer#5|Brand#52|SMALL PLATED COPPER|36|MED PACK|1399.49|s. silent, even d\n6494|brown cyan light peach ivory|Manufacturer#1|Brand#12|LARGE BRUSHED BRASS|11|LG BOX|1400.49|packages. bl\n6495|bisque dark orange lawn frosted|Manufacturer#4|Brand#42|LARGE BURNISHED NICKEL|27|SM PACK|1401.49|posits around the\n6496|sienna blue goldenrod bisque magenta|Manufacturer#3|Brand#35|MEDIUM BRUSHED BRASS|2|JUMBO CAN|1402.49|e quick\n6497|lace plum black deep almond|Manufacturer#4|Brand#45|PROMO BURNISHED TIN|46|WRAP CAN|1403.49|ly above the\n6498|grey tomato rosy blanched brown|Manufacturer#1|Brand#14|SMALL POLISHED TIN|26|WRAP PACK|1404.49| packages\n6499|cornflower magenta dodger metallic spring|Manufacturer#2|Brand#23|STANDARD POLISHED BRASS|46|WRAP PKG|1405.49|sts are s\n6500|antique deep almond black aquamarine|Manufacturer#2|Brand#23|ECONOMY ANODIZED STEEL|1|WRAP DRUM|1406.50|detect slyly\n6501|medium snow floral yellow sky|Manufacturer#2|Brand#22|SMALL ANODIZED TIN|46|JUMBO BOX|1407.50|sleep quickly e\n6502|cornflower saddle lemon spring peru|Manufacturer#2|Brand#23|LARGE POLISHED COPPER|6|LG BOX|1408.50|ending, brave\n6503|royal almond papaya gainsboro orchid|Manufacturer#4|Brand#42|SMALL BRUSHED NICKEL|1|MED CASE|1409.50| deposits cajole furio\n6504|orange salmon dark wheat purple|Manufacturer#3|Brand#34|PROMO BRUSHED TIN|48|SM PACK|1410.50|gle slyly ironic, sile\n6505|lavender metallic burlywood almond khaki|Manufacturer#4|Brand#42|ECONOMY ANODIZED STEEL|4|LG JAR|1411.50| special i\n6506|slate green navy burlywood almond|Manufacturer#5|Brand#52|MEDIUM PLATED COPPER|44|LG CAN|1412.50|de of the express\n6507|thistle lemon orchid lace beige|Manufacturer#1|Brand#14|STANDARD PLATED COPPER|47|WRAP CAN|1413.50|uctions haggl\n6508|beige maroon almond misty cornflower|Manufacturer#4|Brand#44|SMALL BURNISHED STEEL|10|SM JAR|1414.50|even accou\n6509|sienna misty thistle medium powder|Manufacturer#3|Brand#32|MEDIUM ANODIZED STEEL|27|LG PKG|1415.50|ackages for t\n6510|indian aquamarine hot yellow drab|Manufacturer#5|Brand#52|MEDIUM BURNISHED BRASS|29|LG JAR|1416.51|sts across the\n6511|burnished hot navy steel peru|Manufacturer#3|Brand#33|STANDARD BURNISHED TIN|39|MED JAR|1417.51|gular excuses. fluf\n6512|light coral wheat powder rosy|Manufacturer#2|Brand#25|STANDARD PLATED BRASS|3|JUMBO CASE|1418.51|efully expres\n6513|moccasin cornsilk misty frosted honeydew|Manufacturer#5|Brand#53|ECONOMY BURNISHED STEEL|21|LG BAG|1419.51| ironic dolphins i\n6514|violet spring powder saddle burnished|Manufacturer#5|Brand#51|SMALL PLATED BRASS|28|JUMBO PACK|1420.51|al excuses ca\n6515|sienna khaki brown linen purple|Manufacturer#2|Brand#21|SMALL BURNISHED BRASS|2|MED CAN|1421.51|accoun\n6516|azure sandy ghost orange antique|Manufacturer#3|Brand#35|ECONOMY BURNISHED TIN|9|LG CASE|1422.51|ar pa\n6517|metallic grey plum olive cyan|Manufacturer#4|Brand#41|SMALL BURNISHED COPPER|21|MED PACK|1423.51|nstructi\n6518|honeydew goldenrod floral white yellow|Manufacturer#3|Brand#31|SMALL BRUSHED NICKEL|16|WRAP CASE|1424.51|according t\n6519|indian seashell cream azure coral|Manufacturer#4|Brand#45|STANDARD ANODIZED NICKEL|17|LG PACK|1425.51|ckly regular \n6520|green honeydew cyan yellow peru|Manufacturer#2|Brand#22|PROMO PLATED STEEL|4|LG DRUM|1426.52|y steal\n6521|misty powder black linen forest|Manufacturer#2|Brand#24|PROMO BURNISHED NICKEL|42|SM CASE|1427.52|osits after the\n6522|lavender almond spring navy blanched|Manufacturer#2|Brand#24|ECONOMY BURNISHED TIN|16|SM PACK|1428.52|s dazzle: careful\n6523|hot maroon lawn moccasin ivory|Manufacturer#1|Brand#11|MEDIUM ANODIZED STEEL|43|MED BOX|1429.52|out the quickly \n6524|powder blue linen brown peru|Manufacturer#1|Brand#15|PROMO BURNISHED TIN|34|MED CASE|1430.52|unts detect quick\n6525|ghost dim antique honeydew dodger|Manufacturer#1|Brand#11|PROMO POLISHED COPPER|11|JUMBO BOX|1431.52|rding t\n6526|forest black azure ghost beige|Manufacturer#4|Brand#41|ECONOMY PLATED COPPER|26|MED PKG|1432.52|accounts wake careful\n6527|lace coral royal deep sienna|Manufacturer#2|Brand#21|ECONOMY PLATED STEEL|33|SM BOX|1433.52|theodolite\n6528|midnight orange floral dim cyan|Manufacturer#5|Brand#51|PROMO ANODIZED COPPER|34|SM DRUM|1434.52|decoys \n6529|sienna turquoise olive dark tomato|Manufacturer#1|Brand#15|SMALL BRUSHED STEEL|7|SM BOX|1435.52|ld accou\n6530|lawn maroon azure rosy blue|Manufacturer#3|Brand#33|ECONOMY ANODIZED COPPER|33|JUMBO CASE|1436.53|ual packages\n6531|floral metallic medium honeydew firebrick|Manufacturer#2|Brand#22|LARGE BURNISHED COPPER|28|WRAP CASE|1437.53|ths after the \n6532|navajo burnished misty blue sandy|Manufacturer#5|Brand#54|SMALL BRUSHED COPPER|20|SM DRUM|1438.53| theodolites detect \n6533|misty slate cream forest olive|Manufacturer#3|Brand#35|SMALL ANODIZED NICKEL|41|WRAP PKG|1439.53|symptotes\n6534|blue metallic midnight forest powder|Manufacturer#3|Brand#32|STANDARD BRUSHED STEEL|11|WRAP JAR|1440.53|reful\n6535|linen thistle sandy brown chocolate|Manufacturer#5|Brand#53|SMALL POLISHED COPPER|8|JUMBO JAR|1441.53|ular packa\n6536|rosy plum violet peru lemon|Manufacturer#2|Brand#22|ECONOMY BRUSHED NICKEL|31|WRAP BAG|1442.53|aggle. reg\n6537|olive cornsilk blanched seashell magenta|Manufacturer#5|Brand#53|PROMO POLISHED NICKEL|20|MED BOX|1443.53|furiously unu\n6538|powder turquoise bisque grey steel|Manufacturer#5|Brand#51|PROMO ANODIZED NICKEL|10|JUMBO BAG|1444.53|hins wake ac\n6539|black olive metallic azure spring|Manufacturer#1|Brand#13|SMALL POLISHED COPPER|29|MED JAR|1445.53|symptotes? quickly\n6540|papaya salmon sienna ivory lime|Manufacturer#3|Brand#32|MEDIUM ANODIZED COPPER|5|JUMBO PACK|1446.54|beans wake re\n6541|metallic thistle plum frosted light|Manufacturer#2|Brand#23|SMALL POLISHED BRASS|49|JUMBO DRUM|1447.54|its haggle along\n6542|violet peach sienna blue rosy|Manufacturer#3|Brand#34|MEDIUM BRUSHED BRASS|38|SM BAG|1448.54|asymptote\n6543|sky red goldenrod bisque tomato|Manufacturer#1|Brand#14|STANDARD POLISHED COPPER|28|JUMBO PACK|1449.54| packages haggle car\n6544|firebrick aquamarine black midnight red|Manufacturer#2|Brand#25|STANDARD BRUSHED TIN|13|SM PKG|1450.54|e slyl\n6545|powder blue orchid slate lemon|Manufacturer#4|Brand#45|SMALL PLATED COPPER|36|LG PACK|1451.54|ording to the slyly \n6546|magenta sandy pale lavender steel|Manufacturer#2|Brand#25|LARGE BRUSHED BRASS|41|MED PKG|1452.54|ic pinto beans use \n6547|forest khaki firebrick brown light|Manufacturer#2|Brand#23|LARGE PLATED COPPER|33|WRAP CASE|1453.54|ructions\n6548|green slate brown orange sandy|Manufacturer#5|Brand#54|PROMO BURNISHED STEEL|7|WRAP BAG|1454.54|atele\n6549|gainsboro sandy slate beige burlywood|Manufacturer#3|Brand#33|SMALL BRUSHED NICKEL|45|LG CAN|1455.54|ckages.\n6550|cyan cornflower magenta cornsilk lawn|Manufacturer#2|Brand#24|LARGE POLISHED STEEL|23|WRAP PACK|1456.55|requests\n6551|firebrick deep smoke mint peach|Manufacturer#5|Brand#51|PROMO ANODIZED BRASS|35|JUMBO BOX|1457.55|s. carefully\n6552|cream lemon chocolate sienna black|Manufacturer#5|Brand#51|LARGE ANODIZED NICKEL|25|LG BOX|1458.55|ronic \n6553|white green misty wheat indian|Manufacturer#2|Brand#24|SMALL POLISHED COPPER|33|JUMBO CASE|1459.55|ending excu\n6554|drab maroon deep cyan floral|Manufacturer#1|Brand#13|MEDIUM POLISHED NICKEL|42|JUMBO PKG|1460.55|pending accounts \n6555|brown thistle frosted sienna moccasin|Manufacturer#2|Brand#25|LARGE ANODIZED STEEL|23|WRAP BAG|1461.55|are furiou\n6556|forest misty seashell navy ghost|Manufacturer#3|Brand#32|MEDIUM BRUSHED NICKEL|42|MED CAN|1462.55|nusual ideas. sp\n6557|lawn blue navy rose navajo|Manufacturer#1|Brand#15|STANDARD BURNISHED NICKEL|28|SM BOX|1463.55|pendin\n6558|floral cornflower burlywood rose firebrick|Manufacturer#5|Brand#52|SMALL ANODIZED BRASS|3|LG DRUM|1464.55|d ideas are \n6559|honeydew steel seashell peach cornflower|Manufacturer#3|Brand#31|PROMO POLISHED TIN|8|JUMBO DRUM|1465.55|pades boost blithel\n6560|salmon dim moccasin frosted indian|Manufacturer#3|Brand#34|STANDARD BRUSHED BRASS|7|SM BAG|1466.56|d the daringly regula\n6561|dodger bisque blush peach lawn|Manufacturer#3|Brand#35|MEDIUM PLATED BRASS|42|LG BOX|1467.56|r the blithely spec\n6562|spring hot blanched chocolate orange|Manufacturer#5|Brand#52|ECONOMY ANODIZED STEEL|3|LG PACK|1468.56|g accounts slee\n6563|peach royal lemon frosted tomato|Manufacturer#5|Brand#55|ECONOMY POLISHED COPPER|43|LG PACK|1469.56|yly unusual patt\n6564|slate brown floral firebrick grey|Manufacturer#4|Brand#44|PROMO BURNISHED TIN|44|WRAP JAR|1470.56|pendencies boost blith\n6565|cyan lace chiffon papaya coral|Manufacturer#5|Brand#53|MEDIUM POLISHED STEEL|32|MED DRUM|1471.56|ing to the s\n6566|misty salmon frosted antique honeydew|Manufacturer#4|Brand#42|PROMO ANODIZED BRASS|38|LG CASE|1472.56| furiously fur\n6567|snow magenta dark saddle sky|Manufacturer#1|Brand#13|STANDARD POLISHED NICKEL|47|WRAP BAG|1473.56|express deposits \n6568|dark lime sandy burnished blanched|Manufacturer#2|Brand#23|MEDIUM POLISHED BRASS|50|MED JAR|1474.56|t, pendi\n6569|ivory snow red olive lawn|Manufacturer#4|Brand#44|SMALL BURNISHED COPPER|14|JUMBO JAR|1475.56|g the brave foxes dazz\n6570|medium lawn puff magenta saddle|Manufacturer#2|Brand#25|SMALL BURNISHED NICKEL|43|WRAP PACK|1476.57|kages. r\n6571|lace saddle lavender khaki pink|Manufacturer#5|Brand#51|SMALL PLATED BRASS|34|JUMBO CASE|1477.57|en dolphi\n6572|green salmon lace powder lemon|Manufacturer#3|Brand#33|PROMO ANODIZED NICKEL|7|JUMBO PKG|1478.57|quickly final foxe\n6573|cyan smoke firebrick papaya brown|Manufacturer#1|Brand#13|LARGE ANODIZED BRASS|32|JUMBO PACK|1479.57|ges affix.\n6574|lavender mint olive honeydew lemon|Manufacturer#3|Brand#33|LARGE BURNISHED COPPER|45|MED PACK|1480.57|c pinto beans haggle c\n6575|aquamarine red lime pale sandy|Manufacturer#3|Brand#34|STANDARD ANODIZED NICKEL|41|SM BAG|1481.57|into beans\n6576|lawn black cyan tomato cornsilk|Manufacturer#5|Brand#51|LARGE BRUSHED NICKEL|9|LG CAN|1482.57|. carefully silent pi\n6577|firebrick royal purple khaki burnished|Manufacturer#2|Brand#22|ECONOMY BURNISHED COPPER|41|WRAP CASE|1483.57|oost quickly acro\n6578|yellow cyan moccasin aquamarine cornflower|Manufacturer#3|Brand#35|PROMO ANODIZED BRASS|46|SM CASE|1484.57| requests wake blithel\n6579|rosy chartreuse green red navy|Manufacturer#4|Brand#45|STANDARD BRUSHED STEEL|17|SM CAN|1485.57|special accounts wil\n6580|dark thistle coral cornsilk red|Manufacturer#5|Brand#52|PROMO BRUSHED STEEL|43|JUMBO BOX|1486.58|s caj\n6581|khaki purple lawn royal slate|Manufacturer#3|Brand#33|LARGE BRUSHED COPPER|20|MED PKG|1487.58|ily qui\n6582|rosy frosted puff drab dim|Manufacturer#1|Brand#15|LARGE BURNISHED BRASS|19|JUMBO CASE|1488.58|y bold ideas. bold\n6583|magenta blue burnished azure midnight|Manufacturer#5|Brand#52|SMALL BRUSHED BRASS|10|SM CAN|1489.58|y unusual deposit\n6584|peach hot bisque white snow|Manufacturer#4|Brand#42|STANDARD BRUSHED BRASS|44|LG BAG|1490.58|ent th\n6585|tomato snow cornflower black mint|Manufacturer#4|Brand#43|SMALL BRUSHED NICKEL|39|JUMBO BOX|1491.58|ly regular deposit\n6586|navy yellow frosted tomato wheat|Manufacturer#3|Brand#32|MEDIUM POLISHED COPPER|46|WRAP DRUM|1492.58| nod stealthily\n6587|tan cornflower plum dark peru|Manufacturer#1|Brand#14|SMALL PLATED TIN|30|WRAP PKG|1493.58|e slyly\n6588|floral tomato white medium burlywood|Manufacturer#3|Brand#34|LARGE BURNISHED BRASS|31|JUMBO BAG|1494.58|refully pending pin\n6589|olive white spring misty cream|Manufacturer#1|Brand#11|ECONOMY PLATED NICKEL|30|LG PACK|1495.58| even pin\n6590|firebrick chartreuse goldenrod honeydew steel|Manufacturer#1|Brand#13|SMALL BRUSHED COPPER|28|MED DRUM|1496.59|es sleep about th\n6591|gainsboro khaki chocolate rosy navajo|Manufacturer#1|Brand#11|ECONOMY PLATED NICKEL|29|SM BAG|1497.59|x daringly bol\n6592|puff olive drab grey red|Manufacturer#4|Brand#44|LARGE POLISHED NICKEL|4|MED BAG|1498.59| acco\n6593|blanched lime lace magenta rosy|Manufacturer#1|Brand#14|ECONOMY POLISHED COPPER|41|MED PKG|1499.59| slyly regular the\n6594|black khaki navajo purple turquoise|Manufacturer#2|Brand#21|MEDIUM BURNISHED TIN|20|WRAP PACK|1500.59|pendenci\n6595|bisque lemon firebrick gainsboro pale|Manufacturer#3|Brand#31|SMALL PLATED NICKEL|36|MED CASE|1501.59|ar theod\n6596|mint misty coral turquoise cornsilk|Manufacturer#2|Brand#24|LARGE POLISHED TIN|15|SM DRUM|1502.59|y unusual deposits ar\n6597|metallic tan khaki beige salmon|Manufacturer#4|Brand#44|LARGE POLISHED TIN|11|WRAP CAN|1503.59|xes against the care\n6598|cream brown goldenrod lawn burnished|Manufacturer#3|Brand#32|LARGE PLATED COPPER|33|SM PACK|1504.59|odolites\n6599|plum midnight turquoise tan violet|Manufacturer#5|Brand#54|LARGE ANODIZED NICKEL|49|MED BOX|1505.59|fluffily against the f\n6600|aquamarine blanched drab floral burnished|Manufacturer#3|Brand#31|PROMO PLATED STEEL|17|JUMBO CASE|1506.60| are around \n6601|cyan orange metallic blush navajo|Manufacturer#2|Brand#21|LARGE PLATED NICKEL|8|MED CASE|1507.60|ingly brave dol\n6602|blush violet azure black tan|Manufacturer#5|Brand#53|SMALL BURNISHED COPPER|45|JUMBO CASE|1508.60|ly silent deposits. a\n6603|wheat blanched grey slate brown|Manufacturer#4|Brand#42|PROMO BRUSHED NICKEL|33|MED JAR|1509.60|posits are furiously\n6604|powder smoke rose maroon cornsilk|Manufacturer#4|Brand#43|STANDARD BURNISHED NICKEL|48|SM CASE|1510.60|ckly bold ideas.\n6605|peru pale spring orange papaya|Manufacturer#3|Brand#32|MEDIUM ANODIZED STEEL|10|SM PKG|1511.60|lyly. furiously\n6606|rosy grey midnight wheat lavender|Manufacturer#5|Brand#51|SMALL ANODIZED STEEL|7|SM DRUM|1512.60|stealthily even ideas \n6607|khaki goldenrod floral misty navy|Manufacturer#1|Brand#14|LARGE BRUSHED BRASS|39|SM CAN|1513.60|d dependencies. \n6608|lemon ivory cornsilk navy seashell|Manufacturer#2|Brand#21|SMALL PLATED TIN|26|WRAP DRUM|1514.60|lyly among the fu\n6609|cornflower medium grey royal slate|Manufacturer#3|Brand#33|SMALL ANODIZED BRASS|41|WRAP BOX|1515.60|cross the fluff\n6610|moccasin lavender khaki dim white|Manufacturer#3|Brand#31|ECONOMY BRUSHED COPPER|46|SM JAR|1516.61|are furio\n6611|chocolate puff rose indian purple|Manufacturer#4|Brand#42|PROMO ANODIZED BRASS|32|MED BOX|1517.61|fix slyly \n6612|blue hot wheat misty gainsboro|Manufacturer#1|Brand#14|MEDIUM PLATED TIN|44|JUMBO JAR|1518.61|dinos. blithely \n6613|rose slate antique maroon beige|Manufacturer#3|Brand#31|SMALL BURNISHED COPPER|7|WRAP PACK|1519.61|slyly \n6614|misty peach blue cornflower indian|Manufacturer#5|Brand#53|MEDIUM BURNISHED TIN|46|JUMBO DRUM|1520.61|ly bold packages cajo\n6615|rosy drab sienna misty royal|Manufacturer#5|Brand#54|SMALL ANODIZED BRASS|16|MED PACK|1521.61|ar requests. blithe\n6616|azure coral sienna midnight lavender|Manufacturer#5|Brand#51|ECONOMY ANODIZED STEEL|9|LG PKG|1522.61| the q\n6617|dodger black tomato powder peru|Manufacturer#3|Brand#33|ECONOMY BURNISHED COPPER|3|WRAP PACK|1523.61|ithely a\n6618|floral spring antique moccasin peru|Manufacturer#4|Brand#43|PROMO BRUSHED BRASS|25|SM CASE|1524.61|r the unusual, \n6619|violet frosted medium ivory cornsilk|Manufacturer#2|Brand#22|LARGE ANODIZED COPPER|21|JUMBO BOX|1525.61|s hang bli\n6620|puff khaki peru midnight chocolate|Manufacturer#5|Brand#53|STANDARD ANODIZED BRASS|34|MED PACK|1526.62|ounts. pen\n6621|steel deep brown cornflower burnished|Manufacturer#3|Brand#31|SMALL BRUSHED COPPER|7|JUMBO CAN|1527.62|tructions bre\n6622|rose burlywood blue deep salmon|Manufacturer#2|Brand#22|ECONOMY POLISHED COPPER|16|LG PACK|1528.62| foxes are b\n6623|salmon lavender blue medium papaya|Manufacturer#4|Brand#41|ECONOMY POLISHED NICKEL|5|WRAP DRUM|1529.62|lyly above the\n6624|tan blanched cream dodger grey|Manufacturer#2|Brand#23|MEDIUM BRUSHED TIN|26|SM CASE|1530.62|s above the slyly cl\n6625|chartreuse ghost dim frosted sandy|Manufacturer#2|Brand#23|MEDIUM BRUSHED BRASS|18|MED CASE|1531.62| at the slyly iro\n6626|chartreuse lime hot saddle rose|Manufacturer#3|Brand#31|MEDIUM BURNISHED COPPER|17|LG DRUM|1532.62|ctions\n6627|medium peru chartreuse orange royal|Manufacturer#4|Brand#42|STANDARD POLISHED NICKEL|32|JUMBO PACK|1533.62|ronic, even dep\n6628|lemon sienna medium pink saddle|Manufacturer#5|Brand#55|PROMO PLATED TIN|3|SM CASE|1534.62| across the silent, \n6629|deep bisque lavender snow ivory|Manufacturer#4|Brand#43|STANDARD PLATED TIN|29|JUMBO JAR|1535.62|ggle blithely expres\n6630|spring metallic chocolate deep blush|Manufacturer#1|Brand#14|STANDARD ANODIZED BRASS|47|MED BAG|1536.63|t slyly al\n6631|drab moccasin lace white ghost|Manufacturer#3|Brand#33|SMALL PLATED BRASS|28|WRAP CASE|1537.63|es nag. multi\n6632|sandy orchid misty navajo antique|Manufacturer#1|Brand#15|ECONOMY PLATED COPPER|35|JUMBO CAN|1538.63|eas cajole bl\n6633|orchid thistle sky linen khaki|Manufacturer#2|Brand#21|ECONOMY ANODIZED STEEL|27|WRAP JAR|1539.63|ounts thrash \n6634|lace olive indian orange dim|Manufacturer#4|Brand#41|STANDARD BURNISHED STEEL|34|SM CAN|1540.63| above the \n6635|frosted midnight khaki tomato sandy|Manufacturer#4|Brand#43|PROMO PLATED NICKEL|38|SM CAN|1541.63|side \n6636|red linen dodger floral hot|Manufacturer#5|Brand#52|STANDARD POLISHED NICKEL|45|MED BOX|1542.63|nstruct\n6637|chiffon bisque frosted magenta red|Manufacturer#1|Brand#13|LARGE PLATED BRASS|41|MED CAN|1543.63|. blithely\n6638|sandy antique cornflower dim honeydew|Manufacturer#1|Brand#13|PROMO BRUSHED NICKEL|50|LG BAG|1544.63|uickly final\n6639|turquoise midnight smoke maroon chiffon|Manufacturer#1|Brand#11|MEDIUM POLISHED STEEL|34|LG CASE|1545.63|y deposits \n6640|pink sandy red mint black|Manufacturer#5|Brand#54|ECONOMY POLISHED TIN|3|JUMBO PKG|1546.64|ld depos\n6641|cyan indian grey drab chartreuse|Manufacturer#2|Brand#23|ECONOMY ANODIZED COPPER|11|SM BOX|1547.64|ts. quickly\n6642|gainsboro navajo cream medium maroon|Manufacturer#3|Brand#33|ECONOMY BURNISHED NICKEL|34|JUMBO BOX|1548.64|al accounts. carefull\n6643|white lemon blanched deep yellow|Manufacturer#3|Brand#33|PROMO ANODIZED TIN|44|SM BOX|1549.64|the ironic ins\n6644|khaki sandy moccasin goldenrod almond|Manufacturer#2|Brand#24|SMALL BRUSHED COPPER|25|MED CAN|1550.64|ts nag blithely above \n6645|blush antique lemon plum dim|Manufacturer#4|Brand#41|STANDARD POLISHED STEEL|43|LG JAR|1551.64|pendencies affix. regu\n6646|cornflower green slate spring medium|Manufacturer#5|Brand#55|STANDARD BRUSHED STEEL|2|WRAP BAG|1552.64|equests. de\n6647|turquoise forest misty dodger aquamarine|Manufacturer#1|Brand#13|ECONOMY PLATED TIN|3|MED PKG|1553.64|ptotes. slyly ir\n6648|forest beige chiffon hot peach|Manufacturer#1|Brand#14|STANDARD PLATED NICKEL|2|SM DRUM|1554.64|gside of the \n6649|rosy saddle beige almond light|Manufacturer#2|Brand#22|ECONOMY ANODIZED NICKEL|18|WRAP BOX|1555.64|t. regular ideas are: \n6650|almond white misty goldenrod blanched|Manufacturer#3|Brand#32|MEDIUM ANODIZED COPPER|27|SM PACK|1556.65| after the warhors\n6651|burlywood sandy olive orange smoke|Manufacturer#2|Brand#23|ECONOMY ANODIZED NICKEL|20|WRAP PKG|1557.65| frays. \n6652|red puff green sandy smoke|Manufacturer#1|Brand#12|ECONOMY BRUSHED TIN|33|SM JAR|1558.65|. blithely bold ac\n6653|sienna papaya cornflower coral peru|Manufacturer#3|Brand#32|ECONOMY POLISHED NICKEL|10|LG PACK|1559.65|bout the unusual, i\n6654|gainsboro dim goldenrod maroon light|Manufacturer#5|Brand#52|STANDARD BURNISHED COPPER|40|MED BAG|1560.65|en decoy\n6655|burlywood snow frosted dark drab|Manufacturer#4|Brand#42|SMALL BURNISHED BRASS|36|WRAP PACK|1561.65|? blithel\n6656|chiffon indian lace purple cream|Manufacturer#2|Brand#22|LARGE PLATED BRASS|31|MED PACK|1562.65|al excuses \n6657|tomato lavender blush drab lawn|Manufacturer#2|Brand#24|SMALL POLISHED NICKEL|19|LG CASE|1563.65|uests. bravely unusua\n6658|thistle goldenrod yellow beige black|Manufacturer#4|Brand#43|ECONOMY PLATED BRASS|20|MED BAG|1564.65|nding pl\n6659|yellow pink pale rose honeydew|Manufacturer#2|Brand#21|LARGE BRUSHED NICKEL|22|MED JAR|1565.65|xpress asympto\n6660|lime blanched rose salmon forest|Manufacturer#2|Brand#23|STANDARD PLATED BRASS|39|SM PKG|1566.66|p. qu\n6661|beige coral ghost papaya antique|Manufacturer#2|Brand#23|PROMO BURNISHED STEEL|2|MED CASE|1567.66|ly special instr\n6662|almond salmon hot sandy forest|Manufacturer#4|Brand#43|SMALL PLATED TIN|7|SM PKG|1568.66|r requests.\n6663|spring seashell violet aquamarine dodger|Manufacturer#2|Brand#25|LARGE ANODIZED COPPER|32|LG JAR|1569.66|c ideas.\n6664|lace peach snow pale spring|Manufacturer#3|Brand#35|LARGE POLISHED NICKEL|21|LG CAN|1570.66| quickly pending dol\n6665|goldenrod chocolate peru midnight lemon|Manufacturer#5|Brand#55|LARGE BURNISHED BRASS|22|WRAP JAR|1571.66| furiously to\n6666|azure blue smoke metallic powder|Manufacturer#1|Brand#15|LARGE PLATED STEEL|42|SM CAN|1572.66|y deposits. even, regu\n6667|coral beige turquoise chiffon burnished|Manufacturer#1|Brand#14|MEDIUM POLISHED STEEL|13|MED PKG|1573.66|ents along the quic\n6668|turquoise burnished honeydew aquamarine ghost|Manufacturer#5|Brand#55|STANDARD PLATED BRASS|23|WRAP BOX|1574.66|ong the furiously\n6669|rose forest lace chocolate peru|Manufacturer#1|Brand#12|STANDARD PLATED BRASS|40|LG CAN|1575.66|ckly regular do\n6670|snow wheat steel mint pink|Manufacturer#4|Brand#43|PROMO PLATED TIN|3|WRAP JAR|1576.67|iously regul\n6671|lemon linen wheat frosted honeydew|Manufacturer#5|Brand#52|SMALL BURNISHED TIN|35|JUMBO PACK|1577.67|ncies? care\n6672|slate dodger pink blanched thistle|Manufacturer#3|Brand#31|MEDIUM BURNISHED STEEL|32|LG CASE|1578.67| pending accou\n6673|lavender forest grey slate chiffon|Manufacturer#3|Brand#31|LARGE PLATED BRASS|20|LG PACK|1579.67|ic dependenc\n6674|lace frosted rosy chiffon blanched|Manufacturer#2|Brand#21|SMALL ANODIZED NICKEL|47|JUMBO JAR|1580.67|refully even deco\n6675|burlywood purple deep goldenrod pink|Manufacturer#5|Brand#53|SMALL POLISHED COPPER|39|WRAP JAR|1581.67| furious foxe\n6676|saddle tan sienna burlywood green|Manufacturer#5|Brand#53|SMALL BURNISHED NICKEL|41|WRAP CAN|1582.67|e final asymptotes. s\n6677|chiffon royal lace yellow drab|Manufacturer#3|Brand#31|MEDIUM ANODIZED BRASS|31|WRAP CASE|1583.67|riously. unusual depen\n6678|spring peru burnished moccasin khaki|Manufacturer#3|Brand#35|PROMO PLATED TIN|11|SM JAR|1584.67|ages ha\n6679|lace moccasin puff violet plum|Manufacturer#2|Brand#21|SMALL PLATED TIN|37|LG BOX|1585.67| cour\n6680|dim lace cream honeydew wheat|Manufacturer#1|Brand#14|STANDARD POLISHED BRASS|24|SM BAG|1586.68|n dolphins. slyly reg\n6681|steel gainsboro burlywood cream hot|Manufacturer#2|Brand#23|STANDARD ANODIZED STEEL|7|LG PACK|1587.68|en excuses. sl\n6682|slate sandy mint chiffon peru|Manufacturer#3|Brand#33|MEDIUM BRUSHED COPPER|30|MED CAN|1588.68|e. even packa\n6683|cream sandy dodger dim cornflower|Manufacturer#1|Brand#13|SMALL POLISHED STEEL|49|LG BAG|1589.68| pending, silent pac\n6684|green firebrick blanched goldenrod grey|Manufacturer#1|Brand#12|MEDIUM BURNISHED STEEL|2|MED CAN|1590.68|y about the c\n6685|royal sienna mint green khaki|Manufacturer#4|Brand#45|ECONOMY ANODIZED BRASS|49|MED PKG|1591.68|detect\n6686|frosted cornsilk misty light lime|Manufacturer#5|Brand#53|MEDIUM BURNISHED NICKEL|6|WRAP PACK|1592.68| the requests wake sl\n6687|bisque pink navy ivory orange|Manufacturer#4|Brand#42|ECONOMY BURNISHED COPPER|8|SM CASE|1593.68|ully silent pa\n6688|sienna olive lemon dark blue|Manufacturer#5|Brand#53|STANDARD BRUSHED NICKEL|34|JUMBO PACK|1594.68|ully regular theo\n6689|maroon gainsboro cream turquoise cyan|Manufacturer#5|Brand#53|MEDIUM PLATED BRASS|23|WRAP PKG|1595.68| blithely special r\n6690|azure wheat lawn midnight spring|Manufacturer#4|Brand#44|SMALL BRUSHED TIN|20|JUMBO CASE|1596.69|deposits. closely ev\n6691|seashell chiffon peru chocolate antique|Manufacturer#2|Brand#24|MEDIUM PLATED COPPER|7|JUMBO BAG|1597.69| depo\n6692|white deep cornflower purple ivory|Manufacturer#3|Brand#32|ECONOMY PLATED COPPER|24|MED CAN|1598.69|iously ironic court\n6693|moccasin cornflower lemon goldenrod light|Manufacturer#4|Brand#44|MEDIUM ANODIZED COPPER|26|JUMBO DRUM|1599.69|regular d\n6694|misty linen white spring metallic|Manufacturer#4|Brand#45|LARGE BRUSHED BRASS|26|MED PACK|1600.69|gular requests.\n6695|brown yellow mint burlywood medium|Manufacturer#4|Brand#45|STANDARD POLISHED STEEL|13|WRAP BOX|1601.69|riously un\n6696|moccasin mint saddle gainsboro seashell|Manufacturer#2|Brand#24|SMALL PLATED STEEL|1|MED DRUM|1602.69|ithely stealt\n6697|cream smoke orange brown lawn|Manufacturer#4|Brand#42|PROMO POLISHED STEEL|8|SM PKG|1603.69|ic deposits. slyly spe\n6698|orange lace lawn pink papaya|Manufacturer#4|Brand#45|PROMO BURNISHED BRASS|2|SM PACK|1604.69|s. carefu\n6699|almond antique metallic honeydew green|Manufacturer#4|Brand#43|MEDIUM BRUSHED TIN|30|LG BAG|1605.69|wake around\n6700|frosted metallic khaki coral steel|Manufacturer#4|Brand#44|MEDIUM BURNISHED BRASS|16|SM JAR|1606.70| even theodolit\n6701|antique pale honeydew spring maroon|Manufacturer#2|Brand#23|PROMO BRUSHED NICKEL|10|WRAP PKG|1607.70|nto beans doubt qu\n6702|violet sienna gainsboro brown cornsilk|Manufacturer#4|Brand#44|SMALL POLISHED NICKEL|44|MED CAN|1608.70|hy reques\n6703|light coral sky rose almond|Manufacturer#1|Brand#15|LARGE BRUSHED BRASS|37|JUMBO BOX|1609.70|ic pinto be\n6704|lemon burnished firebrick hot royal|Manufacturer#5|Brand#51|SMALL POLISHED NICKEL|23|SM DRUM|1610.70|lyly ironic p\n6705|drab sienna cream yellow navajo|Manufacturer#1|Brand#15|STANDARD BURNISHED NICKEL|11|LG BAG|1611.70|ing to the quickly fi\n6706|black drab peru almond moccasin|Manufacturer#4|Brand#44|STANDARD ANODIZED STEEL|8|SM PKG|1612.70|fluffil\n6707|burlywood snow dark peru saddle|Manufacturer#2|Brand#22|MEDIUM BRUSHED NICKEL|14|SM BOX|1613.70|nusual \n6708|goldenrod linen orchid antique azure|Manufacturer#2|Brand#24|SMALL POLISHED BRASS|3|SM DRUM|1614.70|ic deposits\n6709|lawn pale red steel rose|Manufacturer#4|Brand#45|LARGE BURNISHED STEEL|16|WRAP CAN|1615.70|iously \n6710|plum brown peru cornflower red|Manufacturer#3|Brand#33|LARGE ANODIZED TIN|45|WRAP DRUM|1616.71| are blithely slyly i\n6711|honeydew brown powder blue plum|Manufacturer#1|Brand#15|PROMO PLATED NICKEL|35|MED BOX|1617.71|ctions are furiously\n6712|moccasin burlywood peru dodger smoke|Manufacturer#3|Brand#35|ECONOMY ANODIZED COPPER|41|WRAP JAR|1618.71|ests hag\n6713|sandy midnight honeydew dodger orchid|Manufacturer#5|Brand#55|MEDIUM PLATED NICKEL|31|JUMBO BOX|1619.71|deposits was\n6714|midnight lemon blue frosted misty|Manufacturer#3|Brand#31|LARGE BURNISHED COPPER|50|SM BAG|1620.71|detect\n6715|ivory azure lavender cyan dim|Manufacturer#3|Brand#33|MEDIUM ANODIZED COPPER|13|WRAP BOX|1621.71|y final deposits a\n6716|frosted almond azure hot dim|Manufacturer#3|Brand#31|LARGE ANODIZED STEEL|11|MED CAN|1622.71|ly. platelets are ca\n6717|lace sienna plum gainsboro blanched|Manufacturer#1|Brand#15|LARGE BRUSHED STEEL|14|JUMBO DRUM|1623.71|usly ironic pinto\n6718|lawn coral papaya steel chartreuse|Manufacturer#4|Brand#41|SMALL BRUSHED COPPER|5|LG CAN|1624.71|eep ironic war\n6719|ghost black papaya ivory coral|Manufacturer#2|Brand#21|ECONOMY ANODIZED NICKEL|21|LG CAN|1625.71|kages.\n6720|beige burlywood spring olive orange|Manufacturer#5|Brand#53|SMALL BRUSHED NICKEL|8|MED JAR|1626.72|refully regular p\n6721|burlywood chocolate midnight puff pink|Manufacturer#2|Brand#23|PROMO POLISHED BRASS|20|LG PKG|1627.72|ely ironic courts. i\n6722|ghost moccasin lemon bisque chartreuse|Manufacturer#2|Brand#23|MEDIUM PLATED TIN|10|LG CAN|1628.72|ly even deposits\n6723|mint firebrick sienna yellow chocolate|Manufacturer#5|Brand#53|SMALL ANODIZED STEEL|27|SM CAN|1629.72|quickly \n6724|seashell sandy pink lemon magenta|Manufacturer#3|Brand#33|PROMO ANODIZED COPPER|27|MED CAN|1630.72|y regular dep\n6725|bisque khaki pink chartreuse goldenrod|Manufacturer#4|Brand#45|ECONOMY POLISHED BRASS|50|LG PACK|1631.72|ronic, re\n6726|dodger metallic honeydew cream lavender|Manufacturer#3|Brand#33|ECONOMY POLISHED NICKEL|19|SM DRUM|1632.72|fluff\n6727|tomato antique brown smoke dark|Manufacturer#2|Brand#24|ECONOMY PLATED TIN|41|WRAP BOX|1633.72|ly. packages \n6728|cream antique slate honeydew plum|Manufacturer#5|Brand#53|SMALL BRUSHED COPPER|14|LG PACK|1634.72|riously regular \n6729|forest mint cyan goldenrod azure|Manufacturer#1|Brand#12|LARGE BURNISHED TIN|36|LG BOX|1635.72|ong the warthogs i\n6730|slate rose cyan blue khaki|Manufacturer#2|Brand#21|STANDARD BURNISHED STEEL|38|SM PKG|1636.73|lites.\n6731|steel hot blue dodger ivory|Manufacturer#2|Brand#21|MEDIUM ANODIZED STEEL|10|SM BAG|1637.73|es caj\n6732|dark smoke navy dodger papaya|Manufacturer#3|Brand#32|STANDARD PLATED COPPER|16|LG CAN|1638.73|nts-- fluffily i\n6733|sienna coral lemon gainsboro grey|Manufacturer#1|Brand#12|SMALL BRUSHED STEEL|11|LG BAG|1639.73|y. slyl\n6734|yellow plum powder navajo lavender|Manufacturer#5|Brand#52|SMALL BRUSHED TIN|32|JUMBO DRUM|1640.73|above \n6735|sky wheat goldenrod spring plum|Manufacturer#4|Brand#41|MEDIUM BURNISHED STEEL|44|LG PACK|1641.73|ly regular asympto\n6736|olive burlywood azure cyan plum|Manufacturer#2|Brand#24|MEDIUM POLISHED TIN|16|SM BOX|1642.73|ts. ex\n6737|frosted plum saddle black maroon|Manufacturer#5|Brand#52|PROMO BURNISHED NICKEL|9|WRAP BOX|1643.73|c forges. quickly s\n6738|drab slate steel sienna papaya|Manufacturer#3|Brand#32|STANDARD PLATED TIN|50|SM PKG|1644.73|ajole regul\n6739|bisque tan coral blue salmon|Manufacturer#1|Brand#11|STANDARD POLISHED COPPER|43|JUMBO CAN|1645.73|blithely unusual\n6740|gainsboro mint slate blue coral|Manufacturer#2|Brand#23|LARGE POLISHED STEEL|43|LG JAR|1646.74|xcuses. fluff\n6741|brown salmon medium aquamarine magenta|Manufacturer#3|Brand#32|MEDIUM BURNISHED TIN|20|SM BOX|1647.74|osits. furio\n6742|wheat yellow dark maroon black|Manufacturer#5|Brand#54|LARGE PLATED BRASS|34|JUMBO PACK|1648.74|ithely special deposi\n6743|misty gainsboro sky turquoise forest|Manufacturer#5|Brand#54|STANDARD ANODIZED COPPER|38|MED JAR|1649.74|hely final \n6744|navy lavender magenta white dodger|Manufacturer#1|Brand#15|SMALL POLISHED STEEL|6|WRAP DRUM|1650.74| carefully final a\n6745|cream steel magenta pale bisque|Manufacturer#3|Brand#33|ECONOMY POLISHED COPPER|12|WRAP BOX|1651.74| theodol\n6746|thistle rosy deep mint plum|Manufacturer#5|Brand#51|ECONOMY BURNISHED COPPER|24|MED DRUM|1652.74|refully? slyly \n6747|brown drab puff dim thistle|Manufacturer#1|Brand#12|MEDIUM ANODIZED TIN|40|WRAP CAN|1653.74|ymptotes mai\n6748|sienna plum papaya green drab|Manufacturer#3|Brand#35|SMALL BURNISHED TIN|26|LG CAN|1654.74|ing to th\n6749|yellow royal thistle beige lawn|Manufacturer#4|Brand#41|MEDIUM POLISHED COPPER|44|JUMBO BAG|1655.74|e fluffily after the b\n6750|indian dark rosy navy dodger|Manufacturer#5|Brand#51|LARGE POLISHED BRASS|50|SM DRUM|1656.75|arefully ironic packag\n6751|sandy steel tomato pink royal|Manufacturer#1|Brand#13|ECONOMY BURNISHED STEEL|15|WRAP PACK|1657.75| foxes. slyl\n6752|medium rose sandy puff navy|Manufacturer#4|Brand#42|STANDARD BRUSHED NICKEL|4|SM BOX|1658.75| idea\n6753|firebrick lemon burnished ghost maroon|Manufacturer#5|Brand#54|STANDARD BURNISHED STEEL|14|SM PACK|1659.75|ar ideas\n6754|medium pink magenta rose burlywood|Manufacturer#1|Brand#15|MEDIUM ANODIZED STEEL|13|WRAP PACK|1660.75|lithely \n6755|wheat hot medium honeydew grey|Manufacturer#4|Brand#41|LARGE PLATED COPPER|43|SM BAG|1661.75| to the even p\n6756|rosy drab bisque purple ghost|Manufacturer#1|Brand#15|LARGE BURNISHED BRASS|47|JUMBO PACK|1662.75|ickly e\n6757|steel brown navajo firebrick medium|Manufacturer#5|Brand#52|LARGE ANODIZED STEEL|5|JUMBO BOX|1663.75|ites serve quickly i\n6758|lavender rose honeydew frosted metallic|Manufacturer#5|Brand#55|PROMO PLATED BRASS|38|JUMBO BAG|1664.75|s sleep \n6759|burlywood antique bisque brown orchid|Manufacturer#2|Brand#24|ECONOMY BRUSHED COPPER|23|SM DRUM|1665.75|ts nag blithel\n6760|rosy azure linen gainsboro chiffon|Manufacturer#5|Brand#52|STANDARD BRUSHED NICKEL|34|MED BAG|1666.76|furiously regula\n6761|misty pale aquamarine orange azure|Manufacturer#5|Brand#54|SMALL BURNISHED TIN|26|LG DRUM|1667.76|ular platelets.\n6762|rose royal chiffon magenta metallic|Manufacturer#1|Brand#15|SMALL ANODIZED BRASS|34|JUMBO BOX|1668.76|ckage\n6763|lace papaya azure aquamarine powder|Manufacturer#4|Brand#43|MEDIUM PLATED STEEL|41|JUMBO PKG|1669.76|wake ca\n6764|puff bisque forest almond salmon|Manufacturer#2|Brand#24|PROMO BURNISHED STEEL|50|JUMBO BOX|1670.76|uses cajole furio\n6765|maroon tomato royal lavender papaya|Manufacturer#4|Brand#45|MEDIUM ANODIZED TIN|20|JUMBO BAG|1671.76|cing deposits. furi\n6766|lace moccasin smoke honeydew burlywood|Manufacturer#2|Brand#22|SMALL POLISHED STEEL|30|MED JAR|1672.76|osits. reg\n6767|maroon ghost rosy violet lemon|Manufacturer#2|Brand#25|STANDARD ANODIZED BRASS|12|SM CASE|1673.76|e bli\n6768|lace moccasin orchid red beige|Manufacturer#1|Brand#13|SMALL ANODIZED COPPER|40|SM CAN|1674.76|r package\n6769|turquoise yellow sky pale olive|Manufacturer#1|Brand#13|PROMO PLATED BRASS|26|JUMBO PKG|1675.76| pack\n6770|red pale navajo slate black|Manufacturer#1|Brand#13|LARGE POLISHED COPPER|16|SM DRUM|1676.77|ly ironic asymptotes\n6771|misty papaya almond royal chocolate|Manufacturer#3|Brand#31|MEDIUM ANODIZED STEEL|7|JUMBO CASE|1677.77|ully s\n6772|lime steel black puff orange|Manufacturer#4|Brand#41|ECONOMY BURNISHED COPPER|8|JUMBO DRUM|1678.77|accounts cajole blithe\n6773|orange lime slate powder plum|Manufacturer#2|Brand#23|MEDIUM ANODIZED TIN|24|LG DRUM|1679.77|slyly silent account\n6774|cornsilk royal gainsboro powder cream|Manufacturer#5|Brand#51|SMALL ANODIZED NICKEL|40|MED PKG|1680.77|e evenly unusu\n6775|rosy navy cream cornflower burnished|Manufacturer#5|Brand#51|LARGE ANODIZED BRASS|22|LG BAG|1681.77|efully expr\n6776|puff sienna burlywood midnight drab|Manufacturer#2|Brand#25|SMALL BRUSHED COPPER|26|SM PKG|1682.77|equests boos\n6777|honeydew cornflower cyan navajo tan|Manufacturer#1|Brand#15|STANDARD ANODIZED BRASS|35|JUMBO CASE|1683.77| carefully specia\n6778|medium goldenrod antique green sandy|Manufacturer#2|Brand#25|MEDIUM POLISHED COPPER|18|MED CASE|1684.77|lways final requests.\n6779|floral violet navy tomato maroon|Manufacturer#4|Brand#42|ECONOMY PLATED COPPER|23|LG PACK|1685.77|thely u\n6780|rose deep cyan puff navajo|Manufacturer#4|Brand#41|PROMO POLISHED NICKEL|17|SM PKG|1686.78|es. carefully stealthy\n6781|saddle pale tan lawn sienna|Manufacturer#2|Brand#25|LARGE BRUSHED STEEL|2|MED BAG|1687.78|ecial \n6782|spring khaki bisque honeydew pale|Manufacturer#1|Brand#11|STANDARD BURNISHED STEEL|17|SM CASE|1688.78|counts sleep blithely\n6783|hot ghost navy peru blush|Manufacturer#4|Brand#45|LARGE ANODIZED BRASS|7|LG BAG|1689.78|c pinto \n6784|thistle burlywood papaya gainsboro lemon|Manufacturer#4|Brand#42|LARGE ANODIZED STEEL|32|JUMBO BOX|1690.78|unts nag blithely wit\n6785|chartreuse violet pale puff orchid|Manufacturer#5|Brand#54|STANDARD BURNISHED BRASS|32|MED PKG|1691.78|uickly theodoli\n6786|chiffon khaki lawn gainsboro turquoise|Manufacturer#1|Brand#12|LARGE ANODIZED NICKEL|11|JUMBO CAN|1692.78|ently. even, \n6787|magenta coral red blush green|Manufacturer#4|Brand#43|STANDARD PLATED NICKEL|14|JUMBO BAG|1693.78|le carefully af\n6788|blanched tomato hot chiffon steel|Manufacturer#2|Brand#25|LARGE ANODIZED TIN|50|JUMBO DRUM|1694.78|y bold \n6789|cream dodger moccasin violet green|Manufacturer#4|Brand#45|STANDARD ANODIZED TIN|11|LG BAG|1695.78| among the final de\n6790|bisque firebrick mint plum burnished|Manufacturer#4|Brand#42|PROMO BURNISHED COPPER|39|SM BOX|1696.79| among the b\n6791|moccasin magenta chocolate snow beige|Manufacturer#4|Brand#43|LARGE BURNISHED STEEL|48|WRAP CAN|1697.79|s. pinto beans mainta\n6792|aquamarine powder green spring honeydew|Manufacturer#1|Brand#13|MEDIUM PLATED NICKEL|48|MED BAG|1698.79|y above the stealthil\n6793|misty grey violet plum lace|Manufacturer#3|Brand#35|PROMO BRUSHED NICKEL|14|JUMBO BAG|1699.79|refully r\n6794|khaki seashell pale orange smoke|Manufacturer#3|Brand#34|LARGE BRUSHED NICKEL|23|MED CAN|1700.79|es among\n6795|plum maroon azure hot slate|Manufacturer#5|Brand#53|ECONOMY BRUSHED NICKEL|48|WRAP CASE|1701.79|xes w\n6796|cyan blue lavender puff tan|Manufacturer#2|Brand#21|PROMO BRUSHED STEEL|14|JUMBO PKG|1702.79|accounts detect furio\n6797|almond indian cyan antique snow|Manufacturer#1|Brand#14|STANDARD POLISHED STEEL|8|LG JAR|1703.79|ake furiou\n6798|sandy cyan navy hot black|Manufacturer#5|Brand#52|MEDIUM POLISHED TIN|36|SM CAN|1704.79|ely ironic accoun\n6799|seashell wheat hot almond rose|Manufacturer#3|Brand#32|MEDIUM BURNISHED NICKEL|1|SM DRUM|1705.79|requests wake f\n6800|ghost lawn chartreuse drab plum|Manufacturer#1|Brand#13|STANDARD POLISHED TIN|15|MED CASE|1706.80| express instructi\n6801|seashell ghost azure firebrick cornsilk|Manufacturer#3|Brand#34|SMALL ANODIZED BRASS|36|WRAP PKG|1707.80|oxes. foxes among t\n6802|floral blush grey frosted wheat|Manufacturer#1|Brand#14|STANDARD BRUSHED NICKEL|41|WRAP BAG|1708.80|ing platele\n6803|sienna saddle dodger azure smoke|Manufacturer#3|Brand#33|SMALL BRUSHED BRASS|46|MED BAG|1709.80|ular excus\n6804|turquoise medium thistle puff peach|Manufacturer#2|Brand#25|MEDIUM BRUSHED COPPER|40|SM PKG|1710.80|lyly pending ideas. \n6805|misty azure green orchid orange|Manufacturer#1|Brand#14|PROMO BURNISHED COPPER|30|WRAP PKG|1711.80| furiously \n6806|blush bisque antique lemon gainsboro|Manufacturer#5|Brand#51|PROMO ANODIZED BRASS|28|SM BOX|1712.80|ages. bold foxe\n6807|drab wheat snow moccasin saddle|Manufacturer#1|Brand#12|LARGE PLATED NICKEL|48|JUMBO PACK|1713.80| instructions de\n6808|steel turquoise drab sky misty|Manufacturer#3|Brand#34|PROMO BURNISHED STEEL|3|MED PKG|1714.80|ously unusual\n6809|puff slate misty powder spring|Manufacturer#5|Brand#54|MEDIUM ANODIZED STEEL|22|SM CASE|1715.80|ar requests cajol\n6810|tan tomato seashell navajo lawn|Manufacturer#4|Brand#43|STANDARD BRUSHED NICKEL|21|JUMBO JAR|1716.81|uriously regular r\n6811|navy blush cornflower lace dim|Manufacturer#5|Brand#54|STANDARD ANODIZED COPPER|42|WRAP BOX|1717.81|kly blithely even a\n6812|lime chartreuse papaya blue blush|Manufacturer#4|Brand#41|MEDIUM BURNISHED STEEL|45|SM CAN|1718.81| accounts haggl\n6813|light azure royal brown sienna|Manufacturer#2|Brand#25|STANDARD ANODIZED COPPER|17|MED JAR|1719.81|uickly \n6814|dim cornflower yellow lemon almond|Manufacturer#4|Brand#44|STANDARD POLISHED BRASS|4|WRAP BOX|1720.81|kages. blithely\n6815|deep burlywood aquamarine puff dark|Manufacturer#1|Brand#11|SMALL POLISHED COPPER|14|JUMBO DRUM|1721.81|ss accounts eat dar\n6816|peru brown chartreuse dark frosted|Manufacturer#2|Brand#24|STANDARD BURNISHED NICKEL|26|MED BAG|1722.81|s are! slyly \n6817|cornsilk peru lavender goldenrod cream|Manufacturer#5|Brand#52|PROMO ANODIZED TIN|8|SM JAR|1723.81|al pinto be\n6818|floral smoke light deep pale|Manufacturer#5|Brand#51|PROMO POLISHED BRASS|40|JUMBO PACK|1724.81|nusua\n6819|snow violet royal dark khaki|Manufacturer#2|Brand#24|STANDARD PLATED STEEL|5|JUMBO BOX|1725.81| prom\n6820|light smoke orchid plum wheat|Manufacturer#1|Brand#11|STANDARD ANODIZED STEEL|1|SM PACK|1726.82|nal deposi\n6821|red chocolate tomato orange black|Manufacturer#2|Brand#22|LARGE PLATED BRASS|48|SM DRUM|1727.82|g accounts cajole car\n6822|sky deep medium goldenrod lime|Manufacturer#5|Brand#55|STANDARD BRUSHED NICKEL|4|MED CASE|1728.82|ependencies are thinl\n6823|tomato white magenta floral ivory|Manufacturer#4|Brand#43|ECONOMY PLATED STEEL|9|JUMBO BOX|1729.82| ironi\n6824|tomato firebrick antique mint lemon|Manufacturer#3|Brand#33|LARGE ANODIZED COPPER|45|WRAP JAR|1730.82|ctions alongs\n6825|gainsboro lace steel floral orchid|Manufacturer#4|Brand#45|LARGE POLISHED COPPER|24|LG BAG|1731.82|ns. ex\n6826|sienna cornflower green lime tomato|Manufacturer#5|Brand#51|MEDIUM POLISHED STEEL|19|JUMBO BOX|1732.82|nts. careful\n6827|bisque indian tan red midnight|Manufacturer#3|Brand#32|SMALL BURNISHED BRASS|8|SM BOX|1733.82|accounts-- furiously \n6828|bisque sandy powder cornflower blanched|Manufacturer#1|Brand#15|MEDIUM POLISHED TIN|11|JUMBO JAR|1734.82|ic req\n6829|midnight lawn steel chocolate salmon|Manufacturer#1|Brand#13|LARGE BRUSHED BRASS|6|MED BAG|1735.82|e regu\n6830|violet grey navajo dim blush|Manufacturer#5|Brand#54|PROMO BRUSHED TIN|44|MED CAN|1736.83|s haggle quickly \n6831|medium lime papaya cream sky|Manufacturer#1|Brand#15|SMALL BURNISHED BRASS|21|LG JAR|1737.83| final asymptotes. fi\n6832|medium blush aquamarine bisque deep|Manufacturer#2|Brand#22|SMALL POLISHED TIN|39|SM BOX|1738.83|xpress\n6833|metallic snow black khaki coral|Manufacturer#4|Brand#41|MEDIUM BURNISHED COPPER|25|LG BOX|1739.83|usly final accounts \n6834|grey yellow green honeydew navajo|Manufacturer#3|Brand#34|STANDARD BURNISHED COPPER|17|WRAP PACK|1740.83|s inte\n6835|blush floral midnight magenta frosted|Manufacturer#4|Brand#44|SMALL ANODIZED NICKEL|38|SM CASE|1741.83|blithely \n6836|pale lavender bisque yellow indian|Manufacturer#3|Brand#31|SMALL PLATED TIN|7|LG JAR|1742.83|ges. \n6837|drab salmon floral mint burlywood|Manufacturer#3|Brand#31|ECONOMY ANODIZED TIN|29|SM CASE|1743.83|sits na\n6838|antique gainsboro aquamarine green spring|Manufacturer#1|Brand#15|PROMO ANODIZED STEEL|35|SM PACK|1744.83|ing packages. bold acc\n6839|purple metallic saddle midnight magenta|Manufacturer#3|Brand#32|STANDARD PLATED BRASS|4|MED JAR|1745.83|s wake slyly.\n6840|light drab snow coral deep|Manufacturer#1|Brand#15|SMALL ANODIZED NICKEL|26|WRAP BAG|1746.84|riously final ac\n6841|lime cornflower ivory dim slate|Manufacturer#1|Brand#14|LARGE POLISHED NICKEL|3|SM DRUM|1747.84|y ruthles\n6842|lime medium coral bisque pale|Manufacturer#3|Brand#31|ECONOMY PLATED NICKEL|1|WRAP JAR|1748.84|kages alongside \n6843|honeydew violet cornsilk sienna antique|Manufacturer#3|Brand#35|STANDARD BRUSHED STEEL|46|LG PKG|1749.84|s use\n6844|cyan chiffon orchid plum rose|Manufacturer#2|Brand#22|LARGE POLISHED TIN|49|SM CAN|1750.84|ess deposits sleep \n6845|ghost lime violet cornsilk peach|Manufacturer#4|Brand#45|MEDIUM PLATED STEEL|40|WRAP JAR|1751.84|less fo\n6846|white maroon chocolate smoke pink|Manufacturer#5|Brand#53|MEDIUM BURNISHED BRASS|35|WRAP CASE|1752.84|egular pinto\n6847|pink moccasin peru puff saddle|Manufacturer#1|Brand#14|SMALL ANODIZED NICKEL|45|SM PACK|1753.84| accounts accord\n6848|rosy honeydew plum tomato deep|Manufacturer#1|Brand#14|PROMO POLISHED BRASS|9|LG BOX|1754.84|romis\n6849|powder pale dodger firebrick wheat|Manufacturer#3|Brand#34|STANDARD BURNISHED STEEL|32|JUMBO CASE|1755.84|ost sly\n6850|brown blue tan hot blush|Manufacturer#5|Brand#53|ECONOMY ANODIZED STEEL|26|MED CAN|1756.85|ts integrate accordin\n6851|red peach seashell honeydew burlywood|Manufacturer#5|Brand#51|MEDIUM BRUSHED TIN|36|JUMBO BAG|1757.85|uests kindle f\n6852|pale cornsilk metallic brown beige|Manufacturer#2|Brand#23|PROMO POLISHED COPPER|25|SM PKG|1758.85| fluffily even ac\n6853|purple sandy dark powder indian|Manufacturer#1|Brand#14|ECONOMY POLISHED COPPER|44|MED CASE|1759.85|bout the p\n6854|azure navy gainsboro cream ghost|Manufacturer#4|Brand#41|ECONOMY BRUSHED BRASS|15|JUMBO JAR|1760.85|bout the carefully reg\n6855|magenta spring orchid azure frosted|Manufacturer#5|Brand#51|PROMO ANODIZED NICKEL|6|WRAP DRUM|1761.85|l orbi\n6856|blanched midnight cream papaya drab|Manufacturer#4|Brand#41|SMALL BRUSHED TIN|5|JUMBO CAN|1762.85|s haggle above the flu\n6857|red spring blush cornsilk peach|Manufacturer#4|Brand#42|STANDARD POLISHED TIN|46|LG JAR|1763.85| quickly unu\n6858|blush puff firebrick beige deep|Manufacturer#1|Brand#11|SMALL BRUSHED BRASS|30|WRAP BOX|1764.85|t evenly express ac\n6859|snow lavender navy linen chartreuse|Manufacturer#3|Brand#35|ECONOMY ANODIZED NICKEL|35|MED PKG|1765.85|sual dolphi\n6860|snow green linen dim orange|Manufacturer#1|Brand#14|LARGE POLISHED STEEL|9|LG JAR|1766.86|ly caref\n6861|white violet coral olive metallic|Manufacturer#2|Brand#23|STANDARD POLISHED BRASS|32|MED BAG|1767.86| the slyly\n6862|violet cornflower pink rose burnished|Manufacturer#1|Brand#13|PROMO POLISHED TIN|36|JUMBO CAN|1768.86|ckages cajole sl\n6863|brown bisque linen cyan drab|Manufacturer#3|Brand#35|ECONOMY BURNISHED NICKEL|25|JUMBO CAN|1769.86|aggle slyly ste\n6864|burnished misty firebrick cream papaya|Manufacturer#4|Brand#45|SMALL PLATED NICKEL|20|WRAP CAN|1770.86|y even accounts acros\n6865|seashell salmon lace mint aquamarine|Manufacturer#3|Brand#35|MEDIUM BRUSHED NICKEL|17|SM BOX|1771.86|l depend\n6866|goldenrod midnight metallic cornsilk wheat|Manufacturer#5|Brand#54|ECONOMY POLISHED COPPER|23|SM DRUM|1772.86|quickly along the slyl\n6867|green puff blanched light lawn|Manufacturer#2|Brand#21|MEDIUM BRUSHED TIN|21|WRAP DRUM|1773.86|ns. final foxes al\n6868|gainsboro goldenrod white forest navy|Manufacturer#5|Brand#52|STANDARD ANODIZED BRASS|17|MED BAG|1774.86|es. furiousl\n6869|grey salmon seashell bisque linen|Manufacturer#1|Brand#14|MEDIUM PLATED BRASS|39|WRAP BAG|1775.86|nic ac\n6870|chartreuse firebrick indian royal sky|Manufacturer#5|Brand#53|LARGE POLISHED TIN|33|WRAP BOX|1776.87|ar accounts affix bli\n6871|peach steel turquoise sky forest|Manufacturer#1|Brand#11|ECONOMY PLATED STEEL|46|WRAP PKG|1777.87|r theodolites. ca\n6872|hot almond azure lace forest|Manufacturer#2|Brand#21|PROMO BRUSHED TIN|41|LG PACK|1778.87|l packages boost aft\n6873|medium red dark saddle white|Manufacturer#4|Brand#43|PROMO PLATED STEEL|47|MED DRUM|1779.87|unts snooze by the\n6874|violet burnished misty medium frosted|Manufacturer#5|Brand#53|SMALL BRUSHED TIN|3|MED PKG|1780.87|above th\n6875|blue papaya steel lemon cyan|Manufacturer#2|Brand#25|ECONOMY PLATED NICKEL|22|JUMBO JAR|1781.87|ts are deposits. slyl\n6876|lavender sandy mint light deep|Manufacturer#1|Brand#14|LARGE BRUSHED TIN|5|MED PKG|1782.87|ickly regular, silent \n6877|lemon ghost green thistle peru|Manufacturer#3|Brand#33|LARGE POLISHED COPPER|16|JUMBO PKG|1783.87|kly b\n6878|cream wheat aquamarine yellow burlywood|Manufacturer#1|Brand#12|ECONOMY ANODIZED COPPER|44|LG BOX|1784.87|ular dep\n6879|sienna slate chartreuse lavender rose|Manufacturer#5|Brand#51|STANDARD PLATED COPPER|25|JUMBO DRUM|1785.87|r the d\n6880|lemon tan orchid white grey|Manufacturer#4|Brand#42|LARGE PLATED NICKEL|11|LG JAR|1786.88|ss deposits. carefull\n6881|chiffon indian green salmon antique|Manufacturer#3|Brand#35|STANDARD POLISHED TIN|45|LG CAN|1787.88|he even pinto\n6882|burlywood antique light cornflower beige|Manufacturer#2|Brand#21|LARGE ANODIZED BRASS|5|MED CASE|1788.88|e furiously bold \n6883|royal aquamarine frosted moccasin beige|Manufacturer#1|Brand#14|ECONOMY BRUSHED BRASS|11|LG BAG|1789.88|venly express ideas. q\n6884|smoke misty blanched burnished antique|Manufacturer#4|Brand#41|STANDARD BURNISHED COPPER|35|SM BOX|1790.88|tes use ac\n6885|tomato pale dodger ghost orchid|Manufacturer#3|Brand#35|MEDIUM PLATED COPPER|46|JUMBO CASE|1791.88|ly regular d\n6886|lace olive navy lime snow|Manufacturer#2|Brand#24|SMALL POLISHED COPPER|16|WRAP PKG|1792.88|e the even d\n6887|linen ivory dim smoke seashell|Manufacturer#4|Brand#41|STANDARD BRUSHED BRASS|24|MED BAG|1793.88|s sleep\n6888|azure purple moccasin magenta peru|Manufacturer#1|Brand#11|MEDIUM POLISHED TIN|4|SM PKG|1794.88|r request\n6889|chiffon royal lemon sandy ivory|Manufacturer#2|Brand#22|MEDIUM PLATED BRASS|4|MED CASE|1795.88|ing to the even pl\n6890|magenta misty peach medium peru|Manufacturer#2|Brand#24|ECONOMY POLISHED TIN|22|JUMBO CAN|1796.89|accounts.\n6891|salmon brown red lavender sky|Manufacturer#1|Brand#11|SMALL ANODIZED COPPER|31|LG BAG|1797.89|pecia\n6892|dim almond deep drab pale|Manufacturer#2|Brand#25|LARGE POLISHED NICKEL|37|WRAP DRUM|1798.89| beans; q\n6893|metallic beige azure salmon brown|Manufacturer#2|Brand#21|PROMO BRUSHED BRASS|5|SM PACK|1799.89|ly ironic acc\n6894|cyan grey violet deep almond|Manufacturer#1|Brand#13|SMALL PLATED TIN|30|LG JAR|1800.89|. expres\n6895|thistle turquoise olive mint antique|Manufacturer#1|Brand#14|MEDIUM BRUSHED BRASS|20|JUMBO CASE|1801.89|sly ironic reque\n6896|olive smoke lemon goldenrod moccasin|Manufacturer#3|Brand#35|SMALL PLATED STEEL|43|JUMBO BOX|1802.89|ts. enticingly expre\n6897|coral magenta mint firebrick white|Manufacturer#4|Brand#41|ECONOMY BRUSHED BRASS|4|LG BAG|1803.89| blithely\n6898|green yellow blue peach thistle|Manufacturer#1|Brand#13|LARGE PLATED STEEL|29|MED DRUM|1804.89|. pinto \n6899|lemon maroon drab lace peru|Manufacturer#3|Brand#34|MEDIUM BRUSHED BRASS|49|JUMBO CASE|1805.89|sily express theodolit\n6900|bisque blue navy turquoise saddle|Manufacturer#2|Brand#25|MEDIUM BRUSHED NICKEL|42|SM BOX|1806.90|le. bold\n6901|indian drab lavender purple bisque|Manufacturer#4|Brand#42|PROMO ANODIZED STEEL|25|WRAP CASE|1807.90|e carefully\n6902|ghost green violet mint bisque|Manufacturer#2|Brand#23|STANDARD ANODIZED BRASS|19|LG CAN|1808.90| deposits boost ab\n6903|mint grey turquoise burnished spring|Manufacturer#3|Brand#32|LARGE POLISHED NICKEL|3|JUMBO BAG|1809.90|its acros\n6904|deep salmon olive drab thistle|Manufacturer#3|Brand#32|SMALL PLATED BRASS|6|LG CAN|1810.90|deas promise ab\n6905|sandy powder bisque orchid hot|Manufacturer#1|Brand#12|LARGE BURNISHED BRASS|32|LG BAG|1811.90| ironic packages al\n6906|black moccasin deep lace blush|Manufacturer#5|Brand#51|STANDARD BURNISHED STEEL|23|SM PACK|1812.90|unts will sleep sp\n6907|grey dodger lawn thistle tomato|Manufacturer#1|Brand#12|ECONOMY BRUSHED NICKEL|22|SM CAN|1813.90|ts mold furiously.\n6908|royal rose peach rosy azure|Manufacturer#4|Brand#44|ECONOMY BRUSHED BRASS|7|LG CAN|1814.90|. furiously fi\n6909|plum cornflower gainsboro tomato lavender|Manufacturer#5|Brand#53|SMALL ANODIZED NICKEL|12|SM CASE|1815.90|y ironic request\n6910|coral steel deep sienna orange|Manufacturer#4|Brand#42|MEDIUM BRUSHED STEEL|6|JUMBO BAG|1816.91| silent accounts aft\n6911|sky papaya magenta tomato peach|Manufacturer#2|Brand#22|PROMO POLISHED BRASS|31|JUMBO BOX|1817.91|ously through the\n6912|olive brown navy chartreuse rose|Manufacturer#5|Brand#54|MEDIUM BRUSHED NICKEL|6|JUMBO PKG|1818.91|eat b\n6913|grey rosy tan puff sienna|Manufacturer#1|Brand#13|SMALL PLATED COPPER|15|MED CAN|1819.91| the slyly\n6914|peach indian maroon chiffon saddle|Manufacturer#2|Brand#22|STANDARD PLATED NICKEL|18|WRAP PACK|1820.91|ly final depos\n6915|linen khaki saddle chocolate medium|Manufacturer#5|Brand#51|LARGE ANODIZED COPPER|37|SM BAG|1821.91|pendenc\n6916|azure snow rose sandy chartreuse|Manufacturer#3|Brand#33|MEDIUM PLATED STEEL|1|SM BAG|1822.91|usly unusua\n6917|steel black moccasin lace ghost|Manufacturer#3|Brand#34|SMALL ANODIZED NICKEL|42|LG PACK|1823.91| theo\n6918|bisque mint drab gainsboro orange|Manufacturer#1|Brand#14|SMALL BRUSHED TIN|36|SM CASE|1824.91|ges? furiously e\n6919|linen smoke light powder papaya|Manufacturer#2|Brand#22|ECONOMY BRUSHED TIN|18|WRAP BAG|1825.91|ully blithely bold id\n6920|rose pink chartreuse dim deep|Manufacturer#3|Brand#34|STANDARD PLATED COPPER|50|WRAP CAN|1826.92|carefully. \n6921|green puff forest pale lawn|Manufacturer#3|Brand#34|MEDIUM PLATED COPPER|10|SM BOX|1827.92|usly. stealthy courts \n6922|firebrick lime cream magenta maroon|Manufacturer#3|Brand#32|LARGE ANODIZED STEEL|25|WRAP JAR|1828.92|the blithely bold th\n6923|ivory firebrick seashell peach navajo|Manufacturer#1|Brand#12|STANDARD POLISHED BRASS|49|JUMBO DRUM|1829.92|s. quickly unusual\n6924|powder blanched maroon violet magenta|Manufacturer#2|Brand#21|MEDIUM BURNISHED COPPER|43|WRAP PACK|1830.92| fluffy depende\n6925|dodger forest cream khaki aquamarine|Manufacturer#5|Brand#51|LARGE POLISHED NICKEL|23|WRAP DRUM|1831.92|areful\n6926|azure drab floral khaki spring|Manufacturer#5|Brand#52|LARGE PLATED STEEL|10|WRAP PACK|1832.92|ly pendin\n6927|plum honeydew azure lime pale|Manufacturer#5|Brand#52|PROMO POLISHED BRASS|23|JUMBO BAG|1833.92| furi\n6928|violet magenta peach lemon burnished|Manufacturer#2|Brand#22|MEDIUM ANODIZED STEEL|39|WRAP CAN|1834.92|s. blith\n6929|spring goldenrod violet lemon slate|Manufacturer#3|Brand#35|PROMO PLATED STEEL|4|SM PKG|1835.92|ole fluffily. s\n6930|chartreuse plum orchid violet lavender|Manufacturer#4|Brand#43|MEDIUM PLATED BRASS|15|WRAP CASE|1836.93| express, sile\n6931|ghost black coral magenta mint|Manufacturer#5|Brand#52|SMALL PLATED NICKEL|43|SM CAN|1837.93|l gifts nag slyly af\n6932|red pale firebrick turquoise moccasin|Manufacturer#4|Brand#45|PROMO POLISHED COPPER|30|JUMBO JAR|1838.93|nag carefully theodo\n6933|mint smoke white olive powder|Manufacturer#3|Brand#34|STANDARD BURNISHED TIN|39|JUMBO BOX|1839.93|old accounts sleep\n6934|tan slate orchid peru red|Manufacturer#1|Brand#13|SMALL BURNISHED STEEL|48|SM CAN|1840.93| quickly f\n6935|lawn forest blanched burlywood pale|Manufacturer#2|Brand#24|SMALL PLATED COPPER|14|LG DRUM|1841.93|yly. \n6936|dim orange indian drab midnight|Manufacturer#1|Brand#14|STANDARD ANODIZED NICKEL|28|WRAP DRUM|1842.93|es. s\n6937|drab beige sky cornsilk firebrick|Manufacturer#2|Brand#21|STANDARD BRUSHED STEEL|33|JUMBO PKG|1843.93|asymptotes: final t\n6938|linen burnished lemon drab moccasin|Manufacturer#2|Brand#25|LARGE PLATED STEEL|25|MED PKG|1844.93|nic accounts cajole \n6939|mint pale antique rose orange|Manufacturer#1|Brand#15|SMALL PLATED TIN|28|WRAP BAG|1845.93|among the fur\n6940|linen cyan chiffon tomato red|Manufacturer#3|Brand#33|PROMO BURNISHED NICKEL|5|SM JAR|1846.94|slyly final, \n6941|honeydew plum blush lemon chiffon|Manufacturer#1|Brand#11|PROMO PLATED BRASS|40|WRAP PACK|1847.94|e even, expre\n6942|thistle deep dim almond maroon|Manufacturer#3|Brand#31|PROMO BURNISHED STEEL|4|WRAP PKG|1848.94| packag\n6943|peru rose lavender light puff|Manufacturer#2|Brand#21|STANDARD ANODIZED BRASS|9|SM BAG|1849.94|ts. bli\n6944|sky chocolate olive yellow blanched|Manufacturer#5|Brand#51|MEDIUM POLISHED STEEL|41|JUMBO PACK|1850.94| the pending, final\n6945|cornsilk slate peru chiffon orchid|Manufacturer#2|Brand#25|PROMO POLISHED STEEL|34|JUMBO JAR|1851.94|pecial package\n6946|chartreuse saddle peach plum burnished|Manufacturer#3|Brand#33|PROMO BURNISHED BRASS|44|SM PACK|1852.94|uests boost furiously \n6947|indian dim lace papaya blue|Manufacturer#2|Brand#22|MEDIUM BRUSHED COPPER|18|MED CAN|1853.94| unusu\n6948|peach seashell black gainsboro deep|Manufacturer#1|Brand#12|SMALL BRUSHED STEEL|28|WRAP BOX|1854.94|s accou\n6949|slate dim cyan navajo white|Manufacturer#5|Brand#55|LARGE BRUSHED NICKEL|26|WRAP PACK|1855.94|lets.\n6950|sky ivory cream turquoise aquamarine|Manufacturer#1|Brand#13|ECONOMY BURNISHED STEEL|13|WRAP CAN|1856.95|aggle furiou\n6951|tan sandy royal sienna puff|Manufacturer#1|Brand#14|MEDIUM ANODIZED TIN|12|SM BAG|1857.95| accounts wak\n6952|gainsboro sienna blush forest lemon|Manufacturer#4|Brand#41|MEDIUM ANODIZED BRASS|37|LG CAN|1858.95|nt packages a\n6953|pale plum dark seashell orchid|Manufacturer#3|Brand#34|PROMO BRUSHED NICKEL|12|WRAP PKG|1859.95|. ironic \n6954|cornsilk ivory midnight pale blanched|Manufacturer#3|Brand#34|ECONOMY BURNISHED COPPER|27|WRAP BOX|1860.95|regular dep\n6955|drab chartreuse cyan wheat snow|Manufacturer#2|Brand#24|SMALL BURNISHED TIN|26|JUMBO CAN|1861.95|ronic, ironic acco\n6956|ivory navajo purple blush green|Manufacturer#2|Brand#24|MEDIUM POLISHED COPPER|17|JUMBO DRUM|1862.95| pinto beans sleep fu\n6957|salmon pink sky seashell tomato|Manufacturer#5|Brand#54|LARGE PLATED STEEL|5|JUMBO DRUM|1863.95|thely furiously\n6958|misty azure floral aquamarine dim|Manufacturer#1|Brand#15|PROMO ANODIZED COPPER|32|SM JAR|1864.95|ully after \n6959|puff green deep dark sienna|Manufacturer#5|Brand#51|SMALL PLATED BRASS|28|SM BAG|1865.95| deposits wake care\n6960|antique orchid dim linen peru|Manufacturer#5|Brand#53|ECONOMY ANODIZED BRASS|48|JUMBO PKG|1866.96|beans slee\n6961|slate drab maroon thistle puff|Manufacturer#3|Brand#35|ECONOMY BURNISHED NICKEL|18|SM JAR|1867.96|s believe furiously! a\n6962|green aquamarine azure midnight chiffon|Manufacturer#1|Brand#11|STANDARD BRUSHED BRASS|44|JUMBO JAR|1868.96|e fluffily. expr\n6963|maroon deep frosted hot coral|Manufacturer#1|Brand#13|ECONOMY BRUSHED TIN|31|SM PKG|1869.96|arefully carefull\n6964|moccasin dodger ivory plum sky|Manufacturer#4|Brand#41|SMALL PLATED TIN|50|LG BOX|1870.96|hless \n6965|snow spring peach dodger green|Manufacturer#1|Brand#13|STANDARD BURNISHED COPPER|45|JUMBO CAN|1871.96| nag. q\n6966|olive blanched drab smoke light|Manufacturer#4|Brand#42|STANDARD ANODIZED STEEL|22|WRAP BOX|1872.96|sits haggle bold \n6967|puff hot brown sky pink|Manufacturer#5|Brand#53|SMALL BURNISHED BRASS|9|SM PACK|1873.96| beans cajole qu\n6968|spring red royal turquoise ghost|Manufacturer#3|Brand#35|ECONOMY BRUSHED NICKEL|24|JUMBO BOX|1874.96|indle furiously regula\n6969|powder steel lemon aquamarine deep|Manufacturer#2|Brand#21|MEDIUM ANODIZED BRASS|37|JUMBO BAG|1875.96|ily regular requests. \n6970|powder hot moccasin deep goldenrod|Manufacturer#5|Brand#52|LARGE POLISHED BRASS|12|JUMBO PKG|1876.97|ly eve\n6971|navajo seashell gainsboro light metallic|Manufacturer#4|Brand#45|SMALL BURNISHED NICKEL|6|JUMBO CAN|1877.97|e slyly r\n6972|midnight white smoke lavender goldenrod|Manufacturer#2|Brand#23|ECONOMY POLISHED STEEL|20|SM JAR|1878.97|mptotes. depe\n6973|grey seashell navajo lace cream|Manufacturer#1|Brand#12|SMALL BRUSHED TIN|16|WRAP PKG|1879.97|t packages inte\n6974|beige drab metallic firebrick yellow|Manufacturer#2|Brand#23|MEDIUM ANODIZED STEEL|12|JUMBO BAG|1880.97|ironic ac\n6975|smoke maroon peru salmon puff|Manufacturer#4|Brand#41|MEDIUM BRUSHED COPPER|35|JUMBO JAR|1881.97|iously ironic acc\n6976|snow blanched midnight smoke olive|Manufacturer#4|Brand#42|PROMO BURNISHED COPPER|20|LG CASE|1882.97|theodolites\n6977|papaya lemon olive maroon grey|Manufacturer#4|Brand#42|PROMO POLISHED COPPER|37|LG PACK|1883.97|ld instru\n6978|yellow peach lace chiffon linen|Manufacturer#4|Brand#45|MEDIUM ANODIZED BRASS|6|SM PACK|1884.97| abou\n6979|dodger saddle thistle drab seashell|Manufacturer#1|Brand#12|STANDARD POLISHED COPPER|2|WRAP PKG|1885.97|e regular, even\n6980|thistle linen orange white burlywood|Manufacturer#5|Brand#53|STANDARD BRUSHED STEEL|35|WRAP PACK|1886.98|along the ironi\n6981|cyan blue blanched tan hot|Manufacturer#1|Brand#12|PROMO PLATED TIN|34|WRAP PKG|1887.98|ructions are al\n6982|brown wheat cream cornsilk pink|Manufacturer#2|Brand#22|SMALL POLISHED BRASS|12|LG PKG|1888.98|ons wake against th\n6983|cornsilk royal olive smoke forest|Manufacturer#3|Brand#32|MEDIUM POLISHED BRASS|39|MED CASE|1889.98|uriously special foxes\n6984|lime turquoise lavender floral rosy|Manufacturer#2|Brand#21|PROMO BURNISHED TIN|34|JUMBO PACK|1890.98|ly regular gifts det\n6985|white black navy turquoise light|Manufacturer#4|Brand#44|MEDIUM BRUSHED COPPER|48|SM CASE|1891.98| packa\n6986|lemon sky chocolate green linen|Manufacturer#2|Brand#21|ECONOMY BRUSHED BRASS|14|LG CAN|1892.98|ges. a\n6987|grey burnished white antique beige|Manufacturer#2|Brand#23|ECONOMY POLISHED NICKEL|15|JUMBO PACK|1893.98|ctions wake furiously\n6988|cream mint drab tomato ghost|Manufacturer#2|Brand#22|STANDARD ANODIZED BRASS|35|MED PKG|1894.98| deposit\n6989|puff bisque wheat dim seashell|Manufacturer#5|Brand#55|STANDARD POLISHED COPPER|4|WRAP PACK|1895.98|s use accordi\n6990|red coral violet white medium|Manufacturer#5|Brand#53|STANDARD BURNISHED NICKEL|44|JUMBO CASE|1896.99|ously ironic escapades\n6991|drab chartreuse plum white goldenrod|Manufacturer#5|Brand#52|MEDIUM BRUSHED STEEL|33|WRAP BAG|1897.99| the \n6992|chocolate lawn mint blanched turquoise|Manufacturer#5|Brand#52|SMALL BURNISHED TIN|21|SM BAG|1898.99|refully silent asympt\n6993|thistle blanched peach red navy|Manufacturer#2|Brand#23|SMALL BURNISHED BRASS|1|MED JAR|1899.99|cial theodolites alon\n6994|plum smoke cream navajo wheat|Manufacturer#4|Brand#43|PROMO BRUSHED NICKEL|35|JUMBO CASE|1900.99|gle. furi\n6995|dim sienna navy smoke coral|Manufacturer#5|Brand#54|LARGE PLATED NICKEL|41|MED CASE|1901.99| according to\n6996|metallic pale cornsilk antique tan|Manufacturer#2|Brand#21|LARGE ANODIZED TIN|45|WRAP BOX|1902.99|ly. blithely\n6997|peru blanched burlywood lavender black|Manufacturer#5|Brand#54|SMALL BRUSHED STEEL|27|SM BAG|1903.99|gular sheaves! ca\n6998|metallic khaki purple cyan cream|Manufacturer#1|Brand#12|STANDARD ANODIZED NICKEL|20|JUMBO BAG|1904.99|equest\n6999|moccasin burlywood plum tomato azure|Manufacturer#3|Brand#31|PROMO PLATED TIN|18|JUMBO CAN|1905.99|ular deposits a\n7000|beige orchid royal lace burnished|Manufacturer#4|Brand#44|ECONOMY ANODIZED NICKEL|37|WRAP CASE|907.00|. furiously c\n"
  },
  {
    "path": "src/test/regress/data/range_types.csv",
    "content": "\"[1,3)\",\"[1,3)\",\"[1,3)\",\"[\"\"2000-01-02 00:30:00\"\",\"\"2010-02-03 12:30:00\"\")\"\nempty,\"[1,)\",\"(,)\",empty\n"
  },
  {
    "path": "src/test/regress/enterprise_isolation_logicalrep_3_schedule",
    "content": "test: isolation_setup\n\n# tests that change node metadata should precede\n# isolation_cluster_management such that tests\n# that come later can be parallelized\ntest: isolation_cluster_management\n\ntest: isolation_logical_replication_with_partitioning\ntest: isolation_logical_replication_binaryless\n"
  },
  {
    "path": "src/test/regress/expected/isolation_append_copy_vs_all.out",
    "content": ""
  },
  {
    "path": "src/test/regress/expected/multi_insert_select_behavioral_analytics_create_table.out",
    "content": ""
  },
  {
    "path": "src/test/regress/expected/task_tracker_assign_task.out",
    "content": ""
  },
  {
    "path": "src/test/regress/expected/task_tracker_cleanup_job.out",
    "content": ""
  },
  {
    "path": "src/test/regress/expected/task_tracker_create_table.out",
    "content": ""
  },
  {
    "path": "src/test/regress/expected/task_tracker_partition_task.out",
    "content": ""
  },
  {
    "path": "src/test/regress/expected/upgrade_basic_before_non_mixed.out",
    "content": ""
  },
  {
    "path": "src/test/regress/sql/upgrade_basic_before_non_mixed.sql",
    "content": ""
  },
  {
    "path": "src/test/regress/sql_base_schedule",
    "content": ""
  }
]